summaryrefslogtreecommitdiff
path: root/usr/src/cmd/cmd-inet
diff options
context:
space:
mode:
authorstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
committerstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
commit7c478bd95313f5f23a4c958a745db2134aa03244 (patch)
treec871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/cmd/cmd-inet
downloadillumos-gate-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz
OpenSolaris Launch
Diffstat (limited to 'usr/src/cmd/cmd-inet')
-rw-r--r--usr/src/cmd/cmd-inet/Makefile69
-rw-r--r--usr/src/cmd/cmd-inet/Makefile.cmd-inet43
-rw-r--r--usr/src/cmd/cmd-inet/common/Makefile53
-rw-r--r--usr/src/cmd/cmd-inet/common/compat.c80
-rw-r--r--usr/src/cmd/cmd-inet/common/compat.h53
-rw-r--r--usr/src/cmd/cmd-inet/common/conflib.c2116
-rw-r--r--usr/src/cmd/cmd-inet/common/conflib.h122
-rw-r--r--usr/src/cmd/cmd-inet/common/ifaddrlist.c218
-rw-r--r--usr/src/cmd/cmd-inet/common/ifaddrlist.h63
-rw-r--r--usr/src/cmd/cmd-inet/common/kcmd.c811
-rw-r--r--usr/src/cmd/cmd-inet/common/kcmd.h123
-rw-r--r--usr/src/cmd/cmd-inet/common/mipagentstat_door.h145
-rw-r--r--usr/src/cmd/cmd-inet/common/tftpcommon.h83
-rw-r--r--usr/src/cmd/cmd-inet/etc/Makefile94
-rw-r--r--usr/src/cmd/cmd-inet/etc/datemsk.template2
-rw-r--r--usr/src/cmd/cmd-inet/etc/default/Makefile57
-rw-r--r--usr/src/cmd/cmd-inet/etc/default/inetinit55
-rw-r--r--usr/src/cmd/cmd-inet/etc/default/ipsec52
-rw-r--r--usr/src/cmd/cmd-inet/etc/dhcp/Makefile57
-rw-r--r--usr/src/cmd/cmd-inet/etc/dhcp/inittab184
-rw-r--r--usr/src/cmd/cmd-inet/etc/hosts28
-rw-r--r--usr/src/cmd/cmd-inet/etc/ike/Makefile65
-rw-r--r--usr/src/cmd/cmd-inet/etc/ike/config.sample133
-rw-r--r--usr/src/cmd/cmd-inet/etc/inetd.conf41
-rw-r--r--usr/src/cmd/cmd-inet/etc/init.d/Makefile98
-rw-r--r--usr/src/cmd/cmd-inet/etc/init.d/mipagent42
-rw-r--r--usr/src/cmd/cmd-inet/etc/init.d/ncakmod121
-rw-r--r--usr/src/cmd/cmd-inet/etc/init.d/ncalogd140
-rw-r--r--usr/src/cmd/cmd-inet/etc/init.d/pppd96
-rw-r--r--usr/src/cmd/cmd-inet/etc/ipaddrsel.conf36
-rw-r--r--usr/src/cmd/cmd-inet/etc/ipnodes30
-rw-r--r--usr/src/cmd/cmd-inet/etc/ipqosconf.1.sample135
-rw-r--r--usr/src/cmd/cmd-inet/etc/ipqosconf.2.sample137
-rw-r--r--usr/src/cmd/cmd-inet/etc/ipqosconf.3.sample71
-rw-r--r--usr/src/cmd/cmd-inet/etc/ipsecalgs48
-rw-r--r--usr/src/cmd/cmd-inet/etc/ipsecinit.sample87
-rw-r--r--usr/src/cmd/cmd-inet/etc/mipagent.conf-sample190
-rw-r--r--usr/src/cmd/cmd-inet/etc/mipagent.conf.fa-sample173
-rw-r--r--usr/src/cmd/cmd-inet/etc/mipagent.conf.ha-sample164
-rw-r--r--usr/src/cmd/cmd-inet/etc/nca/Makefile57
-rw-r--r--usr/src/cmd/cmd-inet/etc/nca/nca.if29
-rw-r--r--usr/src/cmd/cmd-inet/etc/nca/ncakmod.conf33
-rw-r--r--usr/src/cmd/cmd-inet/etc/nca/ncalogd.conf33
-rw-r--r--usr/src/cmd/cmd-inet/etc/nca/ncaport.conf38
-rw-r--r--usr/src/cmd/cmd-inet/etc/netmasks40
-rw-r--r--usr/src/cmd/cmd-inet/etc/networks40
-rw-r--r--usr/src/cmd/cmd-inet/etc/ppp/Makefile75
-rw-r--r--usr/src/cmd/cmd-inet/etc/ppp/chap-secrets56
-rw-r--r--usr/src/cmd/cmd-inet/etc/ppp/myisp-chat.tmpl41
-rw-r--r--usr/src/cmd/cmd-inet/etc/ppp/myisp.tmpl51
-rw-r--r--usr/src/cmd/cmd-inet/etc/ppp/options.tmpl54
-rw-r--r--usr/src/cmd/cmd-inet/etc/ppp/options.ttya.tmpl38
-rw-r--r--usr/src/cmd/cmd-inet/etc/ppp/pap-secrets58
-rw-r--r--usr/src/cmd/cmd-inet/etc/protocols68
-rw-r--r--usr/src/cmd/cmd-inet/etc/secret/Makefile67
-rw-r--r--usr/src/cmd/cmd-inet/etc/secret/ike.preshared37
-rw-r--r--usr/src/cmd/cmd-inet/etc/secret/ipseckeys.sample35
-rw-r--r--usr/src/cmd/cmd-inet/etc/services139
-rw-r--r--usr/src/cmd/cmd-inet/etc/sock2path58
-rw-r--r--usr/src/cmd/cmd-inet/etc/wanboot.conf.sample104
-rw-r--r--usr/src/cmd/cmd-inet/req.flg29
-rw-r--r--usr/src/cmd/cmd-inet/sbin/Makefile46
-rw-r--r--usr/src/cmd/cmd-inet/sbin/dhcpagent/Makefile89
-rw-r--r--usr/src/cmd/cmd-inet/sbin/dhcpagent/README459
-rw-r--r--usr/src/cmd/cmd-inet/sbin/dhcpagent/adopt.c185
-rw-r--r--usr/src/cmd/cmd-inet/sbin/dhcpagent/agent.c845
-rw-r--r--usr/src/cmd/cmd-inet/sbin/dhcpagent/agent.h124
-rw-r--r--usr/src/cmd/cmd-inet/sbin/dhcpagent/arp_check.c235
-rw-r--r--usr/src/cmd/cmd-inet/sbin/dhcpagent/arp_check.h54
-rw-r--r--usr/src/cmd/cmd-inet/sbin/dhcpagent/async.c296
-rw-r--r--usr/src/cmd/cmd-inet/sbin/dhcpagent/async.h67
-rw-r--r--usr/src/cmd/cmd-inet/sbin/dhcpagent/bound.c567
-rw-r--r--usr/src/cmd/cmd-inet/sbin/dhcpagent/class_id.c205
-rw-r--r--usr/src/cmd/cmd-inet/sbin/dhcpagent/class_id.h48
-rw-r--r--usr/src/cmd/cmd-inet/sbin/dhcpagent/defaults.c307
-rw-r--r--usr/src/cmd/cmd-inet/sbin/dhcpagent/defaults.h70
-rw-r--r--usr/src/cmd/cmd-inet/sbin/dhcpagent/dhcpagent.dfl101
-rw-r--r--usr/src/cmd/cmd-inet/sbin/dhcpagent/dhcpagent.xcl55
-rw-r--r--usr/src/cmd/cmd-inet/sbin/dhcpagent/dlpi_io.c629
-rw-r--r--usr/src/cmd/cmd-inet/sbin/dhcpagent/dlpi_io.h80
-rw-r--r--usr/src/cmd/cmd-inet/sbin/dhcpagent/dlprims.c208
-rw-r--r--usr/src/cmd/cmd-inet/sbin/dhcpagent/dlprims.h53
-rw-r--r--usr/src/cmd/cmd-inet/sbin/dhcpagent/inc.flg30
-rw-r--r--usr/src/cmd/cmd-inet/sbin/dhcpagent/inform.c148
-rw-r--r--usr/src/cmd/cmd-inet/sbin/dhcpagent/init_reboot.c158
-rw-r--r--usr/src/cmd/cmd-inet/sbin/dhcpagent/interface.c1197
-rw-r--r--usr/src/cmd/cmd-inet/sbin/dhcpagent/interface.h385
-rw-r--r--usr/src/cmd/cmd-inet/sbin/dhcpagent/ipc_action.c187
-rw-r--r--usr/src/cmd/cmd-inet/sbin/dhcpagent/ipc_action.h71
-rw-r--r--usr/src/cmd/cmd-inet/sbin/dhcpagent/packet.c729
-rw-r--r--usr/src/cmd/cmd-inet/sbin/dhcpagent/packet.h115
-rw-r--r--usr/src/cmd/cmd-inet/sbin/dhcpagent/release.c160
-rw-r--r--usr/src/cmd/cmd-inet/sbin/dhcpagent/renew.c367
-rw-r--r--usr/src/cmd/cmd-inet/sbin/dhcpagent/request.c492
-rw-r--r--usr/src/cmd/cmd-inet/sbin/dhcpagent/script_handler.c371
-rw-r--r--usr/src/cmd/cmd-inet/sbin/dhcpagent/script_handler.h84
-rw-r--r--usr/src/cmd/cmd-inet/sbin/dhcpagent/select.c226
-rw-r--r--usr/src/cmd/cmd-inet/sbin/dhcpagent/states.h70
-rw-r--r--usr/src/cmd/cmd-inet/sbin/dhcpagent/util.c757
-rw-r--r--usr/src/cmd/cmd-inet/sbin/dhcpagent/util.h84
-rw-r--r--usr/src/cmd/cmd-inet/sbin/dhcpinfo/Makefile55
-rw-r--r--usr/src/cmd/cmd-inet/sbin/dhcpinfo/dhcpinfo.c230
-rw-r--r--usr/src/cmd/cmd-inet/sbin/dhcpinfo/inc.flg30
-rw-r--r--usr/src/cmd/cmd-inet/sbin/ifparse/Makefile61
-rw-r--r--usr/src/cmd/cmd-inet/sbin/ifparse/ifparse.c584
-rw-r--r--usr/src/cmd/cmd-inet/sbin/netstrategy/Makefile46
-rw-r--r--usr/src/cmd/cmd-inet/sbin/netstrategy/netstrategy.c208
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/Makefile182
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/chat/Makefile27
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/chat/chat.c1762
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/finger.c1475
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/ftp/Makefile75
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/ftp/auth.c423
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/ftp/cmds.c2445
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/ftp/cmds_gss.c95
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/ftp/cmdtab.c216
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/ftp/domacro.c174
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/ftp/ftp.c2487
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/ftp/ftp.dfl39
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/ftp/ftp_var.h348
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/ftp/getpass.c96
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/ftp/glob.c796
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/ftp/main.c690
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/ftp/pclose.c162
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/ftp/ruserpass.c304
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/ftp/secure.c388
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/inc.flg31
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/nca/Makefile82
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/nca/ncab2clf.c906
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/netstat/Makefile67
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/netstat/inc.flg31
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/netstat/netstat.c5950
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/netstat/unix.c246
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppd/Makefile130
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppd/Makefile.def13
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppd/asppp2pppd1634
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppd/auth.c2179
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppd/cbcp.c490
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppd/cbcp.h38
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppd/ccp.c1517
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppd/ccp.h50
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppd/chap.c982
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppd/chap.h144
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppd/chap_ms.c636
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppd/chap_ms.h43
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppd/demand.c378
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppd/eui64.h103
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppd/fsm.c914
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppd/fsm.h190
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppd/ipcp.c2009
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppd/ipcp.h80
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppd/ipv6cp.c1536
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppd/ipv6cp.h128
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppd/lcp.c3184
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppd/lcp.h181
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppd/magic.c93
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppd/magic.h25
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppd/main.c2934
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppd/md4.c288
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppd/md4.h61
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppd/mschap_test.c91
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppd/multilink.c413
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppd/options.c2006
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppd/patchlevel.h26
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppd/pathnames.h95
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppd/plugins/Makefile115
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppd/plugins/Makefile.dist27
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppd/plugins/mapfile-minconn14
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppd/plugins/mapfile-passprompt18
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppd/plugins/mapfile-pppoe49
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppd/plugins/minconn.c40
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppd/plugins/passprompt.c123
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppd/plugins/pppoe.c476
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppd/pppd.h940
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppd/req.flg7
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppd/sha1.c755
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppd/sha1.h48
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppd/sha1_consts.h46
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppd/sys-solaris.c3791
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppd/upap.c734
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppd/upap.h126
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppd/utils.c1054
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppdump/CHANGES.top9
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppdump/COPYING.top340
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppdump/INSTALL.top14
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppdump/LICENSE.top89
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppdump/Makefile68
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppdump/Makefile.dist28
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppdump/Makefile.top22
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppdump/README.top32
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppdump/bsd-comp.c754
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppdump/deflate.c348
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppdump/ppp-comp.h150
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppdump/pppdump.1m71
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppdump/pppdump.c508
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppdump/zlib.c4614
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppdump/zlib.h631
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppstats/Makefile36
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/pppstats/pppstats.c592
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/rcp.c2087
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/rdate.c187
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/rdist/Makefile81
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/rdist/defs.h151
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/rdist/docmd.c783
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/rdist/expand.c674
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/rdist/gram.y533
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/rdist/inc.flg33
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/rdist/krb5defs.h69
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/rdist/lookup.c148
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/rdist/main.c555
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/rdist/server.c1691
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/req.flg30
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/rlogin.c1383
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/rsh.c794
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/ruptime.c297
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/rwho.c192
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/talk/Makefile76
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/talk/ctl.c135
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/talk/ctl.h96
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/talk/ctl_transact.c145
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/talk/display.c279
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/talk/get_addrs.c133
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/talk/get_names.c160
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/talk/init_disp.c186
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/talk/invite.c221
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/talk/io.c176
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/talk/look_up.c199
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/talk/msgs.c88
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/talk/talk.c94
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/talk/talk.h102
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/talk/talk_ctl.h55
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/telnet/Makefile69
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/telnet/auth.c488
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/telnet/auth.h121
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/telnet/authenc.c141
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/telnet/commands.c3684
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/telnet/defines.h79
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/telnet/enc_des.c563
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/telnet/encrypt.c971
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/telnet/encrypt.h155
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/telnet/externs.h402
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/telnet/general.h63
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/telnet/genget.c115
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/telnet/inc.flg32
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/telnet/kerberos5.c650
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/telnet/main.c283
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/telnet/network.c182
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/telnet/ring.c362
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/telnet/ring.h106
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/telnet/sys_bsd.c886
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/telnet/telnet.c2651
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/telnet/terminal.c229
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/telnet/types.h69
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/telnet/utilities.c1258
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/tftp/Makefile54
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/tftp/main.c940
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/tftp/tftp.c736
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/tftp/tftpprivate.h62
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/tftp/tftpsubs.c393
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/whois.c122
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/Makefile66
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/Makefile.inetsvc68
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/Makefile.lib37
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/dhcp/Makefile56
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/dhcp/dhcpconfig.sh39
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/dhcp/dhtadm.sh35
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/dhcp/pntadm.sh39
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/dsvclockd/Makefile63
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/dsvclockd/container.c725
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/dsvclockd/container.h122
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/dsvclockd/datastore.c312
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/dsvclockd/datastore.h95
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/dsvclockd/dsvclockd.c863
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/dsvclockd/dsvclockd.h101
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.chargend/Makefile34
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.chargend/chargen.xml122
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.chargend/in.chargend.c150
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.daytimed/Makefile34
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.daytimed/daytime.xml122
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.daytimed/in.daytimed.c89
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/Makefile108
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/README.caching164
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/bootp.c407
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/dhcp-server.xml124
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/dhcp.c2909
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/dhcpd.h231
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/dhcptab.c1313
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/encode.c248
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/generic.c668
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/hash.c579
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/hash.h168
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/icmp.c236
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/in.dhcpd.xcl152
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/inc.flg31
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/interfaces.c1445
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/interfaces.h99
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/logging.c107
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/main.c1351
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/misc.c769
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/per_dnet.c636
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/per_dnet.h222
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/relay.c303
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/tests/Makefile84
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/tests/get_number_test.c216
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/tests/mkdstore.c184
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/tests/test_client.c1444
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/tests/test_dstore.c940
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/tests/test_per_net.c236
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/tests/test_tnf.nawk100
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.discardd/Makefile34
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.discardd/discard.xml124
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.discardd/in.discardd.c78
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.echod/Makefile34
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.echod/echo.xml125
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.echod/in.echod.c80
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.mpathd/Makefile87
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.mpathd/mpathd.dfl39
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.mpathd/mpd_defs.h216
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.mpathd/mpd_main.c3104
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.mpathd/mpd_probe.c2966
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.mpathd/mpd_tables.c2665
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.mpathd/mpd_tables.h476
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.ndpd/Makefile70
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.ndpd/config.c982
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.ndpd/defs.h148
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.ndpd/dupl_addr.c849
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.ndpd/main.c2083
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.ndpd/ndp.c1354
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.ndpd/tables.c2399
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.ndpd/tables.h366
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.ndpd/trace.c181
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.ripngd/Makefile72
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.ripngd/defs.h168
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.ripngd/if.c141
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.ripngd/input.c557
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.ripngd/interface.h65
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.ripngd/main.c313
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.ripngd/output.c216
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.ripngd/startup.c535
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.ripngd/table.h92
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.ripngd/tables.c697
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.ripngd/timer.c181
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.ripngd/trace.c264
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.ripngd/trace.h108
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.timed/Makefile34
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.timed/in.timed.c96
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.timed/time.xml125
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/inetd/Makefile89
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/inetd/config.c810
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/inetd/contracts.c251
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/inetd/env.c183
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/inetd/inetd-upgrade.sh198
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/inetd/inetd-upgrade.xml103
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/inetd/inetd.c3678
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/inetd/inetd.xml170
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/inetd/inetd_impl.h363
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/inetd/repval.c820
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/inetd/req.flg29
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/inetd/tlx.c679
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/inetd/util.c365
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/inetd/wait.c413
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mipagent/Makefile151
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mipagent/aaa.c2687
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mipagent/aaa.h245
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mipagent/agent.c6816
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mipagent/agent.h774
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mipagent/agentID.c216
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mipagent/agentInit.c2798
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mipagent/agentKernelIntfce.c2727
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mipagent/agentKernelIntfce.h73
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mipagent/agentNet.c2035
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mipagent/agentPeriodic.c1190
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mipagent/agentSaveState.c1079
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mipagent/auth.c1661
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mipagent/auth.h86
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mipagent/fakeDiameter.pl632
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mipagent/hash.c1013
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mipagent/hash.h156
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mipagent/inc.flg34
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mipagent/mip.h432
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mipagent/mipagent.acl108
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mipagent/mipagent.reg46
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mipagent/mipagentstat_server.c446
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mipagent/pool.c241
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mipagent/pool.h72
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mipagent/setup.c749
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mipagent/setup.h57
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mipagent/snmp_appl.c409
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mipagent/snmp_faCOAEntry.c94
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mipagent/snmp_faVisitorEntry.c356
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mipagent/snmp_haCounterEntry.c239
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mipagent/snmp_haMobilityBindingEntry.c322
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mipagent/snmp_maAdvConfigEntry.c285
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mipagent/snmp_mipSecAssocEntry.c226
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mipagent/snmp_mipSecViolationEntry.c277
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mipagent/snmp_stub.c1083
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mipagent/snmp_stub.h211
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mipagent/snmp_trap.c50
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mipagent/snmp_tree.c715
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mipagent/thq.c645
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mipagent/thq.h111
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mipagent/utils.c913
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/ncaconfd/Makefile52
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/ncaconfd/ncaconfd.c1438
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/pppoe/Makefile68
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/pppoe/common.c394
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/pppoe/common.h97
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/pppoe/logging.c355
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/pppoe/logging.h70
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/pppoe/options.c2334
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/pppoe/pppoec.c1673
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/pppoe/pppoed.c443
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/pppoe/pppoed.h53
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/req.flg29
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/slpd/Makefile54
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/slpd/slp80
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/slpd/slp.xml107
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/slpd/slpd.c226
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/wanboot/Makefile79
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/wanboot/Makefile.com34
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/wanboot/bootlog-cgi/Makefile37
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/wanboot/bootlog-cgi/bootlog-cgi59
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/wanboot/encr/Makefile44
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/wanboot/encr/encr.c420
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/wanboot/hmac/Makefile44
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/wanboot/hmac/hmac.c237
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/wanboot/ickey/Makefile45
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/wanboot/ickey/ickey.c287
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/wanboot/keygen/Makefile43
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/wanboot/keygen/keygen.c746
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/wanboot/keymgmt/Makefile43
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/wanboot/keymgmt/keymgmt.c520
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/wanboot/netbootinfo/Makefile47
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/wanboot/netbootinfo/netbootinfo.c123
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/wanboot/p12split/Makefile45
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/wanboot/p12split/p12split.c657
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/wanboot/wanboot-cgi/Makefile46
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/wanboot/wanboot-cgi/wanboot-cgi.c1903
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/wanboot/wanboot.xcl61
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/Makefile67
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/Makefile180
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/bin/Makefile52
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/bin/dhcpmgr.sh44
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/Makefile48
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/Makefile48
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/Makefile48
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/Bridge.java113
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/BridgeException.java104
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/DsymException.java45
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/ExistsException.java52
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/HostExistsException.java41
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/InvalidPathException.java44
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/InvalidRsrcException.java44
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/Makefile92
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/NoDefaultsException.java51
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/NoEntryException.java52
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/NoHostsEntryException.java41
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/NoTableException.java44
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/NotRunningException.java51
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/ResourceBundle.properties79
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/ResourceStrings.java64
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/TableExistsException.java44
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/WordexpException.java45
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/Makefile48
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/Console.java108
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/DhcpCliFunction.java269
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/DhcpCliOption.java156
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/DhcpCliOptions.java140
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/DhcpCliPrint.java52
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/DhcpCliProgram.java114
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/Format.java608
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/GetOpt.java244
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/GetSubOpt.java249
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/Makefile84
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/ResourceBundle.properties71
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/ResourceStrings.java64
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/Util.java67
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpbatch/DhcpBatch.java262
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpbatch/DhcpCommand.java146
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpbatch/Makefile75
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpbatch/ResourceBundle.properties36
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpbatch/ResourceStrings.java64
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ConfigureBootp.java135
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ConfigureDhcp.java330
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ConfigureNetwork.java219
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ConfigureService.java362
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ConvertDataStore.java266
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/DhcpCfg.java243
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/DhcpCfgFunction.java116
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ExportData.java276
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/IPAddressList.java112
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ImportData.java147
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/Makefile86
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ResourceBundle.properties179
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ResourceStrings.java64
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ServerParameter.java581
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/UnconfigureDhcp.java212
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/AddEntry.java145
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/CreateTable.java93
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/DeleteEntry.java130
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/DhtAdm.java257
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/DhtAdmBatch.java80
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/DhtAdmFunction.java59
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/DisplayTable.java122
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/Makefile82
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/ModifyEntry.java204
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/RemoveTable.java93
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/ResourceBundle.properties89
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/ResourceStrings.java64
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/AddClientEntry.java194
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/CreateNetworkTable.java99
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/DeleteClientEntry.java125
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/DisplayNetworkTable.java183
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/ListNetworkTables.java100
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/Makefile82
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/ModifyClientEntry.java215
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/PntAdm.java260
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/PntAdmBatch.java79
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/PntAdmFunction.java114
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/RemoveNetworkTable.java104
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/ResourceBundle.properties98
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/ResourceStrings.java64
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/cfgboot.san123
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/cfgdhcp.san309
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/cfgnet.san229
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/createaddr.san199
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/createdhcp.san75
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/createmac.san138
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/createnet.san114
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/createopt.san128
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/cvtdhcp.san143
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/deleteaddr.san128
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/deletemac.san118
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/deleteopt.san118
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/dhcpconfig.san89
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/dhtadm.san105
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/displaydhcp.san124
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/displaynet.san196
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/export.san136
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/import.san129
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/listnet.san110
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/modifyaddr.san199
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/modifymac.san135
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/modifyopt.san129
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/pntadm.san90
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/removedhcp.san70
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/removenet.san121
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/ucfgdhcp.san108
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/utilities.san285
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/AddressView.java805
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/AddressWizard.java1114
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ConfigWizard.java1696
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ConfigureChoiceDialog.java131
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ConfigureRelayDialog.java121
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ConvertWizard.java674
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/CreateAddressDialog.java656
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/CreateMacroDialog.java763
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/CreateOptionDialog.java716
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DSModule.java145
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DSModuleEvent.java71
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DSModuleListener.java44
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DSWizard.java436
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DataManager.java234
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DeleteAddressDialog.java215
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DeleteMacroDialog.java116
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DeleteNetworksDialog.java290
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DeleteOptionDialog.java115
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DhcpmgrApplet.java662
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DhcpmgrDialog.java155
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DhcptabNameField.java84
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DialogActions.java43
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DisableServiceDialog.java152
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ErrorTable.java122
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ExportWizard.java630
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/HelpBundle.properties107
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ImportWizard.java437
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/MacroNameField.java80
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/MacroView.java673
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/Makefile125
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ModifyAddressesDialog.java434
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/MultipleOperationDialog.java167
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/OptionDescriptions.properties109
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/OptionNameField.java80
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/OptionView.java469
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/RelayView.java106
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ReleaseAddressDialog.java211
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ResourceBundle.properties692
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ResourceStrings.java64
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWModule.java136
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWbinfiles/Makefile72
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWbinfiles/ResourceBundle.properties30
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWbinfiles/ResourceStrings.java64
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWbinfiles/SUNWbinfiles.java84
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWfiles/Makefile72
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWfiles/ResourceBundle.properties30
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWfiles/ResourceStrings.java64
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWfiles/SUNWfiles.java84
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWnisplus/Makefile72
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWnisplus/ResourceBundle.properties30
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWnisplus/ResourceStrings.java64
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWnisplus/SUNWnisplus.java89
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SelectOptionDialog.java270
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ServerOptionsDialog.java875
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/UnconfigureDialog.java282
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ViewMacroDialog.java200
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/common/ExportController.java428
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/common/Exporter.java66
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/common/ImportController.java236
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/common/Importer.java65
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/common/Makefile76
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/common/ResourceBundle.properties73
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/common/ResourceStrings.java64
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/ActionError.java90
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/AsciiOptionValue.java132
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/BogusOptionValue.java102
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/BooleanOptionValue.java71
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/DhcpClientFlagType.java67
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/DhcpClientFlagTypes.java50
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/DhcpClientRecord.java674
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/DhcpDatastore.java211
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/DhcpResource.java104
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/DhcpdOptions.java944
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/DhcptabRecord.java98
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/ExportHeader.java122
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/IPAddress.java251
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/IPInterface.java85
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/IPOptionValue.java146
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/IncludeOptionValue.java79
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/Macro.java421
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/Makefile118
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/Network.java265
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/NumberOptionValue.java235
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/OctetOptionValue.java103
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/Option.java535
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/OptionContext.java82
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/OptionType.java82
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/OptionValue.java56
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/OptionValueFactory.java109
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/OptionsTable.java118
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/ResourceBundle.properties69
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/ResourceStrings.java64
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/StandardOptions.java121
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/ValidationException.java38
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/Makefile80
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/Qualifier.java120
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierAnd.java102
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierArray.java257
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierBoolean.java121
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierEnum.java34
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierFQDN.java58
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierIPv4.java62
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierIPv6.java106
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierImpl.java107
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierInteger.java57
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierIntegerEnum.java94
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierIntegerRange.java101
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierOr.java102
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierRange.java34
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierString.java48
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierStringEnum.java79
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierType.java74
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierTypeImpl.java44
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/DhcpHostsTable.java343
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/DhcpMgr.java66
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/DhcpMgrImpl.java510
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/DhcpNetMgr.java78
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/DhcpNetMgrImpl.java424
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/DhcpServiceMgr.java69
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/DhcpServiceMgrImpl.java255
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/DhcptabMgr.java104
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/DhcptabMgrImpl.java475
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/Makefile75
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/ResourceBundle.properties34
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/ResourceStrings.java64
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ALIGNMENT.java42
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/AutosizingTable.java141
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ButtonLayout.java243
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ButtonPanel.java151
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ButtonPanelListener.java37
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/DownButton.java39
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ExtendedCellRenderer.java62
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/FieldLayout.java293
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/HelpIds.java61
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/HostnameField.java98
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/IPAddressField.java126
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/IPAddressList.java364
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ImageButton.java75
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/IntegerField.java139
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/LeftButton.java40
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ListPair.java344
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/MainFrame.java429
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/Makefile107
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/Mnemonic.java62
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/MnemonicAction.java45
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/NextButton.java39
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/NoSpaceField.java88
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/PreviousButton.java40
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ProgressManager.java91
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ProportionalLayout.java146
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ResourceBundle.properties71
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ResourceStrings.java64
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/RightButton.java39
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/SelectionListener.java34
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/SortedHeaderRenderer.java55
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/SwingWorker.java108
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/TableMap.java101
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/TableSorter.java409
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/UpButton.java39
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/VerticalButtonLayout.java227
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/View.java118
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/Wizard.java678
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/WizardStep.java80
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/back.gifbin0 -> 99 bytes
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/down.gifbin0 -> 872 bytes
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/next.gifbin0 -> 99 bytes
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/up.gifbin0 -> 874 bytes
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/Makefile136
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/art/Makefile81
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/art/bannersmc.gifbin0 -> 3049 bytes
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/art/dot1.gifbin0 -> 91 bytes
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/art/dot2.gifbin0 -> 3696 bytes
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/art/folder.gifbin0 -> 1235 bytes
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/art/macro2.gifbin0 -> 97 bytes
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/art/macroflow.gifbin0 -> 13766 bytes
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/art/tip2.gifbin0 -> 852 bytes
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_addr_create.html235
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_addr_del.html137
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_addr_dup.html234
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_addr_how.html327
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_addr_mod.html253
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_addr_multi.html205
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_addr_ref.html255
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_addr_rel.html138
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_addr_view.html332
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_addr_wiz.html217
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_config_wiz.html314
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_convert_wiz.html172
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_export_wiz.html181
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_import_wiz.html173
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_macro_create.html196
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_macro_del.html133
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_macro_dup.html181
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_macro_how.html235
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_macro_mod.html189
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_macro_ref.html155
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_macro_view.html279
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_macros_about.html296
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_main_hlp.html192
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_main_how.html165
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_main_idx.html624
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_main_menus.html128
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_main_top.html182
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_net_del.html119
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_net_ref.html121
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_net_wiz.html201
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_option_create.html311
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_option_del.html128
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_option_dup.html310
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_option_how.html225
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_option_mod.html316
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_option_ref.html307
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_option_tags.html542
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_option_view.html293
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_relay_choose.html117
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_relay_config.html135
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_relay_dis.html114
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_relay_enable.html113
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_relay_how.html326
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_relay_ref.html138
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_relay_serv.html161
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_relay_unconfig.html113
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_server_serv.html247
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_server_unconfig.html122
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_solaris_about.html169
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/inc.flg32
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/Makefile65
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/class_cache.c265
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/class_cache.h61
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/dd_misc.c680
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/dd_misc.h68
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/dd_opt.c767
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/dd_opt.h75
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/dhcptab.c1014
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/exception.c524
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/exception.h73
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/inc.flg30
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/inittab.c153
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/network.c1102
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/optiondefs.c236
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/service.c716
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/scripts/Makefile42
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/scripts/i.ipsecalgs77
-rw-r--r--usr/src/cmd/cmd-inet/usr.sadm/scripts/r.ipsecalgs62
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/6to4relay.c425
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/Makefile268
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/arp.c396
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/bootconfchk/Makefile50
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/bootconfchk/bootconfchk.c92
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/comsat.xml104
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/dhcpconfig.sh30
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/dhtadm.sh30
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/finger.xml99
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/gettable.c189
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/hostconfig.c519
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/htable/Makefile58
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/htable/htable.c613
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/htable/htable.h74
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/htable/parse.y164
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/htable/scan.l109
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/if_mpadm.c673
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/ifconfig/Makefile80
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/ifconfig/defs.h63
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/ifconfig/dupl_addr.c892
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/ifconfig/ifconfig.c4716
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/ifconfig/ifconfig.h50
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/ifconfig/inc.flg31
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/ifconfig/revarp.c539
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/ikeadm.c2829
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/ikecert.sh60
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.comsat.c461
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.fingerd.c147
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/COPYRIGHT.c69
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/Makefile116
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/access.c1531
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/acl.c195
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/authenticate.c78
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/authenticate.h40
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/authuser.h39
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ckconfig.c186
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/config.h268
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/conversions.c198
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/conversions.h45
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/domain.c324
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/extensions.c2368
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/extensions.h174
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftp.xml75
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpaccess58
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpaddhost.sh319
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpcmd.y2505
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpconfig.sh376
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpconversions14
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpcount.c577
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpd.c7965
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpgroups4
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftphosts4
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftprestart.c343
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpservers4
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpshut.c511
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpusers19
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/getpwnam.c158
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/getpwnam.h25
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/glob.c665
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/gssutil.c1299
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/gssutil.h74
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/hostacc.c359
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/hostacc.h82
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/inet.c233
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/logwtmp.c198
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/pathnames.h152
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/paths.c250
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/popen.c328
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/private.c335
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/privatepw.c392
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/privs.c264
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/proto.h346
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/rdservers.c107
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/realpath.c361
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/restrict.c191
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/routevector.c292
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/strcasestr.c49
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/strsep.c67
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/timeout.c72
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/vers.c3
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/wu_config.h338
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/wu_fnmatch.c206
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/wu_fnmatch.h39
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/xferlog.c52
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.rarpd.c1409
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.rdisc.c2279
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.rexecd.c558
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.rlogind.c1631
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.routed/Makefile82
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.routed/common.c237
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.routed/defs.h805
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.routed/if.c1937
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.routed/input.c1515
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.routed/main.c1091
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.routed/output.c1019
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.routed/parms.c1046
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.routed/pathnames.h66
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.routed/radix.c999
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.routed/radix.h156
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.routed/rdisc.c1397
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.routed/rtquery.c784
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.routed/table.c2795
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.routed/trace.c1252
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.rshd.c1657
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.rwhod.c777
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.talkd/Makefile61
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.talkd/announce.c292
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.talkd/ctl.h118
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.talkd/in.talkd.c184
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.talkd/print.c64
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.talkd/process.c227
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.talkd/table.c285
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.talkd/talk.xml103
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.talkd/talkd_impl.h61
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.telnetd.c4903
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.tftpd.c1376
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.tnamed.c232
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/inc.flg33
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/inetadm/Makefile51
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/inetadm/inetadm.c1095
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/inetconv/Makefile45
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/inetconv/inetconv.c1527
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/ipaddrsel.c510
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/ipqosconf/Makefile89
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/ipqosconf/dlcosmk.types34
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/ipqosconf/dscpmk.types33
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/ipqosconf/flowacct.types34
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/ipqosconf/ipgpc.types48
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/ipqosconf/ipqosconf.c10041
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/ipqosconf/ipqosconf.h344
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/ipqosconf/tokenmt.types39
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/ipqosconf/tswtclmt.types36
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/ipsecalgs.c1073
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/ipsecconf.c5327
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/ipseckey.c3896
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/login.xml158
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/Makefile101
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/addr.c529
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/addr.h46
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/advertisements.c160
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/advertisements.h42
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/general.c188
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/general.h46
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/gsp.c188
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/gsp.h46
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/inc.flg29
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/mipagentconfig.c201
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/mipagentconfig.h81
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/pool.c171
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/pool.h42
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/spi.c450
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/spi.h46
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/utils.c810
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/utils.h64
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/mipagentstat/Makefile66
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/mipagentstat/inc.flg29
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/mipagentstat/main.c495
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/ndd.c299
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/ping/Makefile90
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/ping/ping.c2264
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/ping/ping.h140
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/ping/ping_aux.c1277
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/ping/ping_aux6.c1085
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/ping/req.flg29
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/pntadm.sh30
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/rarp.xml115
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/req.flg30
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/rexec.xml102
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/route.c2432
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/routeadm.c1280
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/shell.xml147
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/Makefile68
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/at.h206
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/dlprims.c342
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/fakewin.h71
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/fw.h147
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/fw_lib.h96
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/fw_rpc.h78
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/inc.flg30
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/nfs4_xdr.c2972
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/nis_clnt.h260
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/ntp.h533
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/slp.h146
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.c1073
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.h288
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_aarp.c143
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_adsp.c142
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_aecho.c72
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_apple.c210
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_arp.c209
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_atp.c128
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_bparam.c220
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_capture.c912
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_dhcp.c783
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_display.c775
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_dns.c746
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ether.c1510
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_filter.c2640
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_http.c141
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_icmp.c1005
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_igmp.c226
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ip.c1022
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ipaddr.c431
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ipsec.c234
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ldap.c1481
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_mip.c869
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_mip.h327
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_mount.c563
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nbp.c145
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_netbios.c556
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nfs.c691
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nfs.h58
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nfs3.c1214
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nfs4.c4889
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nfs_acl.c797
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nis.c676
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nisplus.c1695
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nlm.c1133
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ntp.c516
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ospf.c772
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ospf.h280
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ospf6.c815
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ospf6.h185
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_pf.c1197
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_pmap.c755
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ppp.c1946
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ppp.h192
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_pppoe.c399
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rip.c319
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rip6.c144
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rpc.c775
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rpcprint.c111
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rpcsec.c401
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rport.c500
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rquota.c164
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rstat.c272
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rtmp.c181
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_sctp.c1207
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_slp.c1963
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_smb.c1672
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_socks.c472
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_solarnet.c299
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_tcp.c448
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_tftp.c182
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_udp.c128
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_zip.c407
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/soconfig.c301
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/sppptun/Makefile59
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/sppptun/sppptun.c671
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/syncinit.c308
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/syncloop.c555
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/syncstat.c256
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/telnet.xml96
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/telnetd.dfl39
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/tname.xml95
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/traceroute/Makefile90
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/traceroute/req.flg8
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/traceroute/traceroute.c2184
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/traceroute/traceroute.h100
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/traceroute/traceroute_aux.c566
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/traceroute/traceroute_aux6.c744
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/wanbootutil.sh43
1055 files changed, 428017 insertions, 0 deletions
diff --git a/usr/src/cmd/cmd-inet/Makefile b/usr/src/cmd/cmd-inet/Makefile
new file mode 100644
index 0000000000..748b76e4c5
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/Makefile
@@ -0,0 +1,69 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 1989-2000 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+#
+# include global definitions
+include ../Makefile.cmd
+
+SUBDIR1= common
+SUBDIR2= etc sbin usr.bin usr.sbin usr.lib usr.sadm
+SUBDIRS= $(SUBDIR1) $(SUBDIR2)
+
+MSGSUBDIRS= usr.bin usr.sbin usr.lib usr.sadm
+POFILES= usr.bin/usr.bin.po usr.sbin/usr.sbin.po usr.sadm/usr.sadm.po
+POFILE= cmd-inet.po
+
+all:= TARGET= all
+install:= TARGET= install
+clean:= TARGET= clean
+clobber:= TARGET= clobber
+lint:= TARGET= lint
+_msg:= TARGET= _msg
+
+.KEEP_STATE:
+
+.PARALLEL: $(SUBDIRS)
+
+all install lint: $(SUBDIR1) .WAIT $(SUBDIR2)
+
+clean: $(SUBDIRS)
+
+clobber: $(SUBDIRS) local_clobber
+
+local_clobber:
+ $(RM) $(CLOBBERFILES)
+
+_msg: $(MSGSUBDIRS)
+ $(RM) $(POFILE)
+ cat $(POFILES) > $(POFILE)
+ $(RM) $(MSGDOMAIN)/$(POFILE)
+ cp $(POFILE) $(MSGDOMAIN)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(MFLAGS) $(TARGET)
+
+FRC:
diff --git a/usr/src/cmd/cmd-inet/Makefile.cmd-inet b/usr/src/cmd/cmd-inet/Makefile.cmd-inet
new file mode 100644
index 0000000000..925b389f1d
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/Makefile.cmd-inet
@@ -0,0 +1,43 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright (c) 1998-1999 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# cmd-inet/Makefile.cmd-inet
+#
+# Definitions common to cmd-inet source.
+#
+
+CMDINETSRC= $(SRC)/cmd/cmd-inet
+CMDINETCOMMONDIR= $(CMDINETSRC)/common
+
+#
+# Some files in common are built along with the commands which
+# need them. This is a common build rule for building files
+# from common
+#
+%.o: $(CMDINETCOMMONDIR)/%.c
+ $(COMPILE.c) $(OUTPUT_OPTION) $<
+ $(POST_PROCESS_O)
diff --git a/usr/src/cmd/cmd-inet/common/Makefile b/usr/src/cmd/cmd-inet/common/Makefile
new file mode 100644
index 0000000000..370372df32
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/common/Makefile
@@ -0,0 +1,53 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 1998-2002 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+OBJS= ifaddrlist.o
+
+SRCS= $(OBJS:.o=.c)
+HDRS= ifaddrlist.h
+
+include ../../Makefile.cmd
+
+CPPFLAGS += -O -I.
+
+.KEEP_STATE:
+
+all: $(OBJS)
+
+clean:
+ $(RM) $(OBJS)
+
+lint: lint_SRCS
+
+install: all
+
+# When an internationalized code is added to this directory, _msg has to be
+# defined in order to generate message catalog. Also, move 'common' directory
+# from 'SUBDIR1' to 'SUBDIR2' in usr/src/cmd/cmd-inet/Makefile
+
+include ../../Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/common/compat.c b/usr/src/cmd/cmd-inet/common/compat.c
new file mode 100644
index 0000000000..ec055db910
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/common/compat.c
@@ -0,0 +1,80 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <deflt.h>
+#include <string.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <compat.h>
+
+#define DEFAULT_IP_LINE DEFAULT_IP"="
+
+/*
+ * Code to handle /etc/default/ file for IPv4 command output compatibility
+ *
+ * Note: Handles BOTH the same as IP_VERSION6.
+ *
+ * Returns 1 if IP_VERSION4; 0 for other versions.
+ * Returns -1 if the value of DEFAULT_IP found in /etc/default/inet_type is
+ * invalid.
+ */
+int
+get_compat_flag(char **value)
+{
+ if (defopen(INET_DEFAULT_FILE) == 0) {
+ char *cp;
+ int flags;
+
+ /*
+ * ignore case
+ */
+ flags = defcntl(DC_GETFLAGS, 0);
+ TURNOFF(flags, DC_CASE);
+ (void) defcntl(DC_SETFLAGS, flags);
+
+ if (cp = defread(DEFAULT_IP_LINE))
+ *value = strdup(cp);
+
+ /* close */
+ (void) defopen((char *)NULL);
+
+ if (*value != NULL) {
+ if (strcasecmp(*value, "IP_VERSION4") == 0) {
+ return (DEFAULT_PROT_V4_ONLY);
+ } else if (strcasecmp(*value, "BOTH") == 0 ||
+ strcasecmp(*value, "IP_VERSION6") == 0) {
+ return (DEFAULT_PROT_BOTH);
+ } else {
+ return (DEFAULT_PROT_BAD_VALUE);
+ }
+ }
+ }
+ /* No value set */
+ return (DEFAULT_PROT_BOTH);
+}
diff --git a/usr/src/cmd/cmd-inet/common/compat.h b/usr/src/cmd/cmd-inet/common/compat.h
new file mode 100644
index 0000000000..8830909ce4
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/common/compat.h
@@ -0,0 +1,53 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _COMPAT_H
+#define _COMPAT_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Definitions for IPv4 compat defaults file reader.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define INET_DEFAULT_FILE "/etc/default/inet_type"
+#define DEFAULT_IP "DEFAULT_IP"
+
+#define DEFAULT_PROT_V4_ONLY (1)
+#define DEFAULT_PROT_BOTH (0)
+#define DEFAULT_PROT_BAD_VALUE (-1)
+
+extern int get_compat_flag(char **);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _COMPAT_H */
diff --git a/usr/src/cmd/cmd-inet/common/conflib.c b/usr/src/cmd/cmd-inet/common/conflib.c
new file mode 100644
index 0000000000..b2d4014adc
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/common/conflib.c
@@ -0,0 +1,2116 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * This file implements windows style INI files. It caches
+ * files in memory, so they are not parsed on each read.
+ *
+ * WARNING: This library is completely UN-THREAD-SAFE and NON-REENTRANT.
+ */
+
+/* LINTLIBRARY */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <strings.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/stat.h>
+#include <net/pfkeyv2.h> /* ipsec alg, etc., values */
+#include <errno.h>
+
+#include "conflib.h"
+
+#define INI_MAX_CACHED 10 /* Maximum number of files to keep cached */
+#define MAX_LONG_LEN 20 /* The printed size of a 32 bit number */
+
+/* ********************* Internal Structures ******************** */
+
+/* This is the simplest part. A label. This is the atom of the ini file */
+typedef struct {
+ char Label[MAX_LABEL_LEN];
+ char Value[MAX_VALUE_LEN];
+} LabelType;
+
+/* A section contains an array list of labels */
+typedef struct {
+ int NumLabels;
+ char SectionName[MAX_FILENAME_LEN];
+ LabelType *Labels;
+} SectionType;
+
+/* An ini file contains an array list of Sections */
+typedef struct {
+ int NumSections;
+ char Filename[MAX_FILENAME_LEN];
+ time_t ModificationTime;
+ SectionType *Sections;
+} IniFileType;
+
+/* An IniFileList Contains a linked list of ini files */
+typedef struct s {
+ IniFileType IniFile;
+ struct s *Next;
+} IniFileList;
+
+static IniFileList *IniHead = NULL;
+
+#define BUF_LEN MAX_LABEL_LEN + MAX_VALUE_LEN
+#define INI_SECTION 1
+#define INI_LABEL 2
+#define INI_ERROR 0
+
+char ErrorString[ERROR_STRING_LEN];
+
+boolean_t encr_alg_set = FALSE; /* true if encr_alg set */
+
+/* These are the valid ipsec actions mipagent and friends support. */
+char *validIPsecAction[] = {
+ "apply", /* outbound IPsec Policy */
+ "permit", /* inbound IPsec Policy */
+ NULL
+};
+
+/* Each entry in our various ipsec algorithm tables is one of these */
+struct ipsec_alg {
+ char *alg;
+ uint_t value;
+};
+
+/* Some macroed values */
+#define NO_AH_AALG 256
+#define NO_ESP_AALG 256
+#define NO_ESP_EALG 256
+
+/* valid encr_algs to match with user config */
+struct ipsec_alg encr_algs[] = {
+ {"any", SADB_EALG_NONE},
+ {"des", SADB_EALG_DESCBC},
+ {"des-cbc", SADB_EALG_DESCBC},
+ {"3des", SADB_EALG_3DESCBC},
+ {"3des-cbc", SADB_EALG_3DESCBC},
+ {"blowfish", SADB_EALG_BLOWFISH},
+ {"blowfish-cbc", SADB_EALG_BLOWFISH},
+ {"aes", SADB_EALG_AES},
+ {"aes-cbc", SADB_EALG_AES},
+ {"null", SADB_EALG_NULL},
+ {"none", NO_ESP_EALG},
+ {NULL, 0}
+};
+
+/* one auth_algs to match with user configs */
+struct ipsec_alg auth_algs[] = {
+ {"any", SADB_AALG_NONE},
+ {"md5", SADB_AALG_MD5HMAC},
+ {"hmac-md5", SADB_AALG_MD5HMAC},
+ {"sha1", SADB_AALG_SHA1HMAC},
+ {"hmac-sha1", SADB_AALG_SHA1HMAC},
+ {"sha", SADB_AALG_SHA1HMAC},
+ {"hmac-sha", SADB_AALG_SHA1HMAC},
+ {"none", NO_AH_AALG},
+ {NULL, 0}
+};
+
+#define SADB_SA_SHARED 0
+#define SADB_SA_UNIQUE 1
+
+/*
+ * One for sa_algs. OK, these aren't algs, but in order to make the parsing
+ * of properties easy, they're laid out "<tag> <alg> <tag> <alg> ...", so it's
+ * designed so <tag> maps directly to the function that parses <alg>. The
+ * "... sa (shared | unique)" bit then needs to be done analogously.
+ */
+struct ipsec_alg sa_algs[] = {
+ {"shared", SADB_SA_SHARED},
+ {"unique", SADB_SA_UNIQUE},
+ {NULL, 0},
+};
+
+
+/*
+ * Internal prototypes
+ */
+static int IniLoadFile(char *Filename);
+static void InitializeIniFile(IniFileList *FileList);
+static int IniParseFile(IniFileType *File, char *Filename);
+static int AddSection(IniFileType *IniFile, char *Section);
+static int AddLabel(IniFileType *IniFile, int SectionNdx, char *Label,
+ char *Data);
+static void ModifyLabel(IniFileType *IniFile, int SectionNdx, int LabelNdx,
+ char *Data);
+static int IniCheckComment(char *buffer);
+static int ParseLine(char *buffer, char *TempLabel, char *TempValue);
+static int IniLoaded(char *Filename);
+static int IniUpdateLocalCopy(char *Filename, char *Section, char *Label,
+ char *Data);
+#ifndef lint /* this function is not used for now */
+static char *GetSectionList(IniFileType *IniFile, char *argv, int *NumSections,
+ int *elementSize);
+#endif /* lint */
+static IniFileList *UnlinkFile(char *Filename);
+static int IniUnloadFile(char *Filename);
+static int IniUpdateFile(char *Filename, char *Section, char *Label,
+ char *Data);
+static int CopyToSection(char *Section, FILE *in, FILE *out, int CopySection);
+static void CopyToEOF(FILE *in, FILE *out, char *Label);
+static char *dirname(char *FullPath);
+static void CheckAndAllocateCacheSpace();
+static void IniFreeFile(IniFileList *File);
+static int Rename(char *source, char *dest);
+static int GetSectionProfileString(IniFileList *IniFile, char *Section,
+ char *Label, char *dest, int destLen);
+static int GetLabelProfileString(IniFileList *IniFile, int SectionNdx,
+ char *Label, char *dest, int destLen);
+
+/* ipsec algorithm parsing routines */
+int parsealg(char *, struct ipsec_alg *);
+int parse_algs(int, int, ipsec_req_t *);
+int parse_esp_auth_alg(char *, ipsec_req_t *);
+int parse_esp_encr_alg(char *, ipsec_req_t *);
+int parse_ah_alg(char *, ipsec_req_t *);
+int parse_sa_alg(char *, ipsec_req_t *);
+uint_t isIPsecActionValid(char *);
+
+
+/* something to switch on */
+enum ipsec_alg_type { ESP_ENCR_ALG = 1, ESP_AUTH_ALG, AH_AUTH_ALG, SA_ALG };
+
+/* table for parsing IPSEC related algs */
+struct ipsec_cmd {
+ char *c_name;
+ int (*c_func)(char *, ipsec_req_t *);
+} ipseccmd[] = {
+ { "encr_auth_algs", parse_esp_auth_alg },
+ { "encr_algs", parse_esp_encr_alg },
+ { "auth_algs", parse_ah_alg },
+ { "sa", parse_sa_alg },
+ { 0, 0 },
+};
+
+
+/*
+ * Function: InitializeIniFile
+ *
+ * Arguments: IniFileList *
+ *
+ * Description: This function will Initialize an ini file
+ *
+ * Returns: void
+ *
+ */
+static void
+InitializeIniFile(IniFileList *New)
+{
+ New->Next = NULL;
+ New->IniFile.NumSections = 0;
+ (void) memset(New->IniFile.Filename, 0, MAX_FILENAME_LEN);
+ New->IniFile.ModificationTime = 0;
+ New->IniFile.Sections = NULL;
+} /* InitializeIniFile */
+
+/*
+ * Function: FileHasChanged
+ *
+ * Arguments: ModificationTime, Filename
+ *
+ * Description: This function will return true if the filename's
+ * modification time is different than the one passed in.
+ * It also returns true on error. If we aren't able to get a
+ * stat() of the file, just assume it has changed.
+ *
+ * Returns: int (non zero if time has changed)
+ *
+ */
+static int
+FileHasChanged(time_t ModificationTime, char *Filename)
+{
+ struct stat file_stats;
+
+ if (stat(Filename, &file_stats) == (-1)) {
+ char *errMsg = strerror(errno);
+
+ if (errno == ENOENT)
+ return (TRUE); /* file does not exist */
+
+ /*
+ * Some kind of error. Just return TRUE.
+ * Note: we come in here twice, once via IniLoaded(), and
+ * (if that fails) again from IniLoadFile(). It would be
+ * mostly innocuous to overwrite ErrorString with this [same]
+ * message, but why bother?
+ */
+ if (ErrorString[0] == '\0') {
+ snprintf(ErrorString, sizeof (ErrorString), "Warning: "
+ "unable to retrieve stats on <%s>. %s.", Filename,
+ errMsg != NULL ? errMsg : "Unknown error");
+ }
+
+ return (TRUE);
+ }
+
+ /*
+ * Now return the change state
+ */
+ return (ModificationTime != file_stats.st_mtime);
+} /* FileHasChanged */
+
+/*
+ * Function: IniLoadFile
+ *
+ * Arguments: Filename
+ *
+ * Description: This function loads in a configuration file.
+ * It caches files by moving them to the head of the linked
+ * list. It also checks for files that have changed on the disk
+ * by using stat().
+ *
+ * Returns: int (zero on success)
+ *
+ */
+static int
+IniLoadFile(char *Filename)
+{
+ IniFileList *NewIniFileNode, *Probe;
+ IniFileList *Last = NULL;
+
+ for (Probe = IniHead; Probe; Probe = Probe->Next) {
+ /* Check to see if it is already loaded */
+ if (strcmp(Probe->IniFile.Filename, Filename) == 0) {
+ /*
+ * Found it, we already have it cached.
+ * check Date/Time stamp
+ */
+ if (FileHasChanged(Probe->IniFile.
+ ModificationTime, Filename)) {
+ /* Reload the file */
+ (void) IniUnloadFile(Filename);
+ break; /* Leave loop */
+ } else {
+ /*
+ * It is our file, and it hasn't changed,
+ * Cache our entry by moving it to the head
+ * of the list and return success.
+ */
+ if (Last) {
+ /*
+ * Only move it if this is not the
+ * first entry.
+ */
+ Last->Next = Probe->Next;
+ Probe->Next = IniHead;
+ IniHead = Probe;
+ }
+ return (0);
+ }
+ } /* strcmp checking filename */
+ Last = Probe;
+ } /* For loop searching for file */
+
+ /*
+ * Now, we know that we don't have the file cached or loaded, so
+ * allocate memory and load it.
+ * Note: IniFileList is the bucket that holds a STATIC IniFileType.
+ */
+ NewIniFileNode = malloc(sizeof (IniFileList));
+ if (!NewIniFileNode) {
+ (void) strcpy(ErrorString,
+ "Unable to allocate memory for new ini file.");
+ return (-1);
+ }
+
+ /*
+ * Add it to our head. But first, remove the last entry if we have
+ * Too many items in our cache.
+ */
+ CheckAndAllocateCacheSpace();
+ NewIniFileNode->Next = IniHead;
+ IniHead = NewIniFileNode;
+
+ /*
+ * Now we have a pointer to New. Clear it, and parse in the
+ * new file
+ */
+ InitializeIniFile(NewIniFileNode);
+ if (IniParseFile(&NewIniFileNode->IniFile, Filename)) {
+ /* Remove ini file from list */
+ IniHead = NewIniFileNode->Next;
+ free(NewIniFileNode);
+ return (-2);
+ }
+
+ return (0);
+} /* IniLoadFile */
+
+/*
+ * Function: CheckAndAllocateCacheSpace
+ *
+ * Arguments: none
+ *
+ * Description: This routine will check to see if our INI cache is full, and
+ * if so, will delete the last (least recently used) entry.
+ *
+ * Returns: void
+ *
+ */
+static void
+CheckAndAllocateCacheSpace()
+{
+ IniFileList *Probe;
+ IniFileList *Last;
+ int count;
+
+ /* Return if the cache is empty (or nearly empty) */
+ if (!IniHead || !IniHead->Next)
+ return;
+
+ /*
+ * Send Probe to next to end of list, and last to one before probe,
+ * while counting with count.
+ */
+ for (Probe = IniHead, count = 0, Last = IniHead;
+ Probe->Next;
+ Probe = Probe->Next, count++)
+ Last = Probe;
+
+ if (count >= INI_MAX_CACHED) {
+ /* Our cache is full. Free the last entry */
+ Last->Next = NULL;
+ IniFreeFile(Probe);
+ }
+} /* CheckAndAllocateCacheSpace */
+
+/*
+ * Function: IniParseFile
+ *
+ * Arguments: IniFileType *File, char *Filename
+ *
+ * Description: This routine will parse the INI file and load the Section
+ * and Label arrays.
+ *
+ * Returns: int
+ */
+static int
+IniParseFile(IniFileType *File, char *Filename)
+{
+ int LineNo;
+ char CurrentSection[MAX_LABEL_LEN];
+ char buffer[BUF_LEN];
+ char TempLabel[MAX_LABEL_LEN], TempValue[MAX_VALUE_LEN];
+ FILE *handle;
+ int rc;
+ struct stat file_stat;
+
+ /*
+ * Copy the filename...
+ */
+ (void) strncpy(File->Filename, Filename, MAX_FILENAME_LEN);
+ File->Filename[MAX_FILENAME_LEN-1] = 0; /* Just in case we overran */
+
+ (void) memset(CurrentSection, 0, MAX_LABEL_LEN);
+
+ /* Set the modification time */
+ if (stat(Filename, &file_stat) == 0) {
+ File->ModificationTime = file_stat.st_mtime;
+ } else {
+ char *errMsg = strerror(errno);
+
+ (void) snprintf(ErrorString, sizeof (ErrorString),
+ "Unable to stat <%s>. %s.", Filename,
+ errMsg != NULL ? errMsg : "Unknown error");
+
+ return (-1);
+ }
+
+ /* Open the file */
+ handle = fopen(Filename, "rt");
+ if (handle == NULL) {
+ char *errMsg = strerror(errno);
+
+ (void) snprintf(ErrorString, sizeof (ErrorString),
+ "Unable to open file <%s>. %s.", Filename,
+ errMsg != NULL ? errMsg : "Unknown error");
+
+ return (-1);
+ }
+
+ LineNo = 0;
+ while (fgets(buffer, BUF_LEN, handle)) {
+ LineNo++;
+ /* If it is a comment or a blank line, don't process it */
+ if (IniCheckComment(buffer))
+ continue;
+
+ switch (ParseLine(buffer, TempLabel, TempValue)) {
+ case INI_SECTION:
+ /* Call our add section function */
+ rc = AddSection(File, TempLabel);
+ /* If an error occurs, AddSection sets ErrorString */
+ if (rc < 0) {
+ (void) fclose(handle);
+ return (rc);
+ }
+ break;
+
+ case INI_LABEL:
+ /*
+ * check to see if we are before our first
+ * section label
+ */
+ if (!File->NumSections) {
+ (void) sprintf(ErrorString, "Data outside of "
+ "section on line %d.", LineNo);
+ (void) fclose(handle);
+ return (-2);
+ }
+ rc = AddLabel(File, File->NumSections-1, TempLabel,
+ TempValue);
+ /* If an error occurs, AddLabel sets ErrorString */
+ if (rc < 0) {
+ (void) fclose(handle);
+ return (rc);
+ }
+ break;
+ case INI_ERROR:
+ default:
+ (void) snprintf(ErrorString, sizeof (ErrorString),
+ "Error parsing INI file (%s) on line %d.",
+ Filename, LineNo);
+ (void) fclose(handle);
+ return (-3);
+ }
+ }
+
+ /* Success! */
+ (void) fclose(handle);
+ return (0);
+} /* IniParseFile */
+
+/*
+ * Function: TrimWhiteSpace
+ *
+ * Arguments: char *buffer
+ *
+ * Description: This function removes leading and trailing white space
+ * from a string.
+ *
+ * Returns: static void
+ */
+static void
+TrimWhiteSpace(char *buffer)
+{
+ int i, j;
+ /* First, find first non-whitespace character */
+ for (i = 0; buffer[i] && isspace((int)buffer[i]); i++);
+
+ /*
+ * Now, i is pointing to the first non-white space
+ * character in buffer
+ */
+
+ /* Find the last non-whitespace character */
+ for (j = strlen(buffer)-1; j > 0 && isspace((int)buffer[j]); j--);
+
+ if ((i > j) || (j < 0)) {
+ /* Empty */
+ buffer[0] = 0;
+ } else {
+ /* copy the usable buffer */
+ bcopy(&buffer[i], buffer, (j-i)+1);
+ /* and Null-terminate it */
+ buffer[(j-i)+1] = 0;
+ }
+} /* TrimWhiteSpace */
+
+/*
+ * Function: ParseLine
+ *
+ * Arguments: char *buffer, char *TempTag, char *TempValue
+ *
+ * Description: This routine will return either the section or label
+ * information. If it finds a section line in the buffer,
+ * it will return the section name in the TempTag Field.
+ * If it finds a Label, it will fill out TempValue and
+ * TempTag.
+ *
+ * Returns: int
+ */
+static int
+ParseLine(char *staticBuffer, char *TempTag, char *TempValue)
+{
+ int i = 0, j = 0;
+ int EqualFound = FALSE;
+ char buffer[BUF_LEN];
+
+ (void) strlcpy(buffer, staticBuffer, BUF_LEN);
+
+ /* Trim the white space of the entire buffer first */
+ TrimWhiteSpace(buffer);
+
+ if (buffer[0] == '[') {
+ /* Must be a section */
+ for (i = 1; buffer[i] && buffer[i] != ']'; i++)
+ TempTag[j++] = buffer[i];
+
+ /* Check to make sure we got an end-bracket */
+ if (buffer[i] != ']')
+ /* Bad Section! */
+ return (INI_ERROR);
+
+ TempTag[j] = 0;
+ TrimWhiteSpace(TempTag);
+ return (INI_SECTION);
+ }
+ for (i = 0; buffer[i]; i++) {
+ if (buffer[i] == '=') {
+ EqualFound = TRUE;
+ TempTag[j] = 0;
+ j = 0;
+ } else {
+ if (!EqualFound)
+ TempTag[j++] = buffer[i];
+ else
+ TempValue[j++] = buffer[i];
+ }
+ }
+ if (!EqualFound) {
+ /* Bad Label! */
+ return (INI_ERROR);
+ }
+ TempValue[j] = 0;
+ /* Handle EOLN */
+ if (TempValue[j-1] == '\n')
+ TempValue[j-1] = 0;
+
+ /* And finally, trim the white space off of our return strings */
+ TrimWhiteSpace(TempTag);
+ TrimWhiteSpace(TempValue);
+ return (INI_LABEL);
+} /* ParseLine */
+
+
+/*
+ * Function: IniUpdateFile
+ *
+ * Arguments: char *Filename, char *Section, char *Label, char *Data
+ *
+ * Description: This will update our INI file. It copies the file from
+ * it's original position to a temp one, then renames to
+ * overwrite the original.
+ *
+ * Returns: int (zero on success)
+ */
+static int
+IniUpdateFile(char *Filename, char *Section, char *Label, char *Data)
+{
+ FILE *in, *out;
+ char TempFile[MAX_FILENAME_LEN];
+ int DeleteSection = 0;
+ int DeleteLabel = 0;
+
+ /* Set our deletion flags */
+ if (!Label && !Data)
+ DeleteSection = 1;
+
+ if (!Data)
+ DeleteLabel = 1;
+
+ /*
+ * Don't worry about checking the handle right away. It's checked
+ * later. If the input file open fails, we assume that the file just
+ * does not exist. It will be created when the output file is renamed
+ * into the input file (or it is copied).
+ */
+ in = fopen(Filename, "r+");
+
+ /* Create a temporary file name in the same directory as the INI file */
+ (void) strlcpy(TempFile, tempnam(dirname(Filename), NULL),
+ sizeof (TempFile));
+
+ out = fopen(TempFile, "w");
+ if (!out) {
+ char *errMsg = strerror(errno);
+
+ (void) snprintf(ErrorString, sizeof (ErrorString),
+ "Unable to copy <%s>, can't open temp file <%s>. %s.",
+ Filename, TempFile,
+ errMsg != NULL ? errMsg : "Unknown error");
+
+ (void) fclose(in);
+ return (-1);
+ }
+
+ if (in) {
+ /* Our input file exists */
+ if (!CopyToSection(Section, in, out, DeleteSection?0:1)) {
+ /*
+ * Section did not exist, make one
+ * If DeleteSection or DeleteLabel is set, it's a nop.
+ */
+
+ if (!DeleteSection && !DeleteLabel) {
+ (void) fprintf(out, "[%s]\n", Section);
+ (void) fprintf(out, "%s=%s\n", Label, Data);
+ }
+ } else {
+ if (DeleteSection) {
+ /*
+ * Delete the section.
+ * Copy to EOF with NULL as the label
+ */
+ CopyToEOF(in, out, NULL);
+ } else {
+ /*
+ * Section did exist, just dump label, and
+ * search for next section or same label line,
+ * so we can erase it.
+ */
+ if (!DeleteLabel) {
+ /*
+ * Dump the new label, only if
+ * we're not deleting it
+ */
+ (void) fprintf(out, "%s=%s\n",
+ Label, Data);
+ }
+ CopyToEOF(in, out, Label);
+ }
+ }
+ (void) fclose(in);
+ } else {
+ /* no input file, just put output */
+ (void) fprintf(out, "[%s]\n", Section);
+ (void) fprintf(out, "%s=%s\n", Label, Data);
+ }
+
+ (void) fclose(out);
+ /*
+ * The rename could file for many reasons, but all of them are very
+ * bad ones. Assume that Rename has set ErrorString.
+ */
+ if (Rename(TempFile, Filename)) {
+ return (-1);
+ }
+ /* Let our internal update set the return code now */
+ return (IniUpdateLocalCopy(Filename, Section, Label, Data));
+} /* IniUpdateFile */
+
+/*
+ * Function: Rename
+ *
+ * Arguments: char *source, char *dest
+ *
+ * Description: This function tries a rename. If it fails, it tries a copy.
+ * It should never fail, since the temp file is in the same
+ * directory as the input file.
+ *
+ * Returns: int (zero on success)
+ */
+int
+Rename(char *source, char *dest)
+{
+ int rc;
+ if ((rc = rename(source, dest)) != 0) {
+ /* Do copy! */
+ FILE *in, *out;
+ char buffer[BUFSIZ]; /* use the system BUFSIZ for efficiency */
+ int nchars;
+
+ in = fopen(source, "r");
+ if (!in) {
+ char *errMsg = strerror(errno);
+
+ (void) snprintf(ErrorString, sizeof (ErrorString),
+ "Error opening source file <%s> for rename. %s.",
+ source, errMsg != NULL ? errMsg : "Unknown error");
+
+ return (-1);
+ }
+ out = fopen(dest, "w");
+ if (!out) {
+ char *errMsg = strerror(errno);
+
+ (void) fclose(in);
+ (void) unlink(dest);
+ (void) snprintf(ErrorString, sizeof (ErrorString),
+ "Error opening target file <%s> for rename. %s.",
+ dest, errMsg != NULL ? errMsg : "Unknown error");
+
+ return (-2);
+ }
+
+ /* Ok, now do copy */
+ do {
+ nchars = fread(buffer, 1, BUFSIZ, in);
+ if (nchars > 0) {
+ rc = fwrite(buffer, 1, nchars, out);
+ if (rc != nchars) {
+ char *errMsg = strerror(errno);
+
+ (void) snprintf(ErrorString,
+ sizeof (ErrorString),
+ "Error writing target file <%s> "
+ "during rename. %s.", dest,
+ errMsg != NULL ? errMsg :
+ "Unknown error.");
+
+ (void) fclose(in);
+ (void) fclose(out);
+ (void) unlink(source);
+ return (rc);
+ }
+ }
+ } while (nchars > 0);
+
+ /* if fread() fails... */
+ if (nchars < 0) {
+ char *errMsg = strerror(errno);
+
+ (void) snprintf(ErrorString, sizeof (ErrorString),
+ "Error reading source file <%s>. %s.", source,
+ errMsg != NULL ? errMsg : "Unknown error");
+
+ (void) fclose(in);
+ (void) fclose(out);
+ (void) unlink(source);
+ return (nchars);
+ }
+
+ (void) fclose(in);
+ (void) fclose(out);
+ (void) unlink(source);
+ rc = nchars;
+ }
+
+ return (rc);
+} /* Rename */
+
+/*
+ * Function: dirname
+ *
+ * Arguments: char *FullPath
+ *
+ * Description: This function will return the directory name of the
+ * passed in filename. This is for out temporary file.
+ * the reason it is not in /tmp or /usr/tmp is that
+ * MV does not work across filesystems.
+ *
+ * Returns: char *
+ */
+static char *
+dirname(char *FullPath)
+{
+ int len;
+ static char Dirname[MAX_FILENAME_LEN * 8];
+
+ (void) strncpy(Dirname, FullPath, MAX_FILENAME_LEN * 8);
+ Dirname[MAX_FILENAME_LEN * 8 - 1] = 0;
+
+ for (len = strlen(Dirname); len; len--) {
+ if (Dirname[len] == '/') {
+ Dirname[len] = 0;
+ return (Dirname);
+ }
+ }
+ /*
+ * If we got here, then we searched the whole string, return NULL
+ */
+ return (NULL);
+} /* dirname */
+
+/*
+ * Function: RemoveSection
+ *
+ * Arguments: int SectionNdx, IniFileType *File
+ *
+ * Description: This function will remove a section from the given INI file.
+ * It does all the necessary garbage collection.
+ *
+ * Returns: int (zero on success)
+ */
+static int
+RemoveSection(int SectionNdx, IniFileType *File)
+{
+ /* First, remove all the labels */
+ if (File->Sections[SectionNdx].Labels)
+ free(File->Sections[SectionNdx].Labels);
+
+ /* Finally, remove the Section */
+ if (File->NumSections &&
+ (SectionNdx < File->NumSections) &&
+ SectionNdx >= 0) {
+ int NumToMove;
+ NumToMove = File->NumSections - SectionNdx -1;
+ /* Copy the rest down a notch */
+ if (NumToMove > 0) {
+ (void) memcpy(&File->Sections[SectionNdx],
+ &File->Sections[SectionNdx+1],
+ sizeof (SectionType) * NumToMove);
+ }
+ /* Free the last one */
+ File->NumSections--;
+ return (0);
+ } /* Sanity Check */
+ (void) snprintf(ErrorString, sizeof (ErrorString),
+ "Unable to remove section <%s> in INI file <%s>",
+ File->Sections->SectionName, File->Filename);
+ return (-1);
+} /*RemoveSection*/
+/*
+ * Function: RemoveLabel
+ *
+ * Arguments: SectionType *Section, int LabelNdx
+ *
+ * Description: This function will remove a label from the given INI
+ * file. (No GC is necessary, since the Arrays are Static)
+ *
+ * Returns: int
+ */
+static int
+RemoveLabel(SectionType *Section, int LabelNdx)
+{
+
+ /* Sanity Check */
+ if (Section->NumLabels &&
+ (LabelNdx < Section->NumLabels) &&
+ LabelNdx >= 0) {
+ int NumToMove;
+ NumToMove = Section->NumLabels - LabelNdx -1;
+ /* Copy the rest down a notch */
+ if (NumToMove > 0) {
+ (void) memcpy(&Section->Labels[LabelNdx],
+ &Section->Labels[LabelNdx+1],
+ sizeof (LabelType) * NumToMove);
+ }
+ /* Free the last one */
+ Section->NumLabels--;
+ return (0);
+ } /* Sanity Check */
+ (void) snprintf(ErrorString, sizeof (ErrorString),
+ "Unable to remove label <%s> from INI file ",
+ Section->Labels->Label);
+ return (-1);
+} /* RemoveLabel */
+
+/*
+ * Function: IniUpdateLocalCopy
+ *
+ * Arguments: char *Filename, char *Section, char *Label, char *Data
+ *
+ * Description: This function will update the internal copy of the data
+ * we have. (It is called form IniUpdateFile)
+ *
+ * Returns: int
+ */
+static int
+IniUpdateLocalCopy(char *Filename, char *Section, char *Label, char *Data)
+{
+ IniFileList *FileProbe;
+ IniFileType *File;
+ int SectionNdx, LabelNdx;
+ struct stat file_stats;
+ int DeleteSection = 0;
+ int DeleteLabel = 0;
+ int rc;
+
+ /* Set our deletion flags */
+ if (!Label && !Data)
+ DeleteSection = 1;
+
+ if (!Data)
+ DeleteLabel = 1;
+
+ /*
+ * First, search for the file
+ */
+ for (FileProbe = IniHead;
+ FileProbe;
+ FileProbe = FileProbe->Next) {
+ if (strcmp(FileProbe->IniFile.Filename,
+ Filename) == 0)
+ break;
+ }
+
+ if (!FileProbe) {
+ /*
+ * We did not find the file. Someone did a write without
+ * a read. Return an error. (The file is still written to,
+ * and the next read will parse it in.)
+ */
+ (void) sprintf(ErrorString, "Warning: file not loaded.");
+ return (0);
+ }
+ File = &FileProbe->IniFile;
+
+ /*
+ * Now let's update our statistics, so our cache is up to date.
+ */
+ if (stat(Filename, &file_stats) == (-1)) {
+ char *errMsg = strerror(errno);
+
+ (void) snprintf(ErrorString, sizeof (ErrorString),
+ "Error: Unable to retrieve statistics on <%s>. %s.",
+ Filename, errMsg != NULL ? errMsg : "Unknown error");
+
+ return (-2);
+ } else
+ File->ModificationTime = file_stats.st_mtime;
+
+ /*
+ * now, File contains our file pointer. Check
+ * to see if our section exists.
+ */
+ for (SectionNdx = 0; SectionNdx < File->NumSections; SectionNdx++) {
+ if (strcmp(Section,
+ File->Sections[SectionNdx].SectionName) == 0) {
+ /* We have the section! */
+ break;
+ }
+ }
+ if (SectionNdx >= File->NumSections) {
+ if (DeleteSection || DeleteLabel) {
+ (void) snprintf(ErrorString, sizeof (ErrorString),
+ "Error IniUpdateLocalCopy: "
+ "Section not found in <%s>",
+ Filename);
+ return (-1); /* Error, not found */
+ }
+
+ /* Oops New Section/Label! */
+ SectionNdx = AddSection(File, Section);
+ if (SectionNdx < 0)
+ return (SectionNdx);
+ (void) AddLabel(File, SectionNdx, Label, Data);
+ } else {
+ if (DeleteSection) {
+ return (RemoveSection(SectionNdx, File));
+ }
+ /* We have the section. Search for the label */
+ for (LabelNdx = 0;
+ LabelNdx < File->Sections[SectionNdx].NumLabels;
+ LabelNdx++) {
+ if (strcasecmp(Label,
+ File->Sections[SectionNdx].Labels[LabelNdx].Label)
+ == 0) {
+ if (DeleteLabel) {
+ return (RemoveLabel(
+ &File->Sections[SectionNdx],
+ LabelNdx));
+ }
+
+ /* Found the label. Update the data */
+ ModifyLabel(File, SectionNdx, LabelNdx, Data);
+ break;
+ }
+ }
+ if (LabelNdx >= File->Sections[SectionNdx].NumLabels) {
+ if (DeleteLabel) {
+ (void) snprintf(ErrorString,
+ sizeof (ErrorString),
+ "Error IniUpdateLocalCopy: "
+ "Label not found in <%s>",
+ Filename);
+ return (-1); /* Error, not found */
+ }
+ /* Need to add a label! */
+ rc = AddLabel(File, SectionNdx, Label, Data);
+ if (rc < 0)
+ return (rc);
+ }
+ }
+ return (0);
+} /* IniUpdateLocalCopy */
+
+/*
+ * Function: AddSection
+ *
+ * Arguments: IniFileType *IniFile, char *Section
+ *
+ * Description: This function will add a section to the passed in file.
+ *
+ * Returns: int
+ */
+static int
+AddSection(IniFileType *IniFile, char *Section)
+{
+ SectionType *SectionPointer;
+
+ /*
+ * Set up new section Array
+ */
+ SectionPointer = malloc(sizeof (SectionType) *
+ (IniFile->NumSections + 1));
+ if (!SectionPointer) {
+ (void) strcpy(ErrorString, "Out of memory allocating section.");
+ return (-1);
+ }
+ /*
+ * Copy old sections over - including pointers
+ */
+ if (IniFile->NumSections) {
+ (void) memcpy(SectionPointer, IniFile->Sections,
+ IniFile->NumSections * sizeof (SectionType));
+ free(IniFile->Sections); /* Free the old sections Array */
+ }
+ /*
+ * Update new section
+ */
+ (void) memset(SectionPointer[IniFile->NumSections].SectionName, '0',
+ MAX_FILENAME_LEN);
+ (void) strncpy(SectionPointer[IniFile->NumSections].SectionName,
+ Section,
+ MAX_SECTION_LEN);
+ SectionPointer[
+ IniFile->NumSections].SectionName[MAX_SECTION_LEN - 1] = 0;
+ SectionPointer[IniFile->NumSections].Labels = NULL;
+ SectionPointer[IniFile->NumSections].NumLabels = 0;
+
+ IniFile->NumSections++;
+ IniFile->Sections = SectionPointer; /* Update array pointer */
+
+ /* Return an index to the newly added one */
+ return (IniFile->NumSections-1);
+} /* AddSection */
+
+/*
+ * Function: AddLabel
+ *
+ * Arguments: IniFileType *IniFile, int SectionNdx, char *Label, char *Data
+ *
+ * Description: This function adds a label to a section.
+ *
+ * Returns: static int
+ */
+static int
+AddLabel(IniFileType *IniFile, int SectionNdx, char *Label, char *Data)
+{
+ SectionType *SectionPointer;
+ LabelType *LabelPointer;
+
+ /*
+ * Set up CurrentSection pointer for ease of typing.
+ */
+ SectionPointer = &IniFile->Sections[SectionNdx];
+
+ /*
+ * Allocate enough room for new label
+ */
+ LabelPointer = malloc(sizeof (LabelType) *
+ (SectionPointer->NumLabels+1));
+ if (!LabelPointer) {
+ (void) strcpy(ErrorString, "Out of memory allocating label.");
+ return (-3);
+ }
+ if (SectionPointer->NumLabels) {
+ /* Do the copy */
+ (void) memcpy(LabelPointer, SectionPointer->Labels,
+ sizeof (LabelType) * SectionPointer->NumLabels);
+ free(SectionPointer->Labels);
+ }
+
+ (void) memset(LabelPointer[SectionPointer->NumLabels].Label, 0,
+ MAX_LABEL_LEN);
+ (void) memset(LabelPointer[SectionPointer->NumLabels].Value, 0,
+ MAX_VALUE_LEN);
+ (void) strlcpy(LabelPointer[SectionPointer->NumLabels].Label,
+ Label, MAX_LABEL_LEN);
+ (void) strlcpy(LabelPointer[SectionPointer->NumLabels].Value,
+ Data, MAX_VALUE_LEN);
+
+ SectionPointer->NumLabels++;
+ SectionPointer->Labels = LabelPointer;
+
+ /* return the index of the new label */
+ return (SectionPointer->NumLabels-1);
+} /* AddLabel */
+
+/*
+ * Function: ModifyLabel
+ *
+ * Arguments: IniFileType *IniFile, int SectionNdx, int LabelNdx, char *Data
+ *
+ * Description: This function will just modify the data of a label in memory.
+ *
+ * Returns: void
+ */
+static void
+ModifyLabel(IniFileType *IniFile, int SectionNdx, int LabelNdx, char *Data)
+{
+ (void) memset(IniFile->Sections[SectionNdx].Labels[LabelNdx].Value,
+ 0, MAX_VALUE_LEN);
+ (void) strlcpy(IniFile->Sections[SectionNdx].Labels[LabelNdx].Value,
+ Data, MAX_VALUE_LEN);
+} /* ModifyLabel */
+
+/*
+ * Function: SectionCmp
+ *
+ * Arguments: char *line, char *Section
+ *
+ * Description: This function returns zero if the line is a section line,
+ * and contains our section.
+ *
+ * Returns: int
+ */
+static int
+SectionCmp(char *line, char *Section)
+{
+ char TempLabel[MAX_LABEL_LEN], TempValue[MAX_LABEL_LEN];
+
+ switch (ParseLine(line, TempLabel, TempValue)) {
+ case INI_SECTION:
+ return (strcmp(Section, TempLabel));
+ default: /* Not a section */
+ return (-1);
+ }
+
+} /* SectionCmp */
+
+/*
+ * Function: CopyToSection
+ *
+ * Arguments: char *Section, FILE *in, FILE *out, int CopySection
+ *
+ * Description: Copies the files from in to out, until it gets to
+ * the namedSection. If CopySection is non-zero, it
+ * will also copy the section line. If it is zero
+ * the section line will not be copied (for deletion)
+ *
+ * Returns: int (non-zero if the section was found)
+ */
+static int
+CopyToSection(char *Section, FILE *in, FILE *out, int CopySection)
+{
+ char tempbuffer[MAX_VALUE_LEN + MAX_LABEL_LEN];
+
+ while (fgets(tempbuffer, MAX_VALUE_LEN + MAX_LABEL_LEN, in)) {
+ if (!(SectionCmp(tempbuffer, Section))) {
+ /* We have the section */
+ if (CopySection)
+ (void) fputs(tempbuffer, out);
+
+ return (TRUE);
+ }
+ (void) fputs(tempbuffer, out);
+ }
+
+ return (FALSE);
+} /* CopyToSection */
+
+/*
+ * Function: CopyToEOF
+ *
+ * Arguments: FILE *in, FILE *out, char *Label
+ *
+ * Description: Copies from *in to *out, deleting Label in the current
+ * section. If Lable is NULL, then all labels are deleted until
+ * the next section is reached.
+ *
+ * Returns: void
+ */
+static void
+CopyToEOF(FILE *in, FILE *out, char *Label)
+{
+ char tempbuffer[BUF_LEN];
+ int SectionFound = FALSE;
+ char TempLabel[MAX_LABEL_LEN];
+ char TempValue[MAX_VALUE_LEN];
+
+
+ while (fgets(tempbuffer, BUF_LEN - 1, in)) {
+ if (!SectionFound) {
+ switch (ParseLine(tempbuffer, TempLabel, TempValue)) {
+ case INI_SECTION:
+ (void) fputs(tempbuffer, out);
+ SectionFound = TRUE;
+ break;
+ case INI_LABEL:
+ if (Label) {
+ /*
+ * If label is non-null, then copy
+ * everything EXCEPT the given label.
+ */
+ if (strcasecmp(Label, TempLabel)) {
+ (void) fputs(tempbuffer, out);
+ }
+ }
+ break;
+ default:
+ (void) fputs(tempbuffer, out);
+ break;
+ }
+ } else {
+ /*
+ * If the next section has been found,
+ * just write everything
+ */
+ (void) fputs(tempbuffer, out);
+ } /* if SectionFound */
+ } /* end While */
+} /* CopyToEOF */
+
+
+/*
+ * Function: GetSectionList
+ *
+ * Arguments: IniFileType *IniFile, char *argv, int *NumSections,
+ * int *elementSize
+ *
+ * Description: This routine returns a list of sections in a newly
+ * allocated array.
+ *
+ * Returns: char *, and modifies NumSections and elementSize
+ */
+static char *
+GetSectionList(IniFileType *IniFile, char *argv, int *NumSections,
+ int *elementSize)
+{
+ int i;
+
+ /*
+ * Do garbage collection
+ */
+ if (argv) {
+ free(argv);
+ }
+ argv = NULL;
+
+ if (!IniFile->NumSections)
+ return (argv);
+
+ argv = calloc(1, IniFile->NumSections*MAX_FILENAME_LEN);
+ if (!argv) {
+ (void) sprintf(ErrorString,
+ "Out of memory getting section list.");
+
+ return (NULL);
+ }
+ for (i = 0; i < IniFile->NumSections; i++) {
+ (void) strlcpy(&argv[i*MAX_FILENAME_LEN],
+ IniFile->Sections[i].SectionName,
+ MAX_FILENAME_LEN);
+ }
+ *NumSections = IniFile->NumSections;
+ *elementSize = MAX_FILENAME_LEN;
+ return (argv);
+} /* GetSectionList */
+
+/*
+ * Function: IniLoaded
+ *
+ * Arguments: char *Filename
+ *
+ * Description: Returns true if the specified file is loaded and has
+ * not changed.
+ *
+ * Returns: int
+ */
+static int
+IniLoaded(char *Filename)
+{
+ IniFileList *Probe;
+
+ for (Probe = IniHead; Probe; Probe = Probe->Next) {
+ if (strcmp(Filename, Probe->IniFile. Filename) == 0) {
+ if (FileHasChanged(Probe->IniFile.ModificationTime,
+ Filename)) {
+ return (FALSE);
+ } else {
+ return (TRUE);
+ }
+ }
+ }
+ return (FALSE);
+} /* IniLoaded */
+
+
+/*
+ * Function: IniCheckComment
+ *
+ * Arguments: char *buffer
+ *
+ * Description: Returns true if the given line is a comment, or if it is
+ * blank. Also removes any partial-line comments by
+ * replacing the semi-colon with a NULL;
+ *
+ * A comment is of the form :
+ * ; comment
+ * or
+ * # comment
+ *
+ * Returns: int
+ */
+static int
+IniCheckComment(char *buffer)
+{
+ int i;
+
+ for (i = 0; buffer[i] && (buffer[i] != '\n'); i++) {
+ if (buffer[i] == ';' || buffer[i] == '#') {
+ if (i) {
+ /* check for \; or \# */
+ if (buffer[i-1] == '\\')
+ continue;
+ }
+ /*
+ * Otherwise, mark it as a null, and check for an
+ * empty string
+ */
+ buffer[i] = 0;
+ break;
+ }
+ }
+
+ for (i = 0; buffer[i] && (buffer[i] != '\n'); i++) {
+ if (!isspace(buffer[i]))
+ return (0);
+ }
+
+ return (1); /* This is a blank (or commented) line */
+} /* IniCheckComment */
+
+/*
+ * Function: IniUnloadFile
+ *
+ * Arguments: char *Filename
+ *
+ * Description: This routine will remove an ini file from our list,
+ * and free up memory.
+ *
+ * Returns: int (zero on success)
+ */
+static int
+IniUnloadFile(char *Filename)
+{
+ IniFileList *File;
+
+ File = UnlinkFile(Filename);
+ if (!File)
+ return (-1);
+ IniFreeFile(File);
+ return (0);
+} /* IniUnloadFile */
+
+/*
+ * Function: IniFreeFile
+ *
+ * Arguments: IniFileList *File
+ *
+ * Description: Does the garbage collection on a file. (frees all data)
+ *
+ * Returns: void
+ */
+static void
+IniFreeFile(IniFileList *File)
+{
+ int CurrentSection;
+
+ /*
+ * Now, free up all the memory
+ */
+ for (CurrentSection = 0; CurrentSection < File->IniFile.NumSections;
+ CurrentSection++) {
+ /*
+ * Free up label array
+ */
+ if (File->IniFile.Sections[CurrentSection].Labels)
+ free(File->IniFile.Sections[CurrentSection].Labels);
+ }
+
+ /* Now free up the sections */
+ if (File->IniFile.Sections)
+ free(File->IniFile.Sections);
+
+ /* and finally... */
+ free(File);
+
+} /* IniFreeFile */
+
+/*
+ * Function: UnlinkFile
+ *
+ * Arguments: char *Filename
+ *
+ * Description: This routine will remove the file from the linked list, and
+ * return a pointer to it, so it can be freed.
+ *
+ * Returns: IniFileList *
+ */
+static IniFileList *
+UnlinkFile(char *Filename)
+{
+ IniFileList *Probe;
+ IniFileList *PrevProbe = NULL;
+
+ for (Probe = IniHead; Probe; Probe = Probe->Next) {
+ if (strcmp(Filename, Probe->IniFile.Filename) == 0) {
+ if (!PrevProbe) {
+ IniHead = Probe->Next;
+ } else {
+ PrevProbe->Next = Probe->Next;
+ }
+ return (Probe);
+ }
+ PrevProbe = Probe;
+ }
+ return (NULL);
+} /* UnlinkFile */
+
+/*
+ * Function: GetSectionProfileString
+ *
+ * Arguments: char *Section, char *Label, dest, destLen
+ *
+ * Description: This function is part of GetPrivateProfileString. It was
+ * Only Added to improve the C-Style formatting. (Fors got
+ * nested too deep.)
+ *
+ * Returns: static int
+ */
+static int
+GetSectionProfileString(IniFileList *IniFile, char *Section, char *Label,
+ char *dest, int destLen)
+{
+ int i;
+
+ /* We have the file */
+ for (i = 0; i < IniFile->IniFile.NumSections; i++) {
+ if (strcasecmp(Section,
+ IniFile->IniFile.Sections[i].SectionName)
+ == 0) {
+ return (GetLabelProfileString(IniFile,
+ i, Label, dest, destLen));
+ }
+ } /* end section for */
+
+ (void) snprintf(ErrorString, sizeof (ErrorString),
+ "Unable to find section <%s>.", Section);
+
+ return (-1);
+} /* GetSectionProfileString */
+
+/*
+ * Function: GetLabelProfileString
+ *
+ * Arguments: char *Section, char *Label, char *dest, int destLen
+ *
+ * Description: This function is part of GetPrivateProfileString. It was
+ * Only Added to improve the C-Style formatting. (Fors got
+ * nested too deep.)
+ *
+ * Returns: static int
+ */
+static int
+GetLabelProfileString(IniFileList *IniFile, int si, char *Label,
+ char *dest, int destLen)
+{
+ int j;
+
+ /* We have the section */
+ for (j = 0;
+ j < IniFile->IniFile.Sections[si].NumLabels;
+ j++) {
+ if (strcasecmp(Label,
+ IniFile->IniFile.Sections[si].Labels[j].Label) == 0) {
+ /* We finally have it! */
+ (void) strncpy(dest,
+ IniFile->IniFile.Sections[si].Labels[j].Value,
+ destLen);
+ return (0);
+ }
+ } /* end label for */
+
+ (void) snprintf(ErrorString, sizeof (ErrorString),
+ "Unable to find label <%s>.", Label);
+
+ return (-1);
+} /* GetLabelProfileString */
+
+/*
+ *
+ * E X T E R N A L F U N C T I O N S
+ *
+ * These functions are called by external programs. (They are non-static)
+ *
+ */
+
+
+
+/*
+ * Function: WritePrivateProfileString
+ *
+ * Arguments: char *Section, char *Label, char *data
+ *
+ * Description: Sets the new string into [Section] Label=data
+ *
+ * Returns: int (zero on success)
+ */
+int
+WritePrivateProfileString(char *Section, char *Label, char *data,
+ char *Filename)
+{
+ /*
+ * Unload the file, if it is loaded. The next read will load it.
+ */
+
+ return (IniUpdateFile(Filename, Section, Label, data));
+} /* WritePrivateProfileString */
+
+/*
+ * Function: DeletePrivateProfileSection
+ *
+ * Arguments: char *Section, char *Filename
+ *
+ * Description: Deletes an entire section (and all labels)
+ *
+ * Returns: int (zero on success)
+ */
+int
+DeletePrivateProfileSection(char *Section, char *Filename)
+{
+ return (IniUpdateFile(Filename, Section, NULL, NULL));
+} /* DeletePrivateProfileSection*/
+
+/*
+ * Function: DeletePrivateProfileLabel
+ *
+ * Arguments: char *Section, char *Label, char *Filename
+ *
+ * Description: Deletes a label from a section.
+ *
+ * Returns: int (zero on success)
+ */
+int
+DeletePrivateProfileLabel(char *Section, char *Label, char *Filename)
+{
+ return (IniUpdateFile(Filename, Section, Label, NULL));
+} /* DeletePrivateProfileLabel */
+
+/*
+ * Function: WritePrivateProfileInt
+ *
+ * Arguments: char *Section, char *Label, int data, char *Filename
+ *
+ * Description: Sets the new int into [Section] Label=data
+ *
+ * Returns: int (zero on success)
+ */
+int
+WritePrivateProfileInt(char *Section, char *Label, int data, char *Filename)
+{
+ char Tempbuffer[MAX_LONG_LEN]; /* we only need 10 + a negative sign */
+
+ (void) sprintf(Tempbuffer, "%d", data);
+ return (IniUpdateFile(Filename, Section, Label, Tempbuffer));
+} /* WritePrivateProfileInt */
+
+/*
+ * Function: GetPrivateProfileString
+ *
+ * Arguments: char *Section, char *Label, char *defaultValue
+ *
+ * Description: retrieves data from a section
+ *
+ * Returns: int (zero on success)
+ */
+int
+GetPrivateProfileString(char *Section, char *Label, char *defaultValue,
+ char *dest, int destLen, char *Filename)
+{
+ IniFileList *IniFile;
+ char errMsg[ERROR_SUBSTRING_LEN];
+ int rc;
+
+ /*
+ * First, Lookup the file, then lookup the section,
+ * then lookup the label, then copy the string
+ */
+
+ ErrorString[0] = 0; /* Clear the error string */
+
+ /* set default value */
+ (void) strlcpy(dest, defaultValue, destLen);
+
+ if (!IniLoaded(Filename)) {
+ rc = IniLoadFile(Filename);
+ if (rc < 0) {
+ return (rc);
+ }
+ }
+
+ for (IniFile = IniHead; IniFile; IniFile = IniFile->Next) {
+ if (strcmp(Filename, IniFile->IniFile.Filename) == 0)
+ return (GetSectionProfileString(IniFile, Section,
+ Label, dest, destLen));
+ } /* end file for */
+
+ /*
+ * IniLoaded() and IniLoadFile() call FileHasChanged() which may have
+ * something to say via ErrorString - don't overwrite it!
+ */
+ (void) snprintf(errMsg, ERROR_SUBSTRING_LEN,
+ "Unable to find file <%s>. ", Filename);
+
+ strlcat(ErrorString, errMsg, sizeof (ErrorString));
+
+ return (-1);
+} /* GetPrivateProfileString */
+
+/*
+ * Function: GetPrivateProfileInt
+ *
+ * Arguments: char *Section, char *Label, int defaultValue
+ *
+ * Description: Returns the int associated with [Section], Label=Int
+ *
+ * Returns: int
+ */
+int
+GetPrivateProfileInt(char *Section, char *Label, int defaultValue,
+ char *Filename)
+{
+ char String[MAX_LONG_LEN];
+
+ (void) GetPrivateProfileString(Section, Label, "", String,
+ MAX_LONG_LEN, Filename);
+
+ if (!String[0]) /* Our default return value is "" */
+ return (defaultValue);
+ else
+ return (atoi(String));
+} /* GetPrivateProfileInt */
+
+/*
+ * Function: IniListSections
+ *
+ * Arguments: char *Filename, int *NumSections, int *elementSize
+ *
+ * Description: Returns a list of sections. (allocates an array)
+ *
+ * Returns: char *
+ */
+char *
+IniListSections(char *Filename, int *NumSections, int *elementSize)
+{
+ static char *argv = NULL;
+ IniFileList *IniFile;
+ char errMsg[ERROR_SUBSTRING_LEN];
+ int rc;
+
+ if (!IniLoaded(Filename)) {
+ rc = IniLoadFile(Filename);
+ if (rc < 0) {
+ return (NULL);
+ }
+ }
+ for (IniFile = IniHead; IniFile; IniFile = IniFile->Next) {
+ if (strcmp(Filename, IniFile->IniFile.Filename) == 0) {
+ argv = GetSectionList(&IniFile->IniFile, argv,
+ NumSections, elementSize);
+ return (argv);
+ }
+ }
+
+ /*
+ * IniLoaded() and IniLoadFile() call FileHasChanged() which may have
+ * something to say via ErrorString - don't overwrite it!
+ */
+ (void) snprintf(errMsg, sizeof (ErrorString),
+ "Unable to find file <%s>. ", Filename);
+
+ strlcat(ErrorString, errMsg, sizeof (ErrorString));
+
+ return (NULL);
+} /* IniListSections */
+
+
+/*
+ * Function: parsealg
+ *
+ * Arguments: char *algname, struct ipsec_alg *table
+ *
+ * Description: finds the ipsec algorithm value associated with the ipsec
+ * algorithm string.
+ *
+ * Returns: valid alg value on success, -1 on failure.
+ */
+int
+parsealg(char *algname, struct ipsec_alg *table)
+{
+ struct ipsec_alg *ep;
+
+ if ((algname == NULL) || (table == NULL))
+ return (-1);
+
+ for (ep = table; ep->alg != NULL; ep++) {
+ if (strcasecmp(ep->alg, algname) == 0)
+ return (ep->value);
+ }
+
+ /* no match */
+ return (-1);
+}
+
+
+/*
+ * Function: parse_algs
+ *
+ * Arguments: int which_alg, int alg, ipsec_req_t *ipsr
+ *
+ * Description: sets the values in the ipsec_req_t passed in based on
+ * the algorithm, and which protection scheme it belongs to (ah_auth,
+ * esp_auth, or esp_encr. SA is also passed through here as a sanity
+ * check measure, and because of the way the parsing routines are
+ * designed - the user specifies all this as part of a the properties
+ * of a single IPsec policy.
+ *
+ * Returns: valid alg value on success, -1 on failure.
+ */
+int
+parse_algs(int which_alg, int alg, ipsec_req_t *ipsr)
+{
+ if (alg == -1)
+ /* alg is bad */
+ return (-1);
+
+ if (ipsr == NULL)
+ /* user didn't pass a pointer to set, alg is valid, OK */
+ return (0);
+
+ switch (which_alg) {
+ case ESP_ENCR_ALG:
+ if (alg == NO_ESP_AALG) {
+ if (ipsr->ipsr_esp_auth_alg == SADB_AALG_NONE)
+ ipsr->ipsr_esp_req = 0;
+ ipsr->ipsr_esp_alg = SADB_EALG_NONE;
+ } else {
+ ipsr->ipsr_esp_req =
+ IPSEC_PREF_REQUIRED | IPSEC_PREF_UNIQUE;
+ ipsr->ipsr_esp_alg = alg;
+ }
+ break;
+
+ case ESP_AUTH_ALG:
+ if (alg == NO_ESP_AALG) {
+ if (ipsr->ipsr_esp_alg == SADB_EALG_NONE ||
+ ipsr->ipsr_esp_alg == SADB_EALG_NULL)
+ ipsr->ipsr_esp_req = 0;
+ ipsr->ipsr_esp_auth_alg = SADB_AALG_NONE;
+ } else {
+ ipsr->ipsr_esp_req =
+ IPSEC_PREF_REQUIRED | IPSEC_PREF_UNIQUE;
+ ipsr->ipsr_esp_auth_alg = alg;
+
+ /* Let the user specify NULL encryption implicitly. */
+ if (ipsr->ipsr_esp_alg == SADB_EALG_NONE &&
+ !encr_alg_set)
+ ipsr->ipsr_esp_alg = SADB_EALG_NONE;
+ }
+ break;
+
+ case AH_AUTH_ALG:
+ if (alg == NO_AH_AALG) {
+ ipsr->ipsr_ah_req = 0;
+ ipsr->ipsr_auth_alg = SADB_AALG_NONE;
+ } else {
+ ipsr->ipsr_ah_req =
+ IPSEC_PREF_REQUIRED | IPSEC_PREF_UNIQUE;
+ ipsr->ipsr_auth_alg = alg;
+ }
+ break;
+
+ case SA_ALG:
+ if ((alg != SADB_SA_SHARED) &&
+ (alg != SADB_SA_UNIQUE))
+ return (-1);
+ break;
+
+ default:
+ /* don't know what to do! Fail. */
+ return (-1);
+ }
+ return (0);
+}
+
+/* ipsec algorithm parsing routines */
+int
+parse_esp_encr_alg(char *which_alg, ipsec_req_t *ipsr)
+{
+ return (parse_algs(ESP_ENCR_ALG, parsealg(which_alg, encr_algs), ipsr));
+}
+
+int
+parse_esp_auth_alg(char *which_alg, ipsec_req_t *ipsr)
+{
+ return (parse_algs(ESP_AUTH_ALG, parsealg(which_alg, auth_algs), ipsr));
+}
+
+int
+parse_ah_alg(char *which_alg, ipsec_req_t *ipsr)
+{
+ return (parse_algs(AH_AUTH_ALG, parsealg(which_alg, auth_algs), ipsr));
+}
+
+int
+parse_sa_alg(char *which_alg, ipsec_req_t *ipsr)
+{
+ return (parse_algs(SA_ALG, parsealg(which_alg, sa_algs), ipsr));
+}
+
+
+/*
+ * Function: isIPsecActionValid
+ *
+ * Parameters: action - the string containing the action in question.
+ *
+ * Description: Verifies the action is a valid ipsec action. Note that this
+ * function is NOT tollerant to leading whitespace!
+ *
+ * Returns: 0 on failure.
+ * strlen of the action on success (so the caller can index past it).
+ */
+uint_t
+isIPsecActionValid(char *action)
+{
+ int i;
+ char *cp = action;
+
+ for (i = 0; validIPsecAction[i] != NULL; i++) {
+ /* match check */
+ if (strncasecmp(cp, validIPsecAction[i],
+ strlen(validIPsecAction[i])) == 0)
+ return (strlen(validIPsecAction[i]));
+
+ /* try the next action */
+ }
+
+ return (0);
+}
+
+/*
+ * Function: parseIPsecProps
+ *
+ * Parameters: props - the string containing "{<props>}" for validation.
+ * ipsr - place to put parsed policy, NULL if not required.
+ * In this case parseIPsecProps() just tests to see
+ * if props are valid.
+ *
+ * Description: Verify properties are valid, and if so, put their values
+ * in the struct ipsec_req_t passed in. Valid properties are
+ * of the form "<tag> <alg>". Supported <tags> are in ipseccmd[],
+ * and the corresponding algs are in auth_algs[], and encr_algs[].
+ * Note: this function is tollerant to properties begining with
+ * blank spaces, but an open bracket MUST preceed <tag><alg>.
+ *
+ * Returns: 0 on failure in which case the values in ipsr are incomplete.
+ * number of bytes parsed on success in which case the values in ipsr,
+ * if passed in, are complete.
+ */
+int
+parseIPsecProps(char *props, ipsec_req_t *ipsr)
+{
+ struct ipsec_cmd *cmd;
+ char *p, *freeP, *deltaP = 0, *lasts;
+ int bytes_parsed = 0, ioff = 0, ret;
+ boolean_t bopen_found = _B_FALSE, bclose_found = _B_FALSE;
+
+ p = strdup(props);
+
+ if (p == NULL)
+ return (0);
+
+ freeP = p; /* strtok_r() is destructive */
+
+ while ((p = strtok_r(p, " ", &lasts)) != NULL) {
+ /*
+ * What did strtok_r() return if we have " { <properties..."?
+ * Skip white-space in general. ^^^
+ */
+ if ((*p != '\0') && (isspace(*p))) {
+ deltaP = p + strlen(p);
+ p = NULL;
+ continue;
+ }
+
+ if (!bopen_found) {
+ /* Every property begins with an open-bracket */
+ if (*p != '{') {
+ free(freeP);
+ return (0);
+ }
+ bopen_found = _B_TRUE;
+ /* move on */
+ p++;
+ }
+
+ if (*p == '}') {
+ /* this means we should be done */
+ if ((!bopen_found) || (bclose_found)) {
+ free(freeP);
+ return (0);
+ }
+
+ bclose_found = _B_TRUE;
+
+ deltaP = p + strlen(p);
+
+ p = NULL;
+ continue;
+ }
+
+ if (bclose_found) {
+ /* Stuff after the close! */
+ free(freeP);
+ return (0);
+ }
+
+ /* once for each <tag> the user wants set */
+ for (cmd = ipseccmd; cmd->c_func != NULL; cmd++) {
+ /* valid <tag>? */
+ if ((cmd->c_name) &&
+ (strcasecmp(cmd->c_name, p) == 0)) {
+ /* <alg> follows <tag>> */
+ p = NULL;
+ p = strtok_r(p, " ", &lasts);
+
+ if (p == NULL) {
+ /* nothing follows <tag> = error */
+ free(freeP);
+ return (0);
+ }
+
+ /* last <alg> may have a "}" at the end */
+ if (strcmp(&p[strlen(p)-1], "}") == 0) {
+ if (bclose_found) {
+ /* already found one = fail */
+ free(freeP);
+ return (0);
+ }
+ bclose_found = _B_TRUE;
+
+ /* char shortcuts are cool */
+ p[strlen(p)-1] = NULL;
+ ioff = 1;
+ }
+
+ /*
+ * Call the parse command for this <alg>, but
+ * only if we're filling in the ipsec_req_t.
+ */
+ if (ipsr == NULL)
+ /* make sure it's valid in context */
+ ret = parsealg(p, encr_algs);
+ else
+ /* also parse it into ipsr */
+ ret = (*cmd->c_func)(p, ipsr);
+
+ if (ret < 0) {
+ /* the parse command failed */
+ free(freeP);
+ return (0);
+ } else {
+ /* how far have we've come */
+ deltaP = p + strlen(p) + ioff;
+
+ /* parsed->next */
+ p = NULL;
+ break;
+ }
+ }
+ }
+
+ /* cmd->c_func = NULL, next alg */
+ p = NULL;
+ }
+
+ /* calculate how many bytes we've parsed */
+ bytes_parsed = deltaP - freeP;
+
+ /* finished with this */
+ free(freeP);
+
+ /* reset the per-policy parsing boolean */
+ encr_alg_set = FALSE;
+
+ /* as long as we got to a valid close bracket, we're fine */
+ if (!bclose_found)
+ return (0);
+
+ return (bytes_parsed);
+}
+
+/*
+ * Function:isIPsecPolicyValid
+ *
+ * Arguments: policy what the user is trying to set as policy properties.
+ *
+ * Description: This function validates an IPSec policy. The policy MUST be
+ * a single valid action, followed by a valid set of properties
+ * as specified by ipsec(7P). Note that we don't necessarily
+ * support all the actions that ipsec supports, see
+ * validIPsecActions[].
+ *
+ * Returns: _B_TRUE if the policy is valid, _B_FALSE if the policy is not.
+ *
+ * Note: this function only exists because ipSec has no API at this time.
+ */
+boolean_t
+isIPsecPolicyValid(char *policy, ipsec_req_t *ipsr)
+{
+ int c_len = 0;
+ char *cp;
+
+ if (policy == NULL)
+ return (_B_FALSE);
+
+ /* Parse the policy */
+ cp = policy;
+
+ /* actions first */
+ if ((c_len = isIPsecActionValid(cp)) == 0)
+ return (_B_FALSE);
+
+ cp += c_len;
+
+ while (isspace(*cp))
+ cp++;
+
+ /* now "{<properties>}" */
+ if ((c_len = parseIPsecProps(cp, ipsr)) == 0)
+ return (_B_FALSE);
+
+ cp += c_len;
+
+ /* trailing blanks are OK... */
+ while (isspace(*cp))
+ cp++;
+
+ /* ...but nothing else! */
+ return (*cp == '\0');
+}
diff --git a/usr/src/cmd/cmd-inet/common/conflib.h b/usr/src/cmd/cmd-inet/common/conflib.h
new file mode 100644
index 0000000000..a6ad92e964
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/common/conflib.h
@@ -0,0 +1,122 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _CONFLIB_H
+#define _CONFLIB_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * conflib.h -- Prototypes and defines for conflib.c
+ * WARNING: This code assumes that an int is 32 bits.
+ */
+
+#include <netinet/in.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Static lengths of values. */
+#define MAX_LABEL_LEN 256
+#define MAX_SECTION_LEN 256
+#define MAX_VALUE_LEN 256
+#define MAX_FILENAME_LEN 256
+
+/* Error string readable by external programs */
+#define ERROR_SUBSTRING_LEN 256
+#define ERROR_STRING_LEN (MAX_FILENAME_LEN + ERROR_SUBSTRING_LEN)
+extern char ErrorString[];
+
+/*
+ * Check if OS has defined true or false. This code is portable between
+ * several OSs.
+ */
+#ifndef TRUE
+#define TRUE 1
+#define FALSE 0
+#endif
+
+
+/* these are shared between mipagent and mipagentconfig */
+
+/* IPsec flags - what's in place to protect datagrams to/from an agent peer. */
+#define IPSEC_REQUEST_AH 0x01 /* regRequest using AH */
+#define IPSEC_REQUEST_ESP 0x02 /* regRequest using ESP */
+#define IPSEC_REPLY_AH 0x04 /* regReply using AH */
+#define IPSEC_REPLY_ESP 0x08 /* regReply using ESP */
+#define IPSEC_TUNNEL_AH 0x10 /* forward tunnel using AH */
+#define IPSEC_TUNNEL_ESP 0x20 /* forward tunnel using ESP */
+#define IPSEC_REVERSE_TUNNEL_AH 0x40 /* reverse tunnel using AH */
+#define IPSEC_REVERSE_TUNNEL_ESP 0x80 /* reverse tunnel using ESP */
+
+/* useful combinations */
+#define IPSEC_REQUEST_BOTH (IPSEC_REQUEST_AH | IPSEC_REQUEST_ESP)
+#define IPSEC_REPLY_BOTH (IPSEC_REPLY_AH | IPSEC_REPLY_ESP)
+#define IPSEC_TUNNEL_BOTH (IPSEC_TUNNEL_AH | IPSEC_TUNNEL_ESP)
+#define IPSEC_REVERSE_TUNNEL_BOTH \
+ (IPSEC_REVERSE_TUNNEL_AH | IPSEC_REVERSE_TUNNEL_ESP)
+
+/* useful for checking if there's a policy that should be invoked */
+#define IPSEC_REQUEST_ANY(x) ((x) & (IPSEC_REQUEST_BOTH))
+#define IPSEC_REPLY_ANY(x) ((x) & (IPSEC_REPLY_BOTH))
+#define IPSEC_TUNNEL_ANY(x) ((x) & (IPSEC_TUNNEL_BOTH))
+#define IPSEC_REVERSE_TUNNEL_ANY(x) ((x) & (IPSEC_REVERSE_TUNNEL_BOTH))
+
+/* useful for checking when user is requesting something that's not offered */
+#define IPSEC_ANY_AH(x) ((x) & (IPSEC_REQUEST_AH |\
+ IPSEC_REPLY_AH |\
+ IPSEC_TUNNEL_AH |\
+ IPSEC_REVERSE_TUNNEL_AH))
+
+#define IPSEC_ANY_ESP(x) ((x) & (IPSEC_REQUEST_ESP |\
+ IPSEC_REPLY_ESP |\
+ IPSEC_TUNNEL_ESP |\
+ IPSEC_REVERSE_TUNNEL_ESP))
+
+/* how policies are deliniated in CONF_FILE_NAME */
+#define IPSP_SEPARATOR ":"
+
+/* these functions are necessary to share IPsec functionality */
+int parseIPsecProps(char *, ipsec_req_t *);
+boolean_t isIPsecPolicyValid(char *, ipsec_req_t *);
+
+/* These functions mimic the Windows(tm) equivilants */
+int WritePrivateProfileString(char *, char *, char *, char *);
+int WritePrivateProfileInt(char *, char *, int, char *);
+int GetPrivateProfileString(char *, char *, char *, char *, int, char *);
+int GetPrivateProfileInt(char *, char *, int, char *);
+
+/* These functions were necessary for extra usability */
+char *IniListSections(char *, int *, int *);
+int DeletePrivateProfileLabel(char *, char *, char *);
+int DeletePrivateProfileSection(char *, char *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _CONFLIB_H */
diff --git a/usr/src/cmd/cmd-inet/common/ifaddrlist.c b/usr/src/cmd/cmd-inet/common/ifaddrlist.c
new file mode 100644
index 0000000000..91638f1995
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/common/ifaddrlist.c
@@ -0,0 +1,218 @@
+/*
+ * Copyright 1997,2002-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the Computer Systems
+ * Engineering Group at Lawrence Berkeley Laboratory.
+ * 4. Neither the name of the University nor of the Laboratory may be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#) $Header: ifaddrlist.c,v 1.2 97/04/22 13:31:05 leres Exp $ (LBL)
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/time.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <memory.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <libintl.h>
+
+#include "ifaddrlist.h"
+
+/*
+ * Construct the interface list with given address family.
+ * If it fails, returns -1 and an error message in *errbuf;
+ * otherwise, returns number of interfaces, and the interface list in *ipaddrp.
+ */
+int
+ifaddrlist(struct ifaddrlist **ipaddrp, int family, char *errbuf)
+{
+ int fd;
+ struct lifreq *lifrp, *lifend;
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+ struct ifaddrlist *al;
+ struct lifconf lifc;
+ struct lifreq *ibuf, lifr;
+ char device[LIFNAMSIZ + 1];
+ struct ifaddrlist *ifaddrlist;
+ struct lifnum lifn;
+ int lifc_flags = 0;
+ int count;
+
+ if (family != AF_INET && family != AF_INET6) {
+ (void) sprintf(errbuf, "invalid address family");
+ return (-1);
+ }
+
+ fd = socket(family, SOCK_DGRAM, 0);
+ if (fd < 0) {
+ (void) snprintf(errbuf, ERRBUFSIZE, "socket: %s",
+ strerror(errno));
+ return (-1);
+ }
+
+ /* determine the number of interfaces */
+ lifn.lifn_family = family;
+ lifn.lifn_flags = lifc_flags;
+ if (ioctl(fd, SIOCGLIFNUM, &lifn) < 0) {
+ (void) snprintf(errbuf, ERRBUFSIZE, "SIOCGLIFNUM: %s",
+ strerror(errno));
+ (void) close(fd);
+ return (-1);
+ }
+
+ /* allocate memory for the determined number of interfaces */
+ ifaddrlist = calloc((size_t)lifn.lifn_count,
+ (size_t)sizeof (struct ifaddrlist));
+ if (ifaddrlist == NULL) {
+ (void) snprintf(errbuf, ERRBUFSIZE, "calloc: %s",
+ strerror(errno));
+ (void) close(fd);
+ return (-1);
+ }
+
+ ibuf = calloc((size_t)lifn.lifn_count, (size_t)sizeof (struct lifreq));
+ if (ibuf == NULL) {
+ (void) snprintf(errbuf, ERRBUFSIZE, "calloc: %s",
+ strerror(errno));
+ free(ifaddrlist);
+ (void) close(fd);
+ return (-1);
+ }
+
+ /* pull out the interface list from the kernel */
+ lifc.lifc_family = family;
+ lifc.lifc_len = (int)(lifn.lifn_count * sizeof (struct lifreq));
+ lifc.lifc_buf = (caddr_t)ibuf;
+ lifc.lifc_flags = lifc_flags;
+
+ if (ioctl(fd, SIOCGLIFCONF, (char *)&lifc) < 0 ||
+ lifc.lifc_len < sizeof (struct lifreq)) {
+ (void) snprintf(errbuf, ERRBUFSIZE, "SIOCGLIFCONF: %s",
+ strerror(errno));
+ free(ifaddrlist);
+ free(ibuf);
+ (void) close(fd);
+ return (-1);
+ }
+
+ lifrp = ibuf;
+ /* LINTED */
+ lifend = (struct lifreq *)((char *)ibuf + lifc.lifc_len);
+
+ al = ifaddrlist;
+ count = 0;
+
+ /* let's populate the interface entries in the ifaddrlist */
+ for (; lifrp < lifend; lifrp++) {
+ /*
+ * Need a template to preserve address info that is
+ * used below to locate the next entry. (Otherwise,
+ * SIOCGLIFFLAGS stomps over it because the requests
+ * are returned in a union.)
+ */
+ (void) strlcpy(lifr.lifr_name, lifrp->lifr_name,
+ sizeof (lifr.lifr_name));
+ if (ioctl(fd, SIOCGLIFFLAGS, (char *)&lifr) < 0) {
+ if (errno == ENXIO) {
+ continue;
+ }
+ (void) snprintf(errbuf, ERRBUFSIZE,
+ "SIOCGLIFFLAGS: %.*s: %s",
+ (int)sizeof (lifr.lifr_name), lifr.lifr_name,
+ strerror(errno));
+ free(ifaddrlist);
+ free(ibuf);
+ (void) close(fd);
+ return (-1);
+ }
+
+ al->flags = lifr.lifr_flags;
+
+ /* get the interface address */
+ (void) strncpy(device, lifr.lifr_name, sizeof (device));
+ device[sizeof (device) - 1] = '\0';
+ if (ioctl(fd, SIOCGLIFADDR, (char *)&lifr) < 0) {
+ (void) snprintf(errbuf, ERRBUFSIZE,
+ "SIOCGLIFADDR: %s: %s", device, strerror(errno));
+ free(ifaddrlist);
+ free(ibuf);
+ (void) close(fd);
+ return (-1);
+ }
+
+ if (family == AF_INET) {
+ sin = (struct sockaddr_in *)&lifr.lifr_addr;
+ al->addr.addr = sin->sin_addr;
+ } else {
+ sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
+ al->addr.addr6 = sin6->sin6_addr;
+ }
+
+ (void) strlcpy(al->device, device, sizeof (device));
+
+ /* get the interface index */
+ if (ioctl(fd, SIOCGLIFINDEX, (char *)&lifr) < 0) {
+ (void) snprintf(errbuf, ERRBUFSIZE,
+ "SIOCGLIFADDR: %s: %s", device, strerror(errno));
+ free(ifaddrlist);
+ free(ibuf);
+ (void) close(fd);
+ return (-1);
+ }
+
+ al->index = lifr.lifr_index;
+
+ ++al;
+ ++count;
+ }
+
+ free(ibuf);
+ (void) close(fd);
+
+ *ipaddrp = ifaddrlist;
+ return (count);
+}
diff --git a/usr/src/cmd/cmd-inet/common/ifaddrlist.h b/usr/src/cmd/cmd-inet/common/ifaddrlist.h
new file mode 100644
index 0000000000..d413aac289
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/common/ifaddrlist.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 1997 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+
+/*
+ * Copyright (c) 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * @(#) $Header: traceroute.h,v 1.1 97/01/04 19:33:33 leres Locked $ (LBL)
+ */
+
+
+#ifndef _IFADDRLIST_H
+#define _IFADDRLIST_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <net/if.h>
+
+#define ERRBUFSIZE 128
+
+/* can store both IPv4 and IPv6 address */
+union any_in_addr {
+ struct in6_addr addr6;
+ struct in_addr addr;
+};
+
+struct ifaddrlist {
+ int index; /* interface index */
+ union any_in_addr addr; /* interface address */
+ char device[LIFNAMSIZ + 1]; /* interface name */
+ uint64_t flags; /* interface flags */
+};
+
+int ifaddrlist(struct ifaddrlist **, int, char *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _IFADDRLIST_H */
diff --git a/usr/src/cmd/cmd-inet/common/kcmd.c b/usr/src/cmd/cmd-inet/common/kcmd.c
new file mode 100644
index 0000000000..16c3f1b7f5
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/common/kcmd.c
@@ -0,0 +1,811 @@
+/*
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* derived from @(#)rcmd.c 5.17 (Berkeley) 6/27/88 */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <pwd.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <fcntl.h>
+
+#include <signal.h>
+#include <sys/file.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <locale.h>
+#include <syslog.h>
+
+#include <errno.h>
+#include <com_err.h>
+#include <k5-int.h>
+#include <kcmd.h>
+
+static char *default_service = "host";
+
+#define KCMD_BUFSIZ 102400
+#define KCMD_KEYUSAGE 1026
+
+static char storage[KCMD_BUFSIZ];
+static int nstored = 0;
+static int MAXSIZE = (KCMD_BUFSIZ + 8);
+static char *store_ptr = storage;
+static krb5_data desinbuf, desoutbuf;
+
+static boolean_t encrypt_flag = B_FALSE;
+static krb5_context kcmd_context;
+
+/* XXX Overloaded: use_ivecs!=0 -> new protocol, inband signalling, etc. */
+static boolean_t use_ivecs = B_FALSE;
+static krb5_data encivec_i[2], encivec_o[2];
+static krb5_keyusage enc_keyusage_i[2], enc_keyusage_o[2];
+static krb5_enctype final_enctype;
+static krb5_keyblock *skey;
+
+/* ARGSUSED */
+int
+kcmd(int *sock, char **ahost, ushort_t rport,
+ char *locuser, char *remuser,
+ char *cmd, int *fd2p, char *service, char *realm,
+ krb5_context bsd_context, krb5_auth_context *authconp,
+ krb5_creds **cred, krb5_int32 *seqno, krb5_int32 *server_seqno,
+ krb5_flags authopts,
+ int anyport, enum kcmd_proto *protonump)
+{
+ int s = -1;
+ sigset_t oldmask, urgmask;
+ struct sockaddr_in sin;
+ struct sockaddr_storage from;
+ krb5_creds *get_cred = NULL;
+ krb5_creds *ret_cred = NULL;
+ char c;
+ struct hostent *hp;
+ int rc;
+ char *host_save = NULL;
+ krb5_error_code status;
+ krb5_ap_rep_enc_part *rep_ret;
+ krb5_error *error = 0;
+ krb5_ccache cc;
+ krb5_data outbuf;
+ krb5_flags options = authopts;
+ krb5_auth_context auth_context = NULL;
+ char *cksumbuf;
+ krb5_data cksumdat;
+ int bsize = 0;
+ char *kcmd_version;
+ enum kcmd_proto protonum = *protonump;
+
+ bsize = strlen(cmd) + strlen(remuser) + 64;
+ if ((cksumbuf = malloc(bsize)) == 0) {
+ (void) fprintf(stderr, gettext("Unable to allocate"
+ " memory for checksum buffer.\n"));
+ return (-1);
+ }
+ (void) snprintf(cksumbuf, bsize, "%u:", ntohs(rport));
+ if (strlcat(cksumbuf, cmd, bsize) >= bsize) {
+ (void) fprintf(stderr, gettext("cmd buffer too long.\n"));
+ free(cksumbuf);
+ return (-1);
+ }
+ if (strlcat(cksumbuf, remuser, bsize) >= bsize) {
+ (void) fprintf(stderr, gettext("remuser too long.\n"));
+ free(cksumbuf);
+ return (-1);
+ }
+ cksumdat.data = cksumbuf;
+ cksumdat.length = strlen(cksumbuf);
+
+ hp = gethostbyname(*ahost);
+ if (hp == 0) {
+ (void) fprintf(stderr,
+ gettext("%s: unknown host\n"), *ahost);
+ return (-1);
+ }
+
+ if ((host_save = (char *)strdup(hp->h_name)) == NULL) {
+ (void) fprintf(stderr, gettext("kcmd: no memory\n"));
+ return (-1);
+ }
+
+ /* If no service is given set to the default service */
+ if (!service) service = default_service;
+
+ if (!(get_cred = (krb5_creds *)calloc(1, sizeof (krb5_creds)))) {
+ (void) fprintf(stderr, gettext("kcmd: no memory\n"));
+ return (-1);
+ }
+ (void) sigemptyset(&urgmask);
+ (void) sigaddset(&urgmask, SIGURG);
+ (void) sigprocmask(SIG_BLOCK, &urgmask, &oldmask);
+
+ status = krb5_sname_to_principal(bsd_context, host_save, service,
+ KRB5_NT_SRV_HST, &get_cred->server);
+ if (status) {
+ (void) fprintf(stderr,
+ gettext("kcmd: "
+ "krb5_sname_to_principal failed: %s\n"),
+ error_message(status));
+ status = -1;
+ goto bad;
+ }
+
+ if (realm && *realm) {
+ (void) krb5_xfree(
+ krb5_princ_realm(bsd_context, get_cred->server)->data);
+ krb5_princ_set_realm_length(bsd_context, get_cred->server,
+ strlen(realm));
+ krb5_princ_set_realm_data(bsd_context, get_cred->server,
+ strdup(realm));
+ }
+
+ s = socket(AF_INET, SOCK_STREAM, 0);
+ if (s < 0) {
+ perror(gettext("Error creating socket"));
+ status = -1;
+ goto bad;
+ }
+ /*
+ * Kerberos only supports IPv4 addresses for now.
+ */
+ if (hp->h_addrtype == AF_INET) {
+ sin.sin_family = hp->h_addrtype;
+ (void) memcpy((void *)&sin.sin_addr,
+ hp->h_addr, hp->h_length);
+ sin.sin_port = rport;
+ } else {
+ syslog(LOG_ERR, "Address type %d not supported for "
+ "Kerberos", hp->h_addrtype);
+ status = -1;
+ goto bad;
+ }
+
+ if (connect(s, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
+ perror(host_save);
+ status = -1;
+ goto bad;
+ }
+
+ if (fd2p == 0) {
+ (void) write(s, "", 1);
+ } else {
+ char num[16];
+ int s2;
+ int s3;
+ struct sockaddr_storage sname;
+ struct sockaddr_in *sp;
+ int len = sizeof (struct sockaddr_storage);
+
+ s2 = socket(AF_INET, SOCK_STREAM, 0);
+ if (s2 < 0) {
+ status = -1;
+ goto bad;
+ }
+ (void) memset((char *)&sin, 0, sizeof (sin));
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = INADDR_ANY;
+ sin.sin_port = 0;
+
+ if (bind(s2, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
+ perror(gettext("error binding socket"));
+ (void) close(s2);
+ status = -1;
+ goto bad;
+ }
+ if (getsockname(s2, (struct sockaddr *)&sname, &len) < 0) {
+ perror(gettext("getsockname error"));
+ (void) close(s2);
+ status = -1;
+ goto bad;
+ }
+ sp = (struct sockaddr_in *)&sname;
+ (void) listen(s2, 1);
+ (void) snprintf(num, sizeof (num), "%d",
+ htons((ushort_t)sp->sin_port));
+ if (write(s, num, strlen(num)+1) != strlen(num)+1) {
+ perror(gettext("write: error setting up stderr"));
+ (void) close(s2);
+ status = -1;
+ goto bad;
+ }
+
+ s3 = accept(s2, (struct sockaddr *)&from, &len);
+ (void) close(s2);
+ if (s3 < 0) {
+ perror(gettext("accept"));
+ status = -1;
+ goto bad;
+ }
+ *fd2p = s3;
+ if (SOCK_FAMILY(from) == AF_INET) {
+ if (!anyport && SOCK_PORT(from) >= IPPORT_RESERVED) {
+ (void) fprintf(stderr,
+ gettext("socket: protocol "
+ "failure in circuit setup.\n"));
+ status = -1;
+ goto bad2;
+ }
+ } else {
+ (void) fprintf(stderr,
+ gettext("Kerberos does not support "
+ "address type %d\n"),
+ SOCK_FAMILY(from));
+ status = -1;
+ goto bad2;
+ }
+ }
+
+ if (status = krb5_cc_default(bsd_context, &cc))
+ goto bad2;
+
+ status = krb5_cc_get_principal(bsd_context, cc, &get_cred->client);
+ if (status) {
+ (void) krb5_cc_close(bsd_context, cc);
+ goto bad2;
+ }
+
+ /* Get ticket from credentials cache or kdc */
+ status = krb5_get_credentials(bsd_context, 0, cc, get_cred, &ret_cred);
+ (void) krb5_cc_close(bsd_context, cc);
+ if (status) goto bad2;
+
+ /* Reset internal flags; these should not be sent. */
+ authopts &= (~OPTS_FORWARD_CREDS);
+ authopts &= (~OPTS_FORWARDABLE_CREDS);
+
+ if ((status = krb5_auth_con_init(bsd_context, &auth_context)))
+ goto bad2;
+
+ if ((status = krb5_auth_con_setflags(bsd_context, auth_context,
+ KRB5_AUTH_CONTEXT_RET_TIME)))
+ goto bad2;
+
+ /* Only need local address for mk_cred() to send to krlogind */
+ if ((status = krb5_auth_con_genaddrs(bsd_context, auth_context, s,
+ KRB5_AUTH_CONTEXT_GENERATE_LOCAL_FULL_ADDR)))
+ goto bad2;
+
+ if (protonum == KCMD_PROTOCOL_COMPAT_HACK) {
+ krb5_boolean is_des;
+ status = krb5_c_enctype_compare(bsd_context,
+ ENCTYPE_DES_CBC_CRC,
+ ret_cred->keyblock.enctype,
+ &is_des);
+ if (status)
+ goto bad2;
+ protonum = is_des ? KCMD_OLD_PROTOCOL : KCMD_NEW_PROTOCOL;
+ }
+
+ switch (protonum) {
+ case KCMD_NEW_PROTOCOL:
+ authopts |= AP_OPTS_USE_SUBKEY;
+ kcmd_version = "KCMDV0.2";
+ break;
+ case KCMD_OLD_PROTOCOL:
+ kcmd_version = "KCMDV0.1";
+ break;
+ default:
+ status = -1;
+ goto bad2;
+ }
+
+ /*
+ * Call the Kerberos library routine to obtain an authenticator,
+ * pass it over the socket to the server, and obtain mutual
+ * authentication.
+ */
+ status = krb5_sendauth(bsd_context, &auth_context, (krb5_pointer) &s,
+ kcmd_version, ret_cred->client, ret_cred->server,
+ authopts, &cksumdat, ret_cred, 0, &error,
+ &rep_ret, NULL);
+ krb5_xfree(cksumdat.data);
+ if (status) {
+ (void) fprintf(stderr, gettext("Couldn't authenticate"
+ " to server: %s\n"),
+ error_message(status));
+ if (error) {
+ (void) fprintf(stderr, gettext("Server returned error"
+ " code %d (%s)\n"),
+ error->error,
+ error_message(ERROR_TABLE_BASE_krb5 +
+ error->error));
+ if (error->text.length)
+ (void) fprintf(stderr,
+ gettext("Error text"
+ " sent from server: %s\n"),
+ error->text.data);
+ }
+ if (error) {
+ krb5_free_error(bsd_context, error);
+ error = 0;
+ }
+ goto bad2;
+ }
+ if (rep_ret && server_seqno) {
+ *server_seqno = rep_ret->seq_number;
+ krb5_free_ap_rep_enc_part(bsd_context, rep_ret);
+ }
+
+ (void) write(s, remuser, strlen(remuser)+1);
+ (void) write(s, cmd, strlen(cmd)+1);
+ if (locuser)
+ (void) write(s, locuser, strlen(locuser)+1);
+ else
+ (void) write(s, "", 1);
+
+ if (options & OPTS_FORWARD_CREDS) { /* Forward credentials */
+ if (status = krb5_fwd_tgt_creds(bsd_context, auth_context,
+ host_save,
+ ret_cred->client, ret_cred->server,
+ 0, options & OPTS_FORWARDABLE_CREDS,
+ &outbuf)) {
+ (void) fprintf(stderr,
+ gettext("kcmd: Error getting"
+ " forwarded creds\n"));
+ goto bad2;
+ }
+ /* Send forwarded credentials */
+ if (status = krb5_write_message(bsd_context, (krb5_pointer)&s,
+ &outbuf))
+ goto bad2;
+ } else { /* Dummy write to signal no forwarding */
+ outbuf.length = 0;
+ if (status = krb5_write_message(bsd_context,
+ (krb5_pointer)&s, &outbuf))
+ goto bad2;
+ }
+
+ if ((rc = read(s, &c, 1)) != 1) {
+ if (rc == -1) {
+ perror(*ahost);
+ } else {
+ (void) fprintf(stderr, gettext("kcmd: bad connection "
+ "with remote host\n"));
+ }
+ status = -1;
+ goto bad2;
+ }
+ if (c != 0) {
+ while (read(s, &c, 1) == 1) {
+ (void) write(2, &c, 1);
+ if (c == '\n')
+ break;
+ }
+ status = -1;
+ goto bad2;
+ }
+ (void) sigprocmask(SIG_SETMASK, &oldmask, (sigset_t *)0);
+ *sock = s;
+
+ /* pass back credentials if wanted */
+ if (cred) krb5_copy_creds(bsd_context, ret_cred, cred);
+ krb5_free_creds(bsd_context, ret_cred);
+ /*
+ * Initialize *authconp to auth_context, so
+ * that the clients can make use of it
+ */
+ *authconp = auth_context;
+
+ return (0);
+bad2:
+ if (fd2p != NULL)
+ (void) close(*fd2p);
+bad:
+ if (s > 0)
+ (void) close(s);
+ if (get_cred)
+ krb5_free_creds(bsd_context, get_cred);
+ if (ret_cred)
+ krb5_free_creds(bsd_context, ret_cred);
+ if (host_save)
+ free(host_save);
+ (void) sigprocmask(SIG_SETMASK, &oldmask, (sigset_t *)0);
+ return (status);
+}
+
+/*
+ * Strsave was a routine in the version 4 krb library: we put it here
+ * for compatablilty with version 5 krb library, since kcmd.o is linked
+ * into all programs.
+ */
+
+char *
+strsave(char *sp)
+{
+ char *ret;
+
+ if ((ret = (char *)strdup(sp)) == NULL) {
+ (void) fprintf(stderr, gettext("no memory for saving args\n"));
+ exit(1);
+ }
+ return (ret);
+}
+
+/*
+ * Decode, decrypt and store the forwarded creds in the local ccache.
+ */
+krb5_error_code
+rd_and_store_for_creds(krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_data *inbuf,
+ krb5_ticket *ticket,
+ char *lusername,
+ krb5_ccache *ccache)
+{
+ krb5_creds ** creds;
+ krb5_error_code retval;
+ char ccname[64];
+ struct passwd *pwd;
+ uid_t uid;
+
+ *ccache = NULL;
+ if (!(pwd = (struct passwd *)getpwnam(lusername)))
+ return (ENOENT);
+
+ uid = getuid();
+ if (seteuid(pwd->pw_uid))
+ return (-1);
+
+ if ((retval =
+ krb5_rd_cred(context, auth_context, inbuf, &creds, NULL)) != 0)
+ return (retval);
+
+ (void) snprintf(ccname, sizeof (ccname),
+ "FILE:/tmp/krb5cc_%ld", pwd->pw_uid);
+
+ if ((retval = krb5_cc_resolve(context, ccname, ccache)) != 0)
+ goto cleanup;
+
+ if ((retval = krb5_cc_initialize(context, *ccache,
+ ticket->enc_part2->client)) != 0)
+ goto cleanup;
+
+ if ((retval = krb5_cc_store_cred(context, *ccache, *creds)) != 0)
+ goto cleanup;
+
+ if ((retval = krb5_cc_close(context, *ccache)) != 0)
+ goto cleanup;
+
+cleanup:
+ (void) seteuid(uid);
+ krb5_free_creds(context, *creds);
+ return (retval);
+}
+
+/*
+ * This routine is to initialize the desinbuf, desoutbuf and the session key
+ * structures to carry out desread()'s and deswrite()'s successfully
+ */
+void
+init_encrypt(int enc, krb5_context ctxt, enum kcmd_proto protonum,
+ krb5_data *inbuf, krb5_data *outbuf,
+ int amclient, krb5_encrypt_block *block)
+{
+ krb5_error_code statuscode;
+ size_t blocksize;
+ int i;
+ krb5_error_code ret;
+
+ kcmd_context = ctxt;
+
+ if (enc > 0) {
+ desinbuf.data = inbuf->data;
+ desoutbuf.data = outbuf->data + 4;
+ desinbuf.length = inbuf->length;
+ desoutbuf.length = outbuf->length + 4;
+ encrypt_flag = B_TRUE;
+ } else {
+ encrypt_flag = B_FALSE;
+ return;
+ }
+
+ skey = block->key;
+ final_enctype = skey->enctype;
+
+ enc_keyusage_i[0] = KCMD_KEYUSAGE;
+ enc_keyusage_i[1] = KCMD_KEYUSAGE;
+ enc_keyusage_o[0] = KCMD_KEYUSAGE;
+ enc_keyusage_o[1] = KCMD_KEYUSAGE;
+
+ if (protonum == KCMD_OLD_PROTOCOL) {
+ use_ivecs = B_FALSE;
+ return;
+ }
+
+ use_ivecs = B_TRUE;
+ switch (skey->enctype) {
+ /*
+ * For the DES-based enctypes and the 3DES enctype we
+ * want to use a non-zero IV because that's what we did.
+ * In the future we use different keyusage for each
+ * channel and direction and a fresh cipher state.
+ */
+ case ENCTYPE_DES_CBC_CRC:
+ case ENCTYPE_DES_CBC_MD4:
+ case ENCTYPE_DES_CBC_MD5:
+ case ENCTYPE_DES3_CBC_SHA1:
+ statuscode = krb5_c_block_size(kcmd_context, final_enctype,
+ &blocksize);
+ if (statuscode) {
+ /* XXX what do I do? */
+ abort();
+ }
+
+ encivec_i[0].length = encivec_i[1].length =
+ encivec_o[0].length = encivec_o[1].length = blocksize;
+
+ if ((encivec_i[0].data = malloc(encivec_i[0].length * 4))
+ == NULL) {
+ /* XXX what do I do? */
+ abort();
+ }
+ encivec_i[1].data = encivec_i[0].data + encivec_i[0].length;
+ encivec_o[0].data = encivec_i[1].data + encivec_i[0].length;
+ encivec_o[1].data = encivec_o[0].data + encivec_i[0].length;
+
+ /* is there a better way to initialize this? */
+ (void) memset(encivec_i[0].data, amclient, blocksize);
+ (void) memset(encivec_o[0].data, 1 - amclient, blocksize);
+ (void) memset(encivec_i[1].data, 2 | amclient, blocksize);
+ (void) memset(encivec_o[1].data, 2 | (1 - amclient), blocksize);
+ break;
+ default:
+ if (amclient) {
+ enc_keyusage_i[0] = 1028;
+ enc_keyusage_i[1] = 1030;
+ enc_keyusage_o[0] = 1032;
+ enc_keyusage_o[1] = 1034;
+ } else { /* amclient */
+ enc_keyusage_i[0] = 1032;
+ enc_keyusage_i[1] = 1034;
+ enc_keyusage_o[0] = 1028;
+ enc_keyusage_o[1] = 1030;
+ }
+ for (i = 0; i < 2; i++) {
+ ret = krb5_c_init_state(ctxt,
+ skey, enc_keyusage_i[i],
+ &encivec_i[i]);
+ if (ret)
+ goto fail;
+ ret = krb5_c_init_state(ctxt,
+ skey, enc_keyusage_o[i],
+ &encivec_o[i]);
+ if (ret)
+ goto fail;
+ }
+ break;
+ }
+ return;
+fail:
+ abort();
+}
+
+int
+desread(int fd, char *buf, int len, int secondary)
+{
+ int nreturned = 0;
+ long net_len, rd_len;
+ int cc;
+ size_t ret = 0;
+ unsigned char len_buf[4];
+ krb5_enc_data inputd;
+ krb5_data outputd;
+
+ if (!encrypt_flag)
+ return (read(fd, buf, len));
+
+ /*
+ * If there is stored data from a previous read,
+ * put it into the output buffer and return it now.
+ */
+ if (nstored >= len) {
+ (void) memcpy(buf, store_ptr, len);
+ store_ptr += len;
+ nstored -= len;
+ return (len);
+ } else if (nstored) {
+ (void) memcpy(buf, store_ptr, nstored);
+ nreturned += nstored;
+ buf += nstored;
+ len -= nstored;
+ nstored = 0;
+ }
+
+ if ((cc = krb5_net_read(kcmd_context, fd, (char *)len_buf, 4)) != 4) {
+ if ((cc < 0) && ((errno == EWOULDBLOCK) || (errno == EAGAIN)))
+ return (cc);
+ /* XXX can't read enough, pipe must have closed */
+ return (0);
+ }
+ rd_len = ((len_buf[0] << 24) | (len_buf[1] << 16) |
+ (len_buf[2] << 8) | len_buf[3]);
+
+ if (krb5_c_encrypt_length(kcmd_context, final_enctype,
+ use_ivecs ? (size_t)rd_len + 4 : (size_t)rd_len,
+ &ret))
+ net_len = ((size_t)-1);
+ else
+ net_len = ret;
+
+ if ((net_len <= 0) || (net_len > desinbuf.length)) {
+ /*
+ * preposterous length; assume out-of-sync; only recourse
+ * is to close connection, so return 0
+ */
+ (void) fprintf(stderr, gettext("Read size problem.\n"));
+ return (0);
+ }
+
+ if ((cc = krb5_net_read(kcmd_context, fd, desinbuf.data, net_len))
+ != net_len) {
+ /* pipe must have closed, return 0 */
+ (void) fprintf(stderr,
+ gettext("Read error: length received %d "
+ "!= expected %d.\n"),
+ cc, net_len);
+ return (0);
+ }
+
+ /*
+ * Decrypt information
+ */
+ inputd.enctype = ENCTYPE_UNKNOWN;
+ inputd.ciphertext.length = net_len;
+ inputd.ciphertext.data = (krb5_pointer)desinbuf.data;
+
+ outputd.length = sizeof (storage);
+ outputd.data = (krb5_pointer)storage;
+
+ /*
+ * data is decrypted into the "storage" buffer, which
+ * had better be large enough!
+ */
+ cc = krb5_c_decrypt(kcmd_context, skey,
+ enc_keyusage_i[secondary],
+ use_ivecs ? encivec_i + secondary : 0,
+ &inputd, &outputd);
+ if (cc) {
+ (void) fprintf(stderr, gettext("Cannot decrypt data "
+ "from network\n"));
+ return (0);
+ }
+
+ store_ptr = storage;
+ nstored = rd_len;
+ if (use_ivecs == B_TRUE) {
+ int rd_len2;
+ rd_len2 = storage[0] & 0xff;
+ rd_len2 <<= 8; rd_len2 |= storage[1] & 0xff;
+ rd_len2 <<= 8; rd_len2 |= storage[2] & 0xff;
+ rd_len2 <<= 8; rd_len2 |= storage[3] & 0xff;
+ if (rd_len2 != rd_len) {
+ /* cleartext length trashed? */
+ errno = EIO;
+ return (-1);
+ }
+ store_ptr += 4;
+ }
+ /*
+ * Copy only as much data as the input buffer will allow.
+ * The rest is kept in the 'storage' pointer for the next
+ * read.
+ */
+ if (nstored > len) {
+ (void) memcpy(buf, store_ptr, len);
+ nreturned += len;
+ store_ptr += len;
+ nstored -= len;
+ } else {
+ (void) memcpy(buf, store_ptr, nstored);
+ nreturned += nstored;
+ nstored = 0;
+ }
+
+ return (nreturned);
+}
+
+int
+deswrite(int fd, char *buf, int len, int secondary)
+{
+ int cc;
+ size_t ret = 0;
+ krb5_data inputd;
+ krb5_enc_data outputd;
+ char tmpbuf[KCMD_BUFSIZ + 8];
+ char encrbuf[KCMD_BUFSIZ + 8];
+ unsigned char *len_buf = (unsigned char *)tmpbuf;
+
+ if (!encrypt_flag)
+ return (write(fd, buf, len));
+
+ if (use_ivecs == B_TRUE) {
+ unsigned char *lenbuf2 = (unsigned char *)tmpbuf;
+ if (len + 4 > sizeof (tmpbuf))
+ abort();
+ lenbuf2[0] = (len & 0xff000000) >> 24;
+ lenbuf2[1] = (len & 0xff0000) >> 16;
+ lenbuf2[2] = (len & 0xff00) >> 8;
+ lenbuf2[3] = (len & 0xff);
+ (void) memcpy(tmpbuf + 4, buf, len);
+
+ inputd.data = (krb5_pointer)tmpbuf;
+ inputd.length = len + 4;
+ } else {
+ inputd.data = (krb5_pointer)buf;
+ inputd.length = len;
+ }
+
+ desoutbuf.data = encrbuf;
+
+ if (krb5_c_encrypt_length(kcmd_context, final_enctype,
+ use_ivecs ? (size_t)len + 4 : (size_t)len, &ret)) {
+ desoutbuf.length = ((size_t)-1);
+ goto err;
+ } else {
+ desoutbuf.length = ret;
+ }
+
+ if (desoutbuf.length > MAXSIZE) {
+ (void) fprintf(stderr, gettext("Write size problem.\n"));
+ return (-1);
+ }
+
+ /*
+ * Encrypt information
+ */
+ outputd.ciphertext.length = desoutbuf.length;
+ outputd.ciphertext.data = (krb5_pointer)desoutbuf.data;
+
+ cc = krb5_c_encrypt(kcmd_context, skey,
+ enc_keyusage_o[secondary],
+ use_ivecs ? encivec_o + secondary : 0,
+ &inputd, &outputd);
+
+ if (cc) {
+err:
+ (void) fprintf(stderr, gettext("Write encrypt problem.\n"));
+ return (-1);
+ }
+
+ len_buf[0] = (len & 0xff000000) >> 24;
+ len_buf[1] = (len & 0xff0000) >> 16;
+ len_buf[2] = (len & 0xff00) >> 8;
+ len_buf[3] = (len & 0xff);
+ (void) write(fd, len_buf, 4);
+
+ if (write(fd, desoutbuf.data, desoutbuf.length) != desoutbuf.length) {
+ (void) fprintf(stderr, gettext("Could not write "
+ "out all data.\n"));
+ return (-1);
+ } else {
+ return (len);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/common/kcmd.h b/usr/src/cmd/cmd-inet/common/kcmd.h
new file mode 100644
index 0000000000..a497d1b097
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/common/kcmd.h
@@ -0,0 +1,123 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _KCMD_H
+#define _KCMD_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define OPTS_FORWARD_CREDS 0x00000002
+#define OPTS_FORWARDABLE_CREDS 0x00000001
+
+#define SERVER 0
+#define CLIENT 1
+
+enum kcmd_proto {
+ /*
+ * Old protocol: DES encryption only. No subkeys.
+ * No protection for cleartext length. No ivec supplied.
+ * OOB hacks used for rlogin. Checksum may be omitted at
+ * connection startup.
+ */
+ KCMD_OLD_PROTOCOL = 1,
+ /*
+ * New protocol: Any encryption scheme. Client-generated
+ * subkey required. Prepend cleartext-length to cleartext
+ * data (but don't include it in count). Starting ivec defined,
+ * chained. In-band signalling. Checksum required.
+ */
+ KCMD_NEW_PROTOCOL,
+
+ /*
+ * Hack: Get credentials, and use the old protocol iff the session
+ * key type is single-DES.
+ */
+ KCMD_PROTOCOL_COMPAT_HACK,
+ /* Using Kerberos version 4. */
+ KCMD_V4_PROTOCOL,
+ KCMD_UNKNOWN_PROTOCOL
+};
+
+#define SOCK_FAMILY(ss) ((ss).ss_family)
+
+#define SOCK_PORT(ss) ((ss).ss_family == AF_INET6 ? \
+((struct sockaddr_in6 *)&(ss))->sin6_port : \
+((struct sockaddr_in *)&(ss))->sin_port)
+
+#define SOCK_ADDR(ss) ((ss).ss_family == AF_INET6 ? \
+(void *)&((struct sockaddr_in6 *)&(ss))->sin6_addr : \
+(void *)&((struct sockaddr_in *)&(ss))->sin_addr)
+
+#define SET_SOCK_FAMILY(ss, family) (SOCK_FAMILY(ss) = (family))
+
+#define SET_SOCK_PORT(ss, port) \
+ ((ss).ss_family == AF_INET6 ? \
+ (((struct sockaddr_in6 *)&(ss))->sin6_port = (port)) : \
+ (((struct sockaddr_in *)&(ss))->sin_port = (port)))
+
+#define SET_SOCK_ADDR4(ss, addr) ((void)(sock_set_inaddr(&(ss), (addr))))
+
+#define SET_SOCK_ADDR_ANY(ss) \
+ ((void) ((ss).ss_family == AF_INET6 ? \
+ (void) (((struct sockaddr_in6 *)&(ss))->sin6_addr = in6addr_any) : \
+ (void) (((struct sockaddr_in *)&(ss))->sin_addr.s_addr = \
+ htonl(INADDR_ANY))))
+
+/*
+ * Prototypes for functions in 'kcmd.c'
+ */
+char *strsave(char *sp);
+
+int kcmd(int *sock, char **ahost, ushort_t rport, char *locuser,
+ char *remuser, char *cmd, int *fd2p, char *service, char *realm,
+ krb5_context bsd_context, krb5_auth_context *authconp,
+ krb5_creds **cred, krb5_int32 *seqno, krb5_int32 *server_seqno,
+ krb5_flags authopts,
+ int anyport, enum kcmd_proto *kcmd_proto);
+
+krb5_error_code rd_and_store_for_creds(krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_data *inbuf,
+ krb5_ticket *ticket,
+ char *lusername,
+ krb5_ccache *ccache);
+
+void init_encrypt(int, krb5_context, enum kcmd_proto,
+ krb5_data *, krb5_data *,
+ int, krb5_encrypt_block *);
+
+int desread(int, char *, int, int);
+int deswrite(int, char *, int, int);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _KCMD_H */
diff --git a/usr/src/cmd/cmd-inet/common/mipagentstat_door.h b/usr/src/cmd/cmd-inet/common/mipagentstat_door.h
new file mode 100644
index 0000000000..8a650f35e7
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/common/mipagentstat_door.h
@@ -0,0 +1,145 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _MIPAGENTSTAT_DOOR_H
+#define _MIPAGENTSTAT_DOOR_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * This header defines constants and structures common to the
+ * mipagentstat door client and server. That stat client
+ * can retrieve and display information about mobile nodes
+ * from mipagent by making enumeration RPCs to the stat server
+ * in mipagent. Each door call effects a single enumeration
+ * transaction, so the stat client is in effect a sliding
+ * window across the registration tables in mipagent.
+ *
+ * The client / server protocol is defined by the DoorStatsArgs
+ * structure. The same structure is used for both call and reply
+ * information, and no memory needs to be allocated for the
+ * IPC by either the client or server. All address information
+ * about mobile nodes and agents is communicated via buffers
+ * large enough to hold an IPv6 address, and each address is
+ * tagged with an address family so that it can be processed
+ * by the stat client.
+ *
+ * The client keeps track of all enumeration state by passing
+ * an opaque state handle to the server for each enumeration
+ * transaction. This state handle is the enum_state field in
+ * DoorStatArgs. The server is stateless, and simply conducts
+ * the enumeration operation based on the information in the
+ * state handle and updates the handle each call before passing
+ * it back to the client. The client indicates that it wishes to
+ * start an enumeration setting the op field to FIRST_ENT; all
+ * successive enumeration calls should set the op to NEXT_ENT.
+ *
+ * The stat client can enumerate either the home or foreign agent
+ * tables. Which agent to get stats for is indicated by setting
+ * the type field to either HOME_AGENT or FOREIGN_AGENT.
+ *
+ * The last two fields of DoorStatArgs are the time granted and
+ * time remaining for the mobile node being displayed. These times
+ * are absolute times, in seconds.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Stat door rendezvous point */
+#define MIPAGENTSTAT_DOOR "/var/run/.mipagentstat_door"
+
+/* Bits for tracking command-line arguments for HA / FA stats */
+#define DO_HA 0x01
+#define DO_FA 0x02
+#define DO_BOTH 0x03
+
+/*
+ * Enum describing which agent to get stats for, and if our output should be
+ * be mn-centric, or mobility agent peer-centric.
+ */
+typedef enum { HOME_AGENT,
+ FOREIGN_AGENT,
+ HOME_AGENT_PEER,
+ FOREIGN_AGENT_PEER } enum_stat_type;
+
+/*
+ * Define peer-flags so we know our place in the peer relationship.
+ */
+#define HA_PEER 0x01 /* identifies this agent as an HA peer */
+#define FA_PEER 0x02 /* identifies this agent as an FA peer */
+
+/* Enum describing which enumeration operation to do */
+typedef enum { FIRST_ENT, NEXT_ENT } enum_op;
+
+/* Call / reply buffer; defines the mipagentstat protocol */
+typedef struct door_stat_args {
+ /* control (call) fields */
+ enum_stat_type type; /* home agent or foreign agent */
+ enum_op op; /* first or next entry */
+ uint8_t enum_state[16]; /* 128 bits of enumerator state */
+ /* data (reply) fields */
+ int node_af; /* mobile node addr address family */
+ int agent_af; /* agent addr address family */
+ uint8_t node[sizeof (struct in6_addr)];
+ /* mobile node address */
+ uint8_t agent[sizeof (struct in6_addr)];
+ /* home/foreign agent name */
+ uint32_t granted; /* time granted */
+ uint32_t expires; /* time remaining */
+ /* flags - indicate services, and security */
+ uint8_t service_flags; /* special services for the mn */
+} DoorStatArgs;
+
+/* service flags - keep it tightly anologous to the registration */
+#define SERVICE_BIT_UNUSED 0x01 /* placeholder */
+#define SERVICE_REVERSE_TUNNEL 0x02 /* ReverseTunnel Service */
+#define SERVICE_VJ_COMPRESSION 0x04 /* VJ Compression Service */
+#define SERVICE_GRE_ENCAP 0x08 /* GRE Encapsulation Service */
+#define SERVICE_MIN_ENCAP 0x10 /* MIN Encapsulation Service */
+#define SERVICE_DECAPSULATION_BY_MN 0x20 /* MN is Colocated */
+#define SERVICE_FWD_BROADCASTS 0x40 /* [multi/broad]cast service */
+#define SERVICE_SIMULTANEOUS_BINDINGS 0x80 /* Simultaneous binding service */
+
+/*
+ * These should be indicated in the Flags column (of mipagentstat) by the flag
+ * identifiers in the becon/registration for consistency (when supported):
+ *
+ * Simultaneous Bindings = S
+ * Forwarding Broadcasts = B
+ * Decapsulation by MN = D
+ * Minimim Encapsulation = M
+ * Generic Encapsulation = G
+ * Van Jacobson Compress = V
+ * Reverse Tunneling = T - supported
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MIPAGENTSTAT_DOOR_H */
diff --git a/usr/src/cmd/cmd-inet/common/tftpcommon.h b/usr/src/cmd/cmd-inet/common/tftpcommon.h
new file mode 100644
index 0000000000..d85149e6c2
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/common/tftpcommon.h
@@ -0,0 +1,83 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2001-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _TFTPCOMMON_H
+#define _TFTPCOMMON_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Defines and function declarations common to tftp and in.tftpd.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <arpa/tftp.h>
+
+#define MIN_BLKSIZE 8 /* RFC 2348 */
+#define MAX_BLKSIZE 65464 /* RFC 2348 */
+#define MIN_TIMEOUT 1 /* RFC 2349 */
+#define MAX_TIMEOUT 255 /* RFC 2349 */
+#define PKTSIZE (MAX_BLKSIZE + 4) /* DATA packet max size */
+#define MAX_OPTVAL_LEN 32 /* Option value max length */
+
+/* Format when printing an off_t */
+#if _FILE_OFFSET_BITS == 64
+#define OFF_T_FMT "%lld"
+#else
+#define OFF_T_FMT "%ld"
+#endif
+
+typedef union {
+ struct tftphdr tb_hdr;
+ char tb_data[PKTSIZE];
+} tftpbuf;
+
+struct errmsg {
+ int e_code;
+ char *e_msg;
+};
+extern struct errmsg errmsgs[];
+
+/* Declarations for shared functions in tftpsubs.c */
+extern struct tftphdr *w_init(void);
+extern struct tftphdr *r_init(void);
+extern int readit(FILE *, struct tftphdr **, int);
+extern void read_ahead(FILE *, int);
+extern int writeit(FILE *, struct tftphdr **, int, int);
+extern int write_behind(FILE *, int);
+extern int synchnet(int);
+extern char *next_field(const char *, const char *);
+extern void print_options(FILE *, char *, int);
+extern void cancel_alarm(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TFTPCOMMON_H */
diff --git a/usr/src/cmd/cmd-inet/etc/Makefile b/usr/src/cmd/cmd-inet/etc/Makefile
new file mode 100644
index 0000000000..1af0a828fc
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/etc/Makefile
@@ -0,0 +1,94 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 1989-2003 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+SYMPROG= hosts inetd.conf networks protocols services netmasks sock2path
+
+# New /etc/inet files shouldn't have /etc entries.
+PROG= ipnodes datemsk.ndpd ipaddrsel.conf ipsecalgs ipsecinit.sample \
+ ipqosconf.1.sample ipqosconf.2.sample ipqosconf.3.sample \
+ wanboot.conf.sample
+#mipagent config file permissions need to be tighter
+PROG2= mipagent.conf-sample mipagent.conf.fa-sample mipagent.conf.ha-sample
+ETCPROG= $(SYMPROG) $(PROG) $(PROG2)
+SUBDIRS= default dhcp init.d ike nca ppp secret
+
+include ../../Makefile.cmd
+
+all:= TARGET= all
+install:= TARGET= install
+
+ROOTVAR= $(ROOT)/var
+INETETCDIR= $(ROOTETC)/inet
+INETVARDIR= $(ROOTVAR)/inet
+DIRS= $(INETETCDIR) $(INETVARDIR)
+SYMDIR= inet
+ETCINETPROG= $(ETCPROG:%=$(INETETCDIR)/%)
+ETCINETPROG2= $(PROG2:%=$(INETETCDIR)/%)
+# Only old /etc/inet files get symlinks in /etc.
+SYMETCPROG= $(SYMPROG:%=sym_%)
+
+FILEMODE= 0444
+OWNER= root
+GROUP= sys
+
+$(ETCINETPROG2):= FILEMODE= 0600
+
+.KEEP_STATE:
+
+all: $(ETCPROG) $(SUBDIRS)
+
+install: all $(DIRS) $(ETCINETPROG) $(SYMETCPROG) $(SUBDIRS)
+
+$(INETETCDIR)/% : %
+ $(INS.file)
+
+sym_% : %
+ $(RM) $(ROOTETC)/$<
+ $(SYMLINK) $(SYMDIR)/$< $(ROOTETC)/$<
+
+$(DIRS):
+ $(INS.dir)
+
+$(SUBDIRS): FRC $(DIRS)
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
+
+# datemsk.ndpd is generated from datemsk.template because of a side-effect of
+# SCCS. Some of the datemsk.ndpd format strings include "%<letter>%", which
+# SCCS confuses for ID keywords. datemsk.template should quote the "%"
+# with "\" and code below will filter out the "\". Only datemsk.ndpd format
+# strings next to each other need to be quoted.
+
+datemsk.ndpd: datemsk.template
+ @while read i; do echo $$i; done < datemsk.template > $@
+
+clean clobber:
+ $(RM) datemsk.ndpd
+
+lint:
diff --git a/usr/src/cmd/cmd-inet/etc/datemsk.template b/usr/src/cmd/cmd-inet/etc/datemsk.template
new file mode 100644
index 0000000000..0586b873d3
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/etc/datemsk.template
@@ -0,0 +1,2 @@
+%Y-%m-%d\%t\%R
+%Y-%m-%d
diff --git a/usr/src/cmd/cmd-inet/etc/default/Makefile b/usr/src/cmd/cmd-inet/etc/default/Makefile
new file mode 100644
index 0000000000..e05432407b
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/etc/default/Makefile
@@ -0,0 +1,57 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# cmd/cmd-inet/etc/default/Makefile
+
+DFLTF= inetinit ipsec
+
+include ../../../Makefile.cmd
+
+DFLTD= $(ROOTETC)/default
+ETCDFLTPROG = $(DFLTF:%=$(DFLTD)/%)
+$(ETCDFLTPROG) := FILEMODE = $(LIBFILEMODE)
+$(ETCDFLTPROG) := GROUP = sys
+DIRS=$(DFLTD)
+
+all:
+
+install: all $(DIRS) $(ETCDFLTPROG)
+
+$(DFLTD)/% : % $(DFLTD)
+ $(INS.file)
+
+$(DIRS):
+ $(INS.dir)
+
+clean:
+
+lint:
+
+include ../../../Makefile.targ
+
+.KEEP_STATE:
+.PARALLEL:
diff --git a/usr/src/cmd/cmd-inet/etc/default/inetinit b/usr/src/cmd/cmd-inet/etc/default/inetinit
new file mode 100644
index 0000000000..71b1e1af3f
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/etc/default/inetinit
@@ -0,0 +1,55 @@
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# TCP_STRONG_ISS sets the TCP initial sequence number generation parameters.
+# Set TCP_STRONG_ISS to be:
+# 0 = Old-fashioned sequential initial sequence number generation.
+# 1 = Improved sequential generation, with random variance in increment.
+# 2 = RFC 1948 sequence number generation, unique-per-connection-ID.
+#
+TCP_STRONG_ISS=1
+#
+# ACCEPT6TO4RELAY sets the policy for 6to4 tunnels communicating with 6to4
+# Relay Routers as defined in RFC 3056. Traffic sent from a 6to4 site to a
+# native IPv6 host will be tunneled over the IPv4 Internet to a 6to4 Relay
+# Router before being delivered to the native IPv6 host. Enabling support
+# for sending/receiving traffic to/from a 6to4 Relay Router can create a
+# security risk for a 6to4 site, since there is no default trust
+# mechanism for communicating with Relay Routers. Communication support
+# with 6to4 Relay Routers has been disabled by default. ACCEPT6TO4RELAY
+# can be set to the following values:
+# NO = Disables communication with 6to4 Relay Routers
+# YES = Enables communication with 6to4 Relay Routers and thus native
+# IPv6 hosts through a 6to4 tunnel.
+#
+# When ACCEPT6TO4RELAY=YES, RELAY6TO4ADDR will be used to determine the
+# destination IPv4 address to be used as a tunnel endpoint when communicating
+# with 6to4 Relay Routers. 192.88.99.1 is the well-known 6to4 Relay Router
+# Anycast address as defined in RFC 3068. This value may be changed to
+# the IPv4 unicast address of a particular 6to4 Relay Router, if desired.
+#
+ACCEPT6TO4RELAY=NO
+RELAY6TO4ADDR="192.88.99.1"
diff --git a/usr/src/cmd/cmd-inet/etc/default/ipsec b/usr/src/cmd/cmd-inet/etc/default/ipsec
new file mode 100644
index 0000000000..0a2907ad0e
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/etc/default/ipsec
@@ -0,0 +1,52 @@
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# IKE CONFIGURATION:
+# This file is read once by each in.iked process.
+#
+# IKE_CONFIG defines the default IKE config file pathname. By default, it
+# is /etc/inet/ike/config
+#IKE_CONFIG=/etc/inet/ike/config
+#
+# IKE_DEBUG sets the initial debug level within the IKE daemon; by default,
+# no debug messages are logged.
+#IKE_DEBUG=
+#
+# IKE_DEBUG_FILE controls where IKE debug output goes if enabled. By default,
+# it goes to stderr (the console, when started at boot time, which is usually
+# not what you want)
+#IKE_DEBUG_FILE=
+#
+# IKE_ADM_PRIV sets the ikeadm "privilege level", which determines how
+# much ikeadm is allowed to control while in.iked is running.
+# Possible values are:
+# base ikeadm may not change keys
+# modkeys ikeadm may set keys.
+# keymat ikeadm may set and get keys.
+#IKE_ADM_PRIV=base
+
+
+
diff --git a/usr/src/cmd/cmd-inet/etc/dhcp/Makefile b/usr/src/cmd/cmd-inet/etc/dhcp/Makefile
new file mode 100644
index 0000000000..39cc258b69
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/etc/dhcp/Makefile
@@ -0,0 +1,57 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%W% %E% SMI"
+#
+# Copyright (c) 1999 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+# cmd/cmd-inet/etc/dhcp/Makefile
+#
+
+DHCPDIR= dhcp
+ETCPROG= inittab
+
+include ../../../Makefile.cmd
+
+ETCDHCPDIR= $(ROOTETC)/$(DHCPDIR)
+ETCDHCPPROG= $(ETCPROG:%=$(ETCDHCPDIR)/%)
+
+FILEMODE= 0644
+OWNER= root
+GROUP= sys
+
+.KEEP_STATE:
+
+all: $(ETCPROG)
+
+install: all $(ETCDHCPDIR) $(ETCDHCPPROG)
+
+$(ETCDHCPDIR)/% : %
+ $(INS.file)
+
+$(ETCDHCPDIR):
+ $(INS.dir)
+
+FRC:
+
+clean clobber lint:
diff --git a/usr/src/cmd/cmd-inet/etc/dhcp/inittab b/usr/src/cmd/cmd-inet/etc/dhcp/inittab
new file mode 100644
index 0000000000..42a0d099c6
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/etc/dhcp/inittab
@@ -0,0 +1,184 @@
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# This file provides information about all supported DHCP options, for
+# use by DHCP-related programs. This file should only be modified to
+# add support for SITE options for clients; no existing options should
+# be modified. Only SITE options will be preserved during an upgrade.
+# If you need to configure the Solaris DHCP server to support the vendor
+# options of a different client, see dhcptab(4) for details.
+#
+# Please consult dhcp_inittab(4) for further information. Note that
+# this interface is "Unstable" as defined by attributes(5).
+#
+
+Subnet STANDARD, 1, IP, 1, 1, sdmi
+UTCoffst STANDARD, 2, SNUMBER32, 1, 1, sdmi
+Router STANDARD, 3, IP, 1, 0, sdmi
+Timeserv STANDARD, 4, IP, 1, 0, sdmi
+IEN116ns STANDARD, 5, IP, 1, 0, sdmi
+DNSserv STANDARD, 6, IP, 1, 0, sdmi
+Logserv STANDARD, 7, IP, 1, 0, sdmi
+Cookie STANDARD, 8, IP, 1, 0, sdmi
+Lprserv STANDARD, 9, IP, 1, 0, sdmi
+Impress STANDARD, 10, IP, 1, 0, sdmi
+Resource STANDARD, 11, IP, 1, 0, sdmi
+Hostname STANDARD, 12, ASCII, 1, 0, si
+Bootsize STANDARD, 13, UNUMBER16, 1, 1, sdmi
+Dumpfile STANDARD, 14, ASCII, 1, 0, sdmi
+DNSdmain STANDARD, 15, ASCII, 1, 0, sdmi
+Swapserv STANDARD, 16, IP, 1, 1, sdmi
+Rootpath STANDARD, 17, ASCII, 1, 0, sdmi
+ExtendP STANDARD, 18, ASCII, 1, 0, sdmi
+IpFwdF STANDARD, 19, UNUMBER8, 1, 1, sdmi
+NLrouteF STANDARD, 20, UNUMBER8, 1, 1, sdmi
+PFilter STANDARD, 21, IP, 2, 0, sdmi
+MaxIpSiz STANDARD, 22, UNUMBER16, 1, 1, sdmi
+IpTTL STANDARD, 23, UNUMBER8, 1, 1, sdmi
+PathTO STANDARD, 24, UNUMBER32, 1, 1, sdmi
+PathTbl STANDARD, 25, UNUMBER16, 1, 0, sdmi
+MTU STANDARD, 26, UNUMBER16, 1, 1, sdmi
+SameMtuF STANDARD, 27, UNUMBER8, 1, 1, sdmi
+Broadcst STANDARD, 28, IP, 1, 1, sdmi
+MaskDscF STANDARD, 29, UNUMBER8, 1, 1, sdmi
+MaskSupF STANDARD, 30, UNUMBER8, 1, 1, sdmi
+RDiscvyF STANDARD, 31, UNUMBER8, 1, 1, sdmi
+RSolictS STANDARD, 32, IP, 1, 1, sdmi
+StaticRt STANDARD, 33, IP, 2, 0, sdmi
+TrailerF STANDARD, 34, UNUMBER8, 1, 1, sdmi
+ArpTimeO STANDARD, 35, UNUMBER32, 1, 1, sdmi
+EthEncap STANDARD, 36, UNUMBER8, 1, 1, sdmi
+TcpTTL STANDARD, 37, UNUMBER8, 1, 1, sdmi
+TcpKaInt STANDARD, 38, UNUMBER32, 1, 1, sdmi
+TcpKaGbF STANDARD, 39, UNUMBER8, 1, 1, sdmi
+NISdmain STANDARD, 40, ASCII, 1, 0, sdmi
+NISservs STANDARD, 41, IP, 1, 0, sdmi
+NTPservs STANDARD, 42, IP, 1, 0, sdmi
+Vendor STANDARD, 43, OCTET, 1, 0, sdi
+NetBNms STANDARD, 44, IP, 1, 0, sdmi
+NetBDsts STANDARD, 45, IP, 1, 0, sdmi
+NetBNdT STANDARD, 46, UNUMBER8, 1, 1, sdmi
+NetBScop STANDARD, 47, ASCII, 1, 0, sdmi
+XFontSrv STANDARD, 48, IP, 1, 0, sdmi
+XDispMgr STANDARD, 49, IP, 1, 0, sdmi
+ReqIP STANDARD, 50, IP, 1, 1, sdi
+LeaseTim STANDARD, 51, UNUMBER32, 1, 1, sdmi
+OptOvrld STANDARD, 52, UNUMBER8, 1, 1, sdi
+DHCPType STANDARD, 53, UNUMBER8, 1, 1, sdi
+ServerID STANDARD, 54, IP, 1, 1, sdi
+ReqList STANDARD, 55, OCTET, 1, 0, sdi
+Message STANDARD, 56, ASCII, 1, 0, sdi
+DHCP_MTU STANDARD, 57, UNUMBER16, 1, 1, sdi
+T1Time STANDARD, 58, UNUMBER32, 1, 1, sdmi
+T2Time STANDARD, 59, UNUMBER32, 1, 1, sdmi
+ClassID STANDARD, 60, ASCII, 1, 0, sdi
+ClientID STANDARD, 61, OCTET, 1, 0, sdi
+NW_dmain STANDARD, 62, ASCII, 1, 0, sdmi
+NWIPOpts STANDARD, 63, OCTET, 1, 128, sdmi
+NIS+dom STANDARD, 64, ASCII, 1, 0, sdmi
+NIS+serv STANDARD, 65, IP, 1, 0, sdmi
+TFTPsrvN STANDARD, 66, ASCII, 1, 64, sdmi
+OptBootF STANDARD, 67, ASCII, 1, 128, sdmi
+MblIPAgt STANDARD, 68, IP, 1, 0, sdmi
+SMTPserv STANDARD, 69, IP, 1, 0, sdmi
+POP3serv STANDARD, 70, IP, 1, 0, sdmi
+NNTPserv STANDARD, 71, IP, 1, 0, sdmi
+WWWservs STANDARD, 72, IP, 1, 0, sdmi
+Fingersv STANDARD, 73, IP, 1, 0, sdmi
+IRCservs STANDARD, 74, IP, 1, 0, sdmi
+STservs STANDARD, 75, IP, 1, 0, sdmi
+STDAservs STANDARD, 76, IP, 1, 0, sdmi
+UserClas STANDARD, 77, ASCII, 1, 0, sdi
+SLP_DA STANDARD, 78, OCTET, 1, 0, sdmi
+SLP_SS STANDARD, 79, OCTET, 1, 0, sdmi
+AgentOpt STANDARD, 82, OCTET, 1, 0, sdi
+FQDN STANDARD, 89, OCTET, 1, 0, sdmi
+
+#
+# DHCP packet fields. Code field is byte offset into DHCP packet.
+#
+
+Opcode FIELD, 0, UNUMBER8, 1, 1, id
+Htype FIELD, 1, UNUMBER8, 1, 1, id
+HLen FIELD, 2, UNUMBER8, 1, 1, id
+Hops FIELD, 3, UNUMBER8, 1, 1, id
+Xid FIELD, 4, UNUMBER32, 1, 1, id
+Secs FIELD, 8, UNUMBER16, 1, 1, id
+Flags FIELD, 10, OCTET, 1, 2, id
+Ciaddr FIELD, 12, IP, 1, 1, id
+Yiaddr FIELD, 16, IP, 1, 1, id
+BootSrvA FIELD, 20, IP, 1, 1, idm
+Giaddr FIELD, 24, IP, 1, 1, id
+Chaddr FIELD, 28, OCTET, 1, 16, id
+BootSrvN FIELD, 44, ASCII, 1, 64, idm
+BootFile FIELD, 108, ASCII, 1, 128, idm
+Magic FIELD, 236, OCTET, 1, 4, id
+Options FIELD, 240, OCTET, 1, 60, id
+
+
+#
+# Internal fields.
+#
+
+Hostname INTERNAL, 1024, BOOL, 0, 0, dm
+LeaseNeg INTERNAL, 1025, BOOL, 0, 0, dm
+EchoVC INTERNAL, 1026, BOOL, 0, 0, dm
+BootPath INTERNAL, 1027, ASCII, 1, 128, dm
+
+
+#
+# SunOS vendor space -- see the Solaris System Administrator
+# documentation for more information on these options.
+#
+
+SrootOpt VENDOR, 1, ASCII, 1, 0, smi
+SrootIP4 VENDOR, 2, IP, 1, 1, smi
+SrootNM VENDOR, 3, ASCII, 1, 0, smi
+SrootPTH VENDOR, 4, ASCII, 1, 0, smi
+SswapIP4 VENDOR, 5, IP, 1, 1, smi
+SswapPTH VENDOR, 6, ASCII, 1, 0, smi
+SbootFIL VENDOR, 7, ASCII, 1, 0, smi
+Stz VENDOR, 8, ASCII, 1, 0, smi
+SbootRS VENDOR, 9, UNUMBER16, 1, 1, smi
+SinstIP4 VENDOR, 10, IP, 1, 1, smi
+SinstNM VENDOR, 11, ASCII, 1, 0, smi
+SinstPTH VENDOR, 12, ASCII, 1, 0, smi
+SsysidCF VENDOR, 13, ASCII, 1, 0, smi
+SjumpsCF VENDOR, 14, ASCII, 1, 0, smi
+Sterm VENDOR, 15, ASCII, 1, 0, smi
+SbootURI VENDOR, 16, ASCII, 1, 0, smi
+SHTTPproxy VENDOR, 17, ASCII, 1, 0, smi
+
+#
+# Site option example:
+# The following option describes an option named ipPairs, that is in
+# the SITE category, meaning it is defined by each individual site.
+# It is option code 132, which is of type IP Address, consisting of
+# a potentially infinite number of pairs of IP addresses. (See
+# dhcp_inittab(4) for details)
+#
+# ipPairs SITE, 132, IP, 2, 0, sdmi
+#
diff --git a/usr/src/cmd/cmd-inet/etc/hosts b/usr/src/cmd/cmd-inet/etc/hosts
new file mode 100644
index 0000000000..6cd1caea23
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/etc/hosts
@@ -0,0 +1,28 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 1992 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Internet host table
+#
+127.0.0.1 localhost
diff --git a/usr/src/cmd/cmd-inet/etc/ike/Makefile b/usr/src/cmd/cmd-inet/etc/ike/Makefile
new file mode 100644
index 0000000000..71ccb3e006
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/etc/ike/Makefile
@@ -0,0 +1,65 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+# cmd/cmd-inet/etc/ike/Makefile
+#
+
+IKEDIR= ike
+PUBLICKEYDIR= publickeys
+CRLDIR= crls
+ETCPROG= config.sample
+
+include ../../../Makefile.cmd
+
+ETCINETIKEDIR= $(ROOTETC)/inet/$(IKEDIR)
+ETCINETPUBLICKEYDIR= $(ROOTETC)/inet/$(IKEDIR)/$(PUBLICKEYDIR)
+ETCINETCRLDIR= $(ROOTETC)/inet/$(IKEDIR)/$(CRLDIR)
+ETCINETIKEPROG= $(ETCPROG:%=$(ETCINETIKEDIR)/%)
+
+FILEMODE= 0444
+OWNER= root
+GROUP= sys
+
+.KEEP_STATE:
+
+all: $(ETCPROG)
+
+install: all $(ETCINETIKEDIR) $(ETCINETPUBLICKEYDIR) $(ETCINETIKEPROG) \
+ $(ETCINETCRLDIR)
+
+$(ETCINETIKEDIR)/% : %
+ $(INS.file)
+
+$(ETCINETIKEDIR):
+ $(INS.dir)
+
+$(ETCINETPUBLICKEYDIR) $(ETCINETCRLDIR):
+ $(INS.dir)
+
+FRC:
+
+clean clobber lint:
diff --git a/usr/src/cmd/cmd-inet/etc/ike/config.sample b/usr/src/cmd/cmd-inet/etc/ike/config.sample
new file mode 100644
index 0000000000..a9851a8d8a
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/etc/ike/config.sample
@@ -0,0 +1,133 @@
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+##
+## This file should be copied into /etc/inet/ike/config to enable the
+## launch of the IKE daemon, in.iked(1m), at boot time. You can also
+## launch the IKE daemon after creating this file without rebooting by
+## invoking /usr/lib/inet/in.iked with a root shell.
+##
+
+# Consult the ike.config(4) man page for further details. Here is a small
+# example from the man page.
+
+### BEGINNING OF FILE
+
+### First some global parameters...
+
+## Optional hardware acceleration parameters...
+## Use the pathname of a library that supports PKCS#11 in quotes.
+## The example path is for the Sun Crypto Accelerator 1000.
+# pkcs11_path "/opt/SUNWconn/lib/libpkcs11.so"
+
+## certificate parameters...
+
+# Root certificates. I SHOULD use a full Distinguished Name.
+# I MUST have this certificate in my local filesystem, see ikecert(1m).
+cert_root "C=US, O=Sun Microsystems\\, Inc., CN=Sun CA"
+
+# Explicitly trusted certs that need no signatures, or perhaps self-signed
+# ones. Like root certificates, use full DNs for them for now.
+cert_trust "EMAIL=root@domain.org"
+
+# Where do I send LDAP requests?
+ldap_server "ldap1.domain.org,ldap2.domain.org:389"
+
+# Some PKI-specific tweaks...
+# If you wish to ignore CRLs, uncomment this:
+#ignore_crls
+# If you wish to use HTTP (with name resolution) for URLs inside certs,
+# uncomment this:
+#use_http
+# HTTP proxy and socks URLs should also be indicated if needed...
+socks "socks://socks-relay.domain.org"
+#proxy "http://http-proxy.domain.org:8080"
+
+## Phase 1 transform defaults...
+
+p1_lifetime_secs 14400
+p1_nonce_len 20
+
+## Parameters that may also show up in rules.
+
+p1_xform { auth_method preshared oakley_group 5 auth_alg sha encr_alg 3des }
+p2_pfs 2
+
+### Now some rules...
+
+{
+ label "simple inheritor"
+ local_id_type ip
+ local_addr 10.1.1.1
+ remote_addr 10.1.1.2
+}
+
+{
+ # an index-only rule. If I'm a receiver, and all I
+ # have are index-only rules, what do I do about inbound IKE requests?
+ # Answer: Take them all!
+
+ label "default rule"
+ # Use whatever "host" (e.g. IP address) identity is appropriate
+ local_id_type ipv4
+
+ local_addr 0.0.0.0/0
+ remote_addr 0.0.0.0/0
+
+ p2_pfs 5
+
+ # Now I'm going to have the p1_xforms
+ p1_xform
+ {auth_method preshared oakley_group 5 auth_alg md5 encr_alg blowfish }
+ p1_xform
+ {auth_method preshared oakley_group 5 auth_alg md5 encr_alg 3des }
+
+ # After said list, another keyword (or a '}') will stop xform parsing.
+}
+
+{
+ # Let's try something a little more conventional.
+
+ label "host to .80 subnet"
+ local_id_type ip
+ local_id "10.1.86.51"
+
+ remote_id "" # Take any, use remote_addr for access control.
+
+ local_addr 10.1.86.51
+ remote_addr 10.1.80.0/24
+
+ p1_xform
+ { auth_method rsa_sig oakley_group 5 auth_alg md5 encr_alg 3des }
+ p1_xform
+ { auth_method rsa_sig oakley_group 5 auth_alg md5 encr_alg blowfish }
+ p1_xform
+ { auth_method rsa_sig oakley_group 5 auth_alg sha1 encr_alg 3des }
+ p1_xform
+ { auth_method rsa_sig oakley_group 5 auth_alg sha1 encr_alg blowfish }
+}
+
diff --git a/usr/src/cmd/cmd-inet/etc/inetd.conf b/usr/src/cmd/cmd-inet/etc/inetd.conf
new file mode 100644
index 0000000000..0965497d35
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/etc/inetd.conf
@@ -0,0 +1,41 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Legacy configuration file for inetd(1M). See inetd.conf(4).
+#
+# This file is no longer directly used to configure inetd.
+# The Solaris services which were formerly configured using this file
+# are now configured in the Service Management Facility (see smf(5))
+# using inetadm(1M).
+#
+# Any records remaining in this file after installation or upgrade,
+# or later created by installing additional software, must be converted
+# to smf(5) services and imported into the smf repository using
+# inetconv(1M), otherwise the service will not be available. Once
+# a service has been converted using inetconv, further changes made to
+# its entry here are not reflected in the service.
+#
diff --git a/usr/src/cmd/cmd-inet/etc/init.d/Makefile b/usr/src/cmd/cmd-inet/etc/init.d/Makefile
new file mode 100644
index 0000000000..971a57112e
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/etc/init.d/Makefile
@@ -0,0 +1,98 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# cmd/cmd-inet/etc/init.d/Makefile
+
+PROG4= ncalogd
+PROG5= ncakmod
+MIPD= mipagent
+PPPD= pppd
+PROG= $(PROG4) $(PROG5) \
+ $(MIPD) $(PPPD)
+
+include ../../../Makefile.cmd
+
+STARTINET3= $(ROOTETC)/rc2.d/S94ncalogd
+STARTINET4= $(ROOTETC)/rc2.d/S42ncakmod
+
+MIPDK= K06mipagent
+MIPDS= S80mipagent
+
+PPPDK= K50pppd
+PPPDS= S47pppd
+
+INITD= $(ROOTETC)/init.d
+DIRS= $(INITD) $(ROOTETC)/rc0.d $(ROOTETC)/rcS.d $(ROOTETC)/rc1.d \
+ $(ROOTETC)/rc2.d $(ROOTETC)/rc3.d
+FILEMODE= 0744
+OWNER= root
+GROUP= sys
+
+ETCINITPROG= $(PROG:%=$(INITD)/%)
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+install: all $(DIRS) $(ETCINITPROG) $(ETCDFLTPROG) ln_PROG
+
+$(INITD)/% : % $(INITD)
+ $(INS.file)
+
+ln_PROG : $(ETCINITPROG)
+ @for dir in rc0.d rcS.d rc1.d; do \
+ echo $(LN) $(INITD)/$(MIPD) $(ROOTETC)/$$dir/$(MIPDK); \
+ $(RM) $(ROOTETC)/$$dir/$(MIPDK); \
+ $(LN) $(INITD)/$(MIPD) $(ROOTETC)/$$dir/$(MIPDK); \
+ echo $(LN) $(INITD)/$(PROG4) $(ROOTETC)/$$dir/K34ncalogd; \
+ $(RM) $(ROOTETC)/$$dir/K34ncalogd; \
+ $(LN) $(INITD)/$(PROG4) $(ROOTETC)/$$dir/K34ncalogd; \
+ echo $(LN) $(INITD)/$(PPPD) $(ROOTETC)/$$dir/$(PPPDK); \
+ $(RM) $(ROOTETC)/$$dir/$(PPPDK); \
+ $(LN) $(INITD)/$(PPPD) $(ROOTETC)/$$dir/$(PPPDK); \
+ done
+ -$(RM) $(ROOTETC)/rc2.d/$(MIPDK)
+ $(LN) $(INITD)/$(MIPD) $(ROOTETC)/rc2.d/$(MIPDK)
+ -$(RM) $(ROOTETC)/rc3.d/$(MIPDS)
+ $(LN) $(INITD)/$(MIPD) $(ROOTETC)/rc3.d/$(MIPDS)
+ -$(RM) $(STARTINET3)
+ $(LN) $(INITD)/$(PROG4) $(STARTINET3)
+ -$(RM) $(STARTINET4)
+ $(LN) $(INITD)/$(PROG5) $(STARTINET4)
+ -$(RM) $(ROOTETC)/rc2.d/$(PPPDS)
+ $(LN) $(INITD)/$(PPPD) $(ROOTETC)/rc2.d/$(PPPDS)
+
+$(DIRS):
+ $(INS.dir)
+
+clean:
+
+lint:
+
+include ../../../Makefile.targ
+
+.PARALLEL:
diff --git a/usr/src/cmd/cmd-inet/etc/init.d/mipagent b/usr/src/cmd/cmd-inet/etc/init.d/mipagent
new file mode 100644
index 0000000000..e30c7028a2
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/etc/init.d/mipagent
@@ -0,0 +1,42 @@
+#!/sbin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 1999 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+
+case "$1" in
+'start')
+ [ -f /etc/inet/mipagent.conf ] && /usr/lib/inet/mipagent >/dev/msglog 2>&1 &
+ ;;
+'stop')
+ /usr/bin/pkill -x -u 0 -P 1 mipagent
+ ;;
+*)
+ echo "Usage: $0 { start | stop }"
+ exit 1
+ ;;
+esac
+exit 0
diff --git a/usr/src/cmd/cmd-inet/etc/init.d/ncakmod b/usr/src/cmd/cmd-inet/etc/init.d/ncakmod
new file mode 100644
index 0000000000..e29d31453d
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/etc/init.d/ncakmod
@@ -0,0 +1,121 @@
+#!/sbin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 1999-2002 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+# Default config values used by script
+nca=drv/nca
+ncakmodconf=/etc/nca/ncakmod.conf
+ncaifconf=/etc/nca/nca.if
+tempdir=/tmp
+default_miss_door=/var/run/nca_httpd_1.door
+
+# Function used to parse the interface names from /etc/hostname.* entries
+readifconf()
+{
+ while read i; do
+ case "$i" in
+ '#'* | '') # Ignore comments, empty lines
+ continue ;;
+ '*') configinterfaces="`echo /etc/hostname.*[0-9] \
+ 2>/dev/null`"
+ checkforvirt=false
+ break ;;
+ esac
+ configinterfaces="$configinterfaces $i"
+ done
+}
+
+case "$1" in
+'start')
+
+ if [ ! -f $ncakmodconf ]; then
+ # If configuration file is missing, just exit
+ exit 0
+ fi
+
+ . $ncakmodconf
+
+ # Default is "disabled" so we want to exit
+ [ "x$status" != "xenabled" ] && exit 0
+
+ if [ -f "$ncaifconf" ]; then
+ readifconf < $ncaifconf
+ configinterfaces="`echo $configinterfaces | \
+ /bin/sed 's/.etc.hostname.//g'`"
+ for i in $configinterfaces; do
+ findinterface="`echo $i | /bin/grep '[0-9][0-9]*'`"
+ if [ $? -ne 0 ]; then
+ # Need to expand interface (ie. iprb)
+ interface="`echo /etc/hostname.$i*[0-9] \
+ 2>/dev/null | /bin/sed \
+ 's/.etc.hostname.//g'`"
+ interfaces="$interfaces $interface"
+ else
+ interfaces="$interfaces $i"
+ fi
+ done
+
+ # If we don't have any interfaces configured, exit
+ [ -z "$interfaces" ] && exit 0
+
+ # Prevent multiple instances of ncaconfd
+ if /bin/pgrep ncaconfd > /dev/null 2>&1; then
+ echo "$0: ncaconfd is already running"
+ exit 1
+ fi
+
+ /usr/sbin/modload -p $nca
+
+ # Insert NCA into the stream of all the interfaces configured.
+ interfaces="`echo $interfaces | /bin/tr ' ' '\012' | \
+ /bin/grep -v :`"
+ if [ "x$nca_active" != xenabled ]; then
+ /usr/lib/inet/ncaconfd -l $interfaces
+ else
+ /usr/lib/inet/ncaconfd -al $interfaces
+ fi
+
+ if [ "$httpd_door_path" != "$default_miss_door" ]; then
+ # Set the default HTTPD door in NCA via ndd
+ /usr/sbin/ndd -set /dev/nca httpd_door_path \
+ $httpd_door_path
+ fi
+ fi
+ ;;
+
+'stop')
+ # Need to reboot the system to stop
+ echo "System reset is required to stop NCA functionality"
+ ;;
+
+*)
+
+ echo "Usage: $0 { start | stop }"
+ exit 1
+ ;;
+esac
+exit 0
diff --git a/usr/src/cmd/cmd-inet/etc/init.d/ncalogd b/usr/src/cmd/cmd-inet/etc/init.d/ncalogd
new file mode 100644
index 0000000000..7a435579b5
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/etc/init.d/ncalogd
@@ -0,0 +1,140 @@
+#!/sbin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright (c) 1998-2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+# Default location for script
+ncalogd=/etc/init.d/ncalogd
+success=1
+
+# Default config values used by script
+ncalogdconf=/etc/nca/ncalogd.conf
+ncakmodconf=/etc/nca/ncakmod.conf
+
+isValidFile() {
+ # Check if file exists
+ if [ ! -f $1 ]
+ then
+ # Create subdirectories
+ logd_dir=`/usr/bin/dirname $1`
+ if [ ! -d "$logd_dir" ]; then
+ /usr/bin/mkdir -m 0755 -p $logd_dir > /dev/null 2>&1
+ if [ $? != 0 ]; then
+ echo "Error: $ncalogd: unable to" \
+ "create directory $logd_dir"
+ return 1
+ fi
+ fi
+ # Create the log file
+ touch $1
+ if [ $? != 0 ]; then
+ echo "Error: ${ncalogd}: unable to create file $1"
+ return 1
+ fi
+ fi
+
+ # test if valid local file
+ df -l $1 > /dev/null 2>&1
+ if [ $? != 0 ]; then
+ echo "Error: $ncalogd: $1 is not a local file system"
+ return 1
+ fi
+ return 0
+}
+
+isValidDev() {
+ # Check if device is valid
+ fsck -m $1 > /dev/null 2>&1
+ case $? in
+ 36 | 39 )
+ return 0
+ ;;
+ 0 | 32 | 33 | 40 )
+ echo "Error: $ncalogd: refusing to overwrite filesystem on $1"
+ return 1
+ ;;
+ * )
+ echo "Error: $ncalogd: $1 is an invalid device"
+ return 1
+ ;;
+ esac
+}
+
+case "$1" in
+'start')
+ if [ ! -f $ncalogdconf ]; then
+ # If configuration file is missing, just exit
+ exit 0
+ fi
+
+ . $ncalogdconf
+
+ # Default is "disabled" so we want to exit
+ [ "x$status" != "xenabled" ] && exit 0
+
+ . $ncakmodconf
+
+ # Default is "disabled" so we want to exit
+ [ "x$status" != "xenabled" ] && exit 0
+
+ for i in $logd_path_name; do
+ # make sure that specified logfile is not a directory
+ if [ -d $i ]; then
+ echo "Error: $ncalogd: $i is a directory"
+ continue
+ elif [ -b $i -o -c $i ]; then
+ # Check if file is a device
+ isValidDev $i || continue
+ else
+ isValidFile $i || continue
+ fi
+
+ # Finally, set the specified file as a NCA log file
+ /usr/sbin/ndd -set /dev/nca nca_log_file $i
+ success=0
+ done
+
+ if [ $success = 0 ]; then
+ [ "x$logd_file_size" != "x" ] && \
+ /usr/sbin/ndd -set /dev/nca nca_log_size $logd_file_size
+ /usr/sbin/ndd -set /dev/nca nca_logging_on 1
+ fi
+ ;;
+
+'stop')
+ . $ncakmodconf
+
+ if [ "x$status" = "xenabled" ]; then
+ /usr/sbin/ndd -set /dev/nca nca_logging_on 0
+ fi
+ ;;
+
+*)
+ echo "Usage: $0 { start | stop }"
+ exit 1
+ ;;
+esac
+exit 0
diff --git a/usr/src/cmd/cmd-inet/etc/init.d/pppd b/usr/src/cmd/cmd-inet/etc/init.d/pppd
new file mode 100644
index 0000000000..cdb57e62f5
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/etc/init.d/pppd
@@ -0,0 +1,96 @@
+#!/sbin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright (c) 2000-2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+PATH=/sbin:/usr/bin:/usr/sbin; export PATH
+PPPDIR=/etc/ppp; export PPPDIR
+
+case "$1" in
+'start')
+ if [ ! -x /usr/bin/pppd -o ! -c /dev/sppp ]; then
+ echo "$0: Solaris PPP has not been correctly installed on"
+ echo "$0: this system. Required files are missing."
+ exit 1
+ fi
+ if [ -f $PPPDIR/ifconfig ]; then
+ . $PPPDIR/ifconfig
+ fi
+ if [ -f $PPPDIR/demand ]; then
+ . $PPPDIR/demand
+ fi
+ if [ -f $PPPDIR/pppoe.if ] && [ -x /usr/sbin/sppptun ]; then
+ sed -e 's/^#.*//;s/\([^\\]\)#.*/\1/;s/[ ]*$//;s/^[ ]*//' \
+ $PPPDIR/pppoe.if | \
+ while read intf; do
+ if [ "$intf" ]; then
+ /usr/sbin/sppptun plumb pppoe $intf
+ /usr/sbin/sppptun plumb pppoed $intf
+ fi
+ done
+ fi
+ if [ -f $PPPDIR/pppoe ] && [ -x /usr/lib/inet/pppoed ]; then
+ /usr/lib/inet/pppoed >/dev/null
+ fi
+ ;;
+
+'stop')
+ /usr/bin/pkill -x pppd
+ sleep 1
+ /usr/bin/pkill -x pppoed
+
+ # Use ifconfig to make the interfaces down just in case
+ if [ -f $PPPDIR/ifconfig ]; then
+ nawk '/ifconfig[ ]*sppp/ { \
+ system("ifconfig " $2 " down"); \
+ system("ifconfig " $2 " unplumb"); \
+ next; \
+ } \
+ /ifconfig/ { \
+ $3 = "removeif"; \
+ NF = 4; \
+ system($0); \
+ }' < $PPPDIR/ifconfig
+ fi
+
+ if [ -f $PPPDIR/pppoe.if ] && [ -x /usr/sbin/sppptun ]; then
+ sed -e 's/^#.*//;s/\([^\\]\)#.*/\1/;s/[ ]*$//;s/^[ ]*//' \
+ $PPPDIR/pppoe.if | \
+ while read intf; do
+ if [ "$intf" ]; then
+ /usr/sbin/sppptun unplumb ${intf}:pppoe
+ /usr/sbin/sppptun unplumb ${intf}:pppoed
+ fi
+ done
+ fi
+ ;;
+
+*)
+ echo "Usage: $0 { start | stop }"
+ exit 1
+ ;;
+esac
+exit 0
diff --git a/usr/src/cmd/cmd-inet/etc/ipaddrsel.conf b/usr/src/cmd/cmd-inet/etc/ipaddrsel.conf
new file mode 100644
index 0000000000..65bd3f0938
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/etc/ipaddrsel.conf
@@ -0,0 +1,36 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# This is the IPv6 default address selection policy table. See
+# ipaddrsel(1M) for details and examples.
+#
+# Prefix Precedence Label
+::1/128 50 Loopback
+::/0 40 Default
+2002::/16 30 6to4
+::/96 20 IPv4_Compatible
+::ffff:0.0.0.0/96 10 IPv4
diff --git a/usr/src/cmd/cmd-inet/etc/ipnodes b/usr/src/cmd/cmd-inet/etc/ipnodes
new file mode 100644
index 0000000000..ca09c2e09b
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/etc/ipnodes
@@ -0,0 +1,30 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 1999 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+
+#
+# Internet host table
+#
+::1 localhost
+127.0.0.1 localhost
diff --git a/usr/src/cmd/cmd-inet/etc/ipqosconf.1.sample b/usr/src/cmd/cmd-inet/etc/ipqosconf.1.sample
new file mode 100644
index 0000000000..12652b8df5
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/etc/ipqosconf.1.sample
@@ -0,0 +1,135 @@
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+# Mandatory file version identifier
+fmt_version 1.0
+
+# This configuration marks video traffic for EF PHB, i.e. Expedited Forwarding.
+# Mail traffic is marked for AF11, anonymous user FTP traffic for AF12 and news
+# traffic for AF13 PHBs, i.e. Assured Forwarding class 1 with drop precedences
+# low medium and high respectively.
+# For information on AF and EF PHBs refer to the IPQoS Administration Guide or
+# the RFCs 2597 and 2598 respectively.
+#
+# Before this configuration file can be applied the sport parameter of the
+# filter videoout needs to be given a valid port number/service name of a
+# service whose traffic you wish to apply EF to and the uid parameter of the
+# filter ftpout needs to be given the uid of the user ftp. The ftp user account
+# is the one used by the ftp server for anonymous logins, thus filtering on
+# this enables us to capture anonymous ftp user traffic.
+
+action {
+ module ipgpc
+ # Name must be ipgpc.classify for ipgpc action.
+ name ipgpc.classify
+
+ class {
+ name video
+ next_action markEF
+ }
+ class {
+ name mail
+ next_action markAF11
+ }
+ class {
+ name ftp
+ next_action markAF12
+ }
+ class {
+ name news
+ next_action markAF13
+ }
+
+ filter {
+ name videoout
+ # Source port of video traffic, given by __videoport__.
+ sport __videoport__
+ # Locally generated outbound traffic.
+ direction LOCAL_OUT
+ class video
+ }
+ filter {
+ name mailout
+ sport smtp
+ direction LOCAL_OUT
+ class mail
+ }
+ # This filter catches anonymous ftp user outgoing traffic.
+ filter {
+ name ftpout
+ direction LOCAL_OUT
+ # Traffic generated by ftp user, given by __ftp-uid__.
+ uid __ftp-uid__
+ class ftp
+ }
+ filter {
+ name newsout
+ sport nntp
+ direction LOCAL_OUT
+ class news
+ }
+}
+
+# Mark the DSCP with code point EF, 101110 = 46.
+action {
+ module dscpmk
+ name markEF
+ params {
+ # Set all 64 entries of dscp_map to 46 decimal.
+ dscp_map {0-63:46}
+ next_action continue
+ }
+}
+
+# Mark the DSCP with code point AF11, 001010 = 10.
+action {
+ module dscpmk
+ name markAF11
+ params {
+ dscp_map {0-63:10}
+ next_action continue
+ }
+}
+
+# Mark the DSCP with code point AF12, 001100 = 12.
+action {
+ module dscpmk
+ name markAF12
+ params {
+ dscp_map {0-63:12}
+ next_action continue
+ }
+}
+
+# Mark the DSCP with code point AF13, 001110 = 14.
+action {
+ module dscpmk
+ name markAF13
+ params {
+ dscp_map {0-63:14}
+ next_action continue
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/etc/ipqosconf.2.sample b/usr/src/cmd/cmd-inet/etc/ipqosconf.2.sample
new file mode 100644
index 0000000000..05049ee09c
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/etc/ipqosconf.2.sample
@@ -0,0 +1,137 @@
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+# Mandatory file format version identifier.
+fmt_version 1.0
+
+# Meter traffic from application (identified by source port myport) to
+# somehost.somedomain, where somehost.somedomain is a valid hostname/IP address.
+# Mark a packet with AF11 if it does not exceed the committed burst, AF12 if it
+# exceeds committed burst, but not excess burst and AF13 if it exceeds the
+# excess burst.
+# For information on AF PHBs refer to the IPQoS Administration Guide or the
+# RFC 2597.
+#
+# Before this example configuration file can be applied the sport and daddr
+# parameter values in the ipgpc filter myfilter need to be given actual values.
+
+action {
+ module ipgpc
+ # Name must be ipgpc.classify for ipgpc action.
+ name ipgpc.classify
+
+ filter {
+ name myfilter
+ class meter_myapp
+ sport myport
+ daddr somehost.somedomain
+ }
+ class {
+ name meter_myapp
+ next_action meter5mbps
+ enable_stats true
+ }
+ params {
+ global_stats true
+ }
+}
+
+# meter5mbps invokes action af11 for a packet that does not exceed the
+# committed burst, af12 if it exceeds committed burst, but not excess burst
+# and af13 if it exceeds excess burst.
+action {
+ module tokenmt
+ name meter5mbps
+ params {
+ # Committed rate of 5 Mbps.
+ committed_rate 5000000
+ # Committed burst of 5 Mb.
+ committed_burst 5000000
+ # Excess Burst of 10 Mb.
+ peak_burst 10000000
+ # Action global stats enabled.
+ global_stats true
+ # RED action, mark DSCP with AF13.
+ red_action_name af13
+ # YELLOW action, mark DSCP with AF12.
+ yellow_action_name af12
+ # GREEN action, mark DSCP with AF13.
+ green_action_name af11
+ # Not color aware.
+ color_aware false
+ }
+}
+
+# Mark the DSCP with code point AF13, 001110 = 14.
+action {
+ module dscpmk
+ name af13
+ params {
+ # Enable global stats for action.
+ global_stats true
+ next_action acct_classaf1
+ # Set all 64 entries of dscp_map to 14 decimal.
+ dscp_map {0-63:14}
+ }
+}
+
+# Mark the DSCP with code point AF12, 001100 = 12.
+action {
+ module dscpmk
+ name af12
+ params {
+ global_stats true
+ next_action acct_classaf1
+ dscp_map {0-63:12}
+ }
+}
+
+# Mark the DSCP with code point AF11, 001010 = 10.
+action {
+ module dscpmk
+ name af11
+ params {
+ global_stats true
+ next_action acct_classaf1
+ dscp_map {0-63:10}
+ }
+}
+
+# Account packets for class AF1* (AF11, AF12 and AF13).
+action {
+ module flowacct
+ name acct_classaf1
+ params {
+ global_stats true
+ next_action continue
+ # Timeout flows if packets not seen for at least 60 secs.
+ timeout 60000
+ # Scan the flow table every 15 secs for removing timed out flows.
+ timer 15000
+ # Limit number of flow records in the table to 2K.
+ max_limit 2048
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/etc/ipqosconf.3.sample b/usr/src/cmd/cmd-inet/etc/ipqosconf.3.sample
new file mode 100644
index 0000000000..3e2c3eb27c
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/etc/ipqosconf.3.sample
@@ -0,0 +1,71 @@
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+# Mandatory file version identifier
+fmt_version 1.0
+
+# This configuration marks outbound web traffic ethernet
+# headers on a VLAN interface with a user priority corresponding
+# with the Class of Service value 1.
+
+# Before this configuration can be applied the daddr parameter
+# of filter webout needs to be given a valid ip address/hostname.
+
+action {
+ module ipgpc
+ # Name must be ipgpc.classify for ipgpc action.
+ name ipgpc.classify
+
+ class {
+ name web
+ next_action dlmark1
+ }
+
+ filter {
+ name webout
+ # Source port 80.
+ sport 80
+ # Outgoing locally generated traffic.
+ direction LOCAL_OUT
+ # w.x.y.z and the interface over which this
+ # packet leaves belong to the same VLAN
+ # group.
+ daddr w.x.y.z
+ class web
+ }
+}
+
+# Mark traffic ethernet header user priority with the value
+# corresponding with the CoS value 1.
+action {
+ module dlcosmk
+ name dlmark1
+ params {
+ # Class of Service value.
+ cos 1
+ next_action continue
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/etc/ipsecalgs b/usr/src/cmd/cmd-inet/etc/ipsecalgs
new file mode 100644
index 0000000000..d97ec0c710
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/etc/ipsecalgs
@@ -0,0 +1,48 @@
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# DO NOT EDIT OR PARSE THIS FILE!
+#
+# Use the ipsecalgs(1m) command to change the contents of this file.
+
+# PROTO|protocol-id|protocol-name|exec-mode
+## NOTE: Some protocol numbers are well-known and defined in <netdb.h>
+
+PROTO|2|PROTO_IPSEC_AH|sync
+PROTO|3|PROTO_IPSEC_ESP|sync
+
+# ALG|protocol-id|alg-id|name,name,...|ef-id| \
+# {default/}{key,key..}or{key-key,inc}|block_size or MAC-size
+
+ALG|2|0|none,any|-|0|0
+ALG|2|2|hmac-md5,md5|CKM_MD5_HMAC_GENERAL|128|12
+ALG|2|3|hmac-sha1,sha,sha1,sha-1,hmac-sha,hmac-sha-1|CKM_SHA_1_HMAC_GENERAL|160|12
+ALG|3|0|any|-|0|0
+ALG|3|2|des-cbc,des|CKM_DES_CBC|64|8
+ALG|3|3|3des-cbc,3des|CKM_DES3_CBC|192|8
+ALG|3|7|blowfish-cbc,blowfish|CKM_BF_CBC|128/32-448,8|8
+ALG|3|11|null|-|0|0
+ALG|3|12|aes-cbc,aes|CKM_AES_CBC|128/128-256,64|16
diff --git a/usr/src/cmd/cmd-inet/etc/ipsecinit.sample b/usr/src/cmd/cmd-inet/etc/ipsecinit.sample
new file mode 100644
index 0000000000..76642506f2
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/etc/ipsecinit.sample
@@ -0,0 +1,87 @@
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# This file should be copied to /etc/inet/ipsecinit.conf to enable IPsec
+# systemwide policy (and as a side-effect, load IPsec kernel modules).
+# Even if this file has no entries, IPsec will be loaded if
+# /etc/inet/ipsecinit.conf exists.
+#
+# Add entries to protect the traffic using IPSEC. The entries in this
+# file are currently configured using ipsecconf from inetinit script
+# after /usr is mounted.
+#
+# For example,
+#
+# {rport 23} ipsec {encr_algs des encr_auth_algs md5}
+#
+# Or, in the older (but still usable) syntax
+#
+# {dport 23} apply {encr_algs des encr_auth_algs md5 sa shared}
+# {sport 23} permit {encr_algs des encr_auth_algs md5}
+#
+# will protect the telnet traffic originating from the host with ESP using
+# DES and MD5. Also:
+#
+# {raddr 10.5.5.0/24} ipsec {auth_algs any}
+#
+# Or, in the older (but still usable) syntax
+#
+# {daddr 10.5.5.0/24} apply {auth_algs any sa shared}
+# {saddr 10.5.5.0/24} permit {auth_algs any}
+#
+# will protect traffic to/from the 10.5.5.0 subnet with AH using any available
+# algorithm.
+#
+# To do basic filtering, a drop rule may be used. For example:
+#
+# {lport 23 dir in} drop {}
+# {lport 23 dir out} drop {}
+#
+# will disallow any remote system from telnetting in.
+#
+# If you are using IPv6, it may be useful to bypass neighbor discovery
+# to allow in.iked to work properly with on-link neighbors. To do that,
+# add the following lines:
+#
+# {ulp ipv6-icmp type 133-137 dir both } pass { }
+#
+# This will allow neighbor discovery to work normally.
+#
+# WARNING: This file is read before default routes are established, and
+# before any naming services have been started. The
+# ipsecconf(1M) command attempts to resolve names, but it will
+# fail unless the machine uses files, or DNS and the DNS server
+# is reachable via routing information before ipsecconf(1m)
+# invocation. (E.g. the DNS server is on-subnet, or DHCP
+# has loaded up the default router already.)
+#
+# It is suggested that for this file, use hostnames only if
+# they are in /etc/hosts, or use numeric IP addresses.
+#
+# If DNS gets used, the DNS server is implicitly trusted, which
+# could lead to compromise of this machine if the DNS server
+# has been compromised.
+#
diff --git a/usr/src/cmd/cmd-inet/etc/mipagent.conf-sample b/usr/src/cmd/cmd-inet/etc/mipagent.conf-sample
new file mode 100644
index 0000000000..3400ba17a9
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/etc/mipagent.conf-sample
@@ -0,0 +1,190 @@
+#
+#pragma ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Sample configuration file for mobility agents. Lines starting with the hash
+# character are treated as comments. Blank lines are ignored. All the time
+# values are in seconds unless stated otherwise. For the variable names that
+# are composed of multiple words, the practice is each word should start with
+# upper-case letter, and each value should start with lower-case.
+
+[General]
+ Version = 1.0 # version number for the configuration file. (required)
+
+
+#
+# One section for all interfaces supported by mipagent.
+# The section name must be [Advertisements <intf-name>]
+#
+# HomeAgent yes, no (Determines whether mipagent will provide
+# Home Agent functionality)
+# ForeignAgent yes, no (Determines whether mipagent will provide
+# Foreign Agent functionality)
+# PrefixFlags yes, no (Specifies whether advertisements will include
+# the prefix extension).
+# AdvertiseOnBcast yes, no (If yes, advertisements are sent on
+# 255.255.255.255, rather than 224.0.0.1)
+# RegLifetime n (maximum lifetime value accepted in registration
+# requests).
+# AdvLifetime n (Lifetime advertised in the RFC1256 portion)
+# AdvFrequency n (The frequency of mobility advertisements, in
+# seconds)
+# ReverseTunnel yes, no (Determines whether mipagent has reverse tunnel
+# decapsulation/encapsulation capability. In
+# case of foreign-agent it also means that the
+# foreign agent is advertising reverse tunnel)
+#
+# ReverseTunnelRequired yes, no (Determines local policy of the mipagent
+# on registration request, i.e whether a mobile
+# should/must request reverse tunnel)
+
+[Advertisements hme0]
+ HomeAgent = yes
+ ForeignAgent = yes
+ PrefixFlags = yes
+ AdvertiseOnBcast = yes
+ RegLifetime = 200
+ AdvLifetime = 200
+ AdvFrequency = 5
+ ReverseTunnel = no
+ ReverseTunnelRequired = no
+
+# Advertisement section for dynamic interfaces:
+# The interface with '*' suffix determines dynamic interfaces to mipagent.
+# Additional parameters which may control Advertisement frequency in a newly
+# created mobility interface are useful when a mobility
+# interface does not want to have a periodic advertisement all the time.
+# The following configuration is useful for foreign agents.
+# AdvInitCount n Initial Advertisement count when
+# Unsolicited advertisements are limited.
+# AdvLimitUnsolicited yes, no Determines the local policy of the mipagent
+# (FA) if it sends limited or unlimited
+# unsolicited advertisement.
+# For more details on dynamic interface configuration, visit mipagent.conf(1M)
+
+#[Advertisements sppp*]
+# HomeAgent = no
+# ForeignAgent = yes
+# AdvertiseOnBcast = no
+# PrefixFlags = yes
+# RegLifetime = 300
+# AdvLifetime = 300
+# AdvFrequency = 3
+# ReverseTunnel = yes
+# ReverseTunnelRequired = no
+# AdvInitCount = 5
+# AdvLimitUnsolicited = yes
+
+
+#
+# The GlobalSecurityParameters contains all security related configuration
+# parameters.
+#
+# MaxClockSkew n (The number of seconds that mipagent will
+# accept as a difference between its own local
+# time and the time found in Registration Requets)
+# HA-FAAuth yes, no (Specifies whether HA-FA Authentication
+# extensions must be present in Registration
+# Requests and Replies)
+# MN-FAAuth yes, no (Specifies whether MN-FA Authentication
+# extensions must be present in Registration
+# Requests and Replies)
+# Challenge yes, no (Specifies whether the Foreign Agent will
+# include Challenges in it's mobility
+# advertisements)
+# KeyDistribution files (must be set to files)
+
+[GlobalSecurityParameters]
+ MaxClockSkew = 300
+ HA-FAauth = yes
+ MN-FAauth = yes
+ Challenge = no
+ KeyDistribution = files
+
+#
+# The Address Pools are defined via numerical identifiers, and contain
+
+# BaseAddress n.n.n.n (The first address in the address pool)
+# Size n (The number of addresses in the pool)
+
+[Pool 1]
+ BaseAddress = 10.68.30.7
+ Size = 4
+
+#
+# The SPIs must be configured. An SPI entry contains a numerical value
+# the replay method and keying information.
+#
+# ReplyMethod none, timestamp (Specifies the type of replay
+# authentication for the SPI)
+# Key x (Authentication key in hexadecimal)
+
+[SPI 257]
+ ReplayMethod = none
+ Key= 11111111111111111111111111111111
+
+[SPI 258]
+ ReplayMethod = none
+ Key= 15111111111111111111111111111111
+
+#
+# The Address section contains configuration information for mobility
+# nodes (foreign and home agents) as well as mobile nodes.
+#
+# The # Node-Default keyword in the section header is used to define a
+# default SPI for all mobile nodes. This allows an administrator to
+# simply include a single entry for all mobile nodes, assuming that
+# they all use the same SPI. The Default-Node entry must include the
+# pool entry.
+#
+# The Address section may also contain an NAI as opposed to
+# the home address. These entries must also include the Pool
+# entry.
+#
+# Type node, agent (Specifies whether the entry is for
+# a mobile node, or a mobility agent)
+# SPI n (The SPI value associated with the
+# entry, which must be configured above)
+# Pool n (If the section header contained an NAI,
+# an address will be allocated for the
+# mobile node from the pool defined)
+
+[Address 10.1.1.1]
+ Type = node
+ SPI = 258
+
+[Address mobilenode@sun.com]
+ Type = node
+ SPI = 257
+ Pool = 1
+
+[Address Node-Default]
+ Type = node
+ SPI = 258
+ Pool = 1
+
+[Address 10.68.30.36]
+ Type = agent
+ SPI = 257
diff --git a/usr/src/cmd/cmd-inet/etc/mipagent.conf.fa-sample b/usr/src/cmd/cmd-inet/etc/mipagent.conf.fa-sample
new file mode 100644
index 0000000000..dfe8355a78
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/etc/mipagent.conf.fa-sample
@@ -0,0 +1,173 @@
+#
+#pragma ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Sample configuration file for mobility agents. Lines starting with the hash
+# character are treated as comments. Blank lines are ignored. All the time
+# values are in seconds unless stated otherwise. For the variable names that
+# are composed of multiple words, the practice is each word should start with
+# upper-case letter, and each value should start with lower-case.
+
+[General]
+ Version = 1.0 # version number for the configuration file. (required)
+
+
+#
+# One section for all interfaces supported by mipagent.
+# The section name must be [Advertisements <intf-name>]
+#
+# HomeAgent yes, no (Determines whether mipagent will provide
+# Home Agent functionality)
+# ForeignAgent yes, no (Determines whether mipagent will provide
+# Foreign Agent functionality)
+# PrefixFlags yes, no (Specifies whether advertisements will include
+# the prefix extension).
+# AdvertiseOnBcast yes, no (If yes, advertisements are sent on
+# 255.255.255.255, rather than 224.0.0.1)
+# RegLifetime n (maximum lifetime value accepted in registration
+# requests).
+# AdvLifetime n (Lifetime advertised in the RFC1256 portion)
+# AdvFrequency n (The frequency of mobility advertisements, in
+# seconds)
+# ReverseTunnel yes, no (Determines whether mipagent has reverse tunnel
+# decapsulation/encapsulation capability. In
+# case of foreign-agent it also means that the
+# foreign agent is advertising reverse tunnel)
+#
+# ReverseTunnelRequired yes, no (Determines local policy of the mipagent
+# on registration request, i.e whether a mobile
+# should/must request reverse tunnel)
+
+
+
+[Advertisements hme0]
+ HomeAgent = no
+ ForeignAgent = yes
+ PrefixFlags = yes
+ AdvertiseOnBcast = yes
+ RegLifetime = 200
+ AdvLifetime = 200
+ AdvFrequency = 5
+ ReverseTunnel = yes
+ ReverseTunnelRequired = no
+
+# Advertisement section for dynamic interfaces:
+# The interface with '*' suffix determines dynamic interfaces to mipagent.
+# Additional parameters which may control Advertisement frequency in a newly
+# created mobility interface are useful when a mobility
+# interface does not want to have a periodic advertisement all the time.
+# The following configuration is useful for foreign agents.
+# AdvInitCount n Initial Advertisement count when
+# Unsolicited advertisements are limited.
+# AdvLimitUnsolicited yes, no Determines the local policy of the mipagent
+# (foreign agent) if it sends limited or
+# unlimited unsolicited advertisement.
+# Uncomment the following section, if your foreign agent allows mobility
+# service through newly created Solaris PPP interfaces. For more information
+# on dynamic interface support, please check mipagent.conf(1M).
+
+#[Advertisements sppp*]
+# HomeAgent = no
+# ForeignAgent = yes
+# AdvertiseOnBcast = no
+# PrefixFlags = yes
+# RegLifetime = 300
+# AdvLifetime = 300
+# AdvFrequency = 2
+# ReverseTunnel = yes
+# ReverseTunnelRequired = no
+# AdvInitCount = 5
+# AdvLimitUnsolicited = yes
+
+#
+# The GlobalSecurityParameters contains all security related configuration
+# parameters.
+#
+# MaxClockSkew n (The number of seconds that mipagent will
+# accept as a difference between its own local
+# time and the time found in Registration Requets)
+# HA-FAAuth yes, no (Specifies whether HA-FA Authentication
+# extensions must be present in Registration
+# Requests and Replies)
+# MN-FAAuth yes, no (Specifies whether MN-FA Authentication
+# extensions must be present in Registration
+# Requests and Replies)
+# Challenge yes, no (Specifies whether the Foreign Agent will
+# include Challenges in it's mobility
+# advertisements)
+# KeyDistribution files (must be set to files)
+
+[GlobalSecurityParameters]
+ MaxClockSkew = 300
+ HA-FAauth = yes
+ MN-FAauth = yes
+ Challenge = no
+ KeyDistribution = files
+
+#
+# The SPIs must be configured. An SPI entry contains a numerical value
+# the replay method and keying information.
+#
+# ReplyMethod none, timestamp (Specifies the type of replay
+# authentication for the SPI)
+# Key x (Authentication key in hexadecimal)
+
+[SPI 257]
+ ReplayMethod = none
+ Key= 11111111111111111111111111111111
+
+[SPI 258]
+ ReplayMethod = none
+ Key= 15111111111111111111111111111111
+
+#
+# The Address section contains configuration information for mobility
+# nodes (foreign and home agents) as well as mobile nodes.
+#
+# The # Node-Default keyword in the section header is used to define a
+# default SPI for all mobile nodes. This allows an administrator to
+# simply include a single entry for all mobile nodes, assuming that
+# they all use the same SPI. The Default-Node entry must include the
+# pool entry.
+#
+# The Address section may also contain an NAI as opposed to
+# the home address. These entries must also include the Pool
+# entry.
+#
+# Type node, agent (Specifies whether the entry is for
+# a mobile node, or a mobility agent)
+# SPI n (The SPI value associated with the
+# entry, which must be configured above)
+# Pool n (If the section header contained an NAI,
+# an address will be allocated for the
+# mobile node from the pool defined)
+
+[Address 10.1.1.1]
+ Type = node
+ SPI = 258
+
+[Address 10.68.30.36]
+ Type = agent
+ SPI = 257
diff --git a/usr/src/cmd/cmd-inet/etc/mipagent.conf.ha-sample b/usr/src/cmd/cmd-inet/etc/mipagent.conf.ha-sample
new file mode 100644
index 0000000000..5863ae0cfc
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/etc/mipagent.conf.ha-sample
@@ -0,0 +1,164 @@
+#
+#pragma ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Sample configuration file for mobility agents. Lines starting with the hash
+# character are treated as comments. Blank lines are ignored. All the time
+# values are in seconds unless stated otherwise. For the variable names that
+# are composed of multiple words, the practice is each word should start with
+# upper-case letter, and each value should start with lower-case.
+
+[General]
+ Version = 1.0 # version number for the configuration file. (required)
+
+
+#
+# One section for all interfaces supported by mipagent.
+# The section name must be [Advertisements <intf-name>]
+#
+# HomeAgent yes, no (Determines whether mipagent will provide
+# Home Agent functionality)
+# ForeignAgent yes, no (Determines whether mipagent will provide
+# Foreign Agent functionality)
+# PrefixFlags yes, no (Specifies whether advertisements will include
+# the prefix extension).
+# AdvertiseOnBcast yes, no (If yes, advertisements are sent on
+# 255.255.255.255, rather than 224.0.0.1)
+# RegLifetime n (maximum lifetime value accepted in registration
+# requests).
+# AdvLifetime n (Lifetime advertised in the RFC1256 portion)
+# AdvFrequency n (The frequency of mobility advertisements, in
+# seconds)
+# ReverseTunnel yes, no (Determines whether mipagent has reverse tunnel
+# decapsulation/encapsulation capability. In
+# case of foreign-agent it also means that the
+# foreign agent is advertising reverse tunnel)
+#
+# ReverseTunnelRequired yes, no (Determines local policy of the mipagent
+# on registration request, i.e whether a mobile
+# should/must request reverse tunnel)
+
+
+[Advertisements hme0]
+ HomeAgent = yes
+ ForeignAgent = no
+ PrefixFlags = yes
+ AdvertiseOnBcast = yes
+ RegLifetime = 200
+ AdvLifetime = 200
+ AdvFrequency = 5
+ ReverseTunnel = yes
+ ReverseTunnelRequired = no
+
+#
+# The GlobalSecurityParameters contains all security related configuration
+# parameters.
+#
+# MaxClockSkew n (The number of seconds that mipagent will
+# accept as a difference between its own local
+# time and the time found in Registration Requets)
+# HA-FAAuth yes, no (Specifies whether HA-FA Authentication
+# extensions must be present in Registration
+# Requests and Replies)
+# MN-FAAuth yes, no (Specifies whether MN-FA Authentication
+# extensions must be present in Registration
+# Requests and Replies)
+# Challenge yes, no (Specifies whether the Foreign Agent will
+# include Challenges in it's mobility
+# advertisements)
+# KeyDistribution files (must be set to files)
+
+[GlobalSecurityParameters]
+ MaxClockSkew = 300
+ HA-FAauth = yes
+ MN-FAauth = yes
+ Challenge = no
+ KeyDistribution = files
+
+#
+# The Address Pools are defined via numerical identifiers, and contain
+
+# BaseAddress n.n.n.n (The first address in the address pool)
+# Size n (The number of addresses in the pool)
+
+[Pool 1]
+ BaseAddress = 10.68.30.7
+ Size = 4
+
+#
+# The SPIs must be configured. An SPI entry contains a numerical value
+# the replay method and keying information.
+#
+# ReplyMethod none, timestamp (Specifies the type of replay
+# authentication for the SPI)
+# Key x (Authentication key in hexadecimal)
+
+[SPI 257]
+ ReplayMethod = none
+ Key= 11111111111111111111111111111111
+
+[SPI 258]
+ ReplayMethod = none
+ Key= 15111111111111111111111111111111
+
+#
+# The Address section contains configuration information for mobility
+# nodes (foreign and home agents) as well as mobile nodes.
+#
+# The # Node-Default keyword in the section header is used to define a
+# default SPI for all mobile nodes. This allows an administrator to
+# simply include a single entry for all mobile nodes, assuming that
+# they all use the same SPI. The Default-Node entry must include the
+# pool entry.
+#
+# The Address section may also contain an NAI as opposed to
+# the home address. These entries must also include the Pool
+# entry.
+#
+# Type node, agent (Specifies whether the entry is for
+# a mobile node, or a mobility agent)
+# SPI n (The SPI value associated with the
+# entry, which must be configured above)
+# Pool n (If the section header contained an NAI,
+# an address will be allocated for the
+# mobile node from the pool defined)
+
+[Address 10.1.1.1]
+ Type = node
+ SPI = 258
+
+[Address mobilenode@sun.com]
+ Type = node
+ SPI = 257
+ Pool = 1
+
+[Address Node-Default]
+ Type = node
+ SPI = 258
+ Pool = 1
+
+[Address 10.68.30.36]
+ Type = agent
+ SPI = 257
diff --git a/usr/src/cmd/cmd-inet/etc/nca/Makefile b/usr/src/cmd/cmd-inet/etc/nca/Makefile
new file mode 100644
index 0000000000..bbdac868f5
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/etc/nca/Makefile
@@ -0,0 +1,57 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 1999-2002 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# cmd/cmd-inet/etc/nca/Makefile
+#
+
+NCADIR= nca
+ETCPROG= nca.if ncakmod.conf ncalogd.conf ncaport.conf
+
+include ../../../Makefile.cmd
+
+ETCNCADIR= $(ROOTETC)/$(NCADIR)
+ETCNCAPROG= $(ETCPROG:%=$(ETCNCADIR)/%)
+
+FILEMODE= 0644
+OWNER= root
+GROUP= sys
+
+.KEEP_STATE:
+
+all: $(ETCPROG)
+
+install: all $(ETCNCADIR) $(ETCNCAPROG)
+
+$(ETCNCADIR)/% : %
+ $(INS.file)
+
+$(ETCNCADIR):
+ $(INS.dir)
+
+FRC:
+
+clean clobber lint:
diff --git a/usr/src/cmd/cmd-inet/etc/nca/nca.if b/usr/src/cmd/cmd-inet/etc/nca/nca.if
new file mode 100644
index 0000000000..3866e3fc93
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/etc/nca/nca.if
@@ -0,0 +1,29 @@
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# NCA feature interface configuration file
+#
+# Use this file to tell the NCA feature which interfaces to listen on
diff --git a/usr/src/cmd/cmd-inet/etc/nca/ncakmod.conf b/usr/src/cmd/cmd-inet/etc/nca/ncakmod.conf
new file mode 100644
index 0000000000..66387cc724
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/etc/nca/ncakmod.conf
@@ -0,0 +1,33 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright (c) 1998-2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# NCA Kernel Module Configuration File
+#
+
+status=disabled
+httpd_door_path=/var/run/nca_httpd_1.door
+nca_active=disabled
diff --git a/usr/src/cmd/cmd-inet/etc/nca/ncalogd.conf b/usr/src/cmd/cmd-inet/etc/nca/ncalogd.conf
new file mode 100644
index 0000000000..ce2ccf9e2b
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/etc/nca/ncalogd.conf
@@ -0,0 +1,33 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright (c) 1998-1999 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# NCA Logging Configuration File
+#
+
+status=disabled
+logd_path_name="/var/nca/log"
+logd_file_size=1000000
diff --git a/usr/src/cmd/cmd-inet/etc/nca/ncaport.conf b/usr/src/cmd/cmd-inet/etc/nca/ncaport.conf
new file mode 100644
index 0000000000..f2c7fd229b
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/etc/nca/ncaport.conf
@@ -0,0 +1,38 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# NCA Kernel Module Port Configuration File
+#
+# Use this file to tell NCA socket utility library /usr/lib/ncad_addr.so to
+# convert an AF_INET socket to an AF_NCA socket. Each line consists of two
+# fields - a key and a value - in the format ncaport=ipaddress/port. See
+# man page ncaport(4) for the format and contents of this file.
+#
+# Example configuration file:
+#
+# ncaport=*/80
+# ncaport=192.168.84.71/8888
diff --git a/usr/src/cmd/cmd-inet/etc/netmasks b/usr/src/cmd/cmd-inet/etc/netmasks
new file mode 100644
index 0000000000..a8443c387c
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/etc/netmasks
@@ -0,0 +1,40 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2000 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+
+#
+# The netmasks file associates Internet Protocol (IP) address
+# masks with IP network numbers.
+#
+# network-number netmask
+#
+# The term network-number refers to a number obtained from the Internet Network
+# Information Center.
+#
+# Both the network-number and the netmasks are specified in
+# "decimal dot" notation, e.g:
+#
+# 128.32.0.0 255.255.255.0
+#
diff --git a/usr/src/cmd/cmd-inet/etc/networks b/usr/src/cmd/cmd-inet/etc/networks
new file mode 100644
index 0000000000..44b6e2eecd
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/etc/networks
@@ -0,0 +1,40 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 1992 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.1 */
+#
+# The networks file associates Internet Protocol (IP) network numbers
+# with network names. The format of this file is:
+#
+# network-name network-number nicnames . . .
+#
+
+#
+# The loopback network is used only for intra-machine communication
+#
+loopback 127
+
+#
+# Internet networks
+#
+arpanet 10 arpa # Historical
diff --git a/usr/src/cmd/cmd-inet/etc/ppp/Makefile b/usr/src/cmd/cmd-inet/etc/ppp/Makefile
new file mode 100644
index 0000000000..18bd5ada7b
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/etc/ppp/Makefile
@@ -0,0 +1,75 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 2000 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+# cmd/cmd-inet/etc/ppp/Makefile
+#
+
+PPPDIR= ppp
+PEERSDIR= peers
+ETCPROGROOT= pap-secrets chap-secrets
+ETCPROG= options.tmpl options.ttya.tmpl myisp-chat.tmpl
+ETCPEERS= myisp.tmpl
+
+include ../../../Makefile.cmd
+
+ETCPPPDIR= $(ROOTETC)/$(PPPDIR)
+ETCPEERSDIR= $(ETCPPPDIR)/$(PEERSDIR)
+ETCPPPPROG= $(ETCPROG:%=$(ETCPPPDIR)/%) $(ETCPEERS:%=$(ETCPEERSDIR)/%)
+ETCPPPPROGROOT= $(ETCPROGROOT:%=$(ETCPPPDIR)/%)
+
+OWNER= root
+GROUP= sys
+
+# This is here to allow the make command to override this value;
+# setting it to 0644 simplifies packaging.
+SECRETSMODE= 0600
+
+$(ETCPPPPROG) := FILEMODE = 0644
+$(ETCPPPPROGROOT) := FILEMODE = $(SECRETSMODE)
+
+.KEEP_STATE:
+
+all:
+
+install: all $(ETCPPPDIR) $(ETCPEERSDIR) \
+ $(ETCPPPPROG) $(ETCPPPPROGROOT)
+
+$(ETCPPPDIR)/% : %
+ $(INS.file)
+
+$(ETCPPPDIR):
+ $(INS.dir)
+
+$(ETCPEERSDIR)/% : %
+ $(INS.file)
+
+$(ETCPEERSDIR):
+ $(INS.dir)
+
+FRC:
+
+clean clobber lint:
diff --git a/usr/src/cmd/cmd-inet/etc/ppp/chap-secrets b/usr/src/cmd/cmd-inet/etc/ppp/chap-secrets
new file mode 100644
index 0000000000..6cfa0239cd
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/etc/ppp/chap-secrets
@@ -0,0 +1,56 @@
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Secrets for authentication using CHAP (Challenge Handshake Authentication
+# Protocol) are placed here. Each line is a separate entry and consists of
+# a list of space or tab separated tokens.
+#
+# client server secret [IP addresses ["--" options]]
+#
+# When authenticating to a peer (so-called "client mode;" as when dialing
+# out to an ISP), the "client" will be matched using the local name and
+# "server" will use the remote peer's name. CHAP does specify an
+# authenticator name, but some peers (such as Windows NT) do not provide
+# a peer name, and the "remotename <name>" option should then be used.
+# Typically, the "user <name>" option is also to specify the local name.
+#
+# When authenticating a peer (so-called "server mode;" as when allowing
+# dial-up access to this system), the remote peer's name is the "client"
+# and the local system name is the "server." In this case, the privileged
+# "name <name>" option is sometimes used to set the local name. The "user
+# <name>" option cannot be used. The remote peer's name comes from the
+# CHAP messages the peer sends.
+#
+# After the secret, which must always be clear text for CHAP, a list of
+# valid IP addresses for the peer appears. This must be present when
+# acting as a server. Usually, this is specified as "*" and actual IP
+# addresses are given in the options. If a given dial-in peer has an
+# allocated IP address ("static IP addressing"), then this address may
+# be given here. If there's exactly one address, then this will be sent
+# to the peer as a hint.
+#
+# The entry may also have extra options after a -- token. These are
+# interpreted as privileged pppd options, and may be used to enable
+# proxyarp or other optional features.
diff --git a/usr/src/cmd/cmd-inet/etc/ppp/myisp-chat.tmpl b/usr/src/cmd/cmd-inet/etc/ppp/myisp-chat.tmpl
new file mode 100644
index 0000000000..ea0261f9cc
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/etc/ppp/myisp-chat.tmpl
@@ -0,0 +1,41 @@
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# This is an example chat script for dialing into a typical ISP. See
+# peers/myisp.tmpl for more information.
+#
+# The CONNECT string from the modem will be printed to the user's
+# terminal.
+#
+ABORT BUSY
+ABORT 'NO CARRIER'
+REPORT CONNECT
+TIMEOUT 10
+"" "AT&F1"
+OK "AT&C1&D2"
+SAY "Calling myisp\n"
+TIMEOUT 60
+OK "ATDT1-123-555-1212"
+CONNECT \c
diff --git a/usr/src/cmd/cmd-inet/etc/ppp/myisp.tmpl b/usr/src/cmd/cmd-inet/etc/ppp/myisp.tmpl
new file mode 100644
index 0000000000..95f3b1284e
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/etc/ppp/myisp.tmpl
@@ -0,0 +1,51 @@
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# This is an example configuration for dialing into a typical ISP from a
+# single node. To use this example, uncomment the last line of the
+# pap-secrets file and rename the template files:
+#
+# mv /etc/ppp/options.tmpl /etc/ppp/options
+# mv /etc/ppp/options.ttya.tmpl /etc/ppp/options.ttya
+# mv /etc/ppp/myisp-chat.tmpl /etc/ppp/myisp-chat
+# mv /etc/ppp/peers/myisp.tmpl /etc/ppp/peers/myisp
+#
+# and invoke with:
+#
+# pppd ttya call myisp
+#
+# Options in this file, /etc/ppp/options, /etc/ppp//options.<tty>,
+# /etc/ppp/pap-secrets, and /etc/ppp/chap-secrets are all considered
+# privileged. Those from ~/.ppprc and the command line are privileged
+# if the invoker is root, and unprivileged otherwise.
+#
+connect "/usr/bin/chat -f /etc/ppp/myisp-chat" # dial into ISP
+user myname # my account name at my ISP
+remotename myisp # name of the ISP; for pap-secrets
+noauth # do not authenticate the ISP's identity (client)
+noipdefault # assume no IP address; get it from ISP
+defaultroute # install default route; ISP is Internet gateway
+updetach # log errors and CONNECT string to invoker
+noccp # ISP doesn't support free compression
diff --git a/usr/src/cmd/cmd-inet/etc/ppp/options.tmpl b/usr/src/cmd/cmd-inet/etc/ppp/options.tmpl
new file mode 100644
index 0000000000..a76f822117
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/etc/ppp/options.tmpl
@@ -0,0 +1,54 @@
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Privileged system-wide pppd options may be placed here. Typically, the
+# following options are used in this file:
+#
+# lock -- enable UUCP-style device locking
+# name <name> -- set local system name for authentication
+# domain <name> -- append domain name to local name
+# nodefaultroute -- prevent users from installing a default route
+# noproxyarp -- prevent users from using proxy ARP
+#
+# Device-specific options, such as asyncmap, should go in the per-device
+# option files named /etc/ppp/options.<tty>, where <tty> is the name of
+# the device. For example, if /dev/ttya is used, then /etc/ppp/options.ttya
+# will be read (if it exists). When IP addresses are allocated per-port
+# for dial-in nodes ("dynamic IP addressing"), then the remote address
+# should be placed in that file as well.
+#
+# Peer-specific options, such as connect scripts, IP addresses, and other
+# protocol options, should be placed in /etc/ppp/peers/<name>, where <name>
+# is the name of the peer. This file is then read by using the pppd "call"
+# option; usually from the command line.
+#
+# Options in this file, /etc/ppp/options.<tty>, /etc/ppp/peers/<name>,
+# /etc/ppp/pap-secrets, and /etc/ppp/chap-secrets are all considered
+# privileged. Those from ~/.ppprc and the command line are privileged
+# if the invoker is root, and unprivileged otherwise.
+#
+lock
+nodefaultroute
+noproxyarp
diff --git a/usr/src/cmd/cmd-inet/etc/ppp/options.ttya.tmpl b/usr/src/cmd/cmd-inet/etc/ppp/options.ttya.tmpl
new file mode 100644
index 0000000000..74e759f336
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/etc/ppp/options.ttya.tmpl
@@ -0,0 +1,38 @@
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Privileged pppd options for a given physical device may be placed here.
+# Typically, asyncmap, crtscts, baud rate, and other hardware-related
+# options are placed here, along with remote IP addresses for dial-in
+# nodes when using "dynamic IP addressing."
+#
+# Options in this file, /etc/ppp/options, /etc/ppp/peers/<name>,
+# /etc/ppp/pap-secrets, and /etc/ppp/chap-secrets are all considered
+# privileged. Those from ~/.ppprc and the command line are privileged
+# if the invoker is root, and unprivileged otherwise.
+#
+38400 # default baud rate for this port
+asyncmap 0xa0000 # work around broken peers
+:192.168.1.1 # allow dial-up users to get this address
diff --git a/usr/src/cmd/cmd-inet/etc/ppp/pap-secrets b/usr/src/cmd/cmd-inet/etc/ppp/pap-secrets
new file mode 100644
index 0000000000..655ffee63b
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/etc/ppp/pap-secrets
@@ -0,0 +1,58 @@
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Passwords for authentication using PAP (Password Authentication Protocol)
+# are placed here. Each line is a separate entry and consists of a list of
+# space or tab separated tokens.
+#
+# client server password [IP addresses ["--" options]]
+#
+# When authenticating to a peer (so-called "client mode;" as when dialing
+# out to an ISP), the "client" will be matched using the local name and
+# "server" will use the remote peer's name. PAP does not specify an
+# authenticator name, so the "remotename <name>" option should be used.
+# Typically, the "user <name>" option is also to specify the local name.
+#
+# When authenticating a peer (so-called "server mode;" as when allowing
+# dial-up access to this system), the remote peer's name is the "client"
+# and the local system name is the "server." In this case, the privileged
+# "name <name>" option is sometimes used to set the local name. The "user
+# <name>" option cannot be used. The remote peer's name comes from the PAP
+# messages the peer sends.
+#
+# After the password, which may be a crypt(3c) encoded password when acting
+# as a server, a list of valid IP addresses for the peer appears. This
+# must be present when acting as a server. Usually, this is specified as
+# "*" and actual IP addresses are given in the options. If a given dial-in
+# peer has an allocated IP address ("static IP addressing"), then this
+# address may be given here. If there's exactly one address, then this will
+# be sent to the peer as a hint.
+#
+# The entry may also have extra options after a -- token. These are
+# interpreted as privileged pppd options, and may be used to enable
+# proxyarp or other optional features.
+#
+# This is provided for the "myisp" example; see peers/myisp.tmpl.
+# myname myisp mypassword
diff --git a/usr/src/cmd/cmd-inet/etc/protocols b/usr/src/cmd/cmd-inet/etc/protocols
new file mode 100644
index 0000000000..9897f1fbff
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/etc/protocols
@@ -0,0 +1,68 @@
+#ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.1 */
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Internet (IP) protocols
+#
+ip 0 IP # internet protocol, pseudo protocol number
+icmp 1 ICMP # internet control message protocol
+igmp 2 IGMP # Internet Group Management
+ggp 3 GGP # gateway-gateway protocol
+ipip 4 IP-IP # IP in IP (encapsulation)
+tcp 6 TCP # transmission control protocol
+cbt 7 CBT # Core Based Trees
+egp 8 EGP # exterior gateway protocol
+igp 9 IGP # any private interior gateway
+pup 12 PUP # PARC universal packet protocol
+udp 17 UDP # user datagram protocol
+mux 18 MUX # Multiplexing
+hmp 20 HMP # host monitoring protocol
+xns-idp 22 XNS-IDP # Xerox NS IDP
+rdp 27 RDP # "reliable datagram" protocol
+idpr 35 IDPR # Inter-Domain Policy Routing Protocol
+idpr-cmtp 38 IDPR-CMTP # IDPR Control Message Transport Protocol
+sdrp 42 SDRP # Source Demand Routing Protocol
+idrp 45 IDRP # Inter-Domain Routing Protocol
+rsvp 46 RSVP # Resource Reservation Protocol
+gre 47 GRE # Generic Routing Encapsulation
+esp 50 ESP # Encapsulating Security Payload
+ah 51 AH # Authentication Header
+mobile 55 MOBILE # IP Mobility
+ospf 89 OSPFIGP # Open Shortest Path First
+pim 103 PIM # Protocol Independent Multicast
+ipcomp 108 IPComp # IP Payload Compression Protocol
+vrrp 112 VRRP # Virtual Router Redundancy Protocol
+sctp 132 SCTP # Stream Control Transmission Protocol
+
+#
+# Internet (IPv6) extension headers
+#
+hopopt 0 HOPOPT # Hop-by-hop options for IPv6
+ipv6 41 IPv6 # IPv6 in IP encapsulation
+ipv6-route 43 IPv6-Route # Routing header for IPv6
+ipv6-frag 44 IPv6-Frag # Fragment header for IPv6
+ipv6-icmp 58 IPv6-ICMP # IPv6 internet control message protocol
+ipv6-nonxt 59 IPv6-NoNxt # No next header extension header for IPv6
+ipv6-opts 60 IPv6-Opts # Destination Options for IPv6
diff --git a/usr/src/cmd/cmd-inet/etc/secret/Makefile b/usr/src/cmd/cmd-inet/etc/secret/Makefile
new file mode 100644
index 0000000000..0ef73a4fd5
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/etc/secret/Makefile
@@ -0,0 +1,67 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+# cmd/cmd-inet/etc/secret/Makefile
+#
+
+SECRETDIR= secret
+PRIVATEKEYDIR= ike.privatekeys
+ETCPROG= ike.preshared ipseckeys.sample
+
+include ../../../Makefile.cmd
+
+ETCINETSECRETDIR= $(ROOTETC)/inet/$(SECRETDIR)
+ETCINETPRIVATEKEYDIR= $(ROOTETC)/inet/$(SECRETDIR)/$(PRIVATEKEYDIR)
+ETCINETSECRETPROG= $(ETCPROG:%=$(ETCINETSECRETDIR)/%)
+
+# Be extra paranoid about /etc/inet/secret
+$(ETCINETSECRETDIR):= DIRMODE= 700
+$(ETCINETPRIVATEKEYDIR):= DIRMODE= 700
+
+DIRMODE= 700
+FILEMODE= 600
+OWNER= root
+GROUP= sys
+
+.KEEP_STATE:
+
+all: $(ETCPROG)
+
+install: all $(ETCINETSECRETDIR) $(ETCINETPRIVATEKEYDIR) $(ETCINETSECRETPROG)
+
+$(ETCINETSECRETDIR)/% : %
+ $(INS.file)
+
+$(ETCINETSECRETDIR):
+ $(INS.dir)
+
+$(ETCINETPRIVATEKEYDIR):
+ $(INS.dir)
+
+FRC:
+
+clean clobber lint:
diff --git a/usr/src/cmd/cmd-inet/etc/secret/ike.preshared b/usr/src/cmd/cmd-inet/etc/secret/ike.preshared
new file mode 100644
index 0000000000..49160535c7
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/etc/secret/ike.preshared
@@ -0,0 +1,37 @@
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+# ike.preshared - Pre-shared secrets for IKE authentication.
+#
+# Entries are of the form:
+#
+# {
+# <attribute> <value>
+# ...
+# }
+#
+# Consult the man page for ike.preshared(4) for details.
diff --git a/usr/src/cmd/cmd-inet/etc/secret/ipseckeys.sample b/usr/src/cmd/cmd-inet/etc/secret/ipseckeys.sample
new file mode 100644
index 0000000000..256328e7f6
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/etc/secret/ipseckeys.sample
@@ -0,0 +1,35 @@
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+# ipseckeys - This file takes the file format documented in ipseckey(1m).
+# Note that naming services might not be available when this file
+# loads, just like ipsecinit.conf.
+#
+# This file should be copied into /etc/inet/secret/ipseckeys to load the
+# IPsec Security Association Database (SADB). A side-effect of this is that
+# IPsec kernel modules will load.
+
diff --git a/usr/src/cmd/cmd-inet/etc/services b/usr/src/cmd/cmd-inet/etc/services
new file mode 100644
index 0000000000..3c864f2f66
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/etc/services
@@ -0,0 +1,139 @@
+#ident "%Z%%M% %I% %E% SMI"
+#
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Network services, Internet style
+#
+tcpmux 1/tcp
+echo 7/tcp
+echo 7/udp
+discard 9/tcp sink null
+discard 9/udp sink null
+systat 11/tcp users
+daytime 13/tcp
+daytime 13/udp
+netstat 15/tcp
+chargen 19/tcp ttytst source
+chargen 19/udp ttytst source
+ftp-data 20/tcp
+ftp 21/tcp
+ssh 22/tcp # Secure Shell
+telnet 23/tcp
+smtp 25/tcp mail
+time 37/tcp timserver
+time 37/udp timserver
+name 42/udp nameserver
+whois 43/tcp nicname # usually to sri-nic
+domain 53/udp
+domain 53/tcp
+bootps 67/udp # BOOTP/DHCP server
+bootpc 68/udp # BOOTP/DHCP client
+kerberos 88/udp kdc # Kerberos V5 KDC
+kerberos 88/tcp kdc # Kerberos V5 KDC
+hostnames 101/tcp hostname # usually to sri-nic
+pop2 109/tcp pop-2 # Post Office Protocol - V2
+pop3 110/tcp # Post Office Protocol - Version 3
+sunrpc 111/udp rpcbind
+sunrpc 111/tcp rpcbind
+imap 143/tcp imap2 # Internet Mail Access Protocol v2
+ldap 389/tcp # Lightweight Directory Access Protocol
+ldap 389/udp # Lightweight Directory Access Protocol
+submission 587/tcp # Mail Message Submission
+submission 587/udp # see RFC 2476
+ldaps 636/tcp # LDAP protocol over TLS/SSL (was sldap)
+ldaps 636/udp # LDAP protocol over TLS/SSL (was sldap)
+#
+# Host specific functions
+#
+tftp 69/udp
+rje 77/tcp
+finger 79/tcp
+link 87/tcp ttylink
+supdup 95/tcp
+iso-tsap 102/tcp
+x400 103/tcp # ISO Mail
+x400-snd 104/tcp
+csnet-ns 105/tcp
+pop-2 109/tcp # Post Office
+uucp-path 117/tcp
+nntp 119/tcp usenet # Network News Transfer
+ntp 123/tcp # Network Time Protocol
+ntp 123/udp # Network Time Protocol
+netbios-ns 137/tcp # NETBIOS Name Service
+netbios-ns 137/udp # NETBIOS Name Service
+netbios-dgm 138/tcp # NETBIOS Datagram Service
+netbios-dgm 138/udp # NETBIOS Datagram Service
+netbios-ssn 139/tcp # NETBIOS Session Service
+netbios-ssn 139/udp # NETBIOS Session Service
+NeWS 144/tcp news # Window System
+slp 427/tcp slp # Service Location Protocol, V2
+slp 427/udp slp # Service Location Protocol, V2
+mobile-ip 434/udp mobile-ip # Mobile-IP
+cvc_hostd 442/tcp # Network Console
+ike 500/udp ike # Internet Key Exchange
+uuidgen 697/tcp # UUID Generator
+uuidgen 697/udp # UUID Generator
+#
+# UNIX specific services
+#
+# these are NOT officially assigned
+#
+exec 512/tcp
+login 513/tcp
+shell 514/tcp cmd # no passwords used
+printer 515/tcp spooler # line printer spooler
+courier 530/tcp rpc # experimental
+uucp 540/tcp uucpd # uucp daemon
+biff 512/udp comsat
+who 513/udp whod
+syslog 514/udp
+talk 517/udp
+route 520/udp router routed
+ripng 521/udp
+klogin 543/tcp # Kerberos authenticated rlogin
+kshell 544/tcp cmd # Kerberos authenticated remote shell
+new-rwho 550/udp new-who # experimental
+rmonitor 560/udp rmonitord # experimental
+monitor 561/udp # experimental
+pcserver 600/tcp # ECD Integrated PC board srvr
+sun-dr 665/tcp # Remote Dynamic Reconfiguration
+kerberos-adm 749/tcp # Kerberos V5 Administration
+kerberos-adm 749/udp # Kerberos V5 Administration
+kerberos-iv 750/udp # Kerberos V4 key server
+krb5_prop 754/tcp # Kerberos V5 KDC propogation
+ufsd 1008/tcp ufsd # UFS-aware server
+ufsd 1008/udp ufsd
+cvc 1495/tcp # Network Console
+ingreslock 1524/tcp
+www-ldap-gw 1760/tcp # HTTP to LDAP gateway
+www-ldap-gw 1760/udp # HTTP to LDAP gateway
+listen 2766/tcp # System V listener port
+nfsd 2049/udp nfs # NFS server daemon (clts)
+nfsd 2049/tcp nfs # NFS server daemon (cots)
+eklogin 2105/tcp # Kerberos encrypted rlogin
+lockd 4045/udp # NFS lock daemon/manager
+lockd 4045/tcp
+dtspc 6112/tcp # CDE subprocess control
+fs 7100/tcp # Font server
diff --git a/usr/src/cmd/cmd-inet/etc/sock2path b/usr/src/cmd/cmd-inet/etc/sock2path
new file mode 100644
index 0000000000..425d6c8006
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/etc/sock2path
@@ -0,0 +1,58 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# socket configuration information
+#
+# Family Type Protocol Path
+ 2 2 0 /dev/tcp
+ 2 2 6 /dev/tcp
+
+ 26 2 0 /dev/tcp6
+ 26 2 6 /dev/tcp6
+
+ 2 1 0 /dev/udp
+ 2 1 17 /dev/udp
+
+ 26 1 0 /dev/udp6
+ 26 1 17 /dev/udp6
+
+ 1 2 0 /dev/ticotsord
+ 1 6 0 /dev/ticotsord
+ 1 1 0 /dev/ticlts
+
+ 2 4 0 /dev/rawip
+ 26 4 0 /dev/rawip6
+
+ 2 2 132 /dev/sctp
+ 26 2 132 /dev/sctp6
+ 2 6 132 /dev/sctp
+ 26 6 132 /dev/sctp6
+
+ 24 4 0 /dev/rts
+
+ 27 4 2 /dev/keysock
+ 28 2 0 /dev/nca
+ 29 4 1 /dev/spdsock
+
diff --git a/usr/src/cmd/cmd-inet/etc/wanboot.conf.sample b/usr/src/cmd/cmd-inet/etc/wanboot.conf.sample
new file mode 100644
index 0000000000..8fc0ee5d47
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/etc/wanboot.conf.sample
@@ -0,0 +1,104 @@
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# ident "%Z%%M% %I% %E% SMI"
+
+####################################################################
+# wanboot.conf(4): boot configuration file.
+#
+# Please consult wanboot.conf(4) for further information. Note that
+# this interface is "Evolving" as defined by attributes(5).
+#
+# Anything after a '#' is comment. Values may be quoted (e.g. "val").
+#
+# <empty> means there is no value, i.e. null. The absence of any
+# parameter implies that it takes a default value (<empty> unless
+# otherwise specified).
+#
+# <url> is of the form http://... or https://...
+####################################################################
+
+# The path of the bootstrap file (within htdocs) which is served up
+# by wanboot-cgi(bootfile).
+#
+boot_file=/bootfiles/wanboot # <absolute pathname>
+
+# These are used by wanboot-cgi(bootfile|bootfs|rootfs) to determine
+# whether boot_file or the bootfs is to be sent encrypted/signed, or
+# root_file is to be sent signed; the client must be setup with the
+# corresponding encryption/signature key(s) (which cannot be auto-
+# matically verified).
+#
+# If an encryption_type is specified then a signature_type must also
+# be specified.
+#
+encryption_type=3des # 3des | aes | <empty>
+signature_type=sha1 # sha1 | <empty>
+
+# This is used by wanboot-cgi(bootfs) and wanboot to determine whether
+# server authentication should be requested during SSL connection
+# setup.
+#
+server_authentication=yes # yes | no
+
+# This is used by wanboot-cgi(bootfs) and wanboot to determine whether
+# client authentication should be requested during SSL connection
+# setup. If client_authentication is "yes", then server_authentication
+# must also be "yes".
+#
+client_authentication=yes # yes | no
+
+# wanboot-cgi(bootfs) will construct a hosts file which resolves any
+# hostnames specified in any of the URLs in the wanboot.conf file,
+# plus those found in certificates, etc. The following parameter
+# may be used to add additional mappings to the hosts file.
+#
+resolve_hosts= # <hostname>[,<hostname>*] | <empty>
+
+# This is used to specify the URL of wanboot-cgi on the server on which
+# the root_file exists, and used by wanboot to obtain the root server's
+# URL; wanboot substitutes root_file for the pathname part of the URL.
+# If the schema is http://... then the root_file will be signed if there
+# is a non-empty signature_type. If server_authentication is "yes", the
+# schema must be https://...; otherwise it must be http://...
+#
+root_server=https://host:port/cgi-bin/wanboot-cgi # <url> | <empty>
+
+# This is used by wanboot-cgi(rootfs) to locate the path of the
+# rootfs image (within htdocs) on the root_server.
+#
+root_file=/rootimages/miniroot # <absolute pathname> | <empty>
+
+# This is used by wanboot to determine the URL of the bootserver
+# (and whether bootlog traffic should be sent using http or https),
+# or whether it should simply be sent to the console.
+#
+boot_logger= # <url> | <empty>
+
+# This is used by the system startup scripts. If set, it should
+# point to a file that contains name value pairs to be used at
+# start up time. For example, this file may be used to provide
+# install the values for sysidcfg and jumpscfg.
+#
+system_conf=system.conf
diff --git a/usr/src/cmd/cmd-inet/req.flg b/usr/src/cmd/cmd-inet/req.flg
new file mode 100644
index 0000000000..36913c0d53
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/req.flg
@@ -0,0 +1,29 @@
+#!/bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright (c) 1999 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+echo_file usr/src/cmd/cmd-inet/Makefile.cmd-inet
diff --git a/usr/src/cmd/cmd-inet/sbin/Makefile b/usr/src/cmd/cmd-inet/sbin/Makefile
new file mode 100644
index 0000000000..d18e30d884
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/sbin/Makefile
@@ -0,0 +1,46 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 1999-2002 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+SUBDIRS= dhcpagent dhcpinfo ifparse netstrategy
+
+include ../../Makefile.cmd
+
+all:= TARGET= all
+install:= TARGET= install
+clean:= TARGET= clean
+clobber:= TARGET= clobber
+lint:= TARGET= lint
+
+.KEEP_STATE:
+
+all install clean clobber lint: $(SUBDIRS)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/Makefile b/usr/src/cmd/cmd-inet/sbin/dhcpagent/Makefile
new file mode 100644
index 0000000000..0403bd19a1
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/Makefile
@@ -0,0 +1,89 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+PROG = dhcpagent
+ROOTFS_PROG = $(PROG)
+LOCOBJS = adopt.o agent.o arp_check.o async.o bound.o class_id.o defaults.o \
+ dlpi_io.o dlprims.o inform.o init_reboot.o \
+ interface.o ipc_action.o packet.o release.o renew.o request.o \
+ script_handler.o select.o util.o
+COMDIR = $(SRC)/common/net/dhcp
+COMOBJS = ipv4_sum.o udp_sum.o
+INETDIR = $(SRC)/cmd/cmd-inet/common
+
+include ../../../Makefile.cmd
+
+DFLTD = $(ROOTETC)/default
+ETCDFLTPROG = $(PROG:%=$(DFLTD)/%)
+$(ETCDFLTPROG) := FILEMODE = 0444
+$(ETCDFLTPROG) := OWNER = root
+$(ETCDFLTPROG) := GROUP = sys
+
+OBJS = $(COMOBJS) $(LOCOBJS)
+SRCS = $(COMOBJS:%.o=$(COMDIR)/%.c) $(LOCOBJS:%.o=%.c)
+
+POFILES = $(LOCOBJS:%.o=%.po)
+XGETFLAGS += -a -x dhcpagent.xcl
+
+#
+# to compile a debug version, do a `make COPTFLAG="-g -XO0"'
+#
+
+CPPFLAGS += -I$(COMDIR) -I$(INETDIR)
+LDLIBS += -lsocket -lnvpair -lnsl -ldhcpagent -ldhcputil -linetutil -ldevinfo
+
+.KEEP_STATE:
+
+all: $(ROOTFS_PROG) $(PROG).dfl
+
+install: all $(ROOTSBINPROG) $(ETCDFLTPROG)
+
+$(PROG): $(OBJS)
+ $(LINK.c) -o $@ $(OBJS) $(LDLIBS)
+ $(POST_PROCESS)
+
+%.o: $(COMDIR)/%.c
+ $(COMPILE.c) $(OUTPUT_OPTION) $<
+ $(POST_PROCESS_O)
+
+%.o: $(INETDIR)/%.c
+ $(COMPILE.c) $(OUTPUT_OPTION) $<
+ $(POST_PROCESS_O)
+
+$(DFLTD)/%: %.dfl
+ $(INS.rename)
+
+$(POFILE): $(POFILES)
+ $(RM) $@; $(CAT) $(POFILES) > $@; $(RM) $(POFILES)
+
+clean:
+ $(RM) $(OBJS)
+
+lint: lint_SRCS
+
+include ../../../Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/README b/usr/src/cmd/cmd-inet/sbin/dhcpagent/README
new file mode 100644
index 0000000000..b331b57470
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/README
@@ -0,0 +1,459 @@
+CDDL HEADER START
+
+The contents of this file are subject to the terms of the
+Common Development and Distribution License, Version 1.0 only
+(the "License"). You may not use this file except in compliance
+with the License.
+
+You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+or http://www.opensolaris.org/os/licensing.
+See the License for the specific language governing permissions
+and limitations under the License.
+
+When distributing Covered Code, include this CDDL HEADER in each
+file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+If applicable, add the following below this CDDL HEADER, with the
+fields enclosed by brackets "[]" replaced with your own identifying
+information: Portions Copyright [yyyy] [name of copyright owner]
+
+CDDL HEADER END
+
+Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+Use is subject to license terms.
+
+Architectural Overview for the DHCP agent
+Peter Memishian
+ident "%Z%%M% %I% %E% SMI"
+
+INTRODUCTION
+============
+
+The Solaris DHCP agent (dhcpagent) is an RFC2131-compliant DHCP client
+implementation. The major forces shaping its design were:
+
+ * Must be capable of managing multiple network interfaces.
+ * Must consume little CPU, since it will always be running.
+ * Must have a small memory footprint, since it will always be
+ running.
+ * Must not rely on any shared libraries, since it must run
+ before all filesystems have been mounted.
+
+When a DHCP agent implementation is only required to control a single
+interface on a machine, the problem is expressed well as a simple
+state-machine, as shown in RFC2131. However, when a DHCP agent is
+responsible for managing more than one interface at a time, the
+problem becomes much more complicated, especially when threads cannot
+be used to attack the problem (since the filesystems containing the
+thread libraries may not be available when the agent starts).
+Instead, the problem must be solved using an event-driven model, which
+while tried-and-true, is subtle and easy to get wrong. Indeed, much
+of the agent's code is there to manage the complexity of programming
+in an asynchronous event-driven paradigm.
+
+THE BASICS
+==========
+
+The DHCP agent consists of roughly 20 source files, most with a
+companion header file. While the largest source file is around 700
+lines, most are much shorter. The source files can largely be broken
+up into three groups:
+
+ * Source files, which along with their companion header files,
+ define an abstract "object" that is used by other parts of
+ the system. Examples include "timer_queue.c", which along
+ with "timer_queue.h" provide a Timer Queue object for use
+ by the rest of the agent, and "async.c", which along with
+ "async.h" defines an interface for managing asynchronous
+ transactions within the agent.
+
+ * Source files which implement a given state of the agent; for
+ instance, there is a "request.c" which comprises all of
+ the procedural "work" which must be done while in the
+ REQUESTING state of the agent. By encapsulating states in
+ files, it becomes easier to debug errors in the
+ client/server protocol and adapt the agent to new
+ constraints, since all the relevant code is in one place.
+
+ * Source files, which along with their companion header files,
+ encapsulate a given task or related set of tasks. The
+ difference between this and the first group is that the
+ interfaces exported from these files do not operate on
+ an "object", but rather perform a specific task. Examples
+ include "dlpi_io.c", which provides a useful interface
+ to DLPI-related i/o operations.
+
+OVERVIEW
+========
+
+Here we discuss the essential objects and subtle aspects of the
+DHCP agent implementation. Note that there is of course much more
+that is not discussed here, but after this overview you should be able
+to fend for yourself in the source code.
+
+Event Handlers and Timer Queues
+-------------------------------
+
+The most important object in the agent is the event handler, whose
+interface is in libinetutil.h and whose implementation is in
+libinetutil. The event handler is essentially an object-oriented
+wrapper around poll(2): other components of the agent can register to
+be called back when specific events on file descriptors happen -- for
+instance, to wait for requests to arrive on its IPC socket, the agent
+registers a callback function (accept_event()) that will be called
+back whenever a new connection arrives on the file descriptor
+associated with the IPC socket. When the agent initially begins in
+main(), it registers a number of events with the event handler, and
+then calls iu_handle_events(), which proceeds to wait for events to
+happen -- this function does not return until the agent is shutdown
+via signal.
+
+When the registered events occur, the callback functions are called
+back, which in turn might lead to additional callbacks being
+registered -- this is the classic event-driven model. (As an aside,
+note that programming in an event-driven model means that callbacks
+cannot block, or else the agent will become unresponsive.)
+
+A special kind of "event" is a timeout. Since there are many timers
+which must be maintained for each DHCP-controlled interface (such as a
+lease expiration timer, time-to-first-renewal (t1) timer, and so
+forth), an object-oriented abstraction to timers called a "timer
+queue" is provided, whose interface is in libinetutil.h with a
+corresponding implementation in libinetutil. The timer queue allows
+callback functions to be "scheduled" for callback after a certain
+amount of time has passed.
+
+The event handler and timer queue objects work hand-in-hand: the event
+handler is passed a pointer to a timer queue in iu_handle_events() --
+from there, it can use the iu_earliest_timer() routine to find the
+timer which will next fire, and use this to set its timeout value in
+its call to poll(2). If poll(2) returns due to a timeout, the event
+handler calls iu_expire_timers() to expire all timers that expired
+(note that more than one may have expired if, for example, multiple
+timers were set to expire at the same time).
+
+Although it is possible to instantiate more than one timer queue or
+event handler object, it doesn't make a lot of sense -- these objects
+are really "singletons". Accordingly, the agent has two global
+variables, `eh' and `tq', which store pointers to the global event
+handler and timer queue.
+
+Network Interfaces
+------------------
+
+For each network interface managed by the agent, there is a set of
+associated state that describes both its general properties (such as
+the maximum MTU) and its DHCP-related state (such as when it acquired
+a lease). This state is stored in a a structure called an `ifslist',
+which is a poor name (since it suggests implementation artifacts but
+not purpose) but has historical precedent. Another way to think about
+an `ifslist' is that it provides all of the context necessary to
+perform DHCP on a given interface: the state the interface is in, the
+last packet DHCP packet received on that interface, and so forth. As
+one can imagine, the `ifslist' structure is quite complicated and rules
+governing accessing its fields are equally convoluted -- see the
+comments in interface.h for more information.
+
+One point that was brushed over in the preceding discussion of event
+handlers and timer queues was context. Recall that the event-driven
+nature of the agent requires that functions cannot block, lest they
+starve out others and impact the observed responsiveness of the agent.
+As an example, consider the process of extending a lease: the agent
+must send a REQUEST packet and wait for an ACK or NAK packet in
+response. This is done by sending a REQUEST and then registering a
+callback with the event handler that waits for an ACK or NAK packet to
+arrive on the file descriptor associated with the interface. Note
+however, that when the ACK or NAK does arrive, and the callback
+function called back, it must know which interface this packet is for
+(it must get back its context). This could be handled through an
+ad-hoc mapping of file descriptors to interfaces, but a cleaner
+approach is to have the event handler's register function
+(iu_register_event()) take in an opaque context pointer, which will
+then be passed back to the callback. In the agent, this context
+pointer is always the `ifslist', but for reasons of decoupling and
+generality, the timer queue and event handler objects allow a generic
+(void *) context argument.
+
+Note that there is nothing that guarantees the pointer passed into
+iu_register_event() or iu_schedule_timer() will still be valid when
+the callback is called back (for instance, the memory may have been
+freed in the meantime). To solve this problem, ifslists are reference
+counted. For more details on how the reference count scheme is
+implemented, see the closing comments in interface.h regarding memory
+management.
+
+Transactions
+------------
+
+Many operations performed via DHCP must be performed in groups -- for
+instance, acquiring a lease requires several steps: sending a
+DISCOVER, collecting OFFERs, selecting an OFFER, sending a REQUEST,
+and receiving an ACK, assuming everything goes well. Note however
+that due to the event-driven model the agent operates in, these
+operations are not inherently "grouped" -- instead, the agent sends a
+DISCOVER, goes back into the main event loop, waits for events
+(perhaps even requests on the IPC channel to begin acquiring a lease
+on another interface), eventually checks to see if an acceptable OFFER
+has come in, and so forth. To some degree, the notion of the current
+state of an interface (SELECTING, REQUESTING, etc) helps control the
+potential chaos of the event-driven model (for instance, if while the
+agent is waiting for an OFFER on a given interface, an IPC event comes
+in requesting that the interface be RELEASED, the agent knows to send
+back an error since the interface must be in at least the BOUND state
+before a RELEASE can be performed.)
+
+However, states are not enough -- for instance, suppose that the agent
+begins trying to renew a lease -- this is done by sending a REQUEST
+packet and waiting for an ACK or NAK, which might never come. If,
+while waiting for the ACK or NAK, the user sends a request to renew
+the lease as well, then if the agent were to send another REQUEST,
+things could get quite complicated (and this is only the beginning of
+this rathole). To protect against this, two objects exist:
+`async_action' and `ipc_action'. These objects are related, but
+independent of one another; the more essential object is the
+`async_action', which we will discuss first.
+
+In short, an `async_action' represents a pending transaction (aka
+asynchronous action), of which each interface can have at most one.
+The `async_action' structure is embedded in the `ifslist' structure,
+which is fine since there can be at most one pending transaction per
+interface. Typical "asynchronous transactions" are START, EXTEND, and
+INFORM, since each consists of a sequence of packets that must be done
+without interruption. Note that not all DHCP operations are
+"asynchronous" -- for instance, a RELEASE operation is synchronous
+(not asynchronous) since after the RELEASE is sent no reply is
+expected from the DHCP server. Also, note that there can be
+synchronous operations intermixed with asynchronous operations
+although it's not recommended.
+
+When the agent realizes it must perform an asynchronous transaction,
+it first calls async_pending() to see if there is already one pending;
+if so, the new transaction must fail (the details of failure depend on
+how the transaction was initiated, which is described in more detail
+later when the `ipc_action' object is discussed). If there is no
+pending asynchronous transaction, async_start() is called to begin
+one.
+
+When the transaction is complete, async_finish() must be called to
+complete the asynchronous action on that interface. If the
+transaction is unable to complete within a certain amount of time
+(more on this later), async_timeout() is invoked which attempts to
+cancel the asynchronous action with async_cancel(). If the event is
+not cancellable it is left pending, although this means that no future
+asynchronous actions can be performed on the interface until the
+transaction voluntarily calls async_finish(). While this may seem
+suboptimal, cancellation here is quite analogous to thread
+cancellation, which is generally considered a difficult problem.
+
+The notion of asynchronous transactions is complicated by the fact
+that they may originate from both inside and outside of the agent.
+For instance, a user initiates an asynchronous START transaction when
+he performs an `ifconfig hme0 dhcp start', but the agent will
+internally need to perform asynchronous EXTEND transactions to extend
+the lease before it expires. This leads us into the `ipc_action'
+object.
+
+An `ipc_action' represents the IPC-related pieces of an asynchronous
+transaction that was started as a result of a user request. Only
+IPC-generated asynchronous transactions have a valid `ipc_action'
+object. Note that since there can be at most one asynchronous action
+per interface, there can also be at most one `ipc_action' per
+interface (this means it can also conveniently be embedded inside the
+`ifslist' structure).
+
+One of the main purposes of the `ipc_action' object is to timeout user
+events. This is not the same as timing out the transaction; for
+instance, when the user specifies a timeout value as an argument to
+ifconfig, he is specifying an `ipc_action' timeout; in other words,
+how long he is willing to wait for the command to complete. However,
+even after the command times out for the user, the asynchronous
+transaction continues until async_timeout() occurs.
+
+It is worth understanding these timeouts since the relationship is
+subtle but powerful. The `async_action' timer specifies how long the
+agent will try to perform the transaction; the `ipc_action' timer
+specifies how long the user is willing to wait for the action to
+complete. If when the `async_action' timer fires and async_timeout()
+is called, there is no associated `ipc_action' (either because the
+transaction was not initiated by a user or because the user already
+timed out), then async_cancel() proceeds as described previously. If,
+on the other hand, the user is still waiting for the transaction to
+complete, then async_timeout() is rescheduled and the transaction is
+left pending. While this behavior might seem odd, it adheres to the
+principles of least surprise: when a user is willing to wait for a
+transaction to complete, the agent should try for as long as they're
+willing to wait. On the other hand, if the agent were to take that
+stance with its internal transactions, it would block out
+user-requested operations if the internal transaction never completed
+(perhaps because the server never sent an ACK in response to our lease
+extension REQUEST).
+
+The API provided for the `ipc_action' object is quite similar to the
+one for the `async_action' object: when an IPC request comes in for an
+operation requiring asynchronous operation, ipc_action_start() is
+called. When the request completes, ipc_action_finish() is called.
+If the user times out before the request completes, then
+ipc_action_timeout() is called.
+
+Packet Management
+-----------------
+
+Another complicated area is packet management: building, manipulating,
+sending and receiving packets. These operations are all encapsulated
+behind a dozen or so interfaces (see packet.h) that abstract the
+unimportant details away from the rest of the agent code. In order to
+send a DHCP packet, code first calls init_pkt(), which returns a
+dhcp_pkt_t initialized suitably for transmission. Note that currently
+init_pkt() returns a dhcp_pkt_t that is actually allocated as part of
+the `ifslist', but this may change in the future.. After calling
+init_pkt(), the add_pkt_opt*() functions are used to add options to
+the DHCP packet. Finally, send_pkt() can be used to transmit the
+packet to a given IP address.
+
+The send_pkt() function is actually quite complicated; for one, it
+must internally use either DLPI or sockets depending on the state of
+the interface; for two, it handles the details of packet timeout and
+retransmission. The last argument to send_pkt() is a pointer to a
+"stop function". If this argument is passed as NULL, then the packet
+will only be sent once (it won't be retransmitted). Otherwise, before
+each retransmission, the stop function will be called back prior to
+retransmission. The return value from this function indicates whether
+to continue retransmission or not, which allows the send_pkt() caller
+to control the retransmission policy without making it have to deal
+with the retransmission mechanism. See init_reboot.c for an example
+of this in action.
+
+The recv_pkt() function is simpler but still complicated by the fact
+that one may want to receive several different types of packets at
+once; for instance, after sending a REQUEST, either an ACK or a NAK is
+acceptable. Also, before calling recv_pkt(), the caller must know
+that there is data to be read from the socket (this can be
+accomplished by using the event handler), otherwise recv_pkt() will
+block, which is clearly not acceptable.
+
+Time
+----
+
+The notion of time is an exceptionally subtle area. You will notice
+five ways that time is represented in the source: as lease_t's,
+uint32_t's, time_t's, hrtime_t's, and monosec_t's. Each of these
+types serves a slightly different function.
+
+The `lease_t' type is the simplest to understand; it is the unit of
+time in the CD_{LEASE,T1,T2}_TIME options in a DHCP packet, as defined
+by RFC2131. This is defined as a positive number of seconds (relative
+to some fixed point in time) or the value `-1' (DHCP_PERM) which
+represents infinity (i.e., a permanent lease). The lease_t should be
+used either when dealing with actual DHCP packets that are sent on the
+wire or for variables which follow the exact definition given in the
+RFC.
+
+The `uint32_t' type is also used to represent a relative time in
+seconds. However, here the value `-1' is not special and of course
+this type is not tied to any definition given in RFC2131. Use this
+for representing "offsets" from another point in time that are not
+DHCP lease times.
+
+The `time_t' type is the natural Unix type for representing time since
+the epoch. Unfortunately, it is affected by stime(2) or adjtime(2)
+and since the DHCP client is used during system installation (and thus
+when time is typically being configured), the time_t cannot be used in
+general to represent an absolute time since the epoch. For instance,
+if a time_t were used to keep track of when a lease began, and then a
+minute later stime(2) was called to adjust the system clock forward a
+year, then the lease would appeared to have expired a year ago even
+though it has only been a minute. For this reason, time_t's should
+only be used either when wall time must be displayed (such as in
+DHCP_STATUS ipc transaction) or when a time meaningful across reboots
+must be obtained (such as when caching an ACK packet at system
+shutdown).
+
+The `hrtime_t' type returned from gethrtime() works around the
+limitations of the time_t in that it is not affected by stime(2) or
+adjtime(2), with the disadvantage that it represents time from some
+arbitrary time in the past and in nanoseconds. The timer queue code
+deals with hrtime_t's directly since that particular piece of code is
+meant to be fairly independent of the rest of the DHCP client.
+
+However, dealing with nanoseconds is error-prone when all the other
+time types are in seconds. As a result, yet another time type, the
+`monosec_t' was created to represent a monotonically increasing time
+in seconds, and is really no more than (hrtime_t / NANOSEC). Note
+that this unit is typically used where time_t's would've traditionally
+been used. The function monosec() in util.c returns the current
+monosec, and monosec_to_time() can convert a given monosec to wall
+time, using the system's current notion of time.
+
+One additional limitation of the `hrtime_t' and `monosec_t' types is
+that they are unaware of the passage of time across checkpoint/resume
+events (e.g., those generated by sys-suspend(1M)). For example, if
+gethrtime() returns time T, and then the machine is suspended for 2
+hours, and then gethrtime() is called again, the time returned is not
+T + (2 * 60 * 60 * NANOSEC), but rather approximately still T.
+
+To work around this (and other checkpoint/resume related problems),
+when a system is resumed, the DHCP client makes the pessimistic
+assumption that all finite leases have expired while the machine was
+suspended and must be obtained again. This is known as "refreshing"
+the leases, and is handled by refresh_ifslist().
+
+Note that it appears like a more intelligent approach would be to
+record the time(2) when the system is suspended, compare that against
+the time(2) when the system is resumed, and use the delta between them
+to decide which leases have expired. Sadly, this cannot be done since
+through at least Solaris 8, it is not possible for userland programs
+to be notified of system suspend events.
+
+Configuration
+-------------
+
+For the most part, the DHCP client only *retrieves* configuration data
+from the DHCP server, leaving the configuration to scripts (such as
+boot scripts), which themselves use dhcpinfo(1) to retrieve the data
+from the DHCP client. This is desirable because it keeps the mechanism
+of retrieving the configuration data decoupled from the policy of using
+the data.
+
+However, unless used in "inform" mode, the DHCP client *does* configure
+each interface enough to allow it to communicate with other hosts.
+Specifically, the DHCP client configures the interface's IP address,
+netmask, and broadcast address using the information provided by the
+server. Further, for physical interfaces, any provided default routes
+are also configured. Since logical interfaces cannot be stored in the
+kernel routing table, and in most cases, logical interfaces share a
+default route with their associated physical interface, the DHCP client
+does not automatically add or remove default routes when leases are
+acquired or expired on logical interfaces.
+
+Event Scripting
+---------------
+
+The DHCP client supports user program invocations on DHCP events. The
+supported events are BOUND, EXTEND, EXPIRE, DROP and RELEASE. The user
+program runs asynchronous to the DHCP client so that the main event
+loop stays active to process other events, including events triggered
+by the user program (for example, when it invokes dhcpinfo).
+
+The user program execution is part of the transaction of a DHCP command.
+For example, if the user program is not enabled, the transaction of the
+DHCP command START is considered over when an ACK is received and the
+interface is configured successfully. If the user program is enabled,
+it is invoked after the interface is configured successfully, and the
+transaction is considered over only when the user program exits. The
+event scripting implementation makes use of the asynchronous operations
+discussed in the "Transactions" section.
+
+The upper bound of 58 seconds is imposed on how long the user program
+can run. If the user program does not exit after 55 seconds, the signal
+SIGTERM is sent to it. If it still does not exit after additional 3
+seconds, the signal SIGKILL is sent to it. Since the event handler is
+a wrapper around poll(), the DHCP client cannot directly observe the
+completion of the user program. Instead, the DHCP client creates a
+child "helper" process to synchronously monitor the user program (this
+process is also used to send the aformentioned signals to the process,
+if necessary). The DHCP client and the helper process share a pipe
+which is included in the set of poll descriptors monitored by the DHCP
+client's event handler. When the user program exits, the helper process
+passes the user program exit status to the DHCP client through the pipe,
+informing the DHCP client that the user program has finished. When the
+DHCP client is asked to shut down, it will wait for any running instances
+of the user program to complete.
diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/adopt.c b/usr/src/cmd/cmd-inet/sbin/dhcpagent/adopt.c
new file mode 100644
index 0000000000..c8e8fd3b30
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/adopt.c
@@ -0,0 +1,185 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * ADOPTING state of the client state machine.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/sockio.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/systeminfo.h>
+#include <netinet/inetutil.h>
+#include <netinet/dhcp.h>
+#include <dhcpmsg.h>
+
+#include "async.h"
+#include "util.h"
+#include "packet.h"
+#include "interface.h"
+#include "states.h"
+
+
+typedef struct {
+ char dk_if_name[IFNAMSIZ];
+ char dk_ack[1];
+} dhcp_kcache_t;
+
+static int get_dhcp_kcache(dhcp_kcache_t **, size_t *);
+
+/*
+ * dhcp_adopt(): adopts the interface managed by the kernel for diskless boot
+ *
+ * input: void
+ * output: int: nonzero on success, zero on failure
+ */
+
+int
+dhcp_adopt(void)
+{
+ int retval;
+ dhcp_kcache_t *kcache = NULL;
+ size_t kcache_size;
+ PKT_LIST *plp = NULL;
+ struct ifslist *ifsp;
+
+ retval = get_dhcp_kcache(&kcache, &kcache_size);
+ if (retval == 0 || kcache_size < sizeof (dhcp_kcache_t)) {
+ dhcpmsg(MSG_CRIT, "dhcp_adopt: cannot fetch kernel cache");
+ goto failure;
+ }
+
+ dhcpmsg(MSG_DEBUG, "dhcp_adopt: fetched %s kcache", kcache->dk_if_name);
+
+ /*
+ * convert the kernel's ACK into binary
+ */
+
+ plp = calloc(1, sizeof (PKT_LIST));
+ if (plp == NULL)
+ goto failure;
+
+ plp->len = strlen(kcache->dk_ack) / 2;
+ plp->pkt = malloc(plp->len);
+ if (plp->pkt == NULL)
+ goto failure;
+
+ dhcpmsg(MSG_DEBUG, "dhcp_adopt: allocated ACK of %d bytes", plp->len);
+
+ if (hexascii_to_octet(kcache->dk_ack, plp->len * 2, plp->pkt, &plp->len)
+ != 0) {
+ dhcpmsg(MSG_CRIT, "dhcp_adopt: cannot convert kernel ACK");
+ goto failure;
+ }
+
+ if (dhcp_options_scan(plp, B_TRUE) != 0) {
+ dhcpmsg(MSG_CRIT, "dhcp_adopt: cannot parse kernel ACK");
+ goto failure;
+ }
+
+ /*
+ * make an interface to represent the "cached interface" in
+ * the kernel, hook up the ACK packet we made, and send out
+ * the extend request (to attempt to renew the lease).
+ *
+ * we do a send_extend() instead of doing a dhcp_init_reboot()
+ * because although dhcp_init_reboot() is more correct from a
+ * protocol perspective, it introduces a window where a
+ * diskless client has no IP address but may need to page in
+ * more of this program. we could mlockall(), but that's
+ * going to be a mess, especially with handling malloc() and
+ * stack growth, so it's easier to just renew(). the only
+ * catch here is that if we are not granted a renewal, we're
+ * totally hosed and can only bail out.
+ */
+
+ ifsp = insert_ifs(kcache->dk_if_name, B_TRUE, &retval);
+ if (ifsp == NULL)
+ goto failure;
+
+ ifsp->if_state = ADOPTING;
+ ifsp->if_dflags |= DHCP_IF_PRIMARY;
+
+ /*
+ * move to BOUND and use the information in our ACK packet
+ */
+
+ if (dhcp_bound(ifsp, plp) == 0) {
+ dhcpmsg(MSG_CRIT, "dhcp_adopt: cannot use cached packet");
+ goto failure;
+ }
+
+ if (async_start(ifsp, DHCP_EXTEND, B_FALSE) == 0) {
+ dhcpmsg(MSG_CRIT, "dhcp_adopt: async_start failed");
+ goto failure;
+ }
+
+ if (dhcp_extending(ifsp) == 0) {
+ dhcpmsg(MSG_CRIT, "dhcp_adopt: cannot send renew request");
+ goto failure;
+ }
+
+ free(kcache);
+ return (1);
+
+failure:
+ free(kcache);
+ if (plp != NULL)
+ free(plp->pkt);
+ free(plp);
+ return (0);
+}
+
+/*
+ * get_dhcp_kcache(): fetches the DHCP ACK and interface name from the kernel
+ *
+ * input: dhcp_kcache_t **: a dynamically-allocated cache packet
+ * size_t *: the length of that packet (on return)
+ * output: int: nonzero on success, zero on failure
+ */
+
+static int
+get_dhcp_kcache(dhcp_kcache_t **kernel_cachep, size_t *kcache_size)
+{
+ char dummy;
+ long size;
+
+ size = sysinfo(SI_DHCP_CACHE, &dummy, sizeof (dummy));
+ if (size == -1)
+ return (0);
+
+ *kcache_size = size;
+ *kernel_cachep = malloc(*kcache_size);
+ if (*kernel_cachep == NULL)
+ return (0);
+
+ (void) sysinfo(SI_DHCP_CACHE, (caddr_t)*kernel_cachep, size);
+ return (1);
+}
diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/agent.c b/usr/src/cmd/cmd-inet/sbin/dhcpagent/agent.c
new file mode 100644
index 0000000000..ee7a987003
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/agent.c
@@ -0,0 +1,845 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+#include <locale.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <dhcp_hostconf.h>
+#include <dhcp_symbol.h>
+#include <dhcpagent_ipc.h>
+#include <dhcpmsg.h>
+#include <netinet/dhcp.h>
+
+#include "async.h"
+#include "agent.h"
+#include "script_handler.h"
+#include "util.h"
+#include "class_id.h"
+#include "states.h"
+#include "packet.h"
+
+#ifndef TEXT_DOMAIN
+#define TEXT_DOMAIN "SYS_TEST"
+#endif
+
+iu_timer_id_t inactivity_id;
+int class_id_len = 0;
+char *class_id;
+iu_eh_t *eh;
+iu_tq_t *tq;
+pid_t grandparent;
+
+static boolean_t shutdown_started = B_FALSE;
+static boolean_t do_adopt = B_FALSE;
+static unsigned int debug_level = 0;
+static iu_eh_callback_t accept_event, ipc_event;
+
+/*
+ * The ipc_cmd_allowed[] table indicates which IPC commands are allowed in
+ * which states; a non-zero value indicates the command is permitted.
+ *
+ * START is permitted if the interface is fresh, or if we are in the process
+ * of trying to obtain a lease (as a convenience to save the administrator
+ * from having to do an explicit DROP). EXTEND, RELEASE, and GET_TAG require
+ * a lease to be obtained in order to make sense. INFORM is permitted if the
+ * interface is fresh or has an INFORM in progress or previously done on it --
+ * otherwise a DROP or RELEASE is first required. PING and STATUS always make
+ * sense and thus are always permitted, as is DROP in order to permit the
+ * administrator to always bail out.
+ */
+static int ipc_cmd_allowed[DHCP_NSTATES][DHCP_NIPC] = {
+ /* D E P R S S I G */
+ /* R X I E T T N E */
+ /* O T N L A A F T */
+ /* P E G E R T O _ */
+ /* . N . A T U R T */
+ /* . D . S . S M A */
+ /* . . . E . . . G */
+ /* INIT */ { 1, 0, 1, 0, 1, 1, 1, 0 },
+ /* SELECTING */ { 1, 0, 1, 0, 1, 1, 0, 0 },
+ /* REQUESTING */ { 1, 0, 1, 0, 1, 1, 0, 0 },
+ /* BOUND */ { 1, 1, 1, 1, 0, 1, 0, 1 },
+ /* RENEWING */ { 1, 1, 1, 1, 0, 1, 0, 1 },
+ /* REBINDING */ { 1, 1, 1, 1, 0, 1, 0, 1 },
+ /* INFORMATION */ { 1, 0, 1, 0, 0, 1, 1, 1 },
+ /* INIT_REBOOT */ { 1, 0, 1, 0, 1, 1, 0, 0 },
+ /* ADOPTING */ { 1, 0, 1, 0, 0, 1, 0, 0 },
+ /* INFORM_SENT */ { 1, 0, 1, 0, 0, 1, 1, 0 }
+};
+
+int
+main(int argc, char **argv)
+{
+ boolean_t is_daemon = B_TRUE;
+ boolean_t is_verbose = B_FALSE;
+ int ipc_fd;
+ int c;
+ struct rlimit rl;
+
+ /*
+ * -l is ignored for compatibility with old agent.
+ */
+
+ while ((c = getopt(argc, argv, "vd:l:fa")) != EOF) {
+
+ switch (c) {
+
+ case 'a':
+ do_adopt = B_TRUE;
+ grandparent = getpid();
+ break;
+
+ case 'd':
+ debug_level = strtoul(optarg, NULL, 0);
+ break;
+
+ case 'f':
+ is_daemon = B_FALSE;
+ break;
+
+ case 'v':
+ is_verbose = B_TRUE;
+ break;
+
+ case '?':
+ (void) fprintf(stderr, "usage: %s [-a] [-d n] [-f] [-v]"
+ "\n", argv[0]);
+ return (EXIT_FAILURE);
+
+ default:
+ break;
+ }
+ }
+
+ (void) setlocale(LC_ALL, "");
+ (void) textdomain(TEXT_DOMAIN);
+
+ if (geteuid() != 0) {
+ dhcpmsg_init(argv[0], B_FALSE, is_verbose, debug_level);
+ dhcpmsg(MSG_ERROR, "must be super-user");
+ dhcpmsg_fini();
+ return (EXIT_FAILURE);
+ }
+
+ if (is_daemon && daemonize() == 0) {
+ dhcpmsg_init(argv[0], B_FALSE, is_verbose, debug_level);
+ dhcpmsg(MSG_ERR, "cannot become daemon, exiting");
+ dhcpmsg_fini();
+ return (EXIT_FAILURE);
+ }
+
+ dhcpmsg_init(argv[0], is_daemon, is_verbose, debug_level);
+ (void) atexit(dhcpmsg_fini);
+
+ tq = iu_tq_create();
+ eh = iu_eh_create();
+
+ if (eh == NULL || tq == NULL) {
+ errno = ENOMEM;
+ dhcpmsg(MSG_ERR, "cannot create timer queue or event handler");
+ return (EXIT_FAILURE);
+ }
+
+ /*
+ * ignore most signals that could be reasonably generated.
+ */
+
+ (void) signal(SIGTERM, graceful_shutdown);
+ (void) signal(SIGQUIT, graceful_shutdown);
+ (void) signal(SIGPIPE, SIG_IGN);
+ (void) signal(SIGUSR1, SIG_IGN);
+ (void) signal(SIGUSR2, SIG_IGN);
+ (void) signal(SIGINT, SIG_IGN);
+ (void) signal(SIGHUP, SIG_IGN);
+ (void) signal(SIGCHLD, SIG_IGN);
+
+ /*
+ * upon SIGTHAW we need to refresh any non-infinite leases.
+ */
+
+ (void) iu_eh_register_signal(eh, SIGTHAW, refresh_ifslist, NULL);
+
+ class_id = get_class_id();
+ if (class_id != NULL)
+ class_id_len = strlen(class_id);
+ else
+ dhcpmsg(MSG_WARNING, "get_class_id failed, continuing "
+ "with no vendor class id");
+
+ /*
+ * the inactivity timer is enabled any time there are no
+ * interfaces under DHCP control. if DHCP_INACTIVITY_WAIT
+ * seconds transpire without an interface under DHCP control,
+ * the agent shuts down.
+ */
+
+ inactivity_id = iu_schedule_timer(tq, DHCP_INACTIVITY_WAIT,
+ inactivity_shutdown, NULL);
+
+ /*
+ * max out the number available descriptors, just in case..
+ */
+
+ rl.rlim_cur = RLIM_INFINITY;
+ rl.rlim_max = RLIM_INFINITY;
+ if (setrlimit(RLIMIT_NOFILE, &rl) == -1)
+ dhcpmsg(MSG_ERR, "setrlimit failed");
+
+ /*
+ * create the ipc channel that the agent will listen for
+ * requests on, and register it with the event handler so that
+ * `accept_event' will be called back.
+ */
+
+ switch (dhcp_ipc_init(&ipc_fd)) {
+
+ case 0:
+ break;
+
+ case DHCP_IPC_E_BIND:
+ dhcpmsg(MSG_ERROR, "dhcp_ipc_init: cannot bind to port "
+ "%i (agent already running?)", IPPORT_DHCPAGENT);
+ return (EXIT_FAILURE);
+
+ default:
+ dhcpmsg(MSG_ERROR, "dhcp_ipc_init failed");
+ return (EXIT_FAILURE);
+ }
+
+ if (iu_register_event(eh, ipc_fd, POLLIN, accept_event, 0) == -1) {
+ dhcpmsg(MSG_ERR, "cannot register ipc fd for messages");
+ return (EXIT_FAILURE);
+ }
+
+ /*
+ * if the -a (adopt) option was specified, try to adopt the
+ * kernel-managed interface before we start. Our grandparent
+ * will be waiting for us to finish this, so signal him when
+ * we're done.
+ */
+
+ if (do_adopt) {
+ int result;
+
+ result = dhcp_adopt();
+
+ if (grandparent != (pid_t)0) {
+ dhcpmsg(MSG_DEBUG, "adoption complete, signalling "
+ "parent (%i) to exit.", grandparent);
+ (void) kill(grandparent, SIGALRM);
+ }
+
+ if (result == 0)
+ return (EXIT_FAILURE);
+ }
+
+ /*
+ * enter the main event loop; this is where all the real work
+ * takes place (through registering events and scheduling timers).
+ * this function only returns when the agent is shutting down.
+ */
+
+ switch (iu_handle_events(eh, tq)) {
+
+ case -1:
+ dhcpmsg(MSG_WARNING, "iu_handle_events exited abnormally");
+ break;
+
+ case DHCP_REASON_INACTIVITY:
+ dhcpmsg(MSG_INFO, "no interfaces to manage, shutting down...");
+ break;
+
+ case DHCP_REASON_TERMINATE:
+ dhcpmsg(MSG_INFO, "received SIGTERM, shutting down...");
+ break;
+
+ case DHCP_REASON_SIGNAL:
+ dhcpmsg(MSG_WARNING, "received unexpected signal, shutting "
+ "down...");
+ break;
+ }
+
+ (void) iu_eh_unregister_signal(eh, SIGTHAW, NULL);
+
+ iu_eh_destroy(eh);
+ iu_tq_destroy(tq);
+
+ return (EXIT_SUCCESS);
+}
+
+/*
+ * drain_script(): event loop callback during shutdown
+ *
+ * input: eh_t *: unused
+ * void *: unused
+ * output: boolean_t: B_TRUE if event loop should exit; B_FALSE otherwise
+ */
+
+/* ARGSUSED */
+boolean_t
+drain_script(iu_eh_t *ehp, void *arg)
+{
+ if (shutdown_started == B_FALSE) {
+ shutdown_started = B_TRUE;
+ if (do_adopt == B_FALSE) /* see 4291141 */
+ nuke_ifslist(B_TRUE);
+ }
+ return (script_count == 0);
+}
+
+/*
+ * accept_event(): accepts a new connection on the ipc socket and registers
+ * to receive its messages with the event handler
+ *
+ * input: iu_eh_t *: unused
+ * int: the file descriptor in the iu_eh_t * the connection came in on
+ * (other arguments unused)
+ * output: void
+ */
+
+/* ARGSUSED */
+static void
+accept_event(iu_eh_t *ehp, int fd, short events, iu_event_id_t id, void *arg)
+{
+ int client_fd;
+ int is_priv;
+
+ if (dhcp_ipc_accept(fd, &client_fd, &is_priv) != 0) {
+ dhcpmsg(MSG_ERR, "accept_event: accept on ipc socket");
+ return;
+ }
+
+ if (iu_register_event(eh, client_fd, POLLIN, ipc_event,
+ (void *)is_priv) == -1) {
+ dhcpmsg(MSG_ERROR, "accept_event: cannot register ipc socket "
+ "for callback");
+ }
+}
+
+/*
+ * ipc_event(): processes incoming ipc requests
+ *
+ * input: iu_eh_t *: unused
+ * int: the file descriptor in the iu_eh_t * the request came in on
+ * short: unused
+ * iu_event_id_t: unused
+ * void *: indicates whether the request is from a privileged client
+ * output: void
+ */
+
+/* ARGSUSED */
+static void
+ipc_event(iu_eh_t *ehp, int fd, short events, iu_event_id_t id, void *arg)
+{
+ dhcp_ipc_request_t *request;
+ struct ifslist *ifsp, *primary_ifsp;
+ int error, is_priv = (int)arg;
+ PKT_LIST *plp[2];
+ dhcp_ipc_type_t cmd;
+
+ (void) iu_unregister_event(eh, id, NULL);
+
+ if (dhcp_ipc_recv_request(fd, &request, DHCP_IPC_REQUEST_WAIT) != 0) {
+ dhcpmsg(MSG_ERROR, "ipc_event: dhcp_ipc_recv_request failed");
+ (void) dhcp_ipc_close(fd);
+ return;
+ }
+
+ cmd = DHCP_IPC_CMD(request->message_type);
+ if (cmd >= DHCP_NIPC) {
+ send_error_reply(request, DHCP_IPC_E_CMD_UNKNOWN, &fd);
+ return;
+ }
+
+ /* return EPERM for any of the privileged actions */
+
+ if (!is_priv) {
+ switch (cmd) {
+
+ case DHCP_STATUS:
+ case DHCP_PING:
+ case DHCP_GET_TAG:
+ break;
+
+ default:
+ dhcpmsg(MSG_WARNING, "ipc_event: privileged ipc "
+ "command (%i) attempted on %s", cmd,
+ request->ifname);
+
+ send_error_reply(request, DHCP_IPC_E_PERM, &fd);
+ return;
+ }
+ }
+
+ /*
+ * try to locate the ifs associated with this command. if the
+ * command is DHCP_START or DHCP_INFORM, then if there isn't
+ * an ifs already, make one (there may already be one from a
+ * previous failed attempt to START or INFORM). otherwise,
+ * verify the interface is still valid.
+ */
+
+ ifsp = lookup_ifs(request->ifname);
+
+ switch (cmd) {
+
+ case DHCP_START: /* FALLTHRU */
+ case DHCP_INFORM:
+ /*
+ * it's possible that the interface already exists, but
+ * has been abandoned. usually in those cases we should
+ * return DHCP_IPC_E_UNKIF, but that makes little sense
+ * in the case of "start" or "inform", so just ignore
+ * the abandoned interface and start over anew.
+ */
+
+ if (ifsp != NULL && verify_ifs(ifsp) == 0)
+ ifsp = NULL;
+
+ /*
+ * as part of initializing the ifs, insert_ifs()
+ * creates a DLPI stream at ifsp->if_dlpi_fd.
+ */
+
+ if (ifsp == NULL) {
+ ifsp = insert_ifs(request->ifname, B_FALSE, &error);
+ if (ifsp == NULL) {
+ send_error_reply(request, error, &fd);
+ return;
+ }
+ }
+ break;
+
+ default:
+ if (ifsp == NULL) {
+ if (request->ifname[0] == '\0')
+ error = DHCP_IPC_E_NOPRIMARY;
+ else
+ error = DHCP_IPC_E_UNKIF;
+
+ send_error_reply(request, error, &fd);
+ return;
+ }
+ break;
+ }
+
+ if (verify_ifs(ifsp) == 0) {
+ send_error_reply(request, DHCP_IPC_E_UNKIF, &fd);
+ return;
+ }
+
+ if (ifsp->if_dflags & DHCP_IF_BOOTP) {
+ switch (cmd) {
+
+ case DHCP_EXTEND:
+ case DHCP_RELEASE:
+ case DHCP_INFORM:
+ send_error_reply(request, DHCP_IPC_E_BOOTP, &fd);
+ return;
+
+ default:
+ break;
+ }
+ }
+
+ /*
+ * verify that the interface is in a state which will allow the
+ * command. we do this up front so that we can return an error
+ * *before* needlessly cancelling an in-progress transaction.
+ */
+
+ if (!ipc_cmd_allowed[ifsp->if_state][cmd]) {
+ send_error_reply(request, DHCP_IPC_E_OUTSTATE, &fd);
+ return;
+ }
+
+ if ((request->message_type & DHCP_PRIMARY) && is_priv) {
+ if ((primary_ifsp = lookup_ifs("")) != NULL)
+ primary_ifsp->if_dflags &= ~DHCP_IF_PRIMARY;
+ ifsp->if_dflags |= DHCP_IF_PRIMARY;
+ }
+
+ /*
+ * current design dictates that there can be only one
+ * outstanding transaction per interface -- this simplifies
+ * the code considerably and also fits well with RFC2131.
+ * it is worth classifying the different DHCP commands into
+ * synchronous (those which we will handle now and be done
+ * with) and asynchronous (those which require transactions
+ * and will be completed at an indeterminate time in the
+ * future):
+ *
+ * DROP: removes the agent's management of an interface.
+ * asynchronous as the script program may be invoked.
+ *
+ * PING: checks to see if the agent controls an interface.
+ * synchronous, since no packets need to be sent
+ * to the DHCP server.
+ *
+ * STATUS: returns information about the an interface.
+ * synchronous, since no packets need to be sent
+ * to the DHCP server.
+ *
+ * RELEASE: releases the agent's management of an interface
+ * and brings the interface down. asynchronous as
+ * the script program may be invoked.
+ *
+ * EXTEND: renews a lease. asynchronous, since the agent
+ * needs to wait for an ACK, etc.
+ *
+ * START: starts DHCP on an interface. asynchronous since
+ * the agent needs to wait for OFFERs, ACKs, etc.
+ *
+ * INFORM: obtains configuration parameters for an externally
+ * configured interface. asynchronous, since the
+ * agent needs to wait for an ACK.
+ *
+ * notice that EXTEND, INFORM, START, DROP and RELEASE are
+ * asynchronous. notice also that asynchronous commands may
+ * occur from within the agent -- for instance, the agent
+ * will need to do implicit EXTENDs to extend the lease. in
+ * order to make the code simpler, the following rules apply
+ * for asynchronous commands:
+ *
+ * there can only be one asynchronous command at a time per
+ * interface. the current asynchronous command is managed by
+ * the async_* api: async_start(), async_finish(),
+ * async_timeout(), async_cancel(), and async_pending().
+ * async_start() starts management of a new asynchronous
+ * command on an interface, which should only be done after
+ * async_pending() is called to check that there are no
+ * pending asynchronous commands on that interface. when the
+ * command is completed, async_finish() should be called. all
+ * asynchronous commands have an associated timer, which calls
+ * async_timeout() when it times out. if async_timeout()
+ * decides that the asynchronous command should be cancelled
+ * (see below), it calls async_cancel() to attempt
+ * cancellation.
+ *
+ * asynchronous commands started by a user command have an
+ * associated ipc_action which provides the agent with
+ * information for how to get in touch with the user command
+ * when the action completes. these ipc_action records also
+ * have an associated timeout which may be infinite.
+ * ipc_action_start() should be called when starting an
+ * asynchronous command requested by a user, which sets up the
+ * timer and keeps track of the ipc information (file
+ * descriptor, request type). when the asynchronous command
+ * completes, ipc_action_finish() should be called to return a
+ * command status code to the user and close the ipc
+ * connection). if the command does not complete before the
+ * timer fires, ipc_action_timeout() is called which closes
+ * the ipc connection and returns DHCP_IPC_E_TIMEOUT to the
+ * user. note that independent of ipc_action_timeout(),
+ * ipc_action_finish() should be called.
+ *
+ * on a case-by-case basis, here is what happens (per interface):
+ *
+ * o when an asynchronous command is requested, then
+ * async_pending() is called to see if there is already
+ * an asynchronous event. if so, the command does not
+ * proceed, and if there is an associated ipc_action,
+ * the user command is sent DHCP_IPC_E_PEND.
+ *
+ * o otherwise, the the transaction is started with
+ * async_start(). if the transaction is on behalf
+ * of a user, ipc_action_start() is called to keep
+ * track of the ipc information and set up the
+ * ipc_action timer.
+ *
+ * o if the command completes normally and before a
+ * timeout fires, then async_finish() is called.
+ * if there was an associated ipc_action,
+ * ipc_action_finish() is called to complete it.
+ *
+ * o if the command fails before a timeout fires, then
+ * async_finish() is called, and the interface is
+ * is returned to a known state based on the command.
+ * if there was an associated ipc_action,
+ * ipc_action_finish() is called to complete it.
+ *
+ * o if the ipc_action timer fires before command
+ * completion, then DHCP_IPC_E_TIMEOUT is returned to
+ * the user. however, the transaction continues to
+ * be carried out asynchronously.
+ *
+ * o if async_timeout() fires before command completion,
+ * then if the command was internal to the agent, it
+ * is cancelled. otherwise, if it was a user command,
+ * then if the user is still waiting for the command
+ * to complete, the command continues and async_timeout()
+ * is rescheduled.
+ */
+
+ switch (cmd) {
+
+ case DHCP_DROP: /* FALLTHRU */
+ case DHCP_RELEASE: /* FALLTHRU */
+ case DHCP_EXTEND: /* FALLTHRU */
+ case DHCP_INFORM: /* FALLTHRU */
+ case DHCP_START:
+ /*
+ * if shutdown request has been received, send back an error.
+ */
+ if (shutdown_started) {
+ send_error_reply(request, DHCP_IPC_E_OUTSTATE, &fd);
+ return;
+ }
+
+ if (async_pending(ifsp)) {
+ send_error_reply(request, DHCP_IPC_E_PEND, &fd);
+ return;
+ }
+
+ if (ipc_action_start(ifsp, request, fd) == 0) {
+ dhcpmsg(MSG_WARNING, "ipc_event: ipc_action_start "
+ "failed for %s", ifsp->if_name);
+ send_error_reply(request, DHCP_IPC_E_MEMORY, &fd);
+ return;
+ }
+
+ if (async_start(ifsp, cmd, B_TRUE) == 0) {
+ ipc_action_finish(ifsp, DHCP_IPC_E_MEMORY);
+ return;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ switch (cmd) {
+
+ case DHCP_DROP:
+ (void) script_start(ifsp, EVENT_DROP, dhcp_drop, NULL, NULL);
+ return;
+
+ case DHCP_EXTEND:
+ (void) dhcp_extending(ifsp);
+ break;
+
+ case DHCP_GET_TAG: {
+ dhcp_optnum_t optnum;
+ DHCP_OPT *opt = NULL;
+ boolean_t did_alloc = B_FALSE;
+ PKT_LIST *ack = ifsp->if_ack;
+
+ /*
+ * verify the request makes sense.
+ */
+
+ if (request->data_type != DHCP_TYPE_OPTNUM ||
+ request->data_length != sizeof (dhcp_optnum_t)) {
+ send_error_reply(request, DHCP_IPC_E_PROTO, &fd);
+ return;
+ }
+
+ (void) memcpy(&optnum, request->buffer, sizeof (dhcp_optnum_t));
+load_option:
+ switch (optnum.category) {
+
+ case DSYM_SITE: /* FALLTHRU */
+ case DSYM_STANDARD:
+ if (optnum.code <= DHCP_LAST_OPT)
+ opt = ack->opts[optnum.code];
+ break;
+
+ case DSYM_VENDOR:
+ /*
+ * the test against VS_OPTION_START is broken up into
+ * two tests to avoid compiler warnings under intel.
+ */
+
+ if ((optnum.code > VS_OPTION_START ||
+ optnum.code == VS_OPTION_START) &&
+ optnum.code <= VS_OPTION_END)
+ opt = ack->vs[optnum.code];
+ break;
+
+ case DSYM_FIELD:
+ if (optnum.code + optnum.size > sizeof (PKT))
+ break;
+
+ /* + 2 to account for option code and length byte */
+ opt = malloc(optnum.size + 2);
+ if (opt == NULL) {
+ send_error_reply(request, DHCP_IPC_E_MEMORY,
+ &fd);
+ return;
+ }
+
+ did_alloc = B_TRUE;
+ opt->len = optnum.size;
+ opt->code = optnum.code;
+ (void) memcpy(&opt->value, (caddr_t)ack->pkt +
+ opt->code, opt->len);
+
+ break;
+
+ default:
+ send_error_reply(request, DHCP_IPC_E_PROTO, &fd);
+ return;
+ }
+
+ /*
+ * return the option payload, if there was one. the "+ 2"
+ * accounts for the option code number and length byte.
+ */
+
+ if (opt != NULL) {
+ send_data_reply(request, &fd, 0, DHCP_TYPE_OPTION, opt,
+ opt->len + 2);
+
+ if (did_alloc)
+ free(opt);
+ return;
+ } else if (ack != ifsp->if_orig_ack) {
+ /*
+ * There wasn't any definition for the option in the
+ * current ack, so now retry with the original ack if
+ * the original ack is not the current ack.
+ */
+ ack = ifsp->if_orig_ack;
+ goto load_option;
+ }
+
+ /*
+ * note that an "okay" response is returned either in
+ * the case of an unknown option or a known option
+ * with no payload. this is okay (for now) since
+ * dhcpinfo checks whether an option is valid before
+ * ever performing ipc with the agent.
+ */
+
+ send_ok_reply(request, &fd);
+ return;
+ }
+
+ case DHCP_INFORM:
+ dhcp_inform(ifsp);
+ /* next destination: dhcp_acknak() */
+ return;
+
+ case DHCP_PING:
+ if (ifsp->if_dflags & DHCP_IF_FAILED)
+ send_error_reply(request, DHCP_IPC_E_FAILEDIF, &fd);
+ else
+ send_ok_reply(request, &fd);
+ return;
+
+ case DHCP_RELEASE:
+ (void) script_start(ifsp, EVENT_RELEASE, dhcp_release,
+ "Finished with lease.", NULL);
+ return;
+
+ case DHCP_START:
+ assert(ifsp->if_state == INIT);
+ (void) canonize_ifs(ifsp);
+
+ /*
+ * if we have a valid hostconf lying around, then jump
+ * into INIT_REBOOT. if it fails, we'll end up going
+ * through the whole selecting() procedure again.
+ */
+
+ error = read_hostconf(ifsp->if_name, plp, 2);
+ if (error != -1) {
+ ifsp->if_orig_ack = ifsp->if_ack = plp[0];
+ if (error > 1) {
+ /*
+ * Return indicated we had more than one packet
+ * second one is the original ack. Older
+ * versions of the agent wrote only one ack
+ * to the file, we now keep both the first
+ * ack as well as the last one.
+ */
+ ifsp->if_orig_ack = plp[1];
+ }
+ dhcp_init_reboot(ifsp);
+ /* next destination: dhcp_acknak() */
+ return;
+ }
+
+ /*
+ * if not debugging, wait for a few seconds before
+ * going into SELECTING.
+ */
+
+ if (debug_level == 0) {
+ if (iu_schedule_timer_ms(tq,
+ lrand48() % DHCP_SELECT_WAIT, dhcp_start, ifsp)
+ != -1) {
+ hold_ifs(ifsp);
+ /* next destination: dhcp_start() */
+ return;
+ }
+ }
+
+ dhcp_selecting(ifsp);
+ /* next destination: dhcp_requesting() */
+ return;
+
+ case DHCP_STATUS: {
+ dhcp_status_t status;
+
+ status.if_began = monosec_to_time(ifsp->if_curstart_monosec);
+
+ if (ifsp->if_lease == DHCP_PERM) {
+ status.if_t1 = DHCP_PERM;
+ status.if_t2 = DHCP_PERM;
+ status.if_lease = DHCP_PERM;
+ } else {
+ status.if_t1 = status.if_began + ifsp->if_t1;
+ status.if_t2 = status.if_began + ifsp->if_t2;
+ status.if_lease = status.if_began + ifsp->if_lease;
+ }
+
+ status.version = DHCP_STATUS_VER;
+ status.if_state = ifsp->if_state;
+ status.if_dflags = ifsp->if_dflags;
+ status.if_sent = ifsp->if_sent;
+ status.if_recv = ifsp->if_received;
+ status.if_bad_offers = ifsp->if_bad_offers;
+
+ (void) strlcpy(status.if_name, ifsp->if_name, IFNAMSIZ);
+
+ send_data_reply(request, &fd, 0, DHCP_TYPE_STATUS, &status,
+ sizeof (dhcp_status_t));
+ return;
+ }
+
+ default:
+ return;
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/agent.h b/usr/src/cmd/cmd-inet/sbin/dhcpagent/agent.h
new file mode 100644
index 0000000000..1e068b1186
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/agent.h
@@ -0,0 +1,124 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef AGENT_H
+#define AGENT_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <libinetutil.h>
+
+/*
+ * agent.h contains general symbols that should be available to all
+ * source programs that are part of the agent. in general, files
+ * specific to a given collection of code (such as interface.h or
+ * dhcpmsg.h) are to be preferred to this dumping ground. use only
+ * when necessary.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * global variables: `tq' and `eh' represent the global timer queue
+ * and event handler, as described in the README. `class_id' is our
+ * vendor class id set early on in main(). `inactivity_id' is the
+ * timer id of the global inactivity timer, which shuts down the agent
+ * if there are no interfaces to manage for DHCP_INACTIVITY_WAIT
+ * seconds. `grandparent' is the pid of the original process when in
+ * adopt mode.
+ */
+
+extern iu_tq_t *tq;
+extern iu_eh_t *eh;
+extern char *class_id;
+extern int class_id_len;
+extern iu_timer_id_t inactivity_id;
+extern pid_t grandparent;
+
+boolean_t drain_script(iu_eh_t *, void *);
+
+/*
+ * global tunable parameters. an `I' in the preceding comment indicates
+ * an implementation artifact; a `R' in the preceding comment indicates
+ * that the value was suggested (or required) by RFC2131.
+ */
+
+/* I: how many seconds to wait before restarting DHCP on an interface */
+#define DHCP_RESTART_WAIT 10
+
+/*
+ * I: the maximum number of milliseconds to wait before SELECTING on an
+ * interface. RFC2131 recommends a random wait of between one and ten seconds,
+ * to speed up DHCP at boot we wait between zero and two seconds.
+ */
+#define DHCP_SELECT_WAIT 2000
+
+/* R: how many seconds before lease expiration we give up trying to rebind */
+#define DHCP_REBIND_MIN 60
+
+/* I: seconds to wait retrying dhcp_expire() if uncancellable async event */
+#define DHCP_EXPIRE_WAIT 10
+
+/* R: approximate percentage of lease time to wait until RENEWING state */
+#define DHCP_T1_FACT .5
+
+/* R: approximate percentage of lease time to wait until REBINDING state */
+#define DHCP_T2_FACT .875
+
+/* I: number of REQUEST attempts before assuming something is awry */
+#define DHCP_MAX_REQUESTS 4
+
+/* I: epsilon in seconds used to check if old and new lease times are same */
+#define DHCP_LEASE_EPS 30
+
+/* I: if lease is not being extended, seconds left before alerting user */
+#define DHCP_LEASE_ERROR_THRESH (60*60*24*2) /* two days */
+
+/* I: how many seconds before bailing out if there's no work to do */
+#define DHCP_INACTIVITY_WAIT (60*3) /* three minutes */
+
+/* I: the maximum amount of seconds we use an adopted lease */
+#define DHCP_ADOPT_LEASE_MAX (60*60) /* one hour */
+
+/* I: number of seconds grandparent waits for child to finish adoption. */
+#define DHCP_ADOPT_SLEEP 30
+
+/* I: the maximum amount of milliseconds to wait for an ipc request */
+#define DHCP_IPC_REQUEST_WAIT (3*1000) /* three seconds */
+
+/*
+ * reasons for why iu_handle_events() returned
+ */
+enum { DHCP_REASON_INACTIVITY, DHCP_REASON_SIGNAL, DHCP_REASON_TERMINATE };
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AGENT_H */
diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/arp_check.c b/usr/src/cmd/cmd-inet/sbin/dhcpagent/arp_check.c
new file mode 100644
index 0000000000..f4925468d8
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/arp_check.c
@@ -0,0 +1,235 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%W% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <poll.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <net/if_arp.h>
+#include <sys/dlpi.h>
+#include <stddef.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/pfmod.h>
+#include <dhcpmsg.h>
+#include <stddef.h>
+
+#include "defaults.h"
+#include "util.h"
+#include "interface.h"
+#include "dlpi_io.h"
+#include "arp_check.h"
+
+/*
+ * the struct arp_info is used by arp_reply_filter() to build a filter
+ * that only receives replies from the ARPed IP address.
+ */
+
+struct arp_info {
+
+ uchar_t send_addr_offset; /* from start of ARP frame */
+ in_addr_t send_addr; /* arped IP address */
+};
+
+/*
+ * arp_reply_filter(): builds a filter that permits ARP replies to our request
+ *
+ * input: ushort_t *: a place to store the packet filter code
+ * void *: a struct arp_info containing the requested IP address
+ * output: ushort_t *: two bytes past the last byte of the filter
+ */
+
+static ushort_t *
+arp_reply_filter(ushort_t *pfp, void *arg)
+{
+ struct arp_info *ai = (struct arp_info *)arg;
+
+ *pfp++ = ENF_PUSHWORD + (offsetof(struct arphdr, ar_op) / 2);
+ *pfp++ = ENF_PUSHLIT | ENF_EQ;
+ *pfp++ = htons(ARPOP_REPLY);
+
+ /*
+ * make sure this ARP reply is from the target IP address,
+ * which will be the "sender" IP address in the reply (even in
+ * the case of proxy ARP). the position of sender IP address
+ * depends on the link layer; so we can be link-layer
+ * independent, these values are calculated in arp_check().
+ *
+ * the byteorder issues here are *really* subtle. suppose
+ * that the network address is 0x11223344 (as stored in the
+ * packet read off the wire) by an intel machine. then notice
+ * that since the packet filter operates 16 bits at a time
+ * that the high-order word will load as 0x2211 and the
+ * low-order word will load as 0x4433. so send_addr has the
+ * register value 0x44332211 on intel since that will store to
+ * the network address 0x11223344 in memory. thus, to compare
+ * the low-order word, we must first ntohl() send_addr, which
+ * changes its register-value to 0x11223344, and then mask
+ * off the high-order bits, getting 0x3344, and then convert
+ * that to network order, getting 0x4433, which is what we
+ * want. the same logic applies to the high-order word. you
+ * are not expected to understand this.
+ */
+
+ *pfp++ = ENF_PUSHWORD + (ai->send_addr_offset / 2) + 1;
+ *pfp++ = ENF_PUSHLIT | ENF_EQ;
+ *pfp++ = htons(ntohl(ai->send_addr) & 0xffff);
+ *pfp++ = ENF_AND;
+
+ *pfp++ = ENF_PUSHWORD + (ai->send_addr_offset / 2);
+ *pfp++ = ENF_PUSHLIT | ENF_EQ;
+ *pfp++ = htons(ntohl(ai->send_addr) >> 16);
+ *pfp++ = ENF_AND;
+
+ return (pfp);
+}
+
+/*
+ * arp_check(): checks to see if a given IP address is already in use
+ *
+ * input: struct ifslist *: the interface to send the ARP request on
+ * in_addr_t: the IP address to send from, network order
+ * in_addr_t: the IP address to check on, network order
+ * uchar_t *: a scratch buffer that holds the hardware address
+ * of the machine that replied to our ARP request,
+ * if there was one.
+ * uint32_t: the length of the buffer
+ * uint32_t: how long to wait for an ARP reply, in milliseconds
+ * output: int: 1 if the IP address is in use, 0 if not in use.
+ */
+
+int
+arp_check(struct ifslist *ifsp, in_addr_t send_addr, in_addr_t target_addr,
+ uchar_t *target_hwaddr, uint32_t target_hwlen, uint32_t timeout_msec)
+{
+ uint32_t buf[DLPI_BUF_MAX / sizeof (uint32_t)];
+ dl_info_ack_t *dlia = (dl_info_ack_t *)buf;
+ int fd;
+ struct arphdr *arp_pkt = NULL;
+ uchar_t *arp_daddr = NULL;
+ caddr_t arp_payload;
+ uchar_t arp_dlen;
+ size_t offset;
+ struct pollfd pollfd;
+ int retval;
+ struct arp_info ai;
+ unsigned int arp_pkt_len;
+
+ fd = dlpi_open(ifsp->if_name, dlia, sizeof (buf), ETHERTYPE_ARP);
+ if (fd == -1)
+ goto failure;
+
+ /*
+ * the packet consists of an ARP header, two IP addresses
+ * and two hardware addresses (each ifsp->if_hwlen bytes long).
+ */
+
+ arp_pkt_len = sizeof (struct arphdr) + (sizeof (ipaddr_t) * 2) +
+ (ifsp->if_hwlen * 2);
+
+ arp_pkt = malloc(arp_pkt_len);
+ arp_daddr = build_broadcast_dest(dlia, &arp_dlen);
+ if (arp_pkt == NULL || arp_daddr == NULL)
+ goto failure;
+
+ (void) memset(arp_pkt, 0xff, arp_pkt_len);
+
+ arp_pkt->ar_hrd = htons(ifsp->if_hwtype);
+ arp_pkt->ar_pro = htons(ETHERTYPE_IP);
+ arp_pkt->ar_hln = ifsp->if_hwlen;
+ arp_pkt->ar_pln = sizeof (ipaddr_t);
+ arp_pkt->ar_op = htons(ARPOP_REQUEST);
+
+ arp_payload = (caddr_t)&arp_pkt[1];
+ (void) memcpy(arp_payload, ifsp->if_hwaddr, ifsp->if_hwlen);
+ offset = ifsp->if_hwlen;
+
+ /*
+ * while we're at the appropriate offset for sender IP address,
+ * store it for use by the packet filter.
+ */
+
+ ai.send_addr = target_addr;
+ ai.send_addr_offset = offset + sizeof (struct arphdr);
+
+ (void) memcpy(&arp_payload[offset], &send_addr, sizeof (ipaddr_t));
+ offset += ifsp->if_hwlen + sizeof (ipaddr_t);
+ (void) memcpy(&arp_payload[offset], &target_addr, sizeof (ipaddr_t));
+
+ /*
+ * install the packet filter, send our ARP request, and wait
+ * for a reply. waiting usually isn't a good idea since the
+ * design of the agent is nonblocking. however, we can
+ * tolerate short waits (< 5 seconds).
+ */
+
+ set_packet_filter(fd, arp_reply_filter, &ai, "ARP reply");
+
+ if (dlpi_send_link(fd, arp_pkt, arp_pkt_len, arp_daddr, arp_dlen) == -1)
+ goto failure;
+
+ pollfd.fd = fd;
+ pollfd.events = POLLIN;
+
+ retval = poll(&pollfd, 1, timeout_msec);
+ if (retval > 0 && target_hwaddr != NULL) {
+
+ /*
+ * try to grab the hardware address. if we fail, we'll
+ * just end up with some misleading diagnostics. the
+ * hardware address is at the start of the payload.
+ */
+
+ if (dlpi_recv_link(fd, arp_pkt, arp_pkt_len, DLPI_RECV_SHORT) ==
+ arp_pkt_len)
+ (void) memcpy(target_hwaddr, arp_payload, target_hwlen);
+ }
+
+ free(arp_daddr);
+ free(arp_pkt);
+ (void) close(fd);
+ return ((retval == 0) ? 0 : 1);
+
+failure:
+ free(arp_daddr);
+ free(arp_pkt);
+ (void) close(fd);
+
+ if (df_get_bool(ifsp->if_name, DF_IGNORE_FAILED_ARP)) {
+ dhcpmsg(MSG_WARNING, "arp_check: cannot send ARP request: "
+ "assuming address is available");
+ return (0);
+ }
+
+ dhcpmsg(MSG_WARNING, "arp_check: cannot send ARP request: "
+ "assuming address is unavailable");
+ return (1);
+}
diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/arp_check.h b/usr/src/cmd/cmd-inet/sbin/dhcpagent/arp_check.h
new file mode 100644
index 0000000000..c3fff1ba0c
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/arp_check.h
@@ -0,0 +1,54 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef ARP_CHECK_H
+#define ARP_CHECK_H
+
+#pragma ident "%W% %E% SMI"
+
+#include <sys/types.h>
+#include <netinet/in.h>
+
+#include "interface.h"
+
+/*
+ * arp_check.[ch] provide an interface for checking whether a given IP
+ * address is currently in use. see arp_check.c for documentation on
+ * how to use the exported function.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int arp_check(struct ifslist *, in_addr_t, in_addr_t, uchar_t *,
+ uint32_t, uint32_t);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ARP_CHECK_H */
diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/async.c b/usr/src/cmd/cmd-inet/sbin/dhcpagent/async.c
new file mode 100644
index 0000000000..d7fb37970d
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/async.c
@@ -0,0 +1,296 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dhcpmsg.h>
+#include <libinetutil.h>
+
+#include "async.h"
+#include "util.h"
+#include "agent.h"
+#include "interface.h"
+#include "script_handler.h"
+
+static void async_timeout(iu_tq_t *, void *);
+
+/*
+ * async_pending(): checks to see if an async command is pending. if a stale
+ * async command is found, cancellation is attempted.
+ *
+ * input: struct ifslist *: the interface to check for an async command on
+ * output: boolean_t: B_TRUE if async command is pending, B_FALSE if not
+ */
+
+boolean_t
+async_pending(struct ifslist *ifsp)
+{
+ if (!(ifsp->if_dflags & DHCP_IF_BUSY))
+ return (B_FALSE);
+
+ /*
+ * if the command was not started by the user (i.e., was
+ * started internal to the agent), then it will timeout in
+ * async_timeout() -- don't shoot it here.
+ */
+
+ if (!ifsp->if_async.as_user)
+ return (B_TRUE);
+
+ if (ifsp->if_script_pid != -1)
+ return (B_TRUE);
+
+ /*
+ * user command -- see if they went away. if they went away,
+ * either a timeout was already sent to them or they
+ * control-c'd out.
+ */
+
+ if (ipc_action_pending(ifsp))
+ return (B_TRUE);
+
+ /*
+ * it appears they went away. try to cancel their pending
+ * command. if we can't cancel it, we leave their command
+ * pending and it's just gonna have to complete its business
+ * in any case, cancel the ipc_action timer, since we know
+ * they've gone away.
+ */
+
+ dhcpmsg(MSG_DEBUG, "async_pending: async command left, attempting "
+ "cancellation");
+
+ ipc_action_cancel_timer(ifsp);
+ return (async_cancel(ifsp) ? B_FALSE : B_TRUE);
+}
+
+/*
+ * async_start(): starts an asynchronous command on an interface
+ *
+ * input: struct ifslist *: the interface to start the async command on
+ * dhcp_ipc_type_t: the command to start
+ * boolean_t: B_TRUE if the command was started by a user
+ * output: int: 1 on success, 0 on failure
+ */
+
+int
+async_start(struct ifslist *ifsp, dhcp_ipc_type_t cmd, boolean_t user)
+{
+ iu_timer_id_t tid;
+
+ if (async_pending(ifsp))
+ return (0);
+
+ tid = iu_schedule_timer(tq, DHCP_ASYNC_WAIT, async_timeout, ifsp);
+ if (tid == -1)
+ return (0);
+
+ hold_ifs(ifsp);
+
+ ifsp->if_async.as_tid = tid;
+ ifsp->if_async.as_cmd = cmd;
+ ifsp->if_async.as_user = user;
+ ifsp->if_dflags |= DHCP_IF_BUSY;
+
+ return (1);
+}
+
+
+/*
+ * async_finish(): completes an asynchronous command
+ *
+ * input: struct ifslist *: the interface with the pending async command
+ * output: void
+ * note: should only be used when the command has no residual state to
+ * clean up
+ */
+
+void
+async_finish(struct ifslist *ifsp)
+{
+ /*
+ * be defensive here. the script may still be running if
+ * the asynchronous action times out before it is killed by the
+ * script helper process.
+ */
+
+ if (ifsp->if_script_pid != -1)
+ script_stop(ifsp);
+
+ /*
+ * in case async_timeout() has already called async_cancel(),
+ * and to be idempotent, check the DHCP_IF_BUSY flag
+ */
+
+ if (!(ifsp->if_dflags & DHCP_IF_BUSY))
+ return;
+
+ if (ifsp->if_async.as_tid == -1) {
+ ifsp->if_dflags &= ~DHCP_IF_BUSY;
+ return;
+ }
+
+ if (iu_cancel_timer(tq, ifsp->if_async.as_tid, NULL) == 1) {
+ ifsp->if_dflags &= ~DHCP_IF_BUSY;
+ ifsp->if_async.as_tid = -1;
+ (void) release_ifs(ifsp);
+ return;
+ }
+
+ /*
+ * if we can't cancel this timer, we'll just leave the
+ * interface busy and when the timeout finally fires, we'll
+ * mark it free, which will just cause a minor nuisance.
+ */
+
+ dhcpmsg(MSG_WARNING, "async_finish: cannot cancel async timer");
+}
+
+/*
+ * async_cancel(): cancels a pending asynchronous command
+ *
+ * input: struct ifslist *: the interface with the pending async command
+ * output: int: 1 if cancellation was successful, 0 on failure
+ */
+
+int
+async_cancel(struct ifslist *ifsp)
+{
+ boolean_t do_reset = B_FALSE;
+
+ /*
+ * we decide how to cancel the command depending on our
+ * current state, since commands such as EXTEND may in fact
+ * cause us to enter back into SELECTING (if a NAK results
+ * from the EXTEND).
+ */
+
+ switch (ifsp->if_state) {
+
+ case BOUND:
+ case INFORMATION:
+ break;
+
+ case RENEWING: /* FALLTHRU */
+ case REBINDING: /* FALLTHRU */
+ case INFORM_SENT:
+
+ /*
+ * these states imply that we've sent a packet and we're
+ * awaiting an ACK or NAK. just cancel the wait.
+ */
+
+ if (unregister_acknak(ifsp) == 0)
+ return (0);
+
+ break;
+
+ case INIT: /* FALLTHRU */
+ case SELECTING: /* FALLTHRU */
+ case REQUESTING: /* FALLTHRU */
+ case INIT_REBOOT:
+
+ /*
+ * these states imply we're still trying to get a lease.
+ * just return to a clean slate (INIT) -- but not until
+ * after we've finished the asynchronous command!
+ */
+
+ do_reset = B_TRUE;
+ break;
+
+ default:
+ dhcpmsg(MSG_WARNING, "async_cancel: cancellation in unexpected "
+ "state %d", ifsp->if_state);
+ return (0);
+ }
+
+ async_finish(ifsp);
+ dhcpmsg(MSG_DEBUG, "async_cancel: asynchronous command (%d) aborted",
+ ifsp->if_async.as_cmd);
+ if (do_reset)
+ reset_ifs(ifsp);
+
+ return (1);
+}
+
+/*
+ * async_timeout(): expires stale asynchronous commands
+ *
+ * input: iu_tq_t *: the timer queue on which the timeout went off
+ * void *: the interface with the pending async command
+ * output: void
+ */
+
+static void
+async_timeout(iu_tq_t *tq, void *arg)
+{
+ struct ifslist *ifsp = (struct ifslist *)arg;
+
+ if (check_ifs(ifsp) == 0) {
+ (void) release_ifs(ifsp);
+ return;
+ }
+
+ /* we've expired now */
+ ifsp->if_async.as_tid = -1;
+
+ /*
+ * if the command was generated internally to the agent, try
+ * to cancel it immediately. otherwise, if the user has gone
+ * away, we cancel it in async_pending(). otherwise, we let
+ * it live.
+ */
+
+ if (!ifsp->if_async.as_user) {
+ (void) async_cancel(ifsp);
+ return;
+ }
+
+ if (async_pending(ifsp)) {
+
+ ifsp->if_async.as_tid = iu_schedule_timer(tq, DHCP_ASYNC_WAIT,
+ async_timeout, ifsp);
+
+ if (ifsp->if_async.as_tid != -1) {
+ hold_ifs(ifsp);
+ dhcpmsg(MSG_DEBUG, "async_timeout: asynchronous "
+ "command %d still pending", ifsp->if_async.as_cmd);
+ return;
+ }
+
+ /*
+ * what can we do but cancel it? we can't get called
+ * back again and otherwise we'll end up in the
+ * twilight zone with the interface permanently busy
+ */
+
+ ipc_action_finish(ifsp, DHCP_IPC_E_INT);
+ (void) async_cancel(ifsp);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/async.h b/usr/src/cmd/cmd-inet/sbin/dhcpagent/async.h
new file mode 100644
index 0000000000..05c3d7d21a
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/async.h
@@ -0,0 +1,67 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef ASYNC_H
+#define ASYNC_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <libinetutil.h>
+#include <dhcpagent_ipc.h>
+
+/*
+ * async.[ch] comprise the interface used to handle asynchronous DHCP
+ * commands. see ipc_event() in agent.c for more documentation on
+ * the treatment of asynchronous DHCP commands. see async.c for
+ * documentation on how to use the exported functions.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ifslist; /* forward declaration */
+
+struct async_action {
+
+ dhcp_ipc_type_t as_cmd; /* command/action in progress */
+ iu_timer_id_t as_tid; /* async timer id */
+ boolean_t as_user; /* user-generated async cmd */
+};
+
+#define DHCP_ASYNC_WAIT 60 /* seconds */
+
+boolean_t async_pending(struct ifslist *);
+int async_start(struct ifslist *, dhcp_ipc_type_t, boolean_t);
+void async_finish(struct ifslist *);
+int async_cancel(struct ifslist *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ASYNC_H */
diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/bound.c b/usr/src/cmd/cmd-inet/sbin/dhcpagent/bound.c
new file mode 100644
index 0000000000..883383bb4a
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/bound.c
@@ -0,0 +1,567 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * BOUND state of the DHCP client state machine.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <sys/sockio.h>
+#include <unistd.h>
+#include <time.h>
+#include <arpa/inet.h>
+#include <stdlib.h>
+#include <sys/sysmacros.h>
+#include <dhcp_hostconf.h>
+#include <dhcpmsg.h>
+#include <stdio.h> /* snprintf */
+
+#include "defaults.h"
+#include "arp_check.h"
+#include "states.h"
+#include "packet.h"
+#include "util.h"
+#include "agent.h"
+#include "interface.h"
+#include "script_handler.h"
+
+#define IS_DHCP(plp) ((plp)->opts[CD_DHCP_TYPE] != NULL)
+
+static int configure_if(struct ifslist *);
+static int configure_timers(struct ifslist *);
+
+/*
+ * bound_event_cb(): callback for script_start on the event EVENT_BOUND
+ *
+ * input: struct ifslist *: the interface configured
+ * const char *: unused
+ * output: int: always 1
+ */
+
+/* ARGSUSED */
+static int
+bound_event_cb(struct ifslist *ifsp, const char *msg)
+{
+ ipc_action_finish(ifsp, DHCP_IPC_SUCCESS);
+ async_finish(ifsp);
+ return (1);
+}
+
+/*
+ * dhcp_bound(): configures an interface and ifs using information contained
+ * in the ACK packet and sets up lease timers. before starting,
+ * the requested address is arped to make sure it's not in use.
+ *
+ * input: struct ifslist *: the interface to move to bound
+ * PKT_LIST *: the ACK packet, or NULL if it should use ifsp->if_ack
+ * output: int: 0 on failure, 1 on success
+ */
+
+int
+dhcp_bound(struct ifslist *ifsp, PKT_LIST *ack)
+{
+ lease_t cur_lease, new_lease;
+ int msg_level;
+
+ if (ack != NULL) {
+ /* If ack we're replacing is not the original, then free it */
+ if (ifsp->if_ack != ifsp->if_orig_ack)
+ free_pkt_list(&ifsp->if_ack);
+ ifsp->if_ack = ack;
+ /* Save the first ack as the original */
+ if (ifsp->if_orig_ack == NULL)
+ ifsp->if_orig_ack = ack;
+ }
+
+ switch (ifsp->if_state) {
+
+ case ADOPTING:
+
+ /*
+ * if we're adopting an interface, the lease timers
+ * only provide an upper bound since we don't know
+ * from what time they are relative to. assume we
+ * have a lease time of at most DHCP_ADOPT_LEASE_MAX.
+ */
+
+ if (!IS_DHCP(ifsp->if_ack))
+ return (0);
+
+ (void) memcpy(&new_lease,
+ ifsp->if_ack->opts[CD_LEASE_TIME]->value, sizeof (lease_t));
+
+ new_lease = htonl(MIN(ntohl(new_lease), DHCP_ADOPT_LEASE_MAX));
+
+ (void) memcpy(ifsp->if_ack->opts[CD_LEASE_TIME]->value,
+ &new_lease, sizeof (lease_t));
+
+ /*
+ * we have no idea when the REQUEST that generated
+ * this ACK was sent, but for diagnostic purposes
+ * we'll assume its close to the current time.
+ */
+
+ ifsp->if_newstart_monosec = monosec();
+
+ /* FALLTHRU into REQUESTING/INIT_REBOOT */
+
+ case REQUESTING:
+ case INIT_REBOOT:
+
+ if (configure_if(ifsp) == 0)
+ return (0);
+
+ if (configure_timers(ifsp) == 0)
+ return (0);
+
+ /*
+ * if the state is ADOPTING, event loop has not been started
+ * at this time; so don''t run the script.
+ */
+
+ if (ifsp->if_state != ADOPTING) {
+ (void) script_start(ifsp, EVENT_BOUND, bound_event_cb,
+ NULL, NULL);
+ }
+
+ break;
+
+ case RENEWING:
+ case REBINDING:
+ case BOUND:
+
+ cur_lease = ifsp->if_lease;
+ if (configure_timers(ifsp) == 0)
+ return (0);
+
+ /*
+ * if the current lease is mysteriously close
+ * to the new lease, warn the user...
+ */
+
+ if (abs((ifsp->if_newstart_monosec + ifsp->if_lease) -
+ (ifsp->if_curstart_monosec + cur_lease)) < DHCP_LEASE_EPS) {
+
+ if (ifsp->if_lease < DHCP_LEASE_ERROR_THRESH)
+ msg_level = MSG_ERROR;
+ else
+ msg_level = MSG_VERBOSE;
+
+ dhcpmsg(msg_level, "lease renewed but lease time not "
+ "extended (expires in %d seconds)", ifsp->if_lease);
+ }
+
+
+ (void) script_start(ifsp, EVENT_EXTEND, bound_event_cb,
+ NULL, NULL);
+
+ break;
+
+ case INFORM_SENT:
+
+ (void) bound_event_cb(ifsp, NULL);
+ ifsp->if_state = INFORMATION;
+ break;
+
+ default:
+ /* something is really bizarre... */
+ dhcpmsg(MSG_DEBUG, "dhcp_bound: called in unexpected state");
+ return (0);
+ }
+
+ if (ifsp->if_state != INFORMATION) {
+ ifsp->if_state = BOUND;
+ ifsp->if_curstart_monosec = ifsp->if_newstart_monosec;
+ }
+
+ /*
+ * remove any stale hostconf file that might be lying around for
+ * this interface. (in general, it's harmless, since we'll write a
+ * fresh one when we exit anyway, but just to reduce confusion..)
+ */
+
+ (void) remove_hostconf(ifsp->if_name);
+ return (1);
+}
+
+/*
+ * configure_timers(): configures the lease timers on an interface
+ *
+ * input: struct ifslist *: the interface to configure (with a valid if_ack)
+ * output: int: 1 on success, 0 on failure
+ */
+
+int
+configure_timers(struct ifslist *ifsp)
+{
+ lease_t lease, t1, t2;
+
+ if (ifsp->if_ack->opts[CD_DHCP_TYPE] != NULL &&
+ (ifsp->if_ack->opts[CD_LEASE_TIME] == NULL ||
+ ifsp->if_ack->opts[CD_LEASE_TIME]->len != sizeof (lease_t))) {
+ send_decline(ifsp, "Missing or corrupted lease time",
+ &ifsp->if_ack->pkt->yiaddr);
+ dhcpmsg(MSG_WARNING, "configure_timers: missing or corrupted "
+ "lease time in ACK on %s", ifsp->if_name);
+ return (0);
+ }
+
+ cancel_ifs_timers(ifsp);
+
+ /*
+ * type has already been verified as ACK. if type is not set,
+ * then we got a BOOTP packet. we now fetch the t1, t2, and
+ * lease options out of the packet into variables. they are
+ * returned as relative host-byte-ordered times.
+ */
+
+ get_pkt_times(ifsp->if_ack, &lease, &t1, &t2);
+
+ ifsp->if_t1 = t1;
+ ifsp->if_t2 = t2;
+ ifsp->if_lease = lease;
+
+ if (ifsp->if_lease == DHCP_PERM) {
+ dhcpmsg(MSG_INFO, "%s acquired permanent lease", ifsp->if_name);
+ return (1);
+ }
+
+ dhcpmsg(MSG_INFO, "%s acquired lease, expires %s", ifsp->if_name,
+ monosec_to_string(ifsp->if_newstart_monosec + ifsp->if_lease));
+
+ dhcpmsg(MSG_INFO, "%s begins renewal at %s", ifsp->if_name,
+ monosec_to_string(ifsp->if_newstart_monosec + ifsp->if_t1));
+
+ dhcpmsg(MSG_INFO, "%s begins rebinding at %s", ifsp->if_name,
+ monosec_to_string(ifsp->if_newstart_monosec + ifsp->if_t2));
+
+ /*
+ * according to RFC2131, there is no minimum lease time, but don't
+ * set up renew/rebind timers if lease is shorter than DHCP_REBIND_MIN.
+ */
+
+ if (schedule_ifs_timer(ifsp, DHCP_LEASE_TIMER, lease, dhcp_expire) == 0)
+ goto failure;
+
+ if (lease < DHCP_REBIND_MIN) {
+ dhcpmsg(MSG_WARNING, "dhcp_bound: lease on %s is for "
+ "less than %d seconds!", ifsp->if_name, DHCP_REBIND_MIN);
+ return (1);
+ }
+
+ if (schedule_ifs_timer(ifsp, DHCP_T1_TIMER, t1, dhcp_renew) == 0)
+ goto failure;
+
+ if (schedule_ifs_timer(ifsp, DHCP_T2_TIMER, t2, dhcp_rebind) == 0)
+ goto failure;
+
+ return (1);
+
+failure:
+ cancel_ifs_timers(ifsp);
+ dhcpmsg(MSG_WARNING, "dhcp_bound: cannot schedule lease timers");
+ return (0);
+}
+
+/*
+ * configure_if(): configures an interface with DHCP parameters from an ACK
+ *
+ * input: struct ifslist *: the interface to configure (with a valid if_ack)
+ * output: int: 1 on success, 0 on failure
+ */
+
+static int
+configure_if(struct ifslist *ifsp)
+{
+ struct ifreq ifr;
+ struct sockaddr_in *sin;
+ PKT_LIST *ack = ifsp->if_ack;
+ DHCP_OPT *router_list;
+ uchar_t *target_hwaddr;
+ int i;
+ char in_use[256] = "IP address already in use by";
+
+ /*
+ * if we're using DHCP, then we'll have a valid CD_SERVER_ID
+ * (we checked in dhcp_acknak()); set it now so that
+ * ifsp->if_server is valid in case we need to send_decline().
+ * note that we use comparisons against opts[CD_DHCP_TYPE]
+ * since we haven't set DHCP_IF_BOOTP yet (we don't do that
+ * until we're sure we want the offered address.)
+ */
+
+ if (ifsp->if_ack->opts[CD_DHCP_TYPE] != NULL)
+ (void) memcpy(&ifsp->if_server.s_addr,
+ ack->opts[CD_SERVER_ID]->value, sizeof (ipaddr_t));
+
+ /* no big deal if this fails; we'll just have less diagnostics */
+ target_hwaddr = malloc(ifsp->if_hwlen);
+
+ if (arp_check(ifsp, 0, ack->pkt->yiaddr.s_addr, target_hwaddr,
+ ifsp->if_hwlen, df_get_int(ifsp->if_name, DF_ARP_WAIT)) == 1) {
+
+ for (i = 0; i < ifsp->if_hwlen; i++)
+ (void) snprintf(in_use, sizeof (in_use), "%s %02x",
+ in_use, target_hwaddr[i]);
+
+ dhcpmsg(MSG_ERROR, in_use);
+
+ if (ifsp->if_ack->opts[CD_DHCP_TYPE] != NULL)
+ send_decline(ifsp, in_use, &ack->pkt->yiaddr);
+
+ ifsp->if_bad_offers++;
+ free(target_hwaddr);
+ return (0);
+ }
+ free(target_hwaddr);
+
+ ifsp->if_addr.s_addr = ack->pkt->yiaddr.s_addr;
+ if (ifsp->if_addr.s_addr == htonl(INADDR_ANY)) {
+ dhcpmsg(MSG_ERROR, "configure_if: got invalid IP address");
+ return (0);
+ }
+
+ (void) memset(&ifr, 0, sizeof (struct ifreq));
+ (void) strlcpy(ifr.ifr_name, ifsp->if_name, IFNAMSIZ);
+
+ /*
+ * bring the interface online. note that there is no optimal
+ * order here: it is considered bad taste (and in > solaris 7,
+ * likely illegal) to bring an interface up before it has an
+ * ip address. however, due to an apparent bug in sun fddi
+ * 5.0, fddi will not obtain a network routing entry unless
+ * the interface is brought up before it has an ip address.
+ * we take the lesser of the two evils; if fddi customers have
+ * problems, they can get a newer fddi distribution which
+ * fixes the problem.
+ */
+
+ /* LINTED [ifr_addr is a sockaddr which will be aligned] */
+ sin = (struct sockaddr_in *)&ifr.ifr_addr;
+ sin->sin_family = AF_INET;
+
+ if (ack->opts[CD_SUBNETMASK] != NULL &&
+ ack->opts[CD_SUBNETMASK]->len == sizeof (ipaddr_t)) {
+
+ (void) memcpy(&ifsp->if_netmask.s_addr,
+ ack->opts[CD_SUBNETMASK]->value, sizeof (ipaddr_t));
+
+ } else {
+
+ if (ack->opts[CD_SUBNETMASK] != NULL &&
+ ack->opts[CD_SUBNETMASK]->len != sizeof (ipaddr_t))
+ dhcpmsg(MSG_WARNING, "configure_if: specified subnet "
+ "mask length is %d instead of %d, ignoring",
+ ack->opts[CD_SUBNETMASK]->len, sizeof (ipaddr_t));
+
+ /*
+ * no legitimate IP subnet mask specified.. use best
+ * guess. recall that if_addr is in network order, so
+ * imagine it's 0x11223344: then when it is read into
+ * a register on x86, it becomes 0x44332211, so we
+ * must ntohl() it to convert it to 0x11223344 in
+ * order to use the macros in <netinet/in.h>.
+ */
+
+ if (IN_CLASSA(ntohl(ifsp->if_addr.s_addr)))
+ ifsp->if_netmask.s_addr = htonl(IN_CLASSA_NET);
+ else if (IN_CLASSB(ntohl(ifsp->if_addr.s_addr)))
+ ifsp->if_netmask.s_addr = htonl(IN_CLASSB_NET);
+ else if (IN_CLASSC(ntohl(ifsp->if_addr.s_addr)))
+ ifsp->if_netmask.s_addr = htonl(IN_CLASSC_NET);
+ else /* must be class d */
+ ifsp->if_netmask.s_addr = htonl(IN_CLASSD_NET);
+
+ dhcpmsg(MSG_WARNING, "configure_if: no IP netmask specified "
+ "for %s, making best guess", ifsp->if_name);
+ }
+
+ dhcpmsg(MSG_INFO, "setting IP netmask to %s on %s",
+ inet_ntoa(ifsp->if_netmask), ifsp->if_name);
+
+ sin->sin_addr = ifsp->if_netmask;
+ if (ioctl(ifsp->if_sock_fd, SIOCSIFNETMASK, &ifr) == -1) {
+ dhcpmsg(MSG_ERR, "cannot set IP netmask on %s", ifsp->if_name);
+ return (0);
+ }
+
+ dhcpmsg(MSG_INFO, "setting IP address to %s on %s",
+ inet_ntoa(ifsp->if_addr), ifsp->if_name);
+
+ sin->sin_addr = ifsp->if_addr;
+ if (ioctl(ifsp->if_sock_fd, SIOCSIFADDR, &ifr) == -1) {
+ dhcpmsg(MSG_ERR, "configure_if: cannot set IP address on %s",
+ ifsp->if_name);
+ return (0);
+ }
+
+ if (ack->opts[CD_BROADCASTADDR] != NULL &&
+ ack->opts[CD_BROADCASTADDR]->len == sizeof (ipaddr_t)) {
+
+ (void) memcpy(&ifsp->if_broadcast.s_addr,
+ ack->opts[CD_BROADCASTADDR]->value, sizeof (ipaddr_t));
+
+ } else {
+
+ if (ack->opts[CD_BROADCASTADDR] != NULL &&
+ ack->opts[CD_BROADCASTADDR]->len != sizeof (ipaddr_t))
+ dhcpmsg(MSG_WARNING, "configure_if: specified "
+ "broadcast address length is %d instead of %d, "
+ "ignoring", ack->opts[CD_BROADCASTADDR]->len,
+ sizeof (ipaddr_t));
+
+ /*
+ * no legitimate IP broadcast specified. compute it
+ * from the IP address and netmask.
+ */
+
+ ifsp->if_broadcast.s_addr = ifsp->if_addr.s_addr &
+ ifsp->if_netmask.s_addr | ~ifsp->if_netmask.s_addr;
+
+ dhcpmsg(MSG_WARNING, "configure_if: no IP broadcast specified "
+ "for %s, making best guess", ifsp->if_name);
+ }
+
+ if (ioctl(ifsp->if_sock_fd, SIOCGIFFLAGS, &ifr) == -1) {
+ dhcpmsg(MSG_ERR, "configure_if: cannot get interface flags for "
+ "%s", ifsp->if_name);
+ return (0);
+ }
+
+ ifr.ifr_flags |= IFF_UP;
+
+ if (ioctl(ifsp->if_sock_fd, SIOCSIFFLAGS, &ifr) == -1) {
+ dhcpmsg(MSG_ERR, "configure_if: cannot set interface flags for "
+ "%s", ifsp->if_name);
+ return (0);
+ }
+
+ /*
+ * the kernel will set the broadcast address for us as part of
+ * bringing the interface up. since experience has shown that dhcp
+ * servers sometimes provide a bogus broadcast address, we let the
+ * kernel set it so that it's guaranteed to be correct.
+ *
+ * also, note any inconsistencies and save the broadcast address the
+ * kernel set so that we can watch for changes to it.
+ */
+
+ if (ioctl(ifsp->if_sock_fd, SIOCGIFBRDADDR, &ifr) == -1) {
+ dhcpmsg(MSG_ERR, "configure_if: cannot get broadcast address "
+ "for %s", ifsp->if_name);
+ return (0);
+ }
+
+ if (ifsp->if_broadcast.s_addr != sin->sin_addr.s_addr) {
+ dhcpmsg(MSG_WARNING, "incorrect broadcast address %s specified "
+ "for %s; ignoring", inet_ntoa(ifsp->if_broadcast),
+ ifsp->if_name);
+ }
+
+ ifsp->if_broadcast = sin->sin_addr;
+ dhcpmsg(MSG_INFO, "using broadcast address %s on %s",
+ inet_ntoa(ifsp->if_broadcast), ifsp->if_name);
+
+ /*
+ * add each provided router; we'll clean them up when the
+ * interface goes away or when our lease expires.
+ */
+
+ router_list = ack->opts[CD_ROUTER];
+ if (router_list && (router_list->len % sizeof (ipaddr_t)) == 0) {
+
+ ifsp->if_nrouters = router_list->len / sizeof (ipaddr_t);
+ ifsp->if_routers = malloc(router_list->len);
+ if (ifsp->if_routers == NULL) {
+ dhcpmsg(MSG_ERR, "configure_if: cannot allocate "
+ "default router list, ignoring default routers");
+ ifsp->if_nrouters = 0;
+ }
+
+ for (i = 0; i < ifsp->if_nrouters; i++) {
+
+ (void) memcpy(&ifsp->if_routers[i].s_addr,
+ router_list->value + (i * sizeof (ipaddr_t)),
+ sizeof (ipaddr_t));
+
+ if (add_default_route(ifsp->if_name,
+ &ifsp->if_routers[i]) == 0) {
+ dhcpmsg(MSG_ERR, "configure_if: cannot add "
+ "default router %s on %s", inet_ntoa(
+ ifsp->if_routers[i]), ifsp->if_name);
+ ifsp->if_routers[i].s_addr = htonl(INADDR_ANY);
+ continue;
+ }
+
+ dhcpmsg(MSG_INFO, "added default router %s on %s",
+ inet_ntoa(ifsp->if_routers[i]), ifsp->if_name);
+ }
+ }
+
+ ifsp->if_sock_ip_fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (ifsp->if_sock_ip_fd == -1) {
+ dhcpmsg(MSG_ERR, "configure_if: cannot create socket on %s",
+ ifsp->if_name);
+ return (0);
+ }
+
+ if (bind_sock(ifsp->if_sock_ip_fd, IPPORT_BOOTPC,
+ ntohl(ifsp->if_addr.s_addr)) == 0) {
+ dhcpmsg(MSG_ERR, "configure_if: cannot bind socket on %s",
+ ifsp->if_name);
+ return (0);
+ }
+
+ /*
+ * we wait until here to bind if_sock_fd because it turns out
+ * the kernel has difficulties doing binds before interfaces
+ * are up (although it may work sometimes, it doesn't work all
+ * the time.) that's okay, because we don't use if_sock_fd
+ * for receiving data until we're BOUND anyway.
+ */
+
+ if (bind_sock(ifsp->if_sock_fd, IPPORT_BOOTPC, INADDR_BROADCAST) == 0) {
+ dhcpmsg(MSG_ERR, "configure_if: cannot bind broadcast socket "
+ "on %s", ifsp->if_name);
+ return (0);
+ }
+
+ /*
+ * we'll be using if_sock_fd for the remainder of the lease;
+ * blackhole if_dlpi_fd.
+ */
+
+ set_packet_filter(ifsp->if_dlpi_fd, blackhole_filter, 0, "blackhole");
+
+ if (ack->opts[CD_DHCP_TYPE] == NULL)
+ ifsp->if_dflags |= DHCP_IF_BOOTP;
+
+ dhcpmsg(MSG_DEBUG, "configure_if: bound ifsp->if_sock_ip_fd");
+ return (1);
+}
diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/class_id.c b/usr/src/cmd/cmd-inet/sbin/dhcpagent/class_id.c
new file mode 100644
index 0000000000..3e5a533466
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/class_id.c
@@ -0,0 +1,205 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <sys/openpromio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h> /* sprintf() */
+#include <unistd.h>
+
+/*
+ * opp_zalloc(): allocates and initializes a struct openpromio
+ *
+ * input: size_t: the size of the variable-length part of the openpromio
+ * const char *: an initial value for oprom_array, if non-NULL
+ * output: struct openpromio: the allocated, initialized openpromio
+ */
+
+static struct openpromio *
+opp_zalloc(size_t size, const char *prop)
+{
+ struct openpromio *opp = malloc(sizeof (struct openpromio) + size);
+
+ if (opp != NULL) {
+ (void) memset(opp, 0, sizeof (struct openpromio) + size);
+ opp->oprom_size = size;
+ if (prop != NULL)
+ (void) strcpy(opp->oprom_array, prop);
+ }
+ return (opp);
+}
+
+/*
+ * goto_rootnode(): moves to the root of the devinfo tree
+ *
+ * input: int: an open descriptor to /dev/openprom
+ * output: int: nonzero on success
+ */
+
+static int
+goto_rootnode(int prom_fd)
+{
+ struct openpromio op = { sizeof (int), 0 };
+
+ /* zero it explicitly since a union is involved */
+ op.oprom_node = 0;
+ return (ioctl(prom_fd, OPROMNEXT, &op) == 0);
+}
+
+/*
+ * return_property(): returns the value of a given property
+ *
+ * input: int: an open descriptor to /dev/openprom
+ * const char *: the property to look for in the current devinfo node
+ * output: the value of that property (dynamically allocated)
+ */
+
+static char *
+return_property(int prom_fd, const char *prop)
+{
+ int proplen;
+ char *result;
+ struct openpromio *opp = opp_zalloc(strlen(prop) + 1, prop);
+
+ if (opp == NULL)
+ return (NULL);
+
+ if (ioctl(prom_fd, OPROMGETPROPLEN, opp) == -1) {
+ free(opp);
+ return (NULL);
+ }
+
+ proplen = opp->oprom_len;
+ if (proplen > (strlen(prop) + 1)) {
+ free(opp);
+ opp = opp_zalloc(proplen, prop);
+ if (opp == NULL)
+ return (NULL);
+ }
+
+ if (ioctl(prom_fd, OPROMGETPROP, opp) == -1) {
+ free(opp);
+ return (NULL);
+ }
+
+ result = strdup(opp->oprom_array);
+ free(opp);
+ return (result);
+}
+
+/*
+ * sanitize_class_id(): translates the class id into a canonical format,
+ * so that it can be used easily with dhcptab(4).
+ *
+ * input: char *: the class id to canonicalize
+ * output: void
+ */
+
+static void
+sanitize_class_id(char *src_ptr)
+{
+ char *dst_ptr = src_ptr;
+
+ /* remove all spaces and change all commas to periods */
+ while (*src_ptr != '\0') {
+
+ switch (*src_ptr) {
+
+ case ' ':
+ break;
+
+ case ',':
+ *dst_ptr++ = '.';
+ break;
+
+ default:
+ *dst_ptr++ = *src_ptr;
+ break;
+ }
+ src_ptr++;
+ }
+ *dst_ptr = '\0';
+}
+
+/*
+ * get_class_id(): retrieves the class id from the prom, then canonicalizes it
+ *
+ * input: void
+ * output: char *: the class id (dynamically allocated and sanitized)
+ */
+
+char *
+get_class_id(void)
+{
+ int prom_fd;
+ char *name, *class_id = NULL;
+ size_t len;
+
+ prom_fd = open("/dev/openprom", O_RDONLY);
+ if (prom_fd == -1)
+ return (NULL);
+
+ if (goto_rootnode(prom_fd) == 0) {
+ (void) close(prom_fd);
+ return (NULL);
+ }
+
+ /*
+ * the `name' property is the same as the result of `uname -i', modulo
+ * some stylistic issues we fix up via sanitize_class_id() below.
+ */
+
+ name = return_property(prom_fd, "name");
+ (void) close(prom_fd);
+ if (name == NULL)
+ return (NULL);
+
+ /*
+ * if the name is not prefixed with a vendor name, add "SUNW," to make
+ * it more likely to be globally unique; see PSARC/2004/674.
+ */
+
+ if (strchr(name, ',') == NULL) {
+ len = strlen(name) + sizeof ("SUNW,");
+ class_id = malloc(len);
+ if (class_id == NULL) {
+ free(name);
+ return (NULL);
+ }
+ (void) snprintf(class_id, len, "SUNW,%s", name);
+ free(name);
+ } else {
+ class_id = name;
+ }
+
+ sanitize_class_id(class_id);
+ return (class_id);
+}
diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/class_id.h b/usr/src/cmd/cmd-inet/sbin/dhcpagent/class_id.h
new file mode 100644
index 0000000000..d827fdc588
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/class_id.h
@@ -0,0 +1,48 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef CLASS_ID_H
+#define CLASS_ID_H
+
+#pragma ident "%W% %E% SMI"
+
+/*
+ * class_id.[ch] provides an interface for retrieving the class id
+ * from the prom. see class_id.c for more details on how to use the
+ * exported function.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+char *get_class_id(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CLASS_ID_H */
diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/defaults.c b/usr/src/cmd/cmd-inet/sbin/dhcpagent/defaults.c
new file mode 100644
index 0000000000..6edf5d6da5
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/defaults.c
@@ -0,0 +1,307 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <netinet/inetutil.h>
+#include <netinet/dhcp.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <dhcpmsg.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <libnvpair.h>
+
+#include "defaults.h"
+
+struct dhcp_default {
+
+ const char *df_name; /* parameter name */
+ const char *df_default; /* default value */
+ int df_min; /* min value if type DF_INTEGER */
+ int df_max; /* max value if type DF_INTEGER */
+};
+
+/*
+ * note: keep in the same order as tunable parameter constants in defaults.h
+ */
+
+static struct dhcp_default defaults[] = {
+
+ { "RELEASE_ON_SIGTERM", "0", 0, 0 },
+ { "IGNORE_FAILED_ARP", "1", 0, 0 },
+ { "OFFER_WAIT", "3", 1, 20 },
+ { "ARP_WAIT", "1000", 100, 4000 },
+ { "CLIENT_ID", NULL, 0, 0 },
+ { "PARAM_REQUEST_LIST", NULL, 0, 0 },
+ { "REQUEST_HOSTNAME", "1", 0, 0 }
+};
+
+/*
+ * df_build_cache(): builds the defaults nvlist cache
+ *
+ * input: void
+ * output: a pointer to an nvlist of the current defaults, or NULL on failure
+ */
+
+static nvlist_t *
+df_build_cache(void)
+{
+ char entry[1024];
+ int i;
+ char *param, *value, *end;
+ FILE *fp;
+ nvlist_t *nvlist;
+
+ if ((fp = fopen(DHCP_AGENT_DEFAULTS, "r")) == NULL)
+ return (NULL);
+
+ if (nvlist_alloc(&nvlist, NV_UNIQUE_NAME, 0) != 0) {
+ dhcpmsg(MSG_WARNING, "cannot build default value cache; "
+ "using built-in defaults");
+ (void) fclose(fp);
+ return (NULL);
+ }
+
+ while (fgets(entry, sizeof (entry), fp) != NULL) {
+ for (i = 0; entry[i] == ' '; i++)
+ ;
+
+ end = strrchr(entry, '\n');
+ value = strchr(entry, '=');
+ if (end == NULL || value == NULL || entry[i] == '#')
+ continue;
+
+ *end = '\0';
+ *value++ = '\0';
+
+ /*
+ * to be compatible with the old defread()-based code
+ * which ignored case, store the parameters (except for the
+ * leading interface name) in upper case.
+ */
+
+ if ((param = strchr(entry, '.')) == NULL)
+ param = entry;
+ else
+ param++;
+
+ for (; *param != '\0'; param++)
+ *param = toupper(*param);
+
+ if (nvlist_add_string(nvlist, &entry[i], value) != 0) {
+ dhcpmsg(MSG_WARNING, "cannot build default value cache;"
+ " using built-in defaults");
+ nvlist_free(nvlist);
+ nvlist = NULL;
+ break;
+ }
+ }
+
+ (void) fclose(fp);
+ return (nvlist);
+}
+
+/*
+ * df_get_string(): gets the string value of a given user-tunable parameter
+ *
+ * input: const char *: the interface the parameter applies to
+ * unsigned int: the parameter number to look up
+ * output: const char *: the parameter's value, or default if not set
+ * (must be copied by caller to be kept)
+ * NOTE: df_get_string() is both used by functions outside this source
+ * file to retrieve strings from the defaults file, *and*
+ * internally by other df_get_*() functions.
+ */
+
+const char *
+df_get_string(const char *if_name, unsigned int p)
+{
+ char *value;
+ char param[256];
+ struct stat statbuf;
+ static struct stat df_statbuf;
+ static boolean_t df_unavail_msg = B_FALSE;
+ static nvlist_t *df_nvlist = NULL;
+
+ if (p >= (sizeof (defaults) / sizeof (*defaults)))
+ return (NULL);
+
+ if (stat(DHCP_AGENT_DEFAULTS, &statbuf) != 0) {
+ if (!df_unavail_msg) {
+ dhcpmsg(MSG_WARNING, "cannot access %s; using "
+ "built-in defaults", DHCP_AGENT_DEFAULTS);
+ df_unavail_msg = B_TRUE;
+ }
+ return (defaults[p].df_default);
+ }
+
+ /*
+ * if our cached parameters are stale, rebuild.
+ */
+
+ if (statbuf.st_mtime != df_statbuf.st_mtime ||
+ statbuf.st_size != df_statbuf.st_size) {
+ df_statbuf = statbuf;
+ if (df_nvlist != NULL)
+ nvlist_free(df_nvlist);
+ df_nvlist = df_build_cache();
+ }
+
+ (void) snprintf(param, sizeof (param), "%s.%s", if_name,
+ defaults[p].df_name);
+
+ /*
+ * first look for `if_name.param', then `param'. if neither
+ * has been set, use the built-in default.
+ */
+
+ if (nvlist_lookup_string(df_nvlist, param, &value) == 0 ||
+ nvlist_lookup_string(df_nvlist, defaults[p].df_name, &value) == 0)
+ return (value);
+
+ return (defaults[p].df_default);
+}
+
+/*
+ * df_get_octet(): gets the integer value of a given user-tunable parameter
+ *
+ * input: const char *: the interface the parameter applies to
+ * unsigned int: the parameter number to look up
+ * unsigned int *: the length of the returned value
+ * output: uchar_t *: a pointer to byte array (default value if not set)
+ * (must be copied by caller to be kept)
+ */
+
+uchar_t *
+df_get_octet(const char *if_name, unsigned int p, unsigned int *len)
+{
+ const char *value;
+ static uchar_t octet_value[256]; /* as big as defread() returns */
+
+ if (p >= (sizeof (defaults) / sizeof (*defaults)))
+ return (NULL);
+
+ value = df_get_string(if_name, p);
+ if (value == NULL)
+ goto do_default;
+
+ if (strncasecmp("0x", value, 2) != 0) {
+ *len = strlen(value); /* no NUL */
+ return ((uchar_t *)value);
+ }
+
+ /* skip past the 0x and convert the value to binary */
+ value += 2;
+ *len = sizeof (octet_value);
+ if (hexascii_to_octet(value, strlen(value), octet_value, len) != 0) {
+ dhcpmsg(MSG_WARNING, "df_get_octet: cannot convert value "
+ "for parameter `%s', using default", defaults[p].df_name);
+ goto do_default;
+ }
+ return (octet_value);
+
+do_default:
+ if (defaults[p].df_default == NULL) {
+ *len = 0;
+ return (NULL);
+ }
+
+ *len = strlen(defaults[p].df_default); /* no NUL */
+ return ((uchar_t *)defaults[p].df_default);
+}
+
+/*
+ * df_get_int(): gets the integer value of a given user-tunable parameter
+ *
+ * input: const char *: the interface the parameter applies to
+ * unsigned int: the parameter number to look up
+ * output: int: the parameter's value, or default if not set
+ */
+
+int
+df_get_int(const char *if_name, unsigned int p)
+{
+ const char *value;
+ int value_int;
+
+ if (p >= (sizeof (defaults) / sizeof (*defaults)))
+ return (0);
+
+ value = df_get_string(if_name, p);
+ if (value == NULL || !isdigit(*value))
+ goto failure;
+
+ value_int = atoi(value);
+ if (value_int > defaults[p].df_max || value_int < defaults[p].df_min)
+ goto failure;
+
+ return (value_int);
+
+failure:
+ dhcpmsg(MSG_WARNING, "df_get_int: parameter `%s' is not between %d and "
+ "%d, defaulting to `%s'", defaults[p].df_name, defaults[p].df_min,
+ defaults[p].df_max, defaults[p].df_default);
+ return (atoi(defaults[p].df_default));
+}
+
+/*
+ * df_get_bool(): gets the boolean value of a given user-tunable parameter
+ *
+ * input: const char *: the interface the parameter applies to
+ * unsigned int: the parameter number to look up
+ * output: boolean_t: B_TRUE if true, B_FALSE if false, default if not set
+ */
+
+boolean_t
+df_get_bool(const char *if_name, unsigned int p)
+{
+ const char *value;
+
+ if (p >= (sizeof (defaults) / sizeof (*defaults)))
+ return (0);
+
+ value = df_get_string(if_name, p);
+ if (value != NULL) {
+
+ if (strcasecmp(value, "true") == 0 ||
+ strcasecmp(value, "yes") == 0 || strcmp(value, "1") == 0)
+ return (B_TRUE);
+
+ if (strcasecmp(value, "false") == 0 ||
+ strcasecmp(value, "no") == 0 || strcmp(value, "0") == 0)
+ return (B_FALSE);
+ }
+
+ dhcpmsg(MSG_WARNING, "df_get_bool: parameter `%s' has invalid value "
+ "`%s', defaulting to `%s'", defaults[p].df_name,
+ value ? value : "NULL", defaults[p].df_default);
+
+ return ((atoi(defaults[p].df_default) == 0) ? B_FALSE : B_TRUE);
+}
diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/defaults.h b/usr/src/cmd/cmd-inet/sbin/dhcpagent/defaults.h
new file mode 100644
index 0000000000..4d58c2072d
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/defaults.h
@@ -0,0 +1,70 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef DEFAULTS_H
+#define DEFAULTS_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+
+/*
+ * defaults.[ch] encapsulate the agent's interface to the dhcpagent
+ * defaults file. see defaults.c for documentation on how to use the
+ * exported functions.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * tunable parameters -- keep in the same order as defaults[] in defaults.c
+ */
+
+enum {
+
+ DF_RELEASE_ON_SIGTERM, /* send RELEASE on each if upon SIGTERM */
+ DF_IGNORE_FAILED_ARP, /* what to do if agent can't ARP */
+ DF_OFFER_WAIT, /* how long to wait to collect offers */
+ DF_ARP_WAIT, /* how long to wait for an ARP reply */
+ DF_CLIENT_ID, /* our client id */
+ DF_PARAM_REQUEST_LIST, /* our parameter request list */
+ DF_REQUEST_HOSTNAME /* request hostname associated with interface */
+};
+
+#define DHCP_AGENT_DEFAULTS "/etc/default/dhcpagent"
+
+boolean_t df_get_bool(const char *, unsigned int);
+int df_get_int(const char *, unsigned int);
+const char *df_get_string(const char *, unsigned int);
+uchar_t *df_get_octet(const char *, unsigned int, unsigned int *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DEFAULTS_H */
diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/dhcpagent.dfl b/usr/src/cmd/cmd-inet/sbin/dhcpagent/dhcpagent.dfl
new file mode 100644
index 0000000000..4299f09136
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/dhcpagent.dfl
@@ -0,0 +1,101 @@
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# This file contains tunable parameters for dhcpagent(1M).
+#
+
+# All parameters can be tuned for a specific interface by prepending
+# the interface name to the parameter name. For example, to make
+# RELEASE_ON_SIGTERM happen on all interfaces except hme0, specify:
+#
+# hme0.RELEASE_ON_SIGTERM=no
+# RELEASE_ON_SIGTERM=yes
+
+# By default, when the DHCP agent is sent a SIGTERM, all managed
+# interfaces are dropped. By uncommenting the following
+# parameter-value pair, all managed interfaces are released instead.
+#
+# RELEASE_ON_SIGTERM=yes
+
+# When the DHCP agent gets an ACK from the server, it sends an ARP
+# request to verify that a given IP address is not already in use. If
+# an ARP reply is received, the DHCP agent declines the server's
+# offer. However, if the DHCP agent is unable to send the ARP request
+# packet for whatever reason, it assumes the address is available. To
+# be more cautious, uncomment the following parameter-value pair.
+#
+# IGNORE_FAILED_ARP=no
+
+# By default, the DHCP agent waits 3 seconds to collect OFFER
+# responses to a DISCOVER. If it receives no OFFERs in this time, it
+# then waits for another 3 seconds, and so forth. To change this
+# behavior, set and uncomment the following parameter-value pair.
+# Note: this does not control the retransmission strategy for
+# DISCOVERs, which is formally specified in RFC 2131. This parameter
+# is specified in seconds.
+#
+# OFFER_WAIT=
+
+# By default, the DHCP agent waits 1000 milliseconds to collect ARP
+# replies to an ARP request when verifying that an IP address is not
+# in use. To change this behavior, set and uncomment the following
+# parameter-value pair. This parameter is specified in milliseconds.
+#
+# ARP_WAIT=
+
+# By default, the DHCP agent does not send out a client identifier
+# (and hence, the chaddr field is used by the DHCP server as the
+# client identifier.) To make the DHCP agent send a client
+# identifier, set and uncomment the following parameter-value pair.
+# Note that by default this is treated as an NVT ASCII string. To
+# specify a binary value, prepend "0x" to a sequence of hexadecimal
+# digits (for example, the value 0xAABBCC11 would set the client
+# identifier to the 4-byte binary sequence 0xAA 0xBB 0xCC 0x11).
+#
+# CLIENT_ID=
+
+# By default, the DHCP agent will try to request the hostname currently
+# associated with the interface performing DHCP. If this option is
+# enabled, the agent will attempt to find a host name in /etc/hostname.<if>,
+# which must contain a line of the form
+#
+# inet name
+#
+# where "name" is a single RFC 1101-compliant token. If found, the token
+# will be used to request that host name from the DHCP server. To prevent
+# this, uncomment the following line.
+#
+# REQUEST_HOSTNAME=no
+
+# By default, a parameter request list requesting a subnet mask (1),
+# router (3), DNS server (6), hostname (12), DNS domain (15), broadcast
+# address (28), and encapsulated vendor options (43), is sent to the DHCP
+# server when the DHCP agent sends requests. However, if desired, this
+# can be changed by altering the following parameter-value pair. The
+# numbers correspond to the values defined in RFC 2132.
+#
+PARAM_REQUEST_LIST=1,3,6,12,15,28,43
diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/dhcpagent.xcl b/usr/src/cmd/cmd-inet/sbin/dhcpagent/dhcpagent.xcl
new file mode 100644
index 0000000000..58f30f85aa
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/dhcpagent.xcl
@@ -0,0 +1,55 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+msgid "vd:l:fa"
+msgid ""
+msgid "/"
+msgid "%s %02x"
+msgid "/dev/openprom"
+msgid "name"
+msgid "SUNW."
+msgid "SUNW.%s"
+msgid "RELEASE_ON_SIGTERM="
+msgid "IGNORE_FAILED_ARP="
+msgid "OFFER_WAIT="
+msgid "ARP_WAIT="
+msgid "CLIENT_ID="
+msgid "PARAM_REQUEST_LIST="
+msgid "%s:%s"
+msgid "0x"
+msgid "true"
+msgid "yes"
+msgid "false"
+msgid "no"
+msgid "NULL"
+msgid "/dev/"
+msgid "0123456789"
+msgid "pfmod"
+msgid "DHCP"
+msgid "BOOTP"
+msgid "DISCOVER"
+msgid "OFFER"
+msgid "REQUEST"
+msgid "DECLINE"
+msgid "ACK"
+msgid "NAK"
+msgid "RELEASE"
+msgid "INFORM"
diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/dlpi_io.c b/usr/src/cmd/cmd-inet/sbin/dhcpagent/dlpi_io.c
new file mode 100644
index 0000000000..2c74b6bc88
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/dlpi_io.c
@@ -0,0 +1,629 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/pfmod.h>
+#include <sys/socket.h>
+#include <net/if.h> /* IFNAMSIZ */
+#include <netinet/in_systm.h> /* n_long (ip.h) */
+#include <netinet/in.h> /* in_addr (ip.h) */
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+#include <stropts.h>
+#include <string.h> /* strpbrk */
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/uio.h>
+#include <sys/dlpi.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <dhcpmsg.h>
+#include <libinetutil.h>
+
+#include "agent.h"
+#include "dlprims.h"
+#include "dlpi_io.h"
+#include "interface.h"
+#include "v4_sum_impl.h"
+
+/*
+ * dlpi_open(): opens a DLPI stream to the given interface and returns
+ * information purpose about that interface.
+ *
+ * input: const char *: the name of the interface to open
+ * dl_info_ack_t *: a place to store information about the interface
+ * size_t: the size of dl_info_ack_t
+ * t_uscalar_t: the sap to bind to on this interface
+ * output: int: the open file descriptor on success, -1 on failure
+ */
+
+int
+dlpi_open(const char *if_name, dl_info_ack_t *dlia, size_t dlia_size,
+ t_uscalar_t dl_sap)
+{
+ char device_name[sizeof ("/dev/") + IFNAMSIZ];
+ int fd;
+ ifspec_t ifsp;
+ int is_style2 = 0;
+
+ if (!ifparse_ifspec(if_name, &ifsp)) {
+ dhcpmsg(MSG_ERROR, "dlpi_open: invalid interface name");
+ return (-1);
+ }
+
+ if (ifsp.ifsp_modcnt != 0) {
+ dhcpmsg(MSG_ERROR, "dlpi_open: modules cannot be specified "
+ "with an interface name");
+ return (-1);
+ }
+
+ /* try dlpi style1 interface first; if it fails, try style 2 */
+ (void) snprintf(device_name, sizeof (device_name),
+ "/dev/%s%d", ifsp.ifsp_devnm, ifsp.ifsp_ppa);
+ if ((fd = open(device_name, O_RDWR)) == -1) {
+ dhcpmsg(MSG_DEBUG, "dlpi_open: open on `%s'", device_name);
+
+ /* try style 2 interface */
+ (void) snprintf(device_name, sizeof (device_name),
+ "/dev/%s", ifsp.ifsp_devnm);
+ fd = open(device_name, O_RDWR);
+
+ /*
+ * temporary hack: if the style-2 open of the /dev link fails,
+ * try the corresponding /devices/pseudo path. this allows a
+ * diskless boot to succeed without necessarily pre-creating the
+ * /dev links, by taking advantage of devfs's ability to create
+ * /devices nodes for h/w devices on demand. this is to avoid
+ * the need to fiddle with packaging scripts to boot off a new
+ * NIC device. when /dev links are created on demand, this
+ * work-around may be removed.
+ */
+
+ {
+ const char prefix[] = "/devices/pseudo/clone@0:";
+ char path[sizeof (prefix) + IFNAMSIZ];
+ if (fd == -1 && errno == ENOENT) {
+ (void) snprintf(path, sizeof (path), "%s%s",
+ prefix, ifsp.ifsp_devnm);
+ fd = open(path, O_RDWR);
+ }
+ }
+
+ if (fd == -1) {
+ dhcpmsg(MSG_ERR, "dlpi_open: open on `%s'",
+ device_name);
+ return (-1);
+ }
+ is_style2 = 1;
+ }
+
+ /*
+ * okay, so we've got an open DLPI stream now. make sure that
+ * it's DL_VERSION_2, DL_STYLE2, and that it's connectionless.
+ * from there, attach to the appropriate ppa, bind to dl_sap,
+ * and get ready to roll.
+ */
+
+ if (dlinforeq(fd, dlia, dlia_size) != 0) {
+ dhcpmsg(MSG_ERR, "dlpi_open: DL_INFO_REQ on %s", device_name);
+ (void) close(fd);
+ return (-1);
+ }
+
+ if (dlia->dl_version != DL_VERSION_2) {
+ dhcpmsg(MSG_ERROR, "dlpi_open: %s is DLPI version %d, not 2",
+ device_name, dlia->dl_version);
+ (void) close(fd);
+ return (-1);
+ }
+
+ if (is_style2 && dlia->dl_provider_style != DL_STYLE2) {
+ dhcpmsg(MSG_ERROR, "dlpi_open: %s is DL_STYLE%d, not DL_STYLE2",
+ device_name, dlia->dl_provider_style);
+ (void) close(fd);
+ return (-1);
+ }
+
+ if ((dlia->dl_service_mode & DL_CLDLS) == 0) {
+ dhcpmsg(MSG_ERROR, "dlpi_open: %s is %#x, not DL_CLDLS, which "
+ "is not supported", device_name, dlia->dl_service_mode);
+ (void) close(fd);
+ return (-1);
+ }
+
+ if (is_style2 && dlattachreq(fd, ifsp.ifsp_ppa) == -1) {
+ dhcpmsg(MSG_ERR, "dlpi_open: DL_ATTACH_REQ on %s", device_name);
+ (void) close(fd);
+ return (-1);
+ }
+
+ if (dlbindreq(fd, dl_sap, 0, DL_CLDLS, 0) == -1) {
+ dhcpmsg(MSG_ERR, "dlpi_open: DL_BIND_REQ on %s", device_name);
+ (void) close(fd);
+ return (-1);
+ }
+
+ /*
+ * we call this again since some of the information obtained
+ * previously was not valid since we had not yet attached (in
+ * particular, our MAC address) (but we needed to check the
+ * STYLE before we did the attach)
+ */
+
+ if (dlinforeq(fd, dlia, dlia_size) != 0) {
+ dhcpmsg(MSG_ERR, "dlpi_open: DL_INFO_REQ on %s", device_name);
+ (void) close(fd);
+ return (-1);
+ }
+
+ if (ioctl(fd, I_PUSH, "pfmod") == -1) {
+ dhcpmsg(MSG_ERR, "dlpi_open: cannot push pfmod on stream");
+ (void) close(fd);
+ return (-1);
+ }
+
+ (void) ioctl(fd, I_FLUSH, FLUSHR);
+ return (fd);
+}
+
+/*
+ * dlpi_close(): closes a previously opened DLPI stream
+ *
+ * input: int: the file descriptor of the DLPI stream
+ * output: int: 0 on success, -1 on failure
+ */
+
+int
+dlpi_close(int fd)
+{
+ /* don't bother dismantling. it will happen automatically */
+ return (close(fd));
+}
+
+/*
+ * dlpi_recvfrom(): receives data on a DLPI stream
+ *
+ * input: int: the socket to receive the data on
+ * void *: a buffer to store the data in
+ * size_t: the size of the buffer
+ * struct sockaddr_in *: if non-NULL, sender's IP address is filled in
+ * output: ssize_t: the number of bytes read on success, -1 on failure
+ */
+
+ssize_t
+dlpi_recvfrom(int fd, void *buffer, size_t buf_len, struct sockaddr_in *from)
+{
+ struct ip *ip;
+ struct udphdr *udphdr;
+ void *data_buffer;
+ ssize_t data_length;
+
+ data_length = buf_len + sizeof (struct ip) + sizeof (struct udphdr);
+ data_buffer = malloc(data_length);
+
+ if (data_buffer == NULL) {
+ dhcpmsg(MSG_ERR, "dlpi_recvfrom: cannot allocate packet");
+ return (-1);
+ }
+
+ data_length = dlpi_recv_link(fd, data_buffer, data_length, 0);
+ if (data_length == -1)
+ return (-1);
+
+ /*
+ * since we're just pulling data off the wire, what we have
+ * may look nothing like a DHCP packet. note that this
+ * shouldn't happen (pfmod should have tossed it already).
+ */
+
+ if (data_length < sizeof (struct ip) + sizeof (struct udphdr)) {
+ dhcpmsg(MSG_WARNING, "dlpi_recvfrom: dropped short packet");
+ free(data_buffer);
+ return (-1);
+ }
+
+ /*
+ * verify checksums
+ */
+
+ ip = (struct ip *)data_buffer;
+ if (ipv4cksum((uint16_t *)ip, ip->ip_hl << 2) != 0) {
+ dhcpmsg(MSG_WARNING, "dlpi_recvfrom: dropped packet with bad "
+ "ipv4 checksum");
+ free(data_buffer);
+ return (-1);
+ }
+
+ udphdr = (struct udphdr *)&ip[1];
+ if ((udphdr->uh_sum != 0) &&
+ (udp_chksum(udphdr, &ip->ip_src, &ip->ip_dst, ip->ip_p) != 0)) {
+ dhcpmsg(MSG_WARNING, "dlpi_recvfrom: dropped packet with bad "
+ "UDP checksum");
+ free(data_buffer);
+ return (-1);
+ }
+
+ data_length -= (sizeof (struct ip) + sizeof (struct udphdr));
+ (void) memcpy(buffer, &udphdr[1], data_length);
+
+ if (from != NULL) {
+ from->sin_addr = ip->ip_dst;
+ from->sin_port = udphdr->uh_sport;
+ }
+
+ free(data_buffer);
+ return (data_length);
+}
+
+/*
+ * dlpi_recv_link(): receives raw data on a DLPI stream
+ *
+ * input: int: the DLPI stream to receive the data on
+ * void *: a buffer to store the data in
+ * size_t: the size of the buffer
+ * uint32_t: flags (see dlpi_io.h)
+ * output: ssize_t: the number of bytes received on success, -1 on failure
+ */
+
+ssize_t
+dlpi_recv_link(int fd, void *data_buffer, size_t data_length, uint32_t flags)
+{
+ int getmsg_flags = 0;
+ struct strbuf ctrl, data;
+ char ctrlbuf[1024];
+
+ ctrl.maxlen = sizeof (ctrlbuf);
+ ctrl.buf = ctrlbuf;
+
+ data.maxlen = data_length;
+ data.buf = data_buffer;
+
+ switch (getmsg(fd, &ctrl, &data, &getmsg_flags)) {
+
+ case MORECTL:
+ case MOREDATA:
+ case MOREDATA|MORECTL:
+
+ (void) ioctl(fd, I_FLUSH, FLUSHR);
+
+ if ((flags & DLPI_RECV_SHORT) == 0)
+ dhcpmsg(MSG_WARNING, "dlpi_recv_link: discarding stray "
+ "data on streamhead");
+ break;
+
+ case -1:
+ dhcpmsg(MSG_ERR, "dlpi_recv_link: getmsg");
+ return (-1);
+
+ default:
+ break;
+ }
+
+ return (data.len);
+}
+
+
+/*
+ * dlpi_sendto(): sends UDP packets on a DLPI stream
+ *
+ * input: int: the socket to send the packet on
+ * void *: a buffer to send
+ * size_t: the size of the buffer
+ * struct sockaddr_in *: the IP address to send the data to
+ * uchar_t *: the link-layer destination address
+ * size_t: the size of the link-layer destination address
+ * output: ssize_t: the number of bytes sent on success, -1 on failure
+ */
+
+ssize_t
+dlpi_sendto(int fd, void *buffer, size_t buf_len, struct sockaddr_in *to,
+ uchar_t *dl_to, size_t dl_to_len)
+{
+ struct ip *ip;
+ struct udphdr *udphdr;
+ void *data_buffer;
+ size_t data_length;
+ static uint16_t ip_id = 0;
+
+ /*
+ * TODO: someday we might want to support `to' not being
+ * the same as INADDR_BROADCAST. we don't need the support
+ * right now, but it's annoying to have a general interface
+ * that only supports a specific function.
+ */
+
+ if (to->sin_addr.s_addr != htonl(INADDR_BROADCAST)) {
+ dhcpmsg(MSG_ERROR, "dlpi_sendto: send to unicast address");
+ return (-1);
+ }
+
+ /*
+ * we allocate one extra byte here in case the UDP checksum
+ * routine needs it to get the packet length to be even.
+ */
+
+ data_length = sizeof (struct ip) + sizeof (struct udphdr) + buf_len;
+ data_buffer = calloc(1, data_length + 1);
+ if (data_buffer == NULL) {
+ dhcpmsg(MSG_ERR, "dlpi_sendto: cannot allocate packet");
+ return (-1);
+ }
+
+ ip = (struct ip *)data_buffer;
+ udphdr = (struct udphdr *)&ip[1];
+
+ (void) memcpy(&udphdr[1], buffer, buf_len);
+
+ /*
+ * build the ipv4 header. assume that our source address is 0
+ * (since we wouldn't be using DLPI if we could actually send
+ * packets an easier way). note that we only need to set nonzero
+ * fields since we got calloc()'d memory above.
+ */
+
+ /*
+ * From a purist's perspective, we should set the TTL to 1 for
+ * limited broadcasts. But operational experience (cisco routers)
+ * has shown that doing so results in the relay agent dropping our
+ * packets. These same devices (ciscos) also don't set the TTL
+ * to MAXTTL on the unicast side of the relay agent. Thus, the only
+ * safe thing to do is to always set the ttl to MAXTTL. Sigh.
+ */
+
+ ip->ip_ttl = MAXTTL;
+
+ ip->ip_v = 4;
+ ip->ip_hl = sizeof (struct ip) / 4;
+ ip->ip_id = htons(ip_id++);
+ ip->ip_off = htons(IP_DF);
+ ip->ip_p = IPPROTO_UDP;
+ ip->ip_len = htons(data_length);
+ ip->ip_dst = to->sin_addr;
+ ip->ip_src.s_addr = htonl(INADDR_ANY);
+ ip->ip_sum = ipv4cksum((uint16_t *)ip, sizeof (struct ip));
+
+ udphdr->uh_ulen = htons(sizeof (struct udphdr) + buf_len);
+ udphdr->uh_sport = htons(IPPORT_BOOTPC);
+ udphdr->uh_dport = htons(IPPORT_BOOTPS);
+ udphdr->uh_sum = udp_chksum(udphdr, &ip->ip_src, &ip->ip_dst, ip->ip_p);
+
+ if (dlpi_send_link(fd, data_buffer, data_length, dl_to, dl_to_len)
+ == -1) {
+ free(data_buffer);
+ dhcpmsg(MSG_ERR, "dlpi_sendto: dlpi_send_link");
+ return (-1);
+ }
+
+ free(data_buffer);
+ return (buf_len);
+}
+
+/*
+ * dlpi_send_link(): sends raw data down a DLPI stream
+ *
+ * input: int: the DLPI stream to send the data on
+ * void *: the raw data to send
+ * size_t: the size of the raw data
+ * uchar_t *: the link-layer destination address
+ * size_t: the size of the link-layer destination address
+ * output: ssize_t: 0 on success, -1 on failure
+ */
+
+ssize_t
+dlpi_send_link(int fd, void *data_buffer, size_t data_length,
+ uchar_t *dest_addr, size_t dest_addr_length)
+{
+ struct strbuf ctrl, data;
+ ssize_t retval;
+ dl_unitdata_req_t *dl_req;
+
+ /*
+ * allocate the control part of the message and fill it in.
+ * all we really indicate is the destination address
+ */
+
+ dl_req = malloc(sizeof (dl_unitdata_req_t) + data_length);
+ if (dl_req == NULL) {
+ dhcpmsg(MSG_ERR, "dlpi_send_link: dl_unitdata_req allocation");
+ return (-1);
+ }
+
+ ctrl.len = sizeof (dl_unitdata_req_t) + data_length;
+ ctrl.buf = (caddr_t)dl_req;
+
+ data.len = data_length;
+ data.buf = data_buffer;
+
+ dl_req->dl_primitive = DL_UNITDATA_REQ;
+ dl_req->dl_priority.dl_min = 0;
+ dl_req->dl_priority.dl_max = 0;
+ dl_req->dl_dest_addr_offset = sizeof (dl_unitdata_req_t);
+ dl_req->dl_dest_addr_length = dest_addr_length;
+ (void) memcpy(&dl_req[1], dest_addr, dest_addr_length);
+
+ retval = putmsg(fd, &ctrl, &data, 0);
+ free(dl_req);
+ return (retval);
+}
+
+/*
+ * set_packet_filter(): sets the current packet filter on a DLPI stream
+ *
+ * input: int: the DLPI stream to set the packet filter on
+ * filter_func_t *: the filter to use
+ * void *: an argument to pass to the filter function
+ * const char *: a text description of the filter's purpose
+ * output: void
+ */
+
+void
+set_packet_filter(int fd, filter_func_t *filter, void *arg,
+ const char *filter_name)
+{
+ struct strioctl sioc;
+ struct packetfilt pf;
+ ushort_t *pfp = pf.Pf_Filter;
+
+ pf.Pf_FilterLen = filter(pfp, arg) - pf.Pf_Filter;
+
+ sioc.ic_cmd = PFIOCSETF;
+ sioc.ic_timout = DLPI_TIMEOUT;
+ sioc.ic_len = sizeof (struct packetfilt);
+ sioc.ic_dp = (caddr_t)&pf;
+
+ /*
+ * if this ioctl() fails, we're really hosed. the best we can
+ * really do is play on.
+ */
+
+ if (ioctl(fd, I_STR, &sioc) == -1)
+ dhcpmsg(MSG_ERR, "set_packet_filter: PFIOCSETF");
+ else
+ dhcpmsg(MSG_DEBUG, "set_packet_filter: set filter %#x "
+ "(%s filter)", filter, filter_name);
+
+ /*
+ * clean out any potential cruft on the descriptor that
+ * appeared before we were able to set the filter
+ */
+
+ (void) ioctl(fd, I_FLUSH, FLUSHR);
+}
+
+/*
+ * dhcp_filter(): builds a packet filter that permits only DHCP/BOOTP messages
+ *
+ * input: ushort_t *: a place to store the packet filter code
+ * void *: not used
+ * output: ushort_t *: two bytes past the last byte in the packet filter
+ */
+
+/* ARGSUSED */
+ushort_t *
+dhcp_filter(ushort_t *pfp, void *arg)
+{
+ /*
+ * only pass up UDP packets -- 8th byte is the ttl/proto field
+ */
+
+ *pfp++ = ENF_PUSHWORD + 4;
+ *pfp++ = ENF_PUSHLIT | ENF_AND;
+ *pfp++ = htons(0xff);
+ *pfp++ = ENF_PUSHLIT | ENF_CAND;
+ *pfp++ = htons(IPPROTO_UDP);
+
+ /*
+ * make sure the IP packet doesn't have any options. 2nd
+ * nibble is the header length field.
+ * TODO: if we decide to handle options, this code goes away.
+ */
+
+ *pfp++ = ENF_PUSHWORD + 0;
+ *pfp++ = ENF_PUSHLIT | ENF_AND;
+ *pfp++ = htons(0x0f00); /* only care about 2nd nibble */
+ *pfp++ = ENF_PUSHLIT | ENF_CAND;
+ *pfp++ = htons(0x0500); /* which should be 5 * 4 = 20 */
+
+ /*
+ * if there's a fragment offset, or if the IP_MF bit is lit,
+ * pitch the packet. this pitches all fragments.
+ * TODO: if we decide to handle fragments, this code goes away.
+ */
+
+ *pfp++ = ENF_PUSHWORD + 3;
+ *pfp++ = ENF_PUSHLIT | ENF_AND;
+ *pfp++ = htons(0x1fff | IP_MF);
+ *pfp++ = ENF_PUSHZERO | ENF_CAND;
+
+ /*
+ * make sure the packet is for the DHCP client port -- 22nd
+ * byte is the UDP port number.
+ */
+
+ *pfp++ = ENF_PUSHWORD + 11;
+ *pfp++ = ENF_PUSHLIT | ENF_CAND;
+ *pfp++ = htons(IPPORT_BOOTPC);
+
+ return (pfp);
+}
+
+/*
+ * blackhole_filter(): builds a packet filter that tosses all messages
+ *
+ * input: ushort_t *: a place to store the packet filter code
+ * void *: not used
+ * output: ushort_t *: two bytes past the last byte in the packet filter
+ */
+
+/* ARGSUSED */
+ushort_t *
+blackhole_filter(ushort_t *pfp, void *arg)
+{
+ *pfp++ = ENF_PUSHZERO;
+ return (pfp);
+}
+
+/*
+ * build_broadcast_dest(): builds a DLPI destination address for the broadcast
+ * address for use in DL_UNITDATA_REQs
+ *
+ * input: dl_info_ack_t *: information about the interface
+ * uchar_t *: set to the length of the returned address
+ * output: uchar_t *: the broadcast address (dynamically allocated)
+ */
+
+uchar_t *
+build_broadcast_dest(dl_info_ack_t *dlia, uchar_t *length)
+{
+ uchar_t sap_len = abs(dlia->dl_sap_length);
+ caddr_t dl_sap;
+ uchar_t *dest_addr;
+
+ *length = dlia->dl_brdcst_addr_length + sap_len;
+ dest_addr = malloc(*length);
+ if (dest_addr == NULL)
+ return (NULL);
+
+ if (dlia->dl_sap_length > 0) { /* sap before */
+ dl_sap = (caddr_t)dlia + dlia->dl_addr_offset;
+ (void) memcpy(dest_addr, dl_sap, sap_len);
+ (void) memcpy(dest_addr + sap_len, (caddr_t)dlia +
+ dlia->dl_brdcst_addr_offset, dlia->dl_brdcst_addr_length);
+ } else {
+ dl_sap = (caddr_t)dlia + dlia->dl_addr_offset +
+ (dlia->dl_addr_length - sap_len);
+ (void) memcpy(dest_addr, (caddr_t)dlia +
+ dlia->dl_brdcst_addr_offset, dlia->dl_brdcst_addr_length);
+ (void) memcpy(dest_addr + dlia->dl_brdcst_addr_length,
+ dl_sap, sap_len);
+ }
+
+ return (dest_addr);
+}
diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/dlpi_io.h b/usr/src/cmd/cmd-inet/sbin/dhcpagent/dlpi_io.h
new file mode 100644
index 0000000000..fdfe57be48
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/dlpi_io.h
@@ -0,0 +1,80 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef DLPI_IO_H
+#define DLPI_IO_H
+
+#pragma ident "%W% %E% SMI"
+
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/dlpi.h>
+
+/*
+ * dlpi_io.[ch] contain the interface the agent uses to interact with
+ * DLPI. it makes use of dlprims.c (and should be its only consumer).
+ * see dlpi_io.c for documentation on how to use the exported
+ * functions.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * buffer size to be used in control part of DLPI messages, in bytes
+ */
+#define DLPI_BUF_MAX 256
+
+/*
+ * timeout to be used on DLPI-related operations, in seconds
+ */
+#define DLPI_TIMEOUT 5
+
+/*
+ * flags for dlpi_recv_link()
+ */
+#define DLPI_RECV_SHORT 0x01 /* short reads are expected */
+
+typedef ushort_t *filter_func_t(ushort_t *, void *);
+
+filter_func_t dhcp_filter, blackhole_filter;
+uchar_t *build_broadcast_dest(dl_info_ack_t *, uchar_t *);
+void set_packet_filter(int, filter_func_t *, void *, const char *);
+int dlpi_open(const char *, dl_info_ack_t *, size_t, t_uscalar_t);
+int dlpi_close(int);
+ssize_t dlpi_recvfrom(int, void *, size_t, struct sockaddr_in *);
+ssize_t dlpi_recv_link(int, void *, size_t, uint32_t);
+ssize_t dlpi_send_link(int, void *, size_t, uchar_t *, size_t);
+ssize_t dlpi_sendto(int, void *, size_t, struct sockaddr_in *,
+ uchar_t *, size_t);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DLPI_IO_H */
diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/dlprims.c b/usr/src/cmd/cmd-inet/sbin/dhcpagent/dlprims.c
new file mode 100644
index 0000000000..3d0cf75273
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/dlprims.c
@@ -0,0 +1,208 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1996-1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ *
+ * heavily cannibalized from
+ *
+ * #ident "@(#)dlprims.c 1.12 97/03/27 SMI"
+ */
+
+#pragma ident "%W% %E% SMI"
+
+/*
+ * TODO: get rid of this code as soon as possible and replace it with a
+ * version from a standard library. this stuff is barf-o-riffic.
+ */
+
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/dlpi.h>
+#include <stropts.h>
+#include <sys/poll.h>
+
+#include "dlpi_io.h"
+
+static int strgetmsg(int, struct strbuf *, struct strbuf *);
+
+/*
+ * dlinforeq(): issue DL_INFO_REQ and fetch DL_INFO_ACK on stream
+ *
+ * input: int: the stream to do the DL_INFO_REQ on
+ * dl_info_ack_t: a place to store the DL_INFO_ACK
+ * size_t: the size of the dl_info_ack_t
+ * output: int: 0 on success, 1 on failure (errno is set)
+ */
+
+int
+dlinforeq(int fd, dl_info_ack_t *infoackp, size_t infoack_size)
+{
+ struct strbuf ctl;
+
+ infoackp->dl_primitive = DL_INFO_REQ;
+
+ ctl.maxlen = infoack_size;
+ ctl.len = DL_INFO_REQ_SIZE;
+ ctl.buf = (caddr_t)infoackp;
+
+ if (putmsg(fd, &ctl, NULL, 0) == -1)
+ return (1);
+
+ if (strgetmsg(fd, &ctl, NULL) == 1)
+ return (1);
+
+ if (infoackp->dl_primitive != DL_INFO_ACK ||
+ ctl.len < DL_INFO_ACK_SIZE) {
+ errno = EPROTO;
+ return (1);
+ }
+
+ return (0);
+}
+
+/*
+ * dlattachreq(): issue DL_ATTACH_REQ and fetch DL_OK_ACK on stream
+ *
+ * input: int: the stream to do the DL_ATTACH_REQ on
+ * t_uscalar_t: the ppa to do the attach to
+ * output: int: 0 on success, 1 on failure (errno is set)
+ */
+
+int
+dlattachreq(int fd, t_uscalar_t ppa)
+{
+ union DL_primitives *dlp;
+ uint32_t buf[DLPI_BUF_MAX / sizeof (uint32_t)];
+ struct strbuf ctl;
+
+ dlp = (union DL_primitives *)buf;
+ dlp->attach_req.dl_primitive = DL_ATTACH_REQ;
+ dlp->attach_req.dl_ppa = ppa;
+
+ ctl.maxlen = sizeof (buf);
+ ctl.len = DL_ATTACH_REQ_SIZE;
+ ctl.buf = (caddr_t)dlp;
+
+ if (putmsg(fd, &ctl, NULL, 0) == -1)
+ return (1);
+
+ if (strgetmsg(fd, &ctl, NULL) == 1)
+ return (1);
+
+ if (dlp->dl_primitive != DL_OK_ACK) {
+ errno = EPROTO;
+ return (1);
+ }
+
+ return (0);
+}
+
+/*
+ * dlbindreq(): issue DL_BIND_REQ and fetch DL_BIND_ACK on stream
+ *
+ * input: int: the stream to do the DL_BIND_REQ on
+ * t_uscalar_t: the sap to bind to
+ * t_uscalar_t: the max number of outstanding DL_CONNECT_IND messages
+ * uint16_t: the service mode (connectionless/connection-oriented)
+ * uint16_t: whether this is a connection management stream
+ * output: int: 0 on success, 1 on failure (errno is set)
+ */
+
+int
+dlbindreq(int fd, t_uscalar_t sap, t_uscalar_t max_conind,
+ uint16_t service_mode, uint16_t conn_mgmt)
+{
+ union DL_primitives *dlp;
+ uint32_t buf[DLPI_BUF_MAX / sizeof (uint32_t)];
+ struct strbuf ctl;
+
+ dlp = (union DL_primitives *)buf;
+ dlp->bind_req.dl_primitive = DL_BIND_REQ;
+ dlp->bind_req.dl_sap = sap;
+ dlp->bind_req.dl_max_conind = max_conind;
+ dlp->bind_req.dl_service_mode = service_mode;
+ dlp->bind_req.dl_conn_mgmt = conn_mgmt;
+ dlp->bind_req.dl_xidtest_flg = 0;
+
+ ctl.maxlen = sizeof (buf);
+ ctl.len = DL_BIND_REQ_SIZE;
+ ctl.buf = (caddr_t)dlp;
+
+ if (putmsg(fd, &ctl, NULL, 0) == -1)
+ return (1);
+
+ if (strgetmsg(fd, &ctl, NULL) == 1)
+ return (1);
+
+ if (dlp->dl_primitive != DL_BIND_ACK || ctl.len < DL_BIND_ACK_SIZE) {
+ errno = EPROTO;
+ return (1);
+ }
+
+ return (0);
+}
+
+/*
+ * strgetmsg(): timed getmsg(3C)
+ *
+ * input: int: the stream to wait for the message on
+ * struct strbuf *: a buffer to hold the control part of the message
+ * struct strbuf *: a buffer to hold the data part of the message
+ * output: int: 0 on success, 1 on failure (errno is set)
+ */
+
+static int
+strgetmsg(int fd, struct strbuf *ctlp, struct strbuf *datap)
+{
+ struct pollfd fds;
+ int flags = 0;
+ int retval;
+
+ fds.fd = fd;
+ fds.events = POLLIN|POLLPRI;
+
+ switch (poll(&fds, 1, DLPI_TIMEOUT * 1000)) {
+
+ case 0:
+ errno = ETIME;
+ return (1);
+
+ case -1:
+ return (1);
+
+ default:
+
+ retval = getmsg(fd, ctlp, datap, &flags);
+ if (retval == -1)
+ return (1);
+
+ if (retval > 0 || ctlp->len < sizeof (t_uscalar_t)) {
+ errno = EPROTO;
+ return (1);
+ }
+
+ break;
+ }
+
+ return (0);
+}
diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/dlprims.h b/usr/src/cmd/cmd-inet/sbin/dhcpagent/dlprims.h
new file mode 100644
index 0000000000..6169983c44
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/dlprims.h
@@ -0,0 +1,53 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef DLPRIMS_H
+#define DLPRIMS_H
+
+#pragma ident "%W% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/dlpi.h>
+
+/*
+ * dlprims.[ch] provide a "simpler" interface to DLPI. in truth, it's
+ * rather grotesque, but for now it's the best we can do. remove this
+ * file once DLPI routines are provided in a library.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int dlinforeq(int, dl_info_ack_t *, size_t);
+int dlattachreq(int, t_uscalar_t);
+int dlbindreq(int, t_uscalar_t, t_uscalar_t, uint16_t, uint16_t);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DLPRIMS_H */
diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/inc.flg b/usr/src/cmd/cmd-inet/sbin/dhcpagent/inc.flg
new file mode 100644
index 0000000000..a456cbaede
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/inc.flg
@@ -0,0 +1,30 @@
+#!/bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 1999 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+
+find_files "s.*" usr/src/common/net/dhcp
diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/inform.c b/usr/src/cmd/cmd-inet/sbin/dhcpagent/inform.c
new file mode 100644
index 0000000000..00a106cef1
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/inform.c
@@ -0,0 +1,148 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1995-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ *
+ * INFORM_SENT state of the client state machine.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/sockio.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/udp.h>
+#include <netinet/ip_var.h>
+#include <netinet/udp_var.h>
+#include <dhcpmsg.h>
+
+#include "util.h"
+#include "packet.h"
+#include "interface.h"
+
+/*
+ * dhcp_inform(): sends an INFORM packet and sets up reception for an ACK
+ *
+ * input: struct ifslist *: the interface to send the inform on, ...
+ * output: void
+ * note: the INFORM cannot be sent successfully if the interface
+ * does not have an IP address
+ */
+
+void
+dhcp_inform(struct ifslist *ifsp)
+{
+ dhcp_pkt_t *dpkt;
+ struct in_addr *our_addr;
+ struct ifreq ifr;
+
+ ifsp->if_state = INIT;
+
+ /*
+ * fetch our IP address -- since we may not manage the
+ * interface, go fetch it with ioctl()
+ */
+
+ (void) memset(&ifr, 0, sizeof (struct ifreq));
+ (void) strlcpy(ifr.ifr_name, ifsp->if_name, IFNAMSIZ);
+ ifr.ifr_addr.sa_family = AF_INET;
+
+ if (ioctl(ifsp->if_sock_fd, SIOCGIFADDR, &ifr) == -1) {
+ ifsp->if_dflags |= DHCP_IF_FAILED;
+ ipc_action_finish(ifsp, DHCP_IPC_E_INT);
+ async_finish(ifsp);
+ return;
+ }
+
+ /*
+ * the error handling here and in the check for IFF_UP below
+ * are handled different from most since it is the user who is
+ * at fault for the problem, not the machine.
+ */
+
+ /* LINTED [ifr_addr is a sockaddr which will be aligned] */
+ our_addr = &((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr;
+ if (our_addr->s_addr == htonl(INADDR_ANY)) {
+ dhcpmsg(MSG_WARNING, "dhcp_inform: INFORM attempted on "
+ "interface with no IP address");
+ ipc_action_finish(ifsp, DHCP_IPC_E_NOIPIF);
+ async_finish(ifsp);
+ remove_ifs(ifsp);
+ return;
+ }
+
+ if (ioctl(ifsp->if_sock_fd, SIOCGIFFLAGS, &ifr) == -1) {
+ ifsp->if_dflags |= DHCP_IF_FAILED;
+ ipc_action_finish(ifsp, DHCP_IPC_E_INT);
+ async_finish(ifsp);
+ return;
+ }
+
+ if ((ifr.ifr_flags & IFF_UP) == 0) {
+ dhcpmsg(MSG_WARNING, "dhcp_inform: INFORM attempted on downed "
+ "interface");
+ ipc_action_finish(ifsp, DHCP_IPC_E_DOWNIF);
+ async_finish(ifsp);
+ remove_ifs(ifsp);
+ return;
+ }
+
+ /*
+ * assemble a DHCPREQUEST packet, without the server id
+ * option. fill in ciaddr, since we know this. if_server
+ * will be set to the server's IP address, which will be the
+ * broadcast address if we don't know it. The max dhcp message size
+ * option is set to the interface max, minus the size of the udp and
+ * ip headers.
+ */
+
+ dpkt = init_pkt(ifsp, INFORM);
+ dpkt->pkt->ciaddr = *our_addr;
+
+ add_pkt_opt16(dpkt, CD_MAX_DHCP_SIZE, htons(ifsp->if_max -
+ sizeof (struct udpiphdr)));
+ add_pkt_opt(dpkt, CD_CLASS_ID, class_id, class_id_len);
+ add_pkt_opt(dpkt, CD_REQUEST_LIST, ifsp->if_prl, ifsp->if_prllen);
+ add_pkt_opt(dpkt, CD_END, NULL, 0);
+
+ if (send_pkt(ifsp, dpkt, ifsp->if_server.s_addr, NULL) == 0) {
+ ifsp->if_dflags |= DHCP_IF_FAILED;
+ dhcpmsg(MSG_ERROR, "dhcp_inform: send_pkt failed");
+ ipc_action_finish(ifsp, DHCP_IPC_E_INT);
+ async_finish(ifsp);
+ return;
+ }
+
+ if (register_acknak(ifsp) == 0) {
+ ifsp->if_dflags |= DHCP_IF_FAILED;
+ ipc_action_finish(ifsp, DHCP_IPC_E_MEMORY);
+ async_finish(ifsp);
+ return;
+ }
+
+ ifsp->if_state = INFORM_SENT;
+}
diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/init_reboot.c b/usr/src/cmd/cmd-inet/sbin/dhcpagent/init_reboot.c
new file mode 100644
index 0000000000..a66be18865
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/init_reboot.c
@@ -0,0 +1,158 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * INIT_REBOOT state of the DHCP client state machine.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <limits.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/dhcp.h>
+#include <netinet/udp.h>
+#include <netinet/ip_var.h>
+#include <netinet/udp_var.h>
+#include <dhcpmsg.h>
+#include <string.h>
+
+#include "agent.h"
+#include "packet.h"
+#include "states.h"
+#include "util.h"
+#include "interface.h"
+#include "defaults.h"
+
+static stop_func_t stop_init_reboot;
+
+/*
+ * dhcp_init_reboot(): attempts to reuse a cached configuration on an interface
+ *
+ * input: struct ifslist *: the interface to reuse the configuration on
+ * output: void
+ */
+
+void
+dhcp_init_reboot(struct ifslist *ifsp)
+{
+ dhcp_pkt_t *dpkt;
+ const char *reqhost;
+ char hostfile[PATH_MAX + 1];
+
+ dhcpmsg(MSG_VERBOSE, "%s has cached configuration - entering "
+ "INIT_REBOOT", ifsp->if_name);
+
+ ifsp->if_state = INIT_REBOOT;
+
+ if (register_acknak(ifsp) == 0) {
+
+ ifsp->if_state = INIT;
+ ifsp->if_dflags |= DHCP_IF_FAILED;
+ ipc_action_finish(ifsp, DHCP_IPC_E_MEMORY);
+ async_finish(ifsp);
+
+ dhcpmsg(MSG_ERROR, "dhcp_init_reboot: cannot register to "
+ "collect ACK/NAK packets, reverting to INIT on %s",
+ ifsp->if_name);
+ return;
+ }
+
+ /*
+ * assemble DHCPREQUEST message. The max dhcp message size
+ * option is set to the interface max, minus the size of the udp and
+ * ip headers.
+ */
+
+ dpkt = init_pkt(ifsp, REQUEST);
+ add_pkt_opt32(dpkt, CD_REQUESTED_IP_ADDR,
+ ifsp->if_ack->pkt->yiaddr.s_addr);
+
+ add_pkt_opt32(dpkt, CD_LEASE_TIME, htonl(DHCP_PERM));
+ add_pkt_opt16(dpkt, CD_MAX_DHCP_SIZE, htons(ifsp->if_max -
+ sizeof (struct udpiphdr)));
+
+ add_pkt_opt(dpkt, CD_CLASS_ID, class_id, class_id_len);
+ add_pkt_opt(dpkt, CD_REQUEST_LIST, ifsp->if_prl, ifsp->if_prllen);
+
+ /*
+ * Set CD_HOSTNAME option if REQUEST_HOSTNAME is set and a hostname
+ * is found in /etc/hostname.<ifname>
+ */
+ if (df_get_bool(ifsp->if_name, DF_REQUEST_HOSTNAME)) {
+ dhcpmsg(MSG_DEBUG, "dhcp_selecting: DF_REQUEST_HOSTNAME");
+ (void) snprintf(hostfile, sizeof (hostfile), "/etc/hostname.%s",
+ ifsp->if_name);
+
+ if ((reqhost = iffile_to_hostname(hostfile)) != NULL) {
+ dhcpmsg(MSG_DEBUG, "dhcp_selecting: host %s", reqhost);
+ if ((ifsp->if_reqhost = strdup(reqhost)) != NULL)
+ add_pkt_opt(dpkt, CD_HOSTNAME, ifsp->if_reqhost,
+ strlen(ifsp->if_reqhost));
+ else
+ dhcpmsg(MSG_WARNING, "dhcp_selecting: cannot"
+ " allocate memory for host name option");
+ }
+ }
+
+ add_pkt_opt(dpkt, CD_END, NULL, 0);
+
+ (void) send_pkt(ifsp, dpkt, htonl(INADDR_BROADCAST), stop_init_reboot);
+}
+
+/*
+ * stop_init_reboot(): decides when to stop retransmitting REQUESTs
+ *
+ * input: struct ifslist *: the interface REQUESTs are being sent on
+ * unsigned int: the number of REQUESTs sent so far
+ * output: boolean_t: B_TRUE if retransmissions should stop
+ */
+
+static boolean_t
+stop_init_reboot(struct ifslist *ifsp, unsigned int n_requests)
+{
+ if (n_requests >= DHCP_MAX_REQUESTS) {
+
+ (void) unregister_acknak(ifsp);
+
+ dhcpmsg(MSG_INFO, "no ACK/NAK to INIT_REBOOT REQUEST, "
+ "using remainder of existing lease on %s", ifsp->if_name);
+
+ /*
+ * we already stuck our old ack in ifsp->if_ack and
+ * relativized the packet times, so we can just
+ * pretend that the server sent it to us and move to
+ * bound. if that fails, fall back to selecting.
+ */
+
+ if (dhcp_bound(ifsp, NULL) == 0)
+ dhcp_selecting(ifsp);
+
+ return (B_TRUE);
+ }
+
+ return (B_FALSE);
+}
diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/interface.c b/usr/src/cmd/cmd-inet/sbin/dhcpagent/interface.c
new file mode 100644
index 0000000000..e79dcb0582
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/interface.c
@@ -0,0 +1,1197 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <sys/dlpi.h>
+#include <stdlib.h>
+#include <sys/sockio.h>
+#include <netinet/in.h>
+#include <netinet/dhcp.h>
+#include <string.h>
+#include <unistd.h>
+#include <netinet/if_ether.h>
+#include <signal.h>
+#include <dhcpmsg.h>
+#include <libdevinfo.h>
+
+#include "interface.h"
+#include "util.h"
+#include "dlpi_io.h"
+#include "packet.h"
+#include "defaults.h"
+#include "states.h"
+#include "script_handler.h"
+
+/*
+ * note to the reader:
+ *
+ * the terminology in here is slightly confusing. in particular, the
+ * term `ifslist' is used to refer both to the `struct ifslist' entry
+ * that makes up a specific interface entry, and the `internal
+ * ifslist' which is a linked list of struct ifslists. to reduce
+ * confusion, in the comments, a `struct ifslist' is referred to as
+ * an `ifs', and `ifslist' refers to the internal ifslist.
+ *
+ */
+
+static struct ifslist *ifsheadp;
+static unsigned int ifscount;
+
+static void init_ifs(struct ifslist *);
+static void free_ifs(struct ifslist *);
+static void cancel_ifs_timer(struct ifslist *, int);
+
+static boolean_t get_prom_prop(const char *, const char *, uchar_t **,
+ unsigned int *);
+
+/*
+ * insert_ifs(): creates a new ifs and chains it on the ifslist. initializes
+ * state which remains consistent across all use of the ifs entry
+ *
+ * input: const char *: the name of the ifs entry (interface name)
+ * boolean_t: if B_TRUE, we're adopting the interface
+ * int *: ignored on input; if insert_ifs fails, set to a DHCP_IPC_E_*
+ * error code with the reason why
+ * output: struct ifslist *: a pointer to the new ifs entry, or NULL on failure
+ */
+
+struct ifslist *
+insert_ifs(const char *if_name, boolean_t is_adopting, int *error)
+{
+ uint32_t buf[DLPI_BUF_MAX / sizeof (uint32_t)];
+ dl_info_ack_t *dlia = (dl_info_ack_t *)buf;
+ caddr_t dl_addr;
+ struct ifreq ifr;
+ unsigned int i, client_id_len = 0;
+ uchar_t *client_id = NULL;
+ const char *prl;
+ struct ifslist *ifsp;
+ long seed;
+
+ ifsp = lookup_ifs(if_name);
+ if (ifsp != NULL) {
+ *error = DHCP_IPC_E_INT; /* should never happen */
+ return (NULL);
+ }
+
+ /*
+ * okay, we've got a request to put a new interface under our
+ * control. it's our job to set everything that doesn't
+ * change for the life of the interface. (state that changes
+ * should be initialized in init_ifs() and reset by reset_ifs())
+ *
+ * 1. verify the interface can support DHCP
+ * 2. get the interface mtu
+ * 3. get the interface hardware type and hardware length
+ * 4. get the interface hardware address
+ * 5. get the interface broadcast address
+ * 6. get the interface flags
+ */
+
+ ifsp = calloc(1, sizeof (struct ifslist));
+ if (ifsp == NULL) {
+ dhcpmsg(MSG_ERR, "insert_ifs: cannot allocate ifs entry for "
+ "%s", if_name);
+ *error = DHCP_IPC_E_MEMORY;
+ return (NULL);
+ }
+
+ (void) strlcpy(ifsp->if_name, if_name, IFNAMSIZ);
+
+ /* step 1 */
+ ifsp->if_dlpi_fd = dlpi_open(if_name, dlia, sizeof (buf), ETHERTYPE_IP);
+ if (ifsp->if_dlpi_fd == -1) {
+ *error = DHCP_IPC_E_INVIF;
+ goto failure;
+ }
+
+ init_ifs(ifsp); /* ifsp->if_dlpi_fd must be valid */
+ ipc_action_init(ifsp);
+
+ /* step 2 */
+ ifsp->if_max = dlia->dl_max_sdu;
+ ifsp->if_opt = ifsp->if_max - BASE_PKT_SIZE;
+ ifsp->if_min = dlia->dl_min_sdu;
+
+ if (ifsp->if_max < DHCP_DEF_MAX_SIZE) {
+ dhcpmsg(MSG_ERROR, "insert_ifs: %s does not have a large "
+ "enough maximum SDU to support DHCP", if_name);
+ *error = DHCP_IPC_E_INVIF;
+ goto failure;
+ }
+
+ /* step 3 */
+ ifsp->if_hwtype = dlpi_to_arp(dlia->dl_mac_type);
+ ifsp->if_hwlen = dlia->dl_addr_length - abs(dlia->dl_sap_length);
+
+ dhcpmsg(MSG_DEBUG, "insert_ifs: %s: sdumax %d, optmax %d, hwtype %d, "
+ "hwlen %d", if_name, ifsp->if_max, ifsp->if_opt, ifsp->if_hwtype,
+ ifsp->if_hwlen);
+
+ /* step 4 */
+ ifsp->if_hwaddr = malloc(ifsp->if_hwlen);
+ if (ifsp->if_hwaddr == NULL) {
+ dhcpmsg(MSG_ERR, "insert_ifs: cannot allocate if_hwaddr "
+ "for %s", if_name);
+ *error = DHCP_IPC_E_MEMORY;
+ goto failure;
+ }
+
+ /*
+ * depending on the DLPI device, the sap and hardware addresses
+ * can be in either order within the dlsap address; find the
+ * location of the hardware address using dl_sap_length. see the
+ * DLPI specification for more on this braindamage.
+ */
+
+ dl_addr = (caddr_t)dlia + dlia->dl_addr_offset;
+ if (dlia->dl_sap_length > 0) {
+ ifsp->if_sap_before++;
+ dl_addr += dlia->dl_sap_length;
+ }
+
+ (void) memcpy(ifsp->if_hwaddr, dl_addr, ifsp->if_hwlen);
+
+ /* step 5 */
+ ifsp->if_saplen = abs(dlia->dl_sap_length);
+ ifsp->if_daddr = build_broadcast_dest(dlia, &ifsp->if_dlen);
+ if (ifsp->if_daddr == NULL) {
+ dhcpmsg(MSG_ERR, "insert_ifs: cannot allocate if_daddr "
+ "for %s", if_name);
+ *error = DHCP_IPC_E_MEMORY;
+ goto failure;
+ }
+
+ /* step 6 */
+ (void) strlcpy(ifr.ifr_name, if_name, IFNAMSIZ);
+
+ if (ioctl(ifsp->if_sock_fd, SIOCGIFFLAGS, &ifr) == -1) {
+ if (errno == ENXIO)
+ *error = DHCP_IPC_E_INVIF;
+ else
+ *error = DHCP_IPC_E_INT;
+ dhcpmsg(MSG_ERR, "insert_ifs: SIOCGIFFLAGS for %s", if_name);
+ goto failure;
+ }
+
+ /*
+ * if DHCPRUNNING is already set on the interface and we're
+ * not adopting it, the agent probably crashed and burned.
+ * note it, but don't let it stop the proceedings. we're
+ * pretty sure we're not already running, since we wouldn't
+ * have been able to bind to our IPC port.
+ */
+
+ if ((is_adopting == B_FALSE) && (ifr.ifr_flags & IFF_DHCPRUNNING))
+ dhcpmsg(MSG_WARNING, "insert_ifs: DHCP flag already set on %s",
+ if_name);
+
+ ifr.ifr_flags |= IFF_DHCPRUNNING;
+ (void) ioctl(ifsp->if_sock_fd, SIOCSIFFLAGS, &ifr);
+
+ ifsp->if_send_pkt.pkt = calloc(ifsp->if_max, 1);
+ if (ifsp->if_send_pkt.pkt == NULL) {
+ dhcpmsg(MSG_ERR, "insert_ifs: cannot allocate if_send_pkt "
+ "for %s", if_name);
+ *error = DHCP_IPC_E_MEMORY;
+ goto failure;
+ }
+
+ if (is_adopting) {
+ /*
+ * if the agent is adopting a lease OBP is initially
+ * searched for a client-id
+ */
+
+ dhcpmsg(MSG_DEBUG, "insert_ifs: getting /chosen:clientid "
+ "property");
+
+ if (!get_prom_prop("chosen", "client-id", &ifsp->if_cid,
+ &client_id_len)) {
+ /*
+ * a failure occurred trying to acquire the client-id
+ */
+
+ dhcpmsg(MSG_DEBUG, "insert_ifs: cannot allocate client "
+ "id for %s", if_name);
+ *error = DHCP_IPC_E_INT;
+ goto failure;
+ } else if (dlia->dl_mac_type == DL_IB && ifsp->if_cid == NULL) {
+ /*
+ * when the interface is infiniband and the agent
+ * is adopting the lease there must be an OBP
+ * client-id.
+ */
+
+ dhcpmsg(MSG_DEBUG, "insert_ifs: no /chosen:clientid"
+ "id for %s", if_name);
+ *error = DHCP_IPC_E_INT;
+ goto failure;
+ }
+
+ ifsp->if_cidlen = client_id_len;
+ } else {
+ /*
+ * look in defaults file for the client-id
+ */
+
+ dhcpmsg(MSG_DEBUG, "insert_ifs: getting defaults client-id "
+ "property");
+
+ client_id = df_get_octet(if_name, DF_CLIENT_ID, &client_id_len);
+
+ /*
+ * at this point, all logical interfaces must be explicitly
+ * configured with a client id by the administrator.
+ */
+
+ if (client_id == NULL && strchr(if_name, ':') != NULL) {
+ dhcpmsg(MSG_ERROR, "no client id configured for "
+ "logical interface %s; cannot manage", if_name);
+ *error = DHCP_IPC_E_NOIFCID;
+ goto failure;
+ }
+
+ if (client_id != NULL) {
+ /*
+ * the defaults client-id value must be copied out to
+ * another buffer
+ */
+
+ ifsp->if_cid = calloc(client_id_len, sizeof (uchar_t));
+
+ if (ifsp->if_cid == NULL) {
+ dhcpmsg(MSG_ERR, "insert_ifs: cannot "
+ "allocate client id for %s", if_name);
+ *error = DHCP_IPC_E_MEMORY;
+ goto failure;
+ }
+
+ (void) memcpy(ifsp->if_cid, client_id, client_id_len);
+
+ ifsp->if_cidlen = client_id_len;
+ } else if (dlia->dl_mac_type == DL_IB) {
+ /*
+ * This comes from DHCP over IPoIB spec. In the absence
+ * of an user specified client id, IPoIB automatically
+ * uses the required format, with the unique 4 octet
+ * value set to 0 (since IPoIB driver allows only a
+ * single interface on a port with a specific GID to
+ * belong to an IP subnet (PSARC 2001/289,
+ * FWARC 2002/702).
+ *
+ * Type Client-Identifier
+ * +-----+-----+-----+-----+-----+----....----+
+ * | 0 | 0 (4 octets) | GID (16 octets)|
+ * +-----+-----+-----+-----+-----+----....----+
+ */
+ ifsp->if_cidlen = 1 + 4 + 16;
+ ifsp->if_cid = client_id = malloc(ifsp->if_cidlen);
+ if (ifsp->if_cid == NULL) {
+ dhcpmsg(MSG_ERR, "insert_ifs: cannot "
+ "allocate client id for %s", if_name);
+ *error = DHCP_IPC_E_MEMORY;
+ goto failure;
+ }
+
+ /*
+ * Pick the GID from the mac address. The format
+ * of the hardware address is:
+ * +-----+-----+-----+-----+----....----+
+ * | QPN (4 octets) | GID (16 octets)|
+ * +-----+-----+-----+-----+----....----+
+ */
+ (void) memcpy(client_id + 5, ifsp->if_hwaddr + 4,
+ ifsp->if_hwlen - 4);
+ (void) memset(client_id, 0, 5);
+ }
+ }
+
+ /*
+ * initialize the parameter request list, if there is one.
+ */
+
+ prl = df_get_string(if_name, DF_PARAM_REQUEST_LIST);
+ if (prl == NULL)
+ ifsp->if_prl = NULL;
+ else {
+ for (ifsp->if_prllen = 1, i = 0; prl[i] != '\0'; i++)
+ if (prl[i] == ',')
+ ifsp->if_prllen++;
+
+ ifsp->if_prl = malloc(ifsp->if_prllen);
+ if (ifsp->if_prl == NULL) {
+ dhcpmsg(MSG_WARNING, "insert_ifs: cannot allocate "
+ "parameter request list for %s (continuing)",
+ if_name);
+ } else {
+ for (i = 0; i < ifsp->if_prllen; prl++, i++) {
+ ifsp->if_prl[i] = strtoul(prl, NULL, 0);
+ while (*prl != ',' && *prl != '\0')
+ prl++;
+ if (*prl == '\0')
+ break;
+ }
+ }
+ }
+
+ ifsp->if_offer_wait = df_get_int(if_name, DF_OFFER_WAIT);
+
+ /*
+ * we're past the point of failure; chain it on.
+ */
+
+ ifsp->next = ifsheadp;
+ ifsp->prev = NULL;
+ ifsheadp = ifsp;
+
+ if (ifsheadp->next != NULL)
+ ifsheadp->next->prev = ifsheadp;
+
+ hold_ifs(ifsp);
+ ifscount++;
+
+ if (inactivity_id != -1) {
+ if (iu_cancel_timer(tq, inactivity_id, NULL) == 1)
+ inactivity_id = -1;
+ }
+
+ /*
+ * seed the random number generator, since we're going to need it
+ * to set transaction id's and for exponential backoff. if an
+ * interface is already initialized, then we just end up harmlessly
+ * reseeding it. note that we try to spread the hardware address
+ * over as many bits of the seed as possible.
+ */
+ seed = gethrtime();
+ for (i = 0; i < ifsp->if_hwlen; i++)
+ seed += ifsp->if_hwaddr[i] << ((i % 7) * 4);
+ seed ^= getpid();
+ srand48(seed);
+
+ dhcpmsg(MSG_DEBUG, "insert_ifs: inserted interface %s", if_name);
+ return (ifsp);
+
+failure:
+ free_ifs(ifsp);
+ return (NULL);
+}
+
+/*
+ * init_ifs(): puts an ifs in its initial state
+ *
+ * input: struct ifslist *: the ifs to initialize
+ * output: void
+ * note: if the interface isn't fresh, use reset_ifs()
+ */
+
+static void
+init_ifs(struct ifslist *ifsp)
+{
+ /*
+ * if_sock_ip_fd is created and bound in configure_if().
+ * if_sock_fd is bound in configure_if(); see comments in
+ * bound.c for more details on why. if creation of if_sock_fd
+ * fails, we'll need more context anyway, so don't check.
+ */
+
+ ifsp->if_sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
+ ifsp->if_sock_ip_fd = -1;
+ ifsp->if_state = INIT;
+ ifsp->if_routers = NULL;
+ ifsp->if_nrouters = 0;
+ ifsp->if_ack = NULL;
+ ifsp->if_orig_ack = NULL;
+ ifsp->if_server.s_addr = htonl(INADDR_BROADCAST);
+ ifsp->if_neg_monosec = monosec();
+ ifsp->if_lease = 0;
+ ifsp->if_t1 = 0;
+ ifsp->if_t2 = 0;
+ ifsp->if_reqhost = NULL;
+
+ ifsp->if_script_helper_pid = -1;
+ ifsp->if_script_callback = NULL;
+ ifsp->if_script_event = NULL;
+ ifsp->if_callback_msg = NULL;
+ ifsp->if_script_event_id = -1;
+ ifsp->if_script_pid = -1;
+ ifsp->if_script_fd = -1;
+
+ ifsp->if_offer_id = -1;
+ ifsp->if_acknak_id = -1;
+ ifsp->if_acknak_bcast_id = -1;
+ ifsp->if_timer[DHCP_T1_TIMER] = -1;
+ ifsp->if_timer[DHCP_T2_TIMER] = -1;
+ ifsp->if_timer[DHCP_LEASE_TIMER] = -1;
+
+ set_packet_filter(ifsp->if_dlpi_fd, dhcp_filter, NULL, "DHCP");
+
+ dhcpmsg(MSG_DEBUG, "init_ifs: initted interface %s", ifsp->if_name);
+}
+
+/*
+ * remove_ifs_default_routes(): removes an ifs's default routes
+ *
+ * input: struct ifslist *: the ifs whose default routes need to be removed
+ * output: void
+ */
+
+static void
+remove_ifs_default_routes(struct ifslist *ifsp)
+{
+ if (ifsp->if_routers != NULL) {
+ while (ifsp->if_nrouters > 0) {
+ (void) del_default_route(ifsp->if_name,
+ &ifsp->if_routers[--ifsp->if_nrouters]);
+ }
+ free(ifsp->if_routers);
+ ifsp->if_routers = NULL;
+ }
+}
+
+/*
+ * reset_ifs(): resets an ifs to its initial state
+ *
+ * input: struct ifslist *: the ifs to reset
+ * output: void
+ */
+
+void
+reset_ifs(struct ifslist *ifsp)
+{
+ ifsp->if_dflags &= ~DHCP_IF_FAILED;
+
+ remove_ifs_default_routes(ifsp);
+
+ if (ifsp->if_sock_fd != -1)
+ (void) close(ifsp->if_sock_fd);
+
+ if (ifsp->if_orig_ack != ifsp->if_ack)
+ free_pkt_list(&ifsp->if_orig_ack);
+
+ free_pkt_list(&ifsp->if_ack);
+
+ if (ifsp->if_sock_ip_fd != -1)
+ (void) close(ifsp->if_sock_ip_fd);
+
+ if (ifsp->if_offer_id != -1) {
+ if (iu_unregister_event(eh, ifsp->if_offer_id, NULL) != 0)
+ (void) release_ifs(ifsp);
+ }
+
+ (void) unregister_acknak(ifsp); /* just in case */
+
+ cancel_ifs_timers(ifsp);
+ init_ifs(ifsp);
+}
+
+/*
+ * lookup_ifs(): looks up an ifs, given its name
+ *
+ * input: const char *: the name of the ifs entry (the interface name)
+ * the name "" searches for the primary interface
+ * output: struct ifslist *: the corresponding ifs, or NULL if not found
+ */
+
+struct ifslist *
+lookup_ifs(const char *if_name)
+{
+ struct ifslist *ifs;
+
+ for (ifs = ifsheadp; ifs != NULL; ifs = ifs->next)
+ if (*if_name != '\0') {
+ if (strcmp(ifs->if_name, if_name) == 0)
+ break;
+ } else if (ifs->if_dflags & DHCP_IF_PRIMARY)
+ break;
+
+ return (ifs);
+}
+
+/*
+ * lookup_ifs_by_xid(): looks up an ifs, given its last used transaction id
+ *
+ * input: int: the transaction id to look up
+ * output: struct ifslist *: the corresponding ifs, or NULL if not found
+ */
+
+struct ifslist *
+lookup_ifs_by_xid(uint32_t xid)
+{
+ struct ifslist *ifs;
+
+ for (ifs = ifsheadp; ifs != NULL; ifs = ifs->next) {
+ if (ifs->if_send_pkt.pkt->xid == xid)
+ break;
+ }
+
+ return (ifs);
+}
+
+/*
+ * remove_ifs(): removes a given ifs from the ifslist. marks the ifs
+ * for being freed (but may not actually free it).
+ *
+ * input: struct ifslist *: the ifs to remove
+ * output: void
+ * note: see interface.h for a discussion of ifs memory management
+ */
+
+void
+remove_ifs(struct ifslist *ifsp)
+{
+ struct ifreq ifr;
+
+ if (ifsp->if_dflags & DHCP_IF_REMOVED)
+ return;
+
+ (void) memset(&ifr, 0, sizeof (struct ifreq));
+ (void) strlcpy(ifr.ifr_name, ifsp->if_name, IFNAMSIZ);
+
+ if (ioctl(ifsp->if_sock_fd, SIOCGIFFLAGS, &ifr) == 0) {
+ ifr.ifr_flags &= ~IFF_DHCPRUNNING;
+ (void) ioctl(ifsp->if_sock_fd, SIOCSIFFLAGS, &ifr);
+ }
+
+ ifsp->if_dflags |= DHCP_IF_REMOVED;
+
+ /*
+ * if we have long term timers, cancel them so that interface
+ * resources can be reclaimed in a reasonable amount of time.
+ */
+
+ cancel_ifs_timers(ifsp);
+
+ if (ifsp->prev != NULL)
+ ifsp->prev->next = ifsp->next;
+ else
+ ifsheadp = ifsp->next;
+
+ if (ifsp->next != NULL)
+ ifsp->next->prev = ifsp->prev;
+
+ ifscount--;
+ (void) release_ifs(ifsp);
+
+ /* no big deal if this fails */
+ if (ifscount == 0) {
+ inactivity_id = iu_schedule_timer(tq, DHCP_INACTIVITY_WAIT,
+ inactivity_shutdown, NULL);
+ }
+}
+
+/*
+ * hold_ifs(): acquires a hold on an ifs
+ *
+ * input: struct ifslist *: the ifs entry to acquire a hold on
+ * output: void
+ */
+
+void
+hold_ifs(struct ifslist *ifsp)
+{
+ ifsp->if_hold_count++;
+
+ dhcpmsg(MSG_DEBUG2, "hold_ifs: hold count on %s: %d",
+ ifsp->if_name, ifsp->if_hold_count);
+}
+
+/*
+ * release_ifs(): releases a hold previously acquired on an ifs. if the
+ * hold count reaches 0, the ifs is freed
+ *
+ * input: struct ifslist *: the ifs entry to release the hold on
+ * output: int: the number of holds outstanding on the ifs
+ */
+
+int
+release_ifs(struct ifslist *ifsp)
+{
+ if (ifsp->if_hold_count == 0) {
+ dhcpmsg(MSG_CRIT, "release_ifs: extraneous release");
+ return (0);
+ }
+
+ if (--ifsp->if_hold_count == 0) {
+ free_ifs(ifsp);
+ return (0);
+ }
+
+ dhcpmsg(MSG_DEBUG2, "release_ifs: hold count on %s: %d",
+ ifsp->if_name, ifsp->if_hold_count);
+
+ return (ifsp->if_hold_count);
+}
+
+/*
+ * free_ifs(): frees the memory occupied by an ifs entry
+ *
+ * input: struct ifslist *: the ifs entry to free
+ * output: void
+ */
+
+static void
+free_ifs(struct ifslist *ifsp)
+{
+ dhcpmsg(MSG_DEBUG, "free_ifs: freeing interface %s", ifsp->if_name);
+
+ free_pkt_list(&ifsp->if_recv_pkt_list);
+ if (ifsp->if_ack != ifsp->if_orig_ack)
+ free_pkt_list(&ifsp->if_orig_ack);
+ free_pkt_list(&ifsp->if_ack);
+ free(ifsp->if_send_pkt.pkt);
+ free(ifsp->if_cid);
+ free(ifsp->if_daddr);
+ free(ifsp->if_hwaddr);
+ free(ifsp->if_prl);
+ free(ifsp->if_reqhost);
+ free(ifsp->if_routers);
+
+ if (ifsp->if_sock_fd != -1)
+ (void) close(ifsp->if_sock_fd);
+
+ if (ifsp->if_sock_ip_fd != -1)
+ (void) close(ifsp->if_sock_ip_fd);
+
+ if (ifsp->if_dlpi_fd != -1)
+ (void) dlpi_close(ifsp->if_dlpi_fd);
+
+ free(ifsp);
+}
+
+/*
+ * checkaddr(): checks if the given address is still set on the given ifs
+ *
+ * input: struct ifslist *: the ifs to check
+ * int: the address to lookup on the interface
+ * struct in_addr *: the address to compare to
+ * output: boolean_t: B_TRUE if the address is still set; B_FALSE if not
+ */
+
+static boolean_t
+checkaddr(struct ifslist *ifsp, int ioccmd, struct in_addr *addr)
+{
+ struct ifreq ifr;
+ struct sockaddr_in *sin;
+
+ /* LINTED [ifr_addr is a sockaddr which will be aligned] */
+ sin = (struct sockaddr_in *)&ifr.ifr_addr;
+
+ (void) memset(&ifr, 0, sizeof (struct ifreq));
+ (void) strlcpy(ifr.ifr_name, ifsp->if_name, IFNAMSIZ);
+ ifr.ifr_addr.sa_family = AF_INET;
+
+ switch (ioctl(ifsp->if_sock_fd, ioccmd, &ifr)) {
+ case 0:
+ if (sin->sin_addr.s_addr != addr->s_addr)
+ return (B_FALSE);
+ break;
+ case -1:
+ if (errno == ENXIO)
+ return (B_FALSE);
+ break;
+ }
+ return (B_TRUE);
+}
+
+/*
+ * verify_ifs(): verifies than an ifs is still valid (i.e., has not been
+ * explicitly or implicitly dropped or released)
+ *
+ * input: struct ifslist *: the ifs to verify
+ * output: int: 1 if the ifs is still valid, 0 if the interface is invalid
+ */
+
+int
+verify_ifs(struct ifslist *ifsp)
+{
+ struct ifreq ifr;
+
+ if (ifsp->if_dflags & DHCP_IF_REMOVED)
+ return (0);
+
+ (void) memset(&ifr, 0, sizeof (struct ifreq));
+ (void) strlcpy(ifr.ifr_name, ifsp->if_name, IFNAMSIZ);
+
+ ifr.ifr_addr.sa_family = AF_INET;
+
+ switch (ifsp->if_state) {
+
+ case BOUND:
+ case RENEWING:
+ case REBINDING:
+
+ /*
+ * if the interface has gone down or been unplumbed, then we
+ * act like there has been an implicit drop.
+ */
+
+ switch (ioctl(ifsp->if_sock_fd, SIOCGIFFLAGS, &ifr)) {
+ case 0:
+ if ((ifr.ifr_flags & (IFF_UP|IFF_DHCPRUNNING)) !=
+ (IFF_UP|IFF_DHCPRUNNING))
+ goto abandon;
+ break;
+ case -1:
+ if (errno == ENXIO)
+ goto abandon;
+ break;
+ }
+ /* FALLTHRU */
+
+ case INIT_REBOOT:
+ case SELECTING:
+ case REQUESTING:
+
+ /*
+ * if the IP address, netmask, or broadcast address have
+ * changed, or the interface has been unplumbed, then we act
+ * like there has been an implicit drop.
+ */
+
+ if (!checkaddr(ifsp, SIOCGIFADDR, &ifsp->if_addr) ||
+ !checkaddr(ifsp, SIOCGIFNETMASK, &ifsp->if_netmask) ||
+ !checkaddr(ifsp, SIOCGIFBRDADDR, &ifsp->if_broadcast))
+ goto abandon;
+ }
+
+ return (1);
+abandon:
+ dhcpmsg(MSG_WARNING, "verify_ifs: %s has changed properties, "
+ "abandoning", ifsp->if_name);
+
+ remove_ifs(ifsp);
+ return (0);
+}
+
+/*
+ * canonize_ifs(): puts the interface in a canonical (zeroed) form
+ *
+ * input: struct ifslist *: the interface to canonize
+ * output: int: 1 on success, 0 on failure
+ */
+
+int
+canonize_ifs(struct ifslist *ifsp)
+{
+ struct sockaddr_in *sin;
+ struct ifreq ifr;
+
+ dhcpmsg(MSG_VERBOSE, "canonizing interface %s", ifsp->if_name);
+
+ /*
+ * note that due to infelicities in the routing code, any default
+ * routes must be removed prior to clearing the UP flag.
+ */
+
+ remove_ifs_default_routes(ifsp);
+
+ /* LINTED [ifr_addr is a sockaddr which will be aligned] */
+ sin = (struct sockaddr_in *)&ifr.ifr_addr;
+
+ (void) memset(&ifr, 0, sizeof (struct ifreq));
+ (void) strlcpy(ifr.ifr_name, ifsp->if_name, IFNAMSIZ);
+
+ if (ioctl(ifsp->if_sock_fd, SIOCGIFFLAGS, &ifr) == -1)
+ return (0);
+
+ /*
+ * clear the UP flag, but don't clear DHCPRUNNING since
+ * that should only be done when the interface is removed
+ * (see remove_ifs())
+ */
+
+ ifr.ifr_flags &= ~IFF_UP;
+
+ if (ioctl(ifsp->if_sock_fd, SIOCSIFFLAGS, &ifr) == -1)
+ return (0);
+
+ /*
+ * since ifr is actually a union, we need to explicitly zero
+ * the flags field before we reuse the structure, or otherwise
+ * cruft may leak over into other members of the union.
+ */
+
+ ifr.ifr_flags = 0;
+ ifr.ifr_addr.sa_family = AF_INET;
+ sin->sin_addr.s_addr = htonl(INADDR_ANY);
+
+ if (ioctl(ifsp->if_sock_fd, SIOCSIFADDR, &ifr) == -1)
+ return (0);
+
+ if (ioctl(ifsp->if_sock_fd, SIOCSIFNETMASK, &ifr) == -1)
+ return (0);
+
+ if (ioctl(ifsp->if_sock_fd, SIOCSIFBRDADDR, &ifr) == -1)
+ return (0);
+
+ /*
+ * any time we change the IP address, netmask, or broadcast we
+ * must be careful to also reset bookkeeping of what these are
+ * set to. this is so we can detect if these characteristics
+ * are changed by another process.
+ */
+
+ ifsp->if_addr.s_addr = htonl(INADDR_ANY);
+ ifsp->if_netmask.s_addr = htonl(INADDR_ANY);
+ ifsp->if_broadcast.s_addr = htonl(INADDR_ANY);
+
+ return (1);
+}
+
+/*
+ * check_ifs(): makes sure an ifs is still valid, and if it is, releases the
+ * ifs. otherwise, it informs the caller the ifs is going away
+ * and expects the caller to perform the release
+ *
+ * input: struct ifslist *: the ifs to check
+ * output: int: 1 if the interface is valid, 0 otherwise
+ */
+
+int
+check_ifs(struct ifslist *ifsp)
+{
+ hold_ifs(ifsp);
+ if (release_ifs(ifsp) == 1 || verify_ifs(ifsp) == 0) {
+
+ /*
+ * this interface is going away. if there's an
+ * uncancelled IPC event roaming around, cancel it
+ * now. we leave the hold on in case anyone else has
+ * any cleanup work that needs to be done before the
+ * interface goes away.
+ */
+
+ ipc_action_finish(ifsp, DHCP_IPC_E_UNKIF);
+ async_finish(ifsp);
+ return (0);
+ }
+
+ (void) release_ifs(ifsp);
+ return (1);
+}
+
+/*
+ * nuke_ifslist(): delete the ifslist (for use when the dhcpagent is exiting)
+ *
+ * input: boolean_t: B_TRUE if the agent is exiting due to SIGTERM
+ * output: void
+ */
+
+void
+nuke_ifslist(boolean_t onterm)
+{
+ int status;
+ struct ifslist *ifsp, *ifsp_next;
+
+ for (ifsp = ifsheadp; ifsp != NULL; ifsp = ifsp_next) {
+ ifsp_next = ifsp->next;
+
+ cancel_ifs_timers(ifsp);
+ if (ifsp->if_script_pid != -1) {
+ /* stop a script if it is not for DROP or RELEASE */
+ if (strcmp(ifsp->if_script_event, EVENT_DROP) == 0 ||
+ strcmp(ifsp->if_script_event, EVENT_RELEASE) == 0) {
+ continue;
+ }
+ script_stop(ifsp);
+ }
+
+ /*
+ * if the script is started by script_start, dhcp_drop and
+ * dhcp_release should and will only be called after the
+ * script exits.
+ */
+ if (onterm &&
+ df_get_bool(ifsp->if_name, DF_RELEASE_ON_SIGTERM)) {
+ if (script_start(ifsp, EVENT_RELEASE, dhcp_release,
+ "DHCP agent is exiting", &status) == 1) {
+ continue;
+ }
+ if (status == 1)
+ continue;
+ }
+ (void) script_start(ifsp, EVENT_DROP, dhcp_drop, NULL, NULL);
+ }
+}
+
+/*
+ * refresh_ifslist(): refreshes all finite leases under DHCP control
+ *
+ * input: iu_eh_t *: unused
+ * int: unused
+ * void *: unused
+ * output: void
+ */
+
+/* ARGSUSED */
+void
+refresh_ifslist(iu_eh_t *eh, int sig, void *arg)
+{
+ struct ifslist *ifsp;
+
+ for (ifsp = ifsheadp; ifsp != NULL; ifsp = ifsp->next) {
+
+ if (ifsp->if_state != BOUND && ifsp->if_state != RENEWING &&
+ ifsp->if_state != REBINDING)
+ continue;
+
+ if (ifsp->if_lease == DHCP_PERM)
+ continue;
+
+ /*
+ * this interface has a finite lease and we do not know
+ * how long the machine's been off for. refresh it.
+ */
+
+ dhcpmsg(MSG_WARNING, "refreshing lease on %s", ifsp->if_name);
+ cancel_ifs_timer(ifsp, DHCP_T1_TIMER);
+ cancel_ifs_timer(ifsp, DHCP_T2_TIMER);
+ (void) iu_adjust_timer(tq, ifsp->if_timer[DHCP_LEASE_TIMER], 0);
+ }
+}
+
+/*
+ * ifs_count(): returns the number of interfaces currently managed
+ *
+ * input: void
+ * output: unsigned int: the number of interfaces currently managed
+ */
+
+unsigned int
+ifs_count(void)
+{
+ return (ifscount);
+}
+
+/*
+ * cancel_ifs_timer(): cancels a lease-related timer on an interface
+ *
+ * input: struct ifslist *: the interface to operate on
+ * int: the timer id of the timer to cancel
+ * output: void
+ */
+
+static void
+cancel_ifs_timer(struct ifslist *ifsp, int timer_id)
+{
+ if (ifsp->if_timer[timer_id] != -1) {
+ if (iu_cancel_timer(tq, ifsp->if_timer[timer_id], NULL) == 1) {
+ (void) release_ifs(ifsp);
+ ifsp->if_timer[timer_id] = -1;
+ } else
+ dhcpmsg(MSG_WARNING, "cancel_ifs_timer: cannot cancel "
+ "if_timer[%d]", timer_id);
+ }
+}
+
+/*
+ * cancel_ifs_timers(): cancels an interface's pending lease-related timers
+ *
+ * input: struct ifslist *: the interface to operate on
+ * output: void
+ */
+
+void
+cancel_ifs_timers(struct ifslist *ifsp)
+{
+ cancel_ifs_timer(ifsp, DHCP_T1_TIMER);
+ cancel_ifs_timer(ifsp, DHCP_T2_TIMER);
+ cancel_ifs_timer(ifsp, DHCP_LEASE_TIMER);
+}
+
+/*
+ * schedule_ifs_timer(): schedules a lease-related timer on an interface
+ *
+ * input: struct ifslist *: the interface to operate on
+ * int: the timer to schedule
+ * uint32_t: the number of seconds in the future it should fire
+ * iu_tq_callback_t *: the callback to call upon firing
+ * output: int: 1 if the timer was scheduled successfully, 0 on failure
+ */
+
+int
+schedule_ifs_timer(struct ifslist *ifsp, int timer_id, uint32_t sec,
+ iu_tq_callback_t *expire)
+{
+ cancel_ifs_timer(ifsp, timer_id); /* just in case */
+
+ ifsp->if_timer[timer_id] = iu_schedule_timer(tq, sec, expire, ifsp);
+ if (ifsp->if_timer[timer_id] == -1) {
+ dhcpmsg(MSG_WARNING, "schedule_ifs_timer: cannot schedule "
+ "if_timer[%d]", timer_id);
+ return (0);
+ }
+
+ hold_ifs(ifsp);
+ return (1);
+}
+
+/*
+ * Get the value of the named property on the named node in devinfo root.
+ *
+ * input: const char *: The name of the node containing the property.
+ * const char *: The name of the property.
+ * uchar_t **: The property value, modified iff B_TRUE is returned.
+ * If no value is found the value is set to NULL.
+ * unsigned int *: The length of the property value
+ * output: boolean_t: Returns B_TRUE if successful (no problems),
+ * otherwise B_FALSE.
+ * note: The memory allocated by this function must be freed by
+ * the caller. This code is derived from
+ * usr/src/lib/libwanboot/common/bootinfo_aux.c.
+ */
+
+static boolean_t
+get_prom_prop(const char *nodename, const char *propname, uchar_t **propvaluep,
+ unsigned int *lenp)
+{
+ di_node_t root_node = DI_NODE_NIL;
+ di_node_t node;
+ di_prom_handle_t phdl = DI_PROM_HANDLE_NIL;
+ di_prom_prop_t pp;
+ uchar_t *value = NULL;
+ unsigned int len = 0;
+ boolean_t success = B_TRUE;
+
+ /*
+ * locate root node
+ */
+
+ if ((root_node = di_init("/", DINFOCPYALL)) == DI_NODE_NIL ||
+ (phdl = di_prom_init()) == DI_PROM_HANDLE_NIL) {
+ dhcpmsg(MSG_DEBUG, "get_prom_prop: property root node "
+ "not found");
+ goto get_prom_prop_cleanup;
+ }
+
+ /*
+ * locate nodename within '/'
+ */
+
+ for (node = di_child_node(root_node);
+ node != DI_NODE_NIL;
+ node = di_sibling_node(node)) {
+ if (strcmp(di_node_name(node), nodename) == 0) {
+ break;
+ }
+ }
+
+ if (node == DI_NODE_NIL) {
+ dhcpmsg(MSG_DEBUG, "get_prom_prop: node not found");
+ goto get_prom_prop_cleanup;
+ }
+
+ /*
+ * scan all properties of /nodename for the 'propname' property
+ */
+
+ for (pp = di_prom_prop_next(phdl, node, DI_PROM_PROP_NIL);
+ pp != DI_PROM_PROP_NIL;
+ pp = di_prom_prop_next(phdl, node, pp)) {
+
+ dhcpmsg(MSG_DEBUG, "get_prom_prop: property = %s",
+ di_prom_prop_name(pp));
+
+ if (strcmp(propname, di_prom_prop_name(pp)) == 0) {
+ break;
+ }
+ }
+
+ if (pp == DI_PROM_PROP_NIL) {
+ dhcpmsg(MSG_DEBUG, "get_prom_prop: property not found");
+ goto get_prom_prop_cleanup;
+ }
+
+ /*
+ * get the property; allocate some memory copy it out
+ */
+
+ len = di_prom_prop_data(pp, (uchar_t **)&value);
+
+ if (value == NULL) {
+ /*
+ * property data read problems
+ */
+
+ success = B_FALSE;
+ dhcpmsg(MSG_ERR, "get_prom_prop: cannot read property data");
+ goto get_prom_prop_cleanup;
+ }
+
+ if (propvaluep != NULL) {
+ /*
+ * allocate somewhere to copy the property value to
+ */
+
+ *propvaluep = calloc(len, sizeof (uchar_t));
+
+ if (*propvaluep == NULL) {
+ /*
+ * allocation problems
+ */
+
+ success = B_FALSE;
+ dhcpmsg(MSG_ERR, "get_prom_prop: cannot allocate "
+ "memory for property value");
+ goto get_prom_prop_cleanup;
+ }
+
+ /*
+ * copy data out
+ */
+
+ (void) memcpy(*propvaluep, value, len);
+
+ /*
+ * copy out the length if a suitable pointer has
+ * been supplied
+ */
+
+ if (lenp != NULL) {
+ *lenp = len;
+ }
+
+ dhcpmsg(MSG_DEBUG, "get_prom_prop: property value "
+ "length = %d", len);
+ }
+
+get_prom_prop_cleanup:
+
+ if (phdl != DI_PROM_HANDLE_NIL) {
+ di_prom_fini(phdl);
+ }
+
+ if (root_node != DI_NODE_NIL) {
+ di_fini(root_node);
+ }
+
+ return (success);
+}
diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/interface.h b/usr/src/cmd/cmd-inet/sbin/dhcpagent/interface.h
new file mode 100644
index 0000000000..c525143060
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/interface.h
@@ -0,0 +1,385 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef INTERFACE_H
+#define INTERFACE_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * interface.[ch] encapsulate all of the agent's knowledge of network
+ * interfaces from the DHCP agent's perspective. see interface.c
+ * for documentation on how to use the exported functions. note that
+ * there are not functional interfaces for manipulating all of the fields
+ * in an ifslist -- please read the comments in the ifslist structure
+ * definition below for the rules on accessing various fields.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <net/if.h> /* IFNAMSIZ */
+#include <sys/types.h>
+#include <netinet/dhcp.h>
+#include <dhcpagent_ipc.h>
+#include <libinetutil.h>
+
+#include "async.h"
+#include "agent.h"
+#include "dlpi_io.h"
+#include "ipc_action.h"
+#include "packet.h"
+#include "util.h"
+
+enum { DHCP_T1_TIMER, DHCP_T2_TIMER, DHCP_LEASE_TIMER };
+
+typedef int script_callback_t (struct ifslist *, const char *);
+
+struct ifslist {
+
+ /*
+ * ifslist chain pointers, maintained by insert_ifs() /
+ * remove_ifs().
+ */
+
+ struct ifslist *next;
+ struct ifslist *prev;
+
+ /*
+ * hold count on this ifslist, maintained by hold_ifs() /
+ * release_ifs() -- see below for a discussion of ifs memory
+ * management.
+ */
+
+ uchar_t if_hold_count;
+
+ /*
+ * each interface can have at most one pending asynchronous
+ * action, which is represented in a `struct async_action'.
+ * if that asynchronous action was a result of a user request,
+ * then the `struct ipc_action' is used to hold information
+ * about the user request. these structures are opaque to
+ * users of the ifslist, and the functional interfaces
+ * provided in async.[ch] and ipc_action.[ch] should be used
+ * to maintain them.
+ */
+
+ struct ipc_action if_ia;
+ struct async_action if_async;
+
+ /*
+ * current state of the interface
+ */
+
+ DHCPSTATE if_state;
+
+ /*
+ * flags specific to DHCP (see dhcpagent_ipc.h)
+ */
+
+ uint16_t if_dflags;
+
+ /*
+ * general interface information -- this information is initialized
+ * in insert_ifs() and does not change over the lifetime of the
+ * interface.
+ */
+
+ char if_name[IFNAMSIZ];
+
+ uint16_t if_max; /* largest DHCP packet on this if */
+ uint16_t if_min; /* minimum mtu size on this if */
+ uint16_t if_opt; /* amount of space for options in PKT */
+
+ uchar_t *if_hwaddr; /* our link-layer address */
+ uchar_t if_hwlen; /* our link-layer address len */
+ uchar_t if_hwtype; /* type of link-layer */
+
+ uchar_t *if_cid; /* client id, if set in defaults file */
+ uchar_t if_cidlen; /* client id len */
+
+ uchar_t *if_prl; /* if non-NULL, param request list */
+ uchar_t if_prllen; /* param request list len */
+
+ /*
+ * the destination address is the broadcast address of
+ * the interface, in DLPI terms (which means it
+ * includes both a link-layer broadcast address and a
+ * sap, and the order isn't consistent.) fun, huh?
+ * blame AT&T. we store it as a token like this
+ * because it's generally how we need to use it. we
+ * can pull it apart using the saplen and sap_before
+ * fields below.
+ */
+
+ uchar_t *if_daddr; /* our destination address */
+ uchar_t if_dlen; /* our destination address len */
+
+ uchar_t if_saplen; /* the SAP len */
+ uchar_t if_sap_before; /* does SAP come before address? */
+
+ /*
+ * network descriptors; one is used for the DLPI
+ * traffic before we have our IP address configured;
+ * the other two are used afterwards. there have to
+ * be two socket descriptors since:
+ *
+ * o we need one to be bound to IPPORT_BOOTPC and
+ * and INADDR_BROADCAST, so it can receive all
+ * broadcast traffic. this is if_sock_fd. it
+ * is also used as a general descriptor to perform
+ * socket-related ioctls on, like SIOCGIFFLAGS.
+ *
+ * o we need another to be bound to IPPORT_BOOTPC and
+ * the IP address given to us by the DHCP server,
+ * so we can guarantee the IP address of outgoing
+ * packets when multihomed. (the problem being that
+ * if a packet goes out with the wrong IP address,
+ * then the server's response will come back on the
+ * wrong interface). this is if_sock_ip_fd.
+ *
+ * note that if_sock_fd is created in init_ifs() but
+ * not bound until dhcp_bound(); this is because we
+ * cannot even bind to the broadcast address until we
+ * have an IP address.
+ *
+ * if_sock_ip_fd isn't created until dhcp_bound(),
+ * since we don't need it until then and we can't
+ * bind it until after we have an IP address anyway.
+ *
+ * both socket descriptors are closed in reset_ifs().
+ */
+
+ int if_dlpi_fd;
+ int if_sock_fd;
+ int if_sock_ip_fd;
+
+ /*
+ * the following fields are set when a lease is acquired, and
+ * may be updated over the lifetime of the lease. they are
+ * all reset by reset_ifs().
+ */
+
+ iu_timer_id_t if_timer[3]; /* T1, T2, and LEASE timers */
+
+ lease_t if_t1; /* relative renewal start time, hbo */
+ lease_t if_t2; /* relative rebinding start time, hbo */
+ lease_t if_lease; /* relative expire time, hbo */
+
+ unsigned int if_nrouters; /* the number of default routers */
+ struct in_addr *if_routers; /* an array of default routers */
+ struct in_addr if_server; /* our DHCP server, nbo */
+
+ /*
+ * while in any states except ADOPTING, INIT, INFORMATION and
+ * INFORM_SENT, the following three fields are equal to what
+ * we believe the current address, netmask, and broadcast
+ * address on the interface to be. this is so we can detect
+ * if the user changes them and abandon the interface.
+ */
+
+ struct in_addr if_addr; /* our IP address, nbo */
+ struct in_addr if_netmask; /* our netmask, nbo */
+ struct in_addr if_broadcast; /* our broadcast address, nbo */
+
+ PKT_LIST *if_ack; /* ACK from the server */
+
+ /*
+ * We retain the very first ack obtained on the interface to
+ * provide access to options which were originally assigned by
+ * the server but may not have been included in subsequent
+ * acks, as there are servers which do this and customers have
+ * had unsatisfactory results when using our agent with them.
+ * ipc_event() in agent.c provides a fallback to the original
+ * ack when the current ack doesn't have the information
+ * requested.
+ */
+
+ PKT_LIST *if_orig_ack;
+
+ /*
+ * other miscellaneous variables set or needed in the process
+ * of acquiring a lease.
+ */
+
+ int if_offer_wait; /* seconds between offers */
+ iu_event_id_t if_offer_id; /* event offer id */
+ iu_event_id_t if_acknak_id; /* event acknak id */
+ iu_event_id_t if_acknak_bcast_id;
+
+ /*
+ * `if_neg_monosec' represents the time since lease
+ * acquisition or renewal began, and is used for
+ * computing the pkt->secs field. `if_newstart_monosec'
+ * represents the time the ACKed REQUEST was sent,
+ * which represents the start time of a new lease.
+ * when the lease actually begins (and thus becomes
+ * current), `if_curstart_monosec' is set to
+ * `if_newstart_monosec'.
+ */
+
+ monosec_t if_neg_monosec;
+ monosec_t if_newstart_monosec;
+ monosec_t if_curstart_monosec;
+
+ /*
+ * time we sent the DISCOVER relative to if_neg_monosec,
+ * so that the REQUEST can have the same pkt->secs.
+ */
+
+ uint16_t if_disc_secs;
+
+ /*
+ * the host name we've been asked to request is remembered
+ * here between the DISCOVER and the REQUEST
+ */
+ char *if_reqhost;
+
+ /*
+ * this is a chain of packets which have been received on this
+ * interface over some interval of time. the packets may have
+ * to meet some criteria in order to be put on this list. in
+ * general, packets are put on this list through recv_pkt()
+ */
+
+ PKT_LIST *if_recv_pkt_list;
+
+ /*
+ * these three fields are initially zero, and get incremented
+ * as the ifslist goes from INIT -> BOUND. if and when the
+ * ifslist moves to the RENEWING state, these fields are
+ * reset, so they always either indicate the number of packets
+ * sent, received, and declined while obtaining the current
+ * lease (if BOUND), or the number of packets sent, received,
+ * and declined while attempting to obtain a future lease
+ * (if any other state).
+ */
+
+ uint32_t if_sent;
+ uint32_t if_received;
+ uint32_t if_bad_offers;
+
+ /*
+ * if_send_pkt.pkt is dynamically allocated to be as big a
+ * packet as we can send out on this interface. the remainder
+ * of this information is needed to make it easy to handle
+ * retransmissions. note that other than if_bad_offers, all
+ * of these fields are maintained internally in send_pkt(),
+ * and consequently should never need to be modified by any
+ * other functions.
+ */
+
+ dhcp_pkt_t if_send_pkt;
+ uint32_t if_send_timeout;
+ struct sockaddr_in if_send_dest;
+ stop_func_t *if_send_stop_func;
+ uint32_t if_packet_sent;
+ iu_timer_id_t if_retrans_timer;
+
+ int if_script_fd;
+ pid_t if_script_pid;
+ pid_t if_script_helper_pid;
+ const char *if_script_event;
+ iu_event_id_t if_script_event_id;
+ const char *if_callback_msg;
+ script_callback_t *if_script_callback;
+};
+
+/*
+ * a word on memory management and ifslists:
+ *
+ * since ifslists are often passed as context to callback functions,
+ * they cannot be freed when the interface they represent is dropped
+ * or released (or when those callbacks finally go off, they will be
+ * hosed). to handle this situation, ifslists are reference counted.
+ * here are the rules for managing ifslists:
+ *
+ * an ifslist is created through insert_ifs(). along with
+ * initializing the ifslist, this puts a hold on the ifslist through
+ * hold_ifs().
+ *
+ * whenever an ifslist is released or dropped (implicitly or
+ * explicitly), remove_ifs() is called, which sets the DHCP_IF_REMOVED
+ * flag and removes the interface from the internal list of managed
+ * interfaces. lastly, remove_ifs() calls release_ifs() to remove the
+ * hold acquired in insert_ifs(). if this decrements the hold count
+ * on the interface to zero, then free_ifs() is called. if there are
+ * holds other than the hold acquired in insert_ifs(), the hold count
+ * will still be > 0, and the interface will remain allocated (though
+ * dormant).
+ *
+ * whenever a callback is scheduled against an ifslist, another hold
+ * must be put on the ifslist through hold_ifs().
+ *
+ * whenever a callback is called back against an ifslist,
+ * release_ifs() must be called to decrement the hold count, which may
+ * end up freeing the ifslist if the hold count becomes zero.
+ *
+ * if release_ifs() returns 0, then there are no remaining holds
+ * against this ifslist, and the ifslist in fact no longer exists.
+ *
+ * since some callbacks may take a long time to get called back (such
+ * as timeout callbacks for lease expiration, etc), it is sometimes
+ * more appropriate to cancel the callbacks and call release_ifs() if
+ * the cancellation succeeds. this is done in remove_ifs() for the
+ * lease, t1, and t2 callbacks.
+ *
+ * in general, a callback should also call verify_ifs() when it gets
+ * called back in addition to release_ifs(), to make sure that the
+ * interface is still in fact under the dhcpagent's control. to make
+ * coding simpler, there is a third function, check_ifs(), which
+ * performs both the release_ifs() and the verify_ifs(). in addition,
+ * if check_ifs() detects that the callback has the last hold against
+ * a given interface, it informs it instead of performing the final
+ * release, and thus allows it to clean up appropriately before
+ * performing the final release.
+ */
+
+int canonize_ifs(struct ifslist *);
+int check_ifs(struct ifslist *);
+void hold_ifs(struct ifslist *);
+struct ifslist *insert_ifs(const char *, boolean_t, int *);
+struct ifslist *lookup_ifs(const char *);
+struct ifslist *lookup_ifs_by_xid(uint32_t);
+void nuke_ifslist(boolean_t);
+void refresh_ifslist(iu_eh_t *, int, void *);
+int release_ifs(struct ifslist *);
+void remove_ifs(struct ifslist *);
+void reset_ifs(struct ifslist *);
+int verify_ifs(struct ifslist *);
+unsigned int ifs_count(void);
+void cancel_ifs_timers(struct ifslist *);
+int schedule_ifs_timer(struct ifslist *, int, uint32_t,
+ iu_tq_callback_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* INTERFACE_H */
diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/ipc_action.c b/usr/src/cmd/cmd-inet/sbin/dhcpagent/ipc_action.c
new file mode 100644
index 0000000000..f29730d377
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/ipc_action.c
@@ -0,0 +1,187 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/poll.h>
+#include <dhcpmsg.h>
+
+#include "interface.h"
+#include "ipc_action.h"
+#include "util.h"
+
+static iu_tq_callback_t ipc_action_timeout;
+
+/*
+ * ipc_action_init(): initializes the ipc_action structure
+ *
+ * input: struct ifslist *: the interface to initialize it for
+ * output: void
+ */
+
+void
+ipc_action_init(struct ifslist *ifsp)
+{
+ ifsp->if_ia.ia_tid = -1;
+}
+
+/*
+ * ipc_action_start(): starts an ipc_action request on an interface
+ *
+ * input: struct ifslist *: the interface to start the action on
+ * dhcp_ipc_request_t *: the type of request
+ * int: the descriptor to contact the action requestor
+ * output: int: 1 if the request is started successfully, 0 otherwise
+ */
+
+int
+ipc_action_start(struct ifslist *ifsp, dhcp_ipc_request_t *request, int fd)
+{
+ if (request->timeout == DHCP_IPC_WAIT_DEFAULT)
+ request->timeout = DHCP_IPC_DEFAULT_WAIT;
+
+ ifsp->if_ia.ia_request = request;
+ ifsp->if_ia.ia_fd = fd;
+ ifsp->if_ia.ia_cmd = DHCP_IPC_CMD(request->message_type);
+
+ if (request->timeout == DHCP_IPC_WAIT_FOREVER)
+ ifsp->if_ia.ia_tid = -1;
+ else {
+ ifsp->if_ia.ia_tid = iu_schedule_timer(tq, request->timeout,
+ ipc_action_timeout, ifsp);
+
+ if (ifsp->if_ia.ia_tid == -1)
+ return (0);
+
+ hold_ifs(ifsp);
+ }
+
+ return (1);
+}
+
+/*
+ * ipc_action_pending(): checks if there is a pending ipc_action request on
+ * an interface
+ *
+ * input: struct ifslist *: the interface to check for a pending ipc_action on
+ * output: boolean_t: B_TRUE if there is a pending ipc_action request
+ */
+
+boolean_t
+ipc_action_pending(struct ifslist *ifsp)
+{
+ struct pollfd pollfd;
+
+ if (ifsp->if_ia.ia_fd > 0) {
+
+ pollfd.events = POLLIN;
+ pollfd.fd = ifsp->if_ia.ia_fd;
+
+ if (poll(&pollfd, 1, 0) == 0)
+ return (B_TRUE);
+ }
+
+ return (B_FALSE);
+}
+
+/*
+ * ipc_action_finish(): completes an ipc_action request on an interface
+ *
+ * input: struct ifslist *: the interface to complete the action on
+ * int: the reason why the action finished (nonzero on error)
+ * output: void
+ */
+
+void
+ipc_action_finish(struct ifslist *ifsp, int reason)
+{
+ struct ipc_action *ia = &ifsp->if_ia;
+
+ if (ipc_action_pending(ifsp) == B_FALSE)
+ return;
+
+ if (reason == 0)
+ send_ok_reply(ia->ia_request, &ia->ia_fd);
+ else
+ send_error_reply(ia->ia_request, reason, &ia->ia_fd);
+
+ ipc_action_cancel_timer(ifsp);
+}
+
+/*
+ * ipc_action_timeout(): times out an ipc_action on an interface (the request
+ * continues asynchronously, however)
+ *
+ * input: iu_tq_t *: unused
+ * void *: the struct ifslist * the ipc_action was pending on
+ * output: void
+ */
+
+/* ARGSUSED */
+static void
+ipc_action_timeout(iu_tq_t *tq, void *arg)
+{
+ struct ifslist *ifsp = (struct ifslist *)arg;
+ struct ipc_action *ia = &ifsp->if_ia;
+
+ if (check_ifs(ifsp) == 0) {
+ (void) release_ifs(ifsp);
+ return;
+ }
+
+ dhcpmsg(MSG_VERBOSE, "ipc timeout waiting for agent to complete "
+ "command %d for %s", ia->ia_cmd, ifsp->if_name);
+
+ send_error_reply(ia->ia_request, DHCP_IPC_E_TIMEOUT, &ia->ia_fd);
+ ia->ia_tid = -1;
+}
+
+/*
+ * ipc_action_cancel_timer(): cancels the pending ipc_action timer for this
+ * request
+ *
+ * input: struct ifslist *: the interface with a pending request to cancel
+ * output: void
+ */
+
+void
+ipc_action_cancel_timer(struct ifslist *ifsp)
+{
+ if (ifsp->if_ia.ia_tid == -1)
+ return;
+
+ /*
+ * if we can't cancel this timer, we're really in the
+ * twilight zone. however, as long as we don't drop the
+ * reference to the ifsp, it shouldn't hurt us
+ */
+
+ if (iu_cancel_timer(tq, ifsp->if_ia.ia_tid, NULL) == 1) {
+ ifsp->if_ia.ia_tid = -1;
+ (void) release_ifs(ifsp);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/ipc_action.h b/usr/src/cmd/cmd-inet/sbin/dhcpagent/ipc_action.h
new file mode 100644
index 0000000000..8ecc62189e
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/ipc_action.h
@@ -0,0 +1,71 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef IPC_ACTION_H
+#define IPC_ACTION_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <netinet/dhcp.h>
+#include <dhcpagent_ipc.h>
+#include <libinetutil.h>
+
+#include "agent.h"
+
+/*
+ * ipc_action.[ch] make up the interface used to control the current
+ * pending interprocess communication transaction taking place. see
+ * ipc_action.c for documentation on how to use the exported functions.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ifslist; /* forward declaration */
+
+void ipc_action_init(struct ifslist *);
+int ipc_action_start(struct ifslist *, dhcp_ipc_request_t *, int);
+void ipc_action_finish(struct ifslist *, int);
+boolean_t ipc_action_pending(struct ifslist *);
+void ipc_action_cancel_timer(struct ifslist *);
+
+
+struct ipc_action {
+
+ dhcp_ipc_type_t ia_cmd; /* command/action requested */
+ int ia_fd; /* ipc channel descriptor */
+ iu_timer_id_t ia_tid; /* ipc timer id */
+ dhcp_ipc_request_t *ia_request; /* ipc request pointer */
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* IPC_ACTION_H */
diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/packet.c b/usr/src/cmd/cmd-inet/sbin/dhcpagent/packet.c
new file mode 100644
index 0000000000..4beb5524c0
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/packet.c
@@ -0,0 +1,729 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <string.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <arpa/inet.h>
+#include <dhcpmsg.h>
+#include <stddef.h>
+#include <assert.h>
+
+#include "states.h"
+#include "interface.h"
+#include "agent.h"
+#include "packet.h"
+#include "util.h"
+
+static double fuzzify(uint32_t, double);
+static void retransmit(iu_tq_t *, void *);
+static uint32_t next_retransmission(uint32_t);
+static int send_pkt_internal(struct ifslist *);
+static uchar_t pkt_type(PKT *);
+
+/*
+ * dhcp_type_ptob(): converts the DHCP packet type values in RFC2131 into
+ * values which can be used for recv_pkt()
+ *
+ * input: uchar_t: a DHCP packet type value, as defined in RFC2131
+ * output: dhcp_message_type_t: a packet type value for use with recv_pkt()
+ */
+
+static dhcp_message_type_t
+dhcp_type_ptob(uchar_t type)
+{
+ /*
+ * note: the ordering here allows direct indexing of the table
+ * based on the RFC2131 packet type value passed in.
+ */
+
+ static dhcp_message_type_t type_map[] = {
+ DHCP_PUNTYPED, DHCP_PDISCOVER, DHCP_POFFER, DHCP_PREQUEST,
+ DHCP_PDECLINE, DHCP_PACK, DHCP_PNAK, DHCP_PRELEASE, DHCP_PINFORM
+ };
+
+ if (type < (sizeof (type_map) / sizeof (*type_map)))
+ return (type_map[type]);
+
+ return (0);
+}
+
+/*
+ * pkt_type(): returns an integer representing the packet's type; only
+ * for use with outbound packets.
+ *
+ * input: PKT *: the packet to examine
+ * output: uchar_t: the packet type (0 if unknown)
+ */
+
+static uchar_t
+pkt_type(PKT *pkt)
+{
+ uchar_t *option = pkt->options;
+
+ /*
+ * this is a little dirty but it should get the job done.
+ * assumes that the type is in the statically allocated part
+ * of the options field.
+ */
+
+ while (*option != CD_DHCP_TYPE) {
+ if (option + 2 - pkt->options >= sizeof (pkt->options))
+ return (0);
+
+ option++;
+ option += *option;
+ }
+
+ return (option[2]);
+}
+
+/*
+ * init_pkt(): initializes and returns a packet of a given type
+ *
+ * input: struct ifslist *: the interface the packet will be going out
+ * uchar_t: the packet type (DHCP message type)
+ * output: dhcp_pkt_t *: a pointer to the initialized packet
+ */
+
+dhcp_pkt_t *
+init_pkt(struct ifslist *ifsp, uchar_t type)
+{
+ uint8_t bootmagic[] = BOOTMAGIC;
+ dhcp_pkt_t *dpkt = &ifsp->if_send_pkt;
+ uint32_t xid;
+
+ dpkt->pkt_max_len = ifsp->if_max;
+ dpkt->pkt_cur_len = offsetof(PKT, options);
+
+ (void) memset(dpkt->pkt, 0, ifsp->if_max);
+ (void) memcpy(dpkt->pkt->cookie, bootmagic, sizeof (bootmagic));
+ if (ifsp->if_hwlen <= sizeof (dpkt->pkt->chaddr)) {
+ dpkt->pkt->hlen = ifsp->if_hwlen;
+ (void) memcpy(dpkt->pkt->chaddr, ifsp->if_hwaddr,
+ ifsp->if_hwlen);
+ } else {
+ /*
+ * The mac address does not fit in the chaddr
+ * field, thus it can not be sent to the server,
+ * thus server can not unicast the reply. Per
+ * RFC 2131 4.4.1, client can set this bit in
+ * DISCOVER/REQUEST. If the client is already
+ * in BOUND/REBINDING/RENEWING state, do not set
+ * this bit, as it can respond to unicast responses
+ * from server using the 'ciaddr' address.
+ */
+ if ((type == DISCOVER) || ((type == REQUEST) &&
+ (ifsp->if_state != RENEWING) &&
+ (ifsp->if_state != REBINDING) &&
+ (ifsp->if_state != BOUND)))
+ dpkt->pkt->flags = htons(BCAST_MASK);
+ }
+
+ /*
+ * since multiple dhcp leases may be maintained over the same dlpi
+ * device (e.g. "hme0" and "hme0:1"), make sure the xid is unique.
+ */
+
+ do {
+ xid = mrand48();
+ } while (lookup_ifs_by_xid(xid) != NULL);
+
+ dpkt->pkt->xid = xid;
+ dpkt->pkt->op = BOOTREQUEST;
+ dpkt->pkt->htype = ifsp->if_hwtype;
+
+ add_pkt_opt(dpkt, CD_DHCP_TYPE, &type, 1);
+ add_pkt_opt(dpkt, CD_CLIENT_ID, ifsp->if_cid, ifsp->if_cidlen);
+
+ return (dpkt);
+}
+
+/*
+ * add_pkt_opt(): adds an option to a dhcp_pkt_t
+ *
+ * input: dhcp_pkt_t *: the packet to add the option to
+ * uchar_t: the type of option being added
+ * const void *: the value of that option
+ * uchar_t: the length of the value of the option
+ * output: void
+ */
+
+void
+add_pkt_opt(dhcp_pkt_t *dpkt, uchar_t opt_type, const void *opt_val,
+ uchar_t opt_len)
+{
+ caddr_t raw_pkt = (caddr_t)dpkt->pkt;
+ int16_t req_len = opt_len + 2; /* + 2 for code & length bytes */
+
+ /* CD_END and CD_PAD options don't have a length field */
+ if (opt_type == CD_END || opt_type == CD_PAD)
+ req_len--;
+ else if (opt_val == NULL)
+ return;
+
+ if ((dpkt->pkt_cur_len + req_len) > dpkt->pkt_max_len) {
+ dhcpmsg(MSG_WARNING, "add_pkt_opt: not enough room for option "
+ "%d in packet", opt_type);
+ return;
+ }
+
+ raw_pkt[dpkt->pkt_cur_len++] = opt_type;
+
+ if (opt_len > 0) {
+ raw_pkt[dpkt->pkt_cur_len++] = opt_len;
+ (void) memcpy(&raw_pkt[dpkt->pkt_cur_len], opt_val, opt_len);
+ dpkt->pkt_cur_len += opt_len;
+ }
+}
+
+/*
+ * add_pkt_opt16(): adds an option with a 16-bit value to a dhcp_pkt_t
+ *
+ * input: dhcp_pkt_t *: the packet to add the option to
+ * uchar_t: the type of option being added
+ * uint16_t: the value of that option
+ * output: void
+ */
+
+void
+add_pkt_opt16(dhcp_pkt_t *dpkt, uchar_t opt_type, uint16_t opt_value)
+{
+ add_pkt_opt(dpkt, opt_type, &opt_value, 2);
+}
+
+/*
+ * add_pkt_opt32(): adds an option with a 32-bit value to a dhcp_pkt_t
+ *
+ * input: dhcp_pkt_t *: the packet to add the option to
+ * uchar_t: the type of option being added
+ * uint32_t: the value of that option
+ * output: void
+ */
+
+void
+add_pkt_opt32(dhcp_pkt_t *dpkt, uchar_t opt_type, uint32_t opt_value)
+{
+ add_pkt_opt(dpkt, opt_type, &opt_value, 4);
+}
+
+/*
+ * get_pkt_times(): pulls the lease times out of a packet and stores them as
+ * host-byteorder relative times in the passed in parameters
+ *
+ * input: PKT_LIST *: the packet to pull the packet times from
+ * lease_t *: where to store the relative lease time in hbo
+ * lease_t *: where to store the relative t1 time in hbo
+ * lease_t *: where to store the relative t2 time in hbo
+ * output: void
+ */
+
+void
+get_pkt_times(PKT_LIST *ack, lease_t *lease, lease_t *t1, lease_t *t2)
+{
+ *lease = DHCP_PERM;
+ *t1 = DHCP_PERM;
+ *t2 = DHCP_PERM;
+
+ if (ack->opts[CD_DHCP_TYPE] == NULL ||
+ ack->opts[CD_LEASE_TIME] == NULL ||
+ ack->opts[CD_LEASE_TIME]->len != sizeof (lease_t))
+ return;
+
+ (void) memcpy(lease, ack->opts[CD_LEASE_TIME]->value, sizeof (lease_t));
+ *lease = ntohl(*lease);
+
+ if (*lease == DHCP_PERM)
+ return;
+
+ if (ack->opts[CD_T1_TIME] != NULL &&
+ ack->opts[CD_T1_TIME]->len == sizeof (lease_t)) {
+ (void) memcpy(t1, ack->opts[CD_T1_TIME]->value, sizeof (*t1));
+ *t1 = ntohl(*t1);
+ }
+
+ if ((*t1 == DHCP_PERM) || (*t1 >= *lease))
+ *t1 = (lease_t)fuzzify(*lease, DHCP_T1_FACT);
+
+ if (ack->opts[CD_T2_TIME] != NULL &&
+ ack->opts[CD_T2_TIME]->len == sizeof (lease_t)) {
+ (void) memcpy(t2, ack->opts[CD_T2_TIME]->value, sizeof (*t2));
+ *t2 = ntohl(*t2);
+ }
+
+ if ((*t2 == DHCP_PERM) || (*t2 > *lease) || (*t2 <= *t1))
+ *t2 = (lease_t)fuzzify(*lease, DHCP_T2_FACT);
+}
+
+/*
+ * fuzzify(): adds some "fuzz" to a t1/t2 time, in accordance with RFC2131
+ *
+ * input: uint32_t: the number of seconds until lease expiration
+ * double: the approximate percentage of that time to return
+ * output: double: a number approximating (sec * pct)
+ */
+
+static double
+fuzzify(uint32_t sec, double pct)
+{
+ return (sec * (pct + (drand48() - 0.5) / 25.0));
+}
+
+/*
+ * free_pkt_list(): frees a packet list
+ *
+ * input: PKT_LIST **: the packet list to free
+ * output: void
+ */
+
+void
+free_pkt_list(PKT_LIST **plp)
+{
+ PKT_LIST *plp_next;
+
+ for (; *plp != NULL; *plp = plp_next) {
+ plp_next = (*plp)->next;
+ free((*plp)->pkt);
+ free(*plp);
+ }
+}
+
+/*
+ * prepend_to_pkt_list(): prepends a packet to a packet list
+ *
+ * input: PKT_LIST **: the packet list
+ * PKT_LIST *: the packet to prepend
+ * output: void
+ */
+
+static void
+prepend_to_pkt_list(PKT_LIST **list_head, PKT_LIST *new_entry)
+{
+ new_entry->next = *list_head;
+ new_entry->prev = NULL;
+
+ if (*list_head != NULL)
+ (*list_head)->prev = new_entry;
+
+ *list_head = new_entry;
+}
+
+/*
+ * remove_from_pkt_list(): removes a given packet from a packet list
+ *
+ * input: PKT_LIST **: the packet list
+ * PKT_LIST *: the packet to remove
+ * output: void
+ */
+
+void
+remove_from_pkt_list(PKT_LIST **list_head, PKT_LIST *remove)
+{
+ if (*list_head == NULL)
+ return;
+
+ if (*list_head == remove) {
+ *list_head = remove->next;
+ if (*list_head != NULL)
+ (*list_head)->prev = NULL;
+ } else {
+ remove->prev->next = remove->next;
+ if (remove->next != NULL)
+ remove->next->prev = remove->prev;
+ }
+
+ remove->next = NULL;
+ remove->prev = NULL;
+}
+
+/*
+ * send_pkt_internal(): sends a packet out on an interface
+ *
+ * input: struct ifslist *: the interface to send the packet out on
+ * output: int: 1 if the packet is sent, 0 otherwise
+ */
+
+static int
+send_pkt_internal(struct ifslist *ifsp)
+{
+ ssize_t n_bytes;
+ dhcp_pkt_t *dpkt = &ifsp->if_send_pkt;
+ const char *pkt_name = pkt_type_to_string(pkt_type(dpkt->pkt));
+
+ /*
+ * if needed, schedule a retransmission timer, then attempt to
+ * send the packet. if we fail, then log the error. our
+ * return value should indicate whether or not we were
+ * successful in sending the request, independent of whether
+ * we could schedule a timer.
+ */
+
+ if (ifsp->if_send_timeout != 0) {
+ if ((ifsp->if_retrans_timer = iu_schedule_timer_ms(tq,
+ ifsp->if_send_timeout, retransmit, ifsp)) == -1)
+ dhcpmsg(MSG_WARNING, "send_pkt_internal: cannot "
+ "schedule retransmit timer for %s packet",
+ pkt_name);
+ else
+ hold_ifs(ifsp);
+ }
+
+ /*
+ * set the `pkt->secs' field depending on the type of packet.
+ * it should be zero, except in the following cases:
+ *
+ * DISCOVER: set to the number of seconds since we started
+ * trying to obtain a lease.
+ *
+ * INFORM: set to the number of seconds since we started
+ * trying to get configuration parameters.
+ *
+ * REQUEST: if in the REQUESTING state, then same value as
+ * DISCOVER, otherwise the number of seconds
+ * since we started trying to obtain a lease.
+ *
+ * we also set `if_newstart_monosec', to the time we sent a
+ * REQUEST or DISCOVER packet, so we know the lease start
+ * time (the DISCOVER case is for handling BOOTP servers).
+ */
+
+ switch (pkt_type(dpkt->pkt)) {
+
+ case DISCOVER:
+ ifsp->if_newstart_monosec = monosec();
+ ifsp->if_disc_secs = monosec() - ifsp->if_neg_monosec;
+ dpkt->pkt->secs = htons(ifsp->if_disc_secs);
+ break;
+
+ case INFORM:
+ dpkt->pkt->secs = htons(monosec() - ifsp->if_neg_monosec);
+ break;
+
+ case REQUEST:
+ ifsp->if_newstart_monosec = monosec();
+
+ if (ifsp->if_state == REQUESTING) {
+ dpkt->pkt->secs = htons(ifsp->if_disc_secs);
+ break;
+ }
+
+ dpkt->pkt->secs = htons(monosec() - ifsp->if_neg_monosec);
+ break;
+
+ default:
+ dpkt->pkt->secs = htons(0);
+ }
+
+ switch (ifsp->if_state) {
+
+ case BOUND:
+ case RENEWING:
+ case REBINDING:
+ n_bytes = sendto(ifsp->if_sock_ip_fd, dpkt->pkt,
+ dpkt->pkt_cur_len, 0,
+ (struct sockaddr *)&ifsp->if_send_dest,
+ sizeof (struct sockaddr_in));
+ break;
+
+ default:
+ n_bytes = dlpi_sendto(ifsp->if_dlpi_fd, dpkt->pkt,
+ dpkt->pkt_cur_len, &ifsp->if_send_dest,
+ ifsp->if_daddr, ifsp->if_dlen);
+ break;
+ }
+
+ if (n_bytes != dpkt->pkt_cur_len) {
+ if (ifsp->if_retrans_timer == -1)
+ dhcpmsg(MSG_WARNING, "send_pkt_internal: cannot send "
+ "%s packet to server", pkt_name);
+ else
+ dhcpmsg(MSG_WARNING, "send_pkt_internal: cannot send "
+ "%s packet to server (will retry in %u seconds)",
+ pkt_name, ifsp->if_send_timeout / MILLISEC);
+ return (0);
+ }
+
+ dhcpmsg(MSG_VERBOSE, "sent %s packet out %s", pkt_name,
+ ifsp->if_name);
+
+ ifsp->if_packet_sent++;
+ ifsp->if_sent++;
+ return (1);
+}
+
+/*
+ * send_pkt(): sends a packet out on an interface
+ *
+ * input: struct ifslist *: the interface to send the packet out on
+ * dhcp_pkt_t *: the packet to send out
+ * in_addr_t: the destination IP address for the packet
+ * stop_func_t *: a pointer to function to indicate when to stop
+ * retransmitting the packet (if NULL, packet is
+ * not retransmitted)
+ * output: int: 1 if the packet was sent, 0 otherwise
+ */
+
+int
+send_pkt(struct ifslist *ifsp, dhcp_pkt_t *dpkt, in_addr_t dest,
+ stop_func_t *stop)
+{
+ /*
+ * packets must be at least sizeof (PKT) or they may be dropped
+ * by routers. pad out the packet in this case.
+ */
+
+ dpkt->pkt_cur_len = MAX(dpkt->pkt_cur_len, sizeof (PKT));
+
+ ifsp->if_packet_sent = 0;
+
+ (void) memset(&ifsp->if_send_dest, 0, sizeof (ifsp->if_send_dest));
+ ifsp->if_send_dest.sin_addr.s_addr = dest;
+ ifsp->if_send_dest.sin_family = AF_INET;
+ ifsp->if_send_dest.sin_port = htons(IPPORT_BOOTPS);
+ ifsp->if_send_stop_func = stop;
+
+ /*
+ * TODO: dispose of this gruesome assumption (there's no real
+ * technical gain from doing so, but it would be cleaner)
+ */
+
+ assert(dpkt == &ifsp->if_send_pkt);
+
+ /*
+ * clear out any packets which had been previously received
+ * but not pulled off of the recv_packet queue.
+ */
+
+ free_pkt_list(&ifsp->if_recv_pkt_list);
+
+ if (stop == NULL) {
+ ifsp->if_retrans_timer = -1;
+ ifsp->if_send_timeout = 0; /* prevents retransmissions */
+ } else
+ ifsp->if_send_timeout = next_retransmission(0);
+
+ return (send_pkt_internal(ifsp));
+}
+
+/*
+ * retransmit(): retransmits the current packet on an interface
+ *
+ * input: iu_tq_t *: unused
+ * void *: the struct ifslist * to send the packet on
+ * output: void
+ */
+
+/* ARGSUSED */
+static void
+retransmit(iu_tq_t *tqp, void *arg)
+{
+ struct ifslist *ifsp = (struct ifslist *)arg;
+
+ if (check_ifs(ifsp) == 0) {
+ (void) release_ifs(ifsp);
+ return;
+ }
+
+ /*
+ * check the callback to see if we should keep sending retransmissions
+ */
+
+ if (ifsp->if_send_stop_func(ifsp, ifsp->if_packet_sent))
+ return;
+
+ ifsp->if_send_timeout = next_retransmission(ifsp->if_send_timeout);
+ (void) send_pkt_internal(ifsp);
+}
+
+/*
+ * stop_pkt_retransmission(): stops retransmission of last sent packet
+ *
+ * input: struct ifslist *: the interface to stop retransmission on
+ * output: void
+ */
+
+void
+stop_pkt_retransmission(struct ifslist *ifsp)
+{
+ if (ifsp->if_retrans_timer != -1) {
+ if (iu_cancel_timer(tq, ifsp->if_retrans_timer, NULL) == 1) {
+ (void) release_ifs(ifsp);
+ ifsp->if_retrans_timer = -1;
+ }
+ }
+}
+
+/*
+ * recv_pkt(): receives packets on an interface (put on ifsp->if_recv_pkt_list)
+ *
+ * input: struct ifslist *: the interface to receive packets on
+ * int: the file descriptor to receive the packet on
+ * dhcp_message_type_t: the types of packets to receive
+ * boolean_t: if B_TRUE, more than one packet can be received
+ * output: int: 1 if a packet was received successfully, 0 otherwise
+ */
+
+int
+recv_pkt(struct ifslist *ifsp, int fd, dhcp_message_type_t type,
+ boolean_t chain)
+{
+ PKT_LIST *plp;
+ PKT *pkt;
+ ssize_t retval;
+ uchar_t recv_pkt_type;
+ const char *recv_pkt_name;
+
+ /*
+ * collect replies. chain them up if the chain flag is set
+ * and we've already got one, otherwise drop the packet.
+ * calloc the PKT_LIST since dhcp_options_scan() relies on it
+ * being zeroed.
+ */
+
+ pkt = calloc(1, ifsp->if_max);
+ plp = calloc(1, sizeof (PKT_LIST));
+ if (pkt == NULL || plp == NULL) {
+ dhcpmsg(MSG_ERR, "recv_pkt: dropped packet");
+ goto failure;
+ }
+
+ plp->pkt = pkt;
+
+ switch (ifsp->if_state) {
+
+ case BOUND:
+ case RENEWING:
+ case REBINDING:
+ retval = recvfrom(fd, pkt, ifsp->if_max, 0, NULL, 0);
+ break;
+
+ default:
+ retval = dlpi_recvfrom(fd, pkt, ifsp->if_max, 0);
+ break;
+ }
+
+ if (retval == -1) {
+ dhcpmsg(MSG_ERR, "recv_pkt: recvfrom failed, dropped");
+ goto failure;
+ }
+
+ plp->len = retval;
+
+ switch (dhcp_options_scan(plp, B_TRUE)) {
+
+ case DHCP_WRONG_MSG_TYPE:
+ dhcpmsg(MSG_WARNING, "recv_pkt: unexpected DHCP message");
+ goto failure;
+
+ case DHCP_GARBLED_MSG_TYPE:
+ dhcpmsg(MSG_WARNING, "recv_pkt: garbled DHCP message type");
+ goto failure;
+
+ case DHCP_BAD_OPT_OVLD:
+ dhcpmsg(MSG_WARNING, "recv_pkt: bad option overload");
+ goto failure;
+
+ case 0:
+ break;
+
+ default:
+ dhcpmsg(MSG_WARNING, "recv_pkt: packet corrupted, dropped");
+ goto failure;
+ }
+
+ /*
+ * make sure the packet we got in was one we were expecting --
+ * it needs to have the right type and to have the same xid.
+ */
+
+ if (plp->opts[CD_DHCP_TYPE] != NULL)
+ recv_pkt_type = *plp->opts[CD_DHCP_TYPE]->value;
+ else
+ recv_pkt_type = 0;
+
+ recv_pkt_name = pkt_type_to_string(recv_pkt_type);
+
+ if ((dhcp_type_ptob(recv_pkt_type) & type) == 0) {
+ dhcpmsg(MSG_VERBOSE, "received unexpected %s packet on "
+ "%s, dropped", recv_pkt_name, ifsp->if_name);
+ goto failure;
+ }
+
+ /* the xid is opaque -- no byteorder work */
+ if (plp->pkt->xid != ifsp->if_send_pkt.pkt->xid) {
+ dhcpmsg(MSG_VERBOSE, "received unexpected packet xid (%#x "
+ "instead of %#x) on %s, dropped", plp->pkt->xid,
+ ifsp->if_send_pkt.pkt->xid, ifsp->if_name);
+ goto failure;
+ }
+
+ if (ifsp->if_recv_pkt_list != NULL) {
+ if (chain == B_FALSE) {
+ dhcpmsg(MSG_WARNING, "recv_pkt: unexpected additional "
+ "%s packet, dropped", recv_pkt_name);
+ goto failure;
+ }
+ }
+
+ dhcpmsg(MSG_VERBOSE, "received %s packet on %s", recv_pkt_name,
+ ifsp->if_name);
+
+ prepend_to_pkt_list(&ifsp->if_recv_pkt_list, plp);
+ ifsp->if_received++;
+ return (1);
+
+failure:
+ free(pkt);
+ free(plp);
+ return (0);
+}
+
+/*
+ * next_retransmission(): returns the number of seconds until the next
+ * retransmission, based on the algorithm in RFC2131
+ *
+ * input: uint32_t: the number of milliseconds for the last retransmission
+ * output: uint32_t: the number of milliseconds until the next retransmission
+ */
+
+static uint32_t
+next_retransmission(uint32_t last_timeout_ms)
+{
+ uint32_t timeout_ms;
+
+ /*
+ * start at 4, and increase by a factor of 2 up to 64. at each
+ * iteration, jitter the timeout by some fraction of a second.
+ */
+ if (last_timeout_ms == 0)
+ timeout_ms = 4 * MILLISEC;
+ else
+ timeout_ms = MIN(last_timeout_ms << 1, 64 * MILLISEC);
+
+ return (timeout_ms + ((lrand48() % (2 * MILLISEC)) - MILLISEC));
+}
diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/packet.h b/usr/src/cmd/cmd-inet/sbin/dhcpagent/packet.h
new file mode 100644
index 0000000000..865bc25bd3
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/packet.h
@@ -0,0 +1,115 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _PACKET_H
+#define _PACKET_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/sysmacros.h> /* MIN, MAX, ... */
+#include <netinet/in.h>
+#include <netinet/dhcp.h>
+#include <dhcp_impl.h>
+
+#include "agent.h"
+
+/*
+ * packet.[ch] contain routines for manipulating, setting, and
+ * transmitting DHCP/BOOTP packets. see packet.c for descriptions on
+ * how to use the exported functions.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ifslist; /* forward declaration */
+
+/*
+ * data type for recv_pkt(). needed because we may want to wait for
+ * several kinds of packets at once, and the existing enumeration of
+ * DHCP packet types does not provide a way to do that easily. here,
+ * we light a different bit in the enumeration for each type of packet
+ * we want to receive.
+ */
+
+typedef enum {
+
+ DHCP_PUNTYPED = 0x001, /* untyped (BOOTP) message */
+ DHCP_PDISCOVER = 0x002,
+ DHCP_POFFER = 0x004,
+ DHCP_PREQUEST = 0x008,
+ DHCP_PDECLINE = 0x010,
+ DHCP_PACK = 0x020,
+ DHCP_PNAK = 0x040,
+ DHCP_PRELEASE = 0x080,
+ DHCP_PINFORM = 0x100
+
+} dhcp_message_type_t;
+
+/*
+ * a dhcp_pkt_t is (right now) what is used by the packet manipulation
+ * functions. while the structure is not strictly necessary, it allows
+ * a better separation of functionality since metadata about the packet
+ * (such as its current length) is stored along with the packet.
+ */
+
+typedef struct dhcp_pkt {
+
+ PKT *pkt; /* the real underlying packet */
+ unsigned int pkt_max_len; /* its maximum length */
+ unsigned int pkt_cur_len; /* its current length */
+
+} dhcp_pkt_t;
+
+/*
+ * a `stop_func_t' is used by parts of dhcpagent that use the
+ * retransmission capability of send_pkt(). this makes it so the
+ * callers of send_pkt() decide when to stop retransmitting, which
+ * makes more sense than hardcoding their instance-specific cases into
+ * packet.c
+ */
+
+typedef boolean_t stop_func_t(struct ifslist *, unsigned int);
+
+dhcp_pkt_t *init_pkt(struct ifslist *, uchar_t);
+void add_pkt_opt(dhcp_pkt_t *, uchar_t, const void *, uchar_t);
+void add_pkt_opt16(dhcp_pkt_t *, uchar_t, uint16_t);
+void add_pkt_opt32(dhcp_pkt_t *, uchar_t, uint32_t);
+void free_pkt_list(PKT_LIST **);
+void remove_from_pkt_list(PKT_LIST **, PKT_LIST *);
+void stop_pkt_retransmission(struct ifslist *);
+int recv_pkt(struct ifslist *, int, dhcp_message_type_t, boolean_t);
+int send_pkt(struct ifslist *, dhcp_pkt_t *, in_addr_t,
+ stop_func_t *);
+void get_pkt_times(PKT_LIST *, uint32_t *, uint32_t *, uint32_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PACKET_H */
diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/release.c b/usr/src/cmd/cmd-inet/sbin/dhcpagent/release.c
new file mode 100644
index 0000000000..3d217c77b8
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/release.c
@@ -0,0 +1,160 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * DECLINE/RELEASE configuration functionality for the DHCP client.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/dhcp.h>
+#include <dhcpmsg.h>
+#include <dhcp_hostconf.h>
+#include <unistd.h>
+
+#include "packet.h"
+#include "interface.h"
+#include "states.h"
+
+/*
+ * send_decline(): sends a DECLINE message (broadcasted)
+ *
+ * input: struct ifslist *: the interface to send the DECLINE on
+ * char *: an optional text explanation to send with the message
+ * struct in_addr *: the IP address being declined
+ * output: void
+ */
+
+void
+send_decline(struct ifslist *ifsp, char *msg, struct in_addr *declined_ip)
+{
+ dhcp_pkt_t *dpkt;
+
+ dpkt = init_pkt(ifsp, DECLINE);
+ add_pkt_opt32(dpkt, CD_SERVER_ID, ifsp->if_server.s_addr);
+
+ if (msg != NULL)
+ add_pkt_opt(dpkt, CD_MESSAGE, msg, strlen(msg) + 1);
+
+ add_pkt_opt32(dpkt, CD_REQUESTED_IP_ADDR, declined_ip->s_addr);
+ add_pkt_opt(dpkt, CD_END, NULL, 0);
+
+ (void) send_pkt(ifsp, dpkt, htonl(INADDR_BROADCAST), NULL);
+}
+
+/*
+ * dhcp_release(): sends a RELEASE message to a DHCP server and removes
+ * the interface from DHCP control
+ *
+ * input: struct ifslist *: the interface to send the RELEASE on and remove
+ * const char *: an optional text explanation to send with the message
+ * output: int: 1 on success, 0 on failure
+ */
+
+int
+dhcp_release(struct ifslist *ifsp, const char *msg)
+{
+ int retval = 0;
+ int error = DHCP_IPC_E_INT;
+ dhcp_pkt_t *dpkt;
+
+ if (ifsp->if_dflags & DHCP_IF_BOOTP)
+ goto out;
+
+ if (ifsp->if_state != BOUND && ifsp->if_state != RENEWING &&
+ ifsp->if_state != REBINDING)
+ goto out;
+
+ dhcpmsg(MSG_INFO, "releasing interface %s", ifsp->if_name);
+
+ dpkt = init_pkt(ifsp, RELEASE);
+ dpkt->pkt->ciaddr.s_addr = ifsp->if_addr.s_addr;
+
+ if (msg != NULL)
+ add_pkt_opt(dpkt, CD_MESSAGE, msg, strlen(msg) + 1);
+
+ add_pkt_opt32(dpkt, CD_SERVER_ID, ifsp->if_server.s_addr);
+ add_pkt_opt(dpkt, CD_END, NULL, 0);
+
+ (void) send_pkt(ifsp, dpkt, ifsp->if_server.s_addr, NULL);
+
+ /*
+ * XXX this totally sucks, but since udp is best-effort,
+ * without this delay, there's a good chance that the packet
+ * that we just enqueued for sending will get pitched
+ * when we canonize the interface below.
+ */
+
+ (void) usleep(500);
+ (void) canonize_ifs(ifsp);
+
+ remove_ifs(ifsp);
+ error = DHCP_IPC_SUCCESS;
+ retval = 1;
+out:
+ ipc_action_finish(ifsp, error);
+ async_finish(ifsp);
+ return (retval);
+}
+
+/*
+ * dhcp_drop(): drops the interface from DHCP control
+ *
+ * input: struct ifslist *: the interface to drop
+ * const char *: unused
+ * output: int: always 1
+ */
+
+/* ARGSUSED */
+int
+dhcp_drop(struct ifslist *ifsp, const char *msg)
+{
+ PKT_LIST *plp[2];
+
+ dhcpmsg(MSG_INFO, "dropping interface %s", ifsp->if_name);
+
+ if (ifsp->if_state == BOUND || ifsp->if_state == RENEWING ||
+ ifsp->if_state == REBINDING) {
+
+ if ((ifsp->if_dflags & DHCP_IF_BOOTP) == 0) {
+ plp[0] = ifsp->if_ack;
+ plp[1] = ifsp->if_orig_ack;
+ if (write_hostconf(ifsp->if_name, plp, 2,
+ monosec_to_time(ifsp->if_curstart_monosec)) == -1)
+ dhcpmsg(MSG_ERR, "cannot write %s (reboot will "
+ "not use cached configuration)",
+ ifname_to_hostconf(ifsp->if_name));
+ }
+ (void) canonize_ifs(ifsp);
+ }
+ remove_ifs(ifsp);
+ ipc_action_finish(ifsp, DHCP_IPC_SUCCESS);
+ async_finish(ifsp);
+ return (1);
+}
diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/renew.c b/usr/src/cmd/cmd-inet/sbin/dhcpagent/renew.c
new file mode 100644
index 0000000000..8613a41245
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/renew.c
@@ -0,0 +1,367 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <netinet/dhcp.h>
+#include <netinet/udp.h>
+#include <netinet/ip_var.h>
+#include <netinet/udp_var.h>
+#include <libinetutil.h>
+#include <dhcpmsg.h>
+#include <string.h>
+
+#include "packet.h"
+#include "agent.h"
+#include "script_handler.h"
+#include "interface.h"
+#include "states.h"
+#include "util.h"
+
+/*
+ * next_extend_time(): returns the next time an EXTEND request should be sent
+ *
+ * input: monosec_t: the absolute time when the next state is entered
+ * output: uint32_t: the number of seconds in the future to send the next
+ * EXTEND request
+ */
+
+static uint32_t
+next_extend_time(monosec_t limit_monosec)
+{
+ monosec_t current_monosec = monosec();
+
+ if (limit_monosec - current_monosec < DHCP_REBIND_MIN)
+ return (0);
+
+ return ((limit_monosec - current_monosec) / 2);
+}
+
+/*
+ * dhcp_renew(): attempts to renew a DHCP lease
+ *
+ * input: iu_tq_t *: unused
+ * void *: the ifslist to renew the lease on
+ * output: void
+ */
+
+/* ARGSUSED */
+void
+dhcp_renew(iu_tq_t *tqp, void *arg)
+{
+ struct ifslist *ifsp = (struct ifslist *)arg;
+ uint32_t next;
+
+
+ ifsp->if_timer[DHCP_T1_TIMER] = -1;
+
+ if (check_ifs(ifsp) == 0) {
+ (void) release_ifs(ifsp);
+ return;
+ }
+
+ /*
+ * sanity check: don't send packets if we're past t2.
+ */
+
+ if (monosec() > (ifsp->if_curstart_monosec + ifsp->if_t2))
+ return;
+
+ next = next_extend_time(ifsp->if_curstart_monosec + ifsp->if_t2);
+
+ /*
+ * if there isn't an async event pending, then try to renew.
+ */
+
+ if (!async_pending(ifsp))
+ if (async_start(ifsp, DHCP_EXTEND, B_FALSE) != 0)
+
+ /*
+ * try to send extend. if we don't succeed,
+ * async_timeout() will clean us up.
+ */
+
+ (void) dhcp_extending(ifsp);
+
+ /*
+ * if we're within DHCP_REBIND_MIN seconds of REBINDING, don't
+ * reschedule ourselves.
+ */
+
+ if (next == 0)
+ return;
+
+ /*
+ * no big deal if we can't reschedule; we still have the REBIND
+ * state to save us.
+ */
+
+ (void) schedule_ifs_timer(ifsp, DHCP_T1_TIMER, next, dhcp_renew);
+}
+
+/*
+ * dhcp_rebind(): attempts to renew a DHCP lease from the REBINDING state
+ *
+ * input: iu_tq_t *: unused
+ * void *: the ifslist to renew the lease on
+ * output: void
+ */
+
+/* ARGSUSED */
+void
+dhcp_rebind(iu_tq_t *tqp, void *arg)
+{
+ struct ifslist *ifsp = (struct ifslist *)arg;
+ uint32_t next;
+
+ ifsp->if_timer[DHCP_T2_TIMER] = -1;
+
+ if (check_ifs(ifsp) == 0) {
+ (void) release_ifs(ifsp);
+ return;
+ }
+
+ /*
+ * sanity check: don't send packets if we've already expired.
+ */
+
+ if (monosec() > (ifsp->if_curstart_monosec + ifsp->if_lease))
+ return;
+
+ next = next_extend_time(ifsp->if_curstart_monosec + ifsp->if_lease);
+
+ /*
+ * if this is our first venture into the REBINDING state, then
+ * reset the server address. we know the renew timer has
+ * already been cancelled (or we wouldn't be here).
+ */
+
+ if (ifsp->if_state == RENEWING) {
+ ifsp->if_state = REBINDING;
+ ifsp->if_server.s_addr = htonl(INADDR_BROADCAST);
+ }
+
+ /*
+ * if there isn't an async event pending, then try to rebind.
+ */
+
+ if (!async_pending(ifsp))
+ if (async_start(ifsp, DHCP_EXTEND, B_FALSE) != 0)
+
+ /*
+ * try to send extend. if we don't succeed,
+ * async_timeout() will clean us up.
+ */
+
+ (void) dhcp_extending(ifsp);
+
+ /*
+ * if we're within DHCP_REBIND_MIN seconds of EXPIRE, don't
+ * reschedule ourselves.
+ */
+
+ if (next == 0) {
+ dhcpmsg(MSG_WARNING, "dhcp_rebind: lease on %s expires in less "
+ "than %i seconds!", ifsp->if_name, DHCP_REBIND_MIN);
+ return;
+ }
+
+ if (schedule_ifs_timer(ifsp, DHCP_T2_TIMER, next, dhcp_rebind) == 0)
+
+ /*
+ * we'll just end up in dhcp_expire(), but it sure sucks.
+ */
+
+ dhcpmsg(MSG_CRIT, "dhcp_rebind: cannot reschedule another "
+ "rebind attempt; lease may expire for %s", ifsp->if_name);
+}
+
+/*
+ * dhcp_restart(): callback function to script_start
+ *
+ * input: struct ifslist *: the interface to be restarted
+ * const char *: unused
+ * output: int: always 1
+ */
+
+/* ARGSUSED */
+static int
+dhcp_restart(struct ifslist *ifsp, const char *msg)
+{
+ dhcpmsg(MSG_INFO, "lease expired on %s -- restarting DHCP",
+ ifsp->if_name);
+
+ /*
+ * in the case where the lease is less than DHCP_REBIND_MIN
+ * seconds, we will never enter dhcp_renew() and thus the packet
+ * counters will not be reset. in that case, reset them here.
+ */
+
+ if (ifsp->if_state == BOUND) {
+ ifsp->if_bad_offers = 0;
+ ifsp->if_sent = 0;
+ ifsp->if_received = 0;
+ }
+
+ (void) canonize_ifs(ifsp);
+
+ /* reset_ifs() in dhcp_selecting() will clean up any leftover state */
+ dhcp_selecting(ifsp);
+ return (1);
+}
+
+/*
+ * dhcp_expire(): expires a lease on a given interface and restarts DHCP
+ *
+ * input: iu_tq_t *: unused
+ * void *: the ifslist to expire the lease on
+ * output: void
+ */
+
+/* ARGSUSED */
+void
+dhcp_expire(iu_tq_t *tqp, void *arg)
+{
+ struct ifslist *ifsp = (struct ifslist *)arg;
+
+ ifsp->if_timer[DHCP_LEASE_TIMER] = -1;
+
+ if (check_ifs(ifsp) == 0) {
+ (void) release_ifs(ifsp);
+ return;
+ }
+
+ if (async_pending(ifsp))
+
+ if (async_cancel(ifsp) == 0) {
+
+ dhcpmsg(MSG_WARNING, "dhcp_expire: cannot cancel "
+ "current asynchronous command against %s",
+ ifsp->if_name);
+
+ /*
+ * try to schedule ourselves for callback.
+ * we're really situation critical here
+ * there's not much hope for us if this fails.
+ */
+
+ if (iu_schedule_timer(tq, DHCP_EXPIRE_WAIT, dhcp_expire,
+ ifsp) != -1) {
+ hold_ifs(ifsp);
+ return;
+ }
+
+ dhcpmsg(MSG_CRIT, "dhcp_expire: cannot reschedule "
+ "dhcp_expire to get called back, proceeding...");
+ }
+
+ /*
+ * just march on if this fails; at worst someone will be able
+ * to async_start() while we're actually busy with our own
+ * asynchronous transaction. better than not having a lease.
+ */
+
+ if (async_start(ifsp, DHCP_START, B_FALSE) == 0)
+ dhcpmsg(MSG_WARNING, "dhcp_expire: cannot start asynchronous "
+ "transaction on %s, continuing...", ifsp->if_name);
+
+ (void) script_start(ifsp, EVENT_EXPIRE, dhcp_restart, NULL, NULL);
+}
+
+/*
+ * dhcp_extending(): sends a REQUEST to extend a lease on a given interface
+ * and registers to receive the ACK/NAK server reply
+ *
+ * input: struct ifslist *: the interface to send the REQUEST on
+ * output: int: 1 if the extension request was sent, 0 otherwise
+ */
+
+int
+dhcp_extending(struct ifslist *ifsp)
+{
+ dhcp_pkt_t *dpkt;
+
+ if (ifsp->if_state == BOUND) {
+ ifsp->if_neg_monosec = monosec();
+ ifsp->if_state = RENEWING;
+ ifsp->if_bad_offers = 0;
+ ifsp->if_sent = 0;
+ ifsp->if_received = 0;
+ }
+
+ dhcpmsg(MSG_DEBUG, "dhcp_extending: registering dhcp_acknak on %s",
+ ifsp->if_name);
+
+ if (register_acknak(ifsp) == 0) {
+
+ ipc_action_finish(ifsp, DHCP_IPC_E_MEMORY);
+ async_finish(ifsp);
+
+ dhcpmsg(MSG_WARNING, "dhcp_extending: cannot register "
+ "dhcp_acknak for %s, not sending renew request",
+ ifsp->if_name);
+
+ return (0);
+ }
+
+ /*
+ * assemble DHCPREQUEST message. The max dhcp message size
+ * option is set to the interface max, minus the size of the udp and
+ * ip headers.
+ */
+
+ dpkt = init_pkt(ifsp, REQUEST);
+ dpkt->pkt->ciaddr.s_addr = ifsp->if_addr.s_addr;
+
+ add_pkt_opt16(dpkt, CD_MAX_DHCP_SIZE, htons(ifsp->if_max -
+ sizeof (struct udpiphdr)));
+ add_pkt_opt32(dpkt, CD_LEASE_TIME, htonl(DHCP_PERM));
+
+ add_pkt_opt(dpkt, CD_CLASS_ID, class_id, class_id_len);
+ add_pkt_opt(dpkt, CD_REQUEST_LIST, ifsp->if_prl, ifsp->if_prllen);
+ /*
+ * if_reqhost was set for this interface in dhcp_selecting()
+ * if the REQUEST_HOSTNAME option was set and a host name was
+ * found.
+ */
+ if (ifsp->if_reqhost != NULL) {
+ add_pkt_opt(dpkt, CD_HOSTNAME, ifsp->if_reqhost,
+ strlen(ifsp->if_reqhost));
+ }
+ add_pkt_opt(dpkt, CD_END, NULL, 0);
+
+ /*
+ * if we can't send the packet, leave the event handler registered
+ * anyway, since we're not expecting to get any other types of
+ * packets in other than ACKs/NAKs anyway.
+ */
+
+ return (send_pkt(ifsp, dpkt, ifsp->if_server.s_addr, NULL));
+}
diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/request.c b/usr/src/cmd/cmd-inet/sbin/dhcpagent/request.c
new file mode 100644
index 0000000000..ab6ebb78f5
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/request.c
@@ -0,0 +1,492 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * REQUESTING state of the client state machine.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/stropts.h> /* FLUSHR/FLUSHW */
+#include <netinet/in.h>
+#include <netinet/dhcp.h>
+#include <netinet/udp.h>
+#include <netinet/ip_var.h>
+#include <netinet/udp_var.h>
+#include <dhcp_hostconf.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <dhcpmsg.h>
+
+#include "states.h"
+#include "util.h"
+#include "packet.h"
+#include "interface.h"
+#include "agent.h"
+#include "defaults.h"
+
+static PKT_LIST *select_best(PKT_LIST **);
+static void restart_dhcp(struct ifslist *);
+static stop_func_t stop_requesting;
+
+/*
+ * dhcp_requesting(): checks if OFFER packets to come in from DHCP servers.
+ * if so, chooses the best one, sends a REQUEST to the
+ * server and registers an event handler to receive
+ * the ACK/NAK
+ *
+ * input: iu_tq_t *: unused
+ * void *: the interface receiving OFFER packets
+ * output: void
+ */
+
+/* ARGSUSED */
+void
+dhcp_requesting(iu_tq_t *tqp, void *arg)
+{
+ struct ifslist *ifsp = (struct ifslist *)arg;
+ dhcp_pkt_t *dpkt;
+ PKT_LIST *offer;
+ lease_t lease;
+
+ if (check_ifs(ifsp) == 0) {
+ (void) release_ifs(ifsp);
+ return;
+ }
+
+ /*
+ * select the best OFFER; all others pitched.
+ */
+
+ offer = select_best(&ifsp->if_recv_pkt_list);
+ if (offer == NULL) {
+
+ dhcpmsg(MSG_VERBOSE, "no OFFERs on %s, waiting...",
+ ifsp->if_name);
+
+ /*
+ * no acceptable OFFERs have come in. reschedule
+ * ourselves for callback.
+ */
+
+ if (iu_schedule_timer(tq, ifsp->if_offer_wait,
+ dhcp_requesting, ifsp) == -1) {
+
+ /*
+ * ugh. the best we can do at this point is
+ * revert back to INIT and wait for a user to
+ * restart us.
+ */
+
+ ifsp->if_state = INIT;
+ ifsp->if_dflags |= DHCP_IF_FAILED;
+
+ stop_pkt_retransmission(ifsp);
+ ipc_action_finish(ifsp, DHCP_IPC_E_MEMORY);
+ async_finish(ifsp);
+
+ dhcpmsg(MSG_WARNING, "dhcp_requesting: cannot "
+ "reschedule callback, reverting to INIT state on "
+ "%s", ifsp->if_name);
+ } else
+ hold_ifs(ifsp);
+
+ return;
+ }
+
+ stop_pkt_retransmission(ifsp);
+
+ /*
+ * stop collecting packets. check to see whether we got an
+ * OFFER or a BOOTP packet. if we got a BOOTP packet, go to
+ * the BOUND state now.
+ */
+
+ if (iu_unregister_event(eh, ifsp->if_offer_id, NULL) != 0) {
+ (void) release_ifs(ifsp);
+ ifsp->if_offer_id = -1;
+ }
+
+ if (offer->opts[CD_DHCP_TYPE] == NULL) {
+
+ ifsp->if_state = REQUESTING;
+
+ if (dhcp_bound(ifsp, offer) == 0) {
+ dhcpmsg(MSG_WARNING, "dhcp_requesting: dhcp_bound "
+ "failed for %s", ifsp->if_name);
+ restart_dhcp(ifsp);
+ return;
+ }
+
+ return;
+ }
+
+ /*
+ * if we got a message from the server, display it.
+ */
+
+ if (offer->opts[CD_MESSAGE] != NULL)
+ print_server_msg(ifsp, offer->opts[CD_MESSAGE]);
+
+ /*
+ * assemble a DHCPREQUEST, with the ciaddr field set to 0,
+ * since we got here from the INIT state.
+ */
+
+ dpkt = init_pkt(ifsp, REQUEST);
+
+ /*
+ * grab the lease out of the OFFER; we know it's valid since
+ * select_best() already checked. The max dhcp message size
+ * option is set to the interface max, minus the size of the udp and
+ * ip headers.
+ */
+
+ (void) memcpy(&lease, offer->opts[CD_LEASE_TIME]->value,
+ sizeof (lease_t));
+
+ add_pkt_opt32(dpkt, CD_LEASE_TIME, lease);
+ add_pkt_opt16(dpkt, CD_MAX_DHCP_SIZE, htons(ifsp->if_max -
+ sizeof (struct udpiphdr)));
+ add_pkt_opt32(dpkt, CD_REQUESTED_IP_ADDR, offer->pkt->yiaddr.s_addr);
+ add_pkt_opt(dpkt, CD_SERVER_ID, offer->opts[CD_SERVER_ID]->value,
+ offer->opts[CD_SERVER_ID]->len);
+
+ add_pkt_opt(dpkt, CD_CLASS_ID, class_id, class_id_len);
+ add_pkt_opt(dpkt, CD_REQUEST_LIST, ifsp->if_prl, ifsp->if_prllen);
+
+ /*
+ * if_reqhost was set for this interface in dhcp_selecting()
+ * if the DF_REQUEST_HOSTNAME option set and a host name was
+ * found
+ */
+ if (ifsp->if_reqhost != NULL) {
+ add_pkt_opt(dpkt, CD_HOSTNAME, ifsp->if_reqhost,
+ strlen(ifsp->if_reqhost));
+ }
+ add_pkt_opt(dpkt, CD_END, NULL, 0);
+
+ /* all done with the offer */
+ free_pkt_list(&offer);
+
+ /*
+ * send out the REQUEST, trying retransmissions. either a NAK
+ * or too many REQUEST attempts will revert us to SELECTING.
+ */
+
+ ifsp->if_state = REQUESTING;
+ (void) send_pkt(ifsp, dpkt, htonl(INADDR_BROADCAST), stop_requesting);
+
+ /*
+ * wait for an ACK or NAK to come back from the server. if
+ * we can't register this event handler, then we won't be able
+ * to see the server's responses. the best we can really do
+ * in that case is drop back to INIT and hope someone notices.
+ */
+
+ if (register_acknak(ifsp) == 0) {
+
+ ifsp->if_state = INIT;
+ ifsp->if_dflags |= DHCP_IF_FAILED;
+
+ ipc_action_finish(ifsp, DHCP_IPC_E_MEMORY);
+ async_finish(ifsp);
+
+ dhcpmsg(MSG_ERROR, "dhcp_requesting: cannot register to "
+ "collect ACK/NAK packets, reverting to INIT on %s",
+ ifsp->if_name);
+ }
+}
+
+/*
+ * select_best(): selects the best OFFER packet from a list of OFFER packets
+ *
+ * input: PKT_LIST **: a list of packets to select the best from
+ * output: PKT_LIST *: the best packet, or NULL if none are acceptable
+ */
+
+static PKT_LIST *
+select_best(PKT_LIST **pkts)
+{
+ PKT_LIST *current, *best = NULL;
+ uint32_t points, best_points = 0;
+
+ /*
+ * pick out the best offer. point system.
+ * what's important?
+ *
+ * 0) DHCP
+ * 1) no option overload
+ * 2) encapsulated vendor option
+ * 3) non-null sname and siaddr fields
+ * 4) non-null file field
+ * 5) hostname
+ * 6) subnetmask
+ * 7) router
+ */
+
+ for (current = *pkts; current != NULL; current = current->next) {
+
+ points = 0;
+
+ if (current->opts[CD_DHCP_TYPE] == NULL) {
+ dhcpmsg(MSG_VERBOSE, "valid BOOTP reply");
+ goto valid_offer;
+ }
+
+ if (current->opts[CD_LEASE_TIME] == NULL) {
+ dhcpmsg(MSG_WARNING, "select_best: OFFER without "
+ "lease time");
+ continue;
+ }
+
+ if (current->opts[CD_LEASE_TIME]->len != sizeof (lease_t)) {
+ dhcpmsg(MSG_WARNING, "select_best: OFFER with garbled "
+ "lease time");
+ continue;
+ }
+
+ if (current->opts[CD_SERVER_ID] == NULL) {
+ dhcpmsg(MSG_WARNING, "select_best: OFFER without "
+ "server id");
+ continue;
+ }
+
+ if (current->opts[CD_SERVER_ID]->len != sizeof (ipaddr_t)) {
+ dhcpmsg(MSG_WARNING, "select_best: OFFER with garbled "
+ "server id");
+ continue;
+ }
+
+ /* valid DHCP OFFER. see if we got our parameters. */
+ dhcpmsg(MSG_VERBOSE, "valid OFFER packet");
+ points += 30;
+
+valid_offer:
+ if (current->rfc1048)
+ points += 5;
+
+ /*
+ * also could be faked, though more difficult because
+ * the encapsulation is hard to encode on a BOOTP
+ * server; plus there's not as much real estate in the
+ * packet for options, so it's likely this option
+ * would get dropped.
+ */
+
+ if (current->opts[CD_VENDOR_SPEC] != NULL)
+ points += 80;
+
+ if (current->opts[CD_SUBNETMASK] != NULL)
+ points++;
+
+ if (current->opts[CD_ROUTER] != NULL)
+ points++;
+
+ if (current->opts[CD_HOSTNAME] != NULL)
+ points += 5;
+
+ dhcpmsg(MSG_DEBUG, "select_best: OFFER had %d points", points);
+
+ if (points >= best_points) {
+ best_points = points;
+ best = current;
+ }
+ }
+
+ if (best != NULL) {
+ dhcpmsg(MSG_DEBUG, "select_best: most points: %d", best_points);
+ remove_from_pkt_list(pkts, best);
+ } else
+ dhcpmsg(MSG_DEBUG, "select_best: no valid OFFER/BOOTP reply");
+
+ free_pkt_list(pkts);
+ return (best);
+}
+
+/*
+ * dhcp_acknak(): processes reception of an ACK or NAK packet on an interface
+ *
+ * input: iu_eh_t *: unused
+ * int: the file descriptor the ACK/NAK arrived on
+ * short: unused
+ * iu_event_id_t: the id of this event callback with the handler
+ * void *: the interface that received the ACK or NAK
+ * output: void
+ */
+
+/* ARGSUSED */
+void
+dhcp_acknak(iu_eh_t *ehp, int fd, short events, iu_event_id_t id, void *arg)
+{
+ struct ifslist *ifsp = (struct ifslist *)arg;
+ PKT_LIST *plp;
+
+ if (check_ifs(ifsp) == 0) {
+ /* unregister_acknak() does our release_ifs() */
+ (void) unregister_acknak(ifsp);
+ (void) ioctl(fd, I_FLUSH, FLUSHR|FLUSHW);
+ return;
+ }
+
+ /*
+ * note that check_ifs() did our release_ifs() but we're not
+ * sure we're done yet; call hold_ifs() to reacquire our hold;
+ * if we're done, unregister_acknak() will release_ifs() below.
+ */
+
+ hold_ifs(ifsp);
+
+ if (recv_pkt(ifsp, fd, DHCP_PACK|DHCP_PNAK, B_FALSE) == 0)
+ return;
+
+ /*
+ * we've got a packet; make sure it's acceptable before
+ * cancelling the REQUEST retransmissions.
+ */
+
+ plp = ifsp->if_recv_pkt_list;
+ remove_from_pkt_list(&ifsp->if_recv_pkt_list, plp);
+
+ if (*plp->opts[CD_DHCP_TYPE]->value == ACK) {
+ if (plp->opts[CD_LEASE_TIME] == NULL ||
+ plp->opts[CD_LEASE_TIME]->len != sizeof (lease_t)) {
+ dhcpmsg(MSG_WARNING, "dhcp_acknak: ACK packet on %s "
+ "missing mandatory lease option, ignored",
+ ifsp->if_name);
+ ifsp->if_bad_offers++;
+ free_pkt_list(&plp);
+ return;
+ }
+ if ((ifsp->if_state == RENEWING ||
+ ifsp->if_state == REBINDING) &&
+ ifsp->if_addr.s_addr != plp->pkt->yiaddr.s_addr) {
+ dhcpmsg(MSG_WARNING, "dhcp_acknak: renewal ACK packet "
+ "has a different IP address (%s), ignored",
+ inet_ntoa(plp->pkt->yiaddr));
+ ifsp->if_bad_offers++;
+ free_pkt_list(&plp);
+ return;
+ }
+ }
+
+ /*
+ * looks good; cancel the retransmission timer and unregister
+ * the acknak handler. ACK to BOUND, NAK back to SELECTING.
+ */
+
+ stop_pkt_retransmission(ifsp);
+ (void) unregister_acknak(ifsp);
+
+ if (*(plp->opts[CD_DHCP_TYPE]->value) == NAK) {
+ dhcpmsg(MSG_WARNING, "dhcp_acknak: NAK on interface %s",
+ ifsp->if_name);
+ ifsp->if_bad_offers++;
+ free_pkt_list(&plp);
+ restart_dhcp(ifsp);
+
+ /*
+ * remove any bogus cached configuration we might have
+ * around (right now would only happen if we got here
+ * from INIT_REBOOT).
+ */
+
+ (void) remove_hostconf(ifsp->if_name);
+ return;
+ }
+
+ if (plp->opts[CD_SERVER_ID] == NULL ||
+ plp->opts[CD_SERVER_ID]->len != sizeof (ipaddr_t)) {
+ dhcpmsg(MSG_ERROR, "dhcp_acknak: ACK with no valid server id, "
+ "restarting DHCP on %s", ifsp->if_name);
+ ifsp->if_bad_offers++;
+ free_pkt_list(&plp);
+ restart_dhcp(ifsp);
+ return;
+ }
+
+ if (plp->opts[CD_MESSAGE] != NULL)
+ print_server_msg(ifsp, plp->opts[CD_MESSAGE]);
+
+ if (dhcp_bound(ifsp, plp) == 0) {
+ dhcpmsg(MSG_WARNING, "dhcp_acknak: dhcp_bound failed "
+ "for %s", ifsp->if_name);
+ restart_dhcp(ifsp);
+ return;
+ }
+
+ dhcpmsg(MSG_VERBOSE, "ACK on interface %s", ifsp->if_name);
+}
+
+/*
+ * restart_dhcp(): restarts DHCP (from INIT) on a given interface
+ *
+ * input: struct ifslist *: the interface to restart DHCP on
+ * output: void
+ */
+
+static void
+restart_dhcp(struct ifslist *ifsp)
+{
+ if (iu_schedule_timer(tq, DHCP_RESTART_WAIT, dhcp_start, ifsp) == -1) {
+
+ ifsp->if_state = INIT;
+ ifsp->if_dflags |= DHCP_IF_FAILED;
+
+ ipc_action_finish(ifsp, DHCP_IPC_E_MEMORY);
+ async_finish(ifsp);
+
+ dhcpmsg(MSG_ERROR, "restart_dhcp: cannot schedule dhcp_start, "
+ "reverting to INIT state on %s", ifsp->if_name);
+ } else
+ hold_ifs(ifsp);
+}
+
+/*
+ * stop_requesting(): decides when to stop retransmitting REQUESTs
+ *
+ * input: struct ifslist *: the interface REQUESTs are being sent on
+ * unsigned int: the number of REQUESTs sent so far
+ * output: boolean_t: B_TRUE if retransmissions should stop
+ */
+
+static boolean_t
+stop_requesting(struct ifslist *ifsp, unsigned int n_requests)
+{
+ if (n_requests >= DHCP_MAX_REQUESTS) {
+
+ (void) unregister_acknak(ifsp);
+
+ dhcpmsg(MSG_INFO, "no ACK/NAK to REQUESTING REQUEST, "
+ "restarting DHCP on %s", ifsp->if_name);
+
+ dhcp_selecting(ifsp);
+ return (B_TRUE);
+ }
+
+ return (B_FALSE);
+}
diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/script_handler.c b/usr/src/cmd/cmd-inet/sbin/dhcpagent/script_handler.c
new file mode 100644
index 0000000000..9dd9690748
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/script_handler.c
@@ -0,0 +1,371 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <time.h>
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <dhcpmsg.h>
+#include "script_handler.h"
+
+/*
+ * scripts are directly managed by a script helper process. dhcpagent creates
+ * the helper process and it, in turn, creates a process to run the script
+ * dhcpagent owns one end of a pipe and the helper process owns the other end
+ * the helper process calls waitpid to wait for the script to exit. an alarm
+ * is set for SCRIPT_TIMEOUT seconds. If the alarm fires, SIGTERM is sent to
+ * the script process and a second alarm is set for SCRIPT_TIMEOUT_GRACE. if
+ * the second alarm fires, SIGKILL is sent to forcefully kill the script. when
+ * script exits, the helper process notifies dhcpagent by closing its end
+ * of the pipe.
+ */
+
+unsigned int script_count;
+
+/*
+ * the signal to send to the script process. it is a global variable
+ * to this file as sigterm_handler needs it.
+ */
+
+static int script_signal = SIGTERM;
+
+/*
+ * script's absolute timeout value. the first timeout is set to SCRIPT_TIMEOUT
+ * seconds from the time it is started. SIGTERM is sent on the first timeout
+ * the second timeout is set to SCRIPT_TIMEOUT_GRACE from the first timeout
+ * and SIGKILL is sent on the second timeout.
+ */
+static time_t timeout;
+
+/*
+ * sigalarm_handler(): signal handler for SIGARLM
+ *
+ * input: int: signal the handler was called with
+ * output: void
+ */
+
+/* ARGSUSED */
+static void
+sigalarm_handler(int sig)
+{
+ time_t now;
+
+ /* set a another alarm if it fires too early */
+ now = time(NULL);
+ if (now < timeout)
+ (void) alarm(timeout - now);
+}
+
+/*
+ * sigterm_handler(): signal handler for SIGTERM, fired when dhcpagent wants
+ * to stop the script
+ * input: int: signal the handler was called with
+ * output: void
+ */
+
+/* ARGSUSED */
+static void
+sigterm_handler(int sig)
+{
+ if (script_signal != SIGKILL) {
+ /* send SIGKILL SCRIPT_TIMEOUT_GRACE seconds from now */
+ script_signal = SIGKILL;
+ timeout = time(NULL) + SCRIPT_TIMEOUT_GRACE;
+ (void) alarm(SCRIPT_TIMEOUT_GRACE);
+ }
+}
+
+/*
+ * run_script(): it forks a process to execute the script
+ *
+ * input: struct ifslist *: the interface
+ * const char *: the event name
+ * int: the pipe end owned by the script helper process
+ * output: void
+ */
+static void
+run_script(struct ifslist *ifsp, const char *event, int fd)
+{
+ int n;
+ char c;
+ char *name;
+ pid_t pid;
+ time_t now;
+ extern int errno;
+
+ if ((pid = fork()) == -1) {
+ return;
+ }
+ if (pid == 0) {
+ name = strrchr(SCRIPT_PATH, '/') + 1;
+
+ /* close all files */
+ closefrom(0);
+
+ /* redirect stdin, stdout and stderr to /dev/null */
+ if ((n = open("/dev/null", O_RDWR)) < 0)
+ exit(-1);
+
+ (void) dup2(n, STDOUT_FILENO);
+ (void) dup2(n, STDERR_FILENO);
+
+ (void) execl(SCRIPT_PATH, name, ifsp->if_name, event, NULL);
+ _exit(127);
+ }
+
+ /*
+ * the first timeout fires SCRIPT_TIMEOUT seconds from now.
+ */
+ timeout = time(NULL) + SCRIPT_TIMEOUT;
+ (void) sigset(SIGALRM, sigalarm_handler);
+ (void) alarm(SCRIPT_TIMEOUT);
+
+ /*
+ * pass script's pid to dhcpagent.
+ */
+ (void) write(fd, &pid, sizeof (pid));
+
+ for (;;) {
+ if (waitpid(pid, NULL, 0) >= 0) {
+ /* script has exited */
+ c = SCRIPT_OK;
+ break;
+ }
+
+ if (errno != EINTR) {
+ return;
+ }
+
+ now = time(NULL);
+ if (now >= timeout) {
+ (void) kill(pid, script_signal);
+ if (script_signal == SIGKILL) {
+ c = SCRIPT_KILLED;
+ break;
+ }
+
+ script_signal = SIGKILL;
+ timeout = now + SCRIPT_TIMEOUT_GRACE;
+ (void) alarm(SCRIPT_TIMEOUT_GRACE);
+ }
+ }
+
+ (void) write(fd, &c, 1);
+}
+
+/*
+ * script_cleanup(): cleanup helper function
+ *
+ * input: struct ifslist *: the interface
+ * output: void
+ */
+
+static void
+script_cleanup(struct ifslist *ifsp)
+{
+ ifsp->if_script_helper_pid = -1;
+ ifsp->if_script_pid = -1;
+
+ if (ifsp->if_script_fd != -1) {
+ assert(ifsp->if_script_event_id != -1);
+ assert(ifsp->if_script_callback != NULL);
+
+ (void) iu_unregister_event(eh, ifsp->if_script_event_id, NULL);
+ (void) close(ifsp->if_script_fd);
+ ifsp->if_script_event_id = -1;
+ ifsp->if_script_fd = -1;
+ ifsp->if_script_callback(ifsp, ifsp->if_callback_msg);
+ ifsp->if_script_callback = NULL;
+ ifsp->if_script_event = NULL;
+ ifsp->if_callback_msg = NULL;
+
+ (void) release_ifs(ifsp);
+ script_count--;
+ }
+}
+
+/*
+ * script_exit(): does cleanup and invokes callback when script exits
+ *
+ * input: eh_t *: unused
+ * int: the end of pipe owned by dhcpagent
+ * short: unused
+ * eh_event_id_t: unused
+ * void *: the interface
+ * output: void
+ */
+
+/* ARGSUSED */
+static void
+script_exit(iu_eh_t *ehp, int fd, short events, iu_event_id_t id, void *arg)
+{
+ char c;
+
+ if (read(fd, &c, 1) <= 0) {
+ c = SCRIPT_FAILED;
+ }
+
+ if (c == SCRIPT_OK) {
+ dhcpmsg(MSG_DEBUG, "script ok");
+ } else if (c == SCRIPT_KILLED) {
+ dhcpmsg(MSG_DEBUG, "script killed");
+ } else {
+ dhcpmsg(MSG_DEBUG, "script failed");
+ }
+
+ script_cleanup(arg);
+}
+
+/*
+ * script_start(): tries to run the script
+ *
+ * input: struct ifslist *: the interface
+ * const char *: the event name
+ * script_callback_t: callback function
+ * void *: data to the callback function
+ * output: int: 1 if script starts successfully
+ * int *: the returned value of the callback function if script
+ * starts unsuccessfully
+ */
+int
+script_start(struct ifslist *ifsp, const char *event,
+ script_callback_t *callback, const char *msg, int *status)
+{
+ int n;
+ int fds[2];
+ pid_t pid;
+ iu_event_id_t event_id;
+
+ assert(callback != NULL);
+
+ if (access(SCRIPT_PATH, X_OK) == -1) {
+ /* script does not exist */
+ goto out;
+ }
+ if (ifsp->if_script_pid != -1) {
+ /* script is running, stop it */
+ dhcpmsg(MSG_ERROR, "script_start: stop script");
+ script_stop(ifsp);
+ }
+
+ /*
+ * dhcpagent owns one end of the pipe and script helper process
+ * owns the other end. dhcpagent reads on the pipe; and the helper
+ * process notifies it when the script exits.
+ */
+ if (pipe(fds) < 0) {
+ dhcpmsg(MSG_ERROR, "script_start: can't create pipe");
+ goto out;
+ }
+
+ if ((pid = fork()) < 0) {
+ dhcpmsg(MSG_ERROR, "script_start: can't fork");
+ (void) close(fds[0]);
+ (void) close(fds[1]);
+ goto out;
+ }
+
+ if (pid == 0) {
+ /*
+ * SIGCHLD is ignored in dhcpagent, the helper process
+ * needs it. it calls waitpid to wait for the script to exit.
+ */
+ (void) close(fds[0]);
+ (void) sigset(SIGCHLD, SIG_DFL);
+ (void) sigset(SIGTERM, sigterm_handler);
+ run_script(ifsp, event, fds[1]);
+ exit(0);
+ }
+
+ (void) close(fds[1]);
+
+ /* get the script's pid */
+ if (read(fds[0], &ifsp->if_script_pid, sizeof (pid_t)) !=
+ sizeof (pid_t)) {
+ (void) kill(pid, SIGKILL);
+ ifsp->if_script_pid = -1;
+ (void) close(fds[0]);
+ goto out;
+ }
+
+ ifsp->if_script_helper_pid = pid;
+ event_id = iu_register_event(eh, fds[0], POLLIN, script_exit, ifsp);
+ if (event_id == -1) {
+ (void) close(fds[0]);
+ script_stop(ifsp);
+ goto out;
+ }
+
+ script_count++;
+ ifsp->if_script_event_id = event_id;
+ ifsp->if_script_callback = callback;
+ ifsp->if_script_event = event;
+ ifsp->if_callback_msg = msg;
+ ifsp->if_script_fd = fds[0];
+ hold_ifs(ifsp);
+ return (1);
+
+out:
+ /* callback won't be called in script_exit, so call it here */
+ n = callback(ifsp, msg);
+ if (status != NULL)
+ *status = n;
+
+ return (0);
+}
+
+/*
+ * script_stop(): stops the script if it is running
+ *
+ * input: struct ifslist *: the interface
+ * output: void
+ */
+void
+script_stop(struct ifslist *ifsp)
+{
+ if (ifsp->if_script_pid != -1) {
+ assert(ifsp->if_script_helper_pid != -1);
+
+ /*
+ * sends SIGTERM to the script and asks the helper process
+ * to send SIGKILL if it does not exit after
+ * SCRIPT_TIMEOUT_GRACE seconds.
+ */
+ (void) kill(ifsp->if_script_pid, SIGTERM);
+ (void) kill(ifsp->if_script_helper_pid, SIGTERM);
+ }
+
+ script_cleanup(ifsp);
+}
diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/script_handler.h b/usr/src/cmd/cmd-inet/sbin/dhcpagent/script_handler.h
new file mode 100644
index 0000000000..caf334a10d
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/script_handler.h
@@ -0,0 +1,84 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef SCRIPT_HANDLER_H
+#define SCRIPT_HANDLER_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "interface.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * The signal SIGTERM is sent to a script process if it does not exit after
+ * SCRIPT_TIMEOUT seconds; and the signal SIGKILL is sent if it is still alive
+ * SCRIPT_TIMEOUT_GRACE seconds after SIGTERM is sent. (SCRIPT_TIMEOUT +
+ * SCRIPT_TIMEOUT_GRACE) should be less than DHCP_ASYNC_WAIT.
+ */
+#define SCRIPT_TIMEOUT 55
+#define SCRIPT_TIMEOUT_GRACE 3
+
+/*
+ * script exit status as dhcpagent sees it, for debug purpose only.
+ *
+ * SCRIPT_OK: script exits ok, no timeout
+ * SCRIPT_KILLED: script timeout, killed
+ * SCRIPT_FAILED: unknown status
+ */
+
+enum { SCRIPT_OK, SCRIPT_KILLED, SCRIPT_FAILED };
+
+/*
+ * event names for script.
+ */
+#define EVENT_BOUND "BOUND"
+#define EVENT_EXTEND "EXTEND"
+#define EVENT_EXPIRE "EXPIRE"
+#define EVENT_DROP "DROP"
+#define EVENT_RELEASE "RELEASE"
+
+/*
+ * script location.
+ */
+#define SCRIPT_PATH "/etc/dhcp/eventhook"
+
+/*
+ * the number of running scripts.
+ */
+extern unsigned int script_count;
+
+int script_start(struct ifslist *, const char *,
+ script_callback_t *, const char *, int *);
+void script_stop(struct ifslist *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SCRIPT_HANDLER_H */
diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/select.c b/usr/src/cmd/cmd-inet/sbin/dhcpagent/select.c
new file mode 100644
index 0000000000..abd4b2def1
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/select.c
@@ -0,0 +1,226 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * SELECTING state of the client state machine.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <strings.h>
+#include <time.h>
+#include <limits.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <net/route.h>
+#include <net/if.h>
+#include <netinet/dhcp.h>
+#include <netinet/udp.h>
+#include <netinet/ip_var.h>
+#include <netinet/udp_var.h>
+#include <stropts.h> /* FLUSHR/FLUSHW */
+#include <dhcpmsg.h>
+
+#include "states.h"
+#include "agent.h"
+#include "util.h"
+#include "interface.h"
+#include "packet.h"
+#include "defaults.h"
+
+static iu_eh_callback_t dhcp_collect_offers;
+static stop_func_t stop_selecting;
+
+/*
+ * dhcp_start(): starts DHCP on an interface
+ *
+ * input: iu_tq_t *: unused
+ * void *: the interface to start DHCP on
+ * output: void
+ */
+
+/* ARGSUSED */
+void
+dhcp_start(iu_tq_t *tqp, void *arg)
+{
+ struct ifslist *ifsp = (struct ifslist *)arg;
+
+ if (check_ifs(ifsp) == 0) {
+ (void) release_ifs(ifsp);
+ return;
+ }
+
+ dhcpmsg(MSG_VERBOSE, "starting DHCP on %s", ifsp->if_name);
+ dhcp_selecting(ifsp);
+}
+
+/*
+ * dhcp_selecting(): sends a DISCOVER and sets up reception for an OFFER
+ *
+ * input: struct ifslist *: the interface to send the DISCOVER on, ...
+ * output: void
+ */
+
+void
+dhcp_selecting(struct ifslist *ifsp)
+{
+ dhcp_pkt_t *dpkt;
+ const char *reqhost;
+ char hostfile[PATH_MAX + 1];
+
+ /*
+ * we first set up to collect OFFER packets as they arrive.
+ * we then send out DISCOVER probes. then we wait at a
+ * user-tunable number of seconds before seeing if OFFERs have
+ * come in response to our DISCOVER. if none have come in, we
+ * continue to wait, sending out our DISCOVER probes with
+ * exponential backoff. if an OFFER is never received, we
+ * will wait forever (note that since we're event-driven
+ * though, we're still able to service other interfaces.)
+ *
+ * note that we do an reset_ifs() here because we may be
+ * landing in dhcp_selecting() as a result of restarting DHCP,
+ * so the ifs may not be fresh.
+ */
+
+ reset_ifs(ifsp);
+ ifsp->if_state = SELECTING;
+
+ if ((ifsp->if_offer_id = iu_register_event(eh, ifsp->if_dlpi_fd, POLLIN,
+ dhcp_collect_offers, ifsp)) == -1) {
+
+ dhcpmsg(MSG_ERROR, "dhcp_selecting: cannot register to collect "
+ "OFFER packets, reverting to INIT on %s",
+ ifsp->if_name);
+
+ ifsp->if_state = INIT;
+ ifsp->if_dflags |= DHCP_IF_FAILED;
+ ipc_action_finish(ifsp, DHCP_IPC_E_MEMORY);
+ async_finish(ifsp);
+ return;
+ } else
+ hold_ifs(ifsp);
+
+
+ if (iu_schedule_timer(tq, ifsp->if_offer_wait, dhcp_requesting,
+ ifsp) == -1) {
+
+ dhcpmsg(MSG_ERROR, "dhcp_selecting: cannot schedule to read "
+ "OFFER packets");
+
+ if (iu_unregister_event(eh, ifsp->if_offer_id, NULL) != 0) {
+ ifsp->if_offer_id = -1;
+ (void) release_ifs(ifsp);
+ }
+
+ ifsp->if_state = INIT;
+ ifsp->if_dflags |= DHCP_IF_FAILED;
+ ipc_action_finish(ifsp, DHCP_IPC_E_MEMORY);
+ async_finish(ifsp);
+ return;
+ } else
+ hold_ifs(ifsp);
+
+ /*
+ * Assemble DHCPDISCOVER message. The max dhcp message size
+ * option is set to the interface max, minus the size of the udp and
+ * ip headers.
+ */
+
+ dpkt = init_pkt(ifsp, DISCOVER);
+
+ add_pkt_opt16(dpkt, CD_MAX_DHCP_SIZE, htons(ifsp->if_max -
+ sizeof (struct udpiphdr)));
+ add_pkt_opt32(dpkt, CD_LEASE_TIME, htonl(DHCP_PERM));
+
+ add_pkt_opt(dpkt, CD_CLASS_ID, class_id, class_id_len);
+ add_pkt_opt(dpkt, CD_REQUEST_LIST, ifsp->if_prl, ifsp->if_prllen);
+
+ if (df_get_bool(ifsp->if_name, DF_REQUEST_HOSTNAME)) {
+ dhcpmsg(MSG_DEBUG, "dhcp_selecting: DF_REQUEST_HOSTNAME");
+ (void) snprintf(hostfile, sizeof (hostfile), "/etc/hostname.%s",
+ ifsp->if_name);
+
+ if ((reqhost = iffile_to_hostname(hostfile)) != NULL) {
+ dhcpmsg(MSG_DEBUG, "dhcp_selecting: host %s", reqhost);
+ if ((ifsp->if_reqhost = strdup(reqhost)) != NULL)
+ add_pkt_opt(dpkt, CD_HOSTNAME, ifsp->if_reqhost,
+ strlen(ifsp->if_reqhost));
+ else
+ dhcpmsg(MSG_WARNING, "dhcp_selecting: cannot"
+ " allocate memory for host name option");
+ }
+ }
+ add_pkt_opt(dpkt, CD_END, NULL, 0);
+
+ (void) send_pkt(ifsp, dpkt, htonl(INADDR_BROADCAST), stop_selecting);
+}
+
+/*
+ * dhcp_collect_offers(): collects incoming OFFERs to a DISCOVER
+ *
+ * input: iu_eh_t *: unused
+ * int: the file descriptor the OFFER arrived on
+ * short: unused
+ * iu_event_id_t: the id of this event callback with the handler
+ * void *: the interface that received the OFFER
+ * output: void
+ */
+
+/* ARGSUSED */
+static void
+dhcp_collect_offers(iu_eh_t *eh, int fd, short events, iu_event_id_t id,
+ void *arg)
+{
+ struct ifslist *ifsp = (struct ifslist *)arg;
+
+ if (verify_ifs(ifsp) == 0) {
+ (void) ioctl(fd, I_FLUSH, FLUSHR|FLUSHW);
+ return;
+ }
+
+ /*
+ * DHCP_PUNTYPED messages are BOOTP server responses.
+ */
+
+ (void) recv_pkt(ifsp, fd, DHCP_POFFER|DHCP_PUNTYPED, B_TRUE);
+}
+
+/*
+ * stop_selecting(): decides when to stop retransmitting DISCOVERs (never)
+ *
+ * input: struct ifslist *: the interface DISCOVERs are being sent on
+ * unsigned int: the number of DISCOVERs sent so far
+ * output: boolean_t: B_TRUE if retransmissions should stop
+ */
+
+/* ARGSUSED */
+static boolean_t
+stop_selecting(struct ifslist *ifsp, unsigned int n_discovers)
+{
+ return (B_FALSE);
+}
diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/states.h b/usr/src/cmd/cmd-inet/sbin/dhcpagent/states.h
new file mode 100644
index 0000000000..74190cdd13
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/states.h
@@ -0,0 +1,70 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef STATES_H
+#define STATES_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <netinet/dhcp.h>
+#include <libinetutil.h>
+
+#include "interface.h"
+
+/*
+ * interfaces for state transition/action functions. these functions
+ * can be found in suitably named .c files, such as inform.c, select.c,
+ * renew.c, etc.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void dhcp_acknak(iu_eh_t *, int, short, iu_event_id_t, void *);
+int dhcp_adopt(void);
+int dhcp_bound(struct ifslist *, PKT_LIST *);
+int dhcp_drop(struct ifslist *, const char *);
+void dhcp_expire(iu_tq_t *, void *);
+int dhcp_extending(struct ifslist *);
+void dhcp_inform(struct ifslist *);
+void dhcp_init_reboot(struct ifslist *);
+void dhcp_rebind(iu_tq_t *, void *);
+int dhcp_release(struct ifslist *, const char *);
+void dhcp_renew(iu_tq_t *, void *);
+void dhcp_requesting(iu_tq_t *, void *);
+void dhcp_selecting(struct ifslist *);
+void dhcp_start(iu_tq_t *, void *);
+void send_decline(struct ifslist *, char *, struct in_addr *);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* STATES_H */
diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/util.c b/usr/src/cmd/cmd-inet/sbin/dhcpagent/util.c
new file mode 100644
index 0000000000..5db77d8be3
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/util.c
@@ -0,0 +1,757 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <netinet/in.h> /* struct in_addr */
+#include <netinet/dhcp.h>
+#include <signal.h>
+#include <sys/dlpi.h>
+#include <sys/sockio.h>
+#include <sys/socket.h>
+#include <errno.h>
+#include <net/route.h>
+#include <net/if_arp.h>
+#include <string.h>
+#include <dhcpmsg.h>
+#include <ctype.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <stdio.h>
+
+#include "states.h"
+#include "agent.h"
+#include "interface.h"
+#include "util.h"
+#include "packet.h"
+#include "defaults.h"
+
+/*
+ * this file contains utility functions that have no real better home
+ * of their own. they can largely be broken into six categories:
+ *
+ * o conversion functions -- functions to turn integers into strings,
+ * or to convert between units of a similar measure.
+ *
+ * o ipc-related functions -- functions to simplify the generation of
+ * ipc messages to the agent's clients.
+ *
+ * o signal-related functions -- functions to clean up the agent when
+ * it receives a signal.
+ *
+ * o routing table manipulation functions
+ *
+ * o acknak handler functions
+ *
+ * o true miscellany -- anything else
+ */
+
+/*
+ * pkt_type_to_string(): stringifies a packet type
+ *
+ * input: uchar_t: a DHCP packet type value, as defined in RFC2131
+ * output: const char *: the stringified packet type
+ */
+
+const char *
+pkt_type_to_string(uchar_t type)
+{
+ /*
+ * note: the ordering here allows direct indexing of the table
+ * based on the RFC2131 packet type value passed in.
+ */
+
+ static const char *types[] = {
+ "BOOTP", "DISCOVER", "OFFER", "REQUEST", "DECLINE",
+ "ACK", "NAK", "RELEASE", "INFORM"
+ };
+
+ if (type >= (sizeof (types) / sizeof (*types)) || types[type] == NULL)
+ return ("<unknown>");
+
+ return (types[type]);
+}
+
+/*
+ * dlpi_to_arp(): converts DLPI datalink types into ARP datalink types
+ *
+ * input: uchar_t: the DLPI datalink type
+ * output: uchar_t: the ARP datalink type (0 if no corresponding code)
+ */
+
+uchar_t
+dlpi_to_arp(uchar_t dlpi_type)
+{
+ switch (dlpi_type) {
+
+ case DL_ETHER:
+ return (1);
+
+ case DL_FRAME:
+ return (15);
+
+ case DL_ATM:
+ return (16);
+
+ case DL_HDLC:
+ return (17);
+
+ case DL_FC:
+ return (18);
+
+ case DL_CSMACD: /* ieee 802 networks */
+ case DL_TPB:
+ case DL_TPR:
+ case DL_METRO:
+ case DL_FDDI:
+ return (6);
+ case DL_IB:
+ return (ARPHRD_IB);
+ }
+
+ return (0);
+}
+
+/*
+ * monosec_to_string(): converts a monosec_t into a date string
+ *
+ * input: monosec_t: the monosec_t to convert
+ * output: const char *: the corresponding date string
+ */
+
+const char *
+monosec_to_string(monosec_t monosec)
+{
+ time_t time = monosec_to_time(monosec);
+ char *time_string = ctime(&time);
+
+ /* strip off the newline -- ugh, why, why, why.. */
+ time_string[strlen(time_string) - 1] = '\0';
+ return (time_string);
+}
+
+/*
+ * monosec(): returns a monotonically increasing time in seconds that
+ * is not affected by stime(2) or adjtime(2).
+ *
+ * input: void
+ * output: monosec_t: the number of seconds since some time in the past
+ */
+
+monosec_t
+monosec(void)
+{
+ return (gethrtime() / NANOSEC);
+}
+
+/*
+ * monosec_to_time(): converts a monosec_t into real wall time
+ *
+ * input: monosec_t: the absolute monosec_t to convert
+ * output: time_t: the absolute time that monosec_t represents in wall time
+ */
+
+time_t
+monosec_to_time(monosec_t abs_monosec)
+{
+ return (abs_monosec - monosec()) + time(NULL);
+}
+
+/*
+ * send_ok_reply(): sends an "ok" reply to a request and closes the ipc
+ * connection
+ *
+ * input: dhcp_ipc_request_t *: the request to reply to
+ * int *: the ipc connection file descriptor (set to -1 on return)
+ * output: void
+ * note: the request is freed (thus the request must be on the heap).
+ */
+
+void
+send_ok_reply(dhcp_ipc_request_t *request, int *control_fd)
+{
+ send_error_reply(request, 0, control_fd);
+}
+
+/*
+ * send_error_reply(): sends an "error" reply to a request and closes the ipc
+ * connection
+ *
+ * input: dhcp_ipc_request_t *: the request to reply to
+ * int: the error to send back on the ipc connection
+ * int *: the ipc connection file descriptor (set to -1 on return)
+ * output: void
+ * note: the request is freed (thus the request must be on the heap).
+ */
+
+void
+send_error_reply(dhcp_ipc_request_t *request, int error, int *control_fd)
+{
+ send_data_reply(request, control_fd, error, DHCP_TYPE_NONE, NULL, NULL);
+}
+
+/*
+ * send_data_reply(): sends a reply to a request and closes the ipc connection
+ *
+ * input: dhcp_ipc_request_t *: the request to reply to
+ * int *: the ipc connection file descriptor (set to -1 on return)
+ * int: the status to send back on the ipc connection (zero for
+ * success, DHCP_IPC_E_* otherwise).
+ * dhcp_data_type_t: the type of the payload in the reply
+ * void *: the payload for the reply, or NULL if there is no payload
+ * size_t: the size of the payload
+ * output: void
+ * note: the request is freed (thus the request must be on the heap).
+ */
+
+void
+send_data_reply(dhcp_ipc_request_t *request, int *control_fd,
+ int error, dhcp_data_type_t type, void *buffer, size_t size)
+{
+ dhcp_ipc_reply_t *reply;
+
+ if (*control_fd == -1)
+ return;
+
+ reply = dhcp_ipc_alloc_reply(request, error, buffer, size, type);
+ if (reply == NULL)
+ dhcpmsg(MSG_ERR, "send_data_reply: cannot allocate reply");
+
+ else if (dhcp_ipc_send_reply(*control_fd, reply) != 0)
+ dhcpmsg(MSG_ERR, "send_data_reply: dhcp_ipc_send_reply");
+
+ /*
+ * free the request since we've now used it to send our reply.
+ * we can also close the socket since the reply has been sent.
+ */
+
+ free(reply);
+ free(request);
+ (void) dhcp_ipc_close(*control_fd);
+ *control_fd = -1;
+}
+
+/*
+ * print_server_msg(): prints a message from a DHCP server
+ *
+ * input: struct ifslist *: the interface the message came in on
+ * DHCP_OPT *: the option containing the string to display
+ * output: void
+ */
+
+void
+print_server_msg(struct ifslist *ifsp, DHCP_OPT *p)
+{
+ dhcpmsg(MSG_INFO, "%s: message from server: %.*s", ifsp->if_name,
+ p->len, p->value);
+}
+
+/*
+ * alrm_exit(): Signal handler for SIGARLM. terminates grandparent.
+ *
+ * input: int: signal the handler was called with.
+ *
+ * output: void
+ */
+
+static void
+alrm_exit(int sig)
+{
+ int exitval;
+
+ if (sig == SIGALRM && grandparent != 0)
+ exitval = EXIT_SUCCESS;
+ else
+ exitval = EXIT_FAILURE;
+
+ _exit(exitval);
+}
+
+/*
+ * daemonize(): daemonizes the process
+ *
+ * input: void
+ * output: int: 1 on success, 0 on failure
+ */
+
+int
+daemonize(void)
+{
+ /*
+ * We've found that adoption takes sufficiently long that
+ * a dhcpinfo run after dhcpagent -a is started may occur
+ * before the agent is ready to process the request.
+ * The result is an error message and an unhappy user.
+ *
+ * The initial process now sleeps for DHCP_ADOPT_SLEEP,
+ * unless interrupted by a SIGALRM, in which case it
+ * exits immediately. This has the effect that the
+ * grandparent doesn't exit until the dhcpagent is ready
+ * to process requests. This defers the the balance of
+ * the system start-up script processing until the
+ * dhcpagent is ready to field requests.
+ *
+ * grandparent is only set for the adopt case; other
+ * cases do not require the wait.
+ */
+
+ if (grandparent != 0)
+ (void) signal(SIGALRM, alrm_exit);
+
+ switch (fork()) {
+
+ case -1:
+ return (0);
+
+ case 0:
+ if (grandparent != 0)
+ (void) signal(SIGALRM, SIG_DFL);
+
+ /*
+ * setsid() makes us lose our controlling terminal,
+ * and become both a session leader and a process
+ * group leader.
+ */
+
+ (void) setsid();
+
+ /*
+ * under POSIX, a session leader can accidentally
+ * (through open(2)) acquire a controlling terminal if
+ * it does not have one. just to be safe, fork again
+ * so we are not a session leader.
+ */
+
+ switch (fork()) {
+
+ case -1:
+ return (0);
+
+ case 0:
+ (void) signal(SIGHUP, SIG_IGN);
+ (void) chdir("/");
+ (void) umask(022);
+ closefrom(0);
+ break;
+
+ default:
+ _exit(EXIT_SUCCESS);
+ }
+ break;
+
+ default:
+ if (grandparent != 0) {
+ (void) signal(SIGCHLD, SIG_IGN);
+ dhcpmsg(MSG_DEBUG, "dhcpagent: daemonize: "
+ "waiting for adoption to complete.");
+ if (sleep(DHCP_ADOPT_SLEEP) == 0) {
+ dhcpmsg(MSG_WARNING, "dhcpagent: daemonize: "
+ "timed out awaiting adoption.");
+ }
+ }
+ _exit(EXIT_SUCCESS);
+ }
+
+ return (1);
+}
+
+/*
+ * update_default_route(): update the interface's default route
+ *
+ * input: int: the type of message; either RTM_ADD or RTM_DELETE
+ * struct in_addr: the default gateway to use
+ * const char *: the interface associated with the route
+ * int: any additional flags (besides RTF_STATIC and RTF_GATEWAY)
+ * output: int: 1 on success, 0 on failure
+ */
+
+static int
+update_default_route(const char *ifname, int type, struct in_addr *gateway_nbo,
+ int flags)
+{
+ static int rtsock_fd = -1;
+ struct {
+ struct rt_msghdr rm_mh;
+ struct sockaddr_in rm_dst;
+ struct sockaddr_in rm_gw;
+ struct sockaddr_in rm_mask;
+ struct sockaddr_dl rm_ifp;
+ } rtmsg;
+
+ if (rtsock_fd == -1) {
+ rtsock_fd = socket(PF_ROUTE, SOCK_RAW, 0);
+ if (rtsock_fd == -1) {
+ dhcpmsg(MSG_ERR, "update_default_route: "
+ "cannot create routing socket");
+ return (0);
+ }
+ }
+
+ (void) memset(&rtmsg, 0, sizeof (rtmsg));
+ rtmsg.rm_mh.rtm_version = RTM_VERSION;
+ rtmsg.rm_mh.rtm_msglen = sizeof (rtmsg);
+ rtmsg.rm_mh.rtm_type = type;
+ rtmsg.rm_mh.rtm_pid = getpid();
+ rtmsg.rm_mh.rtm_flags = RTF_GATEWAY | RTF_STATIC | flags;
+ rtmsg.rm_mh.rtm_addrs = RTA_GATEWAY | RTA_DST | RTA_NETMASK | RTA_IFP;
+
+ rtmsg.rm_gw.sin_family = AF_INET;
+ rtmsg.rm_gw.sin_addr = *gateway_nbo;
+
+ rtmsg.rm_dst.sin_family = AF_INET;
+ rtmsg.rm_dst.sin_addr.s_addr = htonl(INADDR_ANY);
+
+ rtmsg.rm_mask.sin_family = AF_INET;
+ rtmsg.rm_mask.sin_addr.s_addr = htonl(0);
+
+ rtmsg.rm_ifp.sdl_family = AF_LINK;
+ rtmsg.rm_ifp.sdl_index = if_nametoindex(ifname);
+
+ return (write(rtsock_fd, &rtmsg, sizeof (rtmsg)) == sizeof (rtmsg));
+}
+
+/*
+ * add_default_route(): add the default route to the given gateway
+ *
+ * input: const char *: the name of the interface associated with the route
+ * struct in_addr: the default gateway to add
+ * output: int: 1 on success, 0 on failure
+ */
+
+int
+add_default_route(const char *ifname, struct in_addr *gateway_nbo)
+{
+ if (strchr(ifname, ':') != NULL) /* see README */
+ return (1);
+
+ return (update_default_route(ifname, RTM_ADD, gateway_nbo, RTF_UP));
+}
+
+/*
+ * del_default_route(): deletes the default route to the given gateway
+ *
+ * input: const char *: the name of the interface associated with the route
+ * struct in_addr: if not INADDR_ANY, the default gateway to remove
+ * output: int: 1 on success, 0 on failure
+ */
+
+int
+del_default_route(const char *ifname, struct in_addr *gateway_nbo)
+{
+ if (strchr(ifname, ':') != NULL)
+ return (1);
+
+ if (gateway_nbo->s_addr == htonl(INADDR_ANY)) /* no router */
+ return (1);
+
+ return (update_default_route(ifname, RTM_DELETE, gateway_nbo, 0));
+}
+
+/*
+ * inactivity_shutdown(): shuts down agent if there are no interfaces to manage
+ *
+ * input: iu_tq_t *: unused
+ * void *: unused
+ * output: void
+ */
+
+/* ARGSUSED */
+void
+inactivity_shutdown(iu_tq_t *tqp, void *arg)
+{
+ if (ifs_count() > 0) /* shouldn't happen, but... */
+ return;
+
+ iu_stop_handling_events(eh, DHCP_REASON_INACTIVITY, NULL, NULL);
+}
+
+/*
+ * graceful_shutdown(): shuts down the agent gracefully
+ *
+ * input: int: the signal that caused graceful_shutdown to be called
+ * output: void
+ */
+
+void
+graceful_shutdown(int sig)
+{
+ iu_stop_handling_events(eh, sig, drain_script, NULL);
+}
+
+/*
+ * register_acknak(): registers dhcp_acknak() to be called back when ACK or
+ * NAK packets are received on a given interface
+ *
+ * input: struct ifslist *: the interface to register for
+ * output: int: 1 on success, 0 on failure
+ */
+
+int
+register_acknak(struct ifslist *ifsp)
+{
+ iu_event_id_t ack_id, ack_bcast_id = -1;
+
+ /*
+ * having an acknak id already registered isn't impossible;
+ * handle the situation as gracefully as possible.
+ */
+
+ if (ifsp->if_acknak_id != -1) {
+ dhcpmsg(MSG_DEBUG, "register_acknak: acknak id pending, "
+ "attempting to cancel");
+ if (unregister_acknak(ifsp) == 0)
+ return (0);
+ }
+
+ switch (ifsp->if_state) {
+
+ case BOUND:
+ case REBINDING:
+ case RENEWING:
+
+ ack_bcast_id = iu_register_event(eh, ifsp->if_sock_fd, POLLIN,
+ dhcp_acknak, ifsp);
+
+ if (ack_bcast_id == -1) {
+ dhcpmsg(MSG_WARNING, "register_acknak: cannot "
+ "register to receive socket broadcasts");
+ return (0);
+ }
+
+ ack_id = iu_register_event(eh, ifsp->if_sock_ip_fd, POLLIN,
+ dhcp_acknak, ifsp);
+ break;
+
+ default:
+ ack_id = iu_register_event(eh, ifsp->if_dlpi_fd, POLLIN,
+ dhcp_acknak, ifsp);
+ break;
+ }
+
+ if (ack_id == -1) {
+ dhcpmsg(MSG_WARNING, "register_acknak: cannot register event");
+ (void) iu_unregister_event(eh, ack_bcast_id, NULL);
+ return (0);
+ }
+
+ ifsp->if_acknak_id = ack_id;
+ hold_ifs(ifsp);
+
+ ifsp->if_acknak_bcast_id = ack_bcast_id;
+ if (ifsp->if_acknak_bcast_id != -1) {
+ hold_ifs(ifsp);
+ dhcpmsg(MSG_DEBUG, "register_acknak: registered broadcast id "
+ "%d", ack_bcast_id);
+ }
+
+ dhcpmsg(MSG_DEBUG, "register_acknak: registered acknak id %d", ack_id);
+ return (1);
+}
+
+/*
+ * unregister_acknak(): unregisters dhcp_acknak() to be called back
+ *
+ * input: struct ifslist *: the interface to unregister for
+ * output: int: 1 on success, 0 on failure
+ */
+
+int
+unregister_acknak(struct ifslist *ifsp)
+{
+ if (ifsp->if_acknak_id != -1) {
+
+ if (iu_unregister_event(eh, ifsp->if_acknak_id, NULL) == 0) {
+ dhcpmsg(MSG_DEBUG, "unregister_acknak: cannot "
+ "unregister acknak id %d on %s",
+ ifsp->if_acknak_id, ifsp->if_name);
+ return (0);
+ }
+
+ dhcpmsg(MSG_DEBUG, "unregister_acknak: unregistered acknak id "
+ "%d", ifsp->if_acknak_id);
+
+ ifsp->if_acknak_id = -1;
+ (void) release_ifs(ifsp);
+ }
+
+ if (ifsp->if_acknak_bcast_id != -1) {
+
+ if (iu_unregister_event(eh, ifsp->if_acknak_bcast_id, NULL)
+ == 0) {
+ dhcpmsg(MSG_DEBUG, "unregister_acknak: cannot "
+ "unregister broadcast id %d on %s",
+ ifsp->if_acknak_id, ifsp->if_name);
+ return (0);
+ }
+
+ dhcpmsg(MSG_DEBUG, "unregister_acknak: unregistered "
+ "broadcast id %d", ifsp->if_acknak_bcast_id);
+
+ ifsp->if_acknak_bcast_id = -1;
+ (void) release_ifs(ifsp);
+ }
+
+ return (1);
+}
+
+/*
+ * bind_sock(): binds a socket to a given IP address and port number
+ *
+ * input: int: the socket to bind
+ * in_port_t: the port number to bind to, host byte order
+ * in_addr_t: the address to bind to, host byte order
+ * output: int: 1 on success, 0 on failure
+ */
+
+int
+bind_sock(int fd, in_port_t port_hbo, in_addr_t addr_hbo)
+{
+ struct sockaddr_in sin;
+ int on = 1;
+
+ (void) memset(&sin, 0, sizeof (struct sockaddr_in));
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(port_hbo);
+ sin.sin_addr.s_addr = htonl(addr_hbo);
+
+ (void) setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (int));
+
+ return (bind(fd, (struct sockaddr *)&sin, sizeof (sin)) == 0);
+}
+
+/*
+ * valid_hostname(): check whether a string is a valid hostname
+ *
+ * input: const char *: the string to verify as a hostname
+ * output: boolean_t: B_TRUE if the string is a valid hostname
+ *
+ * Note that we accept both host names beginning with a digit and
+ * those containing hyphens. Neither is strictly legal according
+ * to the RFCs, but both are in common practice, so we endeavour
+ * to not break what customers are using.
+ */
+
+static boolean_t
+valid_hostname(const char *hostname)
+{
+ unsigned int i;
+
+ for (i = 0; hostname[i] != '\0'; i++) {
+
+ if (isalpha(hostname[i]) || isdigit(hostname[i]) ||
+ (((hostname[i] == '-') || (hostname[i] == '.')) && (i > 0)))
+ continue;
+
+ return (B_FALSE);
+ }
+
+ return (i > 0);
+}
+
+/*
+ * iffile_to_hostname(): return the hostname contained on a line of the form
+ *
+ * [ ^I]*inet[ ^I]+hostname[\n]*\0
+ *
+ * in the file located at the specified path
+ *
+ * input: const char *: the path of the file to look in for the hostname
+ * output: const char *: the hostname at that path, or NULL on failure
+ */
+
+#define IFLINE_MAX 1024 /* maximum length of a hostname.<if> line */
+
+const char *
+iffile_to_hostname(const char *path)
+{
+ FILE *fp;
+ static char ifline[IFLINE_MAX];
+
+ fp = fopen(path, "r");
+ if (fp == NULL)
+ return (NULL);
+
+ /*
+ * /etc/hostname.<if> may contain multiple ifconfig commands, but each
+ * such command is on a separate line (see the "while read ifcmds" code
+ * in /etc/init.d/inetinit). Thus we will read the file a line at a
+ * time, searching for a line of the form
+ *
+ * [ ^I]*inet[ ^I]+hostname[\n]*\0
+ *
+ * extract the host name from it, and check it for validity.
+ */
+ while (fgets(ifline, sizeof (ifline), fp) != NULL) {
+ char *p;
+
+ if ((p = strstr(ifline, "inet")) != NULL) {
+ if ((p != ifline) && !isspace(p[-1])) {
+ (void) fclose(fp);
+ return (NULL);
+ }
+ p += 4; /* skip over "inet" and expect spaces or tabs */
+ if ((*p == '\n') || (*p == '\0')) {
+ (void) fclose(fp);
+ return (NULL);
+ }
+ if (isspace(*p)) {
+ char *nlptr;
+
+ /* no need to read more of the file */
+ (void) fclose(fp);
+
+ while (isspace(*p))
+ p++;
+ if ((nlptr = strrchr(p, '\n')) != NULL)
+ *nlptr = '\0';
+ if (strlen(p) > MAXHOSTNAMELEN) {
+ dhcpmsg(MSG_WARNING,
+ "iffile_to_hostname:"
+ " host name too long");
+ return (NULL);
+ }
+ if (valid_hostname(p)) {
+ return (p);
+ } else {
+ dhcpmsg(MSG_WARNING,
+ "iffile_to_hostname:"
+ " host name not valid");
+ return (NULL);
+ }
+ } else {
+ (void) fclose(fp);
+ return (NULL);
+ }
+ }
+ }
+
+ (void) fclose(fp);
+ return (NULL);
+}
diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/util.h b/usr/src/cmd/cmd-inet/sbin/dhcpagent/util.h
new file mode 100644
index 0000000000..51070ab658
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/util.h
@@ -0,0 +1,84 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef UTIL_H
+#define UTIL_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <netinet/dhcp.h>
+#include <libinetutil.h>
+#include <dhcpagent_ipc.h>
+
+/*
+ * general utility functions which have no better home. see util.c
+ * for documentation on how to use the exported functions.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ifslist; /* forward declaration */
+
+typedef int64_t monosec_t; /* see README for details */
+
+/* conversion functions */
+const char *pkt_type_to_string(uchar_t);
+const char *monosec_to_string(monosec_t);
+time_t monosec_to_time(monosec_t);
+uchar_t dlpi_to_arp(uchar_t);
+
+/* shutdown handlers */
+void graceful_shutdown(int);
+void inactivity_shutdown(iu_tq_t *, void *);
+
+/* acknak handlers */
+int register_acknak(struct ifslist *);
+int unregister_acknak(struct ifslist *);
+
+/* ipc functions */
+void send_error_reply(dhcp_ipc_request_t *, int, int *);
+void send_ok_reply(dhcp_ipc_request_t *, int *);
+void send_data_reply(dhcp_ipc_request_t *, int *, int,
+ dhcp_data_type_t, void *, size_t);
+
+/* miscellaneous */
+int add_default_route(const char *, struct in_addr *);
+int del_default_route(const char *, struct in_addr *);
+int daemonize(void);
+monosec_t monosec(void);
+void print_server_msg(struct ifslist *, DHCP_OPT *);
+int bind_sock(int, in_port_t, in_addr_t);
+const char *iffile_to_hostname(const char *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UTIL_H */
diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpinfo/Makefile b/usr/src/cmd/cmd-inet/sbin/dhcpinfo/Makefile
new file mode 100644
index 0000000000..d97f88d796
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpinfo/Makefile
@@ -0,0 +1,55 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 1999-2003 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# cmd/cmd-inet/sbin/dhcpinfo/Makefile
+#
+
+PROG = dhcpinfo
+ROOTFS_PROG = $(PROG)
+OBJS = $(PROG).o
+
+include ../../../Makefile.cmd
+
+LDLIBS += -ldhcpagent -ldhcputil
+LINTFLAGS += -u # due to problems with libgen
+
+.KEEP_STATE:
+
+all: $(ROOTFS_PROG)
+
+$(PROG): $(OBJS)
+ $(LINK.c) -o $@ $(OBJS) $(LDLIBS)
+ $(POST_PROCESS)
+
+install: all $(ROOTSBINPROG)
+
+clean:
+ $(RM) $(OBJS)
+
+lint: lint_PROG
+
+include ../../../Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpinfo/dhcpinfo.c b/usr/src/cmd/cmd-inet/sbin/dhcpinfo/dhcpinfo.c
new file mode 100644
index 0000000000..ef3d6d1274
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpinfo/dhcpinfo.c
@@ -0,0 +1,230 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <dhcpagent_ipc.h>
+#include <dhcp_inittab.h>
+#include <dhcp_symbol.h>
+
+#define DHCP_INFO_VENDOR_START 256
+
+static void
+usage(const char *program)
+{
+ (void) fprintf(stderr,
+ "usage: %s [-i interface] [-n limit] [-c] code\n"
+ " %s [-i interface] [-n limit] [-c] identifier\n",
+ program, program);
+
+ exit(DHCP_EXIT_BADARGS);
+}
+
+int
+main(int argc, char **argv)
+{
+ ssize_t max_lines = -1;
+ size_t gran, n_spaces = 0;
+ dhcp_optnum_t optnum;
+ dhcp_ipc_request_t *request;
+ dhcp_ipc_reply_t *reply;
+ int c, error, i;
+ char *ifname = "";
+ char *value, *valuep;
+ dhcp_symbol_t *entry;
+ DHCP_OPT *opt;
+ size_t opt_len;
+ boolean_t is_canonical = B_FALSE;
+
+ while ((c = getopt(argc, argv, "cn:i:")) != EOF) {
+
+ switch (c) {
+
+ case 'c':
+ is_canonical = B_TRUE;
+ break;
+
+ case 'i':
+ ifname = optarg;
+ break;
+
+ case 'n':
+ max_lines = strtoul(optarg, NULL, 0);
+ break;
+
+ case '?':
+ usage(argv[0]);
+
+ default:
+ break;
+ }
+ }
+
+ if (argc - optind != 1)
+ usage(argv[0]);
+
+ /*
+ * we either have a code or an identifer. if we have a code,
+ * then values over 256 indicate a vendor option. if we have
+ * an identifier, then use inittab_getbyname() to turn the
+ * identifier into a code, then send the request over the wire.
+ */
+
+ if (isalpha(*argv[optind])) {
+
+ entry = inittab_getbyname(ITAB_CAT_SITE|ITAB_CAT_STANDARD|
+ ITAB_CAT_VENDOR|ITAB_CAT_FIELD, ITAB_CONS_INFO,
+ argv[optind]);
+
+ if (entry == NULL) {
+ (void) fprintf(stderr, "%s: unknown identifier `%s'\n",
+ argv[0], argv[optind]);
+ return (DHCP_EXIT_BADARGS);
+ }
+
+ optnum.code = entry->ds_code;
+ optnum.category = entry->ds_category;
+
+ } else {
+
+ optnum.code = strtoul(argv[optind], 0, 0);
+ optnum.category = ITAB_CAT_STANDARD|ITAB_CAT_SITE;
+
+ /*
+ * sigh. this is a hack, but it's needed for backward
+ * compatibility with the CA dhcpinfo program.
+ */
+
+ if (optnum.code > DHCP_INFO_VENDOR_START) {
+ optnum.code -= DHCP_INFO_VENDOR_START;
+ optnum.category = ITAB_CAT_VENDOR;
+ }
+
+ entry = inittab_getbycode(optnum.category, ITAB_CONS_INFO,
+ optnum.code);
+
+ if (entry == NULL) {
+ (void) fprintf(stderr, "%s: unknown code `%s'\n",
+ argv[0], argv[optind]);
+ return (DHCP_EXIT_BADARGS);
+ }
+ optnum.category = entry->ds_category;
+ }
+
+ optnum.size = entry->ds_max * inittab_type_to_size(entry);
+
+ /*
+ * send the request to the agent and reap the reply
+ */
+
+ request = dhcp_ipc_alloc_request(DHCP_GET_TAG, ifname, &optnum,
+ sizeof (dhcp_optnum_t), DHCP_TYPE_OPTNUM);
+
+ if (request == NULL)
+ return (DHCP_EXIT_SYSTEM);
+
+ error = dhcp_ipc_make_request(request, &reply, DHCP_IPC_WAIT_DEFAULT);
+ if (error != 0 || reply->return_code != 0) {
+
+ if (error == 0)
+ error = reply->return_code;
+
+ (void) fprintf(stderr, "%s: %s\n", argv[0],
+ dhcp_ipc_strerror(error));
+
+ if (error == DHCP_IPC_E_TIMEOUT)
+ return (DHCP_EXIT_TIMEOUT);
+
+ return (DHCP_EXIT_FAILURE);
+ }
+
+ opt = dhcp_ipc_get_data(reply, &opt_len, NULL);
+
+ /*
+ * no data means that the client has an ACK but has no information
+ * about the specified option; return success
+ */
+
+ if (opt_len == 0)
+ return (DHCP_EXIT_SUCCESS);
+
+ /*
+ * check for protocol error
+ */
+
+ if (opt_len < 2 || (opt_len - 2 != opt->len))
+ return (DHCP_EXIT_FAILURE);
+
+ if (is_canonical) {
+
+ value = malloc(opt->len * (sizeof ("0xNN") + 1));
+ if (value == NULL) {
+ (void) fprintf(stderr, "%s: out of memory\n", argv[0]);
+ return (DHCP_EXIT_FAILURE);
+ }
+
+ for (i = 0, valuep = value; i < opt->len; i++)
+ valuep += sprintf(valuep, "0x%02X ", opt->value[i]);
+
+ valuep[-1] = '\0';
+ gran = 1;
+
+ } else {
+
+ value = inittab_decode(entry, opt->value, opt->len, B_TRUE);
+ if (value == NULL) {
+ (void) fprintf(stderr, "%s: cannot decode agent's "
+ "reply\n", argv[0]);
+ return (DHCP_EXIT_FAILURE);
+ }
+
+ gran = entry->ds_gran;
+ }
+
+ /*
+ * now display `gran' items per line, printing at most `max_lines'.
+ */
+
+ for (i = 0; value[i] != '\0'; i++) {
+ if (value[i] == ' ') {
+ if ((++n_spaces % gran) == 0) {
+ value[i] = '\n';
+ if (max_lines != -1 && --max_lines == 0) {
+ value[i] = '\0';
+ break;
+ }
+ }
+ }
+ }
+
+ (void) printf("%s\n", value);
+
+ return (DHCP_EXIT_SUCCESS);
+}
diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpinfo/inc.flg b/usr/src/cmd/cmd-inet/sbin/dhcpinfo/inc.flg
new file mode 100644
index 0000000000..d7246ea4b0
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpinfo/inc.flg
@@ -0,0 +1,30 @@
+#!/bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+find_files "s.*" usr/src/common/net/dhcp
diff --git a/usr/src/cmd/cmd-inet/sbin/ifparse/Makefile b/usr/src/cmd/cmd-inet/sbin/ifparse/Makefile
new file mode 100644
index 0000000000..916bd8b933
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/sbin/ifparse/Makefile
@@ -0,0 +1,61 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Makefile for "ifparse"
+#
+# Copyright 2000-2003 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+PROG = ifparse
+ROOTFS_PROG = $(PROG)
+OBJS= ifparse.o
+SRCS= $(OBJS:%.o=%.c)
+
+include ../../../Makefile.cmd
+include ../../Makefile.cmd-inet
+
+CPPFLAGS += -I$(CMDINETCOMMONDIR)
+LINTFLAGS += -m
+
+# these #defines are required to use UNIX 98 interfaces
+$(OBJS) := CPPFLAGS +=-D_POSIX_C_SOURCE
+LINTFLAGS += -D_POSIX_C_SOURCE -I$(CMDINETCOMMONDIR)
+
+.KEEP_STATE:
+
+all: $(ROOTFS_PROG)
+
+$(PROG): $(OBJS)
+ $(LINK.c) $(OBJS) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+install: all $(ROOTSBINPROG)
+
+clean:
+ $(RM) $(OBJS)
+
+lint: lint_SRCS
+
+include ../../../Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/sbin/ifparse/ifparse.c b/usr/src/cmd/cmd-inet/sbin/ifparse/ifparse.c
new file mode 100644
index 0000000000..fbaf842d3c
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/sbin/ifparse/ifparse.c
@@ -0,0 +1,584 @@
+/*
+ * Copyright 2000-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved. The Berkeley software License Agreement
+ * specifies the terms and conditions for redistribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Ifparse splits up an ifconfig command line, and was written for use
+ * with the networking boot script /etc/init.d/network (which is in the
+ * source tree as usr/src/cmd/initpkg/init.d/network).
+ *
+ * Ifparse can extract selected parts of the ifconfig command line,
+ * such as failover address configuration ("ifparse -f"), or everything
+ * except failover address configuration ("ifparse -s"). By default,
+ * all parts of the command line are extracted (equivalent to ("ifparse -fs").
+ *
+ * Examples:
+ *
+ * The command:
+ *
+ * ifparse inet 1.2.3.4 up group two addif 1.2.3.5 up addif 1.2.3.6 up
+ *
+ * Produces the following on standard output:
+ *
+ * set 1.2.3.4 up
+ * group two
+ * addif 1.2.3.5 up
+ * addif 1.2.3.6 up
+ *
+ * The optional "set" and "destination" keywords are added to make the
+ * output easier to process by a script or another command.
+ *
+ * The command:
+ *
+ * ifparse -f inet 1.2.3.4 -failover up group two addif 1.2.3.5 up
+ *
+ * Produces:
+ *
+ * addif 1.2.3.5 up
+ *
+ * Only failover address configuration has been requested. Address
+ * 1.2.3.4 is a non-failover address, and so isn't output.
+ *
+ * The "failover" and "-failover" commands can occur several times for
+ * a given logical interface. Only the last one counts. For example:
+ *
+ * ifparse -f inet 1.2.3.4 -failover failover -failover failover up
+ *
+ * Produces:
+ *
+ * set 1.2.3.4 -failover failover -failover failover up
+ *
+ * No attempt is made to clean up such "pathological" command lines, by
+ * removing redundant "failover" and "-failover" commands.
+ */
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+/*
+ * Parser flags:
+ *
+ * PARSEFIXED
+ * Command should only appear if non-failover commands
+ * are requested.
+ * PARSEMOVABLE
+ * Command should only appear if failover commands are
+ * requested.
+ * PARSENOW
+ * Don't buffer the command, dump it to output immediately.
+ * PARSEADD
+ * Indicates processing has moved on to additional
+ * logical interfaces.
+ * Dump the buffer to output and clear buffer contents.
+ * PARSESET
+ * The "set" and "destination" keywords are optional.
+ * This flag indicates that the next address not prefixed
+ * with a keyword will be a destination address.
+ * PARSELOG0
+ * Command not valid on additional logical interfaces.
+ */
+
+#define PARSEFIXED 0x01
+#define PARSEMOVABLE 0x02
+#define PARSENOW 0x04
+#define PARSEADD 0x08
+#define PARSESET 0x10
+#define PARSELOG0 0x20
+
+typedef enum { AF_UNSPEC, AF_INET, AF_INET6, AF_ANY } ac_t;
+
+#define NEXTARG (-1)
+
+#define END_OF_TABLE (-1)
+
+/* Parsemode, the type of commands requested by the user. */
+int parsemode = 0;
+
+/* Parsetype, the type of the command currently in the buffer. */
+int parsetype = PARSEFIXED | PARSEMOVABLE;
+
+/* Parsebuf, pointer to the buffer. */
+char *parsebuf = NULL;
+
+/* Parsebuflen, the size of the buffer area. */
+unsigned parsebuflen = 0;
+
+/* Parsedumplen, the amount of the buffer currently in use. */
+unsigned parsedumplen = 0;
+
+/*
+ * Setaddr, used to decide whether an address without a keyword
+ * prefix is a source or destination address.
+ */
+boolean_t setaddr = _B_FALSE;
+
+/*
+ * Some ifconfig commands are only valid on the first logical interface.
+ * As soon as an "addif" command is seen, "addint" is set.
+ */
+boolean_t addint = _B_FALSE;
+
+/*
+ * The parser table is based on that in ifconfig. A command may or
+ * may not have an argument, as indicated by whether NEXTARG is in the
+ * second column. Some commands can only be used with certain address
+ * families, as indicated in the third column. The fourth column
+ * contains flags that control parser action.
+ *
+ * Ifparse buffers logical interface configuration commands such as "set",
+ * "netmask" and "broadcast". This buffering continues until an "addif"
+ * command is seen, at which point the buffer is emptied, and the process
+ * starts again.
+ *
+ * Some commands do not relate to logical interface configuration and are
+ * dumped to output as soon as they are seen, such as "group" and "standby".
+ *
+ */
+
+struct cmd {
+ char *c_name;
+ int c_parameter; /* NEXTARG means next argv */
+ int c_af; /* address family restrictions */
+ int c_parseflags; /* parsing flags */
+} cmds[] = {
+ { "up", 0, AF_ANY, 0 },
+ { "down", 0, AF_ANY, 0 },
+ { "trailers", 0, AF_ANY, PARSENOW },
+ { "-trailers", 0, AF_ANY, PARSENOW },
+ { "arp", 0, AF_INET, PARSENOW },
+ { "-arp", 0, AF_INET, PARSENOW },
+ { "private", 0, AF_ANY, 0 },
+ { "-private", 0, AF_ANY, 0 },
+ { "router", 0, AF_ANY, PARSELOG0 },
+ { "-router", 0, AF_ANY, PARSELOG0 },
+ { "xmit", 0, AF_ANY, 0 },
+ { "-xmit", 0, AF_ANY, 0 },
+ { "-nud", 0, AF_INET6, PARSENOW },
+ { "nud", 0, AF_INET6, PARSENOW },
+ { "anycast", 0, AF_ANY, 0 },
+ { "-anycast", 0, AF_ANY, 0 },
+ { "local", 0, AF_ANY, 0 },
+ { "-local", 0, AF_ANY, 0 },
+ { "deprecated", 0, AF_ANY, 0 },
+ { "-deprecated", 0, AF_ANY, 0 },
+ { "preferred", 0, AF_INET6, 0 },
+ { "-preferred", 0, AF_INET6, 0 },
+ { "debug", 0, AF_ANY, PARSENOW },
+ { "verbose", 0, AF_ANY, PARSENOW },
+ { "netmask", NEXTARG, AF_INET, 0 },
+ { "metric", NEXTARG, AF_ANY, 0 },
+ { "mtu", NEXTARG, AF_ANY, 0 },
+ { "index", NEXTARG, AF_ANY, PARSELOG0 },
+ { "broadcast", NEXTARG, AF_INET, 0 },
+ { "auto-revarp", 0, AF_INET, PARSEFIXED},
+ { "plumb", 0, AF_ANY, PARSENOW },
+ { "unplumb", 0, AF_ANY, PARSENOW },
+ { "subnet", NEXTARG, AF_ANY, 0 },
+ { "token", NEXTARG, AF_INET6, PARSELOG0 },
+ { "tsrc", NEXTARG, AF_ANY, PARSELOG0 },
+ { "tdst", NEXTARG, AF_ANY, PARSELOG0 },
+ { "encr_auth_algs", NEXTARG, AF_ANY, PARSELOG0 },
+ { "encr_algs", NEXTARG, AF_ANY, PARSELOG0 },
+ { "auth_algs", NEXTARG, AF_ANY, PARSELOG0 },
+ { "addif", NEXTARG, AF_ANY, PARSEADD },
+ { "removeif", NEXTARG, AF_ANY, PARSELOG0 },
+ { "modlist", 0, AF_ANY, PARSENOW },
+ { "modinsert", NEXTARG, AF_ANY, PARSENOW },
+ { "modremove", NEXTARG, AF_ANY, PARSENOW },
+ { "failover", 0, AF_ANY, PARSEMOVABLE },
+ { "-failover", 0, AF_ANY, PARSEFIXED },
+ { "standby", 0, AF_ANY, PARSENOW },
+ { "-standby", 0, AF_ANY, PARSENOW },
+ { "failed", 0, AF_ANY, PARSENOW },
+ { "-failed", 0, AF_ANY, PARSENOW },
+ { "group", NEXTARG, AF_ANY, PARSELOG0 },
+ { "configinfo", 0, AF_ANY, PARSENOW },
+ { "encaplimit", NEXTARG, AF_ANY, PARSELOG0 },
+ { "-encaplimit", 0, AF_ANY, PARSELOG0 },
+ { "thoplimit", NEXTARG, AF_ANY, PARSELOG0 },
+#ifdef DEBUG
+ { "getnd", NEXTARG, AF_INET6, PARSELOG0 },
+ { "setnd", NEXTARG, AF_INET6, PARSELOG0 },
+ { "delnd", NEXTARG, AF_INET6, PARSELOG0 },
+#endif
+/* XXX for testing SIOCT* ioctls. Remove */
+ { "set", NEXTARG, AF_ANY, PARSESET },
+ { "destination", NEXTARG, AF_ANY, 0 },
+ { 0 /* ether addr */, 0, AF_UNSPEC, PARSELOG0 },
+ { 0 /* set */, 0, AF_ANY, PARSESET },
+ { 0 /* destination */, 0, AF_ANY, 0 },
+ { 0, END_OF_TABLE, END_OF_TABLE, END_OF_TABLE},
+};
+
+
+/* Known address families */
+struct afswtch {
+ char *af_name;
+ short af_af;
+} afs[] = {
+ { "inet", AF_INET },
+ { "ether", AF_UNSPEC },
+ { "inet6", AF_INET6 },
+ { 0, 0 }
+};
+
+/*
+ * Append "item" to the buffer. If there isn't enough room in the buffer,
+ * expand it.
+ */
+static void
+parse_append_buf(char *item)
+{
+ unsigned itemlen;
+ unsigned newdumplen;
+
+ if (item == NULL)
+ return;
+
+ itemlen = strlen(item);
+ newdumplen = parsedumplen + itemlen;
+
+ /* Expand dump buffer as needed */
+ if (parsebuflen < newdumplen) {
+ if ((parsebuf = realloc(parsebuf, newdumplen)) == NULL) {
+ perror("ifparse");
+ exit(1);
+ }
+ parsebuflen = newdumplen;
+ }
+ (void) memcpy(parsebuf + parsedumplen, item, itemlen);
+
+ parsedumplen = newdumplen;
+}
+
+/*
+ * Dump the buffer to output.
+ */
+static void
+parse_dump_buf(void)
+{
+ /*
+ * When parsing, a set or addif command, we may be some way into
+ * the command before we definitely know it is movable or fixed.
+ * If we get to the end of the command, and haven't seen a
+ * "failover" or "-failover" flag, the command is movable.
+ */
+ if (!((parsemode == PARSEFIXED) &&
+ (parsetype & PARSEMOVABLE) != 0) &&
+ (parsemode & parsetype) != 0 &&
+ parsedumplen != 0) {
+ unsigned i;
+
+ if (parsebuf[parsedumplen] == ' ')
+ parsedumplen--;
+
+ for (i = 0; i < parsedumplen; i++)
+ (void) putchar(parsebuf[i]);
+
+ (void) putchar('\n');
+ }
+ /* The buffer is kept in case there is more parsing to do */
+ parsedumplen = 0;
+ parsetype = PARSEFIXED | PARSEMOVABLE;
+}
+
+/*
+ * Process a command. The command will either be put in the buffer,
+ * or dumped directly to output. The current contents of the buffer
+ * may be dumped to output.
+ *
+ * The buffer holds commands relating to a particular logical interface.
+ * For example, "set", "destination", "failover", "broadcast", all relate
+ * to a particular interface. Such commands have to be buffered until
+ * all the "failover" and "-failover" commands for that interface have
+ * been seen, only then will we know whether the command is movable
+ * or not. When the "addif" command is seen, we know we are about to
+ * start processing a new logical interface, we've seen all the
+ * "failover" and "-failover" commands for the previous interface, and
+ * can decide whether the buffer contents are movable or not.
+ *
+ */
+static void
+parsedump(char *cmd, int param, int flags, char *arg)
+{
+ char *cmdname; /* Command name */
+ char *cmdarg; /* Argument to command, if it takes one, or NULL */
+
+ /*
+ * Is command only valid on logical interface 0?
+ * If processing commands on an additional logical interface, ignore
+ * the command.
+ * If processing commands on logical interface 0, don't buffer the
+ * command, dump it straight to output.
+ */
+ if ((flags & PARSELOG0) != 0) {
+ if (addint)
+ return;
+ flags |= PARSENOW;
+ }
+
+ /*
+ * If processing the "addif" command, a destination address may
+ * follow without the "destination" prefix. Add PARSESET to the
+ * flags so that such an anonymous address is processed correctly.
+ */
+ if ((flags & PARSEADD) != 0) {
+ flags |= PARSESET;
+ addint = _B_TRUE;
+ }
+
+ /*
+ * Commands that must be dumped straight to output are always fixed
+ * (non-movable) commands.
+ *
+ */
+ if ((flags & PARSENOW) != 0)
+ flags |= PARSEFIXED;
+
+ /*
+ * Source and destination addresses do not have to be prefixed
+ * with the keywords "set" or "destination". Ifparse always
+ * inserts the optional keyword.
+ */
+ if (cmd == NULL) {
+ cmdarg = arg;
+ if ((flags & PARSESET) != 0)
+ cmdname = "set";
+ else if (setaddr) {
+ cmdname = "destination";
+ setaddr = _B_FALSE;
+ } else
+ cmdname = "";
+ } else {
+ cmdarg = (param == NEXTARG) ? arg : NULL;
+ cmdname = cmd;
+ }
+
+ /*
+ * The next address without a prefix will be a destination
+ * address.
+ */
+ if ((flags & PARSESET) != 0)
+ setaddr = _B_TRUE;
+
+ /*
+ * Dump the command straight to output?
+ * Only dump the command if the parse mode specified on
+ * the command line matches the type of the command.
+ */
+ if ((flags & PARSENOW) != 0) {
+ if ((parsemode & flags) != 0) {
+ (void) fputs(cmdname, stdout);
+ if (cmdarg != NULL) {
+ (void) fputc(' ', stdout);
+ (void) fputs(cmdarg, stdout);
+ }
+ (void) fputc('\n', stdout);
+ }
+ return;
+ }
+
+ /*
+ * Only the commands relating to a particular logical interface
+ * are buffered. When an "addif" command is seen, processing is
+ * about to start on a new logical interface, so dump the
+ * buffer to output.
+ */
+ if ((flags & PARSEADD) != 0)
+ parse_dump_buf();
+
+ /*
+ * If the command flags indicate the command is fixed or
+ * movable, update the type of the interface in the buffer
+ * accordingly. For example, "-failover" has the "PARSEFIXED"
+ * flag, and the contents of the buffer are not movable if
+ * "-failover" is seen.
+ */
+ if ((flags & PARSEFIXED) != 0)
+ parsetype &= ~PARSEMOVABLE;
+
+ if ((flags & PARSEMOVABLE) != 0)
+ parsetype &= ~PARSEFIXED;
+
+ parsetype |= flags & (PARSEFIXED | PARSEMOVABLE);
+
+ parse_append_buf(cmdname);
+
+ if (cmdarg != NULL) {
+ parse_append_buf(" ");
+ parse_append_buf(cmdarg);
+ }
+
+ parse_append_buf(" ");
+}
+
+/*
+ * Parse the part of the command line following the address family
+ * specification, if any.
+ *
+ * This function is a modified version of the function "ifconfig" in
+ * ifconfig.c.
+ */
+static int
+ifparse(int argc, char *argv[], struct afswtch *afp)
+{
+ int af;
+
+ if (argc == 0) {
+ return (0);
+ }
+
+ af = afp->af_af;
+
+ if (strcmp(*argv, "auto-dhcp") == 0 || strcmp(*argv, "dhcp") == 0) {
+ if (af == AF_INET) {
+ if ((parsemode & PARSEFIXED) != NULL) {
+ while (argc) {
+ (void) fputs(*argv++, stdout);
+ if (--argc != 0)
+ (void) fputc(' ', stdout);
+ else
+ (void) fputc('\n', stdout);
+ }
+ }
+ return (0);
+ } else {
+ (void) fprintf(stderr, "ifparse: dhcp not supported "
+ "for inet6\n");
+
+ return (1);
+ }
+ }
+
+ while (argc > 0) {
+ struct cmd *p;
+ boolean_t found_cmd;
+
+ found_cmd = _B_FALSE;
+ for (p = cmds; ; p++) {
+ assert(p->c_parseflags != END_OF_TABLE);
+ if (p->c_name) {
+ if (strcmp(*argv, p->c_name) == 0) {
+ /*
+ * indicate that the command was
+ * found and check to see if
+ * the address family is valid
+ */
+ found_cmd = _B_TRUE;
+ if (p->c_af == AF_ANY ||
+ af == p->c_af)
+ break;
+ }
+ } else {
+ if (p->c_af == AF_ANY ||
+ af == p->c_af)
+ break;
+ }
+ }
+ assert(p->c_parseflags != END_OF_TABLE);
+ /*
+ * If we found the keyword, but the address family
+ * did not match spit out an error
+ */
+ if (found_cmd && p->c_name == 0) {
+ (void) fprintf(stderr, "ifparse: Operation %s not"
+ " supported for %s\n", *argv, afp->af_name);
+ return (1);
+ }
+ /*
+ * else (no keyword found), we assume it's an address
+ * of some sort
+ */
+ if (p->c_name == 0 && setaddr) {
+ p++; /* got src, do dst */
+ assert(p->c_parseflags != END_OF_TABLE);
+ }
+ if (p->c_parameter == NEXTARG) {
+ argc--, argv++;
+ if (argc == 0) {
+ (void) fprintf(stderr,
+ "ifparse: no argument for %s\n",
+ p->c_name);
+ return (1);
+ }
+ }
+ /*
+ * Dump the command if:
+ *
+ * there's no address family
+ * restriction
+ * OR
+ * there is a restriction AND
+ * the address families match
+ */
+ if ((p->c_af == AF_ANY) || (af == p->c_af))
+ parsedump(p->c_name, p->c_parameter,
+ p->c_parseflags, *argv);
+ argc--, argv++;
+ }
+ parse_dump_buf();
+
+ return (0);
+}
+
+/*
+ * Print command usage on standard error.
+ */
+static void
+usage(void)
+{
+ (void) fprintf(stderr,
+ "usage: ifparse [ -fs ] <addr_family> <commands>\n");
+}
+
+int
+main(int argc, char *argv[])
+{
+ int c;
+ struct afswtch *afp;
+
+ while ((c = getopt(argc, argv, "fs")) != -1) {
+ switch ((char)c) {
+ case 'f':
+ parsemode |= PARSEMOVABLE;
+ break;
+ case 's':
+ parsemode |= PARSEFIXED;
+ break;
+ case '?':
+ usage();
+ exit(1);
+ }
+ }
+
+ if (parsemode == 0)
+ parsemode = PARSEFIXED | PARSEMOVABLE;
+
+ argc -= optind;
+ argv += optind;
+
+ afp = afs;
+ if (argc > 0) {
+ struct afswtch *aftp;
+ for (aftp = afs; aftp->af_name; aftp++) {
+ if (strcmp(aftp->af_name, *argv) == 0) {
+ argc--; argv++;
+ afp = aftp;
+ break;
+ }
+ }
+ }
+
+ return (ifparse(argc, argv, afp));
+}
diff --git a/usr/src/cmd/cmd-inet/sbin/netstrategy/Makefile b/usr/src/cmd/cmd-inet/sbin/netstrategy/Makefile
new file mode 100644
index 0000000000..97516a8e23
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/sbin/netstrategy/Makefile
@@ -0,0 +1,46 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+PROG = netstrategy
+ROOTFS_PROG = $(PROG)
+
+include ../../../Makefile.cmd
+
+LDLIBS += -lsocket
+
+.KEEP_STATE:
+
+all: $(ROOTFS_PROG)
+
+install: all $(ROOTSBINPROG)
+
+clean:
+
+lint: lint_PROG
+
+include ../../../Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/sbin/netstrategy/netstrategy.c b/usr/src/cmd/cmd-inet/sbin/netstrategy/netstrategy.c
new file mode 100644
index 0000000000..819fcbf71b
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/sbin/netstrategy/netstrategy.c
@@ -0,0 +1,208 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * This program does the following:
+ *
+ * a) Returns:
+ * 0 - if the program successfully determined the net strategy.
+ * !0 - if an error occurred.
+ *
+ * b) If the program is successful, it prints three tokens to
+ * stdout: <root fs type> <interface name> <net config strategy>.
+ * where:
+ * <root fs type> - "nfs" or "ufs"
+ * <interface name> - "hme0" or "none"
+ * <net config strategy> - "dhcp", "rarp", or "none"
+ *
+ * Eg:
+ * # /sbin/netstrategy
+ * ufs hme0 dhcp
+ *
+ * <root fs type> identifies the system's root file system type.
+ *
+ * <interface name> is the 16 char name of the root interface, and is only
+ * set if rarp/dhcp was used to configure the interface.
+ *
+ * <net config strategy> can be either "rarp", "dhcp", or "none" depending
+ * on which strategy was used to configure the interface. Is "none" if
+ * no interface was configured using a net-based strategy.
+ *
+ * CAVEATS: what about autoclient systems? XXX
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <alloca.h>
+#include <sys/systeminfo.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <sys/statvfs.h>
+
+/* ARGSUSED */
+int
+main(int argc, char *argv[])
+{
+ struct statvfs vfs;
+ char *root, *interface, *strategy, dummy;
+ long len;
+ int fd, numifs;
+ struct ifreq *ifr;
+ struct ifconf ifconf;
+
+ /* root location */
+ if (statvfs("/", &vfs) < 0)
+ root = "none";
+ else {
+ if (strncmp(vfs.f_basetype, "nfs", sizeof ("nfs") - 1) == 0)
+ vfs.f_basetype[sizeof ("nfs") - 1] = '\0';
+ root = vfs.f_basetype;
+ }
+
+ /*
+ * Handle the simple case where diskless dhcp tells us everything
+ * we need to know.
+ */
+ if ((len = sysinfo(SI_DHCP_CACHE, &dummy, sizeof (dummy))) > 1) {
+ /* interface is first thing in cache. */
+ strategy = "dhcp";
+ interface = alloca(len);
+ (void) sysinfo(SI_DHCP_CACHE, interface, len);
+ (void) printf("%s %s %s\n", root, interface, strategy);
+ return (0);
+ }
+
+ /*
+ * We're not "nfs dhcp", "nfs none" is impossible, and we don't handle
+ * "ufs rarp" (consumers are coded to deal with this reality), so
+ * there are three possible situations:
+ *
+ * 1. We're "ufs dhcp" if there are any interfaces which have
+ * obtained their addresses through DHCP. That is, if there
+ * are any IFF_UP and non-IFF_VIRTUAL interfaces also have
+ * IFF_DHCPRUNNING set.
+ *
+ * 2. We're "ufs none" if our filesystem is local and there
+ * are no interfaces which have obtained their addresses
+ * through DHCP.
+ *
+ * 3. We're "nfs rarp" if our filesystem is remote and there's
+ * at least IFF_UP non-IFF_VIRTUAL interface (which there
+ * *must* be, since we're running over NFS somehow), then
+ * it must be RARP since SI_DHCP_CACHE call above failed.
+ * It's too bad there isn't an IFF_RARPRUNNING flag.
+ */
+
+ if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
+ (void) fprintf(stderr, "%s: socket: %s\n", argv[0],
+ strerror(errno));
+ return (2);
+ }
+
+ if (ioctl(fd, SIOCGIFNUM, &numifs) < 0) {
+ (void) fprintf(stderr, "%s: SIOCGIFNUM: %s\n", argv[0],
+ strerror(errno));
+ (void) close(fd);
+ return (2);
+ }
+
+ ifconf.ifc_len = numifs * sizeof (struct ifreq);
+ ifconf.ifc_buf = alloca(ifconf.ifc_len);
+
+ if (ioctl(fd, SIOCGIFCONF, &ifconf) < 0) {
+ (void) fprintf(stderr, "%s: SIOCGIFCONF: %s\n", argv[0],
+ strerror(errno));
+ (void) close(fd);
+ return (2);
+ }
+
+ strategy = NULL;
+ interface = NULL;
+
+ for (ifr = ifconf.ifc_req; ifr < &ifconf.ifc_req[ifconf.ifc_len /
+ sizeof (struct ifreq)]; ifr++) {
+
+ if (strchr(ifr->ifr_name, ':') != NULL)
+ continue; /* skip virtual interfaces */
+
+ if (ioctl(fd, SIOCGIFFLAGS, ifr) < 0) {
+ (void) fprintf(stderr, "%s: SIOCGIFFLAGS: %s\n",
+ argv[0], strerror(errno));
+ continue;
+ }
+
+ if (ifr->ifr_flags & (IFF_VIRTUAL|IFF_POINTOPOINT))
+ continue;
+
+ if (ifr->ifr_flags & IFF_UP) {
+ /*
+ * For the "nfs rarp" case, we assume that the first
+ * IFF_UP interface is the one using RARP, so stash
+ * away the first interface in case we need it.
+ *
+ * Since the order of the interfaces retrieved via
+ * SIOCGLIFCONF is not deterministic, this is largely
+ * silliness, but (a) "it's always been this way", (b)
+ * machines booted via diskless RARP typically only
+ * have one interface, and (c) no one consumes the
+ * interface name in the RARP case anyway.
+ */
+ if (interface == NULL)
+ interface = ifr->ifr_name;
+
+ if (ifr->ifr_flags & IFF_DHCPRUNNING) {
+ interface = ifr->ifr_name;
+ strategy = "dhcp";
+ break;
+ }
+ }
+ }
+
+ (void) close(fd);
+
+ if (strcmp(root, "nfs") == 0 || strcmp(root, "cachefs") == 0) {
+ if (interface == NULL) {
+ (void) fprintf(stderr,
+ "%s: cannot identify root interface.\n", argv[0]);
+ return (2);
+ }
+ if (strategy == NULL)
+ strategy = "rarp"; /* must be rarp/bootparams */
+ } else {
+ if (interface == NULL || strategy == NULL)
+ interface = strategy = "none";
+ }
+
+ (void) printf("%s %s %s\n", root, interface, strategy);
+ return (0);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/Makefile b/usr/src/cmd/cmd-inet/usr.bin/Makefile
new file mode 100644
index 0000000000..a982e6a334
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/Makefile
@@ -0,0 +1,182 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+PROG= finger rdate ruptime rwho whois
+SUIDPROG= rcp rlogin rsh
+ALL= $(PROG) $(SUIDPROG)
+SRCS= $(ALL:%=%.c)
+KCMDPROGS= rcp rlogin rsh
+
+SUBDIRS= chat ftp nca netstat pppd pppdump pppstats rdist talk tftp telnet
+SUBDIR1= talk
+MSGSUBDIRS= nca talk
+
+# As programs get lint-clean, add them here. Eventually.
+# This hack should go away, and all in PROG should be lint-clean.
+LINTCLEAN= rlogin.c rsh.c rcp.c rdate.c
+
+# Likewise, as subdirs get lint-clean, add them here. Once
+# they're all clean, replace the dependency of the lint target
+# with SUBDIRS. Also (sigh) deal with the commented-out build lines
+# for the lint rule.
+LINTSUBDIRS= nca netstat pppd tftp
+
+include ../../Makefile.cmd
+include ../Makefile.cmd-inet
+
+COMMONOBJS= kcmd.o
+COMMONPOFILES= $(COMMONOBJS:.o=.po)
+COMMONSRCS= $(CMDINETCOMMONDIR)/$(COMMONOBJS:.o=.c)
+
+POFILES= rlogin.po rsh.po rcp.po $(COMMONPOFILES)
+POFILE= usr.bin.po
+
+all:= TARGET= all
+install:= TARGET= install
+clean:= TARGET= clean
+clobber:= TARGET= clobber
+lint:= TARGET= lint
+_msg:= TARGET= _msg
+
+ROOTSUIDPROG= $(SUIDPROG:%=$(ROOTBIN)/%)
+$(ROOTSUIDPROG) := FILEMODE= 04555
+$(ROOTSUIDPROG) := OWNER= root
+
+CPPFLAGS += -DSYSV -DSTRNET -DBSD_COMP -I$(CMDINETCOMMONDIR)
+
+# Eventually just plain CFLAGS should be += -v, but not until all in
+# PROGS are lint clean.
+$(LINTCLEAN) := CFLAGS += $(CCVERBOSE)
+
+finger := CFLAGS += $(CCVERBOSE)
+# Enable large file support for reading the lastlog file.
+finger := CPPFLAGS += -D_FILE_OFFSET_BITS=64
+
+finger := LDLIBS += -lnsl -lcurses -lcmd -lsocket
+rcp lint-rcp := LDLIBS += -lsocket -lsec -lsendfile
+rdate lint-rdate:= LDLIBS += -lsocket
+rlogin lint-rlogin := LDLIBS += -lnsl -lsocket
+rsh lint-rsh := LDLIBS += -lsocket
+whois := LDLIBS += -lnsl -lsocket
+
+include $(SRC)/lib/gss_mechs/mech_krb5/Makefile.mech_krb5
+$(KCMDPROGS) := LDLIBS += -lnsl -lmech_krb5
+$(KCMDPROGS) := LDFLAGS += $(ZIGNORE) $(ZLAZYLOAD) $(KRUNPATH) \
+ -L$(ROOT)$(KLIBDIR_DO) \
+ -L$(ROOT)$(KLIBDIR_GL)
+KCMDLINTS= $(KCMDPROGS:%=lint-%)
+
+$(COMMONPOFILES) \
+rlogin.po rcp.po rsh.po \
+$(KCMDPROGS) \
+$(KCMDLINTS) := CPPFLAGS += -DKERBEROS \
+ -I$(CMDINETCOMMONDIR) \
+ -I$(SRC)/lib/gss_mechs/mech_krb5 \
+ -I$(SRC)/uts/common/gssapi/mechs/krb5/include \
+ -I$(SRC)/lib/gss_mechs/mech_krb5/include \
+ -I$(SRC)/lib/gss_mechs/mech_krb5/include/krb5
+
+# "-erroff=E_NAME_USED_NOT_DEF2" and "-erroff=E_NAME_DEF_NOT_USED2"
+# are required because lint problems in the Kerberos 5 framework.
+$(KCMDLINTS) := LINTFLAGS += -lnsl \
+ -erroff=E_NAME_USED_NOT_DEF2 \
+ -erroff=E_NAME_DEF_NOT_USED2
+
+# Extra source files to lint with
+LINTXTRA=
+$(KCMDLINTS) := LINTXTRA += $(COMMONSRCS)
+
+ROOTSUNWRCP= $(ROOT)/usr/lib/sunw,rcp
+ROOTRSHSYMLINK= $(ROOT)/usr/ucb/rsh
+ROOTREMSHSYMLINK=$(ROOT)/usr/bin/remsh
+
+.KEEP_STATE:
+
+all: $(ALL) $(SUBDIRS)
+
+install: all .WAIT $(ROOTPROG) $(ROOTSUIDPROG) \
+ $(SUBDIRS) $(ROOTSUNWRCP) $(ROOTRSHSYMLINK) $(ROOTREMSHSYMLINK)
+
+# Messaging - copy $POFILES to $POFILE to work with the parent directory
+# Makefile's '_msg' target.
+#
+_msg: $(MSGSUBDIRS) $(POFILES)
+ $(RM) $(POFILE)
+ $(CAT) $(POFILES) > $(POFILE)
+
+$(COMMONPOFILES): $(COMMONSRCS)
+ $(COMPILE.cpp) $(COMMONSRCS) > $(@:.po=.c).i
+ $(XGETTEXT) $(XGETFLAGS) $(@:.po=.c).i
+ $(RM) $@
+ sed "/^domain/d" < messages.po > $@
+ $(RM) messages.po $(@:.po=.c).i
+
+$(COMMONOBJS): $(COMMONSRCS)
+ $(COMPILE.c) $(COMMONSRCS)
+
+rlogin: rlogin.o $(COMMONOBJS)
+ $(LINK.c) $@.o $(COMMONOBJS) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+rcp: rcp.o $(COMMONOBJS)
+ $(LINK.c) $@.o $(COMMONOBJS) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+rsh: rsh.o $(COMMONOBJS)
+ $(LINK.c) $@.o $(COMMONOBJS) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+$(ROOTSUNWRCP):
+ $(RM) $@; $(SYMLINK) ../bin/rcp $@
+
+$(ROOTRSHSYMLINK):
+ $(RM) $@; $(SYMLINK) ../bin/rsh $@
+
+$(ROOTREMSHSYMLINK):
+ $(RM) $@; $(SYMLINK) rsh $@
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(MFLAGS) $(TARGET)
+
+FRC:
+
+clean: $(SUBDIRS)
+
+clobber: $(SUBDIRS) clobber_local
+
+clobber_local:
+ echo $(CLOBBERFILES)
+ $(RM) $(ALL) $(CLOBBERFILES)
+
+
+LINTLOCALS= $(LINTCLEAN:%.c=lint-%)
+
+lint: $(LINTSUBDIRS) $(LINTLOCALS)
+
+$(LINTLOCALS):
+ $(LINT.c) $(@:lint-%=%.c) $(LINTXTRA) $(LDLIBS)
diff --git a/usr/src/cmd/cmd-inet/usr.bin/chat/Makefile b/usr/src/cmd/cmd-inet/usr.bin/chat/Makefile
new file mode 100644
index 0000000000..2dacb88743
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/chat/Makefile
@@ -0,0 +1,27 @@
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 2000 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+
+PROG= chat
+OBJS= chat.o
+SRCS= $(OBJS:%.o=%.c)
+
+include ../../../Makefile.cmd
+
+CPPFLAGS += -DSOL2
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+install: all $(ROOTPROG)
+
+clean:
+ $(RM) $(OBJS)
+
+lint:
+
+include ../../../Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/usr.bin/chat/chat.c b/usr/src/cmd/cmd-inet/usr.bin/chat/chat.c
new file mode 100644
index 0000000000..34730643b3
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/chat/chat.c
@@ -0,0 +1,1762 @@
+/*
+ * Copyright 2000 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * Chat -- a program for automatic session establishment (i.e. dial
+ * the phone and log in).
+ *
+ * Standard termination codes:
+ * 0 - successful completion of the script
+ * 1 - invalid argument, expect string too large, etc.
+ * 2 - error on an I/O operation or fatal error condition.
+ * 3 - timeout waiting for a simple string.
+ * 4 - the first string declared as "ABORT"
+ * 5 - the second string declared as "ABORT"
+ * 6 - ... and so on for successive ABORT strings.
+ *
+ * This software is in the public domain.
+ *
+ * -----------------
+ * 22-May-99 added environment substitutuion, enabled with -E switch.
+ * Andreas Arens <andras@cityweb.de>.
+ *
+ * 12-May-99 added a feature to read data to be sent from a file,
+ * if the send string starts with @. Idea from gpk <gpk@onramp.net>.
+ *
+ * added -T and -U option and \T and \U substitution to pass a phone
+ * number into chat script. Two are needed for some ISDN TA applications.
+ * Keith Dart <kdart@cisco.com>
+ *
+ *
+ * Added SAY keyword to send output to stderr.
+ * This allows to turn ECHO OFF and to output specific, user selected,
+ * text to give progress messages. This best works when stderr
+ * exists (i.e.: pppd in nodetach mode).
+ *
+ * Added HANGUP directives to allow for us to be called
+ * back. When HANGUP is set to NO, chat will not hangup at HUP signal.
+ * We rely on timeouts in that case.
+ *
+ * Added CLR_ABORT to clear previously set ABORT string. This has been
+ * dictated by the HANGUP above as "NO CARRIER" (for example) must be
+ * an ABORT condition until we know the other host is going to close
+ * the connection for call back. As soon as we have completed the
+ * first stage of the call back sequence, "NO CARRIER" is a valid, non
+ * fatal string. As soon as we got called back (probably get "CONNECT"),
+ * we should re-arm the ABORT "NO CARRIER". Hence the CLR_ABORT command.
+ * Note that CLR_ABORT packs the abort_strings[] array so that we do not
+ * have unused entries not being reclaimed.
+ *
+ * In the same vein as above, added CLR_REPORT keyword.
+ *
+ * Allow for comments. Line starting with '#' are comments and are
+ * ignored. If a '#' is to be expected as the first character, the
+ * expect string must be quoted.
+ *
+ *
+ * Francis Demierre <Francis@SwissMail.Com>
+ * Thu May 15 17:15:40 MET DST 1997
+ *
+ *
+ * Added -r "report file" switch & REPORT keyword.
+ * Robert Geer <bgeer@xmission.com>
+ *
+ * Added -s "use stderr" and -S "don't use syslog" switches.
+ * June 18, 1997
+ * Karl O. Pinc <kop@meme.com>
+ *
+ *
+ * Added -e "echo" switch & ECHO keyword
+ * Dick Streefland <dicks@tasking.nl>
+ *
+ *
+ * Considerable updates and modifications by
+ * Al Longyear <longyear@pobox.com>
+ * Paul Mackerras <paulus@cs.anu.edu.au>
+ *
+ *
+ * The original author is:
+ *
+ * Karl Fox <karl@MorningStar.Com>
+ * Morning Star Technologies, Inc.
+ * 1760 Zollinger Road
+ * Columbus, OH 43221
+ * (614)451-1883
+ *
+ */
+
+#ifndef __STDC__
+#define const
+#endif
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifndef lint
+static const char rcsid[] = "$Id: chat.c,v 1.26 1999/12/23 01:39:54 paulus Exp $";
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+#include <time.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <syslog.h>
+
+#ifndef TERMIO
+#undef TERMIOS
+#define TERMIOS
+#endif
+
+#ifdef TERMIO
+#include <termio.h>
+#endif
+#ifdef TERMIOS
+#include <termios.h>
+#endif
+
+#define STR_LEN 1024
+
+#ifndef SIGTYPE
+#define SIGTYPE void
+#endif
+
+#undef __P
+#undef __V
+
+#ifdef __STDC__
+#include <stdarg.h>
+#define __V(x) x
+#define __P(x) x
+#else
+#include <varargs.h>
+#define __V(x) (va_alist) va_dcl
+#define __P(x) ()
+#define const
+#endif
+
+#ifndef O_NONBLOCK
+#define O_NONBLOCK O_NDELAY
+#endif
+
+#ifdef SUNOS
+extern int sys_nerr;
+extern char *sys_errlist[];
+#define memmove(to, from, n) bcopy(from, to, n)
+#define strerror(n) ((unsigned)(n) < sys_nerr? sys_errlist[(n)] :\
+ "unknown error")
+#endif
+
+/*************** Micro getopt() *********************************************/
+#define OPTION(c,v) (_O&2&&**v?*(*v)++:!c||_O&4?0:(!(_O&1)&& \
+ (--c,++v),_O=4,c&&**v=='-'&&v[0][1]?*++*v=='-'\
+ &&!v[0][1]?(--c,++v,0):(_O=2,*(*v)++):0))
+#define OPTARG(c,v) (_O&2?**v||(++v,--c)?(_O=1,--c,*v++): \
+ (_O=4,(char*)0):(char*)0)
+#define OPTONLYARG(c,v) (_O&2&&**v?(_O=1,--c,*v++):(char*)0)
+#define ARG(c,v) (c?(--c,*v++):(char*)0)
+
+static int _O = 0; /* Internal state */
+/*************** Micro getopt() *********************************************/
+
+char *program_name;
+
+#define MAX_ABORTS 50
+#define MAX_REPORTS 50
+#define DEFAULT_CHAT_TIMEOUT 45
+
+int echo = 0;
+int verbose = 0;
+int to_log = 1;
+int to_stderr = 0;
+int Verbose = 0;
+int quiet = 0;
+int report = 0;
+int use_env = 0;
+int exit_code = 0;
+FILE* report_fp = (FILE *) 0;
+char *report_file = (char *) 0;
+char *chat_file = (char *) 0;
+char *phone_num = (char *) 0;
+char *phone_num2 = (char *) 0;
+int timeout = DEFAULT_CHAT_TIMEOUT;
+
+int have_tty_parameters = 0;
+
+#ifdef TERMIO
+#define term_parms struct termio
+#define get_term_param(param) ioctl(0, TCGETA, param)
+#define set_term_param(param) ioctl(0, TCSETA, param)
+struct termio saved_tty_parameters;
+#endif
+
+#ifdef TERMIOS
+#define term_parms struct termios
+#define get_term_param(param) tcgetattr(0, param)
+#define set_term_param(param) tcsetattr(0, TCSANOW, param)
+struct termios saved_tty_parameters;
+#endif
+
+char *abort_string[MAX_ABORTS], *fail_reason = (char *)0,
+ fail_buffer[50];
+int n_aborts = 0, abort_next = 0, timeout_next = 0, echo_next = 0;
+int clear_abort_next = 0;
+
+char *report_string[MAX_REPORTS] ;
+char report_buffer[50] ;
+int n_reports = 0, report_next = 0, report_gathering = 0 ;
+int clear_report_next = 0;
+
+int say_next = 0, hup_next = 0;
+
+void *dup_mem __P((void *b, size_t c));
+void *copy_of __P((char *s));
+void usage __P((void));
+void logf __P((const char *fmt, ...));
+void fatal __P((int code, const char *fmt, ...));
+SIGTYPE sigalrm __P((int signo));
+SIGTYPE sigint __P((int signo));
+SIGTYPE sigterm __P((int signo));
+SIGTYPE sighup __P((int signo));
+void unalarm __P((void));
+void init __P((void));
+void set_tty_parameters __P((void));
+void echo_stderr __P((int));
+void break_sequence __P((void));
+void terminate __P((int status));
+void do_file __P((char *chat_file));
+int get_string __P((register char *string));
+int put_string __P((register char *s));
+int write_char __P((int c));
+int put_char __P((int c));
+int get_char __P((void));
+void chat_send __P((register char *s));
+char *character __P((int c));
+void chat_expect __P((register char *s));
+char *clean __P((register char *s, int sending));
+void break_sequence __P((void));
+void terminate __P((int status));
+void pack_array __P((char **array, int end));
+char *expect_strtok __P((char *, char *));
+int vfmtmsg __P((char *, int, const char *, va_list)); /* vsprintf++ */
+
+int main __P((int, char *[]));
+
+void *dup_mem(b, c)
+void *b;
+size_t c;
+{
+ void *ans = malloc (c);
+ if (!ans)
+ fatal(2, "memory error!");
+
+ memcpy (ans, b, c);
+ return ans;
+}
+
+void *copy_of (s)
+char *s;
+{
+ return dup_mem (s, strlen (s) + 1);
+}
+
+/*
+ * chat [ -v ] [ -E ] [ -T number ] [ -U number ] [ -t timeout ] [ -f chat-file ] \
+ * [ -r report-file ] \
+ * [...[[expect[-say[-expect...]] say expect[-say[-expect]] ...]]]
+ *
+ * Perform a UUCP-dialer-like chat script on stdin and stdout.
+ */
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int option;
+ char *arg;
+
+ program_name = *argv;
+ tzset();
+
+ while ((option = OPTION(argc, argv)) != 0) {
+ switch (option) {
+ case 'e':
+ ++echo;
+ break;
+
+ case 'E':
+ ++use_env;
+ break;
+
+ case 'v':
+ ++verbose;
+ break;
+
+ case 'V':
+ ++Verbose;
+ break;
+
+ case 's':
+ ++to_stderr;
+ break;
+
+ case 'S':
+ to_log = 0;
+ break;
+
+ case 'f':
+ if ((arg = OPTARG(argc, argv)) != NULL)
+ chat_file = copy_of(arg);
+ else
+ usage();
+ break;
+
+ case 't':
+ if ((arg = OPTARG(argc, argv)) != NULL)
+ timeout = atoi(arg);
+ else
+ usage();
+ break;
+
+ case 'r':
+ arg = OPTARG (argc, argv);
+ if (arg) {
+ if (report_fp != NULL)
+ fclose (report_fp);
+ report_file = copy_of (arg);
+ report_fp = fopen (report_file, "a");
+ if (report_fp != NULL) {
+ if (verbose)
+ fprintf (report_fp, "Opening \"%s\"...\n",
+ report_file);
+ report = 1;
+ }
+ }
+ break;
+
+ case 'T':
+ if ((arg = OPTARG(argc, argv)) != NULL)
+ phone_num = copy_of(arg);
+ else
+ usage();
+ break;
+
+ case 'U':
+ if ((arg = OPTARG(argc, argv)) != NULL)
+ phone_num2 = copy_of(arg);
+ else
+ usage();
+ break;
+
+ default:
+ usage();
+ break;
+ }
+ }
+/*
+ * Default the report file to the stderr location
+ */
+ if (report_fp == NULL)
+ report_fp = stderr;
+
+ if (to_log) {
+#ifdef ultrix
+ openlog("chat", LOG_PID);
+#else
+ openlog("chat", LOG_PID | LOG_NDELAY, LOG_LOCAL2);
+
+ if (verbose)
+ setlogmask(LOG_UPTO(LOG_INFO));
+ else
+ setlogmask(LOG_UPTO(LOG_WARNING));
+#endif
+ }
+
+ init();
+
+ if (chat_file != NULL) {
+ arg = ARG(argc, argv);
+ if (arg != NULL)
+ usage();
+ else
+ do_file (chat_file);
+ } else {
+ while ((arg = ARG(argc, argv)) != NULL) {
+ chat_expect(arg);
+
+ if ((arg = ARG(argc, argv)) != NULL)
+ chat_send(arg);
+ }
+ }
+
+ terminate(0);
+ return 0;
+}
+
+/*
+ * Process a chat script when read from a file.
+ */
+
+void do_file (chat_file)
+char *chat_file;
+{
+ int linect, sendflg;
+ char *sp, *arg, quote;
+ char buf [STR_LEN];
+ FILE *cfp;
+
+ cfp = fopen (chat_file, "r");
+ if (cfp == NULL)
+ fatal(1, "%s -- open failed: %m", chat_file);
+
+ linect = 0;
+ sendflg = 0;
+
+ while (fgets(buf, STR_LEN, cfp) != NULL) {
+ sp = strchr (buf, '\n');
+ if (sp)
+ *sp = '\0';
+
+ linect++;
+ sp = buf;
+
+ /* lines starting with '#' are comments. If a real '#'
+ is to be expected, it should be quoted .... */
+ if ( *sp == '#' )
+ continue;
+
+ while (*sp != '\0') {
+ if (*sp == ' ' || *sp == '\t') {
+ ++sp;
+ continue;
+ }
+
+ if (*sp == '"' || *sp == '\'') {
+ quote = *sp++;
+ arg = sp;
+ while (*sp != quote) {
+ if (*sp == '\0')
+ fatal(1, "unterminated quote (line %d)", linect);
+
+ if (*sp++ == '\\') {
+ if (*sp != '\0')
+ ++sp;
+ }
+ }
+ }
+ else {
+ arg = sp;
+ while (*sp != '\0' && *sp != ' ' && *sp != '\t')
+ ++sp;
+ }
+
+ if (*sp != '\0')
+ *sp++ = '\0';
+
+ if (sendflg)
+ chat_send (arg);
+ else
+ chat_expect (arg);
+ sendflg = !sendflg;
+ }
+ }
+ fclose (cfp);
+}
+
+/*
+ * We got an error parsing the command line.
+ */
+void usage()
+{
+ fprintf(stderr, "\
+Usage: %s [-e] [-E] [-v] [-V] [-t timeout] [-r report-file]\n\
+ [-T phone-number] [-U phone-number2] {-f chat-file | chat-script}\n", program_name);
+ exit(1);
+}
+
+char line[1024];
+
+/*
+ * Send a message to syslog and/or stderr.
+ */
+void logf __V((const char *fmt, ...))
+{
+ va_list args;
+
+#ifdef __STDC__
+ va_start(args, fmt);
+#else
+ char *fmt;
+ va_start(args);
+ fmt = va_arg(args, char *);
+#endif
+
+ vfmtmsg(line, sizeof(line), fmt, args);
+ if (to_log)
+ syslog(LOG_INFO, "%s", line);
+ if (to_stderr)
+ fprintf(stderr, "%s\n", line);
+}
+
+/*
+ * Print an error message and terminate.
+ */
+
+void fatal __V((int code, const char *fmt, ...))
+{
+ va_list args;
+
+#ifdef __STDC__
+ va_start(args, fmt);
+#else
+ int code;
+ char *fmt;
+ va_start(args);
+ code = va_arg(args, int);
+ fmt = va_arg(args, char *);
+#endif
+
+ vfmtmsg(line, sizeof(line), fmt, args);
+ if (to_log)
+ syslog(LOG_ERR, "%s", line);
+ if (to_stderr)
+ fprintf(stderr, "%s\n", line);
+ terminate(code);
+}
+
+int alarmed = 0;
+
+SIGTYPE sigalrm(signo)
+int signo;
+{
+ int flags;
+
+ alarm(1);
+ alarmed = 1; /* Reset alarm to avoid race window */
+ signal(SIGALRM, sigalrm); /* that can cause hanging in read() */
+
+ if ((flags = fcntl(0, F_GETFL, 0)) == -1)
+ fatal(2, "Can't get file mode flags on stdin: %m");
+
+ if (fcntl(0, F_SETFL, flags | O_NONBLOCK) == -1)
+ fatal(2, "Can't set file mode flags on stdin: %m");
+
+ if (verbose)
+ logf("alarm");
+}
+
+void unalarm()
+{
+ int flags;
+
+ if ((flags = fcntl(0, F_GETFL, 0)) == -1)
+ fatal(2, "Can't get file mode flags on stdin: %m");
+
+ if (fcntl(0, F_SETFL, flags & ~O_NONBLOCK) == -1)
+ fatal(2, "Can't set file mode flags on stdin: %m");
+}
+
+SIGTYPE sigint(signo)
+int signo;
+{
+ fatal(2, "SIGINT");
+}
+
+SIGTYPE sigterm(signo)
+int signo;
+{
+ fatal(2, "SIGTERM");
+}
+
+SIGTYPE sighup(signo)
+int signo;
+{
+ fatal(2, "SIGHUP");
+}
+
+void init()
+{
+ signal(SIGINT, sigint);
+ signal(SIGTERM, sigterm);
+ signal(SIGHUP, sighup);
+
+ set_tty_parameters();
+ signal(SIGALRM, sigalrm);
+ alarm(0);
+ alarmed = 0;
+}
+
+void set_tty_parameters()
+{
+#if defined(get_term_param)
+ term_parms t;
+
+ if (get_term_param (&t) < 0)
+ fatal(2, "Can't get terminal parameters: %m");
+
+ saved_tty_parameters = t;
+ have_tty_parameters = 1;
+
+ t.c_iflag |= IGNBRK | ISTRIP | IGNPAR;
+ t.c_oflag = 0;
+ t.c_lflag = 0;
+ t.c_cc[VERASE] =
+ t.c_cc[VKILL] = 0;
+ t.c_cc[VMIN] = 1;
+ t.c_cc[VTIME] = 0;
+
+ if (set_term_param (&t) < 0)
+ fatal(2, "Can't set terminal parameters: %m");
+#endif
+}
+
+void break_sequence()
+{
+#ifdef TERMIOS
+ tcsendbreak (0, 0);
+#endif
+}
+
+void terminate(status)
+int status;
+{
+ static int terminating = 0;
+
+ if (terminating)
+ exit(status);
+ terminating = 1;
+ echo_stderr(-1);
+/*
+ * Allow the last of the report string to be gathered before we terminate.
+ */
+ if (report_gathering) {
+ int c, rep_len;
+
+ rep_len = strlen(report_buffer);
+ while (rep_len + 1 <= sizeof(report_buffer)) {
+ alarm(1);
+ c = get_char();
+ alarm(0);
+ if (c < 0 || iscntrl(c))
+ break;
+ report_buffer[rep_len] = c;
+ ++rep_len;
+ }
+ report_buffer[rep_len] = 0;
+ fprintf (report_fp, "chat: %s\n", report_buffer);
+ }
+ if (report_file != (char *) 0 && report_fp != (FILE *) NULL) {
+ if (verbose)
+ fprintf (report_fp, "Closing \"%s\".\n", report_file);
+ fclose (report_fp);
+ report_fp = (FILE *) NULL;
+ }
+
+#if defined(get_term_param)
+ if (have_tty_parameters) {
+ if (set_term_param (&saved_tty_parameters) < 0)
+ fatal(2, "Can't restore terminal parameters: %m");
+ }
+#endif
+
+ exit(status);
+}
+
+/*
+ * 'Clean up' this string.
+ */
+char *clean(s, sending)
+register char *s;
+int sending; /* set to 1 when sending (putting) this string. */
+{
+ char temp[STR_LEN], env_str[STR_LEN], cur_chr;
+ register char *s1, *phchar;
+ int add_return = sending;
+#define isoctal(chr) (((chr) >= '0') && ((chr) <= '7'))
+#define isalnumx(chr) ((((chr) >= '0') && ((chr) <= '9')) \
+ || (((chr) >= 'a') && ((chr) <= 'z')) \
+ || (((chr) >= 'A') && ((chr) <= 'Z')) \
+ || (chr) == '_')
+
+ s1 = temp;
+ while (*s) {
+ cur_chr = *s++;
+ if (cur_chr == '^') {
+ cur_chr = *s++;
+ if (cur_chr == '\0') {
+ *s1++ = '^';
+ break;
+ }
+ cur_chr &= 0x1F;
+ if (cur_chr != 0) {
+ *s1++ = cur_chr;
+ }
+ continue;
+ }
+
+ if (use_env && cur_chr == '$') { /* ARI */
+ phchar = env_str;
+ while (isalnumx(*s))
+ *phchar++ = *s++;
+ *phchar = '\0';
+ phchar = getenv(env_str);
+ if (phchar)
+ while (*phchar)
+ *s1++ = *phchar++;
+ continue;
+ }
+
+ if (cur_chr != '\\') {
+ *s1++ = cur_chr;
+ continue;
+ }
+
+ cur_chr = *s++;
+ if (cur_chr == '\0') {
+ if (sending) {
+ *s1++ = '\\';
+ *s1++ = '\\';
+ }
+ break;
+ }
+
+ switch (cur_chr) {
+ case 'b':
+ *s1++ = '\b';
+ break;
+
+ case 'c':
+ if (sending && *s == '\0')
+ add_return = 0;
+ else
+ *s1++ = cur_chr;
+ break;
+
+ case '\\':
+ case 'K':
+ case 'p':
+ case 'd':
+ if (sending)
+ *s1++ = '\\';
+ *s1++ = cur_chr;
+ break;
+
+ case 'T':
+ if (sending && phone_num) {
+ for (phchar = phone_num; *phchar != '\0'; phchar++)
+ *s1++ = *phchar;
+ }
+ else {
+ *s1++ = '\\';
+ *s1++ = 'T';
+ }
+ break;
+
+ case 'U':
+ if (sending && phone_num2) {
+ for (phchar = phone_num2; *phchar != '\0'; phchar++)
+ *s1++ = *phchar;
+ }
+ else {
+ *s1++ = '\\';
+ *s1++ = 'U';
+ }
+ break;
+
+ case 'q':
+ quiet = 1;
+ break;
+
+ case 'r':
+ *s1++ = '\r';
+ break;
+
+ case 'n':
+ *s1++ = '\n';
+ break;
+
+ case 's':
+ *s1++ = ' ';
+ break;
+
+ case 't':
+ *s1++ = '\t';
+ break;
+
+ case 'N':
+ if (sending) {
+ *s1++ = '\\';
+ *s1++ = '\0';
+ }
+ else
+ *s1++ = 'N';
+ break;
+
+ case '$': /* ARI */
+ if (use_env) {
+ *s1++ = cur_chr;
+ break;
+ }
+ /* FALL THROUGH */
+
+ default:
+ if (isoctal (cur_chr)) {
+ cur_chr &= 0x07;
+ if (isoctal (*s)) {
+ cur_chr <<= 3;
+ cur_chr |= *s++ - '0';
+ if (isoctal (*s)) {
+ cur_chr <<= 3;
+ cur_chr |= *s++ - '0';
+ }
+ }
+
+ if (cur_chr != 0 || sending) {
+ if (sending && (cur_chr == '\\' || cur_chr == 0))
+ *s1++ = '\\';
+ *s1++ = cur_chr;
+ }
+ break;
+ }
+
+ if (sending)
+ *s1++ = '\\';
+ *s1++ = cur_chr;
+ break;
+ }
+ }
+
+ if (add_return)
+ *s1++ = '\r';
+
+ *s1++ = '\0'; /* guarantee closure */
+ *s1++ = '\0'; /* terminate the string */
+ return dup_mem (temp, (size_t) (s1 - temp)); /* may have embedded nuls */
+}
+
+/*
+ * A modified version of 'strtok'. This version skips \ sequences.
+ */
+
+char *expect_strtok (s, term)
+ char *s, *term;
+{
+ static char *str = "";
+ int escape_flag = 0;
+ char *result;
+
+/*
+ * If a string was specified then do initial processing.
+ */
+ if (s)
+ str = s;
+
+/*
+ * If this is the escape flag then reset it and ignore the character.
+ */
+ if (*str)
+ result = str;
+ else
+ result = (char *) 0;
+
+ while (*str) {
+ if (escape_flag) {
+ escape_flag = 0;
+ ++str;
+ continue;
+ }
+
+ if (*str == '\\') {
+ ++str;
+ escape_flag = 1;
+ continue;
+ }
+
+/*
+ * If this is not in the termination string, continue.
+ */
+ if (strchr (term, *str) == (char *) 0) {
+ ++str;
+ continue;
+ }
+
+/*
+ * This is the terminator. Mark the end of the string and stop.
+ */
+ *str++ = '\0';
+ break;
+ }
+ return (result);
+}
+
+/*
+ * Process the expect string
+ */
+
+void chat_expect (s)
+char *s;
+{
+ char *expect;
+ char *reply;
+
+ if (strcmp(s, "HANGUP") == 0) {
+ ++hup_next;
+ return;
+ }
+
+ if (strcmp(s, "ABORT") == 0) {
+ ++abort_next;
+ return;
+ }
+
+ if (strcmp(s, "CLR_ABORT") == 0) {
+ ++clear_abort_next;
+ return;
+ }
+
+ if (strcmp(s, "REPORT") == 0) {
+ ++report_next;
+ return;
+ }
+
+ if (strcmp(s, "CLR_REPORT") == 0) {
+ ++clear_report_next;
+ return;
+ }
+
+ if (strcmp(s, "TIMEOUT") == 0) {
+ ++timeout_next;
+ return;
+ }
+
+ if (strcmp(s, "ECHO") == 0) {
+ ++echo_next;
+ return;
+ }
+
+ if (strcmp(s, "SAY") == 0) {
+ ++say_next;
+ return;
+ }
+
+/*
+ * Fetch the expect and reply string.
+ */
+ for (;;) {
+ expect = expect_strtok (s, "-");
+ s = (char *) 0;
+
+ if (expect == (char *) 0)
+ return;
+
+ reply = expect_strtok (s, "-");
+
+/*
+ * Handle the expect string. If successful then exit.
+ */
+ if (get_string (expect))
+ return;
+
+/*
+ * If there is a sub-reply string then send it. Otherwise any condition
+ * is terminal.
+ */
+ if (reply == (char *) 0 || exit_code != 3)
+ break;
+
+ chat_send (reply);
+ }
+
+/*
+ * The expectation did not occur. This is terminal.
+ */
+ if (fail_reason)
+ logf("Failed (%s)", fail_reason);
+ else
+ logf("Failed");
+ terminate(exit_code);
+}
+
+/*
+ * Translate the input character to the appropriate string for printing
+ * the data.
+ */
+
+char *character(c)
+int c;
+{
+ static char string[10];
+ char *meta;
+
+ meta = (c & 0x80) ? "M-" : "";
+ c &= 0x7F;
+
+ if (c < 32)
+ sprintf(string, "%s^%c", meta, (int)c + '@');
+ else if (c == 127)
+ sprintf(string, "%s^?", meta);
+ else
+ sprintf(string, "%s%c", meta, c);
+
+ return (string);
+}
+
+/*
+ * process the reply string
+ */
+void chat_send (s)
+register char *s;
+{
+ char file_data[STR_LEN];
+
+ if (say_next) {
+ say_next = 0;
+ s = clean(s, 1);
+ write(2, s, strlen(s));
+ free(s);
+ return;
+ }
+
+ if (hup_next) {
+ hup_next = 0;
+ if (strcmp(s, "OFF") == 0)
+ signal(SIGHUP, SIG_IGN);
+ else
+ signal(SIGHUP, sighup);
+ return;
+ }
+
+ if (echo_next) {
+ echo_next = 0;
+ echo = (strcmp(s, "ON") == 0);
+ return;
+ }
+
+ if (abort_next) {
+ char *s1;
+
+ abort_next = 0;
+
+ if (n_aborts >= MAX_ABORTS)
+ fatal(2, "Too many ABORT strings");
+
+ s1 = clean(s, 0);
+
+ if (strlen(s1) > strlen(s)
+ || strlen(s1) + 1 > sizeof(fail_buffer))
+ fatal(1, "Illegal or too-long ABORT string ('%v')", s);
+
+ abort_string[n_aborts++] = s1;
+
+ if (verbose)
+ logf("abort on (%v)", s);
+ return;
+ }
+
+ if (clear_abort_next) {
+ char *s1;
+ int i;
+ int old_max;
+ int pack = 0;
+
+ clear_abort_next = 0;
+
+ s1 = clean(s, 0);
+
+ if (strlen(s1) > strlen(s)
+ || strlen(s1) + 1 > sizeof(fail_buffer))
+ fatal(1, "Illegal or too-long CLR_ABORT string ('%v')", s);
+
+ old_max = n_aborts;
+ for (i=0; i < n_aborts; i++) {
+ if ( strcmp(s1,abort_string[i]) == 0 ) {
+ free(abort_string[i]);
+ abort_string[i] = NULL;
+ pack++;
+ n_aborts--;
+ if (verbose)
+ logf("clear abort on (%v)", s);
+ }
+ }
+ free(s1);
+ if (pack)
+ pack_array(abort_string,old_max);
+ return;
+ }
+
+ if (report_next) {
+ char *s1;
+
+ report_next = 0;
+ if (n_reports >= MAX_REPORTS)
+ fatal(2, "Too many REPORT strings");
+
+ s1 = clean(s, 0);
+
+ if (strlen(s1) > strlen(s) || strlen(s1) > sizeof fail_buffer - 1)
+ fatal(1, "Illegal or too-long REPORT string ('%v')", s);
+
+ report_string[n_reports++] = s1;
+
+ if (verbose)
+ logf("report (%v)", s);
+ return;
+ }
+
+ if (clear_report_next) {
+ char *s1;
+ int i;
+ int old_max;
+ int pack = 0;
+
+ clear_report_next = 0;
+
+ s1 = clean(s, 0);
+
+ if (strlen(s1) > strlen(s) || strlen(s1) > sizeof fail_buffer - 1)
+ fatal(1, "Illegal or too-long REPORT string ('%v')", s);
+
+ old_max = n_reports;
+ for (i=0; i < n_reports; i++) {
+ if ( strcmp(s1,report_string[i]) == 0 ) {
+ free(report_string[i]);
+ report_string[i] = NULL;
+ pack++;
+ n_reports--;
+ if (verbose)
+ logf("clear report (%v)", s);
+ }
+ }
+ free(s1);
+ if (pack)
+ pack_array(report_string,old_max);
+
+ return;
+ }
+
+ if (timeout_next) {
+ timeout_next = 0;
+ timeout = atoi(s);
+
+ if (timeout <= 0)
+ timeout = DEFAULT_CHAT_TIMEOUT;
+
+ if (verbose)
+ logf("timeout set to %d seconds", timeout);
+
+ return;
+ }
+
+ /*
+ * The syntax @filename means read the string to send from the
+ * file `filename'.
+ */
+ if (s[0] == '@') {
+ /* skip the @ and any following white-space */
+ char *fn = s;
+ while (*++fn == ' ' || *fn == '\t')
+ ;
+
+ if (*fn != 0) {
+ FILE *f;
+ int n = 0;
+
+ /* open the file and read until STR_LEN-1 bytes or end-of-file */
+ f = fopen(fn, "r");
+ if (f == NULL)
+ fatal(1, "%s -- open failed: %m", fn);
+ while (n < STR_LEN - 1) {
+ int nr = fread(&file_data[n], 1, STR_LEN - 1 - n, f);
+ if (nr < 0)
+ fatal(1, "%s -- read error", fn);
+ if (nr == 0)
+ break;
+ n += nr;
+ }
+ fclose(f);
+
+ /* use the string we got as the string to send,
+ but trim off the final newline if any. */
+ if (n > 0 && file_data[n-1] == '\n')
+ --n;
+ file_data[n] = 0;
+ s = file_data;
+ }
+ }
+
+ if (strcmp(s, "EOT") == 0)
+ s = "^D\\c";
+ else if (strcmp(s, "BREAK") == 0)
+ s = "\\K\\c";
+
+ if (!put_string(s))
+ fatal(1, "Failed");
+}
+
+int get_char()
+{
+ int status;
+ char c;
+
+ status = read(0, &c, 1);
+
+ switch (status) {
+ case 1:
+ return ((int)c & 0x7F);
+
+ default:
+ logf("warning: read() on stdin returned %d", status);
+
+ case -1:
+ if ((status = fcntl(0, F_GETFL, 0)) == -1)
+ fatal(2, "Can't get file mode flags on stdin: %m");
+
+ if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1)
+ fatal(2, "Can't set file mode flags on stdin: %m");
+
+ return (-1);
+ }
+}
+
+int put_char(c)
+int c;
+{
+ int status;
+ char ch = c;
+
+ usleep(10000); /* inter-character typing delay (?) */
+
+ status = write(1, &ch, 1);
+
+ switch (status) {
+ case 1:
+ return (0);
+
+ default:
+ logf("warning: write() on stdout returned %d", status);
+
+ case -1:
+ if ((status = fcntl(0, F_GETFL, 0)) == -1)
+ fatal(2, "Can't get file mode flags on stdin, %m");
+
+ if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1)
+ fatal(2, "Can't set file mode flags on stdin: %m");
+
+ return (-1);
+ }
+}
+
+int write_char (c)
+int c;
+{
+ if (alarmed || put_char(c) < 0) {
+ alarm(0);
+ alarmed = 0;
+
+ if (verbose) {
+ if (errno == EINTR || errno == EWOULDBLOCK)
+ logf(" -- write timed out");
+ else
+ logf(" -- write failed: %m");
+ }
+ return (0);
+ }
+ return (1);
+}
+
+int put_string (s)
+register char *s;
+{
+ quiet = 0;
+ s = clean(s, 1);
+
+ if (verbose) {
+ if (quiet)
+ logf("send (??????)");
+ else
+ logf("send (%v)", s);
+ }
+
+ alarm(timeout); alarmed = 0;
+
+ while (*s) {
+ register char c = *s++;
+
+ if (c != '\\') {
+ if (!write_char (c))
+ return 0;
+ continue;
+ }
+
+ c = *s++;
+ switch (c) {
+ case 'd':
+ sleep(1);
+ break;
+
+ case 'K':
+ break_sequence();
+ break;
+
+ case 'p':
+ usleep(10000); /* 1/100th of a second (arg is microseconds) */
+ break;
+
+ default:
+ if (!write_char (c))
+ return 0;
+ break;
+ }
+ }
+
+ alarm(0);
+ alarmed = 0;
+ return (1);
+}
+
+/*
+ * Echo a character to stderr.
+ * When called with -1, a '\n' character is generated when
+ * the cursor is not at the beginning of a line.
+ */
+void echo_stderr(n)
+int n;
+{
+ static int need_lf;
+ char *s;
+
+ switch (n) {
+ case '\r': /* ignore '\r' */
+ break;
+ case -1:
+ if (need_lf == 0)
+ break;
+ /* fall through */
+ case '\n':
+ write(2, "\n", 1);
+ need_lf = 0;
+ break;
+ default:
+ s = character(n);
+ write(2, s, strlen(s));
+ need_lf = 1;
+ break;
+ }
+}
+
+/*
+ * 'Wait for' this string to appear on this file descriptor.
+ */
+int get_string(string)
+register char *string;
+{
+ char temp[STR_LEN];
+ int c, printed = 0, len, minlen;
+ register char *s = temp, *end = s + STR_LEN;
+ char *logged = temp;
+
+ fail_reason = (char *)0;
+ string = clean(string, 0);
+ len = strlen(string);
+ minlen = (len > sizeof(fail_buffer)? len: sizeof(fail_buffer)) - 1;
+
+ if (verbose)
+ logf("expect (%v)", string);
+
+ if (len > STR_LEN) {
+ logf("expect string is too long");
+ exit_code = 1;
+ return 0;
+ }
+
+ if (len == 0) {
+ if (verbose)
+ logf("got it");
+ return (1);
+ }
+
+ alarm(timeout);
+ alarmed = 0;
+
+ while ( ! alarmed && (c = get_char()) >= 0) {
+ int n, abort_len, report_len;
+
+ if (echo)
+ echo_stderr(c);
+ if (verbose && c == '\n') {
+ if (s == logged)
+ logf(""); /* blank line */
+ else
+ logf("%0.*v", s - logged, logged);
+ logged = s + 1;
+ }
+
+ *s++ = c;
+
+ if (verbose && s >= logged + 80) {
+ logf("%0.*v", s - logged, logged);
+ logged = s;
+ }
+
+ if (Verbose) {
+ if (c == '\n')
+ fputc( '\n', stderr );
+ else if (c != '\r')
+ fprintf( stderr, "%s", character(c) );
+ }
+
+ if (!report_gathering) {
+ for (n = 0; n < n_reports; ++n) {
+ if ((report_string[n] != (char*) NULL) &&
+ s - temp >= (report_len = strlen(report_string[n])) &&
+ strncmp(s - report_len, report_string[n], report_len) == 0) {
+ time_t time_now = time ((time_t*) NULL);
+ struct tm* tm_now = localtime (&time_now);
+
+ strftime (report_buffer, 20, "%b %d %H:%M:%S ", tm_now);
+ strcat (report_buffer, report_string[n]);
+
+ report_string[n] = (char *) NULL;
+ report_gathering = 1;
+ break;
+ }
+ }
+ }
+ else {
+ if (!iscntrl (c)) {
+ int rep_len = strlen (report_buffer);
+ report_buffer[rep_len] = c;
+ report_buffer[rep_len + 1] = '\0';
+ }
+ else {
+ report_gathering = 0;
+ fprintf (report_fp, "chat: %s\n", report_buffer);
+ }
+ }
+
+ if (s - temp >= len &&
+ c == string[len - 1] &&
+ strncmp(s - len, string, len) == 0) {
+ if (verbose) {
+ if (s > logged)
+ logf("%0.*v", s - logged, logged);
+ logf(" -- got it\n");
+ }
+
+ alarm(0);
+ alarmed = 0;
+ return (1);
+ }
+
+ for (n = 0; n < n_aborts; ++n) {
+ if (s - temp >= (abort_len = strlen(abort_string[n])) &&
+ strncmp(s - abort_len, abort_string[n], abort_len) == 0) {
+ if (verbose) {
+ if (s > logged)
+ logf("%0.*v", s - logged, logged);
+ logf(" -- failed");
+ }
+
+ alarm(0);
+ alarmed = 0;
+ exit_code = n + 4;
+ strcpy(fail_reason = fail_buffer, abort_string[n]);
+ return (0);
+ }
+ }
+
+ if (s >= end) {
+ if (logged < s - minlen) {
+ if (verbose)
+ logf("%0.*v", s - logged, logged);
+ logged = s;
+ }
+ s -= minlen;
+ memmove(temp, s, minlen);
+ logged = temp + (logged - s);
+ s = temp + minlen;
+ }
+
+ if (alarmed && verbose)
+ logf("warning: alarm synchronization problem");
+ }
+
+ alarm(0);
+
+ if (verbose && printed) {
+ if (alarmed)
+ logf(" -- read timed out");
+ else
+ logf(" -- read failed: %m");
+ }
+
+ exit_code = 3;
+ alarmed = 0;
+ return (0);
+}
+
+/*
+ * Gross kludge to handle Solaris versions >= 2.6 having usleep.
+ */
+#ifdef SOL2
+#include <sys/param.h>
+#if MAXUID > 65536 /* then this is Solaris 2.6 or later */
+#undef NO_USLEEP
+#endif
+#endif /* SOL2 */
+
+#ifdef NO_USLEEP
+#include <sys/types.h>
+#include <sys/time.h>
+
+/*
+ usleep -- support routine for 4.2BSD system call emulations
+ last edit: 29-Oct-1984 D A Gwyn
+ */
+
+extern int select();
+
+int
+usleep( usec ) /* returns 0 if ok, else -1 */
+ long usec; /* delay in microseconds */
+{
+ static struct { /* `timeval' */
+ long tv_sec; /* seconds */
+ long tv_usec; /* microsecs */
+ } delay; /* _select() timeout */
+
+ delay.tv_sec = usec / 1000000L;
+ delay.tv_usec = usec % 1000000L;
+
+ return select(0, (long *)0, (long *)0, (long *)0, &delay);
+}
+#endif
+
+void
+pack_array (array, end)
+ char **array; /* The address of the array of string pointers */
+ int end; /* The index of the next free entry before CLR_ */
+{
+ int i, j;
+
+ for (i = 0; i < end; i++) {
+ if (array[i] == NULL) {
+ for (j = i+1; j < end; ++j)
+ if (array[j] != NULL)
+ array[i++] = array[j];
+ for (; i < end; ++i)
+ array[i] = NULL;
+ break;
+ }
+ }
+}
+
+/*
+ * vfmtmsg - format a message into a buffer. Like vsprintf except we
+ * also specify the length of the output buffer, and we handle the
+ * %m (error message) format.
+ * Doesn't do floating-point formats.
+ * Returns the number of chars put into buf.
+ */
+#define OUTCHAR(c) (buflen > 0? (--buflen, *buf++ = (c)): 0)
+
+int
+vfmtmsg(buf, buflen, fmt, args)
+ char *buf;
+ int buflen;
+ const char *fmt;
+ va_list args;
+{
+ int c, i, n;
+ int width, prec, fillch;
+ int base, len, neg, quoted;
+ unsigned long val = 0;
+ char *str, *buf0;
+ const char *f;
+ unsigned char *p;
+ char num[32];
+ static char hexchars[] = "0123456789abcdef";
+
+ buf0 = buf;
+ --buflen;
+ while (buflen > 0) {
+ for (f = fmt; *f != '%' && *f != 0; ++f)
+ ;
+ if (f > fmt) {
+ len = f - fmt;
+ if (len > buflen)
+ len = buflen;
+ memcpy(buf, fmt, len);
+ buf += len;
+ buflen -= len;
+ fmt = f;
+ }
+ if (*fmt == 0)
+ break;
+ c = *++fmt;
+ width = prec = 0;
+ fillch = ' ';
+ if (c == '0') {
+ fillch = '0';
+ c = *++fmt;
+ }
+ if (c == '*') {
+ width = va_arg(args, int);
+ c = *++fmt;
+ } else {
+ while (isdigit(c)) {
+ width = width * 10 + c - '0';
+ c = *++fmt;
+ }
+ }
+ if (c == '.') {
+ c = *++fmt;
+ if (c == '*') {
+ prec = va_arg(args, int);
+ c = *++fmt;
+ } else {
+ while (isdigit(c)) {
+ prec = prec * 10 + c - '0';
+ c = *++fmt;
+ }
+ }
+ }
+ str = 0;
+ base = 0;
+ neg = 0;
+ ++fmt;
+ switch (c) {
+ case 'd':
+ i = va_arg(args, int);
+ if (i < 0) {
+ neg = 1;
+ val = -i;
+ } else
+ val = i;
+ base = 10;
+ break;
+ case 'o':
+ val = va_arg(args, unsigned int);
+ base = 8;
+ break;
+ case 'x':
+ val = va_arg(args, unsigned int);
+ base = 16;
+ break;
+ case 'p':
+ val = (unsigned long) va_arg(args, void *);
+ base = 16;
+ neg = 2;
+ break;
+ case 's':
+ str = va_arg(args, char *);
+ break;
+ case 'c':
+ num[0] = va_arg(args, int);
+ num[1] = 0;
+ str = num;
+ break;
+ case 'm':
+ str = strerror(errno);
+ break;
+ case 'v': /* "visible" string */
+ case 'q': /* quoted string */
+ quoted = c == 'q';
+ p = va_arg(args, unsigned char *);
+ if (fillch == '0' && prec > 0) {
+ n = prec;
+ } else {
+ n = strlen((char *)p);
+ if (prec > 0 && prec < n)
+ n = prec;
+ }
+ while (n > 0 && buflen > 0) {
+ c = *p++;
+ --n;
+ if (!quoted && c >= 0x80) {
+ OUTCHAR('M');
+ OUTCHAR('-');
+ c -= 0x80;
+ }
+ if (quoted && (c == '"' || c == '\\'))
+ OUTCHAR('\\');
+ if (c < 0x20 || (0x7f <= c && c < 0xa0)) {
+ if (quoted) {
+ OUTCHAR('\\');
+ switch (c) {
+ case '\t': OUTCHAR('t'); break;
+ case '\n': OUTCHAR('n'); break;
+ case '\b': OUTCHAR('b'); break;
+ case '\f': OUTCHAR('f'); break;
+ default:
+ OUTCHAR('x');
+ OUTCHAR(hexchars[c >> 4]);
+ OUTCHAR(hexchars[c & 0xf]);
+ }
+ } else {
+ if (c == '\t')
+ OUTCHAR(c);
+ else {
+ OUTCHAR('^');
+ OUTCHAR(c ^ 0x40);
+ }
+ }
+ } else
+ OUTCHAR(c);
+ }
+ continue;
+ default:
+ *buf++ = '%';
+ if (c != '%')
+ --fmt; /* so %z outputs %z etc. */
+ --buflen;
+ continue;
+ }
+ if (base != 0) {
+ str = num + sizeof(num);
+ *--str = 0;
+ while (str > num + neg) {
+ *--str = hexchars[val % base];
+ val = val / base;
+ if (--prec <= 0 && val == 0)
+ break;
+ }
+ switch (neg) {
+ case 1:
+ *--str = '-';
+ break;
+ case 2:
+ *--str = 'x';
+ *--str = '0';
+ break;
+ }
+ len = num + sizeof(num) - 1 - str;
+ } else {
+ len = strlen(str);
+ if (prec > 0 && len > prec)
+ len = prec;
+ }
+ if (width > 0) {
+ if (width > buflen)
+ width = buflen;
+ if ((n = width - len) > 0) {
+ buflen -= n;
+ for (; n > 0; --n)
+ *buf++ = fillch;
+ }
+ }
+ if (len > buflen)
+ len = buflen;
+ memcpy(buf, str, len);
+ buf += len;
+ buflen -= len;
+ }
+ *buf = 0;
+ return buf - buf0;
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/finger.c b/usr/src/cmd/cmd-inet/usr.bin/finger.c
new file mode 100644
index 0000000000..0e8cd23a66
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/finger.c
@@ -0,0 +1,1475 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * This is a finger program. It prints out useful information about users
+ * by digging it up from various system files.
+ *
+ * There are three output formats, all of which give login name, teletype
+ * line number, and login time. The short output format is reminiscent
+ * of finger on ITS, and gives one line of information per user containing
+ * in addition to the minimum basic requirements (MBR), the user's full name,
+ * idle time and location.
+ * The quick style output is UNIX who-like, giving only name, teletype and
+ * login time. Finally, the long style output give the same information
+ * as the short (in more legible format), the home directory and shell
+ * of the user, and, if it exits, a copy of the file .plan in the users
+ * home directory. Finger may be called with or without a list of people
+ * to finger -- if no list is given, all the people currently logged in
+ * are fingered.
+ *
+ * The program is validly called by one of the following:
+ *
+ * finger {short form list of users}
+ * finger -l {long form list of users}
+ * finger -b {briefer long form list of users}
+ * finger -q {quick list of users}
+ * finger -i {quick list of users with idle times}
+ * finger -m {matches arguments against only username}
+ * finger -f {suppress header in non-long form}
+ * finger -p {suppress printing of .plan file}
+ * finger -h {suppress printing of .project file}
+ * finger -i {forces "idle" output format}
+ * finger namelist {long format list of specified users}
+ * finger -s namelist {short format list of specified users}
+ * finger -w namelist {narrow short format list of specified users}
+ *
+ * where 'namelist' is a list of users login names.
+ * The other options can all be given after one '-', or each can have its
+ * own '-'. The -f option disables the printing of headers for short and
+ * quick outputs. The -b option briefens long format outputs. The -p
+ * option turns off plans for long format outputs.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <utmpx.h>
+#include <sys/signal.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <lastlog.h>
+#include <ctype.h>
+#include <sys/time.h>
+#include <time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <locale.h>
+#include <sys/select.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <fcntl.h>
+#include <curses.h>
+#include <unctrl.h>
+#include <maillock.h>
+#include <deflt.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <macros.h>
+
+static char gecos_ignore_c = '*'; /* ignore this in real name */
+static char gecos_sep_c = ','; /* separator in pw_gecos field */
+static char gecos_samename = '&'; /* repeat login name in real name */
+
+#define TALKABLE 0220 /* tty is writable if this mode */
+
+#define NMAX sizeof (((struct utmpx *)0)->ut_name)
+#define LMAX sizeof (((struct utmpx *)0)->ut_line)
+#define HMAX sizeof (((struct utmpx *)0)->ut_host)
+
+struct person { /* one for each person fingered */
+ char *name; /* name */
+ char tty[LMAX+1]; /* null terminated tty line */
+ char host[HMAX+1]; /* null terminated remote host name */
+ char *ttyloc; /* location of tty line, if any */
+ time_t loginat; /* time of (last) login */
+ time_t idletime; /* how long idle (if logged in) */
+ char *realname; /* pointer to full name */
+ struct passwd *pwd; /* structure of /etc/passwd stuff */
+ char loggedin; /* person is logged in */
+ char writable; /* tty is writable */
+ char original; /* this is not a duplicate entry */
+ struct person *link; /* link to next person */
+};
+
+char LASTLOG[] = "/var/adm/lastlog"; /* last login info */
+char PLAN[] = "/.plan"; /* what plan file is */
+char PROJ[] = "/.project"; /* what project file */
+
+int unbrief = 1; /* -b option default */
+int header = 1; /* -f option default */
+int hack = 1; /* -h option default */
+int idle = 0; /* -i option default */
+int large = 0; /* -l option default */
+int match = 1; /* -m option default */
+int plan = 1; /* -p option default */
+int unquick = 1; /* -q option default */
+int small = 0; /* -s option default */
+int wide = 1; /* -w option default */
+
+/*
+ * RFC 1288 says that system administrators should have the option of
+ * separately allowing ASCII characters less than 32 or greater than
+ * 126. The termpass variable keeps track of this.
+ */
+char defaultfile[] = "/etc/default/finger";
+char passvar[] = "PASS=";
+int termpass = 0; /* default is ASCII only */
+char *termopts[] = {
+#define TERM_LOW 0
+ "low",
+#define TERM_HIGH 1
+ "high",
+ (char *)NULL
+};
+#define TS_LOW (1 << TERM_LOW) /* print characters less than 32 */
+#define TS_HIGH (1 << TERM_HIGH) /* print characters greater than 126 */
+
+
+int unshort;
+FILE *lf; /* LASTLOG file pointer */
+struct person *person1; /* list of people */
+time_t tloc; /* current time */
+
+char usagestr[] = "Usage: "
+ "finger [-bfhilmpqsw] [name1 [name2 ...] ]\n";
+
+int AlreadyPrinted(uid_t uid);
+void AnyMail(char *name);
+void catfile(char *s, mode_t mode, int trunc_at_nl);
+void decode(struct person *pers);
+void doall(void);
+void donames(char **argv);
+void findidle(struct person *pers);
+void findwhen(struct person *pers);
+void fwclose(void);
+void fwopen(void);
+void initscreening(void);
+void ltimeprint(char *before, time_t *dt, char *after);
+int matchcmp(char *gname, char *login, char *given);
+int namecmp(char *name1, char *name2);
+int netfinger(char *name);
+void personprint(struct person *pers);
+void print(void);
+struct passwd *pwdcopy(const struct passwd *pfrom);
+void quickprint(struct person *pers);
+void shortprint(struct person *pers);
+void stimeprint(time_t *dt);
+
+
+int
+main(int argc, char **argv)
+{
+ int c;
+
+ (void) setlocale(LC_ALL, "");
+ /* parse command line for (optional) arguments */
+ while ((c = getopt(argc, argv, "bfhilmpqsw")) != EOF)
+ switch (c) {
+ case 'b':
+ unbrief = 0;
+ break;
+ case 'f':
+ header = 0;
+ break;
+ case 'h':
+ hack = 0;
+ break;
+ case 'i':
+ idle = 1;
+ unquick = 0;
+ break;
+ case 'l':
+ large = 1;
+ break;
+ case 'm':
+ match = 0;
+ break;
+ case 'p':
+ plan = 0;
+ break;
+ case 'q':
+ unquick = 0;
+ break;
+ case 's':
+ small = 1;
+ break;
+ case 'w':
+ wide = 0;
+ break;
+ default:
+ (void) fprintf(stderr, usagestr);
+ exit(1);
+ }
+ if (unquick || idle)
+ tloc = time(NULL);
+
+ /* find out what filtering on .plan/.project files we should do */
+ initscreening();
+
+ /*
+ * optind == argc means no names given
+ */
+ if (optind == argc)
+ doall();
+ else
+ donames(&argv[optind]);
+ if (person1)
+ print();
+ return (0);
+ /* NOTREACHED */
+}
+
+void
+doall(void)
+{
+ struct person *p;
+ struct passwd *pw;
+ struct utmpx *u;
+ char name[NMAX + 1];
+
+ unshort = large;
+ setutxent();
+ if (unquick) {
+ setpwent();
+ fwopen();
+ }
+ while (u = getutxent()) {
+ if (u->ut_name[0] == 0 ||
+ nonuserx(*u) ||
+ u->ut_type != USER_PROCESS)
+ continue;
+ if (person1 == 0)
+ p = person1 = malloc(sizeof (*p));
+ else {
+ p->link = malloc(sizeof (*p));
+ p = p->link;
+ }
+ bcopy(u->ut_name, name, NMAX);
+ name[NMAX] = 0;
+ bcopy(u->ut_line, p->tty, LMAX);
+ p->tty[LMAX] = 0;
+ bcopy(u->ut_host, p->host, HMAX);
+ p->host[HMAX] = 0;
+ p->loginat = u->ut_tv.tv_sec;
+ p->pwd = 0;
+ p->loggedin = 1;
+ if (unquick && (pw = getpwnam(name))) {
+ p->pwd = pwdcopy(pw);
+ decode(p);
+ p->name = p->pwd->pw_name;
+ } else
+ p->name = strdup(name);
+ p->ttyloc = NULL;
+ }
+ if (unquick) {
+ fwclose();
+ endpwent();
+ }
+ endutxent();
+ if (person1 == 0) {
+ (void) printf("No one logged on\n");
+ return;
+ }
+ p->link = 0;
+}
+
+void
+donames(char **argv)
+{
+ struct person *p;
+ struct passwd *pw;
+ struct utmpx *u;
+
+ /*
+ * get names from command line and check to see if they're
+ * logged in
+ */
+ unshort = !small;
+ for (; *argv != 0; argv++) {
+ if (netfinger(*argv))
+ continue;
+ if (person1 == 0)
+ p = person1 = malloc(sizeof (*p));
+ else {
+ p->link = malloc(sizeof (*p));
+ p = p->link;
+ }
+ p->name = *argv;
+ p->loggedin = 0;
+ p->original = 1;
+ p->pwd = 0;
+ }
+ if (person1 == 0)
+ return;
+ p->link = 0;
+ /*
+ * if we are doing it, read /etc/passwd for the useful info
+ */
+ if (unquick) {
+ setpwent();
+ if (!match) {
+ for (p = person1; p != 0; p = p->link) {
+ if (pw = getpwnam(p->name))
+ p->pwd = pwdcopy(pw);
+ }
+ } else {
+ while ((pw = getpwent()) != 0) {
+ for (p = person1; p != 0; p = p->link) {
+ if (!p->original)
+ continue;
+ if (strcmp(p->name, pw->pw_name) != 0 &&
+ !matchcmp(pw->pw_gecos, pw->pw_name,
+ p->name)) {
+ continue;
+ }
+ if (p->pwd == 0) {
+ p->pwd = pwdcopy(pw);
+ } else {
+ struct person *new;
+ /*
+ * Handle multiple login names.
+ * Insert new "duplicate" entry
+ * behind.
+ */
+ new = malloc(sizeof (*new));
+ new->pwd = pwdcopy(pw);
+ new->name = p->name;
+ new->original = 1;
+ new->loggedin = 0;
+ new->ttyloc = NULL;
+ new->link = p->link;
+ p->original = 0;
+ p->link = new;
+ p = new;
+ }
+ }
+ }
+ }
+ endpwent();
+ }
+ /* Now get login information */
+ setutxent();
+ while (u = getutxent()) {
+ if (u->ut_name[0] == 0 || u->ut_type != USER_PROCESS)
+ continue;
+ for (p = person1; p != 0; p = p->link) {
+ p->ttyloc = NULL;
+ if (p->loggedin == 2)
+ continue;
+ if (strncmp(p->pwd ? p->pwd->pw_name : p->name,
+ u->ut_name, NMAX) != 0)
+ continue;
+ if (p->loggedin == 0) {
+ bcopy(u->ut_line, p->tty, LMAX);
+ p->tty[LMAX] = 0;
+ bcopy(u->ut_host, p->host, HMAX);
+ p->host[HMAX] = 0;
+ p->loginat = u->ut_tv.tv_sec;
+ p->loggedin = 1;
+ } else { /* p->loggedin == 1 */
+ struct person *new;
+ new = malloc(sizeof (*new));
+ new->name = p->name;
+ bcopy(u->ut_line, new->tty, LMAX);
+ new->tty[LMAX] = 0;
+ bcopy(u->ut_host, new->host, HMAX);
+ new->host[HMAX] = 0;
+ new->loginat = u->ut_tv.tv_sec;
+ new->pwd = p->pwd;
+ new->loggedin = 1;
+ new->original = 0;
+ new->link = p->link;
+ p->loggedin = 2;
+ p->link = new;
+ p = new;
+ }
+ }
+ }
+ endutxent();
+ if (unquick) {
+ fwopen();
+ for (p = person1; p != 0; p = p->link)
+ decode(p);
+ fwclose();
+ }
+}
+
+void
+print(void)
+{
+ struct person *p;
+ char *s;
+
+ /*
+ * print out what we got
+ */
+ if (header) {
+ if (unquick) {
+ if (!unshort) {
+ if (wide) {
+ (void) printf("Login "
+ "Name TTY "
+ "Idle When Where\n");
+ } else {
+ (void) printf("Login TTY Idle "
+ "When Where\n");
+ }
+ }
+ } else {
+ (void) printf("Login TTY When");
+ if (idle)
+ (void) printf(" Idle");
+ (void) putchar('\n');
+ }
+ }
+ for (p = person1; p != 0; p = p->link) {
+ if (!unquick) {
+ quickprint(p);
+ continue;
+ }
+ if (!unshort) {
+ shortprint(p);
+ continue;
+ }
+ personprint(p);
+ if (p->pwd != 0 && !AlreadyPrinted(p->pwd->pw_uid)) {
+ AnyMail(p->pwd->pw_name);
+ if (hack) {
+ struct stat sbuf;
+
+ s = malloc(strlen(p->pwd->pw_dir) +
+ sizeof (PROJ));
+ if (s) {
+ (void) strcpy(s, p->pwd->pw_dir);
+ (void) strcat(s, PROJ);
+ if (stat(s, &sbuf) != -1 &&
+ (S_ISREG(sbuf.st_mode) ||
+ S_ISFIFO(sbuf.st_mode)) &&
+ (sbuf.st_mode & S_IROTH)) {
+ (void) printf("Project: ");
+ catfile(s, sbuf.st_mode, 1);
+ (void) putchar('\n');
+ }
+ free(s);
+ }
+ }
+ if (plan) {
+ struct stat sbuf;
+
+ s = malloc(strlen(p->pwd->pw_dir) +
+ sizeof (PLAN));
+ if (s) {
+ (void) strcpy(s, p->pwd->pw_dir);
+ (void) strcat(s, PLAN);
+ if (stat(s, &sbuf) == -1 ||
+ (!S_ISREG(sbuf.st_mode) &&
+ !S_ISFIFO(sbuf.st_mode)) ||
+ ((sbuf.st_mode & S_IROTH) == 0))
+ (void) printf("No Plan.\n");
+ else {
+ (void) printf("Plan:\n");
+ catfile(s, sbuf.st_mode, 0);
+ }
+ free(s);
+ }
+ }
+ }
+ if (p->link != 0)
+ (void) putchar('\n');
+ }
+}
+
+/*
+ * Duplicate a pwd entry.
+ * Note: Only the useful things (what the program currently uses) are copied.
+ */
+struct passwd *
+pwdcopy(const struct passwd *pfrom)
+{
+ struct passwd *pto;
+
+ pto = malloc(sizeof (*pto));
+ pto->pw_name = strdup(pfrom->pw_name);
+ pto->pw_uid = pfrom->pw_uid;
+ pto->pw_gecos = strdup(pfrom->pw_gecos);
+ pto->pw_dir = strdup(pfrom->pw_dir);
+ pto->pw_shell = strdup(pfrom->pw_shell);
+ return (pto);
+}
+
+/*
+ * print out information on quick format giving just name, tty, login time
+ * and idle time if idle is set.
+ */
+void
+quickprint(struct person *pers)
+{
+ (void) printf("%-8.8s ", pers->name);
+ if (pers->loggedin) {
+ if (idle) {
+ findidle(pers);
+ (void) printf("%c%-12s %-16.16s",
+ pers->writable ? ' ' : '*',
+ pers->tty, ctime(&pers->loginat));
+ ltimeprint(" ", &pers->idletime, "");
+ } else {
+ (void) printf(" %-12s %-16.16s",
+ pers->tty, ctime(&pers->loginat));
+ }
+ (void) putchar('\n');
+ } else {
+ (void) printf(" Not Logged In\n");
+ }
+}
+
+/*
+ * print out information in short format, giving login name, full name,
+ * tty, idle time, login time, and host.
+ */
+void
+shortprint(struct person *pers)
+{
+ char *p;
+
+ if (pers->pwd == 0) {
+ (void) printf("%-15s ???\n", pers->name);
+ return;
+ }
+ (void) printf("%-8s", pers->pwd->pw_name);
+ if (wide) {
+ if (pers->realname) {
+ (void) printf(" %-20.20s", pers->realname);
+ } else {
+ (void) printf(" ??? ");
+ }
+ }
+ (void) putchar(' ');
+ if (pers->loggedin && !pers->writable) {
+ (void) putchar('*');
+ } else {
+ (void) putchar(' ');
+ }
+ if (*pers->tty) {
+ (void) printf("%-11.11s ", pers->tty);
+ } else {
+ (void) printf(" "); /* 12 spaces */
+ }
+ p = ctime(&pers->loginat);
+ if (pers->loggedin) {
+ stimeprint(&pers->idletime);
+ (void) printf(" %3.3s %-5.5s ", p, p + 11);
+ } else if (pers->loginat == 0) {
+ (void) printf(" < . . . . >");
+ } else if (tloc - pers->loginat >= 180 * 24 * 60 * 60) {
+ (void) printf(" <%-6.6s, %-4.4s>", p + 4, p + 20);
+ } else {
+ (void) printf(" <%-12.12s>", p + 4);
+ }
+ if (*pers->host) {
+ (void) printf(" %-20.20s", pers->host);
+ } else {
+ if (pers->ttyloc != NULL)
+ (void) printf(" %-20.20s", pers->ttyloc);
+ }
+ (void) putchar('\n');
+}
+
+
+/*
+ * print out a person in long format giving all possible information.
+ * directory and shell are inhibited if unbrief is clear.
+ */
+void
+personprint(struct person *pers)
+{
+ if (pers->pwd == 0) {
+ (void) printf("Login name: %-10s\t\t\tIn real life: ???\n",
+ pers->name);
+ return;
+ }
+ (void) printf("Login name: %-10s", pers->pwd->pw_name);
+ if (pers->loggedin && !pers->writable) {
+ (void) printf(" (messages off) ");
+ } else {
+ (void) printf(" ");
+ }
+ if (pers->realname) {
+ (void) printf("In real life: %s", pers->realname);
+ }
+ if (unbrief) {
+ (void) printf("\nDirectory: %-25s", pers->pwd->pw_dir);
+ if (*pers->pwd->pw_shell)
+ (void) printf("\tShell: %-s", pers->pwd->pw_shell);
+ }
+ if (pers->loggedin) {
+ char *ep = ctime(&pers->loginat);
+ if (*pers->host) {
+ (void) printf("\nOn since %15.15s on %s from %s",
+ &ep[4], pers->tty, pers->host);
+ ltimeprint("\n", &pers->idletime, " Idle Time");
+ } else {
+ (void) printf("\nOn since %15.15s on %-12s",
+ &ep[4], pers->tty);
+ ltimeprint("\n", &pers->idletime, " Idle Time");
+ }
+ } else if (pers->loginat == 0) {
+ (void) printf("\nNever logged in.");
+ } else if (tloc - pers->loginat > 180 * 24 * 60 * 60) {
+ char *ep = ctime(&pers->loginat);
+ (void) printf("\nLast login %10.10s, %4.4s on %s",
+ ep, ep+20, pers->tty);
+ if (*pers->host) {
+ (void) printf(" from %s", pers->host);
+ }
+ } else {
+ char *ep = ctime(&pers->loginat);
+ (void) printf("\nLast login %16.16s on %s", ep, pers->tty);
+ if (*pers->host) {
+ (void) printf(" from %s", pers->host);
+ }
+ }
+ (void) putchar('\n');
+}
+
+
+/*
+ * decode the information in the gecos field of /etc/passwd
+ */
+void
+decode(struct person *pers)
+{
+ char buffer[256];
+ char *bp, *gp, *lp;
+
+ pers->realname = 0;
+ if (pers->pwd == 0)
+ return;
+ gp = pers->pwd->pw_gecos;
+ bp = buffer;
+
+ if (gecos_ignore_c != '\0' &&
+ *gp == gecos_ignore_c) {
+ gp++;
+ }
+ while (*gp != '\0' &&
+ *gp != gecos_sep_c) { /* name */
+ if (*gp == gecos_samename) {
+ lp = pers->pwd->pw_name;
+ if (islower(*lp))
+ *bp++ = toupper(*lp++);
+ while (*bp++ = *lp++)
+ ;
+ bp--;
+ gp++;
+ } else {
+ *bp++ = *gp++;
+ }
+ }
+ *bp++ = 0;
+ if (bp > (buffer + 1))
+ pers->realname = strdup(buffer);
+ if (pers->loggedin)
+ findidle(pers);
+ else
+ findwhen(pers);
+}
+
+/*
+ * find the last log in of a user by checking the LASTLOG file.
+ * the entry is indexed by the uid, so this can only be done if
+ * the uid is known (which it isn't in quick mode)
+ */
+void
+fwopen(void)
+{
+ if ((lf = fopen(LASTLOG, "r")) == (FILE *)NULL)
+ (void) fprintf(stderr, "finger: %s open error\n", LASTLOG);
+}
+
+void
+findwhen(struct person *pers)
+{
+ struct lastlog ll;
+
+ if (lf != (FILE *)NULL) {
+ if (fseeko(lf, (off_t)pers->pwd->pw_uid * (off_t)sizeof (ll),
+ SEEK_SET) == 0) {
+ if (fread((char *)&ll, sizeof (ll), 1, lf) == 1) {
+ int l_max, h_max;
+
+ l_max = min(LMAX, sizeof (ll.ll_line));
+ h_max = min(HMAX, sizeof (ll.ll_host));
+
+ bcopy(ll.ll_line, pers->tty, l_max);
+ pers->tty[l_max] = '\0';
+ bcopy(ll.ll_host, pers->host, h_max);
+ pers->host[h_max] = '\0';
+ pers->loginat = ll.ll_time;
+ } else {
+ if (ferror(lf))
+ (void) fprintf(stderr,
+ "finger: %s read error\n", LASTLOG);
+ pers->tty[0] = 0;
+ pers->host[0] = 0;
+ pers->loginat = 0L;
+ }
+ } else {
+ (void) fprintf(stderr, "finger: %s fseeko error\n",
+ LASTLOG);
+ }
+ } else {
+ pers->tty[0] = 0;
+ pers->host[0] = 0;
+ pers->loginat = 0L;
+ }
+}
+
+void
+fwclose(void)
+{
+ if (lf != (FILE *)0)
+ (void) fclose(lf);
+}
+
+/*
+ * find the idle time of a user by doing a stat on /dev/tty??,
+ * where tty?? has been gotten from UTMPX_FILE, supposedly.
+ */
+void
+findidle(struct person *pers)
+{
+ struct stat ttystatus;
+#ifdef sun
+ struct stat inputdevstatus;
+#endif
+#define TTYLEN (sizeof ("/dev/") - 1)
+ static char buffer[TTYLEN + LMAX + 1] = "/dev/";
+ time_t t;
+ time_t lastinputtime;
+
+ (void) strcpy(buffer + TTYLEN, pers->tty);
+ buffer[TTYLEN+LMAX] = 0;
+ if (stat(buffer, &ttystatus) < 0) {
+ (void) fprintf(stderr, "finger: Can't stat %s\n", buffer);
+ exit(4);
+ }
+ lastinputtime = ttystatus.st_atime;
+#ifdef sun
+ if (strcmp(pers->tty, "console") == 0) {
+ /*
+ * On the console, the user may be running a window system; if
+ * so, their activity will show up in the last-access times of
+ * "/dev/kbd" and "/dev/mouse", so take the minimum of the idle
+ * times on those two devices and "/dev/console" and treat that
+ * as the idle time.
+ */
+ if (stat("/dev/kbd", &inputdevstatus) == 0) {
+ if (lastinputtime < inputdevstatus.st_atime)
+ lastinputtime = inputdevstatus.st_atime;
+ }
+ if (stat("/dev/mouse", &inputdevstatus) == 0) {
+ if (lastinputtime < inputdevstatus.st_atime)
+ lastinputtime = inputdevstatus.st_atime;
+ }
+ }
+#endif
+ t = time(NULL);
+ if (t < lastinputtime)
+ pers->idletime = (time_t)0;
+ else
+ pers->idletime = t - lastinputtime;
+ pers->writable = (ttystatus.st_mode & TALKABLE) == TALKABLE;
+}
+
+/*
+ * print idle time in short format; this program always prints 4 characters;
+ * if the idle time is zero, it prints 4 blanks.
+ */
+void
+stimeprint(time_t *dt)
+{
+ struct tm *delta;
+
+ delta = gmtime(dt);
+ if (delta->tm_yday == 0)
+ if (delta->tm_hour == 0)
+ if (delta->tm_min == 0)
+ (void) printf(" ");
+ else
+ (void) printf(" %2d", delta->tm_min);
+ else
+ if (delta->tm_hour >= 10)
+ (void) printf("%3d:", delta->tm_hour);
+ else
+ (void) printf("%1d:%02d",
+ delta->tm_hour, delta->tm_min);
+ else
+ (void) printf("%3dd", delta->tm_yday);
+}
+
+/*
+ * print idle time in long format with care being taken not to pluralize
+ * 1 minutes or 1 hours or 1 days.
+ * print "prefix" first.
+ */
+void
+ltimeprint(char *before, time_t *dt, char *after)
+{
+ struct tm *delta;
+
+ delta = gmtime(dt);
+ if (delta->tm_yday == 0 && delta->tm_hour == 0 && delta->tm_min == 0 &&
+ delta->tm_sec <= 10)
+ return;
+ (void) printf("%s", before);
+ if (delta->tm_yday >= 10)
+ (void) printf("%d days", delta->tm_yday);
+ else if (delta->tm_yday > 0)
+ (void) printf("%d day%s %d hour%s",
+ delta->tm_yday, delta->tm_yday == 1 ? "" : "s",
+ delta->tm_hour, delta->tm_hour == 1 ? "" : "s");
+ else
+ if (delta->tm_hour >= 10)
+ (void) printf("%d hours", delta->tm_hour);
+ else if (delta->tm_hour > 0)
+ (void) printf("%d hour%s %d minute%s",
+ delta->tm_hour, delta->tm_hour == 1 ? "" : "s",
+ delta->tm_min, delta->tm_min == 1 ? "" : "s");
+ else
+ if (delta->tm_min >= 10)
+ (void) printf("%2d minutes", delta->tm_min);
+ else if (delta->tm_min == 0)
+ (void) printf("%2d seconds", delta->tm_sec);
+ else
+ (void) printf("%d minute%s %d second%s",
+ delta->tm_min,
+ delta->tm_min == 1 ? "" : "s",
+ delta->tm_sec,
+ delta->tm_sec == 1 ? "" : "s");
+ (void) printf("%s", after);
+}
+
+/*
+ * The grammar of the pw_gecos field is sufficiently complex that the
+ * best way to parse it is by using an explicit finite-state machine,
+ * in which a table defines the rules of interpretation.
+ *
+ * Some special rules are necessary to handle the fact that names
+ * may contain certain punctuation characters. At this writing,
+ * the possible punctuation characters are '.', '-', and '_'.
+ *
+ * Other rules are needed to account for characters that require special
+ * processing when they appear in the pw_gecos field. At present, there
+ * are three such characters, with these default values and effects:
+ *
+ * gecos_ignore_c '*' This character is ignored.
+ * gecos_sep_c ',' Delimits displayed and nondisplayed contents.
+ * gecos_samename '&' Copies the login name into the output.
+ *
+ * As the program examines each successive character in the returned
+ * pw_gecos value, it fetches (from the table) the FSM rule applicable
+ * for that character in the current machine state, and thus determines
+ * the next state.
+ *
+ * The possible states are:
+ * S0 start
+ * S1 in a word
+ * S2 not in a word
+ * S3 copy login name into output
+ * S4 end of GECOS field
+ *
+ * Here follows a depiction of the state transitions.
+ *
+ *
+ * gecos_ignore_c OR isspace OR any other character
+ * +--+
+ * | |
+ * | V
+ * +-----+
+ * NULL OR | S0 | isalpha OR isdigit
+ * +---------------|start|------------------------+
+ * | gecos_sep_c +-----+ | isalpha OR isdigit
+ * | | | | +---------------------+
+ * | | | | | OR '.' '-' '_' |
+ * | | |isspace | | |
+ * | | +-------+ V V |
+ * | | | +-----------+ |
+ * | | | | S1 |<--+ |
+ * | | | | in a word | | isalpha OR |
+ * | | | +-----------+ | isdigit OR |
+ * | | | | | | | | '.' '-' '_' |
+ * | | +----- ---------------+ | | +-----+ |
+ * | | | | | | |
+ * | | | | gecos_ignore_c | | |
+ * | | | | isspace | | |
+ * | | | | ispunct/other | | |
+ * | | | | any other char | | |
+ * | | | | +---------------+ | |
+ * | | | | | |NULL OR gecos_sep_c |
+ * | | | | | +------------------+ |
+ * | gecos_samename| | V V | |
+ * | +-------------+ | +---------------+ | |
+ * | | | | S2 | isspace OR '.' '-' '_' | |
+ * | | gecos_samename | | not in a word |<---------------------+ | |
+ * | | +---------------+ +---------------+ OR gecos_ignore_c | | |
+ * | | | | ^ | | OR ispunct OR other | | |
+ * | | | | | | | | | |
+ * | | | gecos_samename | | | +-----------------------+ | |
+ * | | | +---------------------+ | | | |
+ * | | | | | | | |
+ * | | | | gecos_ignore_c| | NULL OR gecos_sep_c | |
+ * | | | | gecos_samename| +-----------------------+ | |
+ * | | | | ispunct/other | | | |
+ * | V V V isspace | | | |
+ * | +-----------------+ any other char| | | |
+ * | | S3 |---------------+ isalpha OR isdigit OR | | |
+ * | |insert login name|------------------------------------------ ----- ---+
+ * | +-----------------+ '.' '-' '_' | |
+ * | | NULL OR gecos_sep_c | |
+ * | +------------------------------------------+ | |
+ * | | | |
+ * | V V V
+ * | +------------+
+ * | NULL OR gecos_sep_c | S4 |
+ * +-------------------------------------------------------->|end of gecos|<--+
+ * +------------+ |
+ * | all |
+ * +-----+
+ *
+ *
+ * The transitions from the above diagram are summarized in
+ * the following table of target states, which is implemented
+ * in code as the gecos_fsm array.
+ *
+ * Input:
+ * +--gecos_ignore_c
+ * | +--gecos_sep_c
+ * | | +--gecos_samename
+ * | | | +--isalpha
+ * | | | | +--isdigit
+ * | | | | | +--isspace
+ * | | | | | | +--punctuation possible in name
+ * | | | | | | | +--other punctuation
+ * | | | | | | | | +--NULL character
+ * | | | | | | | | | +--any other character
+ * | | | | | | | | | |
+ * V V V V V V V V V V
+ * From: ---------------------------------------------------
+ * S0 | S0 | S4 | S3 | S1 | S1 | S0 | S1 | S2 | S4 | S0 |
+ * S1 | S2 | S4 | S3 | S1 | S1 | S2 | S1 | S2 | S4 | S2 |
+ * S2 | S2 | S4 | S3 | S1 | S1 | S2 | S2 | S2 | S4 | S2 |
+ * S3 | S2 | S4 | S2 | S1 | S1 | S2 | S1 | S2 | S4 | S2 |
+ * S4 | S4 | S4 | S4 | S4 | S4 | S4 | S4 | S4 | S4 | S4 |
+ *
+ */
+
+/*
+ * Data types and structures for scanning the pw_gecos field.
+ */
+typedef enum gecos_state {
+ S0, /* start */
+ S1, /* in a word */
+ S2, /* not in a word */
+ S3, /* copy login */
+ S4 /* end of gecos */
+} gecos_state_t;
+
+#define GFSM_ROWS 5
+#define GFSM_COLS 10
+
+gecos_state_t gecos_fsm[GFSM_ROWS][GFSM_COLS] = {
+ {S0, S4, S3, S1, S1, S0, S1, S2, S4, S0}, /* S0 */
+ {S2, S4, S3, S1, S1, S2, S1, S2, S4, S2}, /* S1 */
+ {S2, S4, S3, S1, S1, S2, S2, S2, S4, S2}, /* S2 */
+ {S2, S4, S2, S1, S1, S2, S1, S2, S4, S2}, /* S3 */
+ {S4, S4, S4, S4, S4, S4, S4, S4, S4, S4} /* S4 */
+};
+
+/*
+ * Scan the pw_gecos field according to defined state table;
+ * return the next state according the the rules.
+ */
+gecos_state_t
+gecos_scan_state(gecos_state_t instate, char ch)
+{
+ if (ch == gecos_ignore_c) {
+ return (gecos_fsm[instate][0]);
+ } else if (ch == gecos_sep_c) {
+ return (gecos_fsm[instate][1]);
+ } else if (ch == gecos_samename) {
+ return (gecos_fsm[instate][2]);
+ } else if (isalpha(ch)) {
+ return (gecos_fsm[instate][3]);
+ } else if (isdigit(ch)) {
+ return (gecos_fsm[instate][4]);
+ } else if (isspace(ch)) {
+ return (gecos_fsm[instate][5]);
+ } else if (ch == '.' || ch == '-' || ch == '_') {
+ return (gecos_fsm[instate][6]);
+ } else if (ispunct(ch)) {
+ return (gecos_fsm[instate][7]);
+ } else if (ch == '\0') {
+ return (gecos_fsm[instate][8]);
+ }
+ return (gecos_fsm[instate][9]);
+}
+
+
+/*
+ * Compare the given argument, which is taken to be a username, with
+ * the login name and with strings in the the pw_gecos field.
+ */
+int
+matchcmp(char *gname, char *login, char *given)
+{
+ char buffer[100];
+ char *bp, *lp, *gp;
+
+ gecos_state_t kstate = S0;
+ gecos_state_t kstate_next = S0;
+
+ if (*gname == '\0' && *given == '\0')
+ return (1);
+
+ bp = buffer;
+ gp = gname;
+
+ do {
+ kstate_next = gecos_scan_state(kstate, *gp);
+
+ switch (kstate_next) {
+
+ case S0:
+ gp++;
+ break;
+ case S1:
+ if (bp < buffer + sizeof (buffer)) {
+ *bp++ = *gp++;
+ }
+ break;
+ case S2:
+ if (kstate == S1 || kstate == S3) {
+ *bp++ = ' ';
+ }
+ gp++;
+ break;
+ case S3:
+ lp = login;
+ do {
+ *bp++ = *lp++;
+ } while (*bp != '\0' && bp < buffer + sizeof (buffer));
+ bp--;
+ break;
+ case S4:
+ *bp++ = '\0';
+ break;
+ default:
+ *bp++ = '\0';
+ break;
+ }
+ kstate = kstate_next;
+
+ } while ((bp < buffer + sizeof (buffer)) && kstate != S4);
+
+ gp = strtok(buffer, " ");
+
+ while (gp != NULL) {
+ if (namecmp(gp, given) > 0) {
+ return (1);
+ }
+ gp = strtok(NULL, " ");
+ }
+ return (0);
+}
+
+/*
+ * Perform the character-by-character comparison.
+ * It is intended that "finger foo" should match "foo2", but an argument
+ * consisting entirely of digits should not be matched too broadly.
+ * Also, we do not want "finger foo123" to match "Mr. Foo" in the gecos.
+ */
+int
+namecmp(char *name1, char *name2)
+{
+ char c1, c2;
+ boolean_t alphaseen = B_FALSE;
+ boolean_t digitseen = B_FALSE;
+
+ for (;;) {
+ c1 = *name1++;
+ if (isalpha(c1))
+ alphaseen = B_TRUE;
+ if (isdigit(c1))
+ digitseen = B_TRUE;
+ if (isupper(c1))
+ c1 = tolower(c1);
+
+ c2 = *name2++;
+ if (isupper(c2))
+ c2 = tolower(c2);
+
+ if (c1 != c2)
+ break;
+ if (c1 == '\0')
+ return (1);
+ }
+ if (!c1) {
+ for (name2--; isdigit(*name2); name2++)
+ ;
+ if (*name2 == '\0' && digitseen) {
+ return (1);
+ }
+ } else if (!c2) {
+ for (name1--; isdigit(*name1); name1++)
+ ;
+ if (*name1 == '\0' && alphaseen) {
+ return (1);
+ }
+ }
+ return (0);
+}
+
+
+int
+netfinger(char *name)
+{
+ char *host;
+ struct hostent *hp;
+ struct sockaddr_in6 sin6;
+ struct in6_addr ipv6addr;
+ struct in_addr ipv4addr;
+ int s;
+ FILE *f;
+ int c;
+ int lastc;
+ char abuf[INET6_ADDRSTRLEN];
+ int error_num;
+
+ if (name == NULL)
+ return (0);
+ host = strrchr(name, '@');
+ if (host == NULL)
+ return (0);
+ *host++ = 0;
+
+ if ((hp = getipnodebyname(host, AF_INET6, AI_ALL | AI_ADDRCONFIG |
+ AI_V4MAPPED, &error_num)) == NULL) {
+ if (error_num == TRY_AGAIN) {
+ (void) fprintf(stderr,
+ "unknown host: %s (try again later)\n", host);
+ } else {
+ (void) fprintf(stderr, "unknown host: %s\n", host);
+ }
+ return (1);
+ }
+
+ /*
+ * If hp->h_name is a IPv4-mapped IPv6 literal, we'll convert it to
+ * IPv4 literal address.
+ */
+ if ((inet_pton(AF_INET6, hp->h_name, &ipv6addr) > 0) &&
+ IN6_IS_ADDR_V4MAPPED(&ipv6addr)) {
+ IN6_V4MAPPED_TO_INADDR(&ipv6addr, &ipv4addr);
+ (void) printf("[%s] ", inet_ntop(AF_INET, &ipv4addr, abuf,
+ sizeof (abuf)));
+ } else {
+ (void) printf("[%s] ", hp->h_name);
+ }
+ bzero(&sin6, sizeof (sin6));
+ sin6.sin6_family = hp->h_addrtype;
+ bcopy(hp->h_addr_list[0], (char *)&sin6.sin6_addr, hp->h_length);
+ sin6.sin6_port = htons(IPPORT_FINGER);
+ s = socket(sin6.sin6_family, SOCK_STREAM, 0);
+ if (s < 0) {
+ (void) fflush(stdout);
+ perror("socket");
+ freehostent(hp);
+ return (1);
+ }
+ while (connect(s, (struct sockaddr *)&sin6, sizeof (sin6)) < 0) {
+
+ if (hp && hp->h_addr_list[1]) {
+
+ hp->h_addr_list++;
+ bcopy(hp->h_addr_list[0],
+ (caddr_t)&sin6.sin6_addr, hp->h_length);
+ (void) close(s);
+ s = socket(sin6.sin6_family, SOCK_STREAM, 0);
+ if (s < 0) {
+ (void) fflush(stdout);
+ perror("socket");
+ freehostent(hp);
+ return (0);
+ }
+ continue;
+ }
+
+ (void) fflush(stdout);
+ perror("connect");
+ (void) close(s);
+ freehostent(hp);
+ return (1);
+ }
+ freehostent(hp);
+ hp = NULL;
+
+ (void) printf("\n");
+ if (large)
+ (void) write(s, "/W ", 3);
+ (void) write(s, name, strlen(name));
+ (void) write(s, "\r\n", 2);
+ f = fdopen(s, "r");
+
+ lastc = '\n';
+ while ((c = getc(f)) != EOF) {
+ /* map CRLF -> newline */
+ if ((lastc == '\r') && (c != '\n'))
+ /* print out saved CR */
+ (void) putchar('\r');
+ lastc = c;
+ if (c == '\r')
+ continue;
+ (void) putchar(c);
+ }
+
+ if (lastc != '\n')
+ (void) putchar('\n');
+ (void) fclose(f);
+ return (1);
+}
+
+/*
+ * AnyMail - takes a username (string pointer thereto), and
+ * prints on standard output whether there is any unread mail,
+ * and if so, how old it is. (JCM@Shasta 15 March 80)
+ */
+void
+AnyMail(char *name)
+{
+ struct stat buf; /* space for file status buffer */
+ char *mbxdir = MAILDIR; /* string with path preamble */
+ char *mbxpath; /* space for entire pathname */
+
+ char *timestr;
+
+ mbxpath = malloc(strlen(name) + strlen(MAILDIR) + 1);
+ if (mbxpath == (char *)NULL)
+ return;
+
+ (void) strcpy(mbxpath, mbxdir); /* copy preamble into path name */
+ (void) strcat(mbxpath, name); /* concatenate user name to path */
+
+ if (stat(mbxpath, &buf) == -1 || buf.st_size == 0) {
+ /* Mailbox is empty or nonexistent */
+ (void) printf("No unread mail\n");
+ } else {
+ if (buf.st_mtime < buf.st_atime) {
+ /*
+ * No new mail since the last time the user read it.
+ */
+ (void) printf("Mail last read ");
+ (void) printf("%s", ctime(&buf.st_atime));
+ } else if (buf.st_mtime > buf.st_atime) {
+ /*
+ * New mail has definitely arrived since the last time
+ * mail was read. mtime is the time the most recent
+ * message arrived; atime is either the time the oldest
+ * unread message arrived, or the last time the mail
+ * was read.
+ */
+ (void) printf("New mail received ");
+ timestr = ctime(&buf.st_mtime); /* time last modified */
+ timestr[24] = '\0'; /* suppress newline (ugh) */
+ (void) printf("%s", timestr);
+ (void) printf(";\n unread since ");
+ (void) printf("%s", ctime(&buf.st_atime));
+ } else {
+ /*
+ * There is something in mailbox, but we can't really
+ * be sure whether it is mail held there by the user
+ * or a (single) new message that was placed in a newly
+ * recreated mailbox, so punt and call it "unread mail."
+ */
+ (void) printf("Unread mail since ");
+ (void) printf("%s", ctime(&buf.st_mtime));
+ }
+ }
+ free(mbxpath);
+}
+
+/*
+ * return true iff we've already printed project/plan for this uid;
+ * if not, enter this uid into table (so this function has a side-effect.)
+ */
+#define PPMAX 4096 /* assume no more than 4096 logged-in users */
+uid_t PlanPrinted[PPMAX+1];
+int PPIndex = 0; /* index of next unused table entry */
+
+int
+AlreadyPrinted(uid_t uid)
+{
+ int i = 0;
+
+ while (i++ < PPIndex) {
+ if (PlanPrinted[i] == uid)
+ return (1);
+ }
+ if (i < PPMAX) {
+ PlanPrinted[i] = uid;
+ PPIndex++;
+ }
+ return (0);
+}
+
+#define FIFOREADTIMEOUT (60) /* read timeout on select */
+/* BEGIN CSTYLED */
+#define PRINT_CHAR(c) \
+ ( \
+ ((termpass & TS_HIGH) && ((int)c) > 126) \
+ || \
+ (isascii((int)c) && \
+ (isprint((int)c) || isspace((int)c)) \
+ ) \
+ || \
+ ((termpass & TS_LOW) && ((int)c) < 32) \
+ )
+/* END CSTYLED */
+
+
+void
+catfile(char *s, mode_t mode, int trunc_at_nl)
+{
+ if (S_ISFIFO(mode)) {
+ int fd;
+
+ fd = open(s, O_RDONLY | O_NONBLOCK);
+ if (fd != -1) {
+ fd_set readfds, exceptfds;
+ struct timeval tv;
+
+ FD_ZERO(&readfds);
+ FD_ZERO(&exceptfds);
+ FD_SET(fd, &readfds);
+ FD_SET(fd, &exceptfds);
+
+ timerclear(&tv);
+ tv.tv_sec = FIFOREADTIMEOUT;
+
+ (void) fflush(stdout);
+ while (select(fd + 1, &readfds, (fd_set *) 0,
+ &exceptfds, &tv) != -1) {
+ unsigned char buf[BUFSIZ];
+ int nread;
+
+ nread = read(fd, buf, sizeof (buf));
+ if (nread > 0) {
+ unsigned char *p;
+
+ FD_SET(fd, &readfds);
+ FD_SET(fd, &exceptfds);
+ for (p = buf; p < buf + nread; p++) {
+ if (trunc_at_nl && *p == '\n')
+ goto out;
+ if (PRINT_CHAR(*p))
+ (void) putchar((int)*p);
+ else if (isascii(*p))
+ (void) fputs(unctrl(*p),
+ stdout);
+ }
+ } else
+ break;
+ }
+out:
+ (void) close(fd);
+ }
+ } else {
+ int c;
+ FILE *fp;
+
+ fp = fopen(s, "r");
+ if (fp) {
+ while ((c = getc(fp)) != EOF) {
+ if (trunc_at_nl && c == '\n')
+ break;
+ if (PRINT_CHAR(c))
+ (void) putchar((int)c);
+ else
+ if (isascii(c))
+ (void) fputs(unctrl(c), stdout);
+ }
+ (void) fclose(fp);
+ }
+ }
+}
+
+
+void
+initscreening(void)
+{
+ char *options, *value;
+
+ if (defopen(defaultfile) == 0) {
+ char *cp;
+ int flags;
+
+ /*
+ * ignore case
+ */
+ flags = defcntl(DC_GETFLAGS, 0);
+ TURNOFF(flags, DC_CASE);
+ defcntl(DC_SETFLAGS, flags);
+
+ if (cp = defread(passvar)) {
+ options = cp;
+ while (*options != '\0')
+ switch (getsubopt(&options, termopts, &value)) {
+ case TERM_LOW:
+ termpass |= TS_LOW;
+ break;
+ case TERM_HIGH:
+ termpass |= TS_HIGH;
+ break;
+ }
+ }
+ (void) defopen(NULL); /* close default file */
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/ftp/Makefile b/usr/src/cmd/cmd-inet/usr.bin/ftp/Makefile
new file mode 100644
index 0000000000..b13f90f99f
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/ftp/Makefile
@@ -0,0 +1,75 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+PROG= ftp
+PROGDFL= ftp
+OBJS= cmds.o cmdtab.o ftp.o getpass.o glob.o main.o pclose.o \
+ ruserpass.o domacro.o
+
+AUTH_OBJS = auth.o cmds_gss.o secure.o
+OBJS += $(AUTH_OBJS)
+SRCS= $(OBJS:%.o=%.c)
+
+include ../../../Makefile.cmd
+
+CFLAGS += $(CCVERBOSE)
+CPPFLAGS += -D_FILE_OFFSET_BITS=64 \
+ -I$(SRC)/lib/gss_mechs/mech_krb5/include \
+ -I$(SRC)/uts/common/gssapi/mechs/krb5/include
+
+LDLIBS += -lsocket -lnsl -lcmd -lgss
+LDFLAGS += $(ZLAZYLOAD)
+
+DFLTD = $(ROOTETC)/default
+ETCDFLTPROG = $(PROGDFL:%=$(DFLTD)/%)
+$(ETCDFLTPROG) := FILEMODE = 0644
+$(ETCDFLTPROG) := OWNER = root
+$(ETCDFLTPROG) := GROUP = sys
+
+.KEEP_STATE:
+
+all: $(PROG) $(PROGDFL).dfl
+
+$(PROG): $(OBJS)
+ $(LINK.c) $(OBJS) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+install: all $(ROOTPROG) $(DFLTD) $(ETCDFLTPROG)
+
+clean:
+ $(RM) $(OBJS)
+
+lint: lint_SRCS
+
+$(DFLTD):
+ $(INS.dir)
+
+$(DFLTD)/%: %.dfl
+ $(INS.rename)
+
+include ../../../Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/usr.bin/ftp/auth.c b/usr/src/cmd/cmd-inet/usr.bin/ftp/auth.c
new file mode 100644
index 0000000000..368b86a5f1
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/ftp/auth.c
@@ -0,0 +1,423 @@
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Copyright (c) 1985, 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "ftp_var.h"
+#include <sys/types.h>
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_ext.h>
+
+int auth_type; /* Authentication succeeded? If so, what type? */
+
+char *radix_error(int);
+static void get_inet_addr_info(struct sockaddr_in6 *, gss_buffer_t);
+
+static char *radixN =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static char radix_pad = '=';
+
+/*
+ * authenticate the user, if auth_type is AUTHTYPE_NONE
+ *
+ * Returns: 0 if there is no auth type
+ * 1 if success
+ * 2 if failure
+ */
+
+gss_OID mechoid;
+gss_ctx_id_t gcontext; /* global gss security context */
+static const char *gss_trials[] = { "ftp", "host" };
+/* the number of elements in gss_trials array */
+static const int n_gss_trials = sizeof (gss_trials)/sizeof (char *);
+char *reply_parse;
+
+int
+do_auth(void)
+{
+ int oldverbose = verbose;
+ uchar_t *out_buf = NULL;
+ size_t outlen;
+ int i;
+
+ if (auth_type != AUTHTYPE_NONE)
+ return (1); /* auth already succeeded */
+
+ /* Other auth types go here ... */
+
+ if (command("AUTH %s", "GSSAPI") == CONTINUE) {
+ OM_uint32 maj_stat, min_stat;
+ gss_name_t target_name;
+ gss_buffer_desc send_tok, recv_tok, *token_ptr;
+ gss_buffer_desc temp_buf;
+ char stbuf[FTPBUFSIZ];
+ int comcode, trial;
+ int req_flags;
+ struct gss_channel_bindings_struct chan;
+
+ get_inet_addr_info(&myctladdr, &temp_buf);
+ chan.initiator_addrtype = GSS_C_AF_INET; /* OM_uint32 */
+ chan.initiator_address.length = temp_buf.length;
+ chan.initiator_address.value = malloc(temp_buf.length);
+ memcpy(chan.initiator_address.value, temp_buf.value,
+ temp_buf.length);
+
+ get_inet_addr_info(&remctladdr, &temp_buf);
+ chan.acceptor_addrtype = GSS_C_AF_INET; /* OM_uint32 */
+ chan.acceptor_address.length = temp_buf.length;
+ chan.acceptor_address.value = malloc(temp_buf.length);
+ memcpy(chan.acceptor_address.value, temp_buf.value,
+ temp_buf.length);
+
+ chan.application_data.length = 0;
+ chan.application_data.value = 0;
+
+ if (verbose)
+ (void) printf("GSSAPI accepted as authentication type\n");
+
+ /* set the forward flag */
+ req_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG;
+
+ if (fflag)
+ req_flags |= GSS_C_DELEG_FLAG;
+
+ /* blob from gss-client */
+ for (trial = 0; trial < n_gss_trials; trial++) {
+ /* ftp@hostname first, then host@hostname */
+ /* the V5 GSSAPI binding canonicalizes this for us... */
+ (void) snprintf(stbuf, FTPBUFSIZ, "%s@%s",
+ gss_trials[trial], hostname);
+ if (debug)
+ (void) fprintf(stderr,
+ "Trying to authenticate to <%s>\n", stbuf);
+
+ send_tok.value = stbuf;
+ send_tok.length = strlen(stbuf) + 1;
+ maj_stat = gss_import_name(&min_stat, &send_tok,
+ GSS_C_NT_HOSTBASED_SERVICE, &target_name);
+
+ if (maj_stat != GSS_S_COMPLETE) {
+ user_gss_error(maj_stat, min_stat, "parsing name");
+ (void) fprintf(stderr, "name parsed <%s>\n", stbuf);
+ continue;
+ }
+
+ token_ptr = GSS_C_NO_BUFFER;
+ gcontext = GSS_C_NO_CONTEXT; /* structure copy */
+
+ do {
+ if (debug)
+ (void) fprintf(stderr,
+ "calling gss_init_sec_context\n");
+
+ if (mechstr && !mechoid &&
+ __gss_mech_to_oid(mechstr, (gss_OID*)&mechoid) !=
+ GSS_S_COMPLETE)
+ (void) printf("do_auth: %s: not a valid "
+ "security mechanism\n", mechstr);
+
+ if (!mechoid)
+ mechoid = GSS_C_NULL_OID;
+
+ maj_stat = gss_init_sec_context(&min_stat,
+ GSS_C_NO_CREDENTIAL,
+ &gcontext,
+ target_name,
+ mechoid,
+ req_flags,
+ 0,
+ &chan, /* channel bindings */
+ token_ptr,
+ NULL, /* ignore mech type */
+ &send_tok,
+ NULL, /* ignore ret_flags */
+ NULL); /* ignore time_rec */
+
+ if (maj_stat != GSS_S_COMPLETE &&
+ maj_stat != GSS_S_CONTINUE_NEEDED) {
+
+ /* return an error if this is NOT the ftp ticket */
+ if (strcmp(gss_trials[trial], "ftp"))
+ user_gss_error(maj_stat, min_stat,
+ "initializing context");
+
+ (void) gss_release_name(&min_stat, &target_name);
+ /* could just be that we missed on the service name */
+ goto outer_loop;
+
+ }
+
+ if (send_tok.length != 0) {
+ int len = send_tok.length;
+ reply_parse = "ADAT="; /* for command() later */
+ oldverbose = verbose;
+ verbose = (trial == n_gss_trials-1)?0:-1;
+
+ outlen = ENCODELEN(send_tok.length);
+ out_buf = (uchar_t *)malloc(outlen);
+ if (out_buf == NULL) {
+ (void) fprintf(stderr, "memory error allocating "
+ "auth buffer\n");
+ maj_stat = GSS_S_FAILURE;
+ goto outer_loop;
+ }
+ auth_error = radix_encode(send_tok.value, out_buf,
+ outlen, &len, 0);
+
+ if (auth_error) {
+ (void) fprintf(stderr, "Base 64 encoding failed: %s\n",
+ radix_error(auth_error));
+ } else if ((comcode = command("ADAT %s", out_buf))
+ != COMPLETE /* && comcode != 3 (335)*/) {
+
+ if (trial == n_gss_trials-1) {
+ (void) fprintf(stderr, "GSSAPI ADAT failed (%d)\n",
+ comcode);
+
+ /* force out of loop */
+ maj_stat = GSS_S_FAILURE;
+ }
+
+ /*
+ * backoff to the v1 gssapi is still possible.
+ * Send a new AUTH command. If that fails,
+ * terminate the loop
+ */
+ if (command("AUTH %s", "GSSAPI") != CONTINUE) {
+ (void) fprintf(stderr,
+ "GSSAPI ADAT failed, AUTH restart failed\n");
+ /* force out of loop */
+ maj_stat = GSS_S_FAILURE;
+ }
+
+ goto outer_loop;
+ } else if (!reply_parse) {
+ (void) fprintf(stderr,
+ "No authentication data received from server\n");
+ if (maj_stat == GSS_S_COMPLETE) {
+ (void) fprintf(stderr,
+ "...but no more was needed\n");
+ goto gss_complete_loop;
+ } else {
+ user_gss_error(maj_stat, min_stat, "no reply.");
+ goto gss_complete_loop;
+ }
+ } else if (auth_error = radix_encode((uchar_t *)
+ reply_parse, out_buf, outlen, &i, 1)) {
+ (void) fprintf(stderr,
+ "Base 64 decoding failed: %s\n",
+ radix_error(auth_error));
+ } else {
+ /* everything worked */
+ token_ptr = &recv_tok;
+ recv_tok.value = out_buf;
+ recv_tok.length = i;
+ continue;
+ } /* end if (auth_error) */
+
+/* get out of loop clean */
+gss_complete_loop:
+ trial = n_gss_trials-1;
+ gss_release_buffer(&min_stat, &send_tok);
+ gss_release_name(&min_stat, &target_name);
+ goto outer_loop;
+ } /* end if (send_tok.length != 0) */
+
+ } while (maj_stat == GSS_S_CONTINUE_NEEDED);
+
+outer_loop:
+ if (maj_stat == GSS_S_COMPLETE)
+ break;
+
+ } /* end for loop */
+
+ verbose = oldverbose;
+ if (out_buf != NULL)
+ free(out_buf);
+
+ if (maj_stat == GSS_S_COMPLETE) {
+ (void) printf("GSSAPI authentication succeeded\n");
+ reply_parse = NULL;
+ auth_type = AUTHTYPE_GSSAPI;
+ return (1);
+ } else {
+ (void) fprintf(stderr, "GSSAPI authentication failed\n");
+ reply_parse = NULL;
+ }
+ } /* end if (command...) */
+
+ /* Other auth types go here ... */
+
+ return (0);
+}
+
+/*
+ * Get the information for the channel structure.
+ */
+void
+get_inet_addr_info(struct sockaddr_in6 *in_ipaddr, gss_buffer_t in_buffer)
+{
+ size_t length;
+ char *value;
+
+ if (in_ipaddr == NULL) {
+ in_buffer->length = 0;
+ in_buffer->value = NULL;
+ return;
+ }
+
+ /* get the initiator address.value and address.length */
+
+ if (in_ipaddr->sin6_family == AF_INET6) {
+ struct in_addr in_ipv4addr;
+ struct sockaddr_in6 *sin6 =
+ (struct sockaddr_in6 *)in_ipaddr;
+ if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
+ IN6_V4MAPPED_TO_INADDR(&sin6->sin6_addr,
+ &in_ipv4addr);
+ in_buffer->length = length = sizeof (struct in_addr);
+ in_buffer->value = value = malloc(length);
+ memcpy(value, &in_ipv4addr, length);
+ } else {
+ in_buffer->length = length = sizeof (struct in6_addr);
+ in_buffer->value = value = malloc(length);
+ memcpy(value, &(sin6->sin6_addr.s6_addr),
+ length);
+ }
+ } else {
+ in_buffer->length = length = sizeof (struct in_addr);
+ in_buffer->value = value = malloc(in_buffer->length);
+ memcpy(value,
+ &((struct sockaddr_in *)(in_ipaddr))->sin_addr,
+ length);
+ }
+}
+
+int
+radix_encode(uchar_t *inbuf, uchar_t *outbuf, size_t buflen,
+ int *outlen, int decode)
+{
+ int i, j, D;
+ char *p;
+ uchar_t c;
+
+ if (decode) {
+ for (i = j = 0;
+ inbuf[i] && inbuf[i] != radix_pad && (j < buflen);
+ i++) {
+ if ((p = strchr(radixN, inbuf[i])) == NULL)
+ return (1);
+ D = p - radixN;
+ switch (i&3) {
+ case 0:
+ outbuf[j] = D<<2;
+ break;
+ case 1:
+ outbuf[j++] |= D>>4;
+ outbuf[j] = (D&15)<<4;
+ break;
+ case 2:
+ outbuf[j++] |= D>>2;
+ outbuf[j] = (D&3)<<6;
+ break;
+ case 3:
+ outbuf[j++] |= D;
+ }
+ }
+ if (j == buflen && (inbuf[i] && inbuf[i] != radix_pad)) {
+ return (4);
+ }
+ switch (i&3) {
+ case 1: return (3);
+ case 2: if (D&15)
+ return (3);
+ if (strcmp((char *)&inbuf[i], "=="))
+ return (2);
+ break;
+ case 3: if (D&3)
+ return (3);
+ if (strcmp((char *)&inbuf[i], "="))
+ return (2);
+ }
+ *outlen = j;
+ } else {
+ for (i = j = 0; i < *outlen && j < buflen; i++)
+ switch (i%3) {
+ case 0:
+ outbuf[j++] = radixN[inbuf[i]>>2];
+ c = (inbuf[i]&3)<<4;
+ break;
+ case 1:
+ outbuf[j++] = radixN[c|inbuf[i]>>4];
+ c = (inbuf[i]&15)<<2;
+ break;
+ case 2:
+ outbuf[j++] = radixN[c|inbuf[i]>>6];
+ outbuf[j++] = radixN[inbuf[i]&63];
+ c = 0;
+ }
+ if (j == buflen && i < *outlen) {
+ return (4);
+ }
+ if (i%3)
+ outbuf[j++] = radixN[c];
+ switch (i%3) {
+ case 1:
+ outbuf[j++] = radix_pad;
+ /* FALLTHROUGH */
+ case 2:
+ outbuf[j++] = radix_pad;
+ break;
+ }
+ outbuf[*outlen = j] = '\0';
+ }
+ return (0);
+}
+
+char *
+radix_error(int e)
+{
+ switch (e) {
+ case 0: return ("Success");
+ case 1: return ("Bad character in encoding");
+ case 2: return ("Encoding not properly padded");
+ case 3: return ("Decoded # of bits not a multiple of 8");
+ case 4: return ("Buffer size error");
+ default: return ("Unknown error");
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/ftp/cmds.c b/usr/src/cmd/cmd-inet/usr.bin/ftp/cmds.c
new file mode 100644
index 0000000000..3b77f44b63
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/ftp/cmds.c
@@ -0,0 +1,2445 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+
+/*
+ * FTP User Program -- Command Routines.
+ */
+#define FTP_NAMES
+#include "ftp_var.h"
+
+FILE *tmp_nlst = NULL; /* tmp file; holds NLST results for mget, etc */
+
+static char *mname;
+static jmp_buf jabort;
+static jmp_buf abortprox;
+
+static char *remglob(char *argv[], int doswitch);
+static char *onoff(int bool);
+static int confirm(char *cmd, char *file);
+static int globulize(char **cpp);
+static void proxabort(int sig);
+static void mabort(int sig);
+static char *dotrans(char *name);
+static char *domap(char *name);
+static void getit(int argc, char *argv[], int restartit, char *mode);
+
+static char *getlevel(int);
+
+/* Prompt for command argument, add to buffer with space separator */
+static int
+prompt_for_arg(char *buffer, int buffer_size, char *prompt)
+{
+ if (strlen(buffer) > buffer_size - 2) {
+ (void) printf("Line too long\n");
+ return (-1);
+ }
+ strcat(buffer, " ");
+ stop_timer();
+ (void) printf("(%s) ", prompt);
+ if (fgets(buffer + strlen(buffer), buffer_size - strlen(buffer), stdin)
+ == NULL) {
+ reset_timer();
+ return (-1);
+ }
+
+ /* Flush what didn't fit in the buffer */
+ if (buffer[strlen(buffer)-1] != '\n') {
+ while (fgetc(stdin) != '\n' && !ferror(stdin) && !feof(stdin))
+ ;
+ (void) printf("Line too long\n");
+ reset_timer();
+ return (-1);
+ } else
+ buffer[strlen(buffer)-1] = 0;
+
+ reset_timer();
+ return (0);
+}
+
+
+/*
+ * Connect to peer server and
+ * auto-login, if possible.
+ */
+void
+setpeer(int argc, char *argv[])
+{
+ char *host;
+
+ if (connected) {
+ (void) printf("Already connected to %s, use close first.\n",
+ hostname);
+ code = -1;
+ return;
+ }
+ if (argc < 2) {
+ if (prompt_for_arg(line, sizeof (line), "to") == -1) {
+ code = -1;
+ return;
+ }
+ makeargv();
+ argc = margc;
+ argv = margv;
+ }
+ if (argc > 3 || argc < 2) {
+ (void) printf("usage: %s host-name [port]\n", argv[0]);
+ code = -1;
+ return;
+ }
+ strcpy(typename, "ascii");
+ host = hookup(argv[1], (argc > 2 ? argv[2] : "ftp"));
+ if (host) {
+ int overbose;
+ extern char reply_string[];
+
+ connected = 1;
+ /*
+ * Set up defaults for FTP.
+ */
+ clevel = dlevel = PROT_C;
+ if (autoauth) {
+ if (do_auth() && autoencrypt) {
+ clevel = PROT_P;
+ setpbsz(1<<20);
+ if (command("PROT P") == COMPLETE)
+ dlevel = PROT_P;
+ else {
+ (void) fprintf(stderr,
+ "%s: couldn't enable encryption\n",
+ argv[0]);
+ /* unable to encrypt command channel, too! */
+ dlevel = clevel = PROT_C;
+ }
+ }
+ if ((auth_type != AUTHTYPE_NONE) && (clevel == PROT_C))
+ clevel = PROT_S;
+ }
+
+ if (autologin)
+ (void) login(argv[1]);
+
+ overbose = verbose;
+ if (debug == 0)
+ verbose = -1;
+ if (command("SYST") == COMPLETE && overbose) {
+ char *cp, c;
+
+ cp = index(reply_string+4, ' ');
+ if (cp == NULL)
+ cp = index(reply_string+4, '\r');
+ if (cp) {
+ if (cp[-1] == '.')
+ cp--;
+ c = *cp;
+ *cp = '\0';
+ }
+
+ (void) printf("Remote system type is %s.\n",
+ reply_string+4);
+ if (cp)
+ *cp = c;
+ }
+ if (strncmp(reply_string, "215 UNIX Type: L8", 17) == 0) {
+ setbinary(0, NULL);
+ if (overbose)
+ (void) printf(
+ "Using %s mode to transfer files.\n",
+ typename);
+ } else if (overbose &&
+ strncmp(reply_string, "215 TOPS20", 10) == 0) {
+ (void) printf(
+ "Remember to set tenex mode when transfering "
+ "binary files from this machine.\n");
+ }
+ verbose = overbose;
+ }
+}
+
+static struct types {
+ char *t_name;
+ char *t_mode;
+ int t_type;
+ char *t_arg;
+} types[] = {
+ { "ascii", "A", TYPE_A, 0 },
+ { "binary", "I", TYPE_I, 0 },
+ { "image", "I", TYPE_I, 0 },
+ { "ebcdic", "E", TYPE_E, 0 },
+ { "tenex", "L", TYPE_L, bytename },
+ 0
+};
+
+/*
+ * Set transfer type.
+ */
+void
+settype(int argc, char *argv[])
+{
+ struct types *p;
+ int comret;
+
+ if (argc > 2) {
+ char *sep;
+
+ (void) printf("usage: %s [", argv[0]);
+ sep = " ";
+ for (p = types; p->t_name; p++) {
+ (void) printf("%s%s", sep, p->t_name);
+ if (*sep == ' ')
+ sep = " | ";
+ }
+ (void) printf(" ]\n");
+ code = -1;
+ return;
+ }
+ if (argc < 2) {
+ (void) printf("Using %s mode to transfer files.\n", typename);
+ code = 0;
+ return;
+ }
+ for (p = types; p->t_name; p++)
+ if (strcmp(argv[1], p->t_name) == 0)
+ break;
+ if (p->t_name == 0) {
+ (void) printf("%s: unknown mode\n", argv[1]);
+ code = -1;
+ return;
+ }
+ if ((p->t_arg != NULL) && (*(p->t_arg) != '\0'))
+ comret = command("TYPE %s %s", p->t_mode, p->t_arg);
+ else
+ comret = command("TYPE %s", p->t_mode);
+ if (comret == COMPLETE) {
+ (void) strcpy(typename, p->t_name);
+ type = p->t_type;
+ }
+}
+
+/*
+ * Set binary transfer type.
+ */
+/*ARGSUSED*/
+void
+setbinary(int argc, char *argv[])
+{
+ call(settype, "type", "binary", 0);
+}
+
+/*
+ * Set ascii transfer type.
+ */
+/*ARGSUSED*/
+void
+setascii(int argc, char *argv[])
+{
+ call(settype, "type", "ascii", 0);
+}
+
+/*
+ * Set tenex transfer type.
+ */
+/*ARGSUSED*/
+void
+settenex(int argc, char *argv[])
+{
+ call(settype, "type", "tenex", 0);
+}
+
+/*
+ * Set ebcdic transfer type.
+ */
+/*ARGSUSED*/
+void
+setebcdic(int argc, char *argv[])
+{
+ call(settype, "type", "ebcdic", 0);
+}
+
+/*
+ * Set file transfer mode.
+ */
+/*ARGSUSED*/
+void
+setmode(int argc, char *argv[])
+{
+ (void) printf("We only support %s mode, sorry.\n", modename);
+ code = -1;
+}
+
+/*
+ * Set file transfer format.
+ */
+/*ARGSUSED*/
+void
+setform(int argc, char *argv[])
+{
+ (void) printf("We only support %s format, sorry.\n", formname);
+ code = -1;
+}
+
+/*
+ * Set file transfer structure.
+ */
+/*ARGSUSED*/
+void
+setstruct(int argc, char *argv[])
+{
+
+ (void) printf("We only support %s structure, sorry.\n", structname);
+ code = -1;
+}
+
+/*
+ * Send a single file.
+ */
+void
+put(int argc, char *argv[])
+{
+ char *cmd;
+ int loc = 0;
+ char *oldargv1;
+
+ if (argc == 2) {
+ argc++;
+ argv[2] = argv[1];
+ loc++;
+ }
+ if (argc < 2) {
+ if (prompt_for_arg(line, sizeof (line), "local-file") == -1) {
+ code = -1;
+ return;
+ }
+ makeargv();
+ argc = margc;
+ argv = margv;
+ }
+ if (argc < 2) {
+usage:
+ (void) printf("usage: %s local-file remote-file\n", argv[0]);
+ code = -1;
+ return;
+ }
+ if (argc < 3) {
+ if (prompt_for_arg(line, sizeof (line), "remote-file") == -1) {
+ code = -1;
+ return;
+ }
+ makeargv();
+ argc = margc;
+ argv = margv;
+ }
+ if (argc < 3)
+ goto usage;
+ oldargv1 = argv[1];
+ if (!globulize(&argv[1])) {
+ code = -1;
+ return;
+ }
+ /*
+ * If "globulize" modifies argv[1], and argv[2] is a copy of
+ * the old argv[1], make it a copy of the new argv[1].
+ */
+ if (argv[1] != oldargv1 && argv[2] == oldargv1) {
+ argv[2] = argv[1];
+ }
+ cmd = (argv[0][0] == 'a') ? "APPE" : ((sunique) ? "STOU" : "STOR");
+ if (loc && ntflag) {
+ argv[2] = dotrans(argv[2]);
+ }
+ if (loc && mapflag) {
+ argv[2] = domap(argv[2]);
+ }
+ sendrequest(cmd, argv[1], argv[2], 1);
+}
+
+/*ARGSUSED*/
+static void
+mabort(int sig)
+{
+ int ointer;
+
+ (void) printf("\n");
+ (void) fflush(stdout);
+ if (mflag && fromatty) {
+ ointer = interactive;
+ interactive = 1;
+ if (confirm("Continue with", mname)) {
+ interactive = ointer;
+ longjmp(jabort, 0);
+ }
+ interactive = ointer;
+ }
+ mflag = 0;
+ longjmp(jabort, 0);
+}
+
+/*
+ * Send multiple files.
+ */
+void
+mput(int argc, char *argv[])
+{
+ int i;
+ int ointer;
+ void (*oldintr)();
+ char *tp;
+ int len;
+
+ if (argc < 2) {
+ if (prompt_for_arg(line, sizeof (line), "local-files") == -1) {
+ code = -1;
+ return;
+ }
+ makeargv();
+ argc = margc;
+ argv = margv;
+ }
+ if (argc < 2) {
+ (void) printf("usage: %s local-files\n", argv[0]);
+ code = -1;
+ return;
+ }
+ mname = argv[0];
+ mflag = 1;
+ oldintr = signal(SIGINT, mabort);
+ (void) setjmp(jabort);
+ if (proxy) {
+ char *cp, *tp2, tmpbuf[MAXPATHLEN];
+
+ while ((cp = remglob(argv, 0)) != NULL) {
+ if (*cp == 0) {
+ mflag = 0;
+ continue;
+ }
+ if (mflag && confirm(argv[0], cp)) {
+ tp = cp;
+ if (mcase) {
+ while (*tp) {
+ if ((len =
+ mblen(tp, MB_CUR_MAX)) <= 0)
+ len = 1;
+ if (islower(*tp))
+ break;
+ tp += len;
+ }
+ if (!*tp) {
+ tp = cp;
+ tp2 = tmpbuf;
+ while (*tp) {
+ if ((len = mblen(tp,
+ MB_CUR_MAX)) <= 0)
+ len = 1;
+ memcpy(tp2, tp, len);
+ if (isupper(*tp2)) {
+ *tp2 = 'a' +
+ *tp2 - 'A';
+ }
+ tp += len;
+ tp2 += len;
+ }
+ *tp2 = 0;
+ tp = tmpbuf;
+ }
+ }
+ if (ntflag) {
+ tp = dotrans(tp);
+ }
+ if (mapflag) {
+ tp = domap(tp);
+ }
+ sendrequest((sunique) ? "STOU" : "STOR",
+ cp, tp, 0);
+ if (!mflag && fromatty) {
+ ointer = interactive;
+ interactive = 1;
+ if (confirm("Continue with", "mput")) {
+ mflag++;
+ }
+ interactive = ointer;
+ }
+ }
+ }
+ (void) signal(SIGINT, oldintr);
+ mflag = 0;
+ return;
+ }
+ for (i = 1; i < argc; i++) {
+ char **cpp, **gargs;
+
+ if (!doglob) {
+ if (mflag && confirm(argv[0], argv[i])) {
+ tp = (ntflag) ? dotrans(argv[i]) : argv[i];
+ tp = (mapflag) ? domap(tp) : tp;
+ sendrequest((sunique) ? "STOU" : "STOR",
+ argv[i], tp, 1);
+ if (!mflag && fromatty) {
+ ointer = interactive;
+ interactive = 1;
+ if (confirm("Continue with", "mput")) {
+ mflag++;
+ }
+ interactive = ointer;
+ }
+ }
+ continue;
+ }
+ gargs = glob(argv[i]);
+ if (globerr != NULL) {
+ (void) printf("%s\n", globerr);
+ if (gargs)
+ blkfree(gargs);
+ continue;
+ }
+ for (cpp = gargs; cpp && *cpp != NULL; cpp++) {
+ if (mflag && confirm(argv[0], *cpp)) {
+ tp = (ntflag) ? dotrans(*cpp) : *cpp;
+ tp = (mapflag) ? domap(tp) : tp;
+ sendrequest((sunique) ? "STOU" : "STOR",
+ *cpp, tp, 0);
+ if (!mflag && fromatty) {
+ ointer = interactive;
+ interactive = 1;
+ if (confirm("Continue with", "mput")) {
+ mflag++;
+ }
+ interactive = ointer;
+ }
+ }
+ }
+ if (gargs != NULL)
+ blkfree(gargs);
+ }
+ (void) signal(SIGINT, oldintr);
+ mflag = 0;
+}
+
+/*
+ * Restart transfer at a specific offset.
+ */
+void
+restart(int argc, char *argv[])
+{
+ off_t orestart_point = restart_point;
+
+ if (argc > 2) {
+ (void) printf("usage: %s [marker]\n", argv[0]);
+ code = -1;
+ return;
+ }
+ if (argc == 2) {
+ longlong_t rp;
+ char *endp;
+
+ errno = 0;
+ rp = strtoll(argv[1], &endp, 10);
+ if (errno || rp < 0 || *endp != '\0')
+ (void) printf("%s: Invalid offset `%s'\n",
+ argv[0], argv[1]);
+ else
+ restart_point = rp;
+ }
+ if (restart_point == 0) {
+ if (orestart_point == 0)
+ (void) printf("No restart marker defined\n");
+ else
+ (void) printf("Restart marker cleared\n");
+ } else
+ (void) printf(
+ "Restarting at %lld for next get, put or append\n",
+ (longlong_t)restart_point);
+}
+
+void
+reget(int argc, char *argv[])
+{
+ getit(argc, argv, 1, "r+w");
+}
+
+void
+get(int argc, char *argv[])
+{
+ getit(argc, argv, 0, restart_point ? "r+w" : "w");
+}
+
+/*
+ * Receive one file.
+ */
+static void
+getit(int argc, char *argv[], int restartit, char *mode)
+{
+ int loc = 0;
+ int len;
+ int allowpipe = 1;
+
+ if (argc == 2) {
+ argc++;
+ argv[2] = argv[1];
+ /* Only permit !file if two arguments. */
+ allowpipe = 0;
+ loc++;
+ }
+ if (argc < 2) {
+ if (prompt_for_arg(line, sizeof (line), "remote-file") == -1) {
+ code = -1;
+ return;
+ }
+ makeargv();
+ argc = margc;
+ argv = margv;
+ }
+ if (argc < 2) {
+usage:
+ (void) printf("usage: %s remote-file [ local-file ]\n",
+ argv[0]);
+ code = -1;
+ return;
+ }
+ if (argc < 3) {
+ if (prompt_for_arg(line, sizeof (line), "local-file") == -1) {
+ code = -1;
+ return;
+ }
+ makeargv();
+ argc = margc;
+ argv = margv;
+ }
+ if (argc < 3)
+ goto usage;
+ if (!globulize(&argv[2])) {
+ code = -1;
+ return;
+ }
+ if (loc && mcase) {
+ char *tp = argv[1], *tp2, tmpbuf[MAXPATHLEN];
+
+ while (*tp) {
+ if ((len = mblen(tp, MB_CUR_MAX)) <= 0)
+ len = 1;
+ if (islower(*tp))
+ break;
+ tp += len;
+ }
+ if (!*tp) {
+ tp = argv[2];
+ tp2 = tmpbuf;
+ while (*tp) {
+ if ((len = mblen(tp, MB_CUR_MAX)) <= 0)
+ len = 1;
+ memcpy(tp2, tp, len);
+ if (isupper(*tp2))
+ *tp2 = 'a' + *tp2 - 'A';
+ tp += len;
+ tp2 += len;
+ }
+ *tp2 = 0;
+ argv[2] = tmpbuf;
+ }
+ }
+ if (loc && ntflag) {
+ argv[2] = dotrans(argv[2]);
+ }
+ if (loc && mapflag) {
+ argv[2] = domap(argv[2]);
+ }
+ if (restartit) {
+ struct stat stbuf;
+
+ if (stat(argv[2], &stbuf) < 0) {
+ perror(argv[2]);
+ code = -1;
+ return;
+ }
+ restart_point = stbuf.st_size;
+ }
+ recvrequest("RETR", argv[2], argv[1], mode, allowpipe);
+ restart_point = 0;
+}
+
+/*
+ * Get multiple files.
+ */
+void
+mget(int argc, char *argv[])
+{
+ char *cp, *tp, *tp2, tmpbuf[MAXPATHLEN];
+ int ointer;
+ void (*oldintr)();
+ int need_convert;
+ int len;
+
+ if (argc < 2) {
+ if (prompt_for_arg(line, sizeof (line), "remote-files") < 0) {
+ code = -1;
+ return;
+ }
+ makeargv();
+ argc = margc;
+ argv = margv;
+ }
+ if (argc < 2) {
+ (void) printf("usage: %s remote-files\n", argv[0]);
+ code = -1;
+ return;
+ }
+ mname = argv[0];
+ mflag = 1;
+ oldintr = signal(SIGINT, mabort);
+ (void) setjmp(jabort);
+ while ((cp = remglob(argv, proxy)) != NULL) {
+ if (*cp == '\0') {
+ mflag = 0;
+ continue;
+ }
+ if (mflag && confirm(argv[0], cp)) {
+ strcpy(tmpbuf, cp);
+ tp = tmpbuf;
+ need_convert = 1;
+ if (mcase) {
+ tp2 = tp;
+ while (*tp2 && need_convert) {
+ /* Need any case convert? */
+ if (islower(*tp2))
+ need_convert = 0;
+ if ((len = mblen(tp2, MB_CUR_MAX)) <= 0)
+ len = 1;
+ tp2 += len;
+ }
+ tp2 = tp;
+ while (need_convert && *tp2) {
+ /* Convert to lower case */
+ if (isupper(*tp2))
+ *tp2 = tolower(*tp2);
+ if ((len = mblen(tp2, MB_CUR_MAX)) <= 0)
+ len = 1;
+ tp2 += len;
+ }
+ }
+
+ if (ntflag) {
+ tp = dotrans(tp);
+ }
+ if (mapflag) {
+ tp = domap(tp);
+ }
+ recvrequest("RETR", tp, cp, "w", 0);
+ restart_point = 0;
+ if (!mflag && fromatty) {
+ ointer = interactive;
+ interactive = 1;
+ if (confirm("Continue with", "mget")) {
+ mflag++;
+ }
+ interactive = ointer;
+ }
+ }
+ }
+ (void) signal(SIGINT, oldintr);
+ mflag = 0;
+}
+
+static char *
+remglob(char *argv[], int doswitch)
+{
+ static char buf[MAXPATHLEN];
+ static char **args;
+ int oldverbose, oldhash;
+ char *cp;
+
+ if (!mflag) {
+ if (!doglob) {
+ args = NULL;
+ } else {
+ if (tmp_nlst != NULL) {
+ (void) fclose(tmp_nlst);
+ tmp_nlst = NULL;
+ }
+ }
+ return (NULL);
+ }
+ if (!doglob) {
+ if (args == NULL)
+ args = argv;
+ if ((cp = *++args) == NULL)
+ args = NULL;
+ return (cp);
+ }
+ if (tmp_nlst == NULL) {
+ if ((tmp_nlst = tmpfile()) == NULL) {
+ (void) printf("%s\n", strerror(errno));
+ return (NULL);
+ }
+ oldverbose = verbose, verbose = 0;
+ oldhash = hash, hash = 0;
+ if (doswitch) {
+ pswitch(!proxy);
+ }
+ for (; *++argv != NULL; )
+ recvrequest("NLST", NULL, *argv, "", 0);
+ rewind(tmp_nlst);
+ if (doswitch) {
+ pswitch(!proxy);
+ }
+ verbose = oldverbose; hash = oldhash;
+ }
+ reset_timer();
+ if (fgets(buf, sizeof (buf), tmp_nlst) == NULL) {
+ (void) fclose(tmp_nlst), tmp_nlst = NULL;
+ return (NULL);
+ }
+ if ((cp = index(buf, '\n')) != NULL)
+ *cp = '\0';
+ return (buf);
+}
+
+static char *
+onoff(int bool)
+{
+ return (bool ? "on" : "off");
+}
+
+/*
+ * Show status.
+ */
+/*ARGSUSED*/
+void
+status(int argc, char *argv[])
+{
+ int i;
+ char *levelp;
+
+ if (connected)
+ (void) printf("Connected to %s.\n", hostname);
+ else
+ (void) printf("Not connected.\n");
+ if (!proxy) {
+ pswitch(1);
+ if (connected) {
+ (void) printf("Connected for proxy commands to %s.\n",
+ hostname);
+ } else {
+ (void) printf("No proxy connection.\n");
+ }
+ pswitch(0);
+ }
+
+ if (auth_type != AUTHTYPE_NONE)
+ (void) printf("Authentication type: %s\n",
+ GSS_AUTHTYPE_NAME(auth_type));
+ else
+ (void) printf("Not authenticated.\n");
+ (void) printf("Mechanism: %s\n", mechstr);
+ (void) printf("Autoauth: %s; Autologin: %s\n",
+ onoff(autoauth), onoff(autologin));
+ levelp = getlevel(clevel);
+ (void) printf("Control Channel Protection Level: %s\n",
+ levelp ? levelp : "<unknown>");
+ levelp = getlevel(dlevel);
+ (void) printf("Data Channel Protection Level: %s\n",
+ levelp ? levelp : "<unknown>");
+
+ (void) printf("Passive mode: %s.\n", onoff(passivemode));
+ (void) printf("Mode: %s; Type: %s; Form: %s; Structure: %s\n",
+ modename, typename, formname, structname);
+ (void) printf("Verbose: %s; Bell: %s; Prompting: %s; Globbing: %s\n",
+ onoff(verbose), onoff(bell), onoff(interactive),
+ onoff(doglob));
+ (void) printf("Store unique: %s; Receive unique: %s\n", onoff(sunique),
+ onoff(runique));
+ (void) printf("Case: %s; CR stripping: %s\n",
+ onoff(mcase), onoff(crflag));
+ if (ntflag) {
+ (void) printf("Ntrans: (in) %s (out) %s\n", ntin, ntout);
+ } else {
+ (void) printf("Ntrans: off\n");
+ }
+ if (mapflag) {
+ (void) printf("Nmap: (in) %s (out) %s\n", mapin, mapout);
+ } else {
+ (void) printf("Nmap: off\n");
+ }
+ (void) printf("Hash mark printing: %s; Use of PORT cmds: %s\n",
+ onoff(hash), onoff(sendport));
+ if (macnum > 0) {
+ (void) printf("Macros:\n");
+ for (i = 0; i < macnum; i++) {
+ (void) printf("\t%s\n", macros[i].mac_name);
+ }
+ }
+ code = 0;
+}
+
+/*
+ * Set beep on cmd completed mode.
+ */
+/*ARGSUSED*/
+void
+setbell(int argc, char *argv[])
+{
+ bell = !bell;
+ (void) printf("Bell mode %s.\n", onoff(bell));
+ code = bell;
+}
+
+/*
+ * Turn on packet tracing.
+ */
+/*ARGSUSED*/
+void
+settrace(int argc, char *argv[])
+{
+ trace = !trace;
+ (void) printf("Packet tracing %s.\n", onoff(trace));
+ code = trace;
+}
+
+/*
+ * Toggle hash mark printing during transfers.
+ */
+/*ARGSUSED*/
+void
+sethash(int argc, char *argv[])
+{
+ hash = !hash;
+ (void) printf("Hash mark printing %s", onoff(hash));
+ code = hash;
+ if (hash)
+ (void) printf(" (%d bytes/hash mark)", HASHSIZ);
+ (void) printf(".\n");
+}
+
+/*
+ * Turn on printing of server echo's.
+ */
+/*ARGSUSED*/
+void
+setverbose(int argc, char *argv[])
+{
+ verbose = !verbose;
+ (void) printf("Verbose mode %s.\n", onoff(verbose));
+ code = verbose;
+}
+
+/*
+ * Toggle PORT cmd use before each data connection.
+ */
+/*ARGSUSED*/
+void
+setport(int argc, char *argv[])
+{
+ sendport = !sendport;
+ (void) printf("Use of PORT cmds %s.\n", onoff(sendport));
+ code = sendport;
+}
+
+/*
+ * Turn on interactive prompting
+ * during mget, mput, and mdelete.
+ */
+/*ARGSUSED*/
+void
+setprompt(int argc, char *argv[])
+{
+ interactive = !interactive;
+ (void) printf("Interactive mode %s.\n", onoff(interactive));
+ code = interactive;
+}
+
+/*
+ * Toggle metacharacter interpretation
+ * on local file names.
+ */
+/*ARGSUSED*/
+void
+setglob(int argc, char *argv[])
+{
+ doglob = !doglob;
+ (void) printf("Globbing %s.\n", onoff(doglob));
+ code = doglob;
+}
+
+/*
+ * Set debugging mode on/off and/or
+ * set level of debugging.
+ */
+void
+setdebug(int argc, char *argv[])
+{
+ int val;
+
+ if (argc > 1) {
+ val = atoi(argv[1]);
+ if (val < 0) {
+ (void) printf("%s: bad debugging value.\n", argv[1]);
+ code = -1;
+ return;
+ }
+ } else
+ val = !debug;
+ debug = val;
+ if (debug)
+ options |= SO_DEBUG;
+ else
+ options &= ~SO_DEBUG;
+ (void) printf("Debugging %s (debug=%d).\n", onoff(debug), debug);
+ code = debug > 0;
+}
+
+/*
+ * Set current working directory
+ * on remote machine.
+ */
+void
+cd(int argc, char *argv[])
+{
+ if (argc < 2) {
+ if (prompt_for_arg(line, sizeof (line), "remote-directory") <
+ 0) {
+ code = -1;
+ return;
+ }
+ makeargv();
+ argc = margc;
+ argv = margv;
+ }
+ if (argc < 2) {
+ (void) printf("usage: %s remote-directory\n", argv[0]);
+ code = -1;
+ return;
+ }
+ (void) command("CWD %s", argv[1]);
+}
+
+/*
+ * Set current working directory
+ * on local machine.
+ */
+void
+lcd(int argc, char *argv[])
+{
+ char buf[MAXPATHLEN], *bufptr;
+
+ if (argc < 2)
+ argc++, argv[1] = home;
+ if (argc != 2) {
+ (void) printf("usage: %s local-directory\n", argv[0]);
+ code = -1;
+ return;
+ }
+ if (!globulize(&argv[1])) {
+ code = -1;
+ return;
+ }
+ if (chdir(argv[1]) < 0) {
+ perror(argv[1]);
+ code = -1;
+ return;
+ }
+ bufptr = getcwd(buf, MAXPATHLEN);
+ /*
+ * Even though chdir may succeed, getcwd may fail if a component
+ * of the pwd is unreadable. In this case, print the argument to
+ * chdir as the resultant directory, since we know it succeeded above.
+ */
+ (void) printf("Local directory now %s\n", (bufptr ? bufptr : argv[1]));
+ code = 0;
+}
+
+/*
+ * Delete a single file.
+ */
+void
+delete(int argc, char *argv[])
+{
+
+ if (argc < 2) {
+ if (prompt_for_arg(line, sizeof (line), "remote-file") < 0) {
+ code = -1;
+ return;
+ }
+ makeargv();
+ argc = margc;
+ argv = margv;
+ }
+ if (argc < 2) {
+ (void) printf("usage: %s remote-file\n", argv[0]);
+ code = -1;
+ return;
+ }
+ (void) command("DELE %s", argv[1]);
+}
+
+/*
+ * Delete multiple files.
+ */
+void
+mdelete(int argc, char *argv[])
+{
+ char *cp;
+ int ointer;
+ void (*oldintr)();
+
+ if (argc < 2) {
+ if (prompt_for_arg(line, sizeof (line), "remote-files") < 0) {
+ code = -1;
+ return;
+ }
+ makeargv();
+ argc = margc;
+ argv = margv;
+ }
+ if (argc < 2) {
+ (void) printf("usage: %s remote-files\n", argv[0]);
+ code = -1;
+ return;
+ }
+ mname = argv[0];
+ mflag = 1;
+ oldintr = signal(SIGINT, mabort);
+ (void) setjmp(jabort);
+ while ((cp = remglob(argv, 0)) != NULL) {
+ if (*cp == '\0') {
+ mflag = 0;
+ continue;
+ }
+ if (mflag && confirm(argv[0], cp)) {
+ (void) command("DELE %s", cp);
+ if (!mflag && fromatty) {
+ ointer = interactive;
+ interactive = 1;
+ if (confirm("Continue with", "mdelete")) {
+ mflag++;
+ }
+ interactive = ointer;
+ }
+ }
+ }
+ (void) signal(SIGINT, oldintr);
+ mflag = 0;
+}
+
+/*
+ * Rename a remote file.
+ */
+void
+renamefile(int argc, char *argv[])
+{
+
+ if (argc < 2) {
+ if (prompt_for_arg(line, sizeof (line), "from-name") < 0) {
+ code = -1;
+ return;
+ }
+ makeargv();
+ argc = margc;
+ argv = margv;
+ }
+ if (argc < 2) {
+usage:
+ (void) printf("%s from-name to-name\n", argv[0]);
+ code = -1;
+ return;
+ }
+ if (argc < 3) {
+ if (prompt_for_arg(line, sizeof (line), "to-name") < 0) {
+ code = -1;
+ return;
+ }
+ makeargv();
+ argc = margc;
+ argv = margv;
+ }
+ if (argc < 3)
+ goto usage;
+ if (command("RNFR %s", argv[1]) == CONTINUE)
+ (void) command("RNTO %s", argv[2]);
+}
+
+/*
+ * Get a directory listing
+ * of remote files.
+ */
+void
+ls(int argc, char *argv[])
+{
+ char *cmd;
+
+ if (argc < 2)
+ argc++, argv[1] = NULL;
+ if (argc < 3)
+ argc++, argv[2] = "-";
+ if (argc > 3) {
+ (void) printf("usage: %s remote-directory local-file\n",
+ argv[0]);
+ code = -1;
+ return;
+ }
+ if (ls_invokes_NLST) {
+ cmd = ((argv[0][0] == 'l' || argv[0][0] == 'n') ?
+ "NLST" : "LIST");
+ } else {
+ cmd = ((argv[0][0] == 'n') ? "NLST" : "LIST");
+ }
+ if (strcmp(argv[2], "-") && !globulize(&argv[2])) {
+ code = -1;
+ return;
+ }
+ recvrequest(cmd, argv[2], argv[1], "w", 1);
+}
+
+/*
+ * Get a directory listing
+ * of multiple remote files.
+ */
+void
+mls(int argc, char *argv[])
+{
+ char *cmd, mode[1], *dest;
+ int ointer, i;
+ void (*oldintr)();
+
+ if (argc < 2) {
+ if (prompt_for_arg(line, sizeof (line), "remote-files") < 0) {
+ code = -1;
+ return;
+ }
+ makeargv();
+ argc = margc;
+ argv = margv;
+ }
+ if (argc < 3) {
+ if (prompt_for_arg(line, sizeof (line), "local-file") < 0) {
+ code = -1;
+ return;
+ }
+ makeargv();
+ argc = margc;
+ argv = margv;
+ }
+ if (argc < 3) {
+ (void) printf("usage: %s remote-files local-file\n", argv[0]);
+ code = -1;
+ return;
+ }
+ dest = argv[argc - 1];
+ argv[argc - 1] = NULL;
+ if (strcmp(dest, "-") && *dest != '|')
+ if (!globulize(&dest) ||
+ !confirm("output to local-file:", dest)) {
+ code = -1;
+ return;
+ }
+ cmd = argv[0][1] == 'l' ? "NLST" : "LIST";
+ mname = argv[0];
+ mflag = 1;
+ oldintr = signal(SIGINT, mabort);
+ (void) setjmp(jabort);
+ for (i = 1; mflag && i < argc-1; ++i) {
+ *mode = (i == 1) ? 'w' : 'a';
+ recvrequest(cmd, dest, argv[i], mode, 1);
+ if (!mflag && fromatty) {
+ ointer = interactive;
+ interactive = 1;
+ if (confirm("Continue with", argv[0])) {
+ mflag ++;
+ }
+ interactive = ointer;
+ }
+ }
+ (void) signal(SIGINT, oldintr);
+ mflag = 0;
+}
+
+/*
+ * Do a shell escape
+ */
+/*ARGSUSED*/
+void
+shell(int argc, char *argv[])
+{
+ pid_t pid;
+ void (*old1)(), (*old2)();
+ char *shellstring, *namep;
+ int status;
+
+ stop_timer();
+ old1 = signal(SIGINT, SIG_IGN);
+ old2 = signal(SIGQUIT, SIG_IGN);
+ if ((pid = fork()) == 0) {
+ closefrom(STDERR_FILENO + 1);
+ (void) signal(SIGINT, SIG_DFL);
+ (void) signal(SIGQUIT, SIG_DFL);
+ shellstring = getenv("SHELL");
+ if (shellstring == NULL)
+ shellstring = "/bin/sh";
+ namep = rindex(shellstring, '/');
+ if (namep == NULL)
+ namep = shellstring;
+ if (argc > 1) {
+ if (debug) {
+ (void) printf("%s -c %s\n", shellstring,
+ altarg);
+ (void) fflush(stdout);
+ }
+ execl(shellstring, namep, "-c", altarg, (char *)0);
+ } else {
+ if (debug) {
+ (void) printf("%s\n", shellstring);
+ (void) fflush(stdout);
+ }
+ execl(shellstring, namep, (char *)0);
+ }
+ perror(shellstring);
+ code = -1;
+ exit(1);
+ }
+ if (pid > 0)
+ while (wait(&status) != pid)
+ ;
+ (void) signal(SIGINT, old1);
+ (void) signal(SIGQUIT, old2);
+ reset_timer();
+ if (pid == (pid_t)-1) {
+ perror("Try again later");
+ code = -1;
+ } else {
+ code = 0;
+ }
+}
+
+/*
+ * Send new user information (re-login)
+ */
+void
+user(int argc, char *argv[])
+{
+ char acct[80];
+ int n, aflag = 0;
+
+ if (argc < 2) {
+ if (prompt_for_arg(line, sizeof (line), "username") < 0) {
+ code = -1;
+ return;
+ }
+ makeargv();
+ argc = margc;
+ argv = margv;
+ }
+ if (argc > 4) {
+ (void) printf("usage: %s username [password] [account]\n",
+ argv[0]);
+ code = -1;
+ return;
+ }
+ if (argv[1] == 0) {
+ (void) printf("access for user (nil) denied\n");
+ code = -1;
+ return;
+ }
+ n = command("USER %s", argv[1]);
+ if (n == CONTINUE) {
+ int oldclevel;
+ if (argc < 3)
+ argv[2] = mygetpass("Password: "), argc++;
+ if ((oldclevel = clevel) == PROT_S)
+ clevel = PROT_P;
+ n = command("PASS %s", argv[2]);
+ /* level may have changed */
+ if (clevel == PROT_P)
+ clevel = oldclevel;
+ }
+ if (n == CONTINUE) {
+ if (argc < 4) {
+ (void) printf("Account: "); (void) fflush(stdout);
+ stop_timer();
+ (void) fgets(acct, sizeof (acct) - 1, stdin);
+ reset_timer();
+ acct[strlen(acct) - 1] = '\0';
+ argv[3] = acct; argc++;
+ }
+ n = command("ACCT %s", argv[3]);
+ aflag++;
+ }
+ if (n != COMPLETE) {
+ (void) fprintf(stdout, "Login failed.\n");
+ return;
+ }
+ if (!aflag && argc == 4) {
+ (void) command("ACCT %s", argv[3]);
+ }
+}
+
+/*
+ * Print working directory.
+ */
+/*ARGSUSED*/
+void
+pwd(int argc, char *argv[])
+{
+ (void) command("PWD");
+}
+
+/*
+ * Make a directory.
+ */
+void
+makedir(int argc, char *argv[])
+{
+ if (argc < 2) {
+ if (prompt_for_arg(line, sizeof (line), "directory-name") <
+ 0) {
+ code = -1;
+ return;
+ }
+ makeargv();
+ argc = margc;
+ argv = margv;
+ }
+ if (argc < 2) {
+ (void) printf("usage: %s directory-name\n", argv[0]);
+ code = -1;
+ return;
+ }
+ (void) command("MKD %s", argv[1]);
+}
+
+/*
+ * Remove a directory.
+ */
+void
+removedir(int argc, char *argv[])
+{
+ if (argc < 2) {
+ if (prompt_for_arg(line, sizeof (line), "directory-name") <
+ 0) {
+ code = -1;
+ return;
+ }
+ makeargv();
+ argc = margc;
+ argv = margv;
+ }
+ if (argc < 2) {
+ (void) printf("usage: %s directory-name\n", argv[0]);
+ code = -1;
+ return;
+ }
+ (void) command("RMD %s", argv[1]);
+}
+
+/*
+ * Send a line, verbatim, to the remote machine.
+ */
+void
+quote(int argc, char *argv[])
+{
+ int i, n, len;
+ char buf[FTPBUFSIZ];
+
+ if (argc < 2) {
+ if (prompt_for_arg(line, sizeof (line),
+ "command line to send") == -1) {
+ code = -1;
+ return;
+ }
+ makeargv();
+ argc = margc;
+ argv = margv;
+ }
+ if (argc < 2) {
+ (void) printf("usage: %s line-to-send\n", argv[0]);
+ code = -1;
+ return;
+ }
+ len = snprintf(buf, sizeof (buf), "%s", argv[1]);
+ if (len >= 0 && len < sizeof (buf) - 1) {
+ for (i = 2; i < argc; i++) {
+ n = snprintf(&buf[len], sizeof (buf) - len, " %s",
+ argv[i]);
+ if (n < 0 || n >= sizeof (buf) - len)
+ break;
+ len += n;
+ }
+ }
+ if (command("%s", buf) == PRELIM) {
+ while (getreply(0) == PRELIM)
+ ;
+ }
+}
+
+/*
+ * Send a line, verbatim, to the remote machine as a SITE command.
+ */
+void
+site(int argc, char *argv[])
+{
+ int i, n, len;
+ char buf[FTPBUFSIZ];
+
+ if (argc < 2) {
+ if (prompt_for_arg(line, sizeof (line),
+ "arguments to SITE command") == -1) {
+ code = -1;
+ return;
+ }
+ makeargv();
+ argc = margc;
+ argv = margv;
+ }
+ if (argc < 2) {
+ (void) printf("usage: %s arg1 [arg2] ...\n", argv[0]);
+ code = -1;
+ return;
+ }
+ len = snprintf(buf, sizeof (buf), "%s", argv[1]);
+ if (len >= 0 && len < sizeof (buf) - 1) {
+ for (i = 2; i < argc; i++) {
+ n = snprintf(&buf[len], sizeof (buf) - len, " %s",
+ argv[i]);
+ if (n < 0 || n >= sizeof (buf) - len)
+ break;
+ len += n;
+ }
+ }
+ if (command("SITE %s", buf) == PRELIM) {
+ while (getreply(0) == PRELIM)
+ ;
+ }
+}
+
+/*
+ * Ask the other side for help.
+ */
+void
+rmthelp(int argc, char *argv[])
+{
+ int oldverbose = verbose;
+
+ verbose = 1;
+ (void) command(argc == 1 ? "HELP" : "HELP %s", argv[1]);
+ verbose = oldverbose;
+}
+
+/*
+ * Terminate session and exit.
+ */
+/*ARGSUSED*/
+void
+quit(int argc, char *argv[])
+{
+ if (connected)
+ disconnect(0, NULL);
+ pswitch(1);
+ if (connected) {
+ disconnect(0, NULL);
+ }
+ exit(0);
+}
+
+/*
+ * Terminate session, but don't exit.
+ */
+/*ARGSUSED*/
+void
+disconnect(int argc, char *argv[])
+{
+ extern FILE *ctrl_in, *ctrl_out;
+ extern int data;
+
+ if (!connected)
+ return;
+ (void) command("QUIT");
+ if (ctrl_in) {
+ reset_timer();
+ (void) fclose(ctrl_in);
+ }
+ if (ctrl_out) {
+ reset_timer();
+ (void) fclose(ctrl_out);
+ }
+ ctrl_out = ctrl_in = NULL;
+ connected = 0;
+ data = -1;
+ if (!proxy) {
+ macnum = 0;
+ }
+
+ auth_type = AUTHTYPE_NONE;
+ clevel = dlevel = PROT_C;
+ goteof = 0;
+}
+
+static int
+confirm(char *cmd, char *file)
+{
+ char line[FTPBUFSIZ];
+
+ if (!interactive)
+ return (1);
+ stop_timer();
+ (void) printf("%s %s? ", cmd, file);
+ (void) fflush(stdout);
+ *line = '\0';
+ (void) fgets(line, sizeof (line), stdin);
+ reset_timer();
+ return (*line != 'n' && *line != 'N');
+}
+
+void
+fatal(char *msg)
+{
+ (void) fprintf(stderr, "ftp: %s\n", msg);
+ exit(1);
+}
+
+/*
+ * Glob a local file name specification with
+ * the expectation of a single return value.
+ * Can't control multiple values being expanded
+ * from the expression, we return only the first.
+ */
+static int
+globulize(char **cpp)
+{
+ char **globbed;
+
+ if (!doglob)
+ return (1);
+ globbed = glob(*cpp);
+ if (globbed != NULL && *globbed == NULL && globerr == NULL)
+ globerr = "No match";
+ if (globerr != NULL) {
+ (void) printf("%s: %s\n", *cpp, globerr);
+ if (globbed)
+ blkfree(globbed);
+ return (0);
+ }
+ if (globbed) {
+ *cpp = strdup(*globbed);
+ blkfree(globbed);
+ if (!*cpp)
+ return (0);
+ }
+ return (1);
+}
+
+void
+account(int argc, char *argv[])
+{
+ char acct[50], *ap;
+
+ if (argc > 1) {
+ ++argv;
+ --argc;
+ (void) strncpy(acct, *argv, 49);
+ acct[49] = '\0';
+ while (argc > 1) {
+ --argc;
+ ++argv;
+ (void) strncat(acct, *argv, 49 - strlen(acct));
+ }
+ ap = acct;
+ } else {
+ ap = mygetpass("Account:");
+ }
+ (void) command("ACCT %s", ap);
+}
+
+/*ARGSUSED*/
+static void
+proxabort(int sig)
+{
+ extern int proxy;
+
+ if (!proxy) {
+ pswitch(1);
+ }
+ if (connected) {
+ proxflag = 1;
+ } else {
+ proxflag = 0;
+ }
+ pswitch(0);
+ longjmp(abortprox, 1);
+}
+
+void
+doproxy(int argc, char *argv[])
+{
+ void (*oldintr)();
+ struct cmd *c;
+
+ if (argc < 2) {
+ if (prompt_for_arg(line, sizeof (line), "command") == -1) {
+ code = -1;
+ return;
+ }
+ makeargv();
+ argc = margc;
+ argv = margv;
+ }
+ if (argc < 2) {
+ (void) printf("usage: %s command\n", argv[0]);
+ code = -1;
+ return;
+ }
+ c = getcmd(argv[1]);
+ if (c == (struct cmd *)-1) {
+ (void) printf("?Ambiguous command\n");
+ (void) fflush(stdout);
+ code = -1;
+ return;
+ }
+ if (c == 0) {
+ (void) printf("?Invalid command\n");
+ (void) fflush(stdout);
+ code = -1;
+ return;
+ }
+ if (!c->c_proxy) {
+ (void) printf("?Invalid proxy command\n");
+ (void) fflush(stdout);
+ code = -1;
+ return;
+ }
+ if (setjmp(abortprox)) {
+ code = -1;
+ return;
+ }
+ oldintr = signal(SIGINT, (void (*)())proxabort);
+ pswitch(1);
+ if (c->c_conn && !connected) {
+ (void) printf("Not connected\n");
+ (void) fflush(stdout);
+ pswitch(0);
+ (void) signal(SIGINT, oldintr);
+ code = -1;
+ return;
+ }
+ (*c->c_handler)(argc-1, argv+1);
+ if (connected) {
+ proxflag = 1;
+ } else {
+ proxflag = 0;
+ }
+ pswitch(0);
+ (void) signal(SIGINT, oldintr);
+}
+
+/*ARGSUSED*/
+void
+setcase(int argc, char *argv[])
+{
+ mcase = !mcase;
+ (void) printf("Case mapping %s.\n", onoff(mcase));
+ code = mcase;
+}
+
+/*ARGSUSED*/
+void
+setcr(int argc, char *argv[])
+{
+ crflag = !crflag;
+ (void) printf("Carriage Return stripping %s.\n", onoff(crflag));
+ code = crflag;
+}
+
+void
+setntrans(int argc, char *argv[])
+{
+ if (argc == 1) {
+ ntflag = 0;
+ (void) printf("Ntrans off.\n");
+ code = ntflag;
+ return;
+ }
+ ntflag++;
+ code = ntflag;
+ (void) strncpy(ntin, argv[1], 16);
+ ntin[16] = '\0';
+ if (argc == 2) {
+ ntout[0] = '\0';
+ return;
+ }
+ (void) strncpy(ntout, argv[2], 16);
+ ntout[16] = '\0';
+}
+
+static char *
+dotrans(char *name)
+{
+ static char new[MAXPATHLEN];
+ char *cp1, *cp2 = new;
+ int i, ostop, found;
+
+ for (ostop = 0; *(ntout + ostop) && ostop < 16; ostop++)
+ ;
+ for (cp1 = name; *cp1; cp1++) {
+ found = 0;
+ for (i = 0; *(ntin + i) && i < 16; i++) {
+ if (*cp1 == *(ntin + i)) {
+ found++;
+ if (i < ostop) {
+ *cp2++ = *(ntout + i);
+ }
+ break;
+ }
+ }
+ if (!found) {
+ *cp2++ = *cp1;
+ }
+ }
+ *cp2 = '\0';
+ return (new);
+}
+
+void
+setnmap(int argc, char *argv[])
+{
+ char *cp;
+
+ if (argc == 1) {
+ mapflag = 0;
+ (void) printf("Nmap off.\n");
+ code = mapflag;
+ return;
+ }
+ if (argc < 3) {
+ if (prompt_for_arg(line, sizeof (line), "mapout") == -1) {
+ code = -1;
+ return;
+ }
+ makeargv();
+ argc = margc;
+ argv = margv;
+ }
+ if (argc < 3) {
+ (void) printf("Usage: %s [mapin mapout]\n", argv[0]);
+ code = -1;
+ return;
+ }
+ mapflag = 1;
+ code = 1;
+ cp = index(altarg, ' ');
+ if (proxy) {
+ while (*++cp == ' ')
+ /* NULL */;
+ altarg = cp;
+ cp = index(altarg, ' ');
+ }
+ *cp = '\0';
+ (void) strncpy(mapin, altarg, MAXPATHLEN - 1);
+ while (*++cp == ' ')
+ /* NULL */;
+ (void) strncpy(mapout, cp, MAXPATHLEN - 1);
+}
+
+static char *
+domap(char *name)
+{
+ static char new[MAXPATHLEN];
+ char *cp1 = name, *cp2 = mapin;
+ char *tp[9], *te[9];
+ int i, toks[9], toknum, match = 1;
+ wchar_t wc1, wc2;
+ int len1, len2;
+
+ for (i = 0; i < 9; ++i) {
+ toks[i] = 0;
+ }
+ while (match && *cp1 && *cp2) {
+ if ((len1 = mbtowc(&wc1, cp1, MB_CUR_MAX)) <= 0) {
+ wc1 = (unsigned char)*cp1;
+ len1 = 1;
+ }
+ cp1 += len1;
+ if ((len2 = mbtowc(&wc2, cp2, MB_CUR_MAX)) <= 0) {
+ wc2 = (unsigned char)*cp2;
+ len2 = 1;
+ }
+ cp2 += len2;
+
+ switch (wc2) {
+ case '\\':
+ if ((len2 = mbtowc(&wc2, cp2, MB_CUR_MAX)) <= 0) {
+ wc2 = (unsigned char)*cp2;
+ len2 = 1;
+ }
+ cp2 += len2;
+ if (wc2 != wc1)
+ match = 0;
+ break;
+
+ case '$':
+ if (*cp2 >= '1' && *cp2 <= '9') {
+ if ((len2 =
+ mbtowc(&wc2, cp2 + 1, MB_CUR_MAX)) <= 0) {
+ wc2 = (unsigned char)*(cp2 + 1);
+ len2 = 1;
+ }
+ if (wc1 != wc2) {
+ toks[toknum = *cp2 - '1']++;
+ tp[toknum] = cp1 - len1;
+ while (*cp1) {
+ if ((len1 = mbtowc(&wc1,
+ cp1, MB_CUR_MAX)) <= 0) {
+ wc1 =
+ (unsigned char)*cp1;
+ len1 = 1;
+ }
+ cp1 += len1;
+ if (wc2 == wc1)
+ break;
+ }
+ if (*cp1 == 0 && wc2 != wc1)
+ te[toknum] = cp1;
+ else
+ te[toknum] = cp1 - len1;
+ }
+ cp2++; /* Consume the digit */
+ if (wc2)
+ cp2 += len2; /* Consume wide char */
+ break;
+ }
+ /* intentional drop through */
+ default:
+ if (wc2 != wc1)
+ match = 0;
+ break;
+ }
+ }
+
+ cp1 = new;
+ *cp1 = '\0';
+ cp2 = mapout;
+ while (*cp2) {
+ match = 0;
+ switch (*cp2) {
+ case '\\':
+ cp2++;
+ if (*cp2) {
+ if ((len2 = mblen(cp2, MB_CUR_MAX)) <= 0)
+ len2 = 1;
+ memcpy(cp1, cp2, len2);
+ cp1 += len2;
+ cp2 += len2;
+ }
+ break;
+
+ case '[':
+LOOP:
+ cp2++;
+ if (*cp2 == '$' && isdigit(*(cp2+1))) {
+ if (*++cp2 == '0') {
+ char *cp3 = name;
+
+ while (*cp3) {
+ *cp1++ = *cp3++;
+ }
+ match = 1;
+ } else if (toks[toknum = *cp2 - '1']) {
+ char *cp3 = tp[toknum];
+
+ while (cp3 != te[toknum]) {
+ *cp1++ = *cp3++;
+ }
+ match = 1;
+ }
+ } else {
+ while (*cp2 && *cp2 != ',' && *cp2 != ']') {
+ if (*cp2 == '\\') {
+ cp2++;
+ continue;
+ }
+
+ if (*cp2 == '$' && isdigit(*(cp2+1))) {
+ if (*++cp2 == '0') {
+ char *cp3 = name;
+
+ while (*cp3)
+ *cp1++ = *cp3++;
+ continue;
+ }
+ if (toks[toknum = *cp2 - '1']) {
+ char *cp3 = tp[toknum];
+
+ while (cp3 !=
+ te[toknum])
+ *cp1++ = *cp3++;
+ }
+ continue;
+ }
+ if (*cp2) {
+ if ((len2 =
+ mblen(cp2, MB_CUR_MAX)) <=
+ 0) {
+ len2 = 1;
+ }
+ memcpy(cp1, cp2, len2);
+ cp1 += len2;
+ cp2 += len2;
+ }
+ }
+ if (!*cp2) {
+ (void) printf(
+ "nmap: unbalanced brackets\n");
+ return (name);
+ }
+ match = 1;
+ }
+ if (match) {
+ while (*cp2 && *cp2 != ']') {
+ if (*cp2 == '\\' && *(cp2 + 1)) {
+ cp2++;
+ }
+ if ((len2 = mblen(cp2, MB_CUR_MAX)) <=
+ 0)
+ len2 = 1;
+ cp2 += len2;
+ }
+ if (!*cp2) {
+ (void) printf(
+ "nmap: unbalanced brackets\n");
+ return (name);
+ }
+ cp2++;
+ break;
+ }
+ switch (*++cp2) {
+ case ',':
+ goto LOOP;
+ case ']':
+ break;
+ default:
+ cp2--;
+ goto LOOP;
+ }
+ cp2++;
+ break;
+ case '$':
+ if (isdigit(*(cp2 + 1))) {
+ if (*++cp2 == '0') {
+ char *cp3 = name;
+
+ while (*cp3) {
+ *cp1++ = *cp3++;
+ }
+ } else if (toks[toknum = *cp2 - '1']) {
+ char *cp3 = tp[toknum];
+
+ while (cp3 != te[toknum]) {
+ *cp1++ = *cp3++;
+ }
+ }
+ cp2++;
+ break;
+ }
+ /* intentional drop through */
+ default:
+ if ((len2 = mblen(cp2, MB_CUR_MAX)) <= 0)
+ len2 = 1;
+ memcpy(cp1, cp2, len2);
+ cp1 += len2;
+ cp2 += len2;
+ break;
+ }
+ }
+ *cp1 = '\0';
+ if (!*new) {
+ return (name);
+ }
+ return (new);
+}
+
+/*ARGSUSED*/
+void
+setsunique(int argc, char *argv[])
+{
+ sunique = !sunique;
+ (void) printf("Store unique %s.\n", onoff(sunique));
+ code = sunique;
+}
+
+/*ARGSUSED*/
+void
+setrunique(int argc, char *argv[])
+{
+ runique = !runique;
+ (void) printf("Receive unique %s.\n", onoff(runique));
+ code = runique;
+}
+
+/*ARGSUSED*/
+void
+setpassive(int argc, char *argv[])
+{
+ passivemode = !passivemode;
+ (void) printf("Passive mode %s.\n", onoff(passivemode));
+ code = passivemode;
+}
+
+void
+settcpwindow(int argc, char *argv[])
+{
+ int owindowsize = tcpwindowsize;
+
+ if (argc > 2) {
+ (void) printf("usage: %s [size]\n", argv[0]);
+ code = -1;
+ return;
+ }
+ if (argc == 2) {
+ int window;
+ char *endp;
+
+ errno = 0;
+ window = (int)strtol(argv[1], &endp, 10);
+ if (errno || window < 0 || *endp != '\0')
+ (void) printf("%s: Invalid size `%s'\n",
+ argv[0], argv[1]);
+ else
+ tcpwindowsize = window;
+ }
+ if (tcpwindowsize == 0) {
+ if (owindowsize == 0)
+ (void) printf("No TCP window size defined\n");
+ else
+ (void) printf("TCP window size cleared\n");
+ } else
+ (void) printf("TCP window size is set to %d\n", tcpwindowsize);
+}
+
+/* change directory to parent directory */
+/*ARGSUSED*/
+void
+cdup(int argc, char *argv[])
+{
+ (void) command("CDUP");
+}
+
+void
+macdef(int argc, char *argv[])
+{
+ char *tmp;
+ int c;
+
+ if (macnum == 16) {
+ (void) printf("Limit of 16 macros have already been defined\n");
+ code = -1;
+ return;
+ }
+ if (argc < 2) {
+ if (prompt_for_arg(line, sizeof (line), "macro name") == -1) {
+ code = -1;
+ return;
+ }
+ makeargv();
+ argc = margc;
+ argv = margv;
+ }
+ if (argc != 2) {
+ (void) printf("Usage: %s macro_name\n", argv[0]);
+ code = -1;
+ return;
+ }
+ if (interactive) {
+ (void) printf("Enter macro line by line, terminating "
+ "it with a null line\n");
+ }
+ (void) strncpy(macros[macnum].mac_name, argv[1], 8);
+ if (macnum == 0) {
+ macros[macnum].mac_start = macbuf;
+ } else {
+ macros[macnum].mac_start = macros[macnum - 1].mac_end + 1;
+ }
+ tmp = macros[macnum].mac_start;
+ while (tmp != macbuf+4096) {
+ if ((c = getchar()) == EOF) {
+ (void) printf("macdef:end of file encountered\n");
+ code = -1;
+ return;
+ }
+ if ((*tmp = c) == '\n') {
+ if (tmp == macros[macnum].mac_start) {
+ macros[macnum++].mac_end = tmp;
+ code = 0;
+ return;
+ }
+ if (*(tmp-1) == '\0') {
+ macros[macnum++].mac_end = tmp - 1;
+ code = 0;
+ return;
+ }
+ *tmp = '\0';
+ }
+ tmp++;
+ }
+ for (;;) {
+ while ((c = getchar()) != '\n' && c != EOF)
+ /* NULL */;
+ if (c == EOF || getchar() == '\n') {
+ (void) printf(
+ "Macro not defined - 4k buffer exceeded\n");
+ code = -1;
+ return;
+ }
+ }
+}
+
+/*
+ * The p_name strings are for the getlevel and setlevel commands.
+ * The name strings for printing are in the arpa/ftp.h file in the
+ * protnames[] array of strings.
+ */
+static struct levels {
+ char *p_name;
+ char *p_mode;
+ int p_level;
+} levels[] = {
+ { "clear", "C", PROT_C },
+ { "safe", "S", PROT_S },
+ { "private", "P", PROT_P },
+ NULL
+};
+
+/*
+ * Return a pointer to a string which is the readable version of the
+ * protection level, or NULL if the input level is not found.
+ */
+static char *
+getlevel(int level)
+{
+ struct levels *p;
+
+ for (p = levels; (p != NULL) && (p->p_level != level); p++)
+ ;
+ return (p ? p->p_name : NULL);
+}
+
+static char *plevel[] = {
+ "protect",
+ "",
+ NULL
+};
+
+/*
+ * Set control channel protection level.
+ */
+void
+setclevel(int argc, char *argv[])
+{
+ struct levels *p;
+ char *levelp;
+ int comret;
+
+ if (argc > 2) {
+ char *sep;
+
+ (void) printf("usage: %s [", argv[0]);
+ sep = " ";
+ for (p = levels; p->p_name; p++) {
+ (void) printf("%s%s", sep, p->p_name);
+ if (*sep == ' ')
+ sep = " | ";
+ }
+ (void) printf(" ]\n");
+ code = -1;
+ return;
+ }
+ if (argc < 2) {
+ levelp = getlevel(clevel);
+ (void) printf("Using %s protection level for commands.\n",
+ levelp ? levelp : "<unknown>");
+ code = 0;
+ return;
+ }
+ for (p = levels; (p != NULL) && (p->p_name); p++)
+ if (strcmp(argv[1], p->p_name) == 0)
+ break;
+ if (p->p_name == 0) {
+ (void) printf("%s: unknown protection level\n", argv[1]);
+ code = -1;
+ return;
+ }
+ if (auth_type == AUTHTYPE_NONE) {
+ if (strcmp(p->p_name, "clear"))
+ (void) printf("Cannot set protection level to %s\n",
+ argv[1]);
+ return;
+ }
+ if (strcmp(p->p_name, "clear") == 0) {
+ comret = command("CCC");
+ if (comret == COMPLETE)
+ clevel = PROT_C;
+ return;
+ }
+ clevel = p->p_level;
+ (void) printf("Control channel protection level set to %s.\n",
+ p->p_name);
+}
+
+/*
+ * Set data channel protection level.
+ */
+void
+setdlevel(int argc, char *argv[])
+{
+ struct levels *p;
+ int comret;
+
+ if (argc != 2) {
+ char *sep;
+
+ (void) printf("usage: %s [", argv[0]);
+ sep = " ";
+ for (p = levels; p->p_name; p++) {
+ (void) printf("%s%s", sep, p->p_name);
+ if (*sep == ' ')
+ sep = " | ";
+ }
+ (void) printf(" ]\n");
+ code = -1;
+ return;
+ }
+ for (p = levels; p->p_name; p++)
+ if (strcmp(argv[1], p->p_name) == 0)
+ break;
+ if (p->p_name == 0) {
+ (void) printf("%s: unknown protection level\n", argv[1]);
+ code = -1;
+ return;
+ }
+ if (auth_type == AUTHTYPE_NONE) {
+ if (strcmp(p->p_name, "clear"))
+ (void) printf("Cannot set protection level to %s\n",
+ argv[1]);
+ return;
+ }
+ /* Start with a PBSZ of 1 meg */
+ if (p->p_level != PROT_C)
+ setpbsz(1<<20);
+ comret = command("PROT %s", p->p_mode);
+ if (comret == COMPLETE)
+ dlevel = p->p_level;
+}
+
+/*
+ * Set clear command protection level.
+ */
+/* VARARGS */
+void
+ccc(int argc, char *argv[])
+{
+ plevel[1] = "clear";
+ setclevel(2, plevel);
+}
+
+/*
+ * Set clear data protection level.
+ */
+/* VARARGS */
+void
+setclear(int argc, char *argv[])
+{
+ plevel[1] = "clear";
+ setdlevel(2, plevel);
+}
+
+/*
+ * Set safe data protection level.
+ */
+/* VARARGS */
+void
+setsafe(int argc, char *argv[])
+{
+ plevel[1] = "safe";
+ setdlevel(2, plevel);
+}
+
+/*
+ * Set private data protection level.
+ */
+/* VARARGS */
+void
+setprivate(int argc, char *argv[])
+{
+ plevel[1] = "private";
+ setdlevel(2, plevel);
+}
+
+/*
+ * Set mechanism type
+ */
+void
+setmech(int argc, char *argv[])
+{
+ char tempmech[MECH_SZ];
+
+ if (argc < 2) {
+ if (prompt_for_arg(line, sizeof (line), "mech-type") == -1) {
+ code = -1;
+ return;
+ }
+ makeargv();
+ argc = margc;
+ argv = margv;
+ }
+
+ if (argc != 2) {
+ (void) printf("usage: %s [ mechanism type ]\n", argv[0]);
+ code = -1;
+ return;
+ }
+
+ if ((strlcpy(tempmech, argv[1], MECH_SZ) >= MECH_SZ) ||
+ __gss_mech_to_oid(tempmech, (gss_OID*)&mechoid) !=
+ GSS_S_COMPLETE) {
+ (void) printf("%s: %s: not a valid security mechanism\n",
+ argv[0], tempmech);
+ code = -1;
+ return;
+ } else {
+ (void) strlcpy(mechstr, tempmech, MECH_SZ);
+ (void) printf("Using %s mechanism type\n", mechstr);
+ code = 0;
+ return;
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/ftp/cmds_gss.c b/usr/src/cmd/cmd-inet/usr.bin/ftp/cmds_gss.c
new file mode 100644
index 0000000000..f6c4c3d4a2
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/ftp/cmds_gss.c
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Copyright (c) 1985, 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "ftp_var.h"
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_ext.h>
+
+void
+user_gss_error(OM_uint32 maj_stat, OM_uint32 min_stat, char *errstr)
+{
+ OM_uint32 gmaj_stat, gmin_stat;
+ gss_buffer_desc msg;
+ OM_uint32 msg_ctx = 0;
+ int display_error = 0;
+
+ /* Print the major status error from GSS */
+ while (!msg_ctx) {
+ gmaj_stat = gss_display_status(&gmin_stat, maj_stat,
+ GSS_C_GSS_CODE, GSS_C_NULL_OID, &msg_ctx, &msg);
+ if ((gmaj_stat == GSS_S_COMPLETE)||
+ (gmaj_stat == GSS_S_CONTINUE_NEEDED)) {
+ /* display error messages only once */
+ if ((debug) || (!display_error)) {
+ (void) fprintf(stderr, "GSSAPI error major: %s\n",
+ (char *)msg.value);
+ display_error = 1;
+ }
+ (void) gss_release_buffer(&gmin_stat, &msg);
+ }
+ if (gmaj_stat != GSS_S_CONTINUE_NEEDED)
+ break;
+ }
+
+ /* Print the minor status error from the mech */
+ msg_ctx = 0;
+ display_error = 0;
+ if (min_stat)
+ while (!msg_ctx) {
+ gmaj_stat = gss_display_status(&gmin_stat, min_stat,
+ GSS_C_MECH_CODE, GSS_C_NULL_OID, &msg_ctx, &msg);
+ if ((gmaj_stat == GSS_S_COMPLETE)||
+ (gmaj_stat == GSS_S_CONTINUE_NEEDED)) {
+ /* display error messages only once */
+ if ((!display_error) || (!debug)) {
+ (void) fprintf(stderr, "GSSAPI error minor: %s\n",
+ (char *)msg.value);
+ display_error = 1;
+ }
+ (void) gss_release_buffer(&gmin_stat, &msg);
+ }
+ if (gmaj_stat != GSS_S_CONTINUE_NEEDED)
+ break;
+ }
+
+ if (debug) {
+ (void) fprintf(stderr, "GSSAPI error: %s\n", errstr);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/ftp/cmdtab.c b/usr/src/cmd/cmd-inet/usr.bin/ftp/cmdtab.c
new file mode 100644
index 0000000000..fe2798f142
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/ftp/cmdtab.c
@@ -0,0 +1,216 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "ftp_var.h"
+
+/*
+ * User FTP -- Command Tables.
+ */
+
+static char accounthelp[] = "send account command to remote server";
+static char appendhelp[] = "append to a file";
+static char asciihelp[] = "set ascii transfer type";
+static char beephelp[] = "beep when command completed";
+static char binaryhelp[] = "set binary transfer type";
+static char casehelp[] = "toggle mget upper/lower case id mapping";
+static char ccchelp[] = "set clear protection level for commands";
+static char cdhelp[] = "change remote working directory";
+static char cduphelp[] = "change remote working directory to parent "
+ "directory";
+static char clearhelp[] = "set clear protection level for data";
+static char connecthelp[] = "connect to remote tftp";
+static char crhelp[] = "toggle carriage return stripping on ascii "
+ "gets";
+static char deletehelp[] = "delete remote file";
+static char debughelp[] = "toggle/set debugging mode";
+static char dirhelp[] = "list contents of remote directory";
+static char disconhelp[] = "terminate ftp session";
+static char domachelp[] = "execute macro";
+static char formhelp[] = "set file transfer format";
+static char globhelp[] = "toggle metacharacter expansion of local file "
+ "names";
+static char hashhelp[] = "toggle printing `#' for each buffer "
+ "transferred";
+static char helphelp[] = "print local help information";
+static char lcdhelp[] = "change local working directory";
+static char lshelp[] = "display contents of remote directory";
+static char macdefhelp[] = "define a macro";
+static char mdeletehelp[] = "delete multiple files";
+static char mdirhelp[] = "list contents of multiple remote directories";
+static char mechhelp[] = "set mechanism type";
+static char mgethelp[] = "get multiple files";
+static char mkdirhelp[] = "make directory on the remote machine";
+static char mlshelp[] = "nlist contents of multiple remote directories";
+static char modehelp[] = "set file transfer mode";
+static char mputhelp[] = "send multiple files";
+static char nlisthelp[] = "nlist contents of remote directory";
+static char nmaphelp[] = "set templates for default file name mapping";
+static char ntranshelp[] = "set translation table for default file name "
+ "mapping";
+static char passivehelp[] = "toggle passive transfer mode";
+static char porthelp[] = "toggle use of PORT cmd for each data "
+ "connection";
+static char privatehelp[] = "set private protection level for data";
+static char prompthelp[] = "force interactive prompting on multiple "
+ "commands";
+static char protecthelp[] = "set protection level for data";
+static char proxyhelp[] = "issue command on alternate connection";
+static char pwdhelp[] = "print working directory on remote machine";
+static char quithelp[] = "terminate ftp session and exit";
+static char quotehelp[] = "send arbitrary ftp command";
+static char receivehelp[] = "receive file";
+static char regethelp[] = "get file restarting at end of local file";
+static char remotehelp[] = "get help from remote server";
+static char renamehelp[] = "rename file";
+static char resethelp[] = "clear queued command replies";
+static char restarthelp[] = "restart file transfer at bytecount";
+static char rmdirhelp[] = "remove directory on the remote machine";
+static char runiquehelp[] = "toggle store unique for local files";
+static char safehelp[] = "set safe protection level for data";
+static char sendhelp[] = "send one file";
+static char shellhelp[] = "escape to the shell";
+static char sitehelp[] = "send site specific command to remote server\n"
+ "\t\tTry \"remotehelp site\" or \"site help\" "
+ "for more information";
+static char statushelp[] = "show current status";
+static char structhelp[] = "set file transfer structure";
+static char suniquehelp[] = "toggle store unique on remote machine";
+static char tenexhelp[] = "set tenex file transfer type";
+static char tracehelp[] = "toggle packet tracing";
+static char typehelp[] = "set file transfer type";
+static char userhelp[] = "send new user information";
+static char verbosehelp[] = "toggle verbose mode";
+static char windowhelp[] = "set TCP window size for the data connection";
+
+/*
+ * NOTE : The BUFSIZE defined in ftp_var.h includes MAXCMDLEN chars to
+ * accomodate the longest command in the cmdtab[] defined below.
+ * If anyone plans to add a new command that is longer than the MAXCMDLEN
+ * make sure to update it in ftp_var.h.
+ */
+
+struct cmd cmdtab[] = {
+ { "!", shellhelp, 0, 0, 0, shell },
+ { "$", domachelp, 1, 0, 0, domacro },
+ { "account", accounthelp, 0, 1, 1, account},
+ { "append", appendhelp, 1, 1, 1, put },
+ { "ascii", asciihelp, 0, 1, 1, setascii },
+ { "bell", beephelp, 0, 0, 0, setbell },
+ { "binary", binaryhelp, 0, 1, 1, setbinary },
+ { "bye", quithelp, 0, 0, 0, quit },
+ { "case", casehelp, 0, 0, 1, setcase },
+
+ { "ccc", ccchelp, 0, 1, 1, ccc },
+
+ { "cd", cdhelp, 0, 1, 1, cd },
+ { "cdup", cduphelp, 0, 1, 1, cdup },
+
+ { "clear", clearhelp, 0, 1, 1, setclear },
+
+ { "close", disconhelp, 0, 1, 1, disconnect },
+ { "cr", crhelp, 0, 0, 0, setcr },
+ { "delete", deletehelp, 0, 1, 1, delete },
+ { "debug", debughelp, 0, 0, 0, setdebug },
+ { "dir", dirhelp, 1, 1, 1, ls },
+ { "disconnect", disconhelp, 0, 1, 1, disconnect },
+ { "form", formhelp, 0, 1, 1, setform },
+ { "get", receivehelp, 1, 1, 1, get },
+ { "glob", globhelp, 0, 0, 0, setglob },
+ { "hash", hashhelp, 0, 0, 0, sethash },
+ { "help", helphelp, 0, 0, 1, help },
+ { "lcd", lcdhelp, 0, 0, 0, lcd },
+ { "ls", lshelp, 1, 1, 1, ls },
+ { "macdef", macdefhelp, 0, 0, 0, macdef },
+ { "mdelete", mdeletehelp, 1, 1, 1, mdelete },
+ { "mdir", mdirhelp, 1, 1, 1, mls },
+
+ { "mechanism", mechhelp, 1, 0, 1, setmech },
+
+ { "mget", mgethelp, 1, 1, 1, mget },
+ { "mkdir", mkdirhelp, 0, 1, 1, makedir },
+ { "mls", mlshelp, 1, 1, 1, mls },
+ { "mode", modehelp, 0, 1, 1, setmode },
+ { "mput", mputhelp, 1, 1, 1, mput },
+ { "nlist", nlisthelp, 1, 1, 1, ls },
+ { "nmap", nmaphelp, 0, 0, 1, setnmap },
+ { "ntrans", ntranshelp, 0, 0, 1, setntrans },
+ { "open", connecthelp, 0, 0, 1, setpeer },
+ { "passive", passivehelp, 0, 0, 0, setpassive },
+
+ { "private", privatehelp, 0, 1, 1, setprivate },
+
+ { "prompt", prompthelp, 0, 0, 0, setprompt },
+
+ { "protect", protecthelp, 0, 1, 1, setdlevel },
+
+ { "proxy", proxyhelp, 0, 0, 1, doproxy },
+ { "put", sendhelp, 1, 1, 1, put },
+ { "pwd", pwdhelp, 0, 1, 1, pwd },
+ { "quit", quithelp, 0, 0, 0, quit },
+ { "quote", quotehelp, 1, 1, 1, quote },
+ { "recv", receivehelp, 1, 1, 1, get },
+ { "reget", regethelp, 1, 1, 1, reget },
+ { "remotehelp", remotehelp, 0, 1, 1, rmthelp },
+ { "rename", renamehelp, 0, 1, 1, renamefile },
+ { "reset", resethelp, 0, 1, 1, reset },
+ { "restart", restarthelp, 1, 1, 1, restart },
+ { "rmdir", rmdirhelp, 0, 1, 1, removedir },
+ { "runique", runiquehelp, 0, 0, 1, setrunique },
+
+ { "safe", safehelp, 0, 1, 1, setsafe },
+
+ { "send", sendhelp, 1, 1, 1, put },
+ { "sendport", porthelp, 0, 0, 0, setport },
+ { "site", sitehelp, 0, 1, 1, site },
+ { "status", statushelp, 0, 0, 1, status },
+ { "struct", structhelp, 0, 1, 1, setstruct },
+ { "sunique", suniquehelp, 0, 0, 1, setsunique },
+ { "tcpwindow", windowhelp, 0, 0, 0, settcpwindow },
+ { "tenex", tenexhelp, 0, 1, 1, settenex },
+ { "trace", tracehelp, 0, 0, 0, settrace },
+ { "type", typehelp, 0, 1, 1, settype },
+ { "user", userhelp, 0, 1, 1, user },
+ { "verbose", verbosehelp, 0, 0, 0, setverbose },
+ { "?", helphelp, 0, 0, 1, help },
+ { 0 },
+};
+
+int NCMDS = (sizeof (cmdtab) / sizeof (cmdtab[0])) - 1;
diff --git a/usr/src/cmd/cmd-inet/usr.bin/ftp/domacro.c b/usr/src/cmd/cmd-inet/usr.bin/ftp/domacro.c
new file mode 100644
index 0000000000..2d8421bbb3
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/ftp/domacro.c
@@ -0,0 +1,174 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2001 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <libintl.h>
+#include <stdlib.h>
+
+#include "ftp_var.h"
+
+#include <signal.h>
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+
+void
+domacro(int argc, char *argv[])
+{
+ register int i, j;
+ register char *cp1, *cp2;
+ int count = 2, loopflg = 0;
+ char line2[200];
+ struct cmd *c;
+ int len;
+
+ if (argc < 2) {
+ stop_timer();
+ (void) strcat(line, " ");
+ printf("(macro name) ");
+ (void) gets(&line[strlen(line)]);
+ reset_timer();
+ makeargv();
+ argc = margc;
+ argv = margv;
+ }
+ if (argc < 2) {
+ printf("Usage: %s macro_name.\n", argv[0]);
+ code = -1;
+ return;
+ }
+ for (i = 0; i < macnum; ++i) {
+ if (strncmp(argv[1], macros[i].mac_name, 9) == 0) {
+ break;
+ }
+ }
+ if (i == macnum) {
+ printf("'%s' macro not found.\n", argv[1]);
+ code = -1;
+ return;
+ }
+ (void) strcpy(line2, line);
+TOP:
+ cp1 = macros[i].mac_start;
+ while (cp1 != macros[i].mac_end) {
+ while (isspace(*cp1)) {
+ cp1++;
+ }
+ cp2 = line;
+ while (*cp1 != '\0') {
+ switch (*cp1) {
+ case '\\':
+ cp1++;
+ if ((len = mblen(cp1, MB_CUR_MAX)) <= 0)
+ len = 1;
+ memcpy(cp2, cp1, len);
+ cp2 += len;
+ cp1 += len;
+ break;
+
+ case '$':
+ if (isdigit(*(cp1+1))) {
+ j = 0;
+ while (isdigit(*++cp1))
+ j = 10 * j + *cp1 - '0';
+ if (argc - 2 >= j) {
+ (void) strcpy(cp2, argv[j+1]);
+ cp2 += strlen(argv[j+1]);
+ }
+ break;
+ }
+ if (*(cp1+1) == 'i') {
+ loopflg = 1;
+ cp1 += 2;
+ if (count < argc) {
+ (void) strcpy(cp2, argv[count]);
+ cp2 += strlen(argv[count]);
+ }
+ break;
+ }
+ /* intentional drop through */
+ default:
+ if ((len = mblen(cp1, MB_CUR_MAX)) <= 0)
+ len = 1;
+ memcpy(cp2, cp1, len);
+ cp2 += len;
+ cp1 += len;
+ break;
+ }
+ }
+ *cp2 = '\0';
+ makeargv();
+ if (margv[0] == NULL) {
+ code = -1;
+ return;
+ } else {
+ c = getcmd(margv[0]);
+ }
+ if (c == (struct cmd *)-1) {
+ printf("?Ambiguous command\n");
+ code = -1;
+ } else if (c == 0) {
+ printf("?Invalid command\n");
+ code = -1;
+ } else if (c->c_conn && !connected) {
+ printf("Not connected.\n");
+ code = -1;
+ } else {
+ if (verbose) {
+ printf("%s\n", line);
+ }
+ (*c->c_handler)(margc, margv);
+#define CTRL(c) ((c)&037)
+ if (bell && c->c_bell) {
+ (void) putchar(CTRL('g'));
+ }
+ (void) strcpy(line, line2);
+ makeargv();
+ argc = margc;
+ argv = margv;
+ }
+ if (cp1 != macros[i].mac_end) {
+ cp1++;
+ }
+ }
+ if (loopflg && ++count < argc) {
+ goto TOP;
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/ftp/ftp.c b/usr/src/cmd/cmd-inet/usr.bin/ftp/ftp.c
new file mode 100644
index 0000000000..f92dcc6759
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/ftp/ftp.c
@@ -0,0 +1,2487 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "ftp_var.h"
+#include <arpa/nameser.h>
+#include <sys/types.h>
+
+/*
+ * WRITE() returns:
+ * >0 no error
+ * -1 error, errorno is set
+ * -2 security error (secure_write() only)
+ */
+#define PUTC(x, y) secure_putc(x, y)
+#define READ(x, y, z) secure_read(x, y, z)
+#define WRITE(x, y, z) secure_write(x, y, z)
+
+static struct sockaddr_in6 data_addr;
+int data = -1;
+static int abrtflag = 0;
+static int ptflag = 0;
+int connected;
+static jmp_buf sendabort;
+static jmp_buf recvabort;
+static jmp_buf ptabort;
+static int ptabflg;
+static boolean_t pasv_refused;
+boolean_t eport_supported = B_TRUE;
+/*
+ * For IPv6 addresses, EPSV will be the default (rather than EPRT/LPRT).
+ * The EPSV/ERPT ftp protocols are specified in RFC 2428.
+ *
+ * Perform EPSV if passivemode is set and ipv6rem is TRUE.
+ */
+static boolean_t ipv6rem;
+int use_eprt = 0; /* Testing option that specifies EPRT by default */
+FILE *ctrl_in, *ctrl_out;
+
+static void abortsend(int sig);
+static void abortpt(int sig);
+static void proxtrans(char *cmd, char *local, char *remote);
+static void cmdabort(int sig);
+static int empty(struct fd_set *mask, int sec, int nfds);
+static void abortrecv(int sig);
+static int initconn(void);
+static FILE *dataconn(char *mode);
+static void ptransfer(char *direction, off_t bytes, hrtime_t t0,
+ hrtime_t t1, char *local, char *remote);
+static void psabort(int sig);
+static char *gunique(char *local);
+static const char *inet_ntop_native(int af, const void *src, char *dst,
+ size_t size);
+static ssize_t timedread(int fd, void *buf, size_t maxlen, int timeout);
+
+static int secure_command(char *);
+static int decode_reply(uchar_t *, int, uchar_t *, int, boolean_t *);
+
+static ssize_t bufcnt; /* number of bytes in buf[] */
+static char *bufp; /* next character in buf */
+static int buferr; /* last errno */
+static size_t bufsize;
+
+static void fdio_setbuf(char *buffer, size_t bufsize);
+static int fdio_fillbuf(int fd);
+static int fdio_error(int fd);
+#define fdio_getc(fd) (--bufcnt < 0 ? fdio_fillbuf((fd)) : \
+ ((unsigned char)*bufp++))
+
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#define NONZERO(x) ((x) == 0 ? 1 : (x))
+
+static void
+fdio_setbuf(char *buffer, size_t maxsize)
+{
+ buf = buffer;
+ bufp = buf;
+ bufcnt = 0;
+ buferr = 0;
+ bufsize = maxsize;
+}
+
+static int
+fdio_fillbuf(int fd)
+{
+ bufcnt = timedread(fd, buf, bufsize, timeout);
+ if (bufcnt < 0)
+ buferr = errno;
+ if (bufcnt <= 0)
+ return (EOF);
+ bufp = buf;
+ bufcnt--;
+ return ((unsigned char)*bufp++);
+}
+
+/*
+ * fdio_error - used on a file descriptor instead of ferror()
+ */
+
+/*ARGSUSED*/
+static int
+fdio_error(int fd)
+{
+ return (buferr);
+}
+
+/*
+ * timedread - read buffer (like "read"), but with timeout (in seconds)
+ */
+
+static ssize_t
+timedread(int fd, void *buf, size_t size, int timeout)
+{
+ struct fd_set mask;
+ struct timeval tv;
+ int err;
+
+ if (!timeout)
+ return (READ(fd, buf, size));
+
+ tv.tv_sec = (time_t)timeout;
+ tv.tv_usec = 0;
+
+ FD_ZERO(&mask);
+ FD_SET(fd, &mask);
+
+ err = select(fd + 1, &mask, NULL, NULL, &tv);
+ if (err == 0)
+ errno = ETIMEDOUT;
+ if (err <= 0)
+ return (-1);
+
+ return (READ(fd, buf, size));
+}
+
+
+char *
+hookup(char *host, char *service)
+{
+ struct addrinfo hints, *ai = NULL, *ai_head;
+ int s;
+ socklen_t len;
+ static char hostnamebuf[80];
+ struct in6_addr ipv6addr;
+ char abuf[INET6_ADDRSTRLEN];
+ int error_num;
+ int on = 1;
+
+ /*
+ * There appears to be a bug in getaddrinfo() where, if the
+ * ai_family is set to AF_INET6, and the host is a v4-only
+ * host, getaddrinfo() returns an error instead of returning
+ * an v4-mapped ipv6 address. Therefore the ai_family is
+ * set to AF_UNSPEC and any returned v4 addresses are
+ * explicitly mapped within ftp.
+ */
+ bzero((char *)&remctladdr, sizeof (remctladdr));
+ bzero((char *)&hints, sizeof (hints));
+ hints.ai_flags = AI_CANONNAME;
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+
+ error_num = getaddrinfo(host, service, &hints, &ai);
+ if (error_num != 0) {
+ if (error_num == EAI_AGAIN) {
+ (void) printf(
+ "%s: unknown host or invalid literal address "
+ "(try again later)\n", host);
+ } else {
+ (void) printf(
+ "%s: unknown host or invalid literal address\n",
+ host);
+ }
+ code = -1;
+ return ((char *)0);
+ }
+ ai_head = ai;
+
+
+ /*
+ * If ai_canonname is a IPv4-mapped IPv6 literal, we'll convert it to
+ * IPv4 literal address.
+ */
+ if (ai->ai_canonname != NULL &&
+ (inet_pton(AF_INET6, ai->ai_canonname, &ipv6addr) > 0) &&
+ IN6_IS_ADDR_V4MAPPED(&ipv6addr)) {
+ struct in_addr src4;
+ hostnamebuf[0] = '\0';
+ IN6_V4MAPPED_TO_INADDR(&ipv6addr, &src4);
+ (void) inet_ntop(AF_INET, &src4, hostnamebuf,
+ sizeof (hostnamebuf));
+
+ /*
+ * It can even be the case that the "host" supplied by the user
+ * can be a IPv4-mapped IPv6 literal. So, let's fix that too.
+ */
+ if ((inet_pton(AF_INET6, host, &ipv6addr) > 0) &&
+ IN6_IS_ADDR_V4MAPPED(&ipv6addr) &&
+ strlen(hostnamebuf) <= strlen(host)) {
+ (void) strlcpy(host, hostnamebuf, strlen(host) + 1);
+ }
+ } else {
+ reset_timer();
+ (void) strlcpy(hostnamebuf,
+ (ai->ai_canonname ? ai->ai_canonname : host),
+ sizeof (hostnamebuf));
+ }
+
+ hostname = hostnamebuf;
+ for (;;) {
+ int oerrno;
+
+ bcopy(ai->ai_addr, &remctladdr, ai->ai_addrlen);
+ if (ai->ai_addr->sa_family == AF_INET) {
+ IN6_INADDR_TO_V4MAPPED(
+ &(((struct sockaddr_in *)ai->ai_addr)->sin_addr),
+ &remctladdr.sin6_addr);
+ remctladdr.sin6_family = AF_INET6;
+ }
+
+ s = socket(AF_INET6, SOCK_STREAM, 0);
+ if (s < 0) {
+ perror("ftp: socket");
+ code = -1;
+ freeaddrinfo(ai_head);
+ return (0);
+ }
+ if (timeout && setsockopt(s, IPPROTO_TCP, TCP_ABORT_THRESHOLD,
+ (char *)&timeoutms, sizeof (timeoutms)) < 0 && debug)
+ perror("ftp: setsockopt (TCP_ABORT_THRESHOLD)");
+ reset_timer();
+
+ error_num = connect(s, (struct sockaddr *)&remctladdr,
+ sizeof (remctladdr));
+ oerrno = errno;
+ if (error_num >= 0)
+ break;
+
+ /*
+ * Maintain message behavior: only include the address in
+ * our error message if we have another one to try; if this
+ * is the last address on our list, just print the error.
+ */
+ if (ai->ai_next != NULL) {
+ (void) fprintf(stderr, "ftp: connect to address %s: ",
+ inet_ntop_native(ai->ai_addr->sa_family,
+ (void *)ai->ai_addr, abuf, sizeof (abuf)));
+ errno = oerrno;
+ perror((char *)0);
+ } else {
+ perror("ftp: connect");
+ code = -1;
+ freeaddrinfo(ai_head);
+ goto bad;
+ }
+ ai = ai->ai_next;
+ (void) fprintf(stdout, "Trying %s...\n",
+ inet_ntop_native(ai->ai_addr->sa_family,
+ (void *)ai->ai_addr, abuf, sizeof (abuf)));
+ (void) close(s);
+
+ }
+
+ /* Set ipv6rem to TRUE if control connection is a native IPv6 address */
+ if (IN6_IS_ADDR_V4MAPPED(&remctladdr.sin6_addr))
+ ipv6rem = B_FALSE;
+ else
+ ipv6rem = B_TRUE;
+
+
+ freeaddrinfo(ai_head);
+ ai = NULL;
+
+ /*
+ * Set passive mode flag on by default only if a native IPv6 address
+ * is being used -and- the use_eprt is not set.
+ */
+ if (ipv6rem == B_TRUE && use_eprt == 0)
+ passivemode = 1;
+
+ len = sizeof (myctladdr);
+ if (getsockname(s, (struct sockaddr *)&myctladdr, &len) < 0) {
+ perror("ftp: getsockname");
+ code = -1;
+ goto bad;
+ }
+ ctrl_in = fdopen(s, "r");
+ ctrl_out = fdopen(s, "w");
+ if (ctrl_in == NULL || ctrl_out == NULL) {
+ (void) fprintf(stderr, "ftp: fdopen failed.\n");
+ if (ctrl_in)
+ (void) fclose(ctrl_in);
+ if (ctrl_out)
+ (void) fclose(ctrl_out);
+ code = -1;
+ goto bad;
+ }
+ if (verbose)
+ (void) printf("Connected to %s.\n", hostname);
+ if (getreply(0) > 2) { /* read startup message from server */
+ if (ctrl_in)
+ (void) fclose(ctrl_in);
+ if (ctrl_out)
+ (void) fclose(ctrl_out);
+ ctrl_in = ctrl_out = NULL;
+ ctrl_in = ctrl_out = NULL;
+ code = -1;
+ goto bad;
+ }
+ if (setsockopt(s, SOL_SOCKET, SO_OOBINLINE, (char *)&on,
+ sizeof (on)) < 0 && debug)
+ perror("ftp: setsockopt (SO_OOBINLINE)");
+
+ return (hostname);
+bad:
+ (void) close(s);
+ return ((char *)0);
+}
+
+int
+login(char *host)
+{
+ char tmp[80];
+ char *user, *pass, *acct;
+ int n, aflag = 0;
+
+ user = pass = acct = 0;
+ if (ruserpass(host, &user, &pass, &acct) < 0) {
+ disconnect(0, NULL);
+ code = -1;
+ return (0);
+ }
+ if (user == NULL) {
+ char *myname = getlogin();
+
+ if (myname == NULL) {
+ struct passwd *pp = getpwuid(getuid());
+
+ if (pp != NULL)
+ myname = pp->pw_name;
+ }
+ stop_timer();
+ (void) printf("Name (%s:%s): ", host,
+ (myname == NULL) ? "" : myname);
+ *tmp = '\0';
+ if (fgets(tmp, sizeof (tmp) - 1, stdin) != NULL)
+ tmp[strlen(tmp) - 1] = '\0';
+ if (*tmp != '\0')
+ user = tmp;
+ else if (myname != NULL)
+ user = myname;
+ else
+ return (0);
+ }
+ n = command("USER %s", user);
+ if (n == CONTINUE) {
+ int oldclevel;
+ if (pass == NULL)
+ pass = mygetpass("Password:");
+ oldclevel = clevel;
+ clevel = PROT_P;
+ n = command("PASS %s", pass);
+ /* level may have changed */
+ if (clevel == PROT_P)
+ clevel = oldclevel;
+ }
+ if (n == CONTINUE) {
+ aflag++;
+ if (acct == NULL)
+ acct = mygetpass("Account:");
+ n = command("ACCT %s", acct);
+ }
+ if (n != COMPLETE) {
+ (void) fprintf(stderr, "Login failed.\n");
+ return (0);
+ }
+ if (!aflag && acct != NULL)
+ (void) command("ACCT %s", acct);
+ if (proxy)
+ return (1);
+ for (n = 0; n < macnum; ++n) {
+ if (strcmp("init", macros[n].mac_name) == 0) {
+ (void) strlcpy(line, "$init", sizeof (line));
+ makeargv();
+ domacro(margc, margv);
+ break;
+ }
+ }
+ return (1);
+}
+
+/*ARGSUSED*/
+static void
+cmdabort(int sig)
+{
+ (void) printf("\n");
+ (void) fflush(stdout);
+ abrtflag++;
+ if (ptflag)
+ longjmp(ptabort, 1);
+}
+
+int
+command(char *fmt, ...)
+{
+ int r;
+ void (*oldintr)();
+ va_list ap;
+ char command_buf[FTPBUFSIZ];
+
+ va_start(ap, fmt);
+ abrtflag = 0;
+ if (debug) {
+ (void) printf("---> ");
+ if (strncmp("PASS ", fmt, 5) == 0)
+ (void) printf("PASS XXXX");
+ else if (strncmp("ACCT ", fmt, 5) == 0)
+ (void) printf("ACCT XXXX");
+ else
+ (void) vfprintf(stdout, fmt, ap);
+ (void) printf("\n");
+ (void) fflush(stdout);
+ }
+ if (ctrl_out == NULL) {
+ perror("No control connection for command");
+ code = -1;
+ return (0);
+ }
+ oldintr = signal(SIGINT, cmdabort);
+ (void) vsnprintf(command_buf, FTPBUFSIZ, fmt, ap);
+ va_end(ap);
+
+again: if (secure_command(command_buf) == 0)
+ return (0);
+
+ cpend = 1;
+ r = getreply(strcmp(fmt, "QUIT") == 0);
+
+ if (r == 533 && clevel == PROT_P) {
+ (void) fprintf(stderr, "ENC command not supported at server; "
+ "retrying under MIC...\n");
+ clevel = PROT_S;
+ goto again;
+ }
+
+ if (abrtflag && oldintr != SIG_IGN)
+ (*oldintr)();
+ (void) signal(SIGINT, oldintr);
+ return (r);
+}
+
+/* Need to save reply reponse from server for use in EPSV mode */
+char reply_string[BUFSIZ];
+
+int
+getreply(int expecteof)
+{
+ /*
+ * 'code' is the 3 digit reply code, form xyz
+ * 'dig' counts the number of digits we are along in the code
+ * 'n' is the first digit of 'code'
+ * 4yz: resource unavailable
+ * 5yz: an error occurred, failure
+ * 6yz: protected reply (is_base64 == TRUE)
+ * 631 - base 64 encoded safe message
+ * 632 - base 64 encoded private message
+ * 633 - base 64 encoded confidential message
+ * 'c' is a wide char type, for international char sets
+ */
+ wint_t c;
+ int i, n;
+ int dig;
+ int originalcode = 0, continuation = 0;
+ void (*oldintr)();
+ int pflag = 0;
+ char *pt = pasv;
+ /*
+ * this is the input and output buffers needed for
+ * radix_encode()
+ */
+ unsigned char ibuf[FTPBUFSIZ];
+ unsigned char obuf[FTPBUFSIZ];
+ boolean_t is_base64;
+ int len;
+ char *cp;
+
+ if (!ctrl_in)
+ return (0);
+ oldintr = signal(SIGINT, cmdabort);
+
+ ibuf[0] = '\0';
+
+ if (reply_parse)
+ reply_ptr = reply_buf;
+
+ for (;;) {
+ obuf[0] = '\0';
+ dig = n = code = 0;
+ i = is_base64 = 0;
+ cp = reply_string;
+ reset_timer(); /* once per line */
+
+ while ((c = ibuf[0] ?
+ (wint_t)ibuf[i++] : fgetwc(ctrl_in)) != '\n') {
+
+ if (i >= FTPBUFSIZ)
+ break;
+
+ if (c == IAC) { /* handle telnet commands */
+ switch (c = fgetwc(ctrl_in)) {
+ case WILL:
+ case WONT:
+ c = fgetwc(ctrl_in);
+ (void) fprintf(ctrl_out, "%c%c%wc", IAC,
+ WONT, c);
+ (void) fflush(ctrl_out);
+ break;
+ case DO:
+ case DONT:
+ c = fgetwc(ctrl_in);
+ (void) fprintf(ctrl_out, "%c%c%wc", IAC,
+ DONT, c);
+ (void) fflush(ctrl_out);
+ break;
+ default:
+ break;
+ }
+ continue;
+ }
+ dig++;
+ if (c == EOF) {
+ if (expecteof) {
+ (void) signal(SIGINT, oldintr);
+ code = 221;
+ return (0);
+ }
+ lostpeer(0);
+ if (verbose) {
+ (void) printf(
+ "421 Service not available, remote"
+ " server has closed connection\n");
+ } else
+ (void) printf("Lost connection\n");
+ (void) fflush(stdout);
+ code = 421;
+ return (4);
+ }
+ if (n == 0)
+ n = c;
+
+ if (n == '6')
+ is_base64 = 1;
+
+ if ((auth_type != AUTHTYPE_NONE) && !ibuf[0] &&
+ (is_base64 || continuation)) {
+ /* start storing chars in obuf */
+ if (c != '\r' && dig > 4)
+ obuf[i++] = (char)c;
+ } else {
+ if ((auth_type != AUTHTYPE_NONE) && !ibuf[0] &&
+ dig == 1 && verbose)
+ (void) printf("Unauthenticated reply received "
+ "from server:\n");
+ if (reply_parse)
+ *reply_ptr++ = (char)c;
+ if (c != '\r' && (verbose > 0 ||
+ (verbose > -1 && n == '5' && dig > 4))) {
+ if (proxflag &&
+ (dig == 1 || dig == 5 && verbose == 0))
+ (void) printf("%s:", hostname);
+ (void) putwchar(c);
+ }
+ } /* endif auth_type && !ibuf[0] ... */
+
+ if ((auth_type != AUTHTYPE_NONE) && !ibuf[0] && !is_base64)
+ continue;
+
+ /* we are still extracting the 3 digit code */
+ if (dig < 4 && isascii(c) && isdigit(c))
+ code = code * 10 + (c - '0');
+
+ /* starting passive mode */
+ if (!pflag && code == 227)
+ pflag = 1;
+
+ /* start to store characters, when dig > 4 */
+ if (dig > 4 && pflag == 1 && isascii(c) && isdigit(c))
+ pflag = 2;
+ if (pflag == 2) {
+ if (c != '\r' && c != ')') {
+ /* the mb array is to deal with the wchar_t */
+ char mb[MB_LEN_MAX];
+ int avail;
+
+ /*
+ * space available in pasv[], accounting
+ * for trailing NULL
+ */
+ avail = &pasv[sizeof (pasv)] - pt - 1;
+
+ len = wctomb(mb, c);
+ if (len <= 0 && avail > 0) {
+ *pt++ = (unsigned char)c;
+ } else if (len > 0 && avail >= len) {
+ bcopy(mb, pt, (size_t)len);
+ pt += len;
+ } else {
+ /*
+ * no room in pasv[];
+ * close connection
+ */
+ (void) printf("\nReply too long - "
+ "closing connection\n");
+ lostpeer(0);
+ (void) fflush(stdout);
+ (void) signal(SIGINT, oldintr);
+ return (4);
+ }
+ } else {
+ *pt = '\0';
+ pflag = 3;
+ }
+ } /* endif pflag == 2 */
+ if (dig == 4 && c == '-' && !is_base64) {
+ if (continuation)
+ code = 0;
+ continuation++;
+ }
+ if (cp < &reply_string[sizeof (reply_string) - 1])
+ *cp++ = c;
+
+ } /* end while */
+
+ if ((auth_type != AUTHTYPE_NONE) && !ibuf[0] && !is_base64)
+ return (getreply(expecteof));
+
+ ibuf[0] = obuf[i] = '\0';
+
+ if (code && is_base64) {
+ boolean_t again = 0;
+ n = decode_reply(ibuf, sizeof (ibuf), obuf, n, &again);
+ if (again)
+ continue;
+ } else
+
+ if (verbose > 0 || verbose > -1 && n == '5') {
+ (void) putwchar(c);
+ (void) fflush(stdout);
+ }
+
+ if (continuation && code != originalcode) {
+ ibuf[0] = obuf[i] = '\0';
+ if (originalcode == 0)
+ originalcode = code;
+ continue;
+ }
+ *cp = '\0';
+ if (n != '1')
+ cpend = 0;
+ (void) signal(SIGINT, oldintr);
+ if (code == 421 || originalcode == 421)
+ lostpeer(0);
+ if (abrtflag && oldintr != cmdabort && oldintr != SIG_IGN)
+ (*oldintr)();
+
+ if (reply_parse) {
+ *reply_ptr = '\0';
+ if (reply_ptr = strstr(reply_buf, reply_parse)) {
+ reply_parse = reply_ptr + strlen(reply_parse);
+ if (reply_ptr = strpbrk(reply_parse, " \r"))
+ *reply_ptr = '\0';
+ } else
+ reply_parse = reply_ptr;
+ }
+
+ return (n - '0');
+ } /* end for */
+}
+
+static int
+empty(struct fd_set *mask, int sec, int nfds)
+{
+ struct timeval t;
+
+ reset_timer();
+ t.tv_sec = (time_t)sec;
+ t.tv_usec = 0;
+ return (select(nfds, mask, NULL, NULL, &t));
+}
+
+/*ARGSUSED*/
+static void
+abortsend(int sig)
+{
+ mflag = 0;
+ abrtflag = 0;
+ (void) printf("\nsend aborted\n");
+ (void) fflush(stdout);
+ longjmp(sendabort, 1);
+}
+
+void
+sendrequest(char *cmd, char *local, char *remote, int allowpipe)
+{
+ FILE *fin, *dout = 0;
+ int (*closefunc)();
+ void (*oldintr)(), (*oldintp)();
+ off_t bytes = 0, hashbytes = HASHSIZ;
+ int c;
+ /*
+ * d >= 0 if there is no error
+ * -1 if there was a normal file i/o error
+ * -2 if there was a security error
+ */
+ int d;
+ struct stat st;
+ hrtime_t start, stop;
+ char *dmode;
+
+ if (proxy) {
+ proxtrans(cmd, local, remote);
+ return;
+ }
+ closefunc = NULL;
+ oldintr = NULL;
+ oldintp = NULL;
+ dmode = "w";
+ if (setjmp(sendabort)) {
+ while (cpend) {
+ (void) getreply(0);
+ }
+ if (data >= 0) {
+ (void) close(data);
+ data = -1;
+ }
+ if (oldintr)
+ (void) signal(SIGINT, oldintr);
+ if (oldintp)
+ (void) signal(SIGPIPE, oldintp);
+ code = -1;
+ restart_point = 0;
+ return;
+ }
+ oldintr = signal(SIGINT, abortsend);
+ if (strcmp(local, "-") == 0)
+ fin = stdin;
+ else if (allowpipe && *local == '|') {
+ oldintp = signal(SIGPIPE, SIG_IGN);
+ fin = mypopen(local + 1, "r");
+ if (fin == NULL) {
+ perror(local + 1);
+ (void) signal(SIGINT, oldintr);
+ (void) signal(SIGPIPE, oldintp);
+ code = -1;
+ restart_point = 0;
+ return;
+ }
+ closefunc = mypclose;
+ } else {
+ fin = fopen(local, "r");
+ if (fin == NULL) {
+ perror(local);
+ (void) signal(SIGINT, oldintr);
+ code = -1;
+ restart_point = 0;
+ return;
+ }
+ closefunc = fclose;
+ if (fstat(fileno(fin), &st) < 0 ||
+ (st.st_mode&S_IFMT) != S_IFREG) {
+ (void) fprintf(stdout,
+ "%s: not a plain file.\n", local);
+ (void) signal(SIGINT, oldintr);
+ code = -1;
+ (void) fclose(fin);
+ restart_point = 0;
+ return;
+ }
+ }
+ if (initconn()) {
+ (void) signal(SIGINT, oldintr);
+ if (oldintp)
+ (void) signal(SIGPIPE, oldintp);
+ code = -1;
+ if (closefunc != NULL)
+ (*closefunc)(fin);
+ restart_point = 0;
+ return;
+ }
+ if (setjmp(sendabort))
+ goto abort;
+ if ((restart_point > 0) &&
+ (strcmp(cmd, "STOR") == 0 || strcmp(cmd, "APPE") == 0)) {
+ if (fseeko(fin, restart_point, SEEK_SET) < 0) {
+ perror(local);
+ if (closefunc != NULL)
+ (*closefunc)(fin);
+ restart_point = 0;
+ return;
+ }
+ if (command("REST %lld", (longlong_t)restart_point)
+ != CONTINUE) {
+ if (closefunc != NULL)
+ (*closefunc)(fin);
+ restart_point = 0;
+ return;
+ }
+ dmode = "r+w";
+ }
+ restart_point = 0;
+ if (remote) {
+ if (command("%s %s", cmd, remote) != PRELIM) {
+ (void) signal(SIGINT, oldintr);
+ if (oldintp)
+ (void) signal(SIGPIPE, oldintp);
+ if (closefunc != NULL)
+ (*closefunc)(fin);
+ if (data >= 0) {
+ (void) close(data);
+ data = -1;
+ }
+ return;
+ }
+ } else
+ if (command("%s", cmd) != PRELIM) {
+ (void) signal(SIGINT, oldintr);
+ if (oldintp)
+ (void) signal(SIGPIPE, oldintp);
+ if (closefunc != NULL)
+ (*closefunc)(fin);
+ if (data >= 0) {
+ (void) close(data);
+ data = -1;
+ }
+ return;
+ }
+ dout = dataconn(dmode);
+ if (dout == NULL)
+ goto abort;
+ stop_timer();
+ oldintp = signal(SIGPIPE, SIG_IGN);
+ start = gethrtime();
+ switch (type) {
+
+ case TYPE_I:
+ case TYPE_L:
+ errno = d = 0;
+ while ((c = read(fileno(fin), buf, FTPBUFSIZ)) > 0) {
+ if ((d = WRITE(fileno(dout), buf, c)) < 0)
+ break;
+ bytes += c;
+ if (hash) {
+ while (bytes >= hashbytes) {
+ (void) putchar('#');
+ hashbytes += HASHSIZ;
+ }
+ (void) fflush(stdout);
+ }
+ }
+ if (hash && bytes > 0) {
+ if (bytes < hashbytes)
+ (void) putchar('#');
+ (void) putchar('\n');
+ (void) fflush(stdout);
+ }
+ if (c < 0)
+ perror(local);
+
+ if (d >= 0)
+ d = secure_flush(fileno(dout));
+
+ if (d < 0) {
+ if ((d == -1) && (errno != EPIPE))
+ perror("netout");
+ bytes = -1;
+ }
+ break;
+
+ case TYPE_A:
+ while ((c = getc(fin)) != EOF) {
+ if (c == '\n') {
+ while (hash && (bytes >= hashbytes)) {
+ (void) putchar('#');
+ (void) fflush(stdout);
+ hashbytes += HASHSIZ;
+ }
+ if (ferror(dout) || PUTC('\r', dout) < 0)
+ break;
+ bytes++;
+ }
+
+ if (PUTC(c, dout) < 0)
+ break;
+ bytes++;
+#ifdef notdef
+ if (c == '\r') {
+ /* this violates rfc */
+ (void) PUTC('\0', dout);
+ bytes++;
+ }
+#endif
+ }
+ if (hash && bytes > 0) {
+ if (bytes < hashbytes)
+ (void) putchar('#');
+ (void) putchar('\n');
+ (void) fflush(stdout);
+ }
+ if (ferror(fin))
+ perror(local);
+
+ d = ferror(dout) ? -1 : 0;
+ if (d == 0)
+ d = secure_flush(fileno(dout));
+
+ if (d < 0) {
+ if ((d == -1) && (errno != EPIPE))
+ perror("netout");
+ bytes = -1;
+ }
+ break;
+ }
+ reset_timer();
+ if (closefunc != NULL)
+ (*closefunc)(fin);
+ if (ctrl_in != NULL) {
+ int dfn = fileno(dout);
+ int nfds = fileno(ctrl_in);
+ fd_set mask;
+
+ /*
+ * There could be data not yet written to dout,
+ * in the stdio buffer; so, before a shutdown()
+ * on further sends, do fflush(dout)
+ */
+ (void) fflush(dout);
+
+ /* sending over; shutdown sending on dfn */
+ (void) shutdown(dfn, SHUT_WR);
+ FD_ZERO(&mask);
+ FD_SET(dfn, &mask);
+ FD_SET(nfds, &mask);
+ nfds = MAX(dfn, nfds);
+
+ /*
+ * Wait for remote end to either close data socket
+ * or ack that we've closed our end; it doesn't
+ * matter which happens first.
+ */
+ (void) select(nfds + 1, &mask, NULL, NULL, NULL);
+ }
+ (void) fclose(dout); data = -1;
+ stop = gethrtime();
+ (void) getreply(0);
+ (void) signal(SIGINT, oldintr);
+ if (oldintp)
+ (void) signal(SIGPIPE, oldintp);
+
+ /*
+ * Only print the transfer successful message if the code returned
+ * from remote is 226 or 250. All other codes are error codes.
+ */
+ if ((bytes > 0) && verbose && ((code == 226) || (code == 250)))
+ ptransfer("sent", bytes, start, stop, local, remote);
+ if (!ctrl_in)
+ (void) printf("Lost connection\n");
+ return;
+abort:
+ (void) signal(SIGINT, oldintr);
+ if (oldintp)
+ (void) signal(SIGPIPE, oldintp);
+ if (!cpend) {
+ code = -1;
+ return;
+ }
+ if (data >= 0) {
+ (void) close(data);
+ data = -1;
+ }
+ if (dout) {
+ (void) fclose(dout);
+ data = -1;
+ }
+ (void) getreply(0);
+ code = -1;
+ if (closefunc != NULL && fin != NULL)
+ (*closefunc)(fin);
+ stop = gethrtime();
+ /*
+ * Only print the transfer successful message if the code returned
+ * from remote is 226 or 250. All other codes are error codes.
+ */
+ if ((bytes > 0) && verbose && ((code == 226) || (code == 250)))
+ ptransfer("sent", bytes, start, stop, local, remote);
+ if (!ctrl_in)
+ (void) printf("Lost connection\n");
+ restart_point = 0;
+}
+
+/*ARGSUSED*/
+static void
+abortrecv(int sig)
+{
+ mflag = 0;
+ abrtflag = 0;
+ (void) printf("\n");
+ (void) fflush(stdout);
+ longjmp(recvabort, 1);
+}
+
+void
+recvrequest(char *cmd, char *local, char *remote, char *mode, int allowpipe)
+{
+ FILE *fout, *din = 0;
+ int (*closefunc)();
+ void (*oldintr)(), (*oldintp)();
+ int oldverbose, oldtype = 0, tcrflag, nfnd;
+ char msg;
+ off_t bytes = 0, hashbytes = HASHSIZ;
+ struct fd_set mask;
+ int c, d, n;
+ hrtime_t start, stop;
+ int errflg = 0;
+ int infd;
+ int nfds;
+ int retrcmd;
+
+ retrcmd = (strcmp(cmd, "RETR") == 0);
+ if (proxy && retrcmd) {
+ proxtrans(cmd, local, remote);
+ return;
+ }
+ closefunc = NULL;
+ oldintr = NULL;
+ oldintp = NULL;
+ tcrflag = !crflag && retrcmd;
+ if (setjmp(recvabort)) {
+ while (cpend) {
+ (void) getreply(0);
+ }
+ if (data >= 0) {
+ (void) close(data);
+ data = -1;
+ }
+ if (oldintr)
+ (void) signal(SIGINT, oldintr);
+ code = -1;
+ return;
+ }
+ oldintr = signal(SIGINT, abortrecv);
+ if (local != NULL &&
+ strcmp(local, "-") != 0 &&
+ (*local != '|' || !allowpipe)) {
+ if (access(local, W_OK) < 0) {
+ char *dir = rindex(local, '/');
+ int file_errno = errno;
+
+ if (file_errno != ENOENT && file_errno != EACCES) {
+ perror(local);
+ (void) signal(SIGINT, oldintr);
+ code = -1;
+ return;
+ }
+ if ((dir != NULL) && (dir != local))
+ *dir = 0;
+ if (dir == local)
+ d = access("/", W_OK);
+ else
+ d = access(dir ? local : ".", W_OK);
+ if ((dir != NULL) && (dir != local))
+ *dir = '/';
+ if (d < 0) {
+ perror(local);
+ (void) signal(SIGINT, oldintr);
+ code = -1;
+ return;
+ }
+ if (!runique && file_errno == EACCES) {
+ errno = file_errno;
+ perror(local);
+ (void) signal(SIGINT, oldintr);
+ code = -1;
+ return;
+ }
+ if (runique && file_errno == EACCES &&
+ (local = gunique(local)) == NULL) {
+ (void) signal(SIGINT, oldintr);
+ code = -1;
+ return;
+ }
+ } else if (runique && (local = gunique(local)) == NULL) {
+ (void) signal(SIGINT, oldintr);
+ code = -1;
+ return;
+ }
+ }
+ if (initconn()) {
+ (void) signal(SIGINT, oldintr);
+ code = -1;
+ return;
+ }
+ if (setjmp(recvabort))
+ goto abort;
+ if (!retrcmd && type != TYPE_A) {
+ oldtype = type;
+ oldverbose = verbose;
+ if (!debug)
+ verbose = 0;
+ setascii(0, NULL);
+ verbose = oldverbose;
+ }
+ if ((restart_point > 0) && retrcmd &&
+ command("REST %lld", (longlong_t)restart_point) != CONTINUE) {
+ return;
+ }
+ if (remote) {
+ if (command("%s %s", cmd, remote) != PRELIM) {
+ (void) signal(SIGINT, oldintr);
+ if (oldtype) {
+ if (!debug)
+ verbose = 0;
+ switch (oldtype) {
+ case TYPE_I:
+ setbinary(0, NULL);
+ break;
+ case TYPE_E:
+ setebcdic(0, NULL);
+ break;
+ case TYPE_L:
+ settenex(0, NULL);
+ break;
+ }
+ verbose = oldverbose;
+ }
+ return;
+ }
+ } else {
+ if (command("%s", cmd) != PRELIM) {
+ (void) signal(SIGINT, oldintr);
+ if (oldtype) {
+ if (!debug)
+ verbose = 0;
+ switch (oldtype) {
+ case TYPE_I:
+ setbinary(0, NULL);
+ break;
+ case TYPE_E:
+ setebcdic(0, NULL);
+ break;
+ case TYPE_L:
+ settenex(0, NULL);
+ break;
+ }
+ verbose = oldverbose;
+ }
+ return;
+ }
+ }
+ din = dataconn("r");
+ if (din == NULL)
+ goto abort;
+
+ if (local == NULL) {
+ fout = tmp_nlst;
+ } else if (strcmp(local, "-") == 0) {
+ fout = stdout;
+ } else if (allowpipe && *local == '|') {
+ oldintp = signal(SIGPIPE, SIG_IGN);
+ fout = mypopen(local + 1, "w");
+ if (fout == NULL) {
+ perror(local+1);
+ goto abort;
+ }
+ closefunc = mypclose;
+ } else {
+ fout = fopen(local, mode);
+ if (fout == NULL) {
+ perror(local);
+ goto abort;
+ }
+ closefunc = fclose;
+ }
+ start = gethrtime();
+ stop_timer();
+ switch (type) {
+
+ case TYPE_I:
+ case TYPE_L:
+ if ((restart_point > 0) && retrcmd &&
+ lseek(fileno(fout), restart_point, SEEK_SET) < 0) {
+ perror(local);
+ goto abort;
+ }
+ errno = d = 0;
+ infd = fileno(din);
+ while ((c = timedread(infd, buf, FTPBUFSIZ, timeout)) > 0) {
+ for (n = 0; n < c; n += d) {
+ d = write(fileno(fout), &buf[n], c - n);
+ if (d == -1)
+ goto writeerr;
+ }
+ bytes += c;
+ if (hash) {
+ while (bytes >= hashbytes) {
+ (void) putchar('#');
+ hashbytes += HASHSIZ;
+ }
+ (void) fflush(stdout);
+ }
+ }
+ if (hash && bytes > 0) {
+ if (bytes < hashbytes)
+ (void) putchar('#');
+ (void) putchar('\n');
+ (void) fflush(stdout);
+ }
+ if (c < 0) {
+ errflg = 1;
+ perror("netin");
+ }
+ if ((d < 0) || ((c == 0) && (fsync(fileno(fout)) == -1))) {
+writeerr:
+ errflg = 1;
+ perror(local);
+ }
+ break;
+
+ case TYPE_A:
+ if ((restart_point > 0) && retrcmd) {
+ int c;
+ off_t i = 0;
+
+ if (fseek(fout, 0L, SEEK_SET) < 0) {
+ perror(local);
+ goto abort;
+ }
+ while (i++ < restart_point) {
+ if ((c = getc(fout)) == EOF) {
+ if (ferror(fout))
+ perror(local);
+ else
+ (void) fprintf(stderr,
+ "%s: Unexpected end of file\n",
+ local);
+ goto abort;
+ }
+ if (c == '\n')
+ i++;
+ }
+ if (fseeko(fout, 0L, SEEK_CUR) < 0) {
+ perror(local);
+ goto abort;
+ }
+ }
+ fdio_setbuf(buf, FTPBUFSIZ);
+ infd = fileno(din);
+ while ((c = fdio_getc(infd)) != EOF) {
+ while (c == '\r') {
+ while (hash && (bytes >= hashbytes)) {
+ (void) putchar('#');
+ (void) fflush(stdout);
+ hashbytes += HASHSIZ;
+ }
+ bytes++;
+
+ if ((c = fdio_getc(infd)) != '\n' || tcrflag) {
+ if (ferror(fout))
+ break;
+ if (putc('\r', fout) == EOF)
+ goto writer_ascii_err;
+ }
+#ifdef notdef
+ if (c == '\0') {
+ bytes++;
+ continue;
+ }
+#endif
+ if (c == EOF)
+ goto endread;
+ }
+ if (putc(c, fout) == EOF)
+ goto writer_ascii_err;
+ bytes++;
+ }
+endread:
+ if (hash && bytes > 0) {
+ if (bytes < hashbytes)
+ (void) putchar('#');
+ (void) putchar('\n');
+ (void) fflush(stdout);
+ }
+ if (fdio_error(infd)) {
+ errflg = 1;
+ perror("netin");
+ }
+ if ((fflush(fout) == EOF) || ferror(fout) ||
+ (fsync(fileno(fout)) == -1)) {
+writer_ascii_err:
+ errflg = 1;
+ perror(local);
+ }
+ break;
+ }
+ reset_timer();
+ if (closefunc != NULL)
+ (*closefunc)(fout);
+ (void) signal(SIGINT, oldintr);
+ if (oldintp)
+ (void) signal(SIGPIPE, oldintp);
+ (void) fclose(din); data = -1;
+ stop = gethrtime();
+ (void) getreply(0);
+ if (bytes > 0 && verbose && !errflg)
+ ptransfer("received", bytes, start, stop, local, remote);
+ if (!ctrl_in)
+ (void) printf("Lost connection\n");
+ if (oldtype) {
+ if (!debug)
+ verbose = 0;
+ switch (oldtype) {
+ case TYPE_I:
+ setbinary(0, NULL);
+ break;
+ case TYPE_E:
+ setebcdic(0, NULL);
+ break;
+ case TYPE_L:
+ settenex(0, NULL);
+ break;
+ }
+ verbose = oldverbose;
+ }
+ return;
+abort:
+
+/* abort using RFC959 recommended IP, SYNC sequence */
+
+ stop = gethrtime();
+ if (oldintp)
+ (void) signal(SIGPIPE, oldintp);
+ (void) signal(SIGINT, SIG_IGN);
+ if (!cpend) {
+ code = -1;
+ (void) signal(SIGINT, oldintr);
+ return;
+ }
+
+ (void) fprintf(ctrl_out, "%c%c", IAC, IP);
+ (void) fflush(ctrl_out);
+ msg = (char)IAC;
+ /*
+ * send IAC in urgent mode instead of DM because UNIX places oob
+ * mark after urgent byte rather than before as now is protocol
+ */
+ if (send(fileno(ctrl_out), &msg, 1, MSG_OOB) != 1) {
+ perror("abort");
+ }
+ (void) fprintf(ctrl_out, "%cABOR\r\n", DM);
+ (void) fflush(ctrl_out);
+ nfds = fileno(ctrl_in) + 1;
+ FD_ZERO(&mask);
+ FD_SET(fileno(ctrl_in), &mask);
+ if (din) {
+ FD_SET(fileno(din), &mask);
+ nfds = MAX(fileno(din) + 1, nfds);
+ }
+ if ((nfnd = empty(&mask, 10, nfds)) <= 0) {
+ if (nfnd < 0) {
+ perror("abort");
+ }
+ code = -1;
+ lostpeer(0);
+ }
+ if (din && FD_ISSET(fileno(din), &mask)) {
+ do {
+ reset_timer();
+ } while ((c = read(fileno(din), buf, FTPBUFSIZ)) > 0);
+ }
+ if ((c = getreply(0)) == ERROR && code == 552) {
+ /* needed for nic style abort */
+ if (data >= 0) {
+ (void) close(data);
+ data = -1;
+ }
+ (void) getreply(0);
+ }
+ if (oldtype) {
+ if (!debug)
+ verbose = 0;
+ switch (oldtype) {
+ case TYPE_I:
+ setbinary(0, NULL);
+ break;
+ case TYPE_E:
+ setebcdic(0, NULL);
+ break;
+ case TYPE_L:
+ settenex(0, NULL);
+ break;
+ }
+ verbose = oldverbose;
+ }
+ (void) getreply(0);
+ code = -1;
+ if (data >= 0) {
+ (void) close(data);
+ data = -1;
+ }
+ if (closefunc != NULL && fout != NULL)
+ (*closefunc)(fout);
+ if (din) {
+ (void) fclose(din);
+ data = -1;
+ }
+ if (bytes > 0 && verbose)
+ ptransfer("received", bytes, start, stop, local, remote);
+ if (!ctrl_in)
+ (void) printf("Lost connection\n");
+ (void) signal(SIGINT, oldintr);
+}
+
+/*
+ * Need to start a listen on the data channel
+ * before we send the command, otherwise the
+ * server's connect may fail.
+ */
+
+static int
+initconn(void)
+{
+ unsigned char *p, *a;
+ int result, tmpno = 0;
+ int on = 1;
+ socklen_t len;
+ int v4_addr;
+ char *c, *c2, delm;
+ in_port_t ports;
+
+ pasv_refused = B_FALSE;
+ if (passivemode) {
+ data = socket(AF_INET6, SOCK_STREAM, 0);
+ if (data < 0) {
+ perror("socket");
+ return (1);
+ }
+ if (timeout && setsockopt(data, IPPROTO_TCP,
+ TCP_ABORT_THRESHOLD, (char *)&timeoutms,
+ sizeof (timeoutms)) < 0 && debug)
+ perror("ftp: setsockopt (TCP_ABORT_THRESHOLD)");
+ if ((options & SO_DEBUG) &&
+ setsockopt(data, SOL_SOCKET, SO_DEBUG, (char *)&on,
+ sizeof (on)) < 0)
+ perror("setsockopt (ignored)");
+ /*
+ * Use the system wide default send and receive buffer sizes
+ * unless one has been specified.
+ */
+ if (tcpwindowsize) {
+ if (setsockopt(data, SOL_SOCKET, SO_SNDBUF,
+ (char *)&tcpwindowsize, sizeof (tcpwindowsize)) < 0)
+ perror("ftp: setsockopt (SO_SNDBUF - ignored)");
+ if (setsockopt(data, SOL_SOCKET, SO_RCVBUF,
+ (char *)&tcpwindowsize, sizeof (tcpwindowsize)) < 0)
+ perror("ftp: setsockopt (SO_RCVBUF - ignored)");
+ }
+
+ data_addr = remctladdr;
+
+ if (ipv6rem == B_TRUE) {
+ if (command("EPSV") != COMPLETE) {
+ (void) fprintf(stderr,
+ "Passive mode refused. Try EPRT\n");
+ pasv_refused = B_TRUE;
+ goto noport;
+ }
+
+ /*
+ * Get the data port from reply string from the
+ * server. The format of the reply string is:
+ * 229 Entering Extended Passive Mode (|||port|)
+ * where | is the delimiter being used.
+ */
+ c = strchr(reply_string, '(');
+ c2 = strchr(reply_string, ')');
+ if (c == NULL || c2 == NULL) {
+ (void) fprintf(stderr, "Extended passive mode"
+ "parsing failure.\n");
+ goto bad;
+ }
+ *(c2 - 1) = NULL;
+ /* Delimiter is the next char in the reply string */
+ delm = *(++c);
+ while (*c == delm) {
+ if (!*(c++)) {
+ (void) fprintf(stderr,
+ "Extended passive mode"
+ "parsing failure.\n");
+ goto bad;
+ }
+ }
+ /* assign the port for data connection */
+ ports = (in_port_t)atoi(c);
+ data_addr.sin6_port = htons(ports);
+ } else {
+ int a1, a2, a3, a4, p1, p2;
+
+ if (command("PASV") != COMPLETE) {
+ (void) fprintf(stderr,
+ "Passive mode refused. Try PORT\n");
+ pasv_refused = B_TRUE;
+ goto noport;
+ }
+
+ /*
+ * Get the data port from reply string from the
+ * server. The format of the reply string is:
+ * 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2)
+ */
+ if (sscanf(pasv, "%d,%d,%d,%d,%d,%d",
+ &a1, &a2, &a3, &a4, &p1, &p2) != 6) {
+ (void) fprintf(stderr,
+ "Passive mode parsing failure.\n");
+ goto bad;
+ }
+ /*
+ * Set the supplied address and port in an
+ * IPv4-mapped IPv6 address.
+ */
+ a = (unsigned char *)&data_addr.sin6_addr +
+ sizeof (struct in6_addr) -
+ sizeof (struct in_addr);
+#define UC(b) ((b)&0xff)
+ a[0] = UC(a1);
+ a[1] = UC(a2);
+ a[2] = UC(a3);
+ a[3] = UC(a4);
+ p = (unsigned char *)&data_addr.sin6_port;
+ p[0] = UC(p1);
+ p[1] = UC(p2);
+ }
+
+ if (connect(data, (struct sockaddr *)&data_addr,
+ sizeof (data_addr)) < 0) {
+ perror("connect");
+ goto bad;
+ }
+ return (0);
+ }
+
+noport:
+ data_addr = myctladdr;
+ if (sendport)
+ data_addr.sin6_port = 0; /* let system pick one */
+
+ if (data != -1)
+ (void) close(data);
+ data = socket(AF_INET6, SOCK_STREAM, 0);
+ if (data < 0) {
+ perror("ftp: socket");
+ if (tmpno)
+ sendport = 1;
+ return (1);
+ }
+ if (!sendport)
+ if (setsockopt(data, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&on, sizeof (on)) < 0) {
+ perror("ftp: setsockopt (SO_REUSEADDR)");
+ goto bad;
+ }
+ if (bind(data,
+ (struct sockaddr *)&data_addr, sizeof (data_addr)) < 0) {
+ perror("ftp: bind");
+ goto bad;
+ }
+ if (timeout && setsockopt(data, IPPROTO_TCP, TCP_ABORT_THRESHOLD,
+ (char *)&timeoutms, sizeof (timeoutms)) < 0 && debug)
+ perror("ftp: setsockopt (TCP_ABORT_THRESHOLD)");
+ if (options & SO_DEBUG &&
+ setsockopt(data, SOL_SOCKET, SO_DEBUG,
+ (char *)&on, sizeof (on)) < 0)
+ perror("ftp: setsockopt (SO_DEBUG - ignored)");
+ /*
+ * Use the system wide default send and receive buffer sizes unless
+ * one has been specified.
+ */
+ if (tcpwindowsize) {
+ if (setsockopt(data, SOL_SOCKET, SO_SNDBUF,
+ (char *)&tcpwindowsize, sizeof (tcpwindowsize)) < 0)
+ perror("ftp: setsockopt (SO_SNDBUF - ignored)");
+ if (setsockopt(data, SOL_SOCKET, SO_RCVBUF,
+ (char *)&tcpwindowsize, sizeof (tcpwindowsize)) < 0)
+ perror("ftp: setsockopt (SO_RCVBUF - ignored)");
+ }
+ len = sizeof (data_addr);
+ if (getsockname(data, (struct sockaddr *)&data_addr, &len) < 0) {
+ perror("ftp: getsockname");
+ goto bad;
+ }
+
+ v4_addr = IN6_IS_ADDR_V4MAPPED(&data_addr.sin6_addr);
+ if (listen(data, 1) < 0)
+ perror("ftp: listen");
+
+ if (sendport) {
+ a = (unsigned char *)&data_addr.sin6_addr;
+ p = (unsigned char *)&data_addr.sin6_port;
+ if (v4_addr) {
+ result =
+ command("PORT %d,%d,%d,%d,%d,%d",
+ UC(a[12]), UC(a[13]), UC(a[14]), UC(a[15]),
+ UC(p[0]), UC(p[1]));
+ } else {
+ char hname[INET6_ADDRSTRLEN];
+
+ result = COMPLETE + 1;
+ /*
+ * if on previous try to server, it was
+ * determined that the server doesn't support
+ * EPRT, don't bother trying again. Just try
+ * LPRT.
+ */
+ if (eport_supported == B_TRUE) {
+ if (inet_ntop(AF_INET6, &data_addr.sin6_addr,
+ hname, sizeof (hname)) != NULL) {
+ result = command("EPRT |%d|%s|%d|", 2,
+ hname, htons(data_addr.sin6_port));
+ if (result != COMPLETE)
+ eport_supported = B_FALSE;
+ }
+ }
+ /* Try LPRT */
+ if (result != COMPLETE) {
+ result = command(
+"LPRT %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",
+6, 16,
+UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
+UC(a[4]), UC(a[5]), UC(a[6]), UC(a[7]),
+UC(a[8]), UC(a[9]), UC(a[10]), UC(a[11]),
+UC(a[12]), UC(a[13]), UC(a[14]), UC(a[15]),
+2, UC(p[0]), UC(p[1]));
+ }
+ }
+
+ if (result == ERROR && sendport == -1) {
+ sendport = 0;
+ tmpno = 1;
+ goto noport;
+ }
+ return (result != COMPLETE);
+ }
+ if (tmpno)
+ sendport = 1;
+ return (0);
+bad:
+ (void) close(data), data = -1;
+ if (tmpno)
+ sendport = 1;
+ return (1);
+}
+
+static FILE *
+dataconn(char *mode)
+{
+ struct sockaddr_in6 from;
+ int s;
+ socklen_t fromlen = sizeof (from);
+
+ reset_timer();
+ if (passivemode && !pasv_refused)
+ return (fdopen(data, mode));
+
+ s = accept(data, (struct sockaddr *)&from, &fromlen);
+ if (s < 0) {
+ perror("ftp: accept");
+ (void) close(data), data = -1;
+ return (NULL);
+ }
+ (void) close(data);
+ data = s;
+ return (fdopen(data, mode));
+}
+
+static void
+ptransfer(char *direction, off_t bytes, hrtime_t t0,
+ hrtime_t t1, char *local, char *remote)
+{
+ hrtime_t td; /* nanoseconds in a 64 bit int */
+ double s, bs;
+
+ td = t1 - t0;
+ s = (double)td / 1000000000.0; /* seconds */
+ bs = (double)bytes / NONZERO(s);
+ if (local && *local != '-')
+ (void) printf("local: %s ", local);
+ if (remote)
+ (void) printf("remote: %s\n", remote);
+ (void) printf("%lld bytes %s in %.2g seconds (%.2f Kbytes/s)\n",
+ (longlong_t)bytes, direction, s, bs / 1024.0);
+}
+
+/*ARGSUSED*/
+static void
+psabort(int sig)
+{
+ abrtflag++;
+}
+
+void
+pswitch(int flag)
+{
+ void (*oldintr)();
+ static struct comvars {
+ int connect;
+ char name[MAXHOSTNAMELEN];
+ struct sockaddr_in6 mctl;
+ struct sockaddr_in6 hctl;
+ FILE *in;
+ FILE *out;
+ int tpe;
+ int cpnd;
+ int sunqe;
+ int runqe;
+ int mcse;
+ int ntflg;
+ char nti[17];
+ char nto[17];
+ int mapflg;
+ char mi[MAXPATHLEN];
+ char mo[MAXPATHLEN];
+ int authtype;
+ int clvl;
+ int dlvl;
+ } proxstruct, tmpstruct;
+ struct comvars *ip, *op;
+
+ abrtflag = 0;
+ oldintr = signal(SIGINT, psabort);
+ if (flag) {
+ if (proxy)
+ return;
+ ip = &tmpstruct;
+ op = &proxstruct;
+ proxy++;
+ } else {
+ if (!proxy)
+ return;
+ ip = &proxstruct;
+ op = &tmpstruct;
+ proxy = 0;
+ }
+ ip->connect = connected;
+ connected = op->connect;
+ if (hostname)
+ (void) strlcpy(ip->name, hostname, sizeof (ip->name));
+ else
+ ip->name[0] = 0;
+ hostname = op->name;
+ ip->hctl = remctladdr;
+ remctladdr = op->hctl;
+ ip->mctl = myctladdr;
+ myctladdr = op->mctl;
+ ip->in = ctrl_in;
+ ctrl_in = op->in;
+ ip->out = ctrl_out;
+ ctrl_out = op->out;
+ ip->tpe = type;
+ type = op->tpe;
+ if (!type)
+ type = 1;
+ ip->cpnd = cpend;
+ cpend = op->cpnd;
+ ip->sunqe = sunique;
+ sunique = op->sunqe;
+ ip->runqe = runique;
+ runique = op->runqe;
+ ip->mcse = mcase;
+ mcase = op->mcse;
+ ip->ntflg = ntflag;
+ ntflag = op->ntflg;
+ (void) strlcpy(ip->nti, ntin, sizeof (ip->nti));
+ (void) strlcpy(ntin, op->nti, sizeof (ntin));
+ (void) strlcpy(ip->nto, ntout, sizeof (ip->nto));
+ (void) strlcpy(ntout, op->nto, sizeof (ntout));
+ ip->mapflg = mapflag;
+ mapflag = op->mapflg;
+ (void) strlcpy(ip->mi, mapin, sizeof (ip->mi));
+ (void) strlcpy(mapin, op->mi, sizeof (mapin));
+ (void) strlcpy(ip->mo, mapout, sizeof (ip->mo));
+ (void) strlcpy(mapout, op->mo, sizeof (mapout));
+
+ ip->authtype = auth_type;
+ auth_type = op->authtype;
+ ip->clvl = clevel;
+ clevel = op->clvl;
+ ip->dlvl = dlevel;
+ dlevel = op->dlvl;
+ if (!clevel)
+ clevel = PROT_C;
+ if (!dlevel)
+ dlevel = PROT_C;
+
+ (void) signal(SIGINT, oldintr);
+ if (abrtflag) {
+ abrtflag = 0;
+ (*oldintr)();
+ }
+}
+
+/*ARGSUSED*/
+static void
+abortpt(int sig)
+{
+ (void) printf("\n");
+ (void) fflush(stdout);
+ ptabflg++;
+ mflag = 0;
+ abrtflag = 0;
+ longjmp(ptabort, 1);
+}
+
+static void
+proxtrans(char *cmd, char *local, char *remote)
+{
+ void (*oldintr)();
+ int tmptype, oldtype = 0, secndflag = 0, nfnd;
+ extern jmp_buf ptabort;
+ char *cmd2;
+ struct fd_set mask;
+ int ipv4_addr = IN6_IS_ADDR_V4MAPPED(&remctladdr.sin6_addr);
+
+ if (strcmp(cmd, "RETR"))
+ cmd2 = "RETR";
+ else
+ cmd2 = runique ? "STOU" : "STOR";
+ if (command(ipv4_addr ? "PASV" : "EPSV") != COMPLETE) {
+ (void) printf(
+ "proxy server does not support third part transfers.\n");
+ return;
+ }
+ tmptype = type;
+ pswitch(0);
+ if (!connected) {
+ (void) printf("No primary connection\n");
+ pswitch(1);
+ code = -1;
+ return;
+ }
+ if (type != tmptype) {
+ oldtype = type;
+ switch (tmptype) {
+ case TYPE_A:
+ setascii(0, NULL);
+ break;
+ case TYPE_I:
+ setbinary(0, NULL);
+ break;
+ case TYPE_E:
+ setebcdic(0, NULL);
+ break;
+ case TYPE_L:
+ settenex(0, NULL);
+ break;
+ }
+ }
+ if (command(ipv4_addr ? "PORT %s" : "EPRT %s", pasv) != COMPLETE) {
+ switch (oldtype) {
+ case 0:
+ break;
+ case TYPE_A:
+ setascii(0, NULL);
+ break;
+ case TYPE_I:
+ setbinary(0, NULL);
+ break;
+ case TYPE_E:
+ setebcdic(0, NULL);
+ break;
+ case TYPE_L:
+ settenex(0, NULL);
+ break;
+ }
+ pswitch(1);
+ return;
+ }
+ if (setjmp(ptabort))
+ goto abort;
+ oldintr = signal(SIGINT, (void (*)())abortpt);
+ if (command("%s %s", cmd, remote) != PRELIM) {
+ (void) signal(SIGINT, oldintr);
+ switch (oldtype) {
+ case 0:
+ break;
+ case TYPE_A:
+ setascii(0, NULL);
+ break;
+ case TYPE_I:
+ setbinary(0, NULL);
+ break;
+ case TYPE_E:
+ setebcdic(0, NULL);
+ break;
+ case TYPE_L:
+ settenex(0, NULL);
+ break;
+ }
+ pswitch(1);
+ return;
+ }
+ (void) sleep(2);
+ pswitch(1);
+ secndflag++;
+ if (command("%s %s", cmd2, local) != PRELIM)
+ goto abort;
+ ptflag++;
+ (void) getreply(0);
+ pswitch(0);
+ (void) getreply(0);
+ (void) signal(SIGINT, oldintr);
+ switch (oldtype) {
+ case 0:
+ break;
+ case TYPE_A:
+ setascii(0, NULL);
+ break;
+ case TYPE_I:
+ setbinary(0, NULL);
+ break;
+ case TYPE_E:
+ setebcdic(0, NULL);
+ break;
+ case TYPE_L:
+ settenex(0, NULL);
+ break;
+ }
+ pswitch(1);
+ ptflag = 0;
+ (void) printf("local: %s remote: %s\n", local, remote);
+ return;
+abort:
+ (void) signal(SIGINT, SIG_IGN);
+ ptflag = 0;
+ if (strcmp(cmd, "RETR") && !proxy)
+ pswitch(1);
+ else if ((strcmp(cmd, "RETR") == 0) && proxy)
+ pswitch(0);
+ if (!cpend && !secndflag) { /* only here if cmd = "STOR" (proxy=1) */
+ if (command("%s %s", cmd2, local) != PRELIM) {
+ pswitch(0);
+ switch (oldtype) {
+ case 0:
+ break;
+ case TYPE_A:
+ setascii(0, NULL);
+ break;
+ case TYPE_I:
+ setbinary(0, NULL);
+ break;
+ case TYPE_E:
+ setebcdic(0, NULL);
+ break;
+ case TYPE_L:
+ settenex(0, NULL);
+ break;
+ }
+ if (cpend) {
+ char msg[2];
+
+ (void) fprintf(ctrl_out, "%c%c", IAC, IP);
+ (void) fflush(ctrl_out);
+ *msg = (char)IAC;
+ *(msg+1) = (char)DM;
+ if (send(fileno(ctrl_out), msg, 2, MSG_OOB)
+ != 2)
+ perror("abort");
+ (void) fprintf(ctrl_out, "ABOR\r\n");
+ (void) fflush(ctrl_out);
+ FD_ZERO(&mask);
+ FD_SET(fileno(ctrl_in), &mask);
+ if ((nfnd = empty(&mask, 10,
+ fileno(ctrl_in) + 1)) <= 0) {
+ if (nfnd < 0) {
+ perror("abort");
+ }
+ if (ptabflg)
+ code = -1;
+ lostpeer(0);
+ }
+ (void) getreply(0);
+ (void) getreply(0);
+ }
+ }
+ pswitch(1);
+ if (ptabflg)
+ code = -1;
+ (void) signal(SIGINT, oldintr);
+ return;
+ }
+ if (cpend) {
+ char msg[2];
+
+ (void) fprintf(ctrl_out, "%c%c", IAC, IP);
+ (void) fflush(ctrl_out);
+ *msg = (char)IAC;
+ *(msg+1) = (char)DM;
+ if (send(fileno(ctrl_out), msg, 2, MSG_OOB) != 2)
+ perror("abort");
+ (void) fprintf(ctrl_out, "ABOR\r\n");
+ (void) fflush(ctrl_out);
+ FD_ZERO(&mask);
+ FD_SET(fileno(ctrl_in), &mask);
+ if ((nfnd = empty(&mask, 10, fileno(ctrl_in) + 1)) <= 0) {
+ if (nfnd < 0) {
+ perror("abort");
+ }
+ if (ptabflg)
+ code = -1;
+ lostpeer(0);
+ }
+ (void) getreply(0);
+ (void) getreply(0);
+ }
+ pswitch(!proxy);
+ if (!cpend && !secndflag) { /* only if cmd = "RETR" (proxy=1) */
+ if (command("%s %s", cmd2, local) != PRELIM) {
+ pswitch(0);
+ switch (oldtype) {
+ case 0:
+ break;
+ case TYPE_A:
+ setascii(0, NULL);
+ break;
+ case TYPE_I:
+ setbinary(0, NULL);
+ break;
+ case TYPE_E:
+ setebcdic(0, NULL);
+ break;
+ case TYPE_L:
+ settenex(0, NULL);
+ break;
+ }
+ if (cpend) {
+ char msg[2];
+
+ (void) fprintf(ctrl_out, "%c%c", IAC, IP);
+ (void) fflush(ctrl_out);
+ *msg = (char)IAC;
+ *(msg+1) = (char)DM;
+ if (send(fileno(ctrl_out), msg, 2, MSG_OOB)
+ != 2)
+ perror("abort");
+ (void) fprintf(ctrl_out, "ABOR\r\n");
+ (void) fflush(ctrl_out);
+ FD_ZERO(&mask);
+ FD_SET(fileno(ctrl_in), &mask);
+ if ((nfnd = empty(&mask, 10,
+ fileno(ctrl_in) + 1)) <= 0) {
+ if (nfnd < 0) {
+ perror("abort");
+ }
+ if (ptabflg)
+ code = -1;
+ lostpeer(0);
+ }
+ (void) getreply(0);
+ (void) getreply(0);
+ }
+ pswitch(1);
+ if (ptabflg)
+ code = -1;
+ (void) signal(SIGINT, oldintr);
+ return;
+ }
+ }
+ if (cpend) {
+ char msg[2];
+
+ (void) fprintf(ctrl_out, "%c%c", IAC, IP);
+ (void) fflush(ctrl_out);
+ *msg = (char)IAC;
+ *(msg+1) = (char)DM;
+ if (send(fileno(ctrl_out), msg, 2, MSG_OOB) != 2)
+ perror("abort");
+ (void) fprintf(ctrl_out, "ABOR\r\n");
+ (void) fflush(ctrl_out);
+ FD_ZERO(&mask);
+ FD_SET(fileno(ctrl_in), &mask);
+ if ((nfnd = empty(&mask, 10, fileno(ctrl_in) + 1)) <= 0) {
+ if (nfnd < 0) {
+ perror("abort");
+ }
+ if (ptabflg)
+ code = -1;
+ lostpeer(0);
+ }
+ (void) getreply(0);
+ (void) getreply(0);
+ }
+ pswitch(!proxy);
+ if (cpend) {
+ FD_ZERO(&mask);
+ FD_SET(fileno(ctrl_in), &mask);
+ if ((nfnd = empty(&mask, 10, fileno(ctrl_in) + 1)) <= 0) {
+ if (nfnd < 0) {
+ perror("abort");
+ }
+ if (ptabflg)
+ code = -1;
+ lostpeer(0);
+ }
+ (void) getreply(0);
+ (void) getreply(0);
+ }
+ if (proxy)
+ pswitch(0);
+ switch (oldtype) {
+ case 0:
+ break;
+ case TYPE_A:
+ setascii(0, NULL);
+ break;
+ case TYPE_I:
+ setbinary(0, NULL);
+ break;
+ case TYPE_E:
+ setebcdic(0, NULL);
+ break;
+ case TYPE_L:
+ settenex(0, NULL);
+ break;
+ }
+ pswitch(1);
+ if (ptabflg)
+ code = -1;
+ (void) signal(SIGINT, oldintr);
+}
+
+/*ARGSUSED*/
+void
+reset(int argc, char *argv[])
+{
+ struct fd_set mask;
+ int nfnd = 1;
+
+ FD_ZERO(&mask);
+ while (nfnd > 0) {
+ FD_SET(fileno(ctrl_in), &mask);
+ if ((nfnd = empty(&mask, 0, fileno(ctrl_in) + 1)) < 0) {
+ perror("reset");
+ code = -1;
+ lostpeer(0);
+ } else if (nfnd > 0) {
+ (void) getreply(0);
+ }
+ }
+}
+
+static char *
+gunique(char *local)
+{
+ static char new[MAXPATHLEN];
+ char *cp = rindex(local, '/');
+ int d, count = 0;
+ char ext = '1';
+
+ if (cp)
+ *cp = '\0';
+ d = access(cp ? local : ".", 2);
+ if (cp)
+ *cp = '/';
+ if (d < 0) {
+ perror(local);
+ return ((char *)0);
+ }
+ if (strlcpy(new, local, sizeof (new)) >= sizeof (new))
+ (void) printf("gunique: too long: local %s, %d, new %d\n",
+ local, strlen(local), sizeof (new));
+
+ cp = new + strlen(new);
+ *cp++ = '.';
+ while (!d) {
+ if (++count == 100) {
+ (void) printf(
+ "runique: can't find unique file name.\n");
+ return ((char *)0);
+ }
+ *cp++ = ext;
+ *cp = '\0';
+ if (ext == '9')
+ ext = '0';
+ else
+ ext++;
+ if ((d = access(new, 0)) < 0)
+ break;
+ if (ext != '0')
+ cp--;
+ else if (*(cp - 2) == '.')
+ *(cp - 1) = '1';
+ else {
+ *(cp - 2) = *(cp - 2) + 1;
+ cp--;
+ }
+ }
+ return (new);
+}
+
+/*
+ * This is a wrap-around function for inet_ntop(). In case the af is AF_INET6
+ * and the address pointed by src is a IPv4-mapped IPv6 address, it
+ * returns printable IPv4 address, not IPv4-mapped IPv6 address. In other cases
+ * it behaves just like inet_ntop().
+ */
+const char *
+inet_ntop_native(int af, const void *src, char *dst, size_t size)
+{
+ struct in_addr src4;
+ const char *result;
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+
+ if (af == AF_INET6) {
+ sin6 = (struct sockaddr_in6 *)src;
+ if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
+ IN6_V4MAPPED_TO_INADDR(&sin6->sin6_addr, &src4);
+ result = inet_ntop(AF_INET, &src4, dst, size);
+ } else {
+ result = inet_ntop(AF_INET6, &sin6->sin6_addr,
+ dst, size);
+ }
+ } else {
+ sin = (struct sockaddr_in *)src;
+ result = inet_ntop(af, &sin->sin_addr, dst, size);
+ }
+
+ return (result);
+}
+
+int
+secure_command(char *cmd)
+{
+ unsigned char *in = NULL, *out = NULL;
+ int length = 0;
+ size_t inlen;
+
+ if ((auth_type != AUTHTYPE_NONE) && clevel != PROT_C) {
+ gss_buffer_desc in_buf, out_buf;
+ OM_uint32 maj_stat, min_stat;
+
+ /* secure_command (based on level) */
+ if (auth_type == AUTHTYPE_GSSAPI) {
+ OM_uint32 expire_time;
+ int conf_state;
+ /* clevel = PROT_P; */
+ in_buf.value = cmd;
+ in_buf.length = strlen(cmd) + 1;
+
+ maj_stat = gss_context_time(&min_stat, gcontext,
+ &expire_time);
+ if (GSS_ERROR(maj_stat)) {
+ user_gss_error(maj_stat, min_stat,
+ "gss context has expired");
+ fatal("Your gss credentials have expired. "
+ "Good-bye!");
+ }
+ maj_stat = gss_seal(&min_stat, gcontext,
+ (clevel == PROT_P), /* private */
+ GSS_C_QOP_DEFAULT,
+ &in_buf, &conf_state,
+ &out_buf);
+ if (maj_stat != GSS_S_COMPLETE) {
+ /* generally need to deal */
+ user_gss_error(maj_stat, min_stat,
+ (clevel == PROT_P) ?
+ "gss_seal ENC didn't complete":
+ "gss_seal MIC didn't complete");
+ } else if ((clevel == PROT_P) && !conf_state) {
+ (void) fprintf(stderr,
+ "GSSAPI didn't encrypt message");
+ out = out_buf.value;
+ } else {
+ if (debug)
+ (void) fprintf(stderr,
+ "sealed (%s) %d bytes\n",
+ clevel == PROT_P ? "ENC" : "MIC",
+ out_buf.length);
+
+ out = out_buf.value;
+ }
+ }
+ /* Other auth types go here ... */
+ inlen = ((4 * out_buf.length) / 3) + 4;
+ in = (uchar_t *)malloc(inlen);
+ if (in == NULL) {
+ gss_release_buffer(&min_stat, &out_buf);
+ fatal("Memory error allocating space for response.");
+ }
+ length = out_buf.length;
+ if (auth_error = radix_encode(out, in, inlen, &length, 0)) {
+ (void) fprintf(stderr,
+ "Couldn't base 64 encode command (%s)\n",
+ radix_error(auth_error));
+ free(in);
+ gss_release_buffer(&min_stat, &out_buf);
+ return (0);
+ }
+
+ (void) fprintf(ctrl_out, "%s %s",
+ clevel == PROT_P ? "ENC" : "MIC", in);
+
+ free(in);
+ gss_release_buffer(&min_stat, &out_buf);
+
+ if (debug)
+ (void) fprintf(stderr,
+ "secure_command(%s)\nencoding %d bytes %s %s\n",
+ cmd, length,
+ (clevel == PROT_P) ? "ENC" : "MIC", in);
+ } else {
+ /*
+ * auth_type = AUTHTYPE_NONE or
+ * command channel is not protected
+ */
+ fputs(cmd, ctrl_out);
+ }
+
+ (void) fprintf(ctrl_out, "\r\n");
+ (void) fflush(ctrl_out);
+ return (1);
+}
+
+unsigned int maxbuf;
+unsigned char *ucbuf;
+
+void
+setpbsz(unsigned int size)
+{
+ unsigned int actualbuf;
+ int oldverbose;
+
+ if (ucbuf)
+ (void) free(ucbuf);
+ actualbuf = size;
+ while ((ucbuf = (unsigned char *)malloc(actualbuf)) == NULL) {
+ if (actualbuf)
+ actualbuf >>= 2;
+ else {
+ perror("Error while trying to malloc PROT buffer:");
+ exit(1);
+ }
+ }
+ oldverbose = verbose;
+ verbose = 0;
+ reply_parse = "PBSZ=";
+ if (command("PBSZ %u", actualbuf) != COMPLETE)
+ fatal("Cannot set PROT buffer size");
+ if (reply_parse) {
+ if ((maxbuf = (unsigned int) atol(reply_parse)) > actualbuf)
+ maxbuf = actualbuf;
+ } else
+ maxbuf = actualbuf;
+ reply_parse = NULL;
+ verbose = oldverbose;
+}
+
+/*
+ * Do the base 64 decoding of the raw input buffer, b64_buf.
+ * Also do the verification and decryption, if required.
+ * retval contains the current error code number
+ *
+ * returns:
+ * (RFC 2228: error returns are 3 digit numbers of the form 5xy)
+ * 5 if an error occurred
+ */
+static int
+decode_reply(uchar_t *plain_buf,
+ int ilen,
+ uchar_t *b64_buf,
+ int retval,
+ boolean_t *again)
+{
+ int len;
+ int safe = 0;
+
+ *again = 0;
+
+ if (!b64_buf[0]) /* if there is no string, no problem */
+ return (retval);
+
+ if ((auth_type == AUTHTYPE_NONE)) {
+ (void) printf("Cannot decode reply:\n%d %s\n", code, b64_buf);
+ return ('5');
+ }
+
+ switch (code) {
+
+ case 631: /* 'safe' */
+ safe = 1;
+ break;
+
+ case 632: /* 'private' */
+ break;
+
+ case 633: /* 'confidential' */
+ break;
+
+ default:
+ (void) printf("Unknown reply: %d %s\n", code, b64_buf);
+ return ('5');
+ }
+
+ /* decode the base64 encoded message */
+ auth_error = radix_encode(b64_buf, plain_buf, ilen, &len, 1);
+
+ if (auth_error) {
+ (void) printf("Can't base 64 decode reply %d (%s)\n\"%s\"\n",
+ code, radix_error(auth_error), b64_buf);
+ return ('5');
+ }
+
+ if (auth_type == AUTHTYPE_GSSAPI) {
+ gss_buffer_desc xmit_buf, msg_buf;
+ OM_uint32 maj_stat, min_stat;
+ int conf_state = safe;
+ xmit_buf.value = plain_buf;
+ xmit_buf.length = len;
+
+ /* decrypt/verify the message */
+ maj_stat = gss_unseal(&min_stat, gcontext,
+ &xmit_buf, &msg_buf, &conf_state, NULL);
+ if (maj_stat != GSS_S_COMPLETE) {
+ user_gss_error(maj_stat, min_stat,
+ "failed unsealing reply");
+ return ('5');
+ }
+ if (msg_buf.length < ilen - 2 - 1) {
+ memcpy(plain_buf, msg_buf.value, msg_buf.length);
+ strcpy((char *)&plain_buf[msg_buf.length], "\r\n");
+ gss_release_buffer(&min_stat, &msg_buf);
+ *again = 1;
+ } else {
+ user_gss_error(maj_stat, min_stat,
+ "reply was too long");
+ return ('5');
+ }
+ } /* end if GSSAPI */
+
+ /* Other auth types go here... */
+
+ return (retval);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/ftp/ftp.dfl b/usr/src/cmd/cmd-inet/usr.bin/ftp/ftp.dfl
new file mode 100644
index 0000000000..b116f2e836
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/ftp/ftp.dfl
@@ -0,0 +1,39 @@
+#ident "%Z%%M% %I% %E% SMI"
+
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+# This is the ftp client configuration file
+
+# The following flag controls the behavior of the "ls" command
+# in /usr/bin/ftp. To get the default behavior, leave these
+# lines commented out. To force a particular behavior,
+# uncomment the appropriate line. When this flag is set to
+# "YES," the "ls" command will send the RFC 959 NLST command.
+# When it is set to "NO," the "ls" command will send the RFC
+# 959 LIST command. "YES" is the current default, causing ftp
+# to send "NLST". This is the same as in previous releases of
+# Solaris.
+
+#FTP_LS_SENDS_NLST=NO
diff --git a/usr/src/cmd/cmd-inet/usr.bin/ftp/ftp_var.h b/usr/src/cmd/cmd-inet/usr.bin/ftp/ftp_var.h
new file mode 100644
index 0000000000..e6c2621319
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/ftp/ftp_var.h
@@ -0,0 +1,348 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#ifndef _FTP_VAR_H
+#define _FTP_VAR_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/resource.h>
+#include <sys/ttold.h>
+#include <sys/stropts.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/ftp.h>
+#include <arpa/telnet.h>
+#include <arpa/inet.h>
+#include <setjmp.h>
+#include <libintl.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <widec.h>
+#include <signal.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <locale.h>
+#include <limits.h>
+#include <fnmatch.h>
+#include <dirent.h>
+#include <termios.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <malloc.h>
+#include <strings.h>
+#include <errno.h>
+#include <ctype.h>
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_ext.h>
+
+#define signal(s, f) sigset(s, f)
+#define setjmp(e) sigsetjmp(e, 1)
+#define longjmp(e, v) siglongjmp(e, v)
+#define jmp_buf sigjmp_buf
+
+/*
+ * FTP global variables.
+ */
+#ifndef EXTERN
+#define EXTERN extern
+#endif
+
+#define DEFAULTFTPFILE "/etc/default/ftp"
+
+/*
+ * Options and other state info.
+ */
+EXTERN int trace; /* trace packets exchanged */
+EXTERN int hash; /* print # for each buffer transferred */
+EXTERN int sendport; /* use PORT cmd for each data connection */
+EXTERN int verbose; /* print messages coming back from server */
+EXTERN int connected; /* connected to server */
+EXTERN int fromatty; /* input is from a terminal */
+EXTERN int interactive; /* interactively prompt on m* cmds */
+EXTERN int debug; /* debugging level */
+EXTERN int bell; /* ring bell on cmd completion */
+EXTERN int doglob; /* glob local file names */
+EXTERN int autologin; /* establish user account on connection */
+EXTERN int proxy; /* proxy server connection active */
+EXTERN int proxflag; /* proxy connection exists */
+EXTERN int sunique; /* store files on server with unique name */
+EXTERN int runique; /* store local files with unique name */
+EXTERN int mcase; /* map upper to lower case for mget names */
+EXTERN int ntflag; /* use ntin ntout tables for name translation */
+EXTERN int mapflag; /* use mapin mapout templates on file names */
+EXTERN int code; /* return/reply code for ftp command */
+EXTERN int crflag; /* if 1, strip car. rets. on ascii gets */
+EXTERN char pasv[64]; /* passive port for proxy data connection */
+EXTERN char *altarg; /* argv[1] with no shell-like preprocessing */
+EXTERN char ntin[17]; /* input translation table */
+EXTERN char ntout[17]; /* output translation table */
+EXTERN char mapin[MAXPATHLEN]; /* input map template */
+EXTERN char mapout[MAXPATHLEN]; /* output map template */
+EXTERN char typename[32]; /* name of file transfer type */
+EXTERN int type; /* file transfer type */
+EXTERN char structname[32]; /* name of file transfer structure */
+EXTERN int stru; /* file transfer structure */
+EXTERN char formname[32]; /* name of file transfer format */
+EXTERN int form; /* file transfer format */
+EXTERN char modename[32]; /* name of file transfer mode */
+EXTERN int mode; /* file transfer mode */
+EXTERN char bytename[32]; /* local byte size in ascii */
+EXTERN int bytesize; /* local byte size in binary */
+EXTERN int passivemode; /* passive transfer mode toggle */
+EXTERN off_t restart_point; /* transfer restart offset */
+EXTERN int tcpwindowsize; /* TCP window size for the data connection */
+
+EXTERN boolean_t ls_invokes_NLST; /* behaviour of 'ls' */
+EXTERN char *hostname; /* name of host connected to */
+EXTERN char *home;
+EXTERN char *globerr;
+
+EXTERN struct sockaddr_in6 myctladdr; /* for channel bindings */
+EXTERN struct sockaddr_in6 remctladdr; /* for channel bindings */
+
+EXTERN int clevel; /* command channel protection level */
+EXTERN int dlevel; /* data channel protection level */
+
+EXTERN int autoauth; /* do authentication on connect */
+EXTERN int auth_type; /* authentication type */
+EXTERN int auth_error; /* one error code for all auth types */
+EXTERN int autoencrypt; /* do encryption on connect */
+EXTERN int fflag; /* forward credentials */
+EXTERN boolean_t goteof;
+
+EXTERN uchar_t *ucbuf; /* clear text buffer */
+
+#define MECH_SZ 40
+#define FTP_DEF_MECH "kerberos_v5"
+EXTERN char mechstr[MECH_SZ]; /* mechanism type */
+
+EXTERN gss_OID mechoid; /* corresponding mechanism oid type */
+EXTERN gss_ctx_id_t gcontext; /* gss security context */
+
+#define FTPBUFSIZ BUFSIZ*16
+#define HASHSIZ BUFSIZ*8
+
+EXTERN char *buf; /* buffer for binary sends and gets */
+
+EXTERN jmp_buf toplevel; /* non-local goto stuff for cmd scanner */
+
+/*
+ * BUFSIZE includes
+ * - (MAXPATHLEN)*2 to accomodate 2 paths (remote and local file names).
+ * - MAXCMDLEN to accomodate the longest command listed in cmdtab[]
+ * (defined in cmdtab.c) as this is stuffed into the buffer along
+ * with the remote and local file names.
+ * - The 4 bytes are for the 2 blank separators, a carriage-return
+ * and a NULL terminator.
+ *
+ * NOTE : The arguments may not be always pathnames (they can be commands
+ * too). But, here we have considered the worst case of two pathnames.
+ */
+#define MAXCMDLEN 10 /* The length of longest command in cmdtab[] */
+#define BUFSIZE ((MAXPATHLEN)*2+MAXCMDLEN+4)
+
+EXTERN char line[BUFSIZE]; /* input line buffer */
+EXTERN char *stringbase; /* current scan point in line buffer */
+EXTERN char argbuf[BUFSIZE]; /* argument storage buffer */
+EXTERN char *argbase; /* current storage point in arg buffer */
+EXTERN int margc; /* count of arguments on input line */
+EXTERN char **margv; /* args parsed from input line */
+EXTERN int cpend; /* flag: if != 0, then pending server reply */
+EXTERN int mflag; /* flag: if != 0, then active multi command */
+EXTERN FILE *tmp_nlst; /* tmp file; holds NLST results for mget, etc */
+
+EXTERN char *reply_parse; /* for parsing replies to the ADAT command */
+EXTERN char reply_buf[FTPBUFSIZ];
+EXTERN char *reply_ptr;
+
+EXTERN int options; /* used during socket creation */
+
+EXTERN int timeout; /* connection timeout */
+EXTERN int timeoutms; /* connection timeout in msec */
+EXTERN jmp_buf timeralarm; /* to recover from global timeout */
+
+
+/*
+ * Format of command table.
+ */
+struct cmd {
+ char *c_name; /* name of command */
+ char *c_help; /* help string */
+ char c_bell; /* give bell when command completes */
+ char c_conn; /* must be connected to use command */
+ char c_proxy; /* proxy server may execute */
+ void (*c_handler)(int argc, char *argv[]); /* function to call */
+};
+
+struct macel {
+ char mac_name[9]; /* macro name */
+ char *mac_start; /* start of macro in macbuf */
+ char *mac_end; /* end of macro in macbuf */
+};
+
+EXTERN int macnum; /* number of defined macros */
+EXTERN struct macel macros[16];
+EXTERN char macbuf[4096];
+
+extern void macdef(int argc, char *argv[]);
+extern void doproxy(int argc, char *argv[]);
+extern void setpeer(int argc, char *argv[]);
+extern void rmthelp(int argc, char *argv[]);
+extern void settype(int argc, char *argv[]);
+extern void setbinary(int argc, char *argv[]);
+extern void setascii(int argc, char *argv[]);
+extern void settenex(int argc, char *argv[]);
+extern void setebcdic(int argc, char *argv[]);
+extern void setmode(int argc, char *argv[]);
+extern void setform(int argc, char *argv[]);
+extern void setstruct(int argc, char *argv[]);
+extern void put(int argc, char *argv[]);
+extern void mput(int argc, char *argv[]);
+extern void get(int argc, char *argv[]);
+extern void mget(int argc, char *argv[]);
+extern void status(int argc, char *argv[]);
+extern void setbell(int argc, char *argv[]);
+extern void settrace(int argc, char *argv[]);
+extern void sethash(int argc, char *argv[]);
+extern void setverbose(int argc, char *argv[]);
+extern void setport(int argc, char *argv[]);
+extern void setprompt(int argc, char *argv[]);
+extern void setglob(int argc, char *argv[]);
+extern void setdebug(int argc, char *argv[]);
+extern void cd(int argc, char *argv[]);
+extern void lcd(int argc, char *argv[]);
+extern void delete(int argc, char *argv[]);
+extern void mdelete(int argc, char *argv[]);
+extern void renamefile(int argc, char *argv[]);
+extern void ls(int argc, char *argv[]);
+extern void mls(int argc, char *argv[]);
+extern void shell(int argc, char *argv[]);
+extern void user(int argc, char *argv[]);
+extern void pwd(int argc, char *argv[]);
+extern void makedir(int argc, char *argv[]);
+extern void removedir(int argc, char *argv[]);
+extern void quote(int argc, char *argv[]);
+extern void rmthelp(int argc, char *argv[]);
+extern void quit(int argc, char *argv[]);
+extern void disconnect(int argc, char *argv[]);
+extern void account(int argc, char *argv[]);
+extern void setcase(int argc, char *argv[]);
+extern void setcr(int argc, char *argv[]);
+extern void setntrans(int argc, char *argv[]);
+extern void setnmap(int argc, char *argv[]);
+extern void setsunique(int argc, char *argv[]);
+extern void setrunique(int argc, char *argv[]);
+extern void cdup(int argc, char *argv[]);
+extern void domacro(int argc, char *argv[]);
+extern void help(int argc, char *argv[]);
+extern void reset(int argc, char *argv[]);
+extern void reget(int argc, char *argv[]);
+extern void restart(int argc, char *argv[]);
+extern void setpassive(int argc, char *argv[]);
+extern void settcpwindow(int argc, char *argv[]);
+extern void site(int argc, char *argv[]);
+
+extern void ccc(int argc, char *argv[]);
+extern void setclear(int argc, char *argv[]);
+extern void setclevel(int argc, char *argv[]);
+extern void setdlevel(int argc, char *argv[]);
+extern void setsafe(int argc, char *argv[]);
+extern void setmech(int argc, char *argv[]);
+
+extern int do_auth(void);
+extern void setpbsz(uint_t size);
+extern char *radix_error(int);
+extern int radix_encode(uchar_t *, uchar_t *, size_t, int *, int);
+extern void user_gss_error(OM_uint32 maj_stat, OM_uint32 min_stat,
+ char *errstr);
+extern void setprivate(int argc, char *argv[]);
+
+extern int secure_flush(int);
+extern int secure_getc(FILE *);
+extern int secure_putc(int, FILE *);
+extern ssize_t secure_read(int, void *, size_t);
+extern ssize_t secure_write(int, const void *, size_t);
+
+extern void fatal(char *msg);
+extern int getreply(int expecteof);
+extern void call(void (*routine)(int argc, char *argv[]), ...);
+extern void sendrequest(char *cmd, char *local, char *remote, int allowpipe);
+extern void recvrequest(char *cmd, char *local, char *remote, char *mode,
+ int allowpipe);
+extern void makeargv(void);
+extern int login(char *host);
+extern int command(char *fmt, ...);
+extern char **glob(char *v);
+extern void blkfree(char **);
+extern void pswitch(int flag);
+
+extern char *hookup(char *host, char *);
+extern char *mygetpass(char *prompt);
+extern void lostpeer(int sig);
+extern int ruserpass(char *host, char **aname, char **apass, char **aacct);
+extern FILE *mypopen(char *cmd, char *mode);
+extern int mypclose(FILE *ptr);
+extern struct cmd *getcmd(char *name);
+
+extern void stop_timer(void);
+extern void reset_timer(void);
+extern int getpagesize(void);
+
+#define ENCODELEN(l) (((4 * (l)) / 3) + 4)
+#define DECODELEN(l) (((3 * (l)) / 4) + 4)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FTP_VAR_H */
diff --git a/usr/src/cmd/cmd-inet/usr.bin/ftp/getpass.c b/usr/src/cmd/cmd-inet/usr.bin/ftp/getpass.c
new file mode 100644
index 0000000000..90f5b47682
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/ftp/getpass.c
@@ -0,0 +1,96 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2001 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "ftp_var.h"
+
+static struct termios termios_b;
+static tcflag_t flags;
+static FILE *fi;
+
+/* ARGSUSED */
+static void
+intfix(int sig)
+{
+ termios_b.c_lflag = flags;
+ (void) tcsetattr(fileno(fi), TCSADRAIN, &termios_b);
+ exit(EXIT_FAILURE);
+}
+
+char *
+mygetpass(char *prompt)
+{
+ register char *p;
+ register int c;
+ static char pbuf[50+1];
+ void (*sig)();
+
+ stop_timer();
+ if ((fi = fopen("/dev/tty", "r")) == NULL)
+ fi = stdin;
+ else
+ setbuf(fi, (char *)NULL);
+
+ if (tcgetattr(fileno(fi), &termios_b) < 0)
+ perror("ftp: tcgetattr"); /* go ahead, anyway */
+ flags = termios_b.c_lflag;
+
+ sig = signal(SIGINT, intfix);
+
+ termios_b.c_lflag &= ~ECHO;
+ (void) tcsetattr(fileno(fi), TCSADRAIN, &termios_b);
+ (void) fprintf(stderr, "%s", prompt);
+ (void) fflush(stderr);
+ p = pbuf;
+ while ((c = getc(fi)) != '\n' && c != EOF) {
+ if (p < &pbuf[sizeof (pbuf)-1])
+ *p++ = c;
+ }
+ *p = '\0';
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+ termios_b.c_lflag = flags;
+ (void) tcsetattr(fileno(fi), TCSADRAIN, &termios_b);
+ (void) signal(SIGINT, sig);
+ if (fi != stdin)
+ (void) fclose(fi);
+ reset_timer();
+ return (pbuf);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/ftp/glob.c b/usr/src/cmd/cmd-inet/usr.bin/ftp/glob.c
new file mode 100644
index 0000000000..34e7b9df5e
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/ftp/glob.c
@@ -0,0 +1,796 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * C-shell glob for random programs.
+ */
+
+#include "ftp_var.h"
+
+#ifndef NCARGS
+#define NCARGS 5120
+#endif
+
+#define QUOTE 0200
+#define TRIM 0177
+#define eq(a, b) (strcmp(a, b) == 0)
+
+/*
+ * According to the person who wrote the C shell "glob" code, a reasonable
+ * limit on number of arguments would seem to be the maximum number of
+ * characters in an arg list / 6.
+ *
+ * XXX: With the new VM system, NCARGS has become enormous, making
+ * it impractical to allocate arrays with NCARGS / 6 entries on
+ * the stack. The proper fix is to revamp code elsewhere (in
+ * sh.dol.c and sh.glob.c) to use a different technique for handling
+ * command line arguments. In the meantime, we simply fall back
+ * on using the old value of NCARGS.
+ */
+#ifdef notyet
+#define GAVSIZ (NCARGS / 6)
+#else /* notyet */
+#define GAVSIZ (10240 / 6)
+#endif /* notyet */
+
+static char **gargv; /* Pointer to the (stack) arglist */
+static char **agargv;
+static int agargv_size;
+static long gargc; /* Number args in gargv */
+static short gflag;
+static char *strspl();
+static char *strend(char *cp);
+static char *strspl(char *cp, char *dp);
+static int tglob(char c);
+static char **copyblk(char **v);
+static void ginit(char **agargv);
+static void addpath(char c);
+static int any(int c, char *s);
+static void Gcat(char *s1, char *s2);
+static void collect(char *as);
+static void acollect(char *as);
+static void sort(void);
+static void expand(char *as);
+static void matchdir(char *pattern);
+static int execbrc(char *p, char *s);
+static int ftp_fnmatch(wchar_t t_ch, wchar_t t_fch, wchar_t t_lch);
+static int gethdir(char *home);
+static void xfree(char *cp);
+static void rscan(char **t, int (*f)(char));
+static int letter(char c);
+static int digit(char c);
+static int match(char *s, char *p);
+static int amatch(char *s, char *p);
+static int blklen(char **av);
+static char **blkcpy(char **oav, char **bv);
+
+static int globcnt;
+
+static char *globchars = "`{[*?";
+
+static char *gpath, *gpathp, *lastgpathp;
+static int globbed;
+static char *entp;
+static char **sortbas;
+
+char **
+glob(char *v)
+{
+ char agpath[FTPBUFSIZ];
+ char *vv[2];
+
+ if (agargv == NULL) {
+ agargv = (char **)malloc(GAVSIZ * sizeof (char *));
+ agargv_size = GAVSIZ;
+ if (agargv == NULL) {
+ globerr = "Arguments too long.";
+ return (0);
+ }
+ }
+ vv[0] = v;
+ vv[1] = 0;
+ globerr = 0;
+ gflag = 0;
+ rscan(vv, tglob);
+ if (gflag == 0)
+ return (copyblk(vv));
+
+ gpath = agpath;
+ gpathp = gpath;
+ *gpathp = 0;
+ lastgpathp = &gpath[sizeof (agpath) - 2];
+ ginit(agargv);
+ globcnt = 0;
+ collect(v);
+ if (globcnt == 0 && (gflag&1)) {
+ blkfree(gargv);
+ if (gargv == agargv)
+ agargv = 0;
+ gargv = 0;
+ return (0);
+ } else
+ return (gargv = copyblk(gargv));
+}
+
+static void
+ginit(char **agargv)
+{
+
+ agargv[0] = 0;
+ gargv = agargv;
+ sortbas = agargv;
+ gargc = 0;
+}
+
+static void
+collect(char *as)
+{
+ if (eq(as, "{") || eq(as, "{}")) {
+ Gcat(as, "");
+ sort();
+ } else
+ acollect(as);
+}
+
+static void
+acollect(char *as)
+{
+ register long ogargc = gargc;
+
+ gpathp = gpath; *gpathp = 0; globbed = 0;
+ expand(as);
+ if (gargc != ogargc)
+ sort();
+}
+
+static void
+sort(void)
+{
+ register char **p1, **p2, *c;
+ char **Gvp = &gargv[gargc];
+
+ p1 = sortbas;
+ while (p1 < Gvp-1) {
+ p2 = p1;
+ while (++p2 < Gvp)
+ if (strcmp(*p1, *p2) > 0)
+ c = *p1, *p1 = *p2, *p2 = c;
+ p1++;
+ }
+ sortbas = Gvp;
+}
+
+static void
+expand(char *as)
+{
+ register char *cs;
+ register char *sgpathp, *oldcs;
+ struct stat stb;
+
+ sgpathp = gpathp;
+ cs = as;
+ if (*cs == '~' && gpathp == gpath) {
+ addpath('~');
+ cs++;
+ while (letter(*cs) || digit(*cs) || *cs == '-')
+ addpath(*cs++);
+ if (!*cs || *cs == '/') {
+ if (gpathp != gpath + 1) {
+ *gpathp = 0;
+ if (gethdir(gpath + 1))
+ globerr = "Unknown user name after ~";
+ (void) strcpy(gpath, gpath + 1);
+ } else
+ (void) strcpy(gpath, home);
+ gpathp = strend(gpath);
+ }
+ }
+ while (!any(*cs, globchars)) {
+ if (*cs == 0) {
+ if (!globbed)
+ Gcat(gpath, "");
+ else if (stat(gpath, &stb) >= 0) {
+ Gcat(gpath, "");
+ globcnt++;
+ }
+ goto endit;
+ }
+ addpath(*cs++);
+ }
+ oldcs = cs;
+ while (cs > as && *cs != '/')
+ cs--, gpathp--;
+ if (*cs == '/')
+ cs++, gpathp++;
+ *gpathp = 0;
+ if (*oldcs == '{') {
+ (void) execbrc(cs, ((char *)0));
+ return;
+ }
+ matchdir(cs);
+endit:
+ gpathp = sgpathp;
+ *gpathp = 0;
+}
+
+static void
+matchdir(char *pattern)
+{
+ struct stat stb;
+ register struct dirent *dp;
+ DIR *dirp;
+
+ /*
+ * BSD/SunOS open() system call maps a null pathname into
+ * "." while System V does not.
+ */
+ if (*gpath == (char)0) {
+ dirp = opendir(".");
+ } else
+ dirp = opendir(gpath);
+ if (dirp == NULL) {
+ if (globbed)
+ return;
+ goto patherr2;
+ }
+ if (fstat(dirp->dd_fd, &stb) < 0)
+ goto patherr1;
+ if (!S_ISDIR(stb.st_mode)) {
+ errno = ENOTDIR;
+ goto patherr1;
+ }
+ while ((dp = readdir(dirp)) != NULL) {
+ if (dp->d_ino == 0)
+ continue;
+ if (match(dp->d_name, pattern)) {
+ Gcat(gpath, dp->d_name);
+ globcnt++;
+ }
+ }
+ closedir(dirp);
+ return;
+
+patherr1:
+ closedir(dirp);
+patherr2:
+ globerr = "Bad directory components";
+}
+
+static int
+execbrc(char *p, char *s)
+{
+ char restbuf[FTPBUFSIZ + 2];
+ register char *pe, *pm, *pl;
+ int brclev = 0;
+ char *lm, savec, *sgpathp;
+ int len;
+
+ for (lm = restbuf; *p != '{'; *lm += len, p += len) {
+ if ((len = mblen(p, MB_CUR_MAX)) <= 0)
+ len = 1;
+ memcpy(lm, p, len);
+ }
+
+ for (pe = ++p; *pe; pe += len) {
+ if ((len = mblen(pe, MB_CUR_MAX)) <= 0)
+ len = 1;
+
+ switch (*pe) {
+
+ case '{':
+ brclev++;
+ continue;
+
+ case '}':
+ if (brclev == 0)
+ goto pend;
+ brclev--;
+ continue;
+
+ case '[':
+ for (pe++; *pe && *pe != ']'; pe += len) {
+ if ((len = mblen(pe, MB_CUR_MAX)) <= 0)
+ len = 1;
+ }
+ len = 1;
+ continue;
+ }
+ }
+pend:
+ brclev = 0;
+ for (pl = pm = p; pm <= pe; pm += len) {
+ if ((len = mblen(pm, MB_CUR_MAX)) <= 0)
+ len = 1;
+
+ switch (*pm & (QUOTE|TRIM)) {
+
+ case '{':
+ brclev++;
+ continue;
+
+ case '}':
+ if (brclev) {
+ brclev--;
+ continue;
+ }
+ goto doit;
+
+ case ','|QUOTE:
+ case ',':
+ if (brclev)
+ continue;
+doit:
+ savec = *pm;
+ *pm = 0;
+ (void) strcpy(lm, pl);
+ (void) strcat(restbuf, pe + 1);
+ *pm = savec;
+ if (s == 0) {
+ sgpathp = gpathp;
+ expand(restbuf);
+ gpathp = sgpathp;
+ *gpathp = 0;
+ } else if (amatch(s, restbuf))
+ return (1);
+ sort();
+ pl = pm + 1;
+ if (brclev)
+ return (0);
+ continue;
+
+ case '[':
+ for (pm++; *pm && *pm != ']'; pm += len) {
+ if ((len = mblen(pm, MB_CUR_MAX)) <= 0)
+ len = 1;
+ }
+ len = 1;
+ if (!*pm)
+ pm--;
+ continue;
+ }
+ }
+ if (brclev)
+ goto doit;
+ return (0);
+}
+
+static int
+match(char *s, char *p)
+{
+ register int c;
+ register char *sentp;
+ char sglobbed = globbed;
+
+ if (*s == '.' && *p != '.')
+ return (0);
+ sentp = entp;
+ entp = s;
+ c = amatch(s, p);
+ entp = sentp;
+ globbed = sglobbed;
+ return (c);
+}
+
+static int
+amatch(char *s, char *p)
+{
+ wchar_t scc;
+ int ok;
+ wchar_t lc1, lc2;
+ char *sgpathp;
+ struct stat stb;
+ wchar_t c, cc;
+ int len_s, len_p;
+
+ globbed = 1;
+ for (;;) {
+ if ((len_s = mbtowc(&scc, s, MB_CUR_MAX)) <= 0) {
+ scc = (unsigned char)*s;
+ len_s = 1;
+ }
+ /* scc = *s++ & TRIM; */
+ s += len_s;
+
+ if ((len_p = mbtowc(&c, p, MB_CUR_MAX)) <= 0) {
+ c = (unsigned char)*p;
+ len_p = 1;
+ }
+ p += len_p;
+ switch (c) {
+
+ case '{':
+ return (execbrc(p - len_p, s - len_s));
+
+ case '[':
+ ok = 0;
+ lc1 = 0;
+ while ((cc = *p) != '\0') {
+ if ((len_p = mbtowc(&cc, p, MB_CUR_MAX)) <= 0) {
+ cc = (unsigned char)*p;
+ len_p = 1;
+ }
+ p += len_p;
+ if (cc == ']') {
+ if (ok)
+ break;
+ return (0);
+ }
+ if (cc == '-') {
+ if ((len_p = mbtowc(&lc2, p,
+ MB_CUR_MAX)) <= 0) {
+ lc2 = (unsigned char)*p;
+ len_p = 1;
+ }
+ p += len_p;
+ if (ftp_fnmatch(scc, lc1, lc2))
+ ok++;
+ } else
+ if (scc == (lc1 = cc))
+ ok++;
+ }
+ if (cc == 0)
+ if (!ok)
+ return (0);
+ continue;
+
+ case '*':
+ if (!*p)
+ return (1);
+ if (*p == '/') {
+ p++;
+ goto slash;
+ }
+ s -= len_s;
+ do {
+ if (amatch(s, p))
+ return (1);
+ } while (*s++);
+ return (0);
+
+ case 0:
+ return (scc == 0);
+
+ default:
+ if (c != scc)
+ return (0);
+ continue;
+
+ case '?':
+ if (scc == 0)
+ return (0);
+ continue;
+
+ case '/':
+ if (scc)
+ return (0);
+slash:
+ s = entp;
+ sgpathp = gpathp;
+ while (*s)
+ addpath(*s++);
+ addpath('/');
+ if (stat(gpath, &stb) == 0 && S_ISDIR(stb.st_mode))
+ if (*p == 0) {
+ Gcat(gpath, "");
+ globcnt++;
+ } else
+ expand(p);
+ gpathp = sgpathp;
+ *gpathp = 0;
+ return (0);
+ }
+ }
+}
+
+#ifdef notdef
+static
+Gmatch(s, p)
+ register char *s, *p;
+{
+ register int scc;
+ int ok, lc;
+ int c, cc;
+
+ for (;;) {
+ scc = *s++ & TRIM;
+ switch (c = *p++) {
+
+ case '[':
+ ok = 0;
+ lc = 077777;
+ while (cc = *p++) {
+ if (cc == ']') {
+ if (ok)
+ break;
+ return (0);
+ }
+ if (cc == '-') {
+ if (lc <= scc && scc <= *p++)
+ ok++;
+ } else
+ if (scc == (lc = cc))
+ ok++;
+ }
+ if (cc == 0)
+ if (ok)
+ p--;
+ else
+ return (0);
+ continue;
+
+ case '*':
+ if (!*p)
+ return (1);
+ for (s--; *s; s++)
+ if (Gmatch(s, p))
+ return (1);
+ return (0);
+
+ case 0:
+ return (scc == 0);
+
+ default:
+ if ((c & TRIM) != scc)
+ return (0);
+ continue;
+
+ case '?':
+ if (scc == 0)
+ return (0);
+ continue;
+
+ }
+ }
+}
+#endif
+
+static void
+Gcat(char *s1, char *s2)
+{
+ if (gargc >= agargv_size - 1) {
+ char **tmp;
+
+ if (globerr) {
+ return;
+ }
+ tmp = (char **)realloc(agargv,
+ (agargv_size + GAVSIZ) * sizeof (char *));
+ if (tmp == NULL) {
+ globerr = "Arguments too long";
+ return;
+ } else {
+ agargv = tmp;
+ agargv_size += GAVSIZ;
+ }
+ gargv = agargv;
+ sortbas = agargv;
+ }
+ gargc++;
+ gargv[gargc] = 0;
+ gargv[gargc - 1] = strspl(s1, s2);
+}
+
+static void
+addpath(char c)
+{
+
+ if (gpathp >= lastgpathp)
+ globerr = "Pathname too long";
+ else {
+ *gpathp++ = c;
+ *gpathp = 0;
+ }
+}
+
+static void
+rscan(char **t, int (*f)(char))
+{
+ register char *p, c;
+ int len;
+
+ while (p = *t++) {
+ if (f == tglob)
+ if (*p == '~')
+ gflag |= 2;
+ else if (eq(p, "{") || eq(p, "{}"))
+ continue;
+ while ((c = *p) != '\0') {
+ (void) (*f)(c);
+ if ((len = mblen(p, MB_CUR_MAX)) <= 0)
+ len = 1;
+ p += len;
+ }
+ }
+}
+
+static int
+tglob(char c)
+{
+ if (any(c, globchars))
+ gflag |= c == '{' ? 2 : 1;
+ return (c);
+}
+
+static int
+letter(char c)
+{
+ return (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '_');
+}
+
+static int
+digit(char c)
+{
+ return (c >= '0' && c <= '9');
+}
+
+static int
+any(int c, char *s)
+{
+ int len;
+
+ while (*s) {
+ if (*s == c)
+ return (1);
+ if ((len = mblen(s, MB_CUR_MAX)) <= 0)
+ len = 1;
+ s += len;
+ }
+ return (0);
+}
+
+static int
+blklen(char **av)
+{
+ register int i = 0;
+
+ while (*av++)
+ i++;
+ return (i);
+}
+
+static char **
+blkcpy(char **oav, char **bv)
+{
+ register char **av = oav;
+
+ while (*av++ = *bv++)
+ continue;
+ return (oav);
+}
+
+void
+blkfree(char **av0)
+{
+ register char **av = av0;
+
+ while (*av)
+ xfree(*av++);
+ free(av0);
+}
+
+static void
+xfree(char *cp)
+{
+ extern char end[];
+
+ if (cp >= end && cp < (char *)&cp)
+ free(cp);
+}
+
+static char *
+strspl(char *cp, char *dp)
+{
+ register char *ep = malloc((unsigned)(strlen(cp) + strlen(dp) + 1));
+
+ if (ep == (char *)0)
+ fatal("Out of memory");
+ (void) strcpy(ep, cp);
+ (void) strcat(ep, dp);
+ return (ep);
+}
+
+static char **
+copyblk(char **v)
+{
+ register char **nv = (char **)malloc((unsigned)((blklen(v) + 1) *
+ sizeof (char **)));
+
+ if (nv == (char **)0)
+ fatal("Out of memory");
+
+ return (blkcpy(nv, v));
+}
+
+static char *
+strend(char *cp)
+{
+
+ while (*cp)
+ cp++;
+ return (cp);
+}
+/*
+ * Extract a home directory from the password file
+ * The argument points to a buffer where the name of the
+ * user whose home directory is sought is currently.
+ * We write the home directory of the user back there.
+ */
+gethdir(char *home)
+{
+ register struct passwd *pp = getpwnam(home);
+
+ if (!pp || home + strlen(pp->pw_dir) >= lastgpathp)
+ return (1);
+ (void) strcpy(home, pp->pw_dir);
+ return (0);
+}
+
+static int
+ftp_fnmatch(wchar_t t_ch, wchar_t t_fch, wchar_t t_lch)
+{
+ char t_char[MB_LEN_MAX + 1];
+ char t_patan[MB_LEN_MAX * 2 + 8];
+ char *p;
+ int i;
+
+ if ((t_ch == t_fch) || (t_ch == t_lch))
+ return (1);
+
+ p = t_patan;
+ if ((i = wctomb(t_char, (wchar_t)t_ch)) <= 0)
+ return (0);
+ t_char[i] = 0;
+
+ *p++ = '[';
+ if ((i = wctomb(p, (wchar_t)t_fch)) <= 0)
+ return (0);
+ p += i;
+ *p++ = '-';
+ if ((i = wctomb(p, (wchar_t)t_lch)) <= 0)
+ return (0);
+ p += i;
+ *p++ = ']';
+ *p = 0;
+
+ if (fnmatch(t_patan, t_char, FNM_NOESCAPE))
+ return (0);
+ return (1);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/ftp/main.c b/usr/src/cmd/cmd-inet/usr.bin/ftp/main.c
new file mode 100644
index 0000000000..852c533a8e
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/ftp/main.c
@@ -0,0 +1,690 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * FTP User Program -- Command Interface.
+ */
+#define EXTERN
+#include "ftp_var.h"
+#include <deflt.h> /* macros that make using libcmd easier */
+
+static void usage(void);
+static void timeout_sig(int sig);
+static void cmdscanner(int top);
+static void intr(int sig);
+static char *slurpstring(void);
+extern int use_eprt;
+
+boolean_t ls_invokes_NLST = B_TRUE;
+
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_ext.h>
+#define GETOPT_STR "dginptvET:axfm:"
+#define USAGE_STR "[-adfginptvx] [-m mech] [-T timeout] " \
+ "[hostname [port]]"
+
+int
+main(int argc, char *argv[])
+{
+ char *cp;
+ int c, top;
+ struct passwd *pw = NULL;
+ char homedir[MAXPATHLEN];
+ char *temp_string = NULL;
+
+ (void) setlocale(LC_ALL, "");
+
+ buf = (char *)memalign(getpagesize(), FTPBUFSIZ);
+ if (buf == NULL) {
+ (void) fprintf(stderr, "ftp: memory allocation failed\n");
+ return (1);
+ }
+
+ timeoutms = timeout = 0;
+ doglob = 1;
+ interactive = 1;
+ autologin = 1;
+
+ autoauth = 0;
+ fflag = 0;
+ autoencrypt = 0;
+ goteof = 0;
+ mechstr[0] = '\0';
+
+ sendport = -1; /* tri-state variable. start out in "automatic" mode. */
+ passivemode = 0;
+
+ while ((c = getopt(argc, argv, GETOPT_STR)) != EOF) {
+ switch (c) {
+ case 'd':
+ options |= SO_DEBUG;
+ debug++;
+ break;
+
+ case 'g':
+ doglob = 0;
+ break;
+
+ case 'i':
+ interactive = 0;
+ break;
+
+ case 'n':
+ autologin = 0;
+ break;
+
+ case 'p':
+ passivemode = 1;
+ break;
+
+ case 't':
+ trace++;
+ break;
+
+ case 'v':
+ verbose++;
+ break;
+
+ /* undocumented option: allows testing of EPRT */
+ case 'E':
+ use_eprt = 1;
+ break;
+
+ case 'T':
+ if (!isdigit(*optarg)) {
+ (void) fprintf(stderr,
+ "ftp: bad timeout: \"%s\"\n", optarg);
+ break;
+ }
+ timeout = atoi(optarg);
+ timeoutms = timeout * MILLISEC;
+ break;
+
+ case 'a':
+ autoauth = 1;
+ break;
+
+ case 'f':
+ autoauth = 1;
+ fflag = 1;
+ break;
+
+ case 'm':
+ autoauth = 1;
+ call(setmech, "ftp", optarg, 0);
+ if (code != 0)
+ exit(1);
+ break;
+
+ case 'x':
+ autoauth = 1;
+ autoencrypt = 1;
+ break;
+
+ case '?':
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc > 2)
+ usage();
+
+ fromatty = isatty(fileno(stdin));
+ /*
+ * Scan env, then DEFAULTFTPFILE
+ * for FTP_LS_SENDS_NLST
+ */
+ temp_string = getenv("FTP_LS_SENDS_NLST");
+ if (temp_string == NULL) { /* env var not set */
+ if (defopen(DEFAULTFTPFILE) == 0) {
+ /*
+ * turn off case sensitivity
+ */
+ int flags = defcntl(DC_GETFLAGS, 0);
+
+ TURNOFF(flags, DC_CASE);
+ (void) defcntl(DC_SETFLAGS, flags);
+
+ temp_string = defread("FTP_LS_SENDS_NLST=");
+ (void) defopen(NULL); /* close default file */
+ }
+ }
+ if (temp_string != NULL &&
+ strncasecmp(temp_string, "n", 1) == 0)
+ ls_invokes_NLST = B_FALSE;
+
+ /*
+ * Set up defaults for FTP.
+ */
+ (void) strcpy(typename, "ascii"), type = TYPE_A;
+ (void) strcpy(formname, "non-print"), form = FORM_N;
+ (void) strcpy(modename, "stream"), mode = MODE_S;
+ (void) strcpy(structname, "file"), stru = STRU_F;
+ (void) strcpy(bytename, "8"), bytesize = 8;
+ if (fromatty)
+ verbose++;
+ cpend = 0; /* no pending replies */
+ proxy = 0; /* proxy not active */
+ crflag = 1; /* strip c.r. on ascii gets */
+
+ if (mechstr[0] == '\0') {
+ strlcpy(mechstr, FTP_DEF_MECH, MECH_SZ);
+ }
+
+ /*
+ * Set up the home directory in case we're globbing.
+ */
+ cp = getlogin();
+ if (cp != NULL) {
+ pw = getpwnam(cp);
+ }
+ if (pw == NULL)
+ pw = getpwuid(getuid());
+ if (pw != NULL) {
+ home = homedir;
+ (void) strcpy(home, pw->pw_dir);
+ }
+ if (setjmp(timeralarm)) {
+ (void) fflush(stdout);
+ (void) printf("Connection timeout\n");
+ exit(1);
+ }
+ (void) signal(SIGALRM, timeout_sig);
+ reset_timer();
+ if (argc > 0) {
+ int nargc = 0;
+ char *nargv[4];
+
+ if (setjmp(toplevel))
+ return (0);
+ (void) signal(SIGINT, intr);
+ (void) signal(SIGPIPE, lostpeer);
+ nargv[nargc++] = "ftp";
+ nargv[nargc++] = argv[0]; /* hostname */
+ if (argc > 1)
+ nargv[nargc++] = argv[1]; /* port */
+ nargv[nargc] = NULL;
+ setpeer(nargc, nargv);
+ }
+ top = setjmp(toplevel) == 0;
+ if (top) {
+ (void) signal(SIGINT, intr);
+ (void) signal(SIGPIPE, lostpeer);
+ }
+
+ for (;;) {
+ cmdscanner(top);
+ top = 1;
+ }
+}
+
+static void
+usage(void)
+{
+ (void) fprintf(stderr, "usage: ftp %s\n", USAGE_STR);
+ exit(1);
+}
+
+void
+reset_timer()
+{
+ /* The test is just to reduce syscalls if timeouts aren't used */
+ if (timeout)
+ alarm(timeout);
+}
+
+void
+stop_timer()
+{
+ if (timeout)
+ alarm(0);
+}
+
+/*ARGSUSED*/
+static void
+timeout_sig(int sig)
+{
+ longjmp(timeralarm, 1);
+}
+
+/*ARGSUSED*/
+static void
+intr(int sig)
+{
+ longjmp(toplevel, 1);
+}
+
+/*ARGSUSED*/
+void
+lostpeer(int sig)
+{
+ extern FILE *ctrl_out;
+ extern int data;
+
+ if (connected) {
+ if (ctrl_out != NULL) {
+ (void) shutdown(fileno(ctrl_out), 1+1);
+ (void) fclose(ctrl_out);
+ ctrl_out = NULL;
+ }
+ if (data >= 0) {
+ (void) shutdown(data, 1+1);
+ (void) close(data);
+ data = -1;
+ }
+ connected = 0;
+
+ auth_type = AUTHTYPE_NONE;
+ clevel = dlevel = PROT_C;
+ goteof = 0;
+ }
+ pswitch(1);
+ if (connected) {
+ if (ctrl_out != NULL) {
+ (void) shutdown(fileno(ctrl_out), 1+1);
+ (void) fclose(ctrl_out);
+ ctrl_out = NULL;
+ }
+ connected = 0;
+
+ auth_type = AUTHTYPE_NONE;
+ clevel = dlevel = PROT_C;
+ goteof = 0;
+ }
+ proxflag = 0;
+ pswitch(0);
+}
+
+/*
+ * Command parser.
+ */
+static void
+cmdscanner(int top)
+{
+ struct cmd *c;
+
+ if (!top)
+ (void) putchar('\n');
+ for (;;) {
+ stop_timer();
+ if (fromatty) {
+ (void) printf("ftp> ");
+ (void) fflush(stdout);
+ }
+ if (fgets(line, sizeof (line), stdin) == 0) {
+ if (feof(stdin) || ferror(stdin))
+ quit(0, NULL);
+ break;
+ }
+ if (line[0] == 0)
+ break;
+ /* If not all, just discard rest of line */
+ if (line[strlen(line)-1] != '\n') {
+ while (fgetc(stdin) != '\n' && !feof(stdin) &&
+ !ferror(stdin))
+ ;
+ (void) printf("Line too long\n");
+ continue;
+ } else
+ line[strlen(line)-1] = 0;
+
+ makeargv();
+ if (margc == 0) {
+ continue;
+ }
+ c = getcmd(margv[0]);
+ if (c == (struct cmd *)-1) {
+ (void) printf("?Ambiguous command\n");
+ continue;
+ }
+ if (c == 0) {
+ (void) printf("?Invalid command\n");
+ continue;
+ }
+ if (c->c_conn && !connected) {
+ (void) printf("Not connected.\n");
+ continue;
+ }
+ reset_timer();
+ (*c->c_handler)(margc, margv);
+#ifndef CTRL
+#define CTRL(c) ((c)&037)
+#endif
+ stop_timer();
+ if (bell && c->c_bell)
+ (void) putchar(CTRL('g'));
+ if (c->c_handler != help)
+ break;
+ }
+ (void) signal(SIGINT, intr);
+ (void) signal(SIGPIPE, lostpeer);
+}
+
+struct cmd *
+getcmd(char *name)
+{
+ char *p, *q;
+ struct cmd *c, *found;
+ int nmatches, longest;
+ extern struct cmd cmdtab[];
+
+ if (name == NULL)
+ return (0);
+
+ longest = 0;
+ nmatches = 0;
+ found = 0;
+ for (c = cmdtab; (p = c->c_name) != NULL; c++) {
+ for (q = name; *q == *p++; q++)
+ if (*q == 0) /* exact match? */
+ return (c);
+ if (!*q) { /* the name was a prefix */
+ if (q - name > longest) {
+ longest = q - name;
+ nmatches = 1;
+ found = c;
+ } else if (q - name == longest)
+ nmatches++;
+ }
+ }
+ if (nmatches > 1)
+ return ((struct cmd *)-1);
+ return (found);
+}
+
+/*
+ * Slice a string up into argc/argv.
+ */
+
+static int slrflag;
+#define MARGV_INC 20
+
+void
+makeargv(void)
+{
+ char **argp;
+ static int margv_size;
+
+ margc = 0;
+ stringbase = line; /* scan from first of buffer */
+ argbase = argbuf; /* store from first of buffer */
+ slrflag = 0;
+
+ if (!margv) {
+ margv_size = MARGV_INC;
+ if ((margv = malloc(margv_size * sizeof (char *))) == NULL)
+ fatal("Out of memory");
+ }
+ argp = margv;
+ while (*argp++ = slurpstring()) {
+ margc++;
+ if (margc == margv_size) {
+ margv_size += MARGV_INC;
+ if ((margv = realloc(margv,
+ margv_size * sizeof (char *))) == NULL)
+ fatal("Out of memory");
+ argp = margv + margc;
+ }
+ }
+}
+
+/*
+ * Parse string into argbuf;
+ * implemented with FSM to
+ * handle quoting and strings
+ */
+static char *
+slurpstring(void)
+{
+ int got_one = 0;
+ char *sb = stringbase;
+ char *ap = argbase;
+ char *tmp = argbase; /* will return this if token found */
+ int len;
+
+ if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */
+ switch (slrflag) { /* and $ as token for macro invoke */
+ case 0:
+ slrflag++;
+ stringbase++;
+ return ((*sb == '!') ? "!" : "$");
+ case 1:
+ slrflag++;
+ altarg = stringbase;
+ break;
+ default:
+ break;
+ }
+ }
+
+S0:
+ switch (*sb) {
+
+ case '\0':
+ goto OUT;
+
+ case ' ':
+ case '\t':
+ sb++; goto S0;
+
+ default:
+ switch (slrflag) {
+ case 0:
+ slrflag++;
+ break;
+ case 1:
+ slrflag++;
+ altarg = sb;
+ break;
+ default:
+ break;
+ }
+ goto S1;
+ }
+
+S1:
+ switch (*sb) {
+
+ case ' ':
+ case '\t':
+ case '\0':
+ goto OUT; /* end of token */
+
+ case '\\':
+ sb++; goto S2; /* slurp next character */
+
+ case '"':
+ sb++; goto S3; /* slurp quoted string */
+
+ default:
+ if ((len = mblen(sb, MB_CUR_MAX)) <= 0)
+ len = 1;
+ memcpy(ap, sb, len);
+ ap += len;
+ sb += len;
+ got_one = 1;
+ goto S1;
+ }
+
+S2:
+ switch (*sb) {
+
+ case '\0':
+ goto OUT;
+
+ default:
+ if ((len = mblen(sb, MB_CUR_MAX)) <= 0)
+ len = 1;
+ memcpy(ap, sb, len);
+ ap += len;
+ sb += len;
+ got_one = 1;
+ goto S1;
+ }
+
+S3:
+ switch (*sb) {
+
+ case '\0':
+ goto OUT;
+
+ case '"':
+ sb++; goto S1;
+
+ default:
+ if ((len = mblen(sb, MB_CUR_MAX)) <= 0)
+ len = 1;
+ memcpy(ap, sb, len);
+ ap += len;
+ sb += len;
+ got_one = 1;
+ goto S3;
+ }
+
+OUT:
+ if (got_one)
+ *ap++ = '\0';
+ argbase = ap; /* update storage pointer */
+ stringbase = sb; /* update scan pointer */
+ if (got_one) {
+ return (tmp);
+ }
+ switch (slrflag) {
+ case 0:
+ slrflag++;
+ break;
+ case 1:
+ slrflag++;
+ altarg = (char *)0;
+ break;
+ default:
+ break;
+ }
+ return ((char *)0);
+}
+
+#define HELPINDENT (sizeof ("directory"))
+
+/*
+ * Help command.
+ * Call each command handler with argc == 0 and argv[0] == name.
+ */
+void
+help(int argc, char *argv[])
+{
+ struct cmd *c;
+ extern struct cmd cmdtab[];
+
+ if (argc == 1) {
+ int i, j, w, k;
+ int columns, width = 0, lines;
+ extern int NCMDS;
+
+ (void) printf(
+ "Commands may be abbreviated. Commands are:\n\n");
+ for (c = cmdtab; c < &cmdtab[NCMDS]; c++) {
+ int len = strlen(c->c_name);
+
+ if (len > width)
+ width = len;
+ }
+ width = (width + 8) &~ 7;
+ columns = 80 / width;
+ if (columns == 0)
+ columns = 1;
+ lines = (NCMDS + columns - 1) / columns;
+ for (i = 0; i < lines; i++) {
+ for (j = 0; j < columns; j++) {
+ c = cmdtab + j * lines + i;
+ if (c->c_name && (!proxy || c->c_proxy)) {
+ (void) printf("%s", c->c_name);
+ } else if (c->c_name) {
+ for (k = 0; k < strlen(c->c_name);
+ k++) {
+ (void) putchar(' ');
+ }
+ }
+ if (c + lines >= &cmdtab[NCMDS]) {
+ (void) printf("\n");
+ break;
+ }
+ w = strlen(c->c_name);
+ while (w < width) {
+ w = (w + 8) &~ 7;
+ (void) putchar('\t');
+ }
+ }
+ }
+ return;
+ }
+ while (--argc > 0) {
+ char *arg;
+ arg = *++argv;
+ c = getcmd(arg);
+ if (c == (struct cmd *)-1)
+ (void) printf("?Ambiguous help command %s\n", arg);
+ else if (c == (struct cmd *)0)
+ (void) printf("?Invalid help command %s\n", arg);
+ else
+ (void) printf("%-*s\t%s\n", HELPINDENT,
+ c->c_name, c->c_help);
+ }
+}
+
+/*
+ * Call routine with argc, argv set from args (terminated by 0).
+ */
+void
+call(void (*routine)(int argc, char *argv[]), ...)
+{
+ va_list ap;
+ char *argv[10];
+ int argc = 0;
+
+ va_start(ap, routine);
+ while ((argv[argc] = va_arg(ap, char *)) != (char *)0)
+ argc++;
+ va_end(ap);
+ (*routine)(argc, argv);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/ftp/pclose.c b/usr/src/cmd/cmd-inet/usr.bin/ftp/pclose.c
new file mode 100644
index 0000000000..53dbec6935
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/ftp/pclose.c
@@ -0,0 +1,162 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1996 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "ftp_var.h"
+
+#ifndef sigmask
+#define sigmask(m) (1 << ((m)-1))
+#endif
+
+#define set2mask(setp) ((setp)->__sigbits[0])
+#define mask2set(mask, setp) \
+ ((mask) == -1 ? sigfillset(setp) : (((setp)->__sigbits[0]) = (mask)))
+
+
+static int
+sigsetmask(int mask)
+{
+ sigset_t oset;
+ sigset_t nset;
+
+ (void) sigprocmask(0, (sigset_t *)0, &nset);
+ mask2set(mask, &nset);
+ (void) sigprocmask(SIG_SETMASK, &nset, &oset);
+ return (set2mask(&oset));
+}
+
+static int
+sigblock(int mask)
+{
+ sigset_t oset;
+ sigset_t nset;
+
+ (void) sigprocmask(0, (sigset_t *)0, &nset);
+ mask2set(mask, &nset);
+ (void) sigprocmask(SIG_BLOCK, &nset, &oset);
+ return (set2mask(&oset));
+}
+
+#define signal(s, f) sigset(s, f)
+
+#define tst(a, b) (*mode == 'r'? (b) : (a))
+#define RDR 0
+#define WTR 1
+#define NOFILES 20 /* just in case */
+
+static pid_t *popen_pid;
+static rlim_t nfiles = 0;
+
+FILE *
+mypopen(char *cmd, char *mode)
+{
+ int p[2];
+ pid_t pid;
+ int myside, remside, i;
+ struct rlimit rl;
+
+ if (nfiles <= 0) {
+ if (getrlimit(RLIMIT_NOFILE, &rl) == 0)
+ nfiles = rl.rlim_max;
+ else
+ nfiles = NOFILES;
+ }
+ if (popen_pid == NULL) {
+ popen_pid = (pid_t *)malloc((unsigned)nfiles *
+ sizeof (*popen_pid));
+ if (popen_pid == NULL)
+ return (NULL);
+ for (i = 0; i < nfiles; i++)
+ popen_pid[i] = (pid_t)-1;
+ }
+ if (pipe(p) < 0)
+ return (NULL);
+ myside = tst(p[WTR], p[RDR]);
+ remside = tst(p[RDR], p[WTR]);
+ if ((pid = vfork()) == 0) {
+ /* myside and remside reverse roles in child */
+ (void) close(myside);
+ if (remside != tst(0, 1)) {
+ (void) dup2(remside, tst(0, 1));
+ (void) close(remside);
+ }
+ execl("/bin/sh", "sh", "-c", cmd, (char *)NULL);
+ _exit(127);
+ }
+ if (pid == (pid_t)-1) {
+ (void) close(myside);
+ (void) close(remside);
+ return (NULL);
+ }
+ popen_pid[myside] = pid;
+ (void) close(remside);
+ return (fdopen(myside, mode));
+}
+
+/*ARGSUSED*/
+static void
+pabort(int sig)
+{
+ extern int mflag;
+
+ mflag = 0;
+}
+
+int
+mypclose(FILE *ptr)
+{
+ pid_t child, pid;
+ int omask;
+ void (*istat)();
+ int status;
+
+ child = popen_pid[fileno(ptr)];
+ popen_pid[fileno(ptr)] = (pid_t)-1;
+ (void) fclose(ptr);
+ if (child == (pid_t)-1)
+ return (-1);
+ istat = signal(SIGINT, pabort);
+ omask = sigblock(sigmask(SIGQUIT)|sigmask(SIGHUP));
+ while ((pid = wait(&status)) != child && pid != (pid_t)-1)
+ ;
+ (void) sigsetmask(omask);
+ (void) signal(SIGINT, istat);
+ return (pid == (pid_t)-1 ? -1 : 0);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/ftp/ruserpass.c b/usr/src/cmd/cmd-inet/usr.bin/ftp/ruserpass.c
new file mode 100644
index 0000000000..db87c776f2
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/ftp/ruserpass.c
@@ -0,0 +1,304 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "ftp_var.h"
+
+static FILE *cfile;
+
+static int rnetrc(char *host, char **aname, char **apass, char **aacct);
+static int token(void);
+
+int
+ruserpass(char *host, char **aname, char **apass, char **aacct)
+{
+#if 0
+ renv(host, aname, apass, aacct);
+ if (*aname == 0 || *apass == 0)
+#endif
+ return (rnetrc(host, aname, apass, aacct));
+}
+
+#define DEFAULT 1
+#define LOGIN 2
+#define PASSWD 3
+#define ACCOUNT 4
+#define MACDEF 5
+#define ID 10
+#define MACHINE 11
+
+static char tokval[100];
+
+static struct toktab {
+ char *tokstr;
+ int tval;
+} toktab[] = {
+ "default", DEFAULT,
+ "login", LOGIN,
+ "password", PASSWD,
+ "account", ACCOUNT,
+ "machine", MACHINE,
+ "macdef", MACDEF,
+ 0, 0
+};
+
+static int
+rnetrc(char *host, char **aname, char **apass, char **aacct)
+{
+ char *hdir, buf[PATH_MAX+1], *tmp;
+ int t, i, c;
+ struct stat stb;
+ extern int errno;
+
+ hdir = getenv("HOME");
+ if (hdir == NULL)
+ hdir = ".";
+ if (snprintf(buf, sizeof (buf), "%s/.netrc", hdir) >= sizeof (buf)) {
+ fprintf(stderr, ".netrc: %s\n", strerror(ENAMETOOLONG));
+ exit(1);
+ }
+
+ cfile = fopen(buf, "r");
+ if (cfile == NULL) {
+ if (errno != ENOENT)
+ perror(buf);
+ return (0);
+ }
+next:
+ while ((t = token()))
+ switch (t) {
+
+ case MACHINE:
+ if (token() != ID || strcmp(host, tokval))
+ continue;
+ /* "machine name" matches host */
+ /* FALLTHROUGH */
+
+ case DEFAULT:
+ /* "default" matches any host */
+ while (((t = token()) != 0) && t != MACHINE && t != DEFAULT)
+ switch (t) {
+
+ case LOGIN:
+ if (token())
+ if (*aname == 0) {
+ *aname = malloc((unsigned)
+ strlen(tokval) + 1);
+ if (*aname == NULL) {
+ fprintf(stderr,
+ "Error - out of VM\n");
+ exit(1);
+ }
+ (void) strcpy(*aname, tokval);
+ } else {
+ if (strcmp(*aname, tokval))
+ goto next;
+ }
+ break;
+ case PASSWD:
+ if (fstat(fileno(cfile), &stb) >= 0 &&
+ (stb.st_mode & 077) != 0) {
+ fprintf(stderr, "Error - .netrc file not "
+ "correct mode.\n");
+ fprintf(stderr, "Remove password or correct "
+ "mode.\n");
+ return (-1);
+ }
+ if (token() && *apass == 0) {
+ *apass = malloc((unsigned)strlen(tokval) + 1);
+ if (*apass == NULL) {
+ fprintf(stderr, "Error - out of VM\n");
+ exit(1);
+ }
+ (void) strcpy(*apass, tokval);
+ }
+ break;
+ case ACCOUNT:
+ if (fstat(fileno(cfile), &stb) >= 0 &&
+ (stb.st_mode & 077) != 0) {
+ fprintf(stderr, "Error - .netrc file not "
+ "correct mode.\n");
+ fprintf(stderr, "Remove account or correct "
+ "mode.\n");
+ return (-1);
+ }
+ if (token() && *aacct == 0) {
+ *aacct = malloc((unsigned)strlen(tokval) + 1);
+ if (*aacct == NULL) {
+ fprintf(stderr, "Error - out of VM\n");
+ exit(1);
+ }
+ (void) strcpy(*aacct, tokval);
+ }
+ break;
+ case MACDEF:
+ if (proxy) {
+ return (0);
+ }
+ while ((c = getc(cfile)) != EOF && c == ' ' ||
+ c == '\t');
+ if (c == EOF || c == '\n') {
+ printf("Missing macdef name argument.\n");
+ return (-1);
+ }
+ if (macnum == 16) {
+ printf("Limit of 16 macros have already "
+ "been defined\n");
+ return (-1);
+ }
+ tmp = macros[macnum].mac_name;
+ *tmp++ = c;
+ for (i = 0; i < 8 && (c = getc(cfile)) != EOF &&
+ !isspace(c); ++i) {
+ *tmp++ = c;
+ }
+ if (c == EOF) {
+ printf("Macro definition for `%s` missing "
+ "null line terminator.\n",
+ macros[macnum].mac_name);
+ return (-1);
+ }
+ *tmp = '\0';
+ if (c != '\n') {
+ while ((c = getc(cfile)) != EOF && c != '\n');
+ }
+ if (c == EOF) {
+ printf("Macro definition for `%s` missing "
+ "null line terminator.\n",
+ macros[macnum].mac_name);
+ return (-1);
+ }
+ if (macnum == 0) {
+ macros[macnum].mac_start = macbuf;
+ } else {
+ macros[macnum].mac_start =
+ macros[macnum-1].mac_end + 1;
+ }
+ tmp = macros[macnum].mac_start;
+ while (tmp != macbuf + 4096) {
+ if ((c = getc(cfile)) == EOF) {
+ printf("Macro definition for `%s` missing "
+ "null line terminator.\n",
+ macros[macnum].mac_name);
+ return (-1);
+ }
+ *tmp = c;
+ if (*tmp == '\n') {
+ if (*(tmp-1) == '\0') {
+ macros[macnum++].mac_end =
+ tmp - 1;
+ break;
+ }
+ *tmp = '\0';
+ }
+ tmp++;
+ }
+ if (tmp == macbuf + 4096) {
+ printf("4K macro buffer exceeded\n");
+ return (-1);
+ }
+ if (*macros[macnum - 1].mac_start == '\n') {
+ printf("Macro definition for `%s` is empty, "
+ "macro not stored.\n",
+ macros[--macnum].mac_name);
+ }
+ break;
+ default:
+ fprintf(stderr, "Unknown .netrc keyword %s\n", tokval);
+ break;
+ }
+ goto done;
+ }
+done:
+ (void) fclose(cfile);
+ return (0);
+}
+
+static int
+token(void)
+{
+ char *cp;
+ int c;
+ struct toktab *t;
+ int len;
+
+ if (feof(cfile))
+ return (0);
+ while ((c = fgetwc(cfile)) != EOF &&
+ (c == '\n' || c == '\t' || c == ' ' || c == ','))
+ continue;
+ if (c == EOF)
+ return (0);
+ cp = tokval;
+ if (c == '"') {
+ while ((c = fgetwc(cfile)) != EOF && c != '"') {
+ if (c == '\\')
+ c = fgetwc(cfile);
+ if ((len = wctomb(cp, c)) <= 0) {
+ len = 1;
+ *cp = (unsigned char)c;
+ }
+ cp += len;
+ }
+ } else {
+ if ((len = wctomb(cp, c)) <= 0) {
+ *cp = (unsigned char)c;
+ len = 1;
+ }
+ cp += len;
+ while ((c = fgetwc(cfile)) != EOF && c != '\n' && c != '\t' &&
+ c != ' ' && c != ',') {
+ if (c == '\\')
+ c = fgetwc(cfile);
+ if ((len = wctomb(cp, c)) <= 0) {
+ len = 1;
+ *cp = (unsigned char)c;
+ }
+ cp += len;
+ }
+ }
+ *cp = 0;
+ if (tokval[0] == 0)
+ return (0);
+ for (t = toktab; t->tokstr; t++)
+ if (strcmp(t->tokstr, tokval) == 0)
+ return (t->tval);
+ return (ID);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/ftp/secure.c b/usr/src/cmd/cmd-inet/usr.bin/ftp/secure.c
new file mode 100644
index 0000000000..3bd6562325
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/ftp/secure.c
@@ -0,0 +1,388 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Shared routines for client and server for
+ * secure read(), write(), getc(), and putc().
+ * Only one security context, thus only work on one fd at a time!
+ */
+
+#include "ftp_var.h"
+#include <gssapi/gssapi.h>
+#include <arpa/ftp.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <errno.h>
+
+extern struct sockaddr_in hisaddr;
+extern struct sockaddr_in myaddr;
+extern int dlevel;
+extern int auth_type;
+extern uint_t maxbuf; /* maximum output buffer size */
+extern uchar_t *ucbuf; /* cleartext buffer */
+static uint_t nout; /* number of chars in ucbuf */
+static uint_t smaxbuf; /* Internal saved value of maxbuf */
+static uint_t smaxqueue; /* Maximum allowed to queue before flush */
+
+extern gss_ctx_id_t gcontext;
+static int secure_putbuf(int, uchar_t *, uint_t);
+
+static int
+looping_write(int fd, const char *buf, int len)
+{
+ int cc, len2 = 0;
+
+ if (len == 0)
+ return (0);
+
+ do {
+ cc = write(fd, buf, len);
+ if (cc < 0) {
+ if (errno == EINTR)
+ continue;
+ return (cc);
+ } else if (cc == 0) {
+ return (len2);
+ } else {
+ buf += cc;
+ len2 += cc;
+ len -= cc;
+ }
+ } while (len > 0);
+ return (len2);
+}
+
+static int
+looping_read(int fd, char *buf, int len)
+{
+ int cc, len2 = 0;
+
+ do {
+ cc = read(fd, buf, len);
+ if (cc < 0) {
+ if (errno == EINTR)
+ continue;
+ return (cc); /* errno is already set */
+ } else if (cc == 0) {
+ return (len2);
+ } else {
+ buf += cc;
+ len2 += cc;
+ len -= cc;
+ }
+ } while (len > 0);
+ return (len2);
+}
+
+#define ERR -2
+
+static void
+secure_error(char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ putc('\n', stderr);
+}
+
+/*
+ * Given maxbuf as a buffer size, determine how much can we
+ * really transfer given the overhead of different algorithms
+ *
+ * Sets smaxbuf and smaxqueue
+ */
+
+static int
+secure_determine_constants(void)
+{
+ smaxbuf = maxbuf;
+ smaxqueue = maxbuf;
+
+ if (auth_type == AUTHTYPE_GSSAPI) {
+ OM_uint32 maj_stat, min_stat, mlen;
+ OM_uint32 msize = maxbuf;
+
+ maj_stat = gss_wrap_size_limit(&min_stat, gcontext,
+ (dlevel == PROT_P),
+ GSS_C_QOP_DEFAULT,
+ msize, &mlen);
+ if (maj_stat != GSS_S_COMPLETE) {
+ user_gss_error(maj_stat, min_stat,
+ "GSSAPI fudge determination");
+ /* Return error how? */
+ return (ERR);
+ }
+ smaxqueue = mlen;
+ }
+
+ return (0);
+}
+
+static uchar_t
+secure_putbyte(int fd, uchar_t c)
+{
+ int ret;
+
+ if ((smaxbuf == 0) || (smaxqueue == 0) || (smaxbuf != maxbuf)) {
+ ret = secure_determine_constants();
+ if (ret)
+ return (ret);
+ }
+ ucbuf[nout++] = c;
+ if (nout == smaxqueue) {
+ nout = 0;
+ ret = secure_putbuf(fd, ucbuf, smaxqueue);
+ return (ret ? ret :c);
+ }
+ return (c);
+}
+
+/*
+ * returns:
+ * 0 on success
+ * -1 on error (errno set)
+ * -2 on security error
+ */
+int
+secure_flush(int fd)
+{
+ int ret;
+
+ if (dlevel == PROT_C)
+ return (0);
+ if (nout)
+ if (ret = secure_putbuf(fd, ucbuf, nout))
+ return (ret);
+ return (secure_putbuf(fd, (uchar_t *)"", nout = 0));
+}
+
+/*
+ * returns:
+ * >= 0 on success
+ * -1 on error
+ * -2 on security error
+ */
+int
+secure_putc(int c, FILE *stream)
+{
+ if (dlevel == PROT_C)
+ return (putc(c, stream));
+ return (secure_putbyte(fileno(stream), (uchar_t)c));
+}
+
+/*
+ * returns:
+ * nbyte on success
+ * -1 on error (errno set)
+ * -2 on security error
+ */
+ssize_t
+secure_write(int fd, const void *inbuf, size_t nbyte)
+{
+ uint_t i;
+ int c;
+ uchar_t *buf = (uchar_t *)inbuf;
+
+ if (dlevel == PROT_C)
+ return (write(fd, buf, nbyte));
+ for (i = 0; nbyte > 0; nbyte--)
+ if ((c = secure_putbyte(fd, buf[i++])) < 0)
+ return (c);
+ return (i);
+}
+
+/*
+ * returns:
+ * 0 on success
+ * -1 on error, errno set
+ * -2 on security error
+ */
+static int secure_putbuf(int fd, uchar_t *buf, uint_t nbyte)
+{
+ static char *outbuf; /* output ciphertext */
+ static uint_t bufsize; /* size of outbuf */
+ int length;
+ uint_t net_len;
+
+ /* Other auth types go here ... */
+
+ if (auth_type == AUTHTYPE_GSSAPI) {
+ gss_buffer_desc in_buf, out_buf;
+ OM_uint32 maj_stat, min_stat;
+ int conf_state;
+
+ in_buf.value = buf;
+ in_buf.length = nbyte;
+ maj_stat = gss_seal(&min_stat, gcontext,
+ (dlevel == PROT_P), /* confidential */
+ GSS_C_QOP_DEFAULT,
+ &in_buf, &conf_state,
+ &out_buf);
+ if (maj_stat != GSS_S_COMPLETE) {
+ /*
+ * generally need to deal
+ * ie. should loop, but for now just fail
+ */
+ user_gss_error(maj_stat, min_stat, dlevel == PROT_P?
+ "GSSAPI seal failed" : "GSSAPI sign failed");
+ return (ERR);
+ }
+
+ if (bufsize < out_buf.length) {
+ outbuf = outbuf ?
+ realloc(outbuf, (size_t)out_buf.length) :
+ malloc((size_t)out_buf.length);
+ if (outbuf)
+ bufsize = out_buf.length;
+ else {
+ bufsize = 0;
+ secure_error("%s (in malloc of PROT buffer)",
+ strerror(errno));
+ return (ERR);
+ }
+ }
+
+ memcpy(outbuf, out_buf.value, length = out_buf.length);
+ gss_release_buffer(&min_stat, &out_buf);
+ }
+ net_len = htonl((uint32_t)length);
+ if (looping_write(fd, (char *)&net_len, 4) == -1)
+ return (-1);
+ if (looping_write(fd, outbuf, length) != length)
+ return (-1);
+ return (0);
+}
+
+static
+secure_getbyte(int fd)
+{
+ /* number of chars in ucbuf, pointer into ucbuf */
+ static uint_t nin, bufp;
+ int kerror;
+ uint_t length;
+
+ if (nin == 0) {
+ if ((kerror =
+ looping_read(fd, (char *)&length, sizeof (length)))
+ != sizeof (length)) {
+ secure_error("Couldn't read PROT buffer length: %d/%s",
+ kerror, (kerror == -1) ? strerror(errno) :
+ "premature EOF");
+ return (ERR);
+ }
+ if ((length = ntohl((uint32_t)length)) > maxbuf) {
+ secure_error("Length (%d) of PROT buffer > PBSZ=%u",
+ length, maxbuf);
+ return (ERR);
+ }
+ if ((kerror = looping_read(fd, (char *)ucbuf, length))
+ != length) {
+ secure_error("Couldn't read %u byte PROT buffer: %s",
+ length, kerror == -1 ?
+ strerror(errno) : "premature EOF");
+ return (ERR);
+ }
+ /* Other auth types go here ... */
+
+ if (auth_type == AUTHTYPE_GSSAPI) {
+ gss_buffer_desc xmit_buf, msg_buf;
+ OM_uint32 maj_stat, min_stat;
+ int conf_state;
+
+ xmit_buf.value = ucbuf;
+ xmit_buf.length = length;
+ conf_state = (dlevel == PROT_P);
+ /* decrypt/verify the message */
+ maj_stat = gss_unseal(&min_stat, gcontext, &xmit_buf,
+ &msg_buf, &conf_state, NULL);
+ if (maj_stat != GSS_S_COMPLETE) {
+ user_gss_error(maj_stat, min_stat,
+ (dlevel == PROT_P)?
+ "failed unsealing ENC message":
+ "failed unsealing MIC message");
+ return (ERR);
+ }
+
+ memcpy(ucbuf, msg_buf.value,
+ nin = bufp = msg_buf.length);
+ gss_release_buffer(&min_stat, &msg_buf);
+ }
+ /* Other auth types go here ... */
+ }
+ return ((nin == 0) ? EOF : ucbuf[bufp - nin--]);
+}
+
+/*
+ * returns:
+ * 0 on success
+ * -1 on EOF
+ * -2 on security error
+ */
+int
+secure_getc(FILE *stream)
+{
+ if (dlevel == PROT_C)
+ return (getc(stream));
+ return (secure_getbyte(fileno(stream)));
+}
+
+/*
+ * returns:
+ * > 0 on success (n == # of bytes read)
+ * 0 on EOF
+ * -1 on error, errno set, only for PROT_C
+ * -2 on security error (ERR = -2)
+ */
+ssize_t
+secure_read(int fd, void *inbuf, size_t nbyte)
+{
+ int c, i;
+ char *buf = (char *)inbuf;
+
+ if (dlevel == PROT_C)
+ return (read(fd, buf, nbyte));
+ if (goteof)
+ return (goteof = 0);
+
+ for (i = 0; nbyte > 0; nbyte--)
+ switch (c = secure_getbyte(fd)) {
+ case ERR:
+ return (c);
+ case EOF:
+ goteof = i ? 1 : 0;
+ return (i);
+ default:
+ buf[i++] = c;
+ }
+ return (i);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/inc.flg b/usr/src/cmd/cmd-inet/usr.bin/inc.flg
new file mode 100644
index 0000000000..7c569a6c06
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/inc.flg
@@ -0,0 +1,31 @@
+#!/bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+find_files "s.*" usr/src/uts/common/gssapi/mechs/krb5/include
+find_files "s.*" usr/src/lib/gss_mechs/mech_krb5/include
diff --git a/usr/src/cmd/cmd-inet/usr.bin/nca/Makefile b/usr/src/cmd/cmd-inet/usr.bin/nca/Makefile
new file mode 100644
index 0000000000..75d8f85858
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/nca/Makefile
@@ -0,0 +1,82 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+PROG= ncab2clf
+OBJS= ncab2clf.o
+SRCS= $(OBJS:%.o=%.c)
+
+include ../../../Makefile.cmd
+
+#
+# Message catalog
+#
+POFILE= ncab2clf.po
+#
+
+# these #defines are required to use UNIX 98 interfaces
+_EXTN= -D_REENTRANT
+
+$(OBJS) := CPPFLAGS += $(_EXTN)
+
+LINTFLAGS += $(_EXTN)
+
+INC_PATH += ../../../../uts/common/inet/nca -I.
+CFLAGS += $(CCVERBOSE)
+CPPFLAGS += -I$(INC_PATH)
+
+.KEEP_STATE:
+
+.PARALLEL: $(OBJS)
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+ $(LINK.c) $(OBJS) -o $@
+ $(POST_PROCESS)
+
+#
+# Message catalog
+#
+_msg: $(POFILE)
+
+$(POFILE): $(SRCS)
+ $(RM) ncab2clf.po
+ $(COMPILE.cpp) $(SRCS) > $(POFILE).i
+ $(XGETTEXT) $(XGETFLAGS) $(POFILE).i
+ sed "/^domain/d" messages.po > $@
+ $(RM) messages.po $(POFILE).i
+
+#
+install: all $(ROOTPROG)
+
+clean:
+ $(RM) $(OBJS)
+
+lint: lint_SRCS
+
+include ../../../Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/usr.bin/nca/ncab2clf.c b/usr/src/cmd/cmd-inet/usr.bin/nca/ncab2clf.c
new file mode 100644
index 0000000000..ac508bc12f
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/nca/ncab2clf.c
@@ -0,0 +1,906 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ *
+ * Converts binary log files to CLF (Common Log Format).
+ *
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <strings.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <locale.h>
+#include <errno.h>
+#include <time.h>
+#include <synch.h>
+#include <syslog.h>
+
+#ifndef TRUE
+#define TRUE 1
+#endif /* TRUE */
+
+#ifndef FALSE
+#define FALSE 0
+#endif /* FALSE */
+
+#include "ncadoorhdr.h"
+#include "ncalogd.h"
+
+extern char *gettext();
+
+typedef enum { /* Boolean type */
+ false = 0,
+ true = 1
+} bool;
+
+static const char *const
+g_method_strings[8] = {
+ "UNKNOWN",
+ "OPTIONS",
+ "GET",
+ "HEAD",
+ "POST",
+ "PUT",
+ "DELETE",
+ "TRACE"
+};
+
+/* Short month strings */
+static const char * const sMonthStr [12] = {
+ "Jan",
+ "Feb",
+ "Mar",
+ "Apr",
+ "May",
+ "Jun",
+ "Jul",
+ "Aug",
+ "Sep",
+ "Oct",
+ "Nov",
+ "Dec",
+};
+
+#define SEC_PER_MIN (60)
+#define SEC_PER_HOUR (60*60)
+#define SEC_PER_DAY (24*60*60)
+#define SEC_PER_YEAR (365*24*60*60)
+#define LEAP_TO_70 (70/4)
+
+#define KILO_BYTE (1024)
+#define MEGA_BYTE (KILO_BYTE * KILO_BYTE)
+#define GIGA_BYTE (KILO_BYTE * MEGA_BYTE)
+
+#define CLF_DATE_BUF_LENGTH (128)
+#define OUTFILE_BUF_SIZE (256 * KILO_BYTE)
+
+static bool g_enable_directio = true;
+static ssize_t g_invalid_count = 0;
+static ssize_t g_skip_count = 0;
+static char *g_start_time_str = NULL;
+
+/* init value must match logd & NCA kmod */
+static ssize_t g_n_log_upcall = 0;
+
+/* input binary file was written in 64k chunks by default */
+static ssize_t g_infile_blk_size = NCA_DEFAULT_LOG_BUF_SIZE;
+
+/* num of output records, by default infinite */
+static ssize_t g_out_records = -1;
+
+/* start time for log output, default none (i.e. output all) */
+static struct tm g_start_time;
+
+/*
+ * http_version(version)
+ *
+ * Returns out the string of a given http version
+ */
+
+static char *
+http_version(int http_ver)
+{
+ char *ver_num;
+
+ switch (http_ver) {
+ case HTTP_0_9:
+ case HTTP_0_0:
+ ver_num = "HTTP/0.9";
+ break;
+ case HTTP_ERR:
+ case HTTP_1_0:
+ ver_num = "HTTP/1.0";
+ break;
+ case HTTP_1_1:
+ ver_num = "HTTP/1.1";
+ break;
+ default:
+ ver_num = "HTTP/unknown";
+ }
+
+ return (ver_num);
+}
+
+static bool
+valid_version(int http_ver)
+{
+ switch (http_ver) {
+ case HTTP_0_9:
+ case HTTP_0_0:
+ case HTTP_1_0:
+ case HTTP_1_1:
+ return (true);
+ default:
+ break;
+ }
+
+ return (false);
+}
+
+static bool
+valid_method(int method)
+{
+ switch (method) {
+ case NCA_OPTIONS:
+ case NCA_GET:
+ case NCA_HEAD:
+ case NCA_POST:
+ case NCA_PUT:
+ case NCA_DELETE:
+ case NCA_TRACE:
+ return (true);
+ default:
+ break;
+ }
+
+ return (false);
+}
+
+/*
+ * http_method
+ *
+ * Returns the method string for the given method.
+ */
+
+static char *
+http_method(int method)
+{
+ if (method < sizeof (g_method_strings) / sizeof (g_method_strings[0]))
+ return ((char *)(g_method_strings[method]));
+ else
+ return ((char *)(g_method_strings[0]));
+}
+
+/* sMonth: Return short month string */
+
+static const char *
+sMonth(int index)
+{
+ return (sMonthStr[index]);
+}
+
+/*
+ * Debug formatting routine. Returns a character string representation of the
+ * addr in buf, of the form xxx.xxx.xxx.xxx. This routine takes the address
+ * as a pointer. The "xxx" parts including left zero padding so the final
+ * string will fit easily in tables. It would be nice to take a padding
+ * length argument instead.
+ */
+
+static char *
+ip_dot_saddr(uchar_t *addr, char *buf)
+{
+ (void) sprintf(buf, "%03d.%03d.%03d.%03d",
+ addr[0] & 0xFF, addr[1] & 0xFF, addr[2] & 0xFF, addr[3] & 0xFF);
+ return (buf);
+}
+
+/*
+ * Debug formatting routine. Returns a character string representation of the
+ * addr in buf, of the form xxx.xxx.xxx.xxx. This routine takes the address
+ * in the form of a ipaddr_t and calls ip_dot_saddr with a pointer.
+ */
+
+static char *
+ip_dot_addr(ipaddr_t addr, char *buf)
+{
+ return (ip_dot_saddr((uchar_t *)&addr, buf));
+}
+
+static int
+http_clf_date(char *buf, int bufsize, time_t t)
+{
+ struct tm local_time;
+ long time_zone_info;
+ char sign;
+
+ if (localtime_r(&t, &local_time) == NULL)
+ return (0);
+
+ if (g_start_time.tm_year > 0 &&
+ (local_time.tm_year < g_start_time.tm_year ||
+ (local_time.tm_year == g_start_time.tm_year &&
+ local_time.tm_mon < g_start_time.tm_mon ||
+ (local_time.tm_mon == g_start_time.tm_mon &&
+ local_time.tm_mday < g_start_time.tm_mday ||
+ (local_time.tm_mday == g_start_time.tm_mday &&
+ local_time.tm_hour < g_start_time.tm_hour ||
+ (local_time.tm_hour == g_start_time.tm_hour &&
+ local_time.tm_min < g_start_time.tm_min ||
+ (local_time.tm_min == g_start_time.tm_min &&
+ local_time.tm_sec < g_start_time.tm_sec))))))) {
+ /* clf record before the specified start time */
+ return (1);
+ }
+
+ if (local_time.tm_isdst)
+ time_zone_info = -timezone + SEC_PER_HOUR;
+ else
+ time_zone_info = -timezone;
+
+ if (time_zone_info < 0) {
+ sign = '-';
+ time_zone_info = -time_zone_info;
+ } else {
+ sign = '+';
+ }
+
+ (void) snprintf(buf, bufsize,
+ "[%02d/%s/%04d:%02d:%02d:%02d %c%02ld%02ld]",
+ local_time.tm_mday, sMonth(local_time.tm_mon),
+ 1900 + local_time.tm_year, local_time.tm_hour,
+ local_time.tm_min, local_time.tm_sec,
+ sign, time_zone_info / SEC_PER_HOUR,
+ time_zone_info % SEC_PER_HOUR);
+
+ return (0);
+}
+
+/*
+ * xmalloc(size)
+ * Abort if malloc fails
+ */
+
+static void *
+xmalloc(size_t size)
+{
+ void *p;
+
+ if (! size)
+ size = 1;
+
+ if ((p = malloc(size)) == NULL) {
+ syslog(LOG_ERR, gettext("Error: ncab2clf: Out of memory\n"));
+ abort();
+ }
+
+ return (p);
+}
+
+/*
+ * xstrdup(string)
+ * duplicate string
+ */
+
+static char *
+xstrdup(const char *string)
+{
+ char *new_string;
+
+ if (string) {
+ new_string = xmalloc(strlen(string) + 1);
+ (void) strcpy(new_string, string);
+
+ return (new_string);
+ }
+
+ return (NULL);
+}
+
+static void
+usage()
+{
+ (void) fprintf(stderr, gettext(
+ "\nncab2clf [-Dhv] [-b <block-size>] [-i <binary-log-file>] "
+ "[-n <n>]\n"
+ " [-o <output-file>] [-s <date/time>]\n"
+ "\tconverts a NCA binary log file to HTTP CLF"
+ " (Common Log Format)\n\n"
+ "\t-b <block-size>\n"
+ "\t\tinput file blocking size in KB\n"
+ "\t\t- default is 64K bytes\n"
+ "\t-D\tdisable directio on <output-file-name>\n"
+ "\t-h\tthis usage message\n"
+ "\t-i <binary-log-file>\n"
+ "\t\tspecify input file\n"
+ "\t-n <n>\n"
+ "\t\toutput <n> CLF records\n"
+ "\t-o <output-file>\n"
+ "\t\tspecify output file\n"
+ "\t-s <date/time>\n"
+ "\t\tskip any records before <date/time>\n"
+ "\t\t- <date/time> may be in CLF format\n"
+ "\t\t- <date/time> may be in time format as specified "
+ "by touch(1)\n"
+ "\t-v\tverbose output\n"
+ "\tNote: if no <output-file> - output goes to standard output\n"
+ "\tNote: if no <binary-log-file> - input is taken from standard "
+ "input\n"));
+
+ exit(3);
+}
+
+/*
+ * atoi_for2(p, value)
+ * - stores the numerical value of the two digit string p into value
+ * - return TRUE upon success and FALSE upon failure
+ */
+
+static int
+atoi_for2(char *p, int *value) {
+
+ *value = (*p - '0') * 10 + *(p+1) - '0';
+ if ((*value < 0) || (*value > 99))
+ return (FALSE);
+ return (TRUE);
+}
+
+/*
+ * parse_time(t, tm)
+ * - parses the string t to retrieve the UNIX time format as specified by
+ * touch(1).
+ * - return TRUE upon success and FALSE upon failure
+ */
+
+static int
+parse_time(char *t, struct tm *tm)
+{
+ int century = 0;
+ int seconds = 0;
+ time_t when;
+ char *p;
+
+ /*
+ * time in the following format (defined by the touch(1) spec):
+ * [[CC]YY]MMDDhhmm[.SS]
+ */
+ if ((p = strchr(t, '.')) != NULL) {
+ if (strchr(p+1, '.') != NULL)
+ return (FALSE);
+ if (!atoi_for2(p+1, &seconds))
+ return (FALSE);
+ *p = '\0';
+ }
+
+ when = time(0);
+ bzero(tm, sizeof (struct tm));
+ tm->tm_year = localtime(&when)->tm_year;
+
+ switch (strlen(t)) {
+ case 12: /* CCYYMMDDhhmm */
+ if (!atoi_for2(t, &century))
+ return (FALSE);
+ t += 2;
+ /* FALLTHROUGH */
+ case 10: /* YYMMDDhhmm */
+ if (!atoi_for2(t, &tm->tm_year))
+ return (FALSE);
+ t += 2;
+ if (century == 0) {
+ if (tm->tm_year < 69)
+ tm->tm_year += 100;
+ } else
+ tm->tm_year += (century - 19) * 100;
+ /* FALLTHROUGH */
+ case 8: /* MMDDhhmm */
+ if (!atoi_for2(t, &tm->tm_mon))
+ return (FALSE);
+ tm->tm_mon--;
+ t += 2;
+
+ if (!atoi_for2(t, &tm->tm_mday))
+ return (FALSE);
+ t += 2;
+
+ if (!atoi_for2(t, &tm->tm_hour))
+ return (FALSE);
+ t += 2;
+
+ if (!atoi_for2(t, &tm->tm_min))
+ return (FALSE);
+
+ tm->tm_sec = seconds;
+ break;
+ default:
+ return (FALSE);
+ }
+
+ return (TRUE);
+}
+
+static void
+close_files(int ifd, int ofd)
+{
+ if (ifd != STDIN_FILENO)
+ (void) close(ifd);
+
+ if (ofd != STDOUT_FILENO)
+ (void) close(ofd);
+}
+
+/*
+ * Read the requested number of bytes from the given file descriptor
+ */
+
+static ssize_t
+read_n_bytes(int fd, char *buf, ssize_t bufsize)
+{
+ ssize_t num_to_read = bufsize;
+ ssize_t num_already_read = 0;
+ ssize_t i;
+
+ while (num_to_read > 0) {
+
+ i = read(fd, &(buf[num_already_read]), num_to_read);
+ if (i < 0) {
+ if (errno == EINTR)
+ continue;
+ else
+ (void) fprintf(stderr, gettext(
+ "Error: ncab2clf: "
+ "reading input file: %s\n"),
+ strerror(errno));
+ return (-1); /* some wierd interrupt */
+ }
+
+ if (i == 0)
+ break;
+
+ num_already_read += i;
+ num_to_read -= i;
+ }
+
+ return (num_already_read);
+}
+
+/*
+ * Write the requested number of bytes to the given file descriptor
+ */
+
+static ssize_t
+write_n_bytes(int fd, char *buf, ssize_t bufsize)
+{
+ ssize_t num_to_write = bufsize;
+ ssize_t num_written = 0;
+ ssize_t i;
+
+ while (num_to_write > 0) {
+
+ i = write(fd, &(buf[num_written]), num_to_write);
+ if (i < 0) {
+ if (errno == EINTR)
+ continue;
+ else
+ (void) fprintf(stderr, gettext(
+ "Error: ncab2clf: "
+ "writing output file: %s\n"),
+ strerror(errno));
+ return (-1); /* some wierd interrupt */
+ }
+
+ num_written += i;
+ num_to_write -= i;
+ }
+
+ return (num_written);
+}
+
+/* do constraint checks and determine if it's a valid header */
+
+static bool
+is_valid_header(void *ibuf)
+{
+ nca_log_buf_hdr_t *h;
+ nca_log_stat_t *s;
+
+ h = (nca_log_buf_hdr_t *)ibuf;
+
+ /* Do some validity checks on ibuf */
+
+ if (((h->nca_loghdr).nca_version != NCA_LOG_VERSION1) ||
+ ((h->nca_loghdr).nca_op != log_op)) {
+ return (false);
+ }
+
+ s = &(h->nca_logstats);
+
+ if (g_n_log_upcall == 0) {
+ g_n_log_upcall = s->n_log_upcall;
+ } else {
+ if ((++g_n_log_upcall) != (ssize_t)s->n_log_upcall) {
+ (void) fprintf(stderr, gettext(
+ "Warning: ncab2clf:"
+ " expected record number (%d) is"
+ " different from the one seen (%d)\n."
+ " Resetting the expected record"
+ " number.\n"), g_n_log_upcall, s->n_log_upcall);
+
+ g_n_log_upcall = s->n_log_upcall;
+ }
+ }
+
+ return (true);
+}
+
+/* convert input binary buffer into CLF */
+
+static int
+b2clf_buf(
+ void *ibuf,
+ char *obuf,
+ ssize_t isize,
+ ssize_t osize,
+ ssize_t *out_size)
+{
+ nca_log_buf_hdr_t *h;
+ nca_log_stat_t *s;
+ nca_request_log_t *r;
+
+ char *br;
+ void *er;
+ char ip_buf[64];
+ ssize_t max_input_size, num_bytes_read;
+ int n_recs;
+ bool error_seen;
+
+ ssize_t count;
+ char clf_timebuf[CLF_DATE_BUF_LENGTH];
+ char *method;
+ char *http_version_string;
+ char *ruser;
+ char *req_url;
+ char *remote_ip;
+
+ h = (nca_log_buf_hdr_t *)ibuf;
+ s = &(h->nca_logstats);
+ r = (nca_request_log_t *)(&(h[1]));
+
+ /* OK, it's a valid buffer which we can use, go ahead and convert it */
+
+ max_input_size = (ssize_t)isize - sizeof (nca_log_buf_hdr_t);
+
+ *out_size = 0;
+ error_seen = false;
+ num_bytes_read = 0;
+ for (n_recs = 0; n_recs < s->n_log_recs; n_recs++) {
+
+ /* Make sure there is enough space in the output buffer */
+
+ if ((*out_size >= osize) ||
+ (num_bytes_read >= max_input_size)) {
+ error_seen = true;
+ break;
+ }
+
+ if (http_clf_date(clf_timebuf, sizeof (clf_timebuf),
+ ((time_t)r->start_process_time))) {
+ /* A start time was speced and we're not there yet */
+ ++g_skip_count;
+ goto skip;
+ }
+
+ /* Only logs valid HTTP ops */
+
+ if ((! valid_method((int)r->method)) ||
+ (! valid_version((int)r->version))) {
+ ++g_invalid_count;
+ goto skip;
+ }
+
+ method = http_method((int)r->method);
+ http_version_string = http_version((int)r->version);
+
+ remote_ip = ip_dot_addr(r->remote_host, (char *)&ip_buf);
+ if (r->remote_user_len) {
+ ruser = NCA_REQLOG_RDATA(r, remote_user);
+ } else {
+ ruser = "-";
+ }
+
+ if (r->request_url_len) {
+ req_url = NCA_REQLOG_RDATA(r, request_url);
+ } else {
+ req_url = "UNKNOWN";
+ }
+
+ count = (ssize_t)snprintf(&(obuf[*out_size]), osize - *out_size,
+ "%s %s %s %s \"%s %s %s\" %d %d\n",
+ ((remote_ip) ? remote_ip : "-"),
+ /* should be remote_log_name */
+ "-",
+ ruser,
+ clf_timebuf,
+ method,
+ req_url,
+ http_version_string,
+ r->response_status,
+ r->response_len);
+
+ *out_size += count;
+ skip:
+ br = (char *)r;
+ er = ((char *)r) + NCA_LOG_REC_SIZE(r);
+
+ /*LINTED*/
+ r = (nca_request_log_t *)NCA_LOG_ALIGN(er);
+ num_bytes_read += (ssize_t)(((char *)r) - br);
+ if (g_out_records > 0 && --g_out_records == 0)
+ break;
+ }
+
+ if (error_seen) {
+ (void) fprintf(stderr, gettext(
+ "Error: ncab2clf: "
+ "Input buffer not fully converted.\n"));
+
+ if (n_recs != s->n_log_recs)
+ (void) fprintf(stderr, gettext(
+ "Warning: ncab2clf: "
+ "Converted only %d of %d records\n"),
+ n_recs, s->n_log_recs);
+ }
+
+ return (0);
+}
+
+static int
+b2clf(int ifd, int ofd)
+{
+ char *ibuf;
+ char *obuf;
+ bool error_seen;
+ bool eof_seen;
+ ssize_t num_iterations, ni, nh, no, olen;
+
+ nca_log_buf_hdr_t *h;
+ nca_log_stat_t *s;
+
+ ibuf = xmalloc(g_infile_blk_size);
+ obuf = xmalloc(OUTFILE_BUF_SIZE);
+ error_seen = false;
+
+ eof_seen = false;
+ num_iterations = 0;
+ while (! eof_seen && g_out_records != 0) {
+ ++num_iterations;
+
+ nh = ni = no = 0;
+
+ /* read the binary header first */
+ nh = read_n_bytes(ifd, ibuf, sizeof (nca_log_buf_hdr_t));
+ if (nh != sizeof (nca_log_buf_hdr_t)) {
+ eof_seen = true;
+ break;
+ }
+
+ if (! is_valid_header(ibuf)) {
+ (void) fprintf(stderr, gettext(
+ "Error: ncab2clf: "
+ "Can't convert the input data to CLF\n"));
+ continue;
+ }
+
+ /* read the data to be converted */
+ /* LINTED */
+ h = (nca_log_buf_hdr_t *)ibuf;
+ s = &(h->nca_logstats);
+
+ if (s->n_log_size == 0)
+ continue;
+
+ ni = read_n_bytes(ifd, &(ibuf[nh]), (ssize_t)s->n_log_size);
+ if (ni < 0) {
+ error_seen = true;
+ break;
+ } else if (ni < (ssize_t)s->n_log_size) {
+ eof_seen = true;
+ }
+
+ if (ni == 0)
+ break;
+
+ /* convert binary input into text output */
+
+ if (b2clf_buf(ibuf, obuf, ni + nh, OUTFILE_BUF_SIZE, &olen)) {
+ (void) fprintf(stderr, gettext(
+ "Error: ncab2clf: "
+ "Can't convert the input data to CLF\n"));
+ error_seen = true;
+ break;
+ }
+
+ /* write out the text data */
+ no = write_n_bytes(ofd, obuf, olen);
+ if (no != olen) {
+ error_seen = true;
+ break;
+ }
+
+ bzero(ibuf, nh + ni);
+ bzero(obuf, no);
+ }
+
+ free(ibuf);
+ free(obuf);
+
+ if (error_seen)
+ return (-1);
+
+ return (0);
+}
+
+
+int
+main(int argc, char **argv)
+{
+ int c;
+ int ifd; /* input fd - binary log file */
+ int ofd;
+ struct tm t;
+
+ char *infile = NULL; /* input file name */
+ char *outfile = NULL; /* output file name */
+
+ char monstr[64];
+
+ (void) setlocale(LC_ALL, "");
+
+#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
+#define TEXT_DOMAIN "SYS_TEST"
+#endif
+
+ (void) textdomain(TEXT_DOMAIN);
+
+ /* parse any arguments */
+ while ((c = getopt(argc, argv, "hvDi:o:b:n:s:")) != EOF) {
+ switch (c) {
+ case 'h':
+ usage();
+ break;
+ case 'i':
+ infile = xstrdup(optarg);
+ break;
+ case 'D':
+ g_enable_directio = false;
+ break;
+ case 'o':
+ outfile = xstrdup(optarg);
+ break;
+ case 'b':
+ g_infile_blk_size = (KILO_BYTE * atoi(optarg));
+ break;
+ case 'n':
+ g_out_records = atoi(optarg);
+ break;
+ case 's':
+ g_start_time_str = strdup(optarg);
+ bzero(&t, sizeof (t));
+ if (sscanf(optarg, "%d/%3s/%d:%d:%d:%d", &t.tm_mday,
+ &monstr[0], &t.tm_year, &t.tm_hour, &t.tm_min,
+ &t.tm_sec) == 6) {
+ /* Valid CLF time (e.g. 06/Apr/2001:09:14:14) */
+ t.tm_mon = 0;
+ do {
+ if (strcasecmp(monstr,
+ sMonthStr[t.tm_mon]) == 0)
+ break;
+ } while (t.tm_mon++ < 12);
+ t.tm_year -= 1900;
+ g_start_time = t;
+ } else if (parse_time(optarg, &t)) {
+ g_start_time = t;
+ } else {
+ (void) fprintf(stderr,
+ gettext("Error: ncab2clf:"
+ " %s: unrecognized date/time.\n"),
+ optarg);
+ }
+ break;
+ case 'v':
+ (void) fprintf(stderr, gettext("Error: ncab2clf: "
+ "verbose functionality not yet supported\n"));
+ exit(3);
+ break;
+ case '?':
+ usage();
+ break;
+ }
+ }
+
+ /* set up the input stream */
+
+ if (infile) {
+
+ if ((ifd = open(infile, O_RDONLY)) < 0) {
+ (void) fprintf(stderr,
+ gettext("Error: ncab2clf: "
+ "Failure to open binary log file %s: %s\n"),
+ infile, strerror(errno));
+ exit(1);
+ }
+
+ } else {
+ ifd = STDIN_FILENO;
+ }
+
+ /* set up the output stream */
+
+ if (outfile) {
+
+ if ((ofd = open(outfile, O_WRONLY|O_CREAT, 0644)) < 0) {
+ (void) fprintf(stderr, gettext(
+ "Error: ncab2clf: "
+ "Failure to open output file %s: %s\n"),
+ outfile, strerror(errno));
+ exit(1);
+ }
+
+ /* Enable directio on output stream if specified */
+
+ if (g_enable_directio)
+ (void) directio(ofd, DIRECTIO_ON);
+
+ } else {
+ ofd = STDOUT_FILENO;
+ }
+
+ if ((b2clf(ifd, ofd) != 0)) {
+ close_files(ifd, ofd);
+ exit(2);
+ }
+
+ close_files(ifd, ofd);
+
+ if (g_invalid_count) {
+ (void) fprintf(stderr, gettext("Warning: ncab2clf: %d"
+ " number of invalid log records encountered in binary input"
+ " file were skipped\n"), g_invalid_count);
+ }
+ if (g_skip_count) {
+ (void) fprintf(stderr, gettext("Warning: ncab2clf:"
+ " %d log records in binary input file before %s"
+ " were skipped\n"),
+ g_skip_count, g_start_time_str);
+ }
+
+ return (0);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/netstat/Makefile b/usr/src/cmd/cmd-inet/usr.bin/netstat/Makefile
new file mode 100644
index 0000000000..e4fdbd84da
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/netstat/Makefile
@@ -0,0 +1,67 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 1996-2003 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# Copyright (c) 1990 Mentat Inc.
+#
+# cmd/cmd-inet/usr.bin/netstat/Makefile
+
+PROG= netstat
+
+LOCALOBJS= netstat.o unix.o
+COMMONOBJS= compat.o
+OBJS= $(LOCALOBJS) $(COMMONOBJS)
+
+include ../../../Makefile.cmd
+include ../../Makefile.cmd-inet
+
+LOCALSRCS= $(LOCALOBJS:%.o=%.c)
+COMMONSRCS= $(CMDINETCOMMONDIR)/$(COMMONOBJS:%.o=%.c)
+SRCS= $(LOCALSRCS) $(COMMONSRCS)
+
+CPPFLAGS += -DNDEBUG -I$(CMDINETCOMMONDIR)
+LDLIBS += -ldhcpagent -lcmd -lsocket -lnsl -lkstat
+LINTFLAGS += -m
+
+.KEEP_STATE:
+
+all: $(PROG) $(NPROG)
+
+ROOTPROG= $(PROG:%=$(ROOTBIN)/%)
+
+$(PROG): $(OBJS)
+ $(LINK.c) $(OBJS) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+install: all $(ROOTPROG)
+
+clean:
+ $(RM) $(OBJS)
+
+lint: lint_SRCS
+
+include ../../../Makefile.targ
+
diff --git a/usr/src/cmd/cmd-inet/usr.bin/netstat/inc.flg b/usr/src/cmd/cmd-inet/usr.bin/netstat/inc.flg
new file mode 100644
index 0000000000..7d1fdef815
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/netstat/inc.flg
@@ -0,0 +1,31 @@
+#!/bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+find_files "s.*" usr/src/common/net/dhcp
+find_files "s.*" usr/src/cmd/cmd-inet/common
diff --git a/usr/src/cmd/cmd-inet/usr.bin/netstat/netstat.c b/usr/src/cmd/cmd-inet/usr.bin/netstat/netstat.c
new file mode 100644
index 0000000000..f978ae489e
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/netstat/netstat.c
@@ -0,0 +1,5950 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 1990 Mentat Inc.
+ * netstat.c 2.2, last change 9/9/91
+ * MROUTING Revision 3.5
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * simple netstat based on snmp/mib-2 interface to the TCP/IP stack
+ *
+ * NOTES:
+ * 1. A comment "LINTED: (note 1)" appears before certain lines where
+ * lint would have complained, "pointer cast may result in improper
+ * alignment". These are lines where lint had suspected potential
+ * improper alignment of a data structure; in each such situation
+ * we have relied on the kernel guaranteeing proper alignment.
+ * 2. Some 'for' loops have been commented as "'for' loop 1", etc
+ * because they have 'continue' or 'break' statements in their
+ * bodies. 'continue' statements have been used inside some loops
+ * where avoiding them would have led to deep levels of indentation.
+ *
+ * TODO:
+ * Add ability to request subsets from kernel (with level = MIB2_IP;
+ * name = 0 meaning everything for compatibility)
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <strings.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <kstat.h>
+#include <assert.h>
+
+#include <sys/types.h>
+#include <sys/stream.h>
+#include <stropts.h>
+#include <sys/strstat.h>
+#include <sys/sysmacros.h>
+#include <sys/tihdr.h>
+
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <netinet/in.h>
+#include <net/if.h>
+#include <net/route.h>
+
+#include <inet/common.h>
+#include <inet/mib2.h>
+#include <inet/ip.h>
+#include <inet/arp.h>
+#include <inet/tcp.h>
+#include <netinet/igmp_var.h>
+#include <netinet/ip_mroute.h>
+
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <sys/systeminfo.h>
+#include <arpa/inet.h>
+
+#include <netinet/dhcp.h>
+#include <dhcpagent_ipc.h>
+#include <dhcpagent_util.h>
+#include <compat.h>
+
+extern void unixpr(kstat_ctl_t *kc);
+extern void setifdhcp(const char *caller, const char *ifname,
+ int argc, char *argv[]);
+
+#define STR_EXPAND 4
+
+#define V4MASK_TO_V6(v4, v6) ((v6)._S6_un._S6_u32[0] = 0xfffffffful, \
+ (v6)._S6_un._S6_u32[1] = 0xfffffffful, \
+ (v6)._S6_un._S6_u32[2] = 0xfffffffful, \
+ (v6)._S6_un._S6_u32[3] = (v4))
+
+#define IN6_IS_V4MASK(v6) ((v6)._S6_un._S6_u32[0] == 0xfffffffful && \
+ (v6)._S6_un._S6_u32[1] == 0xfffffffful && \
+ (v6)._S6_un._S6_u32[2] == 0xfffffffful)
+
+typedef struct mib_item_s {
+ struct mib_item_s *next_item;
+ int group;
+ int mib_id;
+ int length;
+ void *valp;
+} mib_item_t;
+
+struct ifstat {
+ uint64_t ipackets;
+ uint64_t ierrors;
+ uint64_t opackets;
+ uint64_t oerrors;
+ uint64_t collisions;
+};
+
+struct iflist {
+ struct iflist *next_if;
+ char ifname[LIFNAMSIZ];
+ struct ifstat tot;
+};
+
+static mib_item_t *mibget(int sd);
+static void mibfree(mib_item_t *firstitem);
+static int mibopen(void);
+static void mib_get_constants(mib_item_t *item);
+static mib_item_t *mib_item_dup(mib_item_t *item);
+static mib_item_t *mib_item_diff(mib_item_t *item1,
+ mib_item_t *item2);
+static void mib_item_destroy(mib_item_t **item);
+
+static boolean_t octetstrmatch(const Octet_t *a, const Octet_t *b);
+static char *octetstr(Octet_t *op, int code,
+ char *dst, uint_t dstlen);
+static char *pr_addr(uint_t addr,
+ char *dst, uint_t dstlen);
+static char *pr_addrnz(ipaddr_t addr, char *dst, uint_t dstlen);
+static char *pr_addr6(const in6_addr_t *addr,
+ char *dst, uint_t dstlen);
+static char *pr_mask(uint_t addr,
+ char *dst, uint_t dstlen);
+static char *pr_prefix6(struct in6_addr *addr, uint_t prefixlen,
+ char *dst, uint_t dstlen);
+static char *pr_ap(uint_t addr, uint_t port,
+ char *proto, char *dst, uint_t dstlen);
+static char *pr_ap6(const in6_addr_t *addr, uint_t port,
+ char *proto, char *dst, uint_t dstlen);
+static char *pr_net(uint_t addr, uint_t mask,
+ char *dst, uint_t dstlen);
+static char *pr_netaddr(uint_t addr, uint_t mask,
+ char *dst, uint_t dstlen);
+static char *pr_netclassless(ipaddr_t addr, ipaddr_t mask,
+ char *dst, size_t dstlen);
+static char *fmodestr(uint_t fmode);
+static char *portname(uint_t port, char *proto,
+ char *dst, uint_t dstlen);
+
+static char *mitcp_state(int code);
+
+static void stat_report(mib_item_t *item);
+static void mrt_stat_report(mib_item_t *item);
+static void arp_report(mib_item_t *item);
+static void ndp_report(mib_item_t *item);
+static void mrt_report(mib_item_t *item);
+static void if_stat_total(struct ifstat *oldstats,
+ struct ifstat *newstats, struct ifstat *sumstats);
+static void if_report(mib_item_t *item, char *ifname,
+ int Iflag_only, boolean_t once_only);
+static void if_report_ip4(mib2_ipAddrEntry_t *ap,
+ char ifname[], char logintname[],
+ struct ifstat *statptr, boolean_t ksp_not_null);
+static void if_report_ip6(mib2_ipv6AddrEntry_t *ap6,
+ char ifname[], char logintname[],
+ struct ifstat *statptr, boolean_t ksp_not_null);
+static void ire_report(mib_item_t *item);
+static void tcp_report(mib_item_t *item);
+static void udp_report(mib_item_t *item);
+static void group_report(mib_item_t *item);
+static void print_ip_stats(mib2_ip_t *ip);
+static void print_icmp_stats(mib2_icmp_t *icmp);
+static void print_ip6_stats(mib2_ipv6IfStatsEntry_t *ip6);
+static void print_icmp6_stats(mib2_ipv6IfIcmpEntry_t *icmp6);
+static void print_sctp_stats(mib2_sctp_t *tcp);
+static void print_tcp_stats(mib2_tcp_t *tcp);
+static void print_udp_stats(mib2_udp_t *udp);
+static void print_rawip_stats(mib2_rawip_t *rawip);
+static void print_igmp_stats(struct igmpstat *igps);
+static void print_mrt_stats(struct mrtstat *mrts);
+static void sctp_report(mib_item_t *item);
+static void sum_ip6_stats(mib2_ipv6IfStatsEntry_t *ip6,
+ mib2_ipv6IfStatsEntry_t *sum6);
+static void sum_icmp6_stats(mib2_ipv6IfIcmpEntry_t *icmp6,
+ mib2_ipv6IfIcmpEntry_t *sum6);
+static void m_report(void);
+static void dhcp_report(char *);
+
+ void fail(int, char *, ...);
+static uint64_t kstat_named_value(kstat_t *, char *);
+static kid_t safe_kstat_read(kstat_ctl_t *, kstat_t *, void *);
+static int isnum(char *);
+static char *plural(int n);
+static char *pluraly(int n);
+static char *plurales(int n);
+static void process_filter(char *arg);
+static boolean_t family_selected(int family);
+
+static void usage(char *);
+static void fatal(int errcode, char *str1, ...);
+
+#define PLURAL(n) plural((int)n)
+#define PLURALY(n) pluraly((int)n)
+#define PLURALES(n) plurales((int)n)
+#define IFLAGMOD(flg, val1, val2) if (flg == val1) flg = val2
+#define MDIFF(diff, elem2, elem1, member) (diff)->member = \
+ (elem2)->member - (elem1)->member
+
+
+static boolean_t Aflag = B_FALSE; /* All sockets/ifs/rtng-tbls */
+static boolean_t Dflag = B_FALSE; /* Debug Info */
+static boolean_t Iflag = B_FALSE; /* IP Traffic Interfaces */
+static boolean_t Mflag = B_FALSE; /* STREAMS Memory Statistics */
+static boolean_t Nflag = B_FALSE; /* Numeric Network Addresses */
+static boolean_t Rflag = B_FALSE; /* Routing Tables */
+static boolean_t Sflag = B_FALSE; /* Per-protocol Statistics */
+static boolean_t Vflag = B_FALSE; /* Verbose */
+static boolean_t Pflag = B_FALSE; /* Net to Media Tables */
+static boolean_t Gflag = B_FALSE; /* Multicast group membership */
+static boolean_t MMflag = B_FALSE; /* Multicast routing table */
+static boolean_t DHCPflag = B_FALSE; /* DHCP statistics */
+
+static int v4compat = 0; /* Compatible printing format for status */
+
+static int proto = IPPROTO_MAX; /* all protocols */
+kstat_ctl_t *kc = NULL;
+
+/*
+ * Sizes of data structures extracted from the base mib.
+ * This allows the size of the tables entries to grow while preserving
+ * binary compatibility.
+ */
+static int ipAddrEntrySize;
+static int ipRouteEntrySize;
+static int ipNetToMediaEntrySize;
+static int ipMemberEntrySize;
+static int ipGroupSourceEntrySize;
+static int vifctlSize;
+static int mfcctlSize;
+
+static int ipv6IfStatsEntrySize;
+static int ipv6IfIcmpEntrySize;
+static int ipv6AddrEntrySize;
+static int ipv6RouteEntrySize;
+static int ipv6NetToMediaEntrySize;
+static int ipv6MemberEntrySize;
+static int ipv6GroupSourceEntrySize;
+
+static int tcpConnEntrySize;
+static int tcp6ConnEntrySize;
+static int udpEntrySize;
+static int udp6EntrySize;
+static int sctpEntrySize;
+static int sctpLocalEntrySize;
+static int sctpRemoteEntrySize;
+
+#define protocol_selected(p) (proto == IPPROTO_MAX || proto == (p))
+
+/* Machinery used for -f (filter) option */
+#define FK_AF 0
+#define FK_INIF 1
+#define FK_OUTIF 2
+#define FK_SRC 3
+#define FK_DST 4
+#define FK_FLAGS 5
+#define NFILTERKEYS 6
+
+static const char *filter_keys[NFILTERKEYS] = {
+ "af", "inif", "outif", "src", "dst", "flags"
+};
+
+/* Flags on routes */
+#define FLF_A 0x00000001
+#define FLF_B 0x00000002
+#define FLF_D 0x00000004
+#define FLF_G 0x00000008
+#define FLF_H 0x00000010
+#define FLF_L 0x00000020
+#define FLF_U 0x00000040
+#define FLF_M 0x00000080
+#define FLF_S 0x00000100
+static const char flag_list[] = "ABDGHLUMS";
+
+typedef struct filter_rule filter_t;
+
+struct filter_rule {
+ filter_t *f_next;
+ union {
+ int f_family;
+ const char *f_ifname;
+ struct {
+ struct hostent *f_address;
+ in6_addr_t f_mask;
+ } a;
+ struct {
+ uint_t f_flagset;
+ uint_t f_flagclear;
+ } f;
+ } u;
+};
+
+/*
+ * The user-specified filters are linked into lists separated by
+ * keyword (type of filter). Thus, the matching algorithm is:
+ * For each non-empty filter list
+ * If no filters in the list match
+ * then stop here; route doesn't match
+ * If loop above completes, then route does match and will be
+ * displayed.
+ */
+static filter_t *filters[NFILTERKEYS];
+
+int
+main(int argc, char **argv)
+{
+ char *name;
+ mib_item_t *item = NULL;
+ mib_item_t *previtem = NULL;
+ int sd = -1;
+ char *ifname = NULL;
+ int interval = 0; /* Single time by default */
+ int count = -1; /* Forever */
+ int c;
+ int d;
+ /*
+ * Possible values of 'Iflag_only':
+ * -1, no feature-flags;
+ * 0, IFlag and other feature-flags enabled
+ * 1, IFlag is the only feature-flag enabled
+ * : trinary variable, modified using IFLAGMOD()
+ */
+ int Iflag_only = -1;
+ boolean_t once_only = B_FALSE; /* '-i' with count > 1 */
+ extern char *optarg;
+ extern int optind;
+ char *default_ip_str = NULL;
+
+ name = argv[0];
+
+ v4compat = get_compat_flag(&default_ip_str);
+ if (v4compat == DEFAULT_PROT_BAD_VALUE)
+ fatal(2, "%s: %s: Bad value for %s in %s\n", name,
+ default_ip_str, DEFAULT_IP, INET_DEFAULT_FILE);
+ free(default_ip_str);
+
+ while ((c = getopt(argc, argv, "adimnrspMgvf:P:I:D")) != -1) {
+ switch ((char)c) {
+ case 'a': /* all connections */
+ Aflag = B_TRUE;
+ break;
+
+ case 'd': /* turn on debugging */
+ Dflag = B_TRUE;
+ break;
+
+ case 'i': /* interface (ill/ipif report) */
+ Iflag = B_TRUE;
+ IFLAGMOD(Iflag_only, -1, 1); /* '-i' exists */
+ break;
+
+ case 'm': /* streams msg report */
+ Mflag = B_TRUE;
+ IFLAGMOD(Iflag_only, 1, 0); /* see macro def'n */
+ break;
+
+ case 'n': /* numeric format */
+ Nflag = B_TRUE;
+ break;
+
+ case 'r': /* route tables */
+ Rflag = B_TRUE;
+ IFLAGMOD(Iflag_only, 1, 0); /* see macro def'n */
+ break;
+
+ case 's': /* per-protocol statistics */
+ Sflag = B_TRUE;
+ IFLAGMOD(Iflag_only, 1, 0); /* see macro def'n */
+ break;
+
+ case 'p': /* arp/ndp table */
+ Pflag = B_TRUE;
+ IFLAGMOD(Iflag_only, 1, 0); /* see macro def'n */
+ break;
+
+ case 'M': /* multicast routing tables */
+ MMflag = B_TRUE;
+ IFLAGMOD(Iflag_only, 1, 0); /* see macro def'n */
+ break;
+
+ case 'g': /* multicast group membership */
+ Gflag = B_TRUE;
+ IFLAGMOD(Iflag_only, 1, 0); /* see macro def'n */
+ break;
+
+ case 'v': /* verbose output format */
+ Vflag = B_TRUE;
+ IFLAGMOD(Iflag_only, 1, 0); /* see macro def'n */
+ break;
+
+ case 'f':
+ process_filter(optarg);
+ break;
+
+ case 'P':
+ if (strcmp(optarg, "ip") == 0) {
+ proto = IPPROTO_IP;
+ } else if (strcmp(optarg, "ipv6") == 0 ||
+ strcmp(optarg, "ip6") == 0) {
+ v4compat = 0; /* Overridden */
+ proto = IPPROTO_IPV6;
+ } else if (strcmp(optarg, "icmp") == 0) {
+ proto = IPPROTO_ICMP;
+ } else if (strcmp(optarg, "icmpv6") == 0 ||
+ strcmp(optarg, "icmp6") == 0) {
+ v4compat = 0; /* Overridden */
+ proto = IPPROTO_ICMPV6;
+ } else if (strcmp(optarg, "igmp") == 0) {
+ proto = IPPROTO_IGMP;
+ } else if (strcmp(optarg, "udp") == 0) {
+ proto = IPPROTO_UDP;
+ } else if (strcmp(optarg, "tcp") == 0) {
+ proto = IPPROTO_TCP;
+ } else if (strcmp(optarg, "sctp") == 0) {
+ proto = IPPROTO_SCTP;
+ } else if (strcmp(optarg, "raw") == 0 ||
+ strcmp(optarg, "rawip") == 0) {
+ proto = IPPROTO_RAW;
+ } else {
+ fatal(1, "%s: unknown protocol.\n", optarg);
+ }
+ break;
+
+ case 'I':
+ ifname = optarg;
+ Iflag = B_TRUE;
+ IFLAGMOD(Iflag_only, -1, 1); /* see macro def'n */
+ break;
+
+ case 'D':
+ DHCPflag = B_TRUE;
+ Iflag_only = 0;
+ break;
+
+ case '?':
+ default:
+ usage(name);
+ }
+ }
+
+ /*
+ * Handle other arguments: find interval, count; the
+ * flags that accept 'interval' and 'count' are OR'd
+ * in the outermost 'if'; more flags may be added as
+ * required
+ */
+ if (Iflag || Sflag || Mflag) {
+ for (d = optind; d < argc; d++) {
+ if (isnum(argv[d])) {
+ interval = atoi(argv[d]);
+ if (d + 1 < argc &&
+ isnum(argv[d + 1])) {
+ count = atoi(argv[d + 1]);
+ optind++;
+ }
+ optind++;
+ if (interval == 0 || count == 0)
+ usage(name);
+ break;
+ }
+ }
+ }
+ if (optind < argc) {
+ if (Iflag && isnum(argv[optind])) {
+ count = atoi(argv[optind]);
+ if (count == 0)
+ usage(name);
+ optind++;
+ }
+ }
+ if (optind < argc) {
+ (void) fprintf(stderr,
+ "%s: extra arguments\n", name);
+ usage(name);
+ }
+ if (interval)
+ setbuf(stdout, NULL);
+
+ if (DHCPflag) {
+ dhcp_report(Iflag ? ifname : NULL);
+ exit(0);
+ }
+
+ /* Get data structures: priming before iteration */
+ if (family_selected(AF_INET) || family_selected(AF_INET6)) {
+ sd = mibopen();
+ if (sd == -1)
+ fatal(1, "can't open mib stream\n");
+ if ((item = mibget(sd)) == NULL) {
+ (void) close(sd);
+ fatal(1, "mibget() failed\n");
+ }
+ /* Extract constant sizes - need do once only */
+ mib_get_constants(item);
+ }
+ if ((kc = kstat_open()) == NULL) {
+ mibfree(item);
+ (void) close(sd);
+ fail(1, "kstat_open(): can't open /dev/kstat");
+ }
+
+ if (interval <= 0) {
+ count = 1;
+ once_only = B_TRUE;
+ }
+ /* 'for' loop 1: */
+ for (;;) {
+ mib_item_t *curritem = NULL; /* only for -[M]s */
+
+ /* netstat: AF_INET[6] behaviour */
+ if (family_selected(AF_INET) || family_selected(AF_INET6)) {
+ if (Sflag) {
+ curritem = mib_item_diff(previtem, item);
+ if (curritem == NULL)
+ fatal(1, "can't process mib data, "
+ "out of memory\n");
+ mib_item_destroy(&previtem);
+ }
+
+ if (!(Iflag || Rflag || Sflag || Mflag ||
+ MMflag || Pflag || Gflag || DHCPflag)) {
+ if (protocol_selected(IPPROTO_UDP))
+ udp_report(item);
+ if (protocol_selected(IPPROTO_TCP))
+ tcp_report(item);
+ if (protocol_selected(IPPROTO_SCTP))
+ sctp_report(item);
+ }
+ if (Iflag)
+ if_report(item, ifname, Iflag_only, once_only);
+ if (Mflag)
+ m_report();
+ if (Rflag)
+ ire_report(item);
+ if (Sflag && MMflag) {
+ mrt_stat_report(curritem);
+ } else {
+ if (Sflag)
+ stat_report(curritem);
+ if (MMflag)
+ mrt_report(item);
+ }
+ if (Gflag)
+ group_report(item);
+ if (Pflag) {
+ if (family_selected(AF_INET))
+ arp_report(item);
+ if (family_selected(AF_INET6))
+ ndp_report(item);
+ }
+ mib_item_destroy(&curritem);
+ }
+
+ /* netstat: AF_UNIX behaviour */
+ if (family_selected(AF_UNIX) &&
+ (!(Iflag || Rflag || Sflag || Mflag ||
+ MMflag || Pflag || Gflag)))
+ unixpr(kc);
+ (void) kstat_close(kc);
+
+ /* iteration handling code */
+ if (count > 0 && --count == 0)
+ break;
+ (void) sleep(interval);
+
+ /* re-populating of data structures */
+ if (family_selected(AF_INET) || family_selected(AF_INET6)) {
+ if (Sflag) {
+ /* previtem is a cut-down list */
+ previtem = mib_item_dup(item);
+ if (previtem == NULL)
+ fatal(1, "can't process mib data, "
+ "out of memory\n");
+ }
+ mibfree(item);
+ (void) close(sd);
+ if ((sd = mibopen()) == -1)
+ fatal(1, "can't open mib stream anymore\n");
+ if ((item = mibget(sd)) == NULL) {
+ (void) close(sd);
+ fatal(1, "mibget() failed\n");
+ }
+ }
+ if ((kc = kstat_open()) == NULL)
+ fail(1, "kstat_open(): can't open /dev/kstat");
+
+ } /* 'for' loop 1 ends */
+ mibfree(item);
+ (void) close(sd);
+
+ return (0);
+}
+
+
+static int
+isnum(char *p)
+{
+ int len;
+ int i;
+
+ len = strlen(p);
+ for (i = 0; i < len; i++)
+ if (!isdigit(p[i]))
+ return (0);
+ return (1);
+}
+
+
+/* --------------------------------- MIBGET -------------------------------- */
+
+static mib_item_t *
+mibget(int sd)
+{
+ /*
+ * buf is an automatic for this function, so the
+ * compiler has complete control over its alignment;
+ * it is assumed this alignment is satisfactory for
+ * it to be casted to certain other struct pointers
+ * here, such as struct T_optmgmt_ack * .
+ */
+ uintptr_t buf[512 / sizeof (uintptr_t)];
+ int flags;
+ int i, j, getcode;
+ struct strbuf ctlbuf, databuf;
+ struct T_optmgmt_req *tor = (struct T_optmgmt_req *)buf;
+ struct T_optmgmt_ack *toa = (struct T_optmgmt_ack *)buf;
+ struct T_error_ack *tea = (struct T_error_ack *)buf;
+ struct opthdr *req;
+ mib_item_t *first_item = NULL;
+ mib_item_t *last_item = NULL;
+ mib_item_t *temp;
+
+ tor->PRIM_type = T_SVR4_OPTMGMT_REQ;
+ tor->OPT_offset = sizeof (struct T_optmgmt_req);
+ tor->OPT_length = sizeof (struct opthdr);
+ tor->MGMT_flags = T_CURRENT;
+ req = (struct opthdr *)&tor[1];
+ req->level = MIB2_IP; /* any MIB2_xxx value ok here */
+ req->name = 0;
+ req->len = 0;
+
+ ctlbuf.buf = (char *)buf;
+ ctlbuf.len = tor->OPT_length + tor->OPT_offset;
+ flags = 0;
+ if (putmsg(sd, &ctlbuf, (struct strbuf *)0, flags) == -1) {
+ perror("mibget: putmsg(ctl) failed");
+ goto error_exit;
+ }
+
+ /*
+ * Each reply consists of a ctl part for one fixed structure
+ * or table, as defined in mib2.h. The format is a T_OPTMGMT_ACK,
+ * containing an opthdr structure. level/name identify the entry,
+ * len is the size of the data part of the message.
+ */
+ req = (struct opthdr *)&toa[1];
+ ctlbuf.maxlen = sizeof (buf);
+ j = 1;
+ for (;;) {
+ flags = 0;
+ getcode = getmsg(sd, &ctlbuf, (struct strbuf *)0, &flags);
+ if (getcode == -1) {
+ perror("mibget getmsg(ctl) failed");
+ if (Dflag) {
+ (void) fputs("# level name len\n",
+ stderr);
+ i = 0;
+ for (last_item = first_item; last_item;
+ last_item = last_item->next_item)
+ (void) printf("%d %4d %5d %d\n",
+ ++i,
+ last_item->group,
+ last_item->mib_id,
+ last_item->length);
+ }
+ goto error_exit;
+ }
+ if (getcode == 0 &&
+ ctlbuf.len >= sizeof (struct T_optmgmt_ack) &&
+ toa->PRIM_type == T_OPTMGMT_ACK &&
+ toa->MGMT_flags == T_SUCCESS &&
+ req->len == 0) {
+ if (Dflag)
+ (void) printf("mibget getmsg() %d returned "
+ "EOD (level %ld, name %ld)\n",
+ j, req->level, req->name);
+ return (first_item); /* this is EOD msg */
+ }
+
+ if (ctlbuf.len >= sizeof (struct T_error_ack) &&
+ tea->PRIM_type == T_ERROR_ACK) {
+ (void) fprintf(stderr,
+ "mibget %d gives T_ERROR_ACK: TLI_error = 0x%lx, "
+ "UNIX_error = 0x%lx\n",
+ j, tea->TLI_error, tea->UNIX_error);
+
+ errno = (tea->TLI_error == TSYSERR) ?
+ tea->UNIX_error : EPROTO;
+ goto error_exit;
+ }
+
+ if (getcode != MOREDATA ||
+ ctlbuf.len < sizeof (struct T_optmgmt_ack) ||
+ toa->PRIM_type != T_OPTMGMT_ACK ||
+ toa->MGMT_flags != T_SUCCESS) {
+ (void) printf("mibget getmsg(ctl) %d returned %d, "
+ "ctlbuf.len = %d, PRIM_type = %ld\n",
+ j, getcode, ctlbuf.len, toa->PRIM_type);
+
+ if (toa->PRIM_type == T_OPTMGMT_ACK)
+ (void) printf("T_OPTMGMT_ACK: "
+ "MGMT_flags = 0x%lx, req->len = %ld\n",
+ toa->MGMT_flags, req->len);
+ errno = ENOMSG;
+ goto error_exit;
+ }
+
+ temp = (mib_item_t *)malloc(sizeof (mib_item_t));
+ if (temp == NULL) {
+ perror("mibget malloc failed");
+ goto error_exit;
+ }
+ if (last_item != NULL)
+ last_item->next_item = temp;
+ else
+ first_item = temp;
+ last_item = temp;
+ last_item->next_item = NULL;
+ last_item->group = req->level;
+ last_item->mib_id = req->name;
+ last_item->length = req->len;
+ last_item->valp = malloc((int)req->len);
+ if (last_item->valp == NULL)
+ goto error_exit;
+ if (Dflag)
+ (void) printf("msg %d: group = %4d mib_id = %5d"
+ "length = %d\n",
+ j, last_item->group, last_item->mib_id,
+ last_item->length);
+
+ databuf.maxlen = last_item->length;
+ databuf.buf = (char *)last_item->valp;
+ databuf.len = 0;
+ flags = 0;
+ getcode = getmsg(sd, (struct strbuf *)0, &databuf, &flags);
+ if (getcode == -1) {
+ perror("mibget getmsg(data) failed");
+ goto error_exit;
+ } else if (getcode != 0) {
+ (void) printf("mibget getmsg(data) returned %d, "
+ "databuf.maxlen = %d, databuf.len = %d\n",
+ getcode, databuf.maxlen, databuf.len);
+ goto error_exit;
+ }
+ j++;
+ }
+ /* NOTREACHED */
+
+error_exit:;
+ mibfree(first_item);
+ return (NULL);
+}
+
+/*
+ * mibfree: frees a linked list of type (mib_item_t *)
+ * returned by mibget(); this is NOT THE SAME AS
+ * mib_item_destroy(), so should be used for objects
+ * returned by mibget() only
+ */
+static void
+mibfree(mib_item_t *firstitem)
+{
+ mib_item_t *lastitem;
+
+ while (firstitem != NULL) {
+ lastitem = firstitem;
+ firstitem = firstitem->next_item;
+ if (lastitem->valp != NULL)
+ free(lastitem->valp);
+ free(lastitem);
+ }
+}
+
+static int
+mibopen(void)
+{
+ int sd;
+
+ sd = open("/dev/arp", O_RDWR);
+ if (sd == -1) {
+ perror("arp open");
+ return (-1);
+ }
+ if (ioctl(sd, I_PUSH, "tcp") == -1) {
+ perror("tcp I_PUSH");
+ (void) close(sd);
+ return (-1);
+ }
+ if (ioctl(sd, I_PUSH, "udp") == -1) {
+ perror("udp I_PUSH");
+ (void) close(sd);
+ return (-1);
+ }
+ if (ioctl(sd, I_PUSH, "icmp") == -1) {
+ perror("icmp I_PUSH");
+ (void) close(sd);
+ return (-1);
+ }
+ return (sd);
+}
+
+/*
+ * mib_item_dup: returns a clean mib_item_t * linked
+ * list, so that for every element item->mib_id is 0;
+ * to deallocate this linked list, use mib_item_destroy
+ */
+static mib_item_t *
+mib_item_dup(mib_item_t *item)
+{
+ int c = 0;
+ mib_item_t *localp;
+ mib_item_t *tempp;
+
+ for (tempp = item; tempp; tempp = tempp->next_item)
+ if (tempp->mib_id == 0)
+ c++;
+ tempp = NULL;
+
+ localp = (mib_item_t *)malloc(c * sizeof (mib_item_t));
+ if (localp == NULL)
+ return (NULL);
+ c = 0;
+ for (; item; item = item->next_item) {
+ if (item->mib_id == 0) {
+ /* Replicate item in localp */
+ (localp[c]).next_item = NULL;
+ (localp[c]).group = item->group;
+ (localp[c]).mib_id = item->mib_id;
+ (localp[c]).length = item->length;
+ (localp[c]).valp = (uintptr_t *)malloc(
+ item->length);
+ if ((localp[c]).valp == NULL) {
+ mib_item_destroy(&localp);
+ return (NULL);
+ }
+ (void *) memcpy((localp[c]).valp,
+ item->valp,
+ item->length);
+ tempp = &(localp[c]);
+ if (c > 0)
+ (localp[c - 1]).next_item = tempp;
+ c++;
+ }
+ }
+ return (localp);
+}
+
+/*
+ * mib_item_diff: takes two (mib_item_t *) linked lists
+ * item1 and item2 and computes the difference between
+ * differentiable values in item2 against item1 for every
+ * given member of item2; returns an mib_item_t * linked
+ * list of diff's, or a copy of item2 if item1 is NULL;
+ * will return NULL if system out of memory; works only
+ * for item->mib_id == 0
+ */
+static mib_item_t *
+mib_item_diff(mib_item_t *item1, mib_item_t *item2) {
+ int nitems = 0; /* no. of items in item2 */
+ mib_item_t *tempp2; /* walking copy of item2 */
+ mib_item_t *tempp1; /* walking copy of item1 */
+ mib_item_t *diffp;
+ mib_item_t *diffptr; /* walking copy of diffp */
+ mib_item_t *prevp = NULL;
+
+ if (item1 == NULL) {
+ diffp = mib_item_dup(item2);
+ return (diffp);
+ }
+
+ for (tempp2 = item2;
+ tempp2;
+ tempp2 = tempp2->next_item) {
+ if (tempp2->mib_id == 0)
+ switch (tempp2->group) {
+ /*
+ * upon adding a case here, the same
+ * must also be added in the next
+ * switch statement, alongwith
+ * appropriate code
+ */
+ case MIB2_IP:
+ case MIB2_IP6:
+ case EXPER_DVMRP:
+ case EXPER_IGMP:
+ case MIB2_ICMP:
+ case MIB2_ICMP6:
+ case MIB2_TCP:
+ case MIB2_UDP:
+ case MIB2_SCTP:
+ case EXPER_RAWIP:
+ nitems++;
+ }
+ }
+ tempp2 = NULL;
+ if (nitems == 0) {
+ diffp = mib_item_dup(item2);
+ return (diffp);
+ }
+
+ diffp = (mib_item_t *)calloc(nitems, sizeof (mib_item_t));
+ if (diffp == NULL)
+ return (NULL);
+ diffptr = diffp;
+ /* 'for' loop 1: */
+ for (tempp2 = item2; tempp2 != NULL; tempp2 = tempp2->next_item) {
+ if (tempp2->mib_id != 0)
+ continue; /* 'for' loop 1 */
+ /* 'for' loop 2: */
+ for (tempp1 = item1; tempp1 != NULL;
+ tempp1 = tempp1->next_item) {
+ if (!(tempp1->mib_id == 0 &&
+ tempp1->group == tempp2->group &&
+ tempp1->mib_id == tempp2->mib_id))
+ continue; /* 'for' loop 2 */
+ /* found comparable data sets */
+ if (prevp != NULL)
+ prevp->next_item = diffptr;
+ switch (tempp2->group) {
+ /*
+ * Indenting note: Because of long variable names
+ * in cases MIB2_IP6 and MIB2_ICMP6, their contents
+ * have been indented by one tab space only
+ */
+ case MIB2_IP: {
+ mib2_ip_t *i2 = (mib2_ip_t *)tempp2->valp;
+ mib2_ip_t *i1 = (mib2_ip_t *)tempp1->valp;
+ mib2_ip_t *d;
+
+ diffptr->group = tempp2->group;
+ diffptr->mib_id = tempp2->mib_id;
+ diffptr->length = tempp2->length;
+ d = (mib2_ip_t *)calloc(tempp2->length, 1);
+ if (d == NULL)
+ goto mibdiff_out_of_memory;
+ diffptr->valp = d;
+ d->ipForwarding = i2->ipForwarding;
+ d->ipDefaultTTL = i2->ipDefaultTTL;
+ MDIFF(d, i2, i1, ipInReceives);
+ MDIFF(d, i2, i1, ipInHdrErrors);
+ MDIFF(d, i2, i1, ipInAddrErrors);
+ MDIFF(d, i2, i1, ipInCksumErrs);
+ MDIFF(d, i2, i1, ipForwDatagrams);
+ MDIFF(d, i2, i1, ipForwProhibits);
+ MDIFF(d, i2, i1, ipInUnknownProtos);
+ MDIFF(d, i2, i1, ipInDiscards);
+ MDIFF(d, i2, i1, ipInDelivers);
+ MDIFF(d, i2, i1, ipOutRequests);
+ MDIFF(d, i2, i1, ipOutDiscards);
+ MDIFF(d, i2, i1, ipOutNoRoutes);
+ MDIFF(d, i2, i1, ipReasmTimeout);
+ MDIFF(d, i2, i1, ipReasmReqds);
+ MDIFF(d, i2, i1, ipReasmOKs);
+ MDIFF(d, i2, i1, ipReasmFails);
+ MDIFF(d, i2, i1, ipReasmDuplicates);
+ MDIFF(d, i2, i1, ipReasmPartDups);
+ MDIFF(d, i2, i1, ipFragOKs);
+ MDIFF(d, i2, i1, ipFragFails);
+ MDIFF(d, i2, i1, ipFragCreates);
+ MDIFF(d, i2, i1, ipRoutingDiscards);
+ MDIFF(d, i2, i1, tcpInErrs);
+ MDIFF(d, i2, i1, udpNoPorts);
+ MDIFF(d, i2, i1, udpInCksumErrs);
+ MDIFF(d, i2, i1, udpInOverflows);
+ MDIFF(d, i2, i1, rawipInOverflows);
+ MDIFF(d, i2, i1, ipsecInSucceeded);
+ MDIFF(d, i2, i1, ipsecInFailed);
+ MDIFF(d, i2, i1, ipInIPv6);
+ MDIFF(d, i2, i1, ipOutIPv6);
+ MDIFF(d, i2, i1, ipOutSwitchIPv6);
+ prevp = diffptr++;
+ break;
+ }
+ case MIB2_IP6: {
+ mib2_ipv6IfStatsEntry_t *i2;
+ mib2_ipv6IfStatsEntry_t *i1;
+ mib2_ipv6IfStatsEntry_t *d;
+
+ i2 = (mib2_ipv6IfStatsEntry_t *)tempp2->valp;
+ i1 = (mib2_ipv6IfStatsEntry_t *)tempp1->valp;
+ diffptr->group = tempp2->group;
+ diffptr->mib_id = tempp2->mib_id;
+ diffptr->length = tempp2->length;
+ d = (mib2_ipv6IfStatsEntry_t *)calloc(
+ tempp2->length, 1);
+ if (d == NULL)
+ goto mibdiff_out_of_memory;
+ diffptr->valp = d;
+ d->ipv6Forwarding = i2->ipv6Forwarding;
+ d->ipv6DefaultHopLimit =
+ i2->ipv6DefaultHopLimit;
+
+ MDIFF(d, i2, i1, ipv6InReceives);
+ MDIFF(d, i2, i1, ipv6InHdrErrors);
+ MDIFF(d, i2, i1, ipv6InTooBigErrors);
+ MDIFF(d, i2, i1, ipv6InNoRoutes);
+ MDIFF(d, i2, i1, ipv6InAddrErrors);
+ MDIFF(d, i2, i1, ipv6InUnknownProtos);
+ MDIFF(d, i2, i1, ipv6InTruncatedPkts);
+ MDIFF(d, i2, i1, ipv6InDiscards);
+ MDIFF(d, i2, i1, ipv6InDelivers);
+ MDIFF(d, i2, i1, ipv6OutForwDatagrams);
+ MDIFF(d, i2, i1, ipv6OutRequests);
+ MDIFF(d, i2, i1, ipv6OutDiscards);
+ MDIFF(d, i2, i1, ipv6OutNoRoutes);
+ MDIFF(d, i2, i1, ipv6OutFragOKs);
+ MDIFF(d, i2, i1, ipv6OutFragFails);
+ MDIFF(d, i2, i1, ipv6OutFragCreates);
+ MDIFF(d, i2, i1, ipv6ReasmReqds);
+ MDIFF(d, i2, i1, ipv6ReasmOKs);
+ MDIFF(d, i2, i1, ipv6ReasmFails);
+ MDIFF(d, i2, i1, ipv6InMcastPkts);
+ MDIFF(d, i2, i1, ipv6OutMcastPkts);
+ MDIFF(d, i2, i1, ipv6ReasmDuplicates);
+ MDIFF(d, i2, i1, ipv6ReasmPartDups);
+ MDIFF(d, i2, i1, ipv6ForwProhibits);
+ MDIFF(d, i2, i1, udpInCksumErrs);
+ MDIFF(d, i2, i1, udpInOverflows);
+ MDIFF(d, i2, i1, rawipInOverflows);
+ MDIFF(d, i2, i1, ipv6InIPv4);
+ MDIFF(d, i2, i1, ipv6OutIPv4);
+ MDIFF(d, i2, i1, ipv6OutSwitchIPv4);
+ prevp = diffptr++;
+ break;
+ }
+ case EXPER_DVMRP: {
+ struct mrtstat *m2;
+ struct mrtstat *m1;
+ struct mrtstat *d;
+
+ m2 = (struct mrtstat *)tempp2->valp;
+ m1 = (struct mrtstat *)tempp1->valp;
+ diffptr->group = tempp2->group;
+ diffptr->mib_id = tempp2->mib_id;
+ diffptr->length = tempp2->length;
+ d = (struct mrtstat *)calloc(tempp2->length, 1);
+ if (d == NULL)
+ goto mibdiff_out_of_memory;
+ diffptr->valp = d;
+ MDIFF(d, m2, m1, mrts_mfc_hits);
+ MDIFF(d, m2, m1, mrts_mfc_misses);
+ MDIFF(d, m2, m1, mrts_fwd_in);
+ MDIFF(d, m2, m1, mrts_fwd_out);
+ d->mrts_upcalls = m2->mrts_upcalls;
+ MDIFF(d, m2, m1, mrts_fwd_drop);
+ MDIFF(d, m2, m1, mrts_bad_tunnel);
+ MDIFF(d, m2, m1, mrts_cant_tunnel);
+ MDIFF(d, m2, m1, mrts_wrong_if);
+ MDIFF(d, m2, m1, mrts_upq_ovflw);
+ MDIFF(d, m2, m1, mrts_cache_cleanups);
+ MDIFF(d, m2, m1, mrts_drop_sel);
+ MDIFF(d, m2, m1, mrts_q_overflow);
+ MDIFF(d, m2, m1, mrts_pkt2large);
+ MDIFF(d, m2, m1, mrts_pim_badversion);
+ MDIFF(d, m2, m1, mrts_pim_rcv_badcsum);
+ MDIFF(d, m2, m1, mrts_pim_badregisters);
+ MDIFF(d, m2, m1, mrts_pim_regforwards);
+ MDIFF(d, m2, m1, mrts_pim_regsend_drops);
+ MDIFF(d, m2, m1, mrts_pim_malformed);
+ MDIFF(d, m2, m1, mrts_pim_nomemory);
+ prevp = diffptr++;
+ break;
+ }
+ case EXPER_IGMP: {
+ struct igmpstat *i2;
+ struct igmpstat *i1;
+ struct igmpstat *d;
+
+ i2 = (struct igmpstat *)tempp2->valp;
+ i1 = (struct igmpstat *)tempp1->valp;
+ diffptr->group = tempp2->group;
+ diffptr->mib_id = tempp2->mib_id;
+ diffptr->length = tempp2->length;
+ d = (struct igmpstat *)calloc(
+ tempp2->length, 1);
+ if (d == NULL)
+ goto mibdiff_out_of_memory;
+ diffptr->valp = d;
+ MDIFF(d, i2, i1, igps_rcv_total);
+ MDIFF(d, i2, i1, igps_rcv_tooshort);
+ MDIFF(d, i2, i1, igps_rcv_badsum);
+ MDIFF(d, i2, i1, igps_rcv_queries);
+ MDIFF(d, i2, i1, igps_rcv_badqueries);
+ MDIFF(d, i2, i1, igps_rcv_reports);
+ MDIFF(d, i2, i1, igps_rcv_badreports);
+ MDIFF(d, i2, i1, igps_rcv_ourreports);
+ MDIFF(d, i2, i1, igps_snd_reports);
+ prevp = diffptr++;
+ break;
+ }
+ case MIB2_ICMP: {
+ mib2_icmp_t *i2;
+ mib2_icmp_t *i1;
+ mib2_icmp_t *d;
+
+ i2 = (mib2_icmp_t *)tempp2->valp;
+ i1 = (mib2_icmp_t *)tempp1->valp;
+ diffptr->group = tempp2->group;
+ diffptr->mib_id = tempp2->mib_id;
+ diffptr->length = tempp2->length;
+ d = (mib2_icmp_t *)calloc(tempp2->length, 1);
+ if (d == NULL)
+ goto mibdiff_out_of_memory;
+ diffptr->valp = d;
+ MDIFF(d, i2, i1, icmpInMsgs);
+ MDIFF(d, i2, i1, icmpInErrors);
+ MDIFF(d, i2, i1, icmpInCksumErrs);
+ MDIFF(d, i2, i1, icmpInUnknowns);
+ MDIFF(d, i2, i1, icmpInDestUnreachs);
+ MDIFF(d, i2, i1, icmpInTimeExcds);
+ MDIFF(d, i2, i1, icmpInParmProbs);
+ MDIFF(d, i2, i1, icmpInSrcQuenchs);
+ MDIFF(d, i2, i1, icmpInRedirects);
+ MDIFF(d, i2, i1, icmpInBadRedirects);
+ MDIFF(d, i2, i1, icmpInEchos);
+ MDIFF(d, i2, i1, icmpInEchoReps);
+ MDIFF(d, i2, i1, icmpInTimestamps);
+ MDIFF(d, i2, i1, icmpInAddrMasks);
+ MDIFF(d, i2, i1, icmpInAddrMaskReps);
+ MDIFF(d, i2, i1, icmpInFragNeeded);
+ MDIFF(d, i2, i1, icmpOutMsgs);
+ MDIFF(d, i2, i1, icmpOutDrops);
+ MDIFF(d, i2, i1, icmpOutErrors);
+ MDIFF(d, i2, i1, icmpOutDestUnreachs);
+ MDIFF(d, i2, i1, icmpOutTimeExcds);
+ MDIFF(d, i2, i1, icmpOutParmProbs);
+ MDIFF(d, i2, i1, icmpOutSrcQuenchs);
+ MDIFF(d, i2, i1, icmpOutRedirects);
+ MDIFF(d, i2, i1, icmpOutEchos);
+ MDIFF(d, i2, i1, icmpOutEchoReps);
+ MDIFF(d, i2, i1, icmpOutTimestamps);
+ MDIFF(d, i2, i1, icmpOutTimestampReps);
+ MDIFF(d, i2, i1, icmpOutAddrMasks);
+ MDIFF(d, i2, i1, icmpOutAddrMaskReps);
+ MDIFF(d, i2, i1, icmpOutFragNeeded);
+ MDIFF(d, i2, i1, icmpInOverflows);
+ prevp = diffptr++;
+ break;
+ }
+ case MIB2_ICMP6: {
+ mib2_ipv6IfIcmpEntry_t *i2;
+ mib2_ipv6IfIcmpEntry_t *i1;
+ mib2_ipv6IfIcmpEntry_t *d;
+
+ i2 = (mib2_ipv6IfIcmpEntry_t *)tempp2->valp;
+ i1 = (mib2_ipv6IfIcmpEntry_t *)tempp1->valp;
+ diffptr->group = tempp2->group;
+ diffptr->mib_id = tempp2->mib_id;
+ diffptr->length = tempp2->length;
+ d = (mib2_ipv6IfIcmpEntry_t *)calloc(tempp2->length, 1);
+ if (d == NULL)
+ goto mibdiff_out_of_memory;
+ diffptr->valp = d;
+ MDIFF(d, i2, i1, ipv6IfIcmpInMsgs);
+ MDIFF(d, i2, i1, ipv6IfIcmpInErrors);
+ MDIFF(d, i2, i1, ipv6IfIcmpInDestUnreachs);
+ MDIFF(d, i2, i1, ipv6IfIcmpInAdminProhibs);
+ MDIFF(d, i2, i1, ipv6IfIcmpInTimeExcds);
+ MDIFF(d, i2, i1, ipv6IfIcmpInParmProblems);
+ MDIFF(d, i2, i1, ipv6IfIcmpInPktTooBigs);
+ MDIFF(d, i2, i1, ipv6IfIcmpInEchos);
+ MDIFF(d, i2, i1, ipv6IfIcmpInEchoReplies);
+ MDIFF(d, i2, i1, ipv6IfIcmpInRouterSolicits);
+ MDIFF(d, i2, i1, ipv6IfIcmpInRouterAdvertisements);
+ MDIFF(d, i2, i1, ipv6IfIcmpInNeighborSolicits);
+ MDIFF(d, i2, i1, ipv6IfIcmpInNeighborAdvertisements);
+ MDIFF(d, i2, i1, ipv6IfIcmpInRedirects);
+ MDIFF(d, i2, i1, ipv6IfIcmpInBadRedirects);
+ MDIFF(d, i2, i1, ipv6IfIcmpInGroupMembQueries);
+ MDIFF(d, i2, i1, ipv6IfIcmpInGroupMembResponses);
+ MDIFF(d, i2, i1, ipv6IfIcmpInGroupMembReductions);
+ MDIFF(d, i2, i1, ipv6IfIcmpInOverflows);
+ MDIFF(d, i2, i1, ipv6IfIcmpOutMsgs);
+ MDIFF(d, i2, i1, ipv6IfIcmpOutErrors);
+ MDIFF(d, i2, i1, ipv6IfIcmpOutDestUnreachs);
+ MDIFF(d, i2, i1, ipv6IfIcmpOutAdminProhibs);
+ MDIFF(d, i2, i1, ipv6IfIcmpOutTimeExcds);
+ MDIFF(d, i2, i1, ipv6IfIcmpOutParmProblems);
+ MDIFF(d, i2, i1, ipv6IfIcmpOutPktTooBigs);
+ MDIFF(d, i2, i1, ipv6IfIcmpOutEchos);
+ MDIFF(d, i2, i1, ipv6IfIcmpOutEchoReplies);
+ MDIFF(d, i2, i1, ipv6IfIcmpOutRouterSolicits);
+ MDIFF(d, i2, i1, ipv6IfIcmpOutRouterAdvertisements);
+ MDIFF(d, i2, i1, ipv6IfIcmpOutNeighborSolicits);
+ MDIFF(d, i2, i1, ipv6IfIcmpOutNeighborAdvertisements);
+ MDIFF(d, i2, i1, ipv6IfIcmpOutRedirects);
+ MDIFF(d, i2, i1, ipv6IfIcmpOutGroupMembQueries);
+ MDIFF(d, i2, i1, ipv6IfIcmpOutGroupMembResponses);
+ MDIFF(d, i2, i1, ipv6IfIcmpOutGroupMembReductions);
+ prevp = diffptr++;
+ break;
+ }
+ case MIB2_TCP: {
+ mib2_tcp_t *t2;
+ mib2_tcp_t *t1;
+ mib2_tcp_t *d;
+
+ t2 = (mib2_tcp_t *)tempp2->valp;
+ t1 = (mib2_tcp_t *)tempp1->valp;
+ diffptr->group = tempp2->group;
+ diffptr->mib_id = tempp2->mib_id;
+ diffptr->length = tempp2->length;
+ d = (mib2_tcp_t *)calloc(tempp2->length, 1);
+ if (d == NULL)
+ goto mibdiff_out_of_memory;
+ diffptr->valp = d;
+ d->tcpRtoMin = t2->tcpRtoMin;
+ d->tcpRtoMax = t2->tcpRtoMax;
+ d->tcpMaxConn = t2->tcpMaxConn;
+ MDIFF(d, t2, t1, tcpActiveOpens);
+ MDIFF(d, t2, t1, tcpPassiveOpens);
+ MDIFF(d, t2, t1, tcpAttemptFails);
+ MDIFF(d, t2, t1, tcpEstabResets);
+ d->tcpCurrEstab = t2->tcpCurrEstab;
+ MDIFF(d, t2, t1, tcpOutSegs);
+ MDIFF(d, t2, t1, tcpOutDataSegs);
+ MDIFF(d, t2, t1, tcpOutDataBytes);
+ MDIFF(d, t2, t1, tcpRetransSegs);
+ MDIFF(d, t2, t1, tcpRetransBytes);
+ MDIFF(d, t2, t1, tcpOutAck);
+ MDIFF(d, t2, t1, tcpOutAckDelayed);
+ MDIFF(d, t2, t1, tcpOutUrg);
+ MDIFF(d, t2, t1, tcpOutWinUpdate);
+ MDIFF(d, t2, t1, tcpOutWinProbe);
+ MDIFF(d, t2, t1, tcpOutControl);
+ MDIFF(d, t2, t1, tcpOutRsts);
+ MDIFF(d, t2, t1, tcpOutFastRetrans);
+ MDIFF(d, t2, t1, tcpInSegs);
+ MDIFF(d, t2, t1, tcpInAckSegs);
+ MDIFF(d, t2, t1, tcpInAckBytes);
+ MDIFF(d, t2, t1, tcpInDupAck);
+ MDIFF(d, t2, t1, tcpInAckUnsent);
+ MDIFF(d, t2, t1, tcpInDataInorderSegs);
+ MDIFF(d, t2, t1, tcpInDataInorderBytes);
+ MDIFF(d, t2, t1, tcpInDataUnorderSegs);
+ MDIFF(d, t2, t1, tcpInDataUnorderBytes);
+ MDIFF(d, t2, t1, tcpInDataDupSegs);
+ MDIFF(d, t2, t1, tcpInDataDupBytes);
+ MDIFF(d, t2, t1, tcpInDataPartDupSegs);
+ MDIFF(d, t2, t1, tcpInDataPartDupBytes);
+ MDIFF(d, t2, t1, tcpInDataPastWinSegs);
+ MDIFF(d, t2, t1, tcpInDataPastWinBytes);
+ MDIFF(d, t2, t1, tcpInWinProbe);
+ MDIFF(d, t2, t1, tcpInWinUpdate);
+ MDIFF(d, t2, t1, tcpInClosed);
+ MDIFF(d, t2, t1, tcpRttNoUpdate);
+ MDIFF(d, t2, t1, tcpRttUpdate);
+ MDIFF(d, t2, t1, tcpTimRetrans);
+ MDIFF(d, t2, t1, tcpTimRetransDrop);
+ MDIFF(d, t2, t1, tcpTimKeepalive);
+ MDIFF(d, t2, t1, tcpTimKeepaliveProbe);
+ MDIFF(d, t2, t1, tcpTimKeepaliveDrop);
+ MDIFF(d, t2, t1, tcpListenDrop);
+ MDIFF(d, t2, t1, tcpListenDropQ0);
+ MDIFF(d, t2, t1, tcpHalfOpenDrop);
+ MDIFF(d, t2, t1, tcpOutSackRetransSegs);
+ prevp = diffptr++;
+ break;
+ }
+ case MIB2_UDP: {
+ mib2_udp_t *u2;
+ mib2_udp_t *u1;
+ mib2_udp_t *d;
+
+ u2 = (mib2_udp_t *)tempp2->valp;
+ u1 = (mib2_udp_t *)tempp1->valp;
+ diffptr->group = tempp2->group;
+ diffptr->mib_id = tempp2->mib_id;
+ diffptr->length = tempp2->length;
+ d = (mib2_udp_t *)calloc(tempp2->length, 1);
+ if (d == NULL)
+ goto mibdiff_out_of_memory;
+ diffptr->valp = d;
+ MDIFF(d, u2, u1, udpInDatagrams);
+ MDIFF(d, u2, u1, udpInErrors);
+ MDIFF(d, u2, u1, udpOutDatagrams);
+ MDIFF(d, u2, u1, udpOutErrors);
+ prevp = diffptr++;
+ break;
+ }
+ case MIB2_SCTP: {
+ mib2_sctp_t *s2;
+ mib2_sctp_t *s1;
+ mib2_sctp_t *d;
+
+ s2 = (mib2_sctp_t *)tempp2->valp;
+ s1 = (mib2_sctp_t *)tempp1->valp;
+ diffptr->group = tempp2->group;
+ diffptr->mib_id = tempp2->mib_id;
+ diffptr->length = tempp2->length;
+ d = (mib2_sctp_t *)calloc(tempp2->length, 1);
+ if (d == NULL)
+ goto mibdiff_out_of_memory;
+ diffptr->valp = d;
+ d->sctpRtoAlgorithm = s2->sctpRtoAlgorithm;
+ d->sctpRtoMin = s2->sctpRtoMin;
+ d->sctpRtoMax = s2->sctpRtoMax;
+ d->sctpRtoInitial = s2->sctpRtoInitial;
+ d->sctpMaxAssocs = s2->sctpMaxAssocs;
+ d->sctpValCookieLife = s2->sctpValCookieLife;
+ d->sctpMaxInitRetr = s2->sctpMaxInitRetr;
+ d->sctpCurrEstab = s2->sctpCurrEstab;
+ MDIFF(d, s2, s1, sctpActiveEstab);
+ MDIFF(d, s2, s1, sctpPassiveEstab);
+ MDIFF(d, s2, s1, sctpAborted);
+ MDIFF(d, s2, s1, sctpShutdowns);
+ MDIFF(d, s2, s1, sctpOutOfBlue);
+ MDIFF(d, s2, s1, sctpChecksumError);
+ MDIFF(d, s2, s1, sctpOutCtrlChunks);
+ MDIFF(d, s2, s1, sctpOutOrderChunks);
+ MDIFF(d, s2, s1, sctpOutUnorderChunks);
+ MDIFF(d, s2, s1, sctpRetransChunks);
+ MDIFF(d, s2, s1, sctpOutAck);
+ MDIFF(d, s2, s1, sctpOutAckDelayed);
+ MDIFF(d, s2, s1, sctpOutWinUpdate);
+ MDIFF(d, s2, s1, sctpOutFastRetrans);
+ MDIFF(d, s2, s1, sctpOutWinProbe);
+ MDIFF(d, s2, s1, sctpInCtrlChunks);
+ MDIFF(d, s2, s1, sctpInOrderChunks);
+ MDIFF(d, s2, s1, sctpInUnorderChunks);
+ MDIFF(d, s2, s1, sctpInAck);
+ MDIFF(d, s2, s1, sctpInDupAck);
+ MDIFF(d, s2, s1, sctpInAckUnsent);
+ MDIFF(d, s2, s1, sctpFragUsrMsgs);
+ MDIFF(d, s2, s1, sctpReasmUsrMsgs);
+ MDIFF(d, s2, s1, sctpOutSCTPPkts);
+ MDIFF(d, s2, s1, sctpInSCTPPkts);
+ MDIFF(d, s2, s1, sctpInInvalidCookie);
+ MDIFF(d, s2, s1, sctpTimRetrans);
+ MDIFF(d, s2, s1, sctpTimRetransDrop);
+ MDIFF(d, s2, s1, sctpTimHeartBeatProbe);
+ MDIFF(d, s2, s1, sctpTimHeartBeatDrop);
+ MDIFF(d, s2, s1, sctpListenDrop);
+ MDIFF(d, s2, s1, sctpInClosed);
+ prevp = diffptr++;
+ break;
+ }
+ case EXPER_RAWIP: {
+ mib2_rawip_t *r2;
+ mib2_rawip_t *r1;
+ mib2_rawip_t *d;
+
+ r2 = (mib2_rawip_t *)tempp2->valp;
+ r1 = (mib2_rawip_t *)tempp1->valp;
+ diffptr->group = tempp2->group;
+ diffptr->mib_id = tempp2->mib_id;
+ diffptr->length = tempp2->length;
+ d = (mib2_rawip_t *)calloc(tempp2->length, 1);
+ if (d == NULL)
+ goto mibdiff_out_of_memory;
+ diffptr->valp = d;
+ MDIFF(d, r2, r1, rawipInDatagrams);
+ MDIFF(d, r2, r1, rawipInErrors);
+ MDIFF(d, r2, r1, rawipInCksumErrs);
+ MDIFF(d, r2, r1, rawipOutDatagrams);
+ MDIFF(d, r2, r1, rawipOutErrors);
+ prevp = diffptr++;
+ break;
+ }
+ /*
+ * there are more "group" types but they aren't
+ * required for the -s and -Ms options
+ */
+ }
+ } /* 'for' loop 2 ends */
+ tempp1 = NULL;
+ } /* 'for' loop 1 ends */
+ tempp2 = NULL;
+ diffptr--;
+ diffptr->next_item = NULL;
+ return (diffp);
+
+mibdiff_out_of_memory:;
+ mib_item_destroy(&diffp);
+ return (NULL);
+}
+
+/*
+ * mib_item_destroy: cleans up a mib_item_t *
+ * that was created by calling mib_item_dup or
+ * mib_item_diff
+ */
+static void
+mib_item_destroy(mib_item_t **itemp) {
+ int nitems = 0;
+ int c = 0;
+ mib_item_t *tempp;
+
+ if (itemp == NULL || *itemp == NULL)
+ return;
+
+ for (tempp = *itemp; tempp != NULL; tempp = tempp->next_item)
+ if (tempp->mib_id == 0)
+ nitems++;
+ else
+ return; /* cannot destroy! */
+
+ if (nitems == 0)
+ return; /* cannot destroy! */
+
+ for (c = nitems - 1; c >= 0; c--) {
+ if ((itemp[0][c]).valp != NULL)
+ free((itemp[0][c]).valp);
+ }
+ free(*itemp);
+
+ *itemp = NULL;
+}
+
+/* Compare two Octet_ts. Return B_TRUE if they match, B_FALSE if not. */
+static boolean_t
+octetstrmatch(const Octet_t *a, const Octet_t *b)
+{
+ if (a == NULL || b == NULL)
+ return (B_FALSE);
+
+ if (a->o_length != b->o_length)
+ return (B_FALSE);
+
+ return (memcmp(a->o_bytes, b->o_bytes, a->o_length) == 0);
+}
+
+/* If octetstr() changes make an appropriate change to STR_EXPAND */
+static char *
+octetstr(Octet_t *op, int code, char *dst, uint_t dstlen)
+{
+ int i;
+ char *cp;
+
+ cp = dst;
+ if (op) {
+ for (i = 0; i < op->o_length; i++) {
+ switch (code) {
+ case 'd':
+ if (cp - dst + 4 > dstlen) {
+ *cp = '\0';
+ return (dst);
+ }
+ (void) snprintf(cp, 5, "%d.",
+ 0xff & op->o_bytes[i]);
+ cp = strchr(cp, '\0');
+ break;
+ case 'a':
+ if (cp - dst + 1 > dstlen) {
+ *cp = '\0';
+ return (dst);
+ }
+ *cp++ = op->o_bytes[i];
+ break;
+ case 'h':
+ default:
+ if (cp - dst + 3 > dstlen) {
+ *cp = '\0';
+ return (dst);
+ }
+ (void) snprintf(cp, 4, "%02x:",
+ 0xff & op->o_bytes[i]);
+ cp += 3;
+ break;
+ }
+ }
+ }
+ if (code != 'a' && cp != dst)
+ cp--;
+ *cp = '\0';
+ return (dst);
+}
+
+static char *
+mitcp_state(int state)
+{
+static char tcpsbuf[50];
+ char *cp;
+
+ switch (state) {
+ case TCPS_CLOSED:
+ cp = "CLOSED";
+ break;
+ case TCPS_IDLE:
+ cp = "IDLE";
+ break;
+ case TCPS_BOUND:
+ cp = "BOUND";
+ break;
+ case TCPS_LISTEN:
+ cp = "LISTEN";
+ break;
+ case TCPS_SYN_SENT:
+ cp = "SYN_SENT";
+ break;
+ case TCPS_SYN_RCVD:
+ cp = "SYN_RCVD";
+ break;
+ case TCPS_ESTABLISHED:
+ cp = "ESTABLISHED";
+ break;
+ case TCPS_CLOSE_WAIT:
+ cp = "CLOSE_WAIT";
+ break;
+ case TCPS_FIN_WAIT_1:
+ cp = "FIN_WAIT_1";
+ break;
+ case TCPS_CLOSING:
+ cp = "CLOSING";
+ break;
+ case TCPS_LAST_ACK:
+ cp = "LAST_ACK";
+ break;
+ case TCPS_FIN_WAIT_2:
+ cp = "FIN_WAIT_2";
+ break;
+ case TCPS_TIME_WAIT:
+ cp = "TIME_WAIT";
+ break;
+ default:
+ (void) snprintf(tcpsbuf, sizeof (tcpsbuf),
+ "UnknownState(%d)", state);
+ cp = tcpsbuf;
+ break;
+ }
+ return (cp);
+}
+
+static int odd;
+
+static void
+prval_init(void)
+{
+ odd = 0;
+}
+
+static void
+prval(char *str, Counter val)
+{
+ (void) printf("\t%-20s=%6u", str, val);
+ if (odd++ & 1)
+ (void) putchar('\n');
+}
+
+static void
+prval64(char *str, Counter64 val)
+{
+ (void) printf("\t%-20s=%6llu", str, val);
+ if (odd++ & 1)
+ (void) putchar('\n');
+}
+
+static void
+pr_int_val(char *str, int val)
+{
+ (void) printf("\t%-20s=%6d", str, val);
+ if (odd++ & 1)
+ (void) putchar('\n');
+}
+
+static void
+pr_sctp_rtoalgo(char *str, int val)
+{
+ (void) printf("\t%-20s=", str);
+ switch (val) {
+ case MIB2_SCTP_RTOALGO_OTHER:
+ (void) printf("%6.6s", "other");
+ break;
+
+ case MIB2_SCTP_RTOALGO_VANJ:
+ (void) printf("%6.6s", "vanj");
+ break;
+
+ default:
+ (void) printf("%6d", val);
+ break;
+ }
+ if (odd++ & 1)
+ (void) putchar('\n');
+}
+
+static void
+prval_end(void)
+{
+ if (odd++ & 1)
+ (void) putchar('\n');
+}
+
+/* Extract constant sizes */
+static void
+mib_get_constants(mib_item_t *item)
+{
+ /* 'for' loop 1: */
+ for (; item; item = item->next_item) {
+ if (item->mib_id != 0)
+ continue; /* 'for' loop 1 */
+
+ switch (item->group) {
+ case MIB2_IP: {
+ mib2_ip_t *ip = (mib2_ip_t *)item->valp;
+
+ ipAddrEntrySize = ip->ipAddrEntrySize;
+ ipRouteEntrySize = ip->ipRouteEntrySize;
+ ipNetToMediaEntrySize = ip->ipNetToMediaEntrySize;
+ ipMemberEntrySize = ip->ipMemberEntrySize;
+ ipGroupSourceEntrySize = ip->ipGroupSourceEntrySize;
+ assert(IS_P2ALIGNED(ipAddrEntrySize,
+ sizeof (mib2_ipAddrEntry_t *)) &&
+ IS_P2ALIGNED(ipRouteEntrySize,
+ sizeof (mib2_ipRouteEntry_t *)) &&
+ IS_P2ALIGNED(ipNetToMediaEntrySize,
+ sizeof (mib2_ipNetToMediaEntry_t *)) &&
+ IS_P2ALIGNED(ipMemberEntrySize,
+ sizeof (ip_member_t *)) &&
+ IS_P2ALIGNED(ipGroupSourceEntrySize,
+ sizeof (ip_grpsrc_t *)));
+ break;
+ }
+ case EXPER_DVMRP: {
+ struct mrtstat *mrts = (struct mrtstat *)item->valp;
+
+ vifctlSize = mrts->mrts_vifctlSize;
+ mfcctlSize = mrts->mrts_mfcctlSize;
+ assert(IS_P2ALIGNED(vifctlSize,
+ sizeof (struct vifclt *)) &&
+ IS_P2ALIGNED(mfcctlSize, sizeof (struct mfcctl *)));
+ break;
+ }
+ case MIB2_IP6: {
+ mib2_ipv6IfStatsEntry_t *ip6;
+ /* Just use the first entry */
+
+ ip6 = (mib2_ipv6IfStatsEntry_t *)item->valp;
+ ipv6IfStatsEntrySize = ip6->ipv6IfStatsEntrySize;
+ ipv6AddrEntrySize = ip6->ipv6AddrEntrySize;
+ ipv6RouteEntrySize = ip6->ipv6RouteEntrySize;
+ ipv6NetToMediaEntrySize = ip6->ipv6NetToMediaEntrySize;
+ ipv6MemberEntrySize = ip6->ipv6MemberEntrySize;
+ ipv6GroupSourceEntrySize =
+ ip6->ipv6GroupSourceEntrySize;
+ assert(IS_P2ALIGNED(ipv6IfStatsEntrySize,
+ sizeof (mib2_ipv6IfStatsEntry_t *)) &&
+ IS_P2ALIGNED(ipv6AddrEntrySize,
+ sizeof (mib2_ipv6AddrEntry_t *)) &&
+ IS_P2ALIGNED(ipv6RouteEntrySize,
+ sizeof (mib2_ipv6RouteEntry_t *)) &&
+ IS_P2ALIGNED(ipv6NetToMediaEntrySize,
+ sizeof (mib2_ipv6NetToMediaEntry_t *)) &&
+ IS_P2ALIGNED(ipv6MemberEntrySize,
+ sizeof (ipv6_member_t *)) &&
+ IS_P2ALIGNED(ipv6GroupSourceEntrySize,
+ sizeof (ipv6_grpsrc_t *)));
+ break;
+ }
+ case MIB2_ICMP6: {
+ mib2_ipv6IfIcmpEntry_t *icmp6;
+ /* Just use the first entry */
+
+ icmp6 = (mib2_ipv6IfIcmpEntry_t *)item->valp;
+ ipv6IfIcmpEntrySize = icmp6->ipv6IfIcmpEntrySize;
+ assert(IS_P2ALIGNED(ipv6IfIcmpEntrySize,
+ sizeof (mib2_ipv6IfIcmpEntry_t *)));
+ break;
+ }
+ case MIB2_TCP: {
+ mib2_tcp_t *tcp = (mib2_tcp_t *)item->valp;
+
+ tcpConnEntrySize = tcp->tcpConnTableSize;
+ tcp6ConnEntrySize = tcp->tcp6ConnTableSize;
+ assert(IS_P2ALIGNED(tcpConnEntrySize,
+ sizeof (mib2_tcpConnEntry_t *)) &&
+ IS_P2ALIGNED(tcp6ConnEntrySize,
+ sizeof (mib2_tcp6ConnEntry_t *)));
+ break;
+ }
+ case MIB2_UDP: {
+ mib2_udp_t *udp = (mib2_udp_t *)item->valp;
+
+ udpEntrySize = udp->udpEntrySize;
+ udp6EntrySize = udp->udp6EntrySize;
+ assert(IS_P2ALIGNED(udpEntrySize,
+ sizeof (mib2_udpEntry_t *)) &&
+ IS_P2ALIGNED(udp6EntrySize,
+ sizeof (mib2_udp6Entry_t *)));
+ break;
+ }
+ case MIB2_SCTP: {
+ mib2_sctp_t *sctp = (mib2_sctp_t *)item->valp;
+
+ sctpEntrySize = sctp->sctpEntrySize;
+ sctpLocalEntrySize = sctp->sctpLocalEntrySize;
+ sctpRemoteEntrySize = sctp->sctpRemoteEntrySize;
+ break;
+ }
+ }
+ } /* 'for' loop 1 ends */
+
+ if (Dflag) {
+ (void) puts("mib_get_constants:");
+ (void) printf("\tipv6IfStatsEntrySize %d\n",
+ ipv6IfStatsEntrySize);
+ (void) printf("\tipAddrEntrySize %d\n", ipAddrEntrySize);
+ (void) printf("\tipRouteEntrySize %d\n", ipRouteEntrySize);
+ (void) printf("\tipNetToMediaEntrySize %d\n",
+ ipNetToMediaEntrySize);
+ (void) printf("\tipMemberEntrySize %d\n", ipMemberEntrySize);
+ (void) printf("\tvifctlSize %d\n", vifctlSize);
+ (void) printf("\tmfcctlSize %d\n", mfcctlSize);
+
+ (void) printf("\tipv6AddrEntrySize %d\n", ipv6AddrEntrySize);
+ (void) printf("\tipv6RouteEntrySize %d\n", ipv6RouteEntrySize);
+ (void) printf("\tipv6NetToMediaEntrySize %d\n",
+ ipv6NetToMediaEntrySize);
+ (void) printf("\tipv6MemberEntrySize %d\n",
+ ipv6MemberEntrySize);
+ (void) printf("\tipv6IfIcmpEntrySize %d\n",
+ ipv6IfIcmpEntrySize);
+ (void) printf("\ttcpConnEntrySize %d\n", tcpConnEntrySize);
+ (void) printf("\ttcp6ConnEntrySize %d\n", tcp6ConnEntrySize);
+ (void) printf("\tudpEntrySize %d\n", udpEntrySize);
+ (void) printf("\tudp6EntrySize %d\n", udp6EntrySize);
+ (void) printf("\tsctpEntrySize %d\n", sctpEntrySize);
+ (void) printf("\tsctpLocalEntrySize %d\n", sctpLocalEntrySize);
+ (void) printf("\tsctpRemoteEntrySize %d\n",
+ sctpRemoteEntrySize);
+ }
+}
+
+
+/* ----------------------------- STAT_REPORT ------------------------------- */
+
+static void
+stat_report(mib_item_t *item)
+{
+ int jtemp = 0;
+ char ifname[LIFNAMSIZ + 1];
+ char *ifnamep;
+
+ /* 'for' loop 1: */
+ for (; item; item = item->next_item) {
+ if (Dflag) {
+ (void) printf("\n--- Entry %d ---\n", ++jtemp);
+ (void) printf("Group = %d, mib_id = %d, "
+ "length = %d, valp = 0x%p\n",
+ item->group, item->mib_id,
+ item->length, item->valp);
+ }
+ if (item->mib_id != 0)
+ continue; /* 'for' loop 1 */
+
+ switch (item->group) {
+ case MIB2_IP: {
+ mib2_ip_t *ip = (mib2_ip_t *)item->valp;
+
+ if (protocol_selected(IPPROTO_IP) &&
+ family_selected(AF_INET)) {
+ (void) fputs(v4compat ? "\nIP" : "\nIPv4",
+ stdout);
+ print_ip_stats(ip);
+ }
+ break;
+ }
+ case MIB2_ICMP: {
+ mib2_icmp_t *icmp =
+ (mib2_icmp_t *)item->valp;
+
+ if (protocol_selected(IPPROTO_ICMP) &&
+ family_selected(AF_INET)) {
+ (void) fputs(v4compat ? "\nICMP" : "\nICMPv4",
+ stdout);
+ print_icmp_stats(icmp);
+ }
+ break;
+ }
+ case MIB2_IP6: {
+ mib2_ipv6IfStatsEntry_t *ip6;
+ mib2_ipv6IfStatsEntry_t sum6;
+
+ if (!(protocol_selected(IPPROTO_IPV6)) ||
+ !(family_selected(AF_INET6)))
+ break;
+ bzero(&sum6, sizeof (sum6));
+ /* 'for' loop 2a: */
+ for (ip6 = (mib2_ipv6IfStatsEntry_t *)item->valp;
+ (char *)ip6 < (char *)item->valp
+ + item->length;
+ /* LINTED: (note 1) */
+ ip6 = (mib2_ipv6IfStatsEntry_t *)((char *)ip6 +
+ ipv6IfStatsEntrySize)) {
+
+ if (ip6->ipv6IfIndex == 0) {
+ /*
+ * The "unknown interface" ip6
+ * mib. Just add to the sum.
+ */
+ sum_ip6_stats(ip6, &sum6);
+ continue; /* 'for' loop 2a */
+ }
+ ifnamep = if_indextoname(
+ ip6->ipv6IfIndex,
+ ifname);
+ if (ifnamep == NULL) {
+ (void) printf(
+ "Invalid ifindex %d\n",
+ ip6->ipv6IfIndex);
+ continue; /* 'for' loop 2a */
+ }
+
+ if (Aflag) {
+ (void) printf("\nIPv6 for %s\n",
+ ifnamep);
+ print_ip6_stats(ip6);
+ }
+ sum_ip6_stats(ip6, &sum6);
+ } /* 'for' loop 2a ends */
+ (void) fputs("\nIPv6", stdout);
+ print_ip6_stats(&sum6);
+ break;
+ }
+ case MIB2_ICMP6: {
+ mib2_ipv6IfIcmpEntry_t *icmp6;
+ mib2_ipv6IfIcmpEntry_t sum6;
+
+ if (!(protocol_selected(IPPROTO_ICMPV6)) ||
+ !(family_selected(AF_INET6)))
+ break;
+ bzero(&sum6, sizeof (sum6));
+ /* 'for' loop 2b: */
+ for (icmp6 =
+ (mib2_ipv6IfIcmpEntry_t *)item->valp;
+ (char *)icmp6 < (char *)item->valp
+ + item->length;
+ icmp6 =
+ /* LINTED: (note 1) */
+ (mib2_ipv6IfIcmpEntry_t *)((char *)icmp6
+ + ipv6IfIcmpEntrySize)) {
+
+ if (icmp6->ipv6IfIcmpIfIndex == 0) {
+ /*
+ * The "unknown interface" icmp6
+ * mib. Just add to the sum.
+ */
+ sum_icmp6_stats(icmp6, &sum6);
+ continue; /* 'for' loop 2b: */
+ }
+ ifnamep = if_indextoname(
+ icmp6->ipv6IfIcmpIfIndex, ifname);
+ if (ifnamep == NULL) {
+ (void) printf(
+ "Invalid ifindex %d\n",
+ icmp6->ipv6IfIcmpIfIndex);
+ continue; /* 'for' loop 2b: */
+ }
+
+ if (Aflag) {
+ (void) printf(
+ "\nICMPv6 for %s\n",
+ ifnamep);
+ print_icmp6_stats(icmp6);
+ }
+ sum_icmp6_stats(icmp6, &sum6);
+ } /* 'for' loop 2b ends */
+ (void) fputs("\nICMPv6", stdout);
+ print_icmp6_stats(&sum6);
+ break;
+ }
+ case MIB2_TCP: {
+ mib2_tcp_t *tcp = (mib2_tcp_t *)item->valp;
+
+ if (protocol_selected(IPPROTO_TCP) &&
+ (family_selected(AF_INET) ||
+ family_selected(AF_INET6))) {
+ (void) fputs("\nTCP", stdout);
+ print_tcp_stats(tcp);
+ }
+ break;
+ }
+ case MIB2_UDP: {
+ mib2_udp_t *udp = (mib2_udp_t *)item->valp;
+
+ if (protocol_selected(IPPROTO_UDP) &&
+ (family_selected(AF_INET) ||
+ family_selected(AF_INET6))) {
+ (void) fputs("\nUDP", stdout);
+ print_udp_stats(udp);
+ }
+ break;
+ }
+ case MIB2_SCTP: {
+ mib2_sctp_t *sctp = (mib2_sctp_t *)item->valp;
+
+ if (protocol_selected(IPPROTO_SCTP) &&
+ (family_selected(AF_INET) ||
+ family_selected(AF_INET6))) {
+ (void) fputs("\nSCTP", stdout);
+ print_sctp_stats(sctp);
+ }
+ break;
+ }
+ case EXPER_RAWIP: {
+ mib2_rawip_t *rawip =
+ (mib2_rawip_t *)item->valp;
+
+ if (protocol_selected(IPPROTO_RAW) &&
+ (family_selected(AF_INET) ||
+ family_selected(AF_INET6))) {
+ (void) fputs("\nRAWIP", stdout);
+ print_rawip_stats(rawip);
+ }
+ break;
+ }
+ case EXPER_IGMP: {
+ struct igmpstat *igps =
+ (struct igmpstat *)item->valp;
+
+ if (protocol_selected(IPPROTO_IGMP) &&
+ (family_selected(AF_INET))) {
+ (void) fputs("\nIGMP:\n", stdout);
+ print_igmp_stats(igps);
+ }
+ break;
+ }
+ }
+ } /* 'for' loop 1 ends */
+ (void) putchar('\n');
+ (void) fflush(stdout);
+}
+
+static void
+print_ip_stats(mib2_ip_t *ip)
+{
+ prval_init();
+ pr_int_val("ipForwarding", ip->ipForwarding);
+ pr_int_val("ipDefaultTTL", ip->ipDefaultTTL);
+ prval("ipInReceives", ip->ipInReceives);
+ prval("ipInHdrErrors", ip->ipInHdrErrors);
+ prval("ipInAddrErrors", ip->ipInAddrErrors);
+ prval("ipInCksumErrs", ip->ipInCksumErrs);
+ prval("ipForwDatagrams", ip->ipForwDatagrams);
+ prval("ipForwProhibits", ip->ipForwProhibits);
+ prval("ipInUnknownProtos", ip->ipInUnknownProtos);
+ prval("ipInDiscards", ip->ipInDiscards);
+ prval("ipInDelivers", ip->ipInDelivers);
+ prval("ipOutRequests", ip->ipOutRequests);
+ prval("ipOutDiscards", ip->ipOutDiscards);
+ prval("ipOutNoRoutes", ip->ipOutNoRoutes);
+ pr_int_val("ipReasmTimeout", ip->ipReasmTimeout);
+ prval("ipReasmReqds", ip->ipReasmReqds);
+ prval("ipReasmOKs", ip->ipReasmOKs);
+ prval("ipReasmFails", ip->ipReasmFails);
+ prval("ipReasmDuplicates", ip->ipReasmDuplicates);
+ prval("ipReasmPartDups", ip->ipReasmPartDups);
+ prval("ipFragOKs", ip->ipFragOKs);
+ prval("ipFragFails", ip->ipFragFails);
+ prval("ipFragCreates", ip->ipFragCreates);
+ prval("ipRoutingDiscards", ip->ipRoutingDiscards);
+
+ prval("tcpInErrs", ip->tcpInErrs);
+ prval("udpNoPorts", ip->udpNoPorts);
+ prval("udpInCksumErrs", ip->udpInCksumErrs);
+ prval("udpInOverflows", ip->udpInOverflows);
+ prval("rawipInOverflows", ip->rawipInOverflows);
+ prval("ipsecInSucceeded", ip->ipsecInSucceeded);
+ prval("ipsecInFailed", ip->ipsecInFailed);
+ prval("ipInIPv6", ip->ipInIPv6);
+ prval("ipOutIPv6", ip->ipOutIPv6);
+ prval("ipOutSwitchIPv6", ip->ipOutSwitchIPv6);
+ prval_end();
+}
+
+static void
+print_icmp_stats(mib2_icmp_t *icmp)
+{
+ prval_init();
+ prval("icmpInMsgs", icmp->icmpInMsgs);
+ prval("icmpInErrors", icmp->icmpInErrors);
+ prval("icmpInCksumErrs", icmp->icmpInCksumErrs);
+ prval("icmpInUnknowns", icmp->icmpInUnknowns);
+ prval("icmpInDestUnreachs", icmp->icmpInDestUnreachs);
+ prval("icmpInTimeExcds", icmp->icmpInTimeExcds);
+ prval("icmpInParmProbs", icmp->icmpInParmProbs);
+ prval("icmpInSrcQuenchs", icmp->icmpInSrcQuenchs);
+ prval("icmpInRedirects", icmp->icmpInRedirects);
+ prval("icmpInBadRedirects", icmp->icmpInBadRedirects);
+ prval("icmpInEchos", icmp->icmpInEchos);
+ prval("icmpInEchoReps", icmp->icmpInEchoReps);
+ prval("icmpInTimestamps", icmp->icmpInTimestamps);
+ prval("icmpInTimestampReps", icmp->icmpInTimestampReps);
+ prval("icmpInAddrMasks", icmp->icmpInAddrMasks);
+ prval("icmpInAddrMaskReps", icmp->icmpInAddrMaskReps);
+ prval("icmpInFragNeeded", icmp->icmpInFragNeeded);
+ prval("icmpOutMsgs", icmp->icmpOutMsgs);
+ prval("icmpOutDrops", icmp->icmpOutDrops);
+ prval("icmpOutErrors", icmp->icmpOutErrors);
+ prval("icmpOutDestUnreachs", icmp->icmpOutDestUnreachs);
+ prval("icmpOutTimeExcds", icmp->icmpOutTimeExcds);
+ prval("icmpOutParmProbs", icmp->icmpOutParmProbs);
+ prval("icmpOutSrcQuenchs", icmp->icmpOutSrcQuenchs);
+ prval("icmpOutRedirects", icmp->icmpOutRedirects);
+ prval("icmpOutEchos", icmp->icmpOutEchos);
+ prval("icmpOutEchoReps", icmp->icmpOutEchoReps);
+ prval("icmpOutTimestamps", icmp->icmpOutTimestamps);
+ prval("icmpOutTimestampReps", icmp->icmpOutTimestampReps);
+ prval("icmpOutAddrMasks", icmp->icmpOutAddrMasks);
+ prval("icmpOutAddrMaskReps", icmp->icmpOutAddrMaskReps);
+ prval("icmpOutFragNeeded", icmp->icmpOutFragNeeded);
+ prval("icmpInOverflows", icmp->icmpInOverflows);
+ prval_end();
+}
+
+static void
+print_ip6_stats(mib2_ipv6IfStatsEntry_t *ip6)
+{
+ prval_init();
+ prval("ipv6Forwarding", ip6->ipv6Forwarding);
+ prval("ipv6DefaultHopLimit", ip6->ipv6DefaultHopLimit);
+
+ prval("ipv6InReceives", ip6->ipv6InReceives);
+ prval("ipv6InHdrErrors", ip6->ipv6InHdrErrors);
+ prval("ipv6InTooBigErrors", ip6->ipv6InTooBigErrors);
+ prval("ipv6InNoRoutes", ip6->ipv6InNoRoutes);
+ prval("ipv6InAddrErrors", ip6->ipv6InAddrErrors);
+ prval("ipv6InUnknownProtos", ip6->ipv6InUnknownProtos);
+ prval("ipv6InTruncatedPkts", ip6->ipv6InTruncatedPkts);
+ prval("ipv6InDiscards", ip6->ipv6InDiscards);
+ prval("ipv6InDelivers", ip6->ipv6InDelivers);
+ prval("ipv6OutForwDatagrams", ip6->ipv6OutForwDatagrams);
+ prval("ipv6OutRequests", ip6->ipv6OutRequests);
+ prval("ipv6OutDiscards", ip6->ipv6OutDiscards);
+ prval("ipv6OutNoRoutes", ip6->ipv6OutNoRoutes);
+ prval("ipv6OutFragOKs", ip6->ipv6OutFragOKs);
+ prval("ipv6OutFragFails", ip6->ipv6OutFragFails);
+ prval("ipv6OutFragCreates", ip6->ipv6OutFragCreates);
+ prval("ipv6ReasmReqds", ip6->ipv6ReasmReqds);
+ prval("ipv6ReasmOKs", ip6->ipv6ReasmOKs);
+ prval("ipv6ReasmFails", ip6->ipv6ReasmFails);
+ prval("ipv6InMcastPkts", ip6->ipv6InMcastPkts);
+ prval("ipv6OutMcastPkts", ip6->ipv6OutMcastPkts);
+ prval("ipv6ReasmDuplicates", ip6->ipv6ReasmDuplicates);
+ prval("ipv6ReasmPartDups", ip6->ipv6ReasmPartDups);
+ prval("ipv6ForwProhibits", ip6->ipv6ForwProhibits);
+ prval("udpInCksumErrs", ip6->udpInCksumErrs);
+ prval("udpInOverflows", ip6->udpInOverflows);
+ prval("rawipInOverflows", ip6->rawipInOverflows);
+ prval("ipv6InIPv4", ip6->ipv6InIPv4);
+ prval("ipv6OutIPv4", ip6->ipv6OutIPv4);
+ prval("ipv6OutSwitchIPv4", ip6->ipv6OutSwitchIPv4);
+ prval_end();
+}
+
+static void
+print_icmp6_stats(mib2_ipv6IfIcmpEntry_t *icmp6)
+{
+ prval_init();
+ prval("icmp6InMsgs", icmp6->ipv6IfIcmpInMsgs);
+ prval("icmp6InErrors", icmp6->ipv6IfIcmpInErrors);
+ prval("icmp6InDestUnreachs", icmp6->ipv6IfIcmpInDestUnreachs);
+ prval("icmp6InAdminProhibs", icmp6->ipv6IfIcmpInAdminProhibs);
+ prval("icmp6InTimeExcds", icmp6->ipv6IfIcmpInTimeExcds);
+ prval("icmp6InParmProblems", icmp6->ipv6IfIcmpInParmProblems);
+ prval("icmp6InPktTooBigs", icmp6->ipv6IfIcmpInPktTooBigs);
+ prval("icmp6InEchos", icmp6->ipv6IfIcmpInEchos);
+ prval("icmp6InEchoReplies", icmp6->ipv6IfIcmpInEchoReplies);
+ prval("icmp6InRouterSols", icmp6->ipv6IfIcmpInRouterSolicits);
+ prval("icmp6InRouterAds",
+ icmp6->ipv6IfIcmpInRouterAdvertisements);
+ prval("icmp6InNeighborSols", icmp6->ipv6IfIcmpInNeighborSolicits);
+ prval("icmp6InNeighborAds",
+ icmp6->ipv6IfIcmpInNeighborAdvertisements);
+ prval("icmp6InRedirects", icmp6->ipv6IfIcmpInRedirects);
+ prval("icmp6InBadRedirects", icmp6->ipv6IfIcmpInBadRedirects);
+ prval("icmp6InGroupQueries", icmp6->ipv6IfIcmpInGroupMembQueries);
+ prval("icmp6InGroupResps", icmp6->ipv6IfIcmpInGroupMembResponses);
+ prval("icmp6InGroupReds", icmp6->ipv6IfIcmpInGroupMembReductions);
+ prval("icmp6InOverflows", icmp6->ipv6IfIcmpInOverflows);
+ prval_end();
+ prval_init();
+ prval("icmp6OutMsgs", icmp6->ipv6IfIcmpOutMsgs);
+ prval("icmp6OutErrors", icmp6->ipv6IfIcmpOutErrors);
+ prval("icmp6OutDestUnreachs", icmp6->ipv6IfIcmpOutDestUnreachs);
+ prval("icmp6OutAdminProhibs", icmp6->ipv6IfIcmpOutAdminProhibs);
+ prval("icmp6OutTimeExcds", icmp6->ipv6IfIcmpOutTimeExcds);
+ prval("icmp6OutParmProblems", icmp6->ipv6IfIcmpOutParmProblems);
+ prval("icmp6OutPktTooBigs", icmp6->ipv6IfIcmpOutPktTooBigs);
+ prval("icmp6OutEchos", icmp6->ipv6IfIcmpOutEchos);
+ prval("icmp6OutEchoReplies", icmp6->ipv6IfIcmpOutEchoReplies);
+ prval("icmp6OutRouterSols", icmp6->ipv6IfIcmpOutRouterSolicits);
+ prval("icmp6OutRouterAds",
+ icmp6->ipv6IfIcmpOutRouterAdvertisements);
+ prval("icmp6OutNeighborSols", icmp6->ipv6IfIcmpOutNeighborSolicits);
+ prval("icmp6OutNeighborAds",
+ icmp6->ipv6IfIcmpOutNeighborAdvertisements);
+ prval("icmp6OutRedirects", icmp6->ipv6IfIcmpOutRedirects);
+ prval("icmp6OutGroupQueries", icmp6->ipv6IfIcmpOutGroupMembQueries);
+ prval("icmp6OutGroupResps",
+ icmp6->ipv6IfIcmpOutGroupMembResponses);
+ prval("icmp6OutGroupReds",
+ icmp6->ipv6IfIcmpOutGroupMembReductions);
+ prval_end();
+}
+
+static void
+print_sctp_stats(mib2_sctp_t *sctp)
+{
+ prval_init();
+ pr_sctp_rtoalgo("sctpRtoAlgorithm", sctp->sctpRtoAlgorithm);
+ prval("sctpRtoMin", sctp->sctpRtoMin);
+ prval("sctpRtoMax", sctp->sctpRtoMax);
+ prval("sctpRtoInitial", sctp->sctpRtoInitial);
+ pr_int_val("sctpMaxAssocs", sctp->sctpMaxAssocs);
+ prval("sctpValCookieLife", sctp->sctpValCookieLife);
+ prval("sctpMaxInitRetr", sctp->sctpMaxInitRetr);
+ prval("sctpCurrEstab", sctp->sctpCurrEstab);
+ prval("sctpActiveEstab", sctp->sctpActiveEstab);
+ prval("sctpPassiveEstab", sctp->sctpPassiveEstab);
+ prval("sctpAborted", sctp->sctpAborted);
+ prval("sctpShutdowns", sctp->sctpShutdowns);
+ prval("sctpOutOfBlue", sctp->sctpOutOfBlue);
+ prval("sctpChecksumError", sctp->sctpChecksumError);
+ prval64("sctpOutCtrlChunks", sctp->sctpOutCtrlChunks);
+ prval64("sctpOutOrderChunks", sctp->sctpOutOrderChunks);
+ prval64("sctpOutUnorderChunks", sctp->sctpOutUnorderChunks);
+ prval64("sctpRetransChunks", sctp->sctpRetransChunks);
+ prval("sctpOutAck", sctp->sctpOutAck);
+ prval("sctpOutAckDelayed", sctp->sctpOutAckDelayed);
+ prval("sctpOutWinUpdate", sctp->sctpOutWinUpdate);
+ prval("sctpOutFastRetrans", sctp->sctpOutFastRetrans);
+ prval("sctpOutWinProbe", sctp->sctpOutWinProbe);
+ prval64("sctpInCtrlChunks", sctp->sctpInCtrlChunks);
+ prval64("sctpInOrderChunks", sctp->sctpInOrderChunks);
+ prval64("sctpInUnorderChunks", sctp->sctpInUnorderChunks);
+ prval("sctpInAck", sctp->sctpInAck);
+ prval("sctpInDupAck", sctp->sctpInDupAck);
+ prval("sctpInAckUnsent", sctp->sctpInAckUnsent);
+ prval64("sctpFragUsrMsgs", sctp->sctpFragUsrMsgs);
+ prval64("sctpReasmUsrMsgs", sctp->sctpReasmUsrMsgs);
+ prval64("sctpOutSCTPPkts", sctp->sctpOutSCTPPkts);
+ prval64("sctpInSCTPPkts", sctp->sctpInSCTPPkts);
+ prval("sctpInInvalidCookie", sctp->sctpInInvalidCookie);
+ prval("sctpTimRetrans", sctp->sctpTimRetrans);
+ prval("sctpTimRetransDrop", sctp->sctpTimRetransDrop);
+ prval("sctpTimHearBeatProbe", sctp->sctpTimHeartBeatProbe);
+ prval("sctpTimHearBeatDrop", sctp->sctpTimHeartBeatDrop);
+ prval("sctpListenDrop", sctp->sctpListenDrop);
+ prval("sctpInClosed", sctp->sctpInClosed);
+ prval_end();
+}
+
+static void
+print_tcp_stats(mib2_tcp_t *tcp)
+{
+ prval_init();
+ pr_int_val("tcpRtoAlgorithm", tcp->tcpRtoAlgorithm);
+ pr_int_val("tcpRtoMin", tcp->tcpRtoMin);
+ pr_int_val("tcpRtoMax", tcp->tcpRtoMax);
+ pr_int_val("tcpMaxConn", tcp->tcpMaxConn);
+ prval("tcpActiveOpens", tcp->tcpActiveOpens);
+ prval("tcpPassiveOpens", tcp->tcpPassiveOpens);
+ prval("tcpAttemptFails", tcp->tcpAttemptFails);
+ prval("tcpEstabResets", tcp->tcpEstabResets);
+ prval("tcpCurrEstab", tcp->tcpCurrEstab);
+ prval("tcpOutSegs", tcp->tcpOutSegs);
+ prval("tcpOutDataSegs", tcp->tcpOutDataSegs);
+ prval("tcpOutDataBytes", tcp->tcpOutDataBytes);
+ prval("tcpRetransSegs", tcp->tcpRetransSegs);
+ prval("tcpRetransBytes", tcp->tcpRetransBytes);
+ prval("tcpOutAck", tcp->tcpOutAck);
+ prval("tcpOutAckDelayed", tcp->tcpOutAckDelayed);
+ prval("tcpOutUrg", tcp->tcpOutUrg);
+ prval("tcpOutWinUpdate", tcp->tcpOutWinUpdate);
+ prval("tcpOutWinProbe", tcp->tcpOutWinProbe);
+ prval("tcpOutControl", tcp->tcpOutControl);
+ prval("tcpOutRsts", tcp->tcpOutRsts);
+ prval("tcpOutFastRetrans", tcp->tcpOutFastRetrans);
+ prval("tcpInSegs", tcp->tcpInSegs);
+ prval_end();
+ prval("tcpInAckSegs", tcp->tcpInAckSegs);
+ prval("tcpInAckBytes", tcp->tcpInAckBytes);
+ prval("tcpInDupAck", tcp->tcpInDupAck);
+ prval("tcpInAckUnsent", tcp->tcpInAckUnsent);
+ prval("tcpInInorderSegs", tcp->tcpInDataInorderSegs);
+ prval("tcpInInorderBytes", tcp->tcpInDataInorderBytes);
+ prval("tcpInUnorderSegs", tcp->tcpInDataUnorderSegs);
+ prval("tcpInUnorderBytes", tcp->tcpInDataUnorderBytes);
+ prval("tcpInDupSegs", tcp->tcpInDataDupSegs);
+ prval("tcpInDupBytes", tcp->tcpInDataDupBytes);
+ prval("tcpInPartDupSegs", tcp->tcpInDataPartDupSegs);
+ prval("tcpInPartDupBytes", tcp->tcpInDataPartDupBytes);
+ prval("tcpInPastWinSegs", tcp->tcpInDataPastWinSegs);
+ prval("tcpInPastWinBytes", tcp->tcpInDataPastWinBytes);
+ prval("tcpInWinProbe", tcp->tcpInWinProbe);
+ prval("tcpInWinUpdate", tcp->tcpInWinUpdate);
+ prval("tcpInClosed", tcp->tcpInClosed);
+ prval("tcpRttNoUpdate", tcp->tcpRttNoUpdate);
+ prval("tcpRttUpdate", tcp->tcpRttUpdate);
+ prval("tcpTimRetrans", tcp->tcpTimRetrans);
+ prval("tcpTimRetransDrop", tcp->tcpTimRetransDrop);
+ prval("tcpTimKeepalive", tcp->tcpTimKeepalive);
+ prval("tcpTimKeepaliveProbe", tcp->tcpTimKeepaliveProbe);
+ prval("tcpTimKeepaliveDrop", tcp->tcpTimKeepaliveDrop);
+ prval("tcpListenDrop", tcp->tcpListenDrop);
+ prval("tcpListenDropQ0", tcp->tcpListenDropQ0);
+ prval("tcpHalfOpenDrop", tcp->tcpHalfOpenDrop);
+ prval("tcpOutSackRetrans", tcp->tcpOutSackRetransSegs);
+ prval_end();
+
+}
+
+static void
+print_udp_stats(mib2_udp_t *udp)
+{
+ prval_init();
+ prval("udpInDatagrams", udp->udpInDatagrams);
+ prval("udpInErrors", udp->udpInErrors);
+ prval("udpOutDatagrams", udp->udpOutDatagrams);
+ prval("udpOutErrors", udp->udpOutErrors);
+ prval_end();
+}
+
+static void
+print_rawip_stats(mib2_rawip_t *rawip)
+{
+ prval_init();
+ prval("rawipInDatagrams", rawip->rawipInDatagrams);
+ prval("rawipInErrors", rawip->rawipInErrors);
+ prval("rawipInCksumErrs", rawip->rawipInCksumErrs);
+ prval("rawipOutDatagrams", rawip->rawipOutDatagrams);
+ prval("rawipOutErrors", rawip->rawipOutErrors);
+ prval_end();
+}
+
+void
+print_igmp_stats(struct igmpstat *igps)
+{
+ (void) printf(" %10u message%s received\n",
+ igps->igps_rcv_total, PLURAL(igps->igps_rcv_total));
+ (void) printf(" %10u message%s received with too few bytes\n",
+ igps->igps_rcv_tooshort, PLURAL(igps->igps_rcv_tooshort));
+ (void) printf(" %10u message%s received with bad checksum\n",
+ igps->igps_rcv_badsum, PLURAL(igps->igps_rcv_badsum));
+ (void) printf(" %10u membership quer%s received\n",
+ igps->igps_rcv_queries, PLURALY(igps->igps_rcv_queries));
+ (void) printf(" %10u membership quer%s received with invalid "
+ "field(s)\n",
+ igps->igps_rcv_badqueries, PLURALY(igps->igps_rcv_badqueries));
+ (void) printf(" %10u membership report%s received\n",
+ igps->igps_rcv_reports, PLURAL(igps->igps_rcv_reports));
+ (void) printf(" %10u membership report%s received with invalid "
+ "field(s)\n",
+ igps->igps_rcv_badreports, PLURAL(igps->igps_rcv_badreports));
+ (void) printf(" %10u membership report%s received for groups to "
+ "which we belong\n",
+ igps->igps_rcv_ourreports, PLURAL(igps->igps_rcv_ourreports));
+ (void) printf(" %10u membership report%s sent\n",
+ igps->igps_snd_reports, PLURAL(igps->igps_snd_reports));
+}
+
+static void
+print_mrt_stats(struct mrtstat *mrts)
+{
+ (void) puts("DVMRP multicast routing:");
+ (void) printf(" %10u hit%s - kernel forwarding cache hits\n",
+ mrts->mrts_mfc_hits, PLURAL(mrts->mrts_mfc_hits));
+ (void) printf(" %10u miss%s - kernel forwarding cache misses\n",
+ mrts->mrts_mfc_misses, PLURALES(mrts->mrts_mfc_misses));
+ (void) printf(" %10u packet%s potentially forwarded\n",
+ mrts->mrts_fwd_in, PLURAL(mrts->mrts_fwd_in));
+ (void) printf(" %10u packet%s actually sent out\n",
+ mrts->mrts_fwd_out, PLURAL(mrts->mrts_fwd_out));
+ (void) printf(" %10u upcall%s - upcalls made to mrouted\n",
+ mrts->mrts_upcalls, PLURAL(mrts->mrts_upcalls));
+ (void) printf(" %10u packet%s not sent out due to lack of resources\n",
+ mrts->mrts_fwd_drop, PLURAL(mrts->mrts_fwd_drop));
+ (void) printf(" %10u datagram%s with malformed tunnel options\n",
+ mrts->mrts_bad_tunnel, PLURAL(mrts->mrts_bad_tunnel));
+ (void) printf(" %10u datagram%s with no room for tunnel options\n",
+ mrts->mrts_cant_tunnel, PLURAL(mrts->mrts_cant_tunnel));
+ (void) printf(" %10u datagram%s arrived on wrong interface\n",
+ mrts->mrts_wrong_if, PLURAL(mrts->mrts_wrong_if));
+ (void) printf(" %10u datagram%s dropped due to upcall Q overflow\n",
+ mrts->mrts_upq_ovflw, PLURAL(mrts->mrts_upq_ovflw));
+ (void) printf(" %10u datagram%s cleaned up by the cache\n",
+ mrts->mrts_cache_cleanups, PLURAL(mrts->mrts_cache_cleanups));
+ (void) printf(" %10u datagram%s dropped selectively by ratelimiter\n",
+ mrts->mrts_drop_sel, PLURAL(mrts->mrts_drop_sel));
+ (void) printf(" %10u datagram%s dropped - bucket Q overflow\n",
+ mrts->mrts_q_overflow, PLURAL(mrts->mrts_q_overflow));
+ (void) printf(" %10u datagram%s dropped - larger than bkt size\n",
+ mrts->mrts_pkt2large, PLURAL(mrts->mrts_pkt2large));
+ (void) printf("\nPIM multicast routing:\n");
+ (void) printf(" %10u datagram%s dropped - bad version number\n",
+ mrts->mrts_pim_badversion, PLURAL(mrts->mrts_pim_badversion));
+ (void) printf(" %10u datagram%s dropped - bad checksum\n",
+ mrts->mrts_pim_rcv_badcsum, PLURAL(mrts->mrts_pim_rcv_badcsum));
+ (void) printf(" %10u datagram%s dropped - bad register packets\n",
+ mrts->mrts_pim_badregisters,
+ PLURAL(mrts->mrts_pim_badregisters));
+ (void) printf(
+ " %10u datagram%s potentially forwarded - register packets\n",
+ mrts->mrts_pim_regforwards, PLURAL(mrts->mrts_pim_regforwards));
+ (void) printf(" %10u datagram%s dropped - register send drops\n",
+ mrts->mrts_pim_regsend_drops,
+ PLURAL(mrts->mrts_pim_regsend_drops));
+ (void) printf(" %10u datagram%s dropped - packet malformed\n",
+ mrts->mrts_pim_malformed, PLURAL(mrts->mrts_pim_malformed));
+ (void) printf(" %10u datagram%s dropped - no memory to forward\n",
+ mrts->mrts_pim_nomemory, PLURAL(mrts->mrts_pim_nomemory));
+}
+
+static void
+sum_ip6_stats(mib2_ipv6IfStatsEntry_t *ip6, mib2_ipv6IfStatsEntry_t *sum6)
+{
+ /* First few are not additive */
+ sum6->ipv6Forwarding = ip6->ipv6Forwarding;
+ sum6->ipv6DefaultHopLimit = ip6->ipv6DefaultHopLimit;
+
+ sum6->ipv6InReceives += ip6->ipv6InReceives;
+ sum6->ipv6InHdrErrors += ip6->ipv6InHdrErrors;
+ sum6->ipv6InTooBigErrors += ip6->ipv6InTooBigErrors;
+ sum6->ipv6InNoRoutes += ip6->ipv6InNoRoutes;
+ sum6->ipv6InAddrErrors += ip6->ipv6InAddrErrors;
+ sum6->ipv6InUnknownProtos += ip6->ipv6InUnknownProtos;
+ sum6->ipv6InTruncatedPkts += ip6->ipv6InTruncatedPkts;
+ sum6->ipv6InDiscards += ip6->ipv6InDiscards;
+ sum6->ipv6InDelivers += ip6->ipv6InDelivers;
+ sum6->ipv6OutForwDatagrams += ip6->ipv6OutForwDatagrams;
+ sum6->ipv6OutRequests += ip6->ipv6OutRequests;
+ sum6->ipv6OutDiscards += ip6->ipv6OutDiscards;
+ sum6->ipv6OutFragOKs += ip6->ipv6OutFragOKs;
+ sum6->ipv6OutFragFails += ip6->ipv6OutFragFails;
+ sum6->ipv6OutFragCreates += ip6->ipv6OutFragCreates;
+ sum6->ipv6ReasmReqds += ip6->ipv6ReasmReqds;
+ sum6->ipv6ReasmOKs += ip6->ipv6ReasmOKs;
+ sum6->ipv6ReasmFails += ip6->ipv6ReasmFails;
+ sum6->ipv6InMcastPkts += ip6->ipv6InMcastPkts;
+ sum6->ipv6OutMcastPkts += ip6->ipv6OutMcastPkts;
+ sum6->ipv6OutNoRoutes += ip6->ipv6OutNoRoutes;
+ sum6->ipv6ReasmDuplicates += ip6->ipv6ReasmDuplicates;
+ sum6->ipv6ReasmPartDups += ip6->ipv6ReasmPartDups;
+ sum6->ipv6ForwProhibits += ip6->ipv6ForwProhibits;
+ sum6->udpInCksumErrs += ip6->udpInCksumErrs;
+ sum6->udpInOverflows += ip6->udpInOverflows;
+ sum6->rawipInOverflows += ip6->rawipInOverflows;
+}
+
+static void
+sum_icmp6_stats(mib2_ipv6IfIcmpEntry_t *icmp6, mib2_ipv6IfIcmpEntry_t *sum6)
+{
+ sum6->ipv6IfIcmpInMsgs += icmp6->ipv6IfIcmpInMsgs;
+ sum6->ipv6IfIcmpInErrors += icmp6->ipv6IfIcmpInErrors;
+ sum6->ipv6IfIcmpInDestUnreachs += icmp6->ipv6IfIcmpInDestUnreachs;
+ sum6->ipv6IfIcmpInAdminProhibs += icmp6->ipv6IfIcmpInAdminProhibs;
+ sum6->ipv6IfIcmpInTimeExcds += icmp6->ipv6IfIcmpInTimeExcds;
+ sum6->ipv6IfIcmpInParmProblems += icmp6->ipv6IfIcmpInParmProblems;
+ sum6->ipv6IfIcmpInPktTooBigs += icmp6->ipv6IfIcmpInPktTooBigs;
+ sum6->ipv6IfIcmpInEchos += icmp6->ipv6IfIcmpInEchos;
+ sum6->ipv6IfIcmpInEchoReplies += icmp6->ipv6IfIcmpInEchoReplies;
+ sum6->ipv6IfIcmpInRouterSolicits += icmp6->ipv6IfIcmpInRouterSolicits;
+ sum6->ipv6IfIcmpInRouterAdvertisements +=
+ icmp6->ipv6IfIcmpInRouterAdvertisements;
+ sum6->ipv6IfIcmpInNeighborSolicits +=
+ icmp6->ipv6IfIcmpInNeighborSolicits;
+ sum6->ipv6IfIcmpInNeighborAdvertisements +=
+ icmp6->ipv6IfIcmpInNeighborAdvertisements;
+ sum6->ipv6IfIcmpInRedirects += icmp6->ipv6IfIcmpInRedirects;
+ sum6->ipv6IfIcmpInGroupMembQueries +=
+ icmp6->ipv6IfIcmpInGroupMembQueries;
+ sum6->ipv6IfIcmpInGroupMembResponses +=
+ icmp6->ipv6IfIcmpInGroupMembResponses;
+ sum6->ipv6IfIcmpInGroupMembReductions +=
+ icmp6->ipv6IfIcmpInGroupMembReductions;
+ sum6->ipv6IfIcmpOutMsgs += icmp6->ipv6IfIcmpOutMsgs;
+ sum6->ipv6IfIcmpOutErrors += icmp6->ipv6IfIcmpOutErrors;
+ sum6->ipv6IfIcmpOutDestUnreachs += icmp6->ipv6IfIcmpOutDestUnreachs;
+ sum6->ipv6IfIcmpOutAdminProhibs += icmp6->ipv6IfIcmpOutAdminProhibs;
+ sum6->ipv6IfIcmpOutTimeExcds += icmp6->ipv6IfIcmpOutTimeExcds;
+ sum6->ipv6IfIcmpOutParmProblems += icmp6->ipv6IfIcmpOutParmProblems;
+ sum6->ipv6IfIcmpOutPktTooBigs += icmp6->ipv6IfIcmpOutPktTooBigs;
+ sum6->ipv6IfIcmpOutEchos += icmp6->ipv6IfIcmpOutEchos;
+ sum6->ipv6IfIcmpOutEchoReplies += icmp6->ipv6IfIcmpOutEchoReplies;
+ sum6->ipv6IfIcmpOutRouterSolicits +=
+ icmp6->ipv6IfIcmpOutRouterSolicits;
+ sum6->ipv6IfIcmpOutRouterAdvertisements +=
+ icmp6->ipv6IfIcmpOutRouterAdvertisements;
+ sum6->ipv6IfIcmpOutNeighborSolicits +=
+ icmp6->ipv6IfIcmpOutNeighborSolicits;
+ sum6->ipv6IfIcmpOutNeighborAdvertisements +=
+ icmp6->ipv6IfIcmpOutNeighborAdvertisements;
+ sum6->ipv6IfIcmpOutRedirects += icmp6->ipv6IfIcmpOutRedirects;
+ sum6->ipv6IfIcmpOutGroupMembQueries +=
+ icmp6->ipv6IfIcmpOutGroupMembQueries;
+ sum6->ipv6IfIcmpOutGroupMembResponses +=
+ icmp6->ipv6IfIcmpOutGroupMembResponses;
+ sum6->ipv6IfIcmpOutGroupMembReductions +=
+ icmp6->ipv6IfIcmpOutGroupMembReductions;
+ sum6->ipv6IfIcmpInOverflows += icmp6->ipv6IfIcmpInOverflows;
+}
+
+/* ----------------------------- MRT_STAT_REPORT --------------------------- */
+
+static void
+mrt_stat_report(mib_item_t *curritem)
+{
+ int jtemp = 0;
+ mib_item_t *tempitem;
+
+ if (!(family_selected(AF_INET)))
+ return;
+
+ (void) putchar('\n');
+ /* 'for' loop 1: */
+ for (tempitem = curritem;
+ tempitem;
+ tempitem = tempitem->next_item) {
+ if (Dflag) {
+ (void) printf("\n--- Entry %d ---\n", ++jtemp);
+ (void) printf("Group = %d, mib_id = %d, "
+ "length = %d, valp = 0x%p\n",
+ tempitem->group, tempitem->mib_id,
+ tempitem->length, tempitem->valp);
+ }
+
+ if (tempitem->mib_id == 0) {
+ switch (tempitem->group) {
+ case EXPER_DVMRP: {
+ struct mrtstat *mrts;
+ mrts = (struct mrtstat *)tempitem->valp;
+
+ if (!(family_selected(AF_INET)))
+ continue; /* 'for' loop 1 */
+
+ print_mrt_stats(mrts);
+ break;
+ }
+ }
+ }
+ } /* 'for' loop 1 ends */
+ (void) putchar('\n');
+ (void) fflush(stdout);
+}
+
+/*
+ * if_stat_total() - Computes totals for interface statistics
+ * and returns result by updating sumstats.
+ */
+static void
+if_stat_total(struct ifstat *oldstats, struct ifstat *newstats,
+ struct ifstat *sumstats)
+{
+ sumstats->ipackets += newstats->ipackets - oldstats->ipackets;
+ sumstats->opackets += newstats->opackets - oldstats->opackets;
+ sumstats->ierrors += newstats->ierrors - oldstats->ierrors;
+ sumstats->oerrors += newstats->oerrors - oldstats->oerrors;
+ sumstats->collisions += newstats->collisions - oldstats->collisions;
+}
+
+/* --------------------- IF_REPORT (netstat -i) -------------------------- */
+
+static struct ifstat zerostat = {
+ 0LL, 0LL, 0LL, 0LL, 0LL
+};
+
+static void
+if_report(mib_item_t *item, char *matchname,
+ int Iflag_only, boolean_t once_only)
+{
+ static boolean_t reentry = B_FALSE;
+ boolean_t alreadydone = B_FALSE;
+ int jtemp = 0;
+ uint32_t ifindex_v4 = 0;
+ uint32_t ifindex_v6 = 0;
+
+ /* 'for' loop 1: */
+ for (; item; item = item->next_item) {
+ if (Dflag) {
+ (void) printf("\n--- Entry %d ---\n", ++jtemp);
+ (void) printf("Group = %d, mib_id = %d, "
+ "length = %d, valp = 0x%p\n",
+ item->group, item->mib_id, item->length,
+ item->valp);
+ }
+
+ switch (item->group) {
+ case MIB2_IP:
+ if (item->mib_id != MIB2_IP_ADDR ||
+ !family_selected(AF_INET))
+ continue; /* 'for' loop 1 */
+ {
+ static struct ifstat old = {0L, 0L, 0L, 0L, 0L};
+ static struct ifstat new = {0L, 0L, 0L, 0L, 0L};
+ struct ifstat sum;
+ struct iflist *newlist = NULL;
+ static struct iflist *oldlist = NULL;
+ kstat_t *ksp;
+
+ if (once_only) {
+ char ifname[LIFNAMSIZ + 1];
+ char logintname[LIFNAMSIZ + 1];
+ mib2_ipAddrEntry_t *ap;
+ struct ifstat stat = {0L, 0L, 0L, 0L, 0L};
+ boolean_t first = B_TRUE;
+ uint32_t new_ifindex;
+
+ if (Dflag)
+ (void) printf("if_report: %d items\n",
+ (item->length)
+ / sizeof (mib2_ipAddrEntry_t));
+
+ /* 'for' loop 2a: */
+ for (ap = (mib2_ipAddrEntry_t *)item->valp;
+ (char *)ap < (char *)item->valp
+ + item->length;
+ ap++) {
+ (void) octetstr(&ap->ipAdEntIfIndex,
+ 'a', logintname,
+ sizeof (logintname));
+ (void) strcpy(ifname, logintname);
+ (void) strtok(ifname, ":");
+ if (matchname != NULL &&
+ strcmp(matchname, ifname) != 0 &&
+ strcmp(matchname, logintname) != 0)
+ continue; /* 'for' loop 2a */
+ new_ifindex =
+ if_nametoindex(logintname);
+ if (new_ifindex != ifindex_v4 &&
+ (ksp = kstat_lookup(kc, NULL, -1,
+ ifname)) != NULL) {
+ (void) safe_kstat_read(kc, ksp,
+ NULL);
+ stat.ipackets =
+ kstat_named_value(ksp,
+ "ipackets");
+ stat.ierrors =
+ kstat_named_value(ksp,
+ "ierrors");
+ stat.opackets =
+ kstat_named_value(ksp,
+ "opackets");
+ stat.oerrors =
+ kstat_named_value(ksp,
+ "oerrors");
+ stat.collisions =
+ kstat_named_value(ksp,
+ "collisions");
+ if (first) {
+ (void) printf(
+ "%-5.5s %-5.5s%-13.13s "
+ "%-14.14s %-6.6s %-5.5s "
+ "%-6.6s %-5.5s %-6.6s "
+ "%-6.6s\n",
+ "Name", "Mtu", "Net/Dest",
+ "Address", "Ipkts",
+ "Ierrs", "Opkts", "Oerrs",
+ "Collis", "Queue");
+ first = B_FALSE;
+ }
+ if_report_ip4(ap, ifname,
+ logintname, &stat, B_TRUE);
+ ifindex_v4 = new_ifindex;
+ } else {
+ if_report_ip4(ap, ifname,
+ logintname, &stat, B_FALSE);
+ }
+ } /* 'for' loop 2a ends */
+ if (!first)
+ (void) putchar('\n');
+ } else if (!alreadydone) {
+ char ifname[LIFNAMSIZ + 1];
+ char buf[LIFNAMSIZ + 1];
+ mib2_ipAddrEntry_t *ap;
+ struct ifstat t;
+ struct iflist *tlp;
+ struct iflist **nextnew = &newlist;
+ struct iflist *walkold;
+ struct iflist *cleanlist;
+
+ alreadydone = B_TRUE; /* ignore other case */
+ /*
+ * 'for' loop 2b: find the "right" entry
+ */
+ for (ap = (mib2_ipAddrEntry_t *)item->valp;
+ (char *)ap < (char *)item->valp
+ + item->length;
+ ap++) {
+ (void) octetstr(&ap->ipAdEntIfIndex,
+ 'a', ifname, sizeof (ifname));
+ (void) strtok(ifname, ":");
+
+ if (matchname) {
+ if (strcmp(matchname,
+ ifname) == 0)
+ /* 'for' loop 2b */
+ break;
+ } else if (strcmp(ifname, "lo0") != 0) {
+ matchname = ifname;
+ break; /* 'for' loop 2b */
+ }
+ } /* 'for' loop 2b ends */
+
+ if (Iflag_only == 0 || !reentry) {
+ (void) printf(" input %-6.6s "
+ "output ",
+ matchname);
+ (void) printf(" input (Total) "
+ "output\n");
+ (void) printf("%-7.7s %-5.5s %-7.7s "
+ "%-5.5s %-6.6s ",
+ "packets", "errs", "packets",
+ "errs", "colls");
+ (void) printf("%-7.7s %-5.5s %-7.7s "
+ "%-5.5s %-6.6s\n",
+ "packets", "errs", "packets",
+ "errs", "colls");
+ }
+
+ sum = zerostat;
+
+ /* 'for' loop 2c: */
+ for (ap = (mib2_ipAddrEntry_t *)item->valp;
+ (char *)ap < (char *)item->valp
+ + item->length;
+ ap++) {
+ (void) octetstr(&ap->ipAdEntIfIndex,
+ 'a', buf, sizeof (buf));
+ (void) strtok(buf, ":");
+ ksp = kstat_lookup(kc, NULL, -1, buf);
+ if (ksp &&
+ ksp->ks_type == KSTAT_TYPE_NAMED)
+ (void) safe_kstat_read(kc, ksp,
+ NULL);
+
+ t.ipackets = kstat_named_value(ksp,
+ "ipackets");
+ t.ierrors = kstat_named_value(ksp,
+ "ierrors");
+ t.opackets = kstat_named_value(ksp,
+ "opackets");
+ t.oerrors = kstat_named_value(ksp,
+ "oerrors");
+ t.collisions = kstat_named_value(ksp,
+ "collisions");
+
+ if (strcmp(buf, matchname) == 0)
+ new = t;
+
+ /* Build the interface list */
+
+ tlp = malloc(sizeof (struct iflist));
+ (void) strlcpy(tlp->ifname, buf,
+ sizeof (tlp->ifname));
+ tlp->tot = t;
+ *nextnew = tlp;
+ nextnew = &tlp->next_if;
+
+ /*
+ * First time through.
+ * Just add up the interface stats.
+ */
+
+ if (oldlist == NULL) {
+ if_stat_total(&zerostat,
+ &t, &sum);
+ continue;
+ }
+
+ /*
+ * Walk old list for the interface.
+ *
+ * If found, add difference to total.
+ *
+ * If not, an interface has been plumbed
+ * up. In this case, we will simply
+ * ignore the new interface until the
+ * next interval; as there's no easy way
+ * to acquire statistics between time
+ * of the plumb and the next interval
+ * boundary. This results in inaccurate
+ * total values for current interval.
+ *
+ * Note the case when an interface is
+ * unplumbed; as similar problems exist.
+ * The unplumbed interface is not in the
+ * current list, and there's no easy way
+ * to account for the statistics between
+ * the previous interval and time of the
+ * unplumb. Therefore, we (in a sense)
+ * ignore the removed interface by only
+ * involving "current" interfaces when
+ * computing the total statistics.
+ * Unfortunately, this also results in
+ * inaccurate values for interval total.
+ */
+
+ for (walkold = oldlist;
+ walkold != NULL;
+ walkold = walkold->next_if) {
+ if (strcmp(walkold->ifname,
+ buf) == 0) {
+ if_stat_total(
+ &walkold->tot,
+ &t, &sum);
+ break;
+ }
+ }
+
+ } /* 'for' loop 2c ends */
+
+ *nextnew = NULL;
+
+ (void) printf("%-7llu %-5llu %-7llu "
+ "%-5llu %-6llu ",
+ new.ipackets - old.ipackets,
+ new.ierrors - old.ierrors,
+ new.opackets - old.opackets,
+ new.oerrors - old.oerrors,
+ new.collisions - old.collisions);
+
+ (void) printf("%-7llu %-5llu %-7llu "
+ "%-5llu %-6llu\n", sum.ipackets,
+ sum.ierrors, sum.opackets,
+ sum.oerrors, sum.collisions);
+
+ /*
+ * Tidy things up once finished.
+ */
+
+ old = new;
+ cleanlist = oldlist;
+ oldlist = newlist;
+ while (cleanlist != NULL) {
+ tlp = cleanlist->next_if;
+ free(cleanlist);
+ cleanlist = tlp;
+ }
+ }
+ break;
+ }
+ case MIB2_IP6:
+ if (item->mib_id != MIB2_IP6_ADDR ||
+ !family_selected(AF_INET6))
+ continue; /* 'for' loop 1 */
+ {
+ static struct ifstat old6 = {0L, 0L, 0L, 0L, 0L};
+ static struct ifstat new6 = {0L, 0L, 0L, 0L, 0L};
+ struct ifstat sum6;
+ struct iflist *newlist6 = NULL;
+ static struct iflist *oldlist6 = NULL;
+ kstat_t *ksp;
+
+ if (once_only) {
+ char ifname[LIFNAMSIZ + 1];
+ char logintname[LIFNAMSIZ + 1];
+ mib2_ipv6AddrEntry_t *ap6;
+ struct ifstat stat = {0L, 0L, 0L, 0L, 0L};
+ boolean_t first = B_TRUE;
+ uint32_t new_ifindex;
+
+ if (Dflag)
+ (void) printf("if_report: %d items\n",
+ (item->length)
+ / sizeof (mib2_ipv6AddrEntry_t));
+ /* 'for' loop 2d: */
+ for (ap6 = (mib2_ipv6AddrEntry_t *)item->valp;
+ (char *)ap6 < (char *)item->valp
+ + item->length;
+ ap6++) {
+ (void) octetstr(&ap6->ipv6AddrIfIndex,
+ 'a', logintname,
+ sizeof (logintname));
+ (void) strcpy(ifname, logintname);
+ (void) strtok(ifname, ":");
+ if (matchname != NULL &&
+ strcmp(matchname, ifname) != 0 &&
+ strcmp(matchname, logintname) != 0)
+ continue; /* 'for' loop 2d */
+ new_ifindex =
+ if_nametoindex(logintname);
+ if (new_ifindex != ifindex_v6 &&
+ (ksp = kstat_lookup(kc, NULL, -1,
+ ifname)) != NULL) {
+ (void) safe_kstat_read(kc, ksp,
+ NULL);
+ stat.ipackets =
+ kstat_named_value(ksp,
+ "ipackets");
+ stat.ierrors =
+ kstat_named_value(ksp,
+ "ierrors");
+ stat.opackets =
+ kstat_named_value(ksp,
+ "opackets");
+ stat.oerrors =
+ kstat_named_value(ksp,
+ "oerrors");
+ stat.collisions =
+ kstat_named_value(ksp,
+ "collisions");
+ if (first) {
+ (void) printf(
+ "%-5.5s %-5.5s%"
+ "-27.27s %-27.27s "
+ "%-6.6s %-5.5s "
+ "%-6.6s %-5.5s "
+ "%-6.6s\n",
+ "Name", "Mtu",
+ "Net/Dest",
+ "Address", "Ipkts",
+ "Ierrs", "Opkts",
+ "Oerrs", "Collis");
+ first = B_FALSE;
+ }
+ if_report_ip6(ap6, ifname,
+ logintname, &stat, B_TRUE);
+ ifindex_v6 = new_ifindex;
+ } else {
+ if_report_ip6(ap6, ifname,
+ logintname, &stat, B_FALSE);
+ }
+ } /* 'for' loop 2d ends */
+ if (!first)
+ (void) putchar('\n');
+ } else if (!alreadydone) {
+ char ifname[LIFNAMSIZ + 1];
+ char buf[IFNAMSIZ + 1];
+ mib2_ipv6AddrEntry_t *ap6;
+ struct ifstat t;
+ struct iflist *tlp;
+ struct iflist **nextnew = &newlist6;
+ struct iflist *walkold;
+ struct iflist *cleanlist;
+
+ alreadydone = B_TRUE; /* ignore other case */
+ /*
+ * 'for' loop 2e: find the "right" entry
+ */
+ for (ap6 = (mib2_ipv6AddrEntry_t *)item->valp;
+ (char *)ap6 < (char *)item->valp
+ + item->length;
+ ap6++) {
+ (void) octetstr(&ap6->ipv6AddrIfIndex,
+ 'a', ifname, sizeof (ifname));
+ (void) strtok(ifname, ":");
+
+ if (matchname) {
+ if (strcmp(matchname, ifname) ==
+ 0)
+ /* 'for' loop 2e */
+ break;
+ } else if (strcmp(ifname, "lo0") != 0) {
+ matchname = ifname;
+ break; /* 'for' loop 2e */
+ }
+ } /* 'for' loop 2e ends */
+
+ if (Iflag_only == 0 || !reentry) {
+ (void) printf(
+ " input %-6.6s"
+ " output ",
+ matchname);
+ (void) printf(" input (Total)"
+ " output\n");
+ (void) printf("%-7.7s %-5.5s %-7.7s "
+ "%-5.5s %-6.6s ",
+ "packets", "errs", "packets",
+ "errs", "colls");
+ (void) printf("%-7.7s %-5.5s %-7.7s "
+ "%-5.5s %-6.6s\n",
+ "packets", "errs", "packets",
+ "errs", "colls");
+ }
+
+ sum6 = zerostat;
+
+ /* 'for' loop 2f: */
+ for (ap6 = (mib2_ipv6AddrEntry_t *)item->valp;
+ (char *)ap6 < (char *)item->valp
+ + item->length;
+ ap6++) {
+ (void) octetstr(&ap6->ipv6AddrIfIndex,
+ 'a', buf, sizeof (buf));
+ (void) strtok(buf, ":");
+ ksp = kstat_lookup(kc, NULL, -1, buf);
+ if (ksp && ksp->ks_type ==
+ KSTAT_TYPE_NAMED)
+ (void) safe_kstat_read(kc,
+ ksp, NULL);
+
+ t.ipackets = kstat_named_value(ksp,
+ "ipackets");
+ t.ierrors = kstat_named_value(ksp,
+ "ierrors");
+ t.opackets = kstat_named_value(ksp,
+ "opackets");
+ t.oerrors = kstat_named_value(ksp,
+ "oerrors");
+ t.collisions = kstat_named_value(ksp,
+ "collisions");
+
+ if (strcmp(buf, matchname) == 0)
+ new6 = t;
+
+ /* Build the interface list */
+
+ tlp = malloc(sizeof (struct iflist));
+ (void) strlcpy(tlp->ifname, buf,
+ sizeof (tlp->ifname));
+ tlp->tot = t;
+ *nextnew = tlp;
+ nextnew = &tlp->next_if;
+
+ /*
+ * First time through.
+ * Just add up the interface stats.
+ */
+
+ if (oldlist6 == NULL) {
+ if_stat_total(&zerostat,
+ &t, &sum6);
+ continue;
+ }
+
+ /*
+ * Walk old list for the interface.
+ *
+ * If found, add difference to total.
+ *
+ * If not, an interface has been plumbed
+ * up. In this case, we will simply
+ * ignore the new interface until the
+ * next interval; as there's no easy way
+ * to acquire statistics between time
+ * of the plumb and the next interval
+ * boundary. This results in inaccurate
+ * total values for current interval.
+ *
+ * Note the case when an interface is
+ * unplumbed; as similar problems exist.
+ * The unplumbed interface is not in the
+ * current list, and there's no easy way
+ * to account for the statistics between
+ * the previous interval and time of the
+ * unplumb. Therefore, we (in a sense)
+ * ignore the removed interface by only
+ * involving "current" interfaces when
+ * computing the total statistics.
+ * Unfortunately, this also results in
+ * inaccurate values for interval total.
+ */
+
+ for (walkold = oldlist6;
+ walkold != NULL;
+ walkold = walkold->next_if) {
+ if (strcmp(walkold->ifname,
+ buf) == 0) {
+ if_stat_total(
+ &walkold->tot,
+ &t, &sum6);
+ break;
+ }
+ }
+
+ } /* 'for' loop 2f ends */
+
+ *nextnew = NULL;
+
+ (void) printf("%-7llu %-5llu %-7llu "
+ "%-5llu %-6llu ",
+ new6.ipackets - old6.ipackets,
+ new6.ierrors - old6.ierrors,
+ new6.opackets - old6.opackets,
+ new6.oerrors - old6.oerrors,
+ new6.collisions - old6.collisions);
+
+ (void) printf("%-7llu %-5llu %-7llu "
+ "%-5llu %-6llu\n", sum6.ipackets,
+ sum6.ierrors, sum6.opackets,
+ sum6.oerrors, sum6.collisions);
+
+ /*
+ * Tidy things up once finished.
+ */
+
+ old6 = new6;
+ cleanlist = oldlist6;
+ oldlist6 = newlist6;
+ while (cleanlist != NULL) {
+ tlp = cleanlist->next_if;
+ free(cleanlist);
+ cleanlist = tlp;
+ }
+ }
+ break;
+ }
+ }
+ if (Iflag_only == 0)
+ (void) putchar('\n');
+ (void) fflush(stdout);
+ } /* 'for' loop 1 ends */
+ reentry = B_TRUE;
+}
+
+static void
+if_report_ip4(mib2_ipAddrEntry_t *ap,
+ char ifname[], char logintname[], struct ifstat *statptr,
+ boolean_t ksp_not_null) {
+
+ char abuf[MAXHOSTNAMELEN + 1];
+ char dstbuf[MAXHOSTNAMELEN + 1];
+
+ if (ksp_not_null) {
+ (void) printf("%-5s %-5u",
+ ifname, ap->ipAdEntInfo.ae_mtu);
+ if (ap->ipAdEntInfo.ae_flags & IFF_POINTOPOINT)
+ (void) pr_addr(ap->ipAdEntInfo.ae_pp_dst_addr,
+ abuf, sizeof (abuf));
+ else
+ (void) pr_netaddr(ap->ipAdEntAddr,
+ ap->ipAdEntNetMask, abuf, sizeof (abuf));
+ (void) printf("%-13s %-14s %-6llu %-5llu %-6llu %-5llu "
+ "%-6llu %-6llu\n",
+ abuf, pr_addr(ap->ipAdEntAddr, dstbuf, sizeof (dstbuf)),
+ statptr->ipackets, statptr->ierrors,
+ statptr->opackets, statptr->oerrors,
+ statptr->collisions, 0LL);
+ }
+ /*
+ * Print logical interface info if Aflag set (including logical unit 0)
+ */
+ if (Aflag) {
+ *statptr = zerostat;
+ statptr->ipackets = ap->ipAdEntInfo.ae_ibcnt;
+ statptr->opackets = ap->ipAdEntInfo.ae_obcnt;
+
+ (void) printf("%-5s %-5u", logintname, ap->ipAdEntInfo.ae_mtu);
+ if (ap->ipAdEntInfo.ae_flags & IFF_POINTOPOINT)
+ (void) pr_addr(ap->ipAdEntInfo.ae_pp_dst_addr, abuf,
+ sizeof (abuf));
+ else
+ (void) pr_netaddr(ap->ipAdEntAddr, ap->ipAdEntNetMask,
+ abuf, sizeof (abuf));
+
+ (void) printf("%-13s %-14s %-6llu %-5s %-6llu "
+ "%-5s %-6s %-6llu\n", abuf,
+ pr_addr(ap->ipAdEntAddr, dstbuf, sizeof (dstbuf)),
+ statptr->ipackets, "N/A", statptr->opackets, "N/A", "N/A",
+ 0LL);
+ }
+}
+
+static void
+if_report_ip6(mib2_ipv6AddrEntry_t *ap6,
+ char ifname[], char logintname[], struct ifstat *statptr,
+ boolean_t ksp_not_null) {
+
+ char abuf[MAXHOSTNAMELEN + 1];
+ char dstbuf[MAXHOSTNAMELEN + 1];
+
+ if (ksp_not_null) {
+ (void) printf("%-5s %-5u", ifname, ap6->ipv6AddrInfo.ae_mtu);
+ if (ap6->ipv6AddrInfo.ae_flags &
+ IFF_POINTOPOINT) {
+ (void) pr_addr6(&ap6->ipv6AddrInfo.ae_pp_dst_addr,
+ abuf, sizeof (abuf));
+ } else {
+ (void) pr_prefix6(&ap6->ipv6AddrAddress,
+ ap6->ipv6AddrPfxLength, abuf,
+ sizeof (abuf));
+ }
+ (void) printf("%-27s %-27s %-6llu %-5llu "
+ "%-6llu %-5llu %-6llu\n",
+ abuf, pr_addr6(&ap6->ipv6AddrAddress, dstbuf,
+ sizeof (dstbuf)),
+ statptr->ipackets, statptr->ierrors, statptr->opackets,
+ statptr->oerrors, statptr->collisions);
+ }
+ /*
+ * Print logical interface info if Aflag set (including logical unit 0)
+ */
+ if (Aflag) {
+ *statptr = zerostat;
+ statptr->ipackets = ap6->ipv6AddrInfo.ae_ibcnt;
+ statptr->opackets = ap6->ipv6AddrInfo.ae_obcnt;
+
+ (void) printf("%-5s %-5u", logintname,
+ ap6->ipv6AddrInfo.ae_mtu);
+ if (ap6->ipv6AddrInfo.ae_flags & IFF_POINTOPOINT)
+ (void) pr_addr6(&ap6->ipv6AddrInfo.ae_pp_dst_addr,
+ abuf, sizeof (abuf));
+ else
+ (void) pr_prefix6(&ap6->ipv6AddrAddress,
+ ap6->ipv6AddrPfxLength, abuf, sizeof (abuf));
+ (void) printf("%-27s %-27s %-6llu %-5s %-6llu %-5s %-6s\n",
+ abuf, pr_addr6(&ap6->ipv6AddrAddress, dstbuf,
+ sizeof (dstbuf)),
+ statptr->ipackets, "N/A",
+ statptr->opackets, "N/A", "N/A");
+ }
+}
+
+/* --------------------- DHCP_REPORT (netstat -D) ------------------------- */
+
+dhcp_ipc_reply_t *
+dhcp_do_ipc(dhcp_ipc_type_t type, const char *ifname)
+{
+ dhcp_ipc_request_t *request;
+ dhcp_ipc_reply_t *reply;
+ int error;
+
+ request = dhcp_ipc_alloc_request(type, ifname, NULL, 0, DHCP_TYPE_NONE);
+ if (request == NULL)
+ fail(0, "dhcp_do_ipc: out of memory");
+
+ error = dhcp_ipc_make_request(request, &reply, DHCP_IPC_WAIT_DEFAULT);
+ if (error != 0) {
+ free(request);
+ fail(0, "dhcp_do_ipc: %s", dhcp_ipc_strerror(error));
+ }
+
+ free(request);
+ error = reply->return_code;
+ if (error != 0) {
+ free(reply);
+ fail(0, "dhcp_do_ipc: %s", dhcp_ipc_strerror(error));
+ }
+
+ return (reply);
+}
+
+/*
+ * get_ifnames: return a dynamically allocated string of all interface
+ * names which have all of the IFF_* flags listed in `flags_on' on and
+ * all of the IFF_* flags in `flags_off' off. If no such interfaces
+ * are found, "" is returned. If an unexpected failure occurs, NULL
+ * is returned.
+ */
+static char *
+get_ifnames(int flags_on, int flags_off)
+{
+ struct ifconf ifc;
+ int n_ifs, i, sock_fd;
+ char *ifnames;
+
+ sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock_fd == -1)
+ return (NULL);
+
+ if ((ioctl(sock_fd, SIOCGIFNUM, &n_ifs) == -1) || (n_ifs <= 0)) {
+ (void) close(sock_fd);
+ return (NULL);
+ }
+
+ ifnames = calloc(1, n_ifs * (IFNAMSIZ + 1));
+ ifc.ifc_len = n_ifs * sizeof (struct ifreq);
+ ifc.ifc_req = calloc(n_ifs, sizeof (struct ifreq));
+ if (ifc.ifc_req != NULL && ifnames != NULL) {
+
+ if (ioctl(sock_fd, SIOCGIFCONF, &ifc) == -1) {
+ (void) close(sock_fd);
+ free(ifnames);
+ free(ifc.ifc_req);
+ return (NULL);
+ }
+
+ /* 'for' loop 1: */
+ for (i = 0; i < n_ifs; i++) {
+
+ if (ioctl(sock_fd, SIOCGIFFLAGS, &ifc.ifc_req[i]) == 0)
+ if ((ifc.ifc_req[i].ifr_flags &
+ (flags_on | flags_off)) != flags_on)
+ continue; /* 'for' loop 1 */
+
+ (void) strcat(ifnames, ifc.ifc_req[i].ifr_name);
+ (void) strcat(ifnames, " ");
+ } /* 'for' loop 1 ends */
+
+ if (strlen(ifnames) > 1)
+ ifnames[strlen(ifnames) - 1] = '\0';
+ }
+
+ (void) close(sock_fd);
+ if (ifc.ifc_req != NULL)
+ free(ifc.ifc_req);
+ return (ifnames);
+}
+
+static void
+dhcp_report(char *ifname)
+{
+ int did_alloc = 0;
+ dhcp_ipc_reply_t *reply;
+
+ if (!(family_selected(AF_INET)))
+ return;
+
+ if (ifname == NULL) {
+ ifname = get_ifnames(IFF_DHCPRUNNING, 0);
+ if (ifname == NULL)
+ fail(0, "dhcp_report: unable to retrieve list of"
+ " interfaces using DHCP");
+ did_alloc++;
+ }
+
+ (void) printf("%s", dhcp_status_hdr_string());
+
+ for (ifname = strtok(ifname, " ");
+ ifname != NULL;
+ ifname = strtok(NULL, " ")) {
+ reply = dhcp_do_ipc(DHCP_STATUS, ifname);
+ (void) printf("%s", dhcp_status_reply_to_string(reply));
+ free(reply);
+ }
+
+ if (did_alloc)
+ free(ifname);
+}
+
+/* --------------------- GROUP_REPORT (netstat -g) ------------------------- */
+
+static void
+group_report(mib_item_t *item)
+{
+ mib_item_t *v4grp = NULL, *v4src = NULL;
+ mib_item_t *v6grp = NULL, *v6src = NULL;
+ int jtemp = 0;
+ char ifname[LIFNAMSIZ + 1];
+ char abuf[MAXHOSTNAMELEN + 1];
+ ip_member_t *ipmp;
+ ip_grpsrc_t *ips;
+ ipv6_member_t *ipmp6;
+ ipv6_grpsrc_t *ips6;
+ char *ifnamep;
+ boolean_t first, first_src;
+
+ /* 'for' loop 1: */
+ for (; item; item = item->next_item) {
+ if (Dflag) {
+ (void) printf("\n--- Entry %d ---\n", ++jtemp);
+ (void) printf("Group = %d, mib_id = %d, "
+ "length = %d, valp = 0x%p\n",
+ item->group, item->mib_id, item->length,
+ item->valp);
+ }
+ if (item->group == MIB2_IP && family_selected(AF_INET)) {
+ switch (item->mib_id) {
+ case EXPER_IP_GROUP_MEMBERSHIP:
+ v4grp = item;
+ if (Dflag)
+ (void) printf("item is v4grp info\n");
+ break;
+ case EXPER_IP_GROUP_SOURCES:
+ v4src = item;
+ if (Dflag)
+ (void) printf("item is v4src info\n");
+ break;
+ default:
+ continue;
+ }
+ continue;
+ }
+ if (item->group == MIB2_IP6 && family_selected(AF_INET6)) {
+ switch (item->mib_id) {
+ case EXPER_IP6_GROUP_MEMBERSHIP:
+ v6grp = item;
+ if (Dflag)
+ (void) printf("item is v6grp info\n");
+ break;
+ case EXPER_IP6_GROUP_SOURCES:
+ v6src = item;
+ if (Dflag)
+ (void) printf("item is v6src info\n");
+ break;
+ default:
+ continue;
+ }
+ }
+ }
+
+ if (family_selected(AF_INET) && v4grp != NULL) {
+ if (Dflag)
+ (void) printf("%u records for ipGroupMember:\n",
+ v4grp->length / sizeof (ip_member_t));
+
+ first = B_TRUE;
+ for (ipmp = (ip_member_t *)v4grp->valp;
+ (char *)ipmp < (char *)v4grp->valp + v4grp->length;
+ /* LINTED: (note 1) */
+ ipmp = (ip_member_t *)((char *)ipmp + ipMemberEntrySize)) {
+ if (first) {
+ (void) puts(v4compat ?
+ "Group Memberships" :
+ "Group Memberships: IPv4");
+ (void) puts("Interface "
+ "Group RefCnt");
+ (void) puts("--------- "
+ "-------------------- ------");
+ first = B_FALSE;
+ }
+
+ (void) printf("%-9s %-20s %6u\n",
+ octetstr(&ipmp->ipGroupMemberIfIndex, 'a',
+ ifname, sizeof (ifname)),
+ pr_addr(ipmp->ipGroupMemberAddress,
+ abuf, sizeof (abuf)),
+ ipmp->ipGroupMemberRefCnt);
+
+
+ if (!Vflag || v4src == NULL)
+ continue;
+
+ if (Dflag)
+ (void) printf("scanning %u ipGroupSource "
+ "records...\n",
+ v4src->length/sizeof (ip_grpsrc_t));
+
+ first_src = B_TRUE;
+ for (ips = (ip_grpsrc_t *)v4src->valp;
+ (char *)ips < (char *)v4src->valp + v4src->length;
+ /* LINTED: (note 1) */
+ ips = (ip_grpsrc_t *)((char *)ips +
+ ipGroupSourceEntrySize)) {
+ /*
+ * We assume that all source addrs for a given
+ * interface/group pair are contiguous, so on
+ * the first non-match after we've found at
+ * least one, we bail.
+ */
+ if ((ipmp->ipGroupMemberAddress !=
+ ips->ipGroupSourceGroup) ||
+ (!octetstrmatch(&ipmp->ipGroupMemberIfIndex,
+ &ips->ipGroupSourceIfIndex))) {
+ if (first_src)
+ continue;
+ else
+ break;
+ }
+ if (first_src) {
+ (void) printf("\t%s: %s\n",
+ fmodestr(
+ ipmp->ipGroupMemberFilterMode),
+ pr_addr(ips->ipGroupSourceAddress,
+ abuf, sizeof (abuf)));
+ first_src = B_FALSE;
+ continue;
+ }
+
+ (void) printf("\t %s\n",
+ pr_addr(ips->ipGroupSourceAddress, abuf,
+ sizeof (abuf)));
+ }
+ }
+ (void) putchar('\n');
+ }
+
+ if (family_selected(AF_INET6) && v6grp != NULL) {
+ if (Dflag)
+ (void) printf("%u records for ipv6GroupMember:\n",
+ v6grp->length / sizeof (ipv6_member_t));
+
+ first = B_TRUE;
+ for (ipmp6 = (ipv6_member_t *)v6grp->valp;
+ (char *)ipmp6 < (char *)v6grp->valp + v6grp->length;
+ /* LINTED: (note 1) */
+ ipmp6 = (ipv6_member_t *)((char *)ipmp6 +
+ ipv6MemberEntrySize)) {
+ if (first) {
+ (void) puts("Group Memberships: "
+ "IPv6");
+ (void) puts(" If "
+ "Group RefCnt");
+ (void) puts("----- "
+ "--------------------------- ------");
+ first = B_FALSE;
+ }
+
+ ifnamep = if_indextoname(
+ ipmp6->ipv6GroupMemberIfIndex, ifname);
+ if (ifnamep == NULL) {
+ (void) printf("Invalid ifindex %d\n",
+ ipmp6->ipv6GroupMemberIfIndex);
+ continue;
+ }
+ (void) printf("%-5s %-27s %5u\n",
+ ifnamep,
+ pr_addr6(&ipmp6->ipv6GroupMemberAddress,
+ abuf, sizeof (abuf)),
+ ipmp6->ipv6GroupMemberRefCnt);
+
+ if (!Vflag || v6src == NULL)
+ continue;
+
+ if (Dflag)
+ (void) printf("scanning %u ipv6GroupSource "
+ "records...\n",
+ v6src->length/sizeof (ipv6_grpsrc_t));
+
+ first_src = B_TRUE;
+ for (ips6 = (ipv6_grpsrc_t *)v6src->valp;
+ (char *)ips6 < (char *)v6src->valp + v6src->length;
+ /* LINTED: (note 1) */
+ ips6 = (ipv6_grpsrc_t *)((char *)ips6 +
+ ipv6GroupSourceEntrySize)) {
+ /* same assumption as in the v4 case above */
+ if ((ipmp6->ipv6GroupMemberIfIndex !=
+ ips6->ipv6GroupSourceIfIndex) ||
+ (!IN6_ARE_ADDR_EQUAL(
+ &ipmp6->ipv6GroupMemberAddress,
+ &ips6->ipv6GroupSourceGroup))) {
+ if (first_src)
+ continue;
+ else
+ break;
+ }
+ if (first_src) {
+ (void) printf("\t%s: %s\n",
+ fmodestr(
+ ipmp6->ipv6GroupMemberFilterMode),
+ pr_addr6(
+ &ips6->ipv6GroupSourceAddress,
+ abuf, sizeof (abuf)));
+ first_src = B_FALSE;
+ continue;
+ }
+
+ (void) printf("\t %s\n",
+ pr_addr6(&ips6->ipv6GroupSourceAddress,
+ abuf, sizeof (abuf)));
+ }
+ }
+ (void) putchar('\n');
+ }
+
+ (void) putchar('\n');
+ (void) fflush(stdout);
+}
+
+/* --------------------- ARP_REPORT (netstat -p) -------------------------- */
+
+static void
+arp_report(mib_item_t *item)
+{
+ int jtemp = 0;
+ char ifname[LIFNAMSIZ + 1];
+ char abuf[MAXHOSTNAMELEN + 1];
+ char maskbuf[STR_EXPAND * OCTET_LENGTH + 1];
+ char flbuf[32]; /* ACE_F_ flags */
+ char xbuf[STR_EXPAND * OCTET_LENGTH + 1];
+ mib2_ipNetToMediaEntry_t *np;
+ int flags;
+ boolean_t first;
+
+ if (!(family_selected(AF_INET)))
+ return;
+
+ /* 'for' loop 1: */
+ for (; item; item = item->next_item) {
+ if (Dflag) {
+ (void) printf("\n--- Entry %d ---\n", ++jtemp);
+ (void) printf("Group = %d, mib_id = %d, "
+ "length = %d, valp = 0x%p\n",
+ item->group, item->mib_id, item->length,
+ item->valp);
+ }
+ if (!(item->group == MIB2_IP && item->mib_id == MIB2_IP_MEDIA))
+ continue; /* 'for' loop 1 */
+
+ if (Dflag)
+ (void) printf("%u records for "
+ "ipNetToMediaEntryTable:\n",
+ item->length/sizeof (mib2_ipNetToMediaEntry_t));
+
+ first = B_TRUE;
+ /* 'for' loop 2: */
+ for (np = (mib2_ipNetToMediaEntry_t *)item->valp;
+ (char *)np < (char *)item->valp + item->length;
+ /* LINTED: (note 1) */
+ np = (mib2_ipNetToMediaEntry_t *)((char *)np +
+ ipNetToMediaEntrySize)) {
+ if (first) {
+ (void) puts(v4compat ?
+ "Net to Media Table" :
+ "Net to Media Table: IPv4");
+ (void) fputs("Device "
+ "IP Address Mask ",
+ stdout);
+ (void) puts("Flags Phys Addr ");
+ (void) puts("------ -------------------- "
+ "--------------- ----- ---------------");
+ first = B_FALSE;
+ }
+
+ flbuf[0] = '\0';
+ flags = np->ipNetToMediaInfo.ntm_flags;
+ if (flags & ACE_F_PERMANENT)
+ (void) strcat(flbuf, "S");
+ if (flags & ACE_F_PUBLISH)
+ (void) strcat(flbuf, "P");
+ if (flags & ACE_F_DYING)
+ (void) strcat(flbuf, "D");
+ if (!(flags & ACE_F_RESOLVED))
+ (void) strcat(flbuf, "U");
+ if (flags & ACE_F_MAPPING)
+ (void) strcat(flbuf, "M");
+ (void) printf("%-6s %-20s %-15s %-5s %s\n",
+ octetstr(&np->ipNetToMediaIfIndex, 'a',
+ ifname, sizeof (ifname)),
+ pr_addr(np->ipNetToMediaNetAddress,
+ abuf, sizeof (abuf)),
+ octetstr(&np->ipNetToMediaInfo.ntm_mask, 'd',
+ maskbuf, sizeof (maskbuf)),
+ flbuf,
+ octetstr(&np->ipNetToMediaPhysAddress, 'h',
+ xbuf, sizeof (xbuf)));
+ } /* 'for' loop 2 ends */
+ } /* 'for' loop 1 ends */
+ (void) fflush(stdout);
+}
+
+/* --------------------- NDP_REPORT (netstat -p) -------------------------- */
+
+static void
+ndp_report(mib_item_t *item)
+{
+ int jtemp = 0;
+ char abuf[MAXHOSTNAMELEN + 1];
+ char *state;
+ char *type;
+ char xbuf[STR_EXPAND * OCTET_LENGTH + 1];
+ mib2_ipv6NetToMediaEntry_t *np6;
+ char ifname[LIFNAMSIZ + 1];
+ char *ifnamep;
+ boolean_t first;
+
+ if (!(family_selected(AF_INET6)))
+ return;
+
+ /* 'for' loop 1: */
+ for (; item; item = item->next_item) {
+ if (Dflag) {
+ (void) printf("\n--- Entry %d ---\n", ++jtemp);
+ (void) printf("Group = %d, mib_id = %d, "
+ "length = %d, valp = 0x%p\n",
+ item->group, item->mib_id, item->length,
+ item->valp);
+ }
+ if (!(item->group == MIB2_IP6 &&
+ item->mib_id == MIB2_IP6_MEDIA))
+ continue; /* 'for' loop 1 */
+
+ first = B_TRUE;
+ /* 'for' loop 2: */
+ for (np6 = (mib2_ipv6NetToMediaEntry_t *)item->valp;
+ (char *)np6 < (char *)item->valp + item->length;
+ /* LINTED: (note 1) */
+ np6 = (mib2_ipv6NetToMediaEntry_t *)((char *)np6 +
+ ipv6NetToMediaEntrySize)) {
+ if (first) {
+ (void) puts("\nNet to Media Table: IPv6");
+ (void) puts(" If Physical Address "
+ " Type State Destination/Mask");
+ (void) puts("----- ----------------- "
+ "------- ------------ "
+ "---------------------------");
+ first = B_FALSE;
+ }
+
+ ifnamep = if_indextoname(np6->ipv6NetToMediaIfIndex,
+ ifname);
+ if (ifnamep == NULL) {
+ (void) printf("Invalid ifindex %d\n",
+ np6->ipv6NetToMediaIfIndex);
+ continue; /* 'for' loop 2 */
+ }
+ switch (np6->ipv6NetToMediaState) {
+ case ND_INCOMPLETE:
+ state = "INCOMPLETE";
+ break;
+ case ND_REACHABLE:
+ state = "REACHABLE";
+ break;
+ case ND_STALE:
+ state = "STALE";
+ break;
+ case ND_DELAY:
+ state = "DELAY";
+ break;
+ case ND_PROBE:
+ state = "PROBE";
+ break;
+ case ND_UNREACHABLE:
+ state = "UNREACHABLE";
+ break;
+ default:
+ state = "UNKNOWN";
+ }
+
+ switch (np6->ipv6NetToMediaType) {
+ case 1:
+ type = "other";
+ break;
+ case 2:
+ type = "dynamic";
+ break;
+ case 3:
+ type = "static";
+ break;
+ case 4:
+ type = "local";
+ break;
+ }
+ (void) printf("%-5s %-17s %-7s %-12s %-27s\n",
+ ifnamep,
+ octetstr(&np6->ipv6NetToMediaPhysAddress, 'h',
+ xbuf, sizeof (xbuf)),
+ type,
+ state,
+ pr_addr6(&np6->ipv6NetToMediaNetAddress,
+ abuf, sizeof (abuf)));
+ } /* 'for' loop 2 ends */
+ } /* 'for' loop 1 ends */
+ (void) putchar('\n');
+ (void) fflush(stdout);
+}
+
+/* ------------------------- ire_report (netstat -r) ------------------------ */
+
+static boolean_t ire_report_item_v4(mib2_ipRouteEntry_t *rp, boolean_t first);
+static boolean_t ire_report_item_v4src(mib2_ipRouteEntry_t *rp,
+ boolean_t first);
+static boolean_t ire_report_item_v6(mib2_ipv6RouteEntry_t *rp6,
+ boolean_t first);
+
+static void
+ire_report(mib_item_t *item)
+{
+ int jtemp = 0;
+ boolean_t print_hdr_once_v4 = B_TRUE;
+ boolean_t print_hdr_once_v6 = B_TRUE;
+ mib2_ipRouteEntry_t *rp;
+ mib2_ipv6RouteEntry_t *rp6;
+
+ /* 'for' loop 1: */
+ for (; item; item = item->next_item) {
+ if (Dflag) {
+ (void) printf("\n--- Entry %d ---\n", ++jtemp);
+ (void) printf("Group = %d, mib_id = %d, "
+ "length = %d, valp = 0x%p\n",
+ item->group, item->mib_id,
+ item->length, item->valp);
+ }
+ if (!((item->group == MIB2_IP &&
+ item->mib_id == MIB2_IP_ROUTE) ||
+ (item->group == MIB2_IP6 &&
+ item->mib_id == MIB2_IP6_ROUTE)))
+ continue; /* 'for' loop 1 */
+
+ if (item->group == MIB2_IP && !family_selected(AF_INET))
+ continue; /* 'for' loop 1 */
+ else if (item->group == MIB2_IP6 && !family_selected(AF_INET6))
+ continue; /* 'for' loop 1 */
+
+ if (Dflag) {
+ if (item->group == MIB2_IP) {
+ (void) printf("%u records for "
+ "ipRouteEntryTable:\n",
+ item->length/sizeof (mib2_ipRouteEntry_t));
+ } else {
+ (void) printf("%u records for "
+ "ipv6RouteEntryTable:\n",
+ item->length/
+ sizeof (mib2_ipv6RouteEntry_t));
+ }
+ }
+
+ if (item->group == MIB2_IP) {
+ for (rp = (mib2_ipRouteEntry_t *)item->valp;
+ (char *)rp < (char *)item->valp + item->length;
+ /* LINTED: (note 1) */
+ rp = (mib2_ipRouteEntry_t *)((char *)rp +
+ ipRouteEntrySize)) {
+ print_hdr_once_v4 = ire_report_item_v4(rp,
+ print_hdr_once_v4);
+ }
+ print_hdr_once_v4 = B_TRUE;
+ for (rp = (mib2_ipRouteEntry_t *)item->valp;
+ (char *)rp < (char *)item->valp + item->length;
+ /* LINTED: (note 1) */
+ rp = (mib2_ipRouteEntry_t *)((char *)rp +
+ ipRouteEntrySize)) {
+ print_hdr_once_v4 = ire_report_item_v4src(rp,
+ print_hdr_once_v4);
+ }
+ } else {
+ for (rp6 = (mib2_ipv6RouteEntry_t *)item->valp;
+ (char *)rp6 < (char *)item->valp + item->length;
+ /* LINTED: (note 1) */
+ rp6 = (mib2_ipv6RouteEntry_t *)((char *)rp6 +
+ ipv6RouteEntrySize)) {
+ print_hdr_once_v6 = ire_report_item_v6(rp6,
+ print_hdr_once_v6);
+ }
+ }
+ } /* 'for' loop 1 ends */
+ (void) fflush(stdout);
+}
+
+/*
+ * Match a user-supplied device name. We do this by string because
+ * the MIB2 interface gives us interface name strings rather than
+ * ifIndex numbers. The "none" rule matches only routes with no
+ * interface. The "any" rule matches routes with any non-blank
+ * interface. A base name ("hme0") matches all aliases as well
+ * ("hme0:1").
+ */
+static boolean_t
+dev_name_match(const DeviceName *devnam, const char *ifname)
+{
+ int iflen;
+
+ if (ifname == NULL)
+ return (devnam->o_length == 0); /* "none" */
+ if (*ifname == '\0')
+ return (devnam->o_length != 0); /* "any" */
+ iflen = strlen(ifname);
+ /* The check for ':' here supports interface aliases. */
+ if (iflen > devnam->o_length ||
+ (iflen < devnam->o_length && devnam->o_bytes[iflen] != ':'))
+ return (B_FALSE);
+ return (strncmp(ifname, devnam->o_bytes, iflen) == 0);
+}
+
+/*
+ * Match a user-supplied IP address list. The "any" rule matches any
+ * non-zero address. The "none" rule matches only the zero address.
+ * IPv6 addresses supplied by the user are ignored. If the user
+ * supplies a subnet mask, then match routes that are at least that
+ * specific (use the user's mask). If the user supplies only an
+ * address, then select any routes that would match (use the route's
+ * mask).
+ */
+static boolean_t
+v4_addr_match(IpAddress addr, IpAddress mask, const filter_t *fp)
+{
+ char **app;
+ char *aptr;
+ in_addr_t faddr, fmask;
+
+ if (fp->u.a.f_address == NULL) {
+ if (IN6_IS_ADDR_UNSPECIFIED(&fp->u.a.f_mask))
+ return (addr != INADDR_ANY); /* "any" */
+ else
+ return (addr == INADDR_ANY); /* "none" */
+ }
+ if (!IN6_IS_V4MASK(fp->u.a.f_mask))
+ return (B_FALSE);
+ IN6_V4MAPPED_TO_IPADDR(&fp->u.a.f_mask, fmask);
+ if (fmask != IP_HOST_MASK) {
+ if (fmask > mask)
+ return (B_FALSE);
+ mask = fmask;
+ }
+ for (app = fp->u.a.f_address->h_addr_list; (aptr = *app) != NULL; app++)
+ /* LINTED: (note 1) */
+ if (IN6_IS_ADDR_V4MAPPED((in6_addr_t *)aptr)) {
+ /* LINTED: (note 1) */
+ IN6_V4MAPPED_TO_IPADDR((in6_addr_t *)aptr, faddr);
+ if (((faddr ^ addr) & mask) == 0)
+ return (B_TRUE);
+ }
+ return (B_FALSE);
+}
+
+/*
+ * Run through the filter list for an IPv4 MIB2 route entry. If all
+ * filters of a given type fail to match, then the route is filtered
+ * out (not displayed). If no filter is given or at least one filter
+ * of each type matches, then display the route.
+ */
+static boolean_t
+ire_filter_match_v4(mib2_ipRouteEntry_t *rp, uint_t flag_b)
+{
+ filter_t *fp;
+ int idx;
+
+ /* 'for' loop 1: */
+ for (idx = 0; idx < NFILTERKEYS; idx++)
+ if ((fp = filters[idx]) != NULL) {
+ /* 'for' loop 2: */
+ for (; fp != NULL; fp = fp->f_next) {
+ switch (idx) {
+ case FK_AF:
+ if (fp->u.f_family != AF_INET)
+ continue; /* 'for' loop 2 */
+ break;
+ case FK_INIF:
+ if (!dev_name_match(&rp->ipRouteInfo.
+ re_in_ill, fp->u.f_ifname))
+ continue; /* 'for' loop 2 */
+ break;
+ case FK_OUTIF:
+ if (!dev_name_match(&rp->ipRouteIfIndex,
+ fp->u.f_ifname))
+ continue; /* 'for' loop 2 */
+ break;
+ case FK_SRC:
+ if (!v4_addr_match(rp->ipRouteInfo.
+ re_in_src_addr, IP_HOST_MASK, fp))
+ continue; /* 'for' loop 2 */
+ break;
+ case FK_DST:
+ if (!v4_addr_match(rp->ipRouteDest,
+ rp->ipRouteMask, fp))
+ continue; /* 'for' loop 2 */
+ break;
+ case FK_FLAGS:
+ if ((flag_b & fp->u.f.f_flagset) !=
+ fp->u.f.f_flagset ||
+ (flag_b & fp->u.f.f_flagclear))
+ continue; /* 'for' loop 2 */
+ break;
+ }
+ break;
+ } /* 'for' loop 2 ends */
+ if (fp == NULL)
+ return (B_FALSE);
+ }
+ /* 'for' loop 1 ends */
+ return (B_TRUE);
+}
+
+/*
+ * Given an IPv4 MIB2 route entry, form the list of flags for the
+ * route.
+ */
+static uint_t
+form_v4_route_flags(mib2_ipRouteEntry_t *rp, char *flags)
+{
+ uint_t flag_b;
+
+ flag_b = FLF_U;
+ (void) strcpy(flags, "U");
+ if (rp->ipRouteInfo.re_ire_type == IRE_DEFAULT ||
+ rp->ipRouteInfo.re_ire_type == IRE_PREFIX ||
+ rp->ipRouteInfo.re_ire_type == IRE_HOST ||
+ rp->ipRouteInfo.re_ire_type == IRE_HOST_REDIRECT) {
+ (void) strcat(flags, "G");
+ flag_b |= FLF_G;
+ }
+ if (rp->ipRouteMask == IP_HOST_MASK) {
+ (void) strcat(flags, "H");
+ flag_b |= FLF_H;
+ }
+ if (rp->ipRouteInfo.re_ire_type == IRE_HOST_REDIRECT) {
+ (void) strcat(flags, "D");
+ flag_b |= FLF_D;
+ }
+ if (rp->ipRouteInfo.re_ire_type == IRE_CACHE) {
+ /* Address resolution */
+ (void) strcat(flags, "A");
+ flag_b |= FLF_A;
+ }
+ if (rp->ipRouteInfo.re_ire_type == IRE_BROADCAST) { /* Broadcast */
+ (void) strcat(flags, "B");
+ flag_b |= FLF_B;
+ }
+ if (rp->ipRouteInfo.re_ire_type == IRE_LOCAL) { /* Local */
+ (void) strcat(flags, "L");
+ flag_b |= FLF_L;
+ }
+ if (rp->ipRouteInfo.re_flags & RTF_MULTIRT) {
+ (void) strcat(flags, "M"); /* Multiroute */
+ flag_b |= FLF_M;
+ }
+ if (rp->ipRouteInfo.re_flags & RTF_SETSRC) {
+ (void) strcat(flags, "S"); /* Setsrc */
+ flag_b |= FLF_S;
+ }
+ return (flag_b);
+}
+
+static boolean_t
+ire_report_item_v4(mib2_ipRouteEntry_t *rp, boolean_t first)
+{
+ char dstbuf[MAXHOSTNAMELEN + 1];
+ char maskbuf[MAXHOSTNAMELEN + 1];
+ char gwbuf[MAXHOSTNAMELEN + 1];
+ char ifname[LIFNAMSIZ + 1];
+ char flags[10]; /* RTF_ flags */
+ uint_t flag_b;
+
+ if (rp->ipRouteInfo.re_in_src_addr != 0 ||
+ rp->ipRouteInfo.re_in_ill.o_length != 0 ||
+ !(Aflag || (rp->ipRouteInfo.re_ire_type != IRE_CACHE &&
+ rp->ipRouteInfo.re_ire_type != IRE_BROADCAST &&
+ rp->ipRouteInfo.re_ire_type != IRE_LOCAL))) {
+ return (first);
+ }
+
+ flag_b = form_v4_route_flags(rp, flags);
+
+ if (!ire_filter_match_v4(rp, flag_b))
+ return (first);
+
+ if (first) {
+ if (Vflag) {
+ (void) puts(v4compat ?
+ "\nIRE Table:" :
+ "\nIRE Table: IPv4");
+ (void) puts(" Destination Mask "
+ " Gateway "
+ "Device Mxfrg Rtt Ref Flg Out "
+ "In/Fwd");
+ (void) puts("-------------------- --------------- "
+ "-------------------- "
+ "------ ----- ----- --- --- ----- "
+ "------");
+ } else {
+ (void) puts(v4compat ?
+ "\nRouting Table:" :
+ "\nRouting Table: IPv4");
+ (void) puts(" Destination "
+ " Gateway Flags Ref Use "
+ "Interface");
+ (void) puts("-------------------- "
+ "-------------------- ----- ----- ------ "
+ "---------");
+ }
+ first = B_FALSE;
+ }
+
+ if (flag_b & FLF_H) {
+ (void) pr_addr(rp->ipRouteDest, dstbuf, sizeof (dstbuf));
+ } else {
+ (void) pr_net(rp->ipRouteDest, rp->ipRouteMask,
+ dstbuf, sizeof (dstbuf));
+ }
+ if (Vflag) {
+ (void) printf("%-20s %-15s %-20s %-6s %5u%c %4u %3u "
+ "%-4s%6u%6u\n",
+ dstbuf,
+ pr_mask(rp->ipRouteMask, maskbuf, sizeof (maskbuf)),
+ pr_addrnz(rp->ipRouteNextHop, gwbuf, sizeof (gwbuf)),
+ octetstr(&rp->ipRouteIfIndex, 'a', ifname, sizeof (ifname)),
+ rp->ipRouteInfo.re_max_frag,
+ rp->ipRouteInfo.re_frag_flag ? '*' : ' ',
+ rp->ipRouteInfo.re_rtt,
+ rp->ipRouteInfo.re_ref,
+ flags,
+ rp->ipRouteInfo.re_obpkt,
+ rp->ipRouteInfo.re_ibpkt);
+ } else {
+ (void) printf("%-20s %-20s %-5s %4u%7u %s\n",
+ dstbuf,
+ pr_addrnz(rp->ipRouteNextHop, gwbuf, sizeof (gwbuf)),
+ flags,
+ rp->ipRouteInfo.re_ref,
+ rp->ipRouteInfo.re_obpkt + rp->ipRouteInfo.re_ibpkt,
+ octetstr(&rp->ipRouteIfIndex, 'a',
+ ifname, sizeof (ifname)));
+ }
+ return (first);
+}
+
+/*
+ * Report a source-specific route.
+ */
+static boolean_t
+ire_report_item_v4src(mib2_ipRouteEntry_t *rp, boolean_t first)
+{
+ char dstbuf[MAXHOSTNAMELEN + 1];
+ char srcbuf[MAXHOSTNAMELEN + 1];
+ char gwbuf[MAXHOSTNAMELEN + 1];
+ char inif[LIFNAMSIZ + 1];
+ char outif[LIFNAMSIZ + 1];
+ uint_t flag_b;
+ char flags[10];
+
+ /*
+ * If this isn't a source specific route, or if it's filtered
+ * out, then ignore it.
+ */
+ if ((rp->ipRouteInfo.re_in_src_addr == 0 &&
+ rp->ipRouteInfo.re_in_ill.o_length == 0) ||
+ !(Aflag || (rp->ipRouteInfo.re_ire_type != IRE_CACHE &&
+ rp->ipRouteInfo.re_ire_type != IRE_BROADCAST &&
+ rp->ipRouteInfo.re_ire_type != IRE_LOCAL))) {
+ return (first);
+ }
+
+ flag_b = form_v4_route_flags(rp, flags);
+
+ if (!ire_filter_match_v4(rp, flag_b))
+ return (first);
+
+ if (first) {
+ if (Vflag) {
+ (void) printf("\nIRE Table: %sSource-Specific\n",
+ v4compat ? "" : "IPv4 ");
+ (void) puts(" Destination In If "
+ " Source Gateway "
+ " Out If Mxfrg Rtt Ref Flg Out In/Fwd");
+ (void) puts("------------------ ----------- "
+ "----------------- ----------------- "
+ "----------- ----- ----- --- --- ----- ------");
+ } else {
+ (void) printf("\nRouting Table: %sSource-Specific\n",
+ v4compat ? "" : "IPv4 ");
+ (void) puts(" Destination In If "
+ " Source Gateway Flags Use "
+ " Out If");
+ (void) puts("--------------- -------- "
+ "--------------- --------------- ----- ------ "
+ "--------");
+ }
+ first = B_FALSE;
+ }
+
+ /*
+ * This is special-cased here because the kernel doesn't actually
+ * pay any attention to the destination address on mrtun entries.
+ * Saying "default" would be misleading, though technically correct.
+ */
+ if (rp->ipRouteInfo.re_in_src_addr != 0 && rp->ipRouteDest == 0 &&
+ rp->ipRouteMask == 0)
+ (void) strlcpy(dstbuf, " --", sizeof (dstbuf));
+ else
+ (void) pr_netclassless(rp->ipRouteDest, rp->ipRouteMask,
+ dstbuf, sizeof (dstbuf));
+ (void) octetstr(&rp->ipRouteInfo.re_in_ill, 'a', inif, sizeof (inif));
+ (void) pr_addrnz(rp->ipRouteInfo.re_in_src_addr, srcbuf,
+ sizeof (srcbuf));
+ (void) octetstr(&rp->ipRouteIfIndex, 'a', outif, sizeof (outif));
+ (void) pr_addrnz(rp->ipRouteNextHop, gwbuf, sizeof (gwbuf));
+ if (Vflag) {
+ (void) printf("%-18s %-11s %-17s %-17s %-11s %4u%c %5u %3u "
+ "%-3s %5u %6u\n",
+ dstbuf, inif, srcbuf, gwbuf, outif,
+ rp->ipRouteInfo.re_max_frag,
+ rp->ipRouteInfo.re_frag_flag ? '*' : ' ',
+ rp->ipRouteInfo.re_rtt, rp->ipRouteInfo.re_ref, flags,
+ rp->ipRouteInfo.re_obpkt, rp->ipRouteInfo.re_ibpkt);
+ } else {
+ (void) printf("%-15s %-8s %-15s %-15s %-5s %6u %-8s\n", dstbuf,
+ inif, srcbuf, gwbuf, flags,
+ rp->ipRouteInfo.re_obpkt + rp->ipRouteInfo.re_ibpkt,
+ outif);
+ }
+ return (first);
+}
+
+/*
+ * Match a user-supplied IP address list against an IPv6 route entry.
+ * If the user specified "any," then any non-zero address matches. If
+ * the user specified "none," then only the zero address matches. If
+ * the user specified a subnet mask length, then use that in matching
+ * routes (select routes that are at least as specific). If the user
+ * specified only an address, then use the route's mask (select routes
+ * that would match that address). IPv4 addresses are ignored.
+ */
+static boolean_t
+v6_addr_match(const Ip6Address *addr, int masklen, const filter_t *fp)
+{
+ const uint8_t *ucp;
+ int fmasklen;
+ int i;
+ char **app;
+ char *aptr;
+
+ if (fp->u.a.f_address == NULL) {
+ if (IN6_IS_ADDR_UNSPECIFIED(&fp->u.a.f_mask)) /* any */
+ return (!IN6_IS_ADDR_UNSPECIFIED(addr));
+ return (IN6_IS_ADDR_UNSPECIFIED(addr)); /* "none" */
+ }
+ fmasklen = 0;
+ /* 'for' loop 1a: */
+ for (ucp = fp->u.a.f_mask.s6_addr;
+ ucp < fp->u.a.f_mask.s6_addr + sizeof (fp->u.a.f_mask.s6_addr);
+ ucp++) {
+ if (*ucp != 0xff) {
+ if (*ucp != 0)
+ fmasklen += 9 - ffs(*ucp);
+ break; /* 'for' loop 1a */
+ }
+ fmasklen += 8;
+ } /* 'for' loop 1a ends */
+ if (fmasklen != IPV6_ABITS) {
+ if (fmasklen > masklen)
+ return (B_FALSE);
+ masklen = fmasklen;
+ }
+ /* 'for' loop 1b: */
+ for (app = fp->u.a.f_address->h_addr_list; (aptr = *app) != NULL;
+ app++) {
+ /* LINTED: (note 1) */
+ if (IN6_IS_ADDR_V4MAPPED((in6_addr_t *)aptr))
+ continue; /* 'for' loop 1b */
+ ucp = addr->s6_addr;
+ for (i = masklen; i >= 8; i -= 8)
+ if (*ucp++ != *aptr++)
+ break; /* 'for' loop 1b */
+ if (i == 0 ||
+ (i < 8 && ((*ucp ^ *aptr) & ~(0xff >> i)) == 0))
+ return (B_TRUE);
+ } /* 'for' loop 1b ends */
+ return (B_FALSE);
+}
+
+/*
+ * Run through the filter list for an IPv6 MIB2 IRE. For a given
+ * type, if there's at least one filter and all filters of that type
+ * fail to match, then the route doesn't match and isn't displayed.
+ * If at least one matches, or none are specified, for each of the
+ * types, then the route is selected and displayed.
+ */
+static boolean_t
+ire_filter_match_v6(mib2_ipv6RouteEntry_t *rp6, uint_t flag_b)
+{
+ filter_t *fp;
+ int idx;
+
+ /* 'for' loop 1: */
+ for (idx = 0; idx < NFILTERKEYS; idx++)
+ if ((fp = filters[idx]) != NULL) {
+ /* 'for' loop 2: */
+ for (; fp != NULL; fp = fp->f_next) {
+ switch (idx) {
+ case FK_AF:
+ if (fp->u.f_family != AF_INET6)
+ /* 'for' loop 2 */
+ continue;
+ break;
+ case FK_INIF:
+ if (fp->u.f_ifname != NULL)
+ /* 'for' loop 2 */
+ continue;
+ break;
+ case FK_OUTIF:
+ if (!dev_name_match(&rp6->
+ ipv6RouteIfIndex, fp->u.f_ifname))
+ /* 'for' loop 2 */
+ continue;
+ break;
+ case FK_SRC:
+ if (!v6_addr_match(&rp6->ipv6RouteInfo.
+ re_src_addr, IPV6_ABITS, fp))
+ /* 'for' loop 2 */
+ continue;
+ break;
+ case FK_DST:
+ if (!v6_addr_match(&rp6->ipv6RouteDest,
+ rp6->ipv6RoutePfxLength, fp))
+ /* 'for' loop 2 */
+ continue;
+ break;
+ case FK_FLAGS:
+ if ((flag_b & fp->u.f.f_flagset) !=
+ fp->u.f.f_flagset ||
+ (flag_b & fp->u.f.f_flagclear))
+ /* 'for' loop 2 */
+ continue;
+ break;
+ }
+ break;
+ } /* 'for' loop 2 ends */
+ if (fp == NULL)
+ return (B_FALSE);
+ }
+ /* 'for' loop 1 ends */
+ return (B_TRUE);
+}
+
+static boolean_t
+ire_report_item_v6(mib2_ipv6RouteEntry_t *rp6, boolean_t first)
+{
+ char dstbuf[MAXHOSTNAMELEN + 1];
+ char gwbuf[MAXHOSTNAMELEN + 1];
+ char ifname[LIFNAMSIZ + 1];
+ char flags[10]; /* RTF_ flags */
+ uint_t flag_b;
+
+ if (!(Aflag || (rp6->ipv6RouteInfo.re_ire_type != IRE_CACHE &&
+ rp6->ipv6RouteInfo.re_ire_type != IRE_LOCAL))) {
+ return (first);
+ }
+
+ flag_b = FLF_U;
+ (void) strcpy(flags, "U");
+ if (rp6->ipv6RouteInfo.re_ire_type == IRE_DEFAULT ||
+ rp6->ipv6RouteInfo.re_ire_type == IRE_PREFIX ||
+ rp6->ipv6RouteInfo.re_ire_type == IRE_HOST ||
+ rp6->ipv6RouteInfo.re_ire_type == IRE_HOST_REDIRECT) {
+ (void) strcat(flags, "G");
+ flag_b |= FLF_G;
+ }
+
+ if (rp6->ipv6RoutePfxLength == IPV6_ABITS) {
+ (void) strcat(flags, "H");
+ flag_b |= FLF_H;
+ }
+
+ if (rp6->ipv6RouteInfo.re_ire_type == IRE_HOST_REDIRECT) {
+ (void) strcat(flags, "D");
+ flag_b |= FLF_D;
+ }
+ if (rp6->ipv6RouteInfo.re_ire_type == IRE_CACHE) {
+ /* Address resolution */
+ (void) strcat(flags, "A");
+ flag_b |= FLF_A;
+ }
+ if (rp6->ipv6RouteInfo.re_ire_type == IRE_LOCAL) { /* Local */
+ (void) strcat(flags, "L");
+ flag_b |= FLF_L;
+ }
+ if (rp6->ipv6RouteInfo.re_flags & RTF_MULTIRT) {
+ (void) strcat(flags, "M"); /* Multiroute */
+ flag_b |= FLF_M;
+ }
+ if (rp6->ipv6RouteInfo.re_flags & RTF_SETSRC) {
+ (void) strcat(flags, "S"); /* Setsrc */
+ flag_b |= FLF_S;
+ }
+
+ if (!ire_filter_match_v6(rp6, flag_b))
+ return (first);
+
+ if (first) {
+ if (Vflag) {
+ (void) puts("\nIRE Table: IPv6");
+ (void) puts(" Destination/Mask "
+ " Gateway "
+ " If PMTU Rtt Ref Flags Out In/Fwd");
+ (void) puts("--------------------------- "
+ "--------------------------- "
+ "----- ------ ----- --- ----- ------ ------");
+ } else {
+ (void) puts("\nRouting Table: IPv6");
+ (void) puts(" Destination/Mask "
+ " Gateway Flags Ref Use "
+ " If ");
+ (void) puts("--------------------------- "
+ "--------------------------- ----- --- ------ "
+ "-----");
+ }
+ first = B_FALSE;
+ }
+
+ if (Vflag) {
+ (void) printf("%-27s %-27s %-5s %5u%c %5u %3u "
+ "%-5s %6u %6u\n",
+ pr_prefix6(&rp6->ipv6RouteDest,
+ rp6->ipv6RoutePfxLength, dstbuf, sizeof (dstbuf)),
+ IN6_IS_ADDR_UNSPECIFIED(&rp6->ipv6RouteNextHop) ?
+ " --" :
+ pr_addr6(&rp6->ipv6RouteNextHop, gwbuf, sizeof (gwbuf)),
+ octetstr(&rp6->ipv6RouteIfIndex, 'a',
+ ifname, sizeof (ifname)),
+ rp6->ipv6RouteInfo.re_max_frag,
+ rp6->ipv6RouteInfo.re_frag_flag ? '*' : ' ',
+ rp6->ipv6RouteInfo.re_rtt,
+ rp6->ipv6RouteInfo.re_ref,
+ flags,
+ rp6->ipv6RouteInfo.re_obpkt,
+ rp6->ipv6RouteInfo.re_ibpkt);
+ } else {
+ (void) printf("%-27s %-27s %-5s %3u %6u %-5s\n",
+ pr_prefix6(&rp6->ipv6RouteDest,
+ rp6->ipv6RoutePfxLength, dstbuf, sizeof (dstbuf)),
+ IN6_IS_ADDR_UNSPECIFIED(&rp6->ipv6RouteNextHop) ?
+ " --" :
+ pr_addr6(&rp6->ipv6RouteNextHop, gwbuf, sizeof (gwbuf)),
+ flags,
+ rp6->ipv6RouteInfo.re_ref,
+ rp6->ipv6RouteInfo.re_obpkt + rp6->ipv6RouteInfo.re_ibpkt,
+ octetstr(&rp6->ipv6RouteIfIndex, 'a',
+ ifname, sizeof (ifname)));
+ }
+ return (first);
+}
+
+/* ------------------------------ TCP_REPORT------------------------------- */
+
+static const char tcp_hdr_v4[] =
+"\nTCP: IPv4\n";
+static const char tcp_hdr_v4_compat[] =
+"\nTCP\n";
+static const char tcp_hdr_v4_verbose[] =
+"Local/Remote Address Swind Snext Suna Rwind Rnext Rack "
+" Rto Mss State\n"
+"-------------------- ----- -------- -------- ----- -------- -------- "
+"----- ----- -----\n";
+static const char tcp_hdr_v4_normal[] =
+" Local Address Remote Address Swind Send-Q Rwind Recv-Q State\n"
+"-------------------- -------------------- ----- ------ ----- ------ -------\n";
+
+static const char tcp_hdr_v6[] =
+"\nTCP: IPv6\n";
+static const char tcp_hdr_v6_verbose[] =
+"Local/Remote Address Swind Snext Suna Rwind Rnext "
+" Rack Rto Mss State If \n"
+"--------------------------------- ----- -------- -------- ----- -------- "
+"-------- ----- ----- ----------- -----\n";
+static const char tcp_hdr_v6_normal[] =
+" Local Address Remote Address "
+"Swind Send-Q Rwind Recv-Q State If \n"
+"--------------------------------- --------------------------------- "
+"----- ------ ----- ------ ----------- -----\n";
+
+static boolean_t tcp_report_item_v4(mib2_tcpConnEntry_t *tp, boolean_t first);
+static boolean_t tcp_report_item_v6(mib2_tcp6ConnEntry_t *tp6, boolean_t first);
+
+static void
+tcp_report(mib_item_t *item)
+{
+ int jtemp = 0;
+ boolean_t print_hdr_once_v4 = B_TRUE;
+ boolean_t print_hdr_once_v6 = B_TRUE;
+ mib2_tcpConnEntry_t *tp;
+ mib2_tcp6ConnEntry_t *tp6;
+
+ if (!protocol_selected(IPPROTO_TCP))
+ return;
+
+ /* 'for' loop 1: */
+ for (; item; item = item->next_item) {
+ if (Dflag) {
+ (void) printf("\n--- Entry %d ---\n", ++jtemp);
+ (void) printf("Group = %d, mib_id = %d, "
+ "length = %d, valp = 0x%p\n",
+ item->group, item->mib_id,
+ item->length, item->valp);
+ }
+
+ if (!((item->group == MIB2_TCP &&
+ item->mib_id == MIB2_TCP_CONN) ||
+ (item->group == MIB2_TCP6 &&
+ item->mib_id == MIB2_TCP6_CONN)))
+ continue; /* 'for' loop 1 */
+
+ if (item->group == MIB2_TCP && !family_selected(AF_INET))
+ continue; /* 'for' loop 1 */
+ else if (item->group == MIB2_TCP6 && !family_selected(AF_INET6))
+ continue; /* 'for' loop 1 */
+
+ if (item->group == MIB2_TCP) {
+ for (tp = (mib2_tcpConnEntry_t *)item->valp;
+ (char *)tp < (char *)item->valp + item->length;
+ /* LINTED: (note 1) */
+ tp = (mib2_tcpConnEntry_t *)((char *)tp +
+ tcpConnEntrySize)) {
+ print_hdr_once_v4 = tcp_report_item_v4(tp,
+ print_hdr_once_v4);
+ }
+ } else {
+ for (tp6 = (mib2_tcp6ConnEntry_t *)item->valp;
+ (char *)tp6 < (char *)item->valp + item->length;
+ /* LINTED: (note 1) */
+ tp6 = (mib2_tcp6ConnEntry_t *)((char *)tp6 +
+ tcp6ConnEntrySize)) {
+ print_hdr_once_v6 = tcp_report_item_v6(tp6,
+ print_hdr_once_v6);
+ }
+ }
+ } /* 'for' loop 1 ends */
+ (void) fflush(stdout);
+}
+
+static boolean_t
+tcp_report_item_v4(mib2_tcpConnEntry_t *tp, boolean_t first)
+{
+ /*
+ * lname and fname below are for the hostname as well as the portname
+ * There is no limit on portname length so we assume MAXHOSTNAMELEN
+ * as the limit
+ */
+ char lname[MAXHOSTNAMELEN + MAXHOSTNAMELEN + 1];
+ char fname[MAXHOSTNAMELEN + MAXHOSTNAMELEN + 1];
+
+ if (!(Aflag || tp->tcpConnEntryInfo.ce_state >= TCPS_ESTABLISHED))
+ return (first); /* Nothing to print */
+
+ if (first) {
+ (void) fputs(v4compat ? tcp_hdr_v4_compat : tcp_hdr_v4, stdout);
+ (void) fputs(Vflag ? tcp_hdr_v4_verbose : tcp_hdr_v4_normal,
+ stdout);
+ first = B_FALSE;
+ }
+
+ if (Vflag) {
+ (void) printf("%-20s\n%-20s %5u %08x %08x %5u %08x %08x "
+ "%5u %5u %s\n",
+ pr_ap(tp->tcpConnLocalAddress,
+ tp->tcpConnLocalPort, "tcp", lname, sizeof (lname)),
+ pr_ap(tp->tcpConnRemAddress,
+ tp->tcpConnRemPort, "tcp", fname, sizeof (fname)),
+ tp->tcpConnEntryInfo.ce_swnd,
+ tp->tcpConnEntryInfo.ce_snxt,
+ tp->tcpConnEntryInfo.ce_suna,
+ tp->tcpConnEntryInfo.ce_rwnd,
+ tp->tcpConnEntryInfo.ce_rnxt,
+ tp->tcpConnEntryInfo.ce_rack,
+ tp->tcpConnEntryInfo.ce_rto,
+ tp->tcpConnEntryInfo.ce_mss,
+ mitcp_state(tp->tcpConnEntryInfo.ce_state));
+ } else {
+ int sq = (int)tp->tcpConnEntryInfo.ce_snxt -
+ (int)tp->tcpConnEntryInfo.ce_suna - 1;
+ int rq = (int)tp->tcpConnEntryInfo.ce_rnxt -
+ (int)tp->tcpConnEntryInfo.ce_rack;
+
+ (void) printf("%-20s %-20s %5u %6d %5u %6d %s\n",
+ pr_ap(tp->tcpConnLocalAddress,
+ tp->tcpConnLocalPort, "tcp", lname, sizeof (lname)),
+ pr_ap(tp->tcpConnRemAddress,
+ tp->tcpConnRemPort, "tcp", fname, sizeof (fname)),
+ tp->tcpConnEntryInfo.ce_swnd,
+ (sq >= 0) ? sq : 0,
+ tp->tcpConnEntryInfo.ce_rwnd,
+ (rq >= 0) ? rq : 0,
+ mitcp_state(tp->tcpConnEntryInfo.ce_state));
+ }
+ return (first);
+}
+
+static boolean_t
+tcp_report_item_v6(mib2_tcp6ConnEntry_t *tp6, boolean_t first)
+{
+ /*
+ * lname and fname below are for the hostname as well as the portname
+ * There is no limit on portname length so we assume MAXHOSTNAMELEN
+ * as the limit
+ */
+ char lname[MAXHOSTNAMELEN + MAXHOSTNAMELEN + 1];
+ char fname[MAXHOSTNAMELEN + MAXHOSTNAMELEN + 1];
+ char ifname[LIFNAMSIZ + 1];
+ char *ifnamep;
+
+ if (!(Aflag || tp6->tcp6ConnEntryInfo.ce_state >= TCPS_ESTABLISHED))
+ return (first); /* Nothing to print */
+
+ if (first) {
+ (void) fputs(tcp_hdr_v6, stdout);
+ (void) fputs(Vflag ? tcp_hdr_v6_verbose : tcp_hdr_v6_normal,
+ stdout);
+ first = B_FALSE;
+ }
+
+ ifnamep = (tp6->tcp6ConnIfIndex != 0) ?
+ if_indextoname(tp6->tcp6ConnIfIndex, ifname) : NULL;
+
+ if (Vflag) {
+ (void) printf("%-33s\n%-33s %5u %08x %08x %5u %08x %08x "
+ "%5u %5u %-11s %-5s\n",
+ pr_ap6(&tp6->tcp6ConnLocalAddress,
+ tp6->tcp6ConnLocalPort, "tcp", lname, sizeof (lname)),
+ pr_ap6(&tp6->tcp6ConnRemAddress,
+ tp6->tcp6ConnRemPort, "tcp", fname, sizeof (fname)),
+ tp6->tcp6ConnEntryInfo.ce_swnd,
+ tp6->tcp6ConnEntryInfo.ce_snxt,
+ tp6->tcp6ConnEntryInfo.ce_suna,
+ tp6->tcp6ConnEntryInfo.ce_rwnd,
+ tp6->tcp6ConnEntryInfo.ce_rnxt,
+ tp6->tcp6ConnEntryInfo.ce_rack,
+ tp6->tcp6ConnEntryInfo.ce_rto,
+ tp6->tcp6ConnEntryInfo.ce_mss,
+ mitcp_state(tp6->tcp6ConnEntryInfo.ce_state),
+ (ifnamep == NULL) ? "" : ifnamep);
+ } else {
+ int sq = (int)tp6->tcp6ConnEntryInfo.ce_snxt -
+ (int)tp6->tcp6ConnEntryInfo.ce_suna - 1;
+ int rq = (int)tp6->tcp6ConnEntryInfo.ce_rnxt -
+ (int)tp6->tcp6ConnEntryInfo.ce_rack;
+
+ (void) printf("%-33s %-33s %5u %6d %5u %6d %-11s %-5s\n",
+ pr_ap6(&tp6->tcp6ConnLocalAddress,
+ tp6->tcp6ConnLocalPort, "tcp", lname, sizeof (lname)),
+ pr_ap6(&tp6->tcp6ConnRemAddress,
+ tp6->tcp6ConnRemPort, "tcp", fname, sizeof (fname)),
+ tp6->tcp6ConnEntryInfo.ce_swnd,
+ (sq >= 0) ? sq : 0,
+ tp6->tcp6ConnEntryInfo.ce_rwnd,
+ (rq >= 0) ? rq : 0,
+ mitcp_state(tp6->tcp6ConnEntryInfo.ce_state),
+ (ifnamep == NULL) ? "" : ifnamep);
+ }
+ return (first);
+}
+
+/* ------------------------------- UDP_REPORT------------------------------- */
+
+static boolean_t udp_report_item_v4(mib2_udpEntry_t *ude, boolean_t first);
+static boolean_t udp_report_item_v6(mib2_udp6Entry_t *ude6, boolean_t first);
+
+static char *udp_hdr_v6 =
+" Local Address Remote Address "
+" State If \n"
+"--------------------------------- --------------------------------- "
+"---------- -----\n";
+
+static void
+udp_report(mib_item_t *item)
+{
+ int jtemp = 0;
+ boolean_t print_hdr_once_v4 = B_TRUE;
+ boolean_t print_hdr_once_v6 = B_TRUE;
+ mib2_udpEntry_t *ude;
+ mib2_udp6Entry_t *ude6;
+
+ if (!protocol_selected(IPPROTO_UDP))
+ return;
+
+ /* 'for' loop 1: */
+ for (; item; item = item->next_item) {
+ if (Dflag) {
+ (void) printf("\n--- Entry %d ---\n", ++jtemp);
+ (void) printf("Group = %d, mib_id = %d, "
+ "length = %d, valp = 0x%p\n",
+ item->group, item->mib_id,
+ item->length, item->valp);
+ }
+ if (!((item->group == MIB2_UDP &&
+ item->mib_id == MIB2_UDP_ENTRY) ||
+ (item->group == MIB2_UDP6 &&
+ item->mib_id == MIB2_UDP6_ENTRY)))
+ continue; /* 'for' loop 1 */
+
+ if (item->group == MIB2_UDP && !family_selected(AF_INET))
+ continue; /* 'for' loop 1 */
+ else if (item->group == MIB2_UDP6 && !family_selected(AF_INET6))
+ continue; /* 'for' loop 1 */
+
+ /* xxx.xxx.xxx.xxx,pppp sss... */
+ if (item->group == MIB2_UDP) {
+ for (ude = (mib2_udpEntry_t *)item->valp;
+ (char *)ude < (char *)item->valp + item->length;
+ /* LINTED: (note 1) */
+ ude = (mib2_udpEntry_t *)((char *)ude +
+ udpEntrySize)) {
+ print_hdr_once_v4 = udp_report_item_v4(ude,
+ print_hdr_once_v4);
+ }
+ } else {
+ for (ude6 = (mib2_udp6Entry_t *)item->valp;
+ (char *)ude6 < (char *)item->valp + item->length;
+ /* LINTED: (note 1) */
+ ude6 = (mib2_udp6Entry_t *)((char *)ude6 +
+ udp6EntrySize)) {
+ print_hdr_once_v6 = udp_report_item_v6(ude6,
+ print_hdr_once_v6);
+ }
+ }
+ } /* 'for' loop 1 ends */
+ (void) fflush(stdout);
+}
+
+static boolean_t
+udp_report_item_v4(mib2_udpEntry_t *ude, boolean_t first)
+{
+ char lname[MAXHOSTNAMELEN + MAXHOSTNAMELEN + 1];
+ /* hostname + portname */
+ char *cp;
+
+ if (!(Aflag || ude->udpEntryInfo.ue_state >= MIB2_UDP_connected))
+ return (first); /* Nothing to print */
+
+ if (first) {
+ (void) puts(v4compat ? "\nUDP" : "\nUDP: IPv4");
+ (void) puts(
+ " Local Address Remote Address State");
+ (void) puts(
+ "-------------------- -------------------- -------");
+ first = B_FALSE;
+ }
+
+ switch (ude->udpEntryInfo.ue_state) {
+ case MIB2_UDP_unbound:
+ cp = "Unbound";
+ break;
+ case MIB2_UDP_idle:
+ cp = "Idle";
+ break;
+ case MIB2_UDP_connected:
+ cp = "Connected";
+ break;
+ default:
+ cp = "Unknown";
+ break;
+ }
+ (void) printf("%-20s ",
+ pr_ap(ude->udpLocalAddress, ude->udpLocalPort, "udp",
+ lname, sizeof (lname)));
+ if (ude->udpEntryInfo.ue_state == MIB2_UDP_connected) {
+ (void) printf("%-20s ",
+ pr_ap(ude->udpEntryInfo.ue_RemoteAddress,
+ ude->udpEntryInfo.ue_RemotePort, "udp",
+ lname, sizeof (lname)));
+ } else {
+ (void) printf("%-20s ", "");
+ }
+ (void) printf(" %s\n", cp);
+ return (first);
+}
+
+static boolean_t
+udp_report_item_v6(mib2_udp6Entry_t *ude6, boolean_t first)
+{
+ char lname[MAXHOSTNAMELEN + MAXHOSTNAMELEN + 1];
+ /* hostname + portname */
+ char *cp;
+ char ifname[LIFNAMSIZ + 1];
+
+ if (!(Aflag || ude6->udp6EntryInfo.ue_state >= MIB2_UDP_connected))
+ return (first); /* Nothing to print */
+
+ if (first) {
+ (void) printf("\nUDP: IPv6\n%s", udp_hdr_v6);
+ first = B_FALSE;
+ }
+
+ switch (ude6->udp6EntryInfo.ue_state) {
+ case MIB2_UDP_unbound:
+ cp = "Unbound";
+ break;
+ case MIB2_UDP_idle:
+ cp = "Idle";
+ break;
+ case MIB2_UDP_connected:
+ cp = "Connected";
+ break;
+ default:
+ cp = "Unknown";
+ break;
+ }
+ (void) printf("%-33s ",
+ pr_ap6(&ude6->udp6LocalAddress,
+ ude6->udp6LocalPort, "udp", lname, sizeof (lname)));
+ if (ude6->udp6EntryInfo.ue_state == MIB2_UDP_connected) {
+ (void) printf("%-33s ",
+ pr_ap6(&ude6->udp6EntryInfo.ue_RemoteAddress,
+ ude6->udp6EntryInfo.ue_RemotePort, "udp",
+ lname, sizeof (lname)));
+ } else {
+ (void) printf("%-33s ", "");
+ }
+ if (ude6->udp6IfIndex != 0 &&
+ (if_indextoname(ude6->udp6IfIndex, ifname) != NULL)) {
+ (void) printf("%-10s %-5s\n", cp, ifname);
+ } else {
+ (void) printf("%-10s\n", cp);
+ }
+ return (first);
+}
+
+/* ------------------------------ SCTP_REPORT------------------------------- */
+
+static const char sctp_hdr[] =
+"\nSCTP:";
+static const char sctp_hdr_normal[] =
+" Local Address Remote Address "
+"Swind Send-Q Rwind Recv-Q StrsI/O State\n"
+"------------------------------- ------------------------------- "
+"------ ------ ------ ------ ------- -----------";
+
+static const char *
+nssctp_state(int state)
+{
+ switch (state) {
+ case MIB2_SCTP_closed:
+ return ("CLOSED");
+ case MIB2_SCTP_cookieWait:
+ return ("COOKIE_WAIT");
+ case MIB2_SCTP_cookieEchoed:
+ return ("COOKIE_ECHOED");
+ case MIB2_SCTP_established:
+ return ("ESTABLISHED");
+ case MIB2_SCTP_shutdownPending:
+ return ("SHUTDOWN_PENDING");
+ case MIB2_SCTP_shutdownSent:
+ return ("SHUTDOWN_SENT");
+ case MIB2_SCTP_shutdownReceived:
+ return ("SHUTDOWN_RECEIVED");
+ case MIB2_SCTP_shutdownAckSent:
+ return ("SHUTDOWN_ACK_SENT");
+ case MIB2_SCTP_listen:
+ return ("LISTEN");
+ default:
+ return ("UNKNOWN STATE");
+ }
+}
+
+static mib2_sctpConnRemoteEntry_t *
+sctp_getnext_rem(mib_item_t **itemp, mib2_sctpConnRemoteEntry_t *current,
+ uint32_t associd)
+{
+ mib_item_t *item = *itemp;
+ mib2_sctpConnRemoteEntry_t *sre;
+
+ for (; item != NULL; item = item->next_item, current = NULL) {
+ if (!(item->group == MIB2_SCTP &&
+ item->mib_id == MIB2_SCTP_CONN_REMOTE)) {
+ continue;
+ }
+
+ if (current != NULL) {
+ /* LINTED: (note 1) */
+ sre = (mib2_sctpConnRemoteEntry_t *)((char *)current +
+ sctpRemoteEntrySize);
+ } else {
+ sre = item->valp;
+ }
+ for (; (char *)sre < (char *)item->valp + item->length;
+ /* LINTED: (note 1) */
+ sre = (mib2_sctpConnRemoteEntry_t *)((char *)sre +
+ sctpRemoteEntrySize)) {
+ if (sre->sctpAssocId != associd) {
+ continue;
+ }
+ *itemp = item;
+ return (sre);
+ }
+ }
+ *itemp = NULL;
+ return (NULL);
+}
+
+static mib2_sctpConnLocalEntry_t *
+sctp_getnext_local(mib_item_t **itemp, mib2_sctpConnLocalEntry_t *current,
+ uint32_t associd)
+{
+ mib_item_t *item = *itemp;
+ mib2_sctpConnLocalEntry_t *sle;
+
+ for (; item != NULL; item = item->next_item, current = NULL) {
+ if (!(item->group == MIB2_SCTP &&
+ item->mib_id == MIB2_SCTP_CONN_LOCAL)) {
+ continue;
+ }
+
+ if (current != NULL) {
+ /* LINTED: (note 1) */
+ sle = (mib2_sctpConnLocalEntry_t *)((char *)current +
+ sctpLocalEntrySize);
+ } else {
+ sle = item->valp;
+ }
+ for (; (char *)sle < (char *)item->valp + item->length;
+ /* LINTED: (note 1) */
+ sle = (mib2_sctpConnLocalEntry_t *)((char *)sle +
+ sctpLocalEntrySize)) {
+ if (sle->sctpAssocId != associd) {
+ continue;
+ }
+ *itemp = item;
+ return (sle);
+ }
+ }
+ *itemp = NULL;
+ return (NULL);
+}
+
+static void
+sctp_pr_addr(int type, char *name, int namelen, const in6_addr_t *addr,
+ int port)
+{
+ ipaddr_t v4addr;
+ in6_addr_t v6addr;
+
+ /*
+ * Address is either a v4 mapped or v6 addr. If
+ * it's a v4 mapped, convert to v4 before
+ * displaying.
+ */
+ switch (type) {
+ case MIB2_SCTP_ADDR_V4:
+ /* v4 */
+ v6addr = *addr;
+
+ IN6_V4MAPPED_TO_IPADDR(&v6addr, v4addr);
+ if (port > 0) {
+ (void) pr_ap(v4addr, port, "sctp", name, namelen);
+ } else {
+ (void) pr_addr(v4addr, name, namelen);
+ }
+ break;
+
+ case MIB2_SCTP_ADDR_V6:
+ /* v6 */
+ if (port > 0) {
+ (void) pr_ap6(addr, port, "sctp", name, namelen);
+ } else {
+ (void) pr_addr6(addr, name, namelen);
+ }
+ break;
+
+ default:
+ (void) snprintf(name, namelen, "<unknown addr type>");
+ break;
+ }
+}
+
+static void
+sctp_conn_report_item(mib_item_t *head, mib2_sctpConnEntry_t *sp)
+{
+ char lname[MAXHOSTNAMELEN + MAXHOSTNAMELEN + 1];
+ char fname[MAXHOSTNAMELEN + MAXHOSTNAMELEN + 1];
+ mib2_sctpConnRemoteEntry_t *sre = NULL;
+ mib2_sctpConnLocalEntry_t *sle = NULL;
+ mib_item_t *local = head;
+ mib_item_t *remote = head;
+ uint32_t id = sp->sctpAssocId;
+ boolean_t printfirst = B_TRUE;
+
+ sctp_pr_addr(sp->sctpAssocRemPrimAddrType, fname, sizeof (fname),
+ &sp->sctpAssocRemPrimAddr, sp->sctpAssocRemPort);
+ sctp_pr_addr(sp->sctpAssocRemPrimAddrType, lname, sizeof (lname),
+ &sp->sctpAssocLocPrimAddr, sp->sctpAssocLocalPort);
+
+ (void) printf("%-31s %-31s %6u %6d %6u %6d %3d/%-3d %s\n",
+ lname, fname,
+ sp->sctpConnEntryInfo.ce_swnd,
+ sp->sctpConnEntryInfo.ce_sendq,
+ sp->sctpConnEntryInfo.ce_rwnd,
+ sp->sctpConnEntryInfo.ce_recvq,
+ sp->sctpAssocInStreams, sp->sctpAssocOutStreams,
+ nssctp_state(sp->sctpAssocState));
+
+ if (!Vflag) {
+ return;
+ }
+
+ /* Print remote addresses/local addresses on following lines */
+ while ((sre = sctp_getnext_rem(&remote, sre, id)) != NULL) {
+ if (!IN6_ARE_ADDR_EQUAL(&sre->sctpAssocRemAddr,
+ &sp->sctpAssocRemPrimAddr)) {
+ if (printfirst == B_TRUE) {
+ (void) fputs("\t<Remote: ", stdout);
+ printfirst = B_FALSE;
+ } else {
+ (void) fputs(", ", stdout);
+ }
+ sctp_pr_addr(sre->sctpAssocRemAddrType, fname,
+ sizeof (fname), &sre->sctpAssocRemAddr, -1);
+ if (sre->sctpAssocRemAddrActive == MIB2_SCTP_ACTIVE) {
+ (void) fputs(fname, stdout);
+ } else {
+ (void) printf("(%s)", fname);
+ }
+ }
+ }
+ if (printfirst == B_FALSE) {
+ (void) puts(">");
+ printfirst = B_TRUE;
+ }
+ while ((sle = sctp_getnext_local(&local, sle, id)) != NULL) {
+ if (!IN6_ARE_ADDR_EQUAL(&sle->sctpAssocLocalAddr,
+ &sp->sctpAssocLocPrimAddr)) {
+ if (printfirst == B_TRUE) {
+ (void) fputs("\t<Local: ", stdout);
+ printfirst = B_FALSE;
+ } else {
+ (void) fputs(", ", stdout);
+ }
+ sctp_pr_addr(sle->sctpAssocLocalAddrType, lname,
+ sizeof (lname), &sle->sctpAssocLocalAddr, -1);
+ (void) fputs(lname, stdout);
+ }
+ }
+ if (printfirst == B_FALSE) {
+ (void) puts(">");
+ }
+}
+
+static void
+sctp_report(mib_item_t *item)
+{
+ mib_item_t *head;
+ mib2_sctpConnEntry_t *sp;
+ boolean_t first = B_TRUE;
+
+ head = item;
+ for (; item != NULL; item = item->next_item) {
+
+ if (!(item->group == MIB2_SCTP &&
+ item->mib_id == MIB2_SCTP_CONN))
+ continue;
+
+ for (sp = item->valp;
+ (char *)sp < (char *)item->valp + item->length;
+ /* LINTED: (note 1) */
+ sp = (mib2_sctpConnEntry_t *)((char *)sp +
+ sctpEntrySize)) {
+
+ if (Aflag ||
+ sp->sctpAssocState >= MIB2_SCTP_established) {
+ if (first == B_TRUE) {
+ (void) puts(sctp_hdr);
+ (void) puts(sctp_hdr_normal);
+ first = B_FALSE;
+ }
+ sctp_conn_report_item(head, sp);
+ }
+ }
+ }
+}
+
+static char *
+plural(int n)
+{
+ return (n != 1 ? "s" : "");
+}
+
+static char *
+pluraly(int n)
+{
+ return (n != 1 ? "ies" : "y");
+}
+
+static char *
+plurales(int n)
+{
+ return (n != 1 ? "es" : "");
+}
+
+static char *
+pktscale(n)
+ int n;
+{
+ static char buf[6];
+ char t;
+
+ if (n < 1024) {
+ t = ' ';
+ } else if (n < 1024 * 1024) {
+ t = 'k';
+ n /= 1024;
+ } else if (n < 1024 * 1024 * 1024) {
+ t = 'm';
+ n /= 1024 * 1024;
+ } else {
+ t = 'g';
+ n /= 1024 * 1024 * 1024;
+ }
+
+ (void) snprintf(buf, sizeof (buf), "%4u%c", n, t);
+ return (buf);
+}
+
+/* --------------------- mrt_report (netstat -m) -------------------------- */
+
+static void
+mrt_report(mib_item_t *item)
+{
+ int jtemp = 0;
+ struct vifctl *vip;
+ vifi_t vifi;
+ struct mfcctl *mfccp;
+ int numvifs = 0;
+ int nmfc = 0;
+ char abuf[MAXHOSTNAMELEN + 1];
+
+ if (!(family_selected(AF_INET)))
+ return;
+
+ /* 'for' loop 1: */
+ for (; item; item = item->next_item) {
+ if (Dflag) {
+ (void) printf("\n--- Entry %d ---\n", ++jtemp);
+ (void) printf("Group = %d, mib_id = %d, "
+ "length = %d, valp = 0x%p\n",
+ item->group, item->mib_id, item->length,
+ item->valp);
+ }
+ if (item->group != EXPER_DVMRP)
+ continue; /* 'for' loop 1 */
+
+ switch (item->mib_id) {
+
+ case EXPER_DVMRP_VIF:
+ if (Dflag)
+ (void) printf("%u records for ipVifTable:\n",
+ item->length/sizeof (struct vifctl));
+ if (item->length/sizeof (struct vifctl) == 0) {
+ (void) puts("\nVirtual Interface Table is "
+ "empty");
+ break;
+ }
+
+ (void) puts("\nVirtual Interface Table\n"
+ " Vif Threshold Rate_Limit Local-Address"
+ " Remote-Address Pkt_in Pkt_out");
+
+ /* 'for' loop 2: */
+ for (vip = (struct vifctl *)item->valp;
+ (char *)vip < (char *)item->valp + item->length;
+ /* LINTED: (note 1) */
+ vip = (struct vifctl *)((char *)vip +
+ vifctlSize)) {
+ if (vip->vifc_lcl_addr.s_addr == 0)
+ continue; /* 'for' loop 2 */
+ /* numvifs = vip->vifc_vifi; */
+
+ numvifs++;
+ (void) printf(" %2u %3u "
+ "%4u %-15.15s",
+ vip->vifc_vifi,
+ vip->vifc_threshold,
+ vip->vifc_rate_limit,
+ pr_addr(vip->vifc_lcl_addr.s_addr,
+ abuf, sizeof (abuf)));
+ (void) printf(" %-15.15s %8u %8u\n",
+ (vip->vifc_flags & VIFF_TUNNEL) ?
+ pr_addr(vip->vifc_rmt_addr.s_addr,
+ abuf, sizeof (abuf)) : "",
+ vip->vifc_pkt_in,
+ vip->vifc_pkt_out);
+ } /* 'for' loop 2 ends */
+
+ (void) printf("Numvifs: %d\n", numvifs);
+ break;
+
+ case EXPER_DVMRP_MRT:
+ if (Dflag)
+ (void) printf("%u records for ipMfcTable:\n",
+ item->length/sizeof (struct vifctl));
+ if (item->length/sizeof (struct vifctl) == 0) {
+ (void) puts("\nMulticast Forwarding Cache is "
+ "empty");
+ break;
+ }
+
+ (void) puts("\nMulticast Forwarding Cache\n"
+ " Origin-Subnet Mcastgroup "
+ "# Pkts In-Vif Out-vifs/Forw-ttl");
+
+ for (mfccp = (struct mfcctl *)item->valp;
+ (char *)mfccp < (char *)item->valp + item->length;
+ /* LINTED: (note 1) */
+ mfccp = (struct mfcctl *)((char *)mfccp +
+ mfcctlSize)) {
+
+ nmfc++;
+ (void) printf(" %-30.15s",
+ pr_addr(mfccp->mfcc_origin.s_addr,
+ abuf, sizeof (abuf)));
+ (void) printf("%-15.15s %6s %3u ",
+ pr_net(mfccp->mfcc_mcastgrp.s_addr,
+ mfccp->mfcc_mcastgrp.s_addr,
+ abuf, sizeof (abuf)),
+ pktscale((int)mfccp->mfcc_pkt_cnt),
+ mfccp->mfcc_parent);
+
+ for (vifi = 0; vifi < MAXVIFS; ++vifi) {
+ if (mfccp->mfcc_ttls[vifi]) {
+ (void) printf(" %u (%u)",
+ vifi,
+ mfccp->mfcc_ttls[vifi]);
+ }
+
+ }
+ (void) putchar('\n');
+ }
+ (void) printf("\nTotal no. of entries in cache: %d\n",
+ nmfc);
+ break;
+ }
+ } /* 'for' loop 1 ends */
+ (void) putchar('\n');
+ (void) fflush(stdout);
+}
+
+/*
+ * Get the stats for the cache named 'name'. If prefix != 0, then
+ * interpret the name as a prefix, and sum up stats for all caches
+ * named 'name*'.
+ */
+static void
+kmem_cache_stats(char *title, char *name, int prefix, int64_t *total_bytes)
+{
+ int len;
+ int alloc;
+ int64_t total_alloc = 0;
+ int alloc_fail, total_alloc_fail = 0;
+ int buf_size = 0;
+ int buf_avail;
+ int buf_total;
+ int buf_max, total_buf_max = 0;
+ int buf_inuse, total_buf_inuse = 0;
+ kstat_t *ksp;
+ char buf[256];
+
+ len = prefix ? strlen(name) : 256;
+
+ /* 'for' loop 1: */
+ for (ksp = kc->kc_chain; ksp != NULL; ksp = ksp->ks_next) {
+
+ if (strcmp(ksp->ks_class, "kmem_cache") != 0)
+ continue; /* 'for' loop 1 */
+
+ /*
+ * Hack alert: because of the way streams messages are
+ * allocated, every constructed free dblk has an associated
+ * mblk. From the allocator's viewpoint those mblks are
+ * allocated (because they haven't been freed), but from
+ * our viewpoint they're actually free (because they're
+ * not currently in use). To account for this caching
+ * effect we subtract the total constructed free dblks
+ * from the total allocated mblks to derive mblks in use.
+ */
+ if (strcmp(name, "streams_mblk") == 0 &&
+ strncmp(ksp->ks_name, "streams_dblk", 12) == 0) {
+ (void) safe_kstat_read(kc, ksp, NULL);
+ total_buf_inuse -=
+ kstat_named_value(ksp, "buf_constructed");
+ continue; /* 'for' loop 1 */
+ }
+
+ if (strncmp(ksp->ks_name, name, len) != 0)
+ continue; /* 'for' loop 1 */
+
+ (void) safe_kstat_read(kc, ksp, NULL);
+
+ alloc = kstat_named_value(ksp, "alloc");
+ alloc_fail = kstat_named_value(ksp, "alloc_fail");
+ buf_size = kstat_named_value(ksp, "buf_size");
+ buf_avail = kstat_named_value(ksp, "buf_avail");
+ buf_total = kstat_named_value(ksp, "buf_total");
+ buf_max = kstat_named_value(ksp, "buf_max");
+ buf_inuse = buf_total - buf_avail;
+
+ if (Vflag && prefix) {
+ (void) snprintf(buf, sizeof (buf), "%s%s", title,
+ ksp->ks_name + len);
+ (void) printf(" %-18s %6u %9u %11u %11u\n",
+ buf, buf_inuse, buf_max, alloc, alloc_fail);
+ }
+
+ total_alloc += alloc;
+ total_alloc_fail += alloc_fail;
+ total_buf_max += buf_max;
+ total_buf_inuse += buf_inuse;
+ *total_bytes += (int64_t)buf_inuse * buf_size;
+ } /* 'for' loop 1 ends */
+
+ if (buf_size == 0) {
+ (void) printf("%-22s [couldn't find statistics for %s]\n",
+ title, name);
+ return;
+ }
+
+ if (Vflag && prefix)
+ (void) snprintf(buf, sizeof (buf), "%s_total", title);
+ else
+ (void) snprintf(buf, sizeof (buf), "%s", title);
+
+ (void) printf("%-22s %6d %9d %11lld %11d\n", buf,
+ total_buf_inuse, total_buf_max, total_alloc, total_alloc_fail);
+}
+
+static void
+m_report(void)
+{
+ int64_t total_bytes = 0;
+
+ (void) puts("streams allocation:");
+ (void) printf("%63s\n", "cumulative allocation");
+ (void) printf("%63s\n",
+ "current maximum total failures");
+
+ kmem_cache_stats("streams",
+ "stream_head_cache", 0, &total_bytes);
+ kmem_cache_stats("queues", "queue_cache", 0, &total_bytes);
+ kmem_cache_stats("mblk", "streams_mblk", 0, &total_bytes);
+ kmem_cache_stats("dblk", "streams_dblk", 1, &total_bytes);
+ kmem_cache_stats("linkblk", "linkinfo_cache", 0, &total_bytes);
+ kmem_cache_stats("syncq", "syncq_cache", 0, &total_bytes);
+ kmem_cache_stats("qband", "qband_cache", 0, &total_bytes);
+
+ (void) printf("\n%lld Kbytes allocated for streams data\n",
+ total_bytes / 1024);
+
+ (void) putchar('\n');
+ (void) fflush(stdout);
+}
+
+/* --------------------------------- */
+
+/*
+ * Print an IPv4 address. Remove the matching part of the domain name
+ * from the returned name.
+ */
+static char *
+pr_addr(uint_t addr, char *dst, uint_t dstlen)
+{
+ char *cp;
+ struct hostent *hp = NULL;
+ static char domain[MAXHOSTNAMELEN + 1];
+ static boolean_t first = B_TRUE;
+ int error_num;
+
+ if (first) {
+ first = B_FALSE;
+ if (sysinfo(SI_HOSTNAME, domain, MAXHOSTNAMELEN) != -1 &&
+ (cp = strchr(domain, '.'))) {
+ (void) strncpy(domain, cp + 1, sizeof (domain));
+ } else
+ domain[0] = 0;
+ }
+ cp = NULL;
+ if (!Nflag) {
+ hp = getipnodebyaddr((char *)&addr, sizeof (uint_t), AF_INET,
+ &error_num);
+ if (hp) {
+ if ((cp = strchr(hp->h_name, '.')) != NULL &&
+ strcasecmp(cp + 1, domain) == 0)
+ *cp = 0;
+ cp = hp->h_name;
+ }
+ }
+ if (cp != NULL) {
+ (void) strncpy(dst, cp, dstlen);
+ dst[dstlen - 1] = 0;
+ } else {
+ (void) inet_ntop(AF_INET, (char *)&addr, dst, dstlen);
+ }
+ if (hp != NULL)
+ freehostent(hp);
+ return (dst);
+}
+
+/*
+ * Print a non-zero IPv4 address. Print " --" if the address is zero.
+ */
+static char *
+pr_addrnz(ipaddr_t addr, char *dst, uint_t dstlen)
+{
+ if (addr == INADDR_ANY) {
+ (void) strlcpy(dst, " --", dstlen);
+ return (dst);
+ }
+ return (pr_addr(addr, dst, dstlen));
+}
+
+/*
+ * Print an IPv6 address. Remove the matching part of the domain name
+ * from the returned name.
+ */
+static char *
+pr_addr6(const struct in6_addr *addr, char *dst, uint_t dstlen)
+{
+ char *cp;
+ struct hostent *hp = NULL;
+ static char domain[MAXHOSTNAMELEN + 1];
+ static boolean_t first = B_TRUE;
+ int error_num;
+
+ if (first) {
+ first = B_FALSE;
+ if (sysinfo(SI_HOSTNAME, domain, MAXHOSTNAMELEN) != -1 &&
+ (cp = strchr(domain, '.'))) {
+ (void) strncpy(domain, cp + 1, sizeof (domain));
+ } else
+ domain[0] = 0;
+ }
+ cp = NULL;
+ if (!Nflag) {
+ hp = getipnodebyaddr((char *)addr,
+ sizeof (struct in6_addr), AF_INET6, &error_num);
+ if (hp) {
+ if ((cp = strchr(hp->h_name, '.')) != NULL &&
+ strcasecmp(cp + 1, domain) == 0)
+ *cp = 0;
+ cp = hp->h_name;
+ }
+ }
+ if (cp != NULL) {
+ (void) strncpy(dst, cp, dstlen);
+ dst[dstlen - 1] = 0;
+ } else {
+ (void) inet_ntop(AF_INET6, (void *)addr, dst, dstlen);
+ }
+ if (hp != NULL)
+ freehostent(hp);
+ return (dst);
+}
+
+/* For IPv4 masks */
+static char *
+pr_mask(uint_t addr, char *dst, uint_t dstlen)
+{
+ uint8_t *ip_addr = (uint8_t *)&addr;
+
+ (void) snprintf(dst, dstlen, "%d.%d.%d.%d",
+ ip_addr[0], ip_addr[1], ip_addr[2], ip_addr[3]);
+ return (dst);
+}
+
+/*
+ * For ipv6 masks format is : dest/mask
+ * Does not print /128 to save space in printout. H flag carries this notion.
+ */
+static char *
+pr_prefix6(struct in6_addr *addr, uint_t prefixlen, char *dst, uint_t dstlen)
+{
+ char *cp;
+
+ if (IN6_IS_ADDR_UNSPECIFIED(addr) && prefixlen == 0) {
+ (void) strncpy(dst, "default", dstlen);
+ dst[dstlen - 1] = 0;
+ return (dst);
+ }
+
+ (void) pr_addr6(addr, dst, dstlen);
+ if (prefixlen != IPV6_ABITS) {
+ /* How much room is left? */
+ cp = strchr(dst, '\0');
+ if (dst + dstlen > cp) {
+ dstlen -= (cp - dst);
+ (void) snprintf(cp, dstlen, "/%d", prefixlen);
+ }
+ }
+ return (dst);
+}
+
+/* Print IPv4 address and port */
+static char *
+pr_ap(uint_t addr, uint_t port, char *proto,
+ char *dst, uint_t dstlen)
+{
+ char *cp;
+
+ if (addr == INADDR_ANY) {
+ (void) strncpy(dst, " *", dstlen);
+ dst[dstlen - 1] = 0;
+ } else {
+ (void) pr_addr(addr, dst, dstlen);
+ }
+ /* How much room is left? */
+ cp = strchr(dst, '\0');
+ if (dst + dstlen > cp + 1) {
+ *cp++ = '.';
+ dstlen -= (cp - dst);
+ dstlen--;
+ (void) portname(port, proto, cp, dstlen);
+ }
+ return (dst);
+}
+
+/* Print IPv6 address and port */
+static char *
+pr_ap6(const in6_addr_t *addr, uint_t port, char *proto,
+ char *dst, uint_t dstlen)
+{
+ char *cp;
+
+ if (IN6_IS_ADDR_UNSPECIFIED(addr)) {
+ (void) strncpy(dst, " *", dstlen);
+ dst[dstlen - 1] = 0;
+ } else {
+ (void) pr_addr6(addr, dst, dstlen);
+ }
+ /* How much room is left? */
+ cp = strchr(dst, '\0');
+ if (dst + dstlen + 1 > cp) {
+ *cp++ = '.';
+ dstlen -= (cp - dst);
+ dstlen--;
+ (void) portname(port, proto, cp, dstlen);
+ }
+ return (dst);
+}
+
+/*
+ * Return the name of the network whose address is given. The address is
+ * assumed to be that of a net or subnet, not a host.
+ */
+static char *
+pr_net(uint_t addr, uint_t mask, char *dst, uint_t dstlen)
+{
+ char *cp = NULL;
+ struct netent *np = NULL;
+ struct hostent *hp = NULL;
+ uint_t net;
+ int subnetshift;
+ int error_num;
+
+ if (addr == INADDR_ANY && mask == INADDR_ANY) {
+ (void) strncpy(dst, "default", dstlen);
+ dst[dstlen - 1] = 0;
+ return (dst);
+ }
+
+ if (!Nflag && addr) {
+ if (mask == 0) {
+ if (IN_CLASSA(addr)) {
+ mask = (uint_t)IN_CLASSA_NET;
+ subnetshift = 8;
+ } else if (IN_CLASSB(addr)) {
+ mask = (uint_t)IN_CLASSB_NET;
+ subnetshift = 8;
+ } else {
+ mask = (uint_t)IN_CLASSC_NET;
+ subnetshift = 4;
+ }
+ /*
+ * If there are more bits than the standard mask
+ * would suggest, subnets must be in use. Guess at
+ * the subnet mask, assuming reasonable width subnet
+ * fields.
+ */
+ while (addr & ~mask)
+ /* compiler doesn't sign extend! */
+ mask = (mask | ((int)mask >> subnetshift));
+ }
+ net = addr & mask;
+ while ((mask & 1) == 0)
+ mask >>= 1, net >>= 1;
+ np = getnetbyaddr(net, AF_INET);
+ if (np && np->n_net == net)
+ cp = np->n_name;
+ else {
+ /*
+ * Look for subnets in hosts map.
+ */
+ hp = getipnodebyaddr((char *)&addr, sizeof (uint_t),
+ AF_INET, &error_num);
+ if (hp)
+ cp = hp->h_name;
+ }
+ }
+ if (cp != NULL) {
+ (void) strncpy(dst, cp, dstlen);
+ dst[dstlen - 1] = 0;
+ } else {
+ (void) inet_ntop(AF_INET, (char *)&addr, dst, dstlen);
+ }
+ if (hp != NULL)
+ freehostent(hp);
+ return (dst);
+}
+
+/*
+ * Return the name of the network whose address is given.
+ * The address is assumed to be a host address.
+ */
+static char *
+pr_netaddr(uint_t addr, uint_t mask, char *dst, uint_t dstlen)
+{
+ char *cp = NULL;
+ struct netent *np = NULL;
+ struct hostent *hp = NULL;
+ uint_t net;
+ uint_t netshifted;
+ int subnetshift;
+ struct in_addr in;
+ int error_num;
+ uint_t nbo_addr = addr; /* network byte order */
+
+ addr = ntohl(addr);
+ mask = ntohl(mask);
+ if (addr == INADDR_ANY && mask == INADDR_ANY) {
+ (void) strncpy(dst, "default", dstlen);
+ dst[dstlen - 1] = 0;
+ return (dst);
+ }
+
+ /* Figure out network portion of address (with host portion = 0) */
+ if (addr) {
+ /* Try figuring out mask if unknown (all 0s). */
+ if (mask == 0) {
+ if (IN_CLASSA(addr)) {
+ mask = (uint_t)IN_CLASSA_NET;
+ subnetshift = 8;
+ } else if (IN_CLASSB(addr)) {
+ mask = (uint_t)IN_CLASSB_NET;
+ subnetshift = 8;
+ } else {
+ mask = (uint_t)IN_CLASSC_NET;
+ subnetshift = 4;
+ }
+ /*
+ * If there are more bits than the standard mask
+ * would suggest, subnets must be in use. Guess at
+ * the subnet mask, assuming reasonable width subnet
+ * fields.
+ */
+ while (addr & ~mask)
+ /* compiler doesn't sign extend! */
+ mask = (mask | ((int)mask >> subnetshift));
+ }
+ net = netshifted = addr & mask;
+ while ((mask & 1) == 0)
+ mask >>= 1, netshifted >>= 1;
+ }
+ else
+ net = netshifted = 0;
+
+ /* Try looking up name unless -n was specified. */
+ if (!Nflag) {
+ np = getnetbyaddr(netshifted, AF_INET);
+ if (np && np->n_net == netshifted)
+ cp = np->n_name;
+ else {
+ /*
+ * Look for subnets in hosts map.
+ */
+ hp = getipnodebyaddr((char *)&nbo_addr, sizeof (uint_t),
+ AF_INET, &error_num);
+ if (hp)
+ cp = hp->h_name;
+ }
+
+ if (cp != NULL) {
+ (void) strncpy(dst, cp, dstlen);
+ dst[dstlen - 1] = 0;
+ if (hp != NULL)
+ freehostent(hp);
+ return (dst);
+ }
+ /*
+ * No name found for net: fallthru and return in decimal
+ * dot notation.
+ */
+ }
+
+ in.s_addr = htonl(net);
+ (void) inet_ntop(AF_INET, (char *)&in, dst, dstlen);
+ if (hp != NULL)
+ freehostent(hp);
+ return (dst);
+}
+
+
+/*
+ * Return the standard IPv4 classess host or network identifier.
+ *
+ * Returns "default" for the default route.
+ * Returns "x.x.x.x" or host name if mask is 255.255.255.255.
+ * Returns "x.x.x.x/y" (y is bit count) if mask is contiguous.
+ * Otherwise, returns "x.x.x.x/m.m.m.m" (undesirable mask).
+ *
+ * Can also return "****" if inet_ntop fails -- insufficient dst space
+ * available. (Shouldn't happen otherwise.)
+ */
+static char *
+pr_netclassless(ipaddr_t addr, ipaddr_t mask, char *dst, size_t dstlen)
+{
+ struct hostent *hp;
+ int error_num;
+ struct in_addr in;
+ char *cp;
+ int slen;
+
+ if (addr == INADDR_ANY && mask == INADDR_ANY) {
+ (void) strlcpy(dst, "default", dstlen);
+ return (dst);
+ }
+ if (mask == IP_HOST_MASK && !Nflag &&
+ (hp = getipnodebyaddr(&addr, sizeof (addr), AF_INET,
+ &error_num)) != NULL) {
+ (void) strlcpy(dst, hp->h_name, dstlen);
+ freehostent(hp);
+ return (dst);
+ }
+ in.s_addr = addr;
+ if (inet_ntop(AF_INET, &in, dst, dstlen) == NULL) {
+ (void) strlcpy(dst, "****", dstlen);
+ return (dst);
+ }
+ if (mask != IP_HOST_MASK) {
+ slen = strlen(dst);
+ cp = dst + slen;
+ dstlen -= slen;
+ if (mask == 0) {
+ /* Illegal on non-zero addresses */
+ (void) strlcpy(cp, "/0", dstlen);
+ } else if ((mask | (mask - 1)) == IP_HOST_MASK) {
+ (void) snprintf(cp, dstlen, "/%d",
+ IP_ABITS - ffs(mask) + 1);
+ } else {
+ /* Ungood; non-contiguous mask */
+ (void) pr_mask(mask, cp, dstlen);
+ }
+ }
+ return (dst);
+}
+
+/*
+ * Return the filter mode as a string:
+ * 1 => "INCLUDE"
+ * 2 => "EXCLUDE"
+ * otherwise "<unknown>"
+ */
+static char *
+fmodestr(uint_t fmode)
+{
+ switch (fmode) {
+ case 1:
+ return ("INCLUDE");
+ case 2:
+ return ("EXCLUDE");
+ default:
+ return ("<unknown>");
+ }
+}
+
+/*
+ * Pretty print a port number. If the Nflag was
+ * specified, use numbers instead of names.
+ */
+static char *
+portname(uint_t port, char *proto, char *dst, uint_t dstlen)
+{
+ struct servent *sp = NULL;
+
+ if (!Nflag && port)
+ sp = getservbyport(htons(port), proto);
+ if (sp || port == 0)
+ (void) snprintf(dst, dstlen, "%.*s", MAXHOSTNAMELEN,
+ sp ? sp->s_name : "*");
+ else
+ (void) snprintf(dst, dstlen, "%d", port);
+ dst[dstlen - 1] = 0;
+ return (dst);
+}
+
+/*PRINTFLIKE2*/
+void
+fail(int do_perror, char *message, ...)
+{
+ va_list args;
+
+ va_start(args, message);
+ (void) fputs("netstat: ", stderr);
+ (void) vfprintf(stderr, message, args);
+ va_end(args);
+ if (do_perror)
+ (void) fprintf(stderr, ": %s", strerror(errno));
+ (void) fputc('\n', stderr);
+ exit(2);
+}
+
+/*
+ * Return value of named statistic for given kstat_named kstat;
+ * return 0LL if named statistic is not in list (use "ll" as a
+ * type qualifier when printing 64-bit int's with printf() )
+ */
+static uint64_t
+kstat_named_value(kstat_t *ksp, char *name)
+{
+ kstat_named_t *knp;
+ uint64_t value;
+
+ if (ksp == NULL)
+ return (0LL);
+
+ knp = kstat_data_lookup(ksp, name);
+ if (knp == NULL)
+ return (0LL);
+
+ switch (knp->data_type) {
+ case KSTAT_DATA_INT32:
+ case KSTAT_DATA_UINT32:
+ value = (uint64_t)(knp->value.ui32);
+ break;
+ case KSTAT_DATA_INT64:
+ case KSTAT_DATA_UINT64:
+ value = knp->value.ui64;
+ break;
+ default:
+ value = 0LL;
+ break;
+ }
+
+ return (value);
+}
+
+kid_t
+safe_kstat_read(kstat_ctl_t *kc, kstat_t *ksp, void *data)
+{
+ kid_t kstat_chain_id = kstat_read(kc, ksp, data);
+
+ if (kstat_chain_id == -1)
+ fail(1, "kstat_read(%p, '%s') failed", (void *)kc,
+ ksp->ks_name);
+ return (kstat_chain_id);
+}
+
+/*
+ * Parse a list of IRE flag characters into a bit field.
+ */
+static uint_t
+flag_bits(const char *arg)
+{
+ const char *cp;
+ uint_t val;
+
+ if (*arg == '\0')
+ fatal(1, "missing flag list\n");
+
+ val = 0;
+ while (*arg != '\0') {
+ if ((cp = strchr(flag_list, *arg)) == NULL)
+ fatal(1, "%c: illegal flag\n", *arg);
+ val |= 1 << (cp - flag_list);
+ arg++;
+ }
+ return (val);
+}
+
+/*
+ * Handle -f argument. Validate input format, sort by keyword, and
+ * save off digested results.
+ */
+static void
+process_filter(char *arg)
+{
+ int idx;
+ int klen = 0;
+ char *cp, *cp2;
+ int val;
+ filter_t *newf;
+ struct hostent *hp;
+ int error_num;
+ uint8_t *ucp;
+ int maxv;
+
+ /* Look up the keyword first */
+ if (strchr(arg, ':') == NULL) {
+ idx = FK_AF;
+ } else {
+ for (idx = 0; idx < NFILTERKEYS; idx++) {
+ klen = strlen(filter_keys[idx]);
+ if (strncmp(filter_keys[idx], arg, klen) == 0 &&
+ arg[klen] == ':')
+ break;
+ }
+ if (idx >= NFILTERKEYS)
+ fatal(1, "%s: unknown filter keyword\n", arg);
+
+ /* Advance past keyword and separator. */
+ arg += klen + 1;
+ }
+
+ if ((newf = malloc(sizeof (*newf))) == NULL) {
+ perror("filter");
+ exit(1);
+ }
+ switch (idx) {
+ case FK_AF:
+ if (strcmp(arg, "inet") == 0) {
+ newf->u.f_family = AF_INET;
+ } else if (strcmp(arg, "inet6") == 0) {
+ newf->u.f_family = AF_INET6;
+ } else if (strcmp(arg, "unix") == 0) {
+ newf->u.f_family = AF_UNIX;
+ } else {
+ newf->u.f_family = strtol(arg, &cp, 0);
+ if (arg == cp || *cp != '\0')
+ fatal(1, "%s: unknown address family.\n", arg);
+ }
+ break;
+
+ case FK_INIF:
+ case FK_OUTIF:
+ if (strcmp(arg, "none") == 0) {
+ newf->u.f_ifname = NULL;
+ break;
+ }
+ if (strcmp(arg, "any") == 0) {
+ newf->u.f_ifname = "";
+ break;
+ }
+ val = strtol(arg, &cp, 0);
+ if (val <= 0 || arg == cp || cp[0] != '\0') {
+ if ((val = if_nametoindex(arg)) == 0) {
+ perror(arg);
+ exit(1);
+ }
+ }
+ newf->u.f_ifname = arg;
+ break;
+
+ case FK_SRC:
+ case FK_DST:
+ V4MASK_TO_V6(IP_HOST_MASK, newf->u.a.f_mask);
+ if (strcmp(arg, "any") == 0) {
+ /* Special semantics; any address *but* zero */
+ newf->u.a.f_address = NULL;
+ (void) memset(&newf->u.a.f_mask, 0,
+ sizeof (newf->u.a.f_mask));
+ break;
+ }
+ if (strcmp(arg, "none") == 0) {
+ newf->u.a.f_address = NULL;
+ break;
+ }
+ if ((cp = strrchr(arg, '/')) != NULL)
+ *cp++ = '\0';
+ hp = getipnodebyname(arg, AF_INET6, AI_V4MAPPED|AI_ALL,
+ &error_num);
+ if (hp == NULL)
+ fatal(1, "%s: invalid or unknown host address\n", arg);
+ newf->u.a.f_address = hp;
+ if (cp == NULL) {
+ V4MASK_TO_V6(IP_HOST_MASK, newf->u.a.f_mask);
+ } else {
+ val = strtol(cp, &cp2, 0);
+ if (cp != cp2 && cp2[0] == '\0') {
+ /*
+ * If decode as "/n" works, then translate
+ * into a mask.
+ */
+ if (hp->h_addr_list[0] != NULL &&
+ /* LINTED: (note 1) */
+ IN6_IS_ADDR_V4MAPPED((in6_addr_t
+ *)hp->h_addr_list[0])) {
+ maxv = IP_ABITS;
+ } else {
+ maxv = IPV6_ABITS;
+ }
+ if (val < 0 || val >= maxv)
+ fatal(1, "%d: not in range 0 to %d\n",
+ val, maxv - 1);
+ if (maxv == IP_ABITS)
+ val += IPV6_ABITS - IP_ABITS;
+ ucp = newf->u.a.f_mask.s6_addr;
+ while (val >= 8)
+ *ucp++ = 0xff, val -= 8;
+ *ucp++ = (0xff << (8 - val)) & 0xff;
+ while (ucp < newf->u.a.f_mask.s6_addr +
+ sizeof (newf->u.a.f_mask.s6_addr))
+ *ucp++ = 0;
+ /* Otherwise, try as numeric address */
+ } else if (inet_pton(AF_INET6,
+ cp, &newf->u.a.f_mask) <= 0) {
+ fatal(1, "%s: illegal mask format\n", cp);
+ }
+ }
+ break;
+
+ case FK_FLAGS:
+ if (*arg == '+') {
+ newf->u.f.f_flagset = flag_bits(arg + 1);
+ newf->u.f.f_flagclear = 0;
+ } else if (*arg == '-') {
+ newf->u.f.f_flagset = 0;
+ newf->u.f.f_flagclear = flag_bits(arg + 1);
+ } else {
+ newf->u.f.f_flagset = flag_bits(arg);
+ newf->u.f.f_flagclear = ~newf->u.f.f_flagset;
+ }
+ break;
+
+ default:
+ assert(0);
+ }
+ newf->f_next = filters[idx];
+ filters[idx] = newf;
+}
+
+/* Determine if user wants this address family printed. */
+static boolean_t
+family_selected(int family)
+{
+ const filter_t *fp;
+
+ if (v4compat && family == AF_INET6)
+ return (B_FALSE);
+ if ((fp = filters[FK_AF]) == NULL)
+ return (B_TRUE);
+ while (fp != NULL) {
+ if (fp->u.f_family == family)
+ return (B_TRUE);
+ fp = fp->f_next;
+ }
+ return (B_FALSE);
+}
+
+/*
+ * print the usage line
+ */
+static void
+usage(char *cmdname)
+{
+ (void) fprintf(stderr, "usage: %s [-anv] [-f address_family]\n",
+ cmdname);
+ (void) fprintf(stderr, " %s [-n] [-f address_family] "
+ "[-P protocol] [-g | -p | -s [interval [count]]]\n", cmdname);
+ (void) fprintf(stderr, " %s -m [-v] "
+ "[interval [count]]\n", cmdname);
+ (void) fprintf(stderr, " %s -i [-I interface] [-an] "
+ "[-f address_family] [interval [count]]\n", cmdname);
+ (void) fprintf(stderr, " %s -r [-anv] "
+ "[-f address_family|filter]\n", cmdname);
+ (void) fprintf(stderr, " %s -M [-ns] [-f address_family]\n",
+ cmdname);
+ (void) fprintf(stderr, " %s -D [-I interface] "
+ "[-f address_family]\n", cmdname);
+ exit(EXIT_FAILURE);
+}
+
+/*
+ * fatal: print error message to stderr and
+ * call exit(errcode)
+ */
+/*PRINTFLIKE2*/
+static void
+fatal(int errcode, char *format, ...) {
+ va_list argp;
+
+ if (format == NULL)
+ return;
+
+ va_start(argp, format);
+ (void) vfprintf(stderr, format, argp);
+ va_end(argp);
+
+ exit(errcode);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/netstat/unix.c b/usr/src/cmd/cmd-inet/usr.bin/netstat/unix.c
new file mode 100644
index 0000000000..5e7afa8e3d
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/netstat/unix.c
@@ -0,0 +1,246 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2001 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * code for netstat's -k option
+ *
+ * NOTES:
+ * 1. A comment "LINTED: (note 1)" appears before certain lines where
+ * lint would have complained, "pointer cast may result in improper
+ * alignment". These are lines where lint had suspected potential
+ * improper alignment of a data structure; in each such situation
+ * we have relied on the kernel guaranteeing proper alignment.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <strings.h>
+#include <string.h>
+#include <kstat.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stream.h>
+#include <sys/tiuser.h>
+#include <sys/socketvar.h>
+#include <sys/sysmacros.h>
+
+static char *typetoname(t_scalar_t);
+static void print_kn(kstat_t *);
+static char *nextstr(char *);
+extern void fail(int, char *, ...);
+
+#define NALEN 8 /* nulladdress string length */
+
+/*
+ * Print a summary of connections related to a unix protocol.
+ */
+void
+unixpr(kstat_ctl_t *kc)
+{
+ kstat_t *ksp;
+
+ if (kc == NULL) { /* sanity check. */
+ fail(0, "unixpr: No kstat");
+ exit(3);
+ }
+
+ /* find the sockfs kstat: */
+ if ((ksp = kstat_lookup(kc, "sockfs", 0, "sock_unix_list")) ==
+ (kstat_t *)NULL) {
+ fail(0, "kstat_data_lookup failed\n");
+ }
+
+ if (kstat_read(kc, ksp, NULL) == -1) {
+ fail(0, "kstat_read failed for sock_unix_list\n");
+ }
+
+ print_kn(ksp);
+}
+
+static void
+print_kn(kstat_t *ksp)
+{
+ int i;
+ struct sockinfo *psi; /* ptr to current sockinfo */
+ char *pas; /* ptr to string-format addrs */
+ char *nullstr; /* ptr to null string */
+ char *conn_vp;
+ char *local_vp;
+
+ if (ksp->ks_ndata == 0) {
+ return; /* no AF_UNIX sockets found */
+ }
+
+ /*
+ * Having ks_data set with ks_data == NULL shouldn't happen;
+ * If it does, the sockfs kstat is seriously broken.
+ */
+ if ((psi = ksp->ks_data) == NULL) {
+ fail(0, "print_kn: no kstat data\n");
+ }
+
+ /* set pas to the address strings which are after the sockinfo */
+ pas = &((char *)psi)[sizeof (struct sockinfo)];
+
+ /* Create a string of NALEN "0"'s for NULL addresses. */
+ if ((nullstr = calloc(1, NALEN)) == NULL) {
+ fail(0, "print_kn: out of memory\n");
+ }
+ (void) memset((void *)nullstr, '0', NALEN);
+
+ (void) printf("\nActive UNIX domain sockets\n");
+ (void) printf("%-8.8s %-10.10s %8.8s %8.8s "
+ "Local Addr Remote Addr\n",
+ "Address", "Type", "Vnode", "Conn");
+
+ /* for each sockinfo structure, display what we need: */
+ for (i = 0; i < ksp->ks_ndata; i++) {
+ /* display sonode's address. 1st string after sockinfo: */
+ pas = &(((char *)psi)[sizeof (struct sockinfo)]);
+ (void) printf("%s ", pas);
+
+ (void) printf("%-10.10s ", typetoname(psi->si_serv_type));
+
+ /* laddr.sou_vp: 2nd string after sockinfo: */
+ pas = nextstr(pas);
+
+ local_vp = conn_vp = nullstr;
+
+ if ((psi->si_state & SS_ISBOUND) &&
+ (psi->si_ux_laddr_sou_magic == SOU_MAGIC_EXPLICIT)) {
+ local_vp = pas;
+ }
+
+ /* faddr.sou_vp: 3rd string after sockinfo: */
+ pas = nextstr(pas);
+ if ((psi->si_state & SS_ISCONNECTED) &&
+ (psi->si_ux_faddr_sou_magic == SOU_MAGIC_EXPLICIT)) {
+ conn_vp = pas;
+ }
+
+ (void) printf("%s %s ", local_vp, conn_vp);
+
+ /* laddr.soa_sa: */
+ if ((psi->si_state & SS_ISBOUND) &&
+ strlen(psi->si_laddr_sun_path) != 0 &&
+ psi->si_laddr_soa_len != 0) {
+ if (psi->si_state & SS_FADDR_NOXLATE) {
+ (void) printf(" (socketpair) ");
+ } else {
+ if (psi->si_laddr_soa_len >
+ sizeof (psi->si_laddr_family))
+ (void) printf("%s ",
+ psi->si_laddr_sun_path);
+ else
+ (void) printf(" ");
+ }
+ } else
+ (void) printf(" ");
+
+ /* faddr.soa_sa: */
+ if ((psi->si_state & SS_ISCONNECTED) &&
+ strlen(psi->si_faddr_sun_path) != 0 &&
+ psi->si_faddr_soa_len != 0) {
+
+ if (psi->si_state & SS_FADDR_NOXLATE) {
+ (void) printf(" (socketpair) ");
+ } else {
+ if (psi->si_faddr_soa_len >
+ sizeof (psi->si_faddr_family))
+ (void) printf("%s ",
+ psi->si_faddr_sun_path);
+ else
+ (void) printf(" ");
+ }
+ } else
+ (void) printf(" ");
+
+ (void) printf("\n");
+
+ /* if si_size didn't get filled in, then we're done */
+ if (psi->si_size == 0 ||
+ !IS_P2ALIGNED(psi->si_size, sizeof (psi))) {
+ break;
+ }
+
+ /* LINTED: (note 1) */
+ psi = (struct sockinfo *)&(((char *)psi)[psi->si_size]);
+ }
+}
+
+static char *
+typetoname(t_scalar_t type)
+{
+ switch (type) {
+ case T_CLTS:
+ return ("dgram");
+
+ case T_COTS:
+ return ("stream");
+
+ case T_COTS_ORD:
+ return ("stream-ord");
+
+ default:
+ return ("");
+ }
+}
+
+/*
+ * nextstr(): find the beginning of a next string.
+ * The sockfs kstat left-justifies each address string, leaving
+ * null's between the strings. Since we don't necessarily know
+ * the sizes of pointers in the kernel, we need to skip over these
+ * nulls in order to get to the start of the next string.
+ */
+static char *
+nextstr(char *pas)
+{
+ char *next;
+
+ for (next = &pas[strlen(pas) + 1]; *next == NULL; ) {
+ next++;
+ }
+
+ return (next);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppd/Makefile b/usr/src/cmd/cmd-inet/usr.bin/pppd/Makefile
new file mode 100644
index 0000000000..59d95063e2
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppd/Makefile
@@ -0,0 +1,130 @@
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# cmd/cmd-inet/usr.bin/pppd/Makefile
+#
+
+include ../../../Makefile.cmd
+include Makefile.def
+
+PROG= pppd
+SUBDIRS= plugins
+OBJS= auth.o ccp.o chap.o demand.o fsm.o ipcp.o ipv6cp.o \
+ lcp.o magic.o main.o options.o sys-solaris.o upap.o utils.o \
+ multilink.o cbcp.o
+
+# Object tdb.o used only for Multilink; not supported yet.
+
+all:= TARGET= all
+install:= TARGET= install
+clean:= TARGET= clean
+clobber:= TARGET= clobber
+lint:= TARGET= lint
+
+LDLIBS += -lpam -lmd5 -lsocket -lnsl
+
+#
+# We need absolute path to /etc/ppp/plugins and /usr/lib/inet/ppp, not
+# that of the proto area
+#
+LDFLAGS += -R$(ETCPPPPLUGINDIR_ABS) -R$(LIBPPPPLUGINDIR_ABS)
+
+CPPFLAGS += -DPLUGIN -DSVR4 -DSOL2 -DINET6
+CPPFLAGS += -D_PATH_VARRUN='"/var/run/"'
+CPPFLAGS += -DNEGOTIATE_FCS -DCBCP_SUPPORT -DALLOW_PAM -DHAS_SHADOW
+CPPFLAGS += -DHAVE_MMAP -DCOMP_TUNE -DMUX_FRAME
+$(NOT_RELEASE_BUILD)CPPFLAGS += -DDEBUG
+
+.KEEP_STATE:
+
+.PARALLEL: $(SUBDIRS)
+
+all: $(PROG) $(SUBDIRS)
+
+# MS-CHAP support
+CPPFLAGS += -DHAVE_CRYPT_H -DUSE_CRYPT
+CPPFLAGS += -DCHAPMS -DMSLANMAN
+CPPFLAGS += -DCHAPMSV2
+OBJS += chap_ms.o md4.o sha1.o
+EXOBJS += mschap_test.o
+CLOBBERFILES += mschap_test
+
+# This is used *only* for testing the portability of the libraries
+# required for MS-CHAPv1. It is not needed in any normal system and
+# is not built by default.
+mschap_test: mschap_test.o chap_ms.o md4.o sha1.o
+ $(LINK.c) -o mschap_test mschap_test.o chap_ms.o md4.o sha1.o \
+ $(LDFLAGS)
+ @echo "Run with 'mschap_test 00000000000000000000000000000000 hello'"
+ @echo
+ @echo "Output should be:"
+ @echo
+ @echo " MS-CHAPv1 with LAN Manager -- 49 bytes:"
+ @echo " C9 CA EE 9B 1C A7 87 04"
+ @echo " 79 36 8C 55 AB 88 EC 5A"
+ @echo " 57 E9 A1 B7 95 40 C3 74"
+ @echo " F4 D9 9D AF 82 64 DC 3C"
+ @echo " 53 F9 BC 92 14 B5 5D 9E"
+ @echo " 78 C4 21 48 9D B7 A8 B4"
+ @echo " 01"
+ @echo " MS-CHAPv2 -- 49 bytes:"
+ @echo " xx xx xx xx xx xx xx xx"
+ @echo " xx xx xx xx xx xx xx xx"
+ @echo " 00 00 00 00 00 00 00 00"
+ @echo " xx xx xx xx xx xx xx xx"
+ @echo " xx xx xx xx xx xx xx xx"
+ @echo " xx xx xx xx xx xx xx xx"
+ @echo " 00"
+
+LINTFLAGS += -erroff=E_NAME_USED_NOT_DEF2
+LINTFLAGS += -erroff=E_GLOBAL_COULD_BE_STATIC2
+LINTFLAGS += -erroff=E_NAME_DEF_NOT_USED2
+
+SRCS= $(OBJS:%.o=%.c)
+
+.PARALLEL: $(OBJS)
+
+$(PROG): $(OBJS)
+ $(LINK.c) $(OBJS) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+$(ROOTPROG):= FILEMODE = 04555
+$(ROOTPROG):= OWNER = root
+
+ASPPP2PPPD= $(ROOTUSRSBIN)/asppp2pppd
+$(ASPPP2PPPD):= OWNER=root
+$(ASPPP2PPPD):= GROUP=bin
+$(ASPPP2PPPD):= FILEMODE = 0550
+
+install: $(PROG) .WAIT $(SUBDIRS) $(ROOTPROG) $(ETCPPPDIR) $(ASPPP2PPPD)
+
+$(ETCPPPDIR)/%: %
+ $(INS.file)
+
+$(ETCPPPDIR):
+ $(INS.dir)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
+
+LINTOBJS=$(OBJS:%.o=%.ln)
+CLOBBERFILES += $(LINTOBJS)
+
+clean: $(SUBDIRS)
+ $(RM) $(OBJS) $(EXOBJS)
+
+sha1.ln:= LINTFLAGS += -erroff=E_SUPPRESSION_DIRECTIVE_UNUSED
+
+# Not using the default lint target here so that we can disable
+# warnings per module as needed.
+lint: $(SUBDIRS) $(LINTOBJS)
+ $(LINT.c) $(LINTOBJS) $(LDLIBS)
+
+clobber: $(SUBDIRS)
+
+include ../../../Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppd/Makefile.def b/usr/src/cmd/cmd-inet/usr.bin/pppd/Makefile.def
new file mode 100644
index 0000000000..fead1d7ece
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppd/Makefile.def
@@ -0,0 +1,13 @@
+#
+#pragma ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 2000 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+# cmd/cmd-inet/usr.bin/pppd/Makefile.def
+#
+
+ETCPPPDIR= $(ROOTETC)/ppp
+ETCPPPPLUGINDIR_ABS= /etc/ppp/plugins
+LIBPPPPLUGINDIR= $(ROOT)/usr/lib/inet/ppp
+LIBPPPPLUGINDIR_ABS= /usr/lib/inet/ppp
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppd/asppp2pppd b/usr/src/cmd/cmd-inet/usr.bin/pppd/asppp2pppd
new file mode 100644
index 0000000000..408690c18a
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppd/asppp2pppd
@@ -0,0 +1,1634 @@
+#!/usr/bin/perl
+
+# This utility translates from aspppd configuration to Solaris PPP 4.0
+# (or ANU ppp-2.4.0; aka pppd). It can also revert to previous aspppd
+# configuration (discarding the pppd configuration), but does not
+# translate new configuration files into old.
+#
+# This script provides only a suggested translation for your existing
+# aspppd configuration. You will need to evaluate for yourself
+# whether the translation is appropriate for your operating
+# environment.
+
+# Copyright (c) 2000 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+# Steps in translation:
+# - parse /etc/asppp.cf
+# - check for aspppls in /etc/passwd (or NIS or NIS+)
+# - read in current /etc/ppp/options configuration file
+# - read list of configured serial ports from pmadm
+# - read in UUCP configuration files
+# - create translated configuration
+# - write files back out
+
+# Known issues:
+# - translation with point-to-multipoint is incomplete
+
+use Getopt::Std;
+use Fcntl;
+use POSIX qw(tmpnam ENOSYS);
+use Sys::Hostname;
+
+# Secure the path if we're running under RBAC.
+$ENV{PATH} = ( "/bin", "/sbin", "/usr/bin", "/usr/sbin", "/usr/ucb" )
+ if $< != $>;
+
+# General path names that can be configured.
+local($rootetc) = "/etc/";
+local($passwd) = $rootetc . "passwd";
+local($passwdlck) = $rootetc . ".pwd.lock";
+local($asfile) = $rootetc . "asppp.cf";
+local($astemp) = $rootetc . "asppp.temp.cf";
+local($asmoved) = $rootetc . "asppp.saved.cf";
+local($uucpdir) = $rootetc . "uucp/";
+local($Devices) = $uucpdir . "Devices";
+local($Devconfig) = $uucpdir . "Devconfig";
+local($Dialers) = $uucpdir . "Dialers";
+local($Dialcodes) = $uucpdir . "Dialcodes";
+local($Limits) = $uucpdir . "Limits";
+local($Sysfiles) = $uucpdir . "Sysfiles";
+local($Systems) = $uucpdir . "Systems";
+local($pppdir) = $rootetc . "ppp/";
+local($options) = $pppdir . "options";
+local($ttyprefix) = $pppdir . "options.";
+local($peersdir) = $pppdir . "peers/";
+local($initd) = $rootetc . "init.d/";
+local($asctl) = $initd . "asppp";
+local($pppdctl) = $initd . "pppd";
+local($sedpasswd) = "/tmp/sed-passwd";
+
+# Fake asppp keyword used to keep track of dial-in paths.
+local($isdialin) = "-is-dial-in";
+
+# Limits and Sysfiles are keyed on "service=".
+# Devconfig is keyed on "service=" and "device=".
+# Dialcodes, Dialers, Systems, and Devices are single keyword files.
+# Devices alone may have multiple entries for a given key.
+
+# Internal data structures
+local(@sysfiles,@limits,@devconfig);
+local(@sysdefault) = ( "systems=" . $Systems, "dialers=" . $Dialers,
+ "devices=" . $Devices );
+local(@limitdefault) = ( "max=-1" );
+local(@devdefault) = ( "pop=", "push=", "connecttime=-1", "expecttime=-1",
+ "msgtime=-1" );
+
+# List of keywords for which ifconfig takes an additional parameter.
+local($ifconfigtakes) = (
+ addif => 1,
+ removeif => 1,
+ auth_algs => 1,
+ encr_algs => 1,
+ encr_auth_algs => 1,
+ broadcast => 1,
+ destination => 1,
+ index => 1,
+ metric => 1,
+ modinsert => 1,
+ modremove => 1,
+ mtu => 1,
+ netmask => 1,
+ set => 1,
+ subnet => 1,
+ tdst => 1,
+ tsrc => 1,
+ wait => 1,
+
+# These are keywords, but do not take an additional parameter.
+ ether => 0,
+ inet => 0,
+ inet6 => 0,
+ arp => 0,
+ -arp => 0,
+ auto-revarp => 0,
+ modlist => 0,
+ plumb => 0,
+ unplumb => 0,
+ private => 0,
+ -private => 0,
+ nud => 0,
+ -nud => 0,
+ trailers => 0,
+ -trailers => 0,
+ up => 0,
+ down => 0,
+ xmit => 0,
+ -xmit => 0,
+ auto-dhcp => 0,
+ dhcp => 0,
+ primary => 0,
+ drop => 0,
+ extend => 0,
+ inform => 0,
+ ping => 0,
+ release => 0,
+ start => 0,
+ status => 0
+);
+
+# print number of something in English.
+sub nof
+{
+ local($num, $item, @rest) = @_;
+ print "No ", $item, "s", @rest if $num == 0;
+ print "1 ", $item, @rest if $num == 1;
+ print $num, " ", $item, "s", @rest if $num > 1;
+}
+
+# ask a yes or no question.
+sub yesno
+{
+ local ($query, $default) = @_;
+ local ($ans, $defans);
+
+ return $default unless (-t STDIN) && (-t STDOUT) && !$opt_n;
+ $defans = $default ? "Yn" : "yN";
+ while (1) {
+ print $query, " [", $defans, "]? ";
+ chomp($ans = <STDIN>);
+ return $default unless $ans;
+ return 1 if $ans =~ /^[Yy1Tt]/;
+ return 0 if $ans =~ /^[Nn0Ff]/;
+ print "Please enter 'y' or 'n'.\n";
+ }
+}
+
+# Put quotes around a string, if necessary.
+# The tests here aren't perfect -- they think that \\\' isn't an
+# escaped quote -- but they're good enough.
+sub requote
+{
+ local($_) = @_;
+ if (/^$/) {
+ "\"\"";
+ } elsif (/^'/ || /[^\\]'/ || /\\\\'/) {
+# Has unescaped quotes; must use " or redo quoting.
+ if (/^"/ || /[^\\]"/ || /\\\\"/) {
+# Both kinds of quotes; redo the quoting.
+ s/^"/\\"/;
+ s/([^\\]|\\\\)"/$1\\"/g;
+ }
+ "\"" . $_ . "\"";
+ } elsif (/^"/ || /[^\\]"/ || /\\\\"/) {
+ "'" . $_ . "'";
+ } elsif (/\s/) {
+ "\"" . $_ . "\"";
+ } else {
+ $_;
+ }
+}
+
+# Get a single line from a UUCP configuration file and return as a
+# reference to an array of words. Removes comments and escapes.
+# (This is a modified version of the standard Perl shellwords function
+# that understands C escape sequences and continuation lines.)
+# Optionally returns lead-in, source text, and trailing component
+# for editing.
+sub uucpline
+{
+ local($input, $file, $triplet) = @_;
+ local(@words,$snippet,$field,$havefield,$cont,@triparray,$maytrail);
+
+ $cont = "";
+ $maytrail = "";
+ while (<$input>) {
+ # remove leading whitespace
+ if (s/^(\s+)//) {
+ $maytrail .= $1;
+ }
+ if ($cont eq "") {
+ if (s/^(\#(.|\n)*)$//) {
+ $triparray[0] .= $maytrail . $1;
+ $maytrail = "";
+ next;
+ }
+ if (s/^(\\?\n?)$//) {
+ $triparray[0] .= $maytrail . $1;
+ $maytrail = "";
+ next;
+ }
+ $triparray[0] .= $maytrail;
+ $maytrail = "";
+ }
+ $snippet = $_;
+ if (s/^(([^\#\\]|\\.)*)\\\n$//) {
+ $maytrail .= $snippet;
+ $cont .= $1;
+ next;
+ }
+ if (/^(([^\\\#]|\\[^\#])*)(\#?(.|\n)*)$/) {
+ $_ = $maytrail . $1;
+ $maytrail = $3;
+ if (s/((\s|\n)*)$//) {
+ $maytrail = $1 . $maytrail;
+ }
+ $triparray[1] = $_;
+ }
+ $_ = $cont . $snippet;
+ $cont = "";
+ s/\n$//;
+ while ($_ ne '') {
+ for (;;) {
+ if (s/^#.*//) {
+ last;
+ } elsif (s/^"(([^"\\]|\\.)*)"//) {
+ $snippet = $1;
+ } elsif (s/^"//) {
+ warn "Unmatched double quote in $file: \"$_\n";
+ } elsif (s/^'(([^'\\]|\\.)*)'//) {
+ $snippet = $1;
+ } elsif (s/^'//) {
+ warn "Unmatched single quote in $file: '$_\n";
+ } elsif (s/^\\s//) {
+# \s works in chat, but not in the pppd option files
+ $snippet = " ";
+ } elsif (s/^(\\.)//) {
+ $snippet = $1;
+ } elsif (s/^([^\s\\'"#]+)//) {
+ $snippet = $1;
+ } else {
+ s/^\s+//;
+ last;
+ }
+ $havefield = 1;
+ $field .= $snippet;
+ }
+ push(@words, $field) if $havefield;
+ $havefield = 0;
+ $field = '';
+ }
+ last;
+ }
+ $triparray[2] .= $maytrail;
+ @$triplet = @triparray;
+ warn "Bad continuation line in $file: $cont\n" if $cont ne '';
+ \@words;
+}
+
+# Given a logical UUCP file name, return a list of all of the files
+# that should be read.
+sub uucpfiles
+{
+ local ($file) = @_;
+ local (@flist, $value) = ();
+
+ for $value (@sysfiles, @sysdefault) {
+ if ($value =~ /^$file=/i) {
+ $value =~ s/^$file=//i;
+ for $file (split /:/, $value) {
+ $file = $uucpdir . $file if $file !~ /^\//;
+ push @flist, $file;
+ }
+ last;
+ }
+ }
+ @flist;
+}
+
+# Given a file name and some key words, parse the contents of the file
+# and return a reference to a hash constructed from this. All keys
+# except the last must match exactly. The last is used to order the
+# hash.
+sub uucpkeyfile
+{
+ local($file,@keylist) = @_;
+ local($lastkey,$keyval,$words,$i,$flag,%byservice);
+
+ open(SVCFILE, '<' . $file) || return undef;
+ $lastkey = pop @keylist;
+ while (@{$words = uucpline(SVCFILE, $file)}) {
+ $flag = 1;
+ foreach $keyval (@keylist) {
+ $flag = 0;
+ $i = 0;
+ while ($i < @$words) {
+ if ($$words[$i] eq $keyval) {
+ splice @$words, $i, 1;
+ $flag = 1;
+ last;
+ }
+ $i++;
+ }
+ last unless $flag;
+ }
+ next unless $flag;
+ foreach $i (0 .. @{$words}) {
+ if (@{$words}[$i] =~ /^$lastkey(.*)/i) {
+ splice @{$words}, $i, 1;
+ $byservice{$1} = $words;
+ last;
+ }
+ }
+ }
+ close SVCFILE;
+ \%byservice;
+}
+
+# This reads a UUCP file that is keyed on the first token on each
+# line. Duplicates are not permitted; the first encountered is used
+# and the rest are silently discarded. A hash indexed on the first
+# token and containing an array of tokens in each bucket is returned.
+# Used for Dialcodes, Dialers, and Systems.
+sub uucpposfiles
+{
+ local(@files) = @_;
+ local($keyval,$words,%keyeddata);
+
+ foreach $file (@files) {
+ if (!open(POSFILE, '<' . $file)) {
+ warn "$file: $!\n";
+ next;
+ }
+ while (@{$words = uucpline(POSFILE, $file)}) {
+ $keyval = shift @{$words};
+ next if $keyeddata{$keyval};
+ $keyeddata{$keyval} = $words;
+ }
+ close POSFILE;
+ }
+ \%keyeddata;
+}
+
+# This reads a UUCP file that is keyed on the first token on each line
+# and may have duplicate entries. Each entry of the hash contains an
+# array. Each entry of that array points to an array of tokens
+# representing one parsed line. Used for the Devices file.
+sub uucpdevices
+{
+ local(@files) = @_;
+ local($keyval,$words,%keyeddata);
+
+ foreach $file (@files) {
+ if (!open(POSFILE, '<' . $file)) {
+ warn "$file: $!\n";
+ next;
+ }
+ while (@{$words = uucpline(POSFILE, $file)}) {
+ $keyval = shift @{$words};
+ push @{$keyeddata{$keyval}}, $words;
+ }
+ close POSFILE;
+ }
+ \%keyeddata;
+}
+
+# For a path defined in asppp.cf, copy over defaults, validate the
+# required options, and save in the hash to be returned.
+sub savepath
+{
+ local($paths, $options, $defref) = @_;
+ local($peer,$intf);
+
+ return if $options == $defref;
+ foreach $key (keys %$defref) {
+ $$options{$key} = $$defref{$key} unless defined($$options{$key});
+ }
+ $peer = $$options{"peer_system_name"};
+ warn("Discarded path with no peer system name.\n"), return
+ unless defined($peer);
+ $intf = $$options{"interface"};
+ warn("Missing interface on path to peer \"$peer\".\n"), return
+ unless defined($intf);
+ warn("Bad interface $intf on path to peer \"$peer\".\n"), return
+ unless $intf =~ /^ipd([0-9]+|ptp[0-9]+|ptp\*)$/;
+ warn("Missing peer IP address for point-to-multipoint path to \"",
+ $peer, "\".\n"), return
+ if $intf =~ /^ipd[0-9]+$/ && !defined($$options{"peer_ip_address"});
+ warn "Multiple definitions of path to peer \"$peer\".\n",
+ if defined($paths{$peer});
+ warn "Odd version number ", $$options{"version"},
+ " encountered in path to peer \"", $peer, "\" (ignored).\n"
+ if defined($$options{"version"}) && $$options{"version"} != 1;
+ $paths{$peer} = $options;
+}
+
+# Parse through asppp.cf. Unlike the UUCP files, lines don't matter
+# here. The parsing is modal, with "path" introducing a PPP session
+# description and "defaults" reverting back to global definitions.
+sub readaspppcf
+{
+ local($aspppcf) = @_;
+ local(%aspppdkey) = (
+ chap_name => 1,
+ chap_peer_secret => 1,
+ chap_peer_name => 1,
+ chap_secret => 1,
+ debug_level => 1,
+ default_route => 0,
+ ifconfig => -1,
+ inactivity_timeout => 1,
+ interface => 1,
+# sic; aspppd is seriously confused! ACCM isn't in IPCP.
+ ipcp_async_map => 1,
+ ipcp_compression => 1,
+ lcp_compression => 1,
+ lcp_mru => 1,
+ negotiate_address => 1,
+ pap_id => 1,
+ pap_password => 1,
+ pap_peer_id => 1,
+ pap_peer_password => 1,
+ peer_ip_address => 1,
+ peer_system_name => 1,
+ require_authentication => 2,
+ version => 1,
+ will_do_authentication => 2
+ );
+ local($words,$word,$prevword,$i,$errors,%defaults,%ifconfig,%paths);
+ local($options);
+
+ open ASPPPD, "<" . $aspppcf || die "$aspppcf: $!\n";
+ print "Reading configuration from $aspppcf\n" if $opt_v;
+ $defaults{inactivity_timeout} = 120;
+ $defaults{ipcp_compression} = "vj";
+ $defaults{lcp_compression} = "on";
+ $options = \%defaults;
+ while (@{$words = uucpline(ASPPPD, $aspppcf)}) {
+ if ($$words[0] =~ /^ifconfig$/i) {
+ warn "$prevword with missing argument ignored.\n"
+ if defined($prevword);
+ undef $prevword;
+ shift @$words; # discard 'ifconfig' keyword
+ $word = shift @$words;
+ warn("Bad interface on ifconfig $word.\n"), next
+ unless $word =~ /^ipd([0-9]+|ptp[0-9]+)$/;
+ $ifconfig{$word} = \@$words;
+ next;
+ }
+ unshift @{$words}, $prevword if defined($prevword);
+ undef $prevword;
+ while ($word = lc(shift @{$words})) {
+ $_ = $word;
+ if (/^defaults$/i) {
+ savepath(\%paths, $options, \%defaults);
+ $options = \%defaults;
+ next ;
+ }
+ if (/^path$/i) {
+ local(%pathopts);
+ savepath(\%paths, $options, \%defaults);
+ $options = \%pathopts;
+ next;
+ }
+ if (!defined($i = $aspppdkey{$word})) {
+ die "Too many errors in $aspppcf; aborting.\n"
+ if ++$errors > 5;
+ warn "Ignoring unknown keyword $word in $aspppcf\n";
+ next;
+ }
+ warn("$_ unexpected; remainder of line ignored.\n"),
+ last if $i == -1;
+ warn("Duplicate $_ in path ignored.\n"), next
+ if $options != \%defaults && defined($$options{$_});
+ $$options{$_} = 1 if $i == 0;
+ next if $i == 0;
+ $prevword = $_, last unless defined($word = shift @{$words});
+ $$options{$_} = $word if $i == 1;
+ if ($i == 2) {
+ undef $$options{$_}, next if $word =~ /^off$/;
+ $$options{$_} = $word;
+ if ($word = shift @{$words}) {
+ if ($word =~ /^(p|ch)ap$/) {
+ $$options{$_} .= " " . $word;
+ } else {
+ unshift @{$words}, $word;
+ }
+ }
+ }
+ }
+ }
+ warn "Odd trailing keyword \"$prevword\" ignored in $aspppcf\n"
+ if $prevword;
+ savepath(\%paths, $options, \%defaults);
+ die "No paths defined for aspppd.\n" if 0+(keys %paths) == 0;
+ die "No interfaces defined for aspppd.\n" if 0+(keys %ifconfig) == 0;
+ if ($opt_v) {
+ nof 0+(keys %paths), "path", " and ";
+ nof 0+(keys %ifconfig), "interface", " defined for aspppd.\n";
+ }
+ close ASPPPD;
+ ( \%ifconfig, \%paths );
+}
+
+# Read /etc/passwd (or NIS or NIS+) and return hash of users for whom
+# the default shell is aspppls. Each hash entry contains the user's
+# home directory path.
+sub readpasswd
+{
+ local(%users,@pwe);
+
+ setpwent();
+ while (@pwe = getpwent()) {
+ $users{$pwe[0]} = $pwe[7] if $pwe[8] =~ /\/aspppls$/;
+ }
+ endpwent();
+ nof 0+(keys %users), "aspppd dial in user", " found.\n"
+ if $opt_v;
+ \%users;
+}
+
+# Parse through pmadm output to find enabled serial ports.
+# Field 9 has 'I' (modem dial-out only or initialize only), 'b'
+# (bidirectional) or is blank (modem dial-in only or terminal-hardwired).
+# For that latter case, field 18 (software-carrier) has 'y'.
+# Field 3 has 'x' if disabled.
+sub getserialports
+{
+ local(%dialin, %dialout);
+
+ open PMADM, "pmadm -L|" || (warn "pmadm: $!\n", return undef);
+ while (<PMADM>) {
+ split /:/;
+ if ($_[3] !~ /x/) {
+ $dialin{$_[2]} = $_[8] if $_[9] ne "I";
+ $dialout{$_[2]} = $_[8] if $_[9] ne "";
+ }
+ }
+ close PMADM;
+ ( \%dialin, \%dialout );
+}
+
+# Convert an ifconfig statement into a local and remote address pair.
+sub ifconf_addr
+{
+ local($ifconf) = @_;
+ local($arg, $narg, $lcladdr, $remaddr);
+
+ shift @$ifconf; # lose the interface name
+ while (@$ifconf) {
+ local($arg, $narg);
+ $arg = shift @$ifconf;
+ $narg = shift @$ifconf if $ifconfigtakes{$arg};
+ if (exists($ifconfigtakes{$arg})) {
+ if ($arg eq "set") {
+ $lcladdr = $narg;
+ } elsif ($arg eq "destination") {
+ $remaddr = $narg;
+ }
+ } elsif (!defined($lcladdr)) {
+ $lcladdr = $arg;
+ } elsif (!defined($remaddr)) {
+ $remaddr = $arg;
+ }
+ }
+ ( $lcladdr, $remaddr );
+}
+
+# Convert a hash of aspppd options into an array of pppd options. The
+# third argument ($chatpath) is undef for dial-in or a path to the
+# chat script file otherwise.
+sub convert_options
+{
+ local ($pppdargs, $opts, $chatpath, $user) = @_;
+
+# Do the pppd option conversions.
+ push(@$pppdargs, "defaultroute") if $$opts{default_route};
+ push(@$pppdargs, "idle " . $$opts{inactivity_timeout})
+ if $$opts{inactivity_timeout} != 0;
+ push(@$pppdargs, "asyncmap " . $$opts{ipcp_async_map})
+ if $$opts{ipcp_async_map};
+ push(@$pppdargs, "novj") if !$$opts{ipcp_compression};
+ local($peer);
+ if ($$opts{require_authentication}) {
+ local (@authopts);
+ if ($$opts{require_authentication} =~ /chap/) {
+ push(@authopts, "require-chap");
+ $peer = $$opts{chap_peer_name};
+ } else {
+ push(@authopts, "require-pap");
+ $peer = $$opts{pap_peer_id};
+ }
+ push(@authopts, "remotename " . requote($peer)) if $peer;
+ push(@authopts, "auth");
+ if ($chatpath) {
+ push(@$pppdargs, @authopts);
+ } elsif ($dialin_auth == 3) {
+# mixed authentication; must use wrapper script.
+ local($sfile) = $pppdir . "dial-in." . $user;
+ $scriptfiles{$sfile} = "#!/bin/sh\n";
+ $scriptfiles{$sfile} .= "exec /usr/bin/pppd @authopts\n";
+ $dialinshell{$user} = $sfile;
+ }
+ } elsif ($dialin_auth < 2) {
+ push(@$pppdargs, "noauth");
+ }
+ push(@$pppdargs, "noaccomp nopcomp") if !$$opts{lcp_compression};
+ push(@$pppdargs, "mru " . $$opts{lcp_mru}) if $$opts{lcp_mru};
+ push(@$pppdargs, "noipdefault ipcp-accept-local")
+ if $$opts{negotiate_address};
+ local($myname,$csecret,$psecret,$passopt);
+ if ($$opts{will_do_authentication} =~ /chap/i) {
+ $myname = $$opts{chap_name};
+ $csecret = $$opts{chap_secret};
+ }
+ $myname = "" if !$myname;
+ if ($$opts{will_do_authentication} =~ /pap/i) {
+ if ($myname ne "" && $$opts{pap_id} ne "" &&
+ $myname ne $$opts{pap_id}) {
+ warn "pppd cannot have separate local names for PAP and CHAP; using CHAP name:\n";
+ warn "\t\"$myname\"\n";
+ } else {
+ $myname = $$opts{pap_id} if $$opts{pap_id} ne "";
+ }
+ $psecret = $$opts{pap_password};
+ }
+ if ($$opts{will_do_authentication}) {
+ if ($chatpath &&
+ ($$opts{will_do_authentication} !~ /chap/i ||
+ $$opts{will_do_authentication} !~ /pap/i ||
+ $csecret eq $psecret)) {
+ push(@$pppdargs,
+ "password " . requote($csecret ? $csecret : $psecret));
+ $passopt = 1;
+ }
+ push @$pppdargs, "user " . requote($myname);
+ push(@$pppdargs, "remotename " . requote($peer))
+ if $peer && !$$opts{require_authentication};
+ } else {
+ $myname = "NeverAuthenticate";
+ }
+
+ push(@$pppdargs, "debug") if $$opts{debug_level} >= 8;
+ push(@$pppdargs, "kdebug 3") if $$opts{debug_level} >= 9;
+ local($lcladdr, $remaddr) = ifconf_addr($$ifconfig{$$opts{interface}});
+ if ($chatpath) {
+ if ($$opts{debug_level} >= 4) {
+ push(@pppdargs, "connect \"/usr/bin/chat -vf $chatpath\"");
+ } else {
+ push(@pppdargs, "connect \"/usr/bin/chat -f $chatpath\"");
+ }
+ push(@$pppdargs, "user " . requote($myname));
+ local($str) = $remaddr;
+ $str = $$opts{peer_ip_address} if $$opts{peer_ip_address};
+ push(@$pppdargs, $lcladdr . ":" . $str)
+ if !$opt_s && ($lcladdr || $str);
+ } else {
+ push(@$pppdargs, "user " . requote($myname))
+ if $myname eq "NeverAuthenticate";
+ }
+ if ($$opts{interface} && $opt_s) {
+ if ($$opts{interface} =~ /^ipd[0-9]+$/) {
+ push(@$pppdargs, $lcladdr . ":") if $lcladdr;
+ } elsif ($$opts{interface} =~ /^ipd(|ptp)([0-9]+)$/) {
+ push(@pppdargs, "unit " . $2);
+ } else {
+ push(@pppdargs, "plumbed");
+ }
+ }
+
+# Convert the secrets
+ if ($chatpath) {
+ $addsecret = "";
+ } elsif ($$opts{peer_ip_address}) {
+ $addsecret = " " . $$opts{peer_ip_address};
+ } else {
+ $addsecret = " *";
+ }
+ if ($$opts{require_authentication}) {
+ local($lclname, $secret, $authf);
+ $lclname = $$opts{will_do_authentication} ? $myname : hostname();
+ if ($$opts{require_authentication} =~ /chap/) {
+ $secret = $$opts{chap_peer_secret};
+ $authf = \%chapsecrets;
+ } else {
+ $secret = $$opts{pap_peer_password};
+ $authf = \%papsecrets;
+ }
+ ${$$authf{$peer}}{$lclname} = requote($secret) . $addsecret;
+ }
+ if ($$opts{will_do_authentication} && !$passopt) {
+ ${$chapsecrets{$myname}}{$peer} = requote($csecret) if $csecret;
+ ${$papsecrets{$myname}}{$peer} = requote($psecret) if $psecret;
+ }
+}
+
+# Translate options for a dial-in user.
+sub translatedialin
+{
+ local($peer) = @_;
+
+ $optname = $$dialinusers{$peer};
+ $optname .= "/" if $optname !~ /\/$/;
+ $optname .= ".ppprc";
+ if (exists($optfiles{$optname})) {
+ warn "Home directories of $peer and $optuser{$optname} are the same ($optfiles{$optname}\n";
+ warn "Ignoring configuration for $peer.\n";
+ return;
+ }
+ $optuser{$optname} = $peer;
+ $dialinshell{$peer} = "/usr/bin/pppd";
+ local (@pppdargs);
+ convert_options(\@pppdargs, $$paths{$peer}, undef, $peer);
+ push @pppdargs, "nologfd";
+ $optfiles{$optname} = \@pppdargs;
+}
+
+# Translate ifconfig entries in asppp.cf into either an ifconfig
+# script for strict translation or a set of per-port IP addresses
+# for normal translation. Also translate ipdX interfaces into
+# Ethernet aliases to make routing daemon happy.
+sub translateifconfig
+{
+ local (@ifconfiglist,$cstr,$etherif,$intf,$ifconf,$addstr,$port);
+
+ open IFLIST, "/usr/sbin/ifconfig -au4|" || die "cannot run ifconfig: $!\n";
+ while (<IFLIST>) {
+ $etherif = $1 if !$etherif && /^([^:]*).*UP.*BROADCAST/;
+ }
+ close IFLIST;
+ $etherif = $1 if !$etherif && glob("/etc/hostname.*") =~ /[^\.]*.(.*)/;
+ $etherif = $1 if !$etherif && glob("/etc/dhcp.*") =~ /[^\.]*.(.*)/;
+ $etherif = "hme0" if !$etherif;
+
+ $cstr = "";
+ foreach $intf (keys %$ifconfig) {
+ $ifconf = $$ifconfig{$intf};
+ if ($intf =~ /ipd[0-9]+/) {
+ shift @$ifconf;
+ $cstr .= "ifconfig $etherif addif @$ifconf\n";
+ }
+ }
+
+ $ndialin = 0+(keys %$dialin);
+ $addstr = "";
+ foreach $intf (keys %downif) {
+ $ifconf = $downif{$intf};
+ if ($intf =~ /ipdptp([0-9]+)/ && --$ndialin >= 0) {
+ push @ifconfiglist, $ifconf;
+ $addstr .= "ifconfig sppp" . $1 . " @$ifconf\n";
+ }
+ }
+ if ($ndialin > 0) {
+ if (@ifconfiglist) {
+ print "Fewer ifconfigs (", $#ifconfiglist+1,
+ ") than dial-in lines (", $ndialin+$#ifconfiglist+1, ")\n";
+ } else {
+ print "No ifconfigs for ";
+ nof $ndialin, "dial-in port", ".\n";
+ }
+ } elsif ($ndialin < 0) {
+ if (@ifconfiglist) {
+ print "Ignoring ", -$ndialin, " of ";
+ nof $#ifconfiglist-$ndialin+1, "ifconfig",
+ " (too few dial-in ports)\n";
+ } else {
+ print "Ignoring all ";
+ nof -$ndialin, "ifconfig line", " (no dial-in ports)\n";
+ }
+ }
+
+ if ($opt_s) {
+ # Strict translation uses pre-plumbed interfaces.
+ $cstr .= $addstr;
+ } else {
+ foreach $port (values %$dialin) {
+ last if !@ifconfiglist;
+ $port =~ s+/dev/++;
+ $port =~ s+/+.+g;
+ $optfile = $ttyprefix . $port;
+ $ifconf = pop @ifconfiglist;
+ local ($lcladdr, $remaddr) = ifconf_addr($ifconf);
+ next if !defined($lcladdr) || !defined($remaddr);
+ local (@pppdargs) = $lcladdr . ":" . $remaddr;
+ $optfiles{$optfile} = \@pppdargs;
+ }
+ }
+ $scriptfiles{$pppdir . "ifconfig"} = $cstr if $cstr;
+}
+
+# Attempt to modify global passwd file using sed script stored in
+# the $sedpasswd temporary file.
+sub rewrite_passwd
+{
+ print "Updating local passwd file (if any).\n" if $opt_v;
+ if (!sysopen(PWDLCK, $passwdlck, O_WRONLY|O_CREAT, 0600)) {
+ warn "Unable to lock password file: $!\n";
+ } else {
+ $lockstr = pack "ssLLiiLLLL", F_WRLCK, 0, 0, 0, 0, 0, 0, 0, 0, 0;
+ eval {
+ local $SIG{ARLM} = sub {
+ die "alarm while locking password file\n"
+ };
+ alarm 15;
+ fcntl PWDLCK, F_SETLKW, $lockstr ||
+ die "cannot lock password file: $!\n";
+ alarm 0;
+ };
+ if ($@) {
+ warn $@;
+ } else {
+ warn "Password update failed.\n"
+ if (system("sed -f $sedpasswd < $passwd > ${passwd}.new") ||
+ !(rename "${passwd}.new", $passwd));
+ }
+ $lockstr = pack "ssLLiiLLLL", F_UNLCK, 0, 0, 0, 0, 0, 0, 0, 0, 0;
+ fcntl PWDLCK, F_SETLK, $lockstr;
+ close PWDLCK;
+ }
+ if (system("/usr/lib/nis/nisping -u 2>/dev/null") == 0) {
+ print "Updating NIS+ user home directories.\n" if $opt_v;
+ system("/usr/bin/sed 's/\$/p/' <$sedpasswd >${sedpasswd}n");
+ if (open NISUSERS, "/usr/lib/nis/nisaddent -d passwd | " .
+ "/usr/bin/sed -n -f ${sedpasswd}n |") {
+ local (@updatedusers) = <NISUSERS>;
+ close NISUSERS;
+ if (@updatedusers) {
+ if (open NISUSERS, "| /usr/lib/nis/nisaddent passwd") {
+ foreach $user (@updatedusers) {
+ print NISUSERS $user;
+ }
+ close NISUSERS || warn "NIS+ update failure.\n";
+ } else {
+ warn "NIS+ update failure.\n";
+ }
+ } else {
+ print "No NIS+ users need to be updated.\n" if $opt_v;
+ }
+ } else {
+ warn "Unable to read NIS+ users.\n";
+ }
+ unlink $sedpasswd . "n";
+ } elsif (($ypmaster = `/usr/bin/ypwhich 2>/dev/null`) && $? == 0) {
+ $ypmaster =~ /(.*)\n/;
+ ($ypmaster) = gethostbyname($1);
+ ($thishost) = gethostbyname(hostname);
+ if ($ypmaster eq $thishost) {
+ system("cd /var/yp && make")
+ if yesno("Rebuild NIS/YP maps", $opt_y);
+ } else {
+ warn "Not running on NIS/YP master $1; unable to update user shells\n";
+ print "Use 'sed -f $sedpasswd <$passwd >${passwd}.new' on the master\n";
+ print "and then remake the NIS/YP database.\n";
+ undef $sedpasswd;
+ }
+ }
+ unlink $sedpasswd if $sedpasswd;
+}
+
+# Show usage message.
+sub usage
+{
+ print "Usage:\n\n";
+ print "\t$0 [-rsvy]\n\n";
+ print " -n - non-interactive mode.\n";
+ print " -r - revert back to aspppd configuration.\n";
+ print " -s - use strict translation.\n";
+ print " -v - print more detail of the operations performed.\n";
+ print " -y - assume 'yes' as default answer where reasonable.\n";
+ exit;
+}
+
+# Correct an environment variable so that it points at either a useful
+# executable program, or nothing at all.
+sub fixpath
+{
+ local ($prog, $deflt) = @_;
+
+ $prog = $deflt if $prog eq "";
+ if ($prog !~ /^(\.\/|\/)/) {
+ local ($_) = $ENV{PATH};
+ $_ = "/bin:/usr/bin:/sbin:/usr/sbin" if $_ eq "";
+ split /:/;
+ foreach (@_) {
+ $prog = $_ . "/" . $prog, last if -x $_ . "/" . $prog;
+ }
+ }
+ $prog = "" if !(-x $prog);
+ $prog;
+}
+
+getopts('nrsvy') || usage;
+
+die "Need permission to modify system files.\n"
+ unless ($> == 0 || yesno "This script should be run as root. Continue");
+
+if ($opt_r) {
+ local ($intemp);
+
+# Revert to previous configuration. Just rename the aspppd file back
+# and undo changes to the passwd file.
+
+ die "No saved aspppd configuration exists.\n" unless -f $asmoved;
+ if (-e $astemp) {
+ die "$astemp is not a file\n" unless -f $asfile;
+ unlink $astemp || die "Cannot remove temporary $astemp: $!\n";
+ }
+ $intemp = 0;
+ if (-e $asfile) {
+ die "$asfile is not a file\n" unless -f $asfile;
+ die "Not modifying configuration.\n"
+ unless yesno "Remove existing $asfile", $opt_y;
+ rename $asfile, $astemp || die "Cannot rename existing $asfile: $!\n";
+ $intemp = 1;
+ }
+
+ if (rename $asmoved, $asfile) {
+ unlink $astemp || warn "$astemp: $!\n" if $intemp;
+ } else {
+ $failure = "Cannot rename $asmoved to $asfile: $!\n";
+ rename $astemp, $asfile ||
+ die "$failure\nand cannot recover: $!\n" .
+ "Saved current asppp.cf in $astemp\n"
+ if $intemp;
+ die $failure;
+ }
+
+ $( = $);
+ $< = $>;
+
+ system($pppdctl, "stop") if -x $pppdctl;
+ # remove pppd autostart files.
+ unlink $pppdir . "ifconfig";
+ unlink $pppdir . "demand";
+
+ system($asctl, "start") if -x $asctl;
+
+ open SEDFILE, ">$sedpasswd" || die "Cannot write $sedpasswd: $!\n";
+ local ($escdir) = $pppdir;
+ $escdir =~ s+/+\\/+g;
+ print SEDFILE "/${escdir}dial-in\\./s+[^:]*\$+/usr/sbin/aspppls+\n";
+ print SEDFILE "/\\/usr\\/bin\\/pppd/s+[^:]*\$+/usr/sbin/aspppls+\n";
+ close SEDFILE;
+
+ rewrite_passwd;
+
+ exit 0;
+}
+
+$aspppcf = $asfile;
+if (!(-f $asfile)) {
+ die "No aspppd configuration exists; nothing to convert.\n"
+ unless -f $asmoved;
+ die "No changes made.\n"
+ unless yesno "Already converted; rerun anyway";
+ $aspppcf = $asmoved;
+}
+
+print "This script provides only a suggested translation for your existing aspppd\n";
+print "configuration. You will need to evaluate for yourself whether the translation\n";
+print "is appropriate for your operating environment.\n";
+die "No changes made.\n"
+ unless yesno "Continue", 1;
+
+# Read in the asppp.cf file first; there's no reason to continue on to
+# the UUCP files if this file isn't readable or has no paths defined.
+local($ifconfig, $paths) = readaspppcf($aspppcf);
+
+# Loop over the ifconfigs and build a list of the down ones.
+foreach $intf (keys %$ifconfig) {
+ local(@words) = @{$$ifconfig{$intf}};
+ while ($word = shift @words) {
+ shift @words if $ifconfigtakes{$word};
+ if ($word =~ /^down$/) {
+ warn("Why is $intf declared down?\n"), last
+ if $intf =~ /^ipd[0-9]+$/;
+ $downif{$intf} = $$ifconfig{$intf};
+ delete $$ifconfig{$intf};
+ last;
+ }
+ }
+}
+
+# Read /etc/passwd for dial-in users configured for aspppd.
+local($dialinusers) = readpasswd;
+
+# Read in existing pppd configuration. All we really care about
+# is the setting of the "auth" option.
+undef $authoption;
+if (open(OPTIONS,"<" . $options)) {
+ while (@{$words = uucpline(OPTIONS, $options)}) {
+ while ($_ = pop @$words) {
+ $authoption = $_ if /auth/i;
+ }
+ }
+ close OPTIONS;
+ $authoption = "unknown" if !defined($authoption);
+}
+
+$dialin_auth = 0;
+if ($authoption =~ /^auth$/i) {
+ $dialin_auth = 1;
+} elsif ($authoption =~ /^noauth$/i) {
+ $dialin_auth = 2;
+} elsif (defined($authoption)) {
+ $dialin_auth = 3;
+}
+
+# Check that there's a path for each dial in user
+foreach $user (keys %$dialinusers) {
+ if (!defined($$paths{$user})) {
+ warn "Dial-in user ", $user,
+ " does not have a corresponding dial-in path.\n";
+ delete $$dialinusers{$user};
+ next;
+ }
+ $intf = ${$$paths{$user}}{"interface"};
+ if ($intf eq "ipdptp*") {
+ if (0+keys(%downif) == 0) {
+ warn "Dial-in user $path has no available \"down\" interfaces.\n";
+ delete $$dialinusers{$user};
+ next;
+ }
+ } else {
+ if (!defined($downif{$intf}) && !defined($$ifconfig{$intf})) {
+ warn "Dial-in path $user has undefined $intf; deleted.\n";
+ delete $$dialinusers{$user};
+ next;
+ }
+ }
+ ${$$paths{$user}}{$isdialin} = 1;
+# 0 - no info (no options file, "noauth" on call)
+# 1 - all auth ("auth" in options, "noauth" on call)
+# 2 - all noauth ("noauth" in options)
+# 3 - mixed; use auth ("noauth" in options, wrapper script for "auth")
+ if (${$$paths{$user}}{require_authentication}) {
+ if ($dialin_auth == 2) {
+ $dialin_auth = 3;
+ } elsif ($dialin_auth == 0) {
+ $dialin_auth = 1;
+ }
+ } else {
+ if ($dialin_auth == 1) {
+ $dialin_auth = 3;
+ } elsif ($dialin_auth == 0) {
+ $dialin_auth = 2;
+ }
+ }
+}
+
+# Get lists of usable dial-in and dial-out ports.
+local($dialin,$dialout) = getserialports;
+
+# Read and parse the UUCP Sysfiles, Devconfig, and Limits files.
+# These are keyed with the "service=" string. The Sysfiles file can
+# augment or override the list of files read for a given service.
+print "Reading UUCP configuration.\n" if $opt_v;
+@sysfiles = @{${uucpkeyfile($Sysfiles,"service=")}{"ppp"}};
+@limits = @{${uucpkeyfile($Limits,"service=")}{"ppp"}};
+%devconfig = %{uucpkeyfile($Devconfig,"service=ppp","device=")};
+
+# Now read in the UUCP files corresponding to this service.
+$systems = uucpposfiles(uucpfiles("systems"));
+$dialers = uucpposfiles(uucpfiles("dialers"));
+$dialcodes = uucpposfiles($Dialcodes);
+$devices = uucpdevices(uucpfiles("devices"));
+
+# just to make sure
+$$dialcodes{""} = ();
+
+# Loop over paths. Dial-out only paths are translated into demand-dial
+# configurations. Dial-in only paths are translated into appropriate
+# log-in entries.
+local (@bidirectional);
+foreach $peer (keys %$paths) {
+ if (exists($$systems{$peer})) {
+ $sline = $$systems{$peer};
+ if ($$sline[0] eq "Never") {
+ if (${$$paths{$peer}}{$isdialin}) {
+ translatedialin($peer);
+ } else {
+ print "We never call $peer, and he never calls us.\n"
+ if $opt_v;
+ }
+ delete $$paths{$peer};
+ next;
+ }
+ push @bidirectional, $peer if ${$$paths{$peer}}{$isdialin};
+ print "Ignoring time restriction on $peer\n"
+ if $$sline[0] ne "Any";
+ $dlist = $$devices{$$sline[1]};
+ $class = $$sline[2];
+ $i = 0;
+ while ($i < @$dlist) {
+ local($dev) = $$dlist[$i];
+ if ($$dev[1] ne "-") {
+ print "Ignoring device $$dev[0]; 801-type not supported.\n";
+ splice @$dlist, $i, 1;
+ next;
+ }
+ $i++;
+
+ # Make sure that classes match.
+ next if $$dev[2] ne "Any" && $class ne "Any" && $$dev[2] ne $class;
+ # Prepend "/dev/" if it's not present in the device name.
+ if (exists($$dialout{$$dev[0]})) {
+ # This just seems odd.
+ $dname = $$dialout{$$dev[0]};
+ $dname =~ s+/dev/term/+/dev/cua/+;
+ } else {
+ $dname = ($$dev[0] =~ m+^/+ ? $$dev[0] : ("/dev/" . $$dev[0]));
+ }
+ # Skip devices that aren't supposed to be used for dial-out.
+ next if $dname =~ m+^/dev/term/+;
+ next if $dname =~ m+^/dev/tty[a-z]$+;
+ # Make sure this is a character device and we have access to it.
+ next unless -w $dname && -c $dname;
+ warn "Dialer for $$dev[3] is missing.\n"
+ unless exists($warned{$$dev[3]}) ||
+ exists($$dialers{$$dev[3]});
+ $warned{$$dev[3]} = 1;
+
+ # Expand keywords from Dialcodes file. Should have \T or \D.
+ $phone = $$sline[3];
+ $xphone = ($$dev[4] eq "\\T" && $phone =~ /^([A-Za-z]*)(.*)$/ ?
+ "@{$$dialcodes{$1}}" . $2 : $phone);
+
+ # Make a copy of the dialing script.
+ local(@dials) = @{$$dialers{$$dev[3]}};
+
+ # Translate dial tone and wait characters from Dialers file.
+ $_ = shift @dials;
+ s[(.)(.)]{
+ local($from,$to) = ($1,$2);
+ $phone =~ s+(^|[^\\])$from+$1$to+gx;
+ $xphone =~ s+(^|[^\\])$from+$1$to+gx;
+ }ge;
+
+ # Translate escapes in dial specification. Chat has a \T,
+ # but uses \U instead of \D.
+ local($needt, $needu, $isexpect, @chats) = ("", "", 1);
+ foreach $str (@dials) {
+ push(@chats, "") if $str eq "";
+ local ($ostr) = "";
+ if ($isexpect) {
+ while ($str =~ s/([^\\]*)\\(.)//) {
+ local($lead, $_) = ($1, $2);
+ /[Mm]/ ? ($ostr .= $lead) :
+ /[Ee]/ ? ($sorrye = 1, $ostr .= $lead) :
+ ($ostr .= $lead . "\\" . $_);
+ }
+ } else {
+ while ($str =~ s/([^\\]*)\\(.)//) {
+ local($lead, $_) = ($1, $2);
+ /T/ ? ($needt = " -T '$xphone'",
+ $ostr .= $lead . "\\T") :
+ /D/ ? ($needu = " -U '$phone'",
+ $ostr .= $lead . "\\U") :
+ /M/ ? ($ostr .= $lead,
+ ($ostr ne "" ? push(@chats, $ostr, "\\c"):0),
+ push(@chats, "HANGUP", "OFF"), $ostr = "") :
+ /m/ ? ($ostr .= $lead,
+ ($ostr ne "" ? push(@chats, $ostr, "\\c"):0),
+ push(@chats, "HANGUP", "ON"), $ostr = "") :
+ /[Ee]/ ? ($sorrye = 1, $ostr .= $lead) :
+ /[dp]/ ? ($ostr .= $lead . "\\" . $_ . "\\" . $_) :
+ /c/ ? ($str eq "" ? ($ostr .= $lead . "\\c") : 0) :
+ ($ostr .= $lead . "\\" . $_);
+ }
+ }
+ $ostr .= $str;
+ push @chats, $ostr if $ostr ne "";
+ $isexpect = !$isexpect;
+ }
+
+ # Pad out dial list if we're missing a "send" string and tack
+ # on the chat list from the Systems file.
+ if (defined $$sline[4]) {
+ push @chats, "\\c" if !$isexpect;
+ push @chats, (splice @$sline, 4);
+ }
+
+ $chatfile = $pppdir . "chat.$peer.$$dev[3]";
+ if (-e $chatfile) {
+ print "$chatfile already exists.\n";
+ if (!yesno("Should it be overwritten",$opt_y)) {
+ if (yesno("Should it be used as-is")) {
+ warn "Using $chatfile as-is; it may not be correct.\n";
+ } else {
+ for ($n = 0; ; $n++) {
+ last if !(-e $chatfile . "." . $n);
+ }
+ $chatfile .= "." . $n;
+ print "Using $chatfile instead.\n";
+ $chatfiles{$chatfile} = \@chats;
+ }
+ } else {
+ $overwrite{$chatfile} = 1;
+ $chatfiles{$chatfile} = \@chats;
+ }
+ } else {
+ $chatfiles{$chatfile} = \@chats;
+ }
+
+ push @pppdargs, $dname;
+ push @pppdargs, $class if $class =~ /^[0-9]+$/;
+ push @pppdargs, "demand";
+ convert_options(\@pppdargs,$$paths{$peer},
+ $chatfile . $needt . $needu, undef);
+
+ $optname = $peersdir . $peer;
+ if (-e $optname) {
+ print "$optname already exists.\n";
+ if (!yesno("Should it be overwritten", $opt_y)) {
+ if (yesno("Should it be used as-is")) {
+ warn "Using $optname as-is; it may not be correct.\n";
+ } else {
+ for ($n = 0; ; $n++) {
+ last if !(-e $optname . "." . $n);
+ }
+ $optname .= "." . $n;
+ print "Using $optname instead.\n";
+ $optfiles{$optname} = \@pppdargs;
+ }
+ } else {
+ $overwrite{$optname} = 1;
+ $optfiles{$optname} = \@pppdargs;
+ }
+ } else {
+ $optfiles{$optname} = \@pppdargs;
+ }
+ $scriptfiles{$pppdir . "demand"} .= "/usr/bin/pppd file $optname\n";
+ last;
+ }
+ } elsif (${$$paths{$peer}}{$isdialin}) {
+ translatedialin($peer);
+ } else {
+ warn "Path $peer has no dial-in user nor Systems file entry.\n";
+ delete $$paths{$peer};
+ }
+}
+
+warn "Chat cannot do echo checking; requests for this removed.\n" if $sorrye;
+
+if (@bidirectional) {
+ print "\nWarning: The following paths are bidirectional:\n";
+ print "\t@bidirectional\n\n";
+ print "Bidirectional paths (with entries in both Systems and passwd) do not translate\n";
+ print "into Solaris PPP 4.0 semantics in an exact manner. The dial-out portion will\n";
+ print "use the designated interface, but the dial-in portion will use any available\n";
+ print "interface.\n";
+ while ($peer = pop @bidirectional) {
+ delete $ {$$paths{$peer}}{interface};
+ translatedialin($peer);
+ }
+}
+
+translateifconfig;
+
+# Create an /etc/ppp/options if we need to.
+if (!defined($authoption) && $dialin_auth > 0) {
+ local (@pppdopts);
+ push @pppdopts, "lock";
+ push @pppdopts, "auth" if $dialin_auth == 1;
+ push @pppdopts, "noauth" if $dialin_auth > 1;
+ $optfiles{$options} = \@pppdopts;
+}
+# Translate option files to plain text.
+foreach $file (keys %optfiles) {
+ local ($opts) = $optfiles{$file};
+ local ($cstr) = "";
+ $cstr .= shift(@$opts) . "\n" while @$opts;
+ $optfiles{$file} = $cstr;
+}
+# Change "auth" to "noauth" or add "noauth" to /etc/ppp/options.
+if (defined($authoption) && $authoption ne "noauth" && $dialin_auth == 3) {
+ local(@triplet, $cstr);
+ if ($authoption eq "unknown") {
+ warn "Adding 'noauth' to $options\n";
+ } else {
+ warn "Changing 'auth' in $options to 'noauth'\n";
+ }
+ open(OPTIONS,"<" . $options) || die "$options disappeared: $!\n";
+ while (@{$words = uucpline(OPTIONS, $options, \@triplet)}) {
+ $cstr .= $triplet[0];
+ if (grep(/auth/, @$words)) {
+ local(@newwords) = map { $_ = "noauth" if /auth/; $_ } @$words;
+ $cstr .= "@newwords";
+ } else {
+ $cstr .= $triplet[1];
+ }
+ while (pop @$words) {
+ $authoption = $_ if /auth/i;
+ }
+ $cstr .= $triplet[2];
+ }
+ $cstr .= $triplet[0] . $triplet[2];
+ close OPTIONS;
+ $cstr .= "\n" if $cstr !~ /\n$/;
+ $cstr .= "noauth\n" if $authoption eq "unknown";
+ $optfiles{$options} = $cstr;
+}
+
+# Create a sed script to fix the users' shell paths.
+if (0+(keys %dialinshell) != 0) {
+ $cstr = "";
+ foreach $peer (keys %dialinshell) {
+ $cstr .= "/^$peer:/s+[^:]*/aspppls\$+$dialinshell{$peer}+\n";
+ }
+ $scriptfiles{$sedpasswd} = $cstr;
+}
+
+print "\nPreparing to write out translated configuration:\n";
+
+# Enumerate the files we'll write.
+$nfiles = 0;
+if (0+(keys %chatfiles) != 0) {
+ print " ";
+ nof 0+(keys %chatfiles), "chat file", ":\n";
+ foreach $file (keys %chatfiles) {
+ $nfiles++;
+ print "\t$nfiles. $file\n";
+ local ($chats) = $chatfiles{$file};
+ local ($cstr) = "";
+ while (@$chats) {
+ $cstr .= requote(shift(@$chats));
+ $cstr .= " " . requote(shift(@$chats)) if @$chats;
+ $cstr .= "\n";
+ }
+ local (@filerec) = ( $file, $cstr );
+ push @allfiles, \@filerec;
+ }
+}
+if (0+(keys %optfiles) != 0) {
+ print " ";
+ nof 0+(keys %optfiles), "option file", ":\n";
+ foreach $file (keys %optfiles) {
+ $nfiles++;
+ print "\t$nfiles. $file\n";
+ local (@filerec) = ( $file, $optfiles{$file} );
+ push @allfiles, \@filerec;
+ }
+}
+if (0+(keys %scriptfiles) != 0) {
+ print " ";
+ nof 0+(keys %scriptfiles), "script file", ":\n";
+ foreach $file (keys %scriptfiles) {
+ $nfiles++;
+ print "\t$nfiles. $file\n";
+ local (@filerec) = ( $file, $scriptfiles{$file} );
+ push @allfiles, \@filerec;
+ }
+}
+
+# Merge new secrets needed with existing ones, if any.
+sub merge_secrets
+{
+ local ($addsecrets, $fname) = @_;
+ local ($file, $cstr, @triplet, $newsecret);
+
+ $nfiles++;
+ $file = $pppdir . $fname;
+ print "\t$nfiles. $file\n";
+ if (open(SECRETS, '<' . $pppdir . $fname)) {
+ while (@{$words = uucpline(SECRETS, $pppdir . $fname, \@triplet)}) {
+ $cstr .= $triplet[0];
+ $newsecret = $ {$$addsecrets{$$words[0]}}{$$words[1]};
+ if (defined $newsecret) {
+ $cstr .= requote($$words[0]) . " " . requote($$words[1]) .
+ " " . $newsecret;
+ delete $ {$$addsecrets{$$words[0]}}{$$words[1]};
+ } else {
+ $cstr .= $triplet[1];
+ }
+ $cstr .= $triplet[2];
+ }
+ close SECRETS;
+ $cstr .= $triplet[0] . $triplet[2];
+ }
+ foreach $key1 (keys (%$addsecrets)) {
+ foreach $key2 (keys (%{$$addsecrets{$key1}})) {
+ $cstr .= requote($key1) . " " . requote($key2) . " " .
+ $ {$$addsecrets{$key1}}{$key2} . "\n";
+ }
+ }
+ local (@filerec) = ( $file, $cstr );
+ push @allfiles, \@filerec;
+}
+
+$nchap = 0+(keys %chapsecrets) != 0;
+$npap = 0+(keys %papsecrets) != 0;
+if ($nchap != 0 || $npap != 0) {
+ print " ";
+ nof $nchap + $npap, "secrets file", ":\n";
+ merge_secrets(\%chapsecrets, "chap-secrets") if $nchap != 0;
+ merge_secrets(\%papsecrets, "pap-secrets") if $npap != 0;
+}
+
+die "Nothing to write back; I'm done.\n" if $nfiles == 0;
+
+$PAGER = fixpath($ENV{PAGER}, "/usr/bin/less");
+$EDITOR = fixpath($ENV{EDITOR}, "/usr/bin/vi");
+$SHELL = fixpath($ENV{SHELL}, "/usr/bin/ksh");
+
+END {
+ if ($tempname) {
+ unlink($tempname) or
+ die "Cannot remove temporary file $tempname: $!\n";
+ }
+}
+
+sub show_file_options
+{
+ print "\nEnter option number:\n";
+ print "\t1 - view contents of file on standard output\n";
+ print "\t2 - view contents of file using $PAGER\n" if $PAGER ne "";
+ print "\t3 - edit contents of file using $EDITOR\n" if $EDITOR ne "";
+ print "\t4 - delete/undelete file from list\n";
+ print "\t5 - rename file in list\n";
+ print "\t6 - show file list again\n";
+ print "\t7 - escape to shell (or \"!cmd\")\n";
+ print "\t8 - abort without saving anything\n";
+ print "\t9 - save all files and exit (default)\n";
+}
+
+# If interactive, then allow user to view and modify converted data.
+if ((-t STDIN) && (-t STDOUT) && !$opt_n) {
+ show_file_options();
+ while (1) {
+ print "Option: ";
+ chomp($ans = <STDIN>);
+ if ($ans eq "?" || $ans =~ /^h/i) {
+ show_file_options();
+ next;
+ }
+ if ($ans eq "") {
+ last if yesno "Saving all files. Are you sure";
+ next;
+ }
+ last if $ans == 9;
+ print("Aborted.\n"), exit if $ans == 8;
+ if ($ans =~ /^!/ || $ans == 7) {
+ if ($ans =~ /^!(.+)/) {
+ system($1);
+ } else {
+ print("Interactive shell access not permitted here.\n"), next
+ if $< != $>;
+ system($SHELL);
+ }
+ } elsif ($ans == 6) {
+ for ($i = 0; $i < $nfiles; $i++) {
+ print "\t", $i+1, ". $allfiles[$i][0]",
+ ($deleted[$i] ? " (deleted)" : ""), "\n";
+ }
+ } elsif ($ans > 0 && $ans < 6) {
+ $fnum = 0;
+ if ($nfiles > 1) {
+ print "File number (1 .. $nfiles): ";
+ chomp($fnum = <STDIN>);
+ if ($fnum < 1 || $fnum > $nfiles) {
+ print "Unknown file (must be 1 to $nfiles).\n";
+ next;
+ }
+ $fnum--;
+ }
+ if ($ans == 5) {
+ print "Current name is $allfiles[$fnum][0]\n";
+ print "New name: ";
+ chomp($fname = <STDIN>);
+ print("Unchanged\n"), next if $fname eq "";
+ $allfiles[$fnum][0] = $fname;
+ }
+ if ($deleted[$fnum]) {
+ if (yesno("File " . $fnum+1 .
+ " ($allfiles[$fnum][0]) is deleted; undelete",1)) {
+ undef $deleted[$fnum];
+ }
+ next;
+ }
+ if ($ans == 1) {
+ print $allfiles[$fnum][1];
+ } elsif ($ans == 2 && $PAGER ne "") {
+ $i = 0;
+ do {
+ if (++$i > 5) {
+ warn "Unable to open temporary file: $!";
+ undef $tempname;
+ last;
+ }
+ $tempname = tmpnam();
+ } until sysopen(FH, $tempname, O_RDWR|O_CREAT|O_EXCL);
+ next if !$tempname;
+ print FH $allfiles[$fnum][1];
+ close FH;
+ system($PAGER, $tempname);
+ unlink($tempname) ||
+ warn "Trouble removing temporary file: $!";
+ undef $tempname;
+ } elsif ($ans == 3 && $EDITOR ne "") {
+ $i = 0;
+ do {
+ if (++$i > 5) {
+ warn "Unable to open temporary file: $!";
+ undef $tempname;
+ last;
+ }
+ $tempname = tmpnam();
+ } until sysopen(FH, $tempname, O_RDWR|O_CREAT|O_EXCL);
+ next if !$tempname;
+ chown $<, $(, $tempname;
+ print FH $allfiles[$fnum][1];
+ close FH;
+ $i = system($EDITOR, $tempname);
+ if ($i == 0) {
+ if (open FH, "<" . $tempname) {
+ read FH, $allfiles[$fnum][1], (-s $tempname);
+ close FH;
+ }
+ } else {
+ print "Editor dropped core.\n" if $? & 128;
+ print "Editor terminated on signal ", $? & 127, "\n"
+ if $? & 127;
+ print "Editor returned error ", $? >> 8, "\n"
+ if $? >> 8;
+ }
+ unlink($tempname) ||
+ warn "Trouble removing temporary file: $!";
+ undef $tempname;
+ } elsif ($ans == 4) {
+ $deleted[$fnum] = 1;
+ }
+ }
+ }
+}
+
+print "\n";
+
+# Interactive part is over. Become real.
+$( = $);
+$< = $>;
+
+print "Stopping aspppd\n" if $opt_v;
+system($asctl, "stop") if -x $asctl;
+
+print "Saving all files\n" if $opt_v;
+for ($i = 0; $i < $nfiles; $i++) {
+ $filerec = $allfiles[$i];
+ if ($deleted[$i]) {
+ delete $scriptfiles{$$filerec[0]};
+ next;
+ }
+ print "Saving $$filerec[0]\n" if $opt_v;
+ $$filerec[0] =~ m+(.*)/+;
+ if ($1 eq "") {
+ # this is ok; just a top level file
+ } elsif (!(-d $1)) {
+ local ($exdir) = $1;
+ while ($exdir && !(-d $exdir)) {
+ $exdir =~ m+(.*)/+;
+ $exdir = $1;
+ }
+ if ($exdir) {
+ local ($dir) = $1;
+ $dir =~ m+$exdir/([^/]*)(.*)+;
+ local ($tomake, $rest) = ($1, $2);
+ mkdir $exdir . "/" . $tomake, 0775;
+ if ($! == ENOSYS) {
+ warn "Unable to make directory $exdir/$tomake; automount point.\n";
+ next;
+ }
+ if ($! != 0) {
+ warn "Unable to make directory $exdir/$tomake: $!\n";
+ next;
+ }
+ if (system("mkdir", "-p", $dir) != 0) {
+ warn "Failed to make $dir\n";
+ next;
+ }
+ } else {
+ warn "$1 doesn't appear to have a useful path.\n";
+ next;
+ }
+ }
+ undef $fileerr;
+ local ($fname) = $$filerec[0];
+ if (-e $fname && !$overwrite{$chatfile}) {
+ print "$fname already exists.\n"
+ if (-t STDIN) && (-t STDOUT) && !$opt_n;
+ if (!yesno("Should it be overwritten",$opt_y)) {
+ warn "Using $fname as-is; it may not be correct.\n";
+ next;
+ }
+ }
+ if (sysopen(OUTFILE, $$filerec[0], O_WRONLY|O_CREAT|O_TRUNC, 0600)) {
+ print OUTFILE $$filerec[1] || ($fileerr = $!);
+ close OUTFILE || ($fileerr = $!);
+ } else {
+ $fileerr = $!;
+ }
+ warn "Unable to write $$filerec[0]: $fileerr\n" if $fileerr;
+}
+
+local(@scripts) = keys %scriptfiles;
+if (@scripts) {
+ print "Making scripts executable\n" if $opt_v;
+ system("chmod", "u+x", @scripts);
+}
+
+rewrite_passwd if exists($scriptfiles{$sedpasswd});
+
+# clean up after a previous translation.
+unlink $pppdir . "ifconfig" if !$scriptfiles{$pppdir . "ifconfig"};
+unlink $pppdir . "demand" if !$scriptfiles{$pppdir . "demand"};
+
+(rename($asfile, $asmoved) || warn "Cannot move $asfile: $!\n")
+ if $aspppcf ne $astemp;
+
+system($pppdctl, "start") if -x $pppdctl;
+
+# use Dumpvalue;
+# my $dumper = new Dumpvalue;
+# $dumper->set(globPrint => 1);
+# $dumper->dumpValue($ifconfig);
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppd/auth.c b/usr/src/cmd/cmd-inet/usr.bin/pppd/auth.c
new file mode 100644
index 0000000000..be3f7ccb04
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppd/auth.c
@@ -0,0 +1,2179 @@
+/*
+ * auth.c - PPP authentication and phase control.
+ *
+ * Copyright 2000-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 1993 The Australian National University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Australian National University. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+#define RCSID "$Id: auth.c,v 1.65 2000/04/15 01:27:10 masputra Exp $"
+
+/* Pull in crypt() definition. */
+#define __EXTENSIONS__
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <grp.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <utmp.h>
+#include <fcntl.h>
+#if defined(_PATH_LASTLOG) && (defined(_linux_) || defined(__linux__))
+#include <lastlog.h>
+#endif
+
+#if defined(_linux_) || defined(__linux__)
+#include <crypt.h>
+#endif
+
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+/* Backward compatibility with old Makefiles */
+#if defined(USE_PAM) && !defined(ALLOW_PAM)
+#define ALLOW_PAM
+#endif
+
+#ifdef ALLOW_PAM
+#include <security/pam_appl.h>
+#endif
+
+#ifdef HAS_SHADOW
+#include <shadow.h>
+#ifndef PW_PPP
+#define PW_PPP PW_LOGIN
+#endif
+#endif
+
+#include "pppd.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "ipcp.h"
+#include "upap.h"
+#include "chap.h"
+#ifdef CBCP_SUPPORT
+#include "cbcp.h"
+#endif
+#include "pathnames.h"
+
+#if !defined(lint) && !defined(_lint)
+static const char rcsid[] = RCSID;
+#endif
+
+/* Bits in scan_authfile return value */
+#define NONWILD_SERVER 1
+#define NONWILD_CLIENT 2
+
+#define ISWILD(word) (word[0] == '*' && word[1] == '\0')
+
+/* The name by which the peer authenticated itself to us. */
+char peer_authname[MAXNAMELEN];
+
+/* Records which authentication operations haven't completed yet. */
+static int auth_pending[NUM_PPP];
+
+/* Set if we have successfully called plogin() */
+static int logged_in;
+
+/* List of addresses which the peer may use. */
+static struct permitted_ip *addresses[NUM_PPP];
+
+/* Wordlist giving addresses which the peer may use
+ without authenticating itself. */
+static struct wordlist *noauth_addrs;
+
+/* Extra options to apply, from the secrets file entry for the peer. */
+static struct wordlist *extra_options;
+
+/* Source of those extra options. */
+static const char *extra_opt_filename;
+static int extra_opt_line;
+
+/* Number of network protocols which we have opened. */
+static int num_np_open;
+
+/* Number of network protocols which have come up. */
+static int num_np_up;
+
+/* Set if we got the contents of passwd[] from the pap-secrets file. */
+static int passwd_from_file;
+
+/* Set if we require authentication only because we have a default route. */
+static bool default_auth;
+
+/* Hook to enable a plugin to control the idle time limit */
+int (*idle_time_hook) __P((struct ppp_idle *)) = NULL;
+
+/* Hook for a plugin to say whether we can possibly authenticate any peer */
+int (*pap_check_hook) __P((void)) = NULL;
+
+/* Hook for a plugin to check the PAP user and password */
+int (*pap_auth_hook) __P((char *user, char *passwd, char **msgp,
+ struct wordlist **paddrs,
+ struct wordlist **popts)) = NULL;
+
+/* Hook for a plugin to know about the PAP user logout */
+void (*pap_logout_hook) __P((void)) = NULL;
+
+/* Hook for a plugin to get the PAP password for authenticating us */
+int (*pap_passwd_hook) __P((char *user, char *passwd)) = NULL;
+
+/*
+ * This is used to ensure that we don't start an auth-up/down
+ * script while one is already running.
+ */
+enum script_state {
+ s_down,
+ s_up
+};
+
+static enum script_state auth_state = s_down;
+static enum script_state auth_script_state = s_down;
+static pid_t auth_script_pid = 0;
+
+/*
+ * This is set by scan_authfile if a client matches, but server doesn't
+ * (possible configuration error).
+ */
+static char scan_server_match_failed[MAXWORDLEN];
+
+/*
+ * Option variables.
+ */
+bool uselogin = 0; /* Use /etc/passwd for checking PAP */
+bool cryptpap = 0; /* Passwords in pap-secrets are encrypted */
+bool refuse_pap = 0; /* Don't wanna auth. ourselves with PAP */
+bool refuse_chap = 0; /* Don't wanna auth. ourselves with CHAP */
+bool usehostname = 0; /* Use hostname for our_name */
+bool auth_required = 0; /* Always require authentication from peer */
+bool allow_any_ip = 0; /* Allow peer to use any IP address */
+bool explicit_remote = 0; /* User specified explicit remote name */
+char remote_name[MAXNAMELEN]; /* Peer's name for authentication */
+
+#ifdef CHAPMS
+bool refuse_mschap = 0; /* Don't wanna auth. ourself with MS-CHAPv1 */
+#else
+bool refuse_mschap = 1; /* Never auth. ourself with MS-CHAPv1 */
+#endif
+#ifdef CHAPMSV2
+bool refuse_mschapv2 = 0; /* Don't wanna auth. ourself with MS-CHAPv2 */
+#else
+bool refuse_mschapv2 = 1; /* Never auth. ourself with MS-CHAPv2 */
+#endif
+
+#ifdef USE_PAM
+bool use_pam = 1; /* Enable use of PAM by default */
+#else
+bool use_pam = 0; /* Disable use of PAM by default */
+#endif
+
+/* Bits in auth_pending[] */
+#define PAP_WITHPEER 1
+#define PAP_PEER 2
+#define CHAP_WITHPEER 4
+#define CHAP_PEER 8
+
+/* Prototypes for procedures local to this file. */
+
+static void network_phase __P((int));
+static void check_idle __P((void *));
+static void connect_time_expired __P((void *));
+static int plogin __P((char *, char *, char **));
+static void plogout __P((void));
+static int null_login __P((int));
+static int get_pap_passwd __P((char *));
+static int have_pap_secret __P((int *));
+static int have_chap_secret __P((char *, char *, int, int *));
+static int ip_addr_check __P((u_int32_t, struct permitted_ip *));
+static int scan_authfile __P((FILE *, char *, char *, char *,
+ struct wordlist **, struct wordlist **,
+ char *));
+static void free_wordlist __P((struct wordlist *));
+static void auth_script __P((char *));
+static void auth_script_done __P((void *, int));
+static void set_allowed_addrs __P((int, struct wordlist *, struct wordlist *));
+static int some_ip_ok __P((struct wordlist *));
+static int setupapfile __P((char **, option_t *));
+static int privgroup __P((char **, option_t *));
+static int set_noauth_addr __P((char **, option_t *));
+static void check_access __P((FILE *, char *));
+
+/*
+ * Authentication-related options.
+ */
+option_t auth_options[] = {
+ { "require-pap", o_bool, &lcp_wantoptions[0].neg_upap,
+ "Require PAP authentication from peer", 1, &auth_required },
+ { "+pap", o_bool, &lcp_wantoptions[0].neg_upap,
+ "Require PAP authentication from peer", 1, &auth_required },
+ { "refuse-pap", o_bool, &refuse_pap,
+ "Don't agree to auth to peer with PAP", 1 },
+ { "-pap", o_bool, &refuse_pap,
+ "Don't allow PAP authentication with peer", 1 },
+ { "require-chap", o_bool, &lcp_wantoptions[0].neg_chap,
+ "Require CHAP authentication from peer", 1, &auth_required },
+ { "+chap", o_bool, &lcp_wantoptions[0].neg_chap,
+ "Require CHAP authentication from peer", 1, &auth_required },
+ { "refuse-chap", o_bool, &refuse_chap,
+ "Don't agree to auth to peer with CHAP", 1 },
+ { "-chap", o_bool, &refuse_chap,
+ "Don't allow CHAP authentication with peer", 1 },
+ { "name", o_string, our_name,
+ "Set local name for authentication",
+ OPT_PRIV|OPT_STATIC, NULL, MAXNAMELEN },
+ { "user", o_string, user,
+ "Set name for auth with peer", OPT_STATIC, NULL, MAXNAMELEN },
+ { "usehostname", o_bool, &usehostname,
+ "Must use hostname for authentication", 1 },
+ { "remotename", o_string, remote_name,
+ "Set remote name for authentication", OPT_STATIC,
+ &explicit_remote, MAXNAMELEN },
+ { "auth", o_bool, &auth_required,
+ "Require authentication from peer", 1 },
+ { "noauth", o_bool, &auth_required,
+ "Don't require peer to authenticate", OPT_PRIV, &allow_any_ip },
+ { "login", o_bool, &uselogin,
+ "Use system password database for PAP", 1 },
+ { "papcrypt", o_bool, &cryptpap,
+ "PAP passwords are encrypted", 1 },
+ { "+ua", o_special, (void *)setupapfile,
+ "Get PAP user and password from file" },
+ { "password", o_string, passwd,
+ "Password for authenticating us to the peer", OPT_STATIC,
+ NULL, MAXSECRETLEN },
+ { "privgroup", o_special, (void *)privgroup,
+ "Allow group members to use privileged options", OPT_PRIV },
+ { "allow-ip", o_special, (void *)set_noauth_addr,
+ "Set peer IP address(es) usable without authentication",
+ OPT_PRIV },
+#ifdef CHAPMS
+ { "require-mschap", o_bool, &lcp_wantoptions[0].neg_mschap,
+ "Require MS-CHAPv1 authentication from peer", 1, &auth_required },
+ { "refuse-mschap", o_bool, &refuse_mschap,
+ "Don't agree to authenticate to peer with MS-CHAPv1", 1 },
+#endif
+#ifdef CHAPMSV2
+ { "require-mschapv2", o_bool, &lcp_wantoptions[0].neg_mschapv2,
+ "Require MS-CHAPv2 authentication from peer", 1, &auth_required },
+ { "refuse-mschapv2", o_bool, &refuse_mschapv2,
+ "Don't agree to authenticate to peer with MS-CHAPv2", 1 },
+#endif
+#ifdef ALLOW_PAM
+ { "pam", o_bool, &use_pam,
+ "Enable use of Pluggable Authentication Modules", OPT_PRIV|1 },
+ { "nopam", o_bool, &use_pam,
+ "Disable use of Pluggable Authentication Modules", OPT_PRIV|0 },
+#endif
+ { NULL }
+};
+
+/*
+ * setupapfile - specifies UPAP info for authenticating with peer.
+ */
+/*ARGSUSED*/
+static int
+setupapfile(argv, opt)
+ char **argv;
+ option_t *opt;
+{
+ FILE * ufile;
+ int l;
+
+ lcp_allowoptions[0].neg_upap = 1;
+
+ /* open user info file */
+ (void) seteuid(getuid());
+ ufile = fopen(*argv, "r");
+ (void) seteuid(0);
+ if (ufile == NULL) {
+ option_error("unable to open user login data file %s", *argv);
+ return 0;
+ }
+ check_access(ufile, *argv);
+
+ /* get username */
+ if (fgets(user, MAXNAMELEN - 1, ufile) == NULL
+ || fgets(passwd, MAXSECRETLEN - 1, ufile) == NULL){
+ option_error("unable to read user login data file %s", *argv);
+ return 0;
+ }
+ (void) fclose(ufile);
+
+ /* get rid of newlines */
+ l = strlen(user);
+ if (l > 0 && user[l-1] == '\n')
+ user[l-1] = '\0';
+ l = strlen(passwd);
+ if (l > 0 && passwd[l-1] == '\n')
+ passwd[l-1] = '\0';
+
+ return (1);
+}
+
+
+/*
+ * privgroup - allow members of the group to have privileged access.
+ */
+/*ARGSUSED*/
+static int
+privgroup(argv, opt)
+ char **argv;
+ option_t *opt;
+{
+ struct group *g;
+ int i;
+
+ g = getgrnam(*argv);
+ if (g == NULL) {
+ option_error("group %s is unknown", *argv);
+ return 0;
+ }
+ for (i = 0; i < ngroups; ++i) {
+ if (groups[i] == g->gr_gid) {
+ privileged = 1;
+ break;
+ }
+ }
+ return 1;
+}
+
+
+/*
+ * set_noauth_addr - set address(es) that can be used without authentication.
+ * Equivalent to specifying an entry like `"" * "" addr' in pap-secrets.
+ */
+/*ARGSUSED*/
+static int
+set_noauth_addr(argv, opt)
+ char **argv;
+ option_t *opt;
+{
+ char *addr = *argv;
+ int l = strlen(addr);
+ struct wordlist *wp;
+
+ wp = (struct wordlist *) malloc(sizeof(struct wordlist) + l + 1);
+ if (wp == NULL)
+ novm("allow-ip argument");
+ wp->word = (char *) (wp + 1);
+ wp->next = noauth_addrs;
+ (void) strcpy(wp->word, addr);
+ noauth_addrs = wp;
+ return 1;
+}
+
+/*
+ * An Open on LCP has requested a change from Dead to Establish phase.
+ * Do what's necessary to bring the physical layer up.
+ */
+/*ARGSUSED*/
+void
+link_required(unit)
+ int unit;
+{
+}
+
+/*
+ * LCP has terminated the link; go to the Dead phase and take the
+ * physical layer down.
+ */
+/*ARGSUSED*/
+void
+link_terminated(unit)
+ int unit;
+{
+ const char *pn1, *pn2;
+
+ if (phase == PHASE_DEAD)
+ return;
+ if (pap_logout_hook != NULL) {
+ (*pap_logout_hook)();
+ } else {
+ if (logged_in)
+ plogout();
+ }
+ new_phase(PHASE_DEAD);
+ if (peer_nak_auth) {
+ if ((pn1 = protocol_name(nak_auth_orig)) == NULL)
+ pn1 = "?";
+ if ((pn2 = protocol_name(nak_auth_proto)) == NULL)
+ pn2 = "?";
+ warn("Peer sent Configure-Nak for 0x%x (%s) to suggest 0x%x (%s)",
+ nak_auth_orig, pn1, nak_auth_proto, pn2);
+ }
+ if (unsolicited_nak_auth) {
+ if ((pn1 = protocol_name(unsolicit_auth_proto)) == NULL)
+ pn1 = "?";
+ warn("Peer unexpectedly asked us to authenticate with 0x%x (%s)",
+ unsolicit_auth_proto, pn1);
+ }
+ if (peer_reject_auth) {
+ if ((pn1 = protocol_name(reject_auth_proto)) == NULL)
+ pn1 = "?";
+ warn("Peer rejected our demand for 0x%x (%s)",
+ reject_auth_proto, pn1);
+ }
+ if (naked_peers_auth) {
+ if ((pn1 = protocol_name(naked_auth_orig)) == NULL)
+ pn1 = "?";
+ if ((pn2 = protocol_name(naked_auth_proto)) == NULL)
+ pn2 = "?";
+ warn("We set Configure-Nak for 0x%x (%s) to suggest 0x%x (%s)",
+ naked_auth_orig, pn1, naked_auth_proto, pn2);
+ }
+ if (rejected_peers_auth) {
+ if ((pn1 = protocol_name(rejected_auth_proto)) == NULL)
+ pn1 = "?";
+ warn("We rejected the peer's demand for 0x%x (%s)",
+ rejected_auth_proto, pn1);
+ }
+
+ peer_nak_auth = unsolicited_nak_auth = peer_reject_auth =
+ rejected_peers_auth = naked_peers_auth = 0;
+ nak_auth_proto = nak_auth_orig = unsolicit_auth_proto = reject_auth_proto =
+ rejected_auth_proto = naked_auth_orig = naked_auth_proto = 0;
+ notice("Connection terminated.");
+}
+
+/*
+ * LCP has gone down; it will either die or try to re-establish.
+ */
+void
+link_down(unit)
+ int unit;
+{
+ int i;
+ struct protent *protp;
+
+ auth_state = s_down;
+ if (auth_script_state == s_up && auth_script_pid == 0) {
+ update_link_stats(unit);
+ auth_script_state = s_down;
+ auth_script(_PATH_AUTHDOWN);
+ }
+ for (i = 0; (protp = protocols[i]) != NULL; ++i) {
+ if (!protp->enabled_flag)
+ continue;
+ if (protp->protocol != PPP_LCP && protp->lowerdown != NULL)
+ (*protp->lowerdown)(unit);
+ if (protp->protocol < 0xC000 && protp->close != NULL)
+ (*protp->close)(unit, "LCP down");
+ }
+ num_np_open = 0;
+ num_np_up = 0;
+ if (phase != PHASE_DEAD)
+ new_phase(PHASE_TERMINATE);
+}
+
+/*
+ * The link is established.
+ * Proceed to the Dead, Authenticate or Network phase as appropriate.
+ */
+void
+link_established(unit)
+ int unit;
+{
+ int auth;
+ lcp_options *wo = &lcp_wantoptions[unit];
+ lcp_options *go = &lcp_gotoptions[unit];
+ lcp_options *ho = &lcp_hisoptions[unit];
+ int i;
+ struct protent *protp;
+
+ /*
+ * Tell higher-level protocols that LCP is up.
+ */
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ if (protp->protocol != PPP_LCP && protp->enabled_flag
+ && protp->lowerup != NULL)
+ (*protp->lowerup)(unit);
+
+ if (auth_required && !(go->neg_chap || go->neg_mschap ||
+ go->neg_mschapv2 || go->neg_upap)) {
+ /*
+ * We wanted the peer to authenticate itself, and it refused:
+ * if we have some address(es) it can use without auth, fine,
+ * otherwise treat it as though it authenticated with PAP using
+ * a username * of "" and a password of "". If that's not OK,
+ * boot it out.
+ */
+ if (noauth_addrs != NULL) {
+ set_allowed_addrs(unit, noauth_addrs, NULL);
+ } else if (!wo->neg_upap || !null_login(unit)) {
+ warn("peer refused to authenticate: terminating link");
+ lcp_close(unit, "peer refused to authenticate");
+ status = EXIT_PEER_AUTH_FAILED;
+ return;
+ }
+ }
+
+ new_phase(PHASE_AUTHENTICATE);
+ auth = 0;
+ if (go->neg_chap || go->neg_mschap || go->neg_mschapv2) {
+ if (go->neg_chap) {
+ if (debug)
+ dbglog("Authenticating peer with standard CHAP");
+ go->chap_mdtype = CHAP_DIGEST_MD5;
+ } else if (go->neg_mschap) {
+ if (debug)
+ dbglog("Authenticating peer with MS-CHAPv1");
+ go->chap_mdtype = CHAP_MICROSOFT;
+ } else {
+ if (debug)
+ dbglog("Authenticating peer with MS-CHAPv2");
+ go->chap_mdtype = CHAP_MICROSOFT_V2;
+ }
+ ChapAuthPeer(unit, our_name, go->chap_mdtype);
+ auth |= CHAP_PEER;
+ } else if (go->neg_upap) {
+ if (debug)
+ dbglog("Authenticating peer with PAP");
+ upap_authpeer(unit);
+ auth |= PAP_PEER;
+ }
+ if (ho->neg_chap || ho->neg_mschap || ho->neg_mschapv2) {
+ switch (ho->chap_mdtype) {
+ case CHAP_DIGEST_MD5:
+ if (debug)
+ dbglog("Authenticating to peer with standard CHAP");
+ break;
+ case CHAP_MICROSOFT:
+ if (debug)
+ dbglog("Authenticating to peer with MS-CHAPv1");
+ break;
+ case CHAP_MICROSOFT_V2:
+ if (debug)
+ dbglog("Authenticating to peer with MS-CHAPv2");
+ break;
+ default:
+ if (debug)
+ dbglog("Authenticating to peer with CHAP 0x%x", ho->chap_mdtype);
+ break;
+ }
+ ChapAuthWithPeer(unit, user, ho->chap_mdtype);
+ auth |= CHAP_WITHPEER;
+ } else if (ho->neg_upap) {
+ if (passwd[0] == '\0') {
+ passwd_from_file = 1;
+ if (!get_pap_passwd(passwd))
+ error("No secret found for PAP login");
+ }
+ if (debug)
+ dbglog("Authenticating to peer with PAP");
+ upap_authwithpeer(unit, user, passwd);
+ auth |= PAP_WITHPEER;
+ }
+ auth_pending[unit] = auth;
+
+ if (!auth)
+ network_phase(unit);
+}
+
+/*
+ * Proceed to the network phase.
+ */
+static void
+network_phase(unit)
+ int unit;
+{
+ lcp_options *go = &lcp_gotoptions[unit];
+
+ /*
+ * If the peer had to authenticate, run the auth-up script now.
+ */
+ if (go->neg_chap || go->neg_mschap || go->neg_mschapv2 || go->neg_upap) {
+ auth_state = s_up;
+ if (auth_script_state == s_down && auth_script_pid == 0) {
+ auth_script_state = s_up;
+ auth_script(_PATH_AUTHUP);
+ }
+ }
+
+ /*
+ * Process extra options from the secrets file
+ */
+ if (extra_options != NULL) {
+ option_source = (char *)extra_opt_filename;
+ option_line = extra_opt_line;
+ (void) options_from_list(extra_options, 1);
+ free_wordlist(extra_options);
+ extra_options = NULL;
+ }
+
+#ifdef CBCP_SUPPORT
+ /*
+ * If we negotiated callback, do it now.
+ */
+ if (go->neg_cbcp) {
+ new_phase(PHASE_CALLBACK);
+ (*cbcp_protent.open)(unit);
+ return;
+ }
+#endif
+
+ start_networks();
+}
+
+void
+start_networks()
+{
+ int i;
+ struct protent *protp;
+
+ new_phase(PHASE_NETWORK);
+
+#ifdef HAVE_MULTILINK
+ if (multilink) {
+ if (mp_join_bundle()) {
+ if (updetach && !nodetach)
+ detach();
+ return;
+ }
+ }
+#endif /* HAVE_MULTILINK */
+
+#if 0
+ if (!demand)
+ set_filters(&pass_filter, &active_filter);
+#endif
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ if (protp->protocol < 0xC000 && protp->enabled_flag
+ && protp->open != NULL) {
+ (*protp->open)(0);
+ if (protp->protocol != PPP_CCP)
+ ++num_np_open;
+ }
+
+ if (num_np_open == 0)
+ /* nothing to do */
+ lcp_close(0, "No network protocols running");
+}
+
+/*
+ * The peer has failed to authenticate himself using `protocol'.
+ */
+/*ARGSUSED*/
+void
+auth_peer_fail(unit, protocol)
+ int unit, protocol;
+{
+ /*
+ * Authentication failure: take the link down
+ */
+ lcp_close(unit, "Authentication failed");
+ status = EXIT_PEER_AUTH_FAILED;
+}
+
+/*
+ * The peer has been successfully authenticated using `protocol'.
+ */
+void
+auth_peer_success(unit, protocol, name, namelen)
+ int unit, protocol;
+ char *name;
+ int namelen;
+{
+ int bit;
+
+ switch (protocol) {
+ case PPP_CHAP:
+ bit = CHAP_PEER;
+ break;
+ case PPP_PAP:
+ bit = PAP_PEER;
+ break;
+ default:
+ warn("auth_peer_success: unknown protocol %x", protocol);
+ return;
+ }
+
+ /*
+ * Save the authenticated name of the peer for later.
+ */
+ if (namelen > sizeof(peer_authname) - 1)
+ namelen = sizeof(peer_authname) - 1;
+ BCOPY(name, peer_authname, namelen);
+ peer_authname[namelen] = '\0';
+ script_setenv("PEERNAME", peer_authname, 0);
+
+ /*
+ * If there is no more authentication still to be done,
+ * proceed to the network (or callback) phase.
+ */
+ if ((auth_pending[unit] &= ~bit) == 0)
+ network_phase(unit);
+}
+
+/*
+ * We have failed to authenticate ourselves to the peer using `protocol'.
+ */
+/*ARGSUSED*/
+void
+auth_withpeer_fail(unit, protocol)
+ int unit, protocol;
+{
+ if (passwd_from_file)
+ BZERO(passwd, MAXSECRETLEN);
+ /*
+ * We've failed to authenticate ourselves to our peer.
+ * Some servers keep sending CHAP challenges, but there
+ * is no point in persisting without any way to get updated
+ * authentication secrets.
+ */
+ lcp_close(unit, "Failed to authenticate ourselves to peer");
+ status = EXIT_AUTH_TOPEER_FAILED;
+}
+
+/*
+ * We have successfully authenticated ourselves with the peer using `protocol'.
+ */
+void
+auth_withpeer_success(unit, protocol)
+ int unit, protocol;
+{
+ int bit;
+
+ switch (protocol) {
+ case PPP_CHAP:
+ bit = CHAP_WITHPEER;
+ break;
+ case PPP_PAP:
+ if (passwd_from_file)
+ BZERO(passwd, MAXSECRETLEN);
+ bit = PAP_WITHPEER;
+ break;
+ default:
+ warn("auth_withpeer_success: unknown protocol %x", protocol);
+ bit = 0;
+ }
+
+ /*
+ * If there is no more authentication still being done,
+ * proceed to the network (or callback) phase.
+ */
+ if ((auth_pending[unit] &= ~bit) == 0)
+ network_phase(unit);
+}
+
+
+/*
+ * np_up - a network protocol has come up.
+ */
+/*ARGSUSED*/
+void
+np_up(unit, proto)
+ int unit, proto;
+{
+ int tlim;
+
+ if (num_np_up == 0) {
+ /*
+ * At this point we consider that the link has come up successfully.
+ */
+ status = EXIT_OK;
+ unsuccess = 0;
+
+ peer_nak_auth = unsolicited_nak_auth = peer_reject_auth =
+ rejected_peers_auth = naked_peers_auth = 0;
+ nak_auth_proto = nak_auth_orig = unsolicit_auth_proto =
+ reject_auth_proto = rejected_auth_proto = naked_auth_orig =
+ naked_auth_proto = 0;
+
+ new_phase(PHASE_RUNNING);
+
+ if (idle_time_hook != NULL)
+ tlim = (*idle_time_hook)(NULL);
+ else
+ tlim = idle_time_limit;
+ if (tlim > 0)
+ TIMEOUT(check_idle, NULL, tlim);
+
+ /*
+ * Set a timeout to close the connection once the maximum
+ * connect time has expired.
+ */
+ if (maxconnect > 0) {
+ TIMEOUT(connect_time_expired, &lcp_fsm[unit], maxconnect);
+
+ /*
+ * Tell LCP to send Time-Remaining packets. One should be
+ * sent out now, at maxconnect-300, at maxconnect-120, and
+ * again at maxconnect-30.
+ */
+ lcp_settimeremaining(unit, maxconnect, maxconnect);
+ if (maxconnect > 300)
+ lcp_settimeremaining(unit, maxconnect, 300);
+ if (maxconnect > 120)
+ lcp_settimeremaining(unit, maxconnect, 120);
+ if (maxconnect > 30)
+ lcp_settimeremaining(unit, maxconnect, 30);
+ }
+
+ /*
+ * Detach now, if the updetach option was given.
+ */
+ if (updetach && !nodetach)
+ detach();
+ }
+ ++num_np_up;
+}
+
+/*
+ * np_down - a network protocol has gone down.
+ */
+/*ARGSUSED*/
+void
+np_down(unit, proto)
+ int unit, proto;
+{
+ if (--num_np_up == 0) {
+ UNTIMEOUT(check_idle, NULL);
+ new_phase(PHASE_NETWORK);
+ }
+}
+
+/*
+ * np_finished - a network protocol has finished using the link.
+ */
+/*ARGSUSED*/
+void
+np_finished(unit, proto)
+ int unit, proto;
+{
+ if (--num_np_open <= 0) {
+ /* no further use for the link: shut up shop. */
+ lcp_close(0, "No network protocols running");
+ }
+}
+
+/*
+ * check_idle - check whether the link has been idle for long
+ * enough that we can shut it down.
+ */
+/*ARGSUSED*/
+static void
+check_idle(arg)
+ void *arg;
+{
+ struct ppp_idle idle;
+ time_t itime;
+ int tlim;
+
+ if (!get_idle_time(0, &idle))
+ return;
+ if (idle_time_hook != NULL) {
+ tlim = (*idle_time_hook)(&idle);
+ } else {
+ itime = MIN(idle.xmit_idle, idle.recv_idle);
+ tlim = idle_time_limit - itime;
+ }
+ if (tlim <= 0) {
+ /* link is idle: shut it down. */
+ notice("Terminating connection due to lack of activity.");
+ lcp_close(0, "Link inactive");
+ need_holdoff = 0;
+ status = EXIT_IDLE_TIMEOUT;
+ } else {
+ TIMEOUT(check_idle, NULL, tlim);
+ }
+}
+
+/*
+ * connect_time_expired - log a message and close the connection.
+ */
+/*ARGSUSED*/
+static void
+connect_time_expired(arg)
+ void *arg;
+{
+ fsm *f = (fsm *)arg;
+
+ info("Connect time expired");
+ lcp_close(f->unit, "Connect time expired"); /* Close connection */
+ status = EXIT_CONNECT_TIME;
+}
+
+/*
+ * auth_check_options - called to check authentication options.
+ */
+void
+auth_check_options()
+{
+ lcp_options *wo = &lcp_wantoptions[0];
+ int can_auth;
+ int lacks_ip;
+
+ /* Default our_name to hostname, and user to our_name */
+ if (our_name[0] == '\0' || usehostname)
+ (void) strlcpy(our_name, hostname, sizeof(our_name));
+ if (user[0] == '\0')
+ (void) strlcpy(user, our_name, sizeof(user));
+
+ /*
+ * If we have a default route, require the peer to authenticate
+ * unless the noauth option was given or the real user is root.
+ */
+ if (!auth_required && !allow_any_ip && have_route_to(0) && !privileged) {
+ auth_required = 1;
+ default_auth = 1;
+ }
+
+ /* If authentication is required, ask peer for CHAP or PAP. */
+ if (auth_required) {
+ if (!wo->neg_chap && !wo->neg_mschap && !wo->neg_mschapv2 &&
+ !wo->neg_upap) {
+ wo->neg_chap = 1;
+#ifdef CHAPMS
+ wo->neg_mschap = 1;
+#endif
+#ifdef CHAPMSV2
+ wo->neg_mschapv2 = 1;
+#endif
+ wo->chap_mdtype = CHAP_DIGEST_MD5;
+ wo->neg_upap = 1;
+ }
+ } else {
+ wo->neg_chap = 0;
+ wo->neg_mschap = 0;
+ wo->neg_mschapv2 = 0;
+ wo->neg_upap = 0;
+ }
+
+ /*
+ * Check whether we have appropriate secrets to use
+ * to authenticate the peer.
+ */
+ lacks_ip = 0;
+ can_auth = wo->neg_upap && (uselogin || have_pap_secret(&lacks_ip));
+ if (!can_auth && (wo->neg_chap || wo->neg_mschap || wo->neg_mschapv2)) {
+ can_auth = have_chap_secret((explicit_remote? remote_name: NULL),
+ our_name, 1, &lacks_ip);
+ }
+
+ if (auth_required && !can_auth && noauth_addrs == NULL) {
+ if (default_auth) {
+ option_error(
+"By default the remote system is required to authenticate itself");
+ option_error(
+"(because this system has a default route to the Internet)");
+ } else if (explicit_remote)
+ option_error(
+"The remote system (%s) is required to authenticate itself",
+ remote_name);
+ else
+ option_error(
+"The remote system is required to authenticate itself");
+ option_error(
+"but I couldn't find any suitable secret (password) for it to use to do so.");
+ if (lacks_ip)
+ option_error(
+"(None of the available passwords would let it use an IP address.)");
+
+ exit(1);
+ }
+}
+
+/*
+ * auth_reset - called when LCP is starting negotiations to recheck
+ * authentication options, i.e. whether we have appropriate secrets
+ * to use for authenticating ourselves and/or the peer.
+ */
+void
+auth_reset(unit)
+ int unit;
+{
+ lcp_options *go = &lcp_gotoptions[unit];
+ lcp_options *ao = &lcp_allowoptions[unit];
+ int havesecret;
+
+ ao->neg_upap = !refuse_pap && (passwd[0] != '\0' || get_pap_passwd(NULL));
+
+ havesecret = passwd[0] != '\0' ||
+ have_chap_secret(user, (explicit_remote? remote_name: NULL), 0, NULL);
+ ao->neg_chap = !refuse_chap && havesecret;
+ ao->neg_mschap = !refuse_mschap && havesecret;
+ ao->neg_mschapv2 = !refuse_mschapv2 && havesecret;
+ if (ao->neg_chap)
+ ao->chap_mdtype = CHAP_DIGEST_MD5;
+ else if (ao->neg_mschap)
+ ao->chap_mdtype = CHAP_MICROSOFT;
+ else
+ ao->chap_mdtype = CHAP_MICROSOFT_V2;
+
+ if (go->neg_upap && !uselogin && !have_pap_secret(NULL))
+ go->neg_upap = 0;
+ if (go->neg_chap || go->neg_mschap || go->neg_mschapv2) {
+ havesecret = have_chap_secret((explicit_remote? remote_name: NULL),
+ our_name, 1, NULL);
+ if (!havesecret)
+ go->neg_chap = go->neg_mschap = go->neg_mschapv2 = 0;
+ else if (go->neg_chap)
+ go->chap_mdtype = CHAP_DIGEST_MD5;
+ else if (go->neg_mschap)
+ go->chap_mdtype = CHAP_MICROSOFT;
+ else
+ go->chap_mdtype = CHAP_MICROSOFT_V2;
+ }
+}
+
+
+/*
+ * check_passwd - Check the user name and passwd against the PAP secrets
+ * file. If requested, also check against the system password database,
+ * and login the user if OK.
+ *
+ * returns:
+ * UPAP_AUTHNAK: Authentication failed.
+ * UPAP_AUTHACK: Authentication succeeded.
+ * In either case, msg points to an appropriate message.
+ */
+int
+check_passwd(unit, auser, userlen, apasswd, passwdlen, msg)
+ int unit;
+ char *auser;
+ int userlen;
+ char *apasswd;
+ int passwdlen;
+ char **msg;
+{
+ int ret;
+ char *filename;
+ FILE *f;
+ struct wordlist *addrs = NULL, *opts = NULL;
+ char passwd[256], user[256];
+ char secret[MAXWORDLEN];
+ static int attempts = 0;
+
+ /*
+ * Make copies of apasswd and auser, then null-terminate them.
+ * If there are unprintable characters in the password, make
+ * them visible.
+ */
+ (void) slprintf(passwd, sizeof(passwd), "%.*v", passwdlen, apasswd);
+ (void) slprintf(user, sizeof(user), "%.*v", userlen, auser);
+ *msg = "";
+
+ /*
+ * Check if a plugin wants to handle this.
+ */
+ if (pap_auth_hook != NULL) {
+ /* Set a default and allow the plug-in to change it. */
+ extra_opt_filename = "plugin";
+ extra_opt_line = 0;
+ ret = (*pap_auth_hook)(user, passwd, msg, &addrs, &opts);
+ if (ret >= 0) {
+ if (ret > 0)
+ set_allowed_addrs(unit, addrs, opts);
+ BZERO(passwd, sizeof(passwd));
+ if (addrs != NULL)
+ free_wordlist(addrs);
+ return ret? UPAP_AUTHACK: UPAP_AUTHNAK;
+ }
+ }
+
+ /*
+ * Open the file of pap secrets and scan for a suitable secret
+ * for authenticating this user.
+ */
+ filename = _PATH_UPAPFILE;
+ addrs = opts = NULL;
+ ret = UPAP_AUTHNAK;
+ f = fopen(filename, "r");
+ if (f == NULL) {
+ error("Can't open PAP password file %s: %m", filename);
+
+ } else {
+ check_access(f, filename);
+ if (scan_authfile(f, user, our_name, secret, &addrs, &opts, filename) < 0) {
+ warn("no PAP secret found for %s", user);
+ if (scan_server_match_failed[0] != '\0')
+ warn("possible configuration error: local name is %q, but "
+ "found %q instead", our_name, scan_server_match_failed);
+ } else if (secret[0] != '\0') {
+ /* password given in pap-secrets - must match */
+ if ((!cryptpap && strcmp(passwd, secret) == 0)
+ || strcmp(crypt(passwd, secret), secret) == 0)
+ ret = UPAP_AUTHACK;
+ else
+ warn("PAP authentication failure for %s", user);
+ } else if (uselogin) {
+ /* empty password in pap-secrets and login option */
+ ret = plogin(user, passwd, msg);
+ if (ret == UPAP_AUTHNAK)
+ warn("PAP login failure for %s", user);
+ } else {
+ /* empty password in pap-secrets and login option not used */
+ ret = UPAP_AUTHACK;
+ }
+ (void) fclose(f);
+ }
+
+ if (ret == UPAP_AUTHNAK) {
+ if (**msg == '\0')
+ *msg = "Login incorrect";
+ /*
+ * Frustrate passwd stealer programs.
+ * Allow 10 tries, but start backing off after 3 (stolen from login).
+ * On 10'th, drop the connection.
+ */
+ if (attempts++ >= 10) {
+ warn("%d LOGIN FAILURES ON %s, %s", attempts, devnam, user);
+ lcp_close(unit, "login failed");
+ }
+ if (attempts > 3)
+ (void) sleep((u_int) (attempts - 3) * 5);
+ if (opts != NULL)
+ free_wordlist(opts);
+
+ } else {
+ attempts = 0; /* Reset count */
+ if (**msg == '\0')
+ *msg = "Login ok";
+ set_allowed_addrs(unit, addrs, opts);
+ }
+
+ if (addrs != NULL)
+ free_wordlist(addrs);
+ BZERO(passwd, sizeof(passwd));
+ BZERO(secret, sizeof(secret));
+
+ return ret;
+}
+
+/*
+ * This function is needed for PAM.
+ */
+
+#ifdef ALLOW_PAM
+/* Static variables used to communicate between the conversation function
+ * and the server_login function
+ */
+static char *PAM_username;
+static char *PAM_password;
+static int PAM_error = 0;
+static pam_handle_t *pamh = NULL;
+
+/* PAM conversation function
+ * Here we assume (for now, at least) that echo on means login name, and
+ * echo off means password.
+ */
+
+/*ARGSUSED*/
+static int PAM_conv (int num_msg,
+#ifndef SOL2
+ const
+#endif
+ struct pam_message **msg, struct pam_response **resp, void *appdata_ptr)
+{
+ int replies = 0;
+ struct pam_response *reply = NULL;
+
+#define COPY_STRING(s) (s) ? strdup(s) : NULL
+
+ reply = malloc(sizeof(struct pam_response) * num_msg);
+ if (reply == NULL)
+ return PAM_CONV_ERR;
+
+ for (replies = 0; replies < num_msg; replies++) {
+ switch (msg[replies]->msg_style) {
+ case PAM_PROMPT_ECHO_ON:
+ reply[replies].resp_retcode = PAM_SUCCESS;
+ reply[replies].resp = COPY_STRING(PAM_username);
+ /* PAM frees resp */
+ break;
+ case PAM_PROMPT_ECHO_OFF:
+ reply[replies].resp_retcode = PAM_SUCCESS;
+ reply[replies].resp = COPY_STRING(PAM_password);
+ /* PAM frees resp */
+ break;
+ case PAM_TEXT_INFO:
+ /* fall through */
+ case PAM_ERROR_MSG:
+ /* ignore it, but pam still wants a NULL response... */
+ reply[replies].resp_retcode = PAM_SUCCESS;
+ reply[replies].resp = NULL;
+ break;
+ default:
+ /* Must be an error of some sort... */
+ free (reply);
+ PAM_error = 1;
+ return PAM_CONV_ERR;
+ }
+ }
+ *resp = reply;
+ return PAM_SUCCESS;
+}
+
+static struct pam_conv PAM_conversation = {
+ PAM_conv, NULL
+};
+#endif /* ALLOW_PAM */
+
+/*
+ * plogin - Check the user name and password against the system
+ * password database, and login the user if OK.
+ *
+ * returns:
+ * UPAP_AUTHNAK: Login failed.
+ * UPAP_AUTHACK: Login succeeded.
+ * In either case, msg points to an appropriate message.
+ */
+
+static int
+plogin(user, passwd, msg)
+ char *user;
+ char *passwd;
+ char **msg;
+{
+ char *tty;
+#ifdef HAS_SHADOW
+ struct spwd *spwd;
+#endif
+ struct passwd *pw = NULL;
+
+#ifdef ALLOW_PAM
+ int pam_error;
+
+ if (use_pam) {
+ if (debug)
+ dbglog("using PAM for user authentication");
+ pam_error = pam_start ("ppp", user, &PAM_conversation, &pamh);
+ if (pam_error != PAM_SUCCESS) {
+ *msg = (char *) pam_strerror (pamh, pam_error);
+ reopen_log();
+ return UPAP_AUTHNAK;
+ }
+ /*
+ * Define the fields for the credential validation
+ */
+
+ PAM_username = user;
+ PAM_password = passwd;
+ PAM_error = 0;
+ /* this might be useful to some modules; required for Solaris */
+ tty = devnam;
+ if (*tty == '\0')
+ tty = ppp_devnam;
+ (void) pam_set_item(pamh, PAM_TTY, tty);
+#ifdef PAM_RHOST
+ (void) pam_set_item(pamh, PAM_RHOST, "");
+#endif
+
+ /*
+ * Validate the user
+ */
+ pam_error = pam_authenticate (pamh, PAM_SILENT);
+ if (pam_error == PAM_SUCCESS && !PAM_error) {
+ pam_error = pam_acct_mgmt (pamh, PAM_SILENT);
+ if (pam_error == PAM_SUCCESS)
+ (void) pam_open_session (pamh, PAM_SILENT);
+ }
+
+ *msg = (char *) pam_strerror (pamh, pam_error);
+
+ /*
+ * Clean up the mess
+ */
+ reopen_log(); /* apparently the PAM stuff does closelog() */
+ PAM_username = NULL;
+ PAM_password = NULL;
+ if (pam_error != PAM_SUCCESS)
+ return UPAP_AUTHNAK;
+ } else
+#endif /* ALLOW_PAM */
+
+ {
+ if (debug) {
+#ifdef HAS_SHADOW
+ dbglog("using passwd/shadow for user authentication");
+#else
+ dbglog("using passwd for user authentication");
+#endif
+ }
+/*
+ * Use the non-PAM methods directly
+ */
+
+ pw = getpwnam(user);
+
+ endpwent();
+ if (pw == NULL)
+ return (UPAP_AUTHNAK);
+
+#ifdef HAS_SHADOW
+ spwd = getspnam(user);
+ endspent();
+ if (spwd != NULL) {
+ /* check the age of the password entry */
+ long now = time(NULL) / 86400L;
+
+ if ((spwd->sp_expire > 0 && now >= spwd->sp_expire)
+ || ((spwd->sp_max >= 0 && spwd->sp_max < 10000)
+ && spwd->sp_lstchg >= 0
+ && now >= spwd->sp_lstchg + spwd->sp_max)) {
+ warn("Password for %s has expired", user);
+ return (UPAP_AUTHNAK);
+ }
+ pw->pw_passwd = spwd->sp_pwdp;
+ }
+#endif
+
+ /*
+ * If no passwd, don't let them login.
+ */
+ if (pw->pw_passwd == NULL || strlen(pw->pw_passwd) < 2 ||
+ strcmp(crypt(passwd, pw->pw_passwd), pw->pw_passwd) != 0)
+ return (UPAP_AUTHNAK);
+ }
+
+ /*
+ * Write a wtmp entry for this user.
+ */
+
+ tty = devnam;
+ if (strncmp(tty, "/dev/", 5) == 0)
+ tty += 5;
+ logwtmp(tty, user, remote_name); /* Add wtmp login entry */
+
+#ifdef _PATH_LASTLOG
+ if (!use_pam && pw != (struct passwd *)NULL) {
+ struct lastlog ll;
+ int fd;
+
+ if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) {
+ (void)lseek(fd, (off_t)(pw->pw_uid * sizeof(ll)), SEEK_SET);
+ BZERO((void *)&ll, sizeof(ll));
+ (void)time(&ll.ll_time);
+ (void)strncpy(ll.ll_line, tty, sizeof(ll.ll_line));
+ (void)write(fd, (char *)&ll, sizeof(ll));
+ (void)close(fd);
+ }
+ }
+#endif /* _PATH_LASTLOG */
+
+ info("user %s logged in", user);
+ logged_in = 1;
+
+ return (UPAP_AUTHACK);
+}
+
+/*
+ * plogout - Logout the user.
+ */
+static void
+plogout()
+{
+ char *tty;
+
+#ifdef ALLOW_PAM
+ int pam_error;
+
+ if (use_pam) {
+ if (pamh != NULL) {
+ pam_error = pam_close_session (pamh, PAM_SILENT);
+ (void) pam_end (pamh, pam_error);
+ pamh = NULL;
+ }
+ /* Apparently the pam stuff does closelog(). */
+ reopen_log();
+ } else
+#endif /* ALLOW_PAM */
+
+ {
+ tty = devnam;
+ if (strncmp(tty, "/dev/", 5) == 0)
+ tty += 5;
+ /* Wipe out utmp logout entry */
+ logwtmp(tty, "", "");
+ }
+ logged_in = 0;
+}
+
+
+/*
+ * null_login - Check if a username of "" and a password of "" are
+ * acceptable, and iff so, set the list of acceptable IP addresses
+ * and return 1.
+ */
+static int
+null_login(unit)
+ int unit;
+{
+ char *filename;
+ FILE *f;
+ int i, ret;
+ struct wordlist *addrs, *opts;
+ char secret[MAXWORDLEN];
+
+ /*
+ * Open the file of pap secrets and scan for a suitable secret.
+ */
+ filename = _PATH_UPAPFILE;
+ addrs = NULL;
+ f = fopen(filename, "r");
+ if (f == NULL)
+ return 0;
+ check_access(f, filename);
+
+ i = scan_authfile(f, "", our_name, secret, &addrs, &opts, filename);
+ ret = i >= 0 && secret[0] == '\0';
+ BZERO(secret, sizeof(secret));
+
+ if (ret)
+ set_allowed_addrs(unit, addrs, opts);
+ else if (opts != NULL)
+ free_wordlist(opts);
+ if (addrs != NULL)
+ free_wordlist(addrs);
+
+ (void) fclose(f);
+ return ret;
+}
+
+
+/*
+ * get_pap_passwd - get a password for authenticating ourselves with
+ * our peer using PAP. Returns 1 on success, 0 if no suitable password
+ * could be found.
+ * Assumes passwd points to MAXSECRETLEN bytes of space (if non-null).
+ */
+static int
+get_pap_passwd(passwd)
+ char *passwd;
+{
+ char *filename;
+ FILE *f;
+ int ret;
+ char secret[MAXWORDLEN];
+
+ /*
+ * Check whether a plugin wants to supply this.
+ */
+ if (pap_passwd_hook != NULL) {
+ ret = (*pap_passwd_hook)(user, passwd);
+ if (ret >= 0)
+ return ret;
+ }
+
+ filename = _PATH_UPAPFILE;
+ f = fopen(filename, "r");
+ if (f == NULL)
+ return 0;
+ check_access(f, filename);
+ ret = scan_authfile(f, user,
+ (remote_name[0] != '\0' ? remote_name: NULL),
+ secret, NULL, NULL, filename);
+ (void) fclose(f);
+ if (ret < 0)
+ return 0;
+ if (passwd != NULL)
+ (void) strlcpy(passwd, secret, MAXSECRETLEN);
+ BZERO(secret, sizeof(secret));
+ return 1;
+}
+
+
+/*
+ * have_pap_secret - check whether we have a PAP file with any
+ * secrets that we could possibly use for authenticating the peer.
+ */
+static int
+have_pap_secret(lacks_ipp)
+ int *lacks_ipp;
+{
+ FILE *f;
+ int ret;
+ char *filename;
+ struct wordlist *addrs;
+
+ /* let the plugin decide, if there is one */
+ if (pap_check_hook != NULL) {
+ ret = (*pap_check_hook)();
+ if (ret >= 0)
+ return ret;
+ }
+
+ filename = _PATH_UPAPFILE;
+ f = fopen(filename, "r");
+ if (f == NULL)
+ return 0;
+
+ ret = scan_authfile(f, (explicit_remote? remote_name: NULL), our_name,
+ NULL, &addrs, NULL, filename);
+ (void) fclose(f);
+ if (ret >= 0 && !some_ip_ok(addrs)) {
+ if (lacks_ipp != NULL)
+ *lacks_ipp = 1;
+ ret = -1;
+ }
+ if (addrs != NULL)
+ free_wordlist(addrs);
+
+ return ret >= 0;
+}
+
+
+/*
+ * have_chap_secret - check whether we have a CHAP file with a secret
+ * that we could possibly use for authenticating `client' on `server'.
+ * Either or both can be the null string, meaning we don't know the
+ * identity yet.
+ */
+static int
+have_chap_secret(client, server, need_ip, lacks_ipp)
+ char *client;
+ char *server;
+ int need_ip;
+ int *lacks_ipp;
+{
+ FILE *f;
+ int ret;
+ char *filename;
+ struct wordlist *addrs;
+
+ filename = _PATH_CHAPFILE;
+ f = fopen(filename, "r");
+ if (f == NULL)
+ return 0;
+
+ if (client != NULL && client[0] == '\0')
+ client = NULL;
+ if (server != NULL && server[0] == '\0')
+ server = NULL;
+
+ ret = scan_authfile(f, client, server, NULL, &addrs, NULL, filename);
+ (void) fclose(f);
+ if (ret >= 0 && need_ip && !some_ip_ok(addrs)) {
+ if (lacks_ipp != NULL)
+ *lacks_ipp = 1;
+ ret = -1;
+ }
+ if (addrs != NULL)
+ free_wordlist(addrs);
+
+ return ret >= 0;
+}
+
+
+/*
+ * get_secret - open the CHAP secret file and return the secret
+ * for authenticating the given client on the given server.
+ *
+ * "am_server" means that we're the authenticator (demanding
+ * identity from the peer).
+ *
+ * "!am_server" means that we're the authenticatee (supplying
+ * identity to the peer).
+ */
+int
+get_secret(unit, client, server, secret, secret_len, am_server)
+ int unit;
+ char *client;
+ char *server;
+ char *secret;
+ int *secret_len;
+ int am_server;
+{
+ FILE *f;
+ int ret, len;
+ char *filename;
+ struct wordlist *addrs, *opts;
+ char secbuf[MAXWORDLEN];
+
+ /*
+ * Support the 'password' option on authenticatee only in order to
+ * avoid obvious security problem (authenticator and authenticatee
+ * within a given implementation must never share secrets).
+ */
+ if (!am_server && passwd[0] != '\0') {
+ (void) strlcpy(secbuf, passwd, sizeof(secbuf));
+ } else {
+ filename = _PATH_CHAPFILE;
+ addrs = NULL;
+ secbuf[0] = '\0';
+
+ f = fopen(filename, "r");
+ if (f == NULL) {
+ error("Can't open chap secret file %s: %m", filename);
+ return 0;
+ }
+ check_access(f, filename);
+
+ ret = scan_authfile(f, client, server, secbuf, &addrs, &opts,
+ filename);
+ (void) fclose(f);
+ if (ret < 0) {
+ if (scan_server_match_failed[0] != '\0')
+ warn("possible configuration error: local name is %q, but "
+ "found %q instead", our_name, scan_server_match_failed);
+ return 0;
+ }
+
+ /* Only the authenticator cares about limiting peer addresses. */
+ if (am_server)
+ set_allowed_addrs(unit, addrs, opts);
+ else if (opts != NULL)
+ free_wordlist(opts);
+ if (addrs != NULL)
+ free_wordlist(addrs);
+ }
+
+ len = strlen(secbuf);
+ if (len > MAXSECRETLEN) {
+ error("Secret for %s on %s is too long", client, server);
+ len = MAXSECRETLEN;
+ }
+ /* Do not leave a temporary copy of the secret on the stack. */
+ BCOPY(secbuf, secret, len);
+ BZERO(secbuf, sizeof(secbuf));
+ *secret_len = len;
+
+ return 1;
+}
+
+/*
+ * set_allowed_addrs() - set the list of allowed addresses.
+ * The caller must also look for `--' indicating options to apply for
+ * this peer and leaves the following words in extra_options.
+ */
+static void
+set_allowed_addrs(unit, addrs, opts)
+ int unit;
+ struct wordlist *addrs;
+ struct wordlist *opts;
+{
+ int n;
+ struct wordlist *ap, **pap;
+ struct permitted_ip *ip;
+ char *ptr_word, *ptr_mask;
+ struct hostent *hp;
+ struct netent *np;
+ u_int32_t a, mask, newmask, ah, offset;
+ struct ipcp_options *wo = &ipcp_wantoptions[unit];
+ u_int32_t suggested_ip = 0;
+ int err_num;
+
+ if (addresses[unit] != NULL)
+ free(addresses[unit]);
+ addresses[unit] = NULL;
+ if (extra_options != NULL)
+ free_wordlist(extra_options);
+ extra_options = opts;
+
+ /*
+ * Count the number of IP addresses given.
+ */
+ for (n = 0, pap = &addrs; (ap = *pap) != NULL; pap = &ap->next)
+ ++n;
+ if (n == 0)
+ return;
+ ip = (struct permitted_ip *) malloc((n + 1) * sizeof(struct permitted_ip));
+ if (ip == NULL)
+ return;
+
+ n = 0;
+ for (ap = addrs; ap != NULL; ap = ap->next) {
+ /* "-" means no addresses authorized, "*" means any address allowed */
+ ptr_word = ap->word;
+ if (strcmp(ptr_word, "-") == 0)
+ break;
+ if (strcmp(ptr_word, "*") == 0) {
+ ip[n].permit = 1;
+ ip[n].base = ip[n].mask = 0;
+ ++n;
+ break;
+ }
+
+ ip[n].permit = 1;
+ if (*ptr_word == '!') {
+ ip[n].permit = 0;
+ ++ptr_word;
+ }
+
+ mask = ~ (u_int32_t) 0;
+ offset = 0;
+ ptr_mask = strchr (ptr_word, '/');
+ if (ptr_mask != NULL) {
+ int bit_count;
+ char *endp;
+
+ bit_count = (int) strtol (ptr_mask+1, &endp, 10);
+ if (bit_count <= 0 || bit_count > 32) {
+ warn("invalid address length %v in authorized address list",
+ ptr_mask+1);
+ continue;
+ }
+ bit_count = 32 - bit_count; /* # bits in host part */
+ if (*endp == '+') {
+ offset = ifunit + 1;
+ ++endp;
+ }
+ if (*endp != '\0') {
+ warn("invalid address length syntax: %v", ptr_mask+1);
+ continue;
+ }
+ *ptr_mask = '\0';
+ mask <<= bit_count;
+ }
+
+ /* Try to interpret value as host name or numeric address first */
+ hp = getipnodebyname(ptr_word, AF_INET, 0, &err_num);
+ if (hp != NULL) {
+ (void) memcpy(&a, hp->h_addr, sizeof(a));
+ freehostent(hp);
+ } else {
+ char *cp = ptr_word + strlen(ptr_word);
+ if (cp > ptr_word)
+ cp--;
+ if (*cp == '+') {
+ offset = ifunit + 1;
+ *cp = '\0';
+ }
+ np = getnetbyname (ptr_word);
+ if (np != NULL && np->n_addrtype == AF_INET) {
+ ah = np->n_net;
+ newmask = (u_int32_t)~0;
+ if ((ah & 0xff000000ul) == 0)
+ ah <<= 8, newmask <<= 8;
+ if ((ah & 0xff000000ul) == 0)
+ ah <<= 8, newmask <<= 8;
+ if ((ah & 0xff000000ul) == 0)
+ ah <<= 8, newmask <<= 8;
+ if (ptr_mask == NULL)
+ mask = newmask;
+ a = htonl(ah);
+ }
+ }
+
+ if (ptr_mask != NULL)
+ *ptr_mask = '/';
+
+ if (a == (u_int32_t)-1L) {
+ warn("unknown host %s in auth. address list", ap->word);
+ continue;
+ }
+ if (offset != 0) {
+ if (offset >= ~mask) {
+ warn("interface unit %d too large for subnet %v",
+ ifunit, ptr_word);
+ continue;
+ }
+ a = htonl((ntohl(a) & mask) + offset);
+ mask = ~(u_int32_t)0;
+ }
+ ip[n].mask = htonl(mask);
+ ip[n].base = a & ip[n].mask;
+ ++n;
+ if (~mask == 0 && suggested_ip == 0)
+ suggested_ip = a;
+ }
+
+ /* Sentinel value at end of list */
+ ip[n].permit = 0; /* make the last entry forbid all addresses */
+ ip[n].base = 0; /* to terminate the list */
+ ip[n].mask = 0;
+
+ addresses[unit] = ip;
+
+ /*
+ * If the address given for the peer isn't authorized, or if
+ * the user hasn't given one, AND there is an authorized address
+ * which is a single host, then use that if we find one.
+ */
+ if (suggested_ip != 0
+ && (wo->hisaddr == 0 || !auth_ip_addr(unit, wo->hisaddr)))
+ wo->hisaddr = suggested_ip;
+}
+
+/*
+ * auth_ip_addr - check whether the peer is authorized to use
+ * a given IP address. Returns 1 if authorized, 0 otherwise.
+ */
+int
+auth_ip_addr(unit, addr)
+ int unit;
+ u_int32_t addr;
+{
+ int ok;
+
+ /* don't allow loopback or multicast address */
+ if (bad_ip_adrs(addr))
+ return 0;
+
+ if (addresses[unit] != NULL) {
+ ok = ip_addr_check(addr, addresses[unit]);
+ if (ok >= 0)
+ return ok;
+ }
+ if (auth_required)
+ return 0; /* no addresses authorized */
+ return allow_any_ip || privileged || !have_route_to(addr);
+}
+
+static int
+ip_addr_check(addr, addrs)
+ u_int32_t addr;
+ struct permitted_ip *addrs;
+{
+ /* This loop is safe because of the sentinel value in set_allowed_addrs */
+ for (; ; ++addrs)
+ if ((addr & addrs->mask) == addrs->base)
+ return addrs->permit;
+}
+
+/*
+ * bad_ip_adrs - return 1 if the IP address is one we don't want
+ * to use, such as an address in the loopback net or a multicast address.
+ * addr is in network byte order.
+ */
+int
+bad_ip_adrs(addr)
+ u_int32_t addr;
+{
+ addr = ntohl(addr);
+ return
+#ifndef ALLOW_127_NET
+ (addr >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET ||
+#endif
+#ifndef ALLOW_0_NET
+ ((addr >> IN_CLASSA_NSHIFT) == 0 && addr != 0) ||
+#endif
+ IN_MULTICAST(addr) || IN_BADCLASS(addr);
+}
+
+/*
+ * some_ip_ok - check a wordlist to see if it authorizes any
+ * IP address(es).
+ */
+static int
+some_ip_ok(addrs)
+ struct wordlist *addrs;
+{
+ for (; addrs != NULL; addrs = addrs->next) {
+ if (addrs->word[0] == '-')
+ break;
+ if (addrs->word[0] != '!')
+ return 1; /* some IP address is allowed */
+ }
+ return 0;
+}
+
+/*
+ * check_access - complain if a secret file has too-liberal permissions.
+ */
+static void
+check_access(f, filename)
+ FILE *f;
+ char *filename;
+{
+ struct stat sbuf;
+
+ if (fstat(fileno(f), &sbuf) < 0) {
+ warn("cannot stat secret file %s: %m", filename);
+ } else if ((sbuf.st_mode & (S_IRWXG | S_IRWXO)) != 0) {
+ warn("Warning - secret file %s has world and/or group access",
+ filename);
+ }
+}
+
+
+/*
+ * scan_authfile - Scan an authorization file for a secret suitable
+ * for authenticating `client' on `server'. The return value is -1 if
+ * no secret is found, otherwise >= 0. The return value has
+ * NONWILD_CLIENT set if the secret didn't have "*" for the client,
+ * and NONWILD_SERVER set if the secret didn't have "*" for the
+ * server.
+
+ * Any following words on the line up to a "--" (i.e. address
+ * authorization info) are placed in a wordlist and returned in
+ * *addrs. Any following words (extra options) are placed in a
+ * wordlist and returned in *opts. If opts is NULL, these are just
+ * discarded. Otherwise, the extra_opt_* variables are set to
+ * indicate the source of the options.
+ *
+ * We assume secret is NULL or points to MAXWORDLEN bytes of space.
+ */
+static int
+scan_authfile(f, client, server, secret, addrs, opts, filename)
+ FILE *f;
+ char *client;
+ char *server;
+ char *secret;
+ struct wordlist **addrs;
+ struct wordlist **opts;
+ char *filename;
+{
+ int newline, xxx, sline;
+ int got_flag, best_flag;
+ FILE *sf;
+ struct wordlist *ap, *addr_list, *alist, **app;
+ char word[MAXWORDLEN];
+ char atfile[MAXWORDLEN];
+ char lsecret[MAXWORDLEN];
+
+ scan_server_match_failed[0] = '\0';
+
+ if (addrs != NULL)
+ *addrs = NULL;
+ if (opts != NULL)
+ *opts = NULL;
+ addr_list = NULL;
+ option_line = 0;
+ if (!getword(f, word, &newline, filename)) {
+ if (debug)
+ dbglog("%s is apparently empty", filename);
+ return -1; /* file is empty??? */
+ }
+ newline = 1;
+ best_flag = -1;
+ for (;;) {
+ /*
+ * Skip until we find a word at the start of a line.
+ */
+ while (!newline && getword(f, word, &newline, filename))
+ ;
+ if (!newline)
+ break; /* got to end of file */
+
+ sline = option_line;
+ /*
+ * Got a client - check if it's a match or a wildcard.
+ */
+ got_flag = 0;
+ if (client != NULL && strcmp(word, client) != 0 && !ISWILD(word)) {
+ newline = 0;
+ continue;
+ }
+ if (!ISWILD(word))
+ got_flag = NONWILD_CLIENT;
+
+ /*
+ * Now get a server and check if it matches.
+ */
+ if (!getword(f, word, &newline, filename))
+ break;
+ if (newline)
+ continue;
+ if (!ISWILD(word)) {
+ if (server != NULL && strcmp(word, server) != 0) {
+ (void) strcpy(scan_server_match_failed, word);
+ continue;
+ }
+ got_flag |= NONWILD_SERVER;
+ }
+
+ /*
+ * Got some sort of a match - see if it's better than what
+ * we have already.
+ */
+ if (got_flag <= best_flag)
+ continue;
+
+ /*
+ * Get the secret.
+ */
+ if (!getword(f, word, &newline, filename))
+ break;
+ if (newline)
+ continue;
+
+ /*
+ * Special syntax: @filename means read secret from file.
+ * Because the secrets files are modifiable only by root,
+ * it's safe to open this file as root. One small addition --
+ * if open fails, we try as the regular user; just in case
+ * it's over NFS and not root-equivalent.
+ */
+ if (word[0] == '@') {
+ (void) strlcpy(atfile, word+1, sizeof(atfile));
+ if ((sf = fopen(atfile, "r")) == NULL) {
+ (void) seteuid(getuid());
+ sf = fopen(atfile, "r");
+ (void) seteuid(0);
+ }
+ if (sf == NULL) {
+ warn("can't open indirect secret file %s: %m", atfile);
+ continue;
+ }
+ check_access(sf, atfile);
+ if (!getword(sf, word, &xxx, atfile)) {
+ warn("no secret in indirect secret file %s", atfile);
+ (void) fclose(sf);
+ continue;
+ }
+ (void) fclose(sf);
+ }
+ if (secret != NULL)
+ (void) strlcpy(lsecret, word, sizeof(lsecret));
+
+ /*
+ * Now read address authorization info and make a wordlist.
+ */
+ app = &alist;
+ for (;;) {
+ if (!getword(f, word, &newline, filename) || newline)
+ break;
+ ap = (struct wordlist *) malloc(sizeof(struct wordlist));
+ if (ap == NULL)
+ novm("authorized addresses");
+ ap->word = strdup(word);
+ if (ap->word == NULL)
+ novm("authorized addresses");
+ *app = ap;
+ app = &ap->next;
+ }
+ *app = NULL;
+
+ /*
+ * This is the best so far; remember it.
+ */
+ best_flag = got_flag;
+ if (addr_list != NULL)
+ free_wordlist(addr_list);
+ addr_list = alist;
+ if (secret != NULL)
+ (void) strlcpy(secret, lsecret, MAXWORDLEN);
+
+ if (opts != NULL) {
+ extra_opt_filename = filename;
+ extra_opt_line = sline;
+ }
+
+ if (!newline)
+ break;
+ }
+
+ /* scan for a -- word indicating the start of options */
+ for (app = &addr_list; (ap = *app) != NULL; app = &ap->next)
+ if (strcmp(ap->word, "--") == 0)
+ break;
+ /* ap = start of options */
+ if (ap != NULL) {
+ ap = ap->next; /* first option */
+ free(*app); /* free the "--" word */
+ *app = NULL; /* terminate addr list */
+ }
+ if (opts != NULL)
+ *opts = ap;
+ else if (ap != NULL)
+ free_wordlist(ap);
+ if (addrs != NULL)
+ *addrs = addr_list;
+ else if (addr_list != NULL)
+ free_wordlist(addr_list);
+
+ return best_flag;
+}
+
+/*
+ * free_wordlist - release memory allocated for a wordlist.
+ */
+static void
+free_wordlist(wp)
+ struct wordlist *wp;
+{
+ struct wordlist *next;
+
+ while (wp != NULL) {
+ next = wp->next;
+ free(wp);
+ wp = next;
+ }
+}
+
+/*
+ * auth_script_done - called when the auth-up or auth-down script
+ * has finished.
+ */
+/*ARGSUSED*/
+static void
+auth_script_done(arg, status)
+ void *arg;
+ int status;
+{
+ auth_script_pid = 0;
+ switch (auth_script_state) {
+ case s_up:
+ if (auth_state == s_down) {
+ auth_script_state = s_down;
+ auth_script(_PATH_AUTHDOWN);
+ }
+ break;
+ case s_down:
+ if (auth_state == s_up) {
+ auth_script_state = s_up;
+ auth_script(_PATH_AUTHUP);
+ }
+ break;
+ }
+}
+
+/*
+ * auth_script - execute a script with arguments
+ * interface-name peer-name real-user tty speed
+ */
+static void
+auth_script(script)
+ char *script;
+{
+ char strspeed[32];
+ struct passwd *pw;
+ char struid[32];
+ char *user_name;
+ char *argv[8];
+
+ if ((pw = getpwuid(getuid())) != NULL && pw->pw_name != NULL)
+ user_name = pw->pw_name;
+ else {
+ (void) slprintf(struid, sizeof(struid), "%d", getuid());
+ user_name = struid;
+ }
+ (void) slprintf(strspeed, sizeof(strspeed), "%d", baud_rate);
+
+ argv[0] = script;
+ argv[1] = ifname;
+ argv[2] = peer_authname;
+ argv[3] = user_name;
+ argv[4] = devnam;
+ argv[5] = strspeed;
+ argv[6] = NULL;
+
+ auth_script_pid = run_program(script, argv, 0, auth_script_done, NULL);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppd/cbcp.c b/usr/src/cmd/cmd-inet/usr.bin/pppd/cbcp.c
new file mode 100644
index 0000000000..ca5ebab975
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppd/cbcp.c
@@ -0,0 +1,490 @@
+/*
+ * cbcp - Call Back Configuration Protocol.
+ *
+ * Copyright (c) 2000 by Sun Microsystems, Inc.
+ * All rights reserved.
+ *
+ * Copyright (c) 1995 Pedro Roque Marques
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Pedro Roque Marques. The name of the author may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+#define RCSID "$Id: cbcp.c,v 1.10 1999/08/13 06:46:10 paulus Exp $"
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include "pppd.h"
+#include "cbcp.h"
+#include "fsm.h"
+#include "lcp.h"
+
+#if !defined(lint) && !defined(_lint)
+static const char rcsid[] = RCSID;
+#endif
+
+/*
+ * Options.
+ */
+static int setcbcp __P((char **, option_t *));
+
+static option_t cbcp_option_list[] = {
+ { "callback", o_special, (void *)setcbcp,
+ "Ask for callback" },
+ { NULL }
+};
+
+/*
+ * Protocol entry points.
+ */
+static void cbcp_init __P((int unit));
+static void cbcp_lowerup __P((int unit));
+static void cbcp_input __P((int unit, u_char *pkt, int len));
+static void cbcp_protrej __P((int unit));
+static int cbcp_printpkt __P((u_char *pkt, int len,
+ void (*printer) __P((void *, const char *, ...)),
+ void *arg));
+
+struct protent cbcp_protent = {
+ PPP_CBCP, /* PPP protocol number */
+ cbcp_init, /* Initialization procedure */
+ cbcp_input, /* Process a received packet */
+ cbcp_protrej, /* Process a received protocol-reject */
+ cbcp_lowerup, /* Lower layer has come up */
+ NULL, /* Lower layer has gone down */
+ NULL, /* Open the protocol */
+ NULL, /* Close the protocol */
+ cbcp_printpkt, /* Print a packet in readable form */
+ NULL, /* Process a received data packet */
+ 0, /* 0 iff protocol is disabled */
+ "CBCP", /* Text name of protocol */
+ NULL, /* Text name of corresponding data protocol */
+ cbcp_option_list, /* List of command-line options */
+ NULL, /* Check requested options, assign defaults */
+ NULL, /* Configure interface for demand-dial */
+ NULL /* Say whether to bring up link for this pkt */
+};
+
+/* Not static'd for plug-ins */
+cbcp_state cbcp[NUM_PPP];
+
+/* internal prototypes */
+
+static void cbcp_recvreq __P((cbcp_state *us, u_char *pckt, int len));
+static void cbcp_recvack __P((cbcp_state *us, u_char *pckt, int len));
+static void cbcp_send __P((cbcp_state *us, int code, u_char *buf, int len));
+
+/* option processing */
+/*ARGSUSED*/
+static int
+setcbcp(argv, opt)
+ char **argv;
+ option_t *opt;
+{
+ lcp_wantoptions[0].neg_cbcp = 1;
+ cbcp_protent.enabled_flag = 1;
+ cbcp[0].us_number = strdup(*argv);
+ if (cbcp[0].us_number == NULL)
+ novm("callback number");
+ cbcp[0].us_type |= (1 << CB_CONF_USER);
+ cbcp[0].us_type |= (1 << CB_CONF_ADMIN);
+ return (1);
+}
+
+/* init state */
+static void
+cbcp_init(unit)
+ int unit;
+{
+ cbcp_state *us;
+
+ us = &cbcp[unit];
+ BZERO(us, sizeof(cbcp_state));
+ us->us_unit = unit;
+ us->us_type |= (1 << CB_CONF_NO);
+}
+
+/* lower layer is up */
+static void
+cbcp_lowerup(unit)
+ int unit;
+{
+ cbcp_state *us = &cbcp[unit];
+
+ if (debug) {
+ dbglog("cbcp_lowerup: want: %d", us->us_type);
+
+ if (us->us_type == CB_CONF_USER)
+ dbglog("phone no: %s", us->us_number);
+ }
+}
+
+/* process an incoming packet */
+static void
+cbcp_input(unit, inpacket, pktlen)
+ int unit;
+ u_char *inpacket;
+ int pktlen;
+{
+ u_char *inp;
+ u_char code, id;
+ u_short len;
+
+ cbcp_state *us = &cbcp[unit];
+
+ inp = inpacket;
+
+ if (pktlen < CBCP_MINLEN) {
+ error("CBCP packet is too small (%d < %d)", pktlen, CBCP_MINLEN);
+ return;
+ }
+
+ GETCHAR(code, inp);
+ GETCHAR(id, inp);
+ GETSHORT(len, inp);
+
+ if (len > pktlen) {
+ error("CBCP packet: invalid length (%d > %d)", len, pktlen);
+ return;
+ }
+
+ len -= CBCP_MINLEN;
+
+ switch (code) {
+ case CBCP_REQ:
+ us->us_id = id;
+ cbcp_recvreq(us, inp, len);
+ break;
+
+ case CBCP_RESP:
+ if (debug)
+ dbglog("CBCP Response received; no request sent");
+ break;
+
+ case CBCP_ACK:
+ if (id != us->us_id) {
+ if (debug)
+ dbglog("CBCP Ack ID %d doesn't match expected %d", id,
+ us->us_id);
+ break;
+ }
+
+ cbcp_recvack(us, inp, len);
+ break;
+
+ default:
+ if (debug)
+ dbglog("Unknown CBCP code number %d", code);
+ break;
+ }
+}
+
+/* protocol was rejected by foe */
+/*ARGSUSED*/
+static void
+cbcp_protrej(int unit)
+{
+ start_networks();
+}
+
+static char *cbcp_codenames[] = {
+ "Request", "Response", "Ack"
+};
+
+static char *cbcp_optionnames[] = {
+ "NoCallback",
+ "UserDefined",
+ "AdminDefined",
+ "List"
+};
+
+/*
+ * Pretty print a packet. Return value is number of bytes parsed out
+ * of the packet and printed in some way. Caller (in util.c) will
+ * print the remainder of the packet.
+ */
+static int
+cbcp_printpkt(p, plen, printer, arg)
+ u_char *p;
+ int plen;
+ void (*printer) __P((void *, const char *, ...));
+ void *arg;
+{
+ int code, id, len, olen, alen;
+ u_char *pstart, cichar;
+
+ if (plen < HEADERLEN) {
+ printer(arg, "too short (%d<%d)", plen, HEADERLEN);
+ return (0);
+ }
+ pstart = p;
+ GETCHAR(code, p);
+ GETCHAR(id, p);
+ GETSHORT(len, p);
+
+ if (code >= 1 && code <= Dim(cbcp_codenames))
+ printer(arg, " %s", cbcp_codenames[code-1]);
+ else
+ printer(arg, " code=0x%x", code);
+
+ printer(arg, " id=0x%x", id);
+
+ if (len < HEADERLEN) {
+ printer(arg, " header length %d<%d", len, HEADERLEN);
+ return (HEADERLEN);
+ }
+ if (len > plen) {
+ printer(arg, " truncated (%d>%d)", len, plen);
+ len = plen;
+ }
+ len -= HEADERLEN;
+
+ switch (code) {
+ case CBCP_REQ:
+ case CBCP_RESP:
+ case CBCP_ACK:
+ while (len >= 2) {
+ GETCHAR(cichar, p);
+ GETCHAR(olen, p);
+
+ if (olen < 2)
+ break;
+
+ printer(arg, " <");
+
+ if (olen > len) {
+ printer(arg, "trunc[%d>%d] ", olen, len);
+ olen = len;
+ }
+ len -= olen;
+ olen -= 2;
+
+ if (cichar >= 1 && cichar <= Dim(cbcp_optionnames))
+ printer(arg, " %s", cbcp_optionnames[cichar-1]);
+ else
+ printer(arg, " option=0x%x", cichar);
+
+ if (olen > 0) {
+ GETCHAR(cichar, p);
+ olen--;
+ printer(arg, " delay=%d", cichar);
+ }
+
+ while (olen > 0) {
+ GETCHAR(cichar, p);
+ olen--;
+ if (cichar != 1)
+ printer(arg, " (type %d?)", cichar);
+ alen = strllen((const char *)p, olen);
+ if (olen > 0 && alen > 0)
+ printer(arg, " '%.*s'", alen, p);
+ else
+ printer(arg, " null");
+ p += alen + 1;
+ olen -= alen + 1;
+ }
+ printer(arg, ">");
+ }
+
+ default:
+ break;
+ }
+
+ if (len > 0) {
+ if (len > 8)
+ printer(arg, "%8B ...", p);
+ else
+ printer(arg, "%.*B", len, p);
+ }
+ p += len;
+
+ return p - pstart;
+}
+
+/*
+ * received CBCP request.
+ * No reason to print packet contents in detail here, since enabling
+ * debug mode will cause the print routine above to be invoked.
+ */
+static void
+cbcp_recvreq(us, pckt, pcktlen)
+ cbcp_state *us;
+ u_char *pckt;
+ int pcktlen;
+{
+ u_char type, opt_len;
+ int len = pcktlen;
+ u_char cb_type;
+ u_char buf[256];
+ u_char *bufp = buf;
+
+ us->us_allowed = 0;
+ while (len > 0) {
+ GETCHAR(type, pckt);
+ GETCHAR(opt_len, pckt);
+
+ if (opt_len > 2) {
+ pckt++; /* ignore the delay time */
+ }
+
+ len -= opt_len;
+
+ /*
+ * Careful; don't use left-shift operator on numbers that are
+ * too big.
+ */
+ if (type > CB_CONF_LIST) {
+ if (debug)
+ dbglog("CBCP: ignoring unknown type %d", type);
+ continue;
+ }
+
+ us->us_allowed |= (1 << type);
+
+ switch (type) {
+ case CB_CONF_NO:
+ if (debug)
+ dbglog("CBCP: operation without callback allowed");
+ break;
+
+ case CB_CONF_USER:
+ if (debug)
+ dbglog("callback to user-specified number allowed");
+ break;
+
+ case CB_CONF_ADMIN:
+ if (debug)
+ dbglog("CBCP: callback to admin-defined address allowed");
+ break;
+
+ case CB_CONF_LIST:
+ if (debug)
+ dbglog("CBCP: callback to one out of list allowed");
+ break;
+ }
+ }
+
+ /* Now generate the response */
+ len = 0;
+ cb_type = us->us_allowed & us->us_type;
+
+ if (cb_type & ( 1 << CB_CONF_USER ) ) {
+ if (debug)
+ dbglog("CBCP Response: selecting user-specified number");
+ PUTCHAR(CB_CONF_USER, bufp);
+ len = 3 + 1 + strlen(us->us_number) + 1;
+ PUTCHAR(len , bufp);
+ PUTCHAR(5, bufp); /* delay */
+ PUTCHAR(1, bufp);
+ BCOPY(us->us_number, bufp, strlen(us->us_number) + 1);
+ cbcp_send(us, CBCP_RESP, buf, len);
+ return;
+ }
+
+ if (cb_type & ( 1 << CB_CONF_ADMIN ) ) {
+ if (debug)
+ dbglog("CBCP Response: selecting admin-specified number");
+ PUTCHAR(CB_CONF_ADMIN, bufp);
+ len = 3;
+ PUTCHAR(len, bufp);
+ PUTCHAR(5, bufp); /* delay */
+ cbcp_send(us, CBCP_RESP, buf, len);
+ return;
+ }
+
+ if (cb_type & ( 1 << CB_CONF_NO ) ) {
+ if (debug)
+ dbglog("CBCP Response: selecting no-callback mode");
+ PUTCHAR(CB_CONF_NO, bufp);
+ len = 3;
+ PUTCHAR(len , bufp);
+ PUTCHAR(0, bufp);
+ cbcp_send(us, CBCP_RESP, buf, len);
+ start_networks();
+ return;
+ }
+
+ if (debug)
+ dbglog("CBCP: no callback types in common");
+ lcp_close(us->us_unit, "No CBCP callback options available");
+}
+
+static void
+cbcp_send(us, code, buf, len)
+ cbcp_state *us;
+ int code;
+ u_char *buf;
+ int len;
+{
+ u_char *outp;
+ int outlen;
+
+ outp = outpacket_buf;
+
+ outlen = 4 + len;
+
+ MAKEHEADER(outp, PPP_CBCP);
+
+ PUTCHAR(code, outp);
+ PUTCHAR(us->us_id, outp);
+ PUTSHORT(outlen, outp);
+
+ if (len > 0)
+ BCOPY(buf, outp, len);
+
+ output(us->us_unit, outpacket_buf, outlen + PPP_HDRLEN);
+}
+
+/*
+ * Received CBCP Acknowledgment message.
+ */
+static void
+cbcp_recvack(us, pckt, len)
+ cbcp_state *us;
+ u_char *pckt;
+ int len;
+{
+ u_char type, addr_type;
+ int opt_len;
+
+ if (len > 0) {
+ GETCHAR(type, pckt);
+ GETCHAR(opt_len, pckt);
+
+ if (type == CB_CONF_NO) {
+ if (debug)
+ dbglog("CBCP: proceeding without callback");
+ return;
+ }
+
+ /* just ignore the delay time */
+ pckt++;
+
+ if (opt_len > 4) {
+ GETCHAR(addr_type, pckt);
+ if (addr_type != 1)
+ warn("CBCP: unknown callback address type %d", addr_type);
+ }
+ if (debug && opt_len > 5)
+ dbglog("CBCP: peer will call %.*s", pckt, opt_len - 4);
+ }
+
+ persist = 0;
+ lcp_close(us->us_unit, "Call me back, please");
+ status = EXIT_CALLBACK;
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppd/cbcp.h b/usr/src/cmd/cmd-inet/usr.bin/pppd/cbcp.h
new file mode 100644
index 0000000000..fef9cb25cf
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppd/cbcp.h
@@ -0,0 +1,38 @@
+/*
+ * cbcp - Call Back Configuration Protocol.
+ *
+ * Copyright (c) 2000 by Sun Microsystems, Inc.
+ * All rights reserved.
+ *
+ * Copyright (c) 1995 Pedro Roque Marques
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifndef CBCP_H
+#define CBCP_H
+
+typedef struct cbcp_state {
+ int us_unit; /* Interface unit number */
+ u_char us_id; /* Current id */
+ u_char us_allowed;
+ int us_type;
+ char *us_number; /* Telefone Number */
+} cbcp_state;
+
+extern cbcp_state cbcp[];
+
+extern struct protent cbcp_protent;
+
+#define CBCP_MINLEN 4
+
+#define CBCP_REQ 1
+#define CBCP_RESP 2
+#define CBCP_ACK 3
+
+#define CB_CONF_NO 1
+#define CB_CONF_USER 2
+#define CB_CONF_ADMIN 3
+#define CB_CONF_LIST 4
+#endif
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppd/ccp.c b/usr/src/cmd/cmd-inet/usr.bin/pppd/ccp.c
new file mode 100644
index 0000000000..8bf5618136
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppd/ccp.c
@@ -0,0 +1,1517 @@
+/*
+ * ccp.c - PPP Compression Control Protocol.
+ *
+ * Copyright (c) 2000 by Sun Microsystems, Inc.
+ * All rights reserved.
+ *
+ * Copyright (c) 1994 The Australian National University.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies. This software is provided without any
+ * warranty, express or implied. The Australian National University
+ * makes no representations about the suitability of this software for
+ * any purpose.
+ *
+ * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
+ * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
+ * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
+ * OR MODIFICATIONS.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+#define RCSID "$Id: ccp.c,v 1.30 2000/04/15 01:27:11 masputra Exp $"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "pppd.h"
+#include "fsm.h"
+#include "ccp.h"
+#include <net/ppp-comp.h>
+
+#if !defined(lint) && !defined(_lint)
+static const char rcsid[] = RCSID;
+#endif
+
+/*
+ * Command-line options.
+ */
+static int setbsdcomp __P((char **, option_t *));
+static int setdeflate __P((char **, option_t *));
+
+static option_t ccp_option_list[] = {
+ { "noccp", o_bool, &ccp_protent.enabled_flag,
+ "Disable CCP negotiation" },
+ { "-ccp", o_bool, &ccp_protent.enabled_flag,
+ "Disable CCP negotiation" },
+ { "bsdcomp", o_special, (void *)setbsdcomp,
+ "Request BSD-Compress packet compression" },
+ { "nobsdcomp", o_bool, &ccp_wantoptions[0].bsd_compress,
+ "don't allow BSD-Compress", OPT_A2COPY,
+ &ccp_allowoptions[0].bsd_compress },
+ { "-bsdcomp", o_bool, &ccp_wantoptions[0].bsd_compress,
+ "don't allow BSD-Compress", OPT_A2COPY,
+ &ccp_allowoptions[0].bsd_compress },
+ { "deflate", o_special, (void *)setdeflate,
+ "request Deflate compression" },
+ { "nodeflate", o_bool, &ccp_wantoptions[0].deflate,
+ "don't allow Deflate compression", OPT_A2COPY,
+ &ccp_allowoptions[0].deflate },
+ { "-deflate", o_bool, &ccp_wantoptions[0].deflate,
+ "don't allow Deflate compression", OPT_A2COPY,
+ &ccp_allowoptions[0].deflate },
+ { "nodeflatedraft", o_bool, &ccp_wantoptions[0].deflate_draft,
+ "don't use draft deflate #", OPT_A2COPY,
+ &ccp_allowoptions[0].deflate_draft },
+ { "predictor1", o_bool, &ccp_wantoptions[0].predictor_1,
+ "request Predictor-1", 1, &ccp_allowoptions[0].predictor_1 },
+ { "nopredictor1", o_bool, &ccp_wantoptions[0].predictor_1,
+ "don't allow Predictor-1", OPT_A2COPY,
+ &ccp_allowoptions[0].predictor_1 },
+ { "-predictor1", o_bool, &ccp_wantoptions[0].predictor_1,
+ "don't allow Predictor-1", OPT_A2COPY,
+ &ccp_allowoptions[0].predictor_1 },
+
+ { NULL }
+};
+
+/*
+ * Protocol entry points from main code.
+ */
+static void ccp_init __P((int unit));
+static void ccp_open __P((int unit));
+static void ccp_close __P((int unit, char *));
+static void ccp_lowerup __P((int unit));
+static void ccp_lowerdown __P((int));
+static void ccp_input __P((int unit, u_char *pkt, int len));
+static void ccp_protrej __P((int unit));
+static int ccp_printpkt __P((u_char *pkt, int len,
+ void (*printer) __P((void *, const char *, ...)),
+ void *arg));
+static void ccp_datainput __P((int unit, u_char *pkt, int len));
+
+struct protent ccp_protent = {
+ PPP_CCP,
+ ccp_init,
+ ccp_input,
+ ccp_protrej,
+ ccp_lowerup,
+ ccp_lowerdown,
+ ccp_open,
+ ccp_close,
+ ccp_printpkt,
+ ccp_datainput,
+ 1,
+ "CCP",
+ "Compressed",
+ ccp_option_list,
+ NULL,
+ NULL,
+ NULL
+};
+
+fsm ccp_fsm[NUM_PPP];
+ccp_options ccp_wantoptions[NUM_PPP]; /* what to request the peer to use */
+ccp_options ccp_gotoptions[NUM_PPP]; /* what the peer agreed to do */
+ccp_options ccp_allowoptions[NUM_PPP]; /* what we'll agree to do */
+ccp_options ccp_hisoptions[NUM_PPP]; /* what we agreed to do */
+
+/*
+ * Callbacks for fsm code.
+ */
+static void ccp_resetci __P((fsm *));
+static int ccp_cilen __P((fsm *));
+static void ccp_addci __P((fsm *, u_char *, int *));
+static int ccp_ackci __P((fsm *, u_char *, int));
+static int ccp_nakci __P((fsm *, u_char *, int));
+static int ccp_rejci __P((fsm *, u_char *, int));
+static int ccp_reqci __P((fsm *, u_char *, int *, int));
+static void ccp_up __P((fsm *));
+static void ccp_down __P((fsm *));
+static int ccp_extcode __P((fsm *, int, int, u_char *, int));
+static int ccp_codereject __P((fsm *p, int code, int id, u_char *inp,
+ int len));
+
+static fsm_callbacks ccp_callbacks = {
+ ccp_resetci, /* Reset our Configuration Information */
+ ccp_cilen, /* Length of our Configuration Information */
+ ccp_addci, /* Add our Configuration Information */
+ ccp_ackci, /* ACK our Configuration Information */
+ ccp_nakci, /* NAK our Configuration Information */
+ ccp_rejci, /* Reject our Configuration Information */
+ ccp_reqci, /* Request peer's Configuration Information */
+ ccp_up, /* Called when fsm reaches OPENED state */
+ ccp_down, /* Called when fsm leaves OPENED state */
+ NULL, /* Called when we want the lower layer up */
+ NULL, /* Called when we want the lower layer down */
+ NULL, /* Retransmission is necessary */
+ ccp_extcode, /* Called to handle LCP-specific codes */
+ "CCP", /* String name of protocol */
+ ccp_codereject, /* Peer rejected a code number */
+};
+
+/*
+ * Local statics.
+ */
+static void ccp_rack_timeout __P((void *));
+static char * method_name __P((ccp_options *, ccp_options *));
+
+/*
+ * Do we want / did we get any compression?
+ */
+#define ANY_COMPRESS(opt) ((opt).deflate || (opt).bsd_compress \
+ || (opt).predictor_1 || (opt).predictor_2)
+
+/*
+ * Local state (mainly for handling reset-reqs and reset-acks).
+ */
+static int ccp_localstate[NUM_PPP];
+#define RACK_PENDING 0x0001 /* waiting for reset-ack */
+#define RREQ_REPEAT 0x0002 /* send another reset-req if no reset-ack */
+#define RREQ_REJECTED 0x0004 /* peer code-rejected reset-request */
+#define RACK_REJECTED 0x0008 /* peer code-rejected reset-ack */
+#define RREQ_IGNORED 0x0010 /* peer just ignored reset-request */
+
+#define RACKTIMEOUT 1 /* time in seconds between Reset-Requests */
+
+static int all_rejected[NUM_PPP]; /* we rejected all peer's options */
+
+#ifdef COMP_TUNE
+static int deflate_tune = -1; /* compression effort level for deflate */
+#endif
+static int deflate_rmax = DEFLATE_MAX_SIZE; /* max rbits */
+static int deflate_amax = DEFLATE_MAX_SIZE; /* max abits */
+
+/*
+ * Option parsing.
+ */
+/*ARGSUSED*/
+static int
+setbsdcomp(argv, opt)
+ char **argv;
+ option_t *opt;
+{
+ int rbits, abits;
+ char *str, *endp;
+
+ str = *argv;
+ abits = rbits = strtol(str, &endp, 0);
+ if (endp != str && *endp == ',') {
+ str = endp + 1;
+ abits = strtol(str, &endp, 0);
+ }
+ if (*endp != '\0' || endp == str) {
+ option_error("invalid parameter '%s' for bsdcomp option", *argv);
+ return 0;
+ }
+ if ((rbits != 0 && (rbits < BSD_MIN_BITS || rbits > BSD_MAX_BITS))
+ || (abits != 0 && (abits < BSD_MIN_BITS || abits > BSD_MAX_BITS))) {
+ option_error("bsdcomp option values must be 0 or %d .. %d",
+ BSD_MIN_BITS, BSD_MAX_BITS);
+ return 0;
+ }
+ if (rbits > 0) {
+ ccp_wantoptions[0].bsd_compress = 1;
+ ccp_wantoptions[0].bsd_bits = rbits;
+ } else
+ ccp_wantoptions[0].bsd_compress = 0;
+ if (abits > 0) {
+ ccp_allowoptions[0].bsd_compress = 1;
+ ccp_allowoptions[0].bsd_bits = abits;
+ } else
+ ccp_allowoptions[0].bsd_compress = 0;
+ return 1;
+}
+
+/*ARGSUSED*/
+static int
+setdeflate(argv, opt)
+ char **argv;
+ option_t *opt;
+{
+ int rbits, abits, def_rmax, def_amax;
+ char *str, *endp;
+
+ str = endp = *argv;
+ if (*str == ',')
+ abits = rbits = -1;
+ else
+ abits = rbits = strtol(str, &endp, 0);
+ if (*endp == ',') {
+ str = ++endp;
+ if (*str == ',')
+ abits = rbits;
+ else
+ abits = strtol(str, &endp, 0);
+ }
+#ifdef COMP_TUNE
+ if (*endp == ',' && privileged_option) {
+ str = ++endp;
+ deflate_tune = strtol(str, &endp, 0);
+ }
+#endif
+ if (*endp != '\0' || endp == str) {
+ option_error("invalid parameter '%s' for deflate option", *argv);
+ return 0;
+ }
+ if (privileged_option) {
+ def_rmax = def_amax = DEFLATE_MAX_SIZE;
+ } else {
+ def_rmax = deflate_rmax;
+ def_amax = deflate_amax;
+ }
+ if (rbits < 0)
+ rbits = def_rmax;
+ if (abits < 0)
+ abits = def_amax;
+ if ((rbits != 0 && (rbits <= DEFLATE_MIN_SIZE || rbits > def_rmax))
+ || (abits != 0 && (abits <= DEFLATE_MIN_SIZE || abits > def_amax))) {
+ option_error("deflate option values must be 0 or {%d,%d} .. {%d,%d}",
+ DEFLATE_MIN_SIZE+1, DEFLATE_MIN_SIZE+1,
+ def_rmax, def_amax);
+ return 0;
+ }
+ if (privileged_option) {
+ deflate_rmax = rbits;
+ deflate_amax = abits;
+ }
+ if (rbits > 0) {
+ ccp_wantoptions[0].deflate = 1;
+ ccp_wantoptions[0].deflate_size = rbits;
+ } else
+ ccp_wantoptions[0].deflate = 0;
+ if (abits > 0) {
+ ccp_allowoptions[0].deflate = 1;
+ ccp_allowoptions[0].deflate_size = abits;
+ } else
+ ccp_allowoptions[0].deflate = 0;
+ return 1;
+}
+
+
+/*
+ * ccp_init - initialize CCP.
+ */
+static void
+ccp_init(unit)
+ int unit;
+{
+ fsm *f = &ccp_fsm[unit];
+
+ f->unit = unit;
+ f->protocol = PPP_CCP;
+ f->callbacks = &ccp_callbacks;
+ fsm_init(f);
+ f->flags |= OPT_RESTART;
+
+ BZERO(&ccp_wantoptions[unit], sizeof(ccp_options));
+ BZERO(&ccp_gotoptions[unit], sizeof(ccp_options));
+ BZERO(&ccp_allowoptions[unit], sizeof(ccp_options));
+ BZERO(&ccp_hisoptions[unit], sizeof(ccp_options));
+
+ ccp_wantoptions[0].deflate = 1;
+ ccp_wantoptions[0].deflate_size = DEFLATE_MAX_SIZE;
+ ccp_wantoptions[0].deflate_correct = 1;
+ ccp_wantoptions[0].deflate_draft = 1;
+ ccp_allowoptions[0].deflate = 1;
+ ccp_allowoptions[0].deflate_size = DEFLATE_MAX_SIZE;
+ ccp_allowoptions[0].deflate_correct = 1;
+ ccp_allowoptions[0].deflate_draft = 1;
+
+ ccp_wantoptions[0].bsd_compress = 1;
+ ccp_wantoptions[0].bsd_bits = BSD_MAX_BITS;
+ ccp_allowoptions[0].bsd_compress = 1;
+ ccp_allowoptions[0].bsd_bits = BSD_MAX_BITS;
+
+ ccp_allowoptions[0].predictor_1 = 1;
+}
+
+/*
+ * ccp_open - CCP is allowed to come up.
+ */
+static void
+ccp_open(unit)
+ int unit;
+{
+ fsm *f = &ccp_fsm[unit];
+
+ /*
+ * If we haven't gone open yet (first time through), then go open
+ * but not up. Otherwise, skip this to allow reopen to reset the
+ * compressor.
+ */
+ if (f->state != OPENED)
+ ccp_flags_set(unit, 1, 0);
+
+ /*
+ * Find out which compressors the kernel supports before
+ * deciding whether to open in silent mode.
+ */
+ ccp_resetci(f);
+ if (!ANY_COMPRESS(ccp_gotoptions[unit]))
+ f->flags |= OPT_SILENT;
+
+ fsm_open(f);
+}
+
+/*
+ * ccp_close - Terminate CCP.
+ */
+static void
+ccp_close(unit, reason)
+ int unit;
+ char *reason;
+{
+ ccp_flags_set(unit, 0, 0);
+ fsm_close(&ccp_fsm[unit], reason);
+}
+
+/*
+ * ccp_lowerup - we may now transmit CCP packets.
+ */
+static void
+ccp_lowerup(unit)
+ int unit;
+{
+ fsm_lowerup(&ccp_fsm[unit]);
+}
+
+/*
+ * ccp_lowerdown - we may not transmit CCP packets.
+ */
+static void
+ccp_lowerdown(unit)
+ int unit;
+{
+ fsm_lowerdown(&ccp_fsm[unit]);
+}
+
+/*
+ * ccp_input - process a received CCP packet.
+ */
+static void
+ccp_input(unit, p, len)
+ int unit;
+ u_char *p;
+ int len;
+{
+ fsm *f = &ccp_fsm[unit];
+ int oldstate;
+
+ /*
+ * Check for a terminate-request so we can print a message.
+ */
+ oldstate = f->state;
+ fsm_input(f, p, len);
+ if (oldstate == OPENED && p[0] == CODE_TERMREQ && f->state != OPENED)
+ notice("Compression disabled by peer.");
+
+ /*
+ * If we get a terminate-ack and we're not asking for compression,
+ * close CCP. (Terminate-Request is handled by fsm_input() above.)
+ */
+ if (oldstate == REQSENT && p[0] == CODE_TERMACK
+ && !ANY_COMPRESS(ccp_gotoptions[unit]))
+ ccp_close(unit, "No compression negotiated");
+}
+
+/*
+ * Handle a CCP-specific code.
+ */
+static int
+ccp_extcode(f, code, id, p, len)
+ fsm *f;
+ int code, id;
+ u_char *p;
+ int len;
+{
+ switch (code) {
+ case CCP_RESETREQ:
+ /* If not open, then silently ignore. */
+ if (f->state != OPENED)
+ break;
+ /* send a reset-ack, which our transmitter module will see and
+ reset its compression state. */
+ fsm_sdata(f, CCP_RESETACK, id, p, len);
+ break;
+
+ case CCP_RESETACK:
+ /*
+ * Note that the compression module isn't picky about ID
+ * numbers and such.
+ */
+ ccp_localstate[f->unit] &= ~RREQ_IGNORED & ~RREQ_REJECTED;
+ if ((ccp_localstate[f->unit] & RACK_PENDING) && id == f->reqid) {
+ ccp_localstate[f->unit] &= ~RACK_PENDING & ~RREQ_REPEAT;
+ UNTIMEOUT(ccp_rack_timeout, f);
+ }
+ break;
+
+ default:
+ /* Tell fsm to send code reject */
+ return (0);
+ }
+
+ return (1);
+}
+
+/*
+ * Handle Code-Reject for one of our extended codes by dropping back to
+ * reopen as mechanism to restart compression.
+ */
+/*ARGSUSED*/
+static int
+ccp_codereject(f, code, id, inp, len)
+ fsm *f;
+ int code, id;
+ u_char *inp;
+ int len;
+{
+ switch (code) {
+ case CCP_RESETREQ:
+ if (!(ccp_localstate[f->unit] & RREQ_REJECTED)) {
+ info("peer has rejected CCP Reset-Request; falling back on Open");
+ if (f->state == OPENED)
+ ccp_open(f->unit);
+ }
+ ccp_localstate[f->unit] |= RREQ_REJECTED;
+ return (0);
+
+ case CCP_RESETACK:
+ /*
+ * Peer must have sent us CCP Reset-Request but then code-rejected
+ * when we sent CCP Reset-Ack. He's a moron, be we have to obey
+ * his wishes.
+ */
+ ccp_localstate[f->unit] |= RACK_REJECTED;
+ notice("peer has erroneously rejected CCP Reset-Ack");
+ f->term_reason = "peer sent Code-Reject for CCP Reset-Ack";
+ f->term_reason_len = strlen(f->term_reason);
+ break;
+
+ default:
+ f->term_reason = "peer sent invalid Code-Reject";
+ break;
+ }
+
+ f->term_reason_len = strlen(f->term_reason);
+ return (1);
+}
+
+/*
+ * ccp_protrej - peer doesn't talk CCP.
+ */
+static void
+ccp_protrej(unit)
+ int unit;
+{
+ /* Neither open nor up. */
+ ccp_flags_set(unit, 0, 0);
+ fsm_lowerdown(&ccp_fsm[unit]);
+}
+
+/*
+ * ccp_resetci - initialize at start of negotiation.
+ */
+static void
+ccp_resetci(f)
+ fsm *f;
+{
+ ccp_options *go = &ccp_gotoptions[f->unit];
+ u_char opt_buf[16];
+
+ *go = ccp_wantoptions[f->unit];
+ all_rejected[f->unit] = 0;
+
+ /*
+ * Check whether the kernel knows about the various
+ * decompression methods we might request.
+ */
+ if (go->bsd_compress) {
+ opt_buf[0] = CI_BSD_COMPRESS;
+ opt_buf[1] = CILEN_BSD_COMPRESS;
+ opt_buf[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, BSD_MIN_BITS);
+ if (ccp_test(f->unit, opt_buf, CILEN_BSD_COMPRESS, 0) <= 0)
+ go->bsd_compress = 0;
+ }
+ if (go->deflate) {
+ if (go->deflate_correct) {
+ opt_buf[0] = CI_DEFLATE;
+ opt_buf[1] = CILEN_DEFLATE;
+ opt_buf[2] = DEFLATE_MAKE_OPT(DEFLATE_MIN_SIZE+1);
+ opt_buf[3] = DEFLATE_CHK_SEQUENCE;
+ if (ccp_test(f->unit, opt_buf, CILEN_DEFLATE, 0) <= 0)
+ go->deflate_correct = 0;
+ }
+ if (go->deflate_draft) {
+ opt_buf[0] = CI_DEFLATE_DRAFT;
+ opt_buf[1] = CILEN_DEFLATE;
+ opt_buf[2] = DEFLATE_MAKE_OPT(DEFLATE_MIN_SIZE+1);
+ opt_buf[3] = DEFLATE_CHK_SEQUENCE;
+ if (ccp_test(f->unit, opt_buf, CILEN_DEFLATE, 0) <= 0)
+ go->deflate_draft = 0;
+ }
+ if (!go->deflate_correct && !go->deflate_draft)
+ go->deflate = 0;
+ }
+ if (go->predictor_1) {
+ opt_buf[0] = CI_PREDICTOR_1;
+ opt_buf[1] = CILEN_PREDICTOR_1;
+ if (ccp_test(f->unit, opt_buf, CILEN_PREDICTOR_1, 0) <= 0)
+ go->predictor_1 = 0;
+ }
+ if (go->predictor_2) {
+ opt_buf[0] = CI_PREDICTOR_2;
+ opt_buf[1] = CILEN_PREDICTOR_2;
+ if (ccp_test(f->unit, opt_buf, CILEN_PREDICTOR_2, 0) <= 0)
+ go->predictor_2 = 0;
+ }
+}
+
+/*
+ * ccp_cilen - Return total length of our configuration info.
+ */
+static int
+ccp_cilen(f)
+ fsm *f;
+{
+ ccp_options *go = &ccp_gotoptions[f->unit];
+
+ return (go->bsd_compress? CILEN_BSD_COMPRESS: 0)
+ + (go->deflate && go->deflate_correct ? CILEN_DEFLATE : 0)
+ + (go->deflate && go->deflate_draft ? CILEN_DEFLATE : 0)
+ + (go->predictor_1? CILEN_PREDICTOR_1: 0)
+ + (go->predictor_2? CILEN_PREDICTOR_2: 0);
+}
+
+/*
+ * ccp_addci - put our requests in a packet.
+ */
+static void
+ccp_addci(f, p, lenp)
+ fsm *f;
+ u_char *p;
+ int *lenp;
+{
+ int res;
+ ccp_options *go = &ccp_gotoptions[f->unit];
+ u_char *p0 = p;
+
+ /*
+ * Add the compression types that we can receive, in decreasing
+ * preference order. Get the kernel to allocate the first one
+ * in case it gets Acked.
+ */
+ if (go->deflate) {
+ p[0] = go->deflate_correct? CI_DEFLATE: CI_DEFLATE_DRAFT;
+ p[1] = CILEN_DEFLATE;
+ p[2] = DEFLATE_MAKE_OPT(go->deflate_size);
+ p[3] = DEFLATE_CHK_SEQUENCE;
+ for (;;) {
+ res = ccp_test(f->unit, p, CILEN_DEFLATE, 0);
+ if (res > 0) {
+ p += CILEN_DEFLATE;
+ break;
+ }
+ if (res < 0 || go->deflate_size <= DEFLATE_MIN_SIZE+1) {
+ go->deflate = 0;
+ break;
+ }
+ --go->deflate_size;
+ p[2] = DEFLATE_MAKE_OPT(go->deflate_size);
+ }
+ /* If we're offering both, then this is second. */
+ if (p != p0 && go->deflate_correct && go->deflate_draft) {
+ p[0] = CI_DEFLATE_DRAFT;
+ p[1] = CILEN_DEFLATE;
+ p[2] = p[2 - CILEN_DEFLATE];
+ p[3] = DEFLATE_CHK_SEQUENCE;
+ p += CILEN_DEFLATE;
+ }
+ }
+ if (go->bsd_compress) {
+ p[0] = CI_BSD_COMPRESS;
+ p[1] = CILEN_BSD_COMPRESS;
+ p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits);
+ if (p != p0) {
+ p += CILEN_BSD_COMPRESS; /* not the first option */
+ } else {
+ for (;;) {
+ res = ccp_test(f->unit, p, CILEN_BSD_COMPRESS, 0);
+ if (res > 0) {
+ p += CILEN_BSD_COMPRESS;
+ break;
+ }
+ if (res < 0 || go->bsd_bits <= BSD_MIN_BITS) {
+ go->bsd_compress = 0;
+ break;
+ }
+ --go->bsd_bits;
+ p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits);
+ }
+ }
+ }
+ /*
+ * Prefer Predictor-1 over Predictor-2. (The latter requires the use
+ * of LAP-B and has no known implementations.)
+ */
+ if (go->predictor_1) {
+ p[0] = CI_PREDICTOR_1;
+ p[1] = CILEN_PREDICTOR_1;
+ if (p == p0 && ccp_test(f->unit, p, CILEN_PREDICTOR_1, 0) <= 0) {
+ go->predictor_1 = 0;
+ } else {
+ p += CILEN_PREDICTOR_1;
+ }
+ }
+ if (go->predictor_2) {
+ p[0] = CI_PREDICTOR_2;
+ p[1] = CILEN_PREDICTOR_2;
+ if (p == p0 && ccp_test(f->unit, p, CILEN_PREDICTOR_2, 0) <= 0) {
+ go->predictor_2 = 0;
+ } else {
+ p += CILEN_PREDICTOR_2;
+ }
+ }
+
+ go->method = (p > p0)? p0[0]: -1;
+
+ *lenp = p - p0;
+}
+
+/*
+ * ccp_ackci - process a received configure-ack, and return
+ * 1 iff the packet was OK.
+ */
+static int
+ccp_ackci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ ccp_options *go = &ccp_gotoptions[f->unit];
+ u_char *p0 = p;
+
+ if (go->deflate && go->deflate_correct) {
+ if (len < CILEN_DEFLATE
+ || p[0] != CI_DEFLATE || p[1] != CILEN_DEFLATE
+ || p[2] != DEFLATE_MAKE_OPT(go->deflate_size)
+ || p[3] != DEFLATE_CHK_SEQUENCE)
+ return 0;
+ /* Cope with non-standard first/fast ack */
+ if (p == p0 && len == 0)
+ return 1;
+ p += CILEN_DEFLATE;
+ len -= CILEN_DEFLATE;
+ }
+ if (go->deflate && go->deflate_draft) {
+ if (len < CILEN_DEFLATE
+ || p[0] != CI_DEFLATE_DRAFT || p[1] != CILEN_DEFLATE
+ || p[2] != DEFLATE_MAKE_OPT(go->deflate_size)
+ || p[3] != DEFLATE_CHK_SEQUENCE)
+ return 0;
+ /* Cope with non-standard first/fast ack */
+ if (p == p0 && len == 0)
+ return 1;
+ p += CILEN_DEFLATE;
+ len -= CILEN_DEFLATE;
+ }
+ if (go->bsd_compress) {
+ if (len < CILEN_BSD_COMPRESS
+ || p[0] != CI_BSD_COMPRESS || p[1] != CILEN_BSD_COMPRESS
+ || p[2] != BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits))
+ return 0;
+ /* Cope with non-standard first/fast ack */
+ if (p == p0 && len == 0)
+ return 1;
+ p += CILEN_BSD_COMPRESS;
+ len -= CILEN_BSD_COMPRESS;
+ }
+ if (go->predictor_1) {
+ if (len < CILEN_PREDICTOR_1
+ || p[0] != CI_PREDICTOR_1 || p[1] != CILEN_PREDICTOR_1)
+ return 0;
+ /* Cope with non-standard first/fast ack */
+ if (p == p0 && len == 0)
+ return 1;
+ p += CILEN_PREDICTOR_1;
+ len -= CILEN_PREDICTOR_1;
+ }
+ if (go->predictor_2) {
+ if (len < CILEN_PREDICTOR_2
+ || p[0] != CI_PREDICTOR_2 || p[1] != CILEN_PREDICTOR_2)
+ return 0;
+ /* Cope with non-standard first/fast ack */
+ if (p == p0 && len == 0)
+ return 1;
+ p += CILEN_PREDICTOR_2;
+ len -= CILEN_PREDICTOR_2;
+ }
+
+ /* Peer cannot ack something that wasn't sent. */
+ if (len != 0)
+ return 0;
+ return 1;
+}
+
+/*
+ * ccp_nakci - process received configure-nak.
+ * Returns 1 iff the nak was OK.
+ */
+static int
+ccp_nakci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ ccp_options *go = &ccp_gotoptions[f->unit];
+ ccp_options no; /* options we've seen already */
+ ccp_options try; /* options to ask for next time */
+
+ BZERO(&no, sizeof(no));
+ try = *go;
+
+ if (go->deflate && go->deflate_correct && len >= CILEN_DEFLATE &&
+ p[0] == CI_DEFLATE) {
+ no.deflate = 1;
+ /*
+ * Peer wants us to use a different code size or something.
+ * Stop asking for Deflate if we don't understand his suggestion.
+ */
+ if (p[1] != CILEN_DEFLATE
+ || DEFLATE_METHOD(p[2]) != DEFLATE_METHOD_VAL
+ || DEFLATE_SIZE(p[2]) <= DEFLATE_MIN_SIZE
+ || p[3] != DEFLATE_CHK_SEQUENCE)
+ try.deflate_correct = 0;
+ else if (DEFLATE_SIZE(p[2]) < go->deflate_size)
+ try.deflate_size = DEFLATE_SIZE(p[2]);
+ len -= p[1];
+ p += p[1];
+ }
+
+ if (go->deflate && go->deflate_draft && len >= CILEN_DEFLATE &&
+ p[0] == CI_DEFLATE_DRAFT) {
+ no.deflate = 1;
+ /*
+ * Peer wants us to use a different code size or something.
+ * Stop asking for Deflate using the old algorithm number if
+ * we don't understand his suggestion. (Note that this will
+ * happen if the peer is running Magnalink instead of
+ * old-style Deflate.)
+ */
+ if (p[1] != CILEN_DEFLATE
+ || DEFLATE_METHOD(p[2]) != DEFLATE_METHOD_VAL
+ || DEFLATE_SIZE(p[2]) <= DEFLATE_MIN_SIZE
+ || p[3] != DEFLATE_CHK_SEQUENCE)
+ try.deflate_draft = 0;
+ else if (DEFLATE_SIZE(p[2]) < go->deflate_size)
+ try.deflate_size = DEFLATE_SIZE(p[2]);
+ len -= p[1];
+ p += p[1];
+ }
+
+ if (!try.deflate_correct && !try.deflate_draft)
+ try.deflate = 0;
+
+ if (go->bsd_compress && len >= CILEN_BSD_COMPRESS &&
+ p[0] == CI_BSD_COMPRESS) {
+ no.bsd_compress = 1;
+ /*
+ * Peer wants us to use a different number of bits
+ * or a different version.
+ */
+ if (p[1] != CILEN_BSD_COMPRESS ||
+ BSD_VERSION(p[2]) != BSD_CURRENT_VERSION)
+ try.bsd_compress = 0;
+ else if (BSD_NBITS(p[2]) < go->bsd_bits)
+ try.bsd_bits = BSD_NBITS(p[2]);
+ len -= p[1];
+ p += p[1];
+ }
+
+ /*
+ * Predictor-1 and 2 have no options, so they can't be Naked.
+ *
+ * There may be remaining options but we ignore them.
+ */
+
+ if (f->state != OPENED)
+ *go = try;
+ return 1;
+}
+
+/*
+ * ccp_rejci - peer rejects some of our suggested compression methods.
+ */
+static int
+ccp_rejci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ ccp_options *go = &ccp_gotoptions[f->unit];
+ ccp_options try; /* options to request next time */
+
+ try = *go;
+
+ /*
+ * Cope with empty configure-rejects by ceasing to send
+ * configure-requests.
+ */
+ if (len == 0 && all_rejected[f->unit])
+ return -1;
+
+ if (go->deflate && go->deflate_correct && len >= CILEN_DEFLATE &&
+ p[0] == CI_DEFLATE && p[1] == CILEN_DEFLATE) {
+ if (p[2] != DEFLATE_MAKE_OPT(go->deflate_size)
+ || p[3] != DEFLATE_CHK_SEQUENCE)
+ return 0; /* Rej is bad */
+ try.deflate_correct = 0;
+ p += CILEN_DEFLATE;
+ len -= CILEN_DEFLATE;
+ }
+ if (go->deflate && go->deflate_draft && len >= CILEN_DEFLATE &&
+ p[0] == CI_DEFLATE_DRAFT && p[1] == CILEN_DEFLATE) {
+ if (p[2] != DEFLATE_MAKE_OPT(go->deflate_size)
+ || p[3] != DEFLATE_CHK_SEQUENCE)
+ return 0; /* Rej is bad */
+ try.deflate_draft = 0;
+ p += CILEN_DEFLATE;
+ len -= CILEN_DEFLATE;
+ }
+ if (!try.deflate_correct && !try.deflate_draft)
+ try.deflate = 0;
+ if (go->bsd_compress && len >= CILEN_BSD_COMPRESS
+ && p[0] == CI_BSD_COMPRESS && p[1] == CILEN_BSD_COMPRESS) {
+ if (p[2] != BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits))
+ return 0;
+ try.bsd_compress = 0;
+ p += CILEN_BSD_COMPRESS;
+ len -= CILEN_BSD_COMPRESS;
+ }
+ if (go->predictor_1 && len >= CILEN_PREDICTOR_1
+ && p[0] == CI_PREDICTOR_1 && p[1] == CILEN_PREDICTOR_1) {
+ try.predictor_1 = 0;
+ p += CILEN_PREDICTOR_1;
+ len -= CILEN_PREDICTOR_1;
+ }
+ if (go->predictor_2 && len >= CILEN_PREDICTOR_2
+ && p[0] == CI_PREDICTOR_2 && p[1] == CILEN_PREDICTOR_2) {
+ try.predictor_2 = 0;
+ p += CILEN_PREDICTOR_2;
+ len -= CILEN_PREDICTOR_2;
+ }
+
+ if (len != 0)
+ return 0;
+
+ if (f->state != OPENED)
+ *go = try;
+
+ return 1;
+}
+
+/*
+ * ccp_reqci - process a received configure-request.
+ *
+ * Returns CODE_CONFACK, CODE_CONFNAK or CODE_CONFREJ and the packet
+ * is modified appropriately.
+ */
+static int
+ccp_reqci(f, p, lenp, dont_nak)
+ fsm *f;
+ u_char *p;
+ int *lenp;
+ int dont_nak;
+{
+ int ret, newret, res;
+ u_char *p0, *nakp, *rejp, *pv;
+ int len, clen, type, nb;
+ ccp_options *ho = &ccp_hisoptions[f->unit];
+ ccp_options *ao = &ccp_allowoptions[f->unit];
+
+ ret = CODE_CONFACK;
+ rejp = p0 = p;
+ nakp = nak_buffer;
+ len = *lenp;
+
+ BZERO(ho, sizeof(ccp_options));
+ ho->method = (len > 0)? p[0]: -1;
+
+ for (; len > 0; len -= clen, p += clen) {
+ newret = CODE_CONFACK;
+ if (len < 2 || p[1] > len) {
+ /*
+ * RFC 1661 page 40 -- if the option extends beyond the
+ * packet, then discard the entire packet.
+ */
+ return (0);
+ }
+
+ type = p[0];
+ clen = p[1];
+
+ pv = p;
+ switch (type) {
+ case CI_DEFLATE:
+ case CI_DEFLATE_DRAFT:
+ if (!ao->deflate ||
+ (!ao->deflate_correct && type == CI_DEFLATE) ||
+ (!ao->deflate_draft && type == CI_DEFLATE_DRAFT)) {
+ newret = CODE_CONFREJ;
+ break;
+ }
+
+ ho->deflate = 1;
+ nb = clen < CILEN_DEFLATE ? ao->deflate_size : DEFLATE_SIZE(p[2]);
+ ho->deflate_size = nb;
+ if (clen != CILEN_DEFLATE ||
+ DEFLATE_METHOD(p[2]) != DEFLATE_METHOD_VAL ||
+ p[3] != DEFLATE_CHK_SEQUENCE || nb > ao->deflate_size ||
+ nb <= DEFLATE_MIN_SIZE) {
+ newret = CODE_CONFNAK;
+ if (dont_nak)
+ break;
+ if (nb > ao->deflate_size)
+ nb = ao->deflate_size;
+ else if (nb <= DEFLATE_MIN_SIZE)
+ nb = DEFLATE_MIN_SIZE+1;
+ pv = nakp;
+ PUTCHAR(type, nakp);
+ PUTCHAR(CILEN_DEFLATE, nakp);
+ PUTCHAR(DEFLATE_MAKE_OPT(nb), nakp);
+ PUTCHAR(DEFLATE_CHK_SEQUENCE, nakp);
+ }
+
+ /*
+ * Check whether we can do Deflate with the window
+ * size they want. If the window is too big, reduce
+ * it until the kernel can cope and nak with that.
+ * We only check this for the first option.
+ */
+ if (p == p0) {
+ for (;;) {
+ res = ccp_test(f->unit, pv, CILEN_DEFLATE, 1);
+ if (res > 0)
+ break; /* it's OK now */
+ if (res < 0 || nb <= DEFLATE_MIN_SIZE+1 || dont_nak) {
+ newret = CODE_CONFREJ;
+ break;
+ }
+ if (newret == CODE_CONFACK) {
+ BCOPY(pv, nakp, CILEN_DEFLATE);
+ pv = nakp;
+ nakp += CILEN_DEFLATE;
+ newret = CODE_CONFNAK;
+ }
+ --nb;
+ pv[2] = DEFLATE_MAKE_OPT(nb);
+ }
+#ifdef COMP_TUNE
+ /* Tune Deflate compression effort. */
+ if (newret == CODE_CONFACK)
+ ccp_tune(f->unit, deflate_tune);
+#endif
+ }
+ break;
+
+ case CI_BSD_COMPRESS:
+ if (!ao->bsd_compress) {
+ newret = CODE_CONFREJ;
+ break;
+ }
+
+ ho->bsd_compress = 1;
+ nb = clen < CILEN_BSD_COMPRESS ? ao->bsd_bits : BSD_NBITS(p[2]);
+ ho->bsd_bits = nb;
+ if (clen != CILEN_BSD_COMPRESS ||
+ BSD_VERSION(p[2]) != BSD_CURRENT_VERSION ||
+ nb > ao->bsd_bits || nb < BSD_MIN_BITS) {
+ newret = CODE_CONFNAK;
+ if (dont_nak)
+ break;
+ if (nb > ao->bsd_bits)
+ nb = ao->bsd_bits;
+ else if (nb < BSD_MIN_BITS)
+ nb = BSD_MIN_BITS;
+ pv = nakp;
+ PUTCHAR(type, nakp);
+ PUTCHAR(CILEN_BSD_COMPRESS, nakp);
+ PUTCHAR(BSD_MAKE_OPT(BSD_CURRENT_VERSION, nb), nakp);
+ }
+
+ /*
+ * Check whether we can do BSD-Compress with the code
+ * size they want. If the code size is too big, reduce
+ * it until the kernel can cope and nak with that.
+ * We only check this for the first option.
+ */
+ if (p == p0) {
+ for (;;) {
+ res = ccp_test(f->unit, pv, CILEN_BSD_COMPRESS, 1);
+ if (res > 0)
+ break;
+ if (res < 0 || nb == BSD_MIN_BITS || dont_nak) {
+ newret = CODE_CONFREJ;
+ break;
+ }
+ if (newret == CODE_CONFACK) {
+ BCOPY(pv, nakp, CILEN_BSD_COMPRESS);
+ pv = nakp;
+ nakp += CILEN_BSD_COMPRESS;
+ newret = CODE_CONFNAK;
+ }
+ --nb;
+ pv[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, nb);
+ }
+ }
+ break;
+
+ case CI_PREDICTOR_1:
+ if (!ao->predictor_1) {
+ newret = CODE_CONFREJ;
+ break;
+ }
+
+ ho->predictor_1 = 1;
+ if (clen != CILEN_PREDICTOR_1) {
+ newret = CODE_CONFNAK;
+ if (dont_nak)
+ break;
+ pv = nakp;
+ PUTCHAR(type, nakp);
+ PUTCHAR(CILEN_PREDICTOR_1, nakp);
+ }
+ if (p == p0 &&
+ ccp_test(f->unit, pv, CILEN_PREDICTOR_1, 1) <= 0) {
+ newret = CODE_CONFREJ;
+ }
+ break;
+
+ case CI_PREDICTOR_2:
+ if (!ao->predictor_2) {
+ newret = CODE_CONFREJ;
+ break;
+ }
+
+ ho->predictor_2 = 1;
+ if (clen != CILEN_PREDICTOR_2) {
+ newret = CODE_CONFNAK;
+ if (dont_nak)
+ break;
+ pv = nakp;
+ PUTCHAR(type, nakp);
+ PUTCHAR(CILEN_PREDICTOR_2, nakp);
+ }
+ if (p == p0 &&
+ ccp_test(f->unit, p, CILEN_PREDICTOR_2, 1) <= 0) {
+ newret = CODE_CONFREJ;
+ }
+ break;
+
+ default:
+ newret = CODE_CONFREJ;
+ break;
+ }
+
+ /* Cope with confused peers. */
+ if (clen < 2)
+ clen = 2;
+
+ if (newret == CODE_CONFACK && ret != CODE_CONFACK)
+ continue;
+ if (newret == CODE_CONFNAK) {
+ if (dont_nak) {
+ newret = CODE_CONFREJ;
+ } else {
+ /* Ignore subsequent nakable things if rejecting. */
+ if (ret == CODE_CONFREJ)
+ continue;
+ ret = CODE_CONFNAK;
+ }
+ }
+ if (newret == CODE_CONFREJ) {
+ ret = CODE_CONFREJ;
+ if (p != rejp)
+ BCOPY(p, rejp, clen);
+ rejp += clen;
+ }
+ }
+
+ switch (ret) {
+ case CODE_CONFACK:
+ *lenp = p - p0;
+ break;
+ case CODE_CONFNAK:
+ *lenp = nakp - nak_buffer;
+ BCOPY(nak_buffer, p0, *lenp);
+ break;
+ case CODE_CONFREJ:
+ *lenp = rejp - p0;
+ break;
+ }
+ return ret;
+}
+
+/*
+ * Make a string name for a compression method (or 2).
+ */
+static char *
+method_name(opt, opt2)
+ ccp_options *opt, *opt2;
+{
+ static char result[64];
+
+ if (!ANY_COMPRESS(*opt))
+ return "(none)";
+ switch (opt->method) {
+ case CI_DEFLATE:
+ case CI_DEFLATE_DRAFT:
+ if (opt2 != NULL && opt2->deflate_size != opt->deflate_size)
+ (void) slprintf(result, sizeof(result), "Deflate%s (%d/%d)",
+ (opt->method == CI_DEFLATE_DRAFT? "(old#)": ""),
+ opt->deflate_size, opt2->deflate_size);
+ else
+ (void) slprintf(result, sizeof(result), "Deflate%s (%d)",
+ (opt->method == CI_DEFLATE_DRAFT? "(old#)": ""),
+ opt->deflate_size);
+ break;
+ case CI_BSD_COMPRESS:
+ if (opt2 != NULL && opt2->bsd_bits != opt->bsd_bits)
+ (void) slprintf(result, sizeof(result), "BSD-Compress (%d/%d)",
+ opt->bsd_bits, opt2->bsd_bits);
+ else
+ (void) slprintf(result, sizeof(result), "BSD-Compress (%d)",
+ opt->bsd_bits);
+ break;
+ case CI_PREDICTOR_1:
+ return "Predictor 1";
+ case CI_PREDICTOR_2:
+ return "Predictor 2";
+#ifdef CI_STAC
+ case CI_STAC:
+ return "Stac";
+#endif
+#ifdef CI_MPPC
+ case CI_MPPC:
+ return "MS-PPC";
+#endif
+ default:
+ (void) slprintf(result, sizeof(result), "Method %d", opt->method);
+ }
+ return result;
+}
+
+/*
+ * CCP has come up - inform the kernel driver and log a message.
+ */
+static void
+ccp_up(f)
+ fsm *f;
+{
+ ccp_options *go = &ccp_gotoptions[f->unit];
+ ccp_options *ho = &ccp_hisoptions[f->unit];
+ char method1[64];
+
+ /*
+ * We're now open and up (running).
+ */
+ ccp_flags_set(f->unit, 1, 1);
+ if (ANY_COMPRESS(*go)) {
+ if (ANY_COMPRESS(*ho)) {
+ if (go->method == ho->method) {
+ notice("%s compression enabled", method_name(go, ho));
+ } else {
+ (void) strlcpy(method1, method_name(go, NULL), sizeof(method1));
+ notice("%s / %s compression enabled",
+ method1, method_name(ho, NULL));
+ }
+ } else
+ notice("%s receive decompression enabled", method_name(go, NULL));
+ } else if (ANY_COMPRESS(*ho))
+ notice("%s transmit compression enabled", method_name(ho, NULL));
+}
+
+/*
+ * CCP has gone down - inform the kernel driver.
+ */
+static void
+ccp_down(f)
+ fsm *f;
+{
+ if (ccp_localstate[f->unit] & RACK_PENDING)
+ UNTIMEOUT(ccp_rack_timeout, f);
+ /* Don't forget about peer's code rejects or ignoring of requests. */
+ ccp_localstate[f->unit] &= ~RACK_PENDING & ~RREQ_REPEAT;
+ /* We're still open, but no longer up. */
+ ccp_flags_set(f->unit, 1, 0);
+}
+
+static int
+ccp_printpkt(p, plen, printer, arg)
+ u_char *p;
+ int plen;
+ void (*printer) __P((void *, const char *, ...));
+ void *arg;
+{
+ u_char *p0, *optend, cichar;
+ int code, id, len;
+ int optlen, clen;
+ u_short cishort;
+#ifdef CI_MPPC
+ u_int32_t cilong;
+#endif
+
+ p0 = p;
+ if (plen < HEADERLEN) {
+ printer(arg, "too short (%d<%d)", plen, HEADERLEN);
+ return (0);
+ }
+ GETCHAR(code, p);
+ GETCHAR(id, p);
+ GETSHORT(len, p);
+
+ printer(arg, " %s id=0x%x", code_name(code, 1), id);
+
+ if (len < HEADERLEN) {
+ printer(arg, " header length %d<%d", len, HEADERLEN);
+ return (HEADERLEN);
+ }
+ if (len > plen) {
+ printer(arg, " truncated (%d>%d)", len, plen);
+ len = plen;
+ }
+ len -= HEADERLEN;
+
+ switch (code) {
+ case CODE_CONFREQ:
+ case CODE_CONFACK:
+ case CODE_CONFNAK:
+ case CODE_CONFREJ:
+ /* print list of possible compression methods */
+ while (len >= 2) {
+ GETCHAR(code, p);
+ GETCHAR(clen, p);
+ optlen = clen;
+ printer(arg, " <");
+ if (optlen > len)
+ optlen = len;
+ if (optlen < 2)
+ optlen = 2;
+ len -= optlen;
+ optend = p + optlen - 2;
+ switch (code) {
+ case CI_DEFLATE:
+ case CI_DEFLATE_DRAFT:
+ printer(arg, "deflate%s",
+ (code == CI_DEFLATE_DRAFT? "(old#)": ""));
+ if (clen != CILEN_DEFLATE)
+ printer(arg, " length %d", clen);
+ if (optlen >= CILEN_DEFLATE) {
+ GETCHAR(cichar, p);
+ printer(arg, " %d", DEFLATE_SIZE(cichar));
+ if (DEFLATE_METHOD(cichar) != DEFLATE_METHOD_VAL)
+ printer(arg, " method %d", DEFLATE_METHOD(cichar));
+ GETCHAR(cichar, p);
+ if (cichar != DEFLATE_CHK_SEQUENCE)
+ printer(arg, " check %d", cichar);
+ }
+ break;
+ case CI_BSD_COMPRESS:
+ printer(arg, "bsd");
+ if (clen != CILEN_BSD_COMPRESS)
+ printer(arg, " length %d", clen);
+ if (optlen >= CILEN_BSD_COMPRESS) {
+ GETCHAR(cichar, p);
+ printer(arg, " v%d %d", BSD_VERSION(cichar),
+ BSD_NBITS(cichar));
+ }
+ break;
+ case CI_PREDICTOR_1:
+ printer(arg, "predictor-1");
+ if (clen != CILEN_PREDICTOR_1)
+ printer(arg, " length %d", clen);
+ break;
+ case CI_PREDICTOR_2:
+ printer(arg, "predictor-2");
+ if (clen != CILEN_PREDICTOR_2)
+ printer(arg, " length %d", clen);
+ break;
+#ifdef CI_STAC
+ case CI_STAC:
+ printer(arg, "Stac");
+ if (clen != CILEN_STAC)
+ printer(arg, " length %d", clen);
+ if (optlen >= CILEN_STAC) {
+ GETSHORT(cishort, p);
+ GETCHAR(cichar, p);
+ printer(arg, " h%d/m%d", cishort, cichar);
+ }
+ break;
+#endif
+#ifdef CI_MPPC
+ case CI_MPPC:
+ /* There appears to be no good generic name for this one. */
+ if (optlen >= CILEN_MPPC) {
+ GETLONG(cilong, p);
+ if (!(cilong & MPPC_COMP)) {
+ if (cilong & MPPC_MPPE)
+ printer(arg, "MPPE");
+ else
+ printer(arg, "MS-PPC?");
+ } else {
+ if (cilong & MPPC_MPPE)
+ printer(arg, "MPPC+MPPE");
+ else
+ printer(arg, "MPPC");
+ }
+ } else {
+ printer(arg, "MS-?");
+ }
+ if (clen != CILEN_STAC)
+ printer(arg, " length %d", clen);
+ break;
+#endif
+ default:
+ printer(arg, "typ%d len%d ", code, clen);
+ break;
+ }
+ if (p < optend) {
+ if (p+8 < optend)
+ printer(arg, " %.8B ...", p);
+ else
+ printer(arg, " %.*B", optend-p, p);
+ p = optend;
+ }
+ printer(arg, ">");
+ }
+ break;
+
+ case CODE_TERMACK:
+ case CODE_TERMREQ:
+ if (len > 0) {
+ if (len == 2) {
+ GETSHORT(cishort, p);
+ printer(arg, " history %d", cishort);
+ len = 0;
+ } else if (*p >= ' ' && *p < 0x7f) {
+ printer(arg, " ");
+ print_string((char *)p, len, printer, arg);
+ p += len;
+ len = 0;
+ }
+ }
+ break;
+ }
+
+ /* dump out the rest of the packet in hex */
+ if (len > 0) {
+ if (len > 8)
+ printer(arg, " %.8B ...", p);
+ else
+ printer(arg, " %.*B", len, p);
+ p += len;
+ }
+
+ return p - p0;
+}
+
+/*
+ * We have received a packet that the decompressor failed to
+ * decompress. Here we would expect to issue a reset-request, but
+ * Motorola has a patent on resetting the compressor as a result of
+ * detecting an error in the decompressed data after decompression.
+ * (See US patent 5,130,993; international patent publication number
+ * WO 91/10289; Australian patent 73296/91.)
+ *
+ * So we ask the kernel whether the error was detected after
+ * decompression; if it was, we take CCP down, thus disabling
+ * compression :-(, otherwise we issue the reset-request.
+ */
+/*ARGSUSED*/
+static void
+ccp_datainput(unit, pkt, len)
+ int unit;
+ u_char *pkt;
+ int len;
+{
+ fsm *f;
+
+ f = &ccp_fsm[unit];
+ if (f->state == OPENED) {
+ if (ccp_fatal_error(unit)) {
+ /*
+ * Disable compression by taking CCP down.
+ */
+ error("Lost compression sync: disabling compression");
+ ccp_close(unit, "Lost compression sync");
+ } else {
+ /*
+ * Send a reset-request to reset the peer's compressor, if
+ * possible. We don't do anything if we are still waiting
+ * for an acknowledgement to a previous reset-request (to
+ * avoid flooding the peer). We reopen CCP if the peer
+ * doesn't like hearing about CCP Reset-Request (Cisco
+ * sends CCP Code-Reject for Reset-Request). (Reopen
+ * automatically clears the flags and cancels the
+ * timeout.)
+ */
+ if (ccp_localstate[f->unit] & RREQ_REJECTED) {
+ dbglog("reopening CCP to reset peer's compressor");
+ ccp_open(f->unit);
+ } else if (ccp_localstate[f->unit] & RACK_PENDING) {
+ /* Send another reset request; we're out of sequence. */
+ ccp_localstate[f->unit] |= RREQ_REPEAT;
+ } else {
+ dbglog("sending CCP Reset-Request to reset peer's compressor");
+ fsm_sdata(f, CCP_RESETREQ, f->reqid = ++f->id, NULL, 0);
+ TIMEOUT(ccp_rack_timeout, f, RACKTIMEOUT);
+ ccp_localstate[f->unit] |= RACK_PENDING;
+ }
+ }
+ }
+}
+
+/*
+ * Timeout waiting for reset-ack.
+ */
+static void
+ccp_rack_timeout(arg)
+ void *arg;
+{
+ fsm *f = arg;
+
+ /* Timeout; no longer pending. */
+ ccp_localstate[f->unit] &= ~RACK_PENDING;
+
+ /* Frankly, it's a coding flaw if this occurs. */
+ if (f->state != OPENED)
+ return;
+
+ if (ccp_localstate[f->unit] & RREQ_IGNORED) {
+ info("peer ignored our CCP Reset-Request twice; reopen instead");
+ ccp_localstate[f->unit] =
+ (ccp_localstate[f->unit] & ~RREQ_IGNORED) | RREQ_REJECTED;
+ ccp_open(f->unit);
+ } else if (ccp_localstate[f->unit] & RREQ_REPEAT) {
+ dbglog("sending another CCP Reset-Request on timeout");
+ fsm_sdata(f, CCP_RESETREQ, f->reqid, NULL, 0);
+ TIMEOUT(ccp_rack_timeout, f, RACKTIMEOUT);
+ ccp_localstate[f->unit] =
+ (ccp_localstate[f->unit] & ~RREQ_REPEAT) | RREQ_IGNORED |
+ RACK_PENDING;
+ } else {
+ dbglog("timeout waiting for CCP Reset-Ack; hope for the best");
+ ccp_localstate[f->unit] |= RREQ_IGNORED;
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppd/ccp.h b/usr/src/cmd/cmd-inet/usr.bin/pppd/ccp.h
new file mode 100644
index 0000000000..f9be744dfc
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppd/ccp.h
@@ -0,0 +1,50 @@
+/*
+ * ccp.h - Definitions for PPP Compression Control Protocol.
+ *
+ * Copyright (c) 1994 The Australian National University.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies. This software is provided without any
+ * warranty, express or implied. The Australian National University
+ * makes no representations about the suitability of this software for
+ * any purpose.
+ *
+ * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
+ * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
+ * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
+ * OR MODIFICATIONS.
+ *
+ * $Id: ccp.h,v 1.9 1998/11/07 06:59:26 paulus Exp $
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+typedef struct ccp_options {
+ bool bsd_compress; /* do BSD Compress? */
+ bool deflate; /* do Deflate? */
+ bool predictor_1; /* do Predictor-1? */
+ bool predictor_2; /* do Predictor-2? */
+ bool deflate_correct; /* use correct code for deflate? */
+ bool deflate_draft; /* use draft RFC code for deflate? */
+ u_short bsd_bits; /* # bits/code for BSD Compress */
+ u_short deflate_size; /* lg(window size) for Deflate */
+ short method; /* code for chosen compression method */
+} ccp_options;
+
+extern fsm ccp_fsm[];
+extern ccp_options ccp_wantoptions[];
+extern ccp_options ccp_gotoptions[];
+extern ccp_options ccp_allowoptions[];
+extern ccp_options ccp_hisoptions[];
+
+extern struct protent ccp_protent;
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppd/chap.c b/usr/src/cmd/cmd-inet/usr.bin/pppd/chap.c
new file mode 100644
index 0000000000..e9fb56605e
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppd/chap.c
@@ -0,0 +1,982 @@
+/*
+ * chap.c - Challenge Handshake Authentication Protocol.
+ *
+ * Copyright (c) 2000 by Sun Microsystems, Inc.
+ * All rights reserved.
+ *
+ * Copyright (c) 1993 The Australian National University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Australian National University. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Copyright (c) 1991 Gregory M. Christy.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Gregory M. Christy. The name of the author may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+#define RCSID "$Id: chap.c,v 1.24 1999/11/15 01:51:50 paulus Exp $"
+
+/*
+ * TODO:
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include "pppd.h"
+#include "chap.h"
+#include "md5.h"
+#if defined(CHAPMS) || defined(CHAPMSV2)
+#include "chap_ms.h"
+#endif
+
+#if !defined(lint) && !defined(_lint)
+static const char rcsid[] = RCSID;
+#endif
+
+/*
+ * Command-line options.
+ */
+static option_t chap_option_list[] = {
+ { "chap-restart", o_int, &chap[0].timeouttime,
+ "Set CHAP Challenge retry interval" },
+ { "chap-max-challenge", o_int, &chap[0].max_transmits,
+ "Max number of Challenges sent without a valid response" },
+ { "chap-interval", o_int, &chap[0].chal_interval,
+ "Set interval for rechallenge" },
+#if defined(CHAPMS) && defined(MSLANMAN)
+ { "ms-lanman", o_bool, &ms_lanman,
+ "Use obsolete LAN Manager password when using MS-CHAP", 1 },
+#endif
+ { NULL }
+};
+
+/*
+ * Protocol entry points.
+ */
+static void ChapInit __P((int));
+static void ChapLowerUp __P((int));
+static void ChapLowerDown __P((int));
+static void ChapInput __P((int, u_char *, int));
+static void ChapProtocolReject __P((int));
+static int ChapPrintPkt __P((u_char *, int,
+ void (*) __P((void *, const char *, ...)), void *));
+
+struct protent chap_protent = {
+ PPP_CHAP, /* PPP protocol number */
+ ChapInit, /* Initialization procedure */
+ ChapInput, /* Process a received packet */
+ ChapProtocolReject, /* Process a received protocol-reject */
+ ChapLowerUp, /* Lower layer has come up */
+ ChapLowerDown, /* Lower layer has gone down */
+ NULL, /* Open the protocol */
+ NULL, /* Close the protocol */
+ ChapPrintPkt, /* Print a packet in readable form */
+ NULL, /* Process a received data packet */
+ 1, /* 0 iff protocol is disabled */
+ "CHAP", /* Text name of protocol */
+ NULL, /* Text name of corresponding data protocol */
+ chap_option_list, /* List of command-line options */
+ NULL, /* Check requested options, assign defaults */
+ NULL, /* Configure interface for demand-dial */
+ NULL /* Say whether to bring up link for this pkt */
+};
+
+/* Not static'd for plug-ins */
+chap_state chap[NUM_PPP]; /* CHAP state; one for each unit */
+
+static void ChapChallengeTimeout __P((void *));
+static void ChapResponseTimeout __P((void *));
+static void ChapReceiveChallenge __P((chap_state *, u_char *, int, int));
+static void ChapRechallenge __P((void *));
+static void ChapReceiveResponse __P((chap_state *, u_char *, int, int));
+static void ChapReceiveSuccess __P((chap_state *, u_char *, int, int));
+static void ChapReceiveFailure __P((chap_state *, u_char *, int, int));
+static void ChapSendStatus __P((chap_state *, int));
+static void ChapSendChallenge __P((chap_state *));
+static void ChapSendResponse __P((chap_state *));
+static void ChapGenChallenge __P((chap_state *));
+
+static const char *
+chap_cstate(int clientstate)
+{
+ static const char *cstate[] = { CHAPCS__LIST };
+ static char buf[32];
+
+ if (clientstate < 0 || clientstate >= Dim(cstate)) {
+ (void) slprintf(buf, sizeof(buf), "#%d", clientstate);
+ return (buf);
+ }
+ return cstate[clientstate];
+}
+
+static const char *
+chap_sstate(int serverstate)
+{
+ static const char *sstate[] = { CHAPSS__LIST };
+ static char buf[32];
+
+ if (serverstate < 0 || serverstate >= Dim(sstate)) {
+ (void) slprintf(buf, sizeof(buf), "#%d", serverstate);
+ return (buf);
+ }
+ return sstate[serverstate];
+}
+
+/*
+ * ChapInit - Initialize a CHAP unit.
+ */
+static void
+ChapInit(unit)
+ int unit;
+{
+ chap_state *cstate = &chap[unit];
+
+ BZERO(cstate, sizeof(*cstate));
+ cstate->unit = unit;
+ cstate->clientstate = CHAPCS_INITIAL;
+ cstate->serverstate = CHAPSS_INITIAL;
+ cstate->timeouttime = CHAP_DEFTIMEOUT;
+ cstate->max_transmits = CHAP_DEFTRANSMITS;
+
+ /* XXX save get_first_hwaddr() here. */
+ /* random number generator is initialized in magic_init */
+}
+
+
+/*
+ * ChapAuthWithPeer - Authenticate us with our peer (start client).
+ *
+ */
+void
+ChapAuthWithPeer(unit, our_name, digest)
+ int unit;
+ char *our_name;
+ int digest;
+{
+ chap_state *cstate = &chap[unit];
+
+ cstate->resp_name = our_name;
+ cstate->resp_type = digest;
+
+ if (cstate->clientstate == CHAPCS_INITIAL ||
+ cstate->clientstate == CHAPCS_PENDING) {
+ /* lower layer isn't up - wait until later */
+ cstate->clientstate = CHAPCS_PENDING;
+ return;
+ }
+
+ /*
+ * We get here as a result of LCP coming up.
+ * So even if CHAP was open before, we will
+ * have to re-authenticate ourselves.
+ */
+ cstate->clientstate = CHAPCS_LISTEN;
+}
+
+
+/*
+ * ChapAuthPeer - Authenticate our peer (start server).
+ */
+void
+ChapAuthPeer(unit, our_name, digest)
+ int unit;
+ char *our_name;
+ int digest;
+{
+ chap_state *cstate = &chap[unit];
+
+ cstate->chal_name = our_name;
+ cstate->chal_type = digest;
+
+ if (cstate->serverstate == CHAPSS_INITIAL ||
+ cstate->serverstate == CHAPSS_PENDING) {
+ /* lower layer isn't up - wait until later */
+ cstate->serverstate = CHAPSS_PENDING;
+ return;
+ }
+
+ ChapGenChallenge(cstate);
+ ChapSendChallenge(cstate); /* crank it up dude! */
+ cstate->serverstate = CHAPSS_INITIAL_CHAL;
+}
+
+
+/*
+ * ChapChallengeTimeout - Timeout expired on sending challenge.
+ */
+static void
+ChapChallengeTimeout(arg)
+ void *arg;
+{
+ chap_state *cstate = (chap_state *) arg;
+
+ /* if we aren't sending challenges, don't worry. then again we */
+ /* probably shouldn't be here either */
+ if (cstate->serverstate != CHAPSS_INITIAL_CHAL &&
+ cstate->serverstate != CHAPSS_RECHALLENGE)
+ return;
+
+ if (cstate->chal_transmits >= cstate->max_transmits) {
+ /* give up on peer */
+ error("Peer failed to respond to CHAP challenge");
+ cstate->serverstate = CHAPSS_BADAUTH;
+ auth_peer_fail(cstate->unit, PPP_CHAP);
+ return;
+ }
+
+ ChapSendChallenge(cstate); /* Re-send challenge */
+}
+
+
+/*
+ * ChapResponseTimeout - Timeout expired on sending response.
+ */
+static void
+ChapResponseTimeout(arg)
+ void *arg;
+{
+ chap_state *cstate = (chap_state *) arg;
+
+ /* if we aren't sending a response, don't worry. */
+ if (cstate->clientstate != CHAPCS_RESPONSE)
+ return;
+
+ ChapSendResponse(cstate); /* re-send response */
+}
+
+
+/*
+ * ChapRechallenge - Time to challenge the peer again.
+ */
+static void
+ChapRechallenge(arg)
+ void *arg;
+{
+ chap_state *cstate = (chap_state *) arg;
+
+ /* if we aren't sending a response, don't worry. */
+ if (cstate->serverstate != CHAPSS_OPEN)
+ return;
+
+ ChapGenChallenge(cstate);
+ ChapSendChallenge(cstate);
+ cstate->serverstate = CHAPSS_RECHALLENGE;
+}
+
+
+/*
+ * ChapLowerUp - The lower layer is up.
+ *
+ * Start up if we have pending requests.
+ */
+static void
+ChapLowerUp(unit)
+ int unit;
+{
+ chap_state *cstate = &chap[unit];
+
+ if (cstate->clientstate == CHAPCS_INITIAL)
+ cstate->clientstate = CHAPCS_CLOSED;
+ else if (cstate->clientstate == CHAPCS_PENDING)
+ cstate->clientstate = CHAPCS_LISTEN;
+
+ if (cstate->serverstate == CHAPSS_INITIAL)
+ cstate->serverstate = CHAPSS_CLOSED;
+ else if (cstate->serverstate == CHAPSS_PENDING) {
+ ChapGenChallenge(cstate);
+ ChapSendChallenge(cstate);
+ cstate->serverstate = CHAPSS_INITIAL_CHAL;
+ }
+}
+
+
+/*
+ * ChapLowerDown - The lower layer is down.
+ *
+ * Cancel all timeouts.
+ */
+static void
+ChapLowerDown(unit)
+ int unit;
+{
+ chap_state *cstate = &chap[unit];
+
+ /* Timeout(s) pending? Cancel if so. */
+ if (cstate->serverstate == CHAPSS_INITIAL_CHAL ||
+ cstate->serverstate == CHAPSS_RECHALLENGE)
+ UNTIMEOUT(ChapChallengeTimeout, cstate);
+ else if (cstate->serverstate == CHAPSS_OPEN
+ && cstate->chal_interval != 0)
+ UNTIMEOUT(ChapRechallenge, cstate);
+ if (cstate->clientstate == CHAPCS_RESPONSE)
+ UNTIMEOUT(ChapResponseTimeout, cstate);
+
+ cstate->clientstate = CHAPCS_INITIAL;
+ cstate->serverstate = CHAPSS_INITIAL;
+}
+
+
+/*
+ * ChapProtocolReject - Peer doesn't grok CHAP.
+ */
+static void
+ChapProtocolReject(unit)
+ int unit;
+{
+ chap_state *cstate = &chap[unit];
+
+ if (cstate->serverstate != CHAPSS_INITIAL &&
+ cstate->serverstate != CHAPSS_CLOSED)
+ auth_peer_fail(unit, PPP_CHAP);
+ if (cstate->clientstate != CHAPCS_INITIAL &&
+ cstate->clientstate != CHAPCS_CLOSED)
+ auth_withpeer_fail(unit, PPP_CHAP);
+ ChapLowerDown(unit); /* shutdown chap */
+}
+
+
+/*
+ * ChapInput - Input CHAP packet.
+ */
+static void
+ChapInput(unit, inpacket, packet_len)
+ int unit;
+ u_char *inpacket;
+ int packet_len;
+{
+ chap_state *cstate = &chap[unit];
+ u_char *inp;
+ u_char code, id;
+ int len;
+
+ /*
+ * Parse header (code, id and length).
+ * If packet too short, drop it.
+ */
+ inp = inpacket;
+ if (packet_len < CHAP_HEADERLEN) {
+ error("CHAP: packet is too small (%d < %d)", packet_len,
+ CHAP_HEADERLEN);
+ return;
+ }
+ GETCHAR(code, inp);
+ GETCHAR(id, inp);
+ GETSHORT(len, inp);
+ if (len < CHAP_HEADERLEN || len > packet_len) {
+ error("CHAP: packet has illegal length %d (%d..%d)",
+ len, CHAP_HEADERLEN, packet_len);
+ return;
+ }
+ len -= CHAP_HEADERLEN;
+
+ /*
+ * Action depends on code (as in fact it usually does :-).
+ */
+ switch (code) {
+ case CHAP_CHALLENGE:
+ ChapReceiveChallenge(cstate, inp, id, len);
+ break;
+
+ case CHAP_RESPONSE:
+ ChapReceiveResponse(cstate, inp, id, len);
+ break;
+
+ case CHAP_FAILURE:
+ ChapReceiveFailure(cstate, inp, id, len);
+ break;
+
+ case CHAP_SUCCESS:
+ ChapReceiveSuccess(cstate, inp, id, len);
+ break;
+
+ default:
+ /* CHAP does not define a code reject. */
+ warn("Unknown CHAP code (%d) received.", code);
+ break;
+ }
+}
+
+
+/*
+ * ChapReceiveChallenge - Receive Challenge and send Response.
+ */
+static void
+ChapReceiveChallenge(cstate, inp, id, len)
+ chap_state *cstate;
+ u_char *inp;
+ int id;
+ int len;
+{
+ int rchallenge_len;
+ u_char *rchallenge;
+ int secret_len;
+ char rhostname[MAXNAMELEN];
+ char secret[MAXSECRETLEN];
+ MD5_CTX mdContext;
+ u_char hash[MD5_SIGNATURE_SIZE];
+ int fake_response = 0;
+
+ if (cstate->clientstate == CHAPCS_CLOSED ||
+ cstate->clientstate == CHAPCS_PENDING) {
+ if (debug)
+ dbglog("CHAP Challenge unexpectedly received in state %s",
+ chap_cstate(cstate->clientstate));
+ return;
+ }
+
+ if (len < 2) {
+ error("CHAP: Challenge message length %d", len);
+ return;
+ }
+
+ GETCHAR(rchallenge_len, inp);
+ len -= sizeof (u_char) + rchallenge_len; /* now name field length */
+ if (len < 0) {
+ error("CHAP: Challenge truncated");
+ return;
+ }
+ rchallenge = inp;
+ INCPTR(rchallenge_len, inp);
+
+ if (len >= sizeof(rhostname))
+ len = sizeof(rhostname) - 1;
+ if (len > 0) {
+ BCOPY(inp, rhostname, len);
+ }
+ rhostname[len] = '\0';
+
+#ifdef CHECK_CHALLENGE_LENGTH
+ if (rchallenge_len < CHECK_CHALLENGE_LENGTH) {
+ warn("CHAP: Challenge from %s unreasonably short (%d octets)",
+ rhostname, rchallenge_len);
+ fake_response = 1;
+ }
+#endif
+
+ /* XXX compare against saved get_first_hwaddr() here. */
+
+ /* Microsoft NT doesn't send a name in the CHAP Challenge message */
+ if (explicit_remote ||
+ (remote_name[0] != '\0' && rhostname[0] == '\0')) {
+ (void) strlcpy(rhostname, remote_name, sizeof(rhostname));
+ if (debug)
+ dbglog("CHAP: Peer gave no name; using '%q' as remote name",
+ rhostname);
+ }
+
+ if (cstate->peercname[0] == '\0') {
+ (void) strlcpy(cstate->peercname, rhostname,
+ sizeof (cstate->peercname));
+ } else if (strcmp(rhostname, cstate->peercname) != 0) {
+ fake_response = 1;
+ warn("CHAP: peer challenge name changed from '%q' to '%q'",
+ cstate->peercname, rhostname);
+ }
+
+ /* get secret for authenticating ourselves with the specified host */
+ if (!get_secret(cstate->unit, cstate->resp_name, rhostname,
+ secret, &secret_len, 0)) {
+ secret_len = 0; /* assume null secret if can't find one */
+ warn("No CHAP secret found for authenticating us (%q) to %q",
+ cstate->resp_name, rhostname);
+ }
+
+ /* cancel response send timeout if necessary */
+ if (cstate->clientstate == CHAPCS_RESPONSE)
+ UNTIMEOUT(ChapResponseTimeout, cstate);
+
+ cstate->resp_id = id;
+ cstate->resp_transmits = 0;
+
+ /* generate MD based on negotiated type */
+ switch (cstate->resp_type) {
+
+ case CHAP_DIGEST_MD5:
+ MD5Init(&mdContext);
+ MD5Update(&mdContext, &cstate->resp_id, 1);
+ MD5Update(&mdContext, (u_char *)secret, (unsigned)secret_len);
+ MD5Update(&mdContext, rchallenge, rchallenge_len);
+ MD5Final(hash, &mdContext);
+ if (fake_response) {
+ for (len = 0; len < MD5_SIGNATURE_SIZE; len++)
+ cstate->response[len] = (char) (drand48() * 0xff);
+ } else {
+ BCOPY(hash, cstate->response, MD5_SIGNATURE_SIZE);
+ }
+ cstate->resp_length = MD5_SIGNATURE_SIZE;
+ break;
+
+#ifdef CHAPMS
+ case CHAP_MICROSOFT:
+ ChapMS(cstate, rchallenge, rchallenge_len, secret, secret_len);
+ break;
+#endif
+
+#ifdef CHAPMSV2
+ case CHAP_MICROSOFT_V2:
+ ChapMSv2(cstate, rchallenge, rchallenge_len, secret, secret_len);
+ break;
+#endif
+
+ default:
+ error("CHAP: unknown digest type %d", cstate->resp_type);
+ return;
+ }
+
+ BZERO(secret, sizeof(secret));
+ ChapSendResponse(cstate);
+}
+
+
+/*
+ * ChapReceiveResponse - Receive and process response.
+ */
+static void
+ChapReceiveResponse(cstate, inp, id, len)
+ chap_state *cstate;
+ u_char *inp;
+ int id;
+ int len;
+{
+ u_char *remmd, remmd_len;
+ int secret_len, old_state;
+ int code;
+ char rhostname[MAXNAMELEN], *rhn;
+ MD5_CTX mdContext;
+ char secret[MAXSECRETLEN];
+ u_char hash[MD5_SIGNATURE_SIZE];
+
+ if (cstate->serverstate == CHAPSS_CLOSED ||
+ cstate->serverstate == CHAPSS_PENDING) {
+ if (debug)
+ dbglog("CHAP Response unexpectedly received in state %s",
+ chap_sstate(cstate->serverstate));
+ return;
+ }
+
+ if (id != cstate->chal_id) {
+ if (debug)
+ dbglog("CHAP: discard response %d; expecting %d", id,
+ cstate->chal_id);
+ return; /* doesn't match ID of last challenge */
+ }
+
+ /*
+ * If we have received a duplicate or bogus Response,
+ * we have to send the same answer (Success/Failure)
+ * as we did for the first Response we saw.
+ */
+ if (cstate->serverstate == CHAPSS_OPEN) {
+ if (debug)
+ dbglog("CHAP ignoring response and resending success message");
+ ChapSendStatus(cstate, CHAP_SUCCESS);
+ return;
+ }
+ if (cstate->serverstate == CHAPSS_BADAUTH) {
+ if (debug)
+ dbglog("CHAP ignoring response and resending failure message");
+ ChapSendStatus(cstate, CHAP_FAILURE);
+ return;
+ }
+
+ if (len < 2) {
+ error("CHAP: Response message length %d", len);
+ return;
+ }
+ GETCHAR(remmd_len, inp); /* get length of MD */
+ remmd = inp; /* get pointer to MD */
+ INCPTR(remmd_len, inp);
+
+ len -= sizeof (u_char) + remmd_len;
+ if (len < 0) {
+ error("CHAP: Response truncated");
+ return;
+ }
+
+ UNTIMEOUT(ChapChallengeTimeout, cstate);
+
+ if (len >= sizeof(rhostname))
+ len = sizeof(rhostname) - 1;
+ BCOPY(inp, rhostname, len);
+ rhostname[len] = '\0';
+
+ /*
+ * Get secret for authenticating them with us,
+ * do the hash ourselves, and compare the result.
+ */
+ code = CHAP_FAILURE;
+ rhn = (explicit_remote? remote_name: rhostname);
+ if (cstate->serverstate == CHAPSS_RECHALLENGE &&
+ strcmp(rhostname, peer_authname) != 0) {
+ warn("Peer changed his name from '%q' to '%q' on rechallenge",
+ peer_authname, rhostname);
+ } else if (!get_secret(cstate->unit, rhn, cstate->chal_name, secret,
+ &secret_len, 1)) {
+ warn("No CHAP secret found for authenticating %q to us (%q)",
+ rhn, cstate->chal_name);
+ } else {
+
+ /* generate MD based on negotiated type */
+ switch (cstate->chal_type) {
+
+ case CHAP_DIGEST_MD5: /* only MD5 is defined for now */
+ if (remmd_len != MD5_SIGNATURE_SIZE)
+ break; /* it's not even the right length */
+ MD5Init(&mdContext);
+ MD5Update(&mdContext, &cstate->chal_id, 1);
+ MD5Update(&mdContext, (u_char *)secret, secret_len);
+ MD5Update(&mdContext, cstate->challenge, cstate->chal_len);
+ MD5Final(hash, &mdContext);
+
+ /* compare local and remote MDs and send the appropriate status */
+ if (memcmp (hash, remmd, MD5_SIGNATURE_SIZE) == 0)
+ code = CHAP_SUCCESS; /* they are the same! */
+ break;
+
+#ifdef CHAPMS
+ case CHAP_MICROSOFT:
+ if (ChapMSValidate(cstate, remmd, remmd_len, secret,
+ secret_len))
+ code = CHAP_SUCCESS;
+ break;
+#endif
+
+#ifdef CHAPMSV2
+ case CHAP_MICROSOFT_V2:
+ if (ChapMSv2Validate(cstate, rhostname, remmd, remmd_len,
+ secret, secret_len))
+ code = CHAP_SUCCESS;
+ break;
+#endif
+
+ default:
+ error("CHAP: unknown digest type %d", cstate->chal_type);
+ }
+ }
+
+ BZERO(secret, sizeof(secret));
+ ChapSendStatus(cstate, code);
+
+ if (code == CHAP_SUCCESS) {
+ old_state = cstate->serverstate;
+ cstate->serverstate = CHAPSS_OPEN;
+ if (old_state == CHAPSS_INITIAL_CHAL) {
+ auth_peer_success(cstate->unit, PPP_CHAP, rhostname, len);
+ }
+ if (cstate->chal_interval != 0)
+ TIMEOUT(ChapRechallenge, cstate, cstate->chal_interval);
+ if (old_state == CHAPSS_INITIAL_CHAL)
+ notice("CHAP peer authentication succeeded for %q", rhostname);
+ else if (debug)
+ dbglog("CHAP peer reauthentication succeeded for %q", rhostname);
+ } else {
+ error("CHAP peer %sauthentication failed for remote host %q",
+ (cstate->serverstate == CHAPSS_INITIAL_CHAL ? "" : "re"),
+ rhostname);
+ cstate->serverstate = CHAPSS_BADAUTH;
+ auth_peer_fail(cstate->unit, PPP_CHAP);
+ }
+}
+
+/*
+ * ChapReceiveSuccess - Receive Success
+ */
+/*ARGSUSED*/
+static void
+ChapReceiveSuccess(cstate, inp, id, len)
+ chap_state *cstate;
+ u_char *inp;
+ u_char id;
+ int len;
+{
+
+ if (cstate->clientstate == CHAPCS_OPEN) {
+ /* presumably an answer to a duplicate response */
+ if (debug)
+ dbglog("CHAP duplicate Success message ignored");
+ return;
+ }
+
+ if (cstate->clientstate != CHAPCS_RESPONSE) {
+ /* don't know what this is */
+ if (debug)
+ dbglog("CHAP Success unexpectedly received in state %s",
+ chap_cstate(cstate->clientstate));
+ return;
+ }
+
+ UNTIMEOUT(ChapResponseTimeout, cstate);
+
+ /*
+ * Print message.
+ */
+ if (len > 0)
+ PRINTMSG(inp, len);
+
+ cstate->clientstate = CHAPCS_OPEN;
+
+ auth_withpeer_success(cstate->unit, PPP_CHAP);
+}
+
+
+/*
+ * ChapReceiveFailure - Receive failure.
+ */
+/*ARGSUSED*/
+static void
+ChapReceiveFailure(cstate, inp, id, len)
+ chap_state *cstate;
+ u_char *inp;
+ u_char id;
+ int len;
+{
+ if (cstate->clientstate != CHAPCS_RESPONSE) {
+ /* don't know what this is */
+ if (debug)
+ dbglog("CHAP Failure unexpectedly received in state %s",
+ chap_cstate(cstate->clientstate));
+ return;
+ }
+
+ UNTIMEOUT(ChapResponseTimeout, cstate);
+
+ /*
+ * Print message.
+ */
+ if (len > 0)
+ PRINTMSG(inp, len);
+
+ error("CHAP authentication failed");
+ auth_withpeer_fail(cstate->unit, PPP_CHAP);
+}
+
+
+/*
+ * ChapSendChallenge - Send an Authenticate challenge.
+ */
+static void
+ChapSendChallenge(cstate)
+ chap_state *cstate;
+{
+ u_char *outp;
+ int chal_len, name_len;
+ int outlen;
+
+ chal_len = cstate->chal_len;
+ name_len = strlen(cstate->chal_name);
+ outlen = CHAP_HEADERLEN + sizeof (u_char) + chal_len + name_len;
+ outp = outpacket_buf;
+
+ MAKEHEADER(outp, PPP_CHAP); /* paste in a CHAP header */
+
+ PUTCHAR(CHAP_CHALLENGE, outp);
+ PUTCHAR(cstate->chal_id, outp);
+ PUTSHORT(outlen, outp);
+
+ PUTCHAR(chal_len, outp); /* put length of challenge */
+ BCOPY(cstate->challenge, outp, chal_len);
+ INCPTR(chal_len, outp);
+
+ BCOPY(cstate->chal_name, outp, name_len); /* append hostname */
+
+ output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
+
+ TIMEOUT(ChapChallengeTimeout, cstate, cstate->timeouttime);
+ ++cstate->chal_transmits;
+}
+
+
+/*
+ * ChapSendStatus - Send a status response (ack or nak).
+ */
+static void
+ChapSendStatus(cstate, code)
+ chap_state *cstate;
+ int code;
+{
+ u_char *outp;
+ int outlen, msglen;
+ char msg[256], *msgp;
+
+ if ((msgp = cstate->stat_message) != NULL) {
+ msglen = cstate->stat_length;
+ } else {
+ if (code == CHAP_SUCCESS)
+ (void) slprintf(msg, sizeof(msg), "Welcome to %s.", hostname);
+ else
+ (void) slprintf(msg, sizeof(msg), "I don't like you. Go 'way.");
+ msglen = strlen(msg);
+ msgp = msg;
+ }
+
+ outlen = CHAP_HEADERLEN + msglen;
+ outp = outpacket_buf;
+
+ MAKEHEADER(outp, PPP_CHAP); /* paste in a header */
+
+ PUTCHAR(code, outp);
+ PUTCHAR(cstate->chal_id, outp);
+ PUTSHORT(outlen, outp);
+ BCOPY(msgp, outp, msglen);
+ output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
+}
+
+/*
+ * ChapGenChallenge is used to generate a pseudo-random challenge string of
+ * a pseudo-random length between min_len and max_len. The challenge
+ * string and its length are stored in *cstate, and various other fields of
+ * *cstate are initialized.
+ */
+
+static void
+ChapGenChallenge(cstate)
+ chap_state *cstate;
+{
+ int chal_len;
+ u_char *ptr = cstate->challenge;
+ int i;
+
+ /* pick a random challenge length between MIN_CHALLENGE_LENGTH and
+ MAX_CHALLENGE_LENGTH */
+ chal_len = (unsigned) ((drand48() *
+ (MAX_CHALLENGE_LENGTH - MIN_CHALLENGE_LENGTH)) +
+ MIN_CHALLENGE_LENGTH);
+ cstate->chal_len = chal_len;
+ cstate->chal_id = ++cstate->id;
+ cstate->chal_transmits = 0;
+
+ /* XXX tack on saved get_first_hwaddr() here. */
+
+ /* generate a random string */
+ for (i = 0; i < chal_len; i++)
+ *ptr++ = (char) (drand48() * 0xff);
+}
+
+/*
+ * ChapSendResponse - send a response packet with values as specified
+ * in *cstate.
+ */
+/* ARGSUSED */
+static void
+ChapSendResponse(cstate)
+ chap_state *cstate;
+{
+ u_char *outp;
+ int outlen, md_len, name_len;
+
+ md_len = cstate->resp_length;
+ name_len = strlen(cstate->resp_name);
+ outlen = CHAP_HEADERLEN + sizeof (u_char) + md_len + name_len;
+ outp = outpacket_buf;
+
+ MAKEHEADER(outp, PPP_CHAP);
+
+ PUTCHAR(CHAP_RESPONSE, outp); /* we are a response */
+ PUTCHAR(cstate->resp_id, outp); /* copy id from challenge packet */
+ PUTSHORT(outlen, outp); /* packet length */
+
+ PUTCHAR(md_len, outp); /* length of MD */
+ BCOPY(cstate->response, outp, md_len); /* copy MD to buffer */
+ INCPTR(md_len, outp);
+
+ BCOPY(cstate->resp_name, outp, name_len); /* append our name */
+
+ /* send the packet */
+ output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
+
+ cstate->clientstate = CHAPCS_RESPONSE;
+ TIMEOUT(ChapResponseTimeout, cstate, cstate->timeouttime);
+ ++cstate->resp_transmits;
+}
+
+/*
+ * ChapPrintPkt - print the contents of a CHAP packet.
+ */
+static char *ChapCodenames[] = {
+ "Challenge", "Response", "Success", "Failure"
+};
+
+static int
+ChapPrintPkt(p, plen, printer, arg)
+ u_char *p;
+ int plen;
+ void (*printer) __P((void *, const char *, ...));
+ void *arg;
+{
+ int code, id, len;
+ int clen, nlen;
+ u_char x;
+
+ if (plen < CHAP_HEADERLEN)
+ return 0;
+ GETCHAR(code, p);
+ GETCHAR(id, p);
+ GETSHORT(len, p);
+ if (len < CHAP_HEADERLEN || len > plen)
+ return 0;
+
+ if (code >= 1 && code <= sizeof(ChapCodenames) / sizeof(char *))
+ printer(arg, " %s", ChapCodenames[code-1]);
+ else
+ printer(arg, " code=0x%x", code);
+ printer(arg, " id=0x%x", id);
+ len -= CHAP_HEADERLEN;
+ switch (code) {
+ case CHAP_CHALLENGE:
+ case CHAP_RESPONSE:
+ if (len < 1)
+ break;
+ clen = p[0];
+ if (len < clen + 1)
+ break;
+ ++p;
+ nlen = len - clen - 1;
+ printer(arg, " <");
+ for (; clen > 0; --clen) {
+ GETCHAR(x, p);
+ printer(arg, "%.2x", x);
+ }
+ printer(arg, ">, name = ");
+ print_string((char *)p, nlen, printer, arg);
+ break;
+ case CHAP_FAILURE:
+ case CHAP_SUCCESS:
+ printer(arg, " ");
+ print_string((char *)p, len, printer, arg);
+ break;
+ default:
+ for (clen = len; clen > 0; --clen) {
+ GETCHAR(x, p);
+ printer(arg, " %.2x", x);
+ }
+ }
+
+ return len + CHAP_HEADERLEN;
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppd/chap.h b/usr/src/cmd/cmd-inet/usr.bin/pppd/chap.h
new file mode 100644
index 0000000000..56a894c140
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppd/chap.h
@@ -0,0 +1,144 @@
+/*
+ * chap.h - Challenge Handshake Authentication Protocol definitions.
+ *
+ * Copyright (c) 2000 by Sun Microsystems, Inc.
+ * All rights reserved.
+ *
+ * Copyright (c) 1993 The Australian National University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Australian National University. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Copyright (c) 1991 Gregory M. Christy
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the author.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: chap.h,v 1.8 1999/11/15 01:44:41 paulus Exp $
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifndef __CHAP_INCLUDE__
+#define __CHAP_INCLUDE__
+
+/* Code + ID + length */
+#define CHAP_HEADERLEN 4
+
+#define CHAP_DIGEST_MD5 5 /* use MD5 algorithm */
+#define MD5_SIGNATURE_SIZE 16 /* 16 bytes in a MD5 message digest */
+#define CHAP_MICROSOFT 0x80 /* use Microsoft-compatible alg. */
+#define CHAP_MICROSOFT_V2 0x81 /* use MS-CHAPv2 */
+
+#define CHECK_CHALLENGE_LENGTH 8 /* Minimum acceptable challenge */
+
+/*
+ * CHAP message code numbers.
+ */
+#define CHAP_CHALLENGE 1
+#define CHAP_RESPONSE 2
+#define CHAP_SUCCESS 3
+#define CHAP_FAILURE 4
+
+/*
+ * Challenge lengths (for challenges we send) and other limits.
+ */
+#define MIN_CHALLENGE_LENGTH 16
+#define MAX_CHALLENGE_LENGTH 24
+#define MAX_RESPONSE_LENGTH 64 /* sufficient for MD5 or MS-CHAP */
+/* These are here to remind people of the buffer limits */
+#define MS_CHAP_RESPONSE_LEN 49 /* Response length for MS-CHAP */
+#define MS_CHAPV2_RESPONSE_LEN 49 /* Response length for MS-CHAPv2 */
+
+/*
+ * Each interface is described by a chap structure.
+ */
+
+typedef struct chap_state {
+ int unit; /* Interface unit number */
+ int clientstate; /* Client state */
+ int serverstate; /* Server state */
+ char peercname[MAXNAMELEN]; /* unauthenticated peer name in challenge */
+ u_char challenge[MAX_CHALLENGE_LENGTH]; /* last challenge string sent */
+ u_char chal_len; /* challenge length */
+ u_char chal_id; /* ID of last challenge */
+ u_char chal_type; /* hash algorithm for challenges */
+ u_char id; /* Current id */
+ char *chal_name; /* Our name to use with challenge */
+ int chal_interval; /* Time until we challenge peer again */
+ int timeouttime; /* Timeout time in seconds */
+ int max_transmits; /* Maximum # of challenge transmissions */
+ int chal_transmits; /* Number of transmissions of challenge */
+ int resp_transmits; /* Number of transmissions of response */
+ u_char response[MAX_RESPONSE_LENGTH]; /* Response to send */
+ u_char resp_length; /* length of response */
+ u_char resp_id; /* ID for response messages */
+ u_char resp_type; /* hash algorithm for responses */
+ u_char stat_length; /* Length of status message (MS-CHAP) */
+ char *resp_name; /* Our name to send with response */
+ char *stat_message; /* per-algorithm status message (MS-CHAP) */
+} chap_state;
+
+
+/*
+ * Client (authenticatee) states.
+ */
+#define CHAPCS_INITIAL 0 /* Lower layer down, not opened */
+#define CHAPCS_CLOSED 1 /* Lower layer up, not opened */
+#define CHAPCS_PENDING 2 /* Auth us to peer when lower up */
+#define CHAPCS_LISTEN 3 /* Listening for a challenge */
+#define CHAPCS_RESPONSE 4 /* Sent response, waiting for status */
+#define CHAPCS_OPEN 5 /* We've received Success */
+
+#define CHAPCS__LIST \
+ "Initial", "Closed", "Pending", "Listen", \
+ "Response", "Open"
+
+/*
+ * Server (authenticator) states.
+ */
+#define CHAPSS_INITIAL 0 /* Lower layer down, not opened */
+#define CHAPSS_CLOSED 1 /* Lower layer up, not opened */
+#define CHAPSS_PENDING 2 /* Auth peer when lower up */
+#define CHAPSS_INITIAL_CHAL 3 /* We've sent the first challenge */
+#define CHAPSS_OPEN 4 /* We've sent a Success msg */
+#define CHAPSS_RECHALLENGE 5 /* We've sent another challenge */
+#define CHAPSS_BADAUTH 6 /* We've sent a Failure msg */
+
+#define CHAPSS__LIST \
+ "Initial", "Closed", "Pending", "InitialChal", \
+ "Open", "Rechallenge", "BadAuth"
+
+/*
+ * Timeouts.
+ */
+#define CHAP_DEFTIMEOUT 3 /* Timeout time in seconds */
+#define CHAP_DEFTRANSMITS 10 /* max # times to send challenge */
+
+extern chap_state chap[];
+
+void ChapAuthWithPeer __P((int, char *, int));
+void ChapAuthPeer __P((int, char *, int));
+
+extern struct protent chap_protent;
+
+#endif /* __CHAP_INCLUDE__ */
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppd/chap_ms.c b/usr/src/cmd/cmd-inet/usr.bin/pppd/chap_ms.c
new file mode 100644
index 0000000000..7cd7e0a3a7
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppd/chap_ms.c
@@ -0,0 +1,636 @@
+/*
+ * chap_ms.c - Microsoft MS-CHAP compatible implementation.
+ *
+ * Copyright (c) 2000 by Sun Microsystems, Inc.
+ * All rights reserved.
+ *
+ * Copyright (c) 1995 Eric Rosenquist, Strata Software Limited.
+ * http://www.strataware.com/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Eric Rosenquist. The name of the author may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/*
+ * This module implements MS-CHAPv1 (RFC 2433) and MS-CHAPv2 (RFC 2759).
+ *
+ * Modifications by Lauri Pesonen / lpesonen@clinet.fi, april 1997
+ *
+ * Implemented LANManager type password response to MS-CHAP challenges.
+ * Now pppd provides both NT style and LANMan style blocks, and the
+ * prefered is set by option "ms-lanman". Default is to use NT.
+ * The hash text (StdText) was taken from Win95 RASAPI32.DLL.
+ *
+ * You should also use DOMAIN\\USERNAME as described in README.MSCHAP80
+ *
+ * Modifications by James Carlson / james.d.carlson@sun.com, June 1st, 2000.
+ *
+ * Added MS-CHAPv2 support.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+#define RCSID "$Id: chap_ms.c,v 1.15 1999/08/13 06:46:12 paulus Exp $"
+
+#if defined(CHAPMS) || defined(CHAPMSV2)
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+#ifdef HAVE_CRYPT_H
+#include <crypt.h>
+#endif
+
+#ifdef CHAPMSV2
+#include "sha1.h"
+#endif
+
+#ifndef USE_CRYPT
+#include <des.h>
+#endif
+
+#include "pppd.h"
+#include "chap.h"
+#include "chap_ms.h"
+#include "md4.h"
+
+#if !defined(lint) && !defined(_lint)
+static const char rcsid[] = RCSID;
+#endif
+
+typedef struct {
+ u_char LANManResp[24];
+ u_char NTResp[24];
+ u_char UseNT; /* If 1, ignore the LANMan response field */
+} MS_ChapResponse;
+/* We use MS_CHAP_RESPONSE_LEN, rather than sizeof(MS_ChapResponse),
+ in case this struct gets padded. */
+
+typedef struct {
+ u_char PeerChallenge[16];
+ u_char MustBeZero[8];
+ u_char NTResp[24];
+ u_char Flags; /* Should be zero (Win98 sends 04) */
+} MS_Chapv2Response;
+/* We use MS_CHAPV2_RESPONSE_LEN, rather than sizeof(MS_Chapv2Response),
+ in case this struct gets padded. */
+
+static void ChallengeResponse __P((u_char *, u_char *, u_char *));
+static void DesEncrypt __P((u_char *, u_char *, u_char *));
+static void MakeKey __P((u_char *, u_char *));
+static u_char Get7Bits __P((u_char *, int));
+#ifdef CHAPMS
+static void ChapMS_NT __P((u_char *, char *, int, MS_ChapResponse *));
+#ifdef MSLANMAN
+static void ChapMS_LANMan __P((u_char *, char *, int, MS_ChapResponse *));
+#endif
+#endif
+#ifdef CHAPMSV2
+static void ChapMSv2_NT __P((char *, u_char *, char *, int,
+ MS_Chapv2Response *));
+#endif
+
+#ifdef USE_CRYPT
+static void Expand __P((u_char *, char *));
+static void Collapse __P((char *, u_char *));
+#endif
+
+#if defined(MSLANMAN) && defined(CHAPMS)
+bool ms_lanman = 0; /* Use LanMan password instead of NT */
+ /* Has meaning only with MS-CHAP challenges */
+#endif
+
+#ifdef CHAPMSV2
+/* Specially-formatted Microsoft CHAP response message. */
+static char status_message[256];
+#endif
+
+static void
+ChallengeResponse(challenge, pwHash, response)
+ u_char *challenge; /* IN 8 octets */
+ u_char *pwHash; /* IN 16 octets */
+ u_char *response; /* OUT 24 octets */
+{
+ u_char ZPasswordHash[21];
+
+ BZERO(ZPasswordHash, sizeof(ZPasswordHash));
+ BCOPY(pwHash, ZPasswordHash, MD4_SIGNATURE_SIZE);
+
+#if 0
+ dbglog("ChallengeResponse - ZPasswordHash %.*B",
+ sizeof(ZPasswordHash), ZPasswordHash);
+#endif
+
+ DesEncrypt(challenge, ZPasswordHash + 0, response + 0);
+ DesEncrypt(challenge, ZPasswordHash + 7, response + 8);
+ DesEncrypt(challenge, ZPasswordHash + 14, response + 16);
+
+#if 0
+ dbglog("ChallengeResponse - response %.24B", response);
+#endif
+}
+
+
+#ifdef USE_CRYPT
+static void
+DesEncrypt(clear, key, cipher)
+ u_char *clear; /* IN 8 octets */
+ u_char *key; /* IN 7 octets */
+ u_char *cipher; /* OUT 8 octets */
+{
+ u_char des_key[8];
+ char crypt_key[66];
+ char des_input[66];
+
+ MakeKey(key, des_key);
+
+ Expand(des_key, crypt_key);
+ setkey(crypt_key);
+
+#if 0
+ CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet input : %.8B", clear));
+#endif
+
+ Expand(clear, des_input);
+ encrypt(des_input, 0);
+ Collapse(des_input, cipher);
+
+#if 0
+ CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet output: %.8B", cipher));
+#endif
+}
+
+#else /* USE_CRYPT */
+
+static void
+DesEncrypt(clear, key, cipher)
+ u_char *clear; /* IN 8 octets */
+ u_char *key; /* IN 7 octets */
+ u_char *cipher; /* OUT 8 octets */
+{
+ des_cblock des_key;
+ des_key_schedule key_schedule;
+
+ MakeKey(key, des_key);
+
+ des_set_key(&des_key, key_schedule);
+
+#if 0
+ CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet input : %.8B", clear));
+#endif
+
+ des_ecb_encrypt((des_cblock *)clear, (des_cblock *)cipher, key_schedule, 1);
+
+#if 0
+ CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet output: %.8B", cipher));
+#endif
+}
+
+#endif /* USE_CRYPT */
+
+
+static u_char Get7Bits(input, startBit)
+ u_char *input;
+ int startBit;
+{
+ register unsigned int word;
+
+ word = (unsigned)input[startBit / 8] << 8;
+ word |= (unsigned)input[startBit / 8 + 1];
+
+ word >>= 15 - (startBit % 8 + 7);
+
+ return word & 0xFE;
+}
+
+#ifdef USE_CRYPT
+
+/* in == 8-byte string (expanded version of the 56-bit key)
+ * out == 64-byte string where each byte is either 1 or 0
+ * Note that the low-order "bit" is always ignored by by setkey()
+ */
+static void Expand(in, out)
+ u_char *in;
+ char *out;
+{
+ int j, c;
+ int i;
+
+ for(i = 0; i < 64; in++){
+ c = *in;
+ for(j = 7; j >= 0; j--)
+ *out++ = (c >> j) & 01;
+ i += 8;
+ }
+}
+
+/* The inverse of Expand
+ */
+static void Collapse(in, out)
+ char *in;
+ u_char *out;
+{
+ int j;
+ int i;
+ unsigned int c;
+
+ for (i = 0; i < 64; i += 8, out++) {
+ c = 0;
+ for (j = 7; j >= 0; j--, in++)
+ c |= *(u_char *)in << j;
+ *out = c & 0xff;
+ }
+}
+#endif
+
+static void MakeKey(key, des_key)
+ u_char *key; /* IN 56 bit DES key missing parity bits */
+ u_char *des_key; /* OUT 64 bit DES key with parity bits added */
+{
+ des_key[0] = Get7Bits(key, 0);
+ des_key[1] = Get7Bits(key, 7);
+ des_key[2] = Get7Bits(key, 14);
+ des_key[3] = Get7Bits(key, 21);
+ des_key[4] = Get7Bits(key, 28);
+ des_key[5] = Get7Bits(key, 35);
+ des_key[6] = Get7Bits(key, 42);
+ des_key[7] = Get7Bits(key, 49);
+
+#ifndef USE_CRYPT
+ des_set_odd_parity((des_cblock *)des_key);
+#endif
+
+#if 0
+ CHAPDEBUG((LOG_INFO, "MakeKey: 56-bit input : %.7B", key));
+ CHAPDEBUG((LOG_INFO, "MakeKey: 64-bit output: %.8B", des_key));
+#endif
+}
+
+#ifdef CHAPMS
+static void
+ChapMS_NT(rchallenge, secret, secret_len, response)
+ u_char *rchallenge;
+ char *secret;
+ int secret_len;
+ MS_ChapResponse *response;
+{
+ int i;
+#ifdef __NetBSD__
+ /* NetBSD uses the libc md4 routines which take bytes instead of bits */
+ int mdlen = secret_len * 2;
+#else
+ int mdlen = secret_len * 2 * 8;
+#endif
+ MD4_CTX md4Context;
+ u_char hash[MD4_SIGNATURE_SIZE];
+ u_char unicodePassword[MAX_NT_PASSWORD * 2];
+
+ /* Initialize the Unicode version of the secret (== password). */
+ /* This implicitly supports 8-bit ISO8859/1 characters. */
+ BZERO(unicodePassword, sizeof(unicodePassword));
+ for (i = 0; i < secret_len; i++)
+ unicodePassword[i * 2] = (u_char)secret[i];
+
+ MD4Init(&md4Context);
+ MD4Update(&md4Context, unicodePassword, mdlen);
+
+ MD4Final(hash, &md4Context); /* Tell MD4 we're done */
+
+ ChallengeResponse(rchallenge, hash, response->NTResp);
+}
+
+#ifdef MSLANMAN
+static u_char *StdText = (u_char *)"KGS!@#$%"; /* key from rasapi32.dll */
+
+static void
+ChapMS_LANMan(rchallenge, secret, secret_len, response)
+ u_char *rchallenge;
+ char *secret;
+ int secret_len;
+ MS_ChapResponse *response;
+{
+ int i;
+ u_char UcasePassword[MAX_NT_PASSWORD]; /* max is actually 14 */
+ u_char PasswordHash[MD4_SIGNATURE_SIZE];
+
+ /* LANMan password is case insensitive */
+ BZERO(UcasePassword, sizeof(UcasePassword));
+ for (i = 0; i < secret_len; i++)
+ UcasePassword[i] = (u_char)(
+ islower(secret[i]) ? toupper(secret[i]) : secret[i]);
+ DesEncrypt( StdText, UcasePassword + 0, PasswordHash + 0 );
+ DesEncrypt( StdText, UcasePassword + 7, PasswordHash + 8 );
+ ChallengeResponse(rchallenge, PasswordHash, response->LANManResp);
+}
+#endif
+
+void
+ChapMS(cstate, rchallenge, rchallenge_len, secret, secret_len)
+ chap_state *cstate;
+ u_char *rchallenge;
+ int rchallenge_len;
+ char *secret;
+ int secret_len;
+{
+ MS_ChapResponse response;
+
+ if (rchallenge_len < 8) {
+ cstate->resp_length = 0;
+ return;
+ }
+
+#if 0
+ CHAPDEBUG((LOG_INFO, "ChapMS: secret is '%.*s'", secret_len, secret));
+#endif
+ BZERO(&response, sizeof(response));
+
+ /* Calculate both always */
+ ChapMS_NT(rchallenge, secret, secret_len, &response);
+
+#ifdef MSLANMAN
+ ChapMS_LANMan(rchallenge, secret, secret_len, &response);
+
+ /* prefered method is set by option */
+ response.UseNT = !ms_lanman;
+#else
+ response.UseNT = 1;
+#endif
+
+ BCOPY(&response, cstate->response, MS_CHAP_RESPONSE_LEN);
+ cstate->resp_length = MS_CHAP_RESPONSE_LEN;
+}
+
+static int
+ChapMSStatus(cstate, flag)
+ chap_state *cstate;
+ int flag;
+{
+ if (flag != 0) {
+ cstate->stat_message = NULL;
+ cstate->stat_length = 0;
+ } else {
+ cstate->stat_message = "E=691 R=0 M=\"Authentication failed\"";
+ cstate->stat_length = strlen(cstate->stat_message);
+ }
+ return (flag);
+}
+
+int
+ChapMSValidate(cstate, response, response_len, secret, secret_len)
+ chap_state *cstate;
+ u_char *response;
+ int response_len;
+ char *secret;
+ int secret_len;
+{
+ MS_ChapResponse ckresp;
+
+ if (response_len < MS_CHAP_RESPONSE_LEN || cstate->chal_len < 8)
+ return (0);
+
+ BZERO(&ckresp, sizeof(ckresp));
+
+ if (response[MS_CHAP_RESPONSE_LEN-1]) {
+ ChapMS_NT(cstate->challenge, secret, secret_len, &ckresp);
+ return (ChapMSStatus(cstate, memcmp(ckresp.NTResp, response+24,
+ 24) == 0));
+ }
+
+#ifdef MSLANMAN
+ ChapMS_LANMan(cstate->challenge, secret, secret_len, &ckresp);
+ return (ChapMSStatus(cstate,
+ memcmp(ckresp.LANManResp, response, 24) == 0));
+#else
+ return (ChapMSStatus(cstate, 0));
+#endif
+}
+#endif /* CHAPMS */
+
+#ifdef CHAPMSV2
+static void
+ChallengeHash(peerchallenge, authenticatorchallenge, username, challenge)
+u_char *peerchallenge, *authenticatorchallenge, *challenge;
+char *username;
+{
+ uint8_t digest[20];
+ SHA1_CTX sha1Context;
+ char *cp;
+
+ SHA1Init(&sha1Context);
+ SHA1Update(&sha1Context, peerchallenge, 16);
+ SHA1Update(&sha1Context, authenticatorchallenge, 16);
+
+ /*
+ * Only the user name (as presented by the peer and
+ * excluding any prepended domain name)
+ * is used as input to SHAUpdate().
+ */
+ if ((cp = strchr(username,'\\')) != NULL)
+ username = cp;
+
+ SHA1Update(&sha1Context, (uint8_t *)username, strlen(username));
+ SHA1Final(digest, &sha1Context);
+ BCOPY(digest, challenge, 8);
+}
+
+static void
+ChapMSv2_NT(username, rchallenge, secret, secret_len, response)
+ char *username;
+ u_char *rchallenge;
+ char *secret;
+ int secret_len;
+ MS_Chapv2Response *response;
+{
+ int i;
+#ifdef __NetBSD__
+ /* NetBSD uses the libc md4 routines that take bytes instead of bits */
+ int mdlen = secret_len * 2;
+#else
+ int mdlen = secret_len * 2 * 8;
+#endif
+ MD4_CTX md4Context;
+ u_char hash[MD4_SIGNATURE_SIZE];
+ u_char challenge[8];
+ u_char unicodePassword[MAX_NT_PASSWORD * 2];
+
+ /* Initialize the Unicode version of the secret (== password). */
+ /* This implicitly supports 8-bit ISO8859/1 characters. */
+ BZERO(unicodePassword, sizeof(unicodePassword));
+ for (i = 0; i < secret_len && i < MAX_NT_PASSWORD; i++)
+ if ((unicodePassword[i * 2] = (u_char)secret[i]) == '\0')
+ break;
+
+ ChallengeHash(response->PeerChallenge, rchallenge, username, challenge);
+
+ MD4Init(&md4Context);
+ MD4Update(&md4Context, unicodePassword, mdlen);
+
+ MD4Final(hash, &md4Context); /* Tell MD4 we're done */
+
+ ChallengeResponse(challenge, hash, response->NTResp);
+}
+
+void
+ChapMSv2(cstate, rchallenge, rchallenge_len, secret, secret_len)
+ chap_state *cstate;
+ u_char *rchallenge;
+ int rchallenge_len;
+ char *secret;
+ int secret_len;
+{
+ MS_Chapv2Response response;
+ u_char *ptr;
+ int i;
+
+ if (rchallenge_len < 8) {
+ cstate->resp_length = 0;
+ return;
+ }
+
+ BZERO(&response, sizeof(response));
+
+ ptr = response.PeerChallenge;
+ for (i = 0; i < 16; i++)
+ *ptr++ = (u_char) (drand48() * 0xff);
+
+ ChapMSv2_NT(cstate->resp_name, rchallenge, secret, secret_len, &response);
+
+ BCOPY(&response, cstate->response, MS_CHAPV2_RESPONSE_LEN);
+ cstate->resp_length = MS_CHAPV2_RESPONSE_LEN;
+}
+
+static void
+ChapMSv2Success(cstate, msresp, authchall, rhostname, secret, secret_len)
+ chap_state *cstate;
+ MS_Chapv2Response *msresp;
+ u_char *authchall;
+ char *rhostname, *secret;
+ int secret_len;
+{
+ static const u_char Magic1[39] = "Magic server to client signing constant";
+ static const u_char Magic2[41] =
+ "Pad to make it do more than one iteration";
+#ifdef __NetBSD__
+ /* NetBSD uses the libc md4 routines that take bytes instead of bits */
+ int mdlen = 1;
+#else
+ int mdlen = 8;
+#endif
+ u_char unicodePassword[MAX_NT_PASSWORD * 2];
+ MD4_CTX md4Context;
+ u_char hash[MD4_SIGNATURE_SIZE];
+ u_char hashhash[MD4_SIGNATURE_SIZE];
+ SHA1_CTX sha1Context;
+ uint8_t digest[20];
+ u_char challenge[8];
+ char *cp;
+ static const char hexdig[] = "0123456789ABCDEF";
+ int i;
+
+ /* Initialize the Unicode version of the secret (== password). */
+ /* This implicitly supports 8-bit ISO8859/1 characters. */
+ BZERO(unicodePassword, sizeof(unicodePassword));
+ for (i = 0; i < secret_len && i < MAX_NT_PASSWORD; i++)
+ if ((unicodePassword[i * 2] = (u_char)secret[i]) == '\0')
+ break;
+
+ /* Hash the password with MD4 */
+ MD4Init(&md4Context);
+ MD4Update(&md4Context, unicodePassword, secret_len * 2 * mdlen);
+ MD4Final(hash, &md4Context);
+
+ /* Now hash the hash */
+ MD4Init(&md4Context);
+ MD4Update(&md4Context, hash, MD4_SIGNATURE_SIZE * mdlen);
+ MD4Final(hashhash, &md4Context);
+
+ SHA1Init(&sha1Context);
+ SHA1Update(&sha1Context, hashhash, MD4_SIGNATURE_SIZE);
+ SHA1Update(&sha1Context, msresp->NTResp, sizeof (msresp->NTResp));
+ SHA1Update(&sha1Context, Magic1, 39);
+ SHA1Final(digest, &sha1Context);
+
+ ChallengeHash(msresp->PeerChallenge, authchall, rhostname, challenge);
+
+ SHA1Init(&sha1Context);
+ SHA1Update(&sha1Context, digest, 20);
+ SHA1Update(&sha1Context, challenge, 8);
+ SHA1Update(&sha1Context, Magic2, 41);
+ SHA1Final(digest, &sha1Context);
+
+ cp = status_message;
+ *cp++ = 'S';
+ *cp++ = '=';
+ for (i = 0; i < 20; i++) {
+ *cp++ = hexdig[digest[i]>>4];
+ *cp++ = hexdig[digest[i]&15];
+ }
+ /*
+ * RFC 2759 says that a M=<string> greeting message is possible
+ * here. It lies. Any such greeting causes Windoze-98 to give
+ * error number 742, "Dial-Up Networking was unable to complete
+ * the connection. The computer you're dialing in to does not
+ * support the data encryption requirements specified. Please
+ * check your encryption settings in the properties of the
+ * connection. If this problem persists, contact your network
+ * administrator."
+ */
+ *cp = '\0';
+#if 0
+ slprintf(cp, sizeof (status_message) - (cp-status_message),
+ "M=\"Welcome to %s.\"", hostname);
+#endif
+ cstate->stat_message = status_message;
+ cstate->stat_length = strlen(status_message);
+}
+
+int
+ChapMSv2Validate(cstate, rhostname, response, response_len, secret, secret_len)
+ chap_state *cstate;
+ char *rhostname;
+ u_char *response;
+ int response_len;
+ char *secret;
+ int secret_len;
+{
+ MS_Chapv2Response ckresp;
+
+ if (response_len < MS_CHAPV2_RESPONSE_LEN ||
+ /* response[MS_CHAPV2_RESPONSE_LEN-1] != 0 || */cstate->chal_len < 8) {
+ cstate->stat_message = NULL;
+ cstate->stat_length = 0;
+ return 0;
+ }
+
+ BZERO(&ckresp, sizeof(ckresp));
+
+ BCOPY(response, ckresp.PeerChallenge, 16);
+
+ ChapMSv2_NT(rhostname, cstate->challenge, secret, secret_len, &ckresp);
+ if (memcmp(ckresp.NTResp, response+24, 24) != 0) {
+ cstate->stat_message = "E=691 R=0 C=11111111111111111111111111111111 V=3 M=\"Authentication failed\"";
+ cstate->stat_length = strlen(cstate->stat_message);
+ return (0);
+ }
+ ChapMSv2Success(cstate, (MS_Chapv2Response *)response, cstate->challenge,
+ rhostname, secret, secret_len);
+ return (1);
+}
+#endif /* CHAPMSV2 */
+
+#endif /* CHAPMS or CHAPMSV2 */
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppd/chap_ms.h b/usr/src/cmd/cmd-inet/usr.bin/pppd/chap_ms.h
new file mode 100644
index 0000000000..ecc439694e
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppd/chap_ms.h
@@ -0,0 +1,43 @@
+/*
+ * chap_ms.h - Microsoft CHAP definitions.
+ *
+ * Copyright (c) 2000 by Sun Microsystems, Inc.
+ * All rights reserved.
+ *
+ * Copyright (c) 1995 Eric Rosenquist, Strata Software Limited.
+ * http://www.strataware.com/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Eric Rosenquist. The name of the author may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: chap_ms.h,v 1.2 1997/11/27 06:08:10 paulus Exp $
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifndef __CHAPMS_INCLUDE__
+
+#define MD4_SIGNATURE_SIZE 16 /* 16 bytes in a MD4 message digest */
+#define MAX_NT_PASSWORD 256 /* Maximum number of (Unicode) chars in an NT password */
+
+void ChapMS __P((chap_state *, u_char *, int, char *, int));
+void ChapMSv2 __P((chap_state *, u_char *, int, char *, int));
+int ChapMSValidate __P((chap_state *cstate, u_char *response, int response_len,
+ char *secret, int secret_len));
+int ChapMSv2Validate __P((chap_state *cstate, char *rhostname,
+ u_char *response, int response_len, char *secret, int secret_len));
+
+#define __CHAPMS_INCLUDE__
+#endif /* __CHAPMS_INCLUDE__ */
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppd/demand.c b/usr/src/cmd/cmd-inet/usr.bin/pppd/demand.c
new file mode 100644
index 0000000000..4c007175e0
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppd/demand.c
@@ -0,0 +1,378 @@
+/*
+ * demand.c - Support routines for demand-dialling.
+ *
+ * Copyright 2000-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 1993 The Australian National University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Australian National University. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+#define RCSID "$Id: demand.c,v 1.13 2000/04/15 01:27:11 masputra Exp $"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#ifdef PPP_FILTER
+#include <net/if.h>
+#include <net/bpf.h>
+#include <pcap.h>
+#endif
+
+#include "pppd.h"
+#include "fsm.h"
+#include "lcp.h"
+
+#if !defined(lint) && !defined(_lint)
+static const char rcsid[] = RCSID;
+#endif
+
+static char *frame;
+static int framelen;
+static int framemax;
+static int escape_flag;
+static int flush_flag;
+static int fcs;
+
+struct packet {
+ int length;
+ struct packet *next;
+ unsigned char data[1];
+};
+
+static struct packet *pend_q;
+static struct packet *pend_qtail;
+
+static int active_packet __P((unsigned char *, int));
+
+/*
+ * demand_conf - configure the interface for doing dial-on-demand.
+ */
+void
+demand_conf()
+{
+ int i;
+ struct protent *protp;
+ int mtu;
+
+ framemax = lcp_wantoptions[0].mru;
+ if (framemax < PPP_MRU)
+ framemax = PPP_MRU;
+ framemax += PPP_HDRLEN + PPP_FCSLEN;
+ frame = malloc(framemax);
+ if (frame == NULL)
+ novm("demand frame");
+ framelen = 0;
+ pend_q = NULL;
+ escape_flag = 0;
+ flush_flag = 0;
+ fcs = PPP_INITFCS;
+
+ if ((mtu = lcp_allowoptions[0].mru) == 0)
+ mtu = PPP_MTU;
+ ppp_send_config(0, mtu, (u_int32_t) 0, 0, 0);
+ ppp_recv_config(0, framemax, (u_int32_t) 0, 0, 0);
+
+#ifdef PPP_FILTER
+ set_filters(&pass_filter, &active_filter);
+#endif
+
+ /*
+ * Call the demand_conf procedure for each protocol that's got one.
+ */
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ if (protp->enabled_flag && protp->demand_conf != NULL)
+ if (!((*protp->demand_conf)(0)))
+ fatal("unable to set demand configuration on %s", protp->name);
+}
+
+
+/*
+ * demand_block - set each network protocol to block further packets.
+ */
+void
+demand_block()
+{
+ int i;
+ struct protent *protp;
+
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ if (protp->enabled_flag && protp->demand_conf != NULL &&
+ !sifnpmode(0, protp->protocol & ~0x8000, NPMODE_QUEUE))
+ fatal("unable to enable queuing for %s", protp->name);
+ /* Intentionally discard return value; we're on our way up now. */
+ (void) get_loop_output();
+}
+
+/*
+ * demand_discard - set each network protocol to discard packets
+ * with an error.
+ */
+void
+demand_discard()
+{
+ struct packet *pkt, *nextpkt;
+ int i;
+ struct protent *protp;
+
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ if (protp->enabled_flag && protp->demand_conf != NULL &&
+ !sifnpmode(0, protp->protocol & ~0x8000, NPMODE_DROP))
+ fatal("unable to disable %s", protp->name);
+
+ /* Intentionally discard return value; we're on our way down now. */
+ (void) get_loop_output();
+
+ /* discard all saved packets */
+ for (pkt = pend_q; pkt != NULL; pkt = nextpkt) {
+ nextpkt = pkt->next;
+ free(pkt);
+ }
+ pend_q = NULL;
+ framelen = 0;
+ flush_flag = 0;
+ escape_flag = 0;
+ fcs = PPP_INITFCS;
+}
+
+/*
+ * demand_unblock - set each enabled network protocol to pass packets.
+ */
+void
+demand_unblock()
+{
+ int i;
+ struct protent *protp;
+
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ if (protp->enabled_flag && protp->demand_conf != NULL &&
+ !sifnpmode(0, protp->protocol & ~0x8000, NPMODE_PASS))
+ fatal("unable to enable %s", protp->name);
+}
+
+/*
+ * FCS lookup table as calculated by genfcstab.
+ */
+static u_short fcstab[256] = {
+ 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
+ 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
+ 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
+ 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
+ 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
+ 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
+ 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
+ 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
+ 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
+ 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
+ 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
+ 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
+ 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
+ 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
+ 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
+ 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
+ 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
+ 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
+ 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
+ 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
+ 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
+ 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
+ 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
+ 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
+ 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
+ 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
+ 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
+ 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
+ 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
+ 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
+ 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
+ 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
+};
+
+/*
+ * loop_chars - process characters received from the loopback.
+ * Calls loop_frame when a complete frame has been accumulated.
+ * Return value is 1 if we need to bring up the link, 0 otherwise.
+ */
+int
+loop_chars(p, n)
+ unsigned char *p;
+ int n;
+{
+ int c, rv;
+
+ rv = 0;
+ for (; n > 0; --n) {
+ c = *p++;
+ if (c == PPP_FLAG) {
+ if (!escape_flag && !flush_flag
+ && framelen > 2 && fcs == PPP_GOODFCS) {
+ framelen -= 2;
+ if (loop_frame((unsigned char *)frame, framelen))
+ rv = 1;
+ }
+ framelen = 0;
+ flush_flag = 0;
+ escape_flag = 0;
+ fcs = PPP_INITFCS;
+ continue;
+ }
+ if (flush_flag)
+ continue;
+ if (escape_flag) {
+ c ^= PPP_TRANS;
+ escape_flag = 0;
+ } else if (c == PPP_ESCAPE) {
+ escape_flag = 1;
+ continue;
+ }
+ if (framelen >= framemax) {
+ flush_flag = 1;
+ continue;
+ }
+ frame[framelen++] = c;
+ fcs = PPP_FCS(fcs, c);
+ }
+ return rv;
+}
+
+/*
+ * loop_frame - given a frame obtained from the loopback,
+ * decide whether to bring up the link or not, and, if we want
+ * to transmit this frame later, put it on the pending queue.
+ * Return value is 1 if we need to bring up the link, 0 otherwise.
+ * We assume that the kernel driver has already applied the
+ * pass_filter, so we won't get packets it rejected.
+ * We apply the active_filter to see if we want this packet to
+ * bring up the link.
+ */
+int
+loop_frame(frame, len)
+ unsigned char *frame;
+ int len;
+{
+ struct packet *pkt;
+
+ if (len < PPP_HDRLEN)
+ return 0;
+ if ((PPP_PROTOCOL(frame) & 0x8000) != 0)
+ return 0; /* shouldn't get any of these anyway */
+
+ /* Note - once we have pending packets, we don't drop any more. */
+ if (pend_q == NULL && !active_packet(frame, len))
+ return 0;
+
+ pkt = (struct packet *) malloc(sizeof(struct packet) + len);
+ if (pkt != NULL) {
+ pkt->length = len;
+ pkt->next = NULL;
+ (void) memcpy(pkt->data, frame, len);
+ if (pend_q == NULL)
+ pend_q = pkt;
+ else
+ pend_qtail->next = pkt;
+ pend_qtail = pkt;
+ }
+ return 1;
+}
+
+/*
+ * demand_rexmit - Resend all those frames that we got via the
+ * loopback, now that the real serial link is up.
+ */
+void
+demand_rexmit(proto)
+ int proto;
+{
+ struct packet *pkt, *prev, *nextpkt;
+
+ prev = NULL;
+ pkt = pend_q;
+ pend_q = NULL;
+ for (; pkt != NULL; pkt = nextpkt) {
+ nextpkt = pkt->next;
+ if (PPP_PROTOCOL(pkt->data) == proto) {
+ output(0, pkt->data, pkt->length);
+ free(pkt);
+ } else {
+ if (prev == NULL)
+ pend_q = pkt;
+ else
+ prev->next = pkt;
+ prev = pkt;
+ }
+ }
+ pend_qtail = prev;
+ if (prev != NULL)
+ prev->next = NULL;
+}
+
+/*
+ * Scan a packet to decide whether it is an "active" packet,
+ * that is, whether it is worth bringing up the link for.
+ */
+static int
+active_packet(p, len)
+ unsigned char *p;
+ int len;
+{
+ int proto, i;
+ struct protent *protp;
+ const char *cp;
+ char pbuf[32];
+
+ if (len < PPP_HDRLEN)
+ return 0;
+#ifdef PPP_FILTER
+ if (active_filter.bf_len != 0
+ && bpf_filter(active_filter.bf_insns, frame, len, len) == 0) {
+ dbglog("BPF identified packet as not worth bringing up the link.");
+ return 0;
+ }
+#endif
+ proto = PPP_PROTOCOL(p);
+ for (i = 0; (protp = protocols[i]) != NULL; ++i) {
+ if (protp->protocol < 0xC000 && (protp->protocol & ~0x8000) == proto) {
+ if (!protp->enabled_flag) {
+ dbglog("%s: not enabled; not bringing up link", protp->name);
+ return 0;
+ }
+ if (protp->active_pkt == NULL) {
+ dbglog("%s: no active test; bringing up link", protp->name);
+ return 1;
+ }
+ i = (*protp->active_pkt)(p, len);
+ dbglog("%s: active test; %sbringing up link", protp->name,
+ i != 0 ? "" : "not ");
+ return i;
+ }
+ }
+ if ((cp = protocol_name(proto)) == NULL) {
+ (void) slprintf(pbuf, sizeof (pbuf), "0x#X", proto);
+ cp = (const char *)pbuf;
+ }
+ dbglog("%s: unknown protocol; not bringing up link", cp);
+ return 0; /* not a supported protocol !!?? */
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppd/eui64.h b/usr/src/cmd/cmd-inet/usr.bin/pppd/eui64.h
new file mode 100644
index 0000000000..9f930e76f7
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppd/eui64.h
@@ -0,0 +1,103 @@
+/*
+ eui64.h - EUI64 routines for IPv6CP.
+ Copyright (C) 1999 Tommi Komulainen <Tommi.Komulainen@iki.fi>
+
+ Redistribution and use in source and binary forms are permitted
+ provided that the above copyright notice and this paragraph are
+ duplicated in all such forms and that any documentation,
+ advertising materials, and other materials related to such
+ distribution and use acknowledge that the software was developed
+ by Tommi Komulainen. The name of the author may not be used
+ to endorse or promote products derived from this software without
+ specific prior written permission.
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+
+
+ $Id: eui64.h,v 1.3 1999/09/30 19:56:37 masputra Exp $
+*/
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifndef __EUI64_H__
+#define __EUI64_H__
+
+#if !defined(INET6)
+#error "this file should only be included when INET6 is defined"
+#endif /* not defined(INET6) */
+
+#if defined(SOL2)
+#include <netinet/in.h>
+
+typedef union {
+ uint8_t e8[8]; /* lower 64-bit IPv6 address */
+ uint32_t e32[2]; /* lower 64-bit IPv6 address */
+} eui64_t;
+
+/*
+ * Declare the two below, since in.h only defines them when _KERNEL
+ * is declared - which shouldn't be true when dealing with user-land programs
+ */
+#define s6_addr8 _S6_un._S6_u8
+#define s6_addr32 _S6_un._S6_u32
+
+#else /* else if not defined(SOL2) */
+
+/*
+ * TODO:
+ *
+ * Maybe this should be done by processing struct in6_addr directly...
+ */
+typedef union
+{
+ u_int8_t e8[8];
+ u_int16_t e16[4];
+ u_int32_t e32[2];
+} eui64_t;
+
+#endif /* defined(SOL2) */
+
+#define eui64_iszero(e) (((e).e32[0] | (e).e32[1]) == 0)
+#define eui64_equals(e, o) (((e).e32[0] == (o).e32[0]) && \
+ ((e).e32[1] == (o).e32[1]))
+#define eui64_zero(e) ((e).e32[0] = (e).e32[1] = 0)
+
+#define eui64_copy(s, d) (void) memcpy(&(d), &(s), sizeof(eui64_t))
+
+#define eui64_magic(e) ( \
+ (e).e32[0] = magic(), \
+ (e).e32[1] = magic(), \
+ (e).e8[0] &= ~2 \
+ )
+#define eui64_magic_nz(x) do { \
+ eui64_magic(x); \
+ } while (eui64_iszero(x))
+#define eui64_magic_ne(x, y) do { \
+ eui64_magic(x); \
+ } while (eui64_equals(x, y))
+
+#define eui64_get(ll, cp) ( \
+ eui64_copy((*cp), (ll)), \
+ (cp) += sizeof(eui64_t) \
+ )
+
+#define eui64_put(ll, cp) ( \
+ eui64_copy((ll), (*cp)), \
+ (cp) += sizeof(eui64_t) \
+ )
+
+#define eui64_set32(e, l) ( \
+ (e).e32[0] = 0, \
+ (e).e32[1] = htonl(l) \
+ )
+#define eui64_setlo32(e, l) eui64_set32(e, l)
+
+/*
+ * Returns ascii representation of ID. This is at most 20 bytes long;
+ * 19 characters plus one NUL.
+ */
+char *eui64_ntoa __P((eui64_t));
+
+#endif /* __EUI64_H__ */
+
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppd/fsm.c b/usr/src/cmd/cmd-inet/usr.bin/pppd/fsm.c
new file mode 100644
index 0000000000..a9ad47660d
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppd/fsm.c
@@ -0,0 +1,914 @@
+/*
+ * fsm.c - {Link, IP} Control Protocol Finite State Machine.
+ *
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+#define RCSID "$Id: fsm.c,v 1.17 1999/08/13 06:46:12 paulus Exp $"
+
+/*
+ * TODO:
+ * Randomize fsm id on link/init.
+ * Deal with variable outgoing MTU.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#ifndef NO_DRAND48
+#include <stdlib.h>
+#endif /* NO_DRAND48 */
+
+#include "pppd.h"
+#include "fsm.h"
+
+#if !defined(lint) && !defined(_lint)
+static const char rcsid[] = RCSID;
+#endif
+
+static void fsm_timeout __P((void *));
+static void fsm_rconfreq __P((fsm *, int, u_char *, int));
+static void fsm_rconfack __P((fsm *, int, u_char *, int));
+static void fsm_rconfnakrej __P((fsm *, int, int, u_char *, int));
+static void fsm_rtermreq __P((fsm *, int, u_char *, int));
+static void fsm_rtermack __P((fsm *));
+static void fsm_rcoderej __P((fsm *, u_char *, int));
+static void fsm_sconfreq __P((fsm *, int));
+
+#define PROTO_NAME(f) ((f)->callbacks->proto_name)
+
+static int peer_mru[NUM_PPP];
+
+const char *
+fsm_state(int statenum)
+{
+ static const char *fsm_states[] = { FSM__STATES };
+ static char buf[32];
+
+ if (statenum < 0 || statenum >= Dim(fsm_states)) {
+ (void) slprintf(buf, sizeof (buf), "unknown#%d", statenum);
+ return buf;
+ }
+ return fsm_states[statenum];
+}
+
+/*
+ * fsm_init - Initialize fsm.
+ *
+ * Initialize fsm state.
+ */
+void
+fsm_init(f)
+ fsm *f;
+{
+ f->state = INITIAL;
+ f->flags = 0;
+ f->id = (uchar_t)(drand48() * 0xFF); /* Start with random id */
+ f->timeouttime = DEFTIMEOUT;
+ f->maxconfreqtransmits = DEFMAXCONFREQS;
+ f->maxtermtransmits = DEFMAXTERMREQS;
+ f->maxnakloops = DEFMAXNAKLOOPS;
+ f->term_reason_len = 0;
+}
+
+
+/*
+ * fsm_lowerup - The lower layer is up.
+ */
+void
+fsm_lowerup(f)
+ fsm *f;
+{
+ switch( f->state ){
+ case INITIAL:
+ f->state = CLOSED;
+ break;
+
+ case STARTING:
+ if( f->flags & OPT_SILENT )
+ f->state = STOPPED;
+ else {
+ /* Send an initial configure-request */
+ fsm_sconfreq(f, 0);
+ f->state = REQSENT;
+ }
+ break;
+
+ default:
+ error("%s: Up event in state %s", PROTO_NAME(f), fsm_state(f->state));
+ }
+}
+
+
+/*
+ * fsm_lowerdown - The lower layer is down.
+ *
+ * Cancel all timeouts and inform upper layers.
+ */
+void
+fsm_lowerdown(f)
+ fsm *f;
+{
+ switch( f->state ){
+ case CLOSED:
+ f->state = INITIAL;
+ break;
+
+ case STOPPED:
+ f->state = STARTING;
+ if (f->callbacks->starting != NULL)
+ (*f->callbacks->starting)(f);
+ break;
+
+ case CLOSING:
+ f->state = INITIAL;
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ break;
+
+ case STOPPING:
+ case REQSENT:
+ case ACKRCVD:
+ case ACKSENT:
+ f->state = STARTING;
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ break;
+
+ case OPENED:
+ f->state = STARTING;
+ if (f->callbacks->down != NULL)
+ (*f->callbacks->down)(f);
+ break;
+
+ default:
+ dbglog("%s: Down event in state %s", PROTO_NAME(f),
+ fsm_state(f->state));
+ }
+}
+
+
+/*
+ * fsm_open - Link is allowed to come up.
+ */
+void
+fsm_open(f)
+ fsm *f;
+{
+ switch( f->state ){
+ case INITIAL:
+ f->state = STARTING;
+ if (f->callbacks->starting != NULL)
+ (*f->callbacks->starting)(f);
+ break;
+
+ case CLOSED:
+ if( f->flags & OPT_SILENT )
+ f->state = STOPPED;
+ else {
+ /* Send an initial configure-request */
+ fsm_sconfreq(f, 0);
+ f->state = REQSENT;
+ }
+ break;
+
+ case CLOSING:
+ f->state = STOPPING;
+ /*FALLTHROUGH*/
+ case STOPPING:
+ case STOPPED:
+ case OPENED:
+ if( f->flags & OPT_RESTART ){
+ fsm_lowerdown(f);
+ fsm_lowerup(f);
+ }
+ break;
+
+ case STARTING:
+ case REQSENT:
+ case ACKRCVD:
+ case ACKSENT:
+ /* explicitly do nothing here. */
+ break;
+ }
+}
+
+
+/*
+ * fsm_close - Start closing connection.
+ *
+ * Cancel timeouts and either initiate close or possibly go directly to
+ * the CLOSED state.
+ */
+void
+fsm_close(f, reason)
+ fsm *f;
+ char *reason;
+{
+ int prevstate = f->state;
+
+ f->term_reason = reason;
+ f->term_reason_len = (reason == NULL? 0: strlen(reason));
+ switch( f->state ){
+ case STARTING:
+ f->state = INITIAL;
+ if (f->callbacks->finished != NULL)
+ (*f->callbacks->finished)(f);
+ break;
+
+ case STOPPED:
+ f->state = CLOSED;
+ break;
+
+ case STOPPING:
+ f->state = CLOSING;
+ break;
+
+ case REQSENT:
+ case ACKRCVD:
+ case ACKSENT:
+ case OPENED:
+ f->state = CLOSING;
+ if (prevstate != OPENED )
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ else if (f->callbacks->down != NULL)
+ (*f->callbacks->down)(f); /* Inform upper layers we're down */
+ /*
+ * Note that this-layer-down means "stop transmitting."
+ * This-layer-finished means "stop everything."
+ */
+
+ /* Init restart counter, send Terminate-Request */
+ f->retransmits = f->maxtermtransmits;
+ fsm_sdata(f, CODE_TERMREQ, f->reqid = ++f->id,
+ (u_char *) f->term_reason, f->term_reason_len);
+ TIMEOUT(fsm_timeout, f, f->timeouttime);
+ --f->retransmits;
+ break;
+
+ case INITIAL:
+ case CLOSED:
+ case CLOSING:
+ /* explicitly do nothing here. */
+ break;
+ }
+}
+
+
+/*
+ * fsm_timeout - Timeout expired.
+ */
+static void
+fsm_timeout(arg)
+ void *arg;
+{
+ fsm *f = (fsm *) arg;
+
+ switch (f->state) {
+ case CLOSING:
+ case STOPPING:
+ if( f->retransmits <= 0 ){
+ /*
+ * We've waited for an ack long enough. Peer probably heard us.
+ */
+ f->state = (f->state == CLOSING)? CLOSED: STOPPED;
+ if (f->callbacks->finished != NULL)
+ (*f->callbacks->finished)(f);
+ } else {
+ /* Send Terminate-Request */
+ fsm_sdata(f, CODE_TERMREQ, f->reqid = ++f->id,
+ (u_char *) f->term_reason, f->term_reason_len);
+ TIMEOUT(fsm_timeout, f, f->timeouttime);
+ --f->retransmits;
+ }
+ break;
+
+ case REQSENT:
+ case ACKRCVD:
+ case ACKSENT:
+ if (f->retransmits <= 0) {
+ warn("%s: timeout sending Config-Requests\n", PROTO_NAME(f));
+ f->state = STOPPED;
+ if (!(f->flags & OPT_PASSIVE) && f->callbacks->finished != NULL)
+ (*f->callbacks->finished)(f);
+
+ } else {
+ /* Retransmit the configure-request */
+ if (f->callbacks->retransmit != NULL)
+ (*f->callbacks->retransmit)(f);
+ fsm_sconfreq(f, 1); /* Re-send Configure-Request */
+ if( f->state == ACKRCVD )
+ f->state = REQSENT;
+ }
+ break;
+
+ default:
+ fatal("%s: Timeout event in state %s!", PROTO_NAME(f),
+ fsm_state(f->state));
+ }
+}
+
+
+/*
+ * fsm_input - Input packet.
+ */
+void
+fsm_input(f, inpacket, l)
+ fsm *f;
+ u_char *inpacket;
+ int l;
+{
+ u_char *inp;
+ u_char code, id;
+ int len;
+
+ /*
+ * Parse header (code, id and length).
+ * If packet too short, drop it.
+ */
+ inp = inpacket;
+ if (l < HEADERLEN) {
+ error("%s packet: discard; too small (%d < %d)", PROTO_NAME(f), l,
+ HEADERLEN);
+ return;
+ }
+ GETCHAR(code, inp);
+ GETCHAR(id, inp);
+ GETSHORT(len, inp);
+ if (len < HEADERLEN) {
+ error("%s packet: discard; invalid length (%d < %d)", PROTO_NAME(f),
+ len, HEADERLEN);
+ return;
+ }
+ if (len > l) {
+ error("%s packet: discard; truncated (%d > %d)", PROTO_NAME(f), len,
+ l);
+ return;
+ }
+ len -= HEADERLEN; /* subtract header length */
+
+ if (f->state == INITIAL || f->state == STARTING) {
+ dbglog("%s: discarded packet in state %s", PROTO_NAME(f),
+ fsm_state(f->state));
+ return;
+ }
+
+ /*
+ * Action depends on code.
+ */
+ switch (code) {
+ case CODE_CONFREQ:
+ fsm_rconfreq(f, id, inp, len);
+ break;
+
+ case CODE_CONFACK:
+ fsm_rconfack(f, id, inp, len);
+ break;
+
+ case CODE_CONFNAK:
+ case CODE_CONFREJ:
+ fsm_rconfnakrej(f, code, id, inp, len);
+ break;
+
+ case CODE_TERMREQ:
+ fsm_rtermreq(f, id, inp, len);
+ break;
+
+ case CODE_TERMACK:
+ fsm_rtermack(f);
+ break;
+
+ case CODE_CODEREJ:
+ fsm_rcoderej(f, inp, len);
+ break;
+
+ default:
+ if (f->callbacks->extcode == NULL ||
+ !(*f->callbacks->extcode)(f, code, id, inp, len))
+ fsm_sdata(f, CODE_CODEREJ, ++f->id, inpacket, len + HEADERLEN);
+ break;
+ }
+}
+
+
+/*
+ * fsm_rconfreq - Receive Configure-Request.
+ */
+static void
+fsm_rconfreq(f, id, inp, len)
+ fsm *f;
+ u_char id;
+ u_char *inp;
+ int len;
+{
+ int code, reject_if_disagree;
+
+ switch( f->state ){
+ case CLOSED:
+ /* Go away, we're closed */
+ fsm_sdata(f, CODE_TERMACK, id, NULL, 0);
+ return;
+
+ case CLOSING:
+ case STOPPING:
+ dbglog("%s: discarded Configure-Request in state %s", PROTO_NAME(f),
+ fsm_state(f->state));
+ return;
+
+ case OPENED:
+ /* Go down and restart negotiation */
+ if (f->callbacks->down != NULL)
+ (*f->callbacks->down)(f); /* Inform upper layers */
+ break;
+ }
+
+#ifdef DEBUG
+ if (inp >= outpacket_buf && inp < outpacket_buf+PPP_MRU+PPP_HDRLEN)
+ fatal("bad pointer");
+#endif
+
+ /*
+ * Pass the requested configuration options
+ * to protocol-specific code for checking.
+ */
+ if (f->callbacks->reqci != NULL) { /* Check CI */
+ reject_if_disagree = (f->nakloops >= f->maxnakloops);
+ code = (*f->callbacks->reqci)(f, inp, &len, reject_if_disagree);
+ } else if (len > 0)
+ code = CODE_CONFREJ; /* Reject all CI */
+ else
+ code = CODE_CONFACK;
+
+ /* Allow NCP to do fancy footwork, such as reinitializing. */
+ if (code <= 0)
+ return;
+
+ if (f->state == OPENED || f->state == STOPPED)
+ fsm_sconfreq(f, 0); /* Send initial Configure-Request */
+
+ /* send the Ack, Nak or Rej to the peer */
+ fsm_sdata(f, code, id, inp, len);
+
+ if (code == CODE_CONFACK) {
+ /* RFC 1661 event RCR+ */
+ if (f->state == ACKRCVD) {
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ f->state = OPENED;
+ if (f->callbacks->up != NULL)
+ (*f->callbacks->up)(f); /* Inform upper layers */
+ } else
+ f->state = ACKSENT;
+ f->nakloops = 0;
+
+ } else {
+ /* RFC 1661 event RCR- */
+ /* (we sent CODE_CONFNAK or CODE_CONFREJ) */
+ if (f->state != ACKRCVD)
+ f->state = REQSENT;
+ if( code == CODE_CONFNAK )
+ ++f->nakloops;
+ }
+}
+
+
+/*
+ * fsm_rconfack - Receive Configure-Ack.
+ */
+static void
+fsm_rconfack(f, id, inp, len)
+ fsm *f;
+ int id;
+ u_char *inp;
+ int len;
+{
+ if (id != f->reqid || f->seen_ack) /* Expected id? */
+ return; /* Nope, toss... */
+ if( !(f->callbacks->ackci != NULL ? (*f->callbacks->ackci)(f, inp, len):
+ (len == 0)) ){
+ /* Ack is bad - ignore it */
+ error("Received bad configure-ack: %P", inp, len);
+ return;
+ }
+ f->seen_ack = 1;
+
+ switch (f->state) {
+ case CLOSED:
+ case STOPPED:
+ fsm_sdata(f, CODE_TERMACK, id, NULL, 0);
+ break;
+
+ case REQSENT:
+ f->state = ACKRCVD;
+ f->retransmits = f->maxconfreqtransmits;
+ break;
+
+ case ACKRCVD:
+ /* Huh? an extra valid Ack? oh well... */
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ fsm_sconfreq(f, 0);
+ f->state = REQSENT;
+ break;
+
+ case ACKSENT:
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ f->state = OPENED;
+ f->retransmits = f->maxconfreqtransmits;
+ if (f->callbacks->up != NULL)
+ (*f->callbacks->up)(f); /* Inform upper layers */
+ break;
+
+ case OPENED:
+ /* Go down and restart negotiation */
+ fsm_sconfreq(f, 0); /* Send initial Configure-Request */
+ f->state = REQSENT;
+ if (f->callbacks->down != NULL)
+ (*f->callbacks->down)(f); /* Inform upper layers */
+ break;
+ }
+}
+
+
+/*
+ * fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject.
+ */
+static void
+fsm_rconfnakrej(f, code, id, inp, len)
+ fsm *f;
+ int code, id;
+ u_char *inp;
+ int len;
+{
+ int (*proc) __P((fsm *, u_char *, int));
+ int ret;
+
+ if (id != f->reqid || f->seen_ack) /* Expected id? */
+ return; /* Nope, toss... */
+ proc = (code == CODE_CONFNAK)? f->callbacks->nakci: f->callbacks->rejci;
+ if (proc == NULL || !(ret = proc(f, inp, len))) {
+ /* Nak/reject is bad - ignore it */
+ error("Received bad configure-nak/rej: %P", inp, len);
+ return;
+ }
+ f->seen_ack = 1;
+
+ switch (f->state) {
+ case CLOSED:
+ case STOPPED:
+ fsm_sdata(f, CODE_TERMACK, id, NULL, 0);
+ break;
+
+ case REQSENT:
+ case ACKSENT:
+ /* They didn't agree to what we wanted - try another request */
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ if (ret < 0)
+ f->state = STOPPED; /* kludge for stopping CCP */
+ else
+ fsm_sconfreq(f, 0); /* Send Configure-Request */
+ break;
+
+ case ACKRCVD:
+ /* Got a Nak/reject when we had already had an Ack?? oh well... */
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ fsm_sconfreq(f, 0);
+ f->state = REQSENT;
+ break;
+
+ case OPENED:
+ /* Go down and restart negotiation */
+ fsm_sconfreq(f, 0); /* Send initial Configure-Request */
+ f->state = REQSENT;
+ if (f->callbacks->down != NULL)
+ (*f->callbacks->down)(f); /* Inform upper layers */
+ break;
+ }
+}
+
+
+/*
+ * fsm_rtermreq - Receive Terminate-Req.
+ */
+static void
+fsm_rtermreq(f, id, p, len)
+ fsm *f;
+ int id;
+ u_char *p;
+ int len;
+{
+ switch (f->state) {
+ case ACKRCVD:
+ case ACKSENT:
+ f->state = REQSENT; /* Start over but keep trying */
+ break;
+
+ case OPENED:
+ if (len > 0) {
+ info("%s terminated by peer (%0.*v)", PROTO_NAME(f), len, p);
+ } else {
+ info("%s terminated by peer", PROTO_NAME(f));
+ }
+ f->state = STOPPING;
+ if (f->callbacks->down != NULL)
+ (*f->callbacks->down)(f); /* Inform upper layers */
+ f->retransmits = 0;
+ TIMEOUT(fsm_timeout, f, f->timeouttime);
+ break;
+ }
+
+ fsm_sdata(f, CODE_TERMACK, id, NULL, 0);
+}
+
+
+/*
+ * fsm_rtermack - Receive Terminate-Ack.
+ */
+static void
+fsm_rtermack(f)
+ fsm *f;
+{
+ switch (f->state) {
+ case CLOSING:
+ UNTIMEOUT(fsm_timeout, f);
+ f->state = CLOSED;
+ if (f->callbacks->finished != NULL)
+ (*f->callbacks->finished)(f);
+ break;
+ case STOPPING:
+ UNTIMEOUT(fsm_timeout, f);
+ f->state = STOPPED;
+ if (f->callbacks->finished != NULL)
+ (*f->callbacks->finished)(f);
+ break;
+
+ case ACKRCVD:
+ f->state = REQSENT;
+ break;
+
+ case OPENED:
+ fsm_sconfreq(f, 0);
+ f->state = REQSENT;
+ if (f->callbacks->down != NULL)
+ (*f->callbacks->down)(f); /* Inform upper layers */
+ break;
+ }
+}
+
+
+/*
+ * fsm_rcoderej - Receive a Code-Reject.
+ */
+static void
+fsm_rcoderej(f, inp, len)
+ fsm *f;
+ u_char *inp;
+ int len;
+{
+ u_char code, id;
+ int seriouserr;
+
+ if (len < HEADERLEN) {
+ error("%s: Code-Reject too short (%d < %d)", PROTO_NAME(f), len,
+ HEADERLEN);
+ return;
+ }
+ GETCHAR(code, inp);
+ GETCHAR(id, inp);
+ len -= 2;
+ warn("%s: Rcvd Code-Reject for %s id %d", PROTO_NAME(f),
+ code_name(code,0), id);
+
+ setbit(f->codemask, code);
+
+ /* Let the protocol know what happened. */
+ if (f->callbacks->codereject != NULL) {
+ seriouserr = (*f->callbacks->codereject)(f,code,id,inp,len);
+ } else {
+ /*
+ * By default, it's RXJ- for well-known codes and RXJ+ for
+ * unknown ones.
+ */
+ seriouserr = (code >= CODE_CONFREQ && code <= CODE_CODEREJ);
+ }
+
+ if (seriouserr) {
+ /* RXJ- -- shut down the protocol. */
+ switch (f->state) {
+ case CLOSING:
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ /*FALLTHROUGH*/
+ case CLOSED:
+ f->state = CLOSED;
+ if (f->callbacks->finished != NULL)
+ (*f->callbacks->finished)(f);
+ break;
+
+ case STOPPING:
+ case REQSENT:
+ case ACKRCVD:
+ case ACKSENT:
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ f->state = STOPPED;
+ /*FALLTHROUGH*/
+ case STOPPED:
+ if (f->callbacks->finished != NULL)
+ (*f->callbacks->finished)(f);
+ break;
+
+ case OPENED:
+ f->state = STOPPING;
+ if (f->callbacks->down != NULL)
+ (*f->callbacks->down)(f);
+
+ if (f->term_reason == NULL) {
+ f->term_reason = "unacceptable Code-Reject received";
+ f->term_reason_len = strlen(f->term_reason);
+ }
+
+ /* Init restart counter, send Terminate-Request */
+ f->retransmits = f->maxtermtransmits;
+ fsm_sdata(f, CODE_TERMREQ, f->reqid = ++f->id,
+ (u_char *) f->term_reason, f->term_reason_len);
+ TIMEOUT(fsm_timeout, f, f->timeouttime);
+ --f->retransmits;
+ break;
+
+ default:
+ fatal("state error");
+ }
+ } else {
+ /* RXJ+ -- just back up from Ack-Rcvd to Req-Sent. */
+ if (f->state == ACKRCVD)
+ f->state = REQSENT;
+ }
+}
+
+
+/*
+ * fsm_protreject - Peer doesn't speak this protocol.
+ *
+ * Treat this as a catastrophic error (RXJ-).
+ */
+void
+fsm_protreject(f)
+ fsm *f;
+{
+ switch( f->state ){
+ case CLOSING:
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ /*FALLTHROUGH*/
+ case CLOSED:
+ f->state = CLOSED;
+ if (f->callbacks->finished != NULL)
+ (*f->callbacks->finished)(f);
+ break;
+
+ case STOPPING:
+ case REQSENT:
+ case ACKRCVD:
+ case ACKSENT:
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ /*FALLTHROUGH*/
+ case STOPPED:
+ f->state = STOPPED;
+ if (f->callbacks->finished != NULL)
+ (*f->callbacks->finished)(f);
+ break;
+
+ case OPENED:
+ f->state = STOPPING;
+ if (f->callbacks->down != NULL)
+ (*f->callbacks->down)(f);
+
+ /* Init restart counter, send Terminate-Request */
+ f->retransmits = f->maxtermtransmits;
+ fsm_sdata(f, CODE_TERMREQ, f->reqid = ++f->id,
+ (u_char *) f->term_reason, f->term_reason_len);
+ TIMEOUT(fsm_timeout, f, f->timeouttime);
+ --f->retransmits;
+ break;
+
+ default:
+ dbglog("%s: Protocol-Reject in state %s", PROTO_NAME(f),
+ fsm_state(f->state));
+ }
+}
+
+
+/*
+ * fsm_sconfreq - Send a Configure-Request.
+ */
+static void
+fsm_sconfreq(f, retransmit)
+ fsm *f;
+ int retransmit;
+{
+ u_char *outp;
+ int cilen;
+
+ if( f->state != REQSENT && f->state != ACKRCVD && f->state != ACKSENT ){
+ /* Not currently negotiating - reset options */
+ if (f->callbacks->resetci != NULL)
+ (*f->callbacks->resetci)(f);
+ f->nakloops = 0;
+ }
+
+ if( !retransmit ){
+ /* New request - reset retransmission counter, use new ID */
+ f->retransmits = f->maxconfreqtransmits;
+ f->reqid = ++f->id;
+ }
+
+ f->seen_ack = 0;
+
+ /*
+ * Make up the request packet
+ */
+ outp = outpacket_buf + PPP_HDRLEN + HEADERLEN;
+ if (f->callbacks->cilen != NULL) {
+ cilen = (*f->callbacks->cilen)(f);
+ if (cilen > peer_mru[f->unit] - HEADERLEN)
+ cilen = peer_mru[f->unit] - HEADERLEN;
+ } else {
+ cilen = peer_mru[f->unit] - HEADERLEN;
+ }
+
+ if (f->callbacks->addci != NULL)
+ (*f->callbacks->addci)(f, outp, &cilen);
+ else
+ cilen = 0;
+
+ /* send the request to our peer */
+ fsm_sdata(f, CODE_CONFREQ, f->reqid, outp, cilen);
+
+ /* start the retransmit timer */
+ --f->retransmits;
+ TIMEOUT(fsm_timeout, f, f->timeouttime);
+}
+
+
+/*
+ * fsm_sdata - Send some data.
+ *
+ * Used for all packets sent to our peer by this module.
+ */
+void
+fsm_sdata(f, code, id, data, datalen)
+ fsm *f;
+ u_char code, id;
+ u_char *data;
+ int datalen;
+{
+ u_char *outp;
+ int outlen;
+
+ if (isset(f->codemask,code)) {
+ dbglog("%s: Peer has rejected %s; not sending another",
+ PROTO_NAME(f), code_name(code,0));
+ return;
+ }
+
+ /* Adjust length to be smaller than MTU */
+ outp = outpacket_buf;
+ if (datalen > peer_mru[f->unit] - HEADERLEN)
+ datalen = peer_mru[f->unit] - HEADERLEN;
+ if (datalen && data != outp + PPP_HDRLEN + HEADERLEN)
+ BCOPY(data, outp + PPP_HDRLEN + HEADERLEN, datalen);
+ outlen = datalen + HEADERLEN;
+ MAKEHEADER(outp, f->protocol);
+ PUTCHAR(code, outp);
+ PUTCHAR(id, outp);
+ PUTSHORT(outlen, outp);
+ output(f->unit, outpacket_buf, outlen + PPP_HDRLEN);
+}
+
+/*
+ * fsm_setpeermru - Set our idea of the peer's mru
+ *
+ * Used by routines in lcp.c which negotiate this value.
+ */
+void
+fsm_setpeermru(unit, mru)
+ int unit;
+ int mru;
+{
+ if (unit >= NUM_PPP) {
+ dbglog("fsm_setpeermru: unit out of bounds");
+ } else {
+ peer_mru[unit] = mru;
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppd/fsm.h b/usr/src/cmd/cmd-inet/usr.bin/pppd/fsm.h
new file mode 100644
index 0000000000..cb8e1e004b
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppd/fsm.h
@@ -0,0 +1,190 @@
+/*
+ * fsm.h - {Link, IP} Control Protocol Finite State Machine definitions.
+ *
+ * Copyright (c) 2000 by Sun Microsystems, Inc.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies.
+ *
+ * SUN MAKES NO REPRESENTATION OR WARRANTIES ABOUT THE SUITABILITY OF
+ * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+ * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
+ * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
+ * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: fsm.h,v 1.8 1999/11/15 01:51:50 paulus Exp $
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifndef __FSM_H__
+#define __FSM_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Packet header = Code, id, length.
+ */
+#define HEADERLEN 4
+
+/*
+ * Control Protocol (LCP, IPCP, etc.) message code numbers.
+ * (More in lcp.h and net/ppp-comp.h.)
+ */
+#define CODE_CONFREQ 1 /* Configuration Request */
+#define CODE_CONFACK 2 /* Configuration Ack */
+#define CODE_CONFNAK 3 /* Configuration Nak */
+#define CODE_CONFREJ 4 /* Configuration Reject */
+#define CODE_TERMREQ 5 /* Termination Request */
+#define CODE_TERMACK 6 /* Termination Ack */
+#define CODE_CODEREJ 7 /* Code Reject */
+
+
+/*
+ * Each FSM is described by an fsm structure and fsm callbacks.
+ */
+typedef struct fsm {
+ int unit; /* Interface unit number */
+ int protocol; /* Data Link Layer Protocol field value */
+ int state; /* State */
+ int flags; /* Contains option bits */
+ u_char id; /* Current id */
+ u_char reqid; /* Current request id */
+ u_char seen_ack; /* Have received valid Ack/Nak/Rej to Req */
+ int timeouttime; /* Timeout time in milliseconds */
+ int maxconfreqtransmits; /* Maximum Configure-Request transmissions */
+ int retransmits; /* Number of retransmissions left */
+ int maxtermtransmits; /* Maximum Terminate-Request transmissions */
+ int nakloops; /* Number of nak loops since last ack */
+ int maxnakloops; /* Maximum number of nak loops tolerated */
+ struct fsm_callbacks *callbacks; /* Callback routines */
+ char *term_reason; /* Reason for closing protocol */
+ int term_reason_len; /* Length of term_reason */
+ u_int32_t codemask[8]; /* Codes rejected by peer */
+} fsm;
+
+/*
+ * Function RFC 1661
+ * ----- ------------------------------
+ * ackci RCA
+ * nakci RCN
+ * rejci RCN
+ * reqci RCR+ or RCR- (by return value)
+ * up tlu (this-layer-up)
+ * down tld (this-layer-down)
+ * starting tls (this-layer-starting)
+ * finished tlf (this-layer-finished)
+ * codereject RXJ+ or RXJ- (by return value)
+ *
+ * Note that this-layer-down means "stop transmitting."
+ * This-layer-finished means "stop everything."
+ */
+
+typedef struct fsm_callbacks {
+ void (*resetci) /* Reset our Configuration Information */
+ __P((fsm *));
+ int (*cilen) /* Length of our Configuration Information */
+ __P((fsm *));
+ void (*addci) /* Add our Configuration Information */
+ __P((fsm *, u_char *, int *));
+ int (*ackci) /* ACK our Configuration Information */
+ __P((fsm *, u_char *, int));
+ int (*nakci) /* NAK our Configuration Information */
+ __P((fsm *, u_char *, int));
+ int (*rejci) /* Reject our Configuration Information */
+ __P((fsm *, u_char *, int));
+ int (*reqci) /* Request peer's Configuration Information */
+ __P((fsm *, u_char *, int *, int));
+ void (*up) /* Called when fsm reaches OPENED state */
+ __P((fsm *));
+ void (*down) /* Called when fsm leaves OPENED state */
+ __P((fsm *));
+ void (*starting) /* Called when we want the lower layer */
+ __P((fsm *));
+ void (*finished) /* Called when we don't want the lower layer */
+ __P((fsm *));
+ void (*retransmit) /* Retransmission is necessary */
+ __P((fsm *));
+ int (*extcode) /* Called when unknown code received */
+ __P((fsm *, int, int, u_char *, int));
+ char *proto_name; /* String name for protocol (for messages) */
+ int (*codereject) /* Called when Code-Reject received */
+ __P((fsm *p, int code, int id, u_char *inp, int len));
+} fsm_callbacks;
+
+
+/*
+ * RFC 1661 NCP states.
+ */
+#define INITIAL 0 /* Down, hasn't been opened */
+#define STARTING 1 /* Down, been opened */
+#define CLOSED 2 /* Up, hasn't been opened */
+#define STOPPED 3 /* Open, waiting for down event */
+#define CLOSING 4 /* Terminating the connection, not open */
+#define STOPPING 5 /* Terminating, but open */
+#define REQSENT 6 /* We've sent a Config Request */
+#define ACKRCVD 7 /* We've received a Config Ack */
+#define ACKSENT 8 /* We've sent a Config Ack */
+#define OPENED 9 /* Connection available */
+
+#define FSM__STATES \
+ "Initial", "Starting", "Closed", "Stopped", "Closing", "Stopping", \
+ "ReqSent", "AckRcvd", "AckSent", "Opened"
+
+/*
+ * Flags - indicate options controlling FSM operation
+ */
+#define OPT_PASSIVE 1 /* Don't die if we don't get a response */
+#define OPT_RESTART 2 /* Treat 2nd OPEN as DOWN, UP */
+#define OPT_SILENT 4 /* Wait for peer to speak first */
+
+
+/*
+ * Timeouts.
+ */
+#define DEFTIMEOUT 3 /* Timeout time in seconds */
+#define DEFMAXTERMREQS 2 /* Maximum Terminate-Request transmissions */
+#define DEFMAXCONFREQS 10 /* Maximum Configure-Request transmissions */
+#define DEFMAXNAKLOOPS 5 /* Maximum number of nak loops */
+
+
+/*
+ * Prototypes
+ */
+void fsm_init __P((fsm *));
+void fsm_lowerup __P((fsm *));
+void fsm_lowerdown __P((fsm *));
+void fsm_open __P((fsm *));
+void fsm_close __P((fsm *, char *));
+void fsm_input __P((fsm *, u_char *, int));
+void fsm_protreject __P((fsm *));
+void fsm_sdata __P((fsm *, int, int, u_char *, int));
+void fsm_setpeermru __P((int, int));
+
+const char *fsm_state __P((int statenum));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __FSM_H__ */
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppd/ipcp.c b/usr/src/cmd/cmd-inet/usr.bin/pppd/ipcp.c
new file mode 100644
index 0000000000..019178a4c1
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppd/ipcp.c
@@ -0,0 +1,2009 @@
+/*
+ * ipcp.c - PPP IP Control Protocol.
+ *
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+#define RCSID "$Id: ipcp.c,v 1.54 2000/04/15 01:27:11 masputra Exp $"
+
+/*
+ * TODO:
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <netdb.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#if defined(_linux_) || defined(__linux__)
+#define __FAVOR_BSD
+#endif
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+
+#include "pppd.h"
+#include "fsm.h"
+#include "ipcp.h"
+#include "pathnames.h"
+
+#if !defined(lint) && !defined(_lint)
+static const char rcsid[] = RCSID;
+#endif
+
+/* global vars */
+ipcp_options ipcp_wantoptions[NUM_PPP]; /* Options that we want to request */
+ipcp_options ipcp_gotoptions[NUM_PPP]; /* Options that peer ack'd */
+ipcp_options ipcp_allowoptions[NUM_PPP]; /* Options we allow peer to request */
+ipcp_options ipcp_hisoptions[NUM_PPP]; /* Options that we ack'd */
+
+bool ipcp_from_hostname = 0; /* Local IP address is from hostname lookup */
+
+/* Hook for a plugin to know when IP protocol has come up */
+void (*ip_up_hook) __P((void)) = NULL;
+
+/* Hook for a plugin to know when IP protocol has come down */
+void (*ip_down_hook) __P((void)) = NULL;
+
+/* local vars */
+static bool default_route_set[NUM_PPP]; /* Have set up a default route */
+static bool proxy_arp_set[NUM_PPP]; /* Have created proxy arp entry */
+static bool ipcp_is_up[NUM_PPP]; /* have called np_up() */
+static bool proxy_arp_quiet[NUM_PPP]; /* We should be quiet on error */
+static bool disable_defaultip = 0; /* Don't use hostname for IP addr */
+
+/*
+ * Callbacks for fsm code. (CI = Configuration Information)
+ */
+static void ipcp_resetci __P((fsm *)); /* Reset our CI */
+static int ipcp_cilen __P((fsm *)); /* Return length of our CI */
+static void ipcp_addci __P((fsm *, u_char *, int *)); /* Add our CI */
+static int ipcp_ackci __P((fsm *, u_char *, int)); /* Peer ack'd our CI */
+static int ipcp_nakci __P((fsm *, u_char *, int)); /* Peer nak'd our CI */
+static int ipcp_rejci __P((fsm *, u_char *, int)); /* Peer rej'd our CI */
+static int ipcp_reqci __P((fsm *, u_char *, int *, int)); /* Rcv CI */
+static void ipcp_up __P((fsm *)); /* We're UP */
+static void ipcp_down __P((fsm *)); /* We're DOWN */
+static void ipcp_finished __P((fsm *)); /* Don't need lower layer */
+static int setmsservaddr __P((char *, u_int32_t *));
+
+fsm ipcp_fsm[NUM_PPP]; /* IPCP fsm structure */
+
+static fsm_callbacks ipcp_callbacks = { /* IPCP callback routines */
+ ipcp_resetci, /* Reset our Configuration Information */
+ ipcp_cilen, /* Length of our Configuration Information */
+ ipcp_addci, /* Add our Configuration Information */
+ ipcp_ackci, /* ACK our Configuration Information */
+ ipcp_nakci, /* NAK our Configuration Information */
+ ipcp_rejci, /* Reject our Configuration Information */
+ ipcp_reqci, /* Request peer's Configuration Information */
+ ipcp_up, /* Called when fsm reaches OPENED state */
+ ipcp_down, /* Called when fsm leaves OPENED state */
+ NULL, /* Called when we want the lower layer up */
+ ipcp_finished, /* Called when we want the lower layer down */
+ NULL, /* Retransmission is necessary */
+ NULL, /* Called to handle protocol-specific codes */
+ "IPCP", /* String name of protocol */
+ NULL /* Peer rejected a code number */
+};
+
+/*
+ * Command-line options.
+ */
+static int setvjslots __P((char **));
+static int setdnsaddr __P((char **));
+static int setwinsaddr __P((char **));
+static int autoproxyarp __P((char **));
+
+static option_t ipcp_option_list[] = {
+ { "noip", o_bool, &ipcp_protent.enabled_flag,
+ "Disable IP and IPCP" },
+ { "-ip", o_bool, &ipcp_protent.enabled_flag,
+ "Disable IP and IPCP" },
+ { "novj", o_bool, &ipcp_wantoptions[0].neg_vj,
+ "Disable VJ compression", OPT_A2COPY, &ipcp_allowoptions[0].neg_vj },
+ { "-vj", o_bool, &ipcp_wantoptions[0].neg_vj,
+ "Disable VJ compression", OPT_A2COPY, &ipcp_allowoptions[0].neg_vj },
+ { "novjccomp", o_bool, &ipcp_wantoptions[0].cflag,
+ "Disable VJ connection-ID compression", OPT_A2COPY,
+ &ipcp_allowoptions[0].cflag },
+ { "-vjccomp", o_bool, &ipcp_wantoptions[0].cflag,
+ "Disable VJ connection-ID compression", OPT_A2COPY,
+ &ipcp_allowoptions[0].cflag },
+ { "vj-max-slots", o_special, (void *)setvjslots,
+ "Set maximum VJ header slots" },
+ { "ipcp-accept-local", o_bool, &ipcp_wantoptions[0].accept_local,
+ "Accept peer's address for us", 1 },
+ { "ipcp-accept-remote", o_bool, &ipcp_wantoptions[0].accept_remote,
+ "Accept peer's address for it", 1 },
+ { "ipparam", o_string, &ipparam,
+ "Set ip script parameter" },
+ { "noipdefault", o_bool, &disable_defaultip,
+ "Don't use name for default IP adrs", 1 },
+ { "ms-dns", o_special, (void *)setdnsaddr,
+ "DNS address for the peer's use" },
+ { "ms-wins", o_special, (void *)setwinsaddr,
+ "Nameserver for SMB over TCP/IP for peer" },
+ { "ipcp-restart", o_int, &ipcp_fsm[0].timeouttime,
+ "Set timeout for IPCP" },
+ { "ipcp-max-terminate", o_int, &ipcp_fsm[0].maxtermtransmits,
+ "Set max #xmits for term-reqs" },
+ { "ipcp-max-configure", o_int, &ipcp_fsm[0].maxconfreqtransmits,
+ "Set max #xmits for conf-reqs" },
+ { "ipcp-max-failure", o_int, &ipcp_fsm[0].maxnakloops,
+ "Set max #conf-naks for IPCP" },
+ { "defaultroute", o_bool, &ipcp_wantoptions[0].default_route,
+ "Add default route", OPT_ENABLE|1, &ipcp_allowoptions[0].default_route },
+ { "nodefaultroute", o_bool, &ipcp_allowoptions[0].default_route,
+ "disable defaultroute option", OPT_A2COPY,
+ &ipcp_wantoptions[0].default_route },
+ { "-defaultroute", o_bool, &ipcp_allowoptions[0].default_route,
+ "disable defaultroute option", OPT_A2COPY,
+ &ipcp_wantoptions[0].default_route },
+ { "proxyarp", o_bool, &ipcp_wantoptions[0].proxy_arp,
+ "Add proxy ARP entry", OPT_ENABLE|1, &ipcp_allowoptions[0].proxy_arp },
+ { "autoproxyarp", o_special_noarg, (void *)autoproxyarp,
+ "Add proxy ARP entry if needed", OPT_ENABLE,
+ &ipcp_allowoptions[0].proxy_arp },
+ { "noproxyarp", o_bool, &ipcp_allowoptions[0].proxy_arp,
+ "disable proxyarp option", OPT_A2COPY, &ipcp_wantoptions[0].proxy_arp },
+ { "-proxyarp", o_bool, &ipcp_allowoptions[0].proxy_arp,
+ "disable proxyarp option", OPT_A2COPY, &ipcp_wantoptions[0].proxy_arp },
+ { "usepeerdns", o_bool, &ipcp_wantoptions[0].req_dns1,
+ "Ask peer for DNS address(es)", OPT_A2COPY|1,
+ &ipcp_wantoptions[0].req_dns2 },
+ { NULL }
+};
+
+/*
+ * Protocol entry points from main code.
+ */
+static void ipcp_init __P((int));
+static void ipcp_open __P((int));
+static void ipcp_close __P((int, char *));
+static void ipcp_lowerup __P((int));
+static void ipcp_lowerdown __P((int));
+static void ipcp_input __P((int, u_char *, int));
+static void ipcp_protrej __P((int));
+static int ipcp_printpkt __P((u_char *, int,
+ void (*) __P((void *, const char *, ...)), void *));
+static void ip_check_options __P((void));
+static int ip_demand_conf __P((int));
+static int ip_active_pkt __P((u_char *, int));
+static void ipcp_print_stat __P((int, FILE *));
+
+static void create_resolv __P((u_int32_t, u_int32_t));
+
+struct protent ipcp_protent = {
+ PPP_IPCP,
+ ipcp_init,
+ ipcp_input,
+ ipcp_protrej,
+ ipcp_lowerup,
+ ipcp_lowerdown,
+ ipcp_open,
+ ipcp_close,
+ ipcp_printpkt,
+ NULL,
+ 1,
+ "IPCP",
+ "IP",
+ ipcp_option_list,
+ ip_check_options,
+ ip_demand_conf,
+ ip_active_pkt,
+ ipcp_print_stat
+};
+
+static void ipcp_clear_addrs __P((int, u_int32_t, u_int32_t));
+static void ipcp_script __P((char *)); /* Run an up/down script */
+static void ipcp_script_done __P((void *, int));
+
+/*
+ * Lengths of configuration options.
+ */
+#define CILEN_VOID 2
+#define CILEN_COMPRESS 4 /* min length for compression protocol opt. */
+#define CILEN_VJ 6 /* length for RFC1332 Van-Jacobson opt. */
+#define CILEN_ADDR 6 /* new-style single address option */
+#define CILEN_ADDRS 10 /* old-style dual address option */
+
+
+/*
+ * This state variable is used to ensure that we don't
+ * run an ipcp-up/down script while one is already running.
+ */
+static enum script_state {
+ s_down,
+ s_up
+} ipcp_script_state;
+static pid_t ipcp_script_pid;
+
+/*
+ * Make a string representation of a network IP address.
+ */
+char *
+ip_ntoa(ipaddr)
+u_int32_t ipaddr;
+{
+ static char b[64];
+
+ (void) slprintf(b, sizeof(b), "%I", ipaddr);
+ return b;
+}
+
+/*
+ * Option parsing.
+ */
+
+/*
+ * setvjslots - set maximum number of connection slots for VJ compression
+ */
+static int
+setvjslots(argv)
+ char **argv;
+{
+ int value;
+
+ if (!int_option(*argv, &value))
+ return 0;
+ if (value < 2 || value > 16) {
+ option_error("vj-max-slots value must be between 2 and 16");
+ return 0;
+ }
+ ipcp_wantoptions [0].maxslotindex =
+ ipcp_allowoptions[0].maxslotindex = value - 1;
+ return 1;
+}
+
+/*
+ * setmsservaddr - Set the primary and secondary server addresses in the
+ * array. setdnsaddr() and setwinsaddr() call this function with either
+ * dnsaddr[] or winsaddr[] as the serverarray argument.
+ */
+static int
+setmsservaddr(servname, serverarray)
+ char *servname;
+ u_int32_t *serverarray;
+{
+ u_int32_t addr;
+ struct hostent *hp = NULL;
+
+ addr = inet_addr(servname);
+ if (addr == (u_int32_t) -1) {
+ if ((hp = gethostbyname(servname)) == NULL)
+ return 0;
+ BCOPY(hp->h_addr, &addr, sizeof (u_int32_t));
+ }
+
+ /*
+ * If there is no primary then this is the first instance of the
+ * option, we must set the primary. In that case, try to set the
+ * secondary to h_addr_list[1]. If the primary is already set, then
+ * this is the second instance of the option, and we must set
+ * the secondary.
+ */
+ if (serverarray[0] == 0) {
+ serverarray[0] = addr;
+ if (hp != NULL && hp->h_addr_list[1] != NULL)
+ BCOPY(hp->h_addr_list[1], &serverarray[1], sizeof (u_int32_t));
+ else
+ serverarray[1] = addr;
+ } else {
+ serverarray[1] = addr;
+ }
+
+ return (1);
+}
+
+/*
+ * setdnsaddr - set the dns address(es)
+ */
+static int
+setdnsaddr(argv)
+ char **argv;
+{
+ if (setmsservaddr(*argv, &(ipcp_allowoptions[0].dnsaddr[0])) == 0) {
+ option_error("invalid address parameter '%s' for ms-dns option", *argv);
+ return (0);
+ }
+
+ return (1);
+}
+
+/*
+ * setwinsaddr - set the wins address(es)
+ * This is primrarly used with the Samba package under UNIX or for pointing
+ * the caller to the existing WINS server on a Windows NT platform.
+ */
+static int
+setwinsaddr(argv)
+ char **argv;
+{
+ if (setmsservaddr(*argv, &(ipcp_allowoptions[0].winsaddr[0])) == 0) {
+ option_error("invalid address parameter '%s' for ms-wins option",
+ *argv);
+ return (0);
+ }
+
+ return (1);
+}
+
+/*
+ * autoproxyarp -- enable proxy ARP but don't emit error messages if
+ * it's not actually needed.
+ */
+/*ARGSUSED*/
+static int
+autoproxyarp(argv)
+ char **argv;
+{
+ ipcp_wantoptions[0].proxy_arp = 1;
+ proxy_arp_quiet[0] = 1;
+
+ return (1);
+}
+
+
+/*
+ * ipcp_init - Initialize IPCP.
+ */
+static void
+ipcp_init(unit)
+ int unit;
+{
+ fsm *f = &ipcp_fsm[unit];
+ ipcp_options *wo = &ipcp_wantoptions[unit];
+ ipcp_options *ao = &ipcp_allowoptions[unit];
+
+ f->unit = unit;
+ f->protocol = PPP_IPCP;
+ f->callbacks = &ipcp_callbacks;
+ fsm_init(&ipcp_fsm[unit]);
+
+ BZERO(wo, sizeof(*wo));
+ BZERO(ao, sizeof(*ao));
+
+ wo->neg_addr = 1;
+ wo->neg_vj = 1;
+ wo->vj_protocol = IPCP_VJ_COMP;
+ wo->maxslotindex = MAX_STATES - 1; /* really max index */
+ wo->cflag = 1;
+
+ ao->neg_addr = 1;
+ ao->neg_vj = 1;
+ ao->maxslotindex = MAX_STATES - 1;
+ ao->cflag = 1;
+
+ /*
+ * These aren't actually negotiated. Instead, they control
+ * whether the user may use the proxyarp and defaultroute options.
+ */
+ ao->proxy_arp = 1;
+ ao->default_route = 1;
+ proxy_arp_quiet[unit] = 0;
+}
+
+
+/*
+ * ipcp_open - IPCP is allowed to come up.
+ */
+static void
+ipcp_open(unit)
+ int unit;
+{
+ fsm_open(&ipcp_fsm[unit]);
+}
+
+
+/*
+ * ipcp_close - Take IPCP down.
+ */
+static void
+ipcp_close(unit, reason)
+ int unit;
+ char *reason;
+{
+ fsm_close(&ipcp_fsm[unit], reason);
+}
+
+
+/*
+ * ipcp_lowerup - The lower layer is up.
+ */
+static void
+ipcp_lowerup(unit)
+ int unit;
+{
+ fsm_lowerup(&ipcp_fsm[unit]);
+}
+
+
+/*
+ * ipcp_lowerdown - The lower layer is down.
+ */
+static void
+ipcp_lowerdown(unit)
+ int unit;
+{
+ fsm_lowerdown(&ipcp_fsm[unit]);
+}
+
+
+/*
+ * ipcp_input - Input IPCP packet.
+ */
+static void
+ipcp_input(unit, p, len)
+ int unit;
+ u_char *p;
+ int len;
+{
+ fsm_input(&ipcp_fsm[unit], p, len);
+}
+
+
+/*
+ * ipcp_protrej - A Protocol-Reject was received for IPCP.
+ */
+static void
+ipcp_protrej(unit)
+ int unit;
+{
+ fsm_protreject(&ipcp_fsm[unit]);
+}
+
+
+/*
+ * ipcp_resetci - Reset our CI.
+ * Called by fsm_sconfreq, Send Configure Request.
+ */
+static void
+ipcp_resetci(f)
+ fsm *f;
+{
+ ipcp_options *wo = &ipcp_wantoptions[f->unit];
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+
+ wo->req_addr = wo->neg_addr && ipcp_allowoptions[f->unit].neg_addr;
+ if (wo->ouraddr == 0 || disable_defaultip)
+ wo->accept_local = 1;
+ if (wo->hisaddr == 0)
+ wo->accept_remote = 1;
+ *go = *wo;
+ if (disable_defaultip)
+ go->ouraddr = 0;
+}
+
+
+/*
+ * ipcp_cilen - Return length of our CI.
+ * Called by fsm_sconfreq, Send Configure Request.
+ */
+static int
+ipcp_cilen(f)
+ fsm *f;
+{
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+ ipcp_options *wo = &ipcp_wantoptions[f->unit];
+ ipcp_options *ho = &ipcp_hisoptions[f->unit];
+
+#define LENCIVJ(neg, old) (neg ? (old? CILEN_COMPRESS : CILEN_VJ) : 0)
+#define LENCIADDR(neg, old) (neg ? (old? CILEN_ADDRS : CILEN_ADDR) : 0)
+#define LENCIDNS(neg) (neg ? (CILEN_ADDR) : 0)
+
+ /*
+ * First see if we want to change our options to the old
+ * forms because we have received old forms from the peer.
+ */
+ if (wo->neg_addr && !go->neg_addr && !go->old_addrs) {
+ /* use the old style of address negotiation */
+ go->neg_addr = 1;
+ go->old_addrs = 1;
+ }
+ if (wo->neg_vj && !go->neg_vj && !go->old_vj) {
+ /* try an older style of VJ negotiation */
+ /* use the old style only if the peer did */
+ if (ho->neg_vj && ho->old_vj) {
+ go->neg_vj = 1;
+ go->old_vj = 1;
+ go->vj_protocol = ho->vj_protocol;
+ }
+ }
+
+ return (LENCIADDR(go->neg_addr, go->old_addrs) +
+ LENCIVJ(go->neg_vj, go->old_vj) +
+ LENCIDNS(go->req_dns1) +
+ LENCIDNS(go->req_dns2)) ;
+}
+
+
+/*
+ * ipcp_addci - Add our desired CIs to a packet.
+ * Called by fsm_sconfreq, Send Configure Request.
+ */
+static void
+ipcp_addci(f, ucp, lenp)
+ fsm *f;
+ u_char *ucp;
+ int *lenp;
+{
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+ int len = *lenp;
+
+#define ADDCIVJ(opt, neg, val, old, maxslotindex, cflag) \
+ if (neg) { \
+ int vjlen = old? CILEN_COMPRESS : CILEN_VJ; \
+ if (len >= vjlen) { \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(vjlen, ucp); \
+ PUTSHORT(val, ucp); \
+ if (!old) { \
+ PUTCHAR(maxslotindex, ucp); \
+ PUTCHAR(cflag, ucp); \
+ } \
+ len -= vjlen; \
+ } else \
+ neg = 0; \
+ }
+
+#define ADDCIADDR(opt, neg, old, val1, val2) \
+ if (neg) { \
+ int addrlen = (old? CILEN_ADDRS: CILEN_ADDR); \
+ if (len >= addrlen) { \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(addrlen, ucp); \
+ PUTNLONG(val1, ucp); \
+ if (old) { \
+ PUTNLONG(val2, ucp); \
+ } \
+ len -= addrlen; \
+ } else \
+ neg = 0; \
+ }
+
+#define ADDCIDNS(opt, neg, addr) \
+ if (neg) { \
+ if (len >= CILEN_ADDR) { \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(CILEN_ADDR, ucp); \
+ PUTNLONG(addr, ucp); \
+ len -= CILEN_ADDR; \
+ } else \
+ neg = 0; \
+ }
+
+ ADDCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), go->neg_addr,
+ go->old_addrs, go->ouraddr, go->hisaddr);
+
+ ADDCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj,
+ go->maxslotindex, go->cflag);
+
+ ADDCIDNS(CI_MS_DNS1, go->req_dns1, go->dnsaddr[0]);
+
+ ADDCIDNS(CI_MS_DNS2, go->req_dns2, go->dnsaddr[1]);
+
+ *lenp -= len;
+}
+
+
+/*
+ * ipcp_ackci - Ack our CIs.
+ * Called by fsm_rconfack, Receive Configure ACK.
+ *
+ * Returns:
+ * 0 - Ack was bad.
+ * 1 - Ack was good.
+ */
+static int
+ipcp_ackci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+ u_short cilen, citype, cishort;
+ u_int32_t cilong;
+ u_char cimaxslotindex, cicflag;
+
+ /*
+ * CIs must be in exactly the same order that we sent...
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+
+#define ACKCIVJ(opt, neg, val, old, maxslotindex, cflag) \
+ if (neg) { \
+ int vjlen = old? CILEN_COMPRESS : CILEN_VJ; \
+ if ((len -= vjlen) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != vjlen || \
+ citype != opt) \
+ goto bad; \
+ GETSHORT(cishort, p); \
+ if (cishort != val) \
+ goto bad; \
+ if (!old) { \
+ GETCHAR(cimaxslotindex, p); \
+ if (cimaxslotindex != maxslotindex) \
+ goto bad; \
+ GETCHAR(cicflag, p); \
+ if (cicflag != cflag) \
+ goto bad; \
+ } \
+ }
+
+#define ACKCIADDR(opt, neg, old, val1, val2) \
+ if (neg) { \
+ int addrlen = (old? CILEN_ADDRS: CILEN_ADDR); \
+ if ((len -= addrlen) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != addrlen || \
+ citype != opt) \
+ goto bad; \
+ GETNLONG(cilong, p); \
+ if (val1 != cilong) \
+ goto bad; \
+ if (old) { \
+ GETNLONG(cilong, p); \
+ if (val2 != cilong) \
+ goto bad; \
+ } \
+ }
+
+#define ACKCIDNS(opt, neg, addr) \
+ if (neg) { \
+ if ((len -= CILEN_ADDR) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_ADDR || citype != opt) \
+ goto bad; \
+ GETNLONG(cilong, p); \
+ if (addr != cilong) \
+ goto bad; \
+ }
+
+ ACKCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), go->neg_addr,
+ go->old_addrs, go->ouraddr, go->hisaddr);
+
+ ACKCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj,
+ go->maxslotindex, go->cflag);
+
+ ACKCIDNS(CI_MS_DNS1, go->req_dns1, go->dnsaddr[0]);
+
+ ACKCIDNS(CI_MS_DNS2, go->req_dns2, go->dnsaddr[1]);
+
+ /*
+ * If there are any remaining CIs, then this packet is bad.
+ */
+ if (len != 0)
+ goto bad;
+ return (1);
+
+bad:
+ IPCPDEBUG(("ipcp_ackci: received bad Ack!"));
+ return (0);
+}
+
+/*
+ * ipcp_nakci - Peer has sent a NAK for some of our CIs.
+ * This should not modify any state if the Nak is bad
+ * or if IPCP is in the OPENED state.
+ * Calback from fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject.
+ *
+ * Returns:
+ * 0 - Nak was bad.
+ * 1 - Nak was good.
+ */
+static int
+ipcp_nakci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+ u_char cimaxslotindex, cicflag;
+ u_char citype, cilen, *next;
+ u_short cishort;
+ u_int32_t ciaddr1, ciaddr2, cidnsaddr;
+ ipcp_options no; /* options we've seen Naks for */
+ ipcp_options try; /* options to request next time */
+
+ BZERO(&no, sizeof(no));
+ try = *go;
+
+ /*
+ * Any Nak'd CIs must be in exactly the same order that we sent.
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+#define NAKCIADDR(opt, neg, old, code) \
+ if (go->neg && \
+ len >= (cilen = (old? CILEN_ADDRS: CILEN_ADDR)) && \
+ p[1] == cilen && \
+ p[0] == opt) { \
+ len -= cilen; \
+ INCPTR(2, p); \
+ GETNLONG(ciaddr1, p); \
+ if (old) { \
+ GETNLONG(ciaddr2, p); \
+ no.old_addrs = 1; \
+ } else \
+ ciaddr2 = 0; \
+ no.neg = 1; \
+ code \
+ }
+
+#define NAKCIVJ(opt, neg, code) \
+ if (go->neg && \
+ ((cilen = p[1]) == CILEN_COMPRESS || cilen == CILEN_VJ) && \
+ len >= cilen && \
+ p[0] == opt) { \
+ len -= cilen; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ no.neg = 1; \
+ code \
+ }
+
+#define NAKCIDNS(opt, neg, code) \
+ if (go->neg && \
+ ((cilen = p[1]) == CILEN_ADDR) && \
+ len >= cilen && \
+ p[0] == opt) { \
+ len -= cilen; \
+ INCPTR(2, p); \
+ GETNLONG(cidnsaddr, p); \
+ no.neg = 1; \
+ code \
+ }
+
+ /*
+ * Accept the peer's idea of {our,his} address, if different
+ * from our idea, only if the accept_{local,remote} flag is set.
+ */
+ NAKCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), neg_addr, go->old_addrs,
+ if (go->accept_local && ciaddr1) { /* Do we know our address? */
+ try.ouraddr = ciaddr1;
+ }
+ if (go->accept_remote && ciaddr2) { /* Does he know his? */
+ try.hisaddr = ciaddr2;
+ }
+ );
+
+ /*
+ * Accept the peer's value of maxslotindex provided that it
+ * is less than what we asked for. Turn off slot-ID compression
+ * if the peer wants. Send old-style compress-type option if
+ * the peer wants.
+ */
+ NAKCIVJ(CI_COMPRESSTYPE, neg_vj,
+ if (cilen == CILEN_VJ) {
+ GETCHAR(cimaxslotindex, p);
+ GETCHAR(cicflag, p);
+ if (cishort == IPCP_VJ_COMP) {
+ try.old_vj = 0;
+ if (cimaxslotindex < go->maxslotindex)
+ try.maxslotindex = cimaxslotindex;
+ if (!cicflag)
+ try.cflag = 0;
+ } else {
+ try.neg_vj = 0;
+ }
+ } else {
+ if (cishort == IPCP_VJ_COMP || cishort == IPCP_VJ_COMP_OLD) {
+ try.old_vj = 1;
+ try.vj_protocol = cishort;
+ } else {
+ try.neg_vj = 0;
+ }
+ }
+ );
+
+ NAKCIDNS(CI_MS_DNS1, req_dns1,
+ try.dnsaddr[0] = cidnsaddr;
+ );
+
+ NAKCIDNS(CI_MS_DNS2, req_dns2,
+ try.dnsaddr[1] = cidnsaddr;
+ );
+
+ /*
+ * There may be remaining CIs, if the peer is requesting negotiation
+ * on an option that we didn't include in our request packet.
+ * If they want to negotiate about IP addresses, we comply.
+ * If they want us to ask for compression, we refuse.
+ */
+ while (len > CILEN_VOID) {
+ GETCHAR(citype, p);
+ GETCHAR(cilen, p);
+ if( (len -= cilen) < 0 )
+ goto bad;
+ next = p + cilen - 2;
+
+ switch (citype) {
+ case CI_COMPRESSTYPE:
+ if (go->neg_vj || no.neg_vj ||
+ (cilen != CILEN_VJ && cilen != CILEN_COMPRESS))
+ goto bad;
+ no.neg_vj = 1;
+ break;
+ case CI_ADDRS:
+ if ((go->neg_addr && go->old_addrs) || no.old_addrs
+ || cilen != CILEN_ADDRS)
+ goto bad;
+ try.neg_addr = 1;
+ try.old_addrs = 1;
+ GETNLONG(ciaddr1, p);
+ if (ciaddr1 && go->accept_local)
+ try.ouraddr = ciaddr1;
+ GETNLONG(ciaddr2, p);
+ if (ciaddr2 && go->accept_remote)
+ try.hisaddr = ciaddr2;
+ no.old_addrs = 1;
+ break;
+ case CI_ADDR:
+ if (go->neg_addr || no.neg_addr || cilen != CILEN_ADDR)
+ goto bad;
+ try.old_addrs = 0;
+ GETNLONG(ciaddr1, p);
+ if (ciaddr1 && go->accept_local)
+ try.ouraddr = ciaddr1;
+ if (try.ouraddr != 0)
+ try.neg_addr = 1;
+ no.neg_addr = 1;
+ break;
+ }
+ p = next;
+ }
+
+ /*
+ * OK, the Nak is good. Now we can update state.
+ * If there are any remaining options, we ignore them.
+ */
+ if (f->state != OPENED)
+ *go = try;
+
+ return 1;
+
+bad:
+ IPCPDEBUG(("ipcp_nakci: received bad Nak!"));
+ return 0;
+}
+
+
+/*
+ * ipcp_rejci - Reject some of our CIs.
+ * Callback from fsm_rconfnakrej.
+ */
+static int
+ipcp_rejci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+ u_char cimaxslotindex, ciflag, cilen;
+ u_short cishort;
+ u_int32_t cilong;
+ ipcp_options try; /* options to request next time */
+
+ try = *go;
+ /*
+ * Any Rejected CIs must be in exactly the same order that we sent.
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+#define REJCIADDR(opt, neg, old, val1, val2) \
+ if (go->neg && \
+ len >= (cilen = old? CILEN_ADDRS: CILEN_ADDR) && \
+ p[1] == cilen && \
+ p[0] == opt) { \
+ len -= cilen; \
+ INCPTR(2, p); \
+ GETNLONG(cilong, p); \
+ /* Check rejected value. */ \
+ if (cilong != val1) \
+ goto bad; \
+ if (old) { \
+ GETNLONG(cilong, p); \
+ /* Check rejected value. */ \
+ if (cilong != val2) \
+ goto bad; \
+ } \
+ try.neg = 0; \
+ }
+
+#define REJCIVJ(opt, neg, val, old, maxslot, cflag) \
+ if (go->neg && \
+ p[1] == (old? CILEN_COMPRESS : CILEN_VJ) && \
+ len >= p[1] && \
+ p[0] == opt) { \
+ len -= p[1]; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ /* Check rejected value. */ \
+ if (cishort != val) \
+ goto bad; \
+ if (!old) { \
+ GETCHAR(cimaxslotindex, p); \
+ if (cimaxslotindex != maxslot) \
+ goto bad; \
+ GETCHAR(ciflag, p); \
+ if (ciflag != cflag) \
+ goto bad; \
+ } \
+ try.neg = 0; \
+ }
+
+#define REJCIDNS(opt, neg, dnsaddr) \
+ if (go->neg && \
+ ((cilen = p[1]) == CILEN_ADDR) && \
+ len >= cilen && \
+ p[0] == opt) { \
+ len -= cilen; \
+ INCPTR(2, p); \
+ GETNLONG(cilong, p); \
+ /* Check rejected value. */ \
+ if (cilong != dnsaddr) \
+ goto bad; \
+ try.neg = 0; \
+ }
+
+
+ REJCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), neg_addr,
+ go->old_addrs, go->ouraddr, go->hisaddr);
+
+ REJCIVJ(CI_COMPRESSTYPE, neg_vj, go->vj_protocol, go->old_vj,
+ go->maxslotindex, go->cflag);
+
+ REJCIDNS(CI_MS_DNS1, req_dns1, go->dnsaddr[0]);
+
+ REJCIDNS(CI_MS_DNS2, req_dns2, go->dnsaddr[1]);
+
+ /*
+ * If there are any remaining CIs, then this packet is bad.
+ */
+ if (len != 0)
+ goto bad;
+ /*
+ * Now we can update state.
+ */
+ if (f->state != OPENED)
+ *go = try;
+ return 1;
+
+bad:
+ IPCPDEBUG(("ipcp_rejci: received bad Reject!"));
+ return 0;
+}
+
+
+/*
+ * ipcp_reqci - Check the peer's requested CIs and send appropriate response.
+ * Callback from fsm_rconfreq, Receive Configure Request
+ *
+ * Returns: CODE_CONFACK, CODE_CONFNAK or CODE_CONFREJ and input
+ * packet modified appropriately. If reject_if_disagree is non-zero,
+ * doesn't return CODE_CONFNAK; returns CODE_CONFREJ if it can't
+ * return CODE_CONFACK.
+ */
+static int
+ipcp_reqci(f, p, lenp, dont_nak)
+ fsm *f;
+ u_char *p; /* Requested CIs */
+ int *lenp; /* Length of requested CIs */
+ bool dont_nak;
+{
+ ipcp_options *wo = &ipcp_wantoptions[f->unit];
+ ipcp_options *ho = &ipcp_hisoptions[f->unit];
+ ipcp_options *ao = &ipcp_allowoptions[f->unit];
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+ int ret, newret;
+ u_char *p0, *nakp, *rejp, *prev;
+ u_short cishort;
+ int len, cilen, type;
+ u_int32_t tl, ciaddr1, ciaddr2; /* Parsed address values */
+ u_char maxslotindex, cflag;
+ int d;
+
+ ret = CODE_CONFACK;
+ rejp = p0 = p;
+ nakp = nak_buffer;
+
+ /*
+ * Reset all his options.
+ */
+ BZERO(ho, sizeof(*ho));
+
+ /*
+ * Process all his options.
+ */
+ for (len = *lenp; len > 0; len -= cilen, p = prev + cilen) {
+ if ((len < 2) || p[1] > len) {
+ /*
+ * RFC 1661 page 40 -- if the option extends beyond the
+ * packet, then discard the entire packet.
+ */
+ return (0);
+ }
+
+ newret = CODE_CONFACK;
+ prev = p;
+ GETCHAR(type, p);
+ GETCHAR(cilen, p);
+
+ switch (type) { /* Check CI type */
+ case CI_ADDRS:
+ if (!ao->neg_addr) {
+ newret = CODE_CONFREJ;
+ break;
+ }
+
+ if (cilen != CILEN_ADDRS) {
+ /*
+ * rfc1661, page 40 -- a recongnized option with an
+ * invalid length should be Nak'ed.
+ */
+ newret = CODE_CONFNAK;
+ ciaddr1 = wo->hisaddr;
+ ciaddr2 = wo->ouraddr;
+ } else {
+
+ /*
+ * If he has no address, or if we both have his
+ * address but disagree about it, then NAK it with our
+ * idea. In particular, if we don't know his address,
+ * but he does, then accept it.
+ */
+ GETNLONG(ciaddr1, p);
+ if (ciaddr1 != wo->hisaddr &&
+ (ciaddr1 == 0 || !wo->accept_remote)) {
+ newret = CODE_CONFNAK;
+ ciaddr1 = wo->hisaddr;
+ } else if (ciaddr1 == 0 && wo->hisaddr == 0) {
+ /*
+ * If neither we nor he knows his address, reject
+ * the option.
+ */
+ newret = CODE_CONFREJ;
+ wo->req_addr = 0; /* don't NAK with 0.0.0.0 later */
+ break;
+ } else if (ciaddr1 != 0) {
+ go->hisaddr = ciaddr1;
+ }
+
+ /*
+ * If he doesn't know our address, or if we both have
+ * our address * but disagree about it, then NAK it
+ * with our idea.
+ */
+ GETNLONG(ciaddr2, p);
+ if (ciaddr2 != wo->ouraddr) {
+ if (ciaddr2 == 0 || !wo->accept_local) {
+ newret = CODE_CONFNAK;
+ ciaddr2 = wo->ouraddr;
+ } else {
+ go->ouraddr = ciaddr2; /* accept peer's idea */
+ }
+ }
+ }
+
+ if (newret == CODE_CONFNAK) {
+ PUTCHAR(type, nakp);
+ PUTCHAR(CILEN_ADDRS, nakp);
+ PUTNLONG(ciaddr1, nakp);
+ PUTNLONG(ciaddr2, nakp);
+ }
+
+ ho->neg_addr = 1;
+ ho->old_addrs = 1;
+ ho->hisaddr = ciaddr1;
+ ho->ouraddr = ciaddr2;
+ break;
+
+ case CI_ADDR:
+ if (!ao->neg_addr) {
+ newret = CODE_CONFREJ;
+ break;
+ }
+
+ if (cilen != CILEN_ADDR) {
+ /*
+ * rfc1661, page 40 -- a recongnized option with an
+ * invalid length should be Nak'ed.
+ */
+ newret = CODE_CONFNAK;
+ ciaddr1 = wo->hisaddr;
+ } else {
+
+ /*
+ * If he has no address, or if we both have his
+ * address but disagree about it, then NAK it with our
+ * idea. In particular, if we don't know his address,
+ * but he does, then accept it.
+ */
+ GETNLONG(ciaddr1, p);
+ if (ciaddr1 != wo->hisaddr &&
+ (ciaddr1 == 0 || !wo->accept_remote)) {
+ newret = CODE_CONFNAK;
+ ciaddr1 = wo->hisaddr;
+ } else if (ciaddr1 == 0 && wo->hisaddr == 0 &&
+ wo->default_route != 0) {
+ newret = CODE_CONFNAK;
+ /*
+ * If this is a dialup line (default_route is
+ * set), and neither side knows about his address,
+ * suggest an arbitrary rfc1918 address.
+ */
+ ciaddr1 = htonl(0xc0a80101 + ifunit);
+ dbglog("Peer address unknown; suggesting %I", ciaddr1);
+ } else if (ciaddr1 == 0 && wo->hisaddr == 0) {
+ /*
+ * If this is not a dialup line, don't ACK an
+ * address of 0.0.0.0 - reject it instead.
+ */
+ newret = CODE_CONFREJ;
+ wo->req_addr = 0; /* don't NAK with 0.0.0.0 later */
+ break;
+ }
+ }
+
+ if (newret == CODE_CONFNAK) {
+ PUTCHAR(type, nakp);
+ PUTCHAR(CILEN_ADDR, nakp);
+ PUTNLONG(ciaddr1, nakp);
+ }
+
+ ho->neg_addr = 1;
+ ho->hisaddr = ciaddr1;
+ break;
+
+ case CI_MS_DNS1:
+ case CI_MS_DNS2:
+ /* Warning -- these options work backwards. */
+ /* Microsoft primary or secondary DNS request */
+ d = (type == CI_MS_DNS2 ? 1 : 0);
+
+ if (ao->dnsaddr[d] == 0) {
+ newret = CODE_CONFREJ;
+ break;
+ }
+
+ if (cilen != CILEN_ADDR) {
+ newret = CODE_CONFNAK;
+ } else {
+ GETNLONG(tl, p);
+ if (tl != ao->dnsaddr[d]) {
+ newret = CODE_CONFNAK;
+ }
+ }
+
+ if (newret == CODE_CONFNAK) {
+ PUTCHAR(type, nakp);
+ PUTCHAR(CILEN_ADDR, nakp);
+ PUTNLONG(ao->dnsaddr[d], nakp);
+ }
+ break;
+
+ case CI_MS_WINS1:
+ case CI_MS_WINS2:
+ /* Warning -- these options work backwards. */
+ /* Microsoft primary or secondary WINS request */
+ d = (type == CI_MS_WINS2 ? 1 : 0);
+
+ if (ao->winsaddr[d] == 0) {
+ newret = CODE_CONFREJ;
+ break;
+ }
+
+ if (cilen != CILEN_ADDR) {
+ newret = CODE_CONFNAK;
+ } else {
+ GETNLONG(tl, p);
+ if (tl != ao->winsaddr[d]) {
+ newret = CODE_CONFNAK;
+ }
+ }
+
+ if (newret == CODE_CONFNAK) {
+ PUTCHAR(type, nakp);
+ PUTCHAR(CILEN_ADDR, nakp);
+ PUTNLONG(ao->winsaddr[d], nakp);
+ }
+ break;
+
+ case CI_COMPRESSTYPE:
+ if (!ao->neg_vj) {
+ newret = CODE_CONFREJ;
+ break;
+ }
+
+ maxslotindex = ao->maxslotindex;
+ cflag = ao->cflag;
+ if (cilen != CILEN_VJ && cilen != CILEN_COMPRESS) {
+ newret = CODE_CONFNAK;
+ cishort = IPCP_VJ_COMP;
+ } else {
+ GETSHORT(cishort, p);
+ if (cishort != IPCP_VJ_COMP &&
+ (cishort != IPCP_VJ_COMP_OLD || cilen != CILEN_COMPRESS)) {
+ newret = CODE_CONFNAK;
+ cishort = IPCP_VJ_COMP;
+ } else if (cilen == CILEN_VJ) {
+ GETCHAR(maxslotindex, p);
+ if (maxslotindex > ao->maxslotindex) {
+ newret = CODE_CONFNAK;
+ maxslotindex = ao->maxslotindex;
+ }
+ GETCHAR(cflag, p);
+ if (cflag != 0 && ao->cflag == 0) {
+ newret = CODE_CONFNAK;
+ cflag = 0;
+ }
+ } else {
+ ho->old_vj = 1;
+ maxslotindex = MAX_STATES - 1;
+ cflag = 1;
+ }
+ }
+
+ if (newret == CODE_CONFNAK) {
+ PUTCHAR(type, nakp);
+ if (cishort == IPCP_VJ_COMP) {
+ PUTCHAR(CILEN_VJ, nakp);
+ PUTSHORT(cishort, nakp);
+ PUTCHAR(maxslotindex, nakp);
+ PUTCHAR(cflag, nakp);
+ } else {
+ PUTCHAR(CILEN_COMPRESS, nakp);
+ PUTSHORT(cishort, nakp);
+ }
+ }
+ ho->neg_vj = 1;
+ ho->vj_protocol = cishort;
+ ho->maxslotindex = maxslotindex;
+ ho->cflag = cflag;
+ break;
+
+ default:
+ newret = CODE_CONFREJ;
+ break;
+ }
+
+ /* Cope with confused peers. */
+ if (cilen < 2)
+ cilen = 2;
+
+ /*
+ * If this is an Ack'able CI, but we're sending back a Nak,
+ * don't include this CI.
+ */
+ if (newret == CODE_CONFACK && ret != CODE_CONFACK)
+ continue;
+
+ if (newret == CODE_CONFNAK) {
+ if (dont_nak) {
+ newret = CODE_CONFREJ;
+ } else {
+ /* Ignore subsequent Nak'able things if rejecting. */
+ if (ret == CODE_CONFREJ)
+ continue;
+ ret = CODE_CONFNAK;
+ }
+ }
+
+ if (newret == CODE_CONFREJ) {
+ ret = CODE_CONFREJ;
+ if (prev != rejp)
+ BCOPY(prev, rejp, cilen);
+ rejp += cilen;
+ }
+ }
+
+ /*
+ * If we aren't rejecting this packet, and we want to negotiate
+ * their address, and they didn't send their address, then we
+ * send a NAK with a CI_ADDR option appended. We assume the
+ * input buffer is long enough that we can append the extra
+ * option safely.
+ */
+ if (ret != CODE_CONFREJ && !ho->neg_addr && wo->req_addr && !dont_nak) {
+ if (ret == CODE_CONFACK)
+ wo->req_addr = 0; /* don't ask again */
+ ret = CODE_CONFNAK;
+ PUTCHAR(CI_ADDR, nakp);
+ PUTCHAR(CILEN_ADDR, nakp);
+ PUTNLONG(wo->hisaddr, nakp);
+ }
+
+ switch (ret) {
+ case CODE_CONFACK:
+ *lenp = p - p0;
+ sys_block_proto(PPP_IP);
+ break;
+ case CODE_CONFNAK:
+ *lenp = nakp - nak_buffer;
+ BCOPY(nak_buffer, p0, *lenp);
+ break;
+ case CODE_CONFREJ:
+ *lenp = rejp - p0;
+ break;
+ }
+
+ return (ret); /* Return final code */
+}
+
+
+/*
+ * ip_check_options - check that any IP-related options are OK,
+ * and assign appropriate defaults.
+ */
+static void
+ip_check_options()
+{
+ struct hostent *hp;
+ u_int32_t local;
+ ipcp_options *wo = &ipcp_wantoptions[0];
+
+ /*
+ * Default our local IP address based on our hostname.
+ * If local IP address already given, don't bother.
+ */
+ if (wo->ouraddr == 0) {
+ /*
+ * Look up our hostname (possibly with domain name appended)
+ * and take the first IP address as our local IP address.
+ * If there isn't an IP address for our hostname, too bad.
+ */
+ wo->accept_local = 1; /* don't insist on this default value */
+ if ((hp = gethostbyname(hostname)) != NULL) {
+ BCOPY(hp->h_addr, &local, sizeof (hp->h_addr));
+ if (local != 0 && !bad_ip_adrs(local)) {
+ wo->ouraddr = local;
+ ipcp_from_hostname = 1;
+ }
+ }
+ }
+}
+
+
+/*
+ * ip_demand_conf - configure the interface as though
+ * IPCP were up, for use with dial-on-demand.
+ */
+static int
+ip_demand_conf(u)
+ int u;
+{
+ ipcp_options *wo = &ipcp_wantoptions[u];
+
+ if (wo->hisaddr == 0) {
+ /* make up an arbitrary address for the peer */
+ wo->hisaddr = htonl(0x0a707070 + ifunit);
+ wo->accept_remote = 1;
+ }
+ if (wo->ouraddr == 0) {
+ /* make up an arbitrary address for us */
+ wo->ouraddr = htonl(0x0a404040 + ifunit);
+ wo->accept_local = 1;
+ disable_defaultip = 1; /* don't tell the peer this address */
+ }
+ if (!sifaddr(u, wo->ouraddr, wo->hisaddr, GetMask(wo->ouraddr)))
+ return 0;
+ if (!sifup(u))
+ return 0;
+ if (!sifnpmode(u, PPP_IP, NPMODE_QUEUE))
+ return 0;
+ if (wo->default_route && sifdefaultroute(u, wo->ouraddr, wo->hisaddr))
+ default_route_set[u] = 1;
+ if (wo->proxy_arp && sifproxyarp(u, wo->hisaddr, proxy_arp_quiet[u]))
+ proxy_arp_set[u] = 1;
+
+ notice("local IP address %I", wo->ouraddr);
+ notice("remote IP address %I", wo->hisaddr);
+
+ return 1;
+}
+
+
+/*
+ * ipcp_up - IPCP has come UP.
+ *
+ * Configure the IP network interface appropriately and bring it up.
+ */
+static void
+ipcp_up(f)
+ fsm *f;
+{
+ u_int32_t mask;
+ ipcp_options *ho = &ipcp_hisoptions[f->unit];
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+ ipcp_options *wo = &ipcp_wantoptions[f->unit];
+
+ IPCPDEBUG(("ipcp: up"));
+
+ /*
+ * We must have a non-zero IP address for both ends of the link.
+ */
+ if (ho->hisaddr == 0)
+ ho->hisaddr = wo->hisaddr;
+
+ if (ho->hisaddr == 0) {
+ if (wo->accept_remote) {
+ /* Pick some rfc1918 address. */
+ ho->hisaddr = htonl(0xc0a80101 + ifunit);
+ dbglog("Peer refused to provide his address; assuming %I",
+ ho->hisaddr);
+ } else {
+ error("Could not determine remote IP address");
+ ipcp_close(f->unit, "Could not determine remote IP address");
+ return;
+ }
+ }
+ if (go->ouraddr == 0) {
+ error("Could not determine local IP address");
+ ipcp_close(f->unit, "Could not determine local IP address");
+ return;
+ }
+ script_setenv("IPLOCAL", ip_ntoa(go->ouraddr), 0);
+ script_setenv("IPREMOTE", ip_ntoa(ho->hisaddr), 1);
+
+ /*
+ * Check that the peer is allowed to use the IP address it wants.
+ */
+ if (!auth_ip_addr(f->unit, ho->hisaddr)) {
+ error("Peer is not authorized to use remote address %I", ho->hisaddr);
+ ipcp_close(f->unit, "Unauthorized remote IP address");
+ return;
+ }
+
+ if ((go->req_dns1 && go->dnsaddr[0] != 0) ||
+ (go->req_dns2 && go->dnsaddr[1] != 0)) {
+ script_setenv("USEPEERDNS", "1", 0);
+ if (go->dnsaddr[0] != 0)
+ script_setenv("DNS1", ip_ntoa(go->dnsaddr[0]), 0);
+ if (go->dnsaddr[1] != 0)
+ script_setenv("DNS2", ip_ntoa(go->dnsaddr[1]), 0);
+ create_resolv(go->dnsaddr[0], go->dnsaddr[1]);
+ }
+
+ /* set tcp compression */
+ if (sifvjcomp(f->unit, ho->neg_vj, ho->cflag, ho->maxslotindex) != 1) {
+ ipcp_close(f->unit, "Could not enable VJ TCP header compression");
+ return;
+ }
+
+ /*
+ * If we are doing dial-on-demand, the interface is already
+ * configured, so we put out any saved-up packets, then set the
+ * interface to pass IP packets.
+ */
+ if (demand) {
+ if (go->ouraddr != wo->ouraddr || ho->hisaddr != wo->hisaddr) {
+ ipcp_clear_addrs(f->unit, wo->ouraddr, wo->hisaddr);
+ if (go->ouraddr != wo->ouraddr) {
+ warn("Local IP address changed to %I", go->ouraddr);
+ script_setenv("OLDIPLOCAL", ip_ntoa(wo->ouraddr), 0);
+ wo->ouraddr = go->ouraddr;
+ } else
+ script_unsetenv("OLDIPLOCAL");
+ if (ho->hisaddr != wo->hisaddr) {
+ warn("Remote IP address changed to %I", ho->hisaddr);
+ script_setenv("OLDIPREMOTE", ip_ntoa(wo->hisaddr), 0);
+ wo->hisaddr = ho->hisaddr;
+ } else
+ script_unsetenv("OLDIPREMOTE");
+
+ /* Set the interface to the new addresses */
+ mask = GetMask(go->ouraddr);
+ if (!sifaddr(f->unit, go->ouraddr, ho->hisaddr, mask)) {
+ warn("Interface configuration failed");
+ ipcp_close(f->unit, "Interface configuration failed");
+ return;
+ }
+
+ /* assign a default route through the interface if required */
+ if (wo->default_route)
+ if (sifdefaultroute(f->unit, go->ouraddr, ho->hisaddr))
+ default_route_set[f->unit] = 1;
+
+ /* Make a proxy ARP entry if requested. */
+ if (wo->proxy_arp &&
+ sifproxyarp(f->unit, ho->hisaddr, proxy_arp_quiet[f->unit]))
+ proxy_arp_set[f->unit] = 1;
+
+ }
+ demand_rexmit(PPP_IP);
+ if (sifnpmode(f->unit, PPP_IP, NPMODE_PASS) != 1) {
+ ipcp_close(f->unit, "Interface configuration failed.");
+ return;
+ }
+
+ } else {
+ /*
+ * Set IP addresses and (if specified) netmask.
+ */
+ mask = GetMask(go->ouraddr);
+
+#if SIFUPFIRST
+ /* bring the interface up for IP */
+ if (!sifup(f->unit)) {
+ warn("Interface failed to come up");
+ ipcp_close(f->unit, "Interface configuration failed");
+ return;
+ }
+#endif
+
+ if (!sifaddr(f->unit, go->ouraddr, ho->hisaddr, mask)) {
+ warn("Interface configuration failed");
+ ipcp_close(f->unit, "Interface configuration failed");
+ return;
+ }
+
+#if !SIFUPFIRST
+ /* bring the interface up for IP */
+ if (!sifup(f->unit)) {
+ warn("Interface failed to come up");
+ ipcp_close(f->unit, "Interface configuration failed");
+ return;
+ }
+#endif
+
+ if (sifnpmode(f->unit, PPP_IP, NPMODE_PASS) != 1) {
+ ipcp_close(f->unit, "Interface configuration failed.");
+ return;
+ }
+
+ /* assign a default route through the interface if required */
+ if (wo->default_route)
+ if (sifdefaultroute(f->unit, go->ouraddr, ho->hisaddr))
+ default_route_set[f->unit] = 1;
+
+ /* Make a proxy ARP entry if requested. */
+ if (wo->proxy_arp &&
+ sifproxyarp(f->unit, ho->hisaddr, proxy_arp_quiet[f->unit]))
+ proxy_arp_set[f->unit] = 1;
+
+ wo->ouraddr = go->ouraddr;
+
+ notice("local IP address %I", go->ouraddr);
+ notice("remote IP address %I", ho->hisaddr);
+ if (go->dnsaddr[0] != 0)
+ notice("primary DNS address %I", go->dnsaddr[0]);
+ if (go->dnsaddr[1] != 0)
+ notice("secondary DNS address %I", go->dnsaddr[1]);
+ }
+
+ np_up(f->unit, PPP_IP);
+ ipcp_is_up[f->unit] = 1;
+
+ if (ip_up_hook != NULL)
+ (*ip_up_hook)();
+
+ /*
+ * Execute the ip-up script, like this:
+ * /etc/ppp/ip-up interface tty speed local-IP remote-IP
+ */
+ if (ipcp_script_state == s_down && ipcp_script_pid == 0) {
+ ipcp_script_state = s_up;
+ ipcp_script(_PATH_IPUP);
+ }
+ sys_unblock_proto(PPP_IP);
+}
+
+
+/*
+ * ipcp_down - IPCP has gone DOWN.
+ *
+ * Take the IP network interface down, clear its addresses
+ * and delete routes through it.
+ */
+static void
+ipcp_down(f)
+ fsm *f;
+{
+ IPCPDEBUG(("ipcp: down"));
+ /* XXX a bit IPv4-centric here, we only need to get the stats
+ * before the interface is marked down. */
+ update_link_stats(f->unit);
+ if (ip_down_hook != NULL)
+ (*ip_down_hook)();
+ if (ipcp_is_up[f->unit]) {
+ ipcp_is_up[f->unit] = 0;
+ np_down(f->unit, PPP_IP);
+ }
+ if (sifvjcomp(f->unit, 0, 0, 0) != 1) {
+ if (debug)
+ warn("Failed to disable VJ TCP header compression.");
+ }
+
+ /*
+ * If we are doing dial-on-demand, set the interface
+ * to queue up outgoing packets (for now).
+ */
+ if (demand) {
+ if (sifnpmode(f->unit, PPP_IP, NPMODE_QUEUE) != 1) {
+ if (debug)
+ warn("Failed to enable Queueing on outgoing packets.");
+ }
+ } else {
+ if (sifnpmode(f->unit, PPP_IP, NPMODE_ERROR) != 1) {
+ if (debug)
+ warn("Could not set interface to drop packets.");
+ }
+ if (sifdown(f->unit) != 1)
+ warn("Could not bring interface down.");
+ ipcp_clear_addrs(f->unit, ipcp_gotoptions[f->unit].ouraddr,
+ ipcp_hisoptions[f->unit].hisaddr);
+ }
+
+ /* Execute the ip-down script */
+ if (ipcp_script_state == s_up && ipcp_script_pid == 0) {
+ ipcp_script_state = s_down;
+ ipcp_script(_PATH_IPDOWN);
+ }
+}
+
+
+/*
+ * ipcp_clear_addrs() - clear the interface addresses, routes,
+ * proxy arp entries, etc.
+ */
+static void
+ipcp_clear_addrs(unit, ouraddr, hisaddr)
+ int unit;
+ u_int32_t ouraddr; /* local address */
+ u_int32_t hisaddr; /* remote address */
+{
+ if (proxy_arp_set[unit]) {
+ (void) cifproxyarp(unit, hisaddr);
+ proxy_arp_set[unit] = 0;
+ }
+ if (default_route_set[unit]) {
+ (void) cifdefaultroute(unit, ouraddr, hisaddr);
+ default_route_set[unit] = 0;
+ }
+ if (cifaddr(unit, ouraddr, hisaddr) != 1)
+ warn("Could not clear addresses");
+}
+
+
+/*
+ * ipcp_finished - possibly shut down the lower layers.
+ */
+static void
+ipcp_finished(f)
+ fsm *f;
+{
+ np_finished(f->unit, PPP_IP);
+}
+
+
+/*
+ * ipcp_script_done - called when the ip-up or ip-down script
+ * has finished.
+ */
+/*ARGSUSED*/
+static void
+ipcp_script_done(arg, status)
+ void *arg;
+ int status;
+{
+ ipcp_script_pid = 0;
+ switch (ipcp_script_state) {
+ case s_up:
+ if (ipcp_fsm[0].state != OPENED) {
+ ipcp_script_state = s_down;
+ ipcp_script(_PATH_IPDOWN);
+ }
+ break;
+ case s_down:
+ if (ipcp_fsm[0].state == OPENED) {
+ ipcp_script_state = s_up;
+ ipcp_script(_PATH_IPUP);
+ }
+ break;
+ }
+}
+
+
+/*
+ * ipcp_script - Execute a script with arguments
+ * interface-name tty-name speed local-IP remote-IP.
+ */
+static void
+ipcp_script(script)
+ char *script;
+{
+ char strspeed[32], strlocal[32], strremote[32];
+ char *argv[8];
+
+ (void) slprintf(strspeed, sizeof(strspeed), "%d", baud_rate);
+ (void) slprintf(strlocal, sizeof(strlocal), "%I",
+ ipcp_gotoptions[0].ouraddr);
+ (void) slprintf(strremote, sizeof(strremote), "%I",
+ ipcp_hisoptions[0].hisaddr);
+
+ argv[0] = script;
+ argv[1] = ifname;
+ argv[2] = devnam;
+ argv[3] = strspeed;
+ argv[4] = strlocal;
+ argv[5] = strremote;
+ argv[6] = ipparam;
+ argv[7] = NULL;
+ ipcp_script_pid = run_program(script, argv, 0, ipcp_script_done, NULL);
+}
+
+/*
+ * create_resolv - create the replacement resolv.conf file
+ */
+static void
+create_resolv(peerdns1, peerdns2)
+ u_int32_t peerdns1, peerdns2;
+{
+ FILE *f;
+
+ f = fopen(_PATH_RESOLV, "w");
+ if (f == NULL) {
+ error("Failed to create %s: %m", _PATH_RESOLV);
+ return;
+ }
+
+ if (peerdns1)
+ if (fprintf(f, "nameserver %s\n", ip_ntoa(peerdns1)) <= 0)
+ error("Write failed to %s: %m", _PATH_RESOLV);
+
+ if (peerdns2)
+ if (fprintf(f, "nameserver %s\n", ip_ntoa(peerdns2)) <= 0)
+ error("Write failed to %s: %m", _PATH_RESOLV);
+
+ if (fclose(f) != 0)
+ error("Failed to close %s: %m", _PATH_RESOLV);
+}
+
+/*
+ * ipcp_printpkt - print the contents of an IPCP packet.
+ */
+static int
+ipcp_printpkt(p, plen, printer, arg)
+ u_char *p;
+ int plen;
+ void (*printer) __P((void *, const char *, ...));
+ void *arg;
+{
+ int code, id, len, olen;
+ u_char *pstart, *optend;
+ u_short cishort;
+ u_int32_t cilong;
+
+ if (plen < HEADERLEN)
+ return 0;
+ pstart = p;
+ GETCHAR(code, p);
+ GETCHAR(id, p);
+ GETSHORT(len, p);
+ if (len < HEADERLEN || len > plen)
+ return 0;
+
+ printer(arg, " %s id=0x%x", code_name(code, 1), id);
+ len -= HEADERLEN;
+ switch (code) {
+ case CODE_CONFREQ:
+ case CODE_CONFACK:
+ case CODE_CONFNAK:
+ case CODE_CONFREJ:
+ /* print option list */
+ while (len >= 2) {
+ GETCHAR(code, p);
+ GETCHAR(olen, p);
+ p -= 2;
+ if (olen < 2 || olen > len) {
+ break;
+ }
+ printer(arg, " <");
+ len -= olen;
+ optend = p + olen;
+ switch (code) {
+ case CI_ADDRS:
+ if (olen == CILEN_ADDRS) {
+ p += 2;
+ GETNLONG(cilong, p);
+ printer(arg, "addrs %I", cilong);
+ GETNLONG(cilong, p);
+ printer(arg, " %I", cilong);
+ }
+ break;
+ case CI_COMPRESSTYPE:
+ if (olen >= CILEN_COMPRESS) {
+ p += 2;
+ GETSHORT(cishort, p);
+ printer(arg, "compress ");
+ switch (cishort) {
+ case IPCP_VJ_COMP:
+ printer(arg, "VJ");
+ break;
+ case IPCP_VJ_COMP_OLD:
+ printer(arg, "old-VJ");
+ break;
+ default:
+ printer(arg, "0x%x", cishort);
+ }
+ }
+ break;
+ case CI_ADDR:
+ if (olen == CILEN_ADDR) {
+ p += 2;
+ GETNLONG(cilong, p);
+ printer(arg, "addr %I", cilong);
+ }
+ break;
+ case CI_MS_DNS1:
+ case CI_MS_DNS2:
+ p += 2;
+ GETNLONG(cilong, p);
+ printer(arg, "ms-dns%d %I", (code == CI_MS_DNS1 ? 1 : 2),
+ cilong);
+ break;
+ case CI_MS_WINS1:
+ case CI_MS_WINS2:
+ p += 2;
+ GETNLONG(cilong, p);
+ printer(arg, "ms-wins%d %I", (code == CI_MS_WINS1 ? 1 : 2),
+ cilong);
+ break;
+ case CI_SUBNET:
+ p += 2;
+ GETNLONG(cilong, p);
+ printer(arg, "subnet %I", cilong);
+ break;
+ }
+ while (p < optend) {
+ GETCHAR(code, p);
+ printer(arg, " %.2x", code);
+ }
+ printer(arg, ">");
+ }
+ break;
+
+ case CODE_TERMACK:
+ case CODE_TERMREQ:
+ if (len > 0 && *p >= ' ' && *p < 0x7f) {
+ printer(arg, " ");
+ print_string((char *)p, len, printer, arg);
+ p += len;
+ len = 0;
+ }
+ break;
+ }
+
+ /* print the rest of the bytes in the packet */
+ for (; len > 0; --len) {
+ GETCHAR(code, p);
+ printer(arg, " %.2x", code);
+ }
+
+ return p - pstart;
+}
+
+/*
+ * ip_active_pkt - see if this IP packet is worth bringing the link up for.
+ * We don't bring the link up for IP fragments or for TCP FIN packets
+ * with no data.
+ */
+
+static int
+ip_active_pkt(pkt, len)
+ u_char *pkt;
+ int len;
+{
+ u_char *tcp;
+ struct protoent *pep;
+ int val;
+ int hlen;
+ char buf[32], *cp;
+ u_int32_t src, dst;
+
+ len -= PPP_HDRLEN;
+ pkt += PPP_HDRLEN;
+ if (len < IP_HDRLEN) {
+ dbglog("IP packet of length %d is not activity", len);
+ return 0;
+ }
+ src = get_ipsrc(pkt);
+ dst = get_ipdst(pkt);
+ if ((get_ipoff(pkt) & IP_OFFMASK) != 0) {
+ dbglog("IP fragment from %I->%I is not activity", src, dst);
+ return 0;
+ }
+ val = get_ipproto(pkt);
+ if (val != IPPROTO_TCP) {
+ if (debug) {
+ if ((pep = getprotobynumber(val)) != NULL) {
+ cp = pep->p_name;
+ } else {
+ (void) slprintf(buf, sizeof (buf), "IP proto %d", val);
+ cp = buf;
+ }
+ dbglog("%s from %I->%I is activity", cp, src, dst);
+ }
+ return 1;
+ }
+ hlen = get_iphl(pkt) * 4;
+ if (len < hlen + TCP_HDRLEN) {
+ dbglog("Bad TCP length %d<%d+%d %I->%I is not activity", len, hlen,
+ TCP_HDRLEN, src, dst);
+ return 0;
+ }
+ tcp = pkt + hlen;
+ val = get_tcpflags(tcp);
+ hlen += get_tcpoff(tcp) * 4;
+ if ((val & TH_FIN) != 0 && len == hlen) {
+ dbglog("Empty TCP FIN %I->%I is not activity", src, dst);
+ return 0;
+ }
+ if (debug) {
+ cp = buf;
+ if (val & TH_URG)
+ *cp++ = 'U';
+ if (val & TH_ACK)
+ *cp++ = 'A';
+ if (val & TH_PUSH)
+ *cp++ = 'P';
+ if (val & TH_RST)
+ *cp++ = 'R';
+ if (val & TH_SYN)
+ *cp++ = 'S';
+ if (val & TH_FIN)
+ *cp++ = 'F';
+ if (cp != buf)
+ *cp++ = ' ';
+ *cp = '\0';
+ dbglog("TCP %d data %s%I->%I is activity", len - hlen, buf, src, dst);
+ }
+ return 1;
+}
+
+static void
+ipcp_print_stat(unit, strptr)
+ int unit;
+ FILE *strptr;
+{
+ ipcp_options *go = &ipcp_gotoptions[unit];
+ ipcp_options *ho = &ipcp_hisoptions[unit];
+ char *proto_name = ipcp_protent.name;
+
+ if (!ipcp_protent.enabled_flag) {
+ (void) flprintf(strptr, "%s disabled\n", proto_name);
+ return;
+ }
+
+ (void) flprintf(strptr, "%s state: %s", proto_name,
+ fsm_state(ipcp_fsm[unit].state));
+ (void) flprintf(strptr, "%s local %I remote %I", proto_name, go->ouraddr,
+ ho->ouraddr);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppd/ipcp.h b/usr/src/cmd/cmd-inet/usr.bin/pppd/ipcp.h
new file mode 100644
index 0000000000..d993d1265e
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppd/ipcp.h
@@ -0,0 +1,80 @@
+/*
+ * ipcp.h - IP Control Protocol definitions.
+ *
+ * Copyright (c) 2000 by Sun Microsystems, Inc.
+ * All rights reserved.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: ipcp.h,v 1.13 1999/03/02 05:35:09 paulus Exp $
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Options.
+ */
+#define CI_ADDRS 1 /* IP Addresses */
+#define CI_COMPRESSTYPE 2 /* Compression Type */
+#define CI_ADDR 3
+
+#define CI_MS_DNS1 129 /* Primary DNS value */
+#define CI_MS_WINS1 130 /* Primary WINS value */
+#define CI_MS_DNS2 131 /* Secondary DNS value */
+#define CI_MS_WINS2 132 /* Secondary WINS value */
+#define CI_SUBNET 144 /* Bogus subnet option */
+
+#define MAX_STATES 16 /* from slcompress.h */
+
+#define IPCP_VJMODE_OLD 1 /* "old" mode (option # = 0x0037) */
+#define IPCP_VJMODE_RFC1172 2 /* "old-rfc"mode (option # = 0x002d) */
+#define IPCP_VJMODE_RFC1332 3 /* "new-rfc"mode (option # = 0x002d, */
+ /* maxslot and slot number compression) */
+
+#define IPCP_VJ_COMP 0x002d /* current value for VJ compression option*/
+#define IPCP_VJ_COMP_OLD 0x0037 /* "old" (i.e, broken) value for VJ */
+ /* compression option*/
+
+typedef struct ipcp_options {
+ bool neg_addr; /* Negotiate IP Address? */
+ bool old_addrs; /* Use old (IP-Addresses) option? */
+ bool req_addr; /* Ask peer to send IP address? */
+ bool default_route; /* Assign default route through interface? */
+ bool proxy_arp; /* Make proxy ARP entry for peer? */
+ bool neg_vj; /* Van Jacobson Compression? */
+ bool old_vj; /* use old (short) form of VJ option? */
+ bool accept_local; /* accept peer's value for ouraddr */
+ bool accept_remote; /* accept peer's value for hisaddr */
+ bool req_dns1; /* Ask peer to send primary DNS address? */
+ bool req_dns2; /* Ask peer to send secondary DNS address? */
+ bool cflag; /* VJ slot compression flag */
+ int vj_protocol; /* protocol value to use in VJ option */
+ int maxslotindex; /* values for RFC1332 VJ compression neg. */
+ u_int32_t ouraddr, hisaddr; /* Addresses in NETWORK BYTE ORDER */
+ u_int32_t dnsaddr[2]; /* Primary and secondary MS DNS entries */
+ u_int32_t winsaddr[2]; /* Primary and secondary MS WINS entries */
+} ipcp_options;
+
+extern fsm ipcp_fsm[];
+extern ipcp_options ipcp_wantoptions[];
+extern ipcp_options ipcp_gotoptions[];
+extern ipcp_options ipcp_allowoptions[];
+extern ipcp_options ipcp_hisoptions[];
+
+extern char *ip_ntoa __P((u_int32_t));
+
+extern struct protent ipcp_protent;
+extern bool ipcp_from_hostname;
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppd/ipv6cp.c b/usr/src/cmd/cmd-inet/usr.bin/pppd/ipv6cp.c
new file mode 100644
index 0000000000..bf94434288
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppd/ipv6cp.c
@@ -0,0 +1,1536 @@
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ ipv6cp.c - PPP IPV6 Control Protocol.
+ Copyright (C) 1999 Tommi Komulainen <Tommi.Komulainen@iki.fi>
+
+ Redistribution and use in source and binary forms are permitted
+ provided that the above copyright notice and this paragraph are
+ duplicated in all such forms. The name of the author may not be
+ used to endorse or promote products derived from this software
+ without specific prior written permission.
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+*/
+
+/* Original version, based on RFC2023 :
+
+ Copyright (c) 1995, 1996, 1997 Francis.Dupont@inria.fr, INRIA Rocquencourt,
+ Alain.Durand@imag.fr, IMAG,
+ Jean-Luc.Richier@imag.fr, IMAG-LSR.
+
+ Copyright (c) 1998, 1999 Francis.Dupont@inria.fr, GIE DYADE,
+ Alain.Durand@imag.fr, IMAG,
+ Jean-Luc.Richier@imag.fr, IMAG-LSR.
+
+ Ce travail a été fait au sein du GIE DYADE (Groupement d'Intérêt
+ Économique ayant pour membres BULL S.A. et l'INRIA).
+
+ Ce logiciel informatique est disponible aux conditions
+ usuelles dans la recherche, c'est-à-dire qu'il peut
+ être utilisé, copié, modifié, distribué à l'unique
+ condition que ce texte soit conservé afin que
+ l'origine de ce logiciel soit reconnue.
+
+ Le nom de l'Institut National de Recherche en Informatique
+ et en Automatique (INRIA), de l'IMAG, ou d'une personne morale
+ ou physique ayant participé à l'élaboration de ce logiciel ne peut
+ être utilisé sans son accord préalable explicite.
+
+ Ce logiciel est fourni tel quel sans aucune garantie,
+ support ou responsabilité d'aucune sorte.
+ Ce logiciel est dérivé de sources d'origine
+ "University of California at Berkeley" et
+ "Digital Equipment Corporation" couvertes par des copyrights.
+
+ L'Institut d'Informatique et de Mathématiques Appliquées de Grenoble (IMAG)
+ est une fédération d'unités mixtes de recherche du CNRS, de l'Institut National
+ Polytechnique de Grenoble et de l'Université Joseph Fourier regroupant
+ sept laboratoires dont le laboratoire Logiciels, Systèmes, Réseaux (LSR).
+
+ This work has been done in the context of GIE DYADE (joint R & D venture
+ between BULL S.A. and INRIA).
+
+ This software is available with usual "research" terms
+ with the aim of retain credits of the software.
+ Permission to use, copy, modify and distribute this software for any
+ purpose and without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies,
+ and the name of INRIA, IMAG, or any contributor not be used in advertising
+ or publicity pertaining to this material without the prior explicit
+ permission. The software is provided "as is" without any
+ warranties, support or liabilities of any kind.
+ This software is derived from source code from
+ "University of California at Berkeley" and
+ "Digital Equipment Corporation" protected by copyrights.
+
+ Grenoble's Institute of Computer Science and Applied Mathematics (IMAG)
+ is a federation of seven research units funded by the CNRS, National
+ Polytechnic Institute of Grenoble and University Joseph Fourier.
+ The research unit in Software, Systems, Networks (LSR) is member of IMAG.
+*/
+
+/*
+ * Derived from :
+ *
+ *
+ * ipcp.c - PPP IP Control Protocol.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: ipv6cp.c,v 1.9 2000/04/15 01:27:11 masputra Exp $
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+#define RCSID "$Id: ipv6cp.c,v 1.9 2000/04/15 01:27:11 masputra Exp $"
+
+/*
+ * TODO:
+ *
+ * Proxy Neighbour Discovery.
+ *
+ * Better defines for selecting the ordering of
+ * interface up / set address. (currently checks for __linux__,
+ * since SVR4 && (SNI || __USLC__) didn't work properly)
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "pppd.h"
+#include "eui64.h"
+#include "fsm.h"
+#include "ipcp.h"
+#include "ipv6cp.h"
+#include "magic.h"
+#include "pathnames.h"
+
+#if !defined(lint) && !defined(_lint)
+static const char rcsid[] = RCSID;
+#endif
+
+/* global vars */
+ipv6cp_options ipv6cp_wantoptions[NUM_PPP]; /* Options that we want to request */
+ipv6cp_options ipv6cp_gotoptions[NUM_PPP]; /* Options that peer ack'd */
+ipv6cp_options ipv6cp_allowoptions[NUM_PPP]; /* Options we allow peer to request */
+ipv6cp_options ipv6cp_hisoptions[NUM_PPP]; /* Options that we ack'd */
+int no_ifaceid_neg = 0;
+
+/* local vars */
+static bool ipv6cp_is_up;
+
+/*
+ * Callbacks for fsm code. (CI = Configuration Information)
+ */
+static void ipv6cp_resetci __P((fsm *)); /* Reset our CI */
+static int ipv6cp_cilen __P((fsm *)); /* Return length of our CI */
+static void ipv6cp_addci __P((fsm *, u_char *, int *)); /* Add our CI */
+static int ipv6cp_ackci __P((fsm *, u_char *, int)); /* Peer ack'd our CI */
+static int ipv6cp_nakci __P((fsm *, u_char *, int)); /* Peer nak'd our CI */
+static int ipv6cp_rejci __P((fsm *, u_char *, int)); /* Peer rej'd our CI */
+static int ipv6cp_reqci __P((fsm *, u_char *, int *, int)); /* Rcv CI */
+static void ipv6cp_up __P((fsm *)); /* We're UP */
+static void ipv6cp_down __P((fsm *)); /* We're DOWN */
+static void ipv6cp_finished __P((fsm *)); /* Don't need lower layer */
+
+fsm ipv6cp_fsm[NUM_PPP]; /* IPV6CP fsm structure */
+
+static fsm_callbacks ipv6cp_callbacks = { /* IPV6CP callback routines */
+ ipv6cp_resetci, /* Reset our Configuration Information */
+ ipv6cp_cilen, /* Length of our Configuration Information */
+ ipv6cp_addci, /* Add our Configuration Information */
+ ipv6cp_ackci, /* ACK our Configuration Information */
+ ipv6cp_nakci, /* NAK our Configuration Information */
+ ipv6cp_rejci, /* Reject our Configuration Information */
+ ipv6cp_reqci, /* Request peer's Configuration Information */
+ ipv6cp_up, /* Called when fsm reaches OPENED state */
+ ipv6cp_down, /* Called when fsm leaves OPENED state */
+ NULL, /* Called when we want the lower layer up */
+ ipv6cp_finished, /* Called when we want the lower layer down */
+ NULL, /* Retransmission is necessary */
+ NULL, /* Called to handle protocol-specific codes */
+ "IPV6CP", /* String name of protocol */
+ NULL /* Peer rejected a code number */
+};
+
+static int setifaceid __P((char **arg, option_t *));
+
+/*
+ * Command-line options.
+ */
+static option_t ipv6cp_option_list[] = {
+ { "ipv6", o_special, (void *)setifaceid,
+ "Set interface identifiers for IPV6" },
+ { "noipv6", o_bool, &ipv6cp_protent.enabled_flag,
+ "Disable IPv6 and IPv6CP" },
+ { "-ipv6", o_bool, &ipv6cp_protent.enabled_flag,
+ "Disable IPv6 and IPv6CP" },
+ { "+ipv6", o_bool, &ipv6cp_protent.enabled_flag,
+ "Enable IPv6 and IPv6CP", 1 },
+ { "ipv6cp-accept-local", o_bool, &ipv6cp_wantoptions[0].accept_local,
+ "Accept peer's interface identifier for us", 1 },
+ { "ipv6cp-use-ipaddr", o_bool, &ipv6cp_wantoptions[0].use_ip,
+ "Use (default) IPv4 address as interface identifier", 1 },
+#if defined(SOL2)
+ { "ipv6cp-use-persistent", o_bool, &ipv6cp_wantoptions[0].use_persistent,
+ "Use unique persistent value for link local address", 1 },
+#endif /* defined(SOL2) */
+ { "ipv6cp-restart", o_int, &ipv6cp_fsm[0].timeouttime,
+ "Set timeout for IPv6CP" },
+ { "ipv6cp-max-terminate", o_int, &ipv6cp_fsm[0].maxtermtransmits,
+ "Maximum number of IPV6CP Terminate-Request" },
+ { "ipv6cp-max-configure", o_int, &ipv6cp_fsm[0].maxconfreqtransmits,
+ "Maximum number of IPV6CP Configure-Request" },
+ { "ipv6cp-max-failure", o_int, &ipv6cp_fsm[0].maxnakloops,
+ "Maximum number of IPV6CP Configure-Nak" },
+ { NULL }
+};
+
+
+/*
+ * Protocol entry points from main code.
+ */
+static void ipv6cp_init __P((int));
+static void ipv6cp_open __P((int));
+static void ipv6cp_close __P((int, char *));
+static void ipv6cp_lowerup __P((int));
+static void ipv6cp_lowerdown __P((int));
+static void ipv6cp_input __P((int, u_char *, int));
+static void ipv6cp_protrej __P((int));
+static int ipv6cp_printpkt __P((u_char *, int,
+ void (*) __P((void *, const char *, ...)), void *));
+static void ipv6_check_options __P((void));
+static int ipv6_demand_conf __P((int));
+static int ipv6_active_pkt __P((u_char *, int));
+
+struct protent ipv6cp_protent = {
+ PPP_IPV6CP, /* Protocol Number for IPV6CP */
+ ipv6cp_init, /* Initializes IPV6CP */
+ ipv6cp_input, /* Processes a received IPV6CP packet */
+ ipv6cp_protrej, /* Process a received Protocol-reject */
+ ipv6cp_lowerup, /* Called when LCP is brought up */
+ ipv6cp_lowerdown, /* Called when LCP has gone down */
+ ipv6cp_open, /* Called when link is established */
+ ipv6cp_close, /* Called when link has gone down */
+ ipv6cp_printpkt, /* Print a packet in human readable form */
+ NULL, /* Process a received data packet */
+ 0, /* IPV6CP is disabled by default */
+ "IPV6CP", /* Name of the protocol */
+ "IPV6", /* Name of the corresponding data protocol */
+ ipv6cp_option_list, /* List of IPV6CP command-line options */
+ ipv6_check_options, /* Assigns default values for options */
+ ipv6_demand_conf, /* Configures demand-dial */
+ ipv6_active_pkt /* Bring up the link for this packet? */
+};
+
+/*
+ * Local forward function declarations.
+ */
+static void ipv6cp_clear_addrs __P((int, eui64_t, eui64_t));
+static void ipv6cp_script __P((char *));
+static void ipv6cp_script_done __P((void *, int));
+
+/*
+ * Lengths of configuration options.
+ */
+#define CILEN_VOID 2
+#define CILEN_COMPRESS 4 /* length for RFC2023 compress opt. */
+#define CILEN_IFACEID 10 /* RFC2472, interface identifier */
+
+#define CODENAME(x) ((x) == CODE_CONFACK ? "ACK" : \
+ (x) == CODE_CONFNAK ? "NAK" : "REJ")
+
+/*
+ * This state variable is used to ensure that we don't
+ * run an ipcp-up/down script while one is already running.
+ */
+static enum script_state {
+ s_down,
+ s_up
+} ipv6cp_script_state;
+static pid_t ipv6cp_script_pid;
+
+/*
+ * setifaceid - set the interface identifiers manually
+ */
+/*ARGSUSED*/
+static int
+setifaceid(argv, opt)
+ char **argv;
+ option_t *opt;
+{
+ char *comma, *arg;
+ ipv6cp_options *wo = &ipv6cp_wantoptions[0];
+ struct in6_addr addr;
+
+#define VALIDID(a) ( (((a).s6_addr32[0] == 0) && ((a).s6_addr32[1] == 0)) && \
+ (((a).s6_addr32[2] != 0) || ((a).s6_addr32[3] != 0)) )
+
+
+ arg = *argv;
+
+ comma = strchr(arg, ',');
+
+ /*
+ * If comma first character, then no local identifier
+ */
+ if (comma != arg) {
+ if (comma != NULL)
+ *comma = '\0';
+
+ if (inet_pton(AF_INET6, arg, &addr) != 1 || !VALIDID(addr)) {
+ option_error("Illegal interface identifier (local): %s", arg);
+ return 0;
+ }
+
+ eui64_copy(addr.s6_addr32[2], wo->ourid);
+ wo->opt_local = 1;
+ }
+
+ /*
+ * If comma last character, then no remote identifier
+ */
+ if (comma != NULL && *++comma != '\0') {
+ if (inet_pton(AF_INET6, comma, &addr) != 1 || !VALIDID(addr)) {
+ option_error("Illegal interface identifier (remote): %s", comma);
+ return 0;
+ }
+ eui64_copy(addr.s6_addr32[2], wo->hisid);
+ wo->opt_remote = 1;
+ }
+
+ ipv6cp_protent.enabled_flag = 1;
+ return 1;
+}
+
+/*
+ * Given an interface identifier, return a string representation of the
+ * link local address associated with that identifier.
+ * string will be at most 26 characters (including null terminator).
+ */
+static char *
+llv6_ntoa(ifaceid)
+ eui64_t ifaceid;
+{
+ struct in6_addr addr;
+ static char addrstr[26];
+
+ BZERO(&addr, sizeof (addr));
+ addr.s6_addr[0] = 0xfe;
+ addr.s6_addr[1] = 0x80;
+ eui64_copy(ifaceid, addr.s6_addr[8]);
+
+ (void) inet_ntop(AF_INET6, &addr, addrstr, 26);
+
+ return addrstr;
+}
+
+
+/*
+ * ipv6cp_init - Initialize IPV6CP.
+ */
+static void
+ipv6cp_init(unit)
+ int unit;
+{
+ fsm *f = &ipv6cp_fsm[unit];
+ ipv6cp_options *wo = &ipv6cp_wantoptions[unit];
+ ipv6cp_options *ao = &ipv6cp_allowoptions[unit];
+
+ f->unit = unit;
+ f->protocol = PPP_IPV6CP;
+ f->callbacks = &ipv6cp_callbacks;
+ fsm_init(&ipv6cp_fsm[unit]);
+
+ BZERO(wo, sizeof(*wo));
+ BZERO(ao, sizeof(*ao));
+
+ wo->neg_ifaceid = 1;
+ ao->neg_ifaceid = 1;
+
+#ifdef IPV6CP_COMP
+ wo->neg_vj = 1;
+ ao->neg_vj = 1;
+ wo->vj_protocol = IPV6CP_COMP;
+#endif
+
+}
+
+
+/*
+ * ipv6cp_open - IPV6CP is allowed to come up.
+ */
+static void
+ipv6cp_open(unit)
+ int unit;
+{
+ fsm_open(&ipv6cp_fsm[unit]);
+}
+
+
+/*
+ * ipv6cp_close - Take IPV6CP down.
+ */
+static void
+ipv6cp_close(unit, reason)
+ int unit;
+ char *reason;
+{
+ fsm_close(&ipv6cp_fsm[unit], reason);
+}
+
+
+/*
+ * ipv6cp_lowerup - The lower layer is up.
+ */
+static void
+ipv6cp_lowerup(unit)
+ int unit;
+{
+ fsm_lowerup(&ipv6cp_fsm[unit]);
+}
+
+
+/*
+ * ipv6cp_lowerdown - The lower layer is down.
+ */
+static void
+ipv6cp_lowerdown(unit)
+ int unit;
+{
+ fsm_lowerdown(&ipv6cp_fsm[unit]);
+}
+
+
+/*
+ * ipv6cp_input - Input IPV6CP packet.
+ */
+static void
+ipv6cp_input(unit, p, len)
+ int unit;
+ u_char *p;
+ int len;
+{
+ fsm_input(&ipv6cp_fsm[unit], p, len);
+}
+
+
+/*
+ * ipv6cp_protrej - A Protocol-Reject was received for IPV6CP.
+ */
+static void
+ipv6cp_protrej(unit)
+ int unit;
+{
+ fsm_protreject(&ipv6cp_fsm[unit]);
+}
+
+
+/*
+ * ipv6cp_resetci - Reset our CI.
+ */
+static void
+ipv6cp_resetci(f)
+ fsm *f;
+{
+ ipv6cp_options *wo = &ipv6cp_wantoptions[f->unit];
+ ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
+
+ wo->req_ifaceid = wo->neg_ifaceid && ipv6cp_allowoptions[f->unit].neg_ifaceid;
+
+ if (!wo->opt_local) {
+ eui64_magic_nz(wo->ourid);
+ }
+
+ *go = *wo;
+ eui64_zero(go->hisid); /* last proposed interface identifier */
+}
+
+
+/*
+ * ipv6cp_cilen - Return length of our CI.
+ */
+static int
+ipv6cp_cilen(f)
+ fsm *f;
+{
+ ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
+
+#define LENCIVJ(neg) (neg ? CILEN_COMPRESS : 0)
+#define LENCIIFACEID(neg) (neg ? CILEN_IFACEID : 0)
+
+ return (LENCIIFACEID(go->neg_ifaceid) +
+ LENCIVJ(go->neg_vj));
+}
+
+
+/*
+ * ipv6cp_addci - Add our desired CIs to a packet.
+ */
+static void
+ipv6cp_addci(f, ucp, lenp)
+ fsm *f;
+ u_char *ucp;
+ int *lenp;
+{
+ ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
+ int len = *lenp;
+
+#define ADDCIVJ(opt, neg, val) \
+ if (neg) { \
+ int vjlen = CILEN_COMPRESS; \
+ if (len >= vjlen) { \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(vjlen, ucp); \
+ PUTSHORT(val, ucp); \
+ len -= vjlen; \
+ } else \
+ neg = 0; \
+ }
+
+#define ADDCIIFACEID(opt, neg, val1) \
+ if (neg) { \
+ int idlen = CILEN_IFACEID; \
+ if (len >= idlen) { \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(idlen, ucp); \
+ eui64_put(val1, ucp); \
+ len -= idlen; \
+ } else \
+ neg = 0; \
+ }
+
+ ADDCIIFACEID(CI_IFACEID, go->neg_ifaceid, go->ourid);
+
+ ADDCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol);
+
+ *lenp -= len;
+}
+
+
+/*
+ * ipv6cp_ackci - Ack our CIs.
+ *
+ * Returns:
+ * 0 - Ack was bad.
+ * 1 - Ack was good.
+ */
+static int
+ipv6cp_ackci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
+ u_short cilen, citype, cishort;
+ eui64_t ifaceid;
+
+ /*
+ * CIs must be in exactly the same order that we sent...
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+
+#define ACKCIVJ(opt, neg, val) \
+ if (neg) { \
+ int vjlen = CILEN_COMPRESS; \
+ if ((len -= vjlen) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != vjlen || \
+ citype != opt) \
+ goto bad; \
+ GETSHORT(cishort, p); \
+ if (cishort != val) \
+ goto bad; \
+ }
+
+#define ACKCIIFACEID(opt, neg, val1) \
+ if (neg) { \
+ int idlen = CILEN_IFACEID; \
+ if ((len -= idlen) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != idlen || \
+ citype != opt) \
+ goto bad; \
+ eui64_get(ifaceid, p); \
+ if (! eui64_equals(val1, ifaceid)) \
+ goto bad; \
+ }
+
+ ACKCIIFACEID(CI_IFACEID, go->neg_ifaceid, go->ourid);
+
+ ACKCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol);
+
+ /*
+ * If there are any remaining CIs, then this packet is bad.
+ */
+ if (len != 0)
+ goto bad;
+ return (1);
+
+bad:
+ IPV6CPDEBUG(("ipv6cp_ackci: received bad Ack!"));
+ return (0);
+}
+
+/*
+ * ipv6cp_nakci - Peer has sent a NAK for some of our CIs.
+ * This should not modify any state if the Nak is bad
+ * or if IPV6CP is in the OPENED state.
+ *
+ * Returns:
+ * 0 - Nak was bad.
+ * 1 - Nak was good.
+ */
+static int
+ipv6cp_nakci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
+ u_char citype, cilen, *next;
+ u_short cishort;
+ eui64_t ifaceid;
+ ipv6cp_options no; /* options we've seen Naks for */
+ ipv6cp_options try; /* options to request next time */
+
+ BZERO(&no, sizeof(no));
+ try = *go;
+
+ /*
+ * Any Nak'd CIs must be in exactly the same order that we sent.
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+#define NAKCIIFACEID(opt, neg, code) \
+ if (go->neg && \
+ len >= (cilen = CILEN_IFACEID) && \
+ p[1] == cilen && \
+ p[0] == opt) { \
+ len -= cilen; \
+ INCPTR(2, p); \
+ eui64_get(ifaceid, p); \
+ no.neg = 1; \
+ code \
+ }
+
+#define NAKCIVJ(opt, neg, code) \
+ if (go->neg && \
+ ((cilen = p[1]) == CILEN_COMPRESS) && \
+ len >= cilen && \
+ p[0] == opt) { \
+ len -= cilen; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ no.neg = 1; \
+ code \
+ }
+
+ /*
+ * Accept the peer's idea of {our,his} interface identifier, if different
+ * from our idea, only if the accept_{local,remote} flag is set.
+ */
+ NAKCIIFACEID(CI_IFACEID, neg_ifaceid,
+ if (go->accept_local) {
+ while (eui64_iszero(ifaceid) ||
+ eui64_equals(ifaceid, go->hisid)) /* bad luck */
+ eui64_magic(ifaceid);
+ try.ourid = ifaceid;
+ IPV6CPDEBUG(("local LL address %s", llv6_ntoa(ifaceid)));
+ }
+ );
+
+#ifdef IPV6CP_COMP
+ NAKCIVJ(CI_COMPRESSTYPE, neg_vj,
+ {
+ if (cishort == IPV6CP_COMP) {
+ try.vj_protocol = cishort;
+ } else {
+ try.neg_vj = 0;
+ }
+ }
+ );
+#else
+ NAKCIVJ(CI_COMPRESSTYPE, neg_vj,
+ {
+ try.neg_vj = 0;
+ }
+ );
+#endif
+
+ /*
+ * There may be remaining CIs, if the peer is requesting negotiation
+ * on an option that we didn't include in our request packet.
+ * If they want to negotiate about interface identifier, we comply.
+ * If they want us to ask for compression, we refuse.
+ */
+ while (len > CILEN_VOID) {
+ GETCHAR(citype, p);
+ GETCHAR(cilen, p);
+ if( (len -= cilen) < 0 )
+ goto bad;
+ next = p + cilen - 2;
+
+ switch (citype) {
+ case CI_COMPRESSTYPE:
+ if (go->neg_vj || no.neg_vj ||
+ (cilen != CILEN_COMPRESS))
+ goto bad;
+ no.neg_vj = 1;
+ break;
+ case CI_IFACEID:
+ if (go->neg_ifaceid || no.neg_ifaceid || cilen != CILEN_IFACEID)
+ goto bad;
+ try.neg_ifaceid = 1;
+ eui64_get(ifaceid, p);
+ if (go->accept_local) {
+ while (eui64_iszero(ifaceid) ||
+ eui64_equals(ifaceid, go->hisid)) /* bad luck */
+ eui64_magic(ifaceid);
+ try.ourid = ifaceid;
+ }
+ no.neg_ifaceid = 1;
+ break;
+ }
+ p = next;
+ }
+
+ /* If there is still anything left, this packet is bad. */
+ if (len != 0)
+ goto bad;
+
+ /*
+ * OK, the Nak is good. Now we can update state.
+ */
+ if (f->state != OPENED)
+ *go = try;
+
+ return 1;
+
+bad:
+ IPV6CPDEBUG(("ipv6cp_nakci: received bad Nak!"));
+ return 0;
+}
+
+
+/*
+ * ipv6cp_rejci - Reject some of our CIs.
+ */
+static int
+ipv6cp_rejci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
+ u_char cilen;
+ u_short cishort;
+ eui64_t ifaceid;
+ ipv6cp_options try; /* options to request next time */
+
+ try = *go;
+ /*
+ * Any Rejected CIs must be in exactly the same order that we sent.
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+#define REJCIIFACEID(opt, neg, val1) \
+ if (go->neg && \
+ len >= (cilen = CILEN_IFACEID) && \
+ p[1] == cilen && \
+ p[0] == opt) { \
+ len -= cilen; \
+ INCPTR(2, p); \
+ eui64_get(ifaceid, p); \
+ /* Check rejected value. */ \
+ if (! eui64_equals(ifaceid, val1)) \
+ goto bad; \
+ try.neg = 0; \
+ }
+
+#define REJCIVJ(opt, neg, val) \
+ if (go->neg && \
+ p[1] == CILEN_COMPRESS && \
+ len >= p[1] && \
+ p[0] == opt) { \
+ len -= p[1]; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ /* Check rejected value. */ \
+ if (cishort != val) \
+ goto bad; \
+ try.neg = 0; \
+ }
+
+ REJCIIFACEID(CI_IFACEID, neg_ifaceid, go->ourid);
+
+ REJCIVJ(CI_COMPRESSTYPE, neg_vj, go->vj_protocol);
+
+ /*
+ * If there are any remaining CIs, then this packet is bad.
+ */
+ if (len != 0)
+ goto bad;
+ /*
+ * Now we can update state.
+ */
+ if (f->state != OPENED)
+ *go = try;
+ return 1;
+
+bad:
+ IPV6CPDEBUG(("ipv6cp_rejci: received bad Reject!"));
+ return 0;
+}
+
+
+/*
+ * ipv6cp_reqci - Check the peer's requested CIs and send appropriate response.
+ *
+ * Returns: CODE_CONFACK, CODE_CONFNAK or CODE_CONFREJ and input packet modified
+ * appropriately. If reject_if_disagree is non-zero, doesn't return
+ * CODE_CONFNAK; returns CODE_CONFREJ if it can't return CODE_CONFACK.
+ */
+static int
+ipv6cp_reqci(f, p, lenp, dont_nak)
+ fsm *f;
+ u_char *p; /* Requested CIs */
+ int *lenp; /* Length of requested CIs */
+ int dont_nak;
+{
+ ipv6cp_options *wo = &ipv6cp_wantoptions[f->unit];
+ ipv6cp_options *ho = &ipv6cp_hisoptions[f->unit];
+ ipv6cp_options *ao = &ipv6cp_allowoptions[f->unit];
+ ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
+ u_char *p0, *nakp, *rejp, *prev;
+ int ret, newret;
+ int len, cilen, type;
+ eui64_t ifaceid;
+ u_short cishort;
+
+ ret = CODE_CONFACK;
+ rejp = p0 = p;
+ nakp = nak_buffer;
+
+ /*
+ * Reset all his options.
+ */
+ BZERO(ho, sizeof(*ho));
+
+ /*
+ * Process all his options.
+ */
+ for (len = *lenp; len > 0; len -= cilen, p = prev + cilen) {
+ newret = CODE_CONFACK;
+
+ if ((len < 2) || p[1] > len) {
+ /*
+ * RFC 1661 page 40 -- if the option extends beyond the
+ * packet, then discard the entire packet.
+ */
+ return (0);
+ }
+
+ prev = p;
+ GETCHAR(type, p);
+ GETCHAR(cilen, p);
+
+ switch (type) { /* Check CI type */
+ case CI_IFACEID:
+ IPV6CPDEBUG(("ipv6cp: received interface identifier "));
+
+ if (!ao->neg_ifaceid) {
+ newret = CODE_CONFREJ;
+ break;
+ }
+
+ if (cilen != CILEN_IFACEID) {
+ /*
+ * rfc1661, page 40 -- a recongnized option with an
+ * invalid length should be Nak'ed.
+ */
+ newret = CODE_CONFNAK;
+ eui64_copy(wo->hisid, ifaceid);
+ } else {
+
+ /*
+ * If he has no interface identifier, or if we both
+ * have same identifier then NAK it with new idea. In
+ * particular, if we don't know his identifier, but he
+ * does, then accept it.
+ */
+ eui64_get(ifaceid, p);
+ IPV6CPDEBUG(("(%s)", llv6_ntoa(ifaceid)));
+ if (eui64_iszero(ifaceid) && eui64_iszero(go->ourid)) {
+ newret = CODE_CONFREJ; /* Reject CI */
+ break;
+ }
+ /* If we don't like his ID, then nak it. */
+ if (!eui64_iszero(wo->hisid) &&
+ !eui64_equals(ifaceid, wo->hisid) &&
+ eui64_iszero(go->hisid)) {
+ newret = CODE_CONFNAK;
+ eui64_copy(wo->hisid, ifaceid);
+ } else if (eui64_iszero(ifaceid) ||
+ eui64_equals(ifaceid, go->ourid)) {
+ newret = CODE_CONFNAK;
+ /* first time, try option */
+ if (eui64_iszero(go->hisid))
+ eui64_copy(wo->hisid, ifaceid);
+ while (eui64_iszero(ifaceid) ||
+ eui64_equals(ifaceid, go->ourid)) /* bad luck */
+ eui64_magic(ifaceid);
+ }
+ }
+ if (newret == CODE_CONFNAK) {
+ PUTCHAR(type, nakp);
+ PUTCHAR(CILEN_IFACEID, nakp);
+ eui64_put(ifaceid, nakp);
+ }
+
+ ho->neg_ifaceid = 1;
+ eui64_copy(ifaceid, ho->hisid);
+ break;
+
+ case CI_COMPRESSTYPE:
+ IPV6CPDEBUG(("ipv6cp: received COMPRESSTYPE "));
+
+ if (!ao->neg_vj) {
+ newret = CODE_CONFREJ;
+ break;
+ }
+
+ if (cilen != CILEN_COMPRESS) {
+ newret = CODE_CONFNAK;
+ cishort = ao->vj_protocol;
+ } else {
+ GETSHORT(cishort, p);
+ IPV6CPDEBUG(("(%d)", cishort));
+
+#ifdef IPV6CP_COMP
+ if (cishort != IPV6CP_COMP) {
+ newret = CODE_CONFNAK;
+ cishort = IPV6CP_COMP;
+ }
+#else
+ newret = CODE_CONFREJ;
+ break;
+#endif
+ }
+
+ ho->neg_vj = 1;
+ ho->vj_protocol = cishort;
+ break;
+
+ default:
+ newret = CODE_CONFREJ;
+ break;
+ }
+
+ IPV6CPDEBUG((" (%s)\n", CODENAME(newret)));
+
+ /* Cope with confused peers. */
+ if (cilen < 2)
+ cilen = 2;
+
+ /*
+ * If this is an Ack'able CI, but we're sending back a Nak,
+ * don't include this CI.
+ */
+ if (newret == CODE_CONFACK && ret != CODE_CONFACK)
+ continue;
+
+ if (newret == CODE_CONFNAK) {
+ if (dont_nak) {
+ newret = CODE_CONFREJ;
+ } else {
+ /* Ignore subsequent Nak'able things if rejecting. */
+ if (ret == CODE_CONFREJ)
+ continue;
+ ret = CODE_CONFNAK;
+ }
+ }
+
+ if (newret == CODE_CONFREJ) {
+ ret = CODE_CONFREJ;
+ if (prev != rejp)
+ (void) BCOPY(prev, rejp, cilen);
+ rejp += cilen;
+ }
+ }
+
+ /*
+ * If we aren't rejecting this packet, and we want to negotiate
+ * their identifier and they didn't send their identifier, then we
+ * send a NAK with a CI_IFACEID option appended. We assume the
+ * input buffer is long enough that we can append the extra
+ * option safely.
+ */
+ if (ret != CODE_CONFREJ && !ho->neg_ifaceid &&
+ wo->req_ifaceid && !dont_nak) {
+ if (ret == CODE_CONFACK)
+ wo->req_ifaceid = 0;
+ ret = CODE_CONFNAK;
+ PUTCHAR(CI_IFACEID, nakp);
+ PUTCHAR(CILEN_IFACEID, nakp);
+ eui64_put(wo->hisid, nakp);
+ }
+
+ switch (ret) {
+ case CODE_CONFACK:
+ *lenp = p - p0;
+ sys_block_proto(PPP_IPV6);
+ break;
+ case CODE_CONFNAK:
+ *lenp = nakp - nak_buffer;
+ (void) BCOPY(nak_buffer, p0, *lenp);
+ break;
+ case CODE_CONFREJ:
+ *lenp = rejp - p0;
+ break;
+ }
+
+ IPV6CPDEBUG(("ipv6cp: returning Configure-%s", CODENAME(ret)));
+ return (ret); /* Return final code */
+}
+
+
+/*
+ * ipv6_check_options - check that any IP-related options are OK,
+ * and assign appropriate defaults.
+ */
+static void
+ipv6_check_options()
+{
+ ipv6cp_options *wo = &ipv6cp_wantoptions[0];
+
+#if defined(SOL2)
+ /*
+ * Persistent link-local id is only used when user has not explicitly
+ * configure/hard-code the id
+ */
+ if ((wo->use_persistent) && (!wo->opt_local) && (!wo->opt_remote)) {
+
+ /*
+ * On systems where there are no Ethernet interfaces used, there
+ * may be other ways to obtain a persistent id. Right now, it
+ * will fall back to using magic [see eui64_magic] below when
+ * an EUI-48 from MAC address can't be obtained. Other possibilities
+ * include obtaining EEPROM serial numbers, or some other unique
+ * yet persistent number. On Sparc platforms, this is possible,
+ * but too bad there's no standards yet for x86 machines.
+ */
+ if (ether_to_eui64(&wo->ourid)) {
+ wo->opt_local = 1;
+ }
+ }
+#endif
+
+ /*
+ * If ipv6cp-use-ipaddr is used, then both local and remote IPv4
+ * addresses should be specified as options. Otherwise, since
+ * ipcp has yet to negotiate the IPv4 addresses, the interface
+ * identifiers will be based on meaningless values.
+ */
+ if (wo->use_ip) {
+ if ((ipcp_wantoptions[0].accept_local ||
+ ipcp_wantoptions[0].ouraddr == 0) && eui64_iszero(wo->ourid)) {
+ warn("either IPv4 or IPv6 local address should be non-zero for ipv6cp-use-ipaddr");
+ }
+ if ((ipcp_wantoptions[0].accept_remote ||
+ ipcp_wantoptions[0].hisaddr == 0) && eui64_iszero(wo->hisid)) {
+ warn("either IPv4 or IPv6 remote address should be non-zero for ipv6cp-use-ipaddr");
+ }
+ }
+
+ if (!wo->opt_local) { /* init interface identifier */
+ if (wo->use_ip && eui64_iszero(wo->ourid)) {
+ eui64_setlo32(wo->ourid, ntohl(ipcp_wantoptions[0].ouraddr));
+ if (!eui64_iszero(wo->ourid))
+ wo->opt_local = 1;
+ }
+
+ while (eui64_iszero(wo->ourid))
+ eui64_magic(wo->ourid);
+ }
+
+ if (!wo->opt_remote) {
+ if (wo->use_ip && eui64_iszero(wo->hisid)) {
+ eui64_setlo32(wo->hisid, ntohl(ipcp_wantoptions[0].hisaddr));
+ if (!eui64_iszero(wo->hisid))
+ wo->opt_remote = 1;
+ }
+ }
+
+ if (demand && (eui64_iszero(wo->ourid) || eui64_iszero(wo->hisid))) {
+ fatal("local/remote LL address required for demand-dialling\n");
+ }
+}
+
+
+/*
+ * ipv6_demand_conf - configure the interface as though
+ * IPV6CP were up, for use with dial-on-demand.
+ */
+static int
+ipv6_demand_conf(u)
+ int u;
+{
+ ipv6cp_options *wo = &ipv6cp_wantoptions[u];
+
+#if SIF6UPFIRST
+ if (!sif6up(u))
+ return 0;
+#endif
+ if (!sif6addr(u, wo->ourid, wo->hisid))
+ return 0;
+#if !SIF6UPFIRST
+ if (!sif6up(u))
+ return 0;
+#endif
+ if (!sifnpmode(u, PPP_IPV6, NPMODE_QUEUE))
+ return 0;
+
+ notice("ipv6_demand_conf");
+ notice("local LL address %s", llv6_ntoa(wo->ourid));
+ notice("remote LL address %s", llv6_ntoa(wo->hisid));
+
+ return 1;
+}
+
+
+/*
+ * ipv6cp_up - IPV6CP has come UP.
+ *
+ * Configure the IPv6 network interface appropriately and bring it up.
+ */
+static void
+ipv6cp_up(f)
+ fsm *f;
+{
+ ipv6cp_options *ho = &ipv6cp_hisoptions[f->unit];
+ ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
+ ipv6cp_options *wo = &ipv6cp_wantoptions[f->unit];
+
+ IPV6CPDEBUG(("ipv6cp: up"));
+
+ /*
+ * We must have a non-zero LL address for both ends of the link.
+ */
+ if (!ho->neg_ifaceid)
+ ho->hisid = wo->hisid;
+
+ if(!no_ifaceid_neg) {
+ if (eui64_iszero(ho->hisid)) {
+ error("Could not determine remote LL address");
+ ipv6cp_close(f->unit, "Could not determine remote LL address");
+ return;
+ }
+ if (eui64_iszero(go->ourid)) {
+ error("Could not determine local LL address");
+ ipv6cp_close(f->unit, "Could not determine local LL address");
+ return;
+ }
+ if (eui64_equals(go->ourid, ho->hisid)) {
+ error("local and remote LL addresses are equal");
+ ipv6cp_close(f->unit, "local and remote LL addresses are equal");
+ return;
+ }
+ }
+
+#ifdef IPV6CP_COMP
+ /* set tcp compression */
+ if (sif6comp(f->unit, ho->neg_vj) != 1) {
+ ipv6cp_close(f->unit, "Could not enable TCP compression");
+ return;
+ }
+#endif
+
+ /*
+ * If we are doing dial-on-demand, the interface is already
+ * configured, so we put out any saved-up packets, then set the
+ * interface to pass IPv6 packets.
+ */
+ if (demand) {
+ if (! eui64_equals(go->ourid, wo->ourid) ||
+ ! eui64_equals(ho->hisid, wo->hisid)) {
+ if (! eui64_equals(go->ourid, wo->ourid))
+ warn("Local LL address changed to %s",
+ llv6_ntoa(go->ourid));
+ if (! eui64_equals(ho->hisid, wo->hisid))
+ warn("Remote LL address changed to %s",
+ llv6_ntoa(ho->hisid));
+ ipv6cp_clear_addrs(f->unit, go->ourid, ho->hisid);
+
+ /* Set the interface to the new addresses */
+ if (!sif6addr(f->unit, go->ourid, ho->hisid)) {
+ if (debug)
+ warn("sif6addr failed");
+ ipv6cp_close(f->unit, "Interface configuration failed");
+ return;
+ }
+
+ }
+ demand_rexmit(PPP_IPV6);
+ if (sifnpmode(f->unit, PPP_IPV6, NPMODE_PASS) != 1) {
+ ipv6cp_close(f->unit, "Interface configuration failed");
+ return;
+ }
+
+ } else {
+ /*
+ * Set LL addresses
+ */
+#if !SIF6UPFIRST
+ if (!sif6addr(f->unit, go->ourid, ho->hisid)) {
+ if (debug)
+ warn("sif6addr failed");
+ ipv6cp_close(f->unit, "Interface configuration failed");
+ return;
+ }
+#endif
+#if defined(SOL2)
+ /* bring the interface up for IPv6 */
+ if (!sif6up(f->unit)) {
+ if (debug)
+ warn("sifup failed (IPV6)");
+ ipv6cp_close(f->unit, "Interface configuration failed");
+ return;
+ }
+#else
+ if (!sifup(f->unit)) {
+ if (debug)
+ warn("sifup failed (IPV6)");
+ ipv6cp_close(f->unit, "Interface configuration failed");
+ return;
+ }
+#endif
+#if SIF6UPFIRST
+ if (!sif6addr(f->unit, go->ourid, ho->hisid)) {
+ if (debug)
+ warn("sif6addr failed");
+ ipv6cp_close(f->unit, "Interface configuration failed");
+ return;
+ }
+#endif
+ if (sifnpmode(f->unit, PPP_IPV6, NPMODE_PASS) != 1) {
+ ipv6cp_close(f->unit, "Interface configuration failed");
+ return;
+ }
+
+ notice("local LL address %s", llv6_ntoa(go->ourid));
+ notice("remote LL address %s", llv6_ntoa(ho->hisid));
+ }
+
+ np_up(f->unit, PPP_IPV6);
+ ipv6cp_is_up = 1;
+
+ /*
+ * Execute the ipv6-up script, like this:
+ * /etc/ppp/ipv6-up interface tty speed local-LL remote-LL
+ */
+ script_setenv("LLLOCAL", llv6_ntoa(go->ourid), 0);
+ script_setenv("LLREMOTE", llv6_ntoa(ho->hisid), 0);
+ if (ipv6cp_script_state == s_down && ipv6cp_script_pid == 0) {
+ ipv6cp_script_state = s_up;
+ ipv6cp_script(_PATH_IPV6UP);
+ }
+ sys_unblock_proto(PPP_IPV6);
+}
+
+
+/*
+ * ipv6cp_down - IPV6CP has gone DOWN.
+ *
+ * Take the IPv6 network interface down, clear its addresses
+ * and delete routes through it.
+ */
+static void
+ipv6cp_down(f)
+ fsm *f;
+{
+ IPV6CPDEBUG(("ipv6cp: down"));
+ update_link_stats(f->unit);
+ if (ipv6cp_is_up) {
+ ipv6cp_is_up = 0;
+ np_down(f->unit, PPP_IPV6);
+ }
+#ifdef IPV6CP_COMP
+ if (sif6comp(f->unit, 0) != 1) {
+ if (debug)
+ warn("Failed to disable TCP compression.");
+ }
+#endif
+
+ /*
+ * If we are doing dial-on-demand, set the interface
+ * to queue up outgoing packets (for now).
+ */
+ if (demand) {
+ if (sifnpmode(f->unit, PPP_IPV6, NPMODE_QUEUE) != 1) {
+ if (debug)
+ warn("Failed to enable queueing on outgoing packets.");
+ }
+ } else {
+ if (sifnpmode(f->unit, PPP_IPV6, NPMODE_ERROR) != 1) {
+ if (debug)
+ warn("Could not set interface to drop packets.");
+ }
+#if !defined(__linux__) && !(defined(SVR4) && (defined(SNI) || defined(__USLC)))
+#if defined(SOL2)
+ if (sif6down(f->unit) != 1)
+ warn("Couldn not bring interface down.");
+#else
+ if (sifdown(f->unit) != 1)
+ warn("Could not bring interface down.");
+#endif /* defined(SOL2) */
+#endif
+ ipv6cp_clear_addrs(f->unit,
+ ipv6cp_gotoptions[f->unit].ourid,
+ ipv6cp_hisoptions[f->unit].hisid);
+#if defined(__linux__) || (defined(SVR4) && (defined(SNI) || defined(__USLC)))
+ if (sifdown(f->unit) != 1)
+ warn("Could not bring interface down.");
+#endif
+ }
+
+ /* Execute the ipv6-down script */
+ if (ipv6cp_script_state == s_up && ipv6cp_script_pid == 0) {
+ ipv6cp_script_state = s_down;
+ ipv6cp_script(_PATH_IPV6DOWN);
+ }
+}
+
+
+/*
+ * ipv6cp_clear_addrs() - clear the interface addresses, routes,
+ * proxy neighbour discovery entries, etc.
+ */
+static void
+ipv6cp_clear_addrs(unit, ourid, hisid)
+ int unit;
+ eui64_t ourid;
+ eui64_t hisid;
+{
+ if (cif6addr(unit, ourid, hisid) != 1)
+ warn("Could not clear addresses");
+}
+
+
+/*
+ * ipv6cp_finished - possibly shut down the lower layers.
+ */
+static void
+ipv6cp_finished(f)
+ fsm *f;
+{
+ np_finished(f->unit, PPP_IPV6);
+}
+
+
+/*
+ * ipv6cp_script_done - called when the ipv6-up or ipv6-down script
+ * has finished.
+ */
+/*ARGSUSED*/
+static void
+ipv6cp_script_done(arg, status)
+ void *arg;
+ int status;
+{
+ ipv6cp_script_pid = 0;
+ switch (ipv6cp_script_state) {
+ case s_up:
+ if (ipv6cp_fsm[0].state != OPENED) {
+ ipv6cp_script_state = s_down;
+ ipv6cp_script(_PATH_IPV6DOWN);
+ }
+ break;
+ case s_down:
+ if (ipv6cp_fsm[0].state == OPENED) {
+ ipv6cp_script_state = s_up;
+ ipv6cp_script(_PATH_IPV6UP);
+ }
+ break;
+ }
+}
+
+
+/*
+ * ipv6cp_script - Execute a script with arguments
+ * interface-name tty-name speed local-LL remote-LL.
+ */
+static void
+ipv6cp_script(script)
+ char *script;
+{
+ char strspeed[32], strlocal[26], strremote[26];
+ char *argv[8];
+
+ (void) slprintf(strspeed, sizeof (strspeed), "%d", baud_rate);
+ (void) strlcpy(strlocal, llv6_ntoa(ipv6cp_gotoptions[0].ourid),
+ sizeof (strlocal));
+ (void) strlcpy(strremote, llv6_ntoa(ipv6cp_hisoptions[0].hisid),
+ sizeof (strremote));
+
+ argv[0] = script;
+ argv[1] = ifname;
+ argv[2] = devnam;
+ argv[3] = strspeed;
+ argv[4] = strlocal;
+ argv[5] = strremote;
+ argv[6] = ipparam;
+ argv[7] = NULL;
+
+ ipv6cp_script_pid = run_program(script, argv, 0, ipv6cp_script_done, NULL);
+}
+
+static int
+ipv6cp_printpkt(p, plen, printer, arg)
+ u_char *p;
+ int plen;
+ void (*printer) __P((void *, const char *, ...));
+ void *arg;
+{
+ int code, id, len, olen;
+ u_char *pstart, *optend;
+ u_short cishort;
+ eui64_t ifaceid;
+
+ if (plen < HEADERLEN)
+ return 0;
+ pstart = p;
+ GETCHAR(code, p);
+ GETCHAR(id, p);
+ GETSHORT(len, p);
+ if (len < HEADERLEN || len > plen)
+ return 0;
+
+
+ printer(arg, " %s id=0x%x", code_name(code, 1), id);
+ len -= HEADERLEN;
+ switch (code) {
+ case CODE_CONFREQ:
+ case CODE_CONFACK:
+ case CODE_CONFNAK:
+ case CODE_CONFREJ:
+ /* print option list */
+ while (len >= 2) {
+ GETCHAR(code, p);
+ GETCHAR(olen, p);
+ p -= 2;
+ if (olen < 2 || olen > len) {
+ break;
+ }
+ printer(arg, " <");
+ len -= olen;
+ optend = p + olen;
+ switch (code) {
+ case CI_COMPRESSTYPE:
+ if (olen >= CILEN_COMPRESS) {
+ p += 2;
+ GETSHORT(cishort, p);
+ printer(arg, "compress 0x%x", cishort);
+ }
+ break;
+ case CI_IFACEID:
+ if (olen == CILEN_IFACEID) {
+ p += 2;
+ eui64_get(ifaceid, p);
+ printer(arg, "addr %s", llv6_ntoa(ifaceid));
+ }
+ break;
+ }
+ printer(arg, "%8.*B>", optend-p, p);
+ p = optend;
+ }
+ break;
+
+ case CODE_TERMACK:
+ case CODE_TERMREQ:
+ if (len > 0 && *p >= ' ' && *p < 0x7f) {
+ printer(arg, " ");
+ print_string((char *)p, len, printer, arg);
+ p += len;
+ len = 0;
+ }
+ break;
+ }
+
+ /* print the rest of the bytes in the packet */
+ printer(arg, " %32.*B", len, p);
+
+ return p - pstart;
+}
+
+/*
+ * ipv6_active_pkt - see if this IP packet is worth bringing the link up for.
+ * We don't bring the link up for IP fragments or for TCP FIN packets
+ * with no data.
+ */
+#define IP6_HDRLEN 40 /* bytes */
+#define IP6_NHDR_FRAG 44 /* fragment IPv6 header */
+#define IPPROTO_TCP 6
+#define TCP_HDRLEN 20
+#define TH_FIN 0x01
+
+/*
+ * We use these macros because the IP header may be at an odd address,
+ * and some compilers might use word loads to get th_off or ip_hl.
+ */
+
+#define get_ip6nh(x) (((unsigned char *)(x))[6])
+#define get_tcpoff(x) (((unsigned char *)(x))[12] >> 4)
+#define get_tcpflags(x) (((unsigned char *)(x))[13])
+
+static int
+ipv6_active_pkt(pkt, len)
+ u_char *pkt;
+ int len;
+{
+ u_char *tcp;
+
+ len -= PPP_HDRLEN;
+ pkt += PPP_HDRLEN;
+ if (len < IP6_HDRLEN)
+ return 0;
+ if (get_ip6nh(pkt) == IP6_NHDR_FRAG)
+ return 0;
+ if (get_ip6nh(pkt) != IPPROTO_TCP)
+ return 1;
+ if (len < IP6_HDRLEN + TCP_HDRLEN)
+ return 0;
+ tcp = pkt + IP6_HDRLEN;
+ if ((get_tcpflags(tcp) & TH_FIN) != 0 &&
+ len == IP6_HDRLEN + get_tcpoff(tcp) * 4)
+ return 0;
+ return 1;
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppd/ipv6cp.h b/usr/src/cmd/cmd-inet/usr.bin/pppd/ipv6cp.h
new file mode 100644
index 0000000000..7194e2abd5
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppd/ipv6cp.h
@@ -0,0 +1,128 @@
+/*
+ ipv6cp.h - PPP IPV6 Control Protocol.
+ Copyright (C) 1999 Tommi Komulainen <Tommi.Komulainen@iki.fi>
+
+ Redistribution and use in source and binary forms are permitted
+ provided that the above copyright notice and this paragraph are
+ duplicated in all such forms. The name of the author may not be
+ used to endorse or promote products derived from this software
+ without specific prior written permission.
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+*/
+
+/* Original version, based on RFC2023 :
+
+ Copyright (c) 1995, 1996, 1997 Francis.Dupont@inria.fr, INRIA Rocquencourt,
+ Alain.Durand@imag.fr, IMAG,
+ Jean-Luc.Richier@imag.fr, IMAG-LSR.
+
+ Copyright (c) 1998, 1999 Francis.Dupont@inria.fr, GIE DYADE,
+ Alain.Durand@imag.fr, IMAG,
+ Jean-Luc.Richier@imag.fr, IMAG-LSR.
+
+ Ce travail a été fait au sein du GIE DYADE (Groupement d'Intérêt
+ Économique ayant pour membres BULL S.A. et l'INRIA).
+
+ Ce logiciel informatique est disponible aux conditions
+ usuelles dans la recherche, c'est-à-dire qu'il peut
+ être utilisé, copié, modifié, distribué à l'unique
+ condition que ce texte soit conservé afin que
+ l'origine de ce logiciel soit reconnue.
+
+ Le nom de l'Institut National de Recherche en Informatique
+ et en Automatique (INRIA), de l'IMAG, ou d'une personne morale
+ ou physique ayant participé à l'élaboration de ce logiciel ne peut
+ être utilisé sans son accord préalable explicite.
+
+ Ce logiciel est fourni tel quel sans aucune garantie,
+ support ou responsabilité d'aucune sorte.
+ Ce logiciel est dérivé de sources d'origine
+ "University of California at Berkeley" et
+ "Digital Equipment Corporation" couvertes par des copyrights.
+
+ L'Institut d'Informatique et de Mathématiques Appliquées de Grenoble (IMAG)
+ est une fédération d'unités mixtes de recherche du CNRS, de l'Institut National
+ Polytechnique de Grenoble et de l'Université Joseph Fourier regroupant
+ sept laboratoires dont le laboratoire Logiciels, Systèmes, Réseaux (LSR).
+
+ This work has been done in the context of GIE DYADE (joint R & D venture
+ between BULL S.A. and INRIA).
+
+ This software is available with usual "research" terms
+ with the aim of retain credits of the software.
+ Permission to use, copy, modify and distribute this software for any
+ purpose and without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies,
+ and the name of INRIA, IMAG, or any contributor not be used in advertising
+ or publicity pertaining to this material without the prior explicit
+ permission. The software is provided "as is" without any
+ warranties, support or liabilities of any kind.
+ This software is derived from source code from
+ "University of California at Berkeley" and
+ "Digital Equipment Corporation" protected by copyrights.
+
+ Grenoble's Institute of Computer Science and Applied Mathematics (IMAG)
+ is a federation of seven research units funded by the CNRS, National
+ Polytechnic Institute of Grenoble and University Joseph Fourier.
+ The research unit in Software, Systems, Networks (LSR) is member of IMAG.
+*/
+
+/*
+ * Derived from :
+ *
+ *
+ * ipcp.h - IP Control Protocol definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: ipv6cp.h,v 1.3 1999/09/30 19:57:45 masputra Exp $
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Options.
+ */
+#define CI_IFACEID 1 /* Interface Identifier */
+#define CI_COMPRESSTYPE 2 /* Compression Type */
+
+/*
+ *#define IPV6CP_COMP 0x0061
+ */
+typedef struct ipv6cp_options {
+ bool neg_ifaceid; /* Negotiate interface identifier? */
+ bool req_ifaceid; /* Ask peer to send interface identifier? */
+ bool accept_local; /* accept peer's value for iface id? */
+ bool opt_local; /* ourtoken set by option */
+ bool opt_remote; /* histoken set by option */
+ bool use_ip; /* use IP as interface identifier */
+#if defined(SOL2)
+ bool use_persistent; /* use uniquely persistent value for address */
+#endif /* defined(SOL2) */
+ bool neg_vj; /* Van Jacobson Compression? */
+ u_short vj_protocol; /* protocol value to use in VJ option */
+ eui64_t ourid, hisid; /* Interface identifiers */
+} ipv6cp_options;
+
+extern fsm ipv6cp_fsm[];
+extern ipv6cp_options ipv6cp_wantoptions[];
+extern ipv6cp_options ipv6cp_gotoptions[];
+extern ipv6cp_options ipv6cp_allowoptions[];
+extern ipv6cp_options ipv6cp_hisoptions[];
+
+extern struct protent ipv6cp_protent;
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppd/lcp.c b/usr/src/cmd/cmd-inet/usr.bin/pppd/lcp.c
new file mode 100644
index 0000000000..48ca25485a
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppd/lcp.c
@@ -0,0 +1,3184 @@
+/*
+ * lcp.c - PPP Link Control Protocol.
+ *
+ * Copyright 2000-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+#define RCSID "$Id: lcp.c,v 1.54 2000/04/27 03:51:18 masputra Exp $"
+
+/*
+ * TODO:
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#if defined(CHAPMS) || defined(CHAPMSV2)
+#ifdef HAVE_CRYPT_H
+#include <crypt.h>
+#endif
+#ifndef USE_CRYPT
+#include <des.h>
+#endif
+#ifdef SOL2
+#include <errno.h>
+#endif
+#endif
+
+#include "pppd.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "chap.h"
+#include "magic.h"
+#include "patchlevel.h"
+
+#if !defined(lint) && !defined(_lint)
+static const char rcsid[] = RCSID;
+#endif
+
+/*
+ * Special failure codes for logging link failure reasons.
+ */
+bool peer_nak_auth; /* Peer sent nak for our auth request */
+u_short nak_auth_orig; /* Auth proto peer naked */
+u_short nak_auth_proto; /* Auth proto peer suggested instead */
+bool unsolicited_nak_auth; /* Peer asked us to authenticate */
+u_short unsolicit_auth_proto; /* Auth proto peer wants */
+bool peer_reject_auth; /* Peer sent reject for auth */
+u_short reject_auth_proto; /* Protocol that peer rejected */
+bool rejected_peers_auth; /* We sent a reject to the peer */
+u_short rejected_auth_proto; /* Protocol that peer wanted to use */
+bool naked_peers_auth; /* We sent a nak to the peer */
+u_short naked_auth_orig; /* Protocol that we wanted to use */
+u_short naked_auth_proto; /* Protocol that peer wants us to use */
+
+/*
+ * LCP-related command-line options.
+ */
+int lcp_echo_interval = 0; /* Interval between LCP echo-requests */
+int lcp_echo_fails = 0; /* Tolerance to unanswered echo-requests */
+bool lax_recv = 0; /* accept control chars in asyncmap */
+static int use_accm_test = 2; /* use big echo-requests to check ACCM */
+#define ACCM_TEST_FAILS 5
+
+#define _tostr2(x) #x
+#define _tostr(x) _tostr2(x)
+static char identstr[256] = /* Identification string */
+ "ppp-" VERSION "." _tostr(PATCHLEVEL) IMPLEMENTATION;
+static int noident = 0; /* 1 to disable; 2 to reject */
+static int sentident = 0; /* counts the # of ident codes sent */
+
+/* set if we're allowed to send an unsolicited Configure-Nak for MRU. */
+static bool unsolicit_mru;
+
+static int setescape __P((char **, option_t *));
+
+static bool do_msft_workaround = 1;
+static int setasyncmap __P((char **, option_t *));
+
+bool noendpoint = 0; /* don't send/accept endpoint discriminator */
+static int setendpoint __P((char **, option_t *));
+
+static char *callback_strings[] = {
+ "auth", "dialstring", "location", "E.164", "X.500", "", "CBCP", NULL
+};
+
+/* This is used in packet printing even if NEGOTIATE_FCS isn't enabled */
+static char *fcsalt_strings[] = {
+ "null", "crc16", "crc32", NULL
+};
+
+#ifdef NEGOTIATE_FCS
+static int setfcsallow __P((char **, option_t *));
+static int setfcswant __P((char **, option_t *));
+#endif
+
+/* Backward compatibility for Linux */
+#ifndef PPP_MAXMRU
+#define PPP_MTU 1500 /* Default MTU (size of Info field) */
+#define PPP_MAXMTU 65535 - (PPP_HDRLEN + PPP_FCSLEN)
+#define PPP_MINMTU 64
+#define PPP_MAXMRU 65000 /* Largest MRU we allow */
+#define PPP_MINMRU 128
+#endif
+
+static option_t lcp_option_list[] = {
+ /* LCP options */
+ { "noaccomp", o_bool, &lcp_wantoptions[0].neg_accompression,
+ "Disable address/control compression",
+ OPT_A2COPY, &lcp_allowoptions[0].neg_accompression },
+ { "-ac", o_bool, &lcp_wantoptions[0].neg_accompression,
+ "Disable address/control compression",
+ OPT_A2COPY, &lcp_allowoptions[0].neg_accompression },
+ { "default-asyncmap", o_bool, &lcp_wantoptions[0].neg_asyncmap,
+ "Disable asyncmap negotiation",
+ OPT_A2COPY, &lcp_allowoptions[0].neg_asyncmap },
+ { "-am", o_bool, &lcp_wantoptions[0].neg_asyncmap,
+ "Disable asyncmap negotiation",
+ OPT_A2COPY, &lcp_allowoptions[0].neg_asyncmap },
+ { "asyncmap", o_special, (void *)setasyncmap,
+ "Set asyncmap (for received packets)" },
+ { "-as", o_special, (void *)setasyncmap,
+ "Set asyncmap (for received packets)" },
+ { "nomagic", o_bool, &lcp_wantoptions[0].neg_magicnumber,
+ "Disable magic number option (looped-back line detect)",
+ OPT_A2COPY, &lcp_allowoptions[0].neg_magicnumber },
+ { "-mn", o_bool, &lcp_wantoptions[0].neg_magicnumber,
+ "Disable magic number option (looped-back line detect)",
+ OPT_A2COPY, &lcp_allowoptions[0].neg_magicnumber },
+ { "default-mru", o_bool, &lcp_wantoptions[0].neg_mru,
+ "Disable MRU negotiation (use default 1500)",
+ OPT_A2COPY, &lcp_allowoptions[0].neg_mru },
+ { "-mru", o_bool, &lcp_wantoptions[0].neg_mru,
+ "Disable MRU negotiation (use default 1500)",
+ OPT_A2COPY, &lcp_allowoptions[0].neg_mru },
+ { "mru", o_int, &lcp_wantoptions[0].mru,
+ "Set MRU (maximum received packet size) for negotiation",
+ OPT_LIMITS, &lcp_wantoptions[0].neg_mru, PPP_MAXMRU, PPP_MINMRU },
+ { "mtu", o_int, &lcp_allowoptions[0].mru,
+ "Set our MTU", OPT_LIMITS|OPT_A2COPY, &lcp_allowoptions[0].mrru,
+ PPP_MAXMTU, PPP_MINMTU },
+ { "nopcomp", o_bool, &lcp_wantoptions[0].neg_pcompression,
+ "Disable protocol field compression",
+ OPT_A2COPY, &lcp_allowoptions[0].neg_pcompression },
+ { "-pc", o_bool, &lcp_wantoptions[0].neg_pcompression,
+ "Disable protocol field compression",
+ OPT_A2COPY, &lcp_allowoptions[0].neg_pcompression },
+ { "-p", o_bool, &lcp_wantoptions[0].passive,
+ "Set passive mode", 1 },
+ { "passive", o_bool, &lcp_wantoptions[0].passive,
+ "Set passive mode", 1 },
+ { "silent", o_bool, &lcp_wantoptions[0].silent,
+ "Set silent mode", 1 },
+ { "escape", o_special, (void *)setescape,
+ "List of character codes to escape on transmission" },
+ { "lcp-echo-failure", o_int, &lcp_echo_fails,
+ "Number of consecutive echo failures for link failure" },
+ { "lcp-echo-interval", o_int, &lcp_echo_interval,
+ "Set time in seconds between LCP echo requests" },
+ { "no-accm-test", o_int, &use_accm_test,
+ "Disable use of LCP Echo-Request asyncmap checking",
+ OPT_NOARG|OPT_VAL(0) },
+ { "small-accm-test", o_int, &use_accm_test,
+ "Use only small Echo-Requests for asyncmap checking",
+ OPT_NOARG|OPT_VAL(1) },
+ { "lcp-restart", o_int, &lcp_fsm[0].timeouttime,
+ "Set time in seconds between LCP retransmissions" },
+ { "lcp-max-terminate", o_int, &lcp_fsm[0].maxtermtransmits,
+ "Maximum number of LCP terminate-request transmissions" },
+ { "lcp-max-configure", o_int, &lcp_fsm[0].maxconfreqtransmits,
+ "Maximum number of LCP configure-request transmissions" },
+ { "lcp-max-failure", o_int, &lcp_fsm[0].maxnakloops,
+ "Set limit on number of LCP configure-naks" },
+ { "receive-all", o_bool, &lax_recv,
+ "Accept all received control characters", 1 },
+#ifdef HAVE_MULTILINK
+ { "mrru", o_int, &lcp_wantoptions[0].mrru,
+ "Maximum received packet size for multilink bundle",
+ OPT_LIMITS, &lcp_wantoptions[0].neg_mrru, PPP_MAXMRU, PPP_MINMRU },
+ { "mpshortseq", o_bool, &lcp_wantoptions[0].neg_ssnhf,
+ "Use short sequence numbers in multilink headers",
+ OPT_A2COPY | 1, &lcp_allowoptions[0].neg_ssnhf },
+ { "nompshortseq", o_bool, &lcp_wantoptions[0].neg_ssnhf,
+ "Don't use short sequence numbers in multilink headers",
+ OPT_A2COPY, &lcp_allowoptions[0].neg_ssnhf },
+#endif /* HAVE_MULTILINK */
+ { "endpoint", o_special, (void *)setendpoint,
+ "Endpoint discriminator for multilink", },
+ { "noendpoint", o_bool, &noendpoint,
+ "Don't send or accept multilink endpoint discriminator", 1 },
+ { "ident", o_string, identstr,
+ "LCP Identification string", OPT_STATIC, NULL, sizeof(identstr) },
+ { "noident", o_int, &noident,
+ "Disable use of LCP Identification", OPT_INC|OPT_NOARG|1 },
+#ifdef NEGOTIATE_FCS
+ { "default-fcs", o_bool, &lcp_wantoptions[0].neg_fcs,
+ "Disable FCS Alternatives option (use default CRC-16)",
+ OPT_A2COPY, &lcp_allowoptions[0].neg_fcs },
+ { "allow-fcs", o_special, (void *)setfcsallow,
+ "Set allowable FCS types; crc16, crc32, null, or number" },
+ { "fcs", o_special, (void *)setfcswant,
+ "Set FCS type(s) desired; crc16, crc32, null, or number" },
+#endif
+#ifdef MUX_FRAME
+ /*
+ * if pppmux option is turned on, then the parameter to this
+ * is time value in microseconds
+ */
+ { "pppmux", o_int, &lcp_wantoptions[0].pppmux,
+ "Set PPP Multiplexing option timer", OPT_LLIMIT | OPT_A2COPY,
+ &lcp_allowoptions[0].pppmux, 0, 0 },
+#endif
+ {NULL}
+};
+
+/* global vars */
+fsm lcp_fsm[NUM_PPP]; /* LCP fsm structure (global)*/
+lcp_options lcp_wantoptions[NUM_PPP]; /* Options that we want to request */
+lcp_options lcp_gotoptions[NUM_PPP]; /* Options that peer ack'd */
+lcp_options lcp_allowoptions[NUM_PPP]; /* Options we allow peer to request */
+lcp_options lcp_hisoptions[NUM_PPP]; /* Options that we ack'd */
+u_int32_t xmit_accm[NUM_PPP][8]; /* extended transmit ACCM */
+
+/*
+ * These variables allow a plugin to assert limits on the maximum
+ * MRU/MTU values that can be negotiated.
+ */
+int absmax_mru = PPP_MAXMRU;
+int absmax_mtu = PPP_MAXMTU;
+
+static int lcp_echos_pending = 0; /* Number of outstanding echo msgs */
+static int lcp_echo_number = 0; /* ID number of next echo frame */
+static int lcp_echo_timer_running = 0; /* set if a timer is running */
+static int lcp_echo_badreplies = 0; /* number of bad replies from peer */
+/*
+ * The maximum number of bad replies we tolerate before bringing the
+ * link down.
+ */
+#define LCP_ECHO_MAX_BADREPLIES 10
+
+/*
+ * Callbacks for fsm code. (CI = Configuration Information)
+ */
+static void lcp_resetci __P((fsm *)); /* Reset our CI */
+static int lcp_cilen __P((fsm *)); /* Return length of our CI */
+static void lcp_addci __P((fsm *, u_char *, int *)); /* Add our CI to pkt */
+static int lcp_ackci __P((fsm *, u_char *, int)); /* Peer ack'd our CI */
+static int lcp_nakci __P((fsm *, u_char *, int)); /* Peer nak'd our CI */
+static int lcp_rejci __P((fsm *, u_char *, int)); /* Peer rej'd our CI */
+static int lcp_reqci __P((fsm *, u_char *, int *, int)); /* Rcv peer CI */
+static void lcp_up __P((fsm *)); /* We're UP */
+static void lcp_down __P((fsm *)); /* We're DOWN */
+static void lcp_starting __P((fsm *)); /* We need lower layer up */
+static void lcp_finished __P((fsm *)); /* We need lower layer down */
+static int lcp_extcode __P((fsm *, int, int, u_char *, int));
+static void lcp_rprotrej __P((fsm *, u_char *, int));
+static int lcp_coderej __P((fsm *f, int code, int id, u_char *inp, int len));
+
+/*
+ * routines to send LCP echos to peer
+ */
+
+static void lcp_echo_lowerup __P((int));
+static void lcp_echo_lowerdown __P((int));
+static void LcpEchoTimeout __P((void *));
+static int lcp_received_echo_reply __P((fsm *, int, u_char *, int));
+static void LcpSendEchoRequest __P((fsm *));
+static void LcpLinkFailure __P((fsm *));
+static void LcpEchoCheck __P((fsm *));
+
+/*
+ * routines to send and receive additional LCP packets described in
+ * section 1 of rfc1570.
+ */
+static void LcpSendIdentification __P((fsm *));
+static void lcp_received_identification __P((fsm *, int, u_char *, int));
+static void LcpSendTimeRemaining __P((fsm *, u_int32_t));
+static void lcp_timeremaining __P((void *));
+static void lcp_received_timeremain __P((fsm *, int, u_char *, int));
+
+
+static fsm_callbacks lcp_callbacks = { /* LCP callback routines */
+ lcp_resetci, /* Reset our Configuration Information */
+ lcp_cilen, /* Length of our Configuration Information */
+ lcp_addci, /* Add our Configuration Information */
+ lcp_ackci, /* ACK our Configuration Information */
+ lcp_nakci, /* NAK our Configuration Information */
+ lcp_rejci, /* Reject our Configuration Information */
+ lcp_reqci, /* Request peer's Configuration Information */
+ lcp_up, /* Called when fsm reaches OPENED state */
+ lcp_down, /* Called when fsm leaves OPENED state */
+ lcp_starting, /* Called when we want the lower layer up */
+ lcp_finished, /* Called when we want the lower layer down */
+ NULL, /* Retransmission is necessary */
+ lcp_extcode, /* Called to handle LCP-specific codes */
+ "LCP", /* String name of protocol */
+ lcp_coderej, /* Peer rejected a code number */
+};
+
+/*
+ * Protocol entry points.
+ * Some of these are called directly.
+ */
+
+static void lcp_init __P((int));
+static void lcp_input __P((int, u_char *, int));
+static void lcp_protrej __P((int));
+static int lcp_printpkt __P((u_char *, int,
+ void (*) __P((void *, const char *, ...)), void *));
+
+
+struct protent lcp_protent = {
+ PPP_LCP, /* Protocol Number for LCP */
+ lcp_init, /* Initializes LCP */
+ lcp_input, /* Processes a received LCP packet */
+ lcp_protrej, /* Process a received Protocol-reject */
+ lcp_lowerup, /* Called after the serial device has been set up */
+ lcp_lowerdown, /* Called when the link is brought down */
+ lcp_open, /* Called after lcp_lowerup when bringing up the link */
+ lcp_close, /* Called when the link goes down */
+ lcp_printpkt, /* Print a packet in human readable form */
+ NULL, /* Process a received data packet */
+ 1, /* LCP is enabled by default */
+ "LCP", /* Name of the protocol */
+ NULL, /* Name of the corresponding data protocol */
+ lcp_option_list, /* List of LCP command-line options */
+ NULL, /* Assigns default values for options */
+ NULL, /* Configures demand-dial */
+ NULL /* Bring up the link for this packet? */
+};
+
+int lcp_loopbackfail = DEFLOOPBACKFAIL;
+
+/*
+ * Length of each type of configuration option (in octets)
+ */
+#define CILEN_VOID 2
+#define CILEN_CHAR 3
+#define CILEN_SHORT 4 /* CILEN_VOID + 2 */
+#define CILEN_CHAP 5 /* CILEN_VOID + 2 + 1 */
+#define CILEN_LONG 6 /* CILEN_VOID + 4 */
+#define CILEN_LQR 8 /* CILEN_VOID + 2 + 4 */
+#define CILEN_CBCP 3
+
+
+/*
+ * setescape - add chars to the set we escape on transmission.
+ */
+/*ARGSUSED*/
+static int
+setescape(argv, opt)
+ char **argv;
+ option_t *opt;
+{
+ int n, ret;
+ char *p, *endp;
+
+ p = *argv;
+ ret = 1;
+ while (*p != '\0') {
+ n = strtol(p, &endp, 16);
+ if (p == endp) {
+ option_error("escape parameter contains invalid hex number '%s'",
+ p);
+ return 0;
+ }
+ p = endp;
+ if (n < 0 || n == 0x5E || n > 0xFF) {
+ option_error("can't escape character 0x%x", n);
+ ret = 0;
+ } else
+ xmit_accm[0][n >> 5] |= 1 << (n & 0x1F);
+ while (*p == ',' || *p == ' ')
+ ++p;
+ }
+ return ret;
+}
+
+/*
+ * setasyncmap - set async map negotiated
+ */
+/*ARGSUSED*/
+static int
+setasyncmap(argv, opt)
+ char **argv;
+ option_t *opt;
+{
+ u_int32_t val;
+ char *endp;
+
+ val = strtoul(*argv, &endp, 16);
+ if (*argv == endp) {
+ option_error("invalid numeric parameter '%s' for 'asyncmap' option",
+ *argv);
+ return 0;
+ }
+ lcp_wantoptions[0].asyncmap |= val;
+ lcp_wantoptions[0].neg_asyncmap = (~lcp_wantoptions[0].asyncmap != 0);
+ do_msft_workaround = 0;
+ return 1;
+}
+
+/*ARGSUSED*/
+static int
+setendpoint(argv, opt)
+ char **argv;
+ option_t *opt;
+{
+ if (str_to_epdisc(&lcp_wantoptions[0].endpoint, *argv)) {
+ lcp_wantoptions[0].neg_endpoint = 1;
+ return 1;
+ }
+ option_error("Can't parse '%s' as an endpoint discriminator", *argv);
+ return 0;
+}
+
+#ifdef NEGOTIATE_FCS
+static int
+str_to_fcstype(opt,arg)
+ lcp_options *opt;
+ char *arg;
+{
+ char **cpp, *cp;
+ int val, len;
+
+ if (*arg != '\0') {
+ val = 0;
+ while (*arg != '\0') {
+ len = 0;
+ if (isdigit(*arg)) {
+ len = strtol(arg, &cp, 0);
+ if (len < 0 || len > 255 || arg == cp ||
+ (*cp != '\0' && *cp != ','))
+ break;
+ val |= len;
+ len = cp - arg;
+ } else {
+ for (cpp = fcsalt_strings; *cpp != NULL; cpp++) {
+ len = strlen(*cpp);
+ if (strncasecmp(arg, *cpp, len) == 0 &&
+ (arg[len] == '\0' || arg[len] == ','))
+ break;
+ }
+ if (*cpp == NULL)
+ break;
+ val |= 1<<(cpp-fcsalt_strings);
+ }
+ if (arg[len] == '\0') {
+ opt->neg_fcs = 1;
+ opt->fcs_type = val;
+ return (1);
+ }
+ arg += len+1;
+ }
+ }
+ option_error("Can't parse '%s' as an FCS type", arg);
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+setfcsallow(argv, opt)
+ char **argv;
+ option_t *opt;
+{
+ return str_to_fcstype(&lcp_allowoptions[0], *argv);
+}
+
+/*ARGSUSED*/
+static int
+setfcswant(argv, opt)
+ char **argv;
+ option_t *opt;
+{
+ return str_to_fcstype(&lcp_wantoptions[0], *argv);
+}
+#endif
+
+/*
+ * lcp_init - Initialize LCP.
+ */
+static void
+lcp_init(unit)
+ int unit;
+{
+ fsm *f = &lcp_fsm[unit];
+ lcp_options *wo = &lcp_wantoptions[unit];
+ lcp_options *ao = &lcp_allowoptions[unit];
+
+ f->unit = unit;
+ f->protocol = PPP_LCP;
+ f->callbacks = &lcp_callbacks;
+
+ fsm_init(f);
+
+ BZERO(wo, sizeof(*wo));
+ wo->neg_mru = 1;
+ wo->mru = PPP_MRU;
+ wo->neg_asyncmap = 1;
+ wo->chap_mdtype = CHAP_DIGEST_MD5;
+ wo->neg_magicnumber = 1;
+ wo->neg_pcompression = 1;
+ wo->neg_accompression = 1;
+
+ /*
+ * Leave allowed MRU (MTU) at zero; configuration option sets it
+ * non-zero if we should nak for something else.
+ */
+ BZERO(ao, sizeof(*ao));
+ ao->neg_mru = 1;
+ ao->neg_asyncmap = 1;
+ ao->neg_chap = 1;
+#if defined(CHAPMS) || defined(CHAPMSV2)
+#ifdef SOL2
+ /* Check if DES wasn't exported */
+ errno = 0;
+ setkey("\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");
+ if (errno == 0)
+#endif
+ {
+#ifdef CHAPMS
+ ao->neg_mschap = 1;
+#endif
+#ifdef CHAPMSV2
+ ao->neg_mschapv2 = 1;
+#endif
+ }
+#endif
+ ao->chap_mdtype = CHAP_DIGEST_MD5;
+ ao->neg_upap = 1;
+ ao->neg_magicnumber = 1;
+ ao->neg_pcompression = 1;
+ ao->neg_accompression = 1;
+#ifdef CBCP_SUPPORT
+ ao->neg_cbcp = 1;
+#endif
+ ao->neg_endpoint = 1;
+#ifdef NEGOTIATE_FCS
+ ao->neg_fcs = 1;
+ ao->fcs_type = FCSALT_NULL|FCSALT_16|FCSALT_32;
+#endif
+
+ BZERO(xmit_accm[unit], sizeof(xmit_accm[0]));
+ xmit_accm[unit][3] = 0x60000000;
+}
+
+
+/*
+ * lcp_open - LCP is allowed to come up.
+ */
+void
+lcp_open(unit)
+ int unit;
+{
+ fsm *f = &lcp_fsm[unit];
+ lcp_options *wo = &lcp_wantoptions[unit];
+
+ f->flags = 0;
+ if (wo->passive)
+ f->flags |= OPT_PASSIVE;
+ if (wo->silent)
+ f->flags |= OPT_SILENT;
+ fsm_open(f);
+}
+
+
+/*
+ * lcp_close - Take LCP down.
+ */
+void
+lcp_close(unit, reason)
+ int unit;
+ char *reason;
+{
+ fsm *f = &lcp_fsm[unit];
+
+ if (phase != PHASE_DEAD)
+ new_phase(PHASE_TERMINATE);
+ if (f->state == STOPPED && (f->flags & (OPT_PASSIVE|OPT_SILENT))) {
+ /*
+ * This action is not strictly according to the FSM in RFC1548,
+ * but it does mean that the program terminates if you do a
+ * lcp_close() in passive/silent mode when a connection hasn't
+ * been established.
+ */
+ f->state = CLOSED;
+ lcp_finished(f);
+
+ } else
+ fsm_close(&lcp_fsm[unit], reason);
+}
+
+
+/*
+ * lcp_lowerup - The lower layer is up.
+ */
+void
+lcp_lowerup(unit)
+ int unit;
+{
+ lcp_options *wo = &lcp_wantoptions[unit];
+ int mru, mtu;
+
+ mru = PPP_MRU > absmax_mru ? absmax_mru : PPP_MRU;
+ mtu = PPP_MTU > absmax_mtu ? absmax_mtu : PPP_MTU;
+
+ /*
+ * Don't use A/C or protocol compression on transmission,
+ * but accept A/C and protocol compressed packets
+ * if we are going to ask for A/C and protocol compression.
+ */
+ ppp_set_xaccm(unit, xmit_accm[unit]);
+ ppp_send_config(unit, mtu, 0xffffffff, 0, 0);
+ ppp_recv_config(unit, mru, (lax_recv? 0: 0xffffffff),
+ wo->neg_pcompression, wo->neg_accompression);
+#ifdef NEGOTIATE_FCS
+ ppp_send_fcs(unit, FCSALT_16);
+ ppp_recv_fcs(unit, FCSALT_16);
+#endif
+
+ fsm_setpeermru(unit, mtu);
+ lcp_allowoptions[unit].asyncmap = xmit_accm[unit][0];
+
+ fsm_lowerup(&lcp_fsm[unit]);
+}
+
+
+/*
+ * lcp_lowerdown - The lower layer is down.
+ */
+void
+lcp_lowerdown(unit)
+ int unit;
+{
+ fsm_lowerdown(&lcp_fsm[unit]);
+}
+
+
+/*
+ * lcp_input - Input LCP packet.
+ */
+static void
+lcp_input(unit, p, len)
+ int unit;
+ u_char *p;
+ int len;
+{
+ fsm *f = &lcp_fsm[unit];
+
+ fsm_input(f, p, len);
+}
+
+
+/*
+ * lcp_extcode - Handle a LCP-specific code.
+ */
+static int
+lcp_extcode(f, code, id, inp, len)
+ fsm *f;
+ int code, id;
+ u_char *inp;
+ int len;
+{
+ u_char *magp;
+
+ switch( code ){
+ case CODE_PROTREJ:
+ lcp_rprotrej(f, inp, len);
+ break;
+
+ case CODE_ECHOREQ:
+ if (f->state != OPENED)
+ break;
+ magp = inp;
+ PUTLONG(lcp_gotoptions[f->unit].magicnumber, magp);
+ fsm_sdata(f, CODE_ECHOREP, id, inp, len);
+ break;
+
+ case CODE_ECHOREP:
+ if (!lcp_received_echo_reply(f, id, inp, len)) {
+ lcp_echo_badreplies++;
+ if (lcp_echo_badreplies > LCP_ECHO_MAX_BADREPLIES) {
+ LcpLinkFailure(f);
+ lcp_echos_pending = 0;
+ lcp_echo_badreplies = 0;
+ }
+ }
+ break;
+
+ case CODE_DISCREQ:
+ break;
+
+ case CODE_IDENT:
+ /* More than one 'noident' tells us to reject the code number. */
+ if (noident > 1)
+ return 0;
+ lcp_received_identification(f, id, inp, len);
+ break;
+
+ case CODE_TIMEREMAIN:
+ lcp_received_timeremain(f, id, inp, len);
+ break;
+
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * lcp_rprotrej - Receive an Protocol-Reject.
+ *
+ * Figure out which protocol is rejected and inform it.
+ */
+static void
+lcp_rprotrej(f, inp, len)
+ fsm *f;
+ u_char *inp;
+ int len;
+{
+ int i;
+ struct protent *protp;
+ u_short prot;
+
+ if (len < 2) {
+ dbglog("lcp_rprotrej: Rcvd short Protocol-Reject packet!");
+ return;
+ }
+
+ GETSHORT(prot, inp);
+
+ /*
+ * Protocol-Reject packets received in any state other than the LCP
+ * OPENED state SHOULD be silently discarded.
+ */
+ if( f->state != OPENED ){
+ dbglog("Protocol-Reject discarded: LCP in state %s",
+ fsm_state(f->state));
+ return;
+ }
+
+ /*
+ * Upcall the proper Protocol-Reject routine.
+ */
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ if (protp->protocol == prot && protp->enabled_flag) {
+ (*protp->protrej)(f->unit);
+ return;
+ }
+
+ warn("Protocol-Reject for unsupported protocol 0x%x", prot);
+}
+
+
+/*
+ * lcp_protrej - A Protocol-Reject was received.
+ */
+/*ARGSUSED*/
+static void
+lcp_protrej(unit)
+ int unit;
+{
+ /*
+ * Can't reject LCP!
+ */
+ error("Received Protocol-Reject for LCP!");
+}
+
+/*
+ * lcp_coderej - A Code-Reject was received.
+ */
+/*ARGSUSED*/
+static int
+lcp_coderej(f, code, id, inp, len)
+ fsm *f;
+ int code;
+ int id;
+ u_char *inp;
+ int len;
+{
+ /* The peer cannot reject these code numbers. */
+ if (code >= CODE_CONFREQ && code <= CODE_PROTREJ)
+ return 1;
+ switch (code) {
+ case CODE_ECHOREQ:
+ /*
+ * If the peer rejects an Echo-Request, then stop doing that.
+ */
+ if (lcp_echo_timer_running != 0) {
+ UNTIMEOUT (LcpEchoTimeout, f);
+ lcp_echo_timer_running = 0;
+ lcp_echo_interval = 0;
+ }
+ break;
+ }
+ return 0;
+}
+
+/*
+ * lcp_sprotrej - Send a Protocol-Reject for some protocol.
+ */
+void
+lcp_sprotrej(unit, p, len)
+ int unit;
+ u_char *p;
+ int len;
+{
+ /*
+ * Send back the protocol and the information field of the
+ * rejected packet. We only get here if LCP is in the OPENED state.
+ */
+ p += 2;
+ len -= 2;
+
+ fsm_sdata(&lcp_fsm[unit], CODE_PROTREJ, ++lcp_fsm[unit].id,
+ p, len);
+}
+
+
+/*
+ * lcp_resetci - Reset our CI.
+ */
+static void
+lcp_resetci(f)
+ fsm *f;
+{
+ lcp_options *wo = &lcp_wantoptions[f->unit];
+ lcp_options *go = &lcp_gotoptions[f->unit];
+ lcp_options *ao = &lcp_allowoptions[f->unit];
+
+ wo->magicnumber = magic();
+ wo->numloops = 0;
+ sentident = 0;
+ *go = *wo;
+ if (!multilink) {
+ go->neg_mrru = 0;
+ go->neg_ssnhf = 0;
+ }
+ if (noendpoint)
+ ao->neg_endpoint = 0;
+ if (go->mru > absmax_mru)
+ go->mru = absmax_mru;
+ if (ao->mru > absmax_mtu)
+ ao->mru = absmax_mtu;
+ unsolicit_mru = 1;
+ fsm_setpeermru(f->unit, PPP_MTU > absmax_mtu ? absmax_mtu : PPP_MTU);
+ auth_reset(f->unit);
+}
+
+
+/*
+ * lcp_cilen - Return length of our CI.
+ */
+static int
+lcp_cilen(f)
+ fsm *f;
+{
+ lcp_options *go = &lcp_gotoptions[f->unit];
+
+#define LENCIVOID(neg) ((neg) ? CILEN_VOID : 0)
+#define LENCICHAP(neg) ((neg) ? CILEN_CHAP : 0)
+#define LENCICHAR(neg) ((neg) ? CILEN_CHAR : 0)
+#define LENCISHORT(neg) ((neg) ? CILEN_SHORT : 0)
+#define LENCILONG(neg) ((neg) ? CILEN_LONG : 0)
+#define LENCILQR(neg) ((neg) ? CILEN_LQR: 0)
+#define LENCICBCP(neg) ((neg) ? CILEN_CBCP: 0)
+ /*
+ * NB: we only ask for one of CHAP and UPAP, even if we will
+ * accept either.
+ */
+ return (LENCISHORT(go->neg_mru && go->mru != PPP_MRU) +
+ LENCILONG(go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF) +
+ LENCICHAP(go->neg_chap || go->neg_mschap || go->neg_mschapv2) +
+ LENCISHORT(!go->neg_chap && go->neg_upap && !go->neg_mschap &&
+ !go->neg_mschapv2) +
+ LENCILQR(go->neg_lqr) +
+ LENCICBCP(go->neg_cbcp) +
+ LENCILONG(go->neg_magicnumber) +
+ LENCIVOID(go->neg_pcompression) +
+ LENCIVOID(go->neg_accompression) +
+ LENCICHAR(go->neg_fcs) +
+ LENCISHORT(go->neg_mrru) +
+ LENCIVOID(go->neg_ssnhf) +
+#ifdef MUX_FRAME
+ LENCIVOID(go->pppmux) +
+#endif
+ (go->neg_endpoint? CILEN_CHAR + go->endpoint.length: 0));
+}
+
+
+/*
+ * lcp_addci - Add our desired CIs to a packet.
+ */
+static void
+lcp_addci(f, ucp, lenp)
+ fsm *f;
+ u_char *ucp;
+ int *lenp;
+{
+ lcp_options *go = &lcp_gotoptions[f->unit];
+ lcp_options *ho = &lcp_hisoptions[f->unit];
+ u_char *start_ucp = ucp;
+
+#define ADDCIVOID(opt, neg) \
+ if (neg) { \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(CILEN_VOID, ucp); \
+ }
+#define ADDCISHORT(opt, neg, val) \
+ if (neg) { \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(CILEN_SHORT, ucp); \
+ PUTSHORT(val, ucp); \
+ }
+#define ADDCICHAP(opt, neg, val, digest) \
+ if (neg) { \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(CILEN_CHAP, ucp); \
+ PUTSHORT(val, ucp); \
+ PUTCHAR(digest, ucp); \
+ }
+#define ADDCILONG(opt, neg, val) \
+ if (neg) { \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(CILEN_LONG, ucp); \
+ PUTLONG(val, ucp); \
+ }
+#define ADDCILQR(opt, neg, val) \
+ if (neg) { \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(CILEN_LQR, ucp); \
+ PUTSHORT(PPP_LQR, ucp); \
+ PUTLONG(val, ucp); \
+ }
+#define ADDCICHAR(opt, neg, val) \
+ if (neg) { \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(CILEN_CHAR, ucp); \
+ PUTCHAR(val, ucp); \
+ }
+#define ADDCIENDP(opt, neg, class, val, len) \
+ if (neg) { \
+ int i; \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(CILEN_CHAR + len, ucp); \
+ PUTCHAR(class, ucp); \
+ for (i = 0; i < len; ++i) \
+ PUTCHAR(val[i], ucp); \
+ }
+
+ ADDCISHORT(CI_MRU, go->neg_mru && go->mru != PPP_MRU, go->mru);
+ ADDCILONG(CI_ASYNCMAP, go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF,
+ go->asyncmap);
+ /* go->chap_mdtype always points to a useful value */
+ ADDCICHAP(CI_AUTHTYPE, go->neg_chap || go->neg_mschap || go->neg_mschapv2,
+ PPP_CHAP, go->chap_mdtype);
+ ADDCISHORT(CI_AUTHTYPE, !(go->neg_chap || go->neg_mschap ||
+ go->neg_mschapv2) && go->neg_upap, PPP_PAP);
+ /* We can't both say zero for LQR period. */
+ if (f->state == ACKSENT && go->neg_lqr && go->lqr_period == 0 &&
+ ho->neg_lqr && ho->lqr_period == 0)
+ go->lqr_period = 500;
+ ADDCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period);
+ ADDCICHAR(CI_CALLBACK, go->neg_cbcp, CBOP_CBCP);
+ ADDCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber);
+ ADDCIVOID(CI_PCOMPRESSION, go->neg_pcompression);
+ ADDCIVOID(CI_ACCOMPRESSION, go->neg_accompression);
+ ADDCICHAR(CI_FCSALTERN, (go->neg_fcs && go->fcs_type != 0), go->fcs_type);
+ ADDCIENDP(CI_EPDISC, go->neg_endpoint, go->endpoint.class,
+ go->endpoint.value, go->endpoint.length);
+#ifdef MUX_FRAME
+ ADDCIVOID(CI_MUXING, go->pppmux);
+#endif
+ ADDCISHORT(CI_MRRU, go->neg_mrru, go->mrru);
+ ADDCIVOID(CI_SSNHF, go->neg_ssnhf);
+
+ if (ucp - start_ucp != *lenp) {
+ /* this should never happen, because peer_mtu should be 1500 */
+ error("Bug in lcp_addci: wrong length");
+ }
+}
+
+
+/*
+ * lcp_ackci - Ack our CIs.
+ * This should not modify any state if the Ack is bad.
+ *
+ * Returns:
+ * 0 - Ack was bad.
+ * 1 - Ack was good.
+ */
+static int
+lcp_ackci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ lcp_options *go = &lcp_gotoptions[f->unit];
+#ifdef MUX_FRAME
+ lcp_options *ao = &lcp_allowoptions[f->unit];
+#endif
+ u_char cilen, citype, cichar;
+ u_short cishort;
+ u_int32_t cilong;
+
+ /*
+ * CIs must be in exactly the same order that we sent.
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+#define ACKCIVOID(opt, neg) \
+ if (neg) { \
+ if ((len -= CILEN_VOID) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_VOID || \
+ citype != opt) \
+ goto bad; \
+ }
+#define ACKCISHORT(opt, neg, val) \
+ if (neg) { \
+ if ((len -= CILEN_SHORT) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_SHORT || \
+ citype != opt) \
+ goto bad; \
+ GETSHORT(cishort, p); \
+ if (cishort != val) \
+ goto bad; \
+ }
+#define ACKCIAUTH(opt, neg, val) \
+ if (neg) { \
+ if ((len -= CILEN_SHORT) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_SHORT || \
+ citype != opt) \
+ goto bad; \
+ GETSHORT(cishort, p); \
+ if (cishort != val) \
+ goto bad; \
+ peer_nak_auth = 0; \
+ peer_reject_auth = 0; \
+ }
+#define ACKCICHAR(opt, neg, val) \
+ if (neg) { \
+ if ((len -= CILEN_CHAR) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_CHAR || \
+ citype != opt) \
+ goto bad; \
+ GETCHAR(cichar, p); \
+ if (cichar != val) \
+ goto bad; \
+ }
+#define ACKCICHAP(opt, neg, val, digest) \
+ if (neg) { \
+ if ((len -= CILEN_CHAP) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_CHAP || \
+ citype != opt) \
+ goto bad; \
+ GETSHORT(cishort, p); \
+ if (cishort != val) \
+ goto bad; \
+ GETCHAR(cichar, p); \
+ if (cichar != digest) \
+ goto bad; \
+ peer_nak_auth = 0; \
+ peer_reject_auth = 0; \
+ }
+#define ACKCILONG(opt, neg, val) \
+ if (neg) { \
+ if ((len -= CILEN_LONG) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_LONG || \
+ citype != opt) \
+ goto bad; \
+ GETLONG(cilong, p); \
+ if (cilong != val) \
+ goto bad; \
+ }
+#define ACKCILQR(opt, neg, val) \
+ if (neg) { \
+ if ((len -= CILEN_LQR) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_LQR || \
+ citype != opt) \
+ goto bad; \
+ GETSHORT(cishort, p); \
+ if (cishort != PPP_LQR) \
+ goto bad; \
+ GETLONG(cilong, p); \
+ if (cilong != val) \
+ goto bad; \
+ }
+#define ACKCIENDP(opt, neg, class, val, vlen) \
+ if (neg) { \
+ int i; \
+ if ((len -= CILEN_CHAR + vlen) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_CHAR + vlen || \
+ citype != opt) \
+ goto bad; \
+ GETCHAR(cichar, p); \
+ if (cichar != class) \
+ goto bad; \
+ for (i = 0; i < vlen; ++i) { \
+ GETCHAR(cichar, p); \
+ if (cichar != val[i]) \
+ goto bad; \
+ } \
+ }
+
+ ACKCISHORT(CI_MRU, go->neg_mru && go->mru != PPP_MRU, go->mru);
+ ACKCILONG(CI_ASYNCMAP, go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF,
+ go->asyncmap);
+ /* go->chap_mdtype always points to a useful value */
+ ACKCICHAP(CI_AUTHTYPE, go->neg_chap || go->neg_mschap || go->neg_mschapv2,
+ PPP_CHAP, go->chap_mdtype);
+ ACKCIAUTH(CI_AUTHTYPE, !(go->neg_chap || go->neg_mschap ||
+ go->neg_mschapv2) && go->neg_upap, PPP_PAP);
+ ACKCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period);
+ ACKCICHAR(CI_CALLBACK, go->neg_cbcp, CBOP_CBCP);
+ ACKCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber);
+ ACKCIVOID(CI_PCOMPRESSION, go->neg_pcompression);
+ ACKCIVOID(CI_ACCOMPRESSION, go->neg_accompression);
+ ACKCICHAR(CI_FCSALTERN, go->neg_fcs, go->fcs_type);
+ ACKCIENDP(CI_EPDISC, go->neg_endpoint, go->endpoint.class,
+ go->endpoint.value, go->endpoint.length);
+#ifdef MUX_FRAME
+ ACKCIVOID(CI_MUXING, go->pppmux);
+ if (go->pppmux)
+ go->pppmux = ao->pppmux;
+#endif
+ ACKCISHORT(CI_MRRU, go->neg_mrru, go->mrru);
+ ACKCIVOID(CI_SSNHF, go->neg_ssnhf);
+
+ /*
+ * If there are any remaining CIs, then this packet is bad.
+ */
+ if (len != 0)
+ goto bad;
+ return (1);
+bad:
+ dbglog("lcp_acki: received bad Ack!");
+ return (0);
+}
+
+
+/*
+ * lcp_nakci - Peer has sent a NAK for some of our CIs.
+ * This should not modify any state if the Nak is bad
+ * or if LCP is in the OPENED state.
+ *
+ * Returns:
+ * 0 - Nak was bad.
+ * 1 - Nak was good.
+ */
+static int
+lcp_nakci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ lcp_options *go = &lcp_gotoptions[f->unit];
+ lcp_options *wo = &lcp_wantoptions[f->unit];
+ u_char citype, cichar, *next;
+ u_short cishort;
+ u_int32_t cilong;
+ lcp_options no; /* options we've seen Naks for */
+ lcp_options try; /* options to request next time */
+ int looped_back = 0;
+ int cilen;
+
+ BZERO(&no, sizeof(no));
+ try = *go;
+
+ /*
+ * Any Nak'd CIs must be in exactly the same order that we sent.
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+#define NAKCIVOID(opt, neg) \
+ if (go->neg && \
+ len >= CILEN_VOID && \
+ p[1] == CILEN_VOID && \
+ p[0] == opt) { \
+ len -= CILEN_VOID; \
+ INCPTR(CILEN_VOID, p); \
+ no.neg = 1; \
+ try.neg = 0; \
+ }
+#define NAKCICHAR(opt, neg, code) \
+ if (go->neg && \
+ len >= CILEN_CHAR && \
+ p[1] == CILEN_CHAR && \
+ p[0] == opt) { \
+ len -= CILEN_CHAR; \
+ INCPTR(2, p); \
+ GETCHAR(cichar, p); \
+ no.neg = 1; \
+ code \
+ }
+#define NAKCISHORT(opt, neg, code) \
+ if (go->neg && \
+ len >= CILEN_SHORT && \
+ p[1] == CILEN_SHORT && \
+ p[0] == opt) { \
+ len -= CILEN_SHORT; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ no.neg = 1; \
+ code \
+ }
+#define NAKCILONG(opt, neg, code) \
+ if (go->neg && \
+ len >= CILEN_LONG && \
+ p[1] == CILEN_LONG && \
+ p[0] == opt) { \
+ len -= CILEN_LONG; \
+ INCPTR(2, p); \
+ GETLONG(cilong, p); \
+ no.neg = 1; \
+ code \
+ }
+#define NAKCILQR(opt, neg, code) \
+ if (go->neg && \
+ len >= CILEN_LQR && \
+ p[1] == CILEN_LQR && \
+ p[0] == opt) { \
+ len -= CILEN_LQR; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ GETLONG(cilong, p); \
+ no.neg = 1; \
+ code \
+ }
+#define NAKCIENDP(opt, neg) \
+ if (go->neg && \
+ len >= CILEN_CHAR && \
+ p[0] == opt && \
+ p[1] >= CILEN_CHAR && \
+ p[1] <= len) { \
+ len -= p[1]; \
+ INCPTR(p[1], p); \
+ no.neg = 1; \
+ try.neg = 0; \
+ }
+
+ /*
+ * We don't care if they want to send us smaller packets than
+ * we want. Therefore, accept any MRU less than what we asked for,
+ * but then ignore the new value when setting the MRU in the kernel.
+ * If they send us a bigger MRU than what we asked, accept it, up to
+ * the limit of the default MRU we'd get if we didn't negotiate.
+ */
+ if (go->neg_mru && go->mru != PPP_MRU) {
+ NAKCISHORT(CI_MRU, neg_mru,
+ if (cishort <= wo->mru ||
+ (cishort <= PPP_MRU && cishort <= absmax_mru))
+ try.mru = cishort;
+ );
+ }
+
+ /*
+ * Add any characters they want to our (receive-side) asyncmap.
+ */
+ if (go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF) {
+ NAKCILONG(CI_ASYNCMAP, neg_asyncmap,
+ try.asyncmap = go->asyncmap | cilong;
+ );
+ }
+
+ /*
+ * If they've nak'd our authentication-protocol, check whether
+ * they are proposing a different protocol, or a different
+ * hash algorithm for CHAP.
+ */
+ if ((go->neg_chap || go->neg_mschap || go->neg_mschapv2 || go->neg_upap) &&
+ len >= CILEN_SHORT && p[0] == CI_AUTHTYPE && p[1] >= CILEN_SHORT &&
+ p[1] <= len) {
+ cilen = p[1];
+ len -= cilen;
+ INCPTR(2, p);
+ GETSHORT(cishort, p);
+ peer_nak_auth = 1;
+ nak_auth_orig = (go->neg_chap || go->neg_mschap || go->neg_mschapv2) ?
+ PPP_CHAP : PPP_PAP;
+ nak_auth_proto = cishort;
+ if (cishort == PPP_PAP && cilen == CILEN_SHORT) {
+ no.neg_upap = go->neg_upap;
+ /*
+ * If we were asking for CHAP, they obviously don't want to do it.
+ * If we weren't asking for CHAP, then we were asking for PAP,
+ * in which case this Nak is bad.
+ */
+ if (!go->neg_chap && !go->neg_mschap && !go->neg_mschapv2)
+ goto bad;
+ try.neg_chap = 0;
+ try.neg_mschap = 0;
+ try.neg_mschapv2 = 0;
+
+ } else if (cishort == PPP_CHAP && cilen >= CILEN_CHAP) {
+ /* stop asking for that type */
+ switch (go->chap_mdtype) {
+ case CHAP_DIGEST_MD5:
+ no.neg_chap = go->neg_chap;
+ try.neg_chap = 0;
+ break;
+ case CHAP_MICROSOFT:
+ no.neg_mschap = go->neg_mschap;
+ try.neg_mschap = 0;
+ break;
+ case CHAP_MICROSOFT_V2:
+ no.neg_mschapv2 = go->neg_mschapv2;
+ try.neg_mschapv2 = 0;
+ break;
+ }
+ GETCHAR(cichar, p);
+ /* Allow >= on length here for broken and silly peers. */
+ p += cilen - CILEN_CHAP;
+ try.neg_upap = 0;
+ if ((cichar == CHAP_DIGEST_MD5 && wo->neg_chap) ||
+ (cichar == CHAP_MICROSOFT && wo->neg_mschap) ||
+ (cichar == CHAP_MICROSOFT_V2 && wo->neg_mschapv2)) {
+ /* Try his requested algorithm. */
+ try.chap_mdtype = cichar;
+ } else {
+ goto try_another;
+ }
+
+ } else {
+ /*
+ * We don't recognize what they're suggesting.
+ * Stop asking for what we were asking for.
+ */
+ try_another:
+ if (go->neg_chap || go->neg_mschap || go->neg_mschapv2) {
+ switch (go->chap_mdtype) {
+ case CHAP_DIGEST_MD5:
+ try.neg_chap = 0;
+ if (wo->neg_mschap) {
+ try.chap_mdtype = CHAP_MICROSOFT;
+ break;
+ }
+ /*FALLTHROUGH*/
+ case CHAP_MICROSOFT:
+ try.neg_mschap = 0;
+ if (wo->neg_mschapv2) {
+ try.chap_mdtype = CHAP_MICROSOFT_V2;
+ break;
+ }
+ /*FALLTHROUGH*/
+ case CHAP_MICROSOFT_V2:
+ try.neg_mschapv2 = 0;
+ break;
+ }
+ } else
+ try.neg_upap = 0;
+ p += cilen - CILEN_SHORT;
+ }
+ }
+
+ /*
+ * If they can't cope with our link quality protocol, we'll have
+ * to stop asking for LQR. We haven't got any other protocol. If
+ * they Nak the reporting period, then the following logic
+ * applies:
+ * If he suggests zero and go->neg_fcs is true and
+ * ao->lqr_period isn't zero, then take his suggestion. If he
+ * suggests zero otherwise, ignore it. If he suggests a nonzero
+ * value and wo->lqr_period is zero, then take his suggestion. If
+ * he suggests a nonzero value otherwise that's less than
+ * wo->lqr_period, then ignore it.
+ */
+ NAKCILQR(CI_QUALITY, neg_lqr,
+ if (cishort != PPP_LQR)
+ try.neg_lqr = 0;
+ else if (cilong == 0 && go->neg_fcs && wo->lqr_period != 0)
+ try.lqr_period = cilong;
+ else if (cilong != 0 &&
+ (wo->lqr_period == 0 || cilong > wo->lqr_period))
+ try.lqr_period = cilong;
+ );
+
+ /*
+ * Only implementing CBCP...not the rest of the callback options
+ */
+ NAKCICHAR(CI_CALLBACK, neg_cbcp,
+ try.neg_cbcp = 0;
+ );
+
+ /*
+ * Check for a looped-back line.
+ */
+ NAKCILONG(CI_MAGICNUMBER, neg_magicnumber,
+ try.magicnumber = magic();
+ looped_back = 1;
+ );
+
+ /*
+ * Peer shouldn't send Nak for protocol compression or
+ * address/control compression requests; they should send
+ * a Reject instead. If they send a Nak, treat it as a Reject.
+ */
+ NAKCIVOID(CI_PCOMPRESSION, neg_pcompression);
+ NAKCIVOID(CI_ACCOMPRESSION, neg_accompression);
+
+ /*
+ * Remove any FCS types he doesn't like from our (receive-side)
+ * FCS list.
+ */
+ NAKCICHAR(CI_FCSALTERN, neg_fcs, try.fcs_type = go->fcs_type & cichar;);
+
+#ifdef MUX_FRAME
+ /* Nacked MUX option */
+ NAKCIVOID(CI_MUXING, pppmux);
+#endif
+
+ /*
+ * Nak of the endpoint discriminator option is not permitted,
+ * treat it like a reject.
+ */
+ NAKCIENDP(CI_EPDISC, neg_endpoint);
+
+ /*
+ * Nak for MRRU option - accept their value if it is smaller
+ * than the one we want.
+ */
+ if (go->neg_mrru) {
+ NAKCISHORT(CI_MRRU, neg_mrru,
+ if (cishort <= wo->mrru)
+ try.mrru = cishort;
+ );
+ }
+
+ /*
+ * Nak for short sequence numbers shouldn't be sent, treat it
+ * like a reject.
+ */
+ NAKCIVOID(CI_SSNHF, neg_ssnhf);
+
+ /*
+ * There may be remaining CIs, if the peer is requesting negotiation
+ * on an option that we didn't include in our request packet.
+ * If we see an option that we requested, or one we've already seen
+ * in this packet, then this packet is bad.
+ * If we wanted to respond by starting to negotiate on the requested
+ * option(s), we could, but we don't, because except for the
+ * authentication type and quality protocol, if we are not negotiating
+ * an option, it is because we were told not to.
+ * For the authentication type, the Nak from the peer means
+ * `let me authenticate myself with you' which is a bit pointless.
+ * For the quality protocol, the Nak means `ask me to send you quality
+ * reports', but if we didn't ask for them, we don't want them.
+ * An option we don't recognize represents the peer asking to
+ * negotiate some option we don't support, so ignore it.
+ */
+ while (len > CILEN_VOID) {
+ GETCHAR(citype, p);
+ GETCHAR(cilen, p);
+ if (cilen < CILEN_VOID || (len -= cilen) < 0)
+ goto bad;
+ next = p + cilen - 2;
+
+ switch (citype) {
+ case CI_MRU:
+ if ((go->neg_mru && go->mru != PPP_MRU)
+ || no.neg_mru || cilen != CILEN_SHORT)
+ goto bad;
+ GETSHORT(cishort, p);
+ if (cishort < PPP_MRU && cishort < absmax_mru) {
+ try.neg_mru = 1;
+ try.mru = cishort;
+ notice("Peer sent unsolicited Nak for MRU less than default.");
+ }
+ break;
+ case CI_ASYNCMAP:
+ if ((go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF)
+ || no.neg_asyncmap || cilen != CILEN_LONG)
+ goto bad;
+ break;
+ case CI_AUTHTYPE:
+ unsolicited_nak_auth = 1;
+ if (cilen >= CILEN_SHORT) {
+ GETSHORT(unsolicit_auth_proto, p);
+ } else {
+ unsolicit_auth_proto = 0;
+ }
+ if (go->neg_chap || no.neg_chap ||
+ go->neg_mschap || no.neg_mschap ||
+ go->neg_mschapv2 || no.neg_mschapv2 ||
+ go->neg_upap || no.neg_upap)
+ goto bad;
+ break;
+ case CI_MAGICNUMBER:
+ if (go->neg_magicnumber || no.neg_magicnumber ||
+ cilen != CILEN_LONG)
+ goto bad;
+ break;
+ case CI_PCOMPRESSION:
+ if (go->neg_pcompression || no.neg_pcompression
+ || cilen != CILEN_VOID)
+ goto bad;
+ break;
+ case CI_ACCOMPRESSION:
+ if (go->neg_accompression || no.neg_accompression
+ || cilen != CILEN_VOID)
+ goto bad;
+ break;
+ case CI_QUALITY:
+ if (go->neg_lqr || no.neg_lqr || cilen != CILEN_LQR)
+ goto bad;
+ break;
+ case CI_MRRU:
+ if (go->neg_mrru || no.neg_mrru || cilen != CILEN_SHORT)
+ goto bad;
+ break;
+ case CI_SSNHF:
+ if (go->neg_ssnhf || no.neg_ssnhf || cilen != CILEN_VOID)
+ goto bad;
+ try.neg_ssnhf = 1;
+ break;
+ case CI_EPDISC:
+ if (go->neg_endpoint || no.neg_endpoint || cilen < CILEN_CHAR)
+ goto bad;
+ break;
+ case CI_FCSALTERN:
+ if (go->neg_fcs || no.neg_fcs || cilen < CILEN_CHAR)
+ goto bad;
+ break;
+#ifdef MUX_FRAME
+ case CI_MUXING:
+ if (go->pppmux || no.pppmux || cilen < CILEN_VOID)
+ goto bad;
+ break;
+#endif
+ }
+ p = next;
+ }
+
+ /*
+ * OK, the Nak is good. Now we can update state.
+ * If there are any options left we ignore them.
+ */
+ if (f->state != OPENED) {
+ /*
+ * Note: the code once reset try.numloops to zero here if
+ * looped_back wasn't set. This is wrong because a mixture of
+ * looped-back and peer data (possible if half-duplex is used)
+ * will allow the link to come up, and it shouldn't.
+ */
+ if (looped_back) {
+ if (++try.numloops >= lcp_loopbackfail) {
+ notice("Serial line is looped back.");
+ lcp_close(f->unit, "Loopback detected");
+ status = EXIT_LOOPBACK;
+ }
+ }
+ *go = try;
+ }
+
+ return 1;
+
+bad:
+ dbglog("lcp_nakci: received bad Nak!");
+ return 0;
+}
+
+
+/*
+ * lcp_rejci - Peer has Rejected some of our CIs.
+ * This should not modify any state if the Reject is bad
+ * or if LCP is in the OPENED state.
+ *
+ * Returns:
+ * 0 - Reject was bad.
+ * 1 - Reject was good.
+ */
+static int
+lcp_rejci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ lcp_options *go = &lcp_gotoptions[f->unit];
+ u_char cichar;
+ u_short cishort;
+ u_int32_t cilong;
+ lcp_options try; /* options to request next time */
+
+ try = *go;
+
+ /*
+ * Any Rejected CIs must be in exactly the same order that we sent.
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+#define REJCIVOID(opt, neg) \
+ if (go->neg && \
+ len >= CILEN_VOID && \
+ p[1] == CILEN_VOID && \
+ p[0] == opt) { \
+ len -= CILEN_VOID; \
+ INCPTR(CILEN_VOID, p); \
+ try.neg = 0; \
+ }
+#define REJCICHAR(opt, neg, val) \
+ if (go->neg && \
+ len >= CILEN_CHAR && \
+ p[1] == CILEN_CHAR && \
+ p[0] == opt) { \
+ len -= CILEN_CHAR; \
+ INCPTR(2, p); \
+ GETCHAR(cichar, p); \
+ /* Check rejected value. */ \
+ if (cichar != val) \
+ goto bad; \
+ try.neg = 0; \
+ }
+#define REJCISHORT(opt, neg, val) \
+ if (go->neg && \
+ len >= CILEN_SHORT && \
+ p[1] == CILEN_SHORT && \
+ p[0] == opt) { \
+ len -= CILEN_SHORT; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ /* Check rejected value. */ \
+ if (cishort != val) \
+ goto bad; \
+ try.neg = 0; \
+ }
+#define REJCIAUTH(opt, neg, val) \
+ if (go->neg && \
+ len >= CILEN_SHORT && \
+ p[1] == CILEN_SHORT && \
+ p[0] == opt) { \
+ len -= CILEN_SHORT; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ /* Check rejected value. */ \
+ peer_reject_auth = 1; \
+ reject_auth_proto = cishort; \
+ if (cishort != val) \
+ goto bad; \
+ try.neg = 0; \
+ }
+#define REJCILONG(opt, neg, val) \
+ if (go->neg && \
+ len >= CILEN_LONG && \
+ p[1] == CILEN_LONG && \
+ p[0] == opt) { \
+ len -= CILEN_LONG; \
+ INCPTR(2, p); \
+ GETLONG(cilong, p); \
+ /* Check rejected value. */ \
+ if (cilong != val) \
+ goto bad; \
+ try.neg = 0; \
+ }
+#define REJCILQR(opt, neg, val) \
+ if (go->neg && \
+ len >= CILEN_LQR && \
+ p[1] == CILEN_LQR && \
+ p[0] == opt) { \
+ len -= CILEN_LQR; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ GETLONG(cilong, p); \
+ /* Check rejected value. */ \
+ if (cishort != PPP_LQR || cilong != val) \
+ goto bad; \
+ try.neg = 0; \
+ }
+#define REJCICBCP(opt, neg, val) \
+ if (go->neg && \
+ len >= CILEN_CBCP && \
+ p[1] == CILEN_CBCP && \
+ p[0] == opt) { \
+ len -= CILEN_CBCP; \
+ INCPTR(2, p); \
+ GETCHAR(cichar, p); \
+ /* Check rejected value. */ \
+ if (cichar != val) \
+ goto bad; \
+ try.neg = 0; \
+ }
+#define REJCIENDP(opt, neg, class, val, vlen) \
+ if (go->neg && \
+ len >= CILEN_CHAR + vlen && \
+ p[0] == opt && \
+ p[1] == CILEN_CHAR + vlen) { \
+ int i; \
+ len -= CILEN_CHAR + vlen; \
+ INCPTR(2, p); \
+ GETCHAR(cichar, p); \
+ if (cichar != class) \
+ goto bad; \
+ for (i = 0; i < vlen; ++i) { \
+ GETCHAR(cichar, p); \
+ if (cichar != val[i]) \
+ goto bad; \
+ } \
+ try.neg = 0; \
+ }
+
+ /* Received a Configure-Reject, try to send Identification now. */
+ if (!noident && sentident < 3) {
+ LcpSendIdentification(f);
+ sentident++;
+ }
+
+ REJCISHORT(CI_MRU, neg_mru, go->mru);
+ REJCILONG(CI_ASYNCMAP, neg_asyncmap, go->asyncmap);
+
+ /*
+ * There are broken peers (such as unbundled Solaris PPP) that
+ * send Configure-Reject for authentication when they really
+ * intend Configure-Nak. This code works around this problem.
+ */
+ if ((go->neg_chap || go->neg_mschap || go->neg_mschapv2) &&
+ len >= CILEN_CHAP && p[1] == CILEN_CHAP && p[0] == CI_AUTHTYPE) {
+ len -= CILEN_CHAP;
+ INCPTR(2, p);
+ GETSHORT(cishort, p);
+ GETCHAR(cichar, p);
+ peer_reject_auth = 1;
+ reject_auth_proto = cishort;
+ /* Check rejected value. */
+ if (cishort != PPP_CHAP || cichar != go->chap_mdtype)
+ goto bad;
+ /* Disable the one that he rejected */
+ switch (cichar) {
+ case CHAP_DIGEST_MD5:
+ try.neg_chap = 0;
+ break;
+ case CHAP_MICROSOFT:
+ try.neg_mschap = 0;
+ break;
+ case CHAP_MICROSOFT_V2:
+ try.neg_mschapv2 = 0;
+ break;
+ }
+ /* Try another, if we can. */
+ if (try.neg_chap)
+ try.chap_mdtype = CHAP_DIGEST_MD5;
+ else if (try.neg_mschap)
+ try.chap_mdtype = CHAP_MICROSOFT;
+ else
+ try.chap_mdtype = CHAP_MICROSOFT_V2;
+ }
+
+ if (!go->neg_chap && !go->neg_mschap && !go->neg_mschapv2) {
+ REJCIAUTH(CI_AUTHTYPE, neg_upap, PPP_PAP);
+ }
+ REJCILQR(CI_QUALITY, neg_lqr, go->lqr_period);
+ REJCICBCP(CI_CALLBACK, neg_cbcp, CBOP_CBCP);
+ REJCILONG(CI_MAGICNUMBER, neg_magicnumber, go->magicnumber);
+ REJCIVOID(CI_PCOMPRESSION, neg_pcompression);
+ REJCIVOID(CI_ACCOMPRESSION, neg_accompression);
+ REJCICHAR(CI_FCSALTERN, neg_fcs, go->fcs_type);
+#ifdef MUX_FRAME
+ REJCIVOID(CI_MUXING,pppmux);
+#endif
+ REJCIENDP(CI_EPDISC, neg_endpoint, go->endpoint.class,
+ go->endpoint.value, go->endpoint.length);
+ REJCISHORT(CI_MRRU, neg_mrru, go->mrru);
+ REJCIVOID(CI_SSNHF, neg_ssnhf);
+
+ /*
+ * If there are any remaining CIs, then this packet is bad.
+ */
+ if (len != 0)
+ goto bad;
+ /*
+ * Now we can update state.
+ */
+ if (f->state != OPENED)
+ *go = try;
+ return 1;
+
+bad:
+ dbglog("lcp_rejci: received bad Reject!");
+ return 0;
+}
+
+
+/*
+ * lcp_reqci - Check the peer's requested CIs and send appropriate response.
+ *
+ * Returns: CODE_CONFACK, CODE_CONFNAK or CODE_CONFREJ and input
+ * packet modified appropriately. If reject_if_disagree is non-zero,
+ * doesn't return CODE_CONFNAK; returns CODE_CONFREJ if it can't
+ * return CODE_CONFACK.
+ */
+static int
+lcp_reqci(f, p, lenp, dont_nak)
+ fsm *f;
+ u_char *p; /* Requested CIs */
+ int *lenp; /* Length of requested CIs */
+ int dont_nak;
+{
+ lcp_options *wo = &lcp_wantoptions[f->unit];
+ lcp_options *go = &lcp_gotoptions[f->unit];
+ lcp_options *ho = &lcp_hisoptions[f->unit];
+ lcp_options *ao = &lcp_allowoptions[f->unit];
+ int cilen, citype, cichar; /* Parsed len, type, char value */
+ u_short cishort; /* Parsed short value */
+ u_int32_t cilong; /* Parse long value */
+ int ret, newret;
+ u_char *p0, *nakp, *rejp, *prev;
+ int len;
+
+ /*
+ * Loop through options once to find out if peer is offering
+ * Multilink, and repair values as needed.
+ */
+ ao->mru = ao->mrru;
+ p0 = p;
+ for (len = *lenp; len > 0; len -= cilen, p = prev + cilen) {
+ if (len < 2 || p[1] > len) {
+ /*
+ * RFC 1661 page 40 -- if the option extends beyond the
+ * packet, then discard the entire packet.
+ */
+ dbglog("discarding LCP Configure-Request due to truncated option");
+ return (0);
+ }
+ prev = p;
+ GETCHAR(citype, p);
+ GETCHAR(cilen, p);
+ if (citype == CI_MRRU) {
+ if (ao->mrru != 0) {
+ if (ao->mrru+6 > PPP_MTU)
+ ao->mru = PPP_MTU;
+ else
+ ao->mru = ao->mrru + 6;
+ }
+ }
+ if (cilen < 2)
+ cilen = 2;
+ }
+ if (ao->mru > absmax_mtu)
+ ao->mru = absmax_mtu;
+
+ ret = CODE_CONFACK;
+ rejp = p = p0;
+ nakp = nak_buffer;
+
+ /*
+ * Reset all his options.
+ */
+ BZERO(ho, sizeof(*ho));
+
+ /*
+ * Process all his options.
+ */
+ for (len = *lenp; len > 0; len -= cilen, p = prev + cilen) {
+ newret = CODE_CONFACK; /* Assume success */
+
+ prev = p;
+ GETCHAR(citype, p);
+ GETCHAR(cilen, p);
+
+ switch (citype) { /* Check CI type */
+ case CI_MRU:
+ if (!ao->neg_mru) {
+ newret = CODE_CONFREJ;
+ break;
+ }
+
+ if (cilen != CILEN_SHORT) { /* Check CI length */
+ newret = CODE_CONFNAK;
+ cishort = ao->mru;
+ } else {
+ /* extract the MRU from the option */
+ GETSHORT(cishort, p);
+
+ /*
+ * If the offered MRU is less than our desired MTU, we
+ * should nak. This is especially helpful if we're
+ * doing demand-dial, since those queued up packets
+ * might be discarded otherwise.
+ */
+ if (cishort < ao->mru) {
+ newret = CODE_CONFNAK;
+ cishort = ao->mru;
+ }
+ }
+
+ /*
+ * If we're going to send a nak with something less than
+ * or equal to the default PPP MTU, then just reject instead.
+ */
+ if (newret == CODE_CONFNAK && cishort <= PPP_MTU)
+ newret = CODE_CONFREJ;
+
+ if (newret == CODE_CONFNAK) {
+ PUTCHAR(CI_MRU, nakp);
+ PUTCHAR(CILEN_SHORT, nakp);
+ PUTSHORT(cishort, nakp); /* Give him a hint */
+ }
+
+ ho->neg_mru = 1; /* Remember he sent MRU */
+ ho->mru = cishort; /* And remember value */
+ break;
+
+ case CI_ASYNCMAP:
+ if (!ao->neg_asyncmap) {
+ newret = CODE_CONFREJ;
+ break;
+ }
+
+ if (cilen != CILEN_LONG) {
+ newret = CODE_CONFNAK;
+ cilong = 0;
+ } else {
+ GETLONG(cilong, p);
+
+ /*
+ * Asyncmap must have set at least the bits
+ * which are set in lcp_allowoptions[unit].asyncmap.
+ */
+ if ((ao->asyncmap & ~cilong) != 0)
+ newret = CODE_CONFNAK;
+ }
+
+ /*
+ * Workaround for common broken Microsoft software -- if
+ * the peer is sending us a nonzero ACCM, then he *needs*
+ * us to send the same to him. Adjust our Configure-
+ * Request message and restart LCP.
+ */
+ if (do_msft_workaround && (cilong & ~wo->asyncmap)) {
+ dbglog("adjusted requested asyncmap from %X to %X",
+ wo->asyncmap, wo->asyncmap | cilong);
+ do_msft_workaround = 0;
+ wo->neg_asyncmap = 1;
+ wo->asyncmap |= cilong;
+ f->flags &= ~OPT_SILENT;
+ info("possibly broken peer detected; restarting LCP");
+ fsm_lowerdown(f);
+ fsm_lowerup(f);
+ return (0);
+ }
+
+ if (newret == CODE_CONFNAK) {
+ PUTCHAR(CI_ASYNCMAP, nakp);
+ PUTCHAR(CILEN_LONG, nakp);
+ PUTLONG(ao->asyncmap | cilong, nakp);
+ }
+ ho->neg_asyncmap = 1;
+ ho->asyncmap = cilong;
+ break;
+
+ case CI_AUTHTYPE:
+ if (!(ao->neg_upap || ao->neg_chap || ao->neg_mschap ||
+ ao->neg_mschapv2)) {
+ rejected_peers_auth = 1;
+ if (cilen >= CILEN_SHORT) {
+ GETSHORT(rejected_auth_proto, p);
+ } else {
+ rejected_auth_proto = 0;
+ }
+ /*
+ * Reject the option if we're not willing to authenticate.
+ */
+ newret = CODE_CONFREJ;
+ break;
+ }
+ rejected_peers_auth = 0;
+ naked_peers_auth = 0;
+
+ if (cilen >= CILEN_SHORT) {
+ /* Extract the authentication protocol from the option */
+ GETSHORT(cishort, p);
+
+ if (ho->neg_upap || ho->neg_chap || ho->neg_mschap ||
+ ho->neg_mschapv2) {
+ dbglog("Rejecting extra authentication protocol option");
+ newret = CODE_CONFREJ;
+ break;
+ }
+
+ /*
+ * Authtype must be PAP or CHAP.
+ *
+ * Note: if both ao->neg_upap and ao->neg_*chap* are
+ * set, and the peer sends a Configure-Request with
+ * two authenticate-protocol requests, one for CHAP
+ * and one for UPAP, then we will reject the second
+ * request. Whether we end up doing CHAP or UPAP
+ * depends then on the ordering of the CIs in the
+ * peer's Configure-Request.
+ *
+ * We're supposed to list all of the protocols we can
+ * possibly use in the returned Configure-Nak. This
+ * part of RFC 1661 (section 5.3) is in conflict with
+ * the section that says the options shouldn't be
+ * reordered, so it's often ignored.
+ */
+
+ if (cishort == PPP_PAP) {
+ if (ao->neg_upap) {
+ if (cilen != CILEN_SHORT)
+ goto try_pap_anyway;
+ ho->neg_upap = 1;
+ break;
+ }
+ } else if (cishort == PPP_CHAP) {
+ /* Test >= here to allow for broken peers. */
+ if (cilen >= CILEN_CHAP &&
+ (ao->neg_chap || ao->neg_mschap || ao->neg_mschapv2)) {
+ GETCHAR(cichar, p);
+ if (cichar == CHAP_DIGEST_MD5 && ao->neg_chap)
+ ho->neg_chap = 1;
+ else if (cichar == CHAP_MICROSOFT && ao->neg_mschap)
+ ho->neg_mschap = 1;
+ else if (cichar == CHAP_MICROSOFT_V2 &&
+ ao->neg_mschapv2)
+ ho->neg_mschap = 1;
+ if (ho->neg_chap || ho->neg_mschap ||
+ ho->neg_mschapv2) {
+ ho->chap_mdtype = cichar; /* save md type */
+ break;
+ }
+ }
+ }
+ }
+
+ /*
+ * We don't recognize the protocol they're asking for.
+ * Nak it with something we're willing to do.
+ * (At this point we know ao->neg_upap || ao->neg_chap.)
+ */
+ PUTCHAR(CI_AUTHTYPE, nakp);
+ if (ao->neg_chap || ao->neg_mschap || ao->neg_mschapv2) {
+ PUTCHAR(CILEN_CHAP, nakp);
+ PUTSHORT(PPP_CHAP, nakp);
+ PUTCHAR(ao->chap_mdtype, nakp);
+ naked_auth_proto = PPP_CHAP;
+ } else {
+ try_pap_anyway:
+ PUTCHAR(CILEN_SHORT, nakp);
+ PUTSHORT(PPP_PAP, nakp);
+ naked_auth_proto = PPP_PAP;
+ }
+ naked_peers_auth = 1;
+ naked_auth_orig = cishort;
+ newret = CODE_CONFNAK;
+ break;
+
+ case CI_QUALITY:
+ if (!ao->neg_lqr) {
+ newret = CODE_CONFREJ;
+ break;
+ }
+
+ if (cilen != CILEN_LQR) {
+ newret = CODE_CONFNAK;
+ cilong = ao->lqr_period;
+ } else {
+
+ GETSHORT(cishort, p);
+ GETLONG(cilong, p);
+
+ /* Check the LQM protocol */
+ if (cishort != PPP_LQR) {
+ newret = CODE_CONFNAK;
+ }
+
+ /* Check the reporting period; we can't both send zero */
+ if ((cilong == 0 && go->lqr_period == 0) ||
+ cilong < ao->lqr_period) {
+ newret = CODE_CONFNAK;
+ if ((cilong = ao->lqr_period) == 0)
+ cilong = 500;
+ }
+ }
+
+ if (newret == CODE_CONFNAK) {
+ PUTCHAR(CI_QUALITY, nakp);
+ PUTCHAR(CILEN_LQR, nakp);
+ PUTSHORT(PPP_LQR, nakp);
+ PUTLONG(cilong, nakp);
+ }
+
+ ho->neg_lqr = 1;
+ ho->lqr_period = cilong;
+ break;
+
+ case CI_MAGICNUMBER:
+ if (!(ao->neg_magicnumber || go->neg_magicnumber)) {
+ newret = CODE_CONFREJ;
+ break;
+ }
+
+ ho->neg_magicnumber = 1;
+ if (cilen < CILEN_LONG) {
+ /*
+ * If we send Magic-Number, then we must not reject it
+ * when the peer sends it to us, even if his version
+ * looks odd to us. Ack if the cilen is wrong in this
+ * case. If we're not sending Magic-Number, then we don't
+ * much care what his value is anyway.
+ */
+ break;
+ }
+
+ GETLONG(cilong, p);
+ ho->magicnumber = cilong;
+ if (cilen > CILEN_LONG)
+ break;
+
+ /*
+ * He must have a different magic number. Make sure we
+ * give him a good one to use.
+ */
+ while (go->neg_magicnumber && cilong == go->magicnumber) {
+ newret = CODE_CONFNAK;
+ cilong = magic();
+ }
+
+ if (newret == CODE_CONFNAK) {
+ PUTCHAR(CI_MAGICNUMBER, nakp);
+ PUTCHAR(CILEN_LONG, nakp);
+ PUTLONG(cilong, nakp);
+ /*
+ * We don't need to bump the numloops counter here
+ * since it's already done upon reception of a nak.
+ */
+ }
+ break;
+
+ case CI_PCOMPRESSION:
+ if (!ao->neg_pcompression) {
+ newret = CODE_CONFREJ;
+ break;
+ }
+ if (cilen != CILEN_VOID) {
+ newret = CODE_CONFNAK;
+ PUTCHAR(CI_PCOMPRESSION, nakp);
+ PUTCHAR(CILEN_VOID, nakp);
+ }
+ ho->neg_pcompression = 1;
+ break;
+
+ case CI_ACCOMPRESSION:
+ if (!ao->neg_accompression) {
+ newret = CODE_CONFREJ;
+ break;
+ }
+ if (cilen != CILEN_VOID) {
+ newret = CODE_CONFNAK;
+ PUTCHAR(CI_ACCOMPRESSION, nakp);
+ PUTCHAR(CILEN_VOID, nakp);
+ }
+ ho->neg_accompression = 1;
+ break;
+
+ case CI_FCSALTERN:
+ if (!ao->neg_fcs) {
+ newret = CODE_CONFREJ;
+ break;
+ }
+
+ if (cilen != CILEN_CHAR) {
+ newret = CODE_CONFNAK;
+ cichar = ao->fcs_type;
+ } else {
+
+ GETCHAR(cichar, p);
+ /* If he has bits we don't like, tell him to stop. */
+ if (cichar & ~ao->fcs_type) {
+ if ((cichar &= ao->fcs_type) == 0) {
+ newret = CODE_CONFREJ;
+ break;
+ }
+ newret = CODE_CONFNAK;
+ }
+ }
+ if (newret == CODE_CONFNAK) {
+ PUTCHAR(CI_FCSALTERN, nakp);
+ PUTCHAR(CILEN_CHAR, nakp);
+ PUTCHAR(cichar, nakp);
+ }
+ ho->neg_fcs = 1;
+ ho->fcs_type = cichar;
+ break;
+
+ case CI_MRRU:
+ if (!ao->neg_mrru || !multilink) {
+ newret = CODE_CONFREJ;
+ break;
+ }
+ if (cilen != CILEN_SHORT) {
+ newret = CODE_CONFNAK;
+ cishort = ao->mrru;
+ } else {
+ GETSHORT(cishort, p);
+ if (cishort < ao->mrru) {
+ newret = CODE_CONFNAK;
+ cishort = ao->mrru;
+ }
+ }
+
+ if (cishort < PPP_MINMTU) {
+ newret = CODE_CONFNAK;
+ cishort = PPP_MINMTU;
+ }
+
+ if (newret == CODE_CONFNAK) {
+ PUTCHAR(CI_MRRU, nakp);
+ PUTCHAR(CILEN_SHORT, nakp);
+ PUTSHORT(cishort, nakp);
+ }
+
+ ho->neg_mrru = 1;
+ ho->mrru = cishort;
+ break;
+
+ case CI_SSNHF:
+ if (!ao->neg_ssnhf || !multilink) {
+ newret = CODE_CONFREJ;
+ break;
+ }
+ if (cilen != CILEN_VOID) {
+ newret = CODE_CONFNAK;
+ PUTCHAR(CI_SSNHF, nakp);
+ PUTCHAR(CILEN_VOID, nakp);
+ }
+ ho->neg_ssnhf = 1;
+ break;
+
+ case CI_EPDISC:
+ if (!ao->neg_endpoint) {
+ newret = CODE_CONFREJ;
+ break;
+ }
+ if (cilen < CILEN_CHAR || cilen > CILEN_CHAR + MAX_ENDP_LEN) {
+ int i;
+
+ newret = CODE_CONFNAK;
+ PUTCHAR(CI_EPDISC, nakp);
+ PUTCHAR(CILEN_CHAR + ao->endpoint.length, nakp);
+ PUTCHAR(ao->endpoint.class, nakp);
+ for (i = 0; i < ao->endpoint.length; i++)
+ PUTCHAR(ao->endpoint.value[i], nakp);
+ break;
+ }
+ GETCHAR(cichar, p);
+ ho->neg_endpoint = 1;
+ ho->endpoint.class = cichar;
+ ho->endpoint.length = cilen - 3;
+ BCOPY(p, ho->endpoint.value, cilen - 3);
+ break;
+
+#ifdef MUX_FRAME
+ case CI_MUXING:
+ if (ao->pppmux == 0 || cilen != CILEN_VOID) {
+ newret = CODE_CONFREJ;
+ break;
+ }
+ /* remember his option */
+ ho->pppmux = ao->pppmux;
+ break;
+#endif
+
+ default:
+ dbglog("LCP: rejecting unknown option %d", citype);
+ newret = CODE_CONFREJ;
+ break;
+ }
+
+ /* Cope with confused peers. */
+ if (cilen < 2)
+ cilen = 2;
+
+ /*
+ * If this is an Ack'able CI, but we're sending back a Nak,
+ * don't include this CI.
+ */
+ if (newret == CODE_CONFACK && ret != CODE_CONFACK)
+ continue;
+
+ if (newret == CODE_CONFNAK) {
+ /*
+ * Continue naking the Magic Number option until the cows come
+ * home -- rejecting it is wrong.
+ */
+ if (dont_nak && citype != CI_MAGICNUMBER) {
+ newret = CODE_CONFREJ;
+ } else {
+ /* Ignore subsequent Nak'able things if rejecting. */
+ if (ret == CODE_CONFREJ)
+ continue;
+ ret = CODE_CONFNAK;
+ }
+ }
+
+ if (newret == CODE_CONFREJ) {
+ ret = CODE_CONFREJ;
+ if (prev != rejp)
+ BCOPY(prev, rejp, cilen);
+ rejp += cilen;
+ }
+ }
+
+ /*
+ * If the peer hasn't negotiated his MRU, and we'd like an MTU
+ * that's larger than the default, try sending an unsolicited
+ * Nak for what we want.
+ */
+ if (ret != CODE_CONFREJ && !ho->neg_mru && ao->mru > PPP_MTU &&
+ !dont_nak && unsolicit_mru) {
+ unsolicit_mru = 0; /* don't ask again */
+ ret = CODE_CONFNAK;
+ PUTCHAR(CI_MRU, nakp);
+ PUTCHAR(CILEN_SHORT, nakp);
+ PUTSHORT(ao->mru, nakp);
+ }
+
+ switch (ret) {
+ case CODE_CONFACK:
+ *lenp = p - p0;
+ break;
+ case CODE_CONFNAK:
+ /*
+ * Copy the Nak'd options from the nak_buffer to the caller's buffer.
+ */
+ *lenp = nakp - nak_buffer;
+ BCOPY(nak_buffer, p0, *lenp);
+ break;
+ case CODE_CONFREJ:
+ *lenp = rejp - p0;
+
+ /* We're about to send Configure-Reject; send Identification */
+ if (!noident && sentident < 3) {
+ LcpSendIdentification(f);
+ sentident++;
+ }
+ break;
+ }
+
+ LCPDEBUG(("lcp_reqci: returning %s.", code_name(ret, 1)));
+ return (ret); /* Return final code */
+}
+
+
+/*
+ * lcp_up - LCP has come UP.
+ */
+static void
+lcp_up(f)
+ fsm *f;
+{
+ lcp_options *wo = &lcp_wantoptions[f->unit];
+ lcp_options *ho = &lcp_hisoptions[f->unit];
+ lcp_options *go = &lcp_gotoptions[f->unit];
+ lcp_options *ao = &lcp_allowoptions[f->unit];
+ int mru, mtu;
+
+ if (!go->neg_magicnumber)
+ go->magicnumber = 0;
+ if (!ho->neg_magicnumber)
+ ho->magicnumber = 0;
+
+ /*
+ * Set our MTU to the smaller of the MTU we wanted and
+ * the MRU our peer wanted. If we negotiated an MRU,
+ * set our MRU to the larger of value we wanted and
+ * the value we got in the negotiation.
+ */
+ if (ao->mru != 0 && ho->mru > ao->mru)
+ ho->mru = ao->mru;
+ mtu = (ho->neg_mru ? ho->mru: PPP_MRU);
+ if (mtu > absmax_mtu)
+ mtu = absmax_mtu;
+ ppp_send_config(f->unit, mtu,
+ (ho->neg_asyncmap? ho->asyncmap: 0xffffffff),
+ ho->neg_pcompression, ho->neg_accompression);
+ fsm_setpeermru(f->unit, mtu);
+ mru = (go->neg_mru? MAX(wo->mru, go->mru): PPP_MRU);
+ if (mru > absmax_mru)
+ mru = absmax_mru;
+ ppp_recv_config(f->unit, mru,
+ (lax_recv? 0: go->neg_asyncmap? go->asyncmap: 0xffffffff),
+ go->neg_pcompression, go->neg_accompression);
+#ifdef NEGOTIATE_FCS
+ ppp_send_fcs(f->unit, ho->neg_fcs ? ho->fcs_type : FCSALT_16);
+ ppp_recv_fcs(f->unit, go->neg_fcs ? go->fcs_type : FCSALT_16);
+#endif
+#ifdef MUX_FRAME
+ ppp_send_muxoption(f->unit, ho->pppmux);
+ ppp_recv_muxoption(f->unit, go->pppmux);
+#endif
+
+ lcp_echo_lowerup(f->unit); /* Enable echo messages */
+
+ /* LCP is Up; send Identification */
+ if (!noident) {
+ LcpSendIdentification(f);
+ sentident++;
+ }
+
+ link_established(f->unit);
+}
+
+
+/*
+ * lcp_down - LCP has gone DOWN.
+ *
+ * Alert other protocols.
+ */
+static void
+lcp_down(f)
+ fsm *f;
+{
+ int mtu;
+ lcp_options *go = &lcp_gotoptions[f->unit];
+
+ lcp_echo_lowerdown(f->unit);
+
+ link_down(f->unit);
+
+ mtu = PPP_MTU > absmax_mtu ? absmax_mtu : PPP_MTU;
+ ppp_send_config(f->unit, mtu, 0xffffffff, 0, 0);
+ ppp_recv_config(f->unit, (PPP_MRU > absmax_mru ? absmax_mru : PPP_MRU),
+ (go->neg_asyncmap? go->asyncmap: 0xffffffff),
+ go->neg_pcompression, go->neg_accompression);
+#ifdef NEGOTIATE_FCS
+ ppp_send_fcs(f->unit, FCSALT_16);
+ ppp_recv_fcs(f->unit, FCSALT_16);
+#endif
+ fsm_setpeermru(f->unit, mtu);
+}
+
+
+/*
+ * lcp_starting - LCP needs the lower layer up.
+ */
+static void
+lcp_starting(f)
+ fsm *f;
+{
+ link_required(f->unit);
+}
+
+
+/*
+ * lcp_finished - LCP has finished with the lower layer.
+ */
+static void
+lcp_finished(f)
+ fsm *f;
+{
+ link_terminated(f->unit);
+}
+
+
+/*
+ * lcp_printpkt - print the contents of an LCP packet.
+ */
+
+static int
+lcp_printpkt(p, plen, printer, arg)
+ u_char *p;
+ int plen;
+ void (*printer) __P((void *, const char *, ...));
+ void *arg;
+{
+ int code, id, len, olen, i;
+ u_char *pstart, *optend, cichar;
+ u_short cishort;
+ u_int32_t cilong;
+
+ if (plen < HEADERLEN)
+ return 0;
+ pstart = p;
+ GETCHAR(code, p);
+ GETCHAR(id, p);
+ GETSHORT(len, p);
+ if (len < HEADERLEN || len > plen)
+ return 0;
+
+ printer(arg, " %s id=0x%x", code_name(code,1), id);
+ len -= HEADERLEN;
+ switch (code) {
+ case CODE_CONFREQ:
+ case CODE_CONFACK:
+ case CODE_CONFNAK:
+ case CODE_CONFREJ:
+ /* print option list */
+ while (len >= 2) {
+ GETCHAR(code, p);
+ GETCHAR(olen, p);
+ p -= 2;
+ if (olen < 2 || olen > len) {
+ break;
+ }
+ printer(arg, " <");
+ len -= olen;
+ optend = p + olen;
+ switch (code) {
+ case CI_MRU:
+ if (olen >= CILEN_SHORT) {
+ p += 2;
+ GETSHORT(cishort, p);
+ printer(arg, "mru %d", cishort);
+ }
+ break;
+ case CI_ASYNCMAP:
+ if (olen >= CILEN_LONG) {
+ p += 2;
+ GETLONG(cilong, p);
+ printer(arg, "asyncmap 0x%x", cilong);
+ }
+ break;
+ case CI_AUTHTYPE:
+ if (olen >= CILEN_SHORT) {
+ p += 2;
+ printer(arg, "auth ");
+ GETSHORT(cishort, p);
+ switch (cishort) {
+ case PPP_PAP:
+ printer(arg, "pap");
+ break;
+ case PPP_CHAP:
+ printer(arg, "chap");
+ if (p < optend) {
+ switch (*p) {
+ case CHAP_DIGEST_MD5:
+ printer(arg, " MD5");
+ ++p;
+ break;
+ case CHAP_MICROSOFT:
+ printer(arg, " m$oft");
+ ++p;
+ break;
+ case CHAP_MICROSOFT_V2:
+ printer(arg, " m$oft-v2");
+ ++p;
+ break;
+ }
+ }
+ break;
+#ifdef PPP_EAP
+ case PPP_EAP:
+ printer(arg, "eap");
+ break;
+#endif
+ case 0xC027:
+ printer(arg, "spap");
+ break;
+ case 0xC123:
+ printer(arg, "old-spap");
+ break;
+ default:
+ printer(arg, "0x%x", cishort);
+ }
+ }
+ break;
+ case CI_QUALITY:
+ if (olen >= CILEN_SHORT) {
+ p += 2;
+ printer(arg, "quality ");
+ GETSHORT(cishort, p);
+ switch (cishort) {
+ case PPP_LQR:
+ printer(arg, "lqr");
+ break;
+ default:
+ printer(arg, "0x%x", cishort);
+ }
+ }
+ break;
+ case CI_CALLBACK:
+ if (olen >= CILEN_CHAR) {
+ p += 2;
+ printer(arg, "callback ");
+ GETCHAR(cichar, p);
+ if (cichar <= 6 &&
+ *callback_strings[(int)cichar] != '\0') {
+ printer(arg, "%s", callback_strings[(int)cichar]);
+ } else {
+ printer(arg, "0x%x", cichar);
+ }
+ }
+ break;
+ case CI_MAGICNUMBER:
+ if (olen >= CILEN_LONG) {
+ p += 2;
+ GETLONG(cilong, p);
+ printer(arg, "magic 0x%x", cilong);
+ }
+ break;
+ case CI_PCOMPRESSION:
+ if (olen >= CILEN_VOID) {
+ p += 2;
+ printer(arg, "pcomp");
+ }
+ break;
+ case CI_ACCOMPRESSION:
+ if (olen >= CILEN_VOID) {
+ p += 2;
+ printer(arg, "accomp");
+ }
+ break;
+ case CI_FCSALTERN:
+ if (olen >= CILEN_CHAR) {
+ char **cpp;
+ int needcomma = 0;
+
+ p += 2;
+ GETCHAR(cichar, p);
+ for (cpp = fcsalt_strings; *cpp != NULL; cpp++)
+ if (cichar & 1<<(cpp-fcsalt_strings)) {
+ cichar &= ~(1<<(cpp-fcsalt_strings));
+ printer(arg, (needcomma ? ",%s" : "fcs %s"), *cpp);
+ needcomma = 1;
+ }
+ if (cichar != 0 || !needcomma)
+ printer(arg, (needcomma ? ",0x%x" : "fcs 0x%x"),
+ cichar);
+ }
+ break;
+ case CI_NUMBERED:
+ if (olen >= CILEN_SHORT) {
+ p += 2;
+ GETCHAR(cichar, p);
+ printer(arg, "numb win %d", cichar);
+ GETCHAR(cichar, p);
+ printer(arg, " addr %d", cichar);
+ }
+ break;
+ case CI_MRRU:
+ if (olen >= CILEN_SHORT) {
+ p += 2;
+ GETSHORT(cishort, p);
+ printer(arg, "mrru %d", cishort);
+ }
+ break;
+ case CI_SSNHF:
+ if (olen >= CILEN_VOID) {
+ p += 2;
+ printer(arg, "ssnhf");
+ }
+ break;
+ case CI_EPDISC:
+ if (olen >= CILEN_CHAR) {
+ struct epdisc epd;
+ p += 2;
+ GETCHAR(epd.class, p);
+ epd.length = olen - CILEN_CHAR;
+ if (epd.length > MAX_ENDP_LEN)
+ epd.length = MAX_ENDP_LEN;
+ if (epd.length > 0) {
+ BCOPY(p, epd.value, epd.length);
+ p += epd.length;
+ }
+ printer(arg, "endpoint [%s]", epdisc_to_str(&epd));
+ }
+ break;
+ case CI_LINKDISC:
+ if (olen >= CILEN_SHORT) {
+ p += 2;
+ GETSHORT(cishort, p);
+ printer(arg, "linkdisc %d", cishort);
+ }
+ break;
+ case CI_COBS:
+ if (olen >= CILEN_CHAR) {
+ p += 2;
+ GETCHAR(cichar, p);
+ printer(arg, "cobs 0x%x", cichar);
+ }
+ break;
+ case CI_PFXELISION:
+ if (olen >= CILEN_CHAR) {
+ p += 2;
+ printer(arg, "pfx");
+ }
+ break;
+ case CI_MPHDRFMT:
+ if (olen >= CILEN_SHORT) {
+ p += 2;
+ printer(arg, "mphdr ");
+ GETCHAR(cichar, p);
+ switch (cichar) {
+ case 2:
+ printer(arg, "long");
+ break;
+ case 6:
+ printer(arg, "short");
+ break;
+ default:
+ printer(arg, "0x%x", cichar);
+ break;
+ }
+ GETCHAR(cichar, p);
+ printer(arg, " #cl %d", cichar);
+ }
+ break;
+ case CI_I18N:
+ if (olen >= CILEN_LONG) {
+ p += 2;
+ GETLONG(cilong, p);
+ printer(arg, "i18n charset 0x%x", cilong);
+ if (olen > CILEN_LONG) {
+ printer(arg, " lang ");
+ print_string((char *)p, olen-CILEN_LONG, printer, arg);
+ p = optend;
+ }
+ }
+ break;
+ case CI_SDL:
+ if (olen >= CILEN_VOID) {
+ p += 2;
+ printer(arg, "sdl");
+ }
+ break;
+ case CI_MUXING:
+ if (olen >= CILEN_VOID) {
+ p += 2;
+ printer(arg, "mux");
+ }
+ break;
+ }
+ while (p < optend) {
+ GETCHAR(code, p);
+ printer(arg, " %.2x", code);
+ }
+ printer(arg, ">");
+ }
+ break;
+
+ case CODE_TERMACK:
+ case CODE_TERMREQ:
+ if (len > 0 && *p >= ' ' && *p < 0x7f) {
+ printer(arg, " ");
+ print_string((char *)p, len, printer, arg);
+ p += len;
+ len = 0;
+ }
+ break;
+
+ case CODE_ECHOREQ:
+ case CODE_ECHOREP:
+ case CODE_DISCREQ:
+ if (len >= 4) {
+ GETLONG(cilong, p);
+ printer(arg, " magic=0x%x", cilong);
+ len -= 4;
+ }
+ break;
+
+ case CODE_IDENT:
+ if (len >= 4) {
+ GETLONG(cilong, p);
+ printer(arg, " magic=0x%x", cilong);
+ len -= 4;
+ } else
+ break;
+ if (len > 0 && (len > 1 || *p != '\0')) {
+ printer(arg, " ");
+ print_string((char *)p, len, printer, arg);
+ p += len;
+ len = 0;
+ }
+ break;
+
+ case CODE_TIMEREMAIN:
+ if (len >= 4) {
+ GETLONG(cilong, p);
+ printer(arg, " magic=0x%x", cilong);
+ len -= 4;
+ } else
+ break;
+ if (len >= 4) {
+ GETLONG(cilong, p);
+ printer(arg, " seconds=%d", cilong);
+ len -= 4;
+ } else
+ break;
+ if (len > 0 && (len > 1 || *p != '\0')) {
+ printer(arg, " ");
+ print_string((char *)p, len, printer, arg);
+ p += len;
+ len = 0;
+ }
+ break;
+ }
+
+ /* print the rest of the bytes in the packet */
+ for (i = 0; i < len && i < 32; ++i) {
+ GETCHAR(code, p);
+ printer(arg, " %.2x", code);
+ }
+ if (i < len) {
+ printer(arg, " ...");
+ p += len - i;
+ }
+
+ return p - pstart;
+}
+
+/*
+ * Time to shut down the link because there is nothing out there.
+ */
+
+static void
+LcpLinkFailure (f)
+ fsm *f;
+{
+ char *close_message;
+
+ if (f->state == OPENED) {
+ /*
+ * If this is an asynchronous line and we've missed all of
+ * the initial echo requests, then this is probably due to
+ * a bad ACCM.
+ */
+ if (!sync_serial && lcp_echos_pending >= ACCM_TEST_FAILS &&
+ lcp_echo_number <= ACCM_TEST_FAILS && use_accm_test != 0) {
+ notice("Peer not responding to initial Echo-Requests.");
+ notice("Negotiated asyncmap may be incorrect for this link.");
+ close_message = "Peer not responding; perhaps bad asyncmap";
+ } else if (lcp_echo_fails != 0 &&
+ lcp_echos_pending >= lcp_echo_fails) {
+ info("No response to %d echo-requests", lcp_echos_pending);
+ notice("Serial link appears to be disconnected.");
+ close_message = "Peer not responding";
+ } else {
+ info("Received %d bad echo-replies", lcp_echo_badreplies);
+ close_message = "Receiving malformed Echo-Replies";
+ }
+
+ lcp_close(f->unit, close_message);
+ status = EXIT_PEER_DEAD;
+ }
+}
+
+/*
+ * Timer expired for the LCP echo requests from this process.
+ */
+
+static void
+LcpEchoCheck (f)
+ fsm *f;
+{
+ if (f->state != OPENED || lcp_echo_interval == 0)
+ return;
+
+ LcpSendEchoRequest (f);
+
+ /*
+ * Start the timer for the next interval.
+ */
+ if (lcp_echo_timer_running)
+ warn("assertion lcp_echo_timer_running==0 failed");
+ TIMEOUT (LcpEchoTimeout, f, lcp_echo_interval);
+ lcp_echo_timer_running = 1;
+}
+
+/*
+ * LcpEchoTimeout - Timer expired on the LCP echo
+ */
+
+static void
+LcpEchoTimeout (arg)
+ void *arg;
+{
+ if (lcp_echo_timer_running != 0) {
+ lcp_echo_timer_running = 0;
+ LcpEchoCheck ((fsm *) arg);
+ }
+}
+
+/*
+ * LcpEchoReply - LCP has received a reply to the echo
+ */
+/*ARGSUSED*/
+static int
+lcp_received_echo_reply (f, id, inp, len)
+ fsm *f;
+ int id;
+ u_char *inp;
+ int len;
+{
+ u_int32_t magic;
+ static int sayonce = 1;
+
+ /* Check the magic number - don't count replies from ourselves. */
+ if (len < 4) {
+ dbglog("lcp: received short Echo-Reply, length %d", len);
+ return (0);
+ }
+ GETLONG(magic, inp);
+ if (lcp_gotoptions[f->unit].neg_magicnumber &&
+ magic == lcp_gotoptions[f->unit].magicnumber) {
+ warn("appear to have received our own echo-reply!");
+ return (0);
+ }
+
+ /* Reset the number of outstanding echo frames */
+ lcp_echos_pending = 0;
+
+ if (!sync_serial && lcp_echo_number <= ACCM_TEST_FAILS && sayonce &&
+ use_accm_test != 0) {
+ dbglog("lcp: validated asyncmap setting");
+ sayonce = 0;
+ if (lcp_echo_fails == 0)
+ lcp_echo_interval = 0;
+ }
+ return (1);
+}
+
+/*
+ * LcpSendEchoRequest - Send an echo request frame to the peer
+ */
+
+static void
+LcpSendEchoRequest (f)
+ fsm *f;
+{
+ u_int32_t lcp_magic;
+ u_char pkt[4+256], *pktp;
+ int i;
+
+ /*
+ * Detect the failure of the peer at this point.
+ */
+ if ((lcp_echo_fails != 0 && lcp_echos_pending >= lcp_echo_fails) ||
+ (!sync_serial && lcp_echos_pending >= ACCM_TEST_FAILS &&
+ use_accm_test != 0)) {
+ LcpLinkFailure(f);
+ lcp_echos_pending = 0;
+ lcp_echo_badreplies = 0;
+ }
+
+ /*
+ * Make and send the echo request frame.
+ */
+ if (f->state == OPENED) {
+ lcp_magic = lcp_gotoptions[f->unit].magicnumber;
+ pktp = pkt;
+ PUTLONG(lcp_magic, pktp);
+ /* Send some test packets so we can fail the link early. */
+ if (!sync_serial && lcp_echo_number <= ACCM_TEST_FAILS) {
+ switch (use_accm_test) {
+ case 1:
+ /* Only the characters covered by negotiated ACCM */
+ for (i = 0; i < 32; i++)
+ *pktp++ = i;
+ break;
+ case 2:
+ /* All characters */
+ for (i = 0; i < 256; i++)
+ *pktp++ = i;
+ break;
+ }
+ }
+ fsm_sdata(f, CODE_ECHOREQ, lcp_echo_number++ & 0xFF, pkt, pktp - pkt);
+ ++lcp_echos_pending;
+ }
+}
+
+/*
+ * lcp_echo_lowerup - Start the timer for the LCP frame
+ */
+
+static void
+lcp_echo_lowerup (unit)
+ int unit;
+{
+ fsm *f = &lcp_fsm[unit];
+
+ /* Clear the parameters for generating echo frames */
+ lcp_echos_pending = 0;
+ lcp_echo_number = 0;
+ lcp_echo_timer_running = 0;
+
+ /* If a timeout interval is specified then start the timer */
+ LcpEchoCheck(f);
+}
+
+/*
+ * lcp_echo_lowerdown - Stop the timer for the LCP frame
+ */
+
+static void
+lcp_echo_lowerdown (unit)
+ int unit;
+{
+ fsm *f = &lcp_fsm[unit];
+
+ if (lcp_echo_timer_running != 0) {
+ UNTIMEOUT (LcpEchoTimeout, f);
+ lcp_echo_timer_running = 0;
+ }
+}
+
+/*
+ * LcpSendIdentification - Send LCP Identification string to peer.
+ */
+
+static void
+LcpSendIdentification (f)
+ fsm *f;
+{
+ u_int32_t lcp_magic;
+ u_char pkt[4 + sizeof(identstr)], *pktp;
+ int idlen;
+
+ /*
+ * Make and send the Identification frame.
+ */
+ if (f->state == OPENED)
+ lcp_magic = lcp_gotoptions[f->unit].magicnumber;
+ else
+ lcp_magic = 0;
+
+ pktp = pkt;
+ PUTLONG(lcp_magic, pktp);
+ idlen = strlen(identstr);
+ BCOPY(identstr, pktp, idlen);
+ INCPTR(idlen, pktp);
+ fsm_sdata(f, CODE_IDENT, ++f->id, pkt, pktp - pkt);
+}
+
+/*ARGSUSED*/
+static void
+lcp_received_identification (f, id, inp, len)
+ fsm *f;
+ int id;
+ u_char *inp;
+ int len;
+{
+ u_int32_t magic;
+
+ /* Check the magic number - don't count replies from ourselves. */
+ if (len < 4) {
+ dbglog("%s: received short Identification; %d < 4", len);
+ return;
+ }
+ GETLONG(magic, inp);
+ len -= 4;
+ if (lcp_gotoptions[f->unit].neg_magicnumber && f->state == OPENED &&
+ magic == lcp_gotoptions[f->unit].magicnumber) {
+ warn("appear to have received our own Identification!");
+ return;
+ }
+ if (len > 0 && (len > 1 || *inp != '\0'))
+ notice("Peer Identification: %0.*v", len, inp);
+}
+
+/*
+ * Send a Time-Remaining LCP packet. We don't include a message.
+ */
+static void
+LcpSendTimeRemaining(f, time_remaining)
+ fsm *f;
+ u_int32_t time_remaining;
+{
+ u_int32_t lcp_magic;
+ u_char pkt[8];
+ u_char *pktp;
+
+ if (f->state != OPENED)
+ return;
+
+ lcp_magic = lcp_gotoptions[f->unit].magicnumber;
+ pktp = pkt;
+ PUTLONG(lcp_magic, pktp);
+ PUTLONG(time_remaining, pktp);
+ fsm_sdata(f, CODE_TIMEREMAIN, ++f->id, pkt, pktp - pkt);
+}
+
+/*ARGSUSED*/
+static void
+lcp_received_timeremain(f, id, inp, len)
+ fsm *f;
+ int id;
+ u_char *inp;
+ int len;
+{
+ u_int32_t magic;
+ u_int32_t time_remaining;
+
+ /* Check the magic number - don't count replies from ourselves. */
+ if (len < 8) {
+ dbglog("%s: received short Time-Remain; %d < 8", len);
+ return;
+ }
+ GETLONG(magic, inp);
+ if (lcp_gotoptions[f->unit].neg_magicnumber && f->state == OPENED &&
+ magic == lcp_gotoptions[f->unit].magicnumber) {
+ warn("appear to have received our own Time-Remain!");
+ return;
+ }
+ GETLONG(time_remaining, inp);
+ if (len > 8) {
+ notice("%d seconds remain: \"%.*s\"", time_remaining,
+ len-8, inp);
+ } else {
+ notice("Time Remaining: %d seconds", time_remaining);
+ }
+}
+
+/*
+ * lcp_timeremaining - timeout handler which sends LCP Time-Remaining
+ * packet.
+ */
+static void
+lcp_timeremaining(arg)
+ void *arg;
+{
+ struct lcp_timer *lt = (struct lcp_timer *)arg;
+ u_int32_t time_remaining;
+ int unit;
+
+ unit = lt->unit;
+ time_remaining = lt->tr;
+ LcpSendTimeRemaining(&lcp_fsm[unit], time_remaining);
+ free(lt);
+}
+
+/*
+ * lcp_settimeremaining - set a timeout to send an LCP Time-Remaining
+ * packet. The first argument, connecttime, is the time remaining
+ * at the time this function is called. The second argument is the
+ * desired time remaining when the packet should be sent out.
+ */
+void
+lcp_settimeremaining(unit, connecttime, time_remaining)
+ int unit;
+ u_int32_t connecttime;
+ u_int32_t time_remaining;
+{
+ struct lcp_timer *lt;
+
+ if (connecttime == time_remaining) {
+ LcpSendTimeRemaining(&lcp_fsm[unit], time_remaining);
+ } else {
+ lt = (struct lcp_timer *)malloc(sizeof (struct lcp_timer));
+ lt->unit = unit;
+ lt->tr = time_remaining;
+ TIMEOUT(lcp_timeremaining, (void *)lt, connecttime - time_remaining);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppd/lcp.h b/usr/src/cmd/cmd-inet/usr.bin/pppd/lcp.h
new file mode 100644
index 0000000000..a58d35f640
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppd/lcp.h
@@ -0,0 +1,181 @@
+/*
+ * lcp.h - Link Control Protocol definitions.
+ *
+ * Copyright (c) 2000 by Sun Microsystems, Inc.
+ * All rights reserved.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: lcp.h,v 1.15 2000/04/04 07:06:51 paulus Exp $
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifndef __LCP_H_
+#define __LCP_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Options.
+ */
+#define CI_MRU 1 /* Maximum Receive Unit */
+#define CI_ASYNCMAP 2 /* Async Control Character Map */
+#define CI_AUTHTYPE 3 /* Authentication Type */
+#define CI_QUALITY 4 /* Quality Protocol */
+#define CI_MAGICNUMBER 5 /* Magic Number */
+#define CI_PCOMPRESSION 7 /* Protocol Field Compression */
+#define CI_ACCOMPRESSION 8 /* Address/Control Field Compression */
+#define CI_FCSALTERN 9 /* FCS Alternatives */
+#define CI_NUMBERED 11 /* Numbered Mode */
+#define CI_CALLBACK 13 /* callback */
+#define CI_MRRU 17 /* max reconstructed receive unit; multilink */
+#define CI_SSNHF 18 /* short sequence numbers for multilink */
+#define CI_EPDISC 19 /* endpoint discriminator */
+#define CI_LINKDISC 23 /* Link Discriminator (BACP) */
+#define CI_COBS 25 /* Consistent Overhead Byte Stuffing */
+#define CI_PFXELISION 26 /* Prefix Elision */
+#define CI_MPHDRFMT 27 /* Multilink Header Format */
+#define CI_I18N 28 /* Internationalization */
+#define CI_SDL 29 /* Simple Data Link */
+#define CI_MUXING 30 /* PPP Muxing */
+
+/*
+ * LCP-specific packet types.
+ */
+#define CODE_PROTREJ 8 /* Protocol Reject */
+#define CODE_ECHOREQ 9 /* Echo Request */
+#define CODE_ECHOREP 10 /* Echo Reply */
+#define CODE_DISCREQ 11 /* Discard Request */
+#define CODE_IDENT 12 /* Identification */
+#define CODE_TIMEREMAIN 13 /* Time Remaining */
+
+/*
+ * Callback operation field values
+ */
+#define CBOP_AUTH 0 /* Location determined by user auth */
+#define CBOP_DIALSTR 1 /* Dialing string */
+#define CBOP_LOCATION 2 /* Location identifier */
+#define CBOP_E164 3 /* E.164 number */
+#define CBOP_X500 4 /* X.500 distinguished name */
+#define CBOP_CBCP 6 /* Use callback control protocol */
+
+/* FCS-Alternatives bits (RFC 1570) */
+#define FCSALT_NULL 1 /* None for network data; default otherwise */
+#define FCSALT_16 2 /* CRC-16 */
+#define FCSALT_32 4 /* CRC-32 */
+
+/* An endpoint discriminator, used with multilink. */
+#define MAX_ENDP_LEN 20 /* maximum length of discriminator value */
+struct epdisc {
+ unsigned char class;
+ unsigned char length;
+ unsigned char value[MAX_ENDP_LEN];
+};
+
+/* values for epdisc.class */
+#define EPD_NULL 0 /* null discriminator, no data */
+#define EPD_LOCAL 1
+#define EPD_IP 2
+#define EPD_MAC 3
+#define EPD_MAGIC 4
+#define EPD_PHONENUM 5
+
+/*
+ * The state of options is described by an lcp_options structure.
+ *
+ * We encode CHAP/MS-CHAP/MS-CHAPv2 options as Booleans. This is done
+ * so that we can represent the choices of requiring or refusing each
+ * separately. The chap_mdtype value can't do that.
+ */
+typedef struct lcp_options {
+ bool passive; /* Don't die if we don't get a response */
+ bool silent; /* Wait for the other end to start first */
+ bool restart; /* Restart vs. exit after close */
+ bool neg_mru; /* Negotiate the MRU? */
+ bool neg_asyncmap; /* Negotiate the async map? */
+ bool neg_upap; /* Ask for UPAP authentication? */
+ bool neg_chap; /* Ask for CHAP authentication? */
+ bool neg_mschap; /* Ask for MS-CHAPv1 authentication? */
+ bool neg_mschapv2; /* Ask for MS-CHAPv2 authentication? */
+ bool neg_magicnumber; /* Ask for magic number? */
+ bool neg_pcompression; /* HDLC Protocol Field Compression? */
+ bool neg_accompression; /* HDLC Address/Control Field Compression? */
+ bool neg_lqr; /* Negotiate use of Link Quality Reports */
+ bool neg_cbcp; /* Negotiate use of CBCP */
+ bool neg_mrru; /* negotiate multilink MRRU */
+#ifdef MUX_FRAME
+ u_int32_t pppmux; /* Negotiate for PPP Multiplexing option */
+#endif
+ bool neg_ssnhf; /* negotiate short sequence numbers */
+ bool neg_endpoint; /* negotiate endpoint discriminator */
+ bool neg_fcs; /* negotiate FCS alternatives */
+ int mru; /* Value of MRU */
+ int mrru; /* Value of MRRU, and multilink enable */
+ u_char chap_mdtype; /* which MD type (hashing algorithm) */
+ u_char fcs_type; /* selected FCS type(s) */
+ u_int32_t asyncmap; /* Value of async map */
+ u_int32_t magicnumber;
+ int numloops; /* Number of loops during magic number neg. */
+ u_int32_t lqr_period; /* Reporting period for LQR 1/100ths second */
+ struct epdisc endpoint; /* endpoint discriminator */
+} lcp_options;
+
+/*
+ * The structure passed to lcp_settimeremaining(), holds the unit
+ * number of the link being timed, and the time remaining for that
+ * connection.
+ */
+struct lcp_timer {
+ int unit;
+ u_int32_t tr;
+};
+
+extern fsm lcp_fsm[];
+extern lcp_options lcp_wantoptions[];
+extern lcp_options lcp_gotoptions[];
+extern lcp_options lcp_allowoptions[];
+extern lcp_options lcp_hisoptions[];
+extern u_int32_t xmit_accm[][8];
+
+void lcp_open __P((int));
+void lcp_close __P((int, char *));
+void lcp_lowerup __P((int));
+void lcp_lowerdown __P((int));
+void lcp_sprotrej __P((int, u_char *, int)); /* send protocol reject */
+void lcp_settimeremaining __P((int, u_int32_t, u_int32_t));
+
+/*
+ * Procedures exported from multilink.c
+ */
+extern char *epdisc_to_str __P((struct epdisc *));
+ /* string from endpoint discriminator */
+extern int str_to_epdisc __P((struct epdisc *, char *));
+ /* endpt discriminator from str */
+
+extern struct protent lcp_protent;
+
+/* Default number of times we receive our magic number from the peer
+ before deciding the link is looped-back. */
+#define DEFLOOPBACKFAIL 10
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LCP_H_ */
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppd/magic.c b/usr/src/cmd/cmd-inet/usr.bin/pppd/magic.c
new file mode 100644
index 0000000000..09d5086dfe
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppd/magic.c
@@ -0,0 +1,93 @@
+/*
+ * magic.c - PPP Magic Number routines.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+#define RCSID "$Id: magic.c,v 1.9 1999/08/13 06:46:15 paulus Exp $"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include "pppd.h"
+#include "magic.h"
+
+#if !defined(lint) && !defined(_lint)
+static const char rcsid[] = RCSID;
+#endif
+
+#ifdef NO_DRAND48
+long mrand48 __P((void));
+void srand48 __P((long));
+#endif
+
+/*
+ * magic_init - Initialize the magic number generator.
+ *
+ * Attempts to compute a random number seed which will not repeat.
+ * The current method uses the current hostid, current process ID
+ * and current time, currently.
+ */
+void
+magic_init()
+{
+ long seed;
+ struct timeval t;
+
+ (void) gettimeofday(&t, NULL);
+ seed = get_host_seed() ^ t.tv_sec ^ t.tv_usec ^ getpid();
+ srand48(seed);
+}
+
+/*
+ * magic - Returns the next magic number.
+ */
+u_int32_t
+magic()
+{
+ return (u_int32_t) mrand48();
+}
+
+#ifdef NO_DRAND48
+/*
+ * Substitute procedures for those systems which don't have
+ * drand48 et al.
+ */
+
+double
+drand48()
+{
+ return (double)random() / (double)0x7fffffffL; /* 2**31-1 */
+}
+
+long
+mrand48()
+{
+ return random();
+}
+
+void
+srand48(seedval)
+long seedval;
+{
+ srandom((int)seedval);
+}
+
+#endif
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppd/magic.h b/usr/src/cmd/cmd-inet/usr.bin/pppd/magic.h
new file mode 100644
index 0000000000..10708997a7
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppd/magic.h
@@ -0,0 +1,25 @@
+/*
+ * magic.h - PPP Magic Number definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: magic.h,v 1.3 1994/09/21 06:47:37 paulus Exp $
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+void magic_init __P((void)); /* Initialize the magic number generator */
+u_int32_t magic __P((void)); /* Returns the next magic number */
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppd/main.c b/usr/src/cmd/cmd-inet/usr.bin/pppd/main.c
new file mode 100644
index 0000000000..5a64ea5bf9
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppd/main.c
@@ -0,0 +1,2934 @@
+/*
+ * main.c - Point-to-Point Protocol main module
+ *
+ * Copyright 2000-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies.
+ *
+ * SUN MAKES NO REPRESENTATION OR WARRANTIES ABOUT THE SUITABILITY OF
+ * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+ * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
+ * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
+ * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+#define RCSID "$Id: main.c,v 1.97 2000/04/24 02:54:16 masputra Exp $"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <setjmp.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "pppd.h"
+#include "magic.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "ipcp.h"
+#ifdef INET6
+#include "ipv6cp.h"
+#endif
+#include "upap.h"
+#include "chap.h"
+#include "ccp.h"
+#include "pathnames.h"
+#include "patchlevel.h"
+
+#ifdef HAVE_MULTILINK
+#include "tdb.h"
+#endif
+
+#ifdef CBCP_SUPPORT
+#include "cbcp.h"
+#endif
+
+#ifdef IPX_CHANGE
+#include "ipxcp.h"
+#endif /* IPX_CHANGE */
+#ifdef AT_CHANGE
+#include "atcp.h"
+#endif
+
+#if !defined(lint) && !defined(_lint)
+static const char rcsid[] = RCSID;
+#endif
+
+/* interface vars */
+char ifname[32]; /* Interface name */
+int ifunit = -1; /* Interface unit number */
+
+char *progname; /* Name of this program */
+char hostname[MAXHOSTNAMELEN+1]; /* Our hostname */
+static char pidfilename[MAXPATHLEN]; /* name of pid file */
+static char linkpidfile[MAXPATHLEN]; /* name of linkname pid file */
+char ppp_devnam[MAXPATHLEN]; /* name of PPP tty (maybe ttypx) */
+static uid_t uid; /* Our real user-id */
+static int conn_running; /* we have a [dis]connector running */
+
+int ttyfd; /* Serial port file descriptor */
+mode_t tty_mode = (mode_t)-1; /* Original access permissions to tty */
+int baud_rate; /* Actual bits/second for serial device */
+bool hungup; /* terminal has been hung up */
+bool privileged; /* we're running as real uid root */
+bool need_holdoff; /* need holdoff period before restarting */
+bool detached; /* have detached from terminal */
+struct stat devstat; /* result of stat() on devnam */
+bool prepass = 0; /* doing prepass to find device name */
+int devnam_fixed; /* set while in options.ttyxx file */
+volatile int status; /* exit status for pppd */
+int unsuccess; /* # unsuccessful connection attempts */
+int do_callback; /* != 0 if we should do callback next */
+int doing_callback; /* != 0 if we are doing callback */
+char *callback_script; /* script for doing callback */
+#ifdef HAVE_MULTILINK
+TDB_CONTEXT *pppdb; /* database for storing status etc. */
+char db_key[32];
+#endif
+
+/*
+ * For plug-in usage:
+ *
+ * holdoff_hook - Can be used to change the demand-dial hold-off
+ * time dynamically. This is normally set by the
+ * "holdoff" option, and is 30 seconds by default.
+ *
+ * new_phase_hook - This is called for each change in the PPP
+ * phase (per RFC 1661). This can be used to log
+ * progress.
+ *
+ * check_options_hook - This is called before doing sys_init()
+ * and allows the plugin to verify the selected options.
+ *
+ * updown_script_hook - This is called with the proposed
+ * command-line arguments for any of the
+ * /etc/ppp/{ip,ipv6,ipx,auth}-{up,down} scripts before
+ * fork/exec. It can be used to add or change arguments.
+ *
+ * device_pipe_hook - If this is set, then an extra fd (3) is
+ * passed to the connect/disconnect script. This extra
+ * fd is the write side of a pipe, and the read side is
+ * passed to this routine. This can be used to pass
+ * arbitrary data from the script back to pppd.
+ */
+int (*holdoff_hook) __P((void)) = NULL;
+int (*new_phase_hook) __P((int new, int old)) = NULL;
+int (*check_options_hook) __P((uid_t uid)) = NULL;
+int (*updown_script_hook) __P((const char ***argsp)) = NULL;
+void (*device_pipe_hook) __P((int pipefd)) = NULL;
+
+static int fd_ppp = -1; /* fd for talking PPP */
+static int fd_loop; /* fd for getting demand-dial packets */
+static int pty_master; /* fd for master side of pty */
+int pty_slave = -1; /* fd for slave side of pty */
+static int real_ttyfd; /* fd for actual serial port (not pty) */
+
+int phase; /* where the link is at */
+int kill_link;
+int open_ccp_flag;
+
+static int waiting; /* for input from peer or timer expiration */
+static sigjmp_buf sigjmp;
+
+char **script_env; /* Env. variable values for scripts */
+int s_env_nalloc; /* # words avail at script_env */
+
+u_char outpacket_buf[PPP_MRU+PPP_HDRLEN]; /* buffer for outgoing packet */
+u_char inpacket_buf[PPP_MRU+PPP_HDRLEN]; /* buffer for incoming packet */
+u_char nak_buffer[PPP_MRU]; /* where we construct a nak packet */
+
+static int n_children; /* # child processes still running */
+static bool got_sigchld; /* set if we have received a SIGCHLD */
+static sigset_t main_sigmask; /* signals blocked while dispatching */
+
+static bool locked; /* lock() has succeeded */
+static bool privopen; /* don't lock, open device as root */
+
+char *no_ppp_msg = "Sorry - this system lacks PPP kernel support\n";
+
+GIDSET_TYPE groups[NGROUPS_MAX];/* groups the user is in */
+int ngroups; /* How many groups valid in groups */
+
+static struct timeval start_time; /* Time when link was started. */
+
+struct pppd_stats link_stats;
+int link_connect_time;
+bool link_stats_valid;
+
+static pid_t charshunt_pid; /* Process ID for charshunt */
+
+extern option_t general_options[];
+extern option_t auth_options[];
+
+/*
+ * We maintain a list of child process pids and
+ * functions to call when they exit.
+ */
+struct subprocess {
+ pid_t pid;
+ char *prog;
+ void (*done) __P((void *, int));
+ void *arg;
+ struct subprocess *next;
+};
+
+static struct subprocess *children;
+
+/* Prototypes for procedures local to this file. */
+
+static void setup_signals __P((void));
+static void create_pidfile __P((void));
+static void create_linkpidfile __P((void));
+static void cleanup __P((void));
+static void close_tty __P((void));
+static void get_input __P((void));
+static void calltimeout __P((void));
+static struct timeval *timeleft __P((struct timeval *));
+static void kill_my_pg __P((int));
+static void hup __P((int));
+static void term __P((int));
+static void chld __P((int));
+static void toggle_debug __P((int));
+static void open_ccp __P((int));
+static void bad_signal __P((int));
+static void holdoff_end __P((void *));
+static int device_script __P((char *, int, int, int, char *));
+static int reap_kids __P((int waitfor));
+static void record_child __P((pid_t, char *, void (*) (void *, int), void *));
+static int open_socket __P((char *));
+static int start_charshunt __P((int, int));
+static void charshunt_done __P((void *, int));
+static void charshunt __P((int, int, char *));
+static int record_write __P((FILE *, int code, u_char *buf, int nb,
+ struct timeval *));
+static void final_reap __P((void));
+
+#ifdef HAVE_MULTILINK
+static void update_db_entry __P((void));
+static void add_db_key __P((const char *));
+static void delete_db_key __P((const char *));
+static void cleanup_db __P((void));
+#endif
+
+int main __P((int, char *[]));
+
+#ifdef ultrix
+#undef O_NONBLOCK
+#define O_NONBLOCK O_NDELAY
+#endif
+
+#ifdef ULTRIX
+#define setlogmask(x) 0
+#endif
+
+/* Backward compatibility for Linux */
+#ifndef RECMARK_TIMESTART
+#define RECMARK_STARTSEND 1
+#define RECMARK_STARTRECV 2
+#define RECMARK_ENDSEND 3
+#define RECMARK_ENDRECV 4
+#define RECMARK_TIMEDELTA32 5
+#define RECMARK_TIMEDELTA8 6
+#define RECMARK_TIMESTART 7
+#endif
+
+/*
+ * PPP Data Link Layer "protocol" table.
+ * One entry per supported protocol.
+ * The last entry must be NULL.
+ */
+struct protent *protocols[] = {
+ &lcp_protent,
+ &pap_protent,
+ &chap_protent,
+#ifdef CBCP_SUPPORT
+ &cbcp_protent,
+#endif
+ &ipcp_protent,
+#ifdef INET6
+ &ipv6cp_protent,
+#endif
+ &ccp_protent,
+#ifdef IPX_CHANGE
+ &ipxcp_protent,
+#endif
+#ifdef AT_CHANGE
+ &atcp_protent,
+#endif
+ NULL
+};
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int i, fdflags, t;
+ char *p, *connector;
+ struct passwd *pw;
+ struct timeval timo;
+ struct protent *protp;
+ struct stat statbuf;
+ char numbuf[16];
+
+ ifname[0] = '\0';
+ new_phase(PHASE_INITIALIZE);
+
+ /*
+ * Ensure that fds 0, 1, 2 are open, to /dev/null if nowhere else.
+ * This way we can close 0, 1, 2 in detach() without clobbering
+ * a fd that we are using.
+ */
+ if ((i = open(_PATH_DEVNULL, O_RDWR)) >= 0) {
+ while (0 <= i && i <= 2)
+ i = dup(i);
+ if (i >= 0)
+ (void) close(i);
+ }
+
+ script_env = NULL;
+
+ /* Initialize syslog facilities */
+ reopen_log();
+
+ if (gethostname(hostname, MAXHOSTNAMELEN+1) < 0 ) {
+ option_error("Couldn't get hostname: %m");
+ exit(1);
+ }
+ hostname[MAXHOSTNAMELEN] = '\0';
+
+ /* make sure we don't create world or group writable files. */
+ (void) umask(umask(0777) | 022);
+
+ uid = getuid();
+ privileged = (uid == 0);
+ (void) slprintf(numbuf, sizeof(numbuf), "%d", uid);
+ script_setenv("ORIG_UID", numbuf, 0);
+
+ ngroups = getgroups(NGROUPS_MAX, groups);
+
+ /*
+ * Initialize magic number generator now so that protocols may
+ * use magic numbers in initialization.
+ */
+ magic_init();
+
+ progname = *argv;
+ prepass = 0;
+ /*
+ * Initialize to the standard option set, then parse, in order, the
+ * system options file, the user's options file, the tty's options file,
+ * and the command line arguments. At last, install the options declared
+ * by each protocol into the extra_option list.
+ */
+ for (i = 0; (protp = protocols[i]) != NULL; ++i) {
+ (*protp->init)(0);
+ if (protp->options != NULL) {
+ add_options(protp->options);
+ }
+ }
+
+ /*
+ * Install "generic" options into the extra_options list.
+ */
+ add_options(auth_options);
+ add_options(general_options);
+
+ /* Install any system-specific options (or remove unusable ones) */
+ sys_options();
+
+ if (!options_from_file(_PATH_SYSOPTIONS, !privileged, 0, 1)
+ || !options_from_user())
+ exit(EXIT_OPTION_ERROR);
+
+ /* scan command line and options files to find device name */
+ prepass = 1;
+ (void) parse_args(argc-1, argv+1);
+ prepass = 0;
+
+ /*
+ * Work out the device name, if it hasn't already been specified.
+ */
+ using_pty = notty || ptycommand != NULL || pty_socket != NULL;
+ if (!using_pty && default_device && !direct_tty) {
+ char *p;
+
+ if (!isatty(0) || (p = ttyname(0)) == NULL) {
+ option_error("no device specified and stdin is not a tty");
+ exit(EXIT_OPTION_ERROR);
+ }
+ (void) strlcpy(devnam, p, sizeof(devnam));
+ if (stat(devnam, &devstat) < 0)
+ fatal("Couldn't stat default device %s: %m", devnam);
+ }
+
+ /*
+ * Parse the tty options file and the command line.
+ * The per-tty options file should not change
+ * ptycommand, pty_socket, notty or devnam.
+ */
+ devnam_fixed = 1;
+ if (!using_pty && !direct_tty) {
+ if (!options_for_tty())
+ exit(EXIT_OPTION_ERROR);
+ }
+
+ devnam_fixed = 0;
+ if (!parse_args(argc-1, argv+1))
+ exit(EXIT_OPTION_ERROR);
+
+ /*
+ * Check that we are running as root.
+ */
+ if (geteuid() != 0) {
+ option_error("must be root to run %s, since it is not setuid-root",
+ argv[0]);
+ exit(EXIT_NOT_ROOT);
+ }
+
+ if (!ppp_available()) {
+ option_error(no_ppp_msg);
+ exit(EXIT_NO_KERNEL_SUPPORT);
+ }
+
+ /*
+ * Check that the options given are valid and consistent.
+ */
+ if (!sys_check_options())
+ exit(EXIT_OPTION_ERROR);
+ auth_check_options();
+#ifdef HAVE_MULTILINK
+ mp_check_options();
+#endif
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ if (protp->enabled_flag && protp->check_options != NULL)
+ (*protp->check_options)();
+ if (demand && (connect_script == NULL)) {
+ option_error("connect script is required for demand-dialling\n");
+ exit(EXIT_OPTION_ERROR);
+ }
+ if (updetach && (nodetach || demand)) {
+ option_error("updetach cannot be used with %s",
+ nodetach ? "nodetach" : "demand");
+ exit(EXIT_OPTION_ERROR);
+ }
+ /* default holdoff to 0 if no connect script has been given */
+ if ((connect_script == NULL) && !holdoff_specified)
+ holdoff = 0;
+
+ if (using_pty || direct_tty) {
+ if (!default_device) {
+ option_error("%s option precludes specifying device name",
+ notty? "notty": "pty");
+ exit(EXIT_OPTION_ERROR);
+ }
+ if (ptycommand != NULL && (notty || direct_tty)) {
+ option_error("pty option is incompatible with notty option");
+ exit(EXIT_OPTION_ERROR);
+ }
+ if (pty_socket != NULL && (ptycommand != NULL || notty ||
+ direct_tty)) {
+ option_error("socket option is incompatible with pty and notty");
+ exit(EXIT_OPTION_ERROR);
+ }
+ default_device = notty || direct_tty;
+ lockflag = 0;
+ modem = 0;
+ if (default_device && log_to_fd <= 1)
+ log_to_fd = -1;
+ } else {
+ /*
+ * If the user has specified a device which is the same as
+ * the one on stdin, pretend they didn't specify any.
+ * If the device is already open read/write on stdin,
+ * we assume we don't need to lock it, and we can open it as root.
+ */
+ if (fstat(0, &statbuf) >= 0 && S_ISCHR(statbuf.st_mode)
+ && statbuf.st_rdev == devstat.st_rdev) {
+ default_device = 1;
+ fdflags = fcntl(0, F_GETFL);
+ if (fdflags != -1 && (fdflags & O_ACCMODE) == O_RDWR)
+ privopen = 1;
+ }
+ }
+ if (default_device)
+ nodetach = 1;
+
+ /*
+ * Don't send log messages to the serial port, it tends to
+ * confuse the peer. :-)
+ */
+ if (log_to_fd >= 0 && fstat(log_to_fd, &statbuf) >= 0
+ && S_ISCHR(statbuf.st_mode) && statbuf.st_rdev == devstat.st_rdev)
+ log_to_fd = -1;
+ early_log = 0;
+
+ if (debug)
+ (void) setlogmask(LOG_UPTO(LOG_DEBUG));
+
+ /*
+ * Initialize system-dependent stuff.
+ */
+ if (check_options_hook != NULL &&
+ (*check_options_hook)(uid) == -1) {
+ exit(EXIT_OPTION_ERROR);
+ }
+ sys_init(!devnam_info.priv && !privopen);
+
+#ifdef HAVE_MULTILINK
+ pppdb = tdb_open(_PATH_PPPDB, 0, 0, O_RDWR|O_CREAT, 0644);
+ if (pppdb != NULL) {
+ (void) slprintf(db_key, sizeof(db_key), "pppd%d", getpid());
+ update_db_entry();
+ } else {
+ warn("Warning: couldn't open ppp database %s", _PATH_PPPDB);
+ if (multilink) {
+ warn("Warning: disabling multilink");
+ multilink = 0;
+ }
+ }
+#endif
+
+ /*
+ * Detach ourselves from the terminal, if required, and identify
+ * who is running us. Printing to stderr stops here unless
+ * nodetach or updetach is set.
+ */
+ if (!nodetach && !updetach)
+ detach();
+ p = getlogin();
+ if (p == NULL) {
+ pw = getpwuid(uid);
+ if (pw != NULL && pw->pw_name != NULL)
+ p = pw->pw_name;
+ else
+ p = "(unknown)";
+ }
+ syslog(LOG_NOTICE, "pppd %s.%d%s started by %s, uid %d",
+ VERSION, PATCHLEVEL, IMPLEMENTATION, p, uid);
+ script_setenv("PPPLOGNAME", p, 0);
+
+ if (devnam[0] != '\0')
+ script_setenv("DEVICE", devnam, 1);
+ (void) slprintf(numbuf, sizeof(numbuf), "%d", getpid());
+ script_setenv("PPPD_PID", numbuf, 1);
+
+ setup_signals();
+
+ waiting = 0;
+
+ create_linkpidfile();
+
+ /*
+ * If we're doing dial-on-demand, set up the interface now.
+ */
+ if (demand) {
+ /*
+ * Open the loopback channel and set it up to be the ppp interface.
+ */
+#ifdef HAVE_MULTILINK
+ (void) tdb_writelock(pppdb);
+#endif
+ set_ifunit(1);
+ fd_loop = open_ppp_loopback();
+#ifdef HAVE_MULTILINK
+ (void) tdb_writeunlock(pppdb);
+#endif
+
+ /*
+ * Configure the interface and mark it up, etc.
+ */
+ demand_conf();
+ }
+
+ new_phase(PHASE_INITIALIZED);
+ do_callback = 0;
+ for (;;) {
+
+ need_holdoff = 1;
+ ttyfd = -1;
+ real_ttyfd = -1;
+ status = EXIT_OK;
+ ++unsuccess;
+ doing_callback = do_callback;
+ do_callback = 0;
+
+ if (demand && !doing_callback) {
+ /*
+ * Don't do anything until we see some activity.
+ */
+ kill_link = 0;
+ new_phase(PHASE_DORMANT);
+ demand_unblock();
+ add_fd(fd_loop);
+ for (;;) {
+ if (sigsetjmp(sigjmp, 1) == 0) {
+ (void) sigprocmask(SIG_BLOCK, &main_sigmask, NULL);
+ if (kill_link || got_sigchld) {
+ (void) sigprocmask(SIG_UNBLOCK, &main_sigmask, NULL);
+ } else {
+ waiting = 1;
+ (void) sigprocmask(SIG_UNBLOCK, &main_sigmask, NULL);
+ wait_input(timeleft(&timo));
+ }
+ }
+ waiting = 0;
+ calltimeout();
+ if (kill_link) {
+ if (!persist)
+ break;
+ kill_link = 0;
+ }
+ if (get_loop_output())
+ break;
+ if (got_sigchld)
+ (void) reap_kids(0);
+ }
+ remove_fd(fd_loop);
+ if (kill_link && !persist)
+ break;
+
+ /*
+ * Now we want to bring up the link.
+ */
+ demand_block();
+ info("Starting link");
+ }
+
+ new_phase(doing_callback ? PHASE_CALLINGBACK : PHASE_SERIALCONN);
+
+ /*
+ * Get a pty master/slave pair if the pty, notty, socket,
+ * or record options were specified.
+ */
+ (void) strlcpy(ppp_devnam, devnam, sizeof(ppp_devnam));
+ pty_master = -1;
+ pty_slave = -1;
+ if (using_pty || record_file != NULL) {
+ if (!get_pty(&pty_master, &pty_slave, ppp_devnam, uid)) {
+ error("Couldn't allocate pseudo-tty");
+ status = EXIT_FATAL_ERROR;
+ goto fail;
+ }
+ set_up_tty(pty_slave, 1);
+ }
+
+ /*
+ * Lock the device if we've been asked to.
+ */
+ status = EXIT_LOCK_FAILED;
+ if (lockflag && !privopen && !direct_tty) {
+ if (lock(devnam) < 0)
+ goto fail;
+ locked = 1;
+ }
+
+ /*
+ * Open the serial device and set it up to be the ppp interface.
+ * First we open it in non-blocking mode so we can set the
+ * various termios flags appropriately. If we aren't dialling
+ * out and we want to use the modem lines, we reopen it later
+ * in order to wait for the carrier detect signal from the modem.
+ */
+ hungup = 0;
+ kill_link = 0;
+ connector = doing_callback? callback_script: connect_script;
+ if (direct_tty) {
+ ttyfd = 0;
+ } else if (devnam[0] != '\0') {
+ for (;;) {
+ /* If the user specified the device name, become the
+ user before opening it. */
+ int err;
+ if (!devnam_info.priv && !privopen)
+ (void) seteuid(uid);
+ if ((ttyfd = sys_extra_fd()) < 0)
+ ttyfd = open(devnam, O_NONBLOCK | O_RDWR);
+ err = errno;
+ if (!devnam_info.priv && !privopen)
+ (void) seteuid(0);
+ if (ttyfd >= 0)
+ break;
+ errno = err;
+ if (err != EINTR) {
+ error("Failed to open %s: %m", devnam);
+ status = EXIT_OPEN_FAILED;
+ }
+ if (!persist || err != EINTR)
+ goto fail;
+ }
+ if ((fdflags = fcntl(ttyfd, F_GETFL)) == -1
+ || fcntl(ttyfd, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
+ warn("Couldn't reset non-blocking mode on device: %m");
+
+ /*
+ * Do the equivalent of `mesg n' to stop broadcast messages.
+ */
+ if (fstat(ttyfd, &statbuf) < 0
+ || fchmod(ttyfd, statbuf.st_mode & ~(S_IWGRP | S_IWOTH)) < 0) {
+ warn("Couldn't restrict write permissions to %s: %m", devnam);
+ } else
+ tty_mode = statbuf.st_mode;
+
+ /*
+ * Set line speed, flow control, etc.
+ * If we have a non-null connection or initializer script,
+ * on most systems we set CLOCAL for now so that we can talk
+ * to the modem before carrier comes up. But this has the
+ * side effect that we might miss it if CD drops before we
+ * get to clear CLOCAL below. On systems where we can talk
+ * successfully to the modem with CLOCAL clear and CD down,
+ * we could clear CLOCAL at this point.
+ */
+ set_up_tty(ttyfd, ((connector != NULL && connector[0] != '\0')
+ || initializer != NULL));
+ real_ttyfd = ttyfd;
+ }
+
+ /*
+ * If the pty, socket, notty and/or record option was specified,
+ * start up the character shunt now.
+ */
+ status = EXIT_PTYCMD_FAILED;
+ if (ptycommand != NULL) {
+ if (record_file != NULL) {
+ int ipipe[2], opipe[2], ok;
+
+ if (pipe(ipipe) < 0 || pipe(opipe) < 0)
+ fatal("Couldn't create pipes for record option: %m");
+ dbglog("starting charshunt for pty option");
+ ok = device_script(ptycommand, opipe[0], ipipe[1], 1,
+ "record") == 0 && start_charshunt(ipipe[0], opipe[1]);
+ (void) close(ipipe[0]);
+ (void) close(ipipe[1]);
+ (void) close(opipe[0]);
+ (void) close(opipe[1]);
+ if (!ok)
+ goto fail;
+ } else {
+ if (device_script(ptycommand, pty_master, pty_master, 1,
+ "pty") < 0)
+ goto fail;
+ ttyfd = pty_slave;
+ (void) close(pty_master);
+ pty_master = -1;
+ }
+ } else if (pty_socket != NULL) {
+ int fd = open_socket(pty_socket);
+ if (fd < 0)
+ goto fail;
+ dbglog("starting charshunt for socket option");
+ if (!start_charshunt(fd, fd))
+ goto fail;
+ } else if (notty) {
+ dbglog("starting charshunt for notty option");
+ if (!start_charshunt(0, 1))
+ goto fail;
+ } else if (record_file != NULL) {
+ dbglog("starting charshunt for record option");
+ if (!start_charshunt(ttyfd, ttyfd))
+ goto fail;
+ }
+
+ /* run connection script */
+ if (((connector != NULL) && (connector[0] != '\0')) || initializer) {
+ if (real_ttyfd != -1) {
+ /* XXX do this if doing_callback == CALLBACK_DIALIN? */
+ if (!default_device && modem && !direct_tty) {
+ setdtr(real_ttyfd, 0); /* in case modem is off hook */
+ (void) sleep(1);
+ setdtr(real_ttyfd, 1);
+ }
+ }
+
+ if ((initializer != NULL) && (initializer[0] != '\0')) {
+ if (device_script(initializer, ttyfd, ttyfd, 0, "init") < 0) {
+ error("Initializer script failed");
+ status = EXIT_INIT_FAILED;
+ goto fail;
+ }
+ if (kill_link)
+ goto disconnect;
+
+ info("Serial port initialized.");
+ }
+
+ if ((connector != NULL) && (connector[0] != '\0')) {
+ if (device_script(connector, ttyfd, ttyfd, 0, "connect") < 0) {
+ error("Connect script failed");
+ status = EXIT_CONNECT_FAILED;
+ goto fail;
+ }
+ if (kill_link)
+ goto disconnect;
+
+ info("Serial connection established.");
+ }
+
+ /*
+ * Clear CLOCAL if modem option -- we now have carrier
+ * established, and we should respect loss of carrier.
+ */
+ if (real_ttyfd != -1)
+ set_up_tty(real_ttyfd, 0);
+
+ if (doing_callback == CALLBACK_DIALIN)
+ connector = NULL;
+ }
+
+ /* reopen tty if necessary to wait for carrier */
+ if (connector == NULL && modem && devnam[0] != '\0' && !direct_tty) {
+ for (;;) {
+ if ((i = open(devnam, O_RDWR)) >= 0)
+ break;
+ if (errno != EINTR) {
+ error("Failed to reopen %s: %m", devnam);
+ status = EXIT_OPEN_FAILED;
+ }
+ if (!persist || errno != EINTR || hungup || kill_link)
+ goto fail;
+ }
+ (void) close(i);
+ }
+
+ (void) slprintf(numbuf, sizeof(numbuf), "%d", baud_rate);
+ script_setenv("SPEED", numbuf, 0);
+
+ /* run welcome script, if any */
+ if ((welcomer != NULL) && (welcomer[0] != '\0')) {
+ if (device_script(welcomer, ttyfd, ttyfd, 0, "welcome") < 0)
+ warn("Welcome script failed");
+ }
+
+ /* set up the serial device as a ppp interface */
+#ifdef HAVE_MULTILINK
+ (void) tdb_writelock(pppdb);
+#endif
+ fd_ppp = establish_ppp(ttyfd);
+ if (fd_ppp < 0) {
+#ifdef HAVE_MULTILINK
+ (void) tdb_writeunlock(pppdb);
+#endif
+ status = EXIT_FATAL_ERROR;
+ goto disconnect;
+ }
+
+ if (!demand && ifunit >= 0)
+ set_ifunit(1);
+#ifdef HAVE_MULTILINK
+ (void) tdb_writeunlock(pppdb);
+#endif
+
+ /*
+ * Start opening the connection and wait for
+ * incoming events (reply, timeout, etc.).
+ */
+ notice("Connect: %s <--> %s", ifname, ppp_devnam);
+ (void) gettimeofday(&start_time, NULL);
+ link_stats_valid = 0;
+ script_unsetenv("CONNECT_TIME");
+ script_unsetenv("BYTES_SENT");
+ script_unsetenv("BYTES_RCVD");
+ lcp_lowerup(0);
+
+ /* Mostly for accounting purposes */
+ new_phase(PHASE_CONNECTED);
+
+ /*
+ * If we are initiating this connection, wait for a short
+ * time for something from the peer. This can avoid bouncing
+ * our packets off his tty before he has it set up.
+ */
+ add_fd(fd_ppp);
+ if (connect_delay != 0 && (connector != NULL || ptycommand != NULL)) {
+ struct timeval t;
+ t.tv_sec = connect_delay / 1000;
+ t.tv_usec = connect_delay % 1000;
+ wait_input(&t);
+ }
+
+ lcp_open(0); /* Start protocol */
+ open_ccp_flag = 0;
+ status = EXIT_NEGOTIATION_FAILED;
+ new_phase(PHASE_ESTABLISH);
+ while (phase != PHASE_DEAD) {
+ if (sigsetjmp(sigjmp, 1) == 0) {
+ (void) sigprocmask(SIG_BLOCK, &main_sigmask, NULL);
+ if (kill_link || open_ccp_flag || got_sigchld) {
+ (void) sigprocmask(SIG_UNBLOCK, &main_sigmask, NULL);
+ } else {
+ waiting = 1;
+ (void) sigprocmask(SIG_UNBLOCK, &main_sigmask, NULL);
+ wait_input(timeleft(&timo));
+ }
+ }
+ waiting = 0;
+ calltimeout();
+ get_input();
+ if (kill_link) {
+ lcp_close(0, "User request");
+ kill_link = 0;
+ }
+ if (open_ccp_flag) {
+ if (phase == PHASE_NETWORK || phase == PHASE_RUNNING) {
+ /* Uncloak ourselves. */
+ ccp_fsm[0].flags &= ~OPT_SILENT;
+ (*ccp_protent.open)(0);
+ }
+ open_ccp_flag = 0;
+ }
+ if (got_sigchld)
+ (void) reap_kids(0); /* Don't leave dead kids lying around */
+ }
+
+ /*
+ * Print connect time and statistics.
+ */
+ if (link_stats_valid) {
+ int t = (link_connect_time + 5) / 6; /* 1/10ths of minutes */
+ info("Connect time %d.%d minutes.", t/10, t%10);
+ info("Sent %" PPP_COUNTER_F " bytes (%" PPP_COUNTER_F
+ " packets), received %" PPP_COUNTER_F " bytes (%" PPP_COUNTER_F
+ " packets).",
+ link_stats.bytes_out, link_stats.pkts_out,
+ link_stats.bytes_in, link_stats.pkts_in);
+ }
+
+ /*
+ * Delete pid file before disestablishing ppp. Otherwise it
+ * can happen that another pppd gets the same unit and then
+ * we delete its pid file.
+ */
+ if (!demand) {
+ if (pidfilename[0] != '\0'
+ && unlink(pidfilename) < 0 && errno != ENOENT)
+ warn("unable to delete pid file %s: %m", pidfilename);
+ pidfilename[0] = '\0';
+ }
+
+ /*
+ * If we may want to bring the link up again, transfer
+ * the ppp unit back to the loopback. Set the
+ * real serial device back to its normal mode of operation.
+ */
+ remove_fd(fd_ppp);
+ clean_check();
+ if (demand)
+ restore_loop();
+ disestablish_ppp(ttyfd);
+ fd_ppp = -1;
+ if (!hungup)
+ lcp_lowerdown(0);
+ if (!demand)
+ script_unsetenv("IFNAME");
+
+ /*
+ * Run disconnector script, if requested.
+ * XXX we may not be able to do this if the line has hung up!
+ */
+ disconnect:
+ if ((disconnect_script != NULL) && (disconnect_script[0] != '\0') &&
+ !hungup) {
+ new_phase(PHASE_DISCONNECT);
+ if (real_ttyfd >= 0)
+ set_up_tty(real_ttyfd, 1);
+ if (device_script(disconnect_script, ttyfd, ttyfd, 0,
+ "disconnect") < 0) {
+ warn("disconnect script failed");
+ } else {
+ info("Serial link disconnected.");
+ }
+ }
+
+ fail:
+ if (pty_master >= 0)
+ (void) close(pty_master);
+ if (pty_slave >= 0) {
+ (void) close(pty_slave);
+ pty_slave = -1;
+ }
+ if (real_ttyfd >= 0)
+ close_tty();
+ if (locked) {
+ locked = 0;
+ unlock();
+ }
+
+ if (!demand) {
+ if (pidfilename[0] != '\0'
+ && unlink(pidfilename) < 0 && errno != ENOENT)
+ warn("unable to delete pid file %s: %m", pidfilename);
+ pidfilename[0] = '\0';
+ }
+
+ if (!persist || (maxfail > 0 && unsuccess >= maxfail))
+ break;
+
+ kill_link = 0;
+ if (demand)
+ demand_discard();
+ t = need_holdoff? holdoff: 0;
+ if (holdoff_hook != NULL)
+ t = (*holdoff_hook)();
+ if (t > 0) {
+ new_phase(PHASE_HOLDOFF);
+ TIMEOUT(holdoff_end, NULL, t);
+ do {
+ if (sigsetjmp(sigjmp, 1) == 0) {
+ (void) sigprocmask(SIG_BLOCK, &main_sigmask, NULL);
+ if (kill_link || got_sigchld) {
+ (void) sigprocmask(SIG_UNBLOCK, &main_sigmask, NULL);
+ } else {
+ waiting = 1;
+ (void) sigprocmask(SIG_UNBLOCK, &main_sigmask, NULL);
+ wait_input(timeleft(&timo));
+ }
+ }
+ waiting = 0;
+ calltimeout();
+ if (kill_link) {
+ kill_link = 0;
+ new_phase(PHASE_DORMANT); /* allow signal to end holdoff */
+ }
+ if (got_sigchld)
+ (void) reap_kids(0);
+ } while (phase == PHASE_HOLDOFF);
+ if (!persist)
+ break;
+ }
+ }
+
+ /* Wait for scripts to finish */
+ final_reap();
+
+ die(status);
+ return (0);
+}
+
+/*
+ * setup_signals - initialize signal handling.
+ */
+static void
+setup_signals()
+{
+ struct sigaction sa;
+
+ /*
+ * Compute mask of all interesting signals and install signal handlers
+ * for each. Only one signal handler may be active at a time. Therefore,
+ * all other signals should be masked when any handler is executing.
+ */
+ (void) sigemptyset(&main_sigmask);
+ (void) sigaddset(&main_sigmask, SIGHUP);
+ (void) sigaddset(&main_sigmask, SIGINT);
+ (void) sigaddset(&main_sigmask, SIGTERM);
+ (void) sigaddset(&main_sigmask, SIGCHLD);
+ (void) sigaddset(&main_sigmask, SIGUSR2);
+
+#define SIGNAL(s, handler) if (1) { \
+ sa.sa_handler = handler; \
+ if (sigaction(s, &sa, NULL) < 0) \
+ fatal("Couldn't establish signal handler (%d): %m", s); \
+ } else ((void)0)
+
+ sa.sa_mask = main_sigmask;
+ sa.sa_flags = 0;
+/*CONSTANTCONDITION*/ SIGNAL(SIGHUP, hup); /* Hangup */
+/*CONSTANTCONDITION*/ SIGNAL(SIGINT, term); /* Interrupt */
+/*CONSTANTCONDITION*/ SIGNAL(SIGTERM, term); /* Terminate */
+/*CONSTANTCONDITION*/ SIGNAL(SIGCHLD, chld);
+
+/*CONSTANTCONDITION*/ SIGNAL(SIGUSR1, toggle_debug); /* Toggle debug flag */
+/*CONSTANTCONDITION*/ SIGNAL(SIGUSR2, open_ccp); /* Reopen CCP */
+
+ /*
+ * Install a handler for other signals which would otherwise
+ * cause pppd to exit without cleaning up.
+ */
+/*CONSTANTCONDITION*/ SIGNAL(SIGABRT, bad_signal);
+/*CONSTANTCONDITION*/ SIGNAL(SIGALRM, bad_signal);
+/*CONSTANTCONDITION*/ SIGNAL(SIGFPE, bad_signal);
+/*CONSTANTCONDITION*/ SIGNAL(SIGILL, bad_signal);
+/*CONSTANTCONDITION*/ SIGNAL(SIGPIPE, bad_signal);
+/*CONSTANTCONDITION*/ SIGNAL(SIGQUIT, bad_signal);
+#ifndef DEBUG
+/*CONSTANTCONDITION*/ SIGNAL(SIGSEGV, bad_signal);
+#endif
+#ifdef SIGBUS
+/*CONSTANTCONDITION*/ SIGNAL(SIGBUS, bad_signal);
+#endif
+#ifdef SIGEMT
+/*CONSTANTCONDITION*/ SIGNAL(SIGEMT, bad_signal);
+#endif
+#ifdef SIGPOLL
+/*CONSTANTCONDITION*/ SIGNAL(SIGPOLL, bad_signal);
+#endif
+#ifdef SIGPROF
+/*CONSTANTCONDITION*/ SIGNAL(SIGPROF, bad_signal);
+#endif
+#ifdef SIGSYS
+/*CONSTANTCONDITION*/ SIGNAL(SIGSYS, bad_signal);
+#endif
+#ifdef SIGTRAP
+/*CONSTANTCONDITION*/ SIGNAL(SIGTRAP, bad_signal);
+#endif
+#ifdef SIGVTALRM
+/*CONSTANTCONDITION*/ SIGNAL(SIGVTALRM, bad_signal);
+#endif
+#ifdef SIGXCPU
+/*CONSTANTCONDITION*/ SIGNAL(SIGXCPU, bad_signal);
+#endif
+#ifdef SIGXFSZ
+/*CONSTANTCONDITION*/ SIGNAL(SIGXFSZ, bad_signal);
+#endif
+
+ /*
+ * Apparently we can get a SIGPIPE when we call syslog, if
+ * syslogd has died and been restarted. Ignoring it seems
+ * be sufficient.
+ */
+ (void) signal(SIGPIPE, SIG_IGN);
+}
+
+/*
+ * set_ifunit - do things we need to do once we know which ppp
+ * unit we are using.
+ */
+void
+set_ifunit(iskey)
+ int iskey;
+{
+ sys_ifname();
+ info("Using interface %s", ifname);
+ script_setenv("IFNAME", ifname, iskey);
+ if (iskey) {
+ create_pidfile(); /* write pid to file */
+ create_linkpidfile();
+ }
+}
+
+/*
+ * detach - detach us from the controlling terminal.
+ */
+void
+detach()
+{
+ pid_t pid;
+ char numbuf[16];
+
+ if (detached)
+ return;
+ if ((pid = fork()) == (pid_t)-1) {
+ error("Couldn't detach (fork failed: %m)");
+ die(1); /* or just return? */
+ }
+ if (pid != (pid_t)0) {
+ /* parent */
+ if (locked)
+ (void) relock(pid);
+ exit(0); /* parent dies */
+ }
+ (void) setsid();
+ /*
+ * Fork again to relinquish session leadership. This is needed
+ * to prevent the daemon from acquiring controlling terminal.
+ */
+ if ((pid = fork()) == (pid_t)-1) {
+ error("Couldn't detach (second fork failed: %m)");
+ die(1); /* or just return? */
+ }
+ if (pid != (pid_t)0) {
+ /* parent */
+ if (locked)
+ (void) relock(pid);
+ exit(0); /* parent dies */
+ }
+ (void) chdir("/");
+ (void) close(0);
+ (void) close(1);
+ (void) close(2);
+ detached = 1;
+ if (!log_to_file && !log_to_specific_fd)
+ log_to_fd = -1;
+ /* update pid files if they have been written already */
+ if (pidfilename[0] != '\0')
+ create_pidfile();
+ if (linkpidfile[0] != '\0')
+ create_linkpidfile();
+ (void) slprintf(numbuf, sizeof(numbuf), "%d", getpid());
+ script_setenv("PPPD_PID", numbuf, 1);
+}
+
+/*
+ * reopen_log - (re)open our connection to syslog.
+ */
+void
+reopen_log()
+{
+#ifdef ULTRIX
+ openlog("pppd", LOG_PID);
+#else
+ openlog("pppd", LOG_PID | LOG_NDELAY, LOG_PPP);
+ (void) setlogmask(LOG_UPTO(LOG_INFO));
+#endif
+}
+
+/*
+ * Create a file containing our process ID.
+ */
+static void
+create_pidfile()
+{
+ FILE *pidfile;
+
+ (void) slprintf(pidfilename, sizeof(pidfilename), "%s%s.pid",
+ _PATH_VARRUN, ifname);
+ if ((pidfile = fopen(pidfilename, "w")) != NULL) {
+ (void) fprintf(pidfile, "%u\n", (unsigned)getpid());
+ (void) fclose(pidfile);
+ } else {
+ error("Failed to create pid file %s: %m", pidfilename);
+ pidfilename[0] = '\0';
+ }
+}
+
+static void
+create_linkpidfile()
+{
+ FILE *pidfile;
+
+ if (linkname[0] == '\0')
+ return;
+ script_setenv("LINKNAME", linkname, 1);
+ (void) slprintf(linkpidfile, sizeof(linkpidfile), "%sppp-%s.pid",
+ _PATH_VARRUN, linkname);
+ if ((pidfile = fopen(linkpidfile, "w")) != NULL) {
+ (void) fprintf(pidfile, "%u\n", (unsigned)getpid());
+ if (ifname[0] != '\0')
+ (void) fprintf(pidfile, "%s\n", ifname);
+ (void) fclose(pidfile);
+ } else {
+ error("Failed to create pid file %s: %m", linkpidfile);
+ linkpidfile[0] = '\0';
+ }
+}
+
+/*
+ * holdoff_end - called via a timeout when the holdoff period ends.
+ */
+/*ARGSUSED*/
+static void
+holdoff_end(arg)
+ void *arg;
+{
+ new_phase(PHASE_DORMANT);
+}
+
+/* List of protocol names, to make our messages a little more informative. */
+struct protocol_list {
+ u_short proto;
+ const char *name;
+} protocol_list[] = {
+ { 0x21, "IP" },
+ { 0x23, "OSI Network Layer" },
+ { 0x25, "Xerox NS IDP" },
+ { 0x27, "DECnet Phase IV" },
+ { 0x29, "Appletalk" },
+ { 0x2b, "Novell IPX" },
+ { 0x2d, "VJ compressed TCP/IP" },
+ { 0x2f, "VJ uncompressed TCP/IP" },
+ { 0x31, "Bridging PDU" },
+ { 0x33, "Stream Protocol ST-II" },
+ { 0x35, "Banyan Vines" },
+ { 0x37, "Old VJ compressed TCP/IP" },
+ { 0x39, "AppleTalk EDDP" },
+ { 0x3b, "AppleTalk SmartBuffered" },
+ { 0x3d, "Multilink" },
+ { 0x3f, "NetBIOS Frame" },
+ { 0x41, "Cisco LAN Extension" },
+ { 0x43, "Ascom Timeplex" },
+ { 0x45, "Fujitsu Link Backup and Load Balancing (LBLB)" },
+ { 0x47, "DCA Remote Lan" },
+ { 0x49, "Serial Data Transport Protocol (PPP-SDTP)" },
+ { 0x4b, "SNA over 802.2" },
+ { 0x4d, "SNA" },
+ { 0x4f, "IP6 Header Compression" },
+ { 0x51, "KNX Bridging" },
+ { 0x53, "Encrypted" },
+ { 0x55, "per-link encrypted" },
+ { 0x57, "IPv6" },
+ { 0x59, "PPP Muxing" },
+ { 0x6f, "Stampede Bridging" },
+ { 0x73, "MP+" },
+ { 0xc1, "STMF" },
+ { 0xfb, "per-link compressed" },
+ { 0xfd, "compressed datagram" },
+ { 0x0201, "802.1d Hello Packets" },
+ { 0x0203, "IBM Source Routing BPDU" },
+ { 0x0205, "DEC LANBridge100 Spanning Tree" },
+ { 0x0207, "Cisco Discovery Protocol" },
+ { 0x0231, "Luxcom" },
+ { 0x0233, "Sigma Network Systems" },
+ { 0x0235, "Apple Client Server Protocol" },
+ { 0x0281, "MPLS Unicast" },
+ { 0x0283, "MPLS Multicast" },
+ { 0x0285, "IEEE p1284.4" },
+ { 0x0287, "ETSI TETRA TNP1" },
+ { 0x4021, "Stacker LZS" },
+ { 0x8021, "Internet Protocol Control Protocol" },
+ { 0x8023, "OSI Network Layer Control Protocol" },
+ { 0x8025, "Xerox NS IDP Control Protocol" },
+ { 0x8027, "DECnet Phase IV Control Protocol" },
+ { 0x8029, "Appletalk Control Protocol" },
+ { 0x802b, "Novell IPX Control Protocol" },
+ { 0x8031, "Bridging Control Protocol" },
+ { 0x8033, "Stream Protocol Control Protocol" },
+ { 0x8035, "Banyan Vines Control Protocol" },
+ { 0x803f, "NetBIOS Frames Control Protocol" },
+ { 0x8041, "Cisco LAN Extension Control Protocol" },
+ { 0x8043, "Ascom Timeplex Control Protocol" },
+ { 0x8045, "Fujitsu LBLB Control Protocol" },
+ { 0x8047, "DCA Remote Lan Network Control Protocol (RLNCP)" },
+ { 0x8049, "Serial Data Control Protocol (PPP-SDCP)" },
+ { 0x804b, "SNA over 802.2 Control Protocol" },
+ { 0x804d, "SNA Control Protocol" },
+ { 0x8051, "KNX Bridging Control Protocol" },
+ { 0x8053, "Encryption Control Protocol" },
+ { 0x8055, "Per-link Encryption Control Protocol" },
+ { 0x8057, "IPv6 Control Protocol" },
+ { 0x806f, "Stampede Bridging Control Protocol" },
+ { 0x80c1, "STMF Control Protocol" },
+ { 0x80fb, "Per-link Compression Control Protocol" },
+ { 0x80fd, "Compression Control Protocol" },
+ { 0x8207, "Cisco Discovery Control Protocol" },
+ { 0x8235, "Apple Client Server Control Protocol" },
+ { 0x8281, "MPLS Control Protocol" },
+ { 0x8287, "ETSI TETRA TNP1 Control Protocol" },
+ { 0xc021, "Link Control Protocol" },
+ { 0xc023, "Password Authentication Protocol" },
+ { 0xc025, "Link Quality Report" },
+ { 0xc027, "Shiva Password Authentication Protocol" },
+ { 0xc029, "CallBack Control Protocol (CBCP)" },
+ { 0xc02b, "Bandwidth Allocation Control Protocol" },
+ { 0xc02d, "BAP" },
+ { 0xc081, "Container Control Protocol" },
+ { 0xc223, "Challenge Handshake Authentication Protocol" },
+ { 0xc227, "Extensible Authentication Protocol" },
+ { 0xc281, "Funk Proprietary Authentication Protocol" },
+ { 0, NULL },
+};
+
+/*
+ * protocol_name - find a name for a PPP protocol.
+ */
+const char *
+protocol_name(proto)
+ int proto;
+{
+ struct protocol_list *lp;
+
+ for (lp = protocol_list; lp->proto != 0; ++lp)
+ if (proto == lp->proto)
+ return (lp->name);
+ return (NULL);
+}
+
+static const char *phase_names[] = { PHASE__NAMES };
+
+const char *
+phase_name(pval)
+ int pval;
+{
+ static char buf[32];
+
+ if (pval < 0 || pval >= Dim(phase_names)) {
+ (void) slprintf(buf, sizeof (buf), "unknown %d", pval);
+ return ((const char *)buf);
+ }
+ return (phase_names[pval]);
+}
+
+/*
+ * get_input - called when incoming data is available.
+ */
+static void
+get_input()
+{
+ int len, i;
+ u_char *p;
+ u_short protocol;
+ struct protent *protp;
+ const char *pname;
+
+ p = inpacket_buf; /* point to beginning of packet buffer */
+
+ len = read_packet(inpacket_buf);
+ if (len < 0)
+ return;
+
+ if (len == 0) {
+ notice("Modem hangup");
+ hungup = 1;
+ status = EXIT_HANGUP;
+ lcp_lowerdown(0); /* serial link is no longer available */
+ link_terminated(0);
+ return;
+ }
+
+ if (debug /*&& (debugflags & DBG_INPACKET)*/)
+ dbglog("rcvd %P", p, len);
+
+ if (len < PPP_HDRLEN) {
+ dbglog("Discarded short packet (%d < %d)", len, PPP_HDRLEN);
+ return;
+ }
+
+ p += 2; /* Skip address and control */
+ GETSHORT(protocol, p);
+ len -= PPP_HDRLEN;
+
+ pname = debug ? NULL : protocol_name(protocol);
+
+ /*
+ * Toss all non-LCP packets unless LCP is in Opened state and
+ * discard non-authentication protocols if we're not yet
+ * authenticated.
+ */
+ if ((protocol != PPP_LCP &&
+ (phase < PHASE_AUTHENTICATE || phase > PHASE_RUNNING)) ||
+ (phase <= PHASE_AUTHENTICATE &&
+ !(protocol == PPP_LCP || protocol == PPP_LQR ||
+ protocol == PPP_PAP || protocol == PPP_CHAP))) {
+ if (pname == NULL)
+ dbglog("Discarded proto 0x%x in %s phase",
+ protocol, phase_name(phase));
+ else
+ dbglog("Discarded %s (0x%x) in %s phase",
+ pname, protocol, phase_name(phase));
+ return;
+ }
+
+ /*
+ * Upcall the proper protocol input routine.
+ */
+ for (i = 0; (protp = protocols[i]) != NULL; ++i) {
+ if (protp->protocol == protocol && protp->enabled_flag) {
+ (*protp->input)(0, p, len);
+ return;
+ }
+ if (protocol == (protp->protocol & ~0x8000) && protp->enabled_flag
+ && protp->datainput != NULL) {
+ (*protp->datainput)(0, p, len);
+ return;
+ }
+ }
+
+ if (debug) {
+ if (pname != NULL)
+ warn("Unsupported protocol '%s' (0x%x) received", pname, protocol);
+ else
+ warn("Unsupported protocol 0x%x received", protocol);
+ }
+ lcp_sprotrej(0, p - PPP_HDRLEN, len + PPP_HDRLEN);
+}
+
+/*
+ * new_phase - signal the start of a new phase of pppd's operation.
+ */
+void
+new_phase(p)
+ int p;
+{
+ if (new_phase_hook != NULL)
+ (*new_phase_hook)(p, phase);
+ phase = p;
+}
+
+/*
+ * die - clean up state and exit with the specified status.
+ */
+void
+die(status)
+ int status;
+{
+ cleanup();
+ if (phase != PHASE_EXIT) {
+ syslog(LOG_INFO, "Exit.");
+ new_phase(PHASE_EXIT);
+ }
+ exit(status);
+}
+
+/*
+ * cleanup - restore anything which needs to be restored before we exit
+ */
+static void
+cleanup()
+{
+ sys_cleanup(); /* XXX: Need to check if this is okay after close_tty */
+
+ if (fd_ppp >= 0) {
+ fd_ppp = -1;
+ disestablish_ppp(ttyfd);
+ }
+ if (real_ttyfd >= 0)
+ close_tty();
+
+ if (pidfilename[0] != '\0' && unlink(pidfilename) < 0 && errno != ENOENT)
+ warn("unable to delete pid file %s: %m", pidfilename);
+ pidfilename[0] = '\0';
+ if (linkpidfile[0] != '\0' && unlink(linkpidfile) < 0 && errno != ENOENT)
+ warn("unable to delete pid file %s: %m", linkpidfile);
+ linkpidfile[0] = '\0';
+
+ if (locked) {
+ locked = 0;
+ unlock();
+ }
+
+#ifdef HAVE_MULTILINK
+ if (pppdb != NULL) {
+ cleanup_db();
+ pppdb = NULL;
+ }
+#endif
+}
+
+/*
+ * close_tty - restore the terminal device and close it.
+ */
+static void
+close_tty()
+{
+ int fd = real_ttyfd;
+
+ real_ttyfd = -1;
+
+ /* drop dtr to hang up */
+ if (!default_device && modem) {
+ setdtr(fd, 0);
+ /*
+ * This sleep is in case the serial port has CLOCAL set by default,
+ * and consequently will reassert DTR when we close the device.
+ */
+ (void) sleep(1);
+ }
+
+ restore_tty(fd);
+
+ if (tty_mode != (mode_t) -1) {
+ if (fchmod(fd, tty_mode) != 0) {
+ /* XXX if devnam is a symlink, this will change the link */
+ if (chmod(devnam, tty_mode) != 0) {
+ error("Unable to chmod file %s: %m", devnam);
+ }
+ }
+ }
+
+ (void) close(fd);
+}
+
+/*
+ * update_link_stats - get stats at link termination.
+ */
+void
+update_link_stats(u)
+ int u;
+{
+ struct timeval now;
+ char numbuf[32];
+
+ if (gettimeofday(&now, NULL) >= 0) {
+ link_connect_time = now.tv_sec - start_time.tv_sec;
+ (void) slprintf(numbuf, sizeof(numbuf), "%d", link_connect_time);
+ script_setenv("CONNECT_TIME", numbuf, 0);
+ } else {
+ link_connect_time = 0;
+ }
+
+ if (get_ppp_stats(u, &link_stats)) {
+ (void) slprintf(numbuf, sizeof(numbuf), "%" PPP_COUNTER_F,
+ link_stats.bytes_out);
+ script_setenv("BYTES_SENT", numbuf, 0);
+ (void) slprintf(numbuf, sizeof(numbuf), "%" PPP_COUNTER_F,
+ link_stats.bytes_in);
+ script_setenv("BYTES_RCVD", numbuf, 0);
+ (void) slprintf(numbuf, sizeof(numbuf), "%" PPP_COUNTER_F,
+ link_stats.pkts_in);
+ script_setenv("PKTS_RCVD", numbuf, 0);
+ (void) slprintf(numbuf, sizeof(numbuf), "%" PPP_COUNTER_F,
+ link_stats.pkts_out);
+ script_setenv("PKTS_SENT", numbuf, 0);
+ link_stats_valid = 1;
+ }
+}
+
+
+struct callout {
+ struct timeval c_time; /* time at which to call routine */
+ void *c_arg; /* argument to routine */
+ void (*c_func) __P((void *)); /* routine */
+ struct callout *c_next;
+};
+
+static struct callout *callout = NULL; /* Callout list */
+static struct timeval timenow; /* Current time */
+
+/*
+ * timeout - Schedule a timeout.
+ *
+ * Note that this timeout takes the number of seconds, NOT hz (as in
+ * the kernel).
+ */
+void
+timeout(func, arg, time)
+ void (*func) __P((void *));
+ void *arg;
+ int time;
+{
+ struct callout *newp, *p, **pp;
+
+ MAINDEBUG(("Timeout %p:%p in %d seconds.", func, arg, time));
+
+ /*
+ * Allocate timeout.
+ */
+ if ((newp = (struct callout *) malloc(sizeof(struct callout))) == NULL)
+ novm("callout structure for timeout.");
+ newp->c_arg = arg;
+ newp->c_func = func;
+ (void) gettimeofday(&timenow, NULL);
+ newp->c_time.tv_sec = timenow.tv_sec + time;
+ newp->c_time.tv_usec = timenow.tv_usec;
+
+ /*
+ * Find correct place and link it in.
+ */
+ for (pp = &callout; (p = *pp) != NULL; pp = &p->c_next)
+ if (newp->c_time.tv_sec < p->c_time.tv_sec
+ || (newp->c_time.tv_sec == p->c_time.tv_sec
+ && newp->c_time.tv_usec < p->c_time.tv_usec))
+ break;
+ newp->c_next = p;
+ *pp = newp;
+}
+
+
+/*
+ * untimeout - Unschedule a timeout.
+ */
+void
+untimeout(func, arg)
+ void (*func) __P((void *));
+ void *arg;
+{
+ struct callout **copp, *freep;
+
+ MAINDEBUG(("Untimeout %p:%p.", func, arg));
+
+ /*
+ * Find first matching timeout and remove it from the list.
+ */
+ for (copp = &callout; (freep = *copp) != NULL; copp = &freep->c_next)
+ if (freep->c_func == func && freep->c_arg == arg) {
+ *copp = freep->c_next;
+ free((char *) freep);
+ break;
+ }
+}
+
+
+/*
+ * calltimeout - Call any timeout routines which are now due.
+ */
+static void
+calltimeout()
+{
+ struct callout *p;
+
+ while (callout != NULL) {
+ p = callout;
+
+ if (gettimeofday(&timenow, NULL) < 0)
+ fatal("Failed to get time of day: %m");
+ if (!(p->c_time.tv_sec < timenow.tv_sec
+ || (p->c_time.tv_sec == timenow.tv_sec
+ && p->c_time.tv_usec <= timenow.tv_usec)))
+ break; /* no, it's not time yet */
+
+ callout = p->c_next;
+ (*p->c_func)(p->c_arg);
+
+ free((char *) p);
+ }
+}
+
+
+/*
+ * timeleft - return the length of time until the next timeout is due.
+ */
+static struct timeval *
+timeleft(tvp)
+ struct timeval *tvp;
+{
+ if (callout == NULL)
+ return (NULL);
+
+ (void) gettimeofday(&timenow, NULL);
+ tvp->tv_sec = callout->c_time.tv_sec - timenow.tv_sec;
+ tvp->tv_usec = callout->c_time.tv_usec - timenow.tv_usec;
+ if (tvp->tv_usec < 0) {
+ tvp->tv_usec += 1000000;
+ tvp->tv_sec -= 1;
+ }
+ if (tvp->tv_sec < 0)
+ tvp->tv_sec = tvp->tv_usec = 0;
+
+ return (tvp);
+}
+
+
+/*
+ * kill_my_pg - send a signal to our process group, and ignore it ourselves.
+ */
+static void
+kill_my_pg(sig)
+ int sig;
+{
+ struct sigaction act, oldact;
+ sigset_t mask;
+
+ BZERO(&act, sizeof (act));
+ act.sa_handler = SIG_IGN;
+ (void) sigemptyset(&mask);
+ (void) sigaddset(&mask, sig);
+ /*
+ * Ignore signal 'sig' temporarily, before finally re-activating the
+ * original handler. We need to do it in the following sequence, since
+ * otherwise the signal handler for 'sig' will be called forever.
+ */
+ if (sigaction(sig, &act, &oldact) < 0) {
+ fatal("kill_my_pg: couldn't establish signal handler (%d): %m", sig);
+ }
+ (void) sigprocmask(SIG_UNBLOCK, &mask, NULL);
+ /*
+ * Send signal 'sig' to all processes whose process group ID is equal
+ * to the process group ID of the sender.
+ */
+ (void) kill(0, sig);
+ if (sigaction(sig, &oldact, NULL) < 0) {
+ fatal("kill_my_pg: couldn't establish signal handler (%d): %m", sig);
+ }
+}
+
+
+/*
+ * hup - Catch SIGHUP signal.
+ *
+ * Indicates that the physical layer has been disconnected.
+ * We don't rely on this indication; if the user has sent this
+ * signal, we just take the link down.
+ */
+static void
+hup(sig)
+ int sig;
+{
+ info("Hangup (SIGHUP)");
+ kill_link = 1;
+ if (status != EXIT_HANGUP)
+ status = EXIT_USER_REQUEST;
+ if (conn_running > 0)
+ /* Send the signal to the [dis]connector process(es) also */
+ kill_my_pg(sig);
+ if (charshunt_pid)
+ (void) kill(charshunt_pid, sig);
+ if (waiting)
+ siglongjmp(sigjmp, 1);
+}
+
+
+/*
+ * term - Catch SIGTERM signal and SIGINT signal (^C/del).
+ *
+ * Indicates that we should initiate a graceful disconnect and exit.
+ */
+/*ARGSUSED*/
+static void
+term(sig)
+ int sig;
+{
+ info("Terminating on signal %d.", sig);
+ persist = 0; /* don't try to restart */
+ kill_link = 1;
+ status = EXIT_USER_REQUEST;
+ if (conn_running > 0)
+ /* Send the signal to the [dis]connector process(es) also */
+ kill_my_pg(sig);
+ if (charshunt_pid)
+ (void) kill(charshunt_pid, sig);
+ if (waiting)
+ siglongjmp(sigjmp, 1);
+}
+
+
+/*
+ * chld - Catch SIGCHLD signal.
+ * Sets a flag so we will call reap_kids in the mainline.
+ */
+/*ARGSUSED*/
+static void
+chld(sig)
+ int sig;
+{
+ got_sigchld = 1;
+ if (waiting)
+ siglongjmp(sigjmp, 1);
+}
+
+/*
+ * toggle_debug - Catch SIGUSR1 signal.
+ *
+ * Toggle debug flag.
+ */
+/*ARGSUSED*/
+static void
+toggle_debug(sig)
+ int sig;
+{
+ if (debug) {
+ print_ncpstate(0, NULL);
+ dbglog("debug logging disabled");
+ (void) setlogmask(LOG_UPTO(LOG_WARNING));
+ debug = 0;
+ } else {
+ (void) setlogmask(LOG_UPTO(LOG_DEBUG));
+ dbglog("debug logging enabled");
+ print_ncpstate(0, NULL);
+ debug = 1;
+ }
+}
+
+
+/*
+ * open_ccp - Catch SIGUSR2 signal.
+ *
+ * Try to (re)negotiate compression.
+ */
+/*ARGSUSED*/
+static void
+open_ccp(sig)
+ int sig;
+{
+ open_ccp_flag = 1;
+ if (waiting)
+ siglongjmp(sigjmp, 1);
+}
+
+
+/*
+ * bad_signal - We've caught a fatal signal. Clean up state and exit.
+ */
+static void
+bad_signal(sig)
+ int sig;
+{
+ static int crashed = 0;
+
+ if (crashed)
+ _exit(127);
+ crashed = 1;
+ error("Fatal signal %d", sig);
+ if (conn_running > 0)
+ kill_my_pg(SIGTERM);
+ if (charshunt_pid)
+ (void) kill(charshunt_pid, SIGTERM);
+ die(127);
+}
+
+
+/*
+ * device_script - run a program to talk to the serial device
+ * (e.g. to run the connector or disconnector script).
+ */
+static int
+device_script(program, in, out, dont_wait, optname)
+ char *program;
+ int in, out;
+ int dont_wait;
+ char *optname;
+{
+ pid_t pid;
+ int status = -1;
+ int errfd;
+ int envpipe[2];
+
+ envpipe[0] = envpipe[1] = -1;
+ if (!dont_wait && device_pipe_hook != NULL && pipe(envpipe) == -1) {
+ error("Cannot create pipe for child: %m");
+ return (-1);
+ }
+
+ ++conn_running;
+ pid = fork();
+
+ if (pid == (pid_t)-1) {
+ --conn_running;
+ error("Failed to create child process: %m");
+ return (-1);
+ }
+
+ if (pid == (pid_t)0) {
+ sys_close();
+ closelog();
+ if (envpipe[0] >= 0) {
+ if (envpipe[1] <= 2)
+ envpipe[1] = dup(envpipe[1]);
+ (void) close(envpipe[0]);
+ }
+ if (in == 2) {
+ /* aargh!!! */
+ int newin = dup(in);
+ if (in == out)
+ out = newin;
+ in = newin;
+ } else if (out == 2) {
+ out = dup(out);
+ }
+ if (log_to_fd >= 0) {
+ if (log_to_fd != 2) {
+ if (dup2(log_to_fd, 2) < 0)
+ error("dup2(log_to_fd, STDERR) failed: %m");
+ }
+ } else {
+ (void) close(2);
+ errfd = open(_PATH_CONNERRS, O_WRONLY | O_APPEND | O_CREAT, 0600);
+ if (errfd >= 0 && errfd != 2) {
+ if (dup2(errfd, 2) < 0)
+ error("dup2(errfd, STDERR) failed: %m");
+ (void) close(errfd);
+ }
+ }
+ if (in != 0) {
+ if (out == 0)
+ out = dup(out);
+ if (dup2(in, 0) < 0)
+ error("dup2(in, STDIN) failed: %m");
+ }
+ if (out != 1) {
+ if (dup2(out, 1) < 0)
+ error("dup2(out, STDOUT) failed: %m");
+ }
+ if (envpipe[0] >= 0 && dup2(envpipe[1], 3) < 0)
+ error("dup2(pipe, pipeout) failed: %m");
+ if (real_ttyfd > 2)
+ (void) close(real_ttyfd);
+ if (pty_master > 2)
+ (void) close(pty_master);
+ if (pty_slave > 2) {
+ (void) close(pty_slave);
+ pty_slave = -1;
+ }
+ (void) setuid(uid);
+ if (getuid() != uid) {
+ error("setuid failed");
+ exit(1);
+ }
+ (void) setgid(getgid());
+ if (script_env != NULL) {
+ while (*script_env != NULL) {
+ if (putenv(*script_env) == -1)
+ warn("unable to set %s for %s: %m", *script_env, program);
+ script_env++;
+ }
+ }
+ (void) execl("/bin/sh", "sh", "-c", program, (char *)0);
+ error("could not exec /bin/sh: %m");
+ exit(99);
+ /* NOTREACHED */
+ }
+
+ if (debug)
+ dbglog("%s option: '%s' started (pid %d)", optname, program, pid);
+ if (dont_wait) {
+ record_child(pid, program, NULL, NULL);
+ status = 0;
+ } else {
+ if (envpipe[0] >= 0) {
+ (void) close(envpipe[1]);
+ (*device_pipe_hook)(envpipe[0]);
+ }
+ while (waitpid(pid, &status, 0) < 0) {
+ if (errno == EINTR)
+ continue;
+ fatal("error waiting for (dis)connection process: %m");
+ }
+ if (envpipe[0] >= 0)
+ (void) close(envpipe[0]);
+ --conn_running;
+ }
+
+ return (status == 0 ? 0 : -1);
+}
+
+
+/*
+ * run-program - execute a program with given arguments,
+ * but don't wait for it.
+ * If the program can't be executed, logs an error unless
+ * must_exist is 0 and the program file doesn't exist.
+ * Returns -1 if it couldn't fork, 0 if the file doesn't exist
+ * or isn't an executable plain file, or the process ID of the child.
+ * If done != NULL, (*done)(arg, int) will be called later (within
+ * reap_kids) if this routine returns value > 0.
+ */
+pid_t
+run_program(prog, args, must_exist, done, arg)
+ char *prog;
+ char **args;
+ int must_exist;
+ void (*done) __P((void *arg, int status));
+ void *arg;
+{
+ pid_t pid;
+ struct stat sbuf;
+ int retv;
+
+ /*
+ * First check if the file exists and is executable.
+ * We don't use access() because that would use the
+ * real user-id, which might not be root, and the script
+ * might be accessible only to root.
+ */
+ errno = EINVAL;
+ if (stat(prog, &sbuf) < 0 || !S_ISREG(sbuf.st_mode)
+ || (sbuf.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) == 0) {
+ if (must_exist || errno != ENOENT)
+ warn("Can't execute %s: %m", prog);
+ return (0);
+ }
+
+ if (updown_script_hook != NULL) {
+ retv = (*updown_script_hook)((const char ***)&args);
+ if (retv == -1) {
+ return (-1);
+ }
+ }
+
+ pid = fork();
+ if (pid == (pid_t)-1) {
+ error("Failed to create child process for %s: %m", prog);
+ return (-1);
+ }
+ if (pid == (pid_t)0) {
+ int new_fd;
+
+ /* Leave the current location */
+ (void) setsid(); /* No controlling tty. */
+ (void) umask (S_IRWXG|S_IRWXO);
+ (void) chdir ("/"); /* no current directory. */
+ (void) setuid(0); /* set real UID = root */
+ (void) setgid(getegid());
+
+ /* Ensure that nothing of our device environment is inherited. */
+ sys_close();
+ closelog();
+ (void) close(0);
+ (void) close(1);
+ (void) close(2);
+ (void) close(ttyfd); /* tty interface to the ppp device */
+ if (real_ttyfd >= 0)
+ (void) close(real_ttyfd);
+
+ /* Don't pass handles to the PPP device, even by accident. */
+ new_fd = open (_PATH_DEVNULL, O_RDWR);
+ if (new_fd >= 0) {
+ if (new_fd != 0) {
+ if (dup2(new_fd, 0) < 0) /* stdin <- /dev/null */
+ error("dup2(/dev/null, STDIN) failed: %m");
+ (void) close(new_fd);
+ }
+ if (dup2(0, 1) < 0) /* stdout -> /dev/null */
+ error("dup2(/dev/null, STDOUT) failed: %m");
+ if (dup2(0, 2) < 0) /* stderr -> /dev/null */
+ error("dup2(/dev/null, STDERR) failed: %m");
+ }
+
+#ifdef BSD
+ /* Force the priority back to zero if pppd is running higher. */
+ if (setpriority (PRIO_PROCESS, 0, 0) < 0)
+ warn("can't reset priority to 0: %m");
+#endif
+
+ /* SysV recommends a second fork at this point. */
+
+ /* run the program */
+ (void) execve(prog, args, script_env);
+ if (must_exist || errno != ENOENT) {
+ /* have to reopen the log, there's nowhere else
+ for the message to go. */
+ reopen_log();
+ syslog(LOG_ERR, "Can't execute %s: %m", prog);
+ closelog();
+ }
+ _exit(-1);
+ }
+
+ if (debug)
+ dbglog("Script %s started (pid %d)", prog, pid);
+ record_child(pid, prog, done, arg);
+
+ return (pid);
+}
+
+
+/*
+ * record_child - add a child process to the list for reap_kids
+ * to use.
+ */
+static void
+record_child(pid, prog, done, arg)
+ pid_t pid;
+ char *prog;
+ void (*done) __P((void *, int));
+ void *arg;
+{
+ struct subprocess *chp;
+
+ ++n_children;
+
+ chp = (struct subprocess *) malloc(sizeof(struct subprocess));
+ if (chp == NULL) {
+ warn("losing track of %s process", prog);
+ } else {
+ chp->pid = pid;
+ chp->prog = prog;
+ chp->done = done;
+ chp->arg = arg;
+ chp->next = children;
+ children = chp;
+ }
+}
+
+
+/*
+ * reap_kids - get status from any dead child processes,
+ * and log a message for abnormal terminations.
+ */
+static int
+reap_kids(waitfor)
+ int waitfor;
+{
+ pid_t pid;
+ int status, i;
+ struct subprocess *chp, **prevp;
+
+ got_sigchld = 0;
+ if (n_children == 0)
+ return (0);
+
+ /*CONSTANTCONDITION*/
+ while (1) {
+ pid = waitpid(-1, &status, (waitfor ? 0 : WNOHANG));
+ if (pid == 0) {
+ break; /* return 0 */
+ } else if (pid == -1) {
+ if (errno == EINTR)
+ continue;
+ if (errno != ECHILD)
+ error("Error waiting for child process: %m");
+ return (-1);
+ } else {
+ for (prevp = &children; (chp = *prevp) != NULL;
+ prevp = &chp->next) {
+ if (chp->pid == pid) {
+ --n_children;
+ *prevp = chp->next;
+ break;
+ }
+ }
+ if (WIFSIGNALED(status) || WIFSTOPPED(status)) {
+ i = WIFSIGNALED(status) ? WTERMSIG(status) : WSTOPSIG(status);
+ warn("Child process %s (pid %d) %s with signal %d (%s)",
+ (chp != NULL ? chp->prog : "??"), pid,
+ (WIFSIGNALED(status) ? "terminated" : "stopped"),
+ i, signal_name(i));
+ } else if (debug) {
+ dbglog("Child process %s finished (pid %d), status = %d",
+ (chp != NULL ? chp->prog: "??"), pid,
+ WEXITSTATUS(status));
+ }
+ if ((chp != NULL) && (chp->done != NULL))
+ (*chp->done)(chp->arg, status);
+ if (chp != NULL)
+ free(chp);
+ }
+ }
+ return (0);
+}
+
+/*
+ * infanticide - timeout while waiting for child process.
+ */
+/*ARGSUSED*/
+static void
+infanticide(sig)
+ int sig;
+{
+ struct subprocess *chp;
+ static int runcount = 0;
+
+ if (runcount < 2) {
+ for (chp = children; chp != NULL; chp = chp->next)
+ (void) kill(chp->pid, runcount == 0 ? SIGTERM : SIGKILL);
+ } else {
+ kill_my_pg(SIGTERM);
+ /* Quit and hope for the best. */
+ n_children = 0;
+ }
+ runcount++;
+}
+
+/*
+ * Perform final wait before exiting.
+ */
+static void
+final_reap()
+{
+ struct sigaction sa;
+ struct subprocess *chp;
+
+ if (n_children > 0 && debug) {
+ dbglog("Waiting for %d child processes...", n_children);
+ for (chp = children; chp != NULL; chp = chp->next)
+ dbglog(" pid %d: %s", chp->pid, chp->prog);
+ }
+ BZERO(&sa, sizeof (sa));
+/*CONSTANTCONDITION*/ SIGNAL(SIGALRM, infanticide);
+ while (n_children > 0) {
+ (void) alarm(7);
+ if (reap_kids(1) < 0)
+ break;
+ }
+ (void) alarm(0);
+}
+
+/*
+ * novm - log an error message saying we ran out of memory, and die.
+ */
+void
+novm(msg)
+ char *msg;
+{
+ fatal("Virtual memory exhausted allocating %s\n", msg);
+}
+
+/*
+ * script_setenv - set an environment variable value to be used
+ * for scripts that we run (e.g. ip-up, auth-up, etc.)
+ */
+void
+script_setenv(var, value, iskey)
+ const char *var;
+ const char *value;
+ int iskey;
+{
+ size_t varl = strlen(var);
+ size_t vl = varl + strlen(value) + 2;
+ int i;
+ char *p, *newstring;
+
+ /*
+ * XXX: Can we assert that a tdb write lock is held here ? It appears that
+ * Linux's use of tdb is not safe.
+ */
+ newstring = (char *) malloc(vl+1);
+ if (newstring == NULL) {
+ novm("script environment string");
+ return;
+ }
+ *newstring++ = iskey;
+ (void) slprintf(newstring, vl, "%s=%s", var, value);
+
+ /* check if this variable is already set */
+ if (script_env != NULL) {
+ for (i = 0; (p = script_env[i]) != NULL; ++i) {
+ if (strncmp(p, var, varl) == 0 && p[varl] == '=') {
+#ifdef HAVE_MULTILINK
+ if (p[-1] != '\0' && pppdb != NULL)
+ delete_db_key(p);
+#endif
+ free(p-1);
+ script_env[i] = newstring;
+#ifdef HAVE_MULTILINK
+ if (iskey && pppdb != NULL)
+ add_db_key(newstring);
+ update_db_entry();
+#endif
+ return;
+ }
+ }
+ } else {
+ /* no space allocated for script env. ptrs. yet */
+ i = 0;
+ script_env = (char **) malloc(16 * sizeof(char *));
+ if (script_env == NULL) {
+ novm("script environment variable.");
+ return;
+ }
+ s_env_nalloc = 16;
+ }
+
+ /* reallocate script_env with more space if needed */
+ if (i + 1 >= s_env_nalloc) {
+ int new_n = i + 17;
+ char **newenv = (char **) realloc((void *)script_env,
+ new_n * sizeof(char *));
+ if (newenv == NULL) {
+ novm("expanded script environment variable.");
+ return;
+ }
+ script_env = newenv;
+ s_env_nalloc = new_n;
+ }
+
+ script_env[i] = newstring;
+ script_env[i+1] = NULL;
+
+#ifdef HAVE_MULTILINK
+ if (pppdb != NULL) {
+ if (iskey)
+ add_db_key(newstring);
+ update_db_entry();
+ }
+#endif
+}
+
+/*
+ * script_unsetenv - remove a variable from the environment
+ * for scripts.
+ */
+void
+script_unsetenv(var)
+ const char *var;
+{
+ int vl = strlen(var);
+ int i;
+ char *p;
+
+ /*
+ * XXX: Can we assert that a tdb write lock is held here ? It appears that
+ * Linux's use of tdb is not safe.
+ */
+ if (script_env == NULL)
+ return;
+ for (i = 0; (p = script_env[i]) != NULL; ++i) {
+ if (strncmp(p, var, vl) == 0 && p[vl] == '=') {
+#ifdef HAVE_MULTILINK
+ if (p[-1] != '\0' && pppdb != NULL)
+ delete_db_key(p);
+#endif
+ free(p-1);
+ while ((script_env[i] = script_env[i+1]) != NULL)
+ ++i;
+ break;
+ }
+ }
+#ifdef HAVE_MULTILINK
+ if ((pppdb != NULL) && (p != NULL))
+ update_db_entry();
+#endif
+}
+
+/*
+ * script_getenv - find a variable in the script environment.
+ */
+const char *
+script_getenv(var)
+ const char *var;
+{
+ int vl = strlen(var);
+ int i;
+ char *p;
+
+ if (script_env == NULL)
+ return (NULL);
+ for (i = 0; (p = script_env[i]) != NULL; ++i) {
+ if (strncmp(p, var, vl) == 0 && p[vl] == '=')
+ return ((const char *)p+vl+1);
+ }
+ return (NULL);
+}
+
+#ifdef HAVE_MULTILINK
+/*
+ * update_db_entry - update our entry in the database.
+ */
+static void
+update_db_entry()
+{
+ TDB_DATA key, dbuf;
+ int vlen, i;
+ char *p, *q, *vbuf;
+
+ if (script_env == NULL)
+ return;
+ /*
+ * vlen needs to be initialized as 1, or otherwise, the last string
+ * is truncated by slprintf.
+ */
+ vlen = 1;
+ for (i = 0; (p = script_env[i]) != NULL; ++i)
+ vlen += strlen(p) + 1;
+ vbuf = malloc(vlen);
+ if (vbuf == NULL)
+ novm("database entry");
+ q = vbuf;
+ for (i = 0; (p = script_env[i]) != NULL; ++i)
+ q += slprintf(q, vbuf + vlen - q, "%s;", p);
+
+ key.dptr = db_key;
+ key.dsize = strlen(db_key);
+ dbuf.dptr = vbuf;
+ dbuf.dsize = vlen;
+ if (tdb_store(pppdb, key, dbuf, TDB_REPLACE))
+ error("tdb_store failed: %s", tdb_error(pppdb));
+}
+
+/*
+ * add_db_key - add a key that we can use to look up our database entry.
+ */
+static void
+add_db_key(str)
+ const char *str;
+{
+ TDB_DATA key, dbuf;
+
+ key.dptr = (char *) str;
+ key.dsize = strlen(str);
+ dbuf.dptr = db_key;
+ dbuf.dsize = strlen(db_key);
+ if (tdb_store(pppdb, key, dbuf, TDB_REPLACE))
+ error("tdb_store key failed: %s", tdb_error(pppdb));
+}
+
+/*
+ * delete_db_key - delete a key for looking up our database entry.
+ */
+static void
+delete_db_key(str)
+ const char *str;
+{
+ TDB_DATA key;
+
+ key.dptr = (char *) str;
+ key.dsize = strlen(str);
+ (void) tdb_delete(pppdb, key);
+}
+
+/*
+ * cleanup_db - delete all the entries we put in the database.
+ */
+static void
+cleanup_db()
+{
+ TDB_DATA key;
+ int i;
+ char *p;
+
+ key.dptr = db_key;
+ key.dsize = strlen(db_key);
+ (void) tdb_delete(pppdb, key);
+ for (i = 0; (p = script_env[i]) != NULL; ++i)
+ if (p[-1] != '\0')
+ delete_db_key(p);
+}
+#endif /* HAVE_MULTILINK */
+
+/*
+ * open_socket - establish a stream socket connection to the nominated
+ * host and port.
+ * XXX: Need IPv6 support for those systems that support it (use getaddrinfo),
+ * but requires portability changes.
+ */
+static int
+open_socket(dest)
+ char *dest;
+{
+ char *sep, *endp = NULL;
+ int sock;
+ int port = -1;
+ u_int32_t host;
+ struct hostent *hent = NULL;
+ struct sockaddr_in sad;
+ struct servent *se;
+
+ /* parse host:port and resolve host to an IP address */
+ sep = strchr(dest, ':');
+ if (sep != NULL) {
+ se = getservbyname((const char *)sep+1, "tcp");
+ if (se != NULL) {
+ port = ntohs(se->s_port);
+ } else {
+ port = strtol(sep+1, &endp, 10);
+ if (endp == sep+1 || *endp != '\0') {
+ error("Can't parse host:port for socket destination");
+ return (-1);
+ }
+ }
+ }
+ if (port < 0 || port > 65535 || sep == dest) {
+ error("Can't parse host:port for socket destination");
+ return (-1);
+ }
+ *sep = '\0';
+ host = inet_addr(dest);
+ if (host == (u_int32_t) -1) {
+ hent = gethostbyname(dest);
+ if (hent == NULL) {
+ error("%s: unknown host in socket option", dest);
+ *sep = ':';
+ return (-1);
+ }
+ BCOPY(hent->h_addr_list[0], &host, sizeof(host));
+ hent->h_addr_list++;
+ }
+ *sep = ':';
+
+ for (;;) {
+ /* get a socket and connect it to the other end */
+ sock = socket(PF_INET, SOCK_STREAM, 0);
+ if (sock < 0) {
+ error("Can't create socket: %m");
+ return (-1);
+ }
+ BZERO(&sad, sizeof(sad));
+ sad.sin_family = AF_INET;
+ sad.sin_port = htons(port);
+ sad.sin_addr.s_addr = host;
+ if (connect(sock, (struct sockaddr *)&sad, sizeof(sad)) >= 0) {
+ break; /* return sock file descriptor */
+ }
+ if ((hent != NULL) && (hent->h_addr_list != NULL)) {
+ BCOPY(hent->h_addr_list[0], &host, sizeof(host));
+ hent->h_addr_list++;
+ (void) close(sock);
+ continue;
+ }
+ error("Can't connect to %s: %m", dest);
+ (void) close(sock);
+ return (-1);
+ }
+ return (sock);
+}
+
+/*
+ * print_ncpstate - prints out current NCP state.
+ *
+ * We're normally called from SIGUSR1 here, but this is safe because
+ * these signals are blocked unless we're idle waiting for events.
+ * There's no need to otherwise lock the data structures referenced.
+ */
+void
+print_ncpstate(unit, strptr)
+ int unit;
+ FILE *strptr;
+{
+ struct protent *protp;
+ int i;
+
+ (void) flprintf(strptr, "In %s phase\n", phase_name(phase));
+ for (i = 0; (protp = protocols[i]) != NULL; ++i) {
+ if (protp->print_stat != NULL)
+ (*protp->print_stat)(unit, strptr);
+ }
+ sys_print_state(strptr);
+}
+
+/*
+ * start_charshunt - create a child process to run the character shunt.
+ */
+static int
+start_charshunt(ifd, ofd)
+ int ifd, ofd;
+{
+ pid_t cpid;
+
+ cpid = fork();
+ if (cpid == (pid_t)-1) {
+ error("Can't fork process for character shunt: %m");
+ return (0);
+ }
+ if (cpid == (pid_t)0) {
+ /* child */
+ (void) close(pty_slave);
+ pty_slave = -1;
+ (void) setgid(getgid());
+ (void) setuid(uid);
+ if (getuid() != uid)
+ fatal("setuid failed");
+ if (!nodetach)
+ log_to_fd = -1;
+ charshunt(ifd, ofd, record_file);
+ exit(0);
+ }
+ charshunt_pid = cpid;
+ (void) close(pty_master);
+ pty_master = -1;
+ ttyfd = pty_slave;
+ record_child(cpid, "pppd (charshunt)", charshunt_done, NULL);
+ return (1);
+}
+
+/*ARGSUSED*/
+static void
+charshunt_done(arg, status)
+ void *arg;
+ int status;
+{
+ charshunt_pid = (pid_t)0;
+}
+
+static void
+reportme(int signo)
+{
+ dbglog("charshunt taking signal %d", signo);
+ exit(1);
+}
+
+/*
+ * charshunt - the character shunt, which passes characters between
+ * the pty master side and the serial port (or stdin/stdout).
+ * This runs as the user (not as root).
+ * (We assume ofd >= ifd which is true the way this gets called. :-).
+ */
+static void
+charshunt(ifd, ofd, record_file)
+ int ifd, ofd;
+ char *record_file;
+{
+ int n, nfds;
+ fd_set ready, writey;
+ u_char *ibufp, *obufp;
+ int nibuf, nobuf;
+ int flags;
+ struct timeval lasttime;
+ FILE *recordf = NULL;
+ int ilevel, olevel, max_level;
+ struct timeval levelt, tout, *top;
+
+ /*
+ * Reset signal handlers.
+ */
+ (void) signal(SIGHUP, SIG_IGN); /* Hangup */
+ (void) signal(SIGINT, reportme); /* Interrupt */
+ (void) signal(SIGTERM, reportme); /* Terminate */
+ (void) signal(SIGCHLD, reportme);
+ (void) signal(SIGUSR1, reportme);
+ (void) signal(SIGUSR2, reportme);
+ (void) signal(SIGABRT, reportme);
+ (void) signal(SIGALRM, reportme);
+ (void) signal(SIGFPE, reportme);
+ (void) signal(SIGILL, reportme);
+ (void) signal(SIGPIPE, reportme);
+ (void) signal(SIGQUIT, reportme);
+#ifndef DEBUG
+ (void) signal(SIGSEGV, reportme);
+#endif
+#ifdef SIGBUS
+ (void) signal(SIGBUS, reportme);
+#endif
+#ifdef SIGEMT
+ (void) signal(SIGEMT, reportme);
+#endif
+#ifdef SIGPOLL
+ (void) signal(SIGPOLL, reportme);
+#endif
+#ifdef SIGPROF
+ (void) signal(SIGPROF, reportme);
+#endif
+#ifdef SIGSYS
+ (void) signal(SIGSYS, reportme);
+#endif
+#ifdef SIGTRAP
+ (void) signal(SIGTRAP, reportme);
+#endif
+#ifdef SIGVTALRM
+ (void) signal(SIGVTALRM, reportme);
+#endif
+#ifdef SIGXCPU
+ (void) signal(SIGXCPU, reportme);
+#endif
+#ifdef SIGXFSZ
+ (void) signal(SIGXFSZ, reportme);
+#endif
+
+ /*
+ * Open the record file if required.
+ */
+ if (record_file != NULL) {
+ recordf = fopen(record_file, "a");
+ if (recordf == NULL)
+ error("Couldn't create record file %s: %m", record_file);
+ }
+
+ /* set all the fds to non-blocking mode */
+ flags = fcntl(pty_master, F_GETFL);
+ if (flags == -1
+ || fcntl(pty_master, F_SETFL, flags | O_NONBLOCK) == -1)
+ warn("couldn't set pty master to nonblock: %m");
+ flags = fcntl(ifd, F_GETFL);
+ if (flags == -1
+ || fcntl(ifd, F_SETFL, flags | O_NONBLOCK) == -1)
+ warn("couldn't set %s to nonblock: %m", (ifd==0? "stdin": "tty"));
+ if (ofd != ifd) {
+ flags = fcntl(ofd, F_GETFL);
+ if (flags == -1
+ || fcntl(ofd, F_SETFL, flags | O_NONBLOCK) == -1)
+ warn("couldn't set stdout to nonblock: %m");
+ }
+
+ nibuf = nobuf = 0;
+ ibufp = obufp = NULL;
+
+ ilevel = olevel = 0;
+ (void) gettimeofday(&levelt, NULL);
+ if (max_data_rate) {
+ max_level = max_data_rate / 10;
+ if (max_level < MAXLEVELMINSIZE)
+ max_level = MAXLEVELMINSIZE;
+ } else
+ max_level = sizeof(inpacket_buf) + 1;
+
+ nfds = (ofd > pty_master? ofd: pty_master) + 1;
+ if (recordf != NULL) {
+ (void) gettimeofday(&lasttime, NULL);
+ (void) putc(RECMARK_TIMESTART, recordf); /* put start marker */
+ (void) putc(lasttime.tv_sec >> 24, recordf);
+ (void) putc(lasttime.tv_sec >> 16, recordf);
+ (void) putc(lasttime.tv_sec >> 8, recordf);
+ (void) putc(lasttime.tv_sec, recordf);
+ lasttime.tv_usec = 0;
+ }
+
+ while (nibuf != 0 || nobuf != 0 || ofd >= 0 || pty_master >= 0) {
+ top = 0;
+ tout.tv_sec = 0;
+ tout.tv_usec = 10000;
+ FD_ZERO(&ready);
+ FD_ZERO(&writey);
+ if (nibuf != 0) {
+ if (ilevel >= max_level)
+ top = &tout;
+ else if (pty_master >= 0)
+ FD_SET(pty_master, &writey);
+ } else if (ifd >= 0)
+ FD_SET(ifd, &ready);
+ if (nobuf != 0) {
+ if (olevel >= max_level)
+ top = &tout;
+ else if (ofd >= 0)
+ FD_SET(ofd, &writey);
+ } else {
+ /* Don't read from pty if it's gone or it has closed. */
+ if (pty_master >= 0 && ofd >= 0)
+ FD_SET(pty_master, &ready);
+ }
+ if (select(nfds, &ready, &writey, NULL, top) < 0) {
+ if (errno != EINTR)
+ fatal("select");
+ continue;
+ }
+ if (max_data_rate) {
+ double dt;
+ int nbt;
+ struct timeval now;
+
+ (void) gettimeofday(&now, NULL);
+ dt = (now.tv_sec - levelt.tv_sec
+ + (now.tv_usec - levelt.tv_usec) / 1e6);
+ nbt = (int)(dt * max_data_rate);
+ ilevel = (nbt < 0 || nbt > ilevel)? 0: ilevel - nbt;
+ olevel = (nbt < 0 || nbt > olevel)? 0: olevel - nbt;
+ levelt = now;
+ } else
+ ilevel = olevel = 0;
+ if (FD_ISSET(ifd, &ready)) {
+ ibufp = inpacket_buf;
+ nibuf = read(ifd, ibufp, sizeof(inpacket_buf));
+ if (nibuf < 0 && errno == EIO)
+ nibuf = 0;
+ if (nibuf < 0 || pty_master == -1) {
+ if (errno != EINTR && errno != EAGAIN) {
+ error("Error reading standard input: %m");
+ break;
+ }
+ nibuf = 0;
+ } else if (nibuf == 0) {
+ /* end of file from stdin */
+ (void) close(pty_master);
+ pty_master = -1;
+ (void) close(ifd);
+ ifd = -1;
+ if (recordf)
+ if (!record_write(recordf, RECMARK_ENDRECV, NULL, 0,
+ &lasttime))
+ recordf = NULL;
+ } else {
+ FD_SET(pty_master, &writey);
+ if (recordf)
+ if (!record_write(recordf, RECMARK_STARTRECV, ibufp, nibuf,
+ &lasttime))
+ recordf = NULL;
+ }
+ }
+ if (ofd >= 0 && pty_master >= 0 && FD_ISSET(pty_master, &ready)) {
+ obufp = outpacket_buf;
+ nobuf = read(pty_master, obufp, sizeof(outpacket_buf));
+ if (nobuf < 0 && errno == EIO)
+ nobuf = 0;
+ if (nobuf < 0 || ofd == -1) {
+ if (!(errno == EINTR || errno == EAGAIN)) {
+ error("Error reading pseudo-tty master: %m");
+ break;
+ }
+ nobuf = 0;
+ } else if (nobuf == 0) {
+ /* end of file from the pty - slave side has closed */
+ nibuf = 0;
+ (void) close(ofd);
+ ofd = -1;
+ if (recordf)
+ if (!record_write(recordf, RECMARK_ENDSEND, NULL, 0,
+ &lasttime))
+ recordf = NULL;
+ } else {
+ FD_SET(ofd, &writey);
+ if (recordf)
+ if (!record_write(recordf, RECMARK_STARTSEND, obufp, nobuf,
+ &lasttime))
+ recordf = NULL;
+ }
+ }
+ if (ofd == -1)
+ nobuf = 0;
+ else if (FD_ISSET(ofd, &writey)) {
+ n = nobuf;
+ if (olevel + n > max_level)
+ n = max_level - olevel;
+ n = write(ofd, obufp, n);
+ if (n < 0) {
+ if (errno == EIO) {
+ (void) close(ofd);
+ ofd = -1;
+ nobuf = 0;
+ } else if (errno != EAGAIN && errno != EINTR) {
+ error("Error writing standard output: %m");
+ break;
+ }
+ } else {
+ obufp += n;
+ nobuf -= n;
+ olevel += n;
+ }
+ }
+ if (pty_master == -1)
+ nibuf = 0;
+ else if (FD_ISSET(pty_master, &writey)) {
+ n = nibuf;
+ if (ilevel + n > max_level)
+ n = max_level - ilevel;
+ n = write(pty_master, ibufp, n);
+ if (n < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ continue;
+ if (errno != EIO) {
+ error("Error writing pseudo-tty master: %m");
+ break;
+ }
+ (void) close(pty_master);
+ pty_master = -1;
+ nibuf = 0;
+ } else {
+ ibufp += n;
+ nibuf -= n;
+ ilevel += n;
+ }
+ }
+ }
+ exit(0);
+}
+
+static int
+record_write(f, code, buf, nb, tp)
+ FILE *f;
+ int code;
+ u_char *buf;
+ int nb;
+ struct timeval *tp;
+{
+ struct timeval now;
+ int diff;
+
+ (void) gettimeofday(&now, NULL);
+ now.tv_usec /= 100000; /* actually 1/10 s, not usec now */
+ diff = (now.tv_sec - tp->tv_sec) * 10 + (now.tv_usec - tp->tv_usec);
+ if (diff > 0) {
+ if (diff > 255) {
+ (void) putc(RECMARK_TIMEDELTA32, f);
+ (void) putc(diff >> 24, f);
+ (void) putc(diff >> 16, f);
+ (void) putc(diff >> 8, f);
+ (void) putc(diff, f);
+ } else {
+ (void) putc(RECMARK_TIMEDELTA8, f);
+ (void) putc(diff, f);
+ }
+ *tp = now;
+ }
+ (void) putc(code, f);
+ if (buf != NULL) {
+ (void) putc(nb >> 8, f);
+ (void) putc(nb, f);
+ (void) fwrite(buf, nb, 1, f);
+ }
+ (void) fflush(f);
+ if (ferror(f)) {
+ error("Error writing record file: %m");
+ return (0);
+ }
+ return (1);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppd/md4.c b/usr/src/cmd/cmd-inet/usr.bin/pppd/md4.c
new file mode 100644
index 0000000000..fc09a02cad
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppd/md4.c
@@ -0,0 +1,288 @@
+/*
+** ********************************************************************
+** md4.c -- Implementation of MD4 Message Digest Algorithm **
+** Updated: 2/16/90 by Ronald L. Rivest **
+** (C) 1990 RSA Data Security, Inc. **
+** ********************************************************************
+ *
+ * Copyright (c) 2000-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+/*
+** To use MD4:
+** -- Include md4.h in your program
+** -- Declare an MDstruct MD to hold the state of the digest
+** computation.
+** -- Initialize MD using MDbegin(&MD)
+** -- For each full block (64 bytes) X you wish to process, call
+** MD4Update(&MD,X,512)
+** (512 is the number of bits in a full block.)
+** -- For the last block (less than 64 bytes) you wish to process,
+** MD4Update(&MD,X,n)
+** where n is the number of bits in the partial block. A partial
+** block terminates the computation, so every MD computation
+** should terminate by processing a partial block, even if it
+** has n = 0.
+** -- The message digest is available in MD.buffer[0] ...
+** MD.buffer[3]. (Least-significant byte of each word
+** should be output first.)
+** -- You can print out the digest using MDprint(&MD)
+*/
+
+/* Implementation notes:
+** This implementation assumes that ints are 32-bit quantities.
+*/
+
+#define TRUE 1
+#define FALSE 0
+
+/* Compile-time includes
+*/
+#include <stdio.h>
+#include "md4.h"
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+#define RCSID "$Id: $"
+
+#if !defined(lint) && !defined(_lint)
+static const char rcsid[] = RCSID;
+#endif
+
+/* Compile-time declarations of MD4 "magic constants".
+*/
+#define I0 0x67452301 /* Initial values for MD buffer */
+#define I1 0xefcdab89
+#define I2 0x98badcfe
+#define I3 0x10325476
+#define C2 013240474631 /* round 2 constant = sqrt(2) in octal */
+#define C3 015666365641 /* round 3 constant = sqrt(3) in octal */
+/* C2 and C3 are from Knuth, The Art of Programming, Volume 2
+** (Seminumerical Algorithms), Second Edition (1981), Addison-Wesley.
+** Table 2, page 660.
+*/
+
+#define fs1 3 /* round 1 shift amounts */
+#define fs2 7
+#define fs3 11
+#define fs4 19
+#define gs1 3 /* round 2 shift amounts */
+#define gs2 5
+#define gs3 9
+#define gs4 13
+#define hs1 3 /* round 3 shift amounts */
+#define hs2 9
+#define hs3 11
+#define hs4 15
+
+/* Compile-time macro declarations for MD4.
+** Note: The "rot" operator uses the variable "tmp".
+** It assumes tmp is declared as unsigned int, so that the >>
+** operator will shift in zeros rather than extending the sign bit.
+*/
+#define f(X,Y,Z) ((X&Y) | ((~X)&Z))
+#define g(X,Y,Z) ((X&Y) | (X&Z) | (Y&Z))
+#define h(X,Y,Z) (X^Y^Z)
+#define rot(X,S) (tmp=X,(tmp<<S) | (tmp>>(32-S)))
+#define ff(A,B,C,D,i,s) A = rot((A + f(B,C,D) + X[i]),s)
+#define gg(A,B,C,D,i,s) A = rot((A + g(B,C,D) + X[i] + C2),s)
+#define hh(A,B,C,D,i,s) A = rot((A + h(B,C,D) + X[i] + C3),s)
+
+/* MD4Init(MDp)
+** Initialize message digest buffer MDp.
+** This is a user-callable routine.
+*/
+void
+MD4Init(MDp)
+MD4_CTX *MDp;
+{
+ int i;
+ MDp->buffer[0] = I0;
+ MDp->buffer[1] = I1;
+ MDp->buffer[2] = I2;
+ MDp->buffer[3] = I3;
+ for (i=0;i<8;i++) MDp->count[i] = 0;
+ MDp->done = 0;
+}
+
+/* MDblock(MDp,X)
+** Update message digest buffer MDp->buffer using 16-word data block X.
+** Assumes all 16 words of X are full of data.
+** Does not update MDp->count.
+** This routine is not user-callable.
+*/
+static void
+MDblock(MD4_CTX *MDp, const unsigned char *Xb)
+{
+ register unsigned int tmp, A, B, C, D;
+ unsigned int X[16];
+ int i;
+
+ for (i = 0; i < 16; ++i) {
+ X[i] = Xb[0] + (Xb[1] << 8) + (Xb[2] << 16) + (Xb[3] << 24);
+ Xb += 4;
+ }
+
+ A = MDp->buffer[0];
+ B = MDp->buffer[1];
+ C = MDp->buffer[2];
+ D = MDp->buffer[3];
+ /* Update the message digest buffer */
+ ff(A , B , C , D , 0 , fs1); /* Round 1 */
+ ff(D , A , B , C , 1 , fs2);
+ ff(C , D , A , B , 2 , fs3);
+ ff(B , C , D , A , 3 , fs4);
+ ff(A , B , C , D , 4 , fs1);
+ ff(D , A , B , C , 5 , fs2);
+ ff(C , D , A , B , 6 , fs3);
+ ff(B , C , D , A , 7 , fs4);
+ ff(A , B , C , D , 8 , fs1);
+ ff(D , A , B , C , 9 , fs2);
+ ff(C , D , A , B , 10 , fs3);
+ ff(B , C , D , A , 11 , fs4);
+ ff(A , B , C , D , 12 , fs1);
+ ff(D , A , B , C , 13 , fs2);
+ ff(C , D , A , B , 14 , fs3);
+ ff(B , C , D , A , 15 , fs4);
+ gg(A , B , C , D , 0 , gs1); /* Round 2 */
+ gg(D , A , B , C , 4 , gs2);
+ gg(C , D , A , B , 8 , gs3);
+ gg(B , C , D , A , 12 , gs4);
+ gg(A , B , C , D , 1 , gs1);
+ gg(D , A , B , C , 5 , gs2);
+ gg(C , D , A , B , 9 , gs3);
+ gg(B , C , D , A , 13 , gs4);
+ gg(A , B , C , D , 2 , gs1);
+ gg(D , A , B , C , 6 , gs2);
+ gg(C , D , A , B , 10 , gs3);
+ gg(B , C , D , A , 14 , gs4);
+ gg(A , B , C , D , 3 , gs1);
+ gg(D , A , B , C , 7 , gs2);
+ gg(C , D , A , B , 11 , gs3);
+ gg(B , C , D , A , 15 , gs4);
+ hh(A , B , C , D , 0 , hs1); /* Round 3 */
+ hh(D , A , B , C , 8 , hs2);
+ hh(C , D , A , B , 4 , hs3);
+ hh(B , C , D , A , 12 , hs4);
+ hh(A , B , C , D , 2 , hs1);
+ hh(D , A , B , C , 10 , hs2);
+ hh(C , D , A , B , 6 , hs3);
+ hh(B , C , D , A , 14 , hs4);
+ hh(A , B , C , D , 1 , hs1);
+ hh(D , A , B , C , 9 , hs2);
+ hh(C , D , A , B , 5 , hs3);
+ hh(B , C , D , A , 13 , hs4);
+ hh(A , B , C , D , 3 , hs1);
+ hh(D , A , B , C , 11 , hs2);
+ hh(C , D , A , B , 7 , hs3);
+ hh(B , C , D , A , 15 , hs4);
+ MDp->buffer[0] += A;
+ MDp->buffer[1] += B;
+ MDp->buffer[2] += C;
+ MDp->buffer[3] += D;
+}
+
+/* MD4Update(MDp,X,count)
+** Input: X -- a pointer to an array of unsigned characters.
+** count -- the number of bits of X to use.
+** (if not a multiple of 8, uses high bits of last byte.)
+** Update MDp using the number of bits of X given by count.
+** This is the basic input routine for an MD4 user.
+** The routine completes the MD computation when count < 512, so
+** every MD computation should end with one call to MD4Update with a
+** count less than 512. A call with count 0 will be ignored if the
+** MD has already been terminated (done != 0), so an extra call with
+** count 0 can be given as a "courtesy close" to force termination
+** if desired.
+*/
+void
+MD4Update(MDp,X,count)
+MD4_CTX *MDp;
+const unsigned char *X;
+unsigned int count;
+{
+ unsigned int i, tmp, bit, byte, mask;
+ unsigned char XX[64];
+ unsigned char *p;
+
+ /* return with no error if this is a courtesy close with count
+ ** zero and MDp->done is true.
+ */
+ if (count == 0 && MDp->done) return;
+ /* check to see if MD is already done and report error */
+ if (MDp->done)
+ { (void) printf("\nError: MD4Update MD already done."); return; }
+
+ /* Add count to MDp->count */
+ tmp = count;
+ p = MDp->count;
+ while (tmp)
+ { tmp += *p;
+ *p++ = tmp;
+ tmp = tmp >> 8;
+ }
+
+ /* Process data */
+ if (count == 512)
+ { /* Full block of data to handle */
+ MDblock(MDp,X);
+ }
+ else if (count > 512) /* Check for count too large */
+ {
+ (void) printf("\nError: MD4Update called with illegal count value %d.",
+ count);
+ return;
+ }
+ else /* partial block -- must be last block so finish up */
+ {
+ /* Find out how many bytes and residual bits there are */
+ byte = count >> 3;
+ bit = count & 7;
+ /* Copy X into XX since we need to modify it */
+ for (i=0;i<=byte;i++) XX[i] = X[i];
+ for (i=byte+1;i<64;i++) XX[i] = 0;
+ /* Add padding '1' bit and low-order zeros in last byte */
+ mask = 1 << (7 - bit);
+ XX[byte] = (XX[byte] | mask) & ~( mask - 1);
+ /* If room for bit count, finish up with this block */
+ if (byte <= 55)
+ {
+ for (i=0;i<8;i++) XX[56+i] = MDp->count[i];
+ MDblock(MDp,XX);
+ }
+ else /* need to do two blocks to finish up */
+ {
+ MDblock(MDp,XX);
+ for (i=0;i<56;i++) XX[i] = 0;
+ for (i=0;i<8;i++) XX[56+i] = MDp->count[i];
+ MDblock(MDp,XX);
+ }
+ /* Set flag saying we're done with MD computation */
+ MDp->done = 1;
+ }
+}
+
+/*
+** Finish up MD4 computation and return message digest.
+*/
+void
+MD4Final(buf, MD)
+unsigned char *buf;
+MD4_CTX *MD;
+{
+ int i, j;
+ unsigned int w;
+
+ MD4Update(MD, NULL, 0);
+ for (i = 0; i < 4; ++i) {
+ w = MD->buffer[i];
+ for (j = 0; j < 4; ++j) {
+ *buf++ = w;
+ w >>= 8;
+ }
+ }
+}
+
+/*
+** End of md4.c
+****************************(cut)***********************************/
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppd/md4.h b/usr/src/cmd/cmd-inet/usr.bin/pppd/md4.h
new file mode 100644
index 0000000000..3f7ef15c65
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppd/md4.h
@@ -0,0 +1,61 @@
+/*
+** ********************************************************************
+** md4.h -- Header file for implementation of **
+** MD4 Message Digest Algorithm **
+** Updated: 2/13/90 by Ronald L. Rivest **
+** (C) 1990 RSA Data Security, Inc. **
+** ********************************************************************
+ *
+ * Copyright (c) 2000 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifndef __P
+# if defined(__STDC__) || defined(__GNUC__)
+# define __P(x) x
+# else
+# define __P(x) ()
+# endif
+#endif
+
+
+/* MDstruct is the data structure for a message digest computation.
+*/
+typedef struct {
+ unsigned int buffer[4]; /* Holds 4-word result of MD computation */
+ unsigned char count[8]; /* Number of bits processed so far */
+ unsigned int done; /* Nonzero means MD computation finished */
+} MD4_CTX;
+
+/* MD4Init(MD4_CTX *)
+** Initialize the MD4_CTX prepatory to doing a message digest
+** computation.
+*/
+extern void MD4Init __P((MD4_CTX *MD));
+
+/* MD4Update(MD,X,count)
+** Input: X -- a pointer to an array of unsigned characters.
+** count -- the number of bits of X to use (an unsigned int).
+** Updates MD using the first "count" bits of X.
+** The array pointed to by X is not modified.
+** If count is not a multiple of 8, MD4Update uses high bits of
+** last byte.
+** This is the basic input routine for a user.
+** The routine terminates the MD computation when count < 512, so
+** every MD computation should end with one call to MD4Update with a
+** count less than 512. Zero is OK for a count.
+*/
+extern void MD4Update __P((MD4_CTX *MD, const unsigned char *X,
+ unsigned int count));
+
+/* MD4Final(buf, MD)
+** Returns message digest from MD and terminates the message
+** digest computation.
+*/
+extern void MD4Final __P((unsigned char *, MD4_CTX *));
+
+/*
+** End of md4.h
+****************************(cut)***********************************/
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppd/mschap_test.c b/usr/src/cmd/cmd-inet/usr.bin/pppd/mschap_test.c
new file mode 100644
index 0000000000..588a482199
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppd/mschap_test.c
@@ -0,0 +1,91 @@
+/*
+ * Test MS-CHAPv1 library code.
+ *
+ * Copyright (c) 2000 by Sun Microsystems, Inc.
+ * All rights reserved.
+ *
+ * Originally from the README.MSCHAP80 file written by:
+ * Eric Rosenquist rosenqui@strataware.com
+ * (updated by Paul Mackerras)
+ * (updated by Al Longyear)
+ * (updated by Farrell Woods)
+ */
+
+#include <stdio.h>
+
+#include "pppd.h"
+#include "chap.h"
+#include "chap_ms.h"
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+static void
+show_response(chap_state *cstate, const char *str)
+{
+ int i;
+
+ printf("%s -- %d bytes:", str, cstate->resp_length);
+
+ for (i = 0; i < cstate->resp_length; i++) {
+ if (i % 8 == 0)
+ putchar('\n');
+ printf("%02X ", (unsigned int)cstate->response[i]);
+ }
+
+ putchar('\n');
+}
+
+int main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ u_char challenge[8];
+ int challengeInt[sizeof(challenge)];
+ chap_state cstate;
+ int i;
+
+ if (argc != 3) {
+ fprintf(stderr, "Usage: %s <16-hexchar challenge> <password>\n",
+ argv[0]); exit(1);
+ }
+
+ sscanf(argv[1], "%2x%2x%2x%2x%2x%2x%2x%2x",
+ challengeInt + 0, challengeInt + 1, challengeInt + 2,
+ challengeInt + 3, challengeInt + 4, challengeInt + 5,
+ challengeInt + 6, challengeInt + 7);
+
+ for (i = 0; i < sizeof(challenge); i++)
+ challenge[i] = (u_char)challengeInt[i];
+
+ BZERO(&cstate, sizeof(cstate));
+ ChapMS(&cstate, challenge, sizeof(challenge), argv[2], strlen(argv[2]));
+#ifdef MSLANMAN
+ show_response(&cstate, "MS-CHAPv1 with LAN Manager");
+#else
+ show_response(&cstate, "MS-CHAPv1");
+#endif
+
+ cstate.chal_len = sizeof(challenge);
+ BCOPY(challenge, cstate.challenge, cstate.chal_len);
+ if (!ChapMSValidate(&cstate, cstate.response, cstate.resp_length,
+ argv[2], strlen(argv[2])))
+ printf("Cannot validate own MS-CHAPv1 response.\n");
+
+#ifdef MSLANMAN
+ cstate.response[MS_CHAP_RESPONSE_LEN-1] = '\0';
+ if (!ChapMSValidate(&cstate, cstate.response, cstate.resp_length,
+ argv[2], strlen(argv[2])))
+ printf("Cannot validate own LAN Manager response.\n");
+#endif
+
+#ifdef CHAPMSV2
+ cstate.resp_name = "joe user";
+ ChapMSv2(&cstate, cstate.challenge, 16, argv[2], strlen(argv[2]));
+ show_response(&cstate, "MS-CHAPv2");
+ if (!ChapMSv2Validate(&cstate, cstate.resp_name, cstate.response,
+ cstate.resp_length, argv[2], strlen(argv[2])))
+ printf("Cannot validate own MS-CHAPv2 response.\n");
+#endif
+
+ exit(0);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppd/multilink.c b/usr/src/cmd/cmd-inet/usr.bin/pppd/multilink.c
new file mode 100644
index 0000000000..518a2688aa
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppd/multilink.c
@@ -0,0 +1,413 @@
+/*
+ * multilink.c - support routines for multilink.
+ *
+ * Copyright (c) 2000-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ *
+ * Copyright (c) 2000 Paul Mackerras.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms. The name of the author may not be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+#define RCSID "$Id: $"
+
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <netdb.h>
+#include <errno.h>
+#include <signal.h>
+#include <netinet/in.h>
+
+#include "pppd.h"
+#include "fsm.h"
+#include "lcp.h"
+
+#ifdef HAVE_MULTILINK
+#include "tdb.h"
+#endif
+#if !defined(lint) && !defined(_lint)
+static const char rcsid[] = RCSID;
+#endif
+
+#define set_ip_epdisc(ep, addr) ( \
+ ep->length = 4, \
+ ep->value[0] = addr >> 24, \
+ ep->value[1] = addr >> 16, \
+ ep->value[2] = addr >> 8, \
+ ep->value[3] = addr \
+)
+
+#ifdef HAVE_MULTILINK
+bool endpoint_specified; /* user gave explicit endpoint discriminator */
+char *bundle_id; /* identifier for our bundle */
+
+static int get_default_epdisc __P((struct epdisc *));
+static int parse_num __P((char *str, const char *key, int *valp));
+static int owns_unit __P((TDB_DATA pid, int unit));
+
+#define process_exists(n) (kill(0, (n)) == 0 || errno != ESRCH)
+
+void
+mp_check_options()
+{
+ lcp_options *wo = &lcp_wantoptions[0];
+ lcp_options *ao = &lcp_allowoptions[0];
+
+ if (!multilink)
+ return;
+ /* if we're doing multilink, we have to negotiate MRRU */
+ if (!wo->neg_mrru) {
+ /* mrru not specified, default to mru */
+ wo->mrru = wo->mru;
+ wo->neg_mrru = 1;
+ }
+ ao->mrru = ao->mru;
+ ao->neg_mrru = 1;
+
+ if (!wo->neg_endpoint && !noendpoint) {
+ /* get a default endpoint value */
+ wo->neg_endpoint = get_default_epdisc(&wo->endpoint);
+ if (wo->neg_endpoint)
+ dbglog("using default endpoint %s",
+ epdisc_to_str(&wo->endpoint));
+ }
+}
+
+/*
+ * Make a new bundle or join us to an existing bundle
+ * if we are doing multilink.
+ */
+int
+mp_join_bundle()
+{
+ lcp_options *go = &lcp_gotoptions[0];
+ lcp_options *ho = &lcp_hisoptions[0];
+ int unit, pppd_pid;
+ int l;
+ char *p;
+ TDB_DATA key, pid, rec;
+
+ if (!go->neg_mrru || !ho->neg_mrru) {
+ /* not doing multilink */
+ if (go->neg_mrru)
+ notice("oops, multilink negotiated only for receive");
+ multilink = 0;
+ if (demand) {
+ /* already have a bundle */
+ cfg_bundle(0, 0, 0, 0);
+ return 0;
+ }
+ make_new_bundle(0, 0, 0, 0);
+ set_ifunit(1);
+ return 0;
+ }
+
+ /*
+ * Find the appropriate bundle or join a new one.
+ * First we make up a name for the bundle.
+ * The length estimate is worst-case assuming every
+ * character has to be quoted.
+ *
+ * Note - RFC 1990 requires that an unnegotiated endpoint
+ * discriminator value be equivalent to negotiating with class
+ * zero. Do not test ho->neg_endpoint here.
+ */
+ l = 4 * strlen(peer_authname) + 10;
+ l += 3 * ho->endpoint.length + 8;
+ if (bundle_name)
+ l += 3 * strlen(bundle_name) + 2;
+ bundle_id = malloc(l);
+ if (bundle_id == NULL)
+ novm("bundle identifier");
+
+ p = bundle_id;
+ p += slprintf(p, l-1, "BUNDLE=\"%q\"", peer_authname);
+ *p++ = '/';
+ p += slprintf(p, bundle_id+l-p, "%s", epdisc_to_str(&ho->endpoint));
+ if (bundle_name)
+ p += slprintf(p, bundle_id+l-p, "/%v", bundle_name);
+ dbglog("bundle_id = %s", bundle_id+7);
+
+ /*
+ * For demand mode, we only need to configure the bundle
+ * and attach the link.
+ */
+ if (demand) {
+ cfg_bundle(go->mrru, ho->mrru, go->neg_ssnhf, ho->neg_ssnhf);
+ script_setenv("BUNDLE", bundle_id + 7, 1);
+ return 0;
+ }
+
+ /*
+ * Check if the bundle ID is already in the database.
+ */
+ unit = -1;
+ tdb_writelock(pppdb);
+ key.dptr = bundle_id;
+ key.dsize = p - bundle_id;
+ pid = tdb_fetch(pppdb, key);
+ if (pid.dptr != NULL) {
+ /* bundle ID exists, see if the pppd record exists */
+ rec = tdb_fetch(pppdb, pid);
+ if (rec.dptr != NULL) {
+ /* it does, parse the interface number */
+ parse_num(rec.dptr, "IFNAME=ppp", &unit);
+ /* check the pid value */
+ if (!parse_num(rec.dptr, "PPPD_PID=", &pppd_pid)
+ || !process_exists(pppd_pid)
+ || !owns_unit(pid, unit))
+ unit = -1;
+ free(rec.dptr);
+ }
+ free(pid.dptr);
+ }
+
+ if (unit >= 0) {
+ /* attach to existing unit */
+ if (bundle_attach(unit)) {
+ set_ifunit(0);
+ script_setenv("BUNDLE", bundle_id + 7, 0);
+ tdb_writeunlock(pppdb);
+ info("Link attached to %s", ifname);
+ return 1;
+ }
+ /* attach failed because bundle doesn't exist */
+ }
+
+ /* we have to make a new bundle */
+ make_new_bundle(go->mrru, ho->mrru, go->neg_ssnhf, ho->neg_ssnhf);
+ set_ifunit(1);
+ script_setenv("BUNDLE", bundle_id + 7, 1);
+ tdb_writeunlock(pppdb);
+ info("New bundle %s created", ifname);
+ return 0;
+}
+
+static int
+parse_num(str, key, valp)
+ char *str;
+ const char *key;
+ int *valp;
+{
+ char *p, *endp;
+ int i;
+
+ p = strstr(str, key);
+ if (p != 0) {
+ p += strlen(key);
+ i = strtol(p, &endp, 10);
+ if (endp != p && (*endp == 0 || *endp == ';')) {
+ *valp = i;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Check whether the pppd identified by `key' still owns ppp unit `unit'.
+ */
+static int
+owns_unit(key, unit)
+ TDB_DATA key;
+ int unit;
+{
+ char ifkey[32];
+ TDB_DATA kd, vd;
+ int ret = 0;
+
+ (void) slprintf(ifkey, sizeof(ifkey), "IFNAME=ppp%d", unit);
+ kd.dptr = ifkey;
+ kd.dsize = strlen(ifkey);
+ vd = tdb_fetch(pppdb, kd);
+ if (vd.dptr != NULL) {
+ ret = vd.dsize == key.dsize
+ && memcmp(vd.dptr, key.dptr, vd.dsize) == 0;
+ free(vd.dptr);
+ }
+ return ret;
+}
+
+static int
+get_default_epdisc(ep)
+ struct epdisc *ep;
+{
+ struct hostent *hp;
+ u_int32_t addr;
+
+ if (get_first_hwaddr(ep->value, sizeof(ep->value))) {
+ ep->class = EPD_MAC;
+ ep->length = 6;
+ return 1;
+ }
+
+ /* see if our hostname corresponds to a reasonable IP address */
+ hp = gethostbyname(hostname);
+ if (hp != NULL) {
+ addr = *(u_int32_t *)hp->h_addr;
+ if (!bad_ip_adrs(addr)) {
+ addr = ntohl(addr);
+ if (!LOCAL_IP_ADDR(addr)) {
+ ep->class = EPD_IP;
+ set_ip_epdisc(ep, addr);
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+#endif /* HAVE_MULTILINK */
+
+/*
+ * epdisc_to_str - make a printable string from an endpoint discriminator.
+ */
+
+static char *endp_class_names[] = {
+ "null", "local", "IP", "MAC", "magic", "phone"
+};
+
+char *
+epdisc_to_str(ep)
+ struct epdisc *ep;
+{
+ static char str[MAX_ENDP_LEN*3+8];
+ u_char *p = ep->value;
+ int i, mask = 0;
+ char *q, c, c2;
+
+ if (ep->class == EPD_NULL && ep->length == 0)
+ return "null";
+ if (ep->class == EPD_IP && ep->length == 4) {
+ u_int32_t addr;
+
+ GETLONG(addr, p);
+ (void) slprintf(str, sizeof(str), "IP:%I", htonl(addr));
+ return str;
+ }
+
+ c = ':';
+ c2 = '.';
+ if (ep->class == EPD_MAC && ep->length == 6)
+ c2 = ':';
+ else if (ep->class == EPD_MAGIC && (ep->length % 4) == 0)
+ mask = 3;
+ q = str;
+ if (ep->class <= EPD_PHONENUM)
+ q += slprintf(q, sizeof(str)-1, "%s",
+ endp_class_names[ep->class]);
+ else
+ q += slprintf(q, sizeof(str)-1, "%d", ep->class);
+ c = ':';
+ for (i = 0; i < ep->length && i < MAX_ENDP_LEN; ++i) {
+ if ((i & mask) == 0) {
+ *q++ = c;
+ c = c2;
+ }
+ q += slprintf(q, str + sizeof(str) - q, "%.2x", ep->value[i]);
+ }
+ return str;
+}
+
+static int hexc_val(int c)
+{
+ if (c >= 'a')
+ return c - 'a' + 10;
+ if (c >= 'A')
+ return c - 'A' + 10;
+ return c - '0';
+}
+
+int
+str_to_epdisc(ep, str)
+ struct epdisc *ep;
+ char *str;
+{
+ int i, l;
+ char *p, *endp;
+
+ for (i = EPD_NULL; i <= EPD_PHONENUM; ++i) {
+ int sl = strlen(endp_class_names[i]);
+ if (strncasecmp(str, endp_class_names[i], sl) == 0) {
+ str += sl;
+ break;
+ }
+ }
+ if (i > EPD_PHONENUM) {
+ /* not a class name, try a decimal class number */
+ i = strtol(str, &endp, 10);
+ if (endp == str) {
+ option_error("cannot parse endpoint class in \"%s\"",
+ str);
+ return 0; /* can't parse class number */
+ }
+ str = endp;
+ }
+ ep->class = i;
+ if (*str == 0) {
+ ep->length = 0;
+ return 1;
+ }
+ if (*str != ':' && *str != '.') {
+ option_error("invalid class/value separator '%c'", *str);
+ return 0;
+ }
+ ++str;
+
+ if (i == EPD_IP) {
+ u_int32_t addr;
+ i = parse_dotted_ip(str, &addr);
+ if (i == 0 || str[i] != 0)
+ return 0;
+ set_ip_epdisc(ep, addr);
+ dbglog("str_to_epdisc -> %s", epdisc_to_str(ep));
+ return 1;
+ }
+ if (i == EPD_MAC &&
+ get_if_hwaddr(ep->value, sizeof(ep->value), str) >= 0) {
+ ep->length = 6;
+ dbglog("str_to_epdisc -> %s", epdisc_to_str(ep));
+ return 1;
+ }
+
+ p = str;
+ for (l = 0; l < MAX_ENDP_LEN; ++l) {
+ if (*str == 0)
+ break;
+ if (p <= str)
+ for (p = str; isxdigit(*p); ++p)
+ ;
+ i = p - str;
+ if (i == 0) {
+ option_error("no valid hex digits in \"%s\"", str);
+ return 0;
+ }
+ ep->value[l] = hexc_val(*str++);
+ if ((i & 1) == 0)
+ ep->value[l] = (ep->value[l] << 4) + hexc_val(*str++);
+ if (*str == ':' || *str == '.')
+ ++str;
+ }
+ if (*str != 0) {
+ option_error("too many bytes in value; max is %d",
+ MAX_ENDP_LEN);
+ return 0;
+ }
+ if (ep->class == EPD_MAC && l != 6) {
+ option_error("bad endpoint; MAC address must have 6 bytes");
+ return 0;
+ }
+ ep->length = l;
+ dbglog("str_to_epdisc -> %s", epdisc_to_str(ep));
+ return 1;
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppd/options.c b/usr/src/cmd/cmd-inet/usr.bin/pppd/options.c
new file mode 100644
index 0000000000..c62dbbec60
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppd/options.c
@@ -0,0 +1,2006 @@
+/*
+ * options.c - handles option processing for PPP.
+ *
+ * Copyright (c) 2000-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies.
+ *
+ * SUN MAKES NO REPRESENTATION OR WARRANTIES ABOUT THE SUITABILITY OF
+ * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+ * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
+ * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
+ * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+#define RCSID "$Id: options.c,v 1.74 2000/04/15 01:27:13 masputra Exp $"
+
+#include <ctype.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <string.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#ifdef PLUGIN
+#include <dlfcn.h>
+#endif /* PLUGIN */
+#ifdef PPP_FILTER
+#include <pcap.h>
+#include <pcap-int.h> /* XXX: To get struct pcap */
+#endif /* PPP_FILTER */
+
+#include "pppd.h"
+#include "pathnames.h"
+#include "patchlevel.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "ipcp.h"
+
+#if defined(ultrix) || defined(NeXT)
+char *strdup __P((char *));
+#endif
+
+#if !defined(lint) && !defined(_lint)
+static const char rcsid[] = RCSID;
+#endif
+
+/*
+ * Option variables and default values.
+ */
+#ifdef PPP_FILTER
+int dflag = 0; /* Tell libpcap we want debugging */
+#endif /* PPP_FILTER */
+int debug = 0; /* Debug flag */
+int kdebugflag = 0; /* Tell kernel to print debug messages */
+int default_device = 1; /* Using /dev/tty or equivalent */
+char devnam[MAXPATHLEN]; /* Device name */
+int crtscts = 0; /* Use hardware flow control */
+bool modem = 1; /* Use modem control lines */
+int inspeed = 0; /* Input/Output speed requested */
+u_int32_t netmask = 0; /* IP netmask to set on interface */
+bool lockflag = 0; /* Create lock file to lock the serial dev */
+bool nodetach = 0; /* Don't detach from controlling tty */
+bool updetach = 0; /* Detach once link is up */
+char *initializer = NULL; /* Script to initialize physical link */
+char *connect_script = NULL; /* Script to establish physical link */
+char *disconnect_script = NULL; /* Script to disestablish physical link */
+char *welcomer = NULL; /* Script to run after phys link estab. */
+char *ptycommand = NULL; /* Command to run on other side of pty */
+int maxconnect = 0; /* Maximum connect time */
+char user[MAXNAMELEN]; /* Username for PAP */
+char passwd[MAXSECRETLEN]; /* Password for PAP */
+bool persist = 0; /* Reopen link after it goes down */
+char our_name[MAXNAMELEN]; /* Our name for authentication purposes */
+bool demand = 0; /* do dial-on-demand */
+char *ipparam = NULL; /* Extra parameter for ip up/down scripts */
+int idle_time_limit = 0; /* Disconnect if idle for this many seconds */
+int holdoff = 30; /* # seconds to pause before reconnecting */
+bool holdoff_specified; /* true if a holdoff value has been given */
+bool notty = 0; /* Stdin/out is not a tty */
+char *pty_socket = NULL; /* Socket to connect to pty */
+char *record_file = NULL; /* File to record chars sent/received */
+int using_pty = 0;
+bool sync_serial = 0; /* Device is synchronous serial device */
+int log_to_fd = 1; /* send log messages to this fd too */
+int maxfail = 10; /* max # of unsuccessful connection attempts */
+char linkname[MAXPATHLEN]; /* logical name for link */
+bool tune_kernel; /* may alter kernel settings */
+int connect_delay = 1000; /* wait this many ms after connect script */
+int max_data_rate; /* max bytes/sec through charshunt */
+int req_unit = -1; /* requested interface unit */
+bool multilink = 0; /* Enable multilink operation */
+char *bundle_name = NULL; /* bundle name for multilink */
+bool direct_tty = 0; /* use standard input directly; not a tty */
+
+/* Maximum depth of include files; prevents looping. */
+#define MAXFILENESTING 10
+
+struct option_info initializer_info;
+struct option_info connect_script_info;
+struct option_info disconnect_script_info;
+struct option_info welcomer_info;
+struct option_info devnam_info;
+struct option_info ptycommand_info;
+struct option_info ipsrc_info;
+struct option_info ipdst_info;
+struct option_info speed_info;
+
+#ifdef PPP_FILTER
+struct bpf_program pass_filter;/* Filter program for packets to pass */
+struct bpf_program active_filter; /* Filter program for link-active pkts */
+pcap_t pc; /* Fake struct pcap so we can compile expr */
+#endif /* PPP_FILTER */
+
+char *current_option; /* the name of the option being parsed */
+bool privileged_option; /* set iff the current option came from root */
+char *option_source = NULL; /* string saying where the option came from */
+int option_line = 0; /* line number in file */
+bool log_to_file; /* log_to_fd is a file opened by us */
+bool log_to_specific_fd; /* log_to_fd was specified by user option */
+
+/*
+ * Prototypes.
+ */
+static int setdevname __P((char *));
+static int setipaddr __P((char *));
+static int setspeed __P((char *));
+static int noopt __P((char **, option_t *));
+static int setdomain __P((char **, option_t *));
+static int setnetmask __P((char **, option_t *));
+static int setxonxoff __P((char **, option_t *));
+static int readfile __P((char **, option_t *));
+static int callfile __P((char **, option_t *));
+static int showversion __P((char **, option_t *));
+static int showhelp __P((char **, option_t *));
+static int showalloptions __P((char **, option_t *));
+static void usage __P((void));
+static int setlogfile __P((char **, option_t *));
+#ifdef PLUGIN
+static int loadplugin __P((char **, option_t *));
+#endif
+#ifdef PPP_FILTER
+static int setpassfilter __P((char **, option_t *));
+static int setactivefilter __P((char **, option_t *));
+#endif /* PPP_FILTER */
+static option_t *find_option __P((char *name));
+static int process_option __P((option_t *opt, char **argv, int sline));
+static int n_arguments __P((option_t *opt));
+static int number_option __P((char *str, u_int32_t *valp, int base));
+static u_int32_t opt_hash __P((const void *key));
+static int opt_compare __P((const void *p1, const void *p2));
+
+typedef struct _opt_t {
+ option_t *p;
+} opt_t;
+
+typedef struct _hashentry_t {
+ struct _hashentry_t *next;
+ opt_t opt;
+} hashentry_t;
+
+/*
+ * A prime number describing the size of hash table.
+ */
+#define OPTHASH_TBLSIZE 101
+
+/*
+ * Chained hash table containing pointers to available options.
+ */
+static hashentry_t *hash_tbl[OPTHASH_TBLSIZE] = { NULL };
+
+/*
+ * Total number of entries in the hash table.
+ */
+int hash_tblcnt = 0;
+
+/*
+ * Valid arguments.
+ */
+option_t general_options[] = {
+ { "debug", o_int, &debug,
+ "Increase debugging level", OPT_INC|OPT_NOARG|1 },
+ { "-d", o_int, &debug,
+ "Increase debugging level", OPT_INC|OPT_NOARG|1 },
+ { "kdebug", o_int, &kdebugflag,
+ "Set kernel driver debug level" },
+ { "nodetach", o_bool, &nodetach,
+ "Don't detach from controlling tty", 1 },
+ { "-detach", o_bool, &nodetach,
+ "Don't detach from controlling tty", 1 },
+ { "updetach", o_bool, &updetach,
+ "Detach from controlling tty once link is up", 1 },
+ { "holdoff", o_int, &holdoff,
+ "Set time in seconds before retrying connection" },
+ { "idle", o_int, &idle_time_limit,
+ "Set time in seconds before disconnecting idle link" },
+ { "lock", o_bool, &lockflag,
+ "Lock serial device with UUCP-style lock file", 1 },
+ { "-all", o_special_noarg, (void *)noopt,
+ "Don't request/allow any LCP or IPCP options (useless)" },
+ { "init", o_string, &initializer,
+ "A program to initialize the device",
+ OPT_A2INFO | OPT_PRIVFIX, &initializer_info },
+ { "connect", o_string, &connect_script,
+ "A program to set up a connection",
+ OPT_A2INFO | OPT_PRIVFIX, &connect_script_info },
+ { "disconnect", o_string, &disconnect_script,
+ "Program to disconnect serial device",
+ OPT_A2INFO | OPT_PRIVFIX, &disconnect_script_info },
+ { "welcome", o_string, &welcomer,
+ "Script to welcome client",
+ OPT_A2INFO | OPT_PRIVFIX, &welcomer_info },
+ { "pty", o_string, &ptycommand,
+ "Script to run on pseudo-tty master side",
+ OPT_A2INFO | OPT_PRIVFIX | OPT_DEVNAM, &ptycommand_info },
+ { "notty", o_bool, &notty,
+ "Input/output is not a tty", OPT_DEVNAM | 1 },
+ { "directtty", o_bool, &direct_tty,
+ "Use standard input as tty without checking", OPT_DEVNAM | 1 },
+ { "socket", o_string, &pty_socket,
+ "Send and receive over socket, arg is host:port", OPT_DEVNAM },
+ { "record", o_string, &record_file,
+ "Record characters sent/received to file" },
+ { "maxconnect", o_int, &maxconnect,
+ "Set connection time limit", OPT_LLIMIT|OPT_NOINCR|OPT_ZEROINF },
+ { "crtscts", o_int, &crtscts,
+ "Set hardware (RTS/CTS) flow control", OPT_NOARG|OPT_VAL(1) },
+ { "nocrtscts", o_int, &crtscts,
+ "Disable hardware flow control", OPT_NOARG|OPT_VAL(-1) },
+ { "-crtscts", o_int, &crtscts,
+ "Disable hardware flow control", OPT_NOARG|OPT_VAL(-1) },
+ { "cdtrcts", o_int, &crtscts,
+ "Set alternate hardware (DTR/CTS) flow control", OPT_NOARG|OPT_VAL(2) },
+ { "nocdtrcts", o_int, &crtscts,
+ "Disable hardware flow control", OPT_NOARG|OPT_VAL(-1) },
+ { "xonxoff", o_special_noarg, (void *)setxonxoff,
+ "Set software (XON/XOFF) flow control" },
+ { "domain", o_special, (void *)setdomain,
+ "Add given domain name to hostname" },
+ { "netmask", o_special, (void *)setnetmask,
+ "set netmask" },
+ { "modem", o_bool, &modem,
+ "Use modem control lines", 1 },
+ { "local", o_bool, &modem,
+ "Don't use modem control lines" },
+ { "file", o_special, (void *)readfile,
+ "Take options from a file", OPT_PREPASS },
+ { "call", o_special, (void *)callfile,
+ "Take options from a privileged file", OPT_PREPASS },
+ { "persist", o_bool, &persist,
+ "Keep on reopening connection after close", 1 },
+ { "nopersist", o_bool, &persist,
+ "Turn off persist option" },
+ { "demand", o_bool, &demand,
+ "Dial on demand", OPT_INITONLY | 1, &persist },
+ { "--version", o_special_noarg, (void *)showversion,
+ "Show version number" },
+ { "--help", o_special_noarg, (void *)showhelp,
+ "Show brief listing of options" },
+ { "-h", o_special_noarg, (void *)showhelp,
+ "Show brief listing of options" },
+ { "options", o_special_noarg, (void *)showalloptions,
+ "Show full listing of options" },
+ { "sync", o_bool, &sync_serial,
+ "Use synchronous HDLC serial encoding", 1 },
+ { "logfd", o_int, &log_to_fd,
+ "Send log messages to this file descriptor",
+ 0, &log_to_specific_fd },
+ { "logfile", o_special, (void *)setlogfile,
+ "Append log messages to this file" },
+ { "nolog", o_int, &log_to_fd,
+ "Don't send log messages to any file",
+ OPT_NOARG | OPT_VAL(-1) },
+ { "nologfd", o_int, &log_to_fd,
+ "Don't send log messages to any file descriptor",
+ OPT_NOARG | OPT_VAL(-1) },
+ { "linkname", o_string, linkname,
+ "Set logical name for link",
+ OPT_PRIV|OPT_STATIC, NULL, MAXPATHLEN },
+ { "maxfail", o_int, &maxfail,
+ "Number of unsuccessful connection attempts to allow" },
+ { "ktune", o_bool, &tune_kernel,
+ "Alter kernel settings as necessary", 1 },
+ { "noktune", o_bool, &tune_kernel,
+ "Don't alter kernel settings", 0 },
+ { "connect-delay", o_int, &connect_delay,
+ "Maximum wait time (msec) after connect script finishes" },
+ { "datarate", o_int, &max_data_rate,
+ "Max data rate in bytes/sec for pty, notty, or record" },
+ { "unit", o_int, &req_unit,
+ "PPP interface unit number to use if possible", OPT_LLIMIT, 0, 0 },
+#ifdef HAVE_MULTILINK
+ { "multilink", o_bool, &multilink,
+ "Enable multilink operation", 1 },
+ { "nomultilink", o_bool, &multilink,
+ "Disable multilink operation", 0 },
+ { "mp", o_bool, &multilink,
+ "Enable multilink operation", 1 },
+ { "nomp", o_bool, &multilink,
+ "Disable multilink operation", 0 },
+ { "bundle", o_string, &bundle_name,
+ "Bundle name for multilink" },
+#endif /* HAVE_MULTILINK */
+#ifdef PLUGIN
+ { "plugin", o_special, (void *)loadplugin,
+ "Load a plug-in module into pppd", OPT_PRIV },
+#endif /* PLUGIN */
+#ifdef PPP_FILTER
+ { "pdebug", o_int, &dflag,
+ "libpcap debugging" },
+ { "pass-filter", o_special, setpassfilter,
+ "set filter for packets to pass" },
+ { "active-filter", o_special, setactivefilter,
+ "set filter for active pkts" },
+#endif /* PPP_FILTER */
+ { NULL }
+};
+
+/*
+ * This string gets printed out when "options" is given on the command
+ * line. Following this string, all of the available options and
+ * their descriptions are printed out as well. Certain options which
+ * are not available as part of the option_t structure are placed in
+ * the "dummy" option structure.
+ */
+static const char pre_allopt_string[] = "\
+pppd version %s.%d%s\n\
+Usage: %s [ options ], where options are:\n\n\
+";
+
+/* Do not call add_options() on this structure */
+static option_t dummy_options[] = {
+ { "<device>", o_special_noarg, NULL,
+ "Communicate over the named device" },
+ { "<speed>", o_special_noarg, NULL,
+ "Set the baud rate to <speed>" },
+ { "[<loc>]:[<rem>]", o_special_noarg, NULL,
+ "Set the local and/or remote interface IP addresses" },
+ { NULL }
+};
+
+static const char post_allopt_string[] = "\
+\n\
+Notes:\
+\t<n>\tinteger type argument\n\
+\t<s>\tstring type argument\n\
+\t<r>\tspecial type argument\n\
+\t(!)\tprivileged option available only when pppd is executed by root\n\
+\t\tor when found in the privileged option files (/etc/ppp/options,\n\
+\t\t/etc/ppp/options.ttyname, /etc/ppp/peers/name, or following\n\
+\t\t\"--\" in /etc/ppp/pap-secrets or /etc/ppp/chap-secrets).\n\
+\t(#)\tdisabled option\n\
+\n\
+Please see the pppd man page for details.\n";
+
+/*
+ * parse_args - parse a string of arguments from the command line. If prepass
+ * is true, we are scanning for the device name and only processing a few
+ * options, so error messages are suppressed. Returns 1 upon successful
+ * processing of options, and 0 otherwise.
+ */
+int
+parse_args(argc, argv)
+ int argc;
+ char **argv;
+{
+ char *arg;
+ option_t *opt;
+ int ret;
+
+ privileged_option = privileged;
+ option_source = "command line";
+ option_line = 0;
+ while (argc > 0) {
+ arg = *argv++;
+ --argc;
+
+ /*
+ * First check to see if it's a known option name. If so, parse the
+ * argument(s) and set the option.
+ */
+ opt = find_option(arg);
+ if (opt != NULL) {
+ int n = n_arguments(opt);
+ if (argc < n) {
+ option_error("too few parameters for option '%s'", arg);
+ return (0);
+ }
+ current_option = arg;
+ if (!process_option(opt, argv, 0))
+ return (0);
+ argc -= n;
+ argv += n;
+ continue;
+ }
+
+ /*
+ * Maybe a tty name, speed or IP address ?
+ */
+ if (((ret = setdevname(arg)) == 0) &&
+ ((ret = setspeed(arg)) == 0) &&
+ ((ret = setipaddr(arg)) == 0) && !prepass) {
+ option_error("unrecognized option '%s'", arg);
+ usage();
+ return (0);
+ }
+ if (ret < 0) /* error */
+ return (0);
+ }
+ return (1);
+}
+
+/*
+ * options_from_file - read a string of options from a file, and
+ * interpret them. Returns 1 upon successful processing of options,
+ * and 0 otherwise.
+ */
+int
+options_from_file
+#ifdef __STDC__
+ (char *filename, bool must_exist, bool check_prot, bool priv)
+#else
+ (filename, must_exist, check_prot, priv)
+ char *filename;
+ bool must_exist;
+ bool check_prot;
+ bool priv;
+#endif
+{
+ FILE *f;
+ int i, newline, ret, err;
+ option_t *opt;
+ bool oldpriv;
+ int oldline, sline;
+ char *oldsource;
+ char *argv[MAXARGS];
+ char args[MAXARGS][MAXWORDLEN];
+ char cmd[MAXWORDLEN];
+ static bool firsterr = 1;
+ static int nestlevel = 0;
+
+ if (nestlevel >= MAXFILENESTING) {
+ option_error("file nesting too deep");
+ return (0);
+ }
+ if (check_prot)
+ (void) seteuid(getuid());
+ errno = 0;
+ f = fopen(filename, "r");
+ err = errno;
+ if (check_prot)
+ (void) seteuid(0);
+ if (f == NULL) {
+ if (!must_exist && err == ENOENT)
+ return (1);
+ errno = err;
+ option_error("Can't open options file %s: %m", filename);
+ return (0);
+ }
+
+ nestlevel++;
+ oldpriv = privileged_option;
+ privileged_option = priv;
+ oldsource = option_source;
+ /*
+ * strdup() is used here because the pointer might refer to the
+ * caller's automatic (stack) storage, and the option_info array
+ * records the source file name.
+ */
+ option_source = strdup(filename);
+ oldline = option_line;
+ option_line = 1;
+ if (option_source == NULL)
+ option_source = "file";
+ ret = 0;
+ while (getword(f, cmd, &newline, filename)) {
+ sline = option_line;
+ /*
+ * First see if it's a command.
+ */
+ opt = find_option(cmd);
+ if (opt != NULL) {
+ int n = n_arguments(opt);
+ for (i = 0; i < n; ++i) {
+ if (!getword(f, args[i], &newline, filename)) {
+ option_error("too few parameters for option '%s'", cmd);
+ goto err;
+ }
+ argv[i] = args[i];
+ }
+ current_option = cmd;
+ if ((opt->flags & OPT_DEVEQUIV) && devnam_fixed) {
+ option_error("the '%s' option may not be used here", cmd);
+ goto err;
+ }
+ if (!process_option(opt, argv, sline))
+ goto err;
+ continue;
+ }
+
+ /*
+ * Maybe a tty name, speed or IP address ?
+ */
+ if (((i = setdevname(cmd)) == 0) &&
+ ((i = setspeed(cmd)) == 0) &&
+ ((i = setipaddr(cmd)) == 0)) {
+ option_error("unrecognized option '%s'", cmd);
+ goto err;
+ }
+ if (i < 0) /* error */
+ goto err;
+ }
+ ret = 1;
+
+err:
+ (void) fclose(f);
+ /* We assume here that we abort all processing on the first error. */
+ if (firsterr)
+ firsterr = 0;
+ else if (!prepass && !ret)
+ option_error("error in included file");
+ /*
+ * Cannot free option_source because it might be referenced in one
+ * or more option_info structures now.
+ */
+ privileged_option = oldpriv;
+ option_source = oldsource;
+ option_line = oldline;
+ nestlevel--;
+ return (ret);
+}
+
+/*
+ * options_from_user - see if the user has a ~/.ppprc file, and if so,
+ * interpret options from it. Returns 1 upon successful processing of
+ * options, and 0 otherwise.
+ */
+int
+options_from_user()
+{
+ char *user, *path, *file;
+ int ret;
+ struct passwd *pw;
+ size_t pl;
+
+ pw = getpwuid(getuid());
+ if (pw == NULL || (user = pw->pw_dir) == NULL || user[0] == '\0')
+ return (1);
+ file = _PATH_USEROPT;
+ pl = strlen(user) + strlen(file) + 2;
+ path = malloc(pl);
+ if (path == NULL)
+ novm("init file name");
+ (void) slprintf(path, pl, "%s/%s", user, file);
+ ret = options_from_file(path, 0, 1, privileged);
+ free(path);
+ return (ret);
+}
+
+/*
+ * options_for_tty - see if an options file exists for the serial device, and
+ * if so, interpret options from it. Returns 1 upon successful processing of
+ * options, and 0 otherwise.
+ */
+int
+options_for_tty()
+{
+ char *dev, *path, *p;
+ int ret;
+ size_t pl;
+
+ dev = devnam;
+ if (strncmp(dev, "/dev/", 5) == 0)
+ dev += 5;
+ if (dev[0] == '\0' || strcmp(dev, "tty") == 0)
+ return (1); /* don't look for /etc/ppp/options.tty */
+ pl = strlen(_PATH_TTYOPT) + strlen(dev) + 1;
+ path = malloc(pl);
+ if (path == NULL)
+ novm("tty init file name");
+ (void) slprintf(path, pl, "%s%s", _PATH_TTYOPT, dev);
+ /* Turn slashes into dots, for Solaris case (e.g. /dev/term/a) */
+ for (p = path + strlen(_PATH_TTYOPT); *p != '\0'; ++p)
+ if (*p == '/')
+ *p = '.';
+ ret = options_from_file(path, 0, 0, 1);
+ free(path);
+ return (ret);
+}
+
+/*
+ * options_from_list - process a string of options in a wordlist. Returns 1
+ * upon successful processing of options, and 0 otherwise.
+ */
+int
+options_from_list
+#ifdef __STDC__
+ (struct wordlist *w, bool priv)
+#else
+ (w, priv)
+ struct wordlist *w;
+ bool priv;
+#endif
+{
+ char *argv[MAXARGS];
+ option_t *opt;
+ int i, ret = 0;
+
+ privileged_option = priv;
+
+ /* Caller is expected to set option_source and option_line. */
+
+ while (w != NULL) {
+ /*
+ * First see if it's a command.
+ */
+ opt = find_option(w->word);
+ if (opt != NULL) {
+ int n = n_arguments(opt);
+ struct wordlist *w0 = w;
+ for (i = 0; i < n; ++i) {
+ w = w->next;
+ if (w == NULL) {
+ option_error("too few parameters for option '%s'",
+ w0->word);
+ goto err;
+ }
+ argv[i] = w->word;
+ }
+ current_option = w0->word;
+ if (!process_option(opt, argv, option_line))
+ goto err;
+ continue;
+ }
+
+ /*
+ * Options from the {p,ch}ap-secrets files can't change the device
+ * name nor the speed. Therefore, calls to setdevname() and
+ * setspeed() were removed.
+ */
+ if ((i = setipaddr(w->word)) == 0) {
+ option_error("unrecognized option '%s'", w->word);
+ goto err;
+ }
+ if (i < 0) /* error */
+ goto err;
+ }
+ ret = 1;
+
+err:
+ return (ret);
+}
+
+/*
+ * find_option - scan the option lists for the various protocols looking for an
+ * entry with the given name. Returns a pointer to the matching option_t
+ * structure upon successful processing of options, and NULL otherwise.
+ */
+static option_t *
+find_option(name)
+ char *name;
+{
+ hashentry_t *bucket;
+
+ bucket = hash_tbl[opt_hash(name)];
+ for (; bucket != NULL; bucket = bucket->next) {
+ if (bucket->opt.p->name != NULL) {
+ if ((strcmp(bucket->opt.p->name, name) == 0) &&
+ !(bucket->opt.p->flags & OPT_DISABLE)) {
+ return (bucket->opt.p);
+ }
+ }
+ }
+ return (NULL);
+}
+
+/*
+ * process_option - process one new-style option (something other than a
+ * port name, bit rate, or IP address). Returns 1 upon successful
+ * processing of options, and 0 otherwise.
+ */
+static int
+process_option(opt, argv, sline)
+ option_t *opt;
+ char **argv;
+ int sline;
+{
+ u_int32_t v;
+ int iv, a;
+ char *sv;
+ int (*parser) __P((char **, option_t *));
+
+ if ((opt->flags & OPT_PREPASS) == 0 && prepass)
+ return (1);
+ if ((opt->flags & OPT_INITONLY) && phase != PHASE_INITIALIZE) {
+ option_error("it's too late to use the '%s' option", opt->name);
+ return (0);
+ }
+ if ((opt->flags & OPT_PRIV) && !privileged_option) {
+ option_error("using the '%s' option requires root privilege",
+ opt->name);
+ return (0);
+ }
+ if ((opt->flags & OPT_ENABLE) && !privileged_option &&
+ *(bool *)(opt->addr2) == 0) {
+ option_error("'%s' option is disabled", opt->name);
+ return (0);
+ }
+ if ((opt->flags & OPT_PRIVFIX) && !privileged_option) {
+ struct option_info *ip = (struct option_info *) opt->addr2;
+ if ((ip != NULL) && ip->priv) {
+ option_error("'%s' option cannot be overridden", opt->name);
+ return (0);
+ }
+ }
+
+ switch (opt->type) {
+ case o_bool:
+ v = opt->flags & OPT_VALUE;
+ *(bool *)(opt->addr) = (v != 0);
+ if ((opt->addr2 != NULL) && (opt->flags & OPT_A2COPY))
+ *(bool *)(opt->addr2) = (v != 0);
+ break;
+
+ case o_int:
+ iv = 0;
+ if ((opt->flags & OPT_NOARG) == 0) {
+ if (!int_option(*argv, &iv))
+ return (0);
+ if ((((opt->flags & OPT_LLIMIT) && (iv < opt->lower_limit)) ||
+ ((opt->flags & OPT_ULIMIT) && (iv > opt->upper_limit))) &&
+ !((opt->flags & OPT_ZEROOK) && (iv == 0))) {
+ char *zok = (opt->flags & OPT_ZEROOK) ? " zero or" : "";
+ switch (opt->flags & OPT_LIMITS) {
+ case OPT_LLIMIT:
+ option_error("%s value must be%s >= %d",
+ opt->name, zok, opt->lower_limit);
+ break;
+ case OPT_ULIMIT:
+ option_error("%s value must be%s <= %d",
+ opt->name, zok, opt->upper_limit);
+ break;
+ case OPT_LIMITS:
+ option_error("%s value must be%s between %d and %d",
+ opt->name, zok, opt->lower_limit, opt->upper_limit);
+ break;
+ }
+ return (0);
+ }
+ }
+ a = opt->flags & OPT_VALUE;
+ if (a >= 128)
+ a -= 256; /* sign extend */
+ iv += a;
+ if (opt->flags & OPT_INC)
+ iv += *(int *)(opt->addr);
+ if ((opt->flags & OPT_NOINCR) && !privileged_option) {
+ int oldv = *(int *)(opt->addr);
+
+ if ((opt->flags & OPT_ZEROINF) && (iv == 0)) {
+ if (oldv > 0) {
+ option_error("%s value cannot be set to infinity; limited to %d",
+ opt->name, oldv);
+ return (0);
+ }
+ } else if (iv > oldv) {
+ option_error("%s value cannot be increased beyond %d",
+ opt->name, oldv);
+ return (0);
+ }
+ }
+ *(int *)(opt->addr) = iv;
+ if ((opt->addr2 != NULL) && (opt->flags & OPT_A2COPY))
+ *(int *)(opt->addr2) = iv;
+ break;
+
+ case o_uint32:
+ if (opt->flags & OPT_NOARG) {
+ v = opt->flags & OPT_VALUE;
+ } else if (!number_option(*argv, &v, 16))
+ return (0);
+ if (opt->flags & OPT_OR)
+ v |= *(u_int32_t *)(opt->addr);
+ *(u_int32_t *)(opt->addr) = v;
+ if ((opt->addr2 != NULL) && (opt->flags & OPT_A2COPY))
+ *(u_int32_t *)(opt->addr2) = v;
+ break;
+
+ case o_string:
+ if (opt->flags & OPT_STATIC) {
+ (void) strlcpy((char *)(opt->addr), *argv, opt->upper_limit);
+ if ((opt->addr2 != NULL) && (opt->flags & OPT_A2COPY)) {
+ (void) strlcpy((char *)(opt->addr2), *argv, opt->upper_limit);
+ }
+ } else {
+ sv = strdup(*argv);
+ if (sv == NULL)
+ novm("option argument");
+ *(char **)(opt->addr) = sv;
+ if (opt->addr2 != NULL && (opt->flags & OPT_A2COPY))
+ *(char **)(opt->addr2) = sv;
+ }
+ break;
+
+ case o_special_noarg:
+ case o_special:
+ parser = (int (*) __P((char **, option_t *))) opt->addr;
+ if (!(*parser)(argv, opt))
+ return (0);
+ break;
+ }
+
+ if (opt->addr2 != NULL) {
+ if (opt->flags & OPT_A2INFO) {
+ struct option_info *ip = (struct option_info *) opt->addr2;
+ ip->priv = privileged_option;
+ ip->source = option_source;
+ ip->line = sline;
+ } else if ((opt->flags & (OPT_A2COPY|OPT_ENABLE)) == 0)
+ *(bool *)(opt->addr2) = 1;
+ }
+
+ return (1);
+}
+
+/*
+ * n_arguments - tell how many arguments an option takes. Returns 1 upon
+ * successful processing of options, and 0 otherwise.
+ */
+static int
+n_arguments(opt)
+ option_t *opt;
+{
+ return ((opt->type == o_bool || opt->type == o_special_noarg ||
+ (opt->flags & OPT_NOARG)) ? 0 : 1);
+}
+
+/*
+ * opt_hash - a hash function that works quite well for strings. Returns
+ * the hash key of the supplied string.
+ */
+static u_int32_t
+opt_hash(key)
+ const void *key;
+{
+ register const char *ptr;
+ register u_int32_t val;
+
+ val = 0;
+ ptr = key;
+ while (*ptr != '\0') {
+ int tmp;
+ val = (val << 4) + (*ptr);
+ tmp = val & 0xf0000000;
+ if (tmp) {
+ val ^= (tmp >> 24);
+ val ^= tmp;
+ }
+ ptr++;
+ }
+ return (val % OPTHASH_TBLSIZE);
+}
+
+/*
+ * add_options - add a list of options to the chained hash table.
+ * Also detect duplicate options, and if found, disable the older
+ * definition and log it as an error.
+ */
+void
+add_options(opt)
+ option_t *opt;
+{
+ register option_t *sopt;
+ register hashentry_t *bucket;
+ register u_int32_t loc;
+ hashentry_t *he;
+
+ /* fill hash-table */
+ for (sopt = opt; sopt->name != NULL; ++sopt, hash_tblcnt++) {
+
+ /* first, allocate a hash entry */
+ he = (hashentry_t *)malloc(sizeof(*he));
+ if (he == NULL) {
+ novm("option hash table entry");
+ }
+ he->opt.p = sopt;
+ he->next = NULL;
+
+ /*
+ * fill the chained hash table and take care of any collisions or
+ * duplicate items.
+ */
+ loc = opt_hash(sopt->name);
+ bucket = hash_tbl[loc];
+ if (bucket != NULL) {
+ for (;;) {
+ if (!(bucket->opt.p->flags & OPT_DISABLE) &&
+ strcmp(sopt->name, bucket->opt.p->name) == 0) {
+ info("option '%s' redefined; old definition disabled",
+ sopt->name);
+ bucket->opt.p->flags |= OPT_DISABLE;
+ }
+ if (bucket->next == NULL)
+ break;
+ bucket = bucket->next;
+ }
+ bucket->next = he;
+ } else {
+ hash_tbl[loc] = he;
+ }
+ }
+}
+
+/*
+ * remove_option - disable an option. Returns the option_t structure
+ * of the disabled option, or NULL if the option name is invalid or if
+ * the option has already been disabled.
+ */
+option_t *
+remove_option(name)
+ char *name;
+{
+ option_t *opt;
+
+ if ((opt = find_option(name)) != NULL) {
+ opt->flags |= OPT_DISABLE;
+ }
+ return (opt);
+}
+
+/*
+ * opt_compare - a compare function supplied to the quicksort routine.
+ * Returns an integer less than, equal to, or greater than zero to indicate
+ * if the first argument is considered less than, equal to, or greater
+ * than the second argument.
+ */
+static int
+opt_compare(p1, p2)
+ const void *p1;
+ const void *p2;
+{
+ opt_t *o1 = (opt_t *)p1;
+ opt_t *o2 = (opt_t *)p2;
+
+ return (strcmp(o1->p->name, o2->p->name));
+}
+
+/*ARGSUSED*/
+static int
+showalloptions(argv, topt)
+ char **argv;
+ option_t *topt;
+{
+#define MAXOPTSTRLEN 257
+#define PRINTOPTIONS() { \
+ (void) slprintf(opt_str, sizeof(opt_str), "%s", opt->name); \
+ if ((opt->type == o_int || opt->type == o_uint32) && \
+ !(opt->flags & OPT_NOARG)) { \
+ (void) strlcat(opt_str, " <n>", sizeof(opt_str)); \
+ } else if (opt->type == o_string) { \
+ (void) strlcat(opt_str, " <s>", sizeof(opt_str)); \
+ } else if (opt->type == o_special) { \
+ (void) strlcat(opt_str, " <r>", sizeof(opt_str)); \
+ } \
+ if (opt->flags & OPT_PRIV) { \
+ (void) strlcat(opt_str, " (!)", sizeof(opt_str)); \
+ } else if (opt->flags & OPT_DISABLE) { \
+ (void) strlcat(opt_str, " (#)", sizeof(opt_str)); \
+ } \
+ (void) printf("%-26s%s\n", opt_str, opt->description); \
+}
+
+ char opt_str[MAXOPTSTRLEN];
+ option_t *opt;
+ hashentry_t *bucket;
+ int i, sofar;
+ opt_t *sopt;
+
+ if (phase != PHASE_INITIALIZE) {
+ return (0);
+ }
+ (void) printf(pre_allopt_string, VERSION, PATCHLEVEL, IMPLEMENTATION,
+ progname);
+ for (opt = dummy_options; opt->name != NULL; ++opt) {
+ PRINTOPTIONS();
+ }
+
+ sopt = malloc(sizeof(*sopt) * hash_tblcnt);
+ if (sopt == NULL) {
+ novm("sorted option table");
+ }
+
+ sofar = 0;
+ for (i = 0; i < OPTHASH_TBLSIZE; i++) {
+ for (bucket = hash_tbl[i]; bucket != NULL; bucket = bucket->next) {
+ if (sofar >= hash_tblcnt) {
+ fatal("options hash table corrupted; size mismatch");
+ }
+ sopt[sofar++].p = bucket->opt.p;
+ }
+ }
+
+ qsort((void *)sopt, sofar, sizeof(sopt[0]), opt_compare);
+ for (i = 0; i < sofar; i++) {
+ opt = sopt[i].p;
+ PRINTOPTIONS();
+ }
+
+ (void) printf(post_allopt_string);
+ (void) free(sopt);
+
+#undef MAXOPTSTRLEN
+#undef PRINTOPTIONS
+ return (0);
+}
+
+/*
+ * usage - print out a message telling how to use the program.
+ * This string gets printed out when either "--help" or an invalid option
+ * is specified.
+ */
+static void
+usage()
+{
+ static const char usage_string[] = "\
+pppd version %s.%d%s\n\
+Usage: %s [ options ], where options are:\n\
+\t<device>\tCommunicate over the named device\n\
+\t<speed>\t\tSet the baud rate to <speed>\n\
+\t<loc>:<rem>\tSet the local and/or remote interface IP\n\
+\t\t\taddresses. Either one may be omitted.\n\
+\tnoauth\t\tDon't require authentication from peer\n\
+\tconnect <p>\tInvoke shell command <p> to set up the serial line\n\
+\tdefaultroute\tAdd default route through interface\n\
+Use \"%s options\" or \"man pppd\" for more options.\n\
+";
+
+ if (phase == PHASE_INITIALIZE)
+ (void) fprintf(stderr, usage_string, VERSION, PATCHLEVEL,
+ IMPLEMENTATION, progname, progname);
+}
+
+/*
+ * showhelp - print out usage message and exit program upon success, or
+ * return 0 otherwise.
+ */
+/*ARGSUSED*/
+static int
+showhelp(argv, opt)
+ char **argv;
+ option_t *opt;
+{
+ if (phase == PHASE_INITIALIZE) {
+ usage();
+ exit(0);
+ }
+ return (0);
+}
+
+/*
+ * showversion - print out the version number and exit program upon success,
+ * or return 0 otherwise.
+ */
+/*ARGSUSED*/
+static int
+showversion(argv, opt)
+ char **argv;
+ option_t *opt;
+{
+ if (phase == PHASE_INITIALIZE) {
+ (void) fprintf(stderr, "pppd version %s.%d%s\n", VERSION, PATCHLEVEL,
+ IMPLEMENTATION);
+ exit(0);
+ }
+ return (0);
+}
+
+/*
+ * option_error - print a message about an error in an option. The message is
+ * logged, and also sent to stderr if phase == PHASE_INITIALIZE.
+ */
+void
+option_error __V((char *fmt, ...))
+{
+ va_list args;
+ char buf[256];
+ int i, err;
+
+#if defined(__STDC__)
+ va_start(args, fmt);
+#else
+ char *fmt;
+ va_start(args);
+ fmt = va_arg(args, char *);
+#endif
+ if (prepass) {
+ va_end(args);
+ return;
+ }
+ err = errno;
+ if (option_source == NULL) {
+ i = 0;
+ } else if (option_line <= 0) {
+ (void) strlcpy(buf, option_source, sizeof (buf));
+ i = strlen(buf);
+ } else {
+ i = slprintf(buf, sizeof(buf), "%s:%d", option_source, option_line);
+ }
+ if (i != 0) {
+ (void) strlcat(buf, ": ", sizeof (buf));
+ i += 2;
+ }
+ errno = err;
+ (void) vslprintf(buf + i, sizeof (buf) - i, fmt, args);
+ va_end(args);
+ if ((phase == PHASE_INITIALIZE) && !detached)
+ (void) fprintf(stderr, "%s: %s\n", progname, buf);
+ syslog(LOG_ERR, "%s", buf);
+}
+
+/*
+ * getword - read a word from a file. Words are delimited by white-space or by
+ * quotes (" or '). Quotes, white-space and \ may be escaped with \.
+ * \<newline> is ignored. Returns 1 upon successful processing of options,
+ * and 0 otherwise.
+ */
+int
+getword(f, word, newlinep, filename)
+ FILE *f;
+ char *word;
+ int *newlinep;
+ char *filename;
+{
+ int c, len, escape;
+ int quoted, comment;
+ int value, digit, got, n;
+
+#define isoctal(c) ((c) >= '0' && (c) < '8')
+
+ *newlinep = 0;
+ len = 0;
+ escape = 0;
+ comment = 0;
+
+ /*
+ * First skip white-space and comments.
+ */
+ for (;;) {
+ c = getc(f);
+ if (c == EOF)
+ break;
+
+ /*
+ * A newline means the end of a comment; backslash-newline
+ * is ignored. Note that we cannot have escape && comment.
+ */
+ if (c == '\n') {
+ option_line++;
+ if (!escape) {
+ *newlinep = 1;
+ comment = 0;
+ } else
+ escape = 0;
+ continue;
+ }
+
+ /*
+ * Ignore characters other than newline in a comment.
+ */
+ if (comment)
+ continue;
+
+ /*
+ * If this character is escaped, we have a word start.
+ */
+ if (escape)
+ break;
+
+ /*
+ * If this is the escape character, look at the next character.
+ */
+ if (c == '\\') {
+ escape = 1;
+ continue;
+ }
+
+ /*
+ * If this is the start of a comment, ignore the rest of the line.
+ */
+ if (c == '#') {
+ comment = 1;
+ continue;
+ }
+
+ /*
+ * A non-whitespace character is the start of a word.
+ */
+ if (!isspace(c))
+ break;
+ }
+
+ /*
+ * Save the delimiter for quoted strings.
+ */
+ if (!escape && (c == '"' || c == '\'')) {
+ quoted = c;
+ c = getc(f);
+ } else
+ quoted = 0;
+
+ /*
+ * Process characters until the end of the word.
+ */
+ while (c != EOF) {
+ if (escape) {
+ /*
+ * This character is escaped: backslash-newline is ignored,
+ * various other characters indicate particular values
+ * as for C backslash-escapes.
+ */
+ escape = 0;
+ if (c == '\n') {
+ c = getc(f);
+ continue;
+ }
+
+ got = 0;
+ switch (c) {
+ case 'a':
+ value = '\a';
+ break;
+ case 'b':
+ value = '\b';
+ break;
+ case 'f':
+ value = '\f';
+ break;
+ case 'n':
+ value = '\n';
+ break;
+ case 'r':
+ value = '\r';
+ break;
+ case 's':
+ value = ' ';
+ break;
+ case 't':
+ value = '\t';
+ break;
+
+ default:
+ if (isoctal(c)) {
+ /*
+ * \ddd octal sequence
+ */
+ value = 0;
+ for (n = 0; n < 3 && isoctal(c); ++n) {
+ value = (value << 3) + (c & 07);
+ c = getc(f);
+ }
+ got = 1;
+ break;
+ }
+
+ if (c == 'x') {
+ /*
+ * \x<hex_string> sequence
+ */
+ value = 0;
+ c = getc(f);
+ for (n = 0; n < 2 && isxdigit(c); ++n) {
+ digit = (islower(c) ? toupper(c) : c) - '0';
+ if (digit > 10 || digit < 0) /* allow non-ASCII */
+ digit += '0' + 10 - 'A';
+ value = (value << 4) + digit;
+ c = getc (f);
+ }
+ got = 1;
+ break;
+ }
+
+ /*
+ * Otherwise the character stands for itself.
+ */
+ value = c;
+ break;
+ }
+
+ /*
+ * Store the resulting character for the escape sequence.
+ */
+ if (len < MAXWORDLEN-1)
+ word[len] = value;
+ ++len;
+
+ if (!got)
+ c = getc(f);
+ continue;
+
+ }
+
+ /*
+ * Not escaped: see if we've reached the end of the word.
+ */
+ if (quoted) {
+ if (c == quoted)
+ break;
+ } else {
+ if (isspace(c) || c == '#') {
+ (void) ungetc (c, f);
+ break;
+ }
+ }
+
+ /*
+ * Backslash starts an escape sequence.
+ */
+ if (c == '\\') {
+ escape = 1;
+ c = getc(f);
+ continue;
+ }
+
+ /*
+ * An ordinary character: store it in the word and get another.
+ */
+ if (len < MAXWORDLEN-1)
+ word[len] = c;
+ ++len;
+
+ c = getc(f);
+ }
+
+ /*
+ * End of the word: check for errors.
+ */
+ if (c == EOF) {
+ if (ferror(f)) {
+ if (errno == 0)
+ errno = EIO;
+ option_error("Error reading %s: %m", filename);
+ die(1);
+ }
+ /*
+ * If len is zero, then we didn't find a word before the
+ * end of the file.
+ */
+ if (len == 0)
+ return (0);
+ }
+
+ /*
+ * Warn if the word was too long, and append a terminating null.
+ */
+ if (len >= MAXWORDLEN) {
+ option_error("warning: word in file %s too long (%.20s...)",
+ filename, word);
+ len = MAXWORDLEN - 1;
+ }
+ word[len] = '\0';
+
+ return (1);
+
+#undef isoctal
+
+}
+
+/*
+ * number_option - parse an unsigned numeric parameter for an option.
+ * Returns 1 upon successful processing of options, and 0 otherwise.
+ */
+static int
+number_option(str, valp, base)
+ char *str;
+ u_int32_t *valp;
+ int base;
+{
+ char *ptr;
+
+ *valp = strtoul(str, &ptr, base);
+ if (ptr == str || *ptr != '\0') {
+ option_error("invalid numeric parameter '%s' for '%s' option",
+ str, current_option);
+ return (0);
+ }
+ return (1);
+}
+
+/*
+ * save_source - store option source, line, and privilege into an
+ * option_info structure.
+ */
+void
+save_source(info)
+ struct option_info *info;
+{
+ info->priv = privileged_option;
+ info->source = option_source;
+ info->line = option_line;
+}
+
+/*
+ * set_source - set option source, line, and privilege from an
+ * option_info structure.
+ */
+void
+set_source(info)
+ struct option_info *info;
+{
+ privileged_option = info->priv;
+ option_source = info->source;
+ option_line = info->line;
+}
+
+/*
+ * name_source - return string containing option source and line. Can
+ * be used as part of an option_error call.
+ */
+const char *
+name_source(info)
+ struct option_info *info;
+{
+ static char buf[MAXPATHLEN];
+
+ if (info->source == NULL)
+ return "none";
+ if (info->line <= 0)
+ return info->source;
+ (void) slprintf(buf, sizeof (buf), "%s:%d", info->source, info->line);
+ return (const char *)buf;
+}
+
+/*
+ * int_option - like number_option, but valp is int *, the base is assumed to
+ * be 0, and *valp is not changed if there is an error. Returns 1 upon
+ * successful processing of options, and 0 otherwise.
+ */
+int
+int_option(str, valp)
+ char *str;
+ int *valp;
+{
+ u_int32_t v;
+
+ if (!number_option(str, &v, 0))
+ return (0);
+ *valp = (int) v;
+ return (1);
+}
+
+
+/*
+ * The following procedures parse options.
+ */
+
+/*
+ * readfile - take commands from a file.
+ */
+/*ARGSUSED*/
+static int
+readfile(argv, opt)
+ char **argv;
+ option_t *opt;
+{
+ return (options_from_file(*argv, 1, 1, privileged_option));
+}
+
+/*
+ * callfile - take commands from /etc/ppp/peers/<name>. Name may not contain
+ * /../, start with / or ../, or end in /. Returns 1 upon successful
+ * processing of options, and 0 otherwise.
+ */
+/*ARGSUSED*/
+static int
+callfile(argv, opt)
+ char **argv;
+ option_t *opt;
+{
+ char *fname, *arg, *p;
+ int l, ok;
+
+ arg = *argv;
+ ok = 1;
+ if (arg[0] == '/' || arg[0] == '\0')
+ ok = 0;
+ else {
+ for (p = arg; *p != '\0'; ) {
+ if (p[0] == '.' && p[1] == '.' && (p[2] == '/' || p[2] == '\0')) {
+ ok = 0;
+ break;
+ }
+ while (*p != '/' && *p != '\0')
+ ++p;
+ if (*p == '/')
+ ++p;
+ }
+ }
+ if (!ok) {
+ option_error("call option value may not contain .. or start with /");
+ return (0);
+ }
+
+ l = strlen(arg) + strlen(_PATH_PEERFILES) + 1;
+ if ((fname = (char *) malloc(l)) == NULL)
+ novm("call file name");
+ (void) slprintf(fname, l, "%s%s", _PATH_PEERFILES, arg);
+
+ ok = options_from_file(fname, 1, 1, 1);
+
+ free(fname);
+ return (ok);
+}
+
+#ifdef PPP_FILTER
+/*
+ * setpdebug - set libpcap debugging level. Returns 1 upon successful
+ * processing of options, and 0 otherwise.
+ */
+static int
+setpdebug(argv)
+ char **argv;
+{
+ return (int_option(*argv, &dflag));
+}
+
+/*
+ * setpassfilter - set the pass filter for packets. Returns 1 upon successful
+ * processing of options, and 0 otherwise.
+ */
+/*ARGSUSED*/
+static int
+setpassfilter(argv, opt)
+ char **argv;
+ option_t *opt;
+{
+ pc.linktype = DLT_PPP;
+ pc.snapshot = PPP_HDRLEN;
+
+ if (pcap_compile(&pc, &pass_filter, *argv, 1, netmask) == 0)
+ return (1);
+ option_error("error in pass-filter expression: %s\n", pcap_geterr(&pc));
+ return (0);
+}
+
+/*
+ * setactivefilter - set the active filter for packets. Returns 1 upon
+ * successful processing of options, and 0 otherwise.
+ */
+/*ARGSUSED*/
+static int
+setactivefilter(argv, opt)
+ char **argv;
+ option_t *opt;
+{
+ pc.linktype = DLT_PPP;
+ pc.snapshot = PPP_HDRLEN;
+
+ if (pcap_compile(&pc, &active_filter, *argv, 1, netmask) == 0)
+ return (1);
+ option_error("error in active-filter expression: %s\n", pcap_geterr(&pc));
+ return (0);
+}
+#endif /* PPP_FILTER */
+
+/*
+ * noopt - disable all options. Returns 1 upon successful processing of
+ * options, and 0 otherwise.
+ */
+/*ARGSUSED*/
+static int
+noopt(argv, opt)
+ char **argv;
+ option_t *opt;
+{
+ BZERO((char *) &lcp_wantoptions[0], sizeof (struct lcp_options));
+ BZERO((char *) &lcp_allowoptions[0], sizeof (struct lcp_options));
+ BZERO((char *) &ipcp_wantoptions[0], sizeof (struct ipcp_options));
+ BZERO((char *) &ipcp_allowoptions[0], sizeof (struct ipcp_options));
+
+ return (1);
+}
+
+/*
+ * setdomain - set domain name to append to hostname. Returns 1 upon
+ * successful processing of options, and 0 otherwise.
+ */
+/*ARGSUSED*/
+static int
+setdomain(argv, opt)
+ char **argv;
+ option_t *opt;
+{
+ if (!privileged_option) {
+ option_error("using the domain option requires root privilege");
+ return (0);
+ }
+ (void) gethostname(hostname, MAXHOSTNAMELEN+1);
+ if (**argv != '\0') {
+ if (**argv != '.')
+ (void) strncat(hostname, ".", MAXHOSTNAMELEN - strlen(hostname));
+ (void) strncat(hostname, *argv, MAXHOSTNAMELEN - strlen(hostname));
+ }
+ hostname[MAXHOSTNAMELEN] = '\0';
+ return (1);
+}
+
+
+/*
+ * setspeed - set the speed. Returns 1 upon successful processing of options,
+ * and 0 otherwise.
+ */
+static int
+setspeed(arg)
+ char *arg;
+{
+ char *ptr;
+ int spd;
+
+ if (prepass)
+ return (1);
+ spd = strtol(arg, &ptr, 0);
+ if (ptr == arg || *ptr != '\0' || spd <= 0)
+ return (0);
+ inspeed = spd;
+ save_source(&speed_info);
+ return (1);
+}
+
+
+/*
+ * setdevname - set the device name. Returns 1 upon successful processing of
+ * options, 0 when the device does not exist, and -1 when an error is
+ * encountered.
+ */
+static int
+setdevname(cp)
+ char *cp;
+{
+ struct stat statbuf;
+ char dev[MAXPATHLEN];
+
+ if (*cp == '\0')
+ return (0);
+
+ if (strncmp("/dev/", cp, 5) != 0) {
+ (void) strlcpy(dev, "/dev/", sizeof(dev));
+ (void) strlcat(dev, cp, sizeof(dev));
+ cp = dev;
+ }
+
+ /*
+ * Check if there is a character device by this name.
+ */
+ if (stat(cp, &statbuf) < 0) {
+ if (errno == ENOENT) {
+ return (0);
+ }
+ option_error("Couldn't stat '%s': %m", cp);
+ return (-1);
+ }
+ if (!S_ISCHR(statbuf.st_mode)) {
+ option_error("'%s' is not a character device", cp);
+ return (-1);
+ }
+
+ if (phase != PHASE_INITIALIZE) {
+ option_error("device name cannot be changed after initialization");
+ return (-1);
+ } else if (devnam_fixed) {
+ option_error("per-tty options file may not specify device name");
+ return (-1);
+ }
+
+ if (devnam_info.priv && !privileged_option) {
+ option_error("device name %s from %s cannot be overridden",
+ devnam, name_source(&devnam_info));
+ return (-1);
+ }
+
+ (void) strlcpy(devnam, cp, sizeof(devnam));
+ devstat = statbuf;
+ default_device = 0;
+ save_source(&devnam_info);
+
+ return (1);
+}
+
+
+/*
+ * setipaddr - set the IP address. Returns 1 upon successful processing of
+ * options, 0 when the argument does not contain a `:', and -1 for error.
+ */
+static int
+setipaddr(arg)
+ char *arg;
+{
+ struct hostent *hp;
+ char *colon;
+ u_int32_t local, remote;
+ ipcp_options *wo = &ipcp_wantoptions[0];
+
+ /*
+ * IP address pair separated by ":".
+ */
+ if ((colon = strchr(arg, ':')) == NULL)
+ return (0);
+ if (prepass)
+ return (1);
+
+ /*
+ * If colon first character, then no local addr.
+ */
+ if (colon != arg) {
+ *colon = '\0';
+ if ((local = inet_addr(arg)) == (u_int32_t) -1) {
+ if ((hp = gethostbyname(arg)) == NULL) {
+ option_error("unknown host: %s", arg);
+ return (-1);
+ } else {
+ BCOPY(hp->h_addr, &local, sizeof(local));
+ }
+ }
+ if (bad_ip_adrs(local)) {
+ option_error("bad local IP address %I", local);
+ return (-1);
+ }
+ if (local != 0) {
+ save_source(&ipsrc_info);
+ wo->ouraddr = local;
+ }
+ *colon = ':';
+ }
+
+ /*
+ * If colon last character, then no remote addr.
+ */
+ if (*++colon != '\0') {
+ if ((remote = inet_addr(colon)) == (u_int32_t) -1) {
+ if ((hp = gethostbyname(colon)) == NULL) {
+ option_error("unknown host: %s", colon);
+ return (-1);
+ } else {
+ BCOPY(hp->h_addr, &remote, sizeof(remote));
+ if (remote_name[0] == '\0')
+ (void) strlcpy(remote_name, colon, sizeof(remote_name));
+ }
+ }
+ if (bad_ip_adrs(remote)) {
+ option_error("bad remote IP address %I", remote);
+ return (-1);
+ }
+ if (remote != 0) {
+ save_source(&ipdst_info);
+ wo->hisaddr = remote;
+ }
+ }
+
+ return (1);
+}
+
+
+/*
+ * setnetmask - set the netmask to be used on the interface. Returns 1 upon
+ * successful processing of options, and 0 otherwise.
+ */
+/*ARGSUSED*/
+static int
+setnetmask(argv, opt)
+ char **argv;
+ option_t *opt;
+{
+ u_int32_t mask;
+ int n;
+ char *p;
+
+ /*
+ * Unfortunately, if we use inet_addr, we can't tell whether
+ * a result of all 1s is an error or a valid 255.255.255.255.
+ */
+ p = *argv;
+ n = parse_dotted_ip(p, &mask);
+
+ mask = htonl(mask);
+
+ if (n == 0 || p[n] != 0 || (netmask & ~mask) != 0) {
+ option_error("invalid netmask value '%s'", *argv);
+ return (0);
+ }
+
+ netmask = mask;
+ return (1);
+}
+
+/*
+ * parse_dotted_ip - parse and convert the IP address string to make
+ * sure it conforms to the dotted notation. Returns the length of
+ * processed characters upon success, and 0 otherwise. If successful,
+ * the converted IP address number is stored in vp, in the host byte
+ * order.
+ */
+int
+parse_dotted_ip(cp, vp)
+ register char *cp;
+ u_int32_t *vp;
+{
+ register u_int32_t val, base, n;
+ register char c;
+ char *cp0 = cp;
+ u_char parts[3], *pp = parts;
+
+ if ((*cp == '\0') || (vp == NULL))
+ return (0); /* disallow null string in cp */
+ *vp = 0;
+again:
+ /*
+ * Collect number up to ``.''. Values are specified as for C:
+ * 0x=hex, 0=octal, other=decimal.
+ */
+ val = 0; base = 10;
+ if (*cp == '0') {
+ if (*++cp == 'x' || *cp == 'X')
+ base = 16, cp++;
+ else
+ base = 8;
+ }
+ while ((c = *cp) != '\0') {
+ if (isdigit(c)) {
+ if ((c - '0') >= base)
+ break;
+ val = (val * base) + (c - '0');
+ cp++;
+ continue;
+ }
+ if (base == 16 && isxdigit(c)) {
+ val = (val << 4) + (c + 10 - (islower(c) ? 'a' : 'A'));
+ cp++;
+ continue;
+ }
+ break;
+ }
+ if (*cp == '.') {
+ /*
+ * Internet format:
+ * a.b.c.d
+ * a.b.c (with c treated as 16-bits)
+ * a.b (with b treated as 24 bits)
+ */
+ if ((pp >= parts + 3) || (val > 0xff)) {
+ return (0);
+ }
+ *pp++ = (u_char)val;
+ cp++;
+ goto again;
+ }
+ /*
+ * Check for trailing characters.
+ */
+ if (*cp != '\0' && !isspace(*cp)) {
+ return (0);
+ }
+ /*
+ * Concoct the address according to the number of parts specified.
+ */
+ n = pp - parts;
+ switch (n) {
+ case 0: /* a -- 32 bits */
+ break;
+ case 1: /* a.b -- 8.24 bits */
+ if (val > 0xffffff)
+ return (0);
+ val |= parts[0] << 24;
+ break;
+ case 2: /* a.b.c -- 8.8.16 bits */
+ if (val > 0xffff)
+ return (0);
+ val |= (parts[0] << 24) | (parts[1] << 16);
+ break;
+ case 3: /* a.b.c.d -- 8.8.8.8 bits */
+ if (val > 0xff)
+ return (0);
+ val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
+ break;
+ default:
+ return (0);
+ }
+ *vp = val;
+ return (cp - cp0);
+}
+
+/*
+ * setxonxoff - modify the asyncmap to include escaping XON and XOFF
+ * characters used for software flow control. Returns 1 upon successful
+ * processing of options, and 0 otherwise.
+ */
+/*ARGSUSED*/
+static int
+setxonxoff(argv, opt)
+ char **argv;
+ option_t *opt;
+{
+ int xonxoff = 0x000A0000;
+
+ lcp_wantoptions[0].neg_asyncmap = 1;
+ lcp_wantoptions[0].asyncmap |= xonxoff; /* escape ^S and ^Q */
+ lcp_allowoptions[0].asyncmap |= xonxoff;
+ xmit_accm[0][0] |= xonxoff;
+ xmit_accm[0][4] |= xonxoff; /* escape 0x91 and 0x93 as well */
+
+ crtscts = -2;
+ return (1);
+}
+
+/*
+ * setlogfile - open (or create) a file used for logging purposes. Returns 1
+ * upon success, and 0 otherwise.
+ */
+/*ARGSUSED*/
+static int
+setlogfile(argv, opt)
+ char **argv;
+ option_t *opt;
+{
+ int fd, err;
+
+ if (!privileged_option)
+ (void) seteuid(getuid());
+ fd = open(*argv, O_WRONLY | O_APPEND | O_CREAT | O_EXCL, 0644);
+ if (fd < 0 && errno == EEXIST)
+ fd = open(*argv, O_WRONLY | O_APPEND);
+ err = errno;
+ if (!privileged_option)
+ (void) seteuid(0);
+ if (fd < 0) {
+ errno = err;
+ option_error("Can't open log file %s: %m", *argv);
+ return (0);
+ }
+ if (log_to_file && log_to_fd >= 0)
+ (void) close(log_to_fd);
+ log_to_fd = fd;
+ log_to_file = 1;
+ early_log = 0;
+ return (1);
+}
+
+#ifdef PLUGIN
+/*
+ * loadplugin - load and initialize the plugin. Returns 1 upon successful
+ * processing of the plugin, and 0 otherwise.
+ */
+/*ARGSUSED*/
+static int
+loadplugin(argv, opt)
+ char **argv;
+ option_t *opt;
+{
+ char *arg = *argv;
+ void *handle;
+ const char *err;
+ void (*init) __P((void));
+
+ handle = dlopen(arg, RTLD_GLOBAL | RTLD_NOW);
+ if (handle == NULL) {
+ err = dlerror();
+ if (err != NULL)
+ option_error("%s", err);
+ option_error("Couldn't load plugin %s", arg);
+ return (0);
+ }
+ init = (void (*)(void))dlsym(handle, "plugin_init");
+ if (init == NULL) {
+ option_error("%s has no initialization entry point", arg);
+ (void) dlclose(handle);
+ return (0);
+ }
+ info("Plugin %s loaded.", arg);
+ (*init)();
+ return (1);
+}
+#endif /* PLUGIN */
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppd/patchlevel.h b/usr/src/cmd/cmd-inet/usr.bin/pppd/patchlevel.h
new file mode 100644
index 0000000000..840a4dfd9c
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppd/patchlevel.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2000 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifndef _PATCH_LEVEL_H
+#define _PATCH_LEVEL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define PATCHLEVEL 0
+
+#define VERSION "2.4"
+#define IMPLEMENTATION \
+ "b1 (Sun Microsystems, Inc., " __DATE__ " " __TIME__ ")"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PATCH_LEVEL_H */
+
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppd/pathnames.h b/usr/src/cmd/cmd-inet/usr.bin/pppd/pathnames.h
new file mode 100644
index 0000000000..5dbdb43439
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppd/pathnames.h
@@ -0,0 +1,95 @@
+/*
+ * pathnames.h - define path names used by pppd.
+ *
+ * Copyright (c) 2000 by Sun Microsystems, Inc.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies.
+ *
+ * SUN MAKES NO REPRESENTATION OR WARRANTIES ABOUT THE SUITABILITY OF
+ * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+ * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
+ * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
+ * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES
+ *
+ * Copyright (c) 1993 The Australian National University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Australian National University. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: pathnames.h,v 1.13 2000/04/04 07:06:52 paulus Exp $
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifndef __PATHNAMES_H__
+#define __PATHNAMES_H__
+
+#ifdef HAVE_PATHS_H
+#include <paths.h>
+#else /* HAVE_PATHS_H */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _PATH_VARRUN
+#define _PATH_VARRUN "/etc/ppp/"
+#endif /* _PATH_VARRUN */
+
+#define _PATH_DEVNULL "/dev/null"
+#endif /* HAVE_PATHS_H */
+
+#ifndef _ROOT_PATH
+#define _ROOT_PATH
+#endif /* _ROOT_PATH */
+
+/*
+ * Duplication of /etc/ppp allows default compilation on non-ANSI compilers.
+ */
+#define _PATH_UPAPFILE _ROOT_PATH "/etc/ppp/pap-secrets"
+#define _PATH_CHAPFILE _ROOT_PATH "/etc/ppp/chap-secrets"
+#define _PATH_SYSOPTIONS _ROOT_PATH "/etc/ppp/options"
+#define _PATH_IPUP _ROOT_PATH "/etc/ppp/ip-up"
+#define _PATH_IPDOWN _ROOT_PATH "/etc/ppp/ip-down"
+#define _PATH_IPV6UP _ROOT_PATH "/etc/ppp/ipv6-up"
+#define _PATH_IPV6DOWN _ROOT_PATH "/etc/ppp/ipv6-down"
+#define _PATH_IPXUP _ROOT_PATH "/etc/ppp/ipx-up"
+#define _PATH_IPXDOWN _ROOT_PATH "/etc/ppp/ipx-down"
+#define _PATH_AUTHUP _ROOT_PATH "/etc/ppp/auth-up"
+#define _PATH_AUTHDOWN _ROOT_PATH "/etc/ppp/auth-down"
+#define _PATH_TTYOPT _ROOT_PATH "/etc/ppp/options."
+#define _PATH_CONNERRS _ROOT_PATH "/etc/ppp/connect-errors"
+#define _PATH_PEERFILES _ROOT_PATH "/etc/ppp/peers/"
+#define _PATH_RESOLV _ROOT_PATH "/etc/ppp/resolv.conf"
+
+#define _PATH_USEROPT ".ppprc"
+
+#ifdef __STDC__
+#define _PATH_PPPDB _ROOT_PATH _PATH_VARRUN "pppd.tdb"
+#else /* __STDC__ */
+#ifdef HAVE_PATHS_H
+#define _PATH_PPPDB "/var/run/pppd.tdb"
+#else /* HAVE_PATHS_H */
+#define _PATH_PPPDB "/etc/ppp/pppd.tdb"
+#endif /* HAVE_PATHS_H */
+#endif /* __STDC__ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __PATHNAMES_H__ */
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppd/plugins/Makefile b/usr/src/cmd/cmd-inet/usr.bin/pppd/plugins/Makefile
new file mode 100644
index 0000000000..eaed63af74
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppd/plugins/Makefile
@@ -0,0 +1,115 @@
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# cmd/cmd-inet/usr.bin/pppd/plugins/Makefile
+#
+
+
+MINCONN = minconn.so
+PASSPROMPT = passprompt.so
+PPPOE = pppoe.so
+
+LIBRARIES = minconn.so passprompt.so pppoe.so
+OBJECTS = minconn.o passprompt.o pppoe.o
+
+# The rest of the sources are GPL
+LINTSRCS = pppoe.c
+
+include $(SRC)/lib/Makefile.lib
+include ../Makefile.def
+
+# Express objects in terms of "pics" thus triggering the appropriate CPPFLAGS,
+# CFLAGS and DYNFLAGS settings from lib/Makefile.lib to build shared objects.
+$(MINCONN):= PICS = pics/minconn.o
+$(PASSPROMPT):= PICS = pics/passprompt.o
+$(PPPOE):= PICS = pics/pppoe.o
+
+# Suppress -h setting from DYNFLAGS as these libraries aren't linked against.
+# Establish external references through mapfiles and dependencies to allow
+# use of -zdefs.
+HSONAME=
+$(MINCONN):= MAPFILE = mapfile-minconn
+$(PASSPROMPT):= MAPFILE = mapfile-passprompt
+$(PPPOE):= MAPFILE = mapfile-pppoe
+
+# A bug in pmake causes redundancy when '+=' is conditionally assigned, so
+# '=' is used with extra variables.
+XXXLDLIBS =
+$(PASSPROMPT):= XXXLDLIBS = -lc
+LDLIBS += $(XXXLDLIBS)
+
+CPPFLAGS += -I.. -I$(SRC)/uts/common
+# XX64 -- this should not be needed -- fix me
+$(INTEL_BLD)DYNFLAGS += -_gcc=-nostdlib
+DYNFLAGS += -M$(MAPFILE)
+
+CLOBBERFILES += $(LIBRARIES)
+
+LIBPPPPLUGIN= $(LIBRARIES:%=$(LIBPPPPLUGINDIR)/%)
+
+$(LIBPPPPLUGIN):= FILEMODE = 0544
+$(LIBPPPPLUGIN):= OWNER = root
+$(LIBPPPPLUGIN):= GROUP = bin
+
+$(LIBPPPPLUGINDIR):= FILEMODE = 0755
+$(LIBPPPPLUGINDIR):= OWNER = root
+$(LIBPPPPLUGINDIR):= GROUP = bin
+
+# This is needed because install doesn't handle -g well.
+UTILDIR= $(ROOT)/usr/share/src/ppputil
+$(UTILDIR):= OWNER = root
+$(UTILDIR):= GROUP = bin
+ROOTSRC= $(UTILDIR)/plugins
+SRCFILES= Makefile minconn.c passprompt.c pppd.h
+ROOTSRCFILES= $(SRCFILES:%=$(ROOTSRC)/%)
+$(ROOTSRC):= OWNER = root
+$(ROOTSRC):= GROUP = bin
+$(ROOTSRCFILES):= FILEMODE= 0444
+$(ROOTSRCFILES):= OWNER = root
+$(ROOTSRCFILES):= GROUP = bin
+
+.KEEP_STATE:
+
+all: $(LIBRARIES)
+
+$(MINCONN): pics .WAIT $$(MAPFILE) $$(PICS)
+ $(BUILD.SO)
+ $(POST_PROCESS_SO)
+
+$(PASSPROMPT): pics .WAIT $$(MAPFILE) $$(PICS)
+ $(BUILD.SO)
+ $(POST_PROCESS_SO)
+
+$(PPPOE): pics .WAIT $$(MAPFILE) $$(PICS)
+ $(BUILD.SO)
+ $(POST_PROCESS_SO)
+
+install: all $(LIBPPPPLUGINDIR) $(LIBPPPPLUGIN) install_src
+
+$(LIBPPPPLUGINDIR):
+ $(INS.dir)
+
+$(LIBPPPPLUGINDIR)/%: %
+ $(INS.file)
+
+$(UTILDIR) $(ROOTSRC):
+ $(INS.dir)
+
+$(ROOTSRC)/Makefile%: Makefile%.dist
+ $(INS.rename)
+
+$(ROOTSRC)/%.h: ../%.h
+ $(INS.file)
+
+$(ROOTSRC)/%: %
+ $(INS.file)
+
+install_src: $(UTILDIR) .WAIT $(ROOTSRC) .WAIT $(ROOTSRCFILES)
+
+lint:
+ $(LINT.c) $(LINTSRCS) $(LDLIBS)
+
+include $(SRC)/lib/Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppd/plugins/Makefile.dist b/usr/src/cmd/cmd-inet/usr.bin/pppd/plugins/Makefile.dist
new file mode 100644
index 0000000000..972e8036fd
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppd/plugins/Makefile.dist
@@ -0,0 +1,27 @@
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 2000 by Sun Microsystems, Inc.
+# All rights reserved.
+
+PLUGINS= minconn passprompt
+TARGETS= $(PLUGINS:%=%.so)
+OBJS= $(PLUGINS:%=%.o)
+PLUGINDIR= /usr/lib/inet/ppp/
+INSTALLED= $(TARGETS:%=$(PLUGINDIR)/%)
+CFLAGS= -DPPP_DEFS_IN_NET
+
+all: $(TARGETS)
+
+clean:
+ $(RM) -f $(TARGETS) $(OBJS)
+
+%.so: %.o
+ $(LD) -s -G -h $@ -o $@ $^
+
+install: $(TARGETS)
+ @test -d $(PLUGINDIR) || mkdir -m 755 -p $(PLUGINDIR)
+ @cp $(TARGETS) $(PLUGINDIR) && strip $(INSTALLED)
+
+clobber: clean
+ $(RM) -f $(INSTALLED)
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppd/plugins/mapfile-minconn b/usr/src/cmd/cmd-inet/usr.bin/pppd/plugins/mapfile-minconn
new file mode 100644
index 0000000000..1b94c267e4
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppd/plugins/mapfile-minconn
@@ -0,0 +1,14 @@
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+# External declarations - used to suppress -zdefs errors.
+
+{
+ add_options = FUNCTION extern; # Provided by pppd.
+ idle_time_hook = DATA extern; # " "
+ idle_time_limit = DATA extern; # " "
+ info = FUNCTION extern; # " "
+};
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppd/plugins/mapfile-passprompt b/usr/src/cmd/cmd-inet/usr.bin/pppd/plugins/mapfile-passprompt
new file mode 100644
index 0000000000..b7e376f948
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppd/plugins/mapfile-passprompt
@@ -0,0 +1,18 @@
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+# External declarations - used to suppress -zdefs errors.
+
+{
+ add_options = FUNCTION extern; # Provided by pppd.
+ detached = DATA extern; # " "
+ error = FUNCTION extern; # " "
+ pap_passwd_hook = DATA extern; # " "
+ remote_name = DATA extern; # " "
+ slprintf = FUNCTION extern; # " "
+ sys_close = FUNCTION extern; # " "
+ warn = FUNCTION extern; # " "
+};
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppd/plugins/mapfile-pppoe b/usr/src/cmd/cmd-inet/usr.bin/pppd/plugins/mapfile-pppoe
new file mode 100644
index 0000000000..1fd74bbf75
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppd/plugins/mapfile-pppoe
@@ -0,0 +1,49 @@
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+# External declarations - used to suppress -zdefs errors.
+
+{
+ script_setenv = FUNCTION extern; # Provided by pppd.
+ script_getenv = FUNCTION extern; # " "
+ slprintf = FUNCTION extern; # " "
+ option_error = FUNCTION extern; # " "
+ warn = FUNCTION extern; # " "
+ fatal = FUNCTION extern; # " "
+ dbglog = FUNCTION extern; # " "
+ absmax_mtu = DATA extern; # " "
+ absmax_mru = DATA extern; # " "
+ updown_script_hook = DATA extern; # " "
+ devnam_info = DATA extern; # " "
+ check_options_hook = DATA extern; # " "
+ device_pipe_hook = DATA extern; # " "
+ already_ppp = DATA extern; # " "
+ devnam = DATA extern; # " "
+ sys_read_packet_hook = DATA extern; # " "
+ ifname = DATA extern; # " "
+ fclose = FUNCTION extern; # " "
+ ioctl = FUNCTION extern; # " "
+ fdopen = FUNCTION extern; # " "
+ close = FUNCTION extern; # " "
+ open = FUNCTION extern; # " "
+ strcmp = FUNCTION extern; # " "
+ fgets = FUNCTION extern; # " "
+ strlen = FUNCTION extern; # " "
+ seteuid = FUNCTION extern; # " "
+ ether_ntoa = FUNCTION extern; # " "
+ ___errno = FUNCTION extern; # " "
+ strdup = FUNCTION extern; # " "
+ run_program = FUNCTION extern; # " "
+ malloc = FUNCTION extern; # " "
+ strrchr = FUNCTION extern; # " "
+ strchr = FUNCTION extern; # " "
+ error = FUNCTION extern; # " "
+ free = FUNCTION extern; # " "
+ strcpy = FUNCTION extern; # " "
+ memcpy = FUNCTION extern; # " "
+ script_unsetenv = FUNCTION extern; # " "
+ ntohs = FUNCTION extern; # " "
+};
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppd/plugins/minconn.c b/usr/src/cmd/cmd-inet/usr.bin/pppd/plugins/minconn.c
new file mode 100644
index 0000000000..8c9354ebd3
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppd/plugins/minconn.c
@@ -0,0 +1,40 @@
+/*
+ * minconn.c - pppd plugin to implement a `minconnect' option.
+ *
+ * Copyright 1999 Paul Mackerras.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#include <stddef.h>
+#include <time.h>
+#include "pppd.h"
+
+static int minconnect = 0;
+
+static option_t my_options[] = {
+ { "minconnect", o_int, &minconnect,
+ "Set minimum connect time before idle timeout applies" },
+ { NULL }
+};
+
+static int my_get_idle(struct ppp_idle *idle)
+{
+ time_t t;
+
+ if (idle == NULL)
+ return minconnect? minconnect: idle_time_limit;
+ t = idle->xmit_idle;
+ if (idle->recv_idle < t)
+ t = idle->recv_idle;
+ return idle_time_limit - t;
+}
+
+void plugin_init(void)
+{
+ info("plugin_init");
+ add_options(my_options);
+ idle_time_hook = my_get_idle;
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppd/plugins/passprompt.c b/usr/src/cmd/cmd-inet/usr.bin/pppd/plugins/passprompt.c
new file mode 100644
index 0000000000..ce361e9c37
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppd/plugins/passprompt.c
@@ -0,0 +1,123 @@
+/*
+ * passprompt.c - pppd plugin to invoke an external PAP password prompter
+ *
+ * Copyright 1999 Paul Mackerras, Alan Curry.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <syslog.h>
+#include "pppd.h"
+
+static char promptprog[PATH_MAX+1];
+
+static option_t options[] = {
+ { "promptprog", o_string, promptprog,
+ "External PAP password prompting program",
+ OPT_STATIC, NULL, PATH_MAX },
+ { NULL }
+};
+
+static int promptpass(char *user, char *passwd)
+{
+ int p[2];
+ pid_t kid;
+ int readgood, wstat;
+ int red;
+
+ if (promptprog[0] == 0 || access(promptprog, X_OK) < 0)
+ return -1; /* sorry, can't help */
+
+ /* This occurs when we're probed for the ability to supply a password */
+ if (user != NULL && passwd == NULL)
+ return 1;
+
+ if (pipe(p)) {
+ warn("Can't make a pipe for %s", promptprog);
+ return 0;
+ }
+ if ((kid = fork()) == (pid_t) -1) {
+ warn("Can't fork to run %s", promptprog);
+ (void) close(p[0]);
+ (void) close(p[1]);
+ return 0;
+ }
+ if (kid == (pid_t)0) {
+ /* we are the child, exec the program */
+ char *argv[5], fdstr[32];
+
+ sys_close();
+ closelog();
+ if (detached && p[1] <= 2) {
+ (void) dup2(p[1], 3);
+ p[1] = 3;
+ }
+ (void) close(p[0]);
+ if (detached) {
+ red = open("/etc/ppp/prompt-errors", O_WRONLY | O_APPEND | O_CREAT,
+ 0600);
+ (void) dup2(red, 1);
+ (void) dup2(red, 2);
+ }
+ (void) seteuid(getuid());
+ (void) setegid(getgid());
+ argv[0] = promptprog;
+ argv[1] = user == NULL ? "" : user;
+ argv[2] = remote_name;
+ slprintf(fdstr, sizeof (fdstr), "%d", p[1]);
+ argv[3] = fdstr;
+ argv[4] = NULL;
+ (void) execv(*argv, argv);
+ _exit(127);
+ }
+
+ /* we are the parent, read the password from the pipe */
+ (void) close(p[1]);
+ readgood = 0;
+ do {
+ red = read(p[0], passwd + readgood, MAXSECRETLEN-1 - readgood);
+ if (red == 0)
+ break;
+ if (red < 0) {
+ if (errno == EINTR)
+ continue;
+ error("Can't read secret from %s: %m", promptprog);
+ readgood = -1;
+ break;
+ }
+ readgood += red;
+ } while (readgood < MAXSECRETLEN - 1);
+ passwd[readgood] = 0;
+ (void) close(p[0]);
+
+ /* now wait for child to exit */
+ while (waitpid(kid, &wstat, 0) < 0) {
+ if (errno != EINTR) {
+ warn("error waiting for %s: %m", promptprog);
+ break;
+ }
+ }
+
+ if (readgood < 0)
+ return 0;
+ if (readgood > 0 && passwd[--readgood] == '\n')
+ passwd[readgood] = '\0';
+ if (!WIFEXITED(wstat))
+ warn("%s terminated abnormally", promptprog);
+ if (WEXITSTATUS(wstat) != 0)
+ warn("%s exited with code %d", promptprog, WEXITSTATUS(wstat));
+
+ return 1;
+}
+
+void plugin_init(void)
+{
+ add_options(options);
+ pap_passwd_hook = promptpass;
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppd/plugins/pppoe.c b/usr/src/cmd/cmd-inet/usr.bin/pppd/plugins/pppoe.c
new file mode 100644
index 0000000000..8d58ced061
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppd/plugins/pppoe.c
@@ -0,0 +1,476 @@
+/*
+ * pppoe.c - pppd plugin to handle PPPoE operation.
+ *
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <unistd.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <strings.h>
+#include <sys/stropts.h>
+#include <netinet/in.h>
+#include <net/pppio.h>
+#include <net/sppptun.h>
+#include <net/pppoe.h>
+
+#include "pppd.h"
+#include "pathnames.h"
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/* Saved hook pointers */
+static int (*old_check_options)(uid_t uid);
+static int (*old_updown_script)(const char ***argsp);
+static int (*old_sys_read_packet)(int retv, struct strbuf *ctrl,
+ struct strbuf *data, int flags);
+
+/* Room for 3 IPv4 addresses and metric */
+#define RTE_MSG_LEN (3*16 + 10 + 1)
+
+/* Environment string for routes */
+#define RTE_STR "ROUTE_%d"
+
+/*
+ * strioctl()
+ *
+ * wrapper for STREAMS I_STR ioctl.
+ */
+static int
+strioctl(int fd, int cmd, void *ptr, int ilen, int olen)
+{
+ struct strioctl str;
+
+ str.ic_cmd = cmd;
+ str.ic_timout = 0; /* Use default timer; 15 seconds */
+ str.ic_len = ilen;
+ str.ic_dp = ptr;
+
+ if (ioctl(fd, I_STR, &str) == -1) {
+ return (-1);
+ }
+ if (str.ic_len != olen) {
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * If the user named the tunneling device, check that it is
+ * reasonable; otherwise check that standard input is the tunnel.
+ */
+static int
+pppoe_check_options(uid_t uid)
+{
+ int tstfd; /* fd for device being checked */
+ int err; /* saved errno value */
+ int retv; /* return value */
+ int intv; /* integer return value (from ioctl) */
+ union ppptun_name ptn;
+
+ if (devnam[0] != '\0') {
+ /*
+ * Open as real user so that modes on device can be
+ * used to limit access.
+ */
+ if (!devnam_info.priv)
+ (void) seteuid(uid);
+ tstfd = open(devnam, O_NONBLOCK | O_RDWR, 0);
+ err = errno;
+ if (!devnam_info.priv)
+ (void) seteuid(0);
+ if (tstfd == -1) {
+ errno = err;
+ option_error("unable to open %s: %m", devnam);
+ return (-1);
+ }
+ retv = strioctl(tstfd, PPPTUN_GDATA, &ptn, 0, sizeof (ptn));
+ (void) close(tstfd);
+ if (retv == -1) {
+ option_error("device %s is not a PPP tunneling device",
+ devnam);
+ return (-1);
+ }
+ } else {
+ retv = strioctl(0, PPPIO_GTYPE, &intv, 0, sizeof (intv));
+ if (retv == -1) {
+ option_error("standard input is not a PPP device");
+ return (-1);
+ }
+ retv = strioctl(0, PPPTUN_GDATA, &ptn, 0, sizeof (ptn));
+ if (retv == -1) {
+ option_error("standard input is not a PPP tunnel");
+ return (-1);
+ }
+ if (strcmp(ptn.ptn_name + strlen(ptn.ptn_name) - 6,
+ ":pppoe") != 0) {
+ option_error("standard input not connected to PPPoE");
+ return (-1);
+ }
+ }
+ if (old_check_options != NULL &&
+ old_check_options != pppoe_check_options)
+ return ((*old_check_options)(uid));
+ return (0);
+}
+
+/*
+ * When we're about to call one of the up or down scripts, change the
+ * second argument to contain the interface name and selected PPPoE
+ * service.
+ */
+static int
+pppoe_updown_script(const char ***argsp)
+{
+ const char *cp;
+
+ if ((*argsp)[2] == devnam &&
+ (cp = script_getenv("IF_AND_SERVICE")) != NULL)
+ (*argsp)[2] = cp;
+ if (old_updown_script != NULL &&
+ old_updown_script != pppoe_updown_script)
+ return ((*old_updown_script)(argsp));
+ return (0);
+}
+
+/*
+ * Concatenate and save strings from command line into environment
+ * variable.
+ */
+static void
+cat_save_env(char **argv, char idchar, const char *envname)
+{
+ char **argp;
+ int totlen;
+ char *str;
+ char *cp;
+
+ totlen = 0;
+ for (argp = argv; argp[0] != NULL; argp += 2)
+ if (*argp[0] == idchar)
+ totlen += strlen(argp[1]) + 1;
+ if ((str = malloc(totlen + 1)) == NULL) {
+ error("cannot malloc PPPoE environment for %s", envname);
+ return;
+ }
+ cp = str;
+ for (argp = argv; argp[0] != NULL; argp += 2)
+ if (*argp[0] == idchar) {
+ (void) strcpy(cp, argp[1]);
+ cp += strlen(cp);
+ *cp++ = '\n';
+ }
+ *cp = '\0';
+ script_setenv(envname, str, 0);
+}
+
+/*
+ * Convert Message Of The Moment (MOTM) and Host Uniform Resource
+ * Locator (HURL) strings into environment variables and command-line
+ * arguments for script.
+ */
+static void
+handle_motm_hurl(char **argv, int argc, const uint8_t *tagp, int pktlen)
+{
+ int ttype;
+ int tlen;
+ char *str;
+ char **oargv = argv;
+
+ /* Must have room for two strings and NULL terminator. */
+ while (argc >= 3) {
+ str = NULL;
+ while (pktlen >= POET_HDRLEN) {
+ ttype = POET_GET_TYPE(tagp);
+ if (ttype == POETT_END)
+ break;
+ tlen = POET_GET_LENG(tagp);
+ if (tlen > pktlen - POET_HDRLEN)
+ break;
+ if (ttype == POETT_HURL || ttype == POETT_MOTM) {
+ if ((str = malloc(tlen + 1)) == NULL) {
+ error("cannot malloc PPPoE message");
+ break;
+ }
+ (void) memcpy(str, POET_DATA(tagp), tlen);
+ str[tlen] = '\0';
+ }
+ pktlen -= POET_HDRLEN + tlen;
+ tagp += POET_HDRLEN + tlen;
+ if (str != NULL)
+ break;
+ }
+ if (str == NULL)
+ break;
+ *argv++ = ttype == POETT_HURL ? "hurl" : "motm";
+ *argv++ = str;
+ argc -= 2;
+ }
+ *argv = NULL;
+ cat_save_env(oargv, 'h', "HURL");
+ cat_save_env(oargv, 'm', "MOTM");
+}
+
+/*
+ * Convert IP Route Add structures into environment variables and
+ * command-line arguments for script.
+ */
+static void
+handle_ip_route_add(char **argv, int argc, const uint8_t *tagp, int pktlen)
+{
+ int ttype;
+ int tlen;
+ char *str;
+ poer_t poer;
+ int idx;
+ char envname[sizeof (RTE_STR) + 10];
+
+ idx = 0;
+
+ /* Must have room for four strings and NULL terminator. */
+ while (argc >= 5) {
+ str = NULL;
+ while (pktlen >= POET_HDRLEN) {
+ ttype = POET_GET_TYPE(tagp);
+ if (ttype == POETT_END)
+ break;
+ tlen = POET_GET_LENG(tagp);
+ if (tlen > pktlen - POET_HDRLEN)
+ break;
+ if (ttype == POETT_RTEADD && tlen >= sizeof (poer) &&
+ (str = malloc(RTE_MSG_LEN)) == NULL) {
+ error("cannot malloc PPPoE route");
+ break;
+ }
+ pktlen -= POET_HDRLEN + tlen;
+ tagp += POET_HDRLEN + tlen;
+ if (str != NULL)
+ break;
+ }
+ if (str == NULL)
+ break;
+ /* No alignment restrictions on source; copy to local. */
+ (void) memcpy(&poer, POET_DATA(tagp), sizeof (poer));
+ (void) slprintf(str, RTE_MSG_LEN, "%I %I %I %d",
+ poer.poer_dest_network, poer.poer_subnet_mask,
+ poer.poer_gateway, (int)poer.poer_metric);
+ /* Save off the environment variable version of this. */
+ (void) slprintf(envname, sizeof (envname), RTE_STR, ++idx);
+ script_setenv(envname, str, 0);
+ *argv++ = str; /* Destination */
+ str = strchr(str, ' ');
+ *str++ = '\0';
+ *argv++ = str; /* Subnet mask */
+ str = strchr(str, ' ');
+ *str++ = '\0';
+ *argv++ = str; /* Gateway */
+ str = strchr(str, ' ');
+ *str++ = '\0';
+ *argv++ = str; /* Metric */
+ argc -= 4;
+ }
+ *argv = NULL;
+}
+
+/*
+ * If we get here, then the driver has already validated the sender,
+ * the PPPoE version, the message length, and session ID. The code
+ * number is known not to be zero.
+ */
+static int
+handle_pppoe_input(const ppptun_atype *pma, struct strbuf *ctrl,
+ struct strbuf *data)
+{
+ const poep_t *poep;
+ struct ppp_ls *plp;
+ const char *mname;
+ const char *cstr;
+ char *str;
+ char *cp;
+ char *argv[64];
+ pid_t rpid;
+ char **argp;
+ int idx;
+ char envname[sizeof (RTE_STR) + 10];
+ const uint8_t *tagp;
+ int pktlen;
+
+ /*
+ * Warning: the data->buf pointer here is not necessarily properly
+ * aligned for access to the poep_session_id or poep_length members.
+ */
+ /* LINTED: alignment */
+ poep = (const poep_t *)data->buf;
+ tagp = (const uint8_t *)poep + offsetof(poep_t, poep_length);
+ pktlen = (tagp[0] << 8) + tagp[1];
+ tagp = (const uint8_t *)(poep + 1);
+ switch (poep->poep_code) {
+ case POECODE_PADT:
+ dbglog("received PPPoE PADT; connection has been closed");
+ /* LINTED: alignment */
+ plp = (struct ppp_ls *)ctrl->buf;
+ plp->magic = PPPLSMAGIC;
+ plp->ppp_message = PPP_LINKSTAT_HANGUP;
+ ctrl->len = sizeof (*plp);
+ return (0);
+
+ /* Active Discovery Message and Network extensions */
+ case POECODE_PADM:
+ case POECODE_PADN:
+ if (poep->poep_code == POECODE_PADM) {
+ argv[0] = _ROOT_PATH "/etc/ppp/pppoe-msg";
+ mname = "PADM";
+ handle_motm_hurl(argv + 4, Dim(argv) - 4, tagp, pktlen);
+ } else {
+ argv[0] = _ROOT_PATH "/etc/ppp/pppoe-network";
+ mname = "PADN";
+ handle_ip_route_add(argv + 4, Dim(argv) - 4, tagp,
+ pktlen);
+ }
+ argv[1] = ifname;
+ /* Note: strdup doesn't handle NULL input. */
+ str = NULL;
+ if ((cstr = script_getenv("IF_AND_SERVICE")) == NULL ||
+ (str = strdup(cstr)) == NULL) {
+ argv[2] = argv[3] = "";
+ } else {
+ if ((cp = strrchr(str, ':')) == NULL)
+ cp = str + strlen(str);
+ else
+ *cp++ = '\0';
+ argv[2] = str;
+ argv[3] = cp;
+ }
+ rpid = run_program(argv[0], argv, 0, NULL, NULL);
+ if (rpid == (pid_t)0)
+ dbglog("ignored PPPoE %s; no %s script", mname,
+ argv[0]);
+ else if (rpid != (pid_t)-1)
+ dbglog("PPPoE %s: started PID %d", mname, rpid);
+ if (str != NULL)
+ free(str);
+ /* Free storage allocated by handle_{motm_hurl,ip_route_add} */
+ idx = 0;
+ for (argp = argv + 4; *argp != NULL; ) {
+ if (poep->poep_code == POECODE_PADM) {
+ free(argp[1]);
+ argp += 2;
+ } else {
+ free(argp[0]);
+ argp += 4;
+ (void) slprintf(envname, sizeof (envname),
+ RTE_STR, ++idx);
+ script_unsetenv(envname);
+ }
+ }
+ if (poep->poep_code == POECODE_PADM) {
+ script_unsetenv("HURL");
+ script_unsetenv("MOTM");
+ }
+ break;
+
+ default:
+ warn("unexpected PPPoE code %d from %s", poep->poep_code,
+ ether_ntoa(&pma->pta_pppoe.ptma_mac_ether_addr));
+ break;
+ }
+ return (-1);
+}
+
+/*
+ * sys-solaris has just read in a packet; grovel through it and see if
+ * it's something we need to handle ourselves.
+ */
+static int
+pppoe_sys_read_packet(int retv, struct strbuf *ctrl, struct strbuf *data,
+ int flags)
+{
+ struct ppptun_control *ptc;
+
+ if (retv >= 0 && !(retv & MORECTL) && ctrl->len >= sizeof (uint32_t)) {
+ /* LINTED: alignment */
+ ptc = (struct ppptun_control *)ctrl->buf;
+ /* ptc_discrim is the first uint32_t of the structure. */
+ if (ptc->ptc_discrim == PPPOE_DISCRIM) {
+ retv = -1;
+ if (ctrl->len == sizeof (*ptc) &&
+ ptc->ptc_action == PTCA_CONTROL)
+ retv = handle_pppoe_input(&ptc->ptc_address,
+ ctrl, data);
+ if (retv < 0)
+ errno = EAGAIN;
+ return (retv);
+ }
+ }
+ /* Forward along to other plug-ins */
+ if (old_sys_read_packet != NULL &&
+ old_sys_read_packet != pppoe_sys_read_packet)
+ return ((*old_sys_read_packet)(retv, ctrl, data, flags));
+ return (retv);
+}
+
+/*
+ * Get an environment variable from the chat script.
+ */
+static int
+saveenv(FILE *fd, const char *envname)
+{
+ char envstr[1024];
+ int len;
+
+ if (fgets(envstr, sizeof (envstr), fd) == NULL)
+ return (-1);
+ len = strlen(envstr);
+ if (len <= 1)
+ return (0);
+ envstr[len-1] = '\0';
+ script_setenv(envname, envstr, 0);
+ return (1);
+}
+
+/*
+ * Read environment variables exported by chat script.
+ */
+static void
+pppoe_device_pipe(int pipefd)
+{
+ FILE *fd;
+ int i;
+ char envname[32];
+
+ fd = fdopen(pipefd, "r");
+ if (fd == NULL)
+ fatal("unable to open environment file: %m");
+ (void) saveenv(fd, "IF_AND_SERVICE");
+ (void) saveenv(fd, "SERVICE_NAME");
+ (void) saveenv(fd, "AC_NAME");
+ (void) saveenv(fd, "AC_MAC");
+ (void) saveenv(fd, "SESSION_ID");
+ for (i = 1; ; i++) {
+ slprintf(envname, sizeof (envname), "VENDOR_SPECIFIC_%d", i);
+ if (saveenv(fd, envname) <= 0)
+ break;
+ }
+ (void) fclose(fd);
+}
+
+void
+plugin_init(void)
+{
+ if (absmax_mtu > 1492)
+ absmax_mtu = 1492;
+ if (absmax_mru > 1492)
+ absmax_mru = 1492;
+ old_check_options = check_options_hook;
+ check_options_hook = pppoe_check_options;
+ old_updown_script = updown_script_hook;
+ updown_script_hook = pppoe_updown_script;
+ old_sys_read_packet = sys_read_packet_hook;
+ sys_read_packet_hook = pppoe_sys_read_packet;
+ device_pipe_hook = pppoe_device_pipe;
+ already_ppp = 1;
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppd/pppd.h b/usr/src/cmd/cmd-inet/usr.bin/pppd/pppd.h
new file mode 100644
index 0000000000..01e263529e
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppd/pppd.h
@@ -0,0 +1,940 @@
+/*
+ * pppd.h - PPP daemon global declarations.
+ *
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies.
+ *
+ * SUN MAKES NO REPRESENTATION OR WARRANTIES ABOUT THE SUITABILITY OF
+ * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+ * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
+ * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
+ * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: pppd.h,v 1.54 2000/04/15 10:10:25 paulus Exp $
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifndef __PPPD_H__
+#define __PPPD_H__
+
+#include <stdio.h> /* for FILE */
+#include <limits.h> /* for NGROUPS_MAX */
+#include <sys/param.h> /* for MAXPATHLEN and BSD4_4, if defined */
+#include <sys/types.h> /* for u_int32_t, if defined */
+#include <sys/time.h> /* for struct timeval */
+#include <net/ppp_defs.h>
+
+#if defined(__STDC__)
+#include <stdarg.h>
+#define __V(x) x
+#else
+#include <varargs.h>
+#define __V(x) (va_alist) va_dcl
+#define const
+#define volatile
+#endif /* __STDC__ */
+
+#ifdef INET6
+#include "eui64.h"
+#endif /* INET6 */
+
+#ifdef HAVE_MULTILINK
+#include "tdb.h"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Limits.
+ */
+#define NUM_PPP 1 /* One PPP interface supported (per process) */
+#define MAXWORDLEN 1024 /* max length of word in file (incl null) */
+#define MAXARGS 1 /* max # args to a command */
+#define MAXNAMELEN 256 /* max length of name for auth */
+#define MAXSECRETLEN 256 /* max length of password or secret */
+
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN MAXNAMELEN /* max length of hostname */
+#endif /* MAXHOSTNAMELEN */
+
+/*
+ * If this evaluates non-zero, then sifup() must be called before
+ * sifaddr().
+ */
+#define SIFUPFIRST \
+ (defined(SVR4) && (defined(SNI) || defined(__USLC__)))
+
+/*
+ * If this evaluates non-zero, then sif6up() must be called before
+ * sif6addr().
+ */
+#define SIF6UPFIRST \
+ (defined(__linux__) || (defined(SVR4) && (defined(SNI) || defined(__USLC__))))
+
+/*
+ * Option descriptor structure.
+ */
+typedef unsigned char bool;
+
+enum opt_type {
+ o_special_noarg = 0,
+ o_special = 1,
+ o_bool,
+ o_int,
+ o_uint32,
+ o_string
+};
+
+typedef struct {
+ char *name; /* name of the option */
+ enum opt_type type;
+ void *addr;
+ char *description;
+ int flags;
+ void *addr2;
+ int upper_limit;
+ int lower_limit;
+} option_t;
+
+/*
+ * Values for flags.
+ */
+#define OPT_VALUE 0xff /* mask for presupplied value */
+#define OPT_HEX 0x100 /* int option is in hex */
+#define OPT_NOARG 0x200 /* option doesn't take argument */
+#define OPT_OR 0x400 /* OR in argument to value */
+#define OPT_INC 0x800 /* increment value */
+#define OPT_PRIV 0x1000 /* privileged option */
+#define OPT_STATIC 0x2000 /* string option goes into static array */
+#define OPT_LLIMIT 0x4000 /* check value against lower limit */
+#define OPT_ULIMIT 0x8000 /* check value against upper limit */
+#define OPT_LIMITS (OPT_LLIMIT|OPT_ULIMIT)
+#define OPT_ZEROOK 0x10000 /* 0 value is OK even if not within limits */
+#define OPT_NOINCR 0x20000 /* value mustn't be increased */
+#define OPT_ZEROINF 0x40000 /* with OPT_NOINCR, 0 == infinity */
+#define OPT_DISABLE 0x80000 /* ignore option */
+#define OPT_A2INFO 0x100000 /* addr2 -> option_info to update */
+#define OPT_A2COPY 0x200000 /* addr2 -> second location to rcv value */
+#define OPT_ENABLE 0x400000 /* use *addr2 as enable for option */
+#define OPT_PRIVFIX 0x800000 /* can't be overridden if noauth */
+#define OPT_PREPASS 0x1000000 /* do this opt in pre-pass to find device */
+#define OPT_INITONLY 0x2000000 /* option can only be set in init phase */
+#define OPT_DEVEQUIV 0x4000000 /* equiv to device name */
+#define OPT_DEVNAM (OPT_PREPASS | OPT_INITONLY | OPT_DEVEQUIV)
+
+#define OPT_VAL(x) ((x) & OPT_VALUE)
+
+#ifndef GIDSET_TYPE
+#define GIDSET_TYPE gid_t
+#endif /* GIDSET_TYPE */
+
+/*
+ * Structure representing a list of permitted IP addresses.
+ */
+struct permitted_ip {
+ int permit; /* 1 = permit, 0 = forbid */
+ u_int32_t base; /* match if (addr & mask) == base */
+ u_int32_t mask; /* base and mask are in network byte order */
+};
+
+/*
+ * Unfortunately, the linux kernel driver uses a different structure
+ * for statistics from the rest of the ports.
+ * This structure serves as a common representation for the bits
+ * pppd needs.
+ */
+struct pppd_stats {
+ ppp_counter_t bytes_in;
+ ppp_counter_t bytes_out;
+ ppp_counter_t pkts_in;
+ ppp_counter_t pkts_out;
+};
+
+/*
+ * Used for storing a sequence of words. Usually malloced.
+ */
+struct wordlist {
+ struct wordlist *next;
+ char *word;
+};
+
+/*
+ * Global variables.
+ */
+extern bool hungup; /* Physical layer has disconnected */
+extern int ifunit; /* Interface unit number */
+extern char ifname[32]; /* Interface name */
+extern int ttyfd; /* Serial device file descriptor */
+extern char hostname[]; /* Our hostname */
+extern u_char outpacket_buf[]; /* Buffer for outgoing packets */
+extern int phase; /* Current state of link - see values below */
+extern int baud_rate; /* Current link speed in bits/sec */
+extern char *progname; /* Name of this program */
+extern int redirect_stderr;/* Connector's stderr should go to file */
+extern char peer_authname[];/* Authenticated name of peer */
+extern bool privileged; /* We were run by real-uid root */
+extern bool need_holdoff; /* Need holdoff period after link terminates */
+extern char **script_env; /* Environment variables for scripts */
+extern bool detached; /* Have detached from controlling tty */
+extern GIDSET_TYPE groups[NGROUPS_MAX]; /* groups the user is in */
+extern int ngroups; /* How many groups valid in groups */
+extern struct pppd_stats link_stats; /* byte/packet counts etc. for link */
+extern bool link_stats_valid; /* set if link_stats is valid */
+extern int link_connect_time; /* time the link was up for */
+extern int using_pty; /* using pty as device (notty or pty opt.) */
+extern int log_to_fd; /* logging to this fd as well as syslog */
+extern bool log_to_file; /* log_to_fd is a file */
+extern bool log_to_specific_fd; /* log_to_fd was specified by user */
+extern char *no_ppp_msg; /* message to print if ppp not in kernel */
+extern volatile int status; /* exit status for pppd */
+extern int devnam_fixed; /* can no longer change devnam */
+extern int unsuccess; /* # unsuccessful connection attempts */
+extern int do_callback; /* set if we want to do callback next */
+extern int doing_callback; /* set if this is a callback */
+extern u_char nak_buffer[]; /* where we construct a nak packet */
+extern u_char inpacket_buf[]; /* buffer for incoming packet */
+extern bool direct_tty; /* use standard input directly; not a tty */
+extern int absmax_mru; /* absolute maximum link (not i/f) MRU */
+extern int absmax_mtu; /* absolute maximum link (not i/f) MTU */
+extern int pty_slave; /* slave side of PTY, if any */
+extern bool early_log; /* avoid logging to stdout */
+
+/*
+ * Values for do_callback and doing_callback.
+ */
+#define CALLBACK_DIALIN 1 /* we are expecting the call back */
+#define CALLBACK_DIALOUT 2 /* we are dialling out to call back */
+
+/*
+ * Variables set by command-line options.
+ */
+extern int debug; /* Debug flag */
+extern int kdebugflag; /* Tell kernel to print debug messages */
+extern int default_device; /* Using /dev/tty or equivalent */
+extern char devnam[MAXPATHLEN]; /* Device name */
+extern char ppp_devnam[MAXPATHLEN]; /* Device name (might be pty) */
+extern int crtscts; /* Use hardware flow control */
+extern bool modem; /* Use modem control lines */
+extern int inspeed; /* Input/Output speed requested */
+extern u_int32_t netmask; /* IP netmask to set on interface */
+extern bool lockflag; /* Create lock file to lock the serial dev */
+extern bool nodetach; /* Don't detach from controlling tty */
+extern bool updetach; /* Detach from controlling tty when link up */
+extern char *initializer; /* Script to initialize physical link */
+extern char *connect_script; /* Script to establish physical link */
+extern char *disconnect_script; /* Script to disestablish physical link */
+extern char *welcomer; /* Script to welcome client after connection */
+extern char *ptycommand; /* Command to run on other side of pty */
+extern int maxconnect; /* Maximum connect time (seconds) */
+extern char user[MAXNAMELEN];/* Our name for authenticating ourselves */
+extern char passwd[MAXSECRETLEN]; /* Password for PAP or CHAP */
+extern bool auth_required; /* Peer is required to authenticate */
+extern bool persist; /* Reopen link after it goes down */
+extern bool uselogin; /* Use /etc/passwd for checking PAP */
+extern char our_name[MAXNAMELEN];/* Our name for authentication purposes */
+extern char remote_name[MAXNAMELEN]; /* Peer's name for authentication */
+extern bool explicit_remote;/* remote_name specified with remotename opt */
+extern bool demand; /* Do dial-on-demand */
+extern char *ipparam; /* Extra parameter for ip up/down scripts */
+extern bool cryptpap; /* Others' PAP passwords are encrypted */
+extern int idle_time_limit;/* Shut down link if idle for this long */
+extern int holdoff; /* Dead time before restarting */
+extern bool holdoff_specified; /* true if user gave a holdoff value */
+extern bool notty; /* Stdin/out is not a tty */
+extern char *pty_socket; /* Socket to connect to pty */
+extern char *record_file; /* File to record chars sent/received */
+extern bool sync_serial; /* Device is synchronous serial device */
+extern int maxfail; /* Max # of unsuccessful connection attempts */
+extern char linkname[MAXPATHLEN]; /* logical name for link */
+extern bool tune_kernel; /* May alter kernel settings as necessary */
+extern int connect_delay; /* Time to delay after connect script */
+extern int max_data_rate; /* max bytes/sec through charshunt */
+extern int req_unit; /* interface unit number to use */
+extern bool multilink; /* enable multilink operation */
+extern bool noendpoint; /* don't send or accept endpt. discrim. */
+extern char *bundle_name; /* bundle name for multilink */
+
+#ifdef HAVE_MULTILINK
+extern TDB_CONTEXT *pppdb; /* handle to multilink database context */
+extern char db_key[]; /* multilink database key */
+#endif
+
+#ifdef PPP_FILTER
+extern struct bpf_program pass_filter; /* Filter for pkts to pass */
+extern struct bpf_program active_filter; /* Filter for link-active pkts */
+#endif /* PPP_FILTER */
+
+#ifdef MSLANMAN
+extern bool ms_lanman; /* Use LanMan password instead of NT */
+ /* Has meaning only with MS-CHAP challenges */
+#endif /* MXLANMAN */
+
+extern char *current_option; /* the name of the option being parsed */
+extern bool privileged_option; /* set iff the current option came from root */
+extern char *option_source; /* string saying where the option came from */
+extern int option_line; /* and from which line in the file */
+extern bool already_ppp; /* device is already in PPP mode */
+extern bool prepass; /* Doing pre-pass to find device name */
+extern struct stat devstat; /* Result of stat() on device */
+
+extern bool peer_nak_auth; /* Peer sent nak for our auth request */
+extern u_short nak_auth_orig; /* Auth proto peer naked */
+extern u_short nak_auth_proto; /* Auth proto peer suggested instead */
+extern bool unsolicited_nak_auth; /* Peer asked us to authenticate */
+extern u_short unsolicit_auth_proto; /* Auth proto peer wants */
+extern bool peer_reject_auth; /* Peer sent reject for auth */
+extern u_short reject_auth_proto; /* Protocol that peer rejected */
+extern bool rejected_peers_auth; /* We sent a reject to the peer */
+extern u_short rejected_auth_proto; /* Protocol that peer wanted to use */
+extern bool naked_peers_auth; /* We sent a nak to the peer */
+extern u_short naked_auth_orig; /* Protocol that we wanted to use */
+extern u_short naked_auth_proto; /* Protocol that peer wants us to use */
+
+/*
+ * Values for phase.
+ */
+#define PHASE_DEAD 0 /* RFC 1661; link terminated */
+#define PHASE_INITIALIZE 1 /* execution begins */
+#define PHASE_INITIALIZED 2 /* options ok; entering main loop */
+#define PHASE_SERIALCONN 3 /* connecting to peer */
+#define PHASE_CONNECTED 4 /* connecting to peer */
+#define PHASE_DORMANT 5 /* waiting for demand-dial trigger */
+#define PHASE_ESTABLISH 6 /* RFC 1661; LCP negotiation begins */
+#define PHASE_AUTHENTICATE 7 /* RFC 1661; authentication begins */
+#define PHASE_CALLBACK 8 /* negotiating for callback */
+#define PHASE_NETWORK 9 /* RFC 1661; NCP negotiation begins */
+#define PHASE_RUNNING 10 /* first NCP went to Opened state */
+#define PHASE_TERMINATE 11 /* RFC 1661; LCP left Opened state */
+#define PHASE_DISCONNECT 12 /* running disconnect script */
+#define PHASE_HOLDOFF 13 /* waiting before restart */
+#define PHASE_CALLINGBACK 14 /* calling back */
+#define PHASE_EXIT 15 /* execution ends */
+
+#define PHASE__NAMES \
+ "Dead", "Initialize", "Initialized", "Serialconn", "Connected", \
+ "Dormant", "Establish", "Authenticate", "Callback", "Network", \
+ "Running", "Terminate", "Disconnect", "Holdoff", "Callingback", \
+ "Exit"
+
+/*
+ * The following struct gives the addresses of procedures to call
+ * for a particular protocol.
+ */
+struct protent {
+ u_short protocol; /* PPP protocol number */
+ /* Initialization procedure */
+ void (*init) __P((int unit));
+ /* Process a received packet */
+ void (*input) __P((int unit, u_char *pkt, int len));
+ /* Process a received protocol-reject */
+ void (*protrej) __P((int unit));
+ /* Lower layer has come up */
+ void (*lowerup) __P((int unit));
+ /* Lower layer has gone down */
+ void (*lowerdown) __P((int unit));
+ /* Open the protocol */
+ void (*open) __P((int unit));
+ /* Close the protocol */
+ void (*close) __P((int unit, char *reason));
+ /* Print a packet in readable form */
+ int (*printpkt) __P((u_char *pkt, int len,
+ void (*printer) __P((void *, const char *, ...)),
+ void *arg));
+ /* Process a received data packet */
+ void (*datainput) __P((int unit, u_char *pkt, int len));
+ bool enabled_flag; /* 0 iff protocol is disabled */
+ char *name; /* Text name of protocol */
+ char *data_name; /* Text name of corresponding data protocol */
+ option_t *options; /* List of command-line options */
+ /* Check requested options, assign defaults */
+ void (*check_options) __P((void));
+ /* Configure interface for demand-dial */
+ int (*demand_conf) __P((int unit));
+ /* Say whether to bring up link for this pkt */
+ int (*active_pkt) __P((u_char *pkt, int len));
+ /* Print current status to file or syslog (if strptr == NULL) */
+ void (*print_stat) __P((int unit, FILE *strptr));
+};
+
+/*
+ * This structure is used to store information about certain
+ * options, such as where the option value came from (/etc/ppp/options,
+ * command line, etc.) and whether it came from a privileged source.
+ */
+struct option_info {
+ bool priv; /* was value set by sysadmin? */
+ char *source; /* where option came from */
+ int line; /* line number where the option came from */
+};
+
+extern struct option_info devnam_info;
+extern struct option_info initializer_info;
+extern struct option_info connect_script_info;
+extern struct option_info disconnect_script_info;
+extern struct option_info welcomer_info;
+extern struct option_info ptycommand_info;
+extern struct option_info ipsrc_info;
+extern struct option_info ipdst_info;
+extern struct option_info speed_info;
+
+/*
+ * Table of pointers to supported protocols.
+ */
+extern struct protent *protocols[];
+
+/*
+ * Prototypes.
+ */
+
+/*
+ * Procedures exported from main.c.
+ */
+extern void set_ifunit __P((int)); /* set stuff that depends on ifunit */
+extern void detach __P((void)); /* Detach from controlling tty */
+extern void die __P((int)); /* Cleanup and exit */
+extern void quit __P((void)); /* like die(1) */
+extern void novm __P((char *)); /* Say we ran out of memory, and die */
+extern void timeout __P((void (*func)(void *), void *arg, int t));
+ /* Call func(arg) after t seconds */
+extern void untimeout __P((void (*func)(void *), void *arg));
+ /* Cancel call to func(arg) */
+extern pid_t run_program __P((char *prog, char **args, int must_exist,
+ void (*done)(void *, int), void *arg));
+ /* Run program prog with args in child */
+extern void reopen_log __P((void)); /* (re)open the connection to syslog */
+extern void update_link_stats __P((int)); /* Get stats at link termination */
+/* set script env var */
+extern void script_setenv __P((const char *, const char *, int));
+extern void script_unsetenv __P((const char *)); /* unset script env var */
+extern const char *script_getenv __P((const char *var));
+extern void new_phase __P((int)); /* signal start of new phase */
+extern void print_ncpstate __P((int, FILE *)); /* prints NCP state */
+extern const char *protocol_name __P((int proto)); /* canonical name */
+extern const char *phase_name __P((int phaseval));
+
+/*
+ * Procedures exported from utils.c.
+ */
+extern void log_packet __P((u_char *, int, const char *, int));
+ /* Format a packet and log it with syslog */
+extern void print_string __P((char *, int, void (*)(void *, const char *, ...),
+ void *)); /* Format a string for output */
+extern int slprintf __P((char *, int, const char *, ...)); /* sprintf++ */
+extern int vslprintf __P((char *, int, const char *, va_list));/* vsprintf++ */
+extern size_t strlcpy __P((char *, const char *, size_t)); /* safe strcpy */
+extern size_t strlcat __P((char *, const char *, size_t)); /* safe strncpy */
+extern void dbglog __P((const char *, ...));/* log a debug message */
+extern void info __P((const char *, ...)); /* log an informational message */
+extern void notice __P((const char *, ...));/* log a notice-level message */
+extern void warn __P((const char *, ...)); /* log a warning message */
+extern void error __P((const char *, ...)); /* log an error message */
+extern void fatal __P((const char *, ...));
+ /* log an error message and die(1) */
+extern const char *code_name __P((int code, int shortflag));
+ /* Code to string */
+extern int flprintf __P((FILE *, const char *, ...)); /* fprintf++ */
+extern size_t strllen __P((const char *, size_t)); /* safe strlen */
+extern const char *signal_name __P((int signum));
+
+/*
+ * Procedures exported from auth.c
+ */
+extern void link_required __P((int)); /* we are starting to use the link */
+extern void link_terminated __P((int)); /* we are finished with the link */
+extern void link_down __P((int));
+ /* the LCP layer has left the Opened state */
+extern void link_established __P((int)); /* the link is up; authenticate now */
+extern void start_networks __P((void));
+ /* start all the network control protos */
+extern void np_up __P((int, int)); /* a network protocol has come up */
+extern void np_down __P((int, int)); /* a network protocol has gone down */
+extern void np_finished __P((int, int));
+ /* a network protocol no longer needs link */
+extern void auth_peer_fail __P((int, int));
+ /* peer failed to authenticate itself */
+extern void auth_peer_success __P((int, int, char *, int));
+ /* peer successfully authenticated itself */
+extern void auth_withpeer_fail __P((int, int));
+ /* we failed to authenticate ourselves */
+extern void auth_withpeer_success __P((int, int));
+ /* we successfully authenticated ourselves */
+extern void auth_check_options __P((void));
+ /* check authentication options supplied */
+extern void auth_reset __P((int));
+ /* check what secrets we have */
+extern int check_passwd __P((int, char *, int, char *, int, char **));
+ /* Check peer-supplied username/password */
+extern int get_secret __P((int, char *, char *, char *, int *, int));
+ /* get "secret" for chap */
+extern int auth_ip_addr __P((int, u_int32_t));
+ /* check if IP address is authorized */
+extern int bad_ip_adrs __P((u_int32_t));
+ /* check if IP address is unreasonable */
+
+/*
+ * Procedures exported from demand.c
+ */
+extern void demand_conf __P((void));
+ /* config interface(s) for demand-dial */
+extern void demand_block __P((void)); /* set all NPs to queue up packets */
+extern void demand_unblock __P((void)); /* set all NPs to pass packets */
+extern void demand_discard __P((void)); /* set all NPs to discard packets */
+extern void demand_rexmit __P((int)); /* retransmit saved frames for an NP */
+extern int loop_chars __P((unsigned char *, int));
+ /* process chars from loopback */
+extern int loop_frame __P((unsigned char *, int));
+ /* should we bring link up? */
+
+/*
+ * Procedures exported from multilink.c
+ */
+extern void mp_check_options __P((void)); /* Check multilink-related options */
+extern int mp_join_bundle __P((void));
+ /* join our link to an appropriate bundle */
+/*
+ * Procedures exported from sys-*.c
+ */
+extern void sys_init __P((bool)); /* Do system-dependent initialization */
+extern void sys_cleanup __P((void)); /* Restore system state before exiting */
+extern int sys_check_options __P((void)); /* Check options specified */
+extern void sys_options __P((void)); /* add or remove system options */
+extern void sys_close __P((void)); /* Clean up in a child before execing */
+extern int ppp_available __P((void));
+ /* Test whether ppp kernel support exists */
+extern int get_pty __P((int *, int *, char *, int));
+ /* Get pty master/slave */
+extern int open_ppp_loopback __P((void));
+ /* Open loopback for demand-dialling */
+extern int establish_ppp __P((int));
+ /* Turn serial port into a ppp interface */
+extern void restore_loop __P((void));
+ /* Transfer ppp unit back to loopback */
+extern void disestablish_ppp __P((int));
+ /* Restore port to normal operation */
+extern void make_new_bundle __P((int, int, int, int)); /* Create new bundle */
+extern int bundle_attach __P((int)); /* Attach link to existing bundle */
+extern void cfg_bundle __P((int, int, int, int));
+ /* Configure existing bundle */
+extern void clean_check __P((void)); /* Check if line was 8-bit clean */
+extern void set_up_tty __P((int, int));
+ /* Set up port's speed, parameters, etc. */
+extern void restore_tty __P((int)); /* Restore port's original parameters */
+extern void setdtr __P((int, int)); /* Raise or lower port's DTR line */
+extern void output __P((int, u_char *, int)); /* Output a PPP packet */
+extern void wait_input __P((struct timeval *));
+ /* Wait for input, with timeout */
+extern void add_fd __P((int)); /* Add fd to set to wait for */
+extern void remove_fd __P((int)); /* Remove fd from set to wait for */
+extern int read_packet __P((u_char *)); /* Read PPP packet */
+extern int get_loop_output __P((void)); /* Read pkts from loopback */
+extern void ppp_send_config __P((int, int, u_int32_t, int, int));
+ /* Configure i/f transmit parameters */
+extern void ppp_set_xaccm __P((int, ext_accm));
+ /* Set extended transmit ACCM */
+extern void ppp_recv_config __P((int, int, u_int32_t, int, int));
+ /* Configure i/f receive parameters */
+#ifdef NEGOTIATE_FCS
+extern void ppp_send_fcs __P((int unit, int fcstype));
+extern void ppp_recv_fcs __P((int unit, int fcstype));
+#endif /* NEGOTIATE_FCS */
+#ifdef MUX_FRAME
+extern void ppp_send_muxoption __P((int ,u_int32_t));
+extern void ppp_recv_muxoption __P((int ,u_int32_t));
+#endif /* MUX_FRAME */
+extern int ccp_test __P((int, u_char *, int, int));
+ /* Test support for compression scheme */
+#ifdef COMP_TUNE
+extern void ccp_tune __P((int, int)); /* Tune compression effort level */
+#endif /* COMP_TUNE */
+extern void ccp_flags_set __P((int, int, int));
+ /* Set kernel CCP state */
+extern int ccp_fatal_error __P((int));
+ /* Test for fatal decomp error in kernel */
+extern int get_idle_time __P((int, struct ppp_idle *));
+ /* Find out how long link has been idle */
+extern int get_ppp_stats __P((int, struct pppd_stats *));
+ /* Return link statistics */
+extern int sifvjcomp __P((int, int, int, int));
+ /* Configure VJ TCP header compression */
+extern int sifup __P((int)); /* Configure i/f up for one protocol */
+extern int sifnpmode __P((int u, int proto, enum NPmode mode));
+ /* Set mode for handling packets for proto */
+extern int sifdown __P((int)); /* Configure i/f down for one protocol */
+extern int sifaddr __P((int, u_int32_t, u_int32_t, u_int32_t));
+ /* Configure IPv4 addresses for i/f */
+extern int cifaddr __P((int, u_int32_t, u_int32_t));
+ /* Reset i/f IP addresses */
+
+extern void sys_block_proto __P((uint16_t));
+extern void sys_unblock_proto __P((uint16_t));
+
+#ifdef INET6
+extern int sif6addr __P((int, eui64_t, eui64_t));
+ /* Configure IPv6 addresses for i/f */
+extern int cif6addr __P((int, eui64_t, eui64_t));
+ /* Remove an IPv6 address from i/f */
+#endif /* INET6 */
+extern int sifdefaultroute __P((int, u_int32_t, u_int32_t));
+ /* Create default route through i/f */
+extern int cifdefaultroute __P((int, u_int32_t, u_int32_t));
+ /* Delete default route through i/f */
+extern int sifproxyarp __P((int unit, u_int32_t addr, int flag));
+ /* Add proxy ARP entry for peer */
+extern int cifproxyarp __P((int unit, u_int32_t addr));
+ /* Delete proxy ARP entry for peer */
+extern u_int32_t GetMask __P((u_int32_t));
+ /* Get appropriate netmask for address */
+extern int lock __P((char *)); /* Create lock file for device */
+extern int relock __P((int)); /* Rewrite lock file with new pid */
+extern void unlock __P((void)); /* Delete previously-created lock file */
+extern void logwtmp __P((const char *, const char *, const char *));
+ /* Write entry to wtmp file */
+extern int get_host_seed __P((void));
+ /* Get host-dependent random number seed */
+extern int have_route_to __P((u_int32_t)); /* Check if route to addr exists */
+#ifdef PPP_FILTER
+extern int set_filters __P((struct bpf_program *pass,
+ struct bpf_program *active));
+ /* Set filter programs in kernel */
+#endif /* PPP_FILTER */
+#ifdef IPX_CHANGE
+extern int sipxfaddr __P((int, unsigned long, unsigned char *));
+extern int cipxfaddr __P((int));
+#endif /* IPX_CHANGE */
+extern int get_if_hwaddr __P((u_char *addr, int msize, char *name));
+extern int get_first_hwaddr __P((u_char *addr, int msize));
+#if defined(INET6) && defined(SOL2)
+extern int ether_to_eui64 __P((eui64_t *p_eui64));
+extern int sif6up __P((int unit));
+extern int sif6down __P((int unit));
+extern int sif6mtu __P((int mtu));
+extern int sif6flags __P((u_int32_t flags, int set));
+#endif /* INET6 && SOL2*/
+#if defined(INET6) && !defined(SOL2)
+#define sif6up sifup
+#endif
+extern int sifmtu __P((int mtu));
+extern int siflags __P((u_int32_t flags, int set));
+extern void sys_ifname __P((void));
+extern void sys_print_state __P((FILE *strptr));
+extern int sys_extra_fd __P((void));
+
+/*
+ * Procedures exported from options.c
+ */
+extern int parse_args __P((int argc, char **argv));
+ /* Parse options from arguments given */
+extern int options_from_file __P((char *filename, bool must_exist,
+ bool check_prot, bool privileged));
+ /* Parse options from an options file */
+extern int options_from_user __P((void));
+ /* Parse options from user's .ppprc */
+extern int options_for_tty __P((void));
+ /* Parse options from /etc/ppp/options.tty */
+extern int options_from_list __P((struct wordlist *, bool privileged));
+ /* Parse options from a wordlist */
+extern int getword __P((FILE *f, char *word, int *newlinep, char *filename));
+ /* Read a word from a file */
+extern void option_error __P((char *fmt, ...));
+ /* Print an error message about an option */
+extern int int_option __P((char *, int *));
+ /* Simplified number_option for decimal ints */
+extern void add_options __P((option_t *));
+ /* Add extra options */
+extern int parse_dotted_ip __P((char *, u_int32_t *));
+ /* Parse dotted IP notation */
+extern option_t *remove_option __P((char *));
+ /* Remove (disable) an option */
+extern void save_source __P((struct option_info *));
+ /* Save the source information (where an option comes from) */
+extern void set_source __P((struct option_info *));
+ /* Set the source (for logging option errors detected after parsing) */
+extern const char *name_source __P((struct option_info *));
+ /* Return a string containing the option source and line number */
+
+/*
+ * Hooks to enable plugins to change various things.
+ */
+extern int (*new_phase_hook) __P((int new, int old));
+extern int (*idle_time_hook) __P((struct ppp_idle *));
+extern int (*holdoff_hook) __P((void));
+extern int (*pap_check_hook) __P((void));
+extern int (*pap_auth_hook) __P((char *user, char *passwd, char **msgp,
+ struct wordlist **paddrs, struct wordlist **popts));
+extern void (*pap_logout_hook) __P((void));
+extern int (*pap_passwd_hook) __P((char *user, char *passwd));
+extern void (*ip_up_hook) __P((void));
+extern void (*ip_down_hook) __P((void));
+extern int (*check_options_hook) __P((uid_t uid));
+/* extern int (*attach_device_hook) __P((uid_t uid, char *devnam)); */
+extern int (*updown_script_hook) __P((const char ***argsp));
+struct strbuf; /* forward declaration */
+extern int (*sys_read_packet_hook) __P((int retv, struct strbuf *ctrl,
+ struct strbuf *data, int flags));
+extern void (*device_pipe_hook) __P((int pipefd));
+
+/*
+ * Inline versions of get/put char/short/long.
+ * Pointer is advanced; we assume that both arguments
+ * are lvalues and will already be in registers.
+ * cp MUST be u_char *.
+ */
+#define GETCHAR(c, cp) { \
+ (c) = *(cp)++; \
+}
+#define PUTCHAR(c, cp) { \
+ *(cp)++ = (u_char) (c); \
+}
+
+
+#define GETSHORT(s, cp) { \
+ (s) = *(cp)++ << 8; \
+ (s) |= *(cp)++; \
+}
+#define PUTSHORT(s, cp) { \
+ *(cp)++ = (u_char) ((s) >> 8); \
+ *(cp)++ = (u_char) (s); \
+}
+
+#define GETLONG(l, cp) { \
+ (l) = *(cp)++ << 8; \
+ (l) |= *(cp)++; (l) <<= 8; \
+ (l) |= *(cp)++; (l) <<= 8; \
+ (l) |= *(cp)++; \
+}
+#define PUTLONG(l, cp) { \
+ *(cp)++ = (u_char) ((l) >> 24); \
+ *(cp)++ = (u_char) ((l) >> 16); \
+ *(cp)++ = (u_char) ((l) >> 8); \
+ *(cp)++ = (u_char) (l); \
+}
+
+/*
+ * For values that are kept internally in network byte order.
+ */
+#define GETNLONG(l, cp) { \
+ u_int32_t getnlong_val; \
+ getnlong_val = *(cp)++ << 8; \
+ getnlong_val |= *(cp)++; getnlong_val <<= 8; \
+ getnlong_val |= *(cp)++; getnlong_val <<= 8; \
+ getnlong_val |= *(cp)++; \
+ (l) = htonl(getnlong_val); \
+}
+#define PUTNLONG(l, cp) { \
+ u_int32_t putnlong_val = ntohl(l); \
+ *(cp)++ = (u_char) (putnlong_val >> 24); \
+ *(cp)++ = (u_char) (putnlong_val >> 16); \
+ *(cp)++ = (u_char) (putnlong_val >> 8); \
+ *(cp)++ = (u_char) putnlong_val; \
+}
+
+#define INCPTR(n, cp) ((cp) += (n))
+#define DECPTR(n, cp) ((cp) -= (n))
+
+/*
+ * System dependent definitions for user-level 4.3BSD UNIX implementation.
+ */
+
+#define TIMEOUT(r, f, t) timeout((r), (f), (t))
+#define UNTIMEOUT(r, f) untimeout((r), (f))
+
+#ifndef SOL2
+#define BCOPY(s, d, l) memcpy(d, s, l)
+#define BZERO(s, n) memset(s, 0, n)
+#else
+#include <strings.h>
+#define BCOPY bcopy
+#define BZERO bzero
+#endif
+
+#define PRINTMSG(m, l) { info("Remote message: %0.*v", l, m); }
+
+/*
+ * MAKEHEADER - Add Header fields to a packet.
+ */
+#define MAKEHEADER(p, t) { \
+ PUTCHAR(PPP_ALLSTATIONS, p); \
+ PUTCHAR(PPP_UI, p); \
+ PUTSHORT(t, p); }
+
+/*
+ * Exit status values.
+ */
+#define EXIT_OK 0
+#define EXIT_FATAL_ERROR 1
+#define EXIT_OPTION_ERROR 2
+#define EXIT_NOT_ROOT 3
+#define EXIT_NO_KERNEL_SUPPORT 4
+#define EXIT_USER_REQUEST 5
+#define EXIT_LOCK_FAILED 6
+#define EXIT_OPEN_FAILED 7
+#define EXIT_CONNECT_FAILED 8
+#define EXIT_PTYCMD_FAILED 9
+#define EXIT_NEGOTIATION_FAILED 10
+#define EXIT_PEER_AUTH_FAILED 11
+#define EXIT_IDLE_TIMEOUT 12
+#define EXIT_CONNECT_TIME 13
+#define EXIT_CALLBACK 14
+#define EXIT_PEER_DEAD 15
+#define EXIT_HANGUP 16
+#define EXIT_LOOPBACK 17
+#define EXIT_INIT_FAILED 18
+#define EXIT_AUTH_TOPEER_FAILED 19
+
+/*
+ * Character shunt constants.
+ */
+#define MAXLEVELMINSIZE 100
+
+/*
+ * Debug macros. Slightly useful for finding bugs in pppd, not particularly
+ * useful for finding out why your connection isn't being established.
+ */
+#ifdef DEBUGALL
+#define DEBUGMAIN 1
+#define DEBUGSYS 1
+#define DEBUGLCP 1
+#define DEBUGIPCP 1
+#define DEBUGIPV6CP 1
+#define DEBUGCHAP 1
+#define DEBUGIPXCP 1
+#define LOG_PPP LOG_LOCAL2 /* Log here when debugging all */
+#endif /* DEBUGALL */
+
+#ifndef LOG_PPP /* we use LOG_DAEMON for syslog by default */
+#define LOG_PPP LOG_DAEMON
+#endif /* LOG_PPP */
+
+#ifdef DEBUGMAIN
+#define MAINDEBUG(x) if (debug) dbglog x
+#else
+#define MAINDEBUG(x) ((void) 0)
+#endif /* DEBUGMAIN */
+
+#ifdef DEBUGSYS
+#define SYSDEBUG(x) if (debug) dbglog x
+#else
+#define SYSDEBUG(x) ((void) 0)
+#endif /* DEBUGSYS */
+
+#ifdef DEBUGLCP
+#define LCPDEBUG(x) if (debug) dbglog x
+#else
+#define LCPDEBUG(x) ((void) 0)
+#endif /* DEBUGLCP */
+
+#ifdef DEBUGIPCP
+#define IPCPDEBUG(x) if (debug) dbglog x
+#else
+#define IPCPDEBUG(x) ((void) 0)
+#endif /* DEBUGIPCP */
+
+#ifdef DEBUGIPV6CP
+#define IPV6CPDEBUG(x) if (debug) dbglog x
+#else
+#define IPV6CPDEBUG(x) ((void) 0)
+#endif /* DEBUGIPV6CP */
+
+#ifdef DEBUGCHAP
+#define CHAPDEBUG(x) if (debug) dbglog x
+#else
+#define CHAPDEBUG(x) ((void) 0)
+#endif /* DEBUGCHAP */
+
+#ifdef DEBUGIPXCP
+#define IPXCPDEBUG(x) if (debug) dbglog x
+#else
+#define IPXCPDEBUG(x) ((void) 0)
+#endif /* DEBUGIPXCP */
+
+#ifndef SIGTYPE
+#if defined(sun) || defined(SYSV) || defined(POSIX_SOURCE)
+#define SIGTYPE void
+#else
+#define SIGTYPE int
+#endif /* defined(sun) || defined(SYSV) || defined(POSIX_SOURCE) */
+#endif /* SIGTYPE */
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b)? (a): (b))
+#endif /* MIN */
+
+#ifndef MAX
+#define MAX(a, b) ((a) > (b)? (a): (b))
+#endif /* MAX */
+
+#ifndef Dim
+#define Dim(x) (sizeof (x) / sizeof (*(x)))
+#endif /* Dim */
+
+#ifndef NBBY
+#define NBBY 8
+#endif /* NBBY */
+
+#ifndef isset
+#define isset(arr, val) (((u_char *)(arr))[(val)/NBBY] & (1<<((val)%NBBY)))
+#endif /* isset */
+
+#ifndef setbit
+#define setbit(arr, val) (((u_char *)(arr))[(val)/NBBY] |= (1<<((val)%NBBY)))
+#endif /* setbit */
+
+#define IP_HDRLEN 20 /* bytes */
+#define IP_OFFMASK 0x1fff
+#define TCP_HDRLEN 20
+
+/*
+ * We use these macros because the IP header may be at an odd address,
+ * and some compilers might use word loads to get th_off or ip_hl.
+ */
+
+#define net_short(x) (((x)[0] << 8) + (x)[1])
+#define net_long(x) ((net_short(x) << 16) + \
+ net_short((unsigned char *)(x) + 2))
+#define get_ipv(x) ((((unsigned char *)(x))[0] >> 4) & 0xF)
+#define get_iphl(x) (((unsigned char *)(x))[0] & 0xF)
+#define get_iplen(x) net_short((unsigned char *)(x) + 2)
+#define get_ipoff(x) net_short((unsigned char *)(x) + 6)
+#define get_ipproto(x) (((unsigned char *)(x))[9])
+#define get_ipsrc(x) net_long((unsigned char *)(x) + 12)
+#define get_ipdst(x) net_long((unsigned char *)(x) + 16)
+/* Ports for both UDP and TCP are first */
+#define get_sport(x) net_short(x)
+#define get_dport(x) net_short((unsigned char *)(x) + 2)
+#define get_tcpoff(x) (((unsigned char *)(x))[12] >> 4)
+#define get_tcpflags(x) (((unsigned char *)(x))[13])
+
+/* Check for RFC 1918 (local use) addresses */
+#define LOCAL_IP_ADDR(addr) \
+ (((addr) & 0xff000000) == 0x0a000000 || /* 10.x.x.x */ \
+ ((addr) & 0xfff00000) == 0xac100000 || /* 172.16.x.x */ \
+ ((addr) & 0xffff0000) == 0xc0a80000) /* 192.168.x.x */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __PPPD_H__ */
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppd/req.flg b/usr/src/cmd/cmd-inet/usr.bin/pppd/req.flg
new file mode 100644
index 0000000000..c47ddfd6ad
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppd/req.flg
@@ -0,0 +1,7 @@
+#!/bin/sh
+#pragma ident "%Z%%M% %I% %E% SMI"
+# Copyright (c) 2001 by Sun Microsystems, Inc.
+# All rights reserved.
+
+echo_file usr/src/lib/Makefile.lib
+echo_file usr/src/lib/Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppd/sha1.c b/usr/src/cmd/cmd-inet/usr.bin/pppd/sha1.c
new file mode 100644
index 0000000000..64e2dbae64
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppd/sha1.c
@@ -0,0 +1,755 @@
+/*
+ * Copyright (c) 1998-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+/*
+ * The basic framework for this code came from the reference
+ * implementation for MD5. That implementation is Copyright (C)
+ * 1991-2, RSA Data Security, Inc. Created 1991. All rights reserved.
+ *
+ * License to copy and use this software is granted provided that it
+ * is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+ * Algorithm" in all material mentioning or referencing this software
+ * or this function.
+ *
+ * License is also granted to make and use derivative works provided
+ * that such works are identified as "derived from the RSA Data
+ * Security, Inc. MD5 Message-Digest Algorithm" in all material
+ * mentioning or referencing the derived work.
+ *
+ * RSA Data Security, Inc. makes no representations concerning either
+ * the merchantability of this software or the suitability of this
+ * software for any particular purpose. It is provided "as is"
+ * without express or implied warranty of any kind.
+ *
+ * These notices must be retained in any copies of any part of this
+ * documentation and/or software.
+ *
+ * NOTE: Cleaned-up and optimized, version of SHA1, based on the FIPS 180-1
+ * standard, available at http://www.itl.nist.gov/div897/pubs/fip180-1.htm
+ * Not as fast as one would like -- further optimizations are encouraged
+ * and appreciated.
+ */
+
+#include <strings.h>
+#include <sys/types.h>
+#include "sha1.h"
+#include "sha1_consts.h"
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+#define RCSID "$Id: $"
+
+#if !defined(lint) && !defined(_lint)
+static const char rcsid[] = RCSID;
+#endif
+
+static void Encode(uint8_t *, uint32_t *, size_t);
+static void SHA1Transform(uint32_t, uint32_t, uint32_t, uint32_t, uint32_t,
+ SHA1_CTX *, const uint8_t *);
+
+static uint8_t PADDING[64] = { 0x80, /* all zeros */ };
+
+/*
+ * F, G, and H are the basic SHA1 functions.
+ */
+#define F(b, c, d) (((b) & (c)) | ((~b) & (d)))
+#define G(b, c, d) ((b) ^ (c) ^ (d))
+#define H(b, c, d) (((b) & (c)) | ((b) & (d)) | ((c) & (d)))
+
+/*
+ * ROTATE_LEFT rotates x left n bits.
+ */
+#define ROTATE_LEFT(x, n) \
+ (((x) << (n)) | ((x) >> ((sizeof (x) * NBBY)-(n))))
+
+/*
+ * SHA1Init()
+ *
+ * purpose: initializes the sha1 context and begins and sha1 digest operation
+ * input: SHA1_CTX * : the context to initialize.
+ * output: void
+ */
+
+void
+SHA1Init(SHA1_CTX *ctx)
+{
+ ctx->count[0] = ctx->count[1] = 0;
+
+ /*
+ * load magic initialization constants. Tell lint
+ * that these constants are unsigned by using U.
+ */
+
+ ctx->state[0] = 0x67452301U;
+ ctx->state[1] = 0xefcdab89U;
+ ctx->state[2] = 0x98badcfeU;
+ ctx->state[3] = 0x10325476U;
+ ctx->state[4] = 0xc3d2e1f0U;
+}
+
+/*
+ * SHA1Update()
+ *
+ * purpose: continues an sha1 digest operation, using the message block
+ * to update the context.
+ * input: SHA1_CTX * : the context to update
+ * uint8_t * : the message block
+ * uint32_t : the length of the message block in bytes
+ * output: void
+ */
+
+void
+SHA1Update(SHA1_CTX *ctx, const uint8_t *input, uint32_t input_len)
+{
+ uint32_t i, buf_index, buf_len;
+
+ /* compute number of bytes mod 64 */
+ buf_index = (ctx->count[1] >> 3) & 0x3F;
+
+ /* update number of bits */
+ if ((ctx->count[1] += (input_len << 3)) < (input_len << 3))
+ ctx->count[0]++;
+
+ ctx->count[0] += (input_len >> 29);
+
+ buf_len = 64 - buf_index;
+
+ /* transform as many times as possible */
+ i = 0;
+ if (input_len >= buf_len) {
+
+ /*
+ * general optimization:
+ *
+ * only do initial bcopy() and SHA1Transform() if
+ * buf_index != 0. if buf_index == 0, we're just
+ * wasting our time doing the bcopy() since there
+ * wasn't any data left over from a previous call to
+ * SHA1Update().
+ */
+
+ if (buf_index) {
+ bcopy(input, &ctx->buf_un.buf8[buf_index], buf_len);
+
+
+ SHA1Transform(ctx->state[0], ctx->state[1],
+ ctx->state[2], ctx->state[3], ctx->state[4], ctx,
+ ctx->buf_un.buf8);
+
+ i = buf_len;
+ }
+
+ for (; i + 63 < input_len; i += 64)
+ SHA1Transform(ctx->state[0], ctx->state[1],
+ ctx->state[2], ctx->state[3], ctx->state[4],
+ ctx, &input[i]);
+
+ /*
+ * general optimization:
+ *
+ * if i and input_len are the same, return now instead
+ * of calling bcopy(), since the bcopy() in this case
+ * will be an expensive nop.
+ */
+
+ if (input_len == i)
+ return;
+
+ buf_index = 0;
+ }
+
+ /* buffer remaining input */
+ bcopy(&input[i], &ctx->buf_un.buf8[buf_index], input_len - i);
+}
+
+/*
+ * SHA1Final()
+ *
+ * purpose: ends an sha1 digest operation, finalizing the message digest and
+ * zeroing the context.
+ * input: uint8_t * : a buffer to store the digest in
+ * SHA1_CTX * : the context to finalize, save, and zero
+ * output: void
+ */
+
+void
+SHA1Final(uint8_t *digest, SHA1_CTX *ctx)
+{
+ uint8_t bitcount_be[sizeof (ctx->count)];
+ uint32_t index = (ctx->count[1] >> 3) & 0x3f;
+
+ /* store bit count, big endian */
+ Encode(bitcount_be, ctx->count, sizeof (bitcount_be));
+
+ /* pad out to 56 mod 64 */
+ SHA1Update(ctx, PADDING, ((index < 56) ? 56 : 120) - index);
+
+ /* append length (before padding) */
+ SHA1Update(ctx, bitcount_be, sizeof (bitcount_be));
+
+ /* store state in digest */
+ Encode(digest, ctx->state, sizeof (ctx->state));
+
+ /* zeroize sensitive information */
+ bzero(ctx, sizeof (*ctx));
+}
+
+/*
+ * sparc optimization:
+ *
+ * on the sparc, we can load big endian 32-bit data easily. note that
+ * special care must be taken to ensure the address is 32-bit aligned.
+ * in the interest of speed, we don't check to make sure, since
+ * careful programming can guarantee this for us.
+ */
+
+#if defined(__sparc)
+
+#define LOAD_LITTLE_32(addr) (*(uint32_t *)(addr))
+
+#else /* little endian -- will work on big endian, but slowly */
+
+#define LOAD_LITTLE_32(addr) \
+ (((addr)[0] << 24) | ((addr)[1] << 16) | ((addr)[2] << 8) | (addr)[3])
+#endif
+
+/*
+ * sparc register window optimization:
+ *
+ * `a', `b', `c', `d', and `e' are passed into SHA1Transform
+ * explicitly since it increases the number of registers available to
+ * the compiler. under this scheme, these variables can be held in
+ * %i0 - %i4, which leaves more local and out registers available.
+ */
+
+/*
+ * SHA1Transform()
+ *
+ * purpose: sha1 transformation -- updates the digest based on `block'
+ * input: uint32_t : bytes 1 - 4 of the digest
+ * uint32_t : bytes 5 - 8 of the digest
+ * uint32_t : bytes 9 - 12 of the digest
+ * uint32_t : bytes 12 - 16 of the digest
+ * uint32_t : bytes 16 - 20 of the digest
+ * SHA1_CTX * : the context to update
+ * uint8_t [64]: the block to use to update the digest
+ * output: void
+ */
+
+void
+SHA1Transform(uint32_t a, uint32_t b, uint32_t c, uint32_t d, uint32_t e,
+ SHA1_CTX *ctx, const uint8_t blk[64])
+{
+ /*
+ * sparc optimization:
+ *
+ * while it is somewhat counter-intuitive, on sparc, it is
+ * more efficient to place all the constants used in this
+ * function in an array and load the values out of the array
+ * than to manually load the constants. this is because
+ * setting a register to a 32-bit value takes two ops in most
+ * cases: a `sethi' and an `or', but loading a 32-bit value
+ * from memory only takes one `ld' (or `lduw' on v9). while
+ * this increases memory usage, the compiler can find enough
+ * other things to do while waiting to keep the pipeline does
+ * not stall. additionally, it is likely that many of these
+ * constants are cached so that later accesses do not even go
+ * out to the bus.
+ *
+ * this array is declared `static' to keep the compiler from
+ * having to bcopy() this array onto the stack frame of
+ * SHA1Transform() each time it is called -- which is
+ * unacceptably expensive.
+ *
+ * the `const' is to ensure that callers are good citizens and
+ * do not try to munge the array. since these routines are
+ * going to be called from inside multithreaded kernelland,
+ * this is a good safety check. -- `sha1_consts' will end up in
+ * .rodata.
+ *
+ * unfortunately, loading from an array in this manner hurts
+ * performance under intel. so, there is a macro,
+ * SHA1_CONST(), used in SHA1Transform(), that either expands to
+ * a reference to this array, or to the actual constant,
+ * depending on what platform this code is compiled for.
+ */
+
+#if defined(__sparc)
+ static const uint32_t sha1_consts[] = {
+ SHA1_CONST_0, SHA1_CONST_1, SHA1_CONST_2, SHA1_CONST_3,
+ };
+#endif
+
+ /*
+ * general optimization:
+ *
+ * use individual integers instead of using an array. this is a
+ * win, although the amount it wins by seems to vary quite a bit.
+ */
+
+ uint32_t w_0, w_1, w_2, w_3, w_4, w_5, w_6, w_7;
+ uint32_t w_8, w_9, w_10, w_11, w_12, w_13, w_14, w_15;
+
+ /*
+ * sparc optimization:
+ *
+ * if `block' is already aligned on a 4-byte boundary, use
+ * LOAD_LITTLE_32() directly. otherwise, bcopy() into a
+ * buffer that *is* aligned on a 4-byte boundary and then do
+ * the LOAD_LITTLE_32() on that buffer. benchmarks have shown
+ * that using the bcopy() is better than loading the bytes
+ * individually and doing the endian-swap by hand.
+ *
+ * even though it's quite tempting to assign to do:
+ *
+ * blk = bcopy(ctx->buf_un.buf32, blk, sizeof (ctx->buf_un.buf32));
+ *
+ * and only have one set of LOAD_LITTLE_32()'s, the compiler
+ * *does not* like that, so please resist the urge.
+ */
+
+#if defined(__sparc)
+ if ((uintptr_t)blk & 0x3) { /* not 4-byte aligned? */
+ bcopy(blk, ctx->buf_un.buf32, sizeof (ctx->buf_un.buf32));
+ w_15 = LOAD_LITTLE_32(ctx->buf_un.buf32 + 15);
+ w_14 = LOAD_LITTLE_32(ctx->buf_un.buf32 + 14);
+ w_13 = LOAD_LITTLE_32(ctx->buf_un.buf32 + 13);
+ w_12 = LOAD_LITTLE_32(ctx->buf_un.buf32 + 12);
+ w_11 = LOAD_LITTLE_32(ctx->buf_un.buf32 + 11);
+ w_10 = LOAD_LITTLE_32(ctx->buf_un.buf32 + 10);
+ w_9 = LOAD_LITTLE_32(ctx->buf_un.buf32 + 9);
+ w_8 = LOAD_LITTLE_32(ctx->buf_un.buf32 + 8);
+ w_7 = LOAD_LITTLE_32(ctx->buf_un.buf32 + 7);
+ w_6 = LOAD_LITTLE_32(ctx->buf_un.buf32 + 6);
+ w_5 = LOAD_LITTLE_32(ctx->buf_un.buf32 + 5);
+ w_4 = LOAD_LITTLE_32(ctx->buf_un.buf32 + 4);
+ w_3 = LOAD_LITTLE_32(ctx->buf_un.buf32 + 3);
+ w_2 = LOAD_LITTLE_32(ctx->buf_un.buf32 + 2);
+ w_1 = LOAD_LITTLE_32(ctx->buf_un.buf32 + 1);
+ w_0 = LOAD_LITTLE_32(ctx->buf_un.buf32 + 0);
+ } else
+#endif
+ {
+ /*LINTED*/
+ w_15 = LOAD_LITTLE_32(blk + 60);
+ /*LINTED*/
+ w_14 = LOAD_LITTLE_32(blk + 56);
+ /*LINTED*/
+ w_13 = LOAD_LITTLE_32(blk + 52);
+ /*LINTED*/
+ w_12 = LOAD_LITTLE_32(blk + 48);
+ /*LINTED*/
+ w_11 = LOAD_LITTLE_32(blk + 44);
+ /*LINTED*/
+ w_10 = LOAD_LITTLE_32(blk + 40);
+ /*LINTED*/
+ w_9 = LOAD_LITTLE_32(blk + 36);
+ /*LINTED*/
+ w_8 = LOAD_LITTLE_32(blk + 32);
+ /*LINTED*/
+ w_7 = LOAD_LITTLE_32(blk + 28);
+ /*LINTED*/
+ w_6 = LOAD_LITTLE_32(blk + 24);
+ /*LINTED*/
+ w_5 = LOAD_LITTLE_32(blk + 20);
+ /*LINTED*/
+ w_4 = LOAD_LITTLE_32(blk + 16);
+ /*LINTED*/
+ w_3 = LOAD_LITTLE_32(blk + 12);
+ /*LINTED*/
+ w_2 = LOAD_LITTLE_32(blk + 8);
+ /*LINTED*/
+ w_1 = LOAD_LITTLE_32(blk + 4);
+ /*LINTED*/
+ w_0 = LOAD_LITTLE_32(blk + 0);
+ }
+
+ /*
+ * general optimization:
+ *
+ * even though this approach is described in the standard as
+ * being slower algorithmically, it is 30-40% faster than the
+ * "faster" version under SPARC, because this version has more
+ * of the constraints specified at compile-time and uses fewer
+ * variables (and therefore has better register utilization)
+ * than its "speedier" brother. (i've tried both, trust me)
+ *
+ * for either method given in the spec, there is an "assignment"
+ * phase where the following takes place:
+ *
+ * tmp = (main_computation);
+ * e = d; d = c; c = rotate_left(b, 30); b = a; a = tmp;
+ *
+ * we can make the algorithm go faster by not doing this work,
+ * but just pretending that `d' is now `e', etc. this works
+ * really well and obviates the need for a temporary variable.
+ * however, we still explictly perform the rotate action,
+ * since it is cheaper on SPARC to do it once than to have to
+ * do it over and over again.
+ */
+
+ /* round 1 */
+ e = ROTATE_LEFT(a, 5) + F(b, c, d) + e + w_0 + SHA1_CONST(0); /* 0 */
+ b = ROTATE_LEFT(b, 30);
+
+ d = ROTATE_LEFT(e, 5) + F(a, b, c) + d + w_1 + SHA1_CONST(0); /* 1 */
+ a = ROTATE_LEFT(a, 30);
+
+ c = ROTATE_LEFT(d, 5) + F(e, a, b) + c + w_2 + SHA1_CONST(0); /* 2 */
+ e = ROTATE_LEFT(e, 30);
+
+ b = ROTATE_LEFT(c, 5) + F(d, e, a) + b + w_3 + SHA1_CONST(0); /* 3 */
+ d = ROTATE_LEFT(d, 30);
+
+ a = ROTATE_LEFT(b, 5) + F(c, d, e) + a + w_4 + SHA1_CONST(0); /* 4 */
+ c = ROTATE_LEFT(c, 30);
+
+ e = ROTATE_LEFT(a, 5) + F(b, c, d) + e + w_5 + SHA1_CONST(0); /* 5 */
+ b = ROTATE_LEFT(b, 30);
+
+ d = ROTATE_LEFT(e, 5) + F(a, b, c) + d + w_6 + SHA1_CONST(0); /* 6 */
+ a = ROTATE_LEFT(a, 30);
+
+ c = ROTATE_LEFT(d, 5) + F(e, a, b) + c + w_7 + SHA1_CONST(0); /* 7 */
+ e = ROTATE_LEFT(e, 30);
+
+ b = ROTATE_LEFT(c, 5) + F(d, e, a) + b + w_8 + SHA1_CONST(0); /* 8 */
+ d = ROTATE_LEFT(d, 30);
+
+ a = ROTATE_LEFT(b, 5) + F(c, d, e) + a + w_9 + SHA1_CONST(0); /* 9 */
+ c = ROTATE_LEFT(c, 30);
+
+ e = ROTATE_LEFT(a, 5) + F(b, c, d) + e + w_10 + SHA1_CONST(0); /* 10 */
+ b = ROTATE_LEFT(b, 30);
+
+ d = ROTATE_LEFT(e, 5) + F(a, b, c) + d + w_11 + SHA1_CONST(0); /* 11 */
+ a = ROTATE_LEFT(a, 30);
+
+ c = ROTATE_LEFT(d, 5) + F(e, a, b) + c + w_12 + SHA1_CONST(0); /* 12 */
+ e = ROTATE_LEFT(e, 30);
+
+ b = ROTATE_LEFT(c, 5) + F(d, e, a) + b + w_13 + SHA1_CONST(0); /* 13 */
+ d = ROTATE_LEFT(d, 30);
+
+ a = ROTATE_LEFT(b, 5) + F(c, d, e) + a + w_14 + SHA1_CONST(0); /* 14 */
+ c = ROTATE_LEFT(c, 30);
+
+ e = ROTATE_LEFT(a, 5) + F(b, c, d) + e + w_15 + SHA1_CONST(0); /* 15 */
+ b = ROTATE_LEFT(b, 30);
+
+ w_0 = ROTATE_LEFT((w_13 ^ w_8 ^ w_2 ^ w_0), 1); /* 16 */
+ d = ROTATE_LEFT(e, 5) + F(a, b, c) + d + w_0 + SHA1_CONST(0);
+ a = ROTATE_LEFT(a, 30);
+
+ w_1 = ROTATE_LEFT((w_14 ^ w_9 ^ w_3 ^ w_1), 1); /* 17 */
+ c = ROTATE_LEFT(d, 5) + F(e, a, b) + c + w_1 + SHA1_CONST(0);
+ e = ROTATE_LEFT(e, 30);
+
+ w_2 = ROTATE_LEFT((w_15 ^ w_10 ^ w_4 ^ w_2), 1); /* 18 */
+ b = ROTATE_LEFT(c, 5) + F(d, e, a) + b + w_2 + SHA1_CONST(0);
+ d = ROTATE_LEFT(d, 30);
+
+ w_3 = ROTATE_LEFT((w_0 ^ w_11 ^ w_5 ^ w_3), 1); /* 19 */
+ a = ROTATE_LEFT(b, 5) + F(c, d, e) + a + w_3 + SHA1_CONST(0);
+ c = ROTATE_LEFT(c, 30);
+
+ /* round 2 */
+ w_4 = ROTATE_LEFT((w_1 ^ w_12 ^ w_6 ^ w_4), 1); /* 20 */
+ e = ROTATE_LEFT(a, 5) + G(b, c, d) + e + w_4 + SHA1_CONST(1);
+ b = ROTATE_LEFT(b, 30);
+
+ w_5 = ROTATE_LEFT((w_2 ^ w_13 ^ w_7 ^ w_5), 1); /* 21 */
+ d = ROTATE_LEFT(e, 5) + G(a, b, c) + d + w_5 + SHA1_CONST(1);
+ a = ROTATE_LEFT(a, 30);
+
+ w_6 = ROTATE_LEFT((w_3 ^ w_14 ^ w_8 ^ w_6), 1); /* 22 */
+ c = ROTATE_LEFT(d, 5) + G(e, a, b) + c + w_6 + SHA1_CONST(1);
+ e = ROTATE_LEFT(e, 30);
+
+ w_7 = ROTATE_LEFT((w_4 ^ w_15 ^ w_9 ^ w_7), 1); /* 23 */
+ b = ROTATE_LEFT(c, 5) + G(d, e, a) + b + w_7 + SHA1_CONST(1);
+ d = ROTATE_LEFT(d, 30);
+
+ w_8 = ROTATE_LEFT((w_5 ^ w_0 ^ w_10 ^ w_8), 1); /* 24 */
+ a = ROTATE_LEFT(b, 5) + G(c, d, e) + a + w_8 + SHA1_CONST(1);
+ c = ROTATE_LEFT(c, 30);
+
+ w_9 = ROTATE_LEFT((w_6 ^ w_1 ^ w_11 ^ w_9), 1); /* 25 */
+ e = ROTATE_LEFT(a, 5) + G(b, c, d) + e + w_9 + SHA1_CONST(1);
+ b = ROTATE_LEFT(b, 30);
+
+ w_10 = ROTATE_LEFT((w_7 ^ w_2 ^ w_12 ^ w_10), 1); /* 26 */
+ d = ROTATE_LEFT(e, 5) + G(a, b, c) + d + w_10 + SHA1_CONST(1);
+ a = ROTATE_LEFT(a, 30);
+
+ w_11 = ROTATE_LEFT((w_8 ^ w_3 ^ w_13 ^ w_11), 1); /* 27 */
+ c = ROTATE_LEFT(d, 5) + G(e, a, b) + c + w_11 + SHA1_CONST(1);
+ e = ROTATE_LEFT(e, 30);
+
+ w_12 = ROTATE_LEFT((w_9 ^ w_4 ^ w_14 ^ w_12), 1); /* 28 */
+ b = ROTATE_LEFT(c, 5) + G(d, e, a) + b + w_12 + SHA1_CONST(1);
+ d = ROTATE_LEFT(d, 30);
+
+ w_13 = ROTATE_LEFT((w_10 ^ w_5 ^ w_15 ^ w_13), 1); /* 29 */
+ a = ROTATE_LEFT(b, 5) + G(c, d, e) + a + w_13 + SHA1_CONST(1);
+ c = ROTATE_LEFT(c, 30);
+
+ w_14 = ROTATE_LEFT((w_11 ^ w_6 ^ w_0 ^ w_14), 1); /* 30 */
+ e = ROTATE_LEFT(a, 5) + G(b, c, d) + e + w_14 + SHA1_CONST(1);
+ b = ROTATE_LEFT(b, 30);
+
+ w_15 = ROTATE_LEFT((w_12 ^ w_7 ^ w_1 ^ w_15), 1); /* 31 */
+ d = ROTATE_LEFT(e, 5) + G(a, b, c) + d + w_15 + SHA1_CONST(1);
+ a = ROTATE_LEFT(a, 30);
+
+ w_0 = ROTATE_LEFT((w_13 ^ w_8 ^ w_2 ^ w_0), 1); /* 32 */
+ c = ROTATE_LEFT(d, 5) + G(e, a, b) + c + w_0 + SHA1_CONST(1);
+ e = ROTATE_LEFT(e, 30);
+
+ w_1 = ROTATE_LEFT((w_14 ^ w_9 ^ w_3 ^ w_1), 1); /* 33 */
+ b = ROTATE_LEFT(c, 5) + G(d, e, a) + b + w_1 + SHA1_CONST(1);
+ d = ROTATE_LEFT(d, 30);
+
+ w_2 = ROTATE_LEFT((w_15 ^ w_10 ^ w_4 ^ w_2), 1); /* 34 */
+ a = ROTATE_LEFT(b, 5) + G(c, d, e) + a + w_2 + SHA1_CONST(1);
+ c = ROTATE_LEFT(c, 30);
+
+ w_3 = ROTATE_LEFT((w_0 ^ w_11 ^ w_5 ^ w_3), 1); /* 35 */
+ e = ROTATE_LEFT(a, 5) + G(b, c, d) + e + w_3 + SHA1_CONST(1);
+ b = ROTATE_LEFT(b, 30);
+
+ w_4 = ROTATE_LEFT((w_1 ^ w_12 ^ w_6 ^ w_4), 1); /* 36 */
+ d = ROTATE_LEFT(e, 5) + G(a, b, c) + d + w_4 + SHA1_CONST(1);
+ a = ROTATE_LEFT(a, 30);
+
+ w_5 = ROTATE_LEFT((w_2 ^ w_13 ^ w_7 ^ w_5), 1); /* 37 */
+ c = ROTATE_LEFT(d, 5) + G(e, a, b) + c + w_5 + SHA1_CONST(1);
+ e = ROTATE_LEFT(e, 30);
+
+ w_6 = ROTATE_LEFT((w_3 ^ w_14 ^ w_8 ^ w_6), 1); /* 38 */
+ b = ROTATE_LEFT(c, 5) + G(d, e, a) + b + w_6 + SHA1_CONST(1);
+ d = ROTATE_LEFT(d, 30);
+
+ w_7 = ROTATE_LEFT((w_4 ^ w_15 ^ w_9 ^ w_7), 1); /* 39 */
+ a = ROTATE_LEFT(b, 5) + G(c, d, e) + a + w_7 + SHA1_CONST(1);
+ c = ROTATE_LEFT(c, 30);
+
+ /* round 3 */
+ w_8 = ROTATE_LEFT((w_5 ^ w_0 ^ w_10 ^ w_8), 1); /* 40 */
+ e = ROTATE_LEFT(a, 5) + H(b, c, d) + e + w_8 + SHA1_CONST(2);
+ b = ROTATE_LEFT(b, 30);
+
+ w_9 = ROTATE_LEFT((w_6 ^ w_1 ^ w_11 ^ w_9), 1); /* 41 */
+ d = ROTATE_LEFT(e, 5) + H(a, b, c) + d + w_9 + SHA1_CONST(2);
+ a = ROTATE_LEFT(a, 30);
+
+ w_10 = ROTATE_LEFT((w_7 ^ w_2 ^ w_12 ^ w_10), 1); /* 42 */
+ c = ROTATE_LEFT(d, 5) + H(e, a, b) + c + w_10 + SHA1_CONST(2);
+ e = ROTATE_LEFT(e, 30);
+
+ w_11 = ROTATE_LEFT((w_8 ^ w_3 ^ w_13 ^ w_11), 1); /* 43 */
+ b = ROTATE_LEFT(c, 5) + H(d, e, a) + b + w_11 + SHA1_CONST(2);
+ d = ROTATE_LEFT(d, 30);
+
+ w_12 = ROTATE_LEFT((w_9 ^ w_4 ^ w_14 ^ w_12), 1); /* 44 */
+ a = ROTATE_LEFT(b, 5) + H(c, d, e) + a + w_12 + SHA1_CONST(2);
+ c = ROTATE_LEFT(c, 30);
+
+ w_13 = ROTATE_LEFT((w_10 ^ w_5 ^ w_15 ^ w_13), 1); /* 45 */
+ e = ROTATE_LEFT(a, 5) + H(b, c, d) + e + w_13 + SHA1_CONST(2);
+ b = ROTATE_LEFT(b, 30);
+
+ w_14 = ROTATE_LEFT((w_11 ^ w_6 ^ w_0 ^ w_14), 1); /* 46 */
+ d = ROTATE_LEFT(e, 5) + H(a, b, c) + d + w_14 + SHA1_CONST(2);
+ a = ROTATE_LEFT(a, 30);
+
+ w_15 = ROTATE_LEFT((w_12 ^ w_7 ^ w_1 ^ w_15), 1); /* 47 */
+ c = ROTATE_LEFT(d, 5) + H(e, a, b) + c + w_15 + SHA1_CONST(2);
+ e = ROTATE_LEFT(e, 30);
+
+ w_0 = ROTATE_LEFT((w_13 ^ w_8 ^ w_2 ^ w_0), 1); /* 48 */
+ b = ROTATE_LEFT(c, 5) + H(d, e, a) + b + w_0 + SHA1_CONST(2);
+ d = ROTATE_LEFT(d, 30);
+
+ w_1 = ROTATE_LEFT((w_14 ^ w_9 ^ w_3 ^ w_1), 1); /* 49 */
+ a = ROTATE_LEFT(b, 5) + H(c, d, e) + a + w_1 + SHA1_CONST(2);
+ c = ROTATE_LEFT(c, 30);
+
+ w_2 = ROTATE_LEFT((w_15 ^ w_10 ^ w_4 ^ w_2), 1); /* 50 */
+ e = ROTATE_LEFT(a, 5) + H(b, c, d) + e + w_2 + SHA1_CONST(2);
+ b = ROTATE_LEFT(b, 30);
+
+ w_3 = ROTATE_LEFT((w_0 ^ w_11 ^ w_5 ^ w_3), 1); /* 51 */
+ d = ROTATE_LEFT(e, 5) + H(a, b, c) + d + w_3 + SHA1_CONST(2);
+ a = ROTATE_LEFT(a, 30);
+
+ w_4 = ROTATE_LEFT((w_1 ^ w_12 ^ w_6 ^ w_4), 1); /* 52 */
+ c = ROTATE_LEFT(d, 5) + H(e, a, b) + c + w_4 + SHA1_CONST(2);
+ e = ROTATE_LEFT(e, 30);
+
+ w_5 = ROTATE_LEFT((w_2 ^ w_13 ^ w_7 ^ w_5), 1); /* 53 */
+ b = ROTATE_LEFT(c, 5) + H(d, e, a) + b + w_5 + SHA1_CONST(2);
+ d = ROTATE_LEFT(d, 30);
+
+ w_6 = ROTATE_LEFT((w_3 ^ w_14 ^ w_8 ^ w_6), 1); /* 54 */
+ a = ROTATE_LEFT(b, 5) + H(c, d, e) + a + w_6 + SHA1_CONST(2);
+ c = ROTATE_LEFT(c, 30);
+
+ w_7 = ROTATE_LEFT((w_4 ^ w_15 ^ w_9 ^ w_7), 1); /* 55 */
+ e = ROTATE_LEFT(a, 5) + H(b, c, d) + e + w_7 + SHA1_CONST(2);
+ b = ROTATE_LEFT(b, 30);
+
+ w_8 = ROTATE_LEFT((w_5 ^ w_0 ^ w_10 ^ w_8), 1); /* 56 */
+ d = ROTATE_LEFT(e, 5) + H(a, b, c) + d + w_8 + SHA1_CONST(2);
+ a = ROTATE_LEFT(a, 30);
+
+ w_9 = ROTATE_LEFT((w_6 ^ w_1 ^ w_11 ^ w_9), 1); /* 57 */
+ c = ROTATE_LEFT(d, 5) + H(e, a, b) + c + w_9 + SHA1_CONST(2);
+ e = ROTATE_LEFT(e, 30);
+
+ w_10 = ROTATE_LEFT((w_7 ^ w_2 ^ w_12 ^ w_10), 1); /* 58 */
+ b = ROTATE_LEFT(c, 5) + H(d, e, a) + b + w_10 + SHA1_CONST(2);
+ d = ROTATE_LEFT(d, 30);
+
+ w_11 = ROTATE_LEFT((w_8 ^ w_3 ^ w_13 ^ w_11), 1); /* 59 */
+ a = ROTATE_LEFT(b, 5) + H(c, d, e) + a + w_11 + SHA1_CONST(2);
+ c = ROTATE_LEFT(c, 30);
+
+ /* round 4 */
+ w_12 = ROTATE_LEFT((w_9 ^ w_4 ^ w_14 ^ w_12), 1); /* 60 */
+ e = ROTATE_LEFT(a, 5) + G(b, c, d) + e + w_12 + SHA1_CONST(3);
+ b = ROTATE_LEFT(b, 30);
+
+ w_13 = ROTATE_LEFT((w_10 ^ w_5 ^ w_15 ^ w_13), 1); /* 61 */
+ d = ROTATE_LEFT(e, 5) + G(a, b, c) + d + w_13 + SHA1_CONST(3);
+ a = ROTATE_LEFT(a, 30);
+
+ w_14 = ROTATE_LEFT((w_11 ^ w_6 ^ w_0 ^ w_14), 1); /* 62 */
+ c = ROTATE_LEFT(d, 5) + G(e, a, b) + c + w_14 + SHA1_CONST(3);
+ e = ROTATE_LEFT(e, 30);
+
+ w_15 = ROTATE_LEFT((w_12 ^ w_7 ^ w_1 ^ w_15), 1); /* 63 */
+ b = ROTATE_LEFT(c, 5) + G(d, e, a) + b + w_15 + SHA1_CONST(3);
+ d = ROTATE_LEFT(d, 30);
+
+ w_0 = ROTATE_LEFT((w_13 ^ w_8 ^ w_2 ^ w_0), 1); /* 64 */
+ a = ROTATE_LEFT(b, 5) + G(c, d, e) + a + w_0 + SHA1_CONST(3);
+ c = ROTATE_LEFT(c, 30);
+
+ w_1 = ROTATE_LEFT((w_14 ^ w_9 ^ w_3 ^ w_1), 1); /* 65 */
+ e = ROTATE_LEFT(a, 5) + G(b, c, d) + e + w_1 + SHA1_CONST(3);
+ b = ROTATE_LEFT(b, 30);
+
+ w_2 = ROTATE_LEFT((w_15 ^ w_10 ^ w_4 ^ w_2), 1); /* 66 */
+ d = ROTATE_LEFT(e, 5) + G(a, b, c) + d + w_2 + SHA1_CONST(3);
+ a = ROTATE_LEFT(a, 30);
+
+ w_3 = ROTATE_LEFT((w_0 ^ w_11 ^ w_5 ^ w_3), 1); /* 67 */
+ c = ROTATE_LEFT(d, 5) + G(e, a, b) + c + w_3 + SHA1_CONST(3);
+ e = ROTATE_LEFT(e, 30);
+
+ w_4 = ROTATE_LEFT((w_1 ^ w_12 ^ w_6 ^ w_4), 1); /* 68 */
+ b = ROTATE_LEFT(c, 5) + G(d, e, a) + b + w_4 + SHA1_CONST(3);
+ d = ROTATE_LEFT(d, 30);
+
+ w_5 = ROTATE_LEFT((w_2 ^ w_13 ^ w_7 ^ w_5), 1); /* 69 */
+ a = ROTATE_LEFT(b, 5) + G(c, d, e) + a + w_5 + SHA1_CONST(3);
+ c = ROTATE_LEFT(c, 30);
+
+ w_6 = ROTATE_LEFT((w_3 ^ w_14 ^ w_8 ^ w_6), 1); /* 70 */
+ e = ROTATE_LEFT(a, 5) + G(b, c, d) + e + w_6 + SHA1_CONST(3);
+ b = ROTATE_LEFT(b, 30);
+
+ w_7 = ROTATE_LEFT((w_4 ^ w_15 ^ w_9 ^ w_7), 1); /* 71 */
+ d = ROTATE_LEFT(e, 5) + G(a, b, c) + d + w_7 + SHA1_CONST(3);
+ a = ROTATE_LEFT(a, 30);
+
+ w_8 = ROTATE_LEFT((w_5 ^ w_0 ^ w_10 ^ w_8), 1); /* 72 */
+ c = ROTATE_LEFT(d, 5) + G(e, a, b) + c + w_8 + SHA1_CONST(3);
+ e = ROTATE_LEFT(e, 30);
+
+ w_9 = ROTATE_LEFT((w_6 ^ w_1 ^ w_11 ^ w_9), 1); /* 73 */
+ b = ROTATE_LEFT(c, 5) + G(d, e, a) + b + w_9 + SHA1_CONST(3);
+ d = ROTATE_LEFT(d, 30);
+
+ w_10 = ROTATE_LEFT((w_7 ^ w_2 ^ w_12 ^ w_10), 1); /* 74 */
+ a = ROTATE_LEFT(b, 5) + G(c, d, e) + a + w_10 + SHA1_CONST(3);
+ c = ROTATE_LEFT(c, 30);
+
+ w_11 = ROTATE_LEFT((w_8 ^ w_3 ^ w_13 ^ w_11), 1); /* 75 */
+ e = ROTATE_LEFT(a, 5) + G(b, c, d) + e + w_11 + SHA1_CONST(3);
+ b = ROTATE_LEFT(b, 30);
+
+ w_12 = ROTATE_LEFT((w_9 ^ w_4 ^ w_14 ^ w_12), 1); /* 76 */
+ d = ROTATE_LEFT(e, 5) + G(a, b, c) + d + w_12 + SHA1_CONST(3);
+ a = ROTATE_LEFT(a, 30);
+
+ w_13 = ROTATE_LEFT((w_10 ^ w_5 ^ w_15 ^ w_13), 1); /* 77 */
+ c = ROTATE_LEFT(d, 5) + G(e, a, b) + c + w_13 + SHA1_CONST(3);
+ e = ROTATE_LEFT(e, 30);
+
+ w_14 = ROTATE_LEFT((w_11 ^ w_6 ^ w_0 ^ w_14), 1); /* 78 */
+ b = ROTATE_LEFT(c, 5) + G(d, e, a) + b + w_14 + SHA1_CONST(3);
+ d = ROTATE_LEFT(d, 30);
+
+ w_15 = ROTATE_LEFT((w_12 ^ w_7 ^ w_1 ^ w_15), 1); /* 79 */
+
+ ctx->state[0] += ROTATE_LEFT(b, 5) + G(c, d, e) + a + w_15 +
+ SHA1_CONST(3);
+ ctx->state[1] += b;
+ ctx->state[2] += ROTATE_LEFT(c, 30);
+ ctx->state[3] += d;
+ ctx->state[4] += e;
+
+ /* zeroize sensitive information */
+ w_0 = w_1 = w_2 = w_3 = w_4 = w_5 = w_6 = w_7 = w_8 = 0;
+ w_9 = w_10 = w_11 = w_12 = w_13 = w_14 = w_15 = 0;
+}
+
+/*
+ * devpro compiler optimization:
+ *
+ * the compiler can generate better code if it knows that `input' and
+ * `output' do not point to the same source. there is no portable
+ * way to tell the compiler this, but the sun compiler recognizes the
+ * `_Restrict' keyword to indicate this condition. use it if possible.
+ */
+
+#ifdef __RESTRICT
+#define restrict _Restrict
+#else
+#define restrict /* nothing */
+#endif
+
+/*
+ * Encode()
+ *
+ * purpose: to convert a list of numbers from little endian to big endian
+ * input: uint8_t * : place to store the converted big endian numbers
+ * uint32_t * : place to get numbers to convert from
+ * size_t : the length of the input in bytes
+ * output: void
+ */
+
+static void
+Encode(uint8_t *restrict output, uint32_t *restrict input, size_t len)
+{
+ size_t i, j;
+
+ for (i = 0, j = 0; j < len; i++, j += 4) {
+
+#if defined(__sparc)
+
+ /*LINTED*/
+ *(uint32_t *)(output + j) = input[i];
+
+#else /* little endian -- will work on big endian, but slowly */
+
+ output[j] = (input[i] >> 24) & 0xff;
+ output[j + 1] = (input[i] >> 16) & 0xff;
+ output[j + 2] = (input[i] >> 8) & 0xff;
+ output[j + 3] = input[i] & 0xff;
+
+#endif
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppd/sha1.h b/usr/src/cmd/cmd-inet/usr.bin/pppd/sha1.h
new file mode 100644
index 0000000000..af6c511b92
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppd/sha1.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 1998, 2000 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _SHA1_H
+#define _SHA1_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h> /* for uint_* */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined(sun) && !defined(_SYS_INT_TYPES_H) && !defined(_UINT32_T)
+/* Backward compatibility */
+typedef uint_t uint32_t;
+typedef ushort_t uint16_t;
+typedef uchar_t uint8_t;
+typedef unsigned long uintptr_t;
+#define _UINT32_T
+#endif
+
+#ifdef __linux__
+#include <stdint.h>
+#endif
+
+/* SHA-1 context. */
+typedef struct {
+ uint32_t state[5]; /* state (ABCDE) */
+ uint32_t count[2]; /* number of bits, modulo 2^64 (msb first) */
+ union {
+ uint8_t buf8[64]; /* undigested input */
+ uint32_t buf32[16]; /* realigned input */
+ } buf_un;
+} SHA1_CTX;
+
+void SHA1Init(SHA1_CTX *);
+void SHA1Update(SHA1_CTX *, const uint8_t *, uint32_t);
+void SHA1Final(uint8_t *, SHA1_CTX *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SHA1_H */
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppd/sha1_consts.h b/usr/src/cmd/cmd-inet/usr.bin/pppd/sha1_consts.h
new file mode 100644
index 0000000000..126e245498
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppd/sha1_consts.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 1998, by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _SHA1_CONSTS_H
+#define _SHA1_CONSTS_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * as explained in sha1.c, loading 32-bit constants on a sparc is expensive
+ * since it involves both a `sethi' and an `or'. thus, we instead use `ld'
+ * to load the constants from an array called `sha1_consts'. however, on
+ * intel (and perhaps other processors), it is cheaper to load the constant
+ * directly. thus, the c code in SHA1Transform() uses the macro SHA1_CONST()
+ * which either expands to a constant or an array reference, depending on
+ * the architecture the code is being compiled for.
+ */
+
+#include <sys/types.h> /* uint32_t */
+
+extern const uint32_t sha1_consts[];
+
+#if defined(__sparc)
+#define SHA1_CONST(x) (sha1_consts[x])
+#else
+#define SHA1_CONST(x) (SHA1_CONST_ ## x)
+#endif
+
+/* constants, as provided in FIPS 180-1 */
+
+#define SHA1_CONST_0 0x5a827999U
+#define SHA1_CONST_1 0x6ed9eba1U
+#define SHA1_CONST_2 0x8f1bbcdcU
+#define SHA1_CONST_3 0xca62c1d6U
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SHA1_CONSTS_H */
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppd/sys-solaris.c b/usr/src/cmd/cmd-inet/usr.bin/pppd/sys-solaris.c
new file mode 100644
index 0000000000..9d11ac72d8
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppd/sys-solaris.c
@@ -0,0 +1,3791 @@
+/*
+ * System-dependent procedures for pppd under Solaris 2.x (SunOS 5.x).
+ *
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies.
+ *
+ * SUN MAKES NO REPRESENTATION OR WARRANTIES ABOUT THE SUITABILITY OF
+ * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+ * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
+ * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
+ * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES
+ *
+ * Copyright (c) 1994 The Australian National University.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies. This software is provided without any
+ * warranty, express or implied. The Australian National University
+ * makes no representations about the suitability of this software for
+ * any purpose.
+ *
+ * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
+ * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
+ * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
+ * OR MODIFICATIONS.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+#define RCSID "$Id: sys-solaris.c,v 1.2 2000/04/21 01:27:57 masputra Exp $"
+
+#include <limits.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <termios.h>
+#include <signal.h>
+#include <string.h>
+#include <stropts.h>
+#include <utmpx.h>
+#include <sys/types.h>
+#include <sys/ioccom.h>
+#include <sys/stream.h>
+#include <sys/stropts.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/sysmacros.h>
+#include <sys/systeminfo.h>
+#include <sys/dlpi.h>
+#include <sys/stat.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/route.h>
+#include <net/ppp_defs.h>
+#include <net/pppio.h>
+#include <net/if_types.h>
+#include <net/if_dl.h>
+#include <netinet/in.h>
+#include <sys/tihdr.h>
+#include <inet/mib2.h>
+#include <sys/ethernet.h>
+#include <sys/ser_sync.h>
+
+#include "pppd.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "ipcp.h"
+#ifdef INET6
+#include "ipv6cp.h"
+#endif /* INET6 */
+#include "ccp.h"
+
+#if !defined(lint) && !defined(_lint)
+static const char rcsid[] = RCSID;
+#endif
+
+/* Need to use UDP for ifconfig compatibility */
+#if !defined(UDP_DEV_NAME)
+#define UDP_DEV_NAME "/dev/udp"
+#endif /* UDP_DEV_NAME */
+
+#if !defined(IP_DEV_NAME)
+#define IP_DEV_NAME "/dev/ip"
+#endif /* IP_DEV_NAME */
+
+#if !defined(UDP6_DEV_NAME)
+#define UDP6_DEV_NAME "/dev/udp6"
+#endif /* UDP6_DEV_NAME */
+
+#if !defined(IP6_DEV_NAME)
+#define IP6_DEV_NAME "/dev/ip6"
+#endif /* IP6_DEV_NAME */
+
+#if !defined(IP_MOD_NAME)
+#define IP_MOD_NAME "ip"
+#endif /* IP_MOD_NAME */
+
+#define PPPSTRTIMOUT 1 /* Timeout in seconds for ioctl */
+#define MAX_POLLFDS 32
+#define NMODULES 32
+
+#ifndef LIFNAMSIZ
+#define LIFNAMSIZ 32
+#endif /* LIFNAMSIZ */
+
+#ifndef MAXIFS
+#define MAXIFS 256
+#endif /* MAXIFS */
+
+#ifndef ETHERADDRL
+#define ETHERADDRL 6
+#endif /* ETHERADDRL */
+
+#ifdef INET6
+#define _IN6_LLX_FROM_EUI64(l, s, eui64, as, len) \
+ (s->sin6_addr.s6_addr32[0] = htonl(as), \
+ eui64_copy(eui64, s->sin6_addr.s6_addr32[2]), \
+ s->sin6_family = AF_INET6, \
+ l.lifr_addr.ss_family = AF_INET6, \
+ l.lifr_addrlen = len, \
+ l.lifr_addr = laddr)
+
+/*
+ * Generate a link-local address with an interface-id based on the given
+ * EUI64 identifier. Note that the len field is unused by SIOCSLIFADDR.
+ */
+#define IN6_LLADDR_FROM_EUI64(l, s, eui64) \
+ _IN6_LLX_FROM_EUI64(l, s, eui64, 0xfe800000, 0)
+
+/*
+ * Generate an EUI64 based interface-id for use by stateless address
+ * autoconfiguration. These are required to be 64 bits long as defined in
+ * the "Interface Identifiers" section of the IPv6 Addressing Architecture
+ * (RFC3513).
+ */
+#define IN6_LLTOKEN_FROM_EUI64(l, s, eui64) \
+ _IN6_LLX_FROM_EUI64(l, s, eui64, 0, 64)
+#endif /* INET6 */
+
+#define IPCP_ENABLED ipcp_protent.enabled_flag
+#ifdef INET6
+#define IPV6CP_ENABLED ipv6cp_protent.enabled_flag
+#endif /* INET6 */
+
+/* For plug-in usage. */
+int (*sys_read_packet_hook) __P((int retv, struct strbuf *ctrl,
+ struct strbuf *data, int flags)) = NULL;
+bool already_ppp = 0; /* Already in PPP mode */
+
+static int pppfd = -1; /* ppp driver fd */
+static int fdmuxid = -1; /* driver mux fd */
+static int ipfd = -1; /* IPv4 fd */
+static int ipmuxid = -1; /* IPv4 mux fd */
+static int ip6fd = -1; /* IPv6 fd */
+static int ip6muxid = -1; /* IPv6 mux fd */
+static bool if6_is_up = 0; /* IPv6 if marked as up */
+static bool if_is_up = 0; /* IPv4 if marked as up */
+static bool restore_term = 0; /* Restore TTY after closing link */
+static struct termios inittermios; /* TTY settings */
+static struct winsize wsinfo; /* Initial window size info */
+static pid_t tty_sid; /* original sess ID for term */
+static struct pollfd pollfds[MAX_POLLFDS]; /* array of polled fd */
+static int n_pollfds = 0; /* total count of polled fd */
+static int link_mtu; /* link Maximum Transmit Unit */
+static int tty_nmodules; /* total count of TTY modules used */
+static char tty_modules[NMODULES][FMNAMESZ+1];
+ /* array of TTY modules used */
+static int tty_npushed; /* total count of pushed PPP modules */
+static u_int32_t remote_addr; /* IP address of peer */
+static u_int32_t default_route_gateway; /* Gateway for default route */
+static u_int32_t proxy_arp_addr; /* Addr for proxy arp entry */
+static u_int32_t lastlink_status; /* Last link status info */
+
+static bool use_plink = 0; /* Use I_LINK by default */
+static bool plumbed = 0; /* Use existing interface */
+
+/* Default is to use /dev/sppp as driver. */
+static const char *drvnam = PPP_DEV_NAME;
+static bool integrated_driver = 0;
+static int extra_dev_fd = -1; /* keep open until ready */
+
+static option_t solaris_option_list[] = {
+ { "plink", o_bool, &use_plink, "Use I_PLINK instead of I_LINK",
+ OPT_PRIV|1 },
+ { "noplink", o_bool, &use_plink, "Use I_LINK instead of I_PLINK",
+ OPT_PRIV|0 },
+ { "plumbed", o_bool, &plumbed, "Use pre-plumbed interface",
+ OPT_PRIV|1 },
+ { NULL }
+};
+
+/*
+ * Prototypes for procedures local to this file.
+ */
+static int translate_speed __P((int));
+static int baud_rate_of __P((int));
+static int get_ether_addr __P((u_int32_t, struct sockaddr_dl *, int));
+static int dlpi_attach __P((int, int));
+static int dlpi_info_req __P((int));
+static int dlpi_get_reply __P((int, union DL_primitives *, int, int));
+static int strioctl __P((int, int, void *, int, int));
+static int plumb_ipif __P((int));
+static int unplumb_ipif __P((int));
+#ifdef INET6
+static int plumb_ip6if __P((int));
+static int unplumb_ip6if __P((int));
+static int open_ip6fd(void);
+#endif /* INET6 */
+static int open_ipfd(void);
+static int sifroute __P((int, u_int32_t, u_int32_t, int, const char *));
+static int giflags __P((u_int32_t, bool *));
+static void handle_unbind __P((u_int32_t));
+static void handle_bind __P((u_int32_t));
+
+/*
+ * Wrapper for regular ioctl; masks out EINTR.
+ */
+static int
+myioctl(int fd, int cmd, void *arg)
+{
+ int retv;
+
+ errno = 0;
+ while ((retv = ioctl(fd, cmd, arg)) == -1) {
+ if (errno != EINTR)
+ break;
+ }
+ return (retv);
+}
+
+/*
+ * sys_check_options()
+ *
+ * Check the options that the user specified.
+ */
+int
+sys_check_options(void)
+{
+ if (plumbed) {
+ if (req_unit == -1)
+ req_unit = -2;
+ ipmuxid = 0;
+ ip6muxid = 0;
+ }
+ return (1);
+}
+
+/*
+ * sys_options()
+ *
+ * Add or remove system-specific options.
+ */
+void
+sys_options(void)
+{
+ (void) remove_option("ktune");
+ (void) remove_option("noktune");
+ add_options(solaris_option_list);
+}
+
+/*
+ * sys_ifname()
+ *
+ * Set ifname[] to contain name of IP interface for this unit.
+ */
+void
+sys_ifname(void)
+{
+ const char *cp;
+
+ if ((cp = strrchr(drvnam, '/')) == NULL)
+ cp = drvnam;
+ else
+ cp++;
+ (void) slprintf(ifname, sizeof (ifname), "%s%d", cp, ifunit);
+}
+
+/*
+ * ppp_available()
+ *
+ * Check whether the system has any ppp interfaces.
+ */
+int
+ppp_available(void)
+{
+ struct stat buf;
+ int fd;
+ uint32_t typ;
+
+ if (stat(PPP_DEV_NAME, &buf) >= 0)
+ return (1);
+
+ /*
+ * Simple check for system using Apollo POS without SUNWpppd
+ * (/dev/sppp) installed. This is intentionally not kept open
+ * here, since the user may not have the same privileges (as
+ * determined later). If Apollo were just shipped with the
+ * full complement of packages, this wouldn't be an issue.
+ */
+ if (devnam[0] == '\0' &&
+ (fd = open(devnam, O_RDWR | O_NONBLOCK | O_NOCTTY)) >= 0) {
+ if (strioctl(fd, PPPIO_GTYPE, &typ, 0, sizeof (typ)) >= 0 &&
+ typ == PPPTYP_MUX) {
+ (void) close(fd);
+ return (1);
+ }
+ (void) close(fd);
+ }
+ return (0);
+}
+
+static int
+open_ipfd(void)
+{
+ ipfd = open(IP_DEV_NAME, O_RDWR | O_NONBLOCK, 0);
+ if (ipfd < 0) {
+ error("Couldn't open IP device (%s): %m", IP_DEV_NAME);
+ }
+ return (ipfd);
+}
+
+static int
+read_ip_interface(int unit)
+{
+ struct ifreq ifr;
+ struct sockaddr_in sin;
+
+ if (ipfd == -1 && open_ipfd() == -1)
+ return (0);
+
+ BZERO(&ifr, sizeof (ifr));
+ (void) strlcpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
+
+ /* Get the existing MTU */
+ if (myioctl(ipfd, SIOCGIFMTU, &ifr) < 0) {
+ warn("Couldn't get IP MTU on %s: %m", ifr.ifr_name);
+ return (0);
+ }
+ dbglog("got MTU %d from interface", ifr.ifr_metric);
+ if (ifr.ifr_metric != 0 &&
+ (lcp_allowoptions[unit].mru == 0 ||
+ lcp_allowoptions[unit].mru > ifr.ifr_metric))
+ lcp_allowoptions[unit].mru = ifr.ifr_metric;
+
+ /* Get the local IP address */
+ if (ipcp_wantoptions[unit].ouraddr == 0 ||
+ ipcp_from_hostname) {
+ if (myioctl(ipfd, SIOCGIFADDR, &ifr) < 0) {
+ warn("Couldn't get local IP address (%s): %m",
+ ifr.ifr_name);
+ return (0);
+ }
+ BCOPY(&ifr.ifr_addr, &sin, sizeof (struct sockaddr_in));
+ ipcp_wantoptions[unit].ouraddr = sin.sin_addr.s_addr;
+ dbglog("got local address %I from interface",
+ ipcp_wantoptions[unit].ouraddr);
+ }
+
+ /* Get the remote IP address */
+ if (ipcp_wantoptions[unit].hisaddr == 0) {
+ if (myioctl(ipfd, SIOCGIFDSTADDR, &ifr) < 0) {
+ warn("Couldn't get remote IP address (%s): %m",
+ ifr.ifr_name);
+ return (0);
+ }
+ BCOPY(&ifr.ifr_dstaddr, &sin, sizeof (struct sockaddr_in));
+ ipcp_wantoptions[unit].hisaddr = sin.sin_addr.s_addr;
+ dbglog("got remote address %I from interface",
+ ipcp_wantoptions[unit].hisaddr);
+ }
+ return (1);
+}
+
+#ifdef INET6
+static int
+open_ip6fd(void)
+{
+ ip6fd = open(IP6_DEV_NAME, O_RDWR | O_NONBLOCK, 0);
+ if (ip6fd < 0) {
+ error("Couldn't open IPv6 device (%s): %m", IP6_DEV_NAME);
+ }
+ return (ip6fd);
+}
+
+static int
+read_ipv6_interface(int unit)
+{
+ struct lifreq lifr;
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
+
+ if (ip6fd == -1 && open_ip6fd() == -1)
+ return (0);
+
+ BZERO(&lifr, sizeof (lifr));
+ (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
+
+ /* Get the existing MTU */
+ if (myioctl(ip6fd, SIOCGLIFMTU, &lifr) < 0) {
+ warn("Couldn't get IPv6 MTU on %s: %m", lifr.lifr_name);
+ return (0);
+ }
+ if (lifr.lifr_mtu != 0 &&
+ (lcp_allowoptions[unit].mru == 0 ||
+ lcp_allowoptions[unit].mru > lifr.lifr_mtu))
+ lcp_allowoptions[unit].mru = lifr.lifr_mtu;
+
+ /* Get the local IPv6 address */
+ if (eui64_iszero(ipv6cp_wantoptions[unit].ourid) ||
+ (ipcp_from_hostname && ipv6cp_wantoptions[unit].use_ip)) {
+ if (myioctl(ip6fd, SIOCGLIFADDR, &lifr) < 0) {
+ warn("Couldn't get local IPv6 address (%s): %m",
+ lifr.lifr_name);
+ return (0);
+ }
+ eui64_copy(sin6->sin6_addr.s6_addr32[2],
+ ipv6cp_wantoptions[unit].ourid);
+ }
+
+ /* Get the remote IP address */
+ if (eui64_iszero(ipv6cp_wantoptions[unit].hisid)) {
+ if (myioctl(ip6fd, SIOCGLIFDSTADDR, &lifr) < 0) {
+ warn("Couldn't get remote IPv6 address (%s): %m",
+ lifr.lifr_name);
+ return (0);
+ }
+ eui64_copy(sin6->sin6_addr.s6_addr32[2],
+ ipv6cp_wantoptions[unit].hisid);
+ }
+ return (1);
+}
+#endif /* INET6 */
+
+/*
+ * Read information on existing interface(s) and configure ourselves
+ * to negotiate appropriately.
+ */
+static void
+read_interface(int unit)
+{
+ dbglog("reading existing interface data; %sip %sipv6",
+ IPCP_ENABLED ? "" : "!",
+#ifdef INET6
+ IPV6CP_ENABLED ? "" :
+#endif
+ "!");
+ if (IPCP_ENABLED && !read_ip_interface(unit))
+ IPCP_ENABLED = 0;
+#ifdef INET6
+ if (IPV6CP_ENABLED && !read_ipv6_interface(unit))
+ IPV6CP_ENABLED = 0;
+#endif
+}
+
+/*
+ * sys_init()
+ *
+ * System-dependent initialization.
+ */
+void
+sys_init(bool open_as_user)
+{
+ uint32_t x;
+ uint32_t typ;
+
+ if (pppfd != -1) {
+ return;
+ }
+
+ if (!direct_tty && devnam[0] != '\0') {
+ /*
+ * Check for integrated driver-like devices (such as
+ * POS). These identify themselves as "PPP
+ * multiplexor" drivers.
+ */
+ if (open_as_user)
+ (void) seteuid(getuid());
+ pppfd = open(devnam, O_RDWR | O_NONBLOCK);
+ if (open_as_user)
+ (void) seteuid(0);
+ if (pppfd >= 0 &&
+ strioctl(pppfd, PPPIO_GTYPE, &typ, 0, sizeof (typ)) >= 0 &&
+ typ == PPPTYP_MUX) {
+ integrated_driver = 1;
+ drvnam = devnam;
+ } else if (demand) {
+ (void) close(pppfd);
+ pppfd = -1;
+ } else {
+ extra_dev_fd = pppfd;
+ pppfd = -1;
+ }
+ }
+
+ /*
+ * Open Solaris PPP device driver.
+ */
+ if (pppfd < 0)
+ pppfd = open(drvnam, O_RDWR | O_NONBLOCK);
+ if (pppfd < 0) {
+ fatal("Can't open %s: %m", drvnam);
+ }
+ if (kdebugflag & 1) {
+ x = PPPDBG_LOG + PPPDBG_DRIVER;
+ if (strioctl(pppfd, PPPIO_DEBUG, &x, sizeof (x), 0) < 0) {
+ warn("PPPIO_DEBUG ioctl for mux failed: %m");
+ }
+ }
+ /*
+ * Assign a new PPA and get its unit number.
+ */
+ x = req_unit;
+ if (strioctl(pppfd, PPPIO_NEWPPA, &x, sizeof (x), sizeof (x)) < 0) {
+ if (errno == ENXIO && plumbed)
+ fatal("No idle interfaces available for use");
+ fatal("PPPIO_NEWPPA ioctl failed: %m");
+ }
+ ifunit = x;
+ if (req_unit >= 0 && ifunit != req_unit) {
+ if (plumbed)
+ fatal("unable to get requested unit %d", req_unit);
+ else
+ warn("unable to get requested unit %d", req_unit);
+ }
+ /*
+ * Enable packet time-stamping when idle option is specified. Note
+ * that we need to only do this on the control stream. Subsequent
+ * streams attached to this control stream (ppa) will inherit
+ * the time-stamp bit.
+ */
+ if (idle_time_limit > 0) {
+ if (strioctl(pppfd, PPPIO_USETIMESTAMP, NULL, 0, 0) < 0) {
+ warn("PPPIO_USETIMESTAMP ioctl failed: %m");
+ }
+ }
+ if (plumbed) {
+ sys_ifname();
+ read_interface(0);
+ }
+}
+
+int
+sys_extra_fd(void)
+{
+ int fd;
+
+ fd = extra_dev_fd;
+ extra_dev_fd = -1;
+ return (fd);
+}
+
+static int
+open_udpfd(void)
+{
+ int udpfd;
+
+ udpfd = open(UDP_DEV_NAME, O_RDWR | O_NONBLOCK, 0);
+ if (udpfd < 0) {
+ error("Couldn't open UDP device (%s): %m", UDP_DEV_NAME);
+ }
+ return (udpfd);
+}
+
+/*
+ * plumb_ipif()
+ *
+ * Perform IP interface plumbing.
+ */
+/*ARGSUSED*/
+static int
+plumb_ipif(int unit)
+{
+ int udpfd = -1, tmpfd;
+ uint32_t x;
+ struct ifreq ifr;
+
+ if (!IPCP_ENABLED || (ifunit == -1) || (pppfd == -1)) {
+ return (0);
+ }
+ if (plumbed)
+ return (1);
+ if (ipfd == -1 && open_ipfd() == -1)
+ return (0);
+ if (use_plink && (udpfd = open_udpfd()) == -1)
+ return (0);
+ tmpfd = open(drvnam, O_RDWR | O_NONBLOCK, 0);
+ if (tmpfd < 0) {
+ error("Couldn't open PPP device (%s): %m", drvnam);
+ if (udpfd != -1)
+ (void) close(udpfd);
+ return (0);
+ }
+ if (kdebugflag & 1) {
+ x = PPPDBG_LOG + PPPDBG_DRIVER;
+ if (strioctl(tmpfd, PPPIO_DEBUG, &x, sizeof (x), 0) < 0) {
+ warn("PPPIO_DEBUG ioctl for mux failed: %m");
+ }
+ }
+ if (myioctl(tmpfd, I_PUSH, IP_MOD_NAME) < 0) {
+ error("Couldn't push IP module (%s): %m", IP_MOD_NAME);
+ goto err_ret;
+ }
+ /*
+ * Assign ppa according to the unit number returned by ppp device
+ * after plumbing is completed above. Without setting the ppa, ip
+ * module will return EINVAL upon setting the interface UP
+ * (SIOCSxIFFLAGS). This is because ip module in 2.8 expects two
+ * DLPI_INFO_REQ to be sent down to the driver (below ip) before
+ * IFF_UP bit can be set. Plumbing the device causes one DLPI_INFO_REQ
+ * to be sent down, and the second DLPI_INFO_REQ is sent upon receiving
+ * IF_UNITSEL (old) or SIOCSLIFNAME (new) ioctls. Such setting of the
+ * ppa is required because the ppp DLPI provider advertises itself as
+ * a DLPI style 2 type, which requires a point of attachment to be
+ * specified. The only way the user can specify a point of attachment
+ * is via SIOCSLIFNAME or IF_UNITSEL. Such changes in the behavior of
+ * ip module was made to meet new or evolving standards requirements.
+ */
+ if (myioctl(tmpfd, IF_UNITSEL, &ifunit) < 0) {
+ error("Couldn't set ppa for unit %d: %m", ifunit);
+ goto err_ret;
+ }
+ if (use_plink) {
+ ipmuxid = myioctl(udpfd, I_PLINK, (void *)tmpfd);
+ if (ipmuxid < 0) {
+ error("Can't I_PLINK PPP device to IP: %m");
+ goto err_ret;
+ }
+ } else {
+ ipmuxid = myioctl(ipfd, I_LINK, (void *)tmpfd);
+ if (ipmuxid < 0) {
+ error("Can't I_LINK PPP device to IP: %m");
+ goto err_ret;
+ }
+ }
+ BZERO(&ifr, sizeof (ifr));
+ (void) strlcpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
+ ifr.ifr_ip_muxid = ipmuxid;
+ ifr.ifr_arp_muxid = -1;
+ if (myioctl(ipfd, SIOCSIFMUXID, (caddr_t)&ifr) < 0) {
+ error("Can't set mux ID SIOCSIFMUXID on %s: %m", ifname);
+ goto err_ret;
+ }
+ if (udpfd != -1)
+ (void) close(udpfd);
+ (void) close(tmpfd);
+ return (1);
+err_ret:
+ if (udpfd != -1)
+ (void) close(udpfd);
+ (void) close(tmpfd);
+ return (0);
+}
+
+/*
+ * unplumb_ipif()
+ *
+ * Perform IP interface unplumbing. Possibly called from die(), so there
+ * shouldn't be any call to die() or fatal() here.
+ */
+static int
+unplumb_ipif(int unit)
+{
+ int udpfd = -1, fd = -1;
+ int id;
+ struct lifreq lifr;
+
+ if (!IPCP_ENABLED || (ifunit == -1)) {
+ return (0);
+ }
+ if (!plumbed && (ipmuxid == -1 || (ipfd == -1 && !use_plink)))
+ return (1);
+ id = ipmuxid;
+ if (!plumbed && use_plink) {
+ if ((udpfd = open_udpfd()) == -1)
+ return (0);
+ /*
+ * Note: must re-get mux ID, since any intervening
+ * ifconfigs will change this.
+ */
+ BZERO(&lifr, sizeof (lifr));
+ (void) strlcpy(lifr.lifr_name, ifname,
+ sizeof (lifr.lifr_name));
+ if (myioctl(ipfd, SIOCGLIFMUXID, (caddr_t)&lifr) < 0) {
+ warn("Can't get mux fd: SIOCGLIFMUXID: %m");
+ } else {
+ id = lifr.lifr_ip_muxid;
+ fd = myioctl(udpfd, _I_MUXID2FD, (void *)id);
+ if (fd < 0) {
+ warn("Can't get mux fd: _I_MUXID2FD: %m");
+ }
+ }
+ }
+ /*
+ * Mark down and unlink the ip interface.
+ */
+ (void) sifdown(unit);
+ if (default_route_gateway != 0) {
+ (void) cifdefaultroute(0, default_route_gateway,
+ default_route_gateway);
+ }
+ if (proxy_arp_addr != 0) {
+ (void) cifproxyarp(0, proxy_arp_addr);
+ }
+ ipmuxid = -1;
+ if (plumbed)
+ return (1);
+ if (use_plink) {
+ if (myioctl(udpfd, I_PUNLINK, (void *)id) < 0) {
+ error("Can't I_PUNLINK PPP from IP: %m");
+ if (fd != -1)
+ (void) close(fd);
+ (void) close(udpfd);
+ return (0);
+ }
+ if (fd != -1)
+ (void) close(fd);
+ (void) close(udpfd);
+ } else {
+ if (myioctl(ipfd, I_UNLINK, (void *)id) < 0) {
+ error("Can't I_UNLINK PPP from IP: %m");
+ return (0);
+ }
+ }
+ return (1);
+}
+
+/*
+ * sys_cleanup()
+ *
+ * Restore any system state we modified before exiting: mark the
+ * interface down, delete default route and/or proxy arp entry. This
+ * should not call die() because it's called from die().
+ */
+void
+sys_cleanup()
+{
+ (void) unplumb_ipif(0);
+#ifdef INET6
+ (void) unplumb_ip6if(0);
+#endif /* INET6 */
+}
+
+/*
+ * get_first_hwaddr()
+ *
+ * Stores the first hardware interface address found in the system
+ * into addr and return 1 upon success, or 0 if none is found. This
+ * is also called from the multilink code.
+ */
+int
+get_first_hwaddr(addr, msize)
+ uchar_t *addr;
+ int msize;
+{
+ struct ifconf ifc;
+ register struct ifreq *pifreq;
+ struct ifreq ifr;
+ int fd, num_ifs, i;
+ uint_t fl, req_size;
+ char *req;
+ boolean_t found;
+
+ if (addr == NULL) {
+ return (0);
+ }
+ fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd < 0) {
+ error("get_first_hwaddr: error opening IP socket: %m");
+ return (0);
+ }
+ /*
+ * Find out how many interfaces are running
+ */
+ if (myioctl(fd, SIOCGIFNUM, (caddr_t)&num_ifs) < 0) {
+ num_ifs = MAXIFS;
+ }
+ req_size = num_ifs * sizeof (struct ifreq);
+ req = malloc(req_size);
+ if (req == NULL) {
+ novm("interface request structure.");
+ }
+ /*
+ * Get interface configuration info for all interfaces
+ */
+ ifc.ifc_len = req_size;
+ ifc.ifc_buf = req;
+ if (myioctl(fd, SIOCGIFCONF, &ifc) < 0) {
+ error("SIOCGIFCONF: %m");
+ (void) close(fd);
+ free(req);
+ return (0);
+ }
+ /*
+ * And traverse each interface to look specifically for the first
+ * occurence of an Ethernet interface which has been marked up
+ */
+ pifreq = ifc.ifc_req;
+ found = 0;
+ for (i = ifc.ifc_len / sizeof (struct ifreq); i > 0; i--, pifreq++) {
+
+ if (strchr(pifreq->ifr_name, ':') != NULL) {
+ continue;
+ }
+ BZERO(&ifr, sizeof (ifr));
+ (void) strncpy(ifr.ifr_name, pifreq->ifr_name,
+ sizeof (ifr.ifr_name));
+ if (myioctl(fd, SIOCGIFFLAGS, &ifr) < 0) {
+ continue;
+ }
+ fl = ifr.ifr_flags;
+ if ((fl & (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT|IFF_LOOPBACK))
+ != (IFF_UP | IFF_BROADCAST)) {
+ continue;
+ }
+ if (get_if_hwaddr(addr, msize, ifr.ifr_name) <= 0) {
+ continue;
+ }
+ found = 1;
+ break;
+ }
+ free(req);
+ (void) close(fd);
+
+ return (found);
+}
+
+/*
+ * get_if_hwaddr()
+ *
+ * Get the hardware address for the specified network interface device.
+ * Return the length of the MAC address (in bytes) or -1 if error.
+ */
+int
+get_if_hwaddr(addr, msize, if_name)
+ uchar_t *addr;
+ int msize;
+ char *if_name;
+{
+ int unit, iffd, adrlen;
+ bool dlpi_err = 0;
+ char *adrp, *q;
+ char ifdev[4+LIFNAMSIZ+1]; /* take "/dev/" into account */
+ struct {
+ union DL_primitives prim;
+ char space[64];
+ } reply;
+
+ if ((addr == NULL) || (if_name == NULL) || (if_name[0] == '\0')) {
+ return (-1);
+ }
+ /*
+ * We have to open the device and ask it for its hardware address.
+ * First split apart the device name and unit.
+ */
+ (void) slprintf(ifdev, sizeof (ifdev), "/dev/%s", if_name);
+ for (q = ifdev + strlen(ifdev); --q >= ifdev; ) {
+ if (!isdigit(*q)) {
+ break;
+ }
+ }
+ unit = atoi(q + 1);
+ q[1] = '\0';
+ /*
+ * Open the device and do a DLPI attach and phys_addr_req.
+ */
+ iffd = open(ifdev, O_RDWR);
+ if (iffd < 0) {
+ error("Couldn't open %s: %m", ifdev);
+ return (-1);
+ }
+
+ if (dlpi_attach(iffd, unit) < 0) {
+ error("DLPI attach to device %s failed", ifdev);
+ dlpi_err = 1;
+ } else if (dlpi_get_reply(iffd, &reply.prim, DL_OK_ACK,
+ sizeof (reply)) < 0) {
+ error("DLPI get attach reply on device %s failed", ifdev);
+ dlpi_err = 1;
+ } else if (dlpi_info_req(iffd) < 0) {
+ error("DLPI info request on device %s failed", ifdev);
+ dlpi_err = 1;
+ } else if (dlpi_get_reply(iffd, &reply.prim, DL_INFO_ACK,
+ sizeof (reply)) < 0) {
+ error("DLPI get info request reply on device %s failed", ifdev);
+ dlpi_err = 1;
+ }
+ (void) close(iffd);
+ iffd = -1;
+ if (dlpi_err) {
+ return (-1);
+ }
+ adrlen = reply.prim.info_ack.dl_addr_length;
+ adrp = (caddr_t)&reply + reply.prim.info_ack.dl_addr_offset;
+
+ if (reply.prim.info_ack.dl_sap_length < 0) {
+ adrlen += reply.prim.info_ack.dl_sap_length;
+ } else {
+ adrp += reply.prim.info_ack.dl_sap_length;
+ }
+ /*
+ * Check if we have enough space to copy the address to.
+ */
+ if (adrlen > msize) {
+ return (-1);
+ }
+ (void) memcpy(addr, adrp, adrlen);
+ return (adrlen);
+}
+
+/*
+ * giflags()
+ */
+static int
+giflags(u_int32_t flag, bool *retval)
+{
+ struct ifreq ifr;
+ int fd;
+
+ *retval = 0;
+ fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd < 0) {
+ error("giflags: error opening IP socket: %m");
+ return (errno);
+ }
+
+ BZERO(&ifr, sizeof (ifr));
+ (void) strncpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
+ if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) {
+ (void) close(fd);
+ return (errno);
+ }
+
+ *retval = ((ifr.ifr_flags & flag) != 0);
+ (void) close(fd);
+ return (errno);
+}
+
+/*
+ * sys_close()
+ *
+ * Clean up in a child process before exec-ing.
+ */
+void
+sys_close()
+{
+ if (ipfd != -1) {
+ (void) close(ipfd);
+ ipfd = -1;
+ }
+#ifdef INET6
+ if (ip6fd != -1) {
+ (void) close(ip6fd);
+ ip6fd = -1;
+ }
+#endif /* INET6 */
+ if (pppfd != -1) {
+ (void) close(pppfd);
+ pppfd = -1;
+ }
+}
+
+/*
+ * any_compressions()
+ *
+ * Check if compression is enabled or not. In the STREAMS implementation of
+ * kernel-portion pppd, the comp STREAMS module performs the ACFC, PFC, as
+ * well CCP and VJ compressions. However, if the user has explicitly declare
+ * to not enable them from the command line, there is no point of having the
+ * comp module be pushed on the stream.
+ */
+static int
+any_compressions(void)
+{
+ if ((!lcp_wantoptions[0].neg_accompression) &&
+ (!lcp_wantoptions[0].neg_pcompression) &&
+ (!ccp_protent.enabled_flag) &&
+ (!ipcp_wantoptions[0].neg_vj)) {
+ return (0);
+ }
+ return (1);
+}
+
+/*
+ * modpush()
+ *
+ * Push a module on the stream.
+ */
+static int
+modpush(int fd, const char *modname, const char *text)
+{
+ if (myioctl(fd, I_PUSH, (void *)modname) < 0) {
+ error("Couldn't push %s module: %m", text);
+ return (-1);
+ }
+ if (++tty_npushed == 1 && !already_ppp) {
+ if (strioctl(fd, PPPIO_LASTMOD, NULL, 0, 0) < 0) {
+ warn("unable to set LASTMOD on %s: %m", text);
+ }
+ }
+ return (0);
+}
+
+/*
+ * establish_ppp()
+ *
+ * Turn the serial port into a ppp interface.
+ */
+int
+establish_ppp(fd)
+ int fd;
+{
+ int i;
+ uint32_t x;
+
+ if (default_device && !notty) {
+ tty_sid = getsid((pid_t)0);
+ }
+
+ if (integrated_driver)
+ return (pppfd);
+
+ /*
+ * Pop any existing modules off the tty stream
+ */
+ for (i = 0; ; ++i) {
+ if ((myioctl(fd, I_LOOK, tty_modules[i]) < 0) ||
+ (strcmp(tty_modules[i], "ptem") == 0) ||
+ (myioctl(fd, I_POP, (void *)0) < 0)) {
+ break;
+ }
+ }
+ tty_nmodules = i;
+ /*
+ * Push the async hdlc module and the compressor module
+ */
+ tty_npushed = 0;
+ if (!sync_serial && !already_ppp &&
+ modpush(fd, AHDLC_MOD_NAME, "PPP async HDLC") < 0) {
+ return (-1);
+ }
+ /*
+ * There's no need to push comp module if we don't intend
+ * to compress anything
+ */
+ if (any_compressions()) {
+ (void) modpush(fd, COMP_MOD_NAME, "PPP compression");
+ }
+
+ /*
+ * Link the serial port under the PPP multiplexor
+ */
+ if ((fdmuxid = myioctl(pppfd, I_LINK, (void *)fd)) < 0) {
+ error("Can't link tty to PPP mux: %m");
+ return (-1);
+ }
+ if (tty_npushed == 0 && !already_ppp) {
+ if (strioctl(pppfd, PPPIO_LASTMOD, NULL, 0, 0) < 0) {
+ warn("unable to set LASTMOD on PPP mux: %m");
+ }
+ }
+ /*
+ * Debug configuration must occur *after* I_LINK.
+ */
+ if (kdebugflag & 4) {
+ x = PPPDBG_LOG + PPPDBG_AHDLC;
+ if (strioctl(pppfd, PPPIO_DEBUG, &x, sizeof (x), 0) < 0) {
+ warn("PPPIO_DEBUG ioctl for ahdlc module failed: %m");
+ }
+ }
+ if (any_compressions() && (kdebugflag & 2)) {
+ x = PPPDBG_LOG + PPPDBG_COMP;
+ if (strioctl(pppfd, PPPIO_DEBUG, &x, sizeof (x), 0) < 0) {
+ warn("PPPIO_DEBUG ioctl for comp module failed: %m");
+ }
+ }
+ return (pppfd);
+}
+
+/*
+ * restore_loop()
+ *
+ * Reattach the ppp unit to the loopback. This doesn't need to do anything
+ * because disestablish_ppp does it
+ */
+void
+restore_loop()
+{
+}
+
+/*
+ * disestablish_ppp()
+ *
+ * Restore the serial port to normal operation. It attempts to reconstruct
+ * the stream with the previously popped modules. This shouldn't call die()
+ * because it's called from die(). Stream reconstruction is needed in case
+ * pppd is used for dial-in on /dev/tty and there's an option error.
+ */
+void
+disestablish_ppp(fd)
+ int fd;
+{
+ int i;
+
+ if (fdmuxid == -1 || integrated_driver) {
+ return;
+ }
+ if (myioctl(pppfd, I_UNLINK, (void *)fdmuxid) < 0) {
+ if (!hungup) {
+ error("Can't unlink tty from PPP mux: %m");
+ }
+ }
+ fdmuxid = -1;
+ if (!hungup) {
+ while (tty_npushed > 0 && myioctl(fd, I_POP, (void *)0) >= 0) {
+ --tty_npushed;
+ }
+ for (i = tty_nmodules - 1; i >= 0; --i) {
+ if (myioctl(fd, I_PUSH, tty_modules[i]) < 0) {
+ error("Couldn't restore tty module %s: %m",
+ tty_modules[i]);
+ }
+ }
+ }
+ if (hungup && default_device && tty_sid > 0) {
+ /*
+ * If we have received a hangup, we need to send a
+ * SIGHUP to the terminal's controlling process.
+ * The reason is that the original stream head for
+ * the terminal hasn't seen the M_HANGUP message
+ * (it went up through the ppp driver to the stream
+ * head for our fd to /dev/ppp).
+ */
+ (void) kill(tty_sid, SIGHUP);
+ }
+}
+
+/*
+ * clean_check()
+ *
+ * Check whether the link seems not to be 8-bit clean
+ */
+void
+clean_check()
+{
+ uint32_t x;
+ char *s = NULL;
+
+ /*
+ * Skip this is synchronous link is used, since spppasyn won't
+ * be anywhere in the stream below to handle the ioctl.
+ */
+ if (sync_serial) {
+ return;
+ }
+
+ if (strioctl(pppfd, PPPIO_GCLEAN, &x, 0, sizeof (x)) < 0) {
+ warn("unable to obtain serial link status: %m");
+ return;
+ }
+ switch (~x) {
+ case RCV_B7_0:
+ s = "bit 7 set to 1";
+ break;
+ case RCV_B7_1:
+ s = "bit 7 set to 0";
+ break;
+ case RCV_EVNP:
+ s = "odd parity";
+ break;
+ case RCV_ODDP:
+ s = "even parity";
+ break;
+ }
+ if (s != NULL) {
+ warn("Serial link is not 8-bit clean:");
+ warn("All received characters had %s", s);
+ }
+}
+
+/*
+ * List of valid speeds.
+ */
+struct speed {
+ int speed_int;
+ int speed_val;
+} speeds [] = {
+#ifdef B50
+ { 50, B50 },
+#endif
+#ifdef B75
+ { 75, B75 },
+#endif
+#ifdef B110
+ { 110, B110 },
+#endif
+#ifdef B134
+ { 134, B134 },
+#endif
+#ifdef B150
+ { 150, B150 },
+#endif
+#ifdef B200
+ { 200, B200 },
+#endif
+#ifdef B300
+ { 300, B300 },
+#endif
+#ifdef B600
+ { 600, B600 },
+#endif
+#ifdef B1200
+ { 1200, B1200 },
+#endif
+#ifdef B1800
+ { 1800, B1800 },
+#endif
+#ifdef B2000
+ { 2000, B2000 },
+#endif
+#ifdef B2400
+ { 2400, B2400 },
+#endif
+#ifdef B3600
+ { 3600, B3600 },
+#endif
+#ifdef B4800
+ { 4800, B4800 },
+#endif
+#ifdef B7200
+ { 7200, B7200 },
+#endif
+#ifdef B9600
+ { 9600, B9600 },
+#endif
+#ifdef B19200
+ { 19200, B19200 },
+#endif
+#ifdef B38400
+ { 38400, B38400 },
+#endif
+#ifdef EXTA
+ { 19200, EXTA },
+#endif
+#ifdef EXTB
+ { 38400, EXTB },
+#endif
+#ifdef B57600
+ { 57600, B57600 },
+#endif
+#ifdef B76800
+ { 76800, B76800 },
+#endif
+#ifdef B115200
+ { 115200, B115200 },
+#endif
+#ifdef B153600
+ { 153600, B153600 },
+#endif
+#ifdef B230400
+ { 230400, B230400 },
+#endif
+#ifdef B307200
+ { 307200, B307200 },
+#endif
+#ifdef B460800
+ { 460800, B460800 },
+#endif
+ { 0, 0 }
+};
+
+/*
+ * translate_speed()
+ *
+ * Translate from bits/second to a speed_t
+ */
+static int
+translate_speed(int bps)
+{
+ struct speed *speedp;
+
+ if (bps == 0) {
+ return (0);
+ }
+ for (speedp = speeds; speedp->speed_int; speedp++) {
+ if (bps == speedp->speed_int) {
+ return (speedp->speed_val);
+ }
+ }
+ set_source(&speed_info);
+ option_error("speed %d not supported", bps);
+ return (0);
+}
+
+/*
+ * baud_rate_of()
+ *
+ * Translate from a speed_t to bits/second
+ */
+static int
+baud_rate_of(int speed)
+{
+ struct speed *speedp;
+
+ if (speed == 0) {
+ return (0);
+ }
+ for (speedp = speeds; speedp->speed_int; speedp++) {
+ if (speed == speedp->speed_val) {
+ return (speedp->speed_int);
+ }
+ }
+ return (0);
+}
+
+/*
+ * set_up_tty()
+ *
+ * Set up the serial port on `fd' for 8 bits, no parity, at the requested
+ * speed, etc. If `local' is true, set CLOCAL regardless of whether the
+ * modem option was specified.
+ */
+void
+set_up_tty(fd, local)
+ int fd, local;
+{
+ int speed;
+ struct termios tios;
+ struct scc_mode sm;
+
+ if (already_ppp)
+ return;
+
+ if (sync_serial) {
+ restore_term = 0;
+ speed = B0;
+ baud_rate = 0;
+
+ if (strioctl(fd, S_IOCGETMODE, &sm, sizeof (sm),
+ sizeof (sm)) < 0) {
+ return;
+ }
+
+ baud_rate = sm.sm_baudrate;
+ dbglog("synchronous speed appears to be %d bps", baud_rate);
+ } else {
+ if (tcgetattr(fd, &tios) < 0) {
+ fatal("tcgetattr: %m");
+ }
+ if (!restore_term) {
+ inittermios = tios;
+ if (myioctl(fd, TIOCGWINSZ, &wsinfo) < 0) {
+ if (errno == EINVAL) {
+ /*
+ * ptem returns EINVAL if all zeroes.
+ * Strange and unfixable code.
+ */
+ bzero(&wsinfo, sizeof (wsinfo));
+ } else {
+ warn("unable to get TTY window "
+ "size: %m");
+ }
+ }
+ }
+ tios.c_cflag &= ~(CSIZE | CSTOPB | PARENB | CLOCAL);
+ if (crtscts > 0) {
+ tios.c_cflag |= CRTSCTS | CRTSXOFF;
+ } else if (crtscts < 0) {
+ tios.c_cflag &= ~CRTSCTS & ~CRTSXOFF;
+ }
+ tios.c_cflag |= CS8 | CREAD | HUPCL;
+ if (local || !modem) {
+ tios.c_cflag |= CLOCAL;
+ }
+ tios.c_iflag = IGNBRK | IGNPAR;
+ tios.c_oflag = 0;
+ tios.c_lflag = 0;
+ tios.c_cc[VMIN] = 1;
+ tios.c_cc[VTIME] = 0;
+
+ if (crtscts == -2) {
+ tios.c_iflag |= IXON | IXOFF;
+ tios.c_cc[VSTOP] = 0x13; /* DC3 = XOFF = ^S */
+ tios.c_cc[VSTART] = 0x11; /* DC1 = XON = ^Q */
+ }
+ speed = translate_speed(inspeed);
+ if (speed) {
+ (void) cfsetospeed(&tios, speed);
+ (void) cfsetispeed(&tios, speed);
+ } else {
+ speed = cfgetospeed(&tios);
+ /*
+ * We can't proceed if the serial port speed is 0,
+ * since that implies that the serial port is disabled.
+ */
+ if (speed == B0) {
+ fatal("Baud rate for %s is 0; need explicit "
+ "baud rate", devnam);
+ }
+ }
+ if (tcsetattr(fd, TCSAFLUSH, &tios) < 0) {
+ fatal("tcsetattr: %m");
+ }
+ baud_rate = baud_rate_of(speed);
+ dbglog("%s speed set to %d bps",
+ fd == pty_slave ? "pty" : "serial", baud_rate);
+ restore_term = 1;
+ }
+}
+
+/*
+ * restore_tty()
+ *
+ * Restore the terminal to the saved settings.
+ */
+void
+restore_tty(fd)
+ int fd;
+{
+ if (restore_term == 0) {
+ return;
+ }
+ if (!default_device) {
+ /*
+ * Turn off echoing, because otherwise we can get into
+ * a loop with the tty and the modem echoing to each
+ * other. We presume we are the sole user of this tty
+ * device, so when we close it, it will revert to its
+ * defaults anyway.
+ */
+ inittermios.c_lflag &= ~(ECHO | ECHONL);
+ }
+ if (tcsetattr(fd, TCSAFLUSH, &inittermios) < 0) {
+ if (!hungup && errno != ENXIO) {
+ warn("tcsetattr: %m");
+ }
+ }
+ if (wsinfo.ws_row != 0 || wsinfo.ws_col != 0 ||
+ wsinfo.ws_xpixel != 0 || wsinfo.ws_ypixel != 0) {
+ if (myioctl(fd, TIOCSWINSZ, &wsinfo) < 0) {
+ warn("unable to set TTY window size: %m");
+ }
+ }
+ restore_term = 0;
+}
+
+/*
+ * setdtr()
+ *
+ * Control the DTR line on the serial port. This is called from die(), so it
+ * shouldn't call die()
+ */
+void
+setdtr(fd, on)
+ int fd, on;
+{
+ int modembits = TIOCM_DTR;
+ if (!already_ppp &&
+ myioctl(fd, (on ? TIOCMBIS : TIOCMBIC), &modembits) < 0) {
+ warn("unable to set DTR line %s: %m", (on ? "ON" : "OFF"));
+ }
+}
+
+/*
+ * open_loopback()
+ *
+ * Open the device we use for getting packets in demand mode. Under Solaris 2,
+ * we use our existing fd to the ppp driver.
+ */
+int
+open_ppp_loopback()
+{
+ /*
+ * Plumb the interface.
+ */
+ if (IPCP_ENABLED && (plumb_ipif(0) == 0)) {
+ fatal("Unable to initialize IP interface for demand dial.");
+ }
+#ifdef INET6
+ if (IPV6CP_ENABLED && (plumb_ip6if(0) == 0)) {
+ fatal("Unable to initialize IPv6 interface for demand dial.");
+ }
+#endif /* INET6 */
+
+ return (pppfd);
+}
+
+/*
+ * output()
+ *
+ * Output PPP packet downstream
+ */
+/*ARGSUSED*/
+void
+output(unit, p, len)
+ int unit;
+ uchar_t *p;
+ int len;
+{
+ struct strbuf data;
+ struct pollfd pfd;
+ int retries, n;
+ bool sent_ok = 1;
+
+ data.len = len;
+ data.buf = (caddr_t)p;
+ retries = 4;
+
+ while (putmsg(pppfd, NULL, &data, 0) < 0) {
+ if (errno == EINTR)
+ continue;
+ if (--retries < 0 ||
+ (errno != EWOULDBLOCK && errno != EAGAIN)) {
+ if (errno != ENXIO) {
+ error("Couldn't send packet: %m");
+ sent_ok = 0;
+ }
+ break;
+ }
+ pfd.fd = pppfd;
+ pfd.events = POLLOUT;
+ do {
+ /* wait for up to 0.25 seconds */
+ n = poll(&pfd, 1, 250);
+ } while ((n == -1) && (errno == EINTR));
+ }
+ if (debug && sent_ok) {
+ dbglog("sent %P", p, len);
+ }
+}
+
+/*
+ * wait_input()
+ *
+ * Wait until there is data available, for the length of time specified by
+ * timo (indefinite if timo is NULL).
+ */
+void
+wait_input(timo)
+ struct timeval *timo;
+{
+ int t;
+
+ t = (timo == NULL ? -1 : (timo->tv_sec * 1000 + timo->tv_usec / 1000));
+ if ((poll(pollfds, n_pollfds, t) < 0) && (errno != EINTR)) {
+ fatal("poll: %m");
+ }
+}
+
+/*
+ * add_fd()
+ *
+ * Add an fd to the set that wait_input waits for.
+ */
+void
+add_fd(fd)
+ int fd;
+{
+ int n;
+
+ if (fd < 0) {
+ return;
+ }
+ for (n = 0; n < n_pollfds; ++n) {
+ if (pollfds[n].fd == fd) {
+ return;
+ }
+ }
+ if (n_pollfds < MAX_POLLFDS) {
+ pollfds[n_pollfds].fd = fd;
+ pollfds[n_pollfds].events = POLLIN | POLLPRI | POLLHUP;
+ ++n_pollfds;
+ } else {
+ fatal("add_fd: too many inputs!");
+ }
+}
+
+/*
+ * remove_fd()
+ *
+ * Remove an fd from the set that wait_input waits for.
+ */
+void
+remove_fd(fd)
+ int fd;
+{
+ int n;
+
+ for (n = 0; n < n_pollfds; ++n) {
+ if (pollfds[n].fd == fd) {
+ while (++n < n_pollfds) {
+ pollfds[n-1] = pollfds[n];
+ }
+ --n_pollfds;
+ break;
+ }
+ }
+}
+
+static void
+dump_packet(uchar_t *buf, int len)
+{
+ uchar_t *bp;
+ int proto, offs;
+ const char *cp;
+ char sbuf[32];
+ uint32_t src, dst;
+ struct protoent *pep;
+
+ if (len < 4) {
+ dbglog("strange link activity: %.*B", len, buf);
+ return;
+ }
+ bp = buf;
+ if (bp[0] == 0xFF && bp[1] == 0x03)
+ bp += 2;
+ proto = *bp++;
+ if (!(proto & 1))
+ proto = (proto << 8) + *bp++;
+ len -= bp-buf;
+ if (proto == PPP_IP) {
+ if (len < 20 || get_ipv(bp) != 4 || get_iphl(bp) < 5) {
+ dbglog("strange IP packet activity: %16.*B", len, buf);
+ return;
+ }
+ src = get_ipsrc(bp);
+ dst = get_ipdst(bp);
+ proto = get_ipproto(bp);
+ if ((pep = getprotobynumber(proto)) != NULL) {
+ cp = pep->p_name;
+ } else {
+ (void) slprintf(sbuf, sizeof (sbuf), "IP proto %d",
+ proto);
+ cp = sbuf;
+ }
+ if ((get_ipoff(bp) & IP_OFFMASK) != 0) {
+ len -= get_iphl(bp) * 4;
+ bp += get_iphl(bp) * 4;
+ dbglog("%s fragment from %I->%I: %8.*B", cp, src, dst,
+ len, bp);
+ } else {
+ if (len > get_iplen(bp))
+ len = get_iplen(bp);
+ len -= get_iphl(bp) * 4;
+ bp += get_iphl(bp) * 4;
+ offs = proto == IPPROTO_TCP ? (get_tcpoff(bp)*4) : 8;
+ if (proto == IPPROTO_TCP || proto == IPPROTO_UDP)
+ dbglog("%s data:%d %I:%d->%I:%d: %8.*B", cp,
+ len-offs, src, get_sport(bp), dst,
+ get_dport(bp), len-offs, bp+offs);
+ else
+ dbglog("%s %d bytes %I->%I: %8.*B", cp, len,
+ src, dst, len, bp);
+ }
+ return;
+ }
+ if ((cp = protocol_name(proto)) == NULL) {
+ (void) slprintf(sbuf, sizeof (sbuf), "0x#X", proto);
+ cp = (const char *)sbuf;
+ }
+ dbglog("link activity: %s %16.*B", cp, len, bp);
+}
+
+/*
+ * handle_bind()
+ */
+static void
+handle_bind(u_int32_t reason)
+{
+ /*
+ * Here we might, in the future, handle DL_BIND_REQ notifications
+ * in order to close and re-open a NCP when certain interface
+ * parameters (addresses, etc.) are changed via external mechanisms
+ * such as through the "ifconfig" program.
+ */
+ switch (reason) {
+ case PPP_LINKSTAT_IPV4_BOUND:
+ break;
+#ifdef INET6
+ case PPP_LINKSTAT_IPV6_BOUND:
+ break;
+#endif
+ default:
+ error("handle_bind: unrecognized reason");
+ break;
+ }
+}
+
+/*
+ * handle_unbind()
+ */
+static void
+handle_unbind(u_int32_t reason)
+{
+ bool iff_up_isset;
+ int rc;
+ static const char *unplumb_str = "unplumbed";
+ static const char *down_str = "downed";
+
+ /*
+ * Since the kernel driver (sppp) notifies this daemon of the
+ * DLPI bind/unbind activities (for the purpose of bringing down
+ * a NCP), we need to explicitly test the "actual" status of
+ * the interface instance for which the notification is destined
+ * from. This is because /dev/ip performs multiple DLPI attach-
+ * bind-unbind-detach during the early life of the interface,
+ * and when certain interface parameters change. A DL_UNBIND_REQ
+ * coming down to the sppp driver from /dev/ip (which results in
+ * our receiving of the PPP_LINKSTAT_*_UNBOUND link status message)
+ * is not enough to conclude that the interface has been marked
+ * DOWN (its IFF_UP bit is cleared) or is going away. Therefore,
+ * we should query /dev/ip directly, upon receiving such *_UNBOUND
+ * notification, to determine whether the interface is DOWN
+ * for real, and only take the necessary actions when IFF_UP
+ * bit for the interface instance is actually cleared.
+ */
+ switch (reason) {
+ case PPP_LINKSTAT_IPV4_UNBOUND:
+ (void) sleep(1);
+ rc = giflags(IFF_UP, &iff_up_isset);
+ if (!iff_up_isset) {
+ if_is_up = 0;
+ ipmuxid = -1;
+ info("IPv4 interface %s by administrator",
+ ((rc < 0 && rc == ENXIO) ? unplumb_str : down_str));
+ fsm_close(&ipcp_fsm[0],
+ "administratively disconnected");
+ }
+ break;
+#ifdef INET6
+ case PPP_LINKSTAT_IPV6_UNBOUND:
+ (void) sleep(1);
+ rc = giflags(IFF_UP, &iff_up_isset);
+ if (!iff_up_isset) {
+ if6_is_up = 0;
+ ip6muxid = -1;
+ info("IPv6 interface %s by administrator",
+ ((rc < 0 && rc == ENXIO) ? unplumb_str : down_str));
+ fsm_close(&ipv6cp_fsm[0],
+ "administratively disconnected");
+ }
+ break;
+#endif
+ default:
+ error("handle_unbind: unrecognized reason");
+ break;
+ }
+}
+
+/*
+ * read_packet()
+ *
+ * Get a PPP packet from the serial device.
+ */
+int
+read_packet(buf)
+ uchar_t *buf;
+{
+ struct strbuf ctrl;
+ struct strbuf data;
+ int flags;
+ int len;
+ int rc;
+ struct ppp_ls *plp;
+ uint32_t ctrlbuf[1536 / sizeof (uint32_t)];
+ bool flushmode;
+
+ flushmode = 0;
+ for (;;) {
+
+ data.maxlen = PPP_MRU + PPP_HDRLEN;
+ data.buf = (caddr_t)buf;
+
+ ctrl.maxlen = sizeof (ctrlbuf);
+ ctrl.buf = (caddr_t)ctrlbuf;
+
+ flags = 0;
+ rc = len = getmsg(pppfd, &ctrl, &data, &flags);
+ if (sys_read_packet_hook != NULL) {
+ rc = len = (*sys_read_packet_hook)(len, &ctrl, &data,
+ flags);
+ }
+ if (len < 0) {
+ if (errno == EAGAIN || errno == EINTR) {
+ return (-1);
+ }
+ fatal("Error reading packet: %m");
+ }
+ if ((data.len > 0) && (ctrl.len < 0)) {
+ /*
+ * If there's more data on stream head, keep reading
+ * but discard, since the stream is now corrupt.
+ */
+ if (rc & MOREDATA) {
+ dbglog("More data; input packet garbled");
+ flushmode = 1;
+ continue;
+ }
+ if (flushmode)
+ return (-1);
+ return (data.len);
+
+ } else if (ctrl.len > 0) {
+ /*
+ * If there's more ctl on stream head, keep reading,
+ * but start discarding. We can't deal with fragmented
+ * messages at all.
+ */
+ if (rc & MORECTL) {
+ dbglog("More control; stream garbled");
+ flushmode = 1;
+ continue;
+ }
+ if (flushmode)
+ return (-1);
+ if (ctrl.len < sizeof (struct ppp_ls)) {
+ warn("read_packet: ctl.len %d < "
+ "sizeof ppp_ls %d",
+ ctrl.len, sizeof (struct ppp_ls));
+ return (-1);
+ }
+ plp = (struct ppp_ls *)ctrlbuf;
+ if (plp->magic != PPPLSMAGIC) {
+ /* Skip, as we don't understand it */
+ dbglog("read_packet: unrecognized control %lX",
+ plp->magic);
+ return (-1);
+ }
+
+ lastlink_status = plp->ppp_message;
+
+ switch (plp->ppp_message) {
+ case PPP_LINKSTAT_HANGUP:
+ return (0); /* Hangup */
+ /* For use by integrated drivers. */
+ case PPP_LINKSTAT_UP:
+ lcp_lowerdown(0);
+ lcp_lowerup(0);
+ return (0);
+ case PPP_LINKSTAT_NEEDUP:
+ if (data.len > 0 && debug)
+ dump_packet(buf, data.len);
+ return (-1); /* Demand dial */
+ case PPP_LINKSTAT_IPV4_UNBOUND:
+ (void) handle_unbind(plp->ppp_message);
+ return (-1);
+ case PPP_LINKSTAT_IPV4_BOUND:
+ (void) handle_bind(plp->ppp_message);
+ return (-1);
+#ifdef INET6
+ case PPP_LINKSTAT_IPV6_UNBOUND:
+ (void) handle_unbind(plp->ppp_message);
+ return (-1);
+ case PPP_LINKSTAT_IPV6_BOUND:
+ (void) handle_bind(plp->ppp_message);
+ return (-1);
+#endif
+ default:
+ warn("read_packet: unknown link status type!");
+ return (-1);
+ }
+ } else {
+ /*
+ * We get here on zero length data or control.
+ */
+ return (-1);
+ }
+ }
+}
+
+/*
+ * get_loop_output()
+ *
+ * Get outgoing packets from the ppp device, and detect when we want to bring
+ * the real link up. Return value is 1 if we need to bring up the link, or 0
+ * otherwise.
+ */
+int
+get_loop_output()
+{
+ int loops;
+
+ /*
+ * In the Solaris 2.x kernel-level portion implementation, packets
+ * which are received on a demand-dial interface are immediately
+ * discarded, and a notification message is sent up the control
+ * stream to the pppd process. Therefore, the call to read_packet()
+ * below is merely there to wait for such message.
+ */
+ lastlink_status = 0;
+ loops = 0;
+ while (read_packet(inpacket_buf) > 0) {
+ if (++loops > 10)
+ break;
+ }
+ return (lastlink_status == PPP_LINKSTAT_NEEDUP);
+}
+
+#ifdef MUX_FRAME
+/*ARGSUSED*/
+void
+ppp_send_muxoption(unit, muxflag)
+ int unit;
+ u_int32_t muxflag;
+{
+ uint32_t cf[2];
+
+ /*
+ * Since muxed frame feature is implemented in the async module,
+ * don't send down the ioctl in the synchronous case.
+ */
+ if (!sync_serial && fdmuxid >= 0 && pppfd != -1) {
+ cf[0] = muxflag;
+ cf[1] = X_MUXMASK;
+
+ if (strioctl(pppfd, PPPIO_MUX, cf, sizeof (cf), 0) < 0) {
+ error("Couldn't set mux option: %m");
+ }
+ }
+}
+
+/*ARGSUSED*/
+void
+ppp_recv_muxoption(unit, muxflag)
+ int unit;
+ u_int32_t muxflag;
+{
+ uint32_t cf[2];
+
+ /*
+ * Since muxed frame feature is implemented in the async module,
+ * don't send down the ioctl in the synchronous case.
+ */
+ if (!sync_serial && fdmuxid >= 0 && pppfd != -1) {
+ cf[0] = muxflag;
+ cf[1] = R_MUXMASK;
+
+ if (strioctl(pppfd, PPPIO_MUX, cf, sizeof (cf), 0) < 0) {
+ error("Couldn't set receive mux option: %m");
+ }
+ }
+}
+#endif
+
+/*
+ * ppp_send_config()
+ *
+ * Configure the transmit characteristics of the ppp interface.
+ */
+/*ARGSUSED*/
+void
+ppp_send_config(unit, mtu, asyncmap, pcomp, accomp)
+ int unit;
+ int mtu;
+ u_int32_t asyncmap;
+ int pcomp;
+ int accomp;
+{
+ uint32_t cf[2];
+
+ if (pppfd == -1) {
+ error("ppp_send_config called with invalid device handle");
+ return;
+ }
+ cf[0] = link_mtu = mtu;
+ if (strioctl(pppfd, PPPIO_MTU, cf, sizeof (cf[0]), 0) < 0) {
+ if (hungup && errno == ENXIO) {
+ return;
+ }
+ error("Couldn't set MTU: %m");
+ }
+ if (fdmuxid != -1) {
+ if (!sync_serial) {
+ if (strioctl(pppfd, PPPIO_XACCM, &asyncmap,
+ sizeof (asyncmap), 0) < 0) {
+ error("Couldn't set transmit ACCM: %m");
+ }
+ }
+ cf[0] = (pcomp? COMP_PROT: 0) + (accomp? COMP_AC: 0);
+ cf[1] = COMP_PROT | COMP_AC;
+
+ if (any_compressions() && strioctl(pppfd, PPPIO_CFLAGS, cf,
+ sizeof (cf), sizeof (cf[0])) < 0) {
+ error("Couldn't set prot/AC compression: %m");
+ }
+ }
+}
+
+/*
+ * ppp_set_xaccm()
+ *
+ * Set the extended transmit ACCM for the interface.
+ */
+/*ARGSUSED*/
+void
+ppp_set_xaccm(unit, accm)
+ int unit;
+ ext_accm accm;
+{
+ if (sync_serial) {
+ return;
+ }
+ if (fdmuxid != -1 && strioctl(pppfd, PPPIO_XACCM, accm,
+ sizeof (ext_accm), 0) < 0) {
+ if (!hungup || errno != ENXIO) {
+ warn("Couldn't set extended ACCM: %m");
+ }
+ }
+}
+
+/*
+ * ppp_recv_config()
+ *
+ * Configure the receive-side characteristics of the ppp interface.
+ */
+/*ARGSUSED*/
+void
+ppp_recv_config(unit, mru, asyncmap, pcomp, accomp)
+ int unit;
+ int mru;
+ u_int32_t asyncmap;
+ int pcomp;
+ int accomp;
+{
+ uint32_t cf[2];
+
+ if (pppfd == -1) {
+ error("ppp_recv_config called with invalid device handle");
+ return;
+ }
+ cf[0] = mru;
+ if (strioctl(pppfd, PPPIO_MRU, cf, sizeof (cf[0]), 0) < 0) {
+ if (hungup && errno == ENXIO) {
+ return;
+ }
+ error("Couldn't set MRU: %m");
+ }
+ if (fdmuxid != -1) {
+ if (!sync_serial) {
+ if (strioctl(pppfd, PPPIO_RACCM, &asyncmap,
+ sizeof (asyncmap), 0) < 0) {
+ error("Couldn't set receive ACCM: %m");
+ }
+ }
+ cf[0] = (pcomp ? DECOMP_PROT : 0) + (accomp ? DECOMP_AC : 0);
+ cf[1] = DECOMP_PROT | DECOMP_AC;
+
+ if (any_compressions() && strioctl(pppfd, PPPIO_CFLAGS, cf,
+ sizeof (cf), sizeof (cf[0])) < 0) {
+ error("Couldn't set prot/AC decompression: %m");
+ }
+ }
+}
+
+#ifdef NEGOTIATE_FCS
+/*
+ * ppp_send_fcs()
+ *
+ * Configure the sender-side FCS.
+ */
+/*ARGSUSED*/
+void
+ppp_send_fcs(unit, fcstype)
+ int unit, fcstype;
+{
+ uint32_t fcs;
+
+ if (sync_serial) {
+ return;
+ }
+
+ if (fcstype & FCSALT_32) {
+ fcs = PPPFCS_32;
+ } else if (fcstype & FCSALT_NULL) {
+ fcs = PPPFCS_NONE;
+ } else {
+ fcs = PPPFCS_16;
+ }
+ if (strioctl(pppfd, PPPIO_XFCS, &fcs, sizeof (fcs), 0) < 0) {
+ warn("Couldn't set transmit FCS: %m");
+ }
+}
+
+/*
+ * ppp_recv_fcs()
+ *
+ * Configure the receiver-side FCS.
+ */
+/*ARGSUSED*/
+void
+ppp_recv_fcs(unit, fcstype)
+ int unit, fcstype;
+{
+ uint32_t fcs;
+
+ if (sync_serial) {
+ return;
+ }
+
+ if (fcstype & FCSALT_32) {
+ fcs = PPPFCS_32;
+ } else if (fcstype & FCSALT_NULL) {
+ fcs = PPPFCS_NONE;
+ } else {
+ fcs = PPPFCS_16;
+ }
+ if (strioctl(pppfd, PPPIO_RFCS, &fcs, sizeof (fcs), 0) < 0) {
+ warn("Couldn't set receive FCS: %m");
+ }
+}
+#endif
+
+/*
+ * ccp_test()
+ *
+ * Ask kernel whether a given compression method is acceptable for use.
+ */
+/*ARGSUSED*/
+int
+ccp_test(unit, opt_ptr, opt_len, for_transmit)
+ int unit;
+ uchar_t *opt_ptr;
+ int opt_len;
+ int for_transmit;
+{
+ if (strioctl(pppfd, (for_transmit ? PPPIO_XCOMP : PPPIO_RCOMP),
+ opt_ptr, opt_len, 0) >= 0) {
+ return (1);
+ }
+ warn("Error in %s ioctl: %m",
+ (for_transmit ? "PPPIO_XCOMP" : "PPPIO_RCOMP"));
+ return ((errno == ENOSR) ? 0 : -1);
+}
+
+#ifdef COMP_TUNE
+/*
+ * ccp_tune()
+ *
+ * Tune compression effort level.
+ */
+/*ARGSUSED*/
+void
+ccp_tune(unit, effort)
+ int unit, effort;
+{
+ uint32_t x;
+
+ x = effort;
+ if (strioctl(pppfd, PPPIO_COMPLEV, &x, sizeof (x), 0) < 0) {
+ warn("unable to set compression effort level: %m");
+ }
+}
+#endif
+
+/*
+ * ccp_flags_set()
+ *
+ * Inform kernel about the current state of CCP.
+ */
+/*ARGSUSED*/
+void
+ccp_flags_set(unit, isopen, isup)
+ int unit, isopen, isup;
+{
+ uint32_t cf[2];
+
+ cf[0] = (isopen ? CCP_ISOPEN : 0) + (isup ? CCP_ISUP : 0);
+ cf[1] = CCP_ISOPEN | CCP_ISUP | CCP_ERROR | CCP_FATALERROR;
+
+ if (strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof (cf), sizeof (cf[0]))
+ < 0) {
+ if (!hungup || errno != ENXIO) {
+ error("Couldn't set kernel CCP state: %m");
+ }
+ }
+}
+
+/*
+ * get_idle_time()
+ *
+ * Return how long the link has been idle.
+ */
+/*ARGSUSED*/
+int
+get_idle_time(u, pids)
+ int u;
+ struct ppp_idle *pids;
+{
+ int rc;
+
+ rc = strioctl(pppfd, PPPIO_GIDLE, pids, 0, sizeof (struct ppp_idle));
+ if (rc < 0) {
+ warn("unable to obtain idle time: %m");
+ }
+ return ((rc == 0) ? 1 : 0);
+}
+
+/*
+ * get_ppp_stats()
+ *
+ * Return statistics for the link.
+ */
+/*ARGSUSED*/
+int
+get_ppp_stats(u, stats)
+ int u;
+ struct pppd_stats *stats;
+{
+ struct ppp_stats64 s64;
+ struct ppp_stats s;
+
+ /* Try first to get these from the 64-bit interface */
+ if (strioctl(pppfd, PPPIO_GETSTAT64, &s64, 0, sizeof (s64)) >= 0) {
+ stats->bytes_in = s64.p.ppp_ibytes;
+ stats->bytes_out = s64.p.ppp_obytes;
+ stats->pkts_in = s64.p.ppp_ipackets;
+ stats->pkts_out = s64.p.ppp_opackets;
+ return (1);
+ }
+
+ if (strioctl(pppfd, PPPIO_GETSTAT, &s, 0, sizeof (s)) < 0) {
+ error("Couldn't get link statistics: %m");
+ return (0);
+ }
+ stats->bytes_in = s.p.ppp_ibytes;
+ stats->bytes_out = s.p.ppp_obytes;
+ stats->pkts_in = s.p.ppp_ipackets;
+ stats->pkts_out = s.p.ppp_opackets;
+ return (1);
+}
+
+#if defined(FILTER_PACKETS)
+/*
+ * set_filters()
+ *
+ * Transfer the pass and active filters to the kernel.
+ */
+int
+set_filters(pass, active)
+ struct bpf_program *pass;
+ struct bpf_program *active;
+{
+ int ret = 1;
+
+ if (pass->bf_len > 0) {
+ if (strioctl(pppfd, PPPIO_PASSFILT, pass,
+ sizeof (struct bpf_program), 0) < 0) {
+ error("Couldn't set pass-filter in kernel: %m");
+ ret = 0;
+ }
+ }
+ if (active->bf_len > 0) {
+ if (strioctl(pppfd, PPPIO_ACTIVEFILT, active,
+ sizeof (struct bpf_program), 0) < 0) {
+ error("Couldn't set active-filter in kernel: %m");
+ ret = 0;
+ }
+ }
+ return (ret);
+}
+#endif /* FILTER_PACKETS */
+
+/*
+ * ccp_fatal_error()
+ *
+ * Returns 1 if decompression was disabled as a result of an error detected
+ * after decompression of a packet, 0 otherwise. This is necessary because
+ * of patent nonsense.
+ */
+/*ARGSUSED*/
+int
+ccp_fatal_error(unit)
+ int unit;
+{
+ uint32_t cf[2];
+
+ cf[0] = cf[1] = 0;
+ if (strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof (cf), sizeof (cf[0]))
+ < 0) {
+ if (errno != ENXIO && errno != EINVAL) {
+ error("Couldn't get compression flags: %m");
+ }
+ return (0);
+ }
+ return (cf[0] & CCP_FATALERROR);
+}
+
+/*
+ * sifvjcomp()
+ *
+ * Config TCP header compression.
+ */
+/*ARGSUSED*/
+int
+sifvjcomp(u, vjcomp, xcidcomp, xmaxcid)
+ int u, vjcomp, xcidcomp, xmaxcid;
+{
+ uint32_t cf[2];
+ uchar_t maxcid[2];
+
+ /*
+ * Since VJ compression code is in the comp module, there's no
+ * point of sending down any ioctls pertaining to VJ compression
+ * when the module isn't pushed on the stream.
+ */
+ if (!any_compressions()) {
+ return (1);
+ }
+
+ if (vjcomp) {
+ maxcid[0] = xcidcomp;
+ maxcid[1] = 15; /* XXX should be rmaxcid */
+
+ if (strioctl(pppfd, PPPIO_VJINIT, maxcid,
+ sizeof (maxcid), 0) < 0) {
+ error("Couldn't initialize VJ compression: %m");
+ return (0);
+ }
+ }
+
+ cf[0] = (vjcomp ? COMP_VJC + DECOMP_VJC : 0) /* XXX this is wrong */
+ + (xcidcomp? COMP_VJCCID + DECOMP_VJCCID: 0);
+
+ cf[1] = COMP_VJC + DECOMP_VJC + COMP_VJCCID + DECOMP_VJCCID;
+
+ if (strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof (cf), sizeof (cf[0]))
+ < 0) {
+ if (vjcomp) {
+ error("Couldn't enable VJ compression: %m");
+ } else {
+ error("Couldn't disable VJ compression: %m");
+ }
+ return (0);
+ }
+ return (1);
+}
+
+/*
+ * siflags()
+ *
+ * Set or clear the IP interface flags.
+ */
+int
+siflags(f, set)
+ u_int32_t f;
+ int set;
+{
+ struct ifreq ifr;
+
+ if (!IPCP_ENABLED || (ipmuxid == -1)) {
+ return (0);
+ }
+ if (ipfd == -1 && open_ipfd() == -1)
+ return (0);
+ BZERO(&ifr, sizeof (ifr));
+ (void) strlcpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
+ if (myioctl(ipfd, SIOCGIFFLAGS, &ifr) < 0) {
+ error("Couldn't get IP interface flags: %m");
+ return (0);
+ }
+ if (set) {
+ ifr.ifr_flags |= f;
+ } else {
+ ifr.ifr_flags &= ~f;
+ }
+ if (myioctl(ipfd, SIOCSIFFLAGS, &ifr) < 0) {
+ error("Couldn't set IP interface flags: %m");
+ return (0);
+ }
+ return (1);
+}
+
+/*
+ * sifup()
+ *
+ * Config the interface up and enable IP packets to pass.
+ */
+/*ARGSUSED*/
+int
+sifup(u)
+ int u;
+{
+ if (if_is_up) {
+ return (1);
+ } else if (!IPCP_ENABLED) {
+ warn("sifup called when IPCP is disabled");
+ return (0);
+ } else if (ipmuxid == -1) {
+ warn("sifup called in wrong state");
+ return (0);
+ } else if (!siflags(IFF_UP, 1)) {
+ error("Unable to mark the IP interface UP");
+ return (0);
+ }
+ if_is_up = 1;
+ return (1);
+}
+
+/*
+ * sifdown()
+ *
+ * Config the interface down and disable IP. Possibly called from die(),
+ * so there shouldn't be any call to die() here.
+ */
+/*ARGSUSED*/
+int
+sifdown(u)
+ int u;
+{
+ if (!IPCP_ENABLED) {
+ warn("sifdown called when IPCP is disabled");
+ return (0);
+ } else if (!if_is_up || (ipmuxid == -1)) {
+ return (1);
+ } else if (!siflags(IFF_UP, 0)) {
+ error("Unable to mark the IP interface DOWN");
+ return (0);
+ }
+ if_is_up = 0;
+ return (1);
+}
+
+/*
+ * sifnpmode()
+ *
+ * Set the mode for handling packets for a given NP. Not worried
+ * about performance here since this is done only rarely.
+ */
+/*ARGSUSED*/
+int
+sifnpmode(u, proto, mode)
+ int u;
+ int proto;
+ enum NPmode mode;
+{
+ uint32_t npi[2];
+ const char *cp;
+ static const struct npi_entry {
+ enum NPmode ne_value;
+ const char *ne_name;
+ } npi_list[] = {
+ { NPMODE_PASS, "pass" },
+ { NPMODE_DROP, "drop" },
+ { NPMODE_ERROR, "error" },
+ { NPMODE_QUEUE, "queue" },
+ };
+ int i;
+ char pname[32], mname[32];
+
+ npi[0] = proto;
+ npi[1] = (uint32_t)mode;
+
+ cp = protocol_name(proto);
+ if (cp == NULL)
+ (void) slprintf(pname, sizeof (pname), "NP %04X", proto);
+ else
+ (void) strlcpy(pname, cp, sizeof (pname));
+ for (i = 0; i < Dim(npi_list); i++)
+ if (npi_list[i].ne_value == mode)
+ break;
+ if (i >= Dim(npi_list))
+ (void) slprintf(mname, sizeof (mname), "mode %d", (int)mode);
+ else
+ (void) strlcpy(mname, npi_list[i].ne_name, sizeof (mname));
+
+ if ((proto == PPP_IP && !if_is_up) ||
+ (proto == PPP_IPV6 && !if6_is_up)) {
+ dbglog("ignoring request to set %s to %s", pname, mname);
+ return (1);
+ }
+ if (strioctl(pppfd, PPPIO_NPMODE, npi, sizeof (npi), 0) < 0) {
+ error("unable to set %s to %s: %m", pname, mname);
+ return (0);
+ }
+ return (1);
+}
+
+/*
+ * sifmtu()
+ *
+ * Config the interface IP MTU.
+ */
+int
+sifmtu(mtu)
+ int mtu;
+{
+ struct ifreq ifr;
+
+ if (!IPCP_ENABLED || (ipmuxid == -1)) {
+ return (0);
+ }
+ if (ipfd == -1 && open_ipfd() == -1)
+ return (0);
+ BZERO(&ifr, sizeof (ifr));
+ (void) strlcpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
+ ifr.ifr_metric = mtu;
+ if (myioctl(ipfd, SIOCSIFMTU, &ifr) < 0) {
+ error("Couldn't set IP MTU on %s to %d: %m", ifr.ifr_name,
+ mtu);
+ return (0);
+ }
+ return (1);
+}
+
+/*
+ * sifaddr()
+ *
+ * Config the interface IP addresses and netmask.
+ */
+/*ARGSUSED*/
+int
+sifaddr(u, o, h, m)
+ int u;
+ u_int32_t o;
+ u_int32_t h;
+ u_int32_t m;
+{
+ struct ifreq ifr;
+ struct sockaddr_in sin;
+
+ if (!IPCP_ENABLED || (ipmuxid == -1 && plumb_ipif(u) == 0)) {
+ return (0);
+ }
+ if (ipfd == -1 && open_ipfd() == -1)
+ return (0);
+ /*
+ * Set the IP interface MTU.
+ */
+ if (!sifmtu(link_mtu)) {
+ return (0);
+ }
+ /*
+ * Set the IP interface local point-to-point address.
+ */
+ BZERO(&sin, sizeof (sin));
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = o;
+
+ BZERO(&ifr, sizeof (ifr));
+ (void) strlcpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
+ ifr.ifr_addr = *(struct sockaddr *)&sin;
+ if (myioctl(ipfd, SIOCSIFADDR, &ifr) < 0) {
+ error("Couldn't set local IP address (%s): %m", ifr.ifr_name);
+ return (0);
+ }
+ /*
+ * Set the IP interface remote point-to-point address.
+ */
+ sin.sin_addr.s_addr = h;
+
+ ifr.ifr_dstaddr = *(struct sockaddr *)&sin;
+ if (myioctl(ipfd, SIOCSIFDSTADDR, &ifr) < 0) {
+ error("Couldn't set remote IP address (%s): %m", ifr.ifr_name);
+ return (0);
+ }
+ remote_addr = h;
+ return (1);
+}
+
+/*
+ * cifaddr()
+ *
+ * Clear the interface IP addresses.
+ */
+/*ARGSUSED*/
+int
+cifaddr(u, o, h)
+ int u;
+ u_int32_t o;
+ u_int32_t h;
+{
+ if (!IPCP_ENABLED) {
+ return (0);
+ }
+ /*
+ * Most of the work is done in sifdown().
+ */
+ remote_addr = 0;
+ return (1);
+}
+
+/*
+ * sifroute()
+ *
+ * Add or delete a route.
+ */
+/*ARGSUSED*/
+static int
+sifroute(int u, u_int32_t l, u_int32_t g, int add, const char *str)
+{
+ struct sockaddr_in sin_dst, sin_gtw;
+ struct rtentry rt;
+
+ if (!IPCP_ENABLED || (ipmuxid == -1)) {
+ error("Can't %s route: IP is not enabled", str);
+ return (0);
+ }
+ if (ipfd == -1 && open_ipfd() == -1)
+ return (0);
+
+ BZERO(&sin_dst, sizeof (sin_dst));
+ sin_dst.sin_family = AF_INET;
+ sin_dst.sin_addr.s_addr = l;
+
+ BZERO(&sin_gtw, sizeof (sin_gtw));
+ sin_gtw.sin_family = AF_INET;
+ sin_gtw.sin_addr.s_addr = g;
+
+ BZERO(&rt, sizeof (rt));
+ rt.rt_dst = *(struct sockaddr *)&sin_dst;
+ rt.rt_gateway = *(struct sockaddr *)&sin_gtw;
+ rt.rt_flags = (RTF_GATEWAY|RTF_STATIC);
+
+ if (myioctl(ipfd, (add ? SIOCADDRT : SIOCDELRT), &rt) < 0) {
+ error("Can't %s route: %m", str);
+ return (0);
+ }
+ return (1);
+}
+
+/*
+ * sifdefaultroute()
+ *
+ * Assign a default route through the address given.
+ */
+/*ARGSUSED*/
+int
+sifdefaultroute(u, l, g)
+ int u;
+ u_int32_t l;
+ u_int32_t g;
+{
+ if (!sifroute(u, 0, g, 1, "add default")) {
+ return (0);
+ }
+ default_route_gateway = g;
+ return (1);
+}
+
+/*
+ * cifdefaultroute()
+ *
+ * Delete a default route through the address given.
+ */
+/*ARGSUSED*/
+int
+cifdefaultroute(u, l, g)
+ int u;
+ u_int32_t l;
+ u_int32_t g;
+{
+ if (!sifroute(u, 0, g, 0, "delete default")) {
+ return (0);
+ }
+ default_route_gateway = 0;
+ return (1);
+}
+
+/*
+ * sifproxyarp()
+ *
+ * Make a proxy ARP entry for the peer.
+ */
+/*ARGSUSED*/
+int
+sifproxyarp(unit, hisaddr, quietflag)
+ int unit;
+ u_int32_t hisaddr;
+ int quietflag;
+{
+ struct sockaddr_in sin;
+ struct xarpreq arpreq;
+ const uchar_t *cp;
+ char *str = NULL;
+
+ if (!IPCP_ENABLED || (ipmuxid == -1)) {
+ return (0);
+ }
+ if (ipfd == -1 && open_ipfd() == -1)
+ return (0);
+
+ BZERO(&sin, sizeof (sin));
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = hisaddr;
+
+ BZERO(&arpreq, sizeof (arpreq));
+ if (!get_ether_addr(hisaddr, &arpreq.xarp_ha, quietflag)) {
+ return (0);
+ }
+ BCOPY(&sin, &arpreq.xarp_pa, sizeof (sin));
+ arpreq.xarp_flags = ATF_PERM | ATF_PUBL;
+ arpreq.xarp_ha.sdl_family = AF_LINK;
+
+ if (myioctl(ipfd, SIOCSXARP, (caddr_t)&arpreq) < 0) {
+ if (!quietflag)
+ error("Couldn't set proxy ARP entry: %m");
+ return (0);
+ }
+ cp = (const uchar_t *)LLADDR(&arpreq.xarp_ha);
+ str = _link_ntoa(cp, str, arpreq.xarp_ha.sdl_alen, IFT_OTHER);
+ if (str != NULL) {
+ dbglog("established proxy ARP for %I using %s", hisaddr,
+ str);
+ free(str);
+ }
+ proxy_arp_addr = hisaddr;
+ return (1);
+}
+
+/*
+ * cifproxyarp()
+ *
+ * Delete the proxy ARP entry for the peer.
+ */
+/*ARGSUSED*/
+int
+cifproxyarp(unit, hisaddr)
+ int unit;
+ u_int32_t hisaddr;
+{
+ struct sockaddr_in sin;
+ struct xarpreq arpreq;
+
+ if (!IPCP_ENABLED || (ipmuxid == -1)) {
+ return (0);
+ }
+ if (ipfd == -1 && open_ipfd() == -1)
+ return (0);
+
+ BZERO(&sin, sizeof (sin));
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = hisaddr;
+
+ BZERO(&arpreq, sizeof (arpreq));
+ BCOPY(&sin, &arpreq.xarp_pa, sizeof (sin));
+ arpreq.xarp_ha.sdl_family = AF_LINK;
+
+ if (myioctl(ipfd, SIOCDXARP, (caddr_t)&arpreq) < 0) {
+ error("Couldn't delete proxy ARP entry: %m");
+ return (0);
+ }
+ proxy_arp_addr = 0;
+ return (1);
+}
+
+/*
+ * get_ether_addr()
+ *
+ * Get the hardware address of an interface on the the same subnet as
+ * ipaddr. This routine uses old-style interfaces for intentional
+ * backward compatibility -- SIOCGLIF* isn't in older Solaris
+ * releases.
+ */
+static int
+get_ether_addr(u_int32_t ipaddr, struct sockaddr_dl *hwaddr, int quietflag)
+{
+ struct ifreq *ifr, *ifend, ifreq;
+ int nif, s, retv;
+ struct ifconf ifc;
+ u_int32_t ina, mask;
+ struct xarpreq req;
+ struct sockaddr_in sin;
+
+ if (ipfd == -1 && open_ipfd() == -1)
+ return (0);
+
+ /*
+ * Scan through the system's network interfaces.
+ */
+ if (myioctl(ipfd, SIOCGIFNUM, &nif) < 0) {
+ nif = MAXIFS;
+ }
+ if (nif <= 0)
+ return (0);
+ ifc.ifc_len = nif * sizeof (struct ifreq);
+ ifc.ifc_buf = (caddr_t)malloc(ifc.ifc_len);
+ if (ifc.ifc_buf == NULL) {
+ return (0);
+ }
+ if (myioctl(ipfd, SIOCGIFCONF, &ifc) < 0) {
+ error("Couldn't get system interface list: %m");
+ free(ifc.ifc_buf);
+ return (0);
+ }
+ /* LINTED */
+ ifend = (struct ifreq *)(ifc.ifc_buf + ifc.ifc_len);
+ for (ifr = ifc.ifc_req; ifr < ifend; ++ifr) {
+ if (ifr->ifr_addr.sa_family != AF_INET) {
+ continue;
+ }
+ /*
+ * Check that the interface is up, and not
+ * point-to-point or loopback.
+ */
+ (void) strlcpy(ifreq.ifr_name, ifr->ifr_name,
+ sizeof (ifreq.ifr_name));
+ if (myioctl(ipfd, SIOCGIFFLAGS, &ifreq) < 0) {
+ continue;
+ }
+ if ((ifreq.ifr_flags & (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT|
+ IFF_LOOPBACK|IFF_NOARP)) != (IFF_UP|IFF_BROADCAST)) {
+ continue;
+ }
+ /*
+ * Get its netmask and check that it's on the right subnet.
+ */
+ if (myioctl(ipfd, SIOCGIFNETMASK, &ifreq) < 0) {
+ continue;
+ }
+ (void) memcpy(&sin, &ifr->ifr_addr, sizeof (sin));
+ ina = sin.sin_addr.s_addr;
+ (void) memcpy(&sin, &ifreq.ifr_addr, sizeof (sin));
+ mask = sin.sin_addr.s_addr;
+ if ((ipaddr & mask) == (ina & mask)) {
+ break;
+ }
+ }
+ if (ifr >= ifend) {
+ if (!quietflag)
+ warn("No suitable interface found for proxy ARP of %I",
+ ipaddr);
+ free(ifc.ifc_buf);
+ return (0);
+ }
+ info("found interface %s for proxy ARP of %I", ifr->ifr_name, ipaddr);
+
+ /*
+ * New way - get the address by doing an arp request.
+ */
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ error("get_ether_addr: error opening IP socket: %m");
+ free(ifc.ifc_buf);
+ return (0);
+ }
+ BZERO(&sin, sizeof (sin));
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = ina;
+
+ BZERO(&req, sizeof (req));
+ BCOPY(&sin, &req.xarp_pa, sizeof (sin));
+ req.xarp_ha.sdl_family = AF_LINK;
+
+ if (myioctl(s, SIOCGXARP, &req) < 0) {
+ error("Couldn't get ARP entry for %I: %m", ina);
+ retv = 0;
+ } else {
+ (void) memcpy(hwaddr, &req.xarp_ha,
+ sizeof (struct sockaddr_dl));
+ retv = 1;
+ }
+ (void) close(s);
+ free(ifc.ifc_buf);
+ return (retv);
+}
+
+/*
+ * dlpi_attach()
+ *
+ * Send down DL_ATTACH_REQ to driver.
+ */
+static int
+dlpi_attach(int fd, int ppa)
+{
+ dl_attach_req_t req;
+ struct strbuf buf;
+
+ if (fd < 0) {
+ return (-1);
+ }
+ BZERO(&req, sizeof (req));
+ req.dl_primitive = DL_ATTACH_REQ;
+ req.dl_ppa = ppa;
+
+ buf.len = sizeof (req);
+ buf.buf = (void *) &req;
+
+ return (putmsg(fd, &buf, NULL, RS_HIPRI));
+}
+
+/*
+ * dlpi_info_req()
+ *
+ * Send down DL_INFO_REQ to driver.
+ */
+static int
+dlpi_info_req(int fd)
+{
+ dl_info_req_t req;
+ struct strbuf buf;
+
+ if (fd < 0) {
+ return (-1);
+ }
+ BZERO(&req, sizeof (req));
+ req.dl_primitive = DL_INFO_REQ;
+
+ buf.len = sizeof (req);
+ buf.buf = (void *) &req;
+
+ return (putmsg(fd, &buf, NULL, RS_HIPRI));
+}
+
+/*
+ * dlpi_get_reply()
+ *
+ * Poll to get DLPI reply message from driver.
+ */
+static int
+dlpi_get_reply(int fd, union DL_primitives *reply, int expected_prim,
+ int maxlen)
+{
+ struct strbuf buf;
+ struct pollfd pfd;
+ int flags;
+ int n;
+
+ if (fd < 0) {
+ return (-1);
+ }
+ /*
+ * Use poll to wait for a message with a timeout.
+ */
+ pfd.fd = fd;
+ pfd.events = (POLLIN | POLLPRI);
+
+ do {
+ n = poll(&pfd, 1, 1000);
+ } while ((n == -1) && (errno == EINTR));
+
+ if (n <= 0) {
+ return (-1);
+ }
+ /*
+ * Get the reply.
+ */
+ buf.maxlen = maxlen;
+ buf.buf = (void *)reply;
+
+ flags = 0;
+
+ if (getmsg(fd, &buf, NULL, &flags) < 0) {
+ return (-1);
+ }
+ if (buf.len < sizeof (ulong_t)) {
+ if (debug) {
+ dbglog("dlpi response short (len=%d)\n", buf.len);
+ }
+ return (-1);
+ }
+ if (reply->dl_primitive == expected_prim) {
+ return (0);
+ }
+ if (debug) {
+ if (reply->dl_primitive == DL_ERROR_ACK) {
+ dbglog("dlpi error %d (unix errno %d) for prim %x\n",
+ reply->error_ack.dl_errno,
+ reply->error_ack.dl_unix_errno,
+ reply->error_ack.dl_error_primitive);
+ } else {
+ dbglog("dlpi unexpected response prim %x\n",
+ reply->dl_primitive);
+ }
+ }
+ return (-1);
+}
+
+/*
+ * GetMask()
+ *
+ * Return mask (bogus, but needed for compatibility with other platforms).
+ */
+/*ARGSUSED*/
+u_int32_t
+GetMask(addr)
+ u_int32_t addr;
+{
+ return (0xffffffffUL);
+}
+
+/*
+ * logwtmp()
+ *
+ * Write an accounting record to the /var/adm/wtmp file.
+ */
+/*ARGSUSED*/
+void
+logwtmp(line, name, host)
+ const char *line;
+ const char *name;
+ const char *host;
+{
+ static struct utmpx utmpx;
+
+ if (name[0] != '\0') {
+ /*
+ * logging in
+ */
+ (void) strncpy(utmpx.ut_user, name, sizeof (utmpx.ut_user));
+ (void) strncpy(utmpx.ut_id, ifname, sizeof (utmpx.ut_id));
+ (void) strncpy(utmpx.ut_line, line, sizeof (utmpx.ut_line));
+
+ utmpx.ut_pid = getpid();
+ utmpx.ut_type = USER_PROCESS;
+ } else {
+ utmpx.ut_type = DEAD_PROCESS;
+ }
+ (void) gettimeofday(&utmpx.ut_tv, NULL);
+ updwtmpx("/var/adm/wtmpx", &utmpx);
+}
+
+/*
+ * get_host_seed()
+ *
+ * Return the serial number of this machine.
+ */
+int
+get_host_seed()
+{
+ char buf[32];
+
+ if (sysinfo(SI_HW_SERIAL, buf, sizeof (buf)) < 0) {
+ error("sysinfo: %m");
+ return (0);
+ }
+ return ((int)strtoul(buf, NULL, 16));
+}
+
+/*
+ * strioctl()
+ *
+ * Wrapper for STREAMS I_STR ioctl. Masks out EINTR from caller.
+ */
+static int
+strioctl(int fd, int cmd, void *ptr, int ilen, int olen)
+{
+ struct strioctl str;
+
+ str.ic_cmd = cmd;
+ str.ic_timout = PPPSTRTIMOUT;
+ str.ic_len = ilen;
+ str.ic_dp = ptr;
+
+ if (myioctl(fd, I_STR, &str) == -1) {
+ return (-1);
+ }
+ if (str.ic_len != olen) {
+ dbglog("strioctl: expected %d bytes, got %d for cmd %x\n",
+ olen, str.ic_len, cmd);
+ }
+ return (0);
+}
+
+/*
+ * have_route_to()
+ *
+ * Determine if the system has a route to the specified IP address.
+ * Returns 0 if not, 1 if so, -1 if we can't tell. `addr' is in network
+ * byte order. For demand mode to work properly, we have to ignore routes
+ * through our own interface. XXX Would be nice to use routing socket.
+ */
+int
+have_route_to(addr)
+ u_int32_t addr;
+{
+ int r, flags, i;
+ struct {
+ struct T_optmgmt_req req;
+ struct opthdr hdr;
+ } req;
+ union {
+ struct T_optmgmt_ack ack;
+ unsigned char space[64];
+ } ack;
+ struct opthdr *rh;
+ struct strbuf cbuf, dbuf;
+ int nroutes;
+ mib2_ipRouteEntry_t routes[8];
+ mib2_ipRouteEntry_t *rp;
+
+ if (ipfd == -1 && open_ipfd() == -1)
+ return (0);
+
+ req.req.PRIM_type = T_OPTMGMT_REQ;
+ req.req.OPT_offset = (caddr_t)&req.hdr - (caddr_t)&req;
+ req.req.OPT_length = sizeof (req.hdr);
+#ifdef T_CURRENT
+ req.req.MGMT_flags = T_CURRENT;
+#else
+ /* Old-style */
+ req.req.MGMT_flags = T_CHECK;
+#endif
+
+ req.hdr.level = MIB2_IP;
+ req.hdr.name = 0;
+ req.hdr.len = 0;
+
+ cbuf.buf = (caddr_t)&req;
+ cbuf.len = sizeof (req);
+
+ if (putmsg(ipfd, &cbuf, NULL, 0) == -1) {
+ warn("have_route_to: putmsg: %m");
+ return (-1);
+ }
+
+ for (;;) {
+ cbuf.buf = (caddr_t)&ack;
+ cbuf.maxlen = sizeof (ack);
+ dbuf.buf = (caddr_t)routes;
+ dbuf.maxlen = sizeof (routes);
+ flags = 0;
+ r = getmsg(ipfd, &cbuf, &dbuf, &flags);
+ if (r == -1) {
+ warn("have_route_to: getmsg: %m");
+ return (-1);
+ }
+
+ if (cbuf.len < sizeof (struct T_optmgmt_ack) ||
+ ack.ack.PRIM_type != T_OPTMGMT_ACK ||
+ ack.ack.MGMT_flags != T_SUCCESS ||
+ ack.ack.OPT_length < sizeof (struct opthdr)) {
+ dbglog("have_route_to: bad message len=%d prim=%d",
+ cbuf.len, ack.ack.PRIM_type);
+ return (-1);
+ }
+ /* LINTED */
+ rh = (struct opthdr *)((caddr_t)&ack + ack.ack.OPT_offset);
+ if (rh->level == 0 && rh->name == 0) {
+ break;
+ }
+ if (rh->level != MIB2_IP || rh->name != MIB2_IP_21) {
+ while (r == MOREDATA) {
+ r = getmsg(ipfd, NULL, &dbuf, &flags);
+ }
+ continue;
+ }
+
+ /*
+ * Note that we have to skip routes to our own
+ * interface in order for demand dial to work.
+ *
+ * XXX awful hack here. We don't know our own
+ * ifIndex, so we can't check ipRouteIfIndex here.
+ * Instead, we check the next hop address.
+ */
+ for (;;) {
+ nroutes = dbuf.len / sizeof (mib2_ipRouteEntry_t);
+ for (rp = routes, i = 0; i < nroutes; ++i, ++rp) {
+ if (rp->ipRouteNextHop != remote_addr &&
+ ((addr ^ rp->ipRouteDest) &
+ rp->ipRouteMask) == 0) {
+ dbglog("have route to %I/%I via %I",
+ rp->ipRouteDest,
+ rp->ipRouteMask,
+ rp->ipRouteNextHop);
+ return (1);
+ }
+ }
+ if (r == 0) {
+ break;
+ }
+ r = getmsg(ipfd, NULL, &dbuf, &flags);
+ }
+ }
+ return (0);
+}
+
+/*
+ * get_pty()
+ *
+ * Get a pty master/slave pair and chown the slave side to the uid given.
+ * Assumes slave_name points to MAXPATHLEN bytes of space.
+ */
+int
+get_pty(master_fdp, slave_fdp, slave_name, uid)
+ int *master_fdp;
+ int *slave_fdp;
+ char *slave_name;
+ int uid;
+{
+ int mfd;
+ int sfd;
+ char *pty_name;
+
+ mfd = open("/dev/ptmx", O_NOCTTY | O_RDWR);
+ if (mfd < 0) {
+ error("Couldn't open pty master: %m");
+ return (0);
+ }
+ pty_name = ptsname(mfd);
+ if (pty_name == NULL) {
+ dbglog("Didn't get pty slave name on first try; sleeping.");
+ /* In case "grow" operation is in progress; try again. */
+ (void) sleep(1);
+ pty_name = ptsname(mfd);
+ }
+ if (pty_name == NULL) {
+ error("Couldn't get name of pty slave");
+ (void) close(mfd);
+ return (0);
+ }
+ if (chown(pty_name, uid, -1) < 0) {
+ warn("Couldn't change owner of pty slave: %m");
+ }
+ if (chmod(pty_name, S_IRUSR | S_IWUSR) < 0) {
+ warn("Couldn't change permissions on pty slave: %m");
+ }
+ if (unlockpt(mfd) < 0) {
+ warn("Couldn't unlock pty slave: %m");
+ }
+ sfd = open(pty_name, O_RDWR);
+ if (sfd < 0) {
+ error("Couldn't open pty slave %s: %m", pty_name);
+ (void) close(mfd);
+ return (0);
+ }
+ if (myioctl(sfd, I_PUSH, "ptem") < 0) {
+ warn("Couldn't push ptem module on pty slave: %m");
+ }
+ dbglog("Using %s; master fd %d, slave fd %d", pty_name, mfd, sfd);
+
+ (void) strlcpy(slave_name, pty_name, MAXPATHLEN);
+
+ *master_fdp = mfd;
+ *slave_fdp = sfd;
+
+ return (1);
+}
+
+#ifdef INET6
+static int
+open_udp6fd(void)
+{
+ int udp6fd;
+
+ udp6fd = open(UDP6_DEV_NAME, O_RDWR | O_NONBLOCK, 0);
+ if (udp6fd < 0) {
+ error("Couldn't open UDPv6 device (%s): %m", UDP6_DEV_NAME);
+ }
+ return (udp6fd);
+}
+
+/*
+ * plumb_ip6if()
+ *
+ * Perform IPv6 interface plumbing.
+ */
+/*ARGSUSED*/
+static int
+plumb_ip6if(int unit)
+{
+ int udp6fd = -1, tmpfd;
+ uint32_t x;
+ struct lifreq lifr;
+
+ if (!IPV6CP_ENABLED || (ifunit == -1) || (pppfd == -1)) {
+ return (0);
+ }
+ if (plumbed)
+ return (1);
+ if (ip6fd == -1 && open_ip6fd() == -1)
+ return (0);
+ if (use_plink && (udp6fd = open_udp6fd()) == -1)
+ return (0);
+ tmpfd = open(drvnam, O_RDWR | O_NONBLOCK, 0);
+ if (tmpfd < 0) {
+ error("Couldn't open PPP device (%s): %m", drvnam);
+ if (udp6fd != -1)
+ (void) close(udp6fd);
+ return (0);
+ }
+ if (kdebugflag & 1) {
+ x = PPPDBG_LOG + PPPDBG_DRIVER;
+ if (strioctl(tmpfd, PPPIO_DEBUG, &x, sizeof (x), 0) < 0) {
+ warn("PPPIO_DEBUG ioctl for mux failed: %m");
+ }
+ }
+ if (myioctl(tmpfd, I_PUSH, IP_MOD_NAME) < 0) {
+ error("Couldn't push IP module(%s): %m", IP_MOD_NAME);
+ goto err_ret;
+ }
+ /*
+ * Sets interface ppa and flags (refer to comments in plumb_ipif for
+ * the IF_UNITSEL ioctl). In addition, the IFF_IPV6 bit must be set in
+ * order to declare this as an IPv6 interface.
+ */
+ BZERO(&lifr, sizeof (lifr));
+ if (myioctl(tmpfd, SIOCGLIFFLAGS, &lifr) < 0) {
+ error("Couldn't get IPv6 interface flags: %m");
+ goto err_ret;
+ }
+ lifr.lifr_flags |= IFF_IPV6;
+ lifr.lifr_flags &= ~(IFF_BROADCAST | IFF_IPV4);
+ lifr.lifr_ppa = ifunit;
+ (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
+ if (myioctl(tmpfd, SIOCSLIFNAME, &lifr) < 0) {
+ error("Can't set ifname for unit %d: %m", ifunit);
+ goto err_ret;
+ }
+ if (use_plink) {
+ ip6muxid = myioctl(udp6fd, I_PLINK, (void *)tmpfd);
+ if (ip6muxid < 0) {
+ error("Can't I_PLINK PPP device to IPv6: %m");
+ goto err_ret;
+ }
+ } else {
+ ip6muxid = myioctl(ip6fd, I_LINK, (void *)tmpfd);
+ if (ip6muxid < 0) {
+ error("Can't I_LINK PPP device to IPv6: %m");
+ goto err_ret;
+ }
+ }
+ lifr.lifr_ip_muxid = ip6muxid;
+ lifr.lifr_arp_muxid = -1;
+ if (myioctl(ip6fd, SIOCSLIFMUXID, (caddr_t)&lifr) < 0) {
+ error("Can't set mux ID: SIOCSLIFMUXID: %m");
+ goto err_ret;
+ }
+ (void) close(tmpfd);
+ if (udp6fd != -1)
+ (void) close(udp6fd);
+ return (1);
+
+err_ret:
+ (void) close(tmpfd);
+ if (udp6fd != -1)
+ (void) close(udp6fd);
+ return (0);
+}
+
+/*
+ * unplumb_ip6if()
+ *
+ * Perform IPv6 interface unplumbing. Possibly called from die(), so there
+ * shouldn't be any call to die() here.
+ */
+static int
+unplumb_ip6if(int unit)
+{
+ int udp6fd = -1, fd = -1;
+ int id;
+ struct lifreq lifr;
+
+ if (!IPV6CP_ENABLED || ifunit == -1) {
+ return (0);
+ }
+ if (!plumbed && (ip6muxid == -1 || (ip6fd == -1 && !use_plink))) {
+ return (1);
+ }
+ id = ip6muxid;
+ if (!plumbed && use_plink) {
+ if ((udp6fd = open_udp6fd()) == -1)
+ return (0);
+ /*
+ * Note: must re-get mux ID, since any intervening
+ * ifconfigs will change this.
+ */
+ BZERO(&lifr, sizeof (lifr));
+ (void) strlcpy(lifr.lifr_name, ifname,
+ sizeof (lifr.lifr_name));
+ if (myioctl(ip6fd, SIOCGLIFMUXID, (caddr_t)&lifr) < 0) {
+ warn("Can't get mux fd: SIOCGLIFMUXID: %m");
+ } else {
+ id = lifr.lifr_ip_muxid;
+ fd = myioctl(udp6fd, _I_MUXID2FD, (void *)id);
+ if (fd < 0) {
+ warn("Can't get mux fd: _I_MUXID2FD: %m");
+ }
+ }
+ }
+ /*
+ * Mark down and unlink the IPv6 interface.
+ */
+ (void) sif6down(unit);
+ if (plumbed)
+ return (1);
+ ip6muxid = -1;
+ if (use_plink) {
+ if ((fd = myioctl(udp6fd, _I_MUXID2FD, (void *)id)) < 0) {
+ error("Can't recapture mux fd: _I_MUXID2FD: %m");
+ (void) close(udp6fd);
+ return (0);
+ }
+ if (myioctl(udp6fd, I_PUNLINK, (void *)id) < 0) {
+ error("Can't I_PUNLINK PPP from IPv6: %m");
+ (void) close(fd);
+ (void) close(udp6fd);
+ return (0);
+ }
+ (void) close(fd);
+ (void) close(udp6fd);
+ } else {
+ if (myioctl(ip6fd, I_UNLINK, (void *)id) < 0) {
+ error("Can't I_UNLINK PPP from IPv6: %m");
+ return (0);
+ }
+ }
+ return (1);
+}
+
+/*
+ * sif6flags()
+ *
+ * Set or clear the IPv6 interface flags.
+ */
+int
+sif6flags(f, set)
+ u_int32_t f;
+ int set;
+{
+ struct lifreq lifr;
+ int fd;
+
+ if (!IPV6CP_ENABLED || (ip6muxid == -1)) {
+ return (0);
+ }
+ fd = socket(AF_INET6, SOCK_DGRAM, 0);
+ if (fd < 0) {
+ error("sif6flags: error opening IPv6 socket: %m");
+ return (0);
+ }
+ BZERO(&lifr, sizeof (lifr));
+ (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
+ if (myioctl(fd, SIOCGLIFFLAGS, &lifr) < 0) {
+ error("Couldn't get IPv6 interface flags: %m");
+ (void) close(fd);
+ return (0);
+ }
+ if (set) {
+ lifr.lifr_flags |= f;
+ } else {
+ lifr.lifr_flags &= ~f;
+ }
+ (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
+ if (myioctl(fd, SIOCSLIFFLAGS, &lifr) < 0) {
+ error("Couldn't set IPv6 interface flags: %m");
+ (void) close(fd);
+ return (0);
+ }
+ (void) close(fd);
+ return (1);
+}
+
+/*
+ * sif6up()
+ *
+ * Config the IPv6 interface up and enable IPv6 packets to pass.
+ */
+/*ARGSUSED*/
+int
+sif6up(unit)
+ int unit;
+{
+ if (if6_is_up) {
+ return (1);
+ } else if (!IPV6CP_ENABLED) {
+ warn("sif6up called when IPV6CP is disabled");
+ return (0);
+ } else if (ip6muxid == -1) {
+ warn("sif6up called in wrong state");
+ return (0);
+ } else if (!sif6flags(IFF_UP, 1)) {
+ error("Unable to mark the IPv6 interface UP");
+ return (0);
+ }
+ if6_is_up = 1;
+ return (1);
+}
+
+/*
+ * sif6down()
+ *
+ * Config the IPv6 interface down and disable IPv6. Possibly called from
+ * die(), so there shouldn't be any call to die() here.
+ */
+/*ARGSUSED*/
+int
+sif6down(unit)
+ int unit;
+{
+ if (!IPV6CP_ENABLED) {
+ warn("sif6down called when IPV6CP is disabled");
+ return (0);
+ } else if (!if6_is_up || (ip6muxid == -1)) {
+ return (1);
+ } else if (!sif6flags(IFF_UP, 0)) {
+ error("Unable to mark the IPv6 interface DOWN");
+ return (0);
+ }
+ if6_is_up = 0;
+ return (1);
+}
+
+/*
+ * sif6mtu()
+ *
+ * Config the IPv6 interface MTU.
+ */
+int
+sif6mtu(mtu)
+ int mtu;
+{
+ struct lifreq lifr;
+ int s;
+
+ if (!IPV6CP_ENABLED || (ip6muxid == -1)) {
+ return (0);
+ }
+ s = socket(AF_INET6, SOCK_DGRAM, 0);
+ if (s < 0) {
+ error("sif6mtu: error opening IPv6 socket: %m");
+ return (0);
+ }
+ BZERO(&lifr, sizeof (lifr));
+ (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
+ lifr.lifr_mtu = mtu;
+ if (myioctl(s, SIOCSLIFMTU, &lifr) < 0) {
+ error("Couldn't set IPv6 MTU (%s): %m", lifr.lifr_name);
+ (void) close(s);
+ return (0);
+ }
+ (void) close(s);
+ return (1);
+}
+
+/*
+ * sif6addr()
+ *
+ * Config the interface with an IPv6 link-local address.
+ */
+/*ARGSUSED*/
+int
+sif6addr(unit, ourid, hisid)
+ int unit;
+ eui64_t ourid;
+ eui64_t hisid;
+{
+ struct lifreq lifr;
+ struct sockaddr_storage laddr;
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&laddr;
+ int fd;
+
+ if (!IPV6CP_ENABLED || (ip6muxid == -1 && plumb_ip6if(unit) == 0)) {
+ return (0);
+ }
+ fd = socket(AF_INET6, SOCK_DGRAM, 0);
+ if (fd < 0) {
+ error("sif6addr: error opening IPv6 socket: %m");
+ return (0);
+ }
+ /*
+ * Set the IPv6 interface MTU.
+ */
+ if (!sif6mtu(link_mtu)) {
+ (void) close(fd);
+ return (0);
+ }
+ /*
+ * Set the interface address token. Do this because /dev/ppp responds
+ * to DL_PHYS_ADDR_REQ with zero values, hence the interface token
+ * came to be zero too, and without this, in.ndpd will complain.
+ */
+ BZERO(&lifr, sizeof (lifr));
+ (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
+ BZERO(sin6, sizeof (struct sockaddr_in6));
+ IN6_LLTOKEN_FROM_EUI64(lifr, sin6, ourid);
+ if (myioctl(fd, SIOCSLIFTOKEN, &lifr) < 0) {
+ error("Couldn't set IPv6 token (%s): %m", lifr.lifr_name);
+ (void) close(fd);
+ return (0);
+ }
+ /*
+ * Set the IPv6 interface local point-to-point address.
+ */
+ IN6_LLADDR_FROM_EUI64(lifr, sin6, ourid);
+ if (myioctl(fd, SIOCSLIFADDR, &lifr) < 0) {
+ error("Couldn't set local IPv6 address (%s): %m",
+ lifr.lifr_name);
+ (void) close(fd);
+ return (0);
+ }
+ /*
+ * Set the IPv6 interface local point-to-point address.
+ */
+ BZERO(&lifr, sizeof (lifr));
+ (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
+ IN6_LLADDR_FROM_EUI64(lifr, sin6, hisid);
+ if (myioctl(fd, SIOCSLIFDSTADDR, &lifr) < 0) {
+ error("Couldn't set remote IPv6 address (%s): %m",
+ lifr.lifr_name);
+ (void) close(fd);
+ return (0);
+ }
+ (void) close(fd);
+ return (1);
+}
+
+/*
+ * cif6addr()
+ */
+/*ARGSUSED*/
+int
+cif6addr(u, o, h)
+ int u;
+ eui64_t o;
+ eui64_t h;
+{
+ if (!IPV6CP_ENABLED) {
+ return (0);
+ }
+ /*
+ * Do nothing here, as everything has been done in sif6down().
+ */
+ return (1);
+}
+
+/*
+ * ether_to_eui64()
+ *
+ * Convert 48-bit Ethernet address into 64-bit EUI. Walks the list of valid
+ * ethernet interfaces, and convert the first found 48-bit MAC address into
+ * EUI 64. caller also assumes that the system has a properly configured
+ * Ethernet interface for this function to return non-zero.
+ */
+int
+ether_to_eui64(p_eui64)
+ eui64_t *p_eui64;
+{
+ struct ether_addr eth_addr;
+
+ if (p_eui64 == NULL) {
+ return (0);
+ }
+ if (!get_first_hwaddr(eth_addr.ether_addr_octet,
+ sizeof (eth_addr.ether_addr_octet))) {
+ return (0);
+ }
+ /*
+ * And convert the EUI-48 into EUI-64, per RFC 2472 [sec 4.1]
+ */
+ p_eui64->e8[0] = (eth_addr.ether_addr_octet[0] & 0xFF) | 0x02;
+ p_eui64->e8[1] = (eth_addr.ether_addr_octet[1] & 0xFF);
+ p_eui64->e8[2] = (eth_addr.ether_addr_octet[2] & 0xFF);
+ p_eui64->e8[3] = 0xFF;
+ p_eui64->e8[4] = 0xFE;
+ p_eui64->e8[5] = (eth_addr.ether_addr_octet[3] & 0xFF);
+ p_eui64->e8[6] = (eth_addr.ether_addr_octet[4] & 0xFF);
+ p_eui64->e8[7] = (eth_addr.ether_addr_octet[5] & 0xFF);
+ return (1);
+}
+#endif /* INET6 */
+
+struct bit_ent {
+ int val;
+ char *off, *on;
+};
+
+/* see sbuf[] below if you change this list */
+static struct bit_ent bit_list[] = {
+ { TIOCM_DTR, "dtr", "DTR" },
+ { TIOCM_RTS, "rts", "RTS" },
+ { TIOCM_CTS, "cts", "CTS" },
+ { TIOCM_CD, "dcd", "DCD" },
+ { TIOCM_RI, "ri", "RI" },
+ { TIOCM_DSR, "dsr", "DSR" },
+#if 0
+ { TIOCM_LE, "disabled", "ENABLED" },
+ { TIOCM_ST, NULL, "2nd-XMIT" },
+ { TIOCM_SR, NULL, "2nd-RECV" },
+#endif
+ { 0, NULL, NULL }
+};
+
+static void
+getbits(int fd, char *name, FILE *strptr)
+{
+ int nmods, i;
+ struct str_list strlist;
+ struct bit_ent *be;
+ int mstate;
+ char sbuf[50]; /* sum of string lengths in bit_list */
+ char *str;
+
+ nmods = ioctl(fd, I_LIST, NULL);
+ if (nmods < 0) {
+ error("unable to get module count: %m");
+ } else {
+ strlist.sl_nmods = nmods;
+ strlist.sl_modlist = malloc(sizeof (struct str_mlist) * nmods);
+ if (strlist.sl_modlist == NULL)
+ novm("module list");
+ if (ioctl(fd, I_LIST, (caddr_t)&strlist) < 0) {
+ error("unable to get module names: %m");
+ } else {
+ for (i = 0; i < strlist.sl_nmods; i++)
+ (void) flprintf(strptr, "%d: %s", i,
+ strlist.sl_modlist[i].l_name);
+ free(strlist.sl_modlist);
+ }
+ }
+ if (ioctl(fd, TIOCMGET, &mstate) < 0) {
+ error("unable to get modem state: %m");
+ } else {
+ sbuf[0] = '\0';
+ for (be = bit_list; be->val != 0; be++) {
+ str = (be->val & mstate) ? be->on : be->off;
+ if (str != NULL) {
+ if (sbuf[0] != '\0')
+ (void) strcat(sbuf, " ");
+ (void) strcat(sbuf, str);
+ }
+ }
+ (void) flprintf(strptr, "%s: %s\n", name, sbuf);
+ }
+}
+
+/*
+ * Print state of serial link. The stream might be linked under the
+ * /dev/sppp driver. If it is, then it's necessary to unlink it first
+ * and relink it when done. Otherwise, it's not possible to use
+ * ioctl() on the stream.
+ */
+void
+sys_print_state(FILE *strptr)
+{
+ bool was_linked;
+
+ if (pppfd == -1)
+ return;
+ if (ttyfd == -1) {
+ (void) flprintf(strptr, "serial link is not active");
+ return;
+ }
+ was_linked = fdmuxid != -1;
+ if (was_linked && ioctl(pppfd, I_UNLINK, fdmuxid) == -1) {
+ error("I_UNLINK: %m");
+ } else {
+ fdmuxid = -1;
+ getbits(ttyfd, devnam, strptr);
+ if (was_linked &&
+ (fdmuxid = ioctl(pppfd, I_LINK, (void *)ttyfd)) == -1)
+ fatal("I_LINK: %m");
+ }
+}
+
+/*
+ * send ioctl to driver asking it to block packets with network protocol
+ * proto in the control queue until the queue for proto is plumbed.
+ */
+void
+sys_block_proto(uint16_t proto)
+{
+ if (proto > 0x7fff) {
+ warn("cannot block: not a network proto 0x%lx\n", proto);
+ return;
+ }
+ if (strioctl(pppfd, PPPIO_BLOCKNP, &proto, sizeof (proto), 0) < 0) {
+ warn("PPPIO_BLOCKNP ioctl failed %m");
+ }
+}
+/*
+ * send ioctl to driver asking it to release packets with network protocol
+ * proto from control queue to the protocol specific queue.
+ */
+void
+sys_unblock_proto(uint16_t proto)
+{
+ if (proto > 0x7fff) {
+ warn("cannot unblock: not a network proto 0x%lx\n", proto);
+ return;
+ }
+ if (strioctl(pppfd, PPPIO_UNBLOCKNP, &proto, sizeof (proto), 0) < 0) {
+ warn("PPPIO_UNBLOCKNP ioctl failed %m");
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppd/upap.c b/usr/src/cmd/cmd-inet/usr.bin/pppd/upap.c
new file mode 100644
index 0000000000..b9263af55b
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppd/upap.c
@@ -0,0 +1,734 @@
+/*
+ * upap.c - User/Password Authentication Protocol.
+ *
+ * Copyright (c) 2000 by Sun Microsystems, Inc.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies.
+ *
+ * SUN MAKES NO REPRESENTATION OR WARRANTIES ABOUT THE SUITABILITY OF
+ * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+ * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
+ * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
+ * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+#define RCSID "$Id: upap.c,v 1.23 1999/11/20 05:11:47 paulus Exp $"
+
+#include <stdio.h>
+#include <string.h>
+
+#include "pppd.h"
+#include "upap.h"
+
+#if !defined(lint) && !defined(_lint)
+static const char rcsid[] = RCSID;
+#endif
+
+static bool hide_password = 1;
+
+/*
+ * Command-line options.
+ */
+static option_t pap_option_list[] = {
+ { "hide-password", o_bool, &hide_password,
+ "Don't output passwords to log", 1 },
+ { "show-password", o_bool, &hide_password,
+ "Show password string in debug log messages", 0 },
+ { "pap-restart", o_int, &upap[0].us_timeouttime,
+ "Set retransmit timeout for PAP" },
+ { "pap-max-authreq", o_int, &upap[0].us_maxtransmits,
+ "Max number of PAP Authenticate-Request sent" },
+ { "pap-max-receive", o_int, &upap[0].us_maxreceives,
+ "Max allowable PAP Authenticate-Request received" },
+ { "pap-timeout", o_int, &upap[0].us_reqtimeout,
+ "Set time limit for peer PAP authentication" },
+ { NULL }
+};
+
+/*
+ * Protocol entry points.
+ */
+static void upap_init __P((int));
+static void upap_lowerup __P((int));
+static void upap_lowerdown __P((int));
+static void upap_input __P((int, u_char *, int));
+static void upap_protrej __P((int));
+static int upap_printpkt __P((u_char *, int,
+ void (*) __P((void *, const char *, ...)), void *));
+
+struct protent pap_protent = {
+ PPP_PAP,
+ upap_init,
+ upap_input,
+ upap_protrej,
+ upap_lowerup,
+ upap_lowerdown,
+ NULL,
+ NULL,
+ upap_printpkt,
+ NULL,
+ 1,
+ "PAP",
+ NULL,
+ pap_option_list,
+ NULL,
+ NULL,
+ NULL
+};
+
+upap_state upap[NUM_PPP]; /* UPAP state; one for each unit */
+
+static void upap_timeout __P((void *));
+static void upap_reqtimeout __P((void *));
+static void upap_rauthreq __P((upap_state *, u_char *, int, int));
+static void upap_rauthack __P((upap_state *, u_char *, int, int));
+static void upap_rauthnak __P((upap_state *, u_char *, int, int));
+static void upap_sauthreq __P((upap_state *));
+static void upap_sresp __P((upap_state *, int, int, char *, int));
+
+static const char *
+pap_cstate(clientstate)
+ int clientstate;
+{
+ static const char *cstate[] = { UPAPCS__NAMES };
+ static char buf[32];
+
+ if (clientstate < 0 || clientstate >= Dim(cstate)) {
+ (void) slprintf(buf, sizeof (buf), "Cli#%d", clientstate);
+ return ((const char *)buf);
+ }
+ return (cstate[clientstate]);
+}
+
+static const char *
+pap_sstate(serverstate)
+ int serverstate;
+{
+ static const char *sstate[] = { UPAPSS__NAMES };
+ static char buf[32];
+
+ if (serverstate < 0 || serverstate >= Dim(sstate)) {
+ (void) slprintf(buf, sizeof (buf), "Srv#%d", serverstate);
+ return ((const char *)buf);
+ }
+ return (sstate[serverstate]);
+}
+
+/*
+ * upap_init - Initialize a UPAP unit.
+ */
+static void
+upap_init(unit)
+ int unit;
+{
+ upap_state *u = &upap[unit];
+
+ u->us_unit = unit;
+ u->us_user = NULL;
+ u->us_userlen = 0;
+ u->us_passwd = NULL;
+ u->us_clientstate = UPAPCS_INITIAL;
+ u->us_serverstate = UPAPSS_INITIAL;
+ u->us_id = 0;
+ u->us_timeouttime = UPAP_DEFTIMEOUT;
+ u->us_maxtransmits = 10;
+ u->us_reqtimeout = UPAP_DEFREQTIME;
+ u->us_maxreceives = 3;
+ u->us_msg = "";
+ u->us_msglen = 0;
+}
+
+
+/*
+ * upap_authwithpeer - Authenticate us with our peer (start client).
+ *
+ * Set new state and send authenticate's.
+ */
+void
+upap_authwithpeer(unit, user, password)
+ int unit;
+ char *user, *password;
+{
+ upap_state *u = &upap[unit];
+
+ /* Save the username and password we're given */
+ u->us_user = user;
+ u->us_userlen = strlen(user);
+ u->us_passwd = password;
+ u->us_transmits = 0;
+
+ /* Lower layer up yet? */
+ if (u->us_clientstate == UPAPCS_INITIAL ||
+ u->us_clientstate == UPAPCS_PENDING) {
+ u->us_clientstate = UPAPCS_PENDING;
+ return;
+ }
+
+ upap_sauthreq(u); /* Start protocol */
+}
+
+
+/*
+ * upap_authpeer - Authenticate our peer (start server).
+ *
+ * Set new state.
+ */
+void
+upap_authpeer(unit)
+ int unit;
+{
+ upap_state *u = &upap[unit];
+
+ /* Lower layer up yet? */
+ if (u->us_serverstate == UPAPSS_INITIAL ||
+ u->us_serverstate == UPAPSS_PENDING) {
+ u->us_serverstate = UPAPSS_PENDING;
+ return;
+ }
+
+ u->us_serverstate = UPAPSS_LISTEN;
+ u->us_receives = 0;
+ if (u->us_reqtimeout > 0)
+ TIMEOUT(upap_reqtimeout, u, u->us_reqtimeout);
+}
+
+
+/*
+ * upap_timeout - Retransmission timer for sending auth-reqs expired.
+ */
+static void
+upap_timeout(arg)
+ void *arg;
+{
+ upap_state *u = (upap_state *) arg;
+
+ if (u->us_clientstate != UPAPCS_AUTHREQ)
+ return;
+
+ if (u->us_transmits >= u->us_maxtransmits) {
+ /* give up in disgust */
+ error("No response to %d PAP Authenticate-Requests", u->us_transmits);
+ u->us_clientstate = UPAPCS_BADAUTH;
+ auth_withpeer_fail(u->us_unit, PPP_PAP);
+ return;
+ }
+
+ upap_sauthreq(u); /* Send Authenticate-Request */
+}
+
+
+/*
+ * upap_reqtimeout - Give up waiting for the peer to send a valid auth-req.
+ */
+static void
+upap_reqtimeout(arg)
+ void *arg;
+{
+ upap_state *u = (upap_state *) arg;
+
+ if (u->us_serverstate != UPAPSS_LISTEN)
+ return; /* huh?? */
+
+ auth_peer_fail(u->us_unit, PPP_PAP);
+ u->us_serverstate = UPAPSS_BADAUTH;
+}
+
+
+/*
+ * upap_lowerup - The lower layer is up.
+ *
+ * Start authenticating if pending.
+ */
+static void
+upap_lowerup(unit)
+ int unit;
+{
+ upap_state *u = &upap[unit];
+
+ if (u->us_clientstate == UPAPCS_INITIAL)
+ u->us_clientstate = UPAPCS_CLOSED;
+ else if (u->us_clientstate == UPAPCS_PENDING) {
+ upap_sauthreq(u); /* send an auth-request */
+ }
+
+ if (u->us_serverstate == UPAPSS_INITIAL)
+ u->us_serverstate = UPAPSS_CLOSED;
+ else if (u->us_serverstate == UPAPSS_PENDING) {
+ u->us_serverstate = UPAPSS_LISTEN;
+ if (u->us_reqtimeout > 0)
+ TIMEOUT(upap_reqtimeout, u, u->us_reqtimeout);
+ }
+}
+
+
+/*
+ * upap_lowerdown - The lower layer is down.
+ *
+ * Cancel all timeouts.
+ */
+static void
+upap_lowerdown(unit)
+ int unit;
+{
+ upap_state *u = &upap[unit];
+
+ /* Cancel timeouts */
+ if (u->us_clientstate == UPAPCS_AUTHREQ && u->us_timeouttime > 0)
+ UNTIMEOUT(upap_timeout, u);
+ if (u->us_serverstate == UPAPSS_LISTEN && u->us_reqtimeout > 0)
+ UNTIMEOUT(upap_reqtimeout, u);
+
+ u->us_clientstate = UPAPCS_INITIAL;
+ u->us_serverstate = UPAPSS_INITIAL;
+}
+
+
+/*
+ * upap_protrej - Peer doesn't speak this protocol.
+ *
+ * This shouldn't happen. In any case, pretend lower layer went down.
+ */
+static void
+upap_protrej(unit)
+ int unit;
+{
+ upap_state *u = &upap[unit];
+
+ if (u->us_clientstate == UPAPCS_AUTHREQ) {
+ error("PAP authentication failed due to protocol-reject");
+ auth_withpeer_fail(unit, PPP_PAP);
+ }
+ if (u->us_serverstate == UPAPSS_LISTEN) {
+ error("PAP authentication of peer failed (protocol-reject)");
+ auth_peer_fail(unit, PPP_PAP);
+ }
+ upap_lowerdown(unit);
+}
+
+
+/*
+ * upap_input - Input UPAP packet.
+ */
+static void
+upap_input(unit, inpacket, l)
+ int unit;
+ u_char *inpacket;
+ int l;
+{
+ upap_state *u = &upap[unit];
+ u_char *inp;
+ u_char code, id;
+ int len;
+
+ /*
+ * Parse header (code, id and length).
+ * If packet too short, drop it.
+ */
+ inp = inpacket;
+ if (l < UPAP_HEADERLEN) {
+ error("PAP: packet is too small (%d < %d)", l, UPAP_HEADERLEN);
+ return;
+ }
+ GETCHAR(code, inp);
+ GETCHAR(id, inp);
+ GETSHORT(len, inp);
+ if ((len < UPAP_HEADERLEN) || (len > l)) {
+ error("PAP: packet has illegal length %d (%d..%d)", len,
+ UPAP_HEADERLEN, l);
+ return;
+ }
+ len -= UPAP_HEADERLEN;
+
+ /*
+ * Action depends on code.
+ */
+ switch (code) {
+ case UPAP_AUTHREQ:
+ upap_rauthreq(u, inp, id, len);
+ break;
+
+ case UPAP_AUTHACK:
+ upap_rauthack(u, inp, id, len);
+ break;
+
+ case UPAP_AUTHNAK:
+ upap_rauthnak(u, inp, id, len);
+ break;
+
+ default:
+ warn("Unknown PAP code (%d) received.", code);
+ break;
+ }
+}
+
+
+/*
+ * upap_rauth - Receive Authenticate.
+ */
+static void
+upap_rauthreq(u, inp, id, len)
+ upap_state *u;
+ u_char *inp;
+ int id;
+ int len;
+{
+ u_char ruserlen, rpasswdlen;
+ char *ruser, *rpasswd;
+ int retcode;
+ char *msg;
+ int msglen;
+
+ if (u->us_serverstate < UPAPSS_LISTEN) {
+ info("PAP: discarded Authenticate-Request in state %s",
+ pap_sstate(u->us_serverstate));
+ return;
+ }
+
+ /*
+ * If we receive a duplicate authenticate-request, we are
+ * supposed to return the same status as for the first request.
+ */
+ if (u->us_serverstate == UPAPSS_OPEN) {
+ /* return auth-ack */
+ upap_sresp(u, UPAP_AUTHACK, id, u->us_msg, u->us_msglen);
+ return;
+ }
+ if (u->us_serverstate == UPAPSS_BADAUTH) {
+ /* return auth-nak */
+ upap_sresp(u, UPAP_AUTHNAK, id, u->us_msg, u->us_msglen);
+ return;
+ }
+
+ /*
+ * Parse user/passwd.
+ */
+ if (len < 1) {
+ error("PAP: rcvd short packet; no data");
+ return;
+ }
+ GETCHAR(ruserlen, inp);
+ len -= sizeof (u_char) + ruserlen + sizeof (u_char);
+ if (len < 0) {
+ error("PAP: rcvd short packet; peer name missing");
+ return;
+ }
+ ruser = (char *) inp;
+ INCPTR(ruserlen, inp);
+ GETCHAR(rpasswdlen, inp);
+ if (len < rpasswdlen) {
+ error("PAP: rcvd short packet; pass len %d < %d", len, rpasswdlen);
+ return;
+ }
+ rpasswd = (char *) inp;
+
+ /*
+ * Check the username and password given.
+ */
+ retcode = check_passwd(u->us_unit, ruser, ruserlen, rpasswd,
+ rpasswdlen, &msg);
+ BZERO(rpasswd, rpasswdlen);
+ msglen = strlen(msg);
+ if (msglen > 255)
+ msglen = 255;
+
+ u->us_msg = msg;
+ u->us_msglen = msglen;
+ upap_sresp(u, retcode, id, u->us_msg, u->us_msglen);
+
+ if (retcode == UPAP_AUTHACK) {
+ u->us_serverstate = UPAPSS_OPEN;
+ auth_peer_success(u->us_unit, PPP_PAP, ruser, ruserlen);
+ } else if (++u->us_receives >= u->us_maxreceives) {
+ u->us_serverstate = UPAPSS_BADAUTH;
+ auth_peer_fail(u->us_unit, PPP_PAP);
+ } else {
+ /* Just wait for a good one to arrive, or for time-out. */
+ return;
+ }
+
+ if (u->us_reqtimeout > 0)
+ UNTIMEOUT(upap_reqtimeout, u);
+}
+
+
+/*
+ * upap_rauthack - Receive Authenticate-Ack.
+ */
+/*ARGSUSED*/
+static void
+upap_rauthack(u, inp, id, len)
+ upap_state *u;
+ u_char *inp;
+ int id;
+ int len;
+{
+ u_char msglen;
+ char *msg;
+
+ if (u->us_clientstate != UPAPCS_AUTHREQ) {
+ info("PAP: discarded Authenticate-Ack in state %s",
+ pap_cstate(u->us_clientstate));
+ return;
+ }
+
+ if (id != u->us_id) {
+ dbglog("PAP: discard Authenticate-Ack; ID %d != %d",
+ id, u->us_id);
+ return;
+ }
+
+ if (u->us_timeouttime > 0)
+ UNTIMEOUT(upap_timeout, u);
+
+ /*
+ * Parse message.
+ */
+ if (len < 1) {
+ info("PAP: Ignoring missing ack msg-length octet");
+ } else {
+ GETCHAR(msglen, inp);
+ if (msglen > 0) {
+ len -= sizeof (u_char);
+ if (len < msglen) {
+ error("PAP: Discarding short packet (%d < %d)", len, msglen);
+ return;
+ }
+ msg = (char *) inp;
+ PRINTMSG(msg, msglen);
+ }
+ }
+
+ u->us_clientstate = UPAPCS_OPEN;
+
+ auth_withpeer_success(u->us_unit, PPP_PAP);
+}
+
+
+/*
+ * upap_rauthnak - Receive Authenticate-Nakk.
+ */
+/*ARGSUSED*/
+static void
+upap_rauthnak(u, inp, id, len)
+ upap_state *u;
+ u_char *inp;
+ int id;
+ int len;
+{
+ u_char msglen;
+ char *msg;
+
+ if (u->us_clientstate != UPAPCS_AUTHREQ) {
+ info("PAP: discarded Authenticate-Nak in state %s",
+ pap_cstate(u->us_clientstate));
+ return;
+ }
+
+ if (id != u->us_id) {
+ dbglog("PAP: discard Authenticate-Ack; ID %d != %d",
+ id, u->us_id);
+ return;
+ }
+
+ if (u->us_timeouttime > 0)
+ UNTIMEOUT(upap_timeout, u);
+
+ /*
+ * Parse message.
+ */
+ if (len < 1) {
+ error("PAP: ignoring missing nak msg-length octet");
+ } else {
+ GETCHAR(msglen, inp);
+ if (msglen > 0) {
+ len -= sizeof (u_char);
+ if (len < msglen) {
+ error("PAP: Discarding short packet (%d < %d)", len, msglen);
+ return;
+ }
+ msg = (char *) inp;
+ PRINTMSG(msg, msglen);
+ }
+ }
+
+ /* Try to get a new password from the plugin. */
+ if (pap_passwd_hook != NULL) {
+ if (u->us_transmits < u->us_maxtransmits) {
+ if ((*pap_passwd_hook)(user, passwd) >= 0) {
+ upap_sauthreq(u);
+ return;
+ }
+ } else {
+ /* Tell plug-in that we're giving up. */
+ (void) (*pap_passwd_hook)(NULL, NULL);
+ }
+ }
+
+ u->us_clientstate = UPAPCS_BADAUTH;
+
+ error("PAP authentication failed");
+ auth_withpeer_fail(u->us_unit, PPP_PAP);
+}
+
+
+/*
+ * upap_sauthreq - Send an Authenticate-Request.
+ */
+static void
+upap_sauthreq(u)
+ upap_state *u;
+{
+ u_char *outp;
+ int pwlen;
+ int outlen;
+
+ pwlen = strllen(passwd, MAXSECRETLEN);
+ if (pwlen > 0xFF)
+ pwlen = 0xFF;
+ outlen = UPAP_HEADERLEN + 2 * sizeof (u_char) + u->us_userlen + pwlen;
+ outp = outpacket_buf;
+
+ MAKEHEADER(outp, PPP_PAP);
+
+ PUTCHAR(UPAP_AUTHREQ, outp);
+ PUTCHAR(++u->us_id, outp);
+ PUTSHORT(outlen, outp);
+ PUTCHAR(u->us_userlen, outp);
+ BCOPY(u->us_user, outp, u->us_userlen);
+ INCPTR(u->us_userlen, outp);
+ PUTCHAR(pwlen, outp);
+ BCOPY(u->us_passwd, outp, pwlen);
+
+ output(u->us_unit, outpacket_buf, outlen + PPP_HDRLEN);
+
+ if (u->us_timeouttime > 0)
+ TIMEOUT(upap_timeout, u, u->us_timeouttime);
+ ++u->us_transmits;
+ u->us_clientstate = UPAPCS_AUTHREQ;
+}
+
+
+/*
+ * upap_sresp - Send a response (ack or nak).
+ */
+static void
+upap_sresp(u, code, id, msg, msglen)
+ upap_state *u;
+ u_char code, id;
+ char *msg;
+ int msglen;
+{
+ u_char *outp;
+ int outlen;
+
+ outlen = UPAP_HEADERLEN + sizeof (u_char) + msglen;
+ outp = outpacket_buf;
+ MAKEHEADER(outp, PPP_PAP);
+
+ PUTCHAR(code, outp);
+ PUTCHAR(id, outp);
+ PUTSHORT(outlen, outp);
+ PUTCHAR(msglen, outp);
+ BCOPY(msg, outp, msglen);
+ output(u->us_unit, outpacket_buf, outlen + PPP_HDRLEN);
+}
+
+/*
+ * upap_printpkt - print the contents of a PAP packet.
+ */
+static char *upap_codenames[] = {
+ "AuthReq", "AuthAck", "AuthNak"
+};
+
+static int
+upap_printpkt(p, plen, printer, arg)
+ u_char *p;
+ int plen;
+ void (*printer) __P((void *, const char *, ...));
+ void *arg;
+{
+ int code, id, len;
+ int mlen, ulen, wlen;
+ char *user, *pwd, *msg;
+ u_char *pstart;
+
+ if (plen < UPAP_HEADERLEN)
+ return (0);
+ pstart = p;
+ GETCHAR(code, p);
+ GETCHAR(id, p);
+ GETSHORT(len, p);
+ if (len < UPAP_HEADERLEN || len > plen)
+ return (0);
+
+ if (code >= 1 && code <= Dim(upap_codenames))
+ printer(arg, " %s", upap_codenames[code-1]);
+ else
+ printer(arg, " code=0x%x", code);
+ printer(arg, " id=0x%x", id);
+ len -= UPAP_HEADERLEN;
+ switch (code) {
+ case UPAP_AUTHREQ:
+ if (len < 1)
+ break;
+ ulen = p[0];
+ if (len < ulen + 2)
+ break;
+ wlen = p[ulen + 1];
+ if (len < ulen + wlen + 2)
+ break;
+ user = (char *) (p + 1);
+ pwd = (char *) (p + ulen + 2);
+ p += ulen + wlen + 2;
+ len -= ulen + wlen + 2;
+ printer(arg, " user=");
+ print_string(user, ulen, printer, arg);
+ printer(arg, " password=");
+ if (!hide_password)
+ print_string(pwd, wlen, printer, arg);
+ else
+ printer(arg, "<hidden>");
+ break;
+ case UPAP_AUTHACK:
+ case UPAP_AUTHNAK:
+ if (len < 1)
+ break;
+ mlen = p[0];
+ if (len < mlen + 1)
+ break;
+ msg = (char *) (p + 1);
+ p += mlen + 1;
+ len -= mlen + 1;
+ printer(arg, " ");
+ print_string(msg, mlen, printer, arg);
+ break;
+ }
+
+ /* print the rest of the bytes in the packet */
+ for (; len > 0; --len) {
+ GETCHAR(code, p);
+ printer(arg, " %.2x", code);
+ }
+
+ return (p - pstart);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppd/upap.h b/usr/src/cmd/cmd-inet/usr.bin/pppd/upap.h
new file mode 100644
index 0000000000..068aedcf69
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppd/upap.h
@@ -0,0 +1,126 @@
+/*
+ * upap.h - User/Password Authentication Protocol definitions.
+ *
+ * Copyright (c) 2000 by Sun Microsystems, Inc.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies.
+ *
+ * SUN MAKES NO REPRESENTATION OR WARRANTIES ABOUT THE SUITABILITY OF
+ * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+ * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
+ * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
+ * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: upap.h,v 1.7 1999/11/15 01:51:54 paulus Exp $
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifndef __UPAP_H__
+#define __UPAP_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Packet header = Code, id, length.
+ */
+#define UPAP_HEADERLEN 4
+
+
+/*
+ * UPAP codes.
+ */
+#define UPAP_AUTHREQ 1 /* Authenticate-Request */
+#define UPAP_AUTHACK 2 /* Authenticate-Ack */
+#define UPAP_AUTHNAK 3 /* Authenticate-Nak */
+
+
+/*
+ * Each interface is described by upap structure.
+ */
+typedef struct upap_state {
+ int us_unit; /* Interface unit number */
+ char *us_user; /* User */
+ int us_userlen; /* User length */
+ char *us_passwd; /* Password */
+ int us_clientstate; /* Client state */
+ int us_serverstate; /* Server state */
+ u_char us_id; /* Current id */
+ int us_timeouttime; /* Timeout (seconds) for auth-req retrans. */
+ int us_transmits; /* Number of auth-reqs sent */
+ int us_maxtransmits; /* Maximum number of auth-reqs to send */
+ int us_receives; /* Number of auth-reqs received */
+ int us_maxreceives; /* Maximum number of auth-reqs allowed */
+ int us_reqtimeout; /* Time to wait for auth-req from peer */
+ char *us_msg; /* Authentication response message */
+ int us_msglen; /* and its length */
+} upap_state;
+
+
+/*
+ * Client states.
+ */
+#define UPAPCS_INITIAL 0 /* Connection down */
+#define UPAPCS_CLOSED 1 /* Connection up, haven't requested auth */
+#define UPAPCS_PENDING 2 /* Connection down, have requested auth */
+#define UPAPCS_AUTHREQ 3 /* We've sent an Authenticate-Request */
+#define UPAPCS_OPEN 4 /* We've received an Ack */
+#define UPAPCS_BADAUTH 5 /* We've received a Nak */
+
+#define UPAPCS__NAMES \
+ "CliInitial", "CliClosed", "CliPending", "CliAuthReq", "CliOpen", \
+ "CliBadAuth"
+
+/*
+ * Server states.
+ */
+#define UPAPSS_INITIAL 0 /* Connection down */
+#define UPAPSS_CLOSED 1 /* Connection up, haven't requested auth */
+#define UPAPSS_PENDING 2 /* Connection down, have requested auth */
+#define UPAPSS_LISTEN 3 /* Listening for an Authenticate */
+#define UPAPSS_OPEN 4 /* We've sent an Ack */
+#define UPAPSS_BADAUTH 5 /* We've sent a Nak */
+
+#define UPAPSS__NAMES \
+ "SrvInitial", "SrvClosed", "SrvPending", "SrvListen", "SrvOpen", \
+ "SrvBadAuth"
+
+/*
+ * Timeouts.
+ */
+#define UPAP_DEFTIMEOUT 3 /* Timeout (seconds) for retransmitting req */
+#define UPAP_DEFREQTIME 30 /* Time to wait for auth-req from peer */
+
+extern upap_state upap[];
+
+void upap_authwithpeer __P((int, char *, char *));
+void upap_authpeer __P((int));
+
+extern struct protent pap_protent;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __UPAP_H__ */
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppd/utils.c b/usr/src/cmd/cmd-inet/usr.bin/pppd/utils.c
new file mode 100644
index 0000000000..5c5638bc40
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppd/utils.c
@@ -0,0 +1,1054 @@
+/*
+ * utils.c - various utility functions used in pppd.
+ *
+ * Copyright (c) 2000-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies.
+ *
+ * SUN MAKES NO REPRESENTATION OR WARRANTIES ABOUT THE SUITABILITY OF
+ * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+ * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
+ * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
+ * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES
+ *
+ * Copyright (c) 1999 The Australian National University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Australian National University. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+#define RCSID "$Id: utils.c,v 1.10 2000/03/27 01:36:48 paulus Exp $"
+
+#ifdef __linux__
+#define _GNU_SOURCE
+#endif
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <netdb.h>
+#include <utmp.h>
+#include <pwd.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#ifdef SVR4
+#include <sys/mkdev.h>
+#endif
+
+#include "pppd.h"
+
+#if !defined(lint) && !defined(_lint)
+static const char rcsid[] = RCSID;
+#endif
+
+#if defined(SUNOS4)
+extern char *strerror();
+#endif
+
+/* Don't log to stdout until we're sure it's ok to do so. */
+bool early_log = 1;
+
+static void pr_log __P((void *, const char *, ...));
+static void logit __P((int, const char *, va_list));
+static void vslp_printer __P((void *, const char *, ...));
+static void format_packet __P((u_char *, int,
+ void (*) (void *, const char *, ...), void *));
+
+struct buffer_info {
+ char *ptr;
+ int len;
+};
+
+/*
+ * strllen - like strlen, but doesn't run past end of input.
+ */
+size_t
+strllen(str, len)
+ const char *str;
+ size_t len;
+{
+ size_t ret;
+
+ for (ret = 0; ret < len; ret++)
+ if (*str++ == '\0')
+ break;
+ return (ret);
+}
+
+/*
+ * strlcpy - like strcpy/strncpy, doesn't overflow destination buffer,
+ * always leaves destination null-terminated (for len > 0).
+ */
+size_t
+strlcpy(dest, src, len)
+ char *dest;
+ const char *src;
+ size_t len;
+{
+ size_t ret = strlen(src);
+
+ if (len != 0) {
+ if (ret < len)
+ (void) strcpy(dest, src);
+ else {
+ (void) strncpy(dest, src, len - 1);
+ dest[len-1] = 0;
+ }
+ }
+ return (ret);
+}
+
+/*
+ * strlcat - like strcat/strncat, doesn't overflow destination buffer,
+ * always leaves destination null-terminated (for len > 0).
+ */
+size_t
+strlcat(dest, src, len)
+ char *dest;
+ const char *src;
+ size_t len;
+{
+ size_t dlen = strlen(dest);
+
+ return (dlen + strlcpy(dest + dlen, src, (len > dlen? len - dlen: 0)));
+}
+
+
+/*
+ * slprintf - format a message into a buffer. Like sprintf except we
+ * also specify the length of the output buffer, and we handle %m
+ * (error message), %v (visible string), %q (quoted string), %t
+ * (current time), %I (IP address), %P (PPP packet), and %B (sequence
+ * of bytes) formats. Doesn't do floating-point formats. Returns the
+ * number of chars put into buf.
+ */
+int
+slprintf __V((char *buf, int buflen, const char *fmt, ...))
+{
+ va_list args;
+ int n;
+
+#if defined(__STDC__)
+ va_start(args, fmt);
+#else
+ char *buf;
+ int buflen;
+ const char *fmt;
+ va_start(args);
+ buf = va_arg(args, char *);
+ buflen = va_arg(args, int);
+ fmt = va_arg(args, const char *);
+#endif
+ n = vslprintf(buf, buflen, fmt, args);
+ va_end(args);
+ return (n);
+}
+
+/*
+ * Print to file or, if argument is NULL, to syslog at debug level.
+ */
+int
+flprintf __V((FILE *strptr, const char *fmt, ...))
+{
+ va_list args;
+ int n;
+ char buf[1024], *bp, *nlp, *ebp;
+
+#if defined(__STDC__)
+ va_start(args, fmt);
+#else
+ FILE *strptr;
+ const char *fmt;
+ va_start(args);
+ strptr = va_arg(args, FILE *);
+ fmt = va_arg(args, const char *);
+#endif
+ n = vslprintf(buf, sizeof (buf), fmt, args);
+ va_end(args);
+ if (strptr == NULL) {
+ bp = buf;
+ ebp = buf + n;
+ while (bp < ebp) {
+ if ((nlp = strchr(bp, '\n')) == NULL)
+ nlp = ebp;
+ if (nlp > bp) {
+ *nlp = '\0';
+ syslog(LOG_DEBUG, "%s", bp);
+ }
+ bp = nlp + 1;
+ }
+ } else {
+ n = fwrite(buf, 1, n, strptr);
+ }
+ return (n);
+}
+
+/*
+ * vslprintf - like slprintf, takes a va_list instead of a list of args.
+ */
+#define OUTCHAR(c) (buflen > 0? (--buflen, *buf++ = (c)): 0)
+
+int
+vslprintf(buf, buflen, fmt, args)
+ char *buf;
+ int buflen;
+ const char *fmt;
+ va_list args;
+{
+ int c, n, longs;
+ int width, prec, fillch;
+ int base, len, neg, quoted;
+#ifdef SOL2
+ uint64_t val;
+ int64_t sval;
+#else
+ unsigned long val;
+ long sval;
+#endif
+ char *buf0, *mstr;
+ const char *f, *str;
+ unsigned char *p;
+ char num[32]; /* 2^64 is 20 chars decimal, 22 octal */
+ time_t t;
+ u_int32_t ip;
+ static const char hexchars[] = "0123456789abcdef";
+ struct buffer_info bufinfo;
+
+ buf0 = buf;
+ --buflen;
+ while (buflen > 0) {
+ for (f = fmt; *f != '%' && *f != 0; ++f)
+ ;
+ if (f > fmt) {
+ len = f - fmt;
+ if (len > buflen)
+ len = buflen;
+ (void) memcpy(buf, fmt, len);
+ buf += len;
+ buflen -= len;
+ fmt = f;
+ }
+ if (*fmt == 0)
+ break;
+ c = *++fmt;
+ width = 0;
+ prec = -1;
+ fillch = ' ';
+ if (c == '0') {
+ fillch = '0';
+ c = *++fmt;
+ }
+ if (c == '*') {
+ width = va_arg(args, int);
+ c = *++fmt;
+ } else {
+ while (isdigit(c)) {
+ width = width * 10 + c - '0';
+ c = *++fmt;
+ }
+ }
+ if (c == '.') {
+ c = *++fmt;
+ if (c == '*') {
+ prec = va_arg(args, int);
+ c = *++fmt;
+ } else {
+ prec = 0;
+ while (isdigit(c)) {
+ prec = prec * 10 + c - '0';
+ c = *++fmt;
+ }
+ }
+ }
+ longs = 0;
+ if (c == 'l') {
+ longs++;
+ c = *++fmt;
+ if (c == 'l') {
+ longs++;
+ c = *++fmt;
+ }
+ }
+ str = 0;
+ base = 0;
+ neg = 0;
+ val = 0;
+ ++fmt;
+ switch (c) {
+ case 'u':
+#ifdef SOL2
+ if (longs >= 2)
+ val = va_arg(args, uint64_t);
+ else
+#endif
+ if (longs > 0)
+ val = va_arg(args, unsigned long);
+ else
+ val = va_arg(args, unsigned int);
+ base = 10;
+ break;
+ case 'd':
+#ifdef SOL2
+ if (longs >= 2)
+ sval = va_arg(args, int64_t);
+ else
+#endif
+ if (longs > 0)
+ sval = va_arg(args, long);
+ else
+ sval = va_arg(args, int);
+ if (sval < 0) {
+ neg = 1;
+ val = -sval;
+ } else
+ val = sval;
+ base = 10;
+ break;
+ case 'o':
+#ifdef SOL2
+ if (longs >= 2)
+ val = va_arg(args, uint64_t);
+ else
+#endif
+ if (longs > 0)
+ val = va_arg(args, unsigned long);
+ else
+ val = va_arg(args, unsigned int);
+ base = 8;
+ break;
+ case 'x':
+ case 'X':
+#ifdef SOL2
+ if (longs >= 2)
+ val = va_arg(args, uint64_t);
+ else
+#endif
+ if (longs > 0)
+ val = va_arg(args, unsigned long);
+ else
+ val = va_arg(args, unsigned int);
+ base = 16;
+ break;
+ case 'p':
+ val = (unsigned long) va_arg(args, void *);
+ base = 16;
+ neg = 2;
+ break;
+ case 's':
+ str = va_arg(args, const char *);
+ break;
+ case 'c':
+ num[0] = va_arg(args, int);
+ num[1] = 0;
+ str = num;
+ break;
+ case 'm':
+ str = strerror(errno);
+ break;
+ case 'I':
+ ip = va_arg(args, u_int32_t);
+ ip = ntohl(ip);
+ (void) slprintf(num, sizeof(num), "%d.%d.%d.%d", (ip >> 24) & 0xff,
+ (ip >> 16) & 0xff, (ip >> 8) & 0xff, ip & 0xff);
+ str = num;
+ break;
+ case 't':
+ (void) time(&t);
+ mstr = ctime(&t);
+ mstr += 4; /* chop off the day name */
+ mstr[15] = 0; /* chop off year and newline */
+ str = (const char *)mstr;
+ break;
+ case 'v': /* "visible" string */
+ case 'q': /* quoted string */
+ quoted = c == 'q';
+ p = va_arg(args, unsigned char *);
+ if (fillch == '0' && prec >= 0) {
+ n = prec;
+ } else {
+ n = strlen((char *)p);
+ if (prec >= 0 && n > prec)
+ n = prec;
+ }
+ while (n > 0 && buflen > 0) {
+ c = *p++;
+ --n;
+ if (!quoted && c >= 0x80) {
+ (void) OUTCHAR('M');
+ (void) OUTCHAR('-');
+ c -= 0x80;
+ }
+ if (quoted && (c == '"' || c == '\\'))
+ (void) OUTCHAR('\\');
+ if (c < 0x20 || (0x7f <= c && c < 0xa0)) {
+ if (quoted) {
+ (void) OUTCHAR('\\');
+ switch (c) {
+ case '\t': (void) OUTCHAR('t'); break;
+ case '\n': (void) OUTCHAR('n'); break;
+ case '\b': (void) OUTCHAR('b'); break;
+ case '\f': (void) OUTCHAR('f'); break;
+ default:
+ (void) OUTCHAR('x');
+ (void) OUTCHAR(hexchars[c >> 4]);
+ (void) OUTCHAR(hexchars[c & 0xf]);
+ }
+ } else {
+ if (c == '\t')
+ (void) OUTCHAR(c);
+ else {
+ (void) OUTCHAR('^');
+ (void) OUTCHAR(c ^ 0x40);
+ }
+ }
+ } else
+ (void) OUTCHAR(c);
+ }
+ continue;
+ case 'P': /* print PPP packet */
+ bufinfo.ptr = buf;
+ bufinfo.len = buflen + 1;
+ p = va_arg(args, unsigned char *);
+ n = va_arg(args, int);
+ format_packet(p, n, vslp_printer, &bufinfo);
+ buf = bufinfo.ptr;
+ buflen = bufinfo.len - 1;
+ continue;
+ case 'B':
+ p = va_arg(args, unsigned char *);
+ if ((n = prec) > width && width > 0)
+ n = width;
+ /* For safety's sake */
+ if (n > 2000)
+ n = 2000;
+ while (--n >= 0) {
+ c = *p++;
+ if (fillch == ' ')
+ (void) OUTCHAR(' ');
+ (void) OUTCHAR(hexchars[(c >> 4) & 0xf]);
+ (void) OUTCHAR(hexchars[c & 0xf]);
+ }
+ if (prec > width && width > 0) {
+ (void) OUTCHAR('.');
+ (void) OUTCHAR('.');
+ (void) OUTCHAR('.');
+ }
+ continue;
+ default:
+ *buf++ = '%';
+ if (c != '%')
+ --fmt; /* so %z outputs %z etc. */
+ --buflen;
+ continue;
+ }
+ if (base != 0) {
+ mstr = num + sizeof(num);
+ *--mstr = 0;
+ while (mstr > num + neg) {
+ *--mstr = hexchars[val % base];
+ val = val / base;
+ if (--prec <= 0 && val == 0)
+ break;
+ }
+ switch (neg) {
+ case 1:
+ *--mstr = '-';
+ break;
+ case 2:
+ *--mstr = 'x';
+ *--mstr = '0';
+ break;
+ }
+ len = num + sizeof(num) - 1 - mstr;
+ str = (const char *)mstr;
+ } else {
+ len = strlen(str);
+ if (prec >= 0 && len > prec)
+ len = prec;
+ }
+ if (width > 0) {
+ if (width > buflen)
+ width = buflen;
+ if ((n = width - len) > 0) {
+ buflen -= n;
+ for (; n > 0; --n)
+ *buf++ = fillch;
+ }
+ }
+ if (len > buflen)
+ len = buflen;
+ (void) memcpy(buf, str, len);
+ buf += len;
+ buflen -= len;
+ }
+ *buf = 0;
+ return (buf - buf0);
+}
+
+/*
+ * vslp_printer - used in processing a %P format
+ */
+static void
+vslp_printer __V((void *arg, const char *fmt, ...))
+{
+ int n;
+ va_list pvar;
+ struct buffer_info *bi;
+
+#if defined(__STDC__)
+ va_start(pvar, fmt);
+#else
+ void *arg;
+ const char *fmt;
+ va_start(pvar);
+ arg = va_arg(pvar, void *);
+ fmt = va_arg(pvar, const char *);
+#endif
+
+ bi = (struct buffer_info *) arg;
+ n = vslprintf(bi->ptr, bi->len, fmt, pvar);
+ va_end(pvar);
+
+ bi->ptr += n;
+ bi->len -= n;
+}
+
+/*
+ * log_packet - format a packet and log it.
+ */
+
+static char line[256]; /* line to be logged accumulated here */
+static char *linep;
+
+void
+log_packet(p, len, prefix, level)
+ u_char *p;
+ int len;
+ const char *prefix;
+ int level;
+{
+ (void) strlcpy(line, prefix, sizeof(line));
+ linep = line + strlen(line);
+ format_packet(p, len, pr_log, (void *)level);
+ if (linep != line)
+ syslog(level, "%s", line);
+}
+
+/*
+ * format_packet - make a readable representation of a packet,
+ * calling `printer(arg, format, ...)' to output it.
+ */
+static void
+format_packet(p, len, printer, arg)
+ u_char *p;
+ int len;
+ void (*printer) __P((void *, const char *, ...));
+ void *arg;
+{
+ int i, n;
+ u_short proto;
+ struct protent *protp;
+
+ if (len >= PPP_HDRLEN && p[0] == PPP_ALLSTATIONS && p[1] == PPP_UI) {
+ p += 2;
+ GETSHORT(proto, p);
+ len -= PPP_HDRLEN;
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ if (proto == protp->protocol)
+ break;
+ if (protp != NULL) {
+ printer(arg, "[%s", protp->name);
+ n = (*protp->printpkt)(p, len, printer, arg);
+ printer(arg, "]");
+ p += n;
+ len -= n;
+ } else {
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ if (proto == (protp->protocol & ~0x8000))
+ break;
+ if (protp != NULL && protp->data_name != NULL) {
+ printer(arg, "[%s data] %8.*B", protp->data_name, len, p);
+ len = 0;
+ } else
+ printer(arg, "[proto=0x%x]", proto);
+ }
+ }
+
+ printer(arg, "%32.*B", len, p);
+}
+
+static void
+pr_log __V((void *arg, const char *fmt, ...))
+{
+ int n;
+ va_list pvar;
+ char buf[256];
+
+#if defined(__STDC__)
+ va_start(pvar, fmt);
+#else
+ void *arg;
+ const char *fmt;
+ va_start(pvar);
+ arg = va_arg(pvar, void *);
+ fmt = va_arg(pvar, const char *);
+#endif
+
+ n = vslprintf(buf, sizeof(buf), fmt, pvar);
+ va_end(pvar);
+
+ if (linep + n + 1 > line + sizeof(line)) {
+ syslog((int)arg, "%s", line);
+ linep = line;
+ }
+ (void) strlcpy(linep, buf, line + sizeof(line) - linep);
+ linep += n;
+}
+
+/*
+ * print_string - print a readable representation of a string using
+ * printer.
+ */
+void
+print_string(p, len, printer, arg)
+ char *p;
+ int len;
+ void (*printer) __P((void *, const char *, ...));
+ void *arg;
+{
+ int c;
+
+ printer(arg, "\"");
+ for (; len > 0; --len) {
+ c = *p++;
+ if (isprint(c)) {
+ if (c == '\\' || c == '"')
+ printer(arg, "\\");
+ printer(arg, "%c", c);
+ } else {
+ switch (c) {
+ case '\n':
+ printer(arg, "\\n");
+ break;
+ case '\r':
+ printer(arg, "\\r");
+ break;
+ case '\t':
+ printer(arg, "\\t");
+ break;
+ default:
+ printer(arg, "\\%.3o", c);
+ }
+ }
+ }
+ printer(arg, "\"");
+}
+
+/*
+ * logit - does the hard work for fatal et al.
+ */
+static void
+logit(level, fmt, args)
+ int level;
+ const char *fmt;
+ va_list args;
+{
+ int n;
+ char buf[1024];
+
+ n = vslprintf(buf, sizeof(buf), fmt, args);
+ syslog(level, "%s", buf);
+ if (log_to_fd >= 0 && (level != LOG_DEBUG || debug) &&
+ (!early_log || log_to_specific_fd)) {
+ if (buf[n-1] != '\n')
+ buf[n++] = '\n';
+ if (write(log_to_fd, buf, n) != n)
+ log_to_fd = -1;
+ }
+}
+
+/*
+ * fatal - log an error message and die horribly.
+ */
+void
+fatal __V((const char *fmt, ...))
+{
+ va_list pvar;
+
+#if defined(__STDC__)
+ va_start(pvar, fmt);
+#else
+ const char *fmt;
+ va_start(pvar);
+ fmt = va_arg(pvar, const char *);
+#endif
+
+ logit(LOG_ERR, fmt, pvar);
+ va_end(pvar);
+
+ die(1); /* as promised */
+}
+
+/*
+ * error - log an error message.
+ */
+void
+error __V((const char *fmt, ...))
+{
+ va_list pvar;
+
+#if defined(__STDC__)
+ va_start(pvar, fmt);
+#else
+ const char *fmt;
+ va_start(pvar);
+ fmt = va_arg(pvar, const char *);
+#endif
+
+ logit(LOG_ERR, fmt, pvar);
+ va_end(pvar);
+}
+
+/*
+ * warn - log a warning message.
+ */
+void
+warn __V((const char *fmt, ...))
+{
+ va_list pvar;
+
+#if defined(__STDC__)
+ va_start(pvar, fmt);
+#else
+ const char *fmt;
+ va_start(pvar);
+ fmt = va_arg(pvar, const char *);
+#endif
+
+ logit(LOG_WARNING, fmt, pvar);
+ va_end(pvar);
+}
+
+/*
+ * notice - log a notice-level message.
+ */
+void
+notice __V((const char *fmt, ...))
+{
+ va_list pvar;
+
+#if defined(__STDC__)
+ va_start(pvar, fmt);
+#else
+ const char *fmt;
+ va_start(pvar);
+ fmt = va_arg(pvar, const char *);
+#endif
+
+ logit(LOG_NOTICE, fmt, pvar);
+ va_end(pvar);
+}
+
+/*
+ * info - log an informational message.
+ */
+void
+info __V((const char *fmt, ...))
+{
+ va_list pvar;
+
+#if defined(__STDC__)
+ va_start(pvar, fmt);
+#else
+ const char *fmt;
+ va_start(pvar);
+ fmt = va_arg(pvar, const char *);
+#endif
+
+ logit(LOG_INFO, fmt, pvar);
+ va_end(pvar);
+}
+
+/*
+ * dbglog - log a debug message.
+ */
+void
+dbglog __V((const char *fmt, ...))
+{
+ va_list pvar;
+
+#if defined(__STDC__)
+ va_start(pvar, fmt);
+#else
+ const char *fmt;
+ va_start(pvar);
+ fmt = va_arg(pvar, const char *);
+#endif
+
+ logit(LOG_DEBUG, fmt, pvar);
+ va_end(pvar);
+}
+
+/*
+ * Code names for regular PPP messages. Used by LCP and most NCPs,
+ * not used by authentication protocols.
+ */
+const char *
+code_name(int code, int shortflag)
+{
+ static const char *codelist[] = {
+ "Vendor-Extension", "Configure-Request", "Configure-Ack",
+ "Configure-Nak", "Configure-Reject", "Terminate-Request",
+ "Terminate-Ack", "Code-Reject", "Protocol-Reject",
+ "Echo-Request", "Echo-Reply", "Discard-Request",
+ "Identification", "Time-Remaining",
+ "Reset-Request", "Reset-Ack"
+ };
+ static const char *shortcode[] = {
+ "VendExt", "ConfReq", "ConfAck",
+ "ConfNak", "ConfRej", "TermReq",
+ "TermAck", "CodeRej", "ProtRej",
+ "EchoReq", "EchoRep", "DiscReq",
+ "Ident", "TimeRem",
+ "ResetReq", "ResetAck"
+ };
+ static char msgbuf[64];
+
+ if (code < 0 || code >= sizeof (codelist) / sizeof (*codelist)) {
+ if (shortflag)
+ (void) slprintf(msgbuf, sizeof (msgbuf), "Code#%d", code);
+ else
+ (void) slprintf(msgbuf, sizeof (msgbuf), "unknown code %d", code);
+ return ((const char *)msgbuf);
+ }
+ return (shortflag ? shortcode[code] : codelist[code]);
+}
+
+/* Procedures for locking the serial device using a lock file. */
+#ifndef LOCK_DIR
+#ifdef _linux_
+#define LOCK_DIR "/var/lock"
+#else
+#ifdef SVR4
+#define LOCK_DIR "/var/spool/locks"
+#else
+#define LOCK_DIR "/var/spool/lock"
+#endif
+#endif
+#endif /* LOCK_DIR */
+
+static char lock_file[MAXPATHLEN];
+
+/*
+ * lock - create a lock file for the named device
+ */
+int
+lock(dev)
+ char *dev;
+{
+#ifdef LOCKLIB
+ int result;
+
+ result = mklock (dev, (void *) 0);
+ if (result == 0) {
+ (void) strlcpy(lock_file, sizeof(lock_file), dev);
+ return (0);
+ }
+
+ if (result > 0)
+ notice("Device %s is locked by pid %d", dev, result);
+ else
+ error("Can't create lock file %s", lock_file);
+ return (-1);
+
+#else /* LOCKLIB */
+
+ char lock_buffer[12];
+ int fd, pid, n;
+
+#ifdef SVR4
+ struct stat sbuf;
+
+ if (stat(dev, &sbuf) < 0) {
+ error("Can't get device number for %s: %m", dev);
+ return (-1);
+ }
+ if ((sbuf.st_mode & S_IFMT) != S_IFCHR) {
+ error("Can't lock %s: not a character device", dev);
+ return (-1);
+ }
+ (void) slprintf(lock_file, sizeof(lock_file), "%s/LK.%03d.%03d.%03d",
+ LOCK_DIR, major(sbuf.st_dev),
+ major(sbuf.st_rdev), minor(sbuf.st_rdev));
+#else
+ char *p;
+
+ if ((p = strrchr(dev, '/')) != NULL)
+ dev = p + 1;
+ (void) slprintf(lock_file, sizeof(lock_file), "%s/LCK..%s", LOCK_DIR, dev);
+#endif
+
+ while ((fd = open(lock_file, O_EXCL | O_CREAT | O_RDWR, 0644)) < 0) {
+ if (errno != EEXIST) {
+ error("Can't create lock file %s: %m", lock_file);
+ break;
+ }
+
+ /* Read the lock file to find out who has the device locked. */
+ fd = open(lock_file, O_RDONLY, 0);
+ if (fd < 0) {
+ if (errno == ENOENT) /* This is just a timing problem. */
+ continue;
+ error("Can't open existing lock file %s: %m", lock_file);
+ break;
+ }
+#ifndef LOCK_BINARY
+ n = read(fd, lock_buffer, 11);
+#else
+ n = read(fd, &pid, sizeof(pid));
+#endif /* LOCK_BINARY */
+ (void) close(fd);
+ fd = -1;
+ if (n <= 0) {
+ error("Can't read pid from lock file %s", lock_file);
+ break;
+ }
+
+ /* See if the process still exists. */
+#ifndef LOCK_BINARY
+ lock_buffer[n] = 0;
+ pid = atoi(lock_buffer);
+#endif /* LOCK_BINARY */
+ if (pid == getpid())
+ return (1); /* somebody else locked it for us */
+ if (pid == 0
+ || (kill(pid, 0) == -1 && errno == ESRCH)) {
+ if (unlink (lock_file) == 0) {
+ notice("Removed stale lock on %s (pid %d)", dev, pid);
+ continue;
+ }
+ warn("Couldn't remove stale lock on %s", dev);
+ } else
+ notice("Device %s is locked by pid %d", dev, pid);
+ break;
+ }
+
+ if (fd < 0) {
+ lock_file[0] = 0;
+ return (-1);
+ }
+
+ pid = getpid();
+#ifndef LOCK_BINARY
+ (void) slprintf(lock_buffer, sizeof(lock_buffer), "%10d\n", pid);
+ (void) write (fd, lock_buffer, 11);
+#else
+ (void) write(fd, &pid, sizeof (pid));
+#endif
+ (void) close(fd);
+ return (0);
+
+#endif
+}
+
+/*
+ * relock - called to update our lockfile when we are about to detach,
+ * thus changing our pid (we fork, the child carries on, and the parent dies).
+ * Note that this is called by the parent, with pid equal to the pid
+ * of the child. This avoids a potential race which would exist if
+ * we had the child rewrite the lockfile (the parent might die first,
+ * and another process could think the lock was stale if it checked
+ * between when the parent died and the child rewrote the lockfile).
+ */
+int
+relock(pid)
+ int pid;
+{
+#ifdef LOCKLIB
+ /* XXX is there a way to do this? */
+ return (-1);
+#else /* LOCKLIB */
+
+ int fd;
+ char lock_buffer[12];
+
+ if (lock_file[0] == 0)
+ return (-1);
+ fd = open(lock_file, O_WRONLY, 0);
+ if (fd < 0) {
+ error("Couldn't reopen lock file %s: %m", lock_file);
+ lock_file[0] = 0;
+ return (-1);
+ }
+
+#ifndef LOCK_BINARY
+ (void) slprintf(lock_buffer, sizeof(lock_buffer), "%10d\n", pid);
+ (void) write (fd, lock_buffer, 11);
+#else
+ (void) write(fd, &pid, sizeof(pid));
+#endif /* LOCK_BINARY */
+ (void) close(fd);
+ return (0);
+
+#endif /* LOCKLIB */
+}
+
+/*
+ * unlock - remove our lockfile
+ */
+void
+unlock()
+{
+ if (lock_file[0]) {
+#ifdef LOCKLIB
+ (void) rmlock(lock_file, (void *) 0);
+#else
+ (void) unlink(lock_file);
+#endif
+ lock_file[0] = 0;
+ }
+}
+
+const char *
+signal_name(int signum)
+{
+#if defined(SOL2) || defined(__linux__) || defined(_linux_)
+ const char *cp;
+
+ if ((cp = strsignal(signum)) != NULL)
+ return (cp);
+#else
+ extern char *sys_siglist[];
+ extern int sys_nsig;
+
+ if (signum >= 0 && signum < sys_nsig && sys_siglist[signum] != NULL)
+ return (sys_siglist[signum]);
+#endif
+ return ("??");
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppdump/CHANGES.top b/usr/src/cmd/cmd-inet/usr.bin/pppdump/CHANGES.top
new file mode 100644
index 0000000000..c27c069b17
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppdump/CHANGES.top
@@ -0,0 +1,9 @@
+ident "%Z%%M% %I% %E% SMI"
+
+Copyright (c) 2000 by Sun Microsystems, Inc.
+All rights reserved.
+
+This code is extracted from the ANU ppp-2.4.0 package and modified to
+build in a Solaris environment. You can get the original source here:
+
+ ftp://linuxcare.com.au/pub/ppp
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppdump/COPYING.top b/usr/src/cmd/cmd-inet/usr.bin/pppdump/COPYING.top
new file mode 100644
index 0000000000..d60c31a97a
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppdump/COPYING.top
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppdump/INSTALL.top b/usr/src/cmd/cmd-inet/usr.bin/pppdump/INSTALL.top
new file mode 100644
index 0000000000..a532db765e
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppdump/INSTALL.top
@@ -0,0 +1,14 @@
+ident "%Z%%M% %I% %E% SMI"
+
+Copyright (c) 2000 by Sun Microsystems, Inc.
+All rights reserved.
+
+To install, run make:
+
+ % make
+
+Then, as root, run "make install":
+
+ # make install
+
+See README for more details.
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppdump/LICENSE.top b/usr/src/cmd/cmd-inet/usr.bin/pppdump/LICENSE.top
new file mode 100644
index 0000000000..caf94e8eb0
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppdump/LICENSE.top
@@ -0,0 +1,89 @@
+ident "%Z%%M% %I% %E% SMI"
+
+Copyright (c) 2000 by Sun Microsystems, Inc.
+All rights reserved.
+
+This file contains a summary of the licenses on the software in this
+package. Some of these source files are under GNU Public License.
+Those files may be redistributed under the terms of the GNU General
+Public License version 2 or (at your option) any later version. See
+the COPYING file for details or the GNU web site at
+http://www.gnu.org/.
+
+Copyright (c) 2000 by Sun Microsystems, Inc. All rights reserved.
+
+Copyright (C) 1999 Paul Mackerras. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or (at
+ your option) any later version.
+
+Copyright (c) 1985, 1986 The Regents of the University of California.
+All rights reserved.
+
+ This code is derived from software contributed to Berkeley by
+ James A. Woods, derived from original work by Spencer Thomas
+ and Joseph Orost.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce 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 file is derived from zlib.h and zconf.h from the zlib-0.95
+ distribution by Jean-loup Gailly and Mark Adler, with some additions
+ by Paul Mackerras to aid in implementing Deflate compression and
+ decompression for PPP packets.
+
+ zlib.h -- interface of the 'zlib' general purpose compression library
+ version 0.95, Aug 16th, 1995.
+
+ Copyright (C) 1995 Jean-loup Gailly and Mark Adler
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ Jean-loup Gailly Mark Adler
+ gzip@prep.ai.mit.edu madler@alumni.caltech.edu
+
+ Copyright (c) 1994 The Australian National University.
+ All rights reserved.
+
+ Permission to use, copy, modify, and distribute this software and its
+ documentation is hereby granted, provided that the above copyright
+ notice appears in all copies. This software is provided without any
+ warranty, express or implied. The Australian National University
+ makes no representations about the suitability of this software for
+ any purpose.
+
+ Copyright 1999 Paul Mackerras, Alan Curry.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version
+ 2 of the License, or (at your option) any later version.
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppdump/Makefile b/usr/src/cmd/cmd-inet/usr.bin/pppdump/Makefile
new file mode 100644
index 0000000000..c85a91b50b
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppdump/Makefile
@@ -0,0 +1,68 @@
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 2000-2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+# cmd/cmd-inet/usr.bin/pppdump/Makefile
+#
+
+PROG= pppdump
+OBJS= bsd-comp.o deflate.o pppdump.o zlib.o
+SRCS= $(OBJS:%.o=%.c)
+
+include ../../../Makefile.cmd
+
+CPPFLAGS += -DPPP_DEFS_IN_NET
+
+.KEEP_STATE:
+
+.PARALLEL: $(OBJS)
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+ $(LINK.c) $(OBJS) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+$(ROOTMAN1M)/pppdump.1m:= FILEMODE=0444
+$(ROOTMAN1M)/%: %
+ $(INS.file)
+$(ROOTMAN1M):
+ $(INS.dir)
+
+ROOTSRC= $(ROOT)/usr/share/src/ppputil
+TOPFILES= Makefile CHANGES COPYING INSTALL LICENSE README
+ROOTTOPFILES= $(TOPFILES:%=$(ROOTSRC)/%)
+$(ROOTTOPFILES) := FILEMODE= 0444
+ROOTDIST= $(ROOTSRC)/pppdump
+DISTFILES= Makefile bsd-comp.c deflate.c ppp-comp.h pppdump.1m pppdump.c \
+ zlib.c zlib.h
+ROOTDISTFILES= $(DISTFILES:%=$(ROOTDIST)/%)
+$(ROOTDISTFILES) := FILEMODE= 0444
+
+install: all $(ROOTPROG) install_src install_man
+
+install_man: $(ROOTMAN1M) $(ROOTMAN1M)/pppdump.1m
+
+install_src: $(ROOTSRC) .WAIT $(ROOTTOPFILES) $(ROOTDIST) .WAIT \
+ $(ROOTDISTFILES)
+
+$(ROOTSRC) $(ROOTDIST):
+ $(INS.dir)
+
+$(ROOTSRC)/%: %.top
+ $(INS.rename)
+
+$(ROOTDIST)/Makefile%: Makefile%.dist
+ $(INS.rename)
+
+$(ROOTDIST)/%: %
+ $(INS.file)
+
+clean:
+ $(RM) $(OBJS)
+
+lint:
+
+include ../../../Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppdump/Makefile.dist b/usr/src/cmd/cmd-inet/usr.bin/pppdump/Makefile.dist
new file mode 100644
index 0000000000..2876a13462
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppdump/Makefile.dist
@@ -0,0 +1,28 @@
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 2000 by Sun Microsystems, Inc.
+# All rights reserved.
+
+TARGET= pppdump
+OBJS= bsd-comp.o deflate.o pppdump.o zlib.o
+BINDIR= /usr/bin
+MANDIR= /usr/share/man/man1m
+MANFILES= pppdump.1m
+CFLAGS= -DPPP_DEFS_IN_NET
+
+all: $(TARGET)
+
+clean:
+ $(RM) -f $(TARGET) $(OBJS)
+
+$(TARGET): $(OBJS)
+ $(CC) -o $@ $(OBJS)
+
+install: $(TARGET)
+ @cp $(TARGET) $(BINDIR) && strip $(BINDIR)/$(TARGET)
+ @test -d $(MANDIR) || mkdir -m 755 -p $(MANDIR)
+ @cp $(MANFILES) $(MANDIR)
+
+clobber: clean
+ $(RM) -f $(BINDIR)/$(TARGET) $(MANDIR)/$(MANFILES)
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppdump/Makefile.top b/usr/src/cmd/cmd-inet/usr.bin/pppdump/Makefile.top
new file mode 100644
index 0000000000..4f6c491757
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppdump/Makefile.top
@@ -0,0 +1,22 @@
+#
+#pragma ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 2000 by Sun Microsystems, Inc.
+# All rights reserved.
+
+DIRS= pppdump plugins
+
+all: $(DIRS)
+install: $(DIRS)
+clean: $(DIRS)
+clobber: $(DIRS)
+
+all:= TARGET=all
+install:= TARGET=install
+clean:= TARGET=clean
+clobber:= TARGET=clobber
+
+$(DIRS): FORCE
+ @cd $@ && make $(TARGET)
+
+FORCE:
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppdump/README.top b/usr/src/cmd/cmd-inet/usr.bin/pppdump/README.top
new file mode 100644
index 0000000000..53291d6390
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppdump/README.top
@@ -0,0 +1,32 @@
+ident "%Z%%M% %I% %E% SMI"
+
+Copyright (c) 2000 by Sun Microsystems, Inc.
+All rights reserved.
+
+These are GNU utilities that can be used with the Solaris version of
+PPP. They provide optional features and are not needed for normal
+operation.
+
+The pppdump utility reads files produced by the pppd "record" option
+and produces human-readable output. This can be useful when debugging
+problems with the kernel data compression modules, but is otherwise
+generally not as useful as the debugging features already built into
+pppd.
+
+The minconn.so plugin sets a minimum initial connect time when the
+"idle" option is used.
+
+The passprompt.so plugin allows PAP to be used with external prompting
+programs, such as xprompt. This allows the password to be supplied
+interactively, in much the same manner as on PCs.
+
+To build, simply type "make". Depending on how your system is
+configured, you may need to specify the compiler to use, like this:
+
+ % make CC=/usr/local/bin/gcc
+
+To install, you will need to be root (or at least have write
+permission to the /etc/ppp/plugins, /usr/bin, and /usr/share/man/man1m
+directories).
+
+ # make install
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppdump/bsd-comp.c b/usr/src/cmd/cmd-inet/usr.bin/pppdump/bsd-comp.c
new file mode 100644
index 0000000000..5d67a02487
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppdump/bsd-comp.c
@@ -0,0 +1,754 @@
+/* Because this code is derived from the 4.3BSD compress source:
+ *
+ *
+ * Copyright (c) 1985, 1986 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * James A. Woods, derived from original work by Spencer Thomas
+ * and Joseph Orost.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce 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.
+ */
+
+/*
+ * $Id: bsd-comp.c,v 1.3 1999/04/16 11:35:59 paulus Exp $
+ */
+
+#include <sys/types.h>
+#include <stddef.h>
+#include <stdlib.h>
+#ifdef PPP_DEFS_IN_NET
+#include <net/ppp_defs.h>
+#else
+#include "ppp_defs.h"
+#endif
+#include "ppp-comp.h"
+
+#if DO_BSD_COMPRESS
+
+/*
+ * PPP "BSD compress" compression
+ * The differences between this compression and the classic BSD LZW
+ * source are obvious from the requirement that the classic code worked
+ * with files while this handles arbitrarily long streams that
+ * are broken into packets. They are:
+ *
+ * When the code size expands, a block of junk is not emitted by
+ * the compressor and not expected by the decompressor.
+ *
+ * New codes are not necessarily assigned every time an old
+ * code is output by the compressor. This is because a packet
+ * end forces a code to be emitted, but does not imply that a
+ * new sequence has been seen.
+ *
+ * The compression ratio is checked at the first end of a packet
+ * after the appropriate gap. Besides simplifying and speeding
+ * things up, this makes it more likely that the transmitter
+ * and receiver will agree when the dictionary is cleared when
+ * compression is not going well.
+ */
+
+/*
+ * A dictionary for doing BSD compress.
+ */
+struct bsd_db {
+ int totlen; /* length of this structure */
+ u_int hsize; /* size of the hash table */
+ u_char hshift; /* used in hash function */
+ u_char n_bits; /* current bits/code */
+ u_char maxbits;
+ u_char debug;
+ u_char unit;
+ u_short seqno; /* sequence number of next packet */
+ u_int hdrlen; /* header length to preallocate */
+ u_int mru;
+ u_int maxmaxcode; /* largest valid code */
+ u_int max_ent; /* largest code in use */
+ u_int in_count; /* uncompressed bytes, aged */
+ u_int bytes_out; /* compressed bytes, aged */
+ u_int ratio; /* recent compression ratio */
+ u_int checkpoint; /* when to next check the ratio */
+ u_int clear_count; /* times dictionary cleared */
+ u_int incomp_count; /* incompressible packets */
+ u_int incomp_bytes; /* incompressible bytes */
+ u_int uncomp_count; /* uncompressed packets */
+ u_int uncomp_bytes; /* uncompressed bytes */
+ u_int comp_count; /* compressed packets */
+ u_int comp_bytes; /* compressed bytes */
+ u_short *lens; /* array of lengths of codes */
+ struct bsd_dict {
+ union { /* hash value */
+ u_int32_t fcode;
+ struct {
+#ifdef BSD_LITTLE_ENDIAN
+ u_short prefix; /* preceding code */
+ u_char suffix; /* last character of new code */
+ u_char pad;
+#else
+ u_char pad;
+ u_char suffix; /* last character of new code */
+ u_short prefix; /* preceding code */
+#endif
+ } hs;
+ } f;
+ u_short codem1; /* output of hash table -1 */
+ u_short cptr; /* map code to hash table entry */
+ } dict[1];
+};
+
+#define BSD_OVHD 2 /* BSD compress overhead/packet */
+#define BSD_INIT_BITS BSD_MIN_BITS
+
+static void *bsd_decomp_alloc __P((u_char *options, int opt_len));
+static void bsd_free __P((void *state));
+static int bsd_decomp_init __P((void *state, u_char *options, int opt_len,
+ int unit, int hdrlen, int mru, int debug));
+static void bsd_incomp __P((void *state, u_char *dmsg, int len));
+static int bsd_decompress __P((void *state, u_char *cmp, int inlen,
+ u_char *dmp, int *outlen));
+static void bsd_reset __P((void *state));
+static void bsd_comp_stats __P((void *state, struct compstat *stats));
+
+/*
+ * Exported procedures.
+ */
+struct compressor ppp_bsd_compress = {
+ CI_BSD_COMPRESS, /* compress_proto */
+ bsd_decomp_alloc, /* decomp_alloc */
+ bsd_free, /* decomp_free */
+ bsd_decomp_init, /* decomp_init */
+ bsd_reset, /* decomp_reset */
+ bsd_decompress, /* decompress */
+ bsd_incomp, /* incomp */
+ bsd_comp_stats, /* decomp_stat */
+};
+
+/*
+ * the next two codes should not be changed lightly, as they must not
+ * lie within the contiguous general code space.
+ */
+#define CLEAR 256 /* table clear output code */
+#define FIRST 257 /* first free entry */
+#define LAST 255
+
+#define MAXCODE(b) ((1 << (b)) - 1)
+#define BADCODEM1 MAXCODE(BSD_MAX_BITS)
+
+#define BSD_HASH(prefix,suffix,hshift) ((((u_int32_t)(suffix)) << (hshift)) \
+ ^ (u_int32_t)(prefix))
+#define BSD_KEY(prefix,suffix) ((((u_int32_t)(suffix)) << 16) \
+ + (u_int32_t)(prefix))
+
+#define CHECK_GAP 10000 /* Ratio check interval */
+
+#define RATIO_SCALE_LOG 8
+#define RATIO_SCALE (1<<RATIO_SCALE_LOG)
+#define RATIO_MAX (0x7fffffff>>RATIO_SCALE_LOG)
+
+/*
+ * clear the dictionary
+ */
+static void
+bsd_clear(db)
+ struct bsd_db *db;
+{
+ db->clear_count++;
+ db->max_ent = FIRST-1;
+ db->n_bits = BSD_INIT_BITS;
+ db->ratio = 0;
+ db->bytes_out = 0;
+ db->in_count = 0;
+ db->checkpoint = CHECK_GAP;
+}
+
+/*
+ * If the dictionary is full, then see if it is time to reset it.
+ *
+ * Compute the compression ratio using fixed-point arithmetic
+ * with 8 fractional bits.
+ *
+ * Since we have an infinite stream instead of a single file,
+ * watch only the local compression ratio.
+ *
+ * Since both peers must reset the dictionary at the same time even in
+ * the absence of CLEAR codes (while packets are incompressible), they
+ * must compute the same ratio.
+ */
+static int /* 1=output CLEAR */
+bsd_check(db)
+ struct bsd_db *db;
+{
+ u_int new_ratio;
+
+ if (db->in_count >= db->checkpoint) {
+ /* age the ratio by limiting the size of the counts */
+ if (db->in_count >= RATIO_MAX
+ || db->bytes_out >= RATIO_MAX) {
+ db->in_count -= db->in_count/4;
+ db->bytes_out -= db->bytes_out/4;
+ }
+
+ db->checkpoint = db->in_count + CHECK_GAP;
+
+ if (db->max_ent >= db->maxmaxcode) {
+ /* Reset the dictionary only if the ratio is worse,
+ * or if it looks as if it has been poisoned
+ * by incompressible data.
+ *
+ * This does not overflow, because
+ * db->in_count <= RATIO_MAX.
+ */
+ new_ratio = db->in_count << RATIO_SCALE_LOG;
+ if (db->bytes_out != 0)
+ new_ratio /= db->bytes_out;
+
+ if (new_ratio < db->ratio || new_ratio < 1 * RATIO_SCALE) {
+ bsd_clear(db);
+ return 1;
+ }
+ db->ratio = new_ratio;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Return statistics.
+ */
+static void
+bsd_comp_stats(state, stats)
+ void *state;
+ struct compstat *stats;
+{
+ struct bsd_db *db = (struct bsd_db *) state;
+ u_int out;
+
+ stats->unc_bytes = db->uncomp_bytes;
+ stats->unc_packets = db->uncomp_count;
+ stats->comp_bytes = db->comp_bytes;
+ stats->comp_packets = db->comp_count;
+ stats->inc_bytes = db->incomp_bytes;
+ stats->inc_packets = db->incomp_count;
+ stats->ratio = db->in_count;
+ out = db->bytes_out;
+ if (stats->ratio <= 0x7fffff)
+ stats->ratio <<= 8;
+ else
+ out >>= 8;
+ if (out != 0)
+ stats->ratio /= out;
+}
+
+/*
+ * Reset state, as on a CCP ResetReq.
+ */
+static void
+bsd_reset(state)
+ void *state;
+{
+ struct bsd_db *db = (struct bsd_db *) state;
+
+ db->seqno = 0;
+ bsd_clear(db);
+ db->clear_count = 0;
+}
+
+/*
+ * Allocate space for a (de) compressor.
+ */
+static void *
+bsd_alloc(options, opt_len, decomp)
+ u_char *options;
+ int opt_len, decomp;
+{
+ int bits;
+ u_int newlen, hsize, hshift, maxmaxcode;
+ struct bsd_db *db;
+
+ if (opt_len != 3 || options[0] != CI_BSD_COMPRESS || options[1] != 3
+ || BSD_VERSION(options[2]) != BSD_CURRENT_VERSION)
+ return NULL;
+
+ bits = BSD_NBITS(options[2]);
+ switch (bits) {
+ case 9: /* needs 82152 for both directions */
+ case 10: /* needs 84144 */
+ case 11: /* needs 88240 */
+ case 12: /* needs 96432 */
+ hsize = 5003;
+ hshift = 4;
+ break;
+ case 13: /* needs 176784 */
+ hsize = 9001;
+ hshift = 5;
+ break;
+ case 14: /* needs 353744 */
+ hsize = 18013;
+ hshift = 6;
+ break;
+ case 15: /* needs 691440 */
+ hsize = 35023;
+ hshift = 7;
+ break;
+ case 16: /* needs 1366160--far too much, */
+ /* hsize = 69001; */ /* and 69001 is too big for cptr */
+ /* hshift = 8; */ /* in struct bsd_db */
+ /* break; */
+ default:
+ return NULL;
+ }
+
+ maxmaxcode = MAXCODE(bits);
+ newlen = sizeof(*db) + (hsize-1) * (sizeof(db->dict[0]));
+ db = (struct bsd_db *) malloc(newlen);
+ if (!db)
+ return NULL;
+ memset(db, 0, sizeof(*db) - sizeof(db->dict));
+
+ if (!decomp) {
+ db->lens = NULL;
+ } else {
+ db->lens = (u_short *) malloc((maxmaxcode+1) * sizeof(db->lens[0]));
+ if (!db->lens) {
+ free(db);
+ return NULL;
+ }
+ }
+
+ db->totlen = newlen;
+ db->hsize = hsize;
+ db->hshift = hshift;
+ db->maxmaxcode = maxmaxcode;
+ db->maxbits = bits;
+
+ return (void *) db;
+}
+
+static void
+bsd_free(state)
+ void *state;
+{
+ struct bsd_db *db = (struct bsd_db *) state;
+
+ if (db->lens)
+ free(db->lens);
+ free(db);
+}
+
+static void *
+bsd_decomp_alloc(options, opt_len)
+ u_char *options;
+ int opt_len;
+{
+ return bsd_alloc(options, opt_len, 1);
+}
+
+/*
+ * Initialize the database.
+ */
+static int
+bsd_init(db, options, opt_len, unit, hdrlen, mru, debug, decomp)
+ struct bsd_db *db;
+ u_char *options;
+ int opt_len, unit, hdrlen, mru, debug, decomp;
+{
+ int i;
+
+ if (opt_len < CILEN_BSD_COMPRESS
+ || options[0] != CI_BSD_COMPRESS || options[1] != CILEN_BSD_COMPRESS
+ || BSD_VERSION(options[2]) != BSD_CURRENT_VERSION
+ || BSD_NBITS(options[2]) != db->maxbits
+ || decomp && db->lens == NULL)
+ return 0;
+
+ if (decomp) {
+ i = LAST+1;
+ while (i != 0)
+ db->lens[--i] = 1;
+ }
+ i = db->hsize;
+ while (i != 0) {
+ db->dict[--i].codem1 = BADCODEM1;
+ db->dict[i].cptr = 0;
+ }
+
+ db->unit = unit;
+ db->hdrlen = hdrlen;
+ db->mru = mru;
+ if (debug)
+ db->debug = 1;
+
+ bsd_reset(db);
+
+ return 1;
+}
+
+static int
+bsd_decomp_init(state, options, opt_len, unit, hdrlen, mru, debug)
+ void *state;
+ u_char *options;
+ int opt_len, unit, hdrlen, mru, debug;
+{
+ return bsd_init((struct bsd_db *) state, options, opt_len,
+ unit, hdrlen, mru, debug, 1);
+}
+
+
+/*
+ * Update the "BSD Compress" dictionary on the receiver for
+ * incompressible data by pretending to compress the incoming data.
+ */
+static void
+bsd_incomp(state, dmsg, mlen)
+ void *state;
+ u_char *dmsg;
+ int mlen;
+{
+ struct bsd_db *db = (struct bsd_db *) state;
+ u_int hshift = db->hshift;
+ u_int max_ent = db->max_ent;
+ u_int n_bits = db->n_bits;
+ struct bsd_dict *dictp;
+ u_int32_t fcode;
+ u_char c;
+ long hval, disp;
+ int slen, ilen;
+ u_int bitno = 7;
+ u_char *rptr;
+ u_int ent;
+
+ rptr = dmsg;
+ ent = rptr[0]; /* get the protocol */
+ if (ent == 0) {
+ ++rptr;
+ --mlen;
+ ent = rptr[0];
+ }
+ if ((ent & 1) == 0 || ent < 0x21 || ent > 0xf9)
+ return;
+
+ db->seqno++;
+ ilen = 1; /* count the protocol as 1 byte */
+ ++rptr;
+ slen = dmsg + mlen - rptr;
+ ilen += slen;
+ for (; slen > 0; --slen) {
+ c = *rptr++;
+ fcode = BSD_KEY(ent, c);
+ hval = BSD_HASH(ent, c, hshift);
+ dictp = &db->dict[hval];
+
+ /* validate and then check the entry */
+ if (dictp->codem1 >= max_ent)
+ goto nomatch;
+ if (dictp->f.fcode == fcode) {
+ ent = dictp->codem1+1;
+ continue; /* found (prefix,suffix) */
+ }
+
+ /* continue probing until a match or invalid entry */
+ disp = (hval == 0) ? 1 : hval;
+ do {
+ hval += disp;
+ if (hval >= db->hsize)
+ hval -= db->hsize;
+ dictp = &db->dict[hval];
+ if (dictp->codem1 >= max_ent)
+ goto nomatch;
+ } while (dictp->f.fcode != fcode);
+ ent = dictp->codem1+1;
+ continue; /* finally found (prefix,suffix) */
+
+ nomatch: /* output (count) the prefix */
+ bitno += n_bits;
+
+ /* code -> hashtable */
+ if (max_ent < db->maxmaxcode) {
+ struct bsd_dict *dictp2;
+ /* expand code size if needed */
+ if (max_ent >= MAXCODE(n_bits))
+ db->n_bits = ++n_bits;
+
+ /* Invalidate previous hash table entry
+ * assigned this code, and then take it over.
+ */
+ dictp2 = &db->dict[max_ent+1];
+ if (db->dict[dictp2->cptr].codem1 == max_ent)
+ db->dict[dictp2->cptr].codem1 = BADCODEM1;
+ dictp2->cptr = hval;
+ dictp->codem1 = max_ent;
+ dictp->f.fcode = fcode;
+
+ db->max_ent = ++max_ent;
+ db->lens[max_ent] = db->lens[ent]+1;
+ }
+ ent = c;
+ }
+ bitno += n_bits; /* output (count) the last code */
+ db->bytes_out += bitno/8;
+ db->in_count += ilen;
+ (void)bsd_check(db);
+
+ ++db->incomp_count;
+ db->incomp_bytes += ilen;
+ ++db->uncomp_count;
+ db->uncomp_bytes += ilen;
+
+ /* Increase code size if we would have without the packet
+ * boundary and as the decompressor will.
+ */
+ if (max_ent >= MAXCODE(n_bits) && max_ent < db->maxmaxcode)
+ db->n_bits++;
+}
+
+
+/*
+ * Decompress "BSD Compress"
+ *
+ * Because of patent problems, we return DECOMP_ERROR for errors
+ * found by inspecting the input data and for system problems, but
+ * DECOMP_FATALERROR for any errors which could possibly be said to
+ * be being detected "after" decompression. For DECOMP_ERROR,
+ * we can issue a CCP reset-request; for DECOMP_FATALERROR, we may be
+ * infringing a patent of Motorola's if we do, so we take CCP down
+ * instead.
+ *
+ * Given that the frame has the correct sequence number and a good FCS,
+ * errors such as invalid codes in the input most likely indicate a
+ * bug, so we return DECOMP_FATALERROR for them in order to turn off
+ * compression, even though they are detected by inspecting the input.
+ */
+static int
+bsd_decompress(state, cmsg, inlen, dmp, outlenp)
+ void *state;
+ u_char *cmsg, *dmp;
+ int inlen, *outlenp;
+{
+ struct bsd_db *db = (struct bsd_db *) state;
+ u_int max_ent = db->max_ent;
+ u_int32_t accm = 0;
+ u_int bitno = 32; /* 1st valid bit in accm */
+ u_int n_bits = db->n_bits;
+ u_int tgtbitno = 32-n_bits; /* bitno when we have a code */
+ struct bsd_dict *dictp;
+ int explen, i, seq, len;
+ u_int incode, oldcode, finchar;
+ u_char *p, *rptr, *wptr;
+ int ilen;
+ int dlen, space, codelen, extra;
+
+ rptr = cmsg;
+ if (*rptr == 0)
+ ++rptr;
+ ++rptr; /* skip protocol (assumed 0xfd) */
+ seq = (rptr[0] << 8) + rptr[1];
+ rptr += BSD_OVHD;
+ ilen = len = cmsg + inlen - rptr;
+
+ /*
+ * Check the sequence number and give up if it is not what we expect.
+ */
+ if (seq != db->seqno++) {
+ if (db->debug)
+ printf("bsd_decomp%d: bad sequence # %d, expected %d\n",
+ db->unit, seq, db->seqno - 1);
+ return DECOMP_ERROR;
+ }
+
+ wptr = dmp + db->hdrlen;
+
+ oldcode = CLEAR;
+ explen = 0;
+ while (len > 0) {
+ /*
+ * Accumulate bytes until we have a complete code.
+ * Then get the next code, relying on the 32-bit,
+ * unsigned accm to mask the result.
+ */
+ bitno -= 8;
+ accm |= *rptr++ << bitno;
+ --len;
+ if (tgtbitno < bitno)
+ continue;
+ incode = accm >> tgtbitno;
+ accm <<= n_bits;
+ bitno += n_bits;
+
+ if (incode == CLEAR) {
+ /*
+ * The dictionary must only be cleared at
+ * the end of a packet. But there could be an
+ * empty message block at the end.
+ */
+ if (len > 0) {
+ if (db->debug)
+ printf("bsd_decomp%d: bad CLEAR\n", db->unit);
+ return DECOMP_FATALERROR;
+ }
+ bsd_clear(db);
+ explen = ilen = 0;
+ break;
+ }
+
+ if (incode > max_ent + 2 || incode > db->maxmaxcode
+ || incode > max_ent && oldcode == CLEAR) {
+ if (db->debug) {
+ printf("bsd_decomp%d: bad code 0x%x oldcode=0x%x ",
+ db->unit, incode, oldcode);
+ printf("max_ent=0x%x dlen=%d seqno=%d\n",
+ max_ent, dlen, db->seqno);
+ }
+ return DECOMP_FATALERROR; /* probably a bug */
+ }
+
+ /* Special case for KwKwK string. */
+ if (incode > max_ent) {
+ finchar = oldcode;
+ extra = 1;
+ } else {
+ finchar = incode;
+ extra = 0;
+ }
+
+ codelen = db->lens[finchar];
+ explen += codelen + extra;
+ if (explen > db->mru + 1) {
+ if (db->debug)
+ printf("bsd_decomp%d: ran out of mru\n", db->unit);
+ return DECOMP_FATALERROR;
+ }
+
+ /*
+ * Decode this code and install it in the decompressed buffer.
+ */
+ p = (wptr += codelen);
+ while (finchar > LAST) {
+ dictp = &db->dict[db->dict[finchar].cptr];
+#ifdef DEBUG
+ --codelen;
+ if (codelen <= 0) {
+ printf("bsd_decomp%d: fell off end of chain ", db->unit);
+ printf("0x%x at 0x%x by 0x%x, max_ent=0x%x\n",
+ incode, finchar, db->dict[finchar].cptr, max_ent);
+ return DECOMP_FATALERROR;
+ }
+ if (dictp->codem1 != finchar-1) {
+ printf("bsd_decomp%d: bad code chain 0x%x finchar=0x%x ",
+ db->unit, incode, finchar);
+ printf("oldcode=0x%x cptr=0x%x codem1=0x%x\n", oldcode,
+ db->dict[finchar].cptr, dictp->codem1);
+ return DECOMP_FATALERROR;
+ }
+#endif
+ *--p = dictp->f.hs.suffix;
+ finchar = dictp->f.hs.prefix;
+ }
+ *--p = finchar;
+
+#ifdef DEBUG
+ if (--codelen != 0)
+ printf("bsd_decomp%d: short by %d after code 0x%x, max_ent=0x%x\n",
+ db->unit, codelen, incode, max_ent);
+#endif
+
+ if (extra) /* the KwKwK case again */
+ *wptr++ = finchar;
+
+ /*
+ * If not first code in a packet, and
+ * if not out of code space, then allocate a new code.
+ *
+ * Keep the hash table correct so it can be used
+ * with uncompressed packets.
+ */
+ if (oldcode != CLEAR && max_ent < db->maxmaxcode) {
+ struct bsd_dict *dictp2;
+ u_int32_t fcode;
+ int hval, disp;
+
+ fcode = BSD_KEY(oldcode,finchar);
+ hval = BSD_HASH(oldcode,finchar,db->hshift);
+ dictp = &db->dict[hval];
+
+ /* look for a free hash table entry */
+ if (dictp->codem1 < max_ent) {
+ disp = (hval == 0) ? 1 : hval;
+ do {
+ hval += disp;
+ if (hval >= db->hsize)
+ hval -= db->hsize;
+ dictp = &db->dict[hval];
+ } while (dictp->codem1 < max_ent);
+ }
+
+ /*
+ * Invalidate previous hash table entry
+ * assigned this code, and then take it over
+ */
+ dictp2 = &db->dict[max_ent+1];
+ if (db->dict[dictp2->cptr].codem1 == max_ent) {
+ db->dict[dictp2->cptr].codem1 = BADCODEM1;
+ }
+ dictp2->cptr = hval;
+ dictp->codem1 = max_ent;
+ dictp->f.fcode = fcode;
+
+ db->max_ent = ++max_ent;
+ db->lens[max_ent] = db->lens[oldcode]+1;
+
+ /* Expand code size if needed. */
+ if (max_ent >= MAXCODE(n_bits) && max_ent < db->maxmaxcode) {
+ db->n_bits = ++n_bits;
+ tgtbitno = 32-n_bits;
+ }
+ }
+ oldcode = incode;
+ }
+ *outlenp = wptr - (dmp + db->hdrlen);
+
+ /*
+ * Keep the checkpoint right so that incompressible packets
+ * clear the dictionary at the right times.
+ */
+ db->bytes_out += ilen;
+ db->in_count += explen;
+ if (bsd_check(db) && db->debug) {
+ printf("bsd_decomp%d: peer should have cleared dictionary\n",
+ db->unit);
+ }
+
+ ++db->comp_count;
+ db->comp_bytes += ilen + BSD_OVHD;
+ ++db->uncomp_count;
+ db->uncomp_bytes += explen;
+
+ return DECOMP_OK;
+}
+#endif /* DO_BSD_COMPRESS */
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppdump/deflate.c b/usr/src/cmd/cmd-inet/usr.bin/pppdump/deflate.c
new file mode 100644
index 0000000000..a41d124cae
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppdump/deflate.c
@@ -0,0 +1,348 @@
+/*
+ * ppp_deflate.c - interface the zlib procedures for Deflate compression
+ * and decompression (as used by gzip) to the PPP code.
+ *
+ * Copyright (c) 1994 The Australian National University.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies. This software is provided without any
+ * warranty, express or implied. The Australian National University
+ * makes no representations about the suitability of this software for
+ * any purpose.
+ *
+ * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
+ * THE AUSTRALIAN NATIONAL UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
+ * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
+ * OR MODIFICATIONS.
+ *
+ * $Id: deflate.c,v 1.3 1999/04/16 11:35:59 paulus Exp $
+ */
+
+#include <sys/types.h>
+#include <stddef.h>
+#include <stdlib.h>
+#ifdef PPP_DEFS_IN_NET
+#include <net/ppp_defs.h>
+#else
+#include "ppp_defs.h"
+#endif
+#include "ppp-comp.h"
+#include "zlib.h"
+
+#if DO_DEFLATE
+
+#define DEFLATE_DEBUG 1
+
+/*
+ * State for a Deflate (de)compressor.
+ */
+struct deflate_state {
+ int seqno;
+ int w_size;
+ int unit;
+ int hdrlen;
+ int mru;
+ int debug;
+ z_stream strm;
+ struct compstat stats;
+};
+
+#define DEFLATE_OVHD 2 /* Deflate overhead/packet */
+
+static void *z_alloc __P((void *, u_int items, u_int size));
+static void z_free __P((void *, void *ptr, u_int nb));
+static void *z_decomp_alloc __P((u_char *options, int opt_len));
+static void z_decomp_free __P((void *state));
+static int z_decomp_init __P((void *state, u_char *options, int opt_len,
+ int unit, int hdrlen, int mru, int debug));
+static void z_incomp __P((void *state, u_char *dmsg, int len));
+static int z_decompress __P((void *state, u_char *cmp, int inlen,
+ u_char *dmp, int *outlenp));
+static void z_decomp_reset __P((void *state));
+static void z_comp_stats __P((void *state, struct compstat *stats));
+
+/*
+ * Procedures exported to if_ppp.c.
+ */
+struct compressor ppp_deflate = {
+ CI_DEFLATE, /* compress_proto */
+ z_decomp_alloc, /* decomp_alloc */
+ z_decomp_free, /* decomp_free */
+ z_decomp_init, /* decomp_init */
+ z_decomp_reset, /* decomp_reset */
+ z_decompress, /* decompress */
+ z_incomp, /* incomp */
+ z_comp_stats, /* decomp_stat */
+};
+
+/*
+ * Space allocation and freeing routines for use by zlib routines.
+ */
+static void *
+z_alloc(notused, items, size)
+ void *notused;
+ u_int items, size;
+{
+ return malloc(items * size);
+}
+
+static void
+z_free(notused, ptr, nbytes)
+ void *notused;
+ void *ptr;
+ u_int nbytes;
+{
+ free(ptr);
+}
+
+static void
+z_comp_stats(arg, stats)
+ void *arg;
+ struct compstat *stats;
+{
+ struct deflate_state *state = (struct deflate_state *) arg;
+ u_int out;
+
+ *stats = state->stats;
+ stats->ratio = stats->unc_bytes;
+ out = stats->comp_bytes + stats->unc_bytes;
+ if (stats->ratio <= 0x7ffffff)
+ stats->ratio <<= 8;
+ else
+ out >>= 8;
+ if (out != 0)
+ stats->ratio /= out;
+}
+
+/*
+ * Allocate space for a decompressor.
+ */
+static void *
+z_decomp_alloc(options, opt_len)
+ u_char *options;
+ int opt_len;
+{
+ struct deflate_state *state;
+ int w_size;
+
+ if (opt_len != CILEN_DEFLATE || options[0] != CI_DEFLATE
+ || options[1] != CILEN_DEFLATE
+ || DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL
+ || options[3] != DEFLATE_CHK_SEQUENCE)
+ return NULL;
+ w_size = DEFLATE_SIZE(options[2]);
+ if (w_size < DEFLATE_MIN_SIZE || w_size > DEFLATE_MAX_SIZE)
+ return NULL;
+
+ state = (struct deflate_state *) malloc(sizeof(*state));
+ if (state == NULL)
+ return NULL;
+
+ state->strm.next_out = NULL;
+ state->strm.zalloc = (alloc_func) z_alloc;
+ state->strm.zfree = (free_func) z_free;
+ if (inflateInit2(&state->strm, -w_size) != Z_OK) {
+ free(state);
+ return NULL;
+ }
+
+ state->w_size = w_size;
+ memset(&state->stats, 0, sizeof(state->stats));
+ return (void *) state;
+}
+
+static void
+z_decomp_free(arg)
+ void *arg;
+{
+ struct deflate_state *state = (struct deflate_state *) arg;
+
+ inflateEnd(&state->strm);
+ free(state);
+}
+
+static int
+z_decomp_init(arg, options, opt_len, unit, hdrlen, mru, debug)
+ void *arg;
+ u_char *options;
+ int opt_len, unit, hdrlen, mru, debug;
+{
+ struct deflate_state *state = (struct deflate_state *) arg;
+
+ if (opt_len < CILEN_DEFLATE || options[0] != CI_DEFLATE
+ || options[1] != CILEN_DEFLATE
+ || DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL
+ || DEFLATE_SIZE(options[2]) != state->w_size
+ || options[3] != DEFLATE_CHK_SEQUENCE)
+ return 0;
+
+ state->seqno = 0;
+ state->unit = unit;
+ state->hdrlen = hdrlen;
+ state->debug = debug;
+ state->mru = mru;
+
+ inflateReset(&state->strm);
+
+ return 1;
+}
+
+static void
+z_decomp_reset(arg)
+ void *arg;
+{
+ struct deflate_state *state = (struct deflate_state *) arg;
+
+ state->seqno = 0;
+ inflateReset(&state->strm);
+}
+
+/*
+ * Decompress a Deflate-compressed packet.
+ *
+ * Because of patent problems, we return DECOMP_ERROR for errors
+ * found by inspecting the input data and for system problems, but
+ * DECOMP_FATALERROR for any errors which could possibly be said to
+ * be being detected "after" decompression. For DECOMP_ERROR,
+ * we can issue a CCP reset-request; for DECOMP_FATALERROR, we may be
+ * infringing a patent of Motorola's if we do, so we take CCP down
+ * instead.
+ *
+ * Given that the frame has the correct sequence number and a good FCS,
+ * errors such as invalid codes in the input most likely indicate a
+ * bug, so we return DECOMP_FATALERROR for them in order to turn off
+ * compression, even though they are detected by inspecting the input.
+ */
+static int
+z_decompress(arg, mi, inlen, mo, outlenp)
+ void *arg;
+ u_char *mi, *mo;
+ int inlen, *outlenp;
+{
+ struct deflate_state *state = (struct deflate_state *) arg;
+ u_char *rptr, *wptr;
+ int rlen, olen, ospace;
+ int seq, i, flush, r, decode_proto;
+
+ rptr = mi;
+ if (*rptr == 0)
+ ++rptr;
+ ++rptr;
+
+ /* Check the sequence number. */
+ seq = (rptr[0] << 8) + rptr[1];
+ rptr += 2;
+ if (seq != state->seqno) {
+#if !DEFLATE_DEBUG
+ if (state->debug)
+#endif
+ printf("z_decompress%d: bad seq # %d, expected %d\n",
+ state->unit, seq, state->seqno);
+ return DECOMP_ERROR;
+ }
+ ++state->seqno;
+
+ /*
+ * Set up to call inflate.
+ */
+ wptr = mo;
+ state->strm.next_in = rptr;
+ state->strm.avail_in = mi + inlen - rptr;
+ rlen = state->strm.avail_in + PPP_HDRLEN + DEFLATE_OVHD;
+ state->strm.next_out = wptr;
+ state->strm.avail_out = state->mru + 2;
+
+ r = inflate(&state->strm, Z_PACKET_FLUSH);
+ if (r != Z_OK) {
+#if !DEFLATE_DEBUG
+ if (state->debug)
+#endif
+ printf("z_decompress%d: inflate returned %d (%s)\n",
+ state->unit, r, (state->strm.msg? state->strm.msg: ""));
+ return DECOMP_FATALERROR;
+ }
+ olen = state->mru + 2 - state->strm.avail_out;
+ *outlenp = olen;
+
+ if ((wptr[0] & 1) != 0)
+ ++olen; /* for suppressed protocol high byte */
+ olen += 2; /* for address, control */
+
+#if DEFLATE_DEBUG
+ if (olen > state->mru + PPP_HDRLEN)
+ printf("ppp_deflate%d: exceeded mru (%d > %d)\n",
+ state->unit, olen, state->mru + PPP_HDRLEN);
+#endif
+
+ state->stats.unc_bytes += olen;
+ state->stats.unc_packets++;
+ state->stats.comp_bytes += rlen;
+ state->stats.comp_packets++;
+
+ return DECOMP_OK;
+}
+
+/*
+ * Incompressible data has arrived - add it to the history.
+ */
+static void
+z_incomp(arg, mi, mlen)
+ void *arg;
+ u_char *mi;
+ int mlen;
+{
+ struct deflate_state *state = (struct deflate_state *) arg;
+ u_char *rptr;
+ int rlen, proto, r;
+
+ /*
+ * Check that the protocol is one we handle.
+ */
+ rptr = mi;
+ proto = rptr[0];
+ if ((proto & 1) == 0)
+ proto = (proto << 8) + rptr[1];
+ if (proto > 0x3fff || proto == 0xfd || proto == 0xfb)
+ return;
+
+ ++state->seqno;
+
+ if (rptr[0] == 0)
+ ++rptr;
+ rlen = mi + mlen - rptr;
+ state->strm.next_in = rptr;
+ state->strm.avail_in = rlen;
+ r = inflateIncomp(&state->strm);
+ if (r != Z_OK) {
+ /* gak! */
+#if !DEFLATE_DEBUG
+ if (state->debug)
+#endif
+ printf("z_incomp%d: inflateIncomp returned %d (%s)\n",
+ state->unit, r, (state->strm.msg? state->strm.msg: ""));
+ return;
+ }
+
+ /*
+ * Update stats.
+ */
+ if (proto <= 0xff)
+ ++rlen;
+ rlen += 2;
+ state->stats.inc_bytes += rlen;
+ state->stats.inc_packets++;
+ state->stats.unc_bytes += rlen;
+ state->stats.unc_packets++;
+}
+
+#endif /* DO_DEFLATE */
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppdump/ppp-comp.h b/usr/src/cmd/cmd-inet/usr.bin/pppdump/ppp-comp.h
new file mode 100644
index 0000000000..d74ca77956
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppdump/ppp-comp.h
@@ -0,0 +1,150 @@
+/*
+ * ppp-comp.h - Definitions for doing PPP packet compression.
+ *
+ * Copyright (c) 1994 The Australian National University.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies. This software is provided without any
+ * warranty, express or implied. The Australian National University
+ * makes no representations about the suitability of this software for
+ * any purpose.
+ *
+ * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
+ * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
+ * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
+ * OR MODIFICATIONS.
+ *
+ * $Id: ppp-comp.h,v 1.1 1999/03/23 03:21:58 paulus Exp $
+ */
+
+#ifndef _NET_PPP_COMP_H
+#define _NET_PPP_COMP_H
+
+/*
+ * The following symbols control whether we include code for
+ * various compression methods.
+ */
+#ifndef DO_BSD_COMPRESS
+#define DO_BSD_COMPRESS 1 /* by default, include BSD-Compress */
+#endif
+#ifndef DO_DEFLATE
+#define DO_DEFLATE 1 /* by default, include Deflate */
+#endif
+#define DO_PREDICTOR_1 0
+#define DO_PREDICTOR_2 0
+
+/*
+ * Structure giving methods for compression/decompression.
+ */
+struct compressor {
+ int compress_proto; /* CCP compression protocol number */
+
+ /* Allocate space for a decompressor (receive side) */
+ void *(*decomp_alloc) __P((u_char *options, int opt_len));
+ /* Free space used by a decompressor */
+ void (*decomp_free) __P((void *state));
+ /* Initialize a decompressor */
+ int (*decomp_init) __P((void *state, u_char *options, int opt_len,
+ int unit, int hdrlen, int mru, int debug));
+ /* Reset a decompressor */
+ void (*decomp_reset) __P((void *state));
+ /* Decompress a packet. */
+ int (*decompress) __P((void *state, u_char *mp, int inlen,
+ u_char *dmp, int *outlen));
+ /* Update state for an incompressible packet received */
+ void (*incomp) __P((void *state, u_char *mp, int len));
+ /* Return decompression statistics */
+ void (*decomp_stat) __P((void *state, struct compstat *stats));
+};
+
+/*
+ * Return values for decompress routine.
+ * We need to make these distinctions so that we can disable certain
+ * useful functionality, namely sending a CCP reset-request as a result
+ * of an error detected after decompression. This is to avoid infringing
+ * a patent held by Motorola.
+ * Don't you just lurve software patents.
+ */
+#define DECOMP_OK 0 /* everything went OK */
+#define DECOMP_ERROR 1 /* error detected before decomp. */
+#define DECOMP_FATALERROR 2 /* error detected after decomp. */
+
+/*
+ * CCP codes.
+ */
+#define CCP_CONFREQ 1
+#define CCP_CONFACK 2
+#define CCP_CONFNAK 3
+#define CCP_CONFREJ 4
+#define CCP_TERMREQ 5
+#define CCP_TERMACK 6
+#define CCP_RESETREQ 14
+#define CCP_RESETACK 15
+
+/*
+ * Max # bytes for a CCP option
+ */
+#define CCP_MAX_OPTION_LENGTH 32
+
+/*
+ * Parts of a CCP packet.
+ */
+#define CCP_CODE(dp) ((dp)[0])
+#define CCP_ID(dp) ((dp)[1])
+#define CCP_LENGTH(dp) (((dp)[2] << 8) + (dp)[3])
+#define CCP_HDRLEN 4
+
+#define CCP_OPT_CODE(dp) ((dp)[0])
+#define CCP_OPT_LENGTH(dp) ((dp)[1])
+#define CCP_OPT_MINLEN 2
+
+/*
+ * Definitions for BSD-Compress.
+ */
+#define CI_BSD_COMPRESS 21 /* config. option for BSD-Compress */
+#define CILEN_BSD_COMPRESS 3 /* length of config. option */
+
+/* Macros for handling the 3rd byte of the BSD-Compress config option. */
+#define BSD_NBITS(x) ((x) & 0x1F) /* number of bits requested */
+#define BSD_VERSION(x) ((x) >> 5) /* version of option format */
+#define BSD_CURRENT_VERSION 1 /* current version number */
+#define BSD_MAKE_OPT(v, n) (((v) << 5) | (n))
+
+#define BSD_MIN_BITS 9 /* smallest code size supported */
+#define BSD_MAX_BITS 15 /* largest code size supported */
+
+/*
+ * Definitions for Deflate.
+ */
+#define CI_DEFLATE 26 /* config option for Deflate */
+#define CI_DEFLATE_DRAFT 24 /* value used in original draft RFC */
+#define CILEN_DEFLATE 4 /* length of its config option */
+
+#define DEFLATE_MIN_SIZE 8
+#define DEFLATE_MAX_SIZE 15
+#define DEFLATE_METHOD_VAL 8
+#define DEFLATE_SIZE(x) (((x) >> 4) + DEFLATE_MIN_SIZE)
+#define DEFLATE_METHOD(x) ((x) & 0x0F)
+#define DEFLATE_MAKE_OPT(w) ((((w) - DEFLATE_MIN_SIZE) << 4) \
+ + DEFLATE_METHOD_VAL)
+#define DEFLATE_CHK_SEQUENCE 0
+
+/*
+ * Definitions for other, as yet unsupported, compression methods.
+ */
+#define CI_PREDICTOR_1 1 /* config option for Predictor-1 */
+#define CILEN_PREDICTOR_1 2 /* length of its config option */
+#define CI_PREDICTOR_2 2 /* config option for Predictor-2 */
+#define CILEN_PREDICTOR_2 2 /* length of its config option */
+
+#endif /* _NET_PPP_COMP_H */
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppdump/pppdump.1m b/usr/src/cmd/cmd-inet/usr.bin/pppdump/pppdump.1m
new file mode 100644
index 0000000000..c438e55be0
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppdump/pppdump.1m
@@ -0,0 +1,71 @@
+.\" -*- nroff -*-
+.\" manual page for pppdump
+.\" Copyright (c) 2000 by Sun Microsystems, Inc.
+.\" ident "%Z%%M% %I% %E% SMI"
+.\" All rights reserved.
+.\" @(#) $Id: pppdump.8,v 1.1 1999/04/01 11:44:55 paulus Exp $
+.TH PPPDUMP 1M "1 April 1999"
+.SH NAME
+pppdump \- convert PPP record file to readable format
+.SH SYNOPSIS
+.B pppdump
+[
+.B -h
+|
+.B -p
+[
+.B -d
+]] [
+.B -r
+] [
+.B -m \fImru
+] [
+.I file \fR...
+]
+.ti 12
+.SH DESCRIPTION
+The
+.B pppdump
+utility converts the files written using the \fIrecord\fR option of
+.B pppd
+into a human-readable format. If one or more filenames are specified,
+.B pppdump
+will read each in turn; otherwise it will read its standard input. In
+each case the result is written to standard output.
+.PP
+The options are as follows:
+.TP
+.B -h
+Prints the bytes sent and received in hexadecimal. If neither this
+option nor the \fB-p\fR option is specified, the bytes are printed as
+the characters themselves, with non-printing and non-ASCII characters
+printed as escape sequences.
+.TP
+.B -p
+Collects the bytes sent and received into PPP packets, interpreting
+the async HDLC framing and escape characters and checking the FCS
+(frame check sequence) of each packet. The packets are printed as hex
+values and as characters (non-printable characters are printed as
+`.').
+.TP
+.B -d
+With the \fB-p\fR option, this option causes
+.B pppdump
+to decompress packets which have been compressed with the BSD-Compress
+or Deflate methods.
+.TP
+.B -r
+Reverses the direction indicators, so that `sent' is printed for
+bytes or packets received, and `rcvd' is printed for bytes or packets
+sent.
+.TP
+.B -m \fImru
+Use \fImru\fR as the MRU (maximum receive unit) for both directions of
+the link when checking for over-length PPP packets (with the \fB-p\fR
+option).
+.SH SEE ALSO
+pppd(1m)
+.SH NOTES
+The modified source for this package is available in the SUNWpppgS
+package. You can get the original source from
+ftp://linuxcare.com.au/pub/ppp.
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppdump/pppdump.c b/usr/src/cmd/cmd-inet/usr.bin/pppdump/pppdump.c
new file mode 100644
index 0000000000..0f2ae1c936
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppdump/pppdump.c
@@ -0,0 +1,508 @@
+/*
+ * pppdump - print out the contents of a record file generated by
+ * pppd in readable form.
+ *
+ * Copyright (C) 1999 Paul Mackerras. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/types.h>
+#ifdef PPP_DEFS_IN_NET
+#include <net/ppp_defs.h>
+#else
+#include "ppp_defs.h"
+#endif
+#include "ppp-comp.h"
+
+int hexmode;
+int pppmode;
+int reverse;
+int decompress;
+int mru = 1500;
+int abs_times;
+time_t start_time;
+int start_time_tenths;
+int tot_sent, tot_rcvd;
+
+extern int optind;
+extern char *optarg;
+
+main(ac, av)
+ int ac;
+ char **av;
+{
+ int i;
+ char *p;
+ FILE *f;
+
+ while ((i = getopt(ac, av, "hprdm:a")) != -1) {
+ switch (i) {
+ case 'h':
+ hexmode = 1;
+ break;
+ case 'p':
+ pppmode = 1;
+ break;
+ case 'r':
+ reverse = 1;
+ break;
+ case 'd':
+ decompress = 1;
+ break;
+ case 'm':
+ mru = atoi(optarg);
+ break;
+ case 'a':
+ abs_times = 1;
+ break;
+ default:
+ fprintf(stderr, "Usage: %s [-h | -p[d]] [-r] [-m mru] [-a] [file ...]\n", av[0]);
+ exit(1);
+ }
+ }
+ if (optind >= ac)
+ dumplog(stdin);
+ else {
+ for (i = optind; i < ac; ++i) {
+ p = av[i];
+ if ((f = fopen(p, "r")) == NULL) {
+ perror(p);
+ exit(1);
+ }
+ if (pppmode)
+ dumpppp(f);
+ else
+ dumplog(f);
+ fclose(f);
+ }
+ }
+ exit(0);
+}
+
+dumplog(f)
+ FILE *f;
+{
+ int c, n, k, col;
+ int nb, c2;
+ unsigned char buf[16];
+
+ while ((c = getc(f)) != EOF) {
+ switch (c) {
+ case RECMARK_STARTSEND:
+ case RECMARK_STARTRECV:
+ if (reverse)
+ c = c==RECMARK_STARTSEND ? RECMARK_STARTRECV :
+ RECMARK_STARTSEND;
+ printf("%s %c", c==RECMARK_STARTSEND? "sent": "rcvd",
+ hexmode? ' ': '"');
+ col = 6;
+ n = getc(f);
+ n = (n << 8) + getc(f);
+ *(c==1? &tot_sent: &tot_rcvd) += n;
+ nb = 0;
+ for (; n > 0; --n) {
+ c = getc(f);
+ if (c == EOF) {
+ printf("\nEOF\n");
+ exit(0);
+ }
+ if (hexmode) {
+ if (nb >= 16) {
+ printf(" ");
+ for (k = 0; k < nb; ++k) {
+ c2 = buf[k];
+ putchar((' ' <= c2 && c2 <= '~')? c2: '.');
+ }
+ printf("\n ");
+ nb = 0;
+ }
+ buf[nb++] = c;
+ printf(" %.2x", c);
+ } else {
+ k = (' ' <= c && c <= '~')? (c != '\\' && c != '"')? 1: 2: 3;
+ if ((col += k) >= 78) {
+ printf("\n ");
+ col = 6 + k;
+ }
+ switch (k) {
+ case 1:
+ putchar(c);
+ break;
+ case 2:
+ printf("\\%c", c);
+ break;
+ case 3:
+ printf("\\%.2x", c);
+ break;
+ }
+ }
+ }
+ if (hexmode) {
+ for (k = nb; k < 16; ++k)
+ printf(" ");
+ printf(" ");
+ for (k = 0; k < nb; ++k) {
+ c2 = buf[k];
+ putchar((' ' <= c2 && c2 <= '~')? c2: '.');
+ }
+ } else
+ putchar('"');
+ printf("\n");
+ break;
+ case RECMARK_ENDSEND:
+ case RECMARK_ENDRECV:
+ if (reverse)
+ c = c==RECMARK_ENDSEND ? RECMARK_ENDRECV : RECMARK_ENDSEND;
+ printf("end %s\n", c==RECMARK_ENDSEND? "send": "recv");
+ break;
+ case RECMARK_TIMEDELTA32:
+ case RECMARK_TIMEDELTA8:
+ case RECMARK_TIMESTART:
+ show_time(f, c);
+ break;
+ default:
+ printf("?%.2x\n");
+ }
+ }
+}
+
+/*
+ * FCS lookup table as calculated by genfcstab.
+ */
+static u_short fcstab[256] = {
+ 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
+ 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
+ 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
+ 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
+ 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
+ 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
+ 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
+ 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
+ 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
+ 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
+ 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
+ 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
+ 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
+ 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
+ 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
+ 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
+ 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
+ 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
+ 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
+ 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
+ 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
+ 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
+ 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
+ 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
+ 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
+ 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
+ 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
+ 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
+ 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
+ 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
+ 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
+ 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
+};
+
+struct pkt {
+ int cnt;
+ int esc;
+ int flags;
+ struct compressor *comp;
+ void *state;
+ unsigned char buf[8192];
+} spkt, rpkt;
+
+/* Values for flags */
+#define CCP_ISUP 1
+#define CCP_ERROR 2
+#define CCP_FATALERROR 4
+#define CCP_ERR (CCP_ERROR | CCP_FATALERROR)
+#define CCP_DECOMP_RUN 8
+
+unsigned char dbuf[8192];
+
+dumpppp(f)
+ FILE *f;
+{
+ int c, n, k;
+ int nb, nl, dn, proto, rv;
+ char *dir, *q;
+ unsigned char *p, *r, *endp;
+ unsigned char *d;
+ unsigned short fcs;
+ struct pkt *pkt;
+
+ spkt.cnt = rpkt.cnt = 0;
+ spkt.esc = rpkt.esc = 0;
+ while ((c = getc(f)) != EOF) {
+ switch (c) {
+ case RECMARK_STARTSEND:
+ case RECMARK_STARTRECV:
+ if (reverse)
+ c = c==RECMARK_STARTSEND ? RECMARK_STARTRECV :
+ RECMARK_STARTSEND;
+ dir = c==RECMARK_STARTSEND? "sent": "rcvd";
+ pkt = c==RECMARK_STARTSEND? &spkt: &rpkt;
+ n = getc(f);
+ n = (n << 8) + getc(f);
+ *(c==1? &tot_sent: &tot_rcvd) += n;
+ for (; n > 0; --n) {
+ c = getc(f);
+ switch (c) {
+ case EOF:
+ printf("\nEOF\n");
+ if (spkt.cnt > 0)
+ printf("[%d bytes in incomplete send packet]\n",
+ spkt.cnt);
+ if (rpkt.cnt > 0)
+ printf("[%d bytes in incomplete recv packet]\n",
+ rpkt.cnt);
+ exit(0);
+ case '~':
+ if (pkt->cnt > 0) {
+ q = dir;
+ if (pkt->esc) {
+ printf("%s aborted packet:\n ", dir);
+ q = " ";
+ }
+ nb = pkt->cnt;
+ p = pkt->buf;
+ pkt->cnt = 0;
+ pkt->esc = 0;
+ if (nb <= 2) {
+ printf("%s short packet [%d bytes]:", q, nb);
+ for (k = 0; k < nb; ++k)
+ printf(" %.2x", p[k]);
+ printf("\n");
+ break;
+ }
+ fcs = PPP_INITFCS;
+ for (k = 0; k < nb; ++k)
+ fcs = PPP_FCS(fcs, p[k]);
+ fcs &= 0xFFFF;
+ nb -= 2;
+ endp = p + nb;
+ r = p;
+ if (r[0] == 0xff && r[1] == 3)
+ r += 2;
+ if ((r[0] & 1) == 0)
+ ++r;
+ ++r;
+ if (endp - r > mru)
+ printf(" ERROR: length (%d) > MRU (%d)\n",
+ endp - r, mru);
+ if (decompress && fcs == PPP_GOODFCS) {
+ /* See if this is a CCP or compressed packet */
+ d = dbuf;
+ r = p;
+ if (r[0] == 0xff && r[1] == 3) {
+ *d++ = *r++;
+ *d++ = *r++;
+ }
+ proto = r[0];
+ if ((proto & 1) == 0)
+ proto = (proto << 8) + r[1];
+ if (proto == PPP_CCP) {
+ handle_ccp(pkt, r + 2, endp - r - 2);
+ } else if (proto == PPP_COMP) {
+ if ((pkt->flags & CCP_ISUP)
+ && (pkt->flags & CCP_DECOMP_RUN)
+ && pkt->state
+ && (pkt->flags & CCP_ERR) == 0) {
+ rv = pkt->comp->decompress(pkt->state, r,
+ endp - r, d, &dn);
+ switch (rv) {
+ case DECOMP_OK:
+ p = dbuf;
+ nb = d + dn - p;
+ if ((d[0] & 1) == 0)
+ --dn;
+ --dn;
+ if (dn > mru)
+ printf(" ERROR: decompressed length (%d) > MRU (%d)\n", dn, mru);
+ break;
+ case DECOMP_ERROR:
+ printf(" DECOMPRESSION ERROR\n");
+ pkt->flags |= CCP_ERROR;
+ break;
+ case DECOMP_FATALERROR:
+ printf(" FATAL DECOMPRESSION ERROR\n");
+ pkt->flags |= CCP_FATALERROR;
+ break;
+ }
+ }
+ } else if (pkt->state
+ && (pkt->flags & CCP_DECOMP_RUN)) {
+ pkt->comp->incomp(pkt->state, r, endp - r);
+ }
+ }
+ do {
+ nl = nb < 16? nb: 16;
+ printf("%s ", q);
+ for (k = 0; k < nl; ++k)
+ printf(" %.2x", p[k]);
+ for (; k < 16; ++k)
+ printf(" ");
+ printf(" ");
+ for (k = 0; k < nl; ++k) {
+ c = p[k];
+ putchar((' ' <= c && c <= '~')? c: '.');
+ }
+ printf("\n");
+ q = " ";
+ p += nl;
+ nb -= nl;
+ } while (nb > 0);
+ if (fcs != PPP_GOODFCS)
+ printf(" BAD FCS: (residue = %x)\n", fcs);
+ }
+ break;
+ case '}':
+ if (!pkt->esc) {
+ pkt->esc = 1;
+ break;
+ }
+ /* else fall through */
+ default:
+ if (pkt->esc) {
+ c ^= 0x20;
+ pkt->esc = 0;
+ }
+ pkt->buf[pkt->cnt++] = c;
+ break;
+ }
+ }
+ break;
+ case RECMARK_ENDSEND:
+ case RECMARK_ENDRECV:
+ if (reverse)
+ c = c==RECMARK_ENDSEND ? RECMARK_ENDRECV : RECMARK_ENDSEND;
+ dir = c==RECMARK_ENDSEND ? "send": "recv";
+ pkt = c==RECMARK_ENDSEND ? &spkt: &rpkt;
+ printf("end %s", dir);
+ if (pkt->cnt > 0)
+ printf(" [%d bytes in incomplete packet]", pkt->cnt);
+ printf("\n");
+ break;
+ case RECMARK_TIMEDELTA32:
+ case RECMARK_TIMEDELTA8:
+ case RECMARK_TIMESTART:
+ show_time(f, c);
+ break;
+ default:
+ printf("?%.2x\n");
+ }
+ }
+}
+
+extern struct compressor ppp_bsd_compress, ppp_deflate;
+
+struct compressor *compressors[] = {
+#if DO_BSD_COMPRESS
+ &ppp_bsd_compress,
+#endif
+#if DO_DEFLATE
+ &ppp_deflate,
+#endif
+ NULL
+};
+
+handle_ccp(cp, dp, len)
+ struct pkt *cp;
+ u_char *dp;
+ int len;
+{
+ int clen;
+ struct compressor **comp;
+
+ if (len < CCP_HDRLEN)
+ return;
+ clen = CCP_LENGTH(dp);
+ if (clen > len)
+ return;
+
+ switch (CCP_CODE(dp)) {
+ case CCP_CONFACK:
+ cp->flags &= ~(CCP_DECOMP_RUN | CCP_ISUP);
+ if (clen < CCP_HDRLEN + CCP_OPT_MINLEN
+ || clen < CCP_HDRLEN + CCP_OPT_LENGTH(dp + CCP_HDRLEN))
+ break;
+ dp += CCP_HDRLEN;
+ clen -= CCP_HDRLEN;
+ for (comp = compressors; *comp != NULL; ++comp) {
+ if ((*comp)->compress_proto == dp[0]) {
+ if (cp->state != NULL) {
+ (*cp->comp->decomp_free)(cp->state);
+ cp->state = NULL;
+ }
+ cp->comp = *comp;
+ cp->state = (*comp)->decomp_alloc(dp, CCP_OPT_LENGTH(dp));
+ cp->flags |= CCP_ISUP;
+ if (cp->state != NULL
+ && (*cp->comp->decomp_init)
+ (cp->state, dp, clen, 0, 0, 8192, 1))
+ cp->flags = (cp->flags & ~CCP_ERR) | CCP_DECOMP_RUN;
+ break;
+ }
+ }
+ break;
+
+ case CCP_CONFNAK:
+ case CCP_CONFREJ:
+ cp->flags &= ~(CCP_DECOMP_RUN | CCP_ISUP);
+ break;
+
+ case CCP_RESETACK:
+ if (cp->flags & CCP_ISUP) {
+ if (cp->state && (cp->flags & CCP_DECOMP_RUN)) {
+ (*cp->comp->decomp_reset)(cp->state);
+ cp->flags &= ~CCP_ERROR;
+ }
+ }
+ break;
+ }
+}
+
+show_time(f, c)
+ FILE *f;
+ int c;
+{
+ time_t t;
+ int n;
+ struct tm *tm;
+
+ if (c == RECMARK_TIMESTART) {
+ t = getc(f);
+ t = (t << 8) + getc(f);
+ t = (t << 8) + getc(f);
+ t = (t << 8) + getc(f);
+ printf("start %s", ctime(&t));
+ start_time = t;
+ start_time_tenths = 0;
+ tot_sent = tot_rcvd = 0;
+ } else {
+ n = getc(f);
+ if (c == RECMARK_TIMEDELTA32) {
+ for (c = 3; c > 0; --c)
+ n = (n << 8) + getc(f);
+ }
+ if (abs_times) {
+ n += start_time_tenths;
+ start_time += n / 10;
+ start_time_tenths = n % 10;
+ tm = localtime(&start_time);
+ printf("time %.2d:%.2d:%.2d.%d", tm->tm_hour, tm->tm_min,
+ tm->tm_sec, start_time_tenths);
+ printf(" (sent %d, rcvd %d)\n", tot_sent, tot_rcvd);
+ } else
+ printf("time %.1fs\n", (double) n / 10);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppdump/zlib.c b/usr/src/cmd/cmd-inet/usr.bin/pppdump/zlib.c
new file mode 100644
index 0000000000..c3c68f65f8
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppdump/zlib.c
@@ -0,0 +1,4614 @@
+/*
+ * This file is derived from various .h and .c files from the zlib-0.95
+ * distribution by Jean-loup Gailly and Mark Adler, with some additions
+ * by Paul Mackerras to aid in implementing Deflate compression and
+ * decompression for PPP packets. See zlib.h for conditions of
+ * distribution and use.
+ *
+ * Changes that have been made include:
+ * - changed functions not used outside this file to "local"
+ * - added minCompression parameter to deflateInit2
+ * - added Z_PACKET_FLUSH (see zlib.h for details)
+ * - added inflateIncomp
+ *
+ * $Id: zlib.c,v 1.2 1999/04/01 07:26:30 paulus Exp $
+ */
+
+
+/*+++++*/
+/* zutil.h -- internal interface and configuration of the compression library
+ * Copyright (C) 1995 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+/* From: zutil.h,v 1.9 1995/05/03 17:27:12 jloup Exp */
+
+#define _Z_UTIL_H
+
+#include "zlib.h"
+
+#ifdef STDC
+# include <string.h>
+#endif
+
+#ifndef local
+# define local static
+#endif
+/* compile with -Dlocal if your debugger can't find static symbols */
+
+#define FAR
+
+typedef unsigned char uch;
+typedef uch FAR uchf;
+typedef unsigned short ush;
+typedef ush FAR ushf;
+typedef unsigned long ulg;
+
+extern char *z_errmsg[]; /* indexed by 1-zlib_error */
+
+#define ERR_RETURN(strm,err) return (strm->msg=z_errmsg[1-err], err)
+/* To be used only when the state is known to be valid */
+
+#ifndef NULL
+#define NULL ((void *) 0)
+#endif
+
+ /* common constants */
+
+#define DEFLATED 8
+
+#ifndef DEF_WBITS
+# define DEF_WBITS MAX_WBITS
+#endif
+/* default windowBits for decompression. MAX_WBITS is for compression only */
+
+#if MAX_MEM_LEVEL >= 8
+# define DEF_MEM_LEVEL 8
+#else
+# define DEF_MEM_LEVEL MAX_MEM_LEVEL
+#endif
+/* default memLevel */
+
+#define STORED_BLOCK 0
+#define STATIC_TREES 1
+#define DYN_TREES 2
+/* The three kinds of block type */
+
+#define MIN_MATCH 3
+#define MAX_MATCH 258
+/* The minimum and maximum match lengths */
+
+ /* functions */
+
+#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY)
+# define HAVE_MEMCPY
+#endif
+#ifdef HAVE_MEMCPY
+# define zmemcpy memcpy
+# define zmemzero(dest, len) memset(dest, 0, len)
+#else
+# define zmemcpy(d, s, n) bcopy((s), (d), (n))
+# define zmemzero bzero
+#endif
+
+/* Diagnostic functions */
+#ifdef DEBUG_ZLIB
+# include <stdio.h>
+# ifndef verbose
+# define verbose 0
+# endif
+# define Assert(cond,msg) {if(!(cond)) z_error(msg);}
+# define Trace(x) fprintf x
+# define Tracev(x) {if (verbose) fprintf x ;}
+# define Tracevv(x) {if (verbose>1) fprintf x ;}
+# define Tracec(c,x) {if (verbose && (c)) fprintf x ;}
+# define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;}
+#else
+# define Assert(cond,msg)
+# define Trace(x)
+# define Tracev(x)
+# define Tracevv(x)
+# define Tracec(c,x)
+# define Tracecv(c,x)
+#endif
+
+
+typedef uLong (*check_func) OF((uLong check, Bytef *buf, uInt len));
+
+/* voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size)); */
+/* void zcfree OF((voidpf opaque, voidpf ptr)); */
+
+#define ZALLOC(strm, items, size) \
+ (*((strm)->zalloc))((strm)->opaque, (items), (size))
+#define ZFREE(strm, addr, size) \
+ (*((strm)->zfree))((strm)->opaque, (voidpf)(addr), (size))
+#define TRY_FREE(s, p, n) {if (p) ZFREE(s, p, n);}
+
+/* deflate.h -- internal compression state
+ * Copyright (C) 1995 Jean-loup Gailly
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+
+/*+++++*/
+/* From: deflate.h,v 1.5 1995/05/03 17:27:09 jloup Exp */
+
+/* ===========================================================================
+ * Internal compression state.
+ */
+
+/* Data type */
+#define BINARY 0
+#define ASCII 1
+#define UNKNOWN 2
+
+#define LENGTH_CODES 29
+/* number of length codes, not counting the special END_BLOCK code */
+
+#define LITERALS 256
+/* number of literal bytes 0..255 */
+
+#define L_CODES (LITERALS+1+LENGTH_CODES)
+/* number of Literal or Length codes, including the END_BLOCK code */
+
+#define D_CODES 30
+/* number of distance codes */
+
+#define BL_CODES 19
+/* number of codes used to transfer the bit lengths */
+
+#define HEAP_SIZE (2*L_CODES+1)
+/* maximum heap size */
+
+#define MAX_BITS 15
+/* All codes must not exceed MAX_BITS bits */
+
+#define INIT_STATE 42
+#define BUSY_STATE 113
+#define FLUSH_STATE 124
+#define FINISH_STATE 666
+/* Stream status */
+
+
+/* Data structure describing a single value and its code string. */
+typedef struct ct_data_s {
+ union {
+ ush freq; /* frequency count */
+ ush code; /* bit string */
+ } fc;
+ union {
+ ush dad; /* father node in Huffman tree */
+ ush len; /* length of bit string */
+ } dl;
+} FAR ct_data;
+
+#define Freq fc.freq
+#define Code fc.code
+#define Dad dl.dad
+#define Len dl.len
+
+typedef struct static_tree_desc_s static_tree_desc;
+
+typedef struct tree_desc_s {
+ ct_data *dyn_tree; /* the dynamic tree */
+ int max_code; /* largest code with non zero frequency */
+ static_tree_desc *stat_desc; /* the corresponding static tree */
+} FAR tree_desc;
+
+typedef ush Pos;
+typedef Pos FAR Posf;
+typedef unsigned IPos;
+
+/* A Pos is an index in the character window. We use short instead of int to
+ * save space in the various tables. IPos is used only for parameter passing.
+ */
+
+typedef struct deflate_state {
+ z_stream *strm; /* pointer back to this zlib stream */
+ int status; /* as the name implies */
+ Bytef *pending_buf; /* output still pending */
+ Bytef *pending_out; /* next pending byte to output to the stream */
+ int pending; /* nb of bytes in the pending buffer */
+ uLong adler; /* adler32 of uncompressed data */
+ int noheader; /* suppress zlib header and adler32 */
+ Byte data_type; /* UNKNOWN, BINARY or ASCII */
+ Byte method; /* STORED (for zip only) or DEFLATED */
+ int minCompr; /* min size decrease for Z_FLUSH_NOSTORE */
+
+ /* used by deflate.c: */
+
+ uInt w_size; /* LZ77 window size (32K by default) */
+ uInt w_bits; /* log2(w_size) (8..16) */
+ uInt w_mask; /* w_size - 1 */
+
+ Bytef *window;
+ /* Sliding window. Input bytes are read into the second half of the window,
+ * and move to the first half later to keep a dictionary of at least wSize
+ * bytes. With this organization, matches are limited to a distance of
+ * wSize-MAX_MATCH bytes, but this ensures that IO is always
+ * performed with a length multiple of the block size. Also, it limits
+ * the window size to 64K, which is quite useful on MSDOS.
+ * To do: use the user input buffer as sliding window.
+ */
+
+ ulg window_size;
+ /* Actual size of window: 2*wSize, except when the user input buffer
+ * is directly used as sliding window.
+ */
+
+ Posf *prev;
+ /* Link to older string with same hash index. To limit the size of this
+ * array to 64K, this link is maintained only for the last 32K strings.
+ * An index in this array is thus a window index modulo 32K.
+ */
+
+ Posf *head; /* Heads of the hash chains or NIL. */
+
+ uInt ins_h; /* hash index of string to be inserted */
+ uInt hash_size; /* number of elements in hash table */
+ uInt hash_bits; /* log2(hash_size) */
+ uInt hash_mask; /* hash_size-1 */
+
+ uInt hash_shift;
+ /* Number of bits by which ins_h must be shifted at each input
+ * step. It must be such that after MIN_MATCH steps, the oldest
+ * byte no longer takes part in the hash key, that is:
+ * hash_shift * MIN_MATCH >= hash_bits
+ */
+
+ long block_start;
+ /* Window position at the beginning of the current output block. Gets
+ * negative when the window is moved backwards.
+ */
+
+ uInt match_length; /* length of best match */
+ IPos prev_match; /* previous match */
+ int match_available; /* set if previous match exists */
+ uInt strstart; /* start of string to insert */
+ uInt match_start; /* start of matching string */
+ uInt lookahead; /* number of valid bytes ahead in window */
+
+ uInt prev_length;
+ /* Length of the best match at previous step. Matches not greater than this
+ * are discarded. This is used in the lazy match evaluation.
+ */
+
+ uInt max_chain_length;
+ /* To speed up deflation, hash chains are never searched beyond this
+ * length. A higher limit improves compression ratio but degrades the
+ * speed.
+ */
+
+ uInt max_lazy_match;
+ /* Attempt to find a better match only when the current match is strictly
+ * smaller than this value. This mechanism is used only for compression
+ * levels >= 4.
+ */
+# define max_insert_length max_lazy_match
+ /* Insert new strings in the hash table only if the match length is not
+ * greater than this length. This saves time but degrades compression.
+ * max_insert_length is used only for compression levels <= 3.
+ */
+
+ int level; /* compression level (1..9) */
+ int strategy; /* favor or force Huffman coding*/
+
+ uInt good_match;
+ /* Use a faster search when the previous match is longer than this */
+
+ int nice_match; /* Stop searching when current match exceeds this */
+
+ /* used by trees.c: */
+ /* Didn't use ct_data typedef below to supress compiler warning */
+ struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */
+ struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */
+ struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */
+
+ struct tree_desc_s l_desc; /* desc. for literal tree */
+ struct tree_desc_s d_desc; /* desc. for distance tree */
+ struct tree_desc_s bl_desc; /* desc. for bit length tree */
+
+ ush bl_count[MAX_BITS+1];
+ /* number of codes at each bit length for an optimal tree */
+
+ int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */
+ int heap_len; /* number of elements in the heap */
+ int heap_max; /* element of largest frequency */
+ /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used.
+ * The same heap array is used to build all trees.
+ */
+
+ uch depth[2*L_CODES+1];
+ /* Depth of each subtree used as tie breaker for trees of equal frequency
+ */
+
+ uchf *l_buf; /* buffer for literals or lengths */
+
+ uInt lit_bufsize;
+ /* Size of match buffer for literals/lengths. There are 4 reasons for
+ * limiting lit_bufsize to 64K:
+ * - frequencies can be kept in 16 bit counters
+ * - if compression is not successful for the first block, all input
+ * data is still in the window so we can still emit a stored block even
+ * when input comes from standard input. (This can also be done for
+ * all blocks if lit_bufsize is not greater than 32K.)
+ * - if compression is not successful for a file smaller than 64K, we can
+ * even emit a stored file instead of a stored block (saving 5 bytes).
+ * This is applicable only for zip (not gzip or zlib).
+ * - creating new Huffman trees less frequently may not provide fast
+ * adaptation to changes in the input data statistics. (Take for
+ * example a binary file with poorly compressible code followed by
+ * a highly compressible string table.) Smaller buffer sizes give
+ * fast adaptation but have of course the overhead of transmitting
+ * trees more frequently.
+ * - I can't count above 4
+ */
+
+ uInt last_lit; /* running index in l_buf */
+
+ ushf *d_buf;
+ /* Buffer for distances. To simplify the code, d_buf and l_buf have
+ * the same number of elements. To use different lengths, an extra flag
+ * array would be necessary.
+ */
+
+ ulg opt_len; /* bit length of current block with optimal trees */
+ ulg static_len; /* bit length of current block with static trees */
+ ulg compressed_len; /* total bit length of compressed file */
+ uInt matches; /* number of string matches in current block */
+ int last_eob_len; /* bit length of EOB code for last block */
+
+#ifdef DEBUG_ZLIB
+ ulg bits_sent; /* bit length of the compressed data */
+#endif
+
+ ush bi_buf;
+ /* Output buffer. bits are inserted starting at the bottom (least
+ * significant bits).
+ */
+ int bi_valid;
+ /* Number of valid bits in bi_buf. All bits above the last valid bit
+ * are always zero.
+ */
+
+ uInt blocks_in_packet;
+ /* Number of blocks produced since the last time Z_PACKET_FLUSH
+ * was used.
+ */
+
+} FAR deflate_state;
+
+/* Output a byte on the stream.
+ * IN assertion: there is enough room in pending_buf.
+ */
+#define put_byte(s, c) {s->pending_buf[s->pending++] = (c);}
+
+
+#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1)
+/* Minimum amount of lookahead, except at the end of the input file.
+ * See deflate.c for comments about the MIN_MATCH+1.
+ */
+
+#define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD)
+/* In order to simplify the code, particularly on 16 bit machines, match
+ * distances are limited to MAX_DIST instead of WSIZE.
+ */
+
+ /* in trees.c */
+local void ct_init OF((deflate_state *s));
+local int ct_tally OF((deflate_state *s, int dist, int lc));
+local ulg ct_flush_block OF((deflate_state *s, charf *buf, ulg stored_len,
+ int flush));
+local void ct_align OF((deflate_state *s));
+local void ct_stored_block OF((deflate_state *s, charf *buf, ulg stored_len,
+ int eof));
+local void ct_stored_type_only OF((deflate_state *s));
+
+
+/*+++++*/
+/* deflate.c -- compress data using the deflation algorithm
+ * Copyright (C) 1995 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/*
+ * ALGORITHM
+ *
+ * The "deflation" process depends on being able to identify portions
+ * of the input text which are identical to earlier input (within a
+ * sliding window trailing behind the input currently being processed).
+ *
+ * The most straightforward technique turns out to be the fastest for
+ * most input files: try all possible matches and select the longest.
+ * The key feature of this algorithm is that insertions into the string
+ * dictionary are very simple and thus fast, and deletions are avoided
+ * completely. Insertions are performed at each input character, whereas
+ * string matches are performed only when the previous match ends. So it
+ * is preferable to spend more time in matches to allow very fast string
+ * insertions and avoid deletions. The matching algorithm for small
+ * strings is inspired from that of Rabin & Karp. A brute force approach
+ * is used to find longer strings when a small match has been found.
+ * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze
+ * (by Leonid Broukhis).
+ * A previous version of this file used a more sophisticated algorithm
+ * (by Fiala and Greene) which is guaranteed to run in linear amortized
+ * time, but has a larger average cost, uses more memory and is patented.
+ * However the F&G algorithm may be faster for some highly redundant
+ * files if the parameter max_chain_length (described below) is too large.
+ *
+ * ACKNOWLEDGEMENTS
+ *
+ * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and
+ * I found it in 'freeze' written by Leonid Broukhis.
+ * Thanks to many people for bug reports and testing.
+ *
+ * REFERENCES
+ *
+ * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification".
+ * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc
+ *
+ * A description of the Rabin and Karp algorithm is given in the book
+ * "Algorithms" by R. Sedgewick, Addison-Wesley, p252.
+ *
+ * Fiala,E.R., and Greene,D.H.
+ * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595
+ *
+ */
+
+/* From: deflate.c,v 1.8 1995/05/03 17:27:08 jloup Exp */
+
+local char zlib_copyright[] = " deflate Copyright 1995 Jean-loup Gailly ";
+/*
+ If you use the zlib library in a product, an acknowledgment is welcome
+ in the documentation of your product. If for some reason you cannot
+ include such an acknowledgment, I would appreciate that you keep this
+ copyright string in the executable of your product.
+ */
+
+#define NIL 0
+/* Tail of hash chains */
+
+#ifndef TOO_FAR
+# define TOO_FAR 4096
+#endif
+/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */
+
+#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1)
+/* Minimum amount of lookahead, except at the end of the input file.
+ * See deflate.c for comments about the MIN_MATCH+1.
+ */
+
+/* Values for max_lazy_match, good_match and max_chain_length, depending on
+ * the desired pack level (0..9). The values given below have been tuned to
+ * exclude worst case performance for pathological files. Better values may be
+ * found for specific files.
+ */
+
+typedef struct config_s {
+ ush good_length; /* reduce lazy search above this match length */
+ ush max_lazy; /* do not perform lazy search above this match length */
+ ush nice_length; /* quit search above this match length */
+ ush max_chain;
+} config;
+
+local config configuration_table[10] = {
+/* good lazy nice chain */
+/* 0 */ {0, 0, 0, 0}, /* store only */
+/* 1 */ {4, 4, 8, 4}, /* maximum speed, no lazy matches */
+/* 2 */ {4, 5, 16, 8},
+/* 3 */ {4, 6, 32, 32},
+
+/* 4 */ {4, 4, 16, 16}, /* lazy matches */
+/* 5 */ {8, 16, 32, 32},
+/* 6 */ {8, 16, 128, 128},
+/* 7 */ {8, 32, 128, 256},
+/* 8 */ {32, 128, 258, 1024},
+/* 9 */ {32, 258, 258, 4096}}; /* maximum compression */
+
+/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4
+ * For deflate_fast() (levels <= 3) good is ignored and lazy has a different
+ * meaning.
+ */
+
+#define EQUAL 0
+/* result of memcmp for equal strings */
+
+/* ===========================================================================
+ * Prototypes for local functions.
+ */
+
+local void fill_window OF((deflate_state *s));
+local int deflate_fast OF((deflate_state *s, int flush));
+local int deflate_slow OF((deflate_state *s, int flush));
+local void lm_init OF((deflate_state *s));
+local int longest_match OF((deflate_state *s, IPos cur_match));
+local void putShortMSB OF((deflate_state *s, uInt b));
+local void flush_pending OF((z_stream *strm));
+local int read_buf OF((z_stream *strm, charf *buf, unsigned size));
+#ifdef ASMV
+ void match_init OF((void)); /* asm code initialization */
+#endif
+
+#ifdef DEBUG_ZLIB
+local void check_match OF((deflate_state *s, IPos start, IPos match,
+ int length));
+#endif
+
+
+/* ===========================================================================
+ * Update a hash value with the given input byte
+ * IN assertion: all calls to to UPDATE_HASH are made with consecutive
+ * input characters, so that a running hash key can be computed from the
+ * previous key instead of complete recalculation each time.
+ */
+#define UPDATE_HASH(s,h,c) (h = (((h)<<s->hash_shift) ^ (c)) & s->hash_mask)
+
+
+/* ===========================================================================
+ * Insert string str in the dictionary and set match_head to the previous head
+ * of the hash chain (the most recent string with same hash key). Return
+ * the previous length of the hash chain.
+ * IN assertion: all calls to to INSERT_STRING are made with consecutive
+ * input characters and the first MIN_MATCH bytes of str are valid
+ * (except for the last MIN_MATCH-1 bytes of the input file).
+ */
+#define INSERT_STRING(s, str, match_head) \
+ (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \
+ s->prev[(str) & s->w_mask] = match_head = s->head[s->ins_h], \
+ s->head[s->ins_h] = (str))
+
+/* ===========================================================================
+ * Initialize the hash table (avoiding 64K overflow for 16 bit systems).
+ * prev[] will be initialized on the fly.
+ */
+#define CLEAR_HASH(s) \
+ s->head[s->hash_size-1] = NIL; \
+ zmemzero((charf *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head));
+
+/* ========================================================================= */
+int deflateInit (strm, level)
+ z_stream *strm;
+ int level;
+{
+ return deflateInit2 (strm, level, DEFLATED, MAX_WBITS, DEF_MEM_LEVEL,
+ 0, 0);
+ /* To do: ignore strm->next_in if we use it as window */
+}
+
+/* ========================================================================= */
+int deflateInit2 (strm, level, method, windowBits, memLevel,
+ strategy, minCompression)
+ z_stream *strm;
+ int level;
+ int method;
+ int windowBits;
+ int memLevel;
+ int strategy;
+ int minCompression;
+{
+ deflate_state *s;
+ int noheader = 0;
+
+ if (strm == Z_NULL) return Z_STREAM_ERROR;
+
+ strm->msg = Z_NULL;
+/* if (strm->zalloc == Z_NULL) strm->zalloc = zcalloc; */
+/* if (strm->zfree == Z_NULL) strm->zfree = zcfree; */
+
+ if (level == Z_DEFAULT_COMPRESSION) level = 6;
+
+ if (windowBits < 0) { /* undocumented feature: suppress zlib header */
+ noheader = 1;
+ windowBits = -windowBits;
+ }
+ if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != DEFLATED ||
+ windowBits < 8 || windowBits > 15 || level < 1 || level > 9) {
+ return Z_STREAM_ERROR;
+ }
+ s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state));
+ if (s == Z_NULL) return Z_MEM_ERROR;
+ strm->state = (struct internal_state FAR *)s;
+ s->strm = strm;
+
+ s->noheader = noheader;
+ s->w_bits = windowBits;
+ s->w_size = 1 << s->w_bits;
+ s->w_mask = s->w_size - 1;
+
+ s->hash_bits = memLevel + 7;
+ s->hash_size = 1 << s->hash_bits;
+ s->hash_mask = s->hash_size - 1;
+ s->hash_shift = ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH);
+
+ s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte));
+ s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos));
+ s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos));
+
+ s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */
+
+ s->pending_buf = (uchf *) ZALLOC(strm, s->lit_bufsize, 2*sizeof(ush));
+
+ if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL ||
+ s->pending_buf == Z_NULL) {
+ strm->msg = z_errmsg[1-Z_MEM_ERROR];
+ deflateEnd (strm);
+ return Z_MEM_ERROR;
+ }
+ s->d_buf = (ushf *) &(s->pending_buf[s->lit_bufsize]);
+ s->l_buf = (uchf *) &(s->pending_buf[3*s->lit_bufsize]);
+ /* We overlay pending_buf and d_buf+l_buf. This works since the average
+ * output size for (length,distance) codes is <= 32 bits (worst case
+ * is 15+15+13=33).
+ */
+
+ s->level = level;
+ s->strategy = strategy;
+ s->method = (Byte)method;
+ s->minCompr = minCompression;
+ s->blocks_in_packet = 0;
+
+ return deflateReset(strm);
+}
+
+/* ========================================================================= */
+int deflateReset (strm)
+ z_stream *strm;
+{
+ deflate_state *s;
+
+ if (strm == Z_NULL || strm->state == Z_NULL ||
+ strm->zalloc == Z_NULL || strm->zfree == Z_NULL) return Z_STREAM_ERROR;
+
+ strm->total_in = strm->total_out = 0;
+ strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */
+ strm->data_type = Z_UNKNOWN;
+
+ s = (deflate_state *)strm->state;
+ s->pending = 0;
+ s->pending_out = s->pending_buf;
+
+ if (s->noheader < 0) {
+ s->noheader = 0; /* was set to -1 by deflate(..., Z_FINISH); */
+ }
+ s->status = s->noheader ? BUSY_STATE : INIT_STATE;
+ s->adler = 1;
+
+ ct_init(s);
+ lm_init(s);
+
+ return Z_OK;
+}
+
+/* =========================================================================
+ * Put a short in the pending buffer. The 16-bit value is put in MSB order.
+ * IN assertion: the stream state is correct and there is enough room in
+ * pending_buf.
+ */
+local void putShortMSB (s, b)
+ deflate_state *s;
+ uInt b;
+{
+ put_byte(s, (Byte)(b >> 8));
+ put_byte(s, (Byte)(b & 0xff));
+}
+
+/* =========================================================================
+ * Flush as much pending output as possible.
+ */
+local void flush_pending(strm)
+ z_stream *strm;
+{
+ deflate_state *state = (deflate_state *) strm->state;
+ unsigned len = state->pending;
+
+ if (len > strm->avail_out) len = strm->avail_out;
+ if (len == 0) return;
+
+ if (strm->next_out != NULL) {
+ zmemcpy(strm->next_out, state->pending_out, len);
+ strm->next_out += len;
+ }
+ state->pending_out += len;
+ strm->total_out += len;
+ strm->avail_out -= len;
+ state->pending -= len;
+ if (state->pending == 0) {
+ state->pending_out = state->pending_buf;
+ }
+}
+
+/* ========================================================================= */
+int deflate (strm, flush)
+ z_stream *strm;
+ int flush;
+{
+ deflate_state *state = (deflate_state *) strm->state;
+
+ if (strm == Z_NULL || state == Z_NULL) return Z_STREAM_ERROR;
+
+ if (strm->next_in == Z_NULL && strm->avail_in != 0) {
+ ERR_RETURN(strm, Z_STREAM_ERROR);
+ }
+ if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR);
+
+ state->strm = strm; /* just in case */
+
+ /* Write the zlib header */
+ if (state->status == INIT_STATE) {
+
+ uInt header = (DEFLATED + ((state->w_bits-8)<<4)) << 8;
+ uInt level_flags = (state->level-1) >> 1;
+
+ if (level_flags > 3) level_flags = 3;
+ header |= (level_flags << 6);
+ header += 31 - (header % 31);
+
+ state->status = BUSY_STATE;
+ putShortMSB(state, header);
+ }
+
+ /* Flush as much pending output as possible */
+ if (state->pending != 0) {
+ flush_pending(strm);
+ if (strm->avail_out == 0) return Z_OK;
+ }
+
+ /* If we came back in here to get the last output from
+ * a previous flush, we're done for now.
+ */
+ if (state->status == FLUSH_STATE) {
+ state->status = BUSY_STATE;
+ if (flush != Z_NO_FLUSH && flush != Z_FINISH)
+ return Z_OK;
+ }
+
+ /* User must not provide more input after the first FINISH: */
+ if (state->status == FINISH_STATE && strm->avail_in != 0) {
+ ERR_RETURN(strm, Z_BUF_ERROR);
+ }
+
+ /* Start a new block or continue the current one.
+ */
+ if (strm->avail_in != 0 || state->lookahead != 0 ||
+ (flush == Z_FINISH && state->status != FINISH_STATE)) {
+ int quit;
+
+ if (flush == Z_FINISH) {
+ state->status = FINISH_STATE;
+ }
+ if (state->level <= 3) {
+ quit = deflate_fast(state, flush);
+ } else {
+ quit = deflate_slow(state, flush);
+ }
+ if (quit || strm->avail_out == 0)
+ return Z_OK;
+ /* If flush != Z_NO_FLUSH && avail_out == 0, the next call
+ * of deflate should use the same flush parameter to make sure
+ * that the flush is complete. So we don't have to output an
+ * empty block here, this will be done at next call. This also
+ * ensures that for a very small output buffer, we emit at most
+ * one empty block.
+ */
+ }
+
+ /* If a flush was requested, we have a little more to output now. */
+ if (flush != Z_NO_FLUSH && flush != Z_FINISH
+ && state->status != FINISH_STATE) {
+ switch (flush) {
+ case Z_PARTIAL_FLUSH:
+ ct_align(state);
+ break;
+ case Z_PACKET_FLUSH:
+ /* Output just the 3-bit `stored' block type value,
+ but not a zero length. */
+ ct_stored_type_only(state);
+ break;
+ default:
+ ct_stored_block(state, (char*)0, 0L, 0);
+ /* For a full flush, this empty block will be recognized
+ * as a special marker by inflate_sync().
+ */
+ if (flush == Z_FULL_FLUSH) {
+ CLEAR_HASH(state); /* forget history */
+ }
+ }
+ flush_pending(strm);
+ if (strm->avail_out == 0) {
+ /* We'll have to come back to get the rest of the output;
+ * this ensures we don't output a second zero-length stored
+ * block (or whatever).
+ */
+ state->status = FLUSH_STATE;
+ return Z_OK;
+ }
+ }
+
+ Assert(strm->avail_out > 0, "bug2");
+
+ if (flush != Z_FINISH) return Z_OK;
+ if (state->noheader) return Z_STREAM_END;
+
+ /* Write the zlib trailer (adler32) */
+ putShortMSB(state, (uInt)(state->adler >> 16));
+ putShortMSB(state, (uInt)(state->adler & 0xffff));
+ flush_pending(strm);
+ /* If avail_out is zero, the application will call deflate again
+ * to flush the rest.
+ */
+ state->noheader = -1; /* write the trailer only once! */
+ return state->pending != 0 ? Z_OK : Z_STREAM_END;
+}
+
+/* ========================================================================= */
+int deflateEnd (strm)
+ z_stream *strm;
+{
+ deflate_state *state = (deflate_state *) strm->state;
+
+ if (strm == Z_NULL || state == Z_NULL) return Z_STREAM_ERROR;
+
+ TRY_FREE(strm, state->window, state->w_size * 2 * sizeof(Byte));
+ TRY_FREE(strm, state->prev, state->w_size * sizeof(Pos));
+ TRY_FREE(strm, state->head, state->hash_size * sizeof(Pos));
+ TRY_FREE(strm, state->pending_buf, state->lit_bufsize * 2 * sizeof(ush));
+
+ ZFREE(strm, state, sizeof(deflate_state));
+ strm->state = Z_NULL;
+
+ return Z_OK;
+}
+
+/* ===========================================================================
+ * Read a new buffer from the current input stream, update the adler32
+ * and total number of bytes read.
+ */
+local int read_buf(strm, buf, size)
+ z_stream *strm;
+ charf *buf;
+ unsigned size;
+{
+ unsigned len = strm->avail_in;
+ deflate_state *state = (deflate_state *) strm->state;
+
+ if (len > size) len = size;
+ if (len == 0) return 0;
+
+ strm->avail_in -= len;
+
+ if (!state->noheader) {
+ state->adler = adler32(state->adler, strm->next_in, len);
+ }
+ zmemcpy(buf, strm->next_in, len);
+ strm->next_in += len;
+ strm->total_in += len;
+
+ return (int)len;
+}
+
+/* ===========================================================================
+ * Initialize the "longest match" routines for a new zlib stream
+ */
+local void lm_init (s)
+ deflate_state *s;
+{
+ s->window_size = (ulg)2L*s->w_size;
+
+ CLEAR_HASH(s);
+
+ /* Set the default configuration parameters:
+ */
+ s->max_lazy_match = configuration_table[s->level].max_lazy;
+ s->good_match = configuration_table[s->level].good_length;
+ s->nice_match = configuration_table[s->level].nice_length;
+ s->max_chain_length = configuration_table[s->level].max_chain;
+
+ s->strstart = 0;
+ s->block_start = 0L;
+ s->lookahead = 0;
+ s->match_length = MIN_MATCH-1;
+ s->match_available = 0;
+ s->ins_h = 0;
+#ifdef ASMV
+ match_init(); /* initialize the asm code */
+#endif
+}
+
+/* ===========================================================================
+ * Set match_start to the longest match starting at the given string and
+ * return its length. Matches shorter or equal to prev_length are discarded,
+ * in which case the result is equal to prev_length and match_start is
+ * garbage.
+ * IN assertions: cur_match is the head of the hash chain for the current
+ * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1
+ */
+#ifndef ASMV
+/* For 80x86 and 680x0, an optimized version will be provided in match.asm or
+ * match.S. The code will be functionally equivalent.
+ */
+local int longest_match(s, cur_match)
+ deflate_state *s;
+ IPos cur_match; /* current match */
+{
+ unsigned chain_length = s->max_chain_length;/* max hash chain length */
+ register Bytef *scan = s->window + s->strstart; /* current string */
+ register Bytef *match; /* matched string */
+ register int len; /* length of current match */
+ int best_len = s->prev_length; /* best match length so far */
+ IPos limit = s->strstart > (IPos)MAX_DIST(s) ?
+ s->strstart - (IPos)MAX_DIST(s) : NIL;
+ /* Stop when cur_match becomes <= limit. To simplify the code,
+ * we prevent matches with the string of window index 0.
+ */
+ Posf *prev = s->prev;
+ uInt wmask = s->w_mask;
+
+#ifdef UNALIGNED_OK
+ /* Compare two bytes at a time. Note: this is not always beneficial.
+ * Try with and without -DUNALIGNED_OK to check.
+ */
+ register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1;
+ register ush scan_start = *(ushf*)scan;
+ register ush scan_end = *(ushf*)(scan+best_len-1);
+#else
+ register Bytef *strend = s->window + s->strstart + MAX_MATCH;
+ register Byte scan_end1 = scan[best_len-1];
+ register Byte scan_end = scan[best_len];
+#endif
+
+ /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.
+ * It is easy to get rid of this optimization if necessary.
+ */
+ Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever");
+
+ /* Do not waste too much time if we already have a good match: */
+ if (s->prev_length >= s->good_match) {
+ chain_length >>= 2;
+ }
+ Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead");
+
+ do {
+ Assert(cur_match < s->strstart, "no future");
+ match = s->window + cur_match;
+
+ /* Skip to next match if the match length cannot increase
+ * or if the match length is less than 2:
+ */
+#if (defined(UNALIGNED_OK) && MAX_MATCH == 258)
+ /* This code assumes sizeof(unsigned short) == 2. Do not use
+ * UNALIGNED_OK if your compiler uses a different size.
+ */
+ if (*(ushf*)(match+best_len-1) != scan_end ||
+ *(ushf*)match != scan_start) continue;
+
+ /* It is not necessary to compare scan[2] and match[2] since they are
+ * always equal when the other bytes match, given that the hash keys
+ * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at
+ * strstart+3, +5, ... up to strstart+257. We check for insufficient
+ * lookahead only every 4th comparison; the 128th check will be made
+ * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is
+ * necessary to put more guard bytes at the end of the window, or
+ * to check more often for insufficient lookahead.
+ */
+ Assert(scan[2] == match[2], "scan[2]?");
+ scan++, match++;
+ do {
+ } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+ *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+ *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+ *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+ scan < strend);
+ /* The funny "do {}" generates better code on most compilers */
+
+ /* Here, scan <= window+strstart+257 */
+ Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
+ if (*scan == *match) scan++;
+
+ len = (MAX_MATCH - 1) - (int)(strend-scan);
+ scan = strend - (MAX_MATCH-1);
+
+#else /* UNALIGNED_OK */
+
+ if (match[best_len] != scan_end ||
+ match[best_len-1] != scan_end1 ||
+ *match != *scan ||
+ *++match != scan[1]) continue;
+
+ /* The check at best_len-1 can be removed because it will be made
+ * again later. (This heuristic is not always a win.)
+ * It is not necessary to compare scan[2] and match[2] since they
+ * are always equal when the other bytes match, given that
+ * the hash keys are equal and that HASH_BITS >= 8.
+ */
+ scan += 2, match++;
+ Assert(*scan == *match, "match[2]?");
+
+ /* We check for insufficient lookahead only every 8th comparison;
+ * the 256th check will be made at strstart+258.
+ */
+ do {
+ } while (*++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ scan < strend);
+
+ Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
+
+ len = MAX_MATCH - (int)(strend - scan);
+ scan = strend - MAX_MATCH;
+
+#endif /* UNALIGNED_OK */
+
+ if (len > best_len) {
+ s->match_start = cur_match;
+ best_len = len;
+ if (len >= s->nice_match) break;
+#ifdef UNALIGNED_OK
+ scan_end = *(ushf*)(scan+best_len-1);
+#else
+ scan_end1 = scan[best_len-1];
+ scan_end = scan[best_len];
+#endif
+ }
+ } while ((cur_match = prev[cur_match & wmask]) > limit
+ && --chain_length != 0);
+
+ return best_len;
+}
+#endif /* ASMV */
+
+#ifdef DEBUG_ZLIB
+/* ===========================================================================
+ * Check that the match at match_start is indeed a match.
+ */
+local void check_match(s, start, match, length)
+ deflate_state *s;
+ IPos start, match;
+ int length;
+{
+ /* check that the match is indeed a match */
+ if (memcmp((charf *)s->window + match,
+ (charf *)s->window + start, length) != EQUAL) {
+ fprintf(stderr,
+ " start %u, match %u, length %d\n",
+ start, match, length);
+ do { fprintf(stderr, "%c%c", s->window[match++],
+ s->window[start++]); } while (--length != 0);
+ z_error("invalid match");
+ }
+ if (verbose > 1) {
+ fprintf(stderr,"\\[%d,%d]", start-match, length);
+ do { putc(s->window[start++], stderr); } while (--length != 0);
+ }
+}
+#else
+# define check_match(s, start, match, length)
+#endif
+
+/* ===========================================================================
+ * Fill the window when the lookahead becomes insufficient.
+ * Updates strstart and lookahead.
+ *
+ * IN assertion: lookahead < MIN_LOOKAHEAD
+ * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD
+ * At least one byte has been read, or avail_in == 0; reads are
+ * performed for at least two bytes (required for the zip translate_eol
+ * option -- not supported here).
+ */
+local void fill_window(s)
+ deflate_state *s;
+{
+ register unsigned n, m;
+ register Posf *p;
+ unsigned more; /* Amount of free space at the end of the window. */
+ uInt wsize = s->w_size;
+
+ do {
+ more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart);
+
+ /* Deal with !@#$% 64K limit: */
+ if (more == 0 && s->strstart == 0 && s->lookahead == 0) {
+ more = wsize;
+ } else if (more == (unsigned)(-1)) {
+ /* Very unlikely, but possible on 16 bit machine if strstart == 0
+ * and lookahead == 1 (input done one byte at time)
+ */
+ more--;
+
+ /* If the window is almost full and there is insufficient lookahead,
+ * move the upper half to the lower one to make room in the upper half.
+ */
+ } else if (s->strstart >= wsize+MAX_DIST(s)) {
+
+ /* By the IN assertion, the window is not empty so we can't confuse
+ * more == 0 with more == 64K on a 16 bit machine.
+ */
+ zmemcpy((charf *)s->window, (charf *)s->window+wsize,
+ (unsigned)wsize);
+ s->match_start -= wsize;
+ s->strstart -= wsize; /* we now have strstart >= MAX_DIST */
+
+ s->block_start -= (long) wsize;
+
+ /* Slide the hash table (could be avoided with 32 bit values
+ at the expense of memory usage):
+ */
+ n = s->hash_size;
+ p = &s->head[n];
+ do {
+ m = *--p;
+ *p = (Pos)(m >= wsize ? m-wsize : NIL);
+ } while (--n);
+
+ n = wsize;
+ p = &s->prev[n];
+ do {
+ m = *--p;
+ *p = (Pos)(m >= wsize ? m-wsize : NIL);
+ /* If n is not on any hash chain, prev[n] is garbage but
+ * its value will never be used.
+ */
+ } while (--n);
+
+ more += wsize;
+ }
+ if (s->strm->avail_in == 0) return;
+
+ /* If there was no sliding:
+ * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 &&
+ * more == window_size - lookahead - strstart
+ * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1)
+ * => more >= window_size - 2*WSIZE + 2
+ * In the BIG_MEM or MMAP case (not yet supported),
+ * window_size == input_size + MIN_LOOKAHEAD &&
+ * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD.
+ * Otherwise, window_size == 2*WSIZE so more >= 2.
+ * If there was sliding, more >= WSIZE. So in all cases, more >= 2.
+ */
+ Assert(more >= 2, "more < 2");
+
+ n = read_buf(s->strm, (charf *)s->window + s->strstart + s->lookahead,
+ more);
+ s->lookahead += n;
+
+ /* Initialize the hash value now that we have some input: */
+ if (s->lookahead >= MIN_MATCH) {
+ s->ins_h = s->window[s->strstart];
+ UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]);
+#if MIN_MATCH != 3
+ Call UPDATE_HASH() MIN_MATCH-3 more times
+#endif
+ }
+ /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage,
+ * but this is not important since only literal bytes will be emitted.
+ */
+
+ } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0);
+}
+
+/* ===========================================================================
+ * Flush the current block, with given end-of-file flag.
+ * IN assertion: strstart is set to the end of the current match.
+ */
+#define FLUSH_BLOCK_ONLY(s, flush) { \
+ ct_flush_block(s, (s->block_start >= 0L ? \
+ (charf *)&s->window[(unsigned)s->block_start] : \
+ (charf *)Z_NULL), (long)s->strstart - s->block_start, (flush)); \
+ s->block_start = s->strstart; \
+ flush_pending(s->strm); \
+ Tracev((stderr,"[FLUSH]")); \
+}
+
+/* Same but force premature exit if necessary. */
+#define FLUSH_BLOCK(s, flush) { \
+ FLUSH_BLOCK_ONLY(s, flush); \
+ if (s->strm->avail_out == 0) return 1; \
+}
+
+/* ===========================================================================
+ * Compress as much as possible from the input stream, return true if
+ * processing was terminated prematurely (no more input or output space).
+ * This function does not perform lazy evaluationof matches and inserts
+ * new strings in the dictionary only for unmatched strings or for short
+ * matches. It is used only for the fast compression options.
+ */
+local int deflate_fast(s, flush)
+ deflate_state *s;
+ int flush;
+{
+ IPos hash_head = NIL; /* head of the hash chain */
+ int bflush; /* set if current block must be flushed */
+
+ s->prev_length = MIN_MATCH-1;
+
+ for (;;) {
+ /* Make sure that we always have enough lookahead, except
+ * at the end of the input file. We need MAX_MATCH bytes
+ * for the next match, plus MIN_MATCH bytes to insert the
+ * string following the next match.
+ */
+ if (s->lookahead < MIN_LOOKAHEAD) {
+ fill_window(s);
+ if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) return 1;
+
+ if (s->lookahead == 0) break; /* flush the current block */
+ }
+
+ /* Insert the string window[strstart .. strstart+2] in the
+ * dictionary, and set hash_head to the head of the hash chain:
+ */
+ if (s->lookahead >= MIN_MATCH) {
+ INSERT_STRING(s, s->strstart, hash_head);
+ }
+
+ /* Find the longest match, discarding those <= prev_length.
+ * At this point we have always match_length < MIN_MATCH
+ */
+ if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) {
+ /* To simplify the code, we prevent matches with the string
+ * of window index 0 (in particular we have to avoid a match
+ * of the string with itself at the start of the input file).
+ */
+ if (s->strategy != Z_HUFFMAN_ONLY) {
+ s->match_length = longest_match (s, hash_head);
+ }
+ /* longest_match() sets match_start */
+
+ if (s->match_length > s->lookahead) s->match_length = s->lookahead;
+ }
+ if (s->match_length >= MIN_MATCH) {
+ check_match(s, s->strstart, s->match_start, s->match_length);
+
+ bflush = ct_tally(s, s->strstart - s->match_start,
+ s->match_length - MIN_MATCH);
+
+ s->lookahead -= s->match_length;
+
+ /* Insert new strings in the hash table only if the match length
+ * is not too large. This saves time but degrades compression.
+ */
+ if (s->match_length <= s->max_insert_length &&
+ s->lookahead >= MIN_MATCH) {
+ s->match_length--; /* string at strstart already in hash table */
+ do {
+ s->strstart++;
+ INSERT_STRING(s, s->strstart, hash_head);
+ /* strstart never exceeds WSIZE-MAX_MATCH, so there are
+ * always MIN_MATCH bytes ahead.
+ */
+ } while (--s->match_length != 0);
+ s->strstart++;
+ } else {
+ s->strstart += s->match_length;
+ s->match_length = 0;
+ s->ins_h = s->window[s->strstart];
+ UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]);
+#if MIN_MATCH != 3
+ Call UPDATE_HASH() MIN_MATCH-3 more times
+#endif
+ /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not
+ * matter since it will be recomputed at next deflate call.
+ */
+ }
+ } else {
+ /* No match, output a literal byte */
+ Tracevv((stderr,"%c", s->window[s->strstart]));
+ bflush = ct_tally (s, 0, s->window[s->strstart]);
+ s->lookahead--;
+ s->strstart++;
+ }
+ if (bflush) FLUSH_BLOCK(s, Z_NO_FLUSH);
+ }
+ FLUSH_BLOCK(s, flush);
+ return 0; /* normal exit */
+}
+
+/* ===========================================================================
+ * Same as above, but achieves better compression. We use a lazy
+ * evaluation for matches: a match is finally adopted only if there is
+ * no better match at the next window position.
+ */
+local int deflate_slow(s, flush)
+ deflate_state *s;
+ int flush;
+{
+ IPos hash_head = NIL; /* head of hash chain */
+ int bflush; /* set if current block must be flushed */
+
+ /* Process the input block. */
+ for (;;) {
+ /* Make sure that we always have enough lookahead, except
+ * at the end of the input file. We need MAX_MATCH bytes
+ * for the next match, plus MIN_MATCH bytes to insert the
+ * string following the next match.
+ */
+ if (s->lookahead < MIN_LOOKAHEAD) {
+ fill_window(s);
+ if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) return 1;
+
+ if (s->lookahead == 0) break; /* flush the current block */
+ }
+
+ /* Insert the string window[strstart .. strstart+2] in the
+ * dictionary, and set hash_head to the head of the hash chain:
+ */
+ if (s->lookahead >= MIN_MATCH) {
+ INSERT_STRING(s, s->strstart, hash_head);
+ }
+
+ /* Find the longest match, discarding those <= prev_length.
+ */
+ s->prev_length = s->match_length, s->prev_match = s->match_start;
+ s->match_length = MIN_MATCH-1;
+
+ if (hash_head != NIL && s->prev_length < s->max_lazy_match &&
+ s->strstart - hash_head <= MAX_DIST(s)) {
+ /* To simplify the code, we prevent matches with the string
+ * of window index 0 (in particular we have to avoid a match
+ * of the string with itself at the start of the input file).
+ */
+ if (s->strategy != Z_HUFFMAN_ONLY) {
+ s->match_length = longest_match (s, hash_head);
+ }
+ /* longest_match() sets match_start */
+ if (s->match_length > s->lookahead) s->match_length = s->lookahead;
+
+ if (s->match_length <= 5 && (s->strategy == Z_FILTERED ||
+ (s->match_length == MIN_MATCH &&
+ s->strstart - s->match_start > TOO_FAR))) {
+
+ /* If prev_match is also MIN_MATCH, match_start is garbage
+ * but we will ignore the current match anyway.
+ */
+ s->match_length = MIN_MATCH-1;
+ }
+ }
+ /* If there was a match at the previous step and the current
+ * match is not better, output the previous match:
+ */
+ if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) {
+ uInt max_insert = s->strstart + s->lookahead - MIN_MATCH;
+ /* Do not insert strings in hash table beyond this. */
+
+ check_match(s, s->strstart-1, s->prev_match, s->prev_length);
+
+ bflush = ct_tally(s, s->strstart -1 - s->prev_match,
+ s->prev_length - MIN_MATCH);
+
+ /* Insert in hash table all strings up to the end of the match.
+ * strstart-1 and strstart are already inserted. If there is not
+ * enough lookahead, the last two strings are not inserted in
+ * the hash table.
+ */
+ s->lookahead -= s->prev_length-1;
+ s->prev_length -= 2;
+ do {
+ if (++s->strstart <= max_insert) {
+ INSERT_STRING(s, s->strstart, hash_head);
+ }
+ } while (--s->prev_length != 0);
+ s->match_available = 0;
+ s->match_length = MIN_MATCH-1;
+ s->strstart++;
+
+ if (bflush) FLUSH_BLOCK(s, Z_NO_FLUSH);
+
+ } else if (s->match_available) {
+ /* If there was no match at the previous position, output a
+ * single literal. If there was a match but the current match
+ * is longer, truncate the previous match to a single literal.
+ */
+ Tracevv((stderr,"%c", s->window[s->strstart-1]));
+ if (ct_tally (s, 0, s->window[s->strstart-1])) {
+ FLUSH_BLOCK_ONLY(s, Z_NO_FLUSH);
+ }
+ s->strstart++;
+ s->lookahead--;
+ if (s->strm->avail_out == 0) return 1;
+ } else {
+ /* There is no previous match to compare with, wait for
+ * the next step to decide.
+ */
+ s->match_available = 1;
+ s->strstart++;
+ s->lookahead--;
+ }
+ }
+ Assert (flush != Z_NO_FLUSH, "no flush?");
+ if (s->match_available) {
+ Tracevv((stderr,"%c", s->window[s->strstart-1]));
+ ct_tally (s, 0, s->window[s->strstart-1]);
+ s->match_available = 0;
+ }
+ FLUSH_BLOCK(s, flush);
+ return 0;
+}
+
+
+/*+++++*/
+/* trees.c -- output deflated data using Huffman coding
+ * Copyright (C) 1995 Jean-loup Gailly
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/*
+ * ALGORITHM
+ *
+ * The "deflation" process uses several Huffman trees. The more
+ * common source values are represented by shorter bit sequences.
+ *
+ * Each code tree is stored in a compressed form which is itself
+ * a Huffman encoding of the lengths of all the code strings (in
+ * ascending order by source values). The actual code strings are
+ * reconstructed from the lengths in the inflate process, as described
+ * in the deflate specification.
+ *
+ * REFERENCES
+ *
+ * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification".
+ * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc
+ *
+ * Storer, James A.
+ * Data Compression: Methods and Theory, pp. 49-50.
+ * Computer Science Press, 1988. ISBN 0-7167-8156-5.
+ *
+ * Sedgewick, R.
+ * Algorithms, p290.
+ * Addison-Wesley, 1983. ISBN 0-201-06672-6.
+ */
+
+/* From: trees.c,v 1.5 1995/05/03 17:27:12 jloup Exp */
+
+#ifdef DEBUG_ZLIB
+# include <ctype.h>
+#endif
+
+/* ===========================================================================
+ * Constants
+ */
+
+#define MAX_BL_BITS 7
+/* Bit length codes must not exceed MAX_BL_BITS bits */
+
+#define END_BLOCK 256
+/* end of block literal code */
+
+#define REP_3_6 16
+/* repeat previous bit length 3-6 times (2 bits of repeat count) */
+
+#define REPZ_3_10 17
+/* repeat a zero length 3-10 times (3 bits of repeat count) */
+
+#define REPZ_11_138 18
+/* repeat a zero length 11-138 times (7 bits of repeat count) */
+
+local int extra_lbits[LENGTH_CODES] /* extra bits for each length code */
+ = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0};
+
+local int extra_dbits[D_CODES] /* extra bits for each distance code */
+ = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13};
+
+local int extra_blbits[BL_CODES]/* extra bits for each bit length code */
+ = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7};
+
+local uch bl_order[BL_CODES]
+ = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15};
+/* The lengths of the bit length codes are sent in order of decreasing
+ * probability, to avoid transmitting the lengths for unused bit length codes.
+ */
+
+#define Buf_size (8 * 2*sizeof(char))
+/* Number of bits used within bi_buf. (bi_buf might be implemented on
+ * more than 16 bits on some systems.)
+ */
+
+/* ===========================================================================
+ * Local data. These are initialized only once.
+ * To do: initialize at compile time to be completely reentrant. ???
+ */
+
+local ct_data static_ltree[L_CODES+2];
+/* The static literal tree. Since the bit lengths are imposed, there is no
+ * need for the L_CODES extra codes used during heap construction. However
+ * The codes 286 and 287 are needed to build a canonical tree (see ct_init
+ * below).
+ */
+
+local ct_data static_dtree[D_CODES];
+/* The static distance tree. (Actually a trivial tree since all codes use
+ * 5 bits.)
+ */
+
+local uch dist_code[512];
+/* distance codes. The first 256 values correspond to the distances
+ * 3 .. 258, the last 256 values correspond to the top 8 bits of
+ * the 15 bit distances.
+ */
+
+local uch length_code[MAX_MATCH-MIN_MATCH+1];
+/* length code for each normalized match length (0 == MIN_MATCH) */
+
+local int base_length[LENGTH_CODES];
+/* First normalized length for each code (0 = MIN_MATCH) */
+
+local int base_dist[D_CODES];
+/* First normalized distance for each code (0 = distance of 1) */
+
+struct static_tree_desc_s {
+ ct_data *static_tree; /* static tree or NULL */
+ intf *extra_bits; /* extra bits for each code or NULL */
+ int extra_base; /* base index for extra_bits */
+ int elems; /* max number of elements in the tree */
+ int max_length; /* max bit length for the codes */
+};
+
+local static_tree_desc static_l_desc =
+{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS};
+
+local static_tree_desc static_d_desc =
+{static_dtree, extra_dbits, 0, D_CODES, MAX_BITS};
+
+local static_tree_desc static_bl_desc =
+{(ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS};
+
+/* ===========================================================================
+ * Local (static) routines in this file.
+ */
+
+local void ct_static_init OF((void));
+local void init_block OF((deflate_state *s));
+local void pqdownheap OF((deflate_state *s, ct_data *tree, int k));
+local void gen_bitlen OF((deflate_state *s, tree_desc *desc));
+local void gen_codes OF((ct_data *tree, int max_code, ushf *bl_count));
+local void build_tree OF((deflate_state *s, tree_desc *desc));
+local void scan_tree OF((deflate_state *s, ct_data *tree, int max_code));
+local void send_tree OF((deflate_state *s, ct_data *tree, int max_code));
+local int build_bl_tree OF((deflate_state *s));
+local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes,
+ int blcodes));
+local void compress_block OF((deflate_state *s, ct_data *ltree,
+ ct_data *dtree));
+local void set_data_type OF((deflate_state *s));
+local unsigned bi_reverse OF((unsigned value, int length));
+local void bi_windup OF((deflate_state *s));
+local void bi_flush OF((deflate_state *s));
+local void copy_block OF((deflate_state *s, charf *buf, unsigned len,
+ int header));
+
+#ifndef DEBUG_ZLIB
+# define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len)
+ /* Send a code of the given tree. c and tree must not have side effects */
+
+#else /* DEBUG_ZLIB */
+# define send_code(s, c, tree) \
+ { if (verbose>1) fprintf(stderr,"\ncd %3d ",(c)); \
+ send_bits(s, tree[c].Code, tree[c].Len); }
+#endif
+
+#define d_code(dist) \
+ ((dist) < 256 ? dist_code[dist] : dist_code[256+((dist)>>7)])
+/* Mapping from a distance to a distance code. dist is the distance - 1 and
+ * must not have side effects. dist_code[256] and dist_code[257] are never
+ * used.
+ */
+
+/* ===========================================================================
+ * Output a short LSB first on the stream.
+ * IN assertion: there is enough room in pendingBuf.
+ */
+#define put_short(s, w) { \
+ put_byte(s, (uch)((w) & 0xff)); \
+ put_byte(s, (uch)((ush)(w) >> 8)); \
+}
+
+/* ===========================================================================
+ * Send a value on a given number of bits.
+ * IN assertion: length <= 16 and value fits in length bits.
+ */
+#ifdef DEBUG_ZLIB
+local void send_bits OF((deflate_state *s, int value, int length));
+
+local void send_bits(s, value, length)
+ deflate_state *s;
+ int value; /* value to send */
+ int length; /* number of bits */
+{
+ Tracev((stderr," l %2d v %4x ", length, value));
+ Assert(length > 0 && length <= 15, "invalid length");
+ s->bits_sent += (ulg)length;
+
+ /* If not enough room in bi_buf, use (valid) bits from bi_buf and
+ * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid))
+ * unused bits in value.
+ */
+ if (s->bi_valid > (int)Buf_size - length) {
+ s->bi_buf |= (value << s->bi_valid);
+ put_short(s, s->bi_buf);
+ s->bi_buf = (ush)value >> (Buf_size - s->bi_valid);
+ s->bi_valid += length - Buf_size;
+ } else {
+ s->bi_buf |= value << s->bi_valid;
+ s->bi_valid += length;
+ }
+}
+#else /* !DEBUG_ZLIB */
+
+#define send_bits(s, value, length) \
+{ int len = length;\
+ if (s->bi_valid > (int)Buf_size - len) {\
+ int val = value;\
+ s->bi_buf |= (val << s->bi_valid);\
+ put_short(s, s->bi_buf);\
+ s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\
+ s->bi_valid += len - Buf_size;\
+ } else {\
+ s->bi_buf |= (value) << s->bi_valid;\
+ s->bi_valid += len;\
+ }\
+}
+#endif /* DEBUG_ZLIB */
+
+
+#define MAX(a,b) (a >= b ? a : b)
+/* the arguments must not have side effects */
+
+/* ===========================================================================
+ * Initialize the various 'constant' tables.
+ * To do: do this at compile time.
+ */
+local void ct_static_init()
+{
+ int n; /* iterates over tree elements */
+ int bits; /* bit counter */
+ int length; /* length value */
+ int code; /* code value */
+ int dist; /* distance index */
+ ush bl_count[MAX_BITS+1];
+ /* number of codes at each bit length for an optimal tree */
+
+ /* Initialize the mapping length (0..255) -> length code (0..28) */
+ length = 0;
+ for (code = 0; code < LENGTH_CODES-1; code++) {
+ base_length[code] = length;
+ for (n = 0; n < (1<<extra_lbits[code]); n++) {
+ length_code[length++] = (uch)code;
+ }
+ }
+ Assert (length == 256, "ct_static_init: length != 256");
+ /* Note that the length 255 (match length 258) can be represented
+ * in two different ways: code 284 + 5 bits or code 285, so we
+ * overwrite length_code[255] to use the best encoding:
+ */
+ length_code[length-1] = (uch)code;
+
+ /* Initialize the mapping dist (0..32K) -> dist code (0..29) */
+ dist = 0;
+ for (code = 0 ; code < 16; code++) {
+ base_dist[code] = dist;
+ for (n = 0; n < (1<<extra_dbits[code]); n++) {
+ dist_code[dist++] = (uch)code;
+ }
+ }
+ Assert (dist == 256, "ct_static_init: dist != 256");
+ dist >>= 7; /* from now on, all distances are divided by 128 */
+ for ( ; code < D_CODES; code++) {
+ base_dist[code] = dist << 7;
+ for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) {
+ dist_code[256 + dist++] = (uch)code;
+ }
+ }
+ Assert (dist == 256, "ct_static_init: 256+dist != 512");
+
+ /* Construct the codes of the static literal tree */
+ for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0;
+ n = 0;
+ while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++;
+ while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++;
+ while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++;
+ while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++;
+ /* Codes 286 and 287 do not exist, but we must include them in the
+ * tree construction to get a canonical Huffman tree (longest code
+ * all ones)
+ */
+ gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count);
+
+ /* The static distance tree is trivial: */
+ for (n = 0; n < D_CODES; n++) {
+ static_dtree[n].Len = 5;
+ static_dtree[n].Code = bi_reverse(n, 5);
+ }
+}
+
+/* ===========================================================================
+ * Initialize the tree data structures for a new zlib stream.
+ */
+local void ct_init(s)
+ deflate_state *s;
+{
+ if (static_dtree[0].Len == 0) {
+ ct_static_init(); /* To do: at compile time */
+ }
+
+ s->compressed_len = 0L;
+
+ s->l_desc.dyn_tree = s->dyn_ltree;
+ s->l_desc.stat_desc = &static_l_desc;
+
+ s->d_desc.dyn_tree = s->dyn_dtree;
+ s->d_desc.stat_desc = &static_d_desc;
+
+ s->bl_desc.dyn_tree = s->bl_tree;
+ s->bl_desc.stat_desc = &static_bl_desc;
+
+ s->bi_buf = 0;
+ s->bi_valid = 0;
+ s->last_eob_len = 8; /* enough lookahead for inflate */
+#ifdef DEBUG_ZLIB
+ s->bits_sent = 0L;
+#endif
+ s->blocks_in_packet = 0;
+
+ /* Initialize the first block of the first file: */
+ init_block(s);
+}
+
+/* ===========================================================================
+ * Initialize a new block.
+ */
+local void init_block(s)
+ deflate_state *s;
+{
+ int n; /* iterates over tree elements */
+
+ /* Initialize the trees. */
+ for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0;
+ for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0;
+ for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0;
+
+ s->dyn_ltree[END_BLOCK].Freq = 1;
+ s->opt_len = s->static_len = 0L;
+ s->last_lit = s->matches = 0;
+}
+
+#define SMALLEST 1
+/* Index within the heap array of least frequent node in the Huffman tree */
+
+
+/* ===========================================================================
+ * Remove the smallest element from the heap and recreate the heap with
+ * one less element. Updates heap and heap_len.
+ */
+#define pqremove(s, tree, top) \
+{\
+ top = s->heap[SMALLEST]; \
+ s->heap[SMALLEST] = s->heap[s->heap_len--]; \
+ pqdownheap(s, tree, SMALLEST); \
+}
+
+/* ===========================================================================
+ * Compares to subtrees, using the tree depth as tie breaker when
+ * the subtrees have equal frequency. This minimizes the worst case length.
+ */
+#define smaller(tree, n, m, depth) \
+ (tree[n].Freq < tree[m].Freq || \
+ (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m]))
+
+/* ===========================================================================
+ * Restore the heap property by moving down the tree starting at node k,
+ * exchanging a node with the smallest of its two sons if necessary, stopping
+ * when the heap property is re-established (each father smaller than its
+ * two sons).
+ */
+local void pqdownheap(s, tree, k)
+ deflate_state *s;
+ ct_data *tree; /* the tree to restore */
+ int k; /* node to move down */
+{
+ int v = s->heap[k];
+ int j = k << 1; /* left son of k */
+ while (j <= s->heap_len) {
+ /* Set j to the smallest of the two sons: */
+ if (j < s->heap_len &&
+ smaller(tree, s->heap[j+1], s->heap[j], s->depth)) {
+ j++;
+ }
+ /* Exit if v is smaller than both sons */
+ if (smaller(tree, v, s->heap[j], s->depth)) break;
+
+ /* Exchange v with the smallest son */
+ s->heap[k] = s->heap[j]; k = j;
+
+ /* And continue down the tree, setting j to the left son of k */
+ j <<= 1;
+ }
+ s->heap[k] = v;
+}
+
+/* ===========================================================================
+ * Compute the optimal bit lengths for a tree and update the total bit length
+ * for the current block.
+ * IN assertion: the fields freq and dad are set, heap[heap_max] and
+ * above are the tree nodes sorted by increasing frequency.
+ * OUT assertions: the field len is set to the optimal bit length, the
+ * array bl_count contains the frequencies for each bit length.
+ * The length opt_len is updated; static_len is also updated if stree is
+ * not null.
+ */
+local void gen_bitlen(s, desc)
+ deflate_state *s;
+ tree_desc *desc; /* the tree descriptor */
+{
+ ct_data *tree = desc->dyn_tree;
+ int max_code = desc->max_code;
+ ct_data *stree = desc->stat_desc->static_tree;
+ intf *extra = desc->stat_desc->extra_bits;
+ int base = desc->stat_desc->extra_base;
+ int max_length = desc->stat_desc->max_length;
+ int h; /* heap index */
+ int n, m; /* iterate over the tree elements */
+ int bits; /* bit length */
+ int xbits; /* extra bits */
+ ush f; /* frequency */
+ int overflow = 0; /* number of elements with bit length too large */
+
+ for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0;
+
+ /* In a first pass, compute the optimal bit lengths (which may
+ * overflow in the case of the bit length tree).
+ */
+ tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */
+
+ for (h = s->heap_max+1; h < HEAP_SIZE; h++) {
+ n = s->heap[h];
+ bits = tree[tree[n].Dad].Len + 1;
+ if (bits > max_length) bits = max_length, overflow++;
+ tree[n].Len = (ush)bits;
+ /* We overwrite tree[n].Dad which is no longer needed */
+
+ if (n > max_code) continue; /* not a leaf node */
+
+ s->bl_count[bits]++;
+ xbits = 0;
+ if (n >= base) xbits = extra[n-base];
+ f = tree[n].Freq;
+ s->opt_len += (ulg)f * (bits + xbits);
+ if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits);
+ }
+ if (overflow == 0) return;
+
+ Trace((stderr,"\nbit length overflow\n"));
+ /* This happens for example on obj2 and pic of the Calgary corpus */
+
+ /* Find the first bit length which could increase: */
+ do {
+ bits = max_length-1;
+ while (s->bl_count[bits] == 0) bits--;
+ s->bl_count[bits]--; /* move one leaf down the tree */
+ s->bl_count[bits+1] += 2; /* move one overflow item as its brother */
+ s->bl_count[max_length]--;
+ /* The brother of the overflow item also moves one step up,
+ * but this does not affect bl_count[max_length]
+ */
+ overflow -= 2;
+ } while (overflow > 0);
+
+ /* Now recompute all bit lengths, scanning in increasing frequency.
+ * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all
+ * lengths instead of fixing only the wrong ones. This idea is taken
+ * from 'ar' written by Haruhiko Okumura.)
+ */
+ for (bits = max_length; bits != 0; bits--) {
+ n = s->bl_count[bits];
+ while (n != 0) {
+ m = s->heap[--h];
+ if (m > max_code) continue;
+ if (tree[m].Len != (unsigned) bits) {
+ Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits));
+ s->opt_len += ((long)bits - (long)tree[m].Len)
+ *(long)tree[m].Freq;
+ tree[m].Len = (ush)bits;
+ }
+ n--;
+ }
+ }
+}
+
+/* ===========================================================================
+ * Generate the codes for a given tree and bit counts (which need not be
+ * optimal).
+ * IN assertion: the array bl_count contains the bit length statistics for
+ * the given tree and the field len is set for all tree elements.
+ * OUT assertion: the field code is set for all tree elements of non
+ * zero code length.
+ */
+local void gen_codes (tree, max_code, bl_count)
+ ct_data *tree; /* the tree to decorate */
+ int max_code; /* largest code with non zero frequency */
+ ushf *bl_count; /* number of codes at each bit length */
+{
+ ush next_code[MAX_BITS+1]; /* next code value for each bit length */
+ ush code = 0; /* running code value */
+ int bits; /* bit index */
+ int n; /* code index */
+
+ /* The distribution counts are first used to generate the code values
+ * without bit reversal.
+ */
+ for (bits = 1; bits <= MAX_BITS; bits++) {
+ next_code[bits] = code = (code + bl_count[bits-1]) << 1;
+ }
+ /* Check that the bit counts in bl_count are consistent. The last code
+ * must be all ones.
+ */
+ Assert (code + bl_count[MAX_BITS]-1 == (1<<MAX_BITS)-1,
+ "inconsistent bit counts");
+ Tracev((stderr,"\ngen_codes: max_code %d ", max_code));
+
+ for (n = 0; n <= max_code; n++) {
+ int len = tree[n].Len;
+ if (len == 0) continue;
+ /* Now reverse the bits */
+ tree[n].Code = bi_reverse(next_code[len]++, len);
+
+ Tracec(tree != static_ltree, (stderr,"\nn %3d %c l %2d c %4x (%x) ",
+ n, (isgraph(n) ? n : ' '), len, tree[n].Code, next_code[len]-1));
+ }
+}
+
+/* ===========================================================================
+ * Construct one Huffman tree and assigns the code bit strings and lengths.
+ * Update the total bit length for the current block.
+ * IN assertion: the field freq is set for all tree elements.
+ * OUT assertions: the fields len and code are set to the optimal bit length
+ * and corresponding code. The length opt_len is updated; static_len is
+ * also updated if stree is not null. The field max_code is set.
+ */
+local void build_tree(s, desc)
+ deflate_state *s;
+ tree_desc *desc; /* the tree descriptor */
+{
+ ct_data *tree = desc->dyn_tree;
+ ct_data *stree = desc->stat_desc->static_tree;
+ int elems = desc->stat_desc->elems;
+ int n, m; /* iterate over heap elements */
+ int max_code = -1; /* largest code with non zero frequency */
+ int node; /* new node being created */
+
+ /* Construct the initial heap, with least frequent element in
+ * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1].
+ * heap[0] is not used.
+ */
+ s->heap_len = 0, s->heap_max = HEAP_SIZE;
+
+ for (n = 0; n < elems; n++) {
+ if (tree[n].Freq != 0) {
+ s->heap[++(s->heap_len)] = max_code = n;
+ s->depth[n] = 0;
+ } else {
+ tree[n].Len = 0;
+ }
+ }
+
+ /* The pkzip format requires that at least one distance code exists,
+ * and that at least one bit should be sent even if there is only one
+ * possible code. So to avoid special checks later on we force at least
+ * two codes of non zero frequency.
+ */
+ while (s->heap_len < 2) {
+ node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0);
+ tree[node].Freq = 1;
+ s->depth[node] = 0;
+ s->opt_len--; if (stree) s->static_len -= stree[node].Len;
+ /* node is 0 or 1 so it does not have extra bits */
+ }
+ desc->max_code = max_code;
+
+ /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree,
+ * establish sub-heaps of increasing lengths:
+ */
+ for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n);
+
+ /* Construct the Huffman tree by repeatedly combining the least two
+ * frequent nodes.
+ */
+ node = elems; /* next internal node of the tree */
+ do {
+ pqremove(s, tree, n); /* n = node of least frequency */
+ m = s->heap[SMALLEST]; /* m = node of next least frequency */
+
+ s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */
+ s->heap[--(s->heap_max)] = m;
+
+ /* Create a new node father of n and m */
+ tree[node].Freq = tree[n].Freq + tree[m].Freq;
+ s->depth[node] = (uch) (MAX(s->depth[n], s->depth[m]) + 1);
+ tree[n].Dad = tree[m].Dad = (ush)node;
+#ifdef DUMP_BL_TREE
+ if (tree == s->bl_tree) {
+ fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)",
+ node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq);
+ }
+#endif
+ /* and insert the new node in the heap */
+ s->heap[SMALLEST] = node++;
+ pqdownheap(s, tree, SMALLEST);
+
+ } while (s->heap_len >= 2);
+
+ s->heap[--(s->heap_max)] = s->heap[SMALLEST];
+
+ /* At this point, the fields freq and dad are set. We can now
+ * generate the bit lengths.
+ */
+ gen_bitlen(s, (tree_desc *)desc);
+
+ /* The field len is now set, we can generate the bit codes */
+ gen_codes ((ct_data *)tree, max_code, s->bl_count);
+}
+
+/* ===========================================================================
+ * Scan a literal or distance tree to determine the frequencies of the codes
+ * in the bit length tree.
+ */
+local void scan_tree (s, tree, max_code)
+ deflate_state *s;
+ ct_data *tree; /* the tree to be scanned */
+ int max_code; /* and its largest code of non zero frequency */
+{
+ int n; /* iterates over all tree elements */
+ int prevlen = -1; /* last emitted length */
+ int curlen; /* length of current code */
+ int nextlen = tree[0].Len; /* length of next code */
+ int count = 0; /* repeat count of the current code */
+ int max_count = 7; /* max repeat count */
+ int min_count = 4; /* min repeat count */
+
+ if (nextlen == 0) max_count = 138, min_count = 3;
+ tree[max_code+1].Len = (ush)0xffff; /* guard */
+
+ for (n = 0; n <= max_code; n++) {
+ curlen = nextlen; nextlen = tree[n+1].Len;
+ if (++count < max_count && curlen == nextlen) {
+ continue;
+ } else if (count < min_count) {
+ s->bl_tree[curlen].Freq += count;
+ } else if (curlen != 0) {
+ if (curlen != prevlen) s->bl_tree[curlen].Freq++;
+ s->bl_tree[REP_3_6].Freq++;
+ } else if (count <= 10) {
+ s->bl_tree[REPZ_3_10].Freq++;
+ } else {
+ s->bl_tree[REPZ_11_138].Freq++;
+ }
+ count = 0; prevlen = curlen;
+ if (nextlen == 0) {
+ max_count = 138, min_count = 3;
+ } else if (curlen == nextlen) {
+ max_count = 6, min_count = 3;
+ } else {
+ max_count = 7, min_count = 4;
+ }
+ }
+}
+
+/* ===========================================================================
+ * Send a literal or distance tree in compressed form, using the codes in
+ * bl_tree.
+ */
+local void send_tree (s, tree, max_code)
+ deflate_state *s;
+ ct_data *tree; /* the tree to be scanned */
+ int max_code; /* and its largest code of non zero frequency */
+{
+ int n; /* iterates over all tree elements */
+ int prevlen = -1; /* last emitted length */
+ int curlen; /* length of current code */
+ int nextlen = tree[0].Len; /* length of next code */
+ int count = 0; /* repeat count of the current code */
+ int max_count = 7; /* max repeat count */
+ int min_count = 4; /* min repeat count */
+
+ /* tree[max_code+1].Len = -1; */ /* guard already set */
+ if (nextlen == 0) max_count = 138, min_count = 3;
+
+ for (n = 0; n <= max_code; n++) {
+ curlen = nextlen; nextlen = tree[n+1].Len;
+ if (++count < max_count && curlen == nextlen) {
+ continue;
+ } else if (count < min_count) {
+ do { send_code(s, curlen, s->bl_tree); } while (--count != 0);
+
+ } else if (curlen != 0) {
+ if (curlen != prevlen) {
+ send_code(s, curlen, s->bl_tree); count--;
+ }
+ Assert(count >= 3 && count <= 6, " 3_6?");
+ send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2);
+
+ } else if (count <= 10) {
+ send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3);
+
+ } else {
+ send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7);
+ }
+ count = 0; prevlen = curlen;
+ if (nextlen == 0) {
+ max_count = 138, min_count = 3;
+ } else if (curlen == nextlen) {
+ max_count = 6, min_count = 3;
+ } else {
+ max_count = 7, min_count = 4;
+ }
+ }
+}
+
+/* ===========================================================================
+ * Construct the Huffman tree for the bit lengths and return the index in
+ * bl_order of the last bit length code to send.
+ */
+local int build_bl_tree(s)
+ deflate_state *s;
+{
+ int max_blindex; /* index of last bit length code of non zero freq */
+
+ /* Determine the bit length frequencies for literal and distance trees */
+ scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code);
+ scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code);
+
+ /* Build the bit length tree: */
+ build_tree(s, (tree_desc *)(&(s->bl_desc)));
+ /* opt_len now includes the length of the tree representations, except
+ * the lengths of the bit lengths codes and the 5+5+4 bits for the counts.
+ */
+
+ /* Determine the number of bit length codes to send. The pkzip format
+ * requires that at least 4 bit length codes be sent. (appnote.txt says
+ * 3 but the actual value used is 4.)
+ */
+ for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) {
+ if (s->bl_tree[bl_order[max_blindex]].Len != 0) break;
+ }
+ /* Update opt_len to include the bit length tree and counts */
+ s->opt_len += 3*(max_blindex+1) + 5+5+4;
+ Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld",
+ s->opt_len, s->static_len));
+
+ return max_blindex;
+}
+
+/* ===========================================================================
+ * Send the header for a block using dynamic Huffman trees: the counts, the
+ * lengths of the bit length codes, the literal tree and the distance tree.
+ * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4.
+ */
+local void send_all_trees(s, lcodes, dcodes, blcodes)
+ deflate_state *s;
+ int lcodes, dcodes, blcodes; /* number of codes for each tree */
+{
+ int rank; /* index in bl_order */
+
+ Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes");
+ Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES,
+ "too many codes");
+ Tracev((stderr, "\nbl counts: "));
+ send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */
+ send_bits(s, dcodes-1, 5);
+ send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */
+ for (rank = 0; rank < blcodes; rank++) {
+ Tracev((stderr, "\nbl code %2d ", bl_order[rank]));
+ send_bits(s, s->bl_tree[bl_order[rank]].Len, 3);
+ }
+ Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent));
+
+ send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */
+ Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent));
+
+ send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */
+ Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent));
+}
+
+/* ===========================================================================
+ * Send a stored block
+ */
+local void ct_stored_block(s, buf, stored_len, eof)
+ deflate_state *s;
+ charf *buf; /* input block */
+ ulg stored_len; /* length of input block */
+ int eof; /* true if this is the last block for a file */
+{
+ send_bits(s, (STORED_BLOCK<<1)+eof, 3); /* send block type */
+ s->compressed_len = (s->compressed_len + 3 + 7) & ~7L;
+ s->compressed_len += (stored_len + 4) << 3;
+
+ copy_block(s, buf, (unsigned)stored_len, 1); /* with header */
+}
+
+/* Send just the `stored block' type code without any length bytes or data.
+ */
+local void ct_stored_type_only(s)
+ deflate_state *s;
+{
+ send_bits(s, (STORED_BLOCK << 1), 3);
+ bi_windup(s);
+ s->compressed_len = (s->compressed_len + 3) & ~7L;
+}
+
+
+/* ===========================================================================
+ * Send one empty static block to give enough lookahead for inflate.
+ * This takes 10 bits, of which 7 may remain in the bit buffer.
+ * The current inflate code requires 9 bits of lookahead. If the EOB
+ * code for the previous block was coded on 5 bits or less, inflate
+ * may have only 5+3 bits of lookahead to decode this EOB.
+ * (There are no problems if the previous block is stored or fixed.)
+ */
+local void ct_align(s)
+ deflate_state *s;
+{
+ send_bits(s, STATIC_TREES<<1, 3);
+ send_code(s, END_BLOCK, static_ltree);
+ s->compressed_len += 10L; /* 3 for block type, 7 for EOB */
+ bi_flush(s);
+ /* Of the 10 bits for the empty block, we have already sent
+ * (10 - bi_valid) bits. The lookahead for the EOB of the previous
+ * block was thus its length plus what we have just sent.
+ */
+ if (s->last_eob_len + 10 - s->bi_valid < 9) {
+ send_bits(s, STATIC_TREES<<1, 3);
+ send_code(s, END_BLOCK, static_ltree);
+ s->compressed_len += 10L;
+ bi_flush(s);
+ }
+ s->last_eob_len = 7;
+}
+
+/* ===========================================================================
+ * Determine the best encoding for the current block: dynamic trees, static
+ * trees or store, and output the encoded block to the zip file. This function
+ * returns the total compressed length for the file so far.
+ */
+local ulg ct_flush_block(s, buf, stored_len, flush)
+ deflate_state *s;
+ charf *buf; /* input block, or NULL if too old */
+ ulg stored_len; /* length of input block */
+ int flush; /* Z_FINISH if this is the last block for a file */
+{
+ ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */
+ int max_blindex; /* index of last bit length code of non zero freq */
+ int eof = flush == Z_FINISH;
+
+ ++s->blocks_in_packet;
+
+ /* Check if the file is ascii or binary */
+ if (s->data_type == UNKNOWN) set_data_type(s);
+
+ /* Construct the literal and distance trees */
+ build_tree(s, (tree_desc *)(&(s->l_desc)));
+ Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len,
+ s->static_len));
+
+ build_tree(s, (tree_desc *)(&(s->d_desc)));
+ Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len,
+ s->static_len));
+ /* At this point, opt_len and static_len are the total bit lengths of
+ * the compressed block data, excluding the tree representations.
+ */
+
+ /* Build the bit length tree for the above two trees, and get the index
+ * in bl_order of the last bit length code to send.
+ */
+ max_blindex = build_bl_tree(s);
+
+ /* Determine the best encoding. Compute first the block length in bytes */
+ opt_lenb = (s->opt_len+3+7)>>3;
+ static_lenb = (s->static_len+3+7)>>3;
+
+ Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ",
+ opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len,
+ s->last_lit));
+
+ if (static_lenb <= opt_lenb) opt_lenb = static_lenb;
+
+ /* If compression failed and this is the first and last block,
+ * and if the .zip file can be seeked (to rewrite the local header),
+ * the whole file is transformed into a stored file:
+ */
+#ifdef STORED_FILE_OK
+# ifdef FORCE_STORED_FILE
+ if (eof && compressed_len == 0L) /* force stored file */
+# else
+ if (stored_len <= opt_lenb && eof && s->compressed_len==0L && seekable())
+# endif
+ {
+ /* Since LIT_BUFSIZE <= 2*WSIZE, the input data must be there: */
+ if (buf == (charf*)0) error ("block vanished");
+
+ copy_block(buf, (unsigned)stored_len, 0); /* without header */
+ s->compressed_len = stored_len << 3;
+ s->method = STORED;
+ } else
+#endif /* STORED_FILE_OK */
+
+ /* For Z_PACKET_FLUSH, if we don't achieve the required minimum
+ * compression, and this block contains all the data since the last
+ * time we used Z_PACKET_FLUSH, then just omit this block completely
+ * from the output.
+ */
+ if (flush == Z_PACKET_FLUSH && s->blocks_in_packet == 1
+ && opt_lenb > stored_len - s->minCompr) {
+ s->blocks_in_packet = 0;
+ /* output nothing */
+ } else
+
+#ifdef FORCE_STORED
+ if (buf != (char*)0) /* force stored block */
+#else
+ if (stored_len+4 <= opt_lenb && buf != (char*)0)
+ /* 4: two words for the lengths */
+#endif
+ {
+ /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE.
+ * Otherwise we can't have processed more than WSIZE input bytes since
+ * the last block flush, because compression would have been
+ * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to
+ * transform a block into a stored block.
+ */
+ ct_stored_block(s, buf, stored_len, eof);
+ } else
+
+#ifdef FORCE_STATIC
+ if (static_lenb >= 0) /* force static trees */
+#else
+ if (static_lenb == opt_lenb)
+#endif
+ {
+ send_bits(s, (STATIC_TREES<<1)+eof, 3);
+ compress_block(s, (ct_data *)static_ltree, (ct_data *)static_dtree);
+ s->compressed_len += 3 + s->static_len;
+ } else {
+ send_bits(s, (DYN_TREES<<1)+eof, 3);
+ send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1,
+ max_blindex+1);
+ compress_block(s, (ct_data *)s->dyn_ltree, (ct_data *)s->dyn_dtree);
+ s->compressed_len += 3 + s->opt_len;
+ }
+ Assert (s->compressed_len == s->bits_sent, "bad compressed size");
+ init_block(s);
+
+ if (eof) {
+ bi_windup(s);
+ s->compressed_len += 7; /* align on byte boundary */
+ }
+ Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3,
+ s->compressed_len-7*eof));
+
+ return s->compressed_len >> 3;
+}
+
+/* ===========================================================================
+ * Save the match info and tally the frequency counts. Return true if
+ * the current block must be flushed.
+ */
+local int ct_tally (s, dist, lc)
+ deflate_state *s;
+ int dist; /* distance of matched string */
+ int lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */
+{
+ s->d_buf[s->last_lit] = (ush)dist;
+ s->l_buf[s->last_lit++] = (uch)lc;
+ if (dist == 0) {
+ /* lc is the unmatched char */
+ s->dyn_ltree[lc].Freq++;
+ } else {
+ s->matches++;
+ /* Here, lc is the match length - MIN_MATCH */
+ dist--; /* dist = match distance - 1 */
+ Assert((ush)dist < (ush)MAX_DIST(s) &&
+ (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) &&
+ (ush)d_code(dist) < (ush)D_CODES, "ct_tally: bad match");
+
+ s->dyn_ltree[length_code[lc]+LITERALS+1].Freq++;
+ s->dyn_dtree[d_code(dist)].Freq++;
+ }
+
+ /* Try to guess if it is profitable to stop the current block here */
+ if (s->level > 2 && (s->last_lit & 0xfff) == 0) {
+ /* Compute an upper bound for the compressed length */
+ ulg out_length = (ulg)s->last_lit*8L;
+ ulg in_length = (ulg)s->strstart - s->block_start;
+ int dcode;
+ for (dcode = 0; dcode < D_CODES; dcode++) {
+ out_length += (ulg)s->dyn_dtree[dcode].Freq *
+ (5L+extra_dbits[dcode]);
+ }
+ out_length >>= 3;
+ Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ",
+ s->last_lit, in_length, out_length,
+ 100L - out_length*100L/in_length));
+ if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1;
+ }
+ return (s->last_lit == s->lit_bufsize-1);
+ /* We avoid equality with lit_bufsize because of wraparound at 64K
+ * on 16 bit machines and because stored blocks are restricted to
+ * 64K-1 bytes.
+ */
+}
+
+/* ===========================================================================
+ * Send the block data compressed using the given Huffman trees
+ */
+local void compress_block(s, ltree, dtree)
+ deflate_state *s;
+ ct_data *ltree; /* literal tree */
+ ct_data *dtree; /* distance tree */
+{
+ unsigned dist; /* distance of matched string */
+ int lc; /* match length or unmatched char (if dist == 0) */
+ unsigned lx = 0; /* running index in l_buf */
+ unsigned code; /* the code to send */
+ int extra; /* number of extra bits to send */
+
+ if (s->last_lit != 0) do {
+ dist = s->d_buf[lx];
+ lc = s->l_buf[lx++];
+ if (dist == 0) {
+ send_code(s, lc, ltree); /* send a literal byte */
+ Tracecv(isgraph(lc), (stderr," '%c' ", lc));
+ } else {
+ /* Here, lc is the match length - MIN_MATCH */
+ code = length_code[lc];
+ send_code(s, code+LITERALS+1, ltree); /* send the length code */
+ extra = extra_lbits[code];
+ if (extra != 0) {
+ lc -= base_length[code];
+ send_bits(s, lc, extra); /* send the extra length bits */
+ }
+ dist--; /* dist is now the match distance - 1 */
+ code = d_code(dist);
+ Assert (code < D_CODES, "bad d_code");
+
+ send_code(s, code, dtree); /* send the distance code */
+ extra = extra_dbits[code];
+ if (extra != 0) {
+ dist -= base_dist[code];
+ send_bits(s, dist, extra); /* send the extra distance bits */
+ }
+ } /* literal or match pair ? */
+
+ /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */
+ Assert(s->pending < s->lit_bufsize + 2*lx, "pendingBuf overflow");
+
+ } while (lx < s->last_lit);
+
+ send_code(s, END_BLOCK, ltree);
+ s->last_eob_len = ltree[END_BLOCK].Len;
+}
+
+/* ===========================================================================
+ * Set the data type to ASCII or BINARY, using a crude approximation:
+ * binary if more than 20% of the bytes are <= 6 or >= 128, ascii otherwise.
+ * IN assertion: the fields freq of dyn_ltree are set and the total of all
+ * frequencies does not exceed 64K (to fit in an int on 16 bit machines).
+ */
+local void set_data_type(s)
+ deflate_state *s;
+{
+ int n = 0;
+ unsigned ascii_freq = 0;
+ unsigned bin_freq = 0;
+ while (n < 7) bin_freq += s->dyn_ltree[n++].Freq;
+ while (n < 128) ascii_freq += s->dyn_ltree[n++].Freq;
+ while (n < LITERALS) bin_freq += s->dyn_ltree[n++].Freq;
+ s->data_type = (Byte)(bin_freq > (ascii_freq >> 2) ? BINARY : ASCII);
+}
+
+/* ===========================================================================
+ * Reverse the first len bits of a code, using straightforward code (a faster
+ * method would use a table)
+ * IN assertion: 1 <= len <= 15
+ */
+local unsigned bi_reverse(code, len)
+ unsigned code; /* the value to invert */
+ int len; /* its bit length */
+{
+ register unsigned res = 0;
+ do {
+ res |= code & 1;
+ code >>= 1, res <<= 1;
+ } while (--len > 0);
+ return res >> 1;
+}
+
+/* ===========================================================================
+ * Flush the bit buffer, keeping at most 7 bits in it.
+ */
+local void bi_flush(s)
+ deflate_state *s;
+{
+ if (s->bi_valid == 16) {
+ put_short(s, s->bi_buf);
+ s->bi_buf = 0;
+ s->bi_valid = 0;
+ } else if (s->bi_valid >= 8) {
+ put_byte(s, (Byte)s->bi_buf);
+ s->bi_buf >>= 8;
+ s->bi_valid -= 8;
+ }
+}
+
+/* ===========================================================================
+ * Flush the bit buffer and align the output on a byte boundary
+ */
+local void bi_windup(s)
+ deflate_state *s;
+{
+ if (s->bi_valid > 8) {
+ put_short(s, s->bi_buf);
+ } else if (s->bi_valid > 0) {
+ put_byte(s, (Byte)s->bi_buf);
+ }
+ s->bi_buf = 0;
+ s->bi_valid = 0;
+#ifdef DEBUG_ZLIB
+ s->bits_sent = (s->bits_sent+7) & ~7;
+#endif
+}
+
+/* ===========================================================================
+ * Copy a stored block, storing first the length and its
+ * one's complement if requested.
+ */
+local void copy_block(s, buf, len, header)
+ deflate_state *s;
+ charf *buf; /* the input data */
+ unsigned len; /* its length */
+ int header; /* true if block header must be written */
+{
+ bi_windup(s); /* align on byte boundary */
+ s->last_eob_len = 8; /* enough lookahead for inflate */
+
+ if (header) {
+ put_short(s, (ush)len);
+ put_short(s, (ush)~len);
+#ifdef DEBUG_ZLIB
+ s->bits_sent += 2*16;
+#endif
+ }
+#ifdef DEBUG_ZLIB
+ s->bits_sent += (ulg)len<<3;
+#endif
+ while (len--) {
+ put_byte(s, *buf++);
+ }
+}
+
+
+/*+++++*/
+/* infblock.h -- header to use infblock.c
+ * Copyright (C) 1995 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+struct inflate_blocks_state;
+typedef struct inflate_blocks_state FAR inflate_blocks_statef;
+
+local inflate_blocks_statef * inflate_blocks_new OF((
+ z_stream *z,
+ check_func c, /* check function */
+ uInt w)); /* window size */
+
+local int inflate_blocks OF((
+ inflate_blocks_statef *,
+ z_stream *,
+ int)); /* initial return code */
+
+local void inflate_blocks_reset OF((
+ inflate_blocks_statef *,
+ z_stream *,
+ uLongf *)); /* check value on output */
+
+local int inflate_blocks_free OF((
+ inflate_blocks_statef *,
+ z_stream *,
+ uLongf *)); /* check value on output */
+
+local int inflate_addhistory OF((
+ inflate_blocks_statef *,
+ z_stream *));
+
+local int inflate_packet_flush OF((
+ inflate_blocks_statef *));
+
+/*+++++*/
+/* inftrees.h -- header to use inftrees.c
+ * Copyright (C) 1995 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+/* Huffman code lookup table entry--this entry is four bytes for machines
+ that have 16-bit pointers (e.g. PC's in the small or medium model). */
+
+typedef struct inflate_huft_s FAR inflate_huft;
+
+struct inflate_huft_s {
+ union {
+ struct {
+ Byte Exop; /* number of extra bits or operation */
+ Byte Bits; /* number of bits in this code or subcode */
+ } what;
+ uInt Nalloc; /* number of these allocated here */
+ Bytef *pad; /* pad structure to a power of 2 (4 bytes for */
+ } word; /* 16-bit, 8 bytes for 32-bit machines) */
+ union {
+ uInt Base; /* literal, length base, or distance base */
+ inflate_huft *Next; /* pointer to next level of table */
+ } more;
+};
+
+#ifdef DEBUG_ZLIB
+ local uInt inflate_hufts;
+#endif
+
+local int inflate_trees_bits OF((
+ uIntf *, /* 19 code lengths */
+ uIntf *, /* bits tree desired/actual depth */
+ inflate_huft * FAR *, /* bits tree result */
+ z_stream *)); /* for zalloc, zfree functions */
+
+local int inflate_trees_dynamic OF((
+ uInt, /* number of literal/length codes */
+ uInt, /* number of distance codes */
+ uIntf *, /* that many (total) code lengths */
+ uIntf *, /* literal desired/actual bit depth */
+ uIntf *, /* distance desired/actual bit depth */
+ inflate_huft * FAR *, /* literal/length tree result */
+ inflate_huft * FAR *, /* distance tree result */
+ z_stream *)); /* for zalloc, zfree functions */
+
+local int inflate_trees_fixed OF((
+ uIntf *, /* literal desired/actual bit depth */
+ uIntf *, /* distance desired/actual bit depth */
+ inflate_huft * FAR *, /* literal/length tree result */
+ inflate_huft * FAR *)); /* distance tree result */
+
+local int inflate_trees_free OF((
+ inflate_huft *, /* tables to free */
+ z_stream *)); /* for zfree function */
+
+
+/*+++++*/
+/* infcodes.h -- header to use infcodes.c
+ * Copyright (C) 1995 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+struct inflate_codes_state;
+typedef struct inflate_codes_state FAR inflate_codes_statef;
+
+local inflate_codes_statef *inflate_codes_new OF((
+ uInt, uInt,
+ inflate_huft *, inflate_huft *,
+ z_stream *));
+
+local int inflate_codes OF((
+ inflate_blocks_statef *,
+ z_stream *,
+ int));
+
+local void inflate_codes_free OF((
+ inflate_codes_statef *,
+ z_stream *));
+
+
+/*+++++*/
+/* inflate.c -- zlib interface to inflate modules
+ * Copyright (C) 1995 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* inflate private state */
+struct internal_state {
+
+ /* mode */
+ enum {
+ METHOD, /* waiting for method byte */
+ FLAG, /* waiting for flag byte */
+ BLOCKS, /* decompressing blocks */
+ CHECK4, /* four check bytes to go */
+ CHECK3, /* three check bytes to go */
+ CHECK2, /* two check bytes to go */
+ CHECK1, /* one check byte to go */
+ DONE, /* finished check, done */
+ BAD} /* got an error--stay here */
+ mode; /* current inflate mode */
+
+ /* mode dependent information */
+ union {
+ uInt method; /* if FLAGS, method byte */
+ struct {
+ uLong was; /* computed check value */
+ uLong need; /* stream check value */
+ } check; /* if CHECK, check values to compare */
+ uInt marker; /* if BAD, inflateSync's marker bytes count */
+ } sub; /* submode */
+
+ /* mode independent information */
+ int nowrap; /* flag for no wrapper */
+ uInt wbits; /* log2(window size) (8..15, defaults to 15) */
+ inflate_blocks_statef
+ *blocks; /* current inflate_blocks state */
+
+};
+
+
+int inflateReset(z)
+z_stream *z;
+{
+ uLong c;
+
+ if (z == Z_NULL || z->state == Z_NULL)
+ return Z_STREAM_ERROR;
+ z->total_in = z->total_out = 0;
+ z->msg = Z_NULL;
+ z->state->mode = z->state->nowrap ? BLOCKS : METHOD;
+ inflate_blocks_reset(z->state->blocks, z, &c);
+ Trace((stderr, "inflate: reset\n"));
+ return Z_OK;
+}
+
+
+int inflateEnd(z)
+z_stream *z;
+{
+ uLong c;
+
+ if (z == Z_NULL || z->state == Z_NULL || z->zfree == Z_NULL)
+ return Z_STREAM_ERROR;
+ if (z->state->blocks != Z_NULL)
+ inflate_blocks_free(z->state->blocks, z, &c);
+ ZFREE(z, z->state, sizeof(struct internal_state));
+ z->state = Z_NULL;
+ Trace((stderr, "inflate: end\n"));
+ return Z_OK;
+}
+
+
+int inflateInit2(z, w)
+z_stream *z;
+int w;
+{
+ /* initialize state */
+ if (z == Z_NULL)
+ return Z_STREAM_ERROR;
+/* if (z->zalloc == Z_NULL) z->zalloc = zcalloc; */
+/* if (z->zfree == Z_NULL) z->zfree = zcfree; */
+ if ((z->state = (struct internal_state FAR *)
+ ZALLOC(z,1,sizeof(struct internal_state))) == Z_NULL)
+ return Z_MEM_ERROR;
+ z->state->blocks = Z_NULL;
+
+ /* handle undocumented nowrap option (no zlib header or check) */
+ z->state->nowrap = 0;
+ if (w < 0)
+ {
+ w = - w;
+ z->state->nowrap = 1;
+ }
+
+ /* set window size */
+ if (w < 8 || w > 15)
+ {
+ inflateEnd(z);
+ return Z_STREAM_ERROR;
+ }
+ z->state->wbits = (uInt)w;
+
+ /* create inflate_blocks state */
+ if ((z->state->blocks =
+ inflate_blocks_new(z, z->state->nowrap ? Z_NULL : adler32, 1 << w))
+ == Z_NULL)
+ {
+ inflateEnd(z);
+ return Z_MEM_ERROR;
+ }
+ Trace((stderr, "inflate: allocated\n"));
+
+ /* reset state */
+ inflateReset(z);
+ return Z_OK;
+}
+
+
+int inflateInit(z)
+z_stream *z;
+{
+ return inflateInit2(z, DEF_WBITS);
+}
+
+
+#define NEEDBYTE {if(z->avail_in==0)goto empty;r=Z_OK;}
+#define NEXTBYTE (z->avail_in--,z->total_in++,*z->next_in++)
+
+int inflate(z, f)
+z_stream *z;
+int f;
+{
+ int r;
+ uInt b;
+
+ if (z == Z_NULL || z->next_in == Z_NULL)
+ return Z_STREAM_ERROR;
+ r = Z_BUF_ERROR;
+ while (1) switch (z->state->mode)
+ {
+ case METHOD:
+ NEEDBYTE
+ if (((z->state->sub.method = NEXTBYTE) & 0xf) != DEFLATED)
+ {
+ z->state->mode = BAD;
+ z->msg = "unknown compression method";
+ z->state->sub.marker = 5; /* can't try inflateSync */
+ break;
+ }
+ if ((z->state->sub.method >> 4) + 8 > z->state->wbits)
+ {
+ z->state->mode = BAD;
+ z->msg = "invalid window size";
+ z->state->sub.marker = 5; /* can't try inflateSync */
+ break;
+ }
+ z->state->mode = FLAG;
+ case FLAG:
+ NEEDBYTE
+ if ((b = NEXTBYTE) & 0x20)
+ {
+ z->state->mode = BAD;
+ z->msg = "invalid reserved bit";
+ z->state->sub.marker = 5; /* can't try inflateSync */
+ break;
+ }
+ if (((z->state->sub.method << 8) + b) % 31)
+ {
+ z->state->mode = BAD;
+ z->msg = "incorrect header check";
+ z->state->sub.marker = 5; /* can't try inflateSync */
+ break;
+ }
+ Trace((stderr, "inflate: zlib header ok\n"));
+ z->state->mode = BLOCKS;
+ case BLOCKS:
+ r = inflate_blocks(z->state->blocks, z, r);
+ if (f == Z_PACKET_FLUSH && z->avail_in == 0 && z->avail_out != 0)
+ r = inflate_packet_flush(z->state->blocks);
+ if (r == Z_DATA_ERROR)
+ {
+ z->state->mode = BAD;
+ z->state->sub.marker = 0; /* can try inflateSync */
+ break;
+ }
+ if (r != Z_STREAM_END)
+ return r;
+ r = Z_OK;
+ inflate_blocks_reset(z->state->blocks, z, &z->state->sub.check.was);
+ if (z->state->nowrap)
+ {
+ z->state->mode = DONE;
+ break;
+ }
+ z->state->mode = CHECK4;
+ case CHECK4:
+ NEEDBYTE
+ z->state->sub.check.need = (uLong)NEXTBYTE << 24;
+ z->state->mode = CHECK3;
+ case CHECK3:
+ NEEDBYTE
+ z->state->sub.check.need += (uLong)NEXTBYTE << 16;
+ z->state->mode = CHECK2;
+ case CHECK2:
+ NEEDBYTE
+ z->state->sub.check.need += (uLong)NEXTBYTE << 8;
+ z->state->mode = CHECK1;
+ case CHECK1:
+ NEEDBYTE
+ z->state->sub.check.need += (uLong)NEXTBYTE;
+
+ if (z->state->sub.check.was != z->state->sub.check.need)
+ {
+ z->state->mode = BAD;
+ z->msg = "incorrect data check";
+ z->state->sub.marker = 5; /* can't try inflateSync */
+ break;
+ }
+ Trace((stderr, "inflate: zlib check ok\n"));
+ z->state->mode = DONE;
+ case DONE:
+ return Z_STREAM_END;
+ case BAD:
+ return Z_DATA_ERROR;
+ default:
+ return Z_STREAM_ERROR;
+ }
+
+ empty:
+ if (f != Z_PACKET_FLUSH)
+ return r;
+ z->state->mode = BAD;
+ z->state->sub.marker = 0; /* can try inflateSync */
+ return Z_DATA_ERROR;
+}
+
+/*
+ * This subroutine adds the data at next_in/avail_in to the output history
+ * without performing any output. The output buffer must be "caught up";
+ * i.e. no pending output (hence s->read equals s->write), and the state must
+ * be BLOCKS (i.e. we should be willing to see the start of a series of
+ * BLOCKS). On exit, the output will also be caught up, and the checksum
+ * will have been updated if need be.
+ */
+
+int inflateIncomp(z)
+z_stream *z;
+{
+ if (z->state->mode != BLOCKS)
+ return Z_DATA_ERROR;
+ return inflate_addhistory(z->state->blocks, z);
+}
+
+
+int inflateSync(z)
+z_stream *z;
+{
+ uInt n; /* number of bytes to look at */
+ Bytef *p; /* pointer to bytes */
+ uInt m; /* number of marker bytes found in a row */
+ uLong r, w; /* temporaries to save total_in and total_out */
+
+ /* set up */
+ if (z == Z_NULL || z->state == Z_NULL)
+ return Z_STREAM_ERROR;
+ if (z->state->mode != BAD)
+ {
+ z->state->mode = BAD;
+ z->state->sub.marker = 0;
+ }
+ if ((n = z->avail_in) == 0)
+ return Z_BUF_ERROR;
+ p = z->next_in;
+ m = z->state->sub.marker;
+
+ /* search */
+ while (n && m < 4)
+ {
+ if (*p == (Byte)(m < 2 ? 0 : 0xff))
+ m++;
+ else if (*p)
+ m = 0;
+ else
+ m = 4 - m;
+ p++, n--;
+ }
+
+ /* restore */
+ z->total_in += p - z->next_in;
+ z->next_in = p;
+ z->avail_in = n;
+ z->state->sub.marker = m;
+
+ /* return no joy or set up to restart on a new block */
+ if (m != 4)
+ return Z_DATA_ERROR;
+ r = z->total_in; w = z->total_out;
+ inflateReset(z);
+ z->total_in = r; z->total_out = w;
+ z->state->mode = BLOCKS;
+ return Z_OK;
+}
+
+#undef NEEDBYTE
+#undef NEXTBYTE
+
+/*+++++*/
+/* infutil.h -- types and macros common to blocks and codes
+ * Copyright (C) 1995 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+/* inflate blocks semi-private state */
+struct inflate_blocks_state {
+
+ /* mode */
+ enum {
+ TYPE, /* get type bits (3, including end bit) */
+ LENS, /* get lengths for stored */
+ STORED, /* processing stored block */
+ TABLE, /* get table lengths */
+ BTREE, /* get bit lengths tree for a dynamic block */
+ DTREE, /* get length, distance trees for a dynamic block */
+ CODES, /* processing fixed or dynamic block */
+ DRY, /* output remaining window bytes */
+ DONEB, /* finished last block, done */
+ BADB} /* got a data error--stuck here */
+ mode; /* current inflate_block mode */
+
+ /* mode dependent information */
+ union {
+ uInt left; /* if STORED, bytes left to copy */
+ struct {
+ uInt table; /* table lengths (14 bits) */
+ uInt index; /* index into blens (or border) */
+ uIntf *blens; /* bit lengths of codes */
+ uInt bb; /* bit length tree depth */
+ inflate_huft *tb; /* bit length decoding tree */
+ int nblens; /* # elements allocated at blens */
+ } trees; /* if DTREE, decoding info for trees */
+ struct {
+ inflate_huft *tl, *td; /* trees to free */
+ inflate_codes_statef
+ *codes;
+ } decode; /* if CODES, current state */
+ } sub; /* submode */
+ uInt last; /* true if this block is the last block */
+
+ /* mode independent information */
+ uInt bitk; /* bits in bit buffer */
+ uLong bitb; /* bit buffer */
+ Bytef *window; /* sliding window */
+ Bytef *end; /* one byte after sliding window */
+ Bytef *read; /* window read pointer */
+ Bytef *write; /* window write pointer */
+ check_func checkfn; /* check function */
+ uLong check; /* check on output */
+
+};
+
+
+/* defines for inflate input/output */
+/* update pointers and return */
+#define UPDBITS {s->bitb=b;s->bitk=k;}
+#define UPDIN {z->avail_in=n;z->total_in+=p-z->next_in;z->next_in=p;}
+#define UPDOUT {s->write=q;}
+#define UPDATE {UPDBITS UPDIN UPDOUT}
+#define LEAVE {UPDATE return inflate_flush(s,z,r);}
+/* get bytes and bits */
+#define LOADIN {p=z->next_in;n=z->avail_in;b=s->bitb;k=s->bitk;}
+#define NEEDBYTE {if(n)r=Z_OK;else LEAVE}
+#define NEXTBYTE (n--,*p++)
+#define NEEDBITS(j) {while(k<(j)){NEEDBYTE;b|=((uLong)NEXTBYTE)<<k;k+=8;}}
+#define DUMPBITS(j) {b>>=(j);k-=(j);}
+/* output bytes */
+#define WAVAIL (q<s->read?s->read-q-1:s->end-q)
+#define LOADOUT {q=s->write;m=WAVAIL;}
+#define WRAP {if(q==s->end&&s->read!=s->window){q=s->window;m=WAVAIL;}}
+#define FLUSH {UPDOUT r=inflate_flush(s,z,r); LOADOUT}
+#define NEEDOUT {if(m==0){WRAP if(m==0){FLUSH WRAP if(m==0) LEAVE}}r=Z_OK;}
+#define OUTBYTE(a) {*q++=(Byte)(a);m--;}
+/* load local pointers */
+#define LOAD {LOADIN LOADOUT}
+
+/* And'ing with mask[n] masks the lower n bits */
+local uInt inflate_mask[] = {
+ 0x0000,
+ 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff,
+ 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff
+};
+
+/* copy as much as possible from the sliding window to the output area */
+local int inflate_flush OF((
+ inflate_blocks_statef *,
+ z_stream *,
+ int));
+
+/*+++++*/
+/* inffast.h -- header to use inffast.c
+ * Copyright (C) 1995 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+local int inflate_fast OF((
+ uInt,
+ uInt,
+ inflate_huft *,
+ inflate_huft *,
+ inflate_blocks_statef *,
+ z_stream *));
+
+
+/*+++++*/
+/* infblock.c -- interpret and process block types to last block
+ * Copyright (C) 1995 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* Table for deflate from PKZIP's appnote.txt. */
+local uInt border[] = { /* Order of the bit length code lengths */
+ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
+
+/*
+ Notes beyond the 1.93a appnote.txt:
+
+ 1. Distance pointers never point before the beginning of the output
+ stream.
+ 2. Distance pointers can point back across blocks, up to 32k away.
+ 3. There is an implied maximum of 7 bits for the bit length table and
+ 15 bits for the actual data.
+ 4. If only one code exists, then it is encoded using one bit. (Zero
+ would be more efficient, but perhaps a little confusing.) If two
+ codes exist, they are coded using one bit each (0 and 1).
+ 5. There is no way of sending zero distance codes--a dummy must be
+ sent if there are none. (History: a pre 2.0 version of PKZIP would
+ store blocks with no distance codes, but this was discovered to be
+ too harsh a criterion.) Valid only for 1.93a. 2.04c does allow
+ zero distance codes, which is sent as one code of zero bits in
+ length.
+ 6. There are up to 286 literal/length codes. Code 256 represents the
+ end-of-block. Note however that the static length tree defines
+ 288 codes just to fill out the Huffman codes. Codes 286 and 287
+ cannot be used though, since there is no length base or extra bits
+ defined for them. Similarily, there are up to 30 distance codes.
+ However, static trees define 32 codes (all 5 bits) to fill out the
+ Huffman codes, but the last two had better not show up in the data.
+ 7. Unzip can check dynamic Huffman blocks for complete code sets.
+ The exception is that a single code would not be complete (see #4).
+ 8. The five bits following the block type is really the number of
+ literal codes sent minus 257.
+ 9. Length codes 8,16,16 are interpreted as 13 length codes of 8 bits
+ (1+6+6). Therefore, to output three times the length, you output
+ three codes (1+1+1), whereas to output four times the same length,
+ you only need two codes (1+3). Hmm.
+ 10. In the tree reconstruction algorithm, Code = Code + Increment
+ only if BitLength(i) is not zero. (Pretty obvious.)
+ 11. Correction: 4 Bits: # of Bit Length codes - 4 (4 - 19)
+ 12. Note: length code 284 can represent 227-258, but length code 285
+ really is 258. The last length deserves its own, short code
+ since it gets used a lot in very redundant files. The length
+ 258 is special since 258 - 3 (the min match length) is 255.
+ 13. The literal/length and distance code bit lengths are read as a
+ single stream of lengths. It is possible (and advantageous) for
+ a repeat code (16, 17, or 18) to go across the boundary between
+ the two sets of lengths.
+ */
+
+
+local void inflate_blocks_reset(s, z, c)
+inflate_blocks_statef *s;
+z_stream *z;
+uLongf *c;
+{
+ if (s->checkfn != Z_NULL)
+ *c = s->check;
+ if (s->mode == BTREE || s->mode == DTREE)
+ ZFREE(z, s->sub.trees.blens, s->sub.trees.nblens * sizeof(uInt));
+ if (s->mode == CODES)
+ {
+ inflate_codes_free(s->sub.decode.codes, z);
+ inflate_trees_free(s->sub.decode.td, z);
+ inflate_trees_free(s->sub.decode.tl, z);
+ }
+ s->mode = TYPE;
+ s->bitk = 0;
+ s->bitb = 0;
+ s->read = s->write = s->window;
+ if (s->checkfn != Z_NULL)
+ s->check = (*s->checkfn)(0L, Z_NULL, 0);
+ Trace((stderr, "inflate: blocks reset\n"));
+}
+
+
+local inflate_blocks_statef *inflate_blocks_new(z, c, w)
+z_stream *z;
+check_func c;
+uInt w;
+{
+ inflate_blocks_statef *s;
+
+ if ((s = (inflate_blocks_statef *)ZALLOC
+ (z,1,sizeof(struct inflate_blocks_state))) == Z_NULL)
+ return s;
+ if ((s->window = (Bytef *)ZALLOC(z, 1, w)) == Z_NULL)
+ {
+ ZFREE(z, s, sizeof(struct inflate_blocks_state));
+ return Z_NULL;
+ }
+ s->end = s->window + w;
+ s->checkfn = c;
+ s->mode = TYPE;
+ Trace((stderr, "inflate: blocks allocated\n"));
+ inflate_blocks_reset(s, z, &s->check);
+ return s;
+}
+
+
+local int inflate_blocks(s, z, r)
+inflate_blocks_statef *s;
+z_stream *z;
+int r;
+{
+ uInt t; /* temporary storage */
+ uLong b; /* bit buffer */
+ uInt k; /* bits in bit buffer */
+ Bytef *p; /* input data pointer */
+ uInt n; /* bytes available there */
+ Bytef *q; /* output window write pointer */
+ uInt m; /* bytes to end of window or read pointer */
+
+ /* copy input/output information to locals (UPDATE macro restores) */
+ LOAD
+
+ /* process input based on current state */
+ while (1) switch (s->mode)
+ {
+ case TYPE:
+ NEEDBITS(3)
+ t = (uInt)b & 7;
+ s->last = t & 1;
+ switch (t >> 1)
+ {
+ case 0: /* stored */
+ Trace((stderr, "inflate: stored block%s\n",
+ s->last ? " (last)" : ""));
+ DUMPBITS(3)
+ t = k & 7; /* go to byte boundary */
+ DUMPBITS(t)
+ s->mode = LENS; /* get length of stored block */
+ break;
+ case 1: /* fixed */
+ Trace((stderr, "inflate: fixed codes block%s\n",
+ s->last ? " (last)" : ""));
+ {
+ uInt bl, bd;
+ inflate_huft *tl, *td;
+
+ inflate_trees_fixed(&bl, &bd, &tl, &td);
+ s->sub.decode.codes = inflate_codes_new(bl, bd, tl, td, z);
+ if (s->sub.decode.codes == Z_NULL)
+ {
+ r = Z_MEM_ERROR;
+ LEAVE
+ }
+ s->sub.decode.tl = Z_NULL; /* don't try to free these */
+ s->sub.decode.td = Z_NULL;
+ }
+ DUMPBITS(3)
+ s->mode = CODES;
+ break;
+ case 2: /* dynamic */
+ Trace((stderr, "inflate: dynamic codes block%s\n",
+ s->last ? " (last)" : ""));
+ DUMPBITS(3)
+ s->mode = TABLE;
+ break;
+ case 3: /* illegal */
+ DUMPBITS(3)
+ s->mode = BADB;
+ z->msg = "invalid block type";
+ r = Z_DATA_ERROR;
+ LEAVE
+ }
+ break;
+ case LENS:
+ NEEDBITS(32)
+ if (((~b) >> 16) != (b & 0xffff))
+ {
+ s->mode = BADB;
+ z->msg = "invalid stored block lengths";
+ r = Z_DATA_ERROR;
+ LEAVE
+ }
+ s->sub.left = (uInt)b & 0xffff;
+ b = k = 0; /* dump bits */
+ Tracev((stderr, "inflate: stored length %u\n", s->sub.left));
+ s->mode = s->sub.left ? STORED : TYPE;
+ break;
+ case STORED:
+ if (n == 0)
+ LEAVE
+ NEEDOUT
+ t = s->sub.left;
+ if (t > n) t = n;
+ if (t > m) t = m;
+ zmemcpy(q, p, t);
+ p += t; n -= t;
+ q += t; m -= t;
+ if ((s->sub.left -= t) != 0)
+ break;
+ Tracev((stderr, "inflate: stored end, %lu total out\n",
+ z->total_out + (q >= s->read ? q - s->read :
+ (s->end - s->read) + (q - s->window))));
+ s->mode = s->last ? DRY : TYPE;
+ break;
+ case TABLE:
+ NEEDBITS(14)
+ s->sub.trees.table = t = (uInt)b & 0x3fff;
+#ifndef PKZIP_BUG_WORKAROUND
+ if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29)
+ {
+ s->mode = BADB;
+ z->msg = "too many length or distance symbols";
+ r = Z_DATA_ERROR;
+ LEAVE
+ }
+#endif
+ t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f);
+ if (t < 19)
+ t = 19;
+ if ((s->sub.trees.blens = (uIntf*)ZALLOC(z, t, sizeof(uInt))) == Z_NULL)
+ {
+ r = Z_MEM_ERROR;
+ LEAVE
+ }
+ s->sub.trees.nblens = t;
+ DUMPBITS(14)
+ s->sub.trees.index = 0;
+ Tracev((stderr, "inflate: table sizes ok\n"));
+ s->mode = BTREE;
+ case BTREE:
+ while (s->sub.trees.index < 4 + (s->sub.trees.table >> 10))
+ {
+ NEEDBITS(3)
+ s->sub.trees.blens[border[s->sub.trees.index++]] = (uInt)b & 7;
+ DUMPBITS(3)
+ }
+ while (s->sub.trees.index < 19)
+ s->sub.trees.blens[border[s->sub.trees.index++]] = 0;
+ s->sub.trees.bb = 7;
+ t = inflate_trees_bits(s->sub.trees.blens, &s->sub.trees.bb,
+ &s->sub.trees.tb, z);
+ if (t != Z_OK)
+ {
+ r = t;
+ if (r == Z_DATA_ERROR)
+ s->mode = BADB;
+ LEAVE
+ }
+ s->sub.trees.index = 0;
+ Tracev((stderr, "inflate: bits tree ok\n"));
+ s->mode = DTREE;
+ case DTREE:
+ while (t = s->sub.trees.table,
+ s->sub.trees.index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f))
+ {
+ inflate_huft *h;
+ uInt i, j, c;
+
+ t = s->sub.trees.bb;
+ NEEDBITS(t)
+ h = s->sub.trees.tb + ((uInt)b & inflate_mask[t]);
+ t = h->word.what.Bits;
+ c = h->more.Base;
+ if (c < 16)
+ {
+ DUMPBITS(t)
+ s->sub.trees.blens[s->sub.trees.index++] = c;
+ }
+ else /* c == 16..18 */
+ {
+ i = c == 18 ? 7 : c - 14;
+ j = c == 18 ? 11 : 3;
+ NEEDBITS(t + i)
+ DUMPBITS(t)
+ j += (uInt)b & inflate_mask[i];
+ DUMPBITS(i)
+ i = s->sub.trees.index;
+ t = s->sub.trees.table;
+ if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) ||
+ (c == 16 && i < 1))
+ {
+ s->mode = BADB;
+ z->msg = "invalid bit length repeat";
+ r = Z_DATA_ERROR;
+ LEAVE
+ }
+ c = c == 16 ? s->sub.trees.blens[i - 1] : 0;
+ do {
+ s->sub.trees.blens[i++] = c;
+ } while (--j);
+ s->sub.trees.index = i;
+ }
+ }
+ inflate_trees_free(s->sub.trees.tb, z);
+ s->sub.trees.tb = Z_NULL;
+ {
+ uInt bl, bd;
+ inflate_huft *tl, *td;
+ inflate_codes_statef *c;
+
+ bl = 9; /* must be <= 9 for lookahead assumptions */
+ bd = 6; /* must be <= 9 for lookahead assumptions */
+ t = s->sub.trees.table;
+ t = inflate_trees_dynamic(257 + (t & 0x1f), 1 + ((t >> 5) & 0x1f),
+ s->sub.trees.blens, &bl, &bd, &tl, &td, z);
+ if (t != Z_OK)
+ {
+ if (t == (uInt)Z_DATA_ERROR)
+ s->mode = BADB;
+ r = t;
+ LEAVE
+ }
+ Tracev((stderr, "inflate: trees ok\n"));
+ if ((c = inflate_codes_new(bl, bd, tl, td, z)) == Z_NULL)
+ {
+ inflate_trees_free(td, z);
+ inflate_trees_free(tl, z);
+ r = Z_MEM_ERROR;
+ LEAVE
+ }
+ ZFREE(z, s->sub.trees.blens, s->sub.trees.nblens * sizeof(uInt));
+ s->sub.decode.codes = c;
+ s->sub.decode.tl = tl;
+ s->sub.decode.td = td;
+ }
+ s->mode = CODES;
+ case CODES:
+ UPDATE
+ if ((r = inflate_codes(s, z, r)) != Z_STREAM_END)
+ return inflate_flush(s, z, r);
+ r = Z_OK;
+ inflate_codes_free(s->sub.decode.codes, z);
+ inflate_trees_free(s->sub.decode.td, z);
+ inflate_trees_free(s->sub.decode.tl, z);
+ LOAD
+ Tracev((stderr, "inflate: codes end, %lu total out\n",
+ z->total_out + (q >= s->read ? q - s->read :
+ (s->end - s->read) + (q - s->window))));
+ if (!s->last)
+ {
+ s->mode = TYPE;
+ break;
+ }
+ if (k > 7) /* return unused byte, if any */
+ {
+ Assert(k < 16, "inflate_codes grabbed too many bytes")
+ k -= 8;
+ n++;
+ p--; /* can always return one */
+ }
+ s->mode = DRY;
+ case DRY:
+ FLUSH
+ if (s->read != s->write)
+ LEAVE
+ s->mode = DONEB;
+ case DONEB:
+ r = Z_STREAM_END;
+ LEAVE
+ case BADB:
+ r = Z_DATA_ERROR;
+ LEAVE
+ default:
+ r = Z_STREAM_ERROR;
+ LEAVE
+ }
+}
+
+
+local int inflate_blocks_free(s, z, c)
+inflate_blocks_statef *s;
+z_stream *z;
+uLongf *c;
+{
+ inflate_blocks_reset(s, z, c);
+ ZFREE(z, s->window, s->end - s->window);
+ ZFREE(z, s, sizeof(struct inflate_blocks_state));
+ Trace((stderr, "inflate: blocks freed\n"));
+ return Z_OK;
+}
+
+/*
+ * This subroutine adds the data at next_in/avail_in to the output history
+ * without performing any output. The output buffer must be "caught up";
+ * i.e. no pending output (hence s->read equals s->write), and the state must
+ * be BLOCKS (i.e. we should be willing to see the start of a series of
+ * BLOCKS). On exit, the output will also be caught up, and the checksum
+ * will have been updated if need be.
+ */
+local int inflate_addhistory(s, z)
+inflate_blocks_statef *s;
+z_stream *z;
+{
+ uLong b; /* bit buffer */ /* NOT USED HERE */
+ uInt k; /* bits in bit buffer */ /* NOT USED HERE */
+ uInt t; /* temporary storage */
+ Bytef *p; /* input data pointer */
+ uInt n; /* bytes available there */
+ Bytef *q; /* output window write pointer */
+ uInt m; /* bytes to end of window or read pointer */
+
+ if (s->read != s->write)
+ return Z_STREAM_ERROR;
+ if (s->mode != TYPE)
+ return Z_DATA_ERROR;
+
+ /* we're ready to rock */
+ LOAD
+ /* while there is input ready, copy to output buffer, moving
+ * pointers as needed.
+ */
+ while (n) {
+ t = n; /* how many to do */
+ /* is there room until end of buffer? */
+ if (t > m) t = m;
+ /* update check information */
+ if (s->checkfn != Z_NULL)
+ s->check = (*s->checkfn)(s->check, q, t);
+ zmemcpy(q, p, t);
+ q += t;
+ p += t;
+ n -= t;
+ z->total_out += t;
+ s->read = q; /* drag read pointer forward */
+/* WRAP */ /* expand WRAP macro by hand to handle s->read */
+ if (q == s->end) {
+ s->read = q = s->window;
+ m = WAVAIL;
+ }
+ }
+ UPDATE
+ return Z_OK;
+}
+
+
+/*
+ * At the end of a Deflate-compressed PPP packet, we expect to have seen
+ * a `stored' block type value but not the (zero) length bytes.
+ */
+local int inflate_packet_flush(s)
+ inflate_blocks_statef *s;
+{
+ if (s->mode != LENS)
+ return Z_DATA_ERROR;
+ s->mode = TYPE;
+ return Z_OK;
+}
+
+
+/*+++++*/
+/* inftrees.c -- generate Huffman trees for efficient decoding
+ * Copyright (C) 1995 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* simplify the use of the inflate_huft type with some defines */
+#define base more.Base
+#define next more.Next
+#define exop word.what.Exop
+#define bits word.what.Bits
+
+
+local int huft_build OF((
+ uIntf *, /* code lengths in bits */
+ uInt, /* number of codes */
+ uInt, /* number of "simple" codes */
+ uIntf *, /* list of base values for non-simple codes */
+ uIntf *, /* list of extra bits for non-simple codes */
+ inflate_huft * FAR*,/* result: starting table */
+ uIntf *, /* maximum lookup bits (returns actual) */
+ z_stream *)); /* for zalloc function */
+
+local voidpf falloc OF((
+ voidpf, /* opaque pointer (not used) */
+ uInt, /* number of items */
+ uInt)); /* size of item */
+
+local void ffree OF((
+ voidpf q, /* opaque pointer (not used) */
+ voidpf p, /* what to free (not used) */
+ uInt n)); /* number of bytes (not used) */
+
+/* Tables for deflate from PKZIP's appnote.txt. */
+local uInt cplens[] = { /* Copy lengths for literal codes 257..285 */
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
+ 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0};
+ /* actually lengths - 2; also see note #13 above about 258 */
+local uInt cplext[] = { /* Extra bits for literal codes 257..285 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
+ 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 192, 192}; /* 192==invalid */
+local uInt cpdist[] = { /* Copy offsets for distance codes 0..29 */
+ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
+ 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
+ 8193, 12289, 16385, 24577};
+local uInt cpdext[] = { /* Extra bits for distance codes */
+ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
+ 7, 7, 8, 8, 9, 9, 10, 10, 11, 11,
+ 12, 12, 13, 13};
+
+/*
+ Huffman code decoding is performed using a multi-level table lookup.
+ The fastest way to decode is to simply build a lookup table whose
+ size is determined by the longest code. However, the time it takes
+ to build this table can also be a factor if the data being decoded
+ is not very long. The most common codes are necessarily the
+ shortest codes, so those codes dominate the decoding time, and hence
+ the speed. The idea is you can have a shorter table that decodes the
+ shorter, more probable codes, and then point to subsidiary tables for
+ the longer codes. The time it costs to decode the longer codes is
+ then traded against the time it takes to make longer tables.
+
+ This results of this trade are in the variables lbits and dbits
+ below. lbits is the number of bits the first level table for literal/
+ length codes can decode in one step, and dbits is the same thing for
+ the distance codes. Subsequent tables are also less than or equal to
+ those sizes. These values may be adjusted either when all of the
+ codes are shorter than that, in which case the longest code length in
+ bits is used, or when the shortest code is *longer* than the requested
+ table size, in which case the length of the shortest code in bits is
+ used.
+
+ There are two different values for the two tables, since they code a
+ different number of possibilities each. The literal/length table
+ codes 286 possible values, or in a flat code, a little over eight
+ bits. The distance table codes 30 possible values, or a little less
+ than five bits, flat. The optimum values for speed end up being
+ about one bit more than those, so lbits is 8+1 and dbits is 5+1.
+ The optimum values may differ though from machine to machine, and
+ possibly even between compilers. Your mileage may vary.
+ */
+
+
+/* If BMAX needs to be larger than 16, then h and x[] should be uLong. */
+#define BMAX 15 /* maximum bit length of any code */
+#define N_MAX 288 /* maximum number of codes in any set */
+
+#ifdef DEBUG_ZLIB
+ uInt inflate_hufts;
+#endif
+
+local int huft_build(b, n, s, d, e, t, m, zs)
+uIntf *b; /* code lengths in bits (all assumed <= BMAX) */
+uInt n; /* number of codes (assumed <= N_MAX) */
+uInt s; /* number of simple-valued codes (0..s-1) */
+uIntf *d; /* list of base values for non-simple codes */
+uIntf *e; /* list of extra bits for non-simple codes */
+inflate_huft * FAR *t; /* result: starting table */
+uIntf *m; /* maximum lookup bits, returns actual */
+z_stream *zs; /* for zalloc function */
+/* Given a list of code lengths and a maximum table size, make a set of
+ tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR
+ if the given code set is incomplete (the tables are still built in this
+ case), Z_DATA_ERROR if the input is invalid (all zero length codes or an
+ over-subscribed set of lengths), or Z_MEM_ERROR if not enough memory. */
+{
+
+ uInt a; /* counter for codes of length k */
+ uInt c[BMAX+1]; /* bit length count table */
+ uInt f; /* i repeats in table every f entries */
+ int g; /* maximum code length */
+ int h; /* table level */
+ register uInt i; /* counter, current code */
+ register uInt j; /* counter */
+ register int k; /* number of bits in current code */
+ int l; /* bits per table (returned in m) */
+ register uIntf *p; /* pointer into c[], b[], or v[] */
+ inflate_huft *q; /* points to current table */
+ struct inflate_huft_s r; /* table entry for structure assignment */
+ inflate_huft *u[BMAX]; /* table stack */
+ uInt v[N_MAX]; /* values in order of bit length */
+ register int w; /* bits before this table == (l * h) */
+ uInt x[BMAX+1]; /* bit offsets, then code stack */
+ uIntf *xp; /* pointer into x */
+ int y; /* number of dummy codes added */
+ uInt z; /* number of entries in current table */
+
+
+ /* Generate counts for each bit length */
+ p = c;
+#define C0 *p++ = 0;
+#define C2 C0 C0 C0 C0
+#define C4 C2 C2 C2 C2
+ C4 /* clear c[]--assume BMAX+1 is 16 */
+ p = b; i = n;
+ do {
+ c[*p++]++; /* assume all entries <= BMAX */
+ } while (--i);
+ if (c[0] == n) /* null input--all zero length codes */
+ {
+ *t = (inflate_huft *)Z_NULL;
+ *m = 0;
+ return Z_OK;
+ }
+
+
+ /* Find minimum and maximum length, bound *m by those */
+ l = *m;
+ for (j = 1; j <= BMAX; j++)
+ if (c[j])
+ break;
+ k = j; /* minimum code length */
+ if ((uInt)l < j)
+ l = j;
+ for (i = BMAX; i; i--)
+ if (c[i])
+ break;
+ g = i; /* maximum code length */
+ if ((uInt)l > i)
+ l = i;
+ *m = l;
+
+
+ /* Adjust last length count to fill out codes, if needed */
+ for (y = 1 << j; j < i; j++, y <<= 1)
+ if ((y -= c[j]) < 0)
+ return Z_DATA_ERROR;
+ if ((y -= c[i]) < 0)
+ return Z_DATA_ERROR;
+ c[i] += y;
+
+
+ /* Generate starting offsets into the value table for each length */
+ x[1] = j = 0;
+ p = c + 1; xp = x + 2;
+ while (--i) { /* note that i == g from above */
+ *xp++ = (j += *p++);
+ }
+
+
+ /* Make a table of values in order of bit lengths */
+ p = b; i = 0;
+ do {
+ if ((j = *p++) != 0)
+ v[x[j]++] = i;
+ } while (++i < n);
+
+
+ /* Generate the Huffman codes and for each, make the table entries */
+ x[0] = i = 0; /* first Huffman code is zero */
+ p = v; /* grab values in bit order */
+ h = -1; /* no tables yet--level -1 */
+ w = -l; /* bits decoded == (l * h) */
+ u[0] = (inflate_huft *)Z_NULL; /* just to keep compilers happy */
+ q = (inflate_huft *)Z_NULL; /* ditto */
+ z = 0; /* ditto */
+
+ /* go through the bit lengths (k already is bits in shortest code) */
+ for (; k <= g; k++)
+ {
+ a = c[k];
+ while (a--)
+ {
+ /* here i is the Huffman code of length k bits for value *p */
+ /* make tables up to required level */
+ while (k > w + l)
+ {
+ h++;
+ w += l; /* previous table always l bits */
+
+ /* compute minimum size table less than or equal to l bits */
+ z = (z = g - w) > (uInt)l ? l : z; /* table size upper limit */
+ if ((f = 1 << (j = k - w)) > a + 1) /* try a k-w bit table */
+ { /* too few codes for k-w bit table */
+ f -= a + 1; /* deduct codes from patterns left */
+ xp = c + k;
+ if (j < z)
+ while (++j < z) /* try smaller tables up to z bits */
+ {
+ if ((f <<= 1) <= *++xp)
+ break; /* enough codes to use up j bits */
+ f -= *xp; /* else deduct codes from patterns */
+ }
+ }
+ z = 1 << j; /* table entries for j-bit table */
+
+ /* allocate and link in new table */
+ if ((q = (inflate_huft *)ZALLOC
+ (zs,z + 1,sizeof(inflate_huft))) == Z_NULL)
+ {
+ if (h)
+ inflate_trees_free(u[0], zs);
+ return Z_MEM_ERROR; /* not enough memory */
+ }
+ q->word.Nalloc = z + 1;
+#ifdef DEBUG_ZLIB
+ inflate_hufts += z + 1;
+#endif
+ *t = q + 1; /* link to list for huft_free() */
+ *(t = &(q->next)) = Z_NULL;
+ u[h] = ++q; /* table starts after link */
+
+ /* connect to last table, if there is one */
+ if (h)
+ {
+ x[h] = i; /* save pattern for backing up */
+ r.bits = (Byte)l; /* bits to dump before this table */
+ r.exop = (Byte)j; /* bits in this table */
+ r.next = q; /* pointer to this table */
+ j = i >> (w - l); /* (get around Turbo C bug) */
+ u[h-1][j] = r; /* connect to last table */
+ }
+ }
+
+ /* set up table entry in r */
+ r.bits = (Byte)(k - w);
+ if (p >= v + n)
+ r.exop = 128 + 64; /* out of values--invalid code */
+ else if (*p < s)
+ {
+ r.exop = (Byte)(*p < 256 ? 0 : 32 + 64); /* 256 is end-of-block */
+ r.base = *p++; /* simple code is just the value */
+ }
+ else
+ {
+ r.exop = (Byte)e[*p - s] + 16 + 64; /* non-simple--look up in lists */
+ r.base = d[*p++ - s];
+ }
+
+ /* fill code-like entries with r */
+ f = 1 << (k - w);
+ for (j = i >> w; j < z; j += f)
+ q[j] = r;
+
+ /* backwards increment the k-bit code i */
+ for (j = 1 << (k - 1); i & j; j >>= 1)
+ i ^= j;
+ i ^= j;
+
+ /* backup over finished tables */
+ while ((i & ((1 << w) - 1)) != x[h])
+ {
+ h--; /* don't need to update q */
+ w -= l;
+ }
+ }
+ }
+
+
+ /* Return Z_BUF_ERROR if we were given an incomplete table */
+ return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK;
+}
+
+
+local int inflate_trees_bits(c, bb, tb, z)
+uIntf *c; /* 19 code lengths */
+uIntf *bb; /* bits tree desired/actual depth */
+inflate_huft * FAR *tb; /* bits tree result */
+z_stream *z; /* for zfree function */
+{
+ int r;
+
+ r = huft_build(c, 19, 19, (uIntf*)Z_NULL, (uIntf*)Z_NULL, tb, bb, z);
+ if (r == Z_DATA_ERROR)
+ z->msg = "oversubscribed dynamic bit lengths tree";
+ else if (r == Z_BUF_ERROR)
+ {
+ inflate_trees_free(*tb, z);
+ z->msg = "incomplete dynamic bit lengths tree";
+ r = Z_DATA_ERROR;
+ }
+ return r;
+}
+
+
+local int inflate_trees_dynamic(nl, nd, c, bl, bd, tl, td, z)
+uInt nl; /* number of literal/length codes */
+uInt nd; /* number of distance codes */
+uIntf *c; /* that many (total) code lengths */
+uIntf *bl; /* literal desired/actual bit depth */
+uIntf *bd; /* distance desired/actual bit depth */
+inflate_huft * FAR *tl; /* literal/length tree result */
+inflate_huft * FAR *td; /* distance tree result */
+z_stream *z; /* for zfree function */
+{
+ int r;
+
+ /* build literal/length tree */
+ if ((r = huft_build(c, nl, 257, cplens, cplext, tl, bl, z)) != Z_OK)
+ {
+ if (r == Z_DATA_ERROR)
+ z->msg = "oversubscribed literal/length tree";
+ else if (r == Z_BUF_ERROR)
+ {
+ inflate_trees_free(*tl, z);
+ z->msg = "incomplete literal/length tree";
+ r = Z_DATA_ERROR;
+ }
+ return r;
+ }
+
+ /* build distance tree */
+ if ((r = huft_build(c + nl, nd, 0, cpdist, cpdext, td, bd, z)) != Z_OK)
+ {
+ if (r == Z_DATA_ERROR)
+ z->msg = "oversubscribed literal/length tree";
+ else if (r == Z_BUF_ERROR) {
+#ifdef PKZIP_BUG_WORKAROUND
+ r = Z_OK;
+ }
+#else
+ inflate_trees_free(*td, z);
+ z->msg = "incomplete literal/length tree";
+ r = Z_DATA_ERROR;
+ }
+ inflate_trees_free(*tl, z);
+ return r;
+#endif
+ }
+
+ /* done */
+ return Z_OK;
+}
+
+
+/* build fixed tables only once--keep them here */
+local int fixed_lock = 0;
+local int fixed_built = 0;
+#define FIXEDH 530 /* number of hufts used by fixed tables */
+local uInt fixed_left = FIXEDH;
+local inflate_huft fixed_mem[FIXEDH];
+local uInt fixed_bl;
+local uInt fixed_bd;
+local inflate_huft *fixed_tl;
+local inflate_huft *fixed_td;
+
+
+local voidpf falloc(q, n, s)
+voidpf q; /* opaque pointer (not used) */
+uInt n; /* number of items */
+uInt s; /* size of item */
+{
+ Assert(s == sizeof(inflate_huft) && n <= fixed_left,
+ "inflate_trees falloc overflow");
+ if (q) s++; /* to make some compilers happy */
+ fixed_left -= n;
+ return (voidpf)(fixed_mem + fixed_left);
+}
+
+
+local void ffree(q, p, n)
+voidpf q;
+voidpf p;
+uInt n;
+{
+ Assert(0, "inflate_trees ffree called!");
+ if (q) q = p; /* to make some compilers happy */
+}
+
+
+local int inflate_trees_fixed(bl, bd, tl, td)
+uIntf *bl; /* literal desired/actual bit depth */
+uIntf *bd; /* distance desired/actual bit depth */
+inflate_huft * FAR *tl; /* literal/length tree result */
+inflate_huft * FAR *td; /* distance tree result */
+{
+ /* build fixed tables if not built already--lock out other instances */
+ while (++fixed_lock > 1)
+ fixed_lock--;
+ if (!fixed_built)
+ {
+ int k; /* temporary variable */
+ unsigned c[288]; /* length list for huft_build */
+ z_stream z; /* for falloc function */
+
+ /* set up fake z_stream for memory routines */
+ z.zalloc = falloc;
+ z.zfree = ffree;
+ z.opaque = Z_NULL;
+
+ /* literal table */
+ for (k = 0; k < 144; k++)
+ c[k] = 8;
+ for (; k < 256; k++)
+ c[k] = 9;
+ for (; k < 280; k++)
+ c[k] = 7;
+ for (; k < 288; k++)
+ c[k] = 8;
+ fixed_bl = 7;
+ huft_build(c, 288, 257, cplens, cplext, &fixed_tl, &fixed_bl, &z);
+
+ /* distance table */
+ for (k = 0; k < 30; k++)
+ c[k] = 5;
+ fixed_bd = 5;
+ huft_build(c, 30, 0, cpdist, cpdext, &fixed_td, &fixed_bd, &z);
+
+ /* done */
+ fixed_built = 1;
+ }
+ fixed_lock--;
+ *bl = fixed_bl;
+ *bd = fixed_bd;
+ *tl = fixed_tl;
+ *td = fixed_td;
+ return Z_OK;
+}
+
+
+local int inflate_trees_free(t, z)
+inflate_huft *t; /* table to free */
+z_stream *z; /* for zfree function */
+/* Free the malloc'ed tables built by huft_build(), which makes a linked
+ list of the tables it made, with the links in a dummy first entry of
+ each table. */
+{
+ register inflate_huft *p, *q;
+
+ /* Go through linked list, freeing from the malloced (t[-1]) address. */
+ p = t;
+ while (p != Z_NULL)
+ {
+ q = (--p)->next;
+ ZFREE(z, p, p->word.Nalloc * sizeof(inflate_huft));
+ p = q;
+ }
+ return Z_OK;
+}
+
+/*+++++*/
+/* infcodes.c -- process literals and length/distance pairs
+ * Copyright (C) 1995 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* simplify the use of the inflate_huft type with some defines */
+#define base more.Base
+#define next more.Next
+#define exop word.what.Exop
+#define bits word.what.Bits
+
+/* inflate codes private state */
+struct inflate_codes_state {
+
+ /* mode */
+ enum { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */
+ START, /* x: set up for LEN */
+ LEN, /* i: get length/literal/eob next */
+ LENEXT, /* i: getting length extra (have base) */
+ DIST, /* i: get distance next */
+ DISTEXT, /* i: getting distance extra */
+ COPY, /* o: copying bytes in window, waiting for space */
+ LIT, /* o: got literal, waiting for output space */
+ WASH, /* o: got eob, possibly still output waiting */
+ END, /* x: got eob and all data flushed */
+ BADCODE} /* x: got error */
+ mode; /* current inflate_codes mode */
+
+ /* mode dependent information */
+ uInt len;
+ union {
+ struct {
+ inflate_huft *tree; /* pointer into tree */
+ uInt need; /* bits needed */
+ } code; /* if LEN or DIST, where in tree */
+ uInt lit; /* if LIT, literal */
+ struct {
+ uInt get; /* bits to get for extra */
+ uInt dist; /* distance back to copy from */
+ } copy; /* if EXT or COPY, where and how much */
+ } sub; /* submode */
+
+ /* mode independent information */
+ Byte lbits; /* ltree bits decoded per branch */
+ Byte dbits; /* dtree bits decoder per branch */
+ inflate_huft *ltree; /* literal/length/eob tree */
+ inflate_huft *dtree; /* distance tree */
+
+};
+
+
+local inflate_codes_statef *inflate_codes_new(bl, bd, tl, td, z)
+uInt bl, bd;
+inflate_huft *tl, *td;
+z_stream *z;
+{
+ inflate_codes_statef *c;
+
+ if ((c = (inflate_codes_statef *)
+ ZALLOC(z,1,sizeof(struct inflate_codes_state))) != Z_NULL)
+ {
+ c->mode = START;
+ c->lbits = (Byte)bl;
+ c->dbits = (Byte)bd;
+ c->ltree = tl;
+ c->dtree = td;
+ Tracev((stderr, "inflate: codes new\n"));
+ }
+ return c;
+}
+
+
+local int inflate_codes(s, z, r)
+inflate_blocks_statef *s;
+z_stream *z;
+int r;
+{
+ uInt j; /* temporary storage */
+ inflate_huft *t; /* temporary pointer */
+ uInt e; /* extra bits or operation */
+ uLong b; /* bit buffer */
+ uInt k; /* bits in bit buffer */
+ Bytef *p; /* input data pointer */
+ uInt n; /* bytes available there */
+ Bytef *q; /* output window write pointer */
+ uInt m; /* bytes to end of window or read pointer */
+ Bytef *f; /* pointer to copy strings from */
+ inflate_codes_statef *c = s->sub.decode.codes; /* codes state */
+
+ /* copy input/output information to locals (UPDATE macro restores) */
+ LOAD
+
+ /* process input and output based on current state */
+ while (1) switch (c->mode)
+ { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */
+ case START: /* x: set up for LEN */
+#ifndef SLOW
+ if (m >= 258 && n >= 10)
+ {
+ UPDATE
+ r = inflate_fast(c->lbits, c->dbits, c->ltree, c->dtree, s, z);
+ LOAD
+ if (r != Z_OK)
+ {
+ c->mode = r == Z_STREAM_END ? WASH : BADCODE;
+ break;
+ }
+ }
+#endif /* !SLOW */
+ c->sub.code.need = c->lbits;
+ c->sub.code.tree = c->ltree;
+ c->mode = LEN;
+ case LEN: /* i: get length/literal/eob next */
+ j = c->sub.code.need;
+ NEEDBITS(j)
+ t = c->sub.code.tree + ((uInt)b & inflate_mask[j]);
+ DUMPBITS(t->bits)
+ e = (uInt)(t->exop);
+ if (e == 0) /* literal */
+ {
+ c->sub.lit = t->base;
+ Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ?
+ "inflate: literal '%c'\n" :
+ "inflate: literal 0x%02x\n", t->base));
+ c->mode = LIT;
+ break;
+ }
+ if (e & 16) /* length */
+ {
+ c->sub.copy.get = e & 15;
+ c->len = t->base;
+ c->mode = LENEXT;
+ break;
+ }
+ if ((e & 64) == 0) /* next table */
+ {
+ c->sub.code.need = e;
+ c->sub.code.tree = t->next;
+ break;
+ }
+ if (e & 32) /* end of block */
+ {
+ Tracevv((stderr, "inflate: end of block\n"));
+ c->mode = WASH;
+ break;
+ }
+ c->mode = BADCODE; /* invalid code */
+ z->msg = "invalid literal/length code";
+ r = Z_DATA_ERROR;
+ LEAVE
+ case LENEXT: /* i: getting length extra (have base) */
+ j = c->sub.copy.get;
+ NEEDBITS(j)
+ c->len += (uInt)b & inflate_mask[j];
+ DUMPBITS(j)
+ c->sub.code.need = c->dbits;
+ c->sub.code.tree = c->dtree;
+ Tracevv((stderr, "inflate: length %u\n", c->len));
+ c->mode = DIST;
+ case DIST: /* i: get distance next */
+ j = c->sub.code.need;
+ NEEDBITS(j)
+ t = c->sub.code.tree + ((uInt)b & inflate_mask[j]);
+ DUMPBITS(t->bits)
+ e = (uInt)(t->exop);
+ if (e & 16) /* distance */
+ {
+ c->sub.copy.get = e & 15;
+ c->sub.copy.dist = t->base;
+ c->mode = DISTEXT;
+ break;
+ }
+ if ((e & 64) == 0) /* next table */
+ {
+ c->sub.code.need = e;
+ c->sub.code.tree = t->next;
+ break;
+ }
+ c->mode = BADCODE; /* invalid code */
+ z->msg = "invalid distance code";
+ r = Z_DATA_ERROR;
+ LEAVE
+ case DISTEXT: /* i: getting distance extra */
+ j = c->sub.copy.get;
+ NEEDBITS(j)
+ c->sub.copy.dist += (uInt)b & inflate_mask[j];
+ DUMPBITS(j)
+ Tracevv((stderr, "inflate: distance %u\n", c->sub.copy.dist));
+ c->mode = COPY;
+ case COPY: /* o: copying bytes in window, waiting for space */
+#ifndef __TURBOC__ /* Turbo C bug for following expression */
+ f = (uInt)(q - s->window) < c->sub.copy.dist ?
+ s->end - (c->sub.copy.dist - (q - s->window)) :
+ q - c->sub.copy.dist;
+#else
+ f = q - c->sub.copy.dist;
+ if ((uInt)(q - s->window) < c->sub.copy.dist)
+ f = s->end - (c->sub.copy.dist - (q - s->window));
+#endif
+ while (c->len)
+ {
+ NEEDOUT
+ OUTBYTE(*f++)
+ if (f == s->end)
+ f = s->window;
+ c->len--;
+ }
+ c->mode = START;
+ break;
+ case LIT: /* o: got literal, waiting for output space */
+ NEEDOUT
+ OUTBYTE(c->sub.lit)
+ c->mode = START;
+ break;
+ case WASH: /* o: got eob, possibly more output */
+ FLUSH
+ if (s->read != s->write)
+ LEAVE
+ c->mode = END;
+ case END:
+ r = Z_STREAM_END;
+ LEAVE
+ case BADCODE: /* x: got error */
+ r = Z_DATA_ERROR;
+ LEAVE
+ default:
+ r = Z_STREAM_ERROR;
+ LEAVE
+ }
+}
+
+
+local void inflate_codes_free(c, z)
+inflate_codes_statef *c;
+z_stream *z;
+{
+ ZFREE(z, c, sizeof(struct inflate_codes_state));
+ Tracev((stderr, "inflate: codes free\n"));
+}
+
+/*+++++*/
+/* inflate_util.c -- data and routines common to blocks and codes
+ * Copyright (C) 1995 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* copy as much as possible from the sliding window to the output area */
+local int inflate_flush(s, z, r)
+inflate_blocks_statef *s;
+z_stream *z;
+int r;
+{
+ uInt n;
+ Bytef *p, *q;
+
+ /* local copies of source and destination pointers */
+ p = z->next_out;
+ q = s->read;
+
+ /* compute number of bytes to copy as far as end of window */
+ n = (uInt)((q <= s->write ? s->write : s->end) - q);
+ if (n > z->avail_out) n = z->avail_out;
+ if (n && r == Z_BUF_ERROR) r = Z_OK;
+
+ /* update counters */
+ z->avail_out -= n;
+ z->total_out += n;
+
+ /* update check information */
+ if (s->checkfn != Z_NULL)
+ s->check = (*s->checkfn)(s->check, q, n);
+
+ /* copy as far as end of window */
+ if (p != NULL) {
+ zmemcpy(p, q, n);
+ p += n;
+ }
+ q += n;
+
+ /* see if more to copy at beginning of window */
+ if (q == s->end)
+ {
+ /* wrap pointers */
+ q = s->window;
+ if (s->write == s->end)
+ s->write = s->window;
+
+ /* compute bytes to copy */
+ n = (uInt)(s->write - q);
+ if (n > z->avail_out) n = z->avail_out;
+ if (n && r == Z_BUF_ERROR) r = Z_OK;
+
+ /* update counters */
+ z->avail_out -= n;
+ z->total_out += n;
+
+ /* update check information */
+ if (s->checkfn != Z_NULL)
+ s->check = (*s->checkfn)(s->check, q, n);
+
+ /* copy */
+ if (p != NULL) {
+ zmemcpy(p, q, n);
+ p += n;
+ }
+ q += n;
+ }
+
+ /* update pointers */
+ z->next_out = p;
+ s->read = q;
+
+ /* done */
+ return r;
+}
+
+
+/*+++++*/
+/* inffast.c -- process literals and length/distance pairs fast
+ * Copyright (C) 1995 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* simplify the use of the inflate_huft type with some defines */
+#define base more.Base
+#define next more.Next
+#define exop word.what.Exop
+#define bits word.what.Bits
+
+/* macros for bit input with no checking and for returning unused bytes */
+#define GRABBITS(j) {while(k<(j)){b|=((uLong)NEXTBYTE)<<k;k+=8;}}
+#define UNGRAB {n+=(c=k>>3);p-=c;k&=7;}
+
+/* Called with number of bytes left to write in window at least 258
+ (the maximum string length) and number of input bytes available
+ at least ten. The ten bytes are six bytes for the longest length/
+ distance pair plus four bytes for overloading the bit buffer. */
+
+local int inflate_fast(bl, bd, tl, td, s, z)
+uInt bl, bd;
+inflate_huft *tl, *td;
+inflate_blocks_statef *s;
+z_stream *z;
+{
+ inflate_huft *t; /* temporary pointer */
+ uInt e; /* extra bits or operation */
+ uLong b; /* bit buffer */
+ uInt k; /* bits in bit buffer */
+ Bytef *p; /* input data pointer */
+ uInt n; /* bytes available there */
+ Bytef *q; /* output window write pointer */
+ uInt m; /* bytes to end of window or read pointer */
+ uInt ml; /* mask for literal/length tree */
+ uInt md; /* mask for distance tree */
+ uInt c; /* bytes to copy */
+ uInt d; /* distance back to copy from */
+ Bytef *r; /* copy source pointer */
+
+ /* load input, output, bit values */
+ LOAD
+
+ /* initialize masks */
+ ml = inflate_mask[bl];
+ md = inflate_mask[bd];
+
+ /* do until not enough input or output space for fast loop */
+ do { /* assume called with m >= 258 && n >= 10 */
+ /* get literal/length code */
+ GRABBITS(20) /* max bits for literal/length code */
+ if ((e = (t = tl + ((uInt)b & ml))->exop) == 0)
+ {
+ DUMPBITS(t->bits)
+ Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ?
+ "inflate: * literal '%c'\n" :
+ "inflate: * literal 0x%02x\n", t->base));
+ *q++ = (Byte)t->base;
+ m--;
+ continue;
+ }
+ do {
+ DUMPBITS(t->bits)
+ if (e & 16)
+ {
+ /* get extra bits for length */
+ e &= 15;
+ c = t->base + ((uInt)b & inflate_mask[e]);
+ DUMPBITS(e)
+ Tracevv((stderr, "inflate: * length %u\n", c));
+
+ /* decode distance base of block to copy */
+ GRABBITS(15); /* max bits for distance code */
+ e = (t = td + ((uInt)b & md))->exop;
+ do {
+ DUMPBITS(t->bits)
+ if (e & 16)
+ {
+ /* get extra bits to add to distance base */
+ e &= 15;
+ GRABBITS(e) /* get extra bits (up to 13) */
+ d = t->base + ((uInt)b & inflate_mask[e]);
+ DUMPBITS(e)
+ Tracevv((stderr, "inflate: * distance %u\n", d));
+
+ /* do the copy */
+ m -= c;
+ if ((uInt)(q - s->window) >= d) /* offset before dest */
+ { /* just copy */
+ r = q - d;
+ *q++ = *r++; c--; /* minimum count is three, */
+ *q++ = *r++; c--; /* so unroll loop a little */
+ }
+ else /* else offset after destination */
+ {
+ e = d - (q - s->window); /* bytes from offset to end */
+ r = s->end - e; /* pointer to offset */
+ if (c > e) /* if source crosses, */
+ {
+ c -= e; /* copy to end of window */
+ do {
+ *q++ = *r++;
+ } while (--e);
+ r = s->window; /* copy rest from start of window */
+ }
+ }
+ do { /* copy all or what's left */
+ *q++ = *r++;
+ } while (--c);
+ break;
+ }
+ else if ((e & 64) == 0)
+ e = (t = t->next + ((uInt)b & inflate_mask[e]))->exop;
+ else
+ {
+ z->msg = "invalid distance code";
+ UNGRAB
+ UPDATE
+ return Z_DATA_ERROR;
+ }
+ } while (1);
+ break;
+ }
+ if ((e & 64) == 0)
+ {
+ if ((e = (t = t->next + ((uInt)b & inflate_mask[e]))->exop) == 0)
+ {
+ DUMPBITS(t->bits)
+ Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ?
+ "inflate: * literal '%c'\n" :
+ "inflate: * literal 0x%02x\n", t->base));
+ *q++ = (Byte)t->base;
+ m--;
+ break;
+ }
+ }
+ else if (e & 32)
+ {
+ Tracevv((stderr, "inflate: * end of block\n"));
+ UNGRAB
+ UPDATE
+ return Z_STREAM_END;
+ }
+ else
+ {
+ z->msg = "invalid literal/length code";
+ UNGRAB
+ UPDATE
+ return Z_DATA_ERROR;
+ }
+ } while (1);
+ } while (m >= 258 && n >= 10);
+
+ /* not enough input or output--restore pointers and return */
+ UNGRAB
+ UPDATE
+ return Z_OK;
+}
+
+
+/*+++++*/
+/* zutil.c -- target dependent utility functions for the compression library
+ * Copyright (C) 1995 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* From: zutil.c,v 1.8 1995/05/03 17:27:12 jloup Exp */
+
+char *zlib_version = ZLIB_VERSION;
+
+char *z_errmsg[] = {
+"stream end", /* Z_STREAM_END 1 */
+"", /* Z_OK 0 */
+"file error", /* Z_ERRNO (-1) */
+"stream error", /* Z_STREAM_ERROR (-2) */
+"data error", /* Z_DATA_ERROR (-3) */
+"insufficient memory", /* Z_MEM_ERROR (-4) */
+"buffer error", /* Z_BUF_ERROR (-5) */
+""};
+
+
+/*+++++*/
+/* adler32.c -- compute the Adler-32 checksum of a data stream
+ * Copyright (C) 1995 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* From: adler32.c,v 1.6 1995/05/03 17:27:08 jloup Exp */
+
+#define BASE 65521L /* largest prime smaller than 65536 */
+#define NMAX 5552
+/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */
+
+#define DO1(buf) {s1 += *buf++; s2 += s1;}
+#define DO2(buf) DO1(buf); DO1(buf);
+#define DO4(buf) DO2(buf); DO2(buf);
+#define DO8(buf) DO4(buf); DO4(buf);
+#define DO16(buf) DO8(buf); DO8(buf);
+
+/* ========================================================================= */
+uLong adler32(adler, buf, len)
+ uLong adler;
+ Bytef *buf;
+ uInt len;
+{
+ unsigned long s1 = adler & 0xffff;
+ unsigned long s2 = (adler >> 16) & 0xffff;
+ int k;
+
+ if (buf == Z_NULL) return 1L;
+
+ while (len > 0) {
+ k = len < NMAX ? len : NMAX;
+ len -= k;
+ while (k >= 16) {
+ DO16(buf);
+ k -= 16;
+ }
+ if (k != 0) do {
+ DO1(buf);
+ } while (--k);
+ s1 %= BASE;
+ s2 %= BASE;
+ }
+ return (s2 << 16) | s1;
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppdump/zlib.h b/usr/src/cmd/cmd-inet/usr.bin/pppdump/zlib.h
new file mode 100644
index 0000000000..082f6493d5
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppdump/zlib.h
@@ -0,0 +1,631 @@
+/* $Id: zlib.h,v 1.1 1999/03/23 03:21:58 paulus Exp $ */
+
+/*
+ * This file is derived from zlib.h and zconf.h from the zlib-0.95
+ * distribution by Jean-loup Gailly and Mark Adler, with some additions
+ * by Paul Mackerras to aid in implementing Deflate compression and
+ * decompression for PPP packets.
+ */
+
+/* zlib.h -- interface of the 'zlib' general purpose compression library
+ version 0.95, Aug 16th, 1995.
+
+ Copyright (C) 1995 Jean-loup Gailly and Mark Adler
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ Jean-loup Gailly Mark Adler
+ gzip@prep.ai.mit.edu madler@alumni.caltech.edu
+ */
+
+#ifndef _ZLIB_H
+#define _ZLIB_H
+
+/* #include "zconf.h" */ /* included directly here */
+
+/* zconf.h -- configuration of the zlib compression library
+ * Copyright (C) 1995 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* From: zconf.h,v 1.12 1995/05/03 17:27:12 jloup Exp */
+
+/*
+ The library does not install any signal handler. It is recommended to
+ add at least a handler for SIGSEGV when decompressing; the library checks
+ the consistency of the input data whenever possible but may go nuts
+ for some forms of corrupted input.
+ */
+
+/*
+ * Compile with -DMAXSEG_64K if the alloc function cannot allocate more
+ * than 64k bytes at a time (needed on systems with 16-bit int).
+ * Compile with -DUNALIGNED_OK if it is OK to access shorts or ints
+ * at addresses which are not a multiple of their size.
+ * Under DOS, -DFAR=far or -DFAR=__far may be needed.
+ */
+
+#ifndef STDC
+# if defined(MSDOS) || defined(__STDC__) || defined(__cplusplus)
+# define STDC
+# endif
+#endif
+
+#ifdef __MWERKS__ /* Metrowerks CodeWarrior declares fileno() in unix.h */
+# include <unix.h>
+#endif
+
+/* Maximum value for memLevel in deflateInit2 */
+#ifndef MAX_MEM_LEVEL
+# ifdef MAXSEG_64K
+# define MAX_MEM_LEVEL 8
+# else
+# define MAX_MEM_LEVEL 9
+# endif
+#endif
+
+#ifndef FAR
+# define FAR
+#endif
+
+/* Maximum value for windowBits in deflateInit2 and inflateInit2 */
+#ifndef MAX_WBITS
+# define MAX_WBITS 15 /* 32K LZ77 window */
+#endif
+
+/* The memory requirements for deflate are (in bytes):
+ 1 << (windowBits+2) + 1 << (memLevel+9)
+ that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values)
+ plus a few kilobytes for small objects. For example, if you want to reduce
+ the default memory requirements from 256K to 128K, compile with
+ make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7"
+ Of course this will generally degrade compression (there's no free lunch).
+
+ The memory requirements for inflate are (in bytes) 1 << windowBits
+ that is, 32K for windowBits=15 (default value) plus a few kilobytes
+ for small objects.
+*/
+
+ /* Type declarations */
+
+#ifndef OF /* function prototypes */
+# ifdef STDC
+# define OF(args) args
+# else
+# define OF(args) ()
+# endif
+#endif
+
+typedef unsigned char Byte; /* 8 bits */
+typedef unsigned int uInt; /* 16 bits or more */
+typedef unsigned long uLong; /* 32 bits or more */
+
+typedef Byte FAR Bytef;
+typedef char FAR charf;
+typedef int FAR intf;
+typedef uInt FAR uIntf;
+typedef uLong FAR uLongf;
+
+#ifdef STDC
+ typedef void FAR *voidpf;
+ typedef void *voidp;
+#else
+ typedef Byte FAR *voidpf;
+ typedef Byte *voidp;
+#endif
+
+/* end of original zconf.h */
+
+#define ZLIB_VERSION "0.95P"
+
+/*
+ The 'zlib' compression library provides in-memory compression and
+ decompression functions, including integrity checks of the uncompressed
+ data. This version of the library supports only one compression method
+ (deflation) but other algorithms may be added later and will have the same
+ stream interface.
+
+ For compression the application must provide the output buffer and
+ may optionally provide the input buffer for optimization. For decompression,
+ the application must provide the input buffer and may optionally provide
+ the output buffer for optimization.
+
+ Compression can be done in a single step if the buffers are large
+ enough (for example if an input file is mmap'ed), or can be done by
+ repeated calls of the compression function. In the latter case, the
+ application must provide more input and/or consume the output
+ (providing more output space) before each call.
+*/
+
+typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size));
+typedef void (*free_func) OF((voidpf opaque, voidpf address, uInt nbytes));
+
+struct internal_state;
+
+typedef struct z_stream_s {
+ Bytef *next_in; /* next input byte */
+ uInt avail_in; /* number of bytes available at next_in */
+ uLong total_in; /* total nb of input bytes read so far */
+
+ Bytef *next_out; /* next output byte should be put there */
+ uInt avail_out; /* remaining free space at next_out */
+ uLong total_out; /* total nb of bytes output so far */
+
+ char *msg; /* last error message, NULL if no error */
+ struct internal_state FAR *state; /* not visible by applications */
+
+ alloc_func zalloc; /* used to allocate the internal state */
+ free_func zfree; /* used to free the internal state */
+ voidp opaque; /* private data object passed to zalloc and zfree */
+
+ Byte data_type; /* best guess about the data type: ascii or binary */
+
+} z_stream;
+
+/*
+ The application must update next_in and avail_in when avail_in has
+ dropped to zero. It must update next_out and avail_out when avail_out
+ has dropped to zero. The application must initialize zalloc, zfree and
+ opaque before calling the init function. All other fields are set by the
+ compression library and must not be updated by the application.
+
+ The opaque value provided by the application will be passed as the first
+ parameter for calls of zalloc and zfree. This can be useful for custom
+ memory management. The compression library attaches no meaning to the
+ opaque value.
+
+ zalloc must return Z_NULL if there is not enough memory for the object.
+ On 16-bit systems, the functions zalloc and zfree must be able to allocate
+ exactly 65536 bytes, but will not be required to allocate more than this
+ if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS,
+ pointers returned by zalloc for objects of exactly 65536 bytes *must*
+ have their offset normalized to zero. The default allocation function
+ provided by this library ensures this (see zutil.c). To reduce memory
+ requirements and avoid any allocation of 64K objects, at the expense of
+ compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h).
+
+ The fields total_in and total_out can be used for statistics or
+ progress reports. After compression, total_in holds the total size of
+ the uncompressed data and may be saved for use in the decompressor
+ (particularly if the decompressor wants to decompress everything in
+ a single step).
+*/
+
+ /* constants */
+
+#define Z_NO_FLUSH 0
+#define Z_PARTIAL_FLUSH 1
+#define Z_FULL_FLUSH 2
+#define Z_SYNC_FLUSH 3 /* experimental: partial_flush + byte align */
+#define Z_FINISH 4
+#define Z_PACKET_FLUSH 5
+/* See deflate() below for the usage of these constants */
+
+#define Z_OK 0
+#define Z_STREAM_END 1
+#define Z_ERRNO (-1)
+#define Z_STREAM_ERROR (-2)
+#define Z_DATA_ERROR (-3)
+#define Z_MEM_ERROR (-4)
+#define Z_BUF_ERROR (-5)
+/* error codes for the compression/decompression functions */
+
+#define Z_BEST_SPEED 1
+#define Z_BEST_COMPRESSION 9
+#define Z_DEFAULT_COMPRESSION (-1)
+/* compression levels */
+
+#define Z_FILTERED 1
+#define Z_HUFFMAN_ONLY 2
+#define Z_DEFAULT_STRATEGY 0
+
+#define Z_BINARY 0
+#define Z_ASCII 1
+#define Z_UNKNOWN 2
+/* Used to set the data_type field */
+
+#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */
+
+extern char *zlib_version;
+/* The application can compare zlib_version and ZLIB_VERSION for consistency.
+ If the first character differs, the library code actually used is
+ not compatible with the zlib.h header file used by the application.
+ */
+
+ /* basic functions */
+
+extern int deflateInit OF((z_stream *strm, int level));
+/*
+ Initializes the internal stream state for compression. The fields
+ zalloc, zfree and opaque must be initialized before by the caller.
+ If zalloc and zfree are set to Z_NULL, deflateInit updates them to
+ use default allocation functions.
+
+ The compression level must be Z_DEFAULT_COMPRESSION, or between 1 and 9:
+ 1 gives best speed, 9 gives best compression. Z_DEFAULT_COMPRESSION requests
+ a default compromise between speed and compression (currently equivalent
+ to level 6).
+
+ deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_STREAM_ERROR if level is not a valid compression level.
+ msg is set to null if there is no error message. deflateInit does not
+ perform any compression: this will be done by deflate().
+*/
+
+
+extern int deflate OF((z_stream *strm, int flush));
+/*
+ Performs one or both of the following actions:
+
+ - Compress more input starting at next_in and update next_in and avail_in
+ accordingly. If not all input can be processed (because there is not
+ enough room in the output buffer), next_in and avail_in are updated and
+ processing will resume at this point for the next call of deflate().
+
+ - Provide more output starting at next_out and update next_out and avail_out
+ accordingly. This action is forced if the parameter flush is non zero.
+ Forcing flush frequently degrades the compression ratio, so this parameter
+ should be set only when necessary (in interactive applications).
+ Some output may be provided even if flush is not set.
+
+ Before the call of deflate(), the application should ensure that at least
+ one of the actions is possible, by providing more input and/or consuming
+ more output, and updating avail_in or avail_out accordingly; avail_out
+ should never be zero before the call. The application can consume the
+ compressed output when it wants, for example when the output buffer is full
+ (avail_out == 0), or after each call of deflate().
+
+ If the parameter flush is set to Z_PARTIAL_FLUSH, the current compression
+ block is terminated and flushed to the output buffer so that the
+ decompressor can get all input data available so far. For method 9, a future
+ variant on method 8, the current block will be flushed but not terminated.
+ If flush is set to Z_FULL_FLUSH, the compression block is terminated, a
+ special marker is output and the compression dictionary is discarded; this
+ is useful to allow the decompressor to synchronize if one compressed block
+ has been damaged (see inflateSync below). Flushing degrades compression and
+ so should be used only when necessary. Using Z_FULL_FLUSH too often can
+ seriously degrade the compression. If deflate returns with avail_out == 0,
+ this function must be called again with the same value of the flush
+ parameter and more output space (updated avail_out), until the flush is
+ complete (deflate returns with non-zero avail_out).
+
+ If the parameter flush is set to Z_PACKET_FLUSH, the compression
+ block is terminated, and a zero-length stored block is output,
+ omitting the length bytes (the effect of this is that the 3-bit type
+ code 000 for a stored block is output, and the output is then
+ byte-aligned). This is designed for use at the end of a PPP packet.
+ In addition, if the current compression block contains all the data
+ since the last Z_PACKET_FLUSH, it is never output as a stored block.
+ If the current compression block output as a static or dynamic block
+ would not be at least `minCompression' bytes smaller than the
+ original data, then nothing is output for that block. (The type
+ code for the zero-length stored block is still output, resulting in
+ a single zero byte being output for the whole packet.)
+ `MinCompression' is a parameter to deflateInit2, or 0 if deflateInit
+ is used.
+
+ If the parameter flush is set to Z_FINISH, all pending input is processed,
+ all pending output is flushed and deflate returns with Z_STREAM_END if there
+ was enough output space; if deflate returns with Z_OK, this function must be
+ called again with Z_FINISH and more output space (updated avail_out) but no
+ more input data, until it returns with Z_STREAM_END or an error. After
+ deflate has returned Z_STREAM_END, the only possible operations on the
+ stream are deflateReset or deflateEnd.
+
+ Z_FINISH can be used immediately after deflateInit if all the compression
+ is to be done in a single step. In this case, avail_out must be at least
+ 0.1% larger than avail_in plus 12 bytes. If deflate does not return
+ Z_STREAM_END, then it must be called again as described above.
+
+ deflate() may update data_type if it can make a good guess about
+ the input data type (Z_ASCII or Z_BINARY). In doubt, the data is considered
+ binary. This field is only for information purposes and does not affect
+ the compression algorithm in any manner.
+
+ deflate() returns Z_OK if some progress has been made (more input
+ processed or more output produced), Z_STREAM_END if all input has been
+ consumed and all output has been produced (only when flush is set to
+ Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example
+ if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible.
+*/
+
+
+extern int deflateEnd OF((z_stream *strm));
+/*
+ All dynamically allocated data structures for this stream are freed.
+ This function discards any unprocessed input and does not flush any
+ pending output.
+
+ deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the
+ stream state was inconsistent. In the error case, msg may be set
+ but then points to a static string (which must not be deallocated).
+*/
+
+
+extern int inflateInit OF((z_stream *strm));
+/*
+ Initializes the internal stream state for decompression. The fields
+ zalloc and zfree must be initialized before by the caller. If zalloc and
+ zfree are set to Z_NULL, inflateInit updates them to use default allocation
+ functions.
+
+ inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory. msg is set to null if there is no error message.
+ inflateInit does not perform any decompression: this will be done by
+ inflate().
+*/
+
+
+extern int inflate OF((z_stream *strm, int flush));
+/*
+ Performs one or both of the following actions:
+
+ - Decompress more input starting at next_in and update next_in and avail_in
+ accordingly. If not all input can be processed (because there is not
+ enough room in the output buffer), next_in is updated and processing
+ will resume at this point for the next call of inflate().
+
+ - Provide more output starting at next_out and update next_out and avail_out
+ accordingly. inflate() always provides as much output as possible
+ (until there is no more input data or no more space in the output buffer).
+
+ Before the call of inflate(), the application should ensure that at least
+ one of the actions is possible, by providing more input and/or consuming
+ more output, and updating the next_* and avail_* values accordingly.
+ The application can consume the uncompressed output when it wants, for
+ example when the output buffer is full (avail_out == 0), or after each
+ call of inflate().
+
+ If the parameter flush is set to Z_PARTIAL_FLUSH or Z_PACKET_FLUSH,
+ inflate flushes as much output as possible to the output buffer. The
+ flushing behavior of inflate is not specified for values of the flush
+ parameter other than Z_PARTIAL_FLUSH, Z_PACKET_FLUSH or Z_FINISH, but the
+ current implementation actually flushes as much output as possible
+ anyway. For Z_PACKET_FLUSH, inflate checks that once all the input data
+ has been consumed, it is expecting to see the length field of a stored
+ block; if not, it returns Z_DATA_ERROR.
+
+ inflate() should normally be called until it returns Z_STREAM_END or an
+ error. However if all decompression is to be performed in a single step
+ (a single call of inflate), the parameter flush should be set to
+ Z_FINISH. In this case all pending input is processed and all pending
+ output is flushed; avail_out must be large enough to hold all the
+ uncompressed data. (The size of the uncompressed data may have been saved
+ by the compressor for this purpose.) The next operation on this stream must
+ be inflateEnd to deallocate the decompression state. The use of Z_FINISH
+ is never required, but can be used to inform inflate that a faster routine
+ may be used for the single inflate() call.
+
+ inflate() returns Z_OK if some progress has been made (more input
+ processed or more output produced), Z_STREAM_END if the end of the
+ compressed data has been reached and all uncompressed output has been
+ produced, Z_DATA_ERROR if the input data was corrupted, Z_STREAM_ERROR if
+ the stream structure was inconsistent (for example if next_in or next_out
+ was NULL), Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if no
+ progress is possible or if there was not enough room in the output buffer
+ when Z_FINISH is used. In the Z_DATA_ERROR case, the application may then
+ call inflateSync to look for a good compression block. */
+
+
+extern int inflateEnd OF((z_stream *strm));
+/*
+ All dynamically allocated data structures for this stream are freed.
+ This function discards any unprocessed input and does not flush any
+ pending output.
+
+ inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state
+ was inconsistent. In the error case, msg may be set but then points to a
+ static string (which must not be deallocated).
+*/
+
+ /* advanced functions */
+
+/*
+ The following functions are needed only in some special applications.
+*/
+
+extern int deflateInit2 OF((z_stream *strm,
+ int level,
+ int method,
+ int windowBits,
+ int memLevel,
+ int strategy,
+ int minCompression));
+/*
+ This is another version of deflateInit with more compression options. The
+ fields next_in, zalloc and zfree must be initialized before by the caller.
+
+ The method parameter is the compression method. It must be 8 in this
+ version of the library. (Method 9 will allow a 64K history buffer and
+ partial block flushes.)
+
+ The windowBits parameter is the base two logarithm of the window size
+ (the size of the history buffer). It should be in the range 8..15 for this
+ version of the library (the value 16 will be allowed for method 9). Larger
+ values of this parameter result in better compression at the expense of
+ memory usage. The default value is 15 if deflateInit is used instead.
+
+ The memLevel parameter specifies how much memory should be allocated
+ for the internal compression state. memLevel=1 uses minimum memory but
+ is slow and reduces compression ratio; memLevel=9 uses maximum memory
+ for optimal speed. The default value is 8. See zconf.h for total memory
+ usage as a function of windowBits and memLevel.
+
+ The strategy parameter is used to tune the compression algorithm. Use
+ the value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data
+ produced by a filter (or predictor), or Z_HUFFMAN_ONLY to force Huffman
+ encoding only (no string match). Filtered data consists mostly of small
+ values with a somewhat random distribution. In this case, the
+ compression algorithm is tuned to compress them better. The strategy
+ parameter only affects the compression ratio but not the correctness of
+ the compressed output even if it is not set appropriately.
+
+ The minCompression parameter specifies the minimum reduction in size
+ required for a compressed block to be output when Z_PACKET_FLUSH is
+ used (see the description of deflate above).
+
+ If next_in is not null, the library will use this buffer to hold also
+ some history information; the buffer must either hold the entire input
+ data, or have at least 1<<(windowBits+1) bytes and be writable. If next_in
+ is null, the library will allocate its own history buffer (and leave next_in
+ null). next_out need not be provided here but must be provided by the
+ application for the next call of deflate().
+
+ If the history buffer is provided by the application, next_in must
+ must never be changed by the application since the compressor maintains
+ information inside this buffer from call to call; the application
+ must provide more input only by increasing avail_in. next_in is always
+ reset by the library in this case.
+
+ deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was
+ not enough memory, Z_STREAM_ERROR if a parameter is invalid (such as
+ an invalid method). msg is set to null if there is no error message.
+ deflateInit2 does not perform any compression: this will be done by
+ deflate().
+*/
+
+extern int deflateCopy OF((z_stream *dest,
+ z_stream *source));
+/*
+ Sets the destination stream as a complete copy of the source stream. If
+ the source stream is using an application-supplied history buffer, a new
+ buffer is allocated for the destination stream. The compressed output
+ buffer is always application-supplied. It's the responsibility of the
+ application to provide the correct values of next_out and avail_out for the
+ next call of deflate.
+
+ This function is useful when several compression strategies will be
+ tried, for example when there are several ways of pre-processing the input
+ data with a filter. The streams that will be discarded should then be freed
+ by calling deflateEnd. Note that deflateCopy duplicates the internal
+ compression state which can be quite large, so this strategy is slow and
+ can consume lots of memory.
+
+ deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_STREAM_ERROR if the source stream state was inconsistent
+ (such as zalloc being NULL). msg is left unchanged in both source and
+ destination.
+*/
+
+extern int deflateReset OF((z_stream *strm));
+/*
+ This function is equivalent to deflateEnd followed by deflateInit,
+ but does not free and reallocate all the internal compression state.
+ The stream will keep the same compression level and any other attributes
+ that may have been set by deflateInit2.
+
+ deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
+ stream state was inconsistent (such as zalloc or state being NULL).
+*/
+
+extern int inflateInit2 OF((z_stream *strm,
+ int windowBits));
+/*
+ This is another version of inflateInit with more compression options. The
+ fields next_out, zalloc and zfree must be initialized before by the caller.
+
+ The windowBits parameter is the base two logarithm of the maximum window
+ size (the size of the history buffer). It should be in the range 8..15 for
+ this version of the library (the value 16 will be allowed soon). The
+ default value is 15 if inflateInit is used instead. If a compressed stream
+ with a larger window size is given as input, inflate() will return with
+ the error code Z_DATA_ERROR instead of trying to allocate a larger window.
+
+ If next_out is not null, the library will use this buffer for the history
+ buffer; the buffer must either be large enough to hold the entire output
+ data, or have at least 1<<windowBits bytes. If next_out is null, the
+ library will allocate its own buffer (and leave next_out null). next_in
+ need not be provided here but must be provided by the application for the
+ next call of inflate().
+
+ If the history buffer is provided by the application, next_out must
+ never be changed by the application since the decompressor maintains
+ history information inside this buffer from call to call; the application
+ can only reset next_out to the beginning of the history buffer when
+ avail_out is zero and all output has been consumed.
+
+ inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was
+ not enough memory, Z_STREAM_ERROR if a parameter is invalid (such as
+ windowBits < 8). msg is set to null if there is no error message.
+ inflateInit2 does not perform any decompression: this will be done by
+ inflate().
+*/
+
+extern int inflateSync OF((z_stream *strm));
+/*
+ Skips invalid compressed data until the special marker (see deflate()
+ above) can be found, or until all available input is skipped. No output
+ is provided.
+
+ inflateSync returns Z_OK if the special marker has been found, Z_BUF_ERROR
+ if no more input was provided, Z_DATA_ERROR if no marker has been found,
+ or Z_STREAM_ERROR if the stream structure was inconsistent. In the success
+ case, the application may save the current current value of total_in which
+ indicates where valid compressed data was found. In the error case, the
+ application may repeatedly call inflateSync, providing more input each time,
+ until success or end of the input data.
+*/
+
+extern int inflateReset OF((z_stream *strm));
+/*
+ This function is equivalent to inflateEnd followed by inflateInit,
+ but does not free and reallocate all the internal decompression state.
+ The stream will keep attributes that may have been set by inflateInit2.
+
+ inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
+ stream state was inconsistent (such as zalloc or state being NULL).
+*/
+
+extern int inflateIncomp OF((z_stream *strm));
+/*
+ This function adds the data at next_in (avail_in bytes) to the output
+ history without performing any output. There must be no pending output,
+ and the decompressor must be expecting to see the start of a block.
+ Calling this function is equivalent to decompressing a stored block
+ containing the data at next_in (except that the data is not output).
+*/
+
+ /* checksum functions */
+
+/*
+ This function is not related to compression but is exported
+ anyway because it might be useful in applications using the
+ compression library.
+*/
+
+extern uLong adler32 OF((uLong adler, Bytef *buf, uInt len));
+
+/*
+ Update a running Adler-32 checksum with the bytes buf[0..len-1] and
+ return the updated checksum. If buf is NULL, this function returns
+ the required initial value for the checksum.
+ An Adler-32 checksum is almost as reliable as a CRC32 but can be computed
+ much faster. Usage example:
+
+ uLong adler = adler32(0L, Z_NULL, 0);
+
+ while (read_buffer(buffer, length) != EOF) {
+ adler = adler32(adler, buffer, length);
+ }
+ if (adler != original_adler) error();
+*/
+
+#ifndef _Z_UTIL_H
+ struct internal_state {int dummy;}; /* hack for buggy compilers */
+#endif
+
+#endif /* _ZLIB_H */
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppstats/Makefile b/usr/src/cmd/cmd-inet/usr.bin/pppstats/Makefile
new file mode 100644
index 0000000000..73abc7399a
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppstats/Makefile
@@ -0,0 +1,36 @@
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 2000-2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+# cmd/cmd-inet/usr.bin/pppstats/Makefile
+#
+
+PROG= pppstats
+OBJS= pppstats.o
+SRCS= $(OBJS:%.o=%.c)
+
+include ../../../Makefile.cmd
+
+PPPFLAGS += -DSTREAMS -DINTERNAL_BUILD -DSOL2
+CPPFLAGS += $(PPPFLAGS)
+
+.KEEP_STATE:
+
+.PARALLEL: $(OBJS)
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+ $(LINK.c) $(OBJS) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+install: all $(ROOTPROG)
+
+clean:
+ $(RM) $(OBJS)
+
+lint: lint_SRCS
+
+include ../../../Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/usr.bin/pppstats/pppstats.c b/usr/src/cmd/cmd-inet/usr.bin/pppstats/pppstats.c
new file mode 100644
index 0000000000..b29c7a80d4
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/pppstats/pppstats.c
@@ -0,0 +1,592 @@
+/*
+ * print PPP statistics:
+ * pppstats [-a|-d] [-v|-r|-z] [-c count] [-w wait] [interface]
+ *
+ * -a Show absolute values rather than deltas
+ * -d Show data rate (kB/s) rather than bytes
+ * -v Show more stats for VJ TCP header compression
+ * -r Show compression ratio
+ * -z Show compression statistics instead of default display
+ *
+ * History:
+ * perkins@cps.msu.edu: Added compression statistics and alternate
+ * display. 11/94
+ * Brad Parker (brad@cayman.com) 6/92
+ *
+ * from the original "slstats" by Van Jacobson
+ *
+ * Copyright (c) 2000-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ *
+ * Copyright (c) 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef __STDC__
+#define const
+#endif
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifndef lint
+static const char rcsid[] = "$Id: pppstats.c,v 1.27 1999/08/13 06:46:23 paulus Exp $";
+#endif
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#ifndef STREAMS
+#if defined(_linux_) && defined(__powerpc__) \
+ && (__GLIBC__ == 2 && __GLIBC_MINOR__ == 0)
+/* kludge alert! */
+#undef __GLIBC__
+#endif
+#include <sys/socket.h> /* *BSD, Linux, NeXT, Ultrix etc. */
+#ifndef _linux_
+#include <net/if.h>
+#include <net/ppp_defs.h>
+#include <net/if_ppp.h>
+#else
+/* Linux */
+#if __GLIBC__ >= 2
+#include <asm/types.h> /* glibc 2 conflicts with linux/types.h */
+#include <net/if.h>
+#else
+#include <linux/types.h>
+#include <linux/if.h>
+#endif
+#include <linux/ppp_defs.h>
+#include <linux/if_ppp.h>
+#endif /* _linux_ */
+
+#else /* STREAMS */
+#include <sys/stropts.h> /* SVR4, Solaris 2, SunOS 4, OSF/1, etc. */
+#include <net/ppp_defs.h>
+#include <net/pppio.h>
+
+#ifdef PPPIO_GETSTAT64
+#define ppp_stats64 ppp_stats64
+#endif
+#endif /* STREAMS */
+
+#ifndef ppp_stats64
+#define ppp_stats64 ppp_stats
+#endif
+
+static int vflag, rflag, zflag; /* select type of display */
+static int aflag; /* print absolute values, not deltas */
+static int dflag; /* print data rates, not bytes */
+static int interval, count;
+static int infinite;
+static int unit;
+static int s; /* socket or /dev/ppp file descriptor */
+static int signalled; /* set if alarm goes off "early" */
+static char *progname;
+static char *interface;
+
+#if defined(SUNOS4) || defined(ULTRIX) || defined(NeXT)
+extern int optind;
+extern char *optarg;
+#endif
+
+/*
+ * If PPP_DRV_NAME is not defined, use the legacy "ppp" as the
+ * device name.
+ */
+#if !defined(PPP_DRV_NAME)
+#define PPP_DRV_NAME "ppp"
+#endif /* !defined(PPP_DRV_NAME) */
+
+static void usage __P((void));
+static void catchalarm __P((int));
+static void get_ppp_stats __P((struct ppp_stats64 *));
+static void get_ppp_cstats __P((struct ppp_comp_stats *));
+static void intpr __P((void));
+
+int main __P((int, char *argv[]));
+
+static void
+usage()
+{
+ (void) fprintf(stderr,
+ "Usage: %s [-a|-d] [-v|-r|-z] [-c count] [-w wait] [interface]\n",
+ progname);
+ exit(1);
+}
+
+/*
+ * Called if an interval expires before intpr has completed a loop.
+ * Sets a flag to not wait for the alarm.
+ */
+/* ARGSUSED */
+static void
+catchalarm(arg)
+ int arg;
+{
+ signalled = 1;
+}
+
+
+#ifndef STREAMS
+static void
+get_ppp_stats(curp)
+ struct ppp_stats64 *curp;
+{
+ struct ifpppstatsreq req;
+
+ (void) memset (&req, 0, sizeof (req));
+
+#ifdef _linux_
+ req.stats_ptr = (caddr_t) &req.stats;
+#undef ifr_name
+#define ifr_name ifr__name
+#endif
+
+ strncpy(req.ifr_name, interface, sizeof(req.ifr_name));
+ if (ioctl(s, SIOCGPPPSTATS, &req) < 0) {
+ (void) fprintf(stderr, "%s: ", progname);
+ if (errno == ENOTTY)
+ (void) fprintf(stderr, "kernel support missing\n");
+ else
+ perror("couldn't get PPP statistics");
+ exit(1);
+ }
+ *curp = req.stats;
+}
+
+static void
+get_ppp_cstats(csp)
+ struct ppp_comp_stats *csp;
+{
+ struct ifpppcstatsreq creq;
+
+ (void) memset (&creq, 0, sizeof (creq));
+
+#ifdef _linux_
+ creq.stats_ptr = (caddr_t) &creq.stats;
+#undef ifr_name
+#define ifr_name ifr__name
+#endif
+
+ strncpy(creq.ifr_name, interface, sizeof(creq.ifr_name));
+ if (ioctl(s, SIOCGPPPCSTATS, &creq) < 0) {
+ (void) fprintf(stderr, "%s: ", progname);
+ if (errno == ENOTTY) {
+ (void) fprintf(stderr, "no kernel compression support\n");
+ if (zflag)
+ exit(1);
+ rflag = 0;
+ } else {
+ perror("couldn't get PPP compression stats");
+ exit(1);
+ }
+ }
+
+#ifdef _linux_
+ if (creq.stats.c.bytes_out == 0) {
+ creq.stats.c.bytes_out = creq.stats.c.comp_bytes + creq.stats.c.inc_bytes;
+ creq.stats.c.in_count = creq.stats.c.unc_bytes;
+ }
+ if (creq.stats.c.bytes_out == 0)
+ creq.stats.c.ratio = 0.0;
+ else
+ creq.stats.c.ratio = 256.0 * creq.stats.c.in_count /
+ creq.stats.c.bytes_out;
+
+ if (creq.stats.d.bytes_out == 0) {
+ creq.stats.d.bytes_out = creq.stats.d.comp_bytes + creq.stats.d.inc_bytes;
+ creq.stats.d.in_count = creq.stats.d.unc_bytes;
+ }
+ if (creq.stats.d.bytes_out == 0)
+ creq.stats.d.ratio = 0.0;
+ else
+ creq.stats.d.ratio = 256.0 * creq.stats.d.in_count /
+ creq.stats.d.bytes_out;
+#endif
+
+ *csp = creq.stats;
+}
+
+#else /* STREAMS */
+
+static int
+strioctl(fd, cmd, ptr, ilen, olen)
+ int fd, cmd, ilen, olen;
+ char *ptr;
+{
+ struct strioctl str;
+
+ str.ic_cmd = cmd;
+ str.ic_timout = 0;
+ str.ic_len = ilen;
+ str.ic_dp = ptr;
+ if (ioctl(fd, I_STR, &str) == -1)
+ return -1;
+ if (str.ic_len != olen)
+ (void) fprintf(stderr,
+ "strioctl: expected %d bytes, got %d for cmd %x\n",
+ olen, str.ic_len, cmd);
+ return 0;
+}
+
+static void
+get_ppp_stats(curp)
+ struct ppp_stats64 *curp;
+{
+#ifdef PPPIO_GETSTAT64
+ struct ppp_stats oldstat;
+ if (strioctl(s, PPPIO_GETSTAT64, (char *)curp, 0, sizeof(*curp)) >= 0)
+ return;
+ if (strioctl(s, PPPIO_GETSTAT, (char *)&oldstat, 0, sizeof(oldstat)) >= 0) {
+ curp->p.ppp_ibytes = oldstat.p.ppp_ibytes;
+ curp->p.ppp_ipackets = oldstat.p.ppp_ipackets;
+ curp->p.ppp_ierrors = oldstat.p.ppp_ierrors;
+ curp->p.ppp_obytes = oldstat.p.ppp_obytes;
+ curp->p.ppp_opackets = oldstat.p.ppp_opackets;
+ curp->p.ppp_oerrors = oldstat.p.ppp_oerrors;
+ curp->vj = oldstat.vj;
+ return;
+ }
+#else
+ if (strioctl(s, PPPIO_GETSTAT, (char *)curp, 0, sizeof(*curp)) >= 0)
+ return;
+#endif
+
+ (void) fprintf(stderr, "%s: ", progname);
+ if (errno == EINVAL)
+ (void) fprintf(stderr, "kernel support missing\n");
+ else
+ perror("couldn't get PPP statistics");
+ exit(1);
+}
+
+static void
+get_ppp_cstats(csp)
+ struct ppp_comp_stats *csp;
+{
+ if (strioctl(s, PPPIO_GETCSTAT, (char *)csp, 0, sizeof(*csp)) < 0) {
+ (void) fprintf(stderr, "%s: ", progname);
+ if (errno == ENOTTY) {
+ (void) fprintf(stderr, "no kernel compression support\n");
+ if (zflag)
+ exit(1);
+ rflag = 0;
+ } else {
+ perror("couldn't get PPP compression statistics");
+ exit(1);
+ }
+ }
+}
+
+#endif /* STREAMS */
+
+#define MAX0(a) ((int)(a) > 0? (a): 0)
+#define V(offset) MAX0(cur.offset - old.offset)
+#define W(offset) MAX0(ccs.offset - ocs.offset)
+
+#define RATIO(c, i, u) ((c) == 0? 1.0: (u) / ((double)(c) + (i)))
+#define CRATE(x) RATIO(W(x.comp_bytes), W(x.inc_bytes), W(x.unc_bytes))
+
+#define KBPS(n) ((n) / (interval * 1000.0))
+
+/*
+ * Print a running summary of interface statistics.
+ * Repeat display every interval seconds, showing statistics
+ * collected over that interval. Assumes that interval is non-zero.
+ * First line printed is cumulative.
+ */
+static void
+intpr()
+{
+ register int line = 0;
+ sigset_t oldmask, mask;
+ char *bunit;
+ int ratef = 0;
+ struct ppp_stats64 cur, old;
+ struct ppp_comp_stats ccs, ocs;
+
+ (void) memset(&old, 0, sizeof(old));
+ (void) memset(&ocs, 0, sizeof(ocs));
+
+ for (;;) {
+ get_ppp_stats(&cur);
+ if (zflag || rflag)
+ get_ppp_cstats(&ccs);
+
+ (void)signal(SIGALRM, catchalarm);
+ signalled = 0;
+ (void)alarm(interval);
+
+ if ((line % 20) == 0) {
+ if (zflag) {
+ (void) printf("IN: COMPRESSED INCOMPRESSIBLE COMP | ");
+ (void) printf("OUT: COMPRESSED INCOMPRESSIBLE COMP\n");
+ bunit = dflag? "KB/S": "BYTE";
+ (void) printf(" %s PACK %s PACK RATIO | ", bunit,
+ bunit);
+ (void) printf(" %s PACK %s PACK RATIO", bunit,
+ bunit);
+ } else {
+ (void) printf("%8.8s %6.6s %6.6s",
+ "IN", "PACK", "VJCOMP");
+
+ if (!rflag)
+ (void) printf(" %6.6s %6.6s", "VJUNC", "VJERR");
+ if (vflag)
+ (void) printf(" %6.6s %6.6s", "VJTOSS", "NON-VJ");
+ if (rflag)
+ (void) printf(" %6.6s %6.6s", "RATIO", "UBYTE");
+ (void) printf(" | %8.8s %6.6s %6.6s",
+ "OUT", "PACK", "VJCOMP");
+
+ if (!rflag)
+ (void) printf(" %6.6s %6.6s", "VJUNC", "NON-VJ");
+ if (vflag)
+ (void) printf(" %6.6s %6.6s", "VJSRCH", "VJMISS");
+ if (rflag)
+ (void) printf(" %6.6s %6.6s", "RATIO", "UBYTE");
+ }
+ (void) putchar('\n');
+ }
+
+ if (zflag) {
+ if (ratef) {
+ (void) printf("%8.3f %6u %8.3f %6u %6.2f",
+ KBPS(W(d.comp_bytes)),
+ W(d.comp_packets),
+ KBPS(W(d.inc_bytes)),
+ W(d.inc_packets),
+ ccs.d.ratio / 256.0);
+ (void) printf(" | %8.3f %6u %8.3f %6u %6.2f",
+ KBPS(W(c.comp_bytes)),
+ W(c.comp_packets),
+ KBPS(W(c.inc_bytes)),
+ W(c.inc_packets),
+ ccs.c.ratio / 256.0);
+ } else {
+ (void) printf("%8u %6u %8u %6u %6.2f",
+ W(d.comp_bytes),
+ W(d.comp_packets),
+ W(d.inc_bytes),
+ W(d.inc_packets),
+ ccs.d.ratio / 256.0);
+ (void) printf(" | %8u %6u %8u %6u %6.2f",
+ W(c.comp_bytes),
+ W(c.comp_packets),
+ W(c.inc_bytes),
+ W(c.inc_packets),
+ ccs.c.ratio / 256.0);
+ }
+
+ } else {
+ if (ratef)
+ (void) printf("%8.3f", KBPS(V(p.ppp_ibytes)));
+ else
+ (void) printf("%8" PPP_COUNTER_F, V(p.ppp_ibytes));
+ (void) printf(" %6" PPP_COUNTER_F " %6u",
+ V(p.ppp_ipackets),
+ V(vj.vjs_compressedin));
+ if (!rflag)
+ (void) printf(" %6u %6u",
+ V(vj.vjs_uncompressedin),
+ V(vj.vjs_errorin));
+ if (vflag)
+ (void) printf(" %6u %6" PPP_COUNTER_F,
+ V(vj.vjs_tossed),
+ V(p.ppp_ipackets) - V(vj.vjs_compressedin)
+ - V(vj.vjs_uncompressedin) - V(vj.vjs_errorin));
+ if (rflag) {
+ (void) printf(" %6.2f ", CRATE(d));
+ if (ratef)
+ (void) printf("%6.2f", KBPS(W(d.unc_bytes)));
+ else
+ (void) printf("%6u", W(d.unc_bytes));
+ }
+ if (ratef)
+ (void) printf(" | %8.3f", KBPS(V(p.ppp_obytes)));
+ else
+ (void) printf(" | %8" PPP_COUNTER_F, V(p.ppp_obytes));
+ (void) printf(" %6" PPP_COUNTER_F " %6u",
+ V(p.ppp_opackets),
+ V(vj.vjs_compressed));
+ if (!rflag)
+ (void) printf(" %6u %6" PPP_COUNTER_F,
+ V(vj.vjs_packets) - V(vj.vjs_compressed),
+ V(p.ppp_opackets) - V(vj.vjs_packets));
+ if (vflag)
+ (void) printf(" %6u %6u",
+ V(vj.vjs_searches),
+ V(vj.vjs_misses));
+ if (rflag) {
+ (void) printf(" %6.2f ", CRATE(c));
+ if (ratef)
+ (void) printf("%6.2f", KBPS(W(c.unc_bytes)));
+ else
+ (void) printf("%6u", W(c.unc_bytes));
+ }
+
+ }
+
+ (void) putchar('\n');
+ (void) fflush(stdout);
+ line++;
+
+ count--;
+ if (!infinite && !count)
+ break;
+
+ (void) sigemptyset(&mask);
+ (void) sigaddset(&mask, SIGALRM);
+ (void) sigprocmask(SIG_BLOCK, &mask, &oldmask);
+ if (!signalled) {
+ (void) sigemptyset(&mask);
+ (void) sigsuspend(&mask);
+ }
+ (void) sigprocmask(SIG_SETMASK, &oldmask, NULL);
+ signalled = 0;
+ (void)alarm(interval);
+
+ if (!aflag) {
+ old = cur;
+ ocs = ccs;
+ ratef = dflag;
+ }
+ }
+}
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int c;
+#ifdef STREAMS
+ char *dev;
+#endif
+
+ interface = PPP_DRV_NAME "0";
+ if ((progname = strrchr(argv[0], '/')) == NULL)
+ progname = argv[0];
+ else
+ ++progname;
+
+ while ((c = getopt(argc, argv, "advrzc:w:")) != -1) {
+ switch (c) {
+ case 'a':
+ ++aflag;
+ break;
+ case 'd':
+ ++dflag;
+ break;
+ case 'v':
+ ++vflag;
+ break;
+ case 'r':
+ ++rflag;
+ break;
+ case 'z':
+ ++zflag;
+ break;
+ case 'c':
+ count = atoi(optarg);
+ if (count <= 0)
+ usage();
+ break;
+ case 'w':
+ interval = atoi(optarg);
+ if (interval <= 0)
+ usage();
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (!interval && count)
+ interval = 5;
+ if (interval && !count)
+ infinite = 1;
+ if (!interval && !count)
+ count = 1;
+ if (aflag)
+ dflag = 0;
+
+ if (argc > 1)
+ usage();
+ if (argc > 0)
+ interface = argv[0];
+
+ if (sscanf(interface, PPP_DRV_NAME "%d", &unit) != 1) {
+ (void) fprintf(stderr, "%s: invalid interface '%s' specified\n",
+ progname, interface);
+ }
+
+#ifndef STREAMS
+ {
+ struct ifreq ifr;
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ (void) fprintf(stderr, "%s: ", progname);
+ perror("couldn't create IP socket");
+ exit(1);
+ }
+
+#ifdef _linux_
+#undef ifr_name
+#define ifr_name ifr_ifrn.ifrn_name
+#endif
+ strncpy(ifr.ifr_name, interface, sizeof(ifr.ifr_name));
+ if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) {
+ (void) fprintf(stderr, "%s: nonexistent interface '%s' specified\n",
+ progname, interface);
+ exit(1);
+ }
+ }
+
+#else /* STREAMS */
+#ifdef __osf__
+ dev = "/dev/streams/ppp";
+#else
+ dev = "/dev/" PPP_DRV_NAME;
+#endif
+ if ((s = open(dev, O_RDONLY)) < 0) {
+ (void) fprintf(stderr, "%s: couldn't open ", progname);
+ perror(dev);
+ exit(1);
+ }
+ if (strioctl(s, PPPIO_ATTACH, (char *)&unit, sizeof(int), 0) < 0) {
+ (void) fprintf(stderr, "%s: " PPP_DRV_NAME "%d is not available\n",
+ progname, unit);
+ exit(1);
+ }
+
+#endif /* STREAMS */
+
+ intpr();
+ return (0);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/rcp.c b/usr/src/cmd/cmd-inet/usr.bin/rcp.c
new file mode 100644
index 0000000000..b21f426e69
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/rcp.c
@@ -0,0 +1,2087 @@
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Copyright (c) 1983 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ */
+
+#define _FILE_OFFSET_BITS 64
+
+/*
+ * rcp
+ */
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/acl.h>
+#include <dirent.h>
+#include <signal.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <pwd.h>
+#include <netdb.h>
+#include <wchar.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <locale.h>
+#include <strings.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <priv_utils.h>
+#include <sys/sendfile.h>
+#include <sys/sysmacros.h>
+#include <sys/wait.h>
+
+/*
+ * It seems like Berkeley got these from pathnames.h?
+ */
+#define _PATH_RSH "/usr/bin/rsh"
+#define _PATH_CP "/usr/bin/cp"
+#define _PATH_BSHELL "/usr/bin/sh"
+
+#define ACL_FAIL 1
+#define ACL_OK 0
+#define RCP_BUFSIZE (64 * 1024)
+
+#define RCP_ACL "/usr/lib/sunw,rcp"
+ /* see PSARC/1993/004/opinion */
+
+typedef struct _buf {
+ int cnt;
+ char *buf;
+} BUF;
+
+static char *cmd_sunw;
+static struct passwd *pwd;
+static int errs;
+static int pflag;
+static uid_t userid;
+static int rem;
+static int zflag;
+static int iamremote;
+static int iamrecursive;
+static int targetshouldbedirectory;
+static int aclflag;
+static int retval = 0;
+static int portnumber = 0;
+
+static void lostconn(void);
+static char *search_char(unsigned char *, unsigned char);
+static char *removebrackets(char *);
+static char *colon(char *);
+static int response(void);
+static void usage(void);
+static void source(int, char **);
+static void sink(int, char **);
+static void toremote(char *, int, char **);
+static void tolocal(int, char **);
+static void verifydir(char *);
+static int okname(char *);
+static int susystem(char *);
+static void rsource(char *, struct stat *);
+static int sendacl(int);
+static int recvacl(int, int, int);
+static int zwrite(int, char *, int);
+static void zopen(int, int);
+static int zclose(int);
+static int notzero(char *, int);
+static BUF *allocbuf(BUF *, int, int);
+static void error(char *fmt, ...);
+
+/*
+ * As a 32 bit application, we can only transfer (2gb - 1) i.e 0x7FFFFFFF
+ * bytes of data. We would like the size to be aligned to the nearest
+ * MAXBOFFSET (8192) boundary for optimal performance.
+ */
+#define SENDFILE_SIZE 0x7FFFE000
+
+#include <k5-int.h>
+#include <profile/prof_int.h>
+#include <com_err.h>
+#include <kcmd.h>
+
+#define NULLBUF (BUF *) 0
+
+static int sock;
+static char *cmd, *cmd_orig, *cmd_sunw_orig;
+static char *krb_realm = NULL;
+static char *krb_cache = NULL;
+static char *krb_config = NULL;
+static char des_inbuf[2 * RCP_BUFSIZE];
+ /* needs to be > largest read size */
+static char des_outbuf[2 * RCP_BUFSIZE];
+ /* needs to be > largest write size */
+
+static krb5_data desinbuf, desoutbuf;
+static krb5_encrypt_block eblock; /* eblock for encrypt/decrypt */
+static krb5_keyblock *session_key; /* static key for session */
+static krb5_context bsd_context;
+static krb5_auth_context auth_context;
+static krb5_flags authopts;
+static krb5_error_code status;
+
+static void try_normal_rcp(int, char **);
+static int init_service(int);
+static char **save_argv(int, char **);
+static void answer_auth(char *, char *);
+static int desrcpwrite(int, char *, int);
+static int desrcpread(int, char *, int);
+
+/*
+ * Not sure why these two don't have their own header file declarations, but
+ * lint complains about absent declarations so place some here. Sigh.
+ */
+extern errcode_t profile_get_options_boolean(profile_t, char **,
+ profile_options_boolean *);
+extern errcode_t profile_get_options_string(profile_t, char **,
+ profile_option_strings *);
+
+static int krb5auth_flag = 0; /* Flag set, when KERBEROS is enabled */
+static int encrypt_flag = 0; /* Flag set, when encryption is enabled */
+static int encrypt_done = 0; /* Flag set, if "-x" is specified */
+static enum kcmd_proto kcmd_proto = KCMD_NEW_PROTOCOL;
+
+/* Flag set, if -PN / -PO is specified */
+static boolean_t rcmdoption_done = B_FALSE;
+
+static profile_options_boolean option[] = {
+ { "encrypt", &encrypt_flag, 0 },
+ { NULL, NULL, 0 }
+};
+
+static char *rcmdproto = NULL;
+static profile_option_strings rcmdversion[] = {
+ { "rcmd_protocol", &rcmdproto, 0 },
+ { NULL, NULL, 0 }
+};
+
+static char *realmdef[] = { "realms", NULL, "rcp", NULL };
+static char *appdef[] = { "appdefaults", "rcp", NULL };
+static char **prev_argv;
+static int prev_argc;
+
+int
+main(int argc, char *argv[])
+{
+ int ch, fflag, tflag;
+ char *targ;
+ size_t cmdsiz;
+
+ (void) setlocale(LC_ALL, "");
+
+ if (strcmp(argv[0], RCP_ACL) == 0)
+ aclflag = 1;
+
+ if (!(pwd = getpwuid(userid = getuid()))) {
+ (void) fprintf(stderr, "rcp: unknown user %d.\n",
+ (uint_t)userid);
+ return (1);
+ }
+
+ fflag = tflag = 0;
+ while ((ch = getopt(argc, argv, "axdfprtz:D:k:P:")) != EOF) {
+ switch (ch) {
+ case 'd':
+ targetshouldbedirectory = 1;
+ break;
+ case 'f': /* "from" */
+ fflag = 1;
+ if (aclflag)
+ /* ok response */
+ (void) desrcpwrite(rem, "", 1);
+ break;
+ case 'p': /* preserve access/mod times */
+ ++pflag;
+ break;
+ case 'r':
+ ++iamrecursive;
+ break;
+ case 't': /* "to" */
+ tflag = 1;
+ break;
+ case 'x':
+ if (!krb5_privacy_allowed()) {
+ (void) fprintf(stderr, gettext("rcp: "
+ "Encryption not supported.\n"));
+ return (1);
+ }
+ encrypt_flag++;
+ krb5auth_flag++;
+ encrypt_done++;
+ break;
+ case 'k':
+ if ((krb_realm = (char *)strdup(optarg)) == NULL) {
+ (void) fprintf(stderr, gettext("rcp:"
+ " Cannot malloc.\n"));
+ return (1);
+ }
+ krb5auth_flag++;
+ break;
+ case 'P':
+ if (strncmp(optarg, "O", 1) == 0) {
+ if (rcmdoption_done == B_TRUE) {
+ (void) fprintf(stderr, gettext("rcp: "
+ "Only one of -PN and -PO "
+ "allowed.\n"));
+ usage();
+ }
+ kcmd_proto = KCMD_OLD_PROTOCOL;
+ rcmdoption_done = B_TRUE;
+ } else if (strncmp(optarg, "N", 1) == 0) {
+ if (rcmdoption_done == B_TRUE) {
+ (void) fprintf(stderr, gettext("rcp: "
+ "Only one of -PN and -PO "
+ "allowed.\n"));
+ usage();
+ }
+ kcmd_proto = KCMD_NEW_PROTOCOL;
+ rcmdoption_done = B_TRUE;
+ } else {
+ usage();
+ }
+ krb5auth_flag++;
+ break;
+ case 'a':
+ krb5auth_flag++;
+ break;
+#ifdef DEBUG
+ case 'D':
+ portnumber = htons(atoi(optarg));
+ krb5auth_flag++;
+ break;
+#endif /* DEBUG */
+ case '?':
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (krb5auth_flag > 0) {
+ status = krb5_init_context(&bsd_context);
+ if (status) {
+ com_err("rcp", status,
+ gettext("while initializing krb5"));
+ return (1);
+ }
+
+ /*
+ * Set up buffers for desread and deswrite.
+ */
+ desinbuf.data = des_inbuf;
+ desoutbuf.data = des_outbuf;
+ desinbuf.length = sizeof (des_inbuf);
+ desoutbuf.length = sizeof (des_outbuf);
+ }
+
+ if (fflag || tflag)
+ if (encrypt_flag > 0)
+ (void) answer_auth(krb_config, krb_cache);
+
+ if (fflag) {
+ iamremote = 1;
+ (void) response();
+ (void) setuid(userid);
+ source(argc, argv);
+ return (errs);
+ }
+
+ if (tflag) {
+ iamremote = 1;
+ (void) setuid(userid);
+ sink(argc, argv);
+ return (errs);
+ }
+
+ if (argc < 2)
+ usage();
+
+ /* This will make "rcmd_af()" magically get the proper privilege */
+ if (__init_suid_priv(0, PRIV_NET_PRIVADDR, (char *)NULL) == -1) {
+ (void) fprintf(stderr, "rcp: must be set-uid root\n");
+ exit(1);
+ }
+
+ if (krb5auth_flag > 0) {
+ /*
+ * Get our local realm to look up local realm options.
+ */
+ status = krb5_get_default_realm(bsd_context, &realmdef[1]);
+ if (status) {
+ com_err("rcp", status,
+ gettext("while getting default realm"));
+ return (1);
+ }
+ /*
+ * See if encryption should be done for this realm
+ */
+ profile_get_options_boolean(bsd_context->profile, realmdef,
+ option);
+ /*
+ * Check the appdefaults section
+ */
+ profile_get_options_boolean(bsd_context->profile, appdef,
+ option);
+ profile_get_options_string(bsd_context->profile, appdef,
+ rcmdversion);
+ if ((encrypt_done > 0) || (encrypt_flag > 0)) {
+ if (krb5_privacy_allowed() == TRUE) {
+ encrypt_flag++;
+ } else {
+ (void) fprintf(stderr, gettext("rcp: Encryption"
+ " not supported.\n"));
+ return (1);
+ }
+ }
+
+ if ((rcmdoption_done == B_FALSE) && (rcmdproto != NULL)) {
+ if (strncmp(rcmdproto, "rcmdv2", 6) == 0) {
+ kcmd_proto = KCMD_NEW_PROTOCOL;
+ } else if (strncmp(rcmdproto, "rcmdv1", 6) == 0) {
+ kcmd_proto = KCMD_OLD_PROTOCOL;
+ } else {
+ (void) fprintf(stderr, gettext("Unrecognized "
+ "KCMD protocol (%s)"), rcmdproto);
+ return (1);
+ }
+ }
+ }
+
+ if (argc > 2)
+ targetshouldbedirectory = 1;
+
+ rem = -1;
+
+ if (portnumber == 0) {
+ if (krb5auth_flag > 0) {
+ retval = init_service(krb5auth_flag);
+ if (!retval) {
+ /*
+ * Connecting to the kshell service failed,
+ * fallback to normal rcp & reset KRB5 flags.
+ */
+ krb5auth_flag = encrypt_flag = 0;
+ encrypt_done = 0;
+ (void) init_service(krb5auth_flag);
+ }
+ }
+ else
+ (void) init_service(krb5auth_flag);
+ }
+
+#ifdef DEBUG
+ if (retval || krb5auth_flag) {
+ (void) fprintf(stderr, gettext("Kerberized rcp session, "
+ "port %d in use "), portnumber);
+ if (kcmd_proto == KCMD_OLD_PROTOCOL)
+ (void) fprintf(stderr, gettext("[kcmd ver.1]\n"));
+ else
+ (void) fprintf(stderr, gettext("[kcmd ver.2]\n"));
+ } else {
+ (void) fprintf(stderr, gettext("Normal rcp session, port %d "
+ "in use.\n"), portnumber);
+ }
+#endif /* DEBUG */
+
+ if (krb5auth_flag > 0) {
+ /*
+ * We calculate here a buffer size that can be used in the
+ * allocation of the three buffers cmd, cmd_orig and
+ * cmd_sunw_orig that are used to hold different incantations
+ * of rcp.
+ */
+ cmdsiz = MAX(sizeof ("-x rcp -r -p -d -k ") +
+ strlen(krb_realm != NULL ? krb_realm : ""),
+ sizeof (RCP_ACL " -r -p -z -d"));
+
+ if (((cmd = (char *)malloc(cmdsiz)) == NULL) ||
+ ((cmd_sunw_orig = (char *)malloc(cmdsiz)) == NULL) ||
+ ((cmd_orig = (char *)malloc(cmdsiz)) == NULL)) {
+ (void) fprintf(stderr, gettext("rcp: Cannot "
+ "malloc.\n"));
+ return (1);
+ }
+
+ (void) snprintf(cmd, cmdsiz, "%srcp %s%s%s%s%s",
+ encrypt_flag ? "-x " : "",
+
+ iamrecursive ? " -r" : "", pflag ? " -p" : "",
+ targetshouldbedirectory ? " -d" : "",
+ krb_realm != NULL ? " -k " : "",
+ krb_realm != NULL ? krb_realm : "");
+
+ /*
+ * We would use cmd-orig as the 'cmd-buffer' if kerberized
+ * rcp fails, in which case we fallback to normal rcp. We also
+ * save argc & argv for the same purpose
+ */
+ (void) snprintf(cmd_orig, cmdsiz, "rcp%s%s%s%s",
+ iamrecursive ? " -r" : "",
+ pflag ? " -p" : "",
+ zflag ? " -z" : "",
+ targetshouldbedirectory ? " -d" : "");
+
+ (void) snprintf(cmd_sunw_orig, cmdsiz, "%s%s%s%s%s", RCP_ACL,
+ iamrecursive ? " -r" : "",
+ pflag ? " -p" : "",
+ zflag ? " -z" : "",
+ targetshouldbedirectory ? " -d" : "");
+
+ prev_argc = argc;
+ prev_argv = save_argv(argc, argv);
+
+ } else {
+ cmdsiz = sizeof ("rcp -r -p -z -d");
+ if (((cmd = (char *)malloc(cmdsiz)) == NULL)) {
+ (void) fprintf(stderr, gettext("rcp: Cannot "
+ "malloc.\n"));
+ return (1);
+ }
+
+ (void) snprintf(cmd, cmdsiz, "rcp%s%s%s%s",
+ iamrecursive ? " -r" : "",
+ pflag ? " -p" : "",
+ zflag ? " -z" : "",
+ targetshouldbedirectory ? " -d" : "");
+ }
+
+ cmdsiz = sizeof (RCP_ACL " -r -p -z -d");
+ if ((cmd_sunw = (char *)malloc(cmdsiz)) == NULL) {
+ (void) fprintf(stderr, gettext("rcp: Cannot malloc.\n"));
+ return (1);
+ }
+
+ (void) snprintf(cmd_sunw, cmdsiz, "%s%s%s%s%s", RCP_ACL,
+ iamrecursive ? " -r" : "",
+ pflag ? " -p" : "",
+ zflag ? " -z" : "",
+ targetshouldbedirectory ? " -d" : "");
+
+ (void) signal(SIGPIPE, (void (*)(int))lostconn);
+
+ if (targ = colon(argv[argc - 1]))
+ toremote(targ, argc, argv);
+ else {
+ tolocal(argc, argv);
+ if (targetshouldbedirectory)
+ verifydir(argv[argc - 1]);
+ }
+
+ return (errs > 0 ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+
+static void
+toremote(char *targ, int argc, char *argv[])
+{
+ int i;
+ char *host, *src, *suser, *thost, *tuser;
+ char resp;
+ size_t buffersize;
+ char bp[RCP_BUFSIZE];
+ krb5_creds *cred;
+ buffersize = RCP_BUFSIZE;
+
+ *targ++ = 0;
+ if (*targ == 0)
+ targ = ".";
+
+ if (thost = search_char((unsigned char *)argv[argc - 1], '@')) {
+ *thost++ = 0;
+ tuser = argv[argc - 1];
+ if (*tuser == '\0')
+ tuser = NULL;
+ else if (!okname(tuser))
+ exit(1);
+ } else {
+ thost = argv[argc - 1];
+ tuser = NULL;
+ }
+ thost = removebrackets(thost);
+
+ for (i = 0; i < argc - 1; i++) {
+ src = colon(argv[i]);
+ if (src) { /* remote to remote */
+ *src++ = 0;
+ if (*src == 0)
+ src = ".";
+ host = search_char((unsigned char *)argv[i], '@');
+ if (host) {
+ *host++ = 0;
+ host = removebrackets(host);
+ suser = argv[i];
+ if (*suser == '\0') {
+ suser = pwd->pw_name;
+ } else if (!okname(suser)) {
+ errs++;
+ continue;
+ }
+ (void) snprintf(bp, buffersize,
+ "%s %s -l %s -n %s %s '%s%s%s:%s'",
+ _PATH_RSH, host, suser, cmd, src,
+ tuser ? tuser : "", tuser ? "@" : "",
+ thost, targ);
+ } else {
+ host = removebrackets(argv[i]);
+ (void) snprintf(bp, buffersize,
+ "%s %s -n %s %s '%s%s%s:%s'",
+ _PATH_RSH, host, cmd, src,
+ tuser ? tuser : "", tuser ? "@" : "",
+ thost, targ);
+ }
+ if (susystem(bp) == -1)
+ errs++;
+ } else { /* local to remote */
+ if (rem == -1) {
+ host = thost;
+ if (krb5auth_flag > 0) {
+
+ (void) snprintf(bp, buffersize,
+ "%s -t %s", cmd, targ);
+ authopts = AP_OPTS_MUTUAL_REQUIRED;
+ status = kcmd(&sock, &host,
+ portnumber,
+ pwd->pw_name,
+ tuser ? tuser :
+ pwd->pw_name,
+ bp,
+ 0,
+ "host",
+ krb_realm,
+ bsd_context,
+ &auth_context,
+ &cred,
+ 0, /* No seq # */
+ 0, /* No server seq # */
+ authopts,
+ 0, /* Not any port # */
+ &kcmd_proto);
+ if (status) {
+ /*
+ * If new protocol requested, we dont
+ * fallback to less secure ones.
+ */
+
+ if (kcmd_proto == KCMD_NEW_PROTOCOL) {
+ (void) fprintf(stderr,
+ gettext("rcp: kcmdv2 "
+ "to host %s failed - %s"
+ "\nFallback to normal "
+ "rcp denied."), host,
+ error_message(status));
+ exit(1);
+ }
+ if (status != -1) {
+ (void) fprintf(stderr,
+ gettext("rcp: kcmd to host "
+ "%s failed - %s,\n"
+ "trying normal rcp...\n\n"),
+ host, error_message(status));
+ } else {
+ (void) fprintf(stderr,
+ gettext("trying normal"
+ " rcp...\n"));
+ }
+ /*
+ * kcmd() failed, so we have to
+ * fallback to normal rcp
+ */
+ try_normal_rcp(prev_argc, prev_argv);
+ } else {
+ rem = sock;
+ session_key = &cred->keyblock;
+ if (kcmd_proto == KCMD_NEW_PROTOCOL) {
+ /* CSTYLED */
+ status = krb5_auth_con_getlocalsubkey(bsd_context, auth_context, &session_key);
+ if (status) {
+ com_err("rcp", status,
+ "determining "
+ "subkey for "
+ "session");
+ exit(1);
+ }
+ if (!session_key) {
+ com_err("rcp", 0,
+ "no subkey "
+ "negotiated for"
+ " connection");
+ exit(1);
+ }
+ }
+ eblock.crypto_entry =
+ session_key->enctype;
+ eblock.key =
+ (krb5_keyblock *)session_key;
+
+ init_encrypt(encrypt_flag,
+ bsd_context, kcmd_proto,
+ &desinbuf, &desoutbuf, CLIENT,
+ &eblock);
+ if (encrypt_flag > 0) {
+ char *s = gettext("This rcp "
+ "session is using "
+ "encryption for all "
+ "data transmissions."
+ "\r\n");
+
+ (void) write(2, s, strlen(s));
+ }
+ }
+ if (response() < 0)
+ exit(1);
+
+ }
+ else
+ {
+
+ /*
+ * ACL support: try to find out if the remote
+ * site is running acl cognizant version of
+ * rcp. A special binary name is used for this
+ * purpose.
+ */
+ aclflag = 1;
+
+ (void) snprintf(bp, buffersize, "%s -t %s",
+ cmd_sunw, targ);
+ rem = rcmd_af(&host, portnumber, pwd->pw_name,
+ tuser ? tuser : pwd->pw_name,
+ bp, 0, AF_INET6);
+ if (rem < 0)
+ exit(1);
+
+ /*
+ * This is similar to routine response().
+ * If response is not ok, treat the other
+ * side as non-acl rcp.
+ */
+ if (read(rem, &resp, sizeof (resp))
+ != sizeof (resp))
+ lostconn();
+ if (resp != 0) {
+ /*
+ * Not OK:
+ * The other side is running
+ * non-acl rcp. Try again with
+ * normal stuff
+ */
+ aclflag = 0;
+ (void) snprintf(bp, buffersize,
+ "%s -t %s", cmd, targ);
+ (void) close(rem);
+ host = thost;
+ rem = rcmd_af(&host, portnumber,
+ pwd->pw_name,
+ tuser ? tuser :
+ pwd->pw_name, bp, 0,
+ AF_INET6);
+ if (rem < 0)
+ exit(1);
+ if (response() < 0)
+ exit(1);
+ }
+ /* everything should be fine now */
+ (void) setuid(userid);
+
+ }
+
+ }
+ source(1, argv + i);
+ }
+ }
+}
+
+static void
+tolocal(int argc, char *argv[])
+{
+ int i;
+ char *host, *src, *suser, *lhost;
+ char resp;
+ size_t buffersize;
+ char bp[RCP_BUFSIZE];
+ krb5_creds *cred;
+ buffersize = RCP_BUFSIZE;
+
+ for (i = 0; i < argc - 1; i++) {
+ if (!(src = colon(argv[i]))) { /* local to local */
+ (void) snprintf(bp, buffersize, "%s%s%s%s %s %s",
+ _PATH_CP, iamrecursive ? " -r" : "",
+ pflag ? " -p" : "",
+ zflag ? " -z" : "",
+ argv[i], argv[argc - 1]);
+ if (susystem(bp) == -1)
+ errs++;
+ continue;
+ }
+ *src++ = 0;
+ if (*src == 0)
+ src = ".";
+ host = search_char((unsigned char *)argv[i], '@');
+ if (host) {
+ *host++ = 0;
+ suser = argv[i];
+ if (*suser == '\0') {
+ suser = pwd->pw_name;
+ } else if (!okname(suser)) {
+ errs++;
+ continue;
+ }
+ } else {
+ host = argv[i];
+ suser = pwd->pw_name;
+ }
+ host = removebrackets(host);
+ lhost = host;
+ if (krb5auth_flag > 0) {
+
+ (void) snprintf(bp, buffersize, "%s -f %s", cmd, src);
+ authopts = AP_OPTS_MUTUAL_REQUIRED;
+ status = kcmd(&sock, &host,
+ portnumber,
+ pwd->pw_name, suser,
+ bp,
+ 0, /* &rfd2 */
+ "host",
+ krb_realm,
+ bsd_context,
+ &auth_context,
+ &cred,
+ 0, /* No seq # */
+ 0, /* No server seq # */
+ authopts,
+ 1, /* Not any port # */
+ &kcmd_proto);
+ if (status) {
+ /*
+ * If new protocol requested, we dont
+ * fallback to less secure ones.
+ */
+ if (kcmd_proto == KCMD_NEW_PROTOCOL) {
+ (void) fprintf(stderr, gettext("rcp: kcmdv2 "
+ "to host %s failed - %s\n"
+ "Fallback to normal rcp denied."),
+ host, error_message(status));
+ exit(1);
+ }
+ if (status != -1) {
+ (void) fprintf(stderr, gettext("rcp: kcmd "
+ "to host %s failed - %s,\n"
+ "trying normal rcp...\n\n"),
+ host, error_message(status));
+ } else {
+ (void) fprintf(stderr,
+ gettext("trying normal rcp...\n"));
+ }
+ /*
+ * kcmd() failed, so we have to
+ * fallback to normal rcp
+ */
+ try_normal_rcp(prev_argc, prev_argv);
+ } else {
+ rem = sock;
+ session_key = &cred->keyblock;
+ if (kcmd_proto == KCMD_NEW_PROTOCOL) {
+ status = krb5_auth_con_getlocalsubkey(
+ bsd_context, auth_context,
+ &session_key);
+ if (status) {
+ com_err("rcp", status, "determining "
+ "subkey for session");
+ exit(1);
+ }
+ if (!session_key) {
+ com_err("rcp", 0, "no subkey negotiated"
+ " for connection");
+ exit(1);
+ }
+ }
+ eblock.crypto_entry = session_key->enctype;
+ eblock.key = (krb5_keyblock *)session_key;
+
+ init_encrypt(encrypt_flag, bsd_context, kcmd_proto,
+ &desinbuf, &desoutbuf, CLIENT,
+ &eblock);
+ if (encrypt_flag > 0) {
+ char *s = gettext("This rcp "
+ "session is using DES "
+ "encryption for all "
+ "data transmissions."
+ "\r\n");
+
+ (void) write(2, s, strlen(s));
+ }
+ }
+
+ }
+ else
+ {
+
+ /*
+ * ACL support: try to find out if the remote site is
+ * running acl cognizant version of rcp.
+ */
+ aclflag = 1;
+
+ (void) snprintf(bp, buffersize, "%s -f %s", cmd_sunw, src);
+ rem = rcmd_af(&host, portnumber, pwd->pw_name, suser,
+ bp, 0, AF_INET6);
+
+ if (rem < 0) {
+ ++errs;
+ continue;
+ }
+
+ /*
+ * The remote system is supposed to send an ok response.
+ * If there are any data other than "ok", it must be error
+ * messages from the remote system. We can assume the
+ * remote system is running non-acl version rcp.
+ */
+ if (read(rem, &resp, sizeof (resp)) != sizeof (resp))
+ lostconn();
+ if (resp != 0) {
+ /*
+ * NOT ok:
+ * The other side is running non-acl rcp.
+ * Try again with normal stuff
+ */
+ aclflag = 0;
+ (void) snprintf(bp, buffersize, "%s -f %s", cmd, src);
+ (void) close(rem);
+ host = lhost;
+ rem = rcmd_af(&host, portnumber, pwd->pw_name,
+ suser, bp, 0, AF_INET6);
+ if (rem < 0) {
+ ++errs;
+ continue;
+ }
+ }
+ }
+
+ sink(1, argv + argc - 1);
+
+ (void) close(rem);
+ rem = -1;
+ }
+}
+
+
+static void
+verifydir(char *cp)
+{
+ struct stat stb;
+
+ if (stat(cp, &stb) >= 0) {
+ if ((stb.st_mode & S_IFMT) == S_IFDIR)
+ return;
+ errno = ENOTDIR;
+ }
+ error("rcp: %s: %s.\n", cp, strerror(errno));
+ exit(1);
+}
+
+static char *
+colon(char *cp)
+{
+ boolean_t is_bracket_open = B_FALSE;
+
+ for (; *cp; ++cp) {
+ if (*cp == '[')
+ is_bracket_open = B_TRUE;
+ else if (*cp == ']')
+ is_bracket_open = B_FALSE;
+ else if (*cp == ':' && !is_bracket_open)
+ return (cp);
+ else if (*cp == '/')
+ return (0);
+ }
+ return (0);
+}
+
+static int
+okname(char *cp0)
+{
+ register char *cp = cp0;
+ register int c;
+
+ do {
+ c = *cp;
+ if (c & 0200)
+ goto bad;
+ if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-')
+ goto bad;
+ } while (*++cp);
+ return (1);
+bad:
+ (void) fprintf(stderr, "rcp: invalid user name %s\n", cp0);
+ return (0);
+}
+
+
+static char *
+removebrackets(char *str)
+{
+ char *newstr = str;
+
+ if ((str[0] == '[') && (str[strlen(str) - 1] == ']')) {
+ newstr = str + 1;
+ str[strlen(str) - 1] = '\0';
+ }
+ return (newstr);
+}
+
+static int
+susystem(char *s)
+{
+ int status, pid, w;
+ register void (*istat)(), (*qstat)();
+ int pfds[2];
+ char buf[BUFSIZ];
+ int cnt;
+ boolean_t seen_stderr_traffic;
+
+ /*
+ * Due to the fact that rcp uses rsh to copy between 2 remote
+ * machines, rsh doesn't return the exit status of the remote
+ * command, and we can't modify the rcmd protocol used by rsh
+ * (for interoperability reasons) we use the hack of using any
+ * output on stderr as indication that an error occurred and
+ * that we should return a non-zero error code.
+ */
+
+ if (pipe(pfds) == -1) {
+ (void) fprintf(stderr, "Couldn't create pipe: %s\n",
+ strerror(errno));
+ return (-1);
+ }
+
+ if ((pid = vfork()) < 0) {
+ (void) close(pfds[0]);
+ (void) close(pfds[1]);
+ (void) fprintf(stderr, "Couldn't fork child process: %s\n",
+ strerror(errno));
+ return (-1);
+ } else if (pid == 0) {
+ /*
+ * Child.
+ */
+ (void) close(pfds[0]);
+ /*
+ * Send stderr messages down the pipe so that we can detect
+ * them in the parent process.
+ */
+ if (pfds[1] != STDERR_FILENO) {
+ (void) dup2(pfds[1], STDERR_FILENO);
+ (void) close(pfds[1]);
+ }
+ /*
+ * This shell does not inherit the additional privilege
+ * we have in our Permitted set.
+ */
+ (void) execl(_PATH_BSHELL, "sh", "-c", s, (char *)0);
+ _exit(127);
+ }
+ /*
+ * Parent.
+ */
+ istat = signal(SIGINT, SIG_IGN);
+ qstat = signal(SIGQUIT, SIG_IGN);
+
+ (void) close(pfds[1]);
+ seen_stderr_traffic = B_FALSE;
+ while ((cnt = read(pfds[0], buf, sizeof (buf))) > 0) {
+ /*
+ * If any data is read from the pipe the child process
+ * has output something on stderr so we set the boolean
+ * 'seen_stderr_traffic' to true, which will cause the
+ * function to return -1.
+ */
+ (void) write(STDERR_FILENO, buf, cnt);
+ seen_stderr_traffic = B_TRUE;
+ }
+ (void) close(pfds[0]);
+ while ((w = wait(&status)) != pid && w != -1)
+ ;
+ if (w == -1)
+ status = -1;
+
+ (void) signal(SIGINT, istat);
+ (void) signal(SIGQUIT, qstat);
+
+ return (seen_stderr_traffic ? -1 : status);
+}
+
+static void
+source(int argc, char *argv[])
+{
+ struct stat stb;
+ static BUF buffer;
+ BUF *bp;
+ int x, readerr, f, amt;
+ char *last, *name, buf[RCP_BUFSIZE];
+ off_t off, size, i;
+ ssize_t cnt;
+
+ for (x = 0; x < argc; x++) {
+ name = argv[x];
+ if ((f = open(name, O_RDONLY, 0)) < 0) {
+ error("rcp: %s: %s\n", name, strerror(errno));
+ continue;
+ }
+ if (fstat(f, &stb) < 0)
+ goto notreg;
+ switch (stb.st_mode&S_IFMT) {
+
+ case S_IFREG:
+ break;
+
+ case S_IFDIR:
+ if (iamrecursive) {
+ (void) close(f);
+ rsource(name, &stb);
+ continue;
+ }
+ /* FALLTHROUGH */
+ default:
+notreg:
+ (void) close(f);
+ error("rcp: %s: not a plain file\n", name);
+ continue;
+ }
+ last = rindex(name, '/');
+ if (last == 0)
+ last = name;
+ else
+ last++;
+ if (pflag) {
+ time_t mtime, atime;
+ time_t now;
+
+ /*
+ * Make it compatible with possible future
+ * versions expecting microseconds.
+ */
+ mtime = stb.st_mtime;
+ atime = stb.st_atime;
+
+ if ((mtime < 0) || (atime < 0)) {
+ now = time(NULL);
+
+ if (mtime < 0) {
+ mtime = now;
+ error("negative modification time on "
+ "%s; not preserving\n", name);
+ }
+ if (atime < 0) {
+ atime = now;
+ error("negative access time on "
+ "%s; not preserving\n", name);
+ }
+ }
+ (void) snprintf(buf, sizeof (buf), "T%ld 0 %ld 0\n",
+ mtime, atime);
+ (void) desrcpwrite(rem, buf, strlen(buf));
+ if (response() < 0) {
+ (void) close(f);
+ continue;
+ }
+ }
+ (void) snprintf(buf, sizeof (buf), "C%04o %lld %s\n",
+ (uint_t)(stb.st_mode & 07777), (longlong_t)stb.st_size,
+ last);
+ (void) desrcpwrite(rem, buf, strlen(buf));
+ if (response() < 0) {
+ (void) close(f);
+ continue;
+ }
+
+ /* ACL support: send */
+ if (aclflag) {
+ /* get acl from f and send it over */
+ if (sendacl(f) == ACL_FAIL) {
+ (void) close(f);
+ continue;
+ }
+ }
+ if ((krb5auth_flag > 0) || (iamremote == 1)) {
+ bp = allocbuf(&buffer, f, RCP_BUFSIZE);
+ if (bp == NULLBUF) {
+ (void) close(f);
+ continue;
+ }
+ readerr = 0;
+ for (i = 0; i < stb.st_size; i += bp->cnt) {
+ amt = bp->cnt;
+ if (i + amt > stb.st_size)
+ amt = stb.st_size - i;
+ if (readerr == 0 &&
+ read(f, bp->buf, amt) != amt)
+ readerr = errno;
+ (void) desrcpwrite(rem, bp->buf, amt);
+ }
+ (void) close(f);
+ if (readerr == 0)
+ (void) desrcpwrite(rem, "", 1);
+ else
+ error("rcp: %s: %s\n", name,
+ error_message(readerr));
+ } else {
+ cnt = off = 0;
+ size = stb.st_size;
+ while (size != 0) {
+ amt = MIN(size, SENDFILE_SIZE);
+ cnt = sendfile(rem, f, &off, amt);
+ if (cnt == -1)
+ break;
+ size -= cnt;
+ }
+ if (cnt == -1) {
+ error("rcp: %s: %s\n", name, strerror(errno));
+ } else {
+ (void) write(rem, "", 1);
+ }
+ (void) close(f);
+ }
+ (void) response();
+ }
+}
+
+
+static void
+rsource(char *name, struct stat *statp)
+{
+ DIR *d;
+ struct dirent *dp;
+ char *last, *vect[1];
+ char path[MAXPATHLEN];
+
+ if (!(d = opendir(name))) {
+ error("rcp: %s: %s\n", name, strerror(errno));
+ return;
+ }
+ last = rindex(name, '/');
+ if (last == 0)
+ last = name;
+ else
+ last++;
+ if (pflag) {
+ (void) snprintf(path, sizeof (path), "T%ld 0 %ld 0\n",
+ statp->st_mtime, statp->st_atime);
+ (void) desrcpwrite(rem, path, strlen(path));
+ if (response() < 0) {
+ (void) closedir(d);
+ return;
+ }
+ }
+ (void) snprintf(path, sizeof (path), "D%04o %d %s\n",
+ (uint_t)(statp->st_mode & 07777), 0, last);
+ (void) desrcpwrite(rem, path, strlen(path));
+
+ /* acl support for directory */
+ if (aclflag) {
+ /* get acl from f and send it over */
+ if (sendacl(d->dd_fd) == ACL_FAIL) {
+ (void) closedir(d);
+ return;
+ }
+ }
+
+ if (response() < 0) {
+ (void) closedir(d);
+ return;
+ }
+
+ while (dp = readdir(d)) {
+ if (dp->d_ino == 0)
+ continue;
+ if ((strcmp(dp->d_name, ".") == 0) ||
+ (strcmp(dp->d_name, "..") == 0))
+ continue;
+ if ((uint_t)strlen(name) + 1 + strlen(dp->d_name) >=
+ MAXPATHLEN - 1) {
+ error("%s/%s: name too long.\n", name, dp->d_name);
+ continue;
+ }
+ (void) snprintf(path, sizeof (path), "%s/%s",
+ name, dp->d_name);
+ vect[0] = path;
+ source(1, vect);
+ }
+ (void) closedir(d);
+ (void) desrcpwrite(rem, "E\n", 2);
+ (void) response();
+}
+
+static int
+response(void)
+{
+ register char *cp;
+ char ch, resp, rbuf[RCP_BUFSIZE];
+
+ if (desrcpread(rem, &resp, 1) != 1)
+ lostconn();
+ cp = rbuf;
+ switch (resp) {
+ case 0: /* ok */
+ return (0);
+ default:
+ *cp++ = resp;
+ /* FALLTHROUGH */
+ case 1: /* error, followed by err msg */
+ case 2: /* fatal error, "" */
+ do {
+ if (desrcpread(rem, &ch, sizeof (ch)) != sizeof (ch))
+ lostconn();
+ *cp++ = ch;
+ } while (cp < &rbuf[RCP_BUFSIZE] && ch != '\n');
+
+ if (!iamremote)
+ (void) write(STDERR_FILENO, rbuf, cp - rbuf);
+ ++errs;
+ if (resp == 1)
+ return (-1);
+ exit(1);
+ }
+ /*NOTREACHED*/
+}
+
+static void
+lostconn(void)
+{
+ if (!iamremote)
+ (void) fprintf(stderr, "rcp: lost connection\n");
+ exit(1);
+}
+
+
+static void
+sink(int argc, char *argv[])
+{
+ char *cp;
+ static BUF buffer;
+ struct stat stb;
+ struct timeval tv[2];
+ BUF *bp;
+ off_t i, j;
+ char ch, *targ, *why;
+ int amt, count, exists, first, mask, mode;
+ off_t size;
+ int ofd, setimes, targisdir, wrerr;
+ char *np, *vect[1], buf[RCP_BUFSIZE];
+ char *namebuf = NULL;
+ size_t namebuf_sz = 0;
+ size_t need;
+
+#define atime tv[0]
+#define mtime tv[1]
+#define SCREWUP(str) { why = str; goto screwup; }
+
+ setimes = targisdir = 0;
+ mask = umask(0);
+ if (!pflag)
+ (void) umask(mask);
+ if (argc != 1) {
+ error("rcp: ambiguous target\n");
+ exit(1);
+ }
+ targ = *argv;
+ if (targetshouldbedirectory)
+ verifydir(targ);
+ (void) desrcpwrite(rem, "", 1);
+
+ if (stat(targ, &stb) == 0 && (stb.st_mode & S_IFMT) == S_IFDIR)
+ targisdir = 1;
+ for (first = 1; ; first = 0) {
+ cp = buf;
+ if (desrcpread(rem, cp, 1) <= 0) {
+ if (namebuf != NULL)
+ free(namebuf);
+ return;
+ }
+
+ if (*cp++ == '\n')
+ SCREWUP("unexpected <newline>");
+ do {
+ if (desrcpread(rem, &ch, sizeof (ch)) != sizeof (ch))
+ SCREWUP("lost connection");
+ *cp++ = ch;
+ } while (cp < &buf[RCP_BUFSIZE - 1] && ch != '\n');
+ *cp = 0;
+
+ if (buf[0] == '\01' || buf[0] == '\02') {
+ if (iamremote == 0)
+ (void) write(STDERR_FILENO, buf + 1,
+ strlen(buf + 1));
+ if (buf[0] == '\02')
+ exit(1);
+ errs++;
+ continue;
+ }
+ if (buf[0] == 'E') {
+ (void) desrcpwrite(rem, "", 1);
+ if (namebuf != NULL)
+ free(namebuf);
+ return;
+ }
+
+ if (ch == '\n')
+ *--cp = 0;
+ cp = buf;
+ if (*cp == 'T') {
+ setimes++;
+ cp++;
+ mtime.tv_sec = strtol(cp, &cp, 0);
+ if (*cp++ != ' ')
+ SCREWUP("mtime.sec not delimited");
+ mtime.tv_usec = strtol(cp, &cp, 0);
+ if (*cp++ != ' ')
+ SCREWUP("mtime.usec not delimited");
+ atime.tv_sec = strtol(cp, &cp, 0);
+ if (*cp++ != ' ')
+ SCREWUP("atime.sec not delimited");
+ atime.tv_usec = strtol(cp, &cp, 0);
+ if (*cp++ != '\0')
+ SCREWUP("atime.usec not delimited");
+ (void) desrcpwrite(rem, "", 1);
+ continue;
+ }
+ if (*cp != 'C' && *cp != 'D') {
+ /*
+ * Check for the case "rcp remote:foo\* local:bar".
+ * In this case, the line "No match." can be returned
+ * by the shell before the rcp command on the remote is
+ * executed so the ^Aerror_message convention isn't
+ * followed.
+ */
+ if (first) {
+ error("%s\n", cp);
+ exit(1);
+ }
+ SCREWUP("expected control record");
+ }
+ mode = 0;
+ for (++cp; cp < buf + 5; cp++) {
+ if (*cp < '0' || *cp > '7')
+ SCREWUP("bad mode");
+ mode = (mode << 3) | (*cp - '0');
+ }
+ if (*cp++ != ' ')
+ SCREWUP("mode not delimited");
+ size = 0;
+ while (isdigit(*cp))
+ size = size * 10 + (*cp++ - '0');
+ if (*cp++ != ' ')
+ SCREWUP("size not delimited");
+ if (targisdir) {
+ need = strlen(targ) + sizeof ("/") + strlen(cp);
+ if (need > namebuf_sz) {
+ if ((namebuf = realloc(namebuf, need)) == NULL) {
+ error("rcp: out of memory\n");
+ exit(1);
+ }
+ namebuf_sz = need;
+ }
+ (void) snprintf(namebuf, need, "%s%s%s", targ,
+ *targ ? "/" : "", cp);
+ np = namebuf;
+ } else {
+ np = targ;
+ }
+
+ exists = stat(np, &stb) == 0;
+ if (buf[0] == 'D') {
+ if (exists) {
+ if ((stb.st_mode&S_IFMT) != S_IFDIR) {
+ if (aclflag) {
+ /*
+ * consume acl in the pipe
+ * fd = -1 to indicate the
+ * special case
+ */
+ if (recvacl(-1, exists, pflag)
+ == ACL_FAIL) {
+ goto bad;
+ }
+ }
+ errno = ENOTDIR;
+ goto bad;
+ }
+ if (pflag)
+ (void) chmod(np, mode);
+ } else if (mkdir(np, mode) < 0) {
+ if (aclflag) {
+ /* consume acl in the pipe */
+ (void) recvacl(-1, exists, pflag);
+ }
+ goto bad;
+ }
+
+ /* acl support for directories */
+ if (aclflag) {
+ int dfd;
+
+ if ((dfd = open(np, O_RDONLY)) == -1)
+ goto bad;
+
+ /* get acl and set it to ofd */
+ if (recvacl(dfd, exists, pflag) == ACL_FAIL) {
+ (void) close(dfd);
+ if (!exists)
+ (void) rmdir(np);
+ goto bad;
+ }
+ (void) close(dfd);
+ }
+
+ vect[0] = np;
+ sink(1, vect);
+ if (setimes) {
+ setimes = 0;
+ if (utimes(np, tv) < 0)
+ error("rcp: can't set times on %s: %s\n",
+ np, strerror(errno));
+ }
+ continue;
+ }
+
+ if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) {
+bad:
+ error("rcp: %s: %s\n", np, strerror(errno));
+ continue;
+ }
+
+ /*
+ * If the output file exists we have to force zflag off
+ * to avoid erroneously seeking past old data.
+ */
+ zopen(ofd, zflag && !exists);
+
+ if (exists && pflag)
+ (void) fchmod(ofd, mode);
+
+ (void) desrcpwrite(rem, "", 1);
+
+ /*
+ * ACL support: receiving
+ */
+ if (aclflag) {
+ /* get acl and set it to ofd */
+ if (recvacl(ofd, exists, pflag) == ACL_FAIL) {
+ (void) close(ofd);
+ if (!exists)
+ (void) unlink(np);
+ continue;
+ }
+ }
+
+ if ((bp = allocbuf(&buffer, ofd, RCP_BUFSIZE)) == 0) {
+ (void) close(ofd);
+ continue;
+ }
+ cp = bp->buf;
+ count = 0;
+ wrerr = 0;
+ for (i = 0; i < size; i += RCP_BUFSIZE) {
+ amt = RCP_BUFSIZE;
+ if (i + amt > size)
+ amt = size - i;
+ count += amt;
+ do {
+ j = desrcpread(rem, cp, amt);
+ if (j <= 0) {
+ int sverrno = errno;
+
+ /*
+ * Connection to supplier lost.
+ * Truncate file to correspond
+ * to amount already transferred.
+ *
+ * Note that we must call ftruncate()
+ * before any call to error() (which
+ * might result in a SIGPIPE and
+ * sudden death before we have a chance
+ * to correct the file's size).
+ */
+ size = lseek(ofd, 0, SEEK_CUR);
+ if ((ftruncate(ofd, size) == -1) &&
+ (errno != EINVAL) &&
+ (errno != EACCES))
+#define TRUNCERR "rcp: can't truncate %s: %s\n"
+ error(TRUNCERR, np,
+ strerror(errno));
+ error("rcp: %s\n",
+ j ? strerror(sverrno) :
+ "dropped connection");
+ (void) close(ofd);
+ exit(1);
+ }
+ amt -= j;
+ cp += j;
+ } while (amt > 0);
+ if (count == bp->cnt) {
+ cp = bp->buf;
+ if (wrerr == 0 &&
+ zwrite(ofd, cp, count) < 0)
+ wrerr++;
+ count = 0;
+ }
+ }
+ if (count != 0 && wrerr == 0 &&
+ zwrite(ofd, bp->buf, count) < 0)
+ wrerr++;
+ if (zclose(ofd) < 0)
+ wrerr++;
+
+
+ if ((ftruncate(ofd, size) == -1) && (errno != EINVAL) &&
+ (errno != EACCES)) {
+ error(TRUNCERR, np, strerror(errno));
+ }
+ (void) close(ofd);
+ (void) response();
+ if (setimes) {
+ setimes = 0;
+ if (utimes(np, tv) < 0)
+ error("rcp: can't set times on %s: %s\n",
+ np, strerror(errno));
+ }
+ if (wrerr)
+ error("rcp: %s: %s\n", np, strerror(errno));
+ else
+ (void) desrcpwrite(rem, "", 1);
+ }
+screwup:
+ error("rcp: protocol screwup: %s\n", why);
+ exit(1);
+}
+
+#ifndef roundup
+#define roundup(x, y) ((((x)+((y)-1))/(y))*(y))
+#endif /* !roundup */
+
+static BUF *
+allocbuf(BUF *bp, int fd, int blksize)
+{
+ struct stat stb;
+ int size;
+
+ if (fstat(fd, &stb) < 0) {
+ error("rcp: fstat: %s\n", strerror(errno));
+ return (0);
+ }
+ size = roundup(stb.st_blksize, blksize);
+ if (size == 0)
+ size = blksize;
+ if (bp->cnt < size) {
+ if (bp->buf != 0)
+ free(bp->buf);
+ bp->buf = (char *)malloc((uint_t)size);
+ if (!bp->buf) {
+ error("rcp: malloc: out of memory\n");
+ return (0);
+ }
+ }
+ bp->cnt = size;
+ return (bp);
+}
+
+static void
+usage(void)
+{
+ (void) fprintf(stderr, "%s: \t%s\t%s", gettext("Usage"),
+ gettext("\trcp [-p] [-a] [-x] [-k realm] [-PN / -PO] "
+#ifdef DEBUG
+ "[-D port] "
+#endif /* DEBUG */
+ "f1 f2; or:\n"),
+ gettext("\trcp [-r] [-p] [-a] [-x] "
+#ifdef DEBUG
+ "[-D port] "
+#endif /* DEBUG */
+ "[-k realm] [-PN / -PO] f1...fn d2\n"));
+ exit(1);
+}
+
+
+/*
+ * sparse file support
+ */
+
+static off_t zbsize;
+static off_t zlastseek;
+
+/* is it ok to try to create holes? */
+static void
+zopen(int fd, int flag)
+{
+ struct stat st;
+
+ zbsize = 0;
+ zlastseek = 0;
+
+ if (flag &&
+ fstat(fd, &st) == 0 &&
+ (st.st_mode & S_IFMT) == S_IFREG)
+ zbsize = st.st_blksize;
+}
+
+/* write and/or seek */
+static int
+zwrite(int fd, char *buf, int nbytes)
+{
+ off_t block = zbsize ? zbsize : nbytes;
+
+ do {
+ if (block > nbytes)
+ block = nbytes;
+ nbytes -= block;
+
+ if (!zbsize || notzero(buf, block)) {
+ register int n, count = block;
+
+ do {
+ if ((n = write(fd, buf, count)) < 0)
+ return (-1);
+ buf += n;
+ } while ((count -= n) > 0);
+ zlastseek = 0;
+ } else {
+ if (lseek(fd, (off_t)block, SEEK_CUR) < 0)
+ return (-1);
+ buf += block;
+ zlastseek = 1;
+ }
+ } while (nbytes > 0);
+
+ return (0);
+}
+
+/* write last byte of file if necessary */
+static int
+zclose(int fd)
+{
+ zbsize = 0;
+
+ if (zlastseek && (lseek(fd, (off_t)-1, SEEK_CUR) < 0 ||
+ zwrite(fd, "", 1) < 0))
+ return (-1);
+ else
+ return (0);
+}
+
+/* return true if buffer is not all zeros */
+static int
+notzero(char *p, int n)
+{
+ register int result = 0;
+
+ while ((int)p & 3 && --n >= 0)
+ result |= *p++;
+
+ while ((n -= 4 * sizeof (int)) >= 0) {
+ /* LINTED */
+ result |= ((int *)p)[0];
+ /* LINTED */
+ result |= ((int *)p)[1];
+ /* LINTED */
+ result |= ((int *)p)[2];
+ /* LINTED */
+ result |= ((int *)p)[3];
+ if (result)
+ return (result);
+ p += 4 * sizeof (int);
+ }
+ n += 4 * sizeof (int);
+
+ while (--n >= 0)
+ result |= *p++;
+
+ return (result);
+}
+
+/*
+ * New functions to support ACLs
+ */
+
+/*
+ * Get acl from f and send it over.
+ * ACL record includes acl entry count, acl text length, and acl text.
+ */
+static int
+sendacl(int f)
+{
+ int aclcnt;
+ aclent_t *aclbufp;
+ int aclsize;
+ char *acltext;
+ char buf[BUFSIZ];
+
+ if ((aclcnt = facl(f, GETACLCNT, 0, NULL)) < 0) {
+ error("can't get acl count \n");
+ return (ACL_FAIL);
+ }
+
+ /* send the acl count over */
+ (void) snprintf(buf, sizeof (buf), "A%d\n", aclcnt);
+ (void) desrcpwrite(rem, buf, strlen(buf));
+
+ /* only send acl when it is non-trivial */
+ if (aclcnt > MIN_ACL_ENTRIES) {
+ aclsize = aclcnt * sizeof (aclent_t);
+ if ((aclbufp = (aclent_t *)malloc(aclsize)) == NULL) {
+ error("rcp: cant allocate memory: aclcnt %d\n",
+ aclcnt);
+ exit(1);
+ }
+ if (facl(f, GETACL, aclcnt, aclbufp) < 0) {
+ error("rcp: failed to get acl\n");
+ return (ACL_FAIL);
+ }
+ acltext = acltotext(aclbufp, aclcnt);
+ if (acltext == NULL) {
+ error("rcp: failed to convert to text\n");
+ return (ACL_FAIL);
+ }
+
+ /* send ACLs over: send the length first */
+ (void) snprintf(buf, sizeof (buf), "A%d\n", strlen(acltext));
+
+ (void) desrcpwrite(rem, buf, strlen(buf));
+ (void) desrcpwrite(rem, acltext, strlen(acltext));
+ free(acltext);
+ free(aclbufp);
+ if (response() < 0)
+ return (ACL_FAIL);
+
+ }
+ return (ACL_OK);
+}
+
+/*
+ * Use this routine to get acl entry count and acl text size (in bytes)
+ */
+static int
+getaclinfo(int *cnt)
+{
+ char buf[BUFSIZ];
+ char *cp;
+ char ch;
+
+ /* get acl count */
+ cp = buf;
+ if (desrcpread(rem, cp, 1) <= 0)
+ return (ACL_FAIL);
+ if (*cp++ != 'A') {
+ error("rcp: expect an ACL record, but got %c\n", *cp);
+ return (ACL_FAIL);
+ }
+ do {
+ if (desrcpread(rem, &ch, sizeof (ch)) != sizeof (ch)) {
+ error("rcp: lost connection ..\n");
+ return (ACL_FAIL);
+ }
+ *cp++ = ch;
+ } while (cp < &buf[BUFSIZ - 1] && ch != '\n');
+ if (ch != '\n') {
+ error("rcp: ACL record corrupted \n");
+ return (ACL_FAIL);
+ }
+ cp = &buf[1];
+ *cnt = strtol(cp, &cp, 0);
+ if (*cp != '\n') {
+ error("rcp: ACL record corrupted \n");
+ return (ACL_FAIL);
+ }
+ return (ACL_OK);
+}
+
+
+/*
+ * Receive acl from the pipe and set it to f
+ */
+static int
+recvacl(int f, int exists, int preserve)
+{
+ int aclcnt; /* acl entry count */
+ int aclsize; /* acl text length */
+ int j;
+ char *tp;
+ char *acltext; /* external format */
+ aclent_t *aclbufp; /* internal format */
+
+ /* get acl count */
+ if (getaclinfo(&aclcnt) != ACL_OK)
+ return (ACL_FAIL);
+
+ if (aclcnt > MIN_ACL_ENTRIES) {
+ /* get acl text size */
+ if (getaclinfo(&aclsize) != ACL_OK)
+ return (ACL_FAIL);
+ if ((acltext = malloc(aclsize + 1)) == NULL) {
+ error("rcp: cant allocate memory: %d\n", aclsize);
+ return (ACL_FAIL);
+ }
+
+ tp = acltext;
+ do {
+ j = desrcpread(rem, tp, aclsize);
+ if (j <= 0) {
+ error("rcp: %s\n", j ? strerror(errno) :
+ "dropped connection");
+ exit(1);
+ }
+ aclsize -= j;
+ tp += j;
+ } while (aclsize > 0);
+ *tp = '\0';
+
+ if (preserve || !exists) {
+ aclbufp = aclfromtext(acltext, &aclcnt);
+ if (aclbufp == NULL) {
+ error("rcp: failed to parse acl\n");
+ return (ACL_FAIL);
+ }
+ if (f != -1) {
+ if (facl(f, SETACL, aclcnt, aclbufp) < 0) {
+ error("rcp: failed to set acl\n");
+ return (ACL_FAIL);
+ }
+ }
+ /* -1 means that just consume the data in the pipe */
+ free(aclbufp);
+ }
+ free(acltext);
+ (void) desrcpwrite(rem, "", 1);
+ }
+ return (ACL_OK);
+}
+
+
+static char *
+search_char(unsigned char *cp, unsigned char chr)
+{
+ int len;
+
+ while (*cp) {
+ if (*cp == chr)
+ return ((char *)cp);
+ if ((len = mblen((char *)cp, MB_CUR_MAX)) <= 0)
+ len = 1;
+ cp += len;
+ }
+ return (0);
+}
+
+
+static int
+desrcpread(int fd, char *buf, int len)
+{
+ return ((int)desread(fd, buf, len, 0));
+}
+
+static int
+desrcpwrite(int fd, char *buf, int len)
+{
+ /*
+ * Note that rcp depends on the same file descriptor being both
+ * input and output to the remote side. This is bogus, especially
+ * when rcp is being run by a rsh that pipes. Fix it here because
+ * it would require significantly more work in other places.
+ * --hartmans 1/96
+ */
+
+ if (fd == 0)
+ fd = 1;
+ return ((int)deswrite(fd, buf, len, 0));
+}
+
+static char **
+save_argv(int argc, char **argv)
+{
+ int i;
+
+ char **local_argv = (char **)calloc((unsigned)argc + 1,
+ (unsigned)sizeof (char *));
+
+ /*
+ * allocate an extra pointer, so that it is initialized to NULL and
+ * execv() will work
+ */
+ for (i = 0; i < argc; i++) {
+ local_argv[i] = strsave(argv[i]);
+ }
+
+ return (local_argv);
+}
+
+#define SIZEOF_INADDR sizeof (struct in_addr)
+
+static void
+answer_auth(char *config_file, char *ccache_file)
+{
+ krb5_data pname_data, msg;
+ krb5_creds creds, *new_creds;
+ krb5_ccache cc;
+ krb5_auth_context auth_context = NULL;
+
+ if (config_file) {
+ const char *filenames[2];
+
+ filenames[1] = NULL;
+ filenames[0] = config_file;
+ if (krb5_set_config_files(bsd_context, filenames))
+ exit(1);
+ }
+ (void) memset((char *)&creds, 0, sizeof (creds));
+
+ if (krb5_read_message(bsd_context, (krb5_pointer) &rem, &pname_data))
+ exit(1);
+
+ if (krb5_read_message(bsd_context, (krb5_pointer) &rem,
+ &creds.second_ticket))
+ exit(1);
+
+ if (ccache_file == NULL) {
+ if (krb5_cc_default(bsd_context, &cc))
+ exit(1);
+ } else {
+ if (krb5_cc_resolve(bsd_context, ccache_file, &cc))
+ exit(1);
+ }
+
+ if (krb5_cc_get_principal(bsd_context, cc, &creds.client))
+ exit(1);
+
+ if (krb5_parse_name(bsd_context, pname_data.data, &creds.server))
+ exit(1);
+
+ krb5_xfree(pname_data.data);
+ if (krb5_get_credentials(bsd_context, KRB5_GC_USER_USER, cc, &creds,
+ &new_creds))
+ exit(1);
+
+ if (krb5_mk_req_extended(bsd_context, &auth_context,
+ AP_OPTS_USE_SESSION_KEY, NULL, new_creds, &msg))
+ exit(1);
+
+ if (krb5_write_message(bsd_context, (krb5_pointer) & rem, &msg)) {
+ krb5_xfree(msg.data);
+ exit(1);
+ }
+ /* setup eblock for des_read and write */
+ krb5_copy_keyblock(bsd_context, &new_creds->keyblock, &session_key);
+
+ /* OK process key */
+ eblock.crypto_entry = session_key->enctype;
+ eblock.key = (krb5_keyblock *)session_key;
+
+ init_encrypt(encrypt_flag, bsd_context, KCMD_OLD_PROTOCOL,
+ &desinbuf, &desoutbuf, CLIENT, &eblock);
+ /* cleanup */
+ krb5_free_cred_contents(bsd_context, &creds);
+ krb5_free_creds(bsd_context, new_creds);
+ krb5_xfree(msg.data);
+}
+
+
+static void
+try_normal_rcp(int cur_argc, char **cur_argv)
+{
+ char *target;
+
+ /*
+ * Reset all KRB5 relevant flags and set the
+ * cmd-buffer so that normal rcp works
+ */
+ krb5auth_flag = encrypt_flag = encrypt_done = 0;
+ cmd = cmd_orig;
+ cmd_sunw = cmd_sunw_orig;
+
+ if (cur_argc < 2)
+ usage();
+
+ if (cur_argc > 2)
+ targetshouldbedirectory = 1;
+
+ rem = -1;
+
+ prev_argc = cur_argc;
+ prev_argv = save_argv(cur_argc, cur_argv);
+
+ (void) init_service(krb5auth_flag);
+
+ if (target = colon(cur_argv[cur_argc - 1])) {
+ toremote(target, cur_argc, cur_argv);
+ } else {
+ tolocal(cur_argc, cur_argv);
+ if (targetshouldbedirectory)
+ verifydir(cur_argv[cur_argc - 1]);
+ }
+ exit(errs);
+ /* NOTREACHED */
+}
+
+
+static int
+init_service(int krb5flag)
+{
+ struct servent *sp;
+ boolean_t success = B_FALSE;
+
+ if (krb5flag > 0) {
+ sp = getservbyname("kshell", "tcp");
+ if (sp == NULL) {
+ (void) fprintf(stderr,
+ gettext("rcp: kshell/tcp: unknown service.\n"
+ "trying normal shell/tcp service\n"));
+ } else {
+ portnumber = sp->s_port;
+ success = B_TRUE;
+ }
+ } else {
+ portnumber = htons(IPPORT_CMDSERVER);
+ success = B_TRUE;
+ }
+ return (success);
+}
+
+/*PRINTFLIKE1*/
+static void
+error(char *fmt, ...)
+{
+ va_list ap;
+ char buf[RCP_BUFSIZE];
+ char *cp = buf;
+
+ va_start(ap, fmt);
+ errs++;
+ *cp++ = 1;
+ (void) vsnprintf(cp, sizeof (buf) - 1, fmt, ap);
+ va_end(ap);
+
+ (void) desrcpwrite(rem, buf, strlen(buf));
+ if (iamremote == 0)
+ (void) write(2, buf + 1, strlen(buf + 1));
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/rdate.c b/usr/src/cmd/cmd-inet/usr.bin/rdate.c
new file mode 100644
index 0000000000..f5e7139e27
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/rdate.c
@@ -0,0 +1,187 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * rdate - get date from remote machine
+ *
+ * sets time, obtaining value from host
+ * on the tcp/time socket.
+ */
+
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/tcp.h>
+#include <strings.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <netdb.h>
+#include <stdio.h>
+
+/*
+ * The timeserver returns with time of day in seconds since
+ * Jan 1, 1900. We must subtract 86400(365*70 + 17) to get time
+ * since Jan 1, 1970, which is what get/settimeofday uses.
+ */
+
+#define TOFFSET ((unsigned int)86400*(365*70 + 17))
+
+/*
+ * Before setting the system time, the value returned by the
+ * timeserver is checked for plausibility. If the returned date
+ * is before the time this program was written it cannot be
+ * correct.
+ */
+
+#define WRITTEN 440199955 /* 22:45:55 13/Dec/1983 */
+#define SECONDS_TO_MS 1000
+
+static void timeout(int);
+
+int
+main(int argc, char **argv)
+{
+ int s, i;
+ uint32_t time;
+ struct timeval timestruct;
+ unsigned int connect_timeout;
+ /* number of seconds to wait for something to happen. */
+ unsigned int rdate_timeout = 30; /* seconds */
+ struct addrinfo hints;
+ struct addrinfo *res;
+ int rc;
+
+ if (argc != 2) {
+ (void) fputs("usage: rdate host\n", stderr);
+ exit(EXIT_FAILURE);
+ }
+
+ (void) memset(&hints, 0, sizeof (hints));
+ hints.ai_protocol = IPPROTO_TCP;
+ res = NULL;
+
+ /*
+ * getaddrinfo() may take a long time, because it can involve
+ * NIS, DNS, or LDAP lookups. Set an alarm timer. Note that this
+ * may still fail, depending on how SIGALRM is handled by the
+ * functions invoked by getaddrinfo().
+ */
+
+ (void) signal(SIGALRM, timeout);
+ (void) alarm(rdate_timeout);
+
+ /*
+ * Note: memory not freed due to short lifetime of program.
+ */
+
+ rc = getaddrinfo(argv[1], "time", &hints, &res);
+ (void) alarm(0);
+
+ if (rc != 0) {
+ (void) fprintf(stderr, "Host name %s not found: %s\n", argv[1],
+ gai_strerror(rc));
+ exit(EXIT_FAILURE);
+ }
+
+ connect_timeout = rdate_timeout * SECONDS_TO_MS;
+ for (; res != NULL; res = res->ai_next) {
+ s = socket(res->ai_addr->sa_family, res->ai_socktype,
+ res->ai_protocol);
+ if (s < 0) {
+ perror("rdate: socket");
+ exit(EXIT_FAILURE);
+ }
+
+ if (setsockopt(s, IPPROTO_TCP, TCP_CONN_ABORT_THRESHOLD,
+ (char *)&connect_timeout, sizeof (connect_timeout)) == -1) {
+ perror("setsockopt TCP_CONN_ABORT_THRESHOLD");
+ }
+
+ if (connect(s, res->ai_addr, res->ai_addrlen) >= 0)
+ break;
+
+ if (res->ai_next == NULL) {
+ perror("rdate: connect");
+ (void) close(s);
+ exit(EXIT_FAILURE);
+ }
+
+ (void) close(s);
+ }
+
+ (void) alarm(rdate_timeout);
+ if (read(s, (char *)&time, sizeof (time)) != sizeof (time)) {
+ perror("rdate: read");
+ exit(EXIT_FAILURE);
+ }
+ (void) alarm(0);
+
+ time = ntohl(time) - TOFFSET;
+ /* date must be later than when program was written */
+ if (time < WRITTEN) {
+ (void) fprintf(stderr, "didn't get plausible time from %s\n",
+ argv[1]);
+ exit(EXIT_FAILURE);
+ }
+ timestruct.tv_usec = 0;
+ timestruct.tv_sec = time;
+ i = settimeofday(&timestruct, 0);
+ if (i == -1) {
+ perror("couldn't set time of day");
+ exit(EXIT_FAILURE);
+ } else {
+ (void) printf("%s", ctime(&timestruct.tv_sec));
+#if defined(i386)
+ (void) system("/usr/sbin/rtc -c > /dev/null 2>&1");
+#endif
+ }
+ return (EXIT_SUCCESS);
+}
+
+/*ARGSUSED*/
+static void
+timeout(int sig)
+{
+ (void) fputs("couldn't contact time server\n", stderr);
+ exit(EXIT_FAILURE);
+ /* NOTREACHED */
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/rdist/Makefile b/usr/src/cmd/cmd-inet/usr.bin/rdist/Makefile
new file mode 100644
index 0000000000..84545551cd
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/rdist/Makefile
@@ -0,0 +1,81 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# cmd/cmd-inet/usr.bin/rdist/Makefile
+
+PROG= rdist
+OBJS= docmd.o expand.o lookup.o main.o server.o
+KCMDOBJS= kcmd.o
+YOBJS= gram.o
+SRCS= $(OBJS:.o=.c) $(CMDINETCOMMONDIR)/$(KCMDOBJS:.o=.c)
+YSRCS= $(YOBJS:.o=.y)
+
+include ../../../Makefile.cmd
+include ../../Makefile.cmd-inet
+
+CPPFLAGS += -DSYSV -DSTRNET -DBSD_COMP
+LDLIBS += -lsocket -lnsl
+LDFLAGS += $(ZLAZYLOAD)
+
+include $(SRC)/lib/gss_mechs/mech_krb5/Makefile.mech_krb5
+LDFLAGS += $(KRUNPATH) -L$(ROOT)$(KLIBDIR_DO) -L$(ROOT)$(KLIBDIR_GL)
+LDLIBS += -lnsl -lmech_krb5
+CPPFLAGS += -I. -I$(CMDINETCOMMONDIR) \
+ -I$(SRC)/lib/gss_mechs/mech_krb5 \
+ -I$(SRC)/uts/common/gssapi/mechs/krb5/include \
+ -I$(SRC)/lib/gss_mechs/mech_krb5/include \
+ -I$(SRC)/lib/gss_mechs/mech_krb5/include/krb5
+
+ROOTSYMLINK= $(ROOT)/usr/ucb/$(PROG)
+
+# conditional assignments
+$(ROOTPROG) := FILEMODE= 04555
+$(ROOTPROG) := OWNER= root
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+$(PROG): $(OBJS) $(KCMDOBJS) $(YOBJS)
+ $(LINK.c) $(OBJS) $(KCMDOBJS) $(YOBJS) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+install: all $(ROOTPROG) $(ROOTSYMLINK)
+
+$(ROOTSYMLINK):
+ $(RM) $@; $(SYMLINK) ../bin/$(PROG) $@
+
+clean:
+ $(RM) $(OBJS) $(KCMDOBJS) $(YOBJS)
+
+lint:
+ $(YACC.y) $(YSRCS)
+ $(LINT.c) $(SRCS) y.tab.c $(LDLIBS)
+ $(RM) y.tab.c
+
+
+include ../../../Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/usr.bin/rdist/defs.h b/usr/src/cmd/cmd-inet/usr.bin/rdist/defs.h
new file mode 100644
index 0000000000..69c7c8ba05
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/rdist/defs.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Copyright (c) 1998, by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+#include <pwd.h>
+#include <grp.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+
+#ifdef SYSV
+#define rindex strrchr
+#define index strchr
+#endif /* SYSV */
+
+/*
+ * The version number should be changed whenever the protocol changes.
+ */
+#define VERSION 3
+
+#define MAILCMD "/usr/lib/sendmail -oi -t"
+
+ /* defines for yacc */
+#define EQUAL 1
+#define LP 2
+#define RP 3
+#define SM 4
+#define ARROW 5
+#define COLON 6
+#define DCOLON 7
+#define NAME 8
+#define STRING 9
+#define INSTALL 10
+#define NOTIFY 11
+#define EXCEPT 12
+#define PATTERN 13
+#define SPECIAL 14
+#define OPTION 15
+
+ /* lexical definitions */
+#define QUOTE 0200 /* used internally for quoted characters */
+#define TRIM 0177 /* Mask to strip quote bit */
+
+ /* table sizes */
+#define HASHSIZE 1021
+#define INMAX 3500
+#define LINESIZE BUFSIZ
+
+ /* option flags */
+#define VERIFY 0x1
+#define WHOLE 0x2
+#define YOUNGER 0x4
+#define COMPARE 0x8
+#define REMOVE 0x10
+#define FOLLOW 0x20
+#define IGNLNKS 0x40
+#define OBITS "\020\1VERIFY\2WHOLE\3YOUNGER\4COMPARE\5REMOVE\6FOLLOW\7IGNLNKS"
+
+ /* expand type definitions */
+#define E_VARS 0x1
+#define E_SHELL 0x2
+#define E_TILDE 0x4
+#define E_ALL 0x7
+
+ /* actions for lookup() */
+#define LOOKUP 0
+#define INSERT 1
+#define REPLACE 2
+
+#define ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
+
+#define ALLOC(x) (struct x *) malloc(sizeof(struct x))
+
+struct namelist { /* for making lists of strings */
+ char *n_name;
+ struct namelist *n_next;
+};
+
+struct subcmd {
+ short sc_type; /* type - INSTALL,NOTIFY,EXCEPT,SPECIAL */
+ short sc_options;
+ char *sc_name;
+ struct namelist *sc_args;
+ struct subcmd *sc_next;
+};
+
+struct cmd {
+ int c_type; /* type - ARROW,DCOLON */
+ char *c_name; /* hostname or time stamp file name */
+ char *c_label; /* label for partial update */
+ struct namelist *c_files;
+ struct subcmd *c_cmds;
+ struct cmd *c_next;
+};
+
+struct linkbuf {
+ ino_t inum;
+ dev_t devnum;
+ int count;
+ char pathname[LINESIZE];
+ char target[LINESIZE];
+ struct linkbuf *nextp;
+};
+
+extern int debug; /* debugging flag */
+extern int nflag; /* NOP flag, don't execute commands */
+extern int qflag; /* Quiet. don't print messages */
+extern int options; /* global options */
+
+extern int nerrs; /* number of errors seen */
+extern int rem; /* remote file descriptor */
+extern int iamremote; /* acting as remote server */
+extern char Tmpfile[]; /* file name for logging changes */
+extern struct linkbuf *ihead; /* list of files with more than one link */
+extern struct passwd *pw; /* pointer to static area used by getpwent */
+extern struct group *gr; /* pointer to static area used by getgrent */
+extern char host[]; /* host name of master copy */
+extern char buf[]; /* general purpose buffer */
+extern int errno; /* system error number */
+extern char *sys_errlist[];
+
+char *makestr();
+struct namelist *makenl();
+struct subcmd *makesubcmd();
+struct namelist *lookup();
+struct namelist *expand();
+char *exptilde();
+char *rindex();
+char *index();
+char *printb();
+void sendrem();
diff --git a/usr/src/cmd/cmd-inet/usr.bin/rdist/docmd.c b/usr/src/cmd/cmd-inet/usr.bin/rdist/docmd.c
new file mode 100644
index 0000000000..090ed10dfc
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/rdist/docmd.c
@@ -0,0 +1,783 @@
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ */
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "defs.h"
+#include <string.h>
+#include <setjmp.h>
+#include <netdb.h>
+#include <signal.h>
+#include <krb5defs.h>
+
+#ifndef RDIST
+#ifdef SYSV
+/*
+ * Historically, the rdist program has had the following hard-coded
+ * pathname. Some operating systems attempt to "improve" the
+ * directory layout, in the process re-locating the rdist binary
+ * to some other location. However, the first original implementation
+ * sets a standard of sorts. In order to interoperate with other
+ * systems, our implementation must do two things: It must provide
+ * the an rdist binary at the pathname below, and it must use this
+ * pathname when executing rdist on remote systems via the rcmd()
+ * library. Thus the hard-coded path name below can never be changed.
+ */
+#endif /* SYSV */
+#define RDIST "/usr/ucb/rdist"
+#endif
+
+FILE *lfp; /* log file for recording files updated */
+struct subcmd *subcmds; /* list of sub-commands for current cmd */
+jmp_buf env;
+
+void cleanup();
+void lostconn();
+static int init_service(int);
+static struct servent *sp;
+
+#ifdef SYSV
+#include <libgen.h>
+
+static char *recomp;
+static char *errstring = "regcmp failed for some unknown reason";
+
+char *
+re_comp(s)
+char *s;
+{
+ if ((int)recomp != 0)
+ free(recomp);
+ recomp = regcmp(s, (char *)0);
+ if (recomp == NULL)
+ return (errstring);
+ else
+ return ((char *)0);
+}
+
+
+re_exec(s)
+char *s;
+{
+ if ((int)recomp == 0)
+ return (-1);
+ if (regex(recomp, s) == NULL)
+ return (0);
+ else
+ return (1);
+}
+#endif /* SYSV */
+
+/*
+ * Do the commands in cmds (initialized by yyparse).
+ */
+docmds(dhosts, argc, argv)
+ char **dhosts;
+ int argc;
+ char **argv;
+{
+ register struct cmd *c;
+ register struct namelist *f;
+ register char **cpp;
+ extern struct cmd *cmds;
+
+ /* protect backgrounded rdist */
+ if (signal(SIGINT, SIG_IGN) != SIG_IGN)
+ (void) signal(SIGINT, cleanup);
+
+ /* ... and running via nohup(1) */
+ if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
+ (void) signal(SIGHUP, cleanup);
+ if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
+ (void) signal(SIGQUIT, cleanup);
+
+ (void) signal(SIGTERM, cleanup);
+
+if (debug)
+ if (!cmds)
+ printf("docmds: cmds == NULL\n");
+ else {
+ printf("docmds: cmds ");
+ prcmd(cmds);
+ }
+ for (c = cmds; c != NULL; c = c->c_next) {
+ if (dhosts != NULL && *dhosts != NULL) {
+ for (cpp = dhosts; *cpp; cpp++)
+ if (strcmp(c->c_name, *cpp) == 0)
+ goto fndhost;
+ continue;
+ }
+ fndhost:
+ if (argc) {
+ for (cpp = argv; *cpp; cpp++) {
+ if (c->c_label != NULL &&
+ strcmp(c->c_label, *cpp) == 0) {
+ cpp = NULL;
+ goto found;
+ }
+ for (f = c->c_files; f != NULL; f = f->n_next)
+ if (strcmp(f->n_name, *cpp) == 0)
+ goto found;
+ }
+ continue;
+ } else
+ cpp = NULL;
+ found:
+ switch (c->c_type) {
+ case ARROW:
+ doarrow(cpp, c->c_files, c->c_name, c->c_cmds);
+ break;
+ case DCOLON:
+ dodcolon(cpp, c->c_files, c->c_name, c->c_cmds);
+ break;
+ default:
+ fatal("illegal command type %d\n", c->c_type);
+ }
+ }
+ closeconn();
+}
+
+/*
+ * Process commands for sending files to other machines.
+ */
+doarrow(filev, files, rhost, cmds)
+ char **filev;
+ struct namelist *files;
+ char *rhost;
+ struct subcmd *cmds;
+{
+ register struct namelist *f;
+ register struct subcmd *sc;
+ register char **cpp;
+ int n, ddir, opts = options;
+
+ if (debug)
+ printf("doarrow(%x, %s, %x)\n", files, rhost, cmds);
+
+ if (files == NULL) {
+ error("no files to be updated\n");
+ return;
+ }
+
+ subcmds = cmds;
+ ddir = files->n_next != NULL; /* destination is a directory */
+ if (nflag)
+ printf("updating host %s\n", rhost);
+ else {
+ if (setjmp(env))
+ goto done;
+ (void) signal(SIGPIPE, lostconn);
+ if (!makeconn(rhost))
+ return;
+ if (!nflag)
+ if ((lfp = fopen(Tmpfile, "w")) == NULL) {
+ fatal("cannot open %s\n", Tmpfile);
+ exit(1);
+ }
+ }
+ for (f = files; f != NULL; f = f->n_next) {
+ if (filev) {
+ for (cpp = filev; *cpp; cpp++)
+ if (strcmp(f->n_name, *cpp) == 0)
+ goto found;
+ continue;
+ }
+ found:
+ n = 0;
+ for (sc = cmds; sc != NULL; sc = sc->sc_next) {
+ if (sc->sc_type != INSTALL)
+ continue;
+ n++;
+ install(f->n_name, sc->sc_name,
+ sc->sc_name == NULL ? 0 : ddir, sc->sc_options);
+ opts = sc->sc_options;
+ }
+ if (n == 0)
+ install(f->n_name, NULL, 0, options);
+ }
+done:
+ if (!nflag) {
+ (void) signal(SIGPIPE, cleanup);
+ (void) fclose(lfp);
+ lfp = NULL;
+ }
+ for (sc = cmds; sc != NULL; sc = sc->sc_next)
+ if (sc->sc_type == NOTIFY)
+ notify(Tmpfile, rhost, sc->sc_args, 0);
+ if (!nflag) {
+ (void) unlink(Tmpfile);
+ for (; ihead != NULL; ihead = ihead->nextp) {
+ free(ihead);
+ if ((opts & IGNLNKS) || ihead->count == 0)
+ continue;
+ log(lfp, "%s: Warning: missing links\n",
+ ihead->pathname);
+ }
+ }
+}
+
+static int
+init_service(int krb5flag)
+{
+ boolean_t success = B_FALSE;
+
+ if (krb5flag > 0) {
+ if ((sp = getservbyname("kshell", "tcp")) == NULL) {
+ fatal("kshell/tcp: unknown service");
+ (void) fprintf(stderr,
+ gettext("trying shell/tcp service...\n"));
+ } else {
+ success = B_TRUE;
+ }
+ } else {
+ if ((sp = getservbyname("shell", "tcp")) == NULL) {
+ fatal("shell/tcp: unknown service");
+ exit(1);
+ } else {
+ success = B_TRUE;
+ }
+ }
+ return (success);
+}
+/*
+ * Create a connection to the rdist server on the machine rhost.
+ */
+makeconn(rhost)
+ char *rhost;
+{
+ register char *ruser, *cp;
+ static char *cur_host = NULL;
+ static int port = -1;
+ char tuser[20];
+ int n;
+ extern char user[];
+
+ if (debug)
+ printf("makeconn(%s)\n", rhost);
+
+ if (cur_host != NULL && rem >= 0) {
+ if (strcmp(cur_host, rhost) == 0)
+ return (1);
+ closeconn();
+ }
+ cur_host = rhost;
+ cp = index(rhost, '@');
+ if (cp != NULL) {
+ char c = *cp;
+
+ *cp = '\0';
+ strncpy(tuser, rhost, sizeof (tuser)-1);
+ *cp = c;
+ rhost = cp + 1;
+ ruser = tuser;
+ if (*ruser == '\0')
+ ruser = user;
+ else if (!okname(ruser))
+ return (0);
+ } else
+ ruser = user;
+ if (!qflag)
+ printf("updating host %s\n", rhost);
+ (void) snprintf(buf, RDIST_BUFSIZ, "%s%s -Server%s",
+ encrypt_flag ? "-x " : "", RDIST, qflag ? " -q" : "");
+ if (port < 0) {
+ if (debug_port == 0) {
+ if ((retval = (int)init_service(krb5auth_flag)) == 0) {
+ krb5auth_flag = encrypt_flag = 0;
+ (void) init_service(krb5auth_flag);
+ }
+ port = sp->s_port;
+
+ } else {
+ port = debug_port;
+ }
+ }
+
+ if (debug) {
+ printf("port = %d, luser = %s, ruser = %s\n", ntohs(port),
+ user, ruser);
+ printf("buf = %s\n", buf);
+ }
+
+ fflush(stdout);
+
+ if (krb5auth_flag > 0) {
+ if ((encrypt_flag > 0) && (!krb5_privacy_allowed())) {
+ (void) fprintf(stderr, gettext("rdist: Encryption "
+ " not supported.\n"));
+ exit(1);
+ }
+
+ authopts = AP_OPTS_MUTUAL_REQUIRED;
+
+ status = kcmd(&rem, &rhost, port,
+ user, ruser,
+ buf, 0, "host", krb_realm,
+ bsd_context,
+ &auth_context,
+ &cred,
+ 0, /* No need for sequence number */
+ 0, /* No need for server seq # */
+ authopts,
+ 1, /* Always set anyport */
+ &kcmd_proto);
+ if (status) {
+ /*
+ * If new protocol requested, we dont
+ * fallback to less secure ones.
+ */
+ if (kcmd_proto == KCMD_NEW_PROTOCOL) {
+ (void) fprintf(stderr, gettext("rdist: kcmdv2 "
+ "to host %s failed - %s\n"
+ "Fallback to normal rdist denied."),
+ host, error_message(status));
+ exit(1);
+ }
+ /* check NO_TKT_FILE or equivalent... */
+ if (status != -1) {
+ (void) fprintf(stderr, gettext("rdist: "
+ "kcmd to host %s failed - %s\n"
+ "trying normal rdist...\n\n"),
+ host, error_message(status));
+ } else {
+ (void) fprintf(stderr,
+ gettext("trying normal rdist...\n"));
+ }
+ /*
+ * kcmd() failed, so we now fallback to normal rdist
+ */
+ krb5auth_flag = encrypt_flag = 0;
+ (void) init_service(krb5auth_flag);
+ port = sp->s_port;
+ goto do_rcmd;
+ }
+#ifdef DEBUG
+ else {
+ (void) fprintf(stderr, gettext("Kerberized rdist "
+ "session, port %d in use "), port);
+ if (kcmd_proto == KCMD_OLD_PROTOCOL)
+ (void) fprintf(stderr,
+ gettext("[kcmd ver.1].\n"));
+ else
+ (void) fprintf(stderr,
+ gettext("[kcmd ver.2].\n"));
+ }
+#endif /* DEBUG */
+ session_key = &cred->keyblock;
+
+ if (kcmd_proto == KCMD_NEW_PROTOCOL) {
+ status = krb5_auth_con_getlocalsubkey(bsd_context,
+ auth_context,
+ &session_key);
+ if (status) {
+ com_err("rdist", status,
+ "determining subkey for session");
+ exit(1);
+ }
+ if (!session_key) {
+ com_err("rdist", 0,
+ "no subkey negotiated for connection");
+ exit(1);
+ }
+ }
+
+ eblock.crypto_entry = session_key->enctype;
+ eblock.key = (krb5_keyblock *)session_key;
+
+ init_encrypt(encrypt_flag, bsd_context, kcmd_proto, &desinbuf,
+ &desoutbuf, CLIENT, &eblock);
+
+
+ if (encrypt_flag > 0) {
+ char *s = gettext("This rdist session is using "
+ "encryption for all data transmissions.\r\n");
+ (void) write(2, s, strlen(s));
+ }
+
+ }
+ else
+do_rcmd:
+ {
+ rem = rcmd_af(&rhost, port, user, ruser, buf, 0, AF_INET6);
+ }
+
+ if (rem < 0)
+ return (0);
+
+ cp = buf;
+ if (desread(rem, cp, 1, 0) != 1)
+ lostconn();
+ if (*cp == 'V') {
+ do {
+ if (desread(rem, cp, 1, 0) != 1)
+ lostconn();
+ } while (*cp++ != '\n' && cp < &buf[RDIST_BUFSIZ]);
+ *--cp = '\0';
+ cp = buf;
+ n = 0;
+ while (*cp >= '0' && *cp <= '9')
+ n = (n * 10) + (*cp++ - '0');
+ if (*cp == '\0' && n == VERSION)
+ return (1);
+ error("connection failed: version numbers don't match"
+ " (local %d, remote %d)\n", VERSION, n);
+ } else {
+ error("connection failed: version numbers don't match\n");
+ }
+ closeconn();
+ return (0);
+}
+
+/*
+ * Signal end of previous connection.
+ */
+closeconn()
+{
+ if (debug)
+ printf("closeconn()\n");
+
+ if (rem >= 0) {
+ (void) deswrite(rem, "\2\n", 2, 0);
+ (void) close(rem);
+ rem = -1;
+ }
+}
+
+void
+lostconn()
+{
+ if (iamremote)
+ cleanup();
+ log(lfp, "rdist: lost connection\n");
+ longjmp(env, 1);
+}
+
+okname(name)
+ register char *name;
+{
+ register char *cp = name;
+ register int c;
+
+ do {
+ c = *cp;
+ if (c & 0200)
+ goto bad;
+ if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-')
+ goto bad;
+ cp++;
+ } while (*cp);
+ return (1);
+bad:
+ error("invalid user name %s\n", name);
+ return (0);
+}
+
+time_t lastmod;
+FILE *tfp;
+extern char target[], *tp;
+
+/*
+ * Process commands for comparing files to time stamp files.
+ */
+dodcolon(filev, files, stamp, cmds)
+ char **filev;
+ struct namelist *files;
+ char *stamp;
+ struct subcmd *cmds;
+{
+ register struct subcmd *sc;
+ register struct namelist *f;
+ register char **cpp;
+ struct timeval tv[2];
+ struct stat stb;
+
+ if (debug)
+ printf("dodcolon()\n");
+
+ if (files == NULL) {
+ error("no files to be updated\n");
+ return;
+ }
+ if (stat(stamp, &stb) < 0) {
+ error("%s: %s\n", stamp, strerror(errno));
+ return;
+ }
+ if (debug)
+ printf("%s: %d\n", stamp, stb.st_mtime);
+
+ subcmds = cmds;
+ lastmod = stb.st_mtime;
+ if (nflag || (options & VERIFY))
+ tfp = NULL;
+ else {
+ if ((tfp = fopen(Tmpfile, "w")) == NULL) {
+ error("%s: %s\n", stamp, strerror(errno));
+ return;
+ }
+ (void) gettimeofday(&tv[0], (struct timezone *)NULL);
+ tv[1] = tv[0];
+ (void) utimes(stamp, tv);
+ }
+
+ for (f = files; f != NULL; f = f->n_next) {
+ if (filev) {
+ for (cpp = filev; *cpp; cpp++)
+ if (strcmp(f->n_name, *cpp) == 0)
+ goto found;
+ continue;
+ }
+ found:
+ tp = NULL;
+ cmptime(f->n_name);
+ }
+
+ if (tfp != NULL)
+ (void) fclose(tfp);
+ for (sc = cmds; sc != NULL; sc = sc->sc_next)
+ if (sc->sc_type == NOTIFY)
+ notify(Tmpfile, NULL, sc->sc_args, lastmod);
+ if (!nflag && !(options & VERIFY))
+ (void) unlink(Tmpfile);
+}
+
+/*
+ * Compare the mtime of file to the list of time stamps.
+ */
+cmptime(name)
+ char *name;
+{
+ struct stat stb;
+
+ if (debug)
+ printf("cmptime(%s)\n", name);
+
+ if (except(name))
+ return;
+
+ if (nflag) {
+ printf("comparing dates: %s\n", name);
+ return;
+ }
+
+ /*
+ * first time cmptime() is called?
+ */
+ if (tp == NULL) {
+ if (exptilde(target, RDIST_BUFSIZ, name) == NULL)
+ return;
+ tp = name = target;
+ while (*tp)
+ tp++;
+ }
+ if (access(name, 4) < 0 || stat(name, &stb) < 0) {
+ error("%s: %s\n", name, strerror(errno));
+ return;
+ }
+
+ switch (stb.st_mode & S_IFMT) {
+ case S_IFREG:
+ break;
+
+ case S_IFDIR:
+ rcmptime(&stb);
+ return;
+
+ default:
+ error("%s: not a plain file\n", name);
+ return;
+ }
+
+ if (stb.st_mtime > lastmod)
+ log(tfp, "new: %s\n", name);
+}
+
+rcmptime(st)
+ struct stat *st;
+{
+ register DIR *d;
+ register struct dirent *dp;
+ register char *cp;
+ char *otp;
+ int len;
+
+ if (debug)
+ printf("rcmptime(%x)\n", st);
+
+ if ((d = opendir(target)) == NULL) {
+ error("%s: %s\n", target, strerror(errno));
+ return;
+ }
+ otp = tp;
+ len = tp - target;
+ while (dp = readdir(d)) {
+ if ((strcmp(dp->d_name, ".") == 0) ||
+ (strcmp(dp->d_name, "..") == 0))
+ continue;
+ if (len + 1 + strlen(dp->d_name) >= RDIST_BUFSIZ - 1) {
+ error("%s/%s: Name too long\n", target, dp->d_name);
+ continue;
+ }
+ tp = otp;
+ *tp++ = '/';
+ cp = dp->d_name;
+ while (*tp++ = *cp++)
+ ;
+ tp--;
+ cmptime(target);
+ }
+ closedir(d);
+ tp = otp;
+ *tp = '\0';
+}
+
+/*
+ * Notify the list of people the changes that were made.
+ * rhost == NULL if we are mailing a list of changes compared to at time
+ * stamp file.
+ */
+notify(file, rhost, to, lmod)
+ char *file, *rhost;
+ register struct namelist *to;
+ time_t lmod;
+{
+ register int fd, len;
+ FILE *pf, *popen();
+ struct stat stb;
+
+ if ((options & VERIFY) || to == NULL)
+ return;
+ if (!qflag) {
+ printf("notify ");
+ if (rhost)
+ printf("@%s ", rhost);
+ prnames(to);
+ }
+ if (nflag)
+ return;
+
+ if ((fd = open(file, 0)) < 0) {
+ error("%s: %s\n", file, strerror(errno));
+ return;
+ }
+ if (fstat(fd, &stb) < 0) {
+ error("%s: %s\n", file, strerror(errno));
+ (void) close(fd);
+ return;
+ }
+ if (stb.st_size == 0) {
+ (void) close(fd);
+ return;
+ }
+ /*
+ * Create a pipe to mailling program.
+ */
+ pf = popen(MAILCMD, "w");
+ if (pf == NULL) {
+ error("notify: \"%s\" failed\n", MAILCMD);
+ (void) close(fd);
+ return;
+ }
+ /*
+ * Output the proper header information.
+ */
+ fprintf(pf, "From: rdist (Remote distribution program)\n");
+ fprintf(pf, "To:");
+ if (!any('@', to->n_name) && rhost != NULL)
+ fprintf(pf, " %s@%s", to->n_name, rhost);
+ else
+ fprintf(pf, " %s", to->n_name);
+ to = to->n_next;
+ while (to != NULL) {
+ if (!any('@', to->n_name) && rhost != NULL)
+ fprintf(pf, ", %s@%s", to->n_name, rhost);
+ else
+ fprintf(pf, ", %s", to->n_name);
+ to = to->n_next;
+ }
+ putc('\n', pf);
+ if (rhost != NULL)
+ fprintf(pf, "Subject: files updated by rdist from %s to %s\n",
+ host, rhost);
+ else
+ fprintf(pf, "Subject: files updated after %s\n", ctime(&lmod));
+ putc('\n', pf);
+
+ while ((len = read(fd, buf, RDIST_BUFSIZ)) > 0)
+ (void) fwrite(buf, 1, len, pf);
+ (void) close(fd);
+ (void) pclose(pf);
+}
+
+/*
+ * Return true if name is in the list.
+ */
+inlist(list, file)
+ struct namelist *list;
+ char *file;
+{
+ register struct namelist *nl;
+
+ for (nl = list; nl != NULL; nl = nl->n_next)
+ if (strcmp(file, nl->n_name) == 0)
+ return (1);
+ return (0);
+}
+
+/*
+ * Return TRUE if file is in the exception list.
+ */
+except(file)
+ char *file;
+{
+ register struct subcmd *sc;
+ register struct namelist *nl;
+
+ if (debug)
+ printf("except(%s)\n", file);
+
+ for (sc = subcmds; sc != NULL; sc = sc->sc_next) {
+ if (sc->sc_type != EXCEPT && sc->sc_type != PATTERN)
+ continue;
+ for (nl = sc->sc_args; nl != NULL; nl = nl->n_next) {
+ if (sc->sc_type == EXCEPT) {
+ if (strcmp(file, nl->n_name) == 0)
+ return (1);
+ continue;
+ }
+ re_comp(nl->n_name);
+ if (re_exec(file) > 0)
+ return (1);
+ }
+ }
+ return (0);
+}
+
+char *
+colon(cp)
+ register char *cp;
+{
+
+ while (*cp) {
+ if (*cp == ':')
+ return (cp);
+ if (*cp == '/')
+ return (0);
+ cp++;
+ }
+ return (0);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/rdist/expand.c b/usr/src/cmd/cmd-inet/usr.bin/rdist/expand.c
new file mode 100644
index 0000000000..ad387614b5
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/rdist/expand.c
@@ -0,0 +1,674 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Copyright (c) 1998, by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "defs.h"
+#include <string.h>
+
+#define GAVSIZ NCARGS / 6
+#define LC '{'
+#define RC '}'
+
+static char shchars[] = "${[*?";
+
+int which; /* bit mask of types to expand */
+int eargc; /* expanded arg count */
+char **eargv; /* expanded arg vectors */
+char *path;
+char *pathp;
+char *lastpathp;
+char *tilde; /* "~user" if not expanding tilde, else "" */
+char *tpathp;
+int nleft;
+
+int expany; /* any expansions done? */
+char *entp;
+char **sortbase;
+
+char *index();
+int argcmp();
+
+#define sort() qsort((char *)sortbase, &eargv[eargc] - sortbase, \
+ sizeof (*sortbase), argcmp), sortbase = &eargv[eargc]
+
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
+/*
+ * Take a list of names and expand any macros, etc.
+ * wh = E_VARS if expanding variables.
+ * wh = E_SHELL if expanding shell characters.
+ * wh = E_TILDE if expanding `~'.
+ * or any of these or'ed together.
+ *
+ * Major portions of this were snarfed from csh/sh.glob.c.
+ */
+struct namelist *
+expand(list, wh)
+ struct namelist *list;
+ int wh;
+{
+ register struct namelist *nl, *prev;
+ register int n;
+ char pathbuf[LINESIZE];
+ char *argvbuf[GAVSIZ];
+
+ if (debug) {
+ printf("expand(%x, %d)\nlist = ", list, wh);
+ prnames(list);
+ }
+
+ if (wh == 0) {
+ register char *cp;
+
+ for (nl = list; nl != NULL; nl = nl->n_next)
+ for (cp = nl->n_name; *cp; cp++)
+ *cp = *cp & TRIM;
+ return (list);
+ }
+
+ which = wh;
+ path = tpathp = pathp = pathbuf;
+ *pathp = '\0';
+ lastpathp = &path[sizeof pathbuf - 2];
+ tilde = "";
+ eargc = 0;
+ eargv = sortbase = argvbuf;
+ *eargv = 0;
+ nleft = NCARGS - 4;
+ /*
+ * Walk the name list and expand names into eargv[];
+ */
+ for (nl = list; nl != NULL; nl = nl->n_next)
+ expstr(nl->n_name);
+ /*
+ * Take expanded list of names from eargv[] and build a new list.
+ */
+ list = prev = NULL;
+ for (n = 0; n < eargc; n++) {
+ nl = makenl(NULL);
+ nl->n_name = eargv[n];
+ if (prev == NULL)
+ list = prev = nl;
+ else {
+ prev->n_next = nl;
+ prev = nl;
+ }
+ }
+ if (debug) {
+ printf("expanded list = ");
+ prnames(list);
+ }
+ return (list);
+}
+
+expstr(s)
+ char *s;
+{
+ register char *cp, *cp1;
+ register struct namelist *tp;
+ char *tail;
+ char buf[LINESIZE];
+ int savec, oeargc;
+ extern char homedir[];
+
+ if (s == NULL || *s == '\0')
+ return;
+
+ if ((which & E_VARS) && (cp = index(s, '$')) != NULL) {
+ *cp++ = '\0';
+ if (*cp == '\0') {
+ yyerror("no variable name after '$'");
+ return;
+ }
+ if (*cp == LC) {
+ cp++;
+ if ((tail = index(cp, RC)) == NULL) {
+ yyerror("unmatched '{'");
+ return;
+ }
+ *tail++ = savec = '\0';
+ if (*cp == '\0') {
+ yyerror("no variable name after '$'");
+ return;
+ }
+ } else {
+ tail = cp + 1;
+ savec = *tail;
+ *tail = '\0';
+ }
+ tp = lookup(cp, NULL, 0);
+ if (savec != '\0')
+ *tail = savec;
+ if (tp != NULL) {
+ for (; tp != NULL; tp = tp->n_next) {
+ (void) snprintf(buf, sizeof (buf), "%s%s%s", s,
+ tp->n_name, tail);
+ expstr(buf);
+ }
+ return;
+ }
+ (void) snprintf(buf, sizeof (buf), "%s%s", s, tail);
+ expstr(buf);
+ return;
+ }
+ if ((which & ~E_VARS) == 0 || !strcmp(s, "{") || !strcmp(s, "{}")) {
+ Cat(s, "");
+ sort();
+ return;
+ }
+ if (*s == '~') {
+ cp = ++s;
+ if (*cp == '\0' || *cp == '/') {
+ tilde = "~";
+ cp1 = homedir;
+ } else {
+ tilde = cp1 = buf;
+ *cp1++ = '~';
+ do {
+ if (cp1 >= &buf[sizeof (buf)]) {
+ yyerror("User name too long");
+ return;
+ }
+ *cp1++ = *cp++;
+ } while (*cp && *cp != '/');
+ *cp1 = '\0';
+ if (pw == NULL || strcmp(pw->pw_name, buf+1) != 0) {
+ if ((pw = getpwnam(buf+1)) == NULL) {
+ static char unknown_user[] =
+ ": unknown user name";
+
+ cp1 = MIN(cp1,
+ &buf[sizeof (buf)] -
+ sizeof (unknown_user));
+ strcpy(cp1, unknown_user);
+ yyerror(buf+1);
+ return;
+ }
+ }
+ cp1 = pw->pw_dir;
+ s = cp;
+ }
+ for (cp = path; cp <= lastpathp + 1 && (*cp++ = *cp1++); )
+ ;
+ tpathp = pathp = cp - 1;
+ } else {
+ tpathp = pathp = path;
+ tilde = "";
+ }
+ *pathp = '\0';
+ if (!(which & E_SHELL)) {
+ if (which & E_TILDE)
+ Cat(path, s);
+ else
+ Cat(tilde, s);
+ sort();
+ return;
+ }
+ oeargc = eargc;
+ expany = 0;
+ expsh(s);
+ if (eargc == oeargc)
+ Cat(s, ""); /* "nonomatch" is set */
+ sort();
+}
+
+static
+argcmp(a1, a2)
+ char **a1, **a2;
+{
+
+ return (strcmp(*a1, *a2));
+}
+
+/*
+ * If there are any Shell meta characters in the name,
+ * expand into a list, after searching directory
+ */
+expsh(s)
+ char *s;
+{
+ register char *cp;
+ register char *spathp, *oldcp;
+ struct stat stb;
+
+ spathp = pathp;
+ cp = s;
+ while (!any(*cp, shchars)) {
+ if (*cp == '\0') {
+ if (!expany || stat(path, &stb) >= 0) {
+ if (which & E_TILDE)
+ Cat(path, "");
+ else
+ Cat(tilde, tpathp);
+ }
+ goto endit;
+ }
+ addpath(*cp++);
+ }
+ oldcp = cp;
+ while (cp > s && *cp != '/')
+ cp--, pathp--;
+ if (*cp == '/')
+ cp++, pathp++;
+ *pathp = '\0';
+ if (*oldcp == '{') {
+ execbrc(cp, NULL);
+ return;
+ }
+ matchdir(cp);
+endit:
+ pathp = spathp;
+ *pathp = '\0';
+}
+
+matchdir(pattern)
+ char *pattern;
+{
+ struct stat stb;
+ register struct dirent *dp;
+ DIR *dirp;
+
+ dirp = opendir(path);
+ if (dirp == NULL) {
+ if (expany)
+ return;
+ goto patherr2;
+ }
+ if (fstat(dirp->dd_fd, &stb) < 0)
+ goto patherr1;
+ if (!ISDIR(stb.st_mode)) {
+ errno = ENOTDIR;
+ goto patherr1;
+ }
+ while ((dp = readdir(dirp)) != NULL)
+ if (match(dp->d_name, pattern)) {
+ if (which & E_TILDE)
+ Cat(path, dp->d_name);
+ else {
+ if (pathp + strlen(dp->d_name) - 1 >
+ lastpathp) {
+ errno = ENAMETOOLONG;
+ goto patherr1;
+ }
+ strcpy(pathp, dp->d_name);
+ Cat(tilde, tpathp);
+ *pathp = '\0';
+ }
+ }
+ closedir(dirp);
+ return;
+
+patherr1:
+ closedir(dirp);
+patherr2:
+ {
+ char *strerr = strerror(errno);
+
+ if (path + strlen(path) + strlen(strerr) + 1 > lastpathp)
+ strcpy(lastpathp - strlen(strerr) - 1, ": ");
+ else
+ strcat(path, ": ");
+ strcat(path, strerr);
+ }
+ yyerror(path);
+}
+
+execbrc(p, s)
+ char *p, *s;
+{
+ char restbuf[LINESIZE + 2];
+ register char *pe, *pm, *pl;
+ int brclev = 0;
+ char *lm, savec, *spathp;
+
+ for (lm = restbuf; *p != '{'; *lm++ = *p++) {
+ if (lm >= &restbuf[sizeof (restbuf)]) {
+ yyerror("Pathname too long");
+ return (0);
+ }
+ }
+ for (pe = ++p; *pe; pe++)
+ switch (*pe) {
+
+ case '{':
+ brclev++;
+ continue;
+
+ case '}':
+ if (brclev == 0)
+ goto pend;
+ brclev--;
+ continue;
+
+ case '[':
+ for (pe++; *pe && *pe != ']'; pe++)
+ continue;
+ if (!*pe)
+ yyerror("Missing ']'");
+ continue;
+ }
+pend:
+ if (brclev || !*pe) {
+ yyerror("Missing '}'");
+ return (0);
+ }
+ for (pl = pm = p; pm <= pe; pm++)
+ switch (*pm & (QUOTE|TRIM)) {
+
+ case '{':
+ brclev++;
+ continue;
+
+ case '}':
+ if (brclev) {
+ brclev--;
+ continue;
+ }
+ goto doit;
+
+ case ',':
+ if (brclev)
+ continue;
+doit:
+ savec = *pm;
+ *pm = 0;
+ if (lm + strlen(pl) + strlen(pe + 1) >=
+ &restbuf[sizeof (restbuf)]) {
+ yyerror("Pathname too long");
+ return (0);
+ }
+ strcpy(lm, pl);
+ strcat(restbuf, pe + 1);
+ *pm = savec;
+ if (s == 0) {
+ spathp = pathp;
+ expsh(restbuf);
+ pathp = spathp;
+ *pathp = 0;
+ } else if (amatch(s, restbuf))
+ return (1);
+ sort();
+ pl = pm + 1;
+ continue;
+
+ case '[':
+ for (pm++; *pm && *pm != ']'; pm++)
+ continue;
+ if (!*pm)
+ yyerror("Missing ']'");
+ continue;
+ }
+ return (0);
+}
+
+match(s, p)
+ char *s, *p;
+{
+ register int c;
+ register char *sentp;
+ char sexpany = expany;
+
+ if (*s == '.' && *p != '.')
+ return (0);
+ sentp = entp;
+ entp = s;
+ c = amatch(s, p);
+ entp = sentp;
+ expany = sexpany;
+ return (c);
+}
+
+amatch(s, p)
+ register char *s, *p;
+{
+ register int scc;
+ int ok, lc;
+ char *spathp;
+ struct stat stb;
+ int c, cc;
+
+ expany = 1;
+ for (;;) {
+ scc = *s++ & TRIM;
+ switch (c = *p++) {
+
+ case '{':
+ return (execbrc(p - 1, s - 1));
+
+ case '[':
+ ok = 0;
+ lc = 077777;
+ while (cc = *p++) {
+ if (cc == ']') {
+ if (ok)
+ break;
+ return (0);
+ }
+ if (cc == '-') {
+ if (lc <= scc && scc <= *p++)
+ ok++;
+ } else
+ if (scc == (lc = cc))
+ ok++;
+ }
+ if (cc == 0) {
+ yyerror("Missing ']'");
+ return (0);
+ }
+ continue;
+
+ case '*':
+ if (!*p)
+ return (1);
+ if (*p == '/') {
+ p++;
+ goto slash;
+ }
+ for (s--; *s; s++)
+ if (amatch(s, p))
+ return (1);
+ return (0);
+
+ case '\0':
+ return (scc == '\0');
+
+ default:
+ if ((c & TRIM) != scc)
+ return (0);
+ continue;
+
+ case '?':
+ if (scc == '\0')
+ return (0);
+ continue;
+
+ case '/':
+ if (scc)
+ return (0);
+slash:
+ s = entp;
+ spathp = pathp;
+ while (*s)
+ addpath(*s++);
+ addpath('/');
+ if (stat(path, &stb) == 0 && ISDIR(stb.st_mode))
+ if (*p == '\0') {
+ if (which & E_TILDE)
+ Cat(path, "");
+ else
+ Cat(tilde, tpathp);
+ } else
+ expsh(p);
+ pathp = spathp;
+ *pathp = '\0';
+ return (0);
+ }
+ }
+}
+
+smatch(s, p)
+ register char *s, *p;
+{
+ register int scc;
+ int ok, lc;
+ int c, cc;
+
+ for (;;) {
+ scc = *s++ & TRIM;
+ switch (c = *p++) {
+
+ case '[':
+ ok = 0;
+ lc = 077777;
+ while (cc = *p++) {
+ if (cc == ']') {
+ if (ok)
+ break;
+ return (0);
+ }
+ if (cc == '-') {
+ if (lc <= scc && scc <= *p++)
+ ok++;
+ } else
+ if (scc == (lc = cc))
+ ok++;
+ }
+ if (cc == 0) {
+ yyerror("Missing ']'");
+ return (0);
+ }
+ continue;
+
+ case '*':
+ if (!*p)
+ return (1);
+ for (s--; *s; s++)
+ if (smatch(s, p))
+ return (1);
+ return (0);
+
+ case '\0':
+ return (scc == '\0');
+
+ default:
+ if ((c & TRIM) != scc)
+ return (0);
+ continue;
+
+ case '?':
+ if (scc == 0)
+ return (0);
+ continue;
+
+ }
+ }
+}
+
+Cat(s1, s2)
+ register char *s1, *s2;
+{
+ int len = strlen(s1) + strlen(s2) + 1;
+ register char *s;
+
+ nleft -= len;
+ if (nleft <= 0 || ++eargc >= GAVSIZ)
+ fatal("Arguments too long\n");
+ eargv[eargc] = 0;
+ eargv[eargc - 1] = s = (char *)malloc(len);
+ if (s == NULL)
+ fatal("ran out of memory\n");
+ while (*s++ = *s1++ & TRIM)
+ ;
+ s--;
+ while (*s++ = *s2++ & TRIM)
+ ;
+}
+
+addpath(c)
+ char c;
+{
+
+ if (pathp > lastpathp)
+ yyerror("Pathname too long");
+ else {
+ *pathp++ = c & TRIM;
+ *pathp = '\0';
+ }
+}
+
+/*
+ * Expand file names beginning with `~' into the
+ * user's home directory path name. Return a pointer in buf to the
+ * part corresponding to `file'.
+ */
+char *
+exptilde(buf, len, file)
+ char buf[];
+ unsigned int len;
+ register char *file;
+{
+ register char *s1, *s2, *s3;
+ extern char homedir[];
+
+ if (*file != '~') {
+ if (strlen(file) + 1 > len) {
+ error("pathname too long: %s\n", file);
+ return (NULL);
+ }
+ strcpy(buf, file);
+ return (buf);
+ }
+ if (*++file == '\0') {
+ s2 = homedir;
+ s3 = NULL;
+ } else if (*file == '/') {
+ s2 = homedir;
+ s3 = file;
+ } else {
+ s3 = file;
+ while (*s3 && *s3 != '/')
+ s3++;
+ if (*s3 == '/')
+ *s3 = '\0';
+ else
+ s3 = NULL;
+ if (pw == NULL || strcmp(pw->pw_name, file) != 0) {
+ if ((pw = getpwnam(file)) == NULL) {
+ error("%s: unknown user name\n", file);
+ if (s3 != NULL)
+ *s3 = '/';
+ return (NULL);
+ }
+ }
+ if (s3 != NULL)
+ *s3 = '/';
+ s2 = pw->pw_dir;
+ }
+ for (s1 = buf; s1 < &buf[len] && (*s1++ = *s2++); )
+ ;
+ s2 = --s1;
+ if (s3 != NULL) {
+ s2++;
+ while (s1 < &buf[len] && (*s1++ = *s3++))
+ ;
+ }
+ if (s1 == &buf[len]) {
+ error("pathname too long: %s\n", file - 1);
+ return (NULL);
+ }
+ return (s2);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/rdist/gram.y b/usr/src/cmd/cmd-inet/usr.bin/rdist/gram.y
new file mode 100644
index 0000000000..3624168437
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/rdist/gram.y
@@ -0,0 +1,533 @@
+%{
+/*
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "defs.h"
+
+struct cmd *cmds = NULL;
+struct cmd *last_cmd;
+struct namelist *last_n;
+struct subcmd *last_sc;
+
+%}
+
+%term EQUAL 1
+%term LP 2
+%term RP 3
+%term SM 4
+%term ARROW 5
+%term COLON 6
+%term DCOLON 7
+%term NAME 8
+%term STRING 9
+%term INSTALL 10
+%term NOTIFY 11
+%term EXCEPT 12
+%term PATTERN 13
+%term SPECIAL 14
+%term OPTION 15
+
+%union {
+ int intval;
+ char *string;
+ struct subcmd *subcmd;
+ struct namelist *namel;
+}
+
+%type <intval> OPTION, options
+%type <string> NAME, STRING
+%type <subcmd> INSTALL, NOTIFY, EXCEPT, PATTERN, SPECIAL, cmdlist, cmd
+%type <namel> namelist, names, opt_namelist
+
+%%
+
+file: /* VOID */
+ | file command
+ ;
+
+command: NAME EQUAL namelist = {
+ (void) lookup($1, INSERT, $3);
+ }
+ | namelist ARROW namelist cmdlist = {
+ insert(NULL, $1, $3, $4);
+ }
+ | NAME COLON namelist ARROW namelist cmdlist = {
+ insert($1, $3, $5, $6);
+ }
+ | namelist DCOLON NAME cmdlist = {
+ append(NULL, $1, $3, $4);
+ }
+ | NAME COLON namelist DCOLON NAME cmdlist = {
+ append($1, $3, $5, $6);
+ }
+ | error
+ ;
+
+namelist: NAME = {
+ $$ = makenl($1);
+ }
+ | LP names RP = {
+ $$ = $2;
+ }
+ ;
+
+names: /* VOID */ {
+ $$ = last_n = NULL;
+ }
+ | names NAME = {
+ if (last_n == NULL)
+ $$ = last_n = makenl($2);
+ else {
+ last_n->n_next = makenl($2);
+ last_n = last_n->n_next;
+ $$ = $1;
+ }
+ }
+ ;
+
+cmdlist: /* VOID */ {
+ $$ = last_sc = NULL;
+ }
+ | cmdlist cmd = {
+ if (last_sc == NULL)
+ $$ = last_sc = $2;
+ else {
+ last_sc->sc_next = $2;
+ last_sc = $2;
+ $$ = $1;
+ }
+ }
+ ;
+
+cmd: INSTALL options opt_namelist SM = {
+ register struct namelist *nl;
+
+ $1->sc_options = $2 | options;
+ if ($3 != NULL) {
+ nl = expand($3, E_VARS);
+ if (nl && nl->n_next != NULL)
+ yyerror("only one name allowed\n");
+ $1->sc_name = nl ? nl->n_name: NULL;
+ if (nl)
+ free(nl);
+ }
+ $$ = $1;
+ }
+ | NOTIFY namelist SM = {
+ if ($2 != NULL)
+ $1->sc_args = expand($2, E_VARS);
+ $$ = $1;
+ }
+ | EXCEPT namelist SM = {
+ if ($2 != NULL)
+ $1->sc_args = expand($2, E_ALL);
+ $$ = $1;
+ }
+ | PATTERN namelist SM = {
+ struct namelist *nl;
+ char *cp, *re_comp();
+
+ /*
+ * We dup the namelist in $2 because expand()
+ * destroys the list referred to in its first
+ * argument.
+ */
+ for (nl = expand(dupnl($2), E_VARS); nl != NULL;
+ nl = nl->n_next)
+ if ((cp = re_comp(nl->n_name)) != NULL)
+ yyerror(cp);
+ $1->sc_args = expand($2, E_VARS);
+ $$ = $1;
+ }
+ | SPECIAL opt_namelist STRING SM = {
+ if ($2 != NULL)
+ $1->sc_args = expand($2, E_ALL);
+ $1->sc_name = $3;
+ $$ = $1;
+ }
+ ;
+
+options: /* VOID */ = {
+ $$ = 0;
+ }
+ | options OPTION = {
+ $$ |= $2;
+ }
+ ;
+
+opt_namelist: /* VOID */ = {
+ $$ = NULL;
+ }
+ | namelist = {
+ $$ = $1;
+ }
+ ;
+
+%%
+
+int yylineno = 1;
+extern FILE *fin;
+
+yylex()
+{
+ static char yytext[INMAX];
+ register int c;
+ register char *cp1, *cp2;
+ static char quotechars[] = "[]{}*?$";
+
+again:
+ switch (c = getc(fin)) {
+ case EOF: /* end of file */
+ return(0);
+
+ case '#': /* start of comment */
+ while ((c = getc(fin)) != EOF && c != '\n')
+ ;
+ if (c == EOF)
+ return(0);
+ case '\n':
+ yylineno++;
+ case ' ':
+ case '\t': /* skip blanks */
+ goto again;
+
+ case '=': /* EQUAL */
+ return(EQUAL);
+
+ case '(': /* LP */
+ return(LP);
+
+ case ')': /* RP */
+ return(RP);
+
+ case ';': /* SM */
+ return(SM);
+
+ case '-': /* -> */
+ if ((c = getc(fin)) == '>')
+ return(ARROW);
+ ungetc(c, fin);
+ c = '-';
+ break;
+
+ case '"': /* STRING */
+ cp1 = yytext;
+ cp2 = &yytext[INMAX - 1];
+ for (;;) {
+ if (cp1 >= cp2) {
+ yyerror("command string too long\n");
+ break;
+ }
+ c = getc(fin);
+ if (c == EOF || c == '"')
+ break;
+ if (c == '\\') {
+ if ((c = getc(fin)) == EOF) {
+ *cp1++ = '\\';
+ break;
+ }
+ }
+ if (c == '\n') {
+ yylineno++;
+ c = ' '; /* can't send '\n' */
+ }
+ *cp1++ = c;
+ }
+ if (c != '"')
+ yyerror("missing closing '\"'\n");
+ *cp1 = '\0';
+ yylval.string = makestr(yytext);
+ return(STRING);
+
+ case ':': /* : or :: */
+ if ((c = getc(fin)) == ':')
+ return(DCOLON);
+ ungetc(c, fin);
+ return(COLON);
+ }
+ cp1 = yytext;
+ cp2 = &yytext[INMAX - 1];
+ for (;;) {
+ if (cp1 >= cp2) {
+ yyerror("input line too long\n");
+ break;
+ }
+ if (c == '\\') {
+ if ((c = getc(fin)) != EOF) {
+ if (any(c, quotechars))
+ c |= QUOTE;
+ } else {
+ *cp1++ = '\\';
+ break;
+ }
+ }
+ *cp1++ = c;
+ c = getc(fin);
+ if (c == EOF || any(c, " \"'\t()=;:\n")) {
+ ungetc(c, fin);
+ break;
+ }
+ }
+ *cp1 = '\0';
+ if (yytext[0] == '-' && yytext[2] == '\0') {
+ switch (yytext[1]) {
+ case 'b':
+ yylval.intval = COMPARE;
+ return(OPTION);
+
+ case 'R':
+ yylval.intval = REMOVE;
+ return(OPTION);
+
+ case 'v':
+ yylval.intval = VERIFY;
+ return(OPTION);
+
+ case 'w':
+ yylval.intval = WHOLE;
+ return(OPTION);
+
+ case 'y':
+ yylval.intval = YOUNGER;
+ return(OPTION);
+
+ case 'h':
+ yylval.intval = FOLLOW;
+ return(OPTION);
+
+ case 'i':
+ yylval.intval = IGNLNKS;
+ return(OPTION);
+ }
+ }
+ if (!strcmp(yytext, "install"))
+ c = INSTALL;
+ else if (!strcmp(yytext, "notify"))
+ c = NOTIFY;
+ else if (!strcmp(yytext, "except"))
+ c = EXCEPT;
+ else if (!strcmp(yytext, "except_pat"))
+ c = PATTERN;
+ else if (!strcmp(yytext, "special"))
+ c = SPECIAL;
+ else {
+ yylval.string = makestr(yytext);
+ return(NAME);
+ }
+ yylval.subcmd = makesubcmd(c);
+ return(c);
+}
+
+any(c, str)
+ register int c;
+ register char *str;
+{
+ while (*str)
+ if (c == *str++)
+ return(1);
+ return(0);
+}
+
+/*
+ * Insert or append ARROW command to list of hosts to be updated.
+ */
+insert(label, files, hosts, subcmds)
+ char *label;
+ struct namelist *files, *hosts;
+ struct subcmd *subcmds;
+{
+ register struct cmd *c, *prev, *nc;
+ register struct namelist *h, *oldh;
+
+ files = expand(files, E_VARS|E_SHELL);
+ hosts = expand(hosts, E_ALL);
+if (debug) {
+ printf("insert: files = ");
+ prnames(files);
+ printf("insert: hosts = ");
+ prnames(hosts);
+ if (cmds)
+ prcmd(cmds);
+ else
+ printf("insert: cmds NULL\n");
+}
+ for (h = hosts; h != NULL; oldh = h, h = h->n_next, free(oldh)) {
+ /*
+ * Search command list for an update to the same host.
+ */
+ for (prev = NULL, c = cmds; c!=NULL; prev = c, c = c->c_next) {
+ if (strcmp(c->c_name, h->n_name) == 0) {
+ do {
+ prev = c;
+ c = c->c_next;
+ } while (c != NULL &&
+ strcmp(c->c_name, h->n_name) == 0);
+ break;
+ }
+ }
+ /*
+ * Insert new command to update host.
+ */
+ nc = ALLOC(cmd);
+ if (nc == NULL)
+ fatal("ran out of memory\n");
+ nc->c_type = ARROW;
+ nc->c_name = h->n_name;
+ nc->c_label = label;
+ nc->c_files = files;
+ nc->c_cmds = subcmds;
+ nc->c_next = c;
+ if (prev == NULL)
+ cmds = nc;
+ else
+ prev->c_next = nc;
+ /* update last_cmd if appending nc to cmds */
+ if (c == NULL)
+ last_cmd = nc;
+ }
+}
+
+/*
+ * Append DCOLON command to the end of the command list since these are always
+ * executed in the order they appear in the distfile.
+ */
+append(label, files, stamp, subcmds)
+ char *label;
+ struct namelist *files;
+ char *stamp;
+ struct subcmd *subcmds;
+{
+ register struct cmd *c;
+
+ c = ALLOC(cmd);
+ if (c == NULL)
+ fatal("ran out of memory\n");
+ c->c_type = DCOLON;
+ c->c_name = stamp;
+ c->c_label = label;
+ c->c_files = expand(files, E_ALL);
+ c->c_cmds = subcmds;
+ c->c_next = NULL;
+ if (cmds == NULL)
+ cmds = last_cmd = c;
+ else {
+ last_cmd->c_next = c;
+ last_cmd = c;
+ }
+}
+
+/*
+ * Error printing routine in parser.
+ */
+yyerror(s)
+ char *s;
+{
+ extern int yychar;
+
+ nerrs++;
+ fflush(stdout);
+ fprintf(stderr, "rdist: line %d: %s\n", yylineno, s);
+}
+
+/*
+ * Return a copy of the string.
+ */
+char *
+makestr(str)
+ char *str;
+{
+ register char *cp, *s;
+
+ str = cp = malloc(strlen(s = str) + 1);
+ if (cp == NULL)
+ fatal("ran out of memory\n");
+ while (*cp++ = *s++)
+ ;
+ return(str);
+}
+
+/*
+ * Allocate a namelist structure.
+ */
+struct namelist *
+makenl(name)
+ char *name;
+{
+ register struct namelist *nl;
+
+ nl = ALLOC(namelist);
+ if (nl == NULL)
+ fatal("ran out of memory\n");
+ nl->n_name = name;
+ nl->n_next = NULL;
+ return(nl);
+}
+
+/*
+ * Duplicate an existing namelist structure. Only used by the PATTERN
+ * code, and then only because expand() is destructive.
+ */
+struct namelist *
+dupnl(old)
+ struct namelist *old;
+{
+ struct namelist *n;
+ struct namelist *new, *newhead = (struct namelist *) NULL;
+ struct namelist *prev = (struct namelist *) NULL;
+
+ for (n = old; n; n = n->n_next) {
+ new = ALLOC(namelist);
+ if (new == (struct namelist *) NULL)
+ fatal("ran out of memory\n");
+ if (newhead == (struct namelist *) NULL)
+ newhead = new;
+ if (n->n_name) {
+ if ((new->n_name = strdup(n->n_name)) == (char *) NULL)
+ fatal("ran out of memory\n");
+ } else
+ new->n_name = (char *) NULL;
+ if (prev)
+ prev->n_next = new;
+ prev = new;
+ }
+ if (prev)
+ prev->n_next = (struct namelist *) NULL;
+
+ return (newhead);
+}
+
+/*
+ * Make a sub command for lists of variables, commands, etc.
+ */
+struct subcmd *
+makesubcmd(type, name)
+ int type;
+ register char *name;
+{
+ register char *cp;
+ register struct subcmd *sc;
+
+ sc = ALLOC(subcmd);
+ if (sc == NULL)
+ fatal("ran out of memory\n");
+ sc->sc_type = type;
+ sc->sc_args = NULL;
+ sc->sc_next = NULL;
+ sc->sc_name = NULL;
+ return(sc);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/rdist/inc.flg b/usr/src/cmd/cmd-inet/usr.bin/rdist/inc.flg
new file mode 100644
index 0000000000..7b417bc433
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/rdist/inc.flg
@@ -0,0 +1,33 @@
+#!/bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+find_files "s.*" usr/src/uts/common/gssapi/mechs/krb5/include
+find_files "s.*" usr/src/lib/gss_mechs/mech_krb5/include
+find_files "s.*.h" usr/src/lib/gss_mechs/mech_krb5/profile
+find_files "s.kcmd*" usr/src/cmd/cmd-inet/common
diff --git a/usr/src/cmd/cmd-inet/usr.bin/rdist/krb5defs.h b/usr/src/cmd/cmd-inet/usr.bin/rdist/krb5defs.h
new file mode 100644
index 0000000000..3aed122ecd
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/rdist/krb5defs.h
@@ -0,0 +1,69 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2002-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _KRB5DEFS_H
+#define _KRB5DEFS_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <libintl.h>
+#include <locale.h>
+#include <profile/prof_int.h>
+#include <com_err.h>
+#include <syslog.h>
+#include <krb5.h>
+#include <kcmd.h>
+
+#define RDIST_BUFSIZ (50 * 1024)
+
+extern krb5_context bsd_context;
+extern krb5_auth_context auth_context;
+extern krb5_flags authopts;
+extern char *krb_cache;
+extern krb5_creds *cred;
+extern krb5_error_code status;
+extern char des_inbuf[2 * RDIST_BUFSIZ]; /* needs to be > largest read size */
+extern char des_outbuf[2 * RDIST_BUFSIZ]; /* needs to be > largest write size */
+extern krb5_data desinbuf, desoutbuf;
+extern krb5_encrypt_block eblock; /* eblock for encrypt/decrypt */
+extern int encrypt_flag; /* Flag set, when encryption is enabled */
+extern int krb5auth_flag; /* Flag set, when KERBEROS is enabled */
+extern int debug_port;
+extern enum kcmd_proto kcmd_proto;
+extern int retval;
+extern char *krb_realm;
+
+static krb5_keyblock *session_key;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _KRB5DEFS_H */
diff --git a/usr/src/cmd/cmd-inet/usr.bin/rdist/lookup.c b/usr/src/cmd/cmd-inet/usr.bin/rdist/lookup.c
new file mode 100644
index 0000000000..5c0eef80ff
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/rdist/lookup.c
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Copyright (c) 1996, by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "defs.h"
+
+ /* symbol types */
+#define VAR 1
+#define CONST 2
+
+struct syment {
+ int s_type;
+ char *s_name;
+ struct namelist *s_value;
+ struct syment *s_next;
+};
+
+static struct syment *hashtab[HASHSIZE];
+
+/*
+ * Define a variable from a command line argument.
+ */
+define(name)
+ char *name;
+{
+ register char *cp, *s;
+ register struct namelist *nl;
+ struct namelist *value;
+
+ if (debug)
+ printf("define(%s)\n", name);
+
+ cp = index(name, '=');
+ if (cp == NULL)
+ value = NULL;
+ else if (cp[1] == '\0') {
+ *cp = '\0';
+ value = NULL;
+ } else if (cp[1] != '(') {
+ *cp++ = '\0';
+ value = makenl(cp);
+ } else {
+ nl = NULL;
+ *cp++ = '\0';
+ do
+ cp++;
+ while (*cp == ' ' || *cp == '\t');
+ for (s = cp; ; s++) {
+ switch (*s) {
+ case ')':
+ *s = '\0';
+ case '\0':
+ break;
+ case ' ':
+ case '\t':
+ *s++ = '\0';
+ while (*s == ' ' || *s == '\t')
+ s++;
+ if (*s == ')')
+ *s = '\0';
+ break;
+ default:
+ continue;
+ }
+ if (nl == NULL)
+ value = nl = makenl(cp);
+ else {
+ nl->n_next = makenl(cp);
+ nl = nl->n_next;
+ }
+ if (*s == '\0')
+ break;
+ cp = s;
+ }
+ }
+ (void) lookup(name, REPLACE, value);
+}
+
+/*
+ * Lookup name in the table and return a pointer to it.
+ * LOOKUP - just do lookup, return NULL if not found.
+ * INSERT - insert name with value, error if already defined.
+ * REPLACE - insert or replace name with value.
+ */
+
+struct namelist *
+lookup(name, action, value)
+ char *name;
+ int action;
+ struct namelist *value;
+{
+ register unsigned n;
+ register char *cp;
+ register struct syment *s;
+ char buf[256];
+
+ if (debug)
+ printf("lookup(%s, %d, %x)\n", name, action, value);
+
+ n = 0;
+ for (cp = name; *cp; )
+ n += *cp++;
+ n %= HASHSIZE;
+
+ for (s = hashtab[n]; s != NULL; s = s->s_next) {
+ if (strcmp(name, s->s_name))
+ continue;
+ if (action != LOOKUP) {
+ if (action != INSERT || s->s_type != CONST) {
+ (void)sprintf(buf, "%.*s redefined",
+ sizeof(buf) - sizeof(" redefined"), name);
+ yyerror(buf);
+ }
+ }
+ return(s->s_value);
+ }
+
+ if (action == LOOKUP) {
+ (void)sprintf(buf, "%.*s undefined",
+ sizeof(buf) - sizeof(" undefined"), name);
+ yyerror(buf);
+ return(NULL);
+ }
+
+ s = ALLOC(syment);
+ if (s == NULL)
+ fatal("ran out of memory\n");
+ s->s_next = hashtab[n];
+ hashtab[n] = s;
+ s->s_type = action == INSERT ? VAR : CONST;
+ s->s_name = name;
+ s->s_value = value;
+ return(value);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/rdist/main.c b/usr/src/cmd/cmd-inet/usr.bin/rdist/main.c
new file mode 100644
index 0000000000..015daefa6b
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/rdist/main.c
@@ -0,0 +1,555 @@
+/*
+ * Copyright 1998-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ */
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "defs.h"
+#include <string.h>
+#include <syslog.h>
+#include <krb5defs.h>
+#include <k5-int.h>
+#include <priv_utils.h>
+
+#define NHOSTS 100
+
+/*
+ * Remote distribution program.
+ */
+
+char *distfile = NULL;
+char Tmpfile[] = "/tmp/rdistXXXXXX";
+char *tmpname = &Tmpfile[5];
+
+int debug; /* debugging flag */
+int nflag; /* NOP flag, just print commands without executing */
+int qflag; /* Quiet. Don't print messages */
+int options; /* global options */
+int iamremote; /* act as remote server for transfering files */
+
+FILE *fin = NULL; /* input file pointer */
+int rem = -1; /* file descriptor to remote source/sink process */
+char host[32]; /* host name */
+int nerrs; /* number of errors while sending/receiving */
+char user[10]; /* user's name */
+char homedir[128]; /* user's home directory */
+char buf[RDIST_BUFSIZ]; /* general purpose buffer */
+
+struct passwd *pw; /* pointer to static area used by getpwent */
+struct group *gr; /* pointer to static area used by getgrent */
+
+char des_inbuf[2 * RDIST_BUFSIZ]; /* needs to be > largest read size */
+char des_outbuf[2 * RDIST_BUFSIZ]; /* needs to be > largest write size */
+krb5_data desinbuf, desoutbuf;
+krb5_encrypt_block eblock; /* eblock for encrypt/decrypt */
+krb5_context bsd_context;
+krb5_auth_context auth_context;
+krb5_creds *cred;
+char *krb_cache = NULL;
+krb5_flags authopts;
+krb5_error_code status;
+enum kcmd_proto kcmd_proto = KCMD_NEW_PROTOCOL;
+
+int encrypt_flag = 0; /* Flag set when encryption is used */
+int krb5auth_flag = 0; /* Flag set, when KERBEROS is enabled */
+int debug_port = 0;
+
+int retval = 0;
+char *krb_realm = NULL;
+
+/* Flag set, if -PN / -PO is specified */
+static boolean_t rcmdoption_done = B_FALSE;
+
+static int encrypt_done = 0; /* Flag set, if -x is specified */
+profile_options_boolean option[] = {
+ { "encrypt", &encrypt_flag, 0 },
+ { NULL, NULL, 0 }
+};
+
+static char *rcmdproto = NULL;
+profile_option_strings rcmdversion[] = {
+ { "rcmd_protocol", &rcmdproto, 0 },
+ { NULL, NULL, 0 }
+};
+
+char *realmdef[] = { "realms", NULL, "rdist", NULL };
+char *appdef[] = { "appdefaults", "rdist", NULL };
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register char *arg;
+ int cmdargs = 0;
+ char *dhosts[NHOSTS], **hp = dhosts;
+
+ (void) setlocale(LC_ALL, "");
+
+ pw = getpwuid(getuid());
+ if (pw == NULL) {
+ (void) fprintf(stderr, gettext("%s: Who are you?\n"), argv[0]);
+ exit(1);
+ }
+ strncpy(user, pw->pw_name, sizeof (user));
+ user[sizeof (user) - 1] = '\0';
+ strncpy(homedir, pw->pw_dir, sizeof (homedir));
+ homedir[sizeof (homedir) - 1] = '\0';
+ gethostname(host, sizeof (host));
+
+ while (--argc > 0) {
+ if ((arg = *++argv)[0] != '-')
+ break;
+ if ((strcmp(arg, "-Server") == 0))
+ iamremote++;
+ else while (*++arg) {
+ if (strncmp(*argv, "-PO", 3) == 0) {
+ if (rcmdoption_done == B_TRUE) {
+ (void) fprintf(stderr, gettext("rdist: "
+ "Only one of -PN "
+ "and -PO allowed.\n"));
+ usage();
+ }
+ kcmd_proto = KCMD_OLD_PROTOCOL;
+ krb5auth_flag++;
+ rcmdoption_done = B_TRUE;
+ break;
+ }
+ if (strncmp(*argv, "-PN", 3) == 0) {
+ if (rcmdoption_done == B_TRUE) {
+ (void) fprintf(stderr, gettext("rdist: "
+ "Only one of -PN "
+ "and -PO allowed.\n"));
+ usage();
+ }
+ kcmd_proto = KCMD_NEW_PROTOCOL;
+ krb5auth_flag++;
+ rcmdoption_done = B_TRUE;
+ break;
+ }
+
+ switch (*arg) {
+#ifdef DEBUG
+ case 'p':
+ if (--argc <= 0)
+ usage();
+ debug_port = htons(atoi(*++argv));
+ break;
+#endif /* DEBUG */
+ case 'k':
+ if (--argc <= 0) {
+ (void) fprintf(stderr, gettext("rdist: "
+ "-k flag must be followed with "
+ " a realm name.\n"));
+ exit(1);
+ }
+ if ((krb_realm = strdup(*++argv)) == NULL) {
+ (void) fprintf(stderr, gettext("rdist: "
+ "Cannot malloc.\n"));
+ exit(1);
+ }
+ krb5auth_flag++;
+ break;
+
+ case 'a':
+ krb5auth_flag++;
+ break;
+
+ case 'x':
+ encrypt_flag++;
+ encrypt_done++;
+ krb5auth_flag++;
+ break;
+
+ case 'f':
+ if (--argc <= 0)
+ usage();
+ distfile = *++argv;
+ if (distfile[0] == '-' && distfile[1] == '\0')
+ fin = stdin;
+ break;
+
+ case 'm':
+ if (--argc <= 0)
+ usage();
+ if (hp >= &dhosts[NHOSTS-2]) {
+ (void) fprintf(stderr, gettext("rdist:"
+ " too many destination"
+ " hosts\n"));
+ exit(1);
+ }
+ *hp++ = *++argv;
+ break;
+
+ case 'd':
+ if (--argc <= 0)
+ usage();
+ define(*++argv);
+ break;
+
+ case 'D':
+ debug++;
+ break;
+
+ case 'c':
+ cmdargs++;
+ break;
+
+ case 'n':
+ if (options & VERIFY) {
+ printf("rdist: -n overrides -v\n");
+ options &= ~VERIFY;
+ }
+ nflag++;
+ break;
+
+ case 'q':
+ qflag++;
+ break;
+
+ case 'b':
+ options |= COMPARE;
+ break;
+
+ case 'R':
+ options |= REMOVE;
+ break;
+
+ case 'v':
+ if (nflag) {
+ printf("rdist: -n overrides -v\n");
+ break;
+ }
+ options |= VERIFY;
+ break;
+
+ case 'w':
+ options |= WHOLE;
+ break;
+
+ case 'y':
+ options |= YOUNGER;
+ break;
+
+ case 'h':
+ options |= FOLLOW;
+ break;
+
+ case 'i':
+ options |= IGNLNKS;
+ break;
+
+ default:
+ usage();
+ }
+ }
+ }
+ *hp = NULL;
+
+ mktemp(Tmpfile);
+
+ if (krb5auth_flag > 0) {
+ status = krb5_init_context(&bsd_context);
+ if (status) {
+ com_err("rdist", status,
+ gettext("while initializing krb5"));
+ exit(1);
+ }
+
+ /* Set up des buffers */
+ desinbuf.data = des_inbuf;
+ desoutbuf.data = des_outbuf;
+ desinbuf.length = sizeof (des_inbuf);
+ desoutbuf.length = sizeof (des_outbuf);
+
+ /*
+ * Get our local realm to look up local realm options.
+ */
+ status = krb5_get_default_realm(bsd_context, &realmdef[1]);
+ if (status) {
+ com_err("rdist", status,
+ gettext("while getting default realm"));
+ exit(1);
+ }
+ /*
+ * See if encryption should be done for this realm
+ */
+ profile_get_options_boolean(bsd_context->profile, realmdef,
+ option);
+ /*
+ * Check the appdefaults section
+ */
+ profile_get_options_boolean(bsd_context->profile, appdef,
+ option);
+ profile_get_options_string(bsd_context->profile, appdef,
+ rcmdversion);
+
+ if ((encrypt_done > 0) || (encrypt_flag > 0)) {
+ if (krb5_privacy_allowed() == TRUE) {
+ encrypt_flag++;
+ } else {
+ (void) fprintf(stderr, gettext("rdist: "
+ "Encryption not supported.\n"));
+ exit(1);
+ }
+ }
+
+ if ((rcmdoption_done == B_FALSE) && (rcmdproto != NULL)) {
+ if (strncmp(rcmdproto, "rcmdv2", 6) == 0) {
+ kcmd_proto = KCMD_NEW_PROTOCOL;
+ } else if (strncmp(rcmdproto, "rcmdv1", 6) == 0) {
+ kcmd_proto = KCMD_OLD_PROTOCOL;
+ } else {
+ (void) fprintf(stderr, gettext("Unrecognized "
+ "KCMD protocol (%s)"), rcmdproto);
+ exit(1);
+ }
+ }
+ }
+
+ if (iamremote) {
+ setreuid(getuid(), getuid());
+ server();
+ exit(nerrs != 0);
+ }
+ if (__init_suid_priv(0, PRIV_NET_PRIVADDR, NULL) == -1) {
+ (void) fprintf(stderr,
+ "rdist needs to run with sufficient privilege\n");
+ exit(1);
+ }
+
+ if (cmdargs)
+ docmdargs(argc, argv);
+ else {
+ if (fin == NULL) {
+ if (distfile == NULL) {
+ if ((fin = fopen("distfile", "r")) == NULL)
+ fin = fopen("Distfile", "r");
+ } else
+ fin = fopen(distfile, "r");
+ if (fin == NULL) {
+ perror(distfile ? distfile : "distfile");
+ exit(1);
+ }
+ }
+ yyparse();
+ if (nerrs == 0)
+ docmds(dhosts, argc, argv);
+ }
+
+ exit(nerrs != 0);
+ /* NOTREACHED */
+}
+
+usage()
+{
+ printf(gettext("Usage: rdist [-nqbhirvwyDax] [-PN / -PO] "
+#ifdef DEBUG
+ "[-p port] "
+#endif /* DEBUG */
+ "[-k realm] [-f distfile] [-d var=value] [-m host] [file ...]\n"));
+ printf(gettext("or: rdist [-nqbhirvwyDax] [-PN / -PO] [-p port] "
+ "[-k realm] -c source [...] machine[:dest]\n"));
+ exit(1);
+}
+
+/*
+ * rcp like interface for distributing files.
+ */
+docmdargs(nargs, args)
+ int nargs;
+ char *args[];
+{
+ register struct namelist *nl, *prev;
+ register char *cp;
+ struct namelist *files, *hosts;
+ struct subcmd *cmds;
+ char *dest;
+ static struct namelist tnl = { NULL, NULL };
+ int i;
+
+ if (nargs < 2)
+ usage();
+
+ prev = NULL;
+ for (i = 0; i < nargs - 1; i++) {
+ nl = makenl(args[i]);
+ if (prev == NULL)
+ files = prev = nl;
+ else {
+ prev->n_next = nl;
+ prev = nl;
+ }
+ }
+
+ cp = args[i];
+ if ((dest = index(cp, ':')) != NULL)
+ *dest++ = '\0';
+ tnl.n_name = cp;
+ hosts = expand(&tnl, E_ALL);
+ if (nerrs)
+ exit(1);
+
+ if (dest == NULL || *dest == '\0')
+ cmds = NULL;
+ else {
+ cmds = makesubcmd(INSTALL);
+ cmds->sc_options = options;
+ cmds->sc_name = dest;
+ }
+
+ if (debug) {
+ printf("docmdargs()\nfiles = ");
+ prnames(files);
+ printf("hosts = ");
+ prnames(hosts);
+ }
+ insert(NULL, files, hosts, cmds);
+ docmds(NULL, 0, NULL);
+}
+
+/*
+ * Print a list of NAME blocks (mostly for debugging).
+ */
+prnames(nl)
+ register struct namelist *nl;
+{
+ printf("( ");
+ while (nl != NULL) {
+ printf("%s ", nl->n_name);
+ nl = nl->n_next;
+ }
+ printf(")\n");
+}
+
+prcmd(c)
+ struct cmd *c;
+{
+ extern char *prtype();
+
+ while (c) {
+ printf("c_type %s, c_name %s, c_label %s, c_files ",
+ prtype(c->c_type), c->c_name,
+ c->c_label? c->c_label : "NULL");
+ prnames(c->c_files);
+ prsubcmd(c->c_cmds);
+ c = c->c_next;
+ }
+}
+
+prsubcmd(s)
+ struct subcmd *s;
+{
+ extern char *prtype();
+ extern char *proptions();
+
+ while (s) {
+ printf("sc_type %s, sc_options %d%s, sc_name %s, sc_args ",
+ prtype(s->sc_type),
+ s->sc_options, proptions(s->sc_options),
+ s->sc_name ? s->sc_name : "NULL");
+ prnames(s->sc_args);
+ s = s->sc_next;
+ }
+}
+
+char *
+prtype(t)
+ int t;
+{
+ switch (t) {
+ case EQUAL:
+ return ("EQUAL");
+ case LP:
+ return ("LP");
+ case RP:
+ return ("RP");
+ case SM:
+ return ("SM");
+ case ARROW:
+ return ("ARROW");
+ case COLON:
+ return ("COLON");
+ case DCOLON:
+ return ("DCOLON");
+ case NAME:
+ return ("NAME");
+ case STRING:
+ return ("STRING");
+ case INSTALL:
+ return ("INSTALL");
+ case NOTIFY:
+ return ("NOTIFY");
+ case EXCEPT:
+ return ("EXCEPT");
+ case PATTERN:
+ return ("PATTERN");
+ case SPECIAL:
+ return ("SPECIAL");
+ case OPTION:
+ return ("OPTION");
+ }
+}
+
+char *
+proptions(o)
+ int o;
+{
+ return (printb((unsigned short) o, OBITS));
+}
+
+char *
+printb(v, bits)
+ register char *bits;
+ register unsigned short v;
+{
+ register int i, any = 0;
+ register char c;
+ char *p = buf;
+
+ bits++;
+ if (bits) {
+
+ *p++ = '<';
+ while ((i = *bits++) != 0) {
+ if (v & (1 << (i-1))) {
+ if (any)
+ *p++ = ',';
+ any = 1;
+ for (; (c = *bits) > 32; bits++)
+ *p++ = c;
+ } else
+ for (; *bits > 32; bits++)
+ ;
+ }
+ *p++ = '>';
+ }
+
+ *p = '\0';
+ return (buf);
+}
+
+/*VARARGS*/
+warn(fmt, a1, a2, a3)
+ char *fmt;
+{
+ extern int yylineno;
+
+ fprintf(stderr, "rdist: line %d: Warning: ", yylineno);
+ fprintf(stderr, fmt, a1, a2, a3);
+ fputc('\n', stderr);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/rdist/server.c b/usr/src/cmd/cmd-inet/usr.bin/rdist/server.c
new file mode 100644
index 0000000000..8940f3ca60
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/rdist/server.c
@@ -0,0 +1,1691 @@
+/*
+ * Copyright 1998-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ */
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "defs.h"
+#include <signal.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <ctype.h>
+#include <krb5defs.h>
+
+/*
+ * If we want to write *to* the client rdist program, *from* the server
+ * side (server-side child `rdist -Server' process exec'ed off of in.rshd),
+ * we write to stdout/stderr, since there is a pipe connecting stdout/stderr
+ * to the outside world (which is why we use `wrem' and not `rem').
+ */
+int wrem = 1;
+
+#define ack() (void) write(wrem, "\0\n", 2)
+#define err() (void) write(wrem, "\1\n", 2)
+
+/*
+ * Set when a desread() is reqd. in response()
+ */
+
+struct linkbuf *ihead; /* list of files with more than one link */
+char buf[RDIST_BUFSIZ]; /* general purpose buffer */
+char source[RDIST_BUFSIZ]; /* base source directory name */
+char destination[RDIST_BUFSIZ]; /* base destination directory name */
+char target[RDIST_BUFSIZ]; /* target/source directory name */
+char *tp; /* pointer to end of target name */
+char *Tdest; /* pointer to last T dest */
+int catname; /* cat name to target name */
+char *stp[32]; /* stack of saved tp's for directories */
+int oumask; /* old umask for creating files */
+
+extern FILE *lfp; /* log file for mailing changes */
+
+void cleanup();
+struct linkbuf *savelink();
+char *strsub();
+
+/*
+ * Server routine to read requests and process them.
+ * Commands are:
+ * Tname - Transmit file if out of date
+ * Vname - Verify if file out of date or not
+ * Qname - Query if file exists. Return mtime & size if it does.
+ */
+server()
+{
+ char cmdbuf[RDIST_BUFSIZ];
+ register char *cp;
+
+ signal(SIGHUP, cleanup);
+ signal(SIGINT, cleanup);
+ signal(SIGQUIT, cleanup);
+ signal(SIGTERM, cleanup);
+ signal(SIGPIPE, cleanup);
+
+ rem = 0;
+ oumask = umask(0);
+
+ (void) sprintf(buf, "V%d\n", VERSION);
+ (void) write(wrem, buf, strlen(buf));
+
+ for (;;) {
+ cp = cmdbuf;
+ if (read(rem, cp, 1) <= 0)
+ return;
+ if (*cp++ == '\n') {
+ error("server: expected control record\n");
+ continue;
+ }
+ do {
+ if (read(rem, cp, 1) != 1)
+ cleanup();
+ } while (*cp++ != '\n' && cp < &cmdbuf[RDIST_BUFSIZ]);
+ *--cp = '\0';
+ cp = cmdbuf;
+ switch (*cp++) {
+ case 'T': /* init target file/directory name */
+ catname = 1; /* target should be directory */
+ goto dotarget;
+
+ case 't': /* init target file/directory name */
+ catname = 0;
+ dotarget:
+ if (exptilde(target, sizeof (target), cp) == NULL)
+ continue;
+ tp = target;
+ while (*tp)
+ tp++;
+ ack();
+ continue;
+
+ case 'R': /* Transfer a regular file. */
+ recvf(cp, S_IFREG);
+ continue;
+
+ case 'D': /* Transfer a directory. */
+ recvf(cp, S_IFDIR);
+ continue;
+
+ case 'K': /* Transfer symbolic link. */
+ recvf(cp, S_IFLNK);
+ continue;
+
+ case 'k': /* Transfer hard link. */
+ hardlink(cp);
+ continue;
+
+ case 'E': /* End. (of directory) */
+ *tp = '\0';
+ if (catname <= 0) {
+ error("server: too many 'E's\n");
+ continue;
+ }
+ tp = stp[--catname];
+ *tp = '\0';
+ ack();
+ continue;
+
+ case 'C': /* Clean. Cleanup a directory */
+ clean(cp);
+ continue;
+
+ case 'Q': /* Query. Does the file/directory exist? */
+ query(cp);
+ continue;
+
+ case 'S': /* Special. Execute commands */
+ dospecial(cp);
+ continue;
+
+#ifdef notdef
+ /*
+ * These entries are reserved but not currently used.
+ * The intent is to allow remote hosts to have master copies.
+ * Currently, only the host rdist runs on can have masters.
+ */
+ case 'X': /* start a new list of files to exclude */
+ except = bp = NULL;
+ case 'x': /* add name to list of files to exclude */
+ if (*cp == '\0') {
+ ack();
+ continue;
+ }
+ if (*cp == '~') {
+ if (exptilde(buf, sizeof (buf), cp) == NULL)
+ continue;
+ cp = buf;
+ }
+ if (bp == NULL)
+ except = bp = expand(makeblock(NAME, cp),
+ E_VARS);
+ else
+ bp->b_next = expand(makeblock(NAME, cp),
+ E_VARS);
+ while (bp->b_next != NULL)
+ bp = bp->b_next;
+ ack();
+ continue;
+
+ case 'I': /* Install. Transfer file if out of date. */
+ opts = 0;
+ while (*cp >= '0' && *cp <= '7')
+ opts = (opts << 3) | (*cp++ - '0');
+ if (*cp++ != ' ') {
+ error("server: options not delimited\n");
+ return;
+ }
+ install(cp, opts);
+ continue;
+
+ case 'L': /* Log. save message in log file */
+ log(lfp, cp);
+ continue;
+#endif
+
+ case '\1':
+ nerrs++;
+ continue;
+
+ case '\2':
+ return;
+
+ default:
+ error("server: unknown command '%s'\n", cp);
+ case '\0':
+ continue;
+ }
+ }
+}
+
+/*
+ * Update the file(s) if they are different.
+ * destdir = 1 if destination should be a directory
+ * (i.e., more than one source is being copied to the same destination).
+ */
+install(src, dest, destdir, opts)
+ char *src, *dest;
+ int destdir, opts;
+{
+ char *rname;
+ char destcopy[RDIST_BUFSIZ];
+
+ if (dest == NULL) {
+ opts &= ~WHOLE; /* WHOLE mode only useful if renaming */
+ dest = src;
+ }
+
+ if (nflag || debug) {
+ printf("%s%s%s%s%s %s %s\n", opts & VERIFY ? "verify":"install",
+ opts & WHOLE ? " -w" : "",
+ opts & YOUNGER ? " -y" : "",
+ opts & COMPARE ? " -b" : "",
+ opts & REMOVE ? " -R" : "", src, dest);
+ if (nflag)
+ return;
+ }
+
+ rname = exptilde(target, sizeof (target), src);
+ if (rname == NULL)
+ return;
+ tp = target;
+ while (*tp)
+ tp++;
+ /*
+ * If we are renaming a directory and we want to preserve
+ * the directory heirarchy (-w), we must strip off the leading
+ * directory name and preserve the rest.
+ */
+ if (opts & WHOLE) {
+ while (*rname == '/')
+ rname++;
+ destdir = 1;
+ } else {
+ rname = rindex(target, '/');
+ if (rname == NULL)
+ rname = target;
+ else
+ rname++;
+ }
+ if (debug)
+ printf("target = %s, rname = %s\n", target, rname);
+ /*
+ * Pass the destination file/directory name to remote.
+ */
+ if (snprintf(buf, sizeof (buf), "%c%s\n", destdir ? 'T' : 't', dest) >=
+ sizeof (buf)) {
+ error("%s: Name too long\n", dest);
+ return;
+ }
+ if (debug)
+ printf("buf = %s", buf);
+ (void) deswrite(rem, buf, strlen(buf), 0);
+
+ if (response() < 0)
+ return;
+
+ strcpy(source, src);
+ if (destdir) {
+ strcpy(destcopy, dest);
+ Tdest = destcopy;
+ strcpy(destination, rname);
+ } else {
+ strcpy(destination, dest);
+ }
+ sendf(rname, opts);
+ Tdest = 0;
+}
+
+#define protoname() (pw ? pw->pw_name : user)
+#define protogroup() (gr ? gr->gr_name : group)
+/*
+ * Transfer the file or directory in target[].
+ * rname is the name of the file on the remote host.
+ */
+sendf(rname, opts)
+ char *rname;
+ int opts;
+{
+ register struct subcmd *sc;
+ struct stat stb;
+ int sizerr, f, u, len;
+ off_t i;
+ DIR *d;
+ struct dirent *dp;
+ char *otp, *cp;
+ extern struct subcmd *subcmds;
+ static char user[15], group[15];
+
+ if (debug)
+ printf("sendf(%s, %x%s)\n", rname, opts, printb(opts, OBITS));
+
+ if (except(target))
+ return;
+ if ((opts & FOLLOW ? stat(target, &stb) : lstat(target, &stb)) < 0) {
+ error("%s: %s\n", target, strerror(errno));
+ return;
+ }
+ if (index(rname, '\n')) {
+ error("file name '%s' contains an embedded newline - "
+ "can't update\n", rname);
+ return;
+ }
+ if ((u = update(rname, opts, &stb)) == 0) {
+ if ((stb.st_mode & S_IFMT) == S_IFREG && stb.st_nlink > 1)
+ (void) savelink(&stb, opts);
+ return;
+ }
+
+ if (pw == NULL || pw->pw_uid != stb.st_uid)
+ if ((pw = getpwuid(stb.st_uid)) == NULL) {
+ log(lfp, "%s: no password entry for uid %d \n",
+ target, stb.st_uid);
+ pw = NULL;
+ sprintf(user, ":%d", stb.st_uid);
+ }
+ if (gr == NULL || gr->gr_gid != stb.st_gid)
+ if ((gr = getgrgid(stb.st_gid)) == NULL) {
+ log(lfp, "%s: no name for group %d\n",
+ target, stb.st_gid);
+ gr = NULL;
+ sprintf(group, ":%d", stb.st_gid);
+ }
+ if (u == 1) {
+ if (opts & VERIFY) {
+ log(lfp, "need to install: %s\n", target);
+ goto dospecial;
+ }
+ log(lfp, "installing: %s\n", target);
+ opts &= ~(COMPARE|REMOVE);
+ }
+
+ switch (stb.st_mode & S_IFMT) {
+ case S_IFDIR:
+ if ((d = opendir(target)) == NULL) {
+ error("%s: %s\n", target, strerror(errno));
+ return;
+ }
+ if (snprintf(buf, sizeof (buf), "D%o %04o 0 0 %s %s %s\n",
+ opts, stb.st_mode & 07777, protoname(), protogroup(),
+ rname) >= sizeof (buf)) {
+ error("%s: Name too long\n", rname);
+ closedir(d);
+ return;
+ }
+ if (debug)
+ printf("buf = %s", buf);
+ (void) deswrite(rem, buf, strlen(buf), 0);
+ if (response() < 0) {
+ closedir(d);
+ return;
+ }
+
+ if (opts & REMOVE)
+ rmchk(opts);
+
+ otp = tp;
+ len = tp - target;
+ while (dp = readdir(d)) {
+ if ((strcmp(dp->d_name, ".") == 0)||
+ (strcmp(dp->d_name, "..") == 0))
+ continue;
+ if ((int)(len + 1 + strlen(dp->d_name)) >=
+ (int)(RDIST_BUFSIZ - 1)) {
+ error("%.*s/%s: Name too long\n", len, target,
+ dp->d_name);
+ continue;
+ }
+ tp = otp;
+ *tp++ = '/';
+ cp = dp->d_name;
+ while (*tp++ = *cp++)
+ ;
+ tp--;
+ sendf(dp->d_name, opts);
+ }
+ closedir(d);
+ (void) deswrite(rem, "E\n", 2, 0);
+ (void) response();
+ tp = otp;
+ *tp = '\0';
+ return;
+
+ case S_IFLNK:
+ if (u != 1)
+ opts |= COMPARE;
+ if (stb.st_nlink > 1) {
+ struct linkbuf *lp;
+
+ if ((lp = savelink(&stb, opts)) != NULL) {
+ /* install link */
+ if (*lp->target == 0)
+ len = snprintf(buf, sizeof (buf),
+ "k%o %s %s\n", opts, lp->pathname,
+ rname);
+ else
+ len = snprintf(buf, sizeof (buf),
+ "k%o %s/%s %s\n", opts, lp->target,
+ lp->pathname, rname);
+ if (len >= sizeof (buf)) {
+ error("%s: Name too long\n", rname);
+ return;
+ }
+ if (debug)
+ printf("buf = %s", buf);
+ (void) deswrite(rem, buf, strlen(buf), 0);
+ (void) response();
+ return;
+ }
+ }
+ (void) snprintf(buf, sizeof (buf), "K%o %o %ld %ld %s %s %s\n",
+ opts, stb.st_mode & 07777, stb.st_size, stb.st_mtime,
+ protoname(), protogroup(), rname);
+ if (debug)
+ printf("buf = %s", buf);
+ (void) deswrite(rem, buf, strlen(buf), 0);
+ if (response() < 0)
+ return;
+ sizerr = (readlink(target, buf, RDIST_BUFSIZ) != stb.st_size);
+ (void) deswrite(rem, buf, stb.st_size, 0);
+ if (debug)
+ printf("readlink = %.*s\n", (int)stb.st_size, buf);
+ goto done;
+
+ case S_IFREG:
+ break;
+
+ default:
+ error("%s: not a file or directory\n", target);
+ return;
+ }
+
+ if (u == 2) {
+ if (opts & VERIFY) {
+ log(lfp, "need to update: %s\n", target);
+ goto dospecial;
+ }
+ log(lfp, "updating: %s\n", target);
+ }
+
+ if (stb.st_nlink > 1) {
+ struct linkbuf *lp;
+
+ if ((lp = savelink(&stb, opts)) != NULL) {
+ /* install link */
+ if (*lp->target == 0)
+ len = snprintf(buf, sizeof (buf), "k%o %s %s\n",
+ opts, lp->pathname, rname);
+ else
+ len = snprintf(buf, sizeof (buf),
+ "k%o %s/%s %s\n", opts, lp->target,
+ lp->pathname, rname);
+ if (len >= sizeof (buf)) {
+ error("%s: Name too long\n", rname);
+ return;
+ }
+ if (debug)
+ printf("buf = %s", buf);
+ (void) deswrite(rem, buf, strlen(buf), 0);
+ (void) response();
+ return;
+ }
+ }
+
+ if ((f = open(target, 0)) < 0) {
+ error("%s: %s\n", target, strerror(errno));
+ return;
+ }
+ (void) snprintf(buf, sizeof (buf), "R%o %o %ld %ld %s %s %s\n", opts,
+ stb.st_mode & 07777, stb.st_size, stb.st_mtime,
+ protoname(), protogroup(), rname);
+ if (debug)
+ printf("buf = %s", buf);
+ (void) deswrite(rem, buf, strlen(buf), 0);
+
+ if (response() < 0) {
+ (void) close(f);
+ return;
+ }
+
+ sizerr = 0;
+
+ for (i = 0; i < stb.st_size; i += RDIST_BUFSIZ) {
+ int amt = RDIST_BUFSIZ;
+ if (i + amt > stb.st_size)
+ amt = stb.st_size - i;
+ if (sizerr == 0 && read(f, buf, amt) != amt)
+ sizerr = 1;
+ (void) deswrite(rem, buf, amt, 0);
+ }
+ (void) close(f);
+done:
+ if (sizerr) {
+ error("%s: file changed size\n", target);
+ (void) deswrite(rem, "\1\n", 2, 0);
+ } else
+ (void) deswrite(rem, "\0\n", 2, 0);
+ f = response();
+
+ if (f < 0 || f == 0 && (opts & COMPARE))
+ return;
+dospecial:
+ for (sc = subcmds; sc != NULL; sc = sc->sc_next) {
+ if (sc->sc_type != SPECIAL)
+ continue;
+ if (sc->sc_args != NULL && !inlist(sc->sc_args, target))
+ continue;
+ log(lfp, "special \"%s\"\n", sc->sc_name);
+ if (opts & VERIFY)
+ continue;
+ (void) snprintf(buf, sizeof (buf), "SFILE=%s;%s\n", target,
+ sc->sc_name);
+ if (debug)
+ printf("buf = %s", buf);
+ (void) deswrite(rem, buf, strlen(buf), 0);
+ while (response() > 0)
+ ;
+ }
+}
+
+struct linkbuf *
+savelink(stp, opts)
+ struct stat *stp;
+ int opts;
+{
+ struct linkbuf *lp;
+
+ for (lp = ihead; lp != NULL; lp = lp->nextp)
+ if (lp->inum == stp->st_ino && lp->devnum == stp->st_dev) {
+ lp->count--;
+ return (lp);
+ }
+ lp = (struct linkbuf *)malloc(sizeof (*lp));
+ if (lp == NULL)
+ log(lfp, "out of memory, link information lost\n");
+ else {
+ lp->nextp = ihead;
+ ihead = lp;
+ lp->inum = stp->st_ino;
+ lp->devnum = stp->st_dev;
+ lp->count = stp->st_nlink - 1;
+ strcpy(lp->pathname,
+ opts & WHOLE ?
+ target : strsub(source, destination, target));
+ if (Tdest)
+ strcpy(lp->target, Tdest);
+ else
+ *lp->target = 0;
+ }
+ return (NULL);
+}
+
+/*
+ * Check to see if file needs to be updated on the remote machine.
+ * Returns 0 if no update, 1 if remote doesn't exist, 2 if out of date
+ * and 3 if comparing binaries to determine if out of date.
+ */
+update(rname, opts, stp)
+ char *rname;
+ int opts;
+ struct stat *stp;
+{
+ register char *cp, *s;
+ register off_t size;
+ register time_t mtime;
+
+ if (debug)
+ printf("update(%s, %x%s, %x)\n", rname, opts,
+ printb(opts, OBITS), stp);
+
+ /*
+ * Check to see if the file exists on the remote machine.
+ */
+ if (snprintf(buf, sizeof (buf), "Q%s\n", rname) >= sizeof (buf)) {
+ error("%s: Name too long\n", rname);
+ return (0);
+ }
+ if (debug)
+ printf("buf = %s", buf);
+ (void) deswrite(rem, buf, strlen(buf), 0);
+again:
+ cp = s = buf;
+more:
+ do {
+ if (desread(rem, cp, 1, 0) != 1)
+ lostconn();
+ } while (*cp++ != '\n' && cp < &buf[RDIST_BUFSIZ]);
+
+ if (cp < &buf[RDIST_BUFSIZ])
+ *cp = '\0';
+ if (debug) {
+ printf("update reply: ");
+ switch (*s) {
+ case 'Y':
+ case 'N':
+ putchar(*s);
+ break;
+ default:
+ if (iscntrl(*s)) {
+ putchar('^');
+ putchar('A' + *s - 1);
+ } else
+ printf("%#x", *s & 0xff);
+ break;
+ }
+ printf("%s", &s[1]);
+ }
+
+ switch (*s++) {
+ case 'Y':
+ break;
+
+ case 'N': /* file doesn't exist so install it */
+ return (1);
+
+ case '\1':
+ nerrs++;
+ if (*s != '\n') {
+ if (!iamremote) {
+ fflush(stdout);
+ (void) write(2, s, cp - s);
+ }
+ if (lfp != NULL)
+ (void) fwrite(s, 1, cp - s, lfp);
+ }
+ if (cp == &buf[RDIST_BUFSIZ] && *(cp - 1) != '\n') {
+ /* preserve status code */
+ cp = s;
+ s = buf;
+ goto more;
+ }
+ return (0);
+
+ case '\3':
+ *--cp = '\0';
+ if (lfp != NULL)
+ log(lfp, "update: note: %s\n", s);
+ goto again;
+
+ default:
+ *--cp = '\0';
+ error("update: unexpected response '%s'\n", s);
+ return (0);
+ }
+
+ if (*s == '\n')
+ return (2);
+
+ if (opts & COMPARE)
+ return (3);
+
+ size = 0;
+ while (isdigit(*s))
+ size = size * 10 + (*s++ - '0');
+ if (*s++ != ' ') {
+ error("update: size not delimited\n");
+ return (0);
+ }
+ mtime = 0;
+ while (isdigit(*s))
+ mtime = mtime * 10 + (*s++ - '0');
+ if (*s != '\n') {
+ error("update: mtime not delimited\n");
+ return (0);
+ }
+ /*
+ * File needs to be updated?
+ */
+ if (opts & YOUNGER) {
+ if (stp->st_mtime == mtime)
+ return (0);
+ if (stp->st_mtime < mtime) {
+ log(lfp, "Warning: %s: remote copy is newer\n", target);
+ return (0);
+ }
+ } else if (stp->st_mtime == mtime && stp->st_size == size)
+ return (0);
+ return (2);
+}
+
+/*
+ * Query. Check to see if file exists. Return one of the following:
+ * N\n - doesn't exist
+ * Ysize mtime\n - exists and its a regular file (size & mtime of file)
+ * Y\n - exists and its a directory or symbolic link
+ * ^Aerror message\n
+ */
+query(name)
+ char *name;
+{
+ struct stat stb;
+
+ if (catname) {
+ if (sizeof (target) - (tp - target) >= strlen(name) + 2) {
+ (void) sprintf(tp, "/%s", name);
+ } else {
+ error("%.*s/%s: Name too long\n", tp - target,
+ target, name);
+ return;
+ }
+ }
+
+ if (lstat(target, &stb) < 0) {
+ if (errno == ENOENT)
+ (void) write(wrem, "N\n", 2);
+ else
+ error("%s:%s: %s\n", host, target, strerror(errno));
+ *tp = '\0';
+ return;
+ }
+
+ switch (stb.st_mode & S_IFMT) {
+ case S_IFREG:
+ (void) sprintf(buf, "Y%ld %ld\n", stb.st_size, stb.st_mtime);
+ (void) write(wrem, buf, strlen(buf));
+ break;
+
+ case S_IFLNK:
+ case S_IFDIR:
+ (void) write(wrem, "Y\n", 2);
+ break;
+
+ default:
+ error("%s: not a file or directory\n", name);
+ break;
+ }
+ *tp = '\0';
+}
+
+recvf(cmd, type)
+ char *cmd;
+ int type;
+{
+ register char *cp;
+ int f, mode, opts, wrerr, olderrno;
+ off_t i, size;
+ time_t mtime;
+ struct stat stb;
+ struct timeval tvp[2];
+ char *owner, *group;
+ char new[RDIST_BUFSIZ];
+ extern char *tmpname;
+
+ cp = cmd;
+ opts = 0;
+ while (*cp >= '0' && *cp <= '7')
+ opts = (opts << 3) | (*cp++ - '0');
+ if (*cp++ != ' ') {
+ error("recvf: options not delimited\n");
+ return;
+ }
+ mode = 0;
+ while (*cp >= '0' && *cp <= '7')
+ mode = (mode << 3) | (*cp++ - '0');
+ if (*cp++ != ' ') {
+ error("recvf: mode not delimited\n");
+ return;
+ }
+ size = 0;
+ while (isdigit(*cp))
+ size = size * 10 + (*cp++ - '0');
+ if (*cp++ != ' ') {
+ error("recvf: size not delimited\n");
+ return;
+ }
+ mtime = 0;
+ while (isdigit(*cp))
+ mtime = mtime * 10 + (*cp++ - '0');
+ if (*cp++ != ' ') {
+ error("recvf: mtime not delimited\n");
+ return;
+ }
+ owner = cp;
+ while (*cp && *cp != ' ')
+ cp++;
+ if (*cp != ' ') {
+ error("recvf: owner name not delimited\n");
+ return;
+ }
+ *cp++ = '\0';
+ group = cp;
+ while (*cp && *cp != ' ')
+ cp++;
+ if (*cp != ' ') {
+ error("recvf: group name not delimited\n");
+ return;
+ }
+ *cp++ = '\0';
+
+ if (type == S_IFDIR) {
+ int isdot;
+
+ if (strcmp(cp, ".") == 0)
+ isdot = 1;
+ else
+ isdot = 0;
+ if (catname >= sizeof (stp) / sizeof (stp[0])) {
+ error("%s:%s: too many directory levels\n",
+ host, target);
+ return;
+ }
+ stp[catname] = tp;
+ if (catname++) {
+ *tp++ = '/';
+ while (*tp++ = *cp++)
+ ;
+ tp--;
+ }
+ if (opts & VERIFY) {
+ ack();
+ return;
+ }
+ if (lstat(target, &stb) == 0) {
+ if (ISDIR(stb.st_mode)) {
+ if ((stb.st_mode & 07777) == mode) {
+ ack();
+ return;
+ }
+ sendrem("%s: Warning: remote mode %o != "
+ "local mode %o", target,
+ stb.st_mode & 07777, mode);
+ return;
+ }
+ errno = ENOTDIR;
+ } else if (errno == ENOENT && (mkdir(target, mode) == 0 ||
+ chkparent(target) == 0 &&
+ (isdot == 1 || mkdir(target, mode) == 0))) {
+ if (chog(target, owner, group, mode) == 0)
+ ack();
+ return;
+ }
+ error("%s:%s: %s\n", host, target, strerror(errno));
+ tp = stp[--catname];
+ *tp = '\0';
+ return;
+ }
+
+ if (catname) {
+ if (sizeof (target) - (tp - target) >= strlen(cp) + 2) {
+ (void) sprintf(tp, "/%s", cp);
+ } else {
+ error("%.*s/%s: Name too long\n", tp - target,
+ target, cp);
+ return;
+ }
+ }
+ cp = rindex(target, '/');
+ if (cp == NULL)
+ strcpy(new, tmpname);
+ else if (cp == target)
+ (void) sprintf(new, "/%s", tmpname);
+ else {
+ *cp = '\0';
+ (void) sprintf(new, "%s/%s", target, tmpname);
+ *cp = '/';
+ }
+
+ if (type == S_IFLNK) {
+ int j;
+
+ ack();
+ cp = buf;
+ for (i = 0; i < size; i += j) {
+ if ((j = read(rem, cp, size - i)) <= 0)
+ cleanup();
+ cp += j;
+ }
+ *cp = '\0';
+ if (response() < 0) {
+ err();
+ return;
+ }
+ if (symlink(buf, new) < 0) {
+ if (errno != ENOENT || chkparent(new) < 0 ||
+ symlink(buf, new) < 0)
+ goto badn;
+ }
+ mode &= 0777;
+ if (opts & COMPARE) {
+ char tbuf[MAXPATHLEN];
+
+ if ((i = readlink(target, tbuf, MAXPATHLEN)) >= 0 &&
+ i == size && strncmp(buf, tbuf, size) == 0) {
+ (void) unlink(new);
+ ack();
+ return;
+ }
+ if (opts & VERIFY)
+ goto differ;
+ }
+ goto fixup;
+ }
+
+ if ((f = creat(new, mode & ~06000)) < 0) {
+ if (errno != ENOENT || chkparent(new) < 0 ||
+ (f = creat(new, mode & ~06000)) < 0)
+ goto badn;
+ }
+
+ ack();
+ wrerr = 0;
+ for (i = 0; i < size; i += RDIST_BUFSIZ) {
+ int amt = RDIST_BUFSIZ;
+
+ cp = buf;
+ if (i + amt > size)
+ amt = size - i;
+ do {
+ int j = read(rem, cp, amt);
+ if (j <= 0) {
+ (void) close(f);
+ (void) unlink(new);
+ cleanup();
+ }
+ amt -= j;
+ cp += j;
+ } while (amt > 0);
+ amt = RDIST_BUFSIZ;
+ if (i + amt > size)
+ amt = size - i;
+ if (wrerr == 0 && write(f, buf, amt) != amt) {
+ olderrno = errno;
+ wrerr++;
+ }
+ }
+ (void) close(f);
+
+ if (response() < 0) {
+ err();
+ (void) unlink(new);
+ return;
+ }
+ if (wrerr) {
+ error("%s:%s: %s\n", host, new, strerror(olderrno));
+ (void) unlink(new);
+ return;
+ }
+ if (opts & COMPARE) {
+ FILE *f1, *f2;
+ int c;
+
+ if ((f1 = fopen(target, "r")) == NULL)
+ goto badt;
+ if ((f2 = fopen(new, "r")) == NULL) {
+ badn:
+ error("%s:%s: %s\n", host, new, strerror(errno));
+ (void) unlink(new);
+ return;
+ }
+ while ((c = getc(f1)) == getc(f2))
+ if (c == EOF) {
+ (void) fclose(f1);
+ (void) fclose(f2);
+ (void) unlink(new);
+ ack();
+ return;
+ }
+ (void) fclose(f1);
+ (void) fclose(f2);
+ if (opts & VERIFY) {
+ differ:
+ (void) unlink(new);
+ sendrem("need to update: %s", target);
+ return;
+ }
+ }
+
+ /*
+ * Set last modified time. For type == S_IFDIR, the lstat above filled
+ * in stb. Otherwise, do it now.
+ */
+ if (type != S_IFDIR)
+ (void) lstat(new, &stb);
+ tvp[0].tv_sec = stb.st_atime; /* old atime from target */
+ tvp[0].tv_usec = 0;
+ tvp[1].tv_sec = mtime;
+ tvp[1].tv_usec = 0;
+ if (utimes(new, tvp) < 0) {
+ note("%s:utimes failed %s: %s", host, new, strerror(errno));
+ }
+ if (chog(new, owner, group, mode) < 0) {
+ (void) unlink(new);
+ return;
+ }
+fixup:
+ if (rename(new, target) < 0) {
+badt:
+ error("%s:%s: %s\n", host, target, strerror(errno));
+ (void) unlink(new);
+ return;
+ }
+ if (opts & COMPARE) {
+ sendrem("updated %s", target);
+ } else
+ ack();
+}
+
+/*
+ * Creat a hard link to existing file.
+ */
+hardlink(cmd)
+ char *cmd;
+{
+ register char *cp;
+ struct stat stb;
+ char *oldname;
+ int opts, exists = 0;
+ char oldnamebuf[RDIST_BUFSIZ];
+
+ cp = cmd;
+ opts = 0;
+ while (*cp >= '0' && *cp <= '7')
+ opts = (opts << 3) | (*cp++ - '0');
+ if (*cp++ != ' ') {
+ error("hardlink: options not delimited\n");
+ return;
+ }
+ oldname = cp;
+ while (*cp && *cp != ' ')
+ cp++;
+ if (*cp != ' ') {
+ error("hardlink: oldname name not delimited\n");
+ return;
+ }
+ *cp++ = '\0';
+
+ if (catname) {
+ if (sizeof (target) - (tp - target) >= strlen(cp) + 2) {
+ (void) sprintf(tp, "/%s", cp);
+ } else {
+ error("%.*s/%s: Name too long\n", tp - target,
+ target, cp);
+ return;
+ }
+ }
+ if (lstat(target, &stb) == 0) {
+ int mode = stb.st_mode & S_IFMT;
+ if (mode != S_IFREG && mode != S_IFLNK) {
+ error("%s:%s: not a regular file\n", host, target);
+ return;
+ }
+ exists = 1;
+ }
+ if (chkparent(target) < 0) {
+ error("%s:%s: %s (no parent)\n",
+ host, target, strerror(errno));
+ return;
+ }
+ if (opts & VERIFY) {
+ struct stat nstb;
+
+ if (exists && lstat(oldname, &nstb) == 0 &&
+ nstb.st_mode == stb.st_mode &&
+ nstb.st_ino == stb.st_ino &&
+ nstb.st_dev == stb.st_dev) {
+ ack();
+ return;
+ } else {
+ sendrem("need to update: %s", target);
+ return;
+ }
+ }
+ if (exists && (unlink(target) < 0)) {
+ error("%s:%s: %s (unlink)\n",
+ host, target, strerror(errno));
+ return;
+ }
+ if (*oldname == '~')
+ oldname = exptilde(oldnamebuf, sizeof (oldnamebuf), oldname);
+ if (link(oldname, target) < 0) {
+ error("%s:can't link %s to %s\n",
+ host, target, oldname);
+ return;
+ }
+ ack();
+}
+
+/*
+ * Check to see if parent directory exists and create one if not.
+ */
+chkparent(name)
+ char *name;
+{
+ register char *cp;
+ struct stat stb;
+
+ cp = rindex(name, '/');
+ if (cp == NULL || cp == name)
+ return (0);
+ *cp = '\0';
+ if (lstat(name, &stb) < 0) {
+ if (errno == ENOENT && chkparent(name) >= 0 &&
+ mkdir(name, 0777 & ~oumask) >= 0) {
+ *cp = '/';
+ return (0);
+ }
+ } else if (ISDIR(stb.st_mode)) {
+ *cp = '/';
+ return (0);
+ }
+ *cp = '/';
+ return (-1);
+}
+
+/*
+ * Change owner, group and mode of file.
+ */
+chog(file, owner, group, mode)
+ char *file, *owner, *group;
+ int mode;
+{
+ register int i;
+ uid_t uid, gid;
+ extern char user[];
+
+ /*
+ * by default, set uid of file to the uid of the person running
+ * this program.
+ */
+ uid = getuid();
+
+ /*
+ * We'll use available privileges so we just try to do what
+ * the client specifies. If the chown() fails we'll not
+ * add the set-[ug]id bits; and if we want to add the set-[ug]id
+ * bits and we're not permitted to do so, the OS will prevent us
+ * from doing so.
+ */
+ if (*owner == ':') {
+ uid = atoi(owner + 1);
+ } else if (pw == NULL || strcmp(owner, pw->pw_name) != 0) {
+ if ((pw = getpwnam(owner)) == NULL) {
+ if (mode & 04000) {
+ note("%s:%s: unknown login name, "
+ "clearing setuid", host, owner);
+ mode &= ~04000;
+ }
+ } else {
+ uid = pw->pw_uid;
+ }
+ } else {
+ uid = pw->pw_uid;
+ }
+
+ if (*group == ':') {
+ gid = atoi(group + 1);
+ goto ok;
+ }
+
+ gid = -1;
+ if (gr == NULL || strcmp(group, gr->gr_name) != 0) {
+ if ((*group == ':' &&
+ (getgrgid(gid = atoi(group + 1)) == NULL)) ||
+ ((gr = getgrnam(group)) == NULL)) {
+ if (mode & 02000) {
+ note("%s:%s: unknown group", host, group);
+ mode &= ~02000;
+ }
+ } else
+ gid = gr->gr_gid;
+ } else
+ gid = gr->gr_gid;
+ok:
+ if (chown(file, uid, gid) < 0 ||
+ (mode & 07000) && chmod(file, mode) < 0) {
+ note("%s: chown or chmod failed: file %s: %s",
+ host, file, strerror(errno));
+ }
+ return (0);
+}
+
+/*
+ * Check for files on the machine being updated that are not on the master
+ * machine and remove them.
+ */
+rmchk(opts)
+ int opts;
+{
+ register char *cp, *s;
+ struct stat stb;
+
+ if (debug)
+ printf("rmchk()\n");
+
+ /*
+ * Tell the remote to clean the files from the last directory sent.
+ */
+ (void) sprintf(buf, "C%o\n", opts & VERIFY);
+ if (debug)
+ printf("buf = %s", buf);
+ (void) deswrite(rem, buf, strlen(buf), 0);
+ if (response() < 0)
+ return;
+ for (;;) {
+ cp = s = buf;
+ do {
+ if (desread(rem, cp, 1, 0) != 1)
+ lostconn();
+ } while (*cp++ != '\n' && cp < &buf[RDIST_BUFSIZ]);
+
+ switch (*s++) {
+ case 'Q': /* Query if file should be removed */
+ /*
+ * Return the following codes to remove query.
+ * N\n -- file exists - DON'T remove.
+ * Y\n -- file doesn't exist - REMOVE.
+ */
+ *--cp = '\0';
+ (void) sprintf(tp, "/%s", s);
+ if (debug)
+ printf("check %s\n", target);
+ if (except(target))
+ (void) deswrite(rem, "N\n", 2, 0);
+ else if (lstat(target, &stb) < 0)
+ (void) deswrite(rem, "Y\n", 2, 0);
+ else
+ (void) deswrite(rem, "N\n", 2, 0);
+ break;
+
+ case '\0':
+ *--cp = '\0';
+ if (*s != '\0')
+ log(lfp, "%s\n", s);
+ break;
+
+ case 'E':
+ *tp = '\0';
+ (void) deswrite(rem, "\0\n", 2, 0);
+ return;
+
+ case '\1':
+ case '\2':
+ nerrs++;
+ if (*s != '\n') {
+ if (!iamremote) {
+ fflush(stdout);
+ (void) write(2, s, cp - s);
+ }
+ if (lfp != NULL)
+ (void) fwrite(s, 1, cp - s, lfp);
+ }
+ if (buf[0] == '\2')
+ lostconn();
+ break;
+
+ default:
+ error("rmchk: unexpected response '%s'\n", buf);
+ (void) deswrite(rem, "\1\n", 2, 0);
+ }
+ }
+}
+
+/*
+ * Check the current directory (initialized by the 'T' command to server())
+ * for extraneous files and remove them.
+ */
+clean(cp)
+ register char *cp;
+{
+ DIR *d;
+ register struct dirent *dp;
+ struct stat stb;
+ char *otp;
+ int len, opts;
+
+ opts = 0;
+ while (*cp >= '0' && *cp <= '7')
+ opts = (opts << 3) | (*cp++ - '0');
+ if (*cp != '\0') {
+ error("clean: options not delimited\n");
+ return;
+ }
+ if ((d = opendir(target)) == NULL) {
+ error("%s:%s: %s\n", host, target, strerror(errno));
+ return;
+ }
+ ack();
+
+ otp = tp;
+ len = tp - target;
+ while (dp = readdir(d)) {
+ if ((strcmp(dp->d_name, ".") == 0) ||
+ (strcmp(dp->d_name, "..") == 0))
+ continue;
+ if ((int)(len + 1 + strlen(dp->d_name)) >=
+ (int)(RDIST_BUFSIZ - 1)) {
+ error("%s:%s/%s: Name too long\n",
+ host, target, dp->d_name);
+ continue;
+ }
+ tp = otp;
+ *tp++ = '/';
+ cp = dp->d_name;
+ while (*tp++ = *cp++)
+ ;
+ tp--;
+ if (lstat(target, &stb) < 0) {
+ error("%s:%s: %s\n", host, target, strerror(errno));
+ continue;
+ }
+ (void) snprintf(buf, sizeof (buf), "Q%s\n", dp->d_name);
+ (void) write(wrem, buf, strlen(buf));
+ cp = buf;
+ do {
+ if (read(rem, cp, 1) != 1)
+ cleanup();
+ } while (*cp++ != '\n' && cp < &buf[RDIST_BUFSIZ]);
+ *--cp = '\0';
+ cp = buf;
+ if (*cp != 'Y')
+ continue;
+ if (opts & VERIFY) {
+ sendrem("need to remove: %s", target);
+ } else
+ (void) recursive_remove(&stb);
+ }
+ closedir(d);
+ (void) write(wrem, "E\n", 2);
+ (void) response();
+ tp = otp;
+ *tp = '\0';
+}
+
+/*
+ * Remove a file or directory (recursively) and send back an acknowledge
+ * or an error message.
+ */
+static
+recursive_remove(stp)
+ struct stat *stp;
+{
+ DIR *d;
+ struct dirent *dp;
+ register char *cp;
+ struct stat stb;
+ char *otp;
+ int len;
+
+ switch (stp->st_mode & S_IFMT) {
+ case S_IFREG:
+ case S_IFLNK:
+ if (unlink(target) < 0)
+ goto bad;
+ goto removed;
+
+ case S_IFDIR:
+ break;
+
+ default:
+ error("%s:%s: not a plain file\n", host, target);
+ return;
+ }
+
+ if ((d = opendir(target)) == NULL)
+ goto bad;
+
+ otp = tp;
+ len = tp - target;
+ while (dp = readdir(d)) {
+ if ((strcmp(dp->d_name, ".") == 0) ||
+ (strcmp(dp->d_name, "..") == 0))
+ continue;
+ if ((int)(len + 1 + strlen(dp->d_name)) >=
+ (int)(RDIST_BUFSIZ - 1)) {
+ error("%s:%s/%s: Name too long\n",
+ host, target, dp->d_name);
+ continue;
+ }
+ tp = otp;
+ *tp++ = '/';
+ cp = dp->d_name;
+ while (*tp++ = *cp++)
+ ;
+ tp--;
+ if (lstat(target, &stb) < 0) {
+ error("%s:%s: %s\n", host, target, strerror(errno));
+ continue;
+ }
+ recursive_remove(&stb);
+ }
+ closedir(d);
+ tp = otp;
+ *tp = '\0';
+ if (rmdir(target) < 0) {
+bad:
+ error("%s:%s: %s\n", host, target, strerror(errno));
+ return;
+ }
+removed:
+ sendrem("removed %s", target);
+}
+
+/*
+ * Execute a shell command to handle special cases.
+ */
+dospecial(cmd)
+ char *cmd;
+{
+ int fd[2], status, pid, i;
+ register char *cp, *s;
+ char sbuf[RDIST_BUFSIZ];
+
+ if (pipe(fd) < 0) {
+ error("%s\n", strerror(errno));
+ return;
+ }
+ if ((pid = fork()) == 0) {
+ /*
+ * Return everything the shell commands print.
+ */
+ (void) close(0);
+ (void) close(1);
+ (void) close(2);
+ (void) open("/dev/null", 0);
+ (void) dup(fd[1]);
+ (void) dup(fd[1]);
+ (void) close(fd[0]);
+ (void) close(fd[1]);
+ execl("/bin/sh", "sh", "-c", cmd, 0);
+ _exit(127);
+ }
+ (void) close(fd[1]);
+ s = sbuf;
+ *s++ = '\0';
+ while ((i = read(fd[0], buf, RDIST_BUFSIZ)) > 0) {
+ cp = buf;
+ do {
+ *s++ = *cp++;
+ if (cp[-1] != '\n') {
+ if (s < &sbuf[RDIST_BUFSIZ - 1])
+ continue;
+ *s++ = '\n';
+ }
+ /*
+ * Throw away blank lines.
+ */
+ if (s == &sbuf[2]) {
+ s--;
+ continue;
+ }
+ (void) write(wrem, sbuf, s - sbuf);
+ s = &sbuf[1];
+ } while (--i);
+ }
+ if (s > &sbuf[1]) {
+ *s++ = '\n';
+ (void) write(wrem, sbuf, s - sbuf);
+ }
+ while ((i = wait(&status)) != pid && i != -1)
+ ;
+ if (i == -1)
+ status = -1;
+ (void) close(fd[0]);
+ if (status)
+ error("shell returned %d\n", status);
+ else
+ ack();
+}
+
+/*VARARGS2*/
+log(fp, fmt, a1, a2, a3)
+ FILE *fp;
+ char *fmt;
+ int a1, a2, a3;
+{
+ /* Print changes locally if not quiet mode */
+ if (!qflag)
+ printf(fmt, a1, a2, a3);
+
+ /* Save changes (for mailing) if really updating files */
+ if (!(options & VERIFY) && fp != NULL)
+ fprintf(fp, fmt, a1, a2, a3);
+}
+
+/*VARARGS1*/
+error(fmt, a1, a2, a3)
+ char *fmt;
+ int a1, a2, a3;
+{
+ static FILE *fp;
+
+ nerrs++;
+ if (!fp && !(fp = fdopen(rem, "w")))
+ return;
+ if (iamremote) {
+ (void) fprintf(fp, "%crdist: ", 0x01);
+ (void) fprintf(fp, fmt, a1, a2, a3);
+ fflush(fp);
+ } else {
+ fflush(stdout);
+ (void) fprintf(stderr, "rdist: ");
+ (void) fprintf(stderr, fmt, a1, a2, a3);
+ fflush(stderr);
+ }
+ if (lfp != NULL) {
+ (void) fprintf(lfp, "rdist: ");
+ (void) fprintf(lfp, fmt, a1, a2, a3);
+ fflush(lfp);
+ }
+}
+
+/*VARARGS1*/
+fatal(fmt, a1, a2, a3)
+ char *fmt;
+ int a1, a2, a3;
+{
+ static FILE *fp;
+
+ nerrs++;
+ if (!fp && !(fp = fdopen(rem, "w")))
+ return;
+ if (iamremote) {
+ (void) fprintf(fp, "%crdist: ", 0x02);
+ (void) fprintf(fp, fmt, a1, a2, a3);
+ fflush(fp);
+ } else {
+ fflush(stdout);
+ (void) fprintf(stderr, "rdist: ");
+ (void) fprintf(stderr, fmt, a1, a2, a3);
+ fflush(stderr);
+ }
+ if (lfp != NULL) {
+ (void) fprintf(lfp, "rdist: ");
+ (void) fprintf(lfp, fmt, a1, a2, a3);
+ fflush(lfp);
+ }
+ cleanup();
+}
+
+response()
+{
+ char *cp, *s;
+ char resp[RDIST_BUFSIZ];
+
+ if (debug)
+ printf("response()\n");
+
+ cp = s = resp;
+more:
+ do {
+ if (desread(rem, cp, 1, 0) != 1)
+ lostconn();
+ } while (*cp++ != '\n' && cp < &resp[RDIST_BUFSIZ]);
+
+ switch (*s++) {
+ case '\0':
+ *--cp = '\0';
+ if (*s != '\0') {
+ log(lfp, "%s\n", s);
+ return (1);
+ }
+ return (0);
+ case '\3':
+ *--cp = '\0';
+ log(lfp, "Note: %s\n", s);
+ return (response());
+
+ default:
+ s--;
+ /* fall into... */
+ case '\1':
+ case '\2':
+ nerrs++;
+ if (*s != '\n') {
+ if (!iamremote) {
+ fflush(stdout);
+ (void) write(2, s, cp - s);
+ }
+ if (lfp != NULL)
+ (void) fwrite(s, 1, cp - s, lfp);
+ }
+ if (cp == &resp[RDIST_BUFSIZ] && *(cp - 1) != '\n') {
+ /* preserve status code */
+ cp = s;
+ s = resp;
+ goto more;
+ }
+ if (resp[0] == '\2')
+ lostconn();
+ return (-1);
+ }
+}
+
+/*
+ * Remove temporary files and do any cleanup operations before exiting.
+ */
+void
+cleanup()
+{
+ (void) unlink(Tmpfile);
+ exit(1);
+}
+
+note(fmt, a1, a2, a3)
+char *fmt;
+int a1, a2, a3;
+{
+ static char buf[RDIST_BUFSIZ];
+ (void) snprintf(buf, sizeof (buf) - 1, fmt, a1, a2, a3);
+ comment(buf);
+}
+
+comment(s)
+char *s;
+{
+ char three = '\3';
+ char nl = '\n';
+ struct iovec iov[3];
+
+ iov[0].iov_base = &three;
+ iov[0].iov_len = sizeof (char);
+ iov[1].iov_base = s;
+ iov[1].iov_len = strlen(s);
+ iov[2].iov_base = &nl;
+ iov[2].iov_len = sizeof (char);
+ (void) writev(rem, iov, 3);
+}
+
+/*
+ * Send message to other end.
+ * N.B.: uses buf[].
+ */
+void
+sendrem(fmt, a1, a2, a3)
+char *fmt;
+int a1, a2, a3;
+{
+ register int len;
+
+ buf[0] = '\0';
+ len = snprintf(buf + 1, sizeof (buf) - 1, fmt, a1, a2, a3) + 2;
+ if (len > sizeof (buf))
+ len = sizeof (buf);
+ buf[len - 1] = '\n';
+ (void) write(wrem, buf, len);
+}
+
+/*
+ * strsub(old, new, s)
+ *
+ * Return a pointer to a new string created by replacing substring old
+ * with substring new in string s. String s is assumed to begin with
+ * substring old.
+ */
+char *
+strsub(old, new, s)
+ char *old, *new, *s;
+{
+ static char pbuf[PATH_MAX];
+ register char *p, *q, *r, *plim;
+
+ /* prepend new to pbuf */
+ for (p = pbuf, q = new, plim = pbuf + sizeof (pbuf) - 1;
+ /* CSTYLED */
+ *q && (p < plim);)
+ *p++ = *q++;
+ /* p now points to the byte in pbuf where more copying should begin */
+
+ /* skip over the part of s which begins with old */
+ for (r = old, q = s; *r; q++, r++)
+ ;
+ /* q now points to the byte in s where more copying should begin */
+
+ while (*q && (p < plim))
+ *p++ = *q++;
+ *p = '\0';
+
+ return (pbuf);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/req.flg b/usr/src/cmd/cmd-inet/usr.bin/req.flg
new file mode 100644
index 0000000000..fde232588e
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/req.flg
@@ -0,0 +1,30 @@
+#!/bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+echo_file usr/src/lib/gss_mechs/mech_krb5/Makefile.mech_krb5
diff --git a/usr/src/cmd/cmd-inet/usr.bin/rlogin.c b/usr/src/cmd/cmd-inet/usr.bin/rlogin.c
new file mode 100644
index 0000000000..3c4273f4c4
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/rlogin.c
@@ -0,0 +1,1383 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * rlogin - remote login
+ */
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/errno.h>
+#include <sys/file.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/stropts.h>
+#include <sys/ttold.h>
+#include <sys/sockio.h>
+#include <sys/tty.h>
+#include <sys/ptyvar.h>
+#include <sys/resource.h>
+#include <sys/select.h>
+#include <sys/time.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <priv_utils.h>
+
+#include <stdio.h>
+#include <errno.h>
+#include <pwd.h>
+#include <signal.h>
+#include <setjmp.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <locale.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <k5-int.h>
+#include <profile/prof_int.h>
+#include <com_err.h>
+#include <kcmd.h>
+#include <krb5.h>
+
+/* signal disposition - signal handler or SIG_IGN, SIG_ERR, etc. */
+typedef void (*sigdisp_t)(int);
+
+extern errcode_t profile_get_options_boolean(profile_t, char **,
+ profile_options_boolean *);
+extern errcode_t profile_get_options_string(profile_t, char **,
+ profile_option_strings *);
+
+#define RLOGIN_BUFSIZ (1024 * 50)
+static char des_inbuf[2 * RLOGIN_BUFSIZ];
+ /* needs to be > largest read size */
+static char des_outbuf[2 * RLOGIN_BUFSIZ];
+ /* needs to be > largest write size */
+static krb5_data desinbuf, desoutbuf;
+static krb5_encrypt_block eblock; /* eblock for encrypt/decrypt */
+static krb5_keyblock *session_key;
+static krb5_creds *cred;
+static krb5_context bsd_context;
+static krb5_auth_context auth_context;
+
+static char *krb_realm;
+
+static int krb5auth_flag; /* Flag set, when KERBEROS is enabled */
+static int fflag, Fflag; /* Flag set, when option -f / -F used */
+static int encrypt_flag; /* Flag set, when the "-x" option is used */
+
+/* Flag set, if -PN / -PO is specified */
+static boolean_t rcmdoption_done;
+
+/* Flags set, if corres. cmd line options are turned on */
+static boolean_t encrypt_done, fwd_done, fwdable_done;
+
+static profile_options_boolean option[] = {
+ { "encrypt", &encrypt_flag, 0 },
+ { "forward", &fflag, 0 },
+ { "forwardable", &Fflag, 0 },
+ { NULL, NULL, 0 }
+};
+
+static char *rcmdproto;
+static profile_option_strings rcmdversion[] = {
+ { "rcmd_protocol", &rcmdproto, 0 },
+ { NULL, NULL, 0 }
+};
+
+static char rlogin[] = "rlogin";
+
+static char *realmdef[] = { "realms", NULL, rlogin, NULL };
+static char *appdef[] = { "appdefaults", rlogin, NULL };
+
+#ifndef TIOCPKT_WINDOW
+#define TIOCPKT_WINDOW 0x80
+#endif /* TIOCPKT_WINDOW */
+
+#ifndef sigmask
+#define sigmask(m) (1 << ((m)-1))
+#endif
+
+#define set2mask(setp) ((setp)->__sigbits[0])
+#define mask2set(mask, setp) \
+ ((mask) == -1 ? sigfillset(setp) : (((setp)->__sigbits[0]) = (mask)))
+
+#ifdef DEBUG
+#define DEBUGOPTSTRING "D:"
+#else
+#define DEBUGOPTSTRING ""
+#endif /* DEBUG */
+
+static boolean_t ttcompat;
+static struct termios savetty;
+
+static char *errmsg(int);
+static char *host;
+static int port_number;
+static int rem = -1;
+static char cmdchar = '~';
+static boolean_t nocmdchar;
+static boolean_t eight;
+static boolean_t litout;
+static boolean_t null_local_username;
+/*
+ * Note that this list of speeds is shorter than the list of speeds
+ * supported by termios. This is because we can't be sure other rlogind's
+ * in the world will correctly cope with values other than what 4.2/4.3BSD
+ * supported.
+ */
+static char *speeds[] =
+ { "0", "50", "75", "110", "134", "150", "200", "300",
+ "600", "1200", "1800", "2400", "4800", "9600", "19200",
+ "38400" };
+static char term[256] = "network";
+static void lostpeer(void);
+static boolean_t dosigwinch;
+static struct winsize winsize;
+static void sigwinch(int);
+static void oob(void);
+static void doit(int);
+static sigdisp_t sigdisp(int);
+
+#define CRLF "\r\n"
+
+static pid_t child;
+static void catchild(int);
+/* LINTED */
+static void copytochild(int);
+static void writeroob(int);
+static void stop(char), echo(char);
+
+static int defflags, tabflag;
+static int deflflags;
+static char deferase, defkill;
+static struct tchars deftc;
+static struct ltchars defltc;
+static struct tchars notc = { (char)-1, (char)-1, (char)-1,
+ (char)-1, (char)-1, (char)-1 };
+static struct ltchars noltc = { (char)-1, (char)-1, (char)-1,
+ (char)-1, (char)-1, (char)-1 };
+
+static void done(int);
+static void mode(int);
+static int reader(int);
+static void writer(void);
+static void prf(const char *, ...);
+static void sendwindow(void);
+static int compat_ioctl(int, int, void *);
+
+static void
+sigsetmask(int mask)
+{
+ sigset_t oset;
+ sigset_t nset;
+
+ (void) sigprocmask(0, NULL, &nset);
+ mask2set(mask, &nset);
+ (void) sigprocmask(SIG_SETMASK, &nset, &oset);
+}
+
+static int
+sigblock(int mask)
+{
+ sigset_t oset;
+ sigset_t nset;
+
+ (void) sigprocmask(0, NULL, &nset);
+ mask2set(mask, &nset);
+ (void) sigprocmask(SIG_BLOCK, &nset, &oset);
+ return (set2mask(&oset));
+}
+
+static void
+pop(int status) {
+ if (ttcompat) {
+ /*
+ * Pop ttcompat module
+ */
+ (void) ioctl(STDIN_FILENO, I_POP, 0);
+ }
+ (void) tcsetattr(STDIN_FILENO, TCSANOW, &savetty);
+ exit(status);
+}
+
+static void
+usage(void) {
+ (void) fprintf(stderr, "%s\n%s\n",
+ gettext("usage: rlogin [-option] [-option...] "
+ "[-k realm] [-l username] host"),
+ gettext(" where option is e, 8, E, L, A, a, x, "
+ "PN / PO, f or F"));
+ pop(EXIT_FAILURE);
+}
+
+/* PRINTFLIKE(0) */
+static void
+die(const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ (void) vfprintf(stderr, format, ap);
+ va_end(ap);
+ usage();
+}
+
+static void
+usage_forward(void)
+{
+ die(gettext("rlogin: Only one of -f and -F allowed.\n"));
+}
+
+int
+main(int argc, char **argv)
+{
+ int c;
+ char *cp, *cmd, *name = NULL;
+ struct passwd *pwd;
+ uid_t uid;
+ int options = 0, oldmask;
+ int on = 1;
+ speed_t speed = 0;
+ int getattr_ret;
+ char *tmp;
+ int sock;
+ krb5_flags authopts;
+ krb5_error_code status;
+ enum kcmd_proto kcmd_proto = KCMD_NEW_PROTOCOL;
+
+ (void) setlocale(LC_ALL, "");
+
+#if !defined(TEXT_DOMAIN)
+#define TEXT_DOMAIN "SYS_TEST"
+#endif
+ (void) textdomain(TEXT_DOMAIN);
+
+ if (__init_suid_priv(0, PRIV_NET_PRIVADDR, NULL) == -1) {
+ (void) fprintf(stderr,
+ gettext("Insufficient privileges, "
+ "rlogin must be set-uid root\n"));
+ exit(1);
+ }
+
+ {
+ int it;
+
+ if ((getattr_ret = tcgetattr(STDIN_FILENO, &savetty)) < 0)
+ perror("tcgetattr");
+ it = ioctl(STDIN_FILENO, I_FIND, "ttcompat");
+ if (it < 0) {
+ perror("ioctl I_FIND ttcompat");
+ return (EXIT_FAILURE);
+ }
+ if (it == 0) {
+ if (ioctl(STDIN_FILENO, I_PUSH, "ttcompat") < 0) {
+ perror("ioctl I_PUSH ttcompat");
+ exit(EXIT_FAILURE);
+ }
+ ttcompat = B_TRUE;
+ }
+ }
+
+ /*
+ * Determine command name used to invoke to rlogin(1). Users can
+ * create links named by a host pointing to the binary and type
+ * "hostname" to log into that host afterwards.
+ */
+ cmd = strrchr(argv[0], '/');
+ cmd = (cmd != NULL) ? (cmd + 1) : argv[0];
+
+ if (strcmp(cmd, rlogin) == 0) {
+ if (argc < 2)
+ usage();
+ if (*argv[1] != '-') {
+ host = argv[1];
+ argc--;
+ argv[1] = argv[0];
+ argv++;
+ }
+ } else {
+ host = cmd;
+ }
+
+ while ((c = getopt(argc, argv,
+ DEBUGOPTSTRING "8AEFLP:ade:fk:l:x")) != -1) {
+ switch (c) {
+ case '8':
+ eight = B_TRUE;
+ break;
+ case 'A':
+ krb5auth_flag = B_TRUE;
+ break;
+#ifdef DEBUG
+ case 'D':
+ portnumber = htons(atoi(optarg));
+ krb5auth_flag = B_TRUE;
+ break;
+#endif /* DEBUG */
+ case 'E':
+ nocmdchar = B_TRUE;
+ break;
+ case 'F':
+ if (fflag)
+ usage_forward();
+ Fflag = 1;
+ krb5auth_flag = B_TRUE;
+ fwdable_done = B_TRUE;
+ break;
+ case 'f':
+ if (Fflag)
+ usage_forward();
+ fflag = 1;
+ krb5auth_flag = B_TRUE;
+ fwd_done = B_TRUE;
+ break;
+ case 'L':
+ litout = B_TRUE;
+ break;
+ case 'P':
+ if (strcmp(optarg, "N") == 0)
+ kcmd_proto = KCMD_NEW_PROTOCOL;
+ else if (strcmp(optarg, "O") == 0)
+ kcmd_proto = KCMD_OLD_PROTOCOL;
+ else
+ die(gettext("rlogin: Only -PN or -PO "
+ "allowed.\n"));
+ if (rcmdoption_done)
+ die(gettext("rlogin: Only one of -PN and -PO "
+ "allowed.\n"));
+ rcmdoption_done = B_TRUE;
+ krb5auth_flag = B_TRUE;
+ break;
+ case 'a':
+ /*
+ * Force the remote host to prompt for a password by sending
+ * a NULL username. This option is mutually exclusive with
+ * the -A, -x, -f, -F, -k <realm> options.
+ */
+ null_local_username = B_TRUE;
+ break;
+ case 'd':
+ options |= SO_DEBUG;
+ break;
+ case 'e': {
+ int c;
+
+ cp = optarg;
+
+ if ((c = *cp) != '\\') {
+ cmdchar = c;
+ } else {
+ c = cp[1];
+ if (c == '\0' || c == '\\') {
+ cmdchar = '\\';
+ } else if (c >= '0' && c <= '7') {
+ long lc;
+
+ lc = strtol(&cp[1], NULL, 8);
+ if (lc < 0 || lc > 255)
+ die(gettext("rlogin: octal "
+ "escape character %s too "
+ "large.\n"), cp);
+ cmdchar = (char)lc;
+ } else {
+ die(gettext("rlogin: unrecognized "
+ "escape character option %s.\n"),
+ cp);
+ }
+ }
+ break;
+ }
+ case 'k':
+ krb_realm = optarg;
+ krb5auth_flag = B_TRUE;
+ break;
+ case 'l':
+ name = optarg;
+ break;
+ case 'x':
+ encrypt_flag = 1;
+ krb5auth_flag = B_TRUE;
+ encrypt_done = B_TRUE;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (host == NULL) {
+ if (argc == 0)
+ usage();
+ argc--;
+ host = *argv++;
+ }
+
+ if (argc > 0)
+ usage();
+
+ pwd = getpwuid(uid = getuid());
+ if (pwd == NULL) {
+ (void) fprintf(stderr, gettext("getpwuid(): can not find "
+ "password entry for user id %d."), uid);
+ return (EXIT_FAILURE);
+ }
+ if (name == NULL)
+ name = pwd->pw_name;
+
+ /*
+ * If the `-a' option is issued on the cmd line, we reset all
+ * flags associated with other KRB5 specific options, since
+ * the -a option is mutually exclusive with the rest.
+ */
+ if (null_local_username) {
+ krb5auth_flag = B_FALSE;
+ fflag = Fflag = encrypt_flag = 0;
+ (void) fprintf(stderr, gettext("Note: The -a option nullifies "
+ "all other Kerberos-specific\noptions "
+ "you may have used.\n"));
+ }
+
+ if (krb5auth_flag) {
+ status = krb5_init_context(&bsd_context);
+ if (status) {
+ com_err(rlogin, status, gettext("while initializing"
+ " krb5"));
+ return (EXIT_FAILURE);
+ }
+ /*
+ * Set up buffers for desread and deswrite.
+ */
+ desinbuf.data = des_inbuf;
+ desoutbuf.data = des_outbuf;
+ desinbuf.length = sizeof (des_inbuf);
+ desoutbuf.length = sizeof (des_outbuf);
+
+ /*
+ * Get our local realm to look up local realm options.
+ */
+ status = krb5_get_default_realm(bsd_context, &realmdef[1]);
+ if (status) {
+ com_err(rlogin, status,
+ gettext("while getting default realm"));
+ return (EXIT_FAILURE);
+ }
+ /*
+ * Check the realms section in krb5.conf for encryption,
+ * forward & forwardable info
+ */
+ profile_get_options_boolean(bsd_context->profile, realmdef,
+ option);
+ /*
+ * Check the appdefaults section
+ */
+ profile_get_options_boolean(bsd_context->profile, appdef,
+ option);
+ profile_get_options_string(bsd_context->profile, appdef,
+ rcmdversion);
+
+ /*
+ * Set the *_flag variables, if the corresponding *_done are
+ * set to 1, because we dont want the config file values
+ * overriding the command line options.
+ */
+ if (encrypt_done)
+ encrypt_flag = 1;
+ if (fwd_done) {
+ fflag = 1;
+ Fflag = 0;
+ } else if (fwdable_done) {
+ Fflag = 1;
+ fflag = 0;
+ }
+ if (!rcmdoption_done && (rcmdproto != NULL)) {
+ if (strncmp(rcmdproto, "rcmdv2", 6) == 0) {
+ kcmd_proto = KCMD_NEW_PROTOCOL;
+ } else if (strncmp(rcmdproto, "rcmdv1", 6) == 0) {
+ kcmd_proto = KCMD_OLD_PROTOCOL;
+ } else {
+ (void) fprintf(stderr, gettext("Unrecognized "
+ "KCMD protocol (%s)"), rcmdproto);
+ return (EXIT_FAILURE);
+ }
+ }
+
+ if (encrypt_flag && (!krb5_privacy_allowed())) {
+ (void) fprintf(stderr, gettext("rlogin: "
+ "Encryption not supported.\n"));
+ return (EXIT_FAILURE);
+ }
+ }
+
+ if (port_number == 0) {
+ if (krb5auth_flag) {
+ struct servent *sp;
+
+ /*
+ * If the krb5auth_flag is set (via -A, -f, -F, -k) &
+ * if there is an entry in /etc/services for Kerberos
+ * login, attempt to login with Kerberos. If we fail
+ * at any step, use the standard rlogin
+ */
+ sp = getservbyname(encrypt_flag ?
+ "eklogin" : "klogin", "tcp");
+ if (sp == NULL) {
+ port_number = encrypt_flag ?
+ htons(2105) : htons(543);
+ } else {
+ port_number = sp->s_port;
+ }
+ } else {
+ port_number = htons(IPPORT_LOGINSERVER);
+ }
+ }
+
+ cp = getenv("TERM");
+ if (cp) {
+ (void) strncpy(term, cp, sizeof (term));
+ term[sizeof (term) - 1] = '\0';
+ }
+ if (getattr_ret == 0) {
+ speed = cfgetospeed(&savetty);
+ /*
+ * "Be conservative in what we send" -- Only send baud rates
+ * which at least all 4.x BSD derivatives are known to handle
+ * correctly.
+ * NOTE: This code assumes new termios speed values will
+ * be "higher" speeds.
+ */
+ if (speed > B38400)
+ speed = B38400;
+ }
+
+ /*
+ * Only put the terminal speed info in if we have room
+ * so we don't overflow the buffer, and only if we have
+ * a speed we recognize.
+ */
+ if (speed > 0 && speed < sizeof (speeds)/sizeof (char *) &&
+ strlen(term) + strlen("/") + strlen(speeds[speed]) + 1 <
+ sizeof (term)) {
+ (void) strcat(term, "/");
+ (void) strcat(term, speeds[speed]);
+ }
+ (void) sigset(SIGPIPE, (sigdisp_t)lostpeer);
+ /* will use SIGUSR1 for window size hack, so hold it off */
+ oldmask = sigblock(sigmask(SIGURG) | sigmask(SIGUSR1));
+
+ /*
+ * Determine if v4 literal address and if so store it to one
+ * side. This is to correct the undesired behaviour of rcmd_af
+ * which converts a passed in v4 literal address to a v4 mapped
+ * v6 literal address. If it was a v4 literal we then re-assign
+ * it to host.
+ */
+ tmp = NULL;
+ if (inet_addr(host) != (in_addr_t)-1)
+ tmp = host;
+
+ if (krb5auth_flag) {
+ authopts = AP_OPTS_MUTUAL_REQUIRED;
+
+ /* Piggy-back forwarding flags on top of authopts; */
+ /* they will be reset in kcmd */
+ if (fflag || Fflag)
+ authopts |= OPTS_FORWARD_CREDS;
+ if (Fflag)
+ authopts |= OPTS_FORWARDABLE_CREDS;
+
+ status = kcmd(&sock, &host, port_number,
+ null_local_username ? "" : pwd->pw_name,
+ name, term, NULL,
+ "host", krb_realm, bsd_context, &auth_context,
+ &cred,
+ NULL, /* No need for sequence number */
+ NULL, /* No need for server seq # */
+ authopts,
+ 0, /* Not any port # */
+ &kcmd_proto);
+
+ if (status != 0) {
+ /*
+ * If new protocol requested, we dont fallback to
+ * less secure ones.
+ */
+ if (kcmd_proto == KCMD_NEW_PROTOCOL) {
+ (void) fprintf(stderr, gettext("rlogin: kcmdv2 "
+ "to host %s failed - %s\n"
+ "Fallback to normal rlogin denied."),
+ host, error_message(status));
+ return (EXIT_FAILURE);
+ }
+ if (status != -1) {
+ (void) fprintf(stderr, gettext("rlogin: kcmd "
+ "to host %s failed - %s,\n"
+ "trying normal rlogin...\n\n"),
+ host, error_message(status));
+ } else {
+ (void) fprintf(stderr,
+ gettext("trying normal rlogin...\n"));
+ }
+ /*
+ * kcmd() failed, so we have to
+ * fallback to normal rlogin
+ */
+ port_number = htons(IPPORT_LOGINSERVER);
+ krb5auth_flag = B_FALSE;
+ fflag = Fflag = encrypt_flag = 0;
+ null_local_username = B_FALSE;
+ } else {
+ (void) fprintf(stderr,
+ gettext("connected with Kerberos V5\n"));
+
+ /*
+ * Setup eblock for desread and deswrite.
+ */
+ session_key = &cred->keyblock;
+
+ if (kcmd_proto == KCMD_NEW_PROTOCOL) {
+ status = krb5_auth_con_getlocalsubkey(
+ bsd_context,
+ auth_context,
+ &session_key);
+ if (status) {
+ com_err(rlogin, status,
+ "determining subkey for session");
+ return (EXIT_FAILURE);
+ }
+ if (session_key == NULL) {
+ com_err(rlogin, 0,
+ "no subkey negotiated for "
+ "connection");
+ return (EXIT_FAILURE);
+ }
+ }
+
+ eblock.crypto_entry = session_key->enctype;
+ eblock.key = (krb5_keyblock *)session_key;
+
+ init_encrypt(encrypt_flag, bsd_context, kcmd_proto,
+ &desinbuf, &desoutbuf, CLIENT, &eblock);
+
+ rem = sock;
+ if (rem < 0)
+ pop(EXIT_FAILURE);
+ }
+ }
+
+ /*
+ * Don't merge this with the "if" statement above because
+ * "krb5auth_flag" might be set to false inside it.
+ */
+ if (!krb5auth_flag) {
+ rem = rcmd_af(&host, port_number,
+ null_local_username ? "" : pwd->pw_name,
+ name, term, NULL, AF_INET6);
+ if (rem < 0)
+ pop(EXIT_FAILURE);
+ }
+
+ /* Never need our privilege again */
+ __priv_relinquish();
+
+ if (tmp != NULL)
+ host = tmp;
+
+ if (options & SO_DEBUG &&
+ setsockopt(rem, SOL_SOCKET, SO_DEBUG, (char *)&on,
+ sizeof (on)) < 0)
+ perror("rlogin: setsockopt (SO_DEBUG)");
+
+ {
+ int bufsize = 8192;
+
+ (void) setsockopt(rem, SOL_SOCKET, SO_RCVBUF, (char *)&bufsize,
+ sizeof (int));
+ }
+
+ doit(oldmask);
+ /*NOTREACHED*/
+}
+
+static void
+doit(int oldmask)
+{
+ struct sgttyb sb;
+ int atmark;
+
+ if (ioctl(STDIN_FILENO, TIOCGETP, (char *)&sb) == -1)
+ perror("ioctl TIOCGETP");
+ defflags = sb.sg_flags;
+ tabflag = defflags & O_TBDELAY;
+ defflags &= ECHO | O_CRMOD;
+ deferase = sb.sg_erase;
+ defkill = sb.sg_kill;
+ if (ioctl(STDIN_FILENO, TIOCLGET, (char *)&deflflags) == -1)
+ perror("ioctl TIOCLGET");
+ if (ioctl(STDIN_FILENO, TIOCGETC, (char *)&deftc) == -1)
+ perror("ioctl TIOCGETC");
+ notc.t_startc = deftc.t_startc;
+ notc.t_stopc = deftc.t_stopc;
+ if (ioctl(STDIN_FILENO, TIOCGLTC, (char *)&defltc) == -1)
+ perror("ioctl TIOCGLTC");
+ (void) sigset(SIGINT, SIG_IGN);
+ if (sigdisp(SIGHUP) != SIG_IGN)
+ (void) sigset(SIGHUP, exit);
+ if (sigdisp(SIGQUIT) != SIG_IGN)
+ (void) sigset(SIGQUIT, exit);
+ child = fork();
+ if (child == (pid_t)-1) {
+ perror("rlogin: fork");
+ done(EXIT_FAILURE);
+ }
+ if (child == 0) {
+ mode(1);
+ if (reader(oldmask) == 0) {
+ prf(gettext("Connection to %.*s closed."),
+ MAXHOSTNAMELEN, host);
+ exit(EXIT_SUCCESS);
+ }
+ (void) sleep(1);
+ prf(gettext("\aConnection to %.*s closed."),
+ MAXHOSTNAMELEN, host);
+ exit(EXIT_FAILURE);
+ }
+
+ /*
+ * We may still own the socket, and may have a pending SIGURG (or might
+ * receive one soon) that we really want to send to the reader. Set a
+ * trap that simply copies such signals to the child.
+ */
+#ifdef F_SETOWN_BUG_FIXED
+ (void) sigset(SIGURG, copytochild);
+#else
+ (void) sigset(SIGURG, SIG_IGN);
+#endif /* F_SETOWN_BUG_FIXED */
+ (void) sigset(SIGUSR1, writeroob);
+ /*
+ * Of course, if the urgent byte already arrived, allowing SIGURG
+ * won't get us notification. So, we check to see if we've got
+ * an urgent byte. If so, force a call to writeroob() to pretend
+ * we got SIGURG.
+ */
+ if (ioctl(rem, SIOCATMARK, &atmark) >= 0) {
+ if (atmark)
+ writeroob(0);
+ }
+ sigsetmask(oldmask);
+ (void) sigset(SIGCHLD, catchild);
+ writer();
+ prf(gettext("Closed connection to %.*s."), MAXHOSTNAMELEN, host);
+ done(EXIT_SUCCESS);
+}
+
+/*
+ * Get signal disposition (or signal handler) for a given signal
+ */
+static sigdisp_t
+sigdisp(int sig)
+{
+ struct sigaction act;
+
+ act.sa_handler = NULL;
+ act.sa_flags = 0;
+ (void) sigemptyset(&act.sa_mask);
+ (void) sigaction(sig, NULL, &act);
+ return (act.sa_handler);
+}
+
+static void
+done(int status)
+{
+ pid_t w;
+
+ mode(0);
+ if (child > 0) {
+ /* make sure catchild does not snap it up */
+ (void) sigset(SIGCHLD, SIG_DFL);
+ if (kill(child, SIGKILL) >= 0)
+ while ((w = wait(0)) > (pid_t)0 && w != child)
+ /* void */;
+ }
+ pop(status);
+}
+
+/*
+ * Copy SIGURGs to the child process.
+ */
+
+/* ARGSUSED */
+static void
+copytochild(int signum)
+{
+
+ (void) kill(child, SIGURG);
+}
+
+/*
+ * This is called when the reader process gets the out-of-band (urgent)
+ * request to turn on the window-changing protocol.
+ */
+
+/* ARGSUSED */
+static void
+writeroob(int signum)
+{
+ int mask;
+
+ if (!dosigwinch) {
+ /*
+ * Start tracking window size. It doesn't matter which
+ * order the next two are in, because we'll be unconditionally
+ * sending a size notification in a moment.
+ */
+ (void) sigset(SIGWINCH, sigwinch);
+ dosigwinch = B_TRUE;
+
+ /*
+ * It would be bad if a SIGWINCH came in between the ioctl
+ * and sending the data. It could result in the SIGWINCH
+ * handler sending a good message, and then us sending an
+ * outdated or inconsistent message.
+ *
+ * Instead, if the change is made before the
+ * ioctl, the sigwinch handler will send a size message
+ * and we'll send another, identical, one. If the change
+ * is made after the ioctl, we'll send a message with the
+ * old value, and then the sigwinch handler will send
+ * a revised, correct one.
+ */
+ mask = sigblock(sigmask(SIGWINCH));
+ if (ioctl(STDIN_FILENO, TIOCGWINSZ, &winsize) == 0)
+ sendwindow();
+ sigsetmask(mask);
+ }
+}
+
+/* ARGSUSED */
+static void
+catchild(int signum)
+{
+ int options;
+ siginfo_t info;
+ int error;
+
+ for (;;) {
+ options = WNOHANG | WEXITED;
+ error = waitid(P_ALL, 0, &info, options);
+ if (error != 0)
+ return;
+ if (info.si_pid == 0)
+ return;
+ if (info.si_code == CLD_TRAPPED)
+ continue;
+ if (info.si_code == CLD_STOPPED)
+ continue;
+ done(info.si_status);
+ }
+}
+
+/*
+ * writer: write to remote: 0 -> line.
+ * ~. terminate
+ * ~^Z suspend rlogin process.
+ * ~^Y suspend rlogin process, but leave reader alone.
+ */
+static void
+writer(void)
+{
+ char c;
+ int n;
+ boolean_t bol = B_TRUE; /* beginning of line */
+ boolean_t local = B_FALSE;
+
+ for (;;) {
+ n = read(STDIN_FILENO, &c, 1);
+ if (n <= 0) {
+ if (n == 0)
+ break;
+ if (errno == EINTR)
+ continue;
+ else {
+ prf(gettext("Read error from terminal: %s"),
+ errmsg(errno));
+ break;
+ }
+ }
+ /*
+ * If we're at the beginning of the line
+ * and recognize a command character, then
+ * we echo locally. Otherwise, characters
+ * are echo'd remotely. If the command
+ * character is doubled, this acts as a
+ * force and local echo is suppressed.
+ */
+ if (bol && !nocmdchar) {
+ bol = B_FALSE;
+ if (c == cmdchar) {
+ local = B_TRUE;
+ continue;
+ }
+ } else if (local) {
+ local = B_FALSE;
+ if (c == '.' || c == deftc.t_eofc) {
+ echo(c);
+ break;
+ }
+ if (c == defltc.t_suspc || c == defltc.t_dsuspc) {
+ bol = B_TRUE;
+ echo(c);
+ stop(c);
+ continue;
+ }
+ if (c != cmdchar) {
+ if (deswrite(rem, &cmdchar, 1, 0) < 0) {
+ prf(gettext(
+ "Write error to network: %s"),
+ errmsg(errno));
+ break;
+ }
+ }
+ }
+ if ((n = deswrite(rem, &c, 1, 0)) <= 0) {
+ if (n == 0)
+ prf(gettext("line gone"));
+ else
+ prf(gettext("Write error to network: %s"),
+ errmsg(errno));
+ break;
+ }
+ bol = c == defkill || c == deftc.t_eofc ||
+ c == deftc.t_intrc || c == defltc.t_suspc ||
+ c == '\r' || c == '\n';
+ }
+}
+
+static void
+echo(char c)
+{
+ char buf[8];
+ char *p = buf;
+
+ c &= 0177;
+ *p++ = cmdchar;
+ if (c < ' ') {
+ *p++ = '^';
+ *p++ = c + '@';
+ } else if (c == 0177) {
+ *p++ = '^';
+ *p++ = '?';
+ } else
+ *p++ = c;
+ *p++ = '\r';
+ *p++ = '\n';
+ if (write(STDOUT_FILENO, buf, p - buf) < 0)
+ prf(gettext("Write error to terminal: %s"), errmsg(errno));
+}
+
+static void
+stop(char cmdc)
+{
+ mode(0);
+ (void) sigset(SIGCHLD, SIG_IGN);
+ (void) kill(cmdc == defltc.t_suspc ? 0 : getpid(), SIGTSTP);
+ (void) sigset(SIGCHLD, catchild);
+ mode(1);
+ sigwinch(0); /* check for size changes */
+}
+
+/* ARGSUSED */
+static void
+sigwinch(int signum)
+{
+ struct winsize ws;
+
+ if (dosigwinch && ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == 0 &&
+ memcmp(&winsize, &ws, sizeof (ws)) != 0) {
+ winsize = ws;
+ sendwindow();
+ }
+}
+
+/*
+ * Send the window size to the server via the magic escape.
+ * Note: SIGWINCH should be blocked when this is called, lest
+ * winsize change underneath us and chaos result.
+ */
+static void
+sendwindow(void)
+{
+ char obuf[4 + sizeof (struct winsize)];
+ struct winsize *wp = (struct winsize *)(void *)(obuf+4);
+
+ obuf[0] = -1;
+ obuf[1] = -1;
+ obuf[2] = 's';
+ obuf[3] = 's';
+ wp->ws_row = htons(winsize.ws_row);
+ wp->ws_col = htons(winsize.ws_col);
+ wp->ws_xpixel = htons(winsize.ws_xpixel);
+ wp->ws_ypixel = htons(winsize.ws_ypixel);
+ if (deswrite(rem, obuf, sizeof (obuf), 0) < 0)
+ prf(gettext("Write error to network: %s"), errmsg(errno));
+}
+
+
+/*
+ * reader: read from remote: remote -> stdout
+ */
+#define READING 1
+#define WRITING 2
+
+static char rcvbuf[8 * 1024];
+static int rcvcnt;
+static int rcvstate;
+static pid_t ppid;
+static jmp_buf rcvtop;
+
+static void
+oob(void)
+{
+ int out = FWRITE, atmark, n;
+ int rcvd = 0;
+ char waste[4*BUFSIZ], mark;
+ struct sgttyb sb;
+ fd_set exceptfds;
+ struct timeval tv;
+ int ret;
+
+ FD_ZERO(&exceptfds);
+ FD_SET(rem, &exceptfds);
+ timerclear(&tv);
+ ret = select(rem+1, NULL, NULL, &exceptfds, &tv);
+ /*
+ * We may get an extra signal at start up time since we are trying
+ * to take all precautions not to miss the urgent byte. This
+ * means we may get here without any urgent data to process, in which
+ * case we do nothing and just return.
+ */
+ if (ret <= 0)
+ return;
+
+ do {
+ if (ioctl(rem, SIOCATMARK, &atmark) < 0) {
+ break;
+ }
+ if (!atmark) {
+ /*
+ * Urgent data not here yet.
+ * It may not be possible to send it yet
+ * if we are blocked for output
+ * and our input buffer is full.
+ */
+ if (rcvcnt < sizeof (rcvbuf)) {
+ n = desread(rem, rcvbuf + rcvcnt,
+ sizeof (rcvbuf) - rcvcnt, 0);
+ if (n <= 0)
+ return;
+ rcvd += n;
+ rcvcnt += n;
+ } else {
+ /*
+ * We still haven't gotten to the urgent mark
+ * and we're out of buffer space. Since we
+ * must clear our receive window to allow it
+ * to arrive, we will have to throw away
+ * these bytes.
+ */
+ n = desread(rem, waste, sizeof (waste), 0);
+ if (n <= 0)
+ return;
+ }
+ }
+ } while (atmark == 0);
+ while (recv(rem, &mark, 1, MSG_OOB) < 0) {
+ switch (errno) {
+
+ case EWOULDBLOCK:
+ /*
+ * We've reached the urgent mark, so the next
+ * data to arrive will be the urgent, but it must
+ * not have arrived yet.
+ */
+ (void) sleep(1);
+ continue;
+
+ default:
+ return;
+ }
+ }
+ if (mark & TIOCPKT_WINDOW) {
+ /*
+ * Let server know about window size changes
+ */
+ (void) kill(ppid, SIGUSR1);
+ }
+ if (!eight && (mark & TIOCPKT_NOSTOP)) {
+ if (ioctl(STDIN_FILENO, TIOCGETP, (char *)&sb) == -1)
+ perror("ioctl TIOCGETP");
+ sb.sg_flags &= ~O_CBREAK;
+ sb.sg_flags |= O_RAW;
+ if (compat_ioctl(STDIN_FILENO, TIOCSETP, &sb) == -1)
+ perror("ioctl TIOCSETP 1");
+ notc.t_stopc = -1;
+ notc.t_startc = -1;
+ if (compat_ioctl(STDIN_FILENO, TIOCSETC, &notc) == -1)
+ perror("ioctl TIOCSETC");
+ }
+ if (!eight && (mark & TIOCPKT_DOSTOP)) {
+ if (ioctl(STDIN_FILENO, TIOCGETP, (char *)&sb) == -1)
+ perror("ioctl TIOCGETP");
+ sb.sg_flags &= ~O_RAW;
+ sb.sg_flags |= O_CBREAK;
+ if (compat_ioctl(STDIN_FILENO, TIOCSETP, &sb) == -1)
+ perror("ioctl TIOCSETP 2");
+ notc.t_stopc = deftc.t_stopc;
+ notc.t_startc = deftc.t_startc;
+ if (compat_ioctl(STDIN_FILENO, TIOCSETC, &notc) == -1)
+ perror("ioctl TIOCSETC");
+ }
+ if (mark & TIOCPKT_FLUSHWRITE) {
+ if (ioctl(STDOUT_FILENO, TIOCFLUSH, (char *)&out) == -1)
+ perror("ioctl TIOCFLUSH");
+ for (;;) {
+ if (ioctl(rem, SIOCATMARK, &atmark) < 0) {
+ perror("ioctl SIOCATMARK");
+ break;
+ }
+ if (atmark)
+ break;
+ n = desread(rem, waste, sizeof (waste), 0);
+ if (n <= 0) {
+ if (n < 0)
+ prf(gettext(
+ "Read error from network: %s"),
+ errmsg(errno));
+ break;
+ }
+ }
+ /*
+ * Don't want any pending data to be output,
+ * so clear the recv buffer.
+ * If we were hanging on a write when interrupted,
+ * don't want it to restart. If we were reading,
+ * restart anyway.
+ */
+ rcvcnt = 0;
+ longjmp(rcvtop, 1);
+ }
+ /*
+ * If we filled the receive buffer while a read was pending,
+ * longjmp to the top to restart appropriately. Don't abort
+ * a pending write, however, or we won't know how much was written.
+ */
+ if (rcvd && rcvstate == READING)
+ longjmp(rcvtop, 1);
+}
+
+/*
+ * reader: read from remote: line -> 1
+ */
+static int
+reader(int oldmask)
+{
+ /*
+ * 4.3bsd or later and SunOS 4.0 or later use the posiitive
+ * pid; otherwise use the negative.
+ */
+ pid_t pid = getpid();
+ int n, remaining;
+ char *bufp = rcvbuf;
+
+ (void) sigset(SIGTTOU, SIG_IGN);
+ (void) sigset(SIGURG, (void (*)())oob);
+ ppid = getppid();
+ if (fcntl(rem, F_SETOWN, pid) == -1)
+ perror("fcntl F_SETOWN");
+ /*
+ * A SIGURG may have been posted before we were completely forked,
+ * which means we may not have received it. To insure we do not miss
+ * any urgent data, we force the signal. The signal hander will be
+ * able to determine if in fact there is urgent data or not.
+ */
+ (void) kill(pid, SIGURG);
+ (void) setjmp(rcvtop);
+ sigsetmask(oldmask);
+ for (;;) {
+ while ((remaining = rcvcnt - (bufp - rcvbuf)) > 0) {
+ rcvstate = WRITING;
+ n = write(STDOUT_FILENO, bufp, remaining);
+ if (n < 0) {
+ if (errno != EINTR) {
+ prf(gettext(
+ "Write error to terminal: %s"),
+ errmsg(errno));
+ return (-1);
+ }
+ continue;
+ }
+ bufp += n;
+ }
+ bufp = rcvbuf;
+ rcvcnt = 0;
+ rcvstate = READING;
+ rcvcnt = desread(rem, rcvbuf, sizeof (rcvbuf), 0);
+ if (rcvcnt == 0)
+ return (0);
+ if (rcvcnt < 0) {
+ if (errno == EINTR)
+ continue;
+ prf(gettext("Read error from network: %s"),
+ errmsg(errno));
+ return (-1);
+ }
+ }
+}
+
+static void
+mode(int f)
+{
+ struct tchars *tc;
+ struct ltchars *ltc;
+ struct sgttyb sb;
+ int lflags;
+
+ if (ioctl(STDIN_FILENO, TIOCGETP, (char *)&sb) == -1)
+ perror("ioctl TIOCGETP");
+ if (ioctl(STDIN_FILENO, TIOCLGET, (char *)&lflags) == -1)
+ perror("ioctl TIOCLGET");
+ switch (f) {
+
+ case 0:
+ sb.sg_flags &= ~(O_CBREAK|O_RAW|O_TBDELAY);
+ sb.sg_flags |= defflags|tabflag;
+ tc = &deftc;
+ ltc = &defltc;
+ sb.sg_kill = defkill;
+ sb.sg_erase = deferase;
+ lflags = deflflags;
+ break;
+
+ case 1:
+ sb.sg_flags |= (eight ? O_RAW : O_CBREAK);
+ sb.sg_flags &= ~defflags;
+ /* preserve tab delays, but turn off XTABS */
+ if ((sb.sg_flags & O_TBDELAY) == O_XTABS)
+ sb.sg_flags &= ~O_TBDELAY;
+ tc = &notc;
+ ltc = &noltc;
+ sb.sg_kill = sb.sg_erase = -1;
+ if (litout)
+ lflags |= LLITOUT;
+ break;
+
+ default:
+ /*NOTREACHED*/
+ return;
+ }
+ if (compat_ioctl(STDIN_FILENO, TIOCSLTC, ltc) == -1)
+ perror("ioctl TIOCSLTC");
+ if (compat_ioctl(STDIN_FILENO, TIOCSETC, tc) == -1)
+ perror("ioctl TIOCSETC");
+ if (compat_ioctl(STDIN_FILENO, TIOCSETP, &sb) == -1)
+ perror("ioctl TIOCSETP 3");
+ if (compat_ioctl(STDIN_FILENO, TIOCLSET, &lflags) == -1)
+ perror("ioctl TIOCLSET");
+}
+
+/* PRINTFLIKE(0) */
+static void
+prf(const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ (void) vfprintf(stderr, format, ap);
+ va_end(ap);
+ (void) fputs(CRLF, stderr);
+}
+
+static void
+lostpeer(void)
+{
+ (void) sigset(SIGPIPE, SIG_IGN);
+ prf(gettext("\aConnection to %.*s closed."), MAXHOSTNAMELEN, host);
+ done(EXIT_FAILURE);
+}
+
+static char *
+errmsg(int errcode)
+{
+ extern int sys_nerr;
+
+ if (errcode < 0 || errcode > sys_nerr)
+ return (gettext("Unknown error"));
+ else
+ return (strerror(errcode));
+}
+
+static int
+compat_ioctl(int des, int request, void *arg)
+{
+ struct termios tb;
+ boolean_t flag = B_FALSE;
+
+ if (ioctl(des, request, arg) < 0)
+ return (-1);
+
+ if (tcgetattr(des, &tb) < 0)
+ return (-1);
+
+ if (cfgetispeed(&tb) != cfgetispeed(&savetty)) {
+ (void) cfsetispeed(&tb, cfgetispeed(&savetty));
+ flag = B_TRUE;
+ }
+ if (cfgetospeed(&tb) != cfgetospeed(&savetty)) {
+ (void) cfsetospeed(&tb, cfgetospeed(&savetty));
+ flag = B_TRUE;
+ }
+
+ return (flag ? tcsetattr(des, TCSANOW, &tb) : 0);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/rsh.c b/usr/src/cmd/cmd-inet/usr.bin/rsh.c
new file mode 100644
index 0000000000..640931cc1d
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/rsh.c
@@ -0,0 +1,794 @@
+/*
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved. The Berkeley software License Agreement
+ * specifies the terms and conditions for redistribution.
+ *
+ */
+
+#define _FILE_OFFSET_BITS 64
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+/* just for FIONBIO ... */
+#include <sys/filio.h>
+#include <sys/stat.h>
+#include <sys/select.h>
+
+#include <netinet/in.h>
+
+#include <assert.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <signal.h>
+#include <pwd.h>
+#include <netdb.h>
+#include <locale.h>
+#include <priv_utils.h>
+
+#include <k5-int.h>
+#include <profile/prof_int.h>
+#include <com_err.h>
+#include <kcmd.h>
+#include <krb5.h>
+
+/* signal disposition - signal handler or SIG_IGN, SIG_ERR, etc. */
+typedef void (*sigdisp_t)(int);
+
+extern errcode_t profile_get_options_boolean(profile_t, char **,
+ profile_options_boolean *);
+extern errcode_t profile_get_options_string(profile_t, char **,
+ profile_option_strings *);
+
+#define RSH_BUFSIZ (1024 * 50)
+
+static char des_inbuf[2 * RSH_BUFSIZ]; /* needs to be > largest read size */
+static char des_outbuf[2 * RSH_BUFSIZ]; /* needs to be > largest write size */
+static krb5_data desinbuf, desoutbuf;
+static krb5_encrypt_block eblock; /* eblock for encrypt/decrypt */
+static krb5_context bsd_context;
+static krb5_auth_context auth_context;
+static krb5_creds *cred;
+static krb5_keyblock *session_key;
+
+static int encrypt_flag; /* Flag set, when encryption is used */
+static boolean_t krb5auth_flag; /* Flag set, when KERBEROS is enabled */
+static int fflag; /* Flag set, if creds to be fwd'ed via -f */
+static int Fflag; /* Flag set, if fwd'able creds to be fwd'ed via -F */
+
+/* Flag set, if -PN / -PO is specified */
+static boolean_t rcmdoption_done;
+
+/* Flags set, if corres. cmd line options are turned on */
+static boolean_t encrypt_done, fwd_done, fwdable_done;
+
+static profile_options_boolean option[] = {
+ { "encrypt", &encrypt_flag, 0 },
+ { "forward", &fflag, 0 },
+ { "forwardable", &Fflag, 0 },
+ { NULL, NULL, 0 }
+};
+
+static char *rcmdproto;
+static profile_option_strings rcmdversion[] = {
+ { "rcmd_protocol", &rcmdproto, 0 },
+ { NULL, NULL, 0 }
+};
+
+static char *realmdef[] = { "realms", NULL, "rsh", NULL };
+static char *appdef[] = { "appdefaults", "rsh", NULL };
+
+static void sendsig(int);
+static sigdisp_t sigdisp(int);
+static boolean_t init_service(boolean_t);
+static int desrshread(int, char *, int);
+static int desrshwrite(int, char *, int);
+
+static int options;
+static int rfd2;
+static int portnumber;
+
+static const char rlogin_path[] = "/usr/bin/rlogin";
+static const char dash_x[] = "-x "; /* Note the blank after -x */
+
+static boolean_t readiv, writeiv;
+
+#define set2mask(setp) ((setp)->__sigbits[0])
+#define mask2set(mask, setp) \
+ ((mask) == -1 ? sigfillset(setp) : (set2mask(setp) = (mask)))
+
+#ifdef DEBUG
+#define DEBUGOPTSTRING "D:"
+#else
+#define DEBUGOPTSTRING ""
+#endif /* DEBUG */
+
+static void
+sigsetmask(int mask)
+{
+ sigset_t nset;
+
+ (void) sigprocmask(0, NULL, &nset);
+ mask2set(mask, &nset);
+ (void) sigprocmask(SIG_SETMASK, &nset, NULL);
+}
+
+static int
+sigblock(int mask)
+{
+ sigset_t oset;
+ sigset_t nset;
+
+ (void) sigprocmask(0, NULL, &nset);
+ mask2set(mask, &nset);
+ (void) sigprocmask(SIG_BLOCK, &nset, &oset);
+ return (set2mask(&oset));
+}
+
+/*
+ * Get signal disposition (or signal handler) for a given signal
+ */
+static sigdisp_t
+sigdisp(int sig)
+{
+ struct sigaction act;
+
+ act.sa_handler = NULL;
+ act.sa_flags = 0;
+ (void) sigemptyset(&act.sa_mask);
+ (void) sigaction(sig, NULL, &act);
+ return (act.sa_handler);
+}
+
+static pid_t child_pid = -1;
+
+/*
+ * If you do a command like "rsh host output | wc"
+ * and wc terminates, then the parent will receive SIGPIPE
+ * and the child needs to be terminated.
+ */
+/* ARGSUSED */
+static void
+sigpipehandler(int signal)
+{
+ if (child_pid != -1)
+ (void) kill(child_pid, SIGKILL);
+ exit(EXIT_SUCCESS);
+}
+
+#define mask(s) (1 << ((s) - 1))
+
+static void
+usage(void) {
+ (void) fprintf(stderr, "%s\n%s\n",
+ gettext("usage: rsh [ -PN / -PO ] [ -l login ] [ -n ] "
+ "[ -k realm ] [ -a ] [ -x ] [ -f / -F ] host command"),
+ gettext(" rsh [ -PN / -PO ] [ -l login ] [ -k realm ] "
+ "[ -a ] [ -x ] [ -f / -F ] host"));
+ exit(EXIT_FAILURE);
+}
+
+static void
+die(const char *message)
+{
+ (void) fputs(message, stderr);
+ usage();
+}
+
+static void
+usage_forward(void)
+{
+ die(gettext("rsh: Only one of -f and -F allowed.\n"));
+}
+
+/*
+ * rsh - remote shell
+ */
+/* VARARGS */
+int
+main(int argc, char **argv)
+{
+ int c, rem;
+ char *cmd, *cp, **ap, buf[RSH_BUFSIZ], **argv0, *args, *args_no_x;
+ char *host = NULL, *user = NULL;
+ int cc;
+ boolean_t asrsh = B_FALSE;
+ struct passwd *pwd;
+ boolean_t readfrom_rem;
+ boolean_t readfrom_rfd2;
+ int one = 1;
+ int omask;
+ boolean_t nflag = B_FALSE;
+ char *krb_realm = NULL;
+ krb5_flags authopts;
+ krb5_error_code status;
+ enum kcmd_proto kcmd_proto = KCMD_NEW_PROTOCOL;
+ uid_t uid = getuid();
+
+ c = (argc + 1) * sizeof (char *);
+ if ((argv0 = malloc(c)) == NULL) {
+ perror("malloc");
+ return (EXIT_FAILURE);
+ }
+ (void) memcpy(argv0, argv, c);
+
+ (void) setlocale(LC_ALL, "");
+
+ (void) textdomain(TEXT_DOMAIN);
+
+ /*
+ * Determine command name used to invoke to rlogin(1). Users can
+ * create links named by a host pointing to the binary and type
+ * "hostname" to log into that host afterwards.
+ */
+ cmd = strrchr(argv[0], '/');
+ cmd = (cmd != NULL) ? (cmd + 1) : argv[0];
+
+ /*
+ * Add "remsh" as an alias for "rsh" (System III, V networking
+ * add-ons often used this name for the remote shell since rsh
+ * was already taken for the restricted shell). Note that this
+ * usurps the ability to use "remsh" as the name of a host (by
+ * symlinking it to rsh), so we go one step farther: if the
+ * file "/usr/bin/remsh" does not exist, we behave as if "remsh"
+ * is a host name. If it does exist, we accept "remsh" as an
+ * "rsh" alias.
+ */
+ if (strcmp(cmd, "remsh") == 0) {
+ struct stat sb;
+
+ if (stat("/usr/bin/remsh", &sb) < 0)
+ host = cmd;
+ } else if (strcmp(cmd, "rsh") != 0) {
+ host = cmd;
+ }
+
+ /* Handle legacy synopsis "rsh hostname options [command]". */
+ if (host == NULL) {
+ if (argc < 2)
+ usage();
+ if (*argv[1] != '-') {
+ host = argv[1];
+ argc--;
+ argv[1] = argv[0];
+ argv++;
+ asrsh = B_TRUE;
+ }
+ }
+
+ while ((c = getopt(argc, argv,
+ DEBUGOPTSTRING "8AFLP:ade:fk:l:nwx")) != -1) {
+ switch (c) {
+#ifdef DEBUG
+ case 'D':
+ portnumber = htons(atoi(optarg));
+ krb5auth_flag = B_TRUE;
+ break;
+#endif /* DEBUG */
+ case 'F':
+ if (fflag)
+ usage_forward();
+ Fflag = 1;
+ krb5auth_flag = B_TRUE;
+ fwdable_done = B_TRUE;
+ break;
+ case 'f':
+ if (Fflag)
+ usage_forward();
+ fflag = 1;
+ krb5auth_flag = B_TRUE;
+ fwd_done = B_TRUE;
+ break;
+ case 'P':
+ if (strcmp(optarg, "N") == 0)
+ kcmd_proto = KCMD_NEW_PROTOCOL;
+ else if (strcmp(optarg, "O") == 0)
+ kcmd_proto = KCMD_OLD_PROTOCOL;
+ else
+ die(gettext("rsh: Only -PN or -PO "
+ "allowed.\n"));
+ if (rcmdoption_done)
+ die(gettext("rsh: Only one of -PN and -PO "
+ "allowed.\n"));
+ rcmdoption_done = B_TRUE;
+ krb5auth_flag = B_TRUE;
+ break;
+ case 'a':
+ krb5auth_flag = B_TRUE;
+ break;
+ case 'd':
+ options |= SO_DEBUG;
+ break;
+ case 'k':
+ krb_realm = optarg;
+ krb5auth_flag = B_TRUE;
+ break;
+ case 'l':
+ user = optarg;
+ break;
+ case 'n':
+ if (!nflag) {
+ if (close(STDIN_FILENO) < 0) {
+ perror("close");
+ return (EXIT_FAILURE);
+ }
+ /*
+ * "STDION_FILENO" defined to 0 by POSIX
+ * and hence the lowest file descriptor.
+ * So the open(2) below is guaranteed to
+ * reopen it because we closed it above.
+ */
+ if (open("/dev/null", O_RDONLY) < 0) {
+ perror("open");
+ return (EXIT_FAILURE);
+ }
+ nflag = B_TRUE;
+ }
+ break;
+ case 'x':
+ encrypt_flag = 1;
+ krb5auth_flag = B_TRUE;
+ encrypt_done = B_TRUE;
+ break;
+ /*
+ * Ignore the -L, -w, -e and -8 flags to allow aliases with
+ * rlogin to work. Actually rlogin(1) doesn't understand
+ * -w either but because "rsh -w hostname command" used
+ * to work we still accept it.
+ */
+ case '8':
+ case 'L':
+ case 'e':
+ case 'w':
+ /*
+ * On the lines of the -L, -w, -e and -8 options above, we
+ * ignore the -A option too, in order to allow aliases with
+ * rlogin to work.
+ *
+ * Mind you !, the -a option to trigger Kerberos authentication
+ * in rsh, has a totally different usage in rlogin, its the
+ * -A option (in rlogin) which needs to be used to talk
+ * Kerberos.
+ */
+ case 'A':
+ break;
+ default:
+ usage();
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (host == NULL) {
+ if (argc == 0)
+ usage();
+ argc--;
+ host = *argv++;
+ asrsh = B_TRUE;
+ }
+
+ if (argc == 0) {
+ (void) setreuid(uid, uid);
+ if (nflag)
+ usage();
+ if (asrsh)
+ *argv0 = "rlogin";
+ (void) execv(rlogin_path, argv0);
+ perror(rlogin_path);
+
+ (void) fprintf(stderr, gettext("No local rlogin "
+ "program found.\n"));
+ return (EXIT_FAILURE);
+ }
+
+ if (__init_suid_priv(0, PRIV_NET_PRIVADDR, NULL) == -1) {
+ (void) fprintf(stderr,
+ gettext("Insufficient privileges, "
+ "rsh must be set-uid root\n"));
+ return (EXIT_FAILURE);
+ }
+
+ pwd = getpwuid(uid);
+ if (pwd == NULL) {
+ (void) fprintf(stderr, gettext("who are you?\n"));
+ return (EXIT_FAILURE);
+ }
+ if (user == NULL)
+ user = pwd->pw_name;
+
+ if (krb5auth_flag) {
+ status = krb5_init_context(&bsd_context);
+ if (status) {
+ com_err("rsh", status, "while initializing krb5");
+ return (EXIT_FAILURE);
+ }
+
+ /*
+ * Get our local realm to look up local realm options.
+ */
+ status = krb5_get_default_realm(bsd_context, &realmdef[1]);
+ if (status) {
+ com_err("rsh", status,
+ gettext("while getting default realm"));
+ return (EXIT_FAILURE);
+ }
+ /*
+ * Check the realms section in krb5.conf for encryption,
+ * forward & forwardable info
+ */
+ profile_get_options_boolean(bsd_context->profile, realmdef,
+ option);
+ /*
+ * Check the appdefaults section
+ */
+ profile_get_options_boolean(bsd_context->profile, appdef,
+ option);
+ profile_get_options_string(bsd_context->profile, appdef,
+ rcmdversion);
+ /*
+ * Set the *_flag variables, if the corresponding *_done are
+ * set to 1, because we dont want the config file values
+ * overriding the command line options.
+ */
+ if (encrypt_done)
+ encrypt_flag = 1;
+ if (fwd_done) {
+ fflag = 1;
+ Fflag = 0;
+ } else if (fwdable_done) {
+ Fflag = 1;
+ fflag = 0;
+ }
+ if (!rcmdoption_done && (rcmdproto != NULL)) {
+ if (strncmp(rcmdproto, "rcmdv2", 6) == 0) {
+ kcmd_proto = KCMD_NEW_PROTOCOL;
+ } else if (strncmp(rcmdproto, "rcmdv1", 6) == 0) {
+ kcmd_proto = KCMD_OLD_PROTOCOL;
+ } else {
+ (void) fprintf(stderr, gettext("Unrecognized "
+ "KCMD protocol (%s)"), rcmdproto);
+ return (EXIT_FAILURE);
+ }
+ }
+
+
+ if (encrypt_flag && (!krb5_privacy_allowed())) {
+ (void) fprintf(stderr, gettext("rsh: Encryption not "
+ "supported.\n"));
+ return (EXIT_FAILURE);
+ }
+ }
+
+ /*
+ * Connect with the service (shell/kshell) on the daemon side
+ */
+ if (portnumber == 0) {
+ while (!init_service(krb5auth_flag)) {
+ /*
+ * Connecting to the 'kshell' service failed,
+ * fallback to normal rsh; Reset all KRB5 flags
+ * and connect to 'shell' service on the server
+ */
+ krb5auth_flag = B_FALSE;
+ encrypt_flag = fflag = Fflag = 0;
+ }
+ }
+
+ cc = encrypt_flag ? strlen(dash_x) : 0;
+ for (ap = argv; *ap != NULL; ap++)
+ cc += strlen(*ap) + 1;
+ cp = args = malloc(cc);
+ if (cp == NULL)
+ perror("malloc");
+ if (encrypt_flag) {
+ int length;
+
+ length = strlcpy(args, dash_x, cc);
+ cp += length;
+ cc -= length;
+ }
+ args_no_x = args;
+
+ for (ap = argv; *ap != NULL; ap++) {
+ int length;
+
+ length = strlcpy(cp, *ap, cc);
+ assert(length < cc);
+ cp += length;
+ cc -= length;
+ if (ap[1] != NULL) {
+ *cp++ = ' ';
+ cc--;
+ }
+ }
+
+ if (krb5auth_flag) {
+ authopts = AP_OPTS_MUTUAL_REQUIRED;
+ /*
+ * Piggy-back forwarding flags on top of authopts;
+ * they will be reset in kcmd
+ */
+ if (fflag || Fflag)
+ authopts |= OPTS_FORWARD_CREDS;
+ if (Fflag)
+ authopts |= OPTS_FORWARDABLE_CREDS;
+
+ status = kcmd(&rem, &host, portnumber,
+ pwd->pw_name, user,
+ args, &rfd2, "host", krb_realm,
+ bsd_context, &auth_context, &cred,
+ NULL, /* No need for sequence number */
+ NULL, /* No need for server seq # */
+ authopts,
+ 1, /* Always set anyport */
+ &kcmd_proto);
+ if (status != 0) {
+ /*
+ * If new protocol requested, we dont fallback to
+ * less secure ones.
+ */
+ if (kcmd_proto == KCMD_NEW_PROTOCOL) {
+ (void) fprintf(stderr, gettext("rsh: kcmdv2 "
+ "to host %s failed - %s\n"
+ "Fallback to normal rsh denied."),
+ host, error_message(status));
+ return (EXIT_FAILURE);
+ }
+ /* check NO_TKT_FILE or equivalent... */
+ if (status != -1) {
+ (void) fprintf(stderr,
+ gettext("rsh: kcmd to host %s failed - %s\n"
+ "trying normal rsh...\n\n"),
+ host, error_message(status));
+ } else {
+ (void) fprintf(stderr,
+ gettext("trying normal rsh...\n"));
+ }
+ /*
+ * kcmd() failed, so we now fallback to normal rsh,
+ * after resetting the KRB5 flags and the 'args' array
+ */
+ krb5auth_flag = B_FALSE;
+ encrypt_flag = fflag = Fflag = 0;
+ args = args_no_x;
+ (void) init_service(B_FALSE);
+ } else {
+ /*
+ * Set up buffers for desread and deswrite.
+ */
+ desinbuf.data = des_inbuf;
+ desoutbuf.data = des_outbuf;
+ desinbuf.length = sizeof (des_inbuf);
+ desoutbuf.length = sizeof (des_outbuf);
+
+ session_key = &cred->keyblock;
+
+ if (kcmd_proto == KCMD_NEW_PROTOCOL) {
+ status = krb5_auth_con_getlocalsubkey(
+ bsd_context,
+ auth_context,
+ &session_key);
+ if (status) {
+ com_err("rsh", status,
+ "determining subkey for session");
+ return (EXIT_FAILURE);
+ }
+ if (session_key == NULL) {
+ com_err("rsh", 0, "no subkey "
+ "negotiated for connection");
+ return (EXIT_FAILURE);
+ }
+ }
+
+ eblock.crypto_entry = session_key->enctype;
+ eblock.key = (krb5_keyblock *)session_key;
+
+ init_encrypt(encrypt_flag, bsd_context, kcmd_proto,
+ &desinbuf, &desoutbuf, CLIENT, &eblock);
+ if (encrypt_flag) {
+ char *s = gettext("This rsh session is using "
+ "encryption for all data transmissions.");
+ (void) write(STDERR_FILENO, s, strlen(s));
+ (void) write(STDERR_FILENO, "\r\n", 2);
+ }
+ }
+ }
+
+ /*
+ * Don't merge this with the "if" statement above because
+ * "krb5auth_flag" might be set to false inside it.
+ */
+ if (!krb5auth_flag) {
+ rem = rcmd_af(&host, portnumber, pwd->pw_name, user, args,
+ &rfd2, AF_INET6);
+ if (rem < 0)
+ return (EXIT_FAILURE);
+ }
+ __priv_relinquish();
+
+ if (rfd2 < 0) {
+ (void) fprintf(stderr, gettext("rsh: can't establish "
+ "stderr\n"));
+ return (EXIT_FAILURE);
+ }
+ if (options & SO_DEBUG) {
+ if (setsockopt(rem, SOL_SOCKET, SO_DEBUG, (char *)&one,
+ sizeof (one)) < 0)
+ perror("rsh: setsockopt (stdin)");
+ if (setsockopt(rfd2, SOL_SOCKET, SO_DEBUG, (char *)&one,
+ sizeof (one)) < 0)
+ perror("rsh: setsockopt (stderr)");
+ }
+ omask = sigblock(mask(SIGINT)|mask(SIGQUIT)|mask(SIGTERM));
+
+ if (sigdisp(SIGINT) != SIG_IGN)
+ (void) sigset(SIGINT, sendsig);
+ if (sigdisp(SIGQUIT) != SIG_IGN)
+ (void) sigset(SIGQUIT, sendsig);
+ if (sigdisp(SIGTERM) != SIG_IGN)
+ (void) sigset(SIGTERM, sendsig);
+
+ if (nflag) {
+ (void) shutdown(rem, SHUT_WR);
+ } else {
+ child_pid = fork();
+ if (child_pid < 0) {
+ perror("rsh: fork");
+ return (EXIT_FAILURE);
+ }
+
+ if (!encrypt_flag) {
+ (void) ioctl(rfd2, FIONBIO, &one);
+ (void) ioctl(rem, FIONBIO, &one);
+ }
+
+ if (child_pid == 0) {
+ /* Child */
+ fd_set remset;
+ char *bp;
+ int wc;
+ (void) close(rfd2);
+ reread:
+ errno = 0;
+ cc = read(0, buf, sizeof (buf));
+ if (cc <= 0)
+ goto done;
+ bp = buf;
+ rewrite:
+ FD_ZERO(&remset);
+ FD_SET(rem, &remset);
+ if (select(rem + 1, NULL, &remset, NULL, NULL) < 0) {
+ if (errno != EINTR) {
+ perror("rsh: select");
+ return (EXIT_FAILURE);
+ }
+ goto rewrite;
+ }
+ if (!FD_ISSET(rem, &remset))
+ goto rewrite;
+ writeiv = B_FALSE;
+ wc = desrshwrite(rem, bp, cc);
+ if (wc < 0) {
+ if (errno == EWOULDBLOCK)
+ goto rewrite;
+ goto done;
+ }
+ cc -= wc; bp += wc;
+ if (cc == 0)
+ goto reread;
+ goto rewrite;
+ done:
+ (void) shutdown(rem, SHUT_WR);
+ return (EXIT_SUCCESS);
+ }
+ }
+
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+
+ sigsetmask(omask);
+ readfrom_rem = B_TRUE;
+ readfrom_rfd2 = B_TRUE;
+ (void) sigset(SIGPIPE, sigpipehandler);
+ do {
+ fd_set readyset;
+
+ FD_ZERO(&readyset);
+ if (readfrom_rem)
+ FD_SET(rem, &readyset);
+ if (readfrom_rfd2)
+ FD_SET(rfd2, &readyset);
+ if (select(MAX(rem, rfd2) + 1, &readyset, NULL, NULL,
+ NULL) < 0) {
+ if (errno != EINTR) {
+ perror("rsh: select");
+ return (EXIT_FAILURE);
+ }
+ continue;
+ }
+ if (FD_ISSET(rfd2, &readyset)) {
+ errno = 0;
+ readiv = B_TRUE;
+ cc = desrshread(rfd2, buf, sizeof (buf));
+ if (cc <= 0) {
+ if (errno != EWOULDBLOCK)
+ readfrom_rfd2 = B_FALSE;
+ } else {
+ (void) write(STDERR_FILENO, buf, cc);
+ }
+ }
+ if (FD_ISSET(rem, &readyset)) {
+ errno = 0;
+ readiv = B_FALSE;
+ cc = desrshread(rem, buf, sizeof (buf));
+ if (cc <= 0) {
+ if (errno != EWOULDBLOCK)
+ readfrom_rem = B_FALSE;
+ } else
+ (void) write(STDOUT_FILENO, buf, cc);
+ }
+ } while (readfrom_rem || readfrom_rfd2);
+
+ if (!nflag)
+ (void) kill(child_pid, SIGKILL);
+ return (EXIT_SUCCESS);
+}
+
+static void
+sendsig(int signum)
+{
+ char buffer;
+
+ writeiv = B_TRUE;
+ buffer = (char)signum;
+ (void) desrshwrite(rfd2, &buffer, 1);
+}
+
+static boolean_t
+init_service(boolean_t krb5flag)
+{
+ struct servent *sp;
+
+ if (krb5flag) {
+ sp = getservbyname("kshell", "tcp");
+ if (sp == NULL) {
+ (void) fprintf(stderr,
+ gettext("rsh: kshell/tcp: unknown service.\n"
+ "trying normal shell/tcp service\n"));
+ return (B_FALSE);
+ }
+ } else {
+ sp = getservbyname("shell", "tcp");
+ if (sp == NULL) {
+ portnumber = htons(IPPORT_CMDSERVER);
+ return (B_TRUE);
+ }
+ }
+
+ portnumber = sp->s_port;
+ return (B_TRUE);
+}
+
+static int
+desrshread(int fd, char *buf, int len)
+{
+ return (desread(fd, buf, len, readiv ? 1 : 0));
+}
+
+static int
+desrshwrite(int fd, char *buf, int len)
+{
+ return (deswrite(fd, buf, len, writeiv ? 1 : 0));
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/ruptime.c b/usr/src/cmd/cmd-inet/usr.bin/ruptime.c
new file mode 100644
index 0000000000..058b0219a3
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/ruptime.c
@@ -0,0 +1,297 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2001 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/param.h>
+#include <stdio.h>
+#include <dirent.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <protocols/rwhod.h>
+
+static DIR *dirp;
+
+#define HOSTLIM 100
+static int hostslim = HOSTLIM;
+static int nhosts;
+struct hs {
+ struct whod *hs_wd;
+ int hs_nusers;
+};
+static int hscmp(), ucmp(), lcmp(), tcmp();
+
+#define RWHODIR "/var/spool/rwho"
+
+static char *interval();
+static time_t now;
+static int aflg;
+static int rflg = 1;
+
+#define down(h) (now - (h)->hs_wd->wd_recvtime > 11 * 60)
+
+/* ARGSUSED */
+main(int argc, char **argv)
+{
+ struct dirent *dp;
+ int f, i;
+ struct whod *buf;
+ int cc;
+ char *name;
+ struct hs *hs;
+ struct hs *hsp;
+ struct whod *wd;
+ struct whoent *we;
+ int maxloadav = 0;
+ int (*cmp)() = hscmp;
+ ptrdiff_t hoff;
+
+ name = *argv;
+ while (*++argv)
+ while (**argv)
+ switch (*(*argv)++) {
+ case 'a':
+ aflg++;
+ break;
+ case 'l':
+ cmp = lcmp;
+ break;
+ case 'u':
+ cmp = ucmp;
+ break;
+ case 't':
+ cmp = tcmp;
+ break;
+ case 'r':
+ rflg = -rflg;
+ break;
+ case '-':
+ break;
+ default:
+ (void) fprintf(stderr, "Usage: %s [ -alrtu ]"
+ " (choose at most one of l, t, or u)\n",
+ name);
+ exit(1);
+ }
+
+ if ((hs = malloc(hostslim * sizeof (struct hs))) == NULL) {
+ (void) fprintf(stderr, "initial hs malloc failed\n");
+ exit(1);
+ }
+ hsp = hs;
+ if ((buf = malloc(sizeof (struct whod))) == NULL) {
+ (void) fprintf(stderr, "initial buf malloc failed\n");
+ exit(1);
+ }
+
+ if (chdir(RWHODIR) < 0) {
+ perror(RWHODIR);
+ exit(1);
+ }
+ dirp = opendir(".");
+ if (dirp == NULL) {
+ perror(RWHODIR);
+ exit(1);
+ }
+ while (dp = readdir(dirp)) {
+ if (dp->d_ino == 0)
+ continue;
+ if (strncmp(dp->d_name, "whod.", 5))
+ continue;
+ if (nhosts == hostslim) {
+ /*
+ * We trust that the file system's limit on the number
+ * of files in a directory will kick in long before
+ * integer overflow.
+ */
+ hostslim = hostslim << 1;
+
+ /*
+ * hsp points into an area about to be moved,
+ * so we first remember its offset into hs[],
+ * then restore it after realloc() has moved
+ * the data.
+ */
+ hoff = hsp - hs;
+ hs = realloc(hs, hostslim * sizeof (struct hs));
+ if (hs == NULL) {
+ (void) fprintf(stderr, "too many hosts\n");
+ exit(1);
+ }
+ hsp = hs + hoff;
+ }
+ f = open(dp->d_name, 0);
+ if (f > 0) {
+ int whdrsize = sizeof (*buf) - sizeof (buf->wd_we);
+
+ cc = read(f, buf, sizeof (struct whod));
+ if (cc >= whdrsize) {
+ hsp->hs_wd = malloc(whdrsize);
+ wd = buf;
+ bcopy((char *)buf, (char *)hsp->hs_wd,
+ whdrsize);
+ hsp->hs_nusers = 0;
+ for (i = 0; i < 2; i++)
+ if (wd->wd_loadav[i] > maxloadav)
+ maxloadav = wd->wd_loadav[i];
+ /* LINTED: pointer alignment */
+ we = (struct whoent *)(((char *)buf)+cc);
+ while (--we >= wd->wd_we)
+ if (aflg || we->we_idle < 3600)
+ hsp->hs_nusers++;
+ nhosts++; hsp++;
+ }
+ }
+ (void) close(f);
+ }
+ (void) time(&now);
+ qsort((char *)hs, nhosts, sizeof (hs[0]), cmp);
+ if (nhosts == 0) {
+ (void) printf("no hosts!?!\n");
+ exit(1);
+ }
+ for (i = 0; i < nhosts; i++) {
+ hsp = &hs[i];
+ if (down(hsp)) {
+ (void) printf("%-12s%s\n", hsp->hs_wd->wd_hostname,
+ interval((int)(now - hsp->hs_wd->wd_recvtime),
+ "down"));
+ continue;
+ }
+ (void) printf("%-12s%s, %4d user%s load %*.2f,"
+ " %*.2f, %*.2f\n",
+ hsp->hs_wd->wd_hostname,
+ interval(hsp->hs_wd->wd_sendtime -
+ hsp->hs_wd->wd_boottime, " up"),
+ hsp->hs_nusers,
+ hsp->hs_nusers == 1 ? ", " : "s,",
+ maxloadav >= 1000 ? 5 : 4,
+ hsp->hs_wd->wd_loadav[0] / 100.0,
+ maxloadav >= 1000 ? 5 : 4,
+ hsp->hs_wd->wd_loadav[1] / 100.0,
+ maxloadav >= 1000 ? 5 : 4,
+ hsp->hs_wd->wd_loadav[2] / 100.0);
+ free(hsp->hs_wd);
+ }
+
+ return (0);
+}
+
+static char *
+interval(int time, char *updown)
+{
+ static char resbuf[32];
+ int days, hours, minutes;
+
+ if (time < 0 || time > 10*365*24*60*60) {
+ (void) sprintf(resbuf, " %s ??:??", updown);
+ return (resbuf);
+ }
+ minutes = (time + 59) / 60; /* round to minutes */
+ hours = minutes / 60; minutes %= 60;
+ days = hours / 24; hours %= 24;
+ if (days)
+ (void) sprintf(resbuf, "%s %2d+%02d:%02d",
+ updown, days, hours, minutes);
+ else
+ (void) sprintf(resbuf, "%s %2d:%02d",
+ updown, hours, minutes);
+ return (resbuf);
+}
+
+static int
+hscmp(struct hs *h1, struct hs *h2)
+{
+
+ return (rflg * strcmp(h1->hs_wd->wd_hostname, h2->hs_wd->wd_hostname));
+}
+
+/*
+ * Compare according to load average.
+ */
+static int
+lcmp(struct hs *h1, struct hs *h2)
+{
+
+ if (down(h1))
+ if (down(h2))
+ return (tcmp(h1, h2));
+ else
+ return (rflg);
+ else if (down(h2))
+ return (-rflg);
+ else
+ return (rflg *
+ (h2->hs_wd->wd_loadav[0] - h1->hs_wd->wd_loadav[0]));
+}
+
+/*
+ * Compare according to number of users.
+ */
+static int
+ucmp(struct hs *h1, struct hs *h2)
+{
+
+ if (down(h1))
+ if (down(h2))
+ return (tcmp(h1, h2));
+ else
+ return (rflg);
+ else if (down(h2))
+ return (-rflg);
+ else
+ return (rflg * (h2->hs_nusers - h1->hs_nusers));
+}
+
+/*
+ * Compare according to uptime.
+ */
+static int
+tcmp(struct hs *h1, struct hs *h2)
+{
+
+ return (rflg * (
+ (down(h2) ? h2->hs_wd->wd_recvtime - now :
+ h2->hs_wd->wd_sendtime - h2->hs_wd->wd_boottime)
+ -
+ (down(h1) ? h1->hs_wd->wd_recvtime - now :
+ h1->hs_wd->wd_sendtime - h1->hs_wd->wd_boottime)));
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/rwho.c b/usr/src/cmd/cmd-inet/usr.bin/rwho.c
new file mode 100644
index 0000000000..5df84f61bd
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/rwho.c
@@ -0,0 +1,192 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/param.h>
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <protocols/rwhod.h>
+
+static DIR *dirp;
+
+static struct whod wd;
+#define NUSERS 1000
+static struct myutmp {
+ char myhost[32];
+ int myidle;
+ struct outmp myutmp;
+} myutmp[NUSERS];
+static int utmpcmp(const void *, const void *);
+static int nusers;
+
+#define WHDRSIZE (sizeof (wd) - sizeof (wd.wd_we))
+#define RWHODIR "/var/spool/rwho"
+/*
+ * this macro should be shared with ruptime.
+ */
+#define down(w, now) ((now) - (w)->wd_recvtime > 11 * 60)
+
+static time_t now;
+static int aflg = 0;
+
+int
+main(int argc, char **argv)
+{
+ struct dirent *dp;
+ int cc, width;
+ register struct whod *w = &wd;
+ register struct whoent *we;
+ register struct myutmp *mp;
+ int f, n, i;
+
+ argc--, argv++;
+again:
+ if (argc > 0 && strcmp(argv[0], "-a") == 0) {
+ argc--, argv++;
+ aflg++;
+ goto again;
+ }
+ (void) time(&now);
+ if (chdir(RWHODIR) < 0) {
+ perror(RWHODIR);
+ return (EXIT_FAILURE);
+ }
+ dirp = opendir(".");
+ if (dirp == NULL) {
+ perror(RWHODIR);
+ return (EXIT_FAILURE);
+ }
+ mp = myutmp;
+ while (dp = readdir(dirp)) {
+ if (dp->d_ino == 0)
+ continue;
+ if (strncmp(dp->d_name, "whod.", 5))
+ continue;
+ f = open(dp->d_name, 0);
+ if (f < 0)
+ continue;
+ cc = read(f, (char *)&wd, sizeof (struct whod));
+ if (cc < WHDRSIZE) {
+ (void) close(f);
+ continue;
+ }
+ if (down(w, now)) {
+ (void) close(f);
+ continue;
+ }
+ cc -= WHDRSIZE;
+ we = w->wd_we;
+ for (n = cc / sizeof (struct whoent); n > 0; n--) {
+ if (aflg == 0 && we->we_idle >= 60*60) {
+ we++;
+ continue;
+ }
+ if (nusers >= NUSERS) {
+ (void) printf("too many users\n");
+ return (EXIT_FAILURE);
+ }
+ mp->myutmp = we->we_utmp; mp->myidle = we->we_idle;
+ (void) strncpy(mp->myhost, w->wd_hostname,
+ sizeof (mp->myhost));
+ nusers++; we++; mp++;
+ }
+ (void) close(f);
+ }
+ qsort((char *)myutmp, nusers, sizeof (struct myutmp), utmpcmp);
+ mp = myutmp;
+ width = 0;
+ for (i = 0; i < nusers; i++) {
+ int j = strlen(mp->myhost) + 1 + strlen(mp->myutmp.out_line);
+ if (j > width)
+ width = j;
+ mp++;
+ }
+ mp = myutmp;
+ for (i = 0; i < nusers; i++) {
+ char buf[BUFSIZ];
+ (void) snprintf(buf, BUFSIZ, "%.*s:%.*s",
+ sizeof (mp->myhost), mp->myhost,
+ sizeof (mp->myutmp.out_line), mp->myutmp.out_line);
+ (void) printf("%-8.*s %-*s %.12s",
+ sizeof (mp->myutmp.out_name), mp->myutmp.out_name,
+ width, buf,
+ ctime((time_t *)&mp->myutmp.out_time) + 4);
+ mp->myidle /= 60;
+ if (mp->myidle) {
+ if (aflg) {
+ if (mp->myidle >= 100*60)
+ mp->myidle = 100*60 - 1;
+ if (mp->myidle >= 60)
+ (void) printf(" %2d", mp->myidle / 60);
+ else
+ (void) fputs(" ", stdout);
+ } else
+ (void) printf(" ");
+ (void) printf(":%02d", mp->myidle % 60);
+ }
+ (void) puts("");
+ mp++;
+ }
+ return (EXIT_SUCCESS);
+}
+
+static int
+utmpcmp(const void *p1, const void *p2)
+{
+ const struct myutmp *u1 = p1, *u2 = p2;
+ int rc;
+
+ rc = strncmp(u1->myutmp.out_name, u2->myutmp.out_name,
+ sizeof (u1->myutmp.out_name));
+ if (rc != 0)
+ return (rc);
+ rc = strncmp(u1->myhost, u2->myhost, sizeof (u1->myhost));
+ if (rc != 0)
+ return (rc);
+ return (strncmp(u1->myutmp.out_line, u2->myutmp.out_line,
+ sizeof (u1->myutmp.out_line)));
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/talk/Makefile b/usr/src/cmd/cmd-inet/usr.bin/talk/Makefile
new file mode 100644
index 0000000000..0c96c11a97
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/talk/Makefile
@@ -0,0 +1,76 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+PROG= talk
+OBJS= talk.o get_names.o display.o io.o ctl.o init_disp.o \
+ msgs.o get_addrs.o ctl_transact.o invite.o look_up.o
+SRCS= $(OBJS:%.o=%.c)
+
+include ../../../Makefile.cmd
+
+#
+# Message catalog
+#
+POFILE= talk.po
+#
+
+CFLAGS += $(CCVERBOSE)
+CPPFLAGS += -DSYSV -DSTRNET -DBSD_COMP
+LDLIBS += -lcurses -lsocket -lnsl
+
+.KEEP_STATE:
+
+.PARALLEL: $(OBJS)
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+ $(LINK.c) $(OBJS) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+#
+# message catalog
+#
+_msg: $(POFILE)
+
+$(POFILE): $(SRCS)
+ $(RM) talk.po
+ $(COMPILE.cpp) $(SRCS) > $(POFILE).i
+ $(XGETTEXT) $(XGETFLAGS) $(POFILE).i
+ sed "/^domain/d" messages.po > $@
+ $(RM) messages.po $(POFILE).i
+
+#
+install: all $(ROOTPROG)
+
+clean:
+ $(RM) $(OBJS)
+
+lint: lint_SRCS
+
+include ../../../Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/usr.bin/talk/ctl.c b/usr/src/cmd/cmd-inet/usr.bin/talk/ctl.c
new file mode 100644
index 0000000000..0aa6dd53db
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/talk/ctl.c
@@ -0,0 +1,135 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1997 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * This file handles haggling with the various talk daemons to
+ * get a socket to talk to. sockt is opened and connected in
+ * the progress
+ */
+
+#include "talk_ctl.h"
+#include <libintl.h>
+
+struct sockaddr_in daemon_addr = { AF_INET };
+struct sockaddr_in ctl_addr = { AF_INET };
+struct sockaddr_in my_addr = { AF_INET };
+
+/* inet addresses of the two machines */
+struct in_addr my_machine_addr;
+struct in_addr rem_machine_addr;
+
+u_short daemon_port; /* port number of the talk daemon */
+
+int ctl_sockt;
+int sockt;
+int invitation_waiting = 0;
+
+CTL_MSG msg;
+
+void
+open_sockt()
+{
+ socklen_t length;
+
+ my_addr.sin_addr = my_machine_addr;
+ my_addr.sin_port = 0;
+
+ sockt = socket(AF_INET, SOCK_STREAM, 0);
+
+ if (sockt <= 0) {
+ p_error(gettext("Bad socket"));
+ }
+
+ if (bind(sockt, (struct sockaddr *)&my_addr, sizeof (my_addr)) != 0) {
+ p_error(gettext("Binding local socket"));
+ }
+
+ length = (socklen_t) sizeof (my_addr);
+
+ if (getsockname(sockt, (struct sockaddr *)&my_addr, &length) == -1) {
+ p_error(gettext("Bad address for socket"));
+ }
+}
+
+ /* open the ctl socket */
+
+void
+open_ctl()
+{
+ socklen_t length;
+
+ ctl_addr.sin_port = 0;
+ ctl_addr.sin_addr = my_machine_addr;
+
+ ctl_sockt = socket(AF_INET, SOCK_DGRAM, 0);
+
+ if (ctl_sockt <= 0) {
+ p_error(gettext("Bad socket"));
+ }
+
+ if (bind(ctl_sockt, (struct sockaddr *)&ctl_addr, sizeof (ctl_addr))
+ != 0) {
+ p_error(gettext("Couldn't bind to control socket"));
+ }
+
+ length = (socklen_t) sizeof (ctl_addr);
+ if (getsockname(ctl_sockt, (struct sockaddr *)&ctl_addr, &length)
+ == -1) {
+ p_error(gettext("Bad address for ctl socket"));
+ }
+}
+
+/* print_addr is a debug print routine */
+
+void
+print_addr(addr)
+struct sockaddr_in addr;
+{
+ int i;
+
+ printf("addr = %x, port = %o, family = %o zero = ",
+ addr.sin_addr, (int)addr.sin_port, addr.sin_family);
+
+ for (i = 0; i < 8; i++) {
+ printf("%o ", (int)addr.sin_zero[i]);
+ }
+ putchar('\n');
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/talk/ctl.h b/usr/src/cmd/cmd-inet/usr.bin/talk/ctl.h
new file mode 100644
index 0000000000..9020313753
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/talk/ctl.h
@@ -0,0 +1,96 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1990 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/* ctl.h describes the structure that talk and talkd pass back
+ and forth
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+#define NAME_SIZE 9
+#define TTY_SIZE 16
+#define HOST_NAME_LENGTH 256
+
+#define MAX_LIFE 60 /* maximum time an invitation is saved by the
+ talk daemons */
+#define RING_WAIT 30 /* time to wait before refreshing invitation
+ should be 10's of seconds less than MAX_LIFE */
+
+ /* the values for type */
+
+#define LEAVE_INVITE 0
+#define LOOK_UP 1
+#define DELETE 2
+#define ANNOUNCE 3
+
+ /* the values for answer */
+
+#define SUCCESS 0
+#define NOT_HERE 1
+#define FAILED 2
+#define MACHINE_UNKNOWN 3
+#define PERMISSION_DENIED 4
+#define UNKNOWN_REQUEST 5
+
+typedef struct ctl_response CTL_RESPONSE;
+
+struct ctl_response {
+ char type;
+ char answer;
+ int id_num;
+ struct sockaddr_in addr;
+};
+
+typedef struct ctl_msg CTL_MSG;
+
+struct ctl_msg {
+ char type;
+ char l_name[NAME_SIZE];
+ char r_name[NAME_SIZE];
+ int id_num;
+ pid_t pid;
+ char r_tty[TTY_SIZE];
+ struct sockaddr_in addr;
+ struct sockaddr_in ctl_addr;
+};
diff --git a/usr/src/cmd/cmd-inet/usr.bin/talk/ctl_transact.c b/usr/src/cmd/cmd-inet/usr.bin/talk/ctl_transact.c
new file mode 100644
index 0000000000..f465c89ed2
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/talk/ctl_transact.c
@@ -0,0 +1,145 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1997 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "talk_ctl.h"
+#include <sys/time.h>
+#include <libintl.h>
+
+#define CTL_WAIT 2
+ /* the amount of time to wait for a response, in seconds */
+
+
+/*
+ * SOCKDGRAM is unreliable, so we must repeat messages if we have
+ * not recieved an acknowledgement within a reasonable amount of time
+ */
+
+void
+ctl_transact(target, msg, type, response)
+struct in_addr target;
+CTL_MSG msg;
+int type;
+CTL_RESPONSE *response;
+{
+ struct sockaddr junk;
+ int read_mask;
+ int ctl_mask;
+ int nready;
+ int cc;
+ socklen_t junk_size;
+ struct timeval wait;
+
+ wait.tv_sec = CTL_WAIT;
+ wait.tv_usec = 0;
+
+ msg.type = type;
+
+ daemon_addr.sin_addr = target;
+ daemon_addr.sin_port = daemon_port;
+
+ ctl_mask = 1 << ctl_sockt;
+
+ /*
+ * keep sending the message until a response of the right
+ * type is obtained
+ */
+
+ do {
+ /* keep sending the message until a response is obtained */
+
+ do {
+ cc = sendto(ctl_sockt,
+ (char *)&msg, sizeof (CTL_MSG), 0,
+ (struct sockaddr *)&daemon_addr, sizeof (daemon_addr));
+
+ if (cc != sizeof (CTL_MSG)) {
+ if (errno == EINTR) {
+ /* we are returning from an interupt */
+ continue;
+ } else {
+ p_error(
+ gettext("Error on write to talk daemon"));
+ }
+ }
+
+ read_mask = ctl_mask;
+
+ while ((nready = select(32, (fd_set *)&read_mask,
+ 0, 0, &wait)) < 0) {
+ if (errno == EINTR) {
+ /* we are returning from an interupt */
+ continue;
+ } else {
+ p_error(
+ gettext("Error on waiting for response from daemon"));
+ }
+ }
+ } while (nready == 0);
+
+ /*
+ * keep reading while there are queued messages
+ * (this is not necessary, it just saves extra
+ * request/acknowledgements being sent)
+ */
+
+ do {
+
+ junk_size = (socklen_t)sizeof (junk);
+ cc = recvfrom(ctl_sockt, (char *)response,
+ sizeof (CTL_RESPONSE), 0, &junk, &junk_size);
+ if (cc < 0) {
+ if (errno == EINTR) {
+ continue;
+ }
+ p_error(gettext("Error on read from talk daemon"));
+ }
+
+ read_mask = ctl_mask;
+
+ /* an immediate poll */
+
+ timerclear(&wait);
+ nready = select(32, (fd_set *)&read_mask, 0, 0, &wait);
+
+ } while (nready > 0 && response->type != type);
+
+ } while (response->type != type);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/talk/display.c b/usr/src/cmd/cmd-inet/usr.bin/talk/display.c
new file mode 100644
index 0000000000..bc85e1918a
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/talk/display.c
@@ -0,0 +1,279 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1994 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * The window 'manager', initializes curses and handles the actual
+ * displaying of text
+ */
+
+#include "talk.h"
+#include <ctype.h>
+
+static int readwin(WINDOW *, int, int);
+static void xscroll(register xwin_t *, int);
+
+xwin_t my_win;
+xwin_t rem_win;
+WINDOW *line_win;
+
+int curses_initialized = 0;
+
+/*
+ * max HAS to be a function, it is called with
+ * a argument of the form --foo at least once.
+ */
+
+static int
+max(a, b)
+int a, b;
+{
+ if (a > b) {
+ return (a);
+ } else {
+ return (b);
+ }
+}
+
+/*
+ * Display some text on somebody's window, processing some control
+ * characters while we are at it.
+ */
+
+int
+display(win, text, size)
+register xwin_t *win;
+register char *text;
+int size;
+{
+ register int i;
+ int mb_cur_max = MB_CUR_MAX;
+
+ for (i = 0; i < size; i++) {
+ int itext;
+
+ if (*text == '\n'|| *text == '\r') {
+ xscroll(win, 0);
+ text++;
+ continue;
+ }
+
+ /* erase character */
+
+ if (*text == win->cerase) {
+ wmove(win->x_win, win->x_line, max(--win->x_col, 0));
+ getyx(win->x_win, win->x_line, win->x_col);
+ waddch(win->x_win, ' ');
+ wmove(win->x_win, win->x_line, win->x_col);
+ getyx(win->x_win, win->x_line, win->x_col);
+ text++;
+ continue;
+ }
+ /*
+ * On word erase search backwards until we find
+ * the beginning of a word or the beginning of
+ * the line.
+ */
+ if (*text == win->werase) {
+ int endcol, xcol, i, c;
+
+ endcol = win->x_col;
+ xcol = endcol - 1;
+ while (xcol >= 0) {
+ c = readwin(win->x_win, win->x_line, xcol);
+ if (c != ' ')
+ break;
+ xcol--;
+ }
+ while (xcol >= 0) {
+ c = readwin(win->x_win, win->x_line, xcol);
+ if (c == ' ')
+ break;
+ xcol--;
+ }
+ wmove(win->x_win, win->x_line, xcol + 1);
+ for (i = xcol + 1; i < endcol; i++)
+ waddch(win->x_win, ' ');
+ wmove(win->x_win, win->x_line, xcol + 1);
+ getyx(win->x_win, win->x_line, win->x_col);
+ continue;
+ }
+ /* line kill */
+ if (*text == win->kill) {
+ wmove(win->x_win, win->x_line, 0);
+ wclrtoeol(win->x_win);
+ getyx(win->x_win, win->x_line, win->x_col);
+ text++;
+ continue;
+ }
+ if (*text == '\f') {
+ if (win == &my_win)
+ wrefresh(curscr);
+ text++;
+ continue;
+ }
+ /* EOF character */
+ if (*text == '\004') {
+ quit();
+ }
+
+ /* typing alert character will alert recipient's terminal */
+
+ if (*text == '\007') {
+ beep();
+ continue;
+ }
+
+ /* check for wrap around */
+ if (win->x_col == COLS-1) {
+ xscroll(win, 0);
+ }
+
+ /*
+ * Handle the multibyte case
+ * We print '?' for nonprintable widechars.
+ */
+
+ if (mb_cur_max > 1 && mblen(text, mb_cur_max) > 1) {
+ wchar_t wc;
+ int len;
+
+ len = mbtowc(&wc, text, mb_cur_max);
+
+ if (iswprint(wc) || iswspace(wc)) {
+ /* its printable, put out the bytes */
+ do {
+ if (win->x_col == COLS-1) /* wraparound */
+ xscroll(win, 0);
+ waddch(win->x_win, *text++);
+ getyx(win->x_win, win->x_line, win->x_col);
+ } while (--len > 0);
+ continue;
+ }
+ /*
+ * otherwise, punt and print a question mark.
+ */
+ text += len;
+ waddch(win->x_win, '?');
+ getyx(win->x_win, win->x_line, win->x_col);
+ continue;
+ }
+
+ itext = (unsigned int) *text;
+ if (isprint(itext) || *text == ' ' || *text == '\t' ||
+ *text == '\013' || *text == '\007' /* bell */) {
+ waddch(win->x_win, *text);
+ } else {
+
+ if (!isascii(*text)) {
+ /* check for wrap around */
+ if (win->x_col == COLS-3) {
+ xscroll(win, 0);
+ }
+ waddch(win->x_win, 'M');
+ waddch(win->x_win, '-');
+ *text = toascii(*text);
+ }
+ if (iscntrl(*text)) {
+
+ /* check for wrap around */
+ getyx(win->x_win, win->x_line, win->x_col);
+ if (win->x_col == COLS-2) {
+ xscroll(win, 0);
+ }
+
+ waddch(win->x_win, '^');
+ waddch(win->x_win, *text + 0100);
+ }
+ else
+ waddch(win->x_win, *text);
+ }
+
+ getyx(win->x_win, win->x_line, win->x_col);
+ text++;
+
+ } /* for loop */
+ wrefresh(win->x_win);
+ return (0);
+}
+
+/*
+ * Read the character at the indicated position in win
+ */
+
+static int
+readwin(win, line, col)
+WINDOW *win;
+int line, col;
+{
+int oldline, oldcol;
+register int c;
+
+ getyx(win, oldline, oldcol);
+ wmove(win, line, col);
+ c = winch(win);
+ wmove(win, oldline, oldcol);
+ return (c);
+}
+
+/*
+ * Scroll a window, blanking out the line following the current line
+ * so that the current position is obvious
+ */
+
+static void
+xscroll(win, flag)
+register xwin_t *win;
+int flag;
+{
+ if (flag == -1) {
+ wmove(win->x_win, 0, 0);
+ win->x_line = 0;
+ win->x_col = 0;
+ return;
+ }
+ win->x_line = (win->x_line + 1) % win->x_nlines;
+ win->x_col = 0;
+ wmove(win->x_win, win->x_line, win->x_col);
+ wclrtoeol(win->x_win);
+ wmove(win->x_win, (win->x_line + 1) % win->x_nlines, win->x_col);
+ wclrtoeol(win->x_win);
+ wmove(win->x_win, win->x_line, win->x_col);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/talk/get_addrs.c b/usr/src/cmd/cmd-inet/usr.bin/talk/get_addrs.c
new file mode 100644
index 0000000000..bef638be71
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/talk/get_addrs.c
@@ -0,0 +1,133 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1997 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "talk_ctl.h"
+#include <locale.h>
+
+#ifdef SYSV
+#define bcmp(a, b, c) memcmp((a), (b), (c))
+#define bcopy(a, b, c) memcpy((b), (a), (c))
+#endif /* SYSV */
+
+struct hostent *gethostbyname();
+struct servent *getservbyname();
+
+void
+get_addrs(my_machine_name, rem_machine_name)
+char *my_machine_name;
+char *rem_machine_name;
+{
+ struct hostent *hp;
+ struct servent *sp;
+
+ msg.pid = getpid();
+
+ /* look up the address of the local host */
+
+ hp = gethostbyname(my_machine_name);
+
+ if (hp == (struct hostent *) 0) {
+ fprintf(stderr,
+ gettext("This machine doesn't exist. Boy, am I confused!\n"));
+ exit(1);
+ }
+
+ if (hp->h_addrtype != AF_INET) {
+ fprintf(stderr,
+ gettext("Protocol mix up with local machine address\n"));
+ exit(1);
+ }
+
+ bcopy(hp->h_addr, (char *)&my_machine_addr, hp->h_length);
+
+ /* if on the same machine, then simply copy */
+
+ if (bcmp((char *)&rem_machine_name, (char *)&my_machine_name,
+ sizeof (rem_machine_name)) == 0) {
+ bcopy((char *)&my_machine_addr, (char *)&rem_machine_addr,
+ sizeof (rem_machine_name));
+ } else {
+
+ if ((rem_machine_addr.s_addr =
+ (unsigned long)inet_addr(rem_machine_name)) == -1) {
+
+ /* look up the address of the recipient's machine */
+
+ hp = gethostbyname(rem_machine_name);
+
+ if (hp == (struct hostent *) 0) {
+ fprintf(stderr,
+ gettext("%s is an unknown host\n"), rem_machine_name);
+ exit(1);
+ }
+
+ if (hp->h_addrtype != AF_INET) {
+ fprintf(stderr,
+ gettext("Protocol mix up with remote machine address\n"));
+ exit(1);
+ }
+
+ bcopy(hp->h_addr, (char *) &rem_machine_addr, hp->h_length);
+ }
+ }
+
+
+ /* find the daemon portal */
+
+#ifdef NTALK
+ sp = getservbyname("ntalk", "udp");
+#else
+ sp = getservbyname("talk", "udp");
+#endif
+
+ if (strcmp(sp->s_proto, "udp") != 0) {
+ fprintf(stderr, gettext("Protocol mix up with talk daemon\n"));
+ exit(1);
+ }
+
+ if (sp == 0) {
+ p_error(
+ gettext("This machine doesn't support a tcp talk daemon"));
+ exit(1);
+ }
+
+ daemon_port = sp->s_port;
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/talk/get_names.c b/usr/src/cmd/cmd-inet/usr.bin/talk/get_names.c
new file mode 100644
index 0000000000..3370976811
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/talk/get_names.c
@@ -0,0 +1,160 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1997 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "talk.h"
+#include "ctl.h"
+#include <locale.h>
+#include <pwd.h>
+#include <sys/systeminfo.h>
+
+char *getlogin(), *ttyname(int);
+
+extern CTL_MSG msg;
+
+/*
+ * Determine the local and remote user, tty, and machines
+ */
+
+struct hostent *gethostbyname();
+
+void
+get_names(argc, argv)
+int argc;
+char *argv[];
+{
+ char hostname[HOST_NAME_LENGTH + 1];
+ char *rem_name;
+ char *my_name;
+ char *my_machine_name;
+ char *rem_machine_name;
+ char *my_tty;
+ char *rem_tty;
+ char *ptr;
+ int name_length;
+
+ if (argc < 2) {
+ fprintf(stderr,
+ "Usage: talk %s\n", gettext("address [terminal]"));
+ exit(1);
+ }
+ if (!isatty(0)) {
+ fprintf(stderr,
+ gettext("Standard input must be a tty, not a pipe or a file\n"));
+ exit(1);
+ }
+
+ if (!isatty(1)) {
+ fprintf(stderr,
+ gettext("Standard output must be a tty, not a pipe or a file\n"));
+ exit(1);
+ }
+
+ if ((my_name = getlogin()) == NULL) {
+ struct passwd *pass = getpwuid(getuid());
+ if (pass != NULL)
+ my_name = pass->pw_name;
+ }
+ if (my_name == NULL) {
+ fprintf(stderr,
+ gettext("Who are you? You have no entry in /etc/utmp! Aborting..\n"));
+ exit(1);
+ }
+
+ name_length = HOST_NAME_LENGTH;
+ (void) sysinfo(SI_HOSTNAME, hostname, name_length);
+ my_machine_name = hostname;
+
+ my_tty = strrchr(ttyname(0), '/') + 1;
+
+ /*
+ * check for, and strip out, the machine name of the target
+ */
+
+ for (ptr = argv[1]; *ptr != '\0' &&
+ *ptr != '@' &&
+ *ptr != ':' &&
+ *ptr != '!' &&
+ *ptr != '.'; ptr++) {
+ }
+
+ if (*ptr == '\0') {
+
+ /* this is a local to local talk */
+
+ rem_name = argv[1];
+ rem_machine_name = my_machine_name;
+
+ } else {
+
+ if (*ptr == '@') {
+ /* user@host */
+ rem_name = argv[1];
+ rem_machine_name = ptr + 1;
+ } else {
+ /* host.user or host!user or host:user */
+ rem_name = ptr + 1;
+ rem_machine_name = argv[1];
+ }
+ *ptr = '\0';
+ }
+
+
+ if (argc > 2) {
+ rem_tty = argv[2]; /* tty name is arg 2 */
+ } else {
+ rem_tty = "";
+ }
+
+ get_addrs(my_machine_name, rem_machine_name);
+
+ /* Load these useful values into the standard message header */
+
+ msg.id_num = 0;
+
+ strncpy(msg.l_name, my_name, NAME_SIZE);
+ msg.l_name[NAME_SIZE - 1] = '\0';
+
+ strncpy(msg.r_name, rem_name, NAME_SIZE);
+ msg.r_name[NAME_SIZE - 1] = '\0';
+
+ strncpy(msg.r_tty, rem_tty, TTY_SIZE);
+ msg.r_tty[TTY_SIZE - 1] = '\0';
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/talk/init_disp.c b/usr/src/cmd/cmd-inet/usr.bin/talk/init_disp.c
new file mode 100644
index 0000000000..a756651191
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/talk/init_disp.c
@@ -0,0 +1,186 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1994 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * init_disp contains the initialization code for the display package,
+ * as well as the signal handling routines
+ */
+
+#include "talk.h"
+#include <signal.h>
+#include <libintl.h>
+
+#ifdef SYSV
+#define signal(s, f) sigset(s, f)
+#endif /* SYSV */
+
+static void sig_sent();
+
+/*
+ * set up curses, catch the appropriate signals, and build the
+ * various windows
+ */
+
+void
+init_display()
+{
+ initscr();
+ curses_initialized = 1;
+
+ clear();
+ refresh();
+
+ noecho();
+ crmode();
+
+ signal(SIGINT, sig_sent);
+ signal(SIGPIPE, sig_sent);
+
+ /* curses takes care of ^Z */
+
+ my_win.x_nlines = LINES / 2;
+ my_win.x_ncols = COLS;
+ my_win.x_win = newwin(my_win.x_nlines, my_win.x_ncols, 0, 0);
+ scrollok(my_win.x_win, FALSE);
+ wclear(my_win.x_win);
+
+ rem_win.x_nlines = LINES / 2 - 1;
+ rem_win.x_ncols = COLS;
+ rem_win.x_win = newwin(rem_win.x_nlines, rem_win.x_ncols,
+ my_win.x_nlines+1, 0);
+ scrollok(rem_win.x_win, FALSE);
+ wclear(rem_win.x_win);
+
+ line_win = newwin(1, COLS, my_win.x_nlines, 0);
+ box(line_win, '-', '-');
+ wrefresh(line_win);
+
+ /* let them know we are working on it */
+
+ current_state = gettext("No connection yet");
+}
+
+ /*
+ * trade edit characters with the other talk. By agreement
+ * the first three characters each talk transmits after
+ * connection are the three edit characters
+ */
+
+void
+set_edit_chars()
+{
+ char buf[3];
+ int cc;
+#ifdef SYSV
+ struct termios tty;
+ ioctl(0, TCGETS, (struct termios *)&tty);
+
+ buf[0] = my_win.cerase = tty.c_cc[VERASE];
+ /* for SVID should be VERSE */
+ buf[1] = my_win.kill = tty.c_cc[VKILL];
+ buf[2] = my_win.werase = tty.c_cc[VWERASE];
+ /* for SVID should be VWERSE */
+#else /* ! SYSV */
+ struct sgttyb tty;
+ struct ltchars ltc;
+
+ gtty(0, &tty);
+
+ ioctl(0, TIOCGLTC, (struct sgttyb *)&ltc);
+
+ my_win.cerase = tty.sg_erase;
+ my_win.kill = tty.sg_kill;
+
+ if (ltc.t_werasc == (char)-1) {
+ my_win.werase = '\027'; /* control W */
+ } else {
+ my_win.werase = ltc.t_werasc;
+ }
+
+ buf[0] = my_win.cerase;
+ buf[1] = my_win.kill;
+ buf[2] = my_win.werase;
+#endif /* SYSV */
+
+ cc = write(sockt, buf, sizeof (buf));
+
+ if (cc != sizeof (buf)) {
+ p_error(gettext("Lost the connection"));
+ }
+
+ cc = read(sockt, buf, sizeof (buf));
+
+ if (cc != sizeof (buf)) {
+ p_error(gettext("Lost the connection"));
+ }
+
+ rem_win.cerase = buf[0];
+ rem_win.kill = buf[1];
+ rem_win.werase = buf[2];
+}
+
+static void
+sig_sent()
+{
+ message(gettext("Connection closing. Exiting"));
+ quit();
+}
+
+/*
+ * All done talking...hang up the phone and reset terminal thingy's
+ */
+
+void
+quit()
+{
+ if (curses_initialized) {
+ wmove(rem_win.x_win, rem_win.x_nlines-1, 0);
+ wclrtoeol(rem_win.x_win);
+ wrefresh(rem_win.x_win);
+ endwin();
+ }
+
+ if (invitation_waiting) {
+ send_delete();
+ }
+
+ exit(0);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/talk/invite.c b/usr/src/cmd/cmd-inet/usr.bin/talk/invite.c
new file mode 100644
index 0000000000..7b06de4b73
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/talk/invite.c
@@ -0,0 +1,221 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1994 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "talk_ctl.h"
+#include <sys/time.h>
+#include <signal.h>
+#include <setjmp.h>
+#include <libintl.h>
+
+#ifdef SYSV
+#define signal(s, f) sigset(s, f)
+#endif /* SYSV */
+
+ /*
+ * there wasn't an invitation waiting, so send a request containing
+ * our sockt address to the remote talk daemon so it can invite
+ * him
+ */
+
+static int local_id, remote_id;
+
+ /*
+ * the msg.id's for the invitations
+ * on the local and remote machines.
+ * These are used to delete the invitations.
+ */
+
+static jmp_buf invitebuf;
+
+static void re_invite();
+static void announce_invite();
+
+void
+invite_remote()
+{
+ int new_sockt;
+ struct itimerval itimer;
+ CTL_RESPONSE response;
+
+ itimer.it_value.tv_sec = RING_WAIT;
+ itimer.it_value.tv_usec = 0;
+ itimer.it_interval = itimer.it_value;
+
+ if (listen(sockt, 5) != 0) {
+ p_error(gettext("Error on attempt to listen for caller"));
+ }
+
+ msg.addr = my_addr;
+ msg.id_num = -1; /* an impossible id_num */
+
+ invitation_waiting = 1;
+
+ announce_invite();
+
+ /*
+ * shut off the automatic messages for a while,
+ * so we can use the interupt timer to resend the invitation
+ */
+
+ end_msgs();
+ setitimer(ITIMER_REAL, &itimer, (struct itimerval *)0);
+ message(gettext("Waiting for your party to respond"));
+ signal(SIGALRM, re_invite);
+ (void) setjmp(invitebuf);
+
+ while ((new_sockt = accept(sockt, 0, 0)) < 0) {
+ if (errno != EINTR) {
+ p_error(gettext("Unable to connect with your party"));
+ } else {
+ /* we just returned from a interupt, keep trying */
+ continue;
+ }
+ }
+
+ close(sockt);
+ sockt = new_sockt;
+
+ /*
+ * have the daemons delete the invitations now that we have connected.
+ */
+
+ current_state = strdup(gettext("Waiting for your party to respond"));
+ start_msgs();
+
+ msg.id_num = local_id;
+ ctl_transact(my_machine_addr, msg, DELETE, &response);
+ msg.id_num = remote_id;
+ ctl_transact(rem_machine_addr, msg, DELETE, &response);
+ invitation_waiting = 0;
+}
+
+ /* routine called on interupt to re-invite the callee */
+
+static void
+re_invite()
+{
+ message(gettext("Ringing your party again"));
+ current_line++;
+ /* force a re-announce */
+ msg.id_num = remote_id + 1;
+ announce_invite();
+ longjmp(invitebuf, 1);
+}
+
+ /* transmit the invitation and process the response */
+
+static void
+announce_invite()
+{
+ CTL_RESPONSE response;
+
+ current_state =
+ gettext("Trying to connect to your party's talk daemon");
+
+ ctl_transact(rem_machine_addr, msg, ANNOUNCE, &response);
+ remote_id = response.id_num;
+
+ if (response.answer != SUCCESS) {
+
+ switch (response.answer) {
+
+ case NOT_HERE :
+ message(gettext("Your party is not logged on"));
+ break;
+
+ case MACHINE_UNKNOWN :
+ message(
+ gettext("Target machine does not recognize us"));
+ break;
+
+ case UNKNOWN_REQUEST :
+ message(
+ gettext("Target machine can not handle remote talk"));
+ break;
+
+ case FAILED :
+ message(
+ gettext("Target machine is too confused to talk to us"));
+ break;
+
+ case PERMISSION_DENIED :
+ message(gettext("Your party is refusing messages"));
+ break;
+ }
+
+ quit();
+ }
+
+ /* leave the actual invitation on my talk daemon */
+
+ ctl_transact(my_machine_addr, msg, LEAVE_INVITE, &response);
+ local_id = response.id_num;
+}
+
+void
+send_delete()
+{
+
+ /* tell the daemon to remove your invitation */
+
+ msg.type = DELETE;
+
+ /*
+ * this is just a extra clean up, so just send it
+ * and don't wait for an answer
+ */
+
+ msg.id_num = remote_id;
+ daemon_addr.sin_addr = rem_machine_addr;
+ if (sendto(ctl_sockt, (char *)&msg, sizeof (CTL_MSG), 0,
+ (struct sockaddr *)&daemon_addr,
+ sizeof (daemon_addr)) != sizeof (CTL_MSG)) {
+ perror(gettext("send_delete remote"));
+ }
+
+ msg.id_num = local_id;
+ daemon_addr.sin_addr = my_machine_addr;
+ if (sendto(ctl_sockt, (char *)&msg, sizeof (CTL_MSG), 0,
+ (struct sockaddr *)&daemon_addr,
+ sizeof (daemon_addr)) != sizeof (CTL_MSG)) {
+ perror(gettext("send_delete local"));
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/talk/io.c b/usr/src/cmd/cmd-inet/usr.bin/talk/io.c
new file mode 100644
index 0000000000..8650830971
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/talk/io.c
@@ -0,0 +1,176 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1994 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * this file contains the I/O handling and the exchange of
+ * edit characters. This connection itself is established in ctl.c
+ */
+
+#include "talk.h"
+#include <stdio.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <sys/filio.h>
+#include <libintl.h>
+
+#define A_LONG_TIME 10000000
+#define STDIN_MASK (1<<fileno(stdin)) /* the bit mask for standard input */
+extern int errno;
+extern int sys_nerr;
+
+
+/*
+ * The routine to do the actual talking
+ */
+
+void
+talk()
+{
+ register int read_template, sockt_mask;
+ int read_set, nb;
+ char buf[BUFSIZ];
+ struct timeval wait;
+
+ message(gettext("Connection established"));
+ beep(); beep(); beep();
+ current_line = 0;
+
+ sockt_mask = (1<<sockt);
+
+ /*
+ * wait on both the other process (sockt_mask) and
+ * standard input ( STDIN_MASK )
+ */
+
+ read_template = sockt_mask | STDIN_MASK;
+
+ forever {
+
+ read_set = read_template;
+
+ wait.tv_sec = A_LONG_TIME;
+ wait.tv_usec = 0;
+
+ nb = select(32, (fd_set *)&read_set, 0, 0, &wait);
+
+ if (nb <= 0) {
+
+ /* We may be returning from an interrupt handler */
+
+ if (errno == EINTR) {
+ read_set = read_template;
+ continue;
+ } else {
+ /* panic, we don't know what happened */
+ p_error(
+ gettext("Unexpected error from select"));
+ quit();
+ }
+ }
+
+ if (read_set & sockt_mask) {
+
+ /* There is data on sockt */
+ nb = read(sockt, buf, sizeof (buf));
+
+ if (nb <= 0) {
+ message(gettext("Connection closed. Exiting"));
+ pause(); /* wait for Ctrl-C */
+ quit();
+ } else {
+ display(&rem_win, buf, nb);
+ }
+ }
+
+ if (read_set & STDIN_MASK) {
+
+ /*
+ * we can't make the tty non_blocking, because
+ * curses's output routines would screw up
+ */
+
+ ioctl(0, FIONREAD, (struct sgttyb *)&nb);
+ nb = read(0, buf, nb);
+ display(&my_win, buf, nb);
+ write(sockt, buf, nb);
+
+ /* We might lose data here because sockt is non-blocking */
+
+ }
+ }
+}
+
+
+ /*
+ * p_error prints the system error message on the standard location
+ * on the screen and then exits. (i.e. a curses version of perror)
+ */
+
+void
+p_error(string)
+char *string;
+{
+ char *sys;
+
+ if (errno < sys_nerr) {
+ sys = strerror(errno);
+ } else {
+ sys = gettext("Unknown error");
+ }
+
+ wmove(my_win.x_win, current_line%my_win.x_nlines, 0);
+ wprintw(my_win.x_win, "[%s : %s (%d)]\n", string, sys, errno);
+ wrefresh(my_win.x_win);
+ move(LINES-1, 0);
+ refresh();
+ quit();
+}
+
+ /* display string in the standard location */
+
+void
+message(string)
+char *string;
+{
+ wmove(my_win.x_win, current_line%my_win.x_nlines, 0);
+ wprintw(my_win.x_win, "[%s]\n", string);
+ wrefresh(my_win.x_win);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/talk/look_up.c b/usr/src/cmd/cmd-inet/usr.bin/talk/look_up.c
new file mode 100644
index 0000000000..881e0d0c1a
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/talk/look_up.c
@@ -0,0 +1,199 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2000 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "talk_ctl.h"
+#include <libintl.h>
+#include <sys/isa_defs.h>
+
+#ifdef SYSV
+#define bcopy(a, b, c) memcpy((b), (a), (c))
+#endif /* SYSV */
+
+static int look_for_invite(CTL_RESPONSE *);
+static CTL_RESPONSE swapresponse();
+
+
+ /* see if the local daemon has a invitation for us */
+
+int
+check_local()
+{
+ CTL_RESPONSE response;
+
+ /* the rest of msg was set up in get_names */
+
+ msg.ctl_addr = ctl_addr;
+
+ if (!look_for_invite(&response)) {
+
+ /* we must be initiating a talk */
+
+ return (0);
+ }
+
+ /*
+ * there was an invitation waiting for us,
+ * so connect with the other (hopefully waiting) party
+ */
+
+ current_state = gettext("Waiting to connect with caller");
+
+ response = swapresponse(response);
+ while (connect(sockt, (struct sockaddr *)&response.addr,
+ sizeof (response.addr)) != 0) {
+ if (errno == ECONNREFUSED) {
+
+ /*
+ * the caller gave up, but the invitation somehow
+ * was not cleared. Clear it and initiate an
+ * invitation. (We know there are no newer invitations,
+ * the talkd works LIFO.)
+ */
+
+ ctl_transact(rem_machine_addr, msg, DELETE, &response);
+ close(sockt);
+ open_sockt();
+ return (0);
+ } else if (errno == EINTR) {
+
+ /* we have returned from an interupt handler */
+ continue;
+ } else {
+ p_error(gettext("Unable to connect with initiator"));
+ }
+ }
+
+ return (1);
+}
+
+ /* look for an invitation on 'machine' */
+
+static int
+look_for_invite(response)
+CTL_RESPONSE *response;
+{
+ current_state = gettext("Checking for invitation on caller's machine");
+
+ ctl_transact(rem_machine_addr, msg, LOOK_UP, response);
+
+ /*
+ * switch is for later options, such as multiple invitations
+ */
+
+ switch (response->answer) {
+
+ case SUCCESS:
+
+ msg.id_num = response->id_num;
+ return (1);
+
+ default :
+ /* there wasn't an invitation waiting for us */
+ return (0);
+ }
+}
+
+/*
+ * heuristic to detect if need to reshuffle CTL_RESPONSE structure
+ */
+
+#if defined(_LITTLE_ENDIAN)
+struct ctl_response_runrise {
+ char type;
+ char answer;
+ short junk;
+ int id_num;
+ struct sockaddr_in addr;
+};
+
+static CTL_RESPONSE
+swapresponse(rsp)
+ CTL_RESPONSE rsp;
+{
+ struct ctl_response_runrise swaprsp;
+
+ if (rsp.addr.sin_family != AF_INET) {
+ bcopy(&rsp, &swaprsp, sizeof (CTL_RESPONSE));
+ if (swaprsp.addr.sin_family == AF_INET) {
+ rsp.addr = swaprsp.addr;
+ rsp.type = swaprsp.type;
+ rsp.answer = swaprsp.answer;
+ rsp.id_num = swaprsp.id_num;
+ }
+ }
+ return (rsp);
+}
+#endif
+
+#if defined(_BIG_ENDIAN)
+struct ctl_response_sun3 {
+ char type;
+ char answer;
+ unsigned short id_num2;
+ unsigned short id_num1;
+ short sin_family;
+ short sin_port;
+ short sin_addr2;
+ short sin_addr1;
+};
+
+static CTL_RESPONSE
+swapresponse(rsp)
+ CTL_RESPONSE rsp;
+{
+ struct ctl_response_sun3 swaprsp;
+
+ if (rsp.addr.sin_family != AF_INET) {
+ bcopy(&rsp, &swaprsp, sizeof (struct ctl_response_sun3));
+ if (swaprsp.sin_family == AF_INET) {
+ rsp.type = swaprsp.type;
+ rsp.answer = swaprsp.answer;
+ rsp.id_num = swaprsp.id_num1
+ | (swaprsp.id_num2 << 16);
+ rsp.addr.sin_family = swaprsp.sin_family;
+ rsp.addr.sin_port = swaprsp.sin_port;
+ rsp.addr.sin_addr.s_addr =
+ (swaprsp.sin_addr2 << 16)| swaprsp.sin_addr1;
+ }
+ }
+ return (rsp);
+}
+#endif
diff --git a/usr/src/cmd/cmd-inet/usr.bin/talk/msgs.c b/usr/src/cmd/cmd-inet/usr.bin/talk/msgs.c
new file mode 100644
index 0000000000..4560f67a49
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/talk/msgs.c
@@ -0,0 +1,88 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1994 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * a package to display what is happening every MSG_INTERVAL seconds
+ * if we are slow connecting.
+ */
+
+#include <signal.h>
+#include <stdio.h>
+#include <sys/time.h>
+#include "talk.h"
+
+#ifdef SYSV
+#define signal(s, f) sigset(s, f)
+#endif /* SYSV */
+
+#define MSG_INTERVAL 4
+#define LONG_TIME 100000
+
+char *current_state;
+int current_line = 0;
+
+static struct itimerval itimer;
+static struct timeval wait = { MSG_INTERVAL, 0};
+
+
+static void
+disp_msg()
+{
+ message(current_state);
+}
+
+void
+start_msgs()
+{
+ message(current_state);
+ signal(SIGALRM, (void (*)())disp_msg);
+ itimer.it_value = itimer.it_interval = wait;
+ setitimer(ITIMER_REAL, &itimer, (struct itimerval *)0);
+}
+
+void
+end_msgs()
+{
+ signal(SIGALRM, SIG_IGN);
+ timerclear(&itimer.it_value);
+ timerclear(&itimer.it_interval);
+ setitimer(ITIMER_REAL, &itimer, (struct itimerval *)0);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/talk/talk.c b/usr/src/cmd/cmd-inet/usr.bin/talk/talk.c
new file mode 100644
index 0000000000..9b2bd8730e
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/talk/talk.c
@@ -0,0 +1,94 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1994 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "talk.h"
+#include <locale.h>
+
+/*
+ * talk: A visual form of write. Using sockets, a two way
+ * connection is set up between the two people talking.
+ * With the aid of curses, the screen is split into two
+ * windows, and each users text is added to the window,
+ * one character at a time...
+ *
+ */
+
+void
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ register c;
+
+ (void) setlocale(LC_ALL, "");
+
+#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
+#define TEXT_DOMAIN "SYS_TEST"
+#endif
+ (void) textdomain(TEXT_DOMAIN);
+ while ((c = getopt(argc, argv, "")) != EOF)
+ switch (c) {
+ case '?':
+ (void) fprintf(stderr,"Usage: talk %s\n",
+ gettext("address [terminal]"));
+ exit(2);
+ }
+ get_names(argc, argv);
+
+ init_display();
+
+ open_ctl();
+ open_sockt();
+
+ start_msgs();
+
+ if (!check_local()) {
+ invite_remote();
+ }
+
+ end_msgs();
+
+ set_edit_chars();
+
+ talk();
+ exit(0);
+ /* NOTREACHED */
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/talk/talk.h b/usr/src/cmd/cmd-inet/usr.bin/talk/talk.h
new file mode 100644
index 0000000000..fd49ccf1b3
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/talk/talk.h
@@ -0,0 +1,102 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1998 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <netdb.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <curses.h>
+#include <sys/types.h>
+#include <widec.h>
+#include <wctype.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#define forever for(;;)
+
+#define BUF_SIZE 512
+
+FILE *popen();
+
+extern int sockt;
+extern int curses_initialized;
+extern int invitation_waiting;
+
+extern char *current_state;
+extern int current_line;
+
+typedef struct xwin {
+ WINDOW *x_win;
+ int x_nlines;
+ int x_ncols;
+ int x_line;
+ int x_col;
+ char kill;
+ char cerase;
+ char werase;
+} xwin_t;
+
+extern xwin_t my_win;
+extern xwin_t rem_win;
+extern WINDOW *line_win;
+
+/* function prototypes */
+
+void get_names(int, char * argv[]);
+void init_display();
+void open_ctl();
+void open_sockt();
+void start_msgs();
+void end_msgs();
+int check_local();
+void invite_remote();
+void set_edit_chars();
+void talk();
+void get_addrs(char *, char*);
+void send_delete();
+void ctl_transact();
+void message(char *);
+void p_error(char *);
+void quit();
+int display(xwin_t *, char *, int);
diff --git a/usr/src/cmd/cmd-inet/usr.bin/talk/talk_ctl.h b/usr/src/cmd/cmd-inet/usr.bin/talk/talk_ctl.h
new file mode 100644
index 0000000000..b7b564d9b7
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/talk/talk_ctl.h
@@ -0,0 +1,55 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1990 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "ctl.h"
+#include "talk.h"
+#include <errno.h>
+
+extern int errno;
+
+extern struct sockaddr_in daemon_addr;
+extern struct sockaddr_in ctl_addr;
+extern struct sockaddr_in my_addr;
+extern struct in_addr my_machine_addr;
+extern struct in_addr rem_machine_addr;
+extern u_short daemon_port;
+extern int ctl_sockt;
+extern CTL_MSG msg;
diff --git a/usr/src/cmd/cmd-inet/usr.bin/telnet/Makefile b/usr/src/cmd/cmd-inet/usr.bin/telnet/Makefile
new file mode 100644
index 0000000000..34c599123e
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/telnet/Makefile
@@ -0,0 +1,69 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+PROG= telnet
+OBJS= telnet.o commands.o main.o network.o ring.o \
+ sys_bsd.o terminal.o utilities.o genget.o
+
+include $(SRC)/lib/gss_mechs/mech_krb5/Makefile.mech_krb5
+AUTH_OBJS= auth.o authenc.o kerberos5.o
+AUTH_INCLUDES= -I$(SRC)/uts/common/gssapi/mechs/krb5/include \
+ -I$(SRC)/lib/gss_mechs/mech_krb5/include \
+ -I$(SRC)/lib/gss_mechs/mech_krb5
+
+ENC_OBJS= enc_des.o encrypt.o
+
+OBJS += $(AUTH_OBJS) $(ENC_OBJS)
+
+SRCS= $(OBJS:%.o=%.c)
+
+include ../../../Makefile.cmd
+
+CPPFLAGS += -DKLUDGELINEMODE -DUSE_TERMIO -DENV_HACK -DOLD_ENVIRON
+CPPFLAGS += $(AUTH_INCLUDES)
+LDLIBS += -lsocket -lnsl -ltermlib
+LDLIBS += -lmech_krb5
+LDFLAGS += $(KRUNPATH) -L$(ROOT)$(KLIBDIR_DO) -L$(ROOT)$(KLIBDIR_GL)
+LDFLAGS += $(ZLAZYLOAD)
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+ $(LINK.c) $(OBJS) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+install: all $(ROOTPROG)
+
+clean:
+ $(RM) $(OBJS)
+
+lint: lint_SRCS
+
+include ../../../Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/usr.bin/telnet/auth.c b/usr/src/cmd/cmd-inet/usr.bin/telnet/auth.c
new file mode 100644
index 0000000000..c56ec5cc76
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/telnet/auth.c
@@ -0,0 +1,488 @@
+/*
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * usr/src/cmd/cmd-inet/usr.bin/telnet/auth.c
+ */
+
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* based on @(#)auth.c 8.1 (Berkeley) 6/4/93 */
+
+/*
+ * Copyright (C) 1990 by the Massachusetts Institute of Technology
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in 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. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * 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.
+ */
+
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <signal.h>
+
+#define AUTHTYPE_NAMES /* this is needed for arpa/telnet.h */
+#include <arpa/telnet.h>
+
+#ifdef __STDC__
+#include <stdlib.h>
+#endif
+
+#include <string.h>
+
+#include "externs.h"
+#include "encrypt.h"
+#include "auth.h"
+
+#define typemask(x) ((x) > 0 ? 1 << ((x)-1) : 0)
+
+static int auth_onoff(const char *type, boolean_t on);
+static void auth_gen_printsub(uchar_t *, uint_t, uchar_t *, uint_t);
+
+boolean_t auth_debug_mode = B_FALSE;
+boolean_t auth_has_failed = B_FALSE;
+boolean_t auth_enable_encrypt = B_FALSE;
+
+static char *Name = "Noname";
+static Authenticator *authenticated = NULL;
+static uchar_t _auth_send_data[BUFSIZ];
+static uchar_t *auth_send_data;
+static int auth_send_cnt = 0;
+
+/*
+ * Authentication types supported. Note that these are stored
+ * in priority order, i.e. try the first one first.
+ */
+static Authenticator authenticators[] = {
+ { AUTHTYPE_KERBEROS_V5,
+ AUTH_WHO_CLIENT|AUTH_HOW_MUTUAL|AUTH_ENCRYPT_ON,
+ kerberos5_init,
+ kerberos5_send,
+ kerberos5_reply,
+ kerberos5_status,
+ kerberos5_printsub },
+ { AUTHTYPE_KERBEROS_V5, AUTH_WHO_CLIENT|AUTH_HOW_MUTUAL,
+ kerberos5_init,
+ kerberos5_send,
+ kerberos5_reply,
+ kerberos5_status,
+ kerberos5_printsub },
+ { AUTHTYPE_KERBEROS_V5, AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY,
+ kerberos5_init,
+ kerberos5_send,
+ kerberos5_reply,
+ kerberos5_status,
+ kerberos5_printsub },
+ { 0, },
+};
+
+static Authenticator NoAuth = { 0 };
+
+static uint_t i_support = 0;
+static uint_t i_wont_support = 0;
+
+/*
+ * Traverse the Authenticator array until we find the authentication type
+ * and matching direction we are looking for. Return a pointer into the
+ * Authenticator type array.
+ *
+ * Returns: 0 - type not found (error)
+ * nonzero - pointer to authenticator
+ */
+static Authenticator *
+findauthenticator(int type, int way)
+{
+ Authenticator *ap = authenticators;
+
+ while (ap->type && (ap->type != type || ap->way != way))
+ ++ap;
+ return (ap->type ? ap : NULL);
+}
+
+/*
+ * For each authentication type in the Authenticator array,
+ * call the associated init routine, and update the i_support bitfield.
+ */
+void
+auth_init(const char *name)
+{
+ Authenticator *ap = authenticators;
+
+ Name = name ? strdup(name) : "Noname";
+
+ i_support = 0;
+ authenticated = NULL;
+ while (ap->type) {
+ if (!ap->init || (*ap->init)(ap)) {
+ i_support |= typemask(ap->type);
+ if (auth_debug_mode)
+ (void) printf(gettext
+ (">>>%s: I support auth type %d %d\r\n"),
+ Name, ap->type, ap->way);
+ }
+ ++ap;
+ }
+}
+
+/*
+ * Search the Authenticator array for the authentication type 'name',
+ * and disable this type by updating the i_wont_support bitfield.
+ */
+void
+auth_disable_name(const char *name)
+{
+ uint_t x;
+ for (x = 0; x < AUTHTYPE_CNT; ++x) {
+ if (!strcasecmp(name, AUTHTYPE_NAME(x))) {
+ i_wont_support |= typemask(x);
+ break;
+ }
+ }
+
+ if (!i_wont_support)
+ (void) printf(
+ gettext("%s : invalid authentication type\n"),
+ name);
+}
+
+/*
+ * Search the Authenticator array for the authentication type given
+ * by the character string 'type', and return its integer bitmask
+ * in maskp.
+ *
+ * Returns: 1 - no error
+ * 0 - type not found (error)
+ */
+static int
+getauthmask(const char *type, uint_t *maskp)
+{
+ uint_t x;
+
+ if (!strcasecmp(type, AUTHTYPE_NAME(0))) {
+ *maskp = (uint_t)-1;
+ return (1);
+ }
+
+ for (x = 1; x < AUTHTYPE_CNT; ++x) {
+ if (!strcasecmp(type, AUTHTYPE_NAME(x))) {
+ *maskp = typemask(x);
+ return (1);
+ }
+ }
+ return (0);
+}
+
+int
+auth_enable(char *type)
+{
+ return (auth_onoff(type, B_TRUE));
+}
+
+int
+auth_disable(char *type)
+{
+ return (auth_onoff(type, B_FALSE));
+}
+
+/*
+ * Responds to the 'auth enable <option>' and 'auth disable <option>' commands.
+ *
+ * If <option> is:
+ * - a valid authentication type, turns support on / off
+ * - "?" or "help", print a usage message
+ * - not recognized, print an error message.
+ *
+ * Returns: 1 - no error, authentication is enabled or disabled
+ * 0 - error, or help requested
+ */
+static int
+auth_onoff(const char *type, boolean_t on)
+{
+ uint_t i, mask = 0;
+ Authenticator *ap;
+
+ if (!strcasecmp(type, "?") || !strcasecmp(type, "help")) {
+ (void) printf(on ?
+ gettext("auth enable 'type'\n") :
+ gettext("auth disable 'type'\n"));
+ (void) printf(
+ gettext("Where 'type' is one of:\n"));
+ (void) printf("\t%s\n", AUTHTYPE_NAME(0));
+ for (ap = authenticators; ap->type; ap++) {
+ if ((mask & (i = typemask(ap->type))) != 0)
+ continue;
+ mask |= i;
+ (void) printf("\t%s\n", AUTHTYPE_NAME(ap->type));
+ }
+ return (0);
+ }
+
+ if (!getauthmask(type, &mask)) {
+ (void) printf(
+ gettext("%s: invalid authentication type\n"), type);
+ return (0);
+ }
+ if (on)
+ i_wont_support &= ~mask;
+ else
+ i_wont_support |= mask;
+ return (1);
+}
+
+/*
+ * Responds to the 'toggle authdebug' command.
+ *
+ * Returns: 1 - always
+ */
+int
+auth_togdebug(int on)
+{
+ if (on < 0)
+ auth_debug_mode = !auth_debug_mode;
+ else
+ auth_debug_mode = on > 0 ? B_TRUE : B_FALSE;
+ (void) printf(auth_debug_mode ?
+ gettext("auth debugging enabled\n") :
+ gettext("auth debugging disabled\n"));
+ return (1);
+}
+
+/*
+ * Responds to the 'auth status' command.
+ * Traverses the authenticator array and prints enabled or disabled for
+ * each authentication type, depencing on the i_wont_support bitfield.
+ *
+ * Returns: 1 - always
+ */
+int
+auth_status(void)
+{
+ Authenticator *ap;
+ uint_t i, mask;
+
+ if (i_wont_support == (uint_t)-1)
+ (void) printf(gettext("Authentication disabled\n"));
+ else
+ (void) printf(gettext("Authentication enabled\n"));
+
+ mask = 0;
+ for (ap = authenticators; ap->type; ap++) {
+ if ((mask & (i = typemask(ap->type))) != 0)
+ continue;
+ mask |= i;
+ (void) printf("%s: %s\n", AUTHTYPE_NAME(ap->type),
+ (i_wont_support & typemask(ap->type)) ?
+ gettext("disabled") : gettext("enabled"));
+ }
+ return (1);
+}
+
+/*
+ * This is called when an AUTH SEND is received.
+ * data is a list of authentication mechanisms we support
+ */
+void
+auth_send(uchar_t *data, int cnt)
+{
+
+ if (auth_debug_mode) {
+ (void) printf(gettext(">>>%s: auth_send got:"), Name);
+ printd(data, cnt);
+ (void) printf("\r\n");
+ }
+
+ /*
+ * Save the list of authentication mechanisms
+ */
+ auth_send_cnt = cnt;
+ if (auth_send_cnt > sizeof (_auth_send_data))
+ auth_send_cnt = sizeof (_auth_send_data);
+ (void) memcpy((void *)_auth_send_data, (void *)data, auth_send_cnt);
+ auth_send_data = _auth_send_data;
+
+ auth_send_retry();
+}
+
+/*
+ * Try the next authentication mechanism on the list, and see if it
+ * works.
+ */
+void
+auth_send_retry(void)
+{
+ Authenticator *ap;
+ static uchar_t str_none[] = { IAC, SB, TELOPT_AUTHENTICATION,
+ TELQUAL_IS, AUTHTYPE_NULL, 0, IAC, SE };
+
+ for (; (auth_send_cnt -= 2) >= 0; auth_send_data += 2) {
+ if (auth_debug_mode)
+ (void) printf(
+ gettext(">>>%s: Remote host supports %d\r\n"),
+ Name, *auth_send_data);
+ if (!(i_support & typemask(*auth_send_data)))
+ continue;
+ if (i_wont_support & typemask(*auth_send_data))
+ continue;
+ ap = findauthenticator(auth_send_data[0], auth_send_data[1]);
+ if (!ap || !ap->send)
+ continue;
+ if ((ap->way & AUTH_ENCRYPT_MASK) && !auth_enable_encrypt)
+ continue;
+
+ if (auth_debug_mode)
+ (void) printf(
+ gettext(">>>%s: Trying %d %d\r\n"), Name,
+ auth_send_data[0], auth_send_data[1]);
+ if ((*ap->send)(ap)) {
+ /*
+ * Okay, we found one we like and did it. we can go
+ * home now.
+ */
+ if (auth_debug_mode)
+ (void) printf(gettext(">>>%s: Using type %d\r\n"),
+ Name, *auth_send_data);
+ auth_send_data += 2;
+ return;
+ }
+ }
+ (void) net_write(str_none, sizeof (str_none));
+ printsub('>', &str_none[2], sizeof (str_none) - 2);
+ if (auth_debug_mode)
+ (void) printf(
+ gettext(">>>%s: Sent failure message\r\n"), Name);
+ auth_finished(0, AUTH_REJECT);
+ auth_has_failed = B_TRUE;
+}
+
+void
+auth_reply(uchar_t *data, int cnt)
+{
+ Authenticator *ap;
+
+ if (cnt < 2)
+ return;
+
+ if (ap = findauthenticator(data[0], data[1])) {
+ if (ap->reply)
+ (*ap->reply)(ap, data+2, cnt-2);
+ } else if (auth_debug_mode)
+ (void) printf(gettext
+ (">>>%s: Invalid authentication in SEND: %d\r\n"),
+ Name, *data);
+}
+
+int
+auth_sendname(uchar_t *cp, int len)
+{
+ static uchar_t str_request[AUTH_NAME_BUFSIZ + 6] = { IAC, SB,
+ TELOPT_AUTHENTICATION, TELQUAL_NAME, };
+ register uchar_t *e = str_request + 4;
+ register uchar_t *ee = &str_request[sizeof (str_request) - 2];
+
+ while (--len >= 0) {
+ if ((*e++ = *cp++) == IAC)
+ *e++ = IAC;
+ if (e >= ee)
+ return (0);
+ }
+ *e++ = IAC;
+ *e++ = SE;
+ (void) net_write(str_request, e - str_request);
+ printsub('>', &str_request[2], e - &str_request[2]);
+ return (1);
+}
+
+/* ARGSUSED */
+void
+auth_finished(Authenticator *ap, int result)
+{
+ authenticated = ap;
+ if (authenticated == NULL)
+ authenticated = &NoAuth;
+}
+
+void
+auth_printsub(uchar_t *data, uint_t cnt, uchar_t *buf, uint_t buflen)
+{
+ Authenticator *ap;
+
+ ap = findauthenticator(data[1], data[2]);
+ if (ap && ap->printsub)
+ (*ap->printsub)(data, cnt, buf, buflen);
+ else
+ auth_gen_printsub(data, cnt, buf, buflen);
+}
+
+static void
+auth_gen_printsub(uchar_t *data, uint_t cnt, uchar_t *buf, uint_t buflen)
+{
+ register uchar_t *cp;
+ uchar_t lbuf[AUTH_LBUF_BUFSIZ];
+
+ if (buflen < 2)
+ return;
+ cnt = (cnt > 3) ? cnt - 3 : 0;
+ data += 3;
+ buf[buflen - 1] = '\0';
+ buf[buflen - 2] = '*';
+ buflen -= 2;
+ for (; cnt > 0; cnt--, data++) {
+ (void) snprintf((char *)lbuf, AUTH_LBUF_BUFSIZ, " %d", *data);
+ for (cp = lbuf; (*cp != '\0') && (buflen > 0); --buflen)
+ *buf++ = *cp++;
+ if (buflen == 0)
+ return;
+ }
+ *buf = '\0';
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/telnet/auth.h b/usr/src/cmd/cmd-inet/usr.bin/telnet/auth.h
new file mode 100644
index 0000000000..cbf1d992a7
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/telnet/auth.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)auth.h 8.1 (Berkeley) 6/4/93
+ */
+
+/*
+ * Copyright (C) 1990 by the Massachusetts Institute of Technology
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in 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. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * 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.
+ */
+
+#ifndef _AUTH_H
+#define _AUTH_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct XauthP {
+ int type;
+ int way;
+ int (*init)(struct XauthP *);
+ int (*send)(struct XauthP *);
+ void (*reply)(struct XauthP *, unsigned char *, int);
+ int (*status)(struct XauthP *, char *, int);
+ void (*printsub)(unsigned char *, int, unsigned char *, int);
+} Authenticator;
+
+#define AUTH_NAME_BUFSIZ 256
+#define AUTH_LBUF_BUFSIZ 32 /* short temporary buffer */
+
+extern char *UserNameRequested;
+/* extern char *RemoteHostName; */
+
+void auth_init(const char *);
+void auth_request(void);
+void auth_send(unsigned char *, int);
+int auth_sendname(uchar_t *, int);
+void auth_send_retry(void);
+void auth_reply(unsigned char *, int);
+void auth_finished(Authenticator *, int);
+int auth_must_encrypt(void);
+void auth_printsub(uchar_t *, uint_t, uchar_t *, uint_t);
+
+void auth_disable_name(const char *);
+
+void set_krb5_realm(char *);
+int kerberos5_init(Authenticator *);
+int kerberos5_send(Authenticator *);
+void kerberos5_reply(Authenticator *, unsigned char *, int);
+int kerberos5_status(Authenticator *, char *, int);
+void kerberos5_printsub(unsigned char *, int, unsigned char *, int);
+
+#include <profile/prof_int.h>
+extern errcode_t profile_get_options_boolean(profile_t,
+ char **, profile_options_boolean *);
+
+#define OPTS_FORWARD_CREDS 0x00000002
+#define OPTS_FORWARDABLE_CREDS 0x00000001
+
+extern boolean_t auth_debug_mode;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _AUTH_H */
diff --git a/usr/src/cmd/cmd-inet/usr.bin/telnet/authenc.c b/usr/src/cmd/cmd-inet/usr.bin/telnet/authenc.c
new file mode 100644
index 0000000000..e5b4236467
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/telnet/authenc.c
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Miscellaneous routines needed by the telnet client for authentication
+ * and / or encryption.
+ */
+
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)authenc.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <arpa/telnet.h>
+
+#include "general.h"
+#include "ring.h"
+#include "externs.h"
+#include "defines.h"
+#include "types.h"
+
+char *RemoteHostName = NULL;
+char *UserNameRequested = NULL;
+
+#define MAXNETDATA 16
+
+/*
+ * Get ready to do authentication and encryption by calling their
+ * init routines, and clearing the user name variable
+ */
+/* ARGSUSED */
+void
+auth_encrypt_init(char *local, char *remote, char *name)
+{
+ RemoteHostName = remote;
+
+ auth_init(name);
+
+ encrypt_init(name);
+
+ if (UserNameRequested) {
+ free(UserNameRequested);
+ UserNameRequested = NULL;
+ }
+}
+
+/*
+ * Set the user name variable. This is the user name used from now
+ * on for authentication and encryption
+ */
+void
+auth_encrypt_user(char *name)
+{
+ if (UserNameRequested)
+ free(UserNameRequested);
+ UserNameRequested = name ? strdup(name) : NULL;
+}
+
+int
+net_write(unsigned char *str, int len)
+{
+ if (NETROOM() > len) {
+ ring_supply_data(&netoring, str, len);
+ if (str[0] == IAC && str[1] == SE)
+ printsub('>', &str[2], len - 2);
+ return (len);
+ }
+ return (0);
+}
+
+void
+net_encrypt(void)
+{
+ if (encrypt_output)
+ ring_encrypt(&netoring, encrypt_output);
+ else
+ ring_clearto(&netoring);
+}
+
+/*
+ * Spin to wait for authentication to complete
+ * This allows for a timeout
+ */
+void
+telnet_spin(void)
+{
+ extern boolean_t scheduler_lockout_tty;
+
+ scheduler_lockout_tty = B_TRUE;
+ (void) Scheduler(0);
+ scheduler_lockout_tty = B_FALSE;
+}
+
+
+/*
+ * Used to print out unsigned chars as decimals for debugging options
+ */
+void
+printd(unsigned char *data, int cnt)
+{
+ cnt = (cnt < MAXNETDATA) ? cnt:MAXNETDATA;
+ while (cnt-- > 0)
+ (void) printf(" %02x", *data++);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/telnet/commands.c b/usr/src/cmd/cmd-inet/usr.bin/telnet/commands.c
new file mode 100644
index 0000000000..2276bf30d9
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/telnet/commands.c
@@ -0,0 +1,3684 @@
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Copyright (c) 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.
+ *
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)commands.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/socket.h>
+#include <sys/sysmacros.h>
+#include <netinet/in.h>
+
+#include <signal.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <errno.h>
+#include <strings.h>
+
+#include <arpa/telnet.h>
+#include <arpa/inet.h>
+
+#include "general.h"
+
+#include "ring.h"
+
+#include "externs.h"
+#include "defines.h"
+#include "types.h"
+
+extern char *telnet_krb5_realm;
+extern void krb5_profile_get_options(char *, char *,
+ profile_options_boolean*);
+
+#include <k5-int.h>
+#include <profile/prof_int.h>
+
+profile_options_boolean config_file_options[] = {
+ { "forwardable", &forwardable_flag, 0},
+ { "forward", &forward_flag, 0},
+ { "encrypt", &encrypt_flag, 0 },
+ { "autologin", &autologin, 0 },
+ { NULL, NULL, 0}
+};
+
+#include <netinet/ip.h>
+
+/*
+ * Number of maximum IPv4 gateways user can specify. This number is limited by
+ * the maximum size of the IPv4 options in the IPv4 header.
+ */
+#define MAX_GATEWAY 8
+/*
+ * Number of maximum IPv6 gateways user can specify. This number is limited by
+ * the maximum header extension length of the IPv6 routing header.
+ */
+#define MAX_GATEWAY6 127
+#define MAXMAX_GATEWAY MAX(MAX_GATEWAY, MAX_GATEWAY6)
+
+/*
+ * Depending on the address resolutions of the target and gateways,
+ * we determine which addresses of the target we'll try connecting to.
+ */
+#define ALL_ADDRS 0 /* try all addrs of target */
+#define ONLY_V4 1 /* try only IPv4 addrs of target */
+#define ONLY_V6 2 /* try only IPv6 addrs of target */
+
+#if defined(USE_TOS)
+int tos = -1;
+#endif
+
+char *hostname;
+static char _hostname[MAXHOSTNAMELEN];
+
+static int send_tncmd(void (*func)(), char *, char *);
+static void call(int n_ptrs, ...);
+static int cmdrc(char *, char *);
+
+typedef struct {
+ char *name; /* command name */
+ char *help; /* help string (NULL for no help) */
+ int (*handler)(); /* routine which executes command */
+ int needconnect; /* Do we need to be connected to execute? */
+} Command;
+
+/*
+ * storage for IPv6 and/or IPv4 addresses of gateways
+ */
+struct gateway {
+ struct in6_addr gw_addr6;
+ struct in_addr gw_addr;
+};
+
+/*
+ * IPv4 source routing option.
+ * In order to avoid padding for the alignment of IPv4 addresses, ipsr_addrs
+ * is defined as a 2-D array of uint8_t, instead of 1-D array of struct in_addr.
+ * If it were defined as "struct in_addr ipsr_addrs[1]", "ipsr_ptr" would be
+ * followed by one byte of padding to avoid misaligned struct in_addr.
+ */
+struct ip_sourceroute {
+ uint8_t ipsr_code;
+ uint8_t ipsr_len;
+ uint8_t ipsr_ptr;
+ /* up to 9 IPv4 addresses */
+ uint8_t ipsr_addrs[1][sizeof (struct in_addr)];
+};
+
+static char *line = NULL;
+static unsigned linesize = 0;
+static int margc;
+static char **margv = NULL;
+static unsigned margvlen = 0;
+static int doing_rc = 0; /* .telnetrc file is being read and processed */
+
+static void
+Close(int *fd)
+{
+ if (*fd != -1) {
+ (void) close(*fd);
+ *fd = -1;
+ }
+}
+
+static void
+Free(char **p)
+{
+ if (*p != NULL) {
+ free(*p);
+ *p = NULL;
+ }
+}
+
+static void
+FreeHostnameList(char *list[])
+{
+ unsigned i;
+ for (i = 0; i <= MAXMAX_GATEWAY && list[i] != NULL; i++)
+ Free(&list[i]);
+}
+
+#define MARGV_CHUNK_SIZE 8
+
+static void
+set_argv(str)
+char *str;
+{
+ if (margc == margvlen) {
+ char **newmargv;
+
+ margvlen += MARGV_CHUNK_SIZE;
+
+ if ((newmargv = realloc(margv, margvlen * sizeof (char *)))
+ == NULL)
+ ExitString("telnet: no space for arguments",
+ EXIT_FAILURE);
+
+ margv = newmargv;
+ }
+
+ margv[margc] = str;
+ if (str != NULL)
+ margc++;
+}
+
+static void
+makeargv()
+{
+ char *cp, *cp2, c;
+ boolean_t shellcmd = B_FALSE;
+
+ margc = 0;
+ cp = line;
+ if (*cp == '!') { /* Special case shell escape */
+ set_argv("!"); /* No room in string to get this */
+ cp++;
+ shellcmd = B_TRUE;
+ }
+ while ((c = *cp) != '\0') {
+ register int inquote = 0;
+ while (isspace(c))
+ c = *++cp;
+ if (c == '\0')
+ break;
+ set_argv(cp);
+ /*
+ * For the shell escape, put the rest of the line, less
+ * leading space, into a single argument, breaking out from
+ * the loop to prevent the rest of the line being split up
+ * into smaller arguments.
+ */
+ if (shellcmd)
+ break;
+ for (cp2 = cp; c != '\0'; c = *++cp) {
+ if (inquote) {
+ if (c == inquote) {
+ inquote = 0;
+ continue;
+ }
+ } else {
+ if (c == '\\') {
+ if ((c = *++cp) == '\0')
+ break;
+ } else if (c == '"') {
+ inquote = '"';
+ continue;
+ } else if (c == '\'') {
+ inquote = '\'';
+ continue;
+ } else if (isspace(c))
+ break;
+ }
+ *cp2++ = c;
+ }
+ *cp2 = '\0';
+ if (c == '\0')
+ break;
+ cp++;
+ }
+ set_argv((char *)NULL);
+}
+
+/*
+ * Make a character string into a number.
+ *
+ * Todo: 1. Could take random integers (12, 0x12, 012, 0b1).
+ */
+
+ static
+special(s)
+ register char *s;
+{
+ register char c;
+ char b;
+
+ switch (*s) {
+ case '^':
+ b = *++s;
+ if (b == '?') {
+ c = b | 0x40; /* DEL */
+ } else {
+ c = b & 0x1f;
+ }
+ break;
+ default:
+ c = *s;
+ break;
+ }
+ return (c);
+}
+
+/*
+ * Construct a control character sequence
+ * for a special character.
+ */
+ static char *
+control(c)
+ register cc_t c;
+{
+ static char buf[5];
+ /*
+ * The only way I could get the Sun 3.5 compiler
+ * to shut up about
+ * if ((unsigned int)c >= 0x80)
+ * was to assign "c" to an unsigned int variable...
+ * Arggg....
+ */
+ register unsigned int uic = (unsigned int)c;
+
+ if (uic == 0x7f)
+ return ("^?");
+ if (c == (cc_t)_POSIX_VDISABLE) {
+ return ("off");
+ }
+ if (uic >= 0x80) {
+ buf[0] = '\\';
+ buf[1] = ((c>>6)&07) + '0';
+ buf[2] = ((c>>3)&07) + '0';
+ buf[3] = (c&07) + '0';
+ buf[4] = 0;
+ } else if (uic >= 0x20) {
+ buf[0] = c;
+ buf[1] = 0;
+ } else {
+ buf[0] = '^';
+ buf[1] = '@'+c;
+ buf[2] = 0;
+ }
+ return (buf);
+}
+
+/*
+ * Same as control() except that its only used for escape handling, which uses
+ * _POSIX_VDISABLE differently and is aided by the use of the state variable
+ * escape_valid.
+ */
+ static char *
+esc_control(c)
+ register cc_t c;
+{
+ static char buf[5];
+ /*
+ * The only way I could get the Sun 3.5 compiler
+ * to shut up about
+ * if ((unsigned int)c >= 0x80)
+ * was to assign "c" to an unsigned int variable...
+ * Arggg....
+ */
+ register unsigned int uic = (unsigned int)c;
+
+ if (escape_valid == B_FALSE)
+ return ("off");
+ if (uic == 0x7f)
+ return ("^?");
+ if (uic >= 0x80) {
+ buf[0] = '\\';
+ buf[1] = ((c>>6)&07) + '0';
+ buf[2] = ((c>>3)&07) + '0';
+ buf[3] = (c&07) + '0';
+ buf[4] = 0;
+ } else if (uic >= 0x20) {
+ buf[0] = c;
+ buf[1] = 0;
+ } else {
+ buf[0] = '^';
+ buf[1] = '@'+c;
+ buf[2] = 0;
+ }
+ return (buf);
+}
+
+/*
+ * The following are data structures and routines for
+ * the "send" command.
+ *
+ */
+
+struct sendlist {
+ char *name; /* How user refers to it (case independent) */
+ char *help; /* Help information (0 ==> no help) */
+ int needconnect; /* Need to be connected */
+ int narg; /* Number of arguments */
+ int (*handler)(); /* Routine to perform (for special ops) */
+ int nbyte; /* Number of bytes to send this command */
+ int what; /* Character to be sent (<0 ==> special) */
+};
+
+
+static int send_esc(void);
+static int send_help(void);
+static int send_docmd(char *);
+static int send_dontcmd(char *);
+static int send_willcmd(char *);
+static int send_wontcmd(char *);
+
+static struct sendlist Sendlist[] = {
+ { "ao", "Send Telnet Abort output", 1, 0, 0, 2, AO },
+ { "ayt", "Send Telnet 'Are You There'", 1, 0, 0, 2, AYT },
+ { "b", 0, 1, 0, 0, 2, BREAK },
+ { "br", 0, 1, 0, 0, 2, BREAK },
+ { "break", 0, 1, 0, 0, 2, BREAK },
+ { "brk", "Send Telnet Break", 1, 0, 0, 2, BREAK },
+ { "ec", "Send Telnet Erase Character", 1, 0, 0, 2, EC },
+ { "el", "Send Telnet Erase Line", 1, 0, 0, 2, EL },
+ { "escape", "Send current escape character", 1, 0, send_esc, 1, 0 },
+ { "ga", "Send Telnet 'Go Ahead' sequence", 1, 0, 0, 2, GA },
+ { "ip", "Send Telnet Interrupt Process", 1, 0, 0, 2, IP },
+ { "intp", 0, 1, 0, 0, 2, IP },
+ { "interrupt", 0, 1, 0, 0, 2, IP },
+ { "intr", 0, 1, 0, 0, 2, IP },
+ { "nop", "Send Telnet 'No operation'", 1, 0, 0, 2, NOP },
+ { "eor", "Send Telnet 'End of Record'", 1, 0, 0, 2, EOR },
+ { "abort", "Send Telnet 'Abort Process'", 1, 0, 0, 2, ABORT },
+ { "susp", "Send Telnet 'Suspend Process'", 1, 0, 0, 2, SUSP },
+ { "eof", "Send Telnet End of File Character", 1, 0, 0, 2, xEOF },
+ { "synch", "Perform Telnet 'Synch operation'", 1, 0, dosynch, 2, 0 },
+ { "getstatus", "Send request for STATUS", 1, 0, get_status, 6, 0 },
+ { "?", "Display send options", 0, 0, send_help, 0, 0 },
+ { "help", 0, 0, 0, send_help, 0, 0 },
+ { "do", 0, 0, 1, send_docmd, 3, 0 },
+ { "dont", 0, 0, 1, send_dontcmd, 3, 0 },
+ { "will", 0, 0, 1, send_willcmd, 3, 0 },
+ { "wont", 0, 0, 1, send_wontcmd, 3, 0 },
+ { 0 }
+};
+
+#define GETSEND(name) ((struct sendlist *)genget(name, (char **)Sendlist, \
+ sizeof (struct sendlist)))
+
+static int
+sendcmd(argc, argv)
+ int argc;
+ char **argv;
+{
+ int count; /* how many bytes we are going to need to send */
+ int i;
+ struct sendlist *s; /* pointer to current command */
+ int success = 0;
+ int needconnect = 0;
+
+ if (argc < 2) {
+ (void) printf(
+ "need at least one argument for 'send' command\n");
+ (void) printf("'send ?' for help\n");
+ return (0);
+ }
+ /*
+ * First, validate all the send arguments.
+ * In addition, we see how much space we are going to need, and
+ * whether or not we will be doing a "SYNCH" operation (which
+ * flushes the network queue).
+ */
+ count = 0;
+ for (i = 1; i < argc; i++) {
+ s = GETSEND(argv[i]);
+ if (s == 0) {
+ (void) printf("Unknown send argument '%s'\n'send ?' "
+ "for help.\n", argv[i]);
+ return (0);
+ } else if (Ambiguous(s)) {
+ (void) printf("Ambiguous send argument '%s'\n'send ?' "
+ "for help.\n", argv[i]);
+ return (0);
+ }
+ if (i + s->narg >= argc) {
+ (void) fprintf(stderr,
+ "Need %d argument%s to 'send %s' "
+ "command. 'send %s ?' for help.\n",
+ s->narg, s->narg == 1 ? "" : "s", s->name, s->name);
+ return (0);
+ }
+ count += s->nbyte;
+ if (s->handler == send_help) {
+ (void) send_help();
+ return (0);
+ }
+
+ i += s->narg;
+ needconnect += s->needconnect;
+ }
+ if (!connected && needconnect) {
+ (void) printf("?Need to be connected first.\n");
+ (void) printf("'send ?' for help\n");
+ return (0);
+ }
+ /* Now, do we have enough room? */
+ if (NETROOM() < count) {
+ (void) printf("There is not enough room in the buffer "
+ "TO the network\n");
+ (void) printf(
+ "to process your request. Nothing will be done.\n");
+ (void) printf("('send synch' will throw away most "
+ "data in the network\n");
+ (void) printf("buffer, if this might help.)\n");
+ return (0);
+ }
+ /* OK, they are all OK, now go through again and actually send */
+ count = 0;
+ for (i = 1; i < argc; i++) {
+ if ((s = GETSEND(argv[i])) == 0) {
+ (void) fprintf(stderr,
+ "Telnet 'send' error - argument disappeared!\n");
+ (void) quit();
+ /*NOTREACHED*/
+ }
+ if (s->handler) {
+ count++;
+ success += (*s->handler)((s->narg > 0) ? argv[i+1] : 0,
+ (s->narg > 1) ? argv[i+2] : 0);
+ i += s->narg;
+ } else {
+ NET2ADD(IAC, s->what);
+ printoption("SENT", IAC, s->what);
+ }
+ }
+ return (count == success);
+}
+
+static int
+send_esc()
+{
+ NETADD(escape);
+ return (1);
+}
+
+static int
+send_docmd(name)
+ char *name;
+{
+ return (send_tncmd(send_do, "do", name));
+}
+
+static int
+send_dontcmd(name)
+ char *name;
+{
+ return (send_tncmd(send_dont, "dont", name));
+}
+
+static int
+send_willcmd(name)
+ char *name;
+{
+ return (send_tncmd(send_will, "will", name));
+}
+
+static int
+send_wontcmd(name)
+ char *name;
+{
+ return (send_tncmd(send_wont, "wont", name));
+}
+
+int
+send_tncmd(func, cmd, name)
+ void (*func)();
+ char *cmd, *name;
+{
+ char **cpp;
+ extern char *telopts[];
+ register int val = 0;
+
+ if (isprefix(name, "help") || isprefix(name, "?")) {
+ register int col, len;
+
+ (void) printf("Usage: send %s <value|option>\n", cmd);
+ (void) printf("\"value\" must be from 0 to 255\n");
+ (void) printf("Valid options are:\n\t");
+
+ col = 8;
+ for (cpp = telopts; *cpp; cpp++) {
+ len = strlen(*cpp) + 3;
+ if (col + len > 65) {
+ (void) printf("\n\t");
+ col = 8;
+ }
+ (void) printf(" \"%s\"", *cpp);
+ col += len;
+ }
+ (void) printf("\n");
+ return (0);
+ }
+ cpp = (char **)genget(name, telopts, sizeof (char *));
+ if (Ambiguous(cpp)) {
+ (void) fprintf(stderr,
+ "'%s': ambiguous argument ('send %s ?' for help).\n",
+ name, cmd);
+ return (0);
+ }
+ if (cpp) {
+ val = cpp - telopts;
+ } else {
+ register char *cp = name;
+
+ while (*cp >= '0' && *cp <= '9') {
+ val *= 10;
+ val += *cp - '0';
+ cp++;
+ }
+ if (*cp != 0) {
+ (void) fprintf(stderr,
+ "'%s': unknown argument ('send %s ?' for help).\n",
+ name, cmd);
+ return (0);
+ } else if (val < 0 || val > 255) {
+ (void) fprintf(stderr,
+ "'%s': bad value ('send %s ?' for help).\n",
+ name, cmd);
+ return (0);
+ }
+ }
+ if (!connected) {
+ (void) printf("?Need to be connected first.\n");
+ return (0);
+ }
+ (*func)(val, 1);
+ return (1);
+}
+
+static int
+send_help()
+{
+ struct sendlist *s; /* pointer to current command */
+ for (s = Sendlist; s->name; s++) {
+ if (s->help)
+ (void) printf("%-15s %s\n", s->name, s->help);
+ }
+ return (0);
+}
+
+/*
+ * The following are the routines and data structures referred
+ * to by the arguments to the "toggle" command.
+ */
+
+static int
+lclchars()
+{
+ donelclchars = 1;
+ return (1);
+}
+
+static int
+togdebug()
+{
+ if (net > 0 &&
+ (SetSockOpt(net, SOL_SOCKET, SO_DEBUG, debug)) < 0) {
+ perror("setsockopt (SO_DEBUG)");
+ }
+ return (1);
+}
+
+
+static int
+togcrlf()
+{
+ if (crlf) {
+ (void) printf(
+ "Will send carriage returns as telnet <CR><LF>.\n");
+ } else {
+ (void) printf(
+ "Will send carriage returns as telnet <CR><NUL>.\n");
+ }
+ return (1);
+}
+
+static int binmode;
+
+static int
+togbinary(val)
+ int val;
+{
+ donebinarytoggle = 1;
+
+ if (val >= 0) {
+ binmode = val;
+ } else {
+ if (my_want_state_is_will(TELOPT_BINARY) &&
+ my_want_state_is_do(TELOPT_BINARY)) {
+ binmode = 1;
+ } else if (my_want_state_is_wont(TELOPT_BINARY) &&
+ my_want_state_is_dont(TELOPT_BINARY)) {
+ binmode = 0;
+ }
+ val = binmode ? 0 : 1;
+ }
+
+ if (val == 1) {
+ if (my_want_state_is_will(TELOPT_BINARY) &&
+ my_want_state_is_do(TELOPT_BINARY)) {
+ (void) printf("Already operating in binary mode "
+ "with remote host.\n");
+ } else {
+ (void) printf(
+ "Negotiating binary mode with remote host.\n");
+ tel_enter_binary(3);
+ }
+ } else {
+ if (my_want_state_is_wont(TELOPT_BINARY) &&
+ my_want_state_is_dont(TELOPT_BINARY)) {
+ (void) printf("Already in network ascii mode "
+ "with remote host.\n");
+ } else {
+ (void) printf("Negotiating network ascii mode "
+ "with remote host.\n");
+ tel_leave_binary(3);
+ }
+ }
+ return (1);
+}
+
+static int
+togrbinary(val)
+ int val;
+{
+ donebinarytoggle = 1;
+
+ if (val == -1)
+ val = my_want_state_is_do(TELOPT_BINARY) ? 0 : 1;
+
+ if (val == 1) {
+ if (my_want_state_is_do(TELOPT_BINARY)) {
+ (void) printf("Already receiving in binary mode.\n");
+ } else {
+ (void) printf("Negotiating binary mode on input.\n");
+ tel_enter_binary(1);
+ }
+ } else {
+ if (my_want_state_is_dont(TELOPT_BINARY)) {
+ (void) printf(
+ "Already receiving in network ascii mode.\n");
+ } else {
+ (void) printf(
+ "Negotiating network ascii mode on input.\n");
+ tel_leave_binary(1);
+ }
+ }
+ return (1);
+}
+
+static int
+togxbinary(val)
+ int val;
+{
+ donebinarytoggle = 1;
+
+ if (val == -1)
+ val = my_want_state_is_will(TELOPT_BINARY) ? 0 : 1;
+
+ if (val == 1) {
+ if (my_want_state_is_will(TELOPT_BINARY)) {
+ (void) printf("Already transmitting in binary mode.\n");
+ } else {
+ (void) printf("Negotiating binary mode on output.\n");
+ tel_enter_binary(2);
+ }
+ } else {
+ if (my_want_state_is_wont(TELOPT_BINARY)) {
+ (void) printf(
+ "Already transmitting in network ascii mode.\n");
+ } else {
+ (void) printf(
+ "Negotiating network ascii mode on output.\n");
+ tel_leave_binary(2);
+ }
+ }
+ return (1);
+}
+
+
+static int togglehelp(void);
+extern int auth_togdebug(int);
+
+struct togglelist {
+ char *name; /* name of toggle */
+ char *help; /* help message */
+ int (*handler)(); /* routine to do actual setting */
+ int *variable;
+ char *actionexplanation;
+};
+
+static struct togglelist Togglelist[] = {
+ { "autoflush",
+ "flushing of output when sending interrupt characters",
+ 0,
+ &autoflush,
+ "flush output when sending interrupt characters" },
+ { "autosynch",
+ "automatic sending of interrupt characters in urgent mode",
+ 0,
+ &autosynch,
+ "send interrupt characters in urgent mode" },
+ { "autologin",
+ "automatic sending of login and/or authentication info",
+ 0,
+ &autologin,
+ "send login name and/or authentication information" },
+ { "authdebug",
+ "authentication debugging",
+ auth_togdebug,
+ 0,
+ "print authentication debugging information" },
+ { "autoencrypt",
+ "automatic encryption of data stream",
+ EncryptAutoEnc,
+ 0,
+ "automatically encrypt output" },
+ { "autodecrypt",
+ "automatic decryption of data stream",
+ EncryptAutoDec,
+ 0,
+ "automatically decrypt input" },
+ { "verbose_encrypt",
+ "verbose encryption output",
+ EncryptVerbose,
+ 0,
+ "print verbose encryption output" },
+ { "encdebug",
+ "encryption debugging",
+ EncryptDebug,
+ 0,
+ "print encryption debugging information" },
+ { "skiprc",
+ "don't read ~/.telnetrc file",
+ 0,
+ &skiprc,
+ "skip reading of ~/.telnetrc file" },
+ { "binary",
+ "sending and receiving of binary data",
+ togbinary,
+ 0,
+ 0 },
+ { "inbinary",
+ "receiving of binary data",
+ togrbinary,
+ 0,
+ 0 },
+ { "outbinary",
+ "sending of binary data",
+ togxbinary,
+ 0,
+ 0 },
+ { "crlf",
+ "sending carriage returns as telnet <CR><LF>",
+ togcrlf,
+ &crlf,
+ 0 },
+ { "crmod",
+ "mapping of received carriage returns",
+ 0,
+ &crmod,
+ "map carriage return on output" },
+ { "localchars",
+ "local recognition of certain control characters",
+ lclchars,
+ &localchars,
+ "recognize certain control characters" },
+ { " ", "", 0 }, /* empty line */
+ { "debug",
+ "debugging",
+ togdebug,
+ &debug,
+ "turn on socket level debugging" },
+ { "netdata",
+ "printing of hexadecimal network data (debugging)",
+ 0,
+ &netdata,
+ "print hexadecimal representation of network traffic" },
+ { "prettydump",
+ "output of \"netdata\" to user readable format (debugging)",
+ 0,
+ &prettydump,
+ "print user readable output for \"netdata\"" },
+ { "options",
+ "viewing of options processing (debugging)",
+ 0,
+ &showoptions,
+ "show option processing" },
+ { "termdata",
+ "(debugging) toggle printing of hexadecimal terminal data",
+ 0,
+ &termdata,
+ "print hexadecimal representation of terminal traffic" },
+ { "?",
+ 0,
+ togglehelp },
+ { "help",
+ 0,
+ togglehelp },
+ { 0 }
+};
+
+static int
+togglehelp()
+{
+ struct togglelist *c;
+
+ for (c = Togglelist; c->name; c++) {
+ if (c->help) {
+ if (*c->help)
+ (void) printf(
+ "%-15s toggle %s\n", c->name, c->help);
+ else
+ (void) printf("\n");
+ }
+ }
+ (void) printf("\n");
+ (void) printf("%-15s %s\n", "?", "display help information");
+ return (0);
+}
+
+static void
+settogglehelp(set)
+ int set;
+{
+ struct togglelist *c;
+
+ for (c = Togglelist; c->name; c++) {
+ if (c->help) {
+ if (*c->help)
+ (void) printf("%-15s %s %s\n", c->name,
+ set ? "enable" : "disable", c->help);
+ else
+ (void) printf("\n");
+ }
+ }
+}
+
+#define GETTOGGLE(name) (struct togglelist *) \
+ genget(name, (char **)Togglelist, sizeof (struct togglelist))
+
+static int
+toggle(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int retval = 1;
+ char *name;
+ struct togglelist *c;
+
+ if (argc < 2) {
+ (void) fprintf(stderr,
+ "Need an argument to 'toggle' command. "
+ "'toggle ?' for help.\n");
+ return (0);
+ }
+ argc--;
+ argv++;
+ while (argc--) {
+ name = *argv++;
+ c = GETTOGGLE(name);
+ if (Ambiguous(c)) {
+ (void) fprintf(stderr, "'%s': ambiguous argument "
+ "('toggle ?' for help).\n", name);
+ return (0);
+ } else if (c == 0) {
+ (void) fprintf(stderr, "'%s': unknown argument "
+ "('toggle ?' for help).\n", name);
+ return (0);
+ } else {
+ if (c->variable) {
+ *c->variable = !*c->variable; /* invert it */
+ if (c->actionexplanation) {
+ (void) printf("%s %s.\n",
+ *c->variable ? "Will" : "Won't",
+ c->actionexplanation);
+ }
+ }
+ if (c->handler) {
+ retval &= (*c->handler)(-1);
+ }
+ }
+ }
+ return (retval);
+}
+
+/*
+ * The following perform the "set" command.
+ */
+
+#ifdef USE_TERMIO
+struct termio new_tc = { 0 };
+#endif
+
+struct setlist {
+ char *name; /* name */
+ char *help; /* help information */
+ void (*handler)();
+ cc_t *charp; /* where it is located at */
+};
+
+static struct setlist Setlist[] = {
+#ifdef KLUDGELINEMODE
+ { "echo", "character to toggle local echoing on/off", 0, &echoc },
+#endif
+ { "escape", "character to escape back to telnet command mode", 0,
+ &escape },
+ { "rlogin", "rlogin escape character", 0, &rlogin },
+ { "tracefile", "file to write trace information to", SetNetTrace,
+ (cc_t *)NetTraceFile},
+ { " ", "" },
+ { " ", "The following need 'localchars' to be toggled true", 0, 0 },
+ { "flushoutput", "character to cause an Abort Output", 0,
+ termFlushCharp },
+ { "interrupt", "character to cause an Interrupt Process", 0,
+ termIntCharp },
+ { "quit", "character to cause an Abort process", 0, termQuitCharp },
+ { "eof", "character to cause an EOF ", 0, termEofCharp },
+ { " ", "" },
+ { " ", "The following are for local editing in linemode", 0, 0 },
+ { "erase", "character to use to erase a character", 0, termEraseCharp },
+ { "kill", "character to use to erase a line", 0, termKillCharp },
+ { "lnext", "character to use for literal next", 0,
+ termLiteralNextCharp },
+ { "susp", "character to cause a Suspend Process", 0, termSuspCharp },
+ { "reprint", "character to use for line reprint", 0, termRprntCharp },
+ { "worderase", "character to use to erase a word", 0, termWerasCharp },
+ { "start", "character to use for XON", 0, termStartCharp },
+ { "stop", "character to use for XOFF", 0, termStopCharp },
+ { "forw1", "alternate end of line character", 0, termForw1Charp },
+ { "forw2", "alternate end of line character", 0, termForw2Charp },
+ { "ayt", "alternate AYT character", 0, termAytCharp },
+ { 0 }
+};
+
+static struct setlist *
+getset(name)
+ char *name;
+{
+ return ((struct setlist *)
+ genget(name, (char **)Setlist, sizeof (struct setlist)));
+}
+
+ void
+set_escape_char(s)
+ char *s;
+{
+ if (rlogin != _POSIX_VDISABLE) {
+ rlogin = (s && *s) ? special(s) : _POSIX_VDISABLE;
+ (void) printf("Telnet rlogin escape character is '%s'.\n",
+ control(rlogin));
+ } else {
+ escape = (s && *s) ? special(s) : _POSIX_VDISABLE;
+ (void) printf("Telnet escape character is '%s'.\n",
+ esc_control(escape));
+ }
+}
+
+static int
+setcmd(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int value;
+ struct setlist *ct;
+ struct togglelist *c;
+
+ if (argc < 2 || argc > 3) {
+ (void) printf(
+ "Format is 'set Name Value'\n'set ?' for help.\n");
+ return (0);
+ }
+ if ((argc == 2) &&
+ (isprefix(argv[1], "?") || isprefix(argv[1], "help"))) {
+ for (ct = Setlist; ct->name; ct++)
+ (void) printf("%-15s %s\n", ct->name, ct->help);
+ (void) printf("\n");
+ settogglehelp(1);
+ (void) printf("%-15s %s\n", "?", "display help information");
+ return (0);
+ }
+
+ ct = getset(argv[1]);
+ if (ct == 0) {
+ c = GETTOGGLE(argv[1]);
+ if (c == 0) {
+ (void) fprintf(stderr, "'%s': unknown argument "
+ "('set ?' for help).\n", argv[1]);
+ return (0);
+ } else if (Ambiguous(c)) {
+ (void) fprintf(stderr, "'%s': ambiguous argument "
+ "('set ?' for help).\n", argv[1]);
+ return (0);
+ }
+ if (c->variable) {
+ if ((argc == 2) || (strcmp("on", argv[2]) == 0))
+ *c->variable = 1;
+ else if (strcmp("off", argv[2]) == 0)
+ *c->variable = 0;
+ else {
+ (void) printf(
+ "Format is 'set togglename [on|off]'\n"
+ "'set ?' for help.\n");
+ return (0);
+ }
+ if (c->actionexplanation) {
+ (void) printf("%s %s.\n",
+ *c->variable? "Will" : "Won't",
+ c->actionexplanation);
+ }
+ }
+ if (c->handler)
+ (*c->handler)(1);
+ } else if (argc != 3) {
+ (void) printf(
+ "Format is 'set Name Value'\n'set ?' for help.\n");
+ return (0);
+ } else if (Ambiguous(ct)) {
+ (void) fprintf(stderr,
+ "'%s': ambiguous argument ('set ?' for help).\n", argv[1]);
+ return (0);
+ } else if (ct->handler) {
+ (*ct->handler)(argv[2]);
+ (void) printf(
+ "%s set to \"%s\".\n", ct->name, (char *)ct->charp);
+ } else {
+ if (strcmp("off", argv[2])) {
+ value = special(argv[2]);
+ } else {
+ value = _POSIX_VDISABLE;
+ }
+ *(ct->charp) = (cc_t)value;
+ (void) printf("%s character is '%s'.\n", ct->name,
+ control(*(ct->charp)));
+ }
+ slc_check();
+ return (1);
+}
+
+static int
+unsetcmd(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct setlist *ct;
+ struct togglelist *c;
+ register char *name;
+
+ if (argc < 2) {
+ (void) fprintf(stderr, "Need an argument to 'unset' command. "
+ "'unset ?' for help.\n");
+ return (0);
+ }
+ if (isprefix(argv[1], "?") || isprefix(argv[1], "help")) {
+ for (ct = Setlist; ct->name; ct++)
+ (void) printf("%-15s %s\n", ct->name, ct->help);
+ (void) printf("\n");
+ settogglehelp(0);
+ (void) printf("%-15s %s\n", "?", "display help information");
+ return (0);
+ }
+
+ argc--;
+ argv++;
+ while (argc--) {
+ name = *argv++;
+ ct = getset(name);
+ if (ct == 0) {
+ c = GETTOGGLE(name);
+ if (c == 0) {
+ (void) fprintf(stderr, "'%s': unknown argument "
+ "('unset ?' for help).\n", name);
+ return (0);
+ } else if (Ambiguous(c)) {
+ (void) fprintf(stderr,
+ "'%s': ambiguous argument "
+ "('unset ?' for help).\n", name);
+ return (0);
+ }
+ if (c->variable) {
+ *c->variable = 0;
+ if (c->actionexplanation) {
+ (void) printf("%s %s.\n",
+ *c->variable? "Will" : "Won't",
+ c->actionexplanation);
+ }
+ }
+ if (c->handler)
+ (*c->handler)(0);
+ } else if (Ambiguous(ct)) {
+ (void) fprintf(stderr, "'%s': ambiguous argument "
+ "('unset ?' for help).\n", name);
+ return (0);
+ } else if (ct->handler) {
+ (*ct->handler)(0);
+ (void) printf("%s reset to \"%s\".\n", ct->name,
+ (char *)ct->charp);
+ } else {
+ *(ct->charp) = _POSIX_VDISABLE;
+ (void) printf("%s character is '%s'.\n", ct->name,
+ control(*(ct->charp)));
+ }
+ }
+ return (1);
+}
+
+/*
+ * The following are the data structures and routines for the
+ * 'mode' command.
+ */
+extern int reqd_linemode;
+
+#ifdef KLUDGELINEMODE
+extern int kludgelinemode;
+
+static int
+dokludgemode()
+{
+ kludgelinemode = 1;
+ send_wont(TELOPT_LINEMODE, 1);
+ send_dont(TELOPT_SGA, 1);
+ send_dont(TELOPT_ECHO, 1);
+ /*
+ * If processing the .telnetrc file, keep track of linemode and/or
+ * kludgelinemode requests which are processed before initial option
+ * negotiations occur.
+ */
+ if (doing_rc)
+ reqd_linemode = 1;
+ return (1);
+}
+#endif
+
+static int
+dolinemode()
+{
+#ifdef KLUDGELINEMODE
+ if (kludgelinemode)
+ send_dont(TELOPT_SGA, 1);
+#endif
+ send_will(TELOPT_LINEMODE, 1);
+ send_dont(TELOPT_ECHO, 1);
+
+ /*
+ * If processing the .telnetrc file, keep track of linemode and/or
+ * kludgelinemode requests which are processed before initial option
+ * negotiations occur.
+ */
+ if (doing_rc)
+ reqd_linemode = 1;
+ return (1);
+}
+
+static int
+docharmode()
+{
+#ifdef KLUDGELINEMODE
+ if (kludgelinemode)
+ send_do(TELOPT_SGA, 1);
+ else
+#endif
+ send_wont(TELOPT_LINEMODE, 1);
+ send_do(TELOPT_ECHO, 1);
+ reqd_linemode = 0;
+ return (1);
+}
+
+static int
+dolmmode(bit, on)
+ int bit, on;
+{
+ unsigned char c;
+ extern int linemode;
+
+ if (my_want_state_is_wont(TELOPT_LINEMODE)) {
+ (void) printf("?Need to have LINEMODE option enabled first.\n");
+ (void) printf("'mode ?' for help.\n");
+ return (0);
+ }
+
+ if (on)
+ c = (linemode | bit);
+ else
+ c = (linemode & ~bit);
+ lm_mode(&c, 1, 1);
+ return (1);
+}
+
+static int
+setmode(bit)
+{
+ return (dolmmode(bit, 1));
+}
+
+static int
+clearmode(bit)
+{
+ return (dolmmode(bit, 0));
+}
+
+struct modelist {
+ char *name; /* command name */
+ char *help; /* help string */
+ int (*handler)(); /* routine which executes command */
+ int needconnect; /* Do we need to be connected to execute? */
+ int arg1;
+};
+
+static int modehelp();
+
+static struct modelist ModeList[] = {
+ { "character", "Disable LINEMODE option", docharmode, 1 },
+#ifdef KLUDGELINEMODE
+ { "", "(or disable obsolete line-by-line mode)", 0 },
+#endif
+ { "line", "Enable LINEMODE option", dolinemode, 1 },
+#ifdef KLUDGELINEMODE
+ { "", "(or enable obsolete line-by-line mode)", 0 },
+#endif
+ { "", "", 0 },
+ { "", "These require the LINEMODE option to be enabled", 0 },
+ { "isig", "Enable signal trapping", setmode, 1, MODE_TRAPSIG },
+ { "+isig", 0, setmode, 1, MODE_TRAPSIG },
+ { "-isig", "Disable signal trapping", clearmode, 1, MODE_TRAPSIG },
+ { "edit", "Enable character editing", setmode, 1, MODE_EDIT },
+ { "+edit", 0, setmode, 1, MODE_EDIT },
+ { "-edit", "Disable character editing", clearmode, 1, MODE_EDIT },
+ { "softtabs", "Enable tab expansion", setmode, 1, MODE_SOFT_TAB },
+ { "+softtabs", 0, setmode, 1, MODE_SOFT_TAB },
+ { "-softtabs", "Disable tab expansion",
+ clearmode, 1, MODE_SOFT_TAB },
+ { "litecho", "Enable literal character echo",
+ setmode, 1, MODE_LIT_ECHO },
+ { "+litecho", 0, setmode, 1, MODE_LIT_ECHO },
+ { "-litecho", "Disable literal character echo", clearmode, 1,
+ MODE_LIT_ECHO },
+ { "help", 0, modehelp, 0 },
+#ifdef KLUDGELINEMODE
+ { "kludgeline", 0, dokludgemode, 1 },
+#endif
+ { "", "", 0 },
+ { "?", "Print help information", modehelp, 0 },
+ { 0 },
+};
+
+
+static int
+modehelp()
+{
+ struct modelist *mt;
+
+ (void) printf("format is: 'mode Mode', where 'Mode' is one of:\n\n");
+ for (mt = ModeList; mt->name; mt++) {
+ if (mt->help) {
+ if (*mt->help)
+ (void) printf("%-15s %s\n", mt->name, mt->help);
+ else
+ (void) printf("\n");
+ }
+ }
+ return (0);
+}
+
+#define GETMODECMD(name) (struct modelist *) \
+ genget(name, (char **)ModeList, sizeof (struct modelist))
+
+static int
+modecmd(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct modelist *mt;
+
+ if (argc != 2) {
+ (void) printf("'mode' command requires an argument\n");
+ (void) printf("'mode ?' for help.\n");
+ } else if ((mt = GETMODECMD(argv[1])) == 0) {
+ (void) fprintf(stderr,
+ "Unknown mode '%s' ('mode ?' for help).\n", argv[1]);
+ } else if (Ambiguous(mt)) {
+ (void) fprintf(stderr,
+ "Ambiguous mode '%s' ('mode ?' for help).\n", argv[1]);
+ } else if (mt->needconnect && !connected) {
+ (void) printf("?Need to be connected first.\n");
+ (void) printf("'mode ?' for help.\n");
+ } else if (mt->handler) {
+ return (*mt->handler)(mt->arg1);
+ }
+ return (0);
+}
+
+/*
+ * The following data structures and routines implement the
+ * "display" command.
+ */
+
+static int
+display(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct togglelist *tl;
+ struct setlist *sl;
+
+#define dotog(tl) if (tl->variable && tl->actionexplanation) { \
+ if (*tl->variable) { \
+ (void) printf("will"); \
+ } else { \
+ (void) printf("won't"); \
+ } \
+ (void) printf(" %s.\n", tl->actionexplanation); \
+ }
+
+#define doset(sl) if (sl->name && *sl->name != ' ') { \
+ if (sl->handler == 0) \
+ (void) printf("%-15s [%s]\n", sl->name, \
+ control(*sl->charp)); \
+ else \
+ (void) printf("%-15s \"%s\"\n", sl->name, \
+ (char *)sl->charp); \
+ }
+
+ if (argc == 1) {
+ for (tl = Togglelist; tl->name; tl++) {
+ dotog(tl);
+ }
+ (void) printf("\n");
+ for (sl = Setlist; sl->name; sl++) {
+ doset(sl);
+ }
+ } else {
+ int i;
+
+ for (i = 1; i < argc; i++) {
+ sl = getset(argv[i]);
+ tl = GETTOGGLE(argv[i]);
+ if (Ambiguous(sl) || Ambiguous(tl)) {
+ (void) printf(
+ "?Ambiguous argument '%s'.\n", argv[i]);
+ return (0);
+ } else if (!sl && !tl) {
+ (void) printf(
+ "?Unknown argument '%s'.\n", argv[i]);
+ return (0);
+ } else {
+ if (tl) {
+ dotog(tl);
+ }
+ if (sl) {
+ doset(sl);
+ }
+ }
+ }
+ }
+ optionstatus();
+ (void) EncryptStatus();
+ return (1);
+#undef doset
+#undef dotog
+}
+
+/*
+ * The following are the data structures, and many of the routines,
+ * relating to command processing.
+ */
+
+/*
+ * Set the escape character.
+ */
+ static int
+setescape(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register char *arg;
+ char *buf = NULL;
+
+ if (argc > 2)
+ arg = argv[1];
+ else {
+ (void) printf("new escape character: ");
+ if (GetString(&buf, NULL, stdin) == NULL) {
+ if (!feof(stdin)) {
+ perror("can't set escape character");
+ goto setescape_exit;
+ }
+ }
+ arg = buf;
+ }
+ /* we place no limitations on what escape can be. */
+ escape = arg[0];
+ (void) printf("Escape character is '%s'.\n", esc_control(escape));
+ (void) fflush(stdout);
+setescape_exit:
+ Free(&buf);
+ return (1);
+}
+
+/*ARGSUSED*/
+static int
+togcrmod(argc, argv)
+ int argc;
+ char *argv[];
+{
+ crmod = !crmod;
+ (void) printf(
+ "%s map carriage return on output.\n", crmod ? "Will" : "Won't");
+ (void) fflush(stdout);
+ return (1);
+}
+
+/*ARGSUSED*/
+static int
+suspend(argc, argv)
+ int argc;
+ char *argv[];
+{
+ setcommandmode();
+ {
+ unsigned short oldrows, oldcols, newrows, newcols;
+ int err;
+
+ err = (TerminalWindowSize(&oldrows, &oldcols) == 0) ? 1 : 0;
+ (void) kill(0, SIGTSTP);
+ /*
+ * If we didn't get the window size before the SUSPEND, but we
+ * can get them now (?), then send the NAWS to make sure that
+ * we are set up for the right window size.
+ */
+ if (TerminalWindowSize(&newrows, &newcols) && connected &&
+ (err || ((oldrows != newrows) || (oldcols != newcols)))) {
+ sendnaws();
+ }
+ }
+ /* reget parameters in case they were changed */
+ TerminalSaveState();
+ setconnmode(0);
+ return (1);
+}
+
+/*ARGSUSED*/
+static int
+shell(argc, argv)
+ int argc;
+ char *argv[];
+{
+ unsigned short oldrows, oldcols, newrows, newcols;
+ int err;
+
+ setcommandmode();
+
+ err = (TerminalWindowSize(&oldrows, &oldcols) == 0) ? 1 : 0;
+ switch (vfork()) {
+ case -1:
+ perror("Fork failed\n");
+ break;
+
+ case 0:
+ {
+ /*
+ * Fire up the shell in the child.
+ */
+ register char *shellp, *shellname;
+
+ shellp = getenv("SHELL");
+ if (shellp == NULL)
+ shellp = "/bin/sh";
+ if ((shellname = strrchr(shellp, '/')) == 0)
+ shellname = shellp;
+ else
+ shellname++;
+ if (argc > 1)
+ (void) execl(shellp, shellname, "-c", argv[1], 0);
+ else
+ (void) execl(shellp, shellname, 0);
+ perror("Execl");
+ _exit(EXIT_FAILURE);
+ }
+ default:
+ (void) wait((int *)0); /* Wait for the shell to complete */
+
+ if (TerminalWindowSize(&newrows, &newcols) && connected &&
+ (err || ((oldrows != newrows) || (oldcols != newcols)))) {
+ sendnaws();
+ }
+ break;
+ }
+ return (1);
+}
+
+static int
+bye(argc, argv)
+ int argc; /* Number of arguments */
+ char *argv[]; /* arguments */
+{
+ extern int resettermname;
+
+ if (connected) {
+ (void) shutdown(net, 2);
+ (void) printf("Connection to %.*s closed.\n", MAXHOSTNAMELEN,
+ hostname);
+ Close(&net);
+ connected = 0;
+ resettermname = 1;
+ /* reset options */
+ (void) tninit();
+ }
+ if ((argc != 2) || (strcmp(argv[1], "fromquit") != 0)) {
+ longjmp(toplevel, 1);
+ /* NOTREACHED */
+ }
+ return (1); /* Keep lint, etc., happy */
+}
+
+/*VARARGS*/
+int
+quit()
+{
+ (void) call(3, bye, "bye", "fromquit");
+ Exit(EXIT_SUCCESS);
+ /*NOTREACHED*/
+ return (1);
+}
+
+/*ARGSUSED*/
+static int
+logout(argc, argv)
+ int argc;
+ char *argv[];
+{
+ send_do(TELOPT_LOGOUT, 1);
+ (void) netflush();
+ return (1);
+}
+
+
+/*
+ * The SLC command.
+ */
+
+struct slclist {
+ char *name;
+ char *help;
+ void (*handler)();
+ int arg;
+};
+
+static void slc_help();
+
+static struct slclist SlcList[] = {
+ { "export", "Use local special character definitions",
+ slc_mode_export, 0 },
+ { "import", "Use remote special character definitions",
+ slc_mode_import, 1 },
+ { "check", "Verify remote special character definitions",
+ slc_mode_import, 0 },
+ { "help", 0, slc_help, 0 },
+ { "?", "Print help information", slc_help, 0 },
+ { 0 },
+};
+
+static void
+slc_help()
+{
+ struct slclist *c;
+
+ for (c = SlcList; c->name; c++) {
+ if (c->help) {
+ if (*c->help)
+ (void) printf("%-15s %s\n", c->name, c->help);
+ else
+ (void) printf("\n");
+ }
+ }
+}
+
+static struct slclist *
+getslc(name)
+ char *name;
+{
+ return ((struct slclist *)
+ genget(name, (char **)SlcList, sizeof (struct slclist)));
+}
+
+static
+slccmd(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct slclist *c;
+
+ if (argc != 2) {
+ (void) fprintf(stderr,
+ "Need an argument to 'slc' command. 'slc ?' for help.\n");
+ return (0);
+ }
+ c = getslc(argv[1]);
+ if (c == 0) {
+ (void) fprintf(stderr,
+ "'%s': unknown argument ('slc ?' for help).\n",
+ argv[1]);
+ return (0);
+ }
+ if (Ambiguous(c)) {
+ (void) fprintf(stderr,
+ "'%s': ambiguous argument ('slc ?' for help).\n", argv[1]);
+ return (0);
+ }
+ (*c->handler)(c->arg);
+ slcstate();
+ return (1);
+}
+
+/*
+ * The ENVIRON command.
+ */
+
+struct envlist {
+ char *name;
+ char *help;
+ void (*handler)();
+ int narg;
+};
+
+static struct env_lst *env_define(unsigned char *, unsigned char *);
+static void env_undefine(unsigned char *);
+static void env_export(unsigned char *);
+static void env_unexport(unsigned char *);
+static void env_send(unsigned char *);
+#if defined(OLD_ENVIRON) && defined(ENV_HACK)
+static void env_varval(unsigned char *);
+#endif
+static void env_list(void);
+
+static void env_help(void);
+
+static struct envlist EnvList[] = {
+ { "define", "Define an environment variable",
+ (void (*)())env_define, 2 },
+ { "undefine", "Undefine an environment variable",
+ env_undefine, 1 },
+ { "export", "Mark an environment variable for automatic export",
+ env_export, 1 },
+ { "unexport", "Don't mark an environment variable for automatic export",
+ env_unexport, 1 },
+ { "send", "Send an environment variable", env_send, 1 },
+ { "list", "List the current environment variables",
+ env_list, 0 },
+#if defined(OLD_ENVIRON) && defined(ENV_HACK)
+ { "varval", "Reverse VAR and VALUE (auto, right, wrong, status)",
+ env_varval, 1 },
+#endif
+ { "help", 0, env_help, 0 },
+ { "?", "Print help information", env_help, 0 },
+ { 0 },
+};
+
+static void
+env_help()
+{
+ struct envlist *c;
+
+ for (c = EnvList; c->name; c++) {
+ if (c->help) {
+ if (*c->help)
+ (void) printf("%-15s %s\n", c->name, c->help);
+ else
+ (void) printf("\n");
+ }
+ }
+}
+
+static struct envlist *
+getenvcmd(name)
+ char *name;
+{
+ return ((struct envlist *)
+ genget(name, (char **)EnvList, sizeof (struct envlist)));
+}
+
+static int
+env_cmd(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct envlist *c;
+
+ if (argc < 2) {
+ (void) fprintf(stderr,
+ "Need an argument to 'environ' command. "
+ "'environ ?' for help.\n");
+ return (0);
+ }
+ c = getenvcmd(argv[1]);
+ if (c == 0) {
+ (void) fprintf(stderr, "'%s': unknown argument "
+ "('environ ?' for help).\n", argv[1]);
+ return (0);
+ }
+ if (Ambiguous(c)) {
+ (void) fprintf(stderr, "'%s': ambiguous argument "
+ "('environ ?' for help).\n", argv[1]);
+ return (0);
+ }
+ if (c->narg + 2 != argc) {
+ (void) fprintf(stderr,
+ "Need %s%d argument%s to 'environ %s' command. "
+ "'environ ?' for help.\n",
+ c->narg + 2 < argc ? "only " : "",
+ c->narg, c->narg == 1 ? "" : "s", c->name);
+ return (0);
+ }
+ (*c->handler)(argv[2], argv[3]);
+ return (1);
+}
+
+struct env_lst {
+ struct env_lst *next; /* pointer to next structure */
+ struct env_lst *prev; /* pointer to previous structure */
+ unsigned char *var; /* pointer to variable name */
+ unsigned char *value; /* pointer to variable value */
+ int export; /* 1 -> export with default list of variables */
+ int welldefined; /* A well defined variable */
+};
+
+static struct env_lst envlisthead;
+
+static struct env_lst *
+env_find(var)
+ unsigned char *var;
+{
+ register struct env_lst *ep;
+
+ for (ep = envlisthead.next; ep; ep = ep->next) {
+ if (strcmp((char *)ep->var, (char *)var) == 0)
+ return (ep);
+ }
+ return (NULL);
+}
+
+int
+env_init()
+{
+#ifdef lint
+ char **environ = NULL;
+#else /* lint */
+ extern char **environ;
+#endif /* lint */
+ char **epp, *cp;
+ struct env_lst *ep;
+
+ for (epp = environ; *epp; epp++) {
+ if (cp = strchr(*epp, '=')) {
+ *cp = '\0';
+
+ ep = env_define((unsigned char *)*epp,
+ (unsigned char *)cp+1);
+ if (ep == NULL)
+ return (0);
+ ep->export = 0;
+ *cp = '=';
+ }
+ }
+ /*
+ * Special case for DISPLAY variable. If it is ":0.0" or
+ * "unix:0.0", we have to get rid of "unix" and insert our
+ * hostname.
+ */
+ if (((ep = env_find((uchar_t *)"DISPLAY")) != NULL) &&
+ ((*ep->value == ':') ||
+ (strncmp((char *)ep->value, "unix:", 5) == 0))) {
+ char hbuf[MAXHOSTNAMELEN];
+ char *cp2 = strchr((char *)ep->value, ':');
+
+ if (gethostname(hbuf, MAXHOSTNAMELEN) == -1) {
+ perror("telnet: cannot get hostname");
+ return (0);
+ }
+ hbuf[MAXHOSTNAMELEN-1] = '\0';
+ cp = malloc(strlen(hbuf) + strlen(cp2) + 1);
+ if (cp == NULL) {
+ perror("telnet: cannot define DISPLAY variable");
+ return (0);
+ }
+ (void) sprintf((char *)cp, "%s%s", hbuf, cp2);
+ free(ep->value);
+ ep->value = (unsigned char *)cp;
+ }
+ /*
+ * If LOGNAME is defined, but USER is not, then add
+ * USER with the value from LOGNAME. We do this because the "accepted
+ * practice" is to always pass USER on the wire, but SVR4 uses
+ * LOGNAME by default.
+ */
+ if ((ep = env_find((uchar_t *)"LOGNAME")) != NULL &&
+ env_find((uchar_t *)"USER") == NULL) {
+ if (env_define((unsigned char *)"USER", ep->value) != NULL)
+ env_unexport((unsigned char *)"USER");
+ }
+ env_export((unsigned char *)"DISPLAY");
+ env_export((unsigned char *)"PRINTER");
+
+ return (1);
+}
+
+static struct env_lst *
+env_define(var, value)
+ unsigned char *var, *value;
+{
+ unsigned char *tmp_value;
+ unsigned char *tmp_var;
+ struct env_lst *ep;
+
+ /*
+ * Allocate copies of arguments first, to make cleanup easier
+ * in the case of allocation errors.
+ */
+ tmp_var = (unsigned char *)strdup((char *)var);
+ if (tmp_var == NULL) {
+ perror("telnet: can't copy environment variable name");
+ return (NULL);
+ }
+
+ tmp_value = (unsigned char *)strdup((char *)value);
+ if (tmp_value == NULL) {
+ free(tmp_var);
+ perror("telnet: can't copy environment variable value");
+ return (NULL);
+ }
+
+ if (ep = env_find(var)) {
+ if (ep->var)
+ free(ep->var);
+ if (ep->value)
+ free(ep->value);
+ } else {
+ ep = malloc(sizeof (struct env_lst));
+ if (ep == NULL) {
+ perror("telnet: can't define environment variable");
+ free(tmp_var);
+ free(tmp_value);
+ return (NULL);
+ }
+
+ ep->next = envlisthead.next;
+ envlisthead.next = ep;
+ ep->prev = &envlisthead;
+ if (ep->next)
+ ep->next->prev = ep;
+ }
+ ep->welldefined = opt_welldefined((char *)var);
+ ep->export = 1;
+ ep->var = tmp_var;
+ ep->value = tmp_value;
+
+ return (ep);
+}
+
+static void
+env_undefine(var)
+ unsigned char *var;
+{
+ register struct env_lst *ep;
+
+ if (ep = env_find(var)) {
+ ep->prev->next = ep->next;
+ if (ep->next)
+ ep->next->prev = ep->prev;
+ if (ep->var)
+ free(ep->var);
+ if (ep->value)
+ free(ep->value);
+ free(ep);
+ }
+}
+
+static void
+env_export(var)
+ unsigned char *var;
+{
+ register struct env_lst *ep;
+
+ if (ep = env_find(var))
+ ep->export = 1;
+}
+
+static void
+env_unexport(var)
+ unsigned char *var;
+{
+ register struct env_lst *ep;
+
+ if (ep = env_find(var))
+ ep->export = 0;
+}
+
+static void
+env_send(var)
+ unsigned char *var;
+{
+ register struct env_lst *ep;
+
+ if (my_state_is_wont(TELOPT_NEW_ENVIRON)
+#ifdef OLD_ENVIRON
+ /* old style */ && my_state_is_wont(TELOPT_OLD_ENVIRON)
+#endif
+ /* no environ */) {
+ (void) fprintf(stderr,
+ "Cannot send '%s': Telnet ENVIRON option not enabled\n",
+ var);
+ return;
+ }
+ ep = env_find(var);
+ if (ep == 0) {
+ (void) fprintf(stderr,
+ "Cannot send '%s': variable not defined\n", var);
+ return;
+ }
+ env_opt_start_info();
+ env_opt_add(ep->var);
+ env_opt_end(0);
+}
+
+static void
+env_list()
+{
+ register struct env_lst *ep;
+
+ for (ep = envlisthead.next; ep; ep = ep->next) {
+ (void) printf("%c %-20s %s\n", ep->export ? '*' : ' ',
+ ep->var, ep->value);
+ }
+}
+
+ unsigned char *
+env_default(init, welldefined)
+ int init;
+{
+ static struct env_lst *nep = NULL;
+
+ if (init) {
+ /* return value is not used */
+ nep = &envlisthead;
+ return (NULL);
+ }
+ if (nep) {
+ while ((nep = nep->next) != NULL) {
+ if (nep->export && (nep->welldefined == welldefined))
+ return (nep->var);
+ }
+ }
+ return (NULL);
+}
+
+ unsigned char *
+env_getvalue(var)
+ unsigned char *var;
+{
+ register struct env_lst *ep;
+
+ if (ep = env_find(var))
+ return (ep->value);
+ return (NULL);
+}
+
+#if defined(OLD_ENVIRON) && defined(ENV_HACK)
+static void
+env_varval(what)
+ unsigned char *what;
+{
+ extern int old_env_var, old_env_value, env_auto;
+ int len = strlen((char *)what);
+
+ if (len == 0)
+ goto unknown;
+
+ if (strncasecmp((char *)what, "status", len) == 0) {
+ if (env_auto)
+ (void) printf("%s%s", "VAR and VALUE are/will be ",
+ "determined automatically\n");
+ if (old_env_var == OLD_ENV_VAR)
+ (void) printf(
+ "VAR and VALUE set to correct definitions\n");
+ else
+ (void) printf(
+ "VAR and VALUE definitions are reversed\n");
+ } else if (strncasecmp((char *)what, "auto", len) == 0) {
+ env_auto = 1;
+ old_env_var = OLD_ENV_VALUE;
+ old_env_value = OLD_ENV_VAR;
+ } else if (strncasecmp((char *)what, "right", len) == 0) {
+ env_auto = 0;
+ old_env_var = OLD_ENV_VAR;
+ old_env_value = OLD_ENV_VALUE;
+ } else if (strncasecmp((char *)what, "wrong", len) == 0) {
+ env_auto = 0;
+ old_env_var = OLD_ENV_VALUE;
+ old_env_value = OLD_ENV_VAR;
+ } else {
+unknown:
+ (void) printf(
+ "Unknown \"varval\" command. (\"auto\", \"right\", "
+ "\"wrong\", \"status\")\n");
+ }
+}
+#endif /* OLD_ENVIRON && ENV_HACK */
+
+/*
+ * The AUTHENTICATE command.
+ */
+
+struct authlist {
+ char *name;
+ char *help;
+ int (*handler)();
+ int narg;
+};
+
+extern int auth_enable(char *);
+extern int auth_disable(char *);
+extern int auth_status(void);
+
+static int auth_help(void);
+
+static struct authlist AuthList[] = {
+ { "status",
+ "Display current status of authentication information",
+ auth_status, 0 },
+ { "disable",
+ "Disable an authentication type ('auth disable ?' for more)",
+ auth_disable, 1 },
+ { "enable",
+ "Enable an authentication type ('auth enable ?' for more)",
+ auth_enable, 1 },
+ { "help", 0, auth_help, 0 },
+ { "?", "Print help information", auth_help, 0 },
+ { 0 },
+};
+
+static int
+auth_help(void)
+{
+ struct authlist *c;
+
+ for (c = AuthList; c->name; c++) {
+ if (c->help) {
+ if (*c->help)
+ (void) printf("%-15s %s\n", c->name, c->help);
+ else
+ (void) printf("\n");
+ }
+ }
+ return (0);
+}
+
+
+static int
+auth_cmd(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct authlist *c;
+
+ if (argc < 2) {
+ (void) fprintf(stderr, "Need an argument to 'auth' "
+ "command. 'auth ?' for help.\n");
+ return (0);
+ }
+
+ c = (struct authlist *)
+ genget(argv[1], (char **)AuthList, sizeof (struct authlist));
+ if (c == 0) {
+ (void) fprintf(stderr,
+ "'%s': unknown argument ('auth ?' for help).\n",
+ argv[1]);
+ return (0);
+ }
+ if (Ambiguous(c)) {
+ (void) fprintf(stderr,
+ "'%s': ambiguous argument ('auth ?' for help).\n", argv[1]);
+ return (0);
+ }
+ if (c->narg + 2 != argc) {
+ (void) fprintf(stderr,
+ "Need %s%d argument%s to 'auth %s' command."
+ " 'auth ?' for help.\n",
+ c->narg + 2 < argc ? "only " : "",
+ c->narg, c->narg == 1 ? "" : "s", c->name);
+ return (0);
+ }
+ return ((*c->handler)(argv[2], argv[3]));
+}
+
+/*
+ * The FORWARD command.
+ */
+
+extern int forward_flags;
+
+struct forwlist {
+ char *name;
+ char *help;
+ int (*handler)();
+ int f_flags;
+};
+
+static int forw_status(void);
+static int forw_set(int);
+static int forw_help(void);
+
+static struct forwlist ForwList[] = {
+ {"status",
+ "Display current status of credential forwarding",
+ forw_status, 0},
+ {"disable",
+ "Disable credential forwarding",
+ forw_set, 0},
+ {"enable",
+ "Enable credential forwarding",
+ forw_set, OPTS_FORWARD_CREDS},
+ {"forwardable",
+ "Enable credential forwarding of "
+ "forwardable credentials",
+ forw_set, OPTS_FORWARD_CREDS | OPTS_FORWARDABLE_CREDS},
+ {"help",
+ 0,
+ forw_help, 0},
+ {"?",
+ "Print help information",
+ forw_help, 0},
+ {0},
+};
+
+static int
+forw_status(void)
+{
+ if (forward_flags & OPTS_FORWARD_CREDS) {
+ if (forward_flags & OPTS_FORWARDABLE_CREDS)
+ (void) printf(gettext(
+ "Credential forwarding of "
+ "forwardable credentials enabled\n"));
+ else
+ (void) printf(gettext(
+ "Credential forwarding enabled\n"));
+ } else
+ (void) printf(gettext("Credential forwarding disabled\n"));
+ return (0);
+}
+
+forw_set(int f_flags)
+{
+ forward_flags = f_flags;
+ return (0);
+}
+
+static int
+forw_help(void)
+{
+ struct forwlist *c;
+
+ for (c = ForwList; c->name; c++) {
+ if (c->help) {
+ if (*c->help)
+ (void) printf("%-15s %s\r\n", c->name, c->help);
+ else
+ (void) printf("\n");
+ }
+ }
+ return (0);
+}
+
+static int
+forw_cmd(int argc, char *argv[])
+{
+ struct forwlist *c;
+
+ if (argc < 2) {
+ (void) fprintf(stderr, gettext(
+ "Need an argument to 'forward' "
+ "command. 'forward ?' for help.\n"));
+ return (0);
+ }
+ c = (struct forwlist *)genget(argv[1], (char **)ForwList,
+ sizeof (struct forwlist));
+ if (c == 0) {
+ (void) fprintf(stderr, gettext(
+ "'%s': unknown argument ('forward ?' for help).\n"),
+ argv[1]);
+ return (0);
+ }
+ if (Ambiguous(c)) {
+ (void) fprintf(stderr, gettext(
+ "'%s': ambiguous argument ('forward ?' for help).\n"),
+ argv[1]);
+ return (0);
+ }
+ if (argc != 2) {
+ (void) fprintf(stderr, gettext(
+ "No arguments needed to 'forward %s' command. "
+ "'forward ?' for help.\n"), c->name);
+ return (0);
+ }
+ return ((*c->handler) (c->f_flags));
+}
+
+/*
+ * The ENCRYPT command.
+ */
+
+struct encryptlist {
+ char *name;
+ char *help;
+ int (*handler)();
+ int needconnect;
+ int minarg;
+ int maxarg;
+};
+
+static int EncryptHelp(void);
+
+static struct encryptlist EncryptList[] = {
+ { "enable", "Enable encryption. ('encrypt enable ?' for more)",
+ EncryptEnable, 1, 1, 2 },
+ { "disable", "Disable encryption. ('encrypt disable ?' for more)",
+ EncryptDisable, 0, 1, 2 },
+ { "type", "Set encryption type. ('encrypt type ?' for more)",
+ EncryptType, 0, 1, 2 },
+ { "start", "Start encryption. ('encrypt start ?' for more)",
+ EncryptStart, 1, 0, 1 },
+ { "stop", "Stop encryption. ('encrypt stop ?' for more)",
+ EncryptStop, 1, 0, 1 },
+ { "input", "Start encrypting the input stream",
+ EncryptStartInput, 1, 0, 0 },
+ { "-input", "Stop encrypting the input stream",
+ EncryptStopInput, 1, 0, 0 },
+ { "output", "Start encrypting the output stream",
+ EncryptStartOutput, 1, 0, 0 },
+ { "-output", "Stop encrypting the output stream",
+ EncryptStopOutput, 1, 0, 0 },
+
+ { "status", "Display current status of encryption information",
+ EncryptStatus, 0, 0, 0 },
+ { "help", 0,
+ EncryptHelp, 0, 0, 0 },
+ { "?", "Print help information", EncryptHelp, 0, 0, 0 },
+ { 0 },
+};
+
+static int
+EncryptHelp(void)
+{
+ struct encryptlist *c;
+
+ for (c = EncryptList; c->name; c++) {
+ if (c->help) {
+ if (*c->help)
+ (void) printf("%-15s %s\n", c->name, c->help);
+ else
+ (void) printf("\n");
+ }
+ }
+ return (0);
+}
+
+static int
+encrypt_cmd(int argc, char *argv[])
+{
+ struct encryptlist *c;
+
+ if (argc < 2) {
+ (void) fprintf(stderr, gettext(
+ "Need an argument to 'encrypt' command. "
+ "'encrypt ?' for help.\n"));
+ return (0);
+ }
+
+ c = (struct encryptlist *)
+ genget(argv[1], (char **)EncryptList, sizeof (struct encryptlist));
+ if (c == 0) {
+ (void) fprintf(stderr, gettext(
+ "'%s': unknown argument ('encrypt ?' for help).\n"),
+ argv[1]);
+ return (0);
+ }
+ if (Ambiguous(c)) {
+ (void) fprintf(stderr, gettext(
+ "'%s': ambiguous argument ('encrypt ?' for help).\n"),
+ argv[1]);
+ return (0);
+ }
+ argc -= 2;
+ if (argc < c->minarg || argc > c->maxarg) {
+ if (c->minarg == c->maxarg) {
+ (void) fprintf(stderr, gettext("Need %s%d %s "),
+ c->minarg < argc ?
+ gettext("only ") : "", c->minarg,
+ c->minarg == 1 ?
+ gettext("argument") : gettext("arguments"));
+ } else {
+ (void) fprintf(stderr,
+ gettext("Need %s%d-%d arguments "),
+ c->maxarg < argc ?
+ gettext("only ") : "", c->minarg, c->maxarg);
+ }
+ (void) fprintf(stderr, gettext(
+ "to 'encrypt %s' command. 'encrypt ?' for help.\n"),
+ c->name);
+ return (0);
+ }
+ if (c->needconnect && !connected) {
+ if (!(argc &&
+ (isprefix(argv[2], "help") || isprefix(argv[2], "?")))) {
+ (void) printf(
+ gettext("?Need to be connected first.\n"));
+ return (0);
+ }
+ }
+ return ((*c->handler)(argc > 0 ? argv[2] : 0,
+ argc > 1 ? argv[3] : 0, argc > 2 ? argv[4] : 0));
+}
+
+/*
+ * Print status about the connection.
+ */
+static
+status(int argc, char *argv[])
+{
+ if (connected) {
+ (void) printf("Connected to %s.\n", hostname);
+ if ((argc < 2) || strcmp(argv[1], "notmuch")) {
+ int mode = getconnmode();
+
+ if (my_want_state_is_will(TELOPT_LINEMODE)) {
+ (void) printf(
+ "Operating with LINEMODE option\n");
+ (void) printf(
+ "%s line editing\n", (mode&MODE_EDIT) ?
+ "Local" : "No");
+ (void) printf("%s catching of signals\n",
+ (mode&MODE_TRAPSIG) ? "Local" : "No");
+ slcstate();
+#ifdef KLUDGELINEMODE
+ } else if (kludgelinemode &&
+ my_want_state_is_dont(TELOPT_SGA)) {
+ (void) printf(
+ "Operating in obsolete linemode\n");
+#endif
+ } else {
+ (void) printf(
+ "Operating in single character mode\n");
+ if (localchars)
+ (void) printf(
+ "Catching signals locally\n");
+ }
+ (void) printf("%s character echo\n", (mode&MODE_ECHO) ?
+ "Local" : "Remote");
+ if (my_want_state_is_will(TELOPT_LFLOW))
+ (void) printf("%s flow control\n",
+ (mode&MODE_FLOW) ? "Local" : "No");
+
+ encrypt_display();
+ }
+ } else {
+ (void) printf("No connection.\n");
+ }
+ if (rlogin != _POSIX_VDISABLE)
+ (void) printf("Escape character is '%s'.\n", control(rlogin));
+ else
+ (void) printf(
+ "Escape character is '%s'.\n", esc_control(escape));
+ (void) fflush(stdout);
+ return (1);
+}
+
+/*
+ * Parse the user input (cmd_line_input) which should:
+ * - start with the target host, or with "@" or "!@" followed by at least one
+ * gateway.
+ * - each host (can be literal address or hostname) can be separated by ",",
+ * "@", or ",@".
+ * Note that the last host is the target, all the others (if any ) are the
+ * gateways.
+ *
+ * Returns: -1 if a library call fails, too many gateways, or parse
+ * error
+ * num_gw otherwise
+ * On successful return, hostname_list points to a list of hosts (last one being
+ * the target, others gateways), src_rtng_type points to the type of source
+ * routing (strict vs. loose)
+ */
+static int
+parse_input(char *cmd_line_input, char **hostname_list, uchar_t *src_rtng_type)
+{
+ char hname[MAXHOSTNAMELEN + 1];
+ char *cp;
+ int gw_count;
+ int i;
+
+ gw_count = 0;
+ cp = cmd_line_input;
+
+ /*
+ * Defining ICMD generates the Itelnet binary, the special version of
+ * telnet which is used with firewall proxy.
+ * If ICMD is defined, parse_input will treat the whole cmd_line_input
+ * as the target host and set the num_gw to 0. Therefore, none of the
+ * source routing related code paths will be executed.
+ */
+#ifndef ICMD
+ if (*cp == '@') {
+ *src_rtng_type = IPOPT_LSRR;
+ cp++;
+ } else if (*cp == '!') {
+ *src_rtng_type = IPOPT_SSRR;
+
+ /* "!" must be followed by '@' */
+ if (*(cp + 1) != '@')
+ goto parse_error;
+ cp += 2;
+ } else {
+#endif /* ICMD */
+ /* no gateways, just the target */
+ hostname_list[0] = strdup(cp);
+ if (hostname_list[0] == NULL) {
+ perror("telnet: copying host name");
+ return (-1);
+ }
+ return (0);
+#ifndef ICMD
+ }
+
+ while (*cp != '\0') {
+ /*
+ * Identify each gateway separated by ",", "@" or ",@" and
+ * store in hname[].
+ */
+ i = 0;
+ while (*cp != '@' && *cp != ',' && *cp != '\0') {
+ hname[i++] = *cp++;
+ if (i > MAXHOSTNAMELEN)
+ goto parse_error;
+ }
+ hname[i] = '\0';
+
+ /*
+ * Two consecutive delimiters which result in a 0 length hname
+ * is a parse error.
+ */
+ if (i == 0)
+ goto parse_error;
+
+ hostname_list[gw_count] = strdup(hname);
+ if (hostname_list[gw_count] == NULL) {
+ perror("telnet: copying hostname from list");
+ return (-1);
+ }
+
+ if (++gw_count > MAXMAX_GATEWAY) {
+ (void) fprintf(stderr, "telnet: too many gateways\n");
+ return (-1);
+ }
+
+ /* Jump over the next delimiter. */
+ if (*cp != '\0') {
+ /* ...gw1,@gw2... accepted */
+ if (*cp == ',' && *(cp + 1) == '@')
+ cp += 2;
+ else
+ cp++;
+ }
+ }
+
+ /* discount the target */
+ gw_count--;
+
+ /* Any input starting with '!@' or '@' must have at least one gateway */
+ if (gw_count <= 0)
+ goto parse_error;
+
+ return (gw_count);
+
+parse_error:
+ (void) printf("Bad source route option: %s\n", cmd_line_input);
+ return (-1);
+#endif /* ICMD */
+}
+
+/*
+ * Resolves the target and gateway addresses, determines what type of addresses
+ * (ALL_ADDRS, ONLY_V6, ONLY_V4) telnet will be trying to connect.
+ *
+ * Returns: pointer to resolved target if name resolutions succeed
+ * NULL if name resolutions fail or
+ * a library function call fails
+ *
+ * The last host in the hostname_list is the target. After resolving the target,
+ * determines for what type of addresses it should try to resolve gateways. It
+ * resolves gateway addresses and picks one address for each desired address
+ * type and stores in the array pointed by gw_addrsp. Also, this 'type of
+ * addresses' is pointed by addr_type argument on successful return.
+ */
+static struct addrinfo *
+resolve_hosts(char **hostname_list, int num_gw, struct gateway **gw_addrsp,
+ int *addr_type, const char *portp)
+{
+ struct gateway *gw_addrs = NULL;
+ struct gateway *gw;
+ /* whether we already picked an IPv4 address for the current gateway */
+ boolean_t got_v4_addr;
+ boolean_t got_v6_addr;
+ /* whether we need to get an IPv4 address for the current gateway */
+ boolean_t need_v4_addr = B_FALSE;
+ boolean_t need_v6_addr = B_FALSE;
+ int res_failed_at4; /* save which gateway failed to resolve */
+ int res_failed_at6;
+ boolean_t is_v4mapped;
+ struct in6_addr *v6addrp;
+ struct in_addr *v4addrp;
+ int error_num;
+ int i;
+ int rc;
+ struct addrinfo *res, *host, *gateway, *addr;
+ struct addrinfo hints;
+
+ *addr_type = ALL_ADDRS;
+
+ memset(&hints, 0, sizeof (hints));
+ hints.ai_flags = AI_CANONNAME; /* used for config files, diags */
+ hints.ai_socktype = SOCK_STREAM;
+ rc = getaddrinfo(hostname_list[num_gw],
+ (portp != NULL) ? portp : "telnet", &hints, &res);
+ if (rc != 0) {
+ if (hostname_list[num_gw] != NULL &&
+ *hostname_list[num_gw] != '\0')
+ (void) fprintf(stderr, "%s: ", hostname_list[num_gw]);
+ (void) fprintf(stderr, "%s\n", gai_strerror(rc));
+ return (NULL);
+ }
+
+ /*
+ * Let's see what type of addresses we got for the target. This
+ * determines what type of addresses we'd like to resolve gateways
+ * later.
+ */
+ for (host = res; host != NULL; host = host->ai_next) {
+ struct sockaddr_in6 *s6;
+
+ s6 = (struct sockaddr_in6 *)host->ai_addr;
+
+ if (host->ai_addr->sa_family == AF_INET ||
+ IN6_IS_ADDR_V4MAPPED(&s6->sin6_addr))
+ need_v4_addr = B_TRUE;
+ else
+ need_v6_addr = B_TRUE;
+
+ /*
+ * Let's stop after seeing we need both IPv6 and IPv4.
+ */
+ if (need_v4_addr && need_v6_addr)
+ break;
+ }
+
+ if (num_gw > 0) {
+ /*
+ * In the prepare_optbuf(), we'll store the IPv4 address of the
+ * target in the last slot of gw_addrs array. Therefore we need
+ * space for num_gw+1 hosts.
+ */
+ gw_addrs = calloc(num_gw + 1, sizeof (struct gateway));
+ if (gw_addrs == NULL) {
+ perror("telnet: calloc");
+ freeaddrinfo(res);
+ return (NULL);
+ }
+ }
+
+ /*
+ * Now we'll go through all the gateways and try to resolve them to
+ * the desired address types.
+ */
+ gw = gw_addrs;
+
+ /* -1 means 'no address resolution failure yet' */
+ res_failed_at4 = -1;
+ res_failed_at6 = -1;
+ for (i = 0; i < num_gw; i++) {
+ rc = getaddrinfo(hostname_list[i], NULL, NULL, &gateway);
+ if (rc != 0) {
+ if (hostname_list[i] != NULL &&
+ *hostname_list[i] != '\0')
+ (void) fprintf(stderr, "%s: ",
+ hostname_list[i]);
+ (void) fprintf(stderr, "bad address\n");
+ return (NULL);
+ }
+
+ /*
+ * Initially we have no address of any type for this gateway.
+ */
+ got_v6_addr = B_FALSE;
+ got_v4_addr = B_FALSE;
+
+ /*
+ * Let's go through all the addresses of this gateway.
+ * Use the first address which matches the needed family.
+ */
+ for (addr = gateway; addr != NULL; addr = addr->ai_next) {
+ /*LINTED*/
+ v6addrp = &((struct sockaddr_in6 *)addr->ai_addr)->
+ sin6_addr;
+ v4addrp = &((struct sockaddr_in *)addr->ai_addr)->
+ sin_addr;
+
+ if (addr->ai_family == AF_INET6)
+ is_v4mapped = IN6_IS_ADDR_V4MAPPED(v6addrp);
+ else
+ is_v4mapped = B_FALSE;
+
+ /*
+ * If we need to determine an IPv4 address and haven't
+ * found one yet and this is a IPv4-mapped IPv6 address,
+ * then bingo!
+ */
+ if (need_v4_addr && !got_v4_addr) {
+ if (is_v4mapped) {
+ IN6_V4MAPPED_TO_INADDR(v6addrp,
+ &gw->gw_addr);
+ got_v4_addr = B_TRUE;
+ } else if (addr->ai_family = AF_INET) {
+ gw->gw_addr = *v4addrp;
+ got_v4_addr = B_TRUE;
+ }
+ }
+
+ if (need_v6_addr && !got_v6_addr &&
+ addr->ai_family == AF_INET6) {
+ gw->gw_addr6 = *v6addrp;
+ got_v6_addr = B_TRUE;
+ }
+
+ /*
+ * Let's stop if we got all what we looked for.
+ */
+ if ((!need_v4_addr || got_v4_addr) &&
+ (!need_v6_addr || got_v6_addr))
+ break;
+ }
+
+ /*
+ * We needed an IPv4 address for this gateway but couldn't
+ * find one.
+ */
+ if (need_v4_addr && !got_v4_addr) {
+ res_failed_at4 = i;
+ /*
+ * Since we couldn't resolve a gateway to IPv4 address
+ * we can't use IPv4 at all. Therefore we no longer
+ * need IPv4 addresses for any of the gateways.
+ */
+ need_v4_addr = B_FALSE;
+ }
+
+ if (need_v6_addr && !got_v6_addr) {
+ res_failed_at6 = i;
+ need_v6_addr = B_FALSE;
+ }
+
+ /*
+ * If some gateways don't resolve to any of the desired
+ * address types, we fail.
+ */
+ if (!need_v4_addr && !need_v6_addr) {
+ if (res_failed_at6 != -1) {
+ (void) fprintf(stderr,
+ "%s: Host doesn't have any IPv6 address\n",
+ hostname_list[res_failed_at6]);
+ }
+ if (res_failed_at4 != -1) {
+ (void) fprintf(stderr,
+ "%s: Host doesn't have any IPv4 address\n",
+ hostname_list[res_failed_at4]);
+ }
+ free(gw_addrs);
+ return (NULL);
+ }
+
+ gw++;
+ }
+
+ *gw_addrsp = gw_addrs;
+
+ /*
+ * When we get here, need_v4_addr and need_v6_addr have their final
+ * values based on the name resolution of the target and gateways.
+ */
+ if (need_v4_addr && need_v6_addr)
+ *addr_type = ALL_ADDRS;
+ else if (need_v4_addr && !need_v6_addr)
+ *addr_type = ONLY_V4;
+ else if (!need_v4_addr && need_v6_addr)
+ *addr_type = ONLY_V6;
+
+ return (res);
+}
+
+
+/*
+ * Initializes the buffer pointed by opt_bufpp for a IPv4 option of type
+ * src_rtng_type using the gateway addresses stored in gw_addrs. If no buffer
+ * is passed, it allocates one. If a buffer is passed, checks if it's big
+ * enough.
+ * On return opt_buf_len points to the buffer length which we need later for the
+ * setsockopt() call, and opt_bufpp points to the newly allocated or already
+ * passed buffer. Returns B_FALSE if a library function call fails or passed
+ * buffer is not big enough, B_TRUE otherwise.
+ */
+static boolean_t
+prepare_optbuf(struct gateway *gw_addrs, int num_gw, char **opt_bufpp,
+ size_t *opt_buf_len, struct in_addr *target, uchar_t src_rtng_type)
+{
+ struct ip_sourceroute *sr_opt;
+ size_t needed_buflen;
+ int i;
+
+ /*
+ * We have (num_gw + 1) IP addresses in the buffer because the number
+ * of gateway addresses we put in the option buffer includes the target
+ * address.
+ * At the time of setsockopt() call, passed option length needs to be
+ * multiple of 4 bytes. Therefore we need one IPOPT_NOP before (or
+ * after) IPOPT_LSRR.
+ * 1 = preceding 1 byte of IPOPT_NOP
+ * 3 = 1 (code) + 1 (len) + 1 (ptr)
+ */
+ needed_buflen = 1 + 3 + (num_gw + 1) * sizeof (struct in_addr);
+
+ if (*opt_bufpp != NULL) {
+ /* check if the passed buffer is big enough */
+ if (*opt_buf_len < needed_buflen) {
+ (void) fprintf(stderr,
+ "telnet: buffer too small for IPv4 source routing "
+ "option\n");
+ return (B_FALSE);
+ }
+ } else {
+ *opt_bufpp = malloc(needed_buflen);
+ if (*opt_bufpp == NULL) {
+ perror("telnet: malloc");
+ return (B_FALSE);
+ }
+ }
+
+ *opt_buf_len = needed_buflen;
+
+ /* final hop is the target */
+ gw_addrs[num_gw].gw_addr = *target;
+
+ *opt_bufpp[0] = IPOPT_NOP;
+ /* IPOPT_LSRR starts right after IPOPT_NOP */
+ sr_opt = (struct ip_sourceroute *)(*opt_bufpp + 1);
+ sr_opt->ipsr_code = src_rtng_type;
+ /* discount the 1 byte of IPOPT_NOP */
+ sr_opt->ipsr_len = needed_buflen - 1;
+ sr_opt->ipsr_ptr = IPOPT_MINOFF;
+
+ /* copy the gateways into the optlist */
+ for (i = 0; i < num_gw + 1; i++) {
+ (void) bcopy(&gw_addrs[i].gw_addr, &sr_opt->ipsr_addrs[i],
+ sizeof (struct in_addr));
+ }
+
+ return (B_TRUE);
+}
+
+/*
+ * Initializes the buffer pointed by opt_bufpp for a IPv6 routing header option
+ * using the gateway addresses stored in gw_addrs. If no buffer is passed, it
+ * allocates one. If a buffer is passed, checks if it's big enough.
+ * On return opt_buf_len points to the buffer length which we need later for the
+ * setsockopt() call, and opt_bufpp points to the newly allocated or already
+ * passed buffer. Returns B_FALSE if a library function call fails or passed
+ * buffer is not big enough, B_TRUE otherwise.
+ */
+static boolean_t
+prepare_optbuf6(struct gateway *gw_addrs, int num_gw, char **opt_bufpp,
+ size_t *opt_buf_len)
+{
+ char *opt_bufp;
+ size_t needed_buflen;
+ int i;
+
+ needed_buflen = inet6_rth_space(IPV6_RTHDR_TYPE_0, num_gw);
+
+ if (*opt_bufpp != NULL) {
+ /* check if the passed buffer is big enough */
+ if (*opt_buf_len < needed_buflen) {
+ (void) fprintf(stderr,
+ "telnet: buffer too small for IPv6 routing "
+ "header option\n");
+ return (B_FALSE);
+ }
+ } else {
+ *opt_bufpp = malloc(needed_buflen);
+ if (*opt_bufpp == NULL) {
+ perror("telnet: malloc");
+ return (B_FALSE);
+ }
+ }
+ *opt_buf_len = needed_buflen;
+ opt_bufp = *opt_bufpp;
+
+ /*
+ * Initialize the buffer to be used for IPv6 routing header type 0.
+ */
+ if (inet6_rth_init(opt_bufp, needed_buflen, IPV6_RTHDR_TYPE_0,
+ num_gw) == NULL) {
+ perror("telnet: inet6_rth_init");
+ return (B_FALSE);
+ }
+
+ /*
+ * Add gateways one by one.
+ */
+ for (i = 0; i < num_gw; i++) {
+ if (inet6_rth_add(opt_bufp, &gw_addrs[i].gw_addr6) == -1) {
+ perror("telnet: inet6_rth_add");
+ return (B_FALSE);
+ }
+ }
+
+ /* successful operation */
+ return (B_TRUE);
+}
+
+int
+tn(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct addrinfo *host = NULL;
+ struct addrinfo *h;
+ struct sockaddr_in6 sin6;
+ struct sockaddr_in sin;
+ struct in6_addr addr6;
+ struct in_addr addr;
+ void *addrp;
+ struct gateway *gw_addrs;
+ char *hostname_list[MAXMAX_GATEWAY + 1] = {NULL};
+ char *opt_buf6 = NULL; /* used for IPv6 routing header */
+ size_t opt_buf_len6 = 0;
+ uchar_t src_rtng_type; /* type of IPv4 source routing */
+ struct servent *sp = 0;
+ char *opt_buf = NULL; /* used for IPv4 source routing */
+ size_t opt_buf_len = 0;
+ char *cmd;
+ char *hostp = NULL;
+ char *portp = NULL;
+ char *user = NULL;
+#ifdef ICMD
+ char *itelnet_host;
+ char *real_host;
+ unsigned short dest_port;
+#endif /* ICMD */
+ /*
+ * The two strings at the end of this function are 24 and 39
+ * characters long (minus the %.*s in the format strings). Add
+ * one for the null terminator making the longest print string 40.
+ */
+ char buf[MAXHOSTNAMELEN+40];
+ /*
+ * In the case of ICMD defined, dest_port will contain the real port
+ * we are trying to telnet to, and target_port will contain
+ * "telnet-passthru" port.
+ */
+ unsigned short target_port;
+ char abuf[INET6_ADDRSTRLEN];
+ int num_gw;
+ int ret_val;
+ boolean_t is_v4mapped;
+ /*
+ * Type of addresses we'll try to connect to (ALL_ADDRS, ONLY_V6,
+ * ONLY_V4).
+ */
+ int addr_type;
+
+ /* clear the socket address prior to use */
+ (void) memset(&sin6, '\0', sizeof (sin6));
+ sin6.sin6_family = AF_INET6;
+
+ (void) memset(&sin, '\0', sizeof (sin));
+ sin.sin_family = AF_INET;
+
+ if (connected) {
+ (void) printf("?Already connected to %s\n", hostname);
+ return (0);
+ }
+#ifdef ICMD
+ itelnet_host = getenv("INTERNET_HOST");
+ if (itelnet_host == NULL || itelnet_host[0] == '\0') {
+ (void) printf("INTERNET_HOST environment variable undefined\n");
+ goto tn_exit;
+ }
+#endif
+ if (argc < 2) {
+ (void) printf("(to) ");
+ if (GetAndAppendString(&line, &linesize, "open ",
+ stdin) == NULL) {
+ if (!feof(stdin)) {
+ perror("telnet");
+ goto tn_exit;
+ }
+ }
+ makeargv();
+ argc = margc;
+ argv = margv;
+ }
+ cmd = *argv;
+ --argc; ++argv;
+ while (argc) {
+ if (isprefix(*argv, "help") == 4 || isprefix(*argv, "?") == 1)
+ goto usage;
+ if (strcmp(*argv, "-l") == 0) {
+ --argc; ++argv;
+ if (argc == 0)
+ goto usage;
+ user = *argv++;
+ --argc;
+ continue;
+ }
+ if (strcmp(*argv, "-a") == 0) {
+ --argc; ++argv;
+ autologin = autologin_set = 1;
+ continue;
+ }
+ if (hostp == 0) {
+ hostp = *argv++;
+ --argc;
+ continue;
+ }
+ if (portp == 0) {
+ portp = *argv++;
+ --argc;
+ /*
+ * Do we treat this like a telnet port or raw?
+ */
+ if (*portp == '-') {
+ portp++;
+ telnetport = 1;
+ } else
+ telnetport = 0;
+ continue;
+ }
+usage:
+ (void) printf(
+ "usage: %s [-l user] [-a] host-name [port]\n", cmd);
+ goto tn_exit;
+ }
+ if (hostp == 0)
+ goto usage;
+
+#ifdef ICMD
+ /*
+ * For setup phase treat the relay host as the target host.
+ */
+ real_host = hostp;
+ hostp = itelnet_host;
+#endif
+ num_gw = parse_input(hostp, hostname_list, &src_rtng_type);
+ if (num_gw < 0) {
+ goto tn_exit;
+ }
+
+ /* Last host in the hostname_list is the target */
+ hostp = hostname_list[num_gw];
+
+ host = resolve_hosts(hostname_list, num_gw, &gw_addrs, &addr_type,
+ portp);
+ if (host == NULL) {
+ goto tn_exit;
+ }
+
+ /*
+ * Check if number of gateways is less than max. available
+ */
+ if ((addr_type == ALL_ADDRS || addr_type == ONLY_V6) &&
+ num_gw > MAX_GATEWAY6) {
+ (void) fprintf(stderr, "telnet: too many IPv6 gateways\n");
+ goto tn_exit;
+ }
+
+ if ((addr_type == ALL_ADDRS || addr_type == ONLY_V4) &&
+ num_gw > MAX_GATEWAY) {
+ (void) fprintf(stderr, "telnet: too many IPv4 gateways\n");
+ goto tn_exit;
+ }
+
+ /*
+ * If we pass a literal IPv4 address to getaddrinfo(), in the
+ * returned addrinfo structure, hostname is the IPv4-mapped IPv6
+ * address string. We prefer to preserve the literal IPv4 address
+ * string as the hostname. Also, if the hostname entered by the
+ * user is IPv4-mapped IPv6 address, we'll downgrade it to IPv4
+ * address.
+ */
+ if (inet_addr(hostp) != (in_addr_t)-1) {
+ /* this is a literal IPv4 address */
+ (void) strlcpy(_hostname, hostp, sizeof (_hostname));
+ } else if ((inet_pton(AF_INET6, hostp, &addr6) > 0) &&
+ IN6_IS_ADDR_V4MAPPED(&addr6)) {
+ /* this is a IPv4-mapped IPv6 address */
+ IN6_V4MAPPED_TO_INADDR(&addr6, &addr);
+ (void) inet_ntop(AF_INET, &addr, _hostname, sizeof (_hostname));
+ } else {
+ (void) strlcpy(_hostname, host->ai_canonname,
+ sizeof (_hostname));
+ }
+ hostname = _hostname;
+
+ if (portp == NULL) {
+ telnetport = 1;
+ }
+
+ if (host->ai_family == AF_INET) {
+ target_port = ((struct sockaddr_in *)(host->ai_addr))->sin_port;
+ } else {
+ target_port = ((struct sockaddr_in6 *)(host->ai_addr))
+ ->sin6_port;
+ }
+
+#ifdef ICMD
+ /*
+ * Since we pass the port number as an ascii string to the proxy,
+ * we need it in host format.
+ */
+ dest_port = ntohs(target_port);
+ sp = getservbyname("telnet-passthru", "tcp");
+ if (sp == 0) {
+ (void) fprintf(stderr,
+ "telnet: tcp/telnet-passthru: unknown service\n");
+ goto tn_exit;
+ }
+ target_port = sp->s_port;
+#endif
+ h = host;
+
+ /*
+ * For IPv6 source routing, we need to initialize option buffer only
+ * once.
+ */
+ if (num_gw > 0 && (addr_type == ALL_ADDRS || addr_type == ONLY_V6)) {
+ if (!prepare_optbuf6(gw_addrs, num_gw, &opt_buf6,
+ &opt_buf_len6)) {
+ goto tn_exit;
+ }
+ }
+
+ /*
+ * We procure the Kerberos config files options only
+ * if the user has choosen Krb5 authentication.
+ */
+ if (krb5auth_flag > 0) {
+ krb5_profile_get_options(hostname, telnet_krb5_realm,
+ config_file_options);
+ }
+
+ if (encrypt_flag) {
+ extern boolean_t auth_enable_encrypt;
+ if (krb5_privacy_allowed()) {
+ encrypt_auto(1);
+ decrypt_auto(1);
+ wantencryption = B_TRUE;
+ autologin = 1;
+ auth_enable_encrypt = B_TRUE;
+ } else {
+ (void) fprintf(stderr, gettext(
+ "%s:Encryption not supported.\n"), prompt);
+ exit(1);
+ }
+ }
+
+ if (forward_flag && forwardable_flag) {
+ (void) fprintf(stderr, gettext(
+ "Error in krb5 configuration file. "
+ "Both forward and forwardable are set.\n"));
+ exit(1);
+ }
+ if (forwardable_flag) {
+ forward_flags |= OPTS_FORWARD_CREDS | OPTS_FORWARDABLE_CREDS;
+ } else if (forward_flag)
+ forward_flags |= OPTS_FORWARD_CREDS;
+
+
+ do {
+ /*
+ * Search for an address of desired type in the IP address list
+ * of the target.
+ */
+ while (h != NULL) {
+ struct sockaddr_in6 *addr;
+
+ addr = (struct sockaddr_in6 *)h->ai_addr;
+
+ if (h->ai_family == AF_INET6)
+ is_v4mapped =
+ IN6_IS_ADDR_V4MAPPED(&addr->sin6_addr);
+ else
+ is_v4mapped = B_FALSE;
+
+ if (addr_type == ALL_ADDRS ||
+ (addr_type == ONLY_V6 &&
+ h->ai_family == AF_INET6) ||
+ (addr_type == ONLY_V4 &&
+ (h->ai_family == AF_INET || is_v4mapped)))
+ break;
+
+ /* skip undesired typed addresses */
+ h = h->ai_next;
+ }
+
+ if (h == NULL) {
+ fprintf(stderr,
+ "telnet: Unable to connect to remote host");
+ goto tn_exit;
+ }
+
+ /*
+ * We need to open a socket with a family matching the type of
+ * address we are trying to connect to. This is because we
+ * deal with IPv4 options and IPv6 extension headers.
+ */
+ if (h->ai_family == AF_INET) {
+ addrp = &((struct sockaddr_in *)(h->ai_addr))->sin_addr;
+ ((struct sockaddr_in *)(h->ai_addr))->sin_port =
+ target_port;
+ } else {
+ addrp = &((struct sockaddr_in6 *)(h->ai_addr))
+ ->sin6_addr;
+ ((struct sockaddr_in6 *)(h->ai_addr))->sin6_port =
+ target_port;
+ }
+
+ (void) printf("Trying %s...\n", inet_ntop(h->ai_family,
+ addrp, abuf, sizeof (abuf)));
+
+ net = socket(h->ai_family, SOCK_STREAM, 0);
+
+ if (net < 0) {
+ perror("telnet: socket");
+ goto tn_exit;
+ }
+#ifndef ICMD
+ if (num_gw > 0) {
+ if (h->ai_family == AF_INET || is_v4mapped) {
+ if (!prepare_optbuf(gw_addrs, num_gw, &opt_buf,
+ &opt_buf_len, addrp, src_rtng_type)) {
+ goto tn_exit;
+ }
+
+ if (setsockopt(net, IPPROTO_IP, IP_OPTIONS,
+ opt_buf, opt_buf_len) < 0)
+ perror("setsockopt (IP_OPTIONS)");
+ } else {
+ if (setsockopt(net, IPPROTO_IPV6, IPV6_RTHDR,
+ opt_buf6, opt_buf_len6) < 0)
+ perror("setsockopt (IPV6_RTHDR)");
+ }
+ }
+#endif
+#if defined(USE_TOS)
+ if (is_v4mapped) {
+ if (tos < 0)
+ tos = 020; /* Low Delay bit */
+ if (tos &&
+ (setsockopt(net, IPPROTO_IP, IP_TOS,
+ &tos, sizeof (int)) < 0) &&
+ (errno != ENOPROTOOPT))
+ perror("telnet: setsockopt (IP_TOS) (ignored)");
+ }
+#endif /* defined(USE_TOS) */
+
+ if (debug && SetSockOpt(net, SOL_SOCKET, SO_DEBUG, 1) < 0) {
+ perror("setsockopt (SO_DEBUG)");
+ }
+
+ ret_val = connect(net, h->ai_addr, h->ai_addrlen);
+
+ /*
+ * If failed, try the next address of the target.
+ */
+ if (ret_val < 0) {
+ Close(&net);
+ if (h->ai_next != NULL) {
+
+ int oerrno = errno;
+
+ (void) fprintf(stderr,
+ "telnet: connect to address %s: ", abuf);
+ errno = oerrno;
+ perror((char *)0);
+
+ h = h->ai_next;
+ continue;
+ }
+ perror("telnet: Unable to connect to remote host");
+ goto tn_exit;
+ }
+ connected++;
+ } while (connected == 0);
+ freeaddrinfo(host);
+ host = NULL;
+#ifdef ICMD
+ /*
+ * Do initial protocol to connect to farther end...
+ */
+ {
+ char buf[1024];
+ (void) sprintf(buf, "%s %d\n", real_host, (int)dest_port);
+ write(net, buf, strlen(buf));
+ }
+#endif
+ if (cmdrc(hostp, hostname) != 0)
+ goto tn_exit;
+ FreeHostnameList(hostname_list);
+ if (autologin && user == NULL) {
+ struct passwd *pw;
+
+ user = getenv("LOGNAME");
+ if (user == NULL ||
+ ((pw = getpwnam(user)) != NULL) &&
+ pw->pw_uid != getuid()) {
+ if (pw = getpwuid(getuid()))
+ user = pw->pw_name;
+ else
+ user = NULL;
+ }
+ }
+
+ if (user) {
+ if (env_define((unsigned char *)"USER", (unsigned char *)user))
+ env_export((unsigned char *)"USER");
+ else {
+ /* Clean up and exit. */
+ Close(&net);
+ (void) snprintf(buf, sizeof (buf),
+ "Connection to %.*s closed.\n",
+ MAXHOSTNAMELEN, hostname);
+ ExitString(buf, EXIT_FAILURE);
+
+ /* NOTREACHED */
+ }
+ }
+ (void) call(3, status, "status", "notmuch");
+ if (setjmp(peerdied) == 0)
+ telnet(user);
+
+ Close(&net);
+
+ (void) snprintf(buf, sizeof (buf),
+ "Connection to %.*s closed by foreign host.\n",
+ MAXHOSTNAMELEN, hostname);
+ ExitString(buf, EXIT_FAILURE);
+
+ /*NOTREACHED*/
+
+tn_exit:
+ FreeHostnameList(hostname_list);
+ Close(&net);
+ connected = 0;
+ if (host != NULL)
+ freeaddrinfo(host);
+ return (0);
+}
+
+#define HELPINDENT (sizeof ("connect"))
+
+static char openhelp[] = "connect to a site";
+static char closehelp[] = "close current connection";
+static char logouthelp[] =
+ "forcibly logout remote user and close the connection";
+static char quithelp[] = "exit telnet";
+static char statushelp[] = "print status information";
+static char helphelp[] = "print help information";
+static char sendhelp[] =
+ "transmit special characters ('send ?' for more)";
+static char sethelp[] = "set operating parameters ('set ?' for more)";
+static char unsethelp[] = "unset operating parameters ('unset ?' for more)";
+static char togglestring[] =
+ "toggle operating parameters ('toggle ?' for more)";
+static char slchelp[] = "change state of special charaters ('slc ?' for more)";
+static char displayhelp[] = "display operating parameters";
+static char authhelp[] =
+ "turn on (off) authentication ('auth ?' for more)";
+static char forwardhelp[] =
+ "turn on (off) credential forwarding ('forward ?' for more)";
+static char encrypthelp[] =
+ "turn on (off) encryption ('encrypt ?' for more)";
+static char zhelp[] = "suspend telnet";
+static char shellhelp[] = "invoke a subshell";
+static char envhelp[] = "change environment variables ('environ ?' for more)";
+static char modestring[] =
+ "try to enter line or character mode ('mode ?' for more)";
+
+static int help();
+
+static Command cmdtab[] = {
+ { "close", closehelp, bye, 1 },
+ { "logout", logouthelp, logout, 1 },
+ { "display", displayhelp, display, 0 },
+ { "mode", modestring, modecmd, 0 },
+ { "open", openhelp, tn, 0 },
+ { "quit", quithelp, quit, 0 },
+ { "send", sendhelp, sendcmd, 0 },
+ { "set", sethelp, setcmd, 0 },
+ { "unset", unsethelp, unsetcmd, 0 },
+ { "status", statushelp, status, 0 },
+ { "toggle", togglestring, toggle, 0 },
+ { "slc", slchelp, slccmd, 0 },
+ { "auth", authhelp, auth_cmd, 0 },
+ { "encrypt", encrypthelp, encrypt_cmd, 0 },
+ { "forward", forwardhelp, forw_cmd, 0 },
+ { "z", zhelp, suspend, 0 },
+ { "!", shellhelp, shell, 0 },
+ { "environ", envhelp, env_cmd, 0 },
+ { "?", helphelp, help, 0 },
+ 0
+};
+
+
+static Command cmdtab2[] = {
+ { "help", 0, help, 0 },
+ { "escape", 0, setescape, 0 },
+ { "crmod", 0, togcrmod, 0 },
+ 0
+};
+
+
+/*
+ * Call routine with argc, argv set from args.
+ * Uses /usr/include/stdarg.h
+ */
+#define MAXVARGS 100
+/*VARARGS1*/
+static void
+call(int n_ptrs, ...)
+{
+ va_list ap;
+ typedef int (*intrtn_t)();
+ intrtn_t routine;
+ char *args[MAXVARGS+1]; /* leave 1 for trailing NULL */
+ int argno = 0;
+
+ if (n_ptrs > MAXVARGS)
+ n_ptrs = MAXVARGS;
+ va_start(ap, MAXVARGS);
+
+ routine = (va_arg(ap, intrtn_t)); /* extract the routine's name */
+ n_ptrs--;
+
+ while (argno < n_ptrs) /* extract the routine's args */
+ args[argno++] = va_arg(ap, char *);
+ args[argno] = NULL; /* NULL terminate for good luck */
+ va_end(ap);
+
+ (*routine)(argno, args);
+}
+
+
+static Command *
+getcmd(name)
+ char *name;
+{
+ Command *cm;
+
+ if (cm = (Command *) genget(name, (char **)cmdtab, sizeof (Command)))
+ return (cm);
+ return (Command *) genget(name, (char **)cmdtab2, sizeof (Command));
+}
+
+void
+command(top, tbuf, cnt)
+ int top;
+ char *tbuf;
+ int cnt;
+{
+ Command *c;
+
+ setcommandmode();
+ if (!top) {
+ (void) putchar('\n');
+ } else {
+ (void) signal(SIGINT, SIG_DFL);
+ (void) signal(SIGQUIT, SIG_DFL);
+ }
+ for (;;) {
+ if (rlogin == _POSIX_VDISABLE)
+ (void) printf("%s> ", prompt);
+ if (tbuf) {
+ char *cp;
+ if (AllocStringBuffer(&line, &linesize, cnt) == NULL)
+ goto command_exit;
+ cp = line;
+ while (cnt > 0 && (*cp++ = *tbuf++) != '\n')
+ cnt--;
+ tbuf = 0;
+ if (cp == line || *--cp != '\n' || cp == line)
+ goto getline;
+ *cp = '\0';
+ if (rlogin == _POSIX_VDISABLE)
+ (void) printf("%s\n", line);
+ } else {
+getline:
+ if (rlogin != _POSIX_VDISABLE)
+ (void) printf("%s> ", prompt);
+ if (GetString(&line, &linesize, stdin) == NULL) {
+ if (!feof(stdin))
+ perror("telnet");
+ (void) quit();
+ /*NOTREACHED*/
+ break;
+ }
+ }
+ if (line[0] == 0)
+ break;
+ makeargv();
+ if (margv[0] == 0) {
+ break;
+ }
+ c = getcmd(margv[0]);
+ if (Ambiguous(c)) {
+ (void) printf("?Ambiguous command\n");
+ continue;
+ }
+ if (c == 0) {
+ (void) printf("?Invalid command\n");
+ continue;
+ }
+ if (c->needconnect && !connected) {
+ (void) printf("?Need to be connected first.\n");
+ continue;
+ }
+ if ((*c->handler)(margc, margv)) {
+ break;
+ }
+ }
+command_exit:
+ if (!top) {
+ if (!connected) {
+ longjmp(toplevel, 1);
+ /*NOTREACHED*/
+ }
+ setconnmode(0);
+ }
+}
+
+/*
+ * Help command.
+ */
+ static
+help(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register Command *c;
+
+ if (argc == 1) {
+ (void) printf(
+ "Commands may be abbreviated. Commands are:\n\n");
+ for (c = cmdtab; c->name; c++)
+ if (c->help) {
+ (void) printf("%-*s\t%s\n", HELPINDENT,
+ c->name, c->help);
+ }
+ (void) printf("<return>\tleave command mode\n");
+ return (0);
+ }
+ while (--argc > 0) {
+ register char *arg;
+ arg = *++argv;
+ c = getcmd(arg);
+ if (Ambiguous(c))
+ (void) printf("?Ambiguous help command %s\n", arg);
+ else if (c == (Command *)0)
+ (void) printf("?Invalid help command %s\n", arg);
+ else if (c->help) {
+ (void) printf("%s\n", c->help);
+ } else {
+ (void) printf("No additional help on %s\n", arg);
+ }
+ }
+ return (0);
+}
+
+static char *rcname = NULL;
+#define TELNETRC_NAME "telnetrc"
+#define TELNETRC_COMP "/." TELNETRC_NAME
+
+static int
+cmdrc(char *m1, char *m2)
+{
+ Command *c;
+ FILE *rcfile = NULL;
+ int gotmachine = 0;
+ int l1 = strlen(m1);
+ int l2 = strlen(m2);
+ char m1save[MAXHOSTNAMELEN];
+ int ret = 0;
+ char def[] = "DEFAULT";
+
+ if (skiprc)
+ goto cmdrc_exit;
+
+ doing_rc = 1;
+
+ (void) strlcpy(m1save, m1, sizeof (m1save));
+ m1 = m1save;
+
+ if (rcname == NULL) {
+ char *homedir;
+ unsigned rcbuflen;
+
+ if ((homedir = getenv("HOME")) == NULL)
+ homedir = "";
+
+ rcbuflen = strlen(homedir) + strlen(TELNETRC_COMP) + 1;
+ if ((rcname = malloc(rcbuflen)) == NULL) {
+ perror("telnet: can't process " TELNETRC_NAME);
+ ret = 1;
+ goto cmdrc_exit;
+ }
+ (void) strcpy(rcname, homedir);
+ (void) strcat(rcname, TELNETRC_COMP);
+ }
+
+ if ((rcfile = fopen(rcname, "r")) == NULL)
+ goto cmdrc_exit;
+
+ for (;;) {
+ if (GetString(&line, &linesize, rcfile) == NULL) {
+ if (!feof(rcfile)) {
+ perror("telnet: error reading " TELNETRC_NAME);
+ ret = 1;
+ goto cmdrc_exit;
+ }
+ break;
+ }
+ if (line[0] == 0)
+ continue;
+ if (line[0] == '#')
+ continue;
+ if (gotmachine) {
+ if (!isspace(line[0]))
+ gotmachine = 0;
+ }
+ if (gotmachine == 0) {
+ if (isspace(line[0]))
+ continue;
+ if (strncasecmp(line, m1, l1) == 0)
+ (void) strcpy(line, &line[l1]);
+ else if (strncasecmp(line, m2, l2) == 0)
+ (void) strcpy(line, &line[l2]);
+ else if (strncasecmp(line, def, sizeof (def) - 1) == 0)
+ (void) strcpy(line, &line[sizeof (def) - 1]);
+ else
+ continue;
+ if (line[0] != ' ' && line[0] != '\t' &&
+ line[0] != '\n')
+ continue;
+ gotmachine = 1;
+ }
+ makeargv();
+ if (margv[0] == 0)
+ continue;
+ c = getcmd(margv[0]);
+ if (Ambiguous(c)) {
+ (void) printf("?Ambiguous command: %s\n", margv[0]);
+ continue;
+ }
+ if (c == 0) {
+ (void) printf("?Invalid command: %s\n", margv[0]);
+ continue;
+ }
+ /*
+ * This should never happen...
+ */
+ if (c->needconnect && !connected) {
+ (void) printf("?Need to be connected first for %s.\n",
+ margv[0]);
+ continue;
+ }
+ (*c->handler)(margc, margv);
+ }
+cmdrc_exit:
+ if (rcfile != NULL)
+ (void) fclose(rcfile);
+ doing_rc = 0;
+
+ return (ret);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/telnet/defines.h b/usr/src/cmd/cmd-inet/usr.bin/telnet/defines.h
new file mode 100644
index 0000000000..a288d1ef89
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/telnet/defines.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 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.
+ *
+ * @(#)defines.h 8.1 (Berkeley) 6/6/93
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _DEFINES_H
+#define _DEFINES_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define settimer(x) clocks.x = clocks.system++
+
+#define NETADD(c) { *netoring.supply = c; ring_supplied(&netoring, 1); }
+#define NET2ADD(c1, c2) { NETADD(c1); NETADD(c2); }
+#define NETBYTES() (ring_full_count(&netoring))
+#define NETROOM() (ring_empty_count(&netoring))
+
+#define TTYADD(c) if (!(SYNCHing||flushout)) { \
+ *ttyoring.supply = c; \
+ ttyoring.supply = \
+ (ttyoring.supply+1 < ttyoring.top)? \
+ ttyoring.supply+1 : \
+ ttyoring.supply+1-ttyoring.size; \
+ ttyoring.supplytime = ++ring_clock; \
+ }
+#define TTYBYTES() (ring_full_count(&ttyoring))
+#define TTYROOM() (ring_empty_count(&ttyoring))
+
+/* Various modes */
+#define MODE_LOCAL_CHARS(m) ((m)&(MODE_EDIT|MODE_TRAPSIG))
+#define MODE_LOCAL_ECHO(m) ((m)&MODE_ECHO)
+#define MODE_COMMAND_LINE(m) ((m) == -1)
+
+#define CONTROL(x) ((x)&0x1f) /* CTRL(x) is not portable */
+
+extern ulong_t ring_clock;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _DEFINES_H */
diff --git a/usr/src/cmd/cmd-inet/usr.bin/telnet/enc_des.c b/usr/src/cmd/cmd-inet/usr.bin/telnet/enc_des.c
new file mode 100644
index 0000000000..78adc014cb
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/telnet/enc_des.c
@@ -0,0 +1,563 @@
+/*
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * usr/src/cmd/cmd-inet/usr.bin/telnet/enc_des.c
+ */
+
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Copyright (C) 1998 by the FundsXpress, INC.
+ *
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may require
+ * a specific license from the United States Government. It is the
+ * responsibility of any person or organization contemplating export to
+ * obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of FundsXpress. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. FundsXpress 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 ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* based on @(#)enc_des.c 8.1 (Berkeley) 6/4/93 */
+
+#include <krb5.h>
+#include <stdio.h>
+#include <arpa/telnet.h>
+
+#ifdef __STDC__
+#include <stdlib.h>
+#endif
+
+#include "externs.h"
+
+extern boolean_t encrypt_debug_mode;
+extern krb5_context telnet_context;
+
+#define KEYFLAG_SHIFT 2
+#define SHIFT_VAL(a, b) (KEYFLAG_SHIFT*((a)+((b)*2)))
+
+static struct _fb {
+ Block temp_feed;
+ int state[2]; /* state for each direction */
+ int keyid[2]; /* keyid for each direction */
+ int once;
+ unsigned char fb_feed[64];
+ boolean_t need_start;
+ boolean_t validkey;
+ struct stinfo {
+ Block str_output;
+ Block str_feed;
+ Block str_iv;
+ unsigned char str_keybytes[DES_BLOCKSIZE];
+ krb5_keyblock str_key;
+ int str_index;
+ int str_flagshift;
+ } streams[2]; /* one for encrypt, one for decrypt */
+} des_cfb;
+
+static void cfb64_stream_iv(Block, struct stinfo *);
+static void cfb64_stream_key(Block, struct stinfo *);
+
+static void
+ecb_encrypt(struct stinfo *stp, Block in, Block out)
+{
+ krb5_error_code code;
+ krb5_data din;
+ krb5_enc_data dout;
+
+ din.length = DES_BLOCKSIZE;
+ din.data = (char *)in;
+
+ dout.ciphertext.length = DES_BLOCKSIZE;
+ dout.ciphertext.data = (char *)out;
+ /* this is a kerberos enctype, not a telopt enctype */
+ dout.enctype = ENCTYPE_UNKNOWN;
+
+ code = krb5_c_encrypt(telnet_context, &stp->str_key, NULL, NULL,
+ &din, &dout);
+ if (code)
+ (void) fprintf(stderr, gettext(
+ "Error encrypting stream data (%s)\r\n"), code);
+}
+
+void
+cfb64_init(void)
+{
+ register struct _fb *fbp = &des_cfb;
+
+ (void) memset((void *)fbp, 0, sizeof (*fbp));
+ fbp->state[0] = des_cfb.state[1] = ENCR_STATE_FAILED;
+ fbp->fb_feed[0] = IAC;
+ fbp->fb_feed[1] = SB;
+ fbp->fb_feed[2] = TELOPT_ENCRYPT;
+ fbp->fb_feed[3] = ENCRYPT_IS;
+
+ fbp->fb_feed[4] = TELOPT_ENCTYPE_DES_CFB64;
+ fbp->streams[TELNET_DIR_DECRYPT].str_flagshift =
+ SHIFT_VAL(0, CFB);
+ fbp->streams[TELNET_DIR_ENCRYPT].str_flagshift =
+ SHIFT_VAL(1, CFB);
+}
+
+
+/*
+ * Returns:
+ * -1: some error. Negotiation is done, encryption not ready.
+ * 0: Successful, initial negotiation all done.
+ * 1: successful, negotiation not done yet.
+ * 2: Not yet. Other things (like getting the key from
+ * Kerberos) have to happen before we can continue.
+ */
+int
+cfb64_start(int dir)
+{
+ struct _fb *fbp = &des_cfb;
+ int x;
+ unsigned char *p;
+ register int state;
+
+ switch (dir) {
+ case TELNET_DIR_DECRYPT:
+ /*
+ * This is simply a request to have the other side
+ * start output (our input). He will negotiate an
+ * IV so we need not look for it.
+ */
+ state = fbp->state[dir];
+ if (state == ENCR_STATE_FAILED)
+ state = ENCR_STATE_IN_PROGRESS;
+ break;
+
+ case TELNET_DIR_ENCRYPT:
+ state = fbp->state[dir];
+ if (state == ENCR_STATE_FAILED)
+ state = ENCR_STATE_IN_PROGRESS;
+ else if ((state & ENCR_STATE_NO_SEND_IV) == 0)
+ break;
+
+ if (!fbp->validkey) {
+ fbp->need_start = B_TRUE;
+ break;
+ }
+ state &= ~ENCR_STATE_NO_SEND_IV;
+ state |= ENCR_STATE_NO_RECV_IV;
+ if (encrypt_debug_mode)
+ (void) printf(gettext("Creating new feed\r\n"));
+ /*
+ * Create a random feed and send it over.
+ */
+ {
+ krb5_data d;
+ krb5_error_code code;
+
+ d.data = (char *)fbp->temp_feed;
+ d.length = sizeof (fbp->temp_feed);
+
+ code = krb5_c_random_make_octets(telnet_context, &d);
+ if (code != 0)
+ return (ENCR_STATE_FAILED);
+ }
+
+ p = fbp->fb_feed + 3;
+ *p++ = ENCRYPT_IS;
+ p++;
+ *p++ = FB64_IV;
+ for (x = 0; x < sizeof (Block); ++x) {
+ if ((*p++ = fbp->temp_feed[x]) == IAC)
+ *p++ = IAC;
+ }
+ *p++ = IAC;
+ *p++ = SE;
+ printsub('>', &fbp->fb_feed[2], p - &fbp->fb_feed[2]);
+ (void) net_write(fbp->fb_feed, p - fbp->fb_feed);
+ break;
+ default:
+ return (ENCR_STATE_FAILED);
+ }
+ return (fbp->state[dir] = state);
+}
+
+/*
+ * Returns:
+ * -1: some error. Negotiation is done, encryption not ready.
+ * 0: Successful, initial negotiation all done.
+ * 1: successful, negotiation not done yet.
+ */
+int
+cfb64_is(unsigned char *data, int cnt)
+{
+ unsigned char *p;
+ struct _fb *fbp = &des_cfb;
+ register int state = fbp->state[TELNET_DIR_DECRYPT];
+
+ if (cnt-- < 1)
+ goto failure;
+
+ switch (*data++) {
+ case FB64_IV:
+ if (cnt != sizeof (Block)) {
+ if (encrypt_debug_mode)
+ (void) printf(gettext(
+ "CFB64: initial vector failed "
+ "on size\r\n"));
+ state = ENCR_STATE_FAILED;
+ goto failure;
+ }
+
+ if (encrypt_debug_mode)
+ (void) printf(gettext(
+ "CFB64: initial vector received\r\n"));
+
+ if (encrypt_debug_mode)
+ (void) printf(gettext(
+ "Initializing Decrypt stream\r\n"));
+
+ cfb64_stream_iv((void *)data,
+ &fbp->streams[TELNET_DIR_DECRYPT]);
+
+ p = fbp->fb_feed + 3;
+ *p++ = ENCRYPT_REPLY;
+ p++;
+ *p++ = FB64_IV_OK;
+ *p++ = IAC;
+ *p++ = SE;
+ printsub('>', &fbp->fb_feed[2], p - &fbp->fb_feed[2]);
+ (void) net_write(fbp->fb_feed, p - fbp->fb_feed);
+
+ state = fbp->state[TELNET_DIR_DECRYPT] = ENCR_STATE_IN_PROGRESS;
+ break;
+
+ default:
+ if (encrypt_debug_mode) {
+ (void) printf(gettext(
+ "Unknown option type: %d\r\n"), *(data-1));
+ printd(data, cnt);
+ (void) printf("\r\n");
+ }
+ /* FALL THROUGH */
+ failure:
+ /*
+ * We failed. Send an FB64_IV_BAD option
+ * to the other side so it will know that
+ * things failed.
+ */
+ p = fbp->fb_feed + 3;
+ *p++ = ENCRYPT_REPLY;
+ p++;
+ *p++ = FB64_IV_BAD;
+ *p++ = IAC;
+ *p++ = SE;
+ printsub('>', &fbp->fb_feed[2], p - &fbp->fb_feed[2]);
+ (void) net_write(fbp->fb_feed, p - fbp->fb_feed);
+
+ break;
+ }
+ return (fbp->state[TELNET_DIR_DECRYPT] = state);
+}
+
+/*
+ * Returns:
+ * -1: some error. Negotiation is done, encryption not ready.
+ * 0: Successful, initial negotiation all done.
+ * 1: successful, negotiation not done yet.
+ */
+int
+cfb64_reply(unsigned char *data, int cnt)
+{
+ struct _fb *fbp = &des_cfb;
+ register int state = fbp->state[TELNET_DIR_ENCRYPT];
+
+ if (cnt-- < 1)
+ goto failure;
+
+ switch (*data++) {
+ case FB64_IV_OK:
+ cfb64_stream_iv(fbp->temp_feed,
+ &fbp->streams[TELNET_DIR_ENCRYPT]);
+ if (state == ENCR_STATE_FAILED)
+ state = ENCR_STATE_IN_PROGRESS;
+ state &= ~ENCR_STATE_NO_RECV_IV;
+ encrypt_send_keyid(TELNET_DIR_ENCRYPT,
+ (unsigned char *)"\0", 1, 1);
+ break;
+
+ case FB64_IV_BAD:
+ (void) memset(fbp->temp_feed, 0, sizeof (Block));
+ cfb64_stream_iv(fbp->temp_feed,
+ &fbp->streams[TELNET_DIR_ENCRYPT]);
+ state = ENCR_STATE_FAILED;
+ break;
+
+ default:
+ if (encrypt_debug_mode) {
+ (void) printf(gettext(
+ "Unknown option type: %d\r\n"), data[-1]);
+ printd(data, cnt);
+ (void) printf("\r\n");
+ }
+ /* FALL THROUGH */
+ failure:
+ state = ENCR_STATE_FAILED;
+ break;
+ }
+ return (fbp->state[TELNET_DIR_ENCRYPT] = state);
+}
+
+void
+cfb64_session(Session_Key *key)
+{
+ struct _fb *fbp = &des_cfb;
+
+ if (!key || key->type != SK_DES) {
+ if (encrypt_debug_mode)
+ (void) printf(gettext(
+ "Can't set DES's session key (%d != %d)\r\n"),
+ key ? key->type : -1, SK_DES);
+ return;
+ }
+
+ fbp->validkey = B_TRUE;
+
+ cfb64_stream_key(key->data, &fbp->streams[TELNET_DIR_ENCRYPT]);
+ cfb64_stream_key(key->data, &fbp->streams[TELNET_DIR_DECRYPT]);
+
+ /*
+ * Now look to see if cfb64_start() was was waiting for
+ * the key to show up. If so, go ahead an call it now
+ * that we have the key.
+ */
+ if (fbp->need_start) {
+ fbp->need_start = B_FALSE;
+ (void) cfb64_start(TELNET_DIR_ENCRYPT);
+ }
+}
+
+/*
+ * We only accept a keyid of 0. If we get a keyid of
+ * 0, then mark the state as SUCCESS.
+ */
+int
+cfb64_keyid(dir, kp, lenp)
+ int dir, *lenp;
+ unsigned char *kp;
+{
+ struct _fb *fbp = &des_cfb;
+ register int state = fbp->state[dir];
+
+ if (*lenp != 1 || (*kp != '\0')) {
+ *lenp = 0;
+ return (state);
+ }
+
+ if (state == ENCR_STATE_FAILED)
+ state = ENCR_STATE_IN_PROGRESS;
+
+ state &= ~ENCR_STATE_NO_KEYID;
+
+ return (fbp->state[dir] = state);
+}
+
+/*
+ * Print ENCRYPT suboptions to NetTrace when "set opt" is used
+ */
+void
+cfb64_printsub(unsigned char *data, int cnt, unsigned char *buf, int buflen)
+{
+ char lbuf[ENCR_LBUF_BUFSIZ];
+ register int i;
+ char *cp;
+ unsigned char type[] = "CFB64";
+
+ buf[buflen-1] = '\0'; /* make sure it's NULL terminated */
+ buflen -= 1;
+
+ switch (data[2]) {
+ case FB64_IV:
+ (void) snprintf(lbuf, ENCR_LBUF_BUFSIZ, "%s_IV", type);
+ cp = lbuf;
+ goto common;
+
+ case FB64_IV_OK:
+ (void) snprintf(lbuf, ENCR_LBUF_BUFSIZ, "%s_IV_OK", type);
+ cp = lbuf;
+ goto common;
+
+ case FB64_IV_BAD:
+ (void) snprintf(lbuf, ENCR_LBUF_BUFSIZ, "%s_IV_BAD", type);
+ cp = lbuf;
+ goto common;
+
+ default:
+ (void) snprintf(lbuf, ENCR_LBUF_BUFSIZ, " %d (unknown)",
+ data[2]);
+ cp = lbuf;
+ common:
+ for (; (buflen > 0) && (*buf = *cp++); buf++)
+ buflen--;
+ for (i = 3; i < cnt; i++) {
+ (void) snprintf(lbuf, ENCR_LBUF_BUFSIZ, " %d", data[i]);
+ for (cp = lbuf; (buflen > 0) && (*buf = *cp++); buf++)
+ buflen--;
+ }
+ break;
+ }
+}
+
+
+static void
+cfb64_stream_iv(Block seed, register struct stinfo *stp)
+{
+ (void) memcpy((void *)stp->str_iv, (void *)seed, sizeof (Block));
+ (void) memcpy((void *)stp->str_output, (void *)seed, sizeof (Block));
+
+ stp->str_index = sizeof (Block);
+}
+
+void
+cfb64_stream_key(Block key, register struct stinfo *stp)
+{
+ (void) memcpy((void *)stp->str_keybytes, (void *)key, sizeof (Block));
+ stp->str_key.length = DES_BLOCKSIZE;
+ stp->str_key.contents = stp->str_keybytes;
+ /*
+ * the original version of this code uses des ecb mode, but
+ * it only ever does one block at a time. cbc with a zero iv
+ * is identical
+ */
+ /* this is a kerberos enctype, not a telopt enctype */
+ stp->str_key.enctype = ENCTYPE_DES_CBC_RAW;
+
+ (void) memcpy((void *)stp->str_output, (void *)stp->str_iv,
+ sizeof (Block));
+
+ stp->str_index = sizeof (Block);
+}
+
+/*
+ * DES 64 bit Cipher Feedback
+ *
+ * key --->+-----+
+ * +->| DES |--+
+ * | +-----+ |
+ * | v
+ * INPUT --(--------->(+)+---> DATA
+ * | |
+ * +-------------+
+ *
+ *
+ * Given:
+ * iV: Initial vector, 64 bits (8 bytes) long.
+ * Dn: the nth chunk of 64 bits (8 bytes) of data to encrypt (decrypt).
+ * On: the nth chunk of 64 bits (8 bytes) of encrypted (decrypted) output.
+ *
+ * V0 = DES(iV, key)
+ * On = Dn ^ Vn
+ * V(n+1) = DES(On, key)
+ */
+
+void
+cfb64_encrypt(register unsigned char *s, int c)
+{
+ register struct stinfo *stp =
+ &des_cfb.streams[TELNET_DIR_ENCRYPT];
+ register int index;
+
+ index = stp->str_index;
+ while (c-- > 0) {
+ if (index == sizeof (Block)) {
+ Block b;
+ ecb_encrypt(stp, stp->str_output, b);
+ (void) memcpy((void *)stp->str_feed, (void *)b,
+ sizeof (Block));
+ index = 0;
+ }
+
+ /* On encryption, we store (feed ^ data) which is cypher */
+ *s = stp->str_output[index] = (stp->str_feed[index] ^ *s);
+ s++;
+ index++;
+ }
+ stp->str_index = index;
+}
+
+int
+cfb64_decrypt(int data)
+{
+ register struct stinfo *stp =
+ &des_cfb.streams[TELNET_DIR_DECRYPT];
+ int index;
+
+ if (data == -1) {
+ /*
+ * Back up one byte. It is assumed that we will
+ * never back up more than one byte. If we do, this
+ * may or may not work.
+ */
+ if (stp->str_index)
+ --stp->str_index;
+ return (0);
+ }
+
+ index = stp->str_index++;
+ if (index == sizeof (Block)) {
+ Block b;
+ ecb_encrypt(stp, stp->str_output, b);
+ (void) memcpy((void *)stp->str_feed, (void *)b, sizeof (Block));
+ stp->str_index = 1; /* Next time will be 1 */
+ index = 0; /* But now use 0 */
+ }
+
+ /* On decryption we store (data) which is cypher. */
+ stp->str_output[index] = data;
+ return (data ^ stp->str_feed[index]);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/telnet/encrypt.c b/usr/src/cmd/cmd-inet/usr.bin/telnet/encrypt.c
new file mode 100644
index 0000000000..05d6127211
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/telnet/encrypt.c
@@ -0,0 +1,971 @@
+/*
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * usr/src/cmd/cmd-inet/usr.bin/telnet/encrypt.c
+ */
+
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* based on @(#)encrypt.c 8.1 (Berkeley) 6/4/93 */
+
+/*
+ * Copyright (C) 1990 by the Massachusetts Institute of Technology
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in 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. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * 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.
+ */
+
+#ifdef lint
+static char *encrypt_names[] = {0};
+static char *enctype_names[] = {0};
+#else /* lint */
+#define ENCRYPT_NAMES
+#endif /* lint */
+#include <arpa/telnet.h>
+
+#include "externs.h"
+
+#ifdef __STDC__
+#include <stdlib.h>
+#endif
+
+/*
+ * These functions pointers point to the current routines
+ * for encrypting and decrypting data.
+ */
+void (*encrypt_output)(uchar_t *, int);
+int (*decrypt_input)(int);
+static void encrypt_start_output(int);
+static void encrypt_send_end(void);
+static void encrypt_send_request_start(void);
+static void encrypt_send_request_end(void);
+
+boolean_t encrypt_debug_mode = B_FALSE;
+
+static int decrypt_mode = 0;
+static int encrypt_mode = 0;
+static boolean_t encrypt_verbose = B_FALSE;
+static boolean_t autoencrypt = B_FALSE;
+static boolean_t autodecrypt = B_FALSE;
+static char *Name = "Noname";
+
+#define typemask(x) ((x) > 0 ? 1 << ((x)-1) : 0)
+#define SUCCESS 0x00
+#define UNKNOWN gettext("(unknown)")
+
+static int i_support_encrypt = typemask(TELOPT_ENCTYPE_DES_CFB64);
+static int i_support_decrypt = typemask(TELOPT_ENCTYPE_DES_CFB64);
+static int i_wont_support_encrypt = 0;
+static int i_wont_support_decrypt = 0;
+#define I_SUPPORT_ENCRYPT (i_support_encrypt & ~i_wont_support_encrypt)
+#define I_SUPPORT_DECRYPT (i_support_decrypt & ~i_wont_support_decrypt)
+
+static int remote_supports_encrypt = 0;
+static int remote_supports_decrypt = 0;
+
+static Encryptions encryptions[] = {
+ { "DES_CFB64", TELOPT_ENCTYPE_DES_CFB64,
+ cfb64_encrypt,
+ cfb64_decrypt,
+ cfb64_init,
+ cfb64_start,
+ cfb64_is,
+ cfb64_reply,
+ cfb64_session,
+ cfb64_keyid,
+ cfb64_printsub },
+ { 0, },
+};
+
+static uchar_t str_send[64] = { IAC, SB, TELOPT_ENCRYPT,
+ ENCRYPT_SUPPORT };
+static uchar_t str_suplen = 0;
+static uchar_t str_start[72] = { IAC, SB, TELOPT_ENCRYPT };
+static uchar_t str_end[] = { IAC, SB, TELOPT_ENCRYPT, 0, IAC, SE };
+
+static Encryptions *
+findencryption(int type)
+{
+ Encryptions *ep = encryptions;
+
+ if (!(I_SUPPORT_ENCRYPT & remote_supports_decrypt & typemask(type)))
+ return (NULL);
+ for (; (ep->type != NULL) && (ep->type != type); ep++);
+ return (ep->type ? ep : NULL);
+}
+
+static Encryptions *
+finddecryption(int type)
+{
+ Encryptions *ep = encryptions;
+
+ if (!(I_SUPPORT_DECRYPT & remote_supports_encrypt & typemask(type)))
+ return (NULL);
+ while (ep->type && ep->type != type)
+ ++ep;
+ return (ep->type ? ep : NULL);
+}
+
+#define MAXKEYLEN 64
+
+static struct key_info {
+ uchar_t keyid[MAXKEYLEN];
+ int keylen;
+ int dir;
+ int *modep;
+ Encryptions *(*getcrypt)();
+} ki[2] = {
+ { { 0 }, 0, TELNET_DIR_ENCRYPT, &encrypt_mode, findencryption },
+ { { 0 }, 0, TELNET_DIR_DECRYPT, &decrypt_mode, finddecryption },
+};
+#define KI_ENCRYPT 0
+#define KI_DECRYPT 1
+
+void
+encrypt_init(char *name)
+{
+ Encryptions *ep = encryptions;
+
+ Name = name;
+ i_support_encrypt = i_support_decrypt = 0;
+ remote_supports_encrypt = remote_supports_decrypt = 0;
+ encrypt_mode = 0;
+ decrypt_mode = 0;
+ encrypt_output = 0;
+ decrypt_input = 0;
+#ifdef notdef
+ encrypt_verbose = !server;
+#endif
+
+ str_suplen = 4;
+
+ while (ep->type) {
+ if (encrypt_debug_mode)
+ (void) printf(gettext(
+ ">>>%s: I will support %s\r\n"),
+ Name, ENCTYPE_NAME(ep->type));
+ i_support_encrypt |= typemask(ep->type);
+ i_support_decrypt |= typemask(ep->type);
+ if ((i_wont_support_decrypt & typemask(ep->type)) == 0)
+ if ((str_send[str_suplen++] = ep->type) == IAC)
+ str_send[str_suplen++] = IAC;
+ if (ep->init)
+ (*ep->init)();
+ ++ep;
+ }
+ str_send[str_suplen++] = IAC;
+ str_send[str_suplen++] = SE;
+}
+
+static void
+encrypt_list_types(void)
+{
+ Encryptions *ep = encryptions;
+
+ (void) printf(gettext("Valid encryption types:\n"));
+ while (ep->type) {
+ (void) printf("\t%s (%d)\r\n",
+ ENCTYPE_NAME(ep->type), ep->type);
+ ++ep;
+ }
+}
+
+int
+EncryptEnable(char *type, char *mode)
+{
+ if (isprefix(type, "help") || isprefix(type, "?")) {
+ (void) printf(gettext(
+ "Usage: encrypt enable <type> [input|output]\n"));
+ encrypt_list_types();
+ return (0);
+ }
+
+ if (EncryptType(type, mode))
+ return (EncryptStart(mode));
+
+ return (0);
+}
+
+int
+EncryptDisable(char *type, char *mode)
+{
+ register Encryptions *ep;
+ int ret = 0;
+
+ if (isprefix(type, "help") || isprefix(type, "?")) {
+ (void) printf(gettext(
+ "Usage: encrypt disable <type> [input|output]\n"));
+ encrypt_list_types();
+ } else if ((ep = (Encryptions *)genget(type, (char **)encryptions,
+ sizeof (Encryptions))) == 0) {
+ (void) printf(gettext("%s: invalid encryption type\n"), type);
+ } else if (Ambiguous(ep)) {
+ (void) printf(gettext("Ambiguous type '%s'\n"), type);
+ } else {
+ if ((mode == 0) || (isprefix(mode, "input") ? 1 : 0)) {
+ if (decrypt_mode == ep->type)
+ (void) EncryptStopInput();
+ i_wont_support_decrypt |= typemask(ep->type);
+ ret = 1;
+ }
+ if ((mode == 0) || (isprefix(mode, "output"))) {
+ if (encrypt_mode == ep->type)
+ (void) EncryptStopOutput();
+ i_wont_support_encrypt |= typemask(ep->type);
+ ret = 1;
+ }
+ if (ret == 0)
+ (void) printf(gettext(
+ "%s: invalid encryption mode\n"), mode);
+ }
+ return (ret);
+}
+
+int
+EncryptType(char *type, char *mode)
+{
+ register Encryptions *ep;
+ int ret = 0;
+
+ if (isprefix(type, "help") || isprefix(type, "?")) {
+ (void) printf(gettext(
+ "Usage: encrypt type <type> [input|output]\n"));
+ encrypt_list_types();
+ } else if ((ep = (Encryptions *)genget(type, (char **)encryptions,
+ sizeof (Encryptions))) == 0) {
+ (void) printf(gettext("%s: invalid encryption type\n"), type);
+ } else if (Ambiguous(ep)) {
+ (void) printf(gettext("Ambiguous type '%s'\n"), type);
+ } else {
+ if ((mode == 0) || isprefix(mode, "input")) {
+ decrypt_mode = ep->type;
+ i_wont_support_decrypt &= ~typemask(ep->type);
+ ret = 1;
+ }
+ if ((mode == 0) || isprefix(mode, "output")) {
+ encrypt_mode = ep->type;
+ i_wont_support_encrypt &= ~typemask(ep->type);
+ ret = 1;
+ }
+ if (ret == 0)
+ (void) printf(gettext(
+ "%s: invalid encryption mode\n"), mode);
+ }
+ return (ret);
+}
+
+int
+EncryptStart(char *mode)
+{
+ register int ret = 0;
+ if (mode) {
+ if (isprefix(mode, "input"))
+ return (EncryptStartInput());
+ if (isprefix(mode, "output"))
+ return (EncryptStartOutput());
+ if (isprefix(mode, "help") || isprefix(mode, "?")) {
+ (void) printf(gettext(
+ "Usage: encrypt start [input|output]\n"));
+ return (0);
+ }
+ (void) printf(gettext(
+ "%s: invalid encryption mode 'encrypt start ?' "
+ "for help\n"), mode);
+ return (0);
+ }
+ ret += EncryptStartInput();
+ ret += EncryptStartOutput();
+ return (ret);
+}
+
+int
+EncryptStartInput(void)
+{
+ if (decrypt_mode) {
+ encrypt_send_request_start();
+ return (1);
+ }
+ (void) printf(gettext("No previous decryption mode, "
+ "decryption not enabled\r\n"));
+ return (0);
+}
+
+int
+EncryptStartOutput(void)
+{
+ if (encrypt_mode) {
+ encrypt_start_output(encrypt_mode);
+ return (1);
+ }
+ (void) printf(gettext("No previous encryption mode, "
+ "encryption not enabled\r\n"));
+ return (0);
+}
+
+int
+EncryptStop(char *mode)
+{
+ int ret = 0;
+ if (mode) {
+ if (isprefix(mode, "input"))
+ return (EncryptStopInput());
+ if (isprefix(mode, "output"))
+ return (EncryptStopOutput());
+ if (isprefix(mode, "help") || isprefix(mode, "?")) {
+ (void) printf(gettext(
+ "Usage: encrypt stop [input|output]\n"));
+ return (0);
+ }
+ (void) printf(gettext(
+ "%s: invalid encryption mode 'encrypt stop ?' "
+ "for help\n"), mode);
+ return (0);
+ }
+ ret += EncryptStopInput();
+ ret += EncryptStopOutput();
+ return (ret);
+}
+
+int
+EncryptStopInput(void)
+{
+ encrypt_send_request_end();
+ return (1);
+}
+
+int
+EncryptStopOutput(void)
+{
+ encrypt_send_end();
+ return (1);
+}
+
+void
+encrypt_display(void)
+{
+ if (encrypt_output)
+ (void) printf(gettext(
+ "Currently encrypting output with %s\r\n"),
+ ENCTYPE_NAME(encrypt_mode));
+ if (decrypt_input)
+ (void) printf(gettext(
+ "Currently decrypting input with %s\r\n"),
+ ENCTYPE_NAME(decrypt_mode));
+}
+
+int
+EncryptStatus(void)
+{
+ if (encrypt_output)
+ (void) printf(gettext(
+ "Currently encrypting output with %s\r\n"),
+ ENCTYPE_NAME(encrypt_mode));
+ else if (encrypt_mode) {
+ (void) printf(gettext("Currently output is clear text.\r\n"));
+ (void) printf(gettext("Last encryption mode was %s\r\n"),
+ ENCTYPE_NAME(encrypt_mode));
+ }
+ if (decrypt_input) {
+ (void) printf(gettext(
+ "Currently decrypting input with %s\r\n"),
+ ENCTYPE_NAME(decrypt_mode));
+ } else if (decrypt_mode) {
+ (void) printf(gettext("Currently input is clear text.\r\n"));
+ (void) printf(gettext("Last decryption mode was %s\r\n"),
+ ENCTYPE_NAME(decrypt_mode));
+ }
+ return (1);
+}
+
+void
+encrypt_send_support(void)
+{
+ if (str_suplen) {
+ /*
+ * If the user has requested that decryption start
+ * immediatly, then send a "REQUEST START" before
+ * we negotiate the type.
+ */
+ if (autodecrypt)
+ encrypt_send_request_start();
+ (void) net_write(str_send, str_suplen);
+ printsub('>', &str_send[2], str_suplen - 2);
+ str_suplen = 0;
+ }
+}
+
+int
+EncryptDebug(int on)
+{
+ encrypt_debug_mode = (on < 0) ? !encrypt_debug_mode :
+ (on > 0) ? B_TRUE : B_FALSE;
+ (void) printf(encrypt_debug_mode ?
+ gettext("Encryption debugging enabled\r\n") :
+ gettext("Encryption debugging disabled\r\n"));
+ return (1);
+}
+
+int
+EncryptVerbose(int on)
+{
+ encrypt_verbose = (on < 0) ? !encrypt_verbose :
+ (on > 0) ? B_TRUE : B_FALSE;
+ (void) printf(encrypt_verbose ?
+ gettext("Encryption is verbose\r\n") :
+ gettext("Encryption is not verbose\r\n"));
+ return (1);
+}
+
+int
+EncryptAutoEnc(int on)
+{
+ encrypt_auto(on);
+ (void) printf(autoencrypt ?
+ gettext("Automatic encryption of output is enabled\r\n") :
+ gettext("Automatic encryption of output is disabled\r\n"));
+ return (1);
+}
+
+int
+EncryptAutoDec(int on)
+{
+ decrypt_auto(on);
+ (void) printf(autodecrypt ?
+ gettext("Automatic decryption of input is enabled\r\n") :
+ gettext("Automatic decryption of input is disabled\r\n"));
+ return (1);
+}
+
+/*
+ * Called when ENCRYPT SUPPORT is received.
+ */
+void
+encrypt_support(uchar_t *typelist, int cnt)
+{
+ register int type, use_type = 0;
+ Encryptions *ep;
+
+ /*
+ * Forget anything the other side has previously told us.
+ */
+ remote_supports_decrypt = 0;
+
+ while (cnt-- > 0) {
+ type = *typelist++;
+ if (encrypt_debug_mode)
+ (void) printf(gettext(
+ ">>>%s: Remote host supports %s (%d)\r\n"),
+ Name, ENCTYPE_NAME(type), type);
+ if ((type < TELOPT_ENCTYPE_CNT) &&
+ (I_SUPPORT_ENCRYPT & typemask(type))) {
+ remote_supports_decrypt |= typemask(type);
+ if (use_type == 0)
+ use_type = type;
+ }
+ }
+ if (use_type) {
+ ep = findencryption(use_type);
+ if (!ep)
+ return;
+ type = ep->start ? (*ep->start)(TELNET_DIR_ENCRYPT) : 0;
+ if (encrypt_debug_mode)
+ (void) printf(gettext(
+ ">>>%s: (*ep->start)() returned %d\r\n"),
+ Name, type);
+ if (type < 0)
+ return;
+ encrypt_mode = use_type;
+ if (type == 0)
+ encrypt_start_output(use_type);
+ }
+}
+
+void
+encrypt_is(uchar_t *data, int cnt)
+{
+ Encryptions *ep;
+ register int type, ret;
+
+ if (--cnt < 0)
+ return;
+ type = *data++;
+ if (type < TELOPT_ENCTYPE_CNT)
+ remote_supports_encrypt |= typemask(type);
+ if (!(ep = finddecryption(type))) {
+ if (encrypt_debug_mode)
+ (void) printf(gettext(
+ ">>>%s: Can't find type %s (%d) for "
+ "initial negotiation\r\n"), Name,
+ ENCTYPE_NAME_OK(type) ?
+ ENCTYPE_NAME(type) : UNKNOWN, type);
+ return;
+ }
+ if (!ep->is) {
+ if (encrypt_debug_mode)
+ (void) printf(gettext(
+ ">>>%s: No initial negotiation needed "
+ "for type %s (%d)\r\n"), Name,
+ ENCTYPE_NAME_OK(type) ?
+ ENCTYPE_NAME(type) : UNKNOWN, type);
+ ret = 0;
+ } else {
+ ret = (*ep->is)(data, cnt);
+ if (encrypt_debug_mode)
+ (void) printf(gettext(
+ "(*ep->is)(%x, %d) returned %s(%d)\n"),
+ data, cnt, (ret < 0) ? "FAIL " :
+ (ret == 0) ? "SUCCESS " : "MORE_TO_DO ", ret);
+ }
+ if (ret < 0) {
+ autodecrypt = B_FALSE;
+ } else {
+ decrypt_mode = type;
+ if (ret == 0 && autodecrypt)
+ encrypt_send_request_start();
+ }
+}
+
+void
+encrypt_reply(uchar_t *data, int cnt)
+{
+ Encryptions *ep;
+ register int ret, type;
+
+ if (--cnt < 0)
+ return;
+ type = *data++;
+ if (!(ep = findencryption(type))) {
+ if (encrypt_debug_mode)
+ (void) printf(gettext(
+ ">>>%s: Can't find type %s (%d) "
+ "for initial negotiation\r\n"), Name,
+ ENCTYPE_NAME_OK(type) ?
+ ENCTYPE_NAME(type) : UNKNOWN, type);
+ return;
+ }
+ if (!ep->reply) {
+ if (encrypt_debug_mode)
+ (void) printf(gettext(
+ ">>>%s: No initial negotiation needed "
+ "for type %s (%d)\r\n"), Name,
+ ENCTYPE_NAME_OK(type) ?
+ ENCTYPE_NAME(type) : UNKNOWN, type);
+ ret = 0;
+ } else {
+ ret = (*ep->reply)(data, cnt);
+ if (encrypt_debug_mode)
+ (void) printf(gettext(
+ "(*ep->reply)(%x, %d) returned %s(%d)\n"),
+ data, cnt, (ret < 0) ? "FAIL " :
+ (ret == 0) ? "SUCCESS " : "MORE_TO_DO ", ret);
+ }
+ if (encrypt_debug_mode)
+ (void) printf(gettext(
+ ">>>%s: encrypt_reply returned %d\n"), Name, ret);
+ if (ret < 0) {
+ autoencrypt = B_FALSE;
+ } else {
+ encrypt_mode = type;
+ if (ret == 0 && autoencrypt)
+ encrypt_start_output(type);
+ }
+}
+
+/*
+ * Called when a ENCRYPT START command is received.
+ */
+/* ARGSUSED */
+void
+encrypt_start(uchar_t *data, int cnt)
+{
+ Encryptions *ep;
+
+ if (!decrypt_mode) {
+ /*
+ * Something is wrong. We should not get a START
+ * command without having already picked our
+ * decryption scheme. Send a REQUEST-END to
+ * attempt to clear the channel...
+ */
+ (void) printf(gettext("%s: Warning, cannot decrypt "
+ "input stream!!!\r\n"), Name);
+ encrypt_send_request_end();
+ return;
+ }
+
+ if (ep = finddecryption(decrypt_mode)) {
+ decrypt_input = ep->input;
+ if (encrypt_verbose)
+ (void) printf(gettext(
+ "[ Input is now decrypted with type %s ]\r\n"),
+ ENCTYPE_NAME(decrypt_mode));
+ if (encrypt_debug_mode)
+ (void) printf(gettext(
+ ">>>%s: Start to decrypt input with type %s\r\n"),
+ Name, ENCTYPE_NAME(decrypt_mode));
+ } else {
+ (void) printf(gettext(
+ "%s: Warning, cannot decrypt type %s (%d)!!!\r\n"),
+ Name, ENCTYPE_NAME_OK(decrypt_mode) ?
+ ENCTYPE_NAME(decrypt_mode) : UNKNOWN,
+ decrypt_mode);
+ encrypt_send_request_end();
+ }
+}
+
+void
+encrypt_session_key(Session_Key *key)
+{
+ Encryptions *ep = encryptions;
+
+ while (ep->type) {
+ if (ep->session)
+ (*ep->session)(key);
+#ifdef notdef
+ if (!encrypt_output && autoencrypt)
+ encrypt_start_output(ep->type);
+ if (!decrypt_input && autodecrypt)
+ encrypt_send_request_start();
+#endif
+ ++ep;
+ }
+}
+
+/*
+ * Called when ENCRYPT END is received.
+ */
+void
+encrypt_end(void)
+{
+ decrypt_input = 0;
+ if (encrypt_debug_mode)
+ (void) printf(gettext(
+ ">>>%s: Input is back to clear text\r\n"), Name);
+ if (encrypt_verbose)
+ (void) printf(gettext("[ Input is now clear text ]\r\n"));
+}
+
+/*
+ * Called when ENCRYPT REQUEST-END is received.
+ */
+void
+encrypt_request_end(void)
+{
+ encrypt_send_end();
+}
+
+/*
+ * Called when ENCRYPT REQUEST-START is received. If we receive
+ * this before a type is picked, then that indicates that the
+ * other side wants us to start encrypting data as soon as we
+ * can.
+ */
+/* ARGSUSED */
+void
+encrypt_request_start(uchar_t *data, int cnt)
+{
+ if (encrypt_mode == 0)
+ return;
+ encrypt_start_output(encrypt_mode);
+}
+
+static uchar_t str_keyid[(MAXKEYLEN*2)+5] = { IAC, SB, TELOPT_ENCRYPT };
+static void encrypt_keyid(struct key_info *, uchar_t *, int);
+
+void
+encrypt_enc_keyid(uchar_t *keyid, int len)
+{
+ encrypt_keyid(&ki[KI_DECRYPT], keyid, len);
+}
+
+void
+encrypt_dec_keyid(uchar_t *keyid, int len)
+{
+ encrypt_keyid(&ki[KI_ENCRYPT], keyid, len);
+}
+
+static void
+encrypt_keyid(struct key_info *kp, uchar_t *keyid, int len)
+{
+ Encryptions *ep;
+ int dir = kp->dir;
+ register int ret = 0;
+
+ if (!(ep = (*kp->getcrypt)(*kp->modep))) {
+ if (len == 0)
+ return;
+ kp->keylen = 0;
+ } else if (len == 0) {
+ /*
+ * Empty option, indicates a failure.
+ */
+ if (kp->keylen == 0)
+ return;
+ kp->keylen = 0;
+ if (ep->keyid)
+ (void) (*ep->keyid)(dir, kp->keyid, &kp->keylen);
+
+ } else if ((len != kp->keylen) ||
+ (memcmp(keyid, kp->keyid, len) != 0)) {
+ /*
+ * Length or contents are different
+ */
+ kp->keylen = len;
+ (void) memcpy(kp->keyid, keyid, len);
+ if (ep->keyid)
+ (void) (*ep->keyid)(dir, kp->keyid, &kp->keylen);
+ } else {
+ if (ep->keyid)
+ ret = (*ep->keyid)(dir, kp->keyid, &kp->keylen);
+ if ((ret == 0) && (dir == TELNET_DIR_ENCRYPT) && autoencrypt)
+ encrypt_start_output(*kp->modep);
+ return;
+ }
+
+ encrypt_send_keyid(dir, kp->keyid, kp->keylen, 0);
+}
+
+void
+encrypt_send_keyid(int dir, uchar_t *keyid, int keylen, int saveit)
+{
+ uchar_t *strp;
+
+ str_keyid[3] = (dir == TELNET_DIR_ENCRYPT)
+ ? ENCRYPT_ENC_KEYID : ENCRYPT_DEC_KEYID;
+ if (saveit) {
+ struct key_info *kp = &ki[(dir == TELNET_DIR_ENCRYPT) ? 0 : 1];
+ (void) memcpy(kp->keyid, keyid, keylen);
+ kp->keylen = keylen;
+ }
+
+ for (strp = &str_keyid[4]; keylen > 0; --keylen) {
+ if ((*strp++ = *keyid++) == IAC)
+ *strp++ = IAC;
+ }
+ *strp++ = IAC;
+ *strp++ = SE;
+ (void) net_write(str_keyid, strp - str_keyid);
+ printsub('>', &str_keyid[2], strp - str_keyid - 2);
+}
+
+void
+encrypt_auto(int on)
+{
+ autoencrypt = (on < 0) ? !autoencrypt :
+ (on > 0) ? B_TRUE : B_FALSE;
+}
+
+void
+decrypt_auto(int on)
+{
+ autodecrypt = (on < 0) ? !autodecrypt :
+ (on > 0) ? B_TRUE : B_FALSE;
+}
+
+static void
+encrypt_start_output(int type)
+{
+ Encryptions *ep;
+ register uchar_t *p;
+ register int i;
+
+ if (!(ep = findencryption(type))) {
+ if (encrypt_debug_mode) {
+ (void) printf(gettext(
+ ">>>%s: Can't encrypt with type %s (%d)\r\n"),
+ Name, ENCTYPE_NAME_OK(type) ?
+ ENCTYPE_NAME(type) : UNKNOWN, type);
+ }
+ return;
+ }
+ if (ep->start) {
+ i = (*ep->start)(TELNET_DIR_ENCRYPT);
+ if (encrypt_debug_mode) {
+ (void) printf(gettext(
+ ">>>%s: Encrypt start: %s (%d) %s\r\n"),
+ Name, (i < 0) ?
+ gettext("failed") :
+ gettext("initial negotiation in progress"),
+ i, ENCTYPE_NAME(type));
+ }
+ if (i)
+ return;
+ }
+ p = str_start + 3;
+ *p++ = ENCRYPT_START;
+ for (i = 0; i < ki[KI_ENCRYPT].keylen; ++i) {
+ if ((*p++ = ki[KI_ENCRYPT].keyid[i]) == IAC)
+ *p++ = IAC;
+ }
+ *p++ = IAC;
+ *p++ = SE;
+ (void) net_write(str_start, p - str_start);
+ net_encrypt();
+ printsub('>', &str_start[2], p - &str_start[2]);
+ /*
+ * If we are already encrypting in some mode, then
+ * encrypt the ring (which includes our request) in
+ * the old mode, mark it all as "clear text" and then
+ * switch to the new mode.
+ */
+ encrypt_output = ep->output;
+ encrypt_mode = type;
+ if (encrypt_debug_mode)
+ (void) printf(gettext(
+ ">>>%s: Started to encrypt output with type %s\r\n"),
+ Name, ENCTYPE_NAME(type));
+ if (encrypt_verbose)
+ (void) printf(gettext(
+ "[ Output is now encrypted with type %s ]\r\n"),
+ ENCTYPE_NAME(type));
+}
+
+static void
+encrypt_send_end(void)
+{
+ if (!encrypt_output)
+ return;
+
+ str_end[3] = ENCRYPT_END;
+ (void) net_write(str_end, sizeof (str_end));
+ net_encrypt();
+ printsub('>', &str_end[2], sizeof (str_end) - 2);
+ /*
+ * Encrypt the output buffer now because it will not be done by
+ * netflush...
+ */
+ encrypt_output = 0;
+ if (encrypt_debug_mode)
+ (void) printf(gettext(
+ ">>>%s: Output is back to clear text\r\n"), Name);
+ if (encrypt_verbose)
+ (void) printf(gettext("[ Output is now clear text ]\r\n"));
+}
+
+static void
+encrypt_send_request_start(void)
+{
+ register uchar_t *p;
+ register int i;
+
+ p = &str_start[3];
+ *p++ = ENCRYPT_REQSTART;
+ for (i = 0; i < ki[KI_DECRYPT].keylen; ++i) {
+ if ((*p++ = ki[KI_DECRYPT].keyid[i]) == IAC)
+ *p++ = IAC;
+ }
+ *p++ = IAC;
+ *p++ = SE;
+ (void) net_write(str_start, p - str_start);
+ printsub('>', &str_start[2], p - &str_start[2]);
+ if (encrypt_debug_mode)
+ (void) printf(gettext(
+ ">>>%s: Request input to be encrypted\r\n"), Name);
+}
+
+static void
+encrypt_send_request_end(void)
+{
+ str_end[3] = ENCRYPT_REQEND;
+ (void) net_write(str_end, sizeof (str_end));
+ printsub('>', &str_end[2], sizeof (str_end) - 2);
+
+ if (encrypt_debug_mode)
+ (void) printf(gettext(
+ ">>>%s: Request input to be clear text\r\n"), Name);
+}
+
+boolean_t
+encrypt_is_encrypting(void)
+{
+ return (encrypt_output && decrypt_input ? B_TRUE : B_FALSE);
+}
+
+static void
+encrypt_gen_printsub(uchar_t *data, int cnt, uchar_t *buf, int buflen)
+{
+ char lbuf[ENCR_LBUF_BUFSIZ], *cp;
+
+ if (cnt < 2 || buflen < 2)
+ return;
+ cnt -= 2;
+ data += 2;
+ buf[buflen-1] = '\0';
+ buf[buflen-2] = '*';
+ buflen -= 2;
+ for (; cnt > 0; cnt--, data++) {
+ (void) snprintf(lbuf, ENCR_LBUF_BUFSIZ, " %d", *data);
+ for (cp = lbuf; *cp && buflen > 0; --buflen)
+ *buf++ = *cp++;
+ if (buflen <= 0)
+ return;
+ }
+ *buf = '\0';
+}
+
+void
+encrypt_printsub(uchar_t *data, int cnt, uchar_t *buf, int buflen)
+{
+ Encryptions *ep;
+ register int type = data[1];
+
+ for (ep = encryptions; ep->type && ep->type != type; ep++)
+ ;
+
+ if (ep->printsub)
+ (*ep->printsub)(data, cnt, buf, buflen);
+ else
+ encrypt_gen_printsub(data, cnt, buf, buflen);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/telnet/encrypt.h b/usr/src/cmd/cmd-inet/usr.bin/telnet/encrypt.h
new file mode 100644
index 0000000000..dec41072f9
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/telnet/encrypt.h
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)encrypt.h 8.1 (Berkeley) 6/4/93
+ */
+
+/*
+ * Copyright (C) 1990 by the Massachusetts Institute of Technology
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in 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. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * 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.
+ */
+
+#ifndef _ENCRYPT_H
+#define _ENCRYPT_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ENCR_LBUF_BUFSIZ 32 /* short temporary buffer */
+#define SAMEKEY(k1, k2) (!memcmp((void *)k1, (void *)k2, sizeof (Block))
+
+#include <arpa/telnet.h>
+
+typedef struct {
+ char *name;
+ int type;
+ void (*output)(unsigned char *, int);
+ int (*input)(int);
+ void (*init)();
+ int (*start)(int);
+ int (*is)(unsigned char *, int);
+ int (*reply)(unsigned char *, int);
+ void (*session)(Session_Key *);
+ int (*keyid)(int, unsigned char *, int *);
+ void (*printsub)(unsigned char *, int, unsigned char *, int);
+} Encryptions;
+
+#define SK_DES 1 /* Matched Kerberos v5 ENCTYPE_DES */
+
+void encrypt_support(uchar_t *, int);
+void encrypt_init(char *);
+void encrypt_send_supprt(void);
+void encrypt_auto(int);
+void decrypt_auto(int);
+void encrypt_is(unsigned char *, int);
+void encrypt_reply(unsigned char *, int);
+void encrypt_start(uchar_t *, int);
+void encrypt_start_input(int);
+void encrypt_session_key(Session_Key *);
+void encrypt_end(void);
+void encrypt_request_end(void);
+void encrypt_request_start(uchar_t *, int);
+void encrypt_enc_keyid(uchar_t *, int);
+void encrypt_dec_keyid(uchar_t *, int);
+void encrypt_end_input(void);
+void encrypt_end_output(void);
+boolean_t encrypt_is_encrypting(void);
+void encrypt_send_support(void);
+void encrypt_send_keyid(int, unsigned char *, int, int);
+void encrypt_display(void);
+void encrypt_printsub(uchar_t *, int, uchar_t *, int);
+
+void cfb64_encrypt(unsigned char *, int);
+int cfb64_decrypt(int);
+void cfb64_init(void);
+int cfb64_start(int);
+int cfb64_is(unsigned char *, int);
+int cfb64_reply(unsigned char *, int);
+void cfb64_session(Session_Key *);
+int cfb64_keyid(int, unsigned char *, int *);
+void cfb64_printsub(unsigned char *, int, unsigned char *, int);
+
+extern boolean_t encrypt_debug_mode;
+extern int (*decrypt_input)(int);
+extern void (*encrypt_output)(unsigned char *, int);
+
+int key_file_exists(void);
+void key_lookup(unsigned char *, Block);
+void key_stream_init(Block, Block, int);
+unsigned char key_stream(int, int);
+
+int EncryptStatus(void);
+int EncryptEnable(char *, char *);
+int EncryptDisable(char *, char *);
+int EncryptType(char *, char *);
+int EncryptStart(char *);
+int EncryptStartInput(void);
+int EncryptStartOutput(void);
+int EncryptStop(char *);
+int EncryptStopInput(void);
+int EncryptStopOutput(void);
+int EncryptDebug(int);
+int EncryptVerbose(int);
+int EncryptAutoEnc(int);
+int EncryptAutoDec(int);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ENCRYPT_H */
diff --git a/usr/src/cmd/cmd-inet/usr.bin/telnet/externs.h b/usr/src/cmd/cmd-inet/usr.bin/telnet/externs.h
new file mode 100644
index 0000000000..6701386b43
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/telnet/externs.h
@@ -0,0 +1,402 @@
+/*
+ * Copyright 1994-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 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.
+ *
+ * @(#)externs.h 8.1 (Berkeley) 6/6/93
+ */
+
+#ifndef _EXTERNS_H
+#define _EXTERNS_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <libintl.h>
+#include <stdio.h>
+#include <setjmp.h>
+#include <sys/filio.h>
+#ifdef USE_TERMIO
+#include <sys/termios.h>
+#define termio termios
+#endif
+#if defined(NO_CC_T) || !defined(USE_TERMIO)
+#if !defined(USE_TERMIO)
+typedef char cc_t;
+#else
+typedef unsigned char cc_t;
+#endif
+#endif
+
+#include "auth.h"
+#include "encrypt.h"
+#include <profile/prof_int.h>
+
+extern profile_options_boolean config_file_options[];
+#define forwardable_flag_set config_file_options[0].found
+#define forward_flag_set config_file_options[1].found
+#define encrypt_flag_set config_file_options[2].found
+#define autologin_set config_file_options[3].found
+
+#include <string.h>
+
+#ifndef _POSIX_VDISABLE
+#include <sys/param.h> /* pick up VDISABLE definition, mayby */
+#ifdef VDISABLE
+#define _POSIX_VDISABLE VDISABLE
+#else
+#define _POSIX_VDISABLE ((cc_t)'\377')
+#endif
+#endif
+
+#include <wait.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+
+#define SUBBUFSIZE 256
+
+extern int autologin; /* Autologin enabled */
+extern int skiprc; /* Don't process the ~/.telnetrc file */
+extern int eight; /* use eight bit mode (binary in and/or out */
+extern int flushout; /* flush output */
+extern int connected; /* Are we connected to the other side? */
+extern int globalmode; /* Mode tty should be in */
+extern int telnetport; /* Are we connected to the telnet port? */
+extern int localflow; /* Flow control handled locally */
+extern int restartany; /* If flow control, restart output on any character */
+extern int localchars; /* we recognize interrupt/quit */
+extern int donelclchars; /* the user has set "localchars" */
+extern int showoptions;
+extern int net; /* Network file descriptor */
+extern int tout; /* Terminal output file descriptor */
+extern int crlf; /* Should '\r' be mapped to <CR><LF> (or <CR><NUL>)? */
+extern int autoflush; /* flush output when interrupting? */
+extern int autosynch; /* send interrupt characters with SYNCH? */
+extern int SYNCHing; /* Is the stream in telnet SYNCH mode? */
+extern int donebinarytoggle; /* the user has put us in binary */
+extern int dontlecho; /* do we suppress local echoing right now? */
+extern int crmod;
+extern int netdata; /* Print out network data flow */
+extern int prettydump; /* Print "netdata" output in user readable format */
+extern int termdata; /* Print out terminal data flow */
+extern int eof_pending; /* Received EOF in line mode, need to send IAC-xEOF */
+extern int debug; /* Debug level */
+
+
+extern int krb5auth_flag;
+extern char *RemoteHostName;
+extern char *UserNameRequested;
+extern int forwardable_flag;
+extern int forward_flag;
+extern boolean_t wantencryption; /* User has requested encryption */
+extern int encrypt_flag; /* for reading config file for Krb5 */
+
+extern boolean_t Ambiguous(void *);
+extern boolean_t intr_happened; /* for interrupt handling */
+extern boolean_t intr_waiting;
+
+extern cc_t escape; /* Escape to command mode */
+extern cc_t rlogin; /* Rlogin mode escape character */
+extern boolean_t escape_valid;
+#ifdef KLUDGELINEMODE
+extern cc_t echoc; /* Toggle local echoing */
+#endif
+
+extern char *prompt; /* Prompt for command. */
+
+extern char doopt[];
+extern char dont[];
+extern char will[];
+extern char wont[];
+extern char options[]; /* All the little options */
+extern char *hostname; /* Who are we connected to? */
+extern void (*encrypt_output) (unsigned char *, int);
+extern int (*decrypt_input) (int);
+
+/*
+ * We keep track of each side of the option negotiation.
+ */
+
+#define MY_STATE_WILL 0x01
+#define MY_WANT_STATE_WILL 0x02
+#define MY_STATE_DO 0x04
+#define MY_WANT_STATE_DO 0x08
+
+/*
+ * Macros to check the current state of things
+ */
+
+#define my_state_is_do(opt) (options[opt]&MY_STATE_DO)
+#define my_state_is_will(opt) (options[opt]&MY_STATE_WILL)
+#define my_want_state_is_do(opt) (options[opt]&MY_WANT_STATE_DO)
+#define my_want_state_is_will(opt) (options[opt]&MY_WANT_STATE_WILL)
+
+#define my_state_is_dont(opt) (!my_state_is_do(opt))
+#define my_state_is_wont(opt) (!my_state_is_will(opt))
+#define my_want_state_is_dont(opt) (!my_want_state_is_do(opt))
+#define my_want_state_is_wont(opt) (!my_want_state_is_will(opt))
+
+#define set_my_state_do(opt) {options[opt] |= MY_STATE_DO; }
+#define set_my_state_will(opt) {options[opt] |= MY_STATE_WILL; }
+#define set_my_want_state_do(opt) {options[opt] |= MY_WANT_STATE_DO; }
+#define set_my_want_state_will(opt) {options[opt] |= MY_WANT_STATE_WILL; }
+
+#define set_my_state_dont(opt) {options[opt] &= ~MY_STATE_DO; }
+#define set_my_state_wont(opt) {options[opt] &= ~MY_STATE_WILL; }
+#define set_my_want_state_dont(opt) {options[opt] &= ~MY_WANT_STATE_DO; }
+#define set_my_want_state_wont(opt) {options[opt] &= ~MY_WANT_STATE_WILL; }
+
+/*
+ * Make everything symetrical
+ */
+
+#define HIS_STATE_WILL MY_STATE_DO
+#define HIS_WANT_STATE_WILL MY_WANT_STATE_DO
+#define HIS_STATE_DO MY_STATE_WILL
+#define HIS_WANT_STATE_DO MY_WANT_STATE_WILL
+
+#define his_state_is_do my_state_is_will
+#define his_state_is_will my_state_is_do
+#define his_want_state_is_do my_want_state_is_will
+#define his_want_state_is_will my_want_state_is_do
+
+#define his_state_is_dont my_state_is_wont
+#define his_state_is_wont my_state_is_dont
+#define his_want_state_is_dont my_want_state_is_wont
+#define his_want_state_is_wont my_want_state_is_dont
+
+#define set_his_state_do set_my_state_will
+#define set_his_state_will set_my_state_do
+#define set_his_want_state_do set_my_want_state_will
+#define set_his_want_state_will set_my_want_state_do
+
+#define set_his_state_dont set_my_state_wont
+#define set_his_state_wont set_my_state_dont
+#define set_his_want_state_dont set_my_want_state_wont
+#define set_his_want_state_wont set_my_want_state_dont
+
+
+extern FILE *NetTrace; /* Where debugging output goes */
+ /* Name of file where debugging output goes */
+extern unsigned char NetTraceFile[];
+extern void SetNetTrace(char *); /* Function to change where debugging goes */
+
+extern jmp_buf peerdied;
+extern jmp_buf toplevel; /* For error conditions. */
+
+extern char *AllocStringBuffer(char **, unsigned int *, unsigned int);
+extern void ExitString(char *, int);
+extern void Exit(int);
+extern void command(int, char *, int);
+extern void Dump(int, unsigned char *, int);
+extern char *GetAndAppendString(char **, unsigned int *, char *, FILE *);
+extern char *GetString(char **, unsigned int *, FILE *);
+extern void init_network(void);
+extern void init_terminal(void);
+extern void init_sys(void);
+extern void optionstatus(void);
+extern void printoption(char *, int, int);
+extern void printsub(int, unsigned char *, int);
+extern void sendnaws(void);
+extern void setconnmode(int);
+extern void setcommandmode(void);
+extern void setneturg(void);
+extern void sys_telnet_init(void);
+extern void telnet(char *);
+extern void tel_enter_binary(int);
+extern void tel_leave_binary(int);
+extern void TerminalDefaultChars(void);
+extern void TerminalFlushOutput(void);
+extern void TerminalNewMode(int);
+extern void TerminalSaveState(void);
+extern void TerminalSpeeds(int *, int *);
+extern void upcase(char *);
+
+extern void xmitEL(void);
+extern void xmitEC(void);
+extern void intp(void);
+extern void sendabort(void);
+extern void sendsusp(void);
+extern void set_escape_char(char *);
+extern void fatal_tty_error(char *);
+
+extern void send_do(int, int);
+extern void send_dont(int, int);
+extern void send_will(int, int);
+extern void send_wont(int, int);
+
+extern void lm_mode(unsigned char *, int, int);
+
+extern void slcstate(void);
+extern void slc_mode_export(void);
+extern void slc_mode_import(int);
+extern void slc_check(void);
+
+extern void env_opt_start_info(void);
+extern void env_opt_add(unsigned char *);
+extern void env_opt_end(int);
+
+extern char **genget(char *, char **, int);
+extern unsigned char *env_default(int, int);
+extern unsigned char *env_getvalue(unsigned char *);
+
+extern int env_init(void);
+extern int get_status(void);
+extern int init_telnet(void);
+extern int isprefix(register char *, register char *);
+extern int netflush(void);
+extern int opt_welldefined(char *);
+extern int process_rings(int, int, int, int, int, int);
+extern int quit(void);
+extern int rlogin_susp(void);
+extern int Scheduler(int);
+extern int SetSockOpt(int, int, int, int);
+extern int stilloob(void);
+extern int telrcv(void);
+extern int TerminalWindowSize(unsigned short *, unsigned short *);
+extern int TerminalWrite(char *, int);
+extern int TerminalSpecialChars(int);
+extern int tn(int, char **);
+extern int tninit(void);
+extern int ttyflush(int);
+extern int getconnmode(void);
+extern int xmitAO(void);
+extern int sendbrk(void);
+extern int dosynch(void);
+
+extern cc_t *tcval(int);
+
+extern void auth_encrypt_init(char *, char *, char *);
+extern void auth_encrypt_user(char *);
+extern int net_write(unsigned char *, int len);
+extern void net_encrypt(void);
+extern void telnet_spin(void);
+extern void printd(unsigned char *, int);
+
+#ifndef USE_TERMIO
+
+extern struct tchars ntc;
+extern struct ltchars nltc;
+extern struct sgttyb nttyb;
+
+#define termEofChar ntc.t_eofc
+#define termEraseChar nttyb.sg_erase
+#define termFlushChar nltc.t_flushc
+#define termIntChar ntc.t_intrc
+#define termKillChar nttyb.sg_kill
+#define termLiteralNextChar nltc.t_lnextc
+#define termQuitChar ntc.t_quitc
+#define termSuspChar nltc.t_suspc
+#define termRprntChar nltc.t_rprntc
+#define termWerasChar nltc.t_werasc
+#define termStartChar ntc.t_startc
+#define termStopChar ntc.t_stopc
+#define termForw1Char ntc.t_brkc
+extern cc_t termForw2Char;
+extern cc_t termAytChar;
+
+#define termEofCharp (cc_t *)&ntc.t_eofc
+#define termEraseCharp (cc_t *)&nttyb.sg_erase
+#define termFlushCharp (cc_t *)&nltc.t_flushc
+#define termIntCharp (cc_t *)&ntc.t_intrc
+#define termKillCharp (cc_t *)&nttyb.sg_kill
+#define termLiteralNextCharp (cc_t *)&nltc.t_lnextc
+#define termQuitCharp (cc_t *)&ntc.t_quitc
+#define termSuspCharp (cc_t *)&nltc.t_suspc
+#define termRprntCharp (cc_t *)&nltc.t_rprntc
+#define termWerasCharp (cc_t *)&nltc.t_werasc
+#define termStartCharp (cc_t *)&ntc.t_startc
+#define termStopCharp (cc_t *)&ntc.t_stopc
+#define termForw1Charp (cc_t *)&ntc.t_brkc
+#define termForw2Charp (cc_t *)&termForw2Char
+#define termAytCharp (cc_t *)&termAytChar
+
+#else
+
+extern struct termio new_tc;
+
+#define termEofChar new_tc.c_cc[VEOF]
+#define termEraseChar new_tc.c_cc[VERASE]
+#define termIntChar new_tc.c_cc[VINTR]
+#define termKillChar new_tc.c_cc[VKILL]
+#define termQuitChar new_tc.c_cc[VQUIT]
+
+#define termSuspChar new_tc.c_cc[VSUSP]
+#define termFlushChar new_tc.c_cc[VDISCARD]
+#define termWerasChar new_tc.c_cc[VWERASE]
+#define termRprntChar new_tc.c_cc[VREPRINT]
+#define termLiteralNextChar new_tc.c_cc[VLNEXT]
+#define termStartChar new_tc.c_cc[VSTART]
+#define termStopChar new_tc.c_cc[VSTOP]
+#define termForw1Char new_tc.c_cc[VEOL]
+#define termForw2Char new_tc.c_cc[VEOL]
+extern cc_t termAytChar;
+
+#define termEofCharp &termEofChar
+#define termEraseCharp &termEraseChar
+#define termIntCharp &termIntChar
+#define termKillCharp &termKillChar
+#define termQuitCharp &termQuitChar
+#define termSuspCharp &termSuspChar
+#define termFlushCharp &termFlushChar
+#define termWerasCharp &termWerasChar
+#define termRprntCharp &termRprntChar
+#define termLiteralNextCharp &termLiteralNextChar
+#define termStartCharp &termStartChar
+#define termStopCharp &termStopChar
+#define termForw1Charp &termForw1Char
+#define termForw2Charp &termForw2Char
+#define termAytCharp &termAytChar
+#endif
+
+
+/* Ring buffer structures which are shared */
+
+#include "ring.h"
+
+extern Ring netoring;
+extern Ring netiring;
+extern Ring ttyoring;
+extern Ring ttyiring;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _EXTERNS_H */
diff --git a/usr/src/cmd/cmd-inet/usr.bin/telnet/general.h b/usr/src/cmd/cmd-inet/usr.bin/telnet/general.h
new file mode 100644
index 0000000000..2ddc76bafd
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/telnet/general.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 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.
+ *
+ * @(#)general.h 8.1 (Berkeley) 6/6/93
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _GENERAL_H
+#define _GENERAL_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Some general definitions.
+ */
+
+
+#define numberof(x) (sizeof (x)/sizeof (x[0]))
+#define highestof(x) (numberof(x)-1)
+
+#define ClearElement(x) ((void) memset(&x, 0, sizeof (x)))
+#define ClearArray(x) ((void) memset(x, 0, sizeof (x)))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _GENERAL_H */
diff --git a/usr/src/cmd/cmd-inet/usr.bin/telnet/genget.c b/usr/src/cmd/cmd-inet/usr.bin/telnet/genget.c
new file mode 100644
index 0000000000..31c79f9557
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/telnet/genget.c
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)genget.c 8.1 (Berkeley) 6/4/93";
+#endif /* not lint */
+
+#include <ctype.h>
+#include <sys/types.h>
+
+#define LOWER(x) (isupper(x) ? tolower(x) : (x))
+
+static char *ambiguous; /* special return value for command routines */
+
+/*
+ * The prefix function returns 0 if *s1 is not a prefix
+ * of *s2. If *s1 exactly matches *s2, the negative of
+ * the length is returned. If *s1 is a prefix of *s2,
+ * the length of *s1 is returned.
+ */
+int
+isprefix(char *s1, char *s2)
+{
+ char *os1;
+ char c1, c2;
+
+ if (*s1 == '\0')
+ return (-1);
+ os1 = s1;
+ c1 = *s1;
+ c2 = *s2;
+ while (LOWER(c1) == LOWER(c2)) {
+ if (c1 == '\0')
+ break;
+ c1 = *++s1;
+ c2 = *++s2;
+ }
+ return (*s1 ? 0 : (*s2 ? (s1 - os1) : (os1 - s1)));
+}
+
+
+/*
+ * Get an entry from the table, from what might be a prefix
+ *
+ * name = name to match
+ * table = name entry in table
+ */
+char **
+genget(char *name, char **table, int stlen)
+{
+ register char **c, **found;
+ register int n;
+
+ if (name == 0)
+ return (0);
+
+ found = 0;
+ /*LINTED*/
+ for (c = table; *c != NULL; c = (char **)((char *)c + stlen)) {
+ if ((n = isprefix(name, *c)) == 0)
+ continue;
+ if (n < 0) /* exact match */
+ return (c);
+ if (found)
+ return (&ambiguous);
+ found = c;
+ }
+ return (found);
+}
+
+/*
+ * Function call version of Ambiguous()
+ */
+boolean_t
+Ambiguous(void *s)
+{
+ return ((char **)s == &ambiguous);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/telnet/inc.flg b/usr/src/cmd/cmd-inet/usr.bin/telnet/inc.flg
new file mode 100644
index 0000000000..4adc8618d3
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/telnet/inc.flg
@@ -0,0 +1,32 @@
+#!/bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+find_files "s.*" usr/src/uts/common/gssapi/mechs/krb5/include
+find_files "s.*" usr/src/lib/gss_mechs/mech_krb5/include
+find_files "s.*.h" usr/src/lib/gss_mechs/mech_krb5/profile
diff --git a/usr/src/cmd/cmd-inet/usr.bin/telnet/kerberos5.c b/usr/src/cmd/cmd-inet/usr.bin/telnet/kerberos5.c
new file mode 100644
index 0000000000..cef205c348
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/telnet/kerberos5.c
@@ -0,0 +1,650 @@
+/*
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * usr/src/cmd/cmd-inet/usr.bin/telnet/kerberos5.c
+ *
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* based on @(#)kerberos5.c 8.1 (Berkeley) 6/4/93 */
+
+/*
+ * Copyright (C) 1990 by the Massachusetts Institute of Technology
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in 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. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * 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.
+ */
+
+
+#include <arpa/telnet.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <syslog.h>
+#include <stdlib.h>
+
+/* the following are from the kerberos tree */
+#include <k5-int.h>
+#include <com_err.h>
+#include <netdb.h>
+#include <profile/prof_int.h>
+#include <sys/param.h>
+#include "externs.h"
+
+extern char *RemoteHostName;
+extern boolean_t auth_debug_mode;
+extern int net;
+
+#define DEFAULT_ENCTYPE ENCTYPE_DES_CBC_CRC
+#define ACCEPT_ENCTYPES (ENCTYPE_DES_CBC_CRC | ENCTYPE_DES_CBC_MD5)
+/* for comapatibility with non-Solaris KDC's, this has to be big enough */
+#define KERBEROS_BUFSIZ 8192
+
+int forward_flags = 0; /* Flags get set in telnet/main.c on -f and -F */
+static void kerberos5_forward(Authenticator *);
+
+static unsigned char str_data[KERBEROS_BUFSIZ] = { IAC, SB,
+ TELOPT_AUTHENTICATION, 0, AUTHTYPE_KERBEROS_V5, };
+static char *appdef[] = { "appdefaults", "telnet", NULL };
+static char *realmdef[] = { "realms", NULL, "telnet", NULL };
+
+static krb5_auth_context auth_context = 0;
+
+static krb5_data auth; /* telnetd gets session key from here */
+static krb5_ticket *ticket = NULL;
+ /* telnet matches the AP_REQ and AP_REP with this */
+
+static krb5_keyblock *session_key = 0;
+char *telnet_krb5_realm = NULL;
+
+/*
+ * Change the kerberos realm
+ */
+void
+set_krb5_realm(char *name)
+{
+ if (name == NULL) {
+ (void) fprintf(stderr, gettext("Could not set Kerberos realm, "
+ "no realm provided.\n"));
+ return;
+ }
+
+ if (telnet_krb5_realm)
+ free(telnet_krb5_realm);
+
+ telnet_krb5_realm = (char *)strdup(name);
+
+ if (telnet_krb5_realm == NULL)
+ (void) fprintf(stderr, gettext(
+ "Could not set Kerberos realm, malloc failed\n"));
+}
+
+#define RETURN_NOMEM { errno = ENOMEM; return (-1); }
+
+static int
+krb5_send_data(Authenticator *ap, int type, krb5_pointer d, int c)
+{
+ /* the first 3 bytes are control chars */
+ unsigned char *p = str_data + 4;
+ unsigned char *cd = (unsigned char *)d;
+ /* spaceleft is incremented whenever p is decremented */
+ size_t spaceleft = sizeof (str_data) - 4;
+
+ if (c == -1)
+ c = strlen((char *)cd);
+
+ if (auth_debug_mode) {
+ (void) printf("%s:%d: [%d] (%d)",
+ str_data[3] == TELQUAL_IS ? ">>>IS" : ">>>REPLY",
+ str_data[3], type, c);
+ printd(d, c);
+ (void) printf("\r\n");
+ }
+
+ if (spaceleft < 3)
+ RETURN_NOMEM;
+ *p++ = ap->type;
+ *p++ = ap->way;
+ *p++ = type;
+ spaceleft -= 3;
+
+ while (c-- > 0) {
+ if (spaceleft < 2)
+ RETURN_NOMEM;
+ if ((*p++ = *cd++) == IAC) {
+ *p++ = IAC;
+ spaceleft -= 2;
+ }
+ }
+
+ if (spaceleft < 2)
+ RETURN_NOMEM;
+ *p++ = IAC;
+ *p++ = SE;
+ if (str_data[3] == TELQUAL_IS)
+ printsub('>', &str_data[2], p - &str_data[2]);
+ return (net_write(str_data, p - str_data));
+}
+
+krb5_context telnet_context = 0;
+
+/* ARGSUSED */
+int
+kerberos5_init(Authenticator *ap)
+{
+ krb5_error_code retval;
+
+ str_data[3] = TELQUAL_IS;
+ if (krb5auth_flag && (telnet_context == 0)) {
+ retval = krb5_init_context(&telnet_context);
+ if (retval)
+ return (0);
+ }
+ return (1);
+}
+
+int
+kerberos5_send(Authenticator *ap)
+{
+ krb5_error_code retval;
+ krb5_ccache ccache;
+ krb5_creds creds; /* telnet gets session key from here */
+ krb5_creds *new_creds = 0;
+ int ap_opts;
+ char type_check[2];
+ krb5_data check_data;
+
+ krb5_keyblock *newkey = 0;
+
+ if (!UserNameRequested) {
+ if (auth_debug_mode)
+ (void) printf(gettext("telnet: Kerberos V5: "
+ "no user name supplied\r\n"));
+ return (0);
+ }
+
+ if ((retval = krb5_cc_default(telnet_context, &ccache))) {
+ if (auth_debug_mode)
+ (void) printf(gettext("telnet: Kerberos V5: "
+ "could not get default ccache\r\n"));
+ return (0);
+ }
+
+ (void) memset((char *)&creds, 0, sizeof (creds));
+ if ((retval = krb5_sname_to_principal(telnet_context, RemoteHostName,
+ "host", KRB5_NT_SRV_HST, &creds.server))) {
+ if (auth_debug_mode)
+ (void) printf(gettext("telnet: Kerberos V5: error "
+ "while constructing service name: %s\r\n"),
+ error_message(retval));
+ return (0);
+ }
+
+ if (telnet_krb5_realm != NULL) {
+ krb5_data rdata;
+
+ rdata.length = strlen(telnet_krb5_realm);
+ rdata.data = (char *)malloc(rdata.length + 1);
+ if (rdata.data == NULL) {
+ (void) fprintf(stderr, gettext("malloc failed\n"));
+ return (0);
+ }
+ (void) strcpy(rdata.data, telnet_krb5_realm);
+ krb5_princ_set_realm(telnet_context, creds.server, &rdata);
+ if (auth_debug_mode)
+ (void) printf(gettext(
+ "telnet: Kerberos V5: set kerberos realm to %s\r\n"),
+ telnet_krb5_realm);
+ }
+
+ if ((retval = krb5_cc_get_principal(telnet_context, ccache,
+ &creds.client)) != NULL) {
+ if (auth_debug_mode) {
+ (void) printf(gettext(
+ "telnet: Kerberos V5: failure on principal "
+ "(%s)\r\n"), error_message(retval));
+ }
+ krb5_free_cred_contents(telnet_context, &creds);
+ return (0);
+ }
+
+ creds.keyblock.enctype = DEFAULT_ENCTYPE;
+ if ((retval = krb5_get_credentials(telnet_context, 0,
+ ccache, &creds, &new_creds))) {
+ if (auth_debug_mode) {
+ (void) printf(gettext(
+ "telnet: Kerberos V5: failure on credentials "
+ "(%s)\r\n"), error_message(retval));
+ }
+ krb5_free_cred_contents(telnet_context, &creds);
+ return (0);
+ }
+
+ ap_opts = ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) ?
+ AP_OPTS_MUTUAL_REQUIRED : 0;
+
+ ap_opts |= AP_OPTS_USE_SUBKEY;
+
+ if (auth_context) {
+ krb5_auth_con_free(telnet_context, auth_context);
+ auth_context = 0;
+ }
+ if ((retval = krb5_auth_con_init(telnet_context, &auth_context))) {
+ if (auth_debug_mode) {
+ (void) printf(gettext(
+ "Kerberos V5: failed to init auth_context "
+ "(%s)\r\n"), error_message(retval));
+ }
+ return (0);
+ }
+
+ krb5_auth_con_setflags(telnet_context, auth_context,
+ KRB5_AUTH_CONTEXT_RET_TIME);
+
+ type_check[0] = ap->type;
+ type_check[1] = ap->way;
+ check_data.magic = KV5M_DATA;
+ check_data.length = 2;
+ check_data.data = (char *)&type_check;
+
+ retval = krb5_mk_req_extended(telnet_context, &auth_context, ap_opts,
+ &check_data, new_creds, &auth);
+
+ krb5_auth_con_getlocalsubkey(telnet_context, auth_context, &newkey);
+ if (session_key) {
+ krb5_free_keyblock(telnet_context, session_key);
+ session_key = 0;
+ }
+
+ if (newkey) {
+ /*
+ * keep the key in our private storage, but don't use it
+ * yet---see kerberos5_reply() below
+ */
+ if (!(newkey->enctype & ACCEPT_ENCTYPES)) {
+ if (!(new_creds->keyblock.enctype & ACCEPT_ENCTYPES))
+ /* use the session key in credentials instead */
+ krb5_copy_keyblock(telnet_context,
+ &new_creds->keyblock, &session_key);
+ } else
+ krb5_copy_keyblock(telnet_context,
+ newkey, &session_key);
+
+ krb5_free_keyblock(telnet_context, newkey);
+ }
+
+ krb5_free_cred_contents(telnet_context, &creds);
+ krb5_free_creds(telnet_context, new_creds);
+
+ if (retval) {
+ if (auth_debug_mode)
+ (void) printf(gettext(
+ "telnet: Kerberos V5: mk_req failed (%s)\r\n"),
+ error_message(retval));
+ return (0);
+ }
+
+ if ((auth_sendname((uchar_t *)UserNameRequested,
+ strlen(UserNameRequested))) == NULL) {
+ if (auth_debug_mode)
+ (void) printf(gettext(
+ "telnet: Not enough room for user name\r\n"));
+ return (0);
+ }
+ retval = krb5_send_data(ap, KRB_AUTH, auth.data, auth.length);
+ if (auth_debug_mode && retval) {
+ (void) printf(gettext(
+ "telnet: Sent Kerberos V5 credentials to server\r\n"));
+ } else if (auth_debug_mode) {
+ (void) printf(gettext(
+ "telnet: Not enough room for authentication data\r\n"));
+ return (0);
+ }
+ return (1);
+}
+
+void
+kerberos5_reply(Authenticator *ap, unsigned char *data, int cnt)
+{
+ Session_Key skey;
+ static boolean_t mutual_complete = B_FALSE;
+
+ if (cnt-- < 1)
+ return;
+ switch (*data++) {
+ case KRB_REJECT:
+ if (cnt > 0)
+ (void) printf(gettext(
+ "[ Kerberos V5 refuses authentication because "
+ "%.*s ]\r\n"), cnt, data);
+ else
+ (void) printf(gettext(
+ "[ Kerberos V5 refuses authentication ]\r\n"));
+ auth_send_retry();
+ return;
+ case KRB_ACCEPT:
+ if (!mutual_complete) {
+ if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) {
+ (void) printf(gettext(
+ "[ Kerberos V5 accepted you, but didn't "
+ "provide mutual authentication! ]\r\n"));
+ auth_send_retry();
+ return;
+ }
+
+ if (session_key) {
+ skey.type = SK_DES;
+ skey.length = 8;
+ skey.data = session_key->contents;
+ encrypt_session_key(&skey);
+ }
+ }
+ if (cnt)
+ (void) printf(gettext(
+ "[ Kerberos V5 accepts you as ``%.*s'' ]\r\n"),
+ cnt, data);
+ else
+ (void) printf(gettext(
+ "[ Kerberos V5 accepts you ]\r\n"));
+ auth_finished(ap, AUTH_USER);
+
+ if (forward_flags & OPTS_FORWARD_CREDS)
+ kerberos5_forward(ap);
+
+ break;
+ case KRB_RESPONSE:
+ if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) {
+ /* the rest of the reply should contain a krb_ap_rep */
+ krb5_ap_rep_enc_part *reply;
+ krb5_data inbuf;
+ krb5_error_code retval;
+
+ inbuf.length = cnt;
+ inbuf.data = (char *)data;
+
+ retval = krb5_rd_rep(telnet_context, auth_context,
+ &inbuf, &reply);
+ if (retval) {
+ (void) printf(gettext(
+ "[ Mutual authentication failed: "
+ "%s ]\r\n"), error_message(retval));
+ auth_send_retry();
+ return;
+ }
+ krb5_free_ap_rep_enc_part(telnet_context, reply);
+
+ if (session_key) {
+ skey.type = SK_DES;
+ skey.length = 8;
+ skey.data = session_key->contents;
+ encrypt_session_key(&skey);
+ }
+ mutual_complete = B_TRUE;
+ }
+ return;
+ case KRB_FORWARD_ACCEPT:
+ (void) printf(gettext(
+ "[ Kerberos V5 accepted forwarded credentials ]\r\n"));
+ return;
+ case KRB_FORWARD_REJECT:
+ (void) printf(gettext(
+ "[ Kerberos V5 refuses forwarded credentials because "
+ "%.*s ]\r\n"), cnt, data);
+ return;
+ default:
+ if (auth_debug_mode)
+ (void) printf(gettext(
+ "Unknown Kerberos option %d\r\n"), data[-1]);
+ return;
+ }
+}
+
+/* ARGSUSED */
+int
+kerberos5_status(Authenticator *ap, char *name, int level)
+{
+ if (level < AUTH_USER)
+ return (level);
+
+ if (UserNameRequested && krb5_kuserok(telnet_context,
+ ticket->enc_part2->client, UserNameRequested)) {
+
+ /* the name buffer comes from telnetd/telnetd{-ktd}.c */
+ (void) strncpy(name, UserNameRequested, MAXNAMELEN);
+ name[MAXNAMELEN-1] = '\0';
+ return (AUTH_VALID);
+ } else
+ return (AUTH_USER);
+}
+
+#define BUMP(buf, len) while (*(buf)) {++(buf), --(len); }
+#define ADDC(buf, len, c) if ((len) > 0) {*(buf)++ = (c); --(len); }
+
+/*
+ * Used with the set opt command to print suboptions
+ */
+void
+kerberos5_printsub(unsigned char *data, int cnt, unsigned char *buf, int buflen)
+{
+ char lbuf[AUTH_LBUF_BUFSIZ];
+ register int i;
+
+ buf[buflen-1] = '\0'; /* make sure its NULL terminated */
+ buflen -= 1;
+
+ switch (data[3]) {
+ case KRB_REJECT: /* Rejected (reason might follow) */
+ (void) strncpy((char *)buf, " REJECT ", buflen);
+ goto common;
+
+ case KRB_ACCEPT: /* Accepted (name might follow) */
+ (void) strncpy((char *)buf, " ACCEPT ", buflen);
+ common:
+ BUMP(buf, buflen);
+ if (cnt <= 4)
+ break;
+ ADDC(buf, buflen, '"');
+ for (i = 4; i < cnt; i++)
+ ADDC(buf, buflen, data[i]);
+ ADDC(buf, buflen, '"');
+ ADDC(buf, buflen, '\0');
+ break;
+
+ case KRB_AUTH: /* Authentication data follows */
+ (void) strncpy((char *)buf, " AUTH", buflen);
+ goto common2;
+
+ case KRB_RESPONSE:
+ (void) strncpy((char *)buf, " RESPONSE", buflen);
+ goto common2;
+
+ case KRB_FORWARD: /* Forwarded credentials follow */
+ (void) strncpy((char *)buf, " FORWARD", buflen);
+ goto common2;
+
+ case KRB_FORWARD_ACCEPT: /* Forwarded credentials accepted */
+ (void) strncpy((char *)buf, " FORWARD_ACCEPT", buflen);
+ goto common2;
+
+ case KRB_FORWARD_REJECT: /* Forwarded credentials rejected */
+ /* (reason might follow) */
+ (void) strncpy((char *)buf, " FORWARD_REJECT", buflen);
+ goto common2;
+
+ default:
+ (void) snprintf(lbuf, AUTH_LBUF_BUFSIZ,
+ gettext(" %d (unknown)"),
+ data[3]);
+ (void) strncpy((char *)buf, lbuf, buflen);
+ common2:
+ BUMP(buf, buflen);
+ for (i = 4; i < cnt; i++) {
+ (void) snprintf(lbuf, AUTH_LBUF_BUFSIZ, " %d", data[i]);
+ (void) strncpy((char *)buf, lbuf, buflen);
+ BUMP(buf, buflen);
+ }
+ break;
+ }
+}
+
+void
+krb5_profile_get_options(char *host, char *realm,
+ profile_options_boolean *optionsp)
+{
+ char **realms = NULL;
+ krb5_error_code err = 0;
+
+ if (!telnet_context) {
+ err = krb5_init_context(&telnet_context);
+ if (err) {
+ (void) fprintf(stderr, gettext(
+ "Error initializing Kerberos 5 library: %s\n"),
+ error_message(err));
+ return;
+ }
+ }
+
+ if ((realmdef[1] = realm) == NULL) {
+ err = krb5_get_host_realm(telnet_context, host, &realms);
+ if (err) {
+ (void) fprintf(stderr, gettext(
+ "Error getting Kerberos 5 realms for: %s (%s)\n"),
+ host, error_message(err));
+ return;
+ }
+ realmdef[1] = realms[0];
+ }
+
+ profile_get_options_boolean(telnet_context->profile,
+ realmdef, optionsp);
+ profile_get_options_boolean(telnet_context->profile,
+ appdef, optionsp);
+}
+
+static void
+kerberos5_forward(Authenticator *ap)
+{
+ krb5_error_code retval;
+ krb5_ccache ccache;
+ krb5_principal client = 0;
+ krb5_principal server = 0;
+ krb5_data forw_creds;
+
+ forw_creds.data = 0;
+
+ if ((retval = krb5_cc_default(telnet_context, &ccache))) {
+ if (auth_debug_mode)
+ (void) printf(gettext(
+ "Kerberos V5: could not get default ccache - %s\r\n"),
+ error_message(retval));
+ return;
+ }
+
+ retval = krb5_cc_get_principal(telnet_context, ccache, &client);
+ if (retval) {
+ if (auth_debug_mode)
+ (void) printf(gettext(
+ "Kerberos V5: could not get default "
+ "principal - %s\r\n"), error_message(retval));
+ goto cleanup;
+ }
+
+ retval = krb5_sname_to_principal(telnet_context, RemoteHostName,
+ "host", KRB5_NT_SRV_HST, &server);
+ if (retval) {
+ if (auth_debug_mode)
+ (void) printf(gettext(
+ "Kerberos V5: could not make server "
+ "principal - %s\r\n"), error_message(retval));
+ goto cleanup;
+ }
+
+ retval = krb5_auth_con_genaddrs(telnet_context, auth_context, net,
+ KRB5_AUTH_CONTEXT_GENERATE_LOCAL_FULL_ADDR);
+ if (retval) {
+ if (auth_debug_mode)
+ (void) printf(gettext(
+ "Kerberos V5: could not gen local full "
+ "address - %s\r\n"), error_message(retval));
+ goto cleanup;
+ }
+
+ retval = krb5_fwd_tgt_creds(telnet_context, auth_context, 0, client,
+ server, ccache, forward_flags & OPTS_FORWARDABLE_CREDS,
+ &forw_creds);
+ if (retval) {
+ if (auth_debug_mode)
+ (void) printf(gettext(
+ "Kerberos V5: error getting forwarded "
+ "creds - %s\r\n"), error_message(retval));
+ goto cleanup;
+ }
+
+ /* Send forwarded credentials */
+ if (!krb5_send_data(ap, KRB_FORWARD, forw_creds.data,
+ forw_creds.length)) {
+ if (auth_debug_mode)
+ (void) printf(gettext(
+ "Not enough room for authentication data\r\n"));
+ } else if (auth_debug_mode)
+ (void) printf(gettext(
+ "Forwarded local Kerberos V5 credentials to server\r\n"));
+cleanup:
+ if (client)
+ krb5_free_principal(telnet_context, client);
+ if (server)
+ krb5_free_principal(telnet_context, server);
+ if (forw_creds.data)
+ free(forw_creds.data);
+ /* LINTED */
+ krb5_cc_close(telnet_context, ccache);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/telnet/main.c b/usr/src/cmd/cmd-inet/usr.bin/telnet/main.c
new file mode 100644
index 0000000000..e0caacc66b
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/telnet/main.c
@@ -0,0 +1,283 @@
+/*
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Copyright (c) 1988, 1990 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1988, 1990 Regents of the University of California.\n"
+" All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)main.c 5.5 (Berkeley) 12/18/92";
+#endif /* not lint */
+
+#include <string.h>
+#include <sys/types.h>
+
+#include "ring.h"
+#include "externs.h"
+#include "defines.h"
+
+/* These values need to be the same as defined in libtelnet/kerberos5.c */
+/* Either define them in both places, or put in some common header file. */
+#define OPTS_FORWARD_CREDS 0x00000002
+#define OPTS_FORWARDABLE_CREDS 0x00000001
+
+/*
+ * This flag is incremented, if any of the
+ * Kerberos command line options are used.
+ */
+int krb5auth_flag = 0;
+
+/*
+ * Initialize variables.
+ */
+int
+tninit()
+{
+ init_terminal();
+
+ init_network();
+
+ if (init_telnet() == 0)
+ return (0);
+
+ init_sys();
+
+ return (1);
+}
+
+#if defined(USE_TOS)
+#define TELNET_OPTIONS "8EKLS:X:acde:fFk:l:n:rt:x"
+#else
+#define TELNET_OPTIONS "8EKLX:acde:fFk:l:n:rt:x"
+#endif /* USE_TOS */
+
+static void
+usage()
+{
+ (void) fprintf(stderr, "Usage: %s %s\n",
+ prompt,
+ " [-8] [-E] [-K] [-L] [-a] [-c] [-d] [-f/-F] [-r] [-x]"
+ "\n\t[-e char] [-k realm] [-l user] [-n tracefile] [-X atype]"
+ "\n\t[host-name [port]]");
+ exit(1);
+}
+
+/*
+ * main. Parse arguments, invoke the protocol or command parser.
+ */
+
+
+int
+main(int argc, char *argv[])
+{
+ int ch;
+ char *user;
+ extern boolean_t auth_enable_encrypt;
+ extern int forward_flags;
+
+ /* Clear out things */
+ if (tninit() == 0)
+ return (EXIT_FAILURE);
+
+ if (!isatty(fileno(stdin))) {
+ setbuf(stdin, NULL);
+ }
+ if (!isatty(fileno(stdout))) {
+ setbuf(stdout, NULL);
+ }
+
+ TerminalSaveState();
+
+ if (prompt = strrchr(argv[0], '/'))
+ ++prompt;
+ else
+ prompt = argv[0];
+
+ user = NULL;
+
+ rlogin = (strncmp(prompt, "rlog", 4) == 0) ? '~' : _POSIX_VDISABLE;
+ autologin = -1;
+
+ while ((ch = getopt(argc, argv, TELNET_OPTIONS)) != EOF) {
+ switch (ch) {
+ case 'K':
+ autologin_set = 1;
+ autologin = 0;
+ krb5auth_flag++;
+ break;
+ case 'X':
+ auth_disable_name(optarg);
+ krb5auth_flag++;
+ break;
+ case 'a':
+ autologin_set = 1;
+ autologin = 1;
+ krb5auth_flag++;
+ break;
+ case 'f':
+ if (forward_flags & OPTS_FORWARD_CREDS) {
+ (void) fprintf(stderr, gettext(
+ "%s: Only one of -f "
+ "and -F allowed.\n"), prompt);
+ usage();
+ }
+ forward_flags |= OPTS_FORWARD_CREDS;
+ forward_flag_set = 1;
+ krb5auth_flag++;
+ break;
+ case 'F':
+ if (forward_flags & OPTS_FORWARD_CREDS) {
+ (void) fprintf(stderr, gettext(
+ "%s: Only one of -f "
+ "and -F allowed.\n"), prompt);
+ usage();
+ }
+ forward_flags |= OPTS_FORWARD_CREDS;
+ forward_flags |= OPTS_FORWARDABLE_CREDS;
+ forwardable_flag_set = 1;
+ forward_flag = 1;
+ krb5auth_flag++;
+ break;
+ case 'k':
+ set_krb5_realm(optarg);
+ krb5auth_flag++;
+ break;
+ case 'x':
+ if (krb5_privacy_allowed()) {
+ encrypt_auto(1);
+ decrypt_auto(1);
+ wantencryption = B_TRUE;
+ autologin = 1;
+ autologin_set = 1;
+ auth_enable_encrypt = B_TRUE;
+ encrypt_flag_set = 1;
+ krb5auth_flag++;
+ } else {
+ (void) fprintf(stderr, gettext(
+ "%s: Encryption not supported.\n"),
+ prompt);
+ exit(1);
+ }
+ break;
+
+ /* begin common options */
+ case '8':
+ eight = 3; /* binary output and input */
+ break;
+ case 'E':
+ escape_valid = B_FALSE;
+ rlogin = escape = _POSIX_VDISABLE;
+ break;
+ case 'L':
+ eight |= 2; /* binary output only */
+ break;
+#if USE_TOS
+ case 'S':
+ (void) fprintf(stderr,
+ "%s: Warning: -S ignored, no parsetos() support.\n",
+ prompt);
+ break;
+#endif /* USE_TOS */
+ case 'c':
+ skiprc = 1;
+ break;
+ case 'd':
+ debug = 1;
+ break;
+ case 'e':
+ escape_valid = B_TRUE;
+ set_escape_char(optarg);
+ break;
+ case 'l':
+ autologin_set = 1;
+ autologin = 1;
+ user = optarg;
+ break;
+ case 'n':
+ SetNetTrace(optarg);
+ break;
+ case 'r':
+ rlogin = '~';
+ break;
+ case 't':
+ (void) fprintf(stderr,
+ "%s: Warning: -t ignored, no TN3270 support.\n",
+ prompt);
+ break;
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+ if (autologin == -1)
+ autologin = (rlogin == _POSIX_VDISABLE) ? 0 : 1;
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc) {
+ char *args[7], **argp = args;
+
+ if (argc > 2)
+ usage();
+ *argp++ = prompt;
+ if (user) {
+ *argp++ = "-l";
+ *argp++ = user;
+ }
+ *argp++ = argv[0]; /* host */
+ if (argc > 1)
+ *argp++ = argv[1]; /* port */
+ *argp = 0;
+
+ if (setjmp(toplevel) != 0)
+ Exit(EXIT_SUCCESS);
+ if (tn(argp - args, args) == 1)
+ return (EXIT_SUCCESS);
+ else
+ return (EXIT_FAILURE);
+ }
+ (void) setjmp(toplevel);
+ for (;;) {
+ command(1, 0, 0);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/telnet/network.c b/usr/src/cmd/cmd-inet/usr.bin/telnet/network.c
new file mode 100644
index 0000000000..cc5b07c929
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/telnet/network.c
@@ -0,0 +1,182 @@
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Copyright (c) 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.
+ *
+ * Copyright 1994-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)network.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+
+#include <errno.h>
+
+#include <arpa/telnet.h>
+
+#include "ring.h"
+
+#include "defines.h"
+#include "externs.h"
+
+Ring netoring;
+Ring netiring;
+static unsigned char netobuf[2*BUFSIZ];
+static unsigned char netibuf[BUFSIZ];
+
+/*
+ * Initialize internal network data structures.
+ */
+
+void
+init_network()
+{
+ if (ring_init(&netoring, netobuf, sizeof (netobuf)) != 1) {
+ exit(1);
+ }
+ if (ring_init(&netiring, netibuf, sizeof (netibuf)) != 1) {
+ exit(1);
+ }
+ NetTrace = stdout;
+}
+
+
+/*
+ * Check to see if any out-of-band data exists on a socket (for
+ * Telnet "synch" processing).
+ */
+
+int
+stilloob()
+{
+ static struct timeval timeout = { 0 };
+ fd_set excepts;
+ int value;
+
+ do {
+ FD_ZERO(&excepts);
+ FD_SET(net, &excepts);
+ value = select(net+1, NULL, NULL, &excepts, &timeout);
+ } while ((value == -1) && (errno == EINTR));
+
+ if (value < 0) {
+ perror("select");
+ (void) quit();
+ /* NOTREACHED */
+ }
+ if (FD_ISSET(net, &excepts)) {
+ return (1);
+ } else {
+ return (0);
+ }
+}
+
+
+/*
+ * setneturg()
+ *
+ * Sets "neturg" to the current location.
+ */
+
+void
+setneturg()
+{
+ ring_mark(&netoring);
+}
+
+
+/*
+ * netflush
+ * Send as much data as possible to the network,
+ * handling requests for urgent data.
+ *
+ * The return value indicates whether we did any
+ * useful work.
+ */
+
+
+int
+netflush()
+{
+ register int n, n1;
+
+ if (encrypt_output)
+ ring_encrypt(&netoring, encrypt_output);
+
+ if ((n1 = n = ring_full_consecutive(&netoring)) > 0) {
+ if (!ring_at_mark(&netoring)) {
+ /* normal write */
+ n = send(net, netoring.consume, n, 0);
+ } else {
+ /*
+ * In 4.2(and 4.3) systems, there is some question about
+ * what byte in a sendOOB operation is the "OOB" data.
+ * To make ourselves compatible, we only send ONE byte
+ * out of band, the one WE THINK should be OOB (though
+ * we really have more the TCP philosophy of urgent data
+ * rather than the Unix philosophy of OOB data).
+ */
+ n = send(net, netoring.consume, 1, MSG_OOB);
+ }
+ }
+ if (n < 0) {
+ if (errno != ENOBUFS && errno != EWOULDBLOCK) {
+ setcommandmode();
+ perror(hostname);
+ ring_clear_mark(&netoring);
+ longjmp(peerdied, -1);
+ /*NOTREACHED*/
+ }
+ n = 0;
+ }
+ if (netdata && n) {
+ Dump('>', netoring.consume, n);
+ }
+ if (n) {
+ ring_consumed(&netoring, n);
+ /*
+ * If we sent all, and more to send, then recurse to pick
+ * up the other half.
+ */
+ if ((n1 == n) && ring_full_consecutive(&netoring)) {
+ (void) netflush();
+ }
+ return (1);
+ } else {
+ return (0);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/telnet/ring.c b/usr/src/cmd/cmd-inet/usr.bin/telnet/ring.c
new file mode 100644
index 0000000000..9b0dd0bf4e
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/telnet/ring.c
@@ -0,0 +1,362 @@
+/*
+ * Copyright 2001-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * usr/src/cmd/cmd-inet/usr.bin/telnet/ring.c
+ */
+
+/*
+ * Copyright (c) 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ring.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * This defines a structure for a ring buffer.
+ *
+ * The circular buffer has two parts:
+ * (((
+ * full: [consume, supply)
+ * empty: [supply, consume)
+ * ]]]
+ *
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/sysmacros.h>
+
+#include "ring.h"
+#include "general.h"
+
+
+#define ring_subtract(d, a, b) (((a)-(b) >= 0)? \
+ (a)-(b): (((a)-(b))+(d)->size))
+
+#define ring_increment(d, a, c) (((a)+(c) < (d)->top)? \
+ (a)+(c) : (((a)+(c))-(d)->size))
+
+#define ring_decrement(d, a, c) (((a)-(c) >= (d)->bottom)? \
+ (a)-(c) : (((a)-(c))-(d)->size))
+
+
+/*
+ * The following is a clock, used to determine full, empty, etc.
+ *
+ * There is some trickiness here. Since the ring buffers are initialized
+ * to ZERO on allocation, we need to make sure, when interpreting the
+ * clock, that when the times are EQUAL, then the buffer is FULL.
+ */
+ulong_t ring_clock = 0;
+
+
+#define ring_empty(d) (((d)->consume == (d)->supply) && \
+ ((d)->consumetime >= (d)->supplytime))
+#define ring_full(d) (((d)->supply == (d)->consume) && \
+ ((d)->supplytime > (d)->consumetime))
+
+
+
+
+
+/* Buffer state transition routines */
+
+ ring_init(ring, buffer, count)
+Ring *ring;
+ unsigned char *buffer;
+ int count;
+{
+ (void) memset(ring, 0, sizeof (*ring));
+
+ ring->size = count;
+
+ ring->supply = ring->consume = ring->bottom = buffer;
+
+ ring->top = ring->bottom+ring->size;
+
+ ring->clearto = 0;
+
+ return (1);
+}
+
+/* Mark routines */
+
+/*
+ * Mark the most recently supplied byte.
+ */
+
+void
+ring_mark(ring)
+ Ring *ring;
+{
+ ring->mark = ring_decrement(ring, ring->supply, 1);
+}
+
+/*
+ * Is the ring pointing to the mark?
+ */
+
+int
+ring_at_mark(ring)
+ Ring *ring;
+{
+ if (ring->mark == ring->consume) {
+ return (1);
+ } else {
+ return (0);
+ }
+}
+
+/*
+ * Clear any mark set on the ring.
+ */
+
+void
+ring_clear_mark(ring)
+ Ring *ring;
+{
+ ring->mark = 0;
+}
+
+/*
+ * Add characters from current segment to ring buffer.
+ */
+ void
+ring_supplied(ring, count)
+ Ring *ring;
+ int count;
+{
+ ring->supply = ring_increment(ring, ring->supply, count);
+ ring->supplytime = ++ring_clock;
+}
+
+/*
+ * We have just consumed "c" bytes.
+ */
+void
+ring_consumed(ring, count)
+ Ring *ring;
+ int count;
+{
+ if (count == 0) /* don't update anything */
+ return;
+
+ if (ring->mark &&
+ (ring_subtract(ring, ring->mark, ring->consume) < count)) {
+ ring->mark = 0;
+ }
+
+ if (ring->consume < ring->clearto &&
+ ring->clearto <= ring->consume + count)
+ ring->clearto = 0;
+ else if (ring->consume + count > ring->top &&
+ ring->bottom <= ring->clearto &&
+ ring->bottom + ((ring->consume + count) - ring->top))
+ ring->clearto = 0;
+
+ ring->consume = ring_increment(ring, ring->consume, count);
+ ring->consumetime = ++ring_clock;
+ /*
+ * Try to encourage "ring_empty_consecutive()" to be large.
+ */
+ if (ring_empty(ring)) {
+ ring->consume = ring->supply = ring->bottom;
+ }
+}
+
+
+
+/* Buffer state query routines */
+
+
+/* Number of bytes that may be supplied */
+int
+ring_empty_count(ring)
+ Ring *ring;
+{
+ if (ring_empty(ring)) { /* if empty */
+ return (ring->size);
+ } else {
+ return (ring_subtract(ring, ring->consume, ring->supply));
+ }
+}
+
+/* number of CONSECUTIVE bytes that may be supplied */
+int
+ring_empty_consecutive(ring)
+ Ring *ring;
+{
+ if ((ring->consume < ring->supply) || ring_empty(ring)) {
+ /*
+ * if consume is "below" supply, or empty, then
+ * return distance to the top
+ */
+ return (ring_subtract(ring, ring->top, ring->supply));
+ } else {
+ /*
+ * else, return what we may.
+ */
+ return (ring_subtract(ring, ring->consume, ring->supply));
+ }
+}
+
+/*
+ * Return the number of bytes that are available for consuming
+ * (but don't give more than enough to get to cross over set mark)
+ */
+
+int
+ring_full_count(ring)
+ Ring *ring;
+{
+ if ((ring->mark == 0) || (ring->mark == ring->consume)) {
+ if (ring_full(ring)) {
+ return (ring->size); /* nothing consumed, but full */
+ } else {
+ return (ring_subtract(ring, ring->supply,
+ ring->consume));
+ }
+ } else {
+ return (ring_subtract(ring, ring->mark, ring->consume));
+ }
+}
+
+/*
+ * Return the number of CONSECUTIVE bytes available for consuming.
+ * However, don't return more than enough to cross over set mark.
+ */
+int
+ring_full_consecutive(ring)
+ Ring *ring;
+{
+ if ((ring->mark == 0) || (ring->mark == ring->consume)) {
+ if ((ring->supply < ring->consume) || ring_full(ring)) {
+ return (ring_subtract(ring, ring->top, ring->consume));
+ } else {
+ return (ring_subtract(ring, ring->supply,
+ ring->consume));
+ }
+ } else {
+ if (ring->mark < ring->consume) {
+ return (ring_subtract(ring, ring->top, ring->consume));
+ } else { /* Else, distance to mark */
+ return (ring_subtract(ring, ring->mark, ring->consume));
+ }
+ }
+}
+
+/*
+ * Move data into the "supply" portion of of the ring buffer.
+ */
+void
+ring_supply_data(ring, buffer, count)
+ Ring *ring;
+ unsigned char *buffer;
+ int count;
+{
+ int i;
+
+ while (count) {
+ i = MIN(count, ring_empty_consecutive(ring));
+ (void) memcpy(ring->supply, buffer, i);
+ ring_supplied(ring, i);
+ count -= i;
+ buffer += i;
+ }
+}
+
+#ifdef notdef
+
+/*
+ * Move data from the "consume" portion of the ring buffer
+ */
+void
+ring_consume_data(ring, buffer, count)
+ Ring *ring;
+ unsigned char *buffer;
+ int count;
+{
+ int i;
+
+ while (count) {
+ i = MIN(count, ring_full_consecutive(ring));
+ memcpy(buffer, ring->consume, i);
+ ring_consumed(ring, i);
+ count -= i;
+ buffer += i;
+ }
+}
+#endif
+
+void
+ring_encrypt(ring, encryptor)
+ Ring *ring;
+ void (*encryptor)();
+{
+ unsigned char *s, *c;
+
+ if (ring_empty(ring) || ring->clearto == ring->supply)
+ return;
+
+ if ((c = ring->clearto) == NULL)
+ c = ring->consume;
+
+ s = ring->supply;
+
+ if (s <= c) {
+ (*encryptor)(c, ring->top - c);
+ (*encryptor)(ring->bottom, s - ring->bottom);
+ } else
+ (*encryptor)(c, s - c);
+
+ ring->clearto = ring->supply;
+}
+
+ void
+ring_clearto(ring)
+ Ring *ring;
+{
+ if (!ring_empty(ring))
+ ring->clearto = ring->supply;
+ else
+ ring->clearto = 0;
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/telnet/ring.h b/usr/src/cmd/cmd-inet/usr.bin/telnet/ring.h
new file mode 100644
index 0000000000..4eac90208a
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/telnet/ring.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2001-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 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.
+ *
+ * @(#)ring.h 8.1 (Berkeley) 6/6/93
+ */
+
+#ifndef _RING_H
+#define _RING_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * This defines a structure for a ring buffer.
+ *
+ * The circular buffer has two parts:
+ * (((
+ * full: [consume, supply)
+ * empty: [supply, consume)
+ * ]]]
+ *
+ */
+typedef struct {
+ unsigned char *consume; /* where data comes out of */
+ unsigned char *supply; /* where data comes in to */
+ unsigned char *bottom; /* lowest address in buffer */
+ unsigned char *top; /* highest address+1 in buffer */
+ unsigned char *mark; /* marker (user defined) */
+ unsigned char *clearto; /* Data to this point is clear text */
+ unsigned char *encryyptedto; /* Data is encrypted to here */
+ int size; /* size in bytes of buffer */
+ ulong_t consumetime; /* help us keep straight full, empty, etc. */
+ ulong_t supplytime;
+} Ring;
+
+/* Here are some functions and macros to deal with the ring buffer */
+
+/* Initialization routine */
+extern int ring_init(Ring *ring, unsigned char *buffer, int count);
+
+/* Data movement routines */
+extern void ring_supply_data(Ring *ring, unsigned char *buffer, int count);
+#ifdef notdef
+extern void ring_consume_data(Ring *ring, unsigned char *buffer, int count);
+#endif
+
+/* Buffer state transition routines */
+extern void ring_supplied(Ring *ring, int count);
+extern void ring_consumed(Ring *ring, int count);
+
+/* Buffer state query routines */
+extern int ring_at_mark(Ring *ring);
+extern int ring_empty_count(Ring *ring);
+extern int ring_empty_consecutive(Ring *ring);
+extern int ring_full_count(Ring *ring);
+extern int ring_full_consecutive(Ring *ring);
+
+extern void ring_encrypt(Ring *ring, void (*func)());
+extern void ring_clearto(Ring *ring);
+
+extern void ring_clear_mark();
+extern void ring_mark();
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RING_H */
diff --git a/usr/src/cmd/cmd-inet/usr.bin/telnet/sys_bsd.c b/usr/src/cmd/cmd-inet/usr.bin/telnet/sys_bsd.c
new file mode 100644
index 0000000000..541b7c5d07
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/telnet/sys_bsd.c
@@ -0,0 +1,886 @@
+/*
+ * Copyright 1994-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Copyright (c) 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.
+ *
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)sys_bsd.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * The following routines try to encapsulate what is system dependent
+ * (at least between 4.x and dos) which is used in telnet.c.
+ */
+
+
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <signal.h>
+#include <errno.h>
+#include <arpa/telnet.h>
+
+#include "ring.h"
+
+#include "defines.h"
+#include "externs.h"
+#include "types.h"
+
+#define SIG_FUNC_RET void
+
+int tout; /* Output file descriptor */
+static int tin; /* Input file descriptor */
+int net = -1;
+
+
+#ifndef USE_TERMIO
+struct tchars otc = { 0 }, ntc = { 0 };
+struct ltchars oltc = { 0 }, nltc = { 0 };
+struct sgttyb ottyb = { 0 }, nttyb = { 0 };
+int olmode = 0;
+#define cfgetispeed(ptr) (ptr)->sg_ispeed
+#define cfgetospeed(ptr) (ptr)->sg_ospeed
+#define old_tc ottyb
+
+#else /* USE_TERMIO */
+static struct termio old_tc = { 0 };
+extern struct termio new_tc;
+#endif /* USE_TERMIO */
+
+static fd_set ibits, obits, xbits;
+
+static SIG_FUNC_RET susp(int);
+void fatal_tty_error(char *doing_what);
+
+
+void
+init_sys()
+{
+ tout = fileno(stdout);
+ tin = fileno(stdin);
+ FD_ZERO(&ibits);
+ FD_ZERO(&obits);
+ FD_ZERO(&xbits);
+
+ errno = 0;
+}
+
+
+int
+TerminalWrite(buf, n)
+ char *buf;
+ int n;
+{
+ return (write(tout, buf, n));
+}
+
+static int
+TerminalRead(buf, n)
+ char *buf;
+ int n;
+{
+ return (read(tin, buf, n));
+}
+
+#ifdef KLUDGELINEMODE
+extern int kludgelinemode;
+#endif
+/*
+ * TerminalSpecialChars()
+ *
+ * Look at an input character to see if it is a special character
+ * and decide what to do.
+ *
+ * Output:
+ *
+ * 0 Don't add this character.
+ * 1 Do add this character
+ */
+int
+TerminalSpecialChars(c)
+ int c;
+{
+ /*
+ * Don't check for signal characters here. If MODE_TRAPSIG is on,
+ * then the various signal handlers will catch the characters. If
+ * the character in question gets here, then it must have been LNEXTed
+ */
+ if (c == termQuitChar) {
+#ifdef KLUDGELINEMODE
+ if (kludgelinemode) {
+ if (sendbrk() == -1) {
+ /* This won't return. */
+ fatal_tty_error("write");
+ }
+ return (0);
+ }
+#endif
+ } else if (c == termFlushChar) {
+ /* Transmit Abort Output */
+ if (xmitAO() == -1) {
+ /* This won't return. */
+ fatal_tty_error("write");
+ }
+ return (0);
+ } else if (!MODE_LOCAL_CHARS(globalmode)) {
+ if (c == termKillChar) {
+ xmitEL();
+ return (0);
+ } else if (c == termEraseChar) {
+ xmitEC(); /* Transmit Erase Character */
+ return (0);
+ }
+ }
+ return (1);
+}
+
+
+/*
+ * Flush output to the terminal
+ */
+
+void
+TerminalFlushOutput()
+{
+ if (isatty(fileno(stdout))) {
+ (void) ioctl(fileno(stdout), TIOCFLUSH, NULL);
+ }
+}
+
+void
+TerminalSaveState()
+{
+#ifndef USE_TERMIO
+ (void) ioctl(0, TIOCGETP, &ottyb);
+ (void) ioctl(0, TIOCGETC, &otc);
+ (void) ioctl(0, TIOCGLTC, &oltc);
+ (void) ioctl(0, TIOCLGET, &olmode);
+
+ ntc = otc;
+ nltc = oltc;
+ nttyb = ottyb;
+
+#else /* USE_TERMIO */
+ (void) tcgetattr(0, &old_tc);
+
+ new_tc = old_tc;
+ termAytChar = CONTROL('T');
+#endif /* USE_TERMIO */
+}
+
+cc_t *
+tcval(func)
+ register int func;
+{
+ switch (func) {
+ case SLC_IP: return (&termIntChar);
+ case SLC_ABORT: return (&termQuitChar);
+ case SLC_EOF: return (&termEofChar);
+ case SLC_EC: return (&termEraseChar);
+ case SLC_EL: return (&termKillChar);
+ case SLC_XON: return (&termStartChar);
+ case SLC_XOFF: return (&termStopChar);
+ case SLC_FORW1: return (&termForw1Char);
+#ifdef USE_TERMIO
+ case SLC_FORW2: return (&termForw2Char);
+ case SLC_AO: return (&termFlushChar);
+ case SLC_SUSP: return (&termSuspChar);
+ case SLC_EW: return (&termWerasChar);
+ case SLC_RP: return (&termRprntChar);
+ case SLC_LNEXT: return (&termLiteralNextChar);
+#endif
+
+ case SLC_SYNCH:
+ case SLC_BRK:
+ case SLC_EOR:
+ default:
+ return ((cc_t *)0);
+ }
+}
+
+void
+TerminalDefaultChars()
+{
+#ifndef USE_TERMIO
+ ntc = otc;
+ nltc = oltc;
+ nttyb.sg_kill = ottyb.sg_kill;
+ nttyb.sg_erase = ottyb.sg_erase;
+#else /* USE_TERMIO */
+ (void) memcpy(new_tc.c_cc, old_tc.c_cc, sizeof (old_tc.c_cc));
+ termAytChar = CONTROL('T');
+#endif /* USE_TERMIO */
+}
+
+/*
+ * TerminalNewMode - set up terminal to a specific mode.
+ * MODE_ECHO: do local terminal echo
+ * MODE_FLOW: do local flow control
+ * MODE_TRAPSIG: do local mapping to TELNET IAC sequences
+ * MODE_EDIT: do local line editing
+ *
+ * Command mode:
+ * MODE_ECHO|MODE_EDIT|MODE_FLOW|MODE_TRAPSIG
+ * local echo
+ * local editing
+ * local xon/xoff
+ * local signal mapping
+ *
+ * Linemode:
+ * local/no editing
+ * Both Linemode and Single Character mode:
+ * local/remote echo
+ * local/no xon/xoff
+ * local/no signal mapping
+ */
+
+
+void
+TerminalNewMode(f)
+ register int f;
+{
+ static int prevmode = -2; /* guaranteed unique */
+#ifndef USE_TERMIO
+ struct tchars tc;
+ struct ltchars ltc;
+ struct sgttyb sb;
+ int lmode;
+#else /* USE_TERMIO */
+ struct termio tmp_tc;
+#endif /* USE_TERMIO */
+ int onoff;
+ int old;
+ cc_t esc;
+ sigset_t nset;
+
+ globalmode = f&~MODE_FORCE;
+ if (prevmode == f)
+ return;
+
+ /*
+ * Write any outstanding data before switching modes
+ * ttyflush() returns 0 only when there was no data
+ * to write out; it returns -1 if it couldn't do
+ * anything at all, returns -2 if there was a write
+ * error (other than EWOULDBLOCK), and otherwise it
+ * returns 1 + the number of characters left to write.
+ */
+#ifndef USE_TERMIO
+ /*
+ * We would really like ask the kernel to wait for the output
+ * to drain, like we can do with the TCSADRAIN, but we don't have
+ * that option. The only ioctl that waits for the output to
+ * drain, TIOCSETP, also flushes the input queue, which is NOT
+ * what we want(TIOCSETP is like TCSADFLUSH).
+ */
+#endif
+ old = ttyflush(SYNCHing|flushout);
+ if (old == -1 || old > 1) {
+#ifdef USE_TERMIO
+ (void) tcgetattr(tin, &tmp_tc);
+#endif /* USE_TERMIO */
+ do {
+ /*
+ * Wait for data to drain, then flush again.
+ */
+#ifdef USE_TERMIO
+ (void) tcsetattr(tin, TCSADRAIN, &tmp_tc);
+#endif /* USE_TERMIO */
+ old = ttyflush(SYNCHing|flushout);
+ } while (old == -1 || old > 1);
+ }
+
+ old = prevmode;
+ prevmode = f&~MODE_FORCE;
+#ifndef USE_TERMIO
+ sb = nttyb;
+ tc = ntc;
+ ltc = nltc;
+ lmode = olmode;
+#else
+ tmp_tc = new_tc;
+#endif
+
+ if (f&MODE_ECHO) {
+#ifndef USE_TERMIO
+ sb.sg_flags |= ECHO;
+#else
+ tmp_tc.c_lflag |= ECHO;
+ tmp_tc.c_oflag |= ONLCR;
+ if (crlf)
+ tmp_tc.c_iflag |= ICRNL;
+#endif
+ } else {
+#ifndef USE_TERMIO
+ sb.sg_flags &= ~ECHO;
+#else
+ tmp_tc.c_lflag &= ~ECHO;
+ tmp_tc.c_oflag &= ~ONLCR;
+#ifdef notdef
+ if (crlf)
+ tmp_tc.c_iflag &= ~ICRNL;
+#endif
+#endif
+ }
+
+ if ((f&MODE_FLOW) == 0) {
+#ifndef USE_TERMIO
+ tc.t_startc = _POSIX_VDISABLE;
+ tc.t_stopc = _POSIX_VDISABLE;
+#else
+ tmp_tc.c_iflag &= ~(IXOFF|IXON); /* Leave the IXANY bit alone */
+ } else {
+ if (restartany < 0) {
+ /* Leave the IXANY bit alone */
+ tmp_tc.c_iflag |= IXOFF|IXON;
+ } else if (restartany > 0) {
+ tmp_tc.c_iflag |= IXOFF|IXON|IXANY;
+ } else {
+ tmp_tc.c_iflag |= IXOFF|IXON;
+ tmp_tc.c_iflag &= ~IXANY;
+ }
+#endif
+ }
+
+ if ((f&MODE_TRAPSIG) == 0) {
+#ifndef USE_TERMIO
+ tc.t_intrc = _POSIX_VDISABLE;
+ tc.t_quitc = _POSIX_VDISABLE;
+ tc.t_eofc = _POSIX_VDISABLE;
+ ltc.t_suspc = _POSIX_VDISABLE;
+ ltc.t_dsuspc = _POSIX_VDISABLE;
+#else
+ tmp_tc.c_lflag &= ~ISIG;
+#endif
+ localchars = 0;
+ } else {
+#ifdef USE_TERMIO
+ tmp_tc.c_lflag |= ISIG;
+#endif
+ localchars = 1;
+ }
+
+ if (f&MODE_EDIT) {
+#ifndef USE_TERMIO
+ sb.sg_flags &= ~CBREAK;
+ sb.sg_flags |= CRMOD;
+#else
+ tmp_tc.c_lflag |= ICANON;
+#endif
+ } else {
+#ifndef USE_TERMIO
+ sb.sg_flags |= CBREAK;
+ if (f&MODE_ECHO)
+ sb.sg_flags |= CRMOD;
+ else
+ sb.sg_flags &= ~CRMOD;
+#else
+ tmp_tc.c_lflag &= ~ICANON;
+ tmp_tc.c_iflag &= ~ICRNL;
+ tmp_tc.c_cc[VMIN] = 1;
+ tmp_tc.c_cc[VTIME] = 0;
+#endif
+ }
+
+ if ((f&(MODE_EDIT|MODE_TRAPSIG)) == 0) {
+#ifndef USE_TERMIO
+ ltc.t_lnextc = _POSIX_VDISABLE;
+#else
+ tmp_tc.c_cc[VLNEXT] = (cc_t)(_POSIX_VDISABLE);
+#endif
+ }
+
+ if (f&MODE_SOFT_TAB) {
+#ifndef USE_TERMIO
+ sb.sg_flags |= XTABS;
+#else
+ tmp_tc.c_oflag &= ~TABDLY;
+ tmp_tc.c_oflag |= TAB3;
+#endif
+ } else {
+#ifndef USE_TERMIO
+ sb.sg_flags &= ~XTABS;
+#else
+ tmp_tc.c_oflag &= ~TABDLY;
+#endif
+ }
+
+ if (f&MODE_LIT_ECHO) {
+#ifndef USE_TERMIO
+ lmode &= ~LCTLECH;
+#else
+ tmp_tc.c_lflag &= ~ECHOCTL;
+#endif
+ } else {
+#ifndef USE_TERMIO
+ lmode |= LCTLECH;
+#else
+ tmp_tc.c_lflag |= ECHOCTL;
+#endif
+ }
+
+ if (f == -1) {
+ onoff = 0;
+ } else {
+#ifndef USE_TERMIO
+ if (f & MODE_OUTBIN)
+ lmode |= LLITOUT;
+ else
+ lmode &= ~LLITOUT;
+#else
+ if (f & MODE_OUTBIN) {
+ tmp_tc.c_cflag &= ~(CSIZE|PARENB);
+ tmp_tc.c_cflag |= CS8;
+ tmp_tc.c_oflag &= ~OPOST;
+ } else {
+ tmp_tc.c_cflag &= ~(CSIZE|PARENB);
+ tmp_tc.c_cflag |= old_tc.c_cflag & (CSIZE|PARENB);
+ tmp_tc.c_oflag |= OPOST;
+ }
+#endif
+ onoff = 1;
+ }
+
+ if (f != -1) {
+
+ (void) signal(SIGTSTP, susp);
+
+#if defined(USE_TERMIO) && defined(NOKERNINFO)
+ tmp_tc.c_lflag |= NOKERNINFO;
+#endif
+ /*
+ * We don't want to process ^Y here. It's just another
+ * character that we'll pass on to the back end. It has
+ * to process it because it will be processed when the
+ * user attempts to read it, not when we send it.
+ */
+#ifndef USE_TERMIO
+ ltc.t_dsuspc = _POSIX_VDISABLE;
+#else
+ tmp_tc.c_cc[VDSUSP] = (cc_t)(_POSIX_VDISABLE);
+#endif
+#ifdef USE_TERMIO
+ /*
+ * If the VEOL character is already set, then use VEOL2,
+ * otherwise use VEOL.
+ */
+ esc = (rlogin != _POSIX_VDISABLE) ? rlogin : escape;
+ if ((tmp_tc.c_cc[VEOL] != esc)
+ /* XXX */ &&
+ (tmp_tc.c_cc[VEOL2] != esc)
+ /* XXX */) {
+ if (tmp_tc.c_cc[VEOL] == (cc_t)(_POSIX_VDISABLE))
+ tmp_tc.c_cc[VEOL] = esc;
+ else if (tmp_tc.c_cc[VEOL2] == (cc_t)(_POSIX_VDISABLE))
+ tmp_tc.c_cc[VEOL2] = esc;
+ }
+#else
+ if (tc.t_brkc == (cc_t)(_POSIX_VDISABLE))
+ tc.t_brkc = esc;
+#endif
+ } else {
+ (void) signal(SIGTSTP, SIG_DFL);
+ (void) sigemptyset(&nset);
+ (void) sigaddset(&nset, SIGTSTP);
+ (void) sigprocmask(SIG_UNBLOCK, &nset, 0);
+#ifndef USE_TERMIO
+ ltc = oltc;
+ tc = otc;
+ sb = ottyb;
+ lmode = olmode;
+#else
+ tmp_tc = old_tc;
+#endif
+ }
+ if (isatty(tin)) {
+#ifndef USE_TERMIO
+ (void) ioctl(tin, TIOCLSET, &lmode);
+ (void) ioctl(tin, TIOCSLTC, &ltc);
+ (void) ioctl(tin, TIOCSETC, &tc);
+ (void) ioctl(tin, TIOCSETN, &sb);
+#else
+ if (tcsetattr(tin, TCSADRAIN, &tmp_tc) < 0)
+ (void) tcsetattr(tin, TCSANOW, &tmp_tc);
+#endif
+ (void) ioctl(tin, FIONBIO, &onoff);
+ (void) ioctl(tout, FIONBIO, &onoff);
+ }
+
+}
+
+/*
+ * This code assumes that the values B0, B50, B75...
+ * are in ascending order. They do not have to be
+ * contiguous.
+ */
+static struct termspeeds {
+ int speed;
+ int value;
+} termspeeds[] = {
+ { 0, B0 }, { 50, B50 }, { 75, B75 },
+ { 110, B110 }, { 134, B134 }, { 150, B150 },
+ { 200, B200 }, { 300, B300 }, { 600, B600 },
+ { 1200, B1200 }, { 1800, B1800 }, { 2400, B2400 },
+ { 4800, B4800 }, { 9600, B9600 }, { 19200, B19200 },
+ { 38400, B38400 }, { 57600, B57600 }, { 76800, B76800 },
+ { 115200, B115200 }, { 153600, B153600 }, { 230400, B230400 },
+ { 307200, B307200 }, { 460800, B460800 }, { -1, B0 }
+};
+
+void
+TerminalSpeeds(ispeed, ospeed)
+ int *ispeed;
+ int *ospeed;
+{
+ register struct termspeeds *tp;
+ register int in, out;
+
+ out = cfgetospeed(&old_tc);
+ in = cfgetispeed(&old_tc);
+ if (in == 0)
+ in = out;
+
+ tp = termspeeds;
+ while ((tp->speed != -1) && (tp->value < in)) {
+ tp++;
+ }
+ if (tp->speed == -1)
+ tp--; /* back up to fastest defined speed */
+ *ispeed = tp->speed;
+
+ tp = termspeeds;
+ while ((tp->speed != -1) && (tp->value < out)) {
+ tp++;
+ }
+ if (tp->speed == -1)
+ tp--;
+ *ospeed = tp->speed;
+}
+
+int
+TerminalWindowSize(rows, cols)
+ unsigned short *rows, *cols;
+{
+ struct winsize ws;
+
+ if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) >= 0) {
+ *rows = ws.ws_row;
+ *cols = ws.ws_col;
+ return (1);
+ }
+ return (0);
+}
+
+static void
+NetNonblockingIO(fd, onoff)
+ int fd;
+ int onoff;
+{
+ (void) ioctl(fd, FIONBIO, &onoff);
+}
+
+/*
+ * Various signal handling routines.
+ */
+
+/* ARGSUSED */
+static SIG_FUNC_RET
+deadpeer(sig)
+ int sig;
+{
+ /*
+ * Once is all we should catch SIGPIPE. If we get it again,
+ * it means we tried to put still more data out to a pipe
+ * which has disappeared. In that case, telnet will exit.
+ */
+ (void) signal(SIGPIPE, SIG_IGN);
+ flushout = 1;
+ setcommandmode();
+ longjmp(peerdied, -1);
+}
+
+boolean_t intr_happened = B_FALSE;
+boolean_t intr_waiting = B_FALSE;
+
+/* ARGSUSED */
+static SIG_FUNC_RET
+intr(sig)
+ int sig;
+{
+ if (intr_waiting) {
+ intr_happened = 1;
+ return;
+ }
+ (void) signal(SIGINT, intr);
+ if (localchars) {
+ intp();
+ return;
+ }
+ setcommandmode();
+ longjmp(toplevel, -1);
+}
+
+/* ARGSUSED */
+static SIG_FUNC_RET
+intr2(sig)
+ int sig;
+{
+ (void) signal(SIGQUIT, intr2);
+ if (localchars) {
+ /*
+ * Ignore return to the next two function calls
+ * since we're doing SIGQUIT
+ */
+#ifdef KLUDGELINEMODE
+ if (kludgelinemode) {
+ (void) sendbrk();
+ }
+ else
+#endif
+ sendabort();
+ return;
+ }
+}
+
+/* ARGSUSED */
+static SIG_FUNC_RET
+susp(sig)
+ int sig;
+{
+ (void) signal(SIGTSTP, susp);
+ if ((rlogin != _POSIX_VDISABLE) && rlogin_susp())
+ return;
+ if (localchars)
+ sendsusp();
+}
+
+/* ARGSUSED */
+static SIG_FUNC_RET
+sendwin(sig)
+ int sig;
+{
+ (void) signal(SIGWINCH, sendwin);
+ if (connected) {
+ sendnaws();
+ }
+}
+
+void
+sys_telnet_init()
+{
+ (void) signal(SIGINT, intr);
+ (void) signal(SIGQUIT, intr2);
+ (void) signal(SIGPIPE, deadpeer);
+ (void) signal(SIGWINCH, sendwin);
+ (void) signal(SIGTSTP, susp);
+
+ setconnmode(0);
+
+ NetNonblockingIO(net, 1);
+
+ if (SetSockOpt(net, SOL_SOCKET, SO_OOBINLINE, 1) == -1) {
+ perror("SetSockOpt");
+ }
+}
+
+
+/*
+ * fatal_tty_error -
+ * Handle case where there is an unrecoverable error on the tty
+ * connections. Print an error, reset the terminal settings
+ * and get out as painlessly as possible.
+ */
+void
+fatal_tty_error(char *doing_what)
+{
+ TerminalNewMode(-1);
+ (void) fprintf(stderr, "Error processing %s: %s\n", doing_what,
+ strerror(errno));
+ exit(1);
+}
+
+
+/*
+ * Process rings -
+ *
+ * This routine tries to fill up/empty our various rings.
+ *
+ * The parameter specifies whether this is a poll operation,
+ * or a block-until-something-happens operation.
+ *
+ * The return value is 1 if something happened, 0 if not.
+ */
+
+int
+process_rings(netin, netout, netex, ttyin, ttyout, poll)
+ int poll; /* If 0, then block until something to do */
+{
+ register int c;
+ /*
+ * One wants to be a bit careful about setting returnValue
+ * to one, since a one implies we did some useful work,
+ * and therefore probably won't be called to block next
+ * time (TN3270 mode only).
+ */
+ int returnValue = 0;
+ static struct timeval TimeValue = { 0 };
+ int i;
+
+ if (netout) {
+ FD_SET(net, &obits);
+ }
+ if (ttyout) {
+ FD_SET(tout, &obits);
+ }
+ if (ttyin) {
+ FD_SET(tin, &ibits);
+ }
+ if (netin) {
+ FD_SET(net, &ibits);
+ }
+ if (netex) {
+ FD_SET(net, &xbits);
+ }
+ if ((c = select(16, &ibits, &obits, &xbits,
+ (poll == 0) ? NULL : &TimeValue)) < 0) {
+ if (c == -1) {
+ /*
+ * we can get EINTR if we are in line mode,
+ * and the user does an escape (TSTP), or
+ * some other signal generator.
+ */
+ if (errno == EINTR) {
+ return (0);
+ }
+ /* I don't like this, does it ever happen? */
+ (void) printf("sleep(5) from telnet, after select\r\n");
+ (void) sleep(5);
+ }
+ return (0);
+ }
+
+ /*
+ * Any urgent data?
+ */
+ if (FD_ISSET(net, &xbits)) {
+ FD_CLR(net, &xbits);
+ SYNCHing = 1;
+
+ /* flush any data that is already enqueued */
+ i = ttyflush(1);
+ if (i == -2) {
+ /* This will not return. */
+ fatal_tty_error("write");
+ }
+ }
+
+ /*
+ * Something to read from the network...
+ */
+ if (FD_ISSET(net, &ibits)) {
+ int canread;
+
+ FD_CLR(net, &ibits);
+ canread = ring_empty_consecutive(&netiring);
+ c = recv(net, netiring.supply, canread, 0);
+ if (c < 0 && errno == EWOULDBLOCK) {
+ c = 0;
+ } else if (c <= 0) {
+ return (-1);
+ }
+ if (netdata) {
+ Dump('<', netiring.supply, c);
+ }
+ if (c)
+ ring_supplied(&netiring, c);
+ returnValue = 1;
+ }
+
+ /*
+ * Something to read from the tty...
+ */
+ if (FD_ISSET(tin, &ibits)) {
+ FD_CLR(tin, &ibits);
+ c = TerminalRead((char *)ttyiring.supply,
+ ring_empty_consecutive(&ttyiring));
+ if (c < 0) {
+ if (errno != EWOULDBLOCK) {
+ /* This will not return. */
+ fatal_tty_error("read");
+ }
+ c = 0;
+ } else {
+ /* EOF detection for line mode!!!! */
+ if ((c == 0) && MODE_LOCAL_CHARS(globalmode) &&
+ isatty(tin)) {
+ /* must be an EOF... */
+ eof_pending = 1;
+ return (1);
+ }
+ if (c <= 0) {
+ returnValue = -1;
+ goto next;
+ }
+ if (termdata) {
+ Dump('<', ttyiring.supply, c);
+ }
+ ring_supplied(&ttyiring, c);
+ }
+ returnValue = 1; /* did something useful */
+ }
+
+next:
+ if (FD_ISSET(net, &obits)) {
+ FD_CLR(net, &obits);
+ returnValue |= netflush();
+ }
+ if (FD_ISSET(tout, &obits)) {
+ FD_CLR(tout, &obits);
+ i = ttyflush(SYNCHing|flushout);
+ if (i == -2) {
+ /* This will not return. */
+ fatal_tty_error("write");
+ }
+ returnValue |= (i > 0);
+ }
+
+ return (returnValue);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/telnet/telnet.c b/usr/src/cmd/cmd-inet/usr.bin/telnet/telnet.c
new file mode 100644
index 0000000000..9c0cac01c7
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/telnet/telnet.c
@@ -0,0 +1,2651 @@
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Copyright (c) 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)telnet.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <netdb.h>
+#include <sys/types.h>
+
+#include <curses.h>
+#include <signal.h>
+/*
+ * By the way, we need to include curses.h before telnet.h since,
+ * among other things, telnet.h #defines 'DO', which is a variable
+ * declared in curses.h.
+ */
+
+#include <arpa/telnet.h>
+
+#include <ctype.h>
+
+#include "ring.h"
+
+#include "defines.h"
+#include "externs.h"
+#include "types.h"
+#include "general.h"
+
+#include "auth.h"
+#include "encrypt.h"
+
+#define strip(x) ((x)&0x7f)
+
+/* Buffer for sub-options */
+static unsigned char subbuffer[SUBBUFSIZE];
+static unsigned char *subpointer;
+static unsigned char *subend;
+
+#define SB_CLEAR() subpointer = subbuffer;
+#define SB_TERM() { subend = subpointer; SB_CLEAR(); }
+#define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof (subbuffer))) { \
+ *subpointer++ = (c); \
+ }
+
+#define SB_GET() ((*subpointer++)&0xff)
+#define SB_PEEK() ((*subpointer)&0xff)
+#define SB_EOF() (subpointer >= subend)
+#define SB_LEN() (subend - subpointer)
+
+char options[SUBBUFSIZE]; /* The combined options */
+char do_dont_resp[SUBBUFSIZE];
+char will_wont_resp[SUBBUFSIZE];
+
+int eight = 0;
+int autologin = 0; /* Autologin anyone? */
+int skiprc = 0;
+int connected;
+int showoptions;
+static int ISend; /* trying to send network data in */
+int debug = 0;
+int crmod;
+int netdata; /* Print out network data flow */
+int crlf; /* Should '\r' be mapped to <CR><LF> (or <CR><NUL>)? */
+int telnetport;
+int SYNCHing; /* we are in TELNET SYNCH mode */
+int flushout; /* flush output */
+int autoflush = 0; /* flush output when interrupting? */
+int autosynch; /* send interrupt characters with SYNCH? */
+int localflow; /* we handle flow control locally */
+int restartany; /* if flow control enabled, restart on any character */
+int localchars; /* we recognize interrupt/quit */
+int donelclchars; /* the user has set "localchars" */
+int donebinarytoggle; /* the user has put us in binary */
+int dontlecho; /* do we suppress local echoing right now? */
+int eof_pending = 0; /* we received a genuine EOF on input, send IAC-EOF */
+int globalmode;
+
+/* spin while waiting for authentication */
+boolean_t scheduler_lockout_tty = B_FALSE;
+int encrypt_flag = 0;
+int forwardable_flag = 0;
+int forward_flag = 0;
+boolean_t wantencryption = B_FALSE;
+
+char *prompt = 0;
+
+cc_t escape;
+cc_t rlogin;
+boolean_t escape_valid = B_TRUE;
+#ifdef KLUDGELINEMODE
+cc_t echoc;
+#endif
+
+/*
+ * Telnet receiver states for fsm
+ */
+#define TS_DATA 0
+#define TS_IAC 1
+#define TS_WILL 2
+#define TS_WONT 3
+#define TS_DO 4
+#define TS_DONT 5
+#define TS_CR 6
+#define TS_SB 7 /* sub-option collection */
+#define TS_SE 8 /* looking for sub-option end */
+
+static int telrcv_state;
+#ifdef OLD_ENVIRON
+static unsigned char telopt_environ = TELOPT_NEW_ENVIRON;
+#else
+#define telopt_environ TELOPT_NEW_ENVIRON
+#endif
+
+jmp_buf toplevel = { 0 };
+jmp_buf peerdied;
+
+static int flushline;
+int linemode;
+
+int reqd_linemode = 0; /* Set if either new or old line mode in */
+ /* effect since before initial negotiations */
+
+#ifdef KLUDGELINEMODE
+int kludgelinemode = 1;
+#endif
+
+/*
+ * The following are some clocks used to decide how to interpret
+ * the relationship between various variables.
+ */
+
+Clocks clocks;
+
+#ifdef notdef
+Modelist modelist[] = {
+ { "telnet command mode", COMMAND_LINE },
+ { "character-at-a-time mode", 0 },
+ { "character-at-a-time mode (local echo)", LOCAL_ECHO|LOCAL_CHARS },
+ { "line-by-line mode (remote echo)", LINE | LOCAL_CHARS },
+ { "line-by-line mode", LINE | LOCAL_ECHO | LOCAL_CHARS },
+ { "line-by-line mode (local echoing suppressed)", LINE | LOCAL_CHARS },
+ { "3270 mode", 0 },
+};
+#endif
+
+static void willoption(int);
+static void wontoption(int);
+static void lm_will(unsigned char *, int);
+static void lm_wont(unsigned char *, int);
+static void lm_do(unsigned char *, int);
+static void lm_dont(unsigned char *, int);
+static void slc_init(void);
+static void slc_import(int);
+static void slc_export(void);
+static void slc_start_reply(size_t);
+static void slc_add_reply(unsigned char, unsigned char, cc_t);
+static void slc_end_reply(void);
+static void slc(unsigned char *, int);
+static int slc_update(void);
+static void env_opt(unsigned char *, int);
+static void env_opt_start(void);
+static void sendeof(void);
+static int is_unique(register char *, register char **, register char **);
+
+/*
+ * Initialize telnet environment.
+ */
+
+int
+init_telnet()
+{
+ if (env_init() == 0)
+ return (0);
+
+ SB_CLEAR();
+ ClearArray(options);
+
+ connected = ISend = localflow = donebinarytoggle = 0;
+ restartany = -1;
+
+ SYNCHing = 0;
+
+ /* Don't change NetTrace */
+
+ escape = CONTROL(']');
+ rlogin = _POSIX_VDISABLE;
+#ifdef KLUDGELINEMODE
+ echoc = CONTROL('E');
+#endif
+
+ flushline = 1;
+ telrcv_state = TS_DATA;
+
+ return (1);
+}
+
+
+#ifdef notdef
+#include <varargs.h>
+
+/*VARARGS*/
+static void
+printring(va_alist)
+ va_dcl
+{
+ va_list ap;
+ char buffer[100]; /* where things go */
+ char *ptr;
+ char *format;
+ char *string;
+ Ring *ring;
+ int i;
+
+ va_start(ap);
+
+ ring = va_arg(ap, Ring *);
+ format = va_arg(ap, char *);
+ ptr = buffer;
+
+ while ((i = *format++) != 0) {
+ if (i == '%') {
+ i = *format++;
+ switch (i) {
+ case 'c':
+ *ptr++ = va_arg(ap, int);
+ break;
+ case 's':
+ string = va_arg(ap, char *);
+ ring_supply_data(ring, buffer, ptr-buffer);
+ ring_supply_data(ring, string, strlen(string));
+ ptr = buffer;
+ break;
+ case 0:
+ ExitString("printring: trailing %%.\n",
+ EXIT_FAILURE);
+ /*NOTREACHED*/
+ default:
+ ExitString("printring: unknown format "
+ "character.\n", EXIT_FAILURE);
+ /*NOTREACHED*/
+ }
+ } else {
+ *ptr++ = i;
+ }
+ }
+ ring_supply_data(ring, buffer, ptr-buffer);
+}
+#endif
+
+/*
+ * These routines are in charge of sending option negotiations
+ * to the other side.
+ *
+ * The basic idea is that we send the negotiation if either side
+ * is in disagreement as to what the current state should be.
+ */
+
+void
+send_do(c, init)
+ register int c, init;
+{
+ if (init) {
+ if (((do_dont_resp[c] == 0) && my_state_is_do(c)) ||
+ my_want_state_is_do(c))
+ return;
+ set_my_want_state_do(c);
+ do_dont_resp[c]++;
+ }
+ NET2ADD(IAC, DO);
+ NETADD(c);
+ printoption("SENT", DO, c);
+}
+
+void
+send_dont(c, init)
+ register int c, init;
+{
+ if (init) {
+ if (((do_dont_resp[c] == 0) && my_state_is_dont(c)) ||
+ my_want_state_is_dont(c))
+ return;
+ set_my_want_state_dont(c);
+ do_dont_resp[c]++;
+ }
+ NET2ADD(IAC, DONT);
+ NETADD(c);
+ printoption("SENT", DONT, c);
+}
+
+void
+send_will(c, init)
+ register int c, init;
+{
+ if (init) {
+ if (((will_wont_resp[c] == 0) && my_state_is_will(c)) ||
+ my_want_state_is_will(c))
+ return;
+ set_my_want_state_will(c);
+ will_wont_resp[c]++;
+ }
+ NET2ADD(IAC, WILL);
+ NETADD(c);
+ printoption("SENT", WILL, c);
+}
+
+void
+send_wont(c, init)
+ register int c, init;
+{
+ if (init) {
+ if (((will_wont_resp[c] == 0) && my_state_is_wont(c)) ||
+ my_want_state_is_wont(c))
+ return;
+ set_my_want_state_wont(c);
+ will_wont_resp[c]++;
+ }
+ NET2ADD(IAC, WONT);
+ NETADD(c);
+ printoption("SENT", WONT, c);
+}
+
+
+static void
+willoption(option)
+ int option;
+{
+ int new_state_ok = 0;
+
+ if (do_dont_resp[option]) {
+ --do_dont_resp[option];
+ if (do_dont_resp[option] && my_state_is_do(option))
+ --do_dont_resp[option];
+ }
+
+ if ((do_dont_resp[option] == 0) && my_want_state_is_dont(option)) {
+
+ switch (option) {
+
+ case TELOPT_ECHO:
+ case TELOPT_SGA:
+ if (reqd_linemode && my_state_is_dont(option)) {
+ break;
+ }
+ /* FALLTHROUGH */
+ case TELOPT_BINARY:
+ settimer(modenegotiated);
+ /* FALLTHROUGH */
+ case TELOPT_STATUS:
+ case TELOPT_AUTHENTICATION:
+ /* FALLTHROUGH */
+ case TELOPT_ENCRYPT:
+ new_state_ok = 1;
+ break;
+
+ case TELOPT_TM:
+ if (flushout)
+ flushout = 0;
+ /*
+ * Special case for TM. If we get back a WILL,
+ * pretend we got back a WONT.
+ */
+ set_my_want_state_dont(option);
+ set_my_state_dont(option);
+ return; /* Never reply to TM will's/wont's */
+
+ case TELOPT_LINEMODE:
+ default:
+ break;
+ }
+
+ if (new_state_ok) {
+ set_my_want_state_do(option);
+ send_do(option, 0);
+ setconnmode(0); /* possibly set new tty mode */
+ } else {
+ do_dont_resp[option]++;
+ send_dont(option, 0);
+ }
+ }
+ set_my_state_do(option);
+ if (option == TELOPT_ENCRYPT)
+ encrypt_send_support();
+}
+
+static void
+wontoption(option)
+ int option;
+{
+ if (do_dont_resp[option]) {
+ --do_dont_resp[option];
+ if (do_dont_resp[option] && my_state_is_dont(option))
+ --do_dont_resp[option];
+ }
+
+ if ((do_dont_resp[option] == 0) && my_want_state_is_do(option)) {
+
+ switch (option) {
+
+#ifdef KLUDGELINEMODE
+ case TELOPT_SGA:
+ if (!kludgelinemode)
+ break;
+ /* FALLTHROUGH */
+#endif
+ case TELOPT_ECHO:
+ settimer(modenegotiated);
+ break;
+
+ case TELOPT_TM:
+ if (flushout)
+ flushout = 0;
+ set_my_want_state_dont(option);
+ set_my_state_dont(option);
+ return; /* Never reply to TM will's/wont's */
+
+ default:
+ break;
+ }
+ set_my_want_state_dont(option);
+ if (my_state_is_do(option))
+ send_dont(option, 0);
+ setconnmode(0); /* Set new tty mode */
+ } else if (option == TELOPT_TM) {
+ /*
+ * Special case for TM.
+ */
+ if (flushout)
+ flushout = 0;
+ set_my_want_state_dont(option);
+ }
+ set_my_state_dont(option);
+}
+
+static void
+dooption(option)
+ int option;
+{
+ int new_state_ok = 0;
+
+ if (will_wont_resp[option]) {
+ --will_wont_resp[option];
+ if (will_wont_resp[option] && my_state_is_will(option))
+ --will_wont_resp[option];
+ }
+
+ if (will_wont_resp[option] == 0) {
+ if (my_want_state_is_wont(option)) {
+
+ switch (option) {
+
+ case TELOPT_TM:
+ /*
+ * Special case for TM. We send a WILL,
+ * but pretend we sent WONT.
+ */
+ send_will(option, 0);
+ set_my_want_state_wont(TELOPT_TM);
+ set_my_state_wont(TELOPT_TM);
+ return;
+
+ case TELOPT_BINARY: /* binary mode */
+ case TELOPT_NAWS: /* window size */
+ case TELOPT_TSPEED: /* terminal speed */
+ case TELOPT_LFLOW: /* local flow control */
+ case TELOPT_TTYPE: /* terminal type option */
+ case TELOPT_SGA: /* no big deal */
+ case TELOPT_ENCRYPT: /* encryption variable option */
+ new_state_ok = 1;
+ break;
+
+ case TELOPT_NEW_ENVIRON:
+ /* New environment variable option */
+#ifdef OLD_ENVIRON
+ if (my_state_is_will(TELOPT_OLD_ENVIRON))
+ /* turn off the old */
+ send_wont(TELOPT_OLD_ENVIRON, 1);
+goto env_common;
+ case TELOPT_OLD_ENVIRON:
+ /* Old environment variable option */
+ if (my_state_is_will(TELOPT_NEW_ENVIRON))
+ /* Don't enable if new one is in use! */
+ break;
+env_common:
+ telopt_environ = option;
+#endif
+ new_state_ok = 1;
+ break;
+
+ case TELOPT_AUTHENTICATION:
+ if (autologin)
+ new_state_ok = 1;
+ break;
+
+ case TELOPT_XDISPLOC: /* X Display location */
+ if (env_getvalue((unsigned char *)"DISPLAY"))
+ new_state_ok = 1;
+ break;
+
+ case TELOPT_LINEMODE:
+#ifdef KLUDGELINEMODE
+ kludgelinemode = 0;
+ send_do(TELOPT_SGA, 1);
+#endif
+ set_my_want_state_will(TELOPT_LINEMODE);
+ send_will(option, 0);
+ set_my_state_will(TELOPT_LINEMODE);
+ slc_init();
+ return;
+
+ case TELOPT_ECHO: /* We're never going to echo... */
+ default:
+ break;
+ }
+
+ if (new_state_ok) {
+ set_my_want_state_will(option);
+ send_will(option, 0);
+ setconnmode(0); /* Set new tty mode */
+ } else {
+ will_wont_resp[option]++;
+ send_wont(option, 0);
+ }
+ } else {
+ /*
+ * Handle options that need more things done after the
+ * other side has acknowledged the option.
+ */
+ switch (option) {
+ case TELOPT_LINEMODE:
+#ifdef KLUDGELINEMODE
+ kludgelinemode = 0;
+ send_do(TELOPT_SGA, 1);
+#endif
+ set_my_state_will(option);
+ slc_init();
+ send_do(TELOPT_SGA, 0);
+ return;
+ }
+ }
+ }
+ set_my_state_will(option);
+}
+
+ static void
+dontoption(option)
+ int option;
+{
+
+ if (will_wont_resp[option]) {
+ --will_wont_resp[option];
+ if (will_wont_resp[option] && my_state_is_wont(option))
+ --will_wont_resp[option];
+ }
+
+ if ((will_wont_resp[option] == 0) && my_want_state_is_will(option)) {
+ switch (option) {
+ case TELOPT_LINEMODE:
+ linemode = 0; /* put us back to the default state */
+ break;
+#ifdef OLD_ENVIRON
+ case TELOPT_NEW_ENVIRON:
+ /*
+ * The new environ option wasn't recognized, try
+ * the old one.
+ */
+ send_will(TELOPT_OLD_ENVIRON, 1);
+ telopt_environ = TELOPT_OLD_ENVIRON;
+ break;
+#endif
+ }
+ /* we always accept a DONT */
+ set_my_want_state_wont(option);
+ if (my_state_is_will(option))
+ send_wont(option, 0);
+ setconnmode(0); /* Set new tty mode */
+ }
+ set_my_state_wont(option);
+}
+
+/*
+ * Given a buffer returned by tgetent(), this routine will turn
+ * the pipe seperated list of names in the buffer into an array
+ * of pointers to null terminated names. We toss out any bad,
+ * duplicate, or verbose names (names with spaces).
+ */
+
+static char *name_unknown = "UNKNOWN";
+static char *unknown[] = { 0, 0 };
+
+static char **
+mklist(buf, name)
+ char *buf, *name;
+{
+ register int n;
+ register char c, *cp, **argvp, *cp2, **argv, **avt;
+
+ if (name) {
+ if (strlen(name) > 40u) {
+ name = 0;
+ unknown[0] = name_unknown;
+ } else {
+ unknown[0] = name;
+ upcase(name);
+ }
+ } else
+ unknown[0] = name_unknown;
+ /*
+ * Count up the number of names.
+ */
+ for (n = 1, cp = buf; *cp && *cp != ':'; cp++) {
+ if (*cp == '|')
+ n++;
+ }
+ /*
+ * Allocate an array to put the name pointers into
+ */
+ argv = malloc((n+3)*sizeof (char *));
+ if (argv == 0)
+ return (unknown);
+
+ /*
+ * Fill up the array of pointers to names.
+ */
+ *argv = 0;
+ argvp = argv+1;
+ n = 0;
+ for (cp = cp2 = buf; (c = *cp) != NULL; cp++) {
+ if (c == '|' || c == ':') {
+ *cp++ = '\0';
+ /*
+ * Skip entries that have spaces or are over 40
+ * characters long. If this is our environment
+ * name, then put it up front. Otherwise, as
+ * long as this is not a duplicate name (case
+ * insensitive) add it to the list.
+ */
+ if (n || (cp - cp2 > 41))
+ /* EMPTY */;
+ else if (name && (strncasecmp(name, cp2, cp-cp2) == 0))
+ *argv = cp2;
+ else if (is_unique(cp2, argv+1, argvp))
+ *argvp++ = cp2;
+ if (c == ':')
+ break;
+ /*
+ * Skip multiple delimiters. Reset cp2 to
+ * the beginning of the next name. Reset n,
+ * the flag for names with spaces.
+ */
+ while ((c = *cp) == '|')
+ cp++;
+ cp2 = cp;
+ n = 0;
+ }
+ /*
+ * Skip entries with spaces or non-ascii values.
+ * Convert lower case letters to upper case.
+ */
+ if ((c == ' ') || !isascii(c))
+ n = 1;
+ else if (islower(c))
+ *cp = toupper(c);
+ }
+
+ /*
+ * Check for an old V6 2 character name. If the second
+ * name points to the beginning of the buffer, and is
+ * only 2 characters long, move it to the end of the array.
+ */
+ if ((argv[1] == buf) && (strlen(argv[1]) == 2)) {
+ --argvp;
+ for (avt = &argv[1]; avt < argvp; avt++)
+ *avt = *(avt+1);
+ *argvp++ = buf;
+ }
+
+ /*
+ * Duplicate last name, for TTYPE option, and null
+ * terminate the array. If we didn't find a match on
+ * our terminal name, put that name at the beginning.
+ */
+ cp = *(argvp-1);
+ *argvp++ = cp;
+ *argvp = 0;
+
+ if (*argv == 0) {
+ if (name)
+ *argv = name;
+ else {
+ --argvp;
+ for (avt = argv; avt < argvp; avt++)
+ *avt = *(avt+1);
+ }
+ }
+ if (*argv)
+ return (argv);
+ else
+ return (unknown);
+}
+
+static int
+is_unique(name, as, ae)
+ register char *name, **as, **ae;
+{
+ register char **ap;
+ register int n;
+
+ n = strlen(name) + 1;
+ for (ap = as; ap < ae; ap++)
+ if (strncasecmp(*ap, name, n) == 0)
+ return (0);
+ return (1);
+}
+
+#define termbuf ttytype
+extern char ttytype[];
+
+int resettermname = 1;
+
+static char *
+gettermname(void)
+{
+ char *tname;
+ static char **tnamep = 0;
+ static char **next;
+ int err;
+
+ if (resettermname) {
+ resettermname = 0;
+ if (tnamep && tnamep != unknown)
+ free(tnamep);
+ tname = (char *)env_getvalue((unsigned char *)"TERM");
+ if ((tname != NULL) && (setupterm(tname, 1, &err) == 0)) {
+ tnamep = mklist(termbuf, tname);
+ } else {
+ if (tname && (strlen(tname) <= 40u)) {
+ unknown[0] = tname;
+ upcase(tname);
+ } else
+ unknown[0] = name_unknown;
+ tnamep = unknown;
+ }
+ next = tnamep;
+ }
+ if (*next == 0)
+ next = tnamep;
+ return (*next++);
+}
+/*
+ * suboption()
+ *
+ * Look at the sub-option buffer, and try to be helpful to the other
+ * side.
+ *
+ * Currently we recognize:
+ *
+ * Terminal type, send request.
+ * Terminal speed (send request).
+ * Local flow control (is request).
+ * Linemode
+ */
+
+ static void
+suboption()
+{
+ unsigned char subchar;
+
+ printsub('<', subbuffer, SB_LEN()+2);
+ switch (subchar = SB_GET()) {
+ case TELOPT_TTYPE:
+ if (my_want_state_is_wont(TELOPT_TTYPE))
+ return;
+ if (SB_EOF() || SB_GET() != TELQUAL_SEND) {
+ return;
+ } else {
+ char *name;
+ unsigned char temp[50];
+ int len, bytes;
+
+ name = gettermname();
+ len = strlen(name) + 4 + 2;
+ bytes = snprintf((char *)temp, sizeof (temp),
+ "%c%c%c%c%s%c%c", IAC, SB,
+ TELOPT_TTYPE, TELQUAL_IS, name, IAC, SE);
+ if ((len < NETROOM()) && (bytes < sizeof (temp))) {
+ ring_supply_data(&netoring, temp, len);
+ printsub('>', &temp[2], len-2);
+ } else {
+ ExitString("No room in buffer for "
+ "terminal type.\n", EXIT_FAILURE);
+ /*NOTREACHED*/
+ }
+ }
+ break;
+ case TELOPT_TSPEED:
+ if (my_want_state_is_wont(TELOPT_TSPEED))
+ return;
+ if (SB_EOF())
+ return;
+ if (SB_GET() == TELQUAL_SEND) {
+ int ospeed, ispeed;
+ unsigned char temp[50];
+ int len, bytes;
+
+ TerminalSpeeds(&ispeed, &ospeed);
+
+ bytes = snprintf((char *)temp, sizeof (temp),
+ "%c%c%c%c%d,%d%c%c", IAC, SB,
+ TELOPT_TSPEED, TELQUAL_IS, ospeed, ispeed, IAC, SE);
+ len = strlen((char *)temp+4) + 4; /* temp[3] is 0 ... */
+
+ if ((len < NETROOM()) && (bytes < sizeof (temp))) {
+ ring_supply_data(&netoring, temp, len);
+ printsub('>', temp+2, len - 2);
+ }
+ else
+ (void) printf(
+ "telnet: not enough room in buffer "
+ "for terminal speed option reply\n");
+ }
+ break;
+ case TELOPT_LFLOW:
+ if (my_want_state_is_wont(TELOPT_LFLOW))
+ return;
+ if (SB_EOF())
+ return;
+ switch (SB_GET()) {
+ case LFLOW_RESTART_ANY:
+ restartany = 1;
+ break;
+ case LFLOW_RESTART_XON:
+ restartany = 0;
+ break;
+ case LFLOW_ON:
+ localflow = 1;
+ break;
+ case LFLOW_OFF:
+ localflow = 0;
+ break;
+ default:
+ return;
+ }
+ setcommandmode();
+ setconnmode(0);
+ break;
+
+ case TELOPT_LINEMODE:
+ if (my_want_state_is_wont(TELOPT_LINEMODE))
+ return;
+ if (SB_EOF())
+ return;
+ switch (SB_GET()) {
+ case WILL:
+ lm_will(subpointer, SB_LEN());
+ break;
+ case WONT:
+ lm_wont(subpointer, SB_LEN());
+ break;
+ case DO:
+ lm_do(subpointer, SB_LEN());
+ break;
+ case DONT:
+ lm_dont(subpointer, SB_LEN());
+ break;
+ case LM_SLC:
+ slc(subpointer, SB_LEN());
+ break;
+ case LM_MODE:
+ lm_mode(subpointer, SB_LEN(), 0);
+ break;
+ default:
+ break;
+ }
+ break;
+
+#ifdef OLD_ENVIRON
+ case TELOPT_OLD_ENVIRON:
+#endif
+ case TELOPT_NEW_ENVIRON:
+ if (SB_EOF())
+ return;
+ switch (SB_PEEK()) {
+ case TELQUAL_IS:
+ case TELQUAL_INFO:
+ if (my_want_state_is_dont(subchar))
+ return;
+ break;
+ case TELQUAL_SEND:
+ if (my_want_state_is_wont(subchar)) {
+ return;
+ }
+ break;
+ default:
+ return;
+ }
+ env_opt(subpointer, SB_LEN());
+ break;
+
+ case TELOPT_XDISPLOC:
+ if (my_want_state_is_wont(TELOPT_XDISPLOC))
+ return;
+ if (SB_EOF())
+ return;
+ if (SB_GET() == TELQUAL_SEND) {
+ unsigned char temp[50], *dp;
+ int len, bytes;
+
+ if ((dp = env_getvalue((unsigned char *)"DISPLAY")) ==
+ NULL) {
+ /*
+ * Something happened, we no longer have a
+ * DISPLAY variable. So, turn off the option.
+ */
+ send_wont(TELOPT_XDISPLOC, 1);
+ break;
+ }
+ bytes = snprintf((char *)temp, sizeof (temp),
+ "%c%c%c%c%s%c%c", IAC, SB,
+ TELOPT_XDISPLOC, TELQUAL_IS, dp, IAC, SE);
+ len = strlen((char *)temp+4) + 4; /* temp[3] is 0 ... */
+
+ if ((len < NETROOM()) && (bytes < sizeof (temp))) {
+ ring_supply_data(&netoring, temp, len);
+ printsub('>', temp+2, len - 2);
+ }
+ else
+ (void) printf(
+ "telnet: not enough room in buffer"
+ " for display location option reply\n");
+ }
+ break;
+
+ case TELOPT_AUTHENTICATION: {
+ if (!autologin)
+ break;
+ if (SB_EOF())
+ return;
+ switch (SB_GET()) {
+ case TELQUAL_SEND:
+ if (my_want_state_is_wont(TELOPT_AUTHENTICATION))
+ return;
+ auth_send(subpointer, SB_LEN());
+ break;
+ case TELQUAL_REPLY:
+ if (my_want_state_is_wont(TELOPT_AUTHENTICATION))
+ return;
+ auth_reply(subpointer, SB_LEN());
+ break;
+ }
+ }
+ break;
+
+ case TELOPT_ENCRYPT:
+ if (SB_EOF())
+ return;
+ switch (SB_GET()) {
+ case ENCRYPT_START:
+ if (my_want_state_is_dont(TELOPT_ENCRYPT))
+ return;
+ encrypt_start(subpointer, SB_LEN());
+ break;
+ case ENCRYPT_END:
+ if (my_want_state_is_dont(TELOPT_ENCRYPT))
+ return;
+ encrypt_end();
+ break;
+ case ENCRYPT_SUPPORT:
+ if (my_want_state_is_wont(TELOPT_ENCRYPT))
+ return;
+ encrypt_support(subpointer, SB_LEN());
+ break;
+ case ENCRYPT_REQSTART:
+ if (my_want_state_is_wont(TELOPT_ENCRYPT))
+ return;
+ encrypt_request_start(subpointer, SB_LEN());
+ break;
+ case ENCRYPT_REQEND:
+ if (my_want_state_is_wont(TELOPT_ENCRYPT))
+ return;
+ /*
+ * We can always send an REQEND so that we cannot
+ * get stuck encrypting. We should only get this
+ * if we have been able to get in the correct mode
+ * anyhow.
+ */
+ encrypt_request_end();
+ break;
+ case ENCRYPT_IS:
+ if (my_want_state_is_dont(TELOPT_ENCRYPT))
+ return;
+ encrypt_is(subpointer, SB_LEN());
+ break;
+ case ENCRYPT_REPLY:
+ if (my_want_state_is_wont(TELOPT_ENCRYPT))
+ return;
+ encrypt_reply(subpointer, SB_LEN());
+ break;
+ case ENCRYPT_ENC_KEYID:
+ if (my_want_state_is_dont(TELOPT_ENCRYPT))
+ return;
+ encrypt_enc_keyid(subpointer, SB_LEN());
+ break;
+ case ENCRYPT_DEC_KEYID:
+ if (my_want_state_is_wont(TELOPT_ENCRYPT))
+ return;
+ encrypt_dec_keyid(subpointer, SB_LEN());
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static unsigned char str_lm[] = { IAC, SB, TELOPT_LINEMODE, 0, 0, IAC, SE };
+
+static void
+lm_will(cmd, len)
+ unsigned char *cmd;
+ int len;
+{
+ if (len < 1) {
+ /* Should not happen... */
+ (void) printf(
+ "telnet: command missing from linemode WILL request\n");
+ return;
+ }
+ switch (cmd[0]) {
+ case LM_FORWARDMASK: /* We shouldn't ever get this... */
+ default:
+ str_lm[3] = DONT;
+ str_lm[4] = cmd[0];
+ if (NETROOM() > sizeof (str_lm)) {
+ ring_supply_data(&netoring, str_lm, sizeof (str_lm));
+ printsub('>', &str_lm[2], sizeof (str_lm)-2);
+ }
+ else
+ (void) printf("telnet: not enough room in buffer for"
+ "reply to linemode WILL request\n");
+ break;
+ }
+}
+
+static void
+lm_wont(cmd, len)
+ unsigned char *cmd;
+ int len;
+{
+ if (len < 1) {
+ /* Should not happen... */
+ (void) printf(
+ "telnet: command missing from linemode WONT request\n");
+ return;
+ }
+ switch (cmd[0]) {
+ case LM_FORWARDMASK: /* We shouldn't ever get this... */
+ default:
+ /* We are always DONT, so don't respond */
+ return;
+ }
+}
+
+static void
+lm_do(cmd, len)
+ unsigned char *cmd;
+ int len;
+{
+ if (len < 1) {
+ /* Should not happen... */
+ (void) printf(
+ "telnet: command missing from linemode DO request\n");
+ return;
+ }
+ switch (cmd[0]) {
+ case LM_FORWARDMASK:
+ default:
+ str_lm[3] = WONT;
+ str_lm[4] = cmd[0];
+ if (NETROOM() > sizeof (str_lm)) {
+ ring_supply_data(&netoring, str_lm, sizeof (str_lm));
+ printsub('>', &str_lm[2], sizeof (str_lm)-2);
+ }
+ else
+ (void) printf("telnet: not enough room in buffer for"
+ "reply to linemode DO request\n");
+ break;
+ }
+}
+
+static void
+lm_dont(cmd, len)
+ unsigned char *cmd;
+ int len;
+{
+ if (len < 1) {
+ /* Should not happen... */
+ (void) printf(
+ "telnet: command missing from linemode DONT request\n");
+ return;
+ }
+ switch (cmd[0]) {
+ case LM_FORWARDMASK:
+ default:
+ /* we are always WONT, so don't respond */
+ break;
+ }
+}
+
+static unsigned char str_lm_mode[] = {
+ IAC, SB, TELOPT_LINEMODE, LM_MODE, 0, IAC, SE
+};
+
+ void
+lm_mode(cmd, len, init)
+ unsigned char *cmd;
+ int len, init;
+{
+ if (len != 1)
+ return;
+ if ((linemode&MODE_MASK&~MODE_ACK) == *cmd)
+ return;
+ linemode = *cmd&(MODE_MASK&~MODE_ACK);
+ str_lm_mode[4] = linemode;
+ if (!init)
+ str_lm_mode[4] |= MODE_ACK;
+ if (NETROOM() > sizeof (str_lm_mode)) {
+ ring_supply_data(&netoring, str_lm_mode, sizeof (str_lm_mode));
+ printsub('>', &str_lm_mode[2], sizeof (str_lm_mode)-2);
+ }
+ else
+ (void) printf("telnet: not enough room in buffer for"
+ "reply to linemode request\n");
+ setconnmode(0); /* set changed mode */
+}
+
+
+
+/*
+ * slc()
+ * Handle special character suboption of LINEMODE.
+ */
+
+static struct spc {
+ cc_t val;
+ cc_t *valp;
+ char flags; /* Current flags & level */
+ char mylevel; /* Maximum level & flags */
+} spc_data[NSLC+1];
+
+#define SLC_IMPORT 0
+#define SLC_EXPORT 1
+#define SLC_RVALUE 2
+static int slc_mode = SLC_EXPORT;
+
+static void
+slc_init()
+{
+ register struct spc *spcp;
+
+ localchars = 1;
+ for (spcp = spc_data; spcp < &spc_data[NSLC+1]; spcp++) {
+ spcp->val = 0;
+ spcp->valp = 0;
+ spcp->flags = spcp->mylevel = SLC_NOSUPPORT;
+ }
+
+#define initfunc(func, flags) { \
+ spcp = &spc_data[func]; \
+ if (spcp->valp = tcval(func)) { \
+ spcp->val = *spcp->valp; \
+ spcp->mylevel = SLC_VARIABLE|(flags);\
+ } else { \
+ spcp->val = 0; \
+ spcp->mylevel = SLC_DEFAULT; \
+ } \
+ }
+
+ initfunc(SLC_SYNCH, 0);
+ /* No BRK */
+ initfunc(SLC_AO, 0);
+ initfunc(SLC_AYT, 0);
+ /* No EOR */
+ initfunc(SLC_ABORT, SLC_FLUSHIN|SLC_FLUSHOUT);
+ initfunc(SLC_EOF, 0);
+ initfunc(SLC_SUSP, SLC_FLUSHIN);
+ initfunc(SLC_EC, 0);
+ initfunc(SLC_EL, 0);
+ initfunc(SLC_EW, 0);
+ initfunc(SLC_RP, 0);
+ initfunc(SLC_LNEXT, 0);
+ initfunc(SLC_XON, 0);
+ initfunc(SLC_XOFF, 0);
+ initfunc(SLC_FORW1, 0);
+#ifdef USE_TERMIO
+ initfunc(SLC_FORW2, 0);
+ /* No FORW2 */
+#endif
+
+ initfunc(SLC_IP, SLC_FLUSHIN|SLC_FLUSHOUT);
+#undef initfunc
+
+ if (slc_mode == SLC_EXPORT)
+ slc_export();
+ else
+ slc_import(1);
+
+}
+
+void
+slcstate()
+{
+ (void) printf("Special characters are %s values\n",
+ slc_mode == SLC_IMPORT ? "remote default" :
+ slc_mode == SLC_EXPORT ? "local" :
+ "remote");
+}
+
+void
+slc_mode_export()
+{
+ slc_mode = SLC_EXPORT;
+ if (my_state_is_will(TELOPT_LINEMODE))
+ slc_export();
+}
+
+void
+slc_mode_import(def)
+ int def;
+{
+ slc_mode = def ? SLC_IMPORT : SLC_RVALUE;
+ if (my_state_is_will(TELOPT_LINEMODE))
+ slc_import(def);
+}
+
+static unsigned char slc_import_val[] = {
+ IAC, SB, TELOPT_LINEMODE, LM_SLC, 0, SLC_VARIABLE, 0, IAC, SE
+};
+static unsigned char slc_import_def[] = {
+ IAC, SB, TELOPT_LINEMODE, LM_SLC, 0, SLC_DEFAULT, 0, IAC, SE
+};
+
+static void
+slc_import(def)
+ int def;
+{
+ if (NETROOM() > sizeof (slc_import_val)) {
+ if (def) {
+ ring_supply_data(&netoring, slc_import_def,
+ sizeof (slc_import_def));
+ printsub('>', &slc_import_def[2],
+ sizeof (slc_import_def)-2);
+ } else {
+ ring_supply_data(&netoring, slc_import_val,
+ sizeof (slc_import_val));
+ printsub('>', &slc_import_val[2],
+ sizeof (slc_import_val)-2);
+ }
+ }
+ else
+ (void) printf(
+ "telnet: not enough room in buffer for slc import"
+ " request\n");
+}
+
+static uchar_t *slc_reply = NULL;
+static uchar_t *slc_replyp = NULL;
+/*
+ * The SLC reply consists of: IAC, SB, TELOPT_LINEMODE, LM_SLC,
+ * SLC triplets[], IAC, SE. i.e. it has a 'wrapper' of 6 control characters.
+ */
+#define SLC_WRAPPER_SIZE 6
+
+static void
+slc_export()
+{
+ register struct spc *spcp;
+
+ TerminalDefaultChars();
+
+ slc_start_reply(NSLC * 3); /* 3 bytes needed per triplet */
+ for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) {
+ if (spcp->mylevel != SLC_NOSUPPORT) {
+ if (spcp->val == (cc_t)(_POSIX_VDISABLE))
+ spcp->flags = SLC_NOSUPPORT;
+ else
+ spcp->flags = spcp->mylevel;
+ if (spcp->valp)
+ spcp->val = *spcp->valp;
+ slc_add_reply(spcp - spc_data, spcp->flags, spcp->val);
+ }
+ }
+ slc_end_reply();
+ (void) slc_update();
+ setconnmode(1); /* Make sure the character values are set */
+}
+
+static void
+slc(cp, len)
+ register unsigned char *cp;
+ int len;
+{
+ register struct spc *spcp;
+ register int func, level;
+
+ slc_start_reply(len);
+
+ for (; len >= 3; len -= 3, cp += 3) {
+
+ func = cp[SLC_FUNC];
+
+ if (func == 0) {
+ /*
+ * Client side: always ignore 0 function.
+ */
+ continue;
+ }
+ if (func > NSLC) {
+ if ((cp[SLC_FLAGS] & SLC_LEVELBITS) != SLC_NOSUPPORT)
+ slc_add_reply(func, SLC_NOSUPPORT, 0);
+ continue;
+ }
+
+ spcp = &spc_data[func];
+
+ level = cp[SLC_FLAGS]&(SLC_LEVELBITS|SLC_ACK);
+
+ if ((cp[SLC_VALUE] == (unsigned char)spcp->val) &&
+ ((level&SLC_LEVELBITS) == (spcp->flags&SLC_LEVELBITS))) {
+ continue;
+ }
+
+ if (level == (SLC_DEFAULT|SLC_ACK)) {
+ /*
+ * This is an error condition, the SLC_ACK
+ * bit should never be set for the SLC_DEFAULT
+ * level. Our best guess to recover is to
+ * ignore the SLC_ACK bit.
+ */
+ cp[SLC_FLAGS] &= ~SLC_ACK;
+ }
+
+ if (level == ((spcp->flags&SLC_LEVELBITS)|SLC_ACK)) {
+ spcp->val = (cc_t)cp[SLC_VALUE];
+ spcp->flags = cp[SLC_FLAGS]; /* include SLC_ACK */
+ continue;
+ }
+
+ level &= ~SLC_ACK;
+
+ if (level <= (spcp->mylevel&SLC_LEVELBITS)) {
+ spcp->flags = cp[SLC_FLAGS]|SLC_ACK;
+ spcp->val = (cc_t)cp[SLC_VALUE];
+ }
+ if (level == SLC_DEFAULT) {
+ if ((spcp->mylevel&SLC_LEVELBITS) != SLC_DEFAULT)
+ spcp->flags = spcp->mylevel;
+ else
+ spcp->flags = SLC_NOSUPPORT;
+ }
+ slc_add_reply(func, spcp->flags, spcp->val);
+ }
+ slc_end_reply();
+ if (slc_update())
+ setconnmode(1); /* set the new character values */
+}
+
+void
+slc_check()
+{
+ register struct spc *spcp;
+
+ slc_start_reply(NSLC * 3); /* 3 bytes needed per triplet */
+ for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) {
+ if (spcp->valp && spcp->val != *spcp->valp) {
+ spcp->val = *spcp->valp;
+ if (spcp->val == (cc_t)(_POSIX_VDISABLE))
+ spcp->flags = SLC_NOSUPPORT;
+ else
+ spcp->flags = spcp->mylevel;
+ slc_add_reply(spcp - spc_data, spcp->flags, spcp->val);
+ }
+ }
+ slc_end_reply();
+ setconnmode(1);
+}
+
+static void
+slc_start_reply(size_t len)
+{
+ /*
+ * SLC triplets may contain escaped characters, allow for
+ * worst case by allocating 2 bytes for every character.
+ */
+ slc_reply = realloc(slc_reply, (len * 2) + SLC_WRAPPER_SIZE);
+ if (slc_reply == NULL) {
+ fprintf(stderr, "telnet: error allocating SLC reply memory\n");
+ return;
+ }
+ slc_replyp = slc_reply;
+ *slc_replyp++ = IAC;
+ *slc_replyp++ = SB;
+ *slc_replyp++ = TELOPT_LINEMODE;
+ *slc_replyp++ = LM_SLC;
+}
+
+static void
+slc_add_reply(unsigned char func, unsigned char flags, cc_t value)
+{
+ if ((*slc_replyp++ = func) == IAC)
+ *slc_replyp++ = IAC;
+ if ((*slc_replyp++ = flags) == IAC)
+ *slc_replyp++ = IAC;
+ if ((*slc_replyp++ = (unsigned char)value) == IAC)
+ *slc_replyp++ = IAC;
+}
+
+static void
+slc_end_reply()
+{
+ register int len;
+
+ *slc_replyp++ = IAC;
+ *slc_replyp++ = SE;
+ len = slc_replyp - slc_reply;
+ if (len <= SLC_WRAPPER_SIZE)
+ return;
+ if (NETROOM() > len) {
+ ring_supply_data(&netoring, slc_reply, slc_replyp - slc_reply);
+ printsub('>', &slc_reply[2], slc_replyp - slc_reply - 2);
+ }
+ else
+ (void) printf("telnet: not enough room in buffer for slc end "
+ "reply\n");
+}
+
+static int
+slc_update()
+{
+ register struct spc *spcp;
+ int need_update = 0;
+
+ for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) {
+ if (!(spcp->flags&SLC_ACK))
+ continue;
+ spcp->flags &= ~SLC_ACK;
+ if (spcp->valp && (*spcp->valp != spcp->val)) {
+ *spcp->valp = spcp->val;
+ need_update = 1;
+ }
+ }
+ return (need_update);
+}
+
+#ifdef OLD_ENVIRON
+#ifdef ENV_HACK
+/*
+ * Earlier version of telnet/telnetd from the BSD code had
+ * the definitions of VALUE and VAR reversed. To ensure
+ * maximum interoperability, we assume that the server is
+ * an older BSD server, until proven otherwise. The newer
+ * BSD servers should be able to handle either definition,
+ * so it is better to use the wrong values if we don't
+ * know what type of server it is.
+ */
+int env_auto = 1;
+int old_env_var = OLD_ENV_VAR;
+int old_env_value = OLD_ENV_VALUE;
+#else
+#define old_env_var OLD_ENV_VAR
+#define old_env_value OLD_ENV_VALUE
+#endif
+#endif
+
+static void
+env_opt(buf, len)
+ register unsigned char *buf;
+ register int len;
+{
+ register unsigned char *ep = 0, *epc = 0;
+ register int i;
+
+ switch (buf[0]&0xff) {
+ case TELQUAL_SEND:
+ env_opt_start();
+ if (len == 1) {
+ env_opt_add(NULL);
+ } else for (i = 1; i < len; i++) {
+ switch (buf[i]&0xff) {
+#ifdef OLD_ENVIRON
+ case OLD_ENV_VAR:
+#ifdef ENV_HACK
+ if (telopt_environ == TELOPT_OLD_ENVIRON &&
+ env_auto) {
+ /* Server has the same definitions */
+ old_env_var = OLD_ENV_VAR;
+ old_env_value = OLD_ENV_VALUE;
+ }
+ /* FALLTHROUGH */
+#endif
+ case OLD_ENV_VALUE:
+ /*
+ * Although OLD_ENV_VALUE is not legal, we will
+ * still recognize it, just in case it is an
+ * old server that has VAR & VALUE mixed up...
+ */
+ /* FALLTHROUGH */
+#else
+ case NEW_ENV_VAR:
+#endif
+ case ENV_USERVAR:
+ if (ep) {
+ *epc = 0;
+ env_opt_add(ep);
+ }
+ ep = epc = &buf[i+1];
+ break;
+ case ENV_ESC:
+ i++;
+ /*FALLTHROUGH*/
+ default:
+ if (epc)
+ *epc++ = buf[i];
+ break;
+ }
+ }
+ if (ep) {
+ *epc = 0;
+ env_opt_add(ep);
+ }
+ env_opt_end(1);
+ break;
+
+ case TELQUAL_IS:
+ case TELQUAL_INFO:
+ /* Ignore for now. We shouldn't get it anyway. */
+ break;
+
+ default:
+ break;
+ }
+}
+
+static unsigned char *opt_reply;
+static unsigned char *opt_replyp;
+static unsigned char *opt_replyend;
+#define OPT_REPLY_INITIAL_SIZE 256
+/*
+ * The opt reply consists of: IAC, SB, telopt_environ, TELQUAL_IS,
+ * value, IAC, SE. i.e. it has a 'wrapper' of 6 control characters.
+ */
+#define OPT_WRAPPER_SIZE 6
+
+static void
+env_opt_start()
+{
+ opt_reply = realloc(opt_reply, OPT_REPLY_INITIAL_SIZE);
+ if (opt_reply == NULL) {
+ (void) printf(
+ "telnet: error allocating environment option memory\n");
+ opt_reply = opt_replyp = opt_replyend = NULL;
+ return;
+ }
+ opt_replyp = opt_reply;
+ opt_replyend = opt_reply + OPT_REPLY_INITIAL_SIZE;
+ *opt_replyp++ = IAC;
+ *opt_replyp++ = SB;
+ *opt_replyp++ = telopt_environ;
+ *opt_replyp++ = TELQUAL_IS;
+}
+
+ void
+env_opt_start_info()
+{
+ env_opt_start();
+ if (opt_replyp)
+ opt_replyp[-1] = TELQUAL_INFO;
+}
+
+ void
+env_opt_add(ep)
+ register unsigned char *ep;
+{
+ register unsigned char *vp, c;
+ int opt_reply_size;
+ int opt_reply_used;
+
+ if (opt_reply == NULL) /* XXX */
+ return; /* XXX */
+
+ if (ep == NULL || *ep == '\0') {
+ /* Send user defined variables first. */
+ (void) env_default(1, 0);
+ while (ep = env_default(0, 0))
+ env_opt_add(ep);
+
+ /* Now add the list of well know variables. */
+ (void) env_default(1, 1);
+ while (ep = env_default(0, 1))
+ env_opt_add(ep);
+ return;
+ }
+ vp = env_getvalue(ep);
+
+ /*
+ * Calculate space required for opt_reply and allocate more if required.
+ * Assume worst case that every character is escaped, so needs 2 bytes.
+ */
+ opt_reply_used = opt_replyp - opt_reply; /* existing contents */
+ opt_reply_size = opt_reply_used + OPT_WRAPPER_SIZE +
+ (2 * (strlen((char *)ep))) +
+ (vp == NULL ? 0 : (2 * strlen((char *)vp)));
+
+ if (opt_reply_size > (opt_replyend - opt_reply)) {
+ opt_reply = realloc(opt_reply, opt_reply_size);
+ if (opt_reply == NULL) {
+ (void) printf(
+ "telnet: can't allocate environment option "
+ "reply\n");
+ opt_reply = opt_replyp = opt_replyend = NULL;
+ return;
+ }
+ opt_replyp = opt_reply + opt_reply_used;
+ opt_replyend = opt_reply + opt_reply_size;
+ }
+
+ if (opt_welldefined((char *)ep))
+#ifdef OLD_ENVIRON
+ if (telopt_environ == TELOPT_OLD_ENVIRON)
+ *opt_replyp++ = old_env_var;
+ else
+#endif
+ *opt_replyp++ = NEW_ENV_VAR;
+ else
+ *opt_replyp++ = ENV_USERVAR;
+ for (;;) {
+ while ((c = *ep++) != NULL) {
+ switch (c&0xff) {
+ case IAC:
+ *opt_replyp++ = IAC;
+ break;
+ case NEW_ENV_VAR:
+ case NEW_ENV_VALUE:
+ case ENV_ESC:
+ case ENV_USERVAR:
+ *opt_replyp++ = ENV_ESC;
+ break;
+ }
+ *opt_replyp++ = c;
+ }
+ if ((ep = vp) != NULL) {
+#ifdef OLD_ENVIRON
+ if (telopt_environ == TELOPT_OLD_ENVIRON)
+ *opt_replyp++ = old_env_value;
+ else
+#endif
+ *opt_replyp++ = NEW_ENV_VALUE;
+ vp = NULL;
+ } else
+ break;
+ }
+}
+
+ int
+opt_welldefined(ep)
+ char *ep;
+{
+ if ((strcmp(ep, "USER") == 0) ||
+ (strcmp(ep, "DISPLAY") == 0) ||
+ (strcmp(ep, "PRINTER") == 0) ||
+ (strcmp(ep, "SYSTEMTYPE") == 0) ||
+ (strcmp(ep, "JOB") == 0) ||
+ (strcmp(ep, "ACCT") == 0))
+ return (1);
+ return (0);
+}
+ void
+env_opt_end(emptyok)
+ register int emptyok;
+{
+ register int len;
+
+ len = opt_replyp - opt_reply + 2;
+ if (emptyok || len > OPT_WRAPPER_SIZE) {
+ *opt_replyp++ = IAC;
+ *opt_replyp++ = SE;
+ if (NETROOM() > len) {
+ ring_supply_data(&netoring, opt_reply, len);
+ printsub('>', &opt_reply[2], len - 2);
+ }
+ else
+ (void) printf("telnet: not enough room in buffer for "
+ "environment option end reply\n");
+ }
+ if (opt_reply) {
+ free(opt_reply);
+ opt_reply = opt_replyp = opt_replyend = NULL;
+ }
+}
+
+
+
+int
+telrcv()
+{
+ register int c;
+ register int scc;
+ register unsigned char *sbp;
+ int count;
+ int returnValue = 0;
+ int min_room = 0;
+
+ scc = 0;
+ count = 0;
+ while (--min_room > 2 || (min_room = TTYROOM()) > 2) {
+ if (scc == 0) {
+ if (count) {
+ ring_consumed(&netiring, count);
+ returnValue = 1;
+ count = 0;
+ }
+ sbp = netiring.consume;
+ scc = ring_full_consecutive(&netiring);
+ if (scc == 0) {
+ /* No more data coming in */
+ break;
+ }
+ }
+
+ c = *sbp++ & 0xff, scc--; count++;
+
+ if (decrypt_input)
+ c = (*decrypt_input)(c);
+
+ switch (telrcv_state) {
+
+ case TS_CR:
+ telrcv_state = TS_DATA;
+ if (c == '\0') {
+ break; /* Ignore \0 after CR */
+ } else if ((c == '\n') &&
+ my_want_state_is_dont(TELOPT_ECHO) && !crmod) {
+ TTYADD(c);
+ break;
+ }
+ /* FALLTHROUGH */
+
+ case TS_DATA:
+ if (c == IAC) {
+ telrcv_state = TS_IAC;
+ break;
+ }
+ /*
+ * The 'crmod' hack (see following) is needed
+ * since we can't * set CRMOD on output only.
+ * Machines like MULTICS like to send \r without
+ * \n; since we must turn off CRMOD to get proper
+ * input, the mapping is done here (sigh).
+ */
+ if ((c == '\r') &&
+ my_want_state_is_dont(TELOPT_BINARY)) {
+ if (scc > 0) {
+ c = *sbp&0xff;
+
+ if (decrypt_input)
+ c = (*decrypt_input)(c);
+
+ if (c == 0) {
+ sbp++, scc--; count++;
+ /* a "true" CR */
+ TTYADD('\r');
+ } else if (my_want_state_is_dont(
+ TELOPT_ECHO) && (c == '\n')) {
+ sbp++, scc--; count++;
+ TTYADD('\n');
+ } else {
+
+ if (decrypt_input)
+ (*decrypt_input)(-1);
+
+ TTYADD('\r');
+ if (crmod) {
+ TTYADD('\n');
+ }
+ }
+ } else {
+ telrcv_state = TS_CR;
+ TTYADD('\r');
+ if (crmod) {
+ TTYADD('\n');
+ }
+ }
+ } else {
+ TTYADD(c);
+ }
+ continue;
+
+ case TS_IAC:
+process_iac:
+ switch (c) {
+
+ case WILL:
+ telrcv_state = TS_WILL;
+ continue;
+
+ case WONT:
+ telrcv_state = TS_WONT;
+ continue;
+
+ case DO:
+ telrcv_state = TS_DO;
+ continue;
+
+ case DONT:
+ telrcv_state = TS_DONT;
+ continue;
+
+ case DM:
+ /*
+ * We may have missed an urgent notification,
+ * so make sure we flush whatever is in the
+ * buffer currently.
+ */
+ printoption("RCVD", IAC, DM);
+ SYNCHing = 1;
+ if (ttyflush(1) == -2) {
+ /* This will not return. */
+ fatal_tty_error("write");
+ }
+ SYNCHing = stilloob();
+ settimer(gotDM);
+ break;
+
+ case SB:
+ SB_CLEAR();
+ telrcv_state = TS_SB;
+ continue;
+
+ case IAC:
+ TTYADD(IAC);
+ break;
+
+ case NOP:
+ case GA:
+ default:
+ printoption("RCVD", IAC, c);
+ break;
+ }
+ telrcv_state = TS_DATA;
+ continue;
+
+ case TS_WILL:
+ printoption("RCVD", WILL, c);
+ willoption(c);
+ telrcv_state = TS_DATA;
+ continue;
+
+ case TS_WONT:
+ printoption("RCVD", WONT, c);
+ wontoption(c);
+ telrcv_state = TS_DATA;
+ continue;
+
+ case TS_DO:
+ printoption("RCVD", DO, c);
+ dooption(c);
+ if (c == TELOPT_NAWS) {
+ sendnaws();
+ } else if (c == TELOPT_LFLOW) {
+ localflow = 1;
+ setcommandmode();
+ setconnmode(0);
+ }
+ telrcv_state = TS_DATA;
+ continue;
+
+ case TS_DONT:
+ printoption("RCVD", DONT, c);
+ dontoption(c);
+ flushline = 1;
+ setconnmode(0); /* set new tty mode (maybe) */
+ telrcv_state = TS_DATA;
+ continue;
+
+ case TS_SB:
+ if (c == IAC) {
+ telrcv_state = TS_SE;
+ } else {
+ SB_ACCUM(c);
+ }
+ continue;
+
+ case TS_SE:
+ if (c != SE) {
+ if (c != IAC) {
+ /*
+ * This is an error. We only expect to get
+ * "IAC IAC" or "IAC SE". Several things may
+ * have happend. An IAC was not doubled, the
+ * IAC SE was left off, or another option got
+ * inserted into the suboption are all possibilities.
+ * If we assume that the IAC was not doubled,
+ * and really the IAC SE was left off, we could
+ * get into an infinate loop here. So, instead,
+ * we terminate the suboption, and process the
+ * partial suboption if we can.
+ */
+ SB_ACCUM(IAC);
+ SB_ACCUM(c);
+ subpointer -= 2;
+ SB_TERM();
+
+ printoption("In SUBOPTION processing, "
+ "RCVD", IAC, c);
+ suboption(); /* handle sub-option */
+ telrcv_state = TS_IAC;
+ goto process_iac;
+ }
+ SB_ACCUM(c);
+ telrcv_state = TS_SB;
+ } else {
+ SB_ACCUM(IAC);
+ SB_ACCUM(SE);
+ subpointer -= 2;
+ SB_TERM();
+ suboption(); /* handle sub-option */
+ telrcv_state = TS_DATA;
+ }
+ }
+ }
+ if (count)
+ ring_consumed(&netiring, count);
+ return (returnValue||count);
+}
+
+static int bol = 1, local = 0;
+
+int
+rlogin_susp()
+{
+ if (local) {
+ local = 0;
+ bol = 1;
+ command(0, "z\n", 2);
+ return (1);
+ }
+ return (0);
+}
+
+static int
+telsnd()
+{
+ int tcc;
+ int count;
+ int returnValue = 0;
+ unsigned char *tbp;
+
+ tcc = 0;
+ count = 0;
+ while (NETROOM() > 2) {
+ register int sc;
+ register int c;
+
+ if (tcc == 0) {
+ if (count) {
+ ring_consumed(&ttyiring, count);
+ returnValue = 1;
+ count = 0;
+ }
+ tbp = ttyiring.consume;
+ tcc = ring_full_consecutive(&ttyiring);
+ if (tcc == 0) {
+ break;
+ }
+ }
+ c = *tbp++ & 0xff, sc = strip(c), tcc--; count++;
+ if (rlogin != _POSIX_VDISABLE) {
+ if (bol) {
+ bol = 0;
+ if (sc == rlogin) {
+ local = 1;
+ continue;
+ }
+ } else if (local) {
+ local = 0;
+ if (sc == '.' || c == termEofChar) {
+ bol = 1;
+ command(0, "close\n", 6);
+ continue;
+ }
+ if (sc == termSuspChar) {
+ bol = 1;
+ command(0, "z\n", 2);
+ continue;
+ }
+ if (sc == escape) {
+ command(0, (char *)tbp, tcc);
+ bol = 1;
+ count += tcc;
+ tcc = 0;
+ flushline = 1;
+ break;
+ }
+ if (sc != rlogin) {
+ ++tcc;
+ --tbp;
+ --count;
+ c = sc = rlogin;
+ }
+ }
+ if ((sc == '\n') || (sc == '\r'))
+ bol = 1;
+ } else if (sc == escape && escape_valid) {
+ /*
+ * Double escape is a pass through of a single
+ * escape character.
+ */
+ if (tcc && strip(*tbp) == escape) {
+ tbp++;
+ tcc--;
+ count++;
+ bol = 0;
+ } else {
+ command(0, (char *)tbp, tcc);
+ bol = 1;
+ count += tcc;
+ tcc = 0;
+ flushline = 1;
+ break;
+ }
+ } else
+ bol = 0;
+#ifdef KLUDGELINEMODE
+ if (kludgelinemode && (globalmode&MODE_EDIT) && (sc == echoc)) {
+ if (tcc > 0 && strip(*tbp) == echoc) {
+ tcc--; tbp++; count++;
+ } else {
+ dontlecho = !dontlecho;
+ settimer(echotoggle);
+ setconnmode(0);
+ flushline = 1;
+ break;
+ }
+ }
+#endif
+ if (MODE_LOCAL_CHARS(globalmode)) {
+ if (TerminalSpecialChars(sc) == 0) {
+ bol = 1;
+ break;
+ }
+ }
+ if (my_want_state_is_wont(TELOPT_BINARY)) {
+ switch (c) {
+ case '\n':
+ /*
+ * If we are in CRMOD mode (\r ==> \n)
+ * on our local machine, then probably
+ * a newline (unix) is CRLF (TELNET).
+ */
+ if (MODE_LOCAL_CHARS(globalmode)) {
+ NETADD('\r');
+ }
+ NETADD('\n');
+ bol = flushline = 1;
+ break;
+ case '\r':
+ if (!crlf) {
+ NET2ADD('\r', '\0');
+ } else {
+ NET2ADD('\r', '\n');
+ }
+ bol = flushline = 1;
+ break;
+ case IAC:
+ NET2ADD(IAC, IAC);
+ break;
+ default:
+ NETADD(c);
+ break;
+ }
+ } else if (c == IAC) {
+ NET2ADD(IAC, IAC);
+ } else {
+ NETADD(c);
+ }
+ }
+ if (count)
+ ring_consumed(&ttyiring, count);
+ return (returnValue||count); /* Non-zero if we did anything */
+}
+
+/*
+ * Scheduler()
+ *
+ * Try to do something.
+ *
+ * If we do something useful, return 1; else return 0.
+ *
+ */
+
+
+int
+Scheduler(block)
+ int block; /* should we block in the select ? */
+{
+ /*
+ * One wants to be a bit careful about setting returnValue
+ * to one, since a one implies we did some useful work,
+ * and therefore probably won't be called to block next
+ * time (TN3270 mode only).
+ */
+ int returnValue;
+ int netin, netout, netex, ttyin, ttyout;
+
+ /* Decide which rings should be processed */
+
+ netout = ring_full_count(&netoring) &&
+ (flushline ||
+ (my_want_state_is_wont(TELOPT_LINEMODE)
+#ifdef KLUDGELINEMODE
+ /* X */ && (!kludgelinemode || my_want_state_is_do(TELOPT_SGA))
+#endif
+ /* XXX */) ||
+ my_want_state_is_will(TELOPT_BINARY));
+ ttyout = ring_full_count(&ttyoring);
+
+ ttyin = (ring_empty_count(&ttyiring) && !eof_pending);
+
+ netin = !ISend && ring_empty_count(&netiring);
+
+ netex = !SYNCHing;
+
+ if (scheduler_lockout_tty) {
+ ttyin = ttyout = 0;
+ }
+
+ /* Call to system code to process rings */
+
+ returnValue = process_rings(netin, netout, netex, ttyin, ttyout,
+ !block);
+
+ /* Now, look at the input rings, looking for work to do. */
+
+ if (ring_full_count(&ttyiring)) {
+ returnValue |= telsnd();
+ } else {
+ /*
+ * If ttyiring is empty, check to see if there is a real EOF
+ * pending. If so, we can maybe do the EOF write now.
+ */
+ if (eof_pending) {
+ eof_pending = 0;
+ sendeof();
+ }
+ }
+
+ if (ring_full_count(&netiring)) {
+ returnValue |= telrcv();
+ }
+ return (returnValue);
+}
+
+/*
+ * Select from tty and network...
+ */
+void
+telnet(user)
+ char *user;
+{
+ sys_telnet_init();
+
+ {
+ static char local_host[MAXHOSTNAMELEN] = { 0 };
+
+ if (!local_host[0]) {
+ (void) gethostname(local_host, sizeof (local_host));
+ local_host[sizeof (local_host)-1] = 0;
+ }
+ auth_encrypt_init(local_host, hostname, "TELNET");
+ auth_encrypt_user(user);
+ }
+
+ if (autologin)
+ send_will(TELOPT_AUTHENTICATION, 1);
+
+ if (telnetport || wantencryption) {
+ send_do(TELOPT_ENCRYPT, 1);
+ send_will(TELOPT_ENCRYPT, 1);
+ }
+
+ if (telnetport) {
+ if (!reqd_linemode)
+ send_do(TELOPT_SGA, 1);
+ send_will(TELOPT_TTYPE, 1);
+ send_will(TELOPT_NAWS, 1);
+ send_will(TELOPT_TSPEED, 1);
+ send_will(TELOPT_LFLOW, 1);
+ if (!reqd_linemode)
+ send_will(TELOPT_LINEMODE, 1);
+ send_will(TELOPT_NEW_ENVIRON, 1);
+ send_do(TELOPT_STATUS, 1);
+ if (env_getvalue((unsigned char *)"DISPLAY"))
+ send_will(TELOPT_XDISPLOC, 1);
+ if (eight)
+ tel_enter_binary(eight);
+ }
+
+ /*
+ * Note: we assume a tie to the authentication option here. This
+ * is necessary so that authentication fails, we don't spin
+ * forever.
+ */
+ if (wantencryption) {
+ boolean_t printed_encrypt = B_FALSE;
+ extern boolean_t auth_has_failed;
+ time_t timeout = time(0) + 60;
+
+ send_do(TELOPT_ENCRYPT, 1);
+ send_will(TELOPT_ENCRYPT, 1);
+ for (;;) {
+ if (my_want_state_is_wont(TELOPT_AUTHENTICATION)) {
+ (void) printf(gettext(
+ "\nServer refused to negotiate "
+ "authentication, which is required\n"
+ "for encryption. Good-bye.\n\r"));
+ Exit(EXIT_FAILURE);
+ }
+ if (auth_has_failed) {
+ (void) printf(gettext(
+ "\nAuthentication negotation has failed, "
+ "which is required for\n"
+ "encryption. Good-bye.\n\r"));
+ Exit(EXIT_FAILURE);
+ }
+ if (my_want_state_is_dont(TELOPT_ENCRYPT) ||
+ my_want_state_is_wont(TELOPT_ENCRYPT)) {
+ (void) printf(gettext(
+ "\nServer refused to negotiate encryption. "
+ "Good-bye.\n\r"));
+ Exit(EXIT_FAILURE);
+ }
+ if (encrypt_is_encrypting())
+ break;
+
+ if (time(0) > timeout) {
+ (void) printf(gettext(
+ "\nEncryption could not be enabled. "
+ "Good-bye.\n\r"));
+ Exit(EXIT_FAILURE);
+ }
+ if (printed_encrypt == B_FALSE) {
+ printed_encrypt = B_TRUE;
+ (void) printf(gettext(
+ "Waiting for encryption to be negotiated...\n"));
+ /*
+ * Turn on MODE_TRAPSIG and then turn off localchars
+ * so that ^C will cause telnet to exit.
+ */
+ TerminalNewMode(getconnmode()|MODE_TRAPSIG);
+ intr_waiting = 1;
+ }
+ if (intr_happened) {
+ (void) printf(gettext(
+ "\nUser requested an interrupt. Good-bye.\n\r"));
+ Exit(EXIT_FAILURE);
+ }
+ telnet_spin();
+ }
+ if (printed_encrypt) {
+ (void) printf(gettext("done.\n"));
+ intr_waiting = 0;
+ setconnmode(0);
+ }
+ }
+
+ for (;;) {
+ int schedValue;
+
+ while ((schedValue = Scheduler(0)) != 0) {
+ if (schedValue == -1) {
+ setcommandmode();
+ return;
+ }
+ }
+
+ if (Scheduler(1) == -1) {
+ setcommandmode();
+ return;
+ }
+ }
+}
+
+#if 0 /* XXX - this not being in is a bug */
+/*
+ * nextitem()
+ *
+ * Return the address of the next "item" in the TELNET data
+ * stream. This will be the address of the next character if
+ * the current address is a user data character, or it will
+ * be the address of the character following the TELNET command
+ * if the current address is a TELNET IAC ("I Am a Command")
+ * character.
+ */
+
+static char *
+nextitem(current)
+ char *current;
+{
+ if ((*current&0xff) != IAC) {
+ return (current+1);
+ }
+ switch (*(current+1)&0xff) {
+ case DO:
+ case DONT:
+ case WILL:
+ case WONT:
+ return (current+3);
+ case SB: /* loop forever looking for the SE */
+ {
+ register char *look = current+2;
+
+ for (;;) {
+ if ((*look++&0xff) == IAC) {
+ if ((*look++&0xff) == SE) {
+ return (look);
+ }
+ }
+ }
+ }
+ default:
+ return (current+2);
+ }
+}
+#endif /* 0 */
+
+/*
+ * netclear()
+ *
+ * We are about to do a TELNET SYNCH operation. Clear
+ * the path to the network.
+ *
+ * Things are a bit tricky since we may have sent the first
+ * byte or so of a previous TELNET command into the network.
+ * So, we have to scan the network buffer from the beginning
+ * until we are up to where we want to be.
+ *
+ * A side effect of what we do, just to keep things
+ * simple, is to clear the urgent data pointer. The principal
+ * caller should be setting the urgent data pointer AFTER calling
+ * us in any case.
+ */
+
+static void
+netclear()
+{
+#if 0 /* XXX */
+ register char *thisitem, *next;
+ char *good;
+#define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \
+ ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
+
+ thisitem = netobuf;
+
+ while ((next = nextitem(thisitem)) <= netobuf.send) {
+ thisitem = next;
+ }
+
+ /* Now, thisitem is first before/at boundary. */
+
+ good = netobuf; /* where the good bytes go */
+
+ while (netoring.add > thisitem) {
+ if (wewant(thisitem)) {
+ int length;
+
+ next = thisitem;
+ do {
+ next = nextitem(next);
+ } while (wewant(next) && (nfrontp > next));
+ length = next-thisitem;
+ memcpy(good, thisitem, length);
+ good += length;
+ thisitem = next;
+ } else {
+ thisitem = nextitem(thisitem);
+ }
+ }
+
+#endif /* 0 */
+}
+
+/*
+ * These routines add various telnet commands to the data stream.
+ */
+
+/*
+ * doflush - Send do timing mark (for network connection flush) & then
+ * get rid of anything in the output buffer. Return -1 if there was a
+ * non-EWOULDBLOCK error on the tty flush, and otherwise return 0.
+ */
+static int
+doflush()
+{
+ NET2ADD(IAC, DO);
+ NETADD(TELOPT_TM);
+ flushline = 1;
+ flushout = 1;
+
+ /* Drop pending tty output */
+ if (ttyflush(1) == -2)
+ return (-1);
+
+ /* do printoption AFTER flush, otherwise the output gets tossed... */
+ printoption("SENT", DO, TELOPT_TM);
+ return (0);
+}
+
+int
+xmitAO()
+{
+ NET2ADD(IAC, AO);
+ printoption("SENT", IAC, AO);
+ if (autoflush) {
+ if (doflush() == -1)
+ return (-1);
+ }
+ return (0);
+}
+
+
+void
+xmitEL()
+{
+ NET2ADD(IAC, EL);
+ printoption("SENT", IAC, EL);
+}
+
+void
+xmitEC()
+{
+ NET2ADD(IAC, EC);
+ printoption("SENT", IAC, EC);
+}
+
+
+int
+dosynch()
+{
+ netclear(); /* clear the path to the network */
+ NETADD(IAC);
+ setneturg();
+ NETADD(DM);
+ printoption("SENT", IAC, DM);
+ return (1);
+}
+
+int want_status_response = 0;
+
+int
+get_status()
+{
+ unsigned char tmp[16];
+ register unsigned char *cp;
+
+ if (my_want_state_is_dont(TELOPT_STATUS)) {
+ (void) printf("Remote side does not support STATUS option\n");
+ return (0);
+ }
+ cp = tmp;
+
+ *cp++ = IAC;
+ *cp++ = SB;
+ *cp++ = TELOPT_STATUS;
+ *cp++ = TELQUAL_SEND;
+ *cp++ = IAC;
+ *cp++ = SE;
+ if (NETROOM() >= cp - tmp) {
+ ring_supply_data(&netoring, tmp, cp-tmp);
+ printsub('>', tmp+2, cp - tmp - 2);
+ }
+ ++want_status_response;
+ return (1);
+}
+
+void
+intp()
+{
+ NET2ADD(IAC, IP);
+ printoption("SENT", IAC, IP);
+ flushline = 1;
+ if (autoflush) {
+ /* Ignore return as we're ending off anyway. */
+ (void) doflush();
+ }
+ if (autosynch) {
+ (void) dosynch();
+ }
+}
+
+int
+sendbrk()
+{
+ NET2ADD(IAC, BREAK);
+ printoption("SENT", IAC, BREAK);
+ flushline = 1;
+ if (autoflush) {
+ if (doflush() == -1)
+ return (-1);
+ }
+ if (autosynch) {
+ (void) dosynch();
+ }
+ return (0);
+}
+
+void
+sendabort()
+{
+ NET2ADD(IAC, ABORT);
+ printoption("SENT", IAC, ABORT);
+ flushline = 1;
+ if (autoflush) {
+ /*
+ * Since sendabort() gets called while aborting,
+ * ignore the doflush() return
+ */
+ (void) doflush();
+ }
+ if (autosynch) {
+ (void) dosynch();
+ }
+}
+
+void
+sendsusp()
+{
+ NET2ADD(IAC, SUSP);
+ printoption("SENT", IAC, SUSP);
+ flushline = 1;
+ if (autoflush) {
+ if (doflush() == -1) {
+ /* The following will not return. */
+ fatal_tty_error("write");
+ }
+ }
+ if (autosynch) {
+ (void) dosynch();
+ }
+}
+
+static void
+sendeof()
+{
+ NET2ADD(IAC, xEOF);
+ printoption("SENT", IAC, xEOF);
+}
+
+/*
+ * Send a window size update to the remote system.
+ */
+
+void
+sendnaws()
+{
+ unsigned short rows, cols;
+ unsigned char tmp[16];
+ register unsigned char *cp;
+
+ if (my_state_is_wont(TELOPT_NAWS))
+ return;
+
+#define PUTSHORT(cp, x) { if ((*cp++ = ((x)>>8)&0xff) == IAC) *cp++ = IAC; \
+ if ((*cp++ = ((x))&0xff) == IAC) *cp++ = IAC; }
+
+ if (TerminalWindowSize(&rows, &cols) == 0) { /* Failed */
+ return;
+ }
+
+ cp = tmp;
+
+ *cp++ = IAC;
+ *cp++ = SB;
+ *cp++ = TELOPT_NAWS;
+ PUTSHORT(cp, cols);
+ PUTSHORT(cp, rows);
+ *cp++ = IAC;
+ *cp++ = SE;
+ if (NETROOM() >= cp - tmp) {
+ ring_supply_data(&netoring, tmp, cp-tmp);
+ printsub('>', tmp+2, cp - tmp - 2);
+ }
+}
+
+void
+tel_enter_binary(rw)
+ int rw;
+{
+ if (rw&1)
+ send_do(TELOPT_BINARY, 1);
+ if (rw&2)
+ send_will(TELOPT_BINARY, 1);
+}
+
+void
+tel_leave_binary(rw)
+ int rw;
+{
+ if (rw&1)
+ send_dont(TELOPT_BINARY, 1);
+ if (rw&2)
+ send_wont(TELOPT_BINARY, 1);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/telnet/terminal.c b/usr/src/cmd/cmd-inet/usr.bin/telnet/terminal.c
new file mode 100644
index 0000000000..3fd2b029f1
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/telnet/terminal.c
@@ -0,0 +1,229 @@
+/*
+ * Copyright 1994-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * usr/src/cmd/cmd-inet/usr.bin/telnet/terminal.c
+ */
+
+/*
+ * Copyright (c) 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)terminal.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <arpa/telnet.h>
+#include <sys/types.h>
+#include <errno.h>
+
+#include "ring.h"
+
+#include "externs.h"
+#include "types.h"
+
+Ring ttyoring;
+Ring ttyiring;
+static unsigned char ttyobuf[2*BUFSIZ];
+static unsigned char ttyibuf[BUFSIZ];
+
+int termdata; /* Debugging flag */
+
+#ifdef USE_TERMIO
+cc_t termAytChar;
+#else
+cc_t termForw2Char;
+cc_t termAytChar;
+#endif
+
+/*
+ * initialize the terminal data structures.
+ */
+
+ void
+init_terminal()
+{
+ if (ring_init(&ttyoring, ttyobuf, sizeof (ttyobuf)) != 1) {
+ exit(1);
+ }
+ if (ring_init(&ttyiring, ttyibuf, sizeof (ttyibuf)) != 1) {
+ exit(1);
+ }
+ autoflush = 1;
+}
+
+
+/*
+ * Send as much data as possible to the terminal.
+ *
+ * Return value:
+ * -2: Error occurred other than EWOULDBLOCK; see
+ * 'errno' for specifics
+ * -1: No useful work done, although data was waiting.
+ * This may be due to EWOULDBLOCK error.
+ * 0: No data was waiting, so nothing was done.
+ * 1: All waiting data was written out.
+ * n: Part of data was written. 'n' is the number
+ * of bytes remaining which were not written.
+ */
+
+int
+ttyflush(drop)
+ int drop;
+{
+ register int n, n0, n1;
+
+ n0 = ring_full_count(&ttyoring);
+ if ((n1 = n = ring_full_consecutive(&ttyoring)) > 0) {
+ if (drop) {
+ TerminalFlushOutput();
+ /* we leave 'n' alone! */
+ } else {
+ n = TerminalWrite((char *)ttyoring.consume, n);
+ if (n == -1 && errno != EWOULDBLOCK)
+ return (-2);
+ }
+ }
+ if (n > 0) {
+ if (termdata && n) {
+ Dump('>', ttyoring.consume, n);
+ }
+ /*
+ * If we wrote everything, and the full count is
+ * larger than what we wrote, then write the
+ * rest of the buffer.
+ */
+ if (n1 == n && n0 > n) {
+ n1 = n0 - n;
+ if (!drop) {
+ n1 = TerminalWrite((char *)ttyoring.bottom, n1);
+ if (n1 == -1) {
+ if (errno != EWOULDBLOCK)
+ return (-2);
+ n1 = 0;
+ }
+ }
+ n += n1;
+ }
+ ring_consumed(&ttyoring, n);
+ }
+ if (n < 0)
+ return (-1);
+
+ if (n == n0) {
+ if (n0)
+ return (-1);
+ return (0);
+ }
+ return (n0 - n + 1);
+}
+
+
+/*
+ * These routines decides on what the mode should be (based on the values
+ * of various global variables).
+ */
+
+
+int
+getconnmode()
+{
+ extern int linemode;
+ int mode = 0;
+#ifdef KLUDGELINEMODE
+ extern int kludgelinemode;
+#endif
+
+ if (my_want_state_is_dont(TELOPT_ECHO))
+ mode |= MODE_ECHO;
+
+ if (localflow)
+ mode |= MODE_FLOW;
+
+ if (my_want_state_is_will(TELOPT_BINARY))
+ mode |= MODE_INBIN;
+
+ if (my_want_state_is_do(TELOPT_BINARY))
+ mode |= MODE_OUTBIN;
+
+#ifdef KLUDGELINEMODE
+ if (kludgelinemode) {
+ if (my_want_state_is_dont(TELOPT_SGA)) {
+ mode |= (MODE_TRAPSIG|MODE_EDIT);
+ if (dontlecho &&
+ (clocks.echotoggle > clocks.modenegotiated)) {
+ mode &= ~MODE_ECHO;
+ }
+ }
+ return (mode);
+ }
+#endif
+ if (my_want_state_is_will(TELOPT_LINEMODE))
+ mode |= linemode;
+ return (mode);
+}
+
+void
+setconnmode(force)
+ int force;
+{
+ static int enc_passwd = 0;
+ register int newmode;
+
+ newmode = getconnmode()|(force?MODE_FORCE:0);
+
+ TerminalNewMode(newmode);
+
+ if ((newmode & (MODE_ECHO|MODE_EDIT)) == MODE_EDIT) {
+ if (my_want_state_is_will(TELOPT_ENCRYPT) &&
+ (enc_passwd == 0) && !encrypt_output) {
+ encrypt_request_start(0, 0);
+ enc_passwd = 1;
+ }
+ } else {
+ if (enc_passwd) {
+ encrypt_request_end();
+ enc_passwd = 0;
+ }
+ }
+}
+
+
+ void
+setcommandmode()
+{
+ TerminalNewMode(-1);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/telnet/types.h b/usr/src/cmd/cmd-inet/usr.bin/telnet/types.h
new file mode 100644
index 0000000000..3b5ee85e29
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/telnet/types.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 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.
+ *
+ * @(#)types.h 8.1 (Berkeley) 6/6/93
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _TYPES_H
+#define _TYPES_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+ char *modedescriptions;
+ char modetype;
+} Modelist;
+
+extern Modelist modelist[];
+
+typedef struct {
+ int system; /* what the current time is */
+ int echotoggle; /* last time user entered echo character */
+ int modenegotiated; /* last time operating mode negotiated */
+ int didnetreceive; /* last time we read data from network */
+ int gotDM; /* when did we last see a data mark */
+} Clocks;
+
+extern Clocks clocks;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TYPES_H */
diff --git a/usr/src/cmd/cmd-inet/usr.bin/telnet/utilities.c b/usr/src/cmd/cmd-inet/usr.bin/telnet/utilities.c
new file mode 100644
index 0000000000..4c805cd759
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/telnet/utilities.c
@@ -0,0 +1,1258 @@
+/*
+ * Copyright 1994-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * usr/src/cmd/cmd-inet/usr.bin/telnet/utilities.c
+ */
+
+/*
+ * Copyright (c) 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)utilities.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#define TELOPTS
+#ifdef lint
+static char *telcmds[] = {0};
+static char *slc_names[] = {0};
+static char *encrypt_names[] = {0};
+static char *enctype_names[] = {0};
+#else /* lint */
+#define TELCMDS
+#define SLC_NAMES
+#endif /* lint */
+#include <arpa/telnet.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <errno.h>
+
+#include <ctype.h>
+
+#include "general.h"
+
+#include "ring.h"
+
+#include "defines.h"
+
+#include "externs.h"
+
+FILE *NetTrace = 0; /* Not in bss, since needs to stay */
+int prettydump;
+
+/*
+ * upcase()
+ *
+ * Upcase (in place) the argument.
+ */
+
+ void
+upcase(argument)
+ register char *argument;
+{
+ register int c;
+
+ while ((c = *argument) != 0) {
+ if (islower(c)) {
+ *argument = toupper(c);
+ }
+ argument++;
+ }
+}
+
+/*
+ * SetSockOpt()
+ *
+ * Compensate for differences in 4.2 and 4.3 systems.
+ */
+
+ int
+SetSockOpt(fd, level, option, yesno)
+ int fd, level, option, yesno;
+{
+ return (setsockopt(fd, level, option, &yesno, sizeof (yesno)));
+}
+
+/*
+ * The following are routines used to print out debugging information.
+ */
+
+unsigned char NetTraceFile[MAXPATHLEN] = "(standard output)";
+
+ void
+SetNetTrace(file)
+ register char *file;
+{
+ if (NetTrace && NetTrace != stdout)
+ (void) fclose(NetTrace);
+ if (file && (strcmp(file, "-") != 0)) {
+ NetTrace = fopen(file, "w");
+ if (NetTrace) {
+ (void) strcpy((char *)NetTraceFile, file);
+ return;
+ }
+ (void) fprintf(stderr, "Cannot open %s.\n", file);
+ }
+ NetTrace = stdout;
+ (void) strcpy((char *)NetTraceFile, "(standard output)");
+}
+
+ void
+Dump(direction, buffer, length)
+ char direction;
+ unsigned char *buffer;
+ int length;
+{
+#define BYTES_PER_LINE 32
+#define min(x, y) ((x < y) ? x:y)
+ unsigned char *pThis;
+ int offset;
+
+ offset = 0;
+
+ while (length) {
+ /* print one line */
+ (void) fprintf(NetTrace, "%c 0x%x\t", direction, offset);
+ pThis = buffer;
+ if (prettydump) {
+ buffer = buffer + min(length, BYTES_PER_LINE/2);
+ while (pThis < buffer) {
+ (void) fprintf(NetTrace, "%c%.2x",
+ (((*pThis)&0xff) == 0xff) ? '*' : ' ',
+ (*pThis)&0xff);
+ pThis++;
+ }
+ length -= BYTES_PER_LINE/2;
+ offset += BYTES_PER_LINE/2;
+ } else {
+ buffer = buffer + min(length, BYTES_PER_LINE);
+ while (pThis < buffer) {
+ (void) fprintf(NetTrace, "%.2x", (*pThis)&0xff);
+ pThis++;
+ }
+ length -= BYTES_PER_LINE;
+ offset += BYTES_PER_LINE;
+ }
+ if (NetTrace == stdout) {
+ (void) fprintf(NetTrace, "\r\n");
+ } else {
+ (void) fprintf(NetTrace, "\n");
+ }
+ if (length < 0) {
+ (void) fflush(NetTrace);
+ return;
+ }
+ /* find next unique line */
+ }
+ (void) fflush(NetTrace);
+}
+
+
+ void
+printoption(direction, cmd, option)
+ char *direction;
+ int cmd, option;
+{
+ if (!showoptions)
+ return;
+ if (cmd == IAC) {
+ if (TELCMD_OK(option))
+ (void) fprintf(NetTrace, "%s IAC %s", direction,
+ TELCMD(option));
+ else
+ (void) fprintf(NetTrace, "%s IAC %d", direction,
+ option);
+ } else {
+ register char *fmt;
+ fmt = (cmd == WILL) ? "WILL" : (cmd == WONT) ? "WONT" :
+ (cmd == DO) ? "DO" : (cmd == DONT) ? "DONT" : 0;
+ if (fmt) {
+ (void) fprintf(NetTrace, "%s %s ", direction, fmt);
+ if (TELOPT_OK(option))
+ (void) fprintf(NetTrace, "%s", TELOPT(option));
+ else if (option == TELOPT_EXOPL)
+ (void) fprintf(NetTrace, "EXOPL");
+ else
+ (void) fprintf(NetTrace, "%d", option);
+ } else
+ (void) fprintf(NetTrace, "%s %d %d", direction, cmd,
+ option);
+ }
+ if (NetTrace == stdout) {
+ (void) fprintf(NetTrace, "\r\n");
+ (void) fflush(NetTrace);
+ } else {
+ (void) fprintf(NetTrace, "\n");
+ }
+}
+
+ void
+optionstatus()
+{
+ register int i;
+ extern char will_wont_resp[], do_dont_resp[];
+
+ for (i = 0; i < SUBBUFSIZE; i++) {
+ if (do_dont_resp[i]) {
+ if (TELOPT_OK(i))
+ (void) printf("resp DO_DONT %s: %d\n",
+ TELOPT(i), do_dont_resp[i]);
+ else if (TELCMD_OK(i))
+ (void) printf("resp DO_DONT %s: %d\n",
+ TELCMD(i), do_dont_resp[i]);
+ else
+ (void) printf("resp DO_DONT %d: %d\n", i,
+ do_dont_resp[i]);
+ if (my_want_state_is_do(i)) {
+ if (TELOPT_OK(i))
+ (void) printf("want DO %s\n",
+ TELOPT(i));
+ else if (TELCMD_OK(i))
+ (void) printf("want DO %s\n",
+ TELCMD(i));
+ else
+ (void) printf("want DO %d\n", i);
+ } else {
+ if (TELOPT_OK(i))
+ (void) printf("want DONT %s\n",
+ TELOPT(i));
+ else if (TELCMD_OK(i))
+ (void) printf("want DONT %s\n",
+ TELCMD(i));
+ else
+ (void) printf("want DONT %d\n", i);
+ }
+ } else {
+ if (my_state_is_do(i)) {
+ if (TELOPT_OK(i))
+ (void) printf(" DO %s\n",
+ TELOPT(i));
+ else if (TELCMD_OK(i))
+ (void) printf(" DO %s\n",
+ TELCMD(i));
+ else
+ (void) printf(" DO %d\n", i);
+ }
+ }
+ if (will_wont_resp[i]) {
+ if (TELOPT_OK(i))
+ (void) printf("resp WILL_WONT %s: %d\n",
+ TELOPT(i), will_wont_resp[i]);
+ else if (TELCMD_OK(i))
+ (void) printf("resp WILL_WONT %s: %d\n",
+ TELCMD(i), will_wont_resp[i]);
+ else
+ (void) printf("resp WILL_WONT %d: %d\n",
+ i, will_wont_resp[i]);
+ if (my_want_state_is_will(i)) {
+ if (TELOPT_OK(i))
+ (void) printf("want WILL %s\n",
+ TELOPT(i));
+ else if (TELCMD_OK(i))
+ (void) printf("want WILL %s\n",
+ TELCMD(i));
+ else
+ (void) printf("want WILL %d\n", i);
+ } else {
+ if (TELOPT_OK(i))
+ (void) printf("want WONT %s\n",
+ TELOPT(i));
+ else if (TELCMD_OK(i))
+ (void) printf("want WONT %s\n",
+ TELCMD(i));
+ else
+ (void) printf("want WONT %d\n", i);
+ }
+ } else {
+ if (my_state_is_will(i)) {
+ if (TELOPT_OK(i))
+ (void) printf(" WILL %s\n",
+ TELOPT(i));
+ else if (TELCMD_OK(i))
+ (void) printf(" WILL %s\n",
+ TELCMD(i));
+ else
+ (void) printf(" WILL %d\n", i);
+ }
+ }
+ }
+
+}
+
+ void
+printsub(direction, pointer, length)
+ char direction; /* '<' or '>' */
+ unsigned char *pointer; /* where suboption data sits */
+ int length; /* length of suboption data */
+{
+ register int i;
+ char buf[512];
+ extern int want_status_response;
+
+ if (showoptions || direction == 0 ||
+ (want_status_response && (pointer[0] == TELOPT_STATUS))) {
+ if (direction) {
+ (void) fprintf(NetTrace, "%s IAC SB ",
+ (direction == '<')? "RCVD":"SENT");
+ if (length >= 3) {
+ register int j;
+
+ i = pointer[length-2];
+ j = pointer[length-1];
+
+ if (i != IAC || j != SE) {
+ (void) fprintf(NetTrace,
+ "(terminated by ");
+ if (TELOPT_OK(i))
+ (void) fprintf(NetTrace, "%s ",
+ TELOPT(i));
+ else if (TELCMD_OK(i))
+ (void) fprintf(NetTrace, "%s ",
+ TELCMD(i));
+ else
+ (void) fprintf(NetTrace, "%d ",
+ i);
+ if (TELOPT_OK(j))
+ (void) fprintf(NetTrace, "%s",
+ TELOPT(j));
+ else if (TELCMD_OK(j))
+ (void) fprintf(NetTrace, "%s",
+ TELCMD(j));
+ else
+ (void) fprintf(NetTrace, "%d",
+ j);
+ (void) fprintf(NetTrace,
+ ", not IAC SE!) ");
+ }
+ }
+ length -= 2;
+ }
+ if (length < 1) {
+ (void) fprintf(NetTrace, "(Empty suboption??\?)");
+ if (NetTrace == stdout)
+ (void) fflush(NetTrace);
+ return;
+ }
+ switch (pointer[0]) {
+ case TELOPT_TTYPE:
+ (void) fprintf(NetTrace, "TERMINAL-TYPE ");
+ switch (pointer[1]) {
+ case TELQUAL_IS:
+ (void) fprintf(NetTrace, "IS \"%.*s\"",
+ length-2,
+ (char *)pointer+2);
+ break;
+ case TELQUAL_SEND:
+ (void) fprintf(NetTrace, "SEND");
+ break;
+ default:
+ (void) fprintf(NetTrace,
+ "- unknown qualifier %d (0x%x).",
+ pointer[1], pointer[1]);
+ }
+ break;
+ case TELOPT_TSPEED:
+ (void) fprintf(NetTrace, "TERMINAL-SPEED");
+ if (length < 2) {
+ (void) fprintf(NetTrace,
+ " (empty suboption??\?)");
+ break;
+ }
+ switch (pointer[1]) {
+ case TELQUAL_IS:
+ (void) fprintf(NetTrace, " IS ");
+ (void) fprintf(NetTrace, "%.*s", length-2,
+ (char *)pointer+2);
+ break;
+ default:
+ if (pointer[1] == 1)
+ (void) fprintf(NetTrace, " SEND");
+ else
+ (void) fprintf(NetTrace,
+ " %d (unknown)", pointer[1]);
+ for (i = 2; i < length; i++)
+ (void) fprintf(NetTrace, " ?%d?",
+ pointer[i]);
+ break;
+ }
+ break;
+
+ case TELOPT_LFLOW:
+ (void) fprintf(NetTrace, "TOGGLE-FLOW-CONTROL");
+ if (length < 2) {
+ (void) fprintf(NetTrace,
+ " (empty suboption??\?)");
+ break;
+ }
+ switch (pointer[1]) {
+ case LFLOW_OFF:
+ (void) fprintf(NetTrace, " OFF");
+ break;
+ case LFLOW_ON:
+ (void) fprintf(NetTrace, " ON");
+ break;
+ case LFLOW_RESTART_ANY:
+ (void) fprintf(NetTrace, " RESTART-ANY");
+ break;
+ case LFLOW_RESTART_XON:
+ (void) fprintf(NetTrace, " RESTART-XON");
+ break;
+ default:
+ (void) fprintf(NetTrace, " %d (unknown)",
+ pointer[1]);
+ }
+ for (i = 2; i < length; i++)
+ (void) fprintf(NetTrace, " ?%d?",
+ pointer[i]);
+ break;
+
+ case TELOPT_NAWS:
+ (void) fprintf(NetTrace, "NAWS");
+ if (length < 2) {
+ (void) fprintf(NetTrace,
+ " (empty suboption??\?)");
+ break;
+ }
+ if (length == 2) {
+ (void) fprintf(NetTrace, " ?%d?", pointer[1]);
+ break;
+ }
+ (void) fprintf(NetTrace, " %d %d (%d)",
+ pointer[1], pointer[2],
+ (int)((((unsigned int)pointer[1])<<8)|
+ ((unsigned int)pointer[2])));
+ if (length == 4) {
+ (void) fprintf(NetTrace, " ?%d?", pointer[3]);
+ break;
+ }
+ (void) fprintf(NetTrace, " %d %d (%d)",
+ pointer[3], pointer[4],
+ (int)((((unsigned int)pointer[3])<<8)|
+ ((unsigned int)pointer[4])));
+ for (i = 5; i < length; i++)
+ (void) fprintf(NetTrace, " ?%d?", pointer[i]);
+ break;
+
+ case TELOPT_AUTHENTICATION:
+ (void) fprintf(NetTrace, "AUTHENTICATION");
+ if (length < 2) {
+ (void) fprintf(NetTrace,
+ " (empty suboption??\?)");
+ break;
+ }
+ switch (pointer[1]) {
+ case TELQUAL_REPLY:
+ case TELQUAL_IS:
+ (void) fprintf(NetTrace, " %s ",
+ (pointer[1] == TELQUAL_IS) ?
+ "IS" : "REPLY");
+ if (AUTHTYPE_NAME_OK(pointer[2]))
+ (void) fprintf(NetTrace, "%s ",
+ AUTHTYPE_NAME(pointer[2]));
+ else
+ (void) fprintf(NetTrace, "%d ",
+ pointer[2]);
+ if (length < 3) {
+ (void) fprintf(NetTrace,
+ "(partial suboption??\?)");
+ break;
+ }
+ (void) fprintf(NetTrace, "%s|%s",
+ ((pointer[3] & AUTH_WHO_MASK) ==
+ AUTH_WHO_CLIENT) ? "CLIENT" : "SERVER",
+ ((pointer[3] & AUTH_HOW_MASK) ==
+ AUTH_HOW_MUTUAL) ? "MUTUAL" : "ONE-WAY");
+
+ auth_printsub(&pointer[1], length - 1,
+ (uchar_t *)buf, sizeof (buf));
+ (void) fprintf(NetTrace, "%s", buf);
+ break;
+
+ case TELQUAL_SEND:
+ i = 2;
+ (void) fprintf(NetTrace, " SEND ");
+ while (i < length) {
+ if (AUTHTYPE_NAME_OK(pointer[i]))
+ (void) fprintf(NetTrace, "%s ",
+ AUTHTYPE_NAME(pointer[i]));
+ else
+ (void) fprintf(NetTrace, "%d ",
+ pointer[i]);
+ if (++i >= length) {
+ (void) fprintf(NetTrace,
+ "(partial "
+ "suboption??\?)");
+ break;
+ }
+ (void) fprintf(NetTrace, "%s|%s ",
+ ((pointer[i] & AUTH_WHO_MASK) ==
+ AUTH_WHO_CLIENT) ?
+ "CLIENT" : "SERVER",
+ ((pointer[i] & AUTH_HOW_MASK) ==
+ AUTH_HOW_MUTUAL) ?
+ "MUTUAL" : "ONE-WAY");
+ ++i;
+ }
+ break;
+
+ case TELQUAL_NAME:
+ i = 2;
+ (void) fprintf(NetTrace, " NAME \"");
+ while (i < length)
+ (void) putc(pointer[i++], NetTrace);
+ (void) putc('"', NetTrace);
+ break;
+
+ default:
+ for (i = 2; i < length; i++)
+ (void) fprintf(NetTrace, " ?%d?", pointer[i]);
+ break;
+ }
+ break;
+
+ case TELOPT_ENCRYPT:
+ (void) fprintf(NetTrace, "ENCRYPT");
+ if (length < 2) {
+ (void) fprintf(NetTrace,
+ " (empty suboption??\?)");
+ break;
+ }
+ switch (pointer[1]) {
+ case ENCRYPT_START:
+ (void) fprintf(NetTrace, " START");
+ break;
+
+ case ENCRYPT_END:
+ (void) fprintf(NetTrace, " END");
+ break;
+
+ case ENCRYPT_REQSTART:
+ (void) fprintf(NetTrace, " REQUEST-START");
+ break;
+
+ case ENCRYPT_REQEND:
+ (void) fprintf(NetTrace, " REQUEST-END");
+ break;
+
+ case ENCRYPT_IS:
+ case ENCRYPT_REPLY:
+ (void) fprintf(NetTrace, " %s ",
+ (pointer[1] == ENCRYPT_IS) ?
+ "IS" : "REPLY");
+ if (length < 3) {
+ (void) fprintf(NetTrace, " (partial "
+ "suboption??\?)");
+ break;
+ }
+ if (ENCTYPE_NAME_OK(pointer[2]))
+ (void) fprintf(NetTrace, "%s ",
+ ENCTYPE_NAME(pointer[2]));
+ else
+ (void) fprintf(NetTrace,
+ " %d (unknown)", pointer[2]);
+
+ encrypt_printsub(&pointer[1], length - 1,
+ (uchar_t *)buf, sizeof (buf));
+ (void) fprintf(NetTrace, "%s", buf);
+ break;
+
+ case ENCRYPT_SUPPORT:
+ i = 2;
+ (void) fprintf(NetTrace, " SUPPORT ");
+ while (i < length) {
+ if (ENCTYPE_NAME_OK(pointer[i]))
+ (void) fprintf(NetTrace, "%s ",
+ ENCTYPE_NAME(pointer[i]));
+ else
+ (void) fprintf(NetTrace, "%d ",
+ pointer[i]);
+ i++;
+ }
+ break;
+
+ case ENCRYPT_ENC_KEYID:
+ (void) fprintf(NetTrace, " ENC_KEYID ");
+ goto encommon;
+
+ case ENCRYPT_DEC_KEYID:
+ (void) fprintf(NetTrace, " DEC_KEYID ");
+ goto encommon;
+
+ default:
+ (void) fprintf(NetTrace, " %d (unknown)",
+ pointer[1]);
+ encommon:
+ for (i = 2; i < length; i++)
+ (void) fprintf(NetTrace, " %d",
+ pointer[i]);
+ break;
+ }
+ break;
+
+ case TELOPT_LINEMODE:
+ (void) fprintf(NetTrace, "LINEMODE ");
+ if (length < 2) {
+ (void) fprintf(NetTrace,
+ " (empty suboption??\?)");
+ break;
+ }
+ switch (pointer[1]) {
+ case WILL:
+ (void) fprintf(NetTrace, "WILL ");
+ goto common;
+ case WONT:
+ (void) fprintf(NetTrace, "WONT ");
+ goto common;
+ case DO:
+ (void) fprintf(NetTrace, "DO ");
+ goto common;
+ case DONT:
+ (void) fprintf(NetTrace, "DONT ");
+common:
+ if (length < 3) {
+ (void) fprintf(NetTrace,
+ "(no option??\?)");
+ break;
+ }
+ switch (pointer[2]) {
+ case LM_FORWARDMASK:
+ (void) fprintf(NetTrace,
+ "Forward Mask");
+ for (i = 3; i < length; i++)
+ (void) fprintf(NetTrace, " %x",
+ pointer[i]);
+ break;
+ default:
+ (void) fprintf(NetTrace, "%d (unknown)",
+ pointer[2]);
+ for (i = 3; i < length; i++)
+ (void) fprintf(NetTrace,
+ " %d", pointer[i]);
+ break;
+ }
+ break;
+
+ case LM_SLC:
+ (void) fprintf(NetTrace, "SLC");
+ for (i = 2; i < length - 2; i += 3) {
+ if (SLC_NAME_OK(pointer[i+SLC_FUNC]))
+ (void) fprintf(NetTrace, " %s",
+ SLC_NAME(pointer[
+ i+SLC_FUNC]));
+ else
+ (void) fprintf(NetTrace, " %d",
+ pointer[i+SLC_FUNC]);
+ switch (pointer[i+SLC_FLAGS] &
+ SLC_LEVELBITS) {
+ case SLC_NOSUPPORT:
+ (void) fprintf(NetTrace,
+ " NOSUPPORT");
+ break;
+ case SLC_CANTCHANGE:
+ (void) fprintf(NetTrace,
+ " CANTCHANGE");
+ break;
+ case SLC_VARIABLE:
+ (void) fprintf(NetTrace,
+ " VARIABLE");
+ break;
+ case SLC_DEFAULT:
+ (void) fprintf(NetTrace,
+ " DEFAULT");
+ break;
+ }
+ (void) fprintf(NetTrace, "%s%s%s",
+ pointer[i+SLC_FLAGS]&SLC_ACK ?
+ "|ACK" : "",
+ pointer[i+SLC_FLAGS]&SLC_FLUSHIN ?
+ "|FLUSHIN" : "",
+ pointer[i+SLC_FLAGS]&SLC_FLUSHOUT ?
+ "|FLUSHOUT" : "");
+ if (pointer[i+SLC_FLAGS] &
+ ~(SLC_ACK|SLC_FLUSHIN|
+ SLC_FLUSHOUT| SLC_LEVELBITS))
+ (void) fprintf(NetTrace, "(0x%x)",
+ pointer[i+SLC_FLAGS]);
+ (void) fprintf(NetTrace, " %d;",
+ pointer[i+SLC_VALUE]);
+ if ((pointer[i+SLC_VALUE] == IAC) &&
+ (pointer[i+SLC_VALUE+1] == IAC))
+ i++;
+ }
+ for (; i < length; i++)
+ (void) fprintf(NetTrace, " ?%d?",
+ pointer[i]);
+ break;
+
+ case LM_MODE:
+ (void) fprintf(NetTrace, "MODE ");
+ if (length < 3) {
+ (void) fprintf(NetTrace,
+ "(no mode??\?)");
+ break;
+ }
+ {
+ char tbuf[64];
+ (void) sprintf(tbuf, "%s%s%s%s%s",
+ pointer[2]&MODE_EDIT ? "|EDIT" : "",
+ pointer[2]&MODE_TRAPSIG ?
+ "|TRAPSIG" : "",
+ pointer[2]&MODE_SOFT_TAB ?
+ "|SOFT_TAB" : "",
+ pointer[2]&MODE_LIT_ECHO ?
+ "|LIT_ECHO" : "",
+ pointer[2]&MODE_ACK ? "|ACK" : "");
+ (void) fprintf(NetTrace, "%s", tbuf[1] ?
+ &tbuf[1] : "0");
+ }
+ if (pointer[2]&~(MODE_MASK))
+ (void) fprintf(NetTrace, " (0x%x)",
+ pointer[2]);
+ for (i = 3; i < length; i++)
+ (void) fprintf(NetTrace, " ?0x%x?",
+ pointer[i]);
+ break;
+ default:
+ (void) fprintf(NetTrace, "%d (unknown)",
+ pointer[1]);
+ for (i = 2; i < length; i++)
+ (void) fprintf(NetTrace, " %d",
+ pointer[i]);
+ }
+ break;
+
+ case TELOPT_STATUS: {
+ register char *cp;
+ register int j, k;
+
+ (void) fprintf(NetTrace, "STATUS");
+
+ switch (pointer[1]) {
+ default:
+ if (pointer[1] == TELQUAL_SEND)
+ (void) fprintf(NetTrace,
+ " SEND");
+ else
+ (void) fprintf(NetTrace,
+ " %d (unknown)",
+ pointer[1]);
+ for (i = 2; i < length; i++)
+ (void) fprintf(NetTrace, " ?%d?",
+ pointer[i]);
+ break;
+ case TELQUAL_IS:
+ if (--want_status_response < 0)
+ want_status_response = 0;
+ if (NetTrace == stdout)
+ (void) fprintf(NetTrace,
+ " IS\r\n");
+ else
+ (void) fprintf(NetTrace,
+ " IS\n");
+
+ for (i = 2; i < length; i++) {
+ switch (pointer[i]) {
+ case DO:
+ cp = "DO";
+ goto common2;
+ case DONT:
+ cp = "DONT";
+ goto common2;
+ case WILL:
+ cp = "WILL";
+ goto common2;
+ case WONT:
+ cp = "WONT";
+ goto common2;
+common2:
+ i++;
+ if (TELOPT_OK(
+ (int)pointer[i]))
+ (void) fprintf(
+ NetTrace,
+ " %s %s",
+ cp,
+ TELOPT(
+ pointer[
+ i]));
+ else
+ (void) fprintf(
+ NetTrace,
+ " %s %d",
+ cp,
+ pointer[i]);
+
+ if (NetTrace == stdout)
+ (void) fprintf(
+ NetTrace,
+ "\r\n");
+ else
+ (void) fprintf(
+ NetTrace,
+ "\n");
+ break;
+
+ case SB:
+ (void) fprintf(NetTrace,
+ " SB ");
+ i++;
+ j = k = i;
+ while (j < length) {
+ if (pointer[j] == SE) {
+ if (j+1 == length)
+ break;
+ if (pointer[j+1] == SE)
+ j++;
+ else
+ break;
+ }
+ pointer[k++] = pointer[j++];
+ }
+ printsub(0,
+ &pointer[i], k - i);
+ if (i < length) {
+ (void) fprintf(NetTrace, " SE");
+ i = j;
+ } else
+ i = j - 1;
+
+ if (NetTrace == stdout)
+ (void) fprintf(NetTrace, "\r\n");
+ else
+ (void) fprintf(NetTrace, "\n");
+
+ break;
+
+ default:
+ (void) fprintf(NetTrace,
+ " %d", pointer[i]);
+ break;
+ }
+ }
+ break;
+ }
+ break;
+ }
+
+ case TELOPT_XDISPLOC:
+ (void) fprintf(NetTrace, "X-DISPLAY-LOCATION ");
+ switch (pointer[1]) {
+ case TELQUAL_IS:
+ (void) fprintf(NetTrace, "IS \"%.*s\"",
+ length-2, (char *)pointer+2);
+ break;
+ case TELQUAL_SEND:
+ (void) fprintf(NetTrace, "SEND");
+ break;
+ default:
+ (void) fprintf(NetTrace,
+ "- unknown qualifier %d (0x%x).",
+ pointer[1], pointer[1]);
+ }
+ break;
+
+ case TELOPT_NEW_ENVIRON:
+ (void) fprintf(NetTrace, "NEW-ENVIRON ");
+#ifdef OLD_ENVIRON
+ goto env_common1;
+ case TELOPT_OLD_ENVIRON:
+ (void) fprintf(NetTrace, "OLD-ENVIRON ");
+ env_common1:
+#endif
+ switch (pointer[1]) {
+ case TELQUAL_IS:
+ (void) fprintf(NetTrace, "IS ");
+ goto env_common;
+ case TELQUAL_SEND:
+ (void) fprintf(NetTrace, "SEND ");
+ goto env_common;
+ case TELQUAL_INFO:
+ (void) fprintf(NetTrace, "INFO ");
+ env_common:
+ {
+ register int noquote = 2;
+#if defined(ENV_HACK) && defined(OLD_ENVIRON)
+ extern int old_env_var, old_env_value;
+#endif
+ for (i = 2; i < length; i++) {
+ switch (pointer[i]) {
+ case NEW_ENV_VALUE:
+#ifdef OLD_ENVIRON
+ /* case NEW_ENV_OVAR: */
+ if (pointer[0] == TELOPT_OLD_ENVIRON) {
+#ifdef ENV_HACK
+ if (old_env_var == OLD_ENV_VALUE)
+ (void) fprintf(NetTrace,
+ "\" (VALUE) " + noquote);
+ else
+#endif
+ (void) fprintf(NetTrace,
+ "\" VAR " + noquote);
+ } else
+#endif /* OLD_ENVIRON */
+ (void) fprintf(NetTrace, "\" VALUE " + noquote);
+ noquote = 2;
+ break;
+
+ case NEW_ENV_VAR:
+#ifdef OLD_ENVIRON
+ /* case OLD_ENV_VALUE: */
+ if (pointer[0] == TELOPT_OLD_ENVIRON) {
+#ifdef ENV_HACK
+ if (old_env_value == OLD_ENV_VAR)
+ (void) fprintf(NetTrace,
+ "\" (VAR) " + noquote);
+ else
+#endif
+ (void) fprintf(NetTrace,
+ "\" VALUE " + noquote);
+ } else
+#endif /* OLD_ENVIRON */
+ (void) fprintf(NetTrace, "\" VAR " + noquote);
+ noquote = 2;
+ break;
+
+ case ENV_ESC:
+ (void) fprintf(NetTrace, "\" ESC " + noquote);
+ noquote = 2;
+ break;
+
+ case ENV_USERVAR:
+ (void) fprintf(NetTrace, "\" USERVAR " + noquote);
+ noquote = 2;
+ break;
+
+ default:
+ def_case:
+ if (isprint(pointer[i]) && pointer[i] != '"') {
+ if (noquote) {
+ (void) putc('"', NetTrace);
+ noquote = 0;
+ }
+ (void) putc(pointer[i], NetTrace);
+ } else {
+ (void) fprintf(NetTrace, "\" %03o " + noquote,
+ pointer[i]);
+ noquote = 2;
+ }
+ break;
+ }
+ }
+ if (!noquote)
+ (void) putc('"', NetTrace);
+ break;
+ }
+ }
+ break;
+
+ default:
+ if (TELOPT_OK(pointer[0]))
+ (void) fprintf(NetTrace, "%s (unknown)", TELOPT(pointer[0]));
+ else
+ (void) fprintf(NetTrace, "%d (unknown)", pointer[0]);
+ for (i = 1; i < length; i++)
+ (void) fprintf(NetTrace, " %d", pointer[i]);
+ break;
+ }
+ if (direction) {
+ if (NetTrace == stdout)
+ (void) fprintf(NetTrace, "\r\n");
+ else
+ (void) fprintf(NetTrace, "\n");
+ }
+ if (NetTrace == stdout)
+ (void) fflush(NetTrace);
+ }
+}
+
+/*
+ * EmptyTerminal - called to make sure that the terminal buffer is empty.
+ * Note that we consider the buffer to run all the
+ * way to the kernel (thus the select).
+ */
+
+static void
+EmptyTerminal()
+{
+ fd_set o;
+
+ FD_ZERO(&o);
+
+ if (TTYBYTES() == 0) {
+ FD_SET(tout, &o);
+ /* wait for TTLOWAT */
+ (void) select(tout+1, NULL, &o, NULL, NULL);
+ } else {
+ while (TTYBYTES()) {
+ if (ttyflush(0) == -2) {
+ /* This will not return. */
+ fatal_tty_error("write");
+ }
+ FD_SET(tout, &o);
+ /* wait for TTLOWAT */
+ (void) select(tout+1, NULL, &o, NULL, NULL);
+ }
+ }
+}
+
+static void
+SetForExit()
+{
+ setconnmode(0);
+ do {
+ (void) telrcv(); /* Process any incoming data */
+ EmptyTerminal();
+ } while (ring_full_count(&netiring)); /* While there is any */
+ setcommandmode();
+ (void) fflush(stdout);
+ (void) fflush(stderr);
+ setconnmode(0);
+ EmptyTerminal(); /* Flush the path to the tty */
+ setcommandmode();
+}
+
+void
+Exit(returnCode)
+ int returnCode;
+{
+ SetForExit();
+ exit(returnCode);
+}
+
+void
+ExitString(string, returnCode)
+ char *string;
+ int returnCode;
+{
+ SetForExit();
+ (void) fwrite(string, 1, strlen(string), stderr);
+ exit(returnCode);
+}
+
+#define BUFFER_CHUNK_SIZE 64
+
+/* Round up to a multiple of BUFFER_CHUNK_SIZE */
+#define ROUND_CHUNK_SIZE(s) ((((s) + BUFFER_CHUNK_SIZE - 1) / \
+ BUFFER_CHUNK_SIZE) * BUFFER_CHUNK_SIZE)
+
+/*
+ * Optionally allocate a buffer, and optionally read a string from a stream
+ * into the buffer, starting at the given offset. If the buffer isn't
+ * large enough for the given offset, or if buffer space is exhausted
+ * when reading the string, the size of the buffer is increased.
+ *
+ * A buffer can be supplied when the function is called, passing the
+ * buffer address via the first argument. The buffer size can be
+ * passed as well, in the second argument. If the second argument is
+ * NULL, the function makes no assumptions about the buffer size.
+ * The address of the buffer is returned via the first argument, and the
+ * buffer size via the second argument if this is not NULL.
+ * These returned values may differ from the supplied values if the buffer
+ * was reallocated.
+ *
+ * If no buffer is to be supplied, specify a buffer address of NULL, via
+ * the first argument.
+ *
+ * If the pointer to the buffer address is NULL, the function just returns
+ * NULL, and performs no other processing.
+ *
+ * If a NULL stream is passed, the function will just make sure the
+ * supplied buffer is large enough to hold the supplied offset,
+ * reallocating it if is too small or too large.
+ *
+ * The returned buffer will be a multiple of BUFFER_CHUNK_SIZE in size.
+ *
+ * The function stops reading from the stream when a newline is read,
+ * end of file is reached, or an error occurs. The newline is not
+ * returned in the buffer. The returned string will be NULL terminated.
+ *
+ * The function returns the address of the buffer if any characters
+ * are read and no error occurred, otherwise it returns NULL.
+ *
+ * If the function returns NULL, a buffer may have been allocated. The
+ * buffer address will be returned via the first argument, together with
+ * the buffer size if the second argument is not NULL.
+ *
+ */
+static char *
+GetStringAtOffset(bufp, cbufsiz, off, st)
+ char **bufp;
+ unsigned int *cbufsiz;
+ unsigned int off;
+ FILE *st;
+{
+ unsigned int bufsiz;
+ char *buf;
+ char *nbuf;
+ unsigned int idx = off;
+
+ if (bufp == NULL)
+ return (NULL);
+
+ buf = *bufp;
+
+ bufsiz = ROUND_CHUNK_SIZE(off + 1);
+
+ if (buf == NULL || cbufsiz == NULL || *cbufsiz != bufsiz) {
+ if ((nbuf = realloc(buf, bufsiz)) == NULL)
+ return (NULL);
+
+ buf = nbuf;
+ *bufp = buf;
+ if (cbufsiz != NULL)
+ *cbufsiz = bufsiz;
+ }
+
+
+ if (st == NULL)
+ return (buf);
+
+ clearerr(st);
+ for (;;) {
+ int c = getc(st);
+
+ /* Expand the buffer as needed. */
+ if (idx == bufsiz) {
+ bufsiz += BUFFER_CHUNK_SIZE;
+ if ((nbuf = realloc(buf, bufsiz)) == NULL) {
+ /* Discard everything we read. */
+ buf[off] = 0;
+ buf = NULL;
+ break;
+ }
+ buf = nbuf;
+ *bufp = buf;
+ if (cbufsiz != NULL)
+ *cbufsiz = bufsiz;
+ }
+
+ if (c == EOF || c == '\n') {
+ buf[idx] = 0;
+ if (ferror(st) != 0) {
+ /* Retry if interrupted by a signal. */
+ if (errno == EINTR) {
+ clearerr(st);
+ continue;
+ }
+ buf = NULL;
+ } else if (feof(st) != 0) {
+ /* No characters transferred? */
+ if (off == idx)
+ buf = NULL;
+ }
+ break;
+ }
+ buf[idx++] = c;
+ }
+ return (buf);
+}
+
+/*
+ * Read a string from the supplied stream. Stop reading when a newline
+ * is read, end of file reached, or an error occurs.
+ *
+ * A buffer can be supplied by specifying the buffer address via the
+ * first argument. The buffer size can be passed via the second argument.
+ * If the second argument is NULL, the function makes no assumptions
+ * about the buffer size. The buffer will be reallocated if it is too
+ * small or too large for the returned string.
+ *
+ * If no buffer is to be supplied, specify a buffer address of NULL,
+ * via the first argument.
+ *
+ * If the first argument is NULL, the function just returns NULL, and
+ * performs no other processing.
+ *
+ * The function returns the address of the buffer if any characters are
+ * read and no error occurred.
+ *
+ * If the function returns NULL, a buffer may have been allocated. The
+ * buffer address and buffer size will be returned via the first argument,
+ * and the buffer size via the second argument, if this isn't NULL.
+ */
+char *
+GetString(bufp, bufsiz, st)
+ char **bufp;
+ unsigned int *bufsiz;
+ FILE *st;
+{
+ return (GetStringAtOffset(bufp, bufsiz, 0, st));
+}
+
+/*
+ * Allocate a buffer to hold a string of given length.
+ *
+ * An existing buffer can be reallocated by passing its address and via
+ * the first argument. The buffer size can be passed via the second
+ * argument. If the second argument is NULL, the function makes no
+ * assumptions about the buffer size.
+ *
+ * If no existing buffer is to be supplied, pass a NULL buffer address via
+ * the first argument.
+ *
+ * If the first argument is NULL, the function just returns NULL,
+ * and performs no other processing.
+ */
+char *
+AllocStringBuffer(bufp, bufsiz, size)
+ char **bufp;
+ unsigned int *bufsiz;
+ unsigned int size;
+{
+ return (GetStringAtOffset(bufp, bufsiz, size, (FILE *)NULL));
+}
+
+/*
+ * This function is similar to GetString(), except that the string read
+ * from the stream is appended to the supplied string.
+ */
+char *
+GetAndAppendString(bufp, bufsiz, str, st)
+ char **bufp;
+ unsigned int *bufsiz;
+ char *str;
+ FILE *st;
+{
+ unsigned int off = strlen(str);
+
+ if (GetStringAtOffset(bufp, bufsiz, off, st) == NULL)
+ return (NULL);
+
+ return (memcpy(*bufp, str, off));
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/tftp/Makefile b/usr/src/cmd/cmd-inet/usr.bin/tftp/Makefile
new file mode 100644
index 0000000000..cf900fa602
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/tftp/Makefile
@@ -0,0 +1,54 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 1989,2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+
+PROG= tftp
+OBJS= main.o tftp.o tftpsubs.o
+SRCS= $(OBJS:%.o=%.c)
+
+include ../../../Makefile.cmd
+include ../../Makefile.cmd-inet
+
+CPPFLAGS += -DSYSV -DSTRNET -DBSD_COMP -I$(CMDINETCOMMONDIR)
+LDLIBS += -lsocket -lnsl
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+ $(LINK.c) $(OBJS) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+install: all $(ROOTPROG)
+
+clean:
+ $(RM) $(OBJS)
+
+lint: lint_SRCS
+
+include ../../../Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/usr.bin/tftp/main.c b/usr/src/cmd/cmd-inet/usr.bin/tftp/main.c
new file mode 100644
index 0000000000..e10afbdd7c
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/tftp/main.c
@@ -0,0 +1,940 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * TFTP User Program -- Command Interface.
+ */
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <arpa/inet.h>
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <ctype.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <string.h>
+#include <limits.h>
+
+#include "tftpcommon.h"
+#include "tftpprivate.h"
+
+#define NELEM(a) (sizeof (a) / sizeof ((a)[0]))
+
+#define TIMEOUT 5 /* secs between rexmt's */
+
+struct sockaddr_in6 sin6;
+int f;
+int maxtimeout = 5 * TIMEOUT;
+int verbose;
+int trace;
+int srexmtval;
+int blksize;
+int rexmtval = TIMEOUT;
+int tsize_opt;
+jmp_buf toplevel;
+
+static int default_port, port;
+static int connected;
+static char mode[32];
+static char line[200];
+static char *prompt = "tftp";
+static char hostname[MAXHOSTNAMELEN];
+
+static void intr(int);
+static void quit(int, char **);
+static void help(int, char **);
+static void setverbose(int, char **);
+static void settrace(int, char **);
+static void status(int, char **);
+static void get(int, char **);
+static void put(int, char **);
+static void setpeer(int, char **);
+static void modecmd(int, char **);
+static void setrexmt(int, char **);
+static void settimeout(int, char **);
+static void setbinary(int, char **);
+static void setascii(int, char **);
+static void setblksize(int, char **);
+static void setsrexmt(int, char **);
+static void settsize(int, char **);
+static void setmode(char *);
+static void putusage(char *);
+static void getusage(char *);
+static char *finddelimiter(char *);
+static char *removebrackets(char *);
+static int prompt_for_arg(char *, int, char *);
+static struct cmd *getcmd(char *);
+static char *tail(char *);
+static void command(int);
+static void makeargv(int *, char ***);
+
+#define HELPINDENT (sizeof ("connect"))
+
+struct cmd {
+ char *name;
+ char *help;
+ void (*handler)(int, char **);
+};
+
+static char vhelp[] = "toggle verbose mode";
+static char thelp[] = "toggle packet tracing";
+static char chelp[] = "connect to remote tftp";
+static char qhelp[] = "exit tftp";
+static char hhelp[] = "print help information";
+static char shelp[] = "send file";
+static char rhelp[] = "receive file";
+static char mhelp[] = "set file transfer mode";
+static char sthelp[] = "show current status";
+static char xhelp[] = "set per-packet retransmission timeout";
+static char ihelp[] = "set total retransmission timeout";
+static char ashelp[] = "set mode to netascii";
+static char bnhelp[] = "set mode to octet";
+static char bshelp[] = "set transfer blocksize to negotiate with the "
+ "server";
+static char srhelp[] = "set preferred per-packet retransmission "
+ "timeout for server";
+static char tshelp[] = "toggle sending the transfer size option to "
+ "the server";
+
+static struct cmd cmdtab[] = {
+ { "connect", chelp, setpeer },
+ { "mode", mhelp, modecmd },
+ { "put", shelp, put },
+ { "get", rhelp, get },
+ { "quit", qhelp, quit },
+ { "verbose", vhelp, setverbose },
+ { "trace", thelp, settrace },
+ { "status", sthelp, status },
+ { "binary", bnhelp, setbinary },
+ { "ascii", ashelp, setascii },
+ { "rexmt", xhelp, setrexmt },
+ { "timeout", ihelp, settimeout },
+ { "blksize", bshelp, setblksize },
+ { "srexmt", srhelp, setsrexmt },
+ { "tsize", tshelp, settsize },
+ { "?", hhelp, help },
+ { NULL }
+};
+
+#define AMBIGCMD (&cmdtab[NELEM(cmdtab)])
+
+int
+main(int argc, char **argv)
+{
+ struct servent *sp;
+ struct sockaddr_in6 sin6;
+ int top;
+
+ sp = getservbyname("tftp", "udp");
+ default_port = (sp != NULL) ? sp->s_port : htons(IPPORT_TFTP);
+ port = default_port;
+
+ f = socket(AF_INET6, SOCK_DGRAM, 0);
+ if (f < 0) {
+ perror("tftp: socket");
+ exit(3);
+ }
+
+ (void) memset(&sin6, 0, sizeof (sin6));
+ sin6.sin6_family = AF_INET6;
+ if (bind(f, (struct sockaddr *)&sin6, sizeof (sin6)) < 0) {
+ perror("tftp: bind");
+ exit(1);
+ }
+
+ (void) strlcpy(mode, "netascii", sizeof (mode));
+ (void) signal(SIGINT, intr);
+ if (argc > 1) {
+ if (setjmp(toplevel) != 0)
+ exit(0);
+ setpeer(argc, argv);
+ }
+
+ top = (setjmp(toplevel) == 0);
+ for (;;)
+ command(top);
+
+ /*NOTREACHED*/
+ return (0);
+}
+
+/* Prompt for command argument, add to buffer with space separator */
+static int
+prompt_for_arg(char *buffer, int buffer_size, char *prompt)
+{
+ int ch;
+
+ if (strlcat(buffer, " ", buffer_size) >= buffer_size) {
+ (void) fputs("?Line too long\n", stderr);
+ return (-1);
+ }
+ (void) printf("(%s) ", prompt);
+ if (fgets(buffer + strlen(buffer), buffer_size - strlen(buffer),
+ stdin) == NULL) {
+ return (-1);
+ }
+ /* Flush what didn't fit in the buffer */
+ if (buffer[strlen(buffer)-1] != '\n') {
+ while (((ch = getchar()) != EOF) && (ch != '\n'))
+ ;
+ (void) fputs("?Line too long\n", stderr);
+ return (-1);
+ } else {
+ buffer[strlen(buffer)-1] = '\0';
+ }
+ return (0);
+}
+
+static void
+unknown_host(int error, char *hostname)
+{
+ if (error == TRY_AGAIN)
+ (void) fprintf(stderr, "%s: Unknown host (try again later).\n",
+ hostname);
+ else
+ (void) fprintf(stderr, "%s: Unknown host.\n", hostname);
+}
+
+static void
+setpeer(int argc, char **argv)
+{
+ struct hostent *host;
+ int error_num;
+ struct in6_addr ipv6addr;
+ struct in_addr ipv4addr;
+ char *hostnameinput;
+
+ if (argc < 2) {
+ if (prompt_for_arg(line, sizeof (line), "to") == -1)
+ return;
+ makeargv(&argc, &argv);
+ }
+ if (argc > 3 || argc < 2) {
+ (void) fprintf(stderr, "usage: %s host-name [port]\n",
+ argv[0]);
+ return;
+ }
+ hostnameinput = removebrackets(argv[1]);
+
+ (void) memset(&sin6, 0, sizeof (sin6));
+ sin6.sin6_family = AF_INET6;
+ if (host = getipnodebyname(hostnameinput, AF_INET6,
+ AI_ALL | AI_ADDRCONFIG | AI_V4MAPPED, &error_num)) {
+ (void) memcpy(&sin6.sin6_addr, host->h_addr_list[0],
+ host->h_length);
+ /*
+ * If host->h_name is a IPv4-mapped IPv6 literal, we'll convert
+ * it to IPv4 literal address.
+ */
+ if ((inet_pton(AF_INET6, host->h_name, &ipv6addr) > 0) &&
+ IN6_IS_ADDR_V4MAPPED(&ipv6addr)) {
+ IN6_V4MAPPED_TO_INADDR(&ipv6addr, &ipv4addr);
+ (void) inet_ntop(AF_INET, &ipv4addr, hostname,
+ sizeof (hostname));
+ } else {
+ (void) strlcpy(hostname, host->h_name,
+ sizeof (hostname));
+ }
+ freehostent(host);
+ } else {
+ /* Keeping with previous semantics */
+ connected = 0;
+ unknown_host(error_num, hostnameinput);
+ return;
+ }
+
+ port = default_port;
+ if (argc == 3) {
+ port = atoi(argv[2]);
+ if ((port < 1) || (port > 65535)) {
+ (void) fprintf(stderr, "%s: bad port number\n",
+ argv[2]);
+ connected = 0;
+ return;
+ }
+ port = htons(port);
+ }
+ connected = 1;
+}
+
+static struct modes {
+ char *m_name;
+ char *m_mode;
+} modes[] = {
+ { "ascii", "netascii" },
+ { "netascii", "netascii" },
+ { "binary", "octet" },
+ { "image", "octet" },
+ { "octet", "octet" },
+/* { "mail", "mail" }, */
+ { 0, 0 }
+};
+
+static void
+modecmd(int argc, char **argv)
+{
+ struct modes *p;
+
+ if (argc < 2) {
+ (void) fprintf(stderr, "Using %s mode to transfer files.\n",
+ mode);
+ return;
+ }
+ if (argc == 2) {
+ for (p = modes; p->m_name != NULL; p++)
+ if (strcmp(argv[1], p->m_name) == 0) {
+ setmode(p->m_mode);
+ return;
+ }
+ (void) fprintf(stderr, "%s: unknown mode\n", argv[1]);
+ /* drop through and print usage message */
+ }
+
+ p = modes;
+ (void) fprintf(stderr, "usage: %s [ %s", argv[0], p->m_name);
+ for (p++; p->m_name != NULL; p++)
+ (void) fprintf(stderr, " | %s", p->m_name);
+ (void) puts(" ]");
+}
+
+/*ARGSUSED*/
+static void
+setbinary(int argc, char **argv)
+{
+ setmode("octet");
+}
+
+/*ARGSUSED*/
+static void
+setascii(int argc, char **argv)
+{
+ setmode("netascii");
+}
+
+static void
+setmode(char *newmode)
+{
+ (void) strlcpy(mode, newmode, sizeof (mode));
+ if (verbose)
+ (void) printf("mode set to %s\n", mode);
+}
+
+/*
+ * Send file(s).
+ */
+static void
+put(int argc, char **argv)
+{
+ int fd;
+ int n;
+ char *cp, *targ;
+ struct in6_addr ipv6addr;
+ struct in_addr ipv4addr;
+ char buf[PATH_MAX + 1], *argtail;
+
+ if (argc < 2) {
+ if (prompt_for_arg(line, sizeof (line), "file") == -1)
+ return;
+ makeargv(&argc, &argv);
+ }
+ if (argc < 2) {
+ putusage(argv[0]);
+ return;
+ }
+ targ = argv[argc - 1];
+ if (finddelimiter(argv[argc - 1])) {
+ char *cp;
+ struct hostent *hp;
+ int error_num;
+
+ for (n = 1; n < argc - 1; n++)
+ if (finddelimiter(argv[n])) {
+ putusage(argv[0]);
+ return;
+ }
+ cp = argv[argc - 1];
+ targ = finddelimiter(cp);
+ *targ++ = 0;
+ cp = removebrackets(cp);
+
+ if ((hp = getipnodebyname(cp,
+ AF_INET6, AI_ALL | AI_ADDRCONFIG | AI_V4MAPPED,
+ &error_num)) == NULL) {
+ unknown_host(error_num, cp);
+ return;
+ }
+ (void) memcpy(&sin6.sin6_addr, hp->h_addr_list[0],
+ hp->h_length);
+
+ sin6.sin6_family = AF_INET6;
+ connected = 1;
+ /*
+ * If hp->h_name is a IPv4-mapped IPv6 literal, we'll convert
+ * it to IPv4 literal address.
+ */
+ if ((inet_pton(AF_INET6, hp->h_name, &ipv6addr) > 0) &&
+ IN6_IS_ADDR_V4MAPPED(&ipv6addr)) {
+ IN6_V4MAPPED_TO_INADDR(&ipv6addr, &ipv4addr);
+ (void) inet_ntop(AF_INET, &ipv4addr, hostname,
+ sizeof (hostname));
+ } else {
+ (void) strlcpy(hostname, hp->h_name,
+ sizeof (hostname));
+ }
+ }
+ if (!connected) {
+ (void) fputs("No target machine specified.\n", stderr);
+ return;
+ }
+ if (argc < 4) {
+ cp = argc == 2 ? tail(targ) : argv[1];
+ fd = open(cp, O_RDONLY);
+ if (fd < 0) {
+ (void) fprintf(stderr, "tftp: %s: %s\n", cp,
+ strerror(errno));
+ return;
+ }
+ if (verbose)
+ (void) printf("putting %s to %s:%s [%s]\n",
+ cp, hostname, targ, mode);
+ sin6.sin6_port = port;
+ tftp_sendfile(fd, targ, mode);
+ return;
+ }
+ /* this assumes the target is a directory */
+ /* on a remote unix system. hmmmm. */
+ if (strlen(targ) + 1 >= sizeof (buf)) {
+ (void) fprintf(stderr, "tftp: filename too long: %s\n", targ);
+ return;
+ }
+ for (n = 1; n < argc - 1; n++) {
+ argtail = tail(argv[n]);
+ if (snprintf(buf, sizeof (buf), "%s/%s", targ, argtail) >=
+ sizeof (buf)) {
+ (void) fprintf(stderr,
+ "tftp: filename too long: %s/%s\n", targ, argtail);
+ continue;
+ }
+ fd = open(argv[n], O_RDONLY);
+ if (fd < 0) {
+ (void) fprintf(stderr, "tftp: %s: %s\n", argv[n],
+ strerror(errno));
+ continue;
+ }
+ if (verbose)
+ (void) printf("putting %s to %s:%s [%s]\n",
+ argv[n], hostname, buf, mode);
+ sin6.sin6_port = port;
+ tftp_sendfile(fd, buf, mode);
+ }
+}
+
+static void
+putusage(char *s)
+{
+ (void) fprintf(stderr, "usage: %s file ... host:target, or\n"
+ " %s file ... target (when already connected)\n", s, s);
+}
+
+/*
+ * Receive file(s).
+ */
+static void
+get(int argc, char **argv)
+{
+ int fd;
+ int n;
+ char *cp;
+ char *src;
+ struct in6_addr ipv6addr;
+ struct in_addr ipv4addr;
+ int error_num;
+
+ if (argc < 2) {
+ if (prompt_for_arg(line, sizeof (line), "files") == -1)
+ return;
+ makeargv(&argc, &argv);
+ }
+ if (argc < 2) {
+ getusage(argv[0]);
+ return;
+ }
+ if (!connected) {
+ for (n = 1; n < argc; n++)
+ if (finddelimiter(argv[n]) == 0) {
+ getusage(argv[0]);
+ return;
+ }
+ }
+ for (n = 1; n < argc; n++) {
+ src = finddelimiter(argv[n]);
+ if (src == NULL)
+ src = argv[n];
+ else {
+ struct hostent *hp;
+ char *hostnameinput;
+
+ *src++ = 0;
+ hostnameinput = removebrackets(argv[n]);
+
+ if ((hp = getipnodebyname(hostnameinput, AF_INET6,
+ AI_ALL | AI_ADDRCONFIG | AI_V4MAPPED,
+ &error_num)) == NULL) {
+ unknown_host(error_num, hostnameinput);
+ continue;
+ }
+ (void) memcpy((caddr_t)&sin6.sin6_addr,
+ hp->h_addr_list[0], hp->h_length);
+
+ sin6.sin6_family = AF_INET6;
+ connected = 1;
+ /*
+ * If hp->h_name is a IPv4-mapped IPv6 literal, we'll
+ * convert it to IPv4 literal address.
+ */
+ if ((inet_pton(AF_INET6, hp->h_name, &ipv6addr) > 0) &&
+ IN6_IS_ADDR_V4MAPPED(&ipv6addr)) {
+ IN6_V4MAPPED_TO_INADDR(&ipv6addr, &ipv4addr);
+ (void) inet_ntop(AF_INET, &ipv4addr, hostname,
+ sizeof (hostname));
+ } else {
+ (void) strlcpy(hostname, hp->h_name,
+ sizeof (hostname));
+ }
+ }
+ if (argc < 4) {
+ cp = argc == 3 ? argv[2] : tail(src);
+ fd = creat(cp, 0644);
+ if (fd < 0) {
+ (void) fprintf(stderr, "tftp: %s: %s\n", cp,
+ strerror(errno));
+ return;
+ }
+ if (verbose)
+ (void) printf("getting from %s:%s to %s [%s]\n",
+ hostname, src, cp, mode);
+ sin6.sin6_port = port;
+ tftp_recvfile(fd, src, mode);
+ break;
+ }
+ cp = tail(src); /* new .. jdg */
+ fd = creat(cp, 0644);
+ if (fd < 0) {
+ (void) fprintf(stderr, "tftp: %s: %s\n", cp,
+ strerror(errno));
+ continue;
+ }
+ if (verbose)
+ (void) printf("getting from %s:%s to %s [%s]\n",
+ hostname, src, cp, mode);
+ sin6.sin6_port = port;
+ tftp_recvfile(fd, src, mode);
+ }
+}
+
+static void
+getusage(char *s)
+{
+ (void) fprintf(stderr, "usage: %s host:file host:file ... file, or\n"
+ " %s file file ... file if connected\n", s, s);
+}
+
+static void
+setrexmt(int argc, char **argv)
+{
+ int t;
+
+ if (argc < 2) {
+ if (prompt_for_arg(line, sizeof (line), "value") == -1)
+ return;
+ makeargv(&argc, &argv);
+ }
+ if (argc != 2) {
+ (void) fprintf(stderr, "usage: %s value\n", argv[0]);
+ return;
+ }
+ t = atoi(argv[1]);
+ if (t < 0)
+ (void) fprintf(stderr, "%s: bad value\n", argv[1]);
+ else
+ rexmtval = t;
+}
+
+static void
+settimeout(int argc, char **argv)
+{
+ int t;
+
+ if (argc < 2) {
+ if (prompt_for_arg(line, sizeof (line), "value") == -1)
+ return;
+ makeargv(&argc, &argv);
+ }
+ if (argc != 2) {
+ (void) fprintf(stderr, "usage: %s value\n", argv[0]);
+ return;
+ }
+ t = atoi(argv[1]);
+ if (t < 0)
+ (void) fprintf(stderr, "%s: bad value\n", argv[1]);
+ else
+ maxtimeout = t;
+}
+
+/*ARGSUSED*/
+static void
+status(int argc, char **argv)
+{
+ if (connected)
+ (void) printf("Connected to %s.\n", hostname);
+ else
+ (void) puts("Not connected.");
+ (void) printf("Mode: %s Verbose: %s Tracing: %s\n", mode,
+ verbose ? "on" : "off", trace ? "on" : "off");
+ (void) printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n",
+ rexmtval, maxtimeout);
+ (void) printf("Transfer blocksize option: ");
+ if (blksize == 0)
+ (void) puts("off");
+ else
+ (void) printf("%d bytes\n", blksize);
+ (void) printf("Server rexmt-interval option: ");
+ if (srexmtval == 0)
+ (void) puts("off");
+ else
+ (void) printf("%d seconds\n", srexmtval);
+ (void) printf("Transfer size option: %s\n", tsize_opt ? "on" : "off");
+}
+
+/*ARGSUSED*/
+static void
+intr(int signum)
+{
+ (void) cancel_alarm();
+ longjmp(toplevel, -1);
+}
+
+static char *
+tail(char *filename)
+{
+ char *s;
+
+ while (*filename != '\0') {
+ s = strrchr(filename, '/');
+ if (s == NULL)
+ break;
+ if (s[1] != '\0')
+ return (&s[1]);
+ *s = '\0';
+ }
+ return (filename);
+}
+
+/*
+ * Command parser.
+ */
+static void
+command(int top)
+{
+ struct cmd *c;
+ int ch;
+
+ if (!top)
+ (void) putchar('\n');
+ for (;;) {
+ (void) printf("%s> ", prompt);
+ if (fgets(line, sizeof (line), stdin) == NULL) {
+ if (feof(stdin))
+ quit(0, NULL);
+ else
+ continue;
+ }
+
+ /* Flush what didn't fit in the buffer */
+ if (line[strlen(line)-1] != '\n') {
+ while (((ch = getchar()) != EOF) && (ch != '\n'))
+ ;
+ (void) fputs("?Line too long\n", stderr);
+ } else {
+ line[strlen(line)-1] = '\0';
+ if (line[0] != '\0') {
+ int argc;
+ char **argv;
+
+ makeargv(&argc, &argv);
+ c = getcmd(argv[0]);
+ if (c == AMBIGCMD)
+ (void) fputs("?Ambiguous command\n",
+ stderr);
+ else if (c == NULL)
+ (void) fputs("?Invalid command\n",
+ stderr);
+ else
+ (*c->handler)(argc, argv);
+ }
+ }
+ }
+}
+
+static struct cmd *
+getcmd(char *name)
+{
+ char *p, *q;
+ struct cmd *c, *found;
+
+ if (name == NULL)
+ return (NULL);
+
+ found = NULL;
+ for (c = cmdtab; (p = c->name) != NULL; c++) {
+ for (q = name; *q == *p++; q++)
+ if (*q == '\0') /* exact match? */
+ return (c);
+ if (*q == '\0') /* the name was a prefix */
+ found = (found == NULL) ? c : AMBIGCMD;
+ }
+ return (found);
+}
+
+/*
+ * Given a string, this function returns the pointer to the delimiting ':'.
+ * The string can contain an IPv6 literal address, which should be inside a
+ * pair of brackets, e.g. [1::2]. Any colons inside a pair of brackets are not
+ * accepted as delimiters. Returns NULL if delimiting ':' is not found.
+ */
+static char *
+finddelimiter(char *str)
+{
+ boolean_t is_bracket_open = B_FALSE;
+ char *cp;
+
+ for (cp = str; *cp != '\0'; cp++) {
+ if (*cp == '[')
+ is_bracket_open = B_TRUE;
+ else if (*cp == ']')
+ is_bracket_open = B_FALSE;
+ else if (*cp == ':' && !is_bracket_open)
+ return (cp);
+ }
+ return (NULL);
+}
+
+/*
+ * Given a string which is possibly surrounded by brackets, e.g. [1::2], this
+ * function returns a string after removing those brackets. If the brackets
+ * don't match, it does nothing.
+ */
+static char *
+removebrackets(char *str)
+{
+ char *newstr = str;
+
+ if ((str[0] == '[') && (str[strlen(str) - 1] == ']')) {
+ newstr = str + 1;
+ str[strlen(str) - 1] = '\0';
+ }
+ return (newstr);
+}
+
+#define MARGV_INC 20
+
+/*
+ * Slice a string up into argc/argv.
+ */
+static void
+makeargv(int *argcp, char ***argvp)
+{
+ char *cp;
+ char **argp;
+ int argc;
+ static char **argv;
+ static int argv_size;
+
+ if (argv == NULL) {
+ argv_size = MARGV_INC;
+ if ((argv = malloc(argv_size * sizeof (char *))) == NULL) {
+ perror("tftp: malloc");
+ exit(1);
+ }
+ }
+ argc = 0;
+ argp = argv;
+ for (cp = line; *cp != '\0'; ) {
+ while (isspace(*cp))
+ cp++;
+ if (*cp == '\0')
+ break;
+ *argp++ = cp;
+ argc++;
+ if (argc == argv_size) {
+ argv_size += MARGV_INC;
+ if ((argv = realloc(argv,
+ argv_size * sizeof (char *))) == NULL) {
+ perror("tftp: realloc");
+ exit(1);
+ }
+ argp = argv + argc;
+ }
+ while (*cp != '\0' && !isspace(*cp))
+ cp++;
+ if (*cp == '\0')
+ break;
+ *cp++ = '\0';
+ }
+ *argp = NULL;
+
+ *argcp = argc;
+ *argvp = argv;
+}
+
+/*ARGSUSED*/
+static void
+quit(int argc, char **argv)
+{
+ exit(0);
+}
+
+/*
+ * Help command.
+ */
+static void
+help(int argc, char **argv)
+{
+ struct cmd *c;
+
+ if (argc == 1) {
+ (void) puts("Commands may be abbreviated. Commands are:\n");
+ for (c = cmdtab; c->name != NULL; c++)
+ (void) printf("%-*s\t%s\n", HELPINDENT, c->name,
+ c->help);
+ return;
+ }
+ while (--argc > 0) {
+ char *arg;
+ arg = *++argv;
+ c = getcmd(arg);
+ if (c == AMBIGCMD)
+ (void) fprintf(stderr, "?Ambiguous help command %s\n",
+ arg);
+ else if (c == NULL)
+ (void) fprintf(stderr, "?Invalid help command %s\n",
+ arg);
+ else
+ (void) fprintf(stderr, "%s\n", c->help);
+ }
+}
+
+/*ARGSUSED*/
+static void
+settrace(int argc, char **argv)
+{
+ trace = !trace;
+ (void) printf("Packet tracing %s.\n", trace ? "on" : "off");
+}
+
+/*ARGSUSED*/
+static void
+setverbose(int argc, char **argv)
+{
+ verbose = !verbose;
+ (void) printf("Verbose mode %s.\n", verbose ? "on" : "off");
+}
+
+static void
+setblksize(int argc, char **argv)
+{
+ int b;
+
+ if (argc < 2) {
+ if (prompt_for_arg(line, sizeof (line), "value") == -1)
+ return;
+ makeargv(&argc, &argv);
+ }
+ if (argc != 2) {
+ (void) fprintf(stderr, "usage: %s value\n", argv[0]);
+ return;
+ }
+ b = atoi(argv[1]);
+
+ /* RFC 2348 specifies valid blksize range, allow 0 to turn option off */
+ if ((b < MIN_BLKSIZE || b > MAX_BLKSIZE) && b != 0)
+ (void) fprintf(stderr, "%s: bad value\n", argv[1]);
+ else
+ blksize = b;
+}
+
+static void
+setsrexmt(int argc, char **argv)
+{
+ int t;
+
+ if (argc < 2) {
+ if (prompt_for_arg(line, sizeof (line), "value") == -1)
+ return;
+ makeargv(&argc, &argv);
+ }
+ if (argc != 2) {
+ (void) fprintf(stderr, "usage: %s value\n", argv[0]);
+ return;
+ }
+ t = atoi(argv[1]);
+
+ /* RFC 2349 specifies valid timeout range, allow 0 to turn option off */
+ if ((t < MIN_TIMEOUT || t > MAX_TIMEOUT) && t != 0)
+ (void) fprintf(stderr, "%s: bad value\n", argv[1]);
+ else
+ srexmtval = t;
+}
+
+static void
+settsize(int argc, char **argv)
+{
+ if (argc != 1) {
+ (void) fprintf(stderr, "usage: %s\n", argv[0]);
+ return;
+ }
+ tsize_opt = !tsize_opt;
+ (void) printf("Transfer size option %s.\n", tsize_opt ? "on" : "off");
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/tftp/tftp.c b/usr/src/cmd/cmd-inet/usr.bin/tftp/tftp.c
new file mode 100644
index 0000000000..93fd312452
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/tftp/tftp.c
@@ -0,0 +1,736 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * TFTP User Program -- Protocol Machines
+ */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <stddef.h>
+#include <inttypes.h>
+
+#include "tftpcommon.h"
+#include "tftpprivate.h"
+
+static char *blksize_str(void);
+static char *timeout_str(void);
+static char *tsize_str(void);
+static int blksize_handler(char *);
+static int timeout_handler(char *);
+static int tsize_handler(char *);
+static int add_options(char *, char *);
+static int process_oack(tftpbuf *, int);
+static void nak(int);
+static void startclock(void);
+static void stopclock(void);
+static void printstats(char *, off_t);
+static int makerequest(int, char *, struct tftphdr *, char *);
+static void tpacket(char *, struct tftphdr *, int);
+
+static struct options {
+ char *opt_name;
+ char *(*opt_str)(void);
+ int (*opt_handler)(char *);
+} options[] = {
+ { "blksize", blksize_str, blksize_handler },
+ { "timeout", timeout_str, timeout_handler },
+ { "tsize", tsize_str, tsize_handler },
+ { NULL }
+};
+
+static char optbuf[MAX_OPTVAL_LEN];
+static boolean_t tsize_set;
+
+static tftpbuf ackbuf;
+static int timeout;
+static off_t tsize;
+static jmp_buf timeoutbuf;
+
+int blocksize = SEGSIZE; /* Number of data bytes in a DATA packet */
+
+/*ARGSUSED*/
+static void
+timer(int signum)
+{
+ timeout += rexmtval;
+ if (timeout >= maxtimeout) {
+ (void) fputs("Transfer timed out.\n", stderr);
+ longjmp(toplevel, -1);
+ }
+ (void) signal(SIGALRM, timer);
+ longjmp(timeoutbuf, 1);
+}
+
+/*
+ * Send the requested file.
+ */
+void
+tftp_sendfile(int fd, char *name, char *mode)
+{
+ struct tftphdr *ap; /* data and ack packets */
+ struct tftphdr *dp;
+ int block = 0, size, n;
+ off_t amount = 0;
+ struct sockaddr_in6 from;
+ socklen_t fromlen;
+ int convert; /* true if doing nl->crlf conversion */
+ FILE *file;
+ struct stat statb;
+ int errcode;
+
+ startclock(); /* start stat's clock */
+ dp = r_init(); /* reset fillbuf/read-ahead code */
+ ap = &ackbuf.tb_hdr;
+ file = fdopen(fd, "r");
+ convert = (strcmp(mode, "netascii") == 0);
+
+ tsize_set = ((tsize_opt != 0) && !convert && (fstat(fd, &statb) == 0));
+ if (tsize_set)
+ tsize = statb.st_size;
+
+ do {
+ (void) signal(SIGALRM, timer);
+ if (block == 0) {
+ if ((size = makerequest(WRQ, name, dp, mode)) == -1) {
+ (void) fprintf(stderr,
+ "tftp: Error: Write request packet too "
+ "big\n");
+ (void) fclose(file);
+ return;
+ }
+ size -= 4;
+ } else {
+ size = readit(file, &dp, convert);
+ if (size < 0) {
+ nak(errno + 100);
+ break;
+ }
+ dp->th_opcode = htons((ushort_t)DATA);
+ dp->th_block = htons((ushort_t)block);
+ }
+ timeout = 0;
+ (void) setjmp(timeoutbuf);
+ if (trace)
+ tpacket("sent", dp, size + 4);
+ n = sendto(f, dp, size + 4, 0,
+ (struct sockaddr *)&sin6, sizeof (sin6));
+ if (n != size + 4) {
+ perror("tftp: sendto");
+ goto abort;
+ }
+ /* Can't read-ahead first block as OACK may change blocksize */
+ if (block != 0)
+ read_ahead(file, convert);
+ (void) alarm(rexmtval);
+ for (; ; ) {
+ (void) sigrelse(SIGALRM);
+ do {
+ fromlen = (socklen_t)sizeof (from);
+ n = recvfrom(f, ackbuf.tb_data,
+ sizeof (ackbuf.tb_data), 0,
+ (struct sockaddr *)&from, &fromlen);
+ if (n < 0) {
+ perror("tftp: recvfrom");
+ goto abort;
+ }
+ } while (n < offsetof(struct tftphdr, th_data));
+ (void) sighold(SIGALRM);
+ sin6.sin6_port = from.sin6_port; /* added */
+ if (trace)
+ tpacket("received", ap, n);
+ /* should verify packet came from server */
+ ap->th_opcode = ntohs(ap->th_opcode);
+ if (ap->th_opcode == ERROR) {
+ ap->th_code = ntohs(ap->th_code);
+ (void) fprintf(stderr,
+ "Error code %d", ap->th_code);
+ if (n > offsetof(struct tftphdr, th_data))
+ (void) fprintf(stderr, ": %.*s", n -
+ offsetof(struct tftphdr, th_data),
+ ap->th_msg);
+ (void) fputc('\n', stderr);
+ goto abort;
+ }
+ if ((block == 0) && (ap->th_opcode == OACK)) {
+ errcode = process_oack(&ackbuf, n);
+ if (errcode >= 0) {
+ nak(errcode);
+ (void) fputs("Rejected OACK\n",
+ stderr);
+ goto abort;
+ }
+ break;
+ }
+ if (ap->th_opcode == ACK) {
+ ap->th_block = ntohs(ap->th_block);
+ if (ap->th_block == block) {
+ break;
+ }
+ /*
+ * Never resend the current DATA packet on
+ * receipt of a duplicate ACK, doing so would
+ * cause the "Sorcerer's Apprentice Syndrome".
+ */
+ }
+ }
+ cancel_alarm();
+ if (block > 0)
+ amount += size;
+ block++;
+ } while (size == blocksize || block == 1);
+abort:
+ cancel_alarm();
+ (void) fclose(file);
+ stopclock();
+ if (amount > 0)
+ printstats("Sent", amount);
+}
+
+/*
+ * Receive a file.
+ */
+void
+tftp_recvfile(int fd, char *name, char *mode)
+{
+ struct tftphdr *ap;
+ struct tftphdr *dp;
+ int block = 1, n, size;
+ unsigned long amount = 0;
+ struct sockaddr_in6 from;
+ socklen_t fromlen;
+ boolean_t firsttrip = B_TRUE;
+ FILE *file;
+ int convert; /* true if converting crlf -> lf */
+ int errcode;
+
+ startclock();
+ dp = w_init();
+ ap = &ackbuf.tb_hdr;
+ file = fdopen(fd, "w");
+ convert = (strcmp(mode, "netascii") == 0);
+
+ tsize_set = (tsize_opt != 0);
+ if (tsize_set)
+ tsize = 0;
+
+ if ((size = makerequest(RRQ, name, ap, mode)) == -1) {
+ (void) fprintf(stderr,
+ "tftp: Error: Read request packet too big\n");
+ (void) fclose(file);
+ return;
+ }
+
+ do {
+ (void) signal(SIGALRM, timer);
+ if (firsttrip) {
+ firsttrip = B_FALSE;
+ } else {
+ ap->th_opcode = htons((ushort_t)ACK);
+ ap->th_block = htons((ushort_t)(block));
+ size = 4;
+ block++;
+ }
+
+send_oack_ack:
+ timeout = 0;
+ (void) setjmp(timeoutbuf);
+send_ack:
+ if (trace)
+ tpacket("sent", ap, size);
+ if (sendto(f, ackbuf.tb_data, size, 0, (struct sockaddr *)&sin6,
+ sizeof (sin6)) != size) {
+ (void) alarm(0);
+ perror("tftp: sendto");
+ goto abort;
+ }
+ if (write_behind(file, convert) < 0) {
+ nak(errno + 100);
+ goto abort;
+ }
+ (void) alarm(rexmtval);
+ for (; ; ) {
+ (void) sigrelse(SIGALRM);
+ do {
+ fromlen = (socklen_t)sizeof (from);
+ n = recvfrom(f, dp, blocksize + 4, 0,
+ (struct sockaddr *)&from, &fromlen);
+ if (n < 0) {
+ perror("tftp: recvfrom");
+ goto abort;
+ }
+ } while (n < offsetof(struct tftphdr, th_data));
+ (void) sighold(SIGALRM);
+ sin6.sin6_port = from.sin6_port; /* added */
+ if (trace)
+ tpacket("received", dp, n);
+ /* should verify client address */
+ dp->th_opcode = ntohs(dp->th_opcode);
+ if (dp->th_opcode == ERROR) {
+ dp->th_code = ntohs(dp->th_code);
+ (void) fprintf(stderr, "Error code %d",
+ dp->th_code);
+ if (n > offsetof(struct tftphdr, th_data))
+ (void) fprintf(stderr, ": %.*s", n -
+ offsetof(struct tftphdr, th_data),
+ dp->th_msg);
+ (void) fputc('\n', stderr);
+ goto abort;
+ }
+ if ((block == 1) && (dp->th_opcode == OACK)) {
+ errcode = process_oack((tftpbuf *)dp, n);
+ if (errcode >= 0) {
+ cancel_alarm();
+ nak(errcode);
+ (void) fputs("Rejected OACK\n",
+ stderr);
+ (void) fclose(file);
+ return;
+ }
+ ap->th_opcode = htons((ushort_t)ACK);
+ ap->th_block = htons(0);
+ size = 4;
+ goto send_oack_ack;
+ }
+ if (dp->th_opcode == DATA) {
+ int j;
+
+ dp->th_block = ntohs(dp->th_block);
+ if (dp->th_block == block) {
+ break; /* have next packet */
+ }
+ /*
+ * On an error, try to synchronize
+ * both sides.
+ */
+ j = synchnet(f);
+ if (j < 0) {
+ perror("tftp: recvfrom");
+ goto abort;
+ }
+ if ((j > 0) && trace) {
+ (void) printf("discarded %d packets\n",
+ j);
+ }
+ if (dp->th_block == (block-1)) {
+ goto send_ack; /* resend ack */
+ }
+ }
+ }
+ cancel_alarm();
+ size = writeit(file, &dp, n - 4, convert);
+ if (size < 0) {
+ nak(errno + 100);
+ goto abort;
+ }
+ amount += size;
+ } while (size == blocksize);
+
+ cancel_alarm();
+ if (write_behind(file, convert) < 0) { /* flush last buffer */
+ nak(errno + 100);
+ goto abort;
+ }
+ n = fclose(file);
+ file = NULL;
+ if (n == EOF) {
+ nak(errno + 100);
+ goto abort;
+ }
+
+ /* ok to ack, since user has seen err msg */
+ ap->th_opcode = htons((ushort_t)ACK);
+ ap->th_block = htons((ushort_t)block);
+ if (trace)
+ tpacket("sent", ap, 4);
+ if (sendto(f, ackbuf.tb_data, 4, 0,
+ (struct sockaddr *)&sin6, sizeof (sin6)) != 4)
+ perror("tftp: sendto");
+
+abort:
+ cancel_alarm();
+ if (file != NULL)
+ (void) fclose(file);
+ stopclock();
+ if (amount > 0)
+ printstats("Received", amount);
+}
+
+static int
+makerequest(int request, char *name, struct tftphdr *tp, char *mode)
+{
+ char *cp, *cpend;
+ int len;
+
+ tp->th_opcode = htons((ushort_t)request);
+ cp = (char *)&tp->th_stuff;
+
+ /* Maximum size of a request packet is 512 bytes (RFC 2347) */
+ cpend = (char *)tp + SEGSIZE;
+
+ len = strlcpy(cp, name, cpend - cp) + 1;
+ cp += len;
+ if (cp > cpend)
+ return (-1);
+
+ len = strlcpy(cp, mode, cpend - cp) + 1;
+ cp += len;
+ if (cp > cpend)
+ return (-1);
+
+ len = add_options(cp, cpend);
+ if (len == -1)
+ return (-1);
+ cp += len;
+
+ return (cp - (char *)tp);
+}
+
+/*
+ * Return the blksize option value string to include in the request packet.
+ */
+static char *
+blksize_str(void)
+{
+ blocksize = SEGSIZE;
+ if (blksize == 0)
+ return (NULL);
+
+ (void) snprintf(optbuf, sizeof (optbuf), "%d", blksize);
+ return (optbuf);
+}
+
+/*
+ * Return the timeout option value string to include in the request packet.
+ */
+static char *
+timeout_str(void)
+{
+ if (srexmtval == 0)
+ return (NULL);
+
+ (void) snprintf(optbuf, sizeof (optbuf), "%d", srexmtval);
+ return (optbuf);
+}
+
+/*
+ * Return the tsize option value string to include in the request packet.
+ */
+static char *
+tsize_str(void)
+{
+ if (tsize_set == B_FALSE)
+ return (NULL);
+
+ (void) snprintf(optbuf, sizeof (optbuf), OFF_T_FMT, tsize);
+ return (optbuf);
+}
+
+/*
+ * Validate and action the blksize option value string from the OACK packet.
+ * Returns -1 on success or an error code on failure.
+ */
+static int
+blksize_handler(char *optstr)
+{
+ char *endp;
+ int value;
+
+ /* Make sure the option was requested */
+ if (blksize == 0)
+ return (EOPTNEG);
+ errno = 0;
+ value = (int)strtol(optstr, &endp, 10);
+ if (errno != 0 || value < MIN_BLKSIZE || value > blksize ||
+ *endp != '\0')
+ return (EOPTNEG);
+ blocksize = value;
+ return (-1);
+}
+
+/*
+ * Validate and action the timeout option value string from the OACK packet.
+ * Returns -1 on success or an error code on failure.
+ */
+static int
+timeout_handler(char *optstr)
+{
+ char *endp;
+ int value;
+
+ /* Make sure the option was requested */
+ if (srexmtval == 0)
+ return (EOPTNEG);
+ errno = 0;
+ value = (int)strtol(optstr, &endp, 10);
+ if (errno != 0 || value != srexmtval || *endp != '\0')
+ return (EOPTNEG);
+ /*
+ * Nothing to set, client and server retransmission intervals are
+ * set separately in the client.
+ */
+ return (-1);
+}
+
+/*
+ * Validate and action the tsize option value string from the OACK packet.
+ * Returns -1 on success or an error code on failure.
+ */
+static int
+tsize_handler(char *optstr)
+{
+ char *endp;
+ longlong_t value;
+
+ /* Make sure the option was requested */
+ if (tsize_set == B_FALSE)
+ return (EOPTNEG);
+ errno = 0;
+ value = strtoll(optstr, &endp, 10);
+ if (errno != 0 || value < 0 || *endp != '\0')
+ return (EOPTNEG);
+#if _FILE_OFFSET_BITS == 32
+ if (value > MAXOFF_T)
+ return (ENOSPACE);
+#endif
+ /*
+ * Don't bother checking the tsize value we specified in a write
+ * request is echoed back in the OACK.
+ */
+ if (tsize == 0)
+ tsize = value;
+ return (-1);
+}
+
+/*
+ * Add TFTP options to a request packet.
+ */
+static int
+add_options(char *obuf, char *obufend)
+{
+ int i;
+ char *cp, *ostr;
+
+ cp = obuf;
+ for (i = 0; options[i].opt_name != NULL; i++) {
+ ostr = options[i].opt_str();
+ if (ostr != NULL) {
+ cp += strlcpy(cp, options[i].opt_name, obufend - cp)
+ + 1;
+ if (cp > obufend)
+ return (-1);
+
+ cp += strlcpy(cp, ostr, obufend - cp) + 1;
+ if (cp > obufend)
+ return (-1);
+ }
+ }
+ return (cp - obuf);
+}
+
+/*
+ * Process OACK packet sent by server in response to options in the request
+ * packet. Returns -1 on success or an error code on failure.
+ */
+static int
+process_oack(tftpbuf *oackbuf, int n)
+{
+ char *cp, *oackend, *optname, *optval;
+ struct tftphdr *oackp;
+ int i, errcode;
+
+ oackp = &oackbuf->tb_hdr;
+ cp = (char *)&oackp->th_stuff;
+ oackend = (char *)oackbuf + n;
+
+ while (cp < oackend) {
+ optname = cp;
+ if ((optval = next_field(optname, oackend)) == NULL)
+ return (EOPTNEG);
+ if ((cp = next_field(optval, oackend)) == NULL)
+ return (EOPTNEG);
+ for (i = 0; options[i].opt_name != NULL; i++) {
+ if (strcasecmp(optname, options[i].opt_name) == 0)
+ break;
+ }
+ if (options[i].opt_name == NULL)
+ return (EOPTNEG);
+ errcode = options[i].opt_handler(optval);
+ if (errcode >= 0)
+ return (errcode);
+ }
+ return (-1);
+}
+
+/*
+ * Send a nak packet (error message).
+ * Error code passed in is one of the
+ * standard TFTP codes, or a UNIX errno
+ * offset by 100.
+ */
+static void
+nak(int error)
+{
+ struct tftphdr *tp;
+ int length;
+ struct errmsg *pe;
+
+ tp = &ackbuf.tb_hdr;
+ tp->th_opcode = htons((ushort_t)ERROR);
+ tp->th_code = htons((ushort_t)error);
+ for (pe = errmsgs; pe->e_code >= 0; pe++)
+ if (pe->e_code == error)
+ break;
+ if (pe->e_code < 0) {
+ pe->e_msg = strerror(error - 100);
+ tp->th_code = EUNDEF;
+ }
+ (void) strlcpy(tp->th_msg, pe->e_msg,
+ sizeof (ackbuf) - sizeof (struct tftphdr));
+ length = strlen(pe->e_msg) + 4;
+ if (trace)
+ tpacket("sent", tp, length);
+ if (sendto(f, ackbuf.tb_data, length, 0,
+ (struct sockaddr *)&sin6, sizeof (sin6)) != length)
+ perror("nak");
+}
+
+static void
+tpacket(char *s, struct tftphdr *tp, int n)
+{
+ static char *opcodes[] = \
+ { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR", "OACK" };
+ char *cp, *file, *mode;
+ ushort_t op = ntohs(tp->th_opcode);
+ char *tpend;
+
+ if (op < RRQ || op > OACK)
+ (void) printf("%s opcode=%x ", s, op);
+ else
+ (void) printf("%s %s ", s, opcodes[op]);
+
+ switch (op) {
+ case RRQ:
+ case WRQ:
+ tpend = (char *)tp + n;
+ n -= sizeof (tp->th_opcode);
+ file = (char *)&tp->th_stuff;
+ if ((mode = next_field(file, tpend)) == NULL) {
+ (void) printf("<file=%.*s>\n", n, file);
+ break;
+ }
+ n -= mode - file;
+ if ((cp = next_field(mode, tpend)) == NULL) {
+ (void) printf("<file=%s, mode=%.*s>\n", file, n, mode);
+ break;
+ }
+ (void) printf("<file=%s, mode=%s", file, mode);
+ n -= cp - mode;
+ if (n > 0) {
+ (void) printf(", options: ");
+ print_options(stdout, cp, n);
+ }
+ (void) puts(">");
+ break;
+
+ case DATA:
+ (void) printf("<block=%d, %d bytes>\n", ntohs(tp->th_block),
+ n - sizeof (tp->th_opcode) - sizeof (tp->th_block));
+ break;
+
+ case ACK:
+ (void) printf("<block=%d>\n", ntohs(tp->th_block));
+ break;
+
+ case OACK:
+ (void) printf("<options: ");
+ print_options(stdout, (char *)&tp->th_stuff,
+ n - sizeof (tp->th_opcode));
+ (void) puts(">");
+ break;
+
+ case ERROR:
+ (void) printf("<code=%d", ntohs(tp->th_code));
+ n = n - sizeof (tp->th_opcode) - sizeof (tp->th_code);
+ if (n > 0)
+ (void) printf(", msg=%.*s", n, tp->th_msg);
+ (void) puts(">");
+ break;
+ }
+}
+
+static hrtime_t tstart, tstop;
+
+static void
+startclock(void)
+{
+ tstart = gethrtime();
+}
+
+static void
+stopclock(void)
+{
+ tstop = gethrtime();
+}
+
+static void
+printstats(char *direction, off_t amount)
+{
+ hrtime_t delta, tenths;
+
+ delta = tstop - tstart;
+ tenths = delta / (NANOSEC / 10);
+ (void) printf("%s " OFF_T_FMT " bytes in %" PRId64 ".%" PRId64
+ " seconds", direction, amount, tenths / 10, tenths % 10);
+ if (verbose)
+ (void) printf(" [%" PRId64 " bits/sec]\n",
+ ((hrtime_t)amount * 8 * NANOSEC) / delta);
+ else
+ (void) putchar('\n');
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/tftp/tftpprivate.h b/usr/src/cmd/cmd-inet/usr.bin/tftp/tftpprivate.h
new file mode 100644
index 0000000000..02e5eb1b8e
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/tftp/tftpprivate.h
@@ -0,0 +1,62 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _TFTP_PRIVATE_H
+#define _TFTP_PRIVATE_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Global definitions for the implementation of tftp(1).
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <netinet/in.h>
+
+#include <setjmp.h>
+
+extern struct sockaddr_in6 sin6; /* filled in by main */
+extern int f; /* the opened socket */
+extern int trace;
+extern int verbose;
+extern int rexmtval;
+extern int maxtimeout;
+extern int blksize;
+extern int srexmtval;
+extern int tsize_opt;
+extern jmp_buf toplevel;
+
+extern void tftp_sendfile(int, char *, char *);
+extern void tftp_recvfile(int, char *, char *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TFTP_PRIVATE_H */
diff --git a/usr/src/cmd/cmd-inet/usr.bin/tftp/tftpsubs.c b/usr/src/cmd/cmd-inet/usr.bin/tftp/tftpsubs.c
new file mode 100644
index 0000000000..e24bd0c8ba
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/tftp/tftpsubs.c
@@ -0,0 +1,393 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Simple minded read-ahead/write-behind subroutines for tftp user and
+ * server. Written originally with multiple buffers in mind, but current
+ * implementation has two buffer logic wired in.
+ *
+ * Todo: add some sort of final error check so when the write-buffer
+ * is finally flushed, the caller can detect if the disk filled up
+ * (or had an i/o error) and return a nak to the other side.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/filio.h>
+
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <poll.h>
+#include <string.h>
+
+#include "tftpcommon.h"
+
+struct errmsg errmsgs[] = {
+ { EUNDEF, "Undefined error code" },
+ { ENOTFOUND, "File not found" },
+ { EACCESS, "Access violation" },
+ { ENOSPACE, "Disk full or allocation exceeded" },
+ { EBADOP, "Illegal TFTP operation" },
+ { EBADID, "Unknown transfer ID" },
+ { EEXISTS, "File already exists" },
+ { ENOUSER, "No such user" },
+ { EOPTNEG, "Option negotiation error" },
+ { -1, NULL }
+};
+
+static struct bf {
+ int counter; /* size of data in buffer, or flag */
+ tftpbuf buf; /* room for data packet */
+} bfs[2];
+
+extern int blocksize; /* Number of data bytes in a DATA packet */
+ /* Values for bf.counter */
+#define BF_ALLOC -3 /* alloc'd but not yet filled */
+#define BF_FREE -2 /* free */
+/* [-1 .. blocksize] = size of data in the data buffer */
+
+static int nextone; /* index of next buffer to use */
+static int current; /* index of buffer in use */
+
+ /* control flags for crlf conversions */
+static int newline = 0; /* fillbuf: in middle of newline expansion */
+static int prevchar = -1; /* putbuf: previous char (cr check) */
+
+static struct tftphdr *rw_init(int);
+
+struct tftphdr *w_init() { return (rw_init(0)); } /* write-behind */
+struct tftphdr *r_init() { return (rw_init(1)); } /* read-ahead */
+
+/*
+ * Init for either read-ahead or write-behind.
+ * x is zero for write-behind, one for read-head.
+ */
+static struct tftphdr *
+rw_init(int x)
+{
+ newline = 0; /* init crlf flag */
+ prevchar = -1;
+ bfs[0].counter = BF_ALLOC; /* pass out the first buffer */
+ current = 0;
+ bfs[1].counter = BF_FREE;
+ nextone = x; /* ahead or behind? */
+ return (&bfs[0].buf.tb_hdr);
+}
+
+
+/*
+ * Have emptied current buffer by sending to net and getting ack.
+ * Free it and return next buffer filled with data.
+ */
+int
+readit(FILE *file, struct tftphdr **dpp, int convert)
+{
+ struct bf *b;
+
+ bfs[current].counter = BF_FREE; /* free old one */
+ current = !current; /* "incr" current */
+
+ b = &bfs[current]; /* look at new buffer */
+ if (b->counter == BF_FREE) /* if it's empty */
+ read_ahead(file, convert); /* fill it */
+ *dpp = &b->buf.tb_hdr; /* set caller's ptr */
+ return (b->counter);
+}
+
+/*
+ * fill the input buffer, doing ascii conversions if requested
+ * conversions are lf -> cr,lf and cr -> cr, nul
+ */
+void
+read_ahead(FILE *file, int convert)
+{
+ int i;
+ char *p;
+ int c;
+ struct bf *b;
+ struct tftphdr *dp;
+
+ b = &bfs[nextone]; /* look at "next" buffer */
+ if (b->counter != BF_FREE) /* nop if not free */
+ return;
+ nextone = !nextone; /* "incr" next buffer ptr */
+
+ dp = &b->buf.tb_hdr;
+
+ if (!convert) {
+ b->counter = fread(dp->th_data, sizeof (char), blocksize,
+ file);
+ if (ferror(file))
+ b->counter = -1;
+ return;
+ }
+
+ p = dp->th_data;
+ for (i = 0; i < blocksize; i++) {
+ if (newline) {
+ if (prevchar == '\n')
+ c = '\n'; /* lf to cr,lf */
+ else c = '\0'; /* cr to cr,nul */
+ newline = 0;
+ } else {
+ c = getc(file);
+ if (c == EOF) break;
+ if (c == '\n' || c == '\r') {
+ prevchar = c;
+ c = '\r';
+ newline = 1;
+ }
+ }
+ *p++ = c;
+ }
+ b->counter = (int)(p - dp->th_data);
+}
+
+/*
+ * Update count associated with the buffer, get new buffer
+ * from the queue. Calls write_behind only if next buffer not
+ * available.
+ */
+int
+writeit(FILE *file, struct tftphdr **dpp, int ct, int convert)
+{
+ bfs[current].counter = ct; /* set size of data to write */
+ current = !current; /* switch to other buffer */
+ if (bfs[current].counter != BF_FREE) /* if not free */
+ if (write_behind(file, convert) < 0) /* flush it */
+ ct = -1;
+ bfs[current].counter = BF_ALLOC; /* mark as alloc'd */
+ *dpp = &bfs[current].buf.tb_hdr;
+ return (ct); /* this is a lie of course */
+}
+
+/*
+ * Output a buffer to a file, converting from netascii if requested.
+ * CR,NUL -> CR and CR,LF => LF.
+ * Note spec is undefined if we get CR as last byte of file or a
+ * CR followed by anything else. In this case we leave it alone.
+ */
+int
+write_behind(FILE *file, int convert)
+{
+ char *buf;
+ int count;
+ int ct;
+ char *p;
+ int c; /* current character */
+ struct bf *b;
+ struct tftphdr *dp;
+
+ b = &bfs[nextone];
+ if (b->counter < -1) /* anything to flush? */
+ return (0); /* just nop if nothing to do */
+
+ count = b->counter; /* remember byte count */
+ b->counter = BF_FREE; /* reset flag */
+ dp = &b->buf.tb_hdr;
+ nextone = !nextone; /* incr for next time */
+ buf = dp->th_data;
+
+ if (count <= 0)
+ return (0); /* nak logic? */
+
+ if (!convert) {
+ size_t left = count;
+
+ while (left > 0) {
+ size_t written;
+
+ written = fwrite(buf, sizeof (char), left, file);
+ if (ferror(file)) {
+ /* Retry if we were interrupted by a signal. */
+ if (errno == EINTR)
+ continue;
+ return (-1);
+ }
+ if (written == 0)
+ return (-1);
+
+ left -= written;
+ buf += written;
+ }
+
+ return (count);
+ }
+
+ p = buf;
+ ct = count;
+ while (ct--) { /* loop over the buffer */
+ c = *p++; /* pick up a character */
+ if (prevchar == '\r') { /* if prev char was cr */
+ if (c == '\n') { /* if have cr,lf then just */
+ /* smash lf on top of the cr */
+ if (fseek(file, -1, SEEK_CUR) < 0)
+ return (-1);
+ } else {
+ if (c == '\0') {
+ /*
+ * If we have cr,nul then
+ * just skip over the putc.
+ */
+ prevchar = 0;
+ continue;
+ }
+ }
+ /* else just fall through and allow it */
+ }
+ if (putc(c, file) == EOF)
+ return (-1);
+ prevchar = c;
+ }
+ return (count);
+}
+
+
+/*
+ * When an error has occurred, it is possible that the two sides
+ * are out of synch. Ie: that what I think is the other side's
+ * response to packet N is really their response to packet N-1.
+ *
+ * So, to try to prevent that, we flush all the input queued up
+ * for us on the network connection on our host.
+ *
+ * We return the number of packets we flushed (mostly for reporting
+ * when trace is active) or -1 in case of an error.
+ */
+
+int
+synchnet(int socket)
+{
+ struct pollfd pfd;
+ int packets;
+
+ pfd.fd = socket;
+ pfd.events = POLLRDNORM;
+ for (packets = 0; ; packets++) {
+ char buf;
+ struct sockaddr_in6 from;
+ socklen_t fromlen;
+
+ if (poll(&pfd, 1, 0) <= 0)
+ break;
+
+ /*
+ * A one byte buffer is enough because recvfrom() will
+ * discard the remaining data of the packet.
+ */
+ fromlen = sizeof (from);
+ if (recvfrom(socket, &buf, sizeof (buf), 0,
+ (struct sockaddr *)&from, &fromlen) < 0)
+ return (-1);
+ }
+
+ return (packets);
+}
+
+/*
+ * Return a pointer to the next field in string s, or return NULL if no
+ * terminating NUL is found for the current field before end.
+ */
+char *
+next_field(const char *s, const char *end)
+{
+ if (s < end) {
+ s = memchr(s, 0, end - s);
+ if (s != NULL)
+ return ((char *)s + 1);
+ }
+ return (NULL);
+}
+
+/*
+ * Print to stream options in the format option_name=option_value
+ */
+void
+print_options(FILE *stream, char *opts, int len)
+{
+ char *cp, *optname, *optval;
+ char *endopts = opts + len;
+ int first = 1;
+
+ /*
+ * Ignore null padding, appended by broken TFTP clients to
+ * requests which don't include options.
+ */
+ cp = opts;
+ while ((cp < endopts) && (*cp == '\0'))
+ cp++;
+ if (cp == endopts)
+ return;
+
+ while (opts < endopts) {
+ optname = opts;
+ if ((optval = next_field(optname, endopts)) == NULL) {
+ (void) putc('?', stream);
+ return;
+ }
+ if (first)
+ first = 0;
+ else
+ (void) putc(' ', stream);
+ (void) fputs(optname, stream);
+ if ((opts = next_field(optval, endopts)) == NULL) {
+ (void) putc('?', stream);
+ return;
+ }
+ (void) fprintf(stream, "=%s", optval);
+ }
+}
+
+/*
+ * Turn off the alarm timer and ensure any pending SIGALRM signal is ignored.
+ */
+void
+cancel_alarm(void)
+{
+ (void) alarm(0);
+ (void) signal(SIGALRM, SIG_IGN);
+ (void) sigrelse(SIGALRM);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.bin/whois.c b/usr/src/cmd/cmd-inet/usr.bin/whois.c
new file mode 100644
index 0000000000..621f3d120a
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/whois.c
@@ -0,0 +1,122 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1994 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+
+#include <stdio.h>
+#include <netdb.h>
+
+#ifdef SYSV
+#define bcopy(a,b,c) memcpy((b),(a),(c))
+#endif /* SYSV */
+
+#define NICHOST "whois.internic.net"
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int s;
+ register FILE *sfi, *sfo;
+ register int c;
+ char *host = NICHOST;
+ struct sockaddr_in sin;
+ struct hostent *hp;
+ struct servent *sp;
+ char hnamebuf[32];
+ int addrtype;
+
+ argc--, argv++;
+ if (argc > 2 && strcmp(*argv, "-h") == 0) {
+ argv++, argc--;
+ host = *argv++;
+ argc--;
+ }
+ if (argc != 1) {
+ fprintf(stderr, "usage: whois [ -h host ] name\n");
+ exit(1);
+ }
+ sin.sin_addr.s_addr = inet_addr(host);
+ if (sin.sin_addr.s_addr != -1 && sin.sin_addr.s_addr != 0) {
+ addrtype = AF_INET;
+ } else {
+ hp = gethostbyname(host);
+ if (hp == NULL) {
+ fprintf(stderr, "whois: %s: host unknown\n", host);
+ exit(1);
+ }
+ addrtype = hp->h_addrtype;
+ host = hp->h_name;
+ bcopy(hp->h_addr, &sin.sin_addr, hp->h_length);
+ }
+
+ s = socket(addrtype, SOCK_STREAM, 0);
+ if (s < 0) {
+ perror("whois: socket");
+ exit(2);
+ }
+ sin.sin_family = addrtype;
+ sp = getservbyname("whois", "tcp");
+ if (sp == NULL) {
+ sin.sin_port = htons(IPPORT_WHOIS);
+ }
+ else sin.sin_port = sp->s_port;
+ if (connect(s, (struct sockaddr *) &sin, sizeof (sin)) < 0) {
+ perror("whois: connect");
+ exit(5);
+ }
+ sfi = fdopen(s, "r");
+ sfo = fdopen(s, "w");
+ if (sfi == NULL || sfo == NULL) {
+ perror("fdopen");
+ close(s);
+ exit(1);
+ }
+ fprintf(sfo, "%s\r\n", *argv);
+ fflush(sfo);
+ while ((c = getc(sfi)) != EOF)
+ putchar(c);
+ exit(0);
+ /* NOTREACHED */
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/Makefile b/usr/src/cmd/cmd-inet/usr.lib/Makefile
new file mode 100644
index 0000000000..11a80bfa6b
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/Makefile
@@ -0,0 +1,66 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+SUBDIRS= dhcp dsvclockd ike-certutils in.chargend in.daytimed \
+ in.discardd in.echod in.dhcpd in.iked in.mpathd in.ndpd \
+ in.ripngd in.timed inetd mipagent ncaconfd pppoe slpd wanboot
+MSGSUBDIRS= dsvclockd in.dhcpd inetd ncaconfd wanboot
+
+include ../../Makefile.cmd
+
+POFILES= dsvclockd/dsvclockd.po in.dhcpd/in.dhcpd.po \
+ inetd/inetd.po ncaconfd/ncaconfd.po \
+ wanboot/wanboot.po
+POFILE= usr.lib.po
+
+all:= TARGET= all
+install:= TARGET= install
+clean:= TARGET= clean
+clobber:= TARGET= clobber
+lint:= TARGET= lint
+_msg:= TARGET= _msg
+
+.KEEP_STATE:
+
+all install clean clobber lint: $(SUBDIRS)
+
+_msg: $(MSGSUBDIRS)
+
+#
+# The reason this rule checks for the existence of the
+# Makefile is that some of the directories do not exist
+# in our exportable source builds.
+#
+$(SUBDIRS): FRC
+ @if [ -f $@/Makefile ]; then \
+ cd $@; pwd; $(MAKE) $(TARGET); \
+ else \
+ true; \
+ fi
+
+FRC:
diff --git a/usr/src/cmd/cmd-inet/usr.lib/Makefile.inetsvc b/usr/src/cmd/cmd-inet/usr.lib/Makefile.inetsvc
new file mode 100644
index 0000000000..920d0d65f3
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/Makefile.inetsvc
@@ -0,0 +1,68 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# cmd/cmd-inet/usr.lib/%M%
+#
+
+OBJS = ${PROG}.o
+SRCS = $(OBJS:%.o=%.c)
+
+include ../../../Makefile.cmd
+
+ROOTMANIFESTDIR= $(ROOTSVCNETWORK)
+$(ROOTMANIFEST) := FILEMODE= 444
+
+LDLIBS += -linetsvc
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+ $(LINK.c) $(OBJS) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+include ../Makefile.lib
+
+install: all $(ROOTLIBINETPROG) $(ROOTMANIFEST)
+
+$(ROOTMANIFEST): $(ROOTMANIFESTDIR)
+
+$(ROOTMANIFESTDIR):
+ $(INS.dir)
+
+$(ROOTMANIFESTDIR)/%: %
+ $(INS.file)
+
+check: $(CHKMANIFEST)
+
+clean:
+ $(RM) $(OBJS)
+
+lint: lint_SRCS
+
+include ../../../Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/usr.lib/Makefile.lib b/usr/src/cmd/cmd-inet/usr.lib/Makefile.lib
new file mode 100644
index 0000000000..1e7d9dc6de
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/Makefile.lib
@@ -0,0 +1,37 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 1996 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+# cmd/cmd-inet/usr.lib/Makefile.lib
+
+ROOTLIBINET= $(ROOTLIB)/inet
+ROOTLIBINETPROG= $(PROG:%=$(ROOTLIBINET)/%)
+
+$(ROOTLIBINET)/%: % $(ROOTLIBINET)
+ $(INS.file)
+
+$(ROOTLIBINET):
+ $(INS.dir)
diff --git a/usr/src/cmd/cmd-inet/usr.lib/dhcp/Makefile b/usr/src/cmd/cmd-inet/usr.lib/dhcp/Makefile
new file mode 100644
index 0000000000..545a6af747
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/dhcp/Makefile
@@ -0,0 +1,56 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 2000-2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+# cmd/cmd-inet/usr.lib/dhcp/Makefile
+#
+
+PROG = dhcpconfig dhtadm pntadm
+
+include ../../../Makefile.cmd
+
+ROOTLIBDHCP= $(ROOTLIBINET)/dhcp/svcadm
+
+ROOTLIBDHCPPROG= $(PROG:%=$(ROOTLIBDHCP)/%)
+
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+include ../Makefile.lib
+
+$(ROOTLIBDHCP)/%: % $(ROOTLIBDHCP)
+ $(INS.file)
+
+$(ROOTLIBDHCP): $(ROOTLIBINET)
+ $(INS.dir)
+
+install: all $(ROOTLIBDHCPPROG)
+
+clean lint _msg:
+
+include ../../../Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/usr.lib/dhcp/dhcpconfig.sh b/usr/src/cmd/cmd-inet/usr.lib/dhcp/dhcpconfig.sh
new file mode 100644
index 0000000000..e1b013886d
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/dhcp/dhcpconfig.sh
@@ -0,0 +1,39 @@
+#!/bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+DMDIR=/usr/lib/inet/dhcp/svcadm
+L10NDIR=/usr/share/lib/locale
+WBEMDIR=/usr/sadm/lib/wbem
+
+CLASSPATH=${L10NDIR}:${DMDIR}/dhcpcli.jar:${DMDIR}/dhcpsvc.jar:${DMDIR}/dhcpcommon.jar:${WBEMDIR}/providerutility.jar
+export CLASSPATH
+
+LD_LIBRARY_PATH=${WBEMDIR}
+export LD_LIBRARY_PATH
+
+exec /usr/java/bin/java com.sun.dhcpmgr.cli.dhcpconfig.DhcpCfg "$@"
diff --git a/usr/src/cmd/cmd-inet/usr.lib/dhcp/dhtadm.sh b/usr/src/cmd/cmd-inet/usr.lib/dhcp/dhtadm.sh
new file mode 100644
index 0000000000..7dad0bdd02
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/dhcp/dhtadm.sh
@@ -0,0 +1,35 @@
+#!/bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+DMDIR=/usr/lib/inet/dhcp/svcadm
+L10NDIR=/usr/share/lib/locale
+
+CLASSPATH=${L10NDIR}:${DMDIR}/dhcpcli.jar:${DMDIR}/dhcpsvc.jar:${DMDIR}/dhcpcommon.jar
+export CLASSPATH
+
+exec /usr/java/bin/java com.sun.dhcpmgr.cli.dhtadm.DhtAdm "$@"
diff --git a/usr/src/cmd/cmd-inet/usr.lib/dhcp/pntadm.sh b/usr/src/cmd/cmd-inet/usr.lib/dhcp/pntadm.sh
new file mode 100644
index 0000000000..4b5b972228
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/dhcp/pntadm.sh
@@ -0,0 +1,39 @@
+#!/bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+DMDIR=/usr/lib/inet/dhcp/svcadm
+L10NDIR=/usr/share/lib/locale
+WBEMDIR=/usr/sadm/lib/wbem
+
+CLASSPATH=${L10NDIR}:${DMDIR}/dhcpcli.jar:${DMDIR}/dhcpsvc.jar:${DMDIR}/dhcpcommon.jar:${WBEMDIR}/providerutility.jar
+export CLASSPATH
+
+LD_LIBRARY_PATH=${WBEMDIR}
+export LD_LIBRARY_PATH
+
+exec /usr/java/bin/java com.sun.dhcpmgr.cli.pntadm.PntAdm "$@"
diff --git a/usr/src/cmd/cmd-inet/usr.lib/dsvclockd/Makefile b/usr/src/cmd/cmd-inet/usr.lib/dsvclockd/Makefile
new file mode 100644
index 0000000000..194afd44e6
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/dsvclockd/Makefile
@@ -0,0 +1,63 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+PROG = dsvclockd
+OBJS = dsvclockd.o container.o datastore.o
+SRCS = $(OBJS:.o=.c)
+
+include ../../../Makefile.cmd
+
+POFILE = p$(PROG).po
+POFILES = $(OBJS:.o=.po)
+
+LDLIBS += -ldhcputil -ldhcpsvc
+CPPFLAGS += -D_REENTRANT
+XGETFLAGS += -a
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+ $(LINK.c) -o $@ $(OBJS) $(LDLIBS)
+ $(POST_PROCESS)
+
+include ../Makefile.lib
+
+install: all $(ROOTLIBINETPROG)
+
+clean:
+ $(RM) $(OBJS)
+
+lint: lint_SRCS
+
+$(POFILE): $(POFILES)
+ $(RM) $@
+ $(CAT) $(POFILES) > $@
+
+include ../../../Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/usr.lib/dsvclockd/container.c b/usr/src/cmd/cmd-inet/usr.lib/dsvclockd/container.c
new file mode 100644
index 0000000000..c840be940f
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/dsvclockd/container.c
@@ -0,0 +1,725 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2000-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <synch.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <dhcpmsg.h>
+#include <unistd.h>
+#include <dhcp_svc_private.h>
+
+#include "container.h"
+
+/*
+ * Container locking code -- warning: serious pain ahead.
+ *
+ * This code synchronizes access to a given container across multiple
+ * threads in this (dsvclockd) process, and optionally synchronizes across
+ * multiple instances of dsvclockd running on different hosts. The
+ * synchronization allows multiple readers or a single writer at one time.
+ *
+ * Since by definition there is at most one dsvclockd running per host and
+ * all requests by all threads in all processes running on that host funnel
+ * into it, this code effectively synchronizes access to a given container
+ * across all threads in all processes running on a given host. This means
+ * that the optional synchronization across multiple instances of dsvclockd
+ * on different hosts provides true cross-host synchronization for all
+ * threads in all processes on all cooperating machines (though all hosts
+ * must have write access to a common directory).
+ *
+ * The container synchronization here should be viewed as a two step
+ * process, where the first step is optional:
+ *
+ * 1. Synchronize access across the set of cooperating dsvclockd's
+ * on multiple hosts. This is known as acquiring the host lock.
+ *
+ * 2. Synchronize access across the set of threads running inside
+ * this dsvclockd process. This is known as acquiring the
+ * intra-process lock.
+ *
+ * In order to implement the first (host lock) step, we use fcntl()-based
+ * file locking on a file inside an NFS-shared directory and rely on NFS to
+ * do our synchronization for us. Note that this can only be used to
+ * implement the first step since fcntl()-based locks are process locks,
+ * and the effects of using these locks with multiple threads are not
+ * defined. Furthermore, note that this means it requires some fancy
+ * footwork to ensure that only one thread in a given dsvclockd process
+ * tries to acquire the fcntl() lock for that process.
+ *
+ * In order to implement the second step, we use custom-made reader-writer
+ * locks since the stock Solaris ones don't quite have the semantics we
+ * need -- in particular, we need to relax the requirement that the thread
+ * which acquired the lock is the one releasing it.
+ *
+ * Lock ordering guidelines:
+ *
+ * For the most part, this code does not acquire more than one container
+ * lock at a time -- whenever feasible, please do the same. If you must
+ * acquire more than one lock at a time, the correct order is:
+ *
+ * 1. cn_nholds_lock
+ * 2. cn_lock
+ * 3. cn_hlock_lock
+ */
+
+static int host_lock(dsvcd_container_t *, int, boolean_t);
+static int host_unlock(dsvcd_container_t *);
+static unsigned int cn_nlocks(dsvcd_container_t *);
+
+/*
+ * Create a container identified by `cn_id'; returns an instance of the new
+ * container upon success, or NULL on failure. Note that `cn_id' is
+ * treated as a pathname and thus must be a unique name for the container
+ * across all containers, container versions, and datastores -- additionally,
+ * if `crosshost' is set, then the directory named by `cn_id' must be a
+ * directory mounted on all cooperating hosts.
+ */
+dsvcd_container_t *
+cn_create(const char *cn_id, boolean_t crosshost)
+{
+ dsvcd_container_t *cn;
+
+ dhcpmsg(MSG_VERBOSE, "creating %scontainer synchpoint `%s'", crosshost ?
+ "crosshost " : "", cn_id);
+
+ cn = calloc(1, sizeof (dsvcd_container_t));
+ if (cn == NULL)
+ return (NULL);
+
+ cn->cn_id = strdup(cn_id);
+ if (cn->cn_id == NULL) {
+ free(cn);
+ return (NULL);
+ }
+
+ (void) mutex_init(&cn->cn_lock, USYNC_THREAD, NULL);
+ (void) mutex_init(&cn->cn_hlock_lock, USYNC_THREAD, NULL);
+ (void) mutex_init(&cn->cn_nholds_lock, USYNC_THREAD, NULL);
+
+ (void) cond_init(&cn->cn_hlockcv, USYNC_THREAD, NULL);
+
+ cn->cn_whead = NULL;
+ cn->cn_wtail = NULL;
+ cn->cn_nholds = 0;
+ cn->cn_closing = B_FALSE;
+ cn->cn_crosshost = crosshost;
+ cn->cn_hlockstate = CN_HUNLOCKED;
+ cn->cn_hlockcount = 0;
+
+ return (cn);
+}
+
+/*
+ * Destroy container `cn'; wait a decent amount of time for activity on the
+ * container to quiesce first. If the caller has not prohibited other
+ * threads from calling into the container yet, this may take a long time.
+ */
+void
+cn_destroy(dsvcd_container_t *cn)
+{
+ unsigned int attempts;
+ unsigned int nstalelocks;
+
+ dhcpmsg(MSG_VERBOSE, "destroying container synchpoint `%s'", cn->cn_id);
+
+ (void) mutex_lock(&cn->cn_lock);
+ cn->cn_closing = B_TRUE;
+ (void) mutex_unlock(&cn->cn_lock);
+
+ /*
+ * Wait for up to CN_DESTROY_WAIT seconds for all the lock holders
+ * to relinquish their locks. If the container has locks that seem
+ * to be stale, then warn the user before destroying it. The locks
+ * will be unlocked automatically when we exit.
+ */
+ for (attempts = 0; attempts < CN_DESTROY_WAIT; attempts++) {
+ nstalelocks = cn_nlocks(cn);
+ if (nstalelocks == 0)
+ break;
+
+ (void) sleep(1);
+ }
+
+ if (nstalelocks == 1) {
+ dhcpmsg(MSG_WARNING, "unlocking stale lock on "
+ "container `%s'", cn->cn_id);
+ } else if (nstalelocks != 0) {
+ dhcpmsg(MSG_WARNING, "unlocking %d stale locks on "
+ "container `%s'", nstalelocks, cn->cn_id);
+ }
+
+ (void) cond_destroy(&cn->cn_hlockcv);
+ (void) mutex_destroy(&cn->cn_nholds_lock);
+ (void) mutex_destroy(&cn->cn_hlock_lock);
+ (void) mutex_destroy(&cn->cn_lock);
+
+ free(cn->cn_id);
+ free(cn);
+}
+
+/*
+ * Wait (block) until a lock of type `locktype' is obtained on container
+ * `cn'. Returns a DSVC_* return code; if DSVC_SUCCESS is returned, then
+ * the lock is held upon return. Must be called with the container's
+ * cn_nholds_lock held on entry; returns with it unlocked.
+ */
+static int
+cn_wait_for_lock(dsvcd_container_t *cn, dsvcd_locktype_t locktype)
+{
+ dsvcd_waitlist_t waititem;
+ int retval = DSVC_SUCCESS;
+
+ assert(MUTEX_HELD(&cn->cn_nholds_lock));
+ assert(cn->cn_nholds != 0);
+
+ waititem.wl_next = NULL;
+ waititem.wl_prev = NULL;
+ waititem.wl_locktype = locktype;
+ (void) cond_init(&waititem.wl_cv, USYNC_THREAD, NULL);
+
+ /*
+ * Chain our stack-local waititem onto the list; this keeps us from
+ * having to worry about allocation failures and also makes it easy
+ * for cn_unlock() to just pull us off the list without worrying
+ * about freeing the memory.
+ *
+ * Note that we can do this because by definition we are blocked in
+ * this function until we are signalled.
+ */
+ if (cn->cn_whead != NULL) {
+ waititem.wl_prev = cn->cn_wtail;
+ cn->cn_wtail->wl_next = &waititem;
+ cn->cn_wtail = &waititem;
+ } else {
+ cn->cn_whead = &waititem;
+ cn->cn_wtail = &waititem;
+ }
+
+ do {
+ if (cond_wait(&waititem.wl_cv, &cn->cn_nholds_lock) != 0) {
+ dhcpmsg(MSG_DEBUG, "cn_wait_for_lock: cond_wait error");
+ retval = DSVC_INTERNAL;
+ break;
+ }
+ } while ((locktype == DSVCD_RDLOCK && cn->cn_nholds == -1) ||
+ (locktype == DSVCD_WRLOCK && cn->cn_nholds != 0));
+
+ (void) cond_destroy(&waititem.wl_cv);
+
+ assert(MUTEX_HELD(&cn->cn_nholds_lock));
+
+ /*
+ * We got woken up; pull ourselves off of the local waitlist.
+ */
+ if (waititem.wl_prev != NULL)
+ waititem.wl_prev->wl_next = waititem.wl_next;
+ else
+ cn->cn_whead = waititem.wl_next;
+
+ if (waititem.wl_next != NULL)
+ waititem.wl_next->wl_prev = waititem.wl_prev;
+ else
+ cn->cn_wtail = waititem.wl_prev;
+
+ if (retval == DSVC_SUCCESS) {
+ if (locktype == DSVCD_WRLOCK)
+ cn->cn_nholds = -1;
+ else
+ cn->cn_nholds++;
+ }
+
+ /*
+ * If we just acquired a read lock and the next waiter is waiting
+ * for a readlock too, signal the waiter. Note that we wake each
+ * reader up one-by-one like this to avoid excessive contention on
+ * cn_nholds_lock.
+ */
+ if (locktype == DSVCD_RDLOCK && cn->cn_whead != NULL &&
+ cn->cn_whead->wl_locktype == DSVCD_RDLOCK)
+ (void) cond_signal(&cn->cn_whead->wl_cv);
+
+ (void) mutex_unlock(&cn->cn_nholds_lock);
+ return (retval);
+}
+
+/*
+ * Lock container `cn' for reader (shared) access. If the container cannot
+ * be locked immediately (there is currently a writer lock held or a writer
+ * lock waiting for the lock), then if `nonblock' is B_TRUE, DSVC_BUSY is
+ * returned. Otherwise, block until the lock can be obtained. Returns a
+ * DSVC_* code.
+ */
+int
+cn_rdlock(dsvcd_container_t *cn, boolean_t nonblock)
+{
+ int retval;
+
+ /*
+ * The container is going away; no new lock requests.
+ */
+ (void) mutex_lock(&cn->cn_lock);
+ if (cn->cn_closing) {
+ (void) mutex_unlock(&cn->cn_lock);
+ return (DSVC_SYNCH_ERR);
+ }
+ (void) mutex_unlock(&cn->cn_lock);
+
+ /*
+ * See if we can grab the lock without having to block; only
+ * possible if we can acquire the host lock without blocking, if
+ * the lock is not currently owned by a writer and if there are no
+ * writers currently enqueued for accessing this lock (we know that
+ * if there's a waiter it must be a writer since this code doesn't
+ * enqueue readers until there's a writer enqueued). We enqueue
+ * these requests to improve fairness.
+ */
+ (void) mutex_lock(&cn->cn_nholds_lock);
+
+ if (cn->cn_nholds != -1 && cn->cn_whead == NULL &&
+ host_lock(cn, F_RDLCK, B_TRUE) == DSVC_SUCCESS) {
+ cn->cn_nholds++;
+ (void) mutex_unlock(&cn->cn_nholds_lock);
+ return (DSVC_SUCCESS);
+ }
+
+ (void) mutex_unlock(&cn->cn_nholds_lock);
+
+ /*
+ * Cannot grab the lock without blocking somewhere; wait until we
+ * can grab the host lock, then with that lock held obtain our
+ * intra-process lock.
+ */
+ if (nonblock)
+ return (DSVC_BUSY);
+ retval = host_lock(cn, F_RDLCK, B_FALSE);
+ if (retval != DSVC_SUCCESS)
+ return (retval);
+
+ /*
+ * We've got the read lock; if there aren't any writers currently
+ * contending for our intra-process lock then succeed immediately.
+ * It's possible for there to be waiters but for nholds to be zero
+ * via the following scenario:
+ *
+ * 1. The last holder of a lock unlocks, dropping nholds to
+ * zero and signaling the head waiter on the waitlist.
+ *
+ * 2. The last holder drops cn_nholds_lock.
+ *
+ * 3. We acquire cn_nholds_lock before the signaled waiter
+ * does.
+ *
+ * Note that this case won't cause a deadlock even if we didn't
+ * check for it here (when the waiter finally gets cn_nholds_lock,
+ * it'll find that the waitlist is once again non-NULL, and signal
+ * the us). However, as an optimization, handle the case here.
+ */
+ (void) mutex_lock(&cn->cn_nholds_lock);
+ if (cn->cn_nholds != -1 &&
+ (cn->cn_whead == NULL || cn->cn_nholds == 0)) {
+ cn->cn_nholds++;
+ (void) mutex_unlock(&cn->cn_nholds_lock);
+ return (DSVC_SUCCESS);
+ }
+
+ /* cn_wait_for_lock() will drop cn_nholds_lock */
+ retval = cn_wait_for_lock(cn, DSVCD_RDLOCK);
+ if (retval != DSVC_SUCCESS) {
+ (void) host_unlock(cn);
+ return (retval);
+ }
+ return (DSVC_SUCCESS);
+}
+
+/*
+ * Lock container `cn' for writer (exclusive) access. If the container
+ * cannot be locked immediately (there are currently readers or a writer),
+ * then if `nonblock' is B_TRUE, DSVC_BUSY is returned. Otherwise, block
+ * until the lock can be obtained. Returns a DSVC_* code.
+ */
+int
+cn_wrlock(dsvcd_container_t *cn, boolean_t nonblock)
+{
+ int retval;
+
+ /*
+ * The container is going away; no new lock requests.
+ */
+ (void) mutex_lock(&cn->cn_lock);
+ if (cn->cn_closing) {
+ (void) mutex_unlock(&cn->cn_lock);
+ return (DSVC_SYNCH_ERR);
+ }
+ (void) mutex_unlock(&cn->cn_lock);
+
+ /*
+ * See if we can grab the lock without having to block; only
+ * possible if there are no current writers within our process and
+ * that we can immediately acquire the host lock.
+ */
+ (void) mutex_lock(&cn->cn_nholds_lock);
+
+ if (cn->cn_nholds == 0 &&
+ host_lock(cn, F_WRLCK, B_TRUE) == DSVC_SUCCESS) {
+ cn->cn_nholds = -1;
+ (void) mutex_unlock(&cn->cn_nholds_lock);
+ return (DSVC_SUCCESS);
+ }
+
+ (void) mutex_unlock(&cn->cn_nholds_lock);
+
+ /*
+ * Cannot grab the lock without blocking somewhere; wait until we
+ * can grab the host lock, then with that lock held obtain our
+ * intra-process lock.
+ */
+ if (nonblock)
+ return (DSVC_BUSY);
+ retval = host_lock(cn, F_WRLCK, B_FALSE);
+ if (retval != DSVC_SUCCESS)
+ return (retval);
+
+ /*
+ * We've got the host lock; if there aren't any writers currently
+ * contending for our intra-process lock then succeed immediately.
+ */
+ (void) mutex_lock(&cn->cn_nholds_lock);
+ if (cn->cn_nholds == 0) {
+ cn->cn_nholds = -1;
+ (void) mutex_unlock(&cn->cn_nholds_lock);
+ return (DSVC_SUCCESS);
+ }
+
+ /* cn_wait_for_lock() will drop cn_nholds_lock */
+ retval = cn_wait_for_lock(cn, DSVCD_WRLOCK);
+ if (retval != DSVC_SUCCESS) {
+ (void) host_unlock(cn);
+ return (retval);
+ }
+ return (DSVC_SUCCESS);
+}
+
+/*
+ * Unlock reader or writer lock on container `cn'; returns a DSVC_* code
+ */
+int
+cn_unlock(dsvcd_container_t *cn)
+{
+ (void) mutex_lock(&cn->cn_nholds_lock);
+
+ if (cn->cn_nholds == 0) {
+ (void) mutex_unlock(&cn->cn_nholds_lock);
+ return (DSVC_SYNCH_ERR);
+ }
+
+ if (cn->cn_nholds != -1 && cn->cn_nholds != 1) {
+ cn->cn_nholds--;
+ (void) host_unlock(cn);
+ (void) mutex_unlock(&cn->cn_nholds_lock);
+ return (DSVC_SUCCESS);
+ }
+
+ /*
+ * The last reader or a writer just unlocked -- signal the first
+ * waiter. To avoid a thundering herd, we only signal the first
+ * waiter, even if there are multiple readers ready to go --
+ * instead, each reader is responsible for signaling the next
+ * in cn_wait_for_lock().
+ */
+ cn->cn_nholds = 0;
+ if (cn->cn_whead != NULL)
+ (void) cond_signal(&cn->cn_whead->wl_cv);
+
+ (void) host_unlock(cn);
+ (void) mutex_unlock(&cn->cn_nholds_lock);
+
+ return (DSVC_SUCCESS);
+}
+
+/*
+ * Find out what kind of lock is on `cn'. Note that this is just a
+ * snapshot in time and without additional locks the answer may be invalid
+ * by the time the function returns.
+ */
+dsvcd_locktype_t
+cn_locktype(dsvcd_container_t *cn)
+{
+ int nholds;
+
+ (void) mutex_lock(&cn->cn_nholds_lock);
+ nholds = cn->cn_nholds;
+ (void) mutex_unlock(&cn->cn_nholds_lock);
+
+ if (nholds == 0)
+ return (DSVCD_NOLOCK);
+ else if (nholds > 0)
+ return (DSVCD_RDLOCK);
+ else
+ return (DSVCD_WRLOCK);
+}
+
+/*
+ * Obtain a lock of type `locktype' on container `cn' such that we have
+ * shared or exclusive access to this container across all hosts. If
+ * `nonblock' is true and the lock cannot be obtained return DSVC_BUSY. If
+ * the lock is already held, the number of instances of the lock "checked
+ * out" by this host is incremented.
+ */
+static int
+host_lock(dsvcd_container_t *cn, int locktype, boolean_t nonblock)
+{
+ struct flock flock;
+ int fd;
+ char *basename, lockpath[MAXPATHLEN];
+ int error;
+
+ if (!cn->cn_crosshost)
+ return (DSVC_SUCCESS);
+
+ /*
+ * Before we wait for a while, see if the container is going away;
+ * if so, fail now so the container can drain quicker..
+ */
+ (void) mutex_lock(&cn->cn_lock);
+ if (cn->cn_closing) {
+ (void) mutex_unlock(&cn->cn_lock);
+ return (DSVC_SYNCH_ERR);
+ }
+ (void) mutex_unlock(&cn->cn_lock);
+
+ /*
+ * Note that we only wait if (1) there's already a thread trying to
+ * grab the host lock on our host or if (2) this host currently
+ * holds a host shared lock and we need an exclusive lock. Note
+ * that we do *not* wait in the following situations:
+ *
+ * * This host holds an exclusive host lock and another
+ * exclusive host lock request comes in. We rely on the
+ * intra-process lock to do the synchronization.
+ *
+ * * This host holds an exclusive host lock and a shared host
+ * lock request comes in. Since this host already has
+ * exclusive access, we already implicitly hold the shared
+ * host lock as far as this host is concerned, so just rely
+ * on the intra-process lock to do the synchronization.
+ *
+ * These semantics make sense as long as one remembers that the
+ * host lock merely provides exclusive or shared access for a given
+ * host or set of hosts -- that is, exclusive access is exclusive
+ * access for that machine, not for the given request.
+ */
+ (void) mutex_lock(&cn->cn_hlock_lock);
+
+ while (cn->cn_hlockstate == CN_HPENDING ||
+ cn->cn_hlockstate == CN_HRDLOCKED && locktype == F_WRLCK) {
+ if (nonblock) {
+ (void) mutex_unlock(&cn->cn_hlock_lock);
+ return (DSVC_BUSY);
+ }
+
+ if (cond_wait(&cn->cn_hlockcv, &cn->cn_hlock_lock) != 0) {
+ (void) mutex_unlock(&cn->cn_hlock_lock);
+ return (DSVC_SYNCH_ERR);
+ }
+ }
+
+ if (cn->cn_hlockstate == CN_HRDLOCKED ||
+ cn->cn_hlockstate == CN_HWRLOCKED) {
+ /*
+ * Already locked; just bump the held lock count.
+ */
+ assert(cn->cn_hlockcount > 0);
+ cn->cn_hlockcount++;
+ (void) mutex_unlock(&cn->cn_hlock_lock);
+ return (DSVC_SUCCESS);
+ }
+
+ /*
+ * We're the thread that's going to try to acquire the host lock.
+ */
+
+ assert(cn->cn_hlockcount == 0);
+
+ /*
+ * Create the lock file as a hidden file in the directory named by
+ * cn_id. So if cn_id is /var/dhcp/SUNWfiles1_dhcptab, we want the
+ * lock file to be /var/dhcp/.SUNWfiles1_dhcptab.lock. Please, no
+ * giggles about the snprintf().
+ */
+ basename = strrchr(cn->cn_id, '/');
+ if (basename == NULL)
+ basename = cn->cn_id;
+ else
+ basename++;
+
+ (void) snprintf(lockpath, MAXPATHLEN, "%.*s.%s.lock",
+ basename - cn->cn_id, cn->cn_id, basename);
+ fd = open(lockpath, O_RDWR|O_CREAT, 0600);
+ if (fd == -1) {
+ (void) mutex_unlock(&cn->cn_hlock_lock);
+ return (DSVC_SYNCH_ERR);
+ }
+
+ cn->cn_hlockstate = CN_HPENDING;
+ (void) mutex_unlock(&cn->cn_hlock_lock);
+
+ flock.l_len = 0;
+ flock.l_type = locktype;
+ flock.l_start = 0;
+ flock.l_whence = SEEK_SET;
+
+ if (fcntl(fd, nonblock ? F_SETLK : F_SETLKW, &flock) == -1) {
+ /*
+ * For some reason we couldn't acquire the lock. Reset the
+ * host lock state to "unlocked" and signal another thread
+ * (if there's one waiting) to pick up where we left off.
+ */
+ error = errno;
+ (void) mutex_lock(&cn->cn_hlock_lock);
+ cn->cn_hlockstate = CN_HUNLOCKED;
+ (void) cond_signal(&cn->cn_hlockcv);
+ (void) mutex_unlock(&cn->cn_hlock_lock);
+ (void) close(fd);
+ return (error == EAGAIN ? DSVC_BUSY : DSVC_SYNCH_ERR);
+ }
+
+ /*
+ * Got the lock; wake up all the waiters since they can all succeed
+ */
+ (void) mutex_lock(&cn->cn_hlock_lock);
+ cn->cn_hlockstate = (locktype == F_WRLCK ? CN_HWRLOCKED : CN_HRDLOCKED);
+ cn->cn_hlockcount++;
+ cn->cn_hlockfd = fd;
+ (void) cond_broadcast(&cn->cn_hlockcv);
+ (void) mutex_unlock(&cn->cn_hlock_lock);
+
+ return (DSVC_SUCCESS);
+}
+
+/*
+ * Unlock a checked out instance of a shared or exclusive lock on container
+ * `cn'; if the number of checked out instances goes to zero, then the host
+ * lock is unlocked so that other hosts may compete for it.
+ */
+static int
+host_unlock(dsvcd_container_t *cn)
+{
+ struct flock flock;
+
+ if (!cn->cn_crosshost)
+ return (DSVC_SUCCESS);
+
+ assert(cn->cn_hlockcount > 0);
+
+ (void) mutex_lock(&cn->cn_hlock_lock);
+ if (cn->cn_hlockcount > 1) {
+ /*
+ * Not the last unlock by this host; just decrement the
+ * held lock count.
+ */
+ cn->cn_hlockcount--;
+ (void) mutex_unlock(&cn->cn_hlock_lock);
+ return (DSVC_SUCCESS);
+ }
+
+ flock.l_len = 0;
+ flock.l_type = F_UNLCK;
+ flock.l_start = 0;
+ flock.l_whence = SEEK_SET;
+
+ if (fcntl(cn->cn_hlockfd, F_SETLK, &flock) == -1) {
+ (void) mutex_unlock(&cn->cn_hlock_lock);
+ return (DSVC_SYNCH_ERR);
+ }
+
+ /*
+ * Note that we don't unlink the lockfile for a number of reasons,
+ * the most blatant reason being:
+ *
+ * 1. Several hosts lock the lockfile for shared access.
+ * 2. One host unlocks the lockfile and unlinks it (here).
+ * 3. Another host comes in, goes to exclusively lock the
+ * lockfile, finds no lockfile, and creates a new one
+ * (meanwhile, the other hosts are still accessing the
+ * container through the unlinked lockfile).
+ *
+ * We could put in some hairy code to try to unlink lockfiles
+ * elsewhere (when possible), but it hardly seems worth it since
+ * inodes are cheap.
+ */
+
+ (void) close(cn->cn_hlockfd);
+ cn->cn_hlockcount = 0;
+ cn->cn_hlockstate = CN_HUNLOCKED;
+ /*
+ * We need to signal `cn_hlockcv' in case there are threads which
+ * are waiting on it to attempt flock() exclusive access (see the
+ * comments in host_lock() for more details about this case).
+ */
+ (void) cond_signal(&cn->cn_hlockcv);
+ (void) mutex_unlock(&cn->cn_hlock_lock);
+
+ return (DSVC_SUCCESS);
+}
+
+/*
+ * Return the number of locks currently held for container `cn'.
+ */
+static unsigned int
+cn_nlocks(dsvcd_container_t *cn)
+{
+ unsigned int nlocks;
+
+ (void) mutex_lock(&cn->cn_nholds_lock);
+ (void) mutex_lock(&cn->cn_hlock_lock);
+
+ switch (cn->cn_nholds) {
+ case 0:
+ nlocks = cn->cn_hlockcount;
+ break;
+ case -1:
+ nlocks = 1;
+ break;
+ default:
+ nlocks = cn->cn_nholds;
+ break;
+ }
+
+ dhcpmsg(MSG_DEBUG, "cn_nlocks: nholds=%d hlockstate=%d hlockcount=%d",
+ cn->cn_nholds, cn->cn_hlockstate, cn->cn_hlockcount);
+
+ (void) mutex_unlock(&cn->cn_hlock_lock);
+ (void) mutex_unlock(&cn->cn_nholds_lock);
+
+ return (nlocks);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/dsvclockd/container.h b/usr/src/cmd/cmd-inet/usr.lib/dsvclockd/container.h
new file mode 100644
index 0000000000..45694dcf62
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/dsvclockd/container.h
@@ -0,0 +1,122 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2000 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef DSVCD_CONTAINER_H
+#define DSVCD_CONTAINER_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <synch.h>
+
+#include "dsvclockd.h"
+
+/*
+ * Container-related data structures, functions and constants. See
+ * comments in container.c for a description of how to use the exported
+ * functions.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Number of seconds to wait for container lockholders to relinquish all
+ * locks on a given container (when it's being destroyed).
+ */
+#define CN_DESTROY_WAIT 60
+
+/*
+ * Describes a thread waiting to access a given container; exactly one per
+ * waiting thread.
+ */
+typedef struct dsvcd_waitlist {
+ struct dsvcd_waitlist *wl_next; /* next waiter in list */
+ struct dsvcd_waitlist *wl_prev; /* prev waiter in list */
+ cond_t wl_cv; /* our condition variable */
+ dsvcd_locktype_t wl_locktype; /* type of lock we want */
+} dsvcd_waitlist_t;
+
+/*
+ * States for the host lock state machine. The state machine is a simple
+ * cycle of UNLOCKED->PENDING->{RD,WR}LOCKED->UNLOCKED->...
+ */
+enum cn_hlockstate { CN_HUNLOCKED, CN_HPENDING, CN_HRDLOCKED, CN_HWRLOCKED };
+
+/*
+ * Describes a given container within a datastore. There is at most one of
+ * these per datastore container (there may be none if there are no current
+ * consumers of a given container within a datastore). If there is more
+ * than one open handle to a given container (through multiple calls to
+ * open_d?()) there will still only be one dsvcd_container_t for that
+ * container. This object is used to synchronize access to an underlying
+ * container through use of its custom reader/writer lock (it can't use the
+ * rwlock_t's built into Solaris because we need locks that do not care if
+ * the unlocking thread is the same as the locking thread). It also
+ * contains other per-container information like the container id.
+ */
+typedef struct dsvcd_container {
+ char *cn_id; /* container's id */
+ boolean_t cn_crosshost; /* synchronize across hosts */
+ boolean_t cn_closing; /* container is going away */
+ mutex_t cn_lock; /* protects preceding fields */
+
+ dsvcd_waitlist_t *cn_whead; /* head of wait list */
+ dsvcd_waitlist_t *cn_wtail; /* tail of wait list */
+ int cn_nholds; /* num readers (-1 == writer) */
+ mutex_t cn_nholds_lock; /* for nholds and waitlist */
+
+ int cn_hlockfd; /* host lock file descriptor */
+ int cn_hlockcount; /* current # of host locks */
+ enum cn_hlockstate cn_hlockstate; /* host lock state */
+ cond_t cn_hlockcv; /* host lock condvar */
+ mutex_t cn_hlock_lock; /* mutex for cn_hlock* */
+
+ /*
+ * These fields are used to keep metadata state regarding the
+ * container and are actually maintained by the containing
+ * datastore, not the container.
+ */
+ uint_t cn_nout; /* number checked out */
+ time_t cn_lastrel; /* last released */
+ uint32_t cn_idhash; /* hash before modulation */
+ struct dsvcd_container *cn_next; /* hash chain next */
+ struct dsvcd_container *cn_prev; /* hash chain prev */
+} dsvcd_container_t;
+
+extern dsvcd_container_t *cn_create(const char *, boolean_t);
+extern void cn_destroy(dsvcd_container_t *);
+extern int cn_rdlock(dsvcd_container_t *, boolean_t);
+extern int cn_wrlock(dsvcd_container_t *, boolean_t);
+extern int cn_unlock(dsvcd_container_t *);
+extern dsvcd_locktype_t cn_locktype(dsvcd_container_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DSVCD_CONTAINER_H */
diff --git a/usr/src/cmd/cmd-inet/usr.lib/dsvclockd/datastore.c b/usr/src/cmd/cmd-inet/usr.lib/dsvclockd/datastore.c
new file mode 100644
index 0000000000..c6cc98eddf
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/dsvclockd/datastore.c
@@ -0,0 +1,312 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stropts.h>
+#include <synch.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <dhcp_svc_private.h>
+#include <sys/time.h>
+#include <dhcpmsg.h>
+
+#include "dsvclockd.h"
+#include "datastore.h"
+
+static uint32_t ds_hash(const char *);
+
+/*
+ * Create a datastore named `ds_name' and a door which will service requests
+ * for this datastore. When the door is called, callback `ds_callback'.
+ * Returns the created datastore.
+ */
+dsvcd_datastore_t *
+ds_create(const char *ds_name, dsvcd_svc_t *ds_callback)
+{
+ char door_path[MAXPATHLEN];
+ dsvcd_datastore_t *ds = NULL;
+ int fd;
+ unsigned int i;
+ door_info_t info;
+
+ dhcpmsg(MSG_VERBOSE, "managing locks for datastore `%s'", ds_name);
+
+ ds = malloc(sizeof (dsvcd_datastore_t));
+ if (ds == NULL) {
+ dhcpmsg(MSG_ERR, "cannot manage locks for datastore `%s'",
+ ds_name);
+ return (NULL);
+ }
+
+ ds->ds_name = strdup(ds_name);
+ if (ds->ds_name == NULL) {
+ dhcpmsg(MSG_ERR, "cannot manage locks for datastore `%s'",
+ ds_name);
+ free(ds);
+ return (NULL);
+ }
+
+ ds->ds_doorfd = door_create((void (*)())ds_callback, ds,
+ DOOR_REFUSE_DESC | DOOR_NO_CANCEL);
+ if (ds->ds_doorfd == -1) {
+ dhcpmsg(MSG_ERR, "cannot create door for datastore `%s'",
+ ds_name);
+ free(ds->ds_name);
+ free(ds);
+ return (NULL);
+ }
+
+ for (i = 0; i < DSVCD_DS_HASH_SIZE; i++) {
+ ds->ds_hash[i].cl_head = NULL;
+ (void) mutex_init(&ds->ds_hash[i].cl_lock, USYNC_THREAD, 0);
+ }
+
+ /*
+ * Create the door name in the filesystem. First, check to see if
+ * a door already exists at the specified pathname. If it does,
+ * and the server process (no doubt another copy of us) is already
+ * running, then fail. Otherwise, unlink the old door and fattach
+ * a new one.
+ */
+ (void) snprintf(door_path, sizeof (door_path), DSVCD_DOOR_FMT, ds_name);
+
+ fd = open(door_path, O_RDWR);
+ if (fd != -1) {
+ if (door_info(fd, &info) == 0 && info.di_target != -1) {
+ dhcpmsg(MSG_ERROR, "%s is in use by process %u",
+ door_path, info.di_target);
+ (void) close(fd);
+ (void) close(ds->ds_doorfd);
+ free(ds->ds_name);
+ free(ds);
+ return (NULL);
+ }
+ (void) close(fd);
+ (void) unlink(door_path);
+ }
+
+ fd = open(door_path, O_CREAT|O_EXCL|O_RDWR, 0644);
+ if (fd == -1) {
+ dhcpmsg(MSG_ERR, "cannot create door rendezvous for datastore "
+ "`%s'", ds_name);
+ (void) close(ds->ds_doorfd);
+ free(ds->ds_name);
+ free(ds);
+ return (NULL);
+ }
+ (void) close(fd);
+
+ /*
+ * Attach the door onto the name
+ */
+ if (fattach(ds->ds_doorfd, door_path) == -1) {
+ dhcpmsg(MSG_ERR, "cannot fattach door rendezvous for datastore "
+ "`%s'", ds_name);
+ (void) close(ds->ds_doorfd);
+ free(ds->ds_name);
+ free(ds);
+ return (NULL);
+ }
+
+ return (ds);
+}
+
+/*
+ * Destroy a datastore `ds' and its associated containers, and remove
+ * its door from the filesystem.
+ */
+void
+ds_destroy(dsvcd_datastore_t *ds)
+{
+ unsigned int i;
+ char door_path[MAXPATHLEN];
+ dsvcd_container_t *cn, *cn_next;
+
+ dhcpmsg(MSG_VERBOSE, "stopping lock management for datastore `%s'",
+ ds->ds_name);
+
+ /*
+ * Detach and revoke access to the door. The detach makes it so
+ * new callers who open the door will fail; the revoke makes it
+ * so that callers that already have a door descriptor will fail.
+ * We do this prior to calling cn_destroy() to make it easier for
+ * the container lockcount to drain.
+ */
+ (void) snprintf(door_path, MAXPATHLEN, DSVCD_DOOR_FMT, ds->ds_name);
+ (void) fdetach(door_path);
+ (void) unlink(door_path);
+ (void) door_revoke(ds->ds_doorfd);
+ (void) close(ds->ds_doorfd);
+
+ /*
+ * Destroy all the underlying containers. We're single-threaded at
+ * this point, so don't worry about locks.
+ */
+ for (i = 0; i < DSVCD_DS_HASH_SIZE; i++) {
+ for (cn = ds->ds_hash[i].cl_head; cn != NULL; cn = cn_next) {
+ cn_next = cn->cn_next;
+ cn_destroy(cn);
+ }
+ (void) mutex_destroy(&ds->ds_hash[i].cl_lock);
+ }
+
+ free(ds->ds_name);
+ free(ds);
+}
+
+/*
+ * Get a container with id `cn_id' from datastore `ds'; create the
+ * container if it does not exist. If `crosshost' is set and the container
+ * does not yet exist, then the container will synchronize across hosts. .
+ * If the container cannot be found or created, NULL is returned. When the
+ * calling thread is done with the container, ds_release_container() must
+ * be called.
+ */
+dsvcd_container_t *
+ds_get_container(dsvcd_datastore_t *ds, const char *cn_id, boolean_t crosshost)
+{
+ dsvcd_container_list_t *cn_list;
+ dsvcd_container_t *cn;
+ uint32_t idhash = ds_hash(cn_id);
+
+ cn_list = &ds->ds_hash[idhash % DSVCD_DS_HASH_SIZE];
+ (void) mutex_lock(&cn_list->cl_lock);
+
+ for (cn = cn_list->cl_head; cn != NULL; cn = cn->cn_next) {
+ if (idhash == cn->cn_idhash && strcmp(cn_id, cn->cn_id) == 0)
+ break;
+ }
+
+ if (cn == NULL) {
+ cn = cn_create(cn_id, crosshost);
+ if (cn != NULL) {
+ if (cn_list->cl_head != NULL)
+ cn_list->cl_head->cn_prev = cn;
+
+ cn->cn_next = cn_list->cl_head;
+ cn->cn_prev = NULL;
+ cn_list->cl_head = cn;
+ cn->cn_idhash = idhash;
+ cn->cn_nout = 0;
+ cn->cn_lastrel = 0;
+ }
+ }
+
+ if (cn != NULL)
+ cn->cn_nout++;
+
+ (void) mutex_unlock(&cn_list->cl_lock);
+ return (cn);
+}
+
+/*
+ * Release a container `cn' belonging to datastore `ds'. Once a container
+ * has been released, it can no longer be used by the releasing thread.
+ * Used to track the number of active instances of a container.
+ */
+void
+ds_release_container(dsvcd_datastore_t *ds, dsvcd_container_t *cn)
+{
+ dsvcd_container_list_t *cn_list;
+ uint32_t idhash = ds_hash(cn->cn_id);
+
+ cn_list = &ds->ds_hash[idhash % DSVCD_DS_HASH_SIZE];
+
+ (void) mutex_lock(&cn_list->cl_lock);
+
+ cn->cn_nout--;
+ cn->cn_lastrel = time(NULL);
+
+ (void) mutex_unlock(&cn_list->cl_lock);
+}
+
+/*
+ * Destroy any containers in datastore `ds' that have not been accessed in
+ * the last `idle' seconds. Return the number of destroyed (reaped)
+ * containers.
+ */
+unsigned int
+ds_reap_containers(dsvcd_datastore_t *ds, unsigned int idle)
+{
+ dsvcd_container_list_t *cn_list;
+ dsvcd_container_t *cn, *cn_next;
+ unsigned int i, nreaped = 0;
+
+ for (i = 0; i < DSVCD_DS_HASH_SIZE; i++) {
+ cn_list = &ds->ds_hash[i];
+
+ (void) mutex_lock(&cn_list->cl_lock);
+ for (cn = cn_list->cl_head; cn != NULL; cn = cn_next) {
+ cn_next = cn->cn_next;
+
+ /*
+ * Since a container is not checked out across a
+ * lock operation, we must check if the lock is
+ * held as well as the number of instances checked
+ * out.
+ */
+ if (cn->cn_nout != 0 ||
+ cn_locktype(cn) != DSVCD_NOLOCK ||
+ cn->cn_lastrel + idle >= time(NULL))
+ continue;
+
+ if (cn == cn_list->cl_head)
+ cn_list->cl_head = cn->cn_next;
+ else
+ cn->cn_prev->cn_next = cn->cn_next;
+
+ if (cn->cn_next != NULL)
+ cn->cn_next->cn_prev = cn->cn_prev;
+
+ cn_destroy(cn);
+ nreaped++;
+ }
+ (void) mutex_unlock(&cn_list->cl_lock);
+ }
+
+ return (nreaped);
+}
+
+/*
+ * Hash a container identified by `cn_id' into a 32-bit unsigned integer
+ * suitable for use as a key in a hash table.
+ */
+static uint32_t
+ds_hash(const char *cn_id)
+{
+ uint32_t result = 0;
+ unsigned int i;
+
+ for (i = 0; cn_id[i] != '\0'; i++)
+ result += cn_id[i] << i;
+
+ return (result);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/dsvclockd/datastore.h b/usr/src/cmd/cmd-inet/usr.lib/dsvclockd/datastore.h
new file mode 100644
index 0000000000..8d977e3057
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/dsvclockd/datastore.h
@@ -0,0 +1,95 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2000 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef DSVCD_DATASTORE_H
+#define DSVCD_DATASTORE_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <synch.h>
+#include <door.h>
+
+#include "container.h"
+
+/*
+ * Datastore-related data structures, functions and constants. See
+ * comments in datastore.c for a description of how to use the exported
+ * functions.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define DSVCD_DS_HASH_SIZE 100
+
+/*
+ * A linked list of dsvcd_container_t structures. Contains a lock,
+ * `cl_lock', which is used for controlling manipulation of `cl_head'.
+ */
+typedef struct {
+ mutex_t cl_lock; /* protects the list */
+ dsvcd_container_t *cl_head; /* linked list of containers */
+ uint8_t cl_pad[8]; /* prevent false sharing */
+} dsvcd_container_list_t;
+
+/*
+ * Describes the underlying datastore itself. There is exactly one
+ * dsvcd_datastore_t per datastore using doors (so currently, there are
+ * two: one for `ds_files' and one for `ds_data'). Contains per-datastore
+ * information, like the door descriptor being used by dsvclockd to listen
+ * to requests for this datastore, the datastore name, and a list of all
+ * open containers for this datastore. Instances of this data structure
+ * are allocated when dsvclockd is started.
+ */
+typedef struct dsvcd_datastore {
+ char *ds_name; /* datastore name */
+ int ds_doorfd; /* datastore door */
+
+ /*
+ * This hash is used to speed up the open() routine so that a given
+ * container can be located quicker. Hash based on the filename,
+ * and use it as an index into the array..
+ */
+ dsvcd_container_list_t ds_hash[DSVCD_DS_HASH_SIZE];
+} dsvcd_datastore_t;
+
+typedef void dsvcd_svc_t(void *, dsvcd_request_t *, size_t, door_desc_t *,
+ uint_t);
+
+extern dsvcd_datastore_t *ds_create(const char *, dsvcd_svc_t *);
+extern void ds_destroy(dsvcd_datastore_t *);
+extern unsigned int ds_reap_containers(dsvcd_datastore_t *, unsigned int);
+extern void ds_release_container(dsvcd_datastore_t *, dsvcd_container_t *);
+extern dsvcd_container_t *ds_get_container(dsvcd_datastore_t *, const char *,
+ boolean_t);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DSVCD_DATASTORE_H */
diff --git a/usr/src/cmd/cmd-inet/usr.lib/dsvclockd/dsvclockd.c b/usr/src/cmd/cmd-inet/usr.lib/dsvclockd/dsvclockd.c
new file mode 100644
index 0000000000..38f51b92a2
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/dsvclockd/dsvclockd.c
@@ -0,0 +1,863 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * #define _POSIX_PTHREAD_SEMANTICS before #including <signal.h> so that we
+ * get the right (POSIX) version of sigwait(2).
+ */
+#define _POSIX_PTHREAD_SEMANTICS
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/sysmacros.h>
+#include <dhcp_svc_private.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <dhcpmsg.h>
+#include <assert.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <locale.h>
+#include <synch.h>
+
+#include "datastore.h"
+#include "dsvclockd.h"
+
+/*
+ * The DHCP service daemon synchronizes access to containers within a given
+ * datastore. Any datastore which is willing to accept the synchronization
+ * constraints imposed by the DHCP service daemon can use this daemon in
+ * lieu of rolling their own synchronization code.
+ *
+ * See $SRC/lib/libdhcpsvc/private/README.synch for more information.
+ */
+
+#ifndef TEXT_DOMAIN
+#define TEXT_DOMAIN "SYS_TEST"
+#endif
+
+#define DSVCD_REAP_INTERVAL (60 * 60 * 24) /* seconds, thus once a day */
+#define DSVCD_REAP_THRESH (60 * 60) /* seconds, thus 1 hour stale */
+#define DSVCD_STACK_REDSIZE (8 * 1024) /* redzone size, in bytes */
+#define UD_RECLAIM_MAX 128 /* unlock door descriptors */
+
+/*
+ * Unlock descriptor -- one for each lock granted. This descriptor is used
+ * to subsequently unlock the granted lock (and to synchronize unlocking of
+ * the lock; see svc_unlock() below for details).
+ */
+typedef struct dsvcd_unlock_desc {
+ int ud_fd;
+ mutex_t ud_lock;
+ dsvcd_container_t *ud_cn;
+ struct dsvcd_unlock_desc *ud_next;
+} dsvcd_unlock_desc_t;
+
+static mutex_t ud_reclaim_lock = DEFAULTMUTEX;
+static unsigned int ud_reclaim_count = 0;
+static dsvcd_unlock_desc_t *ud_reclaim_list = NULL;
+
+static void *reaper(void *);
+static int daemonize(void);
+static void *stack_create(unsigned int *);
+static void stack_destroy(void *, unsigned int);
+static void doorserv_create(door_info_t *);
+static dsvcd_unlock_desc_t *ud_create(dsvcd_container_t *, int *);
+static void ud_destroy(dsvcd_unlock_desc_t *, boolean_t);
+static dsvcd_svc_t svc_lock, svc_unlock;
+
+int
+main(int argc, char **argv)
+{
+ dsvcd_datastore_t **ds_table;
+ dsvc_datastore_t dd;
+ dsvc_synchtype_t synchtype;
+ char **modules;
+ unsigned int i, j;
+ int debug_level = 0;
+ boolean_t is_daemon = B_TRUE;
+ boolean_t is_verbose = B_FALSE;
+ int sig, nmodules, nsynchmods, c;
+ sigset_t sigset;
+ char signame[SIG2STR_MAX];
+ char *progname;
+ void *stackbase;
+ unsigned int stacksize = 16 * 1024;
+ struct rlimit rl;
+
+ (void) setlocale(LC_ALL, "");
+ (void) textdomain(TEXT_DOMAIN);
+
+ /*
+ * Mask all signals except SIGABRT; doing this here ensures that
+ * all threads created through door_create() have them masked too.
+ */
+ (void) sigfillset(&sigset);
+ (void) sigdelset(&sigset, SIGABRT);
+ (void) thr_sigsetmask(SIG_BLOCK, &sigset, NULL);
+
+ /*
+ * Figure out our program name; just keep the final piece so that
+ * our dhcpmsg() messages don't get too long.
+ */
+ progname = strrchr(argv[0], '/');
+ if (progname != NULL)
+ progname++;
+ else
+ progname = argv[0];
+
+ /*
+ * Set the door thread creation procedure so that all of our
+ * threads are created with thread stacks with backing store.
+ */
+ (void) door_server_create(doorserv_create);
+
+ while ((c = getopt(argc, argv, "d:fv")) != EOF) {
+ switch (c) {
+
+ case 'd':
+ debug_level = atoi(optarg);
+ break;
+
+ case 'f':
+ is_daemon = B_FALSE;
+ break;
+
+ case 'v':
+ is_verbose = B_TRUE;
+ break;
+
+ case '?':
+ (void) fprintf(stderr,
+ gettext("usage: %s [-dn] [-f] [-v]\n"), progname);
+ return (EXIT_FAILURE);
+
+ default:
+ break;
+ }
+ }
+
+ if (geteuid() != 0) {
+ dhcpmsg_init(progname, B_FALSE, is_verbose, debug_level);
+ dhcpmsg(MSG_ERROR, "must be super-user");
+ dhcpmsg_fini();
+ return (EXIT_FAILURE);
+ }
+
+ if (is_daemon && daemonize() == 0) {
+ dhcpmsg_init(progname, B_FALSE, is_verbose, debug_level);
+ dhcpmsg(MSG_ERROR, "cannot become daemon, exiting");
+ dhcpmsg_fini();
+ return (EXIT_FAILURE);
+ }
+
+ dhcpmsg_init(progname, is_daemon, is_verbose, debug_level);
+ (void) atexit(dhcpmsg_fini);
+
+ /*
+ * Max out the number available descriptors since we need to
+ * allocate two per held lock.
+ */
+ rl.rlim_cur = RLIM_INFINITY;
+ rl.rlim_max = RLIM_INFINITY;
+ if (setrlimit(RLIMIT_NOFILE, &rl) == -1)
+ dhcpmsg(MSG_ERR, "setrlimit failed");
+
+ if (enumerate_dd(&modules, &nmodules) != DSVC_SUCCESS) {
+ dhcpmsg(MSG_ERROR, "cannot enumerate public modules, exiting");
+ return (EXIT_FAILURE);
+ }
+
+ /*
+ * NOTE: this code assumes that a module that needs dsvclockd will
+ * always need it (even as the container version is ramped). If
+ * this becomes bogus in a future release, we'll have to make this
+ * logic more sophisticated.
+ */
+ nsynchmods = nmodules;
+ for (i = 0; i < nmodules; i++) {
+ dd.d_resource = modules[i];
+ dd.d_conver = DSVC_CUR_CONVER;
+ dd.d_location = "";
+ if (module_synchtype(&dd, &synchtype) != DSVC_SUCCESS) {
+ dhcpmsg(MSG_WARNING, "cannot determine synchronization "
+ "type for `%s', skipping", modules[i]);
+ free(modules[i]);
+ modules[i] = NULL;
+ nsynchmods--;
+ continue;
+ }
+ if ((synchtype & DSVC_SYNCH_STRATMASK) != DSVC_SYNCH_DSVCD) {
+ free(modules[i]);
+ modules[i] = NULL;
+ nsynchmods--;
+ }
+ }
+
+ if (nsynchmods == 0) {
+ dhcpmsg(MSG_INFO, "no public modules need synchronization");
+ return (EXIT_SUCCESS);
+ }
+
+ /*
+ * Allocate the datastore table; include one extra entry so that
+ * the table is NULL-terminated.
+ */
+ ds_table = calloc(nsynchmods + 1, sizeof (dsvcd_datastore_t *));
+ if (ds_table == NULL) {
+ dhcpmsg(MSG_ERR, "cannot allocate datastore table, exiting");
+ return (EXIT_FAILURE);
+ }
+ ds_table[nsynchmods] = NULL;
+
+ /*
+ * Create the datastores (which implicitly creates the doors).
+ * then sit around and wait for requests to come in on the doors.
+ */
+ for (i = 0, j = 0; i < nmodules; i++) {
+ if (modules[i] != NULL) {
+ ds_table[j] = ds_create(modules[i], svc_lock);
+ if (ds_table[j] == NULL) {
+ while (j-- > 0)
+ ds_destroy(ds_table[j]);
+ return (EXIT_FAILURE);
+ }
+ free(modules[i]);
+ j++;
+ }
+ }
+ free(modules);
+
+ stackbase = stack_create(&stacksize);
+ if (stackbase == NULL)
+ dhcpmsg(MSG_ERR, "cannot create reaper stack; containers "
+ "will not be reaped");
+ else {
+ errno = thr_create(stackbase, stacksize, reaper, ds_table,
+ THR_DAEMON, NULL);
+ if (errno != 0) {
+ dhcpmsg(MSG_ERR, "cannot create reaper thread; "
+ "containers will not be reaped");
+ stack_destroy(stackbase, stacksize);
+ }
+ }
+
+ /*
+ * Synchronously wait for a QUIT, TERM, or INT, then shutdown.
+ */
+ (void) sigemptyset(&sigset);
+ (void) sigaddset(&sigset, SIGQUIT);
+ (void) sigaddset(&sigset, SIGTERM);
+ (void) sigaddset(&sigset, SIGINT);
+
+ (void) sigwait(&sigset, &sig);
+ if (sig != SIGTERM && sig != SIGQUIT && sig != SIGINT)
+ dhcpmsg(MSG_WARNING, "received unexpected signal");
+
+ if (sig2str(sig, signame) == -1)
+ (void) strlcpy(signame, "???", sizeof (signame));
+
+ dhcpmsg(MSG_INFO, "shutting down via SIG%s", signame);
+
+ for (i = 0; i < nsynchmods; i++)
+ ds_destroy(ds_table[i]);
+
+ return (EXIT_SUCCESS);
+}
+
+/*
+ * Sanity check that dsvcd_request_t `req' (which is `reqsize' bytes long)
+ * is a correctly formed request; if not, return an error which will be
+ * returned to the door caller.
+ */
+static int
+check_door_req(dsvcd_request_t *req, size_t reqsize, size_t minsize)
+{
+ door_cred_t cred;
+
+ if (req == NULL) {
+ dhcpmsg(MSG_WARNING, "empty request, ignoring");
+ return (DSVC_SYNCH_ERR);
+ }
+
+ /*
+ * Check credentials; we don't allow any non-super-user requests
+ * since this would open a denial-of-service hole (since a lock
+ * could be checked out indefinitely).
+ */
+ if (door_cred(&cred) != 0) {
+ dhcpmsg(MSG_WARNING, "request with unknown credentials");
+ return (DSVC_ACCESS);
+ }
+
+ if (cred.dc_euid != 0) {
+ dhcpmsg(MSG_WARNING, "request with non-super-user credentials");
+ return (DSVC_ACCESS);
+ }
+
+ /*
+ * Check the version and size; we check this before checking the
+ * size of the request structure since an "incompatible version"
+ * message is more helpful than a "short request" message.
+ */
+ if (reqsize > offsetof(dsvcd_request_t, rq_version) &&
+ req->rq_version != DSVCD_DOOR_VERSION) {
+ dhcpmsg(MSG_WARNING, "request with unsupported version `%d'",
+ req->rq_version);
+ return (DSVC_SYNCH_ERR);
+ }
+
+ if (reqsize < minsize) {
+ dhcpmsg(MSG_VERBOSE, "short request (%d bytes, minimum %d "
+ "bytes)", reqsize, minsize);
+ return (DSVC_SYNCH_ERR);
+ }
+
+ return (DSVC_SUCCESS);
+}
+
+
+/*
+ * Service a lock request `req' passed across the door for datastore `ds'.
+ * After verifying that the request is well-formed, locks the container and
+ * creates an "unlock" door descriptor that the client uses to unlock the
+ * door (either explicitly through door_call()) or implicitly through
+ * terminating abnormally).
+ */
+/* ARGSUSED */
+static void
+svc_lock(void *cookie, dsvcd_request_t *req, size_t reqsize,
+ door_desc_t *doorp, uint_t ndoors)
+{
+ dsvcd_reply_t reply;
+ door_desc_t door_desc;
+ dsvcd_lock_request_t *lreq = (dsvcd_lock_request_t *)req;
+ dsvcd_datastore_t *ds = (dsvcd_datastore_t *)cookie;
+ dsvcd_container_t *cn;
+ dsvcd_unlock_desc_t *ud;
+ char conid[MAXPATHLEN];
+ unsigned int attempts = 0;
+
+ reply.rp_version = DSVCD_DOOR_VERSION;
+ reply.rp_retval = check_door_req(req, reqsize,
+ sizeof (dsvcd_lock_request_t));
+ if (reply.rp_retval != DSVC_SUCCESS) {
+ (void) door_return((char *)&reply, sizeof (reply), NULL, 0);
+ return;
+ }
+
+ /*
+ * Verify that this is a lock request; if in the future we support
+ * other requests, we'll have to abstract this a bit.
+ */
+ if (req->rq_reqtype != DSVCD_LOCK) {
+ dhcpmsg(MSG_WARNING, "unsupported request `%d' on lock "
+ "request door", req->rq_reqtype);
+ reply.rp_retval = DSVC_SYNCH_ERR;
+ (void) door_return((char *)&reply, sizeof (reply), NULL, 0);
+ return;
+ }
+ if (lreq->lrq_locktype != DSVCD_RDLOCK &&
+ lreq->lrq_locktype != DSVCD_WRLOCK) {
+ dhcpmsg(MSG_WARNING, "request for unsupported locktype `%d'",
+ lreq->lrq_locktype);
+ reply.rp_retval = DSVC_SYNCH_ERR;
+ (void) door_return((char *)&reply, sizeof (reply), NULL, 0);
+ return;
+ }
+
+ /*
+ * Find the container; create if it doesn't already exist. We do
+ * this as a single operation to avoid race conditions.
+ */
+ (void) snprintf(conid, sizeof (conid), "%s/%s%d_%s", lreq->lrq_loctoken,
+ ds->ds_name, lreq->lrq_conver, lreq->lrq_conname);
+ cn = ds_get_container(ds, conid, lreq->lrq_crosshost);
+ if (cn == NULL) {
+ reply.rp_retval = DSVC_NO_MEMORY;
+ (void) door_return((char *)&reply, sizeof (reply), NULL, 0);
+ return;
+ }
+
+ /*
+ * We need another door descriptor which is passed back with the
+ * request. This descriptor is used when the caller wants to
+ * gracefully unlock or when the caller terminates abnormally.
+ */
+ ud = ud_create(cn, &reply.rp_retval);
+ if (ud == NULL) {
+ ds_release_container(ds, cn);
+ (void) door_return((char *)&reply, sizeof (reply), NULL, 0);
+ return;
+ }
+
+ /*
+ * We pass a duped door descriptor with the DOOR_RELEASE flag set
+ * instead of just passing the descriptor itself to handle the case
+ * where the client has gone away before we door_return(). Since
+ * we duped, the door descriptor itself will have a refcount of 2
+ * when we go to pass it to the client; if the client does not
+ * exist, the DOOR_RELEASE will drop the count from 2 to 1 which
+ * will cause a DOOR_UNREF_DATA call.
+ *
+ * In the regular (non-error) case, the door_return() will handoff
+ * the descriptor to the client, bumping the refcount to 3, and
+ * then the DOOR_RELEASE will drop the count to 2. If the client
+ * terminates abnormally after this point, the count will drop from
+ * 2 to 1 which will cause a DOOR_UNREF_DATA call. If the client
+ * unlocks gracefully, the refcount will still be 2 when the unlock
+ * door server procedure is called, and the unlock procedure will
+ * unlock the lock and note that the lock has been unlocked (so
+ * that we know the DOOR_UNREF_DATA call generated from the client
+ * subsequently closing the unlock descriptor is benign).
+ *
+ * Note that a DOOR_UNREF_DATA call will be generated *any time*
+ * the refcount goes from 2 to 1 -- even if *we* cause it to
+ * happen, which by default will happen in some of the error logic
+ * below (when we close the duped descriptor). To prevent this
+ * scenario, we tell ud_destroy() *not* to cache the unlock
+ * descriptor, which forces it to blow away the descriptor using
+ * door_revoke(), making the close() that follows benign.
+ */
+ door_desc.d_attributes = DOOR_DESCRIPTOR|DOOR_RELEASE;
+ door_desc.d_data.d_desc.d_descriptor = dup(ud->ud_fd);
+ if (door_desc.d_data.d_desc.d_descriptor == -1) {
+ dhcpmsg(MSG_ERR, "cannot dup unlock door; denying %s "
+ "lock request", cn->cn_id);
+ ud_destroy(ud, B_TRUE);
+ ds_release_container(ds, cn);
+ reply.rp_retval = DSVC_NO_RESOURCES;
+ (void) door_return((char *)&reply, sizeof (reply), NULL, 0);
+ return;
+ }
+
+ /*
+ * Acquire the actual read or write lock on the container.
+ */
+ dhcpmsg(MSG_DEBUG, "tid %d: %s locking %s", thr_self(),
+ lreq->lrq_locktype == DSVCD_RDLOCK ? "read" : "write", cn->cn_id);
+
+ if (lreq->lrq_locktype == DSVCD_RDLOCK)
+ reply.rp_retval = cn_rdlock(cn, lreq->lrq_nonblock);
+ else if (lreq->lrq_locktype == DSVCD_WRLOCK)
+ reply.rp_retval = cn_wrlock(cn, lreq->lrq_nonblock);
+
+ dhcpmsg(MSG_DEBUG, "tid %d: %s %s lock operation: %s", thr_self(),
+ cn->cn_id, lreq->lrq_locktype == DSVCD_RDLOCK ? "read" : "write",
+ dhcpsvc_errmsg(reply.rp_retval));
+
+ ds_release_container(ds, cn);
+ if (reply.rp_retval != DSVC_SUCCESS) {
+ ud_destroy(ud, B_FALSE);
+ (void) close(door_desc.d_data.d_desc.d_descriptor);
+ (void) door_return((char *)&reply, sizeof (reply), NULL, 0);
+ return;
+ }
+
+ while (door_return((char *)&reply, sizeof (reply), &door_desc, 1)
+ == -1 && errno == EMFILE) {
+ if (lreq->lrq_nonblock) {
+ dhcpmsg(MSG_WARNING, "unable to grant lock; client"
+ " is out of file descriptors");
+ (void) cn_unlock(cn);
+ ud_destroy(ud, B_FALSE);
+ (void) close(door_desc.d_data.d_desc.d_descriptor);
+ reply.rp_retval = DSVC_BUSY;
+ (void) door_return((char *)&reply, sizeof (reply),
+ NULL, 0);
+ return;
+ }
+
+ if (attempts++ == 0) {
+ dhcpmsg(MSG_WARNING, "unable to grant lock; client"
+ " is out of file descriptors (retrying)");
+ }
+ (void) poll(NULL, 0, 100);
+ }
+}
+
+/*
+ * Service an unlock request `req' passed across the door associated with
+ * the unlock token `cookie'. We may be called explicitly (in which case
+ * the request is a well-formed dsvcd_request_t) or implicitly (in which
+ * case our request is set to the value DOOR_UNREF_DATA); this latter case
+ * occurs when a process holding a lock terminates. In either case, unlock
+ * the lock; in the implicit case, log a message as well.
+ */
+/* ARGSUSED */
+static void
+svc_unlock(void *cookie, dsvcd_request_t *req, size_t reqsize,
+ door_desc_t *doorp, uint_t ndoors)
+{
+ dsvcd_unlock_desc_t *ud = cookie;
+ dsvcd_container_t *cn;
+ dsvcd_reply_t reply;
+
+ /*
+ * Although unlock descriptors are handed out to only a single
+ * thread who has been granted a lock (ergo it seems that only one
+ * thread should be able to call us back), there's a potential race
+ * here if the process crashes while in this door_call(), since
+ * both this thread and the unref kernel upcall thread may run at
+ * the same time. Protect against this case with a mutex.
+ */
+ (void) mutex_lock(&ud->ud_lock);
+ cn = ud->ud_cn;
+
+ /*
+ * First handle the case where the lock owner has closed the unlock
+ * descriptor, either because they have unlocked the lock and are
+ * thus done using the descriptor, or because they crashed. In the
+ * second case, print a message.
+ */
+ if (req == DOOR_UNREF_DATA) {
+ /*
+ * The last reference is ours; we can free the descriptor.
+ */
+ (void) mutex_unlock(&ud->ud_lock);
+ ud_destroy(ud, B_TRUE);
+
+ /*
+ * Normal case: the caller is closing the unlock descriptor
+ * on a lock they've already unlocked -- just return.
+ */
+ if (cn == NULL) {
+ (void) door_return(NULL, 0, NULL, 0);
+ return;
+ }
+
+ /*
+ * Error case: the caller has crashed while holding the
+ * unlock descriptor (or is otherwise in violation of
+ * protocol). Since all datastores are required to be
+ * robust even if unexpected termination occurs, we assume
+ * the container is not corrupt, even if the process
+ * crashed with the write lock held.
+ */
+ switch (cn_locktype(cn)) {
+ case DSVCD_RDLOCK:
+ dhcpmsg(MSG_WARNING, "process exited while reading "
+ "`%s'; unlocking", cn->cn_id);
+ (void) cn_unlock(cn);
+ break;
+
+ case DSVCD_WRLOCK:
+ dhcpmsg(MSG_WARNING, "process exited while writing "
+ "`%s'; unlocking", cn->cn_id);
+ dhcpmsg(MSG_WARNING, "note that this write operation "
+ "may or may not have succeeded");
+ (void) cn_unlock(cn);
+ break;
+
+ case DSVCD_NOLOCK:
+ dhcpmsg(MSG_CRIT, "unreferenced unheld lock");
+ break;
+ }
+
+ (void) door_return(NULL, 0, NULL, 0);
+ return;
+ }
+
+ /*
+ * Verify that this is a unlock request; if in the future we support
+ * other requests, we'll have to abstract this a bit.
+ */
+ reply.rp_version = DSVCD_DOOR_VERSION;
+ reply.rp_retval = check_door_req(req, reqsize,
+ sizeof (dsvcd_unlock_request_t));
+ if (reply.rp_retval != DSVC_SUCCESS) {
+ (void) mutex_unlock(&ud->ud_lock);
+ (void) door_return((char *)&reply, sizeof (reply), NULL, 0);
+ return;
+ }
+
+ if (req->rq_reqtype != DSVCD_UNLOCK) {
+ dhcpmsg(MSG_WARNING, "unsupported request `%d' on unlock "
+ "request door", req->rq_reqtype);
+ (void) mutex_unlock(&ud->ud_lock);
+ reply.rp_retval = DSVC_SYNCH_ERR;
+ (void) door_return((char *)&reply, sizeof (reply), NULL, 0);
+ return;
+ }
+
+ /*
+ * Attempt to unlock an already-unlocked container; log and return.
+ */
+ if (cn == NULL) {
+ dhcpmsg(MSG_WARNING, "process tried to re-unlock a lock");
+ (void) mutex_unlock(&ud->ud_lock);
+ reply.rp_retval = DSVC_SYNCH_ERR;
+ (void) door_return((char *)&reply, sizeof (reply), NULL, 0);
+ return;
+ }
+ ud->ud_cn = NULL;
+
+ /*
+ * Unlock the container; note that after cn_unlock() has been done
+ * cn->cn_id is no longer accessible.
+ */
+ dhcpmsg(MSG_DEBUG, "tid %d: unlocking %s", thr_self(), cn->cn_id);
+ reply.rp_retval = cn_unlock(cn);
+ dhcpmsg(MSG_DEBUG, "tid %d: unlock operation: %s", thr_self(),
+ dhcpsvc_errmsg(reply.rp_retval));
+
+ /*
+ * Even though we've unlocked the lock, we cannot yet destroy the
+ * unlock descriptor (even if we revoke the door) because it's
+ * possible the unref thread is already waiting on ud_lock.
+ */
+ (void) mutex_unlock(&ud->ud_lock);
+ (void) door_return((char *)&reply, sizeof (reply), NULL, 0);
+}
+
+/*
+ * Reap containers that have not been recently used.
+ */
+static void *
+reaper(void *ds_table_raw)
+{
+ dsvcd_datastore_t **ds_table;
+ unsigned int i, nreaped;
+
+ ds_table = (dsvcd_datastore_t **)ds_table_raw;
+ for (;;) {
+ (void) sleep(DSVCD_REAP_INTERVAL);
+ for (i = 0; ds_table[i] != NULL; i++) {
+ nreaped = ds_reap_containers(ds_table[i],
+ DSVCD_REAP_THRESH);
+ if (nreaped > 0) {
+ dhcpmsg(MSG_VERBOSE, "reaped %u container "
+ "synchpoints from %s", nreaped,
+ ds_table[i]->ds_name);
+ }
+ }
+ }
+ /* NOTREACHED */
+ return (NULL);
+}
+
+/*
+ * Daemonize the process.
+ */
+static int
+daemonize(void)
+{
+ switch (fork()) {
+
+ case -1:
+ return (0);
+
+ case 0:
+ /*
+ * Lose our controlling terminal, and become both a session
+ * leader and a process group leader.
+ */
+ if (setsid() == -1)
+ return (0);
+
+ /*
+ * Under POSIX, a session leader can accidentally (through
+ * open(2)) acquire a controlling terminal if it does not
+ * have one. Just to be safe, fork() again so we are not a
+ * session leader.
+ */
+ switch (fork()) {
+
+ case -1:
+ return (0);
+
+ case 0:
+ (void) signal(SIGHUP, SIG_IGN);
+ (void) chdir("/");
+ (void) umask(022);
+ closefrom(0);
+ break;
+
+ default:
+ _exit(EXIT_SUCCESS);
+ }
+ break;
+
+ default:
+ _exit(EXIT_SUCCESS);
+ }
+
+ return (1);
+}
+
+/*
+ * Create an unlock descriptor for container `cn' -- returns an unlock
+ * descriptor on success, or NULL on failure; the reason for failure is in
+ * `retvalp'. Since creating door descriptors is expensive, we keep a few
+ * cache a small list of old descriptors around on a reclaim list and only
+ * allocate a new one if the list is empty.
+ */
+static dsvcd_unlock_desc_t *
+ud_create(dsvcd_container_t *cn, int *retvalp)
+{
+ dsvcd_unlock_desc_t *ud;
+
+ *retvalp = DSVC_SUCCESS;
+ (void) mutex_lock(&ud_reclaim_lock);
+ if (ud_reclaim_list != NULL) {
+ ud = ud_reclaim_list;
+ ud_reclaim_list = ud->ud_next;
+ ud_reclaim_count--;
+ (void) mutex_unlock(&ud_reclaim_lock);
+ } else {
+ (void) mutex_unlock(&ud_reclaim_lock);
+ ud = malloc(sizeof (dsvcd_unlock_desc_t));
+ if (ud == NULL) {
+ dhcpmsg(MSG_WARNING, "cannot allocate unlock door "
+ "descriptor; denying %s lock request", cn->cn_id);
+ *retvalp = DSVC_NO_MEMORY;
+ return (NULL);
+ }
+
+ (void) mutex_init(&ud->ud_lock, USYNC_THREAD, NULL);
+ ud->ud_fd = door_create((void (*)())svc_unlock, ud,
+ DOOR_UNREF_MULTI | DOOR_REFUSE_DESC | DOOR_NO_CANCEL);
+ if (ud->ud_fd == -1) {
+ dhcpmsg(MSG_WARNING, "cannot create unlock door; "
+ "denying %s lock request", cn->cn_id);
+ free(ud);
+ *retvalp = DSVC_NO_RESOURCES;
+ return (NULL);
+ }
+ }
+
+ ud->ud_next = NULL;
+ ud->ud_cn = cn;
+ return (ud);
+}
+
+/*
+ * Destroy the unlock descriptor `ud' -- `ud' must be unlocked on entry.
+ * If there's room and `cacheable' is set, then, keep the unlock descriptor
+ * on the reclaim list to lower future creation cost.
+ */
+static void
+ud_destroy(dsvcd_unlock_desc_t *ud, boolean_t cacheable)
+{
+ assert(!MUTEX_HELD(&ud->ud_lock));
+
+ ud->ud_cn = NULL;
+ (void) mutex_lock(&ud_reclaim_lock);
+ if (cacheable && ud_reclaim_count < UD_RECLAIM_MAX) {
+ ud->ud_next = ud_reclaim_list;
+ ud_reclaim_list = ud;
+ ud_reclaim_count++;
+ (void) mutex_unlock(&ud_reclaim_lock);
+ } else {
+ (void) mutex_unlock(&ud_reclaim_lock);
+ (void) door_revoke(ud->ud_fd);
+ (void) mutex_destroy(&ud->ud_lock);
+ free(ud);
+ }
+}
+
+/*
+ * Create a stack of `*stacksizep' bytes (rounded up to the nearest page)
+ * including a redzone for catching stack overflow. Set `stacksizep' to
+ * point to the actual usable size of the stack (i.e., everything but the
+ * redzone). Returns a pointer to the base of the stack (not including the
+ * redzone).
+ */
+static void *
+stack_create(unsigned int *stacksizep)
+{
+ caddr_t stackbase;
+ unsigned int redzone = roundup(DSVCD_STACK_REDSIZE, PAGESIZE);
+ unsigned int stacksize = *stacksizep;
+
+ if (stacksize < sysconf(_SC_THREAD_STACK_MIN))
+ stacksize = sysconf(_SC_THREAD_STACK_MIN);
+
+ stacksize = roundup(stacksize, PAGESIZE);
+ stackbase = mmap(NULL, stacksize + redzone, PROT_READ | PROT_WRITE,
+ MAP_ANON | MAP_PRIVATE, -1, 0);
+ if (stackbase == MAP_FAILED)
+ return (NULL);
+
+ *stacksizep = stacksize;
+ (void) mprotect(stackbase, redzone, PROT_NONE);
+ return (stackbase + redzone);
+}
+
+/*
+ * Destroy the stack of `stacksize' bytes pointed to by `stackbase'.
+ */
+static void
+stack_destroy(void *stackbase, unsigned int stacksize)
+{
+ unsigned int redzone = roundup(DSVCD_STACK_REDSIZE, PAGESIZE);
+
+ (void) munmap((caddr_t)stackbase - redzone, stacksize + redzone);
+}
+
+/*
+ * Start function for door server threads; turns off thread cancellation
+ * and then parks in the kernel via door_return().
+ */
+/* ARGSUSED */
+static void *
+doorserv_thread(void *arg)
+{
+ (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
+ (void) door_return(NULL, 0, NULL, 0);
+ return (NULL);
+}
+
+/*
+ * Creation function for door server threads. We require door threads to
+ * have 32K of backed stack. This is a guess but will be more than
+ * sufficient for our uses, since door threads have a shallow call depth
+ * and the functions use little automatic storage.
+ */
+/* ARGSUSED */
+static void
+doorserv_create(door_info_t *infop)
+{
+ void *stackbase;
+ unsigned int stacksize = 32 * 1024;
+
+ stackbase = stack_create(&stacksize);
+ if (stackbase != NULL) {
+ errno = thr_create(stackbase, stacksize, doorserv_thread, NULL,
+ THR_BOUND | THR_DETACHED, NULL);
+ if (errno != 0) {
+ dhcpmsg(MSG_ERR, "cannot create door server thread; "
+ "server thread pool will not be grown");
+ stack_destroy(stackbase, stacksize);
+ }
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/dsvclockd/dsvclockd.h b/usr/src/cmd/cmd-inet/usr.lib/dsvclockd/dsvclockd.h
new file mode 100644
index 0000000000..688a6d316b
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/dsvclockd/dsvclockd.h
@@ -0,0 +1,101 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2000 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef DSVCD_DSVCLOCKD_H
+#define DSVCD_DSVCLOCKD_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/param.h>
+#include <sys/types.h>
+
+/*
+ * Data structures and constants that are shared between dsvclockd and
+ * libdhcpsvc. This protocol is project-private and is thus subject to
+ * change at any time.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define DSVCD_DOOR_VERSION 1 /* current protocol version */
+#define DSVCD_PATH "/usr/lib/inet/dsvclockd"
+#define DSVCD_DOOR_FMT "/var/run/dsvclockd_%s_door"
+
+typedef enum { DSVCD_LOCK, DSVCD_UNLOCK } dsvcd_reqtype_t;
+typedef enum { DSVCD_NOLOCK, DSVCD_RDLOCK, DSVCD_WRLOCK } dsvcd_locktype_t;
+
+typedef struct {
+ uint8_t rq_version; /* version of the API */
+ dsvcd_reqtype_t rq_reqtype; /* request type */
+} dsvcd_request_t;
+
+typedef struct {
+ uint8_t rp_version; /* version of the API */
+ int32_t rp_retval; /* DSVC_* return value */
+} dsvcd_reply_t;
+
+typedef struct {
+ dsvcd_request_t lrq_request; /* generic request header */
+ dsvcd_locktype_t lrq_locktype; /* reader or writer */
+ uint8_t lrq_nonblock; /* cannot block if true */
+ uint8_t lrq_crosshost; /* do cross-host synch */
+
+ /*
+ * The caller filling in this request must provide their current
+ * container version and a name for their container which is
+ * per-datastore unique (but need not be unique across datastores
+ * or different versions of the same container.)
+ *
+ * The `lrq_loctoken' field must contain a token which "names" a
+ * given location where the container exists -- note that a given
+ * location must have exactly one name, though it's permissible for
+ * more than one location to have the same name (in this case,
+ * containers from these locations will be synchronized with one
+ * another, which will hamper performance). Note that standard
+ * pathnames do not meet the first constraint (e.g., /var/dhcp and
+ * /var/../var/dhcp are two different names for the same location),
+ * but pathnames processed by realpath(3C) do.
+ *
+ * If the caller wants cross-host synchronization, then
+ * `lrq_crosshost' must be set and `lrq_loctoken' must be a
+ * realpath(3C)'d directory that all hosts can access.
+ */
+ int lrq_conver;
+ char lrq_conname[64];
+ char lrq_loctoken[MAXPATHLEN];
+} dsvcd_lock_request_t;
+
+typedef struct {
+ dsvcd_request_t urq_request; /* generic request header */
+} dsvcd_unlock_request_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DSVCD_DSVCLOCKD_H */
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.chargend/Makefile b/usr/src/cmd/cmd-inet/usr.lib/in.chargend/Makefile
new file mode 100644
index 0000000000..8273cd6a3d
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.chargend/Makefile
@@ -0,0 +1,34 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# cmd/cmd-inet/usr.lib/in.chargend/%M%
+#
+
+PROG = in.chargend
+MANIFEST= chargen.xml
+
+include ../Makefile.inetsvc
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.chargend/chargen.xml b/usr/src/cmd/cmd-inet/usr.lib/in.chargend/chargen.xml
new file mode 100644
index 0000000000..863ba572e9
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.chargend/chargen.xml
@@ -0,0 +1,122 @@
+<?xml version='1.0'?>
+<!DOCTYPE service_bundle SYSTEM '/usr/share/lib/xml/dtd/service_bundle.dtd.1'>
+<!--
+ Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ ident "%Z%%M% %I% %E% SMI"
+
+ NOTE: This service manifest is not editable; its contents will
+ be overwritten by package or patch operations, including
+ operating system upgrade. Make customizations in a different
+ file.
+-->
+
+<service_bundle type='manifest' name='SUNWcnsr:chargen'>
+
+<service
+ name='network/chargen'
+ type='service'
+ version='1'>
+
+ <restarter>
+ <service_fmri value='svc:/network/inetd:default' />
+ </restarter>
+
+ <property_group name='inetd' type='framework'>
+ <stability value='Evolving' />
+ <propval name='name' type='astring' value='chargen' />
+ <propval name='isrpc' type='boolean' value='false' />
+ </property_group>
+
+ <instance name='dgram' enabled='false' >
+ <exec_method
+ type='method'
+ name='inetd_start'
+ exec='/usr/lib/inet/in.chargend -d'
+ timeout_seconds='0'>
+ <method_context>
+ <method_credential user='root' group='root' />
+ </method_context>
+ </exec_method>
+
+ <exec_method
+ type='method'
+ name='inetd_offline'
+ exec=':kill_process'
+ timeout_seconds='0'>
+ </exec_method>
+
+ <exec_method
+ type='method'
+ name='inetd_disable'
+ exec=':kill'
+ timeout_seconds='0'>
+ </exec_method>
+
+ <property_group name='inetd' type='framework'>
+ <propval name='endpoint_type' type='astring' value='dgram' />
+ <propval name='proto' type='astring' value='udp6' />
+ <propval name='wait' type='boolean' value='true' />
+ </property_group>
+ </instance>
+
+ <instance name='stream' enabled='false' >
+ <exec_method
+ type='method'
+ name='inetd_start'
+ exec='/usr/lib/inet/in.chargend -s'
+ timeout_seconds='0'>
+ <method_context>
+ <method_credential user='root' group='root' />
+ </method_context>
+ </exec_method>
+
+ <exec_method
+ type='method'
+ name='inetd_disable'
+ exec=':kill'
+ timeout_seconds='0'>
+ </exec_method>
+
+ <property_group name='inetd' type='framework'>
+ <propval name='endpoint_type' type='astring' value='stream' />
+ <propval name='proto' type='astring' value='tcp6' />
+ <propval name='wait' type='boolean' value='false' />
+ </property_group>
+ </instance>
+
+ <stability value='Unstable' />
+
+ <template>
+ <common_name>
+ <loctext xml:lang='C'>
+ character generator
+ </loctext>
+ </common_name>
+ <documentation>
+ </documentation>
+ </template>
+</service>
+
+</service_bundle>
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.chargend/in.chargend.c b/usr/src/cmd/cmd-inet/usr.lib/in.chargend/in.chargend.c
new file mode 100644
index 0000000000..ff7b26b9f7
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.chargend/in.chargend.c
@@ -0,0 +1,150 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * chargen inetd service - both stream and dgram based.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <strings.h>
+#include <netinet/in.h>
+#include <sys/sysmacros.h>
+#include <ctype.h>
+#include <inetsvc.h>
+
+
+#define LINESIZE 72
+#define RINGSIZE 128
+
+
+static char ring[RINGSIZE];
+static char *endring;
+
+
+static void
+initring(void)
+{
+ unsigned char ch;
+
+ endring = ring;
+
+ for (ch = 0; ch <= RINGSIZE; ++ch) {
+ if (isprint(ch))
+ *endring++ = ch;
+ }
+}
+
+static void
+chargen_stream(int s, char *argv[])
+{
+ char text[LINESIZE+2];
+ int i;
+ char *rp;
+ char *dp;
+ char *rs = ring;
+
+ setproctitle("chargen", s, argv);
+
+ for (;;) {
+ if (rs >= endring)
+ rs = ring;
+ rp = rs++;
+ dp = text;
+ i = MIN(LINESIZE, endring - rp);
+ (void) memmove(dp, rp, i);
+ dp += i;
+ if ((rp += i) >= endring)
+ rp = ring;
+ if (i < LINESIZE) {
+ i = LINESIZE - i;
+ (void) memmove(dp, rp, i);
+ dp += i;
+ if ((rp += i) >= endring)
+ rp = ring;
+ }
+
+ *dp++ = '\r';
+ *dp++ = '\n';
+
+ if (safe_write(s, text, dp - text) != 0)
+ break;
+ }
+}
+
+/* ARGSUSED3 */
+static void
+chargen_dg(int s, const struct sockaddr *sap, int sa_size, const void *buf,
+ size_t sz)
+{
+ char text[LINESIZE+2];
+ int i;
+ char *rp;
+ static char *rs = ring;
+
+ rp = rs;
+ if (rs++ >= endring)
+ rs = ring;
+ i = MIN(LINESIZE, endring - rp);
+ (void) memmove(text, rp, i);
+ if ((rp += i) >= endring)
+ rp = ring;
+ if (i < LINESIZE) {
+ (void) memmove(text, rp, i);
+ if ((rp += i) >= endring)
+ rp = ring;
+ }
+
+ text[LINESIZE - 2] = '\r';
+ text[LINESIZE - 1] = '\n';
+
+ (void) safe_sendto(s, text, sizeof (text), 0, sap, sa_size);
+}
+
+int
+main(int argc, char *argv[])
+{
+ opterr = 0; /* disable getopt error msgs */
+
+ initring();
+
+ switch (getopt(argc, argv, "ds")) {
+ case 'd':
+ dg_template(chargen_dg, STDIN_FILENO, NULL, 0);
+ break;
+ case 's':
+ chargen_stream(STDIN_FILENO, argv);
+ break;
+ default:
+ return (1);
+ }
+
+ return (0);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.daytimed/Makefile b/usr/src/cmd/cmd-inet/usr.lib/in.daytimed/Makefile
new file mode 100644
index 0000000000..a09aa45a3b
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.daytimed/Makefile
@@ -0,0 +1,34 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# cmd/cmd-inet/usr.lib/in.daytimed/%M%
+#
+
+PROG = in.daytimed
+MANIFEST= daytime.xml
+
+include ../Makefile.inetsvc
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.daytimed/daytime.xml b/usr/src/cmd/cmd-inet/usr.lib/in.daytimed/daytime.xml
new file mode 100644
index 0000000000..052078d919
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.daytimed/daytime.xml
@@ -0,0 +1,122 @@
+<?xml version='1.0'?>
+<!DOCTYPE service_bundle SYSTEM '/usr/share/lib/xml/dtd/service_bundle.dtd.1'>
+<!--
+ Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ ident "%Z%%M% %I% %E% SMI"
+
+ NOTE: This service manifest is not editable; its contents will
+ be overwritten by package or patch operations, including
+ operating system upgrade. Make customizations in a different
+ file.
+-->
+
+<service_bundle type='manifest' name='SUNWcnsr:daytime'>
+
+<service
+ name='network/daytime'
+ type='service'
+ version='1'>
+
+ <restarter>
+ <service_fmri value='svc:/network/inetd:default' />
+ </restarter>
+
+ <property_group name='inetd' type='framework'>
+ <stability value='Evolving' />
+ <propval name='name' type='astring' value='daytime' />
+ <propval name='isrpc' type='boolean' value='false' />
+ </property_group>
+
+ <instance name='dgram' enabled='false' >
+ <exec_method
+ type='method'
+ name='inetd_start'
+ exec='/usr/lib/inet/in.daytimed -d'
+ timeout_seconds='0'>
+ <method_context>
+ <method_credential user='root' group='root' />
+ </method_context>
+ </exec_method>
+
+ <exec_method
+ type='method'
+ name='inetd_offline'
+ exec=':kill_process'
+ timeout_seconds='0'>
+ </exec_method>
+
+ <exec_method
+ type='method'
+ name='inetd_disable'
+ exec=':kill'
+ timeout_seconds='0'>
+ </exec_method>
+
+ <property_group name='inetd' type='framework'>
+ <propval name='endpoint_type' type='astring' value='dgram' />
+ <propval name='proto' type='astring' value='udp6' />
+ <propval name='wait' type='boolean' value='true' />
+ </property_group>
+ </instance>
+
+ <instance name='stream' enabled='false' >
+ <exec_method
+ type='method'
+ name='inetd_start'
+ exec='/usr/lib/inet/in.daytimed -s'
+ timeout_seconds='0'>
+ <method_context>
+ <method_credential user='root' group='root' />
+ </method_context>
+ </exec_method>
+
+ <exec_method
+ type='method'
+ name='inetd_disable'
+ exec=':kill'
+ timeout_seconds='0'>
+ </exec_method>
+
+ <property_group name='inetd' type='framework'>
+ <propval name='endpoint_type' type='astring' value='stream' />
+ <propval name='proto' type='astring' value='tcp6' />
+ <propval name='wait' type='boolean' value='false' />
+ </property_group>
+ </instance>
+
+ <stability value='Unstable' />
+
+ <template>
+ <common_name>
+ <loctext xml:lang='C'>
+ daytime
+ </loctext>
+ </common_name>
+ <documentation>
+ </documentation>
+ </template>
+</service>
+
+</service_bundle>
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.daytimed/in.daytimed.c b/usr/src/cmd/cmd-inet/usr.lib/in.daytimed/in.daytimed.c
new file mode 100644
index 0000000000..6dd9ebd094
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.daytimed/in.daytimed.c
@@ -0,0 +1,89 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * daytime inetd service - both stream and dgram based.
+ * Return human-readable time of day.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <strings.h>
+#include <netinet/in.h>
+#include <inetsvc.h>
+
+
+#define TIMEBUF_SIZE 26
+
+
+static const char *
+daytime(void)
+{
+ time_t clock;
+ static char buf[TIMEBUF_SIZE];
+
+ clock = time(NULL);
+ (void) strlcpy(buf, ctime(&clock), sizeof (buf));
+ /*
+ * Format of ctime is "Fri Sep 13 00:00:00 1986\n\0". To conform to the
+ * required format as specified in RFCs 867 and 854 we replace the
+ * "\n\0" with "\r\n".
+ */
+ buf[TIMEBUF_SIZE - 2] = '\r';
+ buf[TIMEBUF_SIZE - 1] = '\n';
+
+ return (buf);
+}
+
+/* ARGSUSED3 */
+static void
+daytime_dg(int s, const struct sockaddr *sap, int sa_size, const void *buf,
+ size_t sz)
+{
+ (void) safe_sendto(s, daytime(), TIMEBUF_SIZE, 0, sap, sa_size);
+}
+
+int
+main(int argc, char *argv[])
+{
+ opterr = 0; /* disable getopt error msgs */
+ switch (getopt(argc, argv, "ds")) {
+ case 'd':
+ dg_template(daytime_dg, STDIN_FILENO, NULL, 0);
+ break;
+ case 's':
+ (void) safe_write(STDIN_FILENO, daytime(), TIMEBUF_SIZE);
+ break;
+ default:
+ return (1);
+ }
+
+ return (0);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/Makefile b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/Makefile
new file mode 100644
index 0000000000..d226e5abb4
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/Makefile
@@ -0,0 +1,108 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# cmd/cmd-inet/usr.lib/in.dhcpd/%M%
+#
+
+CMN_DIR = $(SRC)/common/net/dhcp
+NSU_DIR = $(ROOTLIBINET)/dhcp/nsu
+
+PROG = in.dhcpd
+MANIFEST= dhcp-server.xml
+
+LOCAL_OBJS = bootp.o dhcp.o dhcptab.o encode.o generic.o hash.o icmp.o \
+ interfaces.o logging.o main.o misc.o per_dnet.o relay.o
+LOCAL_SRCS = $(LOCAL_OBJS:%.o=%.c)
+
+CMN_OBJS = scan.o ipv4_sum.o
+CMN_SRCS = $(CMN_OBJS:%.o=$(CMN_DIR)/%.c)
+
+SRCS = $(LOCAL_SRCS) $(CMN_SRCS)
+OBJS = $(LOCAL_OBJS) $(CMN_OBJS)
+
+include ../../../Makefile.cmd
+
+ROOTMANIFESTDIR= $(ROOTSVCNETWORK)
+$(ROOTMANIFEST) := FILEMODE= 444
+
+CPPFLAGS += -DNDEBUG -DNPROBE -D_REENTRANT -I./ -I$(CMN_DIR)
+LINTFLAGS += -u
+LDFLAGS += -L$(NSU_DIR) -R/usr/lib/inet/dhcp/nsu
+LDLIBS += $(NSU_DIR)/rfc2136.so.1 -lrt -ldhcpsvc -ldhcputil -linetutil \
+ -lsocket -lnsl -lmtmalloc -lresolv
+lint := LDLIBS = $(LDLIBS.cmd) -ldhcpsvc -ldhcputil -linetutil -lsocket -lnsl
+
+#
+# Debugging support; toggle on if needed.
+#
+#CPPFLAGS += -DDEBUG # if used, remove -DNDEBUG above
+#CPPFLAGS += -DTNF_DEBUG
+#COPTFLAG = -g
+#COPTFLAG += -xprofile=func
+
+# for messaging catalog. No messages are present in CMN_DIR sources.
+POFILES = $(LOCAL_OBJS:%.o=%.po)
+XGETFLAGS += -a -x in.dhcpd.xcl
+
+.PARALLEL: $(OBJS)
+.WAIT: $(PROG)
+.KEEP_STATE:
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+ $(LINK.c) $(OBJS) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+include ../Makefile.lib
+
+install: all $(ROOTLIBINETPROG) $(ROOTMANIFEST)
+
+%.o: $(CMN_DIR)/%.c
+ $(COMPILE.c) $(OUTPUT_OPTION) $<
+ $(POST_PROCESS_O)
+
+$(ROOTMANIFEST): $(ROOTMANIFESTDIR)
+
+$(ROOTMANIFESTDIR):
+ $(INS.dir)
+
+$(ROOTMANIFESTDIR)/%: %
+ $(INS.file)
+
+$(POFILE): $(POFILES)
+ $(RM) $@
+ $(CAT) $(POFILES) > $@
+
+check: $(CHKMANIFEST)
+
+clean:
+ $(RM) $(OBJS)
+
+lint: lint_SRCS
+
+include ../../../Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/README.caching b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/README.caching
new file mode 100644
index 0000000000..a8f9dad855
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/README.caching
@@ -0,0 +1,164 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+The DHCP server cache implementation
+Zhenghui.Xie@sun.com
+
+
+#ident "%Z%%M% %I% %E% SMI"
+
+INTRODUCTION
+============
+
+The Solaris DHCP server implements a caching mechanism to:
+
+ * Ensure the ACK is consistent with the original OFFER, so that
+ subsequent requests get the same answer.
+ * Ensure the same IP address isn't offered to a different client.
+ * Improve performance by reducing the frequency of datastore lookups.
+
+IMPLEMENTATION OVERVIEW
+=======================
+
+The cache implementation consists of a number of hash tables and lists,
+enumerated below, along with a timeout and refresh mechanism:
+
+ * A global DHCP table hash (ntable):
+
+ Each active network table (dvsc_dnet_t) is hashed by subnet number
+ into a global hash called 'ntable'. The dsvc_dnet_t itself
+ contains a variety of information about each subnet, an access
+ handle to the underlying datastore, and a variety of caches which
+ we describe next.
+
+ An ntable hash entry will be removed after DHCP_NET_THRESHOLD
+ seconds of inactivity.
+
+ * A per-network client hash (ctable):
+
+ Each client structure (dsvc_clnt_t) is hashed by client identifier
+ into the per-network ctable. This table is used by the interface
+ worker thread to get client information.
+
+ A ctable hash entry will be removed if the client does not communicate
+ with the server for DHCP_CLIENT_THRESHOLD seconds.
+
+ * A per-network offer hash (itable):
+
+ The IP address associated with each pending OFFER is hashed into
+ the per-network itable. This table is used to reserve the offered
+ and in-use IP addresses on the given network.
+
+ An itable hash entry will be removed if more than DSVC_CV_OFFER_TTL
+ seconds elapse without update_offer() being called on it.
+
+ * A per-network free record list (freerec):
+
+ This is a cache of free records, populated with any unused records
+ retrieved from previous datastore lookups or from IP addresses that
+ have been explicitly released by DHCP clients. This is the first
+ list select_offer() consults.
+
+ An entry in the freerec list expires after DSVC_CV_CACHE_TTL seconds.
+
+ * A per-network least recently used record list (lrurec)
+
+ This is a cache of least recently used records, populated with any
+ unused records retrieved from a previous datastore lookup for
+ LRU records. This is the second list select_offer() consults, after
+ checking freerec.
+
+ An entry in the lrurec list expires after DSVC_CV_CACHE_TTL seconds.
+
+The concurrency between the datastore and cached records is handled by the
+underlying datastore implementation using perimeters and is transparent to
+in.dhcpd. $SRC/lib/libdhcpsvc/private/{private.c,public.c} implement the
+functions used by in.dhcpd and DHCP server admin tools to lookup and modify
+the underlying datastore records.
+
+TRANSACTIONS
+============
+
+When in.dhcpd receives a packet from a client, the interface thread first
+calls open_dnet() to retrieve the dsvc_dnet_t which describes the network.
+The dsvc_dnet_t is either in the ntable hash, in which case it is returned,
+or a new dsvc_dnet_t is allocated and inserted to ntable. Then, using the
+dsvc_dnet_t, the interface thread calls open_clnt(), which searches the
+dsvc_dnet_t's ctable hash and returns the client structure (dsvc_clnt_t) if
+found. Otherwise, a new dsvc_clnt_t is allocated and inserted into the
+ctable. Finally, the packet is put to the client's packet list so that the
+client thread can process it.
+
+The client thread then processes the packet according to whether it's a
+DISCOVER, REQUEST, RELEASE, or DECLINE.
+
+For a DISCOVER:
+
+ 1. If there is a pre-assigned IP for this client and the cached offer
+ is not timed out, then use the IP and the record in the
+ dsvc_clnt_t and make an OFFER.
+
+ 2. If there is a pre-assigned IP but the cached offer is expired,
+ remove the cached OFFER from the itable, and try to find a new
+ record for the client (see below).
+
+ 3. If there is no pre-assigned IP for this client, just try to find
+ a new record for the client (see following text).
+
+ To find a new record, the client thread first searches the datastore
+ for a record matching the client identifier provided in the packet.
+ If none is found, then the free record list (freerec) and least
+ recently used record list (lrurec) are searched, in that order. If
+ either freerec or lrurec is empty, or the head record on either list
+ is expired, in.dhcpd removes any existing records and attempts to
+ repopulate them by performing datastore lookups. Finally, any
+ unused records are cached for later use.
+
+ If a usable record is found, the server generates and sends an OFFER
+ to the client. Once sent, the client's dsvc_clnt_t is inserted to
+ the dsvc_dnet_t's itable if it is a new IP, or the itable is refreshed
+ if it is a pre-assigned IP.
+
+For a REQUEST:
+
+ 1. If the REQUEST is a reply to a previous OFFER, it checks if the
+ OFFER has expired. If not, the itable timer is reset, the client
+ record is updated, and the ACK is sent. If it is expired and the
+ address cannot be confirmed to still be free, the REQUEST is
+ silently ignored (and the client should eventually drop back
+ to DISCOVER).
+
+ 2. If the REQUEST is associated with a client INIT-REBOOT or a client
+ extending a lease, then the client thread does a datastore lookup
+ by client identifier. If a match is found, the record is updated
+ and an ACK is sent.
+
+If the packet is a RELEASE, the client thread modifies the record in the
+datastore so that it is marked free, and, if successful, puts the record onto
+the free record list (freerec).
+
+Finally, if the packet is a DECLINE, the client thread modifies the record in
+the datastore so that it is marked unusable.
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/bootp.c b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/bootp.c
new file mode 100644
index 0000000000..dbb99da607
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/bootp.c
@@ -0,0 +1,407 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <assert.h>
+#include <string.h>
+#include <syslog.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <arpa/inet.h>
+#include <nss_dbdefs.h>
+#include <netinet/dhcp.h>
+#include <netdb.h>
+#include <dhcp_symbol.h>
+#include "dhcpd.h"
+#include "per_dnet.h"
+#include "interfaces.h"
+#include <locale.h>
+
+/*
+ * This file contains the code which implements the BOOTP compatibility.
+ */
+
+/*
+ * We are guaranteed that the packet received is a BOOTP request packet,
+ * e.g., *NOT* a DHCP packet.
+ */
+void
+bootp(dsvc_clnt_t *pcd, PKT_LIST *plp)
+{
+ boolean_t result, existing_offer = B_FALSE;
+ int err, write_error = DSVC_SUCCESS, flags = 0;
+ int pkt_len;
+ uint_t crecords = 0, irecords = 0, srecords = 0, clen;
+ uint32_t query;
+ PKT *rep_pktp = NULL;
+ IF *ifp = pcd->ifp;
+ dsvc_dnet_t *pnd = pcd->pnd;
+ uchar_t *optp;
+ dn_rec_t dn, ndn, *dnp;
+ dn_rec_list_t *dncp = NULL, *dnip = NULL, *dnlp = NULL;
+ struct in_addr ciaddr;
+ struct in_addr no_ip; /* network order IP */
+ ENCODE *ecp, *hecp;
+ MACRO *mp, *nmp, *cmp;
+ time_t now = time(NULL);
+ DHCP_MSG_CATEGORIES log;
+ struct hostent h, *hp;
+ char ntoab[INET_ADDRSTRLEN], cipbuf[INET_ADDRSTRLEN];
+ char cidbuf[DHCP_MAX_OPT_SIZE];
+ char hbuf[NSS_BUFLEN_HOSTS];
+
+#ifdef DEBUG
+ dhcpmsg(LOG_DEBUG, "BOOTP request received on %s\n", ifp->nm);
+#endif /* DEBUG */
+
+ if (pcd->off_ip.s_addr != htonl(INADDR_ANY) &&
+ PCD_OFFER_TIMEOUT(pcd, now))
+ purge_offer(pcd, B_TRUE, B_TRUE);
+
+ if (pcd->off_ip.s_addr != htonl(INADDR_ANY)) {
+ existing_offer = B_TRUE;
+ dnlp = pcd->dnlp;
+ assert(dnlp != NULL);
+ dnp = dnlp->dnl_rec;
+ no_ip.s_addr = htonl(dnp->dn_cip.s_addr);
+ crecords = 1;
+ } else {
+ /*
+ * Try to find a CID entry for the client. We don't care about
+ * lease info here, since a BOOTP client always has a permanent
+ * lease. We also don't care about the entry owner either,
+ * unless we end up allocating a new entry for the client.
+ */
+ DSVC_QINIT(query);
+
+ DSVC_QEQ(query, DN_QCID);
+ (void) memcpy(dn.dn_cid, pcd->cid, pcd->cid_len);
+ dn.dn_cid_len = pcd->cid_len;
+
+ DSVC_QEQ(query, DN_QFBOOTP_ONLY);
+ dn.dn_flags = DN_FBOOTP_ONLY;
+
+ /*
+ * If a client address (ciaddr) is given, we simply trust that
+ * the client knows what it's doing, and we use that IP address
+ * to locate the client's record. If we can't find the client's
+ * record, then we keep silent. If the client id of the record
+ * doesn't match this client, then either the client or our
+ * database is inconsistent, and we'll ignore it (notice
+ * message generated).
+ */
+ ciaddr.s_addr = plp->pkt->ciaddr.s_addr;
+ if (ciaddr.s_addr != htonl(INADDR_ANY)) {
+ DSVC_QEQ(query, DN_QCIP);
+ dn.dn_cip.s_addr = ntohl(ciaddr.s_addr);
+ }
+
+ dnlp = dhcp_lookup_dd_classify(pcd->pnd, B_FALSE, query,
+ -1, &dn, (void **)&dncp, S_CID);
+ if (dnlp != NULL) {
+ crecords = 1;
+ dnp = dnlp->dnl_rec;
+ if (dnp->dn_flags & DN_FUNUSABLE)
+ goto leave_bootp;
+ no_ip.s_addr = htonl(dnp->dn_cip.s_addr);
+ }
+ }
+
+ (void) inet_ntop(AF_INET, &no_ip, cipbuf, sizeof (cipbuf));
+
+ if (crecords == 0 && !be_automatic) {
+ if (verbose) {
+ dhcpmsg(LOG_INFO, "BOOTP client: %1$s is looking for "
+ "a configuration on net %2$s\n", pcd->cidbuf,
+ pnd->network);
+ }
+ goto leave_bootp;
+ }
+
+ /*
+ * If the client thinks it knows who it is (ciaddr), and this doesn't
+ * match our registered IP address, then display an error message and
+ * give up.
+ */
+ if (ciaddr.s_addr != htonl(INADDR_ANY) && crecords == 0) {
+ /*
+ * If the client specified an IP address, then let's check
+ * whether it is available, since we have no CID mapping
+ * registered for this client. If it is available and
+ * unassigned but owned by a different server, we ignore the
+ * client.
+ */
+ DSVC_QINIT(query);
+
+ DSVC_QEQ(query, DN_QCIP);
+ dn.dn_cip.s_addr = ntohl(ciaddr.s_addr);
+ (void) inet_ntop(AF_INET, &ciaddr, cipbuf, sizeof (cipbuf));
+
+ DSVC_QEQ(query, DN_QFBOOTP_ONLY);
+ dn.dn_flags = DN_FBOOTP_ONLY;
+
+ dnip = NULL;
+ dnlp = dhcp_lookup_dd_classify(pcd->pnd, B_FALSE, query,
+ -1, &dn, (void **)&dncp, S_CID);
+ if (dnlp == NULL) {
+ /*
+ * We have no record of this client's IP address, thus
+ * we really can't respond to this client, because it
+ * doesn't have a configuration.
+ */
+ if (verbose) {
+ dhcpmsg(LOG_INFO, "No configuration for BOOTP "
+ "client: %1$s. IP address: %2$s not "
+ "administered by this server.\n",
+ pcd->cidbuf, inet_ntop(AF_INET, &ciaddr,
+ ntoab, sizeof (ntoab)));
+ }
+ goto leave_bootp;
+ } else
+ irecords = 1;
+
+ dnp = dnlp->dnl_rec;
+ if (dnp->dn_flags & DN_FUNUSABLE)
+ goto leave_bootp;
+
+ if (dn.dn_cid_len != 0) {
+ if (dn.dn_cid_len != pcd->cid_len || memcmp(dn.dn_cid,
+ pcd->cid, pcd->cid_len) != 0) {
+ if (verbose) {
+ clen = sizeof (cidbuf);
+ (void) octet_to_hexascii(dn.dn_cid,
+ dn.dn_cid_len, cidbuf, &clen);
+ dhcpmsg(LOG_INFO, "BOOTP client: %1$s "
+ "thinks it owns %2$s, but that "
+ "address belongs to %3$s. Ignoring "
+ "client.\n", pcd->cidbuf, cipbuf,
+ cidbuf);
+ }
+ goto leave_bootp;
+ }
+ } else {
+ if (match_ownerip(htonl(dn.dn_sip.s_addr)) == NULL) {
+ if (verbose) {
+ no_ip.s_addr =
+ htonl(dnp->dn_sip.s_addr);
+ dhcpmsg(LOG_INFO, "BOOTP client: %1$s "
+ "believes it owns %2$s. That "
+ "address is free, but is owned by "
+ "DHCP server %3$s. Ignoring "
+ "client.\n", pcd->cidbuf, cipbuf,
+ inet_ntop(AF_INET, &no_ip, ntoab,
+ sizeof (ntoab)));
+ }
+ goto leave_bootp;
+ }
+ }
+ no_ip.s_addr = htonl(dnp->dn_cip.s_addr);
+ (void) inet_ntop(AF_INET, &no_ip, cipbuf, sizeof (cipbuf));
+ }
+
+ if (crecords == 0) {
+ /*
+ * The dhcp-network table did not have any matching entries.
+ * Try to allocate a new one if possible.
+ */
+ if (irecords == 0 && select_offer(pnd, plp, pcd, &dnlp)) {
+ dnp = dnlp->dnl_rec;
+ no_ip.s_addr = htonl(dnp->dn_cip.s_addr);
+ (void) inet_ntop(AF_INET, &no_ip, cipbuf,
+ sizeof (cipbuf));
+ srecords = 1;
+ }
+ }
+
+ if (crecords == 0 && irecords == 0 && srecords == 0) {
+ dhcpmsg(LOG_NOTICE,
+ "(%1$s) No more BOOTP IP addresses for %2$s network.\n",
+ pcd->cidbuf, pnd->network);
+ goto leave_bootp;
+ }
+
+ /* Check the address. But only if client doesn't know its address. */
+ ndn = *dnp; /* struct copy */
+ no_ip.s_addr = htonl(ndn.dn_cip.s_addr);
+ (void) inet_ntop(AF_INET, &no_ip, cipbuf, sizeof (cipbuf));
+ if (ciaddr.s_addr == htonl(INADDR_ANY)) {
+ if ((ifp->flags & IFF_NOARP) == 0)
+ (void) set_arp(ifp, &no_ip, NULL, 0, DHCP_ARP_DEL);
+ if (!noping) {
+ /*
+ * If icmp echo check fails,
+ * let the plp fall by the wayside.
+ */
+ errno = icmp_echo_check(&no_ip, &result);
+ if (errno != 0) {
+ dhcpmsg(LOG_ERR, "ICMP ECHO check cannot be "
+ "performed for: %s, ignoring\n", cipbuf);
+ goto leave_bootp;
+ }
+ if (result) {
+ dhcpmsg(LOG_ERR, "ICMP ECHO reply to BOOTP "
+ "OFFER candidate: %s, disabling.\n",
+ cipbuf);
+
+ ndn.dn_flags |= DN_FUNUSABLE;
+
+ if ((err = dhcp_modify_dd_entry(pnd->dh,
+ dnp, &ndn)) == DSVC_SUCCESS) {
+ /* Keep the cached entry current. */
+ *dnp = ndn; /* struct copy */
+ }
+
+ logtrans(P_BOOTP, L_ICMP_ECHO, 0, no_ip,
+ server_ip, plp);
+
+ goto leave_bootp;
+ }
+ }
+ }
+
+ /*
+ * It is possible that the client could specify a REQUEST list,
+ * but then it would be a DHCP client, wouldn't it? Only copy the
+ * std option list, since that potentially could be changed by
+ * load_options().
+ */
+ ecp = NULL;
+ if (!no_dhcptab) {
+ open_macros();
+ if ((nmp = get_macro(pnd->network)) != NULL)
+ ecp = dup_encode_list(nmp->head);
+ if ((mp = get_macro(dnp->dn_macro)) != NULL)
+ ecp = combine_encodes(ecp, mp->head, ENC_DONT_COPY);
+ if ((cmp = get_macro(pcd->cidbuf)) != NULL)
+ ecp = combine_encodes(ecp, cmp->head, ENC_DONT_COPY);
+
+ /* If dhcptab configured to return hostname, do so. */
+ if (find_encode(ecp, DSYM_INTERNAL, CD_BOOL_HOSTNAME) !=
+ NULL) {
+ hp = gethostbyaddr_r((char *)&ndn.dn_cip,
+ sizeof (struct in_addr), AF_INET, &h, hbuf,
+ sizeof (hbuf), &err);
+ if (hp != NULL) {
+ hecp = make_encode(DSYM_STANDARD,
+ CD_HOSTNAME, strlen(hp->h_name),
+ hp->h_name, ENC_COPY);
+ replace_encode(&ecp, hecp, ENC_DONT_COPY);
+ }
+ }
+ }
+
+ /* Produce a BOOTP reply. */
+ rep_pktp = gen_bootp_pkt(sizeof (PKT), plp->pkt);
+
+ rep_pktp->op = BOOTREPLY;
+ optp = rep_pktp->options;
+
+ /*
+ * Set the client's "your" IP address if client doesn't know it,
+ * otherwise echo the client's ciaddr back to him.
+ */
+ if (ciaddr.s_addr == htonl(INADDR_ANY))
+ rep_pktp->yiaddr.s_addr = htonl(ndn.dn_cip.s_addr);
+ else
+ rep_pktp->ciaddr.s_addr = ciaddr.s_addr;
+
+ /*
+ * Omit lease time options implicitly, e.g.
+ * ~(DHCP_DHCP_CLNT | DHCP_SEND_LEASE)
+ */
+
+ if (!plp->rfc1048)
+ flags |= DHCP_NON_RFC1048;
+
+ /* Now load in configured options. */
+ pkt_len = load_options(flags, plp, rep_pktp, sizeof (PKT), optp, ecp,
+ NULL);
+
+ free_encode_list(ecp);
+ if (!no_dhcptab)
+ close_macros();
+
+ if (pkt_len < sizeof (PKT))
+ pkt_len = sizeof (PKT);
+
+ /*
+ * Only perform a write if we have selected an entry not yet
+ * assigned to the client (a matching DN_FBOOTP_ONLY entry from
+ * ip address lookup, or an unassigned entry from select_offer()).
+ */
+ if (srecords > 0 || irecords > 0) {
+ (void) memcpy(&ndn.dn_cid, pcd->cid, pcd->cid_len);
+ ndn.dn_cid_len = pcd->cid_len;
+
+ write_error = dhcp_modify_dd_entry(pnd->dh, dnp, &ndn);
+
+ /* Keep state of the cached entry current. */
+ *dnp = ndn; /* struct copy */
+
+ log = L_ASSIGN;
+ } else {
+ if (verbose) {
+ dhcpmsg(LOG_INFO, "Database write unnecessary for "
+ "BOOTP client: %1$s, %2$s\n",
+ pcd->cidbuf, cipbuf);
+ }
+ log = L_REPLY;
+ }
+
+ if (write_error == DSVC_SUCCESS) {
+ if (send_reply(ifp, rep_pktp, pkt_len, &no_ip) != 0) {
+ dhcpmsg(LOG_ERR,
+ "Reply to BOOTP client %1$s with %2$s failed.\n",
+ pcd->cidbuf, cipbuf);
+ } else {
+ /* Note that the conversation has completed. */
+ pcd->state = ACK;
+
+ (void) update_offer(pcd, dnlp, 0, &no_ip, B_TRUE);
+ existing_offer = B_TRUE;
+ }
+
+ logtrans(P_BOOTP, log, ndn.dn_lease, no_ip, server_ip, plp);
+ }
+
+leave_bootp:
+ if (rep_pktp != NULL)
+ free(rep_pktp);
+ if (dncp != NULL)
+ dhcp_free_dd_list(pnd->dh, dncp);
+ if (dnip != NULL)
+ dhcp_free_dd_list(pnd->dh, dnip);
+ if (dnlp != NULL && !existing_offer)
+ dhcp_free_dd_list(pnd->dh, dnlp);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/dhcp-server.xml b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/dhcp-server.xml
new file mode 100644
index 0000000000..eec66690c1
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/dhcp-server.xml
@@ -0,0 +1,124 @@
+<?xml version='1.0'?>
+<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
+<!--
+ Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ ident "%Z%%M% %I% %E% SMI"
+
+ NOTE: This service manifest is not editable; its contents will
+ be overwritten by package or patch operations, including
+ operating system upgrade. Make customizations in a different
+ file.
+
+ Service manifest for the SunOS DHCP service.
+-->
+
+<service_bundle type='manifest' name='SUNWdhcsr:dhcp-server'>
+
+<service
+ name='network/dhcp-server'
+ type='service'
+ version='1'>
+
+ <create_default_instance enabled='false' />
+
+ <single_instance/>
+
+ <!--
+ We historically ran only in run-level 3, so depend on the
+ equivalent milestone to level 2. We restart on refresh in
+ order to handle any changes to nameservice or filesystem
+ or other service configuration which may affect DHCP service.
+ -->
+ <dependency
+ name='multi-user'
+ grouping='require_all'
+ restart_on='refresh'
+ type='service'>
+ <service_fmri value='svc:/milestone/multi-user' />
+ </dependency>
+
+ <dependency
+ name='config_data'
+ grouping='require_all'
+ restart_on='restart'
+ type='path'>
+ <service_fmri value='file:///etc/inet/dhcpsvc.conf' />
+ </dependency>
+
+ <dependent
+ name='dhcp_multi-user-server'
+ grouping='optional_all'
+ restart_on='none'>
+ <service_fmri value='svc:/milestone/multi-user-server' />
+ </dependent>
+
+ <exec_method
+ type='method'
+ name='start'
+ exec='/usr/lib/inet/in.dhcpd'
+ timeout_seconds='60' >
+ <method_context>
+ <method_credential
+ user='root'
+ group='root'
+ privileges='all'
+ />
+ </method_context>
+ </exec_method>
+
+ <exec_method
+ type='method'
+ name='stop'
+ exec=':kill'
+ timeout_seconds='60' />
+
+ <exec_method
+ type='method'
+ name='refresh'
+ exec=':kill -HUP'
+ timeout_seconds='60' />
+
+ <stability value='Unstable' />
+
+ <template>
+ <common_name>
+ <loctext xml:lang='C'>
+ DHCP server
+ </loctext>
+ </common_name>
+ <description>
+ <loctext xml:lang='C'>
+The SunOS DHCP server, which provides DHCP and BOOTP
+protocol services to DHCP and BOOTP clients.
+ </loctext>
+ </description>
+ <documentation>
+ <manpage title='in.dhcpd' section='1M'
+ manpath='/usr/share/man' />
+ </documentation>
+ </template>
+</service>
+
+</service_bundle>
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/dhcp.c b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/dhcp.c
new file mode 100644
index 0000000000..44d74f9a27
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/dhcp.c
@@ -0,0 +1,2909 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <alloca.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/sysmacros.h>
+#include <assert.h>
+#include <sys/socket.h>
+#include <sys/byteorder.h>
+#include <string.h>
+#include <time.h>
+#include <sys/socket.h>
+#include <syslog.h>
+#include <sys/errno.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <netinet/dhcp.h>
+#include <dhcp_symbol.h>
+#include <nss_dbdefs.h>
+#include <dlfcn.h>
+#include "dhcpd.h"
+#include "per_dnet.h"
+#include "interfaces.h"
+#include <locale.h>
+#include <resolv.h>
+
+static void dhcp_offer(dsvc_clnt_t *, PKT_LIST *);
+static void dhcp_req_ack(dsvc_clnt_t *, PKT_LIST *);
+static void dhcp_dec_rel(dsvc_clnt_t *, PKT_LIST *, int);
+static void dhcp_inform(dsvc_clnt_t *, PKT_LIST *);
+static PKT *gen_reply_pkt(dsvc_clnt_t *, PKT_LIST *, int, uint_t *,
+ uchar_t **, struct in_addr *);
+static void set_lease_option(ENCODE **, lease_t);
+static int config_lease(PKT_LIST *, dn_rec_t *, ENCODE **, lease_t, boolean_t);
+static int is_option_requested(PKT_LIST *, ushort_t);
+static void add_request_list(IF *, PKT_LIST *, ENCODE **, struct in_addr *);
+static char *disp_clnt_msg(PKT_LIST *, char *, int);
+static void add_dnet_cache(dsvc_dnet_t *, dn_rec_list_t *);
+static void purge_dnet_cache(dsvc_dnet_t *, dn_rec_t *);
+
+static boolean_t addr_avail(dsvc_dnet_t *, dsvc_clnt_t *, dn_rec_list_t **,
+ struct in_addr, boolean_t);
+static boolean_t name_avail(char *, dsvc_clnt_t *, PKT_LIST *,
+ dn_rec_list_t **, ENCODE *, struct in_addr **);
+static boolean_t entry_available(dsvc_clnt_t *, dn_rec_t *);
+static boolean_t do_nsupdate(struct in_addr, ENCODE *, PKT_LIST *);
+
+extern int dns_puthostent(struct hostent *, time_t);
+
+/*
+ * Offer cache.
+ *
+ * The DHCP server maintains a cache of DHCP OFFERs it has extended to DHCP
+ * clients. It does so because:
+ * a) Subsequent requests get the same answer, and the same IP address
+ * isn't offered to a different client.
+ *
+ * b) No ICMP validation is required the second time through, nor is a
+ * database lookup required.
+ *
+ * c) If the client accepts the OFFER and sends a REQUEST, we can simply
+ * lookup the record by client IP address, the one field guaranteed to
+ * be unique within the dhcp network table.
+ *
+ * We don't explicitly delete entries from the offer cache. We let them time
+ * out on their own. This is done to ensure the server responds correctly when
+ * many pending client requests are queued (duplicates). We don't want to ICMP
+ * validate an IP address we just allocated.
+ *
+ * The offer cache (and any database records cached in select_offer()) will
+ * diverge from the database for the length of the D_OFFER lifetime.
+ * SIGHUP flushes the offer cache, allowing management tools to inform the
+ * server of changes in a timely manner.
+ */
+
+/*
+ * Print the DHCP packet type.
+ */
+static void
+dhcpmsgtype(uchar_t pkt, char *buf)
+{
+ char *p;
+
+ switch (pkt) {
+ case DISCOVER:
+ p = "DISCOVER";
+ break;
+ case OFFER:
+ p = "OFFER";
+ break;
+ case REQUEST:
+ p = "REQUEST";
+ break;
+ case DECLINE:
+ p = "DECLINE";
+ break;
+ case ACK:
+ p = "ACK";
+ break;
+ case NAK:
+ p = "NAK";
+ break;
+ case RELEASE:
+ p = "RELEASE";
+ break;
+ case INFORM:
+ p = "INFORM";
+ break;
+ default:
+ p = "UNKNOWN";
+ break;
+ }
+
+ (void) strcpy(buf, p);
+}
+
+/*
+ * Dispatch the DHCP packet based on its type.
+ */
+void
+dhcp(dsvc_clnt_t *pcd, PKT_LIST *plp)
+{
+ char buf[32];
+
+ dhcpmsgtype(*plp->opts[CD_DHCP_TYPE]->value, buf);
+
+ if (plp->opts[CD_DHCP_TYPE]->len != 1) {
+ dhcpmsg(LOG_ERR,
+ "Garbled DHCP Message type option from client: %s\n",
+ pcd->cidbuf);
+ return;
+ }
+
+ pcd->state = *plp->opts[CD_DHCP_TYPE]->value;
+ switch (pcd->state) {
+ case DISCOVER:
+#ifdef DEBUG
+ dhcpmsg(LOG_DEBUG, "dhcp() - processing OFFER...\n");
+#endif /* DEBUG */
+ dhcp_offer(pcd, plp);
+#ifdef DEBUG
+ dhcpmsg(LOG_DEBUG, "dhcp() - processed OFFER.\n");
+#endif /* DEBUG */
+ break;
+ case REQUEST:
+#ifdef DEBUG
+ dhcpmsg(LOG_DEBUG, "dhcp() - processing REQUEST...\n");
+#endif /* DEBUG */
+ dhcp_req_ack(pcd, plp);
+#ifdef DEBUG
+ dhcpmsg(LOG_DEBUG, "dhcp() - processed REQUEST.\n");
+#endif /* DEBUG */
+ break;
+ case DECLINE:
+#ifdef DEBUG
+ dhcpmsg(LOG_DEBUG, "dhcp() - processing DECLINE...\n");
+#endif /* DEBUG */
+ dhcp_dec_rel(pcd, plp, DECLINE);
+#ifdef DEBUG
+ dhcpmsg(LOG_DEBUG, "dhcp() - processed DECLINE.\n");
+#endif /* DEBUG */
+ break;
+ case RELEASE:
+#ifdef DEBUG
+ dhcpmsg(LOG_DEBUG, "dhcp() - processing RELEASE...\n");
+#endif /* DEBUG */
+ dhcp_dec_rel(pcd, plp, RELEASE);
+#ifdef DEBUG
+ dhcpmsg(LOG_DEBUG, "dhcp() - processed RELEASE.\n");
+#endif /* DEBUG */
+ break;
+ case INFORM:
+#ifdef DEBUG
+ dhcpmsg(LOG_DEBUG, "dhcp() - processing INFORM...\n");
+#endif /* DEBUG */
+ dhcp_inform(pcd, plp);
+#ifdef DEBUG
+ dhcpmsg(LOG_DEBUG, "dhcp() - processed INFORM.\n");
+#endif /* DEBUG */
+ break;
+ default:
+ dhcpmsg(LOG_INFO,
+ "Unexpected DHCP message type: %d from client: %s.\n",
+ plp->opts[CD_DHCP_TYPE]->value, pcd->cidbuf);
+ break;
+ }
+}
+
+/*
+ * Responding to a DISCOVER message. icmp echo check (if done) is synchronous.
+ * Previously known requests are in the OFFER cache.
+ */
+static void
+dhcp_offer(dsvc_clnt_t *pcd, PKT_LIST *plp)
+{
+ IF *ifp = pcd->ifp;
+ boolean_t result;
+ struct in_addr nsip, ncip;
+ dsvc_dnet_t *pnd = pcd->pnd;
+ uint_t replen;
+ int used_pkt_len;
+ PKT *rep_pktp = NULL;
+ uchar_t *optp;
+ ENCODE *ecp, *vecp, *macro_ecp, *macro_vecp,
+ *class_ecp, *class_vecp,
+ *cid_ecp, *cid_vecp,
+ *net_ecp, *net_vecp;
+ MACRO *net_mp, *pkt_mp, *class_mp, *cid_mp;
+ char *class_id;
+ time_t now = time(NULL);
+ lease_t newlease, oldlease = 0;
+ int err = 0;
+ boolean_t existing_allocation = B_FALSE;
+ boolean_t existing_offer = B_FALSE;
+ char sipstr[INET_ADDRSTRLEN], cipstr[INET_ADDRSTRLEN];
+ char class_idbuf[DSYM_CLASS_SIZE];
+ dn_rec_t *dnp, dn, ndn;
+ uint32_t query;
+ dn_rec_list_t *dncp = NULL, *dnlp = NULL;
+ boolean_t unreserve = B_FALSE;
+
+ class_id = get_class_id(plp, class_idbuf, sizeof (class_idbuf));
+
+ /*
+ * Purge offers when expired or the database has been re-read.
+ *
+ * Multi-threading: to better distribute garbage collection
+ * and data structure aging tasks, each thread must actively
+ * implement policy, rather then specialized, non-scalable
+ * threads which halt the server and update all data
+ * structures.
+ *
+ * The test below checks whether the offer has expired,
+ * due to aging, or re-reading of the dhcptab, via timeout
+ * or explicit signal.
+ */
+ if (pcd->off_ip.s_addr != htonl(INADDR_ANY) &&
+ PCD_OFFER_TIMEOUT(pcd, now))
+ purge_offer(pcd, B_TRUE, B_TRUE);
+
+ if (pcd->off_ip.s_addr != htonl(INADDR_ANY)) {
+ /*
+ * We've already validated this IP address in the past, and
+ * due to the OFFER hash table, we would not have offered this
+ * IP address to another client, so use the offer-cached record.
+ */
+ existing_offer = B_TRUE;
+ dnlp = pcd->dnlp;
+ dnp = dnlp->dnl_rec;
+ ncip.s_addr = htonl(dnp->dn_cip.s_addr);
+ } else {
+ /* Try to find an existing usable entry for the client. */
+ DSVC_QINIT(query);
+ DSVC_QEQ(query, DN_QCID);
+ (void) memcpy(dn.dn_cid, pcd->cid, pcd->cid_len);
+ dn.dn_cid_len = pcd->cid_len;
+
+ /* No bootp records, thank you. */
+ DSVC_QNEQ(query, DN_QFBOOTP_ONLY);
+ dn.dn_flags = DN_FBOOTP_ONLY;
+
+ /*
+ * We don't limit this search by SIP, because this client
+ * may be owned by another server, and we need to detect this
+ * since that record may be MANUAL.
+ */
+ dncp = NULL;
+ dnlp = dhcp_lookup_dd_classify(pcd->pnd, B_FALSE, query,
+ -1, &dn, (void **)&dncp, S_CID);
+
+ while (dnlp != NULL) {
+
+ dnp = dnlp->dnl_rec;
+ if (match_ownerip(htonl(dnp->dn_sip.s_addr)) == NULL) {
+ /*
+ * An IP address, but not ours! It's up to the
+ * primary to respond to DISCOVERs on this
+ * address.
+ */
+ if (verbose) {
+ char *m1, *m2;
+
+ if (dnp->dn_flags & DN_FMANUAL) {
+ m1 = "MANUAL";
+ m2 = " No other IP address "
+ "will be allocated.";
+ } else {
+ m1 = "DYNAMIC";
+ m2 = "";
+ }
+
+ nsip.s_addr = htonl(dnp->dn_sip.s_addr);
+ (void) inet_ntop(AF_INET, &nsip, sipstr,
+ sizeof (sipstr));
+ ncip.s_addr = htonl(dnp->dn_cip.s_addr);
+ (void) inet_ntop(AF_INET, &ncip, cipstr,
+ sizeof (cipstr));
+ dhcpmsg(LOG_INFO, "Client: %1$s has "
+ "%2$s %3$s owned by server: "
+ "%4$s.%5$s\n", pcd->cidbuf,
+ m1, cipstr, sipstr, m2);
+ }
+
+ /* We give up if that IP address is manual */
+ if (dnp->dn_flags & DN_FMANUAL)
+ goto leave_offer;
+ } else {
+ uint_t bits = DN_FUNUSABLE | DN_FMANUAL;
+ if ((dnp->dn_flags & bits) == bits) {
+ ncip.s_addr = htonl(dnp->dn_cip.s_addr);
+ (void) inet_ntop(AF_INET, &ncip, cipstr,
+ sizeof (cipstr));
+ dhcpmsg(LOG_WARNING, "Client: %1$s "
+ "MANUAL record %2$s is UNUSABLE. "
+ "No other IP address will be "
+ "allocated.\n", pcd->cidbuf,
+ cipstr);
+ goto leave_offer;
+ } else
+ break; /* success */
+ }
+
+ free_dnrec_list(dnlp);
+ dnlp = detach_dnrec_from_list(NULL, dncp, &dncp);
+ }
+
+ if (dnlp == NULL) {
+ /*
+ * select_offer() ONLY selects IP addresses owned
+ * by us. Only log a notice if we own any IP addresses
+ * at all. Otherwise, this is an informational server.
+ */
+ if (!select_offer(pnd, plp, pcd, &dnlp)) {
+ if (pnd->naddrs > 0) {
+ dhcpmsg(LOG_NOTICE,
+ "No more IP addresses on %1$s "
+ "network (%2$s)\n", pnd->network,
+ pcd->cidbuf);
+ }
+ goto leave_offer;
+ }
+ dnp = dnlp->dnl_rec;
+ } else
+ existing_allocation = B_TRUE;
+
+ ncip.s_addr = htonl(dnp->dn_cip.s_addr);
+ (void) inet_ntop(AF_INET, &ncip, cipstr, sizeof (cipstr));
+
+ /*
+ * ICMP echo validate the address.
+ */
+ if (!noping) {
+ /*
+ * If icmp echo validation fails, let the plp fall by
+ * the wayside.
+ */
+ if (icmp_echo_check(&ncip, &result) != 0) {
+ dhcpmsg(LOG_ERR, "ICMP ECHO check cannot be "
+ "registered for: %s, ignoring\n", cipstr);
+ unreserve = B_TRUE;
+ goto leave_offer;
+ }
+ if (result) {
+ dhcpmsg(LOG_WARNING,
+ "ICMP ECHO reply to OFFER candidate: "
+ "%s, disabling.\n", cipstr);
+
+ ndn = *dnp; /* struct copy */
+ ndn.dn_flags |= DN_FUNUSABLE;
+
+ if ((err = dhcp_modify_dd_entry(pnd->dh, dnp,
+ &ndn)) != DSVC_SUCCESS) {
+ dhcpmsg(LOG_ERR,
+ "ICMP ECHO reply to OFFER "
+ "candidate: %1$s. No "
+ "modifiable dhcp network "
+ "record. (%2$s)\n", cipstr,
+ dhcpsvc_errmsg(err));
+ } else {
+ /* Keep the cached entry current. */
+ *dnp = ndn; /* struct copy */
+ }
+
+ logtrans(P_DHCP, L_ICMP_ECHO, 0, ncip,
+ server_ip, plp);
+
+ unreserve = B_TRUE;
+
+ goto leave_offer;
+ }
+ }
+ }
+
+ /*
+ * At this point, we've ICMP validated (if requested) the IP
+ * address, and can go about producing an OFFER for the client.
+ */
+
+ ecp = vecp = NULL;
+ net_vecp = net_ecp = NULL;
+ macro_vecp = macro_ecp = NULL;
+ class_vecp = class_ecp = NULL;
+ cid_vecp = cid_ecp = NULL;
+ if (!no_dhcptab) {
+ open_macros();
+
+ /*
+ * Macros are evaluated this way: First apply parameters from
+ * a client class macro (if present), then apply those from the
+ * network macro (if present), then apply those from the
+ * dhcp network macro (if present), and finally apply those
+ * from a client id macro (if present).
+ */
+
+ /*
+ * First get a handle on network, dhcp network table macro,
+ * and client id macro values.
+ */
+ if ((net_mp = get_macro(pnd->network)) != NULL)
+ net_ecp = net_mp->head;
+ if ((pkt_mp = get_macro(dnp->dn_macro)) != NULL)
+ macro_ecp = pkt_mp->head;
+ if ((cid_mp = get_macro(pcd->cidbuf)) != NULL)
+ cid_ecp = cid_mp->head;
+
+ if (class_id != NULL) {
+ /* Get a handle on the class id macro (if it exists). */
+ if ((class_mp = get_macro(class_id)) != NULL) {
+ /*
+ * Locate the ENCODE list for encapsulated
+ * options associated with our class id within
+ * the class id macro.
+ */
+ class_vecp = vendor_encodes(class_mp, class_id);
+ class_ecp = class_mp->head;
+ }
+
+ /*
+ * Locate the ENCODE list for encapsulated options
+ * associated with our class id within the network,
+ * dhcp network, and client macros.
+ */
+ if (net_mp != NULL)
+ net_vecp = vendor_encodes(net_mp, class_id);
+ if (pkt_mp != NULL)
+ macro_vecp = vendor_encodes(pkt_mp, class_id);
+ if (cid_mp != NULL)
+ cid_vecp = vendor_encodes(cid_mp, class_id);
+
+ /*
+ * Combine the encapsulated option encode lists
+ * associated with our class id in the order defined
+ * above (class, net, dhcp network, client id)
+ */
+ vecp = combine_encodes(class_vecp, net_vecp, ENC_COPY);
+ vecp = combine_encodes(vecp, macro_vecp, ENC_DONT_COPY);
+ vecp = combine_encodes(vecp, cid_vecp, ENC_DONT_COPY);
+ }
+
+ /*
+ * Combine standard option encode lists in the order defined
+ * above (class, net, dhcp network, and client id).
+ */
+ if (class_ecp != NULL)
+ ecp = combine_encodes(class_ecp, net_ecp, ENC_COPY);
+ else
+ ecp = dup_encode_list(net_ecp);
+
+ ecp = combine_encodes(ecp, macro_ecp, ENC_DONT_COPY);
+ ecp = combine_encodes(ecp, cid_ecp, ENC_DONT_COPY);
+
+ /* If dhcptab configured to return hostname, do so. */
+ if (find_encode(ecp, DSYM_INTERNAL, CD_BOOL_HOSTNAME) != NULL) {
+ struct hostent h, *hp;
+ char hbuf[NSS_BUFLEN_HOSTS];
+ ENCODE *hecp;
+ hp = gethostbyaddr_r((char *)&ncip, sizeof (ncip),
+ AF_INET, &h, hbuf, sizeof (hbuf), &err);
+ if (hp != NULL) {
+ hecp = make_encode(DSYM_STANDARD,
+ CD_HOSTNAME, strlen(hp->h_name),
+ hp->h_name, ENC_COPY);
+ replace_encode(&ecp, hecp, ENC_DONT_COPY);
+ }
+ }
+
+ /* If dhcptab configured to echo client class, do so. */
+ if (plp->opts[CD_CLASS_ID] != NULL &&
+ find_encode(ecp, DSYM_INTERNAL, CD_BOOL_ECHO_VCLASS) !=
+ NULL) {
+ ENCODE *echo_ecp;
+ DHCP_OPT *op = plp->opts[CD_CLASS_ID];
+ echo_ecp = make_encode(DSYM_STANDARD, CD_CLASS_ID,
+ op->len, op->value, ENC_COPY);
+ replace_encode(&ecp, echo_ecp, ENC_DONT_COPY);
+ }
+ }
+
+ if ((ifp->flags & IFF_NOARP) == 0)
+ (void) set_arp(ifp, &ncip, NULL, 0, DHCP_ARP_DEL);
+
+ /*
+ * For OFFERs, we don't check the client's lease nor LeaseNeg,
+ * regardless of whether the client has an existing allocation
+ * or not. Lease expiration (w/o LeaseNeg) only occur during
+ * RENEW/REBIND or INIT-REBOOT client states, not SELECTing state.
+ */
+ if (existing_allocation) {
+ if (dnp->dn_lease == DHCP_PERM ||
+ (dnp->dn_flags & DN_FAUTOMATIC)) {
+ oldlease = DHCP_PERM;
+ } else {
+ if ((lease_t)dnp->dn_lease < (lease_t)now)
+ oldlease = (lease_t)0;
+ else {
+ oldlease = (lease_t)dnp->dn_lease -
+ (lease_t)now;
+ }
+ }
+ }
+
+ /* First get a generic reply packet. */
+ rep_pktp = gen_reply_pkt(pcd, plp, OFFER, &replen, &optp, &ifp->addr);
+
+ /* Set the client's IP address */
+ rep_pktp->yiaddr.s_addr = htonl(dnp->dn_cip.s_addr);
+
+ /* Calculate lease time. */
+ newlease = config_lease(plp, dnp, &ecp, oldlease, B_TRUE);
+
+ /*
+ * Client is requesting specific options. let's try and ensure it
+ * gets what it wants, if at all possible.
+ */
+ if (plp->opts[CD_REQUEST_LIST] != NULL)
+ add_request_list(ifp, plp, &ecp, &ncip);
+
+ /* Now load all the asked for / configured options */
+ used_pkt_len = load_options(DHCP_DHCP_CLNT | DHCP_SEND_LEASE, plp,
+ rep_pktp, replen, optp, ecp, vecp);
+
+ free_encode_list(ecp);
+ free_encode_list(vecp);
+ if (!no_dhcptab)
+ close_macros();
+
+ if (used_pkt_len < sizeof (PKT))
+ used_pkt_len = sizeof (PKT);
+
+ if (send_reply(ifp, rep_pktp, used_pkt_len, &ncip) == 0) {
+ if (newlease == DHCP_PERM)
+ newlease = htonl(newlease);
+ else
+ newlease = htonl(now + newlease);
+ (void) update_offer(pcd, dnlp, newlease, NULL, B_TRUE);
+ existing_offer = B_TRUE;
+ } else {
+ unreserve = B_TRUE;
+ }
+
+leave_offer:
+ if (unreserve)
+ purge_offer(pcd, B_FALSE, B_TRUE);
+ if (rep_pktp != NULL)
+ free(rep_pktp);
+ if (dncp != NULL)
+ dhcp_free_dd_list(pnd->dh, dncp);
+ if (dnlp != NULL && !existing_offer)
+ dhcp_free_dd_list(pnd->dh, dnlp);
+}
+
+/*
+ * Responding to REQUEST message.
+ *
+ * Very similar to dhcp_offer(), except that we need to be more
+ * discriminating.
+ *
+ * The ciaddr field is TRUSTED. A INIT-REBOOTing client will place its
+ * notion of its IP address in the requested IP address option. INIT
+ * clients will place the value in the OFFERs yiaddr in the requested
+ * IP address option. INIT-REBOOT packets are differentiated from INIT
+ * packets in that the server id option is missing. ciaddr will only
+ * appear from clients in the RENEW/REBIND states.
+ *
+ * Error messages may be generated. Database write failures are no longer
+ * fatal, since we'll only respond to the client if the write succeeds.
+ */
+static void
+dhcp_req_ack(dsvc_clnt_t *pcd, PKT_LIST *plp)
+{
+ dn_rec_t dn, ndn, *dnp;
+ struct in_addr serverid, ciaddr, claddr, nreqaddr, cipaddr, ncipaddr,
+ sipaddr;
+ struct in_addr dest_in;
+ dsvc_dnet_t *pnd = pcd->pnd;
+ uint_t replen;
+ int actual_len;
+ int pkt_type = ACK;
+ DHCP_MSG_CATEGORIES log;
+ PKT *rep_pktp = NULL;
+ uchar_t *optp;
+ ENCODE *ecp, *vecp,
+ *class_ecp, *class_vecp,
+ *net_ecp, *net_vecp,
+ *macro_ecp, *macro_vecp,
+ *cid_ecp, *cid_vecp;
+ MACRO *class_mp, *pkt_mp, *net_mp, *cid_mp;
+ char *class_id;
+ char nak_mesg[DHCP_SCRATCH];
+ time_t now;
+ lease_t newlease, oldlease;
+ boolean_t negot;
+ int err = 0;
+ int write_error = DSVC_SUCCESS, clnt_state;
+ ushort_t boot_secs;
+ char ntoaa[INET_ADDRSTRLEN], ntoab[INET_ADDRSTRLEN],
+ ntoac[INET_ADDRSTRLEN];
+ char class_idbuf[DSYM_CLASS_SIZE];
+ boolean_t hostname_update = B_FALSE;
+ dn_rec_list_t *nlp, *dncp = NULL, *dnlp = NULL;
+ uint32_t query;
+ IF *ifp = pcd->ifp;
+ boolean_t existing_offer = B_FALSE;
+
+ ciaddr.s_addr = plp->pkt->ciaddr.s_addr;
+ boot_secs = ntohs(plp->pkt->secs);
+ now = time(NULL);
+
+ class_id = get_class_id(plp, class_idbuf, sizeof (class_idbuf));
+
+ /* Determine type of REQUEST we've got. */
+ if (plp->opts[CD_SERVER_ID] != NULL) {
+ if (plp->opts[CD_SERVER_ID]->len != sizeof (struct in_addr)) {
+ dhcpmsg(LOG_ERR, "Garbled DHCP Server ID option from "
+ "client: '%1$s'. Len is %2$d, when it should be "
+ "%3$d \n", pcd->cidbuf,
+ plp->opts[CD_SERVER_ID]->len,
+ sizeof (struct in_addr));
+ goto leave_ack;
+ }
+
+ /*
+ * Request in response to an OFFER. ciaddr must not
+ * be set. Requested IP address option will hold address
+ * we offered the client.
+ */
+ clnt_state = INIT_STATE;
+ (void) memcpy((void *)&serverid,
+ plp->opts[CD_SERVER_ID]->value, sizeof (struct in_addr));
+
+ if (plp->opts[CD_REQUESTED_IP_ADDR] == NULL) {
+ if (verbose) {
+ dhcpmsg(LOG_NOTICE, "%1$s: REQUEST on %2$s is "
+ "missing requested IP option.\n", pcd->cidbuf,
+ pcd->pnd->network);
+ }
+ goto leave_ack;
+ }
+ if (plp->opts[CD_REQUESTED_IP_ADDR]->len !=
+ sizeof (struct in_addr)) {
+ dhcpmsg(LOG_ERR, "Garbled Requested IP option from "
+ "client: '%1$s'. Len is %2$d, when it should be "
+ "%3$d \n",
+ pcd->cidbuf, plp->opts[CD_REQUESTED_IP_ADDR]->len,
+ sizeof (struct in_addr));
+ goto leave_ack;
+ }
+ (void) memcpy((void *)&nreqaddr,
+ plp->opts[CD_REQUESTED_IP_ADDR]->value,
+ sizeof (struct in_addr));
+
+ if (serverid.s_addr != ifp->addr.s_addr) {
+ /*
+ * Someone else was selected. See if we made an
+ * offer, and clear it if we did. If offer expired
+ * before client responded, then no need to do
+ * anything.
+ */
+ purge_offer(pcd, B_FALSE, B_TRUE);
+ if (verbose) {
+ dhcpmsg(LOG_INFO,
+ "Client: %1$s chose %2$s from server: %3$s,"
+ " not %4$s\n", pcd->cidbuf,
+ inet_ntop(AF_INET, &nreqaddr, ntoaa,
+ sizeof (ntoaa)),
+ inet_ntop(AF_INET, &serverid, ntoab,
+ sizeof (ntoab)),
+ inet_ntop(AF_INET, &ifp->addr, ntoac,
+ sizeof (ntoac)));
+ }
+ goto leave_ack;
+ }
+
+ /*
+ * See comment at the top of the file for description of
+ * OFFER cache.
+ *
+ * If the offer expires before the client got around to
+ * requesting, and we can't confirm the address is still free,
+ * we'll silently ignore the client, until it drops back and
+ * tries to discover again. We will print a message in
+ * verbose mode however. If the Offer hasn't timed out, we
+ * bump it up again in case we have a bounce of queued up
+ * INIT requests to respond to.
+ */
+ if (pcd->off_ip.s_addr == htonl(INADDR_ANY) ||
+ PCD_OFFER_TIMEOUT(pcd, now)) {
+ /*
+ * Hopefully, the timeout value is fairly long to
+ * prevent this.
+ */
+ purge_offer(pcd, B_TRUE, B_TRUE);
+ if (verbose) {
+ dhcpmsg(LOG_INFO,
+ "Offer on %1$s expired for client: %2$s\n",
+ pcd->pnd->network, pcd->cidbuf);
+ }
+ goto leave_ack;
+ } else
+ (void) update_offer(pcd, NULL, 0, NULL, B_TRUE);
+
+ /*
+ * The client selected us. Create a ACK, and send
+ * it off to the client, commit to permanent
+ * storage the new binding.
+ */
+ existing_offer = B_TRUE;
+ dnlp = pcd->dnlp;
+ dnp = dnlp->dnl_rec;
+ ndn = *dnp; /* struct copy */
+ ndn.dn_lease = pcd->lease;
+ ncipaddr.s_addr = htonl(dnp->dn_cip.s_addr);
+
+ /*
+ * If client thinks we offered it a different address, then
+ * ignore it.
+ */
+ if (memcmp((char *)&ncipaddr,
+ plp->opts[CD_REQUESTED_IP_ADDR]->value,
+ sizeof (struct in_addr)) != 0) {
+ if (verbose) {
+ dhcpmsg(LOG_INFO, "Client %1$s believes "
+ "offered IP address %2$s is different than "
+ "what was offered.\n", pcd->cidbuf,
+ inet_ntop(AF_INET, &ncipaddr, ntoab,
+ sizeof (ntoab)));
+ }
+ goto leave_ack;
+ }
+
+ /*
+ * Clear out any temporary ARP table entry we may have
+ * created during the offer.
+ */
+ if ((ifp->flags & IFF_NOARP) == 0)
+ (void) set_arp(ifp, &ncipaddr, NULL, 0, DHCP_ARP_DEL);
+ } else {
+ /*
+ * Either a client in the INIT-REBOOT state, or one in
+ * either RENEW or REBIND states. The latter will have
+ * ciaddr set, whereas the former will place its concept
+ * of its IP address in the requested IP address option.
+ */
+ if (ciaddr.s_addr == htonl(INADDR_ANY)) {
+ clnt_state = INIT_REBOOT_STATE;
+ /*
+ * Client isn't sure of its IP address. It's
+ * attempting to verify its address, thus requested
+ * IP option better be present, and correct.
+ */
+ if (plp->opts[CD_REQUESTED_IP_ADDR] == NULL) {
+ dhcpmsg(LOG_ERR,
+ "Client: %s REQUEST is missing "
+ "requested IP option.\n", pcd->cidbuf);
+ goto leave_ack;
+ }
+ if (plp->opts[CD_REQUESTED_IP_ADDR]->len !=
+ sizeof (struct in_addr)) {
+ dhcpmsg(LOG_ERR, "Garbled Requested IP option "
+ "from client: '%1$s'. Len is %2$d, when it "
+ "should be %3$d \n", pcd->cidbuf,
+ plp->opts[CD_REQUESTED_IP_ADDR]->len,
+ sizeof (struct in_addr));
+ goto leave_ack;
+ }
+ (void) memcpy(&claddr,
+ plp->opts[CD_REQUESTED_IP_ADDR]->value,
+ sizeof (struct in_addr));
+
+ DSVC_QINIT(query);
+ DSVC_QEQ(query, DN_QCID);
+ (void) memcpy(dn.dn_cid, pcd->cid, pcd->cid_len);
+ dn.dn_cid_len = pcd->cid_len;
+
+ /* No bootp records, thank you. */
+ DSVC_QNEQ(query, DN_QFBOOTP_ONLY);
+ dn.dn_flags = DN_FBOOTP_ONLY;
+
+ } else {
+ clnt_state = RENEW_REBIND_STATE;
+ /*
+ * Client knows its IP address. It is trying to
+ * RENEW/REBIND (extend its lease). We trust ciaddr,
+ * and use it to locate the client's record. If we
+ * can't find the client's record, then we keep
+ * silent. If the client id of the record doesn't
+ * match this client, then the database is
+ * inconsistent, and we'll ignore it.
+ */
+ DSVC_QINIT(query);
+ DSVC_QEQ(query, DN_QCID|DN_QCIP);
+ (void) memcpy(dn.dn_cid, pcd->cid, pcd->cid_len);
+ dn.dn_cid_len = pcd->cid_len;
+ dn.dn_cip.s_addr = ntohl(ciaddr.s_addr);
+
+ /* No bootp records, thank you. */
+ DSVC_QNEQ(query, DN_QFBOOTP_ONLY);
+ dn.dn_flags = DN_FBOOTP_ONLY;
+
+ claddr.s_addr = ciaddr.s_addr;
+ }
+
+ dncp = NULL;
+ dnlp = dhcp_lookup_dd_classify(pcd->pnd, B_FALSE, query,
+ -1, &dn, (void **)&dncp, S_CID);
+
+ if (dnlp != NULL) {
+ dnp = dnlp->dnl_rec;
+ if (dnp->dn_flags & DN_FUNUSABLE)
+ goto leave_ack;
+
+ sipaddr.s_addr = htonl(dnp->dn_sip.s_addr);
+ cipaddr.s_addr = htonl(dnp->dn_cip.s_addr);
+
+ /*
+ * If this address is not owned by this server and
+ * the client is trying to verify the address, then
+ * ignore the client. If the client is simply trying
+ * to rebind, then don't respond until after
+ * renog_secs passes, to give the server that *OWNS*
+ * the address time to respond first.
+ */
+ if (match_ownerip(sipaddr.s_addr) == NULL) {
+ if (clnt_state == INIT_REBOOT_STATE) {
+ if (verbose) {
+ dhcpmsg(LOG_NOTICE, "Client: "
+ "%1$s is requesting "
+ "verification of %2$s "
+ "owned by %3$s\n",
+ pcd->cidbuf,
+ inet_ntop(AF_INET, &cipaddr,
+ ntoab, sizeof (ntoab)),
+ inet_ntop(AF_INET, &sipaddr,
+ ntoac, sizeof (ntoac)));
+ }
+ goto leave_ack;
+ } else {
+ /* RENEW/REBIND - wait for primary */
+ if (boot_secs < (ushort_t)renog_secs)
+ goto leave_ack;
+ }
+
+ }
+ if (claddr.s_addr != htonl(dnp->dn_cip.s_addr)) {
+ /*
+ * Client has the wrong IP address. Nak.
+ */
+ (void) snprintf(nak_mesg, sizeof (nak_mesg),
+ "Incorrect IP address.");
+ pkt_type = NAK;
+ } else {
+ if (!(dnp->dn_flags & DN_FAUTOMATIC) &&
+ (lease_t)dnp->dn_lease < (lease_t)now) {
+ (void) snprintf(nak_mesg,
+ sizeof (nak_mesg),
+ "Lease has expired.");
+ pkt_type = NAK;
+ }
+ }
+ } else {
+ if (clnt_state == RENEW_REBIND_STATE) {
+ dhcpmsg(LOG_ERR, "Client: %1$s is trying to "
+ "renew %2$s, an IP address it has not "
+ "leased.\n", pcd->cidbuf, inet_ntop(AF_INET,
+ &ciaddr, ntoab, sizeof (ntoab)));
+ goto leave_ack;
+ }
+ /*
+ * There is no such client registered for this
+ * address. Check if their address is on the correct
+ * net. If it is, then we'll assume that some other,
+ * non-database sharing DHCP server knows about this
+ * client. If the client is on the wrong net, NAK'em.
+ */
+ if ((claddr.s_addr & pnd->subnet.s_addr) ==
+ pnd->net.s_addr) {
+ /* Right net, but no record of client. */
+ if (verbose) {
+ dhcpmsg(LOG_INFO,
+ "Client: %1$s is trying to verify "
+ "unrecorded address: %2$s, "
+ "ignored.\n", pcd->cidbuf,
+ inet_ntop(AF_INET, &claddr,
+ ntoab, sizeof (ntoab)));
+ }
+ goto leave_ack;
+ } else {
+ if (ciaddr.s_addr == 0L) {
+ (void) snprintf(nak_mesg,
+ sizeof (nak_mesg),
+ "No valid configuration exists on "
+ "network: %s", pnd->network);
+ pkt_type = NAK;
+ } else {
+ if (verbose) {
+ dhcpmsg(LOG_INFO,
+ "Client: %1$s is not recorded as "
+ "having address: %2$s\n",
+ pcd->cidbuf, inet_ntop(AF_INET,
+ &ciaddr, ntoab, sizeof (ntoab)));
+ }
+ goto leave_ack;
+ }
+ }
+ }
+ }
+
+ /*
+ * Produce the appropriate response.
+ */
+ if (pkt_type == NAK) {
+ rep_pktp = gen_reply_pkt(pcd, plp, NAK, &replen, &optp,
+ &ifp->addr);
+ /*
+ * Setting yiaddr to the client's ciaddr abuses the
+ * semantics of yiaddr, So we set this to 0L.
+ *
+ * We twiddle the broadcast flag to force the
+ * server/relay agents to broadcast the NAK.
+ *
+ * Exception: If a client's lease has expired, and it
+ * is still trying to renegotiate its lease, AND ciaddr
+ * is set, AND ciaddr is on a "remote" net, unicast the
+ * NAK. Gross, huh? But SPA could make this happen with
+ * super short leases.
+ */
+ rep_pktp->yiaddr.s_addr = 0L;
+ if (ciaddr.s_addr != 0L &&
+ (ciaddr.s_addr & pnd->subnet.s_addr) != pnd->net.s_addr) {
+ dest_in.s_addr = ciaddr.s_addr;
+ } else {
+ rep_pktp->flags |= htons(BCAST_MASK);
+ dest_in.s_addr = INADDR_BROADCAST;
+ }
+
+ *optp++ = CD_MESSAGE;
+ *optp++ = (uchar_t)strlen(nak_mesg);
+ (void) memcpy(optp, nak_mesg, strlen(nak_mesg));
+ optp += strlen(nak_mesg);
+ *optp = CD_END;
+ actual_len = BASE_PKT_SIZE + (uint_t)(optp - rep_pktp->options);
+ if (actual_len < sizeof (PKT))
+ actual_len = sizeof (PKT);
+
+ (void) send_reply(ifp, rep_pktp, actual_len, &dest_in);
+
+ logtrans(P_DHCP, L_NAK, 0, dest_in, server_ip, plp);
+ } else {
+ rep_pktp = gen_reply_pkt(pcd, plp, ACK, &replen, &optp,
+ &ifp->addr);
+
+ /* Set the client's IP address */
+ rep_pktp->yiaddr.s_addr = htonl(dnp->dn_cip.s_addr);
+ dest_in.s_addr = htonl(dnp->dn_cip.s_addr);
+
+ /*
+ * Macros are evaluated this way: First apply parameters
+ * from a client class macro (if present), then apply
+ * those from the network macro (if present), then apply
+ * those from the server macro (if present), and finally
+ * apply those from a client id macro (if present).
+ */
+ ecp = vecp = NULL;
+ class_vecp = class_ecp = NULL;
+ net_vecp = net_ecp = NULL;
+ macro_vecp = macro_ecp = NULL;
+ cid_vecp = cid_ecp = NULL;
+
+ if (!no_dhcptab) {
+ open_macros();
+ if ((net_mp = get_macro(pnd->network)) != NULL)
+ net_ecp = net_mp->head;
+ if ((pkt_mp = get_macro(dnp->dn_macro)) != NULL)
+ macro_ecp = pkt_mp->head;
+ if ((cid_mp = get_macro(pcd->cidbuf)) != NULL)
+ cid_ecp = cid_mp->head;
+ if (class_id != NULL) {
+ if ((class_mp = get_macro(class_id)) != NULL) {
+ class_vecp = vendor_encodes(class_mp,
+ class_id);
+ class_ecp = class_mp->head;
+ }
+ if (net_mp != NULL) {
+ net_vecp = vendor_encodes(net_mp,
+ class_id);
+ }
+ if (pkt_mp != NULL)
+ macro_vecp = vendor_encodes(pkt_mp,
+ class_id);
+ if (cid_mp != NULL) {
+ cid_vecp = vendor_encodes(cid_mp,
+ class_id);
+ }
+ vecp = combine_encodes(class_vecp, net_vecp,
+ ENC_COPY);
+ vecp = combine_encodes(vecp, macro_vecp,
+ ENC_DONT_COPY);
+ vecp = combine_encodes(vecp, cid_vecp,
+ ENC_DONT_COPY);
+ }
+ if (class_ecp != NULL) {
+ ecp = combine_encodes(class_ecp, net_ecp,
+ ENC_COPY);
+ } else
+ ecp = dup_encode_list(net_ecp);
+
+ ecp = combine_encodes(ecp, macro_ecp, ENC_DONT_COPY);
+ ecp = combine_encodes(ecp, cid_ecp, ENC_DONT_COPY);
+
+ ncipaddr.s_addr = htonl(dnp->dn_cip.s_addr);
+ /*
+ * If the server is configured to do host name updates
+ * and the REQUEST packet contains a hostname request,
+ * see whether we can honor it.
+ *
+ * First, determine (via name_avail()) whether the host
+ * name is unassigned or belongs to an unleased IP
+ * address under our control. If not, we won't do a
+ * host name update on behalf of the client.
+ *
+ * Second, if we own the IP address and it is in the
+ * correct network table, see whether an update is
+ * necessary (or, in the lucky case, whether the name
+ * requested already belongs to that address), in which
+ * case we need do nothing more than return the option.
+ */
+ if ((nsutimeout_secs != DHCP_NO_NSU) &&
+ (plp->opts[CD_HOSTNAME] != NULL)) {
+ char hname[MAXHOSTNAMELEN + 1];
+ int hlen;
+ struct in_addr ia, *iap = &ia;
+
+ /* turn hostname option into a string */
+ hlen = MIN(plp->opts[CD_HOSTNAME]->len,
+ MAXHOSTNAMELEN);
+ (void) memcpy(hname,
+ plp->opts[CD_HOSTNAME]->value, hlen);
+ hname[hlen] = '\0';
+
+ nlp = NULL;
+ if (name_avail(hname, pcd, plp, &nlp, ecp,
+ &iap)) {
+ ENCODE *hecp;
+
+ /*
+ * If we pass this test, it means either
+ * no address is currently associated
+ * with the requested host name (iap is
+ * NULL) or the address doesn't match
+ * the one to be leased; in either case
+ * an update attempt is needed.
+ *
+ * Otherwise (in the else case), we need
+ * only send the response - the name and
+ * address already match.
+ */
+ if ((iap == NULL) || (iap->s_addr !=
+ dnp->dn_cip.s_addr)) {
+ if (do_nsupdate(dnp->dn_cip,
+ ecp, plp)) {
+ hecp = make_encode(
+ DSYM_STANDARD,
+ CD_HOSTNAME,
+ strlen(hname),
+ hname,
+ ENC_COPY);
+ replace_encode(&ecp,
+ hecp,
+ ENC_DONT_COPY);
+ hostname_update =
+ B_TRUE;
+ }
+ } else {
+ hecp = make_encode(
+ DSYM_STANDARD,
+ CD_HOSTNAME,
+ strlen(hname), hname,
+ ENC_COPY);
+ replace_encode(&ecp, hecp,
+ ENC_DONT_COPY);
+ hostname_update = B_TRUE;
+ }
+ if (nlp != NULL)
+ dhcp_free_dd_list(pnd->dh, nlp);
+ }
+ }
+
+ /*
+ * If dhcptab configured to return hostname, do so.
+ */
+ if ((hostname_update == B_FALSE) &&
+ (find_encode(ecp, DSYM_INTERNAL,
+ CD_BOOL_HOSTNAME) != NULL)) {
+ struct hostent h, *hp;
+ ENCODE *hecp;
+ char hbuf[NSS_BUFLEN_HOSTS];
+ hp = gethostbyaddr_r((char *)&ncipaddr,
+ sizeof (struct in_addr), AF_INET, &h, hbuf,
+ sizeof (hbuf), &err);
+ if (hp != NULL) {
+ hecp = make_encode(DSYM_STANDARD,
+ CD_HOSTNAME, strlen(hp->h_name),
+ hp->h_name, ENC_COPY);
+ replace_encode(&ecp, hecp,
+ ENC_DONT_COPY);
+ }
+ }
+
+ /*
+ * If dhcptab configured to echo client class, do so.
+ */
+ if (plp->opts[CD_CLASS_ID] != NULL &&
+ find_encode(ecp, DSYM_INTERNAL,
+ CD_BOOL_ECHO_VCLASS) != NULL) {
+ ENCODE *echo_ecp;
+ DHCP_OPT *op = plp->opts[CD_CLASS_ID];
+ echo_ecp = make_encode(DSYM_STANDARD,
+ CD_CLASS_ID, op->len, op->value,
+ ENC_COPY);
+ replace_encode(&ecp, echo_ecp, ENC_DONT_COPY);
+ }
+ }
+
+ if (dnp->dn_flags & DN_FAUTOMATIC || dnp->dn_lease == DHCP_PERM)
+ oldlease = DHCP_PERM;
+ else {
+ if (plp->opts[CD_SERVER_ID] != NULL) {
+ /*
+ * Offered absolute Lease time is cached
+ * in the lease field of the record. If
+ * that's expired, then they'll get the
+ * policy value again here. Must have been
+ * LONG time between DISC/REQ!
+ */
+ if ((lease_t)dnp->dn_lease < (lease_t)now)
+ oldlease = (lease_t)0;
+ else
+ oldlease = dnp->dn_lease - now;
+ } else
+ oldlease = dnp->dn_lease - now;
+ }
+
+ if (find_encode(ecp, DSYM_INTERNAL, CD_BOOL_LEASENEG) !=
+ NULL)
+ negot = B_TRUE;
+ else
+ negot = B_FALSE;
+
+ /*
+ * Modify changed fields in new database record.
+ */
+ ndn = *dnp; /* struct copy */
+ (void) memcpy(ndn.dn_cid, pcd->cid, pcd->cid_len);
+ ndn.dn_cid_len = pcd->cid_len;
+
+ /*
+ * This is a little longer than we offered (not taking into
+ * account the secs field), but since I trust the UNIX
+ * clock better than the PC's, it is a good idea to give
+ * the PC a little more time than it thinks, just due to
+ * clock slop on PC's.
+ */
+ newlease = config_lease(plp, &ndn, &ecp, oldlease, negot);
+
+ if (newlease != DHCP_PERM)
+ ndn.dn_lease = now + newlease;
+ else
+ ndn.dn_lease = DHCP_PERM;
+
+
+ /*
+ * It is critical to write the database record if the
+ * client is in the INIT state, so we don't reply to the
+ * client if this fails. However, if the client is simply
+ * trying to verify its address or extend its lease, then
+ * we'll reply regardless of the status of the write,
+ * although we'll return the old lease time.
+ *
+ * If the client is in the INIT_REBOOT state, and the
+ * lease time hasn't changed, we don't bother with the
+ * write, since nothing has changed.
+ */
+ if (clnt_state == INIT_STATE || oldlease != newlease) {
+
+ write_error = dhcp_modify_dd_entry(pnd->dh, dnp, &ndn);
+
+ /* Keep state of the cached entry current. */
+ if (write_error == DSVC_SUCCESS) {
+ *dnp = ndn; /* struct copy */
+ }
+ } else {
+ if (verbose) {
+ dhcpmsg(LOG_INFO,
+ "Database write unnecessary for DHCP client: "
+ "%1$s, %2$s\n", pcd->cidbuf,
+ inet_ntop(AF_INET, &ncipaddr,
+ ntoab, sizeof (ntoab)));
+ }
+ }
+ if (write_error == DSVC_SUCCESS ||
+ clnt_state == INIT_REBOOT_STATE) {
+
+ if (write_error != DSVC_SUCCESS)
+ set_lease_option(&ecp, oldlease);
+ else {
+ /* Note that the conversation has completed. */
+ pcd->state = ACK;
+ }
+
+ if (plp->opts[CD_REQUEST_LIST])
+ add_request_list(ifp, plp, &ecp, &ncipaddr);
+
+ /* Now load all the asked for / configured options */
+ actual_len = load_options(DHCP_DHCP_CLNT |
+ DHCP_SEND_LEASE, plp, rep_pktp, replen, optp, ecp,
+ vecp);
+
+ if (actual_len < sizeof (PKT))
+ actual_len = sizeof (PKT);
+ if (verbose) {
+ dhcpmsg(LOG_INFO,
+ "Client: %1$s maps to IP: %2$s\n",
+ pcd->cidbuf,
+ inet_ntop(AF_INET, &ncipaddr,
+ ntoab, sizeof (ntoab)));
+ }
+ (void) send_reply(ifp, rep_pktp, actual_len, &dest_in);
+
+ if (clnt_state == INIT_STATE)
+ log = L_ASSIGN;
+ else
+ log = L_REPLY;
+
+ logtrans(P_DHCP, log, ndn.dn_lease, ncipaddr,
+ server_ip, plp);
+ }
+
+ free_encode_list(ecp);
+ free_encode_list(vecp);
+ if (!no_dhcptab)
+ close_macros();
+ }
+
+leave_ack:
+ if (rep_pktp != NULL)
+ free(rep_pktp);
+ if (dncp != NULL)
+ dhcp_free_dd_list(pnd->dh, dncp);
+ if (dnlp != NULL && !existing_offer)
+ dhcp_free_dd_list(pnd->dh, dnlp);
+}
+
+/* Reacting to a client's DECLINE or RELEASE. */
+static void
+dhcp_dec_rel(dsvc_clnt_t *pcd, PKT_LIST *plp, int type)
+{
+ char *fmtp;
+ dn_rec_t *dnp, dn, ndn;
+ dsvc_dnet_t *pnd;
+ struct in_addr ip;
+ int err = 0;
+ DHCP_MSG_CATEGORIES log;
+ dn_rec_list_t *dncp, *dnlp = NULL;
+ uint32_t query;
+ char ipb[INET_ADDRSTRLEN];
+ char clnt_msg[DHCP_MAX_OPT_SIZE];
+
+ pnd = pcd->pnd;
+
+ if (type == DECLINE) {
+ if (plp->opts[CD_REQUESTED_IP_ADDR] &&
+ plp->opts[CD_REQUESTED_IP_ADDR]->len ==
+ sizeof (struct in_addr)) {
+ (void) memcpy((char *)&ip,
+ plp->opts[CD_REQUESTED_IP_ADDR]->value,
+ sizeof (struct in_addr));
+ }
+ } else
+ ip.s_addr = plp->pkt->ciaddr.s_addr;
+
+ (void) inet_ntop(AF_INET, &ip, ipb, sizeof (ipb));
+
+ /* Look for a matching IP address and Client ID */
+
+ DSVC_QINIT(query);
+ DSVC_QEQ(query, DN_QCID|DN_QCIP);
+ (void) memcpy(dn.dn_cid, pcd->cid, pcd->cid_len);
+ dn.dn_cid_len = pcd->cid_len;
+ dn.dn_cip.s_addr = ntohl(ip.s_addr);
+
+ dncp = NULL;
+ dnlp = dhcp_lookup_dd_classify(pcd->pnd, B_FALSE, query, -1,
+ &dn, (void **)&dncp, S_CID);
+ assert(dncp == NULL);
+
+ if (dnlp == NULL) {
+ if (verbose) {
+ if (type == DECLINE) {
+ fmtp = "Unregistered client: %1$s is "
+ "DECLINEing address: %2$s.\n";
+ } else {
+ fmtp = "Unregistered client: %1$s is "
+ "RELEASEing address: %2$s.\n";
+ }
+ dhcpmsg(LOG_INFO, fmtp, pcd->cidbuf, ipb);
+ }
+ return;
+ }
+
+ dnp = dnlp->dnl_rec;
+ ndn = *dnp; /* struct copy */
+
+ /* If the entry is not one of ours, then give up. */
+ if (match_ownerip(htonl(ndn.dn_sip.s_addr)) == NULL) {
+ if (verbose) {
+ if (type == DECLINE) {
+ fmtp = "Client: %1$s is DECLINEing: "
+ "%2$s not owned by this server.\n";
+ } else {
+ fmtp = "Client: %1$s is RELEASEing: "
+ "%2$s not owned by this server.\n";
+ }
+ dhcpmsg(LOG_INFO, fmtp, pcd->cidbuf, ipb);
+ }
+ goto leave_dec_rel;
+ }
+
+ if (type == DECLINE) {
+ log = L_DECLINE;
+ dhcpmsg(LOG_ERR, "Client: %1$s DECLINED address: %2$s.\n",
+ pcd->cidbuf, ipb);
+ if (plp->opts[CD_MESSAGE]) {
+ dhcpmsg(LOG_ERR, "DECLINE: client message: %s\n",
+ disp_clnt_msg(plp, clnt_msg, sizeof (clnt_msg)));
+ }
+ ndn.dn_flags |= DN_FUNUSABLE;
+ } else {
+ log = L_RELEASE;
+ if (ndn.dn_flags & DN_FMANUAL) {
+ dhcpmsg(LOG_ERR,
+ "Client: %1$s is trying to RELEASE manual "
+ "address: %2$s\n", pcd->cidbuf, ipb);
+ goto leave_dec_rel;
+ }
+ if (verbose) {
+ dhcpmsg(LOG_INFO,
+ "Client: %1$s RELEASED address: %2$s\n",
+ pcd->cidbuf, ipb);
+ if (plp->opts[CD_MESSAGE]) {
+ dhcpmsg(LOG_INFO,
+ "RELEASE: client message: %s\n",
+ disp_clnt_msg(plp, clnt_msg,
+ sizeof (clnt_msg)));
+ }
+ }
+ }
+
+ /* Clear out the cid and lease fields */
+ if (!(ndn.dn_flags & DN_FMANUAL)) {
+ ndn.dn_cid[0] = '\0';
+ ndn.dn_cid_len = 1;
+ ndn.dn_lease = (lease_t)0;
+ }
+
+ /* Ignore write errors. */
+ err = dhcp_modify_dd_entry(pnd->dh, dnp, &ndn);
+ if (err != DSVC_SUCCESS) {
+ dhcpmsg(LOG_NOTICE,
+ "%1$s: ERROR modifying database: %2$s for client %3$s\n",
+ log == L_RELEASE ? "RELEASE" : "DECLINE",
+ dhcpsvc_errmsg(err), ipb);
+ } else {
+ if (type == RELEASE) {
+ /*
+ * performance: save select_offer() lots of work by
+ * caching this perfectly good ip address in freerec.
+ */
+ *(dnlp->dnl_rec) = ndn; /* struct copy */
+ add_dnet_cache(pnd, dnlp);
+ dnlp = NULL;
+ }
+ }
+
+ logtrans(P_DHCP, log, ndn.dn_lease, ip, server_ip, plp);
+
+leave_dec_rel:
+
+ if (dnlp != NULL)
+ dhcp_free_dd_list(pnd->dh, dnlp);
+}
+
+/*
+ * Responding to an INFORM message.
+ *
+ * INFORM messages are received from clients that already have their network
+ * parameters (such as IP address and subnet mask), but wish to receive
+ * other configuration parameters. The server will not check for an existing
+ * lease as clients may have obtained their network parameters by some
+ * means other than DHCP. Similarly, the DHCPACK generated in response to
+ * the INFORM message will not include lease time information. All other
+ * configuration parameters are returned.
+ */
+static void
+dhcp_inform(dsvc_clnt_t *pcd, PKT_LIST *plp)
+{
+ uint_t replen;
+ int used_pkt_len;
+ PKT *rep_pktp = NULL;
+ uchar_t *optp;
+ ENCODE *ecp, *vecp, *class_ecp, *class_vecp,
+ *cid_ecp, *cid_vecp, *net_ecp, *net_vecp;
+ MACRO *net_mp, *class_mp, *cid_mp;
+ dsvc_dnet_t *pnd;
+ char *class_id;
+ char class_idbuf[DSYM_CLASS_SIZE];
+ IF *ifp = pcd->ifp;
+
+ pnd = pcd->pnd;
+ class_id = get_class_id(plp, class_idbuf, sizeof (class_idbuf));
+
+ /*
+ * Macros are evaluated this way: First apply parameters from
+ * a client class macro (if present), then apply those from the
+ * network macro (if present), and finally apply those from a
+ * client id macro (if present).
+ */
+ ecp = vecp = NULL;
+ net_vecp = net_ecp = NULL;
+ class_vecp = class_ecp = NULL;
+ cid_vecp = cid_ecp = NULL;
+
+ if (!no_dhcptab) {
+ open_macros();
+ if ((net_mp = get_macro(pnd->network)) != NULL)
+ net_ecp = net_mp->head;
+ if ((cid_mp = get_macro(pcd->cidbuf)) != NULL)
+ cid_ecp = cid_mp->head;
+ if (class_id != NULL) {
+ if ((class_mp = get_macro(class_id)) != NULL) {
+ class_vecp = vendor_encodes(class_mp,
+ class_id);
+ class_ecp = class_mp->head;
+ }
+ if (net_mp != NULL)
+ net_vecp = vendor_encodes(net_mp, class_id);
+ if (cid_mp != NULL)
+ cid_vecp = vendor_encodes(cid_mp, class_id);
+ vecp = combine_encodes(class_vecp, net_vecp,
+ ENC_COPY);
+ vecp = combine_encodes(vecp, cid_vecp, ENC_DONT_COPY);
+ }
+
+ ecp = combine_encodes(class_ecp, net_ecp, ENC_COPY);
+ ecp = combine_encodes(ecp, cid_ecp, ENC_DONT_COPY);
+ }
+
+ /* First get a generic reply packet. */
+ rep_pktp = gen_reply_pkt(pcd, plp, ACK, &replen, &optp, &ifp->addr);
+
+ /*
+ * Client is requesting specific options. let's try and ensure it
+ * gets what it wants, if at all possible.
+ */
+ if (plp->opts[CD_REQUEST_LIST] != NULL)
+ add_request_list(ifp, plp, &ecp, &plp->pkt->ciaddr);
+
+ /*
+ * Explicitly set the ciaddr to be that which the client gave
+ * us.
+ */
+ rep_pktp->ciaddr.s_addr = plp->pkt->ciaddr.s_addr;
+
+ /*
+ * Now load all the asked for / configured options. DON'T send
+ * any lease time info!
+ */
+ used_pkt_len = load_options(DHCP_DHCP_CLNT, plp, rep_pktp, replen, optp,
+ ecp, vecp);
+
+ free_encode_list(ecp);
+ free_encode_list(vecp);
+ if (!no_dhcptab)
+ close_macros();
+
+ if (used_pkt_len < sizeof (PKT))
+ used_pkt_len = sizeof (PKT);
+
+ (void) send_reply(ifp, rep_pktp, used_pkt_len, &plp->pkt->ciaddr);
+
+ logtrans(P_DHCP, L_INFORM, 0, plp->pkt->ciaddr, server_ip, plp);
+
+leave_inform:
+ if (rep_pktp != NULL)
+ free(rep_pktp);
+}
+
+static char *
+disp_clnt_msg(PKT_LIST *plp, char *bufp, int len)
+{
+ uchar_t tlen;
+
+ bufp[0] = '\0'; /* null string */
+
+ if (plp && plp->opts[CD_MESSAGE]) {
+ tlen = ((uchar_t)len < plp->opts[CD_MESSAGE]->len) ?
+ (len - 1) : plp->opts[CD_MESSAGE]->len;
+ (void) memcpy(bufp, plp->opts[CD_MESSAGE]->value, tlen);
+ bufp[tlen] = '\0';
+ }
+ return (bufp);
+}
+
+/*
+ * serverip expected in host order
+ */
+static PKT *
+gen_reply_pkt(dsvc_clnt_t *pcd, PKT_LIST *plp, int type, uint_t *len,
+ uchar_t **optpp, struct in_addr *serverip)
+{
+ PKT *reply_pktp;
+ uint16_t plen;
+
+ /*
+ * We need to determine the packet size. Perhaps the client has told
+ * us?
+ */
+ if (plp->opts[CD_MAX_DHCP_SIZE]) {
+ if (plp->opts[CD_MAX_DHCP_SIZE]->len != sizeof (uint16_t)) {
+ dhcpmsg(LOG_ERR, "Garbled MAX DHCP message size option "
+ "from\nclient: '%1$s'. Len is %2$d, when it should "
+ "be %3$d. Defaulting to %4$d.\n",
+ pcd->cidbuf,
+ plp->opts[CD_MAX_DHCP_SIZE]->len,
+ sizeof (uint16_t), DHCP_DEF_MAX_SIZE);
+ plen = DHCP_DEF_MAX_SIZE;
+ } else {
+ (void) memcpy(&plen, plp->opts[CD_MAX_DHCP_SIZE]->value,
+ sizeof (uint16_t));
+ plen = ntohs(plen);
+ }
+ } else {
+ /*
+ * Define size to be a fixed length. Too hard to add up all
+ * possible class id, macro, and hostname/lease time options
+ * without doing just about as much work as constructing the
+ * whole reply packet.
+ */
+ plen = DHCP_MAX_REPLY_SIZE;
+ }
+
+ /* Generate a generically initialized BOOTP packet */
+ reply_pktp = gen_bootp_pkt(plen, plp->pkt);
+
+ reply_pktp->op = BOOTREPLY;
+ *optpp = reply_pktp->options;
+
+ /*
+ * Set pkt type.
+ */
+ *(*optpp)++ = (uchar_t)CD_DHCP_TYPE;
+ *(*optpp)++ = (uchar_t)1;
+ *(*optpp)++ = (uchar_t)type;
+
+ /*
+ * All reply packets have server id set.
+ */
+ *(*optpp)++ = (uchar_t)CD_SERVER_ID;
+ *(*optpp)++ = (uchar_t)4;
+#if defined(_LITTLE_ENDIAN)
+ *(*optpp)++ = (uchar_t)(serverip->s_addr & 0xff);
+ *(*optpp)++ = (uchar_t)((serverip->s_addr >> 8) & 0xff);
+ *(*optpp)++ = (uchar_t)((serverip->s_addr >> 16) & 0xff);
+ *(*optpp)++ = (uchar_t)((serverip->s_addr >> 24) & 0xff);
+#else
+ *(*optpp)++ = (uchar_t)((serverip->s_addr >> 24) & 0xff);
+ *(*optpp)++ = (uchar_t)((serverip->s_addr >> 16) & 0xff);
+ *(*optpp)++ = (uchar_t)((serverip->s_addr >> 8) & 0xff);
+ *(*optpp)++ = (uchar_t)(serverip->s_addr & 0xff);
+#endif /* _LITTLE_ENDIAN */
+
+ *len = plen;
+ return (reply_pktp);
+}
+
+/*
+ * If the client requests it, and either it isn't currently configured
+ * or hasn't already been added, provide the option now. Will also work
+ * for NULL ENCODE lists, but initializing them to point to the requested
+ * options.
+ *
+ * If nsswitch contains host name services which hang, big problems occur
+ * with dhcp server, since the main thread hangs waiting for that name
+ * service's timeout.
+ *
+ * NOTE: this function should be called only after all other parameter
+ * merges have taken place (combine_encode).
+ */
+static void
+add_request_list(IF *ifp, PKT_LIST *plp, ENCODE **ecp, struct in_addr *ip)
+{
+ ENCODE *ep, *ifecp, *end_ecp = NULL;
+ struct hostent h, *hp;
+ char hbuf[NSS_BUFLEN_HOSTS];
+ int herrno;
+
+ /* Find the end. */
+ if (*ecp) {
+ for (ep = *ecp; ep->next; ep = ep->next)
+ /* null */;
+ end_ecp = ep;
+ }
+
+ /* HOSTNAME */
+ if (is_option_requested(plp, CD_HOSTNAME) &&
+ (find_encode(*ecp, DSYM_STANDARD, CD_HOSTNAME) == NULL) &&
+ (find_encode(*ecp, DSYM_INTERNAL, CD_BOOL_HOSTNAME) == NULL)) {
+ hp = gethostbyaddr_r((char *)ip, sizeof (struct in_addr),
+ AF_INET, &h, hbuf, sizeof (hbuf), &herrno);
+ if (hp != NULL) {
+ if (end_ecp) {
+ end_ecp->next = make_encode(DSYM_STANDARD,
+ CD_HOSTNAME, strlen(hp->h_name),
+ hp->h_name, ENC_COPY);
+ end_ecp = end_ecp->next;
+ } else {
+ end_ecp = make_encode(DSYM_STANDARD,
+ CD_HOSTNAME, strlen(hp->h_name),
+ hp->h_name, ENC_COPY);
+ }
+ }
+ }
+
+ /*
+ * all bets off for the following if thru a relay agent.
+ */
+ if (plp->pkt->giaddr.s_addr != 0L)
+ return;
+
+ /* SUBNET MASK */
+ if (is_option_requested(plp, CD_SUBNETMASK) && find_encode(*ecp,
+ DSYM_STANDARD, CD_SUBNETMASK) == NULL) {
+ ifecp = find_encode(ifp->ecp, DSYM_STANDARD, CD_SUBNETMASK);
+ if (end_ecp) {
+ end_ecp->next = dup_encode(ifecp);
+ end_ecp = end_ecp->next;
+ } else
+ end_ecp = dup_encode(ifecp);
+ }
+
+ /* BROADCAST ADDRESS */
+ if (is_option_requested(plp, CD_BROADCASTADDR) && find_encode(*ecp,
+ DSYM_STANDARD, CD_BROADCASTADDR) == NULL) {
+ ifecp = find_encode(ifp->ecp, DSYM_STANDARD,
+ CD_BROADCASTADDR);
+ if (end_ecp) {
+ end_ecp->next = dup_encode(ifecp);
+ end_ecp = end_ecp->next;
+ } else
+ end_ecp = dup_encode(ifecp);
+ }
+
+ /* IP MTU */
+ if (is_option_requested(plp, CD_MTU) && find_encode(*ecp,
+ DSYM_STANDARD, CD_MTU) == NULL) {
+ ifecp = find_encode(ifp->ecp, DSYM_STANDARD, CD_MTU);
+ if (end_ecp) {
+ end_ecp->next = dup_encode(ifecp);
+ end_ecp = end_ecp->next;
+ } else
+ end_ecp = dup_encode(ifecp);
+ }
+
+ if (*ecp == NULL)
+ *ecp = end_ecp;
+}
+
+/*
+ * Is a specific option requested? Returns True if so, False otherwise.
+ */
+static int
+is_option_requested(PKT_LIST *plp, ushort_t code)
+{
+ uchar_t c, *tp;
+ DHCP_OPT *cp = plp->opts[CD_REQUEST_LIST];
+
+ for (c = 0, tp = (uchar_t *)cp->value; c < cp->len; c++, tp++) {
+ if (*tp == (uchar_t)code)
+ return (B_TRUE);
+ }
+ return (B_FALSE);
+}
+
+/*
+ * Locates lease option, if possible, otherwise allocates an encode and
+ * appends it to the end. Changes current lease setting.
+ *
+ * TODO: ugh. We don't address the case where the Lease time changes, but
+ * T1 and T2 don't. We don't want T1 or T2 to be greater than the lease
+ * time! Perhaps T1 and T2 should be a percentage of lease time... Later..
+ */
+static void
+set_lease_option(ENCODE **ecpp, lease_t lease)
+{
+ ENCODE *ep, *prev_ep, *lease_ep;
+
+ lease = htonl(lease);
+
+ if (ecpp != NULL && (lease_ep = find_encode(*ecpp, DSYM_STANDARD,
+ CD_LEASE_TIME)) != NULL && lease_ep->len == sizeof (lease_t)) {
+ (void) memcpy(lease_ep->data, (void *)&lease, sizeof (lease_t));
+ } else {
+ if (*ecpp != NULL) {
+ for (prev_ep = ep = *ecpp; ep != NULL; ep = ep->next)
+ prev_ep = ep;
+ prev_ep->next = make_encode(DSYM_STANDARD,
+ CD_LEASE_TIME, sizeof (lease_t), &lease, ENC_COPY);
+ } else {
+ *ecpp = make_encode(DSYM_STANDARD, CD_LEASE_TIME,
+ sizeof (lease_t), &lease, ENC_COPY);
+ (*ecpp)->next = NULL;
+ }
+ }
+}
+/*
+ * Sets appropriate option in passed ENCODE list for lease. Returns
+ * calculated relative lease time.
+ */
+static int
+config_lease(PKT_LIST *plp, dn_rec_t *dnp, ENCODE **ecpp, lease_t oldlease,
+ boolean_t negot)
+{
+ lease_t newlease, rel_current;
+ ENCODE *lease_ecp;
+
+ if (ecpp != NULL && (lease_ecp = find_encode(*ecpp, DSYM_STANDARD,
+ CD_LEASE_TIME)) != NULL && lease_ecp->len == sizeof (lease_t)) {
+ (void) memcpy((void *)&rel_current, lease_ecp->data,
+ sizeof (lease_t));
+ rel_current = htonl(rel_current);
+ } else
+ rel_current = (lease_t)DEFAULT_LEASE;
+
+ if (dnp->dn_flags & DN_FAUTOMATIC || !negot) {
+ if (dnp->dn_flags & DN_FAUTOMATIC)
+ newlease = ntohl(DHCP_PERM);
+ else {
+ /* sorry! */
+ if (oldlease)
+ newlease = oldlease;
+ else
+ newlease = rel_current;
+ }
+ } else {
+ /*
+ * lease is not automatic and is negotiable!
+ * If the dhcp-network lease is bigger than the current
+ * policy value, then let the client benefit from this
+ * situation.
+ */
+ if (oldlease > rel_current)
+ rel_current = oldlease;
+
+ if (plp->opts[CD_LEASE_TIME] &&
+ plp->opts[CD_LEASE_TIME]->len == sizeof (lease_t)) {
+ /*
+ * Client is requesting a lease renegotiation.
+ */
+ (void) memcpy((void *)&newlease,
+ plp->opts[CD_LEASE_TIME]->value, sizeof (lease_t));
+
+ newlease = ntohl(newlease);
+
+ /*
+ * Note that this comparison handles permanent
+ * leases as well. Limit lease to configured value.
+ */
+ if (newlease > rel_current)
+ newlease = rel_current;
+ } else
+ newlease = rel_current;
+ }
+
+ set_lease_option(ecpp, newlease);
+
+ return (newlease);
+}
+
+/*
+ * If a packet has the classid set, return the value, else return null.
+ */
+char *
+get_class_id(PKT_LIST *plp, char *bufp, int len)
+{
+ uchar_t *ucp, ulen;
+ char *retp;
+
+ if (plp->opts[CD_CLASS_ID]) {
+ /*
+ * If the class id is set, see if there is a macro by this
+ * name. If so, then "OR" the ENCODE settings of the class
+ * macro with the packet macro. Settings in the packet macro
+ * OVERRIDE settings in the class macro.
+ */
+ ucp = plp->opts[CD_CLASS_ID]->value;
+ ulen = plp->opts[CD_CLASS_ID]->len;
+ if (len < ulen)
+ ulen = len;
+ (void) memcpy(bufp, ucp, ulen);
+ bufp[ulen] = '\0';
+
+ retp = bufp;
+ } else
+ retp = NULL;
+
+ return (retp);
+}
+
+/*
+ * Checks whether an offer ip address in the per net inet address
+ * cache.
+ *
+ * pnd - per net structure
+ * reservep - address to check, in network order.
+ */
+static boolean_t
+check_offer(dsvc_dnet_t *pnd, struct in_addr *reservep)
+{
+ dsvc_clnt_t tpcd;
+
+ tpcd.off_ip.s_addr = reservep->s_addr;
+
+ return (hash_Lookup(pnd->itable, reservep, sizeof (struct in_addr),
+ clnt_netcmp, &tpcd, B_FALSE) == NULL ? B_TRUE : B_FALSE);
+}
+
+/*
+ * Adds or updates an offer to the per client data structure. The client
+ * struct is hashed by clientid into the per net ctable hash table, and
+ * by offer address in the itable hash table, which is used to reserve the
+ * ip address. Lease time is expected to be set by caller.
+ * Will update existing OFFER if already provided.
+ *
+ * pcd - per client data struct.
+ * dnlp - pointer to current container entry. Performance: caching reduces
+ * datastore activity, structure copying.
+ * nlease - new lease time.
+ * reservep - new offer address (expected in network order).
+ * purge_cache - Multithreading: avoid redundant cache purging in
+ * select_offer().
+ */
+boolean_t
+update_offer(dsvc_clnt_t *pcd, dn_rec_list_t *dnlp, lease_t nlease,
+ struct in_addr *reservep, boolean_t purge_cache)
+{
+ char ntoab[INET_ADDRSTRLEN];
+ boolean_t insert = B_TRUE;
+ boolean_t update = B_FALSE;
+ boolean_t offer = B_FALSE;
+ dsvc_dnet_t *pnd = pcd->pnd;
+ IF *ifp = pcd->ifp;
+ dn_rec_t *dnp = NULL;
+ struct in_addr off_ip;
+
+ /* Save the original datastore record. */
+ if (dnlp != NULL) {
+ if (pcd->dnlp != NULL && pcd->dnlp != dnlp)
+ dhcp_free_dd_list(pnd->dh, pcd->dnlp);
+ pcd->dnlp = dnlp;
+ }
+ if (pcd->dnlp != NULL)
+ dnp = pcd->dnlp->dnl_rec;
+
+ /* Determine the offer address. */
+ if (reservep == NULL && dnp != NULL)
+ off_ip.s_addr = htonl(dnp->dn_cip.s_addr);
+ else if (reservep != NULL)
+ off_ip.s_addr = reservep->s_addr;
+ else {
+ dhcpmsg(LOG_DEBUG,
+ "Neither offer IP nor IP to reserve present\n");
+ assert(B_FALSE);
+ return (B_FALSE);
+ }
+
+ /* If updating, release the old offer address. */
+ if (pcd->off_ip.s_addr == htonl(INADDR_ANY)) {
+ offer = B_TRUE;
+ } else {
+ update = B_TRUE;
+ if (pcd->off_ip.s_addr != off_ip.s_addr) {
+ purge_offer(pcd, B_FALSE, purge_cache);
+ offer = B_TRUE;
+ } else
+ insert = B_FALSE;
+ }
+
+ if (nlease != 0)
+ pcd->lease = nlease;
+
+ /* Prepare to insert pcd into the offer hash table. */
+ pcd->mtime = reinit_time;
+
+ pcd->off_ip.s_addr = off_ip.s_addr;
+
+ assert(pcd->off_ip.s_addr != htonl(INADDR_ANY));
+
+ if (insert) {
+ if ((pcd->ihand = hash_Insert(pnd->itable, &pcd->off_ip,
+ sizeof (struct in_addr), clnt_netcmp, pcd, pcd)) == NULL) {
+ if (reservep == NULL) {
+ dhcpmsg(LOG_WARNING, "Duplicate offer of %1$s "
+ "to client: %2$s\n",
+ inet_ntop(AF_INET, &pcd->off_ip, ntoab,
+ sizeof (ntoab)), pcd->cidbuf);
+ }
+ pcd->off_ip.s_addr = htonl(INADDR_ANY);
+ dhcp_free_dd_list(pnd->dh, pcd->dnlp);
+ pcd->dnlp = NULL;
+ return (B_FALSE);
+ }
+ } else
+ hash_Dtime(pcd->ihand, time(NULL) + off_secs);
+
+ if (offer) {
+ (void) mutex_lock(&ifp->ifp_mtx);
+ ifp->offers++;
+ (void) mutex_unlock(&ifp->ifp_mtx);
+ }
+
+ if (debug) {
+ if (reservep != NULL) {
+ dhcpmsg(LOG_INFO, "Reserved offer: %s\n",
+ inet_ntop(AF_INET, &pcd->off_ip,
+ ntoab, sizeof (ntoab)));
+ } else if (update) {
+ dhcpmsg(LOG_INFO, "Updated offer: %s\n",
+ inet_ntop(AF_INET, &pcd->off_ip,
+ ntoab, sizeof (ntoab)));
+ } else {
+ dhcpmsg(LOG_INFO, "Added offer: %s\n",
+ inet_ntop(AF_INET, &pcd->off_ip,
+ ntoab, sizeof (ntoab)));
+ }
+ }
+ return (B_TRUE);
+}
+
+/*
+ * Deletes an offer.
+ *
+ * pcd - per client struct
+ * expired - has offer expired, or been purged
+ * purge_cache - Multi-threading: avoid redundant cache purging in
+ * select_offer().
+ */
+void
+purge_offer(dsvc_clnt_t *pcd, boolean_t expired, boolean_t purge_cache)
+{
+ char ntoab[INET_ADDRSTRLEN];
+ dsvc_dnet_t *pnd = pcd->pnd;
+ IF *ifp = pcd->ifp;
+
+ if (pcd->off_ip.s_addr != htonl(INADDR_ANY)) {
+ if (debug) {
+ if (expired == B_TRUE)
+ dhcpmsg(LOG_INFO, "Freeing offer: %s\n",
+ inet_ntop(AF_INET, &pcd->off_ip,
+ ntoab, sizeof (ntoab)));
+ else
+ dhcpmsg(LOG_INFO, "Purging offer: %s\n",
+ inet_ntop(AF_INET, &pcd->off_ip,
+ ntoab, sizeof (ntoab)));
+ }
+
+ /*
+ * The offer cache ensures that recently granted offer
+ * addresses won't attempt to be reused from the dnet
+ * caches. When purging one of these offers, be sure to
+ * remove the associated record from the dnet cache,
+ * to avoid collisions.
+ */
+ if (pcd->state == ACK && pcd->dnlp != NULL) {
+ if (purge_cache)
+ purge_dnet_cache(pnd, pcd->dnlp->dnl_rec);
+ dhcp_free_dd_list(pnd->dh, pcd->dnlp);
+ pcd->dnlp = NULL;
+ }
+
+
+ /* Prepare to delete pcd from the offer hash table. */
+ (void) hash_Delete(pnd->itable, &pcd->off_ip,
+ sizeof (struct in_addr), clnt_netcmp, pcd, NULL);
+
+ pcd->off_ip.s_addr = htonl(INADDR_ANY);
+
+ (void) mutex_lock(&ifp->ifp_mtx);
+ ifp->offers--;
+ assert((int)ifp->offers >= 0);
+ if (expired)
+ ifp->expired++;
+ (void) mutex_unlock(&ifp->ifp_mtx);
+ }
+}
+
+/*
+ * Allocate a new entry in the dhcp-network db for the cid, taking into
+ * account requested IP address. Verify address.
+ *
+ * The network portion of the address doesn't have to be the same as ours,
+ * just owned by us. We also make sure we don't select a record which is
+ * currently in use, by reserving the address in the offer cache. Database
+ * records are cached up to the D_OFFER lifetime to improve performance.
+ *
+ * Returns: 1 if there's a usable entry for the client, 0
+ * if not. Places the record in the dn_rec_list_t structure
+ * pointer handed in.
+ */
+/*ARGSUSED*/
+boolean_t
+select_offer(dsvc_dnet_t *pnd, PKT_LIST *plp, dsvc_clnt_t *pcd,
+ dn_rec_list_t **dnlpp)
+{
+ struct in_addr req_ip, *req_ipp = &req_ip, tip;
+ boolean_t found = B_FALSE;
+ time_t now;
+ dn_rec_t dn, *dnp;
+ dn_rec_list_t *dncp, *dnsp, *tlp;
+ int nrecords;
+ uint32_t query;
+ int retry;
+ boolean_t io_done, is_bootp;
+ struct in_addr *oip;
+
+ if (plp->opts[CD_DHCP_TYPE] == NULL)
+ is_bootp = B_TRUE;
+ else
+ is_bootp = B_FALSE;
+
+ *dnlpp = NULL;
+ if (!is_bootp) {
+ /*
+ * Is the DHCP client requesting a specific address? Is so, and
+ * we can satisfy him, do so.
+ */
+ if (plp->opts[CD_REQUESTED_IP_ADDR] != NULL) {
+ (void) memcpy((void *)&req_ip,
+ plp->opts[CD_REQUESTED_IP_ADDR]->value,
+ sizeof (struct in_addr));
+
+ if ((req_ip.s_addr & pnd->subnet.s_addr) ==
+ pnd->net.s_addr)
+ found = B_TRUE;
+
+ } else if (plp->opts[CD_HOSTNAME] != NULL) {
+ char hname[MAXHOSTNAMELEN + 1];
+ int hlen;
+
+ /* turn hostname option into a string */
+ hlen = MIN(plp->opts[CD_HOSTNAME]->len, MAXHOSTNAMELEN);
+ (void) memcpy(hname, plp->opts[CD_HOSTNAME]->value,
+ hlen);
+ hname[hlen] = '\0';
+
+ dhcpmsg(LOG_DEBUG,
+ "select_offer: hostname request for %s\n", hname);
+ if (name_avail(hname, pcd, plp, dnlpp, NULL,
+ &req_ipp) && req_ipp) {
+ if ((req_ip.s_addr & pnd->subnet.s_addr) ==
+ pnd->net.s_addr) {
+ found = B_TRUE;
+ } else if (*dnlpp != NULL) {
+ dhcp_free_dd_list(pnd->dh, *dnlpp);
+ *dnlpp = NULL;
+ }
+ dhcpmsg(LOG_DEBUG, "select_offer: hostname %s "
+ "available, req_ip %x\n", hname,
+ ntohl(req_ip.s_addr));
+ } else
+ dhcpmsg(LOG_DEBUG, "select_offer: name_avail "
+ "false or no address for %s\n", hname);
+ }
+ }
+
+ /*
+ * Check the offer list and table entry.
+ */
+ if (found && *dnlpp == NULL)
+ found = addr_avail(pnd, pcd, dnlpp, req_ip, B_FALSE);
+
+ if (!found) {
+ /*
+ * Try to find a free entry. Look for an AVAILABLE entry
+ * (cid == 0x00, len == 1), owned by us.
+ * The outer loop runs through the server ips owned by us.
+ *
+ * Multi-threading: to improve performance, the following
+ * algorithm coordinates accesses to the underlying table,
+ * so only one thread is initiating lookups per network.
+ * This is crucial, as lookup operations are expensive,
+ * and not sufficiently malleable to allow partitioned
+ * lookups (e.g. all that can be asked for are n free or
+ * server-owned entries, multiple threads will retrieve
+ * the same records).
+ *
+ * The three iterations through the inner loop attempt to use
+ *
+ * 1) the next cached entry
+ * 2) all cached entries
+ * 3) all free or per-server entries in the underlying table
+ *
+ * Since many threads are consuming the cached entries,
+ * any thread may find itself in the role of having to
+ * refresh the cache. We always read at least enough
+ * entries to satisfy all current threads. Reading all
+ * records is prohibitively expensive, and should only
+ * be done as a last resort.
+ *
+ * As always, to better distribute garbage
+ * collection and data structure aging tasks, each
+ * thread must actively implement policy, checking
+ * for offer expiration (which invalidates the cache).
+ */
+
+ for (oip = owner_ip; oip->s_addr != INADDR_ANY; oip++) {
+ /*
+ * Initialize query.
+ */
+ DSVC_QINIT(query);
+ DSVC_QEQ(query, DN_QCID|DN_QSIP);
+ dn.dn_cid[0] = '\0';
+ dn.dn_cid_len = 1;
+ dn.dn_sip.s_addr = ntohl(oip->s_addr);
+
+ /*
+ * Decide whether a bootp record is required.
+ */
+ dn.dn_flags = 0;
+ DSVC_QEQ(query, DN_QFBOOTP_ONLY);
+ if (is_bootp)
+ dn.dn_flags = DN_FBOOTP_ONLY;
+
+ /*
+ * These flags are used counter-intuitively.
+ * This says that the setting of the bit
+ * (off) in the dn.dn_flags matches the
+ * setting in the record (off).
+ */
+ DSVC_QEQ(query, DN_QFUNUSABLE|DN_QFMANUAL);
+
+ for (retry = 0; !found && retry < 3; retry++) {
+ now = time(NULL);
+ (void) mutex_lock(&pnd->thr_mtx);
+ nrecords = pnd->nthreads < DHCP_MIN_RECORDS ?
+ DHCP_MIN_RECORDS : pnd->nthreads;
+ (void) mutex_unlock(&pnd->thr_mtx);
+
+ /*
+ * Purge cached records when expired or database
+ * re-read.
+ */
+
+ (void) mutex_lock(&pnd->free_mtx);
+ dncp = pnd->freerec;
+ if (dncp != NULL &&
+ PND_FREE_TIMEOUT(pnd, now)) {
+ pnd->freerec = NULL;
+ dhcp_free_dd_list(pnd->dh, dncp);
+ dncp = NULL;
+ }
+
+ if (dncp != NULL) {
+ if (retry == 0) {
+ /* Try the next cached record */
+ pnd->freerec = dncp->dnl_next;
+ dncp->dnl_next = NULL;
+ } else if (retry == 1) {
+ /*
+ * Try all remaining cached
+ * records
+ */
+ pnd->freerec = NULL;
+ }
+ }
+ if (retry > 1) {
+ /* Try all possible records in datastore. */
+ pnd->freerec = NULL;
+ nrecords = -1;
+ if (dncp != NULL) {
+ dhcp_free_dd_list(
+ pnd->dh, dncp);
+ dncp = NULL;
+ }
+ }
+ (void) mutex_unlock(&pnd->free_mtx);
+
+ io_done = (dncp == NULL);
+ *dnlpp = dhcp_lookup_dd_classify(pcd->pnd,
+ nrecords == -1 ? B_FALSE : B_TRUE, query,
+ nrecords, &dn, (void **)&dncp,
+ S_CID | S_FREE);
+ if (*dnlpp != NULL) {
+ dnp = (*dnlpp)->dnl_rec;
+ tip.s_addr = htonl(dnp->dn_cip.s_addr);
+ (void) update_offer(pcd, NULL, 0,
+ &tip, B_TRUE);
+ found = B_TRUE;
+ }
+
+ (void) mutex_lock(&pnd->free_mtx);
+ if (io_done) {
+ /*
+ * Note time when records were read.
+ */
+ if (dncp != NULL) {
+ now = time(NULL);
+ pnd->free_mtime = reinit_time;
+ pnd->free_stamp = now +
+ cache_secs;
+ }
+ }
+
+ /* Save any leftover records for later use. */
+ if (dncp != NULL) {
+ for (tlp = dncp;
+ tlp != NULL && tlp->dnl_next;
+ tlp = tlp->dnl_next)
+ /* null statement */;
+ tlp->dnl_next = pnd->freerec;
+ pnd->freerec = dncp;
+ }
+ (void) mutex_unlock(&pnd->free_mtx);
+ }
+ }
+ }
+
+ if (!found && !is_bootp) {
+ /*
+ * Struck out. No usable available addresses. Let's look for
+ * the LRU expired address. Only makes sense for dhcp
+ * clients. First we'll try the next record from
+ * the lru list (this assumes lru database search capability).
+ * Next we'll try all records. Finally we'll go get all
+ * free records.
+ *
+ * Multi-threading: to improve performance, the following
+ * algorithm coordinates accesses to the underlying table,
+ * so only one thread is initiating lookups per network.
+ * This is crucial, as lookup operations are expensive,
+ * and not sufficiently malleable to allow partitioned
+ * lookups (e.g. all that can be asked for are n free or
+ * server-owned entries, multiple threads will retrieve
+ * the same records).
+ *
+ * We only consider clients owned by us.
+ * The outer loop runs through the server ips owned by us
+ *
+ * The three iterations through the inner loop attempt to use
+ *
+ * 1) the next cached entry
+ * 2) all cached entries
+ * 3) all free or per-server entries in the underlying table
+ *
+ * Since many threads are consuming the cached entries,
+ * any thread may find itself in the role of having to
+ * refresh the cache. We always read at least enough
+ * entries to satisfy all current threads. Reading all
+ * records is prohibitively expensive, and should only
+ * be done as a last resort.
+ *
+ * As always, to better distribute garbage
+ * collection and data structure aging tasks, each
+ * thread must actively implement policy, checking
+ * for offer expiration (which invalidates the cache).
+ */
+
+ for (oip = owner_ip; oip->s_addr != INADDR_ANY; oip++) {
+ /*
+ * Initialize query.
+ */
+ DSVC_QINIT(query);
+ DSVC_QEQ(query, DN_QSIP);
+ dn.dn_sip.s_addr = ntohl(oip->s_addr);
+
+ /*
+ * These flags are used counter-intuitively.
+ * This says that the setting of the bit
+ * (off) in the dn.dn_flags matches the
+ * setting in the record (off).
+ */
+ DSVC_QEQ(query, DN_QFBOOTP_ONLY|
+ DN_QFMANUAL|DN_QFUNUSABLE);
+ dn.dn_flags = 0;
+
+ for (retry = 0; !found && retry < 3; retry++) {
+ now = time(NULL);
+ (void) mutex_lock(&pnd->thr_mtx);
+ nrecords = pnd->nthreads < DHCP_MIN_RECORDS ?
+ DHCP_MIN_RECORDS : pnd->nthreads;
+ (void) mutex_unlock(&pnd->thr_mtx);
+
+ /*
+ * Purge cached records when expired or database
+ * re-read.
+ */
+
+ (void) mutex_lock(&pnd->lru_mtx);
+ dnsp = pnd->lrurec;
+ if (dnsp != NULL && PND_LRU_TIMEOUT(pnd, now)) {
+ pnd->lrurec = NULL;
+ dhcp_free_dd_list(pnd->dh, dnsp);
+ dnsp = NULL;
+ }
+
+ if (dnsp != NULL) {
+ if (retry == 0) {
+ /* Try the next cached record */
+ pnd->lrurec = dnsp->dnl_next;
+ dnsp->dnl_next = NULL;
+ } else if (retry == 1) {
+ /*
+ * Try all remaining cached
+ * records
+ */
+ pnd->lrurec = NULL;
+ }
+ }
+ if (retry > 1) {
+ /* Try all possible records */
+ pnd->lrurec = NULL;
+ nrecords = -1;
+ if (dnsp != NULL) {
+ dhcp_free_dd_list(pnd->dh,
+ dnsp);
+ dnsp = NULL;
+ }
+ }
+ (void) mutex_unlock(&pnd->lru_mtx);
+
+ io_done = (dnsp == NULL);
+ *dnlpp = dhcp_lookup_dd_classify(pcd->pnd,
+ nrecords == -1 ? B_FALSE : B_TRUE, query,
+ nrecords, &dn, (void **)&dnsp, S_LRU);
+ if (*dnlpp != NULL) {
+ dnp = (*dnlpp)->dnl_rec;
+ tip.s_addr = htonl(dnp->dn_cip.s_addr);
+ (void) update_offer(pcd, NULL, 0, &tip,
+ B_TRUE);
+ found = B_TRUE;
+ }
+
+ (void) mutex_lock(&pnd->lru_mtx);
+ if (io_done) {
+ if (dnsp != NULL) {
+ now = time(NULL);
+ pnd->lru_mtime = reinit_time;
+ pnd->lru_stamp = now +
+ cache_secs;
+ }
+ }
+
+ /*
+ * Save any leftover records for possible
+ * later use
+ */
+ if (dnsp != NULL) {
+ for (tlp = dnsp;
+ tlp != NULL && tlp->dnl_next;
+ tlp = tlp->dnl_next)
+ /* null statement */;
+ tlp->dnl_next = pnd->lrurec;
+ pnd->lrurec = dnsp;
+ }
+ (void) mutex_unlock(&pnd->lru_mtx);
+ }
+ }
+ }
+
+ return (found);
+}
+
+/*
+ * purge_dnet_cache() - remove conflicting entries from the
+ * free and lru dnet caches when records are modified. Expensive
+ * but necessary.
+ *
+ * pnd - per net struct
+ * dnp - pointer to cached/modified entry
+ */
+static void
+purge_dnet_cache(dsvc_dnet_t *pnd, dn_rec_t *dnp)
+{
+ dn_rec_list_t *tlp;
+ dn_rec_list_t *plp;
+
+ (void) mutex_lock(&pnd->free_mtx);
+
+ for (plp = tlp = pnd->freerec; tlp != NULL; tlp = tlp->dnl_next) {
+ if (tlp->dnl_rec->dn_cip.s_addr == dnp->dn_cip.s_addr) {
+ if (tlp == plp) {
+ pnd->freerec = tlp->dnl_next;
+ } else {
+ plp->dnl_next = tlp->dnl_next;
+ }
+ tlp->dnl_next = NULL;
+ break;
+ }
+ plp = tlp;
+ }
+ (void) mutex_unlock(&pnd->free_mtx);
+ if (tlp != NULL)
+ dhcp_free_dd_list(pnd->dh, tlp);
+
+ (void) mutex_lock(&pnd->lru_mtx);
+ for (plp = tlp = pnd->lrurec; tlp != NULL; tlp = tlp->dnl_next) {
+ if (tlp->dnl_rec->dn_cip.s_addr == dnp->dn_cip.s_addr) {
+ if (tlp == plp) {
+ pnd->lrurec = tlp->dnl_next;
+ } else {
+ plp->dnl_next = tlp->dnl_next;
+ }
+ tlp->dnl_next = NULL;
+ break;
+ }
+ plp = tlp;
+ }
+ (void) mutex_unlock(&pnd->lru_mtx);
+ if (tlp != NULL)
+ dhcp_free_dd_list(pnd->dh, tlp);
+}
+
+/*
+ * add_dnet_cache() - add a free entry back to the free dnet cache.
+ *
+ * Performance: this can greatly reduce the amount of work select_offer()
+ * must perform.
+ *
+ * pnd - per net struct
+ * dnlp - pointer to cached/modified entry.
+ */
+static void
+add_dnet_cache(dsvc_dnet_t *pnd, dn_rec_list_t *dnlp)
+{
+ (void) mutex_lock(&pnd->free_mtx);
+ dnlp->dnl_next = pnd->freerec;
+ pnd->freerec = dnlp;
+ (void) mutex_unlock(&pnd->free_mtx);
+}
+
+static char unowned_net[] = "the DHCP server believes the IP address that"
+ " corresponds to the requested host name belongs to a network not"
+ " managed by the DHCP server.\n";
+static char unowned_addr[] = "the DHCP server believes the IP address that"
+ " corresponds to the requested host name is not managed by the DHCP"
+ " server.\n";
+
+/*
+ * Determine whether the requested IP address is available to the requesting
+ * client. To be so, its IP address must be managed by us, be on the ``right''
+ * network and neither currently leased nor currently under offer to another
+ * client.
+ */
+static boolean_t
+addr_avail(dsvc_dnet_t *pnd, dsvc_clnt_t *pcd, dn_rec_list_t **dnlpp,
+ struct in_addr req_ip, boolean_t isname)
+{
+ dn_rec_t dn;
+ dn_rec_list_t *dnip;
+ uint32_t query;
+
+ *dnlpp = NULL;
+ /*
+ * first, check the ICMP list or offer list.
+ */
+ if (isname) {
+ if (pcd->off_ip.s_addr != req_ip.s_addr &&
+ check_offer(pnd, &req_ip) == B_FALSE) {
+ /* Offered to someone else. Sorry. */
+ dhcpmsg(LOG_DEBUG, "name_avail(F):"
+ " check_offer failed\n");
+ return (B_FALSE);
+ }
+ } else {
+ if (update_offer(pcd, NULL, 0, &req_ip, B_TRUE) == B_FALSE) {
+ /* Offered to someone else. Sorry. */
+ if (isname) {
+ dhcpmsg(LOG_DEBUG, "name_avail(F):"
+ " check_other_offers failed\n");
+ }
+ return (B_FALSE);
+ }
+ }
+
+ /*
+ * entry_available() searches for owner_ips
+ * query on DN_QCIP will suffice here
+ */
+ DSVC_QINIT(query);
+ DSVC_QEQ(query, DN_QCIP);
+ dn.dn_cip.s_addr = ntohl(req_ip.s_addr);
+
+ dnip = NULL;
+ *dnlpp = dhcp_lookup_dd_classify(pnd, B_FALSE, query, -1, &dn,
+ (void **)&dnip, 0);
+ dhcp_free_dd_list(pnd->dh, dnip);
+ if (*dnlpp != NULL) {
+ /*
+ * Ok, the requested IP exists. But is it available?
+ */
+ if (!entry_available(pcd, (*dnlpp)->dnl_rec)) {
+ dhcp_free_dd_list(pnd->dh, *dnlpp);
+ *dnlpp = NULL;
+ purge_offer(pcd, B_FALSE, B_TRUE);
+ return (B_FALSE);
+ }
+ } else {
+ if (isname)
+ dhcpmsg(LOG_DEBUG, "name_avail(F): %s", unowned_addr);
+ else
+ purge_offer(pcd, B_FALSE, B_TRUE);
+ return (B_FALSE);
+ }
+ return (B_TRUE);
+}
+
+/*
+ * Determine whether "name" is available. To be so, it must either not have
+ * a corresponding IP address, or its IP address must be managed by us and
+ * neither currently leased nor currently under offer to a client.
+ *
+ * To determine this, we first attempt to translate the name to an address.
+ * If no name-to-address translation exists, it's automatically available.
+ * Otherwise, we next check for any outstanding offers. Finally, we look
+ * at the flags in the corresponding per-network table to see whether the
+ * address is currently leased.
+ *
+ * Upon successful completion, we also return the vetted IP address as a
+ * value result parameter.
+ */
+static boolean_t
+name_avail(char *name, dsvc_clnt_t *pcd, PKT_LIST *plp, dn_rec_list_t **dnlpp,
+ ENCODE *ecp, struct in_addr **iap)
+{
+ struct hostent h, *hp, *owner_hp;
+ char hbuf[NSS_BUFLEN_HOSTS];
+ char fqname [NS_MAXDNAME+1];
+ char owner [NS_MAXDNAME+1];
+ int err, ho_len;
+ struct in_addr ia, ma;
+ dsvc_dnet_t *pnd;
+ boolean_t isopen = B_FALSE;
+ ENCODE *ep;
+
+ *dnlpp = NULL;
+ /*
+ * If possible, use a fully-qualified name to do the name-to-
+ * address query. The complication is that the domain name
+ * with which to qualify the client's host name resides in a
+ * dhcptab macro unavailable at the time of the DHCPOFFER.
+ * ecp will be non-NULL if we may have the means to fully-qualify
+ * the name given.
+ */
+ if (strchr(name, '.') != NULL) {
+ (void) strlcpy(fqname, name, sizeof (fqname));
+ if (fqname[(strlen(fqname))-1] != '.')
+ (void) strcat(fqname, ".");
+ } else {
+ /*
+ * Append '.' domain-name '.' to hostname.
+ * Note the use of the trailing '.' to avoid any surprises
+ * because of the ndots value (see resolv.conf(4) for more
+ * information about the latter).
+ *
+ * First see whether we can dredge up domain-name from the
+ * ENCODE list.
+ */
+ if ((ecp != NULL) && ((ep = find_encode(ecp,
+ DSYM_STANDARD, CD_DNSDOMAIN)) != NULL)) {
+ DHCP_OPT *ho = plp->opts[CD_HOSTNAME];
+
+ /*
+ * name_avail() should never be called unless the
+ * CD_HOSTNAME option is present in the client's
+ * packet.
+ */
+ assert(ho != NULL);
+ ho_len = ho->len;
+ if (ho->value[ho_len - 1] == '\0') {
+ /* null at end of the hostname */
+ ho_len = strlen((char *)ho->value);
+ }
+
+ if (qualify_hostname(fqname, (char *)ho->value,
+ (char *)ep->data, ho_len, ep->len) == -1)
+ return (B_FALSE);
+
+ dhcpmsg(LOG_DEBUG, "name_avail: unqualified name\n"
+ "found CD_DNSDOMAIN and qualified: %s\n", fqname);
+ } else {
+ /*
+ * No DNS domain in the ENCODE list, have to use
+ * local domain name.
+ */
+ if ((resolv_conf.defdname == NULL) ||
+ (qualify_hostname(fqname, name,
+ resolv_conf.defdname,
+ strlen(name),
+ strlen(resolv_conf.defdname)) == -1))
+ return (B_FALSE);
+
+ dhcpmsg(LOG_DEBUG,
+ "name_avail: unqualified name\n"
+ "qualified with local domain: %s\n", fqname);
+ }
+ }
+
+ /*
+ * Try a forward lookup on the requested name.
+ * Consider the name available if we get a definitive
+ * ``name doesn't exist'' indication.
+ */
+ hp = gethostbyname_r(fqname, &h, hbuf, sizeof (hbuf), &err);
+ if (hp == NULL)
+ if ((err == HOST_NOT_FOUND) || (err == NO_DATA)) {
+ *iap = NULL;
+ dhcpmsg(LOG_DEBUG,
+ "name_avail(T): gethostbyname_r failed\n");
+ return (B_TRUE);
+ } else {
+ dhcpmsg(LOG_DEBUG,
+ "name_avail(F): gethostbyname_r failed, err %d\n",
+ err);
+ return (B_FALSE);
+ }
+
+ /*
+ * Check that the address has not been leased to someone else.
+ * Bear in mind that there may be inactive A records in the DNS
+ * (since we don't delete them when a lease expires or is released).
+ * Try a reverse lookup on the address returned in hp.
+ * If the owner of this address is different to the requested name
+ * we can infer that owner is a stale A record.
+ */
+
+ (void) memcpy(&ia, hp->h_addr, sizeof (struct in_addr));
+ owner_hp = gethostbyaddr_r((char *)&ia, sizeof (struct in_addr),
+ AF_INET, &h, hbuf, sizeof (hbuf), &err);
+
+ if (owner_hp == NULL) {
+ /* If there's no PTR record the address can't be in use */
+ if ((err == HOST_NOT_FOUND) || (err == NO_DATA)) {
+ *iap = NULL;
+ dhcpmsg(LOG_DEBUG,
+ "name_avail(T): gethostbyaddr_r failed\n");
+ return (B_TRUE);
+ } else {
+ dhcpmsg(LOG_DEBUG,
+ "name_avail(F): gethostbyaddr_r failed\n");
+ return (B_FALSE);
+ }
+ }
+
+ /* If name returned is not a FQDN, qualify with local domain name */
+
+ if (strchr(owner_hp->h_name, '.') != NULL) {
+ (void) strlcpy(owner, owner_hp->h_name, sizeof (owner));
+ if (owner[(strlen(owner))-1] != '.')
+ (void) strcat(owner, ".");
+ } else {
+ if ((resolv_conf.defdname == NULL) ||
+ (qualify_hostname(owner, owner_hp->h_name,
+ resolv_conf.defdname,
+ strlen(owner_hp->h_name),
+ strlen(resolv_conf.defdname)) == -1))
+ return (B_FALSE);
+
+ dhcpmsg(LOG_DEBUG,
+ "name_avail: address owner qualified with %s\n",
+ resolv_conf.defdname);
+ }
+
+ if ((strncmp(owner, fqname, NS_MAXDNAME)) != 0) {
+ /* Forward lookup found an inactive record - ignore it */
+ *iap = NULL;
+ dhcpmsg(LOG_DEBUG, "name_avail(T): 'A' record inactive: %s\n",
+ owner);
+ return (B_TRUE);
+ }
+
+ /* Get pnd of the current client */
+ pnd = pcd->pnd;
+ get_netmask(&ia, &ma);
+ if (pnd->net.s_addr != (ia.s_addr & ma.s_addr)) {
+ /* get pnd of previous owner of the hostname */
+ if (open_dnet(&pnd, &ia, &ma) != DSVC_SUCCESS) {
+ /* we must not manage the net containing this address */
+ dhcpmsg(LOG_DEBUG, "name_avail(F): %s", unowned_net);
+ return (B_FALSE);
+ }
+ isopen = B_TRUE;
+ }
+
+ /*
+ * Test that the address has not been offered to someone else.
+ */
+ if (!addr_avail(pnd, pcd, dnlpp, ia, B_TRUE)) {
+ if (isopen) {
+ close_dnet(pnd, B_FALSE);
+ }
+ return (B_FALSE);
+ }
+ if (isopen)
+ close_dnet(pnd, B_FALSE);
+
+ /* LINTED */
+ **iap = *((struct in_addr *)hp->h_addr);
+ dhcpmsg(LOG_DEBUG, "name_avail(T)\n");
+ return (B_TRUE);
+}
+
+static boolean_t
+entry_available(dsvc_clnt_t *pcd, dn_rec_t *dnp)
+{
+ boolean_t isme = dnp->dn_cid_len == pcd->cid_len &&
+ memcmp(pcd->cid, dnp->dn_cid, pcd->cid_len) == 0;
+
+ if ((dnp->dn_flags & (DN_FMANUAL|DN_FUNUSABLE)) != 0)
+ return (B_FALSE);
+ if (dnp->dn_cid_len != 0 && isme == B_FALSE &&
+ (dnp->dn_flags & (DN_FAUTOMATIC|DN_FBOOTP_ONLY)))
+ return (B_FALSE);
+ if (dnp->dn_cid_len != 0 && isme == B_FALSE &&
+ (lease_t)time(NULL) < (lease_t)ntohl(dnp->dn_lease))
+ return (B_FALSE);
+ if (match_ownerip(htonl(dnp->dn_sip.s_addr)) == NULL)
+ return (B_FALSE);
+ return (B_TRUE);
+}
+
+static char msft_classid[] = "MSFT ";
+static char no_domain[] = "name service update on behalf of client with ID"
+" %s failed because requested name was not fully-qualified and no DNS"
+" domain name was specified for this client in the dhcptab\n";
+
+/*
+ * Given a host name and IP address, try to do a host name update.
+ */
+static boolean_t
+do_nsupdate(struct in_addr ia, ENCODE *ecp, PKT_LIST *plp)
+{
+ struct hostent *hp;
+ DHCP_OPT *ho;
+ ENCODE *ep;
+ char class_idbuf[DSYM_CLASS_SIZE];
+ int puthostent_ret;
+
+ /*
+ * hostent information is dynamically allocated so that threads spawned
+ * by dns_puthostent() will have access to it after the calling thread
+ * has returned.
+ */
+ hp = (struct hostent *)smalloc(sizeof (struct hostent));
+ hp->h_addr_list = (char **)smalloc(2 * sizeof (char **));
+ hp->h_addr_list[1] = NULL;
+ hp->h_addr = smalloc(sizeof (struct in_addr));
+ hp->h_aliases = NULL;
+ hp->h_addrtype = AF_INET;
+ hp->h_length = sizeof (struct in_addr);
+ /*
+ * Convert address to network order, as that's what hostent's are
+ * expected to be.
+ */
+ /* LINTED */
+ ((struct in_addr *)hp->h_addr)->s_addr = htonl(ia.s_addr);
+
+ /*
+ * Is the host name unqualified? If so, try to qualify it. If that
+ * can't be done, explain why the update won't be attempted.
+ */
+ ho = plp->opts[CD_HOSTNAME];
+ if (memchr(ho->value, '.', ho->len) == NULL) {
+ /*
+ * See whether we can dredge up the DNS domain from the
+ * ENCODE list.
+ */
+ if ((ep = find_encode(ecp, DSYM_STANDARD, CD_DNSDOMAIN)) !=
+ NULL) {
+ char *fqname;
+ int ho_len = ho->len;
+
+ /*
+ * We need room for
+ *
+ * hostname len +
+ * strlen(".") +
+ * domainname len +
+ * strlen(".") +
+ * trailing '\0'
+ *
+ * Note the use of the trailing '.' to avoid any
+ * surprises because of the ndots value (see
+ * resolv.conf(4) for more information about
+ * the latter).
+ */
+ if (ho->value[ho_len - 1] == '\0') {
+ ho_len = strlen((char *)ho->value);
+ }
+ fqname = smalloc(ho_len + ep->len + 1 + 1 + 1);
+ /* first copy host name, ... */
+ (void) memcpy(fqname, ho->value, ho_len);
+ /* then '.', ... */
+ (void) memcpy(fqname + ho_len, ".", 1);
+ /* ... then domain name, */
+ (void) memcpy(fqname + ho_len + 1, ep->data, ep->len);
+ /* then a trailing '.', ... */
+ (void) memcpy(fqname + ho_len + ep->len + 1, ".", 1);
+ /* no need to null-terminate - smalloc() did it */
+
+ hp->h_name = fqname;
+ dhcpmsg(LOG_DEBUG, "do_nsupdate: unqualified name\n"
+ "found CD_DNSDOMAIN and qualified: %s\n", fqname);
+ } else {
+ char cidbuf[BUFSIZ];
+
+ (void) disp_cid(plp, cidbuf, sizeof (cidbuf));
+ dhcpmsg(LOG_INFO, no_domain, cidbuf);
+ }
+ } else {
+ hp->h_name = smalloc(ho->len + 1);
+ (void) memcpy(hp->h_name, ho->value, ho->len);
+ dhcpmsg(LOG_DEBUG, "do_nsupdate: fully qualified name: %s\n",
+ hp->h_name);
+ }
+
+ /* returns -1 or the number of name service updates done */
+ puthostent_ret = dns_puthostent(hp, nsutimeout_secs);
+ dhcpmsg(LOG_DEBUG, "do_nsupdate: dns_puthostent returned %d\n",
+ puthostent_ret);
+ if (puthostent_ret == -1) {
+ return (B_FALSE);
+ } else if (puthostent_ret == 0) {
+ /*
+ * dns_puthostent() didn't see any errors occur,
+ * but no updates were done; Microsoft clients
+ * (i.e. clients with a Microsoft class ID) expect
+ * it to succeed, so we lie to them.
+ */
+ if (((get_class_id(plp, class_idbuf,
+ sizeof (class_idbuf))) != NULL) &&
+ (strncmp(msft_classid, class_idbuf,
+ sizeof (msft_classid)) == 0)) {
+ dhcpmsg(LOG_DEBUG, "do_nsupdate: class ID \"%s\"\n",
+ class_idbuf);
+ return (B_TRUE);
+ } else
+ return (B_FALSE);
+ } else {
+ return (B_TRUE);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/dhcpd.h b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/dhcpd.h
new file mode 100644
index 0000000000..321bfa315f
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/dhcpd.h
@@ -0,0 +1,231 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _DHCPD_H
+#define _DHCPD_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * dhcpd.h -- common header file for all the modules of the in.dhcpd program.
+ */
+
+#include <dhcp_svc_confopt.h>
+#include <dhcp_svc_private.h>
+#include <dhcp_svc_public.h>
+#include <dhcp_impl.h>
+#include <dhcp_symbol.h>
+#include <libinetutil.h>
+#include "hash.h"
+#include "per_dnet.h"
+
+/*
+ * Raw encoded packet data. The final state. Note that 'code' not only
+ * describes options: predefined: 1-60, site: 128-254, vendor: 42(*),
+ * but it also defines packet fields for packet data as well.
+ * Note that due to overlap of codes between various DSYM categories,
+ * category must be used to distinguish (see libdhcputil).
+ */
+typedef struct encoded {
+ uchar_t category; /* Option category */
+ ushort_t code; /* Option code: 1--254, pkt loc */
+ uchar_t len; /* len of data */
+ uchar_t *data; /* Encoded DHCP packet field / option */
+ struct encoded *prev; /* previous in list */
+ struct encoded *next; /* next in list */
+} ENCODE;
+
+typedef struct {
+ char class[DSYM_CLASS_SIZE + 1]; /* client class */
+ ENCODE *head; /* options of this class */
+} VNDLIST;
+
+typedef struct {
+ char nm[DN_MAX_CID_LEN + 1]; /* Macro name */
+ ENCODE *head; /* head of encoded opts */
+ int classes; /* num of client classes */
+ VNDLIST **list; /* table of client classes */
+} MACRO;
+
+/* logging message categories */
+typedef enum {
+ L_ASSIGN = 0, /* New assignment */
+ L_REPLY = 1, /* respond to existing client */
+ L_RELEASE = 2, /* client released IP */
+ L_DECLINE = 3, /* client declined IP */
+ L_INFORM = 4, /* client requested information only */
+ L_NAK = 5, /* client NAK'ed */
+ L_ICMP_ECHO = 6, /* Server detected IP in use */
+ L_RELAY_REQ = 7, /* Relay request to server(s) */
+ L_RELAY_REP = 8 /* Relay reply to client */
+} DHCP_MSG_CATEGORIES;
+
+typedef enum {
+ P_BOOTP = 0, /* BOOT Protocol */
+ P_DHCP = 1 /* DHC Protocol */
+} DHCP_PROTO;
+
+#define DHCPD "in.dhcpd" /* daemon's name */
+#define DAEMON_VERS "3.5" /* daemon's version number */
+#define ENC_COPY 0 /* Copy encode list */
+#define ENC_DONT_COPY 1 /* don't copy encode list */
+#define DHCP_MAX_REPLY_SIZE 8192 /* should be big enough */
+#define DHCP_MIN_RECORDS 32 /* should be big enough */
+#define DHCP_ICMP_ATTEMPTS 1 /* Number of ping attempts */
+#define DHCP_ICMP_TIMEOUT 1000 /* Wait # millisecs for resp */
+#define DHCP_ARP_ADD 0 /* Add an ARP table entry */
+#define DHCP_ARP_DEL 1 /* Del an ARP table entry */
+#define DHCP_SCRATCH 128 /* scratch buffer size */
+#define NEW_DHCPTAB 0 /* load initial dhcptab */
+#define PRESERVE_DHCPTAB 1 /* preserve previous dhcptab */
+#define DEFAULT_LEASE 3600 /* Default if not specified */
+#define DHCP_RDCOP_RETRIES 3 /* Attempts to read options */
+#define HASHTABLESIZE 257 /* must be a prime number */
+#define DHCP_RESCAN_SCALE 60L /* scale rescan_interval */
+
+#define DHCP_MIN_CLIENTS 32 /* minimum client structs */
+#define DHCP_DEFAULT_CLIENTS 1024 /* default client structs */
+#define DHCP_MINFREE_CLIENTS 8 /* minimum free clients */
+#define DHCP_NSS_LWP 32 /* free lwps for nss lib use */
+#define DHCP_NSS_TIME 3 /* name service cache time */
+#define DHCP_NO_NSU (-1) /* No Name service updates */
+
+/* load option flags */
+#define DHCP_DHCP_CLNT 1 /* It's a DHCP client */
+#define DHCP_SEND_LEASE 2 /* Send lease parameters */
+#define DHCP_NON_RFC1048 4 /* non-rfc1048 magic cookie */
+#define DHCP_OVRLD_CLR ((uchar_t)0x00) /* SNAME/FILE clear */
+#define DHCP_OVRLD_FILE ((uchar_t)0x01) /* FILE in use */
+#define DHCP_OVRLD_SNAME ((uchar_t)0x02) /* SNAME in use */
+#define DHCP_OVRLD_MASK ((uchar_t)0xfc) /* Only last two bits */
+
+/* dhcp_lookup_dd_classify search flags */
+#define S_CID 0x01 /* find a client match */
+#define S_FREE 0x02 /* find a free record */
+#define S_LRU 0x04 /* find an lru record */
+
+/* DHCP client states */
+#define INIT_STATE 1
+#define INIT_REBOOT_STATE 2
+#define RENEW_REBIND_STATE 3
+
+extern int debug;
+extern boolean_t verbose;
+extern boolean_t noping;
+extern boolean_t no_dhcptab;
+extern boolean_t server_mode;
+extern boolean_t be_automatic;
+extern uchar_t max_hops;
+extern int log_local;
+extern int icmp_tries;
+extern time_t off_secs;
+extern time_t cache_secs;
+extern time_t renog_secs;
+extern time_t min_lru;
+extern time_t icmp_timeout;
+extern time_t nsutimeout_secs;
+extern boolean_t time_to_go;
+extern struct in_addr server_ip;
+extern struct in_addr *owner_ip;
+extern dsvc_datastore_t datastore;
+extern int max_threads; /* maximum number of threads per net */
+extern int max_clients; /* maximum number of clients per net */
+extern ushort_t port_offset; /* offset to port for multiple server */
+extern int net_thresh; /* secs to keep pernet reference */
+extern int clnt_thresh; /* secs to keep client reference */
+extern time_t reinit_time; /* reinitialization time */
+extern struct __res_state resolv_conf;
+#ifdef DEBUG
+extern char *dbg_net; /* simulated debug net (see misc.c) */
+#endif /* DEBUG */
+
+extern void *reinitialize(void *);
+extern PKT *gen_bootp_pkt(int, PKT *);
+extern int initmtab(void);
+extern int initntab(void);
+extern int checktab(void);
+extern int readtab(int);
+extern void resettab(boolean_t);
+extern int relay_agent_init(char *);
+extern void dhcpmsg(int, const char *, ...);
+extern void *smalloc(unsigned);
+extern void *srealloc(void *, uint_t);
+extern struct in_addr *match_ownerip(in_addr_t);
+extern void *stack_create(unsigned int);
+extern ENCODE *combine_encodes(ENCODE *, ENCODE *, int);
+extern void open_macros(void);
+extern void close_macros(void);
+extern MACRO *get_macro(char *);
+extern ENCODE *find_encode(ENCODE *, uchar_t, ushort_t);
+extern ENCODE *dup_encode(ENCODE *);
+extern ENCODE *make_encode(uchar_t, ushort_t, uchar_t, void *, int);
+extern ENCODE *dup_encode_list(ENCODE *);
+extern void free_encode_list(ENCODE *);
+extern void free_encode(ENCODE *);
+extern void replace_encode(ENCODE **, ENCODE *, int);
+extern ENCODE *vendor_encodes(MACRO *, char *);
+extern char *disp_cid(PKT_LIST *, char *, int);
+extern void get_clnt_id(PKT_LIST *, uchar_t *, int, uchar_t *);
+extern char *get_class_id(PKT_LIST *, char *, int);
+extern int load_options(int, PKT_LIST *, PKT *, int, uchar_t *, ENCODE *,
+ ENCODE *);
+extern void free_plp(PKT_LIST *);
+extern void logtrans(DHCP_PROTO, DHCP_MSG_CATEGORIES, time_t,
+ struct in_addr, struct in_addr, PKT_LIST *);
+extern int icmp_echo_check(struct in_addr *, boolean_t *);
+extern void *monitor_client(void *);
+
+extern void dhcp(dsvc_clnt_t *, PKT_LIST *);
+boolean_t update_offer(dsvc_clnt_t *, dn_rec_list_t *, lease_t,
+ struct in_addr *, boolean_t);
+extern void bootp(dsvc_clnt_t *, PKT_LIST *);
+extern void get_netmask(struct in_addr *, struct in_addr *);
+extern boolean_t select_offer(dsvc_dnet_t *dbp, PKT_LIST *, dsvc_clnt_t *,
+ dn_rec_list_t **);
+
+extern int dhcp_open_dd(dsvc_handle_t *, dsvc_datastore_t *, dsvc_contype_t,
+ const char *, uint_t);
+extern int dhcp_close_dd(dsvc_handle_t *);
+extern int dhcp_modify_dd_entry(dsvc_handle_t, const void *, void *);
+extern void dhcp_free_dd_list(dsvc_handle_t, void *);
+
+extern void *dhcp_lookup_dd_classify(dsvc_dnet_t *, boolean_t, uint_t, int,
+ const dn_rec_t *, void **, int);
+
+extern dn_rec_list_t *detach_dnrec_from_list(dn_rec_list_t *, dn_rec_list_t *,
+ dn_rec_list_t **);
+
+extern int qualify_hostname(char *, const char *, const char *, int, int);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _DHCPD_H */
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/dhcptab.c b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/dhcptab.c
new file mode 100644
index 0000000000..db7d2ce975
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/dhcptab.c
@@ -0,0 +1,1313 @@
+/*
+ * Routines and structures which are used from CMU's 2.2 bootp implementation
+ * are labelled as such. Code not labelled is:
+ *
+ * Copyright 1997-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Copyright 1988, 1991 by Carnegie Mellon University
+ *
+ * All Rights Reserved
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of Carnegie Mellon University not be used
+ * in advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+ * IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+ * THIS SOFTWARE.
+ */
+
+/*
+ * in.dhcpd configuration file reading code.
+ *
+ * The routines in this file deal with reading, interpreting, and storing
+ * the information found in the in.dhcpd configuration file (usually
+ * /etc/dhcptab).
+ */
+
+/*
+ * TODO: What's missing: Symbol code is very generic, but doesn't allow
+ * per symbol granularity checking - ie, using goodname() to check the
+ * hostname, for example. Perhaps each symbol should have a verifier
+ * function possibly associated with it (null is ok), which would return
+ * B_TRUE if ok, B_FALSE if not, and print out a nasty message.
+ *
+ * Option overload. If set, then NO BOOTFILE or SNAME values can exist.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <assert.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/byteorder.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <syslog.h>
+#include <dhcp_inittab.h>
+#include <dhcp_symbol.h>
+#include "netinet/dhcp.h"
+#include "hash.h"
+#include "dhcpd.h"
+#include <locale.h>
+
+/*
+ * Local constants
+ */
+#define OP_ADDITION 1 /* Operations on tags */
+#define OP_DELETION 2
+#define OP_BOOLEAN 3
+
+#define MAXENTRYLEN 3072 /* Max size of an entire entry */
+#define MAX_ITEMS 16 /* Max number of items in entry */
+#define MAX_MACRO_NESTING 20 /* Max number of nested includes */
+
+#define DB_DHCP_MAC 'm' /* Like TBL_DHCP_{MACRO,SYMBOL} */
+#define DB_DHCP_SYM 's' /* But not strings! */
+
+static time_t mtable_time; /* load time of hash table */
+
+static uint_t nentries; /* Total number of entries */
+static int include_nest; /* macro nesting counter */
+static dt_rec_list_t **newtbl; /* reordered Tbl. */
+static dt_rec_list_t **oldtbl; /* The original tbl. */
+static hash_tbl *mtable;
+static mutex_t mtable_mtx; /* Reinitialization mutex */
+static cond_t mtable_cv; /* Reinitialization cv */
+static int mtable_refcnt; /* Current reference count */
+static int mtable_closing; /* macros are going away */
+
+#define INCLUDE_SYM "Include" /* symbol for macro include */
+
+/*
+ * Forward declarations.
+ */
+static dhcp_symbol_t *sym_list;
+static size_t sym_num_items;
+static int check_includes(int, char *);
+static MACRO *process_entry(dt_rec_list_t *);
+static int eval_symbol(char **, MACRO *);
+static char *get_string(char **, char *, uchar_t *);
+static void adjust(char **);
+static void eat_whitespace(char **);
+static int first_macro_row(dt_rec_list_t **);
+static void get_sym_name(char *, char **);
+static void print_error_msg(int, uchar_t);
+static boolean_t define_symbol(char **, char *);
+static int scan_include(char **, char *);
+static boolean_t free_macro(MACRO *, boolean_t);
+static int macro_cmp(MACRO *, MACRO *);
+static void add_vndlist(ENCODE *, MACRO *, dhcp_symbol_t *);
+
+/*
+ * Initialize the hash table.
+ */
+int
+initmtab(void)
+{
+ /*
+ * Allocate hash table
+ */
+ mtable = hash_Init(0, NULL, 0, B_FALSE);
+
+ assert(mtable != NULL);
+
+ (void) mutex_init(&mtable_mtx, USYNC_THREAD, NULL);
+ (void) cond_init(&mtable_cv, USYNC_THREAD, 0);
+
+ return (0);
+}
+
+/*
+ * Check presence/access to dhcptab database file.
+ */
+int
+checktab(void)
+{
+ int err;
+ dsvc_handle_t dh;
+
+ err = open_dd(&dh, &datastore, DSVC_DHCPTAB, DT_DHCPTAB, DSVC_READ);
+ switch (err) {
+ case DSVC_SUCCESS:
+ (void) close_dd(&dh);
+ break;
+ case DSVC_ACCESS:
+ dhcpmsg(LOG_ERR,
+ "No permission to access dhcptab in %s (%s)\n",
+ datastore.d_resource, datastore.d_location);
+ err = EACCES;
+ break;
+ case DSVC_NOENT:
+ case DSVC_NO_TABLE:
+ dhcpmsg(LOG_INFO,
+ "Dhcptab table does not exist in %s (%s)\n",
+ datastore.d_resource, datastore.d_location);
+ err = ENOENT;
+ break;
+ default:
+ dhcpmsg(LOG_ERR,
+ "Error checking status of dhcptab in %s (%s)\n",
+ datastore.d_resource, datastore.d_location);
+ }
+ return (err);
+}
+
+/*
+ * Read dhcptab database file.
+ */
+int
+readtab(int preserve)
+{
+ int err = 0, first_mac;
+ MACRO *mc;
+ uint32_t query;
+ dt_rec_t dt;
+ dt_rec_list_t **dhcptab = NULL;
+ dt_rec_list_t *dhcptab_list = NULL;
+ dt_rec_list_t *dtep;
+ int i;
+ int ind;
+ uint_t records;
+ timestruc_t tm;
+ dsvc_handle_t dh;
+ boolean_t tab_open = B_FALSE;
+
+ (void) mutex_lock(&mtable_mtx);
+
+ /*
+ * Wait for any current thread(s) to complete using macros.
+ */
+ mtable_closing = 1;
+ while (mtable_refcnt > 0) {
+ tm.tv_sec = 1;
+ tm.tv_nsec = 0;
+ (void) cond_reltimedwait(&mtable_cv, &mtable_mtx, &tm);
+ }
+
+ /* Get the *entire* dhcptab. */
+ while ((err = open_dd(&dh, &datastore, DSVC_DHCPTAB, DT_DHCPTAB,
+ DSVC_READ)) != DSVC_SUCCESS) {
+ if (err == DSVC_BUSY) {
+ continue;
+ }
+ if (err == DSVC_NOENT) {
+ dhcpmsg(LOG_INFO, "Empty dhcptab macro database.\n");
+ err = 0; /* not a "real" error */
+ } else
+ dhcpmsg(LOG_ERR, "Error opening macro database: %s\n",
+ dhcpsvc_errmsg(err));
+ goto leave_readtab;
+ }
+
+ tab_open = B_TRUE;
+
+ DSVC_QINIT(query);
+ (void) memset(&dt, 0, sizeof (dt_rec_t));
+
+ dhcptab_list = NULL;
+ nentries = 0;
+ err = lookup_dd(dh, B_FALSE, query, -1, (const void *)&dt,
+ (void **)&dhcptab_list, &nentries);
+ if (err != DSVC_SUCCESS) {
+ if (verbose && err == DSVC_NOENT) {
+ dhcpmsg(LOG_INFO, "Error access macro database: %s\n",
+ dhcpsvc_errmsg(err));
+ }
+ goto leave_readtab;
+ } else
+ err = 0;
+ if (nentries == 0) {
+ dhcpmsg(LOG_INFO, "Empty dhcptab macro database.\n");
+ err = 0; /* not a "real" error */
+ goto leave_readtab;
+ }
+
+ /*
+ * Because libdhcpsvc doesn't guarantee any order, we need to
+ * preprocess the macro list to guarantee that macros which are
+ * included in other macro definitions are already defined prior
+ * to their use. This means that macro processing is a two step process.
+ */
+ dhcptab = (dt_rec_list_t **)smalloc((nentries + 1) *
+ sizeof (dt_rec_list_t *));
+
+ /* Extract symbols first. */
+ ind = 0;
+ for (i = 0, dtep = dhcptab_list; dtep != NULL;
+ i++, dtep = dtep->dtl_next) {
+ if (dtep->dtl_rec->dt_type == DT_SYMBOL) {
+ dhcptab[ind++] = dtep;
+ }
+ }
+ /* Copy macros */
+ for (i = 0, dtep = dhcptab_list; dtep != NULL;
+ i++, dtep = dtep->dtl_next) {
+ if (dtep->dtl_rec->dt_type == DT_MACRO) {
+ dhcptab[ind++] = dtep;
+ }
+ }
+
+ first_mac = first_macro_row(dhcptab);
+ include_nest = 0;
+ if (first_mac >= 0) {
+ oldtbl = dhcptab;
+ newtbl = (dt_rec_list_t **)smalloc((nentries + 1) *
+ sizeof (dt_rec_list_t *));
+ for (i = 0; i < first_mac; i++)
+ newtbl[i] = oldtbl[i]; /* copy symdefs */
+ for (i = first_mac; i < nentries; i++) {
+ if ((err = check_includes(first_mac,
+ oldtbl[i]->dtl_rec->dt_key)) != 0)
+ break;
+ }
+ if (err != 0) {
+ free(newtbl);
+ free(dhcptab);
+ goto leave_readtab;
+ } else {
+ free(dhcptab);
+ dhcptab = newtbl;
+ }
+ }
+
+ resettab(B_FALSE);
+
+ /*
+ * Now table is reordered. process as usual.
+ */
+ records = 0;
+ for (i = 0; i < nentries; i++) {
+
+ if ((mc = process_entry(dhcptab[i])) == (MACRO *)NULL)
+ continue;
+
+ if (hash_Insert(mtable, mc->nm, strlen(mc->nm), macro_cmp,
+ mc->nm, mc) == NULL) {
+ dhcpmsg(LOG_WARNING,
+ "Duplicate macro definition: %s\n", mc->nm);
+ continue;
+ }
+ records++;
+ }
+
+ mtable_time = time(NULL);
+
+ if (verbose) {
+ dhcpmsg(LOG_INFO,
+ "Read %d entries from DHCP macro database on %s",
+ records, ctime(&mtable_time));
+ }
+
+ free(dhcptab);
+
+leave_readtab:
+
+ if (dhcptab_list != NULL)
+ free_dd_list(dh, dhcptab_list);
+
+ if (tab_open)
+ (void) close_dd(&dh);
+
+ if (preserve && err != 0) {
+ dhcpmsg(LOG_WARNING,
+ "DHCP macro database rescan failed %d, using scan: %s",
+ err, ctime(&mtable_time));
+ err = 0;
+ }
+
+ mtable_closing = 0;
+ (void) mutex_unlock(&mtable_mtx);
+ return (err);
+}
+
+/*
+ * Reset the dhcptab hash table, free any dynamic symbol definitions.
+ */
+void
+resettab(boolean_t lck)
+{
+ int i;
+ dhcp_symbol_t tmp;
+ timestruc_t tm;
+
+ if (lck == B_TRUE)
+ (void) mutex_lock(&mtable_mtx);
+
+ /*
+ * Wait for any current thread(s) to complete using macros.
+ */
+ if (mtable_closing == 0) {
+ mtable_closing = 1;
+ while (mtable_refcnt > 0) {
+ tm.tv_sec = 1;
+ tm.tv_nsec = 0;
+ (void) cond_reltimedwait(&mtable_cv, &mtable_mtx, &tm);
+ }
+ }
+
+ /* Entirely erase all hash tables. */
+ hash_Reset(mtable, free_macro);
+
+ /*
+ * Dump any dynamically defined symbol definitions, and reinitialize.
+ */
+ if (sym_list != NULL) {
+
+ /*
+ * Free class resources for each symbol.
+ */
+ for (i = 0; i < sym_num_items; i++) {
+ dsym_free_classes(&sym_list[i].ds_classes);
+ }
+
+ free(sym_list);
+ sym_list = NULL;
+ }
+
+ if (time_to_go) {
+ if (lck == B_TRUE) {
+ (void) mutex_unlock(&mtable_mtx);
+ (void) mutex_destroy(&mtable_mtx);
+ (void) cond_destroy(&mtable_cv);
+ }
+ return;
+ }
+
+ /* Allocate the inittab and class tables */
+ sym_list = inittab_load(ITAB_CAT_STANDARD|ITAB_CAT_FIELD|
+ ITAB_CAT_INTERNAL|ITAB_CAT_VENDOR,
+ ITAB_CONS_SERVER, &sym_num_items);
+ /*
+ * Allocate the internal INCLUDE_SYM macro include symbol.
+ * Since this is not part of inittab, it must be added
+ * manually to the list.
+ */
+ sym_list = (dhcp_symbol_t *)realloc(sym_list,
+ (sym_num_items + 1) * sizeof (dhcp_symbol_t));
+
+ if (sym_num_items == 0 || sym_list == NULL) {
+ dhcpmsg(LOG_ERR, "Cannot allocate inittab, exiting\n");
+ (void) exit(1);
+ }
+
+ (void) memset(&sym_list[sym_num_items], 0, sizeof (dhcp_symbol_t));
+ (void) strcpy(sym_list[sym_num_items].ds_name, INCLUDE_SYM);
+ sym_list[sym_num_items].ds_type = DSYM_INCLUDE;
+ sym_list[sym_num_items].ds_max = 32;
+ sym_num_items++;
+
+ /* Verify the inittab entries */
+ for (i = 0; i < sym_num_items; i++) {
+ if (inittab_verify(&sym_list[i], &tmp) == ITAB_FAILURE) {
+ print_error_msg(ITAB_SYNTAX_ERROR, i);
+ (void) memcpy(&sym_list[i], &tmp,
+ sizeof (dhcp_symbol_t));
+ }
+ }
+
+ mtable_closing = 0;
+ if (lck == B_TRUE)
+ (void) mutex_unlock(&mtable_mtx);
+}
+
+/*
+ * Given an value field pptr, return the first INCLUDE_SYM value found in
+ * include, updating pptr along the way. Returns nonzero if no INCLUDE_SYM
+ * symbol is found (pptr is still updated).
+ */
+static int
+scan_include(char **cpp, char *include)
+{
+ char t_sym[DSVC_MAX_MACSYM_LEN + 1];
+ uchar_t ilen;
+
+ while (*cpp && **cpp != '\0') {
+ eat_whitespace(cpp);
+ get_sym_name(t_sym, cpp);
+ if (strcmp(t_sym, INCLUDE_SYM) == 0) {
+ ilen = DHCP_SCRATCH;
+ if (**cpp == '=')
+ (*cpp)++;
+ (void) get_string(cpp, include, &ilen);
+ include[ilen] = '\0';
+ return (0);
+ } else
+ adjust(cpp);
+ }
+ return (1);
+}
+
+/*
+ * Return the first macro row in dhcptab. Returns -1 if no macros exist.
+ */
+static int
+first_macro_row(dt_rec_list_t **tblp)
+{
+ int i;
+
+ for (i = 0; i < nentries; i++) {
+ if (tolower(tblp[i]->dtl_rec->dt_type) == (int)DT_MACRO)
+ return (i);
+ }
+ return (-1);
+}
+
+/*
+ * RECURSIVE function: Scans for included macros, and reorders Tbl to
+ * ensure macro definitions occur in the correct order.
+ *
+ * Returns 0 for success, nonzero otherwise.
+ */
+static int
+check_includes(int first, char *mname)
+{
+ char include[DHCP_SCRATCH + 1];
+ int m, err = 0;
+ dt_rec_list_t *current_rowp = NULL;
+ char *cp;
+
+ include_nest++;
+
+ if (include_nest > MAX_MACRO_NESTING) {
+ dhcpmsg(LOG_ERR,
+ "Circular macro definition using: %s\n", mname);
+ err = -1;
+ goto leave_check_include;
+ }
+
+ for (m = first; m < nentries; m++) {
+ if (newtbl[m] != NULL &&
+ strcmp(newtbl[m]->dtl_rec->dt_key, mname) == 0) {
+ err = 0; /* already processed */
+ goto leave_check_include;
+ }
+ }
+
+ /*
+ * is it defined someplace?
+ */
+ for (m = first; m < nentries; m++) {
+ if (strcmp(oldtbl[m]->dtl_rec->dt_key, mname) == 0) {
+ current_rowp = oldtbl[m];
+ break;
+ }
+ }
+
+ if (current_rowp == NULL) {
+ dhcpmsg(LOG_ERR, "Undefined macro: %s\n", mname);
+ err = -1;
+ goto leave_check_include;
+ }
+
+ /*
+ * Scan value field, looking for includes.
+ */
+ cp = current_rowp->dtl_rec->dt_value;
+ while (cp) {
+ adjust(&cp);
+ if (scan_include(&cp, include) != 0) {
+ /* find a free entry */
+ for (m = first; m < nentries; m++) {
+ if (newtbl[m] == NULL)
+ break;
+ }
+ if (m >= nentries) {
+ dhcpmsg(LOG_ERR,
+ "Macro expansion (Include=%s) error!\n",
+ mname);
+ err = -1;
+ } else {
+ newtbl[m] = current_rowp;
+ err = 0;
+ }
+ break;
+ }
+
+ if (*include == '\0') {
+ /*
+ * Null value for macro name. We can safely ignore
+ * this entry. An error message will be generated
+ * later during encode processing.
+ */
+ continue;
+ }
+
+ if (strcmp(mname, include) == 0) {
+ dhcpmsg(LOG_ERR,
+ "Circular macro definition using: %s\n", mname);
+ err = -1;
+ break;
+ }
+
+ /* Recurse. */
+ if ((err = check_includes(first, include)) != 0)
+ break;
+ }
+
+leave_check_include:
+ include_nest--;
+ return (err);
+}
+
+/*
+ * open_macros: open reference to macro table.
+ */
+void
+open_macros(void) {
+ (void) mutex_lock(&mtable_mtx);
+ mtable_refcnt++;
+ (void) mutex_unlock(&mtable_mtx);
+}
+
+/*
+ * close_macros: close reference to macro table.
+ */
+void
+close_macros(void) {
+ (void) mutex_lock(&mtable_mtx);
+ mtable_refcnt--;
+ (void) cond_signal(&mtable_cv);
+ (void) mutex_unlock(&mtable_mtx);
+}
+
+/*
+ * Given a macro name, look it up in the hash table.
+ * Returns ptr to MACRO structure, NULL if error occurs.
+ */
+MACRO *
+get_macro(char *mnamep)
+{
+ if (mnamep == (char *)NULL)
+ return ((MACRO *)NULL);
+
+ return ((MACRO *)hash_Lookup(mtable, mnamep, strlen(mnamep), macro_cmp,
+ mnamep, B_FALSE));
+}
+
+/*ARGSUSED*/
+static boolean_t
+free_macro(MACRO *mp, boolean_t force)
+{
+ int i;
+
+ if (mp) {
+ free_encode_list(mp->head);
+ for (i = 0; i < mp->classes; i++) {
+ if (mp->list[i]->head != NULL)
+ free_encode_list(mp->list[i]->head);
+ free(mp->list[i]);
+ }
+ free(mp->list);
+ free(mp);
+ }
+ return (B_TRUE);
+}
+
+static int
+macro_cmp(MACRO *m1, MACRO *m2)
+{
+ if (!m1 || !m2)
+ return (B_FALSE);
+
+ if (strcmp(m1->nm, m2->nm) == 0)
+ return (B_TRUE);
+ else
+ return (B_FALSE);
+}
+
+/*
+ * Parse out all the various tags and parameters in the row entry pointed
+ * to by "src".
+ *
+ * Returns 0 for success, nozero otherwise.
+ */
+static MACRO *
+process_entry(dt_rec_list_t *src)
+{
+ char *cp;
+ MACRO *mc, *retval = NULL;
+
+ assert(src != NULL);
+
+ if (strlen(src->dtl_rec->dt_key) > DSVC_MAX_MACSYM_LEN) {
+ dhcpmsg(LOG_ERR,
+ "Token: %s is too long. Limit: %d characters.\n",
+ src->dtl_rec->dt_key, DSVC_MAX_MACSYM_LEN);
+ return (retval);
+ }
+
+ switch (tolower(src->dtl_rec->dt_type)) {
+ case DT_SYMBOL:
+ /* New Symbol definition */
+ cp = src->dtl_rec->dt_value;
+ if (!define_symbol(&cp, src->dtl_rec->dt_key))
+ dhcpmsg(LOG_ERR,
+ "Bad Runtime symbol definition: %s\n",
+ src->dtl_rec->dt_key);
+ /* Success. Treat new symbol like the predefines. */
+ break;
+ case DT_MACRO:
+ /* Macro definition */
+
+ mc = (MACRO *)smalloc(sizeof (MACRO));
+ (void) strcpy(mc->nm, src->dtl_rec->dt_key);
+
+ cp = src->dtl_rec->dt_value;
+ adjust(&cp);
+ while (*cp != '\0') {
+ if (eval_symbol(&cp, mc) != 0) {
+ dhcpmsg(LOG_ERR,
+ "Error processing macro: %s\n", mc->nm);
+ (void) free_macro(mc, B_TRUE);
+ return (NULL);
+ }
+ adjust(&cp);
+ eat_whitespace(&cp);
+ }
+ retval = mc;
+ break;
+ default:
+ dhcpmsg(LOG_ERR, "Unrecognized token: %s.\n",
+ src->dtl_rec->dt_key);
+ break;
+ }
+ return (retval);
+}
+
+/*
+ * This function processes the parameter name pointed to by "symbol" and
+ * updates the appropriate ENCODE structure in data if one already exists,
+ * or allocates a new one for this parameter.
+ */
+static int
+eval_symbol(char **symbol, MACRO *mc)
+{
+ int index, optype, i, j, err = 0;
+ dhcp_symbol_t *sp;
+ char **clp;
+ ENCODE *tmp;
+ VNDLIST **mpp, **ipp;
+ MACRO *ic;
+ char *cp;
+ uchar_t ilen;
+ uint16_t len;
+ char t_sym[DSVC_MAX_MACSYM_LEN + 1];
+ char include[DHCP_SCRATCH + 1];
+ /*
+ * The following buffer must be aligned on a int64_t boundary.
+ */
+ uint64_t scratch[(UCHAR_MAX + sizeof (int64_t) - 1) /
+ sizeof (int64_t)];
+
+ if ((*symbol)[0] == ':')
+ return (0);
+
+ eat_whitespace(symbol);
+ get_sym_name(t_sym, symbol);
+
+ for (index = 0; index < sym_num_items; index++) {
+ if (strcmp(t_sym, sym_list[index].ds_name) == 0)
+ break;
+ }
+ if (index >= sym_num_items) {
+ dhcpmsg(LOG_ERR, "Unrecognized symbol name: '%s'\n", t_sym);
+ return (-1);
+ } else {
+ sp = &sym_list[index];
+ clp = sp->ds_classes.dc_names;
+ }
+ /*
+ * Determine the type of operation to be done on this symbol
+ */
+ switch (**symbol) {
+ case '=':
+ optype = OP_ADDITION;
+ (*symbol)++;
+ break;
+ case '@':
+ optype = OP_DELETION;
+ (*symbol)++;
+ break;
+ case ':':
+ case '\0':
+ optype = OP_BOOLEAN;
+ break;
+ default:
+ dhcpmsg(LOG_ERR, "Syntax error: symbol: '%s' in macro: %s\n",
+ t_sym, mc->nm);
+ return (-1);
+ }
+
+ switch (optype) {
+ case OP_ADDITION:
+ switch (sp->ds_type) {
+ case DSYM_BOOL:
+ err = -1;
+ break;
+
+ case DSYM_INCLUDE:
+ /*
+ * If symbol type is INCLUDE, then walk the encode
+ * list, replacing any previous encodes with those
+ * from the INCLUDed macro. Vendor options are also
+ * merged, if their class and vendor codes match.
+ */
+ ilen = DHCP_SCRATCH;
+ (void) get_string(symbol, include, &ilen);
+ include[ilen] = '\0';
+ ic = get_macro(include);
+ if (ic == (MACRO *)NULL) {
+ dhcpmsg(LOG_ERR, "WARNING: No macro: '%1$s' \
+defined for 'Include' symbol in macro: %2$s\n",
+ include, mc->nm);
+ adjust(symbol);
+ return (0);
+ }
+
+ mc->head = combine_encodes(mc->head, ic->head,
+ ENC_DONT_COPY);
+
+ if (ic->list == NULL && mc->list == NULL)
+ break;
+
+ /* Vendor options. */
+ if (mc->list == NULL) {
+ /*
+ * No combining necessary. Just duplicate
+ * ic's vendor options - all classes.
+ */
+ mc->list = (VNDLIST **)smalloc(
+ sizeof (VNDLIST **) * ic->classes);
+ for (i = 0; i < ic->classes; i++) {
+ mc->list[i] = (VNDLIST *)smalloc(
+ sizeof (VNDLIST));
+ (void) strcpy(mc->list[i]->class,
+ ic->list[i]->class);
+ mc->list[i]->head = dup_encode_list(
+ ic->list[i]->head);
+ }
+ mc->classes = ic->classes;
+ } else {
+ /* Class and vendor code must match. */
+ for (i = 0, ipp = ic->list;
+ ipp && i < ic->classes; i++) {
+ for (j = 0, mpp = mc->list;
+ j < mc->classes; j++) {
+ if (strcmp(mpp[j]->class,
+ ipp[i]->class) == 0) {
+ mpp[j]->head =
+ combine_encodes(
+ mpp[j]->head,
+ ipp[i]->head,
+ ENC_DONT_COPY);
+ break;
+ }
+ }
+ }
+ }
+ break;
+
+ default:
+ /*
+ * Get encode associated with symbol value.
+ */
+ tmp = (ENCODE *)smalloc(sizeof (ENCODE));
+
+ if (sp->ds_type == DSYM_ASCII) {
+ if (sp->ds_max)
+ ilen = sp->ds_max;
+ else
+ ilen = UCHAR_MAX;
+ (void) get_string(symbol, (char *)scratch,
+ &ilen);
+ include[ilen] = '\0';
+
+ tmp->data = inittab_encode_e(sp,
+ (char *)scratch, &len, B_TRUE, &err);
+ } else {
+
+ if ((cp = strchr(*symbol, ':')) != NULL)
+ *cp = '\0';
+
+ tmp->data = inittab_encode_e(sp, *symbol, &len,
+ B_TRUE, &err);
+ /*
+ * Advance symbol pointer to next encode.
+ */
+ if (cp != NULL) {
+ *cp = ':';
+ *symbol = cp;
+ } else {
+ while (*symbol != '\0')
+ symbol++;
+ }
+ }
+
+ tmp->len = len;
+ tmp->category = sp->ds_category;
+ tmp->code = sp->ds_code;
+
+ if (err != 0 || tmp->data == NULL) {
+ if (err == 0)
+ err = -1;
+ free_encode(tmp);
+ } else {
+ /*
+ * Find/replace/add encode.
+ */
+ if (sp->ds_category != DSYM_VENDOR) {
+ replace_encode(&mc->head, tmp,
+ ENC_DONT_COPY);
+ } else
+ add_vndlist(tmp, mc, sp);
+ }
+ break;
+ }
+ break;
+
+ case OP_DELETION:
+ if (sp->ds_type == DSYM_INCLUDE)
+ return (-1);
+
+ if (sp->ds_category != DSYM_VENDOR) {
+ tmp = find_encode(mc->head, sp->ds_category,
+ sp->ds_code);
+ if (tmp != (ENCODE *)NULL) {
+ if (tmp->prev != (ENCODE *)NULL)
+ tmp->prev->next = tmp->next;
+ else
+ mc->head = mc->head->next;
+ free_encode(tmp);
+ }
+ } else {
+ for (i = 0; i < sp->ds_classes.dc_cnt; i++) {
+ for (j = 0; mc->list && j < mc->classes;
+ j++) {
+ if (strcmp(clp[i],
+ mc->list[j]->class) == 0) {
+ tmp = find_encode(
+ mc->list[j]->head,
+ sp->ds_category,
+ sp->ds_code);
+ if (tmp == NULL)
+ continue;
+ if (tmp->prev != NULL) {
+ tmp->prev->next =
+ tmp->next;
+ } else {
+ mc->list[j]->head =
+ mc->list[j]->
+ head->next;
+ }
+ free_encode(tmp);
+ }
+ }
+ }
+ }
+
+ err = 0;
+ break;
+
+ case OP_BOOLEAN:
+ if (sp->ds_type == DSYM_INCLUDE)
+ return (-1);
+ /*
+ * True signified by existence, false by omission.
+ */
+ if (sp->ds_category != DSYM_VENDOR) {
+ tmp = find_encode(mc->head, sp->ds_category,
+ sp->ds_code);
+ if (tmp == (ENCODE *)NULL) {
+ tmp = make_encode(sp->ds_category, sp->ds_code,
+ 0, NULL, ENC_DONT_COPY);
+ replace_encode(&mc->head, tmp,
+ ENC_DONT_COPY);
+ }
+ } else {
+ for (i = 0; i < sp->ds_classes.dc_cnt; i++) {
+ for (j = 0; mc->list && j < mc->classes;
+ j++) {
+ if (strcmp((const char *)clp[i],
+ mc->list[j]->class) == 0) {
+ tmp = find_encode(
+ mc->list[j]->head,
+ sp->ds_category,
+ sp->ds_code);
+ if (tmp == NULL) {
+ tmp = make_encode(
+ sp->ds_category,
+ sp->ds_code, 0,
+ NULL,
+ ENC_DONT_COPY);
+ replace_encode(
+ &mc->list[j]->
+ head, tmp,
+ ENC_DONT_COPY);
+ }
+ }
+ }
+ }
+ }
+
+ err = 0;
+ break;
+ }
+ if (err)
+ print_error_msg(ITAB_SYNTAX_ERROR, index);
+ return (err);
+}
+
+/*
+ * Find/add option to appropriate client classes.
+ */
+static void
+add_vndlist(ENCODE *vp, MACRO *mp, dhcp_symbol_t *sp)
+{
+ int i, j, class_exists, copy;
+ VNDLIST **tmp;
+ char **cp = sp->ds_classes.dc_names;
+
+ copy = ENC_DONT_COPY;
+ for (i = 0; i < sp->ds_classes.dc_cnt; i++) {
+ class_exists = 0;
+ for (j = 0; mp->list && j < mp->classes; j++) {
+ if (strcmp(cp[i], mp->list[j]->class) == 0) {
+ class_exists = 1;
+ replace_encode(&mp->list[j]->head, vp, copy);
+ if (copy == ENC_DONT_COPY)
+ copy = ENC_COPY;
+ }
+ }
+ if (!class_exists) {
+ tmp = (VNDLIST **)realloc(mp->list,
+ sizeof (VNDLIST **) * (j + 1));
+ if (tmp != NULL)
+ mp->list = tmp;
+ else {
+ dhcpmsg(LOG_ERR, "Warning: ran out of \
+memory adding vendor class: '%1$s' for symbol: '%2$s'\n",
+ cp[i], sp->ds_name);
+ break;
+ }
+ mp->list[j] = (VNDLIST *)smalloc(sizeof (VNDLIST));
+ (void) strcpy(mp->list[j]->class, cp[i]);
+ if (copy == ENC_DONT_COPY) {
+ mp->list[j]->head = vp;
+ copy = ENC_COPY;
+ } else
+ mp->list[j]->head = dup_encode(vp);
+ mp->classes++;
+ }
+ }
+}
+
+/*
+ * CMU 2.2 routine.
+ *
+ * Read a string from the buffer indirectly pointed to through "src" and
+ * move it into the buffer pointed to by "dest". A pointer to the maximum
+ * allowable length of the string (including null-terminator) is passed as
+ * "length". The actual length of the string which was read is returned in
+ * the unsigned integer pointed to by "length". This value is the same as
+ * that which would be returned by applying the strlen() function on the
+ * destination string (i.e the terminating null is not counted as a
+ * character). Trailing whitespace is removed from the string. For
+ * convenience, the function returns the new value of "dest".
+ *
+ * The string is read until the maximum number of characters, an unquoted
+ * colon (:), or a null character is read. The return string in "dest" is
+ * null-terminated.
+ */
+static char *
+get_string(char **src, char *dest, uchar_t *length)
+{
+ int n = 0, len, quoteflag;
+
+ quoteflag = B_FALSE;
+ len = *length - 1;
+ while ((n < len) && (**src)) {
+ if (quoteflag == B_FALSE && (**src == ':'))
+ break;
+ if (**src == '"') {
+ (*src)++;
+ quoteflag = !quoteflag;
+ continue;
+ }
+ if (**src == '\\') {
+ (*src)++;
+ if (!**src)
+ break;
+ }
+ *dest++ = *(*src)++;
+ n++;
+ }
+
+ /*
+ * Remove that troublesome trailing whitespace. . .
+ */
+ while ((n > 0) && isspace(*(char *)(dest - 1))) {
+ dest--;
+ n--;
+ }
+
+ *dest = '\0';
+ *length = n;
+ return (dest);
+}
+
+/*
+ * This function adjusts the caller's pointer to point just past the
+ * first-encountered colon. If it runs into a null character, it leaves
+ * the pointer pointing to it.
+ */
+static void
+adjust(char **s)
+{
+ char *t;
+
+ t = *s;
+ while (*t && (*t != ':'))
+ t++;
+
+ if (*t)
+ t++;
+ *s = t;
+}
+
+/*
+ * This function adjusts the caller's pointer to point to the first
+ * non-whitespace character. If it runs into a null character, it leaves
+ * the pointer pointing to it.
+ */
+static void
+eat_whitespace(char **s)
+{
+ char *t;
+
+ t = *s;
+ while (*t && isspace(*t))
+ t++;
+ *s = t;
+}
+
+/*
+ * Copy symbol name into buffer. Sym ends up pointing to the end of the
+ * token.
+ */
+static void
+get_sym_name(char *buf, char **sym)
+{
+ int i;
+
+ for (i = 0; i < DSVC_MAX_MACSYM_LEN; i++) {
+ if (**sym == ':' || **sym == '=' || **sym == '@' ||
+ **sym == '\0')
+ break;
+ *buf++ = *(*sym)++;
+ }
+ *buf = '\0';
+}
+
+static void
+print_error_msg(int error, uchar_t index)
+{
+ switch (error) {
+ case ITAB_BAD_IPADDR:
+ dhcpmsg(LOG_ERR, "Error processing Internet address \
+value(s) for symbol: '%s'\n", sym_list[index].ds_name);
+ break;
+ case ITAB_BAD_STRING:
+ dhcpmsg(LOG_ERR, "Error processing ASCII string value for \
+symbol: '%s'\n", sym_list[index].ds_name);
+ break;
+ case ITAB_BAD_OCTET:
+ dhcpmsg(LOG_ERR, "Error processing OCTET string value for \
+symbol: '%s'\n", sym_list[index].ds_name);
+ break;
+ case ITAB_BAD_NUMBER:
+ dhcpmsg(LOG_ERR, "Error processing NUMBER value for \
+symbol: '%s'\n", sym_list[index].ds_name);
+ break;
+ case ITAB_BAD_BOOLEAN:
+ dhcpmsg(LOG_ERR,
+ "Error processing BOOLEAN value for symbol: '%s'\n",
+ sym_list[index].ds_name);
+ break;
+ case ITAB_SYNTAX_ERROR:
+ /* FALLTHRU */
+ default:
+ dhcpmsg(LOG_ERR,
+ "Syntax error found processing value for symbol: '%s'\n",
+ sym_list[index].ds_name);
+ break;
+ }
+}
+
+/*
+ * Define new symbols for things like site-wide and vendor options.
+ */
+static boolean_t
+define_symbol(char **ptr, char *name)
+{
+
+ dhcp_symbol_t sym;
+ char **fields;
+ int last = 0;
+ dsym_errcode_t ret = DSYM_SUCCESS;
+ ushort_t min;
+ ushort_t max;
+ int i;
+
+ /*
+ * Only permit new symbol definitions, not old ones. I suppose we
+ * could allow the administrator to redefine symbols, but what if
+ * they redefine subnetmask to be a new brownie recipe? Let's stay
+ * out of that rat hole for now.
+ */
+ for (i = 0; i < sym_num_items; i++) {
+ if (strcmp(name, sym_list[i].ds_name) == 0) {
+ dhcpmsg(LOG_ERR, "Symbol: %s already defined. New "
+ "definition ignored.\n", name);
+ adjust(ptr);
+ return (0);
+ }
+ }
+
+ ret = dsym_init_parser(name, *ptr, &fields, &sym);
+ if (ret != DSYM_SUCCESS) {
+ switch (ret) {
+ case DSYM_NULL_FIELD:
+ dhcpmsg(LOG_ERR,
+ "Item is missing in symbol definition: '%s'\n",
+ name);
+ break;
+
+ case DSYM_TOO_MANY_FIELDS:
+ dhcpmsg(LOG_ERR,
+ "Too many items exist in symbol definition: %s\n",
+ name);
+ break;
+ case DSYM_NO_MEMORY:
+ dhcpmsg(LOG_ERR,
+ "Ran out of memory processing symbol: '%s'\n",
+ name);
+ break;
+ default:
+ dhcpmsg(LOG_ERR,
+ "Internal error processing symbol: '%s'\n",
+ name);
+ break;
+
+ }
+ return (B_FALSE);
+ }
+
+ ret = dsym_parser(fields, &sym, &last, B_FALSE);
+ if (ret != DSYM_SUCCESS) {
+ switch (ret) {
+ case DSYM_SYNTAX_ERROR:
+ dhcpmsg(LOG_ERR,
+ "Syntax error parsing symbol definition: '%s'\n",
+ name);
+ break;
+
+ case DSYM_CODE_OUT_OF_RANGE:
+ (void) dsym_get_code_ranges(fields[DSYM_CAT_FIELD],
+ &min, &max, B_TRUE);
+ dhcpmsg(LOG_ERR, "Out of range (%d-%d) option code: "
+ "%d in symbol definition: '%s'\n",
+ min, max, sym.ds_code, name);
+ break;
+
+ case DSYM_VALUE_OUT_OF_RANGE:
+ dhcpmsg(LOG_ERR,
+ "Bad item, %s, in symbol definition: '%s'\n",
+ fields[last], name);
+ break;
+
+ case DSYM_INVALID_CAT:
+ dhcpmsg(LOG_ERR, "Missing/Incorrect Site/Vendor flag "
+ "in symbol definition: '%s'\n", name);
+ break;
+
+ case DSYM_INVALID_TYPE:
+ dhcpmsg(LOG_ERR, "Unrecognized value descriptor: %s "
+ "in symbol definition: '%s'\n",
+ fields[DSYM_TYPE_FIELD], name);
+ break;
+
+ case DSYM_EXCEEDS_CLASS_SIZE:
+ dhcpmsg(LOG_ERR, "Client class is too "
+ "long for vendor symbol: '%s'. Must be "
+ "less than: %d\n", name, DSYM_CLASS_SIZE);
+ break;
+
+ case DSYM_EXCEEDS_MAX_CLASS_SIZE:
+ dhcpmsg(LOG_ERR, "Client class is too long for "
+ "vendor symbol: '%s'. Must be less than: %d\n",
+ name, DSYM_MAX_CLASS_SIZE);
+ break;
+
+ case DSYM_NO_MEMORY:
+ dhcpmsg(LOG_ERR,
+ "Ran out of memory processing symbol: '%s'\n",
+ name);
+ break;
+
+ default:
+ dhcpmsg(LOG_ERR,
+ "Internal error processing symbol: '%s'\n",
+ name);
+ break;
+ }
+ dsym_close_parser(fields, &sym);
+ return (B_FALSE);
+ }
+
+ /*
+ * Don't free the symbol structure resources, we need those.
+ * Just free the fields memory. We will free the symbol structure
+ * resources later.
+ */
+ dsym_free_fields(fields);
+
+ /*
+ * Now add it to the existing definitions, reallocating
+ * the dynamic symbol list.
+ */
+ sym_list = (dhcp_symbol_t *)realloc(sym_list,
+ (sym_num_items + 1) * sizeof (dhcp_symbol_t));
+ if (sym_list != (dhcp_symbol_t *)NULL) {
+ sym_num_items++;
+ (void) memcpy(&sym_list[sym_num_items - 1], &sym,
+ sizeof (dhcp_symbol_t));
+ } else {
+ dhcpmsg(LOG_ERR,
+ "Cannot extend symbol table, using predefined table.\n");
+ resettab(B_FALSE);
+ }
+
+ return (B_TRUE);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/encode.c b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/encode.c
new file mode 100644
index 0000000000..e25e6849fd
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/encode.c
@@ -0,0 +1,248 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <assert.h>
+#include <string.h>
+#include <sys/syslog.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netinet/dhcp.h>
+#include "hash.h"
+#include "dhcpd.h"
+#include "per_dnet.h"
+
+/*
+ * This file contains the code which creates, manipulates, and frees encode
+ * structures.
+ */
+
+/*
+ * Free an individual encode structure, including data.
+ */
+void
+free_encode(ENCODE *ecp)
+{
+ if (ecp != NULL) {
+ if (ecp->data)
+ free(ecp->data);
+ free(ecp);
+ }
+}
+
+/*
+ * Dump an entire encode list, including data.
+ */
+void
+free_encode_list(ENCODE *ecp)
+{
+ ENCODE *tmp;
+
+ while (ecp != NULL) {
+ tmp = ecp;
+ ecp = ecp->next;
+ free_encode(tmp);
+ }
+}
+
+/*
+ * Allocate an ENCODE structure, and fill it in with the passed data.
+ *
+ * Doesn't copy data if copy_flag is not set.
+ *
+ * Returns: ptr for success. Doesn't return if a failure occurs.
+ */
+ENCODE *
+make_encode(uchar_t cat, ushort_t code, uchar_t len, void *data,
+ int copy_flag)
+{
+ ENCODE *ecp;
+
+ ecp = (ENCODE *)smalloc(sizeof (ENCODE));
+
+ ecp->category = cat;
+ ecp->code = code;
+ ecp->len = len;
+
+ if (data != NULL && len != 0) {
+ if (copy_flag == ENC_COPY) {
+ ecp->data = (uchar_t *)smalloc(len);
+ (void) memcpy(ecp->data, data, len);
+ } else
+ ecp->data = data;
+ }
+ return (ecp);
+}
+
+/*
+ * Find a specific code in the ENCODE list. Doesn't consider class.
+ *
+ * Returns: ptr if successful, NULL otherwise.
+ */
+ENCODE *
+find_encode(ENCODE *eclp, uchar_t cat, ushort_t code)
+{
+ for (; eclp != NULL; eclp = eclp->next) {
+ if (eclp->category == cat && eclp->code == code)
+ return (eclp);
+ }
+ return (NULL);
+}
+
+/*
+ * Duplicate the passed encode structure.
+ */
+ENCODE *
+dup_encode(ENCODE *ecp)
+{
+ assert(ecp != NULL);
+ return (make_encode(ecp->category, ecp->code, ecp->len, ecp->data,
+ ENC_COPY));
+}
+
+/*
+ * Duplicate an encode list. May be called with NULL as a convenience.
+ */
+ENCODE *
+dup_encode_list(ENCODE *ecp)
+{
+ ENCODE *pp, *np, *headp;
+
+ if (ecp == NULL)
+ return (NULL);
+
+ /*
+ * Note: pp/np are used as placeholders in parallel list.
+ */
+ pp = headp = NULL;
+ for (; ecp != NULL; ecp = ecp->next) {
+ np = dup_encode(ecp);
+ if (pp == NULL) {
+ headp = np;
+ np->prev = NULL;
+ } else {
+ pp->next = np;
+ np->prev = pp;
+ }
+ pp = np;
+ }
+ return (headp);
+}
+
+/*
+ * Given two ENCODE lists, produce NEW ENCODE list by "OR"ing the first
+ * encode list with the second. Note that the settings in the second encode
+ * list override any identical code settings in the first encode list.
+ *
+ * The primary list is copied if flags argument is ENC_COPY. Class is not
+ * considered.
+ *
+ * Returns a ptr to the merged list for success, NULL ptr otherwise.
+ */
+ENCODE *
+combine_encodes(ENCODE *first_ecp, ENCODE *second_ecp, int flags)
+{
+ ENCODE *ep;
+
+ if (first_ecp != NULL) {
+ if (flags == ENC_COPY)
+ first_ecp = dup_encode_list(first_ecp);
+
+ for (ep = second_ecp; ep != NULL; ep = ep->next)
+ replace_encode(&first_ecp, ep, ENC_COPY);
+ } else {
+ first_ecp = dup_encode_list(second_ecp);
+ }
+ return (first_ecp);
+}
+
+/*
+ * Replace/add the encode matching the code value of the second ENCODE
+ * parameter in the list represented by the first ENCODE parameter.
+ */
+void
+replace_encode(ENCODE **elistpp, ENCODE *rp, int flags)
+{
+ ENCODE *wp;
+
+ assert(elistpp != NULL && rp != NULL);
+
+ if (flags == ENC_COPY)
+ rp = dup_encode(rp);
+
+ if (*elistpp == NULL) {
+ *elistpp = rp;
+ return;
+ }
+ wp = find_encode(*elistpp, rp->category, rp->code);
+
+ if (wp == NULL) {
+ rp->next = *elistpp;
+ rp->next->prev = rp;
+ *elistpp = rp;
+ rp->prev = NULL;
+ } else {
+ if (wp->prev == NULL) {
+ rp->next = wp->next;
+ *elistpp = rp;
+ rp->prev = NULL;
+ } else {
+ rp->next = wp->next;
+ rp->prev = wp->prev;
+ wp->prev->next = rp;
+ }
+ if (wp->next != NULL)
+ wp->next->prev = rp;
+ free_encode(wp);
+ }
+}
+
+/*
+ * Given a MACRO and a class name, return the ENCODE list for
+ * that class name, or null if a ENCODE list by that class doesn't exist.
+ */
+ENCODE *
+vendor_encodes(MACRO *mp, char *class)
+{
+ VNDLIST **tvpp;
+ int i;
+
+ assert(mp != NULL && class != NULL);
+
+ for (tvpp = mp->list, i = 0; tvpp != NULL && i < mp->classes; i++) {
+ if (strcmp(tvpp[i]->class, class) == 0)
+ return (tvpp[i]->head);
+ }
+ return (NULL);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/generic.c b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/generic.c
new file mode 100644
index 0000000000..4f032bb2c9
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/generic.c
@@ -0,0 +1,668 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * This file contains routines that are shared between the DHCP server
+ * implementation and BOOTP server compatibility.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <alloca.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <sys/syslog.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <netinet/dhcp.h>
+#include <search.h>
+#include <dhcp_symbol.h>
+#include "dhcpd.h"
+#include "per_dnet.h"
+#include "interfaces.h"
+#include <locale.h>
+#include <resolv.h>
+
+/*
+ * Get the client id. Sets cid and len.
+ */
+void
+get_clnt_id(PKT_LIST *plp, uchar_t *cid, int cidlen, uchar_t *len)
+{
+ DHCP_OPT *optp = plp->opts[CD_CLIENT_ID];
+
+ /*
+ * If the DHCP client specified the client id option, use that,
+ * otherwise use the client's hardware type and hardware address.
+ */
+ if (plp->opts[CD_DHCP_TYPE] != NULL && optp != NULL) {
+ /* DHCP client w/ client id */
+ if (cidlen < optp->len)
+ *len = (uchar_t)cidlen;
+ else
+ *len = optp->len;
+ (void) memcpy(cid, optp->value, *len);
+ } else {
+ /* BOOTP client or DHCP client w/o client id. */
+ *cid++ = plp->pkt->htype;
+ *len = plp->pkt->hlen + 1;
+ if (cidlen < *len)
+ *len = cidlen;
+ (void) memcpy(cid, plp->pkt->chaddr, *len);
+ }
+}
+
+/*
+ * Return a string representing an ASCII version of the client_id.
+ */
+char *
+disp_cid(PKT_LIST *plp, char *bufp, int len)
+{
+ DHCP_OPT *optp = plp->opts[CD_CLIENT_ID];
+ uchar_t *cp;
+ uchar_t cplen;
+ uint_t tlen;
+
+ if (optp != (DHCP_OPT *)0) {
+ cp = optp->value;
+ cplen = optp->len;
+ } else {
+ cp = plp->pkt->chaddr;
+ cplen = plp->pkt->hlen;
+ }
+
+ tlen = len;
+ (void) octet_to_hexascii(cp, cplen, bufp, &tlen);
+ return (bufp);
+}
+
+/*
+ * Based on the contents of the PKT_LIST structure for an incoming
+ * packet, determine the net address and subnet mask identifying the
+ * dhcp-network database. This centralizes choices that were formerly
+ * made in the specific protocol routines.
+ */
+void
+determine_network(IF *ifp, PKT_LIST *plp, struct in_addr *netp,
+ struct in_addr *subp)
+{
+ /*
+ * For BOOTP, REQUEST, RELEASE, and INFORM packets, trust client's
+ * notion of IP address if ciaddr is set. Use it to figure out correct
+ * dhcp-network.
+ */
+ netp->s_addr = plp->pkt->ciaddr.s_addr;
+ if (netp->s_addr != htonl(INADDR_ANY) &&
+ (plp->opts[CD_DHCP_TYPE] == NULL ||
+ (*plp->opts[CD_DHCP_TYPE]->value == REQUEST ||
+ *plp->opts[CD_DHCP_TYPE]->value == RELEASE ||
+ *plp->opts[CD_DHCP_TYPE]->value == INFORM))) {
+ /*
+ * Calculate client's default net mask, consult netmasks
+ * database to see if net is further subnetted. Use resulting
+ * subnet mask with client's address to produce dhcp-network
+ * database name.
+ */
+ get_netmask(netp, subp);
+ } else
+ netp->s_addr = htonl(INADDR_ANY);
+
+ /*
+ * If no trusted IP address, examine giaddr.
+ */
+ if (netp->s_addr == htonl(INADDR_ANY)) {
+ if (plp->pkt->giaddr.s_addr != htonl(INADDR_ANY)) {
+ netp->s_addr = plp->pkt->giaddr.s_addr;
+ /*
+ * Packet received thru a relay agent. Calculate the
+ * net's address using subnet mask and giaddr.
+ */
+ get_netmask(netp, subp);
+ } else {
+ /* Locally connected net. */
+ netp->s_addr = ifp->addr.s_addr;
+ subp->s_addr = ifp->mask.s_addr;
+ }
+ }
+}
+
+struct netmask_node;
+
+typedef struct netmask_node {
+ struct in_addr net; /* cached network */
+ struct in_addr mask; /* cached netmask */
+} NNODE;
+
+static void *nroot; /* root of netmask tree */
+static time_t nroot_mtime; /* time for dynamic free */
+static time_t nroot_stamp; /* time for dynamic free */
+static rwlock_t nroot_rwlock; /* synchronization variable */
+
+/*
+ * nm_cmp() - determine whether key n1 is within range of net/mask n2
+ */
+static int
+nm_cmp(const void *n1, const void *n2)
+{
+ void *v1 = (void *) (((NNODE *)n1)->net.s_addr &
+ ((NNODE *)n2)->mask.s_addr);
+ void *v2 = (void *) ((NNODE *)n2)->net.s_addr;
+
+ return (memcmp(&v1, &v2, sizeof (struct in_addr)));
+}
+
+/*
+ * Given a network-order address, calculate client's default net mask.
+ * Consult local cache, then netmasks database to see if net is further
+ * subnetted. We'll only snag the first netmask that matches our criteria.
+ */
+void
+get_netmask(struct in_addr *n_addrp, struct in_addr *s_addrp)
+{
+ NNODE key;
+ NNODE *node;
+ NNODE **ret;
+ struct in_addr haddr;
+
+ assert(n_addrp != NULL && s_addrp != NULL);
+
+ /*
+ * First check locally maintained, incomplete cache.
+ */
+ (void) rw_rdlock(&nroot_rwlock);
+ if (nroot != NULL) {
+ /* Delete expired tree. */
+ if (nroot_mtime != reinit_time || nroot_stamp < time(NULL)) {
+ (void) rw_unlock(&nroot_rwlock);
+ (void) rw_wrlock(&nroot_rwlock);
+ while ((ret = (NNODE **)nroot) != NULL) {
+ node = *ret;
+ (void) tdelete(node, &nroot, nm_cmp);
+ free(node);
+ }
+ nroot_mtime = reinit_time;
+ nroot_stamp = time(NULL) + DHCP_NSS_TIME;
+ } else {
+ key.net.s_addr = ntohl(n_addrp->s_addr);
+ key.mask.s_addr = INADDR_ANY;
+ if ((ret = (NNODE **)tfind((void *)&key,
+ (void * const *)&nroot, nm_cmp)) != NULL) {
+ s_addrp->s_addr = htonl((*ret)->mask.s_addr);
+ (void) rw_unlock(&nroot_rwlock);
+ return;
+ }
+ }
+ }
+
+ /*
+ * Note: workaround for 4336124: single-thread access to
+ * nss search routines to avoid getting incorrect results.
+ */
+ node = (NNODE *)smalloc(sizeof (NNODE));
+
+ /* Convert to and from host order. */
+ haddr.s_addr = ntohl(n_addrp->s_addr);
+ get_netmask4(&haddr, s_addrp);
+ node->mask.s_addr = s_addrp->s_addr;
+ node->net.s_addr = haddr.s_addr & node->mask.s_addr;
+ s_addrp->s_addr = htonl(s_addrp->s_addr);
+
+ /* While inserting check that another insert has not occurred. */
+ ret = (NNODE **)tsearch((void *)node, &nroot, nm_cmp);
+ if (ret != NULL && *ret != node)
+ free(node);
+
+ (void) rw_unlock(&nroot_rwlock);
+}
+
+/*
+ * This function is charged with loading the options field with the
+ * configured and/or asked for options. Note that if the packet is too
+ * small to fit the options, then option overload is enabled.
+ *
+ * Note that the caller is expected to free any allocated ENCODE lists,
+ * with the exception of locally-allocated lists in the case where ecp is
+ * NULL, but vecp is not. In this case, the resultant ecp list (ecp == tvep)
+ * is freed locally.
+ *
+ * Returns: The actual size of the utilized packet buffer.
+ */
+
+int
+load_options(int flags, PKT_LIST *c_plp, PKT *r_pktp, int replen, uchar_t *optp,
+ ENCODE *ecp, ENCODE *vecp)
+{
+ ENCODE *ep, *prevep, *tvep = NULL;
+ ENCODE *router_ecp = NULL;
+ PKT *c_pktp = c_plp->pkt;
+ uchar_t cat;
+ ushort_t code;
+ uint_t vend_len;
+ uchar_t len, *vp, *vdata, *data, *endp, *main_optp, *opt_endp;
+ uchar_t overload = DHCP_OVRLD_CLR,
+ using_overload = DHCP_OVRLD_CLR;
+ boolean_t srv_using_file = B_FALSE, clnt_ovrld_file = B_FALSE;
+ boolean_t echo_clnt_file;
+
+ if (c_plp->opts[CD_OPTION_OVERLOAD] != NULL &&
+ *c_plp->opts[CD_OPTION_OVERLOAD]->value & DHCP_OVRLD_FILE)
+ clnt_ovrld_file = B_TRUE;
+
+ opt_endp = (uchar_t *)((uint_t)r_pktp->options + replen -
+ BASE_PKT_SIZE);
+ endp = opt_endp;
+
+ /*
+ * We handle vendor options by fabricating an ENCODE of type
+ * CD_VENDOR_SPEC, and setting its datafield equal to vecp.
+ *
+ * We assume we've been handed the proper class list.
+ */
+ if (vecp != NULL && (flags & DHCP_NON_RFC1048) == 0) {
+ vend_len = 0;
+ for (ep = vecp, vend_len = 0; ep != NULL; ep = ep->next)
+ vend_len += (ep->len + 2);
+
+ if (vend_len != 0) {
+ if (vend_len > (uint_t)0xff) {
+ dhcpmsg(LOG_WARNING,
+ "Warning: Too much vendor data (> 255) to "
+ "encapsulate within option %d.\n",
+ CD_VENDOR_SPEC);
+ vend_len = (uint_t)0xff;
+ }
+ vdata = (uchar_t *)smalloc(vend_len);
+
+ for (vp = vdata, tvep = vecp; tvep != NULL &&
+ (uchar_t *)(vp + tvep->len + 2) <= &vdata[vend_len];
+ tvep = tvep->next) {
+ *vp++ = tvep->code;
+ *vp++ = tvep->len;
+ (void) memcpy(vp, tvep->data, tvep->len);
+ vp += tvep->len;
+ }
+
+ /* this make_encode *doesn't* copy data */
+ tvep = make_encode(DSYM_VENDOR, CD_VENDOR_SPEC,
+ vend_len, vdata, ENC_DONT_COPY);
+
+ /* Tack it on the end of standard list. */
+ for (ep = prevep = ecp; ep != NULL; ep = ep->next)
+ prevep = ep;
+ if (prevep != NULL)
+ prevep->next = tvep;
+ else
+ ecp = tvep;
+ }
+ }
+
+ /*
+ * Scan the options first to determine if we could potentially
+ * option overload.
+ */
+ if (flags & DHCP_DHCP_CLNT) {
+ for (ep = ecp; ep != NULL; ep = ep->next) {
+ if (ep->category == DSYM_FIELD)
+ switch (ep->code) {
+ case CD_SNAME:
+ overload |= DHCP_OVRLD_SNAME;
+ break;
+ case CD_BOOTFILE:
+ overload |= DHCP_OVRLD_FILE;
+ srv_using_file = B_TRUE;
+ break;
+ }
+ }
+ } else
+ overload = ~DHCP_OVRLD_MASK; /* No overload for BOOTP */
+
+ if (c_pktp->file[0] != '\0' && !clnt_ovrld_file && !srv_using_file) {
+ /*
+ * simply echo back client's boot file, and don't overload.
+ * if CD_BOOTPATH is set, we'll simply rewrite the r_pktp
+ * file field to include it along with the client's requested
+ * name during the load pass through the internal options.
+ * Here we let the overload code know we're not to overload
+ * the file field.
+ */
+ (void) memcpy(r_pktp->file, c_pktp->file,
+ sizeof (r_pktp->file));
+ overload |= DHCP_OVRLD_FILE;
+ echo_clnt_file = B_TRUE;
+ } else
+ echo_clnt_file = B_FALSE;
+
+ /* Now actually load the options! */
+ for (ep = ecp; ep != NULL; ep = ep->next) {
+ cat = ep->category;
+ code = ep->code;
+ len = ep->len;
+ data = ep->data;
+
+ /*
+ * non rfc1048 clients can only get packet fields and
+ * the CD_BOOTPATH internal pseudo opt, which only potentially
+ * affects the file field.
+ */
+ if ((flags & DHCP_NON_RFC1048) &&
+ !(cat == DSYM_FIELD || (cat == DSYM_INTERNAL &&
+ code == CD_BOOTPATH))) {
+ continue;
+ }
+
+ if ((flags & DHCP_SEND_LEASE) == 0 &&
+ cat == DSYM_STANDARD &&
+ (code == CD_T1_TIME || code == CD_T2_TIME ||
+ code == CD_LEASE_TIME)) {
+ continue;
+ }
+
+ /* standard and site options */
+ if (cat == DSYM_STANDARD || cat == DSYM_SITE ||
+ cat == DSYM_VENDOR) {
+
+ uchar_t *need_optp;
+
+ /*
+ * This horrible kludge is necessary because the DHCP
+ * options RFCs require that the subnet option MUST
+ * precede the router option. To accomplish this, we
+ *
+ * inspect each of the standard options, waiting
+ * for CD_ROUTER to turn up (if it never does,
+ * no special handling is needed)
+ *
+ * search the remaining options for CD_SUBNETMASK
+ * If it occurs, we
+ * set router_ecp to indicate where to find
+ * the router option's values that we have
+ * not yet emitted
+ *
+ * reinitialize code, len, and data to emit
+ * the CD_SUBNETMASK option now
+ *
+ * when CD_SUBNETMASK is encountered, we
+ * reinitialize code, len, and data to emit
+ * the CD_ROUTER option
+ */
+ if ((cat == DSYM_STANDARD) && (code == CD_ROUTER)) {
+ ENCODE *tp;
+
+ for (tp = ep->next; tp != NULL; tp = tp->next)
+ if ((tp->category == DSYM_STANDARD) &&
+ (tp->code == CD_SUBNETMASK)) {
+ router_ecp = ep;
+ code = CD_SUBNETMASK;
+ len = tp->len;
+ data = tp->data;
+ }
+ } else if ((cat == DSYM_STANDARD) &&
+ (code == CD_SUBNETMASK) && (router_ecp != NULL)) {
+ code = CD_ROUTER;
+ len = router_ecp->len;
+ data = router_ecp->data;
+ }
+
+ /*
+ * Keep an eye on option field. Option overload. Note
+ * that we need to keep track of the space necessary
+ * to place the Overload option in the options section
+ * (that's the 3 octets below.) The 2 octets cover the
+ * necessary code and len portion of the payload.
+ */
+ if (using_overload == DHCP_OVRLD_CLR) {
+ /* 2 for code/len, 3 for overload option */
+ need_optp = &optp[len + 2 + 3];
+ } else {
+ /* Just need 2 for code/len */
+ need_optp = &optp[len + 2];
+ }
+ if (need_optp > endp) {
+ /*
+ * If overload is not possible, we will
+ * keep going, hoping to find an option
+ * that will fit in the remaining space,
+ * rather than just give up.
+ */
+ if (overload != (uchar_t)~DHCP_OVRLD_MASK) {
+ if (using_overload == DHCP_OVRLD_CLR) {
+ *optp++ = CD_OPTION_OVERLOAD;
+ *optp++ = 1;
+ main_optp = optp;
+ } else {
+ if (optp < endp)
+ *optp = CD_END;
+ overload |= using_overload;
+ }
+ }
+ switch (overload) {
+ case DHCP_OVRLD_CLR:
+ /* great, can use both */
+ /* FALLTHRU */
+ case DHCP_OVRLD_FILE:
+ /* Can use sname. */
+ optp = r_pktp->sname;
+ endp = r_pktp->file;
+ using_overload |= DHCP_OVRLD_SNAME;
+ break;
+ case DHCP_OVRLD_SNAME:
+ /* Using sname, can use file. */
+ optp = r_pktp->file;
+ endp = r_pktp->cookie;
+ using_overload |= DHCP_OVRLD_FILE;
+ break;
+ }
+ } else {
+ /* Load options. */
+ *optp++ = (uchar_t)code;
+ *optp++ = len;
+ (void) memcpy(optp, data, len);
+ optp += len;
+ }
+ } else if (cat == DSYM_FIELD) {
+ /* packet field pseudo options */
+ switch (code) {
+ case CD_SIADDR:
+ /*
+ * Configuration includes Boot server addr
+ */
+ (void) memcpy((void *)&r_pktp->siaddr, data,
+ len);
+ break;
+ case CD_SNAME:
+ /*
+ * Configuration includes Boot server name
+ */
+ (void) memcpy(r_pktp->sname, data, len);
+ break;
+ case CD_BOOTFILE:
+ /*
+ * Configuration includes boot file.
+ * Always authoritative.
+ */
+ (void) memset(r_pktp->file, 0,
+ sizeof (r_pktp->file));
+ (void) memcpy(r_pktp->file, data, len);
+ break;
+ default:
+ dhcpmsg(LOG_ERR,
+ "Unsettable DHCP packet field: %d\n", code);
+ break;
+ }
+ } else if (cat == DSYM_INTERNAL) {
+ /* Internal server pseudo options */
+ switch (code) {
+ case CD_BOOTPATH:
+ /*
+ * Prefix for boot file. Only used if
+ * client provides bootfile and server doesn't
+ * specify one. Prepended on client's bootfile
+ * value. Otherwise ignored.
+ */
+ if (echo_clnt_file) {
+ uchar_t alen, flen;
+
+ alen = sizeof (c_pktp->file);
+ flen = alen - 1;
+ if (c_pktp->file[flen] != '\0')
+ flen++;
+ else
+ flen = strlen(
+ (char *)c_pktp->file);
+
+ if ((len + flen + 1) > alen) {
+ char *bp = alloca(alen + 1);
+ char *bf = alloca(alen + 1);
+ (void) memcpy(bp, data, len);
+ bp[len] = '\0';
+ (void) memcpy(bf, c_pktp->file,
+ flen);
+ bf[flen] = '\0';
+ dhcpmsg(LOG_ERR,
+ "BootPath(%1$s) + "
+ "BootFile(%2$s) too "
+ "long: %3$d > %4$d\n",
+ bp, bf, (len + flen), alen);
+ } else {
+ (void) memcpy(r_pktp->file,
+ data, len);
+ r_pktp->file[len] = '/';
+ (void) memcpy(
+ &r_pktp->file[len + 1],
+ c_pktp->file, flen);
+ }
+ }
+ break;
+ case CD_BOOL_HOSTNAME:
+ /* FALLTHRU */
+ case CD_BOOL_LEASENEG:
+ /* FALLTHRU */
+ case CD_BOOL_ECHO_VCLASS:
+ /*
+ * These pseudo opts have had their
+ * affect elsewhere, such as dhcp.c.
+ */
+ break;
+ default:
+ dhcpmsg(LOG_ERR,
+ "Unknown Internal pseudo opt: %d\n", code);
+ break;
+ }
+ } else {
+ dhcpmsg(LOG_ERR,
+ "Unrecognized option with code: %d %d\n", cat,
+ code);
+ }
+ }
+
+ if (using_overload != DHCP_OVRLD_CLR) {
+ *main_optp++ = using_overload;
+ if (optp < endp)
+ *optp = CD_END;
+ } else
+ main_optp = optp; /* no overload */
+
+ if (main_optp < opt_endp)
+ *main_optp++ = CD_END;
+
+ if (ecp == tvep)
+ free_encode_list(ecp);
+
+ return (BASE_PKT_SIZE + (uint_t)(main_optp - r_pktp->options));
+}
+
+/*
+ * Reinitialize the dhcptab database, as a result of timeout or
+ * user signal. Note: if_head_mtx cannot be held by caller.
+ */
+void *
+reinitialize(void *arg)
+{
+ int totpkts;
+ IF *ifp;
+ thread_t *tp = (thread_t *)arg;
+ int err;
+
+ /*
+ * Got a signal to reinitialize
+ */
+
+ if (verbose)
+ dhcpmsg(LOG_INFO, "Reinitializing server\n");
+
+ if (!no_dhcptab) {
+ if (checktab() != 0) {
+ dhcpmsg(LOG_WARNING,
+ "WARNING: Cannot access dhcptab.\n");
+ } else {
+ if ((err = readtab(PRESERVE_DHCPTAB)) != 0) {
+ dhcpmsg(LOG_ERR,
+ "Error reading dhcptab.\n");
+ return ((void *)err);
+ }
+ }
+ }
+
+ /*
+ * Drop all pending offers, display interface statistics.
+ */
+ if (verbose) {
+ (void) mutex_lock(&if_head_mtx);
+ for (ifp = if_head, totpkts = 0; ifp != NULL; ifp = ifp->next) {
+ (void) mutex_lock(&ifp->ifp_mtx);
+ disp_if_stats(ifp);
+ totpkts += ifp->received;
+ (void) mutex_unlock(&ifp->ifp_mtx);
+ }
+ (void) mutex_unlock(&if_head_mtx);
+
+ dhcpmsg(LOG_INFO,
+ "Total Packets received on all interfaces: %d\n", totpkts);
+ dhcpmsg(LOG_INFO, "Server reinitialized.\n");
+ }
+
+ /* Default domain may have changed */
+ if (res_ninit(&resolv_conf) == -1)
+ dhcpmsg(LOG_ERR, "Cannot acquire resolver configuration.\n");
+
+ /* Release reinitialization thread */
+ reinit_time = time(NULL);
+ *tp = NULL;
+ thr_exit(NULL);
+
+ return (NULL);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/hash.c b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/hash.c
new file mode 100644
index 0000000000..0e44430bee
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/hash.c
@@ -0,0 +1,579 @@
+/*
+ * Copyright (c) 1993-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Copyright 1988, 1991 by Carnegie Mellon University
+ *
+ * All Rights Reserved
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of Carnegie Mellon University not be used
+ * in advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+ * IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+ * THIS SOFTWARE.
+ */
+
+/*
+ * Generalized hash table ADT
+ *
+ * Provides multiple, dynamically-allocated, variable-sized hash tables on
+ * various data and keys.
+ *
+ * This package attempts to follow some of the coding conventions suggested
+ * by Bob Sidebotham and the AFS Clean Code Committee of the
+ * Information Technology Center at Carnegie Mellon.
+ *
+ * Additions for per bucket locking, and configurable dynamic free of
+ * unused entries.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/sysmacros.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <assert.h>
+#include <synch.h>
+#include "dhcpd.h"
+#include "hash.h"
+
+/*
+ * Hash table size calculation routine.
+ *
+ * Estimate the size of a hash table based on the expected number of
+ * entries, up to a maximum of HASHTABLESIZE.
+ */
+static unsigned
+hashi_Hsize(unsigned hint)
+{
+ unsigned f;
+
+ if (hint == 0) /* Default size. */
+ hint = HASHTABLESIZE;
+ else if (hint < 16) /* Minimal size. */
+ hint = 16;
+
+ hint /= 4;
+ for (f = 2; f * f <= hint; f++) { /* Find next largest prime. */
+ if (hint % f == 0) {
+ f = 1;
+ hint++;
+ }
+ }
+ return (MIN(HASHTABLESIZE, hint));
+}
+
+/*
+ * Frees an entire linked list of bucket members (used in the
+ * open hashing scheme). Does nothing if the passed pointer is NULL.
+ *
+ * Returns B_FALSE and members which could not be freed in bucketptr, when
+ * force variable is set to B_FALSE, and free_data routine indicates
+ * free did not occur.
+ */
+static boolean_t
+hashi_FreeMember(hash_member **bucketptr, boolean_t (*free_data)(),
+ boolean_t force)
+{
+ hash_member *prev, *next, *unfree = NULL;
+ boolean_t ret = B_TRUE;
+
+ if (bucketptr) {
+ for (prev = *bucketptr; prev; prev = next) {
+ next = prev->next;
+ prev->next = NULL;
+ if (free_data != NULL) {
+ if ((*free_data)(prev->data, force) ==
+ B_FALSE) {
+ ret = B_FALSE;
+ prev->next = unfree;
+ unfree = prev;
+ } else {
+ free(prev);
+ }
+ } else
+ free(prev);
+ }
+ *bucketptr = unfree;
+ }
+ return (ret);
+}
+
+/*
+ * Dynamic free initialization.
+ */
+static void
+hashi_Dinit(hash_tbl *hashtable, hash_member *memberptr)
+{
+ (void) mutex_init(&memberptr->h_mtx, USYNC_THREAD, NULL);
+ memberptr->h_time = time(NULL) + hashtable->dfree_time;
+ memberptr->h_count = 1;
+}
+
+/*
+ * Dynamic free reference count increment.
+ */
+static void
+hashi_Dhold(hash_member *memberptr)
+{
+ (void) mutex_lock(&memberptr->h_mtx);
+ memberptr->h_count++;
+ (void) mutex_unlock(&memberptr->h_mtx);
+}
+
+/*
+ * Dynamic free expired data. Return NULL if memberptr is successfully
+ * dynamically freed, otherwise return memberptr.
+ */
+static hash_member *
+hashi_Dfree(hash_member *memberptr, boolean_t (*free_data)())
+{
+ hash_member *next;
+
+ next = memberptr->next;
+ memberptr->next = NULL;
+ if (hashi_FreeMember(&memberptr, free_data, B_FALSE) == B_TRUE)
+ memberptr = NULL;
+ else
+ memberptr->next = next;
+ return (memberptr);
+}
+
+/*
+ * Hash table initialization routine.
+ *
+ * This routine creates and intializes a hash table of size "tablesize"
+ * entries. Successful calls return a pointer to the hash table (which must
+ * be passed to other hash routines to identify the hash table). Failed
+ * calls return NULL.
+ */
+hash_tbl *
+hash_Init(unsigned tablesize, boolean_t (*dfree_data)(), time_t dtime,
+ boolean_t lck)
+{
+ hash_tbl *hashtblptr;
+ unsigned totalsize;
+ unsigned i;
+
+ tablesize = hashi_Hsize(tablesize);
+
+ totalsize = sizeof (hash_tbl) + (sizeof (hash_bucket) *
+ (tablesize - 1));
+
+ hashtblptr = (hash_tbl *)smalloc(totalsize);
+
+ hashtblptr->size = tablesize; /* Success! */
+ hashtblptr->bucketnum = 0;
+ hashtblptr->dfree_data = dfree_data;
+ hashtblptr->dfree_lck = lck;
+ hashtblptr->dfree_time = dtime;
+ hashtblptr->table = &hashtblptr->data[0];
+ for (i = 0; i < tablesize; i++) {
+ hashtblptr->table[i].table = hashtblptr;
+ if (lck == B_TRUE) {
+ (void) rwlock_init(&(hashtblptr->table[i].rwlock),
+ USYNC_THREAD, NULL);
+ }
+ }
+
+ return (hashtblptr); /* NULL if failure */
+}
+
+/*
+ * Generic hash function to calculate a hash code from the given string.
+ *
+ * For each byte of the string, this function left-shifts the value in an
+ * accumulator and then adds the byte into the accumulator. The contents of
+ * the accumulator is returned after the entire string has been processed.
+ * It is assumed that this result will be used as the "hashcode" parameter in
+ * calls to other functions in this package. These functions automatically
+ * adjust the hashcode for the size of each hashtable.
+ *
+ * This algorithm probably works best when the hash table size is a prime
+ * number.
+ *
+ * Hopefully, this function is better than the previous one which returned
+ * the sum of the squares of all the bytes. I'm still open to other
+ * suggestions for a default hash function. The programmer is more than
+ * welcome to supply his/her own hash function as that is one of the design
+ * features of this package.
+ */
+static unsigned
+hashi_HashFunction(unsigned char *string, unsigned len)
+{
+ unsigned accum;
+
+ /*
+ * Special case: allow hash_Delete() to iterate over buckets.
+ */
+ if (string == NULL)
+ return (len);
+
+ for (accum = 0; len != 0; len--) {
+ accum <<= 1;
+ accum += (unsigned)(*string++ & 0xFF);
+ }
+ return (accum);
+}
+
+/*
+ * This routine re-initializes the hash table. It frees all the allocated
+ * memory and resets all bucket pointers to NULL. For the macro hash
+ * table, the table will be reused. Other tables (with bucket locks)
+ * will be destroyed.
+ */
+void
+hash_Reset(hash_tbl *hashtable, boolean_t (*free_data)())
+{
+ hash_bucket *bucketptr;
+ unsigned i;
+
+ bucketptr = &((hashtable->table)[0]);
+ for (i = 0; i < hashtable->size; i++) {
+ if (hashtable->dfree_lck == B_TRUE)
+ (void) rw_wrlock(&bucketptr->rwlock);
+ /*
+ * Unequivocally free member, using the force parameter.
+ */
+ (void) hashi_FreeMember(&bucketptr->next, free_data, B_TRUE);
+ bucketptr->next = NULL;
+ if (hashtable->dfree_lck == B_TRUE) {
+ (void) rw_unlock(&bucketptr->rwlock);
+ (void) rwlock_destroy(&(bucketptr->rwlock));
+ }
+ bucketptr++;
+ }
+ hashtable->bucketnum = 0;
+}
+
+/*
+ * Returns B_TRUE if at least one entry for the given key exists; B_FALSE
+ * otherwise. Dynamically free expired data as searched.
+ */
+static int
+hashi_Exists(hash_bucket *bucketptr, int (*compare)(), hash_datum *key,
+ boolean_t (*free_data)(), hash_member **prev)
+{
+ hash_member *prevptr = (hash_member *)bucketptr;
+ hash_member *memberptr = bucketptr->next;
+ hash_tbl *hashtable = bucketptr->table;
+ hash_member *next;
+ boolean_t ret = B_FALSE;
+ time_t now = time(NULL);
+
+ while (memberptr != NULL) {
+ /*
+ * Dynamically free expired data.
+ */
+ if (free_data != NULL && hashtable->dfree_data != NULL &&
+ memberptr->h_time < now) {
+ next = memberptr->next;
+ if ((memberptr = hashi_Dfree(memberptr, free_data)) ==
+ NULL) {
+ prevptr->next = memberptr = next;
+ continue;
+ }
+ }
+
+ /*
+ * Entry exists, or we are randomly selecting any
+ * element (compare function is NULL).
+ */
+ if (compare == NULL || (*compare)(key, memberptr->data)) {
+ ret = B_TRUE;
+ break;
+ } else
+ prevptr = memberptr;
+ memberptr = memberptr->next;
+ }
+
+ if (prev != NULL)
+ *prev = prevptr;
+ return (ret);
+}
+
+/*
+ * Returns number of Dynamically freed expired entries.
+ */
+static int
+hashi_Expire(hash_bucket *bucketptr, boolean_t (*free_data)())
+{
+ hash_member *prevptr = (hash_member *)bucketptr;
+ hash_member *memberptr = bucketptr->next;
+ hash_tbl *hashtable = bucketptr->table;
+ hash_member *next;
+ int rcount = 0;
+ time_t now = time(NULL);
+
+ while (memberptr) {
+ /*
+ * Dynamically free expired data.
+ */
+ if (free_data != NULL && hashtable->dfree_data != NULL &&
+ memberptr->h_time < now) {
+ next = memberptr->next;
+ if ((memberptr = hashi_Dfree(memberptr, free_data)) ==
+ NULL) {
+ rcount++;
+ prevptr->next = memberptr = next;
+ continue;
+ }
+ }
+ prevptr = memberptr;
+ memberptr = memberptr->next;
+ }
+ return (rcount);
+}
+
+/*
+ * Insert the data item "element" into the hash table using "hashcode"
+ * to determine the bucket number, and "compare" and "key" to determine
+ * its uniqueness.
+ *
+ * If the insertion is successful the element is returned. If a matching entry
+ * already exists in the given bucket of the hash table, then NULL is returned,
+ * signifying that the entry is already in the table. This happens when some
+ * other thread has already inserted the entry.
+ */
+void *
+hash_Insert(hash_tbl *hashtable, void *hashdata, unsigned hashlen,
+ int (*compare)(), hash_datum *key, hash_datum *element)
+{
+ hash_member *temp = NULL;
+ hash_bucket *bucketptr;
+ hash_member *prev = NULL;
+ unsigned hashcode = hashi_HashFunction(hashdata, hashlen);
+
+ bucketptr = &((hashtable->table)[hashcode % hashtable->size]);
+ if (hashtable->dfree_lck)
+ (void) rw_wrlock(&bucketptr->rwlock);
+
+ if (hashi_Exists(bucketptr, compare, key, hashtable->dfree_data,
+ &prev)) {
+ /* Some other thread got there first, so just return */
+ if (hashtable->dfree_lck)
+ (void) rw_unlock(&bucketptr->rwlock);
+ return (NULL);
+ }
+
+ temp = (hash_member *)smalloc(sizeof (hash_member));
+
+ prev->next = temp;
+ temp->data = element;
+ temp->next = NULL;
+
+ /*
+ * Dynamic free initialization.
+ */
+ if (hashtable->dfree_data != NULL)
+ hashi_Dinit(hashtable, temp);
+
+ if (hashtable->dfree_lck)
+ (void) rw_unlock(&bucketptr->rwlock);
+
+ return ((void *)temp);
+}
+
+/*
+ * Release the reference count on an item. Performance: if item is to be
+ * deleted, mark for future dynamic free.
+ */
+void
+hash_Rele(void *hashp, boolean_t delete)
+{
+ hash_member *memberptr = (hash_member *)hashp;
+
+ (void) mutex_lock(&memberptr->h_mtx);
+ memberptr->h_count--;
+ assert(memberptr->h_count >= 0);
+ if (delete == B_TRUE)
+ memberptr->h_time = 0;
+ (void) mutex_unlock(&memberptr->h_mtx);
+}
+
+/*
+ * Report the reference count on an item.
+ */
+int
+hash_Refcount(void *hashp)
+{
+ hash_member *memberptr = (hash_member *)hashp;
+ int ret;
+
+ (void) mutex_lock(&memberptr->h_mtx);
+ ret = memberptr->h_count;
+ (void) mutex_unlock(&memberptr->h_mtx);
+ return (ret);
+}
+
+/*
+ * Report the dynamic free time on an item.
+ */
+int
+hash_Htime(void *hashp)
+{
+ hash_member *memberptr = (hash_member *)hashp;
+ int ret;
+
+ (void) mutex_lock(&memberptr->h_mtx);
+ ret = memberptr->h_time;
+ (void) mutex_unlock(&memberptr->h_mtx);
+ return (ret);
+}
+
+/*
+ * Increase the dynamic free time on an item.
+ */
+void
+hash_Age(void *hashp)
+{
+ hash_member *memberptr = (hash_member *)hashp;
+
+ (void) mutex_lock(&memberptr->h_mtx);
+ memberptr->h_time++;
+ (void) mutex_unlock(&memberptr->h_mtx);
+}
+
+/*
+ * Set the dynamic free time on an item.
+ */
+void
+hash_Dtime(void *hashp, time_t tm)
+{
+ hash_member *memberptr = (hash_member *)hashp;
+
+ (void) mutex_lock(&memberptr->h_mtx);
+ memberptr->h_time = tm;
+ (void) mutex_unlock(&memberptr->h_mtx);
+}
+
+/*
+ * Delete a data item from the hash table using "hashcode"
+ * to determine the bucket number, and "compare" and "key" to determine
+ * its uniqueness.
+ *
+ * If the deletion is successful 0 is returned. If a matching entry
+ * does not exist in the given bucket of the hash table, or some other error
+ * occurs, -1 is returned and the insertion is not done.
+ */
+boolean_t
+hash_Delete(hash_tbl *hashtable, void *hashdata, unsigned hashlen,
+ int (*compare)(), hash_datum *key, boolean_t (*free_data)())
+{
+ hash_member *prev = NULL;
+ hash_member *temp;
+ hash_bucket *bucketptr;
+ unsigned hashcode = hashi_HashFunction(hashdata, hashlen);
+
+ bucketptr = &((hashtable->table)[hashcode % hashtable->size]);
+ if (hashtable->dfree_lck == B_TRUE)
+ (void) rw_wrlock(&bucketptr->rwlock);
+
+ if (hashi_Exists(bucketptr, compare, key, free_data, &prev) ==
+ B_FALSE || prev == NULL) {
+ if (hashtable->dfree_lck == B_TRUE)
+ (void) rw_unlock(&bucketptr->rwlock);
+ return (B_FALSE); /* Entry does not exist */
+ }
+
+ temp = prev->next;
+ if (temp) {
+ prev->next = temp->next;
+ temp->next = NULL;
+ (void) hashi_FreeMember(&temp, free_data, B_TRUE);
+ } else
+ prev->next = NULL;
+ if (hashtable->dfree_lck == B_TRUE)
+ (void) rw_unlock(&bucketptr->rwlock);
+ return (B_TRUE);
+}
+
+/*
+ * Locate and return the data entry associated with the given key.
+ *
+ * If the data entry is found, a pointer to it is returned. Otherwise,
+ * NULL is returned.
+ */
+hash_datum *
+hash_Lookup(hash_tbl *hashtable, void *hashdata, unsigned hashlen,
+ int (*compare)(), hash_datum *key, boolean_t hold)
+{
+ hash_datum *ret = NULL;
+ hash_bucket *bucketptr;
+ hash_member *prev = NULL;
+ unsigned hashcode = hashi_HashFunction(hashdata, hashlen);
+
+ bucketptr = &((hashtable->table)[hashcode % hashtable->size]);
+ if (hashtable->dfree_lck == B_TRUE)
+ (void) rw_wrlock(&bucketptr->rwlock);
+
+ if (hashi_Exists(bucketptr, compare, key, hashtable->dfree_data,
+ &prev) == B_TRUE) {
+ /*
+ * Dynamic free increment reference.
+ */
+ if (hold)
+ hashi_Dhold(prev->next);
+ ret = prev->next->data;
+
+ }
+ if (hashtable->dfree_lck == B_TRUE)
+ (void) rw_unlock(&bucketptr->rwlock);
+ return (ret);
+}
+
+/*
+ * Reap expired data items, or a random data item from the hash table.
+ */
+void
+hash_Reap(hash_tbl *hashtable, boolean_t (*free_data)())
+{
+ hash_bucket *bucketptr;
+ int rcount;
+ unsigned i;
+
+ bucketptr = &((hashtable->table)[0]);
+ rcount = 0;
+
+ /*
+ * Walk the buckets, reaping expired clients.
+ */
+ for (i = 0; i < hashtable->size; i++) {
+ if (hashtable->dfree_lck == B_TRUE)
+ (void) rw_wrlock(&bucketptr->rwlock);
+ rcount += hashi_Expire(bucketptr, hashtable->dfree_data);
+ if (hashtable->dfree_lck == B_TRUE)
+ (void) rw_unlock(&bucketptr->rwlock);
+ bucketptr++;
+ }
+
+ /*
+ * Nothing to be reaped, delete a random element. Note that
+ * the unhash_data routine will wait for current references
+ * before deletion.
+ */
+ if (rcount == 0) {
+ for (i = 0; i < hashtable->size; i++) {
+ if (hash_Delete(hashtable, NULL, i, NULL, NULL,
+ free_data) == B_TRUE) {
+ break;
+ }
+ }
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/hash.h b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/hash.h
new file mode 100644
index 0000000000..6cd659a550
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/hash.h
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 1993-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+/*
+ * Copyright 1988, 1991 by Carnegie Mellon University
+ * All Rights Reserved
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of Carnegie Mellon University not be used
+ * in advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+ * IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+ * THIS SOFTWARE.
+ */
+
+#ifndef _HASH_H
+#define _HASH_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Generalized hash table ADT
+ *
+ * Provides multiple, dynamically-allocated, variable-sized hash tables on
+ * various data and keys.
+ *
+ * This package attempts to follow some of the coding conventions suggested
+ * by Bob Sidebotham and the AFS Clean Code Committee.
+ */
+
+/*
+ * The user must supply the following:
+ *
+ * 1. A comparison function which is declared as:
+ *
+ * int compare(data1, data2)
+ * hash_datum *data1, *data2;
+ *
+ * This function must compare the desired fields of data1 and
+ * data2 and return B_TRUE (1) if the data should be considered
+ * equivalent (i.e. have the same key value) or B_FALSE (0)
+ * otherwise. This function is called through a pointer passed to
+ * the various hashtable functions (thus pointers to different
+ * functions may be passed to effect different tests on different
+ * hash tables).
+ *
+ * Internally, all the functions of this package always call the
+ * compare function with the "key" parameter as the first parameter,
+ * and a full data element as the second parameter. Thus, the key
+ * and element arguments to functions such as hash_Lookup() may
+ * actually be of different types and the programmer may provide a
+ * compare function which compares the two different object types
+ * as desired.
+ *
+ * Example:
+ *
+ * int compare(key, element)
+ * char *key;
+ * struct some_complex_structure *element;
+ * {
+ * return !strcmp(key, element->name);
+ * }
+ *
+ * key = "John C. Doe"
+ * element = &some_complex_structure
+ * hash_Lookup(table, hashptr, hashlen, compare, key, free_rec, B_TRUE);
+ *
+ * 2. A hash function yielding an unsigned integer value to be used
+ * as the hashcode (index into the hashtable). Thus, the user
+ * may hash on whatever data is desired and may use several
+ * different hash functions for various different hash tables.
+ * The actual hash table index will be the passed hashcode modulo
+ * the hash table size.
+ *
+ * A generalized hash function, hash_HashFunction(), is included
+ * with this package to make things a little easier. It is not
+ * guarenteed to use the best hash algorithm in existence. . . .
+ *
+ * 3. An ability to garbage collect data has been added. Timed garbage
+ * collection of hash members is provided to relieve the interface and worker
+ * threads of explicit data structure management. Expired data structures
+ * are pruned during hash insertion and deletion, or by explicit calls
+ * to Delete and Reap functions.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Various hash table definitions
+ */
+
+/*
+ * Define "hash_datum" as a universal data type
+ */
+typedef void hash_datum;
+typedef void *hash_handle;
+
+typedef struct hash_memberstruct hash_member;
+typedef struct hash_bucketstruct hash_bucket;
+typedef struct hash_tblstruct hash_tbl;
+typedef struct hash_tblstruct_hdr hash_tblhdr;
+
+struct hash_memberstruct {
+ hash_member *next; /* hash next pointer */
+ hash_datum *data; /* hash data */
+ time_t h_time; /* hash dynamic free time */
+ int h_count; /* hash reference count */
+ mutex_t h_mtx; /* hash mutex */
+};
+
+struct hash_tblstruct;
+struct hash_bucketstruct {
+ hash_member *next;
+ struct hash_tblstruct *table;
+ rwlock_t rwlock;
+};
+
+struct hash_tblstruct_hdr {
+ unsigned size;
+ unsigned bucketnum;
+ hash_member *member;
+};
+
+struct hash_tblstruct {
+ unsigned size;
+ unsigned bucketnum;
+ hash_member *member; /* Used for linear dump */
+ boolean_t (*dfree_data)(); /* Used for dynamic free */
+ boolean_t dfree_lck; /* Use for dynamic free locking */
+ time_t dfree_time; /* Unused time to dynamically free */
+ hash_bucket *table; /* Dynamically Extend */
+ hash_bucket data[1];
+};
+
+extern unsigned hash_Size(unsigned int);
+extern hash_tbl *hash_Init(unsigned, boolean_t (*)(), time_t, boolean_t);
+extern void hash_Reset(hash_tbl *, boolean_t (*)());
+extern void *hash_Insert(hash_tbl *, void *, unsigned, int (*)(),
+ hash_datum *, hash_datum *);
+extern hash_datum *hash_Lookup(hash_tbl *, void *, unsigned, int (*)(),
+ hash_datum *, boolean_t);
+extern boolean_t hash_Delete(hash_tbl *, void *, unsigned, int (*)(),
+ hash_datum *, boolean_t (*)());
+extern void hash_Reap(hash_tbl *, boolean_t (*)());
+extern void hash_Age(void *);
+extern void hash_Dtime(void *, time_t);
+extern int hash_Refcount(void *);
+extern int hash_Htime(void *);
+extern void hash_Rele(void *, boolean_t);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _HASH_H */
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/icmp.c b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/icmp.c
new file mode 100644
index 0000000000..f29119cbb8
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/icmp.c
@@ -0,0 +1,236 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1993-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <syslog.h>
+#include <string.h>
+#include <time.h>
+#include <thread.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/udp.h>
+#include <netinet/dhcp.h>
+#include "dhcpd.h"
+#include "per_dnet.h"
+#include "interfaces.h"
+#include <v4_sum_impl.h>
+#include <locale.h>
+
+#define ICMP_ECHO_SIZE (sizeof (struct icmp) + 36)
+
+/*
+ * An implementation of ICMP ECHO for use in detecting addresses already
+ * in use. Address argument expected in network order. Result is set to
+ * B_TRUE if a ICMP ECHO reply is received, B_FALSE if not. Returns 0 if
+ * no errors were encountered, nonzero otherwise.
+ *
+ * NOTES: Not interface specific. We use our routing tables to route the
+ * messages correctly, and collect responses. This may mean that we
+ * receive an ICMP ECHO reply thru an interface the daemon has not been
+ * directed to watch. However, I believe that *ANY* echo reply means
+ * trouble, regardless of the route taken!
+ *
+ * 'cip' is expected in network order.
+ */
+
+int
+icmp_echo_check(struct in_addr *cip, boolean_t *result)
+{
+ struct icmp *icp;
+ struct ip *ipp;
+ int sequence = 0, i, s, s_cnt, r_cnt,
+ icmp_identifier, error = 0;
+ socklen_t fromlen;
+ ushort_t ip_hlen;
+ hrtime_t recv_intrvl;
+ struct sockaddr_in to, from;
+ struct pollfd pfd;
+ char ntoab[INET_ADDRSTRLEN];
+ ulong_t outpack[DHCP_SCRATCH/sizeof (ulong_t)];
+ ulong_t inpack[DHCP_SCRATCH/sizeof (ulong_t)];
+
+ *result = B_FALSE;
+
+ (void) inet_ntop(AF_INET, cip, ntoab, sizeof (ntoab));
+
+ if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) {
+ error = errno;
+ dhcpmsg(LOG_ERR,
+ "Error opening raw socket for ICMP (ping %s).\n", ntoab);
+ return (error);
+ }
+
+ if (fcntl(s, F_SETFL, O_NDELAY) == -1) {
+ error = errno;
+ dhcpmsg(LOG_ERR,
+ "Error setting ICMP socket to no delay. (ping %s)\n",
+ ntoab);
+ (void) close(s);
+ return (error);
+ }
+
+ pfd.fd = s;
+ pfd.events = POLLIN | POLLPRI;
+ pfd.revents = 0;
+
+ icmp_identifier = (int)thr_self() & (ushort_t)-1;
+ (void) memset((void *)outpack, 0, sizeof (outpack));
+ outpack[10] = 0x12345678;
+ icp = (struct icmp *)outpack;
+ icp->icmp_code = 0;
+ icp->icmp_type = ICMP_ECHO;
+ icp->icmp_id = icmp_identifier;
+
+ (void) memset((void *)&to, 0, sizeof (struct sockaddr_in));
+ to.sin_family = AF_INET;
+ to.sin_addr.s_addr = cip->s_addr;
+
+ /*
+ * We make icmp_tries attempts to contact the target. We
+ * wait the same length of time for a response in both cases.
+ */
+ for (i = 0; i < icmp_tries; i++) {
+ icp->icmp_seq = sequence++;
+ icp->icmp_cksum = 0;
+ icp->icmp_cksum = ipv4cksum((uint16_t *)icp, ICMP_ECHO_SIZE);
+
+ /* Deliver our ECHO. */
+ s_cnt = sendto(s, (char *)outpack, ICMP_ECHO_SIZE, 0,
+ (struct sockaddr *)&to, sizeof (struct sockaddr));
+
+ if (s_cnt < 0 || s_cnt != ICMP_ECHO_SIZE) {
+ error = errno;
+ dhcpmsg(LOG_ERR,
+ "Error sending ICMP message. (ping %s).\n",
+ ntoab);
+ (void) close(s);
+ return (error);
+ }
+
+ /* Collect replies. */
+ recv_intrvl = gethrtime() +
+ (hrtime_t)(icmp_timeout) * 1000000;
+
+ while (gethrtime() < recv_intrvl) {
+ if (poll(&pfd, (nfds_t)1, icmp_timeout) < 0 ||
+ pfd.revents & (POLLERR | POLLHUP | POLLNVAL)) {
+ /* EINTR is masked - must be serious */
+ error = errno;
+ dhcpmsg(LOG_ERR, "Poll: ICMP reply for %s.\n",
+ ntoab);
+ (void) close(s);
+ return (error);
+ }
+
+ if (!pfd.revents) {
+ continue; /* no data, timeout */
+ }
+
+ fromlen = sizeof (from);
+ if ((r_cnt = recvfrom(s, (char *)inpack,
+ sizeof (inpack), 0, (struct sockaddr *)&from,
+ &fromlen)) < 0) {
+ error = errno;
+ if (error == EAGAIN) {
+ error = 0;
+ continue;
+ }
+ /* EINTR is masked - must be serious */
+ dhcpmsg(LOG_ERR,
+ "recvfrom: ICMP reply for %s.\n",
+ ntoab);
+ (void) close(s);
+ return (error);
+ }
+
+ if (from.sin_addr.s_addr != cip->s_addr)
+ continue; /* Not from the IP of interest */
+ /*
+ * We know we got an ICMP message of some type from
+ * the IP of interest. Be conservative and
+ * consider it in use. The following logic is just
+ * for identifying problems in the response.
+ */
+ *result = B_TRUE;
+
+ if (!debug)
+ break;
+
+ ipp = (struct ip *)inpack;
+ if (r_cnt != ntohs(ipp->ip_len)) {
+ /* bogus IP header */
+ dhcpmsg(LOG_NOTICE, "Malformed ICMP message "
+ "received from host %s: len %d != %d\n",
+ ntoab, r_cnt, ntohs(ipp->ip_len));
+ break;
+ }
+ ip_hlen = ipp->ip_hl << 2;
+ if (r_cnt < (int)(ip_hlen + ICMP_MINLEN)) {
+ dhcpmsg(LOG_NOTICE, "ICMP message received "
+ "from host %s is too small.\n", ntoab);
+ break;
+ }
+ icp = (struct icmp *)((uint_t)inpack + ip_hlen);
+ if (ipv4cksum((uint16_t *)icp,
+ ntohs(ipp->ip_len) - ip_hlen) != 0) {
+ dhcpmsg(LOG_NOTICE, "Bad checksum on incoming "
+ "ICMP echo reply. (ping %s)\n", ntoab);
+ }
+ if (icp->icmp_type != ICMP_ECHOREPLY) {
+ dhcpmsg(LOG_NOTICE,
+ "Unexpected ICMP type %d from %s.\n",
+ icp->icmp_type, ntoab);
+ }
+ if (icp->icmp_id != icmp_identifier) {
+ dhcpmsg(LOG_NOTICE,
+ "ICMP message id mismatch (from %s).\n",
+ ntoab);
+ }
+ if (icp->icmp_seq != (sequence - 1)) {
+ dhcpmsg(LOG_NOTICE, "ICMP sequence mismatch: "
+ "%d != %d (ping %s)\n", icp->icmp_seq,
+ sequence - 1, ntoab);
+ }
+ break;
+ }
+ }
+ (void) close(s);
+
+ return (error);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/in.dhcpd.xcl b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/in.dhcpd.xcl
new file mode 100644
index 0000000000..f4ddaa112a
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/in.dhcpd.xcl
@@ -0,0 +1,152 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+msgid "XXX_XXX_XXX_XXX"
+msgid ":"
+msgid ","
+msgid "Subnet"
+msgid "UTCoffst"
+msgid "Router"
+msgid "Timeserv"
+msgid "IEN116ns"
+msgid "DNSserv"
+msgid "Logserv"
+msgid "Cookie"
+msgid "Lprserv"
+msgid "Impress"
+msgid "Resource"
+msgid "Hostname"
+msgid "Bootsize"
+msgid "Dumpfile"
+msgid "DNSdmain"
+msgid "Swapserv"
+msgid "Rootpath"
+msgid "ExtendP"
+msgid "IpFwdF"
+msgid "NLrouteF"
+msgid "PFilter"
+msgid "MaxIpSiz"
+msgid "IpTTL"
+msgid "PathTO"
+msgid "PathTbl"
+msgid "MTU"
+msgid "SameMtuF"
+msgid "Broadcst"
+msgid "MaskDscF"
+msgid "MaskSupF"
+msgid "RDiscvyF"
+msgid "RSolictS"
+msgid "StaticRt"
+msgid "TrailerF"
+msgid "ArpTimeO"
+msgid "EthEncap"
+msgid "TcpTTL"
+msgid "TcpKaInt"
+msgid "TcpKaGbF"
+msgid "NISdmain"
+msgid "NISservs"
+msgid "NTPservs"
+msgid "NetBNms"
+msgid "NetBDsts"
+msgid "NetBNdT"
+msgid "NetBScop"
+msgid "XFontSrv"
+msgid "XDispMgr"
+msgid "LeaseTim"
+msgid "Message"
+msgid "T1Time"
+msgid "T2Time"
+msgid "NW_dmain"
+msgid "NWIPOpts"
+msgid "NIS+dom"
+msgid "NIS+serv"
+msgid "TFTPsrvN"
+msgid "OptBootF"
+msgid "MblIPAgt"
+msgid "SMTPserv"
+msgid "POP3serv"
+msgid "NNTPserv"
+msgid "WWWservs"
+msgid "Fingersv"
+msgid "IRCservs"
+msgid "STservs"
+msgid "STDAservs"
+msgid "BootFile"
+msgid "BootSrvA"
+msgid "BootSrvN"
+msgid "LeaseNeg"
+msgid "EchoVC"
+msgid "BootPath"
+msgid "Include"
+msgid "files"
+msgid "nisplus"
+msgid "Vendor"
+msgid "Site"
+msgid "Extend"
+msgid "ASCII"
+msgid "OCTET"
+msgid "IP"
+msgid "NUMBER"
+msgid "BOOL"
+msgid " \t\n"
+msgid "dlinfoack"
+msgid "dlattachreq"
+msgid "dldetachreq"
+msgid "dlbindack"
+msgid "dlunbindack"
+msgid "/tftpboot"
+msgid "inetboot"
+msgid ".PREP"
+msgid "%s/%s"
+msgid "NOENT"
+msgid "PENDING"
+msgid "AVAILABLE"
+msgid "IN_USE"
+msgid "FAILED"
+msgid "DONTCARE"
+msgid "UNKNOWN"
+msgid "/dev/ip"
+msgid "pfmod"
+msgid "0123456789"
+msgid "/dev/"
+msgid "SOCKET"
+msgid "DLPI"
+msgid "BIND: %s\n"
+msgid "I_PUSH: %s, on %s\n"
+msgid "SENDTO: %s.\n"
+msgid "in.dhcpd"
+msgid "denvh:o:r:b:i:t:"
+msgid "automatic"
+msgid "manual"
+msgid "/etc/default/dhcp"
+msgid "/dev/null"
+msgid "3.1"
+msgid "files"
+msgid "nisplus"
+msgid "NIS_PATH"
+msgid "none"
+msgid "(errno: %d)"
+msgid "%s %s"
+msgid "%s"
+msgid "(%%m) %s"
+msgid "%02u"
+msgid "%ld"
+msgid "%ld_%ld_%ld_%ld"
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/inc.flg b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/inc.flg
new file mode 100644
index 0000000000..6303961989
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/inc.flg
@@ -0,0 +1,31 @@
+#!/bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 1999-2000 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+
+echo_file usr/src/cmd/cmd-inet/usr.lib/Makefile.lib
+find_files "s.*" usr/src/common/net/dhcp
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/interfaces.c b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/interfaces.c
new file mode 100644
index 0000000000..1c3333bf69
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/interfaces.c
@@ -0,0 +1,1445 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1993-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <assert.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netinet/if_ether.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+#include <stropts.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <syslog.h>
+#include <netinet/dhcp.h>
+#include <dhcp_symbol.h>
+#include "dhcpd.h"
+#include "per_dnet.h"
+#include "interfaces.h"
+#include <v4_sum_impl.h>
+#include <locale.h>
+
+static int socksize = 64 * 1024; /* large socket window size for data */
+static const uchar_t magic_cookie[] = BOOTMAGIC;
+static void disp_if(IF *);
+
+/*
+ * Network interface configuration. This file contains routines which
+ * handle the input side of the DHCP/BOOTP/Relay agent. Multiple interfaces
+ * are handled by identifying explicitly each interface, and creating a
+ * stream for each. If only one usable interface exists, then a "normal"
+ * UDP socket is used for simplicity's sake.
+ */
+
+IF *if_head; /* head of interfaces list */
+mutex_t if_head_mtx; /* mutex for adding/deleting IF list entries */
+char *interfaces; /* user specified interfaces */
+static int num_interfaces; /* # of usable interfaces on the system */
+
+static char *
+dsrvr_socktype(dsrvr_socktype_t stype)
+{
+ char *rp;
+
+ switch (stype) {
+ case DSRVR_LBCAST:
+ rp = "limited broadcast";
+ break;
+ case DSRVR_DBCAST:
+ rp = "directed broadcast";
+ break;
+ case DSRVR_UCAST:
+ rp = "unicast";
+ break;
+ }
+ return (rp);
+}
+
+/*
+ * Given two packets, match them based on BOOTP header operation, packet len,
+ * hardware type, flags, ciaddr, DHCP type, client id, or chaddr.
+ * Returns B_TRUE if they match, B_FALSE otherwise.
+ */
+static boolean_t
+match_plp(PKT_LIST *alp, PKT_LIST *blp)
+{
+ DHCP_OPT *a, *b;
+
+ assert(alp != NULL && blp != NULL);
+
+ if (alp->pkt->op != blp->pkt->op ||
+ alp->len != alp->len ||
+ alp->pkt->htype != blp->pkt->htype ||
+ alp->pkt->flags != blp->pkt->flags ||
+ alp->pkt->ciaddr.s_addr != blp->pkt->ciaddr.s_addr)
+ return (B_FALSE); /* not even the same BOOTP type. */
+
+#ifdef DEBUG
+ if (alp->pkt->giaddr.s_addr != blp->pkt->giaddr.s_addr) {
+ dhcpmsg(LOG_DEBUG,
+ "%04d match_plp: giaddr mismatch on 0x%x, 0x%x\n",
+ thr_self());
+ }
+#endif /* DEBUG */
+
+ a = alp->opts[CD_DHCP_TYPE];
+ b = blp->opts[CD_DHCP_TYPE];
+ if (a == NULL && b == NULL) {
+ /* bootp */
+ if (memcmp(alp->pkt->chaddr, blp->pkt->chaddr,
+ alp->pkt->hlen) == 0)
+ return (B_TRUE);
+ } else if (a != NULL && b != NULL) {
+ if (a->value[0] == b->value[0]) {
+ /* dhcp - packet types match. */
+ a = alp->opts[CD_CLIENT_ID];
+ b = blp->opts[CD_CLIENT_ID];
+ if (a != NULL && b != NULL) {
+ if (memcmp(a->value, b->value, a->len) == 0)
+ return (B_TRUE);
+ } else {
+ if (memcmp(alp->pkt->chaddr, blp->pkt->chaddr,
+ alp->pkt->hlen) == 0)
+ return (B_TRUE);
+ }
+ }
+ }
+ return (B_FALSE);
+}
+
+/*
+ * Given a packet, searches for a later packet in the
+ * interface's client list. If the search is successful, the argument
+ * packet is deleted, and the later packet is returned with the appropriate
+ * fields/options modified.
+ *
+ * Matches are based on match_plp(). The list is scanned until the final packet
+ * which "matches" is found. The last match replaces
+ * the argument plp. Duplicates are deleted.
+ *
+ * General Notes: After the first candidate is found, the list is checked to
+ * the tail of the list for other matches. For each packet which is deleted.
+ * the duplicate statistic is incremented for each one. If no candidate is
+ * found, then the argument plp is returned.
+ *
+ * Caveats: What about length and contents of packets? By definition, a
+ * client is not supposed to be altering this between frames, so we should
+ * be ok. Since the argument plp may be destroyed, it is assumed to be
+ * detached.
+ */
+PKT_LIST *
+refresh_pktlist(dsvc_clnt_t *pcd, PKT_LIST *plp)
+{
+ PKT_LIST *wplp, *tplp, *retplp = NULL;
+ IF *ifp = pcd->ifp;
+
+ assert(_mutex_held(&pcd->pkt_mtx));
+
+ wplp = pcd->pkthead;
+ while (wplp != NULL) {
+ if (match_plp(plp, wplp)) {
+ pcd->pending--;
+
+ (void) mutex_lock(&ifp->ifp_mtx);
+ ifp->duplicate++;
+ (void) mutex_unlock(&ifp->ifp_mtx);
+
+ /*
+ * Note that tplp, retplp can be synonyms for
+ * wplp. The synonyms are used because moldy plp's
+ * will be nuked, and the plp to return will be
+ * detached.
+ */
+ tplp = wplp;
+ wplp = wplp->next;
+
+ if (retplp != NULL) {
+ /* moldy duplicates */
+ free_plp(retplp);
+ }
+ retplp = tplp;
+ detach_plp(pcd, retplp);
+ } else {
+ wplp = wplp->next;
+ }
+ }
+
+ if (retplp == NULL)
+ retplp = plp;
+ else {
+ if (debug) {
+ dhcpmsg(LOG_DEBUG,
+ "%04d: Refreshed (0x%x) to (0x%x)\n",
+ thr_self(), plp, retplp);
+ }
+ free_plp(plp);
+ }
+
+ return (retplp);
+}
+
+/*
+ * Queries the IP transport layer for configured interfaces. Those that
+ * are acceptable for use by our daemon have these characteristics:
+ *
+ * Not loopback
+ * Is UP
+ *
+ * Sets num_interfaces global to number of valid, selected interfaces.
+ *
+ * Returns: 0 for success, the appropriate errno on fatal failure.
+ *
+ * Notes: Code gleaned from the in.rarpd, solaris 2.2.
+ */
+static int
+find_interfaces(void)
+{
+ int i, k, ip, reqsize, numifs;
+ boolean_t found;
+ ushort_t mtu_tmp;
+ struct ifreq *reqbuf, *ifr;
+ struct ifconf ifconf;
+ IF *ifp, *if_tail;
+ struct sockaddr_in *sin;
+ char **user_if;
+ ENCODE *hecp;
+
+ if ((ip = open("/dev/ip", 0)) < 0) {
+ dhcpmsg(LOG_ERR, "Error: opening /dev/ip: %s\n",
+ strerror(errno));
+ return (1);
+ }
+
+ if (ioctl(ip, SIOCGIFNUM, &numifs) < 0) {
+ dhcpmsg(LOG_WARNING,
+ "Error discovering number of network interfaces: %s\n",
+ strerror(errno));
+ return (1);
+ }
+
+ reqsize = numifs * sizeof (struct ifreq);
+ reqbuf = (struct ifreq *)smalloc(reqsize);
+
+ ifconf.ifc_len = reqsize;
+ ifconf.ifc_buf = (caddr_t)reqbuf;
+
+ if (ioctl(ip, SIOCGIFCONF, &ifconf) < 0) {
+ dhcpmsg(LOG_ERR,
+ "Error getting network interface information: %s\n",
+ strerror(errno));
+ free(reqbuf);
+ (void) close(ip);
+ return (1);
+ }
+
+ /*
+ * Verify that user specified interfaces are valid.
+ */
+ user_if = (char **)smalloc(numifs * sizeof (char *));
+ if (interfaces != NULL) {
+ for (i = 0; i < numifs; i++) {
+ user_if[i] = strtok(interfaces, ",");
+ if (user_if[i] == NULL)
+ break; /* we're done */
+ interfaces = NULL; /* for next call to strtok() */
+
+ for (found = B_FALSE, ifr = ifconf.ifc_req;
+ ifr < &ifconf.ifc_req[ifconf.ifc_len /
+ sizeof (struct ifreq)]; ifr++) {
+ if (strcmp(user_if[i], ifr->ifr_name) == 0) {
+ found = B_TRUE;
+ break;
+ }
+ }
+ if (!found) {
+ dhcpmsg(LOG_ERR,
+ "Invalid network interface: %s\n",
+ user_if[i]);
+ free(reqbuf);
+ free(user_if);
+ (void) close(ip);
+ return (1);
+ }
+ }
+ if (i < numifs)
+ user_if[i] = NULL;
+ } else
+ user_if[0] = NULL;
+
+ /*
+ * For each interface, build an interface structure. Ignore any
+ * LOOPBACK or down interfaces.
+ */
+ if_tail = if_head = NULL;
+ for (ifr = ifconf.ifc_req;
+ ifr < &ifconf.ifc_req[ifconf.ifc_len / sizeof (struct ifreq)];
+ ifr++) {
+ if (ioctl(ip, SIOCGIFFLAGS, ifr) < 0) {
+ dhcpmsg(LOG_ERR,
+"Error encountered getting interface: %s flags: %s\n",
+ ifr->ifr_name, strerror(errno));
+ continue;
+ }
+ if ((ifr->ifr_flags & IFF_LOOPBACK) ||
+ !(ifr->ifr_flags & IFF_UP))
+ continue;
+
+ num_interfaces++; /* all possible interfaces counted */
+
+ /*
+ * If the user specified a list of interfaces,
+ * we'll only consider the ones specified.
+ */
+ if (user_if[0] != NULL) {
+ for (i = 0; i < numifs; i++) {
+ if (user_if[i] == NULL)
+ break; /* skip this interface */
+ if (strcmp(user_if[i], ifr->ifr_name) == 0)
+ break; /* user wants this one */
+ }
+ if (i == numifs || user_if[i] == NULL)
+ continue; /* skip this interface */
+ } else if (strchr(ifr->ifr_name, ':') != NULL)
+ continue; /* skip virtual interfaces */
+
+ ifp = (IF *)smalloc(sizeof (IF));
+ (void) strcpy(ifp->nm, ifr->ifr_name);
+
+ ifp->ifceno = if_nametoindex(ifp->nm);
+ ifp->flags = ifr->ifr_flags;
+ for (k = 0; k < DSRVR_NUM_DESC; k++)
+ ifp->descs[k] = -1;
+
+ /*
+ * Broadcast address. Not valid for POINTOPOINT
+ * connections.
+ */
+ if ((ifp->flags & IFF_POINTOPOINT) == 0) {
+ if (ifp->flags & IFF_BROADCAST) {
+ if (ioctl(ip, SIOCGIFBRDADDR, ifr) < 0) {
+ dhcpmsg(LOG_ERR, "Error encountered \
+getting interface: %s broadcast address: %s\n", ifp->nm, strerror(errno));
+ free(ifp);
+ num_interfaces--;
+ continue;
+ }
+ /* LINTED [alignment ok] */
+ sin = (struct sockaddr_in *)&ifr->ifr_addr;
+ ifp->bcast = sin->sin_addr;
+ } else
+ ifp->bcast.s_addr = htonl(INADDR_ANY);
+
+ hecp = make_encode(DSYM_STANDARD, CD_BROADCASTADDR,
+ sizeof (struct in_addr), &ifp->bcast,
+ ENC_COPY);
+ replace_encode(&ifp->ecp, hecp, ENC_DONT_COPY);
+ }
+
+ /* Subnet mask */
+ if (ioctl(ip, SIOCGIFNETMASK, ifr) < 0) {
+ dhcpmsg(LOG_ERR, "Error encountered getting \
+interface: %s netmask: %s\n", ifp->nm, strerror(errno));
+ free_encode_list(ifp->ecp);
+ free(ifp);
+ num_interfaces--;
+ continue;
+ }
+ /* LINTED [alignment ok] */
+ sin = (struct sockaddr_in *)&ifr->ifr_addr;
+ ifp->mask = sin->sin_addr;
+ hecp = make_encode(DSYM_STANDARD, CD_SUBNETMASK,
+ sizeof (struct in_addr), &ifp->mask, ENC_COPY);
+ replace_encode(&ifp->ecp, hecp, ENC_DONT_COPY);
+
+ /* Address */
+ if (ioctl(ip, SIOCGIFADDR, ifr) < 0) {
+ dhcpmsg(LOG_ERR, "Error encountered getting \
+interface: %s address: %s\n", ifp->nm, strerror(errno));
+ free_encode_list(ifp->ecp);
+ free(ifp);
+ num_interfaces--;
+ continue;
+ }
+ /* LINTED [alignment ok] */
+ sin = (struct sockaddr_in *)&ifr->ifr_addr;
+ ifp->addr = sin->sin_addr;
+
+ /* MTU */
+ if (ioctl(ip, SIOCGIFMTU, ifr) < 0) {
+ dhcpmsg(LOG_ERR, "Error encountered getting \
+interface: %s MTU: %s\n", ifp->nm, strerror(errno));
+ free_encode_list(ifp->ecp);
+ free(ifp);
+ num_interfaces--;
+ continue;
+ }
+
+ ifp->mtu = ifr->ifr_metric;
+ mtu_tmp = htons(ifp->mtu);
+ hecp = make_encode(DSYM_STANDARD, CD_MTU, 2,
+ &mtu_tmp, ENC_COPY);
+ replace_encode(&ifp->ecp, hecp, ENC_DONT_COPY);
+
+ /* Attach to interface list */
+ if (!if_tail) {
+ (void) mutex_init(&if_head_mtx, USYNC_THREAD, 0);
+ (void) mutex_lock(&if_head_mtx);
+ if_tail = if_head = ifp;
+ (void) mutex_unlock(&if_head_mtx);
+ } else {
+ (void) mutex_lock(&if_head_mtx);
+ if_tail->next = ifp;
+ if_tail = ifp;
+ (void) mutex_unlock(&if_head_mtx);
+ }
+ }
+
+ free(reqbuf);
+ free(user_if);
+ (void) close(ip);
+
+ if (if_head == NULL) {
+ num_interfaces = 0;
+ dhcpmsg(LOG_ERR, "Cannot find any valid interfaces.\n");
+ (void) mutex_destroy(&if_head_mtx);
+ return (EINVAL);
+ }
+ return (0);
+}
+
+/*
+ * Destroy an *uninitialized* IF structure - returns next ifp.
+ */
+static IF *
+zap_ifp(IF **ifp_prevpp, IF *ifp)
+{
+ IF *tifp;
+
+ assert(_mutex_held(&if_head_mtx));
+
+ if (*ifp_prevpp == ifp) {
+ if_head = ifp->next;
+ *ifp_prevpp = if_head;
+ } else
+ (*ifp_prevpp)->next = ifp->next;
+
+ tifp = ifp->next;
+
+ free(ifp);
+
+ return (tifp);
+}
+
+/*
+ * Monitor thread function. Poll on interface descriptors. Add valid BOOTP
+ * packets to interfaces PKT_LIST.
+ *
+ * Because the buffer will potentially contain the ip/udp headers, we flag
+ * this by setting the 'offset' field to the length of the two headers so that
+ * free_plp() can "do the right thing"
+ *
+ * Monitor the given interface. Signals are handled by sig_client thread.
+ *
+ * We make some attempt to deal with marginal interfaces as follows. We
+ * keep track of system errors (errors) and protocol errors (ifp->errors).
+ * If we encounter more than DHCP_MON_SYSERRS in DHCP_MON_ERRINTVL,
+ * then the interface thread will put itself to sleep for DHCP_MON_SLEEP
+ * minutes.
+ *
+ * MT SAFE
+ */
+static void *
+monitor_interface(void *argp)
+{
+ PKT_LIST *plp, *tplp;
+ IF *ifp = (IF *)argp;
+ int errors, err, i;
+ uint_t verify_len;
+ struct pollfd pfd[DSRVR_NUM_DESC];
+ struct strbuf data;
+ char cbuf[DN_MAX_CID_LEN], ntoab[INET_ADDRSTRLEN];
+ time_t err_interval;
+ dn_rec_t dn;
+ dsvc_dnet_t *pnd;
+ dsvc_clnt_t *pcd;
+ struct in_addr netaddr, subnetaddr;
+ dsvc_pendclnt_t *workp;
+ int open_ret;
+ dsvc_thr_t *freep;
+ thread_t tid;
+ boolean_t existing_allocation;
+
+ if (debug) {
+ dhcpmsg(LOG_DEBUG, "Monitor (%04d/%s) started...\n",
+ ifp->if_thread, ifp->nm);
+ }
+
+ if (verbose)
+ disp_if(ifp);
+
+ pfd[DSRVR_LBCAST].fd = ifp->descs[DSRVR_LBCAST];
+ pfd[DSRVR_LBCAST].events = POLLIN | POLLPRI;
+ pfd[DSRVR_DBCAST].fd = ifp->descs[DSRVR_DBCAST];
+ pfd[DSRVR_DBCAST].events = POLLIN | POLLPRI;
+ pfd[DSRVR_UCAST].fd = ifp->descs[DSRVR_UCAST];
+ pfd[DSRVR_UCAST].events = POLLIN | POLLPRI;
+
+ err_interval = time(NULL) + DHCP_MON_ERRINTVL;
+ errors = 0;
+ while (time_to_go == 0) {
+ if (errors > DHCP_MON_SYSERRS) {
+ if (time(NULL) < err_interval) {
+ dhcpmsg(LOG_WARNING,
+"Monitor (%04d/%s): Too many system errors (%d), pausing for %d minute(s)...\n",
+ ifp->if_thread, ifp->nm, errors,
+ DHCP_MON_SYSERRS);
+ (void) sleep(DHCP_MON_SLEEP);
+ err_interval = time(NULL) + DHCP_MON_ERRINTVL;
+ }
+ errors = 0;
+ }
+ pfd[DSRVR_LBCAST].revents = 0;
+ pfd[DSRVR_DBCAST].revents = 0;
+ pfd[DSRVR_UCAST].revents = 0;
+ if (poll(&pfd[0], (nfds_t)DSRVR_NUM_DESC, INFTIM) < 0) {
+ dhcpmsg(LOG_ERR,
+ "Monitor (%04d/%s) Polling error: (%s).\n",
+ ifp->if_thread, ifp->nm, strerror(errno));
+ errors++;
+ continue;
+ }
+ /*
+ * See if we are to exit. We can't be holding any locks...
+ */
+ (void) mutex_lock(&ifp->ifp_mtx);
+ if (ifp->thr_exit) {
+ if (debug) {
+ dhcpmsg(LOG_DEBUG,
+ "Monitor (%04d/%s): exiting.\n",
+ ifp->if_thread, ifp->nm);
+ }
+ (void) mutex_unlock(&ifp->ifp_mtx);
+ break;
+ }
+ (void) mutex_unlock(&ifp->ifp_mtx);
+
+ /* examine each socket for packets in turn */
+ for (i = 0; i < DSRVR_NUM_DESC; i++) {
+ if (pfd[i].revents == 0)
+ continue;
+ if (pfd[i].revents & (POLLERR | POLLHUP | POLLNVAL)) {
+ dhcpmsg(LOG_ERR, "Network interface "
+ "error on device: %s(%s)\n", ifp->nm,
+ dsrvr_socktype(i));
+ errors++;
+ continue;
+ }
+ if (!(pfd[i].revents & (POLLIN | POLLRDNORM))) {
+ dhcpmsg(LOG_INFO, "Unsupported event "
+ "on device %s(%s): %d\n", ifp->nm,
+ dsrvr_socktype(i),
+ pfd[i].revents);
+ errors++;
+ continue;
+ }
+ data.buf = smalloc(ifp->mtu);
+ data.len = recv(ifp->descs[i], data.buf, ifp->mtu, 0);
+ if (data.len < 0) {
+ dhcpmsg(LOG_ERR, "Error: %s receiving UDP "
+ "datagrams on %s(%s)\n",
+ strerror(errno), ifp->nm,
+ dsrvr_socktype(i));
+ free(data.buf);
+ errors++;
+ continue;
+ } else
+ verify_len = data.len;
+
+ if (debug) {
+ dhcpmsg(LOG_INFO,
+ "Datagram received on network device: "
+ "%s(%s)\n", ifp->nm, dsrvr_socktype(i));
+ }
+
+ (void) mutex_lock(&ifp->ifp_mtx);
+ ifp->received++;
+ (void) mutex_unlock(&ifp->ifp_mtx);
+
+ if (verify_len < BASE_PKT_SIZE) {
+ if (verbose) {
+ dhcpmsg(LOG_INFO, "Short packet %d < "
+ "%d on %s(%s) ignored\n",
+ verify_len, sizeof (PKT),
+ ifp->nm, dsrvr_socktype(i));
+ }
+ free(data.buf);
+ (void) mutex_lock(&ifp->ifp_mtx);
+ ifp->errors++;
+ (void) mutex_unlock(&ifp->ifp_mtx);
+ continue;
+ }
+
+ plp = (PKT_LIST *)smalloc(sizeof (PKT_LIST));
+ plp->offset = 0;
+ plp->len = data.len;
+ /* LINTED [alignment ok] */
+ plp->pkt = (PKT *)data.buf;
+
+ if (plp->pkt->hops >= max_hops + 1) {
+ if (verbose) {
+ dhcpmsg(LOG_INFO, "%s(%s): Packet "
+ "dropped: too many hops: %d\n",
+ ifp->nm, dsrvr_socktype(i),
+ plp->pkt->hops);
+ }
+ free_plp(plp);
+ (void) mutex_lock(&ifp->ifp_mtx);
+ ifp->errors++;
+ (void) mutex_unlock(&ifp->ifp_mtx);
+ continue;
+ }
+
+ /* validate hardware len */
+ if (plp->pkt->hlen > sizeof (plp->pkt->chaddr))
+ plp->pkt->hlen = sizeof (plp->pkt->chaddr);
+
+ if (debug && plp->pkt->giaddr.s_addr != 0L &&
+ plp->pkt->giaddr.s_addr != ifp->addr.s_addr) {
+ dhcpmsg(LOG_INFO, "%s(%s): Packet received "
+ "from relay agent: %s\n", ifp->nm,
+ dsrvr_socktype(i), inet_ntop(AF_INET,
+ &plp->pkt->giaddr, ntoab, sizeof (ntoab)));
+ }
+
+ if (!server_mode) {
+ /*
+ * Relay agent mode. No further processing
+ * required ; we'll handle it here.
+ */
+ (void) mutex_lock(&if_head_mtx);
+ err = relay_agent(ifp, plp);
+ (void) mutex_unlock(&if_head_mtx);
+ if (err != 0) {
+ dhcpmsg(LOG_ERR, "Relay agent mode "
+ "failed: %d (%s) on: %s(%s)\n",
+ err, (plp->pkt->op == BOOTREPLY) ?
+ "reply" : "request", ifp->nm,
+ dsrvr_socktype(i));
+ errors++; /* considered system error */
+ } else {
+ /* update statistics */
+ (void) mutex_lock(&ifp->ifp_mtx);
+ ifp->processed++;
+ ifp->received++;
+ (void) mutex_unlock(&ifp->ifp_mtx);
+ }
+ free_plp(plp);
+ continue;
+ }
+
+/* ============ Packets destined for bootp and dhcp server modules ========== */
+
+ /*
+ * Allow packets without RFC1048 magic cookies.
+ * Just don't do an options scan on them,
+ * thus we treat them as plain BOOTP packets.
+ * The BOOTP server can deal with requests of
+ * this type.
+ */
+ if (memcmp(plp->pkt->cookie, magic_cookie,
+ sizeof (magic_cookie)) != 0) {
+ if (verbose) {
+ dhcpmsg(LOG_INFO, "%s(%s): Client: %s "
+ "using non-RFC1048 BOOTP cookie.\n",
+ ifp->nm, dsrvr_socktype(i),
+ disp_cid(plp, cbuf, sizeof (cbuf)));
+ }
+ plp->rfc1048 = B_FALSE;
+ } else {
+ /*
+ * Scan the options in the packet and fill in
+ * the opts and vs fields in the * clientlist
+ * structure. If there's a DHCP message type
+ * in the packet then it's a DHCP packet;
+ * otherwise it's a BOOTP packet. Standard
+ * options are RFC1048 style.
+ */
+ if (dhcp_options_scan(plp, B_FALSE) != 0) {
+ dhcpmsg(LOG_ERR, "Garbled DHCP/BOOTP "
+ "packet received on: %s(%s)\n",
+ ifp->nm, dsrvr_socktype(i));
+ free_plp(plp);
+ (void) mutex_lock(&ifp->ifp_mtx);
+ ifp->errors++;
+ (void) mutex_unlock(&ifp->ifp_mtx);
+ continue;
+ }
+ plp->rfc1048 = B_TRUE;
+ }
+
+ /*
+ * Link the new packet to the list of packets
+ * for this network/client. No need to lock plp,
+ * since it isn't visible outside this function yet.
+ */
+ if (plp->pkt->op != BOOTREQUEST) {
+ dhcpmsg(LOG_ERR, "Unexpected packet received "
+ "on %s(%s), BOOTP server port. Ignored.\n",
+ ifp->nm, dsrvr_socktype(i));
+ free_plp(plp);
+ (void) mutex_lock(&ifp->ifp_mtx);
+ ifp->errors++;
+ (void) mutex_unlock(&ifp->ifp_mtx);
+ continue;
+ }
+
+ determine_network(ifp, plp, &netaddr, &subnetaddr);
+ if ((err = open_dnet(&pnd, &netaddr, &subnetaddr)) !=
+ DSVC_SUCCESS) {
+ if (verbose && err == DSVC_NO_TABLE) {
+ netaddr.s_addr &= subnetaddr.s_addr;
+ dhcpmsg(LOG_INFO, "%s(%s): There is no "
+ "%s dhcp-network table for DHCP "
+ "client's network.\n", ifp->nm,
+ dsrvr_socktype(i),
+ inet_ntop(AF_INET, &netaddr,
+ ntoab, sizeof (ntoab)));
+ }
+ free_plp(plp);
+ continue;
+ }
+
+ /* Find client */
+ get_clnt_id(plp, (uchar_t *)dn.dn_cid,
+ sizeof (dn.dn_cid), &dn.dn_cid_len);
+ open_ret = open_clnt(pnd, &pcd, dn.dn_cid,
+ dn.dn_cid_len, B_FALSE);
+
+ if (pcd == NULL) {
+ free_plp(plp);
+ close_dnet(pnd, B_FALSE);
+ continue;
+ }
+
+ /*
+ * DOS via Packet flooding: ensure that each client's
+ * PKT_LIST never exceeds DHCP_MON_THRESHOLD pkts in
+ * length. If it does, we prune it from the head of
+ * the list, dropping sequential packets. Note that
+ * since DHCP is a multi-transaction protocol, we would
+ * like to be sure not to discard a REQUEST for an OFFER
+ * we've extended.
+ *
+ * TODO: we are still vulnerable to flooding attacks
+ * where bogus client ids are presented. This can be
+ * manually controlled via the MAX_CLIENTS and
+ * MAX_THREADS config file knobs.
+ */
+ (void) mutex_lock(&pcd->pkt_mtx);
+ if (pcd->pending > DHCP_MON_THRESHOLD) {
+ if ((tplp = pcd->pkthead) != NULL) {
+ detach_plp(pcd, tplp);
+ free_plp(tplp);
+ pcd->pending--;
+ }
+ }
+
+ if (pcd->pkthead == NULL)
+ pcd->pkthead = plp;
+ else {
+ pcd->pkttail->next = plp;
+ plp->prev = pcd->pkttail;
+ }
+ pcd->pkttail = plp;
+ pcd->pending++;
+ (void) mutex_unlock(&pcd->pkt_mtx);
+
+ /*
+ * Manage worker threads and deferred thread work list.
+ */
+ (void) mutex_lock(&pcd->pcd_mtx);
+ pcd->ifp = ifp;
+ if (pcd->clnt_thread == NULL &&
+ (pcd->flags & DHCP_PCD_CLOSING) == 0) {
+ existing_allocation = B_FALSE;
+ (void) mutex_lock(&pnd->thr_mtx);
+ if ((freep = pnd->thrhead) != NULL) {
+ existing_allocation = B_TRUE;
+ /*
+ * Restart a suspended thread.
+ */
+ pnd->thrhead = freep->thr_next;
+ if (pnd->thrhead == NULL)
+ pnd->thrtail = NULL;
+ (void) mutex_unlock(&pnd->thr_mtx);
+
+ (void) mutex_lock(&freep->thr_mtx);
+ freep->thr_flags &= ~DHCP_THR_LIST;
+ freep->thr_next = NULL;
+ freep->thr_pcd = pcd;
+ (void) mutex_unlock(&freep->thr_mtx);
+ pcd->clnt_thread = freep;
+ } else if (max_threads != -1 &&
+ pnd->nthreads >= max_threads) {
+ /*
+ * Add client once to deferred work
+ * list, to keep track of future work.
+ */
+ if ((pcd->flags & DHCP_PCD_WORK) == 0) {
+ pcd->flags |= DHCP_PCD_WORK;
+ workp = (dsvc_pendclnt_t *)
+ smalloc(
+ sizeof (dsvc_pendclnt_t));
+ get_clnt_id(plp,
+ (uchar_t *)workp->pnd_cid,
+ sizeof (workp->pnd_cid),
+ &workp->pnd_cid_len);
+ if (pnd->workhead == NULL)
+ pnd->workhead = workp;
+ else {
+ pnd->worktail->
+ pnd_next = workp;
+ }
+ pnd->worktail = workp;
+ }
+ (void) mutex_unlock(&pnd->thr_mtx);
+ (void) mutex_unlock(&pcd->pcd_mtx);
+ if (open_ret == DSVC_SUCCESS)
+ close_clnt(pcd, B_FALSE);
+ close_dnet(pnd, B_FALSE);
+ continue;
+ }
+ if (pcd->clnt_thread == NULL) {
+ pnd->nthreads++;
+ (void) mutex_unlock(&pnd->thr_mtx);
+ freep = pcd->clnt_thread =
+ (dsvc_thr_t *)
+ smalloc(sizeof (dsvc_thr_t));
+ (void) mutex_init(&freep->thr_mtx,
+ USYNC_THREAD, 0);
+ freep->thr_pcd = pcd;
+
+ /* Fire up a client thread. */
+ if (thr_create(NULL, 0, monitor_client,
+ freep, THR_BOUND | THR_SUSPENDED |
+ THR_DETACHED, &freep->thr_tid) !=
+ 0) {
+ dhcpmsg(LOG_ERR, "%s(%s): "
+ "Error %s starting client "
+ "monitor thread.\n",
+ ifp->nm, dsrvr_socktype(i),
+ strerror(errno));
+ (void) mutex_lock(
+ &pnd->thr_mtx);
+ pnd->nthreads--;
+ (void) mutex_unlock(
+ &pnd->thr_mtx);
+ free(freep);
+ freep = pcd->clnt_thread = NULL;
+ }
+ }
+ if (freep != NULL) {
+ /*
+ * Continue the new or reused thread.
+ * Let it close the client.
+ */
+ open_ret = DSVC_BUSY;
+ tid = freep->thr_tid;
+ (void) mutex_unlock(&pcd->pcd_mtx);
+ pcd = NULL;
+ if (existing_allocation) {
+ (void) cond_signal(
+ &freep->thr_cv);
+ } else {
+ (void) thr_continue(tid);
+ }
+ }
+ }
+ if (pcd != NULL) {
+ (void) mutex_unlock(&pcd->pcd_mtx);
+ if (open_ret == DSVC_SUCCESS)
+ close_clnt(pcd, B_FALSE);
+ }
+ close_dnet(pnd, B_FALSE);
+ }
+ }
+ return (NULL);
+}
+
+/*
+ * close interface sockets
+ */
+static void
+close_sockets(IF *ifp) {
+ int i;
+
+ for (i = 0; i < DSRVR_NUM_DESC; i++) {
+ if (ifp->descs[i] == -1)
+ continue;
+ (void) close(ifp->descs[i]);
+ ifp->descs[i] = -1;
+ }
+}
+
+/*
+ * initialize interface sockets.
+ *
+ * Returns: 0 for success, -1 otherwise.
+ */
+static int
+init_sockets(IF *ifp)
+{
+ int i, soptbuf = 1;
+ struct sockaddr_in sin;
+
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons((short)IPPORT_BOOTPS + port_offset);
+
+ ifp->descs[DSRVR_LBCAST] = -1;
+ ifp->descs[DSRVR_DBCAST] = -1;
+ ifp->descs[DSRVR_UCAST] = -1;
+
+ for (i = 0; i < DSRVR_NUM_DESC; i++) {
+ ifp->descs[i] = socket(AF_INET, SOCK_DGRAM, 0);
+ if (ifp->descs[i] < 0) {
+ dhcpmsg(LOG_ERR, "Error opening socket on %s(%s) for "
+ "receiving UDP datagrams: %s\n",
+ ifp->nm, dsrvr_socktype(i), strerror(errno));
+ return (-1);
+ }
+
+ if (setsockopt(ifp->descs[i], SOL_SOCKET, SO_REUSEADDR,
+ &soptbuf, (int)sizeof (soptbuf)) < 0) {
+ dhcpmsg(LOG_DEBUG, "Setting socket option on %s(%s) "
+ "to allow reuse on send descriptor failed: %s\n",
+ ifp->nm, dsrvr_socktype(i), strerror(errno));
+ close_sockets(ifp);
+ return (-1);
+ }
+
+ (void) setsockopt(ifp->descs[i], SOL_SOCKET, SO_RCVBUF,
+ &socksize, sizeof (socksize));
+ (void) setsockopt(ifp->descs[i], SOL_SOCKET, SO_SNDBUF,
+ &socksize, sizeof (socksize));
+
+ switch (i) {
+ case DSRVR_LBCAST:
+ if (setsockopt(ifp->descs[i], IPPROTO_IP,
+ IP_BOUND_IF, &ifp->ifceno,
+ (int)sizeof (char *)) < 0) {
+ dhcpmsg(LOG_ERR,
+ "Bind to index failed on %s: %s\n",
+ ifp->nm, strerror(errno));
+ close_sockets(ifp);
+ return (-1);
+ }
+ sin.sin_addr.s_addr = htonl(INADDR_BROADCAST);
+ break;
+ case DSRVR_DBCAST:
+ sin.sin_addr.s_addr =
+ ifp->addr.s_addr & ifp->mask.s_addr;
+ break;
+ case DSRVR_UCAST:
+ /* We send out the unicast socket */
+ if (setsockopt(ifp->descs[i], SOL_SOCKET,
+ SO_BROADCAST, &soptbuf,
+ (int)sizeof (soptbuf)) < 0) {
+ dhcpmsg(LOG_ERR, "Setting socket "
+ "option on %s to allow broadcast "
+ "on send descriptor failed: %s\n",
+ ifp->nm, strerror(errno));
+ close_sockets(ifp);
+ return (-1);
+ }
+ sin.sin_addr.s_addr = ifp->addr.s_addr;
+ break;
+ }
+ if (bind(ifp->descs[i],
+ (struct sockaddr *)&sin, sizeof (sin)) < 0) {
+ dhcpmsg(LOG_ERR,
+ "Error binding to UDP socket on %s(%s): %s\n",
+ ifp->nm, dsrvr_socktype(i), strerror(errno));
+ close_sockets(ifp);
+ return (-1);
+ }
+ }
+ return (0);
+}
+
+/*
+ * Based on the list generated by find_interfaces(), possibly modified by
+ * user arguments, open a stream for each valid / requested interface.
+ *
+ * If:
+ *
+ * 1) Only one interface exists, open a standard bidirectional UDP
+ * socket. Note that this is different than if only ONE
+ * interface is requested (but more exist).
+ *
+ * 2) If more than one valid interface exists, then attach to the
+ * datalink layer, push on the packet filter and buffering
+ * modules, and wait for fragment 0 IP packets that contain
+ * UDP packets with port 67 (server port).
+ *
+ * Comments:
+ * Using DLPI to identify the interface thru which BOOTP
+ * packets pass helps in providing the correct response.
+ * Note that I will open a socket for use in transmitting
+ * responses, suitably specifying the destination relay agent
+ * or host. Note that if I'm unicasting to the client (broadcast
+ * flag not set), that somehow I have to clue the IP layer about
+ * the client's hw address. The only way I can see doing this is
+ * making the appropriate ARP table entry.
+ *
+ * The only remaining unknown is dealing with clients that
+ * require broadcasting, and multiple interfaces exist. I assume
+ * that if I specify the interface's source address when
+ * opening the socket, that a limited broadcast will be
+ * directed to the correct net, and only the correct net.
+ *
+ * Returns: 0 for success, non-zero for failure.
+ */
+int
+open_interfaces(void)
+{
+ int inum, err = 0;
+ IF *ifp, *ifp_prevp;
+
+ /* Uncover list of valid, user-selected interfaces to monitor */
+ if ((err = find_interfaces()) != 0)
+ return (err);
+
+ (void) mutex_lock(&if_head_mtx);
+
+ /*
+ * Setup valid interfaces.
+ */
+ ifp = ifp_prevp = if_head;
+ err = inum = 0;
+ while (ifp != NULL) {
+ if (init_sockets(ifp) < 0) {
+ ifp = zap_ifp(&ifp_prevp, ifp);
+ num_interfaces--;
+ continue;
+ }
+
+ /* Accounting */
+ ifp->transmit = ifp->received = 0;
+ ifp->duplicate = ifp->dropped = 0;
+ ifp->processed = 0;
+
+ /* ifp structure lock */
+ (void) mutex_init(&ifp->ifp_mtx, USYNC_THREAD, 0);
+ ifp->thr_exit = 0;
+
+ /* fire up monitor thread */
+ if (thr_create(NULL, 0, monitor_interface, ifp,
+ THR_BOUND, &ifp->if_thread) != 0) {
+ dhcpmsg(LOG_ERR,
+"Interface: %s - Error %s starting monitor thread.\n", ifp->nm,
+ strerror(errno));
+ close_sockets(ifp);
+ (void) mutex_destroy(&ifp->ifp_mtx);
+ ifp = zap_ifp(&ifp_prevp, ifp);
+ num_interfaces--;
+ continue;
+ }
+ inum++;
+ ifp_prevp = ifp;
+ ifp = ifp->next;
+ }
+ (void) mutex_unlock(&if_head_mtx);
+
+ /*
+ * We must succeed in configuring at least one interface
+ * to be considered successful.
+ */
+ if (num_interfaces == 0) {
+ err = EINVAL;
+ dhcpmsg(LOG_ERR, "Cannot configure any interfaces.\n");
+ }
+ return (err);
+}
+
+/*
+ * Detach the referenced plp from the client list.
+ */
+void
+detach_plp(dsvc_clnt_t *pcd, PKT_LIST *plp)
+{
+ assert(_mutex_held(&pcd->pkt_mtx));
+
+ if (plp->prev == NULL) {
+ pcd->pkthead = plp->next;
+ if (pcd->pkthead != NULL)
+ pcd->pkthead->prev = NULL;
+ } else
+ plp->prev->next = plp->next;
+
+ if (plp->next != NULL)
+ plp->next->prev = plp->prev;
+ else {
+ pcd->pkttail = plp->prev;
+ if (pcd->pkttail != NULL)
+ pcd->pkttail->next = NULL;
+ }
+ plp->prev = plp->next = NULL;
+}
+
+/*
+ * Write a packet to an interface.
+ *
+ * Returns 0 on success otherwise non-zero.
+ */
+int
+write_interface(IF *ifp, PKT *clientp, int len, struct sockaddr_in *to)
+{
+ int err;
+
+ to->sin_family = AF_INET;
+
+ if ((err = sendto(ifp->descs[DSRVR_UCAST], clientp, len, 0,
+ (struct sockaddr *)to, sizeof (struct sockaddr))) < 0) {
+ dhcpmsg(LOG_ERR, "SENDTO: %s.\n", strerror(errno));
+ return (err);
+ }
+
+ (void) mutex_lock(&ifp->ifp_mtx);
+ ifp->transmit++;
+ (void) mutex_unlock(&ifp->ifp_mtx);
+
+ return (0);
+}
+
+/*
+ * Pop any packet filters, buffering modules, close stream, free encode
+ * list, terminate monitor thread, free ifp. Return ifp next ptr.
+ */
+static IF *
+close_interface(IF *ifp)
+{
+ int err;
+ IF *tifp;
+
+ assert(ifp != NULL);
+
+ assert(_mutex_held(&if_head_mtx));
+
+ (void) mutex_lock(&ifp->ifp_mtx);
+ ifp->thr_exit = 1;
+
+ close_sockets(ifp); /* thread will exit poll ... */
+ (void) mutex_unlock(&ifp->ifp_mtx);
+
+ /*
+ * Wait for the thread to exit. We release the if_head_mtx
+ * lock, since the monitor thread(s) need to acquire it to traverse
+ * the list - and we don't want to deadlock. Once the monitor thread
+ * notices the thr_exit flag, it'll be gone anyway. Note that if_head
+ * is changing (in close_interfaces()). At this point, only monitor
+ * threads that haven't been reaped could be walking the interface
+ * list. They will "see" the change in if_head.
+ */
+ (void) mutex_unlock(&if_head_mtx);
+ if ((err = thr_join(ifp->if_thread, NULL, NULL)) != 0) {
+ dhcpmsg(LOG_ERR,
+ "Error %d while waiting for monitor %d of %s\n",
+ err, ifp->if_thread, ifp->nm);
+ }
+ (void) mutex_lock(&if_head_mtx);
+
+ /*
+ * Note: clients and their associated packet lists are freed prior
+ * to interfaces being closed.
+ */
+
+ /* free encode list */
+ free_encode_list(ifp->ecp);
+
+ /* display statistics */
+ disp_if_stats(ifp);
+
+ ifp->received = ifp->processed = 0;
+
+ (void) mutex_unlock(&ifp->ifp_mtx);
+ (void) mutex_destroy(&ifp->ifp_mtx);
+ tifp = ifp->next;
+ free(ifp);
+ return (tifp);
+}
+
+/*
+ * Close all interfaces, freeing up associated resources.
+ * This should only be called from main() during final exit.
+ */
+void
+close_interfaces(void)
+{
+ (void) mutex_lock(&if_head_mtx);
+ for (; if_head != NULL; if_head = close_interface(if_head)) {
+ if (verbose) {
+ dhcpmsg(LOG_INFO, "Closing interface: %s\n",
+ if_head->nm);
+ }
+ }
+ (void) mutex_unlock(&if_head_mtx);
+ (void) mutex_destroy(&if_head_mtx);
+}
+
+/*
+ * display IF info. Must be MT Safe - called from monitor threads.
+ */
+static void
+disp_if(IF *ifp)
+{
+ char ntoab[INET_ADDRSTRLEN];
+
+ dhcpmsg(LOG_INFO, "Thread Id: %04d - Monitoring Interface: %s *****\n",
+ ifp->if_thread, ifp->nm);
+ dhcpmsg(LOG_INFO, "MTU: %d\tType: %s\n", ifp->mtu, "SOCKET");
+ if ((ifp->flags & IFF_POINTOPOINT) == 0)
+ dhcpmsg(LOG_INFO, "Broadcast: %s\n",
+ inet_ntop(AF_INET, &ifp->bcast, ntoab, sizeof (ntoab)));
+ dhcpmsg(LOG_INFO, "Netmask: %s\n",
+ inet_ntop(AF_INET, &ifp->mask, ntoab, sizeof (ntoab)));
+ dhcpmsg(LOG_INFO, "Address: %s\n",
+ inet_ntop(AF_INET, &ifp->addr, ntoab, sizeof (ntoab)));
+}
+
+/*
+ * Display IF statistics.
+ */
+void
+disp_if_stats(IF *ifp)
+{
+ dhcpmsg(LOG_INFO, "Interface statistics for: %s **************\n",
+ ifp->nm);
+
+ dhcpmsg(LOG_INFO, "Pending DHCP offers: %d\n", ifp->offers);
+ dhcpmsg(LOG_INFO, "Total Packets Transmitted: %d\n", ifp->transmit);
+ dhcpmsg(LOG_INFO, "Total Packets Received: %d\n", ifp->received);
+ dhcpmsg(LOG_INFO, "Total Packet Duplicates: %d\n", ifp->duplicate);
+ dhcpmsg(LOG_INFO, "Total Packets Dropped: %d\n", ifp->dropped);
+ dhcpmsg(LOG_INFO, "Total Packets Processed: %d\n", ifp->processed);
+ dhcpmsg(LOG_INFO, "Total Protocol Errors: %d\n", ifp->errors);
+}
+
+/*
+ * Setup the arp cache so that IP address 'ia' will be temporarily
+ * bound to hardware address 'ha' of length 'len'. 'ia' is expected in
+ * network order.
+ *
+ * Returns: 0 if the arp entry was made, 1 otherwise.
+ */
+int
+set_arp(IF *ifp, struct in_addr *ia, uchar_t *ha, int len, uchar_t flags)
+{
+ struct sockaddr_in *si;
+ struct xarpreq arpreq;
+ int err = 0;
+ char scratch[DHCP_SCRATCH];
+ uint_t scratch_len;
+ char ntoab[INET_ADDRSTRLEN];
+
+ (void) memset((caddr_t)&arpreq, 0, sizeof (arpreq));
+
+ arpreq.xarp_ha.sdl_family = AF_LINK;
+
+ si = (struct sockaddr_in *)&arpreq.xarp_pa;
+ si->sin_family = AF_INET;
+ si->sin_addr = *ia; /* struct copy */
+
+ switch (flags) {
+ case DHCP_ARP_ADD:
+ if (debug) {
+ scratch_len = sizeof (scratch);
+ if (octet_to_hexascii(ha, len, scratch,
+ &scratch_len) != 0) {
+ dhcpmsg(LOG_DEBUG, "Cannot convert ARP \
+request to ASCII: %s: len: %d\n",
+ inet_ntop(AF_INET, ia,
+ ntoab, sizeof (ntoab)),
+ len);
+ } else {
+ dhcpmsg(LOG_DEBUG,
+ "Adding ARP entry: %s == %s\n",
+ inet_ntop(AF_INET, ia,
+ ntoab, sizeof (ntoab)),
+ scratch);
+ }
+ }
+ arpreq.xarp_flags = ATF_INUSE | ATF_COM;
+ (void) memcpy(LLADDR(&arpreq.xarp_ha), ha, len);
+ arpreq.xarp_ha.sdl_alen = len;
+
+ if (ioctl(ifp->descs[DSRVR_UCAST], SIOCSXARP, &arpreq) < 0) {
+ dhcpmsg(LOG_ERR,
+ "ADD: Cannot modify ARP table to add: %s\n",
+ inet_ntop(AF_INET, ia, ntoab, sizeof (ntoab)));
+ err = 1;
+ }
+ break;
+ case DHCP_ARP_DEL:
+ /* give it a good effort, but don't worry... */
+ (void) ioctl(ifp->descs[DSRVR_UCAST], SIOCDXARP, &arpreq);
+ break;
+ default:
+ err = 1;
+ break;
+ }
+
+ return (err);
+}
+
+/*
+ * Address and send a BOOTP reply packet appropriately. Does right thing
+ * based on BROADCAST flag. Also checks if giaddr field is set, and
+ * WE are the relay agent...
+ *
+ * Returns: 0 for success, nonzero otherwise (fatal)
+ */
+int
+send_reply(IF *ifp, PKT *pp, int len, struct in_addr *dstp)
+{
+ int local = B_FALSE;
+ struct sockaddr_in to;
+ struct in_addr if_in, cl_in;
+ char ntoab[INET_ADDRSTRLEN];
+
+ if (pp->giaddr.s_addr != 0L && ifp->addr.s_addr !=
+ pp->giaddr.s_addr) {
+ /* Going thru a relay agent */
+ to.sin_addr.s_addr = pp->giaddr.s_addr;
+ to.sin_port = htons(IPPORT_BOOTPS + port_offset);
+ } else {
+ to.sin_port = htons(IPPORT_BOOTPC + port_offset);
+
+ if (ntohs(pp->flags) & BCAST_MASK) {
+ /*
+ * TODO - what should we do if broadcast
+ * flag is set, but ptp connection?
+ */
+ if (debug)
+ dhcpmsg(LOG_INFO,
+ "Sending datagram to broadcast address.\n");
+ to.sin_addr.s_addr = INADDR_BROADCAST;
+ } else {
+ /*
+ * By default, we assume unicast!
+ */
+ to.sin_addr.s_addr = dstp->s_addr;
+
+ if (debug) {
+ dhcpmsg(LOG_INFO,
+ "Unicasting datagram to %s address.\n",
+ inet_ntop(AF_INET, dstp,
+ ntoab, sizeof (ntoab)));
+ }
+ if (ifp->addr.s_addr == pp->giaddr.s_addr) {
+ /*
+ * No doubt a reply packet which we, as
+ * the relay agent, are supposed to deliver.
+ * Local Delivery!
+ */
+ local = B_TRUE;
+ } else {
+ /*
+ * We can't use the giaddr field to
+ * determine whether the client is local
+ * or remote. Use the client's address,
+ * our interface's address, and our
+ * interface's netmask to make this
+ * determination.
+ */
+ if_in.s_addr = ntohl(ifp->addr.s_addr);
+ if_in.s_addr &= ntohl(ifp->mask.s_addr);
+ cl_in.s_addr = ntohl(dstp->s_addr);
+ cl_in.s_addr &= ntohl(ifp->mask.s_addr);
+ if (if_in.s_addr == cl_in.s_addr)
+ local = B_TRUE;
+ }
+
+ if (local) {
+ /*
+ * Local delivery. If we can make an
+ * ARP entry we'll unicast. But only in
+ * cases when we do have the chaddr handy.
+ * RFC2855 and IPoIB are cases that do not
+ * send chaddr and set hlen = 0. Identify
+ * such media by their htype, and rely on
+ * in-kernel ARP for them.
+ */
+ if ((ifp->flags & IFF_NOARP) == 0 &&
+ ((pp->htype == ARPHRD_IB) ||
+ (set_arp(ifp, dstp, pp->chaddr, pp->hlen,
+ DHCP_ARP_ADD) == 0))) {
+ to.sin_addr.s_addr = dstp->s_addr;
+ } else {
+ to.sin_addr.s_addr = INADDR_BROADCAST;
+ }
+ }
+ }
+ }
+ return (write_interface(ifp, pp, len, &to));
+}
+
+/*
+ * Free pkts
+ */
+void
+free_pktlist(dsvc_clnt_t *pcd)
+{
+ PKT_LIST *plp, *plp_next;
+ IF *ifp = pcd->ifp;
+
+ assert(_mutex_held(&pcd->pcd_mtx));
+
+ plp = pcd->pkthead;
+ while (plp != NULL) {
+ plp_next = plp;
+ plp = plp->next;
+ free_plp(plp_next);
+ ifp->dropped++;
+ pcd->pending--;
+ }
+ pcd->pkthead = NULL;
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/interfaces.h b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/interfaces.h
new file mode 100644
index 0000000000..273ca5d042
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/interfaces.h
@@ -0,0 +1,99 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1993-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _INTERFACES_H
+#define _INTERFACES_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define DSRVR_NUM_DESC 3 /* Number of socket descriptors */
+typedef enum {
+ DSRVR_LBCAST = 0, /* Limited broadcast recv descriptor */
+ DSRVR_DBCAST = 1, /* Directed broadcast recv descriptor */
+ DSRVR_UCAST = 2 /* Unicast send/recv descriptor */
+} dsrvr_socktype_t;
+
+typedef struct interfaces {
+ char nm[IFNAMSIZ]; /* Interface name */
+ unsigned int ifceno; /* Interface index */
+ short mtu; /* MTU of interface */
+ int descs[DSRVR_NUM_DESC]; /* recv/send sockets */
+ uint_t flags; /* interface flags */
+ struct in_addr bcast; /* interface broadcast */
+ struct in_addr mask; /* interface netmask */
+ struct in_addr addr; /* interface IP addr */
+ ENCODE *ecp; /* IF specific options */
+ uint_t transmit; /* # of transmitted pkts */
+ uint_t received; /* # of received pkts */
+ uint_t duplicate; /* # of duplicate pkts */
+ uint_t dropped; /* # of dropped pkts */
+ uint_t expired; /* # of expired pkts */
+ uint_t errors; /* # of protocol errors */
+ uint_t processed; /* # of processed pkts */
+ uint_t offers; /* # of pending offers */
+ thread_t if_thread; /* rcv service thread */
+ int thr_exit; /* sent when time to exit */
+ mutex_t ifp_mtx; /* mutex lock on this struct */
+ struct interfaces *next;
+} IF;
+
+#define DHCP_MON_SYSERRS 30 /* Max allowable interface errors */
+#define DHCP_MON_ERRINTVL 1 /* Time interval for IF errors (secs) */
+#define DHCP_MON_THRESHOLD 6 /* Max allowable pending pkts pcd */
+
+/*
+ * Pause interval (mins) if IF error threshold reached.
+ */
+#define DHCP_MON_SLEEP 5
+
+extern IF *if_head; /* head of monitored interfaces */
+extern mutex_t if_head_mtx; /* lock to protect interfaces list */
+extern char *interfaces; /* list of user-requested interfaces. */
+extern int open_interfaces(void);
+extern int write_interface(IF *, PKT *, int, struct sockaddr_in *);
+extern void close_interfaces(void);
+extern void detach_plp(dsvc_clnt_t *, PKT_LIST *);
+extern void free_pktlist(dsvc_clnt_t *);
+extern PKT_LIST *refresh_pktlist(dsvc_clnt_t *, PKT_LIST *);
+extern int set_arp(IF *, struct in_addr *, uchar_t *, int, uchar_t);
+
+extern int send_reply(IF *, PKT *, int, struct in_addr *);
+extern void disp_if_stats(IF *);
+
+extern int relay_agent(IF *, PKT_LIST *);
+extern void determine_network(IF *, PKT_LIST *, struct in_addr *,
+ struct in_addr *);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _INTERFACES_H */
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/logging.c b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/logging.c
new file mode 100644
index 0000000000..13a961def7
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/logging.c
@@ -0,0 +1,107 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <syslog.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <arpa/inet.h>
+#include <netinet/dhcp.h>
+#include <netdb.h>
+#include "dhcpd.h"
+
+static char *dhcp_msg_cats[] = {
+ /* L_ASSIGN */ "ASSIGN",
+ /* L_REPLY */ "EXTEND",
+ /* L_RELEASE */ "RELEASE",
+ /* L_DECLINE */ "DECLINE",
+ /* L_INFORM */ "INFORM",
+ /* L_NAK */ "NAK",
+ /* L_ICMP_ECHO */ "ICMP-ECHO",
+ /* L_RELAY_REQ */ "RELAY-SRVR",
+ /* L_RELAY_REP */ "RELAY-CLNT"
+};
+
+static char *protos[] = {
+ /* P_BOOTP */ "BOOTP",
+ /* P_DHCP */ "DHCP"
+};
+
+/*
+ * Transaction logging. Note - if we're in debug mode, the transactions
+ * are logged to the console!
+ *
+ * 'cip' and 'sip' are expected in network order.
+ */
+void
+logtrans(DHCP_PROTO p, DHCP_MSG_CATEGORIES type, time_t lease,
+ struct in_addr cip, struct in_addr sip, PKT_LIST *plp)
+{
+ char *cat, *proto, *t, *class_id;
+ uint_t maclen;
+ char class_idbuf[DHCP_MAX_OPT_SIZE];
+ char cidbuf[DHCP_MAX_OPT_SIZE];
+ char ntoabc[INET_ADDRSTRLEN], ntoabs[INET_ADDRSTRLEN];
+ char macbuf[(sizeof (((PKT *)NULL)->chaddr) * 2) + 1];
+
+ if (log_local < 0)
+ return;
+
+ proto = protos[p];
+ cat = dhcp_msg_cats[type];
+
+ (void) disp_cid(plp, cidbuf, sizeof (cidbuf));
+
+ class_id = get_class_id(plp, class_idbuf, sizeof (class_idbuf));
+
+ /* convert white space in class id into periods (.) */
+ if (class_id != NULL) {
+ for (t = class_id; *t != '\0'; t++) {
+ if (isspace(*t))
+ *t = '.';
+ }
+ } else
+ class_id = "N/A";
+
+ maclen = sizeof (macbuf);
+ macbuf[0] = '\0';
+ (void) octet_to_hexascii(plp->pkt->chaddr, plp->pkt->hlen, macbuf,
+ &maclen);
+
+ dhcpmsg(log_local | LOG_NOTICE, "%s %s %010d %010d %s %s %s %s %s\n",
+ proto, cat, time(NULL), lease,
+ inet_ntop(AF_INET, &cip, ntoabc, sizeof (ntoabc)),
+ inet_ntop(AF_INET, &sip, ntoabs, sizeof (ntoabs)),
+ cidbuf, class_id, macbuf);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/main.c b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/main.c
new file mode 100644
index 0000000000..6661e294a0
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/main.c
@@ -0,0 +1,1351 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * This file contains the argument parsing routines of the dhcpd daemon.
+ * It corresponds to the START state as spec'ed.
+ */
+
+/*
+ * Multithreading Notes:
+ * =====================
+ *
+ * For Enterprise DHCP scalability, libdhcpsvc has been made reentrant,
+ * and the server has been organized with a worker thread per client.
+ *
+ * There is a thread per configured interface which reads requests,
+ * determines if they are for this server, and appends them to the
+ * interface's PKT list. This thread spawns worker threads as needed
+ * to service incoming clients.
+ *
+ * The main thread creates a thread to handle signals. All subsequent threads
+ * (and the main thread) mask out all signals.
+ *
+ * The signal thread will deal with the -t option. This is done by
+ * waiting in sigtimedwait() for the timeout period, then spawning
+ * a reinitialization thread.
+ *
+ * dhcp: each client worker thread moves through the multi-packet
+ * state machine inline, performing icmp_echo_check() as needed.
+ * We prevent multiple threads from registering the same address for ICMP
+ * validation due to multiple DISCOVERS by reserving addresses in
+ * select_offer() to ensure we don't offer IP addresses currently
+ * undergoing ICMP validation.
+ *
+ * bootp: If automatic allocation is in effect,
+ * bootp behaves in the same fashion as dhcp_offer.
+ *
+ * Summary:
+ *
+ * Threads:
+ * 1) Main thread: Handles startup and shutdown chores.
+ *
+ * 2) Signal thread: The main thread creates this thread, and
+ * then masks out all signals. The signal thread waits on
+ * sigwait(), and processes all signals. It notifies the
+ * main thread of EINTR or ETERM via a global variable, which
+ * the main thread checks upon the exit to cond_wait.
+ * This thread is on it's own LWP, and is DETACHED | DAEMON.
+ * The thread function is sig_handle().
+ *
+ * 3) Interface threads: Each interface structure has a thread
+ * associated with it (created in open_interfaces) which is
+ * responsible for polling the interface, validating bootp
+ * packets received, and placing them on the client's
+ * PKT_LIST. The thread function is monitor_interface().
+ * When notified by the main thread via the thr_exit flag,
+ * the thread prints interface statistics for the interface,
+ * and then exits.
+ *
+ * 4) Client threads: Created as needed when the interface
+ * thread processes each incoming packet. These threads are
+ * created DETACHED and SUSPENDED by the interface thread,
+ * which then places each plp structure on the client's
+ * PKT_LIST, then continues the thread. A client thread exits
+ * when it has processed all incoming packets, and no
+ * deferred client work is queued. See per_dnet.h for
+ * more information on client locks.
+ *
+ * Locks:
+ * 1) if_head_mtx - Locks the global interface list.
+ *
+ * 2) ifp_mtx - Locks contents of the enclosed
+ * interface (IF) structure, including
+ * such things as thr_exit flag and
+ * statistics counters.
+ *
+ * 3) pkt_mtx - Locks PKT_LIST head list within the
+ * enclosed client (dsvc_clnt_t) struct.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <string.h>
+#include <syslog.h>
+#include <signal.h>
+#include <time.h>
+#include <limits.h>
+#include <sys/resource.h>
+#include <sys/fcntl.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/systeminfo.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <netinet/dhcp.h>
+#include <synch.h>
+#include <sys/param.h>
+#include <sys/sysmacros.h>
+#include <netdb.h>
+#include <dhcp_svc_confkey.h>
+#include "dhcpd.h"
+#include "per_dnet.h"
+#include "interfaces.h"
+#include <locale.h>
+#include <mtmalloc.h>
+#include <resolv.h>
+
+extern int optind, opterr;
+extern char *optarg;
+
+typedef struct dhcp_cops {
+ char *cop_name; /* opt name */
+ boolean_t cop_present; /* opt present? */
+ boolean_t (*cop_vinit)(struct dhcp_cops *, const char *);
+ union {
+ char *ucop_str;
+ boolean_t ucop_bool;
+ int ucop_num;
+ } dhcp_cops_un;
+#define cop_bool dhcp_cops_un.ucop_bool /* opt val: boolean_t */
+#define cop_num dhcp_cops_un.ucop_num /* opt val: int */
+#define cop_str dhcp_cops_un.ucop_str /* opt val: string */
+} DHCP_COP;
+
+static boolean_t bool_v(DHCP_COP *, const char *);
+static boolean_t uchar_v(DHCP_COP *, const char *);
+static boolean_t int_v(DHCP_COP *, const char *);
+static boolean_t uint_v(DHCP_COP *, const char *);
+static boolean_t str_v(DHCP_COP *, const char *);
+static boolean_t bootp_v(DHCP_COP *, const char *);
+static boolean_t logging_v(DHCP_COP *, const char *);
+static boolean_t runmode_v(DHCP_COP *, const char *);
+static int collect_options(int, char **);
+static void usage(void);
+static void local_closelog(void);
+static void *sig_handle(void *);
+
+#define C_RUNMODE 0
+#define C_DEBUG 1
+#define C_VERBOSE 2
+#define C_HOPS 3
+#define C_LOGGING 4
+#define C_IF 5
+#define C_OFFER 6
+#define C_ICMP 7
+#define C_RESCAN 8
+#define C_BOOTP 9
+#define C_CLIENT 10
+#define C_THREADS 11
+#define C_MINLRU 12
+#define C_RELAY 13
+#define C_NSUPDATE 14
+#define C_CACHE 15
+
+#define C_DBGPORT 16
+#define C_RENOG 17
+#define C_OWNER 18
+#ifdef DEBUG
+#define C_DBGNET 19
+#define C_LAST C_DBGNET
+#else /* DEBUG */
+#define C_LAST C_OWNER
+#endif /* DEBUG */
+
+
+static DHCP_COP options[C_LAST + 1] = {
+/* name Present? Verify func Value */
+/* ==== ======== =========== ===== */
+ /* Run mode / BOOTP relay agent selection option */
+{ DSVC_CK_RUN_MODE, B_FALSE, runmode_v, DSVC_CV_SERVER },
+ /* Generic daemon options */
+{ "DEBUG", B_FALSE, bool_v, B_FALSE },
+{ DSVC_CK_VERBOSE, B_FALSE, bool_v, B_FALSE },
+{ DSVC_CK_RELAY_HOPS, B_FALSE, uchar_v, (char *)DSVC_CV_HOPS },
+{ DSVC_CK_LOGGING_FACILITY, B_FALSE, logging_v, 0 },
+{ DSVC_CK_INTERFACES, B_FALSE, str_v, NULL },
+ /* DHCP server run mode options */
+{ DSVC_CK_OFFER_CACHE_TIMEOUT, B_FALSE, uint_v, (char *)DSVC_CV_OFFER_TTL },
+{ DSVC_CK_ICMP_VERIFY, B_FALSE, bool_v, (char *)B_TRUE },
+{ DSVC_CK_RESCAN_INTERVAL, B_FALSE, int_v, 0 },
+{ DSVC_CK_BOOTP_COMPAT, B_FALSE, bootp_v, NULL },
+{ DSVC_CK_MAX_CLIENTS, B_FALSE, int_v, (char *)0 },
+{ DSVC_CK_MAX_THREADS, B_FALSE, int_v, (char *)0 },
+{ DSVC_CK_LEASE_MIN_LRU, B_FALSE, int_v, (char *)DSVC_CV_MIN_LRU },
+ /* BOOTP relay agent options */
+{ DSVC_CK_RELAY_DESTINATIONS, B_FALSE, str_v, NULL },
+ /* Name service update timeout */
+{ DSVC_CK_NSU_TIMEOUT, B_FALSE, uint_v, (char *)DSVC_CV_NSU_TO },
+{ DSVC_CK_CACHE_TIMEOUT, B_FALSE, int_v, (char *)DSVC_CV_CACHE_TTL },
+{ DSVC_CK_DBG_PORT_OFFSET, B_FALSE, int_v, 0 },
+{ DSVC_CK_RENOG_INTERVAL, B_FALSE, uint_v, (char *)DSVC_CV_RENOG_INT },
+{ DSVC_CK_OWNER_IP, B_FALSE, str_v, NULL },
+#ifdef DEBUG
+{ DSVC_CK_DBG_MEMORY_NET, B_FALSE, str_v, NULL }
+#endif /* DEBUG */
+};
+
+#define DHCPCOP_NAME(x) (options[x].cop_name)
+#define DHCPCOP_PRES(x) (options[x].cop_present)
+#define DHCPCOP_VINIT(x, y) (options[x].cop_vinit(&options[x], y))
+#define DHCPCOP_BOOL(x) (options[x].cop_bool)
+#define DHCPCOP_NUM(x) (options[x].cop_num)
+#define DHCPCOP_STR(x) (options[x].cop_str)
+
+int debug;
+boolean_t verbose;
+boolean_t noping; /* Always ping before offer by default */
+boolean_t no_dhcptab; /* set if no dhcptab exists */
+boolean_t server_mode; /* set if running in server mode */
+static boolean_t bootp_compat; /* bootp compatibility */
+boolean_t be_automatic; /* set if bootp server should allocate IPs */
+uchar_t max_hops; /* max relay hops before discard */
+int log_local; /* syslog local facility number */
+int icmp_tries = DHCP_ICMP_ATTEMPTS; /* Number of attempts @ icmp_timeout */
+time_t off_secs; /* def ttl of an offer */
+time_t cache_secs; /* def ttl of netmask and table caches */
+time_t renog_secs; /* def wait time for secondary server timeout */
+time_t min_lru; /* def minimum lru of a reclaimed lease */
+time_t icmp_timeout = DHCP_ICMP_TIMEOUT; /* milliseconds to wait for response */
+time_t nsutimeout_secs; /* seconds to wait for a name service up date */
+struct in_addr server_ip; /* IP address of server's primary interface */
+struct in_addr *owner_ip; /* owner IP address list */
+static dhcp_confopt_t *dsp; /* Confopt for datastore access */
+dsvc_datastore_t datastore; /* Datastore for container access */
+int max_threads; /* maximum number of worker threads per net */
+int max_clients; /* maximum number of active clients per net */
+ushort_t port_offset = 0; /* offset to port for multiple server */
+int net_thresh = DHCP_NET_THRESHOLD; /* secs to keep pernet reference */
+int clnt_thresh = DHCP_CLIENT_THRESHOLD; /* secs to keep client reference */
+struct __res_state resolv_conf; /* DNS resolver data, includes domain-name */
+static int rescan_scale = DHCP_RESCAN_SCALE; /* secs to scale */
+#ifdef DEBUG
+char *dbg_net; /* Simulated debug net (see misc.c) */
+#endif /* DEBUG */
+
+static time_t rescan_interval; /* dhcptab rescan interval */
+
+
+/*
+ * This global is set by the signal handler when the main thread (and thus
+ * the daemon) should exit. We only use the mutex in this file, since we make
+ * the main thread wait on it becoming true using a condition variable.
+ */
+boolean_t time_to_go = B_FALSE;
+static mutex_t ttg_mtx;
+static cond_t ttg_cv;
+
+/* local syslog facilities */
+static int log_facilities[] = {
+ LOG_LOCAL0, LOG_LOCAL1, LOG_LOCAL2, LOG_LOCAL3, LOG_LOCAL4,
+ LOG_LOCAL5, LOG_LOCAL6, LOG_LOCAL7
+};
+
+time_t reinit_time; /* reinitialization time */
+static thread_t init_thread; /* reinitialization thread */
+
+int
+main(int argc, char *argv[])
+{
+ sigset_t set;
+ int i, ns, err = 0;
+ struct rlimit rl;
+ struct hostent *hp;
+ thread_t sigthread;
+ int nss_lwp = 0;
+ int32_t ncpus;
+ char scratch[MAXHOSTNAMELEN + 1];
+ char ntoab[INET_ADDRSTRLEN];
+ char *ownerip_args, *sip, *lasts;
+ int np = 1;
+ struct in_addr *oip;
+
+#ifdef DEBUG
+ mallocctl(MTDEBUGPATTERN, 1);
+ mallocctl(MTINITBUFFER, 1);
+#endif /* DEBUG */
+
+ (void) setlocale(LC_ALL, "");
+
+#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
+#define TEXT_DOMAIN "SYS_TEXT"
+#endif /* ! TEXT_DOMAIN */
+
+ (void) textdomain(TEXT_DOMAIN);
+
+ if (geteuid() != (uid_t)0) {
+ (void) fprintf(stderr, gettext("Must be 'root' to run %s.\n"),
+ DHCPD);
+ return (EPERM);
+ }
+
+ if ((err = collect_options(argc, argv)) != 0) {
+ if (errno == EAGAIN) {
+ (void) fprintf(stderr, gettext("DHCP daemon config "
+ "file locked.\n"));
+ err = EAGAIN;
+ } else {
+ usage();
+ err = EINVAL;
+ }
+ return (err);
+ }
+
+ /* Deal with run mode generic options first */
+ debug = DHCPCOP_BOOL(C_DEBUG);
+ verbose = DHCPCOP_BOOL(C_VERBOSE);
+ max_hops = DHCPCOP_NUM(C_HOPS);
+ interfaces = DHCPCOP_STR(C_IF);
+ bootp_compat = DHCPCOP_PRES(C_BOOTP); /* present then yes */
+ max_clients = DHCPCOP_NUM(C_CLIENT);
+ max_threads = DHCPCOP_NUM(C_THREADS);
+ log_local = DHCPCOP_PRES(C_LOGGING) ?
+ log_facilities[DHCPCOP_NUM(C_LOGGING)] : -1;
+
+ server_mode = (strcasecmp(DHCPCOP_STR(C_RUNMODE), DSVC_CV_SERVER) == 0);
+ if (server_mode) {
+
+ if (bootp_compat) {
+ be_automatic = (strcasecmp(DHCPCOP_STR(C_BOOTP),
+ DSVC_CV_AUTOMATIC) == 0);
+ }
+
+ if (DHCPCOP_BOOL(C_ICMP) == B_FALSE) {
+ (void) fprintf(stderr, gettext("\nWARNING: Disabling \
+duplicate IP address detection!\n\n"));
+ noping = B_TRUE;
+ } else {
+ noping = B_FALSE;
+ }
+
+ off_secs = DHCPCOP_NUM(C_OFFER);
+ cache_secs = DHCPCOP_NUM(C_CACHE);
+ renog_secs = DHCPCOP_NUM(C_RENOG);
+ min_lru = DHCPCOP_NUM(C_MINLRU);
+ port_offset = DHCPCOP_NUM(C_DBGPORT); /* Private debug flag */
+#ifdef DEBUG
+ dbg_net = DHCPCOP_STR(C_DBGNET);
+#endif /* DEBUG */
+ nsutimeout_secs = DHCPCOP_PRES(C_NSUPDATE) ?
+ DHCPCOP_NUM(C_NSUPDATE) : DHCP_NO_NSU;
+
+ if ((rescan_interval = DHCPCOP_NUM(C_RESCAN)) != 0) {
+ rescan_interval *= rescan_scale;
+ }
+
+ /* Load current datastore, if any. */
+ if (dsp == NULL)
+ return (1);
+ if ((i = confopt_to_datastore(dsp, &datastore)) !=
+ DSVC_SUCCESS) {
+ (void) fprintf(stderr, gettext(
+ "WARNING: Invalid datastore: %s\n"),
+ dhcpsvc_errmsg(i));
+ return (1);
+ }
+ free_dsvc_conf(dsp);
+
+ ns = status_dd(&datastore);
+ if (ns != DSVC_SUCCESS) {
+ (void) fprintf(stderr, gettext(
+ "Datastore status error: %s\n"),
+ dhcpsvc_errmsg(ns));
+ return (1);
+ }
+ } else {
+ if (!DHCPCOP_PRES(C_RELAY)) {
+ (void) fprintf(stderr, gettext("Missing BOOTP "
+ "relay destinations (%s)\n"),
+ DSVC_CK_RELAY_DESTINATIONS);
+ return (1);
+ }
+ if ((err = relay_agent_init(DHCPCOP_STR(C_RELAY))) != 0)
+ return (err);
+ }
+
+ if (!debug) {
+ /* Daemon (background, detach from controlling tty). */
+ switch (fork()) {
+ case -1:
+ (void) fprintf(stderr,
+ gettext("Daemon cannot fork(): %s\n"),
+ strerror(errno));
+ return (errno);
+ case 0:
+ /* child */
+ break;
+ default:
+ /* parent */
+ return (0);
+ }
+
+ closefrom(0); /* close all open files */
+ errno = 0; /* clean up benign bad file no error */
+ (void) open("/dev/null", O_RDONLY, 0);
+ (void) dup2(0, 1);
+ (void) dup2(0, 2);
+
+ /* set NOFILE to unlimited */
+ rl.rlim_cur = rl.rlim_max = RLIM_INFINITY;
+ if ((err = setrlimit(RLIMIT_NOFILE, &rl)) < 0) {
+ dhcpmsg(LOG_ERR, "Cannot set open file limit: %s\n",
+ strerror(errno));
+ return (err);
+ }
+
+ /* Detach console */
+ (void) setsid();
+
+ (void) openlog(DHCPD, LOG_PID, LOG_DAEMON);
+ if (verbose)
+ dhcpmsg(LOG_INFO, "Daemon started.\n");
+ }
+
+ /*
+ * Block all signals in main thread - threads created will also
+ * ignore signals.
+ */
+ (void) sigfillset(&set);
+
+ (void) sigdelset(&set, SIGABRT); /* allow for user abort */
+
+ (void) thr_sigsetmask(SIG_SETMASK, &set, NULL);
+
+ /*
+ * Create signal handling thread.
+ * Due to threads library limitations, the main program
+ * thread currently cannot function as the signal thread, and
+ * must be a bound thread.
+ */
+ if ((err = thr_create(NULL, 0, sig_handle, NULL, THR_NEW_LWP |
+ THR_DAEMON | THR_BOUND | THR_DETACHED, &sigthread)) != 0) {
+ (void) fprintf(stderr,
+ gettext("Cannot start signal handling thread, error: %d\n"),
+ err);
+ return (err);
+ }
+#ifdef DEBUG
+ (void) fprintf(stderr,
+ gettext("Started signal handling thread: %d\n"), sigthread);
+#endif /* DEBUG */
+
+ /* Save away the IP address associated with our HOSTNAME. */
+
+#ifdef DEBUG
+ /* Debugging: allow shared use of difficult to create databases. */
+ if (getenv("DHCP_HOSTNAME") != NULL)
+ (void) strcpy(scratch, getenv("DHCP_HOSTNAME"));
+ else
+#endif /* DEBUG */
+ (void) sysinfo(SI_HOSTNAME, scratch, MAXHOSTNAMELEN + 1);
+
+ if ((hp = gethostbyname(scratch)) != NULL &&
+ hp->h_addrtype == AF_INET &&
+ hp->h_length == sizeof (struct in_addr)) {
+ (void) memcpy((char *)&server_ip, hp->h_addr_list[0],
+ sizeof (server_ip));
+ /*
+ * server_ip is supplemented by owner_ip list
+ * the first in the list of owner_ips always = server_ip
+ */
+ owner_ip = smalloc((sizeof (struct in_addr)) * (np + 1));
+ (void) memcpy(owner_ip, &server_ip, sizeof (server_ip));
+
+ if (DHCPCOP_PRES(C_OWNER)) {
+ ownerip_args = DHCPCOP_STR(C_OWNER);
+ sip = strtok_r(ownerip_args, ",", &lasts);
+ while (sip != NULL) {
+ owner_ip = srealloc(owner_ip,
+ (sizeof (struct in_addr)) * (np + 2));
+ oip = owner_ip + np;
+ if (inet_pton(AF_INET, sip, oip) == 0 ||
+ oip->s_addr == INADDR_ANY) {
+ dhcpmsg(LOG_ERR,
+ "Invalid OWNER IP address %s\n",
+ sip);
+ sip = strtok_r(NULL, ",", &lasts);
+ continue;
+ }
+ np++;
+ sip = strtok_r(NULL, ",", &lasts);
+ }
+ }
+ oip = owner_ip + np;
+ oip->s_addr = INADDR_ANY;
+ } else {
+ dhcpmsg(LOG_ERR,
+ "Cannot determine server hostname/IP address.\n");
+ local_closelog();
+ return (1);
+ }
+ if (res_ninit(&resolv_conf) == -1)
+ dhcpmsg(LOG_ERR, "Cannot acquire resolver configuration.\n");
+ i = 0;
+ if (server_mode) {
+ /*
+ * Calculate limits to maximum concurrency. Special values:
+ * If max_{threads,clients} == 0, calculate limits
+ * based on cpu and memory.
+ * Else if max_{threads,clients} is set to -1, run without
+ * concurrency limits.
+ * Else use supplied limits.
+ */
+ if ((ncpus = sysconf(_SC_NPROCESSORS_CONF)) < 0)
+ ncpus = 1;
+
+ if (max_clients == 0)
+ max_clients = DHCP_DEFAULT_CLIENTS * ncpus;
+
+ /* Require a minimum number of client structs. */
+ if (max_clients != -1 && max_clients < DHCP_MIN_CLIENTS) {
+ max_clients = DHCP_MIN_CLIENTS;
+ dhcpmsg(LOG_ERR, "Warning: adjusting MAX_CLIENTS"
+ " to minimum value %d\n", max_clients);
+ }
+
+ if (max_threads == 0)
+ max_threads = max_clients/4;
+
+ /*
+ * 4321342: Alloc additional lwps for unbound library threads.
+ * Remove this performance workaround when bug fixed.
+ */
+ if (max_clients != 0)
+ nss_lwp = max_clients/8;
+ if (nss_lwp <= 0 || nss_lwp > DHCP_NSS_LWP)
+ nss_lwp = DHCP_NSS_LWP;
+ i = thr_setconcurrency(nss_lwp);
+ }
+
+ if (verbose) {
+ if (i != 0)
+ dhcpmsg(LOG_ERR, "Error setting concurrency %d: %s\n",
+ max_threads, strerror(i));
+ dhcpmsg(LOG_INFO, "Daemon Version: %s\n", DAEMON_VERS);
+ dhcpmsg(LOG_INFO, "Maximum relay hops: %d\n", max_hops);
+ if (log_local > -1) {
+ dhcpmsg(LOG_INFO,
+ "Transaction logging to %s enabled.\n",
+ debug ? "console" : "syslog");
+ }
+ if (server_mode) {
+ dhcpmsg(LOG_INFO, "Run mode is: DHCP Server Mode.\n");
+ dhcpmsg(LOG_INFO, "Datastore resource: %s\n",
+ datastore.d_resource ?
+ datastore.d_resource : "");
+ dhcpmsg(LOG_INFO, "Location: %s\n",
+ datastore.d_location ?
+ datastore.d_location : "");
+ dhcpmsg(LOG_INFO, "DHCP offer TTL: %d\n", off_secs);
+ if (bootp_compat)
+ dhcpmsg(LOG_INFO,
+ "BOOTP compatibility enabled.\n");
+ if (rescan_interval != 0) {
+ dhcpmsg(LOG_INFO,
+ "Dhcptab rescan interval: %d minutes.\n",
+ rescan_interval / rescan_scale);
+ }
+ dhcpmsg(LOG_INFO, "ICMP validation timeout: %d "
+ "milliseconds, Attempts: %d.\n", icmp_timeout,
+ icmp_tries);
+ if (nsutimeout_secs != DHCP_NO_NSU) {
+ dhcpmsg(LOG_INFO, "Name service update "
+ "enabled, timeout: %d seconds\n",
+ nsutimeout_secs);
+ }
+ for (oip = owner_ip; oip->s_addr != INADDR_ANY; oip++)
+ dhcpmsg(LOG_INFO, "Owner IP address: %s\n",
+ inet_ntop(AF_INET, oip, ntoab,
+ sizeof (ntoab)));
+ dhcpmsg(LOG_INFO, "Maximum concurrent clients: %d\n",
+ max_clients);
+ dhcpmsg(LOG_INFO, "Maximum threads: %d\n", max_threads);
+ } else
+ dhcpmsg(LOG_INFO, "Run mode is: Relay Agent Mode.\n");
+ }
+
+ (void) mutex_init(&ttg_mtx, USYNC_THREAD, 0);
+ (void) cond_init(&ttg_cv, USYNC_THREAD, 0);
+
+ if (server_mode) {
+
+ if (initntab() != 0) {
+ dhcpmsg(LOG_ERR, "Cannot allocate per network hash "
+ "table.\n");
+ local_closelog();
+ (void) mutex_destroy(&ttg_mtx);
+ (void) cond_destroy(&ttg_cv);
+ return (1);
+ }
+
+ if (initmtab() != 0) {
+ dhcpmsg(LOG_ERR, "Cannot allocate macro hash table.\n");
+ local_closelog();
+ (void) mutex_destroy(&ttg_mtx);
+ (void) cond_destroy(&ttg_cv);
+ return (1);
+ }
+
+ if ((err = checktab()) != 0 ||
+ (err = readtab(NEW_DHCPTAB)) != 0) {
+ if (err == ENOENT) {
+ no_dhcptab = B_TRUE;
+ } else {
+ dhcpmsg(LOG_ERR,
+ "Error reading macro table.\n");
+ local_closelog();
+ (void) mutex_destroy(&ttg_mtx);
+ (void) cond_destroy(&ttg_cv);
+ return (err);
+ }
+ } else
+ no_dhcptab = B_FALSE;
+ }
+
+ if ((err = open_interfaces()) != 0) {
+ local_closelog();
+ return (err);
+ }
+
+ /*
+ * While forever, handle signals and dispatch them.
+ */
+ while (!time_to_go) {
+ (void) mutex_lock(&ttg_mtx);
+ while (!time_to_go)
+ (void) cond_wait(&ttg_cv, &ttg_mtx);
+ (void) mutex_unlock(&ttg_mtx);
+ }
+
+ /* Daemon terminated. */
+ if (server_mode) {
+ resettab(B_TRUE);
+ close_clnts(); /* reaps client threads */
+ }
+
+ close_interfaces(); /* reaps monitor threads */
+ local_closelog();
+ (void) fflush(NULL);
+ (void) mutex_destroy(&ttg_mtx);
+ (void) cond_destroy(&ttg_cv);
+ return (err);
+}
+
+/*
+ * Signal handler routine. All signals handled by calling thread.
+ */
+/* ARGSUSED */
+static void *
+sig_handle(void *arg)
+{
+ int err;
+ int sig;
+ sigset_t set;
+ char buf[SIG2STR_MAX];
+ timespec_t ts;
+ siginfo_t si;
+
+ (void) sigfillset(&set); /* catch all signals */
+
+ ts.tv_sec = rescan_interval == 0 ? DEFAULT_LEASE : rescan_interval;
+ ts.tv_nsec = 0L;
+
+ /* wait for a signal */
+ while (!time_to_go) {
+ switch (sig = sigtimedwait(&set, &si, &ts)) {
+ case -1:
+ if (rescan_interval == 0 || errno != EAGAIN)
+ break;
+ /*FALLTHRU*/
+ case SIGHUP:
+ /*
+ * Create reinitialization thread.
+ */
+ if (init_thread != NULL)
+ break;
+
+ if ((err = thr_create(NULL, 0, reinitialize,
+ &init_thread, THR_BOUND | THR_DETACHED,
+ &init_thread)) != 0) {
+ (void) fprintf(stderr, gettext(
+ "Cannot start reinit thread, error: %d\n"),
+ err);
+ }
+ break;
+ case SIGTERM:
+ /* FALLTHRU */
+ case SIGINT:
+ (void) sig2str(sig, buf);
+ dhcpmsg(LOG_NOTICE, "Signal: %s received...Exiting\n",
+ buf);
+ time_to_go = B_TRUE;
+ break;
+ default:
+ if (verbose) {
+ (void) sig2str(sig, buf);
+ dhcpmsg(LOG_INFO,
+ "Signal: %s received...Ignored\n",
+ buf);
+ }
+ break;
+ }
+ if (time_to_go) {
+ (void) mutex_lock(&ttg_mtx);
+ (void) cond_signal(&ttg_cv);
+ (void) mutex_unlock(&ttg_mtx);
+ break;
+ }
+ }
+ return ((void *)sig); /* NOTREACHED */
+}
+
+static void
+usage(void)
+{
+ (void) fprintf(stderr, gettext(
+ "%s:\n\n\tCommon: [-d] [-v] [-i interface, ...] "
+ "[-h hops] [-l local_facility]\n\n\t"
+ "Server: [-n] [-t rescan_interval] [-o DHCP_offer_TTL]\n\t\t"
+ "[ -b automatic | manual]\n\n\t"
+ "Relay Agent: -r IP | hostname, ...\n"), DHCPD);
+}
+
+static void
+local_closelog(void)
+{
+ dhcpmsg(LOG_INFO, "Daemon terminated.\n");
+ if (!debug)
+ closelog();
+}
+
+/*
+ * Given a received BOOTP packet, generate an appropriately sized,
+ * and generically initialized BOOTP packet.
+ */
+PKT *
+gen_bootp_pkt(int size, PKT *srcpktp)
+{
+ PKT *pkt = (PKT *)smalloc(size);
+
+ pkt->htype = srcpktp->htype;
+ pkt->hlen = srcpktp->hlen;
+ pkt->xid = srcpktp->xid;
+ pkt->secs = srcpktp->secs;
+ pkt->flags = srcpktp->flags;
+ pkt->giaddr.s_addr = srcpktp->giaddr.s_addr;
+ (void) memcpy(pkt->cookie, srcpktp->cookie, 4);
+ (void) memcpy(pkt->chaddr, srcpktp->chaddr, srcpktp->hlen);
+
+ return (pkt);
+}
+
+/*
+ * Points field serves to identify those packets whose allocated size
+ * and address is not represented by the address in pkt.
+ */
+void
+free_plp(PKT_LIST *plp)
+{
+ char *tmpp;
+
+#ifdef DEBUG
+ dhcpmsg(LOG_DEBUG,
+"%04d: free_plp(0x%x)pkt(0x%x)len(%d)next(0x%x)prev(0x%x)\n",
+ thr_self(), plp, plp->pkt, plp->len,
+ plp->next, plp->prev);
+#endif /* DEBUG */
+ if (plp->pkt) {
+ if (plp->offset != 0)
+ tmpp = (char *)((uint_t)plp->pkt - plp->offset);
+ else
+ tmpp = (char *)plp->pkt;
+ free(tmpp);
+ }
+ free(plp);
+ plp = NULL;
+}
+
+/*
+ * Validate boolean is "B_TRUE" or "B_FALSE".
+ * Returns B_TRUE if successful, B_FALSE otherwise.
+ */
+static boolean_t
+bool_v(DHCP_COP *dp, const char *option)
+{
+ boolean_t i;
+
+ assert(dp != NULL && option != NULL);
+
+ if (strcasecmp(option, DSVC_CV_TRUE) == 0) {
+ i = B_TRUE;
+ } else if (strcasecmp(option, DSVC_CV_FALSE) == 0) {
+ i = B_FALSE;
+ } else {
+ return (B_FALSE); /* huh? */
+ }
+ dp->cop_bool = i;
+ return (B_TRUE);
+}
+
+/*
+ * Validate uchar data.
+ * Returns B_TRUE if successful, B_FALSE otherwise.
+ */
+static boolean_t
+uchar_v(DHCP_COP *dp, const char *option)
+{
+ if (dp == NULL || option == NULL || !isdigit(*option))
+ return (B_FALSE);
+ dp->cop_num = strtoul(option, 0L, 0L);
+ if (dp->cop_num < 0 || dp->cop_num > 0xFF)
+ return (B_FALSE);
+ return (B_TRUE);
+}
+
+/*
+ * Validate integer data.
+ * Returns B_TRUE if successful, B_FALSE otherwise.
+ */
+static boolean_t
+int_v(DHCP_COP *dp, const char *option)
+{
+ if (dp != NULL && option != NULL) {
+ dp->cop_num = strtol(option, NULL, 0L);
+ if (errno == 0)
+ return (B_TRUE);
+ }
+ return (B_FALSE);
+}
+
+/*
+ * Validate unsigned integer data.
+ * Returns B_TRUE if successful, B_FALSE otherwise.
+ */
+static boolean_t
+uint_v(DHCP_COP *dp, const char *option)
+{
+ if (dp != NULL && option != NULL) {
+ dp->cop_num = strtoul(option, NULL, 0L);
+ if (errno == 0)
+ return (B_TRUE);
+ }
+ return (B_FALSE);
+}
+
+/*
+ * Check if value is a string.
+ * Returns B_TRUE if successful, B_FALSE otherwise
+ */
+static boolean_t
+str_v(DHCP_COP *dp, const char *option)
+{
+ if (dp == NULL || option == NULL ||
+ (dp->cop_str = strdup(option)) == NULL) {
+ return (B_FALSE);
+ }
+ return (B_TRUE);
+}
+
+/*
+ * Validate bootp compatibility options. Must be "automatic" or
+ * "manual".
+ * Returns B_TRUE if successful, B_FALSE otherwise.
+ */
+static boolean_t
+bootp_v(DHCP_COP *dp, const char *option)
+{
+ if (dp == NULL || option == NULL)
+ return (B_FALSE);
+
+ if ((strcasecmp(option, DSVC_CV_AUTOMATIC) == 0 ||
+ strcasecmp(option, DSVC_CV_MANUAL) == 0) &&
+ (dp->cop_str = strdup(option)) != NULL) {
+ return (B_TRUE);
+ }
+ return (B_FALSE);
+}
+
+/*
+ * Validate logging facility. Must be a number between 0 and 7 inclusive.
+ * Returns B_TRUE if successful, B_FALSE otherwise.
+ */
+static boolean_t
+logging_v(DHCP_COP *dp, const char *option)
+{
+ if (uint_v(dp, option) && dp->cop_num <= 7)
+ return (B_TRUE);
+
+ (void) fprintf(stderr, gettext("Syslog local facility must be in the "
+ "range of 0 through 7.\n"));
+ return (B_FALSE);
+}
+
+/*
+ * Validate run mode. Must be "server" or "relay".
+ * Returns B_TRUE if successful, B_FALSE otherwise
+ */
+static boolean_t
+runmode_v(DHCP_COP *dp, const char *option)
+{
+ if (dp == NULL || option == NULL)
+ return (B_FALSE);
+ if ((strcasecmp(option, DSVC_CV_SERVER) == 0 ||
+ strcasecmp(option, DSVC_CV_RELAY) == 0) &&
+ (dp->cop_str = strdup(option)) != NULL) {
+ return (B_TRUE);
+ }
+ return (B_FALSE);
+}
+
+/*
+ * Initialize options table based upon config file settings or command
+ * line flags. Handle all option inter-dependency checking here. No value
+ * checking is done here.
+ *
+ * Returns 0 if successful, nonzero otherwise.
+ */
+static int
+collect_options(int count, char **args)
+{
+ int c, i, j;
+ char *mode;
+
+ /* First, load the configuration options from the file, if present. */
+ for (errno = 0, i = 0; i < DHCP_RDCOP_RETRIES &&
+ read_dsvc_conf(&dsp) < 0; i++) {
+ (void) fprintf(stderr, gettext(
+ "WARNING: DHCP daemon config file: %s\n"),
+ strerror(errno));
+ if (errno == EAGAIN) {
+ /* file's busy, wait one second and try again */
+ (void) sleep(1);
+ } else
+ break;
+ }
+ if (errno == EAGAIN)
+ return (EAGAIN);
+
+ /* set default RUN_MODE to server if it wasn't found in the file */
+ if (query_dsvc_conf(dsp, DSVC_CK_RUN_MODE, &mode) < 0) {
+ if (errno == ENOENT) {
+ if (add_dsvc_conf(&dsp, DSVC_CK_RUN_MODE,
+ DSVC_CV_SERVER) != 0)
+ return (errno);
+ }
+ } else
+ free(mode);
+
+ /*
+ * Second, pick up the user's preferences from the command line,
+ * which modify the config file settings.
+ */
+ while ((c = getopt(count, args, "dnvh:o:r:b:i:t:l:")) != -1) {
+
+ boolean_t relay_mode = B_FALSE;
+ char *key = NULL, *value = NULL;
+
+ switch (c) {
+ case 'd':
+ key = "DEBUG";
+ value = DSVC_CV_TRUE;
+ break;
+ case 'n':
+ key = DSVC_CK_ICMP_VERIFY;
+ value = DSVC_CV_FALSE;
+ break;
+ case 'v':
+ key = DSVC_CK_VERBOSE;
+ value = DSVC_CV_TRUE;
+ break;
+ case 'r':
+ key = DSVC_CK_RELAY_DESTINATIONS;
+ value = optarg;
+ relay_mode = B_TRUE;
+ break;
+ case 'b':
+ key = DSVC_CK_BOOTP_COMPAT;
+ value = optarg;
+ break;
+ case 'h':
+ key = DSVC_CK_RELAY_HOPS;
+ value = optarg;
+ break;
+ case 'i':
+ key = DSVC_CK_INTERFACES;
+ value = optarg;
+ break;
+ case 'o':
+ key = DSVC_CK_OFFER_CACHE_TIMEOUT;
+ value = optarg;
+ break;
+ case 't':
+ key = DSVC_CK_RESCAN_INTERVAL;
+ value = optarg;
+ break;
+ case 'l':
+ key = DSVC_CK_LOGGING_FACILITY;
+ value = optarg;
+ break;
+ default:
+ (void) fprintf(stderr, gettext("Unknown option: %c\n"),
+ c);
+ return (EINVAL);
+ }
+
+ /*
+ * Create parameters if they don't exist, or replace
+ * their value if they exist.
+ */
+ if (replace_dsvc_conf(&dsp, key, value) < 0)
+ return (errno);
+
+ if (relay_mode) {
+ if (replace_dsvc_conf(&dsp, DSVC_CK_RUN_MODE,
+ DSVC_CV_RELAY) < 0)
+ return (errno);
+ }
+ }
+
+ if (optind < count) {
+
+ /* get all unused arguments */
+ (void) fprintf(stderr, "%s: unexpected argument(s) \"",
+ args[0]);
+ for (; optind < count; optind++) {
+ if (args[optind][0] != '-')
+ (void) fprintf(stderr, " %s", args[optind]);
+ else
+ break;
+ }
+ (void) fprintf(stderr, "\"; Aborting\n");
+ return (EINVAL);
+ }
+
+ /* load options table, validating value portions of present as we go */
+ for (i = 0; dsp != NULL && dsp[i].co_key != NULL; i++) {
+ if (dsp[i].co_type != DHCP_KEY)
+ continue; /* comment */
+ for (j = 0; j <= C_LAST; j++) {
+ if (strcasecmp(DHCPCOP_NAME(j),
+ dsp[i].co_key) == 0) {
+ DHCPCOP_PRES(j) = B_TRUE;
+ if (DHCPCOP_VINIT(j, dsp[i].co_value))
+ break;
+ else {
+ (void) fprintf(stderr, gettext(
+ "Invalid value for option: %s\n"),
+ DHCPCOP_NAME(j));
+ return (EINVAL);
+ }
+ }
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * monitor_client: worker thread from pool created for each network.
+ * We loop through and process one packet. Relay agent tasks are handled by
+ * the per-interface threads, thus we should only be dealing with bootp/dhcp
+ * server bound packets here.
+ *
+ * The worker thread treats the client packet lists as
+ * "stacks", or FIFO objects. We do this so that we get
+ * the latest, equivalent request from the client before
+ * responding, thus keeping the chance of responding to
+ * moldy requests to an absolute minimum.
+ *
+ * Performance: a pool of threads are used, to avoid thread startup/teardown.
+ * Per-interface threads keep track of clients who cannot be serviced
+ * due to a lack of threads. After completing the current request, threads
+ * look for other work to do, before suspending and waiting to be
+ * continued when work is available.
+ */
+void *
+monitor_client(void *arg)
+{
+ dsvc_thr_t *thrp = (dsvc_thr_t *)arg;
+ dsvc_clnt_t *pcd;
+ dsvc_dnet_t *pnd;
+ dsvc_pendclnt_t *workp;
+ IF *ifp;
+ PKT_LIST *plp = NULL;
+ int nclients;
+ boolean_t delete;
+ uint_t flags = 0;
+
+ /*
+ * Initialize variables.
+ *
+ * Due to a possible race between suspend and continue, we must
+ * provide a positive indication that the thread has continued to
+ * the per-interface thread.
+ */
+ (void) mutex_lock(&thrp->thr_mtx);
+ pcd = thrp->thr_pcd;
+ thrp->thr_pcd = NULL;
+ (void) mutex_unlock(&thrp->thr_mtx);
+
+ ifp = pcd->ifp;
+ pnd = pcd->pnd;
+
+ /*
+ * The per-interface thread leaves the client struct open,
+ * so it cannot be garbage-collected in the interim.
+ * Keep track of when we must release client structs.
+ */
+ (void) mutex_lock(&pnd->thr_mtx);
+ nclients = pnd->nclients;
+ (void) mutex_unlock(&pnd->thr_mtx);
+
+ for (; (flags & DHCP_THR_EXITING) == 0; ) {
+ if (pcd == NULL) {
+ /*
+ * No work. Place thread struct on free list
+ * if it isn't already, and suspend
+ * until new work is available.
+ */
+ (void) mutex_lock(&thrp->thr_mtx);
+ if ((thrp->thr_flags & DHCP_THR_LIST) == 0) {
+ thrp->thr_flags |= DHCP_THR_LIST;
+ thrp->thr_pcd = NULL;
+ thrp->thr_next = NULL;
+
+ (void) mutex_lock(&pnd->thr_mtx);
+ if (pnd->thrhead != NULL) {
+ pnd->thrtail->thr_next = thrp;
+ } else {
+ pnd->thrhead = thrp;
+ }
+ pnd->thrtail = thrp;
+ (void) mutex_unlock(&pnd->thr_mtx);
+ }
+
+ /* Wait for new work. */
+ (void) cond_wait(&thrp->thr_cv, &thrp->thr_mtx);
+
+ /*
+ * Resume with new client if any.
+ */
+ pcd = thrp->thr_pcd;
+ thrp->thr_pcd = NULL;
+ flags = thrp->thr_flags;
+ (void) mutex_unlock(&thrp->thr_mtx);
+ continue;
+ }
+
+ (void) mutex_lock(&pcd->pkt_mtx);
+ /*
+ * Remove the first packet from the list
+ */
+ plp = pcd->pkthead;
+ if (plp != NULL) {
+
+ detach_plp(pcd, plp);
+ pcd->pending--;
+
+ /*
+ * See if there's a later one
+ * exchanging this plp for that one.
+ */
+ plp = refresh_pktlist(pcd, plp);
+ }
+ (void) mutex_unlock(&pcd->pkt_mtx);
+
+ (void) mutex_lock(&pcd->pcd_mtx);
+ if (plp == NULL || (pcd->flags & DHCP_PCD_CLOSING) != 0) {
+
+ if (plp) {
+ free_plp(plp); /* Free the packet. */
+ plp = NULL;
+ }
+
+ /*
+ * No work remaining for this client. Release,
+ * and check for other deferred clients on the
+ * per net work list.
+ */
+ pcd->flags &= ~DHCP_PCD_WORK;
+
+ /*
+ * Housekeeping: delete pcd immediately if above
+ * threshold and no offer has been made, or offer
+ * has been completed. Only perform deletion if no
+ * other thread has.
+ */
+ delete = B_FALSE;
+ if (max_clients != -1 &&
+ (pcd->flags & DHCP_PCD_CLOSING) == 0) {
+ if (nclients >=
+ max_clients - DHCP_MINFREE_CLIENTS &&
+ pcd->off_ip.s_addr == htonl(INADDR_ANY)) {
+
+ /* Remove clients without offers. */
+ pcd->flags |= DHCP_PCD_CLOSING;
+ delete = B_TRUE;
+
+ } else if (nclients > max_clients/2 &&
+ (pcd->state == ACK ||
+ (pcd->state == REQUEST &&
+ pcd->off_ip.s_addr == htonl(INADDR_ANY)))) {
+
+ /* Remove completed clients. */
+ pcd->flags |= DHCP_PCD_CLOSING;
+ delete = B_TRUE;
+
+ } else if (pcd->state == RELEASE ||
+ pcd->state == DECLINE) {
+
+ /* Remove freed clients. */
+ pcd->flags |= DHCP_PCD_CLOSING;
+ delete = B_TRUE;
+ }
+ }
+ pcd->clnt_thread = NULL;
+ (void) mutex_unlock(&pcd->pcd_mtx);
+
+ /* Close the client. */
+ close_clnt(pcd, delete);
+ pcd = NULL;
+
+ /*
+ * Remove next deferred work from list.
+ */
+ workp = NULL;
+ (void) mutex_lock(&pnd->thr_mtx);
+ nclients = pnd->nclients;
+ workp = pnd->workhead;
+ if (workp &&
+ (pnd->workhead = pnd->workhead->pnd_next) == NULL)
+ pnd->worktail = NULL;
+ (void) mutex_unlock(&pnd->thr_mtx);
+
+ if (workp != NULL) {
+ /* See if the deferred client still exists. */
+ if (open_clnt(pnd, &pcd, workp->pnd_cid,
+ workp->pnd_cid_len, B_TRUE) != DSVC_SUCCESS)
+ pcd = NULL;
+ if (pcd == NULL) {
+ free(workp);
+ continue;
+ }
+
+ (void) mutex_lock(&pcd->pcd_mtx);
+ /* Check if it needs a worker thread. */
+ if (pcd->clnt_thread == NULL &&
+ (pcd->flags & DHCP_PCD_WORK) != 0 &&
+ (pcd->flags & DHCP_PCD_CLOSING) == 0) {
+ /* Found a valid client. Restart. */
+ pcd->clnt_thread = thrp;
+ (void) mutex_unlock(&pcd->pcd_mtx);
+ ifp = pcd->ifp;
+ free(workp);
+ continue;
+ }
+ (void) mutex_unlock(&pcd->pcd_mtx);
+ close_clnt(pcd, B_FALSE);
+ pcd = NULL;
+ free(workp);
+ }
+ continue;
+ }
+ (void) mutex_unlock(&pcd->pcd_mtx);
+
+ /*
+ * Based on the packet type, process accordingly.
+ */
+ if (plp->pkt->op == BOOTREQUEST) {
+ if (plp->opts[CD_DHCP_TYPE]) {
+ /* DHCP packet */
+ dhcp(pcd, plp);
+ } else {
+ /* BOOTP packet */
+ if (!bootp_compat) {
+ dhcpmsg(LOG_INFO, "BOOTP request "
+ "received on interface: %s "
+ "ignored.\n", ifp->nm);
+ } else {
+ bootp(pcd, plp);
+ }
+ }
+ }
+ if (plp != NULL) {
+ free_plp(plp); /* Free the packet. */
+ plp = NULL;
+ }
+
+ (void) mutex_lock(&ifp->ifp_mtx);
+ ifp->processed++;
+ (void) mutex_unlock(&ifp->ifp_mtx);
+ }
+
+ /* Free the packet. */
+ if (plp != NULL)
+ free_plp(plp);
+
+ /* Release the client structure. */
+ if (pcd != NULL) {
+ (void) mutex_lock(&pcd->pcd_mtx);
+ pcd->flags &= ~DHCP_PCD_WORK;
+ pcd->clnt_thread = NULL;
+ (void) mutex_unlock(&pcd->pcd_mtx);
+
+ close_clnt(pcd, B_FALSE);
+ }
+
+ /* Release the thread reference in pernet structure. */
+ if (pnd != NULL) {
+ (void) mutex_lock(&pnd->thr_mtx);
+ pnd->nthreads--;
+ (void) cond_signal(&pnd->thr_cv);
+ (void) mutex_unlock(&pnd->thr_mtx);
+ }
+
+ return (NULL);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/misc.c b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/misc.c
new file mode 100644
index 0000000000..2a0ed5d04c
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/misc.c
@@ -0,0 +1,769 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <syslog.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/sysmacros.h>
+#include <time.h>
+#include <locale.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <assert.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <arpa/inet.h>
+#include <netinet/dhcp.h>
+#include <netdb.h>
+#include <sys/mman.h>
+#include <locale.h>
+#include <tnf/probe.h>
+#include <resolv.h>
+
+#include "dhcpd.h"
+#include "per_dnet.h"
+#include "interfaces.h"
+
+#ifdef DEBUG
+/*
+ * Datastore debugging functions.
+ *
+ * A simple datastore simulation, using the magic DBG_MEMORY_NET network,
+ * is provided, to test the program with minimal datastore overhead.
+ * A concatenated reclist is used to speed record manipulation.
+ * Note that other networks continue to pass-thru to libdhcpsvc, to allow
+ * live comparison.
+ */
+
+/* Simple datastore database. */
+typedef struct db {
+ lease_t dn_lease;
+ char dn_cid[128];
+ char dn_macro[2];
+ uchar_t dn_cid_len;
+ uchar_t dn_flags;
+} dbg_t;
+
+typedef struct reclist {
+ dn_rec_list_t d_reclist;
+ dn_rec_t d_rec;
+} dbg_rec_t;
+
+#define DBG_MAXTABLE 16
+static dsvc_handle_t dbg_handle[DBG_MAXTABLE]; /* simulated handle */
+static rwlock_t dbg_lock[DBG_MAXTABLE]; /* locks */
+static uint32_t dbg_size = 4096; /* table size */
+static uint32_t dbg_msize; /* mapped size */
+static uint32_t dbg_mask = 0xFFFF; /* table mask */
+
+static uint32_t dbg_cid = 0; /* starting cid */
+static uint32_t dbg_flags = 0; /* starting flags */
+static uint32_t dbg_lease = 0; /* starting lease */
+static char dbg_macro = '1'; /* macro */
+#endif /* DEBUG */
+
+int
+dhcp_open_dd(dsvc_handle_t *handp, dsvc_datastore_t *ddp, dsvc_contype_t type,
+ const char *name, uint_t flags)
+{
+#ifndef DEBUG
+ return (open_dd(handp, ddp, type, name, flags));
+
+#else /* DEBUG */
+ int ret;
+ int hind;
+ int net;
+ int pgmsk = sysconf(_SC_PAGESIZE) - 1;
+
+ if (dbg_net && memcmp(name, dbg_net, strlen(dbg_net)) == 0) {
+ for (net = 0, hind = strlen(dbg_net); name[hind] != '.'; hind++)
+ net += (net * 10) + (name[hind] - '0');
+ if (net > DBG_MAXTABLE)
+ return (DSVC_NO_TABLE);
+
+ if (dbg_handle[net] == NULL) {
+ dbg_msize = (sizeof (dbg_t) * dbg_size + pgmsk) &
+ ~pgmsk;
+ /* LINTED [alignment ok] */
+ dbg_handle[net] = (dsvc_handle_t)mmap(0, dbg_msize,
+ PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
+ if ((char *)dbg_handle[net] == MAP_FAILED) {
+ dbg_handle[net] = NULL;
+ return (DSVC_INVAL);
+ }
+ }
+ *handp = (void *)net;
+ ret = DSVC_SUCCESS;
+ } else
+ ret = open_dd(handp, ddp, type, name, flags);
+
+ return (ret);
+#endif /* DEBUG */
+}
+
+int
+dhcp_close_dd(dsvc_handle_t *handp)
+{
+#ifndef DEBUG
+ return (close_dd(handp));
+
+#else /* DEBUG */
+ int ret;
+ int hind = (int)*handp;
+
+ if (dbg_net && hind >= 0 && hind < DBG_MAXTABLE) {
+ if (dbg_handle[hind] != NULL) {
+ (void) munmap((char *)dbg_handle[hind], dbg_msize);
+ dbg_handle[hind] = (dsvc_handle_t)NULL;
+ ret = DSVC_SUCCESS;
+ }
+ } else
+ ret = close_dd(handp);
+
+ return (ret);
+#endif /* DEBUG */
+}
+
+/*
+ * Detach the element from a list, and return it. If the list is empty, NULL is
+ * returned.
+ */
+dn_rec_list_t *
+detach_dnrec_from_list(dn_rec_list_t *prevp, dn_rec_list_t *elemp,
+ dn_rec_list_t **listpp)
+{
+ if (prevp == NULL) {
+ if (elemp == *listpp && elemp != NULL) {
+ /* head of the list */
+ *listpp = (*listpp)->dnl_next;
+ elemp->dnl_next = NULL;
+ }
+ } else if (prevp->dnl_next != NULL) {
+ /* somewhere in the middle */
+ prevp->dnl_next = elemp->dnl_next;
+ elemp->dnl_next = NULL;
+ } else
+ assert(elemp == NULL);
+
+ return (elemp);
+}
+
+/*
+ * Attach an unattached element (elemp) to a list (*listpp).
+ */
+static void
+attach_dnrec_to_list(dn_rec_list_t *elemp, dn_rec_list_t **listpp)
+{
+ if (*listpp != NULL)
+ elemp->dnl_next = *listpp;
+ *listpp = elemp;
+}
+
+/*
+ * dhcp_lookup_dd: perform lookup_dd.
+ */
+static int
+dhcp_lookup_dd(dsvc_handle_t hand, boolean_t partial, uint_t query,
+ int count, const void *targetp, void **recordsp, uint_t *nrecordsp)
+{
+#ifndef DEBUG
+ return (lookup_dd(hand, partial, query, count, targetp, recordsp,
+ nrecordsp));
+
+#else /* DEBUG */
+ int ret;
+ int hind = (int)hand;
+ int ind;
+ dn_rec_t *inp;
+ dn_rec_list_t **outp = (dn_rec_list_t **)recordsp;
+ dbg_t *dbp;
+ dbg_t *endp;
+ dbg_rec_t *rp;
+ dn_rec_t *recp;
+ dn_rec_list_t *reclp;
+ dbg_t *dbg_db;
+
+ if (dbg_net && hind >= 0 && hind < DBG_MAXTABLE) {
+ dbg_db = (dbg_t *)dbg_handle[hind];
+
+ if (outp)
+ *outp = NULL;
+ if (nrecordsp)
+ *nrecordsp = 0;
+ inp = (dn_rec_t *)targetp;
+
+ (void) rw_rdlock(&dbg_lock[hind]);
+ /*
+ * Simple linear search, aided by the fact that
+ * the server currently checks flags.
+ */
+ if (DSVC_QISEQ(query, DN_QCIP)) {
+ ind = inp->dn_cip.s_addr & dbg_mask;
+ dbp = &dbg_db[ind];
+ endp = &dbg_db[ind + 1];
+ } else {
+ ind = 0;
+ dbp = dbg_db;
+ endp = &dbg_db[dbg_size];
+ }
+ for (; dbp < endp; dbp++, ind++) {
+ /*
+ * Initialize record. fields will be zero'd
+ * when initially mmap'd.
+ */
+ if (dbp->dn_cid_len == 0) {
+ /* Skip server address to avoid arp issues. */
+ if (ind == (ntohl(server_ip.s_addr) % 256))
+ continue;
+ if (dbg_cid)
+ (void) snprintf(dbp->dn_cid,
+ sizeof (dbp->dn_cid), "%8X",
+ dbg_cid++);
+ dbp->dn_flags = dbg_flags;
+ dbp->dn_lease = dbg_lease;
+ dbp->dn_macro[0] = dbg_macro;
+ dbp->dn_macro[1] = '\0';
+ dbp->dn_cid_len = 1;
+ }
+ if (DSVC_QISEQ(query, DN_QCID) &&
+ (inp->dn_cid[0] != dbp->dn_cid[0] ||
+ memcmp(dbp->dn_cid, inp->dn_cid, inp->dn_cid_len)))
+ continue;
+
+ rp = (dbg_rec_t *)smalloc(sizeof (dbg_rec_t));
+ reclp = &rp->d_reclist;
+ recp = &rp->d_rec;
+
+ if (nrecordsp)
+ (*nrecordsp)++;
+
+ reclp->dnl_rec = recp;
+ recp->dn_lease = dbp->dn_lease;
+ recp->dn_sip.s_addr = ntohl(owner_ip->s_addr);
+ recp->dn_cip.s_addr = 0xd000000 + (hind << 16) + ind;
+ recp->dn_cid_len = dbp->dn_cid_len;
+ recp->dn_flags = dbp->dn_flags;
+ recp->dn_macro[0] = dbp->dn_macro[0];
+ recp->dn_macro[1] = '\0';
+ if ((recp->dn_cid[0] = dbp->dn_cid[0]) != '\0')
+ (void) memcpy(recp->dn_cid, dbp->dn_cid,
+ dbp->dn_cid_len);
+ if (*outp == NULL)
+ *outp = reclp;
+ else {
+ reclp->dnl_next = *outp;
+ *outp = reclp;
+ }
+ if (count > 0 && nrecordsp && *nrecordsp >= count)
+ break;
+ }
+ (void) rw_unlock(&dbg_lock[hind]);
+ ret = DSVC_SUCCESS;
+ } else
+ ret = lookup_dd(hand, partial, query, count, targetp,
+ recordsp, nrecordsp);
+
+ return (ret);
+#endif /* DEBUG */
+}
+
+int
+dhcp_modify_dd_entry(dsvc_handle_t hand, const void *origp, void *newp)
+{
+#ifndef DEBUG
+ return (modify_dd_entry(hand, origp, newp));
+
+#else /* DEBUG */
+ int ret;
+ int hind = (int)hand;
+ int ind;
+ dn_rec_t *dnp;
+ dbg_t *dbp;
+ dbg_t *dbg_db;
+
+ if (dbg_net && hind >= 0 && hind < DBG_MAXTABLE) {
+ dbg_db = (dbg_t *)dbg_handle[hind];
+
+ dnp = (dn_rec_t *)newp;
+ ind = dnp->dn_cip.s_addr & dbg_mask;
+ dbp = &dbg_db[ind];
+ (void) rw_wrlock(&dbg_lock[hind]);
+ dbp->dn_lease = dnp->dn_lease;
+ dbp->dn_cid_len = dnp->dn_cid_len;
+ dbp->dn_flags = dnp->dn_flags;
+ /*
+ * Performance: avoid routine call when NULL string
+ * is being copied.
+ */
+ if ((dbp->dn_cid[0] = dnp->dn_cid[0]) != '\0')
+ (void) memcpy(dbp->dn_cid, dnp->dn_cid,
+ dnp->dn_cid_len);
+ (void) rw_unlock(&dbg_lock[hind]);
+ ret = DSVC_SUCCESS;
+ } else
+ ret = modify_dd_entry(hand, origp, newp);
+
+ return (ret);
+#endif /* DEBUG */
+}
+
+void
+dhcp_free_dd_list(dsvc_handle_t hand, void *listp)
+{
+#ifndef DEBUG
+ free_dd_list(hand, listp);
+
+#else /* DEBUG */
+ dn_rec_list_t *ptr;
+ int hind = (int)hand;
+
+ if (dbg_net && hind >= 0 && hind < DBG_MAXTABLE) {
+ while ((ptr = listp) != NULL) {
+ listp = ptr->dnl_next;
+ free(ptr);
+ }
+ } else
+ free_dd_list(hand, listp);
+#endif /* DEBUG */
+}
+
+/*
+ * cmp_lrusort: qsort() comparison routine to sort lru list.
+ */
+static int
+cmp_lrusort(const void *a, const void *b)
+{
+ dn_rec_list_t *r1 = *(dn_rec_list_t **)a;
+ dn_rec_list_t *r2 = *(dn_rec_list_t **)b;
+
+ if (r1->dnl_rec->dn_lease < r2->dnl_rec->dn_lease)
+ return (-1);
+ else if (r1->dnl_rec->dn_lease == r2->dnl_rec->dn_lease)
+ return (0);
+ else
+ return (1);
+}
+
+/*
+ * get_lrusort: quick sort of eligible lru container entries.
+ */
+static dn_rec_list_t *
+get_lrusort(dsvc_dnet_t *pnd, dn_rec_list_t *lrup, uint_t *lrecords)
+{
+ size_t nel;
+ size_t size = *lrecords * sizeof (dn_rec_list_t *);
+ dn_rec_list_t *from, **to, *next, *freerec = NULL;
+ dn_rec_list_t *lrupage;
+ dn_rec_t *rp;
+ time_t reuse_time = time(NULL) - min_lru;
+ uint_t records = 0;
+#ifndef NDEBUG
+ int cnt = 0;
+#endif /* !NDEBUG */
+
+ (void) mutex_lock(&pnd->lrupage_mtx);
+ if (pnd->lrupage == NULL || pnd->lrusize < size) {
+ if (pnd->lrupage != NULL)
+ free(pnd->lrupage);
+ pnd->lrupage = (dn_rec_list_t **)smalloc(size);
+ pnd->lrusize = size;
+ }
+ if ((to = pnd->lrupage) == NULL) {
+ pnd->lrusize = 0;
+ (void) mutex_unlock(&pnd->lrupage_mtx);
+ return (lrup);
+ }
+
+ /*
+ * Build a list of entries, discarding those which are in use.
+ */
+ *to = NULL;
+ for (from = lrup; from != NULL; from = next) {
+ next = from->dnl_next;
+ rp = from->dnl_rec;
+ if (rp->dn_lease > reuse_time ||
+ (rp->dn_flags & DN_FAUTOMATIC) ||
+ rp->dn_lease == DHCP_PERM) {
+ from->dnl_next = freerec;
+ freerec = from;
+ } else {
+ records++;
+ *(to++) = from;
+ }
+ assert(++cnt <= *lrecords);
+ }
+
+ /*
+ * Sort any usable elements, and relink.
+ */
+ nel = (int)(to - pnd->lrupage);
+ if (nel > 0) {
+ if (nel > 1)
+ qsort(pnd->lrupage, nel, sizeof (dn_rec_list_t *),
+ cmp_lrusort);
+ for (to = pnd->lrupage; nel > 0; to++, nel--)
+ (*to)->dnl_next = *(to + 1);
+ to--;
+ (*to)->dnl_next = NULL;
+ }
+
+ /*
+ * Free any unusable elements, return any usable elements.
+ */
+ if (freerec)
+ dhcp_free_dd_list(pnd->dh, freerec);
+ *lrecords = records;
+
+ lrupage = *(pnd->lrupage);
+ (void) mutex_unlock(&pnd->lrupage_mtx);
+ return (lrupage);
+}
+
+/*
+ * dhcp_lookup_dd_classify: perform lookup_dd(), or use existing records
+ * if supplied, and classify the results based on the type of search criteria
+ * being employed. Centralized policy for DN_FMANUAL and DN_FUNUSABLE flag
+ * processing are implemented here. Classification is specialized
+ * based on these specific search criteria:
+ *
+ * S_CID A CID match is requested. Perform DN_FMANUAL and
+ * DN_FUNUSABLE processing.
+ * S_FREE A search for free records. Only examine first
+ * matching record.
+ * S_LRU A search for lru records. Perform sort if needed,
+ * and only examine first matching record.
+ *
+ * A matching record is detached and returned if found (ok ||
+ * manual + unusable). Other successful matches are returned in recordsp as
+ * a cache.
+ */
+void *
+dhcp_lookup_dd_classify(dsvc_dnet_t *pnd, boolean_t partial, uint_t query,
+ int count, const dn_rec_t *targetp, void **recordsp, int searchtype)
+{
+ int err;
+ uint_t rec_cnt = 0, manual = 0;
+ dn_rec_t *dnp;
+ dn_rec_list_t *nlp = NULL, *dnlp = NULL;
+ dn_rec_list_t *unulp = NULL; /* list of unusables, !manual */
+ dn_rec_list_t *unu_m_lp = NULL; /* list of unusable + manual */
+ dn_rec_list_t *m_lp = NULL; /* list of manual records */
+ dn_rec_list_t *cachep = NULL; /* match cache */
+ struct in_addr swapaddr;
+ char ntoab[INET_ADDRSTRLEN];
+
+ /*
+ * Lookup records matching the specified criteria, or use
+ * records from a previous lookup supplied for classification.
+ */
+ if (*recordsp == NULL) {
+
+ TNF_PROBE_1_DEBUG(classify, "classify classify",
+ "classify_query%debug 'in func classify'",
+ tnf_long, query, query);
+
+ err = dhcp_lookup_dd(pnd->dh, partial, query, count, targetp,
+ (void **)recordsp, &rec_cnt);
+
+ TNF_PROBE_1_DEBUG(classify_cid_end, "classify classify_end",
+ "classify_end%debug 'in func classify'",
+ tnf_long, rec_cnt, rec_cnt);
+
+ /*
+ * If any error occurs, mark the dsvc_dnet_t table
+ * for immediate close and reopen. Let the protocol
+ * perform recover, rather than attempting time-consuming
+ * in-place error recovery.
+ */
+ if (err != DSVC_SUCCESS) {
+ (void) mutex_lock(&pnd->pnd_mtx);
+ pnd->flags |= DHCP_PND_ERROR;
+ hash_Dtime(pnd->hand, 0);
+ (void) mutex_unlock(&pnd->pnd_mtx);
+#ifdef DEBUG
+ dhcpmsg(LOG_DEBUG, "classify failure %s\n",
+ dhcpsvc_errmsg(err));
+#endif /* DEBUG */
+ *recordsp = NULL;
+ return (NULL);
+ }
+
+ /*
+ * For LRU classification, sort returned records based
+ * on dn_lease field. Discards records with valid lease
+ * times; adjusts rec_cnt accordingly.
+ */
+ if (searchtype & S_LRU)
+ *recordsp = get_lrusort(pnd, *recordsp, &rec_cnt);
+
+ }
+
+ /*
+ * Record classification: scan through all records, performing
+ * DN_FUNUSABLE and DN_FMANUAL processing. Note that most of the
+ * work has been performed by the datastore query. Remove the matching
+ * entry from the singlely-linked record list, for return. Free any
+ * non-matching entries prior to the match. Pass back any additional
+ * entries after the match in the recordsp pointer for possible re-use
+ * by the caching code.
+ */
+
+ for (nlp = detach_dnrec_from_list(NULL, *recordsp,
+ (dn_rec_list_t **)recordsp); nlp != NULL;
+ nlp = detach_dnrec_from_list(NULL, *recordsp,
+ (dn_rec_list_t **)recordsp)) {
+ /*
+ * If we find that there is a DN_FMANUAL entry that is
+ * DN_FUNUSABLE, we fail the request, when performing a
+ * CID search, even though there may be other CID matches. In
+ * the CID case, those other CID matches are errors, because
+ * there should be one and only one record for a client if that
+ * record is marked as being DN_FMANUALly assigned. We tell
+ * the user how many of those CID matches there are. If there
+ * are no DN_FMANUAL records, the first matching record which
+ * is USABLE wins.
+ */
+ dnp = nlp->dnl_rec;
+ if (dnp->dn_flags & DN_FUNUSABLE) {
+ if ((searchtype & (S_CID|S_FREE|S_LRU)) == S_CID) {
+ char cidbuf[DHCP_MAX_OPT_SIZE];
+ uint_t blen = sizeof (cidbuf);
+
+ (void) octet_to_hexascii(targetp->dn_cid,
+ targetp->dn_cid_len,
+ cidbuf, &blen);
+
+ swapaddr.s_addr = htonl(dnp->dn_cip.s_addr);
+
+ dhcpmsg(LOG_NOTICE, "(%1$s,%2$s) "
+ "currently marked as unusable.\n", cidbuf,
+ inet_ntop(AF_INET, &swapaddr, ntoab,
+ sizeof (ntoab)));
+ }
+
+ /* build list of unusable records */
+ if (dnp->dn_flags & DN_FMANUAL) {
+ attach_dnrec_to_list(nlp, &unu_m_lp);
+ manual++;
+ } else
+ attach_dnrec_to_list(nlp, &unulp);
+ } else {
+ if (dnp->dn_flags & DN_FMANUAL) {
+ attach_dnrec_to_list(nlp, &m_lp);
+ manual++;
+ } else
+ attach_dnrec_to_list(nlp, &cachep);
+ /*
+ * These searches do not require examining all
+ * matches.
+ */
+ if (searchtype & (S_FREE|S_LRU))
+ break;
+ }
+ }
+
+ /*
+ * Warnings are printed for CID searches which end with
+ * DN_FUNUSABLE|DN_FMANUAL match(es).
+ */
+ if (m_lp != NULL || unu_m_lp != NULL) {
+ if (manual > 1) {
+ char cidbuf[DHCP_MAX_OPT_SIZE];
+ uint_t blen = sizeof (cidbuf);
+
+ (void) octet_to_hexascii(targetp->dn_cid,
+ targetp->dn_cid_len,
+ cidbuf, &blen);
+ dhcpmsg(LOG_WARNING,
+ "Manual allocation (%1$s) has %2$d other MANUAL"
+ " records. It should have 0.\n", cidbuf,
+ manual - 1);
+ }
+ if (unu_m_lp != NULL) {
+ dnlp = detach_dnrec_from_list(NULL, unu_m_lp,
+ &unu_m_lp);
+ } else
+ dnlp = detach_dnrec_from_list(NULL, m_lp, &m_lp);
+ }
+
+ /* Free any unusable entries */
+ if (unulp != NULL)
+ dhcp_free_dd_list(pnd->dh, unulp);
+
+ /* any other... */
+ if (dnlp == NULL)
+ dnlp = detach_dnrec_from_list(NULL, cachep, &cachep);
+
+ /*
+ * Return any unused elements for possible caching use. These are
+ * the additional manual + unusable (as punishment for having
+ * multiple items), manual, and and any others.
+ */
+ if (cachep != NULL)
+ attach_dnrec_to_list(cachep, (dn_rec_list_t **)recordsp);
+ if (m_lp != NULL)
+ attach_dnrec_to_list(m_lp, (dn_rec_list_t **)recordsp);
+ if (unu_m_lp != NULL)
+ attach_dnrec_to_list(unu_m_lp, (dn_rec_list_t **)recordsp);
+
+ /*
+ * Return one of the matching record(s).
+ */
+ return (dnlp);
+}
+
+/*
+ * Error message function. If debugging off, then logging goes to
+ * syslog.
+ *
+ * Must be MT SAFE - called by various threads as well as the main thread.
+ */
+
+/*VARARGS2*/
+void
+dhcpmsg(int errlevel, const char *fmtp, ...)
+{
+ char buff[BUFSIZ], errbuf[BUFSIZ];
+ const char *f = buff;
+ va_list ap;
+
+ if (debug < 0)
+ return;
+
+ va_start(ap, fmtp);
+
+ if (debug > 0) {
+ if (errlevel != LOG_ERR)
+ (void) snprintf(errbuf, sizeof (errbuf),
+ "%lx: ", time(NULL));
+ else
+ (void) snprintf(errbuf, sizeof (errbuf),
+ "%lx: (%s)", time(NULL), strerror(errno));
+ (void) snprintf(buff, sizeof (buff), "%s %s", errbuf,
+ gettext(fmtp));
+ (void) vfprintf(stderr, f, ap);
+ } else if (debug == 0)
+ (void) vsyslog(errlevel, gettext(fmtp), ap);
+
+ va_end(ap);
+}
+
+/*
+ * smalloc() -- safe malloc()
+ *
+ * Always returns a valid pointer(if it returns at all). The allocated
+ * memory is initialized to all zeros. If malloc() returns an error, a
+ * message is printed using the syslog() function and the program aborts
+ * with a status of 1.
+ *
+ * Must be MT SAFE - called by threads other than the main thread.
+ */
+void *
+smalloc(uint_t nbytes)
+{
+ char *retvalue;
+
+ if ((retvalue = calloc(nbytes, sizeof (char))) == NULL) {
+ dhcpmsg(LOG_ERR, "Cannot allocate memory (%s), exiting\n",
+ strerror(errno));
+ exit(1);
+ }
+ return (retvalue);
+}
+
+/*
+ * srealloc() -- safe realloc()
+ *
+ * Always returns a valid pointer(if it returns at all).
+ * If realloc() returns an error, a message is printed using the syslog()
+ * function and the program aborts with a status of 1.
+ * Unlike smalloc(), does not initialize the buffer to all zeros.
+ *
+ * Must be MT SAFE - called by threads other than the main thread.
+ */
+void *
+srealloc(void *arg, uint_t nbytes)
+{
+ if ((arg = realloc(arg, nbytes)) == NULL) {
+ dhcpmsg(LOG_ERR, "Cannot allocate memory (%s), exiting\n",
+ strerror(errno));
+ exit(1);
+ }
+ return (arg);
+}
+
+/*
+ * Matches the speficied ip address with our owner_ip addresses.
+ * Returns NULL if no match is found.
+ */
+struct in_addr *
+match_ownerip(in_addr_t new)
+{
+ struct in_addr *oip = owner_ip;
+
+ while (oip->s_addr != INADDR_ANY && oip->s_addr != new)
+ oip++;
+ return ((oip->s_addr != INADDR_ANY) ? oip : NULL);
+}
+
+/*
+ * qualify_hostname() -- concatenate host "." domain "." NULL
+ */
+int
+qualify_hostname(char *fqname, const char *host, const char *domain,
+ int host_length, int domain_length)
+{
+ char *fqptr;
+
+ if (domain_length + host_length + 2 > NS_MAXDNAME) {
+ dhcpmsg(LOG_ERR, "qualify_hostname: FQDN too long\n");
+ return (-1);
+ }
+
+ fqptr = fqname;
+
+ (void) memcpy(fqptr, host, host_length);
+ fqptr += host_length;
+
+ *fqptr = '.';
+ fqptr++;
+
+ (void) memcpy(fqptr, domain, domain_length);
+ fqptr += domain_length;
+
+ *fqptr = '.';
+ *(fqptr+1) = '\0';
+
+ return (0);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/per_dnet.c b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/per_dnet.c
new file mode 100644
index 0000000000..0106135a53
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/per_dnet.c
@@ -0,0 +1,636 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/sysmacros.h>
+#include <assert.h>
+#include <errno.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <netinet/dhcp.h>
+#include "dhcpd.h"
+#include "interfaces.h"
+#include <locale.h>
+
+/*
+ * This file contains the access routines for the dhcp databases.
+ */
+
+static dsvc_dnet_t *get_dnet(struct in_addr *);
+static boolean_t unhash_dnet(dsvc_dnet_t *, boolean_t);
+static int dnet_cmp(dsvc_dnet_t *, dsvc_dnet_t *);
+static dsvc_clnt_t *get_client(hash_tbl *, uchar_t *, uchar_t);
+static int clnt_cmp(dsvc_clnt_t *, dsvc_clnt_t *);
+static boolean_t unhash_clnt(dsvc_clnt_t *, boolean_t);
+static boolean_t unhash_offer(dsvc_clnt_t *, boolean_t);
+
+static hash_tbl *ntable; /* global per net datastore table */
+
+/*
+ * Initialize global per network hash table.
+ *
+ * Per-bucket rwlocks reduce lock contention between interface and
+ * client threads.
+ *
+ * Performance: dynamically calculate hash table size.
+ */
+int
+initntab(void)
+{
+ char **listppp;
+ uint_t cnt = 0;
+ uint_t ind;
+
+ if (list_dd(&datastore, DSVC_DHCPNETWORK, &listppp, &cnt) ==
+ DSVC_SUCCESS) {
+ if (listppp) {
+ for (ind = 0; listppp[ind] != NULL && ind < cnt;
+ ind++)
+ free(listppp[ind]);
+ free(listppp);
+ }
+ }
+ ntable = hash_Init(cnt, unhash_dnet, MAX(net_thresh, clnt_thresh),
+ B_TRUE);
+ return (ntable == NULL ? -1 : 0);
+}
+
+/*
+ * open_dnet: Open the appropriate dhcp database given a network address and
+ * a subnet mask. These in_addr's are expected in network order.
+ *
+ * Returns: DSVC_SUCCESS for success or dsvc error.
+ */
+int
+open_dnet(dsvc_dnet_t **pndp, struct in_addr *net, struct in_addr *mask)
+{
+ int err;
+ dsvc_dnet_t *pnd;
+ struct in_addr datum;
+ int hsize = 0;
+ uint32_t query;
+ dn_rec_t dn;
+ uint_t count;
+ struct in_addr *oip;
+
+ datum.s_addr = net->s_addr;
+ datum.s_addr &= mask->s_addr;
+
+ *pndp = NULL;
+ /* Locate existing dnet. */
+ if ((pnd = get_dnet(&datum)) != NULL) {
+ (void) mutex_lock(&pnd->pnd_mtx);
+ if ((pnd->flags & DHCP_PND_ERROR) != 0) {
+ (void) mutex_unlock(&pnd->pnd_mtx);
+ close_dnet(pnd, B_TRUE);
+ return (DSVC_INTERNAL);
+ } else if ((pnd->flags & DHCP_PND_CLOSING) != 0) {
+ (void) mutex_unlock(&pnd->pnd_mtx);
+ close_dnet(pnd, B_FALSE);
+ return (DSVC_BUSY);
+ } else {
+ (void) mutex_unlock(&pnd->pnd_mtx);
+ *pndp = pnd;
+ return (DSVC_SUCCESS);
+ }
+ }
+
+ /* Allocate new dnet. */
+
+ pnd = (dsvc_dnet_t *)smalloc(sizeof (dsvc_dnet_t));
+ pnd->net.s_addr = datum.s_addr;
+ pnd->subnet.s_addr = mask != 0 ? mask->s_addr : htonl(INADDR_ANY);
+ (void) inet_ntop(AF_INET, &datum, pnd->network, sizeof (pnd->network));
+
+ /* Allocate hash tables. */
+ if (max_clients != -1)
+ hsize = max_clients;
+ if ((pnd->ctable =
+ hash_Init(hsize, unhash_clnt, MAX(off_secs, clnt_thresh),
+ B_TRUE)) == NULL) {
+ free(pnd);
+ return (DSVC_INTERNAL);
+ }
+ if ((pnd->itable =
+ hash_Init(hsize, unhash_offer, off_secs, B_TRUE)) == NULL) {
+ free(pnd->ctable);
+ free(pnd);
+ return (DSVC_INTERNAL);
+ }
+
+ err = dhcp_open_dd(&pnd->dh, &datastore, DSVC_DHCPNETWORK, pnd->network,
+ DSVC_READ|DSVC_WRITE);
+
+ if (err != DSVC_SUCCESS) {
+ free(pnd->ctable);
+ free(pnd->itable);
+ free(pnd);
+ return (err);
+ }
+
+ /* Find out how many addresses the server owns in this datastore */
+
+ pnd->naddrs = 0;
+ for (oip = owner_ip; oip->s_addr != INADDR_ANY; oip++) {
+
+ DSVC_QINIT(query);
+ DSVC_QEQ(query, DN_QSIP);
+ dn.dn_sip.s_addr = ntohl(oip->s_addr);
+
+ err = lookup_dd(pnd->dh, B_FALSE, query, -1, &dn, NULL, &count);
+
+ if (err != DSVC_SUCCESS) {
+ free(pnd->ctable);
+ free(pnd->itable);
+ free(pnd);
+ return (err);
+ }
+
+ pnd->naddrs += count;
+ }
+
+ if ((pnd->hand = hash_Insert(ntable, &pnd->net, sizeof (struct in_addr),
+ dnet_cmp, pnd, pnd)) == NULL) {
+ /* Another thread has begun work on this net. */
+#ifdef DEBUG
+ dhcpmsg(LOG_DEBUG, "Duplicate network: %s\n", pnd->network);
+#endif /* DEBUG */
+ free(pnd->ctable);
+ free(pnd->itable);
+ free(pnd);
+ return (DSVC_BUSY);
+ }
+
+ (void) mutex_init(&pnd->pnd_mtx, USYNC_THREAD, NULL);
+ (void) mutex_init(&pnd->thr_mtx, USYNC_THREAD, NULL);
+ (void) mutex_init(&pnd->free_mtx, USYNC_THREAD, NULL);
+ (void) mutex_init(&pnd->lru_mtx, USYNC_THREAD, NULL);
+ (void) mutex_init(&pnd->lrupage_mtx, USYNC_THREAD, NULL);
+
+ *pndp = pnd;
+ return (DSVC_SUCCESS);
+}
+
+/*
+ * close_dnet: Closes specified dhcp-network database.
+ *
+ * delete - immediately delete.
+ */
+void
+close_dnet(dsvc_dnet_t *pnd, boolean_t delete)
+{
+ hash_Rele(pnd->hand, delete);
+}
+
+/*
+ * get_dnet: Given a network name, look it up in the hash table.
+ * Returns ptr to dsvc_dnet_t structure, NULL if error occurs.
+ */
+static dsvc_dnet_t *
+get_dnet(struct in_addr *netp)
+{
+ dsvc_dnet_t tpnd;
+ dsvc_dnet_t *pnd;
+
+ tpnd.net.s_addr = netp->s_addr;
+ pnd = (dsvc_dnet_t *)hash_Lookup(ntable, netp,
+ sizeof (struct in_addr), dnet_cmp, &tpnd, B_TRUE);
+
+ /* refresh pnd hash entry timer */
+ if (pnd != NULL)
+ hash_Dtime(pnd->hand, time(NULL) + ntable->dfree_time);
+ return (pnd);
+}
+
+/*
+ * unhash_dnet: Free a datastore reference.
+ *
+ * Aging in hash routines will trigger freeing of unused references.
+ */
+/*ARGSUSED*/
+static boolean_t
+unhash_dnet(dsvc_dnet_t *pnd, boolean_t force)
+{
+ int err = 0;
+ dsvc_pendclnt_t *workp;
+ dsvc_thr_t *thrp;
+ timestruc_t tm;
+ int nthreads;
+ int refcnt;
+
+ if (pnd == NULL)
+ return (B_FALSE);
+
+ /* Mark as closing. */
+ (void) mutex_lock(&pnd->pnd_mtx);
+ pnd->flags |= DHCP_PND_CLOSING;
+ (void) mutex_unlock(&pnd->pnd_mtx);
+
+ /*
+ * Wait for any remaining thread(s) to exit.
+ */
+ refcnt = hash_Refcount(pnd->hand);
+
+ (void) mutex_lock(&pnd->thr_mtx);
+ nthreads = pnd->nthreads;
+ while (nthreads > 0 || refcnt > 0) {
+ /*
+ * Wait for 1ms to avoid stalling monitor threads.
+ * cond_wait() not used to avoid thread synchronization
+ * overhead.
+ */
+ tm.tv_sec = 0;
+ tm.tv_nsec = 1000 * 10;
+ (void) cond_reltimedwait(&pnd->thr_cv, &pnd->thr_mtx, &tm);
+ nthreads = pnd->nthreads;
+ (void) mutex_unlock(&pnd->thr_mtx);
+ /* Threads will exit. */
+ for (thrp = pnd->thrhead; thrp; thrp = thrp->thr_next) {
+ (void) mutex_lock(&thrp->thr_mtx);
+ thrp->thr_flags |= DHCP_THR_EXITING;
+ (void) mutex_unlock(&thrp->thr_mtx);
+ (void) cond_signal(&thrp->thr_cv);
+ }
+ refcnt = hash_Refcount(pnd->hand);
+ (void) mutex_lock(&pnd->thr_mtx);
+ }
+
+ /* Free threads. */
+ while ((thrp = pnd->thrhead) != NULL) {
+ pnd->thrhead = pnd->thrhead->thr_next;
+ (void) mutex_destroy(&thrp->thr_mtx);
+ free(thrp);
+ }
+ pnd->thrtail = NULL;
+
+ /* Free deferred thread work. */
+ while ((workp = pnd->workhead) != NULL) {
+ pnd->workhead = pnd->workhead->pnd_next;
+ free(workp);
+ }
+ pnd->worktail = NULL;
+ (void) mutex_unlock(&pnd->thr_mtx);
+
+ /* Free clients. */
+ if (pnd->ctable) {
+ hash_Reset(pnd->ctable, unhash_clnt);
+ free(pnd->ctable);
+ }
+
+ /* Free cached datastore records. */
+ (void) mutex_lock(&pnd->free_mtx);
+ if (pnd->freerec != NULL)
+ dhcp_free_dd_list(pnd->dh, pnd->freerec);
+ (void) mutex_unlock(&pnd->free_mtx);
+
+ (void) mutex_lock(&pnd->lru_mtx);
+ if (pnd->lrurec != NULL)
+ dhcp_free_dd_list(pnd->dh, pnd->lrurec);
+ (void) mutex_unlock(&pnd->lru_mtx);
+
+ if (pnd->itable) {
+ hash_Reset(pnd->itable, unhash_offer);
+ free(pnd->itable);
+ }
+
+ (void) mutex_lock(&pnd->lrupage_mtx);
+ if (pnd->lrupage) {
+ free(pnd->lrupage);
+ pnd->lrupage = NULL;
+ }
+ (void) mutex_unlock(&pnd->lrupage_mtx);
+
+ if (pnd->dh != NULL) {
+ if (dhcp_close_dd(&pnd->dh) != DSVC_SUCCESS) {
+ dhcpmsg(LOG_ERR,
+ "Error %d while closing for network %s\n",
+ err, pnd->network);
+ }
+ pnd->dh = NULL;
+ }
+
+ (void) mutex_destroy(&pnd->pnd_mtx);
+ (void) mutex_destroy(&pnd->thr_mtx);
+ (void) mutex_destroy(&pnd->free_mtx);
+ (void) mutex_destroy(&pnd->lru_mtx);
+ (void) mutex_destroy(&pnd->lrupage_mtx);
+ free(pnd);
+
+ return (B_TRUE);
+}
+
+/*
+ * dnet_cmp: Compare datastore references by network address.
+ */
+static int
+dnet_cmp(dsvc_dnet_t *m1, dsvc_dnet_t *m2)
+{
+ return (m1->net.s_addr == m2->net.s_addr);
+}
+
+/*
+ * open_clnt: Open the appropriate dhcp client given a network
+ * database and client id.
+ *
+ * Returns: DSVC_SUCCESS for success or errno if an error occurs.
+ *
+ * pnd - per net struct
+ * pcdp - client struct returned here
+ * cid - clientid
+ * cid_len - cid length
+ * nocreate - if set, client struct must previously exist
+ */
+int
+open_clnt(dsvc_dnet_t *pnd, dsvc_clnt_t **pcdp, uchar_t *cid,
+ uchar_t cid_len, boolean_t nocreate)
+{
+ dsvc_clnt_t *pcd;
+ time_t now;
+ uint_t blen;
+
+ *pcdp = NULL;
+
+ /* Network is closing. */
+ if ((pnd->flags & DHCP_PND_CLOSING) != 0)
+ return (DSVC_BUSY);
+
+ /* Locate existing client. */
+ if ((pcd = get_client(pnd->ctable, cid, cid_len)) != NULL) {
+ (void) mutex_lock(&pcd->pcd_mtx);
+ /* Client is closing - temporarily busy. */
+ if ((pcd->flags & DHCP_PCD_CLOSING) != 0) {
+ (void) mutex_unlock(&pcd->pcd_mtx);
+ close_clnt(pcd, B_FALSE);
+ return (DSVC_BUSY);
+ }
+ (void) mutex_unlock(&pcd->pcd_mtx);
+ *pcdp = pcd;
+ return (DSVC_SUCCESS);
+ }
+ if (nocreate == B_TRUE)
+ return (DSVC_NOENT);
+
+ /* Allocate new client. */
+ (void) mutex_lock(&pnd->thr_mtx);
+ if (max_clients != -1) {
+ now = time(NULL);
+ /*
+ * Performance/DOS: dsvc_clnt_t structs are normally
+ * freed when the protocol conversation completes,
+ * or when garbage collected (see hash.c). In
+ * certain error scenarios (e.g. DOS attacks, or
+ * network failures where large numbers of clients
+ * begin protocol conversations that never complete)
+ * the server will become unresponsive. To detect
+ * these scenarios, free slot time is observed, and
+ * after a grace period (2 * the offer time the currently
+ * allocated clients are allowed), clients are randomly
+ * deleted.
+ */
+ if (pnd->nclients < max_clients) {
+ /* Keep track of last time there were free slots. */
+ pnd->clnt_stamp = now;
+ (void) mutex_unlock(&pnd->thr_mtx);
+ } else if (pnd->clnt_stamp + off_secs > now) {
+ /* Wait for other clients to complete. */
+ (void) mutex_unlock(&pnd->thr_mtx);
+ return (DSVC_INTERNAL);
+ } else {
+ /* Forcibly delete a client to free a slot. */
+ pnd->clnt_stamp = now;
+ (void) mutex_unlock(&pnd->thr_mtx);
+ hash_Reap(pnd->ctable, unhash_clnt);
+ }
+ } else
+ (void) mutex_unlock(&pnd->thr_mtx);
+
+ pcd = (dsvc_clnt_t *)smalloc(sizeof (dsvc_clnt_t));
+ (void) mutex_init(&pcd->pcd_mtx, USYNC_THREAD, NULL);
+ (void) mutex_init(&pcd->pkt_mtx, USYNC_THREAD, NULL);
+ pcd->pkthead = pcd->pkttail = NULL;
+ pcd->pnd = pnd;
+ (void) memcpy(pcd->cid, cid, cid_len);
+ pcd->cid_len = cid_len;
+ blen = sizeof (pcd->cidbuf);
+ (void) octet_to_hexascii(cid, cid_len, pcd->cidbuf, &blen);
+
+ if ((pcd->chand = hash_Insert(pnd->ctable, cid, cid_len, clnt_cmp,
+ pcd, pcd)) == NULL) {
+ /* Another thread has begun work on this client */
+#ifdef DEBUG
+ dhcpmsg(LOG_DEBUG, "Duplicate client\n");
+#endif /* DEBUG */
+ (void) mutex_destroy(&pcd->pcd_mtx);
+ (void) mutex_destroy(&pcd->pkt_mtx);
+ free(pcd);
+ return (DSVC_BUSY);
+ }
+ (void) mutex_lock(&pnd->thr_mtx);
+ pnd->nclients++;
+ (void) mutex_unlock(&pnd->thr_mtx);
+ *pcdp = pcd;
+ return (DSVC_SUCCESS);
+}
+
+/*
+ * close_clnt: Closes specified client.
+ *
+ * delete - immediately delete.
+ */
+void
+close_clnt(dsvc_clnt_t *pcd, boolean_t delete)
+{
+ hash_Rele(pcd->chand, delete);
+}
+
+/*
+ * get_client: Given a client name, look it up in the per client hash table.
+ * Returns ptr to dsvc_clnt_t structure, NULL if error occurs.
+ */
+static dsvc_clnt_t *
+get_client(hash_tbl *table, uchar_t *cid, uchar_t cid_len)
+{
+ dsvc_clnt_t tpcd;
+ dsvc_clnt_t *pcd;
+
+ (void) memcpy(tpcd.cid, cid, cid_len);
+ tpcd.cid_len = cid_len;
+
+ pcd = (dsvc_clnt_t *)hash_Lookup(table, cid, cid_len, clnt_cmp,
+ &tpcd, B_TRUE);
+
+ /* refresh client hash entry's timer */
+ if (pcd != NULL)
+ hash_Dtime(pcd->chand, time(NULL) + table->dfree_time);
+ return (pcd);
+}
+
+/*
+ * unhash_clnt: Free a client structure.
+ *
+ * Aging in hash routines will trigger freeing of unused references.
+ */
+/*ARGSUSED*/
+static boolean_t
+unhash_clnt(dsvc_clnt_t *pcd, boolean_t force)
+{
+ dsvc_dnet_t *pnd = pcd->pnd;
+ timestruc_t tm;
+ int refcnt;
+ struct in_addr off_ip;
+
+
+ refcnt = hash_Refcount(pcd->chand);
+
+ /*
+ * Wait for thread(s) accessing pcd to drop references.
+ */
+ (void) mutex_lock(&pcd->pcd_mtx);
+ pcd->flags |= DHCP_PCD_CLOSING; /* client no longer usable... */
+ while (pcd->clnt_thread != NULL || refcnt > 0) {
+ /*
+ * Wait for 1ms to avoid stalling monitor threads.
+ * cond_wait() not used to avoid thread synchronization
+ * overhead.
+ */
+ tm.tv_sec = 0;
+ tm.tv_nsec = 1000 * 10;
+ (void) cond_reltimedwait(&pcd->pcd_cv, &pcd->pcd_mtx, &tm);
+ (void) mutex_unlock(&pcd->pcd_mtx);
+ refcnt = hash_Refcount(pcd->chand);
+ (void) mutex_lock(&pcd->pcd_mtx);
+ }
+
+ if (pcd->pkthead != NULL)
+ free_pktlist(pcd);
+
+ off_ip.s_addr = pcd->off_ip.s_addr;
+ (void) mutex_unlock(&pcd->pcd_mtx);
+
+ if (off_ip.s_addr != htonl(INADDR_ANY))
+ purge_offer(pcd, B_TRUE, B_FALSE);
+
+ if (pcd->dnlp != NULL)
+ dhcp_free_dd_list(pnd->dh, pcd->dnlp);
+
+ (void) mutex_destroy(&pcd->pcd_mtx);
+ (void) mutex_destroy(&pcd->pkt_mtx);
+ free(pcd);
+
+ (void) mutex_lock(&pnd->thr_mtx);
+ pnd->nclients--;
+ (void) mutex_unlock(&pnd->thr_mtx);
+
+ return (B_TRUE);
+}
+
+/*
+ * unhash_offer: Free offer associated with a client structure.
+ *
+ * Aging in hash routines will trigger freeing of expired offers.
+ */
+static boolean_t
+unhash_offer(dsvc_clnt_t *pcd, boolean_t force)
+{
+ IF *ifp = pcd->ifp;
+ boolean_t ret = B_TRUE;
+ char ntoab[INET_ADDRSTRLEN];
+
+ (void) mutex_lock(&pcd->pcd_mtx);
+ if (pcd->off_ip.s_addr != htonl(INADDR_ANY) &&
+ PCD_OFFER_TIMEOUT(pcd, time(NULL))) {
+ if (pcd->clnt_thread == NULL) {
+ if (debug)
+ dhcpmsg(LOG_INFO, "Freeing offer: %s\n",
+ inet_ntop(AF_INET, &pcd->off_ip,
+ ntoab, sizeof (ntoab)));
+ pcd->off_ip.s_addr = htonl(INADDR_ANY);
+ (void) mutex_unlock(&pcd->pcd_mtx);
+ (void) mutex_lock(&ifp->ifp_mtx);
+ ifp->offers--;
+ assert((int)ifp->offers >= 0);
+ ifp->expired++;
+ (void) mutex_unlock(&ifp->ifp_mtx);
+ } else if (force == B_FALSE) {
+ /*
+ * Worker thread is currently active. To avoid
+ * unnecessary thread synchronization, defer
+ * freeing the offer until the worker thread has
+ * completed.
+ */
+ (void) mutex_unlock(&pcd->pcd_mtx);
+ hash_Age(pcd->ihand);
+ ret = B_FALSE;
+ } else
+ (void) mutex_unlock(&pcd->pcd_mtx);
+ } else
+ (void) mutex_unlock(&pcd->pcd_mtx);
+ return (ret);
+}
+
+/*
+ * clnt_cmp: Compare client structures by cid.
+ */
+static int
+clnt_cmp(dsvc_clnt_t *m1, dsvc_clnt_t *m2)
+{
+ return (m1->cid_len == m2->cid_len &&
+ memcmp((char *)m1->cid, (char *)m2->cid, m1->cid_len) == 0);
+}
+
+/*
+ * clnt_netcmp Compare clients by network address. This is used to maintain
+ * the itable hash table of client addresses.
+ */
+int
+clnt_netcmp(dsvc_clnt_t *d1, dsvc_clnt_t *d2)
+{
+ return (d1->off_ip.s_addr == d2->off_ip.s_addr);
+}
+
+/*
+ * close_clnts: Free the ntable hash table and associated client structs.
+ * Table walk frees each per network and client struct.
+ */
+void
+close_clnts(void)
+{
+ hash_Reset(ntable, unhash_dnet);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/per_dnet.h b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/per_dnet.h
new file mode 100644
index 0000000000..8672e86ad8
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/per_dnet.h
@@ -0,0 +1,222 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _PER_DSTORE_H
+#define _PER_DSTORE_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * per_dnet.h -- DHCP-datastore database definitions.
+ */
+
+/*
+ * DHCP thread deferred work structure. One per deferred client thread.
+ *
+ * Track clients who cannot be immediately serviced due
+ * to a lack of available client threads. Locked by thr_mtx.
+ */
+typedef struct dsvc_pending {
+ uchar_t pnd_cid[DN_MAX_CID_LEN]; /* cid */
+ uchar_t pnd_cid_len; /* cid length */
+ struct dsvc_pending *pnd_next; /* next */
+} dsvc_pendclnt_t;
+
+/*
+ * DHCP thread structure. One per client thread.
+ *
+ * Performance: create and track each client thread, using
+ * thr_suspend/thr_resume, rather than slower thread creation
+ * and deletion. Locked by thr_mtx.
+ */
+struct clnt;
+typedef struct dsvc_free {
+ thread_t thr_tid; /* thread id */
+ cond_t thr_cv; /* suspend/resume cv */
+ mutex_t thr_mtx; /* suspend/resume mutex */
+ uint_t thr_flags; /* suspend/resume flags */
+ struct clnt *thr_pcd; /* per client data struct */
+ struct dsvc_free *thr_next; /* next */
+} dsvc_thr_t;
+
+/*
+ * DHCP thread structure flags
+ */
+#define DHCP_THR_LIST 0x1 /* Thread is on freelist */
+#define DHCP_THR_EXITING 0x2 /* Thread is exiting */
+
+/*
+ * DHCP datastore table. One per active network or dhcptab datastore.
+ *
+ * Timestamps are used to age the datastore structure, and any cached
+ * datastore free or lru records managed in select_offer().
+ * The number of threads and clients can be controlled via MAX_THREADS
+ * and MAX_CLIENTS server parameters.
+ *
+ * Client and offer hashes provide fast lookup/reservation.
+ * Per-bucket rwlocks implemented in the hash routines reduce
+ * lock contention between clients.
+ *
+ * To minimize expensive datastore activity, threads synchronize to
+ * manage cached free and lru datastore records, which have the
+ * same lifetime as cached offers, which can be controlled via
+ * the OFFER_CACHE_TIMEOUT server parameter.
+ */
+typedef struct dnet {
+ hash_handle hand; /* hash insertion handle */
+ time_t free_mtime; /* macro table purge time */
+ time_t free_stamp; /* D_OFFER freerec purge time */
+ time_t lru_mtime; /* macro table purge time */
+ time_t lru_stamp; /* D_OFFER lrurec purge time */
+ time_t clnt_stamp; /* D_OFFER client purge time */
+ uint_t flags; /* dnet flags */
+ struct in_addr net; /* network */
+ struct in_addr subnet; /* subnet mask */
+ int naddrs; /* # addrs owned by server */
+ int nthreads; /* number of active threads */
+ int nclients; /* number of active clients */
+ hash_tbl *ctable; /* per client hash table */
+ hash_tbl *itable; /* per ipaddr hash table */
+ dsvc_thr_t *thrhead; /* free thread list */
+ dsvc_thr_t *thrtail; /* free thread list tail */
+ dsvc_pendclnt_t *workhead; /* head of thread work list */
+ dsvc_pendclnt_t *worktail; /* tail of thread work list */
+ dn_rec_list_t *freerec; /* free records head */
+ dn_rec_list_t *lrurec; /* lru records head */
+ dn_rec_list_t **lrupage; /* lru records sort area */
+ size_t lrusize;
+
+ dsvc_handle_t dh; /* datastore handle */
+ mutex_t pnd_mtx; /* open/close mutex */
+ mutex_t free_mtx; /* lock for free records */
+ mutex_t lru_mtx; /* lock for lru records */
+ mutex_t lrupage_mtx; /* lock for lru page */
+ mutex_t thr_mtx; /* lock for thread work list */
+ cond_t thr_cv; /* cond var (nthreads == 0) */
+ char network[INET_ADDRSTRLEN]; /* display buffer */
+} dsvc_dnet_t;
+
+/*
+ * DHCP datastore table flags
+ */
+#define DHCP_PND_CLOSING 0x1 /* Dstore is closing */
+#define DHCP_PND_ERROR 0x2 /* Dstore experienced error */
+
+/*
+ * DHCP datastore table macros
+ */
+#define PND_FREE_TIMEOUT(pnd, now) ((pnd)->free_stamp < (now) || \
+ (pnd)->free_mtime != reinit_time)
+#define PND_LRU_TIMEOUT(pnd, now) ((pnd)->lru_stamp < (now) || \
+ (pnd)->lru_mtime != reinit_time)
+
+struct interfaces;
+
+/*
+ * DHCP client. One per active client, per network.
+ *
+ * Timestamps are used to age the client structure, and any cached
+ * offer, which can be controlled via the OFFER_CACHE_TIMEOUT server
+ * parameter, and the -t option, and SIGHUP signal.
+ *
+ * The original datastore record is cached, along with offer information,
+ * for use if the datastore record is modified.
+ *
+ * The clnt struct may appear on the client hash table, and offer hash table,
+ * depending on the validity of the current offer. A different dsvc_clnt_t
+ * is used for each link, to allow independent aging of client and offer.
+ */
+typedef struct clnt {
+ hash_handle chand; /* hash insertion handle: client hash */
+ hash_handle ihand; /* hash insertion handle: inet hash */
+ time_t mtime; /* macro table offer purge time */
+ uint_t flags; /* Client flags */
+ uint_t state; /* Client DHCP state */
+ struct in_addr off_ip; /* Offered address */
+ struct interfaces *ifp; /* the ifp the packet arrived on */
+ dsvc_dnet_t *pnd; /* the per network datastore */
+ PKT_LIST *pkthead; /* head of client packet list */
+ PKT_LIST *pkttail; /* tail of client packet list */
+ uint_t pending; /* # of pkts on client packet list */
+ lease_t lease; /* Offered lease time */
+ dsvc_thr_t *clnt_thread; /* client thread */
+
+ dn_rec_list_t *dnlp; /* Original datastore record */
+ mutex_t pcd_mtx; /* overall struct lock */
+ mutex_t pkt_mtx; /* lock for PKT_LIST */
+ cond_t pcd_cv; /* cond var (clnt_thread == 0) */
+ char cidbuf[DHCP_MAX_OPT_SIZE]; /* display buffer */
+ uchar_t cid[DN_MAX_CID_LEN]; /* Offered cid */
+ uchar_t cid_len; /* Offered cid length */
+} dsvc_clnt_t;
+
+/*
+ * Per Client flags and indices
+ */
+#define DHCP_HDR_CLIENT 0x0 /* clnt dsvc_clnt_t */
+#define DHCP_HDR_OFFER 0x1 /* offer dsvc_clnt_t */
+
+#define DHCP_PCD_OFFER 0x1 /* Offered ip addr is valid */
+#define DHCP_PCD_WORK 0x2 /* Client is on deferred work list */
+#define DHCP_PCD_CLOSING 0x4 /* Client thread should exit */
+
+/*
+ * Per Client macros
+ */
+#define PCD_OFFER_TIMEOUT(pcd, now) (hash_Htime(pcd->ihand) < (now) || \
+ (pcd)->mtime != reinit_time)
+
+/*
+ * Datastore hash table dynamic data free timeout values
+ */
+#define DHCP_CLIENT_THRESHOLD 90 /* Time to free inactive clients */
+#define DHCP_NET_THRESHOLD 900 /* Time to free inactive nets */
+
+/*
+ * Datastore database access routines.
+ */
+extern int open_dnet(dsvc_dnet_t **, struct in_addr *, struct in_addr *);
+extern void close_dnet(dsvc_dnet_t *, boolean_t);
+
+/*
+ * Per Client hash and utility routines.
+ */
+extern int open_clnt(dsvc_dnet_t *, dsvc_clnt_t **, uchar_t *, uchar_t,
+ boolean_t);
+extern void close_clnt(dsvc_clnt_t *, boolean_t);
+extern int clnt_netcmp(dsvc_clnt_t *, dsvc_clnt_t *);
+extern void close_clnts(void);
+extern void purge_offer(dsvc_clnt_t *, boolean_t, boolean_t);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PER_DSTORE_H */
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/relay.c b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/relay.c
new file mode 100644
index 0000000000..33f5ff620a
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/relay.c
@@ -0,0 +1,303 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1996-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <assert.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <errno.h>
+#include <netdb.h>
+#include <string.h>
+#include <sys/syslog.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netinet/dhcp.h>
+#include "dhcpd.h"
+#include "per_dnet.h"
+#include "interfaces.h"
+#include <locale.h>
+
+#define MAX_RELAY_IP 5 /* Maximum of destinations */
+
+static struct in_addr relay_ip[MAX_RELAY_IP]; /* IPs of targets */
+static struct in_addr relay_net[MAX_RELAY_IP]; /* target nets */
+static int relay_reply(IF *, PKT_LIST *);
+static int relay_request(IF *, PKT_LIST *);
+
+/*
+ * This file contains the code which implements the BOOTP relay agent.
+ */
+
+/*
+ * Parse arguments. If an agument begins with a digit, then it's
+ * an IP address, otherwise it's a hostname which needs to be
+ * resolved into an IP address.
+ *
+ * Use the arguments to fill in relay_ip array.
+ *
+ * Only callable by main thread. MT UNSAFE
+ */
+int
+relay_agent_init(char *args)
+{
+ int i;
+ struct in_addr *ip;
+ struct hostent *hp;
+ char ntoab[INET_ADDRSTRLEN];
+
+ for (i = 0; i <= MAX_RELAY_IP; i++) {
+ if ((args = strtok(args, ",")) == NULL)
+ break; /* done */
+
+ /*
+ * If there's more than MAX_RELAY_IP addresses
+ * specified that's an error. If we can't
+ * resolve the host name, that's an error.
+ */
+ if (i == MAX_RELAY_IP) {
+ (void) fprintf(stderr,
+ gettext("Too many relay agent destinations.\n"));
+ return (E2BIG);
+ }
+
+ if ((hp = gethostbyname(args)) == NULL) {
+ (void) fprintf(stderr, gettext(
+ "Invalid relay agent destination name: %s\n"),
+ args);
+ return (EINVAL);
+ }
+ /* LINTED [will be lw aligned] */
+ ip = (struct in_addr *)hp->h_addr;
+
+ /*
+ * Note: no way to guess at destination subnet mask,
+ * and verify that it's not a new broadcast addr.
+ */
+ if (ip->s_addr == INADDR_ANY ||
+ ip->s_addr == INADDR_LOOPBACK ||
+ ip->s_addr == INADDR_BROADCAST) {
+ (void) fprintf(stderr, gettext("Relay destination \
+cannot be 0, loopback, or broadcast address.\n"));
+ return (EINVAL);
+ }
+
+ relay_ip[i].s_addr = ip->s_addr;
+
+ ip = &relay_ip[i];
+ get_netmask(ip, &relay_net[i]);
+ relay_net[i].s_addr &= ip->s_addr;
+ if (verbose) {
+ (void) fprintf(stdout,
+ gettext("Relay destination: %s (%s)"),
+ inet_ntop(AF_INET, &relay_ip[i],
+ ntoab, sizeof (ntoab)), args);
+ (void) fprintf(stdout, gettext("\t\tnetwork: %s\n"),
+ inet_ntop(AF_INET, &relay_net[i],
+ ntoab, sizeof (ntoab)));
+ }
+ args = NULL; /* for next call to strtok() */
+ }
+ if (i == 0) {
+ /*
+ * Gotta specify at least one IP addr.
+ */
+ (void) fprintf(stderr,
+ gettext("Specify at least one relay agent destination.\n"));
+ return (ENOENT);
+ }
+ if (i < MAX_RELAY_IP)
+ relay_ip[i].s_addr = NULL; /* terminate the list */
+
+ return (0);
+}
+
+/*
+ * Note: if_head_mtx must be held by caller, as the interface list is
+ * walked in relay_reply.
+ *
+ * MT SAFE
+ */
+int
+relay_agent(IF *ifp, PKT_LIST *plp)
+{
+ if (plp->pkt->op == BOOTREQUEST)
+ return (relay_request(ifp, plp));
+
+ return (relay_reply(ifp, plp));
+}
+
+/*
+ * MT SAFE
+ */
+static int
+relay_request(IF *ifp, PKT_LIST *plp)
+{
+ PKT *pkp;
+ struct sockaddr_in to;
+ struct in_addr ifnet, any;
+ int i;
+ char ntoab[INET_ADDRSTRLEN];
+ char buf[DN_MAX_CID_LEN], *msg;
+
+ pkp = plp->pkt;
+ if (pkp->giaddr.s_addr == 0L)
+ pkp->giaddr.s_addr = ifp->addr.s_addr;
+ pkp->hops++;
+
+ /*
+ * Send it on to the next relay(s)/servers
+ */
+ to.sin_port = htons(IPPORT_BOOTPS + port_offset);
+ any.s_addr = htonl(INADDR_ANY);
+ (void) disp_cid(plp, buf, sizeof (buf));
+
+ for (i = 0; i < MAX_RELAY_IP; i++) {
+ if (relay_ip[i].s_addr == 0L)
+ break; /* we're done */
+
+ ifnet.s_addr = ifp->addr.s_addr & ifp->mask.s_addr;
+ if (relay_net[i].s_addr == ifnet.s_addr) {
+ if (verbose) {
+ dhcpmsg(LOG_INFO, "Target's network: \
+%1$s is the same as client %2$s network, ignored.\n",
+ inet_ntop(AF_INET, &relay_net[i],
+ ntoab, sizeof (ntoab)),
+ buf);
+ }
+ continue; /* skip this target */
+ }
+
+ to.sin_addr.s_addr = relay_ip[i].s_addr;
+
+ if (to.sin_port == htons(IPPORT_BOOTPS + port_offset))
+ msg = "Relaying request %1$s to %2$s, server port.\n";
+ else
+ msg = "Relaying request %1$s to %2$s, client port.\n";
+
+ if (debug) {
+ dhcpmsg(LOG_INFO, msg, buf,
+ inet_ntop(AF_INET, &to.sin_addr,
+ ntoab, sizeof (ntoab)));
+ }
+
+ if (write_interface(ifp, pkp, plp->len, &to)) {
+ dhcpmsg(LOG_INFO, "Cannot relay request %1$s to %2$s\n",
+ buf, inet_ntop(AF_INET, &to.sin_addr,
+ ntoab, sizeof (ntoab)));
+ } else {
+ logtrans(P_BOOTP, L_RELAY_REQ, 0, any, to.sin_addr,
+ plp);
+ }
+ }
+ return (0);
+}
+
+/*
+ * Note: if_head_mtx must be held by caller, as the interface list is
+ * walked here.
+ */
+static int
+relay_reply(IF *ifp, PKT_LIST *plp)
+{
+ int err;
+ IF *tifp;
+ PKT *pkp = plp->pkt;
+ struct in_addr to;
+ char buf[DHCP_MAX_OPT_SIZE];
+ char ntoab_a[INET_ADDRSTRLEN], ntoab_b[INET_ADDRSTRLEN];
+
+ assert(MUTEX_HELD(&if_head_mtx));
+
+ if (pkp->giaddr.s_addr == 0L) {
+ /*
+ * Somehow we picked up a reply packet from a DHCP server
+ * on this net intended for a client on this net. Drop it.
+ */
+ if (verbose) {
+ dhcpmsg(LOG_INFO,
+ "Reply packet without giaddr set ignored.\n");
+ }
+ return (0);
+ }
+
+ /*
+ * We can assume that the message came directly from a dhcp/bootp
+ * server to us, and we are to address it directly to the client.
+ */
+ if (pkp->giaddr.s_addr != ifp->addr.s_addr) {
+ /*
+ * It is possible that this is a multihomed host. We'll
+ * check to see if this is the case, and handle it
+ * appropriately.
+ */
+ for (tifp = if_head; tifp != NULL; tifp = tifp->next) {
+ if (tifp->addr.s_addr == pkp->giaddr.s_addr)
+ break;
+ }
+
+ if (tifp == NULL) {
+ if (verbose) {
+ dhcpmsg(LOG_INFO, "Received relayed reply \
+not intended for this interface: %1$s giaddr: %2$s\n",
+ inet_ntop(AF_INET, &ifp->addr,
+ ntoab_a, sizeof (ntoab_a)),
+ inet_ntop(AF_INET, &pkp->giaddr,
+ ntoab_b, sizeof (ntoab_b)));
+ }
+ return (0);
+ } else
+ ifp = tifp;
+ }
+
+ (void) disp_cid(plp, buf, sizeof (buf));
+ pkp->hops++;
+
+ if (debug)
+ dhcpmsg(LOG_INFO, "Relaying reply to client %s\n", buf);
+
+ if ((ntohs(pkp->flags) & BCAST_MASK) == 0) {
+ if (pkp->yiaddr.s_addr == htonl(INADDR_ANY)) {
+ if (pkp->ciaddr.s_addr == htonl(INADDR_ANY)) {
+ dhcpmsg(LOG_INFO, "No destination IP \
+address or network IP address; cannot send reply to client: %s.\n", buf);
+ return (0);
+ }
+ to.s_addr = pkp->ciaddr.s_addr;
+ } else
+ to.s_addr = pkp->yiaddr.s_addr;
+ }
+
+ if ((err = send_reply(ifp, pkp, plp->len, &to)) == 0)
+ logtrans(P_BOOTP, L_RELAY_REP, 0, ifp->addr, to, plp);
+
+ return (err);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/tests/Makefile b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/tests/Makefile
new file mode 100644
index 0000000000..2e62fe343e
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/tests/Makefile
@@ -0,0 +1,84 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# cmd/cmd-inet/usr.lib/in.dhcpd/tests/Makefile
+#
+
+COMMON_NET = $(SRC)/common/net/dhcp
+
+PROG = test_client
+OBJS = test_client.o
+SRCS = $(OBJS:%.o=%.c)
+
+DPROG = test_dstore
+DOBJS = test_dstore.o
+DSRCS = $(OBJS:%.o=%.c)
+
+MPROG = mkdstore
+MOBJS = mkdstore.o
+MSRCS = $(OBJS:%.o=%.c)
+
+include ../../../../Makefile.cmd
+
+DLIBS = -lrt -linetutil -ldhcpsvc -lsocket -lnsl -lmtmalloc
+LDLIBS += $(DLIBS)
+CPPFLAGS += -g -DNDEBUG -D_REENTRANT -I$(COMMON_NET)
+LINTFLAGS += -Xt
+
+.KEEP_STATE:
+
+all: $(PROG) $(DPROG) $(MPROG)
+
+$(PROG): $(OBJS)
+ $(LINK.c) $(OBJS) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+$(DPROG): $(DOBJS)
+ $(LINK.c) $(DOBJS) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+$(MPROG): $(MOBJS)
+ $(LINK.c) $(MOBJS) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+%.o: $(COMMON_NET)/%.c
+ $(COMPILE.c) $(OUTPUT_OPTION) $<
+ $(POST_PROCESS_O)
+
+clean:
+ $(RM) $(OBJS) $(DOBJS) $(MOBJS)
+
+install:
+ $(ECHO) "Nothing to Install" >&2
+
+
+lint:
+ $(LINT.c) $(PROG).c $(ENVLDLIBS1) $(DLIBS)
+ $(LINT.c) $(DPROG).c $(ENVLDLIBS1) $(DLIBS)
+ $(LINT.c) $(MPROG).c $(ENVLDLIBS1) $(DLIBS)
+
+include ../../../../Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/tests/get_number_test.c b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/tests/get_number_test.c
new file mode 100644
index 0000000000..688aca1af2
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/tests/get_number_test.c
@@ -0,0 +1,216 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+#ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Copyright (c) 1996, by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+
+/*
+ * More generic than get_u_long. Supports byte, short, long, longlong.
+ * Returns 0 for success, -1 for failure.
+ */
+int
+get_number(char **src, void *dest, int len)
+{
+ register unsigned base;
+ register char c;
+
+ if (len != 1 && (len % 2) != 0 || len > 8)
+ return (-1); /* not valid */
+ /*
+ * Collect number up to first illegal character. Values are specified
+ * as for C: 0x=hex, 0=octal, other=decimal.
+ */
+ base = 10;
+ if (**src == '0') {
+ base = 8;
+ (*src)++;
+ }
+ if (**src == 'x' || **src == 'X') {
+ base = 16,
+ (*src)++;
+ }
+
+ while (c = **src) {
+ if (isdigit(c)) {
+ switch (len) {
+ case 1:
+ *(u_char *) dest =
+ (*(u_char *) dest) * base + (c - '0');
+ break;
+ case 2:
+ *(u_short *) dest = (*(u_short *) dest) *
+ base + (c - '0');
+ break;
+ case 4:
+ *(u_long *) dest = (*(u_long *) dest) *
+ base + (c - '0');
+ break;
+ case 8:
+ *(u_longlong_t *) dest =
+ (*(u_longlong_t *) dest) * base +
+ (c - '0');
+ break;
+ }
+ (*src)++;
+ continue;
+ }
+ if (base == 16 && isxdigit(c)) {
+ switch (len) {
+ case 1:
+ *(u_char *) dest =
+ ((*(u_char *) dest) << 4) + ((c & ~32) +
+ 10 - 'A');
+ break;
+ case 2:
+ *(u_short *) dest =
+ ((*(u_short *) dest) << 4) + ((c & ~32) +
+ 10 - 'A');
+ break;
+ case 4:
+ *(u_long *) dest =
+ ((*(u_long *) dest) << 4) + ((c & ~32) +
+ 10 - 'A');
+ break;
+ case 8:
+ *(u_longlong_t *) dest =
+ ((*(u_longlong_t *) dest) << 4) +
+ ((c & ~32) + 10 - 'A');
+ break;
+ }
+ (*src)++;
+ continue;
+ }
+ break;
+ }
+ return (0);
+}
+main()
+{
+ char *src;
+ u_char one;
+ u_short two;
+ u_long four;
+ u_longlong_t eight;
+
+ /*
+ * Try single octet (dec)
+ */
+ src = "a56";
+ one = 0;
+ if (get_number(&src, (void *) &one, 1) != 0)
+ printf("byte failed.\n");
+ else
+ printf("byte: %d\n", one);
+
+ src = "65535";
+ two = 0;
+ if (get_number(&src, (void *) &two, 2) != 0)
+ printf("short failed.\n");
+ else
+ printf("short: %d\n", two);
+
+ src = "4294967296";
+ four = 0;
+ if (get_number(&src, (void *) &four, 4) != 0)
+ printf("long failed.\n");
+ else
+ printf("long: %d\n", four);
+
+ src = "4289672944289672944";
+ eight = 0;
+ if (get_number(&src, (void *) &eight, 8) != 0)
+ printf("longlong failed.\n");
+ else
+ printf("longlong: %d\n", eight);
+
+
+
+ /*
+ * Try single octet (hex)
+ */
+ src = "0xff";
+ one = 0;
+ if (get_number(&src, (void *) &one, 1) != 0)
+ printf("byte failed.\n");
+ else
+ printf("byte: 0x%x\n", one);
+
+ src = "0xffff";
+ two = 0;
+ if (get_number(&src, (void *) &two, 2) != 0)
+ printf("short failed.\n");
+ else
+ printf("short: 0x%x\n", two);
+
+ src = "0xffffffff";
+ four = 0;
+ if (get_number(&src, (void *) &four, 4) != 0)
+ printf("long failed.\n");
+ else
+ printf("long: 0x%x\n", four);
+
+ src = "0xffffffffffffffff";
+ eight = 0;
+ if (get_number(&src, (void *) &eight, 8) != 0)
+ printf("longlong failed.\n");
+ else
+ printf("longlong: 0x%x\n", eight);
+
+ /*
+ * Try single octet (Oct)
+ */
+ src = "0376";
+ one = 0;
+ if (get_number(&src, (void *) &one, 1) != 0)
+ printf("byte failed.\n");
+ else
+ printf("byte: 0x%x\n", one);
+
+ src = "0177776";
+ two = 0;
+ if (get_number(&src, (void *) &two, 2) != 0)
+ printf("short failed.\n");
+ else
+ printf("short: 0x%x\n", two);
+
+ src = "037777777776";
+ four = 0;
+ if (get_number(&src, (void *) &four, 4) != 0)
+ printf("long failed.\n");
+ else
+ printf("long: 0x%x\n", four);
+
+ src = "01777777777777777777776";
+ eight = 0;
+ if (get_number(&src, (void *) &eight, 8) != 0)
+ printf("longlong failed.\n");
+ else
+ printf("longlong: 0x%x\n", eight);
+ return (0);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/tests/mkdstore.c b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/tests/mkdstore.c
new file mode 100644
index 0000000000..0b30377918
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/tests/mkdstore.c
@@ -0,0 +1,184 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2001-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * mkdstore: fast datastore creation program.
+ *
+ * mkdstore <table> <nrecords> <cid> <flags> <cip> <sip> <lease> <comment>
+ */
+
+#include <stdio.h>
+#include <netdb.h>
+
+#include <string.h>
+#include <stdlib.h>
+#include <rpcsvc/nis.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <assert.h>
+#include <string.h>
+#include <syslog.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <arpa/inet.h>
+#include <netinet/dhcp.h>
+#include <netdb.h>
+#include <locale.h>
+#include <signal.h>
+#include <tnf/probe.h>
+
+#include <dhcp_svc_confopt.h>
+#include <dhcp_svc_private.h>
+#include <dhcp_impl.h>
+
+dsvc_handle_t dh; /* data handle */
+dsvc_datastore_t datastore; /* Datastore for container access */
+uint_t nrecords;
+char network[INET_ADDRSTRLEN];
+struct in_addr net;
+
+/*
+ * mkdstore <table> <nrecords> <cid> <flags> <cip> <sip> <lease> <macro>
+ * <comment>
+ */
+main(int c, char **v)
+{
+ long long cid;
+ uchar_t flags;
+ struct in_addr cip;
+ struct in_addr sip;
+ int i, j;
+ char **entries;
+ uint_t lease;
+ char *network = v[1];
+ int ct = strtol(v[2], 0L, 0L);
+ char *server;
+ char *macro;
+ int err;
+ uint32_t query;
+ dn_rec_t dn;
+ dn_rec_list_t *dncp = NULL;
+ dhcp_confopt_t *dsp = NULL;
+
+#ifdef DEBUG
+ mallocctl(MTDEBUGPATTERN, 1);
+ mallocctl(MTINITBUFFER, 1);
+#endif /* DEBUG */
+
+ if (c == 1) {
+ (void) fprintf(stderr, "/*\n * mkdstore <table> <nrecords> "
+ "<cid> <flags> <cip> <sip> <lease> <comment>\n*/");
+ return (0);
+ }
+
+ cid = (c > 3) ? strtoul(v[3], 0L, 0L) : 0;
+ flags = (c > 4) ? (char)strtol(v[4], 0L, 0L) : 0;
+ cip.s_addr = (c > 5) ? strtoul(v[5], 0L, 0L) : 0;
+ sip.s_addr = (c > 6) ? strtoul(v[6], 0L, 0L) : 0;
+ lease = (c > 7) ? strtoul(v[7], 0L, 0L) : 0;
+ macro = (c > 8) ? v[8] : 0;
+ server = (c > 9) ? v[9] : "unknown";
+
+ entries = (char **) malloc(ct * (sizeof (char *) * 8 + 4));
+
+ /* Load current datastore. */
+ (void) read_dsvc_conf(&dsp);
+ if ((i = confopt_to_datastore(dsp, &datastore)) != DSVC_SUCCESS) {
+ (void) fprintf(stderr, "Invalid datastore: %s\n",
+ dhcpsvc_errmsg(i));
+ return (EINVAL);
+ }
+ err = open_dd(&dh, &datastore, DSVC_DHCPNETWORK, network,
+ DSVC_READ | DSVC_WRITE);
+
+ if (err != DSVC_SUCCESS) {
+ (void) fprintf(stderr, "Invalid network: %s trying create...\n",
+ dhcpsvc_errmsg(err));
+
+ err = open_dd(&dh, &datastore, DSVC_DHCPNETWORK, network,
+ DSVC_READ | DSVC_WRITE | DSVC_CREATE);
+ if (err != DSVC_SUCCESS) {
+ (void) fprintf(stderr, "Can't create network: %s\n",
+ dhcpsvc_errmsg(err));
+ return (err);
+ }
+ }
+ /* XXXX: bug: currently can't get the count as advertised */
+ (void) memset(&dn, '\0', sizeof (dn));
+ DSVC_QINIT(query);
+ err = lookup_dd(dh, B_FALSE, query, -1,
+ (const void *) &dn, (void **) &dncp, &nrecords);
+ if (dncp)
+ free_dd_list(dh, dncp);
+
+ if (err != DSVC_SUCCESS) {
+ (void) fprintf(stderr, "Bad nrecords: %s [%d]\n",
+ dhcpsvc_errmsg(err), nrecords);
+ return (err);
+ }
+
+ for (i = 0, j = 0; i < ct; i++) {
+ TNF_PROBE_1(main, "main",
+ "main%debug 'in function main'",
+ tnf_ulong, record, i);
+ if (cid) {
+ (void) memcpy(dn.dn_cid, &cid, sizeof (long long));
+ dn.dn_cid_len = 7;
+ } else {
+ (void) memset(dn.dn_cid, '\0', sizeof (long long));
+ dn.dn_cid_len = 1;
+ }
+ dn.dn_sig = 0;
+ dn.dn_flags = flags;
+ dn.dn_cip.s_addr = cip.s_addr;
+ dn.dn_sip.s_addr = sip.s_addr;
+ dn.dn_lease = lease;
+ strcpy(dn.dn_macro, macro);
+ strcpy(dn.dn_comment, server);
+ (void) add_dd_entry(dh, &dn);
+ if (cid)
+ cid += 0x100;
+ cip.s_addr++;
+
+ TNF_PROBE_0(main_end, "main", "");
+ }
+ (void) close_dd(&dh);
+
+ return (0);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/tests/test_client.c b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/tests/test_client.c
new file mode 100644
index 0000000000..0f84bc8f66
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/tests/test_client.c
@@ -0,0 +1,1444 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1993-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <string.h>
+#include <syslog.h>
+#include <signal.h>
+#include <time.h>
+#include <limits.h>
+#include <sys/resource.h>
+#include <sys/fcntl.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/systeminfo.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <sys/stropts.h>
+#include <netinet/dhcp.h>
+#include <dhcp_impl.h>
+#include <synch.h>
+#include <netdb.h>
+#include <locale.h>
+#include <mtmalloc.h>
+#include <tnf/probe.h>
+#include <libinetutil.h>
+
+struct client {
+ thread_t id;
+ PKT_LIST *pktlistp;
+ cond_t cv;
+ cond_t acv;
+ mutex_t mtx;
+ int proto;
+ uchar_t chaddr[20];
+ char chost[40];
+ int hlen;
+ uint_t xid;
+ int flags;
+ time_t ltime;
+ int state;
+};
+
+#define CLIENT_BUSY 0x1
+#define CLIENT_FIRSTTIME 0x2
+
+ushort_t port_offset = 0; /* offset to port for multiple server */
+int fast = 0; /* higher load */
+int bound = 0; /* only broadcast on given interface */
+int lrecv = 1; /* only receive on given interface */
+static struct in_addr relay; /* spoof being a relay agent */
+static struct sockaddr_in from, relfrom;
+static int clients, s, srelay = -1;
+static struct client *clientsp;
+
+static PKT request;
+static char ifname[IFNAMSIZ];
+static int startindex;
+static mutex_t go_mtx;
+static cond_t go_cv;
+static boolean_t time_to_go;
+static int release_time = 0;
+static int desynch = 1;
+static double avg = 0;
+static timespec_t avgslp;
+static volatile ulong_t tops, otops;
+static volatile ulong_t minops[6];
+static volatile time_t mintim[6];
+static volatile int minind;
+long sample_time = 10L;
+long nsamples = 2;
+
+static volatile ulong_t ops_outstanding;
+static time_t start, ostart;
+int verbose = 0;
+int dohost = 0;
+int randcl = 0;
+int randhlen = 0;
+int randerr = 0;
+int dos = 0;
+int dofork = 0;
+int printid = 0;
+
+static time_t ltime;
+static struct lifreq lifr;
+
+static void corrupt(char *, int);
+
+static void
+dhcpmsgtype(uchar_t pkt, char *buf)
+{
+ char *p;
+
+ switch (pkt) {
+ case DISCOVER:
+ p = "DISCOVER";
+ break;
+ case OFFER:
+ p = "OFFER";
+ break;
+ case REQUEST:
+ p = "REQUEST";
+ break;
+ case DECLINE:
+ p = "DECLINE";
+ break;
+ case ACK:
+ p = "ACK";
+ break;
+ case NAK:
+ p = "NAK";
+ break;
+ case RELEASE:
+ p = "RELEASE";
+ break;
+ case INFORM:
+ p = "INFORM";
+ break;
+ default:
+ p = "UNKNOWN";
+ break;
+ }
+
+ (void) strcpy(buf, p);
+}
+
+static int
+closeif(int ms, char *cifname, struct sockaddr_in *myip, thread_t myself)
+{
+ struct ifreq ifr;
+ int error = 0;
+
+ (void) strcpy(ifr.ifr_name, cifname);
+ if (ioctl(ms, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) {
+ (void) fprintf(stderr,
+ "Client %04d - can't get interface flags on %s\n", myself,
+ cifname);
+ error = 7;
+ }
+ ifr.ifr_flags &= ~IFF_UP;
+ if (ioctl(ms, SIOCSIFFLAGS, (caddr_t)&ifr) < 0) {
+ (void) fprintf(stderr,
+ "Client %04d - can't set interface flags on %s\n", myself,
+ cifname);
+ error = 7;
+ }
+ myip->sin_addr.s_addr = htonl(INADDR_ANY);
+ ifr.ifr_addr = *(struct sockaddr *)myip;
+ if (ioctl(ms, SIOCSIFADDR, (caddr_t)&ifr)) {
+ (void) fprintf(stderr,
+ "Client %04d - Can't unset address on %s\n", myself,
+ cifname);
+ error = 8;
+ }
+ (void) close(ms);
+ return (error);
+}
+
+static void *
+client(void *args)
+{
+ PKT crequest, *irequestp;
+ PKT_LIST *bp = NULL, *wbp, *twbp;
+ struct client *mep = (struct client *)args;
+ time_t retry_time = 2, lease, sleep_time = 0;
+ uchar_t *endp;
+ boolean_t done, config, timeout;
+ int nstate, ms = -1;
+ DHCP_OPT *optp, *unused_optp;
+ timespec_t ts, tr;
+ int error = 0;
+ thread_t myself = thr_self();
+ struct sockaddr_in to, myip, maskip;
+ struct in_addr serverip;
+ time_t start_time, expired = 0;
+ char cid[BUFSIZ];
+ char cifname[IFNAMSIZ];
+ char host[40];
+ char domain[40];
+ char p[30], np[30];
+ struct ifreq ifr;
+ int moldy;
+ int i;
+ uint_t cidlen;
+ char *domainp;
+
+forever:
+ if (bp) {
+ (void) free(bp->pkt);
+ (void) free(bp);
+ bp = NULL;
+ }
+ if (!time_to_go) {
+ (void) mutex_lock(&mep->mtx);
+ mep->flags &= ~CLIENT_BUSY;
+ if (mep->flags & CLIENT_FIRSTTIME) {
+ mep->flags &= ~CLIENT_FIRSTTIME;
+ } else {
+ tops++;
+ ops_outstanding--;
+ }
+ if (avg)
+ (void) cond_wait(&mep->acv, &mep->mtx);
+ mep->flags |= CLIENT_BUSY;
+ mep->ltime = time(NULL);
+ ops_outstanding++;
+ (void) mutex_unlock(&mep->mtx);
+ }
+ if (desynch)
+ (void) sleep((desynch & myself) + 3); /* desynch clients */
+ if (verbose == 1)
+ (void) fprintf(stdout, "Client %04d - started.\n", myself);
+ start_time = time(NULL);
+ (void) sprintf(cifname, "%s:%d", ifname, startindex + myself);
+
+ /* reset client addr each time */
+ if (relay.s_addr != INADDR_ANY) {
+ to.sin_addr.s_addr = relay.s_addr;
+ } else {
+ to.sin_addr.s_addr = INADDR_BROADCAST;
+ }
+ to.sin_port = htons(IPPORT_BOOTPS + port_offset);
+ to.sin_family = AF_INET;
+
+ domain[0] = host[0] = NULL;
+ if (randcl) {
+ /* Further randomize. */
+ if (randhlen > 0) {
+ mep->hlen = randhlen;
+ }
+
+ for (i = 3; i < mep->hlen; i++) {
+ mep->chaddr[i] = random() & 0xff;
+ }
+ }
+
+ (void) memcpy(&crequest, &request, sizeof (request));
+ (void) memcpy(crequest.chaddr, mep->chaddr, mep->hlen);
+ crequest.hlen = mep->hlen;
+
+
+ if (mep->proto) {
+ mep->state = DISCOVER;
+ optp = (DHCP_OPT *) & crequest.options[3]; /* skip TYPE */
+ optp->code = CD_CLIENT_ID;
+ optp->len = mep->hlen + 1;
+ optp->value[0] = 0x01;
+ (void) memcpy(&optp->value[1], mep->chaddr, mep->hlen);
+ cidlen = sizeof (cid);
+ (void) octet_to_hexascii(optp->value, mep->hlen + 1, cid,
+ &cidlen);
+ unused_optp = (DHCP_OPT *) & optp->value[mep->hlen + 1];
+ } else {
+ mep->state = 0;
+ cidlen = sizeof (cid);
+ (void) octet_to_hexascii(mep->chaddr, mep->hlen, cid, &cidlen);
+ unused_optp = (DHCP_OPT *)&crequest.options[3]; /* skip TYPE */
+ }
+
+ /* Use global descriptor at first */
+ ms = s;
+
+ myip.sin_addr.s_addr = htonl(INADDR_ANY);
+ done = B_FALSE;
+ config = B_FALSE;
+ do {
+ timeout = B_FALSE;
+
+ TNF_PROBE_2(client,
+ "client",
+ "client%debug 'in func client'",
+ tnf_ulong, state, mep->state,
+ tnf_string, cid, (char *)cid);
+
+ if (time_to_go) {
+ if (mep->state == ACK) {
+ mep->state = RELEASE;
+ if (verbose == 1)
+ (void) fprintf(stderr,
+ "Client %04d - RELEASEing %s\n",
+ myself, inet_ntoa(myip.sin_addr));
+ else if (verbose == 2)
+ fprintf(stderr, "[%d %s]",
+ clientsp[i].id,
+ inet_ntoa(myip.sin_addr));
+ optp = (DHCP_OPT *) crequest.options;
+ (void) memset((char *)unused_optp, 0,
+ (int)((char *)&crequest.options[
+ sizeof (crequest.options)] -
+ (char *)unused_optp));
+ optp->value[0] = RELEASE;
+ } else {
+ done = B_TRUE;
+ if (verbose == 1)
+ (void) fprintf(stderr,
+ "Client %04d - terminated.\n",
+ myself);
+ break;
+ }
+ } else if (release_time || avg) {
+ if (mep->state == ACK) {
+
+ /* lru testing: don't release lease */
+ if (randcl & 0x2) {
+ done = B_FALSE;
+ mep->state = nstate = 0;
+ sleep_time = 0;
+ if (bp) {
+ (void) free(bp->pkt);
+ (void) free(bp);
+ bp = NULL;
+ }
+ goto forever;
+ }
+ mep->state = RELEASE;
+ if (verbose == 1)
+ (void) fprintf(stderr,
+ "Client %04d - RELEASEing %s\n",
+ myself, inet_ntoa(myip.sin_addr));
+ else if (verbose == 2)
+ fprintf(stderr, "[%d %s]",
+ clientsp[i].id,
+ inet_ntoa(myip.sin_addr));
+ optp = (DHCP_OPT *) crequest.options;
+ (void) memset((char *)unused_optp, 0,
+ (int)((char *)&crequest.options[
+ sizeof (crequest.options)] -
+ (char *)unused_optp));
+ optp->value[0] = RELEASE;
+ }
+ }
+ if (mep->state == REQUEST && expired < time(NULL)) {
+ /* drop back to INIT state. */
+ if (verbose == 1)
+ (void) fprintf(stderr,
+ "Client %04d - Dropping back to INIT.\n",
+ myself);
+ done = B_FALSE;
+ mep->state = nstate = 0;
+ sleep_time = 0;
+ if (bp) {
+ (void) free(bp->pkt);
+ (void) free(bp);
+ bp = NULL;
+ }
+ goto forever;
+ }
+ if (mep->state == RELEASE && !time_to_go) {
+ (void) mutex_lock(&mep->mtx);
+ tops++;
+ ops_outstanding--;
+ mep->flags &= ~CLIENT_BUSY;
+ if (avg)
+ (void) cond_wait(&mep->acv, &mep->mtx);
+ mep->ltime = time(NULL);
+ ops_outstanding++;
+ mep->flags |= CLIENT_BUSY;
+ (void) mutex_unlock(&mep->mtx);
+ }
+ /* Send request... */
+ crequest.secs = htons((ushort_t)(time(NULL) - start_time));
+ crequest.xid = htonl((myself << 2) + mep->xid++);
+
+ /* Randomly corrupt packets of a certain type. */
+ if ((randerr & 0xF) == mep->state || (randerr & 0xF) == 0xF) {
+ if (randerr & 0x10) {
+ /* Randomly corrupt entire request. */
+ corrupt((char *)&crequest, sizeof (crequest));
+ } else {
+ /* Randomly corrupt options. */
+ corrupt((char *)&crequest.options[3],
+ sizeof (crequest.options) - 3);
+ }
+ }
+
+ if (sendto(ms, (char *)&crequest, sizeof (PKT), 0,
+ (struct sockaddr *)&to, sizeof (struct sockaddr)) < 0) {
+ perror("Sendto");
+ error = 4;
+ thr_exit(&error);
+ }
+ if (mep->state == RELEASE) {
+ done = B_TRUE;
+ if (!avg) {
+ (void) strcpy(ifr.ifr_name, cifname);
+ if (ioctl(ms, SIOCGIFFLAGS,
+ (caddr_t)&ifr) < 0) {
+ (void) fprintf(stderr, "Client %04d - "
+ "can't get interface flags on %s\n",
+ myself, cifname);
+ error = 7;
+ }
+ ifr.ifr_flags &= ~IFF_UP;
+ if (ioctl(ms, SIOCSIFFLAGS,
+ (caddr_t)&ifr) < 0) {
+ (void) fprintf(stderr, "Client %04d - "
+ "can't set interface flags on %s\n",
+ myself, cifname);
+ error = 7;
+ }
+ myip.sin_addr.s_addr = htonl(INADDR_ANY);
+ ifr.ifr_addr = *(struct sockaddr *)&myip;
+ if (ioctl(ms, SIOCSIFADDR, (caddr_t)&ifr)) {
+ (void) fprintf(stderr, "Client %04d - "
+ "Can't unset address on %s\n",
+ myself, cifname);
+ error = 8;
+ }
+ (void) close(ms);
+ }
+ if (release_time || avg) {
+ done = B_FALSE;
+ mep->state = nstate = 0;
+ sleep_time = 0;
+ if (bp) {
+ (void) free(bp->pkt);
+ (void) free(bp);
+ bp = NULL;
+ }
+ goto forever;
+ }
+ break;
+ }
+ /* await reply */
+moldy:
+ (void) mutex_lock(&mep->mtx);
+ ts.tv_sec = time(NULL) + retry_time;
+ ts.tv_nsec = 0;
+
+ while (mep->pktlistp == NULL)
+ if (cond_timedwait(&mep->cv, &mep->mtx, &ts) == ETIME) {
+ timeout = B_TRUE;
+ if (retry_time > 64)
+ retry_time = 2;
+ else if (fast)
+ retry_time += 2;
+ else
+ retry_time *= 2;
+ break;
+ } else {
+ if (time_to_go)
+ break;
+ }
+ (void) mutex_unlock(&mep->mtx);
+
+ if (time_to_go || timeout)
+ continue;
+
+ (void) mutex_lock(&mep->mtx);
+ moldy = 0;
+ if (bp) {
+ (void) free(bp->pkt);
+ (void) free(bp);
+ }
+ bp = NULL;
+ wbp = mep->pktlistp;
+ while (wbp != NULL) {
+ irequestp = wbp->pkt;
+ if (bp == NULL && irequestp->op == BOOTREPLY &&
+ memcmp(&crequest.xid, &irequestp->xid,
+ sizeof (crequest.xid)) == 0) {
+ bp = wbp;
+ wbp = wbp->next;
+ continue;
+ }
+ (void) free(wbp->pkt);
+ twbp = wbp;
+ wbp = wbp->next;
+ (void) free(twbp);
+ if (verbose == 1)
+ (void) fprintf(stderr,
+ "Client %04d - Moldy xid\n", myself);
+ moldy++;
+ }
+
+ mep->pktlistp = NULL;
+ (void) mutex_unlock(&mep->mtx);
+
+ if (bp == NULL) {
+ if (moldy > 0)
+ goto moldy;
+
+ continue;
+ }
+ irequestp = bp->pkt;
+
+ if (mep->proto) {
+ /*
+ * Scan for CD_DHCP_TYPE, CD_SERVER_ID, and
+ * CD_LEASE_TIME if proto.
+ */
+ nstate = 0;
+ maskip.sin_addr.s_addr = serverip.s_addr = INADDR_ANY;
+ maskip.sin_family = AF_INET;
+ lease = (time_t)0;
+ optp = (DHCP_OPT *) irequestp->options;
+ endp = (uchar_t *)irequestp + bp->len;
+ host[0] = NULL;
+ while ((uchar_t *)optp < (uchar_t *)endp) {
+ switch (optp->code) {
+ case CD_HOSTNAME:
+ (void) strncpy(host,
+ (const char *)optp->value,
+ optp->len);
+ host[optp->len] = '\0';
+ break;
+ case CD_DNSDOMAIN:
+ (void) strncpy(domain,
+ (const char *)optp->value,
+ optp->len);
+ domain[optp->len] = '\0';
+ break;
+ case CD_DHCP_TYPE:
+ nstate = optp->value[0];
+ break;
+ case CD_SUBNETMASK:
+ (void) memcpy(&maskip.sin_addr,
+ optp->value,
+ sizeof (struct in_addr));
+ break;
+ case CD_SERVER_ID:
+ (void) memcpy(&serverip, optp->value,
+ sizeof (struct in_addr));
+ break;
+ case CD_LEASE_TIME:
+ (void) memcpy(&lease, optp->value,
+ sizeof (time_t));
+ lease = htonl(lease);
+ break;
+ }
+ optp = (DHCP_OPT *) & optp->value[optp->len];
+ }
+ if (mep->state == DISCOVER && nstate == OFFER) {
+ mep->state = REQUEST;
+ expired = time(NULL) + 60;
+ /*
+ * Add in the requested IP address option and
+ * server ID.
+ */
+ optp = (DHCP_OPT *) crequest.options;
+ optp->value[0] = REQUEST;
+ optp = unused_optp; /* step over CD_DHCP_TYPE */
+ optp->code = CD_REQUESTED_IP_ADDR;
+ optp->len = sizeof (struct in_addr);
+ (void) memcpy(optp->value, &irequestp->yiaddr,
+ sizeof (struct in_addr));
+ optp = (DHCP_OPT *) & optp->value[
+ sizeof (struct in_addr)];
+ optp->code = CD_SERVER_ID;
+ optp->len = sizeof (struct in_addr);
+ (void) memcpy(optp->value, &serverip,
+ sizeof (struct in_addr));
+ optp = (DHCP_OPT *) & optp->value[
+ sizeof (struct in_addr)];
+ if (dohost == 0) {
+ if (bp) {
+ (void) free(bp->pkt);
+ (void) free(bp);
+ bp = NULL;
+ }
+ continue;
+ }
+
+ if (domain[0] == '\0' && host[0] != '\0' &&
+ (domainp = strchr(host, '.')) != NULL) {
+ (void) snprintf(domain, sizeof (domain),
+ "%s", domainp);
+ }
+
+ if (dohost & 0x2) {
+ cidlen = sizeof (cid);
+ (void) octet_to_hexascii(mep->chaddr,
+ mep->hlen, host, &cidlen);
+
+ if (domain[0])
+ (void) snprintf(host,
+ sizeof (host), "%s.%s",
+ cid, domain);
+ else
+ (void) snprintf(host,
+ sizeof (host), "%s", cid);
+ }
+
+ optp->code = CD_HOSTNAME;
+ optp->len = strlen(host);
+ (void) memcpy(optp->value, host, strlen(host));
+ optp->value[strlen(host)] = '\0';
+ if (randcl && (random() & 0x1)) {
+ /* create a random name */
+ for (i = 0; i < optp->len &&
+ optp->value[i] != '.'; i++)
+ if (i & 1)
+ optp->value[i] = '0' +
+ (mep->chaddr[i] & 0x7);
+ else
+ optp->value[i] = 'a' +
+ (mep->chaddr[i] & 0x7);
+ strcpy((char *)mep->chost,
+ (const char *)optp->value);
+ } else if (randcl && mep->chost[0]) {
+ /* use the previous one */
+ optp->len = strlen(mep->chost);
+ (void) memcpy(optp->value, mep->chost,
+ strlen(mep->chost));
+ optp->value[strlen(mep->chost)] = '\0';
+ }
+ if (bp) {
+ (void) free(bp->pkt);
+ (void) free(bp);
+ bp = NULL;
+ }
+ continue;
+ } else if ((mep->state == REQUEST ||
+ mep->state == ACK) && nstate == ACK) {
+ /*
+ * we're bound. defend the lease. Add the
+ * address to our interface. Due to the
+ * service architecture of this program, we
+ * can't unset the broadcast bit..
+ */
+ mep->state = ACK;
+ nstate = 0;
+ retry_time = 2;
+ myip.sin_family = AF_INET;
+ myip.sin_addr.s_addr = irequestp->yiaddr.s_addr;
+ crequest.ciaddr.s_addr = myip.sin_addr.s_addr;
+ optp = unused_optp;
+ optp->code = CD_LEASE_TIME;
+ optp->len = sizeof (time_t);
+ (void) memcpy(optp->value, &lease,
+ sizeof (time_t));
+ optp = (DHCP_OPT *)
+ & optp->value[sizeof (time_t)];
+ (void) memset((char *)optp, 0, (int)((char *)
+ &crequest.options[
+ sizeof (crequest.options)] -
+ (char *)optp));
+ to.sin_addr.s_addr = serverip.s_addr;
+
+ if (lease == -1) {
+ done = B_TRUE; /* permanent lease */
+ sleep_time = 0;
+ } else {
+ sleep_time = lease / 2;
+ lease = time(NULL) + lease;
+ }
+
+ if (release_time || avg) {
+ sleep_time = release_time;
+ done = B_FALSE;
+ }
+ if (verbose == 1)
+ (void) fprintf(stdout,
+ "Client %04d(%s) - DHCP: %s == %s",
+ myself, cid,
+ inet_ntoa(myip.sin_addr),
+ (lease == -1) ? "Forever\n" :
+ ctime(&lease));
+ else if (verbose == 2)
+ fprintf(stderr, "(%d %s)", mep->id,
+ cid);
+ if (!config && !avg) {
+ /* Add mask and address */
+ if ((ms = socket(AF_INET, SOCK_DGRAM,
+ 0)) < 0) {
+ (void) fprintf(stderr,
+ "Client %04d - can't open "
+ "DGRAM socket.\n", myself);
+ error = 7;
+ break;
+ }
+ (void) strcpy(ifr.ifr_name, cifname);
+ ifr.ifr_addr =
+ *(struct sockaddr *)&myip;
+ /*
+ * XXXX: needed in on81
+ * for initial
+ * interface creation
+ */
+ (void) (ioctl(ms, SIOCLIFADDIF,
+ (caddr_t)&ifr));
+ (void) strcpy(ifr.ifr_name, cifname);
+ ifr.ifr_addr =
+ *(struct sockaddr *)&maskip;
+ if (ioctl(ms, SIOCSIFNETMASK,
+ (caddr_t)&ifr)) {
+ (void) fprintf(stderr,
+ "Client %04d - Can't set "
+ "netmask: %s on %s\n",
+ myself,
+ inet_ntoa(maskip.sin_addr),
+ cifname);
+ error = 7;
+ (void) close(ms);
+ break;
+ }
+ if (ioctl(ms, SIOCGIFFLAGS,
+ (caddr_t)&ifr) < 0) {
+ (void) fprintf(stderr,
+ "Client %04d - can't get "
+ "interface flags on %s\n",
+ myself, cifname);
+ error = 7;
+ (void) close(ms);
+ break;
+ }
+ ifr.ifr_flags |= IFF_UP;
+ if (ioctl(ms, SIOCSIFFLAGS,
+ (caddr_t)&ifr) < 0) {
+ (void) fprintf(stderr,
+ "Client %04d - can't set "
+ "interface flags on %s\n",
+ myself, cifname);
+ error = 7;
+ (void) close(ms);
+ break;
+ }
+ ifr.ifr_addr =
+ *(struct sockaddr *)&myip;
+ if (ioctl(ms, SIOCSIFADDR,
+ (caddr_t)&ifr)) {
+ (void) fprintf(stderr,
+ "Client %04d - Can't set "
+ "address on %s\n",
+ myself, cifname);
+ error = 8;
+ (void) close(ms);
+ break;
+ }
+ config = B_TRUE;
+ }
+ if (sleep_time != 0) {
+ /* Go to sleep for 50% of lease time. */
+ tr.tv_sec = time(NULL) + sleep_time;
+ if (verbose == 1)
+ (void) fprintf(stderr,
+ "Client %04d - sleeping "
+ "until %s", myself,
+ ctime(&tr.tv_sec));
+ tr.tv_nsec = 0;
+ (void) mutex_lock(&go_mtx);
+ while (!time_to_go) {
+ if (cond_timedwait(&go_cv,
+ &go_mtx, &tr) == ETIME)
+ break;
+ }
+ (void) mutex_unlock(&go_mtx);
+ if (verbose == 1)
+ (void) fprintf(stderr,
+ "Client %04d - awake\n",
+ myself);
+ }
+ } else if (mep->state == ACK && nstate == NAK) {
+ /* drop back to INIT state. */
+ if (verbose == 1) {
+ (void) fprintf(stdout, "Client %04d - "
+ "DHCP: we got NAKed.\n", myself);
+ (void) fprintf(stderr, "Client %04d - "
+ "Dropping back to INIT.\n", myself);
+ }
+ if (!avg)
+ (void) closeif(ms, cifname,
+ &myip, myself);
+ done = B_FALSE;
+ mep->state = nstate = 0;
+ sleep_time = 0;
+ if (bp) {
+ (void) free(bp->pkt);
+ (void) free(bp);
+ bp = NULL;
+ }
+ goto forever;
+ } else {
+ dhcpmsgtype(nstate, np);
+ dhcpmsgtype(mep->state, p);
+ (void) fprintf(stderr, "Client %04d - "
+ "unexpected mesg: %s, when I'm in state: "
+ "%s.\n", myself, np, p);
+ error = 9;
+ break;
+ }
+ } else {
+ done = B_TRUE; /* BOOTP is done */
+ if (verbose == 1)
+ (void) fprintf(stdout,
+ "Client %04d(%s) - BOOTP: %s\n", myself,
+ cid, inet_ntoa(irequestp->yiaddr));
+ if (release_time || avg) {
+ done = B_FALSE;
+ mep->state = nstate = 0;
+ sleep_time = 0;
+ if (bp) {
+ (void) free(bp->pkt);
+ (void) free(bp);
+ bp = NULL;
+ }
+ goto forever;
+ }
+ }
+ if (bp) {
+ (void) free(bp->pkt);
+ (void) free(bp);
+ bp = NULL;
+ }
+ } while (!done);
+
+ if (!done) {
+ (void) fprintf(stderr,
+ "Client %04d - %s: configuration failed.\n",
+ myself, (mep->proto) ? "DHCP" : "BOOTP");
+ }
+ wbp = mep->pktlistp;
+ while (wbp != NULL) {
+ twbp = wbp->next;
+ if (wbp->pkt != NULL)
+ (void) free(wbp->pkt);
+ (void) free(wbp);
+ wbp = twbp;
+ }
+
+ thr_exit(&error);
+ return (NULL); /* NOTREACHED */
+}
+
+/*
+ * Never returns. Just loads client lists.
+ */
+static void *
+service(void *args)
+{
+ struct client *clientp = (struct client *)args;
+ PKT_LIST *bp, *wbp;
+ PKT *irequestp;
+ int error = 0;
+ struct pollfd pfd[2];
+ ulong_t *bufp; /* ulong_t to force alignment */
+ int len, i;
+
+ pfd[0].fd = s;
+ pfd[0].events = POLLIN | POLLPRI;
+ if (relay.s_addr != INADDR_ANY) {
+ pfd[1].fd = srelay;
+ pfd[1].events = POLLIN | POLLPRI;
+ } else {
+ pfd[1].fd = -1;
+ pfd[1].events = 0;
+ }
+
+ for (;;) {
+ pfd[0].revents = 0;
+ pfd[1].revents = 0;
+ if (poll(pfd, (nfds_t)2, INFTIM) < 0) {
+ (void) fprintf(stderr, "Service - can't poll...\n");
+ error = 5;
+ break;
+ }
+ (void) mutex_lock(&go_mtx);
+ if (time_to_go) {
+ (void) fprintf(stderr, "Service - exiting...\n");
+ error = 0;
+ break;
+ }
+ (void) mutex_unlock(&go_mtx);
+ len = BUFSIZ * 2;
+ bufp = malloc(len);
+ if (pfd[0].revents)
+ len = recv(s, (char *)bufp, len, 0);
+ else {
+ len = recv(srelay, (char *)bufp, len, 0);
+ }
+
+ if (len < 0) {
+ (void) fprintf(stderr,
+ "Service - can't receive - %s\n", strerror(errno));
+ error = 6;
+ break;
+ } else {
+ irequestp = (PKT *) bufp;
+ for (i = 0; i < clients; i++) {
+ if (memcmp(clientp[i].chaddr, irequestp->chaddr,
+ clientp[i].hlen) == 0) {
+ (void) mutex_lock(&clientp[i].mtx);
+ bp = malloc(sizeof (PKT_LIST));
+ bp->pkt = irequestp;
+ bp->len = len;
+ if (verbose == 1)
+ (void) fprintf(stderr,
+ "Service - received packet "
+ "for thread %04d...\n",
+ clientp[i].id);
+ if (clientp[i].pktlistp == NULL) {
+ clientp[i].pktlistp = bp;
+ bp->prev = NULL;
+ } else {
+ for (wbp = clientp[i].pktlistp;
+ wbp->next != NULL;
+ wbp = wbp->next)
+ /* null */;
+ wbp->next = bp;
+ bp->prev = wbp;
+ }
+ bp->next = NULL;
+ (void) cond_signal(&clientp[i].cv);
+ (void) mutex_unlock(&clientp[i].mtx);
+ break;
+ }
+ }
+ if (i >= clients)
+ free(bufp);
+ }
+ }
+ thr_exit(&error);
+ return (NULL); /* NOTREACHED */
+}
+
+/* ARGSUSED */
+static void *
+sig_handle(void *arg)
+{
+ boolean_t leave = B_FALSE;
+ int sig;
+ sigset_t set;
+ char buf[SIG2STR_MAX];
+ int old, new, unstarted;
+ int i;
+ int oldi;
+ uint_t cidlen;
+ char cid[BUFSIZ];
+ int discover, offer, req, decline, ack, nak;
+ int release, inform, unknown;
+ int kicked;
+ ulong_t minavg;
+ time_t minstime;
+
+ (void) sigfillset(&set);
+
+ if (avg == 0) {
+ avgslp.tv_sec = sample_time;
+ avgslp.tv_nsec = 0L;
+ }
+ while (!leave) {
+ discover = offer = req = decline = ack = nak = 0;
+ release = inform = unknown = 0;
+ switch (sig = sigtimedwait(&set, NULL, &avgslp)) {
+ case SIGHUP:
+ case -1:
+ old = time(NULL);
+ new = unstarted = 0;
+ kicked = 0;
+ for (i = 0; i < clients; i++) {
+ /* Start next client at avgslp offset */
+ if (avg && kicked == 0 &&
+ (clientsp[i].flags &
+ (CLIENT_FIRSTTIME | CLIENT_BUSY)) == 0) {
+ (void) mutex_lock(&clientsp[i].mtx);
+ (void) cond_signal(&clientsp[i].acv);
+ (void) mutex_unlock(&clientsp[i].mtx);
+ kicked++;
+ }
+ switch (clientsp[i].state) {
+ case DISCOVER:
+ discover++;
+ break;
+ case OFFER:
+ offer++;
+ break;
+ case REQUEST:
+ req++;
+ break;
+ case DECLINE:
+ decline++;
+ break;
+ case ACK:
+ ack++;
+ break;
+ case NAK:
+ nak++;
+ break;
+ case RELEASE:
+ release++;
+ break;
+ case INFORM:
+ inform++;
+ break;
+ default:
+ unknown++;
+ break;
+ }
+ if (clientsp[i].ltime == NULL ||
+ (clientsp[i].flags & CLIENT_BUSY) == 0)
+ unstarted++;
+ if (clientsp[i].ltime &&
+ clientsp[i].ltime < old) {
+ old = clientsp[i].ltime;
+ oldi = i;
+ }
+ if (clientsp[i].ltime &&
+ clientsp[i].ltime > new) {
+ new = clientsp[i].ltime;
+ }
+ }
+
+ if (time(NULL) < ltime + sample_time)
+ continue;
+ ltime = time(NULL);
+
+ if (start == 0) {
+ /* toss initial sample */
+ ostart = start = time(NULL);
+ otops = tops = 0;
+ minind = 0;
+ } else {
+ minops[minind] = tops - otops;
+ mintim[minind] = ostart;
+ otops = tops;
+ ostart = time(NULL);
+ minind = minind + 1 > nsamples - 1 ? 0 :
+ minind + 1;
+ minstime = 0;
+ minavg = 0;
+ for (i = 0; i < nsamples; i++) {
+ if (mintim[i])
+ minavg += minops[i];
+ if (minstime == 0)
+ minstime = mintim[i];
+ else if (mintim[i] &&
+ mintim[i] < minstime)
+ minstime = mintim[i];
+ }
+
+ cidlen = sizeof (cid);
+ (void) octet_to_hexascii(clientsp[oldi].chaddr,
+ clientsp[oldi].hlen, cid, &cidlen);
+ fprintf(stderr, "%9.9d: Totops %d Curr %d "
+ "Persec %4.2f (%4.2f) Oldest %d (%d) "
+ "Gap %d Free %d\n", time(NULL), tops,
+ ops_outstanding,
+ (double)tops / (double)(time(NULL) - start),
+ (double)minavg / (double)(time(NULL)
+ - minstime),
+ time(NULL) - old, clientsp[oldi].id, cid,
+ new - old, unstarted);
+ fprintf(stderr, "\tdiscov %d off %d req %d "
+ "decl %d ack %d nak %d rel %d inf %d "
+ "free/unknown %d\n", discover, offer, req,
+ decline, ack, nak, release, inform,
+ unknown);
+ }
+ break;
+ case SIGINT:
+ /* FALLTHRU */
+ case SIGTERM:
+ (void) sig2str(sig, buf);
+ (void) fprintf(stderr,
+ "Signal: %s received...Exiting\n", buf);
+ (void) mutex_lock(&go_mtx);
+ time_to_go = B_TRUE;
+ (void) cond_broadcast(&go_cv);
+ (void) mutex_unlock(&go_mtx);
+ for (i = 0; i < clients; i++) {
+ (void) mutex_lock(&clientsp[i].mtx);
+ (void) cond_signal(&clientsp[i].acv);
+ (void) mutex_unlock(&clientsp[i].mtx);
+ }
+ leave = B_TRUE;
+ break;
+ default:
+ (void) sig2str(sig, buf);
+ (void) fprintf(stderr,
+ "Signal: %s received...Ignoring\n", buf);
+ leave = B_FALSE;
+ break;
+ }
+ }
+ thr_exit((void *) NULL);
+ return (NULL); /* NOTREACHED */
+}
+
+int
+main(int argc, char *argv[])
+{
+ boolean_t proto;
+ int i, j, threrror = 0, *threrrorp;
+ int sockoptbuf = 1;
+ register char *endp, *octet;
+ thread_t service_id, sig_id;
+ sigset_t set;
+ uint_t buf;
+ int slen;
+ socklen_t sslen = sizeof (slen);
+ unsigned int ifceno;
+ char cifname[IFNAMSIZ];
+ struct rlimit rl;
+
+ if (randcl)
+ srandom(time(NULL));
+
+ if (dofork) {
+ if (fork() != 0)
+ exit(0);
+ }
+ if (getrlimit(RLIMIT_NOFILE, &rl) < 0) {
+ (void) fprintf(stderr, "Cannot get open file limit: %s\n",
+ strerror(errno));
+ }
+ /* handle cases where limit is infinity */
+ if (rl.rlim_cur == RLIM_INFINITY) {
+ rl.rlim_cur = (rl.rlim_max == RLIM_INFINITY) ?
+ OPEN_MAX : rl.rlim_max;
+ }
+ /* set NOFILE to unlimited */
+ rl.rlim_cur = rl.rlim_max = RLIM_INFINITY;
+ if (setrlimit(RLIMIT_NOFILE, &rl) < 0) {
+ (void) fprintf(stderr, "Cannot set open file limit: %s\n",
+ strerror(errno));
+ }
+ if (argc < 5) {
+ (void) fprintf(stderr, "%s <interface> <ether_addr> <protocol> "
+ "<clients> [time] [desynch] [avg] [relayaddr]\n", argv[0]);
+ return (1);
+ }
+ (void) strcpy(ifname, argv[1]);
+ (void) strcpy(cifname, argv[1]);
+ if ((endp = strchr(ifname, ':')) != NULL) {
+ *endp = '\0';
+ startindex = strtol(endp + 1, 0L, 0L);
+ }
+ if (strcasecmp(argv[3], "dhcp") == 0)
+ proto = B_TRUE;
+ else
+ proto = B_FALSE;
+
+ clients = atoi(argv[4]);
+
+ if (argc >= 6) {
+ release_time = atoi(argv[5]);
+ }
+ if (argc >= 7)
+ desynch = atoi(argv[6]);
+
+ if (argc >= 8) {
+ avg = atof(argv[7]);
+ if (avg > 0.0) {
+ avgslp.tv_sec = avg;
+ avgslp.tv_nsec = (avg -
+ (double)((int)avg)) * 1000000000.0;
+ } else if (avg < 0.0) {
+ avgslp.tv_sec = abs((int)avg);
+ avgslp.tv_nsec = (avg + abs((double)((int)avg))) *
+ 1000000000.0;
+ }
+ }
+ if (argc >= 9)
+ relay.s_addr = inet_addr(argv[8]);
+
+ if (argc >= 10)
+ slen = strtol(argv[0], 0L, 0L);
+ else
+ slen = 1024 * 64;
+
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ perror("Socket");
+ return (1);
+ }
+ (void) setsockopt(s, SOL_SOCKET, SO_SNDBUF, &slen, sslen);
+ (void) setsockopt(s, SOL_SOCKET, SO_RCVBUF, &slen, sslen);
+
+ if (relay.s_addr == INADDR_ANY)
+ if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, (char *)&sockoptbuf,
+ (int)sizeof (sockoptbuf)) < 0) {
+ perror("Setsockopt");
+ return (2);
+ }
+ if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&sockoptbuf, (int)sizeof (sockoptbuf)) < 0) {
+ perror("Setsockopt: REUSEADDR");
+ return (2);
+ }
+ if (relay.s_addr != INADDR_ANY) {
+ relfrom.sin_port = htons(IPPORT_BOOTPS + port_offset);
+ relfrom.sin_family = AF_INET;
+ relfrom.sin_addr.s_addr = INADDR_ANY;
+ relfrom.sin_port = htons(IPPORT_BOOTPS + port_offset);
+
+ (void) strncpy(lifr.lifr_name, cifname,
+ sizeof (lifr.lifr_name));
+ if (lrecv) {
+ if (ioctl(s, SIOCGLIFADDR, (char *)&lifr) < 0) {
+ (void) fprintf(stderr, "Warning: SIOCGLIFADDR: %s",
+ strerror(errno));
+ } else {
+ relfrom.sin_addr.s_addr =
+ ((struct sockaddr_in *)
+ &lifr.lifr_addr)->sin_addr.s_addr;
+ }
+ }
+
+ if ((srelay = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ perror("Socket");
+ return (1);
+ }
+ (void) setsockopt(srelay, SOL_SOCKET, SO_SNDBUF, &slen, sslen);
+ (void) setsockopt(srelay, SOL_SOCKET, SO_RCVBUF, &slen, sslen);
+
+ if (setsockopt(srelay, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&sockoptbuf, (int)sizeof (sockoptbuf)) < 0) {
+ perror("Setsockopt: REUSEADDR");
+ return (2);
+ }
+ if (bind(srelay, (struct sockaddr *)&relfrom,
+ sizeof (relfrom)) < 0) {
+ perror("Bind");
+ return (3);
+ }
+ ifceno = if_nametoindex(cifname);
+ if (bound) {
+ if (setsockopt(s, IPPROTO_IP, IP_BOUND_IF,
+ (char *)&ifceno, (int)sizeof (char *)) < 0) {
+ perror("Setsockopt bind");
+ return (3);
+ }
+ }
+ }
+ from.sin_family = AF_INET;
+ if (relay.s_addr != INADDR_ANY) {
+ from.sin_addr.s_addr =
+ ((struct sockaddr_in *)&lifr.lifr_addr)->sin_addr.s_addr;
+ } else {
+ from.sin_addr.s_addr = INADDR_ANY;
+ }
+ from.sin_port = htons(IPPORT_BOOTPC + port_offset);
+
+ if (bind(s, (struct sockaddr *)&from, sizeof (from)) < 0) {
+ perror("Bind");
+ return (3);
+ }
+ ifceno = if_nametoindex(cifname);
+ if (bound) {
+ if (setsockopt(s, IPPROTO_IP, IP_BOUND_IF,
+ (char *)&ifceno, (int)sizeof (char *)) < 0) {
+ perror("Setsockopt bind");
+ return (3);
+ }
+ }
+ request.op = 1; /* BOOTP request */
+ request.htype = 1; /* Ethernet */
+ request.hlen = 6; /* Ethernet addr len */
+
+ endp = octet = argv[2];
+ for (i = 0; i < (int)request.hlen && octet != NULL; i++) {
+ if ((endp = (char *)strchr(endp, ':')) != NULL)
+ *endp++ = '\0';
+ (void) sscanf(octet, "%x", &buf);
+ request.chaddr[i] = (uchar_t)buf;
+ octet = endp;
+ }
+
+ /* broadcast bit */
+ if (relay.s_addr == INADDR_ANY)
+ request.flags = htons(0x8000);
+
+ /* magic cookie */
+ request.cookie[0] = 99;
+ request.cookie[1] = 130;
+ request.cookie[2] = 83;
+ request.cookie[3] = 99;
+
+ if (proto) {
+ /* Pretend to be a discover packet */
+ request.options[0] = CD_DHCP_TYPE;
+ request.options[1] = 1;
+ request.options[2] = DISCOVER;
+ request.options[3] = 0xff;
+
+ (void) cond_init(&go_cv, USYNC_THREAD, NULL);
+ (void) mutex_init(&go_mtx, USYNC_THREAD, NULL);
+ }
+ if (relay.s_addr != INADDR_ANY)
+ request.giaddr.s_addr = from.sin_addr.s_addr;
+
+ (void) sigfillset(&set);
+
+ (void) sigdelset(&set, SIGABRT); /* allow for user abort */
+
+ (void) thr_sigsetmask(SIG_SETMASK, &set, NULL);
+
+ /*
+ * Create the client threads
+ */
+ clientsp = malloc(sizeof (struct client) * clients);
+ (void) memset(clientsp, 0, sizeof (struct client) * clients);
+ if (clientsp == NULL)
+ return (1);
+
+ for (i = 0; i < clients; i++) {
+ (void) memcpy(clientsp[i].chaddr, request.chaddr, request.hlen);
+ clientsp[i].hlen = request.hlen;
+ if (i > 100)
+ j = 3;
+ else if (i > 50)
+ j = 2;
+ else
+ j = 1;
+
+ if (i) {
+ clientsp[i].chaddr[j] = (unsigned char) i;
+ clientsp[i].chaddr[3] += (unsigned char) j;
+ clientsp[i].chaddr[4] = (unsigned char) (i * j);
+ }
+ if (printid)
+ fprintf(stderr, "ID %x:%x:%x:%x:%x:%x\n",
+ clientsp[i].chaddr[0],
+ clientsp[i].chaddr[1],
+ clientsp[i].chaddr[2],
+ clientsp[i].chaddr[3],
+ clientsp[i].chaddr[4],
+ clientsp[i].chaddr[5]);
+
+ (void) cond_init(&clientsp[i].cv, USYNC_THREAD, 0);
+ (void) mutex_init(&clientsp[i].mtx, USYNC_THREAD, 0);
+ clientsp[i].proto = proto;
+ clientsp[i].flags = CLIENT_FIRSTTIME;
+ if (thr_create(NULL, NULL, client, (void *) &clientsp[i],
+ THR_BOUND | THR_SUSPENDED, &clientsp[i].id) != 0) {
+ (void) fprintf(stderr, "Error starting Client %04d\n",
+ clientsp[i].id);
+ }
+ }
+
+ /*
+ * Create signal handling thread.
+ */
+ if (thr_create(NULL, 0, sig_handle, NULL,
+ THR_BOUND | THR_DAEMON | THR_DETACHED, &sig_id) != 0) {
+ (void) fprintf(stderr, "Error starting signal handler.\n");
+ return (1);
+ } else
+ (void) fprintf(stderr, "Started Signal handler: %04d...\n",
+ sig_id);
+
+ /*
+ * Create/start the service thread.
+ */
+ if (thr_create(NULL, NULL, service, (void *) clientsp, THR_BOUND,
+ &service_id) != 0) {
+ (void) fprintf(stderr, "Error starting Service %d\n",
+ service_id);
+ exit(1);
+ } else
+ (void) fprintf(stderr, "Started Service %04d...\n",
+ service_id);
+
+ /*
+ * Continue the client threads.
+ */
+ for (i = 0; i < clients; i++) {
+ (void) thr_continue(clientsp[i].id);
+ }
+
+ /*
+ * join them
+ */
+ threrrorp = &threrror;
+ for (i = 0; i < clients; i++) {
+ if (thr_join(clientsp[i].id, NULL, (void **) &threrrorp) == 0) {
+ if (threrror != 0) {
+ (void) fprintf(stdout,
+ "Client %04d - exited with %d\n",
+ clientsp[i].id, threrror);
+ }
+ (void) cond_destroy(&clientsp[i].cv);
+ (void) mutex_destroy(&clientsp[i].mtx);
+ }
+ }
+
+ (void) close(s); /* force service out of poll */
+
+ if (thr_join(service_id, NULL, (void **) &threrrorp) == 0) {
+ if (threrror != 0) {
+ (void) fprintf(stdout, "Service - exited with %d\n",
+ threrror);
+ }
+ }
+ (void) free((char *)clientsp);
+ (void) fprintf(stdout, "Exiting...\n");
+
+ return (0);
+}
+
+/*
+ * corrupt: simulate packet corruption for debugging server
+ */
+static void
+corrupt(char *pktp, int size)
+{
+ int c;
+ int i;
+ int p;
+ char *pp;
+ char *pe = pktp + size;
+ int li = rand() % (size - 1) + 1;
+
+ for (pp = pktp; pp < pe; pp += li) {
+ c = ((pe - pp) < li ? pe - pp : li);
+ i = (rand() % c)>>1;
+ while (--i > 0) {
+ p = (rand() % c);
+ pp[p] = (unsigned char)(rand() & 0xFF);
+ }
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/tests/test_dstore.c b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/tests/test_dstore.c
new file mode 100644
index 0000000000..ea68bc135b
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/tests/test_dstore.c
@@ -0,0 +1,940 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2001-2002 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <sys/fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <limits.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <thread.h>
+#include <tnf/probe.h>
+
+#include <netinet/dhcp.h>
+#include <locale.h>
+#include <signal.h>
+#include <tnf/probe.h>
+
+#include <dhcp_svc_confopt.h>
+#include <dhcp_svc_private.h>
+#include <dhcp_impl.h>
+
+#ifdef DEBUG
+#include <mtmalloc.h>
+#endif /* DEBUG */
+
+/*
+ * Global variables.
+ */
+int verbose = 0;
+thread_t *tp;
+cond_t never;
+volatile time_t *timp;
+volatile int tms;
+char *fl;
+mutex_t mtx;
+mutex_t thread_mtx;
+volatile ulong_t ops_outstanding;
+
+static volatile ulong_t tops, otops;
+static volatile ulong_t minops[6];
+static volatile time_t mintim[6];
+static volatile int minind;
+long sample_time = 10L;
+long nsamples = 2;
+
+static volatile time_t start, ostart;
+volatile time_t ustart;
+volatile int time_to_go;
+volatile int spawn_helper;
+char b[1024 * 1024];
+volatile double slp;
+volatile int worktype;
+thread_t sigthread;
+volatile int old, new, unstarted;
+volatile uint_t threads;
+volatile unsigned int douwork = 0;
+volatile int dofsync = 0;
+volatile int domalloc = 0;
+volatile int dofork = 0;
+thread_t opnthread;
+volatile int doopen = 0;
+
+dsvc_datastore_t datastore; /* Datastore for container access */
+
+#define MAXTABLE 1024
+int ntable;
+
+dn_rec_list_t *thread_dncp[MAXTABLE];
+dsvc_handle_t dh[MAXTABLE]; /* data handle */
+struct in_addr net[MAXTABLE];
+uint_t nrecords[MAXTABLE];
+char *network;
+
+typedef struct work {
+ boolean_t isthreaded;
+ int thread;
+ cond_t cv;
+ mutex_t mtx;
+ dn_rec_t *dnp;
+}work_t;
+
+void
+free_work_t(work_t *wptr) {
+ free(wptr->dnp);
+ free(wptr);
+}
+/*
+ * Simulated binary datastore work
+ */
+/* ARGSUSED */
+static void *
+uwork(void *argp)
+{
+ int i;
+ int err;
+ int fd;
+ long block;
+ work_t *wptr = argp;
+ char *ptr;
+ size_t size = ((random() & (domalloc - 1)) + 0x200) &
+ ~(0x200 - 1);
+ int wtype;
+
+ if (domalloc)
+ ptr = malloc(size);
+ else
+ ptr = b;
+
+ if (wptr->isthreaded) {
+ (void) mutex_lock(&wptr->mtx);
+ }
+ i = wptr->thread;
+
+ TNF_PROBE_1(uwork, "work", "uwork%debug 'in function work'",
+ tnf_long, size, size);
+
+ wtype = worktype == 0 ? random() & 0x7 : worktype;
+ block = (random() & (douwork - 1)) + (tms / 0x200) + 1;
+
+ /* prewrite legal records */
+ if (timp[i] == NULL && ustart == 0) {
+ ustart = time(NULL);
+ wtype = 4;
+ block = (tms / 0x200) + 1;
+ size = sizeof (b);
+ ptr = b;
+ }
+ timp[i] = time(NULL);
+ fd = open(fl, O_RDWR);
+ (void) write(fd, (char *)timp, tms);
+ (void) close(fd);
+
+ if (wtype == 4) {
+
+ TNF_PROBE_2(uwork_write, "work",
+ "uwork_write%debug 'in function work'",
+ tnf_long, block, block,
+ tnf_long, size, size);
+
+ fd = open(fl, O_RDWR);
+ (void) lseek(fd, block * 0x200, 0L);
+ err = write(fd, ptr, size);
+ (void) close(fd);
+
+ TNF_PROBE_1(uwork_write_end, "work",
+ "uwork_write_end%debug 'in function work'",
+ tnf_long, err, err);
+
+ } else if (wtype == 3 && dofsync) {
+
+ TNF_PROBE_0(uwork_fsync, "work",
+ "uwork_fsync%debug 'in function work'");
+
+ fd = open(fl, O_RDWR);
+ err = fsync(fd);
+ (void) close(fd);
+
+ TNF_PROBE_1(uwork_fsync_end, "work",
+ "uwork_fsync_end%debug 'in function work'",
+ tnf_long, err, err);
+ } else {
+ TNF_PROBE_2(uwork_read, "work",
+ "uwork_read%debug 'in function work'",
+ tnf_long, block, block,
+ tnf_long, size, size);
+
+ fd = open(fl, O_RDWR);
+ (void) lseek(fd, block * 0x200, 0L);
+ err = read(fd, ptr, size);
+ (void) close(fd);
+
+ TNF_PROBE_1(uwork_read_end, "work",
+ "uwork_read_end%debug 'in function work'",
+ tnf_long, err, err);
+
+ }
+ if (domalloc && ptr != b)
+ free(ptr);
+
+ if (wptr->isthreaded) {
+ (void) mutex_unlock(&wptr->mtx);
+ cond_signal(&wptr->cv);
+ TNF_PROBE_0(work_end, "work", "");
+ thr_exit(NULL);
+ }
+ TNF_PROBE_0(uwork_end, "work", "");
+
+ return ((void *) NULL);
+}
+
+/*
+ * Simulated datastore work
+ */
+static void *
+work(void *argp)
+{
+ int i, j;
+ dn_rec_t *dnp;
+ int err;
+ work_t *wptr = argp;
+ uchar_t cid_len;
+ char *ptr;
+ uint32_t query;
+ dn_rec_t dn, ndn;
+ dn_rec_list_t *dncp = NULL;
+ uint_t crecords, irecords;
+ int wtype;
+ int firsttime = 0;
+ int op;
+ size_t size = ((random() & (domalloc - 1)) + 0x100) &
+ ~(0x1000 - 1);
+ int table;
+
+ if (domalloc)
+ ptr = malloc(size);
+ else
+ ptr = b;
+
+ irecords = (random() & 0xff) + 1;
+ if (irecords == 12) {
+ irecords = (uint_t)-1;
+ }
+ if (wptr->isthreaded) {
+ (void) mutex_lock(&wptr->mtx);
+ }
+ i = wptr->thread;
+ dnp = wptr->dnp;
+
+ table = i % ntable;
+ dn = *dnp;
+
+ cid_len = 7;
+
+ if (worktype == 0) {
+ wtype = random() & 0x7;
+ if (wtype == 4)
+ wtype--;
+ } else
+ wtype = worktype;
+
+ /* preload a legal record */
+ if (timp[i] == NULL) {
+ wtype = 3;
+ firsttime = 1;
+ irecords = threads * 2;
+ (void) mutex_lock(&thread_mtx);
+ if ((dncp = thread_dncp[table]) != NULL) {
+ thread_dncp[table] = dncp->dnl_next;
+ *dnp = *(dncp->dnl_rec);
+ dncp->dnl_next = NULL;
+ wtype = -1;
+ (void) mutex_unlock(&thread_mtx);
+ }
+ }
+ TNF_PROBE_2(work, "work", "work%debug 'in function work'",
+ tnf_ulong, worktype, wtype,
+ tnf_ulong, irecords, irecords);
+
+ timp[i] = time(NULL);
+ crecords = 0;
+ DSVC_QINIT(query);
+ switch (wtype) {
+ case -1:
+ break;
+ case 1:
+ switch (random() & 0x7) {
+ case 1:
+ for (j = 0; j < cid_len; j++)
+ dn.dn_cid[j] = random() & 0xff;
+ break;
+ case 2:
+ for (j = 0; j < cid_len; j++)
+ dn.dn_cid[j] = '\0';
+ dn.dn_cid_len = 1;
+ break;
+ }
+ DSVC_QEQ(query, DN_QCID);
+
+ /* LINTED */
+ TNF_PROBE_2(work_cid, "work work_cid",
+ "work_cid%debug 'in function work'",
+ tnf_ulong, cid, *(ulong_t *)&dn.dn_cid,
+ tnf_ulong, cid_len, dn.dn_cid_len);
+
+ err = lookup_dd(dh[table], B_TRUE, query, -1,
+ (const void *)&dn, (void **)&dncp, &crecords);
+
+ TNF_PROBE_2(work_cid_end, "work work_cid",
+ "work_cid_end%debug 'in function work'",
+ tnf_ulong, err, err,
+ tnf_ulong, crecords, crecords);
+
+ if (crecords > 0 && dncp)
+ *dnp = *(dncp->dnl_rec);
+ break;
+
+ case 2:
+ switch (random() & 0x7) {
+ case 1:
+ dn.dn_cip.s_addr = random();
+ break;
+ case 2:
+ dn.dn_cip.s_addr = net[table].s_addr |
+ (random() & (nrecords[table] - 1));
+ break;
+ }
+
+ DSVC_QEQ(query, DN_QCIP);
+
+ TNF_PROBE_1(work_cip, "work work_cip",
+ "work_cip%debug 'in function work'",
+ tnf_ulong, cip, dn.dn_cip.s_addr);
+
+ err = lookup_dd(dh[table], B_TRUE, query, -1,
+ (const void *)&dn, (void **)&dncp, &crecords);
+
+ TNF_PROBE_2(work_cip_end, "work work_cip",
+ "work_cip_end%debug 'in function work'",
+ tnf_ulong, err, err,
+ tnf_ulong, crecords, crecords);
+
+ if (crecords > 0 && dncp)
+ *dnp = *(dncp->dnl_rec);
+ break;
+ case 3:
+ op = random() & 0x7;
+ if (firsttime)
+ op = 2;
+
+ switch (op) {
+ case 1:
+ DSVC_QNEQ(query, DN_QLEASE);
+ dn.dn_lease = 0;
+ break;
+ case 2:
+ DSVC_QEQ(query, DN_QCID);
+ for (j = 0; j < cid_len; j++)
+ dn.dn_cid[j] = '\0';
+ dn.dn_cid_len = 1;
+ break;
+ }
+
+ TNF_PROBE_2(work_read, "work work_read",
+ "work_read%debug 'in function work'",
+ tnf_ulong, query, query,
+ tnf_ulong, cid_len, dn.dn_cid_len);
+
+ err = lookup_dd(dh[table], B_TRUE, query, irecords,
+ (const void *)&dn, (void **)&dncp, &crecords);
+
+ TNF_PROBE_2(work_read_end, "work work_read",
+ "work_read_end%debug 'in function work'",
+ tnf_ulong, err, err,
+ tnf_ulong, crecords, crecords);
+
+ if (crecords > 0 && dncp) {
+ *dnp = *(dncp->dnl_rec);
+ if (firsttime) {
+ thread_dncp[table] = dncp->dnl_next;
+ dncp->dnl_next = NULL;
+ mutex_unlock(&thread_mtx);
+ }
+ }
+ break;
+ case 4:
+ op = dnp->dn_lease & 0x3;
+ switch (op) {
+ case 0:
+ /* write record w/ cid */
+ ndn = *dnp;
+ ndn.dn_lease = (htonl(time(NULL)) & ~0x3) + 1;
+ ndn.dn_cid_len = 14;
+ for (j = 0; j < ndn.dn_cid_len; j++)
+ ndn.dn_cid[j] = random() & 0xff;
+
+ /* LINTED */
+ TNF_PROBE_2(work1_modify, "work work1_modify",
+ "work1_modify%debug 'in function work'",
+ tnf_ulong, cid, *(ulong_t *)&ndn.dn_cid,
+ tnf_ulong, cid_len, ndn.dn_cid_len);
+
+ err = modify_dd_entry(dh[table], dnp, &ndn);
+ if (err != DSVC_SUCCESS && verbose) {
+ fprintf(stderr, "work: %d %d error %d\n",
+ wtype, op, err);
+ }
+
+ TNF_PROBE_1(work1_modify_end, "work work1_modify_end",
+ "work1_modify_end%debug 'in function work'",
+ tnf_ulong, err, err);
+ *dnp = ndn;
+ break;
+ case 1:
+ /* re-read record w/ cid */
+ DSVC_QEQ(query, DN_QCID);
+ TNF_PROBE_2(work_read1, "work work_read1",
+ "work_read1%debug 'in function work'",
+ tnf_ulong, query, query,
+ tnf_ulong, cid_len, dn.dn_cid_len);
+
+ err = lookup_dd(dh[table], B_TRUE, query, - 1,
+ (const void *)dnp, (void **)&dncp,
+ &crecords);
+ TNF_PROBE_2(work_read1_end, "work work_read1",
+ "work_read1_end%debug 'in function work'",
+ tnf_ulong, err, err,
+ tnf_ulong, crecords, crecords);
+
+ if ((err != DSVC_SUCCESS || crecords < 1) && verbose) {
+ fprintf(stderr, "work: %d %d error %d %d\n",
+ wtype, op, err, crecords);
+ }
+ dnp->dn_lease++;
+ break;
+ case 2:
+ /* write free record */
+ dnp->dn_lease--;
+ ndn = *dnp;
+ DSVC_QEQ(query, DN_QCID);
+ for (j = 0; j < cid_len; j++)
+ ndn.dn_cid[j] = '\0';
+ ndn.dn_cid_len = 1;
+ ndn.dn_lease = 0;
+
+ TNF_PROBE_2(work_modify2, "work work_modify2",
+ "work_modify2%debug 'in function work'",
+ tnf_ulong, cid, *(ulong_t *)&ndn.dn_cid,
+ tnf_ulong, cid_len, ndn.dn_cid_len);
+
+ err = modify_dd_entry(dh[table], dnp, &ndn);
+
+ TNF_PROBE_1(work_modify2_end, "work work_modify2_end",
+ "work_modify2_end%debug 'in function work'",
+ tnf_ulong, err, err);
+
+ if (err != DSVC_SUCCESS && verbose) {
+ fprintf(stderr, "work: %d %d error %d\n",
+ wtype, op, err);
+ }
+ *dnp = ndn;
+ break;
+ }
+ break;
+
+
+ default:
+ ndn = *dnp;
+ ndn.dn_cid_len = cid_len;
+ switch (random() & 0x1) {
+ case 0:
+ for (j = 0; j < cid_len; j++)
+ ndn.dn_cid[j] = random() & 0xff;
+ break;
+ case 1:
+ for (j = 0; j < cid_len; j++)
+ ndn.dn_cid[j] = '\0';
+ ndn.dn_cid_len = 1;
+ break;
+ }
+ ndn.dn_lease = htonl(time(NULL));
+
+ /* LINTED */
+ TNF_PROBE_2(work_modify, "work work_modify",
+ "work_modify%debug 'in function work'",
+ tnf_ulong, cid, *(ulong_t *)&ndn.dn_cid,
+ tnf_ulong, cid_len, ndn.dn_cid_len);
+
+ err = modify_dd_entry(dh[table], dnp, &ndn);
+ if (err != DSVC_SUCCESS && err != DSVC_COLLISION) {
+ if (verbose)
+ fprintf(stderr, "modify: error %d\n", err);
+ }
+
+ TNF_PROBE_1(work_modify_end, "work work_modify_end",
+ "work_modify_end%debug 'in function work'",
+ tnf_ulong, err, err);
+
+ *dnp = ndn;
+ break;
+ }
+
+ if (domalloc)
+ free(ptr);
+
+ if (wptr->isthreaded) {
+ (void) mutex_unlock(&wptr->mtx);
+ cond_signal(&wptr->cv);
+ TNF_PROBE_2(work_end, "work", "work_end%debug 'in function "
+ "work'", tnf_ulong, err, err,
+ tnf_ulong, crecords, crecords);
+ thr_exit(NULL);
+ }
+ if (dncp)
+ free_dd_list(dh[table], dncp);
+
+ TNF_PROBE_2(work_end, "work", "work_end%debug 'in function work'",
+ tnf_ulong, err, err,
+ tnf_ulong, crecords, crecords);
+
+ return ((void *) NULL);
+}
+
+/*
+ * Worker thread.
+ */
+static void *
+dowork(void *argp)
+{
+ int i = (int)argp;
+ timestruc_t to;
+ work_t *wptr;
+ dn_rec_t dn;
+
+ (void) memset((char *)&dn, '\0', sizeof (dn));
+ (void) mutex_lock(&mtx);
+ for (; time_to_go == 0; ) {
+ TNF_PROBE_1(dowork, "dowork",
+ "dowork%debug 'in function dowork'",
+ tnf_long, thread_number, i);
+
+ to.tv_sec = time(NULL) + random() & 0x3;
+ to.tv_nsec = 0;
+
+ if (slp > 0.0) {
+ to.tv_sec = time(NULL) + slp;
+ to.tv_nsec = (slp - (double)((int)slp)) * 1000000000.0;
+ } else if (slp < 0.0) {
+ to.tv_sec = time(NULL) + abs((int)slp);
+ to.tv_nsec = (slp + abs((double)((int)slp))) *
+ 1000000000.0;
+ }
+ /* give up processor */
+ if (slp != 0.0) {
+ (void) mutex_unlock(&mtx);
+ (void) cond_timedwait(&never, &mtx, &to);
+ }
+ ops_outstanding++;
+ (void) mutex_unlock(&mtx);
+
+ if (spawn_helper) {
+ wptr = (work_t *)malloc(sizeof (work_t));
+ wptr->thread = i * 2;
+ wptr->isthreaded = B_TRUE;
+ (void) cond_init(&wptr->cv, USYNC_THREAD, NULL);
+ (void) mutex_init(&wptr->mtx, USYNC_THREAD, NULL);
+ (void) mutex_lock(&wptr->mtx);
+
+ /* fire up helper thread */
+ if (thr_create(NULL, 0, douwork ? uwork : work,
+ (void *)wptr, 0, &tp[i * 2]) != 0)
+ fprintf(stderr, "can't spawn lthread %d\n", i);
+
+ /* wait for completion */
+ (void) cond_wait(&wptr->cv, &wptr->mtx);
+ (void) mutex_unlock(&wptr->mtx);
+ (void) thr_join(tp[i * 2], NULL, NULL);
+ free_work_t(wptr);
+ } else {
+ wptr = (work_t *)malloc(sizeof (work_t));
+ wptr->isthreaded = B_FALSE;
+ wptr->thread = i;
+ wptr->dnp = &dn;
+ if (douwork) {
+ (void) uwork((void *)wptr);
+ } else {
+ (void) work((void *)wptr);
+ }
+ free_work_t(wptr);
+ }
+ (void) mutex_lock(&mtx);
+ tops++;
+ ops_outstanding--;
+ TNF_PROBE_0(dowork_end, "dowork", "");
+ }
+ (void) mutex_unlock(&mtx);
+ thr_exit(NULL);
+
+ return ((void *) NULL);
+}
+
+/*
+ * Signal handler routine. All signals handled by calling thread.
+ */
+/* ARGSUSED */
+static void *
+sig_handle(void *arg)
+{
+ int i;
+ int sig;
+ sigset_t set;
+ timespec_t ts;
+ siginfo_t si;
+ int go;
+ int oldi;
+ ulong_t minavg;
+ time_t minstime;
+
+ (void) sigfillset(&set); /* catch all signals */
+
+ ts.tv_sec = sample_time;
+ ts.tv_nsec = 0L;
+
+ for (;;) {
+ (void) mutex_lock(&mtx);
+ go = time_to_go;
+ (void) mutex_unlock(&mtx);
+ if (go)
+ break;
+
+ switch (sig = sigtimedwait(&set, &si, &ts)) {
+ case -1:
+ case SIGHUP:
+ old = time(NULL);
+ oldi = new = unstarted = 0;
+ for (i = 0; i < threads; i++) {
+ if (timp[i] == NULL)
+ unstarted++;
+ if (timp[i] && timp[i] < old) {
+ old = timp[i];
+ oldi = i;
+ }
+ if (timp[i] && timp[i] > new)
+ new = timp[i];
+ }
+
+ if (start == 0) {
+ /* toss initial sample */
+ ostart = start = time(NULL);
+ (void) mutex_lock(&mtx);
+ otops = tops = 0;
+ (void) mutex_unlock(&mtx);
+ minind = 0;
+ } else {
+ minops[minind] = tops - otops;
+ mintim[minind] = ostart;
+ otops = tops;
+ ostart = time(NULL);
+ minind = minind + 1 > nsamples - 1 ? 0 :
+ minind + 1;
+ minstime = 0;
+ minavg = 0;
+ for (i = 0; i < nsamples; i++) {
+ if (mintim[i])
+ minavg += minops[i];
+ if (minstime == 0)
+ minstime = mintim[i];
+ else if (mintim[i] &&
+ mintim[i] < minstime)
+ minstime = mintim[i];
+ }
+
+ fprintf(stderr, "%9.9d: Totops %d Curr %d "\
+ "Persec %4.2f (%4.2f) Oldest %d (%d) "\
+ "Gap %d Unstarted %d\n",
+ time(NULL),
+ tops,
+ ops_outstanding,
+ (double)tops / (double)(time(NULL)
+ - start),
+ (double)minavg / (double)(time(NULL)
+ - minstime),
+ time(NULL) - old,
+ oldi,
+ new - old,
+ unstarted);
+ }
+ break;
+ default:
+ (void) mutex_lock(&mtx);
+ time_to_go++;
+ (void) mutex_unlock(&mtx);
+ break;
+ }
+ }
+ thr_exit(NULL);
+ return ((void *) sig); /* NOTREACHED */
+}
+
+int fd[0x10000];
+/*
+ * open handler routine.
+ */
+/* ARGSUSED */
+static void *
+open_handle(void *arg)
+{
+ int i;
+
+ for (;;) {
+ for (i = 0; i < doopen; i++)
+ fd[i] = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
+ for (i = 0; i < doopen; i++)
+ if (fd[i] >= 0)
+ (void) close(fd[i]);
+ }
+ return ((void *) NULL); /* NOTREACHED */
+}
+/*
+ * test_dstore network[,network] worktype[,worktype] <thr_create flags>
+ * <spawn_helper> <nlwp> <nthread> <file> <sleeptype>
+ *
+ * network - list of network containers, comma-separated
+ * worktypes:
+ * 0 - random
+ * 1 - cid reads
+ * 2 - cip reads
+ * 3 - whole db reads
+ * 4 - write read write (simulate simple test)
+ * 5 - modify writes
+ * sleeptypes:
+ * N == * condwait N sec.nsec period
+ * -N == condwait a random 1-N sec.nsec period
+ */
+main(int c, char **v)
+{
+ int i;
+ timespec_t to;
+ uint_t flags;
+ int err;
+ sigset_t set;
+ dhcp_confopt_t *dsp = NULL;
+ uint32_t query;
+ dn_rec_t dn;
+ dn_rec_list_t *dncp = NULL;
+ struct rlimit rl;
+ char *np;
+
+#ifdef DEBUG
+ mallocctl(MTDEBUGPATTERN, 1);
+ mallocctl(MTINITBUFFER, 1);
+#endif /* DEBUG */
+
+ srandom(time(NULL));
+
+ if (dofork)
+ if (fork() != 0)
+ exit(0);
+
+ if ((err = getrlimit(RLIMIT_NOFILE, &rl)) < 0) {
+ (void) fprintf(stderr, "Cannot get open file limit: %s\n",
+ strerror(errno));
+ }
+ /* handle cases where limit is infinity */
+ if (rl.rlim_cur == RLIM_INFINITY) {
+ rl.rlim_cur = (rl.rlim_max == RLIM_INFINITY) ?
+ OPEN_MAX : rl.rlim_max;
+ }
+ /* set NOFILE to unlimited */
+ rl.rlim_cur = rl.rlim_max = RLIM_INFINITY;
+ if ((err = setrlimit(RLIMIT_NOFILE, &rl)) < 0) {
+ (void) fprintf(stderr, "Cannot set open file limit: %s\n",
+ strerror(errno));
+ }
+ if (c == 1) {
+ (void) fprintf(stderr,
+ "/*\n"\
+ " * test_dstore network[,network] worktype[,"\
+ "worktype] <thr_create flags>\n"\
+ " * <spawn_helper> <nlwp> <nthread> "\
+ "<file> <sleeptype>\n"\
+ " *\n"\
+ " * network - list of network containers, "\
+ "comma-separated\n"\
+ " * worktypes:\n"\
+ " * 0 - random\n"\
+ " * 1 - cid reads\n"\
+ " * 2 - cip reads\n"\
+ " * 3 - whole db reads\n"\
+ " * 4 - write read write (simulate simple"\
+ " test)\n"\
+ " * 5 - modify writes\n"\
+ " * sleeptypes:\n"\
+ " * N == * condwait N sec.nsec period\n"\
+ " * -N == condwait a random 1-N sec.nsec "\
+ "period\n"\
+ " */\n");
+ return (0);
+ }
+ network = v[1];
+
+ worktype = strtoul(v[2], 0L, 0L);
+ flags = strtoul(v[3], 0L, 0L);
+ spawn_helper = strtoul(v[4], 0L, 0L);
+ if (strtoul(v[5], 0L, 0L) > 0)
+ (void) thr_setconcurrency(strtoul(v[5], 0L, 0L));
+ threads = strtoul(v[6], 0L, 0L);
+ fl = v[7];
+ if (c > 8)
+ slp = atof(v[8]);
+
+ if (douwork == 0) {
+ /* Load current datastore. */
+ (void) read_dsvc_conf(&dsp);
+ if ((i = confopt_to_datastore(dsp, &datastore))
+ != DSVC_SUCCESS) {
+ (void) fprintf(stderr, "Invalid datastore: %s\n",
+ dhcpsvc_errmsg(i));
+ return (EINVAL);
+ }
+ for (i = 0, np = strtok(network, ","); np; i++,
+ np = strtok(NULL, ",")) {
+ net[i].s_addr = inet_addr(np);
+
+ err = open_dd(&dh[i], &datastore, DSVC_DHCPNETWORK, np,
+ DSVC_READ | DSVC_WRITE);
+
+ if (err != DSVC_SUCCESS) {
+ (void) fprintf(stderr, "Invalid network: "\
+ "%s %s\n", np,
+ dhcpsvc_errmsg(err));
+ return (err);
+ }
+ /*
+ * XXXX: bug: currently can't get the count as
+ * advertised
+ */
+ (void) memset(&dn, '\0', sizeof (dn));
+ DSVC_QINIT(query);
+ err = lookup_dd(dh[i], B_FALSE, query, -1,
+ (const void *) &dn, (void **) &dncp,
+ &nrecords[i]);
+ if (dncp)
+ free_dd_list(dh[i], dncp);
+
+ if (err != DSVC_SUCCESS) {
+ (void) fprintf(stderr, "Bad nrecords: %s "
+ "[%d]\n", dhcpsvc_errmsg(err),
+ nrecords[i]);
+ return (err);
+ }
+ }
+ ntable = i;
+ }
+ TNF_PROBE_2(main, "main",
+ "main%debug 'in function main'",
+ tnf_ulong, threads, threads,
+ tnf_ulong, nrecords, nrecords[i]);
+
+ (void) sigfillset(&set);
+
+ (void) sigdelset(&set, SIGABRT); /* allow for user abort */
+
+ (void) thr_sigsetmask(SIG_SETMASK, &set, NULL);
+
+ tms = threads * sizeof (thread_t);
+ if (spawn_helper)
+ tms *= 2;
+ tp = malloc(tms);
+ tms = (threads * sizeof (time_t) + 0x200) & ~(0x200 - 1);
+ if (spawn_helper)
+ tms *= 2;
+ timp = malloc(tms);
+ (void) memset((char *)timp, NULL, tms);
+
+ (void) mutex_init(&mtx, USYNC_THREAD, 0);
+
+ /*
+ * Create signal handling thread. XXXX: due to threads library
+ * limitations, this must currently be directly called in the main
+ * program thread.
+ */
+ if ((err = thr_create(NULL, 0, sig_handle, NULL,
+ THR_NEW_LWP | THR_DAEMON | THR_BOUND |
+ THR_DETACHED, &sigthread)) != 0) {
+ (void) fprintf(stderr,
+ gettext("Cannot start signal handling thread, error: %d\n"),
+ err);
+ return (err);
+ }
+ for (i = 0; i < threads; i++)
+ /* fire up monitor thread */
+ if (thr_create(NULL, 0, dowork, (void *) i,
+ flags, &tp[i]) != 0)
+ fprintf(stderr, "can't spawn thread %d\n", i);
+
+ /*
+ * Create open handling thread.
+ */
+ if (doopen && (err = thr_create(NULL, 0, open_handle, NULL,
+ THR_NEW_LWP | THR_DAEMON | THR_BOUND | THR_DETACHED,
+ &opnthread)) != 0) {
+ (void) fprintf(stderr,
+ gettext("Cannot start open handling thread, error: %d\n"),
+ err);
+ return (err);
+ }
+
+ (void) mutex_lock(&mtx);
+ for (; time_to_go == 0; ) {
+ to.tv_sec = time(NULL) + 10;
+ to.tv_nsec = 0L;
+ (void) cond_timedwait(&never, &mtx, &to);
+ (void) mutex_unlock(&mtx);
+ }
+
+ /*
+ * Attempt to join threads.
+ */
+ for (i = 0; i < threads; i++)
+ (void) thr_join(tp[i], NULL, NULL);
+
+ (void) sleep(5);
+
+ TNF_PROBE_0(main_end, "main", "");
+
+ return (0);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/tests/test_per_net.c b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/tests/test_per_net.c
new file mode 100644
index 0000000000..4ec09eccb4
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/tests/test_per_net.c
@@ -0,0 +1,236 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Copyright (c) 1996-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <netinet/in.h>
+#include <netinet/dhcp.h>
+#include <dhcp_gen.h>
+#include <dhcpd.h>
+#include <per_network.h>
+#include <dhcp_msgs.h>
+
+int debug = 1;
+int verbose = 1;
+
+/*
+ * smalloc() -- safe malloc()
+ *
+ * Always returns a valid pointer (if it returns at all). The allocated
+ * memory is initialized to all zeros. If malloc() returns an error, a
+ * message is printed using the syslog() function and the program aborts
+ * with a status of 1.
+ */
+char *
+smalloc(uint_t nbytes)
+{
+ char *retvalue;
+
+ if ((retvalue = (char *)malloc(nbytes)) == (char *)NULL) {
+ dhcp_error(LOG_ERR, DHCP_MSGS(DCMSG_NO_MEMORY));
+ (void) exit(1);
+ }
+ memset(retvalue, 0, nbytes);
+ return (retvalue);
+}
+
+int
+main(void)
+{
+ struct in_addr five_net = {109, 108, 5, 0};
+ struct in_addr five_mask = {109, 108, 5, 255};
+ struct in_addr serverid = {109, 108, 5, 138};
+ struct in_addr scratch;
+ PER_NET_DB pndb;
+ PN_REC pn;
+ uchar_t buf[MAX_CID_LEN] = {0x1, 0x0, 0x0, 0xc0, 0xee, 0xe, 0x4c };
+ char tbuf[MAX_CID_LEN];
+ int recs;
+ unsigned int len;
+ register int i, err = 0;
+
+
+ /*
+ * Test 0. Open the per network database, and locate a *single*
+ * record by cid.
+ */
+ printf("Test 0: START ******************************************\n");
+ memset(&pndb, 0, sizeof (pndb));
+
+ if (open_per_net(&pndb, &five_net, &five_mask) != 0) {
+ printf("didn't work.\n");
+ return (1);
+ }
+
+ /*
+ * Should only be one.
+ */
+ memset(&pn, 0, sizeof (pn));
+ recs = lookup_per_net(&pndb, PN_CID, (void *)buf, 7, &serverid, &pn);
+ if (recs < 0)
+ printf("lookup didn't work.\n");
+ else {
+ if (recs > 0) {
+ len = MAX_CID_LEN;
+ octet_to_hexascii(buf, 7, tbuf, &len);
+ printf("Client id: %s\n", tbuf);
+ printf("flags: 0x%x\n", pn.flags);
+ printf("IP address is: %s\n", inet_ntoa(pn.clientip));
+ printf("server IP address is: %s\n",
+ inet_ntoa(pn.serverip));
+
+ len = MAX_CID_LEN;
+ octet_to_hexascii(&pn.lease, 4, tbuf, &len);
+ printf("lease is %s, 0x%x\n", tbuf, pn.lease);
+ printf("macro is %s\n", pn.macro);
+ printf("Number of records: %d\n", recs);
+ }
+ }
+ close_per_net(&pndb);
+
+ printf("Test 0: END ******************************************\n");
+ /* END TEST 0 ********************************************* */
+
+ /*
+ * Test 1. Open the per net database, locate all records with
+ * cid of 0.
+ */
+ printf("Test 1: START ******************************************\n");
+ if (open_per_net(&pndb, &five_net, &five_mask) != 0) {
+ printf("didn't work.\n");
+ return (1);
+ } else {
+ printf("name: %s\n", pndb.name);
+ }
+
+ memset(buf, 0, MAX_CID_LEN);
+ recs = lookup_per_net(&pndb, PN_CID, (void *)buf, 1, &serverid, &pn);
+ if (recs < 0)
+ printf("lookup didn't work.\n");
+ else {
+ printf("datatype: %d\n", pndb.datatype);
+ printf("row: %d\n", pndb.row);
+ if (recs > 0) {
+ len = MAX_CID_LEN;
+ octet_to_hexascii(buf, 7, tbuf, &len);
+ printf("Client id: %s\n", tbuf);
+ printf("flags: 0x%x\n", pn.flags);
+ printf("IP address is: %s\n", inet_ntoa(pn.clientip));
+ printf("server IP address is: %s\n",
+ inet_ntoa(pn.serverip));
+
+ len = MAX_CID_LEN;
+ octet_to_hexascii(&pn.lease, 4, tbuf, &len);
+ printf("lease is %s, 0x%x\n", tbuf, pn.lease);
+ printf("macro is %s\n", pn.macro);
+ printf("Number of records: %d\n", recs);
+ for (i = 0; i < recs; i++) {
+ if (get_per_net(&pndb, PN_CID, &pn) != 0) {
+ printf("didn't work 2: \n");
+ break;
+ }
+ len = MAX_CID_LEN;
+ octet_to_hexascii(buf, 7, tbuf, &len);
+ printf("Client id: %s\n", tbuf);
+ printf("flags: 0x%x\n", pn.flags);
+ printf("IP address is: %s\n",
+ inet_ntoa(pn.clientip));
+ printf("server IP address is: %s\n",
+ inet_ntoa(pn.serverip));
+
+ len = MAX_CID_LEN;
+ octet_to_hexascii(&pn.lease, 4, tbuf, &len);
+ printf("lease is %s, 0x%x\n", tbuf, pn.lease);
+ printf("macro is %s\n", pn.macro);
+ }
+ }
+ }
+
+ close_per_net(&pndb);
+ printf("Test 1: END ******************************************\n");
+ printf("Test 2: START ******************************************\n");
+ /*
+ * Locate client ip 109.108.5.221.
+ */
+ scratch.s_addr = 0x6d6c05dd;
+ if (open_per_net(&pndb, &five_net, &five_mask) != 0) {
+ printf("didn't work.\n");
+ return (1);
+ } else {
+ printf("name: %s\n", pndb.name);
+ }
+
+ recs = lookup_per_net(&pndb, PN_CLIENT_IP, (void *)&scratch, 4,
+ &serverid, &pn);
+ if (recs < 0)
+ printf("lookup didn't work.\n");
+ else {
+ printf("datatype: %d\n", pndb.datatype);
+ printf("row: %d\n", pndb.row);
+ if (recs > 0) {
+ len = MAX_CID_LEN;
+ octet_to_hexascii(buf, 7, tbuf, &len);
+ printf("Client id: %s\n", tbuf);
+ printf("flags: 0x%x\n", pn.flags);
+ printf("IP address is: %s\n", inet_ntoa(pn.clientip));
+ printf("server IP address is: %s\n",
+ inet_ntoa(pn.serverip));
+
+ len = MAX_CID_LEN;
+ octet_to_hexascii(&pn.lease, 4, tbuf, &len);
+ printf("lease is %s, 0x%x\n", tbuf, pn.lease);
+ printf("macro is %s\n", pn.macro);
+ printf("Number of records: %d\n", recs);
+ }
+ }
+
+ printf("Test 2: END ******************************************\n");
+ printf("Test 3: START ******************************************\n");
+ if (recs > 0) {
+ /*
+ * Using the record from test 2, change the cid, flags, and
+ * lease, then write the record.
+ */
+ pn.cid_len = 7;
+ for (i = 0; (uchar_t)i < pn.cid_len; i++)
+ pn.cid[i] = i;
+ pn.flags |= F_AUTOMATIC;
+ pn.lease = htonl(time(NULL));
+
+ if ((err = put_per_net(&pndb, &pn, PN_CLIENT_IP)) != 0) {
+ printf("didn't work. error: %d\n", err);
+ } else
+ printf("it worked.\n");
+ }
+ close_per_net(&pndb);
+ printf("Test 3: END ******************************************\n");
+
+ return (0);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/tests/test_tnf.nawk b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/tests/test_tnf.nawk
new file mode 100644
index 0000000000..eb5090593f
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/tests/test_tnf.nawk
@@ -0,0 +1,100 @@
+{
+ #
+ # Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ # Use is subject to license terms.
+ #
+ # CDDL HEADER START
+ #
+ # The contents of this file are subject to the terms of the
+ # Common Development and Distribution License, Version 1.0 only
+ # (the "License"). You may not use this file except in compliance
+ # with the License.
+ #
+ # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ # or http://www.opensolaris.org/os/licensing.
+ # See the License for the specific language governing permissions
+ # and limitations under the License.
+ #
+ # When distributing Covered Code, include this CDDL HEADER in each
+ # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ # If applicable, add the following below this CDDL HEADER, with the
+ # fields enclosed by brackets "[]" replaced with your own identifying
+ # information: Portions Copyright [yyyy] [name of copyright owner]
+ #
+ # CDDL HEADER END
+ #
+ #ident "%Z%%M% %I% %E% SMI"
+
+ if (first_time == 0 && ($1 == "probe" || match($1, "---")))
+ next;
+ else
+ first_time = 1;
+
+ time = $1;
+ thread = $5;
+ type = $7;
+ val = pval = "";
+ for (i = 8; i <= NF; i++) {
+ if (pval == "cip:")
+ val = val " " sprintf("%x", $i);
+ else
+ val = val " " $i;
+ pval=$i
+ }
+
+ if (match(type, "_end")) {
+ type = substr(type, 1, match(type, "_end") - 1);
+
+ if (int(start[thread "" type]) == 0) {
+ printf("Warning: missing match line %d: %s\n", NR, $0);
+ next;
+ }
+
+ total[type]++;
+ alltotal++;
+
+ elapsed = time - start[thread "" type];
+ vchar = "";
+ if (longest[type] < elapsed) {
+ longest[type] = elapsed;
+ vchar = "*";
+ }
+ if (verbose) {
+ printf("\t\top: %s thread: %d elapsed %f%s%s\n",
+ type, thread, elapsed, val, vchar);
+ }
+ average[type] = (average[type] + elapsed)/total[type];
+ averagedepth[type] = (averagedepth[type] + depth[type])/total[type];
+
+ allaverage= (allaverage + alldepth)/alltotal;
+
+ depth[type]--;
+ alldepth--;
+ start[thread "" type] = 0;
+
+ } else {
+ if (match(type, "_start")) {
+ type = substr(type, 1, match(type, "_start") - 1);
+ }
+ start[thread "" type] = time;
+ depth[type]++;
+ if (maxdepth[type] < depth[type])
+ maxdepth[type] = depth[type];
+
+ alldepth++;
+ if (allmaxdepth < alldepth)
+ allmaxdepth = alldepth;
+ }
+}
+
+END {
+ printf("\n");
+ for (types in total) {
+ printf("op: %d %s: avg: %8.8f worst: %8.8f\n",
+ total[types], types, average[types], longest[types]);
+ printf(" avg concurrency: %8.8f greatest concurrency %8.8f\n\n",
+ averagedepth[types], maxdepth[types]);
+ }
+ printf("Totals: avg concurrency: %8.8f greatest concurrency %8.8f\n",
+ allaverage, allmaxdepth);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.discardd/Makefile b/usr/src/cmd/cmd-inet/usr.lib/in.discardd/Makefile
new file mode 100644
index 0000000000..da827e09ab
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.discardd/Makefile
@@ -0,0 +1,34 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# cmd/cmd-inet/usr.lib/in.discardd/%M%
+#
+
+PROG = in.discardd
+MANIFEST= discard.xml
+
+include ../Makefile.inetsvc
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.discardd/discard.xml b/usr/src/cmd/cmd-inet/usr.lib/in.discardd/discard.xml
new file mode 100644
index 0000000000..f661641b2b
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.discardd/discard.xml
@@ -0,0 +1,124 @@
+<?xml version='1.0'?>
+<!DOCTYPE service_bundle SYSTEM '/usr/share/lib/xml/dtd/service_bundle.dtd.1'>
+
+<!--
+ Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ ident "%Z%%M% %I% %E% SMI"
+
+ NOTE: This service manifest is not editable; its contents will
+ be overwritten by package or patch operations, including
+ operating system upgrade. Make customizations in a different
+ file.
+
+ Service manifests for discard
+-->
+
+<service_bundle type='manifest' name='SUNWcnsr:discard'>
+
+<service name='network/discard' type='service' version='1'>
+
+ <restarter>
+ <service_fmri value='svc:/network/inetd:default' />
+ </restarter>
+
+ <property_group name='inetd' type='framework'>
+ <stability value='Evolving' />
+ <propval name='name' type='astring' value='discard' />
+ <propval name='isrpc' type='boolean' value='false' />
+ </property_group>
+
+ <instance name='dgram' enabled='false' >
+ <exec_method
+ type='method'
+ name='inetd_start'
+ exec='/usr/lib/inet/in.discardd -d'
+ timeout_seconds='0'>
+ <method_context>
+ <method_credential user='root' group='root' />
+ </method_context>
+ </exec_method>
+
+ <exec_method
+ type='method'
+ name='inetd_offline'
+ exec=':kill_process'
+ timeout_seconds='0'>
+ </exec_method>
+
+ <exec_method
+ type='method'
+ name='inetd_disable'
+ exec=':kill'
+ timeout_seconds='0'>
+ </exec_method>
+
+ <property_group name='inetd' type='framework'>
+ <propval name='endpoint_type' type='astring'
+ value='dgram' />
+ <propval name='proto' type='astring' value='udp6' />
+ <propval name='wait' type='boolean' value='true' />
+ </property_group>
+ </instance>
+
+ <instance name='stream' enabled='false' >
+ <exec_method
+ type='method'
+ name='inetd_start'
+ exec='/usr/lib/inet/in.discardd -s'
+ timeout_seconds='0'>
+ <method_context>
+ <method_credential user='root' group='root' />
+ </method_context>
+ </exec_method>
+
+ <exec_method
+ type='method'
+ name='inetd_disable'
+ exec=':kill'
+ timeout_seconds='0'>
+ </exec_method>
+
+ <property_group name='inetd' type='framework'>
+ <propval name='endpoint_type' type='astring'
+ value='stream' />
+ <propval name='proto' type='astring' value='tcp6' />
+ <propval name='wait' type='boolean' value='false' />
+ </property_group>
+ </instance>
+
+ <stability value='Evolving' />
+
+ <template>
+ <common_name>
+ <loctext xml:lang='C'>
+ discard
+ </loctext>
+ </common_name>
+ <documentation>
+ </documentation>
+ </template>
+</service>
+
+</service_bundle>
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.discardd/in.discardd.c b/usr/src/cmd/cmd-inet/usr.lib/in.discardd/in.discardd.c
new file mode 100644
index 0000000000..ebb25b539f
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.discardd/in.discardd.c
@@ -0,0 +1,78 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * discard inetd service - both stream and dgram based.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <netinet/in.h>
+#include <inetsvc.h>
+
+
+static void
+discard_stream(int s, char *argv[])
+{
+ char buffer[BUFSIZ];
+
+ setproctitle("discard", s, argv);
+ while (read(s, buffer, sizeof (buffer)) > 0)
+ ;
+}
+
+/* ARGSUSED0 */
+static void
+noop(int s, const struct sockaddr *sap, int sa_size, const void *buf, size_t sz)
+{
+}
+
+int
+main(int argc, char *argv[])
+{
+ opterr = 0; /* disable getopt error msgs */
+ switch (getopt(argc, argv, "ds")) {
+ case 'd':
+ /*
+ * We don't need to do any work since dg_template consumes the
+ * datagrams for us, and we just ignore them.
+ */
+ dg_template(noop, STDIN_FILENO, NULL, 0);
+ break;
+ case 's':
+ discard_stream(STDIN_FILENO, argv);
+ break;
+ default:
+ return (1);
+ }
+
+ return (0);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.echod/Makefile b/usr/src/cmd/cmd-inet/usr.lib/in.echod/Makefile
new file mode 100644
index 0000000000..f7c5f4114a
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.echod/Makefile
@@ -0,0 +1,34 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# cmd/cmd-inet/usr.lib/in.echod/%M%
+#
+
+PROG = in.echod
+MANIFEST= echo.xml
+
+include ../Makefile.inetsvc
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.echod/echo.xml b/usr/src/cmd/cmd-inet/usr.lib/in.echod/echo.xml
new file mode 100644
index 0000000000..ee235fef8b
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.echod/echo.xml
@@ -0,0 +1,125 @@
+<?xml version='1.0'?>
+<!DOCTYPE service_bundle SYSTEM '/usr/share/lib/xml/dtd/service_bundle.dtd.1'>
+
+<!--
+ Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ ident "%Z%%M% %I% %E% SMI"
+
+ NOTE: This service manifest is not editable; its contents will
+ be overwritten by package or patch operations, including
+ operating system upgrade. Make customizations in a different
+ file.
+
+ Service manifests for echo
+-->
+
+<service_bundle type='manifest' name='SUNWcnsr:echo'>
+
+<service
+ name='network/echo'
+ type='service'
+ version='1'>
+
+ <restarter>
+ <service_fmri value='svc:/network/inetd:default' />
+ </restarter>
+
+ <property_group name='inetd' type='framework'>
+ <stability value='Evolving' />
+ <propval name='name' type='astring' value='echo' />
+ <propval name='isrpc' type='boolean' value='false' />
+ </property_group>
+
+ <instance name='dgram' enabled='false' >
+ <exec_method
+ type='method'
+ name='inetd_start'
+ exec='/usr/lib/inet/in.echod -d'
+ timeout_seconds='0'>
+ <method_context>
+ <method_credential user='root' group='root' />
+ </method_context>
+ </exec_method>
+
+ <exec_method
+ type='method'
+ name='inetd_offline'
+ exec=':kill_process'
+ timeout_seconds='0'>
+ </exec_method>
+
+ <exec_method
+ type='method'
+ name='inetd_disable'
+ exec=':kill'
+ timeout_seconds='0'>
+ </exec_method>
+
+ <property_group name='inetd' type='framework'>
+ <propval name='endpoint_type' type='astring' value='dgram' />
+ <propval name='proto' type='astring' value='udp6' />
+ <propval name='wait' type='boolean' value='true' />
+ </property_group>
+ </instance>
+
+ <instance name='stream' enabled='false' >
+ <exec_method
+ type='method'
+ name='inetd_start'
+ exec='/usr/lib/inet/in.echod -s'
+ timeout_seconds='0'>
+ <method_context>
+ <method_credential user='root' group='root' />
+ </method_context>
+ </exec_method>
+
+ <exec_method
+ type='method'
+ name='inetd_disable'
+ exec=':kill'
+ timeout_seconds='0'>
+ </exec_method>
+
+ <property_group name='inetd' type='framework'>
+ <propval name='endpoint_type' type='astring' value='stream' />
+ <propval name='proto' type='astring' value='tcp6' />
+ <propval name='wait' type='boolean' value='false' />
+ </property_group>
+ </instance>
+
+ <stability value='Unstable' />
+
+ <template>
+ <common_name>
+ <loctext xml:lang='C'>
+ echo
+ </loctext>
+ </common_name>
+ <documentation>
+ </documentation>
+ </template>
+</service>
+
+</service_bundle>
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.echod/in.echod.c b/usr/src/cmd/cmd-inet/usr.lib/in.echod/in.echod.c
new file mode 100644
index 0000000000..f828ef83da
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.echod/in.echod.c
@@ -0,0 +1,80 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * echo inetd service - both stream and dgram based.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <netinet/in.h>
+#include <inetsvc.h>
+
+
+static void
+echo_stream(int s, char *argv[])
+{
+ char buffer[BUFSIZ];
+ int i;
+
+ setproctitle("echo", s, argv);
+ while (((i = read(s, buffer, sizeof (buffer))) > 0) &&
+ (safe_write(s, buffer, i) == 0)) {
+ ;
+ }
+}
+
+static void
+echo_dg(int s, const struct sockaddr *sap, int sa_size, const void *buf,
+ size_t bufsiz)
+{
+ (void) safe_sendto(s, buf, bufsiz, 0, sap, sa_size);
+}
+
+
+int
+main(int argc, char *argv[])
+{
+ char buf[BUFSIZ];
+
+ opterr = 0; /* disable getopt error msgs */
+ switch (getopt(argc, argv, "ds")) {
+ case 'd':
+ dg_template(echo_dg, STDIN_FILENO, buf, sizeof (buf));
+ break;
+ case 's':
+ echo_stream(STDIN_FILENO, argv);
+ break;
+ default:
+ return (1);
+ }
+
+ return (0);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.mpathd/Makefile b/usr/src/cmd/cmd-inet/usr.lib/in.mpathd/Makefile
new file mode 100644
index 0000000000..ff1e2c0fc8
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.mpathd/Makefile
@@ -0,0 +1,87 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 1992-2002 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+PROG = in.mpathd
+PROGDFL = mpathd
+OBJS = mpd_tables.o mpd_main.o mpd_probe.o
+SRCS = $(OBJS:%.o=%.c)
+
+include ../../../Makefile.cmd
+
+POFILE = $(PROG).po
+POFILES = $(SRCS:%.c=%.po)
+
+#
+# We need access to the ancillary data features which are only available
+# via UNIX98; enable the appropriate #defines for UNIX98.
+#
+CPPFLAGS += -D_XOPEN_SOURCE=500 -D__EXTENSIONS__ -I$(SRC)/cmd/cmd-inet/common
+LDLIBS += -lcmd -lsocket -lnsl -lsysevent -lnvpair -lipmp -lc
+
+DFLTD = $(ROOTETC)/default
+ETCDFLTPROG = $(PROGDFL:%=$(DFLTD)/%)
+$(ETCDFLTPROG) := FILEMODE = 0444
+$(ETCDFLTPROG) := OWNER = root
+$(ETCDFLTPROG) := GROUP = sys
+
+LINTFLAGS += -erroff=E_FUNC_DECL_VAR_ARG2 -erroff=E_INCONS_VAL_TYPE_DECL2 \
+ -erroff=E_FUNC_USED_VAR_ARG2 -erroff=E_INCONS_ARG_DECL2 \
+ -erroff=E_NAME_USED_NOT_DEF2 -erroff=E_INCONS_ARG_USED2 \
+ -errtags=yes
+
+.KEEP_STATE:
+
+all: $(PROG) $(PROGDFL).dfl
+
+$(PROG): $(OBJS)
+ $(LINK.c) -o $@ $(OBJS) $(LDLIBS)
+ $(POST_PROCESS)
+
+include ../Makefile.lib
+
+$(ROOTSBINPROG):
+ $(RM) $@; $(SYMLINK) ../usr/lib/inet/$(PROG) $@
+
+install: all $(ROOTLIBINETPROG) $(ROOTSBINPROG) $(DFLTD) $(ETCDFLTPROG)
+
+clean:
+ $(RM) $(OBJS)
+
+lint: lint_SRCS
+
+$(DFLTD):
+ $(INS.dir)
+
+$(DFLTD)/%: %.dfl
+ $(INS.rename)
+
+$(POFILE): $(POFILES)
+ $(RM) $@
+ $(CAT) $(POFILES) > $@
+
+include ../../../Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.mpathd/mpathd.dfl b/usr/src/cmd/cmd-inet/usr.lib/in.mpathd/mpathd.dfl
new file mode 100644
index 0000000000..8fa27b2ee9
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.mpathd/mpathd.dfl
@@ -0,0 +1,39 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2000 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Time taken by mpathd to detect a NIC failure in ms. The minimum time
+# that can be specified is 100 ms.
+#
+FAILURE_DETECTION_TIME=10000
+#
+# Failback is enabled by default. To disable failback turn off this option
+#
+FAILBACK=yes
+#
+# By default only interfaces configured as part of multipathing groups
+# are tracked. Turn off this option to track all network interfaces
+# on the system
+#
+TRACK_INTERFACES_ONLY_WITH_GROUPS=yes
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.mpathd/mpd_defs.h b/usr/src/cmd/cmd-inet/usr.lib/in.mpathd/mpd_defs.h
new file mode 100644
index 0000000000..11a8692820
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.mpathd/mpd_defs.h
@@ -0,0 +1,216 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _MPD_DEFS_H
+#define _MPD_DEFS_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <alloca.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <poll.h>
+#include <unistd.h>
+#include <time.h>
+#include <stdarg.h>
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/sysmacros.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <sys/ioctl.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <stropts.h>
+#include <sys/tihdr.h>
+#include <inet/mib2.h>
+
+#include <string.h>
+#include <ctype.h>
+
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/if_ether.h>
+#include <netinet/ip6.h>
+#include <netinet/icmp6.h>
+#include <net/route.h>
+#include <net/if_dl.h>
+
+#include <inet/ip.h>
+#include <libintl.h>
+#include <locale.h>
+#include <deflt.h>
+
+#include <libnvpair.h>
+#include <libsysevent.h>
+#include <sys/sysevent.h>
+#include <sys/sysevent/eventdefs.h>
+#include <sys/sysevent/ipmp.h>
+
+#include <ipmp_mpathd.h>
+#include <ipmp_query_impl.h>
+#include <assert.h>
+
+/* Debug flags */
+#define D_ALL 0xffff /* enable all debug */
+#define D_PROBE 0x0001 /* probe mechanism */
+#define D_FAILOVER 0x0002 /* failover mechanism */
+#define D_PHYINT 0x0004 /* phyint table */
+#define D_LOGINT 0x0008 /* logint table */
+#define D_TARGET 0x0010 /* target table */
+#define D_TIMER 0x0020 /* Timer mechanism */
+#define D_PKTBAD 0x0040 /* Malformed packet */
+#define D_LINKNOTE 0x0080 /* Link up/down notifications */
+/*
+ * Need a common header file that defines the 2 constants below.
+ * Many applications need them.
+ */
+#define IF_SEPARATOR ':'
+#define IPV6_MAX_HOPS 255
+
+/*
+ * General parameters for phyint failure/repair detection
+ */
+#define NUM_PROBE_FAILS 5 /* NUM_PROBE_FAILS probe failures */
+ /* trigger NIC failure detection */
+#define NUM_PROBE_REPAIRS 10 /* NUM_PROBE_REPAIRS probe repairs */
+ /* trigger NIC repair detection */
+
+#define MIN_RANDOM_FACTOR 0.5 /* Randomization factors to */
+#define MAX_RANDOM_FACTOR 1.0 /* determine probe send time */
+
+#define MIN_PROBE_TARGETS 3 /* Minimum number of targets */
+#define MAX_PROBE_TARGETS 5 /* Maximum number of targets */
+
+/*
+ * A target that is declared slow is usable again after MIN_RECOVERY_TIME ns
+ */
+#define MIN_RECOVERY_TIME (60000000000LL) /* (In ns) 60 secs */
+
+/*
+ * If the Failure Detection Time (FDT) is bumped up because the target CRTT
+ * is high, it won't be reduced for the next MIN_SETTLING_TIME ns, to prevent
+ * flapping of FDT
+ */
+#define MIN_SETTLING_TIME (60000000000LL) /* (In ns) 60 secs */
+
+/*
+ * The circular probe stats array should be able to hold enough
+ * samples to detect phyint failure, target failure, phyint repair
+ * and target repair.
+ */
+#define PROBE_STATS_COUNT \
+ ((uint16_t)(NUM_PROBE_REPAIRS * MAX_PROBE_TARGETS + 2))
+
+#define FAILURE_DETECTION_TIME 10000 /* Default is 10 s */
+#define MIN_FAILURE_DETECTION_TIME 100 /* Minimum is 100 ms */
+#define FAILURE_DETECTION_QP 40 /* quiet period, in seconds */
+
+#define NEXT_FDT_MULTIPLE 2 /* Raise or lower the FDT by this */
+ /* factor when required */
+#define LOWER_FDT_TRIGGER 4 /* Lower the FDT if crtt is less */
+ /* than FDT / LOWER_FDT_TRIGGER */
+#define EXCEPTION_FACTOR 2 /* The exception target has a crtt */
+ /* greater by this factor */
+
+#define IF_SCAN_INTERVAL 20000 /* Do initifs() every 20 secs */
+
+/* Return a random number from a range inclusive of the endpoints */
+#define GET_RANDOM(LOW, HIGH) (random() % ((HIGH) - (LOW) + 1) + (LOW))
+
+#define TIMER_INFINITY 0x7FFFFFFFU /* Never time out */
+
+/*
+ * Comparing unsigned 32 bit time values in a circular 32-bit sequence space
+ */
+#define TIME_GE(a, b) ((int32_t)((a) - (b)) >= 0)
+#define TIME_GT(a, b) ((int32_t)((a) - (b)) > 0)
+#define TIME_LT(a, b) ((int32_t)((a) - (b)) < 0)
+#define TIME_LE(a, b) ((int32_t)((a) - (b)) <= 0)
+
+/*
+ * Comparing unsigned 16 bit sequence numbers in a circular 16-bit
+ * sequence space
+ */
+#define SEQ_GE(a, b) ((int16_t)((a) - (b)) >= (int16_t)0)
+#define SEQ_GT(a, b) ((int16_t)((a) - (b)) > (int16_t)0)
+#define SEQ_LT(a, b) ((int16_t)((a) - (b)) < (int16_t)0)
+#define SEQ_LE(a, b) ((int16_t)((a) - (b)) <= (int16_t)0)
+
+#define AF_OTHER(af) ((af) == AF_INET ? AF_INET6 : AF_INET)
+#define AF_STR(af) ((af) == AF_INET ? "inet" : "inet6")
+
+/*
+ * Globals
+ */
+extern boolean_t failback_enabled; /* cmd option to disable failbacks */
+extern boolean_t track_all_phyints; /* cmd option to track all phyints */
+
+ /* all times below in millisec */
+extern int user_probe_interval; /* interval between probes, as */
+ /* derived from user specified fdt */
+extern int user_failure_detection_time; /* User specified fdt */
+
+extern int ifsock_v4; /* IPv4 socket for ioctls */
+extern int ifsock_v6; /* IPv6 socket for ioctls */
+
+extern boolean_t full_scan_required; /* Do full scans */
+
+extern int debug; /* debug option */
+
+extern boolean_t handle_link_notifications;
+
+/*
+ * Function prototypes
+ */
+void timer_schedule(uint_t delay);
+extern void logerr(char *fmt, ...);
+extern void logtrace(char *fmt, ...);
+extern void logdebug(char *fmt, ...);
+extern void logperror(char *str);
+
+extern int poll_add(int fd);
+extern uint_t getcurrenttime(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MPD_DEFS_H */
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.mpathd/mpd_main.c b/usr/src/cmd/cmd-inet/usr.lib/in.mpathd/mpd_main.c
new file mode 100644
index 0000000000..8f9b5cad09
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.mpathd/mpd_main.c
@@ -0,0 +1,3104 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "mpd_defs.h"
+#include "mpd_tables.h"
+
+int debug = 0; /* Debug flag */
+static int pollfd_num = 0; /* Num. of poll descriptors */
+static struct pollfd *pollfds = NULL; /* Array of poll descriptors */
+
+ /* All times below in ms */
+int user_failure_detection_time; /* user specified failure detection */
+ /* time (fdt) */
+int user_probe_interval; /* derived from user specified fdt */
+
+static int rtsock_v4; /* AF_INET routing socket */
+static int rtsock_v6; /* AF_INET6 routing socket */
+int ifsock_v4 = -1; /* IPv4 socket for ioctls */
+int ifsock_v6 = -1; /* IPv6 socket for ioctls */
+static int lsock_v4; /* Listen socket to detect mpathd */
+static int lsock_v6; /* Listen socket to detect mpathd */
+static int mibfd = -1; /* fd to get mib info */
+static boolean_t force_mcast = _B_FALSE; /* Only for test purposes */
+
+boolean_t full_scan_required = _B_FALSE;
+static uint_t last_initifs_time; /* Time when initifs was last run */
+static char **argv0; /* Saved for re-exec on SIGHUP */
+boolean_t handle_link_notifications = _B_TRUE;
+
+static void initlog(void);
+static void run_timeouts(void);
+static void initifs(void);
+static void check_if_removed(struct phyint_instance *pii);
+static void select_test_ifs(void);
+static void ire_process_v4(mib2_ipRouteEntry_t *buf, size_t len);
+static void ire_process_v6(mib2_ipv6RouteEntry_t *buf, size_t len);
+static void router_add_v4(mib2_ipRouteEntry_t *rp1,
+ struct in_addr nexthop_v4);
+static void router_add_v6(mib2_ipv6RouteEntry_t *rp1,
+ struct in6_addr nexthop_v6);
+static void router_add_common(int af, char *ifname,
+ struct in6_addr nexthop);
+static void init_router_targets();
+static void cleanup(void);
+static int setup_listener(int af);
+static void check_config(void);
+static void check_addr_unique(int af, char *name);
+static void init_host_targets(void);
+static void dup_host_targets(struct phyint_instance *desired_pii);
+static void loopback_cmd(int sock, int family);
+static int poll_remove(int fd);
+static boolean_t daemonize(void);
+static int closefunc(void *, int);
+static unsigned int process_cmd(int newfd, union mi_commands *mpi);
+static unsigned int process_query(int fd, mi_query_t *miq);
+static unsigned int send_groupinfo(int fd, ipmp_groupinfo_t *grinfop);
+static unsigned int send_grouplist(int fd, ipmp_grouplist_t *grlistp);
+static unsigned int send_ifinfo(int fd, ipmp_ifinfo_t *ifinfop);
+static unsigned int send_result(int fd, unsigned int error, int syserror);
+
+/*
+ * Return the current time in milliseconds (from an arbitrary reference)
+ * truncated to fit into an int. Truncation is ok since we are interested
+ * only in differences and not the absolute values.
+ */
+uint_t
+getcurrenttime(void)
+{
+ uint_t cur_time; /* In ms */
+
+ /*
+ * Use of a non-user-adjustable source of time is
+ * required. However millisecond precision is sufficient.
+ * divide by 10^6
+ */
+ cur_time = (uint_t)(gethrtime() / 1000000LL);
+ return (cur_time);
+}
+
+/*
+ * Add fd to the set being polled. Returns 0 if ok; -1 if failed.
+ */
+int
+poll_add(int fd)
+{
+ int i;
+ int new_num;
+ struct pollfd *newfds;
+retry:
+ /* Check if already present */
+ for (i = 0; i < pollfd_num; i++) {
+ if (pollfds[i].fd == fd)
+ return (0);
+ }
+ /* Check for empty spot already present */
+ for (i = 0; i < pollfd_num; i++) {
+ if (pollfds[i].fd == -1) {
+ pollfds[i].fd = fd;
+ return (0);
+ }
+ }
+
+ /* Allocate space for 32 more fds and initialize to -1 */
+ new_num = pollfd_num + 32;
+ newfds = realloc(pollfds, new_num * sizeof (struct pollfd));
+ if (newfds == NULL) {
+ logperror("poll_add: realloc");
+ return (-1);
+ }
+ for (i = pollfd_num; i < new_num; i++) {
+ newfds[i].fd = -1;
+ newfds[i].events = POLLIN;
+ }
+ pollfd_num = new_num;
+ pollfds = newfds;
+ goto retry;
+}
+
+/*
+ * Remove fd from the set being polled. Returns 0 if ok; -1 if failed.
+ */
+static int
+poll_remove(int fd)
+{
+ int i;
+
+ /* Check if already present */
+ for (i = 0; i < pollfd_num; i++) {
+ if (pollfds[i].fd == fd) {
+ pollfds[i].fd = -1;
+ return (0);
+ }
+ }
+ return (-1);
+}
+
+/*
+ * Extract information about the phyint instance. If the phyint instance still
+ * exists in the kernel then set pii_in_use, else clear it. check_if_removed()
+ * will use it to detect phyint instances that don't exist any longer and
+ * remove them, from our database of phyint instances.
+ * Return value:
+ * returns true if the phyint instance exists in the kernel,
+ * returns false otherwise
+ */
+static boolean_t
+pii_process(int af, char *name, struct phyint_instance **pii_p)
+{
+ int err;
+ struct phyint_instance *pii;
+ struct phyint_instance *pii_other;
+
+ if (debug & D_PHYINT)
+ logdebug("pii_process(%s %s)\n", AF_STR(af), name);
+
+ pii = phyint_inst_lookup(af, name);
+ if (pii == NULL) {
+ /*
+ * Phyint instance does not exist in our tables,
+ * create new phyint instance
+ */
+ pii = phyint_inst_init_from_k(af, name);
+ } else {
+ /* Phyint exists in our tables */
+ err = phyint_inst_update_from_k(pii);
+
+ switch (err) {
+ case PI_IOCTL_ERROR:
+ /* Some ioctl error. don't change anything */
+ pii->pii_in_use = 1;
+ break;
+
+ case PI_GROUP_CHANGED:
+ /*
+ * The phyint has changed group.
+ */
+ restore_phyint(pii->pii_phyint);
+ /* FALLTHRU */
+
+ case PI_IFINDEX_CHANGED:
+ /*
+ * Interface index has changed. Delete and
+ * recreate the phyint as it is quite likely
+ * the interface has been unplumbed and replumbed.
+ */
+ pii_other = phyint_inst_other(pii);
+ if (pii_other != NULL)
+ phyint_inst_delete(pii_other);
+ phyint_inst_delete(pii);
+ pii = phyint_inst_init_from_k(af, name);
+ break;
+
+ case PI_DELETED:
+ /* Phyint instance has disappeared from kernel */
+ pii->pii_in_use = 0;
+ break;
+
+ case PI_OK:
+ /* Phyint instance exists and is fine */
+ pii->pii_in_use = 1;
+ break;
+
+ default:
+ /* Unknown status */
+ logerr("pii_process: Unknown status %d\n", err);
+ break;
+ }
+ }
+
+ *pii_p = pii;
+ if (pii != NULL)
+ return (pii->pii_in_use ? _B_TRUE : _B_FALSE);
+ else
+ return (_B_FALSE);
+}
+
+/*
+ * This phyint is leaving the group. Try to restore the phyint to its
+ * initial state. Return the addresses that belong to other group members,
+ * to the group, and take back any addresses owned by this phyint
+ */
+void
+restore_phyint(struct phyint *pi)
+{
+ if (pi->pi_group == phyint_anongroup)
+ return;
+
+ /*
+ * Move everthing to some other member in the group.
+ * The phyint has changed group in the kernel. But we
+ * have yet to do it in our tables.
+ */
+ if (!pi->pi_empty)
+ (void) try_failover(pi, FAILOVER_TO_ANY);
+ /*
+ * Move all addresses owned by 'pi' back to pi, from each
+ * of the other members of the group
+ */
+ (void) try_failback(pi, _B_FALSE);
+}
+
+/*
+ * Scan all interfaces to detect changes as well as new and deleted interfaces
+ */
+static void
+initifs()
+{
+ int n;
+ int af;
+ char *cp;
+ char *buf;
+ int numifs;
+ struct lifnum lifn;
+ struct lifconf lifc;
+ struct lifreq *lifr;
+ struct logint *li;
+ struct phyint_instance *pii;
+ struct phyint_instance *next_pii;
+ char pi_name[LIFNAMSIZ + 1];
+ boolean_t exists;
+ struct phyint *pi;
+
+ if (debug & D_PHYINT)
+ logdebug("initifs: Scanning interfaces\n");
+
+ last_initifs_time = getcurrenttime();
+
+ /*
+ * Mark the interfaces so that we can find phyints and logints
+ * which have disappeared from the kernel. pii_process() and
+ * logint_init_from_k() will set {pii,li}_in_use when they find
+ * the interface in the kernel. Also, clear dupaddr bit on probe
+ * logint. check_addr_unique() will set the dupaddr bit on the
+ * probe logint, if the testaddress is not unique.
+ */
+ for (pii = phyint_instances; pii != NULL; pii = pii->pii_next) {
+ pii->pii_in_use = 0;
+ for (li = pii->pii_logint; li != NULL; li = li->li_next) {
+ li->li_in_use = 0;
+ if (pii->pii_probe_logint == li)
+ li->li_dupaddr = 0;
+ }
+ }
+
+ lifn.lifn_family = AF_UNSPEC;
+ lifn.lifn_flags = 0;
+ if (ioctl(ifsock_v4, SIOCGLIFNUM, (char *)&lifn) < 0) {
+ logperror("initifs: ioctl (get interface numbers)");
+ return;
+ }
+ numifs = lifn.lifn_count;
+
+ buf = (char *)calloc(numifs, sizeof (struct lifreq));
+ if (buf == NULL) {
+ logperror("initifs: calloc");
+ return;
+ }
+
+ lifc.lifc_family = AF_UNSPEC;
+ lifc.lifc_flags = 0;
+ lifc.lifc_len = numifs * sizeof (struct lifreq);
+ lifc.lifc_buf = buf;
+
+ if (ioctl(ifsock_v4, SIOCGLIFCONF, (char *)&lifc) < 0) {
+ /*
+ * EINVAL is commonly encountered, when things change
+ * underneath us rapidly, (eg. at boot, when new interfaces
+ * are plumbed successively) and the kernel finds the buffer
+ * size we passed as too small. We will retry again
+ * when we see the next routing socket msg, or at worst after
+ * IF_SCAN_INTERVAL ms.
+ */
+ if (errno != EINVAL) {
+ logperror("initifs: ioctl"
+ " (get interface configuration)");
+ }
+ free(buf);
+ return;
+ }
+
+ lifr = (struct lifreq *)lifc.lifc_req;
+
+ /*
+ * For each lifreq returned by SIOGGLIFCONF, call pii_process()
+ * and get the state of the corresponding phyint_instance. If it is
+ * successful, then call logint_init_from_k() to get the state of the
+ * logint.
+ */
+ for (n = lifc.lifc_len / sizeof (struct lifreq); n > 0; n--, lifr++) {
+ af = lifr->lifr_addr.ss_family;
+
+ /*
+ * Need to pass a phyint name to pii_process. Insert the
+ * null where the ':' IF_SEPARATOR is found in the logical
+ * name.
+ */
+ (void) strncpy(pi_name, lifr->lifr_name, sizeof (pi_name));
+ pi_name[sizeof (pi_name) - 1] = '\0';
+ if ((cp = strchr(pi_name, IF_SEPARATOR)) != NULL)
+ *cp = '\0';
+
+ exists = pii_process(af, pi_name, &pii);
+ if (exists) {
+ /* The phyint is fine. So process the logint */
+ logint_init_from_k(pii, lifr->lifr_name);
+ }
+ check_addr_unique(af, lifr->lifr_name);
+ }
+
+ free(buf);
+
+ /*
+ * If the test address is now unique, and if it was not unique
+ * previously, clear the li_dupaddrmsg_printed flag and log a
+ * recovery message
+ */
+ for (pii = phyint_instances; pii != NULL; pii = pii->pii_next) {
+ struct logint *li;
+ char abuf[INET6_ADDRSTRLEN];
+
+ li = pii->pii_probe_logint;
+ if ((li != NULL) && !li->li_dupaddr &&
+ li->li_dupaddrmsg_printed) {
+ logerr("Test address %s is unique; enabling probe-"
+ "based failure detection\n",
+ pr_addr(pii->pii_af, li->li_addr, abuf,
+ sizeof (abuf)));
+ li->li_dupaddrmsg_printed = 0;
+ }
+ }
+
+ /*
+ * Scan for phyints and logints that have disappeared from the
+ * kernel, and delete them.
+ */
+ pii = phyint_instances;
+
+ while (pii != NULL) {
+ next_pii = pii->pii_next;
+ check_if_removed(pii);
+ pii = next_pii;
+ }
+
+ /*
+ * Select a test address for sending probes on each phyint instance
+ */
+ select_test_ifs();
+
+ /*
+ * Handle link up/down notifications from the NICs.
+ */
+ process_link_state_changes();
+
+ for (pi = phyints; pi != NULL; pi = pi->pi_next) {
+ /*
+ * If this is a case of group failure, we don't have much
+ * to do until the group recovers again.
+ */
+ if (GROUP_FAILED(pi->pi_group))
+ continue;
+
+ /*
+ * Try/Retry any pending failovers / failbacks, that did not
+ * not complete, or that could not be initiated previously.
+ * This implements the 3 invariants described in the big block
+ * comment at the beginning of probe.c
+ */
+ if (pi->pi_flags & IFF_INACTIVE) {
+ if (!pi->pi_empty)
+ (void) try_failover(pi, FAILOVER_TO_NONSTANDBY);
+ } else {
+ struct phyint_instance *pii;
+
+ pii = pi->pi_v4;
+ if (LINK_UP(pi) && !PROBE_CAPABLE(pii))
+ pii = pi->pi_v6;
+ if (LINK_UP(pi) && !PROBE_CAPABLE(pii))
+ continue;
+ /*
+ * It is possible that the phyint has started
+ * receiving packets, after it has been marked
+ * PI_FAILED. Don't initiate failover, if the
+ * phyint has started recovering. failure_state()
+ * captures this check. A similar logic is used
+ * for failback/repair case.
+ */
+ if (pi->pi_state == PI_FAILED && !pi->pi_empty &&
+ (failure_state(pii) == PHYINT_FAILURE)) {
+ (void) try_failover(pi, FAILOVER_NORMAL);
+ } else if (pi->pi_state == PI_RUNNING && !pi->pi_full) {
+ if (try_failback(pi, _B_FALSE) !=
+ IPMP_FAILURE) {
+ (void) change_lif_flags(pi, IFF_FAILED,
+ _B_FALSE);
+ /* Per state diagram */
+ pi->pi_empty = 0;
+ }
+ }
+ }
+ }
+}
+
+/*
+ * Check that test/probe addresses are always unique. link-locals and
+ * ptp unnumbered may not be unique, and bind to such an (IFF_NOFAILOVER)
+ * address can produce unexpected results. Log an error and alert the user.
+ */
+static void
+check_addr_unique(int af, char *name)
+{
+ struct lifreq lifr;
+ struct phyint *pi;
+ struct in6_addr addr;
+ struct phyint_instance *pii;
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+ int ifsock;
+ char abuf[INET6_ADDRSTRLEN];
+
+ /* Get the socket for doing ioctls */
+ ifsock = (af == AF_INET) ? ifsock_v4 : ifsock_v6;
+
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+ lifr.lifr_name[sizeof (lifr.lifr_name) - 1] = '\0';
+ /*
+ * Get the address corresponding to 'name'. We cannot
+ * do a logint lookup in our tables, because, not all logints
+ * in the system are tracked by mpathd. (eg. things not in a group)
+ */
+ if (ioctl(ifsock, SIOCGLIFADDR, (char *)&lifr) < 0) {
+ if (errno == ENXIO) {
+ /* Interface has vanished */
+ return;
+ } else {
+ logperror("ioctl (get addr)");
+ return;
+ }
+ }
+
+ if (af == AF_INET) {
+ sin = (struct sockaddr_in *)&lifr.lifr_addr;
+ IN6_INADDR_TO_V4MAPPED(&sin->sin_addr, &addr);
+ } else {
+ sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
+ addr = sin6->sin6_addr;
+ }
+
+ /*
+ * Does the address 'addr' match any known test address ? If so
+ * it is a duplicate, unless we are looking at the same logint
+ */
+ for (pi = phyints; pi != NULL; pi = pi->pi_next) {
+ pii = PHYINT_INSTANCE(pi, af);
+ if (pii == NULL || pii->pii_probe_logint == NULL)
+ continue;
+
+ if (!IN6_ARE_ADDR_EQUAL(&addr,
+ &pii->pii_probe_logint->li_addr)) {
+ continue;
+ }
+
+ if (strncmp(pii->pii_probe_logint->li_name, name,
+ sizeof (pii->pii_probe_logint->li_name)) == 0) {
+ continue;
+ }
+
+ /*
+ * This test address is not unique. Set the dupaddr bit
+ */
+ pii->pii_probe_logint->li_dupaddr = 1;
+
+ /*
+ * Log an error message if not already logged
+ */
+ if (pii->pii_probe_logint->li_dupaddrmsg_printed)
+ continue;
+
+ logerr("Test address %s is not unique; disabling "
+ "probe-based failure detection\n",
+ pr_addr(af, addr, abuf, sizeof (abuf)));
+
+ pii->pii_probe_logint->li_dupaddrmsg_printed = 1;
+ }
+}
+
+/*
+ * The pii_probe_logint used for probing, must satisfy the following properties
+ * with respect to its li_flags.
+ * IFF_NOFAILOVER - must be set (except in singleton group case)
+ * IFF_UP - must be set
+ * IFF_NOXMIT - must be clear
+ * IFF_NOLOCAL - must be clear
+ * IFF_DEPRECATED - preferably set (for IPv4)
+ */
+#define BEST_FLAG_SET (IFF_NOFAILOVER | IFF_UP | IFF_DEPRECATED)
+#define CLEAR_FLAG_SET (IFF_NOXMIT | IFF_NOLOCAL)
+#define TEST_CLEAR_FLAG_SET CLEAR_FLAG_SET
+#define TEST_MINIMAL_FLAG_SET (IFF_UP | CLEAR_FLAG_SET)
+#define TEST_BEST_FLAG_SET (BEST_FLAG_SET | CLEAR_FLAG_SET)
+
+/*
+ * Stop probing an interface. Called when an interface is offlined.
+ * The probe socket is closed on each interface instance, and the
+ * interface state set to PI_OFFLINE.
+ */
+static void
+stop_probing(struct phyint *pi)
+{
+ struct phyint_instance *pii;
+
+ pii = pi->pi_v4;
+ if (pii != NULL) {
+ if (pii->pii_probe_sock != -1)
+ close_probe_socket(pii, _B_TRUE);
+ pii->pii_probe_logint = NULL;
+ }
+
+ pii = pi->pi_v6;
+ if (pii != NULL) {
+ if (pii->pii_probe_sock != -1)
+ close_probe_socket(pii, _B_TRUE);
+ pii->pii_probe_logint = NULL;
+ }
+
+ phyint_chstate(pi, PI_OFFLINE);
+}
+
+/*
+ * Do the test address selection for each phyint instance. Pick an
+ * IFF_NOFAILOVER address as test address. For singleton case,
+ * if user didn't configure an IFF_NOFAILOVER address, we will pick a
+ * normal address as test address. For (multiple adapter) groups,
+ * user is required to configure IFF_NOFAILOVER test address. Call
+ * phyint_inst_sockinit() to complete the initializations.
+ */
+static void
+select_test_ifs(void)
+{
+ struct phyint *pi;
+ struct phyint_instance *pii;
+ struct phyint_instance *next_pii;
+ struct logint *li;
+ struct logint *test_logint;
+ boolean_t target_scan_reqd = _B_FALSE;
+ struct target *tg;
+
+ if (debug & D_PHYINT)
+ logdebug("select_test_ifs\n");
+
+ /*
+ * For each phyint instance, do the test address selection
+ */
+ for (pii = phyint_instances; pii != NULL; pii = next_pii) {
+ next_pii = pii->pii_next;
+ /*
+ * An interface that is offline, should not be probed.
+ * Offline interfaces should always in PI_OFFLINE state,
+ * unless some other entity has set the offline flag.
+ */
+ if (pii->pii_phyint->pi_flags & IFF_OFFLINE) {
+ if (pii->pii_phyint->pi_state != PI_OFFLINE) {
+ logerr("shouldn't be probing offline"
+ " interface %s (state is: %u)."
+ " Stopping probes.\n",
+ pii->pii_phyint->pi_name,
+ pii->pii_phyint->pi_state);
+ stop_probing(pii->pii_phyint);
+ }
+ continue;
+ }
+
+ test_logint = pii->pii_probe_logint;
+
+ if (test_logint != NULL) {
+ if ((test_logint->li_flags & TEST_BEST_FLAG_SET)
+ == BEST_FLAG_SET)
+ continue;
+
+ /*
+ * If user configures IFF_NOXMIT or IFF_NOLOCAL
+ * flags on test addresses after in.mpathd has
+ * has started, the daemon aborts. In future
+ * this can be better handling, i.e. instead
+ * of abort the daemon, a more appropriate
+ * action may be issuing a warning and choose
+ * a different test address.
+ */
+ assert((test_logint->li_flags & TEST_CLEAR_FLAG_SET)
+ == 0);
+ }
+
+ /*
+ * Walk the logints of this phyint instance, and select
+ * the best available test address
+ */
+ for (li = pii->pii_logint; li != NULL; li = li->li_next) {
+ /*
+ * Skip any IPv6 logints that are not link-local,
+ * since we should always have a link-local address
+ * anyway and in6_data() expects link-local replies.
+ */
+ if (pii->pii_af == AF_INET6 &&
+ !IN6_IS_ADDR_LINKLOCAL(&li->li_addr))
+ continue;
+
+ if ((li->li_flags & TEST_MINIMAL_FLAG_SET) == IFF_UP) {
+ /*
+ * Now we have a testaddress, that satisfies
+ * the minimal properties.
+ */
+ if ((li->li_flags & TEST_BEST_FLAG_SET)
+ == BEST_FLAG_SET) {
+ /*
+ * This is the best possible address.
+ * So break, and continue to the
+ * next phyint
+ */
+ test_logint = li;
+ break;
+ }
+ if ((test_logint == NULL) ||
+ (!(test_logint->li_flags &
+ IFF_NOFAILOVER) &&
+ (li->li_flags & IFF_NOFAILOVER)))
+ /*
+ * This is a possible candidate,
+ * unless we find a better one.
+ */
+ test_logint = li;
+ }
+ }
+
+ /*
+ * If we've gone from a singleton group to a multiple adapter
+ * group, and we haven't found an IFF_NOFAILOVER test address
+ * by now, the old test address is no longer valid. If we are
+ * not dealing with a singleton group, and the above test
+ * address selection loop has selected a non IFF_NOFAILOVER
+ * address as a candidate, we will correct that here.
+ */
+ if ((test_logint != NULL) &&
+ !SINGLETON_GROUP(pii->pii_phyint) &&
+ !(test_logint->li_flags & IFF_NOFAILOVER)) {
+ test_logint = NULL;
+ if (pii->pii_probe_sock != -1)
+ close_probe_socket(pii, _B_TRUE);
+ pii->pii_probe_logint = NULL;
+ }
+
+ if (test_logint == NULL) {
+ /*
+ * We don't have a test address. Don't print an
+ * error message immediately. check_config() will
+ * take care of it. Zero out the probe stats array
+ * since it is no longer relevant. Optimize by
+ * checking if it is already zeroed out.
+ */
+ int pr_ndx;
+
+ pr_ndx = PROBE_INDEX_PREV(pii->pii_probe_next);
+ if (pii->pii_probes[pr_ndx].pr_status != PR_UNUSED) {
+ clear_pii_probe_stats(pii);
+ reset_crtt_all(pii->pii_phyint);
+ }
+ continue;
+ } else if (test_logint == pii->pii_probe_logint) {
+ /*
+ * If we didn't find any new test addr, go to the
+ * next phyint.
+ */
+ continue;
+ }
+
+ /*
+ * The phyint is either being assigned a new testaddr
+ * or is being assigned a testaddr for the 1st time.
+ * Need to initialize the phyint socket
+ */
+ pii->pii_probe_logint = test_logint;
+ if (!phyint_inst_sockinit(pii)) {
+ if (debug & D_PHYINT) {
+ logdebug("select_test_ifs: "
+ "phyint_sockinit failed\n");
+ }
+ phyint_inst_delete(pii);
+ continue;
+ }
+
+ /*
+ * This phyint instance is now enabled for probes; this
+ * impacts our state machine in two ways:
+ *
+ * 1. If we're probe *capable* as well (i.e., we have
+ * probe targets) and the interface is in PI_NOTARGETS,
+ * then transition to PI_RUNNING.
+ *
+ * 2. If we're not probe capable, and the other phyint
+ * instance is also not probe capable, and we were in
+ * PI_RUNNING, then transition to PI_NOTARGETS.
+ *
+ * Also see the state diagram in mpd_probe.c.
+ */
+ if (PROBE_CAPABLE(pii)) {
+ if (pii->pii_phyint->pi_state == PI_NOTARGETS)
+ phyint_chstate(pii->pii_phyint, PI_RUNNING);
+ } else if (!PROBE_CAPABLE(phyint_inst_other(pii))) {
+ if (pii->pii_phyint->pi_state == PI_RUNNING)
+ phyint_chstate(pii->pii_phyint, PI_NOTARGETS);
+ }
+
+ if (pii->pii_phyint->pi_flags & IFF_POINTOPOINT) {
+ tg = pii->pii_targets;
+ if (tg != NULL)
+ target_delete(tg);
+ assert(pii->pii_targets == NULL);
+ assert(pii->pii_target_next == NULL);
+ assert(pii->pii_ntargets == 0);
+ target_create(pii, test_logint->li_dstaddr,
+ _B_TRUE);
+ }
+
+ /*
+ * If no targets are currently known for this phyint
+ * we need to call init_router_targets. Since
+ * init_router_targets() initializes the list of targets
+ * for all phyints it is done below the loop.
+ */
+ if (pii->pii_targets == NULL)
+ target_scan_reqd = _B_TRUE;
+
+ /*
+ * Start the probe timer for this instance.
+ */
+ if (!pii->pii_basetime_inited && pii->pii_probe_sock != -1) {
+ start_timer(pii);
+ pii->pii_basetime_inited = 1;
+ }
+ }
+
+ /*
+ * Check the interface list for any interfaces that are marked
+ * PI_FAILED but no longer enabled to send probes, and call
+ * phyint_check_for_repair() to see if the link now indicates that the
+ * interface should be repaired. Also see the state diagram in
+ * mpd_probe.c.
+ */
+ for (pi = phyints; pi != NULL; pi = pi->pi_next) {
+ if (pi->pi_state == PI_FAILED &&
+ !PROBE_ENABLED(pi->pi_v4) && !PROBE_ENABLED(pi->pi_v6)) {
+ phyint_check_for_repair(pi);
+ }
+ }
+
+ /*
+ * Try to populate the target list. init_router_targets populates
+ * the target list from the routing table. If our target list is
+ * still empty, init_host_targets adds host targets based on the
+ * host target list of other phyints in the group.
+ */
+ if (target_scan_reqd) {
+ init_router_targets();
+ init_host_targets();
+ }
+}
+
+/*
+ * Check phyint group configuration, to detect any inconsistencies,
+ * and log an error message. This is called from runtimeouts every
+ * 20 secs. But the error message is displayed once. If the
+ * consistency is resolved by the admin, a recovery message is displayed
+ * once.
+ */
+static void
+check_config(void)
+{
+ struct phyint_group *pg;
+ struct phyint *pi;
+ boolean_t v4_in_group;
+ boolean_t v6_in_group;
+
+ /*
+ * All phyints of a group must be homogenous to ensure that
+ * failover or failback can be done. If any phyint in a group
+ * has IPv4 plumbed, check that all phyints have IPv4 plumbed.
+ * Do a similar check for IPv6.
+ */
+ for (pg = phyint_groups; pg != NULL; pg = pg->pg_next) {
+ if (pg == phyint_anongroup)
+ continue;
+
+ v4_in_group = _B_FALSE;
+ v6_in_group = _B_FALSE;
+ /*
+ * 1st pass. Determine if at least 1 phyint in the group
+ * has IPv4 plumbed and if so set v4_in_group to true.
+ * Repeat similarly for IPv6.
+ */
+ for (pi = pg->pg_phyint; pi != NULL; pi = pi->pi_pgnext) {
+ if (pi->pi_v4 != NULL)
+ v4_in_group = _B_TRUE;
+ if (pi->pi_v6 != NULL)
+ v6_in_group = _B_TRUE;
+ }
+
+ /*
+ * 2nd pass. If v4_in_group is true, check that phyint
+ * has IPv4 plumbed. Repeat similarly for IPv6. Print
+ * out a message the 1st time only.
+ */
+ for (pi = pg->pg_phyint; pi != NULL; pi = pi->pi_pgnext) {
+ if (pi->pi_flags & IFF_OFFLINE)
+ continue;
+
+ if (v4_in_group == _B_TRUE && pi->pi_v4 == NULL) {
+ if (!pi->pi_cfgmsg_printed) {
+ logerr("NIC %s of group %s is"
+ " not plumbed for IPv4 and may"
+ " affect failover capability\n",
+ pi->pi_name,
+ pi->pi_group->pg_name);
+ pi->pi_cfgmsg_printed = 1;
+ }
+ } else if (v6_in_group == _B_TRUE &&
+ pi->pi_v6 == NULL) {
+ if (!pi->pi_cfgmsg_printed) {
+ logerr("NIC %s of group %s is"
+ " not plumbed for IPv6 and may"
+ " affect failover capability\n",
+ pi->pi_name,
+ pi->pi_group->pg_name);
+ pi->pi_cfgmsg_printed = 1;
+ }
+ } else {
+ /*
+ * The phyint matches the group configuration,
+ * if we have reached this point. If it was
+ * improperly configured earlier, log an
+ * error recovery message
+ */
+ if (pi->pi_cfgmsg_printed) {
+ logerr("NIC %s is now consistent with "
+ "group %s and failover capability "
+ "is restored\n", pi->pi_name,
+ pi->pi_group->pg_name);
+ pi->pi_cfgmsg_printed = 0;
+ }
+ }
+
+ }
+ }
+
+ /*
+ * In order to perform probe-based failure detection, a phyint must
+ * have at least 1 test/probe address for sending and receiving probes
+ * (either on IPv4 or IPv6 instance or both). If no test address has
+ * been configured, notify the administrator, but continue on since we
+ * can still perform load spreading, along with "link up/down" based
+ * failure detection.
+ *
+ * Note: In the singleton group case, when user didn't configure
+ * a test address, the probe address is picked by this daemon.
+ */
+ for (pi = phyints; pi != NULL; pi = pi->pi_next) {
+ if (pi->pi_flags & IFF_OFFLINE)
+ continue;
+
+ if ((pi->pi_v4 == NULL ||
+ pi->pi_v4->pii_probe_logint == NULL) &&
+ (pi->pi_v6 == NULL ||
+ pi->pi_v6->pii_probe_logint == NULL)) {
+ if (!pi->pi_taddrmsg_printed) {
+ logerr("No test address configured on "
+ "interface %s; disabling probe-based "
+ "failure detection on it\n", pi->pi_name);
+ pi->pi_taddrmsg_printed = 1;
+ }
+ } else if (pi->pi_taddrmsg_printed) {
+ logerr("Test address now configured on interface %s; "
+ "enabling probe-based failure detection on it\n",
+ pi->pi_name);
+ pi->pi_taddrmsg_printed = 0;
+ }
+
+ }
+}
+
+/*
+ * Timer mechanism using relative time (in milliseconds) from the
+ * previous timer event. Timers exceeding TIMER_INFINITY milliseconds
+ * will fire after TIMER_INFINITY milliseconds.
+ * Unsigned arithmetic note: We assume a 32-bit circular sequence space for
+ * time values. Hence 2 consecutive timer events cannot be spaced farther
+ * than 0x7fffffff. We call this TIMER_INFINITY, and it is the maximum value
+ * that can be passed for the delay parameter of timer_schedule()
+ */
+static uint_t timer_next; /* Currently scheduled timeout */
+static boolean_t timer_active = _B_FALSE; /* SIGALRM has not yet occurred */
+
+static void
+timer_init(void)
+{
+ timer_next = getcurrenttime() + TIMER_INFINITY;
+ /*
+ * The call to run_timeouts() will get the timer started
+ * Since there are no phyints at this point, the timer will
+ * be set for IF_SCAN_INTERVAL ms.
+ */
+ run_timeouts();
+}
+
+/*
+ * Make sure the next SIGALRM occurs delay milliseconds from the current
+ * time if not earlier. We are interested only in time differences.
+ */
+void
+timer_schedule(uint_t delay)
+{
+ uint_t now;
+ struct itimerval itimerval;
+
+ if (debug & D_TIMER)
+ logdebug("timer_schedule(%u)\n", delay);
+
+ assert(delay <= TIMER_INFINITY);
+
+ now = getcurrenttime();
+ if (delay == 0) {
+ /* Minimum allowed delay */
+ delay = 1;
+ }
+ /* Will this timer occur before the currently scheduled SIGALRM? */
+ if (timer_active && TIME_GE(now + delay, timer_next)) {
+ if (debug & D_TIMER) {
+ logdebug("timer_schedule(%u) - no action: "
+ "now %u next %u\n", delay, now, timer_next);
+ }
+ return;
+ }
+ timer_next = now + delay;
+
+ itimerval.it_value.tv_sec = delay / 1000;
+ itimerval.it_value.tv_usec = (delay % 1000) * 1000;
+ itimerval.it_interval.tv_sec = 0;
+ itimerval.it_interval.tv_usec = 0;
+ if (debug & D_TIMER) {
+ logdebug("timer_schedule(%u): sec %ld usec %ld\n",
+ delay, itimerval.it_value.tv_sec,
+ itimerval.it_value.tv_usec);
+ }
+ timer_active = _B_TRUE;
+ if (setitimer(ITIMER_REAL, &itimerval, NULL) < 0) {
+ logperror("timer_schedule: setitimer");
+ exit(2);
+ }
+}
+
+/*
+ * Timer has fired. Determine when the next timer event will occur by asking
+ * all the timer routines. Should not be called from a timer routine.
+ */
+static void
+run_timeouts(void)
+{
+ uint_t next;
+ uint_t next_event_time;
+ struct phyint_instance *pii;
+ struct phyint_instance *next_pii;
+ static boolean_t timeout_running;
+
+ /* assert that recursive timeouts don't happen. */
+ assert(!timeout_running);
+
+ timeout_running = _B_TRUE;
+
+ if (debug & D_TIMER)
+ logdebug("run_timeouts()\n");
+
+ next = TIMER_INFINITY;
+
+ for (pii = phyint_instances; pii != NULL; pii = next_pii) {
+ next_pii = pii->pii_next;
+ next_event_time = phyint_inst_timer(pii);
+ if (next_event_time != TIMER_INFINITY && next_event_time < next)
+ next = next_event_time;
+
+ if (debug & D_TIMER) {
+ logdebug("run_timeouts(%s %s): next scheduled for"
+ " this phyint inst %u, next scheduled global"
+ " %u ms\n",
+ AF_STR(pii->pii_af), pii->pii_phyint->pi_name,
+ next_event_time, next);
+ }
+ }
+
+ /*
+ * Make sure initifs() is called at least once every
+ * IF_SCAN_INTERVAL, to make sure that we are in sync
+ * with the kernel, in case we have missed any routing
+ * socket messages.
+ */
+ if (next > IF_SCAN_INTERVAL)
+ next = IF_SCAN_INTERVAL;
+
+ if ((getcurrenttime() - last_initifs_time) > IF_SCAN_INTERVAL) {
+ initifs();
+ check_config();
+ }
+
+ if (debug & D_TIMER)
+ logdebug("run_timeouts: %u ms\n", next);
+
+ timer_schedule(next);
+ timeout_running = _B_FALSE;
+}
+
+static int eventpipe_read = -1; /* Used for synchronous signal delivery */
+static int eventpipe_write = -1;
+static boolean_t cleanup_started = _B_FALSE;
+ /* Don't write to eventpipe if in cleanup */
+/*
+ * Ensure that signals are processed synchronously with the rest of
+ * the code by just writing a one character signal number on the pipe.
+ * The poll loop will pick this up and process the signal event.
+ */
+static void
+sig_handler(int signo)
+{
+ uchar_t buf = (uchar_t)signo;
+
+ /*
+ * Don't write to pipe if cleanup has already begun. cleanup()
+ * might have closed the pipe already
+ */
+ if (cleanup_started)
+ return;
+
+ if (eventpipe_write == -1) {
+ logerr("sig_handler: no pipe found\n");
+ return;
+ }
+ if (write(eventpipe_write, &buf, sizeof (buf)) < 0)
+ logperror("sig_handler: write");
+}
+
+extern struct probes_missed probes_missed;
+
+/*
+ * Pick up a signal "byte" from the pipe and process it.
+ */
+static void
+in_signal(int fd)
+{
+ uchar_t buf;
+ uint64_t sent, acked, lost, unacked, unknown;
+ struct phyint_instance *pii;
+ int pr_ndx;
+
+ switch (read(fd, &buf, sizeof (buf))) {
+ case -1:
+ logperror("in_signal: read");
+ exit(1);
+ /* NOTREACHED */
+ case 1:
+ break;
+ case 0:
+ logerr("in_signal: read end of file\n");
+ exit(1);
+ /* NOTREACHED */
+ default:
+ logerr("in_signal: read > 1\n");
+ exit(1);
+ }
+
+ if (debug & D_TIMER)
+ logdebug("in_signal() got %d\n", buf);
+
+ switch (buf) {
+ case SIGALRM:
+ if (debug & D_TIMER) {
+ uint_t now = getcurrenttime();
+
+ logdebug("in_signal(SIGALRM) delta %u\n",
+ now - timer_next);
+ }
+ timer_active = _B_FALSE;
+ run_timeouts();
+ break;
+ case SIGUSR1:
+ logdebug("Printing configuration:\n");
+ /* Print out the internal tables */
+ phyint_inst_print_all();
+
+ /*
+ * Print out the accumulated statistics about missed
+ * probes (happens due to scheduling delay).
+ */
+ logerr("Missed sending total of %d probes spread over"
+ " %d occurrences\n", probes_missed.pm_nprobes,
+ probes_missed.pm_ntimes);
+
+ /*
+ * Print out the accumulated statistics about probes
+ * that were sent.
+ */
+ for (pii = phyint_instances; pii != NULL;
+ pii = pii->pii_next) {
+ unacked = 0;
+ acked = pii->pii_cum_stats.acked;
+ lost = pii->pii_cum_stats.lost;
+ sent = pii->pii_cum_stats.sent;
+ unknown = pii->pii_cum_stats.unknown;
+ for (pr_ndx = 0; pr_ndx < PROBE_STATS_COUNT; pr_ndx++) {
+ switch (pii->pii_probes[pr_ndx].pr_status) {
+ case PR_ACKED:
+ acked++;
+ break;
+ case PR_LOST:
+ lost++;
+ break;
+ case PR_UNACKED:
+ unacked++;
+ break;
+ }
+ }
+ logerr("\nProbe stats on (%s %s)\n"
+ "Number of probes sent %lld\n"
+ "Number of probe acks received %lld\n"
+ "Number of probes/acks lost %lld\n"
+ "Number of valid unacknowled probes %lld\n"
+ "Number of ambiguous probe acks received %lld\n",
+ AF_STR(pii->pii_af), pii->pii_name,
+ sent, acked, lost, unacked, unknown);
+ }
+ break;
+ case SIGHUP:
+ logerr("SIGHUP: restart and reread config file\n");
+ cleanup();
+ (void) execv(argv0[0], argv0);
+ _exit(0177);
+ /* NOTREACHED */
+ case SIGINT:
+ case SIGTERM:
+ case SIGQUIT:
+ cleanup();
+ exit(0);
+ /* NOTREACHED */
+ default:
+ logerr("in_signal: unknown signal: %d\n", buf);
+ }
+}
+
+static void
+cleanup(void)
+{
+ struct phyint_instance *pii;
+ struct phyint_instance *next_pii;
+
+ /*
+ * Make sure that we don't write to eventpipe in
+ * sig_handler() if any signal notably SIGALRM,
+ * occurs after we close the eventpipe descriptor below
+ */
+ cleanup_started = _B_TRUE;
+
+ for (pii = phyint_instances; pii != NULL; pii = next_pii) {
+ next_pii = pii->pii_next;
+ phyint_inst_delete(pii);
+ }
+
+ (void) close(ifsock_v4);
+ (void) close(ifsock_v6);
+ (void) close(rtsock_v4);
+ (void) close(rtsock_v6);
+ (void) close(lsock_v4);
+ (void) close(lsock_v6);
+ (void) close(0);
+ (void) close(1);
+ (void) close(2);
+ (void) close(mibfd);
+ (void) close(eventpipe_read);
+ (void) close(eventpipe_write);
+}
+
+/*
+ * Create pipe for signal delivery and set up signal handlers.
+ */
+static void
+setup_eventpipe(void)
+{
+ int fds[2];
+ struct sigaction act;
+
+ if ((pipe(fds)) < 0) {
+ logperror("setup_eventpipe: pipe");
+ exit(1);
+ }
+ eventpipe_read = fds[0];
+ eventpipe_write = fds[1];
+ if (poll_add(eventpipe_read) == -1) {
+ exit(1);
+ }
+
+ act.sa_handler = sig_handler;
+ act.sa_flags = SA_RESTART;
+ (void) sigaction(SIGALRM, &act, NULL);
+
+ (void) sigset(SIGHUP, sig_handler);
+ (void) sigset(SIGUSR1, sig_handler);
+ (void) sigset(SIGTERM, sig_handler);
+ (void) sigset(SIGINT, sig_handler);
+ (void) sigset(SIGQUIT, sig_handler);
+}
+
+/*
+ * Create a routing socket for receiving RTM_IFINFO messages.
+ */
+static int
+setup_rtsock(int af)
+{
+ int s;
+ int flags;
+
+ s = socket(PF_ROUTE, SOCK_RAW, af);
+ if (s == -1) {
+ logperror("setup_rtsock: socket PF_ROUTE");
+ exit(1);
+ }
+ if ((flags = fcntl(s, F_GETFL, 0)) < 0) {
+ logperror("setup_rtsock: fcntl F_GETFL");
+ (void) close(s);
+ exit(1);
+ }
+ if ((fcntl(s, F_SETFL, flags | O_NONBLOCK)) < 0) {
+ logperror("setup_rtsock: fcntl F_SETFL");
+ (void) close(s);
+ exit(1);
+ }
+ if (poll_add(s) == -1) {
+ (void) close(s);
+ exit(1);
+ }
+ return (s);
+}
+
+/*
+ * Process an RTM_IFINFO message received on a routing socket.
+ * The return value indicates whether a full interface scan is required.
+ * Link up/down notifications from the NICs are reflected in the
+ * IFF_RUNNING flag.
+ * If just the state of the IFF_RUNNING interface flag has changed, a
+ * a full interface scan isn't required.
+ */
+static boolean_t
+process_rtm_ifinfo(if_msghdr_t *ifm, int type)
+{
+ struct sockaddr_dl *sdl;
+ struct phyint *pi;
+ uint64_t old_flags;
+ struct phyint_instance *pii;
+
+ assert(ifm->ifm_type == RTM_IFINFO && ifm->ifm_addrs == RTA_IFP);
+
+ /*
+ * Although the sockaddr_dl structure is directly after the
+ * if_msghdr_t structure. At the time of writing, the size of the
+ * if_msghdr_t structure is different on 32 and 64 bit kernels, due
+ * to the presence of a timeval structure, which contains longs,
+ * in the if_data structure. Anyway, we know where the message ends,
+ * so we work backwards to get the start of the sockaddr_dl structure.
+ */
+ /*LINTED*/
+ sdl = (struct sockaddr_dl *)((char *)ifm + ifm->ifm_msglen -
+ sizeof (struct sockaddr_dl));
+
+ assert(sdl->sdl_family == AF_LINK);
+
+ /*
+ * The interface name is in sdl_data.
+ * RTM_IFINFO messages are only generated for logical interface
+ * zero, so there is no colon and logical interface number to
+ * strip from the name. The name is not null terminated, but
+ * there should be enough space in sdl_data to add the null.
+ */
+ if (sdl->sdl_nlen >= sizeof (sdl->sdl_data)) {
+ if (debug & D_LINKNOTE)
+ logdebug("process_rtm_ifinfo: "
+ "phyint name too long\n");
+ return (_B_TRUE);
+ }
+ sdl->sdl_data[sdl->sdl_nlen] = 0;
+
+ pi = phyint_lookup(sdl->sdl_data);
+ if (pi == NULL) {
+ if (debug & D_LINKNOTE)
+ logdebug("process_rtm_ifinfo: phyint lookup failed"
+ " for %s\n", sdl->sdl_data);
+ return (_B_TRUE);
+ }
+
+ /*
+ * We want to try and avoid doing a full interface scan for
+ * link state notifications from the NICs, as indicated
+ * by the state of the IFF_RUNNING flag. If just the
+ * IFF_RUNNING flag has changed state, the link state changes
+ * are processed without a full scan.
+ * If there is both an IPv4 and IPv6 instance associated with
+ * the physical interface, we will get an RTM_IFINFO message
+ * for each instance. If we just maintained a single copy of
+ * the physical interface flags, it would appear that no flags
+ * had changed when the second message is processed, leading us
+ * to believe that the message wasn't generated by a flags change,
+ * and that a full interface scan is required.
+ * To get around this problem, two additional copies of the flags
+ * are kept, one copy for each instance. These are only used in
+ * this routine. At any one time, all three copies of the flags
+ * should be identical except for the IFF_RUNNING flag. The
+ * copy of the flags in the "phyint" structure is always up to
+ * date.
+ */
+ pii = (type == AF_INET) ? pi->pi_v4 : pi->pi_v6;
+ if (pii == NULL) {
+ if (debug & D_LINKNOTE)
+ logdebug("process_rtm_ifinfo: no instance of address "
+ "family %s for %s\n", AF_STR(type), pi->pi_name);
+ return (_B_TRUE);
+ }
+
+ old_flags = pii->pii_flags;
+ pii->pii_flags = PHYINT_FLAGS(ifm->ifm_flags);
+ pi->pi_flags = pii->pii_flags;
+
+ if (debug & D_LINKNOTE) {
+ logdebug("process_rtm_ifinfo: %s address family: %s, "
+ "old flags: %llx, new flags: %llx\n", pi->pi_name,
+ AF_STR(type), old_flags, pi->pi_flags);
+ }
+
+ /*
+ * If IFF_STANDBY has changed, indicate that the interface has changed
+ * types.
+ */
+ if ((old_flags ^ pii->pii_flags) & IFF_STANDBY)
+ phyint_newtype(pi);
+
+ /*
+ * If IFF_INACTIVE has been set, then no data addresses should be
+ * hosted on the interface. If IFF_INACTIVE has been cleared, then
+ * move previously failed-over addresses back to it, provided it is
+ * not failed. For details, see the state diagram in mpd_probe.c.
+ */
+ if ((old_flags ^ pii->pii_flags) & IFF_INACTIVE) {
+ if (pii->pii_flags & IFF_INACTIVE) {
+ assert(pii->pii_flags & IFF_STANDBY);
+ if (!pi->pi_empty) {
+ (void) try_failover(pi, FAILOVER_TO_NONSTANDBY);
+ }
+ } else {
+ if (pi->pi_state == PI_RUNNING && !pi->pi_full) {
+ pi->pi_empty = 0;
+ (void) try_failback(pi, _B_FALSE);
+ }
+ }
+ }
+
+ /* Has just the IFF_RUNNING flag changed state ? */
+ if ((old_flags ^ pii->pii_flags) != IFF_RUNNING) {
+ struct phyint_instance *pii_other;
+ /*
+ * It wasn't just a link state change. Update
+ * the other instance's copy of the flags.
+ */
+ pii_other = phyint_inst_other(pii);
+ if (pii_other != NULL)
+ pii_other->pii_flags = pii->pii_flags;
+ return (_B_TRUE);
+ }
+
+ return (_B_FALSE);
+}
+
+/*
+ * Retrieve as many routing socket messages as possible, and try to
+ * empty the routing sockets. Initiate full scan of targets or interfaces
+ * as needed.
+ * We listen on separate IPv4 an IPv6 sockets so that we can accurately
+ * detect changes in certain flags (see "process_rtm_ifinfo()" above).
+ */
+static void
+process_rtsock(int rtsock_v4, int rtsock_v6)
+{
+ int nbytes;
+ int64_t msg[2048 / 8];
+ struct rt_msghdr *rtm;
+ boolean_t need_if_scan = _B_FALSE;
+ boolean_t need_rt_scan = _B_FALSE;
+ boolean_t rtm_ifinfo_seen = _B_FALSE;
+ int type;
+
+ /* Read as many messages as possible and try to empty the sockets */
+ for (type = AF_INET; ; type = AF_INET6) {
+ for (;;) {
+ nbytes = read((type == AF_INET) ? rtsock_v4 :
+ rtsock_v6, msg, sizeof (msg));
+ if (nbytes <= 0) {
+ /* No more messages */
+ break;
+ }
+ rtm = (struct rt_msghdr *)msg;
+ if (rtm->rtm_version != RTM_VERSION) {
+ logerr("process_rtsock: version %d "
+ "not understood\n", rtm->rtm_version);
+ break;
+ }
+
+ if (debug & D_PHYINT) {
+ logdebug("process_rtsock: message %d\n",
+ rtm->rtm_type);
+ }
+
+ switch (rtm->rtm_type) {
+ case RTM_NEWADDR:
+ case RTM_DELADDR:
+ /*
+ * Some logical interface has changed,
+ * have to scan everything to determine
+ * what actually changed.
+ */
+ need_if_scan = _B_TRUE;
+ break;
+
+ case RTM_IFINFO:
+ rtm_ifinfo_seen = _B_TRUE;
+ need_if_scan |=
+ process_rtm_ifinfo((if_msghdr_t *)rtm,
+ type);
+ break;
+
+ case RTM_ADD:
+ case RTM_DELETE:
+ case RTM_CHANGE:
+ case RTM_OLDADD:
+ case RTM_OLDDEL:
+ need_rt_scan = _B_TRUE;
+ break;
+
+ default:
+ /* Not interesting */
+ break;
+ }
+ }
+ if (type == AF_INET6)
+ break;
+ }
+
+ if (need_if_scan) {
+ if (debug & D_LINKNOTE && rtm_ifinfo_seen)
+ logdebug("process_rtsock: synchronizing with kernel\n");
+ initifs();
+ } else if (rtm_ifinfo_seen) {
+ if (debug & D_LINKNOTE)
+ logdebug("process_rtsock: "
+ "link up/down notification(s) seen\n");
+ process_link_state_changes();
+ }
+
+ if (need_rt_scan)
+ init_router_targets();
+}
+
+/*
+ * Look if the phyint instance or one of its logints have been removed from
+ * the kernel and take appropriate action.
+ * Uses {pii,li}_in_use.
+ */
+static void
+check_if_removed(struct phyint_instance *pii)
+{
+ struct logint *li;
+ struct logint *next_li;
+
+ /* Detect phyints that have been removed from the kernel. */
+ if (!pii->pii_in_use) {
+ logtrace("%s %s has been removed from kernel\n",
+ AF_STR(pii->pii_af), pii->pii_phyint->pi_name);
+ phyint_inst_delete(pii);
+ } else {
+ /* Detect logints that have been removed. */
+ for (li = pii->pii_logint; li != NULL; li = next_li) {
+ next_li = li->li_next;
+ if (!li->li_in_use) {
+ logint_delete(li);
+ }
+ }
+ }
+}
+
+/*
+ * Send down a T_OPTMGMT_REQ to ip asking for all data in the various
+ * tables defined by mib2.h. Parse the returned data and extract
+ * the 'routing' information table. Process the 'routing' table
+ * to get the list of known onlink routers, and update our database.
+ * These onlink routers will serve as our probe targets.
+ * Returns false, if any system calls resulted in errors, true otherwise.
+ */
+static boolean_t
+update_router_list(int fd)
+{
+ union {
+ char ubuf[1024];
+ union T_primitives uprim;
+ } buf;
+
+ int flags;
+ struct strbuf ctlbuf;
+ struct strbuf databuf;
+ struct T_optmgmt_req *tor;
+ struct T_optmgmt_ack *toa;
+ struct T_error_ack *tea;
+ struct opthdr *optp;
+ struct opthdr *req;
+ int status;
+ t_scalar_t prim;
+
+ tor = (struct T_optmgmt_req *)&buf;
+
+ tor->PRIM_type = T_SVR4_OPTMGMT_REQ;
+ tor->OPT_offset = sizeof (struct T_optmgmt_req);
+ tor->OPT_length = sizeof (struct opthdr);
+ tor->MGMT_flags = T_CURRENT;
+
+ req = (struct opthdr *)&tor[1];
+ req->level = MIB2_IP; /* any MIB2_xxx value ok here */
+ req->name = 0;
+ req->len = 0;
+
+ ctlbuf.buf = (char *)&buf;
+ ctlbuf.len = tor->OPT_length + tor->OPT_offset;
+ ctlbuf.maxlen = sizeof (buf);
+ flags = 0;
+ if (putmsg(fd, &ctlbuf, NULL, flags) == -1) {
+ logperror("update_router_list: putmsg(ctl)");
+ return (_B_FALSE);
+ }
+
+ /*
+ * The response consists of multiple T_OPTMGMT_ACK msgs, 1 msg for
+ * each table defined in mib2.h. Each T_OPTMGMT_ACK msg contains
+ * a control and data part. The control part contains a struct
+ * T_optmgmt_ack followed by a struct opthdr. The 'opthdr' identifies
+ * the level, name and length of the data in the data part. The
+ * data part contains the actual table data. The last message
+ * is an end-of-data (EOD), consisting of a T_OPTMGMT_ACK and a
+ * single option with zero optlen.
+ */
+
+ for (;;) {
+ /*
+ * Go around this loop once for each table. Ignore
+ * all tables except the routing information table.
+ */
+ flags = 0;
+ status = getmsg(fd, &ctlbuf, NULL, &flags);
+ if (status < 0) {
+ if (errno == EINTR)
+ continue;
+ logperror("update_router_list: getmsg(ctl)");
+ return (_B_FALSE);
+ }
+ if (ctlbuf.len < sizeof (t_scalar_t)) {
+ logerr("update_router_list: ctlbuf.len %d\n",
+ ctlbuf.len);
+ return (_B_FALSE);
+ }
+
+ prim = buf.uprim.type;
+
+ switch (prim) {
+
+ case T_ERROR_ACK:
+ tea = &buf.uprim.error_ack;
+ if (ctlbuf.len < sizeof (struct T_error_ack)) {
+ logerr("update_router_list: T_ERROR_ACK"
+ " ctlbuf.len %d\n", ctlbuf.len);
+ return (_B_FALSE);
+ }
+ logerr("update_router_list: T_ERROR_ACK:"
+ " TLI_error = 0x%lx, UNIX_error = 0x%lx\n",
+ tea->TLI_error, tea->UNIX_error);
+ return (_B_FALSE);
+
+ case T_OPTMGMT_ACK:
+ toa = &buf.uprim.optmgmt_ack;
+ optp = (struct opthdr *)&toa[1];
+ if (ctlbuf.len < sizeof (struct T_optmgmt_ack)) {
+ logerr("update_router_list: ctlbuf.len %d\n",
+ ctlbuf.len);
+ return (_B_FALSE);
+ }
+ if (toa->MGMT_flags != T_SUCCESS) {
+ logerr("update_router_list: MGMT_flags 0x%lx\n",
+ toa->MGMT_flags);
+ return (_B_FALSE);
+ }
+ break;
+
+ default:
+ logerr("update_router_list: unknown primitive %ld\n",
+ prim);
+ return (_B_FALSE);
+ }
+
+ /* Process the T_OPGMGMT_ACK below */
+ assert(prim == T_OPTMGMT_ACK);
+
+ switch (status) {
+ case 0:
+ /*
+ * We have reached the end of this T_OPTMGMT_ACK
+ * message. If this is the last message i.e EOD,
+ * return, else process the next T_OPTMGMT_ACK msg.
+ */
+ if ((ctlbuf.len == sizeof (struct T_optmgmt_ack) +
+ sizeof (struct opthdr)) && optp->len == 0 &&
+ optp->name == 0 && optp->level == 0) {
+ /*
+ * This is the EOD message. Return
+ */
+ return (_B_TRUE);
+ }
+ continue;
+
+ case MORECTL:
+ case MORECTL | MOREDATA:
+ /*
+ * This should not happen. We should be able to read
+ * the control portion in a single getmsg.
+ */
+ logerr("update_router_list: MORECTL\n");
+ return (_B_FALSE);
+
+ case MOREDATA:
+ databuf.maxlen = optp->len;
+ /* malloc of 0 bytes is ok */
+ databuf.buf = malloc((size_t)optp->len);
+ if (databuf.maxlen != 0 && databuf.buf == NULL) {
+ logperror("update_router_list: malloc");
+ return (_B_FALSE);
+ }
+ databuf.len = 0;
+ flags = 0;
+ for (;;) {
+ status = getmsg(fd, NULL, &databuf, &flags);
+ if (status >= 0) {
+ break;
+ } else if (errno == EINTR) {
+ continue;
+ } else {
+ logperror("update_router_list:"
+ " getmsg(data)");
+ free(databuf.buf);
+ return (_B_FALSE);
+ }
+ }
+
+ if (optp->level == MIB2_IP &&
+ optp->name == MIB2_IP_ROUTE) {
+ /* LINTED */
+ ire_process_v4((mib2_ipRouteEntry_t *)
+ databuf.buf, databuf.len);
+ } else if (optp->level == MIB2_IP6 &&
+ optp->name == MIB2_IP6_ROUTE) {
+ /* LINTED */
+ ire_process_v6((mib2_ipv6RouteEntry_t *)
+ databuf.buf, databuf.len);
+ }
+ free(databuf.buf);
+ }
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * Examine the IPv4 routing table, for default routers. For each default
+ * router, populate the list of targets of each phyint that is on the same
+ * link as the default router
+ */
+static void
+ire_process_v4(mib2_ipRouteEntry_t *buf, size_t len)
+{
+ mib2_ipRouteEntry_t *rp;
+ mib2_ipRouteEntry_t *rp1;
+ struct in_addr nexthop_v4;
+ mib2_ipRouteEntry_t *endp;
+
+ if (len == 0)
+ return;
+ assert((len % sizeof (mib2_ipRouteEntry_t)) == 0);
+
+ endp = buf + (len / sizeof (mib2_ipRouteEntry_t));
+
+ /*
+ * Loop thru the routing table entries. Process any IRE_DEFAULT,
+ * IRE_PREFIX, IRE_HOST, IRE_HOST_REDIRECT ire. Ignore the others.
+ * For each such IRE_OFFSUBNET ire, get the nexthop gateway address.
+ * This is a potential target for probing, which we try to add
+ * to the list of probe targets.
+ */
+ for (rp = buf; rp < endp; rp++) {
+ if (!(rp->ipRouteInfo.re_ire_type & IRE_OFFSUBNET))
+ continue;
+
+ /* Get the nexthop address. */
+ nexthop_v4.s_addr = rp->ipRouteNextHop;
+
+ /*
+ * Get the nexthop address. Then determine the outgoing
+ * interface, by examining all interface IREs, and picking the
+ * match. We don't look at the interface specified in the route
+ * because we need to add the router target on all matching
+ * interfaces anyway; the goal is to avoid falling back to
+ * multicast when some interfaces are in the same subnet but
+ * not in the same group.
+ */
+ for (rp1 = buf; rp1 < endp; rp1++) {
+ if (!(rp1->ipRouteInfo.re_ire_type & IRE_INTERFACE)) {
+ continue;
+ }
+
+ /*
+ * Determine the interface IRE that matches the nexthop.
+ * i.e. (IRE addr & IRE mask) == (nexthop & IRE mask)
+ */
+ if ((rp1->ipRouteDest & rp1->ipRouteMask) ==
+ (nexthop_v4.s_addr & rp1->ipRouteMask)) {
+ /*
+ * We found the interface ire
+ */
+ router_add_v4(rp1, nexthop_v4);
+ }
+ }
+ }
+}
+
+void
+router_add_v4(mib2_ipRouteEntry_t *rp1, struct in_addr nexthop_v4)
+{
+ char *cp;
+ char ifname[LIFNAMSIZ + 1];
+ struct in6_addr nexthop;
+ int len;
+
+ if (debug & D_TARGET)
+ logdebug("router_add_v4()\n");
+
+ len = MIN(rp1->ipRouteIfIndex.o_length, sizeof (ifname) - 1);
+ (void) memcpy(ifname, rp1->ipRouteIfIndex.o_bytes, len);
+ ifname[len] = '\0';
+
+ if (ifname[0] == '\0')
+ return;
+
+ cp = strchr(ifname, IF_SEPARATOR);
+ if (cp != NULL)
+ *cp = '\0';
+
+ IN6_INADDR_TO_V4MAPPED(&nexthop_v4, &nexthop);
+ router_add_common(AF_INET, ifname, nexthop);
+}
+
+void
+router_add_common(int af, char *ifname, struct in6_addr nexthop)
+{
+ struct phyint_instance *pii;
+ struct phyint *pi;
+
+ if (debug & D_TARGET)
+ logdebug("router_add_common(%s %s)\n", AF_STR(af), ifname);
+
+ /*
+ * Retrieve the phyint instance; bail if it's not known to us yet.
+ */
+ pii = phyint_inst_lookup(af, ifname);
+ if (pii == NULL)
+ return;
+
+ /*
+ * Don't use our own addresses as targets.
+ */
+ if (own_address(pii->pii_af, nexthop))
+ return;
+
+ /*
+ * If the phyint is part a named group, then add the address to all
+ * members of the group; note that this is suboptimal in the IPv4 case
+ * as it has already been added to all matching interfaces in
+ * ire_process_v4(). Otherwise, add the address only to the phyint
+ * itself, since other phyints in the anongroup may not be on the same
+ * subnet.
+ */
+ pi = pii->pii_phyint;
+ if (pi->pi_group == phyint_anongroup) {
+ target_add(pii, nexthop, _B_TRUE);
+ } else {
+ pi = pi->pi_group->pg_phyint;
+ for (; pi != NULL; pi = pi->pi_pgnext)
+ target_add(PHYINT_INSTANCE(pi, af), nexthop, _B_TRUE);
+ }
+}
+
+/*
+ * Examine the IPv6 routing table, for default routers. For each default
+ * router, populate the list of targets of each phyint that is on the same
+ * link as the default router
+ */
+static void
+ire_process_v6(mib2_ipv6RouteEntry_t *buf, size_t len)
+{
+ mib2_ipv6RouteEntry_t *rp;
+ mib2_ipv6RouteEntry_t *endp;
+ struct in6_addr nexthop_v6;
+
+ if (debug & D_TARGET)
+ logdebug("ire_process_v6(len %d)\n", len);
+
+ if (len == 0)
+ return;
+
+ assert((len % sizeof (mib2_ipv6RouteEntry_t)) == 0);
+ endp = buf + (len / sizeof (mib2_ipv6RouteEntry_t));
+
+ /*
+ * Loop thru the routing table entries. Process any IRE_DEFAULT,
+ * IRE_PREFIX, IRE_HOST, IRE_HOST_REDIRECT ire. Ignore the others.
+ * For each such IRE_OFFSUBNET ire, get the nexthop gateway address.
+ * This is a potential target for probing, which we try to add
+ * to the list of probe targets.
+ */
+ for (rp = buf; rp < endp; rp++) {
+ if (!(rp->ipv6RouteInfo.re_ire_type & IRE_OFFSUBNET))
+ continue;
+
+ /*
+ * We have the outgoing interface in ipv6RouteIfIndex
+ * if ipv6RouteIfindex.o_length is non-zero. The outgoing
+ * interface must be present for link-local addresses. Since
+ * we use only link-local addreses for probing, we don't
+ * consider the case when the outgoing interface is not
+ * known and we need to scan interface ires
+ */
+ nexthop_v6 = rp->ipv6RouteNextHop;
+ if (rp->ipv6RouteIfIndex.o_length != 0) {
+ /*
+ * We already have the outgoing interface
+ * in ipv6RouteIfIndex.
+ */
+ router_add_v6(rp, nexthop_v6);
+ }
+ }
+}
+
+
+void
+router_add_v6(mib2_ipv6RouteEntry_t *rp1, struct in6_addr nexthop_v6)
+{
+ char ifname[LIFNAMSIZ + 1];
+ char *cp;
+ int len;
+
+ if (debug & D_TARGET)
+ logdebug("router_add_v6()\n");
+
+ len = MIN(rp1->ipv6RouteIfIndex.o_length, sizeof (ifname) - 1);
+ (void) memcpy(ifname, rp1->ipv6RouteIfIndex.o_bytes, len);
+ ifname[len] = '\0';
+
+ if (ifname[0] == '\0')
+ return;
+
+ cp = strchr(ifname, IF_SEPARATOR);
+ if (cp != NULL)
+ *cp = '\0';
+
+ router_add_common(AF_INET6, ifname, nexthop_v6);
+}
+
+
+
+/*
+ * Build a list of target routers, by scanning the routing tables.
+ * It is assumed that interface routes exist, to reach the routers.
+ */
+static void
+init_router_targets(void)
+{
+ struct target *tg;
+ struct target *next_tg;
+ struct phyint_instance *pii;
+ struct phyint *pi;
+
+ if (force_mcast)
+ return;
+
+ for (pii = phyint_instances; pii != NULL; pii = pii->pii_next) {
+ pi = pii->pii_phyint;
+ /*
+ * Exclude ptp and host targets. Set tg_in_use to false,
+ * only for router targets.
+ */
+ if (!pii->pii_targets_are_routers ||
+ (pi->pi_flags & IFF_POINTOPOINT))
+ continue;
+
+ for (tg = pii->pii_targets; tg != NULL; tg = tg->tg_next)
+ tg->tg_in_use = 0;
+ }
+
+ if (mibfd < 0) {
+ mibfd = open("/dev/ip", O_RDWR);
+ if (mibfd < 0) {
+ logperror("mibopen: ip open");
+ exit(1);
+ }
+ }
+
+ if (!update_router_list(mibfd)) {
+ (void) close(mibfd);
+ mibfd = -1;
+ }
+
+ for (pii = phyint_instances; pii != NULL; pii = pii->pii_next) {
+ if (!pii->pii_targets_are_routers ||
+ (pi->pi_flags & IFF_POINTOPOINT))
+ continue;
+
+ for (tg = pii->pii_targets; tg != NULL; tg = next_tg) {
+ next_tg = tg->tg_next;
+ if (!tg->tg_in_use) {
+ target_delete(tg);
+ }
+ }
+ }
+}
+
+/*
+ * Attempt to assign host targets to any interfaces that do not currently
+ * have probe targets by sharing targets with other interfaces in the group.
+ */
+static void
+init_host_targets(void)
+{
+ struct phyint_instance *pii;
+ struct phyint_group *pg;
+
+ for (pii = phyint_instances; pii != NULL; pii = pii->pii_next) {
+ pg = pii->pii_phyint->pi_group;
+ if (pg != phyint_anongroup && pii->pii_targets == NULL)
+ dup_host_targets(pii);
+ }
+}
+
+/*
+ * Duplicate host targets from other phyints of the group to
+ * the phyint instance 'desired_pii'.
+ */
+static void
+dup_host_targets(struct phyint_instance *desired_pii)
+{
+ int af;
+ struct phyint *pi;
+ struct phyint_instance *pii;
+ struct target *tg;
+
+ assert(desired_pii->pii_phyint->pi_group != phyint_anongroup);
+
+ af = desired_pii->pii_af;
+
+ /*
+ * For every phyint in the same group as desired_pii, check if
+ * it has any host targets. If so add them to desired_pii.
+ */
+ for (pi = desired_pii->pii_phyint; pi != NULL; pi = pi->pi_pgnext) {
+ pii = PHYINT_INSTANCE(pi, af);
+ /*
+ * We know that we don't have targets on this phyint instance
+ * since we have been called. But we still check for
+ * pii_targets_are_routers because another phyint instance
+ * could have router targets, since IFF_NOFAILOVER addresses
+ * on different phyint instances may belong to different
+ * subnets.
+ */
+ if ((pii == NULL) || (pii == desired_pii) ||
+ pii->pii_targets_are_routers)
+ continue;
+ for (tg = pii->pii_targets; tg != NULL; tg = tg->tg_next) {
+ target_create(desired_pii, tg->tg_address, _B_FALSE);
+ }
+ }
+}
+
+static void
+usage(char *cmd)
+{
+ (void) fprintf(stderr, "usage: %s\n", cmd);
+}
+
+
+#define MPATHD_DEFAULT_FILE "/etc/default/mpathd"
+
+/* Get an option from the /etc/default/mpathd file */
+static char *
+getdefault(char *name)
+{
+ char namebuf[BUFSIZ];
+ char *value = NULL;
+
+ if (defopen(MPATHD_DEFAULT_FILE) == 0) {
+ char *cp;
+ int flags;
+
+ /*
+ * ignore case
+ */
+ flags = defcntl(DC_GETFLAGS, 0);
+ TURNOFF(flags, DC_CASE);
+ (void) defcntl(DC_SETFLAGS, flags);
+
+ /* Add "=" to the name */
+ (void) strncpy(namebuf, name, sizeof (namebuf) - 2);
+ (void) strncat(namebuf, "=", 2);
+
+ if ((cp = defread(namebuf)) != NULL)
+ value = strdup(cp);
+
+ /* close */
+ (void) defopen((char *)NULL);
+ }
+ return (value);
+}
+
+
+/*
+ * Command line options below
+ */
+boolean_t failback_enabled = _B_TRUE; /* failback enabled/disabled */
+boolean_t track_all_phyints = _B_FALSE; /* option to track all NICs */
+static boolean_t adopt = _B_FALSE;
+static boolean_t foreground = _B_FALSE;
+
+int
+main(int argc, char *argv[])
+{
+ int i;
+ int c;
+ struct phyint_instance *pii;
+ char *value;
+
+ argv0 = argv; /* Saved for re-exec on SIGHUP */
+ srandom(gethostid()); /* Initialize the random number generator */
+
+ /*
+ * NOTE: The messages output by in.mpathd are not suitable for
+ * translation, so we do not call textdomain().
+ */
+ (void) setlocale(LC_ALL, "");
+
+ /*
+ * Get the user specified value of 'failure detection time'
+ * from /etc/default/mpathd
+ */
+ value = getdefault("FAILURE_DETECTION_TIME");
+ if (value != NULL) {
+ user_failure_detection_time =
+ (int)strtol((char *)value, NULL, 0);
+
+ if (user_failure_detection_time <= 0) {
+ user_failure_detection_time = FAILURE_DETECTION_TIME;
+ logerr("Invalid failure detection time %s, assuming "
+ "default %d\n", value, user_failure_detection_time);
+
+ } else if (user_failure_detection_time <
+ MIN_FAILURE_DETECTION_TIME) {
+ user_failure_detection_time =
+ MIN_FAILURE_DETECTION_TIME;
+ logerr("Too small failure detection time of %s, "
+ "assuming minimum %d\n", value,
+ user_failure_detection_time);
+ }
+ free(value);
+ } else {
+ /* User has not specified the parameter, Use default value */
+ user_failure_detection_time = FAILURE_DETECTION_TIME;
+ }
+
+ /*
+ * This gives the frequency at which probes will be sent.
+ * When fdt ms elapses, we should be able to determine
+ * whether 5 consecutive probes have failed or not.
+ * 1 probe will be sent in every user_probe_interval ms,
+ * randomly anytime in the (0.5 - 1.0) 2nd half of every
+ * user_probe_interval. Thus when we send out probe 'n' we
+ * can be sure that probe 'n - 2' is lost, if we have not
+ * got the ack. (since the probe interval is > crtt). But
+ * probe 'n - 1' may be a valid unacked probe, since the
+ * time between 2 successive probes could be as small as
+ * 0.5 * user_probe_interval. Hence the NUM_PROBE_FAILS + 2
+ */
+ user_probe_interval = user_failure_detection_time /
+ (NUM_PROBE_FAILS + 2);
+
+ /*
+ * Get the user specified value of failback_enabled from
+ * /etc/default/mpathd
+ */
+ value = getdefault("FAILBACK");
+ if (value != NULL) {
+ if (strncasecmp(value, "yes", 3) == 0)
+ failback_enabled = _B_TRUE;
+ else if (strncasecmp(value, "no", 2) == 0)
+ failback_enabled = _B_FALSE;
+ else
+ logerr("Invalid value for FAILBACK %s\n", value);
+ free(value);
+ } else {
+ failback_enabled = _B_TRUE;
+ }
+
+ /*
+ * Get the user specified value of track_all_phyints from
+ * /etc/default/mpathd. The sense is reversed in
+ * TRACK_INTERFACES_ONLY_WITH_GROUPS.
+ */
+ value = getdefault("TRACK_INTERFACES_ONLY_WITH_GROUPS");
+ if (value != NULL) {
+ if (strncasecmp(value, "yes", 3) == 0)
+ track_all_phyints = _B_FALSE;
+ else if (strncasecmp(value, "no", 2) == 0)
+ track_all_phyints = _B_TRUE;
+ else
+ logerr("Invalid value for "
+ "TRACK_INTERFACES_ONLY_WITH_GROUPS %s\n", value);
+ free(value);
+ } else {
+ track_all_phyints = _B_FALSE;
+ }
+
+ while ((c = getopt(argc, argv, "adD:ml")) != EOF) {
+ switch (c) {
+ case 'a':
+ adopt = _B_TRUE;
+ break;
+ case 'm':
+ force_mcast = _B_TRUE;
+ break;
+ case 'd':
+ debug = D_ALL;
+ foreground = _B_TRUE;
+ break;
+ case 'D':
+ i = (int)strtol(optarg, NULL, 0);
+ if (i == 0) {
+ (void) fprintf(stderr, "Bad debug flags: %s\n",
+ optarg);
+ exit(1);
+ }
+ debug |= i;
+ foreground = _B_TRUE;
+ break;
+ case 'l':
+ /*
+ * Turn off link state notification handling.
+ * Undocumented command line flag, for debugging
+ * purposes.
+ */
+ handle_link_notifications = _B_FALSE;
+ break;
+ default:
+ usage(argv[0]);
+ exit(1);
+ }
+ }
+
+ /*
+ * The sockets for the loopback command interface should be listening
+ * before we fork and exit in daemonize(). This way, whoever started us
+ * can use the loopback interface as soon as they get a zero exit
+ * status.
+ */
+ lsock_v4 = setup_listener(AF_INET);
+ lsock_v6 = setup_listener(AF_INET6);
+
+ if (lsock_v4 < 0 && lsock_v6 < 0) {
+ logerr("main: setup_listener failed for both IPv4 and IPv6\n");
+ exit(1);
+ }
+
+ if (!foreground) {
+ if (!daemonize()) {
+ logerr("cannot daemonize\n");
+ exit(EXIT_FAILURE);
+ }
+ initlog();
+ }
+
+ /*
+ * Initializations:
+ * 1. Create ifsock* sockets. These are used for performing SIOC*
+ * ioctls. We have 2 sockets 1 each for IPv4 and IPv6.
+ * 2. Initialize a pipe for handling/recording signal events.
+ * 3. Create the routing sockets, used for listening
+ * to routing / interface changes.
+ * 4. phyint_init() - Initialize physical interface state
+ * (in mpd_tables.c). Must be done before creating interfaces,
+ * which timer_init() does indirectly.
+ * 5. timer_init() - Initialize timer related stuff
+ * 6. initifs() - Initialize our database of all known interfaces
+ * 7. init_router_targets() - Initialize our database of all known
+ * router targets.
+ */
+ ifsock_v4 = socket(AF_INET, SOCK_DGRAM, 0);
+ if (ifsock_v4 < 0) {
+ logperror("main: IPv4 socket open");
+ exit(1);
+ }
+
+ ifsock_v6 = socket(AF_INET6, SOCK_DGRAM, 0);
+ if (ifsock_v6 < 0) {
+ logperror("main: IPv6 socket open");
+ exit(1);
+ }
+
+ setup_eventpipe();
+
+ rtsock_v4 = setup_rtsock(AF_INET);
+ rtsock_v6 = setup_rtsock(AF_INET6);
+
+ if (phyint_init() == -1) {
+ logerr("cannot initialize physical interface structures");
+ exit(1);
+ }
+
+ timer_init();
+
+ initifs();
+
+ /*
+ * If we're operating in "adopt" mode and no interfaces need to be
+ * tracked, shut down (ifconfig(1M) will restart us on demand if
+ * interfaces are subsequently put into multipathing groups).
+ */
+ if (adopt && phyint_instances == NULL)
+ exit(0);
+
+ /*
+ * Main body. Keep listening for activity on any of the sockets
+ * that we are monitoring and take appropriate action as necessary.
+ * signals are also handled synchronously.
+ */
+ for (;;) {
+ if (poll(pollfds, pollfd_num, -1) < 0) {
+ if (errno == EINTR)
+ continue;
+ logperror("main: poll");
+ exit(1);
+ }
+ for (i = 0; i < pollfd_num; i++) {
+ if ((pollfds[i].fd == -1) ||
+ !(pollfds[i].revents & POLLIN))
+ continue;
+ if (pollfds[i].fd == eventpipe_read) {
+ in_signal(eventpipe_read);
+ break;
+ }
+ if (pollfds[i].fd == rtsock_v4 ||
+ pollfds[i].fd == rtsock_v6) {
+ process_rtsock(rtsock_v4, rtsock_v6);
+ break;
+ }
+ for (pii = phyint_instances; pii != NULL;
+ pii = pii->pii_next) {
+ if (pollfds[i].fd == pii->pii_probe_sock) {
+ if (pii->pii_af == AF_INET)
+ in_data(pii);
+ else
+ in6_data(pii);
+ break;
+ }
+ }
+ if (pollfds[i].fd == lsock_v4)
+ loopback_cmd(lsock_v4, AF_INET);
+ else if (pollfds[i].fd == lsock_v6)
+ loopback_cmd(lsock_v6, AF_INET6);
+ }
+ if (full_scan_required) {
+ initifs();
+ full_scan_required = _B_FALSE;
+ }
+ }
+ /* NOTREACHED */
+ return (EXIT_SUCCESS);
+}
+
+static int
+setup_listener(int af)
+{
+ int sock;
+ int on;
+ int len;
+ int ret;
+ struct sockaddr_storage laddr;
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+ struct in6_addr loopback_addr = IN6ADDR_LOOPBACK_INIT;
+
+ assert(af == AF_INET || af == AF_INET6);
+
+ sock = socket(af, SOCK_STREAM, 0);
+ if (sock < 0) {
+ logperror("setup_listener: socket");
+ exit(1);
+ }
+
+ on = 1;
+ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&on,
+ sizeof (on)) < 0) {
+ logperror("setup_listener: setsockopt (SO_REUSEADDR)");
+ exit(1);
+ }
+
+ bzero(&laddr, sizeof (laddr));
+ laddr.ss_family = af;
+
+ if (af == AF_INET) {
+ sin = (struct sockaddr_in *)&laddr;
+ sin->sin_port = htons(MPATHD_PORT);
+ sin->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ len = sizeof (struct sockaddr_in);
+ } else {
+ sin6 = (struct sockaddr_in6 *)&laddr;
+ sin6->sin6_port = htons(MPATHD_PORT);
+ sin6->sin6_addr = loopback_addr;
+ len = sizeof (struct sockaddr_in6);
+ }
+
+ ret = bind(sock, (struct sockaddr *)&laddr, len);
+ if (ret < 0) {
+ if (errno == EADDRINUSE) {
+ /*
+ * Another instance of mpathd may be already active.
+ */
+ logerr("main: is another instance of in.mpathd "
+ "already active?\n");
+ exit(1);
+ } else {
+ (void) close(sock);
+ return (-1);
+ }
+ }
+ if (listen(sock, 30) < 0) {
+ logperror("main: listen");
+ exit(1);
+ }
+ if (poll_add(sock) == -1) {
+ (void) close(sock);
+ exit(1);
+ }
+
+ return (sock);
+}
+
+/*
+ * Table of commands and their expected size; used by loopback_cmd().
+ */
+static struct {
+ const char *name;
+ unsigned int size;
+} commands[] = {
+ { "MI_PING", sizeof (uint32_t) },
+ { "MI_OFFLINE", sizeof (mi_offline_t) },
+ { "MI_UNDO_OFFLINE", sizeof (mi_undo_offline_t) },
+ { "MI_SETOINDEX", sizeof (mi_setoindex_t) },
+ { "MI_QUERY", sizeof (mi_query_t) }
+};
+
+/*
+ * Commands received over the loopback interface come here. Currently
+ * the agents that send commands are ifconfig, if_mpadm and the RCM IPMP
+ * module. ifconfig only makes a connection, and closes it to check if
+ * in.mpathd is running.
+ * if_mpadm sends commands in the format specified by the mpathd_interface
+ * structure.
+ */
+static void
+loopback_cmd(int sock, int family)
+{
+ int newfd;
+ ssize_t len;
+ struct sockaddr_storage peer;
+ struct sockaddr_in *peer_sin;
+ struct sockaddr_in6 *peer_sin6;
+ socklen_t peerlen;
+ union mi_commands mpi;
+ struct in6_addr loopback_addr = IN6ADDR_LOOPBACK_INIT;
+ char abuf[INET6_ADDRSTRLEN];
+ uint_t cmd;
+ int retval;
+
+ peerlen = sizeof (peer);
+ newfd = accept(sock, (struct sockaddr *)&peer, &peerlen);
+ if (newfd < 0) {
+ logperror("loopback_cmd: accept");
+ return;
+ }
+
+ switch (family) {
+ case AF_INET:
+ /*
+ * Validate the address and port to make sure that
+ * non privileged processes don't connect and start
+ * talking to us.
+ */
+ if (peerlen != sizeof (struct sockaddr_in)) {
+ logerr("loopback_cmd: AF_INET peerlen %d\n", peerlen);
+ (void) close(newfd);
+ return;
+ }
+ peer_sin = (struct sockaddr_in *)&peer;
+ if ((ntohs(peer_sin->sin_port) >= IPPORT_RESERVED) ||
+ (ntohl(peer_sin->sin_addr.s_addr) != INADDR_LOOPBACK)) {
+ (void) inet_ntop(AF_INET, &peer_sin->sin_addr.s_addr,
+ abuf, sizeof (abuf));
+ logerr("Attempt to connect from addr %s port %d\n",
+ abuf, ntohs(peer_sin->sin_port));
+ (void) close(newfd);
+ return;
+ }
+ break;
+
+ case AF_INET6:
+ if (peerlen != sizeof (struct sockaddr_in6)) {
+ logerr("loopback_cmd: AF_INET6 peerlen %d\n", peerlen);
+ (void) close(newfd);
+ return;
+ }
+ /*
+ * Validate the address and port to make sure that
+ * non privileged processes don't connect and start
+ * talking to us.
+ */
+ peer_sin6 = (struct sockaddr_in6 *)&peer;
+ if ((ntohs(peer_sin6->sin6_port) >= IPPORT_RESERVED) ||
+ (!IN6_ARE_ADDR_EQUAL(&peer_sin6->sin6_addr,
+ &loopback_addr))) {
+ (void) inet_ntop(AF_INET6, &peer_sin6->sin6_addr, abuf,
+ sizeof (abuf));
+ logerr("Attempt to connect from addr %s port %d\n",
+ abuf, ntohs(peer_sin6->sin6_port));
+ (void) close(newfd);
+ return;
+ }
+
+ default:
+ logdebug("loopback_cmd: family %d\n", family);
+ (void) close(newfd);
+ return;
+ }
+
+ /*
+ * The sizeof the 'mpi' buffer corresponds to the maximum size of
+ * all supported commands
+ */
+ len = read(newfd, &mpi, sizeof (mpi));
+
+ /*
+ * ifconfig does not send any data. Just tests to see if mpathd
+ * is already running.
+ */
+ if (len <= 0) {
+ (void) close(newfd);
+ return;
+ }
+
+ /*
+ * In theory, we can receive any sized message for a stream socket,
+ * but we don't expect that to happen for a small message over a
+ * loopback connection.
+ */
+ if (len < sizeof (uint32_t)) {
+ logerr("loopback_cmd: bad command format or read returns "
+ "partial data %d\n", len);
+ }
+
+ cmd = mpi.mi_command;
+ if (cmd >= MI_NCMD) {
+ logerr("loopback_cmd: unknown command id `%d'\n", cmd);
+ (void) close(newfd);
+ return;
+ }
+
+ if (len < commands[cmd].size) {
+ logerr("loopback_cmd: short %s command (expected %d, got %d)\n",
+ commands[cmd].name, commands[cmd].size, len);
+ (void) close(newfd);
+ return;
+ }
+
+ retval = process_cmd(newfd, &mpi);
+ if (retval != IPMP_SUCCESS) {
+ logerr("failed processing %s: %s\n", commands[cmd].name,
+ ipmp_errmsg(retval));
+ }
+ (void) close(newfd);
+}
+
+extern int global_errno; /* set by failover() or failback() */
+
+/*
+ * Process the offline, undo offline and set original index commands,
+ * received from if_mpadm(1M)
+ */
+static unsigned int
+process_cmd(int newfd, union mi_commands *mpi)
+{
+ uint_t nif = 0;
+ uint32_t cmd;
+ struct phyint *pi;
+ struct phyint *pi2;
+ struct phyint_group *pg;
+ boolean_t success;
+ int error;
+ struct mi_offline *mio;
+ struct mi_undo_offline *miu;
+ struct lifreq lifr;
+ int ifsock;
+ struct mi_setoindex *mis;
+
+ cmd = mpi->mi_command;
+
+ switch (cmd) {
+ case MI_OFFLINE:
+ mio = &mpi->mi_ocmd;
+ /*
+ * Lookup the interface that needs to be offlined.
+ * If it does not exist, return a suitable error.
+ */
+ pi = phyint_lookup(mio->mio_ifname);
+ if (pi == NULL)
+ return (send_result(newfd, IPMP_FAILURE, EINVAL));
+
+ /*
+ * Verify that the minimum redundancy requirements are met.
+ * The multipathing group must have at least the specified
+ * number of functional interfaces after offlining the
+ * requested interface. Otherwise return a suitable error.
+ */
+ pg = pi->pi_group;
+ nif = 0;
+ if (pg != phyint_anongroup) {
+ for (nif = 0, pi2 = pg->pg_phyint; pi2 != NULL;
+ pi2 = pi2->pi_pgnext) {
+ if ((pi2->pi_state == PI_RUNNING) ||
+ (pg->pg_groupfailed &&
+ !(pi2->pi_flags & IFF_OFFLINE)))
+ nif++;
+ }
+ }
+ if (nif < mio->mio_min_redundancy)
+ return (send_result(newfd, IPMP_EMINRED, 0));
+
+ /*
+ * The order of operation is to set IFF_OFFLINE, followed by
+ * failover. Setting IFF_OFFLINE ensures that no new ipif's
+ * can be created. Subsequent failover moves everything on
+ * the OFFLINE interface to some other functional interface.
+ */
+ success = change_lif_flags(pi, IFF_OFFLINE, _B_TRUE);
+ if (success) {
+ if (!pi->pi_empty) {
+ error = try_failover(pi, FAILOVER_NORMAL);
+ if (error != 0) {
+ if (!change_lif_flags(pi, IFF_OFFLINE,
+ _B_FALSE)) {
+ logerr("process_cmd: couldn't"
+ " clear OFFLINE flag on"
+ " %s\n", pi->pi_name);
+ /*
+ * Offline interfaces should
+ * not be probed.
+ */
+ stop_probing(pi);
+ }
+ return (send_result(newfd, error,
+ global_errno));
+ }
+ }
+ } else {
+ return (send_result(newfd, IPMP_FAILURE, errno));
+ }
+
+ /*
+ * The interface is now Offline, so stop probing it.
+ * Note that if_mpadm(1M) will down the test addresses,
+ * after receiving a success reply from us. The routing
+ * socket message will then make us close the socket used
+ * for sending probes. But it is more logical that an
+ * offlined interface must not be probed, even if it has
+ * test addresses.
+ */
+ stop_probing(pi);
+ return (send_result(newfd, IPMP_SUCCESS, 0));
+
+ case MI_UNDO_OFFLINE:
+ miu = &mpi->mi_ucmd;
+ /*
+ * Undo the offline command. As usual lookup the interface.
+ * Send an error if it does not exist.
+ */
+ pi = phyint_lookup(miu->miu_ifname);
+ if (pi == NULL)
+ return (send_result(newfd, IPMP_FAILURE, EINVAL));
+
+ /*
+ * Inverse of the offline operation. Do a failback, and then
+ * clear the IFF_OFFLINE flag.
+ */
+ error = do_failback(pi, _B_TRUE);
+ if (error == IPMP_EFBPARTIAL)
+ return (send_result(newfd, IPMP_EFBPARTIAL, 0));
+ error = do_failback(pi, _B_FALSE);
+
+ switch (error) {
+ case IPMP_SUCCESS:
+ if (!change_lif_flags(pi, IFF_OFFLINE, _B_FALSE)) {
+ logdebug("undo error %X\n", global_errno);
+ error = IPMP_FAILURE;
+ break;
+ }
+ /* FALLTHROUGH */
+
+ case IPMP_EFBPARTIAL:
+ /*
+ * Reset the state of the interface based on the
+ * current link state; if this phyint subsequently
+ * acquires a test address, the state will be changed
+ * again later as a result of the probes.
+ */
+ if (LINK_UP(pi))
+ phyint_chstate(pi, PI_RUNNING);
+ else
+ phyint_chstate(pi, PI_FAILED);
+ break;
+
+ case IPMP_FAILURE:
+ break;
+
+ default:
+ logdebug("do_failback: unexpected return value\n");
+ break;
+ }
+ return (send_result(newfd, error, global_errno));
+
+ case MI_SETOINDEX:
+ mis = &mpi->mi_scmd;
+
+ /* Get the socket for doing ioctls */
+ ifsock = (mis->mis_iftype == AF_INET) ? ifsock_v4 : ifsock_v6;
+
+ /*
+ * Get index of new original interface.
+ * The index is returned in lifr.lifr_index.
+ */
+ (void) strlcpy(lifr.lifr_name, mis->mis_new_pifname,
+ sizeof (lifr.lifr_name));
+
+ if (ioctl(ifsock, SIOCGLIFINDEX, (char *)&lifr) < 0)
+ return (send_result(newfd, IPMP_FAILURE, errno));
+
+ /*
+ * Set new original interface index.
+ * The new index was put into lifr.lifr_index by the
+ * SIOCGLIFINDEX ioctl.
+ */
+ (void) strlcpy(lifr.lifr_name, mis->mis_lifname,
+ sizeof (lifr.lifr_name));
+
+ if (ioctl(ifsock, SIOCSLIFOINDEX, (char *)&lifr) < 0)
+ return (send_result(newfd, IPMP_FAILURE, errno));
+
+ return (send_result(newfd, IPMP_SUCCESS, 0));
+
+ case MI_QUERY:
+ return (process_query(newfd, &mpi->mi_qcmd));
+
+ default:
+ break;
+ }
+
+ return (send_result(newfd, IPMP_EPROTO, 0));
+}
+
+/*
+ * Process the query request pointed to by `miq' and send a reply on file
+ * descriptor `fd'. Returns an IPMP error code.
+ */
+static unsigned int
+process_query(int fd, mi_query_t *miq)
+{
+ ipmp_groupinfo_t *grinfop;
+ ipmp_groupinfolist_t *grlp;
+ ipmp_grouplist_t *grlistp;
+ ipmp_ifinfo_t *ifinfop;
+ ipmp_ifinfolist_t *iflp;
+ ipmp_snap_t *snap;
+ unsigned int retval;
+
+ switch (miq->miq_inforeq) {
+ case IPMP_GROUPLIST:
+ retval = getgrouplist(&grlistp);
+ if (retval != IPMP_SUCCESS)
+ return (send_result(fd, retval, errno));
+
+ retval = send_result(fd, IPMP_SUCCESS, 0);
+ if (retval == IPMP_SUCCESS)
+ retval = send_grouplist(fd, grlistp);
+
+ ipmp_freegrouplist(grlistp);
+ return (retval);
+
+ case IPMP_GROUPINFO:
+ miq->miq_grname[LIFGRNAMSIZ - 1] = '\0';
+ retval = getgroupinfo(miq->miq_ifname, &grinfop);
+ if (retval != IPMP_SUCCESS)
+ return (send_result(fd, retval, errno));
+
+ retval = send_result(fd, IPMP_SUCCESS, 0);
+ if (retval == IPMP_SUCCESS)
+ retval = send_groupinfo(fd, grinfop);
+
+ ipmp_freegroupinfo(grinfop);
+ return (retval);
+
+ case IPMP_IFINFO:
+ miq->miq_ifname[LIFNAMSIZ - 1] = '\0';
+ retval = getifinfo(miq->miq_ifname, &ifinfop);
+ if (retval != IPMP_SUCCESS)
+ return (send_result(fd, retval, errno));
+
+ retval = send_result(fd, IPMP_SUCCESS, 0);
+ if (retval == IPMP_SUCCESS)
+ retval = send_ifinfo(fd, ifinfop);
+
+ ipmp_freeifinfo(ifinfop);
+ return (retval);
+
+ case IPMP_SNAP:
+ retval = getsnap(&snap);
+ if (retval != IPMP_SUCCESS)
+ return (send_result(fd, retval, errno));
+
+ retval = send_result(fd, IPMP_SUCCESS, 0);
+ if (retval != IPMP_SUCCESS)
+ goto out;
+
+ retval = ipmp_writetlv(fd, IPMP_SNAP, sizeof (*snap), snap);
+ if (retval != IPMP_SUCCESS)
+ goto out;
+
+ retval = send_grouplist(fd, snap->sn_grlistp);
+ if (retval != IPMP_SUCCESS)
+ goto out;
+
+ iflp = snap->sn_ifinfolistp;
+ for (; iflp != NULL; iflp = iflp->ifl_next) {
+ retval = send_ifinfo(fd, iflp->ifl_ifinfop);
+ if (retval != IPMP_SUCCESS)
+ goto out;
+ }
+
+ grlp = snap->sn_grinfolistp;
+ for (; grlp != NULL; grlp = grlp->grl_next) {
+ retval = send_groupinfo(fd, grlp->grl_grinfop);
+ if (retval != IPMP_SUCCESS)
+ goto out;
+ }
+ out:
+ ipmp_snap_free(snap);
+ return (retval);
+
+ default:
+ break;
+
+ }
+ return (send_result(fd, IPMP_EPROTO, 0));
+}
+
+/*
+ * Send the group information pointed to by `grinfop' on file descriptor `fd'.
+ * Returns an IPMP error code.
+ */
+static unsigned int
+send_groupinfo(int fd, ipmp_groupinfo_t *grinfop)
+{
+ ipmp_iflist_t *iflistp = grinfop->gr_iflistp;
+ unsigned int retval;
+
+ retval = ipmp_writetlv(fd, IPMP_GROUPINFO, sizeof (*grinfop), grinfop);
+ if (retval != IPMP_SUCCESS)
+ return (retval);
+
+ return (ipmp_writetlv(fd, IPMP_IFLIST,
+ IPMP_IFLIST_SIZE(iflistp->il_nif), iflistp));
+}
+
+/*
+ * Send the interface information pointed to by `ifinfop' on file descriptor
+ * `fd'. Returns an IPMP error code.
+ */
+static unsigned int
+send_ifinfo(int fd, ipmp_ifinfo_t *ifinfop)
+{
+ return (ipmp_writetlv(fd, IPMP_IFINFO, sizeof (*ifinfop), ifinfop));
+}
+
+/*
+ * Send the group list pointed to by `grlistp' on file descriptor `fd'.
+ * Returns an IPMP error code.
+ */
+static unsigned int
+send_grouplist(int fd, ipmp_grouplist_t *grlistp)
+{
+ return (ipmp_writetlv(fd, IPMP_GROUPLIST,
+ IPMP_GROUPLIST_SIZE(grlistp->gl_ngroup), grlistp));
+}
+
+/*
+ * Initialize an mi_result_t structure using `error' and `syserror' and
+ * send it on file descriptor `fd'. Returns an IPMP error code.
+ */
+static unsigned int
+send_result(int fd, unsigned int error, int syserror)
+{
+ mi_result_t me;
+
+ me.me_mpathd_error = error;
+ if (error == IPMP_FAILURE)
+ me.me_sys_error = syserror;
+ else
+ me.me_sys_error = 0;
+
+ return (ipmp_write(fd, &me, sizeof (me)));
+}
+
+/*
+ * Daemonize the process.
+ */
+static boolean_t
+daemonize(void)
+{
+ switch (fork()) {
+ case -1:
+ return (_B_FALSE);
+
+ case 0:
+ /*
+ * Lose our controlling terminal, and become both a session
+ * leader and a process group leader.
+ */
+ if (setsid() == -1)
+ return (_B_FALSE);
+
+ /*
+ * Under POSIX, a session leader can accidentally (through
+ * open(2)) acquire a controlling terminal if it does not
+ * have one. Just to be safe, fork() again so we are not a
+ * session leader.
+ */
+ switch (fork()) {
+ case -1:
+ return (_B_FALSE);
+
+ case 0:
+ (void) chdir("/");
+ (void) umask(022);
+ (void) fdwalk(closefunc, NULL);
+ break;
+
+ default:
+ _exit(EXIT_SUCCESS);
+ }
+ break;
+
+ default:
+ _exit(EXIT_SUCCESS);
+ }
+
+ return (_B_TRUE);
+}
+
+/*
+ * The parent has created some fds before forking on purpose, keep them open.
+ */
+static int
+closefunc(void *not_used, int fd)
+/* ARGSUSED */
+{
+ if (fd != lsock_v4 && fd != lsock_v6)
+ (void) close(fd);
+ return (0);
+}
+
+/* LOGGER */
+
+#include <syslog.h>
+
+/*
+ * Logging routines. All routines log to syslog, unless the daemon is
+ * running in the foreground, in which case the logging goes to stderr.
+ *
+ * The following routines are available:
+ *
+ * logdebug(): A printf-like function for outputting debug messages
+ * (messages at LOG_DEBUG) that are only of use to developers.
+ *
+ * logtrace(): A printf-like function for outputting tracing messages
+ * (messages at LOG_INFO) from the daemon. This is typically used
+ * to log the receipt of interesting network-related conditions.
+ *
+ * logerr(): A printf-like function for outputting error messages
+ * (messages at LOG_ERR) from the daemon.
+ *
+ * logperror*(): A set of functions used to output error messages
+ * (messages at LOG_ERR); these automatically append strerror(errno)
+ * and a newline to the message passed to them.
+ *
+ * NOTE: since the logging functions write to syslog, the messages passed
+ * to them are not eligible for localization. Thus, gettext() must
+ * *not* be used.
+ */
+
+static int logging = 0;
+
+static void
+initlog(void)
+{
+ logging++;
+ openlog("in.mpathd", LOG_PID | LOG_CONS, LOG_DAEMON);
+}
+
+/* PRINTFLIKE1 */
+void
+logerr(char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ if (logging)
+ vsyslog(LOG_ERR, fmt, ap);
+ else
+ (void) vfprintf(stderr, fmt, ap);
+ va_end(ap);
+}
+
+/* PRINTFLIKE1 */
+void
+logtrace(char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ if (logging)
+ vsyslog(LOG_INFO, fmt, ap);
+ else
+ (void) vfprintf(stderr, fmt, ap);
+ va_end(ap);
+}
+
+/* PRINTFLIKE1 */
+void
+logdebug(char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ if (logging)
+ vsyslog(LOG_DEBUG, fmt, ap);
+ else
+ (void) vfprintf(stderr, fmt, ap);
+ va_end(ap);
+}
+
+/* PRINTFLIKE1 */
+void
+logperror(char *str)
+{
+ if (logging)
+ syslog(LOG_ERR, "%s: %m\n", str);
+ else
+ (void) fprintf(stderr, "%s: %s\n", str, strerror(errno));
+}
+
+void
+logperror_pii(struct phyint_instance *pii, char *str)
+{
+ if (logging) {
+ syslog(LOG_ERR, "%s (%s %s): %m\n",
+ str, AF_STR(pii->pii_af), pii->pii_phyint->pi_name);
+ } else {
+ (void) fprintf(stderr, "%s (%s %s): %s\n",
+ str, AF_STR(pii->pii_af), pii->pii_phyint->pi_name,
+ strerror(errno));
+ }
+}
+
+void
+logperror_li(struct logint *li, char *str)
+{
+ struct phyint_instance *pii = li->li_phyint_inst;
+
+ if (logging) {
+ syslog(LOG_ERR, "%s (%s %s): %m\n",
+ str, AF_STR(pii->pii_af), li->li_name);
+ } else {
+ (void) fprintf(stderr, "%s (%s %s): %s\n",
+ str, AF_STR(pii->pii_af), li->li_name,
+ strerror(errno));
+ }
+}
+
+void
+close_probe_socket(struct phyint_instance *pii, boolean_t polled)
+{
+ if (polled)
+ (void) poll_remove(pii->pii_probe_sock);
+ (void) close(pii->pii_probe_sock);
+ pii->pii_probe_sock = -1;
+ pii->pii_basetime_inited = 0;
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.mpathd/mpd_probe.c b/usr/src/cmd/cmd-inet/usr.lib/in.mpathd/mpd_probe.c
new file mode 100644
index 0000000000..5b6c15f395
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.mpathd/mpd_probe.c
@@ -0,0 +1,2966 @@
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 1987 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "mpd_defs.h"
+#include "mpd_tables.h"
+
+/*
+ * Probe types for probe()
+ */
+#define PROBE_UNI 0x1234 /* Unicast probe packet */
+#define PROBE_MULTI 0x5678 /* Multicast probe packet */
+#define PROBE_RTT 0x9abc /* RTT only probe packet */
+
+#define MSEC_PERMIN (60 * MILLISEC) /* Number of milliseconds in a minute */
+
+/*
+ * Format of probe / probe response packets. This is an ICMP Echo request
+ * or ICMP Echo reply. Packet format is same for both IPv4 and IPv6
+ */
+struct pr_icmp
+{
+ uint8_t pr_icmp_type; /* type field */
+ uint8_t pr_icmp_code; /* code field */
+ uint16_t pr_icmp_cksum; /* checksum field */
+ uint16_t pr_icmp_id; /* Identification */
+ uint16_t pr_icmp_seq; /* sequence number */
+ uint32_t pr_icmp_timestamp; /* Time stamp */
+ uint32_t pr_icmp_mtype; /* Message type */
+};
+
+static struct in6_addr all_nodes_mcast_v6 = { { 0xff, 0x2, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x1 } };
+
+static struct in_addr all_nodes_mcast_v4 = { { { 0xe0, 0x0, 0x0, 0x1 } } };
+
+static hrtime_t last_fdt_bumpup_time; /* When FDT was bumped up last */
+
+static void *find_ancillary(struct msghdr *msg, int cmsg_type);
+static void pi_set_crtt(struct target *tg, int m,
+ boolean_t is_probe_uni);
+static void incoming_echo_reply(struct phyint_instance *pii,
+ struct pr_icmp *reply, struct in6_addr fromaddr);
+static void incoming_rtt_reply(struct phyint_instance *pii,
+ struct pr_icmp *reply, struct in6_addr fromaddr);
+static void incoming_mcast_reply(struct phyint_instance *pii,
+ struct pr_icmp *reply, struct in6_addr fromaddr);
+
+static boolean_t check_pg_crtt_improved(struct phyint_group *pg);
+static boolean_t check_pii_crtt_improved(struct phyint_instance *pii);
+static boolean_t check_exception_target(struct phyint_instance *pii,
+ struct target *target);
+static void probe_fail_info(struct phyint_instance *pii,
+ struct target *cur_tg, struct probe_fail_count *pfinfo);
+static void probe_success_info(struct phyint_instance *pii,
+ struct target *cur_tg, struct probe_success_count *psinfo);
+static boolean_t phyint_repaired(struct phyint *pi);
+
+static int failover(struct phyint *from, struct phyint *to);
+static int failback(struct phyint *from, struct phyint *to);
+static struct phyint *get_failover_dst(struct phyint *pi, int failover_type);
+
+static boolean_t highest_ack_tg(uint16_t seq, struct target *tg);
+static int in_cksum(ushort_t *addr, int len);
+static void reset_snxt_basetimes(void);
+
+/*
+ * CRTT - Conservative Round Trip Time Estimate
+ * Probe success - A matching probe reply received before CRTT ms has elapsed
+ * after sending the probe.
+ * Probe failure - No probe reply received and more than CRTT ms has elapsed
+ * after sending the probe.
+ *
+ * TLS - Time last success. Most recent probe ack received at this time.
+ * TFF - Time first fail. The time of the earliest probe failure in
+ * a consecutive series of probe failures.
+ * NUM_PROBE_REPAIRS - Number of consecutive successful probes required
+ * before declaring phyint repair.
+ * NUM_PROBE_FAILS - Number of consecutive probe failures required to
+ * declare a phyint failure.
+ *
+ * Phyint state diagram
+ *
+ * The state of a phyint that is capable of being probed, is completely
+ * specified by the 5-tuple <pi_state, pg_groupfailed, I, pi_empty, pi_full>.
+ *
+ * A phyint starts in either PI_RUNNING or PI_FAILED, depending on the state
+ * of the link (according to the driver). If the phyint is also configured
+ * with a test address (the common case) and probe targets, then a phyint must
+ * also successfully be able to send and receive probes in order to remain in
+ * the PI_RUNNING state (otherwise, it transitions to PI_FAILED).
+ *
+ * Further, if a PI_RUNNING phyint is configured with a test address but is
+ * unable to find any probe targets, it will transition to the PI_NOTARGETS
+ * state, which indicates that the link is apparently functional but that
+ * in.mpathd is unable to send probes to verify functionality (in this case,
+ * in.mpathd makes the optimistic assumption that the interface is working
+ * correctly and thus does not perform a failover, but reports the interface
+ * as IPMP_IF_UNKNOWN through the async events and query interfaces).
+ *
+ * At any point, a phyint may be administratively marked offline via if_mpadm.
+ * In this case, the interface always transitions to PI_OFFLINE, regardless
+ * of its previous state. When the interface is later brought back online,
+ * in.mpathd acts as if the interface is new (and thus it transitions to
+ * PI_RUNNING or PI_FAILED based on the status of the link and the result of
+ * its probes, if probes are sent).
+ *
+ * pi_state - PI_RUNNING or PI_FAILED
+ * PI_RUNNING: The failure detection logic says the phyint is good.
+ * PI_FAILED: The failure detection logic says the phyint has failed.
+ *
+ * pg_groupfailed - Group failure, all interfaces in the group have failed.
+ * The pi_state may be either PI_FAILED or PI_NOTARGETS.
+ * In the case of router targets, we assume that the current list of
+ * targets obtained from the routing table, is still valid, so the
+ * phyint stat is PI_FAILED. In the case of host targets, we delete the
+ * list of targets, and multicast to the all hosts, to reconstruct the
+ * target list. So the phyints are in the PI_NOTARGETS state.
+ *
+ * I - value of (pi_flags & IFF_INACTIVE)
+ * IFF_INACTIVE: No failovers have been done to the standby, from
+ * other phyints. This phyint is an inactive standby.
+ *
+ * pi_empty
+ * This phyint has failed over successfully to another phyint, and
+ * this phyint is currently "empty". It does not host any addresses or
+ * multicast membership etc. This is the state of a phyint after a
+ * failover from the phyint has completed successfully and no subsequent
+ * 'failover to' or 'failback to' has occurred on the phyint.
+ * IP guarantees that no new logicals will be hosted nor any multicast
+ * joins permitted on the phyint, since the phyint is either failed or
+ * inactive. pi_empty is set implies the phyint is either failed or
+ * inactive.
+ *
+ * pi_full
+ * The phyint hosts all of its own addresses that it "owns". If the
+ * phyint was previously failed or inactive, failbacks to the phyint
+ * has completed successfully. i.e. No more failbacks to this phyint
+ * can produce any change in system state whatsoever.
+ *
+ * Not all 32 possible combinations of the above 5-tuple are possible.
+ * Furthermore some of the above combinations are transient. They may occur
+ * only because the failover or failback did not complete successfully. The
+ * failover/failback will be retried and eventually a stable state will be
+ * reached.
+ *
+ * I is tracked by IP. pi_state, pi_empty and pi_full are tracked by mpathd.
+ * The following are the state machines. 'from' and 'to' are the src and
+ * dst of the failover/failback, below
+ *
+ * pi_empty state machine
+ * ---------------------------------------------------------------------------
+ * Event State -> New State
+ * ---------------------------------------------------------------------------
+ * successful completion from.pi_empty = 0 -> from.pi_empty = 1
+ * of failover
+ *
+ * Initiate failover to.pi_empty = X -> to.pi_empty = 0
+ *
+ * Initiate failback to.pi_empty = X -> to.pi_empty = 0
+ *
+ * group failure pi_empty = X -> pi_empty = 0
+ * ---------------------------------------------------------------------------
+ *
+ * pi_full state machine
+ * ---------------------------------------------------------------------------
+ * Event State -> New State
+ * ---------------------------------------------------------------------------
+ * successful completion to.pi_full = 0 -> to.pi_full = 1
+ * of failback from
+ * each of the other phyints
+ *
+ * Initiate failover from.pi_full = X -> from.pi_full = 0
+ *
+ * group failure pi_full = X -> pi_full = 0
+ * ---------------------------------------------------------------------------
+ *
+ * pi_state state machine
+ * ---------------------------------------------------------------------------
+ * Event State New State
+ * Action:
+ * ---------------------------------------------------------------------------
+ * NIC failure (PI_RUNNING, I == 0) -> (PI_FAILED, I == 0)
+ * detection : set IFF_FAILED on this phyint
+ * : failover from this phyint to another
+ *
+ * NIC failure (PI_RUNNING, I == 1) -> (PI_FAILED, I == 1)
+ * detection : set IFF_FAILED on this phyint
+ *
+ * NIC repair (PI_FAILED, I == 0) -> (PI_RUNNING, I == 0)
+ * detection : to.pi_empty = 0
+ * : failback to this phyint if enabled
+ * : clear IFF_FAILED on this phyint
+ *
+ * NIC repair (PI_FAILED, I == 1) -> (PI_RUNNING, I == 1)
+ * detection : clear IFF_FAILED on this phyint
+ *
+ * Group failure (perform on all phyints in the group)
+ * detection PI_RUNNING PI_FAILED
+ * (Router targets) : set IFF_FAILED
+ * : clear pi_empty and pi_full
+ *
+ * Group failure (perform on all phyints in the group)
+ * detection PI_RUNNING PI_NOTARGETS
+ * (Host targets) : set IFF_FAILED
+ * : clear pi_empty and pi_full
+ * : delete the target list on all phyints
+ * ---------------------------------------------------------------------------
+ *
+ * I state machine
+ * ---------------------------------------------------------------------------
+ * Event State Action:
+ * ---------------------------------------------------------------------------
+ * Turn on I pi_empty == 0 : failover from standby
+ *
+ * Turn off I PI_RUNNING, : pi_empty = 0
+ * pi_full == 0 : failback to this if enabled
+ * ---------------------------------------------------------------------------
+ *
+ * Assertions: (Read '==>' as implies)
+ *
+ * (pi_empty == 1) ==> (I == 1 || pi_state == PI_FAILED)
+ * (pi_empty == 1) ==> (pi_full == 0)
+ * (pi_full == 1) ==> (pi_empty == 0)
+ *
+ * Invariants
+ *
+ * pg_groupfailed = 0 &&
+ * 1. (I == 1, pi_empty == 0) ==> initiate failover from standby
+ * 2. (I == 0, PI_FAILED, pi_empty == 0) ==> initiate failover from phyint
+ * 3. (I == 0, PI_RUNNING, pi_full == 0) ==> initiate failback to phyint
+ *
+ * 1. says that an inactive standby, that is not empty, has to be failed
+ * over. For a standby to be truly inactive, it should not host any
+ * addresses. So we move them to some other phyint. Usually we catch the
+ * turn on of IFF_INACTIVE, and perform this action. However if the failover
+ * did not complete successfully, then subsequently we have lost the edge
+ * trigger, and this invariant kicks in and completes the action.
+ *
+ * 2. says that any failed phyint that is not empty must be failed over.
+ * Usually we do the failover when we detect NIC failure. However if the
+ * failover does not complete successfully, this invariant kicks in and
+ * completes the failover. We exclude inactive standby which is covered by 1.
+ *
+ * 3. says that any running phyint that is not full must be failed back.
+ * Usually we do the failback when we detect NIC repair. However if the
+ * failback does not complete successfully, this invariant kicks in and
+ * completes the failback. Note that we don't want to failback to an inactive
+ * standby.
+ *
+ * The invariants 1 - 3 and the actions are in initifs().
+ */
+
+struct probes_missed probes_missed;
+
+/*
+ * Compose and transmit an ICMP ECHO REQUEST packet. The IP header
+ * will be added on by the kernel. The id field identifies this phyint.
+ * and the sequence number is an increasing (modulo 2^^16) integer. The data
+ * portion holds the time value when the packet is sent. On echo this is
+ * extracted to compute the round-trip time. Three different types of
+ * probe packets are used.
+ *
+ * PROBE_UNI: This type is used to do failure detection / failure recovery
+ * and RTT calculation. PROBE_UNI probes are spaced apart in time,
+ * not less than the current CRTT. pii_probes[] stores data
+ * about these probes. These packets consume sequence number space.
+ *
+ * PROBE_RTT: This type is used to make only rtt measurments. Normally these
+ * are not used. Under heavy network load, the rtt may go up very high,
+ * due to a spike, or may appear to go high, due to extreme scheduling
+ * delays. Once the network stress is removed, mpathd takes long time to
+ * recover, because the probe_interval is already high, and it takes
+ * a long time to send out sufficient number of probes to bring down the
+ * rtt. To avoid this problem, PROBE_RTT probes are sent out every
+ * user_probe_interval ms. and will cause only rtt updates. These packets
+ * do not consume sequence number space nor is information about these
+ * packets stored in the pii_probes[]
+ *
+ * PROBE_MULTI: This type is only used to construct a list of targets, when
+ * no targets are known. The packet is multicast to the all hosts addr.
+ */
+static void
+probe(struct phyint_instance *pii, uint_t probe_type, uint_t cur_time)
+{
+ struct pr_icmp probe_pkt; /* Probe packet */
+ struct sockaddr_in6 whereto6; /* target address IPv6 */
+ struct sockaddr_in whereto; /* target address IPv4 */
+ int pr_ndx; /* probe index in pii->pii_probes[] */
+ boolean_t sent = _B_TRUE;
+
+ if (debug & D_TARGET) {
+ logdebug("probe(%s %s %d %u)\n", AF_STR(pii->pii_af),
+ pii->pii_name, probe_type, cur_time);
+ }
+
+ assert(pii->pii_probe_sock != -1);
+ assert(probe_type == PROBE_UNI || probe_type == PROBE_MULTI ||
+ probe_type == PROBE_RTT);
+
+ probe_pkt.pr_icmp_type = (pii->pii_af == AF_INET) ?
+ ICMP_ECHO_REQUEST : ICMP6_ECHO_REQUEST;
+ probe_pkt.pr_icmp_code = 0;
+ probe_pkt.pr_icmp_cksum = 0;
+ probe_pkt.pr_icmp_seq = htons(pii->pii_snxt);
+
+ /*
+ * Since there is no need to do arithmetic on the icmpid,
+ * (only equality check is done) pii_icmpid is stored in
+ * network byte order at initialization itself.
+ */
+ probe_pkt.pr_icmp_id = pii->pii_icmpid;
+ probe_pkt.pr_icmp_timestamp = htonl(cur_time);
+ probe_pkt.pr_icmp_mtype = htonl(probe_type);
+
+ /*
+ * If probe_type is PROBE_MULTI, this packet will be multicast to
+ * the all hosts address. Otherwise it is unicast to the next target.
+ */
+ assert(probe_type == PROBE_MULTI || ((pii->pii_target_next != NULL) &&
+ pii->pii_rtt_target_next != NULL));
+
+ if (pii->pii_af == AF_INET6) {
+ bzero(&whereto6, sizeof (whereto6));
+ whereto6.sin6_family = AF_INET6;
+ if (probe_type == PROBE_MULTI) {
+ whereto6.sin6_addr = all_nodes_mcast_v6;
+ } else if (probe_type == PROBE_UNI) {
+ whereto6.sin6_addr = pii->pii_target_next->tg_address;
+ } else {
+ /* type is PROBE_RTT */
+ whereto6.sin6_addr =
+ pii->pii_rtt_target_next->tg_address;
+ }
+ if (sendto(pii->pii_probe_sock, (char *)&probe_pkt,
+ sizeof (probe_pkt), 0, (struct sockaddr *)&whereto6,
+ sizeof (whereto6)) != sizeof (probe_pkt)) {
+ logperror_pii(pii, "probe: probe sendto");
+ sent = _B_FALSE;
+ }
+ } else {
+ bzero(&whereto, sizeof (whereto));
+ whereto.sin_family = AF_INET;
+ if (probe_type == PROBE_MULTI) {
+ whereto.sin_addr = all_nodes_mcast_v4;
+ } else if (probe_type == PROBE_UNI) {
+ IN6_V4MAPPED_TO_INADDR(
+ &pii->pii_target_next->tg_address,
+ &whereto.sin_addr);
+ } else {
+ /* type is PROBE_RTT */
+ IN6_V4MAPPED_TO_INADDR(
+ &pii->pii_rtt_target_next->tg_address,
+ &whereto.sin_addr);
+ }
+
+ /*
+ * Compute the IPv4 icmp checksum. Does not cover the IP header.
+ */
+ probe_pkt.pr_icmp_cksum =
+ in_cksum((ushort_t *)&probe_pkt, (int)sizeof (probe_pkt));
+ if (sendto(pii->pii_probe_sock, (char *)&probe_pkt,
+ sizeof (probe_pkt), 0, (struct sockaddr *)&whereto,
+ sizeof (whereto)) != sizeof (probe_pkt)) {
+ logperror_pii(pii, "probe: probe sendto");
+ sent = _B_FALSE;
+ }
+ }
+
+ /*
+ * If this is a PROBE_UNI probe packet being unicast to a target, then
+ * update our tables. We will need this info in processing the probe
+ * response. PROBE_MULTI and PROBE_RTT packets are not used for
+ * the purpose of failure or recovery detection. PROBE_MULTI packets
+ * are only used to construct a list of targets. PROBE_RTT packets are
+ * used only for updating the rtt and not for failure detection.
+ */
+ if (probe_type == PROBE_UNI && sent) {
+ pr_ndx = pii->pii_probe_next;
+ assert(pr_ndx >= 0 && pr_ndx < PROBE_STATS_COUNT);
+
+ /* Collect statistics, before we reuse the last slot. */
+ if (pii->pii_probes[pr_ndx].pr_status == PR_LOST)
+ pii->pii_cum_stats.lost++;
+ else if (pii->pii_probes[pr_ndx].pr_status == PR_ACKED)
+ pii->pii_cum_stats.acked++;
+ pii->pii_cum_stats.sent++;
+
+ pii->pii_probes[pr_ndx].pr_status = PR_UNACKED;
+ pii->pii_probes[pr_ndx].pr_target = pii->pii_target_next;
+ pii->pii_probes[pr_ndx].pr_time_sent = cur_time;
+ pii->pii_probe_next = PROBE_INDEX_NEXT(pii->pii_probe_next);
+ pii->pii_target_next = target_next(pii->pii_target_next);
+ assert(pii->pii_target_next != NULL);
+ /*
+ * If we have a single variable to denote the next target to
+ * probe for both rtt probes and failure detection probes, we
+ * could end up with a situation where the failure detection
+ * probe targets become disjoint from the rtt probe targets.
+ * Eg. if 2 targets and the actual fdt is double the user
+ * specified fdt. So we have 2 variables. In this scheme
+ * we also reset pii_rtt_target_next for every fdt probe,
+ * though that may not be necessary.
+ */
+ pii->pii_rtt_target_next = pii->pii_target_next;
+ pii->pii_snxt++;
+ } else if (probe_type == PROBE_RTT) {
+ pii->pii_rtt_target_next =
+ target_next(pii->pii_rtt_target_next);
+ assert(pii->pii_rtt_target_next != NULL);
+ }
+}
+
+/*
+ * Incoming IPv4 data from wire, is received here. Called from main.
+ */
+void
+in_data(struct phyint_instance *pii)
+{
+ struct sockaddr_in from;
+ struct in6_addr fromaddr;
+ uint_t fromlen;
+ static uint_t in_packet[(IP_MAXPACKET + 1)/4];
+ struct ip *ip;
+ int iphlen;
+ int len;
+ char abuf[INET_ADDRSTRLEN];
+ struct pr_icmp *reply;
+
+ if (debug & D_PROBE) {
+ logdebug("in_data(%s %s)\n",
+ AF_STR(pii->pii_af), pii->pii_name);
+ }
+
+ /*
+ * Poll has already told us that a message is waiting,
+ * on this socket. Read it now. We should not block.
+ */
+ fromlen = sizeof (from);
+ len = recvfrom(pii->pii_probe_sock, (char *)in_packet,
+ sizeof (in_packet), 0, (struct sockaddr *)&from, &fromlen);
+ if (len < 0) {
+ logperror_pii(pii, "in_data: recvfrom");
+ return;
+ }
+
+ /*
+ * If the NIC has indicated the link is down, don't go
+ * any further.
+ */
+ if (LINK_DOWN(pii->pii_phyint))
+ return;
+
+ /* Get the printable address for error reporting */
+ (void) inet_ntop(AF_INET, &from.sin_addr, abuf, sizeof (abuf));
+
+ /* Make sure packet contains at least minimum ICMP header */
+ ip = (struct ip *)in_packet;
+ iphlen = ip->ip_hl << 2;
+ if (len < iphlen + ICMP_MINLEN) {
+ if (debug & D_PKTBAD) {
+ logdebug("in_data: packet too short (%d bytes)"
+ " from %s\n", len, abuf);
+ }
+ return;
+ }
+
+ /*
+ * Subtract the IP hdr length, 'len' will be length of the probe
+ * reply, starting from the icmp hdr.
+ */
+ len -= iphlen;
+ /* LINTED */
+ reply = (struct pr_icmp *)((char *)in_packet + iphlen);
+
+ /* Probe replies are icmp echo replies. Ignore anything else */
+ if (reply->pr_icmp_type != ICMP_ECHO_REPLY)
+ return;
+
+ /*
+ * The icmp id should match what we sent, which is stored
+ * in pi_icmpid. The icmp code for reply must be 0.
+ * The reply content must be a struct pr_icmp
+ */
+ if (reply->pr_icmp_id != pii->pii_icmpid) {
+ /* Not in response to our probe */
+ return;
+ }
+
+ if (reply->pr_icmp_code != 0) {
+ logtrace("probe reply code %d from %s on %s\n",
+ reply->pr_icmp_code, abuf, pii->pii_name);
+ return;
+ }
+
+ if (len < sizeof (struct pr_icmp)) {
+ logtrace("probe reply too short: %d bytes from %s on %s\n",
+ len, abuf, pii->pii_name);
+ return;
+ }
+
+ IN6_INADDR_TO_V4MAPPED(&from.sin_addr, &fromaddr);
+ if (reply->pr_icmp_mtype == htonl(PROBE_UNI))
+ /* Unicast probe reply */
+ incoming_echo_reply(pii, reply, fromaddr);
+ else if (reply->pr_icmp_mtype == htonl(PROBE_MULTI)) {
+ /* Multicast reply */
+ incoming_mcast_reply(pii, reply, fromaddr);
+ } else if (reply->pr_icmp_mtype == htonl(PROBE_RTT)) {
+ incoming_rtt_reply(pii, reply, fromaddr);
+ } else {
+ /* Probably not in response to our probe */
+ logtrace("probe reply type: %d from %s on %s\n",
+ reply->pr_icmp_mtype, abuf, pii->pii_name);
+ return;
+ }
+
+}
+
+/*
+ * Incoming IPv6 data from wire is received here. Called from main.
+ */
+void
+in6_data(struct phyint_instance *pii)
+{
+ struct sockaddr_in6 from;
+ static uint64_t in_packet[(IP_MAXPACKET + 1)/8];
+ static uint64_t ancillary_data[(IP_MAXPACKET + 1)/8];
+ int len;
+ char abuf[INET6_ADDRSTRLEN];
+ struct msghdr msg;
+ struct iovec iov;
+ uchar_t *opt;
+ struct pr_icmp *reply;
+
+ if (debug & D_PROBE) {
+ logdebug("in6_data(%s %s)\n",
+ AF_STR(pii->pii_af), pii->pii_name);
+ }
+
+ iov.iov_base = (char *)in_packet;
+ iov.iov_len = sizeof (in_packet);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_name = (struct sockaddr *)&from;
+ msg.msg_namelen = sizeof (from);
+ msg.msg_control = ancillary_data;
+ msg.msg_controllen = sizeof (ancillary_data);
+
+ if ((len = recvmsg(pii->pii_probe_sock, &msg, 0)) < 0) {
+ logperror_pii(pii, "in6_data: recvfrom");
+ return;
+ }
+
+ /*
+ * If the NIC has indicated that the link is down, don't go
+ * any further.
+ */
+ if (LINK_DOWN(pii->pii_phyint))
+ return;
+
+ /* Get the printable address for error reporting */
+ (void) inet_ntop(AF_INET6, &from.sin6_addr, abuf, sizeof (abuf));
+ if (len < ICMP_MINLEN) {
+ if (debug & D_PKTBAD) {
+ logdebug("Truncated message: msg_flags 0x%x from %s\n",
+ msg.msg_flags, abuf);
+ }
+ return;
+ }
+ /* Ignore packets > 64k or control buffers that don't fit */
+ if (msg.msg_flags & (MSG_TRUNC|MSG_CTRUNC)) {
+ if (debug & D_PKTBAD) {
+ logdebug("Truncated message: msg_flags 0x%x from %s\n",
+ msg.msg_flags, abuf);
+ }
+ return;
+ }
+
+ reply = (struct pr_icmp *)in_packet;
+ if (reply->pr_icmp_type != ICMP6_ECHO_REPLY)
+ return;
+
+ if (reply->pr_icmp_id != pii->pii_icmpid) {
+ /* Not in response to our probe */
+ return;
+ }
+
+ /*
+ * The kernel has already verified the the ICMP checksum.
+ */
+ if (!IN6_IS_ADDR_LINKLOCAL(&from.sin6_addr)) {
+ logtrace("ICMPv6 echo reply source address not linklocal from "
+ "%s on %s\n", abuf, pii->pii_name);
+ return;
+ }
+ opt = find_ancillary(&msg, IPV6_RTHDR);
+ if (opt != NULL) {
+ /* Can't allow routing headers in probe replies */
+ logtrace("message with routing header from %s on %s\n",
+ abuf, pii->pii_name);
+ return;
+ }
+ if (reply->pr_icmp_code != 0) {
+ logtrace("probe reply code: %d from %s on %s\n",
+ reply->pr_icmp_code, abuf, pii->pii_name);
+ return;
+ }
+ if (len < (sizeof (struct pr_icmp))) {
+ logtrace("probe reply too short: %d bytes from %s on %s\n",
+ len, abuf, pii->pii_name);
+ return;
+ }
+ if (reply->pr_icmp_mtype == htonl(PROBE_UNI)) {
+ incoming_echo_reply(pii, reply, from.sin6_addr);
+ } else if (reply->pr_icmp_mtype == htonl(PROBE_MULTI)) {
+ incoming_mcast_reply(pii, reply, from.sin6_addr);
+ } else if (reply->pr_icmp_mtype == htonl(PROBE_RTT)) {
+ incoming_rtt_reply(pii, reply, from.sin6_addr);
+ } else {
+ /* Probably not in response to our probe */
+ logtrace("probe reply type: %d from %s on %s\n",
+ reply->pr_icmp_mtype, abuf, pii->pii_name);
+ }
+}
+
+/*
+ * Process the incoming rtt reply, in response to our rtt probe.
+ * Common for both IPv4 and IPv6. Unlike incoming_echo_reply() we don't
+ * have any stored information about the probe we sent. So we don't log
+ * any errors if we receive bad replies.
+ */
+static void
+incoming_rtt_reply(struct phyint_instance *pii, struct pr_icmp *reply,
+ struct in6_addr fromaddr)
+{
+ int m; /* rtt measurment in ms */
+ uint32_t cur_time; /* in ms from some arbitrary point */
+ char abuf[INET6_ADDRSTRLEN];
+ struct target *target;
+ uint32_t pr_icmp_timestamp;
+ struct phyint_group *pg;
+
+ /* Get the printable address for error reporting */
+ (void) pr_addr(pii->pii_af, fromaddr, abuf, sizeof (abuf));
+
+ if (debug & D_PROBE) {
+ logdebug("incoming_rtt_reply: %s %s %s\n",
+ AF_STR(pii->pii_af), pii->pii_name, abuf);
+ }
+
+ /* Do we know this target ? */
+ target = target_lookup(pii, fromaddr);
+ if (target == NULL)
+ return;
+
+ pr_icmp_timestamp = ntohl(reply->pr_icmp_timestamp);
+ cur_time = getcurrenttime();
+ m = (int)(cur_time - pr_icmp_timestamp);
+
+ /* Invalid rtt. It has wrapped around */
+ if (m < 0)
+ return;
+
+ /*
+ * Don't update rtt until we see NUM_PROBE_REPAIRS probe responses
+ * The initial few responses after the interface is repaired may
+ * contain high rtt's because they could have been queued up waiting
+ * for ARP/NDP resolution on a failed interface.
+ */
+ pg = pii->pii_phyint->pi_group;
+ if ((pii->pii_state != PI_RUNNING) || GROUP_FAILED(pg))
+ return;
+
+ /*
+ * Update rtt only if the new rtt is lower than the current rtt.
+ * (specified by the 3rd parameter to pi_set_crtt).
+ * If a spike has caused the current probe_interval to be >
+ * user_probe_interval, then this mechanism is used to bring down
+ * the rtt rapidly once the network stress is removed.
+ * If the new rtt is higher than the current rtt, we don't want to
+ * update the rtt. We are having more than 1 outstanding probe and
+ * the increase in rtt we are seeing is being unnecessarily weighted
+ * many times. The regular rtt update will be handled by
+ * incoming_echo_reply() and will take care of any rtt increase.
+ */
+ pi_set_crtt(target, m, _B_FALSE);
+ if ((target->tg_crtt < (pg->pg_probeint / LOWER_FDT_TRIGGER)) &&
+ (user_failure_detection_time < pg->pg_fdt) &&
+ (last_fdt_bumpup_time + MIN_SETTLING_TIME < gethrtime())) {
+ /*
+ * If the crtt has now dropped by a factor of LOWER_FT_TRIGGER,
+ * investigate if we can improve the failure detection time to
+ * meet whatever the user specified.
+ */
+ if (check_pg_crtt_improved(pg)) {
+ pg->pg_fdt = MAX(pg->pg_fdt / NEXT_FDT_MULTIPLE,
+ user_failure_detection_time);
+ pg->pg_probeint = pg->pg_fdt / (NUM_PROBE_FAILS + 2);
+ if (pii->pii_phyint->pi_group != phyint_anongroup) {
+ logerr("Improved failure detection time %d ms "
+ "on (%s %s) for group \"%s\"\n",
+ pg->pg_fdt, AF_STR(pii->pii_af),
+ pii->pii_name,
+ pii->pii_phyint->pi_group->pg_name);
+ }
+ if (user_failure_detection_time == pg->pg_fdt) {
+ /* Avoid any truncation or rounding errors */
+ pg->pg_probeint = user_probe_interval;
+ /*
+ * No more rtt probes will be sent. The actual
+ * fdt has dropped to the user specified value.
+ * pii_fd_snxt_basetime and pii_snxt_basetime
+ * will be in sync henceforth.
+ */
+ reset_snxt_basetimes();
+ }
+ }
+ }
+}
+
+/*
+ * Process the incoming echo reply, in response to our unicast probe.
+ * Common for both IPv4 and IPv6
+ */
+static void
+incoming_echo_reply(struct phyint_instance *pii, struct pr_icmp *reply,
+ struct in6_addr fromaddr)
+{
+ int m; /* rtt measurment in ms */
+ uint32_t cur_time; /* in ms from some arbitrary point */
+ char abuf[INET6_ADDRSTRLEN];
+ int pr_ndx;
+ struct target *target;
+ boolean_t exception;
+ uint32_t pr_icmp_timestamp;
+ uint16_t pr_icmp_seq;
+ struct phyint_group *pg = pii->pii_phyint->pi_group;
+
+ /* Get the printable address for error reporting */
+ (void) pr_addr(pii->pii_af, fromaddr, abuf, sizeof (abuf));
+
+ if (debug & D_PROBE) {
+ logdebug("incoming_echo_reply: %s %s %s seq %u\n",
+ AF_STR(pii->pii_af), pii->pii_name, abuf,
+ ntohs(reply->pr_icmp_seq));
+ }
+
+ pr_icmp_timestamp = ntohl(reply->pr_icmp_timestamp);
+ pr_icmp_seq = ntohs(reply->pr_icmp_seq);
+
+ /* Reject out of window probe replies */
+ if (SEQ_GE(pr_icmp_seq, pii->pii_snxt) ||
+ SEQ_LT(pr_icmp_seq, pii->pii_snxt - PROBE_STATS_COUNT)) {
+ logtrace("out of window probe seq %u snxt %u on %s from %s\n",
+ pr_icmp_seq, pii->pii_snxt, pii->pii_name, abuf);
+ pii->pii_cum_stats.unknown++;
+ return;
+ }
+ cur_time = getcurrenttime();
+ m = (int)(cur_time - pr_icmp_timestamp);
+ if (m < 0) {
+ /*
+ * This is a ridiculously high value of rtt. rtt has wrapped
+ * around. Log a message, and ignore the rtt.
+ */
+ logerr("incoming_echo_reply: rtt wraparound cur_time %u reply "
+ "timestamp %u\n", cur_time, pr_icmp_timestamp);
+ }
+
+ /*
+ * Get the probe index pr_ndx corresponding to the received icmp seq.
+ * number in our pii->pii_probes[] array. The icmp sequence number
+ * pii_snxt corresponds to the probe index pii->pii_probe_next
+ */
+ pr_ndx = MOD_SUB(pii->pii_probe_next,
+ (uint16_t)(pii->pii_snxt - pr_icmp_seq), PROBE_STATS_COUNT);
+
+ assert(PR_STATUS_VALID(pii->pii_probes[pr_ndx].pr_status));
+
+ target = pii->pii_probes[pr_ndx].pr_target;
+
+ /*
+ * Perform sanity checks, whether this probe reply that we
+ * have received is genuine
+ */
+ if (target != NULL) {
+ /*
+ * Compare the src. addr of the received ICMP or ICMPv6
+ * probe reply with the target address in our tables.
+ */
+ if (!IN6_ARE_ADDR_EQUAL(&target->tg_address, &fromaddr)) {
+ /*
+ * We don't have any record of having sent a probe to
+ * this target. This is a fake probe reply. Log an error
+ */
+ logtrace("probe status %d Fake probe reply seq %u "
+ "snxt %u on %s from %s\n",
+ pii->pii_probes[pr_ndx].pr_status,
+ pr_icmp_seq, pii->pii_snxt, pii->pii_name, abuf);
+ pii->pii_cum_stats.unknown++;
+ return;
+ } else if (pii->pii_probes[pr_ndx].pr_status == PR_ACKED) {
+ /*
+ * The address matches, but our tables indicate that
+ * this probe reply has been acked already. So this
+ * is a duplicate probe reply. Log an error
+ */
+ logtrace("probe status %d Duplicate probe reply seq %u "
+ "snxt %u on %s from %s\n",
+ pii->pii_probes[pr_ndx].pr_status,
+ pr_icmp_seq, pii->pii_snxt, pii->pii_name, abuf);
+ pii->pii_cum_stats.unknown++;
+ return;
+ }
+ } else {
+ /*
+ * Target must not be NULL in the PR_UNACKED state
+ */
+ assert(pii->pii_probes[pr_ndx].pr_status != PR_UNACKED);
+ if (pii->pii_probes[pr_ndx].pr_status == PR_UNUSED) {
+ /*
+ * The probe stats slot is unused. So we didn't
+ * send out any probe to this target. This is a fake.
+ * Log an error.
+ */
+ logtrace("probe status %d Fake probe reply seq %u "
+ "snxt %u on %s from %s\n",
+ pii->pii_probes[pr_ndx].pr_status,
+ pr_icmp_seq, pii->pii_snxt, pii->pii_name, abuf);
+ }
+ pii->pii_cum_stats.unknown++;
+ return;
+ }
+
+ /*
+ * If the rtt does not appear to be right, don't update the
+ * rtt stats. This can happen if the system dropped into the
+ * debugger, or the system was hung or too busy for a
+ * substantial time that we didn't get a chance to run.
+ */
+ if ((m < 0) || (m > PROBE_STATS_COUNT * pg->pg_probeint)) {
+ /*
+ * If the probe corresponding to this receieved response
+ * was truly sent 'm' ms. ago, then this response must
+ * have been rejected by the sequence number checks. The
+ * fact that it has passed the sequence number checks
+ * means that the measured rtt is wrong. We were probably
+ * scheduled long after the packet was received.
+ */
+ goto out;
+ }
+
+ /*
+ * Don't update rtt until we see NUM_PROBE_REPAIRS probe responses
+ * The initial few responses after the interface is repaired may
+ * contain high rtt's because they could have been queued up waiting
+ * for ARP/NDP resolution on a failed interface.
+ */
+ if ((pii->pii_state != PI_RUNNING) || GROUP_FAILED(pg))
+ goto out;
+
+ /*
+ * Don't update the Conservative Round Trip Time estimate for this
+ * (phint, target) pair if this is the not the highest ack seq seen
+ * thus far on this target.
+ */
+ if (!highest_ack_tg(pr_icmp_seq, target))
+ goto out;
+
+ /*
+ * Always update the rtt. This is a failure detection probe
+ * and we want to measure both increase / decrease in rtt.
+ */
+ pi_set_crtt(target, m, _B_TRUE);
+
+ /*
+ * If the crtt exceeds the average time between probes,
+ * investigate if this slow target is an exception. If so we
+ * can avoid this target and still meet the failure detection
+ * time. Otherwise we can't meet the failure detection time.
+ */
+ if (target->tg_crtt > pg->pg_probeint) {
+ exception = check_exception_target(pii, target);
+ if (exception) {
+ /*
+ * This target is exceptionally slow. Don't use it
+ * for future probes. check_exception_target() has
+ * made sure that we have at least MIN_PROBE_TARGETS
+ * other active targets
+ */
+ if (pii->pii_targets_are_routers) {
+ /*
+ * This is a slow router, mark it as slow
+ * and don't use it for further probes. We
+ * don't delete it, since it will be populated
+ * again when we do a router scan. Hence we
+ * need to maintain extra state (unlike the
+ * host case below). Mark it as TG_SLOW.
+ */
+ if (target->tg_status == TG_ACTIVE)
+ pii->pii_ntargets--;
+ target->tg_status = TG_SLOW;
+ target->tg_latime = gethrtime();
+ target->tg_rtt_sa = -1;
+ target->tg_crtt = 0;
+ target->tg_rtt_sd = 0;
+ if (pii->pii_target_next == target) {
+ pii->pii_target_next =
+ target_next(target);
+ }
+ } else {
+ /*
+ * the slow target is not a router, we can
+ * just delete it. Send an icmp multicast and
+ * pick the fastest responder that is not
+ * already an active target. target_delete()
+ * adjusts pii->pii_target_next
+ */
+ target_delete(target);
+ probe(pii, PROBE_MULTI, cur_time);
+ }
+ } else {
+ /*
+ * We can't meet the failure detection time.
+ * Log a message, and update the detection time to
+ * whatever we can achieve.
+ */
+ pg->pg_probeint = target->tg_crtt * NEXT_FDT_MULTIPLE;
+ pg->pg_fdt = pg->pg_probeint * (NUM_PROBE_FAILS + 2);
+ last_fdt_bumpup_time = gethrtime();
+ if (pg != phyint_anongroup) {
+ logerr("Cannot meet requested failure detection"
+ " time of %d ms on (%s %s) new failure"
+ " detection time for group \"%s\" is %d"
+ " ms\n", user_failure_detection_time,
+ AF_STR(pii->pii_af), pii->pii_name,
+ pg->pg_name, pg->pg_fdt);
+ }
+ }
+ } else if ((target->tg_crtt < (pg->pg_probeint / LOWER_FDT_TRIGGER)) &&
+ (user_failure_detection_time < pg->pg_fdt) &&
+ (last_fdt_bumpup_time + MIN_SETTLING_TIME < gethrtime())) {
+ /*
+ * If the crtt has now dropped by a factor of LOWER_FDT_TRIGGER
+ * investigate if we can improve the failure detection time to
+ * meet whatever the user specified.
+ */
+ if (check_pg_crtt_improved(pg)) {
+ pg->pg_fdt = MAX(pg->pg_fdt / NEXT_FDT_MULTIPLE,
+ user_failure_detection_time);
+ pg->pg_probeint = pg->pg_fdt / (NUM_PROBE_FAILS + 2);
+ if (pg != phyint_anongroup) {
+ logerr("Improved failure detection time %d ms "
+ "on (%s %s) for group \"%s\"\n", pg->pg_fdt,
+ AF_STR(pii->pii_af), pii->pii_name,
+ pg->pg_name);
+ }
+ if (user_failure_detection_time == pg->pg_fdt) {
+ /* Avoid any truncation or rounding errors */
+ pg->pg_probeint = user_probe_interval;
+ /*
+ * No more rtt probes will be sent. The actual
+ * fdt has dropped to the user specified value.
+ * pii_fd_snxt_basetime and pii_snxt_basetime
+ * will be in sync henceforth.
+ */
+ reset_snxt_basetimes();
+ }
+ }
+ }
+out:
+ pii->pii_probes[pr_ndx].pr_status = PR_ACKED;
+ pii->pii_probes[pr_ndx].pr_time_acked = cur_time;
+
+ /*
+ * Update pii->pii_rack, i.e. the sequence number of the last received
+ * probe response, based on the echo reply we have received now, if
+ * either of the following conditions are satisfied.
+ * a. pii_rack is outside the current receive window of
+ * [pii->pii_snxt - PROBE_STATS_COUNT, pii->pii_snxt).
+ * This means we have not received probe responses for a
+ * long time, and the sequence number has wrapped around.
+ * b. pii_rack is within the current receive window and this echo
+ * reply corresponds to the highest sequence number we have seen
+ * so far.
+ */
+ if (SEQ_GE(pii->pii_rack, pii->pii_snxt) ||
+ SEQ_LT(pii->pii_rack, pii->pii_snxt - PROBE_STATS_COUNT) ||
+ SEQ_GT(pr_icmp_seq, pii->pii_rack)) {
+ pii->pii_rack = pr_icmp_seq;
+ }
+}
+
+/*
+ * Returns true if seq is the highest unacknowledged seq for target tg
+ * else returns false
+ */
+static boolean_t
+highest_ack_tg(uint16_t seq, struct target *tg)
+{
+ struct phyint_instance *pii;
+ int pr_ndx;
+ uint16_t pr_seq;
+
+ pii = tg->tg_phyint_inst;
+
+ /*
+ * Get the seq number of the most recent probe sent so far,
+ * and also get the corresponding probe index in the probe stats
+ * array.
+ */
+ pr_ndx = PROBE_INDEX_PREV(pii->pii_probe_next);
+ pr_seq = pii->pii_snxt;
+ pr_seq--;
+
+ /*
+ * Start from the most recent probe and walk back, trying to find
+ * an acked probe corresponding to target tg.
+ */
+ for (; pr_ndx != pii->pii_probe_next;
+ pr_ndx = PROBE_INDEX_PREV(pr_ndx), pr_seq--) {
+ if (pii->pii_probes[pr_ndx].pr_target == tg &&
+ pii->pii_probes[pr_ndx].pr_status == PR_ACKED) {
+ if (SEQ_GT(pr_seq, seq))
+ return (_B_FALSE);
+ }
+ }
+ return (_B_TRUE);
+}
+
+/*
+ * Check whether the crtt for the group has improved by a factor of
+ * LOWER_FDT_TRIGGER. Small crtt improvements are ignored to avoid failure
+ * detection time flapping in the face of small crtt changes.
+ */
+static boolean_t
+check_pg_crtt_improved(struct phyint_group *pg)
+{
+ struct phyint *pi;
+
+ if (debug & D_PROBE)
+ logdebug("check_pg_crtt_improved()\n");
+
+ /*
+ * The crtt for the group is only improved if each phyint_instance
+ * for both ipv4 and ipv6 is improved.
+ */
+ for (pi = pg->pg_phyint; pi != NULL; pi = pi->pi_pgnext) {
+ if (!check_pii_crtt_improved(pi->pi_v4) ||
+ !check_pii_crtt_improved(pi->pi_v6))
+ return (_B_FALSE);
+ }
+
+ return (_B_TRUE);
+}
+
+/*
+ * Check whether the crtt has improved substantially on this phyint_instance.
+ * Returns _B_TRUE if there's no crtt information available, because pii
+ * is NULL or the phyint_instance is not capable of probing.
+ */
+boolean_t
+check_pii_crtt_improved(struct phyint_instance *pii) {
+ struct target *tg;
+
+ if (pii == NULL)
+ return (_B_TRUE);
+
+ if (!PROBE_CAPABLE(pii) ||
+ pii->pii_phyint->pi_state == PI_FAILED)
+ return (_B_TRUE);
+
+ for (tg = pii->pii_targets; tg != NULL; tg = tg->tg_next) {
+ if (tg->tg_status != TG_ACTIVE)
+ continue;
+ if (tg->tg_crtt > (pii->pii_phyint->pi_group->pg_probeint /
+ LOWER_FDT_TRIGGER)) {
+ return (_B_FALSE);
+ }
+ }
+
+ return (_B_TRUE);
+}
+
+/*
+ * This target responds very slowly to probes. The target's crtt exceeds
+ * the probe interval of its group. Compare against other targets
+ * and determine if this target is an exception, if so return true, else false
+ */
+static boolean_t
+check_exception_target(struct phyint_instance *pii, struct target *target)
+{
+ struct target *tg;
+ char abuf[INET6_ADDRSTRLEN];
+
+ if (debug & D_PROBE) {
+ logdebug("check_exception_target(%s %s target %s)\n",
+ AF_STR(pii->pii_af), pii->pii_name,
+ pr_addr(pii->pii_af, target->tg_address,
+ abuf, sizeof (abuf)));
+ }
+
+ /*
+ * We should have at least MIN_PROBE_TARGETS + 1 good targets now,
+ * to make a good judgement. Otherwise don't drop this target.
+ */
+ if (pii->pii_ntargets < MIN_PROBE_TARGETS + 1)
+ return (_B_FALSE);
+
+ /*
+ * Determine whether only this particular target is slow.
+ * We know that this target's crtt exceeds the group's probe interval.
+ * If all other active targets have a
+ * crtt < (this group's probe interval) / EXCEPTION_FACTOR,
+ * then this target is considered slow.
+ */
+ for (tg = pii->pii_targets; tg != NULL; tg = tg->tg_next) {
+ if (tg != target && tg->tg_status == TG_ACTIVE) {
+ if (tg->tg_crtt >
+ pii->pii_phyint->pi_group->pg_probeint /
+ EXCEPTION_FACTOR) {
+ return (_B_FALSE);
+ }
+ }
+ }
+
+ return (_B_TRUE);
+}
+
+/*
+ * Update the target list. The icmp all hosts multicast has given us
+ * some host to which we can send probes. If we already have sufficient
+ * targets, discard it.
+ */
+static void
+incoming_mcast_reply(struct phyint_instance *pii, struct pr_icmp *reply,
+ struct in6_addr fromaddr)
+/* ARGSUSED */
+{
+ int af;
+ char abuf[INET6_ADDRSTRLEN];
+ struct phyint *pi;
+
+ if (debug & D_PROBE) {
+ logdebug("incoming_mcast_reply(%s %s %s)\n",
+ AF_STR(pii->pii_af), pii->pii_name,
+ pr_addr(pii->pii_af, fromaddr, abuf, sizeof (abuf)));
+ }
+
+ /*
+ * Using host targets is a fallback mechanism. If we have
+ * found a router, don't add this host target. If we already
+ * know MAX_PROBE_TARGETS, don't add another target.
+ */
+ assert(pii->pii_ntargets <= MAX_PROBE_TARGETS);
+ if (pii->pii_targets != NULL) {
+ if (pii->pii_targets_are_routers ||
+ (pii->pii_ntargets == MAX_PROBE_TARGETS)) {
+ return;
+ }
+ }
+
+ if (IN6_IS_ADDR_UNSPECIFIED(&fromaddr) ||
+ IN6_IS_ADDR_V4MAPPED_ANY(&fromaddr)) {
+ /*
+ * Guard against response from 0.0.0.0
+ * and ::. Log a trace message
+ */
+ logtrace("probe response from %s on %s\n",
+ pr_addr(pii->pii_af, fromaddr, abuf, sizeof (abuf)),
+ pii->pii_name);
+ return;
+ }
+
+ /*
+ * This address is one of our own, so reject this address as a
+ * valid probe target.
+ */
+ af = pii->pii_af;
+ if (own_address(af, fromaddr))
+ return;
+
+ /*
+ * If the phyint is part a named group, then add the address to all
+ * members of the group. Otherwise, add the address only to the
+ * phyint itself, since other phyints in the anongroup may not be on
+ * the same subnet.
+ */
+ pi = pii->pii_phyint;
+ if (pi->pi_group == phyint_anongroup) {
+ target_add(pii, fromaddr, _B_FALSE);
+ } else {
+ pi = pi->pi_group->pg_phyint;
+ for (; pi != NULL; pi = pi->pi_pgnext)
+ target_add(PHYINT_INSTANCE(pi, af), fromaddr, _B_FALSE);
+ }
+}
+
+/*
+ * Compute CRTT given an existing scaled average, scaled deviation estimate
+ * and a new rtt time. The formula is from Jacobson and Karels'
+ * "Congestion Avoidance and Control" in SIGCOMM '88. The variable names
+ * are the same as those in Appendix A.2 of that paper.
+ *
+ * m = new measurement
+ * sa = scaled RTT average (8 * average estimates)
+ * sv = scaled mean deviation (mdev) of RTT (4 * deviation estimates).
+ * crtt = Conservative round trip time. Used to determine whether probe
+ * has timed out.
+ *
+ * New scaled average and deviation are passed back via sap and svp
+ */
+static int
+compute_crtt(int *sap, int *svp, int m)
+{
+ int sa = *sap;
+ int sv = *svp;
+ int crtt;
+ int saved_m = m;
+
+ assert(*sap >= -1);
+ assert(*svp >= 0);
+
+ if (sa != -1) {
+ /*
+ * Update average estimator:
+ * new rtt = old rtt + 1/8 Error
+ * where Error = m - old rtt
+ * i.e. 8 * new rtt = 8 * old rtt + Error
+ * i.e. new sa = old sa + Error
+ */
+ m -= sa >> 3; /* m is now Error in estimate. */
+ if ((sa += m) < 0) {
+ /* Don't allow the smoothed average to be negative. */
+ sa = 0;
+ }
+
+ /*
+ * Update deviation estimator:
+ * new mdev = old mdev + 1/4 (abs(Error) - old mdev)
+ * i.e. 4 * new mdev = 4 * old mdev +
+ * (abs(Error) - old mdev)
+ * i.e. new sv = old sv + (abs(Error) - old mdev)
+ */
+ if (m < 0)
+ m = -m;
+ m -= sv >> 2;
+ sv += m;
+ } else {
+ /* Initialization. This is the first response received. */
+ sa = (m << 3);
+ sv = (m << 1);
+ }
+
+ crtt = (sa >> 3) + sv;
+
+ if (debug & D_PROBE) {
+ logdebug("compute_crtt: m = %d sa = %d, sv = %d -> crtt = "
+ "%d\n", saved_m, sa, sv, crtt);
+ }
+
+ *sap = sa;
+ *svp = sv;
+
+ /*
+ * CRTT = average estimates + 4 * deviation estimates
+ * = sa / 8 + sv
+ */
+ return (crtt);
+}
+
+static void
+pi_set_crtt(struct target *tg, int m, boolean_t is_probe_uni)
+{
+ struct phyint_instance *pii = tg->tg_phyint_inst;
+ int probe_interval = pii->pii_phyint->pi_group->pg_probeint;
+ int sa = tg->tg_rtt_sa;
+ int sv = tg->tg_rtt_sd;
+ int new_crtt;
+ int i;
+
+ if (debug & D_PROBE)
+ logdebug("pi_set_crtt: target - m %d\n", m);
+
+ /* store the round trip time, in case we need to defer computation */
+ tg->tg_deferred[tg->tg_num_deferred] = m;
+
+ new_crtt = compute_crtt(&sa, &sv, m);
+
+ /*
+ * If this probe's round trip time would singlehandedly cause an
+ * increase in the group's probe interval consider it suspect.
+ */
+ if ((new_crtt > probe_interval) && is_probe_uni) {
+ if (debug & D_PROBE) {
+ logdebug("Received a suspect probe on %s, new_crtt ="
+ " %d, probe_interval = %d, num_deferred = %d\n",
+ pii->pii_probe_logint->li_name, new_crtt,
+ probe_interval, tg->tg_num_deferred);
+ }
+
+ /*
+ * If we've deferred as many rtts as we plan on deferring, then
+ * assume the link really did slow down and process all queued
+ * rtts
+ */
+ if (tg->tg_num_deferred == MAXDEFERREDRTT) {
+ if (debug & D_PROBE) {
+ logdebug("Received MAXDEFERREDRTT probes which "
+ "would cause an increased probe_interval. "
+ "Integrating queued rtt data points.\n");
+ }
+
+ for (i = 0; i <= tg->tg_num_deferred; i++) {
+ tg->tg_crtt = compute_crtt(&tg->tg_rtt_sa,
+ &tg->tg_rtt_sd, tg->tg_deferred[i]);
+ }
+
+ tg->tg_num_deferred = 0;
+ } else {
+ tg->tg_num_deferred++;
+ }
+ return;
+ }
+
+ /*
+ * If this is a normal probe, or an RTT probe that would lead to a
+ * reduced CRTT, then update our CRTT data. Further, if this was
+ * a normal probe, pitch any deferred probes since our probes are
+ * again being answered within our CRTT estimates.
+ */
+ if (is_probe_uni || new_crtt < tg->tg_crtt) {
+ tg->tg_rtt_sa = sa;
+ tg->tg_rtt_sd = sv;
+ tg->tg_crtt = new_crtt;
+ if (is_probe_uni)
+ tg->tg_num_deferred = 0;
+ }
+}
+
+/*
+ * Return a pointer to the specified option buffer.
+ * If not found return NULL.
+ */
+static void *
+find_ancillary(struct msghdr *msg, int cmsg_type)
+{
+ struct cmsghdr *cmsg;
+
+ for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL;
+ cmsg = CMSG_NXTHDR(msg, cmsg)) {
+ if (cmsg->cmsg_level == IPPROTO_IPV6 &&
+ cmsg->cmsg_type == cmsg_type) {
+ return (CMSG_DATA(cmsg));
+ }
+ }
+ return (NULL);
+}
+
+/*
+ * See if a previously failed interface has started working again.
+ */
+void
+phyint_check_for_repair(struct phyint *pi)
+{
+ if (phyint_repaired(pi)) {
+ if (pi->pi_group == phyint_anongroup) {
+ logerr("NIC repair detected on %s\n", pi->pi_name);
+ } else {
+ logerr("NIC repair detected on %s of group %s\n",
+ pi->pi_name, pi->pi_group->pg_name);
+ }
+
+ /*
+ * If the interface is offline, just clear the FAILED flag,
+ * delaying the state change and failback operation until it
+ * is brought back online.
+ */
+ if (pi->pi_state == PI_OFFLINE) {
+ (void) change_lif_flags(pi, IFF_FAILED, _B_FALSE);
+ return;
+ }
+
+ if (pi->pi_flags & IFF_INACTIVE) {
+ (void) change_lif_flags(pi, IFF_FAILED, _B_FALSE);
+ } else {
+ if (try_failback(pi, _B_FALSE) != IPMP_FAILURE) {
+ (void) change_lif_flags(pi,
+ IFF_FAILED, _B_FALSE);
+ /* Per state diagram */
+ pi->pi_empty = 0;
+ }
+ }
+
+ phyint_chstate(pi, PI_RUNNING);
+
+ if (GROUP_FAILED(pi->pi_group)) {
+ /*
+ * This is the 1st phyint to receive a response
+ * after group failure.
+ */
+ logerr("At least 1 interface (%s) of group %s has "
+ "repaired\n", pi->pi_name, pi->pi_group->pg_name);
+ phyint_group_chstate(pi->pi_group, PG_RUNNING);
+ }
+ }
+}
+
+/*
+ * See if a previously functioning interface has failed, or if the
+ * whole group of interfaces has failed.
+ */
+static void
+phyint_inst_check_for_failure(struct phyint_instance *pii)
+{
+ struct phyint *pi;
+ struct phyint *pi2;
+
+ pi = pii->pii_phyint;
+
+ switch (failure_state(pii)) {
+ case PHYINT_FAILURE:
+ (void) change_lif_flags(pi, IFF_FAILED, _B_TRUE);
+ if (pi->pi_group == phyint_anongroup) {
+ logerr("NIC failure detected on %s\n", pii->pii_name);
+ } else {
+ logerr("NIC failure detected on %s of group %s\n",
+ pii->pii_name, pi->pi_group->pg_name);
+ }
+ /*
+ * Do the failover, unless the interface is offline (in
+ * which case we've already failed over).
+ */
+ if (pi->pi_state != PI_OFFLINE) {
+ phyint_chstate(pi, PI_FAILED);
+ reset_crtt_all(pi);
+ if (!(pi->pi_flags & IFF_INACTIVE))
+ (void) try_failover(pi, FAILOVER_NORMAL);
+ }
+ break;
+
+ case GROUP_FAILURE:
+ logerr("All Interfaces in group %s have failed\n",
+ pi->pi_group->pg_name);
+ for (pi2 = pi->pi_group->pg_phyint; pi2 != NULL;
+ pi2 = pi2->pi_pgnext) {
+ if (pi2->pi_flags & IFF_OFFLINE)
+ continue;
+ (void) change_lif_flags(pi2, IFF_FAILED, _B_TRUE);
+ reset_crtt_all(pi2);
+
+ /*
+ * In the case of host targets, we
+ * would have flushed the targets,
+ * and gone to PI_NOTARGETS state.
+ */
+ if (pi2->pi_state == PI_RUNNING)
+ phyint_chstate(pi, PI_FAILED);
+
+ pi2->pi_empty = 0;
+ pi2->pi_full = 0;
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+/*
+ * Determines if any timeout event has occurred and returns the number of
+ * milliseconds until the next timeout event for the phyint. Returns
+ * TIMER_INFINITY for "never".
+ */
+uint_t
+phyint_inst_timer(struct phyint_instance *pii)
+{
+ int pr_ndx;
+ uint_t timeout;
+ struct target *cur_tg;
+ struct probe_stats *pr_statp;
+ struct phyint_instance *pii_other;
+ struct phyint *pi;
+ int valid_unack_count;
+ int i;
+ int interval;
+ uint_t check_time;
+ uint_t cur_time;
+ hrtime_t cur_hrtime;
+ int probe_interval = pii->pii_phyint->pi_group->pg_probeint;
+
+ cur_time = getcurrenttime();
+
+ if (debug & D_TIMER) {
+ logdebug("phyint_inst_timer(%s %s)\n",
+ AF_STR(pii->pii_af), pii->pii_name);
+ }
+
+ pii_other = phyint_inst_other(pii);
+ if (!PROBE_ENABLED(pii) && !PROBE_ENABLED(pii_other)) {
+ /*
+ * Check to see if we're here due to link up/down flapping; If
+ * enough time has passed, then try to bring the interface
+ * back up; otherwise, schedule a timer to bring it back up
+ * when enough time *has* elapsed.
+ */
+ pi = pii->pii_phyint;
+ if (pi->pi_state == PI_FAILED && LINK_UP(pi)) {
+ check_time = pi->pi_whenup[pi->pi_whendx] + MSEC_PERMIN;
+ if (check_time > cur_time)
+ return (check_time - cur_time);
+
+ phyint_check_for_repair(pi);
+ }
+ }
+
+ /*
+ * If this phyint is not yet initialized for probes,
+ * don't proceed further
+ */
+ if (pii->pii_probe_sock == -1)
+ return (TIMER_INFINITY);
+
+ /*
+ * If the timer has fired too soon, probably triggered
+ * by some other phyint instance, return the remaining
+ * time
+ */
+ if (TIME_LT(cur_time, pii->pii_snxt_time))
+ return (pii->pii_snxt_time - cur_time);
+
+ /*
+ * If the link is down, don't send any probes for now.
+ */
+ if (LINK_DOWN(pii->pii_phyint))
+ return (TIMER_INFINITY);
+
+ /*
+ * Randomize the next probe time, between MIN_RANDOM_FACTOR
+ * and MAX_RANDOM_FACTOR with respect to the base probe time.
+ * Base probe time is strictly periodic.
+ */
+ interval = GET_RANDOM(
+ (int)(MIN_RANDOM_FACTOR * user_probe_interval),
+ (int)(MAX_RANDOM_FACTOR * user_probe_interval));
+ pii->pii_snxt_time = pii->pii_snxt_basetime + interval;
+
+ /*
+ * Check if the current time > next time to probe. If so, we missed
+ * sending 1 or more probes, probably due to heavy system load. At least
+ * 'MIN_RANDOM_FACTOR * user_probe_interval' ms has elapsed since we
+ * were scheduled. Make adjustments to the times, in multiples of
+ * user_probe_interval.
+ */
+ if (TIME_GT(cur_time, pii->pii_snxt_time)) {
+ int n;
+
+ n = (cur_time - pii->pii_snxt_time) / user_probe_interval;
+ pii->pii_snxt_time += (n + 1) * user_probe_interval;
+ pii->pii_snxt_basetime += (n + 1) * user_probe_interval;
+ logtrace("missed sending %d probes cur_time %u snxt_time %u"
+ " snxt_basetime %u\n", n + 1, cur_time, pii->pii_snxt_time,
+ pii->pii_snxt_basetime);
+
+ /* Collect statistics about missed probes */
+ probes_missed.pm_nprobes += n + 1;
+ probes_missed.pm_ntimes++;
+ }
+ pii->pii_snxt_basetime += user_probe_interval;
+ interval = pii->pii_snxt_time - cur_time;
+ if (debug & D_TARGET) {
+ logdebug("cur_time %u snxt_time %u snxt_basetime %u"
+ " interval %u\n", cur_time, pii->pii_snxt_time,
+ pii->pii_snxt_basetime, interval);
+ }
+
+ /*
+ * If no targets are known, we need to send an ICMP multicast. The
+ * probe type is PROBE_MULTI. We'll check back in 'interval' msec
+ * to see if we found a target.
+ */
+ if (pii->pii_target_next == NULL) {
+ assert(pii->pii_ntargets == 0);
+ pii->pii_fd_snxt_basetime = pii->pii_snxt_basetime;
+ probe(pii, PROBE_MULTI, cur_time);
+ return (interval);
+ }
+
+ if ((user_probe_interval != probe_interval) &&
+ TIME_LT(pii->pii_snxt_time, pii->pii_fd_snxt_basetime)) {
+ /*
+ * the failure detection (fd) probe timer has not yet fired.
+ * Need to send only an rtt probe. The probe type is PROBE_RTT.
+ */
+ probe(pii, PROBE_RTT, cur_time);
+ return (interval);
+ }
+ /*
+ * the fd probe timer has fired. Need to do all failure
+ * detection / recovery calculations, and then send an fd probe
+ * of type PROBE_UNI.
+ */
+ if (user_probe_interval == probe_interval) {
+ /*
+ * We could have missed some probes, and then adjusted
+ * pii_snxt_basetime above. Otherwise we could have
+ * blindly added probe_interval to pii_fd_snxt_basetime.
+ */
+ pii->pii_fd_snxt_basetime = pii->pii_snxt_basetime;
+ } else {
+ pii->pii_fd_snxt_basetime += probe_interval;
+ if (TIME_GT(cur_time, pii->pii_fd_snxt_basetime)) {
+ int n;
+
+ n = (cur_time - pii->pii_fd_snxt_basetime) /
+ probe_interval;
+ pii->pii_fd_snxt_basetime += (n + 1) * probe_interval;
+ }
+ }
+
+ /*
+ * We can have at most, the latest 2 probes that we sent, in
+ * the PR_UNACKED state. All previous probes sent, are either
+ * PR_LOST or PR_ACKED. An unacknowledged probe is considered
+ * timed out if the probe's time_sent + the CRTT < currenttime.
+ * For each of the last 2 probes, examine whether it has timed
+ * out. If so, mark it PR_LOST. The probe stats is a circular array.
+ */
+ pr_ndx = PROBE_INDEX_PREV(pii->pii_probe_next);
+ valid_unack_count = 0;
+
+ for (i = 0; i < 2; i++) {
+ pr_statp = &pii->pii_probes[pr_ndx];
+ cur_tg = pii->pii_probes[pr_ndx].pr_target;
+ switch (pr_statp->pr_status) {
+ case PR_ACKED:
+ /*
+ * We received back an ACK, so the switch clearly
+ * is not dropping our traffic, and thus we can
+ * enable failure detection immediately.
+ */
+ if (pii->pii_fd_hrtime > gethrtime()) {
+ if (debug & D_PROBE) {
+ logdebug("successful probe on %s; "
+ "ending quiet period\n",
+ pii->pii_phyint->pi_name);
+ }
+ pii->pii_fd_hrtime = gethrtime();
+ }
+ break;
+
+ case PR_UNACKED:
+ assert(cur_tg != NULL);
+ /*
+ * The crtt could be zero for some reason,
+ * Eg. the phyint could be failed. If the crtt is
+ * not available use group's probe interval,
+ * which is a worst case estimate.
+ */
+ if (cur_tg->tg_crtt != 0) {
+ timeout = pr_statp->pr_time_sent +
+ cur_tg->tg_crtt;
+ } else {
+ timeout = pr_statp->pr_time_sent +
+ probe_interval;
+ }
+ if (TIME_LT(timeout, cur_time)) {
+ pr_statp->pr_status = PR_LOST;
+ pr_statp->pr_time_lost = timeout;
+ } else if (i == 1) {
+ /*
+ * We are forced to consider this probe
+ * lost, as we can have at most 2 unack.
+ * probes any time, and we will be sending a
+ * probe at the end of this function.
+ * Normally, we should not be here, but
+ * this can happen if an incoming response
+ * that was considered lost has increased
+ * the crtt for this target, and also bumped
+ * up the FDT. Note that we never cancel or
+ * increase the current pii_time_left, so
+ * when the timer fires, we find 2 valid
+ * unacked probes, and they are yet to timeout
+ */
+ pr_statp->pr_status = PR_LOST;
+ pr_statp->pr_time_lost = cur_time;
+ } else {
+ /*
+ * Only the most recent probe can enter
+ * this 'else' arm. The second most recent
+ * probe must take either of the above arms,
+ * if it is unacked.
+ */
+ valid_unack_count++;
+ }
+ break;
+ }
+ pr_ndx = PROBE_INDEX_PREV(pr_ndx);
+ }
+
+ /*
+ * We send out 1 probe randomly in the interval between one half
+ * and one probe interval for the group. Given that the CRTT is always
+ * less than the group's probe interval, we can have at most 1
+ * unacknowledged probe now. All previous probes are either lost or
+ * acked.
+ */
+ assert(valid_unack_count == 0 || valid_unack_count == 1);
+
+ /*
+ * The timer has fired. Take appropriate action depending
+ * on the current state of the phyint.
+ *
+ * PI_RUNNING state - Failure detection and failover
+ * PI_FAILED state - Repair detection and failback
+ */
+ switch (pii->pii_phyint->pi_state) {
+ case PI_FAILED:
+ /*
+ * If the most recent probe (excluding unacked probes that
+ * are yet to time out) has been acked, check whether the
+ * phyint is now repaired. If the phyint is repaired, then
+ * attempt failback, unless it is an inactive standby.
+ */
+ if (pii->pii_rack + valid_unack_count + 1 == pii->pii_snxt) {
+ phyint_check_for_repair(pii->pii_phyint);
+ }
+ break;
+
+ case PI_RUNNING:
+ /*
+ * It's possible our probes have been lost because of a
+ * spanning-tree mandated quiet period on the switch. If so,
+ * ignore the lost probes and consider the interface to still
+ * be functioning.
+ */
+ cur_hrtime = gethrtime();
+ if (pii->pii_fd_hrtime - cur_hrtime > 0)
+ break;
+
+ if (pii->pii_rack + valid_unack_count + 1 != pii->pii_snxt) {
+ /*
+ * We have 1 or more failed probes (excluding unacked
+ * probes that are yet to time out). Determine if the
+ * phyint has failed. If so attempt a failover,
+ * unless it is an inactive standby
+ */
+ phyint_inst_check_for_failure(pii);
+ }
+ break;
+
+ default:
+ logerr("phyint_inst_timer: invalid state %d\n",
+ pii->pii_phyint->pi_state);
+ abort();
+ }
+
+ /*
+ * Start the next probe. probe() will also set pii->pii_probe_time_left
+ * to the group's probe interval. If phyint_failed -> target_flush_hosts
+ * was called, the target list may be empty.
+ */
+ if (pii->pii_target_next != NULL) {
+ probe(pii, PROBE_UNI, cur_time);
+ /*
+ * If we have just the one probe target, and we're not using
+ * router targets, try to find another as we presently have
+ * no resilience.
+ */
+ if (!pii->pii_targets_are_routers && pii->pii_ntargets == 1)
+ probe(pii, PROBE_MULTI, cur_time);
+ } else {
+ probe(pii, PROBE_MULTI, cur_time);
+ }
+ return (interval);
+}
+
+/*
+ * Start the probe timer for an interface instance.
+ */
+void
+start_timer(struct phyint_instance *pii)
+{
+ uint32_t interval;
+
+ /*
+ * Spread the base probe times (pi_snxt_basetime) across phyints
+ * uniformly over the (curtime..curtime + the group's probe_interval).
+ * pi_snxt_basetime is strictly periodic with a frequency of
+ * the group's probe interval. The actual probe time pi_snxt_time
+ * adds some randomness to pi_snxt_basetime and happens in probe().
+ * For the 1st probe on each phyint after the timer is started,
+ * pi_snxt_time and pi_snxt_basetime are the same.
+ */
+ interval = GET_RANDOM(0,
+ (int)pii->pii_phyint->pi_group->pg_probeint);
+
+ pii->pii_snxt_basetime = getcurrenttime() + interval;
+ pii->pii_fd_snxt_basetime = pii->pii_snxt_basetime;
+ pii->pii_snxt_time = pii->pii_snxt_basetime;
+ timer_schedule(interval);
+}
+
+/*
+ * Restart the probe timer on an interface instance.
+ */
+static void
+restart_timer(struct phyint_instance *pii)
+{
+ /*
+ * We don't need to restart the timer if it was never started in
+ * the first place (pii->pii_basetime_inited not set), as the timer
+ * won't have gone off yet.
+ */
+ if (pii->pii_basetime_inited != 0) {
+
+ if (debug & D_LINKNOTE)
+ logdebug("restart timer: restarting timer on %s, "
+ "address family %s\n", pii->pii_phyint->pi_name,
+ AF_STR(pii->pii_af));
+
+ start_timer(pii);
+ }
+}
+
+static void
+process_link_state_down(struct phyint *pi)
+{
+ logerr("The link has gone down on %s\n", pi->pi_name);
+
+ /*
+ * Clear the probe statistics arrays, we don't want the repair
+ * detection logic relying on probes that were succesful prior
+ * to the link going down.
+ */
+ if (PROBE_CAPABLE(pi->pi_v4))
+ clear_pii_probe_stats(pi->pi_v4);
+ if (PROBE_CAPABLE(pi->pi_v6))
+ clear_pii_probe_stats(pi->pi_v6);
+ /*
+ * Check for interface failure. Although we know the interface
+ * has failed, we don't know if all the other interfaces in the
+ * group have failed as well.
+ */
+ if ((pi->pi_state == PI_RUNNING) ||
+ (pi->pi_state != PI_FAILED && !GROUP_FAILED(pi->pi_group))) {
+ if (debug & D_LINKNOTE) {
+ logdebug("process_link_state_down:"
+ " checking for failure on %s\n", pi->pi_name);
+ }
+
+ if (pi->pi_v4 != NULL)
+ phyint_inst_check_for_failure(pi->pi_v4);
+ else if (pi->pi_v6 != NULL)
+ phyint_inst_check_for_failure(pi->pi_v6);
+ }
+}
+
+static void
+process_link_state_up(struct phyint *pi)
+{
+ logerr("The link has come up on %s\n", pi->pi_name);
+
+ /*
+ * We stopped any running timers on each instance when the link
+ * went down, so restart them.
+ */
+ if (pi->pi_v4)
+ restart_timer(pi->pi_v4);
+ if (pi->pi_v6)
+ restart_timer(pi->pi_v6);
+
+ phyint_check_for_repair(pi);
+
+ pi->pi_whenup[pi->pi_whendx++] = getcurrenttime();
+ if (pi->pi_whendx == LINK_UP_PERMIN)
+ pi->pi_whendx = 0;
+}
+
+/*
+ * Process any changes in link state passed up from the interfaces.
+ */
+void
+process_link_state_changes(void)
+{
+ struct phyint *pi;
+
+ /* Look for interfaces where the link state has just changed */
+
+ for (pi = phyints; pi != NULL; pi = pi->pi_next) {
+ boolean_t old_link_state_up = LINK_UP(pi);
+
+ /*
+ * Except when the "phyint" structure is created, this is
+ * the only place the link state is updated. This allows
+ * this routine to detect changes in link state, rather
+ * than just the current state.
+ */
+ UPDATE_LINK_STATE(pi);
+
+ if (LINK_DOWN(pi)) {
+ /*
+ * Has link just gone down?
+ */
+ if (old_link_state_up)
+ process_link_state_down(pi);
+ } else {
+ /*
+ * Has link just gone back up?
+ */
+ if (!old_link_state_up)
+ process_link_state_up(pi);
+ }
+ }
+}
+
+void
+reset_crtt_all(struct phyint *pi)
+{
+ struct phyint_instance *pii;
+ struct target *tg;
+
+ pii = pi->pi_v4;
+ if (pii != NULL) {
+ for (tg = pii->pii_targets; tg != NULL; tg = tg->tg_next) {
+ tg->tg_crtt = 0;
+ tg->tg_rtt_sa = -1;
+ tg->tg_rtt_sd = 0;
+ }
+ }
+
+ pii = pi->pi_v6;
+ if (pii != NULL) {
+ for (tg = pii->pii_targets; tg != NULL; tg = tg->tg_next) {
+ tg->tg_crtt = 0;
+ tg->tg_rtt_sa = -1;
+ tg->tg_rtt_sd = 0;
+ }
+ }
+}
+
+/*
+ * Check if the phyint has failed the last NUM_PROBE_FAILS consecutive
+ * probes on both instances IPv4 and IPv6.
+ * If the interface has failed, return the time of the first probe failure
+ * in "tff".
+ */
+static int
+phyint_inst_probe_failure_state(struct phyint_instance *pii, uint_t *tff)
+{
+ uint_t pi_tff;
+ struct target *cur_tg;
+ struct probe_fail_count pfinfo;
+ struct phyint_instance *pii_other;
+ int pr_ndx;
+
+ /*
+ * Get the number of consecutive failed probes on
+ * this phyint across all targets. Also get the number
+ * of consecutive failed probes on this target only
+ */
+ pr_ndx = PROBE_INDEX_PREV(pii->pii_probe_next);
+ cur_tg = pii->pii_probes[pr_ndx].pr_target;
+ probe_fail_info(pii, cur_tg, &pfinfo);
+
+ /* Get the time of first failure, for later use */
+ pi_tff = pfinfo.pf_tff;
+
+ /*
+ * If the current target has not responded to the
+ * last NUM_PROBE_FAILS probes, and other targets are
+ * responding delete this target. Dead gateway detection
+ * will eventually remove this target (if router) from the
+ * routing tables. If that does not occur, we may end
+ * up adding this to our list again.
+ */
+ if (pfinfo.pf_nfail < NUM_PROBE_FAILS &&
+ pfinfo.pf_nfail_tg >= NUM_PROBE_FAILS) {
+ if (pii->pii_targets_are_routers) {
+ if (cur_tg->tg_status == TG_ACTIVE)
+ pii->pii_ntargets--;
+ cur_tg->tg_status = TG_DEAD;
+ cur_tg->tg_crtt = 0;
+ cur_tg->tg_rtt_sa = -1;
+ cur_tg->tg_rtt_sd = 0;
+ if (pii->pii_target_next == cur_tg)
+ pii->pii_target_next = target_next(cur_tg);
+ } else {
+ target_delete(cur_tg);
+ probe(pii, PROBE_MULTI, getcurrenttime());
+ }
+ return (PHYINT_OK);
+ }
+
+ /*
+ * If the phyint has lost NUM_PROBE_FAILS or more
+ * consecutive probes, on both IPv4 and IPv6 protocol
+ * instances of the phyint, then trigger failure
+ * detection, else return false
+ */
+ if (pfinfo.pf_nfail < NUM_PROBE_FAILS)
+ return (PHYINT_OK);
+
+ pii_other = phyint_inst_other(pii);
+ if (PROBE_CAPABLE(pii_other)) {
+ probe_fail_info(pii_other, NULL, &pfinfo);
+ if (pfinfo.pf_nfail >= NUM_PROBE_FAILS) {
+ /*
+ * We have NUM_PROBE_FAILS or more failures
+ * on both IPv4 and IPv6. Get the earliest
+ * time when failure was detected on this
+ * phyint across IPv4 and IPv6.
+ */
+ if (TIME_LT(pfinfo.pf_tff, pi_tff))
+ pi_tff = pfinfo.pf_tff;
+ } else {
+ /*
+ * This instance has < NUM_PROBE_FAILS failure.
+ * So return false
+ */
+ return (PHYINT_OK);
+ }
+ }
+ *tff = pi_tff;
+ return (PHYINT_FAILURE);
+}
+
+/*
+ * Check if the link has gone down on this phyint, or it has failed the
+ * last NUM_PROBE_FAILS consecutive probes on both instances IPv4 and IPv6.
+ * Also look at other phyints of this group, for group failures.
+ */
+int
+failure_state(struct phyint_instance *pii)
+{
+ struct probe_success_count psinfo;
+ uint_t pi2_tls; /* time last success */
+ uint_t pi_tff; /* time first fail */
+ struct phyint *pi2;
+ struct phyint *pi;
+ struct phyint_instance *pii2;
+ struct phyint_group *pg;
+ boolean_t alone;
+
+ if (debug & D_FAILOVER)
+ logdebug("phyint_failed(%s)\n", pii->pii_name);
+
+ pi = pii->pii_phyint;
+ pg = pi->pi_group;
+
+ if (LINK_UP(pi) && phyint_inst_probe_failure_state(pii, &pi_tff) ==
+ PHYINT_OK)
+ return (PHYINT_OK);
+
+ /*
+ * At this point, the link is down, or the phyint is suspect,
+ * as it has lost NUM_PROBE_FAILS or more probes. If the phyint
+ * does not belong to any group, or is the only member of the
+ * group capable of being probed, return PHYINT_FAILURE.
+ */
+ alone = _B_TRUE;
+ if (pg != phyint_anongroup) {
+ for (pi2 = pg->pg_phyint; pi2 != NULL; pi2 = pi2->pi_pgnext) {
+ if (pi2 == pi)
+ continue;
+ if (PROBE_CAPABLE(pi2->pi_v4) ||
+ PROBE_CAPABLE(pi2->pi_v6)) {
+ alone = _B_FALSE;
+ break;
+ }
+ }
+ }
+ if (alone)
+ return (PHYINT_FAILURE);
+
+ /*
+ * Need to compare against other phyints of the same group
+ * to exclude group failures. If the failure was detected via
+ * probing, then if the time of last success (tls) of any
+ * phyint is more recent than the time of first fail (tff) of the
+ * phyint in question, and the link is up on the phyint,
+ * then it is a phyint failure. Otherwise it is a group failure.
+ * If failure was detected via a link down notification sent from
+ * the driver to IP, we see if any phyints in the group are still
+ * running and haven't received a link down notification. We
+ * will usually be processing the link down notification shortly
+ * after it was received, so there is no point looking at the tls
+ * of other phyints.
+ */
+ for (pi2 = pg->pg_phyint; pi2 != NULL; pi2 = pi2->pi_pgnext) {
+ /* Exclude ourself from comparison */
+ if (pi2 == pi)
+ continue;
+
+ if (LINK_DOWN(pi)) {
+ /*
+ * We use FLAGS_TO_LINK_STATE() to test the
+ * flags directly, rather then LINK_UP() or
+ * LINK_DOWN(), as we may not have got round
+ * to processing the link state for the other
+ * phyints in the group yet.
+ *
+ * The check for PI_RUNNING and group
+ * failure handles the case when the
+ * group begins to recover. The first
+ * phyint to recover should not trigger
+ * a failover from the soon-to-recover
+ * other phyints to the first recovered
+ * phyint. PI_RUNNING will be set, and
+ * pg_groupfailed cleared only after
+ * receipt of NUM_PROBE_REPAIRS, by
+ * which time the other phyints should
+ * have received at least 1 packet,
+ * and so will not have NUM_PROBE_FAILS.
+ */
+ if ((pi2->pi_state == PI_RUNNING) &&
+ !GROUP_FAILED(pg) && FLAGS_TO_LINK_STATE(pi2))
+ return (PHYINT_FAILURE);
+ } else {
+ /*
+ * Need to compare against both IPv4 and
+ * IPv6 instances.
+ */
+ pii2 = pi2->pi_v4;
+ if (pii2 != NULL) {
+ probe_success_info(pii2, NULL, &psinfo);
+ if (psinfo.ps_tls_valid) {
+ pi2_tls = psinfo.ps_tls;
+ /*
+ * See comment above regarding check
+ * for PI_RUNNING and group failure.
+ */
+ if (TIME_GT(pi2_tls, pi_tff) &&
+ (pi2->pi_state == PI_RUNNING) &&
+ !GROUP_FAILED(pg) &&
+ FLAGS_TO_LINK_STATE(pi2))
+ return (PHYINT_FAILURE);
+ }
+ }
+
+ pii2 = pi2->pi_v6;
+ if (pii2 != NULL) {
+ probe_success_info(pii2, NULL, &psinfo);
+ if (psinfo.ps_tls_valid) {
+ pi2_tls = psinfo.ps_tls;
+ /*
+ * See comment above regarding check
+ * for PI_RUNNING and group failure.
+ */
+ if (TIME_GT(pi2_tls, pi_tff) &&
+ (pi2->pi_state == PI_RUNNING) &&
+ !GROUP_FAILED(pg) &&
+ FLAGS_TO_LINK_STATE(pi2))
+ return (PHYINT_FAILURE);
+ }
+ }
+ }
+ }
+
+ /*
+ * Change the group state to PG_FAILED if it's not already.
+ */
+ if (!GROUP_FAILED(pg))
+ phyint_group_chstate(pg, PG_FAILED);
+
+ return (GROUP_FAILURE);
+}
+
+/*
+ * Return the information associated with consecutive probe successes
+ * starting with the most recent probe. At most the last 2 probes can be
+ * in the unacknowledged state. All previous probes have either failed
+ * or succeeded.
+ */
+static void
+probe_success_info(struct phyint_instance *pii, struct target *cur_tg,
+ struct probe_success_count *psinfo)
+{
+ uint_t i;
+ struct probe_stats *pr_statp;
+ uint_t most_recent;
+ uint_t second_most_recent;
+ boolean_t pi_found_failure = _B_FALSE;
+ boolean_t tg_found_failure = _B_FALSE;
+ uint_t now;
+ uint_t timeout;
+ struct target *tg;
+
+ if (debug & D_FAILOVER)
+ logdebug("probe_success_info(%s)\n", pii->pii_name);
+
+ bzero(psinfo, sizeof (*psinfo));
+ now = getcurrenttime();
+
+ /*
+ * Start with the most recent probe, and count the number
+ * of consecutive probe successes. Latch the number of successes
+ * on hitting a failure.
+ */
+ most_recent = PROBE_INDEX_PREV(pii->pii_probe_next);
+ second_most_recent = PROBE_INDEX_PREV(most_recent);
+
+ for (i = most_recent; i != pii->pii_probe_next;
+ i = PROBE_INDEX_PREV(i)) {
+ pr_statp = &pii->pii_probes[i];
+
+ switch (pr_statp->pr_status) {
+ case PR_UNACKED:
+ /*
+ * Only the most recent 2 probes can be unacknowledged
+ */
+ assert(i == most_recent || i == second_most_recent);
+
+ tg = pr_statp->pr_target;
+ assert(tg != NULL);
+ /*
+ * The crtt could be zero for some reason,
+ * Eg. the phyint could be failed. If the crtt is
+ * not available use the value of the group's probe
+ * interval which is a worst case estimate.
+ */
+ if (tg->tg_crtt != 0) {
+ timeout = pr_statp->pr_time_sent + tg->tg_crtt;
+ } else {
+ timeout = pr_statp->pr_time_sent +
+ pii->pii_phyint->pi_group->pg_probeint;
+ }
+
+ if (TIME_LT(timeout, now)) {
+ /*
+ * We hit a failure. Latch the total number of
+ * recent consecutive successes.
+ */
+ pr_statp->pr_time_lost = timeout;
+ pr_statp->pr_status = PR_LOST;
+ pi_found_failure = _B_TRUE;
+ if (cur_tg != NULL && tg == cur_tg) {
+ /*
+ * We hit a failure for the desired
+ * target. Latch the number of recent
+ * consecutive successes for this target
+ */
+ tg_found_failure = _B_TRUE;
+ }
+ }
+ break;
+
+ case PR_ACKED:
+ /*
+ * Bump up the count of probe successes, if we
+ * have not seen any failure so far.
+ */
+ if (!pi_found_failure)
+ psinfo->ps_nsucc++;
+
+ if (cur_tg != NULL && pr_statp->pr_target == cur_tg &&
+ !tg_found_failure) {
+ psinfo->ps_nsucc_tg++;
+ }
+
+ /*
+ * Record the time of last success, if this is
+ * the most recent probe success.
+ */
+ if (!psinfo->ps_tls_valid) {
+ psinfo->ps_tls = pr_statp->pr_time_acked;
+ psinfo->ps_tls_valid = _B_TRUE;
+ }
+ break;
+
+ case PR_LOST:
+ /*
+ * We hit a failure. Latch the total number of
+ * recent consecutive successes.
+ */
+ pi_found_failure = _B_TRUE;
+ if (cur_tg != NULL && pr_statp->pr_target == cur_tg) {
+ /*
+ * We hit a failure for the desired target.
+ * Latch the number of recent consecutive
+ * successes for this target
+ */
+ tg_found_failure = _B_TRUE;
+ }
+ break;
+
+ default:
+ return;
+
+ }
+ }
+}
+
+/*
+ * Return the information associated with consecutive probe failures
+ * starting with the most recent probe. Only the last 2 probes can be in the
+ * unacknowledged state. All previous probes have either failed or succeeded.
+ */
+static void
+probe_fail_info(struct phyint_instance *pii, struct target *cur_tg,
+ struct probe_fail_count *pfinfo)
+{
+ int i;
+ struct probe_stats *pr_statp;
+ boolean_t tg_found_success = _B_FALSE;
+ boolean_t pi_found_success = _B_FALSE;
+ int most_recent;
+ int second_most_recent;
+ uint_t now;
+ uint_t timeout;
+ struct target *tg;
+
+ if (debug & D_FAILOVER)
+ logdebug("probe_fail_info(%s)\n", pii->pii_name);
+
+ bzero(pfinfo, sizeof (*pfinfo));
+ now = getcurrenttime();
+
+ /*
+ * Start with the most recent probe, and count the number
+ * of consecutive probe failures. Latch the number of failures
+ * on hitting a probe success.
+ */
+ most_recent = PROBE_INDEX_PREV(pii->pii_probe_next);
+ second_most_recent = PROBE_INDEX_PREV(most_recent);
+
+ for (i = most_recent; i != pii->pii_probe_next;
+ i = PROBE_INDEX_PREV(i)) {
+ pr_statp = &pii->pii_probes[i];
+
+ assert(PR_STATUS_VALID(pr_statp->pr_status));
+
+ switch (pr_statp->pr_status) {
+ case PR_UNACKED:
+ /*
+ * Only the most recent 2 probes can be unacknowledged
+ */
+ assert(i == most_recent || i == second_most_recent);
+
+ tg = pr_statp->pr_target;
+ /*
+ * Target is guaranteed to exist in the unack. state
+ */
+ assert(tg != NULL);
+ /*
+ * The crtt could be zero for some reason,
+ * Eg. the phyint could be failed. If the crtt is
+ * not available use the group's probe interval,
+ * which is a worst case estimate.
+ */
+ if (tg->tg_crtt != 0) {
+ timeout = pr_statp->pr_time_sent + tg->tg_crtt;
+ } else {
+ timeout = pr_statp->pr_time_sent +
+ pii->pii_phyint->pi_group->pg_probeint;
+ }
+
+ if (TIME_GT(timeout, now))
+ break;
+
+ pr_statp->pr_time_lost = timeout;
+ pr_statp->pr_status = PR_LOST;
+ /* FALLTHRU */
+
+ case PR_LOST:
+ if (!pi_found_success) {
+ pfinfo->pf_nfail++;
+ pfinfo->pf_tff = pr_statp->pr_time_lost;
+ }
+ if (cur_tg != NULL && pr_statp->pr_target == cur_tg &&
+ !tg_found_success) {
+ pfinfo->pf_nfail_tg++;
+ }
+ break;
+
+ default:
+ /*
+ * We hit a success or unused slot. Latch the
+ * total number of recent consecutive failures.
+ */
+ pi_found_success = _B_TRUE;
+ if (cur_tg != NULL && pr_statp->pr_target == cur_tg) {
+ /*
+ * We hit a success for the desired target.
+ * Latch the number of recent consecutive
+ * failures for this target
+ */
+ tg_found_success = _B_TRUE;
+ }
+ }
+ }
+}
+
+/*
+ * Check if the phyint has been repaired. If no test address has been
+ * configured, then consider the interface repaired if the link is up (unless
+ * the link is flapping; see below). Otherwise, look for proof of probes
+ * being sent and received. If last NUM_PROBE_REPAIRS probes are fine on
+ * either IPv4 or IPv6 instance, the phyint can be considered repaired.
+ */
+static boolean_t
+phyint_repaired(struct phyint *pi)
+{
+ struct probe_success_count psinfo;
+ struct phyint_instance *pii;
+ struct target *cur_tg;
+ int pr_ndx;
+ uint_t cur_time;
+
+ if (debug & D_FAILOVER)
+ logdebug("phyint_repaired(%s)\n", pi->pi_name);
+
+ if (LINK_DOWN(pi))
+ return (_B_FALSE);
+
+ /*
+ * If we don't have any test addresses and the link is up, then
+ * consider the interface repaired, unless we've received more than
+ * LINK_UP_PERMIN link up notifications in the last minute, in
+ * which case we keep the link down until we drop back below
+ * the threshold.
+ */
+ if (!PROBE_ENABLED(pi->pi_v4) && !PROBE_ENABLED(pi->pi_v6)) {
+ cur_time = getcurrenttime();
+ if ((pi->pi_whenup[pi->pi_whendx] == 0 ||
+ (cur_time - pi->pi_whenup[pi->pi_whendx]) > MSEC_PERMIN)) {
+ pi->pi_lfmsg_printed = 0;
+ return (_B_TRUE);
+ }
+ if (!pi->pi_lfmsg_printed) {
+ logerr("The link has come up on %s more than %d times "
+ "in the last minute; disabling failback until it "
+ "stabilizes\n", pi->pi_name, LINK_UP_PERMIN);
+ pi->pi_lfmsg_printed = 1;
+ }
+
+ return (_B_FALSE);
+ }
+
+ pii = pi->pi_v4;
+ if (PROBE_CAPABLE(pii)) {
+ pr_ndx = PROBE_INDEX_PREV(pii->pii_probe_next);
+ cur_tg = pii->pii_probes[pr_ndx].pr_target;
+ probe_success_info(pii, cur_tg, &psinfo);
+ if (psinfo.ps_nsucc >= NUM_PROBE_REPAIRS ||
+ psinfo.ps_nsucc_tg >= NUM_PROBE_REPAIRS)
+ return (_B_TRUE);
+ }
+
+ pii = pi->pi_v6;
+ if (PROBE_CAPABLE(pii)) {
+ pr_ndx = PROBE_INDEX_PREV(pii->pii_probe_next);
+ cur_tg = pii->pii_probes[pr_ndx].pr_target;
+ probe_success_info(pii, cur_tg, &psinfo);
+ if (psinfo.ps_nsucc >= NUM_PROBE_REPAIRS ||
+ psinfo.ps_nsucc_tg >= NUM_PROBE_REPAIRS)
+ return (_B_TRUE);
+ }
+
+ return (_B_FALSE);
+}
+
+/*
+ * Try failover from phyint 'pi' to a suitable destination.
+ */
+int
+try_failover(struct phyint *pi, int failover_type)
+{
+ struct phyint *dst;
+ int err;
+
+ if (debug & D_FAILOVER)
+ logdebug("try_failover(%s %d)\n", pi->pi_name, failover_type);
+
+ /*
+ * Attempt to find a failover destination 'dst'.
+ * dst will be null if any of the following is true
+ * Phyint is not part of a group OR
+ * Phyint is the only member of a group OR
+ * No suitable failover dst was available
+ */
+ dst = get_failover_dst(pi, failover_type);
+ if (dst == NULL)
+ return (IPMP_EMINRED);
+
+ dst->pi_empty = 0; /* Per state diagram */
+ pi->pi_full = 0; /* Per state diagram */
+
+ err = failover(pi, dst);
+
+ if (debug & D_FAILOVER) {
+ logdebug("failed over from %s to %s ret %d\n",
+ pi->pi_name, dst->pi_name, err);
+ }
+ if (err == 0) {
+ pi->pi_empty = 1; /* Per state diagram */
+ /*
+ * we don't want to print out this message if a
+ * phyint is leaving the group, nor for failover from
+ * standby
+ */
+ if (failover_type == FAILOVER_NORMAL) {
+ logerr("Successfully failed over from NIC %s to NIC "
+ "%s\n", pi->pi_name, dst->pi_name);
+ }
+ return (0);
+ } else {
+ /*
+ * The failover did not succeed. We must retry the failover
+ * only after resyncing our state based on the kernel's.
+ * For eg. either the src or the dst might have been unplumbed
+ * causing this failure. initifs() will be called again,
+ * from main, since full_scan_required has been set to true
+ * by failover();
+ */
+ return (IPMP_FAILURE);
+ }
+}
+
+/*
+ * global_errno captures the errno value, if failover() or failback()
+ * fails. This is sent to if_mpadm(1M).
+ */
+int global_errno;
+
+/*
+ * Attempt failover from phyint 'from' to phyint 'to'.
+ * IP moves everything from phyint 'from' to phyint 'to'.
+ */
+static int
+failover(struct phyint *from, struct phyint *to)
+{
+ struct lifreq lifr;
+ int ret;
+
+ if (debug & D_FAILOVER) {
+ logdebug("failing over from %s to %s\n",
+ from->pi_name, to->pi_name);
+ }
+
+ /*
+ * Perform the failover. Both IPv4 and IPv6 are failed over
+ * using a single ioctl by passing in AF_UNSPEC family.
+ */
+ lifr.lifr_addr.ss_family = AF_UNSPEC;
+ (void) strncpy(lifr.lifr_name, from->pi_name, sizeof (lifr.lifr_name));
+ lifr.lifr_movetoindex = to->pi_ifindex;
+
+ ret = ioctl(ifsock_v4, SIOCLIFFAILOVER, (caddr_t)&lifr);
+ if (ret < 0) {
+ global_errno = errno;
+ logperror("failover: ioctl (failover)");
+ }
+
+ /*
+ * Set full_scan_required to true. This will make us read
+ * the state from the kernel in initifs() and update our tables,
+ * to reflect the current state after the failover. If the
+ * failover has failed it will then reissue the failover.
+ */
+ full_scan_required = _B_TRUE;
+ return (ret);
+}
+
+/*
+ * phyint 'pi' has recovered. Attempt failback from every phyint in the same
+ * group as phyint 'pi' that is a potential failback source, to phyint 'pi'.
+ * Return values:
+ * IPMP_SUCCESS: Failback successful from each of the other
+ * phyints in the group.
+ * IPMP_EFBPARTIAL: Failback successful from some of the other
+ * phyints in the group.
+ * IPMP_FAILURE: Failback syscall failed with some error.
+ *
+ * Note that failback is attempted regardless of the setting of the
+ * failback_enabled flag.
+ */
+int
+do_failback(struct phyint *pi, boolean_t check_only)
+{
+ struct phyint *from;
+ boolean_t done;
+ boolean_t partial;
+ boolean_t attempted_failback = _B_FALSE;
+
+ if (debug & D_FAILOVER)
+ logdebug("do_failback(%s)\n", pi->pi_name);
+
+ /* If this phyint is not part of a named group, return. */
+ if (pi->pi_group == phyint_anongroup) {
+ pi->pi_full = 1;
+ return (IPMP_SUCCESS);
+ }
+
+ /*
+ * Attempt failback from every phyint in the group to 'pi'.
+ * The reason for doing this, instead of only from the
+ * phyint to which we did the failover is given below.
+ *
+ * After 'pi' failed, if any app. tries to join on a multicast
+ * address (IPv6), on the failed phyint, IP picks any arbitrary
+ * non-failed phyint in the group, instead of the failed phyint,
+ * in.mpathd is not aware of this. Thus failing back only from the
+ * interface to which 'pi' failed over, will failback the ipif's
+ * but not the ilm's. So we need to failback from all members of
+ * the phyint group
+ */
+ done = _B_TRUE;
+ partial = _B_FALSE;
+ for (from = pi->pi_group->pg_phyint; from != NULL;
+ from = from->pi_pgnext) {
+ /* Exclude ourself as a failback src */
+ if (from == pi)
+ continue;
+
+ /*
+ * If the 'from' phyint has IPv4 plumbed, the 'to'
+ * phyint must also have IPv4 plumbed. Similar check
+ * for IPv6. IP makes the same check. Otherwise the
+ * failback will fail.
+ */
+ if ((from->pi_v4 != NULL && pi->pi_v4 == NULL) ||
+ (from->pi_v6 != NULL && pi->pi_v6 == NULL)) {
+ partial = _B_TRUE;
+ continue;
+ }
+
+ if (!check_only) {
+ pi->pi_empty = 0; /* Per state diagram */
+ attempted_failback = _B_TRUE;
+ if (failback(from, pi) != 0) {
+ done = _B_FALSE;
+ break;
+ }
+ }
+ }
+
+ if (check_only) {
+ return (partial ? IPMP_EFBPARTIAL : IPMP_SUCCESS);
+ }
+
+ /*
+ * We are done. No more phyint from which we can src the failback
+ */
+ if (done) {
+ if (!partial)
+ pi->pi_full = 1; /* Per state diagram */
+ /*
+ * Don't print out a message unless there is a
+ * transition from FAILED to RUNNING. For eg.
+ * we don't want to print out this message if a
+ * phyint is leaving the group, or at startup
+ */
+ if (attempted_failback && (pi->pi_flags &
+ (IFF_FAILED | IFF_OFFLINE))) {
+ logerr("Successfully failed back to NIC %s\n",
+ pi->pi_name);
+ }
+ return (partial ? IPMP_EFBPARTIAL : IPMP_SUCCESS);
+ }
+
+ return (IPMP_FAILURE);
+}
+
+/*
+ * This function is similar to do_failback() above, but respects the
+ * failback_enabled flag for phyints in named groups.
+ */
+int
+try_failback(struct phyint *pi, boolean_t check_only)
+{
+ if (debug & D_FAILOVER)
+ logdebug("try_failback(%s)\n", pi->pi_name);
+
+ if (pi->pi_group != phyint_anongroup && !failback_enabled)
+ return (IPMP_EFBDISABLED);
+
+ return (do_failback(pi, check_only));
+}
+
+/*
+ * Failback everything from phyint 'from' that has the same ifindex
+ * as phyint to's ifindex.
+ */
+static int
+failback(struct phyint *from, struct phyint *to)
+{
+ struct lifreq lifr;
+ int ret;
+
+ if (debug & D_FAILOVER)
+ logdebug("failback(%s %s)\n", from->pi_name, to->pi_name);
+
+ lifr.lifr_addr.ss_family = AF_UNSPEC;
+ (void) strncpy(lifr.lifr_name, from->pi_name, sizeof (lifr.lifr_name));
+ lifr.lifr_movetoindex = to->pi_ifindex;
+
+ ret = ioctl(ifsock_v4, SIOCLIFFAILBACK, (caddr_t)&lifr);
+ if (ret < 0) {
+ global_errno = errno;
+ logperror("failback: ioctl (failback)");
+ }
+
+ /*
+ * Set full_scan_required to true. This will make us read
+ * the state from the kernel in initifs() and update our tables,
+ * to reflect the current state after the failback. If the
+ * failback has failed it will then reissue the failback.
+ */
+ full_scan_required = _B_TRUE;
+
+ return (ret);
+}
+
+/*
+ * Select a target phyint for failing over from 'pi'.
+ * In the normal case i.e. failover_type is FAILOVER_NORMAL, the preferred
+ * target phyint is chosen as follows,
+ * 1. Pick any inactive standby interface.
+ * 2. If no inactive standby is available, select any phyint in the
+ * same group that has the least number of logints, (excluding
+ * IFF_NOFAILOVER and !IFF_UP logints)
+ * If we are failing over from a standby, failover_type is
+ * FAILOVER_TO_NONSTANDBY, and we won't pick a standby for the destination.
+ * If a phyint is leaving the group, then failover_type is FAILOVER_TO_ANY,
+ * and we won't return NULL, as long as there is at least 1 other phyint
+ * in the group.
+ */
+static struct phyint *
+get_failover_dst(struct phyint *pi, int failover_type)
+{
+ struct phyint *maybe = NULL;
+ struct phyint *pi2;
+ struct phyint *last_choice = NULL;
+
+ if (pi->pi_group == phyint_anongroup)
+ return (NULL);
+
+ /*
+ * Loop thru the phyints in the group, and pick the preferred
+ * phyint for the target.
+ */
+ for (pi2 = pi->pi_group->pg_phyint; pi2 != NULL; pi2 = pi2->pi_pgnext) {
+ /* Exclude ourself and offlined interfaces */
+ if (pi2 == pi || pi2->pi_state == PI_OFFLINE)
+ continue;
+
+ /*
+ * The chosen target phyint must have IPv4 instance
+ * plumbed, if the src phyint has IPv4 plumbed. Similarly
+ * for IPv6.
+ */
+ if ((pi2->pi_v4 == NULL && pi->pi_v4 != NULL) ||
+ (pi2->pi_v6 == NULL && pi->pi_v6 != NULL))
+ continue;
+
+ /* The chosen target must be PI_RUNNING. */
+ if (pi2->pi_state != PI_RUNNING) {
+ last_choice = pi2;
+ continue;
+ }
+
+ if ((pi2->pi_flags & IFF_INACTIVE) &&
+ (failover_type != FAILOVER_TO_NONSTANDBY)) {
+ return (pi2);
+ } else {
+ if (maybe == NULL)
+ maybe = pi2;
+ else if (logint_upcount(pi2) < logint_upcount(maybe))
+ maybe = pi2;
+ }
+ }
+ if (maybe == NULL && failover_type == FAILOVER_TO_ANY)
+ return (last_choice);
+ else
+ return (maybe);
+}
+
+/*
+ * Used to set/clear phyint flags, by making a SIOCSLIFFLAGS call.
+ */
+boolean_t
+change_lif_flags(struct phyint *pi, uint64_t flags, boolean_t setfl)
+{
+ int ifsock;
+ struct lifreq lifr;
+
+ if (debug & D_FAILOVER) {
+ logdebug("change_lif_flags(%s): flags %llx setfl %d\n",
+ pi->pi_name, flags, (int)setfl);
+ }
+
+ if (pi->pi_v4 != NULL) {
+ ifsock = ifsock_v4;
+ } else {
+ ifsock = ifsock_v6;
+ }
+
+ /*
+ * Get the current flags from the kernel, and set/clear the
+ * desired phyint flags. Since we set only phyint flags, we can
+ * do it on either IPv4 or IPv6 instance.
+ */
+ (void) strncpy(lifr.lifr_name, pi->pi_name, sizeof (lifr.lifr_name));
+ lifr.lifr_name[sizeof (lifr.lifr_name) - 1] = '\0';
+ if (ioctl(ifsock, SIOCGLIFFLAGS, (char *)&lifr) < 0) {
+ if (errno != ENXIO)
+ logperror("change_lif_flags: ioctl (get flags)");
+ return (_B_FALSE);
+ }
+ if (setfl)
+ lifr.lifr_flags |= flags;
+ else
+ lifr.lifr_flags &= ~flags;
+ if (ioctl(ifsock, SIOCSLIFFLAGS, (char *)&lifr) < 0) {
+ if (errno != ENXIO)
+ logperror("change_lif_flags: ioctl (set flags)");
+ return (_B_FALSE);
+ }
+
+ /*
+ * Keep pi_flags in synch. with actual flags. Assumes flags are
+ * phyint flags.
+ */
+ if (setfl)
+ pi->pi_flags |= flags;
+ else
+ pi->pi_flags &= ~flags;
+
+ if (pi->pi_v4)
+ pi->pi_v4->pii_flags = pi->pi_flags;
+
+ if (pi->pi_v6)
+ pi->pi_v6->pii_flags = pi->pi_flags;
+
+ return (_B_TRUE);
+}
+
+/*
+ * icmp cksum computation for IPv4.
+ */
+static int
+in_cksum(ushort_t *addr, int len)
+{
+ register int nleft = len;
+ register ushort_t *w = addr;
+ register ushort_t answer;
+ ushort_t odd_byte = 0;
+ register int sum = 0;
+
+ /*
+ * Our algorithm is simple, using a 32 bit accumulator (sum),
+ * we add sequential 16 bit words to it, and at the end, fold
+ * back all the carry bits from the top 16 bits into the lower
+ * 16 bits.
+ */
+ while (nleft > 1) {
+ sum += *w++;
+ nleft -= 2;
+ }
+
+ /* mop up an odd byte, if necessary */
+ if (nleft == 1) {
+ *(uchar_t *)(&odd_byte) = *(uchar_t *)w;
+ sum += odd_byte;
+ }
+
+ /*
+ * add back carry outs from top 16 bits to low 16 bits
+ */
+ sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
+ sum += (sum >> 16); /* add carry */
+ answer = ~sum; /* truncate to 16 bits */
+ return (answer);
+}
+
+static void
+reset_snxt_basetimes(void)
+{
+ struct phyint_instance *pii;
+
+ for (pii = phyint_instances; pii != NULL; pii = pii->pii_next) {
+ pii->pii_fd_snxt_basetime = pii->pii_snxt_basetime;
+ }
+}
+
+/*
+ * Is the address one of our own addresses? Unfortunately,
+ * we cannot check our phyint tables to determine if the address
+ * is our own. This is because, we don't track interfaces that
+ * are not part of any group. We have to either use a 'bind' or
+ * get the complete list of all interfaces using SIOCGLIFCONF,
+ * to do this check. We choose to use 'bind'. We could use
+ * SIOCTMYADDR, but bind is preferred, since it is stronger.
+ * SIOCTMYADDR excludes down interfaces, while bind includes even
+ * down interfaces.
+ */
+boolean_t
+own_address(int af, struct in6_addr addr)
+{
+ int sock;
+ boolean_t ours = _B_TRUE;
+
+ sock = socket(AF_INET6, SOCK_DGRAM, 0);
+ if (sock == -1) {
+ logperror("own_address: socket");
+ /*
+ * If the socket call fails, err on the side of caution,
+ * and return true.
+ */
+ } else {
+ struct sockaddr_in6 sin6;
+
+ (void) memset(&sin6, 0, sizeof (struct sockaddr_in6));
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_addr = addr;
+ /*
+ * If the bind succeeds, then this address is one of our
+ * addresses.
+ * If bind returns error EADDRNOTAVAIL, the address is
+ * not one of ours.
+ * If bind returns an error other than EADDRNOTAVAIL, err
+ * on the side of caution and report the address as one of
+ * our own.
+ */
+ if (bind(sock, (struct sockaddr *)&sin6,
+ sizeof (struct sockaddr_in6)) == -1) {
+ if (errno == EADDRNOTAVAIL)
+ ours = _B_FALSE;
+ else
+ logperror("own_address: bind");
+ }
+ (void) close(sock);
+ }
+ if (debug & D_TARGET) {
+ char abuf[INET6_ADDRSTRLEN];
+
+ logdebug("own_address: addr %s is %s ours\n",
+ pr_addr(af, addr, abuf, sizeof (abuf)),
+ ours ? "one of" : "not");
+ }
+ return (ours);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.mpathd/mpd_tables.c b/usr/src/cmd/cmd-inet/usr.lib/in.mpathd/mpd_tables.c
new file mode 100644
index 0000000000..244566efb7
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.mpathd/mpd_tables.c
@@ -0,0 +1,2665 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "mpd_defs.h"
+#include "mpd_tables.h"
+
+/*
+ * Global list of phyints, phyint instances, phyint groups and the anonymous
+ * group; the latter is initialized in phyint_init().
+ */
+struct phyint *phyints = NULL;
+struct phyint_instance *phyint_instances = NULL;
+struct phyint_group *phyint_groups = NULL;
+struct phyint_group *phyint_anongroup;
+
+/*
+ * Grouplist signature; initialized in phyint_init().
+ */
+static uint64_t phyint_grouplistsig;
+
+static void phyint_inst_insert(struct phyint_instance *pii);
+static void phyint_inst_print(struct phyint_instance *pii);
+
+static void phyint_insert(struct phyint *pi, struct phyint_group *pg);
+static void phyint_delete(struct phyint *pi);
+
+static void phyint_group_insert(struct phyint_group *pg);
+static void phyint_group_delete(struct phyint_group *pg);
+static struct phyint_group *phyint_group_lookup(const char *pg_name);
+static struct phyint_group *phyint_group_create(const char *pg_name);
+
+static void logint_print(struct logint *li);
+static void logint_insert(struct phyint_instance *pii, struct logint *li);
+static struct logint *logint_lookup(struct phyint_instance *pii, char *li_name);
+
+static void target_print(struct target *tg);
+static void target_insert(struct phyint_instance *pii, struct target *tg);
+static struct target *target_first(struct phyint_instance *pii);
+static struct target *target_select_best(struct phyint_instance *pii);
+static void target_flush_hosts(struct phyint_group *pg);
+
+static void reset_pii_probes(struct phyint_instance *pii, struct target *tg);
+
+static boolean_t phyint_inst_v6_sockinit(struct phyint_instance *pii);
+static boolean_t phyint_inst_v4_sockinit(struct phyint_instance *pii);
+
+static void ip_index_to_mask_v6(uint_t masklen, struct in6_addr *bitmask);
+static boolean_t prefix_equal(struct in6_addr p1, struct in6_addr p2,
+ int prefix_len);
+
+static int phyint_state_event(struct phyint_group *pg, struct phyint *pi);
+static int phyint_group_state_event(struct phyint_group *pg);
+static int phyint_group_change_event(struct phyint_group *pg, ipmp_group_op_t);
+static int phyint_group_member_event(struct phyint_group *pg, struct phyint *pi,
+ ipmp_if_op_t op);
+
+static uint64_t gensig(void);
+
+/* Initialize any per-file global state. Returns 0 on success, -1 on failure */
+int
+phyint_init(void)
+{
+ phyint_grouplistsig = gensig();
+ if (track_all_phyints) {
+ phyint_anongroup = phyint_group_create("");
+ if (phyint_anongroup == NULL)
+ return (-1);
+ phyint_group_insert(phyint_anongroup);
+ }
+ return (0);
+}
+
+/* Return the phyint with the given name */
+struct phyint *
+phyint_lookup(const char *name)
+{
+ struct phyint *pi;
+
+ if (debug & D_PHYINT)
+ logdebug("phyint_lookup(%s)\n", name);
+
+ for (pi = phyints; pi != NULL; pi = pi->pi_next) {
+ if (strncmp(pi->pi_name, name, sizeof (pi->pi_name)) == 0)
+ break;
+ }
+ return (pi);
+}
+
+/* Return the phyint instance with the given name and the given family */
+struct phyint_instance *
+phyint_inst_lookup(int af, char *name)
+{
+ struct phyint *pi;
+
+ if (debug & D_PHYINT)
+ logdebug("phyint_inst_lookup(%s %s)\n", AF_STR(af), name);
+
+ assert(af == AF_INET || af == AF_INET6);
+
+ pi = phyint_lookup(name);
+ if (pi == NULL)
+ return (NULL);
+
+ return (PHYINT_INSTANCE(pi, af));
+}
+
+static struct phyint_group *
+phyint_group_lookup(const char *pg_name)
+{
+ struct phyint_group *pg;
+
+ if (debug & D_PHYINT)
+ logdebug("phyint_group_lookup(%s)\n", pg_name);
+
+ for (pg = phyint_groups; pg != NULL; pg = pg->pg_next) {
+ if (strncmp(pg->pg_name, pg_name, sizeof (pg->pg_name)) == 0)
+ break;
+ }
+ return (pg);
+}
+
+/*
+ * Insert the phyint in the linked list of all phyints. If the phyint belongs
+ * to some group, insert it in the phyint group list.
+ */
+static void
+phyint_insert(struct phyint *pi, struct phyint_group *pg)
+{
+ if (debug & D_PHYINT)
+ logdebug("phyint_insert(%s '%s')\n", pi->pi_name, pg->pg_name);
+
+ /* Insert the phyint at the head of the 'all phyints' list */
+ pi->pi_next = phyints;
+ pi->pi_prev = NULL;
+ if (phyints != NULL)
+ phyints->pi_prev = pi;
+ phyints = pi;
+
+ /*
+ * Insert the phyint at the head of the 'phyint_group members' list
+ * of the phyint group to which it belongs.
+ */
+ pi->pi_pgnext = NULL;
+ pi->pi_pgprev = NULL;
+ pi->pi_group = pg;
+
+ pi->pi_pgnext = pg->pg_phyint;
+ if (pi->pi_pgnext != NULL)
+ pi->pi_pgnext->pi_pgprev = pi;
+ pg->pg_phyint = pi;
+
+ pg->pg_sig++;
+ (void) phyint_group_member_event(pg, pi, IPMP_IF_ADD);
+}
+
+/* Insert the phyint instance in the linked list of all phyint instances. */
+static void
+phyint_inst_insert(struct phyint_instance *pii)
+{
+ if (debug & D_PHYINT) {
+ logdebug("phyint_inst_insert(%s %s)\n",
+ AF_STR(pii->pii_af), pii->pii_name);
+ }
+
+ /*
+ * Insert the phyint at the head of the 'all phyint instances' list.
+ */
+ pii->pii_next = phyint_instances;
+ pii->pii_prev = NULL;
+ if (phyint_instances != NULL)
+ phyint_instances->pii_prev = pii;
+ phyint_instances = pii;
+}
+
+/*
+ * Create a new phyint with the given parameters. Also insert it into
+ * the list of all phyints and the list of phyint group members by calling
+ * phyint_insert().
+ */
+static struct phyint *
+phyint_create(char *pi_name, struct phyint_group *pg, uint_t ifindex,
+ uint64_t flags)
+{
+ struct phyint *pi;
+
+ pi = calloc(1, sizeof (struct phyint));
+ if (pi == NULL) {
+ logperror("phyint_create: calloc");
+ return (NULL);
+ }
+
+ /*
+ * Record the phyint values. Also insert the phyint into the
+ * phyint group by calling phyint_insert().
+ */
+ (void) strncpy(pi->pi_name, pi_name, sizeof (pi->pi_name));
+ pi->pi_name[sizeof (pi->pi_name) - 1] = '\0';
+ pi->pi_ifindex = ifindex;
+ pi->pi_icmpid =
+ htons(((getpid() & 0xFF) << 8) | (pi->pi_ifindex & 0xFF));
+ /*
+ * We optimistically start in the PI_RUNNING state. Later (in
+ * process_link_state_changes()), we will readjust this to match the
+ * current state of the link. Further, if test addresses are
+ * subsequently assigned, we will transition to PI_NOTARGETS and then
+ * either PI_RUNNING or PI_FAILED, depending on the result of the test
+ * probes.
+ */
+ pi->pi_state = PI_RUNNING;
+ pi->pi_flags = PHYINT_FLAGS(flags);
+ /*
+ * Initialise the link state. The link state is initialised to
+ * up, so that if the link is down when IPMP starts monitoring
+ * the interface, it will appear as though there has been a
+ * transition from the link up to link down. This avoids
+ * having to treat this situation as a special case.
+ */
+ INIT_LINK_STATE(pi);
+
+ /*
+ * Insert the phyint in the list of all phyints, and the
+ * list of phyint group members
+ */
+ phyint_insert(pi, pg);
+
+ /*
+ * If we are joining a failed group, mark the interface as
+ * failed.
+ */
+ if (GROUP_FAILED(pg))
+ (void) change_lif_flags(pi, IFF_FAILED, _B_TRUE);
+
+ return (pi);
+}
+
+/*
+ * Create a new phyint instance belonging to the phyint 'pi' and address
+ * family 'af'. Also insert it into the list of all phyint instances by
+ * calling phyint_inst_insert().
+ */
+static struct phyint_instance *
+phyint_inst_create(struct phyint *pi, int af)
+{
+ struct phyint_instance *pii;
+
+ pii = calloc(1, sizeof (struct phyint_instance));
+ if (pii == NULL) {
+ logperror("phyint_inst_create: calloc");
+ return (NULL);
+ }
+
+ /*
+ * Attach the phyint instance to the phyint.
+ * Set the back pointers as well
+ */
+ pii->pii_phyint = pi;
+ if (af == AF_INET)
+ pi->pi_v4 = pii;
+ else
+ pi->pi_v6 = pii;
+
+ pii->pii_in_use = 1;
+ pii->pii_probe_sock = -1;
+ pii->pii_snxt = 1;
+ pii->pii_af = af;
+ pii->pii_fd_hrtime = gethrtime() +
+ (FAILURE_DETECTION_QP * (hrtime_t)NANOSEC);
+ pii->pii_flags = pi->pi_flags;
+
+ /* Insert the phyint instance in the list of all phyint instances. */
+ phyint_inst_insert(pii);
+ return (pii);
+}
+
+/*
+ * Change the state of phyint `pi' to state `state'.
+ */
+void
+phyint_chstate(struct phyint *pi, enum pi_state state)
+{
+ /*
+ * To simplify things, some callers always set a given state
+ * regardless of the previous state of the phyint (e.g., setting
+ * PI_RUNNING when it's already set). We shouldn't bother
+ * generating an event or consuming a signature for these, since
+ * the actual state of the interface is unchanged.
+ */
+ if (pi->pi_state == state)
+ return;
+
+ pi->pi_state = state;
+ pi->pi_group->pg_sig++;
+ (void) phyint_state_event(pi->pi_group, pi);
+}
+
+/*
+ * Note that the type of phyint `pi' has changed.
+ */
+void
+phyint_newtype(struct phyint *pi)
+{
+ pi->pi_group->pg_sig++;
+ (void) phyint_state_event(pi->pi_group, pi);
+}
+
+/*
+ * Insert the phyint group in the linked list of all phyint groups
+ * at the head of the list
+ */
+static void
+phyint_group_insert(struct phyint_group *pg)
+{
+ pg->pg_next = phyint_groups;
+ pg->pg_prev = NULL;
+ if (phyint_groups != NULL)
+ phyint_groups->pg_prev = pg;
+ phyint_groups = pg;
+
+ phyint_grouplistsig++;
+ (void) phyint_group_change_event(pg, IPMP_GROUP_ADD);
+}
+
+/*
+ * Create a new phyint group called 'name'.
+ */
+static struct phyint_group *
+phyint_group_create(const char *name)
+{
+ struct phyint_group *pg;
+
+ if (debug & D_PHYINT)
+ logdebug("phyint_group_create(%s)\n", name);
+
+ pg = calloc(1, sizeof (struct phyint_group));
+ if (pg == NULL) {
+ logperror("phyint_group_create: calloc");
+ return (NULL);
+ }
+
+ (void) strncpy(pg->pg_name, name, sizeof (pg->pg_name));
+ pg->pg_name[sizeof (pg->pg_name) - 1] = '\0';
+ pg->pg_sig = gensig();
+
+ pg->pg_fdt = user_failure_detection_time;
+ pg->pg_probeint = user_probe_interval;
+
+ return (pg);
+}
+
+/*
+ * Change the state of the phyint group `pg' to state `state'.
+ */
+void
+phyint_group_chstate(struct phyint_group *pg, enum pg_state state)
+{
+ assert(pg != phyint_anongroup);
+
+ switch (state) {
+ case PG_FAILED:
+ pg->pg_groupfailed = 1;
+
+ /*
+ * We can never know with certainty that a group has
+ * failed. It is possible that all known targets have
+ * failed simultaneously, and new targets have come up
+ * instead. If the targets are routers then router
+ * discovery will kick in, and we will see the new routers
+ * thru routing socket messages. But if the targets are
+ * hosts, we have to discover it by multicast. So flush
+ * all the host targets. The next probe will send out a
+ * multicast echo request. If this is a group failure, we
+ * will still not see any response, otherwise we will
+ * clear the pg_groupfailed flag after we get
+ * NUM_PROBE_REPAIRS consecutive unicast replies on any
+ * phyint.
+ */
+ target_flush_hosts(pg);
+ break;
+
+ case PG_RUNNING:
+ pg->pg_groupfailed = 0;
+ break;
+
+ default:
+ logerr("phyint_group_chstate: invalid group state %d; "
+ "aborting\n", state);
+ abort();
+ }
+
+ pg->pg_sig++;
+ (void) phyint_group_state_event(pg);
+}
+
+/*
+ * Create a new phyint instance and initialize it from the values supplied by
+ * the kernel. Always check for ENXIO before logging any error, because the
+ * interface could have vanished after completion of SIOCGLIFCONF.
+ * Return values:
+ * pointer to the phyint instance on success
+ * NULL on failure Eg. if the phyint instance is not found in the kernel
+ */
+struct phyint_instance *
+phyint_inst_init_from_k(int af, char *pi_name)
+{
+ char pg_name[LIFNAMSIZ + 1];
+ int ifsock;
+ uint_t ifindex;
+ uint64_t flags;
+ struct lifreq lifr;
+ struct phyint *pi;
+ struct phyint_instance *pii;
+ boolean_t pg_created;
+ boolean_t pi_created;
+ struct phyint_group *pg;
+
+retry:
+ pii = NULL;
+ pi = NULL;
+ pg = NULL;
+ pi_created = _B_FALSE;
+ pg_created = _B_FALSE;
+
+ if (debug & D_PHYINT) {
+ logdebug("phyint_inst_init_from_k(%s %s)\n",
+ AF_STR(af), pi_name);
+ }
+
+ assert(af == AF_INET || af == AF_INET6);
+
+ /* Get the socket for doing ioctls */
+ ifsock = (af == AF_INET) ? ifsock_v4 : ifsock_v6;
+
+ /*
+ * Get the interface flags. Ignore loopback and multipoint
+ * interfaces.
+ */
+ (void) strncpy(lifr.lifr_name, pi_name, sizeof (lifr.lifr_name));
+ lifr.lifr_name[sizeof (lifr.lifr_name) - 1] = '\0';
+ if (ioctl(ifsock, SIOCGLIFFLAGS, (char *)&lifr) < 0) {
+ if (errno != ENXIO) {
+ logperror("phyint_inst_init_from_k:"
+ " ioctl (get flags)");
+ }
+ return (NULL);
+ }
+ flags = lifr.lifr_flags;
+ if (!(flags & IFF_MULTICAST) || (flags & IFF_LOOPBACK))
+ return (NULL);
+
+ /*
+ * Get the ifindex for recording later in our tables, in case we need
+ * to create a new phyint.
+ */
+ if (ioctl(ifsock, SIOCGLIFINDEX, (char *)&lifr) < 0) {
+ if (errno != ENXIO) {
+ logperror("phyint_inst_init_from_k: "
+ " ioctl (get lifindex)");
+ }
+ return (NULL);
+ }
+ ifindex = lifr.lifr_index;
+
+ /*
+ * Get the phyint group name of this phyint, from the kernel.
+ */
+ if (ioctl(ifsock, SIOCGLIFGROUPNAME, (char *)&lifr) < 0) {
+ if (errno != ENXIO) {
+ logperror("phyint_inst_init_from_k: "
+ "ioctl (get group name)");
+ }
+ return (NULL);
+ }
+ (void) strncpy(pg_name, lifr.lifr_groupname, sizeof (pg_name));
+ pg_name[sizeof (pg_name) - 1] = '\0';
+
+ /*
+ * If the phyint is not part of any group, pg_name is the
+ * null string. If 'track_all_phyints' is false, there is no
+ * need to create a phyint.
+ */
+ if (pg_name[0] == '\0' && !track_all_phyints) {
+ /*
+ * If the IFF_FAILED or IFF_OFFLINE flags are set, reset
+ * them. These flags shouldn't be set if IPMP isn't
+ * tracking the interface.
+ */
+ if ((flags & (IFF_FAILED | IFF_OFFLINE)) != 0) {
+ lifr.lifr_flags = flags & ~(IFF_FAILED | IFF_OFFLINE);
+ if (ioctl(ifsock, SIOCSLIFFLAGS, (char *)&lifr) < 0) {
+ if (errno != ENXIO) {
+ logperror("phyint_inst_init_from_k:"
+ " ioctl (set flags)");
+ }
+ }
+ }
+ return (NULL);
+ }
+
+ /*
+ * We need to create a new phyint instance. A phyint instance
+ * belongs to a phyint, and the phyint belongs to a phyint group.
+ * So we first lookup the 'parents' and if they don't exist then
+ * we create them.
+ */
+ pg = phyint_group_lookup(pg_name);
+ if (pg == NULL) {
+ pg = phyint_group_create(pg_name);
+ if (pg == NULL) {
+ logerr("phyint_inst_init_from_k:"
+ " unable to create group %s\n", pg_name);
+ return (NULL);
+ }
+ phyint_group_insert(pg);
+ pg_created = _B_TRUE;
+ }
+
+ /*
+ * Lookup the phyint. If the phyint does not exist create it.
+ */
+ pi = phyint_lookup(pi_name);
+ if (pi == NULL) {
+ pi = phyint_create(pi_name, pg, ifindex, flags);
+ if (pi == NULL) {
+ logerr("phyint_inst_init_from_k:"
+ " unable to create phyint %s\n", pi_name);
+ if (pg_created)
+ phyint_group_delete(pg);
+ return (NULL);
+ }
+ pi_created = _B_TRUE;
+ } else {
+ /* The phyint exists already. */
+ assert(pi_created == _B_FALSE);
+ /*
+ * Normally we should see consistent values for the IPv4 and
+ * IPv6 instances, for phyint properties. If we don't, it
+ * means things have changed underneath us, and we should
+ * resync our tables with the kernel. Check whether the
+ * interface index has changed. If so, it is most likely
+ * the interface has been unplumbed and replumbed,
+ * while we are yet to update our tables. Do it now.
+ */
+ if (pi->pi_ifindex != ifindex) {
+ if (pg_created)
+ phyint_group_delete(pg);
+ phyint_inst_delete(PHYINT_INSTANCE(pi, AF_OTHER(af)));
+ goto retry;
+ }
+ assert(PHYINT_INSTANCE(pi, af) == NULL);
+
+ /*
+ * If the group name seen by the IPv4 and IPv6 instances
+ * are different, it is most likely the groupname has
+ * changed, while we are yet to update our tables. Do it now.
+ */
+ if (strcmp(pi->pi_group->pg_name, pg_name) != 0) {
+ if (pg_created)
+ phyint_group_delete(pg);
+ restore_phyint(pi);
+ phyint_inst_delete(PHYINT_INSTANCE(pi,
+ AF_OTHER(af)));
+ goto retry;
+ }
+ }
+
+ /*
+ * Create a new phyint instance, corresponding to the 'af'
+ * passed in.
+ */
+ pii = phyint_inst_create(pi, af);
+ if (pii == NULL) {
+ logerr("phyint_inst_init_from_k: unable to create"
+ "phyint inst %s\n", pi->pi_name);
+ if (pi_created) {
+ /*
+ * Deleting the phyint will delete the phyint group
+ * if this is the last phyint in the group.
+ */
+ phyint_delete(pi);
+ }
+ return (NULL);
+ }
+
+ return (pii);
+}
+
+/*
+ * Bind the pii_probe_sock to the chosen IFF_NOFAILOVER address in
+ * pii_probe_logint. This socket will be used for sending and receiving
+ * ICMP/ICMPv6 probes to targets. Do the common part in this function, and
+ * complete the initializations by calling the protocol specific functions
+ * phyint_inst_v{4,6}_sockinit() respectively.
+ *
+ * Return values: _B_TRUE/_B_FALSE for success or failure respectively.
+ */
+boolean_t
+phyint_inst_sockinit(struct phyint_instance *pii)
+{
+ boolean_t success;
+ struct phyint_group *pg;
+
+ if (debug & D_PHYINT) {
+ logdebug("phyint_inst_sockinit(%s %s)\n",
+ AF_STR(pii->pii_af), pii->pii_name);
+ }
+
+ assert(pii->pii_probe_logint != NULL);
+ assert(pii->pii_probe_logint->li_flags & IFF_UP);
+ assert(SINGLETON_GROUP(pii->pii_phyint) ||
+ (pii->pii_probe_logint->li_flags & IFF_NOFAILOVER));
+ assert(pii->pii_af == AF_INET || pii->pii_af == AF_INET6);
+
+ /*
+ * If the socket is already bound, close pii_probe_sock
+ */
+ if (pii->pii_probe_sock != -1)
+ close_probe_socket(pii, _B_TRUE);
+
+ /*
+ * If the phyint is not part of a named group and track_all_phyints is
+ * false, simply return.
+ */
+ pg = pii->pii_phyint->pi_group;
+ if (pg == phyint_anongroup && !track_all_phyints) {
+ if (debug & D_PHYINT)
+ logdebug("phyint_inst_sockinit: no group\n");
+ return (_B_FALSE);
+ }
+
+ /*
+ * Initialize the socket by calling the protocol specific function.
+ * If it succeeds, add the socket to the poll list.
+ */
+ if (pii->pii_af == AF_INET6)
+ success = phyint_inst_v6_sockinit(pii);
+ else
+ success = phyint_inst_v4_sockinit(pii);
+
+ if (success && (poll_add(pii->pii_probe_sock) == 0))
+ return (_B_TRUE);
+
+ /* Something failed, cleanup and return false */
+ if (pii->pii_probe_sock != -1)
+ close_probe_socket(pii, _B_FALSE);
+
+ return (_B_FALSE);
+}
+
+/*
+ * IPv6 specific part in initializing the pii_probe_sock. This socket is
+ * used to send/receive ICMPv6 probe packets.
+ */
+static boolean_t
+phyint_inst_v6_sockinit(struct phyint_instance *pii)
+{
+ icmp6_filter_t filter;
+ int hopcount = 1;
+ int int_op;
+ struct sockaddr_in6 testaddr;
+
+ /*
+ * Open a raw socket with ICMPv6 protocol.
+ *
+ * Use IPV6_DONTFAILOVER_IF to make sure that probes go out
+ * on the specified phyint only, and are not subject to load
+ * balancing. Bind to the src address chosen will ensure that
+ * the responses are received only on the specified phyint.
+ *
+ * Set the hopcount to 1 so that probe packets are not routed.
+ * Disable multicast loopback. Set the receive filter to
+ * receive only ICMPv6 echo replies.
+ */
+ pii->pii_probe_sock = socket(pii->pii_af, SOCK_RAW, IPPROTO_ICMPV6);
+ if (pii->pii_probe_sock < 0) {
+ logperror_pii(pii, "phyint_inst_v6_sockinit: socket");
+ return (_B_FALSE);
+}
+
+ bzero(&testaddr, sizeof (testaddr));
+ testaddr.sin6_family = AF_INET6;
+ testaddr.sin6_port = 0;
+ testaddr.sin6_addr = pii->pii_probe_logint->li_addr;
+
+ if (bind(pii->pii_probe_sock, (struct sockaddr *)&testaddr,
+ sizeof (testaddr)) < 0) {
+ logperror_pii(pii, "phyint_inst_v6_sockinit: IPv6 bind");
+ return (_B_FALSE);
+ }
+
+ /*
+ * IPV6_DONTFAILOVER_IF option takes precedence over setting
+ * IP_MULTICAST_IF. So we don't set IPV6_MULTICAST_IF again.
+ */
+ if (setsockopt(pii->pii_probe_sock, IPPROTO_IPV6, IPV6_DONTFAILOVER_IF,
+ (char *)&pii->pii_ifindex, sizeof (uint_t)) < 0) {
+ logperror_pii(pii, "phyint_inst_v6_sockinit: setsockopt"
+ " IPV6_DONTFAILOVER_IF");
+ return (_B_FALSE);
+ }
+
+ if (setsockopt(pii->pii_probe_sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
+ (char *)&hopcount, sizeof (hopcount)) < 0) {
+ logperror_pii(pii, "phyint_inst_v6_sockinit: setsockopt"
+ " IPV6_UNICAST_HOPS");
+ return (_B_FALSE);
+ }
+
+ if (setsockopt(pii->pii_probe_sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
+ (char *)&hopcount, sizeof (hopcount)) < 0) {
+ logperror_pii(pii, "phyint_inst_v6_sockinit: setsockopt"
+ " IPV6_MULTICAST_HOPS");
+ return (_B_FALSE);
+ }
+
+ int_op = 0; /* used to turn off option */
+ if (setsockopt(pii->pii_probe_sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
+ (char *)&int_op, sizeof (int_op)) < 0) {
+ logperror_pii(pii, "phyint_inst_v6_sockinit: setsockopt"
+ " IPV6_MULTICAST_LOOP");
+ return (_B_FALSE);
+ }
+
+ /*
+ * Filter out so that we only receive ICMP echo replies
+ */
+ ICMP6_FILTER_SETBLOCKALL(&filter);
+ ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filter);
+
+ if (setsockopt(pii->pii_probe_sock, IPPROTO_ICMPV6, ICMP6_FILTER,
+ (char *)&filter, sizeof (filter)) < 0) {
+ logperror_pii(pii, "phyint_inst_v6_sockinit: setsockopt"
+ " ICMP6_FILTER");
+ return (_B_FALSE);
+ }
+
+ /* Enable receipt of ancillary data */
+ int_op = 1;
+ if (setsockopt(pii->pii_probe_sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT,
+ (char *)&int_op, sizeof (int_op)) < 0) {
+ logperror_pii(pii, "phyint_inst_v6_sockinit: setsockopt"
+ " IPV6_RECVHOPLIMIT");
+ return (_B_FALSE);
+ }
+
+ return (_B_TRUE);
+}
+
+/*
+ * IPv4 specific part in initializing the pii_probe_sock. This socket is
+ * used to send/receive ICMPv4 probe packets.
+ */
+static boolean_t
+phyint_inst_v4_sockinit(struct phyint_instance *pii)
+{
+ struct sockaddr_in testaddr;
+ char char_op;
+ int ttl = 1;
+ char char_ttl = 1;
+
+ /*
+ * Open a raw socket with ICMPv4 protocol.
+ *
+ * Use IP_DONTFAILOVER_IF to make sure that probes go out
+ * on the specified phyint only, and are not subject to load
+ * balancing. Bind to the src address chosen will ensure that
+ * the responses are received only on the specified phyint.
+ *
+ * Set the ttl to 1 so that probe packets are not routed.
+ * Disable multicast loopback.
+ */
+ pii->pii_probe_sock = socket(pii->pii_af, SOCK_RAW, IPPROTO_ICMP);
+ if (pii->pii_probe_sock < 0) {
+ logperror_pii(pii, "phyint_inst_v4_sockinit: socket");
+ return (_B_FALSE);
+ }
+
+ bzero(&testaddr, sizeof (testaddr));
+ testaddr.sin_family = AF_INET;
+ testaddr.sin_port = 0;
+ IN6_V4MAPPED_TO_INADDR(&pii->pii_probe_logint->li_addr,
+ &testaddr.sin_addr);
+
+ if (bind(pii->pii_probe_sock, (struct sockaddr *)&testaddr,
+ sizeof (testaddr)) < 0) {
+ logperror_pii(pii, "phyint_inst_v4_sockinit: IPv4 bind");
+ return (_B_FALSE);
+ }
+
+ /*
+ * IP_DONTFAILOVER_IF option takes precedence over setting
+ * IP_MULTICAST_IF. So we don't set IP_MULTICAST_IF again.
+ */
+ if (setsockopt(pii->pii_probe_sock, IPPROTO_IP, IP_DONTFAILOVER_IF,
+ (char *)&testaddr.sin_addr, sizeof (struct in_addr)) < 0) {
+ logperror_pii(pii, "phyint_inst_v4_sockinit: setsockopt"
+ " IP_DONTFAILOVER");
+ return (_B_FALSE);
+ }
+
+ if (setsockopt(pii->pii_probe_sock, IPPROTO_IP, IP_TTL,
+ (char *)&ttl, sizeof (ttl)) < 0) {
+ logperror_pii(pii, "phyint_inst_v4_sockinit: setsockopt"
+ " IP_TTL");
+ return (_B_FALSE);
+ }
+
+ char_op = 0; /* used to turn off option */
+ if (setsockopt(pii->pii_probe_sock, IPPROTO_IP, IP_MULTICAST_LOOP,
+ (char *)&char_op, sizeof (char_op)) == -1) {
+ logperror_pii(pii, "phyint_inst_v4_sockinit: setsockopt"
+ " IP_MULTICAST_LOOP");
+ return (_B_FALSE);
+ }
+
+ if (setsockopt(pii->pii_probe_sock, IPPROTO_IP, IP_MULTICAST_TTL,
+ (char *)&char_ttl, sizeof (char_ttl)) == -1) {
+ logperror_pii(pii, "phyint_inst_v4_sockinit: setsockopt"
+ " IP_MULTICAST_TTL");
+ return (_B_FALSE);
+ }
+
+ return (_B_TRUE);
+}
+
+/*
+ * Remove the phyint group from the list of 'all phyint groups'
+ * and free it.
+ */
+static void
+phyint_group_delete(struct phyint_group *pg)
+{
+ /*
+ * The anonymous group always exists, even when empty.
+ */
+ if (pg == phyint_anongroup)
+ return;
+
+ if (debug & D_PHYINT)
+ logdebug("phyint_group_delete('%s')\n", pg->pg_name);
+
+ /*
+ * The phyint group must be empty, and must not have any phyints.
+ * The phyint group must be in the list of all phyint groups
+ */
+ assert(pg->pg_phyint == NULL);
+ assert(phyint_groups == pg || pg->pg_prev != NULL);
+
+ if (pg->pg_prev != NULL)
+ pg->pg_prev->pg_next = pg->pg_next;
+ else
+ phyint_groups = pg->pg_next;
+
+ if (pg->pg_next != NULL)
+ pg->pg_next->pg_prev = pg->pg_prev;
+
+ pg->pg_next = NULL;
+ pg->pg_prev = NULL;
+
+ phyint_grouplistsig++;
+ (void) phyint_group_change_event(pg, IPMP_GROUP_REMOVE);
+
+ free(pg);
+}
+
+/*
+ * Extract information from the kernel about the desired phyint.
+ * Look only for properties of the phyint and not properties of logints.
+ * Take appropriate action on the changes.
+ * Return codes:
+ * PI_OK
+ * The phyint exists in the kernel and matches our knowledge
+ * of the phyint.
+ * PI_DELETED
+ * The phyint has vanished in the kernel.
+ * PI_IFINDEX_CHANGED
+ * The phyint's interface index has changed.
+ * Ask the caller to delete and recreate the phyint.
+ * PI_IOCTL_ERROR
+ * Some ioctl error. Don't change anything.
+ * PI_GROUP_CHANGED
+ * The phyint has changed group.
+ */
+int
+phyint_inst_update_from_k(struct phyint_instance *pii)
+{
+ struct lifreq lifr;
+ int ifsock;
+ struct phyint *pi;
+
+ pi = pii->pii_phyint;
+
+ if (debug & D_PHYINT) {
+ logdebug("phyint_inst_update_from_k(%s %s)\n",
+ AF_STR(pii->pii_af), pi->pi_name);
+ }
+
+ /*
+ * Get the ifindex from the kernel, for comparison with the
+ * value in our tables.
+ */
+ (void) strncpy(lifr.lifr_name, pi->pi_name, sizeof (lifr.lifr_name));
+ lifr.lifr_name[sizeof (lifr.lifr_name) - 1] = '\0';
+
+ ifsock = (pii->pii_af == AF_INET) ? ifsock_v4 : ifsock_v6;
+ if (ioctl(ifsock, SIOCGLIFINDEX, &lifr) < 0) {
+ if (errno == ENXIO) {
+ return (PI_DELETED);
+ } else {
+ logperror_pii(pii, "phyint_inst_update_from_k:"
+ " ioctl (get lifindex)");
+ return (PI_IOCTL_ERROR);
+ }
+ }
+
+ if (lifr.lifr_index != pi->pi_ifindex) {
+ /*
+ * The index has changed. Most likely the interface has
+ * been unplumbed and replumbed. Ask the caller to take
+ * appropriate action.
+ */
+ if (debug & D_PHYINT) {
+ logdebug("phyint_inst_update_from_k:"
+ " old index %d new index %d\n",
+ pi->pi_ifindex, lifr.lifr_index);
+ }
+ return (PI_IFINDEX_CHANGED);
+ }
+
+ /*
+ * Get the group name from the kernel, for comparison with
+ * the value in our tables.
+ */
+ if (ioctl(ifsock, SIOCGLIFGROUPNAME, &lifr) < 0) {
+ if (errno == ENXIO) {
+ return (PI_DELETED);
+ } else {
+ logperror_pii(pii, "phyint_inst_update_from_k:"
+ " ioctl (get groupname)");
+ return (PI_IOCTL_ERROR);
+ }
+ }
+
+ /*
+ * If the phyint has changed group i.e. if the phyint group name
+ * returned by the kernel is different, ask the caller to delete
+ * and recreate the phyint in the right group
+ */
+ if (strcmp(lifr.lifr_groupname, pi->pi_group->pg_name) != 0) {
+ /* Groupname has changed */
+ if (debug & D_PHYINT) {
+ logdebug("phyint_inst_update_from_k:"
+ " groupname change\n");
+ }
+ return (PI_GROUP_CHANGED);
+ }
+
+ /*
+ * Get the current phyint flags from the kernel, and determine what
+ * flags have changed by comparing against our tables. Note that the
+ * IFF_INACTIVE processing in initifs() relies on this call to ensure
+ * that IFF_INACTIVE is really still set on the interface.
+ */
+ if (ioctl(ifsock, SIOCGLIFFLAGS, &lifr) < 0) {
+ if (errno == ENXIO) {
+ return (PI_DELETED);
+ } else {
+ logperror_pii(pii, "phyint_inst_update_from_k: "
+ " ioctl (get flags)");
+ return (PI_IOCTL_ERROR);
+ }
+ }
+
+ pi->pi_flags = PHYINT_FLAGS(lifr.lifr_flags);
+ if (pi->pi_v4 != NULL)
+ pi->pi_v4->pii_flags = pi->pi_flags;
+ if (pi->pi_v6 != NULL)
+ pi->pi_v6->pii_flags = pi->pi_flags;
+
+ if (pi->pi_flags & IFF_FAILED) {
+ /*
+ * If we are in the running and full state, we have
+ * completed failbacks successfully and we would have
+ * expected IFF_FAILED to have been clear. That it is
+ * set means there was a race condition. Some other
+ * process turned on the IFF_FAILED flag. Since the
+ * flag setting is not atomic, i.e. a get ioctl followed
+ * by a set ioctl, and since there is no way to set an
+ * individual flag bit, this could have occurred.
+ */
+ if (pi->pi_state == PI_RUNNING && pi->pi_full)
+ (void) change_lif_flags(pi, IFF_FAILED, _B_FALSE);
+ } else {
+ /*
+ * If we are in the failed state, there was a race.
+ * we have completed failover successfully because our
+ * state is failed and empty. Some other process turned
+ * off the IFF_FAILED flag. Same comment as above
+ */
+ if (pi->pi_state == PI_FAILED && pi->pi_empty)
+ (void) change_lif_flags(pi, IFF_FAILED, _B_TRUE);
+ }
+
+ /* No change in phyint status */
+ return (PI_OK);
+}
+
+/*
+ * Delete the phyint. Remove it from the list of all phyints, and the
+ * list of phyint group members. If the group becomes empty, delete the
+ * group also.
+ */
+static void
+phyint_delete(struct phyint *pi)
+{
+ struct phyint_group *pg = pi->pi_group;
+
+ if (debug & D_PHYINT)
+ logdebug("phyint_delete(%s)\n", pi->pi_name);
+
+ /* Both IPv4 and IPv6 phyint instances must have been deleted. */
+ assert(pi->pi_v4 == NULL && pi->pi_v6 == NULL);
+
+ /*
+ * The phyint must belong to a group.
+ */
+ assert(pg->pg_phyint == pi || pi->pi_pgprev != NULL);
+
+ /* The phyint must be in the list of all phyints */
+ assert(phyints == pi || pi->pi_prev != NULL);
+
+ /* Remove the phyint from the phyint group list */
+ pg->pg_sig++;
+ (void) phyint_group_member_event(pg, pi, IPMP_IF_REMOVE);
+
+ if (pi->pi_pgprev == NULL) {
+ /* Phyint is the 1st in the phyint group list */
+ pg->pg_phyint = pi->pi_pgnext;
+ } else {
+ pi->pi_pgprev->pi_pgnext = pi->pi_pgnext;
+ }
+ if (pi->pi_pgnext != NULL)
+ pi->pi_pgnext->pi_pgprev = pi->pi_pgprev;
+ pi->pi_pgnext = NULL;
+ pi->pi_pgprev = NULL;
+
+ /* Remove the phyint from the global list of phyints */
+ if (pi->pi_prev == NULL) {
+ /* Phyint is the 1st in the list */
+ phyints = pi->pi_next;
+ } else {
+ pi->pi_prev->pi_next = pi->pi_next;
+ }
+ if (pi->pi_next != NULL)
+ pi->pi_next->pi_prev = pi->pi_prev;
+ pi->pi_next = NULL;
+ pi->pi_prev = NULL;
+
+ free(pi);
+
+ /* Delete the phyint_group if the last phyint has been deleted */
+ if (pg->pg_phyint == NULL)
+ phyint_group_delete(pg);
+}
+
+/*
+ * Delete (unlink and free), the phyint instance.
+ */
+void
+phyint_inst_delete(struct phyint_instance *pii)
+{
+ struct phyint *pi = pii->pii_phyint;
+
+ assert(pi != NULL);
+
+ if (debug & D_PHYINT) {
+ logdebug("phyint_inst_delete(%s %s)\n",
+ AF_STR(pii->pii_af), pi->pi_name);
+ }
+
+ /*
+ * If the phyint instance has associated probe targets
+ * delete all the targets
+ */
+ while (pii->pii_targets != NULL)
+ target_delete(pii->pii_targets);
+
+ /*
+ * Delete all the logints associated with this phyint
+ * instance.
+ */
+ while (pii->pii_logint != NULL)
+ logint_delete(pii->pii_logint);
+
+ /*
+ * Close the IFF_NOFAILOVER socket used to send probes to targets
+ * from this phyint.
+ */
+ if (pii->pii_probe_sock != -1)
+ close_probe_socket(pii, _B_TRUE);
+
+ /*
+ * Phyint instance must be in the list of all phyint instances.
+ * Remove phyint instance from the global list of phyint instances.
+ */
+ assert(phyint_instances == pii || pii->pii_prev != NULL);
+ if (pii->pii_prev == NULL) {
+ /* Phyint is the 1st in the list */
+ phyint_instances = pii->pii_next;
+ } else {
+ pii->pii_prev->pii_next = pii->pii_next;
+ }
+ if (pii->pii_next != NULL)
+ pii->pii_next->pii_prev = pii->pii_prev;
+ pii->pii_next = NULL;
+ pii->pii_prev = NULL;
+
+ /*
+ * Reset the phyint instance pointer in the phyint.
+ * If this is the last phyint instance (being deleted) on this
+ * phyint, then delete the phyint.
+ */
+ if (pii->pii_af == AF_INET)
+ pi->pi_v4 = NULL;
+ else
+ pi->pi_v6 = NULL;
+
+ if (pi->pi_v4 == NULL && pi->pi_v6 == NULL)
+ phyint_delete(pi);
+
+ free(pii);
+}
+
+static void
+phyint_inst_print(struct phyint_instance *pii)
+{
+ struct logint *li;
+ struct target *tg;
+ char abuf[INET6_ADDRSTRLEN];
+ int most_recent;
+ int i;
+
+ if (pii->pii_phyint == NULL) {
+ logdebug("pii->pi_phyint NULL can't print\n");
+ return;
+ }
+
+ logdebug("\nPhyint instance: %s %s index %u state %x flags %llx "
+ "sock %x in_use %d empty %x full %x\n",
+ AF_STR(pii->pii_af), pii->pii_name, pii->pii_ifindex,
+ pii->pii_state, pii->pii_phyint->pi_flags, pii->pii_probe_sock,
+ pii->pii_in_use, pii->pii_phyint->pi_empty,
+ pii->pii_phyint->pi_full);
+
+ for (li = pii->pii_logint; li != NULL; li = li->li_next)
+ logint_print(li);
+
+ logdebug("\n");
+ for (tg = pii->pii_targets; tg != NULL; tg = tg->tg_next)
+ target_print(tg);
+
+ if (pii->pii_targets == NULL)
+ logdebug("pi_targets NULL\n");
+
+ if (pii->pii_target_next != NULL) {
+ logdebug("pi_target_next %s %s\n", AF_STR(pii->pii_af),
+ pr_addr(pii->pii_af, pii->pii_target_next->tg_address,
+ abuf, sizeof (abuf)));
+ } else {
+ logdebug("pi_target_next NULL\n");
+ }
+
+ if (pii->pii_rtt_target_next != NULL) {
+ logdebug("pi_rtt_target_next %s %s\n", AF_STR(pii->pii_af),
+ pr_addr(pii->pii_af, pii->pii_rtt_target_next->tg_address,
+ abuf, sizeof (abuf)));
+ } else {
+ logdebug("pi_rtt_target_next NULL\n");
+ }
+
+ if (pii->pii_targets != NULL) {
+ most_recent = PROBE_INDEX_PREV(pii->pii_probe_next);
+
+ i = most_recent;
+ do {
+ if (pii->pii_probes[i].pr_target != NULL) {
+ logdebug("#%d target %s ", i,
+ pr_addr(pii->pii_af,
+ pii->pii_probes[i].pr_target->tg_address,
+ abuf, sizeof (abuf)));
+ } else {
+ logdebug("#%d target NULL ", i);
+ }
+ logdebug("time_sent %u status %d time_ack/lost %u\n",
+ pii->pii_probes[i].pr_time_sent,
+ pii->pii_probes[i].pr_status,
+ pii->pii_probes[i].pr_time_lost);
+ i = PROBE_INDEX_PREV(i);
+ } while (i != most_recent);
+ }
+}
+
+/*
+ * Lookup a logint based on the logical interface name, on the given
+ * phyint instance.
+ */
+static struct logint *
+logint_lookup(struct phyint_instance *pii, char *name)
+{
+ struct logint *li;
+
+ if (debug & D_LOGINT) {
+ logdebug("logint_lookup(%s, %s)\n",
+ AF_STR(pii->pii_af), name);
+ }
+
+ for (li = pii->pii_logint; li != NULL; li = li->li_next) {
+ if (strncmp(name, li->li_name, sizeof (li->li_name)) == 0)
+ break;
+ }
+ return (li);
+}
+
+/*
+ * Insert a logint at the head of the list of logints of the given
+ * phyint instance
+ */
+static void
+logint_insert(struct phyint_instance *pii, struct logint *li)
+{
+ li->li_next = pii->pii_logint;
+ li->li_prev = NULL;
+ if (pii->pii_logint != NULL)
+ pii->pii_logint->li_prev = li;
+ pii->pii_logint = li;
+ li->li_phyint_inst = pii;
+}
+
+/*
+ * Create a new named logint, on the specified phyint instance.
+ */
+static struct logint *
+logint_create(struct phyint_instance *pii, char *name)
+{
+ struct logint *li;
+
+ if (debug & D_LOGINT) {
+ logdebug("logint_create(%s %s %s)\n",
+ AF_STR(pii->pii_af), pii->pii_name, name);
+ }
+
+ li = calloc(1, sizeof (struct logint));
+ if (li == NULL) {
+ logperror("logint_create: calloc");
+ return (NULL);
+ }
+
+ (void) strncpy(li->li_name, name, sizeof (li->li_name));
+ li->li_name[sizeof (li->li_name) - 1] = '\0';
+ logint_insert(pii, li);
+ return (li);
+}
+
+/*
+ * Initialize the logint based on the data returned by the kernel.
+ */
+void
+logint_init_from_k(struct phyint_instance *pii, char *li_name)
+{
+ int ifsock;
+ uint64_t flags;
+ uint64_t saved_flags;
+ struct logint *li;
+ struct lifreq lifr;
+ struct in6_addr test_subnet;
+ struct in6_addr test_subnet_mask;
+ struct in6_addr testaddr;
+ int test_subnet_len;
+ struct sockaddr_in6 *sin6;
+ struct sockaddr_in *sin;
+ char abuf[INET6_ADDRSTRLEN];
+ boolean_t ptp = _B_FALSE;
+ struct in6_addr tgaddr;
+
+ if (debug & D_LOGINT) {
+ logdebug("logint_init_from_k(%s %s)\n",
+ AF_STR(pii->pii_af), li_name);
+ }
+
+ /* Get the socket for doing ioctls */
+ ifsock = (pii->pii_af == AF_INET) ? ifsock_v4 : ifsock_v6;
+
+ /*
+ * Get the flags from the kernel. Also serves as a check whether
+ * the logical still exists. If it doesn't exist, no need to proceed
+ * any further. li_in_use will make the caller clean up the logint
+ */
+ (void) strncpy(lifr.lifr_name, li_name, sizeof (lifr.lifr_name));
+ lifr.lifr_name[sizeof (lifr.lifr_name) - 1] = '\0';
+ if (ioctl(ifsock, SIOCGLIFFLAGS, (char *)&lifr) < 0) {
+ /* Interface may have vanished */
+ if (errno != ENXIO) {
+ logperror_pii(pii, "logint_init_from_k: "
+ "ioctl (get flags)");
+ }
+ return;
+ }
+
+ flags = lifr.lifr_flags;
+
+ /*
+ * Verified the logint exists. Now lookup the logint in our tables.
+ * If it does not exist, create a new logint.
+ */
+ li = logint_lookup(pii, li_name);
+ if (li == NULL) {
+ li = logint_create(pii, li_name);
+ if (li == NULL) {
+ /*
+ * Pretend the interface does not exist
+ * in the kernel
+ */
+ return;
+ }
+ }
+
+ /*
+ * Update li->li_flags with the new flags, after saving the old
+ * value. This is used later to check what flags has changed and
+ * take any action
+ */
+ saved_flags = li->li_flags;
+ li->li_flags = flags;
+
+ /*
+ * Get the address, prefix, prefixlength and update the logint.
+ * Check if anything has changed. If the logint used for the
+ * test address has changed, take suitable action.
+ */
+ if (ioctl(ifsock, SIOCGLIFADDR, (char *)&lifr) < 0) {
+ /* Interface may have vanished */
+ if (errno != ENXIO) {
+ logperror_li(li, "logint_init_from_k: (get addr)");
+ }
+ goto error;
+ }
+
+ if (pii->pii_af == AF_INET) {
+ sin = (struct sockaddr_in *)&lifr.lifr_addr;
+ IN6_INADDR_TO_V4MAPPED(&sin->sin_addr, &testaddr);
+ } else {
+ sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
+ testaddr = sin6->sin6_addr;
+ }
+
+ if (pii->pii_phyint->pi_flags & IFF_POINTOPOINT) {
+ ptp = _B_TRUE;
+ if (ioctl(ifsock, SIOCGLIFDSTADDR, (char *)&lifr) < 0) {
+ if (errno != ENXIO) {
+ logperror_li(li, "logint_init_from_k:"
+ " (get dstaddr)");
+ }
+ goto error;
+ }
+ if (pii->pii_af == AF_INET) {
+ sin = (struct sockaddr_in *)&lifr.lifr_addr;
+ IN6_INADDR_TO_V4MAPPED(&sin->sin_addr, &tgaddr);
+ } else {
+ sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
+ tgaddr = sin6->sin6_addr;
+ }
+ } else {
+ if (ioctl(ifsock, SIOCGLIFSUBNET, (char *)&lifr) < 0) {
+ /* Interface may have vanished */
+ if (errno != ENXIO) {
+ logperror_li(li, "logint_init_from_k:"
+ " (get subnet)");
+ }
+ goto error;
+ }
+ if (lifr.lifr_subnet.ss_family == AF_INET6) {
+ sin6 = (struct sockaddr_in6 *)&lifr.lifr_subnet;
+ test_subnet = sin6->sin6_addr;
+ test_subnet_len = lifr.lifr_addrlen;
+ } else {
+ sin = (struct sockaddr_in *)&lifr.lifr_subnet;
+ IN6_INADDR_TO_V4MAPPED(&sin->sin_addr, &test_subnet);
+ test_subnet_len = lifr.lifr_addrlen +
+ (IPV6_ABITS - IP_ABITS);
+ }
+ (void) ip_index_to_mask_v6(test_subnet_len, &test_subnet_mask);
+ }
+
+ /*
+ * Also record the OINDEX for completeness. This information is
+ * not used.
+ */
+ if (ioctl(ifsock, SIOCGLIFOINDEX, (char *)&lifr) < 0) {
+ if (errno != ENXIO) {
+ logperror_li(li, "logint_init_from_k:"
+ " (get lifoindex)");
+ }
+ goto error;
+ }
+
+ /*
+ * If this is the logint corresponding to the test address used for
+ * sending probes, then if anything significant has changed we need to
+ * determine the test address again. We ignore changes to the
+ * IFF_FAILED and IFF_RUNNING flags since those happen as a matter of
+ * course.
+ */
+ if (pii->pii_probe_logint == li) {
+ if (((li->li_flags ^ saved_flags) &
+ ~(IFF_FAILED | IFF_RUNNING)) != 0 ||
+ !IN6_ARE_ADDR_EQUAL(&testaddr, &li->li_addr) ||
+ (!ptp && !IN6_ARE_ADDR_EQUAL(&test_subnet,
+ &li->li_subnet)) ||
+ (!ptp && test_subnet_len != li->li_subnet_len) ||
+ (ptp && !IN6_ARE_ADDR_EQUAL(&tgaddr, &li->li_dstaddr))) {
+ /*
+ * Something significant that affects the testaddress
+ * has changed. Redo the testaddress selection later on
+ * in select_test_ifs(). For now do the cleanup and
+ * set pii_probe_logint to NULL.
+ */
+ if (pii->pii_probe_sock != -1)
+ close_probe_socket(pii, _B_TRUE);
+ pii->pii_probe_logint = NULL;
+ }
+ }
+
+
+ /* Update the logint with the values obtained from the kernel. */
+ li->li_addr = testaddr;
+ li->li_in_use = 1;
+ li->li_oifindex = lifr.lifr_index;
+ if (ptp) {
+ li->li_dstaddr = tgaddr;
+ li->li_subnet_len = (pii->pii_af == AF_INET) ?
+ IP_ABITS : IPV6_ABITS;
+ } else {
+ li->li_subnet = test_subnet;
+ li->li_subnet_len = test_subnet_len;
+ }
+
+ if (debug & D_LOGINT)
+ logint_print(li);
+
+ return;
+
+error:
+ logerr("logint_init_from_k: IGNORED %s %s %s addr %s\n",
+ AF_STR(pii->pii_af), pii->pii_name, li->li_name,
+ pr_addr(pii->pii_af, testaddr, abuf, sizeof (abuf)));
+ logint_delete(li);
+}
+
+/*
+ * Delete (unlink and free) a logint.
+ */
+void
+logint_delete(struct logint *li)
+{
+ struct phyint_instance *pii;
+
+ pii = li->li_phyint_inst;
+ assert(pii != NULL);
+
+ if (debug & D_LOGINT) {
+ int af;
+ char abuf[INET6_ADDRSTRLEN];
+
+ af = pii->pii_af;
+ logdebug("logint_delete(%s %s %s/%u)\n",
+ AF_STR(af), li->li_name,
+ pr_addr(af, li->li_addr, abuf, sizeof (abuf)),
+ li->li_subnet_len);
+ }
+
+ /* logint must be in the list of logints */
+ assert(pii->pii_logint == li || li->li_prev != NULL);
+
+ /* Remove the logint from the list of logints */
+ if (li->li_prev == NULL) {
+ /* logint is the 1st in the list */
+ pii->pii_logint = li->li_next;
+ } else {
+ li->li_prev->li_next = li->li_next;
+ }
+ if (li->li_next != NULL)
+ li->li_next->li_prev = li->li_prev;
+ li->li_next = NULL;
+ li->li_prev = NULL;
+
+ /*
+ * If this logint corresponds to the IFF_NOFAILOVER testaddress of
+ * this phyint, then close the associated socket, if it exists
+ */
+ if (pii->pii_probe_logint == li) {
+ if (pii->pii_probe_sock != -1)
+ close_probe_socket(pii, _B_TRUE);
+ pii->pii_probe_logint = NULL;
+ }
+
+ free(li);
+}
+
+static void
+logint_print(struct logint *li)
+{
+ char abuf[INET6_ADDRSTRLEN];
+ int af;
+
+ af = li->li_phyint_inst->pii_af;
+
+ logdebug("logint: %s %s addr %s/%u", AF_STR(af), li->li_name,
+ pr_addr(af, li->li_addr, abuf, sizeof (abuf)), li->li_subnet_len);
+
+ logdebug("\tFlags: %llx in_use %d oifindex %d\n",
+ li->li_flags, li->li_in_use, li->li_oifindex);
+}
+
+char *
+pr_addr(int af, struct in6_addr addr, char *abuf, int len)
+{
+ struct in_addr addr_v4;
+
+ if (af == AF_INET) {
+ IN6_V4MAPPED_TO_INADDR(&addr, &addr_v4);
+ (void) inet_ntop(AF_INET, (void *)&addr_v4, abuf, len);
+ } else {
+ (void) inet_ntop(AF_INET6, (void *)&addr, abuf, len);
+ }
+ return (abuf);
+}
+
+/* Lookup target on its address */
+struct target *
+target_lookup(struct phyint_instance *pii, struct in6_addr addr)
+{
+ struct target *tg;
+
+ if (debug & D_TARGET) {
+ char abuf[INET6_ADDRSTRLEN];
+
+ logdebug("target_lookup(%s %s): addr %s\n",
+ AF_STR(pii->pii_af), pii->pii_name,
+ pr_addr(pii->pii_af, addr, abuf, sizeof (abuf)));
+ }
+
+ for (tg = pii->pii_targets; tg != NULL; tg = tg->tg_next) {
+ if (IN6_ARE_ADDR_EQUAL(&tg->tg_address, &addr))
+ break;
+ }
+ return (tg);
+}
+
+/*
+ * Find and return the next active target, for the next probe.
+ * If no active targets are available, return NULL.
+ */
+struct target *
+target_next(struct target *tg)
+{
+ struct phyint_instance *pii = tg->tg_phyint_inst;
+ struct target *marker = tg;
+ hrtime_t now;
+
+ now = gethrtime();
+
+ /*
+ * Target must be in the list of targets for this phyint
+ * instance.
+ */
+ assert(pii->pii_targets == tg || tg->tg_prev != NULL);
+ assert(pii->pii_targets != NULL);
+
+ /* Return the next active target */
+ do {
+ /*
+ * Go to the next target. If we hit the end,
+ * reset the ptr to the head
+ */
+ tg = tg->tg_next;
+ if (tg == NULL)
+ tg = pii->pii_targets;
+
+ assert(TG_STATUS_VALID(tg->tg_status));
+
+ switch (tg->tg_status) {
+ case TG_ACTIVE:
+ return (tg);
+
+ case TG_UNUSED:
+ assert(pii->pii_targets_are_routers);
+ if (pii->pii_ntargets < MAX_PROBE_TARGETS) {
+ /*
+ * Bubble up the unused target to active
+ */
+ tg->tg_status = TG_ACTIVE;
+ pii->pii_ntargets++;
+ return (tg);
+ }
+ break;
+
+ case TG_SLOW:
+ assert(pii->pii_targets_are_routers);
+ if (tg->tg_latime + MIN_RECOVERY_TIME < now) {
+ /*
+ * Bubble up the slow target to unused
+ */
+ tg->tg_status = TG_UNUSED;
+ }
+ break;
+
+ case TG_DEAD:
+ assert(pii->pii_targets_are_routers);
+ if (tg->tg_latime + MIN_RECOVERY_TIME < now) {
+ /*
+ * Bubble up the dead target to slow
+ */
+ tg->tg_status = TG_SLOW;
+ tg->tg_latime = now;
+ }
+ break;
+ }
+
+ } while (tg != marker);
+
+ return (NULL);
+}
+
+/*
+ * Select the best available target, that is not already TG_ACTIVE,
+ * for the caller. The caller will determine whether it wants to
+ * make the returned target TG_ACTIVE.
+ * The selection order is as follows.
+ * 1. pick a TG_UNSED target, if it exists.
+ * 2. else pick a TG_SLOW target that has recovered, if it exists
+ * 3. else pick any TG_SLOW target, if it exists
+ * 4. else pick a TG_DEAD target that has recovered, if it exists
+ * 5. else pick any TG_DEAD target, if it exists
+ * 6. else return null
+ */
+static struct target *
+target_select_best(struct phyint_instance *pii)
+{
+ struct target *tg;
+ struct target *slow = NULL;
+ struct target *dead = NULL;
+ struct target *slow_recovered = NULL;
+ struct target *dead_recovered = NULL;
+ hrtime_t now;
+
+ now = gethrtime();
+
+ for (tg = pii->pii_targets; tg != NULL; tg = tg->tg_next) {
+ assert(TG_STATUS_VALID(tg->tg_status));
+
+ switch (tg->tg_status) {
+ case TG_UNUSED:
+ return (tg);
+
+ case TG_SLOW:
+ if (tg->tg_latime + MIN_RECOVERY_TIME < now) {
+ slow_recovered = tg;
+ /*
+ * Promote the slow_recoverd to unused
+ */
+ tg->tg_status = TG_UNUSED;
+ } else {
+ slow = tg;
+ }
+ break;
+
+ case TG_DEAD:
+ if (tg->tg_latime + MIN_RECOVERY_TIME < now) {
+ dead_recovered = tg;
+ /*
+ * Promote the dead_recoverd to slow
+ */
+ tg->tg_status = TG_SLOW;
+ tg->tg_latime = now;
+ } else {
+ dead = tg;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (slow_recovered != NULL)
+ return (slow_recovered);
+ else if (slow != NULL)
+ return (slow);
+ else if (dead_recovered != NULL)
+ return (dead_recovered);
+ else
+ return (dead);
+}
+
+/*
+ * Some target was deleted. If we don't have even MIN_PROBE_TARGETS
+ * that are active, pick the next best below.
+ */
+static void
+target_activate_all(struct phyint_instance *pii)
+{
+ struct target *tg;
+
+ assert(pii->pii_ntargets == 0);
+ assert(pii->pii_target_next == NULL);
+ assert(pii->pii_rtt_target_next == NULL);
+ assert(pii->pii_targets_are_routers);
+
+ while (pii->pii_ntargets < MIN_PROBE_TARGETS) {
+ tg = target_select_best(pii);
+ if (tg == NULL) {
+ /* We are out of targets */
+ return;
+ }
+
+ assert(TG_STATUS_VALID(tg->tg_status));
+ assert(tg->tg_status != TG_ACTIVE);
+ tg->tg_status = TG_ACTIVE;
+ pii->pii_ntargets++;
+ if (pii->pii_target_next == NULL) {
+ pii->pii_target_next = tg;
+ pii->pii_rtt_target_next = tg;
+ }
+ }
+}
+
+static struct target *
+target_first(struct phyint_instance *pii)
+{
+ struct target *tg;
+
+ for (tg = pii->pii_targets; tg != NULL; tg = tg->tg_next) {
+ assert(TG_STATUS_VALID(tg->tg_status));
+ if (tg->tg_status == TG_ACTIVE)
+ break;
+ }
+
+ return (tg);
+}
+
+/*
+ * Create a default target entry.
+ */
+void
+target_create(struct phyint_instance *pii, struct in6_addr addr,
+ boolean_t is_router)
+{
+ struct target *tg;
+ struct phyint *pi;
+ struct logint *li;
+
+ if (debug & D_TARGET) {
+ char abuf[INET6_ADDRSTRLEN];
+
+ logdebug("target_create(%s %s, %s)\n",
+ AF_STR(pii->pii_af), pii->pii_name,
+ pr_addr(pii->pii_af, addr, abuf, sizeof (abuf)));
+ }
+
+ /*
+ * If the test address is not yet initialized, do not add
+ * any target, since we cannot determine whether the target
+ * belongs to the same subnet as the test address.
+ */
+ li = pii->pii_probe_logint;
+ if (li == NULL)
+ return;
+
+ /*
+ * If there are multiple subnets associated with an interface, then
+ * add the target to this phyint instance, only if it belongs to the
+ * same subnet as the test address. The reason is that interface
+ * routes derived from non-test-addresses i.e. non-IFF_NOFAILOVER
+ * addresses, will disappear after failover, and the targets will not
+ * be reachable from this interface.
+ */
+ if (!prefix_equal(li->li_subnet, addr, li->li_subnet_len))
+ return;
+
+ if (pii->pii_targets != NULL) {
+ assert(pii->pii_ntargets <= MAX_PROBE_TARGETS);
+ if (is_router) {
+ if (!pii->pii_targets_are_routers) {
+ /*
+ * Prefer router over hosts. Using hosts is a
+ * fallback mechanism, hence delete all host
+ * targets.
+ */
+ while (pii->pii_targets != NULL)
+ target_delete(pii->pii_targets);
+ }
+ } else {
+ /*
+ * Routers take precedence over hosts. If this
+ * is a router list and we are trying to add a
+ * host, just return. If this is a host list
+ * and if we have sufficient targets, just return
+ */
+ if (pii->pii_targets_are_routers ||
+ pii->pii_ntargets == MAX_PROBE_TARGETS)
+ return;
+ }
+ }
+
+ tg = calloc(1, sizeof (struct target));
+ if (tg == NULL) {
+ logperror("target_create: calloc");
+ return;
+ }
+
+ tg->tg_phyint_inst = pii;
+ tg->tg_address = addr;
+ tg->tg_in_use = 1;
+ tg->tg_rtt_sa = -1;
+ tg->tg_num_deferred = 0;
+
+ /*
+ * If this is the first target, set 'pii_targets_are_routers'
+ * The list of targets is either a list of hosts or list or
+ * routers, but not a mix.
+ */
+ if (pii->pii_targets == NULL) {
+ assert(pii->pii_ntargets == 0);
+ assert(pii->pii_target_next == NULL);
+ assert(pii->pii_rtt_target_next == NULL);
+ pii->pii_targets_are_routers = is_router ? 1 : 0;
+ }
+
+ if (pii->pii_ntargets == MAX_PROBE_TARGETS) {
+ assert(pii->pii_targets_are_routers);
+ assert(pii->pii_target_next != NULL);
+ assert(pii->pii_rtt_target_next != NULL);
+ tg->tg_status = TG_UNUSED;
+ } else {
+ if (pii->pii_ntargets == 0) {
+ assert(pii->pii_target_next == NULL);
+ pii->pii_target_next = tg;
+ pii->pii_rtt_target_next = tg;
+ }
+ pii->pii_ntargets++;
+ tg->tg_status = TG_ACTIVE;
+ }
+
+ target_insert(pii, tg);
+
+ /*
+ * Change to running state, if this phyint instance is capable of
+ * sending and receiving probes. i.e if we know of at least 1 target,
+ * and this phyint instance socket is bound to the IFF_NOFAILOVER
+ * address. More details in phyint state diagram in probe.c.
+ */
+ pi = pii->pii_phyint;
+ if (pi->pi_state == PI_NOTARGETS && PROBE_CAPABLE(pii)) {
+ if (pi->pi_flags & IFF_FAILED)
+ phyint_chstate(pi, PI_FAILED);
+ else
+ phyint_chstate(pi, PI_RUNNING);
+ }
+}
+
+/*
+ * Add the target address named by `addr' to phyint instance `pii' if it does
+ * not already exist. If the target is a router, `is_router' should be set to
+ * B_TRUE.
+ */
+void
+target_add(struct phyint_instance *pii, struct in6_addr addr,
+ boolean_t is_router)
+{
+ struct target *tg;
+
+ if (pii == NULL)
+ return;
+
+ tg = target_lookup(pii, addr);
+
+ /*
+ * If the target does not exist, create it; target_create() will set
+ * tg_in_use to true. If it exists already, and it is a router
+ * target, set tg_in_use to to true, so that init_router_targets()
+ * won't delete it
+ */
+ if (tg == NULL)
+ target_create(pii, addr, is_router);
+ else if (is_router)
+ tg->tg_in_use = 1;
+}
+
+/*
+ * Insert target at head of linked list of targets for the associated
+ * phyint instance
+ */
+static void
+target_insert(struct phyint_instance *pii, struct target *tg)
+{
+ tg->tg_next = pii->pii_targets;
+ tg->tg_prev = NULL;
+ if (tg->tg_next != NULL)
+ tg->tg_next->tg_prev = tg;
+ pii->pii_targets = tg;
+}
+
+/*
+ * Delete a target (unlink and free).
+ */
+void
+target_delete(struct target *tg)
+{
+ int af;
+ struct phyint_instance *pii;
+ struct phyint_instance *pii_other;
+
+ pii = tg->tg_phyint_inst;
+ af = pii->pii_af;
+
+ if (debug & D_TARGET) {
+ char abuf[INET6_ADDRSTRLEN];
+
+ logdebug("target_delete(%s %s, %s)\n",
+ AF_STR(af), pii->pii_name,
+ pr_addr(af, tg->tg_address, abuf, sizeof (abuf)));
+ }
+
+ /*
+ * Target must be in the list of targets for this phyint
+ * instance.
+ */
+ assert(pii->pii_targets == tg || tg->tg_prev != NULL);
+
+ /*
+ * Reset all references to 'tg' in the probe information
+ * for this phyint.
+ */
+ reset_pii_probes(pii, tg);
+
+ /*
+ * Remove this target from the list of targets of this
+ * phyint instance.
+ */
+ if (tg->tg_prev == NULL) {
+ pii->pii_targets = tg->tg_next;
+ } else {
+ tg->tg_prev->tg_next = tg->tg_next;
+ }
+
+ if (tg->tg_next != NULL)
+ tg->tg_next->tg_prev = tg->tg_prev;
+
+ tg->tg_next = NULL;
+ tg->tg_prev = NULL;
+
+ if (tg->tg_status == TG_ACTIVE)
+ pii->pii_ntargets--;
+
+ /*
+ * Adjust the next target to probe, if it points to
+ * to the currently deleted target.
+ */
+ if (pii->pii_target_next == tg)
+ pii->pii_target_next = target_first(pii);
+
+ if (pii->pii_rtt_target_next == tg)
+ pii->pii_rtt_target_next = target_first(pii);
+
+ free(tg);
+
+ /*
+ * The number of active targets pii_ntargets == 0 iff
+ * the next active target pii->pii_target_next == NULL
+ */
+ if (pii->pii_ntargets != 0) {
+ assert(pii->pii_target_next != NULL);
+ assert(pii->pii_rtt_target_next != NULL);
+ assert(pii->pii_target_next->tg_status == TG_ACTIVE);
+ assert(pii->pii_rtt_target_next->tg_status == TG_ACTIVE);
+ return;
+ }
+
+ /* At this point, we don't have any active targets. */
+ assert(pii->pii_target_next == NULL);
+ assert(pii->pii_rtt_target_next == NULL);
+
+ if (pii->pii_targets_are_routers) {
+ /*
+ * Activate any TG_SLOW or TG_DEAD router targets,
+ * since we don't have any other targets
+ */
+ target_activate_all(pii);
+
+ if (pii->pii_ntargets != 0) {
+ assert(pii->pii_target_next != NULL);
+ assert(pii->pii_rtt_target_next != NULL);
+ assert(pii->pii_target_next->tg_status == TG_ACTIVE);
+ assert(pii->pii_rtt_target_next->tg_status ==
+ TG_ACTIVE);
+ return;
+ }
+ }
+
+ /*
+ * If we still don't have any active targets, the list must
+ * must be really empty. There aren't even TG_SLOW or TG_DEAD
+ * targets. Zero out the probe stats since it will not be
+ * relevant any longer.
+ */
+ assert(pii->pii_targets == NULL);
+ clear_pii_probe_stats(pii);
+ pii_other = phyint_inst_other(pii);
+
+ /*
+ * If there are no targets on both instances,
+ * go back to PI_NOTARGETS state, since we cannot
+ * probe this phyint any more. For more details,
+ * please see phyint state diagram in mpd_probe.c.
+ */
+ if (!PROBE_CAPABLE(pii_other))
+ phyint_chstate(pii->pii_phyint, PI_NOTARGETS);
+}
+
+/*
+ * Flush the target list of every phyint in the group, if the list
+ * is a host target list. This is called if group failure is suspected.
+ * If all targets have failed, multicast will subsequently discover new
+ * targets. Else it is a group failure.
+ * Note: This function is a no-op if the list is a router target list.
+ */
+static void
+target_flush_hosts(struct phyint_group *pg)
+{
+ struct phyint *pi;
+ struct phyint_instance *pii;
+
+ if (debug & D_TARGET)
+ logdebug("target_flush_hosts(%s)\n", pg->pg_name);
+
+ for (pi = pg->pg_phyint; pi != NULL; pi = pi->pi_pgnext) {
+ pii = pi->pi_v4;
+ if (pii != NULL && !pii->pii_targets_are_routers) {
+ /*
+ * Delete all the targets. When the list becomes
+ * empty, target_delete() will set pii->pii_targets
+ * to NULL.
+ */
+ while (pii->pii_targets != NULL)
+ target_delete(pii->pii_targets);
+ }
+ pii = pi->pi_v6;
+ if (pii != NULL && !pii->pii_targets_are_routers) {
+ /*
+ * Delete all the targets. When the list becomes
+ * empty, target_delete() will set pii->pii_targets
+ * to NULL.
+ */
+ while (pii->pii_targets != NULL)
+ target_delete(pii->pii_targets);
+ }
+ }
+}
+
+/*
+ * Reset all references to 'target' in the probe info, as this target is
+ * being deleted. The pr_target field is guaranteed to be non-null if
+ * pr_status is PR_UNACKED. So we change the pr_status to PR_LOST, so that
+ * pr_target will not be accessed unconditionally.
+ */
+static void
+reset_pii_probes(struct phyint_instance *pii, struct target *tg)
+{
+ int i;
+
+ for (i = 0; i < PROBE_STATS_COUNT; i++) {
+ if (pii->pii_probes[i].pr_target == tg) {
+ pii->pii_probes[i].pr_target = NULL;
+ if (pii->pii_probes[i].pr_status == PR_UNACKED)
+ pii->pii_probes[i].pr_status = PR_LOST;
+ }
+ }
+
+}
+
+/*
+ * Clear the probe statistics array.
+ */
+void
+clear_pii_probe_stats(struct phyint_instance *pii)
+{
+ bzero(pii->pii_probes, sizeof (struct probe_stats) * PROBE_STATS_COUNT);
+ /* Reset the next probe index in the probe stats array */
+ pii->pii_probe_next = 0;
+}
+
+static void
+target_print(struct target *tg)
+{
+ char abuf[INET6_ADDRSTRLEN];
+ char buf[128];
+ char buf2[128];
+ int af;
+ int i;
+
+ af = tg->tg_phyint_inst->pii_af;
+
+ logdebug("Target on %s %s addr %s\n"
+ "status %d rtt_sa %d rtt_sd %d crtt %d tg_in_use %d\n",
+ AF_STR(af), tg->tg_phyint_inst->pii_name,
+ pr_addr(af, tg->tg_address, abuf, sizeof (abuf)),
+ tg->tg_status, tg->tg_rtt_sa, tg->tg_rtt_sd,
+ tg->tg_crtt, tg->tg_in_use);
+
+ buf[0] = '\0';
+ for (i = 0; i < tg->tg_num_deferred; i++) {
+ (void) snprintf(buf2, sizeof (buf2), " %dms",
+ tg->tg_deferred[i]);
+ (void) strlcat(buf, buf2, sizeof (buf));
+ }
+ logdebug("deferred rtts:%s\n", buf);
+}
+
+void
+phyint_inst_print_all(void)
+{
+ struct phyint_instance *pii;
+
+ for (pii = phyint_instances; pii != NULL; pii = pii->pii_next) {
+ phyint_inst_print(pii);
+ }
+}
+
+/*
+ * Convert length for a mask to the mask.
+ */
+static void
+ip_index_to_mask_v6(uint_t masklen, struct in6_addr *bitmask)
+{
+ int j;
+
+ assert(masklen <= IPV6_ABITS);
+ bzero((char *)bitmask, sizeof (*bitmask));
+
+ /* Make the 'masklen' leftmost bits one */
+ for (j = 0; masklen > 8; masklen -= 8, j++)
+ bitmask->s6_addr[j] = 0xff;
+
+ bitmask->s6_addr[j] = 0xff << (8 - masklen);
+
+}
+
+/*
+ * Compare two prefixes that have the same prefix length.
+ * Fails if the prefix length is unreasonable.
+ */
+static boolean_t
+prefix_equal(struct in6_addr p1, struct in6_addr p2, int prefix_len)
+{
+ uchar_t mask;
+ int j;
+
+ if (prefix_len < 0 || prefix_len > IPV6_ABITS)
+ return (_B_FALSE);
+
+ for (j = 0; prefix_len > 8; prefix_len -= 8, j++)
+ if (p1.s6_addr[j] != p2.s6_addr[j])
+ return (_B_FALSE);
+
+ /* Make the N leftmost bits one */
+ mask = 0xff << (8 - prefix_len);
+ if ((p1.s6_addr[j] & mask) != (p2.s6_addr[j] & mask))
+ return (_B_FALSE);
+
+ return (_B_TRUE);
+}
+
+/*
+ * Get the number of UP logints (excluding IFF_NOFAILOVERs), on both
+ * IPv4 and IPv6 put together. The phyint with the least such number
+ * will be used as the failover destination, if no standby interface is
+ * available
+ */
+int
+logint_upcount(struct phyint *pi)
+{
+ struct logint *li;
+ struct phyint_instance *pii;
+ int count = 0;
+
+ pii = pi->pi_v4;
+ if (pii != NULL) {
+ for (li = pii->pii_logint; li != NULL; li = li->li_next) {
+ if ((li->li_flags &
+ (IFF_UP | IFF_NOFAILOVER)) == IFF_UP) {
+ count++;
+ }
+ }
+ }
+
+ pii = pi->pi_v6;
+ if (pii != NULL) {
+ for (li = pii->pii_logint; li != NULL; li = li->li_next) {
+ if ((li->li_flags &
+ (IFF_UP | IFF_NOFAILOVER)) == IFF_UP) {
+ count++;
+ }
+ }
+ }
+
+ return (count);
+}
+
+/*
+ * Get the phyint instance with the other (IPv4 / IPv6) protocol
+ */
+struct phyint_instance *
+phyint_inst_other(struct phyint_instance *pii)
+{
+ if (pii->pii_af == AF_INET)
+ return (pii->pii_phyint->pi_v6);
+ else
+ return (pii->pii_phyint->pi_v4);
+}
+
+/*
+ * Post an EC_IPMP sysevent of subclass `subclass' and attributes `nvl'.
+ * Before sending the event, it prepends the current version of the IPMP
+ * sysevent API. Returns 0 on success, -1 on failure (in either case,
+ * `nvl' is freed).
+ */
+static int
+post_event(const char *subclass, nvlist_t *nvl)
+{
+ sysevent_id_t eid;
+
+ errno = nvlist_add_uint32(nvl, IPMP_EVENT_VERSION,
+ IPMP_EVENT_CUR_VERSION);
+ if (errno != 0) {
+ logerr("cannot create `%s' event: %s", subclass,
+ strerror(errno));
+ goto failed;
+ }
+
+ if (sysevent_post_event(EC_IPMP, (char *)subclass, SUNW_VENDOR,
+ "in.mpathd", nvl, &eid) == -1) {
+ logerr("cannot send `%s' event: %s\n", subclass,
+ strerror(errno));
+ goto failed;
+ }
+
+ nvlist_free(nvl);
+ return (0);
+failed:
+ nvlist_free(nvl);
+ return (-1);
+}
+
+/*
+ * Return the external IPMP state associated with phyint `pi'.
+ */
+static ipmp_if_state_t
+ifstate(struct phyint *pi)
+{
+ switch (pi->pi_state) {
+ case PI_NOTARGETS:
+ return (IPMP_IF_UNKNOWN);
+
+ case PI_OFFLINE:
+ return (IPMP_IF_OFFLINE);
+
+ case PI_FAILED:
+ return (IPMP_IF_FAILED);
+
+ case PI_RUNNING:
+ return (IPMP_IF_OK);
+ }
+
+ logerr("ifstate: unknown state %d; aborting\n", pi->pi_state);
+ abort();
+ /* NOTREACHED */
+}
+
+/*
+ * Return the external IPMP interface type associated with phyint `pi'.
+ */
+static ipmp_if_type_t
+iftype(struct phyint *pi)
+{
+ if (pi->pi_flags & IFF_STANDBY)
+ return (IPMP_IF_STANDBY);
+ else
+ return (IPMP_IF_NORMAL);
+}
+
+/*
+ * Return the external IPMP group state associated with phyint group `pg'.
+ */
+static ipmp_group_state_t
+groupstate(struct phyint_group *pg)
+{
+ return (GROUP_FAILED(pg) ? IPMP_GROUP_FAILED : IPMP_GROUP_OK);
+}
+
+/*
+ * Generate an ESC_IPMP_GROUP_STATE sysevent for phyint group `pg'.
+ * Returns 0 on success, -1 on failure.
+ */
+static int
+phyint_group_state_event(struct phyint_group *pg)
+{
+ nvlist_t *nvl;
+
+ errno = nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0);
+ if (errno != 0) {
+ logperror("cannot create `group state change' event");
+ return (-1);
+ }
+
+ errno = nvlist_add_string(nvl, IPMP_GROUP_NAME, pg->pg_name);
+ if (errno != 0)
+ goto failed;
+
+ errno = nvlist_add_uint64(nvl, IPMP_GROUP_SIGNATURE, pg->pg_sig);
+ if (errno != 0)
+ goto failed;
+
+ errno = nvlist_add_uint32(nvl, IPMP_GROUP_STATE, groupstate(pg));
+ if (errno != 0)
+ goto failed;
+
+ return (post_event(ESC_IPMP_GROUP_STATE, nvl));
+failed:
+ logperror("cannot create `group state change' event");
+ nvlist_free(nvl);
+ return (-1);
+}
+
+/*
+ * Generate an ESC_IPMP_GROUP_CHANGE sysevent of type `op' for phyint group
+ * `pg'. Returns 0 on success, -1 on failure.
+ */
+static int
+phyint_group_change_event(struct phyint_group *pg, ipmp_group_op_t op)
+{
+ nvlist_t *nvl;
+
+ errno = nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0);
+ if (errno != 0) {
+ logperror("cannot create `group change' event");
+ return (-1);
+ }
+
+ errno = nvlist_add_string(nvl, IPMP_GROUP_NAME, pg->pg_name);
+ if (errno != 0)
+ goto failed;
+
+ errno = nvlist_add_uint64(nvl, IPMP_GROUP_SIGNATURE, pg->pg_sig);
+ if (errno != 0)
+ goto failed;
+
+ errno = nvlist_add_uint64(nvl, IPMP_GROUPLIST_SIGNATURE,
+ phyint_grouplistsig);
+ if (errno != 0)
+ goto failed;
+
+ errno = nvlist_add_uint32(nvl, IPMP_GROUP_OPERATION, op);
+ if (errno != 0)
+ goto failed;
+
+ return (post_event(ESC_IPMP_GROUP_CHANGE, nvl));
+failed:
+ logperror("cannot create `group change' event");
+ nvlist_free(nvl);
+ return (-1);
+}
+
+/*
+ * Generate an ESC_IPMP_GROUP_MEMBER_CHANGE sysevent for phyint `pi' in
+ * group `pg'. Returns 0 on success, -1 on failure.
+ */
+static int
+phyint_group_member_event(struct phyint_group *pg, struct phyint *pi,
+ ipmp_if_op_t op)
+{
+ nvlist_t *nvl;
+
+ errno = nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0);
+ if (errno != 0) {
+ logperror("cannot create `group member change' event");
+ return (-1);
+ }
+
+ errno = nvlist_add_string(nvl, IPMP_GROUP_NAME, pg->pg_name);
+ if (errno != 0)
+ goto failed;
+
+ errno = nvlist_add_uint64(nvl, IPMP_GROUP_SIGNATURE, pg->pg_sig);
+ if (errno != 0)
+ goto failed;
+
+ errno = nvlist_add_uint32(nvl, IPMP_IF_OPERATION, op);
+ if (errno != 0)
+ goto failed;
+
+ errno = nvlist_add_string(nvl, IPMP_IF_NAME, pi->pi_name);
+ if (errno != 0)
+ goto failed;
+
+ errno = nvlist_add_uint32(nvl, IPMP_IF_TYPE, iftype(pi));
+ if (errno != 0)
+ goto failed;
+
+ errno = nvlist_add_uint32(nvl, IPMP_IF_STATE, ifstate(pi));
+ if (errno != 0)
+ goto failed;
+
+ return (post_event(ESC_IPMP_GROUP_MEMBER_CHANGE, nvl));
+failed:
+ logperror("cannot create `group member change' event");
+ nvlist_free(nvl);
+ return (-1);
+
+}
+
+/*
+ * Generate an ESC_IPMP_IF_CHANGE sysevent for phyint `pi' in group `pg'.
+ * Returns 0 on success, -1 on failure.
+ */
+static int
+phyint_state_event(struct phyint_group *pg, struct phyint *pi)
+{
+ nvlist_t *nvl;
+
+ errno = nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0);
+ if (errno != 0) {
+ logperror("cannot create `interface change' event");
+ return (-1);
+ }
+
+ errno = nvlist_add_string(nvl, IPMP_GROUP_NAME, pg->pg_name);
+ if (errno != 0)
+ goto failed;
+
+ errno = nvlist_add_uint64(nvl, IPMP_GROUP_SIGNATURE, pg->pg_sig);
+ if (errno != 0)
+ goto failed;
+
+ errno = nvlist_add_string(nvl, IPMP_IF_NAME, pi->pi_name);
+ if (errno != 0)
+ goto failed;
+
+ errno = nvlist_add_uint32(nvl, IPMP_IF_TYPE, iftype(pi));
+ if (errno != 0)
+ goto failed;
+
+ errno = nvlist_add_uint32(nvl, IPMP_IF_STATE, ifstate(pi));
+ if (errno != 0)
+ goto failed;
+
+ return (post_event(ESC_IPMP_IF_CHANGE, nvl));
+failed:
+ logperror("cannot create `interface change' event");
+ nvlist_free(nvl);
+ return (-1);
+
+}
+
+/*
+ * Generate a signature for use. The signature is conceptually divided
+ * into two pieces: a random 16-bit "generation number" and a 48-bit
+ * monotonically increasing integer. The generation number protects
+ * against stale updates to entities (e.g., IPMP groups) that have been
+ * deleted and since recreated.
+ */
+static uint64_t
+gensig(void)
+{
+ static int seeded = 0;
+
+ if (seeded == 0) {
+ srand48((long)gethrtime());
+ seeded++;
+ }
+
+ return ((uint64_t)lrand48() << 48 | 1);
+}
+
+/*
+ * Store the information associated with group `grname' into a dynamically
+ * allocated structure pointed to by `*grinfopp'. Returns an IPMP error code.
+ */
+unsigned int
+getgroupinfo(const char *grname, ipmp_groupinfo_t **grinfopp)
+{
+ struct phyint_group *pg;
+ struct phyint *pi;
+ char (*ifs)[LIFNAMSIZ];
+ unsigned int nif, i;
+
+ pg = phyint_group_lookup(grname);
+ if (pg == NULL)
+ return (IPMP_EUNKGROUP);
+
+ /*
+ * Tally up the number of interfaces, allocate an array to hold them,
+ * and insert their names into the array.
+ */
+ for (nif = 0, pi = pg->pg_phyint; pi != NULL; pi = pi->pi_pgnext)
+ nif++;
+
+ ifs = alloca(nif * sizeof (*ifs));
+ for (i = 0, pi = pg->pg_phyint; pi != NULL; pi = pi->pi_pgnext, i++) {
+ assert(i < nif);
+ (void) strlcpy(ifs[i], pi->pi_name, LIFNAMSIZ);
+ }
+ assert(i == nif);
+
+ *grinfopp = ipmp_groupinfo_create(pg->pg_name, pg->pg_sig,
+ groupstate(pg), nif, ifs);
+ return (*grinfopp == NULL ? IPMP_ENOMEM : IPMP_SUCCESS);
+}
+
+/*
+ * Store the information associated with interface `ifname' into a dynamically
+ * allocated structure pointed to by `*ifinfopp'. Returns an IPMP error code.
+ */
+unsigned int
+getifinfo(const char *ifname, ipmp_ifinfo_t **ifinfopp)
+{
+ struct phyint *pi;
+
+ pi = phyint_lookup(ifname);
+ if (pi == NULL)
+ return (IPMP_EUNKIF);
+
+ *ifinfopp = ipmp_ifinfo_create(pi->pi_name, pi->pi_group->pg_name,
+ ifstate(pi), iftype(pi));
+ return (*ifinfopp == NULL ? IPMP_ENOMEM : IPMP_SUCCESS);
+}
+
+/*
+ * Store the current list of IPMP groups into a dynamically allocated
+ * structure pointed to by `*grlistpp'. Returns an IPMP error code.
+ */
+unsigned int
+getgrouplist(ipmp_grouplist_t **grlistpp)
+{
+ struct phyint_group *pg;
+ char (*groups)[LIFGRNAMSIZ];
+ unsigned int i, ngroup;
+
+ /*
+ * Tally up the number of groups, allocate an array to hold them, and
+ * insert their names into the array.
+ */
+ for (ngroup = 0, pg = phyint_groups; pg != NULL; pg = pg->pg_next)
+ ngroup++;
+
+ groups = alloca(ngroup * sizeof (*groups));
+ for (i = 0, pg = phyint_groups; pg != NULL; pg = pg->pg_next, i++) {
+ assert(i < ngroup);
+ (void) strlcpy(groups[i], pg->pg_name, LIFGRNAMSIZ);
+ }
+ assert(i == ngroup);
+
+ *grlistpp = ipmp_grouplist_create(phyint_grouplistsig, ngroup, groups);
+ return (*grlistpp == NULL ? IPMP_ENOMEM : IPMP_SUCCESS);
+}
+
+/*
+ * Store a snapshot of the IPMP subsystem into a dynamically allocated
+ * structure pointed to by `*snapp'. Returns an IPMP error code.
+ */
+unsigned int
+getsnap(ipmp_snap_t **snapp)
+{
+ ipmp_grouplist_t *grlistp;
+ ipmp_groupinfo_t *grinfop;
+ ipmp_ifinfo_t *ifinfop;
+ ipmp_snap_t *snap;
+ struct phyint *pi;
+ unsigned int i;
+ int retval;
+
+ snap = ipmp_snap_create();
+ if (snap == NULL)
+ return (IPMP_ENOMEM);
+
+ /*
+ * Add group list.
+ */
+ retval = getgrouplist(&snap->sn_grlistp);
+ if (retval != IPMP_SUCCESS) {
+ ipmp_snap_free(snap);
+ return (retval);
+ }
+
+ /*
+ * Add information for each group in the list.
+ */
+ grlistp = snap->sn_grlistp;
+ for (i = 0; i < grlistp->gl_ngroup; i++) {
+ retval = getgroupinfo(grlistp->gl_groups[i], &grinfop);
+ if (retval != IPMP_SUCCESS) {
+ ipmp_snap_free(snap);
+ return (retval);
+ }
+ retval = ipmp_snap_addgroupinfo(snap, grinfop);
+ if (retval != IPMP_SUCCESS) {
+ ipmp_freegroupinfo(grinfop);
+ ipmp_snap_free(snap);
+ return (retval);
+ }
+ }
+
+ /*
+ * Add information for each configured phyint.
+ */
+ for (pi = phyints; pi != NULL; pi = pi->pi_next) {
+ retval = getifinfo(pi->pi_name, &ifinfop);
+ if (retval != IPMP_SUCCESS) {
+ ipmp_snap_free(snap);
+ return (retval);
+ }
+ retval = ipmp_snap_addifinfo(snap, ifinfop);
+ if (retval != IPMP_SUCCESS) {
+ ipmp_freeifinfo(ifinfop);
+ ipmp_snap_free(snap);
+ return (retval);
+ }
+ }
+
+ *snapp = snap;
+ return (IPMP_SUCCESS);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.mpathd/mpd_tables.h b/usr/src/cmd/cmd-inet/usr.lib/in.mpathd/mpd_tables.h
new file mode 100644
index 0000000000..f466c9f9a2
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.mpathd/mpd_tables.h
@@ -0,0 +1,476 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _MPD_TABLES_H
+#define _MPD_TABLES_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Terminology:
+ *
+ * phyint: A NIC eg. hme0. This is represented as 'struct phyint'
+ *
+ * phyint instance: A protocol instance of a phyint. Eg. the IPv4 instance of
+ * hme0 or the IPv6 instance of hme0. (struct phyint_instance)
+ *
+ * logint: A logical interface eg. hme0:1 (struct logint)
+ *
+ * phyint_group: A group of phyints i.e. physical interfaces that are
+ * (i) connected to the same level 2 topology e.g. the same ethernet
+ * switch AND
+ * (ii) share the same phyint group name.
+ * Load spreading and failover occur across members of the same phyint group.
+ * phyint group members must be homogenous. i.e. if a phyint belonging to a
+ * phyint group has a IPv6 protocol instance, then all members of the phyint
+ * group, must have IPv6 protocol instances. (struct phyint_group)
+ */
+
+/*
+ * Parameter passed to try_failover(), indicating the type of failover
+ * that is requested.
+ */
+#define FAILOVER_NORMAL 1 /* Failover to another phyint */
+ /* that is preferably a standby */
+#define FAILOVER_TO_NONSTANDBY 2 /* Failover to non-standby phyint */
+#define FAILOVER_TO_ANY 3 /* Failover to any available phyint */
+
+#define MAXDEFERREDRTT 1 /* Maximum number of deferred rtts */
+
+/*
+ * Status of the phyint, expressed by the return code of failure_state()
+ */
+#define PHYINT_OK 0 /* No failure detected */
+#define PHYINT_FAILURE 1 /* NIC failure detected */
+#define GROUP_FAILURE 2 /* All NICs have failed */
+
+/*
+ * Return values of phyint_inst_update_from_k()
+ */
+#define PI_OK 1 /* Phyint matches in the kernel */
+#define PI_DELETED 2 /* Phyint has vanished in the kernel */
+#define PI_IFINDEX_CHANGED 3 /* Phyint's ifindex has changed */
+#define PI_IOCTL_ERROR 4 /* Some ioctl error */
+#define PI_GROUP_CHANGED 5 /* The phyint has changed group. */
+
+/*
+ * Though IFF_POINTOPOINT is a logint property, for the purpose of
+ * failover, we treat it as a phyint property. Note that we cannot failover
+ * individual logints.
+ */
+#define PHYINT_FLAGS(flags) \
+ (((flags) & (IFF_STANDBY | IFF_INACTIVE | IFF_FAILED | IFF_OFFLINE | \
+ IFF_POINTOPOINT | IFF_RUNNING)) | (handle_link_notifications ? \
+ 0 : IFF_RUNNING))
+
+/* A Phyint can have up to 2 instances, the IPv4 and the IPv6 instance */
+#define PHYINT_INSTANCE(pi, af) \
+ ((af) == AF_INET ? (pi)->pi_v4 : (pi)->pi_v6)
+
+/*
+ * A phyint instance is probe *enabled* if it has been configured with a probe
+ * address (i.e., an IFF_NOFAILOVER address). It is probe *capable* if it is
+ * also able to send probes (i.e., has one or more targets available).
+ */
+#define PROBE_ENABLED(pii) \
+ (((pii) != NULL) && ((pii)->pii_probe_logint != NULL))
+
+#define PROBE_CAPABLE(pii) \
+ (PROBE_ENABLED(pii) && ((pii)->pii_ntargets != 0))
+
+/* Subtract b from a modulo n. i.e. (a - b) mod n */
+#define MOD_SUB(a, b, n) \
+ ((((a) + (n)) - (b)) % (n))
+
+/* Increment modulo n */
+#define MOD_INCR(a, n) \
+ (((a) + 1) % (n))
+
+/* Decrement modulo n */
+#define MOD_DCR(a, n) \
+ MOD_SUB(a, 1, n)
+
+/*
+ * 'index' represents an index into the circular probe stats array of
+ * size PROBE_STATS_COUNT. 0 <= index < PROBE_STATS_COUNT. This is used
+ * to access members of the pii_probes[] array defined in the phyint_instance
+ * structure.
+ */
+#define PROBE_INDEX_PREV(index) \
+ MOD_DCR(index, PROBE_STATS_COUNT)
+
+#define PROBE_INDEX_NEXT(index) \
+ MOD_INCR(index, PROBE_STATS_COUNT)
+
+
+/*
+ * If we receive more than LINK_UP_PERMIN "link up" notifications in a minute,
+ * then don't actually perform the repair operation until we've dropped back
+ * below the threshold (or we have a probe address and our probes indicate
+ * that the link is functioning again). This is to prevent link flapping in
+ * the case where we don't have a probe address.
+ */
+#define LINK_UP_PERMIN 2
+
+#define LINK_DOWN(pi) ((pi)->pi_link_state == 0)
+#define LINK_UP(pi) (!LINK_DOWN(pi))
+#define FLAGS_TO_LINK_STATE(pi) (((pi)->pi_flags & IFF_RUNNING) != 0)
+#define UPDATE_LINK_STATE(pi) ((pi)->pi_link_state = \
+ FLAGS_TO_LINK_STATE(pi) ? 1 : 0)
+#define INIT_LINK_STATE(pi) ((pi)->pi_link_state = 1)
+
+#define SINGLETON_GROUP(pi) ((pi) != NULL && \
+ (pi)->pi_group->pg_name[0] != '\0' && \
+ (pi)->pi_group->pg_phyint == (pi) && (pi)->pi_pgnext == NULL)
+
+/*
+ * Phyint group states; see below for the phyint group definition.
+ */
+enum pg_state {
+ PG_RUNNING = 1, /* at least one interface in group is working */
+ PG_FAILED = 2 /* group has failed completely */
+};
+
+/*
+ * Convenience macro to check if the whole group has failed.
+ */
+#define GROUP_FAILED(pg) ((pg)->pg_groupfailed)
+
+/*
+ * A doubly linked list of all phyint groups in the system.
+ * A phyint group is identified by its group name.
+ */
+struct phyint_group {
+ char pg_name[LIFNAMSIZ + 1]; /* Phyint group name */
+ struct phyint *pg_phyint; /* List of phyints in this group */
+ struct phyint_group *pg_next; /* Next phyint group */
+ struct phyint_group *pg_prev; /* Prev phyint group */
+ uint64_t pg_sig; /* Current signature of this group */
+ int pg_probeint; /* Interval between probes */
+ int pg_fdt; /* Time needed to detect failure */
+ uint_t
+ pg_groupfailed : 1; /* The whole group has failed */
+};
+
+/*
+ * Phyint states; see below for the phyint definition.
+ */
+enum pi_state {
+ PI_NOTARGETS = 1, /* Phyint has no targets */
+ PI_RUNNING = 2, /* Phyint is functioning */
+ PI_FAILED = 3, /* Phyint is failed */
+ PI_OFFLINE = 4 /* Phyint is offline */
+};
+
+/*
+ * Representation of a NIC or a phyint. There is a list of all known phyints.
+ * There is also a list of phyints belonging to a phyint group, one list
+ * per phyint group.
+ */
+struct phyint {
+ char pi_name[LIFNAMSIZ + 1]; /* Phyint name eg. le0 */
+ struct phyint_instance *pi_v4; /* The IPv4 instance */
+ struct phyint_instance *pi_v6; /* The IPv6 instance */
+ struct phyint_group *pi_group; /* Pointer to the group */
+ struct phyint *pi_next; /* List of all phyints */
+ struct phyint *pi_prev; /* List of all phyints */
+ struct phyint *pi_pgnext; /* List of phyints in this group */
+ struct phyint *pi_pgprev; /* List of phyints in this group */
+ uint_t pi_ifindex; /* interface index */
+ enum pi_state pi_state; /* State of the phyint */
+ uint64_t pi_flags; /* Phyint flags from kernel */
+ uint16_t pi_icmpid; /* icmp id in icmp echo request */
+ /*
+ * The pi_whenup array is a circular buffer of the most recent
+ * times (in milliseconds since some arbitrary point of time in
+ * the past) that the interface was brought up; pi_whendx identifies
+ * the oldest element of the array.
+ */
+ uint_t pi_whenup[LINK_UP_PERMIN];
+ unsigned int pi_whendx;
+
+ uint_t
+ pi_empty : 1, /* failover done, empty */
+ pi_full : 1, /* failback done, full */
+ /* More details in probe.c */
+ pi_taddrmsg_printed : 1, /* testaddr msg printed */
+ pi_cfgmsg_printed : 1, /* bad config msg printed */
+ pi_lfmsg_printed : 1, /* link-flapping msg printed */
+ pi_link_state : 1; /* interface link state */
+};
+
+/*
+ * A doubly linked list of all phyint_instances each of which contains a
+ * doubly linked list of logical interfaces and targets. For eg. if both
+ * IPv4 and IPv6 are used over hme0, we have 2 phyint instances, 1 for each
+ * protocol.
+ */
+struct phyint_instance {
+ struct phyint_instance *pii_next; /* List of all phyint insts */
+ struct phyint_instance *pii_prev; /* List of all phyint insts */
+
+ struct phyint *pii_phyint; /* Back pointer to the phyint */
+ struct target *pii_targets; /* List of targets on this link */
+ struct logint *pii_probe_logint; /* IFF_NOFAILOVER addr for probing */
+ struct logint *pii_logint; /* Doubly linked list of logical ifs */
+
+ int pii_probe_sock; /* Socket for ICMP Probe packets */
+ int pii_af; /* Address family */
+ uint16_t pii_rack; /* highest acknowledged seq number */
+ uint16_t pii_snxt; /* sequence number of next probe */
+ uint_t pii_snxt_time; /* actual next probe time that */
+ /* includes some randomness */
+
+ uint_t pii_snxt_basetime; /* strictly periodic base probe time */
+ /* for all periodic probes */
+ uint_t pii_fd_snxt_basetime; /* strictly periodic base probe time */
+ /* for failure detection probes */
+
+ hrtime_t pii_fd_hrtime; /* hrtime_t before which we should */
+ /* not send probes out this pii */
+
+ uint64_t pii_flags; /* Phyint flags from kernel */
+
+ struct probe_stats {
+ struct target *pr_target; /* Probe Target */
+ uint_t pr_time_sent; /* Time probe was sent */
+ uint_t pr_status; /* probe status as below */
+#define PR_UNUSED 0 /* Probe slot unused */
+#define PR_UNACKED 1 /* Probe is unacknowledged */
+#define PR_ACKED 2 /* Probe has been acknowledged */
+#define PR_LOST 3 /* Probe is declared lost */
+ union {
+ uint_t tl; /* time probe is declared lost */
+ uint_t ta; /* time probe is acked */
+ } prt;
+#define pr_time_lost prt.tl
+#define pr_time_acked prt.ta
+ } pii_probes[PROBE_STATS_COUNT];
+
+ uint_t
+ pii_in_use : 1, /* To detect removed phyints */
+ pii_basetime_inited : 1, /* probe time initialized */
+ pii_targets_are_routers : 1; /* routers or hosts ? */
+
+ uint_t pii_probe_next; /* next index to use in pii_probes[] */
+ struct target *pii_target_next; /* next target for probing */
+ struct target *pii_rtt_target_next;
+ /* next target for rtt probes */
+
+ int pii_ntargets; /* Number of active targets */
+ struct stats { /* Cumulative statistics */
+ uint64_t lost; /* Number of probes lost */
+ uint64_t acked; /* Number of probes acked */
+ uint64_t sent; /* Number of probes sent */
+ uint64_t unknown; /* Number of ambiguous */
+ /* probe acks */
+ } pii_cum_stats;
+};
+
+#define pii_name pii_phyint->pi_name
+#define pii_ifindex pii_phyint->pi_ifindex
+#define pii_state pii_phyint->pi_state
+#define pii_icmpid pii_phyint->pi_icmpid
+
+#define PR_STATUS_VALID(status) ((status) <= PR_LOST)
+
+
+/*
+ * A doubly linked list of prefixes or logicals, hanging off the
+ * phyint instance.
+ */
+struct logint {
+ struct logint *li_next; /* Next logint of this phyint inst. */
+ struct logint *li_prev; /* Prev logint of this phyint inst. */
+ struct phyint_instance *li_phyint_inst;
+ /* Back pointer to phyint inst. */
+
+ char li_name[LIFNAMSIZ + 1]; /* name Eg. hme0:1 */
+ struct in6_addr li_addr; /* IP address */
+ struct in6_addr li_dstaddr; /* Dst IP address for pointopoint */
+ struct in6_addr li_subnet; /* prefix / subnet */
+ uint_t li_subnet_len; /* prefix / subnet length */
+ uint64_t li_flags; /* IFF_* flags */
+ uint_t li_oifindex; /* original ifindex (SIOCGLIFOINDEX) */
+ uint_t
+ li_in_use : 1, /* flag to detect deleted logints */
+ li_dupaddr : 1, /* This test address is not unique */
+ li_dupaddrmsg_printed : 1;
+ /* Error has been logged to console */
+};
+
+
+/*
+ * Doubly-linked list of probe targets on a phyint instance. Probe targets are
+ * usually onlink routers. If no onlink routers can be found, onlink hosts
+ * are used.
+ */
+struct target {
+ struct target *tg_next; /* Next target for this phyint inst. */
+ struct target *tg_prev; /* Prev target for this phyint inst. */
+ struct phyint_instance *tg_phyint_inst;
+ /* Back pointer to phyint instance */
+
+ struct in6_addr tg_address; /* Target IP address */
+ int tg_status; /* Status of the target below */
+#define TG_ACTIVE 1 /* active probe target */
+#define TG_UNUSED 2 /* target not in use now */
+#define TG_SLOW 3 /* rtt is high - Not in use now */
+#define TG_DEAD 4 /* Target is not responding */
+
+ hrtime_t tg_latime; /* Target's last active time */
+ int tg_rtt_sa; /* Scaled round trip time(RTT) avg. */
+ int tg_rtt_sd; /* Scaled RTT deviation */
+ int tg_crtt; /* Conservative RTT = A + 4D */
+ uint32_t
+ tg_in_use : 1; /* In use flag */
+ int tg_deferred[MAXDEFERREDRTT + 1];
+ /* Deferred rtt data points */
+ int tg_num_deferred;
+ /* Number of deferred rtt data points */
+};
+
+#define TG_STATUS_VALID(status) \
+ (((status) >= TG_ACTIVE) && ((status) <= TG_DEAD))
+
+/*
+ * Statistics about consecutive probe failures are passed around between
+ * functions in this structure.
+ */
+struct probe_fail_count
+{
+ uint_t pf_tff; /* Earliest time of failure in a series */
+ int pf_nfail; /* Number of consecutive probe failures */
+ int pf_nfail_tg; /* Number of consecutive probe fails for */
+ /* some given target 'tg' */
+};
+
+/*
+ * Statistics about consecutive probe successes is passed around between
+ * functions in this structure.
+ */
+struct probe_success_count
+{
+ uint_t ps_tls; /* Most recent time of probe success */
+ boolean_t ps_tls_valid; /* is ps_tls valid */
+ int ps_nsucc; /* Number of consecutive probe successes */
+ /* starting from the most recent */
+ int ps_nsucc_tg; /* Number of consecutive probe successes */
+ /* for some given target 'tg' */
+};
+
+/*
+ * Statistics about missed probes that were never sent.
+ * Happens due to scheduling delay.
+ */
+
+struct probes_missed
+{
+ uint_t pm_nprobes; /* Cumulative number of missed probes */
+ uint_t pm_ntimes; /* Total number of occassions */
+};
+
+/*
+ * Globals
+ */
+extern struct phyint *phyints; /* List of all phyints */
+extern struct phyint_group *phyint_groups; /* List of all phyint groups */
+extern struct phyint_group *phyint_anongroup; /* Pointer to the anon group */
+extern struct phyint_instance *phyint_instances;
+ /* List of all phyint instances */
+extern struct probes_missed probes_missed;
+ /* statistics about missed probes */
+
+/*
+ * Function prototypes
+ */
+extern int phyint_init(void);
+extern struct phyint *phyint_lookup(const char *name);
+extern struct phyint_instance *phyint_inst_lookup(int af, char *name);
+extern struct phyint_instance *phyint_inst_init_from_k(int af, char *name);
+extern struct phyint_instance *phyint_inst_other(struct phyint_instance *pii);
+extern int phyint_inst_update_from_k(struct phyint_instance *pii);
+extern void phyint_inst_delete(struct phyint_instance *pii);
+extern uint_t phyint_inst_timer(struct phyint_instance *pii);
+extern boolean_t phyint_inst_sockinit(struct phyint_instance *pii);
+
+extern void phyint_newtype(struct phyint *pi);
+extern void phyint_chstate(struct phyint *pi, enum pi_state state);
+extern void phyint_group_chstate(struct phyint_group *pg, enum pg_state state);
+extern void phyint_check_for_repair(struct phyint *pi);
+
+extern void logint_init_from_k(struct phyint_instance *pii, char *li_name);
+extern void logint_delete(struct logint *li);
+
+extern struct target *target_lookup(struct phyint_instance *pii,
+ struct in6_addr addr);
+extern void target_create(struct phyint_instance *pii,
+ struct in6_addr addr, boolean_t is_router);
+extern void target_delete(struct target *tg);
+extern struct target *target_next(struct target *tg);
+extern void target_add(struct phyint_instance *pii, struct in6_addr addr,
+ boolean_t is_router);
+
+extern void in_data(struct phyint_instance *pii);
+extern void in6_data(struct phyint_instance *pii);
+
+extern int try_failover(struct phyint *pi, int failover_type);
+extern int try_failback(struct phyint *pi, boolean_t check_only);
+extern int do_failback(struct phyint *pi, boolean_t check_only);
+extern boolean_t change_lif_flags(struct phyint *pi, uint64_t flags,
+ boolean_t setfl);
+
+extern void logperror_pii(struct phyint_instance *pii, char *str);
+extern void logperror_li(struct logint *li, char *str);
+extern char *pr_addr(int af, struct in6_addr addr, char *abuf, int len);
+extern void phyint_inst_print_all(void);
+
+extern int logint_upcount(struct phyint *pi);
+extern void restore_phyint(struct phyint *pi);
+extern void reset_crtt_all(struct phyint *pi);
+extern int failure_state(struct phyint_instance *pii);
+extern void process_link_state_changes(void);
+extern void clear_pii_probe_stats(struct phyint_instance *pii);
+extern void start_timer(struct phyint_instance *pii);
+
+extern boolean_t own_address(int af, struct in6_addr addr);
+
+extern void close_probe_socket(struct phyint_instance *pii, boolean_t flag);
+
+extern unsigned int getifinfo(const char *, ipmp_ifinfo_t **);
+extern unsigned int getgroupinfo(const char *, ipmp_groupinfo_t **);
+extern unsigned int getgrouplist(ipmp_grouplist_t **);
+extern unsigned int getsnap(ipmp_snap_t **);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MPD_TABLES_H */
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/Makefile b/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/Makefile
new file mode 100644
index 0000000000..b760270924
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/Makefile
@@ -0,0 +1,70 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#pragma ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# cmd/cmd-inet/usr.lib/in.ndpd/Makefile
+#
+
+PROG= in.ndpd
+OBJS= config.o main.o ndp.o tables.o trace.o dupl_addr.o
+SRCS= $(OBJS:%.o=%.c)
+
+include ../../../Makefile.cmd
+
+# in.ndpd uses the ancillary data feature which is available only through
+# UNIX 98 standards version of Socket interface. This interface is supposed to
+# be accessed by -lxnet. In addition -lsocket and -lnsl are used to
+# capture new not-yet-standard interfaces. Someday -lxnet alone should be enough
+# when IPv6 inspired new interfaces are part of standards.
+LDLIBS += -lxnet -lsocket -lnsl
+
+# these #defines are required to use UNIX 98 interfaces
+_D_UNIX98_EXTN= -D_XOPEN_SOURCE=500 -D__EXTENSIONS__
+
+$(OBJS) := CPPFLAGS += $(_D_UNIX98_EXTN)
+
+LINTFLAGS += $(_D_UNIX98_EXTN)
+
+.KEEP_STATE:
+
+.PARALLEL: $(OBJS)
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+ $(LINK.c) $(OBJS) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+include ../Makefile.lib
+
+install: all $(ROOTLIBINETPROG)
+
+clean:
+ $(RM) $(OBJS)
+
+lint: lint_SRCS
+
+include ../../../Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/config.c b/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/config.c
new file mode 100644
index 0000000000..314a083c47
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/config.c
@@ -0,0 +1,982 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "defs.h"
+#include "tables.h"
+
+/*
+ * Parse the config file which consists of entries of the form:
+ * ifdefault [<variable> <value>]*
+ * prefixdefault [<variable> <value>]*
+ * if <ifname> [<variable> <value>]*
+ * prefix <prefix>/<length> <ifname> [<variable> <value>]*
+ *
+ * All "ifdefault" and "prefixdefault" entries must preceed any
+ * "if" and "prefix" entries.
+ *
+ * Values (such as expiry dates) which contain white space
+ * can be quoted with single or double quotes.
+ */
+
+/* maximum length of messages we send to syslog */
+#define NDPD_LOGMSGSIZE 1024
+typedef boolean_t (*pfb_t)(char *, uint_t *);
+
+struct configinfo {
+ char *ci_name;
+ uint_t ci_min; /* 0: no min check */
+ uint_t ci_max; /* ~0U: no max check */
+ uint_t ci_default;
+ uint_t ci_index; /* Into result array */
+ pfb_t ci_parsefunc; /* Parse function returns -1 on failure */
+};
+
+enum config_type { CONFIG_IF, CONFIG_PREFIX};
+typedef enum config_type config_type_t;
+
+static void set_protocol_defaults(void);
+static void print_defaults(void);
+static void parse_var_value(config_type_t, struct configinfo *, char *, char *,
+ struct confvar *);
+static void parse_default(config_type_t, struct configinfo *, char **, int,
+ struct confvar *);
+static void parse_if(struct configinfo *, char **, int);
+static void parse_prefix(struct configinfo *, char **, int);
+static boolean_t parse_onoff(char *, uint_t *); /* boolean */
+static boolean_t parse_int(char *, uint_t *); /* integer */
+static boolean_t parse_ms(char *, uint_t *); /* milliseconds */
+static boolean_t parse_s(char *, uint_t *); /* seconds */
+static boolean_t parse_date(char *, uint_t *); /* date format */
+static void conferr(char *fmt, ...);
+static FILE *open_conffile(char *filename);
+static int parse_line(char *line, char *argvec[], int argcount);
+static int readline(FILE *fp, char *line, int length);
+static int parse_addrprefix(char *strin, struct in6_addr *in6);
+
+/*
+ * Per interface configuration variables.
+ * Min, max, and default values are from RFC 2461.
+ */
+static struct configinfo iflist[] = {
+ /* Name, Min, Max, Default, Index */
+ { "DupAddrDetectTransmits", 0, 100, 1, I_DupAddrDetectTransmits,
+ parse_int },
+ { "AdvSendAdvertisements", 0, 1, 0, I_AdvSendAdvertisements,
+ parse_onoff },
+ { "MaxRtrAdvInterval", 4, 1800, 600, I_MaxRtrAdvInterval, parse_s },
+ { "MinRtrAdvInterval", 3, 1350, 200, I_MinRtrAdvInterval, parse_s },
+ /*
+ * No greater than .75 * MaxRtrAdvInterval.
+ * Default: 0.33 * MaxRtrAdvInterval
+ */
+ { "AdvManagedFlag", 0, 1, 0, I_AdvManagedFlag, parse_onoff },
+ { "AdvOtherConfigFlag", 0, 1, 0, I_AdvOtherConfigFlag, parse_onoff },
+ { "AdvLinkMTU", IPV6_MIN_MTU, 65535, 0, I_AdvLinkMTU, parse_int },
+ { "AdvReachableTime", 0, 3600000, 0, I_AdvReachableTime, parse_ms },
+ { "AdvRetransTimer", 0, ~0U, 0, I_AdvRetransTimer, parse_ms },
+ { "AdvCurHopLimit", 0, 255, 0, I_AdvCurHopLimit, parse_int },
+ { "AdvDefaultLifetime", 0, 9000, 1800, I_AdvDefaultLifetime, parse_s },
+ /*
+ * MUST be either zero or between MaxRtrAdvInterval and 9000 seconds.
+ * Default: 3 * MaxRtrAdvInterval
+ */
+ { "StatelessAddrConf", 0, 1, 1, I_StatelessAddrConf, parse_onoff },
+ /*
+ * Tmp* variables from RFC 3041, where defaults are defined.
+ */
+ { "TmpAddrsEnabled", 0, 1, 0, I_TmpAddrsEnabled, parse_onoff },
+ { "TmpValidLifetime", 0, ~0U, 604800, I_TmpValidLifetime, parse_s },
+ { "TmpPreferredLifetime", 0, ~0U, 86400, I_TmpPreferredLifetime,
+ parse_s },
+ { "TmpRegenAdvance", 0, 60, 5, I_TmpRegenAdvance, parse_s },
+ { "TmpMaxDesyncFactor", 0, 600, 600, I_TmpMaxDesyncFactor, parse_s },
+ { NULL, 0, 0, 0, 0 }
+};
+
+/*
+ * Per prefix: AdvPrefixList configuration variables.
+ * Min, max, and default values are from RFC 2461.
+ */
+static struct configinfo prefixlist[] = {
+ /* Name, Min, Max, Default, Index */
+ { "AdvValidLifetime", 0, ~0U, 2592000, I_AdvValidLifetime,
+ parse_s },
+ { "AdvOnLinkFlag", 0, 1, 1, I_AdvOnLinkFlag, parse_onoff },
+ { "AdvPreferredLifetime", 0, ~0U, 604800, I_AdvPreferredLifetime,
+ parse_s},
+ { "AdvAutonomousFlag", 0, 1, 1, I_AdvAutonomousFlag, parse_onoff },
+ { "AdvValidExpiration", 0, ~0U, 0, I_AdvValidExpiration,
+ parse_date },
+ { "AdvPreferredExpiration", 0, ~0U, 0, I_AdvPreferredExpiration,
+ parse_date},
+ { NULL, 0, 0, 0, 0 },
+};
+
+/*
+ * Data structures used to merge above protocol defaults
+ * with defaults specified in the configuration file.
+ * ifdefault is not static because new interfaces can be
+ * created outside of the configuration context.
+ */
+struct confvar ifdefaults[I_IFSIZE];
+static struct confvar prefixdefaults[I_PREFIXSIZE];
+
+static char conf_filename[MAXPATHLEN];
+static int lineno;
+
+/*
+ * Checks for violations of section 5.5.3 (c) of RFC 2462.
+ */
+static void
+check_var_consistency(struct confvar *cv, void *save, int size)
+{
+ boolean_t rollback = _B_FALSE;
+ int prefl, prefe, valid;
+
+ prefl = cv[I_AdvPreferredLifetime].cf_value;
+ prefe = cv[I_AdvPreferredExpiration].cf_value;
+ valid = cv[I_AdvValidLifetime].cf_value;
+
+ if (prefl > valid) {
+ conferr("AdvPreferredLifetime (%u) is greater than "
+ "valid lifetime (%u)\n", prefl, valid);
+ rollback = _B_TRUE;
+ }
+
+ if (prefe > valid) {
+ conferr("AdvPreferredExpiration (%u) is greater than "
+ "valid lifetime (%u)\n", prefe, valid);
+ rollback = _B_TRUE;
+ }
+
+ if (rollback) {
+ (void) memcpy(cv, save, size);
+ }
+}
+
+/*
+ * Check for invalid lifetime values for RFC3041 addresses
+ */
+static void
+check_if_var_consistency(struct confvar *cv, void *save, int size)
+{
+ boolean_t rollback = _B_FALSE;
+ int tpref, tvalid, tdesync, tregen;
+
+ tpref = cv[I_TmpPreferredLifetime].cf_value;
+ tvalid = cv[I_TmpValidLifetime].cf_value;
+ tdesync = cv[I_TmpMaxDesyncFactor].cf_value;
+ tregen = cv[I_TmpRegenAdvance].cf_value;
+
+ /*
+ * Only need to do this if tmp addrs are enabled.
+ */
+ if (cv[I_TmpAddrsEnabled].cf_value == 0)
+ return;
+
+ if (tdesync > tpref) {
+ conferr("TmpDesyncFactor (%u) is greater than "
+ "TmpPreferredLifetime (%u)\n", tdesync, tpref);
+ rollback = _B_TRUE;
+ }
+
+ if (tpref > tvalid) {
+ conferr("TmpPreferredLifetime (%u) is greater than "
+ "TmpValidLifetime (%u)\n", tpref, tvalid);
+ rollback = _B_TRUE;
+ }
+
+ if (tregen > tvalid) {
+ conferr("TmpRegenAdvance (%u) is greater than "
+ "TmpValidLifetime (%u)\n", tregen, tvalid);
+ rollback = _B_TRUE;
+ }
+
+ if (rollback) {
+ (void) memcpy(cv, save, size);
+ }
+}
+
+int
+parse_config(char *config_file, boolean_t file_required)
+{
+ FILE *fp;
+ char line[MAXLINELEN];
+ char pline[MAXLINELEN];
+ int argcount;
+ char *argvec[MAXARGSPERLINE];
+ int defaultdone = 0; /* Set when first non-default command found */
+
+ if (debug & D_CONFIG)
+ logmsg(LOG_DEBUG, "parse_config()\n");
+
+ set_protocol_defaults();
+ if (debug & D_DEFAULTS)
+ print_defaults();
+
+ fp = open_conffile(config_file);
+ if (fp == NULL) {
+ if (errno == ENOENT && !file_required)
+ return (0);
+ logperror(config_file);
+ return (-1);
+ }
+ while (readline(fp, line, sizeof (line)) != 0) {
+ (void) strncpy(pline, line, sizeof (pline));
+ pline[sizeof (pline) - 1] = '\0'; /* NULL terminate */
+ argcount = parse_line(pline, argvec,
+ sizeof (argvec) / sizeof (argvec[0]));
+ if (debug & D_PARSE) {
+ int i;
+
+ logmsg(LOG_DEBUG, "scanned %d args\n", argcount);
+ for (i = 0; i < argcount; i++)
+ logmsg(LOG_DEBUG, "arg[%d]: %s\n",
+ i, argvec[i]);
+ }
+ if (argcount == 0) {
+ /* Empty line - or comment only line */
+ continue;
+ }
+ if (strcmp(argvec[0], "ifdefault") == 0) {
+ char save[sizeof (ifdefaults)];
+
+ if (defaultdone) {
+ conferr("ifdefault after non-default "
+ "command\n");
+ continue;
+ }
+ /*
+ * Save existing values in case what we read is
+ * invalid and we need to restore previous settings.
+ */
+ (void) memcpy(save, ifdefaults, sizeof (ifdefaults));
+ parse_default(CONFIG_IF, iflist, argvec+1, argcount-1,
+ ifdefaults);
+ check_if_var_consistency(ifdefaults, save,
+ sizeof (save));
+ } else if (strcmp(argvec[0], "prefixdefault") == 0) {
+ char save[sizeof (prefixdefaults)];
+
+ if (defaultdone) {
+ conferr("prefixdefault after non-default "
+ "command\n");
+ continue;
+ }
+ /*
+ * Save existing values in case what we read is
+ * invalid and we need to restore previous settings.
+ */
+ (void) memcpy(save, prefixdefaults,
+ sizeof (prefixdefaults));
+ parse_default(CONFIG_PREFIX, prefixlist, argvec+1,
+ argcount-1, prefixdefaults);
+ check_var_consistency(prefixdefaults, save,
+ sizeof (save));
+ } else if (strcmp(argvec[0], "if") == 0) {
+ defaultdone = 1;
+ parse_if(iflist, argvec+1, argcount-1);
+ } else if (strcmp(argvec[0], "prefix") == 0) {
+ defaultdone = 1;
+ parse_prefix(prefixlist, argvec+1, argcount-1);
+ } else {
+ conferr("Unknown command: %s\n", argvec[0]);
+ }
+ }
+ (void) fclose(fp);
+ if (debug & D_DEFAULTS)
+ print_defaults();
+ return (0);
+}
+
+/*
+ * Extract the defaults from the configinfo tables to initialize
+ * the ifdefaults and prefixdefaults arrays.
+ * The arrays are needed to track which defaults have been changed
+ * by the config file.
+ */
+static void
+set_protocol_defaults(void)
+{
+ struct configinfo *cip;
+
+ if (debug & D_DEFAULTS)
+ logmsg(LOG_DEBUG, "extract_protocol_defaults\n");
+ for (cip = iflist; cip->ci_name != NULL; cip++) {
+ ifdefaults[cip->ci_index].cf_value = cip->ci_default;
+ ifdefaults[cip->ci_index].cf_notdefault = _B_FALSE;
+ }
+ for (cip = prefixlist; cip->ci_name != NULL; cip++) {
+ prefixdefaults[cip->ci_index].cf_value = cip->ci_default;
+ prefixdefaults[cip->ci_index].cf_notdefault = _B_FALSE;
+ }
+}
+
+void
+print_iflist(struct confvar *confvar)
+{
+ struct configinfo *cip;
+
+ for (cip = iflist; cip->ci_name != NULL; cip++) {
+ logmsg(LOG_DEBUG, "\t%s min %u max %u def %u value %u set %d\n",
+ cip->ci_name, cip->ci_min, cip->ci_max, cip->ci_default,
+ confvar[cip->ci_index].cf_value,
+ confvar[cip->ci_index].cf_notdefault);
+ }
+}
+
+void
+print_prefixlist(struct confvar *confvar)
+{
+ struct configinfo *cip;
+
+ for (cip = prefixlist; cip->ci_name != NULL; cip++) {
+ logmsg(LOG_DEBUG, "\t%s min %u max %u def %u value %u set %d\n",
+ cip->ci_name, cip->ci_min, cip->ci_max, cip->ci_default,
+ confvar[cip->ci_index].cf_value,
+ confvar[cip->ci_index].cf_notdefault);
+ }
+}
+
+
+static void
+print_defaults(void)
+{
+ logmsg(LOG_DEBUG, "Default interface variables:\n");
+ print_iflist(ifdefaults);
+ logmsg(LOG_DEBUG, "Default prefix variables:\n");
+ print_prefixlist(prefixdefaults);
+}
+
+/*
+ * Read from fp. Handle \ at the end of the line by joining lines together.
+ * Return 0 on EOF.
+ */
+static int
+readline(FILE *fp, char *line, int length)
+{
+ int got = 0;
+
+retry:
+ errno = 0;
+ if (fgets(line, length, fp) == NULL) {
+ if (errno == EINTR)
+ goto retry;
+ if (got != 0)
+ return (1);
+ else
+ return (0);
+ }
+ lineno++;
+ got = strlen(line);
+ /* Look for trailing \. Note that fgets includes the linefeed. */
+ if (got >= 2 && line[got-2] == '\\') {
+ /* Skip \ and LF */
+ line += got - 2;
+ length -= got - 2;
+ goto retry;
+ }
+ /* Remove the trailing linefeed */
+ if (got > 0)
+ line[got-1] = '\0';
+
+ return (1);
+}
+
+/*
+ * Parse a line splitting it off at whitspace characters.
+ * Modifies the content of the string by inserting NULLs.
+ * If more arguments than fits in argvec/argcount then ignore the last.
+ * Returns argcount.
+ * Handles single quotes and double quotes.
+ */
+static int
+parse_line(char *line, char *argvec[], int argcount)
+{
+ int i = 0;
+ char *cp;
+ boolean_t insingle_quote = _B_FALSE;
+ boolean_t indouble_quote = _B_FALSE;
+
+ /* Truncate at the beginning of a comment */
+ cp = strchr(line, '#');
+ if (cp != NULL)
+ *cp = '\0';
+
+ for (;;) {
+ /* Skip any whitespace */
+ while (isspace(*line) && *line != '\0')
+ line++;
+
+ if (*line == '\'') {
+ line++;
+ if (*line == '\0')
+ return (i);
+ insingle_quote = _B_TRUE;
+ } else if (*line == '"') {
+ line++;
+ if (*line == '\0')
+ return (i);
+ indouble_quote = _B_TRUE;
+ }
+ argvec[i] = line;
+ if (*line == '\0')
+ return (i);
+ i++;
+ /* Skip until next whitespace or end of quoted text */
+ if (insingle_quote) {
+ while (*line != '\'' && *line != '\0')
+ line++;
+ if (*line == '\'') {
+ *line = ' ';
+ } else {
+ /* Handle missing quote at end */
+ i--;
+ conferr("Missing end quote - ignoring <%s>\n",
+ argvec[i]);
+ return (i);
+ }
+ insingle_quote = _B_FALSE;
+ } else if (indouble_quote) {
+ while (*line != '"' && *line != '\0')
+ line++;
+ if (*line == '"') {
+ *line = ' ';
+ } else {
+ /* Handle missing quote at end */
+ i--;
+ conferr("Missing end quote - ignoring <%s>\n",
+ argvec[i]);
+ return (i);
+ }
+ indouble_quote = _B_FALSE;
+ } else {
+ while (!isspace(*line) && *line != '\0')
+ line++;
+ }
+ if (*line != '\0') {
+ /* Break off argument */
+ *line++ = '\0';
+ }
+ if (i > argcount)
+ return (argcount);
+ }
+ /* NOTREACHED */
+}
+
+static void
+parse_var_value(config_type_t type, struct configinfo *list, char *varstr,
+ char *valstr, struct confvar *confvar)
+{
+ struct configinfo *cip;
+ uint_t val;
+
+ if (debug & D_CONFIG) {
+ logmsg(LOG_DEBUG, "parse_var_value(%d, %s, %s)\n",
+ (int)type, varstr, valstr);
+ }
+
+ for (cip = list; cip->ci_name != NULL; cip++) {
+ if (strcasecmp(cip->ci_name, varstr) == 0)
+ break;
+ }
+ if (cip->ci_name == NULL) {
+ conferr("Unknown variable: <%s>\n", varstr);
+ return;
+ }
+ if (!(*cip->ci_parsefunc)(valstr, &val)) {
+ conferr("Bad value: <%s>\n", valstr);
+ return;
+ }
+ if (cip->ci_min != 0 && val < cip->ci_min) {
+ conferr("Value %s is below minimum %u for %s\n",
+ valstr, cip->ci_min, varstr);
+ return;
+ }
+ if (cip->ci_max != ~0U && val > cip->ci_max) {
+ conferr("Value %s is above maximum %u for %s\n",
+ valstr, cip->ci_max, varstr);
+ return;
+ }
+ /* Check against dynamic/relative limits */
+ if (type == CONFIG_IF) {
+ if (cip->ci_index == I_MinRtrAdvInterval &&
+ confvar[I_MaxRtrAdvInterval].cf_notdefault &&
+ val > confvar[I_MaxRtrAdvInterval].cf_value * 0.75) {
+ conferr("MinRtrAdvInterval exceeds .75 * "
+ "MaxRtrAdvInterval (%u)\n",
+ confvar[I_MaxRtrAdvInterval].cf_value);
+ return;
+ }
+ if (cip->ci_index == I_MaxRtrAdvInterval &&
+ confvar[I_MinRtrAdvInterval].cf_notdefault &&
+ confvar[I_MinRtrAdvInterval].cf_value > val * 0.75) {
+ conferr("MinRtrAdvInterval (%u) exceeds .75 * "
+ "MaxRtrAdvInterval\n",
+ confvar[I_MinRtrAdvInterval].cf_value);
+ return;
+ }
+ if (cip->ci_index == I_AdvDefaultLifetime &&
+ confvar[I_MaxRtrAdvInterval].cf_notdefault &&
+ val != 0 &&
+ val < confvar[I_MaxRtrAdvInterval].cf_value) {
+ conferr("AdvDefaultLifetime is not between "
+ "MaxRtrAdrInterval (%u) and 9000 seconds\n",
+ confvar[I_MaxRtrAdvInterval].cf_value);
+ return;
+ }
+ if (cip->ci_index == I_MaxRtrAdvInterval &&
+ confvar[I_AdvDefaultLifetime].cf_notdefault &&
+ confvar[I_AdvDefaultLifetime].cf_value < val) {
+ conferr("AdvDefaultLifetime (%u) is not between "
+ "MaxRtrAdrInterval and 9000 seconds\n",
+ confvar[I_AdvDefaultLifetime].cf_value);
+ return;
+ }
+ }
+ confvar[cip->ci_index].cf_value = val;
+ confvar[cip->ci_index].cf_notdefault = _B_TRUE;
+
+ /* Derive dynamic/relative variables based on this one */
+ if (type == CONFIG_IF) {
+ if (cip->ci_index == I_MaxRtrAdvInterval &&
+ !confvar[I_MinRtrAdvInterval].cf_notdefault)
+ confvar[I_MinRtrAdvInterval].cf_value = val / 3;
+ if (cip->ci_index == I_MaxRtrAdvInterval &&
+ !confvar[I_AdvDefaultLifetime].cf_notdefault)
+ confvar[I_AdvDefaultLifetime].cf_value = 3 * val;
+ }
+}
+
+/*
+ * Split up the line into <variable> <value> pairs
+ */
+static void
+parse_default(config_type_t type, struct configinfo *list,
+ char *argvec[], int argcount, struct confvar *defaults)
+{
+ if (debug & D_CONFIG)
+ logmsg(LOG_DEBUG, "parse_default: argc %d\n", argcount);
+ while (argcount >= 2) {
+ parse_var_value(type, list, argvec[0], argvec[1], defaults);
+
+ argcount -= 2;
+ argvec += 2;
+ }
+ if (argcount != 0)
+ conferr("Trailing text <%s> ignored\n", argvec[0]);
+}
+
+/*
+ * Returns true if ok; otherwise false.
+ */
+static void
+parse_if(struct configinfo *list, char *argvec[], int argcount)
+{
+ char *ifname;
+ struct phyint *pi;
+ char save[sizeof (pi->pi_config)];
+
+ if (debug & D_CONFIG)
+ logmsg(LOG_DEBUG, "parse_if: argc %d\n", argcount);
+
+ if (argcount < 1) {
+ conferr("Missing interface name\n");
+ return;
+ }
+ ifname = argvec[0];
+ argvec++;
+ argcount--;
+
+ pi = phyint_lookup(ifname);
+ if (pi == NULL) {
+ /*
+ * Create the physical interface structure.
+ * Note, phyint_create() sets the interface
+ * defaults in pi_config.
+ */
+ pi = phyint_create(ifname);
+ if (pi == NULL) {
+ conferr("Unable to use interface %s\n", ifname);
+ return;
+ }
+ }
+
+ (void) memcpy(save, pi->pi_config, sizeof (save));
+ while (argcount >= 2) {
+ parse_var_value(CONFIG_IF, list, argvec[0], argvec[1],
+ pi->pi_config);
+
+ argcount -= 2;
+ argvec += 2;
+ }
+ if (argcount != 0)
+ logmsg(LOG_ERR, "Trailing text <%s> ignored\n", argvec[0]);
+ check_if_var_consistency(pi->pi_config, save, sizeof (save));
+}
+
+static void
+parse_prefix(struct configinfo *list, char *argvec[], int argcount)
+{
+ char *ifname, *prefix;
+ struct phyint *pi;
+ struct adv_prefix *adv_pr;
+ struct in6_addr in6;
+ int prefixlen;
+ char save[sizeof (adv_pr->adv_pr_config)];
+
+ if (debug & D_CONFIG)
+ logmsg(LOG_DEBUG, "parse_prefix: argc %d\n", argcount);
+
+ if (argcount < 2) {
+ conferr("Missing prefix and/or interface name\n");
+ return;
+ }
+ prefix = argvec[0];
+ ifname = argvec[1];
+ argvec += 2;
+ argcount -= 2;
+
+ prefixlen = parse_addrprefix(prefix, &in6);
+ if (prefixlen == -1) {
+ conferr("Bad prefix %s\n", prefix);
+ return;
+ }
+
+ pi = phyint_lookup(ifname);
+ if (pi == NULL) {
+ /*
+ * Create the physical interface structure.
+ * Note, phyint_create() sets the interface
+ * defaults in pi_config.
+ */
+ pi = phyint_create(ifname);
+ if (pi == NULL) {
+ conferr("Unable to use interface %s\n", ifname);
+ return;
+ }
+ }
+ adv_pr = adv_prefix_lookup(pi, in6, prefixlen);
+ if (adv_pr == NULL) {
+ int i;
+
+ adv_pr = adv_prefix_create(pi, in6, prefixlen);
+ if (adv_pr == NULL) {
+ conferr("Unable to create prefix %s\n", prefix);
+ return;
+ }
+ /*
+ * Copy the defaults from the default array.
+ */
+ for (i = 0; i < I_PREFIXSIZE; i++) {
+ adv_pr->adv_pr_config[i].cf_value =
+ prefixdefaults[i].cf_value;
+ adv_pr->adv_pr_config[i].cf_notdefault =
+ prefixdefaults[i].cf_notdefault;
+ }
+ }
+
+ (void) memcpy(save, adv_pr->adv_pr_config, sizeof (save));
+ while (argcount >= 2) {
+ parse_var_value(CONFIG_PREFIX, list, argvec[0], argvec[1],
+ adv_pr->adv_pr_config);
+
+ argcount -= 2;
+ argvec += 2;
+ }
+ check_var_consistency(adv_pr->adv_pr_config, save, sizeof (save));
+ if (argcount != 0)
+ logmsg(LOG_ERR, "Trailing text <%s> ignored\n", argvec[0]);
+}
+
+/*
+ * Returns true if ok (and *resp updated) and false if failed.
+ */
+static boolean_t
+parse_onoff(char *str, uint_t *resp)
+{
+ if (strcasecmp(str, "on") == 0) {
+ *resp = 1;
+ return (_B_TRUE);
+ }
+ if (strcasecmp(str, "off") == 0) {
+ *resp = 0;
+ return (_B_TRUE);
+ }
+ if (strcasecmp(str, "true") == 0) {
+ *resp = 1;
+ return (_B_TRUE);
+ }
+ if (strcasecmp(str, "false") == 0) {
+ *resp = 0;
+ return (_B_TRUE);
+ }
+ if (parse_int(str, resp)) {
+ if (*resp == 0 || *resp == 1)
+ return (_B_TRUE);
+ }
+ return (_B_FALSE);
+}
+
+/*
+ * Returns true if ok (and *resp updated) and false if failed.
+ */
+static boolean_t
+parse_int(char *str, uint_t *resp)
+{
+ char *end;
+ int res;
+
+ res = strtoul(str, &end, 0);
+ if (end == str)
+ return (_B_FALSE);
+ *resp = res;
+ return (_B_TRUE);
+}
+
+/*
+ * Parse something with a unit of millseconds.
+ * Regognizes the suffixes "ms", "s", "m", "h", and "d".
+ *
+ * Returns true if ok (and *resp updated) and false if failed.
+ */
+static boolean_t
+parse_ms(char *str, uint_t *resp)
+{
+ /* Look at the last and next to last character */
+ char *cp, *last, *nlast;
+ char str2[BUFSIZ]; /* For local modification */
+ int multiplier = 1;
+
+ (void) strncpy(str2, str, sizeof (str2));
+ str2[sizeof (str2) - 1] = '\0';
+
+ last = str2;
+ nlast = NULL;
+ for (cp = str2; *cp != '\0'; cp++) {
+ nlast = last;
+ last = cp;
+ }
+ if (debug & D_PARSE) {
+ logmsg(LOG_DEBUG, "parse_ms: last <%c> nlast <%c>\n",
+ (last != NULL ? *last : ' '),
+ (nlast != NULL ? *nlast : ' '));
+ }
+ switch (*last) {
+ case 'd':
+ multiplier *= 24;
+ /* FALLTHRU */
+ case 'h':
+ multiplier *= 60;
+ /* FALLTHRU */
+ case 'm':
+ multiplier *= 60;
+ *last = '\0';
+ multiplier *= 1000; /* Convert to milliseconds */
+ break;
+ case 's':
+ /* Could be "ms" or "s" */
+ if (nlast != NULL && *nlast == 'm') {
+ /* "ms" */
+ *nlast = '\0';
+ } else {
+ *last = '\0';
+ multiplier *= 1000; /* Convert to milliseconds */
+ }
+ break;
+ }
+
+ if (!parse_int(str2, resp))
+ return (_B_FALSE);
+
+ *resp *= multiplier;
+ return (_B_TRUE);
+}
+
+/*
+ * Parse something with a unit of seconds.
+ * Regognizes the suffixes "s", "m", "h", and "d".
+ *
+ * Returns true if ok (and *resp updated) and false if failed.
+ */
+static boolean_t
+parse_s(char *str, uint_t *resp)
+{
+ /* Look at the last character */
+ char *cp, *last;
+ char str2[BUFSIZ]; /* For local modification */
+ int multiplier = 1;
+
+ (void) strncpy(str2, str, sizeof (str2));
+ str2[sizeof (str2) - 1] = '\0';
+
+ last = str2;
+ for (cp = str2; *cp != '\0'; cp++) {
+ last = cp;
+ }
+ if (debug & D_PARSE) {
+ logmsg(LOG_DEBUG, "parse_s: last <%c>\n",
+ (last != NULL ? *last : ' '));
+ }
+ switch (*last) {
+ case 'd':
+ multiplier *= 24;
+ /* FALLTHRU */
+ case 'h':
+ multiplier *= 60;
+ /* FALLTHRU */
+ case 'm':
+ multiplier *= 60;
+ /* FALLTHRU */
+ case 's':
+ *last = '\0';
+ break;
+ }
+ if (!parse_int(str2, resp))
+ return (_B_FALSE);
+
+ *resp *= multiplier;
+ return (_B_TRUE);
+}
+
+/*
+ * Return prefixlen (0 to 128) if ok; -1 if failed.
+ */
+static int
+parse_addrprefix(char *strin, struct in6_addr *in6)
+{
+ char str[BUFSIZ]; /* Local copy for modification */
+ int prefixlen;
+ char *cp;
+ char *end;
+
+ (void) strncpy(str, strin, sizeof (str));
+ str[sizeof (str) - 1] = '\0';
+
+ cp = strchr(str, '/');
+ if (cp == NULL)
+ return (-1);
+ *cp = '\0';
+ cp++;
+
+ prefixlen = strtol(cp, &end, 10);
+ if (cp == end)
+ return (-1);
+
+ if (prefixlen < 0 || prefixlen > IPV6_ABITS)
+ return (-1);
+
+ if (inet_pton(AF_INET6, str, in6) != 1)
+ return (-1);
+
+ return (prefixlen);
+}
+
+/*
+ * Parse an absolute date using a datemsk config file.
+ * Return the difference (measured in seconds) between that date/time and
+ * the current date/time.
+ * If the date has passed return zero.
+ *
+ * Returns true if ok (and *resp updated) and false if failed.
+ * XXX Due to getdate limitations can not exceed year 2038.
+ */
+static boolean_t
+parse_date(char *str, uint_t *resp)
+{
+ struct tm *tm;
+ struct timeval tvs;
+ time_t time, ntime;
+
+ if (getenv("DATEMSK") == NULL) {
+ (void) putenv("DATEMSK=/etc/inet/datemsk.ndpd");
+ }
+
+ if (gettimeofday(&tvs, NULL) < 0) {
+ logperror("gettimeofday");
+ return (_B_FALSE);
+ }
+ time = tvs.tv_sec;
+ tm = getdate(str);
+ if (tm == NULL) {
+ logmsg(LOG_ERR, "Bad date <%s> (error %d)\n",
+ str, getdate_err);
+ return (_B_FALSE);
+ }
+
+ ntime = mktime(tm);
+
+ if (debug & D_PARSE) {
+ char buf[BUFSIZ];
+
+ (void) strftime(buf, sizeof (buf), "%Y-%m-%d %R %Z", tm);
+ logmsg(LOG_DEBUG, "parse_date: <%s>, delta %ld seconds\n",
+ buf, ntime - time);
+ }
+ if (ntime < time) {
+ conferr("Date in the past <%s>\n", str);
+ *resp = 0;
+ return (_B_TRUE);
+ }
+ *resp = (ntime - time);
+ return (_B_TRUE);
+}
+
+/* PRINTFLIKE1 */
+static void
+conferr(char *fmt, ...)
+{
+ char msg[NDPD_LOGMSGSIZE];
+ size_t slen;
+
+ va_list ap;
+ va_start(ap, fmt);
+
+ (void) snprintf(msg, NDPD_LOGMSGSIZE, "%s line %d: ",
+ conf_filename, lineno);
+ slen = strlen(msg);
+ (void) vsnprintf(msg + slen, NDPD_LOGMSGSIZE - slen, fmt, ap);
+
+ logmsg(LOG_ERR, "%s", msg);
+
+ va_end(ap);
+}
+
+static FILE *
+open_conffile(char *filename)
+{
+ if (strlcpy(conf_filename, filename, MAXPATHLEN) >= MAXPATHLEN) {
+ logmsg(LOG_ERR, "config file pathname is too long\n");
+ return (NULL);
+ }
+
+ lineno = 0;
+
+ return (fopen(filename, "r"));
+
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/defs.h b/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/defs.h
new file mode 100644
index 0000000000..b3cd02cece
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/defs.h
@@ -0,0 +1,148 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _NDPD_DEFS_H
+#define _NDPD_DEFS_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <poll.h>
+#include <unistd.h>
+#include <time.h>
+#include <stdarg.h>
+#include <syslog.h>
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <sys/ioctl.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <sys/stropts.h>
+
+#include <string.h>
+#include <ctype.h>
+
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/if_ether.h>
+#include <netinet/ip6.h>
+#include <netinet/icmp6.h>
+#include <net/route.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define PATH_NDPD_CONF "/etc/inet/ndpd.conf"
+#define PATH_PID "/var/run/in.ndpd.pid"
+
+extern int debug, no_loopback;
+
+extern struct in6_addr all_nodes_mcast;
+extern struct in6_addr all_routers_mcast;
+
+extern int rtsock;
+extern struct rt_msghdr *rt_msg;
+extern struct sockaddr_in6 *rta_gateway;
+extern struct sockaddr_dl *rta_ifp;
+
+/* Debug flags */
+#define D_ALL 0xffff
+#define D_DEFAULTS 0x0001 /* Default values in config file */
+#define D_CONFIG 0x0002 /* Config file */
+#define D_PHYINT 0x0004 /* phyint table */
+#define D_PREFIX 0x0008 /* prefix table */
+#define D_ROUTER 0x0010 /* router table */
+#define D_STATE 0x0020 /* RS/RA state machine */
+#define D_IFSCAN 0x0040 /* Scan of kernel interfaces */
+#define D_TIMER 0x0080 /* Timer mechanism */
+#define D_PARSE 0x0100 /* config file parser */
+#define D_PKTIN 0x0200 /* Received packet */
+#define D_PKTBAD 0x0400 /* Malformed packet */
+#define D_PKTOUT 0x0800 /* Sent packet */
+#define D_TMP 0x1000 /* RFC3041 mechanism */
+#define D_DAD 0x2000 /* Duplciate Address Detection */
+
+#define IF_SEPARATOR ':'
+#define IPV6_MAX_HOPS 255
+#define IPV6_MIN_MTU (1024+256)
+#define IPV6_ABITS 128
+#define TMP_TOKEN_BITS 64
+#define TMP_TOKEN_BYTES (TMP_TOKEN_BITS / 8)
+#define MAX_DAD_FAILURES 5
+
+/* Return a random number from a an range inclusive of the endpoints */
+#define GET_RANDOM(LOW, HIGH) (random() % ((HIGH) - (LOW) + 1) + (LOW))
+
+#define TIMER_INFINITY 0xFFFFFFFFU /* Never time out */
+#define PREFIX_INFINITY 0XFFFFFFFFU /* A "forever" prefix lifetime */
+
+/*
+ * Used by 2 hour rule for stateless addrconf
+ */
+#define MIN_VALID_LIFETIME (2*60*60) /* In seconds */
+
+/*
+ * Control how often pi_ReachableTime gets re-randomized
+ */
+#define MIN_REACH_RANDOM_INTERVAL (60*1000) /* 1 minute in ms */
+#define MAX_REACH_RANDOM_INTERVAL (60*60*1000) /* 1 hour in ms */
+
+/*
+ * Parsing constants
+ */
+#define MAXLINELEN 4096
+#define MAXARGSPERLINE 128
+
+void timer_schedule(uint_t delay);
+extern void logmsg(int level, char *fmt, ...);
+extern void logperror(char *str);
+extern int parse_config(char *config_file, boolean_t file_required);
+
+extern int poll_add(int fd);
+extern int poll_remove(int fd);
+
+extern char *fmt_lla(char *llabuf, int bufsize, uchar_t *lla, int llalen);
+
+extern int do_dad(char *ifname, struct sockaddr_in6 *testaddr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _NDPD_DEFS_H */
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/dupl_addr.c b/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/dupl_addr.c
new file mode 100644
index 0000000000..937546d1d7
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/dupl_addr.c
@@ -0,0 +1,849 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Perform IPv6 duplicate address detection for a given interface
+ * and IPv6 address.
+ *
+ * This file is copied from usr/src/cmd/cmd-inet/usr.sbin/ifconfig.
+ * Only the modifications necessary to integrate into the message
+ * scheme of in.ndpd have been made. This is intended to be a
+ * temporary fix to allow Duplicate Address Detection to be performed
+ * by in.ndpd for temporary (rfc 3041) addresses; the long-term
+ * solution will be to use libinetcfg.
+ */
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "defs.h"
+#include <netinet/icmp6.h>
+#include <netinet/in_systm.h> /* For IP_MAXPACKET */
+#include <netinet/ip.h> /* For IP_MAXPACKET */
+
+int DupAddrDetectTransmits = 1;
+int RetransTimer = ND_RETRANS_TIMER; /* Milliseconds. */
+
+#define IPV6_MAX_HOPS 255
+
+extern struct in6_addr all_nodes_mcast;
+
+static void in6_solmulti_addr(struct in6_addr *addr,
+ struct in6_addr *multi);
+static int run_dad(int s, char *phyname, struct sockaddr_in6 *testaddr,
+ struct sockaddr_in6 *solicited_mc, int ifindex);
+static int send_dad_probe(int s, char *phyname,
+ struct sockaddr_in6 *testaddr,
+ struct sockaddr_in6 *solicited_mc);
+static int recv_dad(int s, char *phyname, struct sockaddr_in6 *testaddr,
+ int ifindex);
+static boolean_t verify_opt_len(struct nd_opt_hdr *opt, int optlen,
+ struct sockaddr_in6 *from);
+static void dad_failed(char *phyname, struct sockaddr_in6 *testaddr,
+ int code);
+static void print_na(char *str, char *phyname,
+ struct nd_neighbor_advert *na, int len,
+ struct sockaddr_in6 *addr);
+static void print_ns(char *str, char *phyname,
+ struct nd_neighbor_solicit *ns, int len,
+ struct sockaddr_in6 *addr);
+static void print_opt(struct nd_opt_hdr *opt, int len);
+
+
+/*
+ * Performing duplicate address detection.
+ *
+ * Returns 0 if the address is ok, 1 if there is a duplicate,
+ * and -1 (with errno set) if there is some internal error.
+ * As a side effect this does a syslog printf identifying any
+ * duplicate.
+ * Note that the state of the interface name is unchanged.
+ */
+int
+do_dad(char *ifname, struct sockaddr_in6 *testaddr)
+{
+ int s;
+ struct lifreq lifr;
+ char *cp;
+ char phyname[LIFNAMSIZ];
+ int ifindex;
+ int64_t saved_flags;
+ int ret = -1; /* Assume error by default */
+ struct sockaddr_in6 solicited_mc;
+
+ /*
+ * Truncate name at ':'. Needed for SIOCGLIFLNKINFO
+ * Keep untruncated ifname for other use.
+ */
+ (void) strncpy(phyname, ifname, sizeof (phyname));
+ cp = strchr(phyname, ':');
+ if (cp != NULL)
+ *cp = '\0';
+
+ /*
+ * Get a socket to use to send and receive neighbor solicitations
+ * for DAD. Also used for ioctls below.
+ */
+ if ((s = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) {
+ logperror("do_dad: socket");
+ return (-1);
+ }
+
+ /*
+ * Determine interface index (for IPV6_BOUND_PIF) and
+ * save the flag values so they can be restored on return.
+ */
+ (void) strncpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
+ if (ioctl(s, SIOCGLIFINDEX, (caddr_t)&lifr) < 0) {
+ logperror("do_dad: SIOCGLIFINDEX");
+ goto done;
+ }
+ ifindex = lifr.lifr_index;
+ if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0) {
+ logperror("do_dad: SIOCGLIFFLAGS");
+ goto done;
+ }
+ saved_flags = lifr.lifr_flags;
+ if (!(saved_flags & IFF_MULTICAST)) {
+ /* Not possible to do DAD. Pretend it is ok */
+ ret = 0;
+ goto done;
+ }
+ (void) strncpy(lifr.lifr_name, phyname, sizeof (lifr.lifr_name));
+ if (ioctl(s, SIOCGLIFLNKINFO, (caddr_t)&lifr) < 0) {
+ logperror("do_dad: SIOCGLIFLNKINFO");
+ goto done;
+ }
+ if (lifr.lifr_ifinfo.lir_reachretrans != 0) {
+ RetransTimer = lifr.lifr_ifinfo.lir_reachretrans;
+ }
+
+ /*
+ * Set NOLOCAL and UP flags.
+ * This prevents the use of the interface except when the user binds
+ * to unspecified IPv6 address, and sends to a link local multicast
+ * address.
+ */
+ lifr.lifr_flags = saved_flags | IFF_NOLOCAL | IFF_UP;
+
+ (void) strncpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
+ if (ioctl(s, SIOCSLIFFLAGS, (caddr_t)&lifr) < 0) {
+ logperror("do_dad: SIOCSLIFFLAGS");
+ goto restore;
+ }
+
+ /*
+ * IPV6_BOUND_PIF prevents load spreading to happen. If we
+ * just do IPV6_BOUND_IF, the packet can go out on a different
+ * interface other than "ifindex", if interface is part of
+ * a group. In that case, we will get back the copy of NS that
+ * we sent and think it is a duplicate(Switch loops back the
+ * copy on all interfaces other than the one we sent the packet on).
+ */
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_BOUND_PIF, (char *)&ifindex,
+ sizeof (ifindex)) < 0) {
+ logperror("do_dad: IPV6_BOUND_PIF");
+ goto restore;
+ }
+
+ {
+ int hops = IPV6_MAX_HOPS;
+ int on = 1;
+ int off = 0;
+
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
+ (char *)&hops, sizeof (hops)) < 0) {
+ logperror("do_dad: IPV6_MULTICAST_HOPS");
+ goto restore;
+ }
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_UNSPEC_SRC,
+ (char *)&on, sizeof (on)) < 0) {
+ logperror("do_dad: IPV6_UNSPEC_SRC");
+ goto restore;
+ }
+
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
+ (char *)&off, sizeof (off)) < 0) {
+ logperror("do_dad: IPV6_MULTICAST_LOOP");
+ goto restore;
+ }
+
+ /* Enable receipt of ancillary data */
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVHOPLIMIT,
+ (char *)&on, sizeof (on)) < 0) {
+ logperror("do_dad: IPV6_RECVHOPLIMIT");
+ goto restore;
+ }
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO,
+ (char *)&on, sizeof (on)) < 0) {
+ logperror("do_dad: IPV6_RECVPKTINFO");
+ goto restore;
+ }
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVRTHDR,
+ (char *)&on, sizeof (on)) < 0) {
+ logperror("do_dad: IPV6_RECVRTHDR");
+ goto restore;
+ }
+ }
+
+ /*
+ * Extract the address and determine the solicited node multicast
+ * address to use.
+ */
+ (void) memset(&solicited_mc, 0, sizeof (solicited_mc));
+ solicited_mc.sin6_family = AF_INET6;
+ in6_solmulti_addr(&testaddr->sin6_addr, &solicited_mc.sin6_addr);
+
+ /* Join the solicited node multicast address and all-nodes. */
+ {
+ struct ipv6_mreq v6mcastr;
+
+ v6mcastr.ipv6mr_multiaddr = solicited_mc.sin6_addr;
+ v6mcastr.ipv6mr_interface = ifindex;
+
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_JOIN_GROUP,
+ (char *)&v6mcastr, sizeof (v6mcastr)) < 0) {
+ logperror("do_dad: IPV6_JOIN_GROUP");
+ goto restore;
+ }
+
+ v6mcastr.ipv6mr_multiaddr = all_nodes_mcast;
+ v6mcastr.ipv6mr_interface = ifindex;
+
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_JOIN_GROUP,
+ (char *)&v6mcastr, sizeof (v6mcastr)) < 0) {
+ logperror("do_dad: IPV6_JOIN_GROUP");
+ goto restore;
+ }
+ }
+
+ ret = run_dad(s, phyname, testaddr, &solicited_mc, ifindex);
+
+restore:
+ /* Restore flags */
+ (void) strncpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
+ lifr.lifr_flags = saved_flags;
+ if (ioctl(s, SIOCSLIFFLAGS, (caddr_t)&lifr) < 0) {
+ logperror("do_dad: SIOCSLIFFLAGS");
+ ret = -1;
+ goto done;
+ }
+done:
+ (void) close(s);
+ return (ret);
+}
+
+
+/*
+ * Determine the solicited node multicast address for a given address.
+ */
+static void
+in6_solmulti_addr(struct in6_addr *addr, struct in6_addr *multi)
+{
+ struct in6_addr solicited_prefix = {
+ { 0xff, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x1, 0xFF, 0x0, 0x0, 0x0 } };
+ int i;
+
+ *multi = solicited_prefix;
+ for (i = 13; i < 16; i++)
+ multi->s6_addr[i] = addr->s6_addr[i];
+}
+
+static int
+run_dad(int s, char *phyname, struct sockaddr_in6 *testaddr,
+ struct sockaddr_in6 *solicited_mc, int ifindex)
+{
+ int time_left; /* In milliseconds */
+ struct timeval starttime;
+ struct timeval curtime;
+ struct pollfd fds;
+ int i;
+ int ret;
+
+ if (debug & D_DAD)
+ logmsg(LOG_DEBUG, "run_dad(%s)\n", phyname);
+
+ /*
+ * Perform duplicate address detection sequence
+ * 1. Send a neighbor solicitation with an unspecified source
+ * address to the solicited node MC address with the testaddr
+ * being the target.
+ * 2. Wait for up to RetransTimer milliseconds for either a
+ * neighbor advertisement (sent to all-nodes) or a DAD neighbor
+ * solicitation for the testaddr.
+ * 3. Perform step 1 and 2 DupAddrDetectTransmits times.
+ */
+
+ /* XXX perform a random delay: 0 - MAX_RTR_SOLICITATION_DELAY */
+ /* XXX use poll+recv logic for the random delay */
+
+ for (i = 0; i < DupAddrDetectTransmits; i++) {
+ if (send_dad_probe(s, phyname, testaddr, solicited_mc) < 0)
+ return (-1);
+
+ /*
+ * Track time to make sure total wait is RetransTimer
+ * even though random packet will awake poll.
+ */
+ (void) gettimeofday(&starttime, NULL);
+ /* CONSTCOND */
+ while (1) {
+ (void) gettimeofday(&curtime, NULL);
+ time_left = RetransTimer -
+ (curtime.tv_sec - starttime.tv_sec) * 1000 -
+ (curtime.tv_usec - starttime.tv_usec) / 1000;
+
+ if (debug & D_DAD) {
+ logmsg(LOG_DEBUG, "run_dad: time_left %d ms\n",
+ time_left);
+ }
+ if (time_left <= 0) {
+ if (debug & D_DAD)
+ logmsg(LOG_DEBUG, "run_dad: timeout\n");
+ break;
+ }
+ fds.fd = s;
+ fds.events = POLLIN;
+
+ switch (poll(&fds, 1, time_left)) {
+ case -1:
+ logperror("run_dad: poll");
+ return (-1);
+ case 0:
+ /* Need loop will break */
+ break;
+ default:
+ /* Huh? */
+ logmsg(LOG_ERR, "poll returns > 1!\n");
+ return (-1);
+ case 1:
+ if (fds.revents & POLLIN) {
+ ret = recv_dad(s, phyname, testaddr,
+ ifindex);
+ if (ret < 0)
+ return (-1);
+ if (ret > 0) {
+ dad_failed(phyname, testaddr,
+ ret);
+ return (1);
+ }
+ }
+ break;
+ }
+ }
+ }
+ return (0);
+}
+
+/*
+ * Send a DAD NS packet. Assumes an IPV6_UNSPEC_SRC and an IPV6_BOUND_IF
+ * have been done by the caller.
+ */
+static int
+send_dad_probe(int s, char *phyname, struct sockaddr_in6 *testaddr,
+ struct sockaddr_in6 *solicited_mc)
+{
+ static uint64_t outpack[(IP_MAXPACKET + 1)/8];
+ struct nd_neighbor_solicit *ns = (struct nd_neighbor_solicit *)outpack;
+ int packetlen = 0;
+ int cc;
+
+ ns->nd_ns_type = ND_NEIGHBOR_SOLICIT;
+ ns->nd_ns_code = 0;
+ ns->nd_ns_cksum = 0;
+ ns->nd_ns_reserved = 0;
+ ns->nd_ns_target = testaddr->sin6_addr;
+ packetlen += sizeof (struct nd_neighbor_solicit);
+ cc = sendto(s, (char *)outpack, packetlen, 0,
+ (struct sockaddr *)solicited_mc, sizeof (*solicited_mc));
+ if (cc < 0 || cc != packetlen) {
+ char abuf[INET6_ADDRSTRLEN];
+
+ if (cc < 0) {
+ logperror("DAD sendto");
+ return (-1);
+ }
+ if (debug & D_DAD) {
+ (void) inet_ntop(solicited_mc->sin6_family,
+ (void *)&solicited_mc->sin6_addr, abuf,
+ sizeof (abuf));
+
+ logmsg(LOG_DEBUG, "wrote %s %d chars, ret=%d\n",
+ abuf, packetlen, cc);
+ }
+ return (-1);
+ }
+ if (debug & D_DAD)
+ print_ns("Sent NS", phyname, ns, packetlen, solicited_mc);
+
+ return (0);
+}
+
+/*
+ * Return a pointer to the specified option buffer.
+ * If not found return NULL.
+ */
+static void *
+find_ancillary(struct msghdr *msg, int cmsg_type)
+{
+ struct cmsghdr *cmsg;
+
+ for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL;
+ cmsg = CMSG_NXTHDR(msg, cmsg)) {
+ if (cmsg->cmsg_level == IPPROTO_IPV6 &&
+ cmsg->cmsg_type == cmsg_type) {
+ return (CMSG_DATA(cmsg));
+ }
+ }
+ return (NULL);
+}
+
+/*
+ * Receive an ICMP packet. If the packet signals a duplicate address for
+ * testaddr then return a positive non-zero number. Otherwise return zero.
+ * Internal errors cause a return of -1.
+ */
+static int
+recv_dad(int s, char *phyname, struct sockaddr_in6 *testaddr, int ifindex)
+{
+ struct sockaddr_in6 from;
+ struct icmp6_hdr *icmp;
+ struct nd_neighbor_solicit *ns;
+ struct nd_neighbor_advert *na;
+ static uint64_t in_packet[(IP_MAXPACKET + 1)/8];
+ static uint64_t ancillary_data[(IP_MAXPACKET + 1)/8];
+ int len;
+ char abuf[INET6_ADDRSTRLEN];
+ struct msghdr msg;
+ struct iovec iov;
+ uchar_t *opt;
+ uint_t hoplimit;
+ struct in6_addr dst;
+ int rcv_ifindex;
+
+ iov.iov_base = (char *)in_packet;
+ iov.iov_len = sizeof (in_packet);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_name = (struct sockaddr *)&from;
+ msg.msg_namelen = sizeof (from);
+ msg.msg_control = ancillary_data;
+ msg.msg_controllen = sizeof (ancillary_data);
+
+ if ((len = recvmsg(s, &msg, 0)) < 0) {
+ logperror("DAD recvmsg");
+ return (-1);
+ }
+ if (len == 0)
+ return (0);
+
+ if (debug & D_DAD) {
+ (void) inet_ntop(AF_INET6, (void *)&from.sin6_addr,
+ abuf, sizeof (abuf));
+ }
+ /* Ignore packets > 64k or control buffers that don't fit */
+ if (msg.msg_flags & (MSG_TRUNC|MSG_CTRUNC)) {
+ if (debug & D_DAD) {
+ logmsg(LOG_DEBUG, "Truncated message: msg_flags "
+ "0x%x from %s\n", msg.msg_flags, abuf);
+ }
+ return (0);
+ }
+
+ icmp = (struct icmp6_hdr *)in_packet;
+
+ if (len < ICMP6_MINLEN) {
+ if (debug & D_DAD) {
+ logmsg(LOG_DEBUG, "Too short ICMP packet: %d bytes "
+ "from %s\n", len, abuf);
+ }
+ return (0);
+ }
+
+ opt = find_ancillary(&msg, IPV6_HOPLIMIT);
+ if (opt == NULL) {
+ /* Unknown hoplimit - must drop */
+ if (debug & D_DAD) {
+ logmsg(LOG_DEBUG, "Unknown hop limit from %s\n", abuf);
+ }
+ return (0);
+ }
+ hoplimit = *(uint_t *)opt;
+ opt = find_ancillary(&msg, IPV6_PKTINFO);
+ if (opt == NULL) {
+ /* Unknown destination address - must drop */
+ if (debug & D_DAD) {
+ logmsg(LOG_DEBUG, "Unknown destination from %s\n",
+ abuf);
+ }
+ return (0);
+ }
+ dst = ((struct in6_pktinfo *)opt)->ipi6_addr;
+ rcv_ifindex = ((struct in6_pktinfo *)opt)->ipi6_ifindex;
+ opt = find_ancillary(&msg, IPV6_RTHDR);
+ if (opt != NULL) {
+ /* Can't allow routing headers in ND messages */
+ if (debug & D_DAD) {
+ logmsg(LOG_DEBUG,
+ "ND message with routing header from %s\n", abuf);
+ }
+ return (0);
+ }
+
+ switch (icmp->icmp6_type) {
+ case ND_NEIGHBOR_SOLICIT:
+ /*
+ * Assumes that the kernel has verified the AH (if present)
+ * and the ICMP checksum.
+ */
+ if (hoplimit != IPV6_MAX_HOPS) {
+ if (debug & D_DAD) {
+ logmsg(LOG_DEBUG, "NS hop limit: %d from %s\n",
+ hoplimit, abuf);
+ }
+ return (0);
+ }
+
+ if (icmp->icmp6_code != 0) {
+ if (debug & D_DAD) {
+ logmsg(LOG_DEBUG, "NS code: %d from %s\n",
+ icmp->icmp6_code, abuf);
+ }
+ return (0);
+ }
+
+ if (len < sizeof (struct nd_neighbor_solicit)) {
+ if (debug & D_DAD) {
+ logmsg(LOG_DEBUG, "NS too short: %d bytes "
+ "from %s\n", len, abuf);
+ }
+ return (0);
+ }
+ ns = (struct nd_neighbor_solicit *)icmp;
+ if (IN6_IS_ADDR_MULTICAST(&ns->nd_ns_target)) {
+ if (debug & D_DAD) {
+ char abuf2[INET6_ADDRSTRLEN];
+
+ (void) inet_ntop(AF_INET6,
+ (void *)&ns->nd_ns_target,
+ abuf2, sizeof (abuf2));
+ logmsg(LOG_DEBUG, "NS with multicast target: "
+ "%s from %s\n", abuf2, abuf);
+ }
+ return (0);
+ }
+
+ if (len > sizeof (struct nd_neighbor_solicit)) {
+ if (!verify_opt_len((struct nd_opt_hdr *)&ns[1],
+ len - sizeof (struct nd_neighbor_solicit), &from))
+ return (0);
+ }
+
+ if (debug & D_DAD)
+ print_ns("Received valid NS", phyname, ns, len, &from);
+ if (!IN6_IS_ADDR_UNSPECIFIED(&from.sin6_addr)) {
+ /* Sender is doing address resolution */
+ return (0);
+ }
+ if (rcv_ifindex != ifindex) {
+ if (debug & D_DAD) {
+ logmsg(LOG_DEBUG, "Received Neighbor "
+ "solicitation on ifindex %d, "
+ "expecting on %d\n", rcv_ifindex, ifindex);
+ }
+ return (0);
+ }
+ if (IN6_ARE_ADDR_EQUAL(&testaddr->sin6_addr,
+ &ns->nd_ns_target)) {
+ if (debug & D_DAD) {
+ logmsg(LOG_DEBUG, "NS - duplicate from %s\n",
+ abuf);
+ }
+ return (1);
+ }
+ return (0);
+
+ case ND_NEIGHBOR_ADVERT:
+ /*
+ * Assumes that the kernel has verified the AH (if present)
+ * and the ICMP checksum.
+ */
+ if (hoplimit != IPV6_MAX_HOPS) {
+ if (debug & D_DAD) {
+ logmsg(LOG_DEBUG, "NA hop limit: %d from %s\n",
+ hoplimit, abuf);
+ }
+ return (0);
+ }
+
+ if (icmp->icmp6_code != 0) {
+ if (debug & D_DAD) {
+ logmsg(LOG_DEBUG, "NA code: %d from %s\n",
+ icmp->icmp6_code, abuf);
+ }
+ return (0);
+ }
+
+ if (len < sizeof (struct nd_neighbor_advert)) {
+ if (debug & D_DAD) {
+ logmsg(LOG_DEBUG, "NA too short: %d bytes "
+ "from %s\n", len, abuf);
+ }
+ return (0);
+ }
+ na = (struct nd_neighbor_advert *)icmp;
+ if (IN6_IS_ADDR_MULTICAST(&na->nd_na_target)) {
+ if (debug & D_DAD) {
+ char abuf2[INET6_ADDRSTRLEN];
+
+ (void) inet_ntop(AF_INET6,
+ (void *)&na->nd_na_target,
+ abuf2, sizeof (abuf2));
+ logmsg(LOG_DEBUG, "NA with multicast target: "
+ "%s from %s\n", abuf2, abuf);
+ }
+ return (0);
+ }
+
+ if (IN6_IS_ADDR_MULTICAST(&dst) &&
+ (na->nd_na_flags_reserved & ND_NA_FLAG_SOLICITED)) {
+ if (debug & D_DAD) {
+ char abuf2[INET6_ADDRSTRLEN];
+
+ (void) inet_ntop(AF_INET6,
+ (void *)&na->nd_na_target,
+ abuf2, sizeof (abuf2));
+ logmsg(LOG_DEBUG, "NA solicited w/ mc target: "
+ "%s from %s\n", abuf2, abuf);
+ }
+ return (0);
+ }
+
+ if (len > sizeof (struct nd_neighbor_advert)) {
+ if (!verify_opt_len((struct nd_opt_hdr *)&na[1],
+ len - sizeof (struct nd_neighbor_advert), &from))
+ return (0);
+ }
+
+ if (debug & D_DAD)
+ print_na("Received valid NA", phyname, na, len, &from);
+
+ if (IN6_ARE_ADDR_EQUAL(&testaddr->sin6_addr,
+ &na->nd_na_target)) {
+ if (debug & D_DAD) {
+ logmsg(LOG_DEBUG, "NA - duplicate from %s\n",
+ abuf);
+ }
+ return (1);
+ }
+ return (0);
+ default:
+ return (0);
+ }
+}
+
+/*
+ * Verify that all options have a non-zero length and that
+ * the options fit within the total length of the packet (optlen).
+ */
+static boolean_t
+verify_opt_len(struct nd_opt_hdr *opt, int optlen, struct sockaddr_in6 *from)
+{
+ while (optlen > 0) {
+ if (opt->nd_opt_len == 0) {
+ if (debug & D_DAD) {
+ char abuf[INET6_ADDRSTRLEN];
+
+ (void) inet_ntop(AF_INET6,
+ (void *)&from->sin6_addr,
+ abuf, sizeof (abuf));
+
+ logmsg(LOG_DEBUG, "Zero length option type "
+ "0x%x from %s\n", opt->nd_opt_type, abuf);
+ }
+ return (_B_FALSE);
+ }
+ optlen -= 8 * opt->nd_opt_len;
+ if (optlen < 0) {
+ if (debug & D_DAD) {
+ char abuf[INET6_ADDRSTRLEN];
+
+ (void) inet_ntop(AF_INET6,
+ (void *)&from->sin6_addr,
+ abuf, sizeof (abuf));
+
+ logmsg(LOG_DEBUG, "Too large option: type "
+ "0x%x len %u from %s\n",
+ opt->nd_opt_type, opt->nd_opt_len, abuf);
+ }
+ return (_B_FALSE);
+ }
+ opt = (struct nd_opt_hdr *)((char *)opt +
+ 8 * opt->nd_opt_len);
+ }
+ return (_B_TRUE);
+}
+
+
+static void
+dad_failed(char *phyname, struct sockaddr_in6 *testaddr, int code)
+{
+ char abuf[INET6_ADDRSTRLEN];
+
+ (void) inet_ntop(testaddr->sin6_family,
+ (void *)&testaddr->sin6_addr,
+ abuf, sizeof (abuf));
+ logmsg(LOG_CRIT, "Duplicate address detected on link %s for address "
+ "%s. Code %d\n", phyname, abuf, code);
+}
+
+/* Printing functions */
+
+static void
+print_ns(char *str, char *phyname,
+ struct nd_neighbor_solicit *ns, int len, struct sockaddr_in6 *addr)
+{
+ struct nd_opt_hdr *opt;
+ char abuf[INET6_ADDRSTRLEN];
+
+ logmsg(LOG_DEBUG, "%s %s (%d bytes) on %s\n", str,
+ inet_ntop(addr->sin6_family, (void *)&addr->sin6_addr,
+ abuf, sizeof (abuf)),
+ len, phyname);
+ logmsg(LOG_DEBUG, "\ttarget %s\n",
+ inet_ntop(addr->sin6_family, (void *)&ns->nd_ns_target,
+ abuf, sizeof (abuf)));
+ len -= sizeof (*ns);
+ opt = (struct nd_opt_hdr *)&ns[1];
+ print_opt(opt, len);
+}
+
+static void
+print_na(char *str, char *phyname,
+ struct nd_neighbor_advert *na, int len, struct sockaddr_in6 *addr)
+{
+ struct nd_opt_hdr *opt;
+ char abuf[INET6_ADDRSTRLEN];
+
+ logmsg(LOG_DEBUG, "%s %s (%d bytes) on %s\n", str,
+ inet_ntop(addr->sin6_family, (void *)&addr->sin6_addr,
+ abuf, sizeof (abuf)),
+ len, phyname);
+ logmsg(LOG_DEBUG, "\ttarget %s\n",
+ inet_ntop(addr->sin6_family, (void *)&na->nd_na_target,
+ abuf, sizeof (abuf)));
+ logmsg(LOG_DEBUG, "\tRouter: %s\n",
+ (na->nd_na_flags_reserved & ND_NA_FLAG_ROUTER) ?
+ "Set" : "Not set");
+ logmsg(LOG_DEBUG, "\tSolicited: %s\n",
+ (na->nd_na_flags_reserved & ND_NA_FLAG_SOLICITED) ?
+ "Set" : "Not set");
+ logmsg(LOG_DEBUG, "\tOverride: %s\n",
+ (na->nd_na_flags_reserved & ND_NA_FLAG_OVERRIDE) ?
+ "Set" : "Not set");
+
+ len -= sizeof (*na);
+ opt = (struct nd_opt_hdr *)&na[1];
+ print_opt(opt, len);
+}
+
+static void
+print_opt(struct nd_opt_hdr *opt, int len)
+{
+ struct nd_opt_prefix_info *po;
+ struct nd_opt_mtu *mo;
+ struct nd_opt_lla *lo;
+ int optlen;
+ char abuf[INET6_ADDRSTRLEN];
+ char llabuf[BUFSIZ];
+
+ while (len >= sizeof (struct nd_opt_hdr)) {
+ optlen = opt->nd_opt_len * 8;
+ if (optlen == 0) {
+ logmsg(LOG_DEBUG, "Zero length option!\n");
+ break;
+ }
+ switch (opt->nd_opt_type) {
+ case ND_OPT_PREFIX_INFORMATION:
+ po = (struct nd_opt_prefix_info *)opt;
+ if (optlen != sizeof (*po) ||
+ optlen > len)
+ break;
+
+ logmsg(LOG_DEBUG, "\tOn link flag:%s\n",
+ (po->nd_opt_pi_flags_reserved &
+ ND_OPT_PI_FLAG_ONLINK) ? "Set" : "Not set");
+ logmsg(LOG_DEBUG, "\tAuto addrconf flag:%s\n",
+ (po->nd_opt_pi_flags_reserved &
+ ND_OPT_PI_FLAG_AUTO) ? "Set" : "Not set");
+ logmsg(LOG_DEBUG, "\tValid time: %u\n",
+ ntohl(po->nd_opt_pi_valid_time));
+ logmsg(LOG_DEBUG, "\tPreferred time: %u\n",
+ ntohl(po->nd_opt_pi_preferred_time));
+ logmsg(LOG_DEBUG, "\tPrefix: %s/%u\n",
+ inet_ntop(AF_INET6, (void *)&po->nd_opt_pi_prefix,
+ abuf, sizeof (abuf)),
+ po->nd_opt_pi_prefix_len);
+ break;
+ case ND_OPT_MTU:
+ mo = (struct nd_opt_mtu *)opt;
+ if (optlen != sizeof (*mo) ||
+ optlen > len)
+ break;
+ logmsg(LOG_DEBUG, "\tMTU: %d\n",
+ ntohl(mo->nd_opt_mtu_mtu));
+ break;
+ case ND_OPT_SOURCE_LINKADDR:
+ lo = (struct nd_opt_lla *)opt;
+ if (optlen < 8 ||
+ optlen > len)
+ break;
+ (void) fmt_lla(llabuf, sizeof (llabuf),
+ (uchar_t *)lo->nd_opt_lla_hdw_addr, optlen - 2);
+ logmsg(LOG_DEBUG, "\tSource LLA: len %d <%s>\n",
+ optlen-2, llabuf);
+ break;
+ case ND_OPT_TARGET_LINKADDR:
+ lo = (struct nd_opt_lla *)opt;
+ if (optlen < 8||
+ optlen > len)
+ break;
+ (void) fmt_lla(llabuf, sizeof (llabuf),
+ (uchar_t *)lo->nd_opt_lla_hdw_addr, optlen - 2);
+ logmsg(LOG_DEBUG, "\tTarget LLA: len %d <%s>\n",
+ optlen-2, llabuf);
+ break;
+ case ND_OPT_REDIRECTED_HEADER:
+ logmsg(LOG_DEBUG, "\tRedirected header option!\n");
+ break;
+ default:
+ logmsg(LOG_DEBUG, "Unkown option %d (0x%x)\n",
+ opt->nd_opt_type, opt->nd_opt_type);
+ break;
+ }
+ opt = (struct nd_opt_hdr *)((char *)opt + optlen);
+ len -= optlen;
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/main.c b/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/main.c
new file mode 100644
index 0000000000..1b0a1ca22b
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/main.c
@@ -0,0 +1,2083 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "defs.h"
+#include "tables.h"
+#include <fcntl.h>
+
+static void initlog(void);
+static void run_timeouts(void);
+static void check_fallback(void);
+
+static void advertise(struct sockaddr_in6 *sin6, struct phyint *pi,
+ boolean_t no_prefixes);
+static void solicit(struct sockaddr_in6 *sin6, struct phyint *pi);
+static void initifs(boolean_t first);
+static void check_if_removed(struct phyint *pi);
+static void loopback_ra_enqueue(struct phyint *pi,
+ struct nd_router_advert *ra, int len);
+static void loopback_ra_dequeue(void);
+static void check_daemonize(void);
+
+struct in6_addr all_nodes_mcast = { { 0xff, 0x2, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x1 } };
+
+struct in6_addr all_routers_mcast = { { 0xff, 0x2, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x2 } };
+
+static struct sockaddr_in6 v6allnodes = { AF_INET6, 0, 0,
+ { 0xff, 0x2, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x1 } };
+
+static struct sockaddr_in6 v6allrouters = { AF_INET6, 0, 0,
+ { 0xff, 0x2, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x2 } };
+
+static char **argv0; /* Saved for re-exec on SIGHUP */
+
+static uint64_t packet[(IP_MAXPACKET + 1)/8];
+
+static int show_ifs = 0;
+static boolean_t already_daemonized = _B_FALSE;
+int debug = 0;
+int no_loopback = 0; /* Do not send RA packets to ourselves */
+
+/*
+ * Size of routing socket message used by in.ndpd which includes the header,
+ * space for the RTA_DST, RTA_GATEWAY and RTA_NETMASK (each a sockaddr_in6)
+ * plus space for the RTA_IFP (a sockaddr_dl).
+ */
+#define NDP_RTM_MSGLEN sizeof (struct rt_msghdr) + \
+ sizeof (struct sockaddr_in6) + \
+ sizeof (struct sockaddr_in6) + \
+ sizeof (struct sockaddr_in6) + \
+ sizeof (struct sockaddr_dl)
+
+/*
+ * These are referenced externally in tables.c in order to fill in the
+ * dynamic portions of the routing socket message and then to send the message
+ * itself.
+ */
+int rtsock = -1; /* Routing socket */
+struct rt_msghdr *rt_msg; /* Routing socket message */
+struct sockaddr_in6 *rta_gateway; /* RTA_GATEWAY sockaddr */
+struct sockaddr_dl *rta_ifp; /* RTA_IFP sockaddr */
+
+/*
+ * Return the current time in milliseconds truncated to
+ * fit in an integer.
+ */
+uint_t
+getcurrenttime(void)
+{
+ struct timeval tp;
+
+ if (gettimeofday(&tp, NULL) < 0) {
+ logperror("getcurrenttime: gettimeofday failed");
+ exit(1);
+ }
+ return (tp.tv_sec * 1000 + tp.tv_usec / 1000);
+}
+
+/*
+ * Output a preformated packet from the packet[] buffer.
+ */
+static void
+sendpacket(struct sockaddr_in6 *sin6, int sock, int size, int flags)
+{
+ int cc;
+ char abuf[INET6_ADDRSTRLEN];
+
+ cc = sendto(sock, (char *)packet, size, flags,
+ (struct sockaddr *)sin6, sizeof (*sin6));
+ if (cc < 0 || cc != size) {
+ if (cc < 0) {
+ logperror("sendpacket: sendto");
+ }
+ logmsg(LOG_ERR, "sendpacket: wrote %s %d chars, ret=%d\n",
+ inet_ntop(sin6->sin6_family,
+ (void *)&sin6->sin6_addr,
+ abuf, sizeof (abuf)),
+ size, cc);
+ }
+}
+
+/* Send a Router Solicitation */
+static void
+solicit(struct sockaddr_in6 *sin6, struct phyint *pi)
+{
+ int packetlen = 0;
+ struct nd_router_solicit *rs = (struct nd_router_solicit *)packet;
+ char *pptr = (char *)packet;
+
+ rs->nd_rs_type = ND_ROUTER_SOLICIT;
+ rs->nd_rs_code = 0;
+ rs->nd_rs_cksum = htons(0);
+ rs->nd_rs_reserved = htonl(0);
+
+ packetlen += sizeof (*rs);
+ pptr += sizeof (*rs);
+
+ /* Attach any options */
+ if (pi->pi_hdw_addr_len != 0) {
+ struct nd_opt_lla *lo = (struct nd_opt_lla *)pptr;
+ int optlen;
+
+ /* roundup to multiple of 8 and make padding zero */
+ optlen = ((sizeof (struct nd_opt_hdr) +
+ pi->pi_hdw_addr_len + 7) / 8) * 8;
+ bzero(pptr, optlen);
+
+ lo->nd_opt_lla_type = ND_OPT_SOURCE_LINKADDR;
+ lo->nd_opt_lla_len = optlen / 8;
+ bcopy((char *)pi->pi_hdw_addr,
+ (char *)lo->nd_opt_lla_hdw_addr,
+ pi->pi_hdw_addr_len);
+ packetlen += optlen;
+ pptr += optlen;
+ }
+
+ if (debug & D_PKTOUT) {
+ print_route_sol("Sending solicitation to ", pi, rs, packetlen,
+ sin6);
+ }
+ sendpacket(sin6, pi->pi_sock, packetlen, 0);
+}
+
+/*
+ * Send a (set of) Router Advertisements and feed them back to ourselves
+ * for processing. Unless no_prefixes is set all prefixes are included.
+ * If there are too many prefix options to fit in one packet multiple
+ * packets will be sent - each containing a subset of the prefix options.
+ */
+static void
+advertise(struct sockaddr_in6 *sin6, struct phyint *pi, boolean_t no_prefixes)
+{
+ struct nd_opt_prefix_info *po;
+ char *pptr = (char *)packet;
+ struct nd_router_advert *ra;
+ struct adv_prefix *adv_pr;
+ int packetlen = 0;
+
+ ra = (struct nd_router_advert *)pptr;
+ ra->nd_ra_type = ND_ROUTER_ADVERT;
+ ra->nd_ra_code = 0;
+ ra->nd_ra_cksum = htons(0);
+ ra->nd_ra_curhoplimit = pi->pi_AdvCurHopLimit;
+ ra->nd_ra_flags_reserved = 0;
+ if (pi->pi_AdvManagedFlag)
+ ra->nd_ra_flags_reserved |= ND_RA_FLAG_MANAGED;
+ if (pi->pi_AdvOtherConfigFlag)
+ ra->nd_ra_flags_reserved |= ND_RA_FLAG_OTHER;
+
+ if (pi->pi_adv_state == FINAL_ADV)
+ ra->nd_ra_router_lifetime = htons(0);
+ else
+ ra->nd_ra_router_lifetime = htons(pi->pi_AdvDefaultLifetime);
+ ra->nd_ra_reachable = htonl(pi->pi_AdvReachableTime);
+ ra->nd_ra_retransmit = htonl(pi->pi_AdvRetransTimer);
+
+ packetlen = sizeof (*ra);
+ pptr += sizeof (*ra);
+
+ if (pi->pi_adv_state == FINAL_ADV) {
+ if (debug & D_PKTOUT) {
+ print_route_adv("Sending advert (FINAL) to ", pi,
+ ra, packetlen, sin6);
+ }
+ sendpacket(sin6, pi->pi_sock, packetlen, 0);
+ /* Feed packet back in for router operation */
+ loopback_ra_enqueue(pi, ra, packetlen);
+ return;
+ }
+
+ /* Attach any options */
+ if (pi->pi_hdw_addr_len != 0) {
+ struct nd_opt_lla *lo = (struct nd_opt_lla *)pptr;
+ int optlen;
+
+ /* roundup to multiple of 8 and make padding zero */
+ optlen = ((sizeof (struct nd_opt_hdr) +
+ pi->pi_hdw_addr_len + 7) / 8) * 8;
+ bzero(pptr, optlen);
+
+ lo->nd_opt_lla_type = ND_OPT_SOURCE_LINKADDR;
+ lo->nd_opt_lla_len = optlen / 8;
+ bcopy((char *)pi->pi_hdw_addr,
+ (char *)lo->nd_opt_lla_hdw_addr,
+ pi->pi_hdw_addr_len);
+ packetlen += optlen;
+ pptr += optlen;
+ }
+
+ if (pi->pi_AdvLinkMTU != 0) {
+ struct nd_opt_mtu *mo = (struct nd_opt_mtu *)pptr;
+
+ mo->nd_opt_mtu_type = ND_OPT_MTU;
+ mo->nd_opt_mtu_len = sizeof (struct nd_opt_mtu) / 8;
+ mo->nd_opt_mtu_reserved = 0;
+ mo->nd_opt_mtu_mtu = htonl(pi->pi_AdvLinkMTU);
+
+ packetlen += sizeof (struct nd_opt_mtu);
+ pptr += sizeof (struct nd_opt_mtu);
+ }
+
+ if (no_prefixes) {
+ if (debug & D_PKTOUT) {
+ print_route_adv("Sending advert to ", pi,
+ ra, packetlen, sin6);
+ }
+ sendpacket(sin6, pi->pi_sock, packetlen, 0);
+ /* Feed packet back in for router operation */
+ loopback_ra_enqueue(pi, ra, packetlen);
+ return;
+ }
+
+ po = (struct nd_opt_prefix_info *)pptr;
+ for (adv_pr = pi->pi_adv_prefix_list; adv_pr != NULL;
+ adv_pr = adv_pr->adv_pr_next) {
+ if (!adv_pr->adv_pr_AdvOnLinkFlag &&
+ !adv_pr->adv_pr_AdvAutonomousFlag) {
+ continue;
+ }
+
+ /*
+ * If the prefix doesn't fit in packet send
+ * what we have so far and start with new packet.
+ */
+ if (packetlen + sizeof (*po) >
+ pi->pi_LinkMTU - sizeof (struct ip6_hdr)) {
+ if (debug & D_PKTOUT) {
+ print_route_adv("Sending advert "
+ "(FRAG) to ",
+ pi, ra, packetlen, sin6);
+ }
+ sendpacket(sin6, pi->pi_sock, packetlen, 0);
+ /* Feed packet back in for router operation */
+ loopback_ra_enqueue(pi, ra, packetlen);
+ packetlen = sizeof (*ra);
+ pptr = (char *)packet + sizeof (*ra);
+ po = (struct nd_opt_prefix_info *)pptr;
+ }
+ po->nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION;
+ po->nd_opt_pi_len = sizeof (*po)/8;
+ po->nd_opt_pi_flags_reserved = 0;
+ if (adv_pr->adv_pr_AdvOnLinkFlag) {
+ po->nd_opt_pi_flags_reserved |=
+ ND_OPT_PI_FLAG_ONLINK;
+ }
+ if (adv_pr->adv_pr_AdvAutonomousFlag) {
+ po->nd_opt_pi_flags_reserved |=
+ ND_OPT_PI_FLAG_AUTO;
+ }
+ po->nd_opt_pi_prefix_len = adv_pr->adv_pr_prefix_len;
+ /*
+ * If both Adv*Expiration and Adv*Lifetime are
+ * set we prefer the former and make the lifetime
+ * decrement in real time.
+ */
+ if (adv_pr->adv_pr_AdvValidRealTime) {
+ po->nd_opt_pi_valid_time =
+ htonl(adv_pr->adv_pr_AdvValidExpiration);
+ } else {
+ po->nd_opt_pi_valid_time =
+ htonl(adv_pr->adv_pr_AdvValidLifetime);
+ }
+ if (adv_pr->adv_pr_AdvPreferredRealTime) {
+ po->nd_opt_pi_preferred_time =
+ htonl(adv_pr->adv_pr_AdvPreferredExpiration);
+ } else {
+ po->nd_opt_pi_preferred_time =
+ htonl(adv_pr->adv_pr_AdvPreferredLifetime);
+ }
+ po->nd_opt_pi_reserved2 = htonl(0);
+ po->nd_opt_pi_prefix = adv_pr->adv_pr_prefix;
+
+ po++;
+ packetlen += sizeof (*po);
+ }
+ if (debug & D_PKTOUT) {
+ print_route_adv("Sending advert to ", pi,
+ ra, packetlen, sin6);
+ }
+ sendpacket(sin6, pi->pi_sock, packetlen, 0);
+ /* Feed packet back in for router operation */
+ loopback_ra_enqueue(pi, ra, packetlen);
+}
+
+/* Poll support */
+static int pollfd_num = 0; /* Allocated and initialized */
+static struct pollfd *pollfds = NULL;
+
+/*
+ * Add fd to the set being polled. Returns 0 if ok; -1 if failed.
+ */
+int
+poll_add(int fd)
+{
+ int i;
+ int new_num;
+ struct pollfd *newfds;
+retry:
+ /* Check if already present */
+ for (i = 0; i < pollfd_num; i++) {
+ if (pollfds[i].fd == fd)
+ return (0);
+ }
+ /* Check for empty spot already present */
+ for (i = 0; i < pollfd_num; i++) {
+ if (pollfds[i].fd == -1) {
+ pollfds[i].fd = fd;
+ return (0);
+ }
+ }
+
+ /* Allocate space for 32 more fds and initialize to -1 */
+ new_num = pollfd_num + 32;
+ newfds = realloc(pollfds, new_num * sizeof (struct pollfd));
+ if (newfds == NULL) {
+ logperror("poll_add: realloc");
+ return (-1);
+ }
+ for (i = pollfd_num; i < new_num; i++) {
+ newfds[i].fd = -1;
+ newfds[i].events = POLLIN;
+ }
+ pollfd_num = new_num;
+ pollfds = newfds;
+ goto retry;
+}
+
+/*
+ * Remove fd from the set being polled. Returns 0 if ok; -1 if failed.
+ */
+int
+poll_remove(int fd)
+{
+ int i;
+
+ /* Check if already present */
+ for (i = 0; i < pollfd_num; i++) {
+ if (pollfds[i].fd == fd) {
+ pollfds[i].fd = -1;
+ return (0);
+ }
+ }
+ return (-1);
+}
+
+/*
+ * Extract information about the ifname (either a physical interface and
+ * the ":0" logical interface or just a logical interface).
+ * If the interface (still) exists in kernel set pr_in_use
+ * for caller to be able to detect interfaces that are removed.
+ * Starts sending advertisements/solicitations when new physical interfaces
+ * are detected.
+ */
+static void
+if_process(int s, char *ifname, boolean_t first)
+{
+ struct lifreq lifr;
+ struct phyint *pi;
+ struct prefix *pr;
+ char *cp;
+ char phyintname[LIFNAMSIZ + 1];
+
+ if (debug & D_IFSCAN)
+ logmsg(LOG_DEBUG, "if_process(%s)\n", ifname);
+
+ (void) strncpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
+ lifr.lifr_name[sizeof (lifr.lifr_name) - 1] = '\0';
+ if (ioctl(s, SIOCGLIFFLAGS, (char *)&lifr) < 0) {
+ if (errno == ENXIO) {
+ /*
+ * Interface has disappeared
+ */
+ return;
+ }
+ logperror("if_process: ioctl (get interface flags)");
+ return;
+ }
+
+ /*
+ * Ignore loopback and point-to-multipoint interfaces.
+ * Point-to-point interfaces always have IFF_MULTICAST set.
+ */
+ if (!(lifr.lifr_flags & IFF_MULTICAST) ||
+ (lifr.lifr_flags & IFF_LOOPBACK)) {
+ return;
+ }
+
+ if (!(lifr.lifr_flags & IFF_IPV6))
+ return;
+
+ (void) strncpy(phyintname, ifname, sizeof (phyintname));
+ phyintname[sizeof (phyintname) - 1] = '\0';
+ if ((cp = strchr(phyintname, IF_SEPARATOR)) != NULL) {
+ *cp = '\0';
+ }
+
+ pi = phyint_lookup(phyintname);
+ if (pi == NULL) {
+ /*
+ * Do not add anything for new interfaces until they are UP.
+ * For existing interfaces we track the up flag.
+ */
+ if (!(lifr.lifr_flags & IFF_UP))
+ return;
+
+ pi = phyint_create(phyintname);
+ if (pi == NULL) {
+ logmsg(LOG_ERR, "if_process: out of memory\n");
+ return;
+ }
+ }
+ (void) phyint_init_from_k(pi);
+ if (pi->pi_sock == -1 && !(pi->pi_kernel_state & PI_PRESENT)) {
+ /* Interface is not yet present */
+ if (debug & D_PHYINT) {
+ logmsg(LOG_DEBUG, "if_process: interface not yet "
+ "present %s\n", pi->pi_name);
+ }
+ return;
+ }
+
+ if (pi->pi_sock != -1) {
+ if (poll_add(pi->pi_sock) == -1) {
+ /*
+ * reset state.
+ */
+ phyint_cleanup(pi);
+ }
+ }
+
+ /*
+ * Check if IFF_ROUTER has been turned off in kernel in which
+ * case we have to turn off AdvSendAdvertisements.
+ * The kernel will automatically turn off IFF_ROUTER if
+ * ip6_forwarding is turned off.
+ * Note that we do not switch back should IFF_ROUTER be turned on.
+ */
+ if (!first &&
+ pi->pi_AdvSendAdvertisements && !(pi->pi_flags & IFF_ROUTER)) {
+ logmsg(LOG_INFO, "No longer a router on %s\n", pi->pi_name);
+ check_to_advertise(pi, START_FINAL_ADV);
+
+ pi->pi_AdvSendAdvertisements = 0;
+ pi->pi_sol_state = NO_SOLICIT;
+ }
+
+ /*
+ * Send advertisments and solicitation only if the interface is
+ * present in the kernel.
+ */
+ if (pi->pi_kernel_state & PI_PRESENT) {
+
+ if (pi->pi_AdvSendAdvertisements) {
+ if (pi->pi_adv_state == NO_ADV)
+ check_to_advertise(pi, START_INIT_ADV);
+ } else {
+ if (pi->pi_sol_state == NO_SOLICIT)
+ check_to_solicit(pi, START_INIT_SOLICIT);
+ }
+ }
+
+ /*
+ * Track static kernel prefixes to prevent in.ndpd from clobbering
+ * them by creating a struct prefix for each prefix detected in the
+ * kernel.
+ */
+ pr = prefix_lookup_name(pi, ifname);
+ if (pr == NULL) {
+ pr = prefix_create_name(pi, ifname);
+ if (pr == NULL) {
+ logmsg(LOG_ERR, "if_process: out of memory\n");
+ return;
+ }
+ if (prefix_init_from_k(pr) == -1) {
+ prefix_delete(pr);
+ return;
+ }
+ }
+ /* Detect prefixes which are removed */
+ if (pr->pr_kernel_state != 0)
+ pr->pr_in_use = _B_TRUE;
+}
+
+static int ifsock = -1;
+
+/*
+ * Scan all interfaces to detect changes as well as new and deleted intefaces
+ * 'first' is set for the initial call only. Do not effect anything.
+ */
+static void
+initifs(boolean_t first)
+{
+ char *buf;
+ int bufsize;
+ int numifs;
+ int n;
+ struct lifnum lifn;
+ struct lifconf lifc;
+ struct lifreq *lifr;
+ struct phyint *pi;
+ struct phyint *next_pi;
+ struct prefix *pr;
+
+ if (debug & D_IFSCAN)
+ logmsg(LOG_DEBUG, "Reading interface configuration\n");
+ if (ifsock < 0) {
+ ifsock = socket(AF_INET6, SOCK_DGRAM, 0);
+ if (ifsock < 0) {
+ logperror("initifs: socket");
+ return;
+ }
+ }
+ lifn.lifn_family = AF_INET6;
+ lifn.lifn_flags = LIFC_NOXMIT | LIFC_TEMPORARY;
+ if (ioctl(ifsock, SIOCGLIFNUM, (char *)&lifn) < 0) {
+ logperror("initifs: ioctl (get interface numbers)");
+ return;
+ }
+ numifs = lifn.lifn_count;
+ bufsize = numifs * sizeof (struct lifreq);
+
+ buf = (char *)malloc(bufsize);
+ if (buf == NULL) {
+ logmsg(LOG_ERR, "initifs: out of memory\n");
+ return;
+ }
+
+ /*
+ * Mark the interfaces so that we can find phyints and prefixes
+ * which have disappeared from the kernel.
+ * if_process will set pr_in_use when it finds the interface
+ * in the kernel.
+ */
+ for (pi = phyints; pi != NULL; pi = pi->pi_next) {
+ /*
+ * Before re-examining the state of the interfaces,
+ * PI_PRESENT should be cleared from pi_kernel_state.
+ */
+ pi->pi_kernel_state &= ~PI_PRESENT;
+ for (pr = pi->pi_prefix_list; pr != NULL; pr = pr->pr_next) {
+ pr->pr_in_use = _B_FALSE;
+ }
+ }
+
+ lifc.lifc_family = AF_INET6;
+ lifc.lifc_flags = LIFC_NOXMIT | LIFC_TEMPORARY;
+ lifc.lifc_len = bufsize;
+ lifc.lifc_buf = buf;
+
+ if (ioctl(ifsock, SIOCGLIFCONF, (char *)&lifc) < 0) {
+ logperror("initifs: ioctl (get interface configuration)");
+ free(buf);
+ return;
+ }
+
+ lifr = (struct lifreq *)lifc.lifc_req;
+ for (n = lifc.lifc_len / sizeof (struct lifreq); n > 0; n--, lifr++)
+ if_process(ifsock, lifr->lifr_name, first);
+ free(buf);
+
+ /*
+ * Detect phyints that have been removed from the kernel.
+ * Since we can't recreate it here (would require ifconfig plumb
+ * logic) we just terminate use of that phyint.
+ */
+ for (pi = phyints; pi != NULL; pi = next_pi) {
+ next_pi = pi->pi_next;
+ /*
+ * If interface (still) exists in kernel, set
+ * pi_state to indicate that.
+ */
+ if (pi->pi_kernel_state & PI_PRESENT) {
+ pi->pi_state |= PI_PRESENT;
+ }
+
+ check_if_removed(pi);
+ }
+ if (show_ifs)
+ phyint_print_all();
+}
+
+
+/*
+ * Router advertisement state machine. Used for everything but timer
+ * events which use advertise_event directly.
+ */
+void
+check_to_advertise(struct phyint *pi, enum adv_events event)
+{
+ uint_t delay;
+ enum adv_states old_state = pi->pi_adv_state;
+
+ if (debug & D_STATE) {
+ logmsg(LOG_DEBUG, "check_to_advertise(%s, %d) state %d\n",
+ pi->pi_name, (int)event, (int)old_state);
+ }
+ delay = advertise_event(pi, event, 0);
+ if (delay != TIMER_INFINITY) {
+ /* Make sure the global next event is updated */
+ timer_schedule(delay);
+ }
+
+ if (debug & D_STATE) {
+ logmsg(LOG_DEBUG, "check_to_advertise(%s, %d) state %d -> %d\n",
+ pi->pi_name, (int)event, (int)old_state,
+ (int)pi->pi_adv_state);
+ }
+}
+
+/*
+ * Router advertisement state machine.
+ * Return the number of milliseconds until next timeout (TIMER_INFINITY
+ * if never).
+ * For the ADV_TIMER event the caller passes in the number of milliseconds
+ * since the last timer event in the 'elapsed' parameter.
+ */
+uint_t
+advertise_event(struct phyint *pi, enum adv_events event, uint_t elapsed)
+{
+ uint_t delay;
+
+ if (debug & D_STATE) {
+ logmsg(LOG_DEBUG, "advertise_event(%s, %d, %d) state %d\n",
+ pi->pi_name, (int)event, elapsed, (int)pi->pi_adv_state);
+ }
+ check_daemonize();
+ if (!pi->pi_AdvSendAdvertisements)
+ return (TIMER_INFINITY);
+ if (pi->pi_flags & IFF_NORTEXCH) {
+ if (debug & D_PKTOUT) {
+ logmsg(LOG_DEBUG, "Suppress sending RA packet on %s "
+ "(no route exchange on interface)\n",
+ pi->pi_name);
+ }
+ return (TIMER_INFINITY);
+ }
+
+ switch (event) {
+ case ADV_OFF:
+ pi->pi_adv_state = NO_ADV;
+ return (TIMER_INFINITY);
+
+ case START_INIT_ADV:
+ if (pi->pi_adv_state == INIT_ADV)
+ return (pi->pi_adv_time_left);
+ pi->pi_adv_count = ND_MAX_INITIAL_RTR_ADVERTISEMENTS;
+ pi->pi_adv_time_left = 0;
+ pi->pi_adv_state = INIT_ADV;
+ break; /* send advertisement */
+
+ case START_FINAL_ADV:
+ if (pi->pi_adv_state == NO_ADV)
+ return (TIMER_INFINITY);
+ if (pi->pi_adv_state == FINAL_ADV)
+ return (pi->pi_adv_time_left);
+ pi->pi_adv_count = ND_MAX_FINAL_RTR_ADVERTISEMENTS;
+ pi->pi_adv_time_left = 0;
+ pi->pi_adv_state = FINAL_ADV;
+ break; /* send advertisement */
+
+ case RECEIVED_SOLICIT:
+ if (pi->pi_adv_state == NO_ADV)
+ return (TIMER_INFINITY);
+ if (pi->pi_adv_state == SOLICIT_ADV) {
+ if (pi->pi_adv_time_left != 0)
+ return (pi->pi_adv_time_left);
+ break;
+ }
+ delay = GET_RANDOM(0, ND_MAX_RA_DELAY_TIME);
+ if (delay < pi->pi_adv_time_left)
+ pi->pi_adv_time_left = delay;
+ if (pi->pi_adv_time_since_sent < ND_MIN_DELAY_BETWEEN_RAS) {
+ /*
+ * Send an advertisement (ND_MIN_DELAY_BETWEEN_RAS
+ * plus random delay) after the previous
+ * advertisement was sent.
+ */
+ pi->pi_adv_time_left = delay +
+ ND_MIN_DELAY_BETWEEN_RAS -
+ pi->pi_adv_time_since_sent;
+ }
+ pi->pi_adv_state = SOLICIT_ADV;
+ break;
+
+ case ADV_TIMER:
+ if (pi->pi_adv_state == NO_ADV)
+ return (TIMER_INFINITY);
+ /* Decrease time left */
+ if (pi->pi_adv_time_left >= elapsed)
+ pi->pi_adv_time_left -= elapsed;
+ else
+ pi->pi_adv_time_left = 0;
+
+ /* Increase time since last advertisement was sent */
+ pi->pi_adv_time_since_sent += elapsed;
+ break;
+ default:
+ logmsg(LOG_ERR, "advertise_event: Unknown event %d\n",
+ (int)event);
+ return (TIMER_INFINITY);
+ }
+
+ if (pi->pi_adv_time_left != 0)
+ return (pi->pi_adv_time_left);
+
+ /* Send advertisement and calculate next time to send */
+ if (pi->pi_adv_state == FINAL_ADV) {
+ /* Omit the prefixes */
+ advertise(&v6allnodes, pi, _B_TRUE);
+ } else {
+ advertise(&v6allnodes, pi, _B_FALSE);
+ }
+ pi->pi_adv_time_since_sent = 0;
+
+ switch (pi->pi_adv_state) {
+ case SOLICIT_ADV:
+ /*
+ * The solicited advertisement has been sent.
+ * Revert to periodic advertisements.
+ */
+ pi->pi_adv_state = REG_ADV;
+ /* FALLTHRU */
+ case REG_ADV:
+ pi->pi_adv_time_left =
+ GET_RANDOM(1000 * pi->pi_MinRtrAdvInterval,
+ 1000 * pi->pi_MaxRtrAdvInterval);
+ break;
+
+ case INIT_ADV:
+ if (--pi->pi_adv_count > 0) {
+ delay = GET_RANDOM(1000 * pi->pi_MinRtrAdvInterval,
+ 1000 * pi->pi_MaxRtrAdvInterval);
+ if (delay > ND_MAX_INITIAL_RTR_ADVERT_INTERVAL)
+ delay = ND_MAX_INITIAL_RTR_ADVERT_INTERVAL;
+ pi->pi_adv_time_left = delay;
+ } else {
+ pi->pi_adv_time_left =
+ GET_RANDOM(1000 * pi->pi_MinRtrAdvInterval,
+ 1000 * pi->pi_MaxRtrAdvInterval);
+ pi->pi_adv_state = REG_ADV;
+ }
+ break;
+
+ case FINAL_ADV:
+ if (--pi->pi_adv_count > 0) {
+ pi->pi_adv_time_left =
+ ND_MAX_INITIAL_RTR_ADVERT_INTERVAL;
+ } else {
+ pi->pi_adv_state = NO_ADV;
+ }
+ break;
+ }
+ if (pi->pi_adv_state != NO_ADV)
+ return (pi->pi_adv_time_left);
+ else
+ return (TIMER_INFINITY);
+}
+
+/*
+ * Router solicitation state machine. Used for everything but timer
+ * events which use solicit_event directly.
+ */
+void
+check_to_solicit(struct phyint *pi, enum solicit_events event)
+{
+ uint_t delay;
+ enum solicit_states old_state = pi->pi_sol_state;
+
+ if (debug & D_STATE) {
+ logmsg(LOG_DEBUG, "check_to_solicit(%s, %d) state %d\n",
+ pi->pi_name, (int)event, (int)old_state);
+ }
+ delay = solicit_event(pi, event, 0);
+ if (delay != TIMER_INFINITY) {
+ /* Make sure the global next event is updated */
+ timer_schedule(delay);
+ }
+
+ if (debug & D_STATE) {
+ logmsg(LOG_DEBUG, "check_to_solicit(%s, %d) state %d -> %d\n",
+ pi->pi_name, (int)event, (int)old_state,
+ (int)pi->pi_sol_state);
+ }
+}
+
+static void
+daemonize_ndpd(void)
+{
+ FILE *pidfp;
+ mode_t pidmode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); /* 0644 */
+ struct itimerval it;
+ boolean_t timerval = _B_TRUE;
+
+ /*
+ * Need to get current timer settings so they can be restored
+ * after the fork(), as the it_value and it_interval values for
+ * the ITIMER_REAL timer are reset to 0 in the child process.
+ */
+ if (getitimer(ITIMER_REAL, &it) < 0) {
+ if (debug & D_TIMER)
+ logmsg(LOG_DEBUG,
+ "daemonize_ndpd: failed to get itimerval\n");
+ timerval = _B_FALSE;
+ }
+
+ /* Daemonize. */
+ switch (fork()) {
+ case 0:
+ /* Child */
+ break;
+ case -1:
+ logperror("fork");
+ exit(1);
+ default:
+ /* Parent */
+ _exit(0);
+ }
+
+ /* Store our process id, blow away any existing file if it exists. */
+ if ((pidfp = fopen(PATH_PID, "w")) == NULL) {
+ (void) fprintf(stderr, "%s: unable to open " PATH_PID ": %s\n",
+ argv0[0], strerror(errno));
+ } else {
+ (void) fprintf(pidfp, "%ld\n", getpid());
+ (void) fclose(pidfp);
+ (void) chmod(PATH_PID, pidmode);
+ }
+
+ (void) close(0);
+ (void) close(1);
+ (void) close(2);
+
+ (void) chdir("/");
+ (void) open("/dev/null", O_RDWR);
+ (void) dup2(0, 1);
+ (void) dup2(0, 2);
+ (void) setsid();
+
+ already_daemonized = _B_TRUE;
+
+ /*
+ * Restore timer values, if we were able to save them; if not,
+ * check and set the right value by calling run_timeouts().
+ */
+ if (timerval) {
+ if (setitimer(ITIMER_REAL, &it, NULL) < 0) {
+ logperror("daemonize_ndpd: setitimer");
+ exit(2);
+ }
+ } else {
+ run_timeouts();
+ }
+}
+
+/*
+ * Check to see if the time is right to daemonize. The right time is when:
+ *
+ * 1. We haven't already daemonized.
+ * 2. We are not in debug mode.
+ * 3. All interfaces are marked IFF_NOXMIT.
+ * 4. All non-router interfaces have their prefixes set up and we're
+ * done sending router solicitations on those interfaces without
+ * prefixes.
+ */
+static void
+check_daemonize(void)
+{
+ struct phyint *pi;
+
+ if (already_daemonized || debug != 0)
+ return;
+
+ for (pi = phyints; pi != NULL; pi = pi->pi_next) {
+ if (!(pi->pi_flags & IFF_NOXMIT))
+ break;
+ }
+
+ /*
+ * If we can't transmit on any of the interfaces there is no reason
+ * to hold up progress.
+ */
+ if (pi == NULL) {
+ daemonize_ndpd();
+ return;
+ }
+
+ /* Check all interfaces. If any are still soliciting, just return. */
+ for (pi = phyints; pi != NULL; pi = pi->pi_next) {
+ if (pi->pi_AdvSendAdvertisements ||
+ !(pi->pi_kernel_state & PI_PRESENT))
+ continue;
+
+ if (pi->pi_sol_state == INIT_SOLICIT)
+ return;
+ }
+
+ daemonize_ndpd();
+}
+
+/*
+ * Router solicitation state machine.
+ * Return the number of milliseconds until next timeout (TIMER_INFINITY
+ * if never).
+ * For the SOL_TIMER event the caller passes in the number of milliseconds
+ * since the last timer event in the 'elapsed' parameter.
+ */
+uint_t
+solicit_event(struct phyint *pi, enum solicit_events event, uint_t elapsed)
+{
+ if (debug & D_STATE) {
+ logmsg(LOG_DEBUG, "solicit_event(%s, %d, %d) state %d\n",
+ pi->pi_name, (int)event, elapsed, (int)pi->pi_sol_state);
+ }
+
+ if (pi->pi_AdvSendAdvertisements)
+ return (TIMER_INFINITY);
+ if (pi->pi_flags & IFF_NORTEXCH) {
+ if (debug & D_PKTOUT) {
+ logmsg(LOG_DEBUG, "Suppress sending RS packet on %s "
+ "(no route exchange on interface)\n",
+ pi->pi_name);
+ }
+ return (TIMER_INFINITY);
+ }
+
+ switch (event) {
+ case SOLICIT_OFF:
+ pi->pi_sol_state = NO_SOLICIT;
+ check_daemonize();
+ return (TIMER_INFINITY);
+
+ case SOLICIT_DONE:
+ pi->pi_sol_state = DONE_SOLICIT;
+ check_daemonize();
+ return (TIMER_INFINITY);
+
+ case START_INIT_SOLICIT:
+ if (pi->pi_sol_state == INIT_SOLICIT)
+ return (pi->pi_sol_time_left);
+ pi->pi_sol_count = ND_MAX_RTR_SOLICITATIONS;
+ pi->pi_sol_time_left =
+ GET_RANDOM(0, ND_MAX_RTR_SOLICITATION_DELAY);
+ pi->pi_sol_state = INIT_SOLICIT;
+ break;
+
+ case SOL_TIMER:
+ if (pi->pi_sol_state == NO_SOLICIT)
+ return (TIMER_INFINITY);
+ /* Decrease time left */
+ if (pi->pi_sol_time_left >= elapsed)
+ pi->pi_sol_time_left -= elapsed;
+ else
+ pi->pi_sol_time_left = 0;
+ break;
+ default:
+ logmsg(LOG_ERR, "solicit_event: Unknown event %d\n",
+ (int)event);
+ return (TIMER_INFINITY);
+ }
+
+ if (pi->pi_sol_time_left != 0)
+ return (pi->pi_sol_time_left);
+
+ /* Send solicitation and calculate next time */
+ switch (pi->pi_sol_state) {
+ case INIT_SOLICIT:
+ solicit(&v6allrouters, pi);
+ if (--pi->pi_sol_count == 0) {
+ pi->pi_sol_state = DONE_SOLICIT;
+ /* check if default route needs to be installed */
+ check_fallback();
+ check_daemonize();
+ return (TIMER_INFINITY);
+ }
+ pi->pi_sol_time_left = ND_RTR_SOLICITATION_INTERVAL;
+ return (pi->pi_sol_time_left);
+ case NO_SOLICIT:
+ case DONE_SOLICIT:
+ return (TIMER_INFINITY);
+ default:
+ return (pi->pi_sol_time_left);
+ }
+}
+
+/*
+ * If no interfaces are advertising and have gone to DONE_SOLICIT
+ * and there are no default routers:
+ * We add an "everything is on-link" default route if there
+ * is only one non-point-to-point phyint in the kernel.
+ * XXX What to do when there are multiple phyints?
+ * XXX The router_delete_onlink checks only operate on one phyint!
+ */
+void
+check_fallback(void)
+{
+ struct phyint *pi;
+ struct router *dr;
+ boolean_t add_default;
+ int num_phyints;
+
+ if (debug & D_PREFIX) {
+ logmsg(LOG_DEBUG, "check_fallback()\n");
+ }
+ add_default = _B_TRUE;
+ num_phyints = 0;
+ for (pi = phyints; pi != NULL; pi = pi->pi_next) {
+ if (pi->pi_AdvSendAdvertisements ||
+ pi->pi_sol_state != DONE_SOLICIT) {
+ add_default = _B_FALSE;
+ break;
+ }
+ if (!(pi->pi_kernel_state & PI_PRESENT))
+ continue;
+
+ if (!(pi->pi_flags & IFF_POINTOPOINT))
+ num_phyints++;
+ for (dr = pi->pi_router_list; dr != NULL; dr = dr->dr_next) {
+ if (dr->dr_inkernel) {
+ add_default = _B_FALSE;
+ break;
+ }
+ }
+ if (!add_default)
+ break;
+ }
+ if (num_phyints == 1 && add_default) {
+ if (debug & D_ROUTER) {
+ logmsg(LOG_DEBUG,
+ "check_fallback: create default router\n");
+ }
+ for (pi = phyints; pi != NULL; pi = pi->pi_next) {
+ if (!(pi->pi_kernel_state & PI_PRESENT))
+ continue;
+ if (debug & D_ROUTER) {
+ logmsg(LOG_DEBUG, "check_fallback: "
+ "%s default router\n", pi->pi_name);
+ }
+ if (!(pi->pi_flags & IFF_POINTOPOINT))
+ (void) router_create_onlink(pi);
+ }
+ }
+
+}
+
+/*
+ * Timer mechanism using relative time (in milliseconds) from the
+ * previous timer event. Timers exceeding TIMER_INFINITY milliseconds
+ * will fire after TIMER_INFINITY milliseconds.
+ */
+static uint_t timer_previous; /* When last SIGALRM occurred */
+static uint_t timer_next; /* Currently scheduled timeout */
+
+static void
+timer_init(void)
+{
+ timer_previous = getcurrenttime();
+ timer_next = TIMER_INFINITY;
+ run_timeouts();
+}
+
+/*
+ * Make sure the next SIGALRM occurs delay milliseconds from the current
+ * time if not earlier.
+ * Handles getcurrenttime (32 bit integer holding milliseconds) wraparound
+ * by treating differences greater than 0x80000000 as negative.
+ */
+void
+timer_schedule(uint_t delay)
+{
+ uint_t now;
+ struct itimerval itimerval;
+
+ now = getcurrenttime();
+ if (debug & D_TIMER) {
+ logmsg(LOG_DEBUG, "timer_schedule(%u): now %u next %u\n",
+ delay, now, timer_next);
+ }
+ /* Will this timer occur before the currently scheduled SIGALRM? */
+ if (delay >= timer_next - now) {
+ if (debug & D_TIMER) {
+ logmsg(LOG_DEBUG, "timer_schedule(%u): no action - "
+ "next in %u ms\n",
+ delay, timer_next - now);
+ }
+ return;
+ }
+ if (delay == 0) {
+ /* Minimum allowed delay */
+ delay = 1;
+ }
+ timer_next = now + delay;
+
+ itimerval.it_value.tv_sec = delay / 1000;
+ itimerval.it_value.tv_usec = (delay % 1000) * 1000;
+ itimerval.it_interval.tv_sec = 0;
+ itimerval.it_interval.tv_usec = 0;
+ if (debug & D_TIMER) {
+ logmsg(LOG_DEBUG, "timer_schedule(%u): sec %lu usec %lu\n",
+ delay,
+ itimerval.it_value.tv_sec, itimerval.it_value.tv_usec);
+ }
+ if (setitimer(ITIMER_REAL, &itimerval, NULL) < 0) {
+ logperror("timer_schedule: setitimer");
+ exit(2);
+ }
+}
+
+/*
+ * Conditional running of timer. If more than 'minimal_time' millseconds
+ * since the timer routines were last run we run them.
+ * Used when packets arrive.
+ */
+static void
+conditional_run_timeouts(uint_t minimal_time)
+{
+ uint_t now;
+ uint_t elapsed;
+
+ now = getcurrenttime();
+ elapsed = now - timer_previous;
+ if (elapsed > minimal_time) {
+ if (debug & D_TIMER) {
+ logmsg(LOG_DEBUG, "conditional_run_timeouts: "
+ "elapsed %d\n", elapsed);
+ }
+ run_timeouts();
+ }
+}
+
+/*
+ * Timer has fired.
+ * Determine when the next timer event will occur by asking all
+ * the timer routines.
+ * Should not be called from a timer routine but in some cases this is
+ * done because the code doesn't know that e.g. it was called from
+ * ifconfig_timer(). In this case the nested run_timeouts will just return but
+ * the running run_timeouts will ensure to call all the timer functions by
+ * looping once more.
+ */
+static void
+run_timeouts(void)
+{
+ uint_t now;
+ uint_t elapsed;
+ uint_t next;
+ uint_t nexti;
+ struct phyint *pi;
+ struct phyint *next_pi;
+ struct prefix *pr;
+ struct prefix *next_pr;
+ struct adv_prefix *adv_pr;
+ struct adv_prefix *next_adv_pr;
+ struct router *dr;
+ struct router *next_dr;
+ static boolean_t timeout_running;
+ static boolean_t do_retry;
+
+ if (timeout_running) {
+ if (debug & D_TIMER)
+ logmsg(LOG_DEBUG, "run_timeouts: nested call\n");
+ do_retry = _B_TRUE;
+ return;
+ }
+ timeout_running = _B_TRUE;
+retry:
+ /* How much time since the last time we were called? */
+ now = getcurrenttime();
+ elapsed = now - timer_previous;
+ timer_previous = now;
+
+ if (debug & D_TIMER)
+ logmsg(LOG_DEBUG, "run_timeouts: elapsed %d\n", elapsed);
+
+ next = TIMER_INFINITY;
+ for (pi = phyints; pi != NULL; pi = next_pi) {
+ next_pi = pi->pi_next;
+ nexti = phyint_timer(pi, elapsed);
+ if (nexti != TIMER_INFINITY && nexti < next)
+ next = nexti;
+ if (debug & D_TIMER) {
+ logmsg(LOG_DEBUG, "run_timeouts (pi %s): %d -> %u ms\n",
+ pi->pi_name, nexti, next);
+ }
+ for (pr = pi->pi_prefix_list; pr != NULL; pr = next_pr) {
+ next_pr = pr->pr_next;
+ nexti = prefix_timer(pr, elapsed);
+ if (nexti != TIMER_INFINITY && nexti < next)
+ next = nexti;
+ if (debug & D_TIMER) {
+ logmsg(LOG_DEBUG, "run_timeouts (pr %s): "
+ "%d -> %u ms\n", pr->pr_name, nexti, next);
+ }
+ }
+ for (adv_pr = pi->pi_adv_prefix_list; adv_pr != NULL;
+ adv_pr = next_adv_pr) {
+ next_adv_pr = adv_pr->adv_pr_next;
+ nexti = adv_prefix_timer(adv_pr, elapsed);
+ if (nexti != TIMER_INFINITY && nexti < next)
+ next = nexti;
+ if (debug & D_TIMER) {
+ logmsg(LOG_DEBUG, "run_timeouts "
+ "(adv pr on %s): %d -> %u ms\n",
+ adv_pr->adv_pr_physical->pi_name,
+ nexti, next);
+ }
+ }
+ for (dr = pi->pi_router_list; dr != NULL; dr = next_dr) {
+ next_dr = dr->dr_next;
+ nexti = router_timer(dr, elapsed);
+ if (nexti != TIMER_INFINITY && nexti < next)
+ next = nexti;
+ if (debug & D_TIMER) {
+ logmsg(LOG_DEBUG, "run_timeouts (dr): "
+ "%d -> %u ms\n", nexti, next);
+ }
+ }
+ if (pi->pi_TmpAddrsEnabled) {
+ nexti = tmptoken_timer(pi, elapsed);
+ if (nexti != TIMER_INFINITY && nexti < next)
+ next = nexti;
+ if (debug & D_TIMER) {
+ logmsg(LOG_DEBUG, "run_timeouts (tmp on %s): "
+ "%d -> %u ms\n", pi->pi_name, nexti, next);
+ }
+ }
+ }
+ /*
+ * Make sure the timer functions are run at least once
+ * an hour.
+ */
+ if (next == TIMER_INFINITY)
+ next = 3600 * 1000; /* 1 hour */
+
+ if (debug & D_TIMER)
+ logmsg(LOG_DEBUG, "run_timeouts: %u ms\n", next);
+ timer_schedule(next);
+ if (do_retry) {
+ if (debug & D_TIMER)
+ logmsg(LOG_DEBUG, "run_timeouts: retry\n");
+ do_retry = _B_FALSE;
+ goto retry;
+ }
+ timeout_running = _B_FALSE;
+}
+
+static int eventpipe_read = -1; /* Used for synchronous signal delivery */
+static int eventpipe_write = -1;
+
+/*
+ * Ensure that signals are processed synchronously with the rest of
+ * the code by just writing a one character signal number on the pipe.
+ * The poll loop will pick this up and process the signal event.
+ */
+static void
+sig_handler(int signo)
+{
+ uchar_t buf = (uchar_t)signo;
+
+ if (eventpipe_write == -1) {
+ logmsg(LOG_ERR, "sig_handler: no pipe\n");
+ return;
+ }
+ if (write(eventpipe_write, &buf, sizeof (buf)) < 0)
+ logperror("sig_handler: write");
+}
+
+/*
+ * Pick up a signal "byte" from the pipe and process it.
+ */
+static void
+in_signal(int fd)
+{
+ uchar_t buf;
+ struct phyint *pi;
+ struct phyint *next_pi;
+
+ switch (read(fd, &buf, sizeof (buf))) {
+ case -1:
+ logperror("in_signal: read");
+ exit(1);
+ /* NOTREACHED */
+ case 1:
+ break;
+ case 0:
+ logmsg(LOG_ERR, "in_signal: read eof\n");
+ exit(1);
+ /* NOTREACHED */
+ default:
+ logmsg(LOG_ERR, "in_signal: read > 1\n");
+ exit(1);
+ }
+
+ if (debug & D_TIMER)
+ logmsg(LOG_DEBUG, "in_signal() got %d\n", buf);
+
+ switch (buf) {
+ case SIGALRM:
+ if (debug & D_TIMER) {
+ uint_t now = getcurrenttime();
+
+ logmsg(LOG_DEBUG, "in_signal(SIGALRM) delta %u\n",
+ now - timer_next);
+ }
+ timer_next = TIMER_INFINITY;
+ run_timeouts();
+ break;
+ case SIGHUP:
+ /* Re-read config file by exec'ing ourselves */
+ for (pi = phyints; pi != NULL; pi = next_pi) {
+ next_pi = pi->pi_next;
+ if (pi->pi_AdvSendAdvertisements)
+ check_to_advertise(pi, START_FINAL_ADV);
+
+ phyint_delete(pi);
+ }
+
+ /*
+ * Prevent fd leaks. Everything gets re-opened at start-up
+ * time. 0, 1, and 2 are closed and re-opened as
+ * /dev/null, so we'll leave those open.
+ */
+ closefrom(3);
+
+ logmsg(LOG_ERR, "SIGHUP: restart and reread config file\n");
+ (void) execv(argv0[0], argv0);
+ (void) unlink(PATH_PID);
+ _exit(0177);
+ /* NOTREACHED */
+ case SIGUSR1:
+ logmsg(LOG_DEBUG, "Printing configuration:\n");
+ phyint_print_all();
+ break;
+ case SIGINT:
+ case SIGTERM:
+ case SIGQUIT:
+ for (pi = phyints; pi != NULL; pi = next_pi) {
+ next_pi = pi->pi_next;
+ if (pi->pi_AdvSendAdvertisements)
+ check_to_advertise(pi, START_FINAL_ADV);
+
+ phyint_delete(pi);
+ }
+ (void) unlink(PATH_PID);
+ logmsg(LOG_ERR, "terminated\n");
+ exit(0);
+ /* NOTREACHED */
+ case 255:
+ /*
+ * Special "signal" from looback_ra_enqueue.
+ * Handle any queued loopback router advertisements.
+ */
+ loopback_ra_dequeue();
+ break;
+ default:
+ logmsg(LOG_ERR, "in_signal: unknown signal: %d\n", buf);
+ }
+}
+
+/*
+ * Create pipe for signal delivery and set up signal handlers.
+ */
+static void
+setup_eventpipe(void)
+{
+ int fds[2];
+ struct sigaction act;
+
+ if ((pipe(fds)) < 0) {
+ logperror("setup_eventpipe: pipe");
+ exit(1);
+ }
+ eventpipe_read = fds[0];
+ eventpipe_write = fds[1];
+ if (poll_add(eventpipe_read) == -1) {
+ exit(1);
+ }
+ act.sa_handler = sig_handler;
+ act.sa_flags = SA_RESTART;
+ (void) sigaction(SIGALRM, &act, NULL);
+
+ (void) sigset(SIGHUP, sig_handler);
+ (void) sigset(SIGUSR1, sig_handler);
+ (void) sigset(SIGTERM, sig_handler);
+ (void) sigset(SIGINT, sig_handler);
+ (void) sigset(SIGQUIT, sig_handler);
+}
+
+/*
+ * Create a routing socket for receiving RTM_IFINFO messages and initialize
+ * the routing socket message header and as much of the sockaddrs as possible.
+ */
+static int
+setup_rtsock(void)
+{
+ int s;
+ int ret;
+ char *cp;
+ struct sockaddr_in6 *sin6;
+
+ s = socket(PF_ROUTE, SOCK_RAW, AF_INET6);
+ if (s == -1) {
+ logperror("socket(PF_ROUTE)");
+ exit(1);
+ }
+ ret = fcntl(s, F_SETFL, O_NDELAY|O_NONBLOCK);
+ if (ret < 0) {
+ logperror("fcntl(O_NDELAY)");
+ exit(1);
+ }
+ if (poll_add(s) == -1) {
+ exit(1);
+ }
+
+ /*
+ * Allocate storage for the routing socket message.
+ */
+ rt_msg = (struct rt_msghdr *)malloc(NDP_RTM_MSGLEN);
+ if (rt_msg == NULL) {
+ logperror("malloc");
+ exit(1);
+ }
+
+ /*
+ * Initialize the routing socket message by zero-filling it and then
+ * setting the fields where are constant through the lifetime of the
+ * process.
+ */
+ bzero(rt_msg, NDP_RTM_MSGLEN);
+ rt_msg->rtm_msglen = NDP_RTM_MSGLEN;
+ rt_msg->rtm_version = RTM_VERSION;
+ rt_msg->rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK | RTA_IFP;
+ rt_msg->rtm_pid = getpid();
+ if (rt_msg->rtm_pid < 0) {
+ logperror("getpid");
+ exit(1);
+ }
+
+ /*
+ * The RTA_DST sockaddr does not change during the lifetime of the
+ * process so it can be completely initialized at this time.
+ */
+ cp = (char *)rt_msg + sizeof (struct rt_msghdr);
+ sin6 = (struct sockaddr_in6 *)cp;
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_addr = in6addr_any;
+
+ /*
+ * Initialize the constant portion of the RTA_GATEWAY sockaddr.
+ */
+ cp += sizeof (struct sockaddr_in6);
+ rta_gateway = (struct sockaddr_in6 *)cp;
+ rta_gateway->sin6_family = AF_INET6;
+
+ /*
+ * The RTA_NETMASK sockaddr does not change during the lifetime of the
+ * process so it can be completely initialized at this time.
+ */
+ cp += sizeof (struct sockaddr_in6);
+ sin6 = (struct sockaddr_in6 *)cp;
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_addr = in6addr_any;
+
+ /*
+ * Initialize the constant portion of the RTA_IFP sockaddr.
+ */
+ cp += sizeof (struct sockaddr_in6);
+ rta_ifp = (struct sockaddr_dl *)cp;
+ rta_ifp->sdl_family = AF_LINK;
+
+ return (s);
+}
+
+/*
+ * Retrieve one routing socket message. If RTM_IFINFO indicates
+ * new phyint do a full scan of the interfaces. If RTM_IFINFO
+ * indicates an existing phyint only scan that phyint and asociated
+ * prefixes.
+ */
+static void
+process_rtsock(int rtsock)
+{
+ int n;
+#define MSG_SIZE 2048/8
+ int64_t msg[MSG_SIZE];
+ struct rt_msghdr *rtm;
+ struct if_msghdr *ifm;
+ struct phyint *pi;
+ struct prefix *pr;
+ boolean_t need_initifs = _B_FALSE;
+ boolean_t need_ifscan = _B_FALSE;
+ int64_t ifscan_msg[10][MSG_SIZE];
+ int ifscan_index = 0;
+ int i;
+
+ /* Empty the rtsock and coealesce all the work that we have */
+ while (ifscan_index < 10) {
+ n = read(rtsock, msg, sizeof (msg));
+ if (n <= 0) {
+ /* No more messages */
+ break;
+ }
+ rtm = (struct rt_msghdr *)msg;
+ if (rtm->rtm_version != RTM_VERSION) {
+ logmsg(LOG_ERR,
+ "process_rtsock: version %d not understood\n",
+ rtm->rtm_version);
+ return;
+ }
+ switch (rtm->rtm_type) {
+ case RTM_NEWADDR:
+ case RTM_DELADDR:
+ /*
+ * Some logical interface has changed - have to scan
+ * everything to determine what actually changed.
+ */
+ if (debug & D_IFSCAN) {
+ logmsg(LOG_DEBUG, "process_rtsock: "
+ "message %d\n", rtm->rtm_type);
+ }
+ need_initifs = _B_TRUE;
+ break;
+ case RTM_IFINFO:
+ need_ifscan = _B_TRUE;
+ (void) memcpy(ifscan_msg[ifscan_index], rtm,
+ sizeof (msg));
+ ifscan_index++;
+ /* Handled below */
+ break;
+ default:
+ /* Not interesting */
+ break;
+ }
+ }
+ /*
+ * If we do full scan i.e initifs, we don't need to
+ * scan a particular interface as we should have
+ * done that as part of initifs.
+ */
+ if (need_initifs) {
+ initifs(_B_FALSE);
+ return;
+ }
+
+ if (!need_ifscan)
+ return;
+
+ for (i = 0; i < ifscan_index; i++) {
+ ifm = (struct if_msghdr *)ifscan_msg[i];
+ if (debug & D_IFSCAN)
+ logmsg(LOG_DEBUG, "process_rtsock: index %d\n",
+ ifm->ifm_index);
+
+ pi = phyint_lookup_on_index(ifm->ifm_index);
+ if (pi == NULL) {
+ /*
+ * A new physical interface. Do a full scan of the
+ * to catch any new logical interfaces.
+ */
+ initifs(_B_FALSE);
+ return;
+ }
+
+ if (ifm->ifm_flags != pi->pi_flags) {
+ if (debug & D_IFSCAN) {
+ logmsg(LOG_DEBUG, "process_rtsock: clr for "
+ "%s old flags 0x%x new flags 0x%x\n",
+ pi->pi_name, pi->pi_flags, ifm->ifm_flags);
+ }
+ }
+
+
+ /*
+ * Mark the interfaces so that we can find phyints and prefixes
+ * which have disappeared from the kernel.
+ * if_process will set pr_in_use when it finds the
+ * interface in the kernel.
+ * Before re-examining the state of the interfaces,
+ * PI_PRESENT should be cleared from pi_kernel_state.
+ */
+ pi->pi_kernel_state &= ~PI_PRESENT;
+ for (pr = pi->pi_prefix_list; pr != NULL; pr = pr->pr_next) {
+ pr->pr_in_use = _B_FALSE;
+ }
+
+ if (ifsock < 0) {
+ ifsock = socket(AF_INET6, SOCK_DGRAM, 0);
+ if (ifsock < 0) {
+ logperror("process_rtsock: socket");
+ return;
+ }
+ }
+ if_process(ifsock, pi->pi_name, _B_FALSE);
+ for (pr = pi->pi_prefix_list; pr != NULL; pr = pr->pr_next) {
+ if_process(ifsock, pr->pr_name, _B_FALSE);
+ }
+ /*
+ * If interface (still) exists in kernel, set
+ * pi_state to indicate that.
+ */
+ if (pi->pi_kernel_state & PI_PRESENT) {
+ pi->pi_state |= PI_PRESENT;
+ }
+ check_if_removed(pi);
+ if (show_ifs)
+ phyint_print_all();
+ }
+}
+
+/*
+ * Check whether the address formed by pr->pr_prefix and pi_token
+ * exists in the kernel. Cannot call SIOCTMYADDR/ONLINK as it
+ * does not check for down addresses. This function should not
+ * be called for onlink prefixes.
+ */
+static boolean_t
+is_address_present(struct phyint *pi, struct prefix *pr, uint64_t flags)
+{
+ int s;
+ in6_addr_t addr, *token;
+ int i;
+ int ret;
+ struct sockaddr_in6 sin6;
+
+ s = socket(AF_INET6, SOCK_DGRAM, 0);
+ if (s < 0) {
+ logperror("is_address_present: socket");
+ /*
+ * By returning B_TRUE, we make the caller delete
+ * the prefix from the internal table. In the worst
+ * case the next RA will create the prefix.
+ */
+ return (_B_TRUE);
+ }
+ if (flags & IFF_TEMPORARY)
+ token = &pi->pi_tmp_token;
+ else
+ token = &pi->pi_token;
+ for (i = 0; i < 16; i++) {
+ /*
+ * prefix_create ensures that pr_prefix has all-zero
+ * bits after prefixlen.
+ */
+ addr.s6_addr[i] = pr->pr_prefix.s6_addr[i] | token->s6_addr[i];
+ }
+ (void) memset(&sin6, 0, sizeof (struct sockaddr_in6));
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_addr = addr;
+ ret = bind(s, (struct sockaddr *)&sin6, sizeof (struct sockaddr_in6));
+ (void) close(s);
+ if (ret < 0 && errno == EADDRNOTAVAIL)
+ return (_B_FALSE);
+ else
+ return (_B_TRUE);
+}
+
+/*
+ * Look if the phyint or one of its prefixes have been removed from
+ * the kernel and take appropriate action.
+ * Uses {pi,pr}_in_use.
+ */
+static void
+check_if_removed(struct phyint *pi)
+{
+ struct prefix *pr;
+ struct prefix *next_pr;
+
+ /*
+ * Detect phyints that have been removed from the kernel.
+ * Since we can't recreate it here (would require ifconfig plumb
+ * logic) we just terminate use of that phyint.
+ */
+ if (!(pi->pi_kernel_state & PI_PRESENT) &&
+ (pi->pi_state & PI_PRESENT)) {
+ logmsg(LOG_ERR, "Interface %s has been removed from kernel. "
+ "in.ndpd will no longer use it\n", pi->pi_name);
+ /*
+ * Clear state so that should the phyint reappear
+ * we will start with initial advertisements or
+ * solicitations.
+ */
+ phyint_cleanup(pi);
+ }
+ /*
+ * Detect prefixes which are removed.
+ *
+ * We remove the prefix in all of the following cases :
+ *
+ * 1) Static prefixes are not the ones we create. So,
+ * just remove it from our tables.
+ *
+ * 2) On-link prefixes potentially move to a different
+ * phyint during failover. As it does not have
+ * an address, we can't use the logic in is_address_present
+ * to detect whether it is present in the kernel or not.
+ * Thus when it is manually removed we don't recreate it.
+ *
+ * 3) If there is a token mis-match and this prefix is not
+ * in the kernel, it means we don't need this prefix on
+ * this interface anymore. It must have been moved to a
+ * different interface by in.mpathd. This normally
+ * happens after a failover followed by a failback (or
+ * another failover) and we re-read the network
+ * configuration. For the failover from A to B, we would
+ * have created state on B about A's address, which will
+ * not be in use after the subsequent failback. So, we
+ * remove that prefix here.
+ *
+ * 4) If the physical interface is not present, then remove
+ * the prefix. In the cases where we are advertising
+ * prefixes, the state is kept in advertisement prefix and
+ * hence we can delete the prefix.
+ *
+ * 5) Similar to case (3), when we failover from A to B, the
+ * prefix in A will not be in use as it has been moved to B.
+ * We will delete it from our tables and recreate it when
+ * it fails back. is_address_present makes sure that the
+ * address is still valid in kernel.
+ *
+ * If none of the above is true, we recreate the prefix as it
+ * has been manually removed. We do it only when the interface
+ * is not FAILED or INACTIVE or OFFLINE.
+ */
+ for (pr = pi->pi_prefix_list; pr != NULL; pr = next_pr) {
+ next_pr = pr->pr_next;
+ if (!pr->pr_in_use) {
+ /* Clear PR_AUTO and PR_ONLINK */
+ pr->pr_kernel_state &= PR_STATIC;
+ if ((pr->pr_state & PR_STATIC) ||
+ !(pr->pr_state & PR_AUTO) ||
+ !(prefix_token_match(pi, pr, pr->pr_flags)) ||
+ (!(pi->pi_kernel_state & PI_PRESENT)) ||
+ (is_address_present(pi, pr, pr->pr_flags))) {
+ prefix_delete(pr);
+ } else if (!(pi->pi_flags &
+ (IFF_FAILED|IFF_INACTIVE|IFF_OFFLINE)) &&
+ pr->pr_state != pr->pr_kernel_state) {
+ pr->pr_name[0] = '\0';
+ logmsg(LOG_INFO, "Prefix manually removed "
+ "on %s - recreating it!\n",
+ pi->pi_name);
+ prefix_update_k(pr);
+ }
+ }
+ }
+}
+
+
+/*
+ * Queuing mechanism for router advertisements that are sent by in.ndpd
+ * and that also need to be processed by in.ndpd.
+ * Uses "signal number" 255 to indicate to the main poll loop
+ * that there is something to dequeue and send to incomining_ra().
+ */
+struct raq {
+ struct raq *raq_next;
+ struct phyint *raq_pi;
+ int raq_packetlen;
+ uchar_t *raq_packet;
+};
+static struct raq *raq_head = NULL;
+
+/*
+ * Allocate a struct raq and memory for the packet.
+ * Send signal 255 to have poll dequeue.
+ */
+static void
+loopback_ra_enqueue(struct phyint *pi, struct nd_router_advert *ra, int len)
+{
+ struct raq *raq;
+ struct raq **raqp;
+
+ if (no_loopback)
+ return;
+
+ if (debug & D_PKTOUT)
+ logmsg(LOG_DEBUG, "loopback_ra_enqueue for %s\n", pi->pi_name);
+
+ raq = calloc(sizeof (struct raq), 1);
+ if (raq == NULL) {
+ logmsg(LOG_ERR, "loopback_ra_enqueue: out of memory\n");
+ return;
+ }
+ raq->raq_packet = malloc(len);
+ if (raq->raq_packet == NULL) {
+ free(raq);
+ logmsg(LOG_ERR, "loopback_ra_enqueue: out of memory\n");
+ return;
+ }
+ bcopy(ra, raq->raq_packet, len);
+ raq->raq_packetlen = len;
+ raq->raq_pi = pi;
+
+ /* Tail insert */
+ raqp = &raq_head;
+ while (*raqp != NULL)
+ raqp = &((*raqp)->raq_next);
+ *raqp = raq;
+
+ /* Signal for poll loop */
+ sig_handler(255);
+}
+
+/*
+ * Dequeue and process all queued advertisements.
+ */
+static void
+loopback_ra_dequeue(void)
+{
+ struct sockaddr_in6 from = IN6ADDR_LOOPBACK_INIT;
+ struct raq *raq;
+
+ if (debug & D_PKTIN)
+ logmsg(LOG_DEBUG, "loopback_ra_dequeue()\n");
+
+ while ((raq = raq_head) != NULL) {
+ raq_head = raq->raq_next;
+ raq->raq_next = NULL;
+
+ if (debug & D_PKTIN) {
+ logmsg(LOG_DEBUG, "loopback_ra_dequeue for %s\n",
+ raq->raq_pi->pi_name);
+ }
+
+ incoming_ra(raq->raq_pi,
+ (struct nd_router_advert *)raq->raq_packet,
+ raq->raq_packetlen, &from, _B_TRUE);
+ free(raq->raq_packet);
+ free(raq);
+ }
+}
+
+
+static void
+usage(char *cmd)
+{
+ (void) fprintf(stderr,
+ "usage: %s [ -adt ] [-f <config file>]\n", cmd);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int i;
+ struct phyint *pi;
+ int c;
+ char *config_file = PATH_NDPD_CONF;
+ boolean_t file_required = _B_FALSE;
+
+ argv0 = argv;
+ srandom(gethostid());
+ (void) umask(0022);
+
+ while ((c = getopt(argc, argv, "adD:ntIf:")) != EOF) {
+ switch (c) {
+ case 'a':
+ /*
+ * The StatelessAddrConf variable in ndpd.conf, if
+ * present, will override this setting.
+ */
+ ifdefaults[I_StatelessAddrConf].cf_value = 0;
+ break;
+ case 'd':
+ debug = D_ALL;
+ break;
+ case 'D':
+ i = strtol((char *)optarg, NULL, 0);
+ if (i == 0) {
+ (void) fprintf(stderr, "Bad debug flags: %s\n",
+ (char *)optarg);
+ exit(1);
+ }
+ debug |= i;
+ break;
+ case 'n':
+ no_loopback = 1;
+ break;
+ case 'I':
+ show_ifs = 1;
+ break;
+ case 't':
+ debug |= D_PKTIN | D_PKTOUT | D_PKTBAD;
+ break;
+ case 'f':
+ config_file = (char *)optarg;
+ file_required = _B_TRUE;
+ break;
+ case '?':
+ usage(argv[0]);
+ exit(1);
+ }
+ }
+
+ if (parse_config(config_file, file_required) == -1)
+ exit(2);
+
+ if (show_ifs)
+ phyint_print_all();
+
+ if (debug == 0) {
+ initlog();
+ }
+
+ setup_eventpipe();
+ rtsock = setup_rtsock();
+ timer_init();
+ initifs(_B_TRUE);
+
+ check_daemonize();
+
+ for (;;) {
+ if (poll(pollfds, pollfd_num, -1) < 0) {
+ if (errno == EINTR)
+ continue;
+ logperror("main: poll");
+ exit(1);
+ }
+ for (i = 0; i < pollfd_num; i++) {
+ if (!(pollfds[i].revents & POLLIN))
+ continue;
+ if (pollfds[i].fd == eventpipe_read) {
+ in_signal(eventpipe_read);
+ break;
+ }
+ if (pollfds[i].fd == rtsock) {
+ process_rtsock(rtsock);
+ break;
+ }
+ /*
+ * Run timer routine to advance clock if more than
+ * half a second since the clock was advanced.
+ * This limits CPU usage under severe packet
+ * arrival rates but it creates a slight inaccuracy
+ * in the timer mechanism.
+ */
+ conditional_run_timeouts(500U);
+ for (pi = phyints; pi != NULL; pi = pi->pi_next) {
+ if (pollfds[i].fd == pi->pi_sock) {
+ in_data(pi);
+ break;
+ }
+ }
+ }
+ }
+ /* NOTREACHED */
+ return (0);
+}
+
+/*
+ * LOGGER
+ */
+
+static boolean_t logging = _B_FALSE;
+
+static void
+initlog(void)
+{
+ logging = _B_TRUE;
+ openlog("in.ndpd", LOG_PID | LOG_CONS, LOG_DAEMON);
+}
+
+/* Print the date/time without a trailing carridge return */
+static void
+fprintdate(FILE *file)
+{
+ char buf[BUFSIZ];
+ struct tm tms;
+ time_t now;
+
+ now = time(NULL);
+ (void) localtime_r(&now, &tms);
+ (void) strftime(buf, sizeof (buf), "%h %d %X", &tms);
+ (void) fprintf(file, "%s ", buf);
+}
+
+/* PRINTFLIKE1 */
+void
+logmsg(int level, char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+
+ if (logging) {
+ vsyslog(level, fmt, ap);
+ } else {
+ fprintdate(stderr);
+ (void) vfprintf(stderr, fmt, ap);
+ }
+ va_end(ap);
+}
+
+void
+logperror(char *str)
+{
+ if (logging) {
+ syslog(LOG_ERR, "%s: %m\n", str);
+ } else {
+ fprintdate(stderr);
+ (void) fprintf(stderr, "%s: %s\n", str, strerror(errno));
+ }
+}
+
+void
+logperror_pi(struct phyint *pi, char *str)
+{
+ if (logging) {
+ syslog(LOG_ERR, "%s (interface %s): %m\n",
+ str, pi->pi_name);
+ } else {
+ fprintdate(stderr);
+ (void) fprintf(stderr, "%s (interface %s): %s\n",
+ str, pi->pi_name, strerror(errno));
+ }
+}
+
+void
+logperror_pr(struct prefix *pr, char *str)
+{
+ if (logging) {
+ syslog(LOG_ERR, "%s (prefix %s if %s): %m\n",
+ str, pr->pr_name, pr->pr_physical->pi_name);
+ } else {
+ fprintdate(stderr);
+ (void) fprintf(stderr, "%s (prefix %s if %s): %s\n",
+ str, pr->pr_name, pr->pr_physical->pi_name,
+ strerror(errno));
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/ndp.c b/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/ndp.c
new file mode 100644
index 0000000000..1e6d24a413
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/ndp.c
@@ -0,0 +1,1354 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "defs.h"
+#include "tables.h"
+
+#include <sys/sysmacros.h>
+
+static boolean_t verify_opt_len(struct nd_opt_hdr *opt, int optlen,
+ struct phyint *pi, struct sockaddr_in6 *from);
+
+static void incoming_rs(struct phyint *pi, struct nd_router_solicit *rs,
+ int len, struct sockaddr_in6 *from);
+
+void incoming_ra(struct phyint *pi, struct nd_router_advert *ra,
+ int len, struct sockaddr_in6 *from, boolean_t loopback);
+static void incoming_prefix_opt(struct phyint *pi, uchar_t *opt,
+ struct sockaddr_in6 *from, boolean_t loopback);
+static void incoming_prefix_onlink(struct phyint *pi, uchar_t *opt,
+ struct sockaddr_in6 *from, boolean_t loopback);
+void incoming_prefix_onlink_process(struct prefix *pr,
+ uchar_t *opt);
+static boolean_t incoming_prefix_addrconf(struct phyint *pi,
+ uchar_t *opt, struct sockaddr_in6 *from,
+ boolean_t loopback);
+boolean_t incoming_prefix_addrconf_process(struct phyint *pi,
+ struct prefix *pr, uchar_t *opt,
+ struct sockaddr_in6 *from, boolean_t loopback,
+ boolean_t new_prefix);
+static void incoming_mtu_opt(struct phyint *pi, uchar_t *opt,
+ struct sockaddr_in6 *from);
+static void incoming_lla_opt(struct phyint *pi, uchar_t *opt,
+ struct sockaddr_in6 *from, int isrouter);
+
+static void verify_ra_consistency(struct phyint *pi,
+ struct nd_router_advert *ra,
+ int len, struct sockaddr_in6 *from);
+static void verify_prefix_opt(struct phyint *pi, uchar_t *opt,
+ char *frombuf);
+static void verify_mtu_opt(struct phyint *pi, uchar_t *opt,
+ char *frombuf);
+
+static uint_t ra_flags; /* Global to detect when to trigger DHCP */
+
+/*
+ * Return a pointer to the specified option buffer.
+ * If not found return NULL.
+ */
+static void *
+find_ancillary(struct msghdr *msg, int cmsg_type)
+{
+ struct cmsghdr *cmsg;
+
+ for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL;
+ cmsg = CMSG_NXTHDR(msg, cmsg)) {
+ if (cmsg->cmsg_level == IPPROTO_IPV6 &&
+ cmsg->cmsg_type == cmsg_type) {
+ return (CMSG_DATA(cmsg));
+ }
+ }
+ return (NULL);
+}
+
+void
+in_data(struct phyint *pi)
+{
+ struct sockaddr_in6 from;
+ struct icmp6_hdr *icmp;
+ struct nd_router_solicit *rs;
+ struct nd_router_advert *ra;
+ static uint64_t in_packet[(IP_MAXPACKET + 1)/8];
+ static uint64_t ancillary_data[(IP_MAXPACKET + 1)/8];
+ int len;
+ char abuf[INET6_ADDRSTRLEN];
+ const char *msgbuf;
+ struct msghdr msg;
+ struct iovec iov;
+ uchar_t *opt;
+ uint_t hoplimit;
+
+ iov.iov_base = (char *)in_packet;
+ iov.iov_len = sizeof (in_packet);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_name = (struct sockaddr *)&from;
+ msg.msg_namelen = sizeof (from);
+ msg.msg_control = ancillary_data;
+ msg.msg_controllen = sizeof (ancillary_data);
+
+ if ((len = recvmsg(pi->pi_sock, &msg, 0)) < 0) {
+ logperror_pi(pi, "in_data: recvfrom");
+ return;
+ }
+ if (len == 0)
+ return;
+
+ if (inet_ntop(AF_INET6, (void *)&from.sin6_addr,
+ abuf, sizeof (abuf)) == NULL)
+ msgbuf = "Unspecified Router";
+ else
+ msgbuf = abuf;
+
+ /* Ignore packets > 64k or control buffers that don't fit */
+ if (msg.msg_flags & (MSG_TRUNC|MSG_CTRUNC)) {
+ if (debug & D_PKTBAD) {
+ logmsg(LOG_DEBUG, "Truncated message: msg_flags 0x%x "
+ "from %s\n", msg.msg_flags, msgbuf);
+ }
+ return;
+ }
+
+ icmp = (struct icmp6_hdr *)in_packet;
+
+ if (len < ICMP6_MINLEN) {
+ logmsg(LOG_INFO, "Too short ICMP packet: %d bytes "
+ "from %s on %s\n",
+ len, msgbuf, pi->pi_name);
+ return;
+ }
+
+ opt = find_ancillary(&msg, IPV6_HOPLIMIT);
+ if (opt == NULL) {
+ /* Unknown hoplimit - must drop */
+ logmsg(LOG_INFO, "Unknown hop limit from %s on %s\n",
+ msgbuf, pi->pi_name);
+ return;
+ }
+ hoplimit = *(uint_t *)opt;
+ opt = find_ancillary(&msg, IPV6_RTHDR);
+ if (opt != NULL) {
+ /* Can't allow routing headers in ND messages */
+ logmsg(LOG_INFO, "ND message with routing header "
+ "from %s on %s\n",
+ msgbuf, pi->pi_name);
+ return;
+ }
+ switch (icmp->icmp6_type) {
+ case ND_ROUTER_SOLICIT:
+ if (!pi->pi_AdvSendAdvertisements)
+ return;
+ if (pi->pi_flags & IFF_NORTEXCH) {
+ if (debug & D_PKTIN) {
+ logmsg(LOG_DEBUG, "Ignore received RS packet "
+ "on %s (no route exchange on interface)\n",
+ pi->pi_name);
+ }
+ return;
+ }
+
+ /*
+ * Assumes that the kernel has verified the AH (if present)
+ * and the ICMP checksum.
+ */
+ if (hoplimit != IPV6_MAX_HOPS) {
+ logmsg(LOG_INFO, "RS hop limit: %d from %s on %s\n",
+ hoplimit, msgbuf, pi->pi_name);
+ return;
+ }
+
+ if (icmp->icmp6_code != 0) {
+ logmsg(LOG_INFO, "RS code: %d from %s on %s\n",
+ icmp->icmp6_code, msgbuf, pi->pi_name);
+ return;
+ }
+
+ if (len < sizeof (struct nd_router_solicit)) {
+ logmsg(LOG_INFO, "RS too short: %d bytes "
+ "from %s on %s\n",
+ len, msgbuf, pi->pi_name);
+ return;
+ }
+ rs = (struct nd_router_solicit *)icmp;
+ if (len > sizeof (struct nd_router_solicit)) {
+ if (!verify_opt_len((struct nd_opt_hdr *)&rs[1],
+ len - sizeof (struct nd_router_solicit), pi, &from))
+ return;
+ }
+ if (debug & D_PKTIN) {
+ print_route_sol("Received valid solicit from ", pi,
+ rs, len, &from);
+ }
+ incoming_rs(pi, rs, len, &from);
+ break;
+
+ case ND_ROUTER_ADVERT:
+ if (IN6_IS_ADDR_UNSPECIFIED(&from.sin6_addr)) {
+ /*
+ * Router advt. must have address!
+ * Logging the news and returning.
+ */
+ logmsg(LOG_DEBUG,
+ "Router's address unspecified in advertisement\n");
+ return;
+ }
+ if (pi->pi_flags & IFF_NORTEXCH) {
+ if (debug & D_PKTIN) {
+ logmsg(LOG_DEBUG, "Ignore received RA packet "
+ "on %s (no route exchange on interface)\n",
+ pi->pi_name);
+ }
+ return;
+ }
+
+ /*
+ * Assumes that the kernel has verified the AH (if present)
+ * and the ICMP checksum.
+ */
+ if (!IN6_IS_ADDR_LINKLOCAL(&from.sin6_addr)) {
+ logmsg(LOG_INFO, "RA from %s - not link local on %s\n",
+ msgbuf, pi->pi_name);
+ return;
+ }
+
+ if (hoplimit != IPV6_MAX_HOPS) {
+ logmsg(LOG_INFO, "RA hop limit: %d from %s on %s\n",
+ hoplimit, msgbuf, pi->pi_name);
+ return;
+ }
+
+ if (icmp->icmp6_code != 0) {
+ logmsg(LOG_INFO, "RA code: %d from %s on %s\n",
+ icmp->icmp6_code, msgbuf, pi->pi_name);
+ return;
+ }
+
+ if (len < sizeof (struct nd_router_advert)) {
+ logmsg(LOG_INFO, "RA too short: %d bytes "
+ "from %s on %s\n",
+ len, msgbuf, pi->pi_name);
+ return;
+ }
+ ra = (struct nd_router_advert *)icmp;
+ if (len > sizeof (struct nd_router_advert)) {
+ if (!verify_opt_len((struct nd_opt_hdr *)&ra[1],
+ len - sizeof (struct nd_router_advert), pi, &from))
+ return;
+ }
+ if (debug & D_PKTIN) {
+ print_route_adv("Received valid advert from ", pi,
+ ra, len, &from);
+ }
+ if (pi->pi_AdvSendAdvertisements)
+ verify_ra_consistency(pi, ra, len, &from);
+ else
+ incoming_ra(pi, ra, len, &from, _B_FALSE);
+ break;
+ }
+}
+
+/*
+ * Process a received router solicitation.
+ * Check for source link-layer address option and check if it
+ * is time to advertise.
+ */
+static void
+incoming_rs(struct phyint *pi, struct nd_router_solicit *rs, int len,
+ struct sockaddr_in6 *from)
+{
+ struct nd_opt_hdr *opt;
+ int optlen;
+
+ /* Process any options */
+ len -= sizeof (struct nd_router_solicit);
+ opt = (struct nd_opt_hdr *)&rs[1];
+ while (len >= sizeof (struct nd_opt_hdr)) {
+ optlen = opt->nd_opt_len * 8;
+ switch (opt->nd_opt_type) {
+ case ND_OPT_SOURCE_LINKADDR:
+ incoming_lla_opt(pi, (uchar_t *)opt,
+ from, NDF_ISROUTER_OFF);
+ break;
+ default:
+ break;
+ }
+ opt = (struct nd_opt_hdr *)((char *)opt + optlen);
+ len -= optlen;
+ }
+ /* Simple algorithm: treat unicast and multicast RSs the same */
+ check_to_advertise(pi, RECEIVED_SOLICIT);
+}
+
+/*
+ * Process a received router advertisement.
+ * Called both when packets arrive as well as when we send RAs.
+ * In the latter case 'loopback' is set.
+ */
+void
+incoming_ra(struct phyint *pi, struct nd_router_advert *ra, int len,
+ struct sockaddr_in6 *from, boolean_t loopback)
+{
+ struct nd_opt_hdr *opt;
+ int optlen;
+ struct lifreq lifr;
+ boolean_t set_needed = _B_FALSE;
+ struct router *dr;
+ uint16_t router_lifetime;
+ uint_t reachable, retrans;
+ boolean_t reachable_time_changed = _B_FALSE;
+
+ if (no_loopback && loopback)
+ return;
+
+ /*
+ * If the interface is FAILED or INACTIVE or OFFLINE, don't
+ * create any addresses on them. in.mpathd assumes that no new
+ * addresses will appear on these. This implies that we
+ * won't create any new prefixes advertised by the router
+ * on FAILED/INACTIVE/OFFLINE interfaces. When the state changes,
+ * the next RA will create the prefix on this interface.
+ */
+ if (pi->pi_flags & (IFF_FAILED|IFF_INACTIVE|IFF_OFFLINE))
+ return;
+
+ (void) strncpy(lifr.lifr_name, pi->pi_name, sizeof (lifr.lifr_name));
+ lifr.lifr_name[sizeof (lifr.lifr_name) - 1] = '\0';
+ if (ioctl(pi->pi_sock, SIOCGLIFLNKINFO, (char *)&lifr) < 0) {
+ if (errno == ENXIO)
+ return;
+ logperror_pi(pi, "incoming_ra: SIOCGLIFLNKINFO");
+ return;
+ }
+ if (ra->nd_ra_curhoplimit != pi->pi_CurHopLimit) {
+ pi->pi_CurHopLimit = ra->nd_ra_curhoplimit;
+
+ lifr.lifr_ifinfo.lir_maxhops = pi->pi_CurHopLimit;
+ set_needed = _B_TRUE;
+ }
+
+ reachable = ntohl(ra->nd_ra_reachable);
+ if (reachable != 0 &&
+ reachable != pi->pi_BaseReachableTime) {
+ pi->pi_BaseReachableTime = reachable;
+ reachable_time_changed = _B_TRUE;
+ }
+
+ if (pi->pi_reach_time_since_random < MIN_REACH_RANDOM_INTERVAL ||
+ reachable_time_changed) {
+ phyint_reach_random(pi, _B_FALSE);
+ set_needed = _B_TRUE;
+ }
+ lifr.lifr_ifinfo.lir_reachtime = pi->pi_ReachableTime;
+
+ retrans = ntohl(ra->nd_ra_retransmit);
+ if (retrans != 0 &&
+ pi->pi_RetransTimer != retrans) {
+ pi->pi_RetransTimer = retrans;
+ lifr.lifr_ifinfo.lir_reachretrans = pi->pi_RetransTimer;
+ set_needed = _B_TRUE;
+ }
+
+ if (set_needed) {
+ if (ioctl(pi->pi_sock, SIOCSLIFLNKINFO, (char *)&lifr) < 0) {
+ logperror_pi(pi, "incoming_ra: SIOCSLIFLNKINFO");
+ return;
+ }
+ }
+
+ if ((ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED) &&
+ !(ra_flags & ND_RA_FLAG_MANAGED)) {
+ ra_flags |= ND_RA_FLAG_MANAGED;
+ /* TODO trigger dhcpv6 */
+ logmsg(LOG_INFO, "incoming_ra: trigger dhcp MANAGED\n");
+ }
+ if ((ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER) &&
+ !(ra_flags & ND_RA_FLAG_OTHER)) {
+ ra_flags |= ND_RA_FLAG_OTHER;
+ if (!(ra_flags & ND_RA_FLAG_MANAGED)) {
+ /* TODO trigger dhcpv6 for non-address info */
+ logmsg(LOG_INFO, "incoming_ra: trigger dhcp OTHER\n");
+ }
+ }
+ /* Skip default router code if sent from ourselves */
+ if (!loopback) {
+ /* Find and update or add default router in list */
+ dr = router_lookup(pi, from->sin6_addr);
+ router_lifetime = ntohs(ra->nd_ra_router_lifetime);
+ if (dr == NULL) {
+ if (router_lifetime != 0) {
+ dr = router_create(pi, from->sin6_addr,
+ MILLISEC * router_lifetime);
+ timer_schedule(dr->dr_lifetime);
+ }
+ } else {
+ dr->dr_lifetime = MILLISEC * router_lifetime;
+ if (dr->dr_lifetime != 0)
+ timer_schedule(dr->dr_lifetime);
+ if ((dr->dr_lifetime != 0 && !dr->dr_inkernel) ||
+ (dr->dr_lifetime == 0 && dr->dr_inkernel))
+ router_update_k(dr);
+ }
+ }
+ /* Process any options */
+ len -= sizeof (struct nd_router_advert);
+ opt = (struct nd_opt_hdr *)&ra[1];
+ while (len >= sizeof (struct nd_opt_hdr)) {
+ optlen = opt->nd_opt_len * 8;
+ switch (opt->nd_opt_type) {
+ case ND_OPT_PREFIX_INFORMATION:
+ incoming_prefix_opt(pi, (uchar_t *)opt, from,
+ loopback);
+ break;
+ case ND_OPT_MTU:
+ incoming_mtu_opt(pi, (uchar_t *)opt, from);
+ break;
+ case ND_OPT_SOURCE_LINKADDR:
+ /* skip lla option if sent from ourselves! */
+ if (!loopback) {
+ incoming_lla_opt(pi, (uchar_t *)opt,
+ from, NDF_ISROUTER_ON);
+ }
+ break;
+ default:
+ break;
+ }
+ opt = (struct nd_opt_hdr *)((char *)opt + optlen);
+ len -= optlen;
+ }
+ /* Stop sending solicitations */
+ check_to_solicit(pi, SOLICIT_DONE);
+}
+
+/*
+ * Process a received prefix option.
+ * Unless addrconf is turned off we process both the addrconf and the
+ * onlink aspects of the prefix option.
+ *
+ * Note that when a flag (onlink or auto) is turned off we do nothing -
+ * the prefix will time out.
+ */
+static void
+incoming_prefix_opt(struct phyint *pi, uchar_t *opt,
+ struct sockaddr_in6 *from, boolean_t loopback)
+{
+ struct nd_opt_prefix_info *po = (struct nd_opt_prefix_info *)opt;
+ boolean_t good_prefix = _B_TRUE;
+
+ if (8 * po->nd_opt_pi_len != sizeof (*po)) {
+ char abuf[INET6_ADDRSTRLEN];
+
+ (void) inet_ntop(AF_INET6, (void *)&from->sin6_addr,
+ abuf, sizeof (abuf));
+ logmsg(LOG_INFO, "prefix option from %s on %s wrong size "
+ "(%d bytes)\n",
+ abuf, pi->pi_name,
+ 8 * (int)po->nd_opt_pi_len);
+ return;
+ }
+ if (IN6_IS_ADDR_LINKLOCAL(&po->nd_opt_pi_prefix)) {
+ char abuf[INET6_ADDRSTRLEN];
+
+ (void) inet_ntop(AF_INET6, (void *)&from->sin6_addr,
+ abuf, sizeof (abuf));
+ logmsg(LOG_INFO, "RA from %s on %s contains link-local prefix "
+ "- ignored\n",
+ abuf, pi->pi_name);
+ return;
+ }
+ if ((po->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_AUTO) &&
+ pi->pi_StatelessAddrConf) {
+ good_prefix = incoming_prefix_addrconf(pi, opt, from, loopback);
+ }
+ if ((po->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK) &&
+ good_prefix) {
+ incoming_prefix_onlink(pi, opt, from, loopback);
+ }
+}
+
+/*
+ * Process prefix options with the onlink flag set.
+ *
+ * If there are no routers ndpd will add an onlink
+ * default route which will allow communication
+ * between neighbors.
+ *
+ * This function needs to loop to find the same prefix multiple times
+ * as if a failover happened earlier, the addresses belonging to
+ * a different interface may be found here on this interface.
+ */
+/* ARGSUSED2 */
+static void
+incoming_prefix_onlink(struct phyint *pi, uchar_t *opt,
+ struct sockaddr_in6 *from, boolean_t loopback)
+{
+ struct nd_opt_prefix_info *po = (struct nd_opt_prefix_info *)opt;
+ int plen;
+ struct prefix *pr;
+ uint32_t validtime; /* Without 2 hour rule */
+ boolean_t found_one = _B_FALSE;
+
+ plen = po->nd_opt_pi_prefix_len;
+ for (pr = pi->pi_prefix_list; pr != NULL; pr = pr->pr_next) {
+ if (pr->pr_prefix_len == plen &&
+ prefix_equal(po->nd_opt_pi_prefix, pr->pr_prefix, plen)) {
+ /* Exclude static prefixes */
+ if (pr->pr_state & PR_STATIC)
+ continue;
+ found_one = _B_TRUE;
+ incoming_prefix_onlink_process(pr, opt);
+ }
+ }
+
+ validtime = ntohl(po->nd_opt_pi_valid_time);
+ /*
+ * If we have found a matching prefix already or validtime
+ * is zero, we have nothing to do.
+ */
+ if (validtime == 0 || found_one)
+ return;
+ pr = prefix_create(pi, po->nd_opt_pi_prefix, plen, 0);
+ if (pr == NULL)
+ return;
+ incoming_prefix_onlink_process(pr, opt);
+}
+
+void
+incoming_prefix_onlink_process(struct prefix *pr, uchar_t *opt)
+{
+ struct nd_opt_prefix_info *po = (struct nd_opt_prefix_info *)opt;
+ uint32_t validtime; /* Without 2 hour rule */
+ char abuf[INET6_ADDRSTRLEN];
+
+ validtime = ntohl(po->nd_opt_pi_valid_time);
+ if (validtime != 0)
+ pr->pr_state |= PR_ONLINK;
+ else
+ pr->pr_state &= ~PR_ONLINK;
+
+ /*
+ * Convert from seconds to milliseconds avoiding overflow.
+ * If the lifetime in the packet is e.g. PREFIX_INFINITY - 1
+ * (4 billion seconds - about 130 years) we will in fact time
+ * out the prefix after 4 billion milliseconds - 46 days).
+ * Thus the longest lifetime (apart from infinity) is 46 days.
+ * Note that this ensures that PREFIX_INFINITY still means "forever".
+ */
+ if (pr->pr_flags & IFF_TEMPORARY) {
+ pr->pr_OnLinkLifetime = pr->pr_ValidLifetime;
+ } else {
+ if (validtime >= PREFIX_INFINITY / MILLISEC)
+ pr->pr_OnLinkLifetime = PREFIX_INFINITY - 1;
+ else
+ pr->pr_OnLinkLifetime = validtime * MILLISEC;
+ }
+ pr->pr_OnLinkFlag = _B_TRUE;
+ if (debug & (D_PREFIX|D_TMP)) {
+ logmsg(LOG_DEBUG, "incoming_prefix_onlink_process(%s, %s/%u) "
+ "onlink %u state 0x%x, kstate 0x%x\n",
+ pr->pr_name, inet_ntop(AF_INET6, (void *)&pr->pr_prefix,
+ abuf, sizeof (abuf)), pr->pr_prefix_len,
+ pr->pr_OnLinkLifetime, pr->pr_state, pr->pr_kernel_state);
+ }
+
+ if (pr->pr_kernel_state != pr->pr_state) {
+ prefix_update_k(pr);
+ }
+
+ if (pr->pr_OnLinkLifetime != 0)
+ timer_schedule(pr->pr_OnLinkLifetime);
+}
+
+/*
+ * Process prefix options with the autonomous flag set.
+ * Returns false if this prefix results in a bad address (duplicate)
+ * This function needs to loop to find the same prefix multiple times
+ * as if a failover happened earlier, the addresses belonging to
+ * a different interface may be found here on this interface.
+ */
+/* ARGSUSED2 */
+static boolean_t
+incoming_prefix_addrconf(struct phyint *pi, uchar_t *opt,
+ struct sockaddr_in6 *from, boolean_t loopback)
+{
+ struct nd_opt_prefix_info *po = (struct nd_opt_prefix_info *)opt;
+ int plen;
+ struct prefix *pr;
+ uint32_t validtime, preftime; /* In seconds */
+ char abuf[INET6_ADDRSTRLEN];
+ char pbuf[INET6_ADDRSTRLEN];
+ boolean_t found_pub = _B_FALSE;
+ boolean_t found_tmp = _B_FALSE;
+ boolean_t ret;
+
+ validtime = ntohl(po->nd_opt_pi_valid_time);
+ preftime = ntohl(po->nd_opt_pi_preferred_time);
+ plen = po->nd_opt_pi_prefix_len;
+
+ /* Sanity checks */
+ if (validtime < preftime) {
+ (void) inet_ntop(AF_INET6, (void *)&from->sin6_addr,
+ abuf, sizeof (abuf));
+ (void) inet_ntop(AF_INET6,
+ (void *)&po->nd_opt_pi_prefix,
+ pbuf, sizeof (pbuf));
+ logmsg(LOG_WARNING, "prefix option %s/%u from %s on %s: "
+ "valid %u < pref %u ignored\n",
+ pbuf, plen, abuf, pi->pi_name,
+ validtime, preftime);
+ return (_B_FALSE);
+ }
+
+ for (pr = pi->pi_prefix_list; pr != NULL; pr = pr->pr_next) {
+ if (pr->pr_prefix_len == plen &&
+ prefix_equal(po->nd_opt_pi_prefix, pr->pr_prefix, plen)) {
+
+ /* Exclude static prefixes */
+ if (pr->pr_state & PR_STATIC)
+ continue;
+ if (pr->pr_flags & IFF_TEMPORARY) {
+ /*
+ * If this address is deprecated and its token
+ * doesn't match the current tmp token, we want
+ * to create a new address with the current
+ * token. So don't count this addr as a match.
+ */
+ if (!((pr->pr_flags & IFF_DEPRECATED) &&
+ !token_equal(pi->pi_tmp_token,
+ pr->pr_address, TMP_TOKEN_BITS)))
+ found_tmp = _B_TRUE;
+ } else {
+ found_pub = _B_TRUE;
+ }
+ (void) incoming_prefix_addrconf_process(pi, pr, opt,
+ from, loopback, _B_FALSE);
+ }
+ }
+
+ /*
+ * If we have found a matching prefix (for public and, if temp addrs
+ * are enabled, for temporary) already or validtime is zero, we have
+ * nothing to do.
+ */
+ if (validtime == 0 ||
+ (found_pub && (!pi->pi_TmpAddrsEnabled || found_tmp)))
+ return (_B_TRUE);
+
+ if (!found_pub) {
+ pr = prefix_create(pi, po->nd_opt_pi_prefix, plen, 0);
+ if (pr == NULL)
+ return (_B_TRUE);
+ ret = incoming_prefix_addrconf_process(pi, pr, opt, from,
+ loopback, _B_TRUE);
+ }
+ /*
+ * if processing of the public address failed,
+ * don't bother with the temporary address.
+ */
+ if (ret == _B_FALSE)
+ return (_B_FALSE);
+
+ if (pi->pi_TmpAddrsEnabled && !found_tmp) {
+ pr = prefix_create(pi, po->nd_opt_pi_prefix, plen,
+ IFF_TEMPORARY);
+ if (pr == NULL)
+ return (_B_TRUE);
+ ret = incoming_prefix_addrconf_process(pi, pr, opt, from,
+ loopback, _B_TRUE);
+ }
+
+ return (ret);
+}
+
+boolean_t
+incoming_prefix_addrconf_process(struct phyint *pi, struct prefix *pr,
+ uchar_t *opt, struct sockaddr_in6 *from, boolean_t loopback,
+ boolean_t new_prefix)
+{
+ struct nd_opt_prefix_info *po = (struct nd_opt_prefix_info *)opt;
+ char abuf[INET6_ADDRSTRLEN];
+ char pbuf[INET6_ADDRSTRLEN];
+ uint32_t validtime, preftime; /* In seconds */
+ uint32_t recorded_validtime; /* In seconds */
+ int plen, dadfails = 0;
+ struct prefix *other_pr;
+
+ validtime = ntohl(po->nd_opt_pi_valid_time);
+ preftime = ntohl(po->nd_opt_pi_preferred_time);
+ plen = po->nd_opt_pi_prefix_len;
+ if (!new_prefix) {
+ /*
+ * Check 2 hour rule on valid lifetime.
+ * Follows: RFC 2462
+ * If we advertised this prefix ourselves we skip
+ * these checks. They are also skipped if we did not
+ * previously do addrconf on this prefix.
+ */
+ recorded_validtime = pr->pr_ValidLifetime / MILLISEC;
+
+ if (loopback || !(pr->pr_state & PR_AUTO) ||
+ validtime >= MIN_VALID_LIFETIME ||
+ /* LINTED - statement has no consequent */
+ validtime >= recorded_validtime) {
+ /* OK */
+ } else if (recorded_validtime < MIN_VALID_LIFETIME &&
+ validtime < recorded_validtime) {
+ /* Ignore the prefix */
+ (void) inet_ntop(AF_INET6,
+ (void *)&from->sin6_addr,
+ abuf, sizeof (abuf));
+ (void) inet_ntop(AF_INET6,
+ (void *)&po->nd_opt_pi_prefix,
+ pbuf, sizeof (pbuf));
+ logmsg(LOG_INFO, "prefix option %s/%u from %s on %s: "
+ "too short valid lifetime %u stored %u "
+ "- ignored\n",
+ pbuf, plen, abuf, pi->pi_name,
+ validtime, recorded_validtime);
+ return (_B_TRUE);
+ } else {
+ /*
+ * If the router clock runs slower than the
+ * host by 1 second over 2 hours then this
+ * test will set the lifetime back to 2 hours
+ * once i.e. a lifetime decrementing in
+ * realtime might cause the prefix to live an
+ * extra 2 hours on the host.
+ */
+ (void) inet_ntop(AF_INET6,
+ (void *)&from->sin6_addr,
+ abuf, sizeof (abuf));
+ (void) inet_ntop(AF_INET6,
+ (void *)&po->nd_opt_pi_prefix,
+ pbuf, sizeof (pbuf));
+ logmsg(LOG_INFO, "prefix option %s/%u from %s on %s: "
+ "valid time %u stored %u rounded up "
+ "to %u\n",
+ pbuf, plen, abuf, pi->pi_name,
+ validtime, recorded_validtime,
+ MIN_VALID_LIFETIME);
+ validtime = MIN_VALID_LIFETIME;
+ }
+ }
+
+ /*
+ * For RFC3041 addresses, need to take token lifetime
+ * into account, too.
+ */
+ if (pr->pr_flags & IFF_TEMPORARY) {
+ uint_t cur_tpreftime =
+ pi->pi_TmpPreferredLifetime - pi->pi_TmpDesyncFactor;
+
+ if (new_prefix) {
+ validtime = MIN(validtime, pi->pi_TmpValidLifetime);
+ preftime = MIN(preftime, cur_tpreftime);
+ } else {
+ uint_t cur_vexp, cur_pexp, curtime;
+ curtime = getcurrenttime() / MILLISEC;
+
+ cur_vexp = pr->pr_CreateTime + pi->pi_TmpValidLifetime;
+ cur_pexp = pr->pr_CreateTime + cur_tpreftime;
+ if (curtime > cur_vexp)
+ validtime = 0;
+ else if ((curtime + validtime) > cur_vexp)
+ validtime = cur_vexp - curtime;
+ /*
+ * If this is an existing address which was deprecated
+ * because of a bad token, we don't want to update its
+ * preferred lifetime!
+ */
+ if ((pr->pr_PreferredLifetime == 0) &&
+ !token_equal(pr->pr_address, pi->pi_tmp_token,
+ TMP_TOKEN_BITS))
+ preftime = 0;
+ else if (curtime > cur_pexp)
+ preftime = 0;
+ else if ((curtime + preftime) > cur_pexp)
+ preftime = cur_pexp - curtime;
+ }
+ if ((preftime != 0) && (preftime <= pi->pi_TmpRegenAdvance)) {
+ (void) inet_ntop(AF_INET6,
+ (void *)&from->sin6_addr,
+ abuf, sizeof (abuf));
+ (void) inet_ntop(AF_INET6,
+ (void *)&po->nd_opt_pi_prefix,
+ pbuf, sizeof (pbuf));
+ logmsg(LOG_WARNING, "prefix opt %s/%u from %s on %s: "
+ "preferred lifetime(%d) <= TmpRegenAdvance(%d)\n",
+ pbuf, plen, abuf, pi->pi_name, preftime,
+ pi->pi_TmpRegenAdvance);
+ if (new_prefix)
+ prefix_delete(pr);
+ return (_B_TRUE);
+ }
+ }
+ if (debug & D_TMP)
+ logmsg(LOG_DEBUG, "calculated lifetimes(%s, 0x%llx): v %d, "
+ "p %d\n", pr->pr_name, pr->pr_flags, validtime, preftime);
+
+ if (!(pr->pr_state & PR_AUTO)) {
+ int i, tokenlen;
+ in6_addr_t *token;
+ /*
+ * Form a new local address if the lengths match.
+ */
+ if (pr->pr_flags && IFF_TEMPORARY) {
+RETRY_TOKEN:
+ if (IN6_IS_ADDR_UNSPECIFIED(&pi->pi_tmp_token)) {
+ if (!tmptoken_create(pi)) {
+ prefix_delete(pr);
+ return (_B_TRUE);
+ }
+ }
+ tokenlen = TMP_TOKEN_BITS;
+ token = &pi->pi_tmp_token;
+ } else {
+ tokenlen = pi->pi_token_length;
+ token = &pi->pi_token;
+ }
+ if (pr->pr_prefix_len + tokenlen != IPV6_ABITS) {
+ (void) inet_ntop(AF_INET6,
+ (void *)&from->sin6_addr,
+ abuf, sizeof (abuf));
+ (void) inet_ntop(AF_INET6,
+ (void *)&po->nd_opt_pi_prefix,
+ pbuf, sizeof (pbuf));
+ logmsg(LOG_INFO, "prefix option %s/%u from %s on %s: "
+ "mismatched length %d token length %d\n",
+ pbuf, plen, abuf, pi->pi_name,
+ pr->pr_prefix_len, tokenlen);
+ return (_B_TRUE);
+ }
+ for (i = 0; i < 16; i++) {
+ /*
+ * prefix_create ensures that pr_prefix has all-zero
+ * bits after prefixlen.
+ */
+ pr->pr_address.s6_addr[i] = pr->pr_prefix.s6_addr[i] |
+ token->s6_addr[i];
+ }
+ /*
+ * Check if any other physical interface has the same
+ * address configured already
+ */
+ if ((other_pr = prefix_lookup_addr_match(pr)) != NULL) {
+ /*
+ * Delete this prefix structure as kernel
+ * does not allow duplicated addresses
+ */
+
+ logmsg(LOG_ERR, "incoming_prefix_addrconf_process: "
+ "Duplicate prefix %s received on interface %s\n",
+ inet_ntop(AF_INET6,
+ (void *)&po->nd_opt_pi_prefix, abuf,
+ sizeof (abuf)), pi->pi_name);
+ logmsg(LOG_ERR, "incoming_prefix_addrconf_process: "
+ "Prefix already exists in interface %s\n",
+ other_pr->pr_physical->pi_name);
+ if (new_prefix) {
+ prefix_delete(pr);
+ return (_B_FALSE);
+ }
+ /* Ignore for addrconf purposes */
+ validtime = preftime = 0;
+ }
+ if ((pr->pr_flags & IFF_TEMPORARY) && new_prefix) {
+ struct sockaddr_in6 sin6;
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_addr = pr->pr_address;
+ if (do_dad(pi->pi_name, &sin6) != 0) {
+ /* DAD failed, need a new token */
+ dadfails++;
+ logmsg(LOG_WARNING,
+ "incoming_prefix_addrconf_process: "
+ "deprecating temporary token %s\n",
+ inet_ntop(AF_INET6,
+ (void *)&pi->pi_tmp_token, abuf,
+ sizeof (abuf)));
+ tmptoken_delete(pi);
+ if (dadfails == MAX_DAD_FAILURES) {
+ logmsg(LOG_ERR, "Too many DAD "
+ "failures; disabling temporary "
+ "addresses on %s\n", pi->pi_name);
+ pi->pi_TmpAddrsEnabled = 0;
+ prefix_delete(pr);
+ return (_B_TRUE);
+ }
+ goto RETRY_TOKEN;
+ }
+ pr->pr_CreateTime = getcurrenttime() / MILLISEC;
+ if (debug & D_TMP)
+ logmsg(LOG_DEBUG,
+ "created tmp addr(%s v %d p %d)\n",
+ pr->pr_name, validtime, preftime);
+ }
+ }
+
+ if (validtime != 0)
+ pr->pr_state |= PR_AUTO;
+ else
+ pr->pr_state &= ~(PR_AUTO|PR_DEPRECATED);
+ if (preftime != 0 || !(pr->pr_state & PR_AUTO))
+ pr->pr_state &= ~PR_DEPRECATED;
+ else
+ pr->pr_state |= PR_DEPRECATED;
+
+ /*
+ * Convert from seconds to milliseconds avoiding overflow.
+ * If the lifetime in the packet is e.g. PREFIX_INFINITY - 1
+ * (4 billion seconds - about 130 years) we will in fact time
+ * out the prefix after 4 billion milliseconds - 46 days).
+ * Thus the longest lifetime (apart from infinity) is 46 days.
+ * Note that this ensures that PREFIX_INFINITY still means "forever".
+ */
+ if (validtime >= PREFIX_INFINITY / MILLISEC)
+ pr->pr_ValidLifetime = PREFIX_INFINITY - 1;
+ else
+ pr->pr_ValidLifetime = validtime * MILLISEC;
+ if (preftime >= PREFIX_INFINITY / MILLISEC)
+ pr->pr_PreferredLifetime = PREFIX_INFINITY - 1;
+ else
+ pr->pr_PreferredLifetime = preftime * MILLISEC;
+ pr->pr_AutonomousFlag = _B_TRUE;
+
+ if (debug & D_PREFIX) {
+ logmsg(LOG_DEBUG, "incoming_prefix_addrconf_process(%s, %s/%u) "
+ "valid %u pref %u\n",
+ pr->pr_physical->pi_name,
+ inet_ntop(AF_INET6, (void *)&pr->pr_prefix,
+ abuf, sizeof (abuf)), pr->pr_prefix_len,
+ pr->pr_ValidLifetime, pr->pr_PreferredLifetime);
+ }
+
+ if (pr->pr_state & PR_AUTO) {
+ /* Take the min of the two timeouts by calling it twice */
+ if (pr->pr_ValidLifetime != 0)
+ timer_schedule(pr->pr_ValidLifetime);
+ if (pr->pr_PreferredLifetime != 0)
+ timer_schedule(pr->pr_PreferredLifetime);
+ }
+ if (pr->pr_kernel_state != pr->pr_state) {
+ /* Log a message when an addrconf prefix goes away */
+ if ((pr->pr_kernel_state & PR_AUTO) &&
+ !(pr->pr_state & PR_AUTO)) {
+ char abuf[INET6_ADDRSTRLEN];
+
+ logmsg(LOG_WARNING, "Address removed due to zero "
+ "valid lifetime %s\n",
+ inet_ntop(AF_INET6, (void *)&pr->pr_address,
+ abuf, sizeof (abuf)));
+ }
+ prefix_update_k(pr);
+ }
+ return (_B_TRUE);
+}
+
+/*
+ * Process an MTU option received in a router advertisement.
+ */
+static void
+incoming_mtu_opt(struct phyint *pi, uchar_t *opt,
+ struct sockaddr_in6 *from)
+{
+ struct nd_opt_mtu *mo = (struct nd_opt_mtu *)opt;
+ struct lifreq lifr;
+ uint32_t mtu;
+
+ if (8 * mo->nd_opt_mtu_len != sizeof (*mo)) {
+ char abuf[INET6_ADDRSTRLEN];
+
+ (void) inet_ntop(AF_INET6, (void *)&from->sin6_addr,
+ abuf, sizeof (abuf));
+ logmsg(LOG_INFO, "mtu option from %s on %s wrong size "
+ "(%d bytes)\n",
+ abuf, pi->pi_name,
+ 8 * (int)mo->nd_opt_mtu_len);
+ return;
+ }
+ mtu = ntohl(mo->nd_opt_mtu_mtu);
+ if (pi->pi_LinkMTU == mtu)
+ return; /* No change */
+ if (mtu > pi->pi_mtu) {
+ /* Can't exceed physical MTU */
+ char abuf[INET6_ADDRSTRLEN];
+
+ (void) inet_ntop(AF_INET6, (void *)&from->sin6_addr,
+ abuf, sizeof (abuf));
+ logmsg(LOG_INFO, "mtu option from %s on %s too large "
+ "MTU %d - %d\n", abuf, pi->pi_name, mtu, pi->pi_mtu);
+ return;
+ }
+ if (mtu < IPV6_MIN_MTU) {
+ char abuf[INET6_ADDRSTRLEN];
+
+ (void) inet_ntop(AF_INET6, (void *)&from->sin6_addr,
+ abuf, sizeof (abuf));
+ logmsg(LOG_INFO, "mtu option from %s on %s too small "
+ "MTU (%d)\n", abuf, pi->pi_name, mtu);
+ return;
+ }
+
+ pi->pi_LinkMTU = mtu;
+ (void) strncpy(lifr.lifr_name, pi->pi_name, sizeof (lifr.lifr_name));
+ lifr.lifr_name[sizeof (lifr.lifr_name) - 1] = '\0';
+ if (ioctl(pi->pi_sock, SIOCGLIFLNKINFO, (char *)&lifr) < 0) {
+ logperror_pi(pi, "incoming_mtu_opt: SIOCGLIFLNKINFO");
+ return;
+ }
+ lifr.lifr_ifinfo.lir_maxmtu = pi->pi_LinkMTU;
+ if (ioctl(pi->pi_sock, SIOCSLIFLNKINFO, (char *)&lifr) < 0) {
+ logperror_pi(pi, "incoming_mtu_opt: SIOCSLIFLNKINFO");
+ return;
+ }
+}
+
+/*
+ * Process a source link-layer address option received in a router
+ * advertisement or solicitation.
+ */
+static void
+incoming_lla_opt(struct phyint *pi, uchar_t *opt,
+ struct sockaddr_in6 *from, int isrouter)
+{
+ struct nd_opt_lla *lo = (struct nd_opt_lla *)opt;
+ struct lifreq lifr;
+ struct sockaddr_in6 *sin6;
+ int max_content_len;
+
+ if (pi->pi_hdw_addr_len == 0)
+ return;
+
+ /*
+ * Can't remove padding since it is link type specific.
+ * However, we check against the length of our link-layer
+ * address.
+ * Note: assumes that all links have a fixed lengh address.
+ */
+ max_content_len = lo->nd_opt_lla_len * 8 - sizeof (struct nd_opt_hdr);
+ if (max_content_len < pi->pi_hdw_addr_len ||
+ (max_content_len >= 8 &&
+ max_content_len - 7 > pi->pi_hdw_addr_len)) {
+ char abuf[INET6_ADDRSTRLEN];
+
+ (void) inet_ntop(AF_INET6, (void *)&from->sin6_addr,
+ abuf, sizeof (abuf));
+ logmsg(LOG_INFO, "lla option from %s on %s too long with bad "
+ "physaddr length (%d vs. %d bytes)\n",
+ abuf, pi->pi_name,
+ max_content_len, pi->pi_hdw_addr_len);
+ return;
+ }
+
+ lifr.lifr_nd.lnr_hdw_len = pi->pi_hdw_addr_len;
+ bcopy((char *)lo->nd_opt_lla_hdw_addr,
+ (char *)lifr.lifr_nd.lnr_hdw_addr,
+ lifr.lifr_nd.lnr_hdw_len);
+
+ sin6 = (struct sockaddr_in6 *)&lifr.lifr_nd.lnr_addr;
+ bzero(sin6, sizeof (struct sockaddr_in6));
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_addr = from->sin6_addr;
+
+ /*
+ * Set IsRouter flag if RA; clear if RS.
+ */
+ lifr.lifr_nd.lnr_state_create = ND_STALE;
+ lifr.lifr_nd.lnr_state_same_lla = ND_UNCHANGED;
+ lifr.lifr_nd.lnr_state_diff_lla = ND_STALE;
+ lifr.lifr_nd.lnr_flags = isrouter;
+ (void) strncpy(lifr.lifr_name, pi->pi_name, sizeof (lifr.lifr_name));
+ lifr.lifr_name[sizeof (lifr.lifr_name) - 1] = '\0';
+ if (ioctl(pi->pi_sock, SIOCLIFSETND, (char *)&lifr) < 0) {
+ logperror_pi(pi, "incoming_lla_opt: SIOCLIFSETND");
+ return;
+ }
+}
+
+/*
+ * Verify the content of the received router advertisement against our
+ * own configuration as specified in RFC 2461.
+ */
+static void
+verify_ra_consistency(struct phyint *pi, struct nd_router_advert *ra, int len,
+ struct sockaddr_in6 *from)
+{
+ char frombuf[INET6_ADDRSTRLEN];
+ struct nd_opt_hdr *opt;
+ int optlen;
+ uint_t reachable, retrans;
+ boolean_t pktflag, myflag;
+
+ (void) inet_ntop(AF_INET6, (void *)&from->sin6_addr,
+ frombuf, sizeof (frombuf));
+
+ if (ra->nd_ra_curhoplimit != 0 &&
+ pi->pi_AdvCurHopLimit != 0 &&
+ ra->nd_ra_curhoplimit != pi->pi_AdvCurHopLimit) {
+ logmsg(LOG_INFO, "RA from %s on %s inconsistent cur hop "
+ "limit:\n\treceived %d configuration %d\n",
+ frombuf, pi->pi_name,
+ ra->nd_ra_curhoplimit, pi->pi_AdvCurHopLimit);
+ }
+
+ reachable = ntohl(ra->nd_ra_reachable);
+ if (reachable != 0 && pi->pi_AdvReachableTime != 0 &&
+ reachable != pi->pi_AdvReachableTime) {
+ logmsg(LOG_INFO, "RA from %s on %s inconsistent reachable "
+ "time:\n\treceived %d configuration %d\n",
+ frombuf, pi->pi_name,
+ reachable, pi->pi_AdvReachableTime);
+ }
+
+ retrans = ntohl(ra->nd_ra_retransmit);
+ if (retrans != 0 && pi->pi_AdvRetransTimer != 0 &&
+ retrans != pi->pi_AdvRetransTimer) {
+ logmsg(LOG_INFO, "RA from %s on %s inconsistent retransmit "
+ "timer:\n\treceived %d configuration %d\n",
+ frombuf, pi->pi_name,
+ retrans, pi->pi_AdvRetransTimer);
+ }
+
+ pktflag = ((ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED) != 0);
+ myflag = (pi->pi_AdvManagedFlag != 0);
+ if (pktflag != myflag) {
+ logmsg(LOG_INFO, "RA from %s on %s inconsistent managed "
+ "flag:\n\treceived %s configuration %s\n",
+ frombuf, pi->pi_name,
+ (pktflag ? "ON" : "OFF"),
+ (myflag ? "ON" : "OFF"));
+ }
+ pktflag = ((ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER) != 0);
+ myflag = (pi->pi_AdvOtherConfigFlag != 0);
+ if (pktflag != myflag) {
+ logmsg(LOG_INFO, "RA from %s on %s inconsistent other config "
+ "flag:\n\treceived %s configuration %s\n",
+ frombuf, pi->pi_name,
+ (pktflag ? "ON" : "OFF"),
+ (myflag ? "ON" : "OFF"));
+ }
+
+ /* Process any options */
+ len -= sizeof (struct nd_router_advert);
+ opt = (struct nd_opt_hdr *)&ra[1];
+ while (len >= sizeof (struct nd_opt_hdr)) {
+ optlen = opt->nd_opt_len * 8;
+ switch (opt->nd_opt_type) {
+ case ND_OPT_PREFIX_INFORMATION:
+ verify_prefix_opt(pi, (uchar_t *)opt, frombuf);
+ break;
+ case ND_OPT_MTU:
+ verify_mtu_opt(pi, (uchar_t *)opt, frombuf);
+ break;
+ default:
+ break;
+ }
+ opt = (struct nd_opt_hdr *)((char *)opt + optlen);
+ len -= optlen;
+ }
+}
+
+/*
+ * Verify that the lifetimes and onlink/auto flags are consistent
+ * with our settings.
+ */
+static void
+verify_prefix_opt(struct phyint *pi, uchar_t *opt, char *frombuf)
+{
+ struct nd_opt_prefix_info *po = (struct nd_opt_prefix_info *)opt;
+ int plen;
+ struct adv_prefix *adv_pr;
+ uint32_t validtime, preftime;
+ char prefixbuf[INET6_ADDRSTRLEN];
+ int pktflag, myflag;
+
+ if (8 * po->nd_opt_pi_len != sizeof (*po)) {
+ logmsg(LOG_INFO, "RA prefix option from %s on %s wrong size "
+ "(%d bytes)\n",
+ frombuf, pi->pi_name,
+ 8 * (int)po->nd_opt_pi_len);
+ return;
+ }
+ if (IN6_IS_ADDR_LINKLOCAL(&po->nd_opt_pi_prefix)) {
+ logmsg(LOG_INFO, "RA from %s on %s contains link-local "
+ "prefix - ignored\n",
+ frombuf, pi->pi_name);
+ return;
+ }
+ plen = po->nd_opt_pi_prefix_len;
+ adv_pr = adv_prefix_lookup(pi, po->nd_opt_pi_prefix, plen);
+ if (adv_pr == NULL)
+ return;
+
+ /* Ignore prefixes which we do not advertise */
+ if (!adv_pr->adv_pr_AdvAutonomousFlag && !adv_pr->adv_pr_AdvOnLinkFlag)
+ return;
+ (void) inet_ntop(AF_INET6, (void *)&adv_pr->adv_pr_prefix,
+ prefixbuf, sizeof (prefixbuf));
+ pktflag = ((po->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_AUTO) != 0);
+ myflag = (adv_pr->adv_pr_AdvAutonomousFlag != 0);
+ if (pktflag != myflag) {
+ logmsg(LOG_INFO,
+ "RA from %s on %s inconsistent autonumous flag for \n\t"
+ "prefix %s/%u: received %s configuration %s\n",
+ frombuf, pi->pi_name, prefixbuf, adv_pr->adv_pr_prefix_len,
+ (pktflag ? "ON" : "OFF"),
+ (myflag ? "ON" : "OFF"));
+ }
+
+ pktflag = ((po->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK) != 0);
+ myflag = (adv_pr->adv_pr_AdvOnLinkFlag != 0);
+ if (pktflag != myflag) {
+ logmsg(LOG_INFO, "RA from %s on %s inconsistent on link flag "
+ "for \n\tprefix %s/%u: received %s configuration %s\n",
+ frombuf, pi->pi_name, prefixbuf, adv_pr->adv_pr_prefix_len,
+ (pktflag ? "ON" : "OFF"),
+ (myflag ? "ON" : "OFF"));
+ }
+ validtime = ntohl(po->nd_opt_pi_valid_time);
+ preftime = ntohl(po->nd_opt_pi_preferred_time);
+
+ /*
+ * Take into account variation for lifetimes decrementing
+ * in real time. Allow +/- 10 percent and +/- 10 seconds.
+ */
+#define LOWER_LIMIT(val) ((val) - (val)/10 - 10)
+#define UPPER_LIMIT(val) ((val) + (val)/10 + 10)
+ if (adv_pr->adv_pr_AdvValidRealTime) {
+ if (adv_pr->adv_pr_AdvValidExpiration > 0 &&
+ (validtime <
+ LOWER_LIMIT(adv_pr->adv_pr_AdvValidExpiration) ||
+ validtime >
+ UPPER_LIMIT(adv_pr->adv_pr_AdvValidExpiration))) {
+ logmsg(LOG_INFO, "RA from %s on %s inconsistent valid "
+ "lifetime for\n\tprefix %s/%u: received %d "
+ "configuration %d\n",
+ frombuf, pi->pi_name, prefixbuf,
+ adv_pr->adv_pr_prefix_len,
+ validtime, adv_pr->adv_pr_AdvValidExpiration);
+ }
+ } else {
+ if (validtime != adv_pr->adv_pr_AdvValidLifetime) {
+ logmsg(LOG_INFO, "RA from %s on %s inconsistent valid "
+ "lifetime for\n\tprefix %s/%u: received %d "
+ "configuration %d\n",
+ frombuf, pi->pi_name, prefixbuf,
+ adv_pr->adv_pr_prefix_len,
+ validtime, adv_pr->adv_pr_AdvValidLifetime);
+ }
+ }
+
+ if (adv_pr->adv_pr_AdvPreferredRealTime) {
+ if (adv_pr->adv_pr_AdvPreferredExpiration > 0 &&
+ (preftime <
+ LOWER_LIMIT(adv_pr->adv_pr_AdvPreferredExpiration) ||
+ preftime >
+ UPPER_LIMIT(adv_pr->adv_pr_AdvPreferredExpiration))) {
+ logmsg(LOG_INFO, "RA from %s on %s inconsistent "
+ "preferred lifetime for\n\tprefix %s/%u: "
+ "received %d configuration %d\n",
+ frombuf, pi->pi_name, prefixbuf,
+ adv_pr->adv_pr_prefix_len,
+ preftime, adv_pr->adv_pr_AdvPreferredExpiration);
+ }
+ } else {
+ if (preftime != adv_pr->adv_pr_AdvPreferredLifetime) {
+ logmsg(LOG_INFO, "RA from %s on %s inconsistent "
+ "preferred lifetime for\n\tprefix %s/%u: "
+ "received %d configuration %d\n",
+ frombuf, pi->pi_name, prefixbuf,
+ adv_pr->adv_pr_prefix_len,
+ preftime, adv_pr->adv_pr_AdvPreferredLifetime);
+ }
+ }
+}
+
+/*
+ * Verify the received MTU against our own configuration.
+ */
+static void
+verify_mtu_opt(struct phyint *pi, uchar_t *opt, char *frombuf)
+{
+ struct nd_opt_mtu *mo = (struct nd_opt_mtu *)opt;
+ uint32_t mtu;
+
+ if (8 * mo->nd_opt_mtu_len != sizeof (*mo)) {
+ logmsg(LOG_INFO, "mtu option from %s on %s wrong size "
+ "(%d bytes)\n",
+ frombuf, pi->pi_name,
+ 8 * (int)mo->nd_opt_mtu_len);
+ return;
+ }
+ mtu = ntohl(mo->nd_opt_mtu_mtu);
+ if (pi->pi_AdvLinkMTU != 0 &&
+ pi->pi_AdvLinkMTU != mtu) {
+ logmsg(LOG_INFO, "RA from %s on %s inconsistent MTU: "
+ "received %d configuration %d\n",
+ frombuf, pi->pi_name,
+ mtu, pi->pi_AdvLinkMTU);
+ }
+}
+
+/*
+ * Verify that all options have a non-zero length and that
+ * the options fit within the total length of the packet (optlen).
+ */
+static boolean_t
+verify_opt_len(struct nd_opt_hdr *opt, int optlen,
+ struct phyint *pi, struct sockaddr_in6 *from)
+{
+ while (optlen > 0) {
+ if (opt->nd_opt_len == 0) {
+ char abuf[INET6_ADDRSTRLEN];
+
+ (void) inet_ntop(AF_INET6,
+ (void *)&from->sin6_addr,
+ abuf, sizeof (abuf));
+
+ logmsg(LOG_INFO, "Zero length option type 0x%x "
+ "from %s on %s\n",
+ opt->nd_opt_type, abuf, pi->pi_name);
+ return (_B_FALSE);
+ }
+ optlen -= 8 * opt->nd_opt_len;
+ if (optlen < 0) {
+ char abuf[INET6_ADDRSTRLEN];
+
+ (void) inet_ntop(AF_INET6,
+ (void *)&from->sin6_addr,
+ abuf, sizeof (abuf));
+
+ logmsg(LOG_INFO, "Too large option: type 0x%x len %u "
+ "from %s on %s\n",
+ opt->nd_opt_type, opt->nd_opt_len,
+ abuf, pi->pi_name);
+ return (_B_FALSE);
+ }
+ opt = (struct nd_opt_hdr *)((char *)opt +
+ 8 * opt->nd_opt_len);
+ }
+ return (_B_TRUE);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/tables.c b/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/tables.c
new file mode 100644
index 0000000000..4e7c2bf3b3
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/tables.c
@@ -0,0 +1,2399 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "defs.h"
+#include "tables.h"
+
+#include <time.h>
+#include <inet/ip6.h>
+
+struct phyint *phyints = NULL;
+
+static void phyint_print(struct phyint *pi);
+static void phyint_insert(struct phyint *pi);
+
+static boolean_t tmptoken_isvalid(struct in6_addr *token);
+
+static void prefix_print(struct prefix *pr);
+static void prefix_insert(struct phyint *pi, struct prefix *pr);
+static char *prefix_print_state(int state, char *buf, int buflen);
+static void prefix_set(struct in6_addr *prefix, struct in6_addr addr,
+ int bits);
+
+static void adv_prefix_print(struct adv_prefix *adv_pr);
+static void adv_prefix_insert(struct phyint *pi, struct adv_prefix *adv_pr);
+static void adv_prefix_delete(struct adv_prefix *adv_pr);
+
+static void router_print(struct router *dr);
+static void router_insert(struct phyint *pi, struct router *dr);
+static void router_delete(struct router *dr);
+static void router_add_k(struct router *dr);
+static void router_delete_k(struct router *dr);
+static void router_delete_onlink(struct phyint *pi);
+
+static int rtmseq; /* rtm_seq sequence number */
+
+/* 1 week in ms */
+#define NDP_PREFIX_DEFAULT_LIFETIME (7*24*60*60*1000)
+struct phyint *
+phyint_lookup(char *name)
+{
+ struct phyint *pi;
+
+ if (debug & D_PHYINT)
+ logmsg(LOG_DEBUG, "phyint_lookup(%s)\n", name);
+
+ for (pi = phyints; pi != NULL; pi = pi->pi_next) {
+ if (strcmp(pi->pi_name, name) == 0)
+ break;
+ }
+ return (pi);
+}
+
+struct phyint *
+phyint_lookup_on_index(uint_t ifindex)
+{
+ struct phyint *pi;
+
+ if (debug & D_PHYINT)
+ logmsg(LOG_DEBUG, "phyint_lookup_on_index(%d)\n", ifindex);
+
+ for (pi = phyints; pi != NULL; pi = pi->pi_next) {
+ if (pi->pi_index == ifindex)
+ break;
+ }
+ return (pi);
+}
+
+struct phyint *
+phyint_create(char *name)
+{
+ struct phyint *pi;
+ int i;
+
+ if (debug & D_PHYINT)
+ logmsg(LOG_DEBUG, "phyint_create(%s)\n", name);
+
+ pi = (struct phyint *)calloc(sizeof (struct phyint), 1);
+ if (pi == NULL) {
+ logmsg(LOG_ERR, "phyint_create: out of memory\n");
+ return (NULL);
+ }
+ (void) strncpy(pi->pi_name, name, sizeof (pi->pi_name));
+ pi->pi_name[sizeof (pi->pi_name) - 1] = '\0';
+
+ /*
+ * Copy the defaults from the defaults array.
+ * Do not copy the cf_notdefault fields since these have not
+ * been explicitly set for the phyint.
+ */
+ for (i = 0; i < I_IFSIZE; i++)
+ pi->pi_config[i].cf_value = ifdefaults[i].cf_value;
+
+ /*
+ * TmpDesyncFactor is used to desynchronize temporary token
+ * generation among systems; the actual preferred lifetime value
+ * of a temporary address will be (TmpPreferredLifetime -
+ * TmpDesyncFactor). It's a random value, with a user-configurable
+ * maximum value. The value is constant throughout the lifetime
+ * of the in.ndpd process, but can change if the daemon is restarted,
+ * per RFC3041.
+ */
+ if (pi->pi_TmpMaxDesyncFactor != 0) {
+ time_t seed = time(NULL);
+ srand((uint_t)seed);
+ pi->pi_TmpDesyncFactor = rand() % pi->pi_TmpMaxDesyncFactor;
+ /* we actually want [1,max], not [0,(max-1)] */
+ pi->pi_TmpDesyncFactor++;
+ }
+ pi->pi_TmpRegenCountdown = TIMER_INFINITY;
+
+ pi->pi_sock = -1;
+ if (phyint_init_from_k(pi) == -1) {
+ if (pi->pi_group_name != NULL)
+ free(pi->pi_group_name);
+ free(pi);
+ return (NULL);
+ }
+ phyint_insert(pi);
+ if (pi->pi_sock != -1) {
+ if (poll_add(pi->pi_sock) == -1) {
+ phyint_delete(pi);
+ return (NULL);
+ }
+ }
+ return (pi);
+}
+
+/* Insert in linked list */
+static void
+phyint_insert(struct phyint *pi)
+{
+ /* Insert in list */
+ pi->pi_next = phyints;
+ pi->pi_prev = NULL;
+ if (phyints)
+ phyints->pi_prev = pi;
+ phyints = pi;
+}
+
+/*
+ * Initialize both the phyint data structure and the pi_sock for
+ * sending and receving on the interface.
+ * Extract information from the kernel (if present) and set pi_kernel_state.
+ */
+int
+phyint_init_from_k(struct phyint *pi)
+{
+ struct ipv6_mreq v6mcastr;
+ struct lifreq lifr;
+ int fd;
+ boolean_t newsock;
+ uint_t ttl;
+ struct sockaddr_in6 *sin6;
+
+ if (debug & D_PHYINT)
+ logmsg(LOG_DEBUG, "phyint_init_from_k(%s)\n", pi->pi_name);
+
+start_over:
+
+ if (pi->pi_sock < 0) {
+ pi->pi_sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
+ if (pi->pi_sock < 0) {
+ logperror_pi(pi, "phyint_init_from_k: socket");
+ return (-1);
+ }
+ newsock = _B_TRUE;
+ } else {
+ newsock = _B_FALSE;
+ }
+ fd = pi->pi_sock;
+
+ (void) strncpy(lifr.lifr_name, pi->pi_name, sizeof (lifr.lifr_name));
+ lifr.lifr_name[sizeof (lifr.lifr_name) - 1] = '\0';
+ if (ioctl(fd, SIOCGLIFINDEX, (char *)&lifr) < 0) {
+ if (errno == ENXIO) {
+ if (newsock) {
+ (void) close(pi->pi_sock);
+ pi->pi_sock = -1;
+ }
+ if (debug & D_PHYINT) {
+ logmsg(LOG_DEBUG, "phyint_init_from_k(%s): "
+ "not exist\n", pi->pi_name);
+ }
+ return (0);
+ }
+ logperror_pi(pi, "phyint_init_from_k: SIOCGLIFINDEX");
+ goto error;
+ }
+
+ if (!newsock && (pi->pi_index != lifr.lifr_index)) {
+ /*
+ * Interface has been re-plumbed, lets open a new socket.
+ * This situation can occur if plumb/unplumb are happening
+ * quite frequently.
+ */
+
+ phyint_cleanup(pi);
+ goto start_over;
+ }
+
+ pi->pi_index = lifr.lifr_index;
+
+ if (ioctl(fd, SIOCGLIFFLAGS, (char *)&lifr) < 0) {
+ logperror_pi(pi, "phyint_init_from_k: ioctl (get flags)");
+ goto error;
+ }
+ pi->pi_flags = lifr.lifr_flags;
+
+ /*
+ * If the link local interface is not up yet or it's IFF_UP
+ * and the flag is set to IFF_NOLOCAL as Duplicate Address
+ * Detection is in progress.
+ * IFF_NOLOCAL is "normal" on other prefixes.
+ */
+
+ if (!(pi->pi_flags & IFF_UP) || (pi->pi_flags & IFF_NOLOCAL)) {
+ if (newsock) {
+ (void) close(pi->pi_sock);
+ pi->pi_sock = -1;
+ }
+ if (debug & D_PHYINT) {
+ logmsg(LOG_DEBUG, "phyint_init_from_k(%s): "
+ "not IFF_UP\n", pi->pi_name);
+ }
+ return (0);
+ }
+ pi->pi_kernel_state |= PI_PRESENT;
+
+ bzero(lifr.lifr_groupname, sizeof (lifr.lifr_groupname));
+ if (ioctl(fd, SIOCGLIFGROUPNAME, (caddr_t)&lifr) < 0) {
+ logperror_pi(pi, "phyint_init_from_k: ioctl (get group name)");
+ goto error;
+ }
+
+ if (lifr.lifr_groupname != NULL && strlen(lifr.lifr_groupname) != 0) {
+ if (pi->pi_group_name == NULL) {
+ pi->pi_group_name = malloc(
+ sizeof (lifr.lifr_groupname));
+ if (pi->pi_group_name == NULL) {
+ logperror_pi(pi, "phyint_init_from_k:"
+ " malloc(group name)");
+ goto error;
+ }
+ }
+ /*
+ * Size of the group name can only be LIFNAMESZ -1 characters
+ * which is ensured by kernel. Thus, we don't need strncpy.
+ */
+ (void) strncpy(pi->pi_group_name, lifr.lifr_groupname,
+ sizeof (lifr.lifr_name));
+ pi->pi_group_name[sizeof (pi->pi_group_name) - 1] = '\0';
+ } else if (pi->pi_group_name != NULL) {
+ free(pi->pi_group_name);
+ pi->pi_group_name = NULL;
+ }
+
+ if (ioctl(fd, SIOCGLIFMTU, (caddr_t)&lifr) < 0) {
+ logperror_pi(pi, "phyint_init_from_k: ioctl (get mtu)");
+ goto error;
+ }
+ pi->pi_mtu = lifr.lifr_mtu;
+
+ if (ioctl(fd, SIOCGLIFADDR, (char *)&lifr) < 0) {
+ logperror_pi(pi, "phyint_init_from_k: SIOCGLIFADDR");
+ goto error;
+ }
+ sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
+ pi->pi_ifaddr = sin6->sin6_addr;
+
+ if (ioctl(fd, SIOCGLIFTOKEN, (char *)&lifr) < 0) {
+ logperror_pi(pi, "phyint_init_from_k: SIOCGLIFTOKEN");
+ goto error;
+ }
+ /* Ignore interface if the token is all zeros */
+ sin6 = (struct sockaddr_in6 *)&lifr.lifr_token;
+ if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
+ logmsg(LOG_ERR, "ignoring interface %s: zero token\n",
+ pi->pi_name);
+ goto error;
+ }
+ pi->pi_token = sin6->sin6_addr;
+ pi->pi_token_length = lifr.lifr_addrlen;
+
+ /*
+ * Guess a remote token for POINTOPOINT by looking at
+ * the link-local destination address.
+ */
+ if (pi->pi_flags & IFF_POINTOPOINT) {
+ if (ioctl(fd, SIOCGLIFDSTADDR, (char *)&lifr) < 0) {
+ logperror_pi(pi, "phyint_init_from_k: SIOCGLIFDSTADDR");
+ goto error;
+ }
+ sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
+ if (sin6->sin6_family != AF_INET6 ||
+ IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) ||
+ !IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
+ pi->pi_dst_token = in6addr_any;
+ } else {
+ pi->pi_dst_token = sin6->sin6_addr;
+ /* Clear link-local prefix (first 10 bits) */
+ pi->pi_dst_token.s6_addr[0] = 0;
+ pi->pi_dst_token.s6_addr[1] &= 0x3f;
+ }
+ } else {
+ pi->pi_dst_token = in6addr_any;
+ }
+
+ /* Get link-layer address */
+ if (!(pi->pi_flags & IFF_MULTICAST) ||
+ (pi->pi_flags & IFF_POINTOPOINT)) {
+ pi->pi_hdw_addr_len = 0;
+ } else {
+ sin6 = (struct sockaddr_in6 *)&lifr.lifr_nd.lnr_addr;
+ bzero(sin6, sizeof (struct sockaddr_in6));
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_addr = pi->pi_ifaddr;
+
+ if (ioctl(fd, SIOCLIFGETND, (char *)&lifr) < 0) {
+ logperror_pi(pi, "phyint_init_from_k: SIOCLIFGETND");
+ goto error;
+ }
+
+ pi->pi_hdw_addr_len = lifr.lifr_nd.lnr_hdw_len;
+
+ if (lifr.lifr_nd.lnr_hdw_len != 0) {
+ bcopy((char *)lifr.lifr_nd.lnr_hdw_addr,
+ (char *)pi->pi_hdw_addr,
+ lifr.lifr_nd.lnr_hdw_len);
+ }
+ }
+
+ if (newsock) {
+ icmp6_filter_t filter;
+ int on = 1;
+
+ /* Set default values */
+ pi->pi_LinkMTU = pi->pi_mtu;
+ pi->pi_CurHopLimit = 0;
+ pi->pi_BaseReachableTime = ND_REACHABLE_TIME;
+ phyint_reach_random(pi, _B_FALSE);
+ pi->pi_RetransTimer = ND_RETRANS_TIMER;
+
+ /* Setup socket for transmission and reception */
+ if (setsockopt(fd, IPPROTO_IPV6,
+ IPV6_BOUND_IF, (char *)&pi->pi_index,
+ sizeof (pi->pi_index)) < 0) {
+ logperror_pi(pi, "phyint_init_from_k: setsockopt "
+ "IPV6_BOUND_IF");
+ goto error;
+ }
+
+ ttl = IPV6_MAX_HOPS;
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
+ (char *)&ttl, sizeof (ttl)) < 0) {
+ logperror_pi(pi, "phyint_init_from_k: setsockopt "
+ "IPV6_UNICAST_HOPS");
+ goto error;
+ }
+
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
+ (char *)&ttl, sizeof (ttl)) < 0) {
+ logperror_pi(pi, "phyint_init_from_k: setsockopt "
+ "IPV6_MULTICAST_HOPS");
+ goto error;
+ }
+
+ v6mcastr.ipv6mr_multiaddr = all_nodes_mcast;
+ v6mcastr.ipv6mr_interface = pi->pi_index;
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP,
+ (char *)&v6mcastr, sizeof (v6mcastr)) < 0) {
+ logperror_pi(pi, "phyint_init_from_k: "
+ "setsockopt IPV6_JOIN_GROUP");
+ goto error;
+ }
+ pi->pi_state |= PI_JOINED_ALLNODES;
+ pi->pi_kernel_state |= PI_JOINED_ALLNODES;
+
+ /*
+ * Filter out so that we only receive router advertisements and
+ * router solicitations.
+ */
+ ICMP6_FILTER_SETBLOCKALL(&filter);
+ ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filter);
+ ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filter);
+
+ if (setsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER,
+ (char *)&filter, sizeof (filter)) < 0) {
+ logperror_pi(pi, "phyint_init_from_k: setsockopt "
+ "ICMP6_FILTER");
+ goto error;
+ }
+
+ /* Enable receipt of ancillary data */
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT,
+ (char *)&on, sizeof (on)) < 0) {
+ logperror_pi(pi, "phyint_init_from_k: setsockopt "
+ "IPV6_RECVHOPLIMIT");
+ goto error;
+ }
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVRTHDR,
+ (char *)&on, sizeof (on)) < 0) {
+ logperror_pi(pi, "phyint_init_from_k: setsockopt "
+ "IPV6_RECVRTHDR");
+ goto error;
+ }
+ }
+
+ if (pi->pi_AdvSendAdvertisements &&
+ !(pi->pi_kernel_state & PI_JOINED_ALLROUTERS)) {
+ v6mcastr.ipv6mr_multiaddr = all_routers_mcast;
+ v6mcastr.ipv6mr_interface = pi->pi_index;
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP,
+ (char *)&v6mcastr, sizeof (v6mcastr)) < 0) {
+ logperror_pi(pi, "phyint_init_from_k: setsockopt "
+ "IPV6_JOIN_GROUP");
+ goto error;
+ }
+ pi->pi_state |= PI_JOINED_ALLROUTERS;
+ pi->pi_kernel_state |= PI_JOINED_ALLROUTERS;
+ }
+ /*
+ * If not already set, set the IFF_ROUTER interface flag based on
+ * AdvSendAdvertisements. Note that this will also enable IPv6
+ * forwarding on the interface. We don't clear IFF_ROUTER if we're
+ * not advertising on an interface, because we could still be
+ * forwarding on those interfaces.
+ */
+ (void) strncpy(lifr.lifr_name, pi->pi_name, sizeof (lifr.lifr_name));
+ lifr.lifr_name[sizeof (lifr.lifr_name) - 1] = '\0';
+ if (ioctl(fd, SIOCGLIFFLAGS, (char *)&lifr) < 0) {
+ logperror_pi(pi, "phyint_init_from_k: SIOCGLIFFLAGS");
+ goto error;
+ }
+ if (!(lifr.lifr_flags & IFF_ROUTER) && pi->pi_AdvSendAdvertisements) {
+ lifr.lifr_flags |= IFF_ROUTER;
+
+ if (ioctl(fd, SIOCSLIFFLAGS, (char *)&lifr) < 0) {
+ logperror_pi(pi, "phyint_init_from_k: SIOCSLIFFLAGS");
+ goto error;
+ }
+ pi->pi_flags = lifr.lifr_flags;
+ }
+
+ /* Set linkinfo parameters */
+ (void) strncpy(lifr.lifr_name, pi->pi_name, sizeof (lifr.lifr_name));
+ lifr.lifr_name[sizeof (lifr.lifr_name) - 1] = '\0';
+ if (ioctl(fd, SIOCGLIFLNKINFO, (char *)&lifr) < 0) {
+ logperror_pi(pi, "phyint_init_from_k: SIOCGLIFLNKINFO");
+ goto error;
+ }
+ lifr.lifr_ifinfo.lir_maxhops = pi->pi_CurHopLimit;
+ lifr.lifr_ifinfo.lir_reachtime = pi->pi_ReachableTime;
+ lifr.lifr_ifinfo.lir_reachretrans = pi->pi_RetransTimer;
+ if (ioctl(fd, SIOCSLIFLNKINFO, (char *)&lifr) < 0) {
+ logperror_pi(pi, "phyint_init_from_k: SIOCSLIFLNKINFO");
+ goto error;
+ }
+ if (debug & D_PHYINT) {
+ logmsg(LOG_DEBUG, "phyint_init_from_k(%s): done\n",
+ pi->pi_name);
+ }
+ return (0);
+
+error:
+ /* Pretend the interface does not exist in the kernel */
+ pi->pi_kernel_state &= ~PI_PRESENT;
+ if (newsock) {
+ (void) close(pi->pi_sock);
+ pi->pi_sock = -1;
+ }
+ return (-1);
+}
+
+/*
+ * Delete (unlink and free).
+ * Handles delete of things that have not yet been inserted in the list.
+ */
+void
+phyint_delete(struct phyint *pi)
+{
+ if (debug & D_PHYINT)
+ logmsg(LOG_DEBUG, "phyint_delete(%s)\n", pi->pi_name);
+
+ while (pi->pi_router_list)
+ router_delete(pi->pi_router_list);
+ while (pi->pi_prefix_list)
+ prefix_delete(pi->pi_prefix_list);
+ while (pi->pi_adv_prefix_list)
+ adv_prefix_delete(pi->pi_adv_prefix_list);
+
+ if (pi->pi_sock != -1) {
+ (void) poll_remove(pi->pi_sock);
+ if (close(pi->pi_sock) < 0) {
+ logperror_pi(pi, "phyint_delete: close");
+ }
+ pi->pi_sock = -1;
+ }
+
+ if (pi->pi_prev == NULL) {
+ if (phyints == pi)
+ phyints = pi->pi_next;
+ } else {
+ pi->pi_prev->pi_next = pi->pi_next;
+ }
+ if (pi->pi_next != NULL)
+ pi->pi_next->pi_prev = pi->pi_prev;
+ pi->pi_next = pi->pi_prev = NULL;
+ if (pi->pi_group_name != NULL)
+ free(pi->pi_group_name);
+ free(pi);
+}
+
+/*
+ * Called with the number of millseconds elapsed since the last call.
+ * Determines if any timeout event has occurred and
+ * returns the number of milliseconds until the next timeout event
+ * for the phyint iself (excluding prefixes and routers).
+ * Returns TIMER_INFINITY for "never".
+ */
+uint_t
+phyint_timer(struct phyint *pi, uint_t elapsed)
+{
+ uint_t next = TIMER_INFINITY;
+
+ if (pi->pi_AdvSendAdvertisements) {
+ if (pi->pi_adv_state != NO_ADV) {
+ int old_state = pi->pi_adv_state;
+
+ if (debug & (D_STATE|D_PHYINT)) {
+ logmsg(LOG_DEBUG, "phyint_timer ADV(%s) "
+ "state %d\n", pi->pi_name, (int)old_state);
+ }
+ next = advertise_event(pi, ADV_TIMER, elapsed);
+ if (debug & D_STATE) {
+ logmsg(LOG_DEBUG, "phyint_timer ADV(%s) "
+ "state %d -> %d\n",
+ pi->pi_name, (int)old_state,
+ (int)pi->pi_adv_state);
+ }
+ }
+ } else {
+ if (pi->pi_sol_state != NO_SOLICIT) {
+ int old_state = pi->pi_sol_state;
+
+ if (debug & (D_STATE|D_PHYINT)) {
+ logmsg(LOG_DEBUG, "phyint_timer SOL(%s) "
+ "state %d\n", pi->pi_name, (int)old_state);
+ }
+ next = solicit_event(pi, SOL_TIMER, elapsed);
+ if (debug & D_STATE) {
+ logmsg(LOG_DEBUG, "phyint_timer SOL(%s) "
+ "state %d -> %d\n",
+ pi->pi_name, (int)old_state,
+ (int)pi->pi_sol_state);
+ }
+ }
+ }
+
+ /*
+ * If the phyint has been unplumbed, we don't want to call
+ * phyint_reach_random. We will be in the NO_ADV or NO_SOLICIT state.
+ */
+ if ((pi->pi_AdvSendAdvertisements && (pi->pi_adv_state != NO_ADV)) ||
+ (!pi->pi_AdvSendAdvertisements &&
+ (pi->pi_sol_state != NO_SOLICIT))) {
+ pi->pi_reach_time_since_random += elapsed;
+ if (pi->pi_reach_time_since_random >= MAX_REACH_RANDOM_INTERVAL)
+ phyint_reach_random(pi, _B_TRUE);
+ }
+
+ return (next);
+}
+
+static void
+phyint_print(struct phyint *pi)
+{
+ struct prefix *pr;
+ struct adv_prefix *adv_pr;
+ struct router *dr;
+ char abuf[INET6_ADDRSTRLEN];
+ char llabuf[BUFSIZ];
+
+ logmsg(LOG_DEBUG, "Phyint %s index %d state %x, kernel %x, "
+ "onlink_def %d num routers %d\n",
+ pi->pi_name, pi->pi_index,
+ pi->pi_state, pi->pi_kernel_state,
+ pi->pi_onlink_default ? 1 : 0,
+ pi->pi_num_k_routers);
+ logmsg(LOG_DEBUG, "\taddress: %s flags %x\n",
+ inet_ntop(AF_INET6, (void *)&pi->pi_ifaddr,
+ abuf, sizeof (abuf)), pi->pi_flags);
+ logmsg(LOG_DEBUG, "\tsock %d mtu %d hdw_addr len %d <%s>\n",
+ pi->pi_sock, pi->pi_mtu, pi->pi_hdw_addr_len,
+ ((pi->pi_hdw_addr_len != 0) ?
+ fmt_lla(llabuf, sizeof (llabuf), pi->pi_hdw_addr,
+ pi->pi_hdw_addr_len) : "none"));
+ logmsg(LOG_DEBUG, "\ttoken: len %d %s\n",
+ pi->pi_token_length,
+ inet_ntop(AF_INET6, (void *)&pi->pi_token,
+ abuf, sizeof (abuf)));
+ if (pi->pi_TmpAddrsEnabled) {
+ logmsg(LOG_DEBUG, "\ttmp_token: %s\n",
+ inet_ntop(AF_INET6, (void *)&pi->pi_tmp_token,
+ abuf, sizeof (abuf)));
+ logmsg(LOG_DEBUG, "\ttmp config: pref %d valid %d "
+ "maxdesync %d desync %d regen %d\n",
+ pi->pi_TmpPreferredLifetime, pi->pi_TmpValidLifetime,
+ pi->pi_TmpMaxDesyncFactor, pi->pi_TmpDesyncFactor,
+ pi->pi_TmpRegenAdvance);
+ }
+ if (pi->pi_flags & IFF_POINTOPOINT) {
+ logmsg(LOG_DEBUG, "\tdst_token: %s\n",
+ inet_ntop(AF_INET6, (void *)&pi->pi_dst_token,
+ abuf, sizeof (abuf)));
+ }
+ logmsg(LOG_DEBUG, "\tLinkMTU %d CurHopLimit %d "
+ "BaseReachableTime %d\n\tReachableTime %d RetransTimer %d\n",
+ pi->pi_LinkMTU, pi->pi_CurHopLimit, pi->pi_BaseReachableTime,
+ pi->pi_ReachableTime, pi->pi_RetransTimer);
+ if (!pi->pi_AdvSendAdvertisements) {
+ /* Solicit state */
+ logmsg(LOG_DEBUG, "\tSOLICIT: time_left %d state %d count %d\n",
+ pi->pi_sol_time_left, pi->pi_sol_state, pi->pi_sol_count);
+ } else {
+ /* Advertise state */
+ logmsg(LOG_DEBUG, "\tADVERT: time_left %d state %d count %d "
+ "since last %d\n",
+ pi->pi_adv_time_left, pi->pi_adv_state, pi->pi_adv_count,
+ pi->pi_adv_time_since_sent);
+ print_iflist(pi->pi_config);
+ }
+ for (pr = pi->pi_prefix_list; pr != NULL; pr = pr->pr_next)
+ prefix_print(pr);
+
+ for (adv_pr = pi->pi_adv_prefix_list; adv_pr != NULL;
+ adv_pr = adv_pr->adv_pr_next) {
+ adv_prefix_print(adv_pr);
+ }
+
+ for (dr = pi->pi_router_list; dr != NULL; dr = dr->dr_next)
+ router_print(dr);
+
+ logmsg(LOG_DEBUG, "\n");
+}
+
+/*
+ * Randomize pi->pi_ReachableTime.
+ * Done periodically when there are no RAs and at a maximum frequency when
+ * RA's arrive.
+ * Assumes that caller has determined that it is time to generate
+ * a new random ReachableTime.
+ */
+void
+phyint_reach_random(struct phyint *pi, boolean_t set_needed)
+{
+ pi->pi_ReachableTime = GET_RANDOM(
+ (int)(ND_MIN_RANDOM_FACTOR * pi->pi_BaseReachableTime),
+ (int)(ND_MAX_RANDOM_FACTOR * pi->pi_BaseReachableTime));
+ if (set_needed) {
+ struct lifreq lifr;
+
+ (void) strncpy(lifr.lifr_name, pi->pi_name,
+ sizeof (lifr.lifr_name));
+ pi->pi_name[sizeof (pi->pi_name) - 1] = '\0';
+ if (ioctl(pi->pi_sock, SIOCGLIFLNKINFO, (char *)&lifr) < 0) {
+ logperror_pi(pi,
+ "phyint_reach_random: SIOCGLIFLNKINFO");
+ return;
+ }
+ lifr.lifr_ifinfo.lir_reachtime = pi->pi_ReachableTime;
+ if (ioctl(pi->pi_sock, SIOCSLIFLNKINFO, (char *)&lifr) < 0) {
+ logperror_pi(pi,
+ "phyint_reach_random: SIOCSLIFLNKINFO");
+ return;
+ }
+ }
+ pi->pi_reach_time_since_random = 0;
+}
+
+/*
+ * Validate a temporary token against a list of known bad values.
+ * Currently assumes that token is 8 bytes long! Current known
+ * bad values include 0, reserved anycast tokens (RFC 2526), tokens
+ * used by ISATAP (draft-ietf-ngtrans-isatap-N), any token already
+ * assigned to this interface, or any token for which the global
+ * bit is set.
+ *
+ * Called by tmptoken_create().
+ *
+ * Return _B_TRUE if token is valid (no match), _B_FALSE if not.
+ */
+static boolean_t
+tmptoken_isvalid(struct in6_addr *token)
+{
+ struct phyint *pi;
+ struct in6_addr mask;
+ struct in6_addr isatap = { 0, 0, 0, 0, 0, 0, 0, 0, \
+ 0, 0, 0x5e, 0xfe, 0, 0, 0, 0 };
+ struct in6_addr anycast = { 0, 0, 0, 0, \
+ 0, 0, 0, 0, \
+ 0xfd, 0xff, 0xff, 0xff, \
+ 0xff, 0xff, 0xff, 0x80 };
+
+ if (IN6_IS_ADDR_UNSPECIFIED(token))
+ return (_B_FALSE);
+
+ if (token->s6_addr[8] & 0x2)
+ return (_B_FALSE);
+
+ (void) memcpy(&mask, token, sizeof (mask));
+ mask._S6_un._S6_u32[3] = 0;
+ if (IN6_ARE_ADDR_EQUAL(&isatap, token))
+ return (_B_FALSE);
+
+ mask._S6_un._S6_u32[3] = token->_S6_un._S6_u32[3] & 0xffffff80;
+ if (IN6_ARE_ADDR_EQUAL(&anycast, token))
+ return (_B_FALSE);
+
+ for (pi = phyints; pi != NULL; pi = pi->pi_next) {
+ if (((pi->pi_token_length == TMP_TOKEN_BITS) &&
+ IN6_ARE_ADDR_EQUAL(&pi->pi_token, token)) ||
+ IN6_ARE_ADDR_EQUAL(&pi->pi_tmp_token, token))
+ return (_B_FALSE);
+ }
+
+ /* none of our tests failed, must be a good one! */
+ return (_B_TRUE);
+}
+
+/*
+ * Generate a temporary token and set up its timer
+ *
+ * Called from incoming_prefix_addrconf_process() (when token is first
+ * needed) and from tmptoken_timer() (when current token expires).
+ *
+ * Returns _B_TRUE if a token was successfully generated, _B_FALSE if not.
+ */
+boolean_t
+tmptoken_create(struct phyint *pi)
+{
+ int fd, i = 0, max_tries = 15;
+ struct in6_addr token;
+ uint32_t *tokenp = &(token._S6_un._S6_u32[2]);
+ char buf[INET6_ADDRSTRLEN];
+
+ if ((fd = open("/dev/urandom", O_RDONLY)) == -1) {
+ perror("open /dev/urandom");
+ goto no_token;
+ }
+
+ bzero((char *)&token, sizeof (token));
+ do {
+ if (read(fd, (void *)tokenp, TMP_TOKEN_BYTES) == -1) {
+ perror("read /dev/urandom");
+ (void) close(fd);
+ goto no_token;
+ }
+
+ /*
+ * Assume EUI-64 formatting, and thus 64-bit
+ * token len; need to clear global bit.
+ */
+ token.s6_addr[8] &= 0xfd;
+
+ i++;
+
+ } while (!tmptoken_isvalid(&token) && i < max_tries);
+
+ (void) close(fd);
+
+ if (i == max_tries) {
+no_token:
+ logmsg(LOG_WARNING, "tmptoken_create(%s): failed to create "
+ "token; disabling temporary addresses on %s\n",
+ pi->pi_name, pi->pi_name);
+ pi->pi_TmpAddrsEnabled = 0;
+ return (_B_FALSE);
+ }
+
+ pi->pi_tmp_token = token;
+
+ if (debug & D_TMP)
+ logmsg(LOG_DEBUG, "tmptoken_create(%s): created temporary "
+ "token %s\n", pi->pi_name,
+ inet_ntop(AF_INET6, &pi->pi_tmp_token, buf, sizeof (buf)));
+
+ pi->pi_TmpRegenCountdown = (pi->pi_TmpPreferredLifetime -
+ pi->pi_TmpDesyncFactor - pi->pi_TmpRegenAdvance) * MILLISEC;
+ if (pi->pi_TmpRegenCountdown != 0)
+ timer_schedule(pi->pi_TmpRegenCountdown);
+
+ return (_B_TRUE);
+}
+
+/*
+ * Delete a temporary token. This is outside the normal timeout process,
+ * so mark any existing addresses based on this token DEPRECATED and set
+ * their preferred lifetime to 0. Don't tamper with valid lifetime, that
+ * will be used to eventually remove the address. Also reset the current
+ * pi_tmp_token value to 0.
+ *
+ * Called from incoming_prefix_addrconf_process() if DAD fails on a temp
+ * addr.
+ */
+void
+tmptoken_delete(struct phyint *pi)
+{
+ struct prefix *pr;
+
+ for (pr = pi->pi_prefix_list; pr != NULL; pr = pr->pr_next) {
+ if (!(pr->pr_flags & IFF_TEMPORARY) ||
+ (pr->pr_flags & IFF_DEPRECATED) ||
+ (!token_equal(pr->pr_address, pi->pi_tmp_token,
+ TMP_TOKEN_BITS))) {
+ continue;
+ }
+ pr->pr_PreferredLifetime = 0;
+ pr->pr_state |= PR_DEPRECATED;
+ prefix_update_k(pr);
+ }
+
+ (void) memset(&pi->pi_tmp_token, 0, sizeof (pi->pi_tmp_token));
+}
+
+/*
+ * Called from run_timeouts() with the number of milliseconds elapsed
+ * since the last call. Determines if any timeout event has occurred
+ * and returns the number of milliseconds until the next timeout event
+ * for the tmp token. Returns TIMER_INFINITY for "never".
+ */
+uint_t
+tmptoken_timer(struct phyint *pi, uint_t elapsed)
+{
+ struct nd_opt_prefix_info opt;
+ struct sockaddr_in6 sin6;
+ struct prefix *pr, *newpr;
+
+ if (debug & D_TMP) {
+ logmsg(LOG_DEBUG, "tmptoken_timer(%s, %d) regencountdown %d\n",
+ pi->pi_name, (int)elapsed, pi->pi_TmpRegenCountdown);
+ }
+ if (!pi->pi_TmpAddrsEnabled ||
+ (pi->pi_TmpRegenCountdown == TIMER_INFINITY))
+ return (TIMER_INFINITY);
+
+ if (pi->pi_TmpRegenCountdown > elapsed) {
+ pi->pi_TmpRegenCountdown -= elapsed;
+ return (pi->pi_TmpRegenCountdown);
+ }
+
+ /*
+ * Tmp token timer has expired. Start by generating a new token.
+ * If we can't get a new token, tmp addrs are disabled on this
+ * interface, so there's no need to continue, or to set a timer.
+ */
+ if (!tmptoken_create(pi))
+ return (TIMER_INFINITY);
+
+ /*
+ * Now that we have a new token, walk the list of prefixes to
+ * find which ones need a corresponding tmp addr generated.
+ */
+ for (pr = pi->pi_prefix_list; pr != NULL; pr = pr->pr_next) {
+
+ if (!(pr->pr_state & PR_AUTO) || pr->pr_state & PR_STATIC ||
+ pr->pr_state & PR_DEPRECATED ||
+ pr->pr_flags & IFF_TEMPORARY)
+ continue;
+
+ newpr = prefix_create(pi, pr->pr_prefix, pr->pr_prefix_len,
+ IFF_TEMPORARY);
+ if (newpr == NULL) {
+ char pbuf[INET6_ADDRSTRLEN];
+ char tbuf[INET6_ADDRSTRLEN];
+ (void) inet_ntop(AF_INET6, &pr->pr_prefix, pbuf,
+ sizeof (pbuf));
+ (void) inet_ntop(AF_INET6, &pi->pi_tmp_token, tbuf,
+ sizeof (tbuf));
+ logmsg(LOG_ERR, "can't create new tmp addr "
+ "(%s, %s, %s)\n", pi->pi_name, pbuf, tbuf);
+ continue;
+ }
+
+ /*
+ * We want to use incoming_prefix_*_process() functions to
+ * set up the new tmp addr, so cobble together a prefix
+ * info option struct based on the existing prefix to pass
+ * in. The lifetimes will be based on the current time
+ * remaining.
+ *
+ * The "from" param is only used for messages; pass in
+ * ::0 for that.
+ */
+ opt.nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION;
+ opt.nd_opt_pi_len = sizeof (opt) / 8;
+ opt.nd_opt_pi_prefix_len = pr->pr_prefix_len;
+ opt.nd_opt_pi_flags_reserved = ND_OPT_PI_FLAG_AUTO;
+ opt.nd_opt_pi_valid_time =
+ htonl(pr->pr_ValidLifetime / 1000);
+ opt.nd_opt_pi_preferred_time =
+ htonl(pr->pr_PreferredLifetime / 1000);
+ if (pr->pr_state & PR_ONLINK)
+ opt.nd_opt_pi_flags_reserved &= ND_OPT_PI_FLAG_ONLINK;
+ opt.nd_opt_pi_prefix = pr->pr_prefix;
+
+ (void) memset(&sin6, 0, sizeof (sin6));
+
+ if (!incoming_prefix_addrconf_process(pi, newpr,
+ (uchar_t *)&opt, &sin6, _B_FALSE, _B_TRUE)) {
+ char pbuf[INET6_ADDRSTRLEN];
+ char tbuf[INET6_ADDRSTRLEN];
+ (void) inet_ntop(AF_INET6, &pr->pr_prefix, pbuf,
+ sizeof (pbuf));
+ (void) inet_ntop(AF_INET6, &pi->pi_tmp_token, tbuf,
+ sizeof (tbuf));
+ logmsg(LOG_ERR, "can't create new tmp addr "
+ "(%s, %s, %s)\n", pi->pi_name, pbuf, tbuf);
+ continue;
+ }
+
+ if (pr->pr_state & PR_ONLINK) {
+ incoming_prefix_onlink_process(newpr, (uchar_t *)&opt);
+ }
+ }
+
+ /*
+ * appropriate timers were scheduled when
+ * the token and addresses were created.
+ */
+ return (TIMER_INFINITY);
+}
+
+/*
+ * tlen specifies the token length in bits. Compares the lower
+ * tlen bits of the two addresses provided and returns _B_TRUE if
+ * they match, _B_FALSE if not. Also returns _B_FALSE for invalid
+ * values of tlen.
+ */
+boolean_t
+token_equal(struct in6_addr t1, struct in6_addr t2, int tlen)
+{
+ uchar_t mask;
+ int j, abytes, tbytes, tbits;
+
+ if (tlen < 0 || tlen > IPV6_ABITS)
+ return (_B_FALSE);
+
+ abytes = IPV6_ABITS >> 3;
+ tbytes = tlen >> 3;
+ tbits = tlen & 7;
+
+ for (j = abytes - 1; j >= abytes - tbytes; j--)
+ if (t1.s6_addr[j] != t2.s6_addr[j])
+ return (_B_FALSE);
+
+ if (tbits == 0)
+ return (_B_TRUE);
+
+ /* We only care about the tbits rightmost bits */
+ mask = 0xff >> (8 - tbits);
+ if ((t1.s6_addr[j] & mask) != (t2.s6_addr[j] & mask))
+ return (_B_FALSE);
+
+ return (_B_TRUE);
+}
+
+/*
+ * Lookup prefix structure that matches the prefix and prefix length.
+ * Assumes that the bits after prefixlen might not be zero.
+ */
+static struct prefix *
+prefix_lookup(struct phyint *pi, struct in6_addr prefix, int prefixlen)
+{
+ struct prefix *pr;
+ char abuf[INET6_ADDRSTRLEN];
+
+ if (debug & D_PREFIX) {
+ logmsg(LOG_DEBUG, "prefix_lookup(%s, %s/%u)\n", pi->pi_name,
+ inet_ntop(AF_INET6, (void *)&prefix,
+ abuf, sizeof (abuf)), prefixlen);
+ }
+
+ for (pr = pi->pi_prefix_list; pr != NULL; pr = pr->pr_next) {
+ if (pr->pr_prefix_len == prefixlen &&
+ prefix_equal(prefix, pr->pr_prefix, prefixlen))
+ return (pr);
+ }
+ return (NULL);
+}
+
+/*
+ * Compare two prefixes that have the same prefix length.
+ * Fails if the prefix length is unreasonable.
+ */
+boolean_t
+prefix_equal(struct in6_addr p1, struct in6_addr p2, int plen)
+{
+ uchar_t mask;
+ int j, pbytes, pbits;
+
+ if (plen < 0 || plen > IPV6_ABITS)
+ return (_B_FALSE);
+
+ pbytes = plen >> 3;
+ pbits = plen & 7;
+
+ for (j = 0; j < pbytes; j++)
+ if (p1.s6_addr[j] != p2.s6_addr[j])
+ return (_B_FALSE);
+
+ if (pbits == 0)
+ return (_B_TRUE);
+
+ /* Make the N leftmost bits one */
+ mask = 0xff << (8 - pbits);
+ if ((p1.s6_addr[j] & mask) != (p2.s6_addr[j] & mask))
+ return (_B_FALSE);
+
+ return (_B_TRUE);
+}
+
+/*
+ * Set a prefix from an address and a prefix length.
+ * Force all the bits after the prefix length to be zero.
+ */
+void
+prefix_set(struct in6_addr *prefix, struct in6_addr addr, int prefix_len)
+{
+ uchar_t mask;
+ int j;
+
+ if (prefix_len < 0 || prefix_len > IPV6_ABITS)
+ return;
+
+ bzero((char *)prefix, sizeof (*prefix));
+
+ for (j = 0; prefix_len > 8; prefix_len -= 8, j++)
+ prefix->s6_addr[j] = addr.s6_addr[j];
+
+ /* Make the N leftmost bits one */
+ mask = 0xff << (8 - prefix_len);
+ prefix->s6_addr[j] = addr.s6_addr[j] & mask;
+}
+
+/*
+ * Lookup a prefix based on the kernel's interface name.
+ */
+struct prefix *
+prefix_lookup_name(struct phyint *pi, char *name)
+{
+ struct prefix *pr;
+
+ if (debug & D_PREFIX) {
+ logmsg(LOG_DEBUG, "prefix_lookup_name(%s, %s)\n",
+ pi->pi_name, name);
+ }
+ if (name[0] == '\0')
+ return (NULL);
+
+ for (pr = pi->pi_prefix_list; pr != NULL; pr = pr->pr_next) {
+ if (strcmp(name, pr->pr_name) == 0)
+ return (pr);
+ }
+ return (NULL);
+}
+
+/*
+ * Search the phyints list to make sure that this new prefix does
+ * not already exist in any other physical interfaces that have
+ * the same address as this one
+ */
+struct prefix *
+prefix_lookup_addr_match(struct prefix *pr)
+{
+ char abuf[INET6_ADDRSTRLEN];
+ struct phyint *pi;
+ struct prefix *otherpr = NULL;
+ struct in6_addr prefix;
+ int prefixlen;
+
+ if (debug & D_PREFIX) {
+ logmsg(LOG_DEBUG, "prefix_lookup_addr_match(%s/%u)\n",
+ inet_ntop(AF_INET6, (void *)&pr->pr_address,
+ abuf, sizeof (abuf)), pr->pr_prefix_len);
+ }
+ prefix = pr->pr_prefix;
+ prefixlen = pr->pr_prefix_len;
+ for (pi = phyints; pi != NULL; pi = pi->pi_next) {
+ otherpr = prefix_lookup(pi, prefix, prefixlen);
+ if (otherpr == pr)
+ continue;
+ if (otherpr != NULL && (otherpr->pr_state & PR_AUTO) &&
+ IN6_ARE_ADDR_EQUAL(&pr->pr_address,
+ &otherpr->pr_address))
+ return (otherpr);
+ }
+ return (NULL);
+}
+
+/*
+ * Initialize a new prefix without setting lifetimes etc.
+ */
+struct prefix *
+prefix_create(struct phyint *pi, struct in6_addr prefix, int prefixlen,
+ uint64_t flags)
+{
+ struct prefix *pr;
+ char abuf[INET6_ADDRSTRLEN];
+
+ if (debug & D_PREFIX) {
+ logmsg(LOG_DEBUG, "prefix_create(%s, %s/%u, 0x%llx)\n",
+ pi->pi_name, inet_ntop(AF_INET6, (void *)&prefix,
+ abuf, sizeof (abuf)), prefixlen, flags);
+ }
+ pr = (struct prefix *)calloc(sizeof (struct prefix), 1);
+ if (pr == NULL) {
+ logmsg(LOG_ERR, "prefix_create: out of memory\n");
+ return (NULL);
+ }
+ /*
+ * The prefix might have non-zero bits after the prefix len bits.
+ * Force them to be zero.
+ */
+ prefix_set(&pr->pr_prefix, prefix, prefixlen);
+ pr->pr_prefix_len = prefixlen;
+ pr->pr_PreferredLifetime = PREFIX_INFINITY;
+ pr->pr_ValidLifetime = PREFIX_INFINITY;
+ pr->pr_OnLinkLifetime = PREFIX_INFINITY;
+ pr->pr_kernel_state = 0;
+ pr->pr_flags |= flags;
+ prefix_insert(pi, pr);
+ return (pr);
+}
+
+/*
+ * Create a new named prefix. Caller should use prefix_init_from_k
+ * to initialize the content.
+ */
+struct prefix *
+prefix_create_name(struct phyint *pi, char *name)
+{
+ struct prefix *pr;
+
+ if (debug & D_PREFIX) {
+ logmsg(LOG_DEBUG, "prefix_create_name(%s, %s)\n",
+ pi->pi_name, name);
+ }
+ pr = (struct prefix *)calloc(sizeof (struct prefix), 1);
+ if (pr == NULL) {
+ logmsg(LOG_ERR, "prefix_create_name: out of memory\n");
+ return (NULL);
+ }
+ (void) strncpy(pr->pr_name, name, sizeof (pr->pr_name));
+ pr->pr_name[sizeof (pr->pr_name) - 1] = '\0';
+ prefix_insert(pi, pr);
+ return (pr);
+}
+
+/* Insert in linked list */
+static void
+prefix_insert(struct phyint *pi, struct prefix *pr)
+{
+ pr->pr_next = pi->pi_prefix_list;
+ pr->pr_prev = NULL;
+ if (pi->pi_prefix_list != NULL)
+ pi->pi_prefix_list->pr_prev = pr;
+ pi->pi_prefix_list = pr;
+ pr->pr_physical = pi;
+}
+
+/*
+ * Initialize the prefix from the content of the kernel.
+ * If IFF_ADDRCONF is set we treat it as PR_AUTO (i.e. an addrconf
+ * prefix). However, we not derive the lifetimes from
+ * the kernel thus they are set to 1 week.
+ * Ignore the prefix if the interface is not IFF_UP.
+ */
+int
+prefix_init_from_k(struct prefix *pr)
+{
+ struct lifreq lifr;
+ struct sockaddr_in6 *sin6;
+ int sock = pr->pr_physical->pi_sock;
+
+ (void) strncpy(lifr.lifr_name, pr->pr_name, sizeof (lifr.lifr_name));
+ lifr.lifr_name[sizeof (lifr.lifr_name) - 1] = '\0';
+ if (ioctl(sock, SIOCGLIFADDR, (char *)&lifr) < 0) {
+ logperror_pr(pr, "prefix_init_from_k: ioctl (get addr)");
+ goto error;
+ }
+ if (lifr.lifr_addr.ss_family != AF_INET6) {
+ logmsg(LOG_ERR, "ignoring interface %s: not AF_INET6\n",
+ pr->pr_name);
+ goto error;
+ }
+ sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
+ pr->pr_address = sin6->sin6_addr;
+
+ if (ioctl(sock, SIOCGLIFFLAGS, (char *)&lifr) < 0) {
+ logperror_pr(pr, "prefix_init_from_k: ioctl (get flags)");
+ goto error;
+ }
+ pr->pr_flags = lifr.lifr_flags;
+
+ if (ioctl(sock, SIOCGLIFSUBNET, (char *)&lifr) < 0) {
+ logperror_pr(pr, "prefix_init_from_k: ioctl (get subnet)");
+ goto error;
+ }
+ if (lifr.lifr_subnet.ss_family != AF_INET6) {
+ logmsg(LOG_ERR, "ignoring interface %s: not AF_INET6\n",
+ pr->pr_name);
+ goto error;
+ }
+ /*
+ * Guard against the prefix having non-zero bits after the prefix
+ * len bits.
+ */
+ sin6 = (struct sockaddr_in6 *)&lifr.lifr_subnet;
+ pr->pr_prefix_len = lifr.lifr_addrlen;
+ prefix_set(&pr->pr_prefix, sin6->sin6_addr, pr->pr_prefix_len);
+
+ if (pr->pr_prefix_len != IPV6_ABITS && (pr->pr_flags & IFF_UP) &&
+ IN6_ARE_ADDR_EQUAL(&pr->pr_address, &pr->pr_prefix)) {
+ char abuf[INET6_ADDRSTRLEN];
+
+ logmsg(LOG_ERR, "ingoring interface %s: it appears to be "
+ "configured with an invalid interface id (%s/%u)\n",
+ pr->pr_name,
+ inet_ntop(AF_INET6, (void *)&pr->pr_address,
+ abuf, sizeof (abuf)), pr->pr_prefix_len);
+ goto error;
+ }
+ pr->pr_kernel_state = 0;
+ if (pr->pr_prefix_len != IPV6_ABITS)
+ pr->pr_kernel_state |= PR_ONLINK;
+ if (!(pr->pr_flags & IFF_NOLOCAL))
+ pr->pr_kernel_state |= PR_AUTO;
+ if ((pr->pr_flags & IFF_DEPRECATED) && (pr->pr_kernel_state & PR_AUTO))
+ pr->pr_kernel_state |= PR_DEPRECATED;
+ if (!(pr->pr_flags & IFF_ADDRCONF)) {
+ /* Prevent ndpd from stepping on this prefix */
+ pr->pr_kernel_state |= PR_STATIC;
+ }
+ pr->pr_state = pr->pr_kernel_state;
+ /* Adjust pr_prefix_len based if PR_AUTO is set */
+ if (pr->pr_state & PR_AUTO) {
+ pr->pr_prefix_len =
+ IPV6_ABITS - pr->pr_physical->pi_token_length;
+ prefix_set(&pr->pr_prefix, pr->pr_prefix, pr->pr_prefix_len);
+ }
+
+ /* Can't extract lifetimes from the kernel - use 1 week */
+ pr->pr_ValidLifetime = NDP_PREFIX_DEFAULT_LIFETIME;
+ pr->pr_PreferredLifetime = NDP_PREFIX_DEFAULT_LIFETIME;
+ pr->pr_OnLinkLifetime = NDP_PREFIX_DEFAULT_LIFETIME;
+
+ /*
+ * If this is a temp addr, the creation time needs to be set.
+ * Though it won't be entirely accurate, the current time is
+ * an okay approximation.
+ */
+ if (pr->pr_flags & IFF_TEMPORARY)
+ pr->pr_CreateTime = getcurrenttime() / MILLISEC;
+
+ if (pr->pr_kernel_state == 0)
+ pr->pr_name[0] = '\0';
+ return (0);
+
+error:
+ /* Pretend that the prefix does not exist in the kernel */
+ pr->pr_kernel_state = 0;
+ pr->pr_name[0] = '\0';
+ return (-1);
+}
+
+/*
+ * Delete (unlink and free) and remove from kernel if the prefix
+ * was added by in.ndpd (i.e. PR_STATIC is not set).
+ * Handles delete of things that have not yet been inserted in the list
+ * i.e. pr_physical is NULL.
+ */
+void
+prefix_delete(struct prefix *pr)
+{
+ struct phyint *pi;
+ char abuf[INET6_ADDRSTRLEN];
+
+ if (debug & D_PREFIX) {
+ logmsg(LOG_DEBUG, "prefix_delete(%s, %s, %s/%u)\n",
+ pr->pr_physical->pi_name, pr->pr_name,
+ inet_ntop(AF_INET6, (void *)&pr->pr_prefix,
+ abuf, sizeof (abuf)), pr->pr_prefix_len);
+ }
+ /* Remove non-static prefixes from the kernel. */
+ pr->pr_state &= PR_STATIC;
+ pi = pr->pr_physical;
+ if (pr->pr_kernel_state != pr->pr_state)
+ prefix_update_k(pr);
+
+ if (pr->pr_prev == NULL) {
+ if (pi != NULL)
+ pi->pi_prefix_list = pr->pr_next;
+ } else {
+ pr->pr_prev->pr_next = pr->pr_next;
+ }
+ if (pr->pr_next != NULL)
+ pr->pr_next->pr_prev = pr->pr_prev;
+ pr->pr_next = pr->pr_prev = NULL;
+ free(pr);
+}
+
+/*
+ * Toggle one or more IFF_ flags for a prefix. Turn on 'onflags' and
+ * turn off 'offflags'.
+ */
+static int
+prefix_modify_flags(struct prefix *pr, uint64_t onflags, uint64_t offflags)
+{
+ struct lifreq lifr;
+ struct phyint *pi = pr->pr_physical;
+ uint64_t old_flags;
+ char abuf[INET6_ADDRSTRLEN];
+
+ if (debug & D_PREFIX) {
+ logmsg(LOG_DEBUG, "prefix_modify_flags(%s, %s, %s/%u) "
+ "flags %llx on %llx off %llx\n",
+ pr->pr_physical->pi_name,
+ pr->pr_name,
+ inet_ntop(AF_INET6, (void *)&pr->pr_prefix,
+ abuf, sizeof (abuf)), pr->pr_prefix_len,
+ pr->pr_flags, onflags, offflags);
+ }
+ /* Assumes that only the PR_STATIC link-local matches the pi_name */
+ if (!(pr->pr_state & PR_STATIC) &&
+ strcmp(pr->pr_name, pi->pi_name) == 0) {
+ logmsg(LOG_ERR, "prefix_modify_flags(%s, on %llx, off %llx): "
+ "name matches interface name\n",
+ pi->pi_name, onflags, offflags);
+ return (-1);
+ }
+
+ (void) strncpy(lifr.lifr_name, pr->pr_name, sizeof (lifr.lifr_name));
+ lifr.lifr_name[sizeof (lifr.lifr_name) - 1] = '\0';
+ if (ioctl(pi->pi_sock, SIOCGLIFFLAGS, (char *)&lifr) < 0) {
+ logperror_pr(pr, "prefix_modify_flags: SIOCGLIFFLAGS");
+ logmsg(LOG_ERR, "prefix_modify_flags(%s, %s) old 0x%llx "
+ "on 0x%llx off 0x%llx\n",
+ pr->pr_physical->pi_name,
+ pr->pr_name,
+ pr->pr_flags, onflags, offflags);
+ return (-1);
+ }
+ old_flags = lifr.lifr_flags;
+ lifr.lifr_flags |= onflags;
+ lifr.lifr_flags &= ~offflags;
+ pr->pr_flags = lifr.lifr_flags;
+ if (ioctl(pi->pi_sock, SIOCSLIFFLAGS, (char *)&lifr) < 0) {
+ logperror_pr(pr, "prefix_modify_flags: SIOCSLIFFLAGS");
+ logmsg(LOG_ERR, "prefix_modify_flags(%s, %s) old 0x%llx "
+ "new 0x%llx on 0x%llx off 0x%llx\n",
+ pr->pr_physical->pi_name,
+ pr->pr_name,
+ old_flags, lifr.lifr_flags, onflags, offflags);
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * Make the kernel state match what is in the prefix structure.
+ * This includes creating the prefix (allocating a new interface name)
+ * as well as setting the local address and on-link subnet prefix
+ * and controlling the IFF_ADDRCONF and IFF_DEPRECATED flags.
+ */
+void
+prefix_update_k(struct prefix *pr)
+{
+ struct lifreq lifr;
+ char abuf[INET6_ADDRSTRLEN];
+ char buf1[PREFIX_STATESTRLEN], buf2[PREFIX_STATESTRLEN];
+ struct phyint *pi = pr->pr_physical;
+ struct sockaddr_in6 *sin6;
+
+ if (debug & D_PREFIX) {
+ logmsg(LOG_DEBUG, "prefix_update_k(%s, %s, %s/%u) "
+ "from %s to %s\n", pr->pr_physical->pi_name, pr->pr_name,
+ inet_ntop(AF_INET6, (void *)&pr->pr_prefix,
+ abuf, sizeof (abuf)), pr->pr_prefix_len,
+ prefix_print_state(pr->pr_kernel_state, buf1,
+ sizeof (buf1)),
+ prefix_print_state(pr->pr_state, buf2, sizeof (buf2)));
+ }
+
+ if (pr->pr_kernel_state == pr->pr_state)
+ return; /* No changes */
+
+ /* Skip static prefixes */
+ if (pr->pr_state & PR_STATIC)
+ return;
+
+ if (pr->pr_kernel_state == 0) {
+ uint64_t onflags;
+ /*
+ * Create a new logical interface name and store in pr_name.
+ * Set IFF_ADDRCONF. Do not set an address (yet).
+ */
+ if (pr->pr_name[0] != '\0') {
+ /* Name already set! */
+ logmsg(LOG_ERR, "prefix_update_k(%s, %s, %s/%u) "
+ "from %s to %s name is already allocated\n",
+ pr->pr_physical->pi_name, pr->pr_name,
+ inet_ntop(AF_INET6, (void *)&pr->pr_prefix,
+ abuf, sizeof (abuf)), pr->pr_prefix_len,
+ prefix_print_state(pr->pr_kernel_state, buf1,
+ sizeof (buf1)),
+ prefix_print_state(pr->pr_state, buf2,
+ sizeof (buf2)));
+ return;
+ }
+
+ (void) strncpy(lifr.lifr_name, pi->pi_name,
+ sizeof (lifr.lifr_name));
+ lifr.lifr_name[sizeof (lifr.lifr_name) - 1] = '\0';
+ lifr.lifr_addr.ss_family = AF_UNSPEC;
+ if (ioctl(pi->pi_sock, SIOCLIFADDIF, (char *)&lifr) < 0) {
+ logperror_pr(pr, "prefix_update_k: SIOCLIFADDIF");
+ return;
+ }
+ (void) strncpy(pr->pr_name, lifr.lifr_name,
+ sizeof (pr->pr_name));
+ pr->pr_name[sizeof (pr->pr_name) - 1] = '\0';
+ if (debug & D_PREFIX) {
+ logmsg(LOG_DEBUG, "prefix_update_k: new name %s\n",
+ pr->pr_name);
+ }
+ /*
+ * The IFF_TEMPORARY flag might have already been set; if
+ * so, it needs to be or'd into the flags we're turning on.
+ * But be careful, we might be re-creating a manually
+ * removed interface, in which case we don't want to try
+ * to set *all* the flags we might have in our copy of the
+ * flags yet.
+ */
+ onflags = IFF_ADDRCONF;
+ if (pr->pr_flags & IFF_TEMPORARY)
+ onflags |= IFF_TEMPORARY;
+ if (prefix_modify_flags(pr, onflags, 0) == -1)
+ return;
+ }
+ if ((pr->pr_state & (PR_ONLINK|PR_AUTO)) == 0) {
+ /* Remove the interface */
+ if (prefix_modify_flags(pr, 0, IFF_UP|IFF_DEPRECATED) == -1)
+ return;
+ (void) strncpy(lifr.lifr_name, pr->pr_name,
+ sizeof (lifr.lifr_name));
+ lifr.lifr_name[sizeof (lifr.lifr_name) - 1] = '\0';
+
+ if (debug & D_PREFIX) {
+ logmsg(LOG_DEBUG, "prefix_update_k: remove name %s\n",
+ pr->pr_name);
+ }
+
+ /*
+ * Assumes that only the PR_STATIC link-local matches
+ * the pi_name
+ */
+ if (!(pr->pr_state & PR_STATIC) &&
+ strcmp(pr->pr_name, pi->pi_name) == 0) {
+ logmsg(LOG_ERR, "prefix_update_k(%s): "
+ "name matches if\n", pi->pi_name);
+ return;
+ }
+
+ /* Remove logical interface based on pr_name */
+ lifr.lifr_addr.ss_family = AF_UNSPEC;
+ if (ioctl(pi->pi_sock, SIOCLIFREMOVEIF, (char *)&lifr) < 0) {
+ logperror_pr(pr, "prefix_update_k: SIOCLIFREMOVEIF");
+ }
+ pr->pr_kernel_state = 0;
+ pr->pr_name[0] = '\0';
+ return;
+ }
+ if ((pr->pr_state & PR_AUTO) && !(pr->pr_kernel_state & PR_AUTO)) {
+ /*
+ * Set local address and set the prefix length to 128.
+ * Turn off IFF_NOLOCAL in case it was set.
+ * Turn on IFF_UP.
+ */
+ (void) strncpy(lifr.lifr_name, pr->pr_name,
+ sizeof (lifr.lifr_name));
+ lifr.lifr_name[sizeof (lifr.lifr_name) - 1] = '\0';
+ sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
+ bzero(sin6, sizeof (struct sockaddr_in6));
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_addr = pr->pr_address;
+ if (debug & D_PREFIX) {
+ logmsg(LOG_DEBUG, "prefix_update_k(%s) set addr %s "
+ "for PR_AUTO on\n",
+ pr->pr_name,
+ inet_ntop(AF_INET6, (void *)&pr->pr_address,
+ abuf, sizeof (abuf)));
+ }
+ if (ioctl(pi->pi_sock, SIOCSLIFADDR, (char *)&lifr) < 0) {
+ logperror_pr(pr, "prefix_update_k: SIOCSLIFADDR");
+ return;
+ }
+ if (pr->pr_state & PR_ONLINK) {
+ sin6->sin6_addr = pr->pr_prefix;
+ lifr.lifr_addrlen = pr->pr_prefix_len;
+ } else {
+ sin6->sin6_addr = pr->pr_address;
+ lifr.lifr_addrlen = IPV6_ABITS;
+ }
+ if (debug & D_PREFIX) {
+ logmsg(LOG_DEBUG, "prefix_update_k(%s) set subnet "
+ "%s/%u for PR_AUTO on\n", pr->pr_name,
+ inet_ntop(AF_INET6, (void *)&sin6->sin6_addr,
+ abuf, sizeof (abuf)), lifr.lifr_addrlen);
+ }
+ if (ioctl(pi->pi_sock, SIOCSLIFSUBNET, (char *)&lifr) < 0) {
+ logperror_pr(pr, "prefix_update_k: SIOCSLIFSUBNET");
+ return;
+ }
+ /*
+ * For ptp interfaces, create a destination based on
+ * prefix and prefix len together with the remote token
+ * extracted from the remote pt-pt address. This is used by
+ * ip to choose a proper source for outgoing packets.
+ */
+ if (pi->pi_flags & IFF_POINTOPOINT) {
+ int i;
+
+ sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
+ bzero(sin6, sizeof (struct sockaddr_in6));
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_addr = pr->pr_prefix;
+ for (i = 0; i < 16; i++) {
+ sin6->sin6_addr.s6_addr[i] |=
+ pi->pi_dst_token.s6_addr[i];
+ }
+ if (debug & D_PREFIX) {
+ logmsg(LOG_DEBUG, "prefix_update_k(%s) "
+ "set dstaddr %s for PR_AUTO on\n",
+ pr->pr_name, inet_ntop(AF_INET6,
+ (void *)&sin6->sin6_addr,
+ abuf, sizeof (abuf)));
+ }
+ if (ioctl(pi->pi_sock, SIOCSLIFDSTADDR,
+ (char *)&lifr) < 0) {
+ logperror_pr(pr,
+ "prefix_update_k: SIOCSLIFDSTADDR");
+ return;
+ }
+ }
+ if (prefix_modify_flags(pr, IFF_UP, IFF_NOLOCAL) == -1)
+ return;
+ pr->pr_kernel_state |= PR_AUTO;
+ if (pr->pr_state & PR_ONLINK)
+ pr->pr_kernel_state |= PR_ONLINK;
+ else
+ pr->pr_kernel_state &= ~PR_ONLINK;
+ }
+ if (!(pr->pr_state & PR_AUTO) && (pr->pr_kernel_state & PR_AUTO)) {
+ /* Turn on IFF_NOLOCAL and set the local address to all zero */
+ if (prefix_modify_flags(pr, IFF_NOLOCAL, 0) == -1)
+ return;
+ (void) strncpy(lifr.lifr_name, pr->pr_name,
+ sizeof (lifr.lifr_name));
+ lifr.lifr_name[sizeof (lifr.lifr_name) - 1] = '\0';
+ sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
+ bzero(sin6, sizeof (struct sockaddr_in6));
+ sin6->sin6_family = AF_INET6;
+ if (debug & D_PREFIX) {
+ logmsg(LOG_DEBUG, "prefix_update_k(%s) set addr %s "
+ "for PR_AUTO off\n", pr->pr_name,
+ inet_ntop(AF_INET6, (void *)&sin6->sin6_addr,
+ abuf, sizeof (abuf)));
+ }
+ if (ioctl(pi->pi_sock, SIOCSLIFADDR, (char *)&lifr) < 0) {
+ logperror_pr(pr, "prefix_update_k: SIOCSLIFADDR");
+ return;
+ }
+ pr->pr_kernel_state &= ~PR_AUTO;
+ }
+ if ((pr->pr_state & PR_DEPRECATED) &&
+ !(pr->pr_kernel_state & PR_DEPRECATED) &&
+ (pr->pr_kernel_state & PR_AUTO)) {
+ /* Only applies if PR_AUTO */
+ if (prefix_modify_flags(pr, IFF_DEPRECATED, 0) == -1)
+ return;
+ pr->pr_kernel_state |= PR_DEPRECATED;
+ }
+ if (!(pr->pr_state & PR_DEPRECATED) &&
+ (pr->pr_kernel_state & PR_DEPRECATED)) {
+ if (prefix_modify_flags(pr, 0, IFF_DEPRECATED) == -1)
+ return;
+ pr->pr_kernel_state &= ~PR_DEPRECATED;
+ }
+ if ((pr->pr_state & PR_ONLINK) && !(pr->pr_kernel_state & PR_ONLINK)) {
+ /* Set the subnet and set IFF_UP */
+ (void) strncpy(lifr.lifr_name, pr->pr_name,
+ sizeof (lifr.lifr_name));
+ lifr.lifr_name[sizeof (lifr.lifr_name) - 1] = '\0';
+ sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
+ bzero(sin6, sizeof (struct sockaddr_in6));
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_addr = pr->pr_prefix;
+ lifr.lifr_addrlen = pr->pr_prefix_len;
+ if (debug & D_PREFIX) {
+ logmsg(LOG_DEBUG, "prefix_update_k(%s) set subnet "
+ "%s/%d for PR_ONLINK on\n", pr->pr_name,
+ inet_ntop(AF_INET6, (void *)&sin6->sin6_addr,
+ abuf, sizeof (abuf)), lifr.lifr_addrlen);
+ }
+ if (ioctl(pi->pi_sock, SIOCSLIFSUBNET, (char *)&lifr) < 0) {
+ logperror_pr(pr, "prefix_update_k: SIOCSLIFSUBNET");
+ return;
+ }
+ if (!(pr->pr_state & PR_AUTO)) {
+ if (prefix_modify_flags(pr, IFF_NOLOCAL, 0) == -1)
+ return;
+ }
+ if (prefix_modify_flags(pr, IFF_UP, 0) == -1)
+ return;
+ pr->pr_kernel_state |= PR_ONLINK;
+ }
+ if (!(pr->pr_state & PR_ONLINK) && (pr->pr_kernel_state & PR_ONLINK)) {
+ /* Set the prefixlen to 128 */
+ (void) strncpy(lifr.lifr_name, pr->pr_name,
+ sizeof (lifr.lifr_name));
+ lifr.lifr_name[sizeof (lifr.lifr_name) - 1] = '\0';
+ sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
+ bzero(sin6, sizeof (struct sockaddr_in6));
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_addr = pr->pr_address;
+ lifr.lifr_addrlen = IPV6_ABITS;
+ if (debug & D_PREFIX) {
+ logmsg(LOG_DEBUG, "prefix_update_k(%s) set subnet "
+ "%s/%d for PR_ONLINK off\n", pr->pr_name,
+ inet_ntop(AF_INET6, (void *)&sin6->sin6_addr,
+ abuf, sizeof (abuf)), lifr.lifr_addrlen);
+ }
+ if (ioctl(pi->pi_sock, SIOCSLIFSUBNET, (char *)&lifr) < 0) {
+ logperror_pr(pr, "prefix_update_k: SIOCSLIFSUBNET");
+ return;
+ }
+ pr->pr_kernel_state &= ~PR_ONLINK;
+ }
+}
+
+/*
+ * Called with the number of millseconds elapsed since the last call.
+ * Determines if any timeout event has occurred and
+ * returns the number of milliseconds until the next timeout event.
+ * Returns TIMER_INFINITY for "never".
+ */
+uint_t
+prefix_timer(struct prefix *pr, uint_t elapsed)
+{
+ uint_t next = TIMER_INFINITY;
+ char abuf[INET6_ADDRSTRLEN];
+
+ if (debug & (D_PREFIX|D_TMP)) {
+ logmsg(LOG_DEBUG, "prefix_timer(%s, %s/%u, %d) "
+ "valid %d pref %d onlink %d\n",
+ pr->pr_name,
+ inet_ntop(AF_INET6, (void *)&pr->pr_prefix,
+ abuf, sizeof (abuf)), pr->pr_prefix_len,
+ elapsed, pr->pr_ValidLifetime, pr->pr_PreferredLifetime,
+ pr->pr_OnLinkLifetime);
+ }
+
+ /* Exclude static prefixes */
+ if (pr->pr_state & PR_STATIC)
+ return (next);
+
+ if (pr->pr_AutonomousFlag &&
+ (pr->pr_PreferredLifetime != PREFIX_INFINITY)) {
+ if (pr->pr_PreferredLifetime <= elapsed) {
+ pr->pr_PreferredLifetime = 0;
+ } else {
+ pr->pr_PreferredLifetime -= elapsed;
+ if (pr->pr_PreferredLifetime < next)
+ next = pr->pr_PreferredLifetime;
+ }
+ }
+ if (pr->pr_AutonomousFlag &&
+ (pr->pr_ValidLifetime != PREFIX_INFINITY)) {
+ if (pr->pr_ValidLifetime <= elapsed) {
+ pr->pr_ValidLifetime = 0;
+ } else {
+ pr->pr_ValidLifetime -= elapsed;
+ if (pr->pr_ValidLifetime < next)
+ next = pr->pr_ValidLifetime;
+ }
+ }
+ if (pr->pr_OnLinkFlag &&
+ (pr->pr_OnLinkLifetime != PREFIX_INFINITY)) {
+ if (pr->pr_OnLinkLifetime <= elapsed) {
+ pr->pr_OnLinkLifetime = 0;
+ } else {
+ pr->pr_OnLinkLifetime -= elapsed;
+ if (pr->pr_OnLinkLifetime < next)
+ next = pr->pr_OnLinkLifetime;
+ }
+ }
+ if (pr->pr_AutonomousFlag && pr->pr_ValidLifetime == 0)
+ pr->pr_state &= ~(PR_AUTO|PR_DEPRECATED);
+ if (pr->pr_AutonomousFlag && pr->pr_PreferredLifetime == 0 &&
+ (pr->pr_state & PR_AUTO)) {
+ pr->pr_state |= PR_DEPRECATED;
+ if (debug & D_TMP)
+ logmsg(LOG_WARNING, "prefix_timer: deprecated "
+ "prefix(%s)\n", pr->pr_name);
+ }
+ if (pr->pr_OnLinkFlag && pr->pr_OnLinkLifetime == 0)
+ pr->pr_state &= ~PR_ONLINK;
+
+ if (pr->pr_state != pr->pr_kernel_state) {
+ /* Might cause prefix to be deleted! */
+
+ /* Log a message when an addrconf prefix goes away */
+ if ((pr->pr_kernel_state & PR_AUTO) &&
+ !(pr->pr_state & PR_AUTO)) {
+ char abuf[INET6_ADDRSTRLEN];
+
+ logmsg(LOG_WARNING,
+ "Address removed due to timeout %s\n",
+ inet_ntop(AF_INET6, (void *)&pr->pr_address,
+ abuf, sizeof (abuf)));
+ }
+ prefix_update_k(pr);
+ }
+
+ return (next);
+}
+
+static char *
+prefix_print_state(int state, char *buf, int buflen)
+{
+ char *cp;
+ int cplen = buflen;
+
+ cp = buf;
+ cp[0] = '\0';
+
+ if (state & PR_ONLINK) {
+ if (strlcat(cp, "ONLINK ", cplen) >= cplen)
+ return (buf);
+ cp += strlen(cp);
+ cplen = buflen - (cp - buf);
+ }
+ if (state & PR_AUTO) {
+ if (strlcat(cp, "AUTO ", cplen) >= cplen)
+ return (buf);
+ cp += strlen(cp);
+ cplen = buflen - (cp - buf);
+ }
+ if (state & PR_DEPRECATED) {
+ if (strlcat(cp, "DEPRECATED ", cplen) >= cplen)
+ return (buf);
+ cp += strlen(cp);
+ cplen = buflen - (cp - buf);
+ }
+ if (state & PR_STATIC) {
+ if (strlcat(cp, "STATIC ", cplen) >= cplen)
+ return (buf);
+ cp += strlen(cp);
+ cplen = buflen - (cp - buf);
+ }
+ return (buf);
+}
+
+static void
+prefix_print(struct prefix *pr)
+{
+ char abuf[INET6_ADDRSTRLEN];
+ char buf1[PREFIX_STATESTRLEN], buf2[PREFIX_STATESTRLEN];
+
+ logmsg(LOG_DEBUG, "Prefix name: %s prefix %s/%u state %s "
+ "kernel_state %s\n", pr->pr_name,
+ inet_ntop(AF_INET6, (void *)&pr->pr_prefix, abuf, sizeof (abuf)),
+ pr->pr_prefix_len,
+ prefix_print_state(pr->pr_state, buf2, sizeof (buf2)),
+ prefix_print_state(pr->pr_kernel_state, buf1, sizeof (buf1)));
+ logmsg(LOG_DEBUG, "\tAddress: %s flags %llx in_use %d\n",
+ inet_ntop(AF_INET6, (void *)&pr->pr_address, abuf, sizeof (abuf)),
+ pr->pr_flags, pr->pr_in_use);
+ logmsg(LOG_DEBUG, "\tValidLifetime %u PreferredLifetime %u "
+ "OnLinkLifetime %u\n", pr->pr_ValidLifetime,
+ pr->pr_PreferredLifetime, pr->pr_OnLinkLifetime);
+ logmsg(LOG_DEBUG, "\tOnLink %d Auto %d\n",
+ pr->pr_OnLinkFlag, pr->pr_AutonomousFlag);
+ logmsg(LOG_DEBUG, "\n");
+}
+
+/*
+ * Does the address formed by pr->pr_prefix and pi->pi_token match
+ * pr->pr_address. It does not match if a failover has happened
+ * earlier (done by in.mpathd) from a different pi. Should not
+ * be called for onlink prefixes.
+ */
+boolean_t
+prefix_token_match(struct phyint *pi, struct prefix *pr, uint64_t flags)
+{
+ int i;
+ in6_addr_t addr, *token;
+
+ if (flags & IFF_TEMPORARY)
+ token = &pi->pi_tmp_token;
+ else
+ token = &pi->pi_token;
+ for (i = 0; i < 16; i++) {
+ /*
+ * prefix_create ensures that pr_prefix has all-zero
+ * bits after prefixlen.
+ */
+ addr.s6_addr[i] = pr->pr_prefix.s6_addr[i] | token->s6_addr[i];
+ }
+ if (IN6_ARE_ADDR_EQUAL(&pr->pr_address, &addr)) {
+ return (_B_TRUE);
+ } else {
+ return (_B_FALSE);
+ }
+}
+
+/*
+ * Lookup advertisement prefix structure that matches the prefix and
+ * prefix length.
+ * Assumes that the bits after prefixlen might not be zero.
+ */
+struct adv_prefix *
+adv_prefix_lookup(struct phyint *pi, struct in6_addr prefix, int prefixlen)
+{
+ struct adv_prefix *adv_pr;
+ char abuf[INET6_ADDRSTRLEN];
+
+ if (debug & D_PREFIX) {
+ logmsg(LOG_DEBUG, "adv_prefix_lookup(%s, %s/%u)\n",
+ pi->pi_name, inet_ntop(AF_INET6, (void *)&prefix,
+ abuf, sizeof (abuf)), prefixlen);
+ }
+
+ for (adv_pr = pi->pi_adv_prefix_list; adv_pr != NULL;
+ adv_pr = adv_pr->adv_pr_next) {
+ if (adv_pr->adv_pr_prefix_len == prefixlen &&
+ prefix_equal(prefix, adv_pr->adv_pr_prefix, prefixlen))
+ return (adv_pr);
+ }
+ return (NULL);
+}
+
+/*
+ * Initialize a new advertisement prefix.
+ */
+struct adv_prefix *
+adv_prefix_create(struct phyint *pi, struct in6_addr prefix, int prefixlen)
+{
+ struct adv_prefix *adv_pr;
+ char abuf[INET6_ADDRSTRLEN];
+
+ if (debug & D_PREFIX) {
+ logmsg(LOG_DEBUG, "adv_prefix_create(%s, %s/%u)\n",
+ pi->pi_name, inet_ntop(AF_INET6, (void *)&prefix,
+ abuf, sizeof (abuf)), prefixlen);
+ }
+ adv_pr = (struct adv_prefix *)calloc(sizeof (struct adv_prefix), 1);
+ if (adv_pr == NULL) {
+ logmsg(LOG_ERR, "adv_prefix_create: calloc\n");
+ return (NULL);
+ }
+ /*
+ * The prefix might have non-zero bits after the prefix len bits.
+ * Force them to be zero.
+ */
+ prefix_set(&adv_pr->adv_pr_prefix, prefix, prefixlen);
+ adv_pr->adv_pr_prefix_len = prefixlen;
+ adv_prefix_insert(pi, adv_pr);
+ return (adv_pr);
+}
+
+/* Insert in linked list */
+static void
+adv_prefix_insert(struct phyint *pi, struct adv_prefix *adv_pr)
+{
+ adv_pr->adv_pr_next = pi->pi_adv_prefix_list;
+ adv_pr->adv_pr_prev = NULL;
+ if (pi->pi_adv_prefix_list != NULL)
+ pi->pi_adv_prefix_list->adv_pr_prev = adv_pr;
+ pi->pi_adv_prefix_list = adv_pr;
+ adv_pr->adv_pr_physical = pi;
+}
+
+/*
+ * Delete (unlink and free) from our tables. There should be
+ * a corresponding "struct prefix *" which will clean up the kernel
+ * if necessary. adv_prefix is just used for sending out advertisements.
+ */
+static void
+adv_prefix_delete(struct adv_prefix *adv_pr)
+{
+ struct phyint *pi;
+ char abuf[INET6_ADDRSTRLEN];
+
+ if (debug & D_PREFIX) {
+ logmsg(LOG_DEBUG, "adv_prefix_delete(%s, %s/%u)\n",
+ adv_pr->adv_pr_physical->pi_name,
+ inet_ntop(AF_INET6, (void *)&adv_pr->adv_pr_prefix,
+ abuf, sizeof (abuf)), adv_pr->adv_pr_prefix_len);
+ }
+ pi = adv_pr->adv_pr_physical;
+
+ if (adv_pr->adv_pr_prev == NULL) {
+ if (pi != NULL)
+ pi->pi_adv_prefix_list = adv_pr->adv_pr_next;
+ } else {
+ adv_pr->adv_pr_prev->adv_pr_next = adv_pr->adv_pr_next;
+ }
+ if (adv_pr->adv_pr_next != NULL)
+ adv_pr->adv_pr_next->adv_pr_prev = adv_pr->adv_pr_prev;
+ adv_pr->adv_pr_next = adv_pr->adv_pr_prev = NULL;
+ free(adv_pr);
+}
+
+/*
+ * Called with the number of millseconds elapsed since the last call.
+ * Determines if any timeout event has occurred and
+ * returns the number of milliseconds until the next timeout event.
+ * Returns TIMER_INFINITY for "never".
+ */
+uint_t
+adv_prefix_timer(struct adv_prefix *adv_pr, uint_t elapsed)
+{
+ int seconds_elapsed = (elapsed + 500) / 1000; /* Rounded */
+ char abuf[INET6_ADDRSTRLEN];
+
+ if (debug & D_PREFIX) {
+ logmsg(LOG_DEBUG, "adv_prefix_timer(%s, %s/%u, %d)\n",
+ adv_pr->adv_pr_physical->pi_name,
+ inet_ntop(AF_INET6, (void *)&adv_pr->adv_pr_prefix,
+ abuf, sizeof (abuf)), adv_pr->adv_pr_prefix_len,
+ elapsed);
+ }
+
+ /* Decrement Expire time left for real-time lifetimes */
+ if (adv_pr->adv_pr_AdvValidRealTime) {
+ if (adv_pr->adv_pr_AdvValidExpiration > seconds_elapsed)
+ adv_pr->adv_pr_AdvValidExpiration -= seconds_elapsed;
+ else
+ adv_pr->adv_pr_AdvValidExpiration = 0;
+ }
+ if (adv_pr->adv_pr_AdvPreferredRealTime) {
+ if (adv_pr->adv_pr_AdvPreferredExpiration > seconds_elapsed) {
+ adv_pr->adv_pr_AdvPreferredExpiration -=
+ seconds_elapsed;
+ } else {
+ adv_pr->adv_pr_AdvPreferredExpiration = 0;
+ }
+ }
+ return (TIMER_INFINITY);
+}
+
+static void
+adv_prefix_print(struct adv_prefix *adv_pr)
+{
+ print_prefixlist(adv_pr->adv_pr_config);
+}
+
+/* Lookup router on its link-local IPv6 address */
+struct router *
+router_lookup(struct phyint *pi, struct in6_addr addr)
+{
+ struct router *dr;
+ char abuf[INET6_ADDRSTRLEN];
+
+ if (debug & D_ROUTER) {
+ logmsg(LOG_DEBUG, "router_lookup(%s, %s)\n", pi->pi_name,
+ inet_ntop(AF_INET6, (void *)&addr,
+ abuf, sizeof (abuf)));
+ }
+
+ for (dr = pi->pi_router_list; dr != NULL; dr = dr->dr_next) {
+ if (bcmp((char *)&addr, (char *)&dr->dr_address,
+ sizeof (addr)) == 0)
+ return (dr);
+ }
+ return (NULL);
+}
+
+/*
+ * Create a default router entry.
+ * The lifetime parameter is in seconds.
+ */
+struct router *
+router_create(struct phyint *pi, struct in6_addr addr, uint_t lifetime)
+{
+ struct router *dr;
+ char abuf[INET6_ADDRSTRLEN];
+
+ if (debug & D_ROUTER) {
+ logmsg(LOG_DEBUG, "router_create(%s, %s, %u)\n", pi->pi_name,
+ inet_ntop(AF_INET6, (void *)&addr,
+ abuf, sizeof (abuf)), lifetime);
+ }
+
+ dr = (struct router *)calloc(sizeof (struct router), 1);
+ if (dr == NULL) {
+ logmsg(LOG_ERR, "router_create: out of memory\n");
+ return (NULL);
+ }
+ dr->dr_address = addr;
+ dr->dr_lifetime = lifetime;
+ router_insert(pi, dr);
+ if (dr->dr_lifetime != 0) {
+ /*
+ * Delete an onlink default if it exists since we now have
+ * at least one default router.
+ */
+ if (pi->pi_onlink_default)
+ router_delete_onlink(dr->dr_physical);
+ router_add_k(dr);
+ }
+ return (dr);
+}
+
+/* Insert in linked list */
+static void
+router_insert(struct phyint *pi, struct router *dr)
+{
+ dr->dr_next = pi->pi_router_list;
+ dr->dr_prev = NULL;
+ if (pi->pi_router_list != NULL)
+ pi->pi_router_list->dr_prev = dr;
+ pi->pi_router_list = dr;
+ dr->dr_physical = pi;
+}
+
+/*
+ * Delete (unlink and free).
+ * Handles delete of things that have not yet been inserted in the list
+ * i.e. dr_physical is NULL.
+ */
+static void
+router_delete(struct router *dr)
+{
+ struct phyint *pi;
+ char abuf[INET6_ADDRSTRLEN];
+
+ if (debug & D_ROUTER) {
+ logmsg(LOG_DEBUG, "router_delete(%s, %s, %u)\n",
+ dr->dr_physical->pi_name,
+ inet_ntop(AF_INET6, (void *)&dr->dr_address,
+ abuf, sizeof (abuf)), dr->dr_lifetime);
+ }
+ pi = dr->dr_physical;
+ if (dr->dr_inkernel) {
+ /*
+ * Create a on-link default route only if the interface
+ * is present in the kernel. This function is called
+ * to clean up the routes when the interface is
+ * unplumbed. In that case, don't try to create one
+ * in the kernel.
+ */
+ if (pi->pi_kernel_state & PI_PRESENT) {
+ if (!dr->dr_onlink &&
+ dr->dr_physical->pi_num_k_routers == 1) {
+ (void) router_create_onlink(dr->dr_physical);
+ }
+ router_delete_k(dr);
+ }
+ }
+ if (dr->dr_onlink)
+ pi->pi_onlink_default = _B_FALSE;
+
+ if (dr->dr_prev == NULL) {
+ if (pi != NULL)
+ pi->pi_router_list = dr->dr_next;
+ } else {
+ dr->dr_prev->dr_next = dr->dr_next;
+ }
+ if (dr->dr_next != NULL)
+ dr->dr_next->dr_prev = dr->dr_prev;
+ dr->dr_next = dr->dr_prev = NULL;
+ free(dr);
+}
+
+
+/* Create an onlink default route */
+struct router *
+router_create_onlink(struct phyint *pi)
+{
+ struct router *dr;
+ struct prefix *pr;
+
+ if (debug & D_ROUTER) {
+ logmsg(LOG_DEBUG, "router_create_onlink(%s)\n", pi->pi_name);
+ }
+
+ if (pi->pi_onlink_default) {
+ logmsg(LOG_ERR, "router_create_onlink: already an onlink "
+ "default: %s\n", pi->pi_name);
+ return (NULL);
+ }
+
+ /*
+ * Find the interface address to use for the route gateway.
+ * We need to use the link-local since the others ones might be
+ * deleted when the prefixes get invalidated.
+ */
+ for (pr = pi->pi_prefix_list; pr != NULL; pr = pr->pr_next) {
+ if ((pr->pr_state & PR_AUTO) &&
+ IN6_IS_ADDR_LINKLOCAL(&pr->pr_address))
+ break;
+ }
+ if (pr == NULL) {
+ logmsg(LOG_ERR, "router_create_onlink: no source address\n");
+ return (NULL);
+ }
+ dr = (struct router *)calloc(sizeof (struct router), 1);
+ if (dr == NULL) {
+ logmsg(LOG_ERR, "router_create_onlink: out of memory\n");
+ return (NULL);
+ }
+ dr->dr_address = pr->pr_address;
+ dr->dr_lifetime = 1; /* Not used */
+ dr->dr_onlink = _B_TRUE;
+ router_insert(pi, dr);
+
+ router_add_k(dr);
+ pi->pi_onlink_default = _B_TRUE;
+ return (dr);
+}
+
+/* Remove an onlink default route */
+static void
+router_delete_onlink(struct phyint *pi)
+{
+ struct router *dr, *next_dr;
+
+ if (debug & D_ROUTER) {
+ logmsg(LOG_DEBUG, "router_delete_onlink(%s)\n", pi->pi_name);
+ }
+
+ if (!pi->pi_onlink_default) {
+ logmsg(LOG_ERR, "router_delete_onlink: no onlink default: "
+ "%s\n", pi->pi_name);
+ return;
+ }
+ /* Find all onlink routes */
+ for (dr = pi->pi_router_list; dr != NULL; dr = next_dr) {
+ next_dr = dr->dr_next;
+ if (dr->dr_onlink)
+ router_delete(dr);
+ }
+}
+
+/*
+ * Update the kernel to match dr_lifetime
+ */
+void
+router_update_k(struct router *dr)
+{
+ char abuf[INET6_ADDRSTRLEN];
+
+ if (debug & D_ROUTER) {
+ logmsg(LOG_DEBUG, "router_update_k(%s, %s, %u)\n",
+ dr->dr_physical->pi_name,
+ inet_ntop(AF_INET6, (void *)&dr->dr_address,
+ abuf, sizeof (abuf)), dr->dr_lifetime);
+ }
+
+ if (dr->dr_lifetime == 0 && dr->dr_inkernel) {
+ /* Log a message when last router goes away */
+ if (dr->dr_physical->pi_num_k_routers == 1) {
+ logmsg(LOG_WARNING,
+ "Last default router (%s) removed on %s\n",
+ inet_ntop(AF_INET6, (void *)&dr->dr_address,
+ abuf, sizeof (abuf)), dr->dr_physical->pi_name);
+ }
+ router_delete(dr);
+ } else if (dr->dr_lifetime != 0 && !dr->dr_inkernel) {
+ /*
+ * Delete an onlink default if it exists since we now have
+ * at least one default router.
+ */
+ if (dr->dr_physical->pi_onlink_default)
+ router_delete_onlink(dr->dr_physical);
+ router_add_k(dr);
+ }
+}
+
+
+/*
+ * Called with the number of millseconds elapsed since the last call.
+ * Determines if any timeout event has occurred and
+ * returns the number of milliseconds until the next timeout event.
+ * Returns TIMER_INFINITY for "never".
+ */
+uint_t
+router_timer(struct router *dr, uint_t elapsed)
+{
+ uint_t next = TIMER_INFINITY;
+ char abuf[INET6_ADDRSTRLEN];
+
+ if (debug & D_ROUTER) {
+ logmsg(LOG_DEBUG, "router_timer(%s, %s, %u, %d)\n",
+ dr->dr_physical->pi_name,
+ inet_ntop(AF_INET6, (void *)&dr->dr_address,
+ abuf, sizeof (abuf)), dr->dr_lifetime, elapsed);
+ }
+ if (dr->dr_onlink) {
+ /* No timeout */
+ return (next);
+ }
+ if (dr->dr_lifetime <= elapsed) {
+ dr->dr_lifetime = 0;
+ } else {
+ dr->dr_lifetime -= elapsed;
+ if (dr->dr_lifetime < next)
+ next = dr->dr_lifetime;
+ }
+
+ if (dr->dr_lifetime == 0) {
+ /* Log a message when last router goes away */
+ if (dr->dr_physical->pi_num_k_routers == 1) {
+ logmsg(LOG_WARNING,
+ "Last default router (%s) timed out on %s\n",
+ inet_ntop(AF_INET6, (void *)&dr->dr_address,
+ abuf, sizeof (abuf)), dr->dr_physical->pi_name);
+ }
+ router_delete(dr);
+ }
+ return (next);
+}
+
+/*
+ * Add a default route to the kernel (unless the lifetime is zero)
+ * Handles onlink default routes.
+ */
+static void
+router_add_k(struct router *dr)
+{
+ struct phyint *pi = dr->dr_physical;
+ char abuf[INET6_ADDRSTRLEN];
+ int rlen;
+
+ if (debug & D_ROUTER) {
+ logmsg(LOG_DEBUG, "router_add_k(%s, %s, %u)\n",
+ dr->dr_physical->pi_name,
+ inet_ntop(AF_INET6, (void *)&dr->dr_address,
+ abuf, sizeof (abuf)), dr->dr_lifetime);
+ }
+
+ if (dr->dr_onlink)
+ rt_msg->rtm_flags = 0;
+ else
+ rt_msg->rtm_flags = RTF_GATEWAY;
+
+ rta_gateway->sin6_addr = dr->dr_address;
+
+ rta_ifp->sdl_index = if_nametoindex(pi->pi_name);
+ if (rta_ifp->sdl_index == 0) {
+ logperror_pi(pi, "router_add_k: if_nametoindex");
+ return;
+ }
+
+ rt_msg->rtm_type = RTM_ADD;
+ rt_msg->rtm_seq = ++rtmseq;
+ rlen = write(rtsock, rt_msg, rt_msg->rtm_msglen);
+ if (rlen < 0) {
+ if (errno != EEXIST) {
+ logperror_pi(pi, "router_add_k: RTM_ADD");
+ return;
+ }
+ } else if (rlen < rt_msg->rtm_msglen) {
+ logmsg(LOG_ERR, "router_add_k: write to routing socket got "
+ "only %d for rlen (interface %s)\n", rlen, pi->pi_name);
+ return;
+ }
+ dr->dr_inkernel = _B_TRUE;
+ if (!dr->dr_onlink)
+ pi->pi_num_k_routers++;
+}
+
+/*
+ * Delete a route from the kernel.
+ * Handles onlink default routes.
+ */
+static void
+router_delete_k(struct router *dr)
+{
+ struct phyint *pi = dr->dr_physical;
+ char abuf[INET6_ADDRSTRLEN];
+ int rlen;
+
+ if (debug & D_ROUTER) {
+ logmsg(LOG_DEBUG, "router_delete_k(%s, %s, %u)\n",
+ dr->dr_physical->pi_name,
+ inet_ntop(AF_INET6, (void *)&dr->dr_address,
+ abuf, sizeof (abuf)), dr->dr_lifetime);
+ }
+
+ if (dr->dr_onlink)
+ rt_msg->rtm_flags = 0;
+ else
+ rt_msg->rtm_flags = RTF_GATEWAY;
+
+ rta_gateway->sin6_addr = dr->dr_address;
+
+ rta_ifp->sdl_index = if_nametoindex(pi->pi_name);
+ if (rta_ifp->sdl_index == 0) {
+ logperror_pi(pi, "router_delete_k: if_nametoindex");
+ return;
+ }
+
+ rt_msg->rtm_type = RTM_DELETE;
+ rt_msg->rtm_seq = ++rtmseq;
+ rlen = write(rtsock, rt_msg, rt_msg->rtm_msglen);
+ if (rlen < 0) {
+ if (errno != ESRCH) {
+ logperror_pi(pi, "router_delete_k: RTM_DELETE");
+ }
+ } else if (rlen < rt_msg->rtm_msglen) {
+ logmsg(LOG_ERR, "router_delete_k: write to routing socket got "
+ "only %d for rlen (interface %s)\n", rlen, pi->pi_name);
+ }
+ dr->dr_inkernel = _B_FALSE;
+ if (!dr->dr_onlink)
+ pi->pi_num_k_routers--;
+}
+
+
+static void
+router_print(struct router *dr)
+{
+ char abuf[INET6_ADDRSTRLEN];
+
+ logmsg(LOG_DEBUG, "Router %s on %s inkernel %d onlink %d lifetime %u\n",
+ inet_ntop(AF_INET6, (void *)&dr->dr_address,
+ abuf, sizeof (abuf)),
+ dr->dr_physical->pi_name,
+ dr->dr_inkernel, dr->dr_onlink, dr->dr_lifetime);
+}
+
+
+void
+phyint_print_all(void)
+{
+ struct phyint *pi;
+
+ for (pi = phyints; pi != NULL; pi = pi->pi_next) {
+ phyint_print(pi);
+ }
+}
+
+void
+phyint_cleanup(pi)
+ struct phyint *pi;
+{
+ pi->pi_state = 0;
+ pi->pi_kernel_state = 0;
+
+ if (pi->pi_AdvSendAdvertisements) {
+ check_to_advertise(pi, ADV_OFF);
+ } else {
+ check_to_solicit(pi, SOLICIT_OFF);
+ }
+
+ while (pi->pi_router_list)
+ router_delete(pi->pi_router_list);
+ (void) poll_remove(pi->pi_sock);
+ (void) close(pi->pi_sock);
+ pi->pi_sock = -1;
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/tables.h b/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/tables.h
new file mode 100644
index 0000000000..36cfd59f47
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/tables.h
@@ -0,0 +1,366 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _NDPD_TABLES_H
+#define _NDPD_TABLES_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum adv_states { NO_ADV = 0, REG_ADV, INIT_ADV, SOLICIT_ADV, FINAL_ADV };
+enum adv_events { ADV_OFF, START_INIT_ADV, START_FINAL_ADV, RECEIVED_SOLICIT,
+ ADV_TIMER };
+
+enum solicit_states { NO_SOLICIT = 0, INIT_SOLICIT, DONE_SOLICIT };
+enum solicit_events { SOLICIT_OFF, START_INIT_SOLICIT, SOL_TIMER,
+ SOLICIT_DONE };
+
+/*
+ * Data structures used to handle configuration variables set in ndpd.conf.
+ * cf_notdefault is set for variables explicitly set in ndpd.conf.
+ */
+struct confvar {
+ uint_t cf_value;
+ boolean_t cf_notdefault;
+};
+
+extern struct confvar ifdefaults[];
+
+/*
+ * Interfaces configuration variable indicies
+ */
+#define I_DupAddrDetectTransmits 0 /* From RFC 2462 */
+#define I_AdvSendAdvertisements 1
+#define I_MaxRtrAdvInterval 2 /* In seconds */
+#define I_MinRtrAdvInterval 3 /* In seconds */
+#define I_AdvManagedFlag 4
+#define I_AdvOtherConfigFlag 5
+#define I_AdvLinkMTU 6
+#define I_AdvReachableTime 7 /* In milliseconds */
+#define I_AdvRetransTimer 8 /* In milliseconds */
+#define I_AdvCurHopLimit 9
+#define I_AdvDefaultLifetime 10 /* In seconds */
+#define I_StatelessAddrConf 11
+#define I_TmpAddrsEnabled 12 /* From RFC 3041 */
+#define I_TmpValidLifetime 13 /* In seconds */
+#define I_TmpPreferredLifetime 14 /* In seconds */
+#define I_TmpRegenAdvance 15 /* In seconds */
+#define I_TmpMaxDesyncFactor 16 /* In seconds */
+#define I_IFSIZE 17 /* # of variables */
+
+/*
+ * A doubly linked list of all physical interfaces that each contain a
+ * doubly linked list of prefixes (i.e. logical interfaces) and default
+ * routers.
+ */
+struct phyint {
+ struct phyint *pi_next;
+ struct phyint *pi_prev;
+ struct prefix *pi_prefix_list; /* Doubly linked prefixes */
+ struct router *pi_router_list; /* Doubly linked routers */
+ struct adv_prefix *pi_adv_prefix_list; /* Doubly linked adv.prefixes */
+
+ uint_t pi_index; /* Identifier > 0 */
+ char pi_name[LIFNAMSIZ]; /* Used to identify it */
+ int pi_sock; /* For sending and receiving */
+ struct in6_addr pi_ifaddr; /* Local address */
+ uint_t pi_flags; /* IFF_* flags */
+ uint_t pi_hdw_addr_len;
+ uchar_t pi_hdw_addr[ND_MAX_HDW_LEN];
+ uint_t pi_mtu; /* From SIOCGLIFMTU */
+ struct in6_addr pi_token;
+ uint_t pi_token_length;
+ struct in6_addr pi_tmp_token; /* For RFC3041 addrs */
+ struct in6_addr pi_dst_token; /* For POINTOPOINT */
+
+ uint_t pi_state; /* PI_* below */
+ uint_t pi_kernel_state; /* PI_* below */
+ boolean_t pi_onlink_default; /* Has onlink default route */
+ uint_t pi_num_k_routers; /* # routers in kernel */
+ uint_t pi_reach_time_since_random; /* In milliseconds */
+
+ /* Applies if pi_AdvSendAdvertisements */
+ uint_t pi_adv_time_left; /* In milliseconds */
+ uint_t pi_adv_time_since_sent; /* In milliseconds */
+ enum adv_states pi_adv_state;
+ uint_t pi_adv_count;
+
+ /* Applies if not pi_AdvSendAdvertisements */
+ uint_t pi_sol_time_left; /* In milliseconds */
+ enum solicit_states pi_sol_state;
+ uint_t pi_sol_count;
+
+ /* Interface specific configurable variables */
+ struct confvar pi_config[I_IFSIZE];
+#define pi_DupAddrDetectTransmits pi_config[I_DupAddrDetectTransmits].cf_value
+#define pi_AdvSendAdvertisements pi_config[I_AdvSendAdvertisements].cf_value
+#define pi_MaxRtrAdvInterval pi_config[I_MaxRtrAdvInterval].cf_value
+#define pi_MinRtrAdvInterval pi_config[I_MinRtrAdvInterval].cf_value
+#define pi_AdvManagedFlag pi_config[I_AdvManagedFlag].cf_value
+#define pi_AdvOtherConfigFlag pi_config[I_AdvOtherConfigFlag].cf_value
+#define pi_AdvLinkMTU pi_config[I_AdvLinkMTU].cf_value
+#define pi_AdvReachableTime pi_config[I_AdvReachableTime].cf_value
+#define pi_AdvRetransTimer pi_config[I_AdvRetransTimer].cf_value
+#define pi_AdvCurHopLimit pi_config[I_AdvCurHopLimit].cf_value
+#define pi_AdvDefaultLifetime pi_config[I_AdvDefaultLifetime].cf_value
+#define pi_StatelessAddrConf pi_config[I_StatelessAddrConf].cf_value
+#define pi_TmpAddrsEnabled pi_config[I_TmpAddrsEnabled].cf_value
+#define pi_TmpValidLifetime pi_config[I_TmpValidLifetime].cf_value
+#define pi_TmpPreferredLifetime pi_config[I_TmpPreferredLifetime].cf_value
+#define pi_TmpRegenAdvance pi_config[I_TmpRegenAdvance].cf_value
+#define pi_TmpMaxDesyncFactor pi_config[I_TmpMaxDesyncFactor].cf_value
+
+ /* Recorded variables for RFC3041 addresses */
+ uint_t pi_TmpDesyncFactor; /* In milliseconds */
+ uint_t pi_TmpRegenCountdown; /* In milliseconds */
+
+ /* Recorded variables on node/host */
+ uint_t pi_LinkMTU;
+ uint_t pi_CurHopLimit;
+ uint_t pi_BaseReachableTime; /* In milliseconds */
+ uint_t pi_ReachableTime; /* In milliseconds */
+ /*
+ * The above value should be a uniformly-distributed random
+ * value between ND_MIN_RANDOM_FACTOR and
+ * ND_MAX_RANDOM_FACTOR times BaseReachableTime
+ * milliseconds. A new random value should be
+ * calculated when BaseReachableTime changes (due to
+ * Router Advertisements) or at least every few hours
+ * even if no Router Advertisements are received.
+ * Tracked using pi_each_time_since_random.
+ */
+ uint_t pi_RetransTimer; /* In milliseconds */
+ char *pi_group_name;
+};
+
+/*
+ * pi_state/pr_kernel_state values
+ */
+#define PI_PRESENT 0x01
+#define PI_JOINED_ALLNODES 0x02 /* allnodes multicast joined */
+#define PI_JOINED_ALLROUTERS 0x04 /* allrouters multicast joined */
+
+/*
+ * Prefix configuration variable indices
+ */
+#define I_AdvValidLifetime 0 /* In seconds */
+#define I_AdvOnLinkFlag 1
+#define I_AdvPreferredLifetime 2 /* In seconds */
+#define I_AdvAutonomousFlag 3
+#define I_AdvValidExpiration 4 /* Seconds left */
+#define I_AdvPreferredExpiration 5 /* Seconds left */
+#define I_PREFIXSIZE 6 /* # of variables */
+
+/*
+ * A doubly linked list of prefixes for onlink and addrconf.
+ */
+struct prefix {
+ struct prefix *pr_next; /* Next prefix for this physical */
+ struct prefix *pr_prev; /* Prev prefix for this physical */
+ struct phyint *pr_physical; /* Back pointer */
+
+ struct in6_addr pr_prefix; /* Used to indentify prefix */
+ uint_t pr_prefix_len; /* Num bits valid */
+
+ char pr_name[LIFNAMSIZ];
+ struct in6_addr pr_address;
+ uint64_t pr_flags; /* IFF_* flags */
+
+ uint_t pr_state; /* PR_ONLINK | PR_AUTO etc */
+ uint_t pr_kernel_state; /* PR_ONLINK | PR_AUTO etc */
+ boolean_t pr_in_use; /* To detect removed prefixes */
+
+ /* Recorded variables on node/host */
+ uint_t pr_ValidLifetime; /* In ms w/ 2 hour rule */
+ uint_t pr_PreferredLifetime; /* In millseconds */
+ uint_t pr_OnLinkLifetime; /* ms valid w/o 2 hour rule */
+ boolean_t pr_OnLinkFlag;
+ boolean_t pr_AutonomousFlag;
+
+ uint_t pr_CreateTime; /* tmpaddr creation time */
+ /* in SECONDS */
+};
+
+/*
+ * Flags used for pr_kernel_state and pr_state where the latter is
+ * user-level state.
+ */
+#define PR_ONLINK 0x01 /* On-link */
+#define PR_AUTO 0x02 /* Stateless addrconf */
+#define PR_DEPRECATED 0x04 /* Address is deprecated */
+#define PR_STATIC 0x08 /* Not created by ndpd */
+
+/*
+ * The sum of all possible state string lengths, plus terminating
+ * null character; if new states are added, this needs to be updated.
+ * Useful for passing an appropriately sized buffer to prefix_print_state().
+ *
+ * Current strings: "ONLINK ", "AUTO ", "DEPRECATED ", "STATIC ", "\n"
+ * 7 + 5 + 11 + 7 + 1
+ */
+#define PREFIX_STATESTRLEN 31
+
+/* Prefix used for storing advertisement specific stuff */
+struct adv_prefix {
+ struct adv_prefix *adv_pr_next; /* Next prefix */
+ struct adv_prefix *adv_pr_prev; /* Prev prefix */
+ struct phyint *adv_pr_physical; /* Back pointer */
+
+ struct in6_addr adv_pr_prefix; /* Used to indentify prefix */
+ uint_t adv_pr_prefix_len; /* Num bits valid */
+
+ /* Used when sending advertisements */
+ struct confvar adv_pr_config[I_PREFIXSIZE];
+#define adv_pr_AdvValidLifetime adv_pr_config[I_AdvValidLifetime].cf_value
+#define adv_pr_AdvOnLinkFlag adv_pr_config[I_AdvOnLinkFlag].cf_value
+#define adv_pr_AdvPreferredLifetime \
+ adv_pr_config[I_AdvPreferredLifetime].cf_value
+#define adv_pr_AdvAutonomousFlag \
+ adv_pr_config[I_AdvAutonomousFlag].cf_value
+#define adv_pr_AdvValidExpiration \
+ adv_pr_config[I_AdvValidExpiration].cf_value
+#define adv_pr_AdvPreferredExpiration \
+ adv_pr_config[I_AdvPreferredExpiration].cf_value
+ /* The two below are set if the timers decrement in real time */
+#define adv_pr_AdvValidRealTime \
+ adv_pr_config[I_AdvValidExpiration].cf_notdefault
+#define adv_pr_AdvPreferredRealTime \
+ adv_pr_config[I_AdvPreferredExpiration].cf_notdefault
+};
+
+/*
+ * Doubly-linked list of default routers on a phyint.
+ */
+struct router {
+ struct router *dr_next; /* Next router for this physical */
+ struct router *dr_prev; /* Prev router for this physical */
+ struct phyint *dr_physical; /* Back pointer */
+
+ struct in6_addr dr_address; /* Used to identify the router */
+ uint_t dr_lifetime; /* In milliseconds */
+ boolean_t dr_inkernel; /* Route added to kernel */
+ boolean_t dr_onlink; /* Is this the onlink default route? */
+};
+
+/*
+ * Globals
+ */
+extern struct phyint *phyints;
+
+
+/*
+ * Functions
+ */
+extern uint_t getcurrenttime(void);
+
+extern struct phyint *phyint_lookup(char *name);
+extern struct phyint *phyint_lookup_on_index(uint_t ifindex);
+extern struct phyint *phyint_create(char *name);
+extern int phyint_init_from_k(struct phyint *pi);
+extern void phyint_delete(struct phyint *pi);
+extern uint_t phyint_timer(struct phyint *pi, uint_t elapsed);
+extern void phyint_print_all(void);
+extern void phyint_reach_random(struct phyint *pi,
+ boolean_t set_needed);
+extern void phyint_cleanup(struct phyint *pi);
+
+extern boolean_t tmptoken_create(struct phyint *pi);
+extern void tmptoken_delete(struct phyint *pi);
+extern uint_t tmptoken_timer(struct phyint *pi, uint_t elapsed);
+extern boolean_t token_equal(struct in6_addr t1, struct in6_addr t2,
+ int bits);
+
+extern struct prefix *prefix_create(struct phyint *pi, struct in6_addr addr,
+ int addrlen, uint64_t flags);
+extern struct prefix *prefix_lookup_name(struct phyint *pi, char *name);
+extern struct prefix *prefix_lookup_addr_match(struct prefix *pr);
+extern struct prefix *prefix_create_name(struct phyint *pi, char *name);
+extern int prefix_init_from_k(struct prefix *pr);
+extern void prefix_delete(struct prefix *pr);
+extern boolean_t prefix_equal(struct in6_addr p1, struct in6_addr p2,
+ int bits);
+extern void prefix_update_k(struct prefix *pr);
+extern uint_t prefix_timer(struct prefix *pr, uint_t elapsed);
+extern uint_t adv_prefix_timer(struct adv_prefix *adv_pr,
+ uint_t elapsed);
+extern boolean_t prefix_token_match(struct phyint *pi,
+ struct prefix *pr, uint64_t flags);
+extern struct prefix *prefix_lookup_addr(struct phyint *pi,
+ struct in6_addr prefix);
+
+extern struct adv_prefix *adv_prefix_lookup(struct phyint *pi,
+ struct in6_addr addr, int addrlen);
+extern struct adv_prefix *adv_prefix_create(struct phyint *pi,
+ struct in6_addr addr, int addrlen);
+
+extern struct router *router_lookup(struct phyint *pi, struct in6_addr addr);
+extern struct router *router_create(struct phyint *pi, struct in6_addr addr,
+ uint_t lifetime);
+extern struct router *router_create_onlink(struct phyint *pi);
+extern void router_update_k(struct router *dr);
+extern uint_t router_timer(struct router *dr, uint_t elapsed);
+
+
+extern void logperror_pi(struct phyint *pi, char *str);
+extern void logperror_pr(struct prefix *pr, char *str);
+extern void check_to_advertise(struct phyint *pi, enum adv_events event);
+extern void check_to_solicit(struct phyint *pi,
+ enum solicit_events event);
+extern uint_t advertise_event(struct phyint *pi, enum adv_events event,
+ uint_t elapsed);
+extern uint_t solicit_event(struct phyint *pi, enum solicit_events event,
+ uint_t elapsed);
+
+extern void print_route_sol(char *str, struct phyint *pi,
+ struct nd_router_solicit *rs, int len,
+ struct sockaddr_in6 *addr);
+extern void print_route_adv(char *str, struct phyint *pi,
+ struct nd_router_advert *ra, int len,
+ struct sockaddr_in6 *addr);
+extern void print_iflist(struct confvar *confvar);
+extern void print_prefixlist(struct confvar *confvar);
+
+extern void in_data(struct phyint *pi);
+
+extern void incoming_ra(struct phyint *pi, struct nd_router_advert *ra,
+ int len, struct sockaddr_in6 *from, boolean_t loopback);
+
+extern boolean_t incoming_prefix_addrconf_process(struct phyint *pi,
+ struct prefix *pr, uchar_t *opt,
+ struct sockaddr_in6 *from, boolean_t loopback,
+ boolean_t new_prefix);
+
+extern void incoming_prefix_onlink_process(struct prefix *pr,
+ uchar_t *opt);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _NDPD_TABLES_H */
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/trace.c b/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/trace.c
new file mode 100644
index 0000000000..375d8b91a2
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/trace.c
@@ -0,0 +1,181 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "defs.h"
+#include "tables.h"
+
+static void print_opt(struct nd_opt_hdr *opt, int len);
+
+void
+print_route_sol(char *str, struct phyint *pi,
+ struct nd_router_solicit *rs, int len, struct sockaddr_in6 *addr)
+{
+ struct nd_opt_hdr *opt;
+ char abuf[INET6_ADDRSTRLEN];
+
+ logmsg(LOG_DEBUG, "%s %s (%d bytes) on %s\n", str,
+ inet_ntop(addr->sin6_family, (void *)&addr->sin6_addr,
+ abuf, sizeof (abuf)),
+ len, pi->pi_name);
+
+ len -= sizeof (*rs);
+ opt = (struct nd_opt_hdr *)&rs[1];
+ print_opt(opt, len);
+}
+
+void
+print_route_adv(char *str, struct phyint *pi,
+ struct nd_router_advert *ra, int len, struct sockaddr_in6 *addr)
+{
+ struct nd_opt_hdr *opt;
+ char abuf[INET6_ADDRSTRLEN];
+
+ logmsg(LOG_DEBUG, "%s %s (%d bytes) on %s\n", str,
+ inet_ntop(addr->sin6_family, (void *)&addr->sin6_addr,
+ abuf, sizeof (abuf)),
+ len, pi->pi_name);
+ logmsg(LOG_DEBUG, "\tMax hop limit: %u\n", ra->nd_ra_curhoplimit);
+ logmsg(LOG_DEBUG, "\tManaged address configuration: %s\n",
+ (ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED) ?
+ "Set" : "Not set");
+ logmsg(LOG_DEBUG, "\tOther configuration flag: %s\n",
+ (ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER) ?
+ "Set" : "Not set");
+ logmsg(LOG_DEBUG, "\tRouter lifetime: %u\n",
+ ntohs(ra->nd_ra_router_lifetime));
+ logmsg(LOG_DEBUG, "\tReachable timer: %u\n",
+ ntohl(ra->nd_ra_reachable));
+ logmsg(LOG_DEBUG, "\tReachable retrans timer: %u\n",
+ ntohl(ra->nd_ra_retransmit));
+
+ len -= sizeof (*ra);
+ opt = (struct nd_opt_hdr *)&ra[1];
+ print_opt(opt, len);
+}
+
+static void
+print_opt(struct nd_opt_hdr *opt, int len)
+{
+ struct nd_opt_prefix_info *po;
+ struct nd_opt_mtu *mo;
+ struct nd_opt_lla *lo;
+ int optlen;
+ char abuf[INET6_ADDRSTRLEN];
+ char llabuf[BUFSIZ];
+
+ while (len >= sizeof (struct nd_opt_hdr)) {
+ optlen = opt->nd_opt_len * 8;
+ if (optlen == 0) {
+ logmsg(LOG_DEBUG, "Zero length option!\n");
+ break;
+ }
+ switch (opt->nd_opt_type) {
+ case ND_OPT_PREFIX_INFORMATION:
+ po = (struct nd_opt_prefix_info *)opt;
+ if (optlen != sizeof (*po) ||
+ optlen > len)
+ break;
+
+ logmsg(LOG_DEBUG, "\tPrefix: %s/%u\n",
+ inet_ntop(AF_INET6, (void *)&po->nd_opt_pi_prefix,
+ abuf, sizeof (abuf)),
+ po->nd_opt_pi_prefix_len);
+ logmsg(LOG_DEBUG, "\t\tOn link flag:%s\n",
+ (po->nd_opt_pi_flags_reserved &
+ ND_OPT_PI_FLAG_ONLINK) ?
+ "Set" : "Not set");
+ logmsg(LOG_DEBUG, "\t\tAuto addrconf flag:%s\n",
+ (po->nd_opt_pi_flags_reserved &
+ ND_OPT_PI_FLAG_AUTO) ?
+ "Set" : "Not set");
+ logmsg(LOG_DEBUG, "\t\tValid time: %u\n",
+ ntohl(po->nd_opt_pi_valid_time));
+ logmsg(LOG_DEBUG, "\t\tPreferred time: %u\n",
+ ntohl(po->nd_opt_pi_preferred_time));
+ break;
+ case ND_OPT_MTU:
+ mo = (struct nd_opt_mtu *)opt;
+ if (optlen != sizeof (*mo) ||
+ optlen > len)
+ break;
+ logmsg(LOG_DEBUG, "\tMTU: %d\n",
+ ntohl(mo->nd_opt_mtu_mtu));
+ break;
+ case ND_OPT_SOURCE_LINKADDR:
+ lo = (struct nd_opt_lla *)opt;
+ if (optlen < 8 ||
+ optlen > len)
+ break;
+ (void) fmt_lla(llabuf, sizeof (llabuf),
+ lo->nd_opt_lla_hdw_addr,
+ optlen - sizeof (nd_opt_hdr_t));
+ logmsg(LOG_DEBUG, "\tSource LLA: len %d <%s>\n",
+ optlen - sizeof (nd_opt_hdr_t),
+ llabuf);
+ break;
+ case ND_OPT_TARGET_LINKADDR:
+ lo = (struct nd_opt_lla *)opt;
+ if (optlen < 8||
+ optlen > len)
+ break;
+ (void) fmt_lla(llabuf, sizeof (llabuf),
+ lo->nd_opt_lla_hdw_addr,
+ optlen - sizeof (nd_opt_hdr_t));
+ logmsg(LOG_DEBUG, "\tTarget LLA: len %d <%s>\n",
+ optlen - sizeof (nd_opt_hdr_t),
+ llabuf);
+ break;
+ case ND_OPT_REDIRECTED_HEADER:
+ logmsg(LOG_DEBUG, "\tRedirected header option!\n");
+ break;
+ default:
+ logmsg(LOG_DEBUG, "Unknown option %d (0x%x)\n",
+ opt->nd_opt_type, opt->nd_opt_type);
+ break;
+ }
+ opt = (struct nd_opt_hdr *)((char *)opt + optlen);
+ len -= optlen;
+ }
+}
+
+char *
+fmt_lla(char *llabuf, int bufsize, uchar_t *lla, int llalen)
+{
+ int i;
+ char *cp = llabuf;
+
+ for (i = 0; i < llalen; i++) {
+ if (i == llalen - 1) /* Last byte? */
+ (void) snprintf(cp, bufsize, "%02x", lla[i] & 0xFF);
+ else
+ (void) snprintf(cp, bufsize, "%02x:", lla[i] & 0xFF);
+ bufsize -= strlen(cp);
+ cp += strlen(cp);
+ }
+ return (llabuf);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.ripngd/Makefile b/usr/src/cmd/cmd-inet/usr.lib/in.ripngd/Makefile
new file mode 100644
index 0000000000..e31bb691a2
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.ripngd/Makefile
@@ -0,0 +1,72 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# cmd/cmd-inet/usr.lib/in.ripngd/Makefile
+#
+
+PROG= in.ripngd
+OBJS= if.o input.o main.o output.o startup.o tables.o timer.o trace.o
+SRCS= $(OBJS:%.o=%.c)
+
+include ../../../Makefile.cmd
+
+# these #defines are required to use UNIX 98 interfaces
+_D_UNIX98_EXTN= -D_XOPEN_SOURCE=500 -D__EXTENSIONS__
+
+$(OBJS) := CFLAGS += $(CCVERBOSE)
+$(OBJS) := CPPFLAGS += $(_D_UNIX98_EXTN)
+
+LINTFLAGS += $(_D_UNIX98_EXTN)
+
+# in.ripngd uses the ancillary data feature which is available only through
+# UNIX 98 standards version of Socket interface. This interface is supposed to
+# be accessed by -lxnet. In addition, -lsocket and -lnsl are used to
+# capture new not-yet-standard interfaces. Someday -lxnet alone should be enough
+# when IPv6 inspired new interfaces are part of standards.
+LDLIBS += -lxnet -lsocket -lnsl
+
+.KEEP_STATE:
+
+.PARALLEL: $(OBJS)
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+ $(LINK.c) $(OBJS) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+include ../Makefile.lib
+
+install: all $(ROOTLIBINETPROG)
+
+clean:
+ $(RM) $(OBJS)
+
+lint:
+ $(LINT.c) $(SRCS) $(LDLIBS)
+
+include ../../../Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.ripngd/defs.h b/usr/src/cmd/cmd-inet/usr.lib/in.ripngd/defs.h
new file mode 100644
index 0000000000..830ca9ed58
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.ripngd/defs.h
@@ -0,0 +1,168 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * Portions of this source code were derived from Berkeley 4.3 BSD
+ * under license from the Regents of the University of California.
+ */
+
+#ifndef _IN_RIPNGD_DEFS_H
+#define _IN_RIPNGD_DEFS_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/stream.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <netinet/in.h>
+#include <netinet/ip6.h>
+#include <netinet/udp.h>
+#include <net/if.h>
+#include <net/route.h>
+#include <protocols/ripngd.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <netdb.h>
+
+#include <signal.h>
+#include <stropts.h>
+#include <arpa/inet.h>
+
+#include <strings.h>
+#include <unistd.h>
+#include <errno.h>
+#include <malloc.h>
+#include <limits.h>
+
+#include "table.h"
+#include "trace.h"
+#include "interface.h"
+
+#define PATH_PID "/var/run/in.ripngd.pid"
+
+/*
+ * Timer values (in seconds) used in managing the routing table.
+ * Every update forces an entry's timer to be reset. After
+ * EXPIRE_TIME without updates, the entry is marked invalid,
+ * but held onto until GARBAGE_TIME so that others may
+ * see it "be deleted".
+ */
+#define EXPIRE_TIME 180 /* time to mark entry invalid */
+#define GARBAGE_TIME 300 /* time to garbage collect */
+#define MIN_SUPPLY_TIME 15 /* min. time to supply tables */
+#define MAX_SUPPLY_TIME 45 /* max. time to supply tables */
+#define MIN_WAIT_TIME 1 /* min. interval to multicast changes */
+#define MAX_WAIT_TIME 5 /* max. time to delay changes */
+
+/*
+ * Return a random number from a an range inclusive of the endpoints
+ */
+#define GET_RANDOM(LOW, HIGH) (random() % ((HIGH) - (LOW) + 1) + (LOW))
+
+/*
+ * When we find any interfaces marked down we rescan the
+ * kernel every CHECK_INTERVAL seconds to see if they've
+ * come up.
+ */
+#define CHECK_INTERVAL 60
+#define START_POLL_SIZE 5
+
+#define min(a, b) ((a) > (b) ? (b) : (a))
+
+/*
+ * The maximum receive buffer size is controlled via Solaris' NDD udp_max_buf
+ * tunable.
+ */
+#define RCVBUFSIZ 65536
+
+#define TIME_TO_MSECS(tval) ((tval).tv_sec * 1000 + (tval).tv_usec / 1000)
+
+#define HOPCNT_INFINITY 16 /* RFC 2080, section 2.1 */
+#define HOPCNT_NEXTHOP 255 /* RFC 2080, section 2.1.1 */
+
+/*
+ * XXX Some of these are defined in <inet/ip6.h> under _KERNEL (but should be
+ * defined in <netinet/ip6.h> for completeness).
+ */
+#define IPV6_MAX_HOPS 255 /* Max IPv6 hops */
+#define IPV6_MAX_PACKET 65535 /* maximum IPv6 packet size */
+#define IPV6_MIN_MTU 1280 /* Minimum IPv6 MTU */
+
+extern struct sockaddr_in6 allrouters;
+extern struct in6_addr allrouters_in6;
+extern char *control;
+extern boolean_t dopoison;
+extern struct interface *ifnet;
+extern boolean_t install;
+extern int iocsoc;
+extern struct timeval lastfullupdate;
+extern struct timeval lastmcast;
+extern int max_poll_ifs;
+extern struct rip6 *msg;
+extern boolean_t needupdate;
+extern struct timeval nextmcast;
+extern struct timeval now;
+extern char *packet;
+extern struct pollfd *poll_ifs;
+extern int poll_ifs_num;
+extern int rip6_port;
+extern int supplyinterval;
+extern boolean_t supplier;
+
+extern void dynamic_update(struct interface *);
+extern void in_data(struct interface *);
+extern void initifs(void);
+extern void sendpacket(struct sockaddr_in6 *, struct interface *,
+ int, int);
+extern void setup_rtsock(void);
+extern void solicitall(struct sockaddr_in6 *);
+extern void supply(struct sockaddr_in6 *, struct interface *,
+ int, boolean_t);
+extern void supplyall(struct sockaddr_in6 *, int,
+ struct interface *, boolean_t);
+extern void term(void);
+extern void timer(void);
+extern void timevaladd(struct timeval *, struct timeval *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _IN_RIPNGD_DEFS_H */
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.ripngd/if.c b/usr/src/cmd/cmd-inet/usr.lib/in.ripngd/if.c
new file mode 100644
index 0000000000..d9a4d5d385
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.ripngd/if.c
@@ -0,0 +1,141 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * Portions of this source code were derived from Berkeley 4.3 BSD
+ * under license from the Regents of the University of California.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Routing Table Management Daemon
+ */
+#include "defs.h"
+
+/*
+ * Find the interface with given name.
+ */
+struct interface *
+if_ifwithname(char *name)
+{
+ struct interface *ifp;
+
+ for (ifp = ifnet; ifp != NULL; ifp = ifp->int_next) {
+ if (ifp->int_name != NULL &&
+ strcmp(ifp->int_name, name) == 0)
+ break;
+ }
+ return (ifp);
+}
+
+/*
+ * An interface has declared itself down - remove it completely
+ * from our routing tables but keep the interface structure around.
+ */
+void
+if_purge(struct interface *pifp)
+{
+ rtpurgeif(pifp);
+ pifp->int_flags &= ~RIP6_IFF_UP;
+}
+
+static void
+if_dump2(FILE *fp)
+{
+ struct interface *ifp;
+ char buf1[INET6_ADDRSTRLEN];
+ static struct bits {
+ uint_t t_bits;
+ char *t_name;
+ } flagbits[] = {
+ /* BEGIN CSTYLED */
+ { RIP6_IFF_UP, "UP" },
+ { RIP6_IFF_POINTOPOINT, "POINTOPOINT" },
+ { RIP6_IFF_MARKED, "MARKED" },
+ { RIP6_IFF_NORTEXCH, "NORTEXCH" },
+ { RIP6_IFF_PRIVATE, "PRIVATE" },
+ { 0, NULL }
+ /* END CSTYLED */
+ };
+ struct bits *p;
+ char c;
+ boolean_t first;
+
+ for (ifp = ifnet; ifp != NULL; ifp = ifp->int_next) {
+ (void) fprintf(fp, "interface %s:\n",
+ (ifp->int_name != NULL) ? ifp->int_name : "(noname)");
+
+ (void) fprintf(fp, "\tflags ");
+ c = ' ';
+ for (first = _B_TRUE, p = flagbits; p->t_bits > 0; p++) {
+ if ((ifp->int_flags & p->t_bits) == 0)
+ continue;
+ (void) fprintf(fp, "%c%s", c, p->t_name);
+ if (first) {
+ c = '|';
+ first = _B_FALSE;
+ }
+ }
+ if (first)
+ (void) fprintf(fp, " 0");
+
+ (void) fprintf(fp, "\n\tpackets received %d\n",
+ ifp->int_ipackets);
+ (void) fprintf(fp, "\tpackets sent %d\n", ifp->int_opackets);
+ (void) fprintf(fp, "\ttransitions %d\n", ifp->int_transitions);
+ if ((ifp->int_flags & RIP6_IFF_UP) == 0)
+ continue;
+ if (ifp->int_flags & RIP6_IFF_POINTOPOINT) {
+ (void) fprintf(fp, "\tlocal %s\n",
+ inet_ntop(AF_INET6, (void *)&ifp->int_addr, buf1,
+ sizeof (buf1)));
+ (void) fprintf(fp, "\tremote %s\n",
+ inet_ntop(AF_INET6, (void *)&ifp->int_dstaddr, buf1,
+ sizeof (buf1)));
+ } else {
+ (void) fprintf(fp, "\tprefix %s/%d\n",
+ inet_ntop(AF_INET6, (void *)&ifp->int_addr, buf1,
+ sizeof (buf1)),
+ ifp->int_prefix_length);
+ }
+ (void) fprintf(fp, "\tmetric %d\n", ifp->int_metric);
+ (void) fprintf(fp, "\tmtu %d\n", ifp->int_mtu);
+ }
+ (void) fflush(fp);
+}
+
+void
+if_dump(void)
+{
+ if (ftrace != NULL)
+ if_dump2(ftrace);
+ else
+ if_dump2(stderr);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.ripngd/input.c b/usr/src/cmd/cmd-inet/usr.lib/in.ripngd/input.c
new file mode 100644
index 0000000000..f018add72c
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.ripngd/input.c
@@ -0,0 +1,557 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1999 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * Portions of this source code were derived from Berkeley 4.3 BSD
+ * under license from the Regents of the University of California.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Routing Table Management Daemon
+ */
+#include "defs.h"
+
+static char buf1[INET6_ADDRSTRLEN];
+static char buf2[INET6_ADDRSTRLEN];
+
+static void rip_input(struct sockaddr_in6 *from, int size, uint_t hopcount,
+ struct interface *ifp);
+
+/*
+ * Return a pointer to the specified option buffer.
+ * If not found return NULL.
+ */
+static void *
+find_ancillary(struct msghdr *rmsg, int cmsg_type)
+{
+ struct cmsghdr *cmsg;
+
+ for (cmsg = CMSG_FIRSTHDR(rmsg); cmsg != NULL;
+ cmsg = CMSG_NXTHDR(rmsg, cmsg)) {
+ if (cmsg->cmsg_level == IPPROTO_IPV6 &&
+ cmsg->cmsg_type == cmsg_type) {
+ return (CMSG_DATA(cmsg));
+ }
+ }
+ return (NULL);
+}
+
+/*
+ * Read a packet and passes it to rip_input() for processing.
+ */
+void
+in_data(struct interface *ifp)
+{
+ struct sockaddr_in6 from;
+ int len;
+ struct msghdr rmsg;
+ struct iovec iov;
+ uchar_t *hopcntopt;
+
+ iov.iov_base = packet;
+ iov.iov_len = IPV6_MAX_PACKET;
+ rmsg.msg_name = &from;
+ rmsg.msg_namelen = (socklen_t)sizeof (from);
+ rmsg.msg_iov = &iov;
+ rmsg.msg_iovlen = 1;
+ rmsg.msg_control = control;
+ rmsg.msg_controllen = IPV6_MAX_PACKET;
+
+ if ((len = recvmsg(ifp->int_sock, &rmsg, 0)) < 0) {
+ /*
+ * Only syslog if a true error occurred.
+ */
+ if (errno != EINTR)
+ syslog(LOG_ERR, "in_data: recvmsg: %m");
+ return;
+ }
+ if (len == 0)
+ return;
+
+ if (tracing & INPUT_BIT) {
+ (void) inet_ntop(from.sin6_family, &from.sin6_addr, buf1,
+ sizeof (buf1));
+ }
+
+ /* Ignore packets > 64k or control buffers that don't fit */
+ if (rmsg.msg_flags & (MSG_TRUNC | MSG_CTRUNC)) {
+ if (tracing & INPUT_BIT) {
+ (void) fprintf(stderr,
+ "Truncated message: msg_flags 0x%x from %s\n",
+ rmsg.msg_flags, buf1);
+ }
+ return;
+ }
+
+ if ((hopcntopt = find_ancillary(&rmsg, IPV6_HOPLIMIT)) == NULL) {
+ if (tracing & INPUT_BIT) {
+ (void) fprintf(stderr, "Unknown hop limit from %s\n",
+ buf1);
+ }
+ return;
+ }
+ rip_input(&from, len, *(uint_t *)hopcntopt, ifp);
+}
+
+/*
+ * Process a newly received packet.
+ */
+static void
+rip_input(struct sockaddr_in6 *from, int size, uint_t hopcount,
+ struct interface *ifp)
+{
+ struct rt_entry *rt;
+ struct netinfo6 *n;
+ int newsize;
+ boolean_t changes = _B_FALSE;
+ int answer = supplier;
+ struct in6_addr prefix;
+ struct in6_addr nexthop;
+ struct in6_addr *gate;
+ boolean_t foundnexthop = _B_FALSE;
+ struct sioc_addrreq sa;
+ struct sockaddr_in6 *sin6;
+
+ TRACE_INPUT(ifp, from, size);
+ if (tracing & INPUT_BIT) {
+ (void) inet_ntop(from->sin6_family, (void *)&from->sin6_addr,
+ buf1, sizeof (buf1));
+ }
+
+ /*
+ * If the packet is recevied on an interface with IFF_NORTEXCH flag set,
+ * we ignore the packet.
+ */
+ if (ifp->int_flags & RIP6_IFF_NORTEXCH) {
+ if (tracing & INPUT_BIT) {
+ (void) fprintf(ftrace,
+ "Ignore received RIPng packet on %s "
+ "(no route exchange on interface)\n",
+ ifp->int_name);
+ (void) fflush(ftrace);
+ }
+ return;
+ }
+ if (msg->rip6_vers != RIPVERSION6) {
+ if (tracing & INPUT_BIT) {
+ (void) fprintf(ftrace,
+ "Bad version number %d in packet from %s\n",
+ msg->rip6_vers, buf1);
+ (void) fflush(ftrace);
+ }
+ return;
+ }
+ if (ntohs(msg->rip6_res1) != 0) {
+ if (tracing & INPUT_BIT) {
+ (void) fprintf(ftrace,
+ "Non-zero reserved octets found in packet from "
+ "%s\n",
+ buf1);
+ (void) fflush(ftrace);
+ }
+ }
+
+ switch (msg->rip6_cmd) {
+
+ case RIPCMD6_REQUEST: /* multicasted request */
+ ifp->int_ipackets++;
+ newsize = 0;
+
+ /*
+ * Adjust size by the length of the command, version and
+ * reserved fields (which are in total 32-bit aligned).
+ */
+ size -= sizeof (msg->rip6_cmd) + sizeof (msg->rip6_vers) +
+ sizeof (msg->rip6_res1);
+
+ /*
+ * From section 2.4.1 of RFC 2080:
+ *
+ * If there is exactly one entry in the request with a
+ * destination prefix of zero, a prefix length of zero and
+ * an infinite metric, then supply the entire routing
+ * table.
+ */
+ n = msg->rip6_nets;
+ if (size == sizeof (struct netinfo6) &&
+ n->rip6_prefix_length == 0 &&
+ n->rip6_metric == HOPCNT_INFINITY) {
+ rtcreate_prefix(&n->rip6_prefix, &prefix,
+ n->rip6_prefix_length);
+ if (IN6_IS_ADDR_UNSPECIFIED(&prefix)) {
+ supply(from, ifp, 0,
+ from->sin6_port == rip6_port);
+ return;
+ }
+ }
+ for (; size >= sizeof (struct netinfo6);
+ size -= sizeof (struct netinfo6), n++) {
+ if (n->rip6_prefix_length > IPV6_ABITS) {
+ if (tracing & INPUT_BIT) {
+ (void) fprintf(ftrace,
+ "Bad prefix length %d in request "
+ "from %s\n",
+ n->rip6_prefix_length, buf1);
+ (void) fflush(ftrace);
+ }
+ continue;
+ }
+ if (IN6_IS_ADDR_LINKLOCAL(&n->rip6_prefix) ||
+ IN6_IS_ADDR_MULTICAST(&n->rip6_prefix)) {
+ if (tracing & INPUT_BIT) {
+ (void) fprintf(ftrace,
+ "Bad prefix %s in request from "
+ "%s\n",
+ inet_ntop(AF_INET6,
+ (void *)&n->rip6_prefix, buf2,
+ sizeof (buf2)),
+ buf1);
+ (void) fflush(ftrace);
+ }
+ continue;
+ }
+ rtcreate_prefix(&n->rip6_prefix, &prefix,
+ n->rip6_prefix_length);
+ rt = rtlookup(&prefix, n->rip6_prefix_length);
+
+ n->rip6_metric = (rt == NULL ?
+ HOPCNT_INFINITY :
+ min(rt->rt_metric, HOPCNT_INFINITY));
+ newsize += sizeof (struct netinfo6);
+ }
+ if (size > 0) {
+ if (tracing & INPUT_BIT) {
+ (void) fprintf(ftrace,
+ "Ignoring %d octets of trailing data in "
+ "request from %s\n",
+ size, buf1);
+ (void) fflush(ftrace);
+ }
+ }
+ if (answer && newsize > 0) {
+ /*
+ * Adjust newsize by the length of the command, version
+ * and reserved fields (which are in total 32-bit
+ * aligned).
+ */
+ msg->rip6_cmd = RIPCMD6_RESPONSE;
+ newsize += sizeof (msg->rip6_cmd) +
+ sizeof (msg->rip6_vers) + sizeof (msg->rip6_res1);
+ sendpacket(from, ifp, newsize, 0);
+ }
+ return;
+
+ case RIPCMD6_RESPONSE:
+ if (hopcount != IPV6_MAX_HOPS) {
+ if (tracing & INPUT_BIT) {
+ (void) fprintf(ftrace,
+ "Bad hop count %d in response from %s\n",
+ hopcount, buf1);
+ (void) fflush(ftrace);
+ }
+ return;
+ }
+
+ if (from->sin6_port != rip6_port) {
+ if (tracing & INPUT_BIT) {
+ (void) fprintf(ftrace,
+ "Bad source port %d in response from %s\n",
+ from->sin6_port, buf1);
+ (void) fflush(ftrace);
+ }
+ return;
+ }
+
+ if (!IN6_IS_ADDR_LINKLOCAL(&from->sin6_addr)) {
+ if (tracing & INPUT_BIT) {
+ (void) fprintf(ftrace,
+ "Bad source address (not link-local) in "
+ "response from %s\n", buf1);
+ (void) fflush(ftrace);
+ }
+ return;
+ }
+ ifp->int_ipackets++;
+
+ /*
+ * Adjust size by the length of the command, version and
+ * reserved fields (which are in total 32-bit aligned).
+ */
+ size -= sizeof (msg->rip6_cmd) + sizeof (msg->rip6_vers) +
+ sizeof (msg->rip6_res1);
+ for (n = msg->rip6_nets;
+ supplier && size >= sizeof (struct netinfo6);
+ size -= sizeof (struct netinfo6), n++) {
+ /*
+ * From section 2.1.1 of RFC 2080:
+ *
+ * This is a next hop RTE if n->rip6_metric is set to
+ * HOPCNT_NEXTHOP. If the next hop address (which is
+ * placed in the prefix field of this special RTE) is
+ * unspecified or is not a link-local address, then use
+ * the originator's address instead (effectively turning
+ * off next hop RTE processing.)
+ */
+ if (n->rip6_metric == HOPCNT_NEXTHOP) {
+ /*
+ * First check to see if the unspecified address
+ * was given as the next hop address. This is
+ * the correct way of specifying the end of use
+ * of a next hop address.
+ */
+ if (IN6_IS_ADDR_UNSPECIFIED(&n->rip6_prefix)) {
+ foundnexthop = _B_FALSE;
+ continue;
+ }
+ /*
+ * A next hop address that is not a link-local
+ * address is treated as the unspecified one.
+ * Trace this event if input tracing is enabled.
+ */
+ if (!IN6_IS_ADDR_LINKLOCAL(&n->rip6_prefix)) {
+ foundnexthop = _B_FALSE;
+ if (tracing & INPUT_BIT) {
+ (void) fprintf(ftrace,
+ "Bad next hop %s in "
+ "response from %s\n",
+ inet_ntop(AF_INET6,
+ (void *)&n->rip6_prefix,
+ buf2, sizeof (buf2)),
+ buf1);
+ }
+ continue;
+ }
+ /*
+ * Verify that the next hop address is not one
+ * of our own.
+ */
+ sin6 = (struct sockaddr_in6 *)&sa.sa_addr;
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_addr = n->rip6_prefix;
+ if (ioctl(iocsoc, SIOCTMYADDR,
+ (char *)&sa) < 0) {
+ syslog(LOG_ERR,
+ "rip_input: "
+ "ioctl (verify my address): %m");
+ return;
+ }
+ if (sa.sa_res != 0) {
+ foundnexthop = _B_FALSE;
+ if (tracing & INPUT_BIT) {
+ (void) fprintf(ftrace,
+ "Bad next hop %s is self "
+ "in response from %s\n",
+ inet_ntop(AF_INET6,
+ (void *)&n->rip6_prefix,
+ buf2, sizeof (buf2)),
+ buf1);
+ }
+ continue;
+ }
+ foundnexthop = _B_TRUE;
+ nexthop = n->rip6_prefix;
+ continue;
+ }
+ if (foundnexthop)
+ gate = &nexthop;
+ else
+ gate = &from->sin6_addr;
+
+ if (n->rip6_metric > HOPCNT_INFINITY ||
+ n->rip6_metric < 1) {
+ if (tracing & INPUT_BIT) {
+ (void) fprintf(ftrace,
+ "Bad metric %d in response from "
+ "%s\n",
+ n->rip6_metric, buf1);
+ (void) fflush(ftrace);
+ }
+ continue;
+ }
+ if (n->rip6_prefix_length > IPV6_ABITS) {
+ if (tracing & INPUT_BIT) {
+ (void) fprintf(ftrace,
+ "Bad prefix length %d in response "
+ "from %s\n",
+ n->rip6_prefix_length, buf1);
+ (void) fflush(ftrace);
+ }
+ continue;
+ }
+
+ if (IN6_IS_ADDR_LINKLOCAL(&n->rip6_prefix) ||
+ IN6_IS_ADDR_MULTICAST(&n->rip6_prefix)) {
+ if (tracing & INPUT_BIT) {
+
+ (void) fprintf(ftrace,
+ "Bad prefix %s in response from "
+ "%s\n",
+ inet_ntop(AF_INET6,
+ (void *)&n->rip6_prefix, buf2,
+ sizeof (buf2)),
+ buf1);
+ (void) fflush(ftrace);
+ }
+ continue;
+ }
+ /* Include metric for incoming interface */
+ n->rip6_metric += IFMETRIC(ifp);
+
+ rtcreate_prefix(&n->rip6_prefix, &prefix,
+ n->rip6_prefix_length);
+ rt = rtlookup(&prefix, n->rip6_prefix_length);
+ if (rt == NULL) {
+ if (n->rip6_metric < HOPCNT_INFINITY) {
+ rtadd(&prefix,
+ gate, n->rip6_prefix_length,
+ n->rip6_metric, n->rip6_route_tag,
+ _B_FALSE, ifp);
+ changes = _B_TRUE;
+ }
+ continue;
+ }
+
+ /*
+ * If the supplied metric is at least HOPCNT_INFINITY
+ * and the current metric of the route is
+ * HOPCNT_INFINITY, then this particular RTE is ignored.
+ */
+ if (n->rip6_metric >= HOPCNT_INFINITY &&
+ rt->rt_metric == HOPCNT_INFINITY)
+ continue;
+
+ /*
+ * From section 2.4.2 of RFC 2080:
+ *
+ * Update if any one of the following is true
+ *
+ * 1) From current gateway and a different metric.
+ * 2) From current gateway and a different index.
+ * 3) A shorter (smaller) metric.
+ * 4) Equivalent metric and an age at least
+ * one-half of EXPIRE_TIME.
+ *
+ * Otherwise, update timer for the interface on which
+ * the packet arrived.
+ */
+ if (IN6_ARE_ADDR_EQUAL(gate, &rt->rt_router)) {
+ if (n->rip6_metric != rt->rt_metric ||
+ rt->rt_ifp != ifp) {
+ rtchange(rt, gate, n->rip6_metric, ifp);
+ changes = _B_TRUE;
+ } else if (n->rip6_metric < HOPCNT_INFINITY) {
+ rt->rt_timer = 0;
+ }
+ } else if (n->rip6_metric < rt->rt_metric ||
+ (rt->rt_timer > (EXPIRE_TIME / 2) &&
+ rt->rt_metric == n->rip6_metric)) {
+ rtchange(rt, gate, n->rip6_metric, ifp);
+ changes = _B_TRUE;
+ }
+ }
+ if (changes && supplier)
+ dynamic_update(ifp);
+ return;
+
+ default:
+ if (tracing & INPUT_BIT) {
+ (void) fprintf(ftrace,
+ "Bad command %d in packet from %s\n",
+ msg->rip6_cmd, buf1);
+ (void) fflush(ftrace);
+ }
+ return;
+ }
+}
+
+/*
+ * If changes have occurred, and if we have not sent a multicast
+ * recently, send a dynamic update. This update is sent only
+ * on interfaces other than the one on which we received notice
+ * of the change. If we are within MIN_WAIT_TIME of a full update,
+ * don't bother sending; if we just sent a dynamic update
+ * and set a timer (nextmcast), delay until that time.
+ * If we just sent a full update, delay the dynamic update.
+ * Set a timer for a randomized value to suppress additional
+ * dynamic updates until it expires; if we delayed sending
+ * the current changes, set needupdate.
+ */
+void
+dynamic_update(struct interface *ifp)
+{
+ int delay;
+
+ if (now.tv_sec - lastfullupdate.tv_sec >=
+ supplyinterval - MIN_WAIT_TIME)
+ return;
+
+ if (now.tv_sec - lastmcast.tv_sec >= MIN_WAIT_TIME &&
+ /* BEGIN CSTYLED */
+ timercmp(&nextmcast, &now, <)) {
+ /* END CSTYLED */
+ TRACE_ACTION("send dynamic update",
+ (struct rt_entry *)NULL);
+ supplyall(&allrouters, RTS_CHANGED, ifp, _B_TRUE);
+ lastmcast = now;
+ needupdate = _B_FALSE;
+ nextmcast.tv_sec = 0;
+ } else {
+ needupdate = _B_TRUE;
+ TRACE_ACTION("delay dynamic update",
+ (struct rt_entry *)NULL);
+ }
+
+ if (nextmcast.tv_sec == 0) {
+ delay = GET_RANDOM(MIN_WAIT_TIME * 1000000,
+ MAX_WAIT_TIME * 1000000);
+ if (tracing & ACTION_BIT) {
+ (void) fprintf(ftrace,
+ "inhibit dynamic update for %d msec\n",
+ delay / 1000);
+ (void) fflush(ftrace);
+ }
+ nextmcast.tv_sec = delay / 1000000;
+ nextmcast.tv_usec = delay % 1000000;
+ timevaladd(&nextmcast, &now);
+ /*
+ * If the next possibly dynamic update
+ * is within MIN_WAIT_TIME of the next full
+ * update, force the delay past the full
+ * update, or we might send a dynamic update
+ * just before the full update.
+ */
+ if (nextmcast.tv_sec >
+ lastfullupdate.tv_sec + supplyinterval - MIN_WAIT_TIME) {
+ nextmcast.tv_sec =
+ lastfullupdate.tv_sec + supplyinterval + 1;
+ }
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.ripngd/interface.h b/usr/src/cmd/cmd-inet/usr.lib/in.ripngd/interface.h
new file mode 100644
index 0000000000..d235b644a1
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.ripngd/interface.h
@@ -0,0 +1,65 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1999 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * Portions of this source code were derived from Berkeley 4.3 BSD
+ * under license from the Regents of the University of California.
+ */
+
+#ident "%Z%%M% %I% %E% SMI"
+
+struct interface {
+ struct interface *int_next;
+ struct in6_addr int_addr; /* address on this if */
+ struct in6_addr int_dstaddr; /* other end of p-to-p link */
+ int int_metric; /* init's routing entry */
+ uint_t int_flags; /* see below */
+ int int_prefix_length; /* prefix length on this if */
+ char *int_name; /* from kernel if structure */
+ char *int_ifbase; /* name of physical interface */
+ int int_sock; /* socket on if to send/recv */
+ int int_ifindex; /* interface index */
+ uint_t int_mtu; /* maximum transmission unit */
+ struct ifdebug int_input, int_output; /* packet tracing stuff */
+ int int_ipackets; /* input packets received */
+ int int_opackets; /* output packets sent */
+ ushort_t int_transitions; /* times gone up-down */
+};
+
+#define RIP6_IFF_UP 0x1 /* interface is up */
+#define RIP6_IFF_POINTOPOINT 0x2 /* interface is p-to-p link */
+#define RIP6_IFF_MARKED 0x4 /* to determine removed ifs */
+#define RIP6_IFF_NORTEXCH 0x8 /* don't exchange route info */
+#define RIP6_IFF_PRIVATE 0x10 /* interface is private */
+
+#define IFMETRIC(ifp) ((ifp != NULL) ? (ifp)->int_metric : 1)
+
+extern void if_dump(void);
+extern struct interface *if_ifwithname(char *);
+extern void if_purge(struct interface *);
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.ripngd/main.c b/usr/src/cmd/cmd-inet/usr.lib/in.ripngd/main.c
new file mode 100644
index 0000000000..dbbcf4d0fa
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.ripngd/main.c
@@ -0,0 +1,313 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * Portions of this source code were derived from Berkeley 4.3 BSD
+ * under license from the Regents of the University of California.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "defs.h"
+
+struct sockaddr_in6 allrouters;
+char *control;
+boolean_t dopoison = _B_TRUE; /* Do poison reverse */
+int iocsoc;
+struct timeval lastfullupdate; /* last time full table multicast */
+struct timeval lastmcast; /* last time all/changes multicast */
+int max_poll_ifs = START_POLL_SIZE;
+struct rip6 *msg;
+boolean_t needupdate; /* true if need update at nextmcast */
+struct timeval nextmcast; /* time to wait before changes mcast */
+struct timeval now; /* current idea of time */
+char *packet;
+struct pollfd *poll_ifs = NULL;
+int poll_ifs_num = 0;
+int rip6_port;
+boolean_t supplier = _B_TRUE; /* process should supply updates */
+
+struct in6_addr allrouters_in6 = {
+/* BEGIN CSTYLED */
+ { 0xff, 0x2, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x9 }
+/* END CSTYLED */
+};
+
+static void timevalsub(struct timeval *t1, struct timeval *t2);
+
+static void
+usage(char *fname)
+{
+ (void) fprintf(stderr,
+ "usage: "
+ "%s [ -P ] [ -p port ] [ -q ] [ -s ] [ -t ] [ -v ] [<logfile>]\n",
+ fname);
+ exit(EXIT_FAILURE);
+}
+
+void
+main(int argc, char *argv[])
+{
+ int i, n;
+ struct interface *ifp;
+ int c;
+ struct timeval waittime;
+ int timeout;
+ boolean_t daemon = _B_TRUE; /* Fork off a detached daemon */
+ FILE *pidfp;
+ mode_t pidmode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); /* 0644 */
+
+ rip6_port = htons(IPPORT_ROUTESERVER6);
+ allrouters.sin6_family = AF_INET6;
+ allrouters.sin6_port = rip6_port;
+ allrouters.sin6_addr = allrouters_in6;
+
+ while ((c = getopt(argc, argv, "nsqvTtdgPp:")) != EOF) {
+ switch (c) {
+ case 'n':
+ install = _B_FALSE;
+ break;
+ case 's':
+ supplier = _B_TRUE;
+ break;
+ case 'q':
+ supplier = _B_FALSE;
+ break;
+ case 'v':
+ tracing |= ACTION_BIT;
+ break;
+ case 'T':
+ daemon = _B_FALSE;
+ break;
+ case 't':
+ tracepackets = _B_TRUE;
+ daemon = _B_FALSE;
+ tracing |= (INPUT_BIT | OUTPUT_BIT);
+ break;
+ case 'd':
+ break;
+ case 'P':
+ dopoison = _B_FALSE;
+ break;
+ case 'p':
+ rip6_port = htons(atoi(optarg));
+ allrouters.sin6_port = rip6_port;
+ break;
+ default:
+ usage(argv[0]);
+ /* NOTREACHED */
+ }
+ }
+
+ /*
+ * Any extra argument is considered
+ * a tracing log file.
+ */
+ if (optind < argc) {
+ traceon(argv[optind]);
+ } else if (tracing && !daemon) {
+ traceonfp(stdout);
+ } else if (tracing) {
+ (void) fprintf(stderr, "Need logfile with -v\n");
+ usage(argv[0]);
+ /* NOTREACHED */
+ }
+
+ if (daemon) {
+ int t;
+
+ if (fork())
+ exit(EXIT_SUCCESS);
+ for (t = 0; t < 20; t++) {
+ if (!tracing || (t != fileno(ftrace)))
+ (void) close(t);
+ }
+ (void) open("/", 0);
+ (void) dup2(0, 1);
+ (void) dup2(0, 2);
+ (void) setsid();
+ }
+
+ /* Store our process id, blow away any existing file if it exists. */
+ if ((pidfp = fopen(PATH_PID, "w")) == NULL) {
+ (void) fprintf(stderr, "%s: unable to open " PATH_PID ": %s\n",
+ argv[0], strerror(errno));
+ } else {
+ (void) fprintf(pidfp, "%ld\n", getpid());
+ (void) fclose(pidfp);
+ (void) chmod(PATH_PID, pidmode);
+ }
+
+
+ iocsoc = socket(AF_INET6, SOCK_DGRAM, 0);
+ if (iocsoc < 0) {
+ syslog(LOG_ERR, "main: socket: %m");
+ exit(EXIT_FAILURE);
+ }
+
+ setup_rtsock();
+
+ /*
+ * Allocate the buffer to hold the RIPng packet. In reality, it will be
+ * smaller than IPV6_MAX_PACKET octets due to (at least) the IPv6 and
+ * UDP headers but IPV6_MAX_PACKET is a convenient size.
+ */
+ packet = (char *)malloc(IPV6_MAX_PACKET);
+ if (packet == NULL) {
+ syslog(LOG_ERR, "main: malloc: %m");
+ exit(EXIT_FAILURE);
+ }
+ msg = (struct rip6 *)packet;
+
+ /*
+ * Allocate the buffer to hold the ancillary data. This data is used to
+ * insure that the incoming hop count of a RIPCMD6_RESPONSE message is
+ * IPV6_MAX_HOPS which indicates that it came from a direct neighbor
+ * (namely, no intervening router decremented it).
+ */
+ control = (char *)malloc(IPV6_MAX_PACKET);
+ if (control == NULL) {
+ syslog(LOG_ERR, "main: malloc: %m");
+ exit(EXIT_FAILURE);
+ }
+
+ openlog("in.ripngd", LOG_PID | LOG_CONS, LOG_DAEMON);
+
+ (void) gettimeofday(&now, (struct timezone *)NULL);
+
+ initifs();
+ solicitall(&allrouters);
+
+ if (supplier)
+ supplyall(&allrouters, 0, (struct interface *)NULL, _B_TRUE);
+
+ (void) sigset(SIGALRM, (void (*)(int))timer);
+ (void) sigset(SIGHUP, (void (*)(int))initifs);
+ (void) sigset(SIGTERM, (void (*)(int))term);
+ (void) sigset(SIGUSR1, (void (*)(int))if_dump);
+ (void) sigset(SIGUSR2, (void (*)(int))rtdump);
+
+ /*
+ * Seed the pseudo-random number generator for GET_RANDOM().
+ */
+ srandom((uint_t)gethostid());
+
+ timer();
+
+ for (;;) {
+ if (needupdate) {
+ waittime = nextmcast;
+ timevalsub(&waittime, &now);
+ if (waittime.tv_sec < 0) {
+ timeout = 0;
+ } else {
+ timeout = TIME_TO_MSECS(waittime);
+ }
+ if (tracing & ACTION_BIT) {
+ (void) fprintf(ftrace,
+ "poll until dynamic update in %d msec\n",
+ timeout);
+ (void) fflush(ftrace);
+ }
+ } else {
+ timeout = INFTIM;
+ }
+
+ if ((n = poll(poll_ifs, poll_ifs_num, timeout)) < 0) {
+ if (errno == EINTR)
+ continue;
+ syslog(LOG_ERR, "main: poll: %m");
+ exit(EXIT_FAILURE);
+ }
+ (void) sighold(SIGALRM);
+ (void) sighold(SIGHUP);
+ /*
+ * Poll timed out.
+ */
+ if (n == 0) {
+ if (needupdate) {
+ TRACE_ACTION("send delayed dynamic update",
+ (struct rt_entry *)NULL);
+ (void) gettimeofday(&now,
+ (struct timezone *)NULL);
+ supplyall(&allrouters, RTS_CHANGED,
+ (struct interface *)NULL, _B_TRUE);
+ lastmcast = now;
+ needupdate = _B_FALSE;
+ nextmcast.tv_sec = 0;
+ }
+ (void) sigrelse(SIGHUP);
+ (void) sigrelse(SIGALRM);
+ continue;
+ }
+ (void) gettimeofday(&now, (struct timezone *)NULL);
+ for (i = 0; i < poll_ifs_num; i++) {
+ /*
+ * This case should never happen.
+ */
+ if (poll_ifs[i].revents & POLLERR) {
+ syslog(LOG_ERR,
+ "main: poll returned a POLLERR event");
+ continue;
+ }
+ if (poll_ifs[i].revents & POLLIN) {
+ for (ifp = ifnet; ifp != NULL;
+ ifp = ifp->int_next) {
+ if (poll_ifs[i].fd == ifp->int_sock)
+ in_data(ifp);
+ }
+ }
+ }
+ (void) sigrelse(SIGHUP);
+ (void) sigrelse(SIGALRM);
+ }
+}
+
+void
+timevaladd(struct timeval *t1, struct timeval *t2)
+{
+ t1->tv_sec += t2->tv_sec;
+ if ((t1->tv_usec += t2->tv_usec) > 1000000) {
+ t1->tv_sec++;
+ t1->tv_usec -= 1000000;
+ }
+}
+
+void
+timevalsub(struct timeval *t1, struct timeval *t2)
+{
+ t1->tv_sec -= t2->tv_sec;
+ if ((t1->tv_usec -= t2->tv_usec) < 0) {
+ t1->tv_sec--;
+ t1->tv_usec += 1000000;
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.ripngd/output.c b/usr/src/cmd/cmd-inet/usr.lib/in.ripngd/output.c
new file mode 100644
index 0000000000..72fa31b138
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.ripngd/output.c
@@ -0,0 +1,216 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1999 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * Portions of this source code were derived from Berkeley 4.3 BSD
+ * under license from the Regents of the University of California.
+ */
+
+#ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.1 */
+
+/*
+ * Routing Table Management Daemon
+ */
+#include "defs.h"
+
+/*
+ * Apply the function "supply" to all active
+ * interfaces with a link-local address.
+ */
+void
+supplyall(struct sockaddr_in6 *sin6, int rtstate, struct interface *skipif,
+ boolean_t splith)
+{
+ struct interface *ifp;
+
+ for (ifp = ifnet; ifp != NULL; ifp = ifp->int_next) {
+ if ((ifp->int_flags & RIP6_IFF_UP) == 0)
+ continue;
+ if (ifp->int_flags & RIP6_IFF_NORTEXCH) {
+ if (tracing & OUTPUT_BIT) {
+ (void) fprintf(ftrace,
+ "Suppress sending RIPng response packet "
+ "on %s (no route exchange on interface)\n",
+ ifp->int_name);
+ (void) fflush(ftrace);
+ }
+ continue;
+ }
+ if (ifp->int_sock == -1)
+ continue;
+ if (ifp == skipif)
+ continue;
+ if (!IN6_IS_ADDR_LINKLOCAL(&ifp->int_addr))
+ continue;
+ supply(sin6, ifp, rtstate, splith);
+ }
+}
+
+static void
+solicit(struct sockaddr_in6 *sin6, struct interface *ifp)
+{
+ msg->rip6_cmd = RIPCMD6_REQUEST;
+ msg->rip6_vers = RIPVERSION6;
+ msg->rip6_nets[0].rip6_prefix = in6addr_any;
+ msg->rip6_nets[0].rip6_prefix_length = 0;
+ msg->rip6_nets[0].rip6_metric = HOPCNT_INFINITY;
+ sendpacket(sin6, ifp, sizeof (struct rip6), 0);
+}
+
+void
+solicitall(struct sockaddr_in6 *sin6)
+{
+ struct interface *ifp;
+
+ for (ifp = ifnet; ifp != NULL; ifp = ifp->int_next) {
+ if ((ifp->int_flags & RIP6_IFF_UP) == 0)
+ continue;
+ if (ifp->int_flags & RIP6_IFF_NORTEXCH) {
+ if (tracing & OUTPUT_BIT) {
+ (void) fprintf(ftrace,
+ "Suppress sending RIPng request packet "
+ "on %s (no route exchange on interface)\n",
+ ifp->int_name);
+ (void) fflush(ftrace);
+ }
+ continue;
+ }
+ if (ifp->int_sock == -1)
+ continue;
+ solicit(sin6, ifp);
+ }
+}
+
+
+/*
+ * Output a preformed packet.
+ */
+/*ARGSUSED*/
+void
+sendpacket(struct sockaddr_in6 *sin6, struct interface *ifp, int size,
+ int flags)
+{
+ if (sendto(ifp->int_sock, packet, size, flags,
+ (struct sockaddr *)sin6, sizeof (*sin6)) < 0) {
+ syslog(LOG_ERR, "sendpacket: sendto: %m");
+ return;
+ }
+ TRACE_OUTPUT(ifp, sin6, sizeof (struct rip6));
+ ifp->int_opackets++;
+}
+
+/*
+ * Supply dst with the contents of the routing tables.
+ * If this won't fit in one packet, chop it up into several.
+ */
+void
+supply(struct sockaddr_in6 *sin6, struct interface *ifp, int rtstate,
+ boolean_t splith)
+{
+ struct rt_entry *rt;
+ struct netinfo6 *n = msg->rip6_nets;
+ struct rthash *rh;
+ int size, i, maxsize;
+ uint8_t rtmetric;
+
+ msg->rip6_cmd = RIPCMD6_RESPONSE;
+ msg->rip6_vers = RIPVERSION6;
+
+ /*
+ * Initialize maxsize to the size of the largest RIPng packet supported
+ * on the outgoing interface.
+ */
+ maxsize = ifp->int_mtu - sizeof (ip6_t) - sizeof (struct udphdr);
+
+ for (i = IPV6_ABITS; i >= 0; i--) {
+ if (net_hashes[i] == NULL)
+ continue;
+
+ for (rh = net_hashes[i]; rh < &net_hashes[i][ROUTEHASHSIZ];
+ rh++) {
+ for (rt = rh->rt_forw; rt != (struct rt_entry *)rh;
+ rt = rt->rt_forw) {
+
+ if (IN6_IS_ADDR_LINKLOCAL(&rt->rt_dst))
+ continue;
+ if (IN6_IS_ADDR_UNSPECIFIED(&rt->rt_dst))
+ continue;
+
+ /* do not send if private */
+ if (rt->rt_state & RTS_PRIVATE)
+ continue;
+
+ /*
+ * Don't resend the information
+ * on the network from which it was received.
+ */
+ if (splith && rt->rt_ifp != NULL &&
+ strcmp(ifp->int_ifbase,
+ rt->rt_ifp->int_ifbase) == 0) {
+ if (dopoison)
+ rtmetric = HOPCNT_INFINITY;
+ else
+ continue;
+ } else {
+ rtmetric = rt->rt_metric;
+ }
+
+ /*
+ * For dynamic updates, limit update to routes
+ * with the specified state.
+ */
+ if (rtstate != 0 &&
+ (rt->rt_state & rtstate) == 0)
+ continue;
+
+ /*
+ * Check if there is space for another RTE. If
+ * not, send the packet built up and reset n for
+ * the remaining RTEs.
+ */
+ size = (char *)n - packet;
+ if (size > maxsize - sizeof (struct netinfo6)) {
+ sendpacket(sin6, ifp, size, 0);
+ TRACE_OUTPUT(ifp, sin6, size);
+ n = msg->rip6_nets;
+ }
+ n->rip6_prefix = rt->rt_dst;
+ n->rip6_route_tag = rt->rt_tag;
+ n->rip6_prefix_length = rt->rt_prefix_length;
+ n->rip6_metric = min(rtmetric, HOPCNT_INFINITY);
+ n++;
+ } /* end of hash chain */
+ } /* end of particular prefix length */
+ } /* end of all prefix lengths */
+ if (n != msg->rip6_nets) {
+ size = (char *)n - packet;
+ sendpacket(sin6, ifp, size, 0);
+ TRACE_OUTPUT(ifp, sin6, size);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.ripngd/startup.c b/usr/src/cmd/cmd-inet/usr.lib/in.ripngd/startup.c
new file mode 100644
index 0000000000..592f288193
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.ripngd/startup.c
@@ -0,0 +1,535 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * Portions of this source code were derived from Berkeley 4.3 BSD
+ * under license from the Regents of the University of California.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "defs.h"
+
+#define IF_SEPARATOR ':'
+
+struct interface *ifnet;
+
+static int setup_listen_sock(int ifindex);
+static void addrouteforif(struct interface *ifp);
+static void resetup_listen_sock(struct interface *, int);
+
+/*
+ * This is called at startup and after that, every CHECK_INTERVAL seconds or
+ * when a SIGHUP is received.
+ */
+void
+initifs(void)
+{
+ static char *buf = NULL;
+ static uint_t maxbufsize = 0;
+ int bufsize;
+ int numifs;
+ struct lifnum lifn;
+ struct lifconf lifc;
+ struct lifreq lifr;
+ struct lifreq *lifrp;
+ int n;
+ struct interface ifs;
+ struct interface *ifp;
+ int netmaskchange = 0;
+ boolean_t changes = _B_FALSE;
+
+ lifn.lifn_family = AF_INET6;
+ lifn.lifn_flags = 0;
+ if (ioctl(iocsoc, SIOCGLIFNUM, (char *)&lifn) < 0) {
+ syslog(LOG_ERR, "initifs: ioctl (get interface numbers): %m");
+ return;
+ }
+ numifs = lifn.lifn_count;
+ bufsize = numifs * sizeof (struct lifreq);
+
+ if (buf == NULL || bufsize > maxbufsize) {
+ if (buf != NULL)
+ free(buf);
+ maxbufsize = bufsize;
+ buf = (char *)malloc(maxbufsize);
+ if (buf == NULL) {
+ syslog(LOG_ERR, "initifs: out of memory");
+ return;
+ }
+ }
+
+ lifc.lifc_family = AF_INET6;
+ lifc.lifc_flags = 0;
+ lifc.lifc_len = bufsize;
+ lifc.lifc_buf = buf;
+ if (ioctl(iocsoc, SIOCGLIFCONF, (char *)&lifc) < 0) {
+ syslog(LOG_ERR,
+ "initifs: ioctl (get interface configuration): %m");
+ return;
+ }
+
+ /*
+ * Mark all of the currently known interfaces in order to determine
+ * which of the these interfaces no longer exist.
+ */
+ for (ifp = ifnet; ifp != NULL; ifp = ifp->int_next)
+ ifp->int_flags |= RIP6_IFF_MARKED;
+ lifrp = lifc.lifc_req;
+ for (n = lifc.lifc_len / sizeof (struct lifreq); n > 0; n--, lifrp++) {
+ bzero((char *)&ifs, sizeof (ifs));
+ (void) strncpy(lifr.lifr_name, lifrp->lifr_name,
+ sizeof (lifr.lifr_name));
+ if (ioctl(iocsoc, SIOCGLIFFLAGS, (char *)&lifr) < 0) {
+ syslog(LOG_ERR,
+ "initifs: ioctl (get interface flags): %m");
+ continue;
+ }
+ if (!(lifr.lifr_flags & IFF_IPV6) ||
+ !(lifr.lifr_flags & IFF_MULTICAST) ||
+ (lifr.lifr_flags & IFF_LOOPBACK))
+ continue;
+
+ ifp = if_ifwithname(lifr.lifr_name);
+ if (ifp != NULL)
+ ifp->int_flags &= ~RIP6_IFF_MARKED;
+ if (lifr.lifr_flags & IFF_POINTOPOINT)
+ ifs.int_flags |= RIP6_IFF_POINTOPOINT;
+ if (lifr.lifr_flags & IFF_NORTEXCH)
+ ifs.int_flags |= RIP6_IFF_NORTEXCH;
+ if (lifr.lifr_flags & IFF_PRIVATE)
+ ifs.int_flags |= RIP6_IFF_PRIVATE;
+ if (lifr.lifr_flags & IFF_UP) {
+ ifs.int_flags |= RIP6_IFF_UP;
+ } else {
+ if (ifp != NULL) {
+ if (ifp->int_flags & RIP6_IFF_UP) {
+ /*
+ * If there is an transition from up to
+ * down for an exisiting interface,
+ * increment the counter.
+ */
+ ifp->int_transitions++;
+ changes = _B_TRUE;
+ }
+ if_purge(ifp);
+ }
+ continue;
+ }
+
+ if (ifs.int_flags & RIP6_IFF_POINTOPOINT) {
+ /*
+ * For point-to-point interfaces, retrieve both the
+ * local and the remote addresses.
+ */
+ if (ioctl(iocsoc, SIOCGLIFADDR, (char *)&lifr) < 0) {
+ syslog(LOG_ERR,
+ "initifs: ioctl (get interface address): "
+ "%m");
+ continue;
+ }
+ ifs.int_addr =
+ ((struct sockaddr_in6 *)&lifr.lifr_addr)->sin6_addr;
+ if (ioctl(iocsoc, SIOCGLIFDSTADDR, (char *)&lifr) < 0) {
+ syslog(LOG_ERR,
+ "initifs: ioctl (get destination address): "
+ "%m");
+ continue;
+ }
+ ifs.int_dstaddr = ((struct sockaddr_in6 *)
+ &lifr.lifr_dstaddr)->sin6_addr;
+ ifs.int_prefix_length = IPV6_ABITS;
+ } else {
+ /*
+ * For other interfaces, retreieve the prefix (including
+ * the prefix length.
+ */
+ if (ioctl(iocsoc, SIOCGLIFSUBNET, (char *)&lifr) < 0) {
+ syslog(LOG_ERR,
+ "initifs: ioctl (get subnet prefix): %m");
+ continue;
+ }
+ /*
+ * This should never happen but check for it in any case
+ * since the kernel stores it as an signed integer.
+ */
+ if (lifr.lifr_addrlen < 0 ||
+ lifr.lifr_addrlen > IPV6_ABITS) {
+ syslog(LOG_ERR,
+ "initifs: ioctl (get subnet prefix) "
+ "returned invalid prefix length of %d",
+ lifr.lifr_addrlen);
+ continue;
+ }
+ ifs.int_prefix_length = lifr.lifr_addrlen;
+ ifs.int_addr = ((struct sockaddr_in6 *)
+ &lifr.lifr_subnet)->sin6_addr;
+ }
+
+ if (ioctl(iocsoc, SIOCGLIFMETRIC, (char *)&lifr) < 0 ||
+ lifr.lifr_metric < 0)
+ ifs.int_metric = 1;
+ else
+ ifs.int_metric = lifr.lifr_metric + 1;
+
+ if (ioctl(iocsoc, SIOCGLIFINDEX, (char *)&lifr) < 0) {
+ syslog(LOG_ERR, "initifs: ioctl (get index): %m");
+ continue;
+ }
+ ifs.int_ifindex = lifr.lifr_index;
+
+ if (ioctl(iocsoc, SIOCGLIFMTU, (char *)&lifr) < 0) {
+ syslog(LOG_ERR, "initifs: ioctl (get mtu): %m");
+ continue;
+ }
+
+ /*
+ * If the interface's recorded MTU doesn't make sense, use
+ * IPV6_MIN_MTU instead.
+ */
+ if (lifr.lifr_mtu < IPV6_MIN_MTU)
+ ifs.int_mtu = IPV6_MIN_MTU;
+ else
+ ifs.int_mtu = lifr.lifr_mtu;
+
+ if (ifp != NULL) {
+ /*
+ * RIP6_IFF_NORTEXCH flag change by itself shouldn't
+ * cause an if_purge() call, which also purges all the
+ * routes heard off this interface. So, let's suppress
+ * changes of RIP6_IFF_NORTEXCH in the following
+ * comparisons.
+ */
+ if (ifp->int_prefix_length == ifs.int_prefix_length &&
+ ((ifp->int_flags | RIP6_IFF_NORTEXCH) ==
+ (ifs.int_flags | RIP6_IFF_NORTEXCH)) &&
+ ifp->int_metric == ifs.int_metric &&
+ ifp->int_ifindex == ifs.int_ifindex) {
+ /*
+ * Now let's make sure we capture the latest
+ * value of RIP6_IFF_NORTEXCH flag.
+ */
+ if (ifs.int_flags & RIP6_IFF_NORTEXCH)
+ ifp->int_flags |= RIP6_IFF_NORTEXCH;
+ else
+ ifp->int_flags &= ~RIP6_IFF_NORTEXCH;
+
+ if (!(ifp->int_flags & RIP6_IFF_POINTOPOINT) &&
+ IN6_ARE_ADDR_EQUAL(&ifp->int_addr,
+ &ifs.int_addr))
+ continue;
+ if ((ifp->int_flags & RIP6_IFF_POINTOPOINT) &&
+ IN6_ARE_ADDR_EQUAL(&ifp->int_dstaddr,
+ &ifs.int_dstaddr))
+ continue;
+ }
+ if_purge(ifp);
+ if (ifp->int_prefix_length != ifs.int_prefix_length)
+ netmaskchange = 1;
+ ifp->int_addr = ifs.int_addr;
+ ifp->int_dstaddr = ifs.int_dstaddr;
+ ifp->int_metric = ifs.int_metric;
+ /*
+ * If there is an transition from down to up for an
+ * exisiting interface, increment the counter.
+ */
+ if (!(ifp->int_flags & RIP6_IFF_UP) &&
+ (ifs.int_flags & RIP6_IFF_UP))
+ ifp->int_transitions++;
+ ifp->int_flags |= ifs.int_flags;
+ ifp->int_prefix_length = ifs.int_prefix_length;
+
+ /*
+ * If the interface index has changed, we may need to
+ * set up the listen socket again.
+ */
+ if (ifp->int_ifindex != ifs.int_ifindex) {
+ if (ifp->int_sock != -1) {
+ resetup_listen_sock(ifp,
+ ifs.int_ifindex);
+ }
+ ifp->int_ifindex = ifs.int_ifindex;
+ }
+
+ ifp->int_mtu = ifs.int_mtu;
+ } else {
+ char *cp;
+ int log_num;
+
+ ifp = (struct interface *)
+ malloc(sizeof (struct interface));
+ if (ifp == NULL) {
+ syslog(LOG_ERR, "initifs: out of memory");
+ return;
+ }
+ *ifp = ifs;
+ ifp->int_name = ifp->int_ifbase = NULL;
+ ifp->int_name =
+ (char *)malloc((size_t)strlen(lifr.lifr_name) + 1);
+ if (ifp->int_name == NULL) {
+ free(ifp);
+ syslog(LOG_ERR, "initifs: out of memory");
+ return;
+ }
+ (void) strcpy(ifp->int_name, lifr.lifr_name);
+ ifp->int_ifbase =
+ (char *)malloc((size_t)strlen(lifr.lifr_name) + 1);
+ if (ifp->int_ifbase == NULL) {
+ free(ifp->int_name);
+ free(ifp);
+ syslog(LOG_ERR, "initifs: out of memory");
+ return;
+ }
+ (void) strcpy(ifp->int_ifbase, lifr.lifr_name);
+ cp = (char *)index(ifp->int_ifbase, IF_SEPARATOR);
+ if (cp != NULL) {
+ /*
+ * Verify that the value following the separator
+ * is an integer greater than zero (the only
+ * possible value for a logical interface).
+ */
+ log_num = atoi((char *)(cp + 1));
+ if (log_num <= 0) {
+ free(ifp->int_ifbase);
+ free(ifp->int_name);
+ free(ifp);
+ syslog(LOG_ERR,
+ "initifs: interface name %s could "
+ "not be parsed", ifp->int_name);
+ return;
+ }
+ *cp = '\0';
+ } else {
+ log_num = 0;
+ }
+ if (log_num == 0) {
+ ifp->int_sock =
+ setup_listen_sock(ifp->int_ifindex);
+ } else {
+ ifp->int_sock = -1;
+ }
+ ifp->int_next = ifnet;
+ ifnet = ifp;
+ traceinit(ifp);
+ }
+ addrouteforif(ifp);
+ changes = _B_TRUE;
+ }
+
+ /*
+ * Any remaining interfaces that are still marked and which were in an
+ * up state (RIP6_IFF_UP) need to removed from the routing table.
+ */
+ for (ifp = ifnet; ifp != NULL; ifp = ifp->int_next) {
+ if ((ifp->int_flags & (RIP6_IFF_MARKED | RIP6_IFF_UP)) ==
+ (RIP6_IFF_MARKED | RIP6_IFF_UP)) {
+ if_purge(ifp);
+ ifp->int_flags &= ~RIP6_IFF_MARKED;
+ changes = _B_TRUE;
+ }
+ }
+ if (netmaskchange)
+ rtchangeall();
+ if (supplier & changes)
+ dynamic_update((struct interface *)NULL);
+}
+
+static void
+addrouteforif(struct interface *ifp)
+{
+ struct rt_entry *rt;
+ struct in6_addr *dst;
+
+ if (ifp->int_flags & RIP6_IFF_POINTOPOINT)
+ dst = &ifp->int_dstaddr;
+ else
+ dst = &ifp->int_addr;
+
+ rt = rtlookup(dst, ifp->int_prefix_length);
+
+ if (rt != NULL) {
+ if (rt->rt_state & RTS_INTERFACE)
+ return;
+ rtdelete(rt);
+ }
+ rtadd(dst, &ifp->int_addr, ifp->int_prefix_length, ifp->int_metric, 0,
+ _B_TRUE, ifp);
+}
+
+static int
+setup_listen_sock(int ifindex)
+{
+ int sock;
+ struct sockaddr_in6 sin6;
+ uint_t hops;
+ struct ipv6_mreq allrouters_mreq;
+ int on = 1;
+ int off = 0;
+ int recvsize;
+
+ sock = socket(AF_INET6, SOCK_DGRAM, 0);
+ if (sock == -1)
+ goto sock_fail;
+
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_BOUND_IF, (char *)&ifindex,
+ sizeof (ifindex)) < 0) {
+ syslog(LOG_ERR,
+ "setup_listen_sock: setsockopt: IPV6_BOUND_IF: %m");
+ goto sock_fail;
+ }
+
+ hops = IPV6_MAX_HOPS;
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, (char *)&hops,
+ sizeof (hops)) < 0) {
+ syslog(LOG_ERR,
+ "setup_listen_sock: setsockopt: IPV6_UNICAST_HOPS: %m");
+ goto sock_fail;
+ }
+
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (char *)&hops,
+ sizeof (hops)) < 0) {
+ syslog(LOG_ERR,
+ "setup_listen_sock: setsockopt: IPV6_MULTICAST_HOPS: %m");
+ goto sock_fail;
+ }
+
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (char *)&off,
+ sizeof (off)) < 0) {
+ syslog(LOG_ERR,
+ "setup_listen_sock: setsockopt: IPV6_MULTICAST_LOOP: %m");
+ goto sock_fail;
+ }
+
+ allrouters_mreq.ipv6mr_multiaddr = allrouters_in6;
+ allrouters_mreq.ipv6mr_interface = ifindex;
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP,
+ (char *)&allrouters_mreq, sizeof (allrouters_mreq)) < 0) {
+ if (errno != EADDRINUSE) {
+ syslog(LOG_ERR,
+ "setup_listen_sock: setsockopt: "
+ "IPV6_JOIN_GROUP: %m");
+ goto sock_fail;
+ }
+ }
+
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, (char *)&on,
+ sizeof (off)) < 0) {
+ syslog(LOG_ERR,
+ "setup_listen_sock: setsockopt: IPV6_RECVHOPLIMIT: %m");
+ goto sock_fail;
+ }
+
+ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&on,
+ sizeof (on)) < 0) {
+ syslog(LOG_ERR,
+ "setup_listen_sock: setsockopt: SO_REUSEADDR: %m");
+ goto sock_fail;
+ }
+
+ recvsize = RCVBUFSIZ;
+ if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *)&recvsize,
+ sizeof (int)) < 0) {
+ syslog(LOG_ERR, "setup_listen_sock: setsockopt: SO_RCVBUF: %m");
+ goto sock_fail;
+ }
+
+ bzero((char *)&sin6, sizeof (sin6));
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_port = rip6_port;
+ if (bind(sock, (struct sockaddr *)&sin6, sizeof (sin6)) < 0) {
+ syslog(LOG_ERR, "setup_listen_sock: bind: %m");
+ goto sock_fail;
+ }
+
+ poll_ifs_num++;
+ if (poll_ifs == NULL) {
+ poll_ifs = (struct pollfd *)
+ malloc(max_poll_ifs * sizeof (struct pollfd));
+ } else if (poll_ifs_num > max_poll_ifs) {
+ max_poll_ifs *= 2;
+ poll_ifs = (struct pollfd *)realloc((char *)poll_ifs,
+ max_poll_ifs * sizeof (struct pollfd));
+ }
+ if (poll_ifs == NULL) {
+ syslog(LOG_ERR, "setup_listen_sock: out of memory");
+ goto sock_fail;
+ }
+
+ poll_ifs[poll_ifs_num - 1].fd = sock;
+ poll_ifs[poll_ifs_num - 1].events = POLLIN;
+ return (sock);
+
+sock_fail:
+ if (sock > 0)
+ (void) close(sock);
+ return (-1);
+}
+
+/*
+ * resetup_listen_sock is primarily used in the case where a tunnel was
+ * plumbed, unplumbed, then plumbed again. This would cause the binding set by
+ * IPV6_BOUND_IF to be useless, and sends to the associated socket will be
+ * transmitted on the wrong interface. resetup_listen_sock
+ * closes the socket,
+ * removes the socket from poll_ifs[]
+ * plugs the hole in poll_ifs[]
+ * calls setup_listen_sock to set up the socket again
+ */
+void
+resetup_listen_sock(struct interface *ifp, int newindex)
+{
+ int i;
+
+ (void) close(ifp->int_sock);
+
+ /* Remove socket from poll_ifs[]. */
+ for (i = poll_ifs_num - 1; i >= 0; i--) {
+
+ if (poll_ifs[i].fd == ifp->int_sock) {
+
+ poll_ifs[i].fd = 0;
+ poll_ifs[i].events = 0;
+
+ /*
+ * Remove hole in poll_ifs. Possibly exchange
+ * poll_ifs[i] with poll_ifs[poll_ifs_num-1].
+ */
+ if (i != poll_ifs_num - 1) {
+ poll_ifs[i] = poll_ifs[poll_ifs_num - 1];
+ poll_ifs[poll_ifs_num - 1].fd = 0;
+ poll_ifs[poll_ifs_num - 1].events = 0;
+ }
+ poll_ifs_num--;
+
+ /* Now set everything up again. */
+ ifp->int_sock = setup_listen_sock(newindex);
+ break;
+ }
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.ripngd/table.h b/usr/src/cmd/cmd-inet/usr.lib/in.ripngd/table.h
new file mode 100644
index 0000000000..0458133c8a
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.ripngd/table.h
@@ -0,0 +1,92 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1999 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * Portions of this source code were derived from Berkeley 4.3 BSD
+ * under license from the Regents of the University of California.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Routing table management daemon.
+ */
+
+/*
+ * Routing table structure; differs a bit from kernel tables.
+ */
+struct rthash {
+ struct rt_entry *rt_forw;
+ struct rt_entry *rt_back;
+};
+
+struct rt_entry {
+ struct rt_entry *rt_forw;
+ struct rt_entry *rt_back;
+ uint_t rt_hash; /* for net or host */
+ struct in6_addr rt_dst; /* match value */
+ struct in6_addr rt_router; /* who to forward to */
+ int rt_prefix_length; /* bits in prefix */
+ struct interface *rt_ifp; /* interface to take */
+ uint_t rt_flags; /* kernel flags */
+ uint_t rt_state; /* see below */
+ int rt_timer; /* for invalidation */
+ int rt_metric; /* cost of route including the if */
+ int rt_tag; /* route tag attribute */
+};
+
+#define ROUTEHASHSIZ 32 /* must be a power of 2 */
+#define ROUTEHASHMASK (ROUTEHASHSIZ - 1)
+
+/*
+ * "State" of routing table entry.
+ */
+#define RTS_CHANGED 0x1 /* route has been altered recently */
+#define RTS_INTERFACE 0x2 /* route is for network interface */
+#define RTS_PRIVATE 0x4 /* route is private, do not advertise */
+
+/*
+ * XXX This is defined in <inet/ip.h> (but should be defined in <netinet/ip6.h>
+ * for completeness).
+ */
+#define IPV6_ABITS 128 /* Number of bits in an IPv6 address */
+
+extern struct rthash *net_hashes[IPV6_ABITS + 1];
+
+extern void rtadd(struct in6_addr *, struct in6_addr *, int, int, int,
+ boolean_t, struct interface *);
+extern void rtchange(struct rt_entry *, struct in6_addr *, short,
+ struct interface *);
+extern void rtchangeall(void);
+extern void rtcreate_prefix(struct in6_addr *, struct in6_addr *, int);
+extern void rtdelete(struct rt_entry *);
+extern void rtdown(struct rt_entry *);
+extern void rtdump(void);
+extern struct rt_entry *rtlookup(struct in6_addr *, int);
+extern void rtpurgeif(struct interface *);
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.ripngd/tables.c b/usr/src/cmd/cmd-inet/usr.lib/in.ripngd/tables.c
new file mode 100644
index 0000000000..a59f23458f
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.ripngd/tables.c
@@ -0,0 +1,697 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * Portions of this source code were derived from Berkeley 4.3 BSD
+ * under license from the Regents of the University of California.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Routing Table Management Daemon
+ */
+#include "defs.h"
+
+boolean_t install = _B_TRUE; /* update kernel routing table */
+struct rthash *net_hashes[IPV6_ABITS + 1];
+
+/*
+ * Size of routing socket message used by in.ripngd which includes the header,
+ * space for the RTA_DST, RTA_GATEWAY and RTA_NETMASK (each a sockaddr_in6)
+ * plus space for the RTA_IFP (a sockaddr_dl).
+ */
+#define RIPNG_RTM_MSGLEN sizeof (struct rt_msghdr) + \
+ sizeof (struct sockaddr_in6) + \
+ sizeof (struct sockaddr_in6) + \
+ sizeof (struct sockaddr_in6) + \
+ sizeof (struct sockaddr_dl)
+
+static int rtmseq; /* rtm_seq sequence number */
+static int rtsock; /* Routing socket */
+static struct rt_msghdr *rt_msg; /* Routing socket message */
+static struct sockaddr_in6 *rta_dst; /* RTA_DST sockaddr */
+static struct sockaddr_in6 *rta_gateway; /* RTA_GATEWAY sockaddr */
+static struct sockaddr_in6 *rta_netmask; /* RTA_NETMASK sockaddr */
+static struct sockaddr_dl *rta_ifp; /* RTA_IFP sockaddr */
+
+/* simulate vax insque and remque instructions. */
+
+typedef struct vq {
+ caddr_t fwd, back;
+} vq_t;
+
+#define insque(e, p) ((vq_t *)(e))->back = (caddr_t)(p); \
+ ((vq_t *)(e))->fwd = \
+ (caddr_t)((vq_t *)((vq_t *)(p))->fwd); \
+ ((vq_t *)((vq_t *)(p))->fwd)->back = (caddr_t)(e); \
+ ((vq_t *)(p))->fwd = (caddr_t)(e);
+
+#define remque(e) ((vq_t *)((vq_t *)(e))->back)->fwd = \
+ (caddr_t)((vq_t *)(e))->fwd; \
+ ((vq_t *)((vq_t *)(e))->fwd)->back = \
+ (caddr_t)((vq_t *)(e))->back; \
+ ((vq_t *)(e))->fwd = NULL; \
+ ((vq_t *)(e))->back = NULL;
+
+static void
+log_change(int level, struct rt_entry *orig, struct rt_entry *new)
+{
+ char buf1[INET6_ADDRSTRLEN];
+ char buf2[INET6_ADDRSTRLEN];
+ char buf3[INET6_ADDRSTRLEN];
+
+ (void) inet_ntop(AF_INET6, (void *) &new->rt_dst, buf1, sizeof (buf1));
+ (void) inet_ntop(AF_INET6, (void *) &orig->rt_router, buf2,
+ sizeof (buf2));
+ (void) inet_ntop(AF_INET6, (void *) &new->rt_router, buf3,
+ sizeof (buf3));
+
+ syslog(level, "\tdst %s from gw %s if %s to gw %s if %s metric %d",
+ buf1, buf2,
+ (orig->rt_ifp != NULL && orig->rt_ifp->int_name != NULL) ?
+ orig->rt_ifp->int_name : "(noname)",
+ buf3,
+ (new->rt_ifp != NULL && new->rt_ifp->int_name != NULL) ?
+ new->rt_ifp->int_name : "(noname)", new->rt_metric);
+}
+
+static void
+log_single(int level, struct rt_entry *rt)
+{
+ char buf1[INET6_ADDRSTRLEN];
+ char buf2[INET6_ADDRSTRLEN];
+
+ (void) inet_ntop(AF_INET6, (void *)&rt->rt_dst, buf1, sizeof (buf1));
+ (void) inet_ntop(AF_INET6, (void *)&rt->rt_router, buf2, sizeof (buf2));
+
+ syslog(level, "\tdst %s gw %s if %s metric %d",
+ buf1, buf2,
+ (rt->rt_ifp != NULL && rt->rt_ifp->int_name != NULL) ?
+ rt->rt_ifp->int_name : "(noname)",
+ rt->rt_metric);
+}
+
+/*
+ * Computes a hash by XOR-ing the (up to sixteen) octets that make up an IPv6
+ * address. This function assumes that that there are no one-bits in the
+ * address beyond the prefix length.
+ */
+static uint8_t
+rthash(struct in6_addr *dst, int prefix_length)
+{
+ uint8_t val = 0;
+ int i;
+
+ for (i = 0; prefix_length > 0; prefix_length -= 8, i++)
+ val ^= dst->s6_addr[i];
+ return (val);
+}
+
+/*
+ * Given a prefix length, fill in the struct in6_addr representing an IPv6
+ * netmask.
+ */
+static void
+rtmask_to_bits(uint_t prefix_length, struct in6_addr *prefix)
+{
+ uint_t mask = 0xff;
+ int i;
+
+ bzero((caddr_t)prefix, sizeof (struct in6_addr));
+ for (i = 0; prefix_length >= 8; prefix_length -= 8, i++)
+ prefix->s6_addr[i] = 0xff;
+ mask = (mask << (8 - prefix_length));
+ if (mask != 0)
+ prefix->s6_addr[i] = mask;
+}
+
+void
+rtcreate_prefix(struct in6_addr *p1, struct in6_addr *dst, int bits)
+{
+ uchar_t mask;
+ int j;
+
+ for (j = 0; bits >= 8; bits -= 8, j++)
+ dst->s6_addr[j] = p1->s6_addr[j];
+
+ if (bits != 0) {
+ mask = 0xff << (8 - bits);
+ dst->s6_addr[j] = p1->s6_addr[j] & mask;
+ j++;
+ }
+
+ for (; j < 16; j++)
+ dst->s6_addr[j] = 0;
+}
+
+/*
+ * Lookup dst in the tables for an exact match.
+ */
+struct rt_entry *
+rtlookup(struct in6_addr *dst, int prefix_length)
+{
+ struct rt_entry *rt;
+ struct rthash *rh;
+ uint_t hash;
+
+ if (net_hashes[prefix_length] == NULL)
+ return (NULL);
+
+ hash = rthash(dst, prefix_length);
+
+ rh = &net_hashes[prefix_length][hash & ROUTEHASHMASK];
+
+ for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
+ if (rt->rt_hash != hash)
+ continue;
+ if (IN6_ARE_ADDR_EQUAL(&rt->rt_dst, dst) &&
+ rt->rt_prefix_length == prefix_length)
+ return (rt);
+ }
+ return (NULL);
+}
+
+/*
+ * Given an IPv6 prefix (destination and prefix length), a gateway, an
+ * interface name and route flags, send down the requested command returning
+ * the return value and errno (in the case of error) from the write() on the
+ * routing socket.
+ */
+static int
+rtcmd(uchar_t type, struct in6_addr *dst, struct in6_addr *gateway,
+ uint_t prefix_length, char *name, int flags)
+{
+ int rlen;
+
+ rta_ifp->sdl_index = if_nametoindex(name);
+ if (rta_ifp->sdl_index == 0)
+ return (-1);
+
+ rta_dst->sin6_addr = *dst;
+ rta_gateway->sin6_addr = *gateway;
+ rtmask_to_bits(prefix_length, &rta_netmask->sin6_addr);
+
+ rt_msg->rtm_type = type;
+ rt_msg->rtm_flags = flags;
+ rt_msg->rtm_seq = ++rtmseq;
+ rlen = write(rtsock, rt_msg, RIPNG_RTM_MSGLEN);
+ if (rlen >= 0 && rlen < RIPNG_RTM_MSGLEN) {
+ syslog(LOG_ERR,
+ "rtcmd: write to routing socket got only %d for rlen\n",
+ rlen);
+ }
+ return (rlen);
+}
+
+void
+rtadd(struct in6_addr *dst, struct in6_addr *gate, int prefix_length,
+ int metric, int tag, boolean_t ifroute, struct interface *ifp)
+{
+ struct rt_entry *rt;
+ struct rthash *rh;
+ uint_t hash;
+ struct in6_addr pdst;
+ int rlen;
+
+ if (metric >= HOPCNT_INFINITY)
+ return;
+
+ if (net_hashes[prefix_length] == NULL) {
+ struct rthash *trh;
+
+ rh = (struct rthash *)
+ calloc(ROUTEHASHSIZ, sizeof (struct rt_entry));
+ if (rh == NULL)
+ return;
+ for (trh = rh; trh < &rh[ROUTEHASHSIZ]; trh++)
+ trh->rt_forw = trh->rt_back = (struct rt_entry *)trh;
+ net_hashes[prefix_length] = rh;
+ }
+ rtcreate_prefix(dst, &pdst, prefix_length);
+
+ hash = rthash(&pdst, prefix_length);
+ rh = &net_hashes[prefix_length][hash & ROUTEHASHMASK];
+ rt = (struct rt_entry *)malloc(sizeof (*rt));
+ if (rt == NULL) {
+ /*
+ * In the event of an allocation failure, log the error and
+ * continue since on the next update another attempt will be
+ * made.
+ */
+ syslog(LOG_ERR, "rtadd: malloc: %m");
+ return;
+ }
+ rt->rt_hash = hash;
+ rt->rt_dst = pdst;
+ rt->rt_prefix_length = prefix_length;
+ rt->rt_router = *gate;
+ rt->rt_metric = metric;
+ rt->rt_tag = tag;
+ rt->rt_timer = 0;
+ rt->rt_flags = RTF_UP;
+ if (prefix_length == IPV6_ABITS)
+ rt->rt_flags |= RTF_HOST;
+ rt->rt_state = RTS_CHANGED;
+ if (ifroute) {
+ rt->rt_state |= RTS_INTERFACE;
+ if (ifp->int_flags & RIP6_IFF_PRIVATE)
+ rt->rt_state |= RTS_PRIVATE;
+ } else {
+ rt->rt_flags |= RTF_GATEWAY;
+ }
+ rt->rt_ifp = ifp;
+
+ insque(rt, rh);
+ TRACE_ACTION("ADD", rt);
+ /*
+ * If the RTM_ADD fails because the gateway is unreachable
+ * from this host, discard the entry. This should never
+ * happen.
+ */
+ if (install && (rt->rt_state & RTS_INTERFACE) == 0) {
+ rlen = rtcmd(RTM_ADD, &rt->rt_dst, &rt->rt_router,
+ prefix_length, ifp->int_name, rt->rt_flags);
+ if (rlen < 0) {
+ if (errno != EEXIST) {
+ syslog(LOG_ERR, "rtadd: RTM_ADD: %m");
+ log_single(LOG_ERR, rt);
+ }
+ if (errno == ENETUNREACH) {
+ TRACE_ACTION("DELETE", rt);
+ remque(rt);
+ free((char *)rt);
+ }
+ } else if (rlen < RIPNG_RTM_MSGLEN) {
+ log_single(LOG_ERR, rt);
+ }
+ }
+}
+
+/*
+ * Handle the case when the metric changes but the gateway is the same (or the
+ * interface index associated with the gateway changes), or when both gateway
+ * and metric changes, or when only the gateway changes but the existing route
+ * is more than one-half of EXPIRE_TIME in age. Note that routes with metric >=
+ * HOPCNT_INFINITY are not in the kernel.
+ */
+void
+rtchange(struct rt_entry *rt, struct in6_addr *gate, short metric,
+ struct interface *ifp)
+{
+ boolean_t dokern = _B_FALSE;
+ boolean_t dokerndelete;
+ boolean_t metricchanged = _B_FALSE;
+ int oldmetric;
+ struct rt_entry oldroute;
+ int rlen;
+
+ if (metric >= HOPCNT_INFINITY) {
+ rtdown(rt);
+ return;
+ }
+
+ if (!IN6_ARE_ADDR_EQUAL(&rt->rt_router, gate) || rt->rt_ifp != ifp)
+ dokern = _B_TRUE;
+ oldmetric = rt->rt_metric;
+ if (oldmetric >= HOPCNT_INFINITY)
+ dokerndelete = _B_FALSE;
+ else
+ dokerndelete = dokern;
+ if (metric != rt->rt_metric)
+ metricchanged = _B_TRUE;
+ rt->rt_timer = 0;
+ if (dokern || metricchanged) {
+ TRACE_ACTION("CHANGE FROM", rt);
+ if ((rt->rt_state & RTS_INTERFACE) && metric != 0) {
+ rt->rt_state &= ~RTS_INTERFACE;
+ if (rt->rt_ifp != NULL) {
+ syslog(LOG_ERR,
+ "rtchange: changing route from "
+ "interface %s (timed out)",
+ (rt->rt_ifp->int_name != NULL) ?
+ rt->rt_ifp->int_name : "(noname)");
+ } else {
+ syslog(LOG_ERR,
+ "rtchange: "
+ "changing route no interface for route");
+ }
+ }
+ if (dokern) {
+ oldroute = *rt;
+ rt->rt_router = *gate;
+ rt->rt_ifp = ifp;
+ }
+ rt->rt_metric = metric;
+ if (!(rt->rt_state & RTS_INTERFACE))
+ rt->rt_flags |= RTF_GATEWAY;
+ else
+ rt->rt_flags &= ~RTF_GATEWAY;
+ rt->rt_state |= RTS_CHANGED;
+ TRACE_ACTION("CHANGE TO", rt);
+ }
+ if (install && (rt->rt_state & RTS_INTERFACE) == 0) {
+ if (dokerndelete) {
+ rlen = rtcmd(RTM_ADD, &rt->rt_dst, &rt->rt_router,
+ rt->rt_prefix_length, rt->rt_ifp->int_name,
+ rt->rt_flags);
+ if (rlen < 0) {
+ if (errno != EEXIST) {
+ syslog(LOG_ERR,
+ "rtchange: RTM_ADD: %m");
+ log_change(LOG_ERR, rt,
+ (struct rt_entry *)&oldroute);
+ }
+ } else if (rlen < RIPNG_RTM_MSGLEN) {
+ log_change(LOG_ERR, rt,
+ (struct rt_entry *)&oldroute);
+ }
+
+ rlen = rtcmd(RTM_DELETE, &oldroute.rt_dst,
+ &oldroute.rt_router, oldroute.rt_prefix_length,
+ oldroute.rt_ifp->int_name, oldroute.rt_flags);
+ if (rlen < 0) {
+ syslog(LOG_ERR, "rtchange: RTM_DELETE: %m");
+ log_change(LOG_ERR, rt,
+ (struct rt_entry *)&oldroute);
+ } else if (rlen < RIPNG_RTM_MSGLEN) {
+ log_change(LOG_ERR, rt,
+ (struct rt_entry *)&oldroute);
+ }
+ } else if (dokern || oldmetric >= HOPCNT_INFINITY) {
+ rlen = rtcmd(RTM_ADD, &rt->rt_dst, &rt->rt_router,
+ rt->rt_prefix_length, ifp->int_name, rt->rt_flags);
+ if (rlen < 0 && errno != EEXIST) {
+ syslog(LOG_ERR, "rtchange: RTM_ADD: %m");
+ log_change(LOG_ERR, rt,
+ (struct rt_entry *)&oldroute);
+ } else if (rlen < RIPNG_RTM_MSGLEN) {
+ log_change(LOG_ERR, rt,
+ (struct rt_entry *)&oldroute);
+ }
+ }
+ }
+}
+
+void
+rtdown(struct rt_entry *rt)
+{
+ int rlen;
+
+ if (rt->rt_metric != HOPCNT_INFINITY) {
+ TRACE_ACTION("DELETE", rt);
+ if (install && (rt->rt_state & RTS_INTERFACE) == 0) {
+ rlen = rtcmd(RTM_DELETE, &rt->rt_dst,
+ &rt->rt_router, rt->rt_prefix_length,
+ rt->rt_ifp->int_name, rt->rt_flags);
+ if (rlen < 0) {
+ syslog(LOG_ERR, "rtdown: RTM_DELETE: %m");
+ log_single(LOG_ERR, rt);
+ } else if (rlen < RIPNG_RTM_MSGLEN) {
+ log_single(LOG_ERR, rt);
+ }
+ }
+ rt->rt_metric = HOPCNT_INFINITY;
+ rt->rt_state |= RTS_CHANGED;
+ }
+ if (rt->rt_timer < EXPIRE_TIME)
+ rt->rt_timer = EXPIRE_TIME;
+}
+
+void
+rtdelete(struct rt_entry *rt)
+{
+
+ if (rt->rt_state & RTS_INTERFACE) {
+ if (rt->rt_ifp != NULL) {
+ syslog(LOG_ERR,
+ "rtdelete: "
+ "deleting route to interface %s (timed out)",
+ (rt->rt_ifp->int_name != NULL) ?
+ rt->rt_ifp->int_name : "(noname)");
+ log_single(LOG_ERR, rt);
+ }
+ }
+ rtdown(rt);
+ remque(rt);
+ free((char *)rt);
+}
+
+/*
+ * Mark all the routes heard off a particular interface "down". Unlike the
+ * routes managed by in.routed, all of these routes have an interface associated
+ * with them.
+ */
+void
+rtpurgeif(struct interface *ifp)
+{
+ struct rthash *rh;
+ struct rt_entry *rt;
+ int i;
+
+ for (i = IPV6_ABITS; i >= 0; i--) {
+ if (net_hashes[i] == NULL)
+ continue;
+
+ for (rh = net_hashes[i];
+ rh < &net_hashes[i][ROUTEHASHSIZ]; rh++) {
+ for (rt = rh->rt_forw; rt != (struct rt_entry *)rh;
+ rt = rt->rt_forw) {
+ if (rt->rt_ifp == ifp) {
+ rtdown(rt);
+ rt->rt_ifp = NULL;
+ rt->rt_state &= ~RTS_INTERFACE;
+ }
+ }
+ }
+ }
+}
+
+/*
+ * Called when the subnetmask has changed on one or more interfaces.
+ * Re-evaluates all non-interface routes by doing a rtchange so that
+ * routes that were believed to be host routes before the netmask change
+ * can be converted to network routes and vice versa.
+ */
+void
+rtchangeall(void)
+{
+ struct rthash *rh;
+ struct rt_entry *rt;
+ int i;
+
+ for (i = IPV6_ABITS; i >= 0; i--) {
+ if (net_hashes[i] == NULL)
+ continue;
+
+ for (rh = net_hashes[i];
+ rh < &net_hashes[i][ROUTEHASHSIZ]; rh++) {
+ for (rt = rh->rt_forw; rt != (struct rt_entry *)rh;
+ rt = rt->rt_forw) {
+ if ((rt->rt_state & RTS_INTERFACE) == 0) {
+ rtchange(rt, &rt->rt_router,
+ rt->rt_metric, rt->rt_ifp);
+ }
+ }
+ }
+ }
+}
+
+static void
+rtdumpentry(FILE *fp, struct rt_entry *rt)
+{
+ char buf1[INET6_ADDRSTRLEN];
+ static struct bits {
+ ulong_t t_bits;
+ char *t_name;
+ } flagbits[] = {
+ /* BEGIN CSTYLED */
+ { RTF_UP, "UP" },
+ { RTF_GATEWAY, "GATEWAY" },
+ { RTF_HOST, "HOST" },
+ { 0, NULL }
+ /* END CSTYLED */
+ }, statebits[] = {
+ /* BEGIN CSTYLED */
+ { RTS_INTERFACE, "INTERFACE" },
+ { RTS_CHANGED, "CHANGED" },
+ { RTS_PRIVATE, "PRIVATE" },
+ { 0, NULL }
+ /* END CSTYLED */
+ };
+ struct bits *p;
+ boolean_t first;
+ char c;
+
+ (void) fprintf(fp, "prefix %s/%d ",
+ inet_ntop(AF_INET6, (void *)&rt->rt_dst, buf1, sizeof (buf1)),
+ rt->rt_prefix_length);
+ (void) fprintf(fp, "via %s metric %d timer %d",
+ inet_ntop(AF_INET6, (void *)&rt->rt_router, buf1, sizeof (buf1)),
+ rt->rt_metric, rt->rt_timer);
+ if (rt->rt_ifp != NULL) {
+ (void) fprintf(fp, " if %s",
+ (rt->rt_ifp->int_name != NULL) ?
+ rt->rt_ifp->int_name : "(noname)");
+ }
+ (void) fprintf(fp, " state");
+ c = ' ';
+ for (first = _B_TRUE, p = statebits; p->t_bits > 0; p++) {
+ if ((rt->rt_state & p->t_bits) == 0)
+ continue;
+ (void) fprintf(fp, "%c%s", c, p->t_name);
+ if (first) {
+ c = '|';
+ first = _B_FALSE;
+ }
+ }
+ if (first)
+ (void) fprintf(fp, " 0");
+ if (rt->rt_flags & (RTF_UP | RTF_GATEWAY)) {
+ c = ' ';
+ for (first = _B_TRUE, p = flagbits; p->t_bits > 0; p++) {
+ if ((rt->rt_flags & p->t_bits) == 0)
+ continue;
+ (void) fprintf(fp, "%c%s", c, p->t_name);
+ if (first) {
+ c = '|';
+ first = _B_FALSE;
+ }
+ }
+ }
+ (void) putc('\n', fp);
+ (void) fflush(fp);
+}
+
+static void
+rtdump2(FILE *fp)
+{
+ struct rthash *rh;
+ struct rt_entry *rt;
+ int i;
+
+ for (i = IPV6_ABITS; i >= 0; i--) {
+ if (net_hashes[i] == NULL)
+ continue;
+
+ for (rh = net_hashes[i];
+ rh < &net_hashes[i][ROUTEHASHSIZ]; rh++) {
+ for (rt = rh->rt_forw; rt != (struct rt_entry *)rh;
+ rt = rt->rt_forw) {
+ rtdumpentry(fp, rt);
+ }
+ }
+ }
+}
+
+void
+rtdump(void)
+{
+ if (ftrace != NULL)
+ rtdump2(ftrace);
+ else
+ rtdump2(stderr);
+}
+
+/*
+ * Create a routing socket for sending RTM_ADD and RTM_DELETE messages and
+ * initialize the routing socket message header and as much of the sockaddrs
+ * as possible.
+ */
+void
+setup_rtsock(void)
+{
+ char *cp;
+ int off = 0;
+
+ rtsock = socket(PF_ROUTE, SOCK_RAW, AF_INET6);
+ if (rtsock < 0) {
+ syslog(LOG_ERR, "setup_rtsock: socket: %m");
+ exit(EXIT_FAILURE);
+ }
+
+ /* We don't want to listen to our own messages */
+ if (setsockopt(rtsock, SOL_SOCKET, SO_USELOOPBACK, (char *)&off,
+ sizeof (off)) < 0) {
+ syslog(LOG_ERR, "setup_rtsock: setsockopt: SO_USELOOPBACK: %m");
+ exit(EXIT_FAILURE);
+ }
+
+ /*
+ * Allocate storage for the routing socket message.
+ */
+ rt_msg = (struct rt_msghdr *)malloc(RIPNG_RTM_MSGLEN);
+ if (rt_msg == NULL) {
+ syslog(LOG_ERR, "setup_rtsock: malloc: %m");
+ exit(EXIT_FAILURE);
+ }
+
+ /*
+ * Initialize the routing socket message by zero-filling it and then
+ * setting the fields where are constant through the lifetime of the
+ * process.
+ */
+ bzero(rt_msg, RIPNG_RTM_MSGLEN);
+ rt_msg->rtm_msglen = RIPNG_RTM_MSGLEN;
+ rt_msg->rtm_version = RTM_VERSION;
+ rt_msg->rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK | RTA_IFP;
+ rt_msg->rtm_pid = getpid();
+ if (rt_msg->rtm_pid < 0) {
+ syslog(LOG_ERR, "setup_rtsock: getpid: %m");
+ exit(EXIT_FAILURE);
+ }
+
+ /*
+ * Initialize the constant portion of the RTA_DST sockaddr.
+ */
+ cp = (char *)rt_msg + sizeof (struct rt_msghdr);
+ rta_dst = (struct sockaddr_in6 *)cp;
+ rta_dst->sin6_family = AF_INET6;
+
+ /*
+ * Initialize the constant portion of the RTA_GATEWAY sockaddr.
+ */
+ cp += sizeof (struct sockaddr_in6);
+ rta_gateway = (struct sockaddr_in6 *)cp;
+ rta_gateway->sin6_family = AF_INET6;
+
+ /*
+ * Initialize the constant portion of the RTA_NETMASK sockaddr.
+ */
+ cp += sizeof (struct sockaddr_in6);
+ rta_netmask = (struct sockaddr_in6 *)cp;
+ rta_netmask->sin6_family = AF_INET6;
+
+ /*
+ * Initialize the constant portion of the RTA_IFP sockaddr.
+ */
+ cp += sizeof (struct sockaddr_in6);
+ rta_ifp = (struct sockaddr_dl *)cp;
+ rta_ifp->sdl_family = AF_LINK;
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.ripngd/timer.c b/usr/src/cmd/cmd-inet/usr.lib/in.ripngd/timer.c
new file mode 100644
index 0000000000..5339b56907
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.ripngd/timer.c
@@ -0,0 +1,181 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * Portions of this source code were derived from Berkeley 4.3 BSD
+ * under license from the Regents of the University of California.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Routing Table Management Daemon
+ */
+#include "defs.h"
+
+int supplyinterval; /* current supply interval */
+
+/*
+ * Timer routine. Performs routing information supply
+ * duties and manages timers on routing table entries.
+ * Management of the RTS_CHANGED bit assumes that we multicast
+ * each time called.
+ */
+void
+timer(void)
+{
+ struct rthash *rh;
+ struct rt_entry *rt;
+ boolean_t timetomulticast = _B_FALSE;
+ int i;
+ static int iftime; /* interface timer */
+ static int mtime; /* periodic mcast supply timer */
+ static int alarmtime = 0; /* time elapsed since last call */
+ int mintime; /* tracks when next timer will expire */
+
+ /*
+ * On the initial call to timer(), the various times that are kept track
+ * of need to be initialized. After initializing everything, "remember"
+ * (via a static) how long until the next timer expires.
+ */
+ if (alarmtime == 0) {
+ supplyinterval = GET_RANDOM(MIN_SUPPLY_TIME, MAX_SUPPLY_TIME);
+ iftime = 0;
+ mtime = supplyinterval;
+ alarmtime = supplyinterval;
+ (void) alarm(alarmtime);
+ return;
+ }
+
+ /*
+ * Initialize mintime to a suitable "large" value and then compare it to
+ * other times in the future to determine which event will occur next.
+ */
+ mintime = INT_MAX;
+ (void) sighold(SIGHUP);
+ (void) sighold(SIGUSR1);
+ (void) sighold(SIGUSR2);
+
+ iftime += alarmtime;
+ if (iftime >= CHECK_INTERVAL) {
+ initifs();
+ iftime = 0;
+ }
+ mintime = min(mintime, CHECK_INTERVAL - iftime);
+
+ mtime += alarmtime;
+ if (mtime >= supplyinterval) {
+ if (supplier)
+ timetomulticast = _B_TRUE;
+ mtime = 0;
+ supplyinterval = GET_RANDOM(MIN_SUPPLY_TIME, MAX_SUPPLY_TIME);
+ }
+ mintime = min(mintime, supplyinterval - mtime);
+
+ for (i = IPV6_ABITS; i >= 0; i--) {
+ if (net_hashes[i] == NULL)
+ continue;
+
+ for (rh = net_hashes[i];
+ rh < &net_hashes[i][ROUTEHASHSIZ]; rh++) {
+ for (rt = rh->rt_forw; rt != (struct rt_entry *)rh;
+ rt = rt->rt_forw) {
+ /*
+ * We don't advance time on a routing entry for
+ * an interface because we catch
+ * interfaces going up and down in initifs.
+ */
+ rt->rt_state &= ~RTS_CHANGED;
+ if ((rt->rt_state & RTS_INTERFACE) != 0)
+ continue;
+ rt->rt_timer += alarmtime;
+ if (rt->rt_timer >= GARBAGE_TIME) {
+ rt = rt->rt_back;
+ rtdelete(rt->rt_forw);
+ continue;
+ }
+ if (rt->rt_timer >= EXPIRE_TIME) {
+ rtdown(rt);
+ mintime = min(mintime,
+ GARBAGE_TIME - rt->rt_timer);
+ } else {
+ mintime = min(mintime,
+ EXPIRE_TIME - rt->rt_timer);
+ }
+ }
+ }
+ }
+
+ if (timetomulticast) {
+ supplyall(&allrouters, 0, (struct interface *)NULL, _B_TRUE);
+ (void) gettimeofday(&now, (struct timezone *)NULL);
+ lastmcast = now;
+ lastfullupdate = now;
+ needupdate = _B_FALSE; /* cancel any pending dynamic update */
+ nextmcast.tv_sec = 0;
+ }
+ (void) sigrelse(SIGUSR2);
+ (void) sigrelse(SIGUSR1);
+ (void) sigrelse(SIGHUP);
+
+ /*
+ * "Remember" (via a static) how long until the next timer expires.
+ */
+ alarmtime = mintime;
+ (void) alarm(alarmtime);
+}
+
+/*
+ * On SIGTERM, let everyone know we're going away.
+ */
+void
+term(void)
+{
+ struct rthash *rh;
+ struct rt_entry *rt;
+ int i;
+
+ if (!supplier)
+ exit(EXIT_SUCCESS);
+ for (i = IPV6_ABITS; i >= 0; i--) {
+ if (net_hashes[i] == NULL)
+ continue;
+
+ for (rh = net_hashes[i]; rh < &net_hashes[i][ROUTEHASHSIZ];
+ rh++) {
+ for (rt = rh->rt_forw; rt != (struct rt_entry *)rh;
+ rt = rt->rt_forw) {
+ rt->rt_metric = HOPCNT_INFINITY;
+ }
+ }
+ }
+ supplyall(&allrouters, 0, (struct interface *)NULL, _B_TRUE);
+ (void) unlink(PATH_PID);
+ exit(EXIT_SUCCESS);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.ripngd/trace.c b/usr/src/cmd/cmd-inet/usr.lib/in.ripngd/trace.c
new file mode 100644
index 0000000000..4aeb2ffefa
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.ripngd/trace.c
@@ -0,0 +1,264 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * Portions of this source code were derived from Berkeley 4.3 BSD
+ * under license from the Regents of the University of California.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.1 */
+
+/*
+ * Routing Table Management Daemon
+ */
+#include "defs.h"
+
+#define NRECORDS 50 /* size of circular trace buffer */
+
+boolean_t tracepackets; /* watch packets as they go by */
+int tracing; /* bitmask: */
+FILE *ftrace; /* output trace file */
+
+static int iftraceinit(struct interface *ifp, struct ifdebug *ifd);
+static void dumpif(FILE *fp, struct interface *ifp);
+static void dumptrace(FILE *fp, char *dir, struct ifdebug *ifd);
+
+void
+traceinit(struct interface *ifp)
+{
+ if (iftraceinit(ifp, &ifp->int_input) &&
+ iftraceinit(ifp, &ifp->int_output))
+ return;
+ tracing = 0;
+ (void) fprintf(stderr, "traceinit: can't init %s\n",
+ (ifp->int_name != NULL) ? ifp->int_name : "(noname)");
+}
+
+static int
+iftraceinit(struct interface *ifp, struct ifdebug *ifd)
+{
+ struct iftrace *t;
+
+ ifd->ifd_records = (struct iftrace *)
+ malloc((size_t)NRECORDS * sizeof (struct iftrace));
+ if (ifd->ifd_records == NULL)
+ return (0);
+ ifd->ifd_front = ifd->ifd_records;
+ ifd->ifd_count = 0;
+ for (t = ifd->ifd_records; t < ifd->ifd_records + NRECORDS; t++) {
+ t->ift_size = 0;
+ t->ift_packet = NULL;
+ }
+ ifd->ifd_if = ifp;
+ return (1);
+}
+
+void
+traceon(char *file)
+{
+ struct stat stbuf;
+
+ if (ftrace != NULL)
+ return;
+ if (stat(file, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) != S_IFREG)
+ return;
+ ftrace = fopen(file, "a");
+ if (ftrace == NULL)
+ return;
+ (void) dup2(fileno(ftrace), 1);
+ (void) dup2(fileno(ftrace), 2);
+}
+
+void
+traceonfp(FILE *fp)
+{
+ if (ftrace != NULL)
+ return;
+ ftrace = fp;
+ if (ftrace == NULL)
+ return;
+ (void) dup2(fileno(ftrace), 1);
+ (void) dup2(fileno(ftrace), 2);
+}
+
+void
+trace(struct ifdebug *ifd, struct sockaddr_in6 *who, char *p, int len, int m)
+{
+ struct iftrace *t;
+
+ if (ifd->ifd_records == 0)
+ return;
+ t = ifd->ifd_front++;
+ if (ifd->ifd_front >= ifd->ifd_records + NRECORDS)
+ ifd->ifd_front = ifd->ifd_records;
+ if (ifd->ifd_count < NRECORDS)
+ ifd->ifd_count++;
+ if (t->ift_size > 0 && t->ift_size < len && t->ift_packet != NULL) {
+ free(t->ift_packet);
+ t->ift_packet = NULL;
+ }
+ (void) time(&t->ift_stamp);
+ t->ift_who = *who;
+ if (len > 0 && t->ift_packet == NULL) {
+ t->ift_packet = (char *)malloc((size_t)len);
+ if (t->ift_packet == NULL)
+ len = 0;
+ }
+ if (len > 0)
+ bcopy(p, t->ift_packet, len);
+ t->ift_size = len;
+ t->ift_metric = m;
+}
+
+void
+traceaction(FILE *fp, char *action, struct rt_entry *rt)
+{
+ static struct bits {
+ ulong_t t_bits;
+ char *t_name;
+ } flagbits[] = {
+ /* BEGIN CSTYLED */
+ { RTF_UP, "UP" },
+ { RTF_GATEWAY, "GATEWAY" },
+ { RTF_HOST, "HOST" },
+ { 0, NULL }
+ /* END CSTYLED */
+ }, statebits[] = {
+ /* BEGIN CSTYLED */
+ { RTS_INTERFACE, "INTERFACE" },
+ { RTS_CHANGED, "CHANGED" },
+ { RTS_PRIVATE, "PRIVATE" },
+ { 0, NULL }
+ /* END CSTYLED */
+ };
+ struct bits *p;
+ boolean_t first;
+ char c;
+ time_t t;
+
+ if (fp == NULL)
+ return;
+ (void) time(&t);
+ (void) fprintf(fp, "%.15s %s ", ctime(&t) + 4, action);
+ if (rt != NULL) {
+ char buf1[INET6_ADDRSTRLEN];
+
+ (void) fprintf(fp, "prefix %s/%d ",
+ inet_ntop(AF_INET6, (void *)&rt->rt_dst, buf1,
+ sizeof (buf1)),
+ rt->rt_prefix_length);
+ (void) fprintf(fp, "via %s metric %d",
+ inet_ntop(AF_INET6, (void *)&rt->rt_router, buf1,
+ sizeof (buf1)),
+ rt->rt_metric);
+ if (rt->rt_ifp != NULL) {
+ (void) fprintf(fp, " if %s",
+ (rt->rt_ifp->int_name != NULL) ?
+ rt->rt_ifp->int_name : "(noname)");
+ }
+ (void) fprintf(fp, " state");
+ c = ' ';
+ for (first = _B_TRUE, p = statebits; p->t_bits > 0; p++) {
+ if ((rt->rt_state & p->t_bits) == 0)
+ continue;
+ (void) fprintf(fp, "%c%s", c, p->t_name);
+ if (first) {
+ c = '|';
+ first = _B_FALSE;
+ }
+ }
+ if (first)
+ (void) fprintf(fp, " 0");
+ if (rt->rt_flags & (RTF_UP | RTF_GATEWAY)) {
+ c = ' ';
+ for (first = _B_TRUE, p = flagbits; p->t_bits > 0;
+ p++) {
+ if ((rt->rt_flags & p->t_bits) == 0)
+ continue;
+ (void) fprintf(fp, "%c%s", c, p->t_name);
+ if (first) {
+ c = '|';
+ first = _B_FALSE;
+ }
+ }
+ }
+ }
+ (void) putc('\n', fp);
+ if (!tracepackets && rt != NULL && rt->rt_ifp != NULL)
+ dumpif(fp, rt->rt_ifp);
+ (void) fflush(fp);
+}
+
+static void
+dumpif(FILE *fp, struct interface *ifp)
+{
+ if (ifp->int_input.ifd_count != 0 || ifp->int_output.ifd_count != 0) {
+ (void) fprintf(fp, "*** Packet history for interface %s ***\n",
+ (ifp->int_name != NULL) ? ifp->int_name : "(noname)");
+ dumptrace(fp, "to", &ifp->int_output);
+ dumptrace(fp, "from", &ifp->int_input);
+ (void) fprintf(fp, "*** end packet history ***\n");
+ }
+ (void) fflush(fp);
+}
+
+static void
+dumptrace(FILE *fp, char *dir, struct ifdebug *ifd)
+{
+ struct iftrace *t;
+ char *cp = (strcmp(dir, "to") != 0) ? "Output" : "Input";
+
+ if (ifd->ifd_front == ifd->ifd_records &&
+ ifd->ifd_front->ift_size == 0) {
+ (void) fprintf(fp, "%s: no packets.\n", cp);
+ (void) fflush(fp);
+ return;
+ }
+ (void) fprintf(fp, "%s trace:\n", cp);
+ t = ifd->ifd_front - ifd->ifd_count;
+ if (t < ifd->ifd_records)
+ t += NRECORDS;
+ for (; ifd->ifd_count; ifd->ifd_count--, t++) {
+ if (t >= ifd->ifd_records + NRECORDS)
+ t = ifd->ifd_records;
+ if (t->ift_size == 0)
+ continue;
+ (void) fprintf(fp, "%.24s: metric=%d\n", ctime(&t->ift_stamp),
+ t->ift_metric);
+ dumppacket(fp, dir, (struct sockaddr_in6 *)&t->ift_who,
+ t->ift_packet, t->ift_size);
+ }
+}
+
+/*ARGSUSED*/
+void
+dumppacket(FILE *fp, char *dir, struct sockaddr_in6 *who, char *cp, int size)
+{
+ /* XXX Output contents of the RIP packet */
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.ripngd/trace.h b/usr/src/cmd/cmd-inet/usr.lib/in.ripngd/trace.h
new file mode 100644
index 0000000000..1d504e0e5d
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.ripngd/trace.h
@@ -0,0 +1,108 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1999 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * Portions of this source code were derived from Berkeley 4.3 BSD
+ * under license from the Regents of the University of California.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Routing table management daemon.
+ */
+
+/*
+ * Trace record format.
+ */
+struct iftrace {
+ time_t ift_stamp; /* time stamp */
+ struct sockaddr_in6 ift_who; /* from/to */
+ char *ift_packet; /* pointer to packet */
+ int ift_size; /* size of packet */
+ int ift_metric; /* metric on associated metric */
+};
+
+/*
+ * Per interface packet tracing buffers. An incoming and
+ * outgoing circular buffer of packets is maintained, per
+ * interface, for debugging. Buffers are dumped whenever
+ * an interface is marked down.
+ */
+struct ifdebug {
+ struct iftrace *ifd_records; /* array of trace records */
+ struct iftrace *ifd_front; /* next empty trace record */
+ int ifd_count; /* number of unprinted records */
+ struct interface *ifd_if; /* for locating stuff */
+};
+
+/*
+ * Packet tracing stuff.
+ */
+extern FILE *ftrace;
+extern boolean_t tracepackets;
+extern int tracing;
+
+#define ACTION_BIT 0x0001
+#define INPUT_BIT 0x0002
+#define OUTPUT_BIT 0x0004
+
+#define TRACE_ACTION(action, route) { \
+ if (tracing & ACTION_BIT) \
+ traceaction(ftrace, (action), (route)); \
+}
+
+#define TRACE_INPUT(ifp, src, size) { \
+ if ((tracing & INPUT_BIT) && ((ifp) != NULL)) { \
+ trace(&(ifp)->int_input, (src), packet, (size), \
+ (ifp)->int_metric); \
+ } \
+ if (tracepackets) { \
+ dumppacket(stdout, "from", (struct sockaddr_in6 *)(src), \
+ packet, (size)); \
+ } \
+}
+#define TRACE_OUTPUT(ifp, dst, size) { \
+ if ((tracing & OUTPUT_BIT) && ((ifp) != NULL)) { \
+ trace(&(ifp)->int_output, (dst), packet, (size), \
+ (ifp)->int_metric); \
+ } \
+ if (tracepackets) { \
+ dumppacket(stdout, "to", (struct sockaddr_in6 *)(dst), \
+ packet, (size)); \
+ } \
+}
+
+extern void dumppacket(FILE *, char *, struct sockaddr_in6 *, char *, int);
+extern void trace(struct ifdebug *, struct sockaddr_in6 *, char *, int,
+ int);
+extern void traceaction(FILE *, char *, struct rt_entry *);
+extern void traceinit(struct interface *);
+extern void traceon(char *);
+extern void traceonfp(FILE *);
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.timed/Makefile b/usr/src/cmd/cmd-inet/usr.lib/in.timed/Makefile
new file mode 100644
index 0000000000..0c6784929e
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.timed/Makefile
@@ -0,0 +1,34 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# cmd/cmd-inet/usr.lib/in.timed/%M%
+#
+
+PROG = in.timed
+MANIFEST= time.xml
+
+include ../Makefile.inetsvc
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.timed/in.timed.c b/usr/src/cmd/cmd-inet/usr.lib/in.timed/in.timed.c
new file mode 100644
index 0000000000..151ec844f1
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.timed/in.timed.c
@@ -0,0 +1,96 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * time inetd service - both stream and dgram based.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <netinet/in.h>
+#include <sys/time.h>
+#include <syslog.h>
+#include <inetsvc.h>
+
+
+/*
+ * Return a machine readable date and time, in the form of the
+ * number of seconds since midnight, Jan 1, 1900. Since gettimeofday
+ * returns the number of seconds since midnight, Jan 1, 1970,
+ * we must add 2208988800 seconds to this figure to make up for
+ * some seventy years Bell Labs was asleep.
+ */
+static uint32_t
+machtime(void)
+{
+ struct timeval tv;
+
+ if (gettimeofday(&tv, NULL) < 0) {
+ syslog(LOG_INFO, "Unable to get time of day");
+ return (0);
+ }
+ return ((uint32_t)htonl(tv.tv_sec + 2208988800U));
+}
+
+static void
+machtime_stream(int s)
+{
+ uint32_t result = machtime();
+
+ (void) safe_write(s, &result, sizeof (result));
+}
+
+/* ARGSUSED3 */
+static void
+machtime_dg(int s, const struct sockaddr *sap, int sa_len, const void *buf,
+ size_t sz)
+{
+ uint32_t result = machtime();
+
+ (void) safe_sendto(s, &result, sizeof (result), 0, sap, sa_len);
+}
+
+int
+main(int argc, char *argv[])
+{
+ opterr = 0; /* disable getopt error msgs */
+ switch (getopt(argc, argv, "ds")) {
+ case 'd':
+ dg_template(machtime_dg, STDIN_FILENO, NULL, 0);
+ break;
+ case 's':
+ machtime_stream(STDIN_FILENO);
+ break;
+ default:
+ return (1);
+ }
+
+ return (0);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.timed/time.xml b/usr/src/cmd/cmd-inet/usr.lib/in.timed/time.xml
new file mode 100644
index 0000000000..e643e91e2d
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.timed/time.xml
@@ -0,0 +1,125 @@
+<?xml version='1.0'?>
+<!DOCTYPE service_bundle SYSTEM '/usr/share/lib/xml/dtd/service_bundle.dtd.1'>
+
+<!--
+ Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ ident "%Z%%M% %I% %E% SMI"
+
+ NOTE: This service manifest is not editable; its contents will
+ be overwritten by package or patch operations, including
+ operating system upgrade. Make customizations in a different
+ file.
+
+ Service manifests for time
+-->
+
+<service_bundle type='manifest' name='SUNWcnsr:time'>
+
+<service
+ name='network/time'
+ type='service'
+ version='1'>
+
+ <restarter>
+ <service_fmri value='svc:/network/inetd:default' />
+ </restarter>
+
+ <property_group name='inetd' type='framework'>
+ <stability value='Evolving' />
+ <propval name='name' type='astring' value='time' />
+ <propval name='isrpc' type='boolean' value='false' />
+ </property_group>
+
+ <instance name='dgram' enabled='false' >
+ <exec_method
+ type='method'
+ name='inetd_start'
+ exec='/usr/lib/inet/in.timed -d'
+ timeout_seconds='0'>
+ <method_context>
+ <method_credential user='root' group='root' />
+ </method_context>
+ </exec_method>
+
+ <exec_method
+ type='method'
+ name='inetd_offline'
+ exec=':kill_process'
+ timeout_seconds='0'>
+ </exec_method>
+
+ <exec_method
+ type='method'
+ name='inetd_disable'
+ exec=':kill'
+ timeout_seconds='0'>
+ </exec_method>
+
+ <property_group name='inetd' type='framework'>
+ <propval name='endpoint_type' type='astring' value='dgram' />
+ <propval name='proto' type='astring' value='udp6' />
+ <propval name='wait' type='boolean' value='true' />
+ </property_group>
+ </instance>
+
+ <instance name='stream' enabled='false' >
+ <exec_method
+ type='method'
+ name='inetd_start'
+ exec='/usr/lib/inet/in.timed -s'
+ timeout_seconds='0'>
+ <method_context>
+ <method_credential user='root' group='root' />
+ </method_context>
+ </exec_method>
+
+ <exec_method
+ type='method'
+ name='inetd_disable'
+ exec=':kill'
+ timeout_seconds='0'>
+ </exec_method>
+
+ <property_group name='inetd' type='framework'>
+ <propval name='endpoint_type' type='astring' value='stream' />
+ <propval name='proto' type='astring' value='tcp6' />
+ <propval name='wait' type='boolean' value='false' />
+ </property_group>
+ </instance>
+
+ <stability value='Unstable' />
+
+ <template>
+ <common_name>
+ <loctext xml:lang='C'>
+ time
+ </loctext>
+ </common_name>
+ <documentation>
+ </documentation>
+ </template>
+</service>
+
+</service_bundle>
diff --git a/usr/src/cmd/cmd-inet/usr.lib/inetd/Makefile b/usr/src/cmd/cmd-inet/usr.lib/inetd/Makefile
new file mode 100644
index 0000000000..90d268cf6b
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/inetd/Makefile
@@ -0,0 +1,89 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# cmd/cmd-inet/usr.lib/inetd/%M%
+#
+
+PROG = inetd
+MANIFEST= inetd.xml inetd-upgrade.xml
+SVCMETHOD= inetd-upgrade
+
+OBJS = inetd.o tlx.o config.o util.o contracts.o repval.o wait.o env.o
+SRCS = $(OBJS:%.o=%.c)
+
+include ../../../Makefile.cmd
+include ../../Makefile.cmd-inet
+include ../../../svc/Makefile.ctf
+
+ROOTMANIFESTDIR= $(ROOTSVCNETWORK)
+$(ROOTMANIFEST) := FILEMODE= 444
+
+CPPFLAGS += -D_FILE_OFFSET_BITS=64 -I$(CMDINETCOMMONDIR) -I$(ROOTSFWINCLUDE) \
+ -D_REENTRANT
+$(RELEASE_BUILD)CPPFLAGS += -DNDEBUG
+
+LDFLAGS += -R$(SFW_ROOT)/lib
+LDLIBS += -lsocket -lnsl -lrestart -lscf -lcontract -linetutil \
+ -L$(ROOTSFWLIB) -lwrap -linetsvc -luutil -lumem -lbsm
+
+CLOBBERFILES += $(SVCMETHOD)
+
+# lint doesn't like the unused _umem_*_init()
+lint_SRCS := LINTFLAGS += -xerroff=E_NAME_DEF_NOT_USED2
+
+.PARALLEL: $(OBJS)
+.WAIT: $(PROG)
+.KEEP_STATE:
+
+all: $(PROG) $(SVCMETHOD)
+
+$(PROG): $(OBJS)
+ $(LINK.c) $(OBJS) -o $@ $(LDLIBS) $(CTFMERGE_HOOK)
+ $(POST_PROCESS)
+
+include ../Makefile.lib
+
+install: all $(ROOTLIBINETPROG) $(ROOTMANIFEST) $(ROOTSVCMETHOD)
+ -$(RM) $(ROOTUSRSBINPROG)
+ -$(SYMLINK) ../lib/inet/${PROG} $(ROOTUSRSBINPROG)
+
+$(ROOTMANIFEST): $(ROOTMANIFESTDIR)
+
+$(ROOTMANIFESTDIR):
+ $(INS.dir)
+
+$(ROOTMANIFESTDIR)/%: %
+ $(INS.file)
+
+check: $(CHKMANIFEST)
+
+clean:
+ $(RM) $(OBJS)
+
+lint: lint_SRCS
+
+include ../../../Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/usr.lib/inetd/config.c b/usr/src/cmd/cmd-inet/usr.lib/inetd/config.c
new file mode 100644
index 0000000000..68fb319c30
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/inetd/config.c
@@ -0,0 +1,810 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Routines used by inetd to read inetd's configuration from the repository,
+ * to validate it and setup inetd's data structures appropriately based on
+ * in.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <libintl.h>
+#include <nss_dbdefs.h>
+#include <signal.h>
+#include <wait.h>
+#include "inetd_impl.h"
+
+
+/* method timeout used if one isn't explicitly specified */
+#define DEFAULT_METHOD_TIMEOUT 10
+
+
+/* supported method properties and their attributes */
+static inetd_prop_t method_props[] = {
+{PR_EXEC_NAME, "", SCF_TYPE_ASTRING, B_FALSE, IVE_UNSET, NULL},
+{PR_ARG0_NAME, "", SCF_TYPE_ASTRING, B_TRUE, IVE_UNSET, NULL},
+{NULL, "", SCF_TYPE_COUNT, B_TRUE, IVE_UNSET, NULL}
+};
+
+/* enumeration of method properties; used to index into method_props[] */
+typedef enum {
+ MP_EXEC,
+ MP_ARG0,
+ MP_TIMEOUT,
+ NUM_METHOD_PROPS
+} method_prop_t;
+
+
+/* handle used for repository access in read_prop() */
+static scf_handle_t *rep_handle = NULL;
+
+/* pool used to create proto_info_t lists (generic proto info structure) */
+static uu_list_pool_t *proto_info_pool = NULL;
+
+static void destroy_method_props(inetd_prop_t *);
+static int proto_info_compare(const void *, const void *, void *);
+
+int
+config_init(void)
+{
+ if ((rep_handle = scf_handle_create(SCF_VERSION)) == NULL) {
+ error_msg("%s: %s",
+ gettext("Failed to create repository handle"),
+ scf_strerror(scf_error()));
+ return (-1);
+ } else if (make_handle_bound(rep_handle) == -1) {
+ /* let config_fini clean-up */
+ return (-1);
+ }
+
+ /*
+ * Work around the (const *) nature of SCF property #defines in
+ * libscf.h that prevent us from directly initializing the name
+ * element of members of the method properties table.
+ */
+ if ((method_props[MP_TIMEOUT].ip_name = strdup(SCF_PROPERTY_TIMEOUT))
+ == NULL) {
+ error_msg(strerror(errno));
+ return (-1);
+ }
+
+ if ((proto_info_pool = uu_list_pool_create("proto_info_pool",
+ sizeof (proto_info_t), offsetof(proto_info_t, link),
+ proto_info_compare, UU_LIST_POOL_DEBUG)) == NULL) {
+ error_msg(gettext("Failed to create uu list pool: %s"),
+ uu_strerror(uu_error()));
+ return (-1);
+ }
+
+ return (0);
+}
+
+void
+config_fini(void)
+{
+ if (rep_handle == NULL)
+ return;
+
+ if (proto_info_pool != NULL) {
+ uu_list_pool_destroy(proto_info_pool);
+ proto_info_pool = NULL;
+ }
+
+ (void) scf_handle_unbind(rep_handle);
+ scf_handle_destroy(rep_handle);
+ rep_handle = NULL;
+}
+
+static void
+destroy_method_info(method_info_t *mi)
+{
+ if (mi == NULL)
+ return;
+
+ if (mi->wordexp_arg0_backup != NULL) {
+ /*
+ * Return the wordexp structure back to its original
+ * state so it can be consumed by wordfree.
+ */
+ free(mi->exec_args_we.we_wordv[0]);
+ mi->exec_args_we.we_wordv[0] =
+ (char *)mi->wordexp_arg0_backup;
+ }
+
+ free(mi->exec_path);
+
+ wordfree(&mi->exec_args_we);
+
+ free(mi);
+}
+
+/*
+ * Transforms the properties read from the repository for a method into a
+ * method_info_t and returns a pointer to it. If expansion of the exec
+ * property fails, due to an invalid string or memory allocation failure,
+ * NULL is returned and exec_invalid is set appropriately to indicate whether
+ * it was a memory allocation failure or an invalid exec string.
+ */
+static method_info_t *
+create_method_info(const inetd_prop_t *mprops, boolean_t *exec_invalid)
+{
+ method_info_t *ret;
+ int i;
+
+ debug_msg("Entering create_method_info");
+
+ if ((ret = calloc(1, sizeof (method_info_t))) == NULL)
+ goto alloc_fail;
+
+ /* Expand the exec string. */
+ if ((i = wordexp(get_prop_value(mprops, PR_EXEC_NAME),
+ &ret->exec_args_we, WRDE_NOCMD|WRDE_UNDEF)) != 0) {
+ if (i == WRDE_NOSPACE)
+ goto alloc_fail;
+
+ *exec_invalid = B_TRUE;
+ free(ret);
+ return (NULL);
+ }
+
+ if ((ret->exec_path = strdup(ret->exec_args_we.we_wordv[0])) == NULL)
+ goto alloc_fail;
+
+ if (mprops[MP_ARG0].ip_error == IVE_VALID) { /* arg0 is set */
+ /*
+ * Keep a copy of arg0 of the wordexp structure so that
+ * wordfree() gets passed what wordexp() originally returned,
+ * as documented as required in the man page.
+ */
+ ret->wordexp_arg0_backup = ret->exec_args_we.we_wordv[0];
+ if ((ret->exec_args_we.we_wordv[0] =
+ strdup(get_prop_value(mprops, PR_ARG0_NAME))) == NULL)
+ goto alloc_fail;
+ }
+
+ if (mprops[MP_TIMEOUT].ip_error == IVE_VALID) {
+ ret->timeout = *(int64_t *)get_prop_value(mprops,
+ (char *)SCF_PROPERTY_TIMEOUT);
+ } else {
+ ret->timeout = DEFAULT_METHOD_TIMEOUT;
+ }
+
+ /* exec_invalid not set on success */
+
+ return (ret);
+
+alloc_fail:
+ error_msg(strerror(errno));
+ destroy_method_info(ret);
+ *exec_invalid = B_FALSE;
+ return (NULL);
+}
+
+/*
+ * Returns B_TRUE if the contents of the 2 method_info_t structures are
+ * equivalent, else B_FALSE.
+ */
+boolean_t
+method_info_equal(const method_info_t *mi, const method_info_t *mi2)
+{
+ int i;
+
+ debug_msg("Entering method_info_equal");
+
+ if ((mi == NULL) && (mi2 == NULL)) {
+ return (B_TRUE);
+ } else if (((mi == NULL) || (mi2 == NULL)) ||
+ (mi->exec_args_we.we_wordc != mi2->exec_args_we.we_wordc) ||
+ (strcmp(mi->exec_path, mi2->exec_path) != 0)) {
+ return (B_FALSE);
+ }
+
+ for (i = 0; i < mi->exec_args_we.we_wordc; i++) {
+ if (strcmp(mi->exec_args_we.we_wordv[i],
+ mi2->exec_args_we.we_wordv[i]) != 0) {
+ return (B_FALSE);
+ }
+ }
+
+ return (B_TRUE);
+}
+
+/*
+ * Checks if the contents of the 2 socket_info_t structures are equivalent.
+ * If 'isrpc' is false, the address components of the two structures are
+ * compared for equality as part of this. If the two structures are
+ * equivalent B_TRUE is returned, else B_FALSE.
+ */
+boolean_t
+socket_info_equal(const socket_info_t *si, const socket_info_t *si2,
+ boolean_t isrpc)
+{
+ return ((isrpc || (memcmp(&si->local_addr, &si2->local_addr,
+ sizeof (si->local_addr)) == 0)) &&
+ (si->type == si2->type));
+
+}
+
+/*
+ * proto_info_t comparison function. Returns 0 on match, else -1, as required
+ * by uu_list_find().
+ */
+static int
+proto_info_compare(const void *lv, const void *rv, void *istlx)
+{
+ proto_info_t *pi = (proto_info_t *)lv;
+ proto_info_t *pi2 = (proto_info_t *)rv;
+
+ /* check their RPC configuration matches */
+ if (pi->ri != NULL) {
+ if ((pi2->ri == NULL) || !rpc_info_equal(pi->ri, pi2->ri))
+ return (-1);
+ } else if (pi2->ri != NULL) {
+ return (-1);
+ }
+
+ if (pi->v6only != pi2->v6only)
+ return (-1);
+
+ if (*(boolean_t *)istlx) {
+ if (tlx_info_equal((tlx_info_t *)lv, (tlx_info_t *)rv,
+ pi->ri != NULL))
+ return (0);
+ } else {
+ if (socket_info_equal((socket_info_t *)lv,
+ (socket_info_t *)rv, pi->ri != NULL))
+ return (0);
+ }
+ return (-1);
+}
+
+/*
+ * Returns B_TRUE if the bind configuration of the two instance_cfg_t
+ * structures are equivalent, else B_FALSE.
+ */
+boolean_t
+bind_config_equal(const basic_cfg_t *c1, const basic_cfg_t *c2)
+{
+ proto_info_t *pi;
+
+ debug_msg("Entering bind_config_equal");
+
+ if ((c1->iswait != c2->iswait) ||
+ (c1->istlx != c2->istlx))
+ return (B_FALSE);
+
+ if (uu_list_numnodes(c1->proto_list) !=
+ uu_list_numnodes(c2->proto_list))
+ return (B_FALSE);
+ /*
+ * For each element in the first configuration's socket/tlx list,
+ * check there's a matching one in the other list.
+ */
+ for (pi = uu_list_first(c1->proto_list); pi != NULL;
+ pi = uu_list_next(c1->proto_list, pi)) {
+ uu_list_index_t idx;
+
+ if (uu_list_find(c2->proto_list, pi, (void *)&c1->istlx,
+ &idx) == NULL)
+ return (B_FALSE);
+ }
+
+ return (B_TRUE);
+}
+
+/*
+ * Write the default values contained in 'bprops', read by
+ * read_instance_props(), into 'cfg'.
+ * Returns -1 if memory allocation fails, else 0.
+ */
+static int
+populate_defaults(inetd_prop_t *bprops, basic_cfg_t *cfg)
+{
+ debug_msg("Entering populate_defaults");
+
+ /*
+ * All time related values below are stored as 32 bits values because
+ * the consumers of the data rely on this, and so we cast them all
+ * to int's here.
+ */
+ cfg->do_tcp_wrappers =
+ *(boolean_t *)get_prop_value(bprops, PR_DO_TCP_WRAPPERS_NAME);
+ cfg->do_tcp_trace =
+ *(boolean_t *)get_prop_value(bprops, PR_DO_TCP_TRACE_NAME);
+ cfg->inherit_env =
+ *(boolean_t *)get_prop_value(bprops, PR_INHERIT_ENV_NAME);
+ cfg->wait_fail_cnt =
+ *(int64_t *)get_prop_value(bprops, PR_MAX_FAIL_RATE_CNT_NAME);
+ cfg->wait_fail_interval = (int)*(int64_t *)get_prop_value(bprops,
+ PR_MAX_FAIL_RATE_INTVL_NAME);
+ cfg->max_copies =
+ *(int64_t *)get_prop_value(bprops, PR_MAX_COPIES_NAME);
+ cfg->conn_rate_offline =
+ (int)*(int64_t *)get_prop_value(bprops, PR_CON_RATE_OFFLINE_NAME);
+ cfg->conn_rate_max =
+ *(int64_t *)get_prop_value(bprops, PR_CON_RATE_MAX_NAME);
+ cfg->bind_fail_interval =
+ (int)*(int64_t *)get_prop_value(bprops, PR_BIND_FAIL_INTVL_NAME);
+ cfg->bind_fail_max =
+ *(int64_t *)get_prop_value(bprops, PR_BIND_FAIL_MAX_NAME);
+ if ((cfg->bind_addr =
+ strdup(get_prop_value(bprops, PR_BIND_ADDR_NAME))) == NULL) {
+ error_msg(strerror(errno));
+ return (-1);
+ }
+ return (0);
+}
+
+void
+destroy_method_infos(method_info_t **mis)
+{
+ int i;
+
+ for (i = 0; i < NUM_METHODS; i++) {
+ destroy_method_info(mis[i]);
+ mis[i] = NULL;
+ }
+}
+
+/*
+ * For each method, if it was specifed convert its entry in 'mprops',
+ * into an entry in 'mis'. Returns -1 if memory allocation fails or one of the
+ * exec strings was invalid, else 0.
+ */
+static int
+create_method_infos(const char *fmri, inetd_prop_t **mprops,
+ method_info_t **mis)
+{
+ int i;
+
+ debug_msg("Entering create_method_infos, inst: %s", fmri);
+
+ for (i = 0; i < NUM_METHODS; i++) {
+ /*
+ * Only create a method info structure if the method properties
+ * contain an exec string, which we take to mean the method
+ * is specified.
+ */
+ if (mprops[i][MP_EXEC].ip_error == IVE_VALID) {
+ boolean_t exec_invalid;
+
+ if ((mis[i] = create_method_info(mprops[i],
+ &exec_invalid)) == NULL) {
+ if (exec_invalid) {
+ error_msg(gettext("Property %s for "
+ "method %s of instance %s is "
+ "invalid"), PR_EXEC_NAME,
+ methods[i].name, fmri);
+ }
+ return (-1);
+ }
+ }
+ }
+ return (0);
+}
+
+/*
+ * Try and read each of the method properties for the method 'method' of
+ * instance 'inst', and return a table containing all method properties. If an
+ * error occurs, NULL is returned, with 'err' set to indicate the cause.
+ * Otherwise, a pointer to an inetd_prop_t table is returned containing all
+ * the method properties, and each of the properties is flagged according to
+ * whether it was present or not, and if it was present its value is set in
+ * the property's entry in the table.
+ */
+static inetd_prop_t *
+read_method_props(const char *inst, instance_method_t method, scf_error_t *err)
+{
+ inetd_prop_t *ret;
+ int i;
+
+ debug_msg("Entering read_method_props");
+
+ if ((ret = calloc(1, sizeof (method_props))) == NULL) {
+ *err = SCF_ERROR_NO_MEMORY;
+ return (NULL);
+ }
+
+ (void) memcpy(ret, method_props, sizeof (method_props));
+ for (i = 0; i < NUM_METHOD_PROPS; i++) {
+ *err = read_prop(rep_handle, &ret[i], i, inst,
+ methods[method].name);
+ if ((*err != 0) && (*err != SCF_ERROR_NOT_FOUND)) {
+ destroy_method_props(ret);
+ return (NULL);
+ }
+ }
+
+ return (ret);
+}
+
+static void
+destroy_method_props(inetd_prop_t *mprop)
+{
+ int i;
+
+ if (mprop == NULL)
+ return;
+
+ for (i = 0; i < NUM_METHOD_PROPS; i++) {
+ if (mprop[i].ip_type == SCF_TYPE_ASTRING)
+ free(mprop[i].ip_value.iv_astring);
+ }
+
+ free(mprop);
+}
+
+/*
+ * Destroy the basic and method properties returned by read_inst_props().
+ */
+static void
+destroy_inst_props(inetd_prop_t *bprops, inetd_prop_t **mprops)
+{
+ int i;
+
+ free_instance_props(bprops);
+ for (i = 0; i < NUM_METHODS; i++)
+ destroy_method_props(mprops[i]);
+}
+
+/*
+ * Read all the basic and method properties for instance 'inst', as inetd_prop_t
+ * tables, into the spaces referenced by 'bprops' and 'mprops' respectively.
+ * Each of the properties in the tables are flagged to indicate if the
+ * property was present or not, and if it was the value is stored within it.
+ * If an error occurs at any time -1 is returned and 'err' is set to
+ * indicate the reason, else 0 is returned.
+ */
+static int
+read_inst_props(const char *fmri, inetd_prop_t **bprops,
+ inetd_prop_t **mprops, scf_error_t *err)
+{
+ size_t nprops;
+ int i;
+
+ debug_msg("Entering read_inst_props");
+
+ if ((*bprops = read_instance_props(rep_handle, (char *)fmri, &nprops,
+ err)) == NULL)
+ return (-1);
+
+ for (i = 0; i < NUM_METHODS; i++) {
+ if ((mprops[i] =
+ read_method_props(fmri, (instance_method_t)i, err)) ==
+ NULL) {
+ for (i--; i >= 0; i--)
+ destroy_method_props(mprops[i]);
+ free_instance_props(*bprops);
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * Returns B_TRUE if all required properties were read from the repository
+ * (whether taken from the defaults or directly from the instance), they
+ * all had valid values, all the required methods were present, and they
+ * each had the required properties with valid values. Else, returns B_FALSE.
+ * If the function returns B_TRUE, the storage referenced by 'cfg' is set
+ * to point at an allocated instance_cfg_t initialized based on the basic
+ * properties (not method or defaults).
+ */
+static boolean_t
+valid_inst_props(const char *fmri, inetd_prop_t *bprops, inetd_prop_t **mprops,
+ basic_cfg_t **cfg)
+{
+ boolean_t valid;
+ size_t num_bprops;
+ int i;
+
+ debug_msg("Entering valid_inst_props: inst: %s, bprops: %x, mprops: %x",
+ fmri, bprops, *mprops);
+
+ valid = valid_props(bprops, fmri, cfg, proto_info_pool, conn_ind_pool);
+
+ /*
+ * Double check we've got all necessary properties (valid_props()
+ * doesn't enforce the presence of defaults), and output error messages
+ * for each invalid/ missing property.
+ */
+ (void) get_prop_table(&num_bprops);
+ for (i = 0; i < num_bprops; i++) {
+ switch (bprops[i].ip_error) {
+ case IVE_UNSET:
+ if (!bprops[i].ip_default)
+ continue;
+ if ((i == PT_ARG0_INDEX) || (i == PT_EXEC_INDEX))
+ continue;
+ /* FALLTHROUGH */
+ case IVE_INVALID:
+ error_msg(gettext("Property '%s' of instance "
+ "%s is missing, inconsistent or invalid"),
+ bprops[i].ip_name, fmri);
+ valid = B_FALSE;
+ }
+ }
+
+ for (i = 0; i < NUM_METHODS; i++) {
+ int j;
+
+ /* check if any properties are set */
+ for (j = 0; j < NUM_METHOD_PROPS; j++) {
+ if (mprops[i][j].ip_error != IVE_UNSET)
+ break;
+ }
+
+ if (j == NUM_METHOD_PROPS) {
+ /* an unspecified method */
+ if ((instance_method_t)i == IM_START) {
+ error_msg(gettext(
+ "Unspecified %s method for instance %s"),
+ START_METHOD_NAME, fmri);
+ valid = B_FALSE;
+ }
+ } else if (mprops[i][MP_EXEC].ip_error == IVE_UNSET) {
+ error_msg(gettext("Missing %s property from method %s "
+ "of instance %s"), PR_EXEC_NAME,
+ methods[(instance_method_t)i].name, fmri);
+ valid = B_FALSE;
+ }
+ }
+
+ if (!valid)
+ destroy_basic_cfg(*cfg);
+
+ return (valid);
+}
+
+void
+destroy_instance_cfg(instance_cfg_t *cfg)
+{
+ if (cfg != NULL) {
+ destroy_basic_cfg(cfg->basic);
+ destroy_method_infos(cfg->methods);
+ free(cfg);
+ }
+}
+
+/*
+ * Returns an allocated instance_cfg_t representation of an instance's
+ * configuration read from the repository. If the configuration is invalid, a
+ * repository error occurred, or a memory allocation occurred returns NULL,
+ * else returns a pointer to the allocated instance_cfg_t.
+ */
+instance_cfg_t *
+read_instance_cfg(const char *fmri)
+{
+ uint_t retries;
+ inetd_prop_t *bprops;
+ inetd_prop_t *mprops[NUM_METHODS];
+ instance_cfg_t *ret = NULL;
+ scf_error_t err;
+
+ debug_msg("Entering read_instance_cfg");
+
+ if ((ret = calloc(1, sizeof (instance_cfg_t))) == NULL)
+ return (NULL);
+
+ for (retries = 0; retries <= REP_OP_RETRIES; retries++) {
+ if (make_handle_bound(rep_handle) == -1) {
+ err = scf_error();
+ goto read_error;
+ }
+
+ if (read_inst_props(fmri, &bprops, mprops, &err) == 0)
+ break;
+ if (err != SCF_ERROR_CONNECTION_BROKEN)
+ goto read_error;
+ (void) scf_handle_unbind(rep_handle);
+ }
+ if (retries > REP_OP_RETRIES)
+ goto read_error;
+
+ /*
+ * Switch off validation of the start method's exec string, since
+ * during boot the filesystem it resides on may not have been
+ * mounted yet, which would result in a false validation failure.
+ * We'll catch any real errors when the start method is first run
+ * in passes_basic_exec_checks().
+ */
+ bprops[PT_EXEC_INDEX].ip_error = IVE_UNSET;
+
+ if ((!valid_inst_props(fmri, bprops, mprops, &ret->basic)) ||
+ (populate_defaults(bprops, ret->basic) != 0) ||
+ (create_method_infos(fmri, mprops, ret->methods) != 0)) {
+ destroy_instance_cfg(ret);
+ ret = NULL;
+ }
+
+ destroy_inst_props(bprops, mprops);
+ return (ret);
+
+read_error:
+ error_msg(gettext(
+ "Failed to read the configuration of instance %s: %s"), fmri,
+ scf_strerror(err));
+ free(ret);
+ return (NULL);
+}
+
+/*
+ * Returns a pointer to an allocated method context for the specified method
+ * of the specified instance if it could retrieve it. Else, if there were
+ * errors retrieving it, NULL is returned and the pointer referenced by
+ * 'errstr' is set to point at an appropriate error string.
+ */
+struct method_context *
+read_method_context(const char *inst_fmri, const char *method, const char *path,
+ const char **errstr)
+{
+ scf_instance_t *scf_inst = NULL;
+ struct method_context *ret;
+ uint_t retries;
+ const char *tmpstr;
+
+ debug_msg("Entering read_method_context: inst: %s, method: %s, "
+ "path: %s", inst_fmri, method, path);
+
+ for (retries = 0; retries <= REP_OP_RETRIES; retries++) {
+ if (make_handle_bound(rep_handle) == -1)
+ goto inst_failure;
+
+ if (((scf_inst = scf_instance_create(rep_handle)) != NULL) &&
+ (scf_handle_decode_fmri(rep_handle, inst_fmri, NULL, NULL,
+ scf_inst, NULL, NULL, SCF_DECODE_FMRI_EXACT) == 0))
+ break;
+ if (scf_error() != SCF_ERROR_CONNECTION_BROKEN) {
+ scf_instance_destroy(scf_inst);
+ goto inst_failure;
+ }
+
+ (void) scf_instance_destroy(scf_inst);
+ scf_inst = NULL;
+
+ (void) scf_handle_unbind(rep_handle);
+ }
+ if (retries > REP_OP_RETRIES)
+ goto inst_failure;
+
+ if ((tmpstr = restarter_get_method_context(
+ RESTARTER_METHOD_CONTEXT_VERSION, scf_inst, NULL, method, path,
+ &ret)) != NULL) {
+ ret = NULL;
+ *errstr = tmpstr;
+ }
+
+ scf_instance_destroy(scf_inst);
+ return (ret);
+
+inst_failure:
+ /*
+ * We can rely on this string not becoming invalid
+ * since we don't call bind_textdomain_codeset() or
+ * setlocale(3C) after initialization.
+ */
+ *errstr = gettext("failed to get instance from repository");
+ return (NULL);
+}
+
+/*
+ * Reads the value of the enabled property from the named property group
+ * of the given instance.
+ * If an error occurs, the SCF error code is returned. The possible errors are:
+ * - SCF_ERROR_INVALID_ARGUMENT: The enabled property is not a boolean.
+ * - SCF_ERROR_NONE: No value exists for the enabled property.
+ * - SCF_ERROR_CONNECTION_BROKEN: Repository connection broken.
+ * - SCF_ERROR_NOT_FOUND: The property wasn't found.
+ * - SCF_ERROR_NO_MEMORY: allocation failure.
+ * Else 0 is returned and 'enabled' set appropriately.
+ */
+static scf_error_t
+read_enable_prop(const char *fmri, boolean_t *enabled, const char *pg)
+{
+ scf_simple_prop_t *sp;
+ uint8_t *u8p;
+
+ if ((sp = scf_simple_prop_get(rep_handle, fmri, pg,
+ SCF_PROPERTY_ENABLED)) == NULL)
+ return (scf_error());
+
+ if ((u8p = scf_simple_prop_next_boolean(sp)) == NULL) {
+ scf_simple_prop_free(sp);
+ return (scf_error());
+ }
+
+ *enabled = (*u8p != 0);
+ scf_simple_prop_free(sp);
+ return (0);
+}
+
+/*
+ * Reads the enabled value for the given instance FMRI. The read value
+ * is based on a merge of the 'standard' enabled property, and the temporary
+ * override one; the merge involves using the latter properties value if
+ * present, else resporting to the formers. If an error occurs -1 is returned,
+ * else 0 is returned and 'enabled' set approriately.
+ */
+int
+read_enable_merged(const char *fmri, boolean_t *enabled)
+{
+ uint_t retries;
+
+ debug_msg("Entering read_enabled_prop: inst: %s", fmri);
+
+ for (retries = 0; retries <= REP_OP_RETRIES; retries++) {
+ if (make_handle_bound(rep_handle) == -1)
+ goto gen_fail;
+
+ switch (read_enable_prop(fmri, enabled, SCF_PG_GENERAL_OVR)) {
+ case 0:
+ debug_msg("read %d from override", *enabled);
+ return (0);
+ case SCF_ERROR_CONNECTION_BROKEN:
+ break;
+ case SCF_ERROR_NOT_FOUND:
+ case SCF_ERROR_NONE:
+ case SCF_ERROR_INVALID_ARGUMENT:
+ switch (read_enable_prop(fmri, enabled,
+ SCF_PG_GENERAL)) {
+ case 0:
+ debug_msg("read %d from non_override",
+ *enabled);
+ return (0);
+ case SCF_ERROR_CONNECTION_BROKEN:
+ break;
+ case SCF_ERROR_NOT_FOUND:
+ case SCF_ERROR_NONE:
+ case SCF_ERROR_INVALID_ARGUMENT:
+ error_msg(gettext("Missing %s property/value "
+ "for instance %s"), SCF_PROPERTY_ENABLED,
+ fmri);
+ return (-1);
+ default:
+ goto gen_fail;
+ }
+ break;
+ default:
+ goto gen_fail;
+ }
+
+ (void) scf_handle_unbind(rep_handle);
+ continue;
+ }
+
+gen_fail:
+ error_msg(gettext("Failed to read the %s property of instance %s: %s"),
+ SCF_PROPERTY_ENABLED, fmri, scf_strerror(scf_error()));
+ return (-1);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/inetd/contracts.c b/usr/src/cmd/cmd-inet/usr.lib/inetd/contracts.c
new file mode 100644
index 0000000000..5b02d4ea89
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/inetd/contracts.c
@@ -0,0 +1,251 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <assert.h>
+#include <errno.h>
+#include <libintl.h>
+#include <sys/wait.h>
+#include <sys/ctfs.h>
+#include <sys/contract/process.h>
+#include <libcontract.h>
+#include <libcontract_priv.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "inetd_impl.h"
+
+
+/* paths/filenames of contract related files */
+#define CONTRACT_ROOT_PATH CTFS_ROOT "/process/"
+#define CONTRACT_TEMPLATE_PATH CONTRACT_ROOT_PATH "template"
+
+static int active_tmpl_fd = -1;
+
+/*
+ * Creates and configures the the contract template used for all inetd's
+ * methods.
+ * Returns -1 on error, else the fd of the created template.
+ */
+static int
+create_contract_template(void)
+{
+ int fd;
+ int err;
+
+ debug_msg("Entering create_contract_template");
+
+ if ((fd = open(CONTRACT_TEMPLATE_PATH, O_RDWR)) == -1) {
+ error_msg(gettext("Failed to open contract file %s: %s"),
+ CONTRACT_TEMPLATE_PATH, strerror(errno));
+ return (-1);
+ }
+
+ /*
+ * Make contract inheritable and make hardware errors fatal.
+ * We also limit the scope of fatal events to the process
+ * group. In order of preference we would have contract-aware
+ * login services or a property indicating which services need
+ * such scoping, but for the time being we'll assume that most
+ * non login-style services run in a single process group.
+ */
+ if (((err = ct_pr_tmpl_set_param(fd,
+ CT_PR_INHERIT|CT_PR_PGRPONLY)) != 0) ||
+ ((err = ct_pr_tmpl_set_fatal(fd, CT_PR_EV_HWERR)) != 0) ||
+ ((err = ct_tmpl_set_critical(fd, 0)) != 0) ||
+ ((err = ct_tmpl_set_informative(fd, 0)) != 0)) {
+ error_msg(gettext(
+ "Failed to set parameter for contract template: %s"),
+ strerror(err));
+ (void) close(fd);
+ return (-1);
+ }
+
+ return (fd);
+}
+
+/* Returns -1 on error, else 0. */
+int
+contract_init(void)
+{
+ debug_msg("Entering contract_init");
+
+ if ((active_tmpl_fd = create_contract_template()) == -1) {
+ error_msg(gettext("Failed to create contract template"));
+ return (-1);
+ }
+ return (0);
+}
+
+void
+contract_fini(void)
+{
+ debug_msg("Entering contract_fini");
+
+ if (active_tmpl_fd != -1) {
+ (void) close(active_tmpl_fd);
+ active_tmpl_fd = -1;
+ }
+}
+
+/*
+ * To be called directly before a service method is forked, this function
+ * results in the method process being in a new contract based on the active
+ * contract template.
+ */
+int
+contract_prefork(void)
+{
+ int err;
+
+ debug_msg("Entering contract_prefork");
+
+ if ((err = ct_tmpl_activate(active_tmpl_fd)) != 0) {
+ error_msg(gettext("Failed to activate contract template: %s"),
+ strerror(err));
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * To be called in both processes directly after a service method is forked,
+ * this function results in switching off contract creation for any
+ * forks done by either process, unless contract_prefork() is called beforehand.
+ */
+void
+contract_postfork(void)
+{
+ int err;
+
+ debug_msg("Entering contract_postfork");
+
+ if ((err = ct_tmpl_clear(active_tmpl_fd)) != 0)
+ error_msg("Failed to clear active contract template: %s",
+ strerror(err));
+}
+
+/*
+ * Fetch the latest created contract id into the space referenced by 'cid'.
+ * Returns -1 on error, else 0.
+ */
+int
+get_latest_contract(ctid_t *cid)
+{
+ debug_msg("Entering get_latest_contract");
+
+ if ((errno = contract_latest(cid)) != 0) {
+ error_msg(gettext("Failed to get new contract's id: %s"),
+ strerror(errno));
+ return (-1);
+ }
+
+ return (0);
+}
+
+/* Returns -1 on error (with errno set), else fd. */
+static int
+open_contract_ctl_file(ctid_t cid)
+{
+ debug_msg("Entering open_contract_ctl_file");
+
+ return (contract_open(cid, "process", "ctl", O_WRONLY));
+}
+
+/*
+ * Adopt a contract. Emits an error message and returns -1 on failure, else
+ * 0.
+ */
+int
+adopt_contract(ctid_t ctid, const char *fmri)
+{
+ int fd;
+ int err;
+ int ret = 0;
+
+ debug_msg("Entering adopt_contract, id: %d", ctid);
+
+ if ((fd = open_contract_ctl_file(ctid)) == -1) {
+ if (errno == EACCES || errno == ENOENT) {
+ /*
+ * We must not have inherited this contract. That can
+ * happen if we were disabled and restarted.
+ */
+ debug_msg("Could not adopt contract %ld for %s "
+ "(could not open ctl file: permission denied).\n",
+ ctid, fmri);
+ return (-1);
+ }
+
+ error_msg(gettext("Could not adopt contract id %ld registered "
+ "with %s (could not open ctl file: %s). Events will be "
+ "ignored."), ctid, fmri, strerror(errno));
+ return (-1);
+ }
+
+ if ((err = ct_ctl_adopt(fd)) != 0) {
+ error_msg(gettext("Could not adopt contract id %ld registered "
+ "with %s (%s). Events will be ignored."), ctid, fmri,
+ strerror(err));
+ ret = -1;
+ }
+
+ err = close(fd);
+ if (err != 0)
+ error_msg(gettext("Could not close file descriptor %d."), fd);
+
+ return (ret);
+}
+
+/* Returns -1 on error, else 0. */
+int
+abandon_contract(ctid_t ctid)
+{
+ int fd;
+ int err;
+
+ debug_msg("Entering abandon_contract, id: %d", ctid);
+
+ assert(ctid != -1);
+
+ if ((fd = open_contract_ctl_file(ctid)) == -1) {
+ error_msg(gettext("Failed to abandon contract %d: %s"), ctid,
+ strerror(errno));
+ return (-1);
+ }
+
+ if ((err = ct_ctl_abandon(fd)) != 0) {
+ (void) close(fd);
+ error_msg(gettext("Failed to abandon contract %d: %s"), ctid,
+ strerror(err));
+ return (-1);
+ }
+
+ (void) close(fd);
+
+ return (0);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/inetd/env.c b/usr/src/cmd/cmd-inet/usr.lib/inetd/env.c
new file mode 100644
index 0000000000..40ca7b39de
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/inetd/env.c
@@ -0,0 +1,183 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdlib.h>
+#include <strings.h>
+#include <libintl.h>
+
+#include "inetd_impl.h"
+
+extern char **environ;
+
+static int
+valid_env_var(const char *var, const char *instance, const char *method)
+{
+ char *cp = strchr(var, '=');
+
+ if (cp == NULL || cp == var) {
+ if (method == NULL)
+ return (0);
+ error_msg(gettext("Invalid environment variable \"%s\" for "
+ "method %s of instance %s.\n"), var, method, instance);
+ return (0);
+ } else if (strncmp(var, "SMF_", 4) == 0) {
+ if (method == NULL)
+ return (0);
+ error_msg(gettext("Invalid environment variable \"%s\" for "
+ "method %s of instance %s; \"SMF_\" prefix is reserved.\n"),
+ var, method, instance);
+ return (0);
+ }
+
+ return (1);
+}
+
+static char **
+find_dup(const char *var, char **env, const char *instance, const char *method)
+{
+ char **p;
+ char *tmp;
+
+ for (p = env; *p != NULL; p++) {
+ tmp = strchr(*p, '=');
+ assert(tmp != NULL);
+ tmp++;
+ if (strncmp(*p, var, tmp - *p) == 0)
+ break;
+ }
+
+ if (*p == NULL)
+ return (NULL);
+
+ error_msg(gettext("Ignoring duplicate environment variable \"%s\" "
+ "for method %s of instance %s.\n"), *p, method, instance);
+ return (p);
+}
+
+/*
+ * Create an environment which is appropriate for spawning an SMF aware
+ * process.
+ *
+ * In order to preserve the correctness of the new environment, various
+ * checks are performed:
+ *
+ * - All SMF_ entries are ignored. All SMF_ entries should be provided
+ * by this function.
+ * - Duplicates in the entry are eliminated.
+ * - Malformed entries are eliminated.
+ *
+ * Detected errors are logged but not fatal, since a single bad entry
+ * should not be enough to prevent an SMF_ functional environment from
+ * being created.
+ */
+char **
+set_smf_env(struct method_context *mthd_ctxt, instance_t *instance,
+ const char *method)
+{
+ char **nenv;
+ char **p, **np;
+ size_t nenv_size;
+
+ /*
+ * Max. of env, three SMF_ variables, and terminating NULL.
+ */
+ nenv_size = mthd_ctxt->env_sz + 3 + 1;
+
+ if (instance->config->basic->inherit_env) {
+ for (p = environ; *p != NULL; p++)
+ nenv_size++;
+ }
+
+ nenv = malloc(sizeof (char *) * nenv_size);
+ if (nenv == NULL)
+ return (NULL);
+ (void) memset(nenv, 0, sizeof (char *) * nenv_size);
+
+ np = nenv;
+
+ *np = uu_msprintf("SMF_RESTARTER=%s", INETD_INSTANCE_FMRI);
+ if (*np == NULL)
+ goto fail;
+ else
+ np++;
+ *np = uu_msprintf("SMF_FMRI=%s", instance->fmri);
+ if (*np == NULL)
+ goto fail;
+ else
+ np++;
+ *np = uu_msprintf("SMF_METHOD=%s", method);
+ if (*np == NULL)
+ goto fail;
+ else
+ np++;
+
+ if (instance->config->basic->inherit_env) {
+ for (p = environ; *p != NULL; p++) {
+ if (!valid_env_var(*p, NULL, NULL))
+ continue;
+
+ *np = strdup(*p);
+ if (*np == NULL)
+ goto fail;
+ else
+ np++;
+ }
+ }
+
+ if (mthd_ctxt->env != NULL) {
+ for (p = mthd_ctxt->env; *p != NULL; p++) {
+ char **dup_pos;
+
+ if (!valid_env_var(*p, instance->fmri, method))
+ continue;
+
+ if ((dup_pos = find_dup(*p, nenv, instance->fmri,
+ method)) != NULL) {
+ free(*dup_pos);
+ *dup_pos = strdup(*p);
+ if (*dup_pos == NULL)
+ goto fail;
+ } else {
+ *np = strdup(*p);
+ if (*np == NULL)
+ goto fail;
+ else
+ np++;
+ }
+ }
+ }
+ *np = NULL;
+
+ return (nenv);
+fail:
+ p = nenv;
+ while (nenv_size--)
+ free(*p++);
+ free(nenv);
+ return (NULL);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/inetd/inetd-upgrade.sh b/usr/src/cmd/cmd-inet/usr.lib/inetd/inetd-upgrade.sh
new file mode 100644
index 0000000000..a170c60c80
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/inetd/inetd-upgrade.sh
@@ -0,0 +1,198 @@
+#! /usr/bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+# Start by cleaning out obsolete instances. For each one that
+# exists in the repository, remove it.
+inetd_obsolete_instances="
+ network/nfs/rquota:ticlts
+ network/nfs/rquota:udp
+ network/rexec:tcp
+ network/rexec:tcp6
+ network/rpc/gss:ticotsord
+ network/rpc/mdcomm:tcp
+ network/rpc/mdcomm:tcp6
+ network/rpc/meta:tcp
+ network/rpc/meta:tcp6
+ network/rpc/metamed:tcp
+ network/rpc/metamed:tcp6
+ network/rpc/metamh:tcp
+ network/rpc/metamh:tcp6
+ network/rpc/rex:tcp
+ network/rpc/rstat:ticlts
+ network/rpc/rstat:udp
+ network/rpc/rstat:udp6
+ network/rpc/rusers:udp
+ network/rpc/rusers:udp6
+ network/rpc/rusers:ticlts
+ network/rpc/rusers:tcp
+ network/rpc/rusers:tcp6
+ network/rpc/rusers:ticotsord
+ network/rpc/rusers:ticots
+ network/rpc/spray:ticlts
+ network/rpc/spray:udp
+ network/rpc/spray:udp6
+ network/rpc/wall:ticlts
+ network/rpc/wall:udp
+ network/rpc/wall:udp6
+ network/security/krb5_prop:tcp
+ network/security/ktkt_warn:ticotsord
+ network/shell:tcp
+ network/shell:tcp6only
+ platform/sun4u/dcs:tcp
+ platform/sun4u/dcs:tcp6
+"
+
+for i in $inetd_obsolete_instances; do
+ enable=`svcprop -p general/enabled $i`
+ if [ $? = 0 ]; then
+ # Instance found, so disable and delete
+ svcadm disable $i
+ svccfg delete $i
+ if [ "$enable" = "true" ]; then
+ # Instance was enabled, so enable the replacement.
+ # We must do this here because the profile which
+ # normally enables these is only applied on first
+ # install of smf.
+ s=`echo $i | cut -f1 -d:`
+ svcadm enable $s:default
+ fi
+ fi
+done
+
+
+# The Following blocks of code cause the inetconv generated services to be
+# re-generated, so that the latest inetconv modifications are applied to all
+# services generated by it.
+
+inetdconf_entries_file=/tmp/iconf_entries.$$
+
+# Create sed script that prints out inetd.conf src line from inetconv generated
+# manifest.
+cat <<EOF > /tmp/inetd-upgrade.$$.sed
+/propval name='source_line'/{
+n
+s/'//g
+p
+}
+/from the inetd.conf(4) format line/{
+n
+p
+}
+EOF
+
+# get list of inetconv generated manifests
+inetconv_manifests=`/usr/bin/find /var/svc/manifest -type f -name \*.xml | \
+ /usr/bin/xargs /usr/bin/grep -l "Generated by inetconv"`
+
+# For each inetconv generated manifest determine the instances that should
+# be disabled when the new manifests are imported, and generate a file with
+# the inetd.conf entries from all the manifests for consumption by inetconv.
+
+> $inetdconf_entries_file
+inetconv_services=""
+instances_to_disable=""
+
+for manifest in $inetconv_manifests; do
+
+ manifest_instances=`/usr/sbin/svccfg inventory $manifest | \
+ egrep "svc:/.*:.*"`
+ manifest_service=`/usr/sbin/svccfg inventory $manifest | \
+ egrep -v "svc:/.*:.*"`
+
+ instance_disabled=""
+ default_enabled=""
+ enabled=""
+
+ for instance in $manifest_instances; do
+ # if the instance doesn't exist in the repository skip it
+ svcprop -q $instance
+ if [ $? -ne 0 ]; then
+ continue
+ fi
+
+ enabled=`svcprop -p general/enabled $instance`
+
+ default_instance=`echo $instance | grep ":default"`
+ if [ "$default_instance" != "" ]; then
+ default_enabled=$enabled
+ else
+ # add all non-default instances to disable list
+ instances_to_disable="$instances_to_disable \
+ $instance"
+ if [ "$enabled" != "true" ]; then
+ instance_disabled="true"
+ fi
+ fi
+ done
+
+ # if none of the manifest's instances existed, skip this manifest
+ if [ "$enabled" = "" ]; then
+ continue
+ fi
+
+ # If the default instance existed and was disabled, or if didn't
+ # exist and one of the other instances was disabled, add the default
+ # to the list of instances to disable.
+ if [ "$default_enabled" = "false" -o "$default_enabled" = "" -a \
+ "$instance_disabled" = "true" ]; then
+ instances_to_disable="$instances_to_disable \
+ $manifest_service:default"
+ fi
+
+ # add the manifest's inetd.conf src line to file for inetconv
+ sed -n -f /tmp/inetd-upgrade.$$.sed $manifest >> \
+ $inetdconf_entries_file
+done
+
+rm /tmp/inetd-upgrade.$$.sed
+
+# Run inetconv on generated file, overwriting previous manifests and values
+# in repository.
+/usr/sbin/inetconv -f -i $inetdconf_entries_file
+
+# disable the necessary instances
+for inst in $instances_to_disable; do
+ svcadm disable $inst
+done
+
+
+# If there is a saved config file from upgrade, use it to enable services
+saved_config=/etc/inet/inetd.conf.preupgrade
+
+if [ -f ${saved_config} ]; then
+ /usr/sbin/inetconv -e -i ${saved_config}
+fi
+
+# Now convert the remaining entries in inetd.conf to service manifests
+/usr/sbin/inetconv
+
+# Now disable myself as the upgrade is done
+svcadm disable network/inetd-upgrade
+
+exit 0
diff --git a/usr/src/cmd/cmd-inet/usr.lib/inetd/inetd-upgrade.xml b/usr/src/cmd/cmd-inet/usr.lib/inetd/inetd-upgrade.xml
new file mode 100644
index 0000000000..dd08fccd35
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/inetd/inetd-upgrade.xml
@@ -0,0 +1,103 @@
+<?xml version="1.0"?>
+<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
+<!--
+ Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ ident "%Z%%M% %I% %E% SMI"
+
+ NOTE: This service manifest is not editable; its contents will
+ be overwritten by package or patch operations, including
+ operating system upgrade. Make customizations in a different
+ file.
+
+ Service manifest for the inetd upgrade service.
+-->
+
+<service_bundle type='manifest' name='SUNWcsr:inetd-upgrade'>
+
+<service
+ name='network/inetd-upgrade'
+ type='service'
+ version='1'>
+
+ <create_default_instance enabled='true' />
+
+ <single_instance />
+
+ <dependency
+ name='network'
+ grouping='require_all'
+ restart_on='error'
+ type='service'
+ delete='true'>
+ <service_fmri value='svc:/network/inetd'/>
+ </dependency>
+
+ <dependency
+ name='filesystem'
+ grouping='require_all'
+ restart_on='none'
+ type='service'>
+ <service_fmri value='svc:/system/filesystem/local'/>
+ </dependency>
+
+ <exec_method
+ type='method'
+ name='stop'
+ exec=':true'
+ timeout_seconds='60' >
+ </exec_method>
+
+ <exec_method
+ type='method'
+ name='start'
+ exec='/lib/svc/method/inetd-upgrade %m'
+ timeout_seconds='120' >
+ </exec_method>
+
+ <property_group
+ name='startd'
+ type='framework'>
+ <propval name='duration' type='astring' value='transient' />
+ </property_group>
+
+ <stability value='Unstable' />
+
+ <template>
+ <common_name>
+ <loctext xml:lang='C'>inetd-upgrade</loctext>
+ </common_name>
+ <description>
+ <loctext xml:lang='C'>
+inetd-upgrade handles the upgrade of old inetd.conf entries to SMF service instances.
+ </loctext>
+ </description>
+ <documentation>
+ <manpage title='inetd' section='1M'
+ manpath='/usr/share/man' />
+ </documentation>
+ </template>
+</service>
+
+</service_bundle>
diff --git a/usr/src/cmd/cmd-inet/usr.lib/inetd/inetd.c b/usr/src/cmd/cmd-inet/usr.lib/inetd/inetd.c
new file mode 100644
index 0000000000..915b68545b
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/inetd/inetd.c
@@ -0,0 +1,3678 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * NOTES: To be expanded.
+ *
+ * The SMF inetd.
+ *
+ * Below are some high level notes of the operation of the SMF inetd. The
+ * notes don't go into any real detail, and the viewer of this file is
+ * encouraged to look at the code and its associated comments to better
+ * understand inetd's operation. This saves the potential for the code
+ * and these notes diverging over time.
+ *
+ * Inetd's major work is done from the context of event_loop(). Within this
+ * loop, inetd polls for events arriving from a number of different file
+ * descriptors, representing the following event types, and initiates
+ * any necessary event processing:
+ * - incoming network connections/datagrams.
+ * - notification of terminated processes (discovered via contract events).
+ * - instance specific events originating from the SMF master restarter.
+ * - stop/refresh requests from the inetd method processes (coming in on a
+ * Unix Domain socket).
+ * There's also a timeout set for the poll, which is set to the nearest
+ * scheduled timer in a timer queue that inetd uses to perform delayed
+ * processing, such as bind retries.
+ * The SIGHUP and SIGINT signals can also interrupt the poll, and will
+ * result in inetd being refreshed or stopped respectively, as was the
+ * behavior with the old inetd.
+ *
+ * Inetd implements a state machine for each instance. The states within the
+ * machine are: offline, online, disabled, maintenance, uninitialized and
+ * specializations of the offline state for when an instance exceeds one of
+ * its DOS limits. The state of an instance can be changed as a
+ * result/side-effect of one of the above events occurring, or inetd being
+ * started up. The ongoing state of an instance is stored in the SMF
+ * repository, as required of SMF restarters. This enables an administrator
+ * to view the state of each instance, and, if inetd was to terminate
+ * unexpectedly, it could use the stored state to re-commence where it left off.
+ *
+ * Within the state machine a number of methods are run (if provided) as part
+ * of a state transition to aid/ effect a change in an instance's state. The
+ * supported methods are: offline, online, disable, refresh and start. The
+ * latter of these is the equivalent of the server program and its arguments
+ * in the old inetd.
+ *
+ * Events from the SMF master restarter come in on a number of threads
+ * created in the registration routine of librestart, the delegated restarter
+ * library. These threads call into the restart_event_proxy() function
+ * when an event arrives. To serialize the processing of instances, these events
+ * are then written down a pipe to the process's main thread, which listens
+ * for these events via a poll call, with the file descriptor of the other
+ * end of the pipe in its read set, and processes the event appropriately.
+ * When the event has been processed (which may be delayed if the instance
+ * for which the event is for is in the process of executing one of its methods
+ * as part of a state transition) it writes an acknowledgement back down the
+ * pipe the event was received on. The thread in restart_event_proxy() that
+ * wrote the event will read the acknowledgement it was blocked upon, and will
+ * then be able to return to its caller, thus implicitly acknowledging the
+ * event, and allowing another event to be written down the pipe for the main
+ * thread to process.
+ */
+
+
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <errno.h>
+#include <locale.h>
+#include <syslog.h>
+#include <libintl.h>
+#include <librestart.h>
+#include <pthread.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <limits.h>
+#include <libgen.h>
+#include <tcpd.h>
+#include <libscf.h>
+#include <libuutil.h>
+#include <stddef.h>
+#include <bsm/adt_event.h>
+#include "inetd_impl.h"
+
+/* path to inetd's binary */
+#define INETD_PATH "/usr/lib/inet/inetd"
+
+/*
+ * inetd's default configuration file paths. /etc/inetd/inetd.conf is set
+ * be be the primary file, so it is checked before /etc/inetd.conf.
+ */
+#define PRIMARY_DEFAULT_CONF_FILE "/etc/inet/inetd.conf"
+#define SECONDARY_DEFAULT_CONF_FILE "/etc/inetd.conf"
+
+/* Arguments passed to this binary to request which method to execute. */
+#define START_METHOD_ARG "start"
+#define STOP_METHOD_ARG "stop"
+#define REFRESH_METHOD_ARG "refresh"
+
+/* connection backlog for unix domain socket */
+#define UDS_BACKLOG 2
+
+/* number of retries to recv() a request on the UDS socket before giving up */
+#define UDS_RECV_RETRIES 10
+
+/* enumeration of the different ends of a pipe */
+enum pipe_end {
+ PE_CONSUMER,
+ PE_PRODUCER
+};
+
+typedef struct {
+ internal_inst_state_t istate;
+ const char *name;
+ restarter_instance_state_t smf_state;
+ instance_method_t method_running;
+} state_info_t;
+
+
+/*
+ * Collection of information for each state.
+ * NOTE: This table is indexed into using the internal_inst_state_t
+ * enumeration, so the ordering needs to be kept in synch.
+ */
+static state_info_t states[] = {
+ {IIS_UNINITIALIZED, "uninitialized", RESTARTER_STATE_UNINIT,
+ IM_NONE},
+ {IIS_ONLINE, "online", RESTARTER_STATE_ONLINE, IM_START},
+ {IIS_IN_ONLINE_METHOD, "online_method", RESTARTER_STATE_OFFLINE,
+ IM_ONLINE},
+ {IIS_OFFLINE, "offline", RESTARTER_STATE_OFFLINE, IM_NONE},
+ {IIS_IN_OFFLINE_METHOD, "offline_method", RESTARTER_STATE_OFFLINE,
+ IM_OFFLINE},
+ {IIS_DISABLED, "disabled", RESTARTER_STATE_DISABLED, IM_NONE},
+ {IIS_IN_DISABLE_METHOD, "disabled_method", RESTARTER_STATE_OFFLINE,
+ IM_DISABLE},
+ {IIS_IN_REFRESH_METHOD, "refresh_method", RESTARTER_STATE_ONLINE,
+ IM_REFRESH},
+ {IIS_MAINTENANCE, "maintenance", RESTARTER_STATE_MAINT, IM_NONE},
+ {IIS_OFFLINE_CONRATE, "cr_offline", RESTARTER_STATE_OFFLINE, IM_NONE},
+ {IIS_OFFLINE_BIND, "bind_offline", RESTARTER_STATE_OFFLINE, IM_NONE},
+ {IIS_OFFLINE_COPIES, "copies_offline", RESTARTER_STATE_OFFLINE,
+ IM_NONE},
+ {IIS_DEGRADED, "degraded", RESTARTER_STATE_DEGRADED, IM_NONE},
+ {IIS_NONE, "none", RESTARTER_STATE_NONE, IM_NONE}
+};
+
+/*
+ * Pipe used to send events from the threads created by restarter_bind_handle()
+ * to the main thread of control.
+ */
+static int rst_event_pipe[] = {-1, -1};
+/*
+ * Used to protect the critical section of code in restarter_event_proxy() that
+ * involves writing an event down the event pipe and reading an acknowledgement.
+ */
+static pthread_mutex_t rst_event_pipe_mtx = PTHREAD_MUTEX_INITIALIZER;
+
+/* handle used in communication with the master restarter */
+static restarter_event_handle_t *rst_event_handle = NULL;
+
+/* set to indicate a refresh of inetd is requested */
+static boolean_t refresh_inetd_requested = B_FALSE;
+
+/* set by the SIGTERM handler to flag we got a SIGTERM */
+static boolean_t got_sigterm = B_FALSE;
+
+/*
+ * Timer queue used to store timers for delayed event processing, such as
+ * bind retries.
+ */
+iu_tq_t *timer_queue = NULL;
+
+/*
+ * fd of Unix Domain socket used to communicate stop and refresh requests
+ * to the inetd start method process.
+ */
+static int uds_fd = -1;
+
+/*
+ * List of inetd's currently managed instances; each containing its state,
+ * and in certain states its configuration.
+ */
+static uu_list_pool_t *instance_pool = NULL;
+uu_list_t *instance_list = NULL;
+
+/* set to indicate we're being stopped */
+boolean_t inetd_stopping = B_FALSE;
+
+/* TCP wrappers syslog globals. Consumed by libwrap. */
+int allow_severity = LOG_INFO;
+int deny_severity = LOG_WARNING;
+
+/* path of the configuration file being monitored by check_conf_file() */
+static char *conf_file = NULL;
+
+/* Auditing session handle */
+static adt_session_data_t *audit_handle;
+
+static void uds_fini(void);
+static int uds_init(void);
+static int run_method(instance_t *, instance_method_t, const proto_info_t *);
+static void create_bound_fds(instance_t *);
+static void destroy_bound_fds(instance_t *);
+static void destroy_instance(instance_t *);
+static void inetd_stop(void);
+
+/*
+ * The following two functions are callbacks that libumem uses to determine
+ * inetd's desired debugging/logging levels. The interface they consume is
+ * exported by FMA and is consolidation private. The comments in the two
+ * functions give the environment variable that will effectively be set to
+ * their returned value, and thus whose behavior for this value, described in
+ * umem_debug(3MALLOC), will be followed.
+ */
+
+const char *
+_umem_debug_init(void)
+{
+ return ("default,verbose"); /* UMEM_DEBUG setting */
+}
+
+const char *
+_umem_logging_init(void)
+{
+ return ("fail,contents"); /* UMEM_LOGGING setting */
+}
+
+static void
+log_invalid_cfg(const char *fmri)
+{
+ error_msg(gettext(
+ "Invalid configuration for instance %s, placing in maintenance"),
+ fmri);
+}
+
+/*
+ * Returns B_TRUE if the instance is in a suitable state for inetd to stop.
+ */
+static boolean_t
+instance_stopped(const instance_t *inst)
+{
+ return ((inst->cur_istate == IIS_OFFLINE) ||
+ (inst->cur_istate == IIS_MAINTENANCE) ||
+ (inst->cur_istate == IIS_DISABLED) ||
+ (inst->cur_istate == IIS_UNINITIALIZED));
+}
+
+/*
+ * Updates the current and next repository states of instance 'inst'. If
+ * any errors occur an error message is output.
+ */
+static void
+update_instance_states(instance_t *inst, internal_inst_state_t new_cur_state,
+ internal_inst_state_t new_next_state, restarter_error_t err)
+{
+ internal_inst_state_t old_cur = inst->cur_istate;
+ internal_inst_state_t old_next = inst->next_istate;
+ scf_error_t sret;
+ int ret;
+
+ debug_msg("Entering update_instance_states: oldcur: %s, newcur: %s "
+ "oldnext: %s, newnext: %s", states[old_cur].name,
+ states[new_cur_state].name, states[old_next].name,
+ states[new_next_state].name);
+
+
+ /* update the repository/cached internal state */
+ inst->cur_istate = new_cur_state;
+ inst->next_istate = new_next_state;
+ (void) set_single_rep_val(inst->cur_istate_rep,
+ (int64_t)new_cur_state);
+ (void) set_single_rep_val(inst->next_istate_rep,
+ (int64_t)new_next_state);
+
+ if (((sret = store_rep_vals(inst->cur_istate_rep, inst->fmri,
+ PR_NAME_CUR_INT_STATE)) != 0) ||
+ ((sret = store_rep_vals(inst->next_istate_rep, inst->fmri,
+ PR_NAME_NEXT_INT_STATE)) != 0))
+ error_msg(gettext("Failed to update state of instance %s in "
+ "repository: %s"), inst->fmri, scf_strerror(sret));
+
+ /* update the repository SMF state */
+ if ((ret = restarter_set_states(rst_event_handle, inst->fmri,
+ states[old_cur].smf_state, states[new_cur_state].smf_state,
+ states[old_next].smf_state, states[new_next_state].smf_state,
+ err, 0)) != 0)
+ error_msg(gettext("Failed to update state of instance %s in "
+ "repository: %s"), inst->fmri, strerror(ret));
+
+}
+
+void
+update_state(instance_t *inst, internal_inst_state_t new_cur,
+ restarter_error_t err)
+{
+ update_instance_states(inst, new_cur, IIS_NONE, err);
+}
+
+/*
+ * Sends a refresh event to the inetd start method process and returns
+ * SMF_EXIT_OK if it managed to send it. If it fails to send the request for
+ * some reason it returns SMF_EXIT_ERR_OTHER.
+ */
+static int
+refresh_method(void)
+{
+ uds_request_t req = UR_REFRESH_INETD;
+ int fd;
+
+ debug_msg("Entering refresh_method");
+
+ if ((fd = connect_to_inetd()) < 0) {
+ error_msg(gettext("Failed to connect to inetd: %s"),
+ strerror(errno));
+ return (SMF_EXIT_ERR_OTHER);
+ }
+
+ /* write the request and return success */
+ if (safe_write(fd, &req, sizeof (req)) == -1) {
+ error_msg(
+ gettext("Failed to send refresh request to inetd: %s"),
+ strerror(errno));
+ (void) close(fd);
+ return (SMF_EXIT_ERR_OTHER);
+ }
+
+ (void) close(fd);
+
+ return (SMF_EXIT_OK);
+}
+
+/*
+ * Sends a stop event to the inetd start method process and wait till it goes
+ * away. If inetd is determined to have stopped SMF_EXIT_OK is returned, else
+ * SMF_EXIT_ERR_OTHER is returned.
+ */
+static int
+stop_method(void)
+{
+ uds_request_t req = UR_STOP_INETD;
+ int fd;
+ char c;
+ ssize_t ret;
+
+ debug_msg("Entering stop_method");
+
+ if ((fd = connect_to_inetd()) == -1) {
+ debug_msg(gettext("Failed to connect to inetd: %s"),
+ strerror(errno));
+ /*
+ * Assume connect_to_inetd() failed because inetd was already
+ * stopped, and return success.
+ */
+ return (SMF_EXIT_OK);
+ }
+
+ /*
+ * This is safe to do since we're fired off in a separate process
+ * than inetd and in the case we get wedged, the stop method timeout
+ * will occur and we'd be killed by our restarter.
+ */
+ enable_blocking(fd);
+
+ /* write the stop request to inetd and wait till it goes away */
+ if (safe_write(fd, &req, sizeof (req)) != 0) {
+ error_msg(gettext("Failed to send stop request to inetd"));
+ (void) close(fd);
+ return (SMF_EXIT_ERR_OTHER);
+ }
+
+ /* wait until remote end of socket is closed */
+ while (((ret = recv(fd, &c, sizeof (c), 0)) != 0) && (errno == EINTR))
+ ;
+
+ (void) close(fd);
+
+ if (ret != 0) {
+ error_msg(gettext("Failed to determine whether inetd stopped"));
+ return (SMF_EXIT_ERR_OTHER);
+ }
+
+ return (SMF_EXIT_OK);
+}
+
+
+/*
+ * This function is called to handle restarter events coming in from the
+ * master restarter. It is registered with the master restarter via
+ * restarter_bind_handle() and simply passes a pointer to the event down
+ * the event pipe, which will be discovered by the poll in the event loop
+ * and processed there. It waits for an acknowledgement to be written back down
+ * the pipe before returning.
+ * Writing a pointer to the function's 'event' parameter down the pipe will
+ * be safe, as the thread in restarter_event_proxy() doesn't return until
+ * the main thread has finished its processing of the passed event, thus
+ * the referenced event will remain around until the function returns.
+ * To impose the limit of only one event being in the pipe and processed
+ * at once, a lock is taken on entry to this function and returned on exit.
+ * Always returns 0.
+ */
+static int
+restarter_event_proxy(restarter_event_t *event)
+{
+ restarter_event_type_t ev_type;
+ boolean_t processed;
+
+ debug_msg("Entering restarter_event_proxy");
+ ev_type = restarter_event_get_type(event);
+ debug_msg("event: %x, event type: %d", event, ev_type);
+
+ (void) pthread_mutex_lock(&rst_event_pipe_mtx);
+
+ /* write the event to the main worker thread down the pipe */
+ if (safe_write(rst_event_pipe[PE_PRODUCER], &event,
+ sizeof (event)) != 0)
+ goto pipe_error;
+
+ /*
+ * Wait for an acknowledgement that the event has been processed from
+ * the same pipe. In the case that inetd is stopping, any thread in
+ * this function will simply block on this read until inetd eventually
+ * exits. This will result in this function not returning success to
+ * its caller, and the event that was being processed when the
+ * function exited will be re-sent when inetd is next started.
+ */
+ if (safe_read(rst_event_pipe[PE_PRODUCER], &processed,
+ sizeof (processed)) != 0)
+ goto pipe_error;
+
+ (void) pthread_mutex_unlock(&rst_event_pipe_mtx);
+
+ return (processed ? 0 : EAGAIN);
+
+pipe_error:
+ /*
+ * Something's seriously wrong with the event pipe. Notify the
+ * worker thread by closing this end of the event pipe and pause till
+ * inetd exits.
+ */
+ error_msg(gettext("Can't process restarter events: %s"),
+ strerror(errno));
+ (void) close(rst_event_pipe[PE_PRODUCER]);
+ for (;;)
+ (void) pause();
+
+ /* NOTREACHED */
+}
+
+/*
+ * Let restarter_event_proxy() know we're finished with the event it's blocked
+ * upon. The 'processed' argument denotes whether we successfully processed the
+ * event.
+ */
+static void
+ack_restarter_event(boolean_t processed)
+{
+ debug_msg("Entering ack_restarter_event");
+
+ /*
+ * If safe_write returns -1 something's seriously wrong with the event
+ * pipe, so start the shutdown proceedings.
+ */
+ if (safe_write(rst_event_pipe[PE_CONSUMER], &processed,
+ sizeof (processed)) == -1)
+ inetd_stop();
+}
+
+/*
+ * Switch the syslog identification string to 'ident'.
+ */
+static void
+change_syslog_ident(const char *ident)
+{
+ debug_msg("Entering change_syslog_ident: ident: %s", ident);
+
+ closelog();
+ openlog(ident, LOG_PID|LOG_CONS, LOG_DAEMON);
+}
+
+/*
+ * Perform TCP wrappers checks on this instance. Due to the fact that the
+ * current wrappers code used in Solaris is taken untouched from the open
+ * source version, we're stuck with using the daemon name for the checks, as
+ * opposed to making use of instance FMRIs. Sigh.
+ * Returns B_TRUE if the check passed, else B_FALSE.
+ */
+static boolean_t
+tcp_wrappers_ok(instance_t *instance)
+{
+ boolean_t rval = B_TRUE;
+ char *daemon_name;
+ basic_cfg_t *cfg = instance->config->basic;
+ struct request_info req;
+
+ debug_msg("Entering tcp_wrappers_ok, instance: %s", instance->fmri);
+
+ /*
+ * Wrap the service using libwrap functions. The code below implements
+ * the functionality of tcpd. This is done only for stream,nowait
+ * services, following the convention of other vendors. udp/dgram and
+ * stream/wait can NOT be wrapped with this libwrap, so be wary of
+ * changing the test below.
+ */
+ if (cfg->do_tcp_wrappers && !cfg->iswait && !cfg->istlx) {
+
+ daemon_name = instance->config->methods[
+ IM_START]->exec_args_we.we_wordv[0];
+ if (*daemon_name == '/')
+ daemon_name = strrchr(daemon_name, '/') + 1;
+
+ /*
+ * Change the syslog message identity to the name of the
+ * daemon being wrapped, as opposed to "inetd".
+ */
+ change_syslog_ident(daemon_name);
+
+ (void) request_init(&req, RQ_DAEMON, daemon_name, RQ_FILE,
+ instance->conn_fd, NULL);
+ fromhost(&req);
+
+ if (strcasecmp(eval_hostname(req.client), paranoid) == 0) {
+ syslog(deny_severity,
+ "refused connect from %s (name/address mismatch)",
+ eval_client(&req));
+ if (req.sink != NULL)
+ req.sink(instance->conn_fd);
+ rval = B_FALSE;
+ } else if (!hosts_access(&req)) {
+ syslog(deny_severity,
+ "refused connect from %s (access denied)",
+ eval_client(&req));
+ if (req.sink != NULL)
+ req.sink(instance->conn_fd);
+ rval = B_FALSE;
+ } else {
+ syslog(allow_severity, "connect from %s",
+ eval_client(&req));
+ }
+
+ /* Revert syslog identity back to "inetd". */
+ change_syslog_ident(SYSLOG_IDENT);
+ }
+ return (rval);
+}
+
+/*
+ * Handler registered with the timer queue code to remove an instance from
+ * the connection rate offline state when it has been there for its allotted
+ * time.
+ */
+/* ARGSUSED */
+static void
+conn_rate_online(iu_tq_t *tq, void *arg)
+{
+ instance_t *instance = arg;
+
+ debug_msg("Entering conn_rate_online, instance: %s",
+ instance->fmri);
+
+ assert(instance->cur_istate == IIS_OFFLINE_CONRATE);
+ instance->timer_id = -1;
+ update_state(instance, IIS_OFFLINE, RERR_RESTART);
+ process_offline_inst(instance);
+}
+
+/*
+ * Check whether this instance in the offline state is in transition to
+ * another state and do the work to continue this transition.
+ */
+void
+process_offline_inst(instance_t *inst)
+{
+ debug_msg("Entering process_offline_inst");
+
+ if (inst->disable_req) {
+ inst->disable_req = B_FALSE;
+ (void) run_method(inst, IM_DISABLE, NULL);
+ } else if (inst->maintenance_req) {
+ inst->maintenance_req = B_FALSE;
+ update_state(inst, IIS_MAINTENANCE, RERR_RESTART);
+ /*
+ * If inetd is in the process of stopping, we don't want to enter
+ * any states but offline, disabled and maintenance.
+ */
+ } else if (!inetd_stopping) {
+ if (inst->conn_rate_exceeded) {
+ basic_cfg_t *cfg = inst->config->basic;
+
+ inst->conn_rate_exceeded = B_FALSE;
+ update_state(inst, IIS_OFFLINE_CONRATE, RERR_RESTART);
+ /*
+ * Schedule a timer to bring the instance out of the
+ * connection rate offline state.
+ */
+ inst->timer_id = iu_schedule_timer(timer_queue,
+ cfg->conn_rate_offline, conn_rate_online,
+ inst);
+ if (inst->timer_id == -1) {
+ error_msg(gettext("%s unable to set timer, "
+ "won't be brought on line after %d "
+ "seconds."), inst->fmri,
+ cfg->conn_rate_offline);
+ }
+
+ } else if (copies_limit_exceeded(inst)) {
+ update_state(inst, IIS_OFFLINE_COPIES, RERR_RESTART);
+ }
+ }
+}
+
+/*
+ * Create a socket bound to the instance's configured address. If the
+ * bind fails, returns -1, else the fd of the bound socket.
+ */
+static int
+create_bound_socket(const char *fmri, socket_info_t *sock_info)
+{
+ int fd;
+ int on = 1;
+ rpc_info_t *rpc = sock_info->pr_info.ri;
+ const char *proto = sock_info->pr_info.proto;
+
+ debug_msg("Entering create_bound_socket");
+
+ fd = socket(sock_info->local_addr.ss_family, sock_info->type,
+ sock_info->protocol);
+ if (fd < 0) {
+ error_msg(gettext(
+ "Socket creation failure for instance %s, proto %s: %s"),
+ fmri, proto, strerror(errno));
+ return (-1);
+ }
+
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) == -1) {
+ error_msg(gettext("setsockopt SO_REUSEADDR failed for service "
+ "instance %s, proto %s: %s"), fmri, proto, strerror(errno));
+ (void) close(fd);
+ return (-1);
+ }
+ if (sock_info->pr_info.v6only) {
+ /* restrict socket to IPv6 communications only */
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &on,
+ sizeof (on)) == -1) {
+ error_msg(gettext("setsockopt IPV6_V6ONLY failed for "
+ "service instance %s, proto %s: %s"), fmri, proto,
+ strerror(errno));
+ (void) close(fd);
+ return (-1);
+ }
+ }
+
+ if (rpc != NULL)
+ SS_SETPORT(sock_info->local_addr, 0);
+
+ if (bind(fd, (struct sockaddr *)&(sock_info->local_addr),
+ SS_ADDRLEN(sock_info->local_addr)) < 0) {
+ error_msg(gettext(
+ "Failed to bind to the port of service instance %s, "
+ "proto %s: %s"), fmri, proto, strerror(errno));
+ (void) close(fd);
+ return (-1);
+ }
+
+ /*
+ * Retrieve and store the address bound to for RPC services.
+ */
+ if (rpc != NULL) {
+ struct sockaddr_storage ss;
+ int ss_size = sizeof (ss);
+
+ if (getsockname(fd, (struct sockaddr *)&ss, &ss_size) < 0) {
+ error_msg(gettext("Failed getsockname for instance %s, "
+ "proto %s: %s"), fmri, proto, strerror(errno));
+ (void) close(fd);
+ return (-1);
+ }
+ (void) memcpy(rpc->netbuf.buf, &ss,
+ sizeof (struct sockaddr_storage));
+ rpc->netbuf.len = SS_ADDRLEN(ss);
+ rpc->netbuf.maxlen = SS_ADDRLEN(ss);
+ }
+
+ if (sock_info->type == SOCK_STREAM)
+ (void) listen(fd, CONNECTION_BACKLOG);
+
+ return (fd);
+}
+
+/*
+ * Handler registered with the timer queue code to retry the creation
+ * of a bound fd.
+ */
+/* ARGSUSED */
+static void
+retry_bind(iu_tq_t *tq, void *arg)
+{
+ instance_t *instance = arg;
+
+ debug_msg("Entering retry_bind, instance: %s", instance->fmri);
+
+ switch (instance->cur_istate) {
+ case IIS_OFFLINE_BIND:
+ case IIS_ONLINE:
+ case IIS_DEGRADED:
+ case IIS_IN_ONLINE_METHOD:
+ case IIS_IN_REFRESH_METHOD:
+ break;
+ default:
+#ifndef NDEBUG
+ (void) fprintf(stderr, "%s:%d: Unknown instance state %d.\n",
+ __FILE__, __LINE__, instance->cur_istate);
+#endif
+ abort();
+ }
+
+ instance->bind_timer_id = -1;
+ create_bound_fds(instance);
+}
+
+/*
+ * For each of the fds for the given instance that are bound, if 'listen' is
+ * set add them to the poll set, else remove them from it. If any additions
+ * fail, returns -1, else 0 on success.
+ */
+int
+poll_bound_fds(instance_t *instance, boolean_t listen)
+{
+ basic_cfg_t *cfg = instance->config->basic;
+ proto_info_t *pi;
+ int ret = 0;
+
+ debug_msg("Entering poll_bound_fds: instance: %s, on: %d",
+ instance->fmri, listen);
+
+ for (pi = uu_list_first(cfg->proto_list); pi != NULL;
+ pi = uu_list_next(cfg->proto_list, pi)) {
+ if (pi->listen_fd != -1) { /* fd bound */
+ if (!listen) {
+ clear_pollfd(pi->listen_fd);
+ } else if (set_pollfd(pi->listen_fd, POLLIN) == -1) {
+ ret = -1;
+ }
+ }
+ }
+
+ return (ret);
+}
+
+/*
+ * Handle the case were we either fail to create a bound fd or we fail
+ * to add a bound fd to the poll set for the given instance.
+ */
+static void
+handle_bind_failure(instance_t *instance)
+{
+ basic_cfg_t *cfg = instance->config->basic;
+
+ debug_msg("Entering handle_bind_failure: instance: %s", instance);
+
+ /*
+ * We must be being called as a result of a failed poll_bound_fds()
+ * as a bind retry is already scheduled. Just return and let it do
+ * the work.
+ */
+ if (instance->bind_timer_id != -1)
+ return;
+
+ /*
+ * Check if the rebind retries limit is operative and if so,
+ * if it has been reached.
+ */
+ if (((cfg->bind_fail_interval <= 0) || /* no retries */
+ ((cfg->bind_fail_max >= 0) && /* limit reached */
+ (++instance->bind_fail_count > cfg->bind_fail_max))) ||
+ ((instance->bind_timer_id = iu_schedule_timer(timer_queue,
+ cfg->bind_fail_interval, retry_bind, instance)) == -1)) {
+ proto_info_t *pi;
+
+ instance->bind_fail_count = 0;
+
+ switch (instance->cur_istate) {
+ case IIS_DEGRADED:
+ case IIS_ONLINE:
+ /* check if any of the fds are being poll'd upon */
+ for (pi = uu_list_first(cfg->proto_list); pi != NULL;
+ pi = uu_list_next(cfg->proto_list, pi)) {
+ if ((pi->listen_fd != -1) &&
+ (find_pollfd(pi->listen_fd) != NULL))
+ break;
+ }
+ if (pi != NULL) { /* polling on > 0 fds */
+ warn_msg(gettext("Failed to bind on "
+ "all protocols for instance %s, "
+ "transitioning to degraded"),
+ instance->fmri);
+ update_state(instance, IIS_DEGRADED, RERR_NONE);
+ instance->bind_retries_exceeded = B_TRUE;
+ break;
+ }
+
+ destroy_bound_fds(instance);
+ /*
+ * In the case we failed the 'bind' because set_pollfd()
+ * failed on all bound fds, use the offline handling.
+ */
+ /* FALLTHROUGH */
+ case IIS_OFFLINE:
+ case IIS_OFFLINE_BIND:
+ error_msg(gettext("Too many bind failures for instance "
+ "%s, transitioning to maintenance"), instance->fmri);
+ update_state(instance, IIS_MAINTENANCE,
+ RERR_FAULT);
+ break;
+ case IIS_IN_ONLINE_METHOD:
+ case IIS_IN_REFRESH_METHOD:
+ warn_msg(gettext("Failed to bind on all "
+ "protocols for instance %s, instance will go to "
+ "degraded"), instance->fmri);
+ /*
+ * Set the retries exceeded flag so when the method
+ * completes the instance goes to the degraded state.
+ */
+ instance->bind_retries_exceeded = B_TRUE;
+ break;
+ default:
+#ifndef NDEBUG
+ (void) fprintf(stderr,
+ "%s:%d: Unknown instance state %d.\n",
+ __FILE__, __LINE__, instance->cur_istate);
+#endif
+ abort();
+ }
+ } else if (instance->cur_istate == IIS_OFFLINE) {
+ /*
+ * bind re-scheduled, so if we're offline reflect this in the
+ * state.
+ */
+ update_state(instance, IIS_OFFLINE_BIND, RERR_NONE);
+ }
+}
+
+/*
+ * Independent of the transport, for each of the entries in the instance's
+ * proto list this function first attempts to create an associated network fd;
+ * for RPC services these are then bound to a kernel chosen port and the
+ * fd is registered with rpcbind; for non-RPC services the fds are bound
+ * to the port associated with the instance's service name. On any successful
+ * binds the instance is taken online. Failed binds are handled by
+ * handle_bind_failure().
+ */
+void
+create_bound_fds(instance_t *instance)
+{
+ basic_cfg_t *cfg = instance->config->basic;
+ boolean_t failure = B_FALSE;
+ boolean_t success = B_FALSE;
+ proto_info_t *pi;
+
+ debug_msg("Entering create_bound_fd: instance: %s", instance->fmri);
+
+ /*
+ * Loop through and try and bind any unbound protos.
+ */
+ for (pi = uu_list_first(cfg->proto_list); pi != NULL;
+ pi = uu_list_next(cfg->proto_list, pi)) {
+ if (pi->listen_fd != -1)
+ continue;
+ if (cfg->istlx) {
+ pi->listen_fd = create_bound_endpoint(instance->fmri,
+ (tlx_info_t *)pi);
+ } else {
+ /*
+ * We cast pi to a void so we can then go on to cast
+ * it to a socket_info_t without lint complaining
+ * about alignment. This is done because the x86
+ * version of lint thinks a lint suppression directive
+ * is unnecessary and flags it as such, yet the sparc
+ * version complains if it's absent.
+ */
+ void *p = pi;
+ pi->listen_fd = create_bound_socket(instance->fmri,
+ (socket_info_t *)p);
+ }
+ if (pi->listen_fd == -1) {
+ failure = B_TRUE;
+ continue;
+ }
+
+ if (pi->ri != NULL) {
+ unregister_rpc_service(instance->fmri, pi->ri);
+ if (register_rpc_service(instance->fmri, pi->ri) ==
+ -1) {
+ close_net_fd(instance, pi->listen_fd);
+ pi->listen_fd = -1;
+ failure = B_TRUE;
+ continue;
+ }
+ }
+
+ success = B_TRUE;
+ }
+
+ switch (instance->cur_istate) {
+ case IIS_OFFLINE:
+ case IIS_OFFLINE_BIND:
+ /*
+ * If we've managed to bind at least one proto lets run the
+ * online method, so we can start listening for it.
+ */
+ if (success && run_method(instance, IM_ONLINE, NULL) == -1)
+ return; /* instance gone to maintenance */
+ break;
+ case IIS_ONLINE:
+ case IIS_IN_REFRESH_METHOD:
+ /*
+ * We're 'online', so start polling on any bound fds we're
+ * currently not.
+ */
+ if (poll_bound_fds(instance, B_TRUE) != 0) {
+ failure = B_TRUE;
+ } else if (!failure) {
+ /*
+ * We've successfully bound and poll'd upon all protos,
+ * so reset the failure count.
+ */
+ instance->bind_fail_count = 0;
+ }
+ break;
+ case IIS_IN_ONLINE_METHOD:
+ /*
+ * Nothing to do here as the method completion code will start
+ * listening for any successfully bound fds.
+ */
+ break;
+ default:
+#ifndef NDEBUG
+ (void) fprintf(stderr, "%s:%d: Unknown instance state %d.\n",
+ __FILE__, __LINE__, instance->cur_istate);
+#endif
+ abort();
+ }
+
+ if (failure)
+ handle_bind_failure(instance);
+}
+
+/*
+ * Counter to create_bound_fds(), for each of the bound network fds this
+ * function unregisters the instance from rpcbind if it's an RPC service,
+ * stops listening for new connections for it and then closes the listening fd.
+ */
+static void
+destroy_bound_fds(instance_t *instance)
+{
+ basic_cfg_t *cfg = instance->config->basic;
+ proto_info_t *pi;
+
+ debug_msg("Entering destroy_bound_fds: instance: %s", instance->fmri);
+
+ for (pi = uu_list_first(cfg->proto_list); pi != NULL;
+ pi = uu_list_next(cfg->proto_list, pi)) {
+ if (pi->listen_fd != -1) {
+ if (pi->ri != NULL)
+ unregister_rpc_service(instance->fmri, pi->ri);
+ clear_pollfd(pi->listen_fd);
+ close_net_fd(instance, pi->listen_fd);
+ pi->listen_fd = -1;
+ }
+ }
+
+ /* cancel any bind retries */
+ if (instance->bind_timer_id != -1)
+ cancel_bind_timer(instance);
+
+ instance->bind_retries_exceeded = B_FALSE;
+}
+
+/*
+ * Perform %A address expansion and return a pointer to a static string
+ * array containing crafted arguments. This expansion is provided for
+ * compatibility with 4.2BSD daemons, and as such we've copied the logic of
+ * the legacy inetd to maintain this compatibility as much as possible. This
+ * logic is a bit scatty, but it dates back at least as far as SunOS 4.x.
+ */
+static char **
+expand_address(instance_t *inst, const proto_info_t *pi)
+{
+ static char addrbuf[sizeof ("ffffffff.65536")];
+ static char *ret[3];
+ instance_cfg_t *cfg = inst->config;
+ /*
+ * We cast pi to a void so we can then go on to cast it to a
+ * socket_info_t without lint complaining about alignment. This
+ * is done because the x86 version of lint thinks a lint suppression
+ * directive is unnecessary and flags it as such, yet the sparc
+ * version complains if it's absent.
+ */
+ const void *p = pi;
+
+ debug_msg("Entering expand_address");
+
+ /* set ret[0] to the basename of exec path */
+ if ((ret[0] = strrchr(cfg->methods[IM_START]->exec_path, '/'))
+ != NULL) {
+ ret[0]++;
+ } else {
+ ret[0] = cfg->methods[IM_START]->exec_path;
+ }
+
+ if (!cfg->basic->istlx &&
+ (((socket_info_t *)p)->type == SOCK_DGRAM)) {
+ ret[1] = NULL;
+ } else {
+ addrbuf[0] = '\0';
+ if (!cfg->basic->iswait &&
+ (inst->remote_addr.ss_family == AF_INET)) {
+ struct sockaddr_in *sp;
+
+ sp = (struct sockaddr_in *)&(inst->remote_addr);
+ (void) snprintf(addrbuf, sizeof (addrbuf), "%x.%hu",
+ ntohl(sp->sin_addr.s_addr), ntohs(sp->sin_port));
+ }
+ ret[1] = addrbuf;
+ ret[2] = NULL;
+ }
+
+ return (ret);
+}
+
+/*
+ * Returns the state associated with the supplied method being run for an
+ * instance.
+ */
+static internal_inst_state_t
+get_method_state(instance_method_t method)
+{
+ state_info_t *sip;
+
+ for (sip = states; sip->istate != IIS_NONE; sip++) {
+ if (sip->method_running == method)
+ break;
+ }
+ assert(sip->istate != IIS_NONE);
+
+ return (sip->istate);
+}
+
+/*
+ * Store the method's PID and CID in the repository. If the store fails
+ * we ignore it and just drive on.
+ */
+static void
+add_method_ids(instance_t *ins, pid_t pid, ctid_t cid, instance_method_t mthd)
+{
+ debug_msg("Entering add_method_ids");
+
+ if (cid != -1)
+ (void) add_remove_contract(ins->fmri, B_TRUE, cid);
+
+ if (mthd == IM_START) {
+ if (add_rep_val(ins->start_pids, (int64_t)pid) == 0) {
+ (void) store_rep_vals(ins->start_pids, ins->fmri,
+ PR_NAME_START_PIDS);
+ }
+ } else {
+ if (add_rep_val(ins->non_start_pid, (int64_t)pid) == 0) {
+ (void) store_rep_vals(ins->non_start_pid, ins->fmri,
+ PR_NAME_NON_START_PID);
+ }
+ }
+}
+
+/*
+ * Remove the method's PID and CID from the repository. If the removal
+ * fails we ignore it and drive on.
+ */
+void
+remove_method_ids(instance_t *inst, pid_t pid, ctid_t cid,
+ instance_method_t mthd)
+{
+ debug_msg("Entering remove_method_ids");
+
+ if (cid != -1)
+ (void) add_remove_contract(inst->fmri, B_FALSE, cid);
+
+ if (mthd == IM_START) {
+ remove_rep_val(inst->start_pids, (int64_t)pid);
+ (void) store_rep_vals(inst->start_pids, inst->fmri,
+ PR_NAME_START_PIDS);
+ } else {
+ remove_rep_val(inst->non_start_pid, (int64_t)pid);
+ (void) store_rep_vals(inst->non_start_pid, inst->fmri,
+ PR_NAME_NON_START_PID);
+ }
+}
+
+static instance_t *
+create_instance(const char *fmri)
+{
+ instance_t *ret;
+
+ debug_msg("Entering create_instance, instance: %s", fmri);
+
+ if (((ret = calloc(1, sizeof (instance_t))) == NULL) ||
+ ((ret->fmri = strdup(fmri)) == NULL))
+ goto alloc_fail;
+
+ ret->conn_fd = -1;
+
+ ret->copies = 0;
+
+ ret->conn_rate_count = 0;
+ ret->fail_rate_count = 0;
+ ret->bind_fail_count = 0;
+
+ if (((ret->non_start_pid = create_rep_val_list()) == NULL) ||
+ ((ret->start_pids = create_rep_val_list()) == NULL))
+ goto alloc_fail;
+
+ ret->cur_istate = IIS_NONE;
+ ret->next_istate = IIS_NONE;
+
+ if (((ret->cur_istate_rep = create_rep_val_list()) == NULL) ||
+ ((ret->next_istate_rep = create_rep_val_list()) == NULL))
+ goto alloc_fail;
+
+ ret->config = NULL;
+ ret->new_config = NULL;
+
+ ret->timer_id = -1;
+ ret->bind_timer_id = -1;
+
+ ret->disable_req = B_FALSE;
+ ret->maintenance_req = B_FALSE;
+ ret->conn_rate_exceeded = B_FALSE;
+ ret->bind_retries_exceeded = B_FALSE;
+
+ ret->pending_rst_event = RESTARTER_EVENT_TYPE_INVALID;
+
+ return (ret);
+
+alloc_fail:
+ error_msg(strerror(errno));
+ destroy_instance(ret);
+ return (NULL);
+}
+
+static void
+destroy_instance(instance_t *inst)
+{
+ debug_msg("Entering destroy_instance");
+
+ if (inst == NULL)
+ return;
+
+ destroy_instance_cfg(inst->config);
+ destroy_instance_cfg(inst->new_config);
+
+ destroy_rep_val_list(inst->cur_istate_rep);
+ destroy_rep_val_list(inst->next_istate_rep);
+
+ destroy_rep_val_list(inst->start_pids);
+ destroy_rep_val_list(inst->non_start_pid);
+
+ free(inst->fmri);
+
+ free(inst);
+}
+
+/*
+ * Retrieves the current and next states internal states. Returns 0 on success,
+ * else returns one of the following on error:
+ * SCF_ERROR_NO_MEMORY if memory allocation failed.
+ * SCF_ERROR_CONNECTION_BROKEN if the connection to the repository was broken.
+ * SCF_ERROR_TYPE_MISMATCH if the property was of an unexpected type.
+ * SCF_ERROR_NO_RESOURCES if the server doesn't have adequate resources.
+ * SCF_ERROR_NO_SERVER if the server isn't running.
+ */
+static scf_error_t
+retrieve_instance_state(instance_t *inst)
+{
+ scf_error_t ret;
+
+ debug_msg("Entering retrieve_instance_state: instance: %s",
+ inst->fmri);
+
+ /* retrieve internal states */
+ if (((ret = retrieve_rep_vals(inst->cur_istate_rep, inst->fmri,
+ PR_NAME_CUR_INT_STATE)) != 0) ||
+ ((ret = retrieve_rep_vals(inst->next_istate_rep, inst->fmri,
+ PR_NAME_NEXT_INT_STATE)) != 0)) {
+ if (ret != SCF_ERROR_NOT_FOUND) {
+ error_msg(gettext(
+ "Failed to read state of instance %s: %s"),
+ inst->fmri, scf_strerror(scf_error()));
+ return (ret);
+ }
+
+ debug_msg("instance with no previous int state - "
+ "setting state to uninitialized");
+
+ if ((set_single_rep_val(inst->cur_istate_rep,
+ (int64_t)IIS_UNINITIALIZED) == -1) ||
+ (set_single_rep_val(inst->next_istate_rep,
+ (int64_t)IIS_NONE) == -1)) {
+ return (SCF_ERROR_NO_MEMORY);
+ }
+ }
+
+ /* update convenience states */
+ inst->cur_istate = get_single_rep_val(inst->cur_istate_rep);
+ inst->next_istate = get_single_rep_val(inst->next_istate_rep);
+ debug_msg("previous states: cur: %d, next: %d", inst->cur_istate,
+ inst->next_istate);
+
+ return (0);
+}
+
+/*
+ * Retrieve stored process ids and register each of them so we process their
+ * termination.
+ */
+static int
+retrieve_method_pids(instance_t *inst)
+{
+ rep_val_t *rv;
+
+ debug_msg("Entering remove_method_pids");
+
+ switch (retrieve_rep_vals(inst->start_pids, inst->fmri,
+ PR_NAME_START_PIDS)) {
+ case 0:
+ break;
+ case SCF_ERROR_NOT_FOUND:
+ return (0);
+ default:
+ error_msg(gettext("Failed to retrieve the start pids of "
+ "instance %s from repository: %s"), inst->fmri,
+ scf_strerror(scf_error()));
+ return (-1);
+ }
+
+ rv = uu_list_first(inst->start_pids);
+ while (rv != NULL) {
+ if (register_method(inst, (pid_t)rv->val, (ctid_t)-1,
+ IM_START) == 0) {
+ inst->copies++;
+ rv = uu_list_next(inst->start_pids, rv);
+ } else if (errno == ENOENT) {
+ pid_t pid = (pid_t)rv->val;
+
+ /*
+ * The process must have already terminated. Remove
+ * it from the list.
+ */
+ rv = uu_list_next(inst->start_pids, rv);
+ remove_rep_val(inst->start_pids, pid);
+ } else {
+ error_msg(gettext("Failed to listen for the completion "
+ "of %s method of instance %s"), START_METHOD_NAME,
+ inst->fmri);
+ rv = uu_list_next(inst->start_pids, rv);
+ }
+ }
+
+ /* synch the repository pid list to remove any terminated pids */
+ (void) store_rep_vals(inst->start_pids, inst->fmri, PR_NAME_START_PIDS);
+
+ return (0);
+}
+
+/*
+ * Remove the passed instance from inetd control.
+ */
+static void
+remove_instance(instance_t *instance)
+{
+ debug_msg("Entering remove_instance");
+
+ switch (instance->cur_istate) {
+ case IIS_ONLINE:
+ case IIS_DEGRADED:
+ /* stop listening for network connections */
+ destroy_bound_fds(instance);
+ break;
+ case IIS_OFFLINE_BIND:
+ cancel_bind_timer(instance);
+ break;
+ case IIS_OFFLINE_CONRATE:
+ cancel_inst_timer(instance);
+ break;
+ }
+
+ /* stop listening for terminated methods */
+ unregister_instance_methods(instance);
+
+ uu_list_remove(instance_list, instance);
+ destroy_instance(instance);
+}
+
+/*
+ * Refresh the configuration of instance 'inst'. This method gets called as
+ * a result of a refresh event for the instance from the master restarter, so
+ * we can rely upon the instance's running snapshot having been updated from
+ * its configuration snapshot.
+ */
+void
+refresh_instance(instance_t *inst)
+{
+ instance_cfg_t *cfg;
+
+ debug_msg("Entering refresh_instance: inst: %s", inst->fmri);
+
+ switch (inst->cur_istate) {
+ case IIS_MAINTENANCE:
+ case IIS_DISABLED:
+ case IIS_UNINITIALIZED:
+ /*
+ * Ignore any possible changes, we'll re-read the configuration
+ * automatically when we exit these states.
+ */
+ break;
+
+ case IIS_OFFLINE_COPIES:
+ case IIS_OFFLINE_BIND:
+ case IIS_OFFLINE:
+ case IIS_OFFLINE_CONRATE:
+ destroy_instance_cfg(inst->config);
+ if ((inst->config = read_instance_cfg(inst->fmri)) == NULL) {
+ log_invalid_cfg(inst->fmri);
+ if (inst->cur_istate == IIS_OFFLINE_BIND) {
+ cancel_bind_timer(inst);
+ } else if (inst->cur_istate == IIS_OFFLINE_CONRATE) {
+ cancel_inst_timer(inst);
+ }
+ update_state(inst, IIS_MAINTENANCE, RERR_FAULT);
+ } else {
+ switch (inst->cur_istate) {
+ case IIS_OFFLINE_BIND:
+ if (copies_limit_exceeded(inst)) {
+ /* Cancel scheduled bind retries. */
+ cancel_bind_timer(inst);
+
+ /*
+ * Take the instance to the copies
+ * offline state, via the offline
+ * state.
+ */
+ update_state(inst, IIS_OFFLINE,
+ RERR_RESTART);
+ process_offline_inst(inst);
+ }
+ break;
+
+ case IIS_OFFLINE:
+ process_offline_inst(inst);
+ break;
+
+ case IIS_OFFLINE_CONRATE:
+ /*
+ * Since we're already in a DOS state,
+ * don't bother evaluating the copies
+ * limit. This will be evaluated when
+ * we leave this state in
+ * process_offline_inst().
+ */
+ break;
+
+ case IIS_OFFLINE_COPIES:
+ /*
+ * Check if the copies limit has been increased
+ * above the current count.
+ */
+ if (!copies_limit_exceeded(inst)) {
+ update_state(inst, IIS_OFFLINE,
+ RERR_RESTART);
+ process_offline_inst(inst);
+ }
+ break;
+
+ default:
+ assert(0);
+ }
+ }
+ break;
+
+ case IIS_DEGRADED:
+ case IIS_ONLINE:
+ if ((cfg = read_instance_cfg(inst->fmri)) != NULL) {
+ instance_cfg_t *ocfg = inst->config;
+
+ /*
+ * Try to avoid the overhead of taking an instance
+ * offline and back on again. We do this by limiting
+ * this behavior to two eventualities:
+ * - there needs to be a re-bind to listen on behalf
+ * of the instance with its new configuration. This
+ * could be because for example its service has been
+ * associated with a different port, or because the
+ * v6only protocol option has been newly applied to
+ * the instance.
+ * - one or both of the start or online methods of the
+ * instance have changed in the new configuration.
+ * Without taking the instance offline when the
+ * start method changed the instance may be running
+ * with unwanted parameters (or event an unwanted
+ * binary); and without taking the instance offline
+ * if its online method was to change, some part of
+ * its running environment may have changed and would
+ * not be picked up until the instance next goes
+ * offline for another reason.
+ */
+ if ((!bind_config_equal(ocfg->basic, cfg->basic)) ||
+ !method_info_equal(ocfg->methods[IM_ONLINE],
+ cfg->methods[IM_ONLINE]) ||
+ !method_info_equal(ocfg->methods[IM_START],
+ cfg->methods[IM_START])) {
+ destroy_bound_fds(inst);
+
+ assert(inst->new_config == NULL);
+ inst->new_config = cfg;
+
+ (void) run_method(inst, IM_OFFLINE, NULL);
+ } else { /* no bind config / method changes */
+
+ /*
+ * swap the proto list over from the old
+ * configuration to the new, so we retain
+ * our set of network fds.
+ */
+ destroy_proto_list(cfg->basic);
+ cfg->basic->proto_list =
+ ocfg->basic->proto_list;
+ ocfg->basic->proto_list = NULL;
+ destroy_instance_cfg(ocfg);
+ inst->config = cfg;
+
+ /* re-evaluate copies limits based on new cfg */
+ if (copies_limit_exceeded(inst)) {
+ destroy_bound_fds(inst);
+ (void) run_method(inst, IM_OFFLINE,
+ NULL);
+ } else {
+ /*
+ * Since the instance isn't being
+ * taken offline, where we assume it
+ * would pick-up any configuration
+ * changes automatically when it goes
+ * back online, run its refresh method
+ * to allow it to pick-up any changes
+ * whilst still online.
+ */
+ (void) run_method(inst, IM_REFRESH,
+ NULL);
+ }
+ }
+ } else {
+ log_invalid_cfg(inst->fmri);
+
+ destroy_bound_fds(inst);
+
+ inst->maintenance_req = B_TRUE;
+ (void) run_method(inst, IM_OFFLINE, NULL);
+ }
+ break;
+
+ default:
+ debug_msg("Unhandled current state %d for instance in "
+ "refresh_instance", inst->cur_istate);
+ assert(0);
+ }
+}
+
+/*
+ * Called by process_restarter_event() to handle a restarter event for an
+ * instance.
+ */
+static void
+handle_restarter_event(instance_t *instance, restarter_event_type_t event,
+ boolean_t send_ack)
+{
+ debug_msg("Entering handle_restarter_event: inst: %s, event: %d, "
+ "curr state: %d", instance->fmri, event, instance->cur_istate);
+
+ switch (event) {
+ case RESTARTER_EVENT_TYPE_ADMIN_REFRESH:
+ refresh_instance(instance);
+ goto done;
+ case RESTARTER_EVENT_TYPE_REMOVE_INSTANCE:
+ remove_instance(instance);
+ goto done;
+ case RESTARTER_EVENT_TYPE_STOP:
+ switch (instance->cur_istate) {
+ case IIS_OFFLINE_CONRATE:
+ case IIS_OFFLINE_BIND:
+ case IIS_OFFLINE_COPIES:
+ /*
+ * inetd must be closing down as we wouldn't get this
+ * event in one of these states from the master
+ * restarter. Take the instance to the offline resting
+ * state.
+ */
+ if (instance->cur_istate == IIS_OFFLINE_BIND) {
+ cancel_bind_timer(instance);
+ } else if (instance->cur_istate ==
+ IIS_OFFLINE_CONRATE) {
+ cancel_inst_timer(instance);
+ }
+ update_state(instance, IIS_OFFLINE, RERR_RESTART);
+ goto done;
+ }
+ break;
+ case RESTARTER_EVENT_TYPE_ADMIN_RESTART:
+ /*
+ * We've got a restart event, so if the instance is online
+ * in any way initiate taking it offline, and rely upon
+ * our restarter to send us an online event to bring
+ * it back online.
+ */
+ switch (instance->cur_istate) {
+ case IIS_ONLINE:
+ case IIS_DEGRADED:
+ destroy_bound_fds(instance);
+ (void) run_method(instance, IM_OFFLINE, NULL);
+ }
+ goto done;
+ }
+
+ switch (instance->cur_istate) {
+ case IIS_OFFLINE:
+ switch (event) {
+ case RESTARTER_EVENT_TYPE_START:
+ /*
+ * Dependencies are met, let's take the service online.
+ * Only try and bind for a wait type service if
+ * no process is running on its behalf. Otherwise, just
+ * mark the service online and binding will be attempted
+ * when the process exits.
+ */
+ if (!(instance->config->basic->iswait &&
+ (uu_list_first(instance->start_pids) != NULL))) {
+ create_bound_fds(instance);
+ } else {
+ update_state(instance, IIS_ONLINE, RERR_NONE);
+ }
+ break;
+ case RESTARTER_EVENT_TYPE_DISABLE:
+ case RESTARTER_EVENT_TYPE_ADMIN_DISABLE:
+ /*
+ * The instance should be disabled, so run the
+ * instance's disabled method that will do the work
+ * to take it there.
+ */
+ (void) run_method(instance, IM_DISABLE, NULL);
+ break;
+ case RESTARTER_EVENT_TYPE_ADMIN_MAINT_ON:
+ case RESTARTER_EVENT_TYPE_DEPENDENCY_CYCLE:
+ case RESTARTER_EVENT_TYPE_INVALID_DEPENDENCY:
+ /*
+ * The master restarter has requested the instance
+ * go to maintenance; since we're already offline
+ * just update the state to the maintenance state.
+ */
+ update_state(instance, IIS_MAINTENANCE, RERR_RESTART);
+ break;
+ }
+ break;
+
+ case IIS_OFFLINE_BIND:
+ switch (event) {
+ case RESTARTER_EVENT_TYPE_DISABLE:
+ case RESTARTER_EVENT_TYPE_ADMIN_DISABLE:
+ /*
+ * The instance should be disabled. Firstly, as for
+ * the above dependencies unmet comment, cancel
+ * the bind retry timer and update the state to
+ * offline. Then, run the disable method to do the
+ * work to take the instance from offline to
+ * disabled.
+ */
+ cancel_bind_timer(instance);
+ update_state(instance, IIS_OFFLINE, RERR_RESTART);
+ (void) run_method(instance, IM_DISABLE, NULL);
+ break;
+ case RESTARTER_EVENT_TYPE_ADMIN_MAINT_ON:
+ case RESTARTER_EVENT_TYPE_DEPENDENCY_CYCLE:
+ case RESTARTER_EVENT_TYPE_INVALID_DEPENDENCY:
+ /*
+ * The master restarter has requested the instance
+ * be placed in the maintenance state. Cancel the
+ * outstanding retry timer, and since we're already
+ * offline, update the state to maintenance.
+ */
+ cancel_bind_timer(instance);
+ update_state(instance, IIS_MAINTENANCE, RERR_RESTART);
+ break;
+ }
+ break;
+
+ case IIS_DEGRADED:
+ case IIS_ONLINE:
+ switch (event) {
+ case RESTARTER_EVENT_TYPE_DISABLE:
+ case RESTARTER_EVENT_TYPE_ADMIN_DISABLE:
+ /*
+ * The instance needs to be disabled. Do the same work
+ * as for the dependencies unmet event below to
+ * take the instance offline.
+ */
+ destroy_bound_fds(instance);
+ /*
+ * Indicate that the offline method is being run
+ * as part of going to the disabled state, and to
+ * carry on this transition.
+ */
+ instance->disable_req = B_TRUE;
+ (void) run_method(instance, IM_OFFLINE, NULL);
+ break;
+ case RESTARTER_EVENT_TYPE_ADMIN_MAINT_ON:
+ case RESTARTER_EVENT_TYPE_DEPENDENCY_CYCLE:
+ case RESTARTER_EVENT_TYPE_INVALID_DEPENDENCY:
+ /*
+ * The master restarter has requested the instance be
+ * placed in the maintenance state. This involves
+ * firstly taking the service offline, so do the
+ * same work as for the dependencies unmet event
+ * below. We set the maintenance_req flag to
+ * indicate that when we get to the offline state
+ * we should be placed directly into the maintenance
+ * state.
+ */
+ instance->maintenance_req = B_TRUE;
+ /* FALLTHROUGH */
+ case RESTARTER_EVENT_TYPE_STOP:
+ /*
+ * Dependencies have become unmet. Close and
+ * stop listening on the instance's network file
+ * descriptor, and run the offline method to do
+ * any work required to take us to the offline state.
+ */
+ destroy_bound_fds(instance);
+ (void) run_method(instance, IM_OFFLINE, NULL);
+ }
+ break;
+
+ case IIS_UNINITIALIZED:
+ if (event == RESTARTER_EVENT_TYPE_DISABLE ||
+ event == RESTARTER_EVENT_TYPE_ADMIN_DISABLE) {
+ update_state(instance, IIS_DISABLED, RERR_NONE);
+ break;
+ } else if (event != RESTARTER_EVENT_TYPE_ENABLE) {
+ /*
+ * Ignore other events until we know whether we're
+ * enabled or not.
+ */
+ break;
+ }
+
+ /*
+ * We've got an enabled event; make use of the handling in the
+ * disable case.
+ */
+ /* FALLTHROUGH */
+
+ case IIS_DISABLED:
+ switch (event) {
+ case RESTARTER_EVENT_TYPE_ENABLE:
+ /*
+ * The instance needs enabling. Commence reading its
+ * configuration and if successful place the instance
+ * in the offline state and let process_offline_inst()
+ * take it from there.
+ */
+ destroy_instance_cfg(instance->config);
+ instance->config = read_instance_cfg(instance->fmri);
+ if (instance->config != NULL) {
+ update_state(instance, IIS_OFFLINE,
+ RERR_RESTART);
+ process_offline_inst(instance);
+ } else {
+ log_invalid_cfg(instance->fmri);
+ update_state(instance, IIS_MAINTENANCE,
+ RERR_RESTART);
+ }
+
+ break;
+ case RESTARTER_EVENT_TYPE_ADMIN_MAINT_ON:
+ case RESTARTER_EVENT_TYPE_DEPENDENCY_CYCLE:
+ case RESTARTER_EVENT_TYPE_INVALID_DEPENDENCY:
+ /*
+ * The master restarter has requested the instance be
+ * placed in the maintenance state, so just update its
+ * state to maintenance.
+ */
+ update_state(instance, IIS_MAINTENANCE, RERR_RESTART);
+ break;
+ }
+ break;
+
+ case IIS_MAINTENANCE:
+ switch (event) {
+ case RESTARTER_EVENT_TYPE_ADMIN_MAINT_OFF:
+ case RESTARTER_EVENT_TYPE_ADMIN_DISABLE:
+ /*
+ * The master restarter has requested that the instance
+ * be taken out of maintenance. Read its configuration,
+ * and if successful place the instance in the offline
+ * state and call process_offline_inst() to take it
+ * from there.
+ */
+ destroy_instance_cfg(instance->config);
+ instance->config = read_instance_cfg(instance->fmri);
+ if (instance->config != NULL) {
+ update_state(instance, IIS_OFFLINE,
+ RERR_RESTART);
+ process_offline_inst(instance);
+ } else {
+ boolean_t enabled;
+
+ /*
+ * The configuration was invalid. If the
+ * service has disabled requested, let's
+ * just place the instance in disabled even
+ * though we haven't been able to run its
+ * disable method, as the slightly incorrect
+ * state is likely to be less of an issue to
+ * an administrator than refusing to move an
+ * instance to disabled. If disable isn't
+ * requested, re-mark the service's state
+ * as maintenance, so the administrator can
+ * see the request was processed.
+ */
+ if ((read_enable_merged(instance->fmri,
+ &enabled) == 0) && !enabled) {
+ update_state(instance, IIS_DISABLED,
+ RERR_RESTART);
+ } else {
+ log_invalid_cfg(instance->fmri);
+ update_state(instance, IIS_MAINTENANCE,
+ RERR_FAULT);
+ }
+ }
+ break;
+ }
+ break;
+
+ case IIS_OFFLINE_CONRATE:
+ switch (event) {
+ case RESTARTER_EVENT_TYPE_DISABLE:
+ /*
+ * The instance wants disabling. Take the instance
+ * offline as for the dependencies unmet event above,
+ * and then from there run the disable method to do
+ * the work to take the instance to the disabled state.
+ */
+ cancel_inst_timer(instance);
+ update_state(instance, IIS_OFFLINE, RERR_RESTART);
+ (void) run_method(instance, IM_DISABLE, NULL);
+ break;
+ case RESTARTER_EVENT_TYPE_ADMIN_MAINT_ON:
+ case RESTARTER_EVENT_TYPE_DEPENDENCY_CYCLE:
+ case RESTARTER_EVENT_TYPE_INVALID_DEPENDENCY:
+ /*
+ * The master restarter has requested the instance
+ * be taken to maintenance. Cancel the timer setup
+ * when we entered this state, and go directly to
+ * maintenance.
+ */
+ cancel_inst_timer(instance);
+ update_state(instance, IIS_MAINTENANCE, RERR_RESTART);
+ break;
+ }
+ break;
+
+ case IIS_OFFLINE_COPIES:
+ switch (event) {
+ case RESTARTER_EVENT_TYPE_DISABLE:
+ /*
+ * The instance wants disabling. Update the state
+ * to offline, and run the disable method to do the
+ * work to take it to the disabled state.
+ */
+ update_state(instance, IIS_OFFLINE, RERR_RESTART);
+ (void) run_method(instance, IM_DISABLE, NULL);
+ break;
+ case RESTARTER_EVENT_TYPE_ADMIN_MAINT_ON:
+ case RESTARTER_EVENT_TYPE_DEPENDENCY_CYCLE:
+ case RESTARTER_EVENT_TYPE_INVALID_DEPENDENCY:
+ /*
+ * The master restarter has requested the instance be
+ * placed in maintenance. Since it's already offline
+ * simply update the state.
+ */
+ update_state(instance, IIS_MAINTENANCE, RERR_RESTART);
+ break;
+ }
+ break;
+
+ default:
+ debug_msg("handle_restarter_event: instance in an "
+ "unexpected state");
+ assert(0);
+ }
+
+done:
+ if (send_ack)
+ ack_restarter_event(B_TRUE);
+}
+
+/*
+ * Tries to read and process an event from the event pipe. If there isn't one
+ * or an error occurred processing the event it returns -1. Else, if the event
+ * is for an instance we're not already managing we read its state, add it to
+ * our list to manage, and if appropriate read its configuration. Whether it's
+ * new to us or not, we then handle the specific event.
+ * Returns 0 if an event was read and processed successfully, else -1.
+ */
+static int
+process_restarter_event(void)
+{
+ char *fmri;
+ size_t fmri_size;
+ restarter_event_type_t event_type;
+ instance_t *instance;
+ restarter_event_t *event;
+ ssize_t sz;
+
+ debug_msg("Entering process_restarter_event");
+
+ /*
+ * Try to read an event pointer from the event pipe.
+ */
+ errno = 0;
+ switch (safe_read(rst_event_pipe[PE_CONSUMER], &event,
+ sizeof (event))) {
+ case 0:
+ break;
+ case 1:
+ if (errno == EAGAIN) /* no event to read */
+ return (-1);
+
+ /* other end of pipe closed */
+
+ /* FALLTHROUGH */
+ default: /* unexpected read error */
+ /*
+ * There's something wrong with the event pipe. Let's
+ * shutdown and be restarted.
+ */
+ inetd_stop();
+ return (-1);
+ }
+
+ /*
+ * Check if we're currently managing the instance which the event
+ * pertains to. If not, read its complete state and add it to our
+ * list to manage.
+ */
+
+ fmri_size = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH);
+ if ((fmri = malloc(fmri_size)) == NULL) {
+ error_msg(strerror(errno));
+ goto fail;
+ }
+ sz = restarter_event_get_instance(event, fmri, fmri_size);
+ if (sz >= fmri_size)
+ assert(0);
+
+ for (instance = uu_list_first(instance_list); instance != NULL;
+ instance = uu_list_next(instance_list, instance)) {
+ if (strcmp(instance->fmri, fmri) == 0)
+ break;
+ }
+
+ if (instance == NULL) {
+ int err;
+
+ debug_msg("New instance to manage: %s", fmri);
+
+ if (((instance = create_instance(fmri)) == NULL) ||
+ (retrieve_instance_state(instance) != 0) ||
+ (retrieve_method_pids(instance) != 0)) {
+ destroy_instance(instance);
+ free(fmri);
+ goto fail;
+ }
+
+ if (((err = iterate_repository_contracts(instance->fmri, 0))
+ != 0) && (err != ENOENT)) {
+ error_msg(gettext(
+ "Failed to adopt contracts of instance %s: %s"),
+ instance->fmri, strerror(err));
+ destroy_instance(instance);
+ free(fmri);
+ goto fail;
+ }
+
+ uu_list_node_init(instance, &instance->link, instance_pool);
+ (void) uu_list_insert_after(instance_list, NULL, instance);
+
+ /*
+ * Only read configuration for instances that aren't in any of
+ * the disabled, maintenance or uninitialized states, since
+ * they'll read it on state exit.
+ */
+ if ((instance->cur_istate != IIS_DISABLED) &&
+ (instance->cur_istate != IIS_MAINTENANCE) &&
+ (instance->cur_istate != IIS_UNINITIALIZED)) {
+ instance->config = read_instance_cfg(instance->fmri);
+ if (instance->config == NULL) {
+ log_invalid_cfg(instance->fmri);
+ update_state(instance, IIS_MAINTENANCE,
+ RERR_FAULT);
+ }
+ }
+ }
+
+ free(fmri);
+
+ event_type = restarter_event_get_type(event);
+ debug_msg("Event type: %d for instance: %s", event_type,
+ instance->fmri);
+
+ /*
+ * If the instance is currently running a method, don't process the
+ * event now, but attach it to the instance for processing when
+ * the instance finishes its transition.
+ */
+ if (INST_IN_TRANSITION(instance)) {
+ debug_msg("storing event %d for instance %s", event_type,
+ instance->fmri);
+ instance->pending_rst_event = event_type;
+ } else {
+ handle_restarter_event(instance, event_type, B_TRUE);
+ }
+
+ return (0);
+
+fail:
+ ack_restarter_event(B_FALSE);
+ return (-1);
+}
+
+/*
+ * Do the state machine processing associated with the termination of instance
+ * 'inst''s start method.
+ */
+void
+process_start_term(instance_t *inst)
+{
+ basic_cfg_t *cfg;
+
+ debug_msg("Entering process_start_term: inst: %s", inst->fmri);
+
+ inst->copies--;
+
+ if ((inst->cur_istate == IIS_MAINTENANCE) ||
+ (inst->cur_istate == IIS_DISABLED)) {
+ /* do any further processing/checks when we exit these states */
+ return;
+ }
+
+ cfg = inst->config->basic;
+
+ if (cfg->iswait) {
+ proto_info_t *pi;
+
+ switch (inst->cur_istate) {
+ case IIS_ONLINE:
+ case IIS_DEGRADED:
+ case IIS_IN_REFRESH_METHOD:
+ /*
+ * A wait type service's start method has exited.
+ * Check if the method was fired off in this inetd's
+ * lifetime, or a previous one; if the former,
+ * re-commence listening on the service's behalf; if
+ * the latter, mark the service offline and let bind
+ * attempts commence.
+ */
+ for (pi = uu_list_first(cfg->proto_list); pi != NULL;
+ pi = uu_list_next(cfg->proto_list, pi)) {
+ /*
+ * If a bound fd exists, the method was fired
+ * off during this inetd's lifetime.
+ */
+ if (pi->listen_fd != -1)
+ break;
+ }
+ if (pi != NULL) {
+ if (poll_bound_fds(inst, B_TRUE) != 0)
+ handle_bind_failure(inst);
+ } else {
+ update_state(inst, IIS_OFFLINE, RERR_RESTART);
+ create_bound_fds(inst);
+ }
+ }
+ } else {
+ /*
+ * Check if a nowait service should be brought back online
+ * after exceeding its copies limit.
+ */
+ if ((inst->cur_istate == IIS_OFFLINE_COPIES) &&
+ !copies_limit_exceeded(inst)) {
+ update_state(inst, IIS_OFFLINE, RERR_NONE);
+ process_offline_inst(inst);
+ }
+ }
+}
+
+/*
+ * If the instance has a pending event process it and initiate the
+ * acknowledgement.
+ */
+static void
+process_pending_rst_event(instance_t *inst)
+{
+ if (inst->pending_rst_event != RESTARTER_EVENT_TYPE_INVALID) {
+ restarter_event_type_t re;
+
+ debug_msg("Injecting pending event %d for instance %s",
+ inst->pending_rst_event, inst->fmri);
+ re = inst->pending_rst_event;
+ inst->pending_rst_event = RESTARTER_EVENT_TYPE_INVALID;
+ handle_restarter_event(inst, re, B_TRUE);
+ }
+}
+
+/*
+ * Do the state machine processing associated with the termination
+ * of the specified instance's non-start method with the specified status.
+ * Once the processing of the termination is done, the function also picks up
+ * any processing that was blocked on the method running.
+ */
+void
+process_non_start_term(instance_t *inst, int status)
+{
+ boolean_t ran_online_method = B_FALSE;
+
+ debug_msg("Entering process_non_start_term: inst: %s, method: %s",
+ inst->fmri, methods[states[inst->cur_istate].method_running].name);
+
+ if (status == IMRET_FAILURE) {
+ error_msg(gettext("The %s method of instance %s failed, "
+ "transitioning to maintenance"),
+ methods[states[inst->cur_istate].method_running].name,
+ inst->fmri);
+
+ if ((inst->cur_istate == IIS_IN_ONLINE_METHOD) ||
+ (inst->cur_istate == IIS_IN_REFRESH_METHOD))
+ destroy_bound_fds(inst);
+
+ update_state(inst, IIS_MAINTENANCE, RERR_FAULT);
+
+ inst->maintenance_req = B_FALSE;
+ inst->conn_rate_exceeded = B_FALSE;
+
+ if (inst->new_config != NULL) {
+ destroy_instance_cfg(inst->new_config);
+ inst->new_config = NULL;
+ }
+
+ if (!inetd_stopping)
+ process_pending_rst_event(inst);
+
+ return;
+ }
+
+ /* non-failure method return */
+
+ if (status != IMRET_SUCCESS) {
+ /*
+ * An instance method never returned a supported return code.
+ * We'll assume this means the method succeeded for now whilst
+ * non-GL-cognizant methods are used - eg. pkill.
+ */
+ debug_msg("The %s method of instance %s returned "
+ "non-compliant exit code: %d, assuming success",
+ methods[states[inst->cur_istate].method_running].name,
+ inst->fmri, status);
+ }
+
+ /*
+ * Update the state from the in-transition state.
+ */
+ switch (inst->cur_istate) {
+ case IIS_IN_ONLINE_METHOD:
+ ran_online_method = B_TRUE;
+ /* FALLTHROUGH */
+ case IIS_IN_REFRESH_METHOD:
+ /*
+ * If we've exhausted the bind retries, flag that by setting
+ * the instance's state to degraded.
+ */
+ if (inst->bind_retries_exceeded) {
+ update_state(inst, IIS_DEGRADED, RERR_NONE);
+ break;
+ }
+ /* FALLTHROUGH */
+ default:
+ update_state(inst,
+ methods[states[inst->cur_istate].method_running].dst_state,
+ RERR_NONE);
+ }
+
+ if (inst->cur_istate == IIS_OFFLINE) {
+ if (inst->new_config != NULL) {
+ /*
+ * This instance was found during refresh to need
+ * taking offline because its newly read configuration
+ * was sufficiently different. Now we're offline,
+ * activate this new configuration.
+ */
+ destroy_instance_cfg(inst->config);
+ inst->config = inst->new_config;
+ inst->new_config = NULL;
+ }
+
+ /* continue/complete any transitions that are in progress */
+ process_offline_inst(inst);
+
+ } else if (ran_online_method) {
+ /*
+ * We've just successfully executed the online method. We have
+ * a set of bound network fds that were created before running
+ * this method, so now we're online start listening for
+ * connections on them.
+ */
+ if (poll_bound_fds(inst, B_TRUE) != 0)
+ handle_bind_failure(inst);
+ }
+
+ /*
+ * If we're now out of transition (process_offline_inst() could have
+ * fired off another method), carry out any jobs that were blocked by
+ * us being in transition.
+ */
+ if (!INST_IN_TRANSITION(inst)) {
+ if (inetd_stopping) {
+ if (!instance_stopped(inst)) {
+ /*
+ * inetd is stopping, and this instance hasn't
+ * been stopped. Inject a stop event.
+ */
+ handle_restarter_event(inst,
+ RESTARTER_EVENT_TYPE_STOP, B_FALSE);
+ }
+ } else {
+ process_pending_rst_event(inst);
+ }
+ }
+}
+
+/*
+ * Check if configuration file specified is readable. If not return B_FALSE,
+ * else return B_TRUE.
+ */
+static boolean_t
+can_read_file(const char *path)
+{
+ int ret;
+ int serrno;
+
+ debug_msg("Entering can_read_file");
+ do {
+ ret = access(path, R_OK);
+ } while ((ret < 0) && (errno == EINTR));
+ if (ret < 0) {
+ if (errno != ENOENT) {
+ serrno = errno;
+ error_msg(gettext("Failed to access configuration "
+ "file %s for performing modification checks: %s"),
+ path, strerror(errno));
+ errno = serrno;
+ }
+ return (B_FALSE);
+ }
+ return (B_TRUE);
+}
+
+/*
+ * Check whether the configuration file has changed contents since inetd
+ * was last started/refreshed, and if so, log a message indicating that
+ * inetconv needs to be run.
+ */
+static void
+check_conf_file(void)
+{
+ char *new_hash;
+ char *old_hash = NULL;
+ scf_error_t ret;
+ const char *file;
+
+ debug_msg("Entering check_conf_file");
+
+ if (conf_file == NULL) {
+ /*
+ * No explicit config file specified, so see if one of the
+ * default two are readable, checking the primary one first
+ * followed by the secondary.
+ */
+ if (can_read_file(PRIMARY_DEFAULT_CONF_FILE)) {
+ file = PRIMARY_DEFAULT_CONF_FILE;
+ } else if ((errno == ENOENT) &&
+ can_read_file(SECONDARY_DEFAULT_CONF_FILE)) {
+ file = SECONDARY_DEFAULT_CONF_FILE;
+ } else {
+ return;
+ }
+ } else {
+ file = conf_file;
+ if (!can_read_file(file))
+ return;
+ }
+
+ if (calculate_hash(file, &new_hash) == 0) {
+ ret = retrieve_inetd_hash(&old_hash);
+ if (((ret == SCF_ERROR_NONE) &&
+ (strcmp(old_hash, new_hash) != 0))) {
+ /* modified config file */
+ warn_msg(gettext(
+ "Configuration file %s has been modified since "
+ "inetconv was last run. \"inetconv -i %s\" must be "
+ "run to apply any changes to the SMF"), file, file);
+ } else if ((ret != SCF_ERROR_NOT_FOUND) &&
+ (ret != SCF_ERROR_NONE)) {
+ /* No message if hash not yet computed */
+ error_msg(gettext("Failed to check whether "
+ "configuration file %s has been modified: %s"),
+ file, scf_strerror(ret));
+ }
+ free(old_hash);
+ free(new_hash);
+ } else {
+ error_msg(gettext("Failed to check whether configuration file "
+ "%s has been modified: %s"), file, strerror(errno));
+ }
+}
+
+/*
+ * Refresh all inetd's managed instances and check the configuration file
+ * for any updates since inetconv was last run, logging a message if there
+ * are. We call the SMF refresh function to refresh each instance so that
+ * the refresh request goes through the framework, and thus results in the
+ * running snapshot of each instance being updated from the configuration
+ * snapshot.
+ */
+static void
+inetd_refresh(void)
+{
+ instance_t *inst;
+
+ debug_msg("Entering inetd_refresh");
+
+ /* call libscf to send refresh requests for all managed instances */
+ for (inst = uu_list_first(instance_list); inst != NULL;
+ inst = uu_list_next(instance_list, inst)) {
+ if (smf_refresh_instance(inst->fmri) < 0) {
+ error_msg(gettext("Failed to refresh instance %s: %s"),
+ inst->fmri, scf_strerror(scf_error()));
+ }
+ }
+
+ /*
+ * Log a message if the configuration file has changed since inetconv
+ * was last run.
+ */
+ check_conf_file();
+}
+
+/*
+ * Initiate inetd's shutdown.
+ */
+static void
+inetd_stop(void)
+{
+ instance_t *inst;
+
+ debug_msg("Entering inetd_stop");
+
+ /* Block handling signals for stop and refresh */
+ (void) sighold(SIGHUP);
+ (void) sighold(SIGTERM);
+
+ /* Indicate inetd is coming down */
+ inetd_stopping = B_TRUE;
+
+ /* Stop polling on restarter events. */
+ clear_pollfd(rst_event_pipe[PE_CONSUMER]);
+
+ /* Stop polling for any more stop/refresh requests. */
+ clear_pollfd(uds_fd);
+
+ /*
+ * Send a stop event to all currently unstopped instances that
+ * aren't in transition. For those that are in transition, the
+ * event will get sent when the transition completes.
+ */
+ for (inst = uu_list_first(instance_list); inst != NULL;
+ inst = uu_list_next(instance_list, inst)) {
+ if (!instance_stopped(inst) && !INST_IN_TRANSITION(inst))
+ handle_restarter_event(inst,
+ RESTARTER_EVENT_TYPE_STOP, B_FALSE);
+ }
+}
+
+/*
+ * Sets up the intra-inetd-process Unix Domain Socket.
+ * Returns -1 on error, else 0.
+ */
+static int
+uds_init(void)
+{
+ struct sockaddr_un addr;
+
+ debug_msg("Entering uds_init");
+
+ if ((uds_fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
+ error_msg("socket: %s", strerror(errno));
+ return (-1);
+ }
+
+ disable_blocking(uds_fd);
+
+ (void) unlink(INETD_UDS_PATH); /* clean-up any stale files */
+
+ (void) memset(&addr, 0, sizeof (addr));
+ addr.sun_family = AF_UNIX;
+ /* CONSTCOND */
+ assert(sizeof (INETD_UDS_PATH) <= sizeof (addr.sun_path));
+ (void) strlcpy(addr.sun_path, INETD_UDS_PATH, sizeof (addr.sun_path));
+
+ if (bind(uds_fd, (struct sockaddr *)(&addr), sizeof (addr)) < 0) {
+ error_msg(gettext("Failed to bind socket to %s: %s"),
+ INETD_UDS_PATH, strerror(errno));
+ (void) close(uds_fd);
+ return (-1);
+ }
+
+ (void) listen(uds_fd, UDS_BACKLOG);
+
+ if ((set_pollfd(uds_fd, POLLIN)) == -1) {
+ (void) close(uds_fd);
+ (void) unlink(INETD_UDS_PATH);
+ return (-1);
+ }
+
+ return (0);
+}
+
+static void
+uds_fini(void)
+{
+ if (uds_fd != -1)
+ (void) close(uds_fd);
+ (void) unlink(INETD_UDS_PATH);
+}
+
+/*
+ * Handle an incoming request on the Unix Domain Socket. Returns -1 if there
+ * was an error handling the event, else 0.
+ */
+static int
+process_uds_event(void)
+{
+ uds_request_t req;
+ int fd;
+ struct sockaddr_un addr;
+ socklen_t len = sizeof (addr);
+ int ret;
+ uint_t retries = 0;
+
+ debug_msg("Entering process_uds_event");
+
+ do {
+ fd = accept(uds_fd, (struct sockaddr *)&addr, &len);
+ } while ((fd < 0) && (errno == EINTR));
+ if (fd < 0) {
+ if (errno != EWOULDBLOCK)
+ error_msg("accept failed: %s", strerror(errno));
+ return (-1);
+ }
+
+ for (retries = 0; retries < UDS_RECV_RETRIES; retries++) {
+ if (((ret = safe_read(fd, &req, sizeof (req))) != 1) ||
+ (errno != EAGAIN))
+ break;
+
+ (void) poll(NULL, 0, 100); /* 100ms pause */
+ }
+
+ if (ret != 0) {
+ error_msg(gettext("Failed read: %s"), strerror(errno));
+ (void) close(fd);
+ return (-1);
+ }
+
+ switch (req) {
+ case UR_REFRESH_INETD:
+ /* flag the request for event_loop() to process */
+ refresh_inetd_requested = B_TRUE;
+ (void) close(fd);
+ break;
+ case UR_STOP_INETD:
+ inetd_stop();
+ break;
+ default:
+ error_msg("unexpected UDS request");
+ (void) close(fd);
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * Perform checks for common exec string errors. We limit the checks to
+ * whether the file exists, is a regular file, and has at least one execute
+ * bit set. We leave the core security checks to exec() so as not to duplicate
+ * and thus incur the associated drawbacks, but hope to catch the common
+ * errors here.
+ */
+static boolean_t
+passes_basic_exec_checks(const char *instance, const char *method,
+ const char *path)
+{
+ struct stat sbuf;
+
+ debug_msg("Entering passes_basic_exec_checks");
+
+ /* check the file exists */
+ while (stat(path, &sbuf) == -1) {
+ if (errno != EINTR) {
+ error_msg(gettext(
+ "Can't stat the %s method of instance %s: %s"),
+ method, instance, strerror(errno));
+ return (B_FALSE);
+ }
+ }
+
+ /*
+ * Check if the file is a regular file and has at least one execute
+ * bit set.
+ */
+ if ((sbuf.st_mode & S_IFMT) != S_IFREG) {
+ error_msg(gettext(
+ "The %s method of instance %s isn't a regular file"),
+ method, instance);
+ return (B_FALSE);
+ } else if ((sbuf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0) {
+ error_msg(gettext("The %s method instance %s doesn't have "
+ "any execute permissions set"), method, instance);
+ return (B_FALSE);
+ }
+
+ return (B_TRUE);
+}
+
+static void
+exec_method(instance_t *instance, instance_method_t method, method_info_t *mi,
+ struct method_context *mthd_ctxt, const proto_info_t *pi)
+{
+ char **args;
+ char **env;
+ const char *errf;
+ int serrno;
+ basic_cfg_t *cfg = instance->config->basic;
+
+ if (method == IM_START) {
+ /*
+ * If wrappers checks fail, pretend the method was exec'd and
+ * failed.
+ */
+ if (!tcp_wrappers_ok(instance))
+ exit(IMRET_FAILURE);
+ }
+
+ /*
+ * Revert the disposition of handled signals and ignored signals to
+ * their defaults, unblocking any blocked ones as a side effect.
+ */
+ (void) sigset(SIGHUP, SIG_DFL);
+ (void) sigset(SIGTERM, SIG_DFL);
+ (void) sigset(SIGINT, SIG_DFL);
+
+ /*
+ * Setup exec arguments. Do this before the fd setup below, so our
+ * logging related file fd doesn't get taken over before we call
+ * expand_address().
+ */
+ if ((method == IM_START) &&
+ (strcmp(mi->exec_args_we.we_wordv[0], "%A") == 0)) {
+ args = expand_address(instance, pi);
+ } else {
+ args = mi->exec_args_we.we_wordv;
+ }
+
+ /* Generate audit trail for start operations */
+ if (method == IM_START) {
+ adt_event_data_t *ae;
+ struct sockaddr_storage ss;
+ priv_set_t *privset;
+ socklen_t sslen = sizeof (ss);
+
+ if ((ae = adt_alloc_event(audit_handle, ADT_inetd_connect))
+ == NULL) {
+ error_msg(gettext("Unable to allocate audit event for "
+ "the %s method of instance %s"),
+ methods[method].name, instance->fmri);
+ exit(IMRET_FAILURE);
+ }
+
+ /*
+ * The inetd_connect audit record consists of:
+ * Service name
+ * Execution path
+ * Remote address and port
+ * Local port
+ * Process privileges
+ */
+ ae->adt_inetd_connect.service_name = cfg->svc_name;
+ ae->adt_inetd_connect.cmd = mi->exec_path;
+
+ if (instance->remote_addr.ss_family == AF_INET) {
+ struct in_addr *in = SS_SINADDR(instance->remote_addr);
+ ae->adt_inetd_connect.ip_adr[0] = in->s_addr;
+ ae->adt_inetd_connect.ip_type = ADT_IPv4;
+ } else {
+ uint32_t *addr6;
+ int i;
+
+ ae->adt_inetd_connect.ip_type = ADT_IPv6;
+ addr6 = (uint32_t *)SS_SINADDR(instance->remote_addr);
+ for (i = 0; i < 4; ++i)
+ ae->adt_inetd_connect.ip_adr[i] = addr6[i];
+ }
+
+ ae->adt_inetd_connect.ip_remote_port =
+ ntohs(SS_PORT(instance->remote_addr));
+
+ if (getsockname(instance->conn_fd, (struct sockaddr *)&ss,
+ &sslen) == 0)
+ ae->adt_inetd_connect.ip_local_port =
+ ntohs(SS_PORT(ss));
+
+ privset = mthd_ctxt->priv_set;
+ if (privset == NULL) {
+ privset = priv_allocset();
+ if (privset != NULL &&
+ getppriv(PRIV_EFFECTIVE, privset) != 0) {
+ priv_freeset(privset);
+ privset = NULL;
+ }
+ }
+
+ ae->adt_inetd_connect.privileges = privset;
+
+ (void) adt_put_event(ae, ADT_SUCCESS, ADT_SUCCESS);
+ adt_free_event(ae);
+
+ if (privset != NULL && mthd_ctxt->priv_set == NULL)
+ priv_freeset(privset);
+ }
+
+ /*
+ * Set method context before the fd setup below so we can output an
+ * error message if it fails.
+ */
+ if ((errno = restarter_set_method_context(mthd_ctxt, &errf)) != 0) {
+ const char *msg;
+
+ if (errno == -1) {
+ if (strcmp(errf, "core_set_process_path") == 0) {
+ msg = gettext("Failed to set the corefile path "
+ "for the %s method of instance %s");
+ } else if (strcmp(errf, "setproject") == 0) {
+ msg = gettext("Failed to assign a resource "
+ "control for the %s method of instance %s");
+ } else if (strcmp(errf, "pool_set_binding") == 0) {
+ msg = gettext("Failed to bind the %s method of "
+ "instance %s to a pool due to a system "
+ "error");
+ } else {
+ assert(0);
+ abort();
+ }
+
+ error_msg(msg, methods[method].name, instance->fmri);
+
+ exit(IMRET_FAILURE);
+ }
+
+ if (errf != NULL && strcmp(errf, "pool_set_binding") == 0) {
+ switch (errno) {
+ case ENOENT:
+ msg = gettext("Failed to find resource pool "
+ "for the %s method of instance %s");
+ break;
+
+ case EBADF:
+ msg = gettext("Failed to bind the %s method of "
+ "instance %s to a pool due to invalid "
+ "configuration");
+ break;
+
+ default:
+ assert(0);
+ abort();
+ }
+
+ exit(IMRET_FAILURE);
+ }
+
+ if (errf != NULL) {
+ error_msg(gettext("Failed to set credentials for the "
+ "%s method of instance %s (%s: %s)"),
+ methods[method].name, instance->fmri, errf,
+ strerror(errno));
+ exit(IMRET_FAILURE);
+ }
+
+ switch (errno) {
+ case ENOMEM:
+ msg = gettext("Failed to set credentials for the %s "
+ "method of instance %s (out of memory)");
+ break;
+
+ case ENOENT:
+ msg = gettext("Failed to set credentials for the %s "
+ "method of instance %s (no passwd or shadow "
+ "entry for user)");
+ break;
+
+ default:
+ assert(0);
+ abort();
+ }
+
+ error_msg(msg, methods[method].name, instance->fmri);
+ exit(IMRET_FAILURE);
+ }
+
+ /* let exec() free mthd_ctxt */
+
+ /* setup standard fds */
+ if (method == IM_START) {
+ (void) dup2(instance->conn_fd, STDIN_FILENO);
+ } else {
+ (void) close(STDIN_FILENO);
+ (void) open("/dev/null", O_RDONLY);
+ }
+ (void) dup2(STDIN_FILENO, STDOUT_FILENO);
+ (void) dup2(STDIN_FILENO, STDERR_FILENO);
+
+ closefrom(STDERR_FILENO + 1);
+
+ method_preexec();
+
+ env = set_smf_env(mthd_ctxt, instance, methods[method].name);
+
+ if (env != NULL) {
+ do {
+ (void) execve(mi->exec_path, args, env);
+ } while (errno == EINTR);
+ }
+
+ serrno = errno;
+ /* start up logging again to report the error */
+ msg_init();
+ errno = serrno;
+
+ error_msg(
+ gettext("Failed to exec %s method of instance %s: %s"),
+ methods[method].name, instance->fmri, strerror(errno));
+
+ if ((method == IM_START) && (instance->config->basic->iswait)) {
+ /*
+ * We couldn't exec the start method for a wait type service.
+ * Eat up data from the endpoint, so that hopefully the
+ * service's fd won't wake poll up on the next time round
+ * event_loop(). This behavior is carried over from the old
+ * inetd, and it seems somewhat arbitrary that it isn't
+ * also done in the case of fork failures; but I guess
+ * it assumes an exec failure is less likely to be the result
+ * of a resource shortage, and is thus not worth retrying.
+ */
+ consume_wait_data(instance, 0);
+ }
+
+ exit(IMRET_FAILURE);
+}
+
+static restarter_error_t
+get_method_error_success(instance_method_t method)
+{
+ switch (method) {
+ case IM_OFFLINE:
+ return (RERR_RESTART);
+ case IM_ONLINE:
+ return (RERR_RESTART);
+ case IM_DISABLE:
+ return (RERR_RESTART);
+ case IM_REFRESH:
+ return (RERR_REFRESH);
+ case IM_START:
+ return (RERR_RESTART);
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * Runs the specified method of the specified service instance.
+ * If the method was never specified, we handle it the same as if the
+ * method was called and returned success, carrying on any transition the
+ * instance may be in the midst of.
+ * If the method isn't executable in its specified profile or an error occurs
+ * forking a process to run the method in the function returns -1.
+ * If a method binary is successfully executed, the function switches the
+ * instance's cur state to the method's associated 'run' state and the next
+ * state to the methods associated next state.
+ * Returns -1 if there's an error before forking, else 0.
+ */
+int
+run_method(instance_t *instance, instance_method_t method,
+ const proto_info_t *start_info)
+{
+ pid_t child_pid;
+ method_info_t *mi;
+ struct method_context *mthd_ctxt = NULL;
+ const char *errstr;
+ int sig;
+ int ret;
+ instance_cfg_t *cfg = instance->config;
+ ctid_t cid;
+ boolean_t trans_failure = B_TRUE;
+ int serrno;
+
+ debug_msg("Entering run_method, instance: %s, method: %s",
+ instance->fmri, methods[method].name);
+
+ /*
+ * Don't bother updating the instance's state for the start method
+ * as there isn't a separate start method state.
+ */
+ if (method != IM_START)
+ update_instance_states(instance, get_method_state(method),
+ methods[method].dst_state,
+ get_method_error_success(method));
+
+ if ((mi = cfg->methods[method]) == NULL) {
+ /*
+ * An unspecified method. Since the absence of this method
+ * must be valid (otherwise it would have been caught
+ * during configuration validation), simply pretend the method
+ * ran and returned success.
+ */
+ process_non_start_term(instance, IMRET_SUCCESS);
+ return (0);
+ }
+
+ /* Handle special method tokens, not allowed on start */
+ if (method != IM_START) {
+ if (restarter_is_null_method(mi->exec_path)) {
+ /* :true means nothing should be done */
+ process_non_start_term(instance, IMRET_SUCCESS);
+ return (0);
+ }
+
+ if ((sig = restarter_is_kill_method(mi->exec_path)) >= 0) {
+ /* Carry out contract assassination */
+ ret = iterate_repository_contracts(instance->fmri, sig);
+ /* ENOENT means we didn't find any contracts */
+ if (ret != 0 && ret != ENOENT) {
+ error_msg(gettext("Failed to send signal %d "
+ "to contracts of instance %s: %s"), sig,
+ instance->fmri, strerror(ret));
+ goto prefork_failure;
+ } else {
+ process_non_start_term(instance, IMRET_SUCCESS);
+ return (0);
+ }
+ }
+
+ if ((sig = restarter_is_kill_proc_method(mi->exec_path)) >= 0) {
+ /* Carry out process assassination */
+ rep_val_t *rv;
+
+ ret = IMRET_SUCCESS;
+ for (rv = uu_list_first(instance->start_pids);
+ rv != NULL;
+ rv = uu_list_next(instance->start_pids, rv)) {
+ if ((kill((pid_t)rv->val, sig) != 0) &&
+ (errno != ESRCH)) {
+ ret = IMRET_FAILURE;
+ error_msg(gettext("Unable to signal "
+ "start process of instance %s: %s"),
+ instance->fmri, strerror(errno));
+ }
+ }
+
+ process_non_start_term(instance, ret);
+ return (0);
+ }
+ }
+
+ /*
+ * Get the associated method context before the fork so we can
+ * modify the instances state if things go wrong.
+ */
+ if ((mthd_ctxt = read_method_context(instance->fmri,
+ methods[method].name, mi->exec_path, &errstr)) == NULL) {
+ error_msg(gettext("Failed to retrieve method context for the "
+ "%s method of instance %s: %s"), methods[method].name,
+ instance->fmri, errstr);
+ goto prefork_failure;
+ }
+
+ /*
+ * Perform some basic checks before we fork to limit the possibility
+ * of exec failures, so we can modify the instance state if necessary.
+ */
+ if (!passes_basic_exec_checks(instance->fmri, methods[method].name,
+ mi->exec_path)) {
+ trans_failure = B_FALSE;
+ goto prefork_failure;
+ }
+
+ if (contract_prefork() == -1)
+ goto prefork_failure;
+ child_pid = fork();
+ serrno = errno;
+ contract_postfork();
+
+ switch (child_pid) {
+ case -1:
+ error_msg(gettext(
+ "Unable to fork %s method of instance %s: %s"),
+ methods[method].name, instance->fmri, strerror(serrno));
+ if ((serrno != EAGAIN) && (serrno != ENOMEM))
+ trans_failure = B_FALSE;
+ goto prefork_failure;
+ case 0: /* child */
+ exec_method(instance, method, mi, mthd_ctxt, start_info);
+ break;
+ default: /* parent */
+ restarter_free_method_context(mthd_ctxt);
+ mthd_ctxt = NULL;
+
+ if (get_latest_contract(&cid) < 0)
+ cid = -1;
+
+ /*
+ * Register this method so its termination is noticed and
+ * the state transition this method participates in is
+ * continued.
+ */
+ if (register_method(instance, child_pid, cid, method) != 0) {
+ /*
+ * Since we will never find out about the termination
+ * of this method, if it's a non-start method treat
+ * is as a failure so we don't block restarter event
+ * processing on it whilst it languishes in a method
+ * running state.
+ */
+ error_msg(gettext("Failed to monitor status of "
+ "%s method of instance %s"), methods[method].name,
+ instance->fmri);
+ if (method != IM_START)
+ process_non_start_term(instance, IMRET_FAILURE);
+ }
+
+ add_method_ids(instance, child_pid, cid, method);
+
+ /* do tcp tracing for those nowait instances that request it */
+ if ((method == IM_START) && cfg->basic->do_tcp_trace &&
+ !cfg->basic->iswait) {
+ char buf[INET6_ADDRSTRLEN];
+
+ syslog(LOG_NOTICE, "%s[%d] from %s %d",
+ cfg->basic->svc_name, child_pid,
+ inet_ntop_native(instance->remote_addr.ss_family,
+ SS_SINADDR(instance->remote_addr), buf,
+ sizeof (buf)),
+ ntohs(SS_PORT(instance->remote_addr)));
+ }
+ }
+
+ return (0);
+
+prefork_failure:
+ if (mthd_ctxt != NULL) {
+ restarter_free_method_context(mthd_ctxt);
+ mthd_ctxt = NULL;
+ }
+
+ if (method == IM_START) {
+ /*
+ * Only place a start method in maintenance if we're sure
+ * that the failure was non-transient.
+ */
+ if (!trans_failure) {
+ destroy_bound_fds(instance);
+ update_state(instance, IIS_MAINTENANCE, RERR_FAULT);
+ }
+ } else {
+ /* treat the failure as if the method ran and failed */
+ process_non_start_term(instance, IMRET_FAILURE);
+ }
+
+ return (-1);
+}
+
+static int
+accept_connection(instance_t *instance, proto_info_t *pi)
+{
+ int fd;
+ socklen_t size;
+
+ debug_msg("Entering accept_connection");
+
+ if (instance->config->basic->istlx) {
+ fd = tlx_accept(instance->fmri, (tlx_info_t *)pi,
+ &(instance->remote_addr));
+ } else {
+ size = sizeof (instance->remote_addr);
+ fd = accept(pi->listen_fd,
+ (struct sockaddr *)&(instance->remote_addr), &size);
+ if (fd < 0)
+ error_msg("accept: %s", strerror(errno));
+ }
+
+ return (fd);
+}
+
+/*
+ * Handle an incoming connection request for a nowait service.
+ * This involves accepting the incoming connection on a new fd. Connection
+ * rate checks are then performed, transitioning the service to the
+ * conrate offline state if these fail. Otherwise, the service's start method
+ * is run (performing TCP wrappers checks if applicable as we do), and on
+ * success concurrent copies checking is done, transitioning the service to the
+ * copies offline state if this fails.
+ */
+static void
+process_nowait_request(instance_t *instance, proto_info_t *pi)
+{
+ basic_cfg_t *cfg = instance->config->basic;
+ int ret;
+ adt_event_data_t *ae;
+ char buf[BUFSIZ];
+
+ debug_msg("Entering process_nowait_req");
+
+ /* accept nowait service connections on a new fd */
+ if ((instance->conn_fd = accept_connection(instance, pi)) == -1) {
+ /*
+ * Failed accept. Return and allow the event loop to initiate
+ * another attempt later if the request is still present.
+ */
+ return;
+ }
+
+ /*
+ * Limit connection rate of nowait services. If either conn_rate_max
+ * or conn_rate_offline are <= 0, no connection rate limit checking
+ * is done. If the configured rate is exceeded, the instance is taken
+ * to the connrate_offline state and a timer scheduled to try and
+ * bring the instance back online after the configured offline time.
+ */
+ if ((cfg->conn_rate_max > 0) && (cfg->conn_rate_offline > 0)) {
+ if (instance->conn_rate_count++ == 0) {
+ instance->conn_rate_start = time(NULL);
+ } else if (instance->conn_rate_count >
+ cfg->conn_rate_max) {
+ time_t now = time(NULL);
+
+ if ((now - instance->conn_rate_start) > 1) {
+ instance->conn_rate_start = now;
+ instance->conn_rate_count = 1;
+ } else {
+ /* Generate audit record */
+ if ((ae = adt_alloc_event(audit_handle,
+ ADT_inetd_ratelimit)) == NULL) {
+ error_msg(gettext("Unable to allocate "
+ "rate limit audit event"));
+ } else {
+ adt_inetd_ratelimit_t *rl =
+ &ae->adt_inetd_ratelimit;
+ /*
+ * The inetd_ratelimit audit
+ * record consists of:
+ * Service name
+ * Connection rate limit
+ */
+ rl->service_name = cfg->svc_name;
+ (void) snprintf(buf, sizeof (buf),
+ "limit=%lld", cfg->conn_rate_max);
+ rl->limit = buf;
+ (void) adt_put_event(ae, ADT_SUCCESS,
+ ADT_SUCCESS);
+ adt_free_event(ae);
+ }
+
+ error_msg(gettext(
+ "Instance %s has exceeded its configured "
+ "connection rate, additional connections "
+ "will not be accepted for %d seconds"),
+ instance->fmri, cfg->conn_rate_offline);
+
+ close_net_fd(instance, instance->conn_fd);
+ instance->conn_fd = -1;
+
+ destroy_bound_fds(instance);
+
+ instance->conn_rate_count = 0;
+
+ instance->conn_rate_exceeded = B_TRUE;
+ (void) run_method(instance, IM_OFFLINE, NULL);
+
+ return;
+ }
+ }
+ }
+
+ ret = run_method(instance, IM_START, pi);
+
+ close_net_fd(instance, instance->conn_fd);
+ instance->conn_fd = -1;
+
+ if (ret == -1) /* the method wasn't forked */
+ return;
+
+ instance->copies++;
+
+ /*
+ * Limit concurrent connections of nowait services.
+ */
+ if (copies_limit_exceeded(instance)) {
+ /* Generate audit record */
+ if ((ae = adt_alloc_event(audit_handle, ADT_inetd_copylimit))
+ == NULL) {
+ error_msg(gettext("Unable to allocate copy limit "
+ "audit event"));
+ } else {
+ /*
+ * The inetd_copylimit audit record consists of:
+ * Service name
+ * Copy limit
+ */
+ ae->adt_inetd_copylimit.service_name = cfg->svc_name;
+ (void) snprintf(buf, sizeof (buf), "limit=%lld",
+ cfg->max_copies);
+ ae->adt_inetd_copylimit.limit = buf;
+ (void) adt_put_event(ae, ADT_SUCCESS, ADT_SUCCESS);
+ adt_free_event(ae);
+ }
+
+ warn_msg(gettext("Instance %s has reached its maximum "
+ "configured copies, no new connections will be accepted"),
+ instance->fmri);
+ destroy_bound_fds(instance);
+ (void) run_method(instance, IM_OFFLINE, NULL);
+ }
+}
+
+/*
+ * Handle an incoming request for a wait type service.
+ * Failure rate checking is done first, taking the service to the maintenance
+ * state if the checks fail. Following this, the service's start method is run,
+ * and on success, we stop listening for new requests for this service.
+ */
+static void
+process_wait_request(instance_t *instance, const proto_info_t *pi)
+{
+ basic_cfg_t *cfg = instance->config->basic;
+ int ret;
+ adt_event_data_t *ae;
+ char buf[BUFSIZ];
+
+ debug_msg("Entering process_wait_request");
+
+ instance->conn_fd = pi->listen_fd;
+
+ /*
+ * Detect broken servers and transition them to maintenance. If a
+ * wait type service exits without accepting the connection or
+ * consuming (reading) the datagram, that service's descriptor will
+ * select readable again, and inetd will fork another instance of
+ * the server. If either wait_fail_cnt or wait_fail_interval are <= 0,
+ * no failure rate detection is done.
+ */
+ if ((cfg->wait_fail_cnt > 0) && (cfg->wait_fail_interval > 0)) {
+ if (instance->fail_rate_count++ == 0) {
+ instance->fail_rate_start = time(NULL);
+ } else if (instance->fail_rate_count > cfg->wait_fail_cnt) {
+ time_t now = time(NULL);
+
+ if ((now - instance->fail_rate_start) >
+ cfg->wait_fail_interval) {
+ instance->fail_rate_start = now;
+ instance->fail_rate_count = 1;
+ } else {
+ /* Generate audit record */
+ if ((ae = adt_alloc_event(audit_handle,
+ ADT_inetd_failrate)) == NULL) {
+ error_msg(gettext("Unable to allocate "
+ "failure rate audit event"));
+ } else {
+ adt_inetd_failrate_t *fr =
+ &ae->adt_inetd_failrate;
+ /*
+ * The inetd_failrate audit record
+ * consists of:
+ * Service name
+ * Failure rate
+ * Interval
+ * Last two are expressed as k=v pairs
+ * in the values field.
+ */
+ fr->service_name = cfg->svc_name;
+ (void) snprintf(buf, sizeof (buf),
+ "limit=%lld,interval=%d",
+ cfg->wait_fail_cnt,
+ cfg->wait_fail_interval);
+ fr->values = buf;
+ (void) adt_put_event(ae, ADT_SUCCESS,
+ ADT_SUCCESS);
+ adt_free_event(ae);
+ }
+
+ error_msg(gettext(
+ "Instance %s has exceeded its configured "
+ "failure rate, transitioning to "
+ "maintenance"), instance->fmri);
+ instance->fail_rate_count = 0;
+
+ destroy_bound_fds(instance);
+
+ instance->maintenance_req = B_TRUE;
+ (void) run_method(instance, IM_OFFLINE, NULL);
+ return;
+ }
+ }
+ }
+
+ ret = run_method(instance, IM_START, pi);
+
+ instance->conn_fd = -1;
+
+ if (ret == 0) {
+ /*
+ * Stop listening for connections now we've fired off the
+ * server for a wait type instance.
+ */
+ (void) poll_bound_fds(instance, B_FALSE);
+ }
+}
+
+/*
+ * Process any networks requests for each proto for each instance.
+ */
+void
+process_network_events(void)
+{
+ instance_t *instance;
+
+ debug_msg("Entering process_network_events");
+
+ for (instance = uu_list_first(instance_list); instance != NULL;
+ instance = uu_list_next(instance_list, instance)) {
+ basic_cfg_t *cfg;
+ proto_info_t *pi;
+
+ /*
+ * Ignore instances in states that definitely don't have any
+ * listening fds.
+ */
+ switch (instance->cur_istate) {
+ case IIS_ONLINE:
+ case IIS_DEGRADED:
+ case IIS_IN_REFRESH_METHOD:
+ break;
+ default:
+ continue;
+ }
+
+ cfg = instance->config->basic;
+
+ for (pi = uu_list_first(cfg->proto_list); pi != NULL;
+ pi = uu_list_next(cfg->proto_list, pi)) {
+ if ((pi->listen_fd != -1) &&
+ isset_pollfd(pi->listen_fd)) {
+ if (cfg->iswait) {
+ process_wait_request(instance, pi);
+ } else {
+ process_nowait_request(instance, pi);
+ }
+ }
+ }
+ }
+}
+
+/* ARGSUSED0 */
+static void
+sigterm_handler(int sig)
+{
+ debug_msg("Entering sigterm_handler");
+
+ got_sigterm = B_TRUE;
+}
+
+/* ARGSUSED0 */
+static void
+sighup_handler(int sig)
+{
+ debug_msg("Entering sighup_handler");
+
+ refresh_inetd_requested = B_TRUE;
+}
+
+/*
+ * inetd's major work loop. This function sits in poll waiting for events
+ * to occur, processing them when they do. The possible events are
+ * master restarter requests, expired timer queue timers, stop/refresh signal
+ * requests, contract events indicating process termination, stop/refresh
+ * requests originating from one of the stop/refresh inetd processes and
+ * network events.
+ * The loop is exited when a stop request is received and processed, and
+ * all the instances have reached a suitable 'stopping' state.
+ */
+static void
+event_loop(void)
+{
+ instance_t *instance;
+ int timeout;
+
+ debug_msg("Entering event_loop");
+
+ for (;;) {
+ int pret = -1;
+
+ timeout = iu_earliest_timer(timer_queue);
+
+ debug_msg("Doing signal check/poll");
+ if (!got_sigterm && !refresh_inetd_requested) {
+ pret = poll(poll_fds, num_pollfds, timeout);
+ if ((pret == -1) && (errno != EINTR)) {
+ error_msg(gettext("poll failure: %s"),
+ strerror(errno));
+ continue;
+ }
+ debug_msg("Exiting poll, returned: %d", pret);
+ }
+
+ if (got_sigterm) {
+ msg_fini();
+ inetd_stop();
+ got_sigterm = B_FALSE;
+ goto check_if_stopped;
+ }
+
+ /*
+ * Process any stop/refresh requests from the Unix Domain
+ * Socket.
+ */
+ if ((pret != -1) && isset_pollfd(uds_fd)) {
+ while (process_uds_event() == 0)
+ ;
+ }
+
+ /*
+ * Process refresh request. We do this check after the UDS
+ * event check above, as it would be wasted processing if we
+ * started refreshing inetd based on a SIGHUP, and then were
+ * told to shut-down via a UDS event.
+ */
+ if (refresh_inetd_requested) {
+ refresh_inetd_requested = B_FALSE;
+ if (!inetd_stopping)
+ inetd_refresh();
+ }
+
+ /*
+ * We were interrupted by a signal. Don't waste any more
+ * time processing a potentially inaccurate poll return.
+ */
+ if (pret == -1)
+ continue;
+
+ /*
+ * Process any instance restarter events.
+ */
+ if (isset_pollfd(rst_event_pipe[PE_CONSUMER])) {
+ while (process_restarter_event() == 0)
+ ;
+ }
+
+ /*
+ * Process any expired timers (bind retry, con-rate offline,
+ * method timeouts).
+ */
+ (void) iu_expire_timers(timer_queue);
+
+ process_terminated_methods();
+
+ /*
+ * If inetd is stopping, check whether all our managed
+ * instances have been stopped and we can return.
+ */
+ if (inetd_stopping) {
+check_if_stopped:
+ for (instance = uu_list_first(instance_list);
+ instance != NULL;
+ instance = uu_list_next(instance_list, instance)) {
+ if (!instance_stopped(instance)) {
+ debug_msg("%s not yet stopped",
+ instance->fmri);
+ break;
+ }
+ }
+ /* if all instances are stopped, return */
+ if (instance == NULL)
+ return;
+ }
+
+ process_network_events();
+ }
+}
+
+static void
+fini(void)
+{
+ debug_msg("Entering fini");
+
+ method_fini();
+ uds_fini();
+ if (timer_queue != NULL)
+ iu_tq_destroy(timer_queue);
+ if (rst_event_handle != NULL)
+ restarter_unbind_handle(rst_event_handle);
+
+ /*
+ * We don't explicitly close the event pipe as restarter_event_proxy()
+ * doesn't anticipate the pipe being closed; in the case it was trying
+ * to write to it it would get a SIGPIPE and result in an ungraceful
+ * shutdown, and in the case it was trying to read from it, safe_read()
+ * would return, and it would have to block itself in some way until the
+ * process exited. Not closing this end of the pipe prevents the first
+ * problem from occurring, and allows the thread in
+ * restarter_event_proxy() to block on the read till the process exits.
+ */
+
+ if (instance_list != NULL) {
+ void *cookie = NULL;
+ instance_t *inst;
+
+ while ((inst = uu_list_teardown(instance_list, &cookie)) !=
+ NULL)
+ destroy_instance(inst);
+ uu_list_destroy(instance_list);
+ }
+ if (instance_pool != NULL)
+ uu_list_pool_destroy(instance_pool);
+ tlx_fini();
+ config_fini();
+ repval_fini();
+ poll_fini();
+
+ /* Close audit session */
+ (void) adt_end_session(audit_handle);
+}
+
+static int
+init(void)
+{
+ int err;
+
+ debug_msg("Entering init");
+
+ if (repval_init() < 0)
+ goto failed;
+
+ if (config_init() < 0)
+ goto failed;
+
+ if (tlx_init() < 0)
+ goto failed;
+
+ /* Setup instance list. */
+ if ((instance_pool = uu_list_pool_create("instance_pool",
+ sizeof (instance_t), offsetof(instance_t, link), NULL,
+ UU_LIST_POOL_DEBUG)) == NULL) {
+ error_msg("%s: %s",
+ gettext("Failed to create instance pool"),
+ uu_strerror(uu_error()));
+ goto failed;
+ }
+ if ((instance_list = uu_list_create(instance_pool, NULL, 0)) == NULL) {
+ error_msg("%s: %s",
+ gettext("Failed to create instance list"),
+ uu_strerror(uu_error()));
+ goto failed;
+ }
+
+ /*
+ * Create event pipe to communicate events with the main event
+ * loop and add it to the event loop's fdset.
+ */
+ if (pipe(rst_event_pipe) < 0) {
+ error_msg("pipe: %s", strerror(errno));
+ goto failed;
+ }
+ /*
+ * We only leave the producer end to block on reads/writes as we
+ * can't afford to block in the main thread, yet need to in
+ * the restarter event thread, so it can sit and wait for an
+ * acknowledgement to be written to the pipe.
+ */
+ disable_blocking(rst_event_pipe[PE_CONSUMER]);
+ if ((set_pollfd(rst_event_pipe[PE_CONSUMER], POLLIN)) == -1)
+ goto failed;
+
+ /*
+ * Register with master restarter for managed service events. This
+ * will fail, amongst other reasons, if inetd is already running.
+ */
+ if ((err = restarter_bind_handle(RESTARTER_EVENT_VERSION,
+ INETD_INSTANCE_FMRI, restarter_event_proxy, 0,
+ &rst_event_handle)) != 0) {
+ error_msg(gettext(
+ "Failed to register for restarter events: %s"),
+ strerror(err));
+ goto failed;
+ }
+
+ if (contract_init() < 0)
+ goto failed;
+
+ if ((timer_queue = iu_tq_create()) == NULL) {
+ error_msg(gettext("Failed to create timer queue."));
+ goto failed;
+ }
+
+ if (uds_init() < 0)
+ goto failed;
+
+ if (method_init() < 0)
+ goto failed;
+
+ /* Initialize auditing session */
+ if (adt_start_session(&audit_handle, NULL, ADT_USE_PROC_DATA) != 0) {
+ error_msg(gettext("Unable to start audit session"));
+ }
+
+ /*
+ * Initialize signal dispositions/masks
+ */
+ (void) sigset(SIGHUP, sighup_handler);
+ (void) sigset(SIGTERM, sigterm_handler);
+ (void) sigignore(SIGINT);
+
+ return (0);
+
+failed:
+ fini();
+ return (-1);
+}
+
+static int
+start_method(void)
+{
+ int i;
+ int pipe_fds[2];
+ int child;
+
+ debug_msg("ENTERING START_METHOD:");
+
+ /* Create pipe for child to notify parent of initialization success. */
+ if (pipe(pipe_fds) < 0) {
+ debug_msg("pipe: %s", strerror(errno));
+ return (SMF_EXIT_ERR_OTHER);
+ }
+
+ if ((child = fork()) == -1) {
+ debug_msg("fork: %s", strerror(errno));
+ (void) close(pipe_fds[PE_CONSUMER]);
+ (void) close(pipe_fds[PE_PRODUCER]);
+ return (SMF_EXIT_ERR_OTHER);
+ } else if (child > 0) { /* parent */
+
+ /* Wait on child to return success of initialization. */
+ (void) close(pipe_fds[PE_PRODUCER]);
+ if ((safe_read(pipe_fds[PE_CONSUMER], &i, sizeof (i)) != 0) ||
+ (i < 0)) {
+ error_msg(gettext(
+ "Initialization failed, unable to start"));
+ (void) close(pipe_fds[PE_CONSUMER]);
+ /*
+ * Batch all initialization errors as 'other' errors,
+ * resulting in retries being attempted.
+ */
+ return (SMF_EXIT_ERR_OTHER);
+ } else {
+ (void) close(pipe_fds[PE_CONSUMER]);
+ return (SMF_EXIT_OK);
+ }
+ } else { /* child */
+ /*
+ * Perform initialization and return success code down
+ * the pipe.
+ */
+ (void) close(pipe_fds[PE_CONSUMER]);
+ i = init();
+ if ((safe_write(pipe_fds[PE_PRODUCER], &i, sizeof (i)) < 0) ||
+ (i < 0)) {
+ error_msg(gettext("pipe write failure: %s"),
+ strerror(errno));
+ exit(1);
+ }
+ (void) close(pipe_fds[PE_PRODUCER]);
+
+ (void) setsid();
+
+ /*
+ * Log a message if the configuration file has changed since
+ * inetconv was last run.
+ */
+ check_conf_file();
+
+ event_loop();
+
+ fini();
+ debug_msg("inetd stopped");
+ msg_fini();
+ exit(0);
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * When inetd is run from outside the SMF, this message is output to provide
+ * the person invoking inetd with further information that will help them
+ * understand how to start and stop inetd, and to achieve the other
+ * behaviors achievable with the legacy inetd command line interface, if
+ * it is possible.
+ */
+static void
+legacy_usage(void)
+{
+ (void) fprintf(stderr,
+ "inetd is now an smf(5) managed service and can no longer be run "
+ "from the\n"
+ "command line. To enable or disable inetd refer to svcadm(1M) on\n"
+ "how to enable \"%s\", the inetd instance.\n"
+ "\n"
+ "The traditional inetd command line option mappings are:\n"
+ "\t-d : there is no supported debug output\n"
+ "\t-s : inetd is only runnable from within the SMF\n"
+ "\t-t : See inetadm(1M) on how to enable TCP tracing\n"
+ "\t-r : See inetadm(1M) on how to set a failure rate\n"
+ "\n"
+ "To specify an alternative configuration file see svccfg(1M)\n"
+ "for how to modify the \"%s/%s\" string type property of\n"
+ "the inetd instance, and modify it according to the syntax:\n"
+ "\"%s [alt_config_file] %%m\".\n"
+ "\n"
+ "For further information on inetd see inetd(1M).\n",
+ INETD_INSTANCE_FMRI, START_METHOD_ARG, SCF_PROPERTY_EXEC,
+ INETD_PATH);
+}
+
+/*
+ * Usage message printed out for usage errors when running under the SMF.
+ */
+static void
+smf_usage(const char *arg0)
+{
+ error_msg("Usage: %s [alt_conf_file] %s|%s|%s", arg0, START_METHOD_ARG,
+ STOP_METHOD_ARG, REFRESH_METHOD_ARG);
+}
+
+/*
+ * Returns B_TRUE if we're being run from within the SMF, else B_FALSE.
+ */
+static boolean_t
+run_through_smf(void)
+{
+ char *fmri;
+
+ /*
+ * check if the instance fmri environment variable has been set by
+ * our restarter.
+ */
+ return (((fmri = getenv("SMF_FMRI")) != NULL) &&
+ (strcmp(fmri, INETD_INSTANCE_FMRI) == 0));
+}
+
+int
+main(int argc, char *argv[])
+{
+ char *method;
+ int ret;
+
+#if !defined(TEXT_DOMAIN)
+#define TEXT_DOMAIN "SYS_TEST"
+#endif
+ (void) textdomain(TEXT_DOMAIN);
+ (void) setlocale(LC_ALL, "");
+
+ if (!run_through_smf()) {
+ legacy_usage();
+ return (SMF_EXIT_ERR_NOSMF);
+ }
+
+ msg_init(); /* setup logging */
+
+ /* inetd invocation syntax is inetd [alt_conf_file] method_name */
+
+ switch (argc) {
+ case 2:
+ method = argv[1];
+ break;
+ case 3:
+ conf_file = argv[1];
+ method = argv[2];
+ break;
+ default:
+ smf_usage(argv[0]);
+ return (SMF_EXIT_ERR_CONFIG);
+
+ }
+
+ if (strcmp(method, START_METHOD_ARG) == 0) {
+ ret = start_method();
+ } else if (strcmp(method, STOP_METHOD_ARG) == 0) {
+ ret = stop_method();
+ } else if (strcmp(method, REFRESH_METHOD_ARG) == 0) {
+ ret = refresh_method();
+ } else {
+ smf_usage(argv[0]);
+ return (SMF_EXIT_ERR_CONFIG);
+ }
+
+ return (ret);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/inetd/inetd.xml b/usr/src/cmd/cmd-inet/usr.lib/inetd/inetd.xml
new file mode 100644
index 0000000000..68bcf1570f
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/inetd/inetd.xml
@@ -0,0 +1,170 @@
+<?xml version="1.0"?>
+<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
+<!--
+ Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ ident "%Z%%M% %I% %E% SMI"
+
+ NOTE: This service manifest is not editable; its contents will
+ be overwritten by package or patch operations, including
+ operating system upgrade. Make customizations in a different
+ file.
+
+ Service manifest for the inetd delegated restarter.
+-->
+
+<service_bundle type='manifest' name='SUNWcsr:inetd'>
+
+<service
+ name='network/inetd'
+ type='service'
+ version='1'>
+
+ <create_default_instance enabled='false' />
+
+ <single_instance />
+
+ <dependency
+ name='loopback'
+ grouping='require_any'
+ restart_on='error'
+ type='service'>
+ <service_fmri value='svc:/network/loopback' />
+ </dependency>
+
+ <dependency
+ name='filesystem'
+ grouping='require_all'
+ restart_on='error'
+ type='service'>
+ <service_fmri value='svc:/system/filesystem/local'/>
+ </dependency>
+
+ <dependency
+ name='network'
+ grouping='optional_all'
+ restart_on='error'
+ type='service'>
+ <service_fmri value='svc:/milestone/network' />
+ </dependency>
+
+ <dependency
+ name='rpc'
+ grouping='optional_all'
+ restart_on='error'
+ type='service'>
+ <service_fmri value='svc:/network/rpc/bind' />
+ </dependency>
+
+ <!--
+ Ensure that upgrade has the chance to run before
+ the service to avoid gratuitous complaints about
+ inetd.conf having been modified.
+ -->
+ <dependency
+ name='upgrade'
+ grouping='optional_all'
+ restart_on='none'
+ type='service'>
+ <service_fmri value='svc:/network/inetd-upgrade' />
+ </dependency>
+
+ <dependency
+ name='milestones'
+ grouping='require_all'
+ restart_on='none'
+ type='service'>
+ <service_fmri value='svc:/milestone/sysconfig' />
+ <service_fmri value='svc:/milestone/name-services' />
+ </dependency>
+
+ <dependent
+ name='inetd_multi-user'
+ grouping='optional_all'
+ restart_on='none'>
+ <service_fmri value='svc:/milestone/multi-user' />
+ </dependent>
+
+ <exec_method
+ type='method'
+ name='stop'
+ exec='/usr/lib/inet/inetd %m'
+ timeout_seconds='60' >
+ </exec_method>
+
+ <exec_method
+ type='method'
+ name='start'
+ exec='/usr/lib/inet/inetd %m'
+ timeout_seconds='60' >
+ </exec_method>
+
+ <exec_method
+ type='method'
+ name='refresh'
+ exec='/usr/lib/inet/inetd %m'
+ timeout_seconds='60' >
+ </exec_method>
+
+ <!--
+ inetd's managed service property defaults. Values chosen to
+ provide legacy inetd's behavior.
+ -->
+ <property_group
+ name='defaults'
+ type='application'>
+ <stability value='Evolving' />
+ <propval name='max_con_rate' type='integer' value='-1' />
+ <propval name='con_rate_offline' type='integer' value='-1' />
+ <propval name='max_copies' type='integer' value='-1' />
+ <propval name='failrate_cnt' type='integer' value='40' />
+ <propval name='failrate_interval' type='integer' value='60' />
+ <propval name='inherit_env' type='boolean' value='true' />
+ <propval name='tcp_trace' type='boolean' value='false' />
+ <propval name='tcp_wrappers' type='boolean' value='false' />
+ <propval name='bind_addr' type='astring' value='' />
+ <propval name='bind_fail_max' type='integer' value='-1' />
+ <propval name='bind_fail_interval' type='integer' value='-1' />
+ </property_group>
+
+ <stability value='Unstable' />
+
+ <template>
+ <common_name>
+ <loctext xml:lang='C'>inetd</loctext>
+ </common_name>
+ <description>
+ <loctext xml:lang='C'>
+inetd provides listening and spawning services for registered Internet
+services.
+ </loctext>
+ </description>
+ <documentation>
+ <manpage title='inetd' section='1M'
+ manpath='/usr/share/man' />
+ </documentation>
+ </template>
+</service>
+
+</service_bundle>
diff --git a/usr/src/cmd/cmd-inet/usr.lib/inetd/inetd_impl.h b/usr/src/cmd/cmd-inet/usr.lib/inetd/inetd_impl.h
new file mode 100644
index 0000000000..423d7cdff5
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/inetd/inetd_impl.h
@@ -0,0 +1,363 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _INETD_IMPL_H
+#define _INETD_IMPL_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+
+/*
+ * Header file containing inetd's shared types/data structures and
+ * function declarations.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <stdarg.h>
+#include <rpc/rpc.h>
+#include <assert.h>
+#include <libscf.h>
+#include <libinetutil.h>
+#include <inetsvc.h>
+#include <librestart.h>
+#include <libuutil.h>
+#include <wordexp.h>
+
+
+/*
+ * Number of consecutive retries of a repository operation that failed due
+ * to a broken connection performed before giving up and failing.
+ */
+#define REP_OP_RETRIES 10
+
+/* retryable SMF method error */
+#define SMF_EXIT_ERR_OTHER 1
+
+/* inetd's syslog ident string */
+#define SYSLOG_IDENT "inetd"
+
+/* Is this instance currently executing a method ? */
+#define INST_IN_TRANSITION(i) ((i)->next_istate != IIS_NONE)
+
+/* Names of properties that inetd uses to store instance state. */
+#define PR_NAME_NON_START_PID "non_start_pid"
+#define PR_NAME_START_PIDS "start_pids"
+#define PR_NAME_CUR_INT_STATE "cur_state"
+#define PR_NAME_NEXT_INT_STATE "next_state"
+
+/* Connection backlog applied to connection oriented services. */
+#define CONNECTION_BACKLOG 10
+
+/*
+ * Instance states used internal to svc.inetd.
+ * NOTE: The states table in cmd/cmd-inetd/inetd/inetd.c relies on the
+ * ordering of this enumeration, so take care if modifying it.
+ */
+typedef enum {
+ IIS_UNINITIALIZED,
+ IIS_ONLINE,
+ IIS_IN_ONLINE_METHOD,
+ IIS_OFFLINE,
+ IIS_IN_OFFLINE_METHOD,
+ IIS_DISABLED,
+ IIS_IN_DISABLE_METHOD,
+ IIS_IN_REFRESH_METHOD,
+ IIS_MAINTENANCE,
+ IIS_OFFLINE_CONRATE,
+ IIS_OFFLINE_BIND,
+ IIS_OFFLINE_COPIES,
+ IIS_DEGRADED,
+ IIS_NONE
+} internal_inst_state_t;
+
+/*
+ * inetd's instance methods.
+ * NOTE: The methods table in cmd/cmd-inetd/inetd/util.c relies on the
+ * ordering of this enumeration, so take care if modifying it.
+ */
+typedef enum {
+ IM_START,
+ IM_ONLINE,
+ IM_OFFLINE,
+ IM_DISABLE,
+ IM_REFRESH,
+ NUM_METHODS,
+ IM_NONE
+} instance_method_t;
+
+/* Collection of information pertaining to a method */
+typedef struct {
+ char *exec_path; /* path passed to exec() */
+
+ /*
+ * Structure returned from wordexp(3c) that contains an expansion of the
+ * exec property into a form suitable for exec(2).
+ */
+ wordexp_t exec_args_we;
+
+ /*
+ * Copy of the first argument of the above wordexp_t structure in the
+ * event that an alternate arg0 is provided, and we replace the first
+ * argument with the alternate arg0. This is necessary so the
+ * contents of the wordexp_t structure can be returned to their
+ * original form as returned from wordexp(3c), which is a requirement
+ * for calling wordfree(3c), wordexp()'s associated cleanup routine.
+ */
+ const char *wordexp_arg0_backup;
+
+ /* time a method can run for before being considered broken */
+ int timeout;
+} method_info_t;
+
+typedef struct {
+ basic_cfg_t *basic;
+ method_info_t *methods[NUM_METHODS];
+} instance_cfg_t;
+
+/*
+ * Structure used to construct a list of int64_t's and their associated
+ * scf values. Used to store lists of process ids, internal states, and to
+ * store the associated scf value used when writing the values back to the
+ * repository.
+ */
+typedef struct {
+ int64_t val;
+ scf_value_t *scf_val;
+ uu_list_node_t link;
+} rep_val_t;
+
+/* Structure containing the state and configuration of a service instance. */
+typedef struct {
+ char *fmri;
+
+ /* fd we're going to take a connection on */
+ int conn_fd;
+
+ /* number of copies of this instance active */
+ int64_t copies;
+
+ /* connection rate counters */
+ int64_t conn_rate_count;
+ time_t conn_rate_start;
+
+ /* failure rate counters */
+ int64_t fail_rate_count;
+ time_t fail_rate_start;
+ /* bind failure count */
+ int64_t bind_fail_count;
+
+ /* pids of currently running methods */
+ uu_list_t *non_start_pid;
+ uu_list_t *start_pids;
+
+ /* remote address, used for TCP tracing */
+ struct sockaddr_storage remote_addr;
+
+ internal_inst_state_t cur_istate;
+ internal_inst_state_t next_istate;
+
+ /* repository compatible versions of the above 2 states */
+ uu_list_t *cur_istate_rep;
+ uu_list_t *next_istate_rep;
+
+ /*
+ * Current instance configuration resulting from its repository
+ * configuration.
+ */
+ instance_cfg_t *config;
+
+ /*
+ * Soon to be applied instance configuration. This configuration was
+ * read during a refresh when this instance was online, and the
+ * instance needed taking offline for this configuration to be applied.
+ * The instance is currently on its way offline, and this configuration
+ * will become the current configuration when it arrives there.
+ */
+ instance_cfg_t *new_config;
+
+ /* current pending conrate-offline/method timer; -1 if none pending */
+ iu_timer_id_t timer_id;
+
+ /* current pending bind retry timer; -1 if none pending */
+ iu_timer_id_t bind_timer_id;
+
+ /*
+ * Flags that assist in the fanout of an instance arriving in the
+ * offline state on-route to some other state.
+ */
+ boolean_t disable_req;
+ boolean_t maintenance_req;
+ boolean_t conn_rate_exceeded;
+ boolean_t bind_retries_exceeded;
+
+ /*
+ * Event waiting to be processed. RESTARTER_EVENT_TYPE_INVALID is used
+ * to mean no event waiting.
+ */
+ restarter_event_type_t pending_rst_event;
+
+ /* link to next instance in list */
+ uu_list_node_t link;
+} instance_t;
+
+
+/* Structure used to store information pertaining to instance method types. */
+typedef struct {
+ instance_method_t method;
+ const char *name;
+ internal_inst_state_t dst_state;
+} method_type_info_t;
+
+
+extern uu_list_t *instance_list;
+extern struct pollfd *poll_fds;
+extern nfds_t num_pollfds;
+extern method_type_info_t methods[];
+extern iu_tq_t *timer_queue;
+extern uu_list_pool_t *conn_ind_pool;
+
+/*
+ * util.c
+ */
+extern void msg_init(void);
+extern void msg_fini(void);
+/* PRINTFLIKE1 */
+extern void debug_msg(const char *, ...);
+/* PRINTFLIKE1 */
+extern void error_msg(const char *, ...);
+/* PRINTFLIKE1 */
+extern void warn_msg(const char *, ...);
+extern void poll_fini(void);
+extern boolean_t isset_pollfd(int);
+extern void clear_pollfd(int);
+extern int set_pollfd(int, uint16_t);
+extern struct pollfd *find_pollfd(int);
+extern int safe_read(int, void *, size_t);
+extern boolean_t copies_limit_exceeded(instance_t *);
+extern void cancel_inst_timer(instance_t *);
+extern void cancel_bind_timer(instance_t *);
+extern void enable_blocking(int);
+extern void disable_blocking(int);
+
+/*
+ * tlx.c
+ */
+extern rpc_info_t *create_rpc_info(const char *, const char *, const char *,
+ int, int);
+extern void destroy_rpc_info(rpc_info_t *);
+extern boolean_t rpc_info_equal(const rpc_info_t *, const rpc_info_t *);
+extern int register_rpc_service(const char *, const rpc_info_t *);
+extern void unregister_rpc_service(const char *, const rpc_info_t *);
+extern int create_bound_endpoint(const char *, tlx_info_t *);
+extern void close_net_fd(instance_t *, int);
+extern int tlx_accept(const char *, tlx_info_t *, struct sockaddr_storage *);
+extern struct t_call *dequeue_conind(uu_list_t *);
+extern int queue_conind(uu_list_t *, struct t_call *);
+extern void tlx_fini(void);
+extern int tlx_init(void);
+extern boolean_t tlx_info_equal(const tlx_info_t *, const tlx_info_t *,
+ boolean_t);
+extern void consume_wait_data(instance_t *, int);
+
+/*
+ * config.c
+ */
+extern int config_init(void);
+extern void config_fini(void);
+extern boolean_t socket_info_equal(const socket_info_t *, const socket_info_t *,
+ boolean_t);
+extern boolean_t method_info_equal(const method_info_t *,
+ const method_info_t *);
+extern struct method_context *read_method_context(const char *, const char *,
+ const char *, const char **);
+extern void destroy_instance_cfg(instance_cfg_t *);
+extern instance_cfg_t *read_instance_cfg(const char *);
+extern boolean_t bind_config_equal(const basic_cfg_t *, const basic_cfg_t *);
+extern int read_enable_merged(const char *, boolean_t *);
+
+/*
+ * repval.c
+ */
+extern void repval_fini(void);
+extern int repval_init(void);
+extern uu_list_t *create_rep_val_list(void);
+extern void destroy_rep_val_list(uu_list_t *);
+extern scf_error_t store_rep_vals(uu_list_t *, const char *, const char *);
+extern scf_error_t retrieve_rep_vals(uu_list_t *, const char *, const char *);
+extern rep_val_t *find_rep_val(uu_list_t *, int64_t);
+extern int set_single_rep_val(uu_list_t *, int64_t);
+extern int64_t get_single_rep_val(uu_list_t *);
+extern int add_rep_val(uu_list_t *, int64_t);
+extern void remove_rep_val(uu_list_t *, int64_t);
+extern void empty_rep_val_list(uu_list_t *);
+extern int make_handle_bound(scf_handle_t *);
+extern int add_remove_contract(const char *, boolean_t, ctid_t);
+extern int iterate_repository_contracts(const char *, int);
+
+/*
+ * contracts.c
+ */
+extern int contract_init(void);
+extern void contract_fini(void);
+void contract_postfork(void);
+int contract_prefork(void);
+extern int get_latest_contract(ctid_t *cid);
+extern int adopt_contract(ctid_t, const char *);
+extern int abandon_contract(ctid_t);
+
+/*
+ * inetd.c
+ */
+extern void process_offline_inst(instance_t *);
+extern void process_non_start_term(instance_t *, int);
+extern void process_start_term(instance_t *);
+extern void remove_method_ids(instance_t *, pid_t, ctid_t, instance_method_t);
+
+/*
+ * env.c
+ */
+char **set_smf_env(struct method_context *, instance_t *, const char *);
+
+/*
+ * wait.c
+ */
+extern int register_method(instance_t *, pid_t, ctid_t cid, instance_method_t);
+extern int method_init(void);
+extern void method_fini(void);
+extern void process_terminated_methods(void);
+extern void unregister_instance_methods(const instance_t *);
+extern void method_preexec(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _INETD_IMPL_H */
diff --git a/usr/src/cmd/cmd-inet/usr.lib/inetd/repval.c b/usr/src/cmd/cmd-inet/usr.lib/inetd/repval.c
new file mode 100644
index 0000000000..e84582f3f8
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/inetd/repval.c
@@ -0,0 +1,820 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ *
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * This file contains routines to manipulate lists of repository values that
+ * are used to store process ids and the internal state. There are routines
+ * to read/write the lists from/to the repository and routines to modify or
+ * inspect the lists. It also contains routines that deal with the
+ * repository side of contract ids.
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <libintl.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include "inetd_impl.h"
+
+
+/*
+ * Number of consecutive repository bind retries performed by bind_to_rep()
+ * before failing.
+ */
+#define BIND_TO_REP_RETRIES 10
+
+/* Name of property group where inetd's state for a service is stored. */
+#define PG_NAME_INSTANCE_STATE (const char *) "inetd_state"
+
+/* uu_list repval list pool */
+static uu_list_pool_t *rep_val_pool = NULL;
+
+/*
+ * Repository object pointers that get set-up in repval_init() and closed down
+ * in repval_fini(). They're used in _retrieve_rep_vals(), _store_rep_vals(),
+ * add_remove_contract_norebind(), and adopt_repository_contracts(). They're
+ * global so they can be initialized once on inetd startup, and re-used
+ * there-after in the referenced functions.
+ */
+static scf_handle_t *rep_handle = NULL;
+static scf_propertygroup_t *pg = NULL;
+static scf_instance_t *inst = NULL;
+static scf_transaction_t *trans = NULL;
+static scf_transaction_entry_t *entry = NULL;
+static scf_property_t *prop = NULL;
+
+/*
+ * Try and make the given handle bind be bound to the repository. If
+ * it's already bound, or we succeed a new bind return 0; else return
+ * -1 on failure, with the SCF error set to one of the following:
+ * SCF_ERROR_NO_SERVER
+ * SCF_ERROR_NO_RESOURCES
+ */
+int
+make_handle_bound(scf_handle_t *hdl)
+{
+ uint_t retries;
+
+ for (retries = 0; retries <= BIND_TO_REP_RETRIES; retries++) {
+ if ((scf_handle_bind(hdl) == 0) ||
+ (scf_error() == SCF_ERROR_IN_USE))
+ return (0);
+
+ assert(scf_error() != SCF_ERROR_INVALID_ARGUMENT);
+ }
+
+ return (-1);
+}
+
+int
+repval_init(void)
+{
+ debug_msg("Entering repval_init");
+
+ /*
+ * Create the repval list pool.
+ */
+ rep_val_pool = uu_list_pool_create("rep_val_pool", sizeof (rep_val_t),
+ offsetof(rep_val_t, link), NULL, UU_LIST_POOL_DEBUG);
+ if (rep_val_pool == NULL) {
+ error_msg("%s: %s", gettext("Failed to create rep_val pool"),
+ uu_strerror(uu_error()));
+ return (-1);
+ }
+
+ /*
+ * Create and bind a repository handle, and create all repository
+ * objects that we'll use later that are associated with it. On any
+ * errors we simply return -1 and let repval_fini() clean-up after
+ * us.
+ */
+ if ((rep_handle = scf_handle_create(SCF_VERSION)) == NULL) {
+ error_msg("%s: %s",
+ gettext("Failed to create repository handle"),
+ scf_strerror(scf_error()));
+ goto cleanup;
+ } else if (make_handle_bound(rep_handle) == -1) {
+ goto cleanup;
+ } else if (((pg = scf_pg_create(rep_handle)) == NULL) ||
+ ((inst = scf_instance_create(rep_handle)) == NULL) ||
+ ((trans = scf_transaction_create(rep_handle)) == NULL) ||
+ ((entry = scf_entry_create(rep_handle)) == NULL) ||
+ ((prop = scf_property_create(rep_handle)) == NULL)) {
+ error_msg("%s: %s",
+ gettext("Failed to create repository object"),
+ scf_strerror(scf_error()));
+ goto cleanup;
+ }
+
+ return (0);
+cleanup:
+ repval_fini();
+ return (-1);
+}
+
+void
+repval_fini(void)
+{
+ debug_msg("Entering repval_fini");
+
+ if (rep_handle != NULL) {
+ /*
+ * We unbind from the repository before we free the repository
+ * objects for efficiency reasons.
+ */
+ (void) scf_handle_unbind(rep_handle);
+
+ scf_pg_destroy(pg);
+ pg = NULL;
+ scf_instance_destroy(inst);
+ inst = NULL;
+ scf_transaction_destroy(trans);
+ trans = NULL;
+ scf_entry_destroy(entry);
+ entry = NULL;
+ scf_property_destroy(prop);
+ prop = NULL;
+
+ scf_handle_destroy(rep_handle);
+ rep_handle = NULL;
+ }
+
+ if (rep_val_pool != NULL) {
+ uu_list_pool_destroy(rep_val_pool);
+ rep_val_pool = NULL;
+ }
+}
+
+uu_list_t *
+create_rep_val_list(void)
+{
+ uu_list_t *ret;
+
+ debug_msg("Entering create_rep_val_list");
+
+ if ((ret = uu_list_create(rep_val_pool, NULL, 0)) == NULL)
+ assert(uu_error() == UU_ERROR_NO_MEMORY);
+
+ return (ret);
+}
+
+void
+destroy_rep_val_list(uu_list_t *list)
+{
+ debug_msg("Entering destroy_rep_val_list");
+
+ if (list != NULL) {
+ empty_rep_val_list(list);
+ uu_list_destroy(list);
+ }
+}
+
+rep_val_t *
+find_rep_val(uu_list_t *list, int64_t val)
+{
+ rep_val_t *rv;
+
+ debug_msg("Entering find_rep_val: val: %lld", val);
+
+ for (rv = uu_list_first(list); rv != NULL;
+ rv = uu_list_next(list, rv)) {
+ if (rv->val == val)
+ break;
+ }
+ return (rv);
+}
+
+int
+add_rep_val(uu_list_t *list, int64_t val)
+{
+ rep_val_t *rv;
+
+ debug_msg("Entering add_rep_val: val: %lld", val);
+
+ if ((rv = malloc(sizeof (rep_val_t))) == NULL)
+ return (-1);
+
+ uu_list_node_init(rv, &rv->link, rep_val_pool);
+ rv->val = val;
+ rv->scf_val = NULL;
+ (void) uu_list_insert_after(list, NULL, rv);
+
+ return (0);
+}
+
+void
+remove_rep_val(uu_list_t *list, int64_t val)
+{
+ rep_val_t *rv;
+
+ debug_msg("Entering remove_rep_val: val: %lld", val);
+
+ if ((rv = find_rep_val(list, val)) != NULL) {
+ uu_list_remove(list, rv);
+ assert(rv->scf_val == NULL);
+ free(rv);
+ }
+}
+
+void
+empty_rep_val_list(uu_list_t *list)
+{
+ void *cookie = NULL;
+ rep_val_t *rv;
+
+ debug_msg("Entering empty_rep_val_list");
+
+ while ((rv = uu_list_teardown(list, &cookie)) != NULL) {
+ if (rv->scf_val != NULL)
+ scf_value_destroy(rv->scf_val);
+ free(rv);
+ }
+}
+
+int64_t
+get_single_rep_val(uu_list_t *list)
+{
+ rep_val_t *rv = uu_list_first(list);
+
+ debug_msg("Entering get_single_rep_val");
+
+ assert(rv != NULL);
+ return (rv->val);
+}
+
+int
+set_single_rep_val(uu_list_t *list, int64_t val)
+{
+ rep_val_t *rv = uu_list_first(list);
+
+ debug_msg("Entering set_single_rep_val");
+
+ if (rv == NULL) {
+ if (add_rep_val(list, val) == -1)
+ return (-1);
+ } else {
+ rv->val = val;
+ }
+
+ return (0);
+}
+
+/*
+ * Partner to add_tr_entry_values. This function frees the scf_values created
+ * in add_tr_entry_values() in the list 'vals'.
+ */
+static void
+remove_tr_entry_values(uu_list_t *vals)
+{
+ rep_val_t *rval;
+
+ debug_msg("Entering remove_tr_entry_values");
+
+ for (rval = uu_list_first(vals); rval != NULL;
+ rval = uu_list_next(vals, rval)) {
+ if (rval->scf_val != NULL) {
+ scf_value_destroy(rval->scf_val);
+ rval->scf_val = NULL;
+ }
+ }
+}
+
+/*
+ * This function creates and associates with transaction entry 'entry' an
+ * scf value for each value in 'vals'. The pointers to the scf values
+ * are stored in the list for later cleanup by remove_tr_entry_values.
+ * Returns 0 on success, else -1 on error with scf_error() set to:
+ * SCF_ERROR_NO_MEMORY if memory allocation failed.
+ * SCF_ERROR_CONNECTION_BROKEN if the connection to the repository was broken.
+ */
+static int
+add_tr_entry_values(scf_handle_t *hdl, scf_transaction_entry_t *entry,
+ uu_list_t *vals)
+{
+ rep_val_t *rval;
+
+ debug_msg("Entering add_tr_entry_values");
+
+ for (rval = uu_list_first(vals); rval != NULL;
+ rval = uu_list_next(vals, rval)) {
+
+ assert(rval->scf_val == NULL);
+ if ((rval->scf_val = scf_value_create(hdl)) == NULL) {
+ remove_tr_entry_values(vals);
+ return (-1);
+ }
+
+ scf_value_set_integer(rval->scf_val, rval->val);
+
+ if (scf_entry_add_value(entry, rval->scf_val) < 0) {
+ remove_tr_entry_values(vals);
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * Stores the values contained in the list 'vals' into the property 'prop_name'
+ * of the instance with fmri 'inst_fmri', within the instance's instance
+ * state property group.
+ *
+ * Returns 0 on success, else one of the following on failure:
+ * SCF_ERROR_NO_MEMORY if memory allocation failed.
+ * SCF_ERROR_NO_RESOURCES if the server doesn't have required resources.
+ * SCF_ERROR_VERSION_MISMATCH if program compiled against a newer libscf
+ * than on system.
+ * SCF_ERROR_PERMISSION_DENIED if insufficient privileges to modify pg.
+ * SCF_ERROR_BACKEND_ACCESS if the repository back-end refused the pg modify.
+ * SCF_ERROR_CONNECTION_BROKEN if the connection to the repository was broken.
+ */
+static scf_error_t
+_store_rep_vals(uu_list_t *vals, const char *inst_fmri, const char *prop_name)
+{
+ int cret;
+ int ret;
+
+ debug_msg("Entering _store_rep_vals: fmri: %s, prop: %s", inst_fmri,
+ prop_name);
+
+ if (scf_handle_decode_fmri(rep_handle, inst_fmri, NULL, NULL, inst,
+ NULL, NULL, SCF_DECODE_FMRI_EXACT) == -1)
+ return (scf_error());
+
+ /*
+ * Fetch the instance state pg, and if it doesn't exist try and
+ * create it.
+ */
+ if (scf_instance_get_pg(inst, PG_NAME_INSTANCE_STATE, pg) < 0) {
+ if (scf_error() != SCF_ERROR_NOT_FOUND)
+ return (scf_error());
+ if (scf_instance_add_pg(inst, PG_NAME_INSTANCE_STATE,
+ SCF_GROUP_FRAMEWORK, SCF_PG_FLAG_NONPERSISTENT, pg) < 0)
+ return (scf_error());
+ }
+
+ /*
+ * Perform a transaction to write the values to the requested property.
+ * If someone got there before us, loop and retry.
+ */
+ do {
+ if (scf_transaction_start(trans, pg) < 0)
+ return (scf_error());
+
+ if ((scf_transaction_property_new(trans, entry,
+ prop_name, SCF_TYPE_INTEGER) < 0) &&
+ (scf_transaction_property_change_type(trans, entry,
+ prop_name, SCF_TYPE_INTEGER) < 0)) {
+ ret = scf_error();
+ goto cleanup;
+ }
+
+ if (add_tr_entry_values(rep_handle, entry, vals) < 0) {
+ ret = scf_error();
+ goto cleanup;
+ }
+
+ if ((cret = scf_transaction_commit(trans)) < 0) {
+ ret = scf_error();
+ goto cleanup;
+ } else if (cret == 0) {
+ scf_transaction_reset(trans);
+ scf_entry_reset(entry);
+ remove_tr_entry_values(vals);
+ if (scf_pg_update(pg) < 0) {
+ ret = scf_error();
+ goto cleanup;
+ }
+ }
+ } while (cret == 0);
+
+ ret = 0;
+cleanup:
+ scf_transaction_reset(trans);
+ scf_entry_reset(entry);
+ remove_tr_entry_values(vals);
+ return (ret);
+}
+
+/*
+ * Retrieves the repository values of property 'prop_name', of the instance
+ * with fmri 'fmri', from within the instance's instance state property
+ * group and adds them to the value list 'list'.
+ *
+ * Returns 0 on success, else one of the following values on error:
+ * SCF_ERROR_NOT_FOUND if the property doesn't exist.
+ * SCF_ERROR_NO_MEMORY if memory allocation failed.
+ * SCF_ERROR_CONNECTION_BROKEN if the connection to the repository was broken.
+ * SCF_ERROR_TYPE_MISMATCH if the property was of an unexpected type.
+ *
+ */
+static scf_error_t
+_retrieve_rep_vals(uu_list_t *list, const char *fmri, const char *prop_name)
+{
+ scf_simple_prop_t *sp;
+ int64_t *ip;
+
+ debug_msg("Entering _retrieve_rep_vals: fmri: %s, prop: %s", fmri,
+ prop_name);
+
+ if ((sp = scf_simple_prop_get(rep_handle, fmri, PG_NAME_INSTANCE_STATE,
+ prop_name)) == NULL)
+ return (scf_error());
+
+ while ((ip = scf_simple_prop_next_integer(sp)) != NULL) {
+ if (add_rep_val(list, *ip) == -1) {
+ empty_rep_val_list(list);
+ scf_simple_prop_free(sp);
+ return (SCF_ERROR_NO_MEMORY);
+ }
+ }
+ if (scf_error() != SCF_ERROR_NONE) {
+ assert(scf_error() == SCF_ERROR_TYPE_MISMATCH);
+ empty_rep_val_list(list);
+ scf_simple_prop_free(sp);
+ return (scf_error());
+ }
+
+ scf_simple_prop_free(sp);
+ return (0);
+}
+
+/*
+ * A routine that loops trying to read/write repository values until
+ * either success, an error other that a broken repository connection or
+ * the number of retries reaches REP_OP_RETRIES.
+ * Returns 0 on success, else the error value from either _store_rep_vals or
+ * retrieve_rep_vals (based on whether 'store' was set or not), or one of the
+ * following if a rebind failed:
+ * SCF_ERROR_NO_RESOURCES if the server doesn't have adequate resources.
+ * SCF_ERROR_NO_SERVER if the server isn't running.
+ */
+static scf_error_t
+store_retrieve_rep_vals(uu_list_t *vals, const char *fmri,
+ const char *prop, boolean_t store)
+{
+ scf_error_t ret;
+ uint_t retries;
+
+ debug_msg("Entering store_retrieve_rep_vals, store: %d", store);
+
+
+ for (retries = 0; retries <= REP_OP_RETRIES; retries++) {
+ if (make_handle_bound(rep_handle) == -1) {
+ ret = scf_error();
+ break;
+ }
+
+ if ((ret = (store ? _store_rep_vals(vals, fmri, prop) :
+ _retrieve_rep_vals(vals, fmri, prop))) !=
+ SCF_ERROR_CONNECTION_BROKEN)
+ break;
+
+ (void) scf_handle_unbind(rep_handle);
+ }
+
+ return (ret);
+}
+
+scf_error_t
+store_rep_vals(uu_list_t *vals, const char *fmri, const char *prop)
+{
+ return (store_retrieve_rep_vals(vals, fmri, prop, B_TRUE));
+}
+
+scf_error_t
+retrieve_rep_vals(uu_list_t *vals, const char *fmri, const char *prop)
+{
+ return (store_retrieve_rep_vals(vals, fmri, prop, B_FALSE));
+}
+
+/*
+ * Fails with ECONNABORTED, ENOENT, EACCES, EROFS, ENOMEM, or EPERM.
+ */
+static int
+add_remove_contract_norebind(const char *fmri, boolean_t add, ctid_t ctid)
+{
+ int err;
+
+ if (scf_handle_decode_fmri(rep_handle, fmri, NULL, NULL, inst, NULL,
+ NULL, SCF_DECODE_FMRI_EXACT) != 0) {
+ switch (scf_error()) {
+ case SCF_ERROR_CONNECTION_BROKEN:
+ return (ECONNABORTED);
+
+ case SCF_ERROR_NOT_FOUND:
+ return (ENOENT);
+
+ case SCF_ERROR_CONSTRAINT_VIOLATED:
+ case SCF_ERROR_INVALID_ARGUMENT:
+ case SCF_ERROR_HANDLE_MISMATCH:
+ default:
+ assert(0);
+ abort();
+ }
+ }
+
+redo:
+ if (add)
+ err = restarter_store_contract(inst, ctid,
+ RESTARTER_CONTRACT_PRIMARY);
+ else
+ err = restarter_remove_contract(inst, ctid,
+ RESTARTER_CONTRACT_PRIMARY);
+ switch (err) {
+ case 0:
+ case ENOMEM:
+ case ECONNABORTED:
+ return (err);
+
+ case ECANCELED:
+ return (ENOENT);
+
+ case EPERM:
+ assert(0);
+ return (err);
+
+ case EACCES:
+ error_msg(add ? gettext("Failed to write contract id %ld for "
+ "instance %s to repository: backend access denied.") :
+ gettext("Failed to remove contract id %ld for instance %s "
+ "from repository: backend access denied."), ctid, fmri);
+ return (err);
+
+ case EROFS:
+ error_msg(add ? gettext("Failed to write contract id %ld for "
+ "instance %s to repository: backend is read-only.") :
+ gettext("Failed to remove contract id %ld for instance %s "
+ "from repository: backend is read-only."), ctid, fmri);
+ return (err);
+
+ case EINVAL:
+ case EBADF:
+ default:
+ assert(0);
+ abort();
+ /* NOTREACHED */
+ }
+}
+
+/*
+ * Tries to add/remove (dependent on the value of 'add') the specified
+ * contract id to the specified instance until either success, an error
+ * other that connection broken occurs, or the number of bind retries reaches
+ * REP_OP_RETRIES.
+ * Returns 0 on success else fails with one of ENOENT, EACCES, EROFS, EPERM,
+ * ECONNABORTED or ENOMEM.
+ */
+int
+add_remove_contract(const char *fmri, boolean_t add, ctid_t ctid)
+{
+ uint_t retries;
+ int err;
+
+ for (retries = 0; retries <= REP_OP_RETRIES; retries++) {
+ if (make_handle_bound(rep_handle) == -1) {
+ err = ECONNABORTED;
+ break;
+ }
+
+ if ((err = add_remove_contract_norebind(fmri, add, ctid)) !=
+ ECONNABORTED)
+ break;
+
+ (void) scf_handle_unbind(rep_handle);
+ }
+
+ return (err);
+}
+
+/*
+ * Iterate over all contracts associated with the instance specified by
+ * fmri; if sig !=0, we send each contract the specified signal, otherwise
+ * we call adopt_contract() to take ownership. This really ought to be
+ * reworked to use a callback mechanism if more functionality is added.
+ *
+ * Returns 0 on success or ENOENT if the instance, its restarter property
+ * group, or its contract property don't exist, or EINVAL if the property
+ * is not of the correct type, ENOMEM if there was a memory allocation
+ * failure, EPERM if there were permission problems accessing the repository,
+ * or ECONNABORTED if the connection with the repository was broken.
+ */
+int
+iterate_repository_contracts(const char *fmri, int sig)
+{
+ scf_iter_t *iter;
+ scf_value_t *val = NULL;
+ uint64_t c;
+ int err;
+ int ret = 0;
+ uint_t retries = 0;
+
+ debug_msg("Entering iterate_repository_contracts");
+
+ if (make_handle_bound(rep_handle) == -1)
+ return (ECONNABORTED);
+
+ if (((iter = scf_iter_create(rep_handle)) == NULL) ||
+ ((val = scf_value_create(rep_handle)) == NULL)) {
+ ret = ENOMEM;
+ goto out;
+ }
+
+rep_retry:
+ if (scf_handle_decode_fmri(rep_handle, fmri, NULL, NULL, inst, NULL,
+ NULL, SCF_DECODE_FMRI_EXACT) != 0) {
+ switch (scf_error()) {
+ case SCF_ERROR_CONNECTION_BROKEN:
+rebind:
+ (void) scf_handle_unbind(rep_handle);
+
+ if (retries++ == REP_OP_RETRIES) {
+ ret = ECONNABORTED;
+ goto out;
+ }
+
+ if (make_handle_bound(rep_handle) == -1) {
+ ret = ECONNABORTED;
+ goto out;
+ }
+
+ goto rep_retry;
+
+ case SCF_ERROR_NOT_FOUND:
+ ret = ENOENT;
+ goto out;
+
+ case SCF_ERROR_CONSTRAINT_VIOLATED:
+ case SCF_ERROR_INVALID_ARGUMENT:
+ case SCF_ERROR_HANDLE_MISMATCH:
+ default:
+ assert(0);
+ abort();
+ }
+ }
+
+ if (scf_instance_get_pg(inst, SCF_PG_RESTARTER, pg) != 0) {
+ switch (scf_error()) {
+ case SCF_ERROR_CONNECTION_BROKEN:
+ goto rebind;
+
+ case SCF_ERROR_NOT_SET:
+ ret = 0;
+ goto out;
+
+ case SCF_ERROR_NOT_FOUND:
+ ret = ENOENT;
+ goto out;
+
+ case SCF_ERROR_INVALID_ARGUMENT:
+ case SCF_ERROR_HANDLE_MISMATCH:
+ default:
+ assert(0);
+ abort();
+ }
+ }
+
+ if (scf_pg_get_property(pg, SCF_PROPERTY_CONTRACT, prop) != 0) {
+ switch (scf_error()) {
+ case SCF_ERROR_CONNECTION_BROKEN:
+ goto rebind;
+
+ case SCF_ERROR_NOT_SET:
+ ret = 0;
+ goto out;
+
+ case SCF_ERROR_NOT_FOUND:
+ ret = ENOENT;
+ goto out;
+
+ case SCF_ERROR_INVALID_ARGUMENT:
+ case SCF_ERROR_HANDLE_MISMATCH:
+ default:
+ assert(0);
+ abort();
+ }
+ }
+
+ if (scf_property_is_type(prop, SCF_TYPE_COUNT) != 0) {
+ switch (scf_error()) {
+ case SCF_ERROR_CONNECTION_BROKEN:
+ goto rebind;
+
+ case SCF_ERROR_NOT_SET:
+ ret = ENOENT;
+ goto out;
+
+ case SCF_ERROR_TYPE_MISMATCH:
+ ret = EINVAL;
+ goto out;
+
+ default:
+ assert(0);
+ abort();
+ }
+ }
+
+ if (scf_iter_property_values(iter, prop) != 0) {
+ switch (scf_error()) {
+ case SCF_ERROR_CONNECTION_BROKEN:
+ goto rebind;
+
+ case SCF_ERROR_NOT_SET:
+ ret = ENOENT;
+ goto out;
+
+ case SCF_ERROR_HANDLE_MISMATCH:
+ default:
+ assert(0);
+ abort();
+ }
+ }
+
+ for (;;) {
+ err = scf_iter_next_value(iter, val);
+ if (err == 0) {
+ break;
+ } else if (err != 1) {
+ assert(scf_error() == SCF_ERROR_CONNECTION_BROKEN);
+ goto rebind;
+ }
+
+ err = scf_value_get_count(val, &c);
+ assert(err == 0);
+
+ if (sig == 0) {
+ /* Try to adopt the contract */
+ if (adopt_contract((ctid_t)c, fmri) != 0) {
+ /*
+ * Adoption failed. No reason to think it'll
+ * work later, so remove the id from our list
+ * in the repository.
+ *
+ * Beware: add_remove_contract_norebind() uses
+ * the global scf_ handles. Fortunately we're
+ * done with them. We need to be cognizant of
+ * repository disconnection, though.
+ */
+ switch (add_remove_contract_norebind(fmri,
+ B_FALSE, (ctid_t)c)) {
+ case 0:
+ case ENOENT:
+ case EACCES:
+ case EROFS:
+ break;
+
+ case ECONNABORTED:
+ goto rebind;
+
+ default:
+ assert(0);
+ abort();
+ }
+ }
+ } else {
+ /*
+ * Send a signal to all in the contract; ESRCH just
+ * means they all exited before we could kill them
+ */
+ if (sigsend(P_CTID, (ctid_t)c, sig) == -1 &&
+ errno != ESRCH) {
+ warn_msg(gettext("Unable to signal all contract"
+ "members of instance %s: %s"), fmri,
+ strerror(errno));
+ }
+ }
+ }
+
+out:
+ scf_value_destroy(val);
+ scf_iter_destroy(iter);
+ return (ret);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/inetd/req.flg b/usr/src/cmd/cmd-inet/usr.lib/inetd/req.flg
new file mode 100644
index 0000000000..d31375e098
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/inetd/req.flg
@@ -0,0 +1,29 @@
+#!/bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+echo_file usr/src/cmd/svc/Makefile.ctf
diff --git a/usr/src/cmd/cmd-inet/usr.lib/inetd/tlx.c b/usr/src/cmd/cmd-inet/usr.lib/inetd/tlx.c
new file mode 100644
index 0000000000..dafcf942b6
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/inetd/tlx.c
@@ -0,0 +1,679 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Contains routines that deal with TLI/XTI endpoints and rpc services.
+ */
+
+#include <sys/types.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <libintl.h>
+#include <unistd.h>
+#include <sys/sysmacros.h>
+#include <netconfig.h>
+#include <errno.h>
+#include <sys/sockio.h>
+#include "inetd_impl.h"
+
+uu_list_pool_t *conn_ind_pool = NULL;
+
+/*
+ * RPC functions.
+ */
+
+/*
+ * Returns B_TRUE if the non-address components of the 2 rpc_info_t structures
+ * are equivalent, else B_FALSE.
+ */
+boolean_t
+rpc_info_equal(const rpc_info_t *ri, const rpc_info_t *ri2)
+{
+ return ((ri->prognum == ri2->prognum) &&
+ (ri->lowver == ri2->lowver) &&
+ (ri->highver == ri2->highver) &&
+ (strcmp(ri->netid, ri2->netid) == 0));
+}
+
+/*
+ * Determine if we have a configured interface for the specified address
+ * family. This code is a mirror of libnsl's __can_use_af(). We mirror
+ * it because we need an exact duplicate of its behavior, yet the
+ * function isn't exported by libnsl, and this fix is considered short-
+ * term, so it's not worth exporting it.
+ *
+ * We need to duplicate __can_use_af() so we can accurately determine
+ * when getnetconfigent() returns failure for a v6 netid due to no IPv6
+ * interfaces being configured: getnetconfigent() returns failure
+ * if a netid is either 'tcp6' or 'udp6' and __can_use_af() returns 0,
+ * but it doesn't return a return code to uniquely determine this
+ * failure. If we don't accurately determine these failures, we could
+ * output error messages in a case when they weren't justified.
+ */
+static int
+can_use_af(sa_family_t af)
+{
+ struct lifnum lifn;
+ int fd;
+
+ if ((fd = open("/dev/udp", O_RDONLY)) < 0) {
+ return (0);
+ }
+ lifn.lifn_family = af;
+ /* LINTED ECONST_EXPR */
+ lifn.lifn_flags = IFF_UP & !(IFF_NOXMIT | IFF_DEPRECATED);
+ if (ioctl(fd, SIOCGLIFNUM, &lifn, sizeof (lifn)) < 0) {
+ lifn.lifn_count = 0;
+ }
+
+ (void) close(fd);
+ return (lifn.lifn_count);
+}
+
+static boolean_t
+is_v6_netid(const char *netid)
+{
+ return ((strcmp(netid, SOCKET_PROTO_TCP6) == 0) ||
+ (strcmp(netid, SOCKET_PROTO_UDP6) == 0));
+}
+
+/*
+ * Registers with rpcbind the program number with all versions, from low to
+ * high, with the netid, all specified in 'rpc'. If registration fails,
+ * returns -1, else 0.
+ */
+int
+register_rpc_service(const char *fmri, const rpc_info_t *rpc)
+{
+ struct netconfig *nconf;
+ int ver;
+
+ debug_msg("Entering register_rpc_service: instance: %s", fmri);
+
+ if ((nconf = getnetconfigent(rpc->netid)) == NULL) {
+ /*
+ * Check whether getnetconfigent() failed as a result of
+ * having no IPv6 interfaces configured for a v6 netid, or
+ * as a result of a 'real' error, and output an appropriate
+ * message with an appropriate severity.
+ */
+ if (is_v6_netid(rpc->netid) && !can_use_af(AF_INET6)) {
+ warn_msg(gettext(
+ "Couldn't register netid %s for RPC instance %s "
+ "because no IPv6 interfaces are plumbed"),
+ rpc->netid, fmri);
+ } else {
+ error_msg(gettext(
+ "Failed to lookup netid '%s' for instance %s: %s"),
+ rpc->netid, fmri, nc_sperror());
+ }
+ return (-1);
+ }
+
+ for (ver = rpc->lowver; ver <= rpc->highver; ver++) {
+ if (!rpcb_set(rpc->prognum, ver, nconf, &(rpc->netbuf))) {
+ error_msg(gettext("Failed to register version %d "
+ "of RPC service instance %s, netid %s"), ver,
+ fmri, rpc->netid);
+
+ for (ver--; ver >= rpc->lowver; ver--)
+ (void) rpcb_unset(rpc->prognum, ver, nconf);
+
+ freenetconfigent(nconf);
+ return (-1);
+ }
+ }
+
+ freenetconfigent(nconf);
+ return (0);
+}
+
+/* Unregister all the registrations done by register_rpc_service */
+void
+unregister_rpc_service(const char *fmri, const rpc_info_t *rpc)
+{
+ int ver;
+ struct netconfig *nconf;
+
+ debug_msg("Entering unregister_rpc_service, instance: %s", fmri);
+
+ if ((nconf = getnetconfigent(rpc->netid)) == NULL) {
+ /*
+ * Don't output an error message if getnetconfigent() fails for
+ * a v6 netid when an IPv6 interface isn't configured.
+ */
+ if (!(is_v6_netid(rpc->netid) && !can_use_af(AF_INET6))) {
+ error_msg(gettext(
+ "Failed to lookup netid '%s' for instance %s: %s"),
+ rpc->netid, fmri, nc_sperror());
+ }
+ return;
+ }
+
+ for (ver = rpc->lowver; ver <= rpc->highver; ver++)
+ (void) rpcb_unset(rpc->prognum, ver, nconf);
+
+ freenetconfigent(nconf);
+}
+
+/*
+ * TLI/XTI functions.
+ */
+
+int
+tlx_init(void)
+{
+ if ((conn_ind_pool = uu_list_pool_create("conn_ind_pool",
+ sizeof (tlx_conn_ind_t), offsetof(tlx_conn_ind_t, link),
+ NULL, UU_LIST_POOL_DEBUG)) == NULL) {
+ error_msg("%s: %s", gettext("Failed to create uu pool"),
+ uu_strerror(uu_error()));
+ return (-1);
+ }
+
+ return (0);
+}
+
+void
+tlx_fini(void)
+{
+ if (conn_ind_pool != NULL) {
+ uu_list_pool_destroy(conn_ind_pool);
+ conn_ind_pool = NULL;
+ }
+}
+
+/*
+ * Checks if the contents of the 2 tlx_info_t structures are equivalent.
+ * If 'isrpc' is false, the address components of the two structures are
+ * compared for equality as part of this. If the two structures are
+ * equivalent B_TRUE is returned, else B_FALSE.
+ */
+boolean_t
+tlx_info_equal(const tlx_info_t *ti, const tlx_info_t *ti2, boolean_t isrpc)
+{
+ return ((isrpc || (memcmp(ti->local_addr.buf, ti2->local_addr.buf,
+ sizeof (struct sockaddr_storage)) == 0)) &&
+ (strcmp(ti->dev_name, ti2->dev_name) == 0));
+}
+
+/*
+ * Attempts to bind an address to the network fd 'fd'. If 'reqaddr' is non-NULL,
+ * it attempts to bind to that requested address, else it binds to a kernel
+ * selected address. In the former case, the function returning success
+ * doesn't guarantee that the requested address was bound (the caller needs to
+ * check). If 'retaddr' is non-NULL, the bound address is returned in it. The
+ * 'qlen' parameter is used to set the connection backlog. If the bind
+ * succeeds 0 is returned, else -1.
+ */
+static int
+tlx_bind(int fd, const struct netbuf *reqaddr, struct netbuf *retaddr, int qlen)
+{
+ struct t_bind breq;
+ struct t_bind bret;
+
+ debug_msg("Entering tlx_bind: req: %x, ret: %x, qlen: %d", reqaddr,
+ retaddr, qlen);
+
+
+ if (retaddr != NULL) { /* caller requests bound address be returned */
+ bret.addr.buf = retaddr->buf;
+ bret.addr.maxlen = retaddr->maxlen;
+ }
+
+ if (reqaddr != NULL) { /* caller requests specific address */
+ breq.addr.buf = reqaddr->buf;
+ breq.addr.len = reqaddr->len;
+ } else {
+ breq.addr.len = 0;
+ }
+ breq.qlen = qlen;
+
+ if (t_bind(fd, &breq, retaddr != NULL ? &bret : NULL) < 0)
+ return (-1);
+
+ if (retaddr != NULL)
+ retaddr->len = bret.addr.len;
+
+ return (0);
+}
+
+static int
+tlx_setsockopt(int fd, int level, int optname, const void *optval,
+ socklen_t optlen)
+{
+ struct t_optmgmt request, reply;
+ struct {
+ struct opthdr sockopt;
+ char data[256];
+ } optbuf;
+
+ debug_msg("Entering tlx_setsockopt, "
+ "fd: %d, level: %d, optname: %d, optval: %x, optlen: %d",
+ fd, level, optname, optval, optlen);
+
+ if (optlen > sizeof (optbuf.data)) {
+ error_msg(gettext("t_optmgmt request too long"));
+ return (-1);
+ }
+
+ optbuf.sockopt.level = level;
+ optbuf.sockopt.name = optname;
+ optbuf.sockopt.len = optlen;
+ (void) memcpy(optbuf.data, optval, optlen);
+
+ request.opt.len = sizeof (struct opthdr) + optlen;
+ request.opt.buf = (char *)&optbuf;
+ request.flags = T_NEGOTIATE;
+
+ reply.opt.maxlen = sizeof (struct opthdr) + optlen;
+ reply.opt.buf = (char *)&optbuf;
+ reply.flags = 0;
+
+ if ((t_optmgmt(fd, &request, &reply) == -1) ||
+ (reply.flags != T_SUCCESS)) {
+ error_msg("t_optmgmt: %s", t_strerror(t_errno));
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * Compare contents of netbuf for equality. Return B_TRUE on a match and
+ * B_FALSE for mismatch.
+ */
+static boolean_t
+netbufs_equal(struct netbuf *n1, struct netbuf *n2)
+{
+ return ((n1->len == n2->len) &&
+ (memcmp(n1->buf, n2->buf, (size_t)n1->len) == 0));
+}
+
+/*
+ * Create a tli/xti endpoint, either bound to the address specified in
+ * 'instance' for non-RPC services, else a kernel chosen address.
+ * Returns -1 on failure, else 0.
+ */
+int
+create_bound_endpoint(const char *fmri, tlx_info_t *tlx_info)
+{
+ int fd;
+ int qlen;
+ struct netbuf *reqaddr;
+ struct netbuf *retaddr;
+ struct netbuf netbuf;
+ struct sockaddr_storage ss;
+ rpc_info_t *rpc = tlx_info->pr_info.ri;
+
+ debug_msg("Entering create_bound_endpoint");
+
+ if ((fd = t_open(tlx_info->dev_name, O_RDWR, NULL)) == -1) {
+ error_msg(gettext("Failed to open transport %s for "
+ "instance %s, proto %s: %s"), tlx_info->dev_name,
+ fmri, tlx_info->pr_info.proto, t_strerror(t_errno));
+ return (-1);
+ }
+
+ if (tlx_info->pr_info.v6only) {
+ int on = 1;
+
+ /* restrict to IPv6 communications only */
+ if (tlx_setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &on,
+ sizeof (on)) == -1) {
+ (void) t_close(fd);
+ return (-1);
+ }
+ }
+
+ /*
+ * Negotiate for the returning of the remote uid for loopback
+ * transports for RPC services. This needs to be done before the
+ * endpoint is bound using t_bind(), so that any requests to it
+ * contain the uid.
+ */
+ if ((rpc != NULL) && (rpc->is_loopback))
+ svc_fd_negotiate_ucred(fd);
+
+ /*
+ * Bind the service's address to the endpoint and setup connection
+ * backlog. In the case of RPC services, we specify a NULL requested
+ * address and accept what we're given, storing the returned address
+ * for later RPC binding. In the case of non-RPC services we specify
+ * the service's associated address.
+ */
+ if (rpc != NULL) {
+ reqaddr = NULL;
+ retaddr = &(rpc->netbuf);
+ } else {
+ reqaddr = &(tlx_info->local_addr);
+ netbuf.buf = (char *)&ss;
+ netbuf.maxlen = sizeof (ss);
+ retaddr = &netbuf;
+ }
+
+ qlen = CONNECTION_BACKLOG; /* ignored for conn/less services */
+
+ if ((tlx_bind(fd, reqaddr, retaddr, qlen) == -1) ||
+ ((reqaddr != NULL) && !netbufs_equal(reqaddr, retaddr))) {
+ error_msg(gettext("Failed to bind to the requested address "
+ "for instance %s, proto %s"), fmri,
+ tlx_info->pr_info.proto);
+ (void) t_close(fd);
+ return (-1);
+ }
+
+ return (fd);
+}
+
+/*
+ * Takes a connection request off 'fd' in the form of a t_call structure
+ * and returns a pointer to it.
+ * Returns NULL on failure, else pointer to t_call structure on success.
+ */
+static struct t_call *
+get_new_conind(int fd)
+{
+ struct t_call *call;
+
+ debug_msg("Entering get_new_conind");
+
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ if ((call = (struct t_call *)t_alloc(fd, T_CALL, T_ALL)) == NULL) {
+ error_msg("t_alloc: %s", t_strerror(t_errno));
+ return (NULL);
+ }
+ if (t_listen(fd, call) < 0) {
+ error_msg("t_listen: %s", t_strerror(t_errno));
+ (void) t_free((char *)call, T_CALL);
+ return (NULL);
+ }
+
+ return (call);
+}
+
+/* Add 'call' to the connection indication queue 'queue'. */
+int
+queue_conind(uu_list_t *queue, struct t_call *call)
+{
+ tlx_conn_ind_t *ci;
+
+ if ((ci = malloc(sizeof (tlx_conn_ind_t))) == NULL) {
+ error_msg(strerror(errno));
+ return (-1);
+ }
+
+ ci->call = call;
+ uu_list_node_init(ci, &ci->link, conn_ind_pool);
+ (void) uu_list_insert_after(queue, NULL, ci);
+
+ return (0);
+}
+
+/*
+ * Remove and return a pointer to the first call on queue 'queue'. However,
+ * if the queue is empty returns NULL.
+ */
+struct t_call *
+dequeue_conind(uu_list_t *queue)
+{
+ struct t_call *ret;
+ tlx_conn_ind_t *ci = uu_list_first(queue);
+
+ if (ci == NULL)
+ return (NULL);
+
+ ret = ci->call;
+ uu_list_remove(queue, ci);
+ free(ci);
+
+ return (ret);
+}
+
+/*
+ * Handle a TLOOK notification received during a t_accept() call.
+ * Returns -1 on failure, else 0.
+ */
+static int
+process_tlook(const char *fmri, tlx_info_t *tlx_info)
+{
+ int event;
+ int fd = tlx_info->pr_info.listen_fd;
+
+ debug_msg("Entering process_tlook:");
+
+ switch (event = t_look(fd)) {
+ case T_LISTEN: {
+ struct t_call *call;
+
+ debug_msg("process_tlook: T_LISTEN event");
+ if ((call = get_new_conind(fd)) == NULL)
+ return (-1);
+ if (queue_conind(tlx_info->conn_ind_queue, call) == -1) {
+ error_msg(gettext("Failed to queue connection "
+ "indication for instance %s"), fmri);
+ (void) t_free((char *)call, T_CALL);
+ return (-1);
+ }
+ break;
+ }
+ case T_DISCONNECT: {
+ /*
+ * Note: In Solaris 2.X (SunOS 5.X) bundled
+ * connection-oriented transport drivers
+ * [ e.g /dev/tcp and /dev/ticots and
+ * /dev/ticotsord (tl)] we do not send disconnect
+ * indications to listening endpoints.
+ * So this will not be seen with endpoints on Solaris
+ * bundled transport devices. However, Streams TPI
+ * allows for this (broken?) behavior and so we account
+ * for it here because of the possibility of unbundled
+ * transport drivers causing this.
+ */
+ tlx_conn_ind_t *cip;
+ struct t_discon *discon;
+
+ debug_msg("process_tlook: T_DISCONNECT event");
+
+ /* LINTED */
+ if ((discon = (struct t_discon *)
+ t_alloc(fd, T_DIS, T_ALL)) == NULL) {
+ error_msg("t_alloc: %s", t_strerror(t_errno));
+ return (-1);
+ }
+ if (t_rcvdis(fd, discon) < 0) {
+ error_msg("t_rcvdis: %s", t_strerror(t_errno));
+ (void) t_free((char *)discon, T_DIS);
+ return (-1);
+ }
+
+ /*
+ * Find any queued connection pending that matches this
+ * disconnect notice and remove from the pending queue.
+ */
+ cip = uu_list_first(tlx_info->conn_ind_queue);
+ while ((cip != NULL) &&
+ (cip->call->sequence != discon->sequence)) {
+ cip = uu_list_next(tlx_info->conn_ind_queue, cip);
+ }
+ if (cip != NULL) { /* match found */
+ uu_list_remove(tlx_info->conn_ind_queue, cip);
+ (void) t_free((char *)cip->call, T_CALL);
+ free(cip);
+ }
+
+ (void) t_free((char *)discon, T_DIS);
+ break;
+ }
+ case -1:
+ error_msg("t_look: %s", t_errno);
+ return (-1);
+ default:
+ error_msg(gettext("do_tlook: unexpected t_look event: %d"),
+ event);
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * This call attempts to t_accept() an incoming/pending TLI connection.
+ * If it is thwarted by a TLOOK, it is deferred and whatever is on the
+ * file descriptor, removed after a t_look. (Incoming connect indications
+ * get queued for later processing and disconnect indications remove a
+ * a queued connection request if a match found).
+ * Returns -1 on failure, else 0.
+ */
+int
+tlx_accept(const char *fmri, tlx_info_t *tlx_info,
+ struct sockaddr_storage *remote_addr)
+{
+ tlx_conn_ind_t *conind;
+ struct t_call *call;
+ int fd;
+ int listen_fd = tlx_info->pr_info.listen_fd;
+
+ debug_msg("Entering tlx_accept: instance: %s", fmri);
+
+ if ((fd = t_open(tlx_info->dev_name, O_RDWR, NULL)) == -1) {
+ error_msg("t_open: %s", t_strerror(t_errno));
+ return (-1);
+ }
+
+ if (tlx_info->pr_info.v6only) {
+ int on = 1;
+
+ /* restrict to IPv6 communications only */
+ if (tlx_setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &on,
+ sizeof (on)) == -1) {
+ (void) t_close(fd);
+ return (-1);
+ }
+ }
+
+ if (t_bind(fd, NULL, NULL) == -1) {
+ error_msg("t_bind: %s", t_strerror(t_errno));
+ (void) t_close(fd);
+ return (-1);
+ }
+
+ /*
+ * Get the next connection indication - first try the pending
+ * queue, then, if none there, get a new one from the file descriptor.
+ */
+ if ((conind = uu_list_first(tlx_info->conn_ind_queue)) != NULL) {
+ debug_msg("taking con off queue");
+ call = conind->call;
+ } else if ((call = get_new_conind(listen_fd)) == NULL) {
+ (void) t_close(fd);
+ return (-1);
+ }
+
+ /*
+ * Accept the connection indication on the newly created endpoint.
+ * If we fail, and it's the result of a tlook, queue the indication
+ * if it isn't already, and go and process the t_look.
+ */
+ if (t_accept(listen_fd, fd, call) == -1) {
+ if (t_errno == TLOOK) {
+ if (uu_list_first(tlx_info->conn_ind_queue) == NULL) {
+ /*
+ * We are first one to have to defer accepting
+ * and start the pending connections list.
+ */
+ if (queue_conind(tlx_info->conn_ind_queue,
+ call) == -1) {
+ error_msg(gettext(
+ "Failed to queue connection "
+ "indication for instance %s"),
+ fmri);
+ (void) t_free((char *)call, T_CALL);
+ return (-1);
+ }
+ }
+ (void) process_tlook(fmri, tlx_info);
+ } else { /* non-TLOOK accept failure */
+ error_msg("%s: %s", "t_accept failed",
+ t_strerror(t_errno));
+ /*
+ * If we were accepting a queued connection, dequeue
+ * it.
+ */
+ if (uu_list_first(tlx_info->conn_ind_queue) != NULL)
+ (void) dequeue_conind(tlx_info->conn_ind_queue);
+ (void) t_free((char *)call, T_CALL);
+ }
+
+ (void) t_close(fd);
+ return (-1);
+ }
+
+ /* Copy remote address into address parameter */
+ (void) memcpy(remote_addr, call->addr.buf,
+ MIN(call->addr.len, sizeof (*remote_addr)));
+
+ /* If we were accepting a queued connection, dequeue it. */
+ if (uu_list_first(tlx_info->conn_ind_queue) != NULL)
+ (void) dequeue_conind(tlx_info->conn_ind_queue);
+ (void) t_free((char *)call, T_CALL);
+
+ return (fd);
+}
+
+/* protocol independent network fd close routine */
+void
+close_net_fd(instance_t *inst, int fd)
+{
+ debug_msg("Entering close_net_fd: inst: %s, fd: %d", inst->fmri, fd);
+
+ if (inst->config->basic->istlx) {
+ (void) t_close(fd);
+ } else {
+ (void) close(fd);
+ }
+}
+
+/*
+ * Consume some data from the given endpoint of the given wait-based instance.
+ */
+void
+consume_wait_data(instance_t *inst, int fd)
+{
+ int flag;
+ char buf[50]; /* same arbitrary size as old inetd */
+
+ debug_msg("Entering consume_wait_data: inst: %s, fd: %d", inst->fmri,
+ fd);
+
+ if (inst->config->basic->istlx) {
+ (void) t_rcv(fd, buf, sizeof (buf), &flag);
+ } else {
+ (void) recv(fd, buf, sizeof (buf), 0);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/inetd/util.c b/usr/src/cmd/cmd-inet/usr.lib/inetd/util.c
new file mode 100644
index 0000000000..4772a71162
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/inetd/util.c
@@ -0,0 +1,365 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * General utility routines.
+ */
+
+#include <syslog.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <strings.h>
+#include <time.h>
+#include <errno.h>
+#include <libintl.h>
+#include <unistd.h>
+#include "inetd_impl.h"
+
+
+/* location of inetd's debug log file */
+#define DEBUG_LOG_FILE "/var/tmp/inetd.log"
+
+/* size of buffer used in msg() to expand printf() like messages into */
+#define MSG_BUF_SIZE 1024
+
+/* size of buffer used in msg() to store a date/time string */
+#define TIME_BUF_SIZE 50
+
+/* number of pollfd we grow the pollfd array by at a time in set_pollfd() */
+#define POLLFDS_GROWTH_SIZE 16
+
+/* enumeration of message types supported by msg() */
+typedef enum {
+ MT_ERROR,
+ MT_DEBUG,
+ MT_WARN
+} si_msg_type_t;
+
+/*
+ * Collection of information for each method type.
+ * NOTE: This table is indexed into using the instance_method_t
+ * enumeration, so the ordering needs to be kept in synch.
+ */
+method_type_info_t methods[] = {
+ {IM_START, START_METHOD_NAME, IIS_NONE},
+ {IM_ONLINE, ONLINE_METHOD_NAME, IIS_ONLINE},
+ {IM_OFFLINE, OFFLINE_METHOD_NAME, IIS_OFFLINE},
+ {IM_DISABLE, DISABLE_METHOD_NAME, IIS_DISABLED},
+ {IM_REFRESH, REFRESH_METHOD_NAME, IIS_ONLINE},
+ {IM_NONE, "none", IIS_NONE}
+};
+
+struct pollfd *poll_fds = NULL;
+nfds_t num_pollfds;
+static FILE *debug_fp = NULL;
+
+boolean_t logging_enabled;
+
+void
+msg_init(void)
+{
+ openlog(SYSLOG_IDENT, LOG_PID|LOG_CONS, LOG_DAEMON);
+
+ /* Try once at startup to open the log file. */
+ debug_fp = fopen(DEBUG_LOG_FILE, "r+");
+ if (debug_fp != NULL)
+ (void) fseeko(debug_fp, 0, SEEK_END);
+ logging_enabled = B_TRUE;
+}
+
+void
+msg_fini(void)
+{
+ logging_enabled = B_FALSE;
+ if (debug_fp != NULL) {
+ (void) fclose(debug_fp);
+ debug_fp = NULL;
+ }
+ closelog();
+}
+
+/*
+ * Outputs a msg. If 'type' is set tp MT_ERROR or MT_WARN the message goes
+ * to syslog with severitys LOG_ERROR and LOG_WARN respectively. For all
+ * values of 'type' the message is written to the debug log file, if it
+ * was openable when inetd started.
+ */
+static void
+msg(si_msg_type_t type, const char *format, va_list ap)
+{
+ /*
+ * Use a stack buffer so we stand more chance of reporting a
+ * memory shortage failure.
+ */
+ char buf[MSG_BUF_SIZE];
+ char timebuf[TIME_BUF_SIZE];
+
+ if (!logging_enabled)
+ return;
+
+ (void) vsnprintf(buf, sizeof (buf), format, ap);
+
+ /*
+ * Log error and warning messages to syslog with appropriate severity.
+ */
+ if (type == MT_ERROR) {
+ syslog(LOG_ERR, "%s", buf);
+ } else if (type == MT_WARN) {
+ syslog(LOG_WARNING, "%s", buf);
+ }
+
+ if (debug_fp != NULL) {
+ struct tm tms;
+ time_t tm;
+
+ /*
+ * We managed to open the log file at startup. Log all
+ * message types there - in addition to syslog.
+ */
+ tm = time(NULL);
+ (void) strftime(timebuf, sizeof (timebuf), NULL,
+ localtime_r(&tm, &tms));
+ (void) fputs(timebuf, debug_fp);
+ (void) fputs(": ", debug_fp);
+
+ if (type == MT_ERROR) {
+ (void) fputs("ERROR: ", debug_fp);
+ } else if (type == MT_DEBUG) {
+ (void) fputs("DEBUG: ", debug_fp);
+ } else if (type == MT_WARN) {
+ (void) fputs("WARN: ", debug_fp);
+ }
+
+ (void) fputs(buf, debug_fp);
+
+ if (buf[strlen(buf) - 1] != '\n')
+ (void) fputc('\n', debug_fp);
+
+ (void) fflush(debug_fp);
+ }
+}
+
+/*
+ * Output a warning message. Unlike error_msg(), syslog doesn't get told
+ * to log to the console if syslogd isn't around.
+ */
+void
+warn_msg(const char *format, ...)
+{
+ va_list ap;
+
+ closelog();
+ openlog(SYSLOG_IDENT, LOG_PID, LOG_DAEMON);
+
+ va_start(ap, format);
+ msg(MT_WARN, format, ap);
+ va_end(ap);
+
+ closelog();
+ openlog(SYSLOG_IDENT, LOG_PID|LOG_CONS, LOG_DAEMON);
+}
+
+void
+debug_msg(const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ msg(MT_DEBUG, format, ap);
+ va_end(ap);
+}
+
+void
+error_msg(const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ msg(MT_ERROR, format, ap);
+ va_end(ap);
+}
+
+void
+poll_fini(void)
+{
+ if (poll_fds != NULL) {
+ free(poll_fds);
+ poll_fds = NULL;
+ }
+}
+
+struct pollfd *
+find_pollfd(int fd)
+{
+ nfds_t n;
+
+ for (n = 0; n < num_pollfds; n++) {
+ if (poll_fds[n].fd == fd)
+ return (&(poll_fds[n]));
+ }
+ return (NULL);
+}
+
+int
+set_pollfd(int fd, uint16_t events)
+{
+ struct pollfd *p;
+ int i;
+
+ debug_msg("Entering set_pollfd, fd: %d, num_pollfds: %d, fds: %x,"
+ " events: %hu", fd, num_pollfds, poll_fds, events);
+
+ p = find_pollfd(fd);
+ if ((p == NULL) && ((p = find_pollfd(-1)) == NULL)) {
+ if ((p = realloc(poll_fds,
+ ((num_pollfds + POLLFDS_GROWTH_SIZE) *
+ sizeof (struct pollfd)))) == NULL) {
+ return (-1);
+ }
+ poll_fds = p;
+
+ for (i = 1; i < POLLFDS_GROWTH_SIZE; i++)
+ poll_fds[num_pollfds + i].fd = -1;
+
+ p = &poll_fds[num_pollfds];
+ num_pollfds += POLLFDS_GROWTH_SIZE;
+ }
+
+ p->fd = fd;
+ p->events = events;
+ p->revents = 0;
+
+ return (0);
+}
+
+void
+clear_pollfd(int fd)
+{
+ struct pollfd *p;
+
+ if ((p = find_pollfd(fd)) != NULL) {
+ p->fd = -1;
+ p->events = 0;
+ p->revents = 0;
+ }
+}
+
+boolean_t
+isset_pollfd(int fd)
+{
+ struct pollfd *p = find_pollfd(fd);
+
+ return ((p != NULL) && (p->revents & POLLIN));
+}
+
+/*
+ * An extension of read() that keeps retrying until either the full request has
+ * completed, the other end of the connection/pipe is closed, no data is
+ * readable for a non-blocking socket/pipe, or an unexpected error occurs.
+ * Returns 0 if the data is successfully read, 1 if the other end of the pipe/
+ * socket is closed or there's nothing to read from a non-blocking socket/pipe,
+ * else -1 if an unexpected error occurs.
+ */
+int
+safe_read(int fd, void *buf, size_t sz)
+{
+ int ret;
+ size_t cnt = 0;
+ char *cp = (char *)buf;
+
+ if (sz == 0)
+ return (0);
+
+ do {
+ switch (ret = read(fd, cp + cnt, sz - cnt)) {
+ case 0: /* other end of pipe/socket closed */
+ return (1);
+ case -1:
+ if (errno == EAGAIN) { /* nothing to read */
+ return (1);
+ } else if (errno != EINTR) {
+ error_msg(gettext("Unexpected read error: %s"),
+ strerror(errno));
+ return (-1);
+ }
+ break;
+
+ default:
+ cnt += ret;
+ }
+ } while (cnt != sz);
+
+ return (0);
+}
+
+/*
+ * Return B_TRUE if instance 'inst' has exceeded its configured maximum
+ * concurrent copies limit, else B_FALSE.
+ */
+boolean_t
+copies_limit_exceeded(instance_t *inst)
+{
+ /* any value <=0 means that copies limits are disabled */
+ return ((inst->config->basic->max_copies > 0) &&
+ (inst->copies >= inst->config->basic->max_copies));
+}
+
+/*
+ * Cancel the method/con-rate offline timer associated with the instance.
+ */
+void
+cancel_inst_timer(instance_t *inst)
+{
+ (void) iu_cancel_timer(timer_queue, inst->timer_id, NULL);
+ inst->timer_id = -1;
+}
+
+/*
+ * Cancel the bind retry timer associated with the instance.
+ */
+void
+cancel_bind_timer(instance_t *inst)
+{
+ (void) iu_cancel_timer(timer_queue, inst->bind_timer_id, NULL);
+ inst->bind_timer_id = -1;
+}
+
+void
+enable_blocking(int fd)
+{
+ int flags = fcntl(fd, F_GETFL, 0);
+ (void) fcntl(fd, F_SETFL, (flags & ~O_NONBLOCK));
+}
+
+void
+disable_blocking(int fd)
+{
+ int flags = fcntl(fd, F_GETFL, 0);
+ (void) fcntl(fd, F_SETFL, (flags | O_NONBLOCK));
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/inetd/wait.c b/usr/src/cmd/cmd-inet/usr.lib/inetd/wait.c
new file mode 100644
index 0000000000..9278c2ca17
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/inetd/wait.c
@@ -0,0 +1,413 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * This file contains a set of routines used to perform wait based method
+ * reaping.
+ */
+
+#include <wait.h>
+#include <sys/param.h>
+#include <fcntl.h>
+#include <libcontract.h>
+#include <errno.h>
+#include <libintl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/resource.h>
+#include "inetd_impl.h"
+
+/* inetd's open file limit, set in method_init() */
+#define INETD_NOFILE_LIMIT RLIM_INFINITY
+
+/* structure used to represent an active method process */
+typedef struct {
+ int fd; /* fd of process's /proc psinfo file */
+ /* associated contract id if known, else -1 */
+ ctid_t cid;
+ pid_t pid;
+ instance_t *inst; /* pointer to associated instance */
+ instance_method_t method; /* the method type running */
+ uu_list_node_t link;
+} method_el_t;
+
+
+static void unregister_method(method_el_t *);
+
+
+/* list of currently executing method processes */
+static uu_list_pool_t *method_pool = NULL;
+static uu_list_t *method_list = NULL;
+
+/*
+ * File limit saved during initialization before modification, so that it can
+ * be reverted back to for inetd's exec'd methods.
+ */
+static struct rlimit saved_file_limit;
+
+/*
+ * Setup structures used for method termination monitoring.
+ * Returns -1 if an allocation failure occurred, else 0.
+ */
+int
+method_init(void)
+{
+ struct rlimit rl;
+
+ debug_msg("Entering method_init");
+
+ /*
+ * Save aside the old file limit and impose one large enough to support
+ * all the /proc file handles we could have open.
+ */
+
+ (void) getrlimit(RLIMIT_NOFILE, &saved_file_limit);
+
+ rl.rlim_cur = rl.rlim_max = INETD_NOFILE_LIMIT;
+ if (setrlimit(RLIMIT_NOFILE, &rl) == -1) {
+ error_msg("Failed to set file limit: %s", strerror(errno));
+ return (-1);
+ }
+
+ if ((method_pool = uu_list_pool_create("method_pool",
+ sizeof (method_el_t), offsetof(method_el_t, link), NULL,
+ UU_LIST_POOL_DEBUG)) == NULL) {
+ error_msg("%s: %s", gettext("Failed to create method pool"),
+ uu_strerror(uu_error()));
+ return (-1);
+ }
+
+ if ((method_list = uu_list_create(method_pool, NULL, 0)) == NULL) {
+ error_msg("%s: %s",
+ gettext("Failed to create method list"),
+ uu_strerror(uu_error()));
+ /* let method_fini() clean-up */
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * Tear-down structures created in method_init().
+ */
+void
+method_fini(void)
+{
+ debug_msg("Entering method_fini");
+
+ if (method_list != NULL) {
+ method_el_t *me;
+
+ while ((me = uu_list_first(method_list)) != NULL)
+ unregister_method(me);
+
+ (void) uu_list_destroy(method_list);
+ method_list = NULL;
+ }
+ if (method_pool != NULL) {
+ (void) uu_list_pool_destroy(method_pool);
+ method_pool = NULL;
+ }
+
+ /* revert file limit */
+ method_preexec();
+}
+
+/*
+ * Revert file limit back to pre-initialization one. This shouldn't fail as
+ * long as its called *after* descriptor cleanup.
+ */
+void
+method_preexec(void)
+{
+ (void) setrlimit(RLIMIT_NOFILE, &saved_file_limit);
+}
+
+
+/*
+ * Callback function that handles the timeout of an instance's method.
+ * 'arg' points at the method_el_t representing the method.
+ */
+/* ARGSUSED0 */
+static void
+method_timeout(iu_tq_t *tq, void *arg)
+{
+ method_el_t *mp = arg;
+
+ error_msg(gettext("The %s method of instance %s timed-out"),
+ methods[mp->method].name, mp->inst->fmri);
+
+ mp->inst->timer_id = -1;
+
+ if (mp->method == IM_START) {
+ process_start_term(mp->inst);
+ } else {
+ process_non_start_term(mp->inst, IMRET_FAILURE);
+ }
+
+ unregister_method(mp);
+}
+
+/*
+ * Registers the attributes of a running method passed as arguments so that
+ * the method's termination is noticed and any further processing of the
+ * associated instance is carried out. The function also sets up any
+ * necessary timers so we can detect hung methods.
+ * Returns -1 if either it failed to open the /proc psinfo file which is used
+ * to monitor the method process, it failed to setup a required timer or
+ * memory allocation failed; else 0.
+ */
+int
+register_method(instance_t *ins, pid_t pid, ctid_t cid, instance_method_t mthd)
+{
+ char path[MAXPATHLEN];
+ int fd;
+ method_el_t *me;
+
+ debug_msg("Entering register_method: inst: %s, pid: %d, mthd: %s",
+ ins->fmri, pid, methods[mthd].name);
+
+ /* open /proc psinfo file of process to listen for POLLHUP events on */
+ (void) snprintf(path, sizeof (path), "/proc/%u/psinfo", pid);
+ for (;;) {
+ if ((fd = open(path, O_RDONLY)) >= 0) {
+ break;
+ } else if (errno != EINTR) {
+ /*
+ * Don't output an error for ENOENT; we get this
+ * if a method has gone away whilst we were stopped,
+ * and we're now trying to re-listen for it.
+ */
+ if (errno != ENOENT) {
+ error_msg(gettext("Failed to open %s: %s"),
+ path, strerror(errno));
+ }
+ return (-1);
+ }
+ }
+
+ /* add method record to in-memory list */
+ if ((me = calloc(1, sizeof (method_el_t))) == NULL) {
+ error_msg(strerror(errno));
+ (void) close(fd);
+ return (-1);
+ }
+ me->fd = fd;
+ me->inst = (instance_t *)ins;
+ me->method = mthd;
+ me->pid = pid;
+ me->cid = cid;
+
+ /* register a timeout for the method, if required */
+ if (mthd != IM_START) {
+ method_info_t *mi = ins->config->methods[mthd];
+
+ if (mi->timeout > 0) {
+ assert(ins->timer_id == -1);
+ ins->timer_id = iu_schedule_timer(timer_queue,
+ mi->timeout, method_timeout, me);
+ if (ins->timer_id == -1) {
+ error_msg(gettext(
+ "Failed to schedule method timeout"));
+ free(me);
+ (void) close(fd);
+ return (-1);
+ }
+ }
+ }
+
+ /*
+ * Add fd of psinfo file to poll set, but pass 0 for events to
+ * poll for, so we should only get a POLLHUP event on the fd.
+ */
+ if (set_pollfd(fd, 0) == -1) {
+ cancel_inst_timer(ins);
+ free(me);
+ (void) close(fd);
+ return (-1);
+ }
+
+ uu_list_node_init(me, &me->link, method_pool);
+ (void) uu_list_insert_after(method_list, NULL, me);
+
+ return (0);
+}
+
+/*
+ * A counterpart to register_method(), this function stops the monitoring of a
+ * method process for its termination.
+ */
+static void
+unregister_method(method_el_t *me)
+{
+ debug_msg("Entering unregister_method: inst: %s, pid: %d, mthd: %s",
+ me->inst->fmri, me->pid, methods[me->method].name);
+
+ /* cancel any timer associated with the method */
+ if (me->inst->timer_id != -1)
+ cancel_inst_timer(me->inst);
+
+ /* stop polling on the psinfo file fd */
+ clear_pollfd(me->fd);
+ (void) close(me->fd);
+
+ /* remove method record from list */
+ uu_list_remove(method_list, me);
+
+ free(me);
+}
+
+/*
+ * Unregister all methods associated with instance 'inst'.
+ */
+void
+unregister_instance_methods(const instance_t *inst)
+{
+ method_el_t *me = uu_list_first(method_list);
+
+ while (me != NULL) {
+ if (me->inst == inst) {
+ method_el_t *tmp = me;
+
+ me = uu_list_next(method_list, me);
+ unregister_method(tmp);
+ } else {
+ me = uu_list_next(method_list, me);
+ }
+ }
+}
+
+/*
+ * Process any terminated methods. For each method determined to have
+ * terminated, the function determines its return value and calls the
+ * appropriate handling function, depending on the type of the method.
+ */
+void
+process_terminated_methods(void)
+{
+ method_el_t *me = uu_list_first(method_list);
+
+ debug_msg("Entering process_terminated_methods");
+
+ while (me != NULL) {
+ struct pollfd *pfd;
+ pid_t pid;
+ int status;
+ int ret;
+ method_el_t *tmp;
+
+ pfd = find_pollfd(me->fd);
+
+ /*
+ * We expect to get a POLLHUP back on the fd of the process's
+ * open psinfo file from /proc when the method terminates.
+ * A POLLERR could(?) mask a POLLHUP, so handle this
+ * also.
+ */
+ if ((pfd->revents & (POLLHUP|POLLERR)) == 0) {
+ me = uu_list_next(method_list, me);
+ continue;
+ }
+
+ /* get the method's exit code (no need to loop for EINTR) */
+ pid = waitpid(me->pid, &status, WNOHANG);
+
+ switch (pid) {
+ case 0: /* child still around */
+ /*
+ * Either poll() is sending us invalid POLLHUP events
+ * or is flagging a POLLERR on the fd. Neither should
+ * happen, but in the event they do, ignore this fd
+ * this time around and wait out the termination
+ * of its associated method. This may result in
+ * inetd swiftly looping in event_loop(), but means
+ * we don't miss the termination of a method.
+ */
+ me = uu_list_next(method_list, me);
+ continue;
+
+ case -1: /* non-existent child */
+ assert(errno == ECHILD);
+ /*
+ * the method must not be owned by inetd due to it
+ * persisting over an inetd restart. Let's assume the
+ * best, that it was successful.
+ */
+ ret = IMRET_SUCCESS;
+ break;
+
+ default: /* child terminated */
+ if (WIFEXITED(status)) {
+ ret = WEXITSTATUS(status);
+ debug_msg("process %d of instance %s returned "
+ "%d", pid, me->inst->fmri, ret);
+ } else if (WIFSIGNALED(status)) {
+ /*
+ * Terminated by signal. This may be due
+ * to a kill that we sent from a disable or
+ * offline event. We flag it as a failure, but
+ * this flagged failure will only be processed
+ * in the case of non-start methods, or when
+ * the instance is still enabled.
+ */
+ debug_msg("process %d of instance %s exited "
+ "due to signal %d", pid, me->inst->fmri,
+ WTERMSIG(status));
+ ret = IMRET_FAILURE;
+ } else {
+ /*
+ * Can we actually get here? Don't think so.
+ * Treat it as a failure, anyway.
+ */
+ debug_msg("waitpid() for %s method of "
+ "instance %s returned %d",
+ methods[me->method].name, me->inst->fmri,
+ status);
+ ret = IMRET_FAILURE;
+ }
+ }
+
+ remove_method_ids(me->inst, me->pid, me->cid, me->method);
+
+ /* continue state transition processing of the instance */
+ if (me->method != IM_START) {
+ process_non_start_term(me->inst, ret);
+ } else {
+ process_start_term(me->inst);
+ }
+
+ if (me->cid != -1)
+ (void) abandon_contract(me->cid);
+
+ tmp = me;
+ me = uu_list_next(method_list, me);
+ unregister_method(tmp);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mipagent/Makefile b/usr/src/cmd/cmd-inet/usr.lib/mipagent/Makefile
new file mode 100644
index 0000000000..17beee0787
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mipagent/Makefile
@@ -0,0 +1,151 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+include $(SRC)/cmd/Makefile.cmd
+include $(SRC)/cmd/cmd-inet/Makefile.cmd-inet
+
+PROG= mipagent
+
+LCLOBJS=aaa.o \
+ agent.o \
+ agentID.o \
+ agentInit.o \
+ agentKernelIntfce.o \
+ agentNet.o \
+ agentPeriodic.o \
+ agentSaveState.o \
+ auth.o \
+ utils.o \
+ snmp_tree.o \
+ snmp_stub.o \
+ snmp_trap.o \
+ snmp_appl.o \
+ snmp_faCOAEntry.o \
+ snmp_faVisitorEntry.o \
+ snmp_haCounterEntry.o \
+ snmp_haMobilityBindingEntry.o \
+ snmp_maAdvConfigEntry.o \
+ snmp_mipSecAssocEntry.o \
+ snmp_mipSecViolationEntry.o \
+ mipagentstat_server.o \
+ pool.o \
+ hash.o \
+ thq.o \
+ setup.o
+
+COMOBJS= conflib.o
+
+OBJS= $(LCLOBJS) $(COMOBJS)
+
+HDRS= aaa.h \
+ agent.h \
+ agentKernelIntfce.h \
+ auth.h \
+ hash.h \
+ mip.h \
+ snmp_stub.h \
+ pool.h \
+ setup.h \
+ thq.h
+
+# Auxiliary files for the SNMP subagent
+SNMP_FILES= mipagent.acl mipagent.reg
+SNMP_CONF_DIR= $(ROOT)/etc/snmp/conf
+INS_SNMP_FILES= $(SNMP_FILES:%=$(SNMP_CONF_DIR)/%)
+$(SNMP_CONF_DIR)/mipagent.acl:= FILEMODE= 600
+$(SNMP_CONF_DIR)/mipagent.reg:= FILEMODE= 644
+$(INS_SNMP_FILES):= OWNER= root
+$(INS_SNMP_FILES):= GROUP= sys
+$(SNMP_CONF_DIR):= OWNER= root
+$(SNMP_CONF_DIR):= GROUP= sys
+
+SRCS= $(LCLOBJS:%.o=%.c) $(COMOBJS:%.o=$(CMDINETCOMMONDIR)/%.c)
+
+SNMPINC = -I$(SRC)/cmd/agents/snmp/snmplib -I$(SRC)/cmd/agents/snmp/agent
+
+LDLIBS += -lxnet -lnsl -lsocket -lssagent -lmd5
+CPPFLAGS += -DMIP_DEBUG -I$(CMDINETCOMMONDIR) $(SNMPINC) -DNO_SIMULTANEOUS \
+ -DINI -D_POSIX_PTHREAD_SEMANTICS -D_REENTRANT
+
+# mipagent uses the ancillary data feature which is available
+# only through UNIX 98 standards version of Socket interface.
+# these #defines are required to use UNIX 98 interfaces
+CPPFLAGS += -D_XOPEN_SOURCE=500 -D__EXTENSIONS__
+
+#
+# Note that we include KEY_DISTRIBUTION in LINTFLAGS because otherwise
+# lint thinks aaaCreateKey() can be static, which is really not right.
+#
+LINTFLAGS += -DKEY_DISTRIBUTION
+
+#
+# Turn off some of lint's argument checking to workaround the fact
+# that we use both libsocket *and* libxnet interfaces (whose prototypes
+# don't always agree).
+#
+LINTFLAGS += -erroff=E_INCONS_ARG_DECL2 -erroff=E_INCONS_ARG_USED2 \
+ -erroff=E_INCONS_VAL_TYPE_DECL2
+
+#
+# We turn off these errors work around brokenness in the snmp interfaces
+# (specifically: they require us to define and export data for their
+# use, which lint has no clue about).
+#
+LINTFLAGS += -erroff=E_NAME_DEF_NOT_USED2 -erroff=E_GLOBAL_COULD_BE_STATIC2
+
+.KEEP_STATE:
+
+.PARALLEL: $(OBJS) $(INS_SNMP_FILES)
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+ $(LINK.c) -o $@ $(OBJS) $(LDLIBS)
+ $(POST_PROCESS)
+
+include ../Makefile.lib
+
+install: all $(ROOTLIBINETPROG) $(SNMP_CONF_DIR) .WAIT $(INS_SNMP_FILES)
+
+$(SNMP_CONF_DIR):
+ $(INS.dir)
+
+$(SNMP_CONF_DIR)/%: %
+ $(INS.file)
+
+clean:
+ $(RM) $(OBJS)
+
+check: $(HDRS:%.h=%.check) $(SRCS:%.c=%.check)
+
+%.check: %.c
+ $(DOT_C_CHECK)
+
+lint: lint_SRCS
+
+include $(SRC)/cmd/Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mipagent/aaa.c b/usr/src/cmd/cmd-inet/usr.lib/mipagent/aaa.c
new file mode 100644
index 0000000000..678d8fcba6
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mipagent/aaa.c
@@ -0,0 +1,2687 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1999-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * file: aaa.c
+ *
+ * This file processes the Diameter AAA requests from mipagent.
+ * This file also contains the routines used to parse and process the
+ * Diameter messages.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <syslog.h>
+
+#include "mip.h"
+#include "agent.h"
+#include "setup.h"
+#include "hash.h"
+#include "aaa.h"
+
+static int gbl_TCPSocket = -1; /* Global socket */
+
+static int gbl_hashInitialized = 0;
+in_port_t gbl_aaaPort = AAA_PORT;
+char gbl_aaaHost[MAX_SERVER_NAME_LEN];
+static HashTable naiHash;
+
+extern char maNai[];
+extern HashTable mipAgentHash;
+extern HashTable faVisitorHash;
+extern AAA_Protocol_Code aaaProtocol;
+extern ForeignAgentCounters faCounters;
+
+static pthread_t aaaThreadId = 0;
+
+/* External prototypes . . . this should be somewhere else -- WORK:todo */
+extern int hexdump(char *, unsigned char *, int);
+extern void forwardFromFAToHA(MessageHdr *, MaAdvConfigEntry *, boolean_t);
+extern void rejectFromFAToMN(MessageHdr *, MaAdvConfigEntry *, int);
+extern void FreeMessageHdr(MessageHdr *);
+MessageHdr *AllocateMessageHdr();
+extern boolean_t forcefullyDeregisterMN(ipaddr_t *, ipaddr_t, ipaddr_t);
+extern boolean_t mkPendingFAVEHashLookup(void *, uint32_t, uint32_t, uint32_t);
+extern void delFAVE(HashTable *, FaVisitorEntry **, ipaddr_t, uint32_t);
+extern void enableService();
+/*
+ * Internal Prototypes
+ */
+
+static int sendCloseSessionAnswer(AAA_Packet *, char *, size_t, boolean_t);
+static size_t aaaAddAvp(AAA_AVPCode avpCode, unsigned char *dest,
+ size_t destLen, void *data, size_t dataLen);
+static AAA_AVP *aaaFindAvpByCode(AAA_Packet *packet, AAA_AVPCode avpCode);
+
+static int readTCPPacket(unsigned char *buffer, uint32_t bufLen);
+static void *mainAAAThread();
+static int sendTCPPacket(unsigned char *buffer, uint32_t length);
+static void processAuthFailure(MessageHdr *msgHdr,
+ char *mnNAI, size_t mnNAILen, uint32_t result);
+#ifdef TEST_DIAMETER
+static void processOpenSessionRequest(AAA_Packet *packet, char *mnNAI,
+ size_t mnNAILen);
+static void processOpenSessionIndicationResponse(AAA_Packet *packet,
+ char *mnNAI, size_t mnNAILen);
+static void aaaGenerateKey(unsigned char *buffer, size_t buffLen);
+static uint32_t aaaGenerateSpi();
+#endif
+static void processOpenSessionAnswer(AAA_Packet *packet, char *mnNAI,
+ size_t mnNAILen, uint32_t resultCode);
+static void processOpenSessionAnswerRadius(AAA_Packet *packet, char *mnNAI,
+ size_t mnNAILen, AAA_HashEntry *);
+static void processOpenSessionAnswerRadiusHA(AAA_Packet *packet, char *mnNAI,
+ size_t mnNAILen, AAA_HashEntry *, uint32_t resultCode);
+static void processOpenSessionIndication(AAA_Packet *packet, char *mnNAI,
+ size_t mnNAILen);
+static void *aaaFindAvpPtr(AAA_Packet *packet, AAA_AVPCode avpCode,
+ size_t *length);
+static boolean_t aaaFindAvpInt(AAA_Packet *, AAA_AVPCode, int32_t *);
+static int sendCloseSession(AAA_Packet *srcPacket, char *mnNAI,
+ size_t mnNAILen);
+static void aaaSendErrorResponse(uint32_t commandCode, int32_t returnCode,
+ char *mnNAI, size_t mnNAILen, uint32_t handle);
+
+/* Not prototyped in .h file for cyclic dependency problems */
+int aaaSendRegistrationReply(MessageHdr *, size_t, ipaddr_t, ipaddr_t);
+
+
+extern int logVerbosity; /* WORK -- This should be in a .h file. PRC? */
+extern ipaddr_t getClosestInterfaceAddr(ipaddr_t dest); /* Where? WORK */
+extern MaAdvConfigEntry *getFirstInterface(); /* Where? WORK */
+extern int dispatchMsgToThread(MessageHdr **messageHdr);
+extern boolean_t advBusy;
+extern struct hash_table maAdvConfigHash;
+
+/*
+ * Function: aaaAddAvp
+ *
+ * Arguments: unsigned char *dest, size_t destLen, uint32_t avpCode,
+ * void *data, size_t dataLen
+ *
+ * Description: This function will build an AVP perform all byte-ordering/
+ * copying operations, and will return the length of the
+ * destination AVP.
+ *
+ * Returns: size_t (length of block added, zero on error)
+ */
+static size_t
+aaaAddAvp(AAA_AVPCode avpCode, unsigned char *dest, size_t destLen,
+ void *data, size_t dataLen)
+{
+ AAA_AVP staticAvp;
+ AAA_AVP *avp;
+ int32_t TempInt;
+
+ /* First, check to make sure it will fit */
+ if ((dataLen + (2 * sizeof (uint32_t))) > destLen) {
+ syslog(LOG_ERR, "ERROR: avp will not fit in dest! ("
+ "avpSize = %d, destSize = %d, avpCode = %d)",
+ dataLen + (2 * sizeof (uint32_t)), destLen, avpCode);
+ return (0);
+ }
+
+ /* Now, build the avp */
+ staticAvp.avpCode = htonl(avpCode);
+
+ /* Note: dataLen = size of header */
+ staticAvp.length = htonl(2 * sizeof (uint32_t) + dataLen);
+
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ avp = (AAA_AVP *)dest;
+ (void) memcpy(avp, &staticAvp, 2 * sizeof (uint32_t));
+
+ switch (avpCode) {
+ /* These fields require no mangling */
+ case MOBILE_NODE_NAI:
+ case FOREIGN_AGENT_NAI:
+ case REGISTRATION_REQUEST:
+ case MOBILE_NODE_RESPONSE:
+ case REGISTRATION_REPLY:
+ case MN_FA_KEY:
+ case FA_HA_KEY:
+ case HA_FA_KEY:
+ case FA_MN_KEY:
+ case MN_HA_KEY:
+ case HA_MN_KEY:
+ case MN_FA_CHALLENGE_VALUE:
+ (void) memcpy(avp->data, data, dataLen);
+ break;
+
+ /* These fields require mangling (They're 32 bit numbers) */
+ case NUMBER_OF_CHALLENGE_BYTES_IN_RR:
+ case MOBILE_NODE_HOME_ADDRESS:
+ case HOME_AGENT_ADDRESS:
+ case RESULT_CODE:
+ case MN_FA_SPI:
+ case FA_HA_SPI:
+ case SESSION_TIMEOUT:
+ case MN_HA_SPI:
+ case SESSION_TIMEOUT_1:
+ case SESSION_TIME:
+ case FOREIGN_AGENT_ADDRESS:
+ case IS_FROM_HA:
+ case MN_AAA_SPI:
+ case REV_TUN:
+ case MN_HANDLE:
+ case RELEASE_INDICATOR:
+ if (dataLen != sizeof (uint32_t)) {
+ syslog(LOG_ERR, "Internal error: avp should have a "
+ "length of %d, not %d!", sizeof (uint32_t),
+ dataLen);
+ return (0);
+ }
+ (void) memcpy(&TempInt, data, sizeof (uint32_t));
+ TempInt = htonl(TempInt);
+ (void) memcpy(avp->data, &TempInt, sizeof (uint32_t));
+ break;
+
+ default: /* Error! */
+ syslog(LOG_ERR, "ERROR: Invalid AVP Code! <%d>\n", avpCode);
+ return (0);
+ } /* switch (avpCode) */
+
+ return (ntohl(staticAvp.length));
+} /* aaaAddAvp */
+
+/*
+ * Function: addNaiToHash
+ *
+ * Arguments: char *mnNAI, size_t mnNAILen,
+ * unsigned char *mnChallenge, uint32_t mnChallengeLen,
+ * ipaddr_t homeAddress, ipaddr_t homeAgentAddress,
+ * void *messageHdr
+ *
+ * Description: This function will add the nai to our local hash. It
+ * checks to make sure the add was successful (value unique)
+ * It is called from the foreign agent or home agent when it first
+ * receives a NAI that it has not seen before. The has entry is
+ * used to store any interim data that is needed for either
+ * the home or foreign agents.
+ *
+ * Returns: int (zero on success)
+ */
+static int
+addNaiToHash(char *mnNAI, size_t mnNAILen,
+ unsigned char *mnChallenge, uint32_t mnChallengeLen,
+ ipaddr_t homeAddress, ipaddr_t homeAgentAddress,
+ void *messageHdr)
+{
+ AAA_HashEntry *p;
+ int rc;
+
+ /* Allocate and initialize HashEntry */
+ p = (AAA_HashEntry *)malloc(sizeof (AAA_HashEntry));
+ if (!p) {
+ syslog(LOG_CRIT, "FATAL: Unable to allocate memory!");
+ return (-1);
+ }
+
+ (void) memset(p, 0, sizeof (AAA_HashEntry));
+ (void) strncpy(p->mnNAI, mnNAI, MIN(MAX_NAI_LEN - 1, mnNAILen));
+ p->mnNAI[MIN(MAX_NAI_LEN - 1, mnNAILen)] = '\0';
+ (void) memcpy(p->mnChallenge, mnChallenge, MIN(MAX_CHALLENGE_LEN - 1,
+ mnChallengeLen));
+
+ p->homeAddress = homeAddress;
+ p->homeAgentAddress = homeAgentAddress;
+ p->timeOut = 0;
+ p->handle = 0;
+ p->messageHdr = messageHdr;
+
+ /* Now link it. */
+ rc = linkHashTableEntryString(&naiHash, (unsigned char *)mnNAI,
+ mnNAILen, p, LOCK_NONE);
+ if (rc != 0) {
+ syslog(LOG_ERR,
+ "ERROR: Unable to add entry to hash! (unique?)");
+ free(p);
+ return (-2);
+ }
+
+ return (0);
+
+} /* addNaiToHash */
+
+/*
+ * Function: removeFromHash
+ *
+ * Arguments: char *mnNAI, size_t mnNAILen
+ *
+ * Description: This function will delete the nai from our local hash.
+ * It is called in the foreign agent when the CloseSession
+ * Answer returns. It should be called in the home agent when
+ * the Accounting Stop arrives.
+ *
+ * Returns: int (zero on success)
+ */
+static int
+removeFromHash(char *mnNAI, size_t mnNAILen)
+{
+ int rc;
+ AAA_HashEntry *p;
+
+ /*
+ * Lock it for writing.
+ */
+ p = (AAA_HashEntry *)findHashTableEntryString(&naiHash,
+ (unsigned char *)mnNAI, mnNAILen, LOCK_WRITE, NULL, NULL, NULL,
+ NULL);
+ if (!p) {
+ syslog(LOG_ERR, "ERROR: Unable to find NAI <%.*s> in hash!",
+ mnNAILen, mnNAI);
+ return (-1);
+ }
+ rc = delHashTableEntryString(&naiHash, p, (unsigned char *)mnNAI,
+ mnNAILen, LOCK_NONE);
+
+ /* And, finally, free our data */
+ /* WARNING: Check return value xxx WORK */
+ (void) rw_unlock(&p->aaaNodeLock);
+ (void) rwlock_destroy(&p->aaaNodeLock);
+ free(p);
+
+ return (rc);
+
+} /* removeFromHash */
+
+/*
+ * Function: aaaUpdateHash
+ *
+ * Arguments: AAA_Packet *packet, char *mnNAI, size_t mnNAILen
+ *
+ * Description: This function will update the hash information for the
+ * given node. It gets called when any response comes from the
+ * AAA server. The only field that is currently updated is the
+ * handle. The handle is ONLY updated if our current copy is a
+ * zero.
+ *
+ * Also, since we probably sent out accounting messages with a
+ * handle of zero, it accepts any response that contains a handle
+ * of zero (but does not update our local information).
+ *
+ * Returns: int
+ */
+static AAA_HashEntry *
+aaaUpdateHash(AAA_Packet *packet, char *mnNAI, size_t mnNAILen)
+{
+ AAA_HashEntry *p;
+
+ /*
+ * Since we only have one thread reading from the socket, and
+ * that thread is the only thread that will update these data
+ * items, no locking is necessary.
+ */
+ p = (AAA_HashEntry *)findHashTableEntryString(&naiHash,
+ (unsigned char *)mnNAI, mnNAILen, LOCK_WRITE, NULL, NULL,
+ NULL, NULL);
+
+ if (p) {
+ if (p->handle) {
+ /*
+ * We already have a handle .. . check it
+ * But, it's ok if we get a zero . . .just means that
+ * the sender doesn't know that the handle is set yet.
+ */
+ if ((p->handle != ntohl(packet->handle)) &&
+ (ntohl(packet->handle) != 0)) {
+ /* Error! */
+ syslog(LOG_ERR,
+ "Error: incoming handle does not match"
+ " handle in hash: (%d <> %d)\n",
+ ntohl(packet->handle),
+ p->handle);
+ (void) rw_unlock(&p->aaaNodeLock);
+ return (NULL);
+ }
+ } else {
+ /* Since we don't have a handle on file, update it */
+ p->handle = ntohl(packet->handle);
+ }
+ } else {
+ syslog(LOG_ERR, "Error: Unable to find nai (%*s) in hash!",
+ mnNAILen, mnNAI);
+ return (NULL);
+ }
+
+ return (p);
+
+} /* aaaUpdateHash */
+
+/*
+ * Function: aaaLookupHandle
+ *
+ * Arguments: unsigned char *mnNAI, size_t mnNAILen
+ *
+ * Description: This function will lookup the NAI in the hash, and will return
+ * the handle associated with it.
+ *
+ * Returns: int32_t (-1 on error)
+ */
+static int32_t
+aaaLookupHandle(char *mnNAI, size_t mnNAILen)
+{
+ AAA_HashEntry *p;
+ int32_t handle;
+
+ /*
+ * Since we only have one thread reading from the socket, and
+ * that thread is the only thread that will update these data
+ * items, no locking is necessary.
+ */
+ p = (AAA_HashEntry *)findHashTableEntryString(&naiHash, (unsigned
+ char *)mnNAI, mnNAILen, LOCK_READ, NULL, NULL, NULL, NULL);
+
+ if (p) {
+ handle = p->handle;
+ (void) rw_unlock(&p->aaaNodeLock);
+
+ return (handle);
+ } else {
+ syslog(LOG_ERR, "Error: Unable to find nai (%.*s) in hash!",
+ mnNAILen, mnNAI);
+ return (-1);
+ }
+
+} /* aaaLookupHandle */
+
+/*
+ * Function: aaaFindAvpInt
+ *
+ * Arguments: AAA_Packet *packet, AAA_AVPCode avpCode, int32_t *dest
+ *
+ * Description: This routine will return the long specified by avpCode.
+ * On error, it will return _B_FALSE.
+ *
+ * Returns: boolean_t (B_FALSE on error)
+ */
+static boolean_t
+aaaFindAvpInt(AAA_Packet *packet, AAA_AVPCode avpCode, int32_t *dest)
+{
+ AAA_AVP *avp, staticAvp;
+
+ avp = aaaFindAvpByCode(packet, avpCode);
+ if (!avp)
+ return (_B_FALSE);
+
+ /* Make our static copy */
+ (void) memcpy(&staticAvp, avp, 2 * sizeof (uint32_t));
+ staticAvp.length = ntohl(staticAvp.length);
+
+ /* subtract the header size */
+ staticAvp.length -= sizeof (uint32_t) * 2;
+
+ if (staticAvp.length != sizeof (uint32_t)) {
+ syslog(LOG_ERR, "Error: aaaFindAvpInt: bad length for int."
+ " avp code = %d, length = %d", staticAvp.avpCode,
+ staticAvp.length);
+ return (_B_FALSE);
+ }
+
+ (void) memcpy(dest, avp->data, sizeof (uint32_t));
+
+ return (_B_TRUE);
+
+} /* aaaFindAvpInt */
+
+/*
+ * Function: aaaFindAvpPtr
+ *
+ * Arguments: AAA_Packet *packet, AAA_AVPCode avpCode, size_t *length
+ *
+ * Description: This routine will return the data specified by avpCode.
+ * It will set the length to the length of the data.
+ * On error, it will return null.
+ *
+ * Returns: uint32_t (defaultValue on error)
+ */
+static void *
+aaaFindAvpPtr(AAA_Packet *packet, AAA_AVPCode avpCode, size_t *length)
+{
+ AAA_AVP *avp, staticAvp;
+
+ *length = 0; /* Initialize this first */
+
+ avp = aaaFindAvpByCode(packet, avpCode);
+ if (!avp)
+ return (NULL);
+
+ /* Make our static copy */
+ (void) memcpy(&staticAvp, avp, 2 * sizeof (uint32_t));
+ staticAvp.length = ntohl(staticAvp.length);
+
+ /* subtract the header size */
+ staticAvp.length -= sizeof (uint32_t) * 2;
+
+ *length = staticAvp.length;
+
+ return (avp->data);
+
+} /* aaaFindAvpPtr */
+
+/*
+ * Function: aaaFindAvpByCode
+ *
+ * Arguments: packet containing avps, avpCode
+ *
+ * Description: This function will walk through the AVPS and return a
+ * pointer to the avp that matches the given code.
+ * This function is not efficient, so if the number of
+ * avps expected grows over 15 or so, we should index them
+ * once, then call an indexed lookup.
+ *
+ * Returns: pointer to the avp, or NULL
+ *
+ */
+static AAA_AVP *
+aaaFindAvpByCode(AAA_Packet *packet, AAA_AVPCode avpCode)
+{
+ AAA_AVP *avp;
+ uint32_t packetLength;
+ uint32_t currentPosition;
+ AAA_AVP staticAVP;
+ unsigned char *buffer;
+
+ /* First, get the length of the packet, so we don't overshoot */
+ packetLength = ntohl(packet->length);
+
+ /* Now, set buffer to point to the start of the AVPs */
+ buffer = (unsigned char *)&packet[1];
+
+ currentPosition = 0;
+ while (currentPosition < (packetLength - (sizeof (AAA_AVP)))) {
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ avp = (AAA_AVP*)&buffer[currentPosition];
+
+ /*
+ * now avp points to the right place. copy the
+ * data somewhere byte aligned.
+ */
+
+ (void) memcpy(&staticAVP, avp, sizeof (uint32_t)*2);
+ staticAVP.length = ntohl(staticAVP.length);
+ staticAVP.avpCode = ntohl(staticAVP.avpCode);
+
+ if (staticAVP.length <= (2 * sizeof (uint32_t))) {
+ /* Bad packet */
+ syslog(LOG_ERR, "Error: bad packet avp code = %d, "
+ "length = %d", staticAVP.avpCode,
+ staticAVP.length);
+ return (NULL);
+ }
+
+ if (staticAVP.avpCode == avpCode) {
+ /* We found it! return our position */
+ return (avp);
+ }
+
+
+ /* That wasn't it, so move on to the next one */
+ currentPosition += staticAVP.length;
+
+ }
+
+ /* We didn't find it! */
+ return (NULL);
+} /* aaaFindAvpByCode */
+
+/*
+ * Function: aaaCreateKey
+ *
+ * Arguments: AAA_Packet *packet, int spiTag, int keyTag
+ *
+ * Description: This routine will create the SA for the given key / SPI.
+ * If the SA already exists, and it is dynamic, then replace the
+ * key information.
+ *
+ * Returns: int (zero on success)
+ */
+int
+aaaCreateKey(int spi, unsigned char *key, size_t keyLen,
+ uint32_t sessionTimeout)
+{
+ MipSecAssocEntry *entry;
+
+ entry = CreateSecAssocEntry(_B_TRUE, spi, TIMESTAMPS, MD5, PREFIXSUFFIX,
+ keyLen, (char *)key, sessionTimeout);
+
+ if (entry == NULL) {
+ syslog(LOG_ERR,
+ "Unable to create SA for Mobile Node (SPI %d)\n", spi);
+ return (-1);
+ }
+
+ /*
+ * The Create function ends up locking the node, so
+ * we need to free it.
+ */
+ (void) rw_unlock(&entry->mipSecNodeLock);
+ return (0);
+
+} /* aaaCreateKey */
+
+/*
+ * Function: aaaCreateAgent
+ *
+ * Arguments: AAA_Packet *packet, int addrTag, uint32_t spi,
+ * uint32_t SessionTimeout
+ *
+ * Description: This function will create an agent from the data in the
+ * packet. It is used to create bothe the foreign and home
+ * agent entries. (The FA would create a corresponding HA entry,
+ * and vice versa)
+ *
+ * Returns: void
+ */
+static void
+aaaCreateAgent(AAA_Packet *packet, int addrTag, uint32_t spi,
+ uint32_t sessionTimeout)
+{
+ MobilityAgentEntry *maEntry;
+ ipaddr_t peerAddress;
+
+ /* Check the Agent Address */
+ if (!aaaFindAvpInt(packet, addrTag, (int *)&peerAddress)) {
+ /* BAD error . . . malformed packet */
+ syslog(LOG_ERR, "ERROR: bad packet (no ADDR:%d)", addrTag);
+ return;
+ }
+
+ /*
+ * On the Foreign Agent, we will need to create the Mobility
+ * Agent entry so that we can find the Home Agent's SPI
+ * when forwarding messages to it.
+ */
+ maEntry = CreateMobilityAgentEntry(_B_TRUE, peerAddress, spi,
+ sessionTimeout);
+ if (maEntry == NULL) {
+ /* Find it. */
+ maEntry = findHashTableEntryUint(&mipAgentHash, peerAddress,
+ LOCK_WRITE, NULL, 0, 0, 0);
+ if (maEntry == NULL) {
+ /* Error! */
+ syslog(LOG_ERR, "Error: Unable to create the maEntry!");
+ return;
+ }
+ }
+
+ /*
+ * Now, make sure the entry is a dynamic one, and update the SPI
+ */
+ if (maEntry->maSPI != spi) {
+ if (maEntry->maIsEntryDynamic) {
+ maEntry->maSPI = spi;
+ } else {
+ syslog(LOG_ERR,
+ "Error: received an SPI that does not match static"
+ " Agent entry");
+ }
+ }
+
+ /*
+ * The Create function ends up locking the node, so
+ * we need to free it.
+ */
+ (void) rw_unlock(&maEntry->maNodeLock);
+
+} /* aaaCreateAgent */
+
+/*
+ * Function: checkResultCode
+ *
+ * Arguments: AAA_Packet *packet
+ *
+ * Description: This routine checks the ResultCode field of the packet, and
+ * returns it (or -1 or -2 on error)
+ *
+ * Returns: int (resultCode, -1 or -2 on error)
+ */
+static int
+checkResultCode(AAA_Packet *packet)
+{
+ int32_t resultCode;
+
+ if (!aaaFindAvpInt(packet, RESULT_CODE, &resultCode)) {
+ /* BAD error . . . malformed packet */
+ syslog(LOG_ERR, "ERROR: bad packet (no RESULT_CODE)");
+ }
+
+ return (resultCode);
+} /* checkResultCode */
+
+/*
+ * Function: startAAATaskThread
+ *
+ * Arguments:
+ *
+ * Description: This function starts our AAA thread.
+ *
+ * Returns: int (zero on success)
+ */
+int
+startAAATaskThread()
+{
+ pthread_attr_t pthreadAttribute;
+ int result;
+
+ result = pthread_attr_init(&pthreadAttribute);
+
+ if (result) {
+ syslog(LOG_CRIT, "Error Initializing AAA pthread.");
+ return (-1);
+ }
+
+ /*
+ * We now create a thread to deal with all periodic task.
+ */
+ result = pthread_create(&aaaThreadId, &pthreadAttribute,
+ (void *(*)()) mainAAAThread, (void *)NULL);
+
+ if (result) {
+ syslog(LOG_CRIT, "pthread_create() failed.");
+ return (-1);
+ }
+
+ /*
+ * In order for system resources the be properly cleaned up,
+ * we need to detach the thread. Otherwise, we need to wait for
+ * a pthread_join(), which we do not want.
+ */
+ result = pthread_detach(aaaThreadId);
+
+ if (result) {
+ syslog(LOG_CRIT, "pthread_detach() failed.");
+ return (-1);
+ }
+
+ return (0);
+} /* StartAAATaskThread */
+
+/*
+ * Function: killAAATaskThread
+ *
+ * Arguments:
+ *
+ * Description: This function kills our AAA task thread.
+ *
+ * Returns: int
+ */
+int
+killAAATaskThread()
+{
+ int result;
+
+ if (aaaThreadId) {
+ /*
+ * Next we need to kill the dispatching thread.
+ */
+ result = pthread_cancel(aaaThreadId);
+
+ if (result) {
+ /*
+ * Well, there's not much we can do here..
+ */
+ syslog(LOG_CRIT, "Unable to kill AAA thread");
+ return (-1);
+ }
+ }
+
+ return (0);
+} /* killAAATaskThread */
+
+/*
+ * Function: mainAAAThread
+ *
+ * Arguments:
+ *
+ * Description: This is our main AAA thread. It receives the messages, and
+ * processes them based on command code.
+ *
+ * Returns: void *
+ */
+static void *
+mainAAAThread()
+{
+ unsigned char buffer[MAX_TCP_LEN];
+ uint32_t commandCode;
+ AAA_Packet *packet;
+ AAA_HashEntry *NaiEntry;
+ uint32_t resultCode;
+ boolean_t result;
+ char *mobileNodeNAI;
+ size_t mobileNodeNAILen;
+ ipaddr_t *homeaddr = NULL;
+ ipaddr_t *homeAgentaddr = NULL;
+ size_t homeaddrLen;
+ MessageHdr *msgHdr;
+ int rc;
+ uint32_t forHA;
+
+ /* The below is an endless loop that will not give any lint warnings */
+ for (; ; ) {
+ if ((rc = readTCPPacket(buffer, MAX_TCP_LEN - 1)) <= 0) {
+ syslog(LOG_ERR, "Error: <%d> reading packet (%d:%s)",
+ rc, errno, strerror(errno));
+ mipverbose(("readTCPPacket Failed errno is %d\n",
+ errno));
+ (void) sleep(1);
+ /*
+ * Clean up MN binding & visitor entries and tunnels
+ * when a down link between mobility agent and AAA
+ * infrastructure is down. readTCPPacket will only
+ * return 0 when this link is initally down. A
+ * reconnection try will result in rc being -1 so
+ * docleanup will only be called once - when link
+ * goes down.
+ */
+ if (rc == 0) {
+ syslog(LOG_ERR, "AAA readTCPPacket returned 0");
+ mipverbose(("AAA readTCPPacket returned 0\n"));
+ docleanup();
+ disableService(&maAdvConfigHash);
+ /*
+ * need to reconnect
+ */
+ (void) close(gbl_TCPSocket);
+ gbl_TCPSocket = -1;
+ }
+ continue;
+ }
+
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ packet = (AAA_Packet *)buffer;
+
+ commandCode = ntohl(packet->commandCode);
+ /*
+ * First, lookup this in our hash, and update the handle
+ * if necessary.
+ */
+ mobileNodeNAI = aaaFindAvpPtr(packet, MOBILE_NODE_NAI,
+ &mobileNodeNAILen);
+ if ((mobileNodeNAI == NULL) || (mobileNodeNAILen == 0)) {
+ /* Malformed packet */
+ syslog(LOG_ERR,
+ "Error: bad packet(no MOBILE_NODE_NAI)");
+ continue;
+ }
+
+ switch (commandCode) {
+ case MOBILE_IP_OPEN_SESSION_ANSWER:
+ /* Update the handle in the hash */
+
+ NaiEntry = aaaUpdateHash(packet, mobileNodeNAI,
+ mobileNodeNAILen);
+ if (NaiEntry == NULL) {
+ syslog(LOG_ERR,
+ "Error: Received packet for "
+ "non-pending NAI (ANSWER)");
+ continue;
+ }
+ (void) rw_unlock(&NaiEntry->aaaNodeLock);
+
+ /* Check for a good result code */
+ resultCode = checkResultCode(packet);
+ if (resultCode != 0) {
+ mipverbose(("result code was %d\n",
+ resultCode));
+
+
+ /*
+ * Remember we had preserved the messageHdr
+ * if Radius used, let's free it now.
+ */
+ if (aaaProtocol == RADIUS) {
+ (void) rw_wrlock(&NaiEntry->aaaNodeLock);
+ msgHdr = (MessageHdr *)NaiEntry->messageHdr;
+ processAuthFailure(msgHdr, mobileNodeNAI,
+ mobileNodeNAILen, resultCode);
+
+ msgHdr->dontDeleteNow = _B_FALSE;
+ FreeMessageHdr(msgHdr);
+ NaiEntry->messageHdr = NULL;
+ (void) rw_unlock(&NaiEntry->aaaNodeLock);
+ }
+
+
+ }
+ if (aaaProtocol == RADIUS) {
+
+ /*
+ * MOBILE_IP_OPEN_SESSION_ANSWER && RADIUS:
+ *
+ * IS_FROM_HA must be present so that
+ * the mobility agent will know that
+ * this is for the Home Agent vs the
+ * Foreign Agent.
+ *
+ * In this case the IS_FROM_HA means
+ * that this packet is for the Home
+ * Agent (if the value of the AVP is 1).
+ * If the packet is for the Home Agent,
+ * call processOpenSessionAnswerRadiusHA.
+ * If the packet is for the Foreign Agent,
+ * callprocessOpenSessionAnswerRadius().
+ */
+
+ if (!aaaFindAvpInt(packet, IS_FROM_HA, (int *)&forHA)) {
+ syslog(LOG_ERR, "ERROR: bad packet"
+ " (no IS_FROM_HA)");
+ continue;
+ }
+ if (forHA == 1) { /* HA */
+ processOpenSessionAnswerRadiusHA(packet,
+ mobileNodeNAI, mobileNodeNAILen,
+ NaiEntry, resultCode);
+ } else { /* FA */
+ processOpenSessionAnswerRadius(packet,
+ mobileNodeNAI, mobileNodeNAILen,
+ NaiEntry);
+ }
+
+ } else {
+ processOpenSessionAnswer(packet, mobileNodeNAI,
+ mobileNodeNAILen, resultCode);
+ }
+ break;
+
+ case MOBILE_IP_ACCOUNTING_START_ANSWER:
+ case MOBILE_IP_ACCOUNTING_INTERIM_ANSWER:
+ case MOBILE_IP_ACCOUNTING_STOP_ANSWER:
+ /* Check the handle in the hash */
+ NaiEntry = aaaUpdateHash(packet, mobileNodeNAI,
+ mobileNodeNAILen);
+ if (NaiEntry == NULL) {
+ syslog(LOG_ERR,
+ "Error: Received packet for "
+ "non-pending NAI");
+ continue;
+ }
+ (void) rw_unlock(&NaiEntry->aaaNodeLock);
+
+ /* Check the result code */
+ resultCode = checkResultCode(packet);
+ if (resultCode != 0) {
+ syslog(LOG_ERR, "Error: commandCode: %d"
+ " resultCode = %d", commandCode,
+ resultCode);
+ }
+ if (commandCode == MOBILE_IP_ACCOUNTING_STOP_ANSWER) {
+ (void) sendCloseSession(packet, mobileNodeNAI,
+ mobileNodeNAILen);
+ }
+ break;
+
+ case MOBILE_IP_CLOSE_SESSION_ANSWER:
+ /* Check the handle */
+ if ((NaiEntry = aaaUpdateHash(packet, mobileNodeNAI,
+ mobileNodeNAILen)) == NULL) {
+ syslog(LOG_ERR,
+ "Error: Received packet for "
+ "non-pending NAI");
+ continue;
+ }
+ (void) rw_unlock(&NaiEntry->aaaNodeLock);
+ /* Check the result code */
+ resultCode = checkResultCode(packet);
+ if (resultCode != 0) {
+ syslog(LOG_ERR,
+ "Error: ACCOUNTING_START_ANSWER"
+ " resultCode = %d", resultCode);
+ }
+ /* And finally, remove the hash entry */
+ (void) removeFromHash(mobileNodeNAI, mobileNodeNAILen);
+ break;
+
+ case MOBILE_IP_OPEN_SESSION_INDICATION:
+ if (aaaProtocol != DIAMETER) {
+ syslog(LOG_ERR,
+ "Error: "
+ "MOBILE_IP_OPEN_SESSION_INDICATION "
+ "AAA protocol should be DIAMETER not "
+ "%d", aaaProtocol);
+ break;
+ }
+ /*
+ * We get this message from diameter when we are
+ * acting as a home agent.
+ */
+ processOpenSessionIndication(packet, mobileNodeNAI,
+ mobileNodeNAILen);
+ break;
+
+ /* We should not get these. */
+#ifdef TEST_DIAMETER
+ case MOBILE_IP_OPEN_SESSION_REQUEST:
+ /*
+ * We are faking a diameter connection. Accept the
+ * OPEN_SESSION, and respond with an OPEN_SESSION
+ * response. Generate keys too.
+ */
+ processOpenSessionRequest(packet, mobileNodeNAI,
+ mobileNodeNAILen);
+ break;
+
+ case MOBILE_IP_OPEN_SESSION_INDICATION_RESPONSE:
+ /*
+ * We are faking a diameter connection. Accept the
+ * OPEN_SESSION_INDICATION_RESPONSE as the foreign
+ * agent, and convert it to a OpenSessionAnswer
+ */
+ processOpenSessionIndicationResponse(packet,
+ mobileNodeNAI, mobileNodeNAILen);
+ break;
+#else
+ case MOBILE_IP_OPEN_SESSION_REQUEST:
+ case MOBILE_IP_OPEN_SESSION_INDICATION_RESPONSE:
+#endif
+ case MOBILE_IP_CLOSE_SESSION_REQUEST:
+ /*
+ * AAA server can force mipagent to de-register
+ * a MN. No need to send a Accounting Stop, since
+ * AAA already knows this MN is de-registering.
+ * This message applies to both AAAH and AAAF.
+ */
+ homeaddr = aaaFindAvpPtr(packet,
+ MOBILE_NODE_HOME_ADDRESS, &homeaddrLen);
+ homeAgentaddr = aaaFindAvpPtr(packet,
+ HOME_AGENT_ADDRESS, &homeaddrLen);
+
+ if (homeaddr == NULL || homeAgentaddr == NULL) {
+ result = 1;
+ syslog(LOG_ERR,
+ "ERROR: bad packet "
+ "(no agent addresses or homeaddr)");
+ } else {
+ result = forcefullyDeregisterMN(homeaddr,
+ 0, *homeAgentaddr);
+ }
+ result = sendCloseSessionAnswer(packet, mobileNodeNAI,
+ mobileNodeNAILen, result);
+ if (result != 0) {
+ syslog(LOG_ERR, "sendto failed at mipagent "
+ "while replying to AAA. ");
+ }
+ break;
+
+ case MOBILE_IP_ACCOUNTING_START_REQUEST:
+ case MOBILE_IP_ACCOUNTING_INTERIM_REQUEST:
+ case MOBILE_IP_ACCOUNTING_STOP_REQUEST:
+ default:
+ syslog(LOG_ERR,
+ "Error: Received invalid commandCode <%d>",
+ commandCode);
+ }
+ }
+
+ /* LINTED E_STMT_NOT_REACHED */
+ return (NULL);
+} /* mainAAAThread */
+
+#ifdef TEST_DIAMETER
+/*
+ * Function: processOpenSessionRequest
+ *
+ * Arguments: AAA_Packet *packet, char *mnNAI, size_t mnNAILen
+ *
+ * Description: This is a DEBUG ONLY routine used to test the code without
+ * DIAMETER. It should normally be conditionally compiled
+ * OUT of the code. This routine takes an OpenSessionRequest,
+ * and generates a OpenSessionIndication. It then calls
+ * processOpenSessionIndication.
+ * THIS ROUTINE DISTRUCTIVELY MODIFIES packet IN PLACE
+ *
+ * Returns: void
+ */
+static void
+processOpenSessionRequest(AAA_Packet *packet, char *mnNAI, size_t mnNAILen)
+{
+ unsigned char buffer[MAX_TCP_LEN];
+ AAA_Packet *response;
+ size_t length;
+ unsigned char *regReq;
+ size_t regReqLen;
+ char *faNai;
+ size_t faNaiLen;
+ uint32_t numChallengeBytes;
+ unsigned char *mnResponse;
+ size_t mnResponseLen;
+ ipaddr_t homeAddress, homeAgentAddress, foreignAgentAddress;
+ uint32_t sessionTimeout;
+ uint32_t MNFASpi, FAHASpi, MNHASpi;
+ unsigned char MNFAKey[MAX_GENERATE_KEY_LEN];
+ unsigned char FAHAKey[MAX_GENERATE_KEY_LEN];
+ unsigned char MNHAKey[MAX_GENERATE_KEY_LEN];
+ /* These are encrypted versions of the above */
+ unsigned char HAFAKey[MAX_GENERATE_KEY_LEN];
+ unsigned char FAMNKey[MAX_GENERATE_KEY_LEN];
+ unsigned char HAMNKey[MAX_GENERATE_KEY_LEN];
+
+ /* FOREIGN_AGENT_NAI */
+ faNai = aaaFindAvpPtr(packet, FOREIGN_AGENT_NAI, &faNaiLen);
+ if (!faNai || !faNaiLen) {
+ syslog(LOG_ERR, "ERROR: bad packet (no FOREIGN_AGENT_NAI)");
+ return;
+ }
+
+ /* REGISTRATION_REQUEST */
+ regReq = aaaFindAvpPtr(packet, REGISTRATION_REQUEST, &regReqLen);
+ if (!regReq || !regReqLen) {
+ syslog(LOG_ERR, "ERROR: bad packet (no REGISTRATION_REQUEST)");
+ return;
+ }
+
+ /* NUMBER_OF_CHALLENGE_BYTES_IN_RR */
+ if (!aaaFindAvpInt(packet,
+ NUMBER_OF_CHALLENGE_BYTES_IN_RR, &numChallengeBytes)) {
+ syslog(LOG_ERR, "ERROR: bad packet (no "
+ "NUMBER_OF_CHALLENGE_BYTES_IN_RR)");
+ return;
+ }
+
+ /* MOBILE_NODE_RESPONSE */
+ mnResponse = aaaFindAvpPtr(packet, MOBILE_NODE_RESPONSE,
+ &mnResponseLen);
+ if (!mnResponse || !mnResponseLen) {
+ syslog(LOG_ERR, "ERROR: bad packet (no MOBILE_NODE_RESPONSE)");
+ return;
+ }
+
+ /* MOBILE_NODE_HOME_ADDRESS */
+ if (!aaaFindAvpInt(packet, MOBILE_NODE_HOME_ADDRESS,
+ &homeAddress)) {
+ syslog(LOG_ERR,
+ "ERROR: bad packet (no MOBILE_NODE_HOME_ADDRESS)");
+ return;
+ }
+
+ /* HOME_AGENT_ADDRESS */
+ if (!aaaFindAvpInt(packet, HOME_AGENT_ADDRESS,
+ &homeAgentAddress)) {
+ syslog(LOG_ERR, "ERROR: bad packet (no HOME_AGENT_ADDRESS)");
+ return;
+ }
+
+ /* FOREIGN_AGENT_ADDRESS */
+ if (!aaaFindAvpInt(packet, FOREIGN_AGENT_ADDRESS,
+ &foreignAgentAddress)) {
+ syslog(LOG_ERR,
+ "ERROR: bad packet (no FOREIGN_AGENT_ADDRESS)");
+ return;
+ }
+
+ /* ******** Build Fake Packet ******** */
+
+ /*
+ * Generate our keys
+ */
+ aaaGenerateKey(FAHAKey, MAX_GENERATE_KEY_LEN);
+ aaaGenerateKey(MNFAKey, MAX_GENERATE_KEY_LEN);
+ aaaGenerateKey(MNHAKey, MAX_GENERATE_KEY_LEN);
+
+ /* These should be computed. (MD5?) */
+ aaaGenerateKey(HAFAKey, MAX_GENERATE_KEY_LEN);
+ aaaGenerateKey(FAMNKey, MAX_GENERATE_KEY_LEN);
+ aaaGenerateKey(HAMNKey, MAX_GENERATE_KEY_LEN);
+
+ MNFASpi = aaaGenerateSpi();
+ FAHASpi = aaaGenerateSpi();
+ MNHASpi = aaaGenerateSpi();
+
+
+ /* Build our response (An OpenSessionIndication) */
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ response = (AAA_Packet *)buffer;
+ response->commandCode = htonl(MOBILE_IP_OPEN_SESSION_INDICATION);
+ response->handle = packet->handle; /* assume ordered correctly */
+
+ length = sizeof (AAA_Packet);
+
+ /* Mobile Node NAI */
+ length += aaaAddAvp(MOBILE_NODE_NAI, &buffer[length],
+ MAX_TCP_LEN - length, mnNAI, mnNAILen);
+
+ /* Foreign Agent NAI */
+ length += aaaAddAvp(FOREIGN_AGENT_NAI, &buffer[length],
+ MAX_TCP_LEN - length, maNai, strlen(maNai));
+
+ /* Foreign Agent Address */
+ length += aaaAddAvp(FOREIGN_AGENT_ADDRESS, &buffer[length],
+ MAX_TCP_LEN - length, &foreignAgentAddress, sizeof (uint32_t));
+
+ /* Registration Request Packet */
+ length += aaaAddAvp(REGISTRATION_REQUEST, &buffer[length],
+ MAX_TCP_LEN - length, regReq, regReqLen);
+
+ /* Mobile Node Home Address */
+ length += aaaAddAvp(MOBILE_NODE_HOME_ADDRESS, &buffer[length],
+ MAX_TCP_LEN - length, &homeAddress, sizeof (uint32_t));
+
+ /* Home Agent Address */
+ length += aaaAddAvp(HOME_AGENT_ADDRESS, &buffer[length],
+ MAX_TCP_LEN - length, &homeAgentAddress, sizeof (uint32_t));
+
+ /* fa-ha spi */
+ length += aaaAddAvp(FA_HA_SPI, &buffer[length],
+ MAX_TCP_LEN - length, &FAHASpi, sizeof (uint32_t));
+
+ /* FA-HA key */
+ length += aaaAddAvp(FA_HA_KEY, &buffer[length],
+ MAX_TCP_LEN - length, &FAHAKey, MAX_GENERATE_KEY_LEN);
+
+ /* HA-FA Key */
+ length += aaaAddAvp(HA_FA_KEY, &buffer[length],
+ MAX_TCP_LEN - length, &HAFAKey, MAX_GENERATE_KEY_LEN);
+
+ /* MN-FA SPI */
+ length += aaaAddAvp(MN_FA_SPI, &buffer[length],
+ MAX_TCP_LEN - length, &MNFASpi, sizeof (uint32_t));
+
+ /* MN-FA key */
+ length += aaaAddAvp(MN_FA_KEY, &buffer[length],
+ MAX_TCP_LEN - length, &MNFAKey, MAX_GENERATE_KEY_LEN);
+
+ /* FA-MN Key */
+ length += aaaAddAvp(FA_MN_KEY, &buffer[length],
+ MAX_TCP_LEN - length, &FAMNKey, MAX_GENERATE_KEY_LEN);
+
+ /* MN-HA SPI */
+ length += aaaAddAvp(MN_HA_SPI, &buffer[length],
+ MAX_TCP_LEN - length, &MNHASpi, sizeof (uint32_t));
+
+ /* MN-HA key */
+ length += aaaAddAvp(MN_HA_KEY, &buffer[length],
+ MAX_TCP_LEN - length, &MNHAKey, MAX_GENERATE_KEY_LEN);
+
+ /* HA-MN Key */
+ length += aaaAddAvp(HA_MN_KEY, &buffer[length],
+ MAX_TCP_LEN - length, &HAMNKey, MAX_GENERATE_KEY_LEN);
+
+ /* Send a fake session time out */
+ sessionTimeout = 60;
+ length += aaaAddAvp(SESSION_TIMEOUT, &buffer[length],
+ MAX_TCP_LEN - length, &sessionTimeout, sizeof (uint32_t));
+
+ response->length = htonl(length);
+
+
+ processOpenSessionIndication(response, mnNAI, mnNAILen);
+} /* processOpenSessionRequest */
+
+/*
+ * Function: processOpenSessionIndicationResponse
+ *
+ * Arguments: AAA_Packet *packet
+ *
+ * Description: This function will handle converting packets from
+ * OpenSessionIndicationResponses into OpenSessionAnswers,
+ * for testing without diameter. (This is the foreign agent
+ * side catching the home agent's response.)
+ *
+ * Returns: void
+ */
+static void
+processOpenSessionIndicationResponse(AAA_Packet *packet, char *mnNAI,
+ size_t mnNAILen)
+{
+ mipverbose(("Processing OpenSessionIndicationResponse (DEBUG)\n"));
+
+ processOpenSessionAnswer(packet, mnNAI, mnNAILen, 0);
+} /* processOpenSessionIndicationResponse */
+#endif /* TEST_DIAMETER */
+
+/*
+ * Setup the basic message handling fields. I think that we
+ * could possibly re-use ifEntry here as a pointer back to the
+ * AAA server. Otherwise, we can create a new field in the
+ * message header structure.
+ */
+
+static void
+aaaSetupMessageHdr(MessageHdr *messageHdr, uint32_t resultCode)
+{
+ messageHdr->pktSource = MIP_PKT_FROM_AAA;
+ messageHdr->ifType = ON_UNICAST_SOCK;
+ messageHdr->pktType = PKT_UDP;
+ messageHdr->ifEntry = getFirstInterface();
+ messageHdr->aaaResultCode = resultCode;
+
+} /* aaaSetupMessageHdr */
+
+/*
+ * Function: processOpenSessionAnswer
+ *
+ * Arguments: AAA_Packet *packet
+ *
+ * Description: This function handles the message to the foreign node, from
+ * DIAMETER.
+ *
+ * Returns: void
+ */
+static void
+/* LINTED E_FUNC_ARG_UNUSED */
+processOpenSessionAnswer(AAA_Packet *packet, char *mnNAI, size_t mnNAILen,
+ uint32_t resultCode)
+{
+ static MessageHdr *messageHdr = NULL;
+ unsigned char *FAMNKey, *FAHAKey;
+ size_t FAMNKeyLen = 0, FAHAKeyLen = 0;
+ uint32_t FAHASpi = 0, FAMNSpi = 0;
+ uint32_t sessionTimeout = 0;
+ unsigned char *regResponse;
+ size_t regResponseLen;
+
+ /* Check the SessionTimeout */
+ if (!aaaFindAvpInt(packet, SESSION_TIMEOUT, (int *)&sessionTimeout)) {
+ /* BAD error . . . malformed packet */
+ syslog(LOG_ERR, "ERROR: bad packet (no SESSION_TIMEOUT)");
+ }
+
+ /* Now, make sure we have a response field */
+ regResponse = aaaFindAvpPtr(packet, REGISTRATION_REPLY,
+ &regResponseLen);
+
+ if (!regResponse) {
+ syslog(LOG_ERR, "ERROR: bad packet (no REGISTRATION_REPLY)");
+ }
+
+ /* FA-HA Spi */
+ if (!aaaFindAvpInt(packet, FA_HA_SPI, (int *)&FAHASpi)) {
+ syslog(LOG_ERR, "ERROR: bad packet (no FA_HA_SPI)");
+ }
+ /* FA-HA Key */
+ FAHAKey = aaaFindAvpPtr(packet, FA_HA_KEY, &FAHAKeyLen);
+ if (!FAHAKey || !FAHAKeyLen) {
+ syslog(LOG_ERR, "ERROR: bad packet (no FA_HA_KEY)");
+ }
+ /* MN-FA Spi */
+ if (!aaaFindAvpInt(packet, MN_FA_SPI, (int *)&FAMNSpi)) {
+ syslog(LOG_ERR, "ERROR: bad packet (no MN_FA_SPI)");
+ }
+ /* MN-FA Key */
+ FAMNKey = aaaFindAvpPtr(packet, FA_MN_KEY, &FAMNKeyLen);
+ if (!FAMNKey || !FAMNKeyLen) {
+ syslog(LOG_ERR, "ERROR: bad packet (no MN_FA_KEY)");
+ }
+
+ /* Session Timeout */
+ if (!aaaFindAvpInt(packet, SESSION_TIMEOUT, (int *)&sessionTimeout)) {
+ /* BAD error . . . malformed packet */
+ syslog(LOG_ERR, "ERROR: bad packet (no SESSION_TIMEOUT)");
+ }
+
+ /*
+ * Create our keys
+ */
+ if ((FAMNKeyLen != 0) &&
+ (aaaCreateKey(FAMNSpi, FAMNKey, FAMNKeyLen, sessionTimeout) < 0)) {
+ syslog(LOG_ERR, "Error: Invalid MN-FA SPI/Key pair");
+ }
+
+ if ((FAHAKeyLen != 0) &&
+ (aaaCreateKey(FAHASpi, FAHAKey, FAHAKeyLen, sessionTimeout) < 0)) {
+ syslog(LOG_ERR, "Error: Invalid FA-HA SPI/Key pair");
+ }
+
+ aaaCreateAgent(packet, HOME_AGENT_ADDRESS, FAHASpi, sessionTimeout);
+
+ /*
+ * If we don't already have a message header,
+ * allocate one.
+ */
+ if (messageHdr == NULL) {
+ if ((messageHdr = AllocateMessageHdr()) == NULL) {
+ syslog(LOG_CRIT,
+ "Unable to allocate a message header");
+ return;
+ }
+ }
+ aaaSetupMessageHdr(messageHdr, resultCode);
+ (void) memcpy(messageHdr->pkt, regResponse, regResponseLen);
+ messageHdr->pktLen = regResponseLen;
+
+ messageHdr->mnFaSPI = FAMNSpi;
+ messageHdr->aaaSessionTimeout = sessionTimeout;
+
+ /*
+ * Dispatch the message!
+ */
+ (void) dispatchMsgToThread(&messageHdr);
+} /* processOpenSessionAnswer */
+
+/*
+ * Function: processOpenSessionAnswerRadius
+ *
+ * Arguments: AAA_Packet *packet, char *mnNAI, size_t mnNAILen
+ *
+ * Description: This function handles the message to the FA, from
+ * RADIUS.
+ *
+ * Returns: void
+ */
+static void
+processOpenSessionAnswerRadius(AAA_Packet *packet, char *mnNAI, size_t mnNAILen,
+ AAA_HashEntry *NaiEntry)
+{
+ MessageHdr *msgHdr;
+ regRequest *requestPtr;
+ uint32_t revtun;
+ int code = 0;
+
+ mipverbose(("Processing OpenSessionAnswer for RADIUS (FA Side)\n"));
+
+ /*
+ * We are here after seeing that the FA received a positive response
+ * from the RADIUS server. That completes the auth check for the MN
+ * which is attempting to register through this FA. Now we need to go
+ * back and forward this registration request to HA.
+ *
+ * We shouldn't get any lifetime info from Radius server. FA has been
+ * advertising the lifetime value it's willing to support. It has also
+ * already rejected if MN was asking more than that. Now Radius server
+ * coming along and forcing FA to lower the lifetime doesn't make sense,
+ * and not compliant with RFC2002.
+ *
+ * If we use this function for HA, it's a different story. Radius has
+ * the authority to dictate lifetime.
+ */
+
+ /*
+ * Let's make sure this is not a replay of an already recevied
+ * OpenSessionAnswer. If we had freed the msgHdr before, that means
+ * this is a replay (not necessarily an attack :).
+ */
+ (void) rw_rdlock(&(NaiEntry->aaaNodeLock));
+ msgHdr = (MessageHdr *)NaiEntry->messageHdr;
+ if (msgHdr == NULL) {
+ mipverbose(("This was a repeat OpenSessionAnswer\n"));
+ (void) rw_unlock(&NaiEntry->aaaNodeLock);
+ return;
+ }
+
+ /* REV_TUN */
+ if (!aaaFindAvpInt(packet, REV_TUN, (int *)&revtun) ||
+ (revtun != REVTUN_REQUIRED && revtun != REVTUN_NOTREQUIRED)) {
+ syslog(LOG_ERR,
+ "ERROR: OpenSessionAnswer (no or bad REV_TUN)");
+ if (revtun != 0) {
+ syslog(LOG_ERR,
+ "ERROR: OpenSessionAnswer bad REV_TUN value %d\n",
+ revtun);
+ } else {
+ syslog(LOG_ERR,
+ "ERROR: OpenSessionAnswer (no REV_TUN)");
+ }
+ (void) rw_unlock(&NaiEntry->aaaNodeLock);
+ return;
+ }
+ /*
+ * Check if revtun value matches with T bit value of request.
+ * FAprocessRegRequest already checks first whether
+ * FA supports Reverse tunnel when there is a request
+ * with T bit. According to IS-835, the rule for radius:
+ * revtun = 0; Reverse tunnel is not required
+ * revtun = 1; Reverse tunnel is required
+ * So, if FA is reverse tunnel capable, we allow reverse tunnel
+ * when revtun = REVTUN_NOTREQUIRED(0) and MN requests reverse tunnel
+ */
+ mipverbose(("Radius returned REV_TUN value %d", revtun));
+ /* LINTED */
+ requestPtr = (regRequest *) msgHdr->pkt;
+ if (!(requestPtr->regFlags & REG_REVERSE_TUNNEL) &&
+ revtun == REVTUN_REQUIRED) {
+ /*
+ * AAA recommends reverse tunnel for this MN, it must
+ * request reverse tunnel in registration request
+ */
+ mipverbose(("Mobile node must request reverse tunnel\n"));
+ code = FA_REVERSE_TUNNEL_REQUIRED;
+ faCounters.faReverseTunnelRequiredCnt++;
+ }
+ /* Release Read lock now */
+ (void) rw_unlock(&NaiEntry->aaaNodeLock);
+
+ if (code == FA_REVERSE_TUNNEL_REQUIRED) {
+ FaVisitorEntry *visitor_entry = NULL;
+
+ (void) rw_wrlock(&NaiEntry->aaaNodeLock);
+ (void) rejectFromFAToMN(msgHdr, msgHdr->ifEntry, code);
+
+ /* Cleanup the pending visitor entry now */
+ visitor_entry = findHashTableEntryString(&faVisitorHash,
+ (unsigned char *)mnNAI, mnNAILen, LOCK_WRITE,
+ mkPendingFAVEHashLookup, _B_FALSE, 0, 0);
+ if (visitor_entry != NULL) {
+ /* Pending entry found */
+ delFAVE(&faVisitorHash, &visitor_entry,
+ requestPtr->homeAddr, REG_REVOKED);
+ }
+ msgHdr->dontDeleteNow = _B_FALSE;
+ FreeMessageHdr(msgHdr);
+ NaiEntry->messageHdr = NULL;
+ (void) rw_unlock(&NaiEntry->aaaNodeLock);
+ return;
+ }
+
+
+ (void) forwardFromFAToHA(msgHdr, msgHdr->ifEntry, _B_TRUE);
+
+ /*
+ * Now we are done with the messageHdr stored in NaiEntry, let's
+ * free it, as we promised before.
+ */
+ (void) rw_wrlock(&NaiEntry->aaaNodeLock);
+ msgHdr->dontDeleteNow = _B_FALSE;
+ FreeMessageHdr(msgHdr);
+ NaiEntry->messageHdr = NULL;
+ (void) rw_unlock(&NaiEntry->aaaNodeLock);
+
+} /* processOpenSessionAnswerRadius */
+
+
+/*
+ * Function: processOpenSessionAnswerRadiusHA
+ *
+ * Arguments: AAA_Packet *packet, char *mnNAI, size_t mnNAILen, uint32_t
+ * resultCode
+ *
+ * Description: This function handles the message to the HA, from
+ * RADIUS client.
+ *
+ * Returns: void
+ */
+/* ARGSUSED */
+static void
+processOpenSessionAnswerRadiusHA(AAA_Packet *packet, char *mnNAI,
+ size_t mnNAILen, AAA_HashEntry *NaiEntry, uint32_t resultCode)
+{
+ static MessageHdr *messageHdr = NULL;
+ regRequest *requestPtr;
+ uint32_t sessionTimeout = 0;
+ uint32_t revtun;
+ uint32_t MNHASpi;
+ unsigned char *MNHAKey;
+ size_t MNHAKeyLen;
+ int code = 0;
+
+ /*
+ * AVPs expected to receive:
+ *
+ * SESSION_TIMEOUT
+ * MN_HA_SPI
+ * MN_HA_KEY
+ * HOME AGENT ADDRESS
+ * Mobile Node Home Address
+ */
+ mipverbose(("Processing OpenSessionAnswer for RADIUS (HA Side)\n"));
+
+ /*
+ * Let's make sure this is not a replay of an already recevied
+ * OpenSessionAnswer. If we had freed the messsageHdr before, that means
+ * this is a replay (not necessarily an attack :).
+ */
+ (void) rw_rdlock(&(NaiEntry->aaaNodeLock));
+ messageHdr = (MessageHdr *)NaiEntry->messageHdr;
+ if (messageHdr == NULL) {
+ mipverbose(("This was a repeat OpenSessionAnswer\n"));
+ (void) rw_unlock(&NaiEntry->aaaNodeLock);
+ return;
+ }
+ (void) rw_unlock(&NaiEntry->aaaNodeLock);
+
+ /* Check the SessionTimeout */
+ if (!aaaFindAvpInt(packet, SESSION_TIMEOUT, (int *)&sessionTimeout)) {
+ /* BAD error . . . malformed packet */
+ syslog(LOG_ERR, "ERROR: bad packet (no SESSION_TIMEOUT)");
+ code = HA_MN_AUTH_FAILURE;
+ }
+
+ /* MN-HA Spi */
+ if (!aaaFindAvpInt(packet, MN_HA_SPI, (int *)&MNHASpi)) {
+ syslog(LOG_ERR, "ERROR: bad packet (no MN_HA_SPI)");
+ code = HA_MN_AUTH_FAILURE;
+ }
+ /* MN-HA Key */
+ MNHAKey = aaaFindAvpPtr(packet, MN_HA_KEY, &MNHAKeyLen);
+ if (!MNHAKey || !MNHAKeyLen) {
+ syslog(LOG_ERR, "ERROR: bad packet (no MN_HA_KEY)");
+ code = HA_MN_AUTH_FAILURE;
+ }
+
+ /* REV_TUN */
+ if (!aaaFindAvpInt(packet, REV_TUN, (int *)&revtun) ||
+ (revtun != REVTUN_REQUIRED && revtun != REVTUN_NOTREQUIRED)) {
+ syslog(LOG_ERR,
+ "ERROR: bad packet (no or bad REV_TUN from RADIUS)");
+ if (revtun != 0) {
+ syslog(LOG_ERR,
+ "ERROR: REV_TUN value %d\n", revtun);
+ } else {
+ syslog(LOG_ERR,
+ "ERROR: (no REV_TUN from RADIUS)");
+ }
+ code = HA_MN_AUTH_FAILURE;
+ } else {
+ mipverbose(("processOpenSessionAnswerHA:"
+ "HA received reverse tunnel value from RADIUS %d\n",
+ revtun));
+ /* Found valid revtun entry */
+ (void) rw_rdlock(&NaiEntry->aaaNodeLock);
+ /* LINTED */
+ requestPtr = (regRequest *) messageHdr->pkt;
+ if (!(requestPtr->regFlags & REG_REVERSE_TUNNEL) &&
+ revtun == REVTUN_REQUIRED) {
+ mipverbose(("processOpenSessionAnswerHA:"
+ "T bit required in regRequest\n"));
+ code = HA_REVERSE_TUNNEL_REQUIRED;
+ }
+ (void) rw_unlock(&NaiEntry->aaaNodeLock);
+ }
+
+ if (aaaCreateKey(MNHASpi, MNHAKey, MNHAKeyLen, sessionTimeout) < 0) {
+ syslog(LOG_ERR, "Error: Invalid MN-HA SPI/Key pair");
+ code = HA_MN_AUTH_FAILURE;
+ }
+
+ if (code == 0)
+ code = resultCode; /* resultCode is from pkt from Radius */
+
+ (void) rw_wrlock(&NaiEntry->aaaNodeLock);
+ messageHdr->pktSource = MIP_PKT_FROM_RADIUS;
+ messageHdr->ifType = ON_UNICAST_SOCK;
+ messageHdr->pktType = PKT_UDP;
+ messageHdr->aaaResultCode = code;
+
+ messageHdr->mnAAASPI = 0;
+ messageHdr->algorithm = MD5;
+ messageHdr->mnHaSPI = MNHASpi;
+ (void) memcpy(messageHdr->mnHaKey, MNHAKey, MNHAKeyLen);
+ messageHdr->mnHaKeyLen = MNHAKeyLen;
+
+ messageHdr->aaaSessionTimeout = sessionTimeout;
+
+ (void) rw_unlock(&NaiEntry->aaaNodeLock);
+
+ /*
+ * Dispatch the message!
+ */
+ (void) dispatchMsgToThread(&messageHdr);
+} /* processOpenSessionAnswerRadiusHA */
+
+/*
+ * Function: processOpenSessionIndication
+ *
+ * Arguments: AAA_Packet *packet, char *mnNAI, size_t mnNAILen
+ *
+ * Description: This routine will handle the OPEN_SESSION_INDICATION message.
+ * (The message from Diameter to the Home Agent)
+ *
+ * Returns: void
+ */
+static void
+processOpenSessionIndication(AAA_Packet *packet, char *mnNAI, size_t mnNAILen)
+{
+ static MessageHdr *messageHdr = NULL;
+ unsigned char *regReq;
+ size_t regReqLen;
+ char *faNai;
+ size_t faNaiLen;
+ ipaddr_t homeAddress, homeAgentAddress, foreignAgentAddress;
+ uint32_t sessionTimeout;
+ uint32_t FAHASpi, MNHASpi;
+ unsigned char *MNFAKey;
+ size_t MNFAKeyLen;
+ unsigned char *MNHAKey;
+ size_t MNHAKeyLen;
+ /* These are encrypted versions of the above */
+ unsigned char *HAFAKey;
+ size_t HAFAKeyLen;
+ unsigned char *HAMNKey;
+ size_t HAMNKeyLen;
+
+ /*
+ * Initialize hash on HA side.
+ */
+ /* Check to see if we are initialized */
+ if (!gbl_hashInitialized) {
+ (void) InitHash(&naiHash);
+ naiHash.uniqueData = 1; /* Set our unique flag */
+ gbl_hashInitialized = 1;
+ }
+
+ (void) hexdump("Got message:", (unsigned char *)packet,
+ ntohl(packet->length));
+
+ /* FOREIGN_AGENT_NAI */
+ faNai = aaaFindAvpPtr(packet, FOREIGN_AGENT_NAI, &faNaiLen);
+ if (!faNai || !faNaiLen) {
+ syslog(LOG_ERR, "ERROR: bad packet (no FOREIGN_AGENT_NAI)");
+ aaaSendErrorResponse(MOBILE_IP_OPEN_SESSION_INDICATION_RESPONSE,
+ -1, mnNAI, mnNAILen, packet->handle);
+ return;
+ }
+
+ /* REGISTRATION_REQUEST */
+ regReq = aaaFindAvpPtr(packet, REGISTRATION_REQUEST, &regReqLen);
+ if (!regReq || !regReqLen) {
+ syslog(LOG_ERR, "ERROR: bad packet (no REGISTRATION_REQUEST)");
+ aaaSendErrorResponse(MOBILE_IP_OPEN_SESSION_INDICATION_RESPONSE,
+ -2, mnNAI, mnNAILen, packet->handle);
+ return;
+ }
+
+ /* MOBILE_NODE_HOME_ADDRESS */
+ if (!aaaFindAvpInt(packet, MOBILE_NODE_HOME_ADDRESS,
+ (int *)&homeAddress)) {
+ syslog(LOG_ERR,
+ "ERROR: bad packet (no MOBILE_NODE_HOME_ADDRESS)");
+ aaaSendErrorResponse(MOBILE_IP_OPEN_SESSION_INDICATION_RESPONSE,
+ -3, mnNAI, mnNAILen, packet->handle);
+ return;
+ }
+
+ /* HOME_AGENT_ADDRESS */
+ if (!aaaFindAvpInt(packet,
+ HOME_AGENT_ADDRESS, (int *)&homeAgentAddress)) {
+ syslog(LOG_ERR, "ERROR: bad packet (no HOME_AGENT_ADDRESS)");
+ aaaSendErrorResponse(MOBILE_IP_OPEN_SESSION_INDICATION_RESPONSE,
+ -4, mnNAI, mnNAILen, packet->handle);
+ return;
+ }
+
+ /* FOREIGN_AGENT_ADDRESS */
+ if (!aaaFindAvpInt(packet, FOREIGN_AGENT_ADDRESS,
+ (int *)&foreignAgentAddress)) {
+ syslog(LOG_ERR,
+ "ERROR: bad packet (no FOREIGN_AGENT_ADDRESS)");
+ aaaSendErrorResponse(MOBILE_IP_OPEN_SESSION_INDICATION_RESPONSE,
+ -5, mnNAI, mnNAILen, packet->handle);
+ return;
+ }
+
+ /* FA-HA Spi */
+ if (!aaaFindAvpInt(packet, FA_HA_SPI, (int *)&FAHASpi)) {
+ syslog(LOG_ERR, "ERROR: bad packet (no "
+ "FA_HA_SPI)");
+ aaaSendErrorResponse(MOBILE_IP_OPEN_SESSION_INDICATION_RESPONSE,
+ -6, mnNAI, mnNAILen, packet->handle);
+ return;
+ }
+
+ /* HA-FA Key */
+ HAFAKey = aaaFindAvpPtr(packet, HA_FA_KEY, &HAFAKeyLen);
+ if (!HAFAKey || !HAFAKeyLen) {
+ syslog(LOG_ERR, "ERROR: bad packet (no HA_FA_KEY)");
+ aaaSendErrorResponse(MOBILE_IP_OPEN_SESSION_INDICATION_RESPONSE,
+ -8, mnNAI, mnNAILen, packet->handle);
+ return;
+ }
+
+
+ /* MN-FA Key */
+ MNFAKey = aaaFindAvpPtr(packet, MN_FA_KEY, &MNFAKeyLen);
+ if (!MNFAKey || !MNFAKeyLen) {
+ syslog(LOG_ERR, "ERROR: bad packet (no MN_FA_KEY)");
+ aaaSendErrorResponse(MOBILE_IP_OPEN_SESSION_INDICATION_RESPONSE,
+ -10, mnNAI, mnNAILen, packet->handle);
+ return;
+ }
+ /* MN-HA Spi */
+ if (!aaaFindAvpInt(packet, MN_HA_SPI, (int *)&MNHASpi)) {
+ syslog(LOG_ERR, "ERROR: bad packet (no MN_HA_SPI)");
+ aaaSendErrorResponse(MOBILE_IP_OPEN_SESSION_INDICATION_RESPONSE,
+ -12, mnNAI, mnNAILen, packet->handle);
+ return;
+ }
+ /* MN-HA Key */
+ MNHAKey = aaaFindAvpPtr(packet, MN_HA_KEY, &MNHAKeyLen);
+ if (!MNHAKey || !MNHAKeyLen) {
+ syslog(LOG_ERR, "ERROR: bad packet (no MN_HA_KEY)");
+ aaaSendErrorResponse(MOBILE_IP_OPEN_SESSION_INDICATION_RESPONSE,
+ -13, mnNAI, mnNAILen, packet->handle);
+ return;
+ }
+ /* HA-MN Key */
+ HAMNKey = aaaFindAvpPtr(packet, HA_MN_KEY, &HAMNKeyLen);
+ if (!HAMNKey || !HAMNKeyLen) {
+ syslog(LOG_ERR, "ERROR: bad packet (no HA_MN_KEY)");
+ aaaSendErrorResponse(MOBILE_IP_OPEN_SESSION_INDICATION_RESPONSE,
+ -14, mnNAI, mnNAILen, packet->handle);
+ return;
+ }
+
+ /* Session Timeout */
+ if (!aaaFindAvpInt(packet, SESSION_TIMEOUT, (int *)&sessionTimeout)) {
+ /* BAD error . . . malformed packet */
+ syslog(LOG_ERR, "ERROR: bad packet (no SESSION_TIMEOUT)");
+ aaaSendErrorResponse(MOBILE_IP_OPEN_SESSION_INDICATION_RESPONSE,
+ -15, mnNAI, mnNAILen, packet->handle);
+ return;
+ }
+
+ /*
+ *
+ * Add NAI to hash on home agent side so accounting messages
+ * can be generated.
+ */
+ (void) addNaiToHash(mnNAI, mnNAILen, NULL, 0, homeAddress,
+ homeAgentAddress, messageHdr);
+
+ /*
+ * Create our keys
+ */
+ if (aaaCreateKey(FAHASpi, HAFAKey, HAFAKeyLen, sessionTimeout) < 0) {
+ syslog(LOG_ERR, "Error: Invalid FA-HA SPI/Key pair");
+ aaaSendErrorResponse(MOBILE_IP_OPEN_SESSION_INDICATION_RESPONSE,
+ -17, mnNAI, mnNAILen, packet->handle);
+ return;
+ }
+
+ if (aaaCreateKey(MNHASpi, HAMNKey, HAMNKeyLen, sessionTimeout) < 0) {
+ syslog(LOG_ERR, "Error: Invalid MN-HA SPI/Key pair");
+ aaaSendErrorResponse(MOBILE_IP_OPEN_SESSION_INDICATION_RESPONSE,
+ -18, mnNAI, mnNAILen, packet->handle);
+ return;
+ }
+ aaaCreateAgent(packet, HOME_AGENT_ADDRESS, FAHASpi, sessionTimeout);
+
+ /*
+ * If we don't already have a message header,
+ * allocate one.
+ */
+ if (messageHdr == NULL) {
+ if ((messageHdr = AllocateMessageHdr()) == NULL) {
+ syslog(LOG_CRIT,
+ "Unable to allocate a message header");
+ aaaSendErrorResponse(
+ MOBILE_IP_OPEN_SESSION_INDICATION_RESPONSE,
+ -19, mnNAI, mnNAILen, packet->handle);
+ return;
+ }
+ }
+ aaaSetupMessageHdr(messageHdr, 0);
+ (void) memcpy(messageHdr->pkt, regReq, regReqLen);
+ messageHdr->pktLen = regReqLen;
+
+ /* Don't worry about byte ordering this. */
+ messageHdr->messageHandle = packet->handle;
+
+ /* Copy our fa NAI */
+ messageHdr->faNAI = malloc(faNaiLen + 1);
+ if (messageHdr->faNAI == NULL) {
+ syslog(LOG_CRIT,
+ "Unable to allocate a faNAI in message header");
+ aaaSendErrorResponse(
+ MOBILE_IP_OPEN_SESSION_INDICATION_RESPONSE,
+ -20, faNai, faNaiLen, packet->handle);
+ return;
+ }
+
+ (void) memcpy(messageHdr->faNAI, faNai, faNaiLen);
+ messageHdr->faNAI[faNaiLen] = 0; /* Drop the null */
+ messageHdr->faNAILen = faNaiLen;
+
+ messageHdr->mnAAASPI = 0; /* WORK - PRC - where do we get this */
+ messageHdr->algorithm = MD5;
+ messageHdr->mnHaSPI = MNHASpi;
+ (void) memcpy(messageHdr->mnHaKey, MNHAKey, MNHAKeyLen);
+ messageHdr->mnHaKeyLen = MNHAKeyLen;
+
+ (void) memcpy(messageHdr->mnFaKey, MNFAKey, MNFAKeyLen);
+ messageHdr->mnFaKeyLen = MNFAKeyLen;
+
+
+ messageHdr->faHaSPI = FAHASpi;
+
+ messageHdr->aaaSessionTimeout = sessionTimeout;
+ /*
+ * Dispatch the message!
+ */
+ (void) dispatchMsgToThread(&messageHdr);
+} /* processOpenSessionIndication */
+
+/*
+ * Function: processAuthFailure
+ *
+ * Arguments:
+ * MessageHdr * - pointer to message hdr
+ * mnNAI - nai
+ * mnNAILen - nai len
+ * result - result code returned by RADIUS
+ *
+ * Description: This function will handle a failure. (will send an error)
+ *
+ * Returns: void
+ */
+static void
+processAuthFailure(MessageHdr *msgHdr, char *mnNAI, size_t mnNAILen,
+ uint32_t result)
+{
+ uint32_t code;
+
+ syslog(LOG_ERR, "Error: processAuthFailure: %*.*",
+ mnNAILen, mnNAILen, mnNAI);
+
+ switch (result) {
+ case MIP_ADMINISTRATIVELY_PROHIBITED:
+ code = FA_ADM_PROHIBITED;
+ break;
+ case MIP_INSUFFICIENT_RESOURCES:
+ code = FA_INSUFFICIENT_RESOURCES;
+ break;
+ case MIP_FAILED_AUTHENTICATION:
+ code = FA_MN_AUTH_FAILURE;
+ break;
+ case MIP_REASON_UNSPECIFIED:
+ default:
+ code = FA_REASON_UNSPECIFIED;
+ break;
+ }
+
+ rejectFromFAToMN(msgHdr, msgHdr->ifEntry, code);
+
+
+} /* processAuthFailure */
+
+#ifdef TEST_DIAMETER
+static void
+aaaGenerateKey(unsigned char *key, size_t keyLen)
+{
+ static boolean_t initialized = _B_FALSE;
+ int32_t *intPtr;
+ int i;
+
+ /* Seed the random number generator once */
+ if (initialized == _B_FALSE) {
+ srand(time(NULL));
+ initialized = _B_TRUE;
+ }
+
+ if (keyLen % 4) {
+ syslog(LOG_ERR,
+ "ERROR: Key length must be a multiple of 4 (len = %d)",
+ keyLen);
+ }
+
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ intPtr = (int32_t *)key;
+
+ /* Build the key, 4 bytes at a time */
+ for (i = 0; i < (keyLen / 4); i++) {
+ intPtr[i] = rand();
+ }
+} /* aaaGenerateKey */
+
+static uint32_t
+aaaGenerateSpi()
+{
+ static uint32_t SPI = 0x80000000;
+ return (SPI++);
+} /* return a psuedo random spi */
+#endif /* TEST_DIAMETER */
+
+/*
+ * Function: aaaSendRegistrationReply
+ *
+ * Arguments: MessageHdr *messageHdr
+ *
+ * Description: This function will lookup the relivant data, and send
+ * a response back to DIAMETER. This function is called from
+ * the HomeAgent, in response to a OpenSessionIndication.
+ *
+ * Returns: int
+ */
+int
+aaaSendRegistrationReply(MessageHdr *messageHdr, size_t replyLen,
+ ipaddr_t homeAddress, ipaddr_t homeAgentAddress)
+{
+ unsigned char buffer[MAX_TCP_LEN];
+ AAA_Packet *packet;
+ uint32_t length;
+ int resultCode = 0; /* WORK -- pass this in */
+
+ /* Check to see if we are initialized */
+ if (!gbl_hashInitialized) {
+ (void) InitHash(&naiHash);
+ naiHash.uniqueData = 1; /* Set our unique flag */
+ gbl_hashInitialized = 1;
+ }
+
+ /* Build the message */
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ packet = (AAA_Packet *)buffer;
+ packet->protocol = htonl(DIAMETER);
+ packet->commandCode =
+ htonl(MOBILE_IP_OPEN_SESSION_INDICATION_RESPONSE);
+
+ packet->handle = messageHdr->messageHandle;
+
+ length = sizeof (AAA_Packet);
+
+ /* Add our AVPs */
+
+ /* Mobile Node NAI */
+ length += aaaAddAvp(MOBILE_NODE_NAI, &buffer[length],
+ MAX_TCP_LEN - length, messageHdr->mnNAI,
+ messageHdr->mnNAILen);
+
+ /* Foreign Agent NAI */
+ length += aaaAddAvp(FOREIGN_AGENT_NAI, &buffer[length],
+ MAX_TCP_LEN - length, messageHdr->faNAI,
+ messageHdr->faNAILen);
+
+ /* Registration Reply */
+ length += aaaAddAvp(REGISTRATION_REPLY, &buffer[length],
+ MAX_TCP_LEN - length, messageHdr->pkt,
+ replyLen);
+
+ /* Mobile Node Home Address */
+ length += aaaAddAvp(MOBILE_NODE_HOME_ADDRESS, &buffer[length],
+ MAX_TCP_LEN - length, &homeAddress,
+ sizeof (ipaddr_t));
+
+ /* Home Agent Address */
+ length += aaaAddAvp(HOME_AGENT_ADDRESS, &buffer[length],
+ MAX_TCP_LEN - length, &homeAgentAddress,
+ sizeof (ipaddr_t));
+
+ /* Session Timeout */
+ length += aaaAddAvp(SESSION_TIMEOUT, &buffer[length],
+ MAX_TCP_LEN - length, &messageHdr->aaaSessionTimeout,
+ sizeof (uint32_t));
+
+ /* Result Code */
+ length += aaaAddAvp(RESULT_CODE, &buffer[length],
+ MAX_TCP_LEN - length, &resultCode,
+ sizeof (uint32_t));
+
+ packet->length = htonl(length);
+
+ return (sendTCPPacket(buffer, length));
+} /* sendRegistrationReply */
+
+/*
+ * Function: AAAAuthenticateRegReq
+ *
+ * Arguments:
+ *
+ * Description: This function is called from the foreign agent. It is the
+ * first function that is called for a given mobile node, so
+ * we do all startup code here. (We add the entry to the
+ * hash.)
+ *
+ * Returns: int
+ *
+ */
+int
+AAAAuthenticateRegReq(unsigned char *reqPtr, uint32_t reqLen,
+ unsigned char *mnNAI, size_t mnNAILen, uint32_t aaaSPI,
+ unsigned char *mnChallengeResponse, uint32_t mnChallengeResponseLen,
+ uint32_t mnChallengeLen,
+ ipaddr_t homeAddress, ipaddr_t homeAgentAddress, boolean_t isFromHA,
+ uint32_t inIfindex, void *messageHdr, unsigned char *MNFAChallengeValue,
+ uint32_t MNFAChallengeValueLen)
+{
+ unsigned char buffer[MAX_TCP_LEN];
+ AAA_Packet *packet;
+ uint32_t length;
+ ipaddr_t faAddr;
+ uint32_t one = 1;
+ uint32_t zero = 0;
+
+
+ /* Check to see if we are initialized */
+ if (!gbl_hashInitialized) {
+ (void) InitHash(&naiHash);
+ naiHash.uniqueData = 1; /* Set our unique flag */
+ }
+
+ /* Build the message */
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ packet = (AAA_Packet *)buffer;
+ packet->protocol = htonl(aaaProtocol);
+ packet->commandCode = htonl(MOBILE_IP_OPEN_SESSION_REQUEST);
+ packet->handle = htonl(0);
+
+ length = sizeof (AAA_Packet);
+
+ /* Add our AVPs */
+
+ /*
+ * Radius server needs to know if this is coming from HA,
+ * in which case it'll send back the MN-HA key.
+ */
+ if (aaaProtocol == RADIUS) {
+ length += aaaAddAvp(IS_FROM_HA, &buffer[length],
+ MAX_TCP_LEN - length,
+ (isFromHA) ? &one : &zero, sizeof (uint32_t));
+ }
+
+ /* Mobile Node NAI */
+ length += aaaAddAvp(MOBILE_NODE_NAI, &buffer[length],
+ MAX_TCP_LEN - length, mnNAI, mnNAILen);
+
+ /* Foreign Agent NAI */
+ length += aaaAddAvp(FOREIGN_AGENT_NAI, &buffer[length],
+ MAX_TCP_LEN - length, maNai, strlen(maNai));
+
+ /* Foreign Agent Address */
+ faAddr = getClosestInterfaceAddr(homeAgentAddress);
+ length += aaaAddAvp(FOREIGN_AGENT_ADDRESS, &buffer[length],
+ MAX_TCP_LEN - length, &faAddr, sizeof (uint32_t));
+
+ /* Registration Request Packet */
+ length += aaaAddAvp(REGISTRATION_REQUEST, &buffer[length],
+ MAX_TCP_LEN - length, reqPtr, reqLen);
+
+ /* Challenge Bytes */
+ length += aaaAddAvp(NUMBER_OF_CHALLENGE_BYTES_IN_RR,
+ &buffer[length], MAX_TCP_LEN - length,
+ &mnChallengeLen, sizeof (uint32_t));
+
+ /* Mobile Node Response */
+ length += aaaAddAvp(MOBILE_NODE_RESPONSE,
+ &buffer[length], MAX_TCP_LEN - length,
+ mnChallengeResponse, mnChallengeResponseLen);
+
+
+ /* Mobile Node Home Address */
+ length += aaaAddAvp(MOBILE_NODE_HOME_ADDRESS, &buffer[length],
+ MAX_TCP_LEN - length, &homeAddress, sizeof (uint32_t));
+
+ /* Home Agent Address */
+ length += aaaAddAvp(HOME_AGENT_ADDRESS, &buffer[length],
+ MAX_TCP_LEN - length, &homeAgentAddress, sizeof (uint32_t));
+
+ /* MN-AAA SPI */
+ length += aaaAddAvp(MN_AAA_SPI, &buffer[length],
+ MAX_TCP_LEN - length, &aaaSPI, sizeof (uint32_t));
+
+ /* Radius needs MN-FA Challenge Value for authentication */
+ if (aaaProtocol == RADIUS) {
+ length += aaaAddAvp(MN_FA_CHALLENGE_VALUE, &buffer[length],
+ MAX_TCP_LEN - length, MNFAChallengeValue,
+ MNFAChallengeValueLen);
+ }
+
+ /* MN_HANDLE (only send if from FA in which case inIfindex is nonzero */
+ if (inIfindex != 0) {
+ length += aaaAddAvp(MN_HANDLE, &buffer[length],
+ MAX_TCP_LEN - length, &inIfindex, sizeof (uint32_t));
+ }
+
+ packet->length = htonl(length);
+
+ /* Add entry to hash */
+ if (addNaiToHash((char *)mnNAI, mnNAILen, mnChallengeResponse,
+ mnChallengeResponseLen, homeAddress, homeAgentAddress,
+ messageHdr)) {
+ /* Error! */
+ return (-1);
+ }
+ return (sendTCPPacket(buffer, length));
+} /* AAAAuthenticateRegReq */
+
+/*
+ * Function: sendCloseSession
+ *
+ * Arguments: AAA_Packet *packet, unsigned char *nai, size_t naiLen
+ *
+ * Description: This function will send the CLOSE_SESSION message
+ * to AAA server. It is called by the main thread when an
+ * MOBILE_IP_ACCOUNTING_STOP_ANSWER is received.
+ *
+ * Returns: int
+ */
+static int
+sendCloseSession(AAA_Packet *srcPacket, char *mnNAI, size_t mnNAILen)
+{
+ unsigned char buffer[MAX_TCP_LEN];
+ AAA_Packet *destPacket;
+ size_t length;
+ int32_t handle;
+ char *faNai;
+ size_t faNaiLen;
+ ipaddr_t homeAgentAddress, homeAddress;
+ uint32_t sessionTime;
+
+ /* Lookup the NAI to make sure it exists. */
+ handle = aaaLookupHandle(mnNAI, mnNAILen);
+ if (handle < 0) {
+ syslog(LOG_ERR, "Error: NAI not found!");
+ return (handle);
+ }
+
+ /* Retrieve Our Fields */
+ faNai = (char *)aaaFindAvpPtr(srcPacket, FOREIGN_AGENT_NAI, &faNaiLen);
+ if (!faNai) {
+ syslog(LOG_ERR, "Error: Foreign Agent NAI not found!");
+ return (-1);
+ }
+
+ if (!aaaFindAvpInt(srcPacket, MOBILE_NODE_HOME_ADDRESS,
+ (int *)&homeAddress)) {
+ syslog(LOG_ERR, "Error: Home Address not found!");
+ return (-1);
+ }
+ if (!aaaFindAvpInt(srcPacket, HOME_AGENT_ADDRESS,
+ (int *)&homeAgentAddress)) {
+ syslog(LOG_ERR, "Error: Home Agent Address not found!");
+ return (-1);
+ }
+ if (!aaaFindAvpInt(srcPacket, SESSION_TIME, (int *)&sessionTime)) {
+ syslog(LOG_ERR, "Error: Session Time not found!");
+ return (-1);
+ }
+
+ /* Build the message */
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ destPacket = (AAA_Packet *)buffer;
+ destPacket->protocol = htonl(aaaProtocol);
+ destPacket->commandCode = htonl(MOBILE_IP_CLOSE_SESSION_REQUEST);
+ destPacket->handle = htonl(handle);
+
+ length = sizeof (AAA_Packet);
+
+ /* Add our AVPs */
+ /* Mobile Node NAI */
+ length += aaaAddAvp(MOBILE_NODE_NAI, &buffer[length],
+ MAX_TCP_LEN - length, mnNAI, mnNAILen);
+
+ /* Foreign Agent NAI */
+ length += aaaAddAvp(FOREIGN_AGENT_NAI, &buffer[length],
+ MAX_TCP_LEN - length, faNai, faNaiLen);
+
+ /* Mobile Node Home Address */
+ length += aaaAddAvp(MOBILE_NODE_HOME_ADDRESS, &buffer[length],
+ MAX_TCP_LEN - length, &homeAddress, sizeof (uint32_t));
+
+ /* Home Agent Address */
+ length += aaaAddAvp(HOME_AGENT_ADDRESS, &buffer[length],
+ MAX_TCP_LEN - length, &homeAgentAddress, sizeof (uint32_t));
+
+ /* Mobile Node Session Time */
+ length += aaaAddAvp(SESSION_TIME, &buffer[length],
+ MAX_TCP_LEN - length, &sessionTime, sizeof (uint32_t));
+
+ destPacket->length = htonl(length);
+
+ return (sendTCPPacket(buffer, length));
+} /* sendCloseSession */
+
+static int
+sendCloseSessionAnswer(AAA_Packet *srcPacket, char *mnNAI, size_t mnNAILen,
+ boolean_t result)
+{
+ unsigned char buffer[MAX_TCP_LEN];
+ AAA_Packet *destPacket;
+ size_t length;
+ uint32_t resultCode;
+
+ /* LINTED */
+ destPacket = (AAA_Packet *)buffer;
+ destPacket->protocol = htonl(srcPacket->protocol);
+ destPacket->commandCode = htonl(MOBILE_IP_CLOSE_SESSION_ANSWER);
+ destPacket->handle = srcPacket->handle;
+
+ length = sizeof (AAA_Packet);
+
+ /* Add our AVPs */
+
+ /* Mobile Node NAI */
+ length += aaaAddAvp(MOBILE_NODE_NAI, &buffer[length],
+ MAX_TCP_LEN - length, mnNAI, mnNAILen);
+
+ /* Result code */
+ resultCode = result ? 0 : 1;
+ length += aaaAddAvp(RESULT_CODE, &buffer[length], MAX_TCP_LEN - length,
+ &resultCode, sizeof (uint32_t));
+
+ destPacket->length = htonl(length);
+
+ return (sendTCPPacket(buffer, length));
+}
+
+
+
+
+/*
+ * Function: sendAccountingRecord
+ *
+ * Arguments: commandCode record, unsigned char *mnNAI, size_t mnNAILen,
+ * ipaddr_t homeAddr, ipaddr_t coaAddr,
+ * ipaddr_t homeAgentAddr, int32_t sessionLifetime)
+ *
+ * Description: This function will send an accounting start record.
+ * ToDo: coaAddr is specified in this function calls' prototype,
+ * but it is not in the diameter api protocol. (PRC TODO)
+ *
+ * Returns: int
+ *
+ */
+int
+sendAccountingRecord(AAA_CommandCode code, unsigned char *mnNAI,
+ /* LINTED E_FUNC_ARG_UNUSED */
+ size_t mnNAILen, ipaddr_t homeAddress, ipaddr_t coaAddr,
+ ipaddr_t homeAgentAddress, uint32_t sessionTime, int32_t relindicator)
+{
+ unsigned char buffer[MAX_TCP_LEN];
+ AAA_Packet *packet;
+ uint32_t length;
+ int32_t handle;
+
+ /* Lookup the NAI to make sure it exists. */
+ handle = aaaLookupHandle((char *)mnNAI, mnNAILen);
+ if (handle < 0) {
+ syslog(LOG_ERR, "Error: NAI not found!");
+ return (handle);
+ }
+
+ switch (code) {
+ /* These codes are good */
+ case MOBILE_IP_ACCOUNTING_START_REQUEST:
+ case MOBILE_IP_ACCOUNTING_INTERIM_REQUEST:
+ case MOBILE_IP_ACCOUNTING_STOP_REQUEST:
+ break;
+ /* Everything else is an error */
+ default:
+ syslog(LOG_ERR, "ERROR: invalid code passed to "
+ "sendAccountingRecord (%d)", code);
+ return (-1);
+ } /* switch code */
+
+ /* Build the message */
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ packet = (AAA_Packet *)buffer;
+ packet->protocol = htonl(aaaProtocol);
+ packet->commandCode = htonl(code);
+ packet->handle = htonl(handle);
+
+ length = sizeof (AAA_Packet);
+
+ /* Add our AVPs */
+
+ /* Mobile Node NAI */
+ length += aaaAddAvp(MOBILE_NODE_NAI, &buffer[length],
+ MAX_TCP_LEN - length, mnNAI, mnNAILen);
+
+ /* Foreign Agent NAI */
+ length += aaaAddAvp(FOREIGN_AGENT_NAI, &buffer[length],
+ MAX_TCP_LEN - length, maNai, strlen(maNai));
+
+ /* Mobile Node Home Address */
+ length += aaaAddAvp(MOBILE_NODE_HOME_ADDRESS, &buffer[length],
+ MAX_TCP_LEN - length, &homeAddress, sizeof (uint32_t));
+
+ /* Home Agent Address */
+ length += aaaAddAvp(HOME_AGENT_ADDRESS, &buffer[length],
+ MAX_TCP_LEN - length, &homeAgentAddress, sizeof (uint32_t));
+
+ /* Mobile Node Session Time */
+ length += aaaAddAvp(SESSION_TIME, &buffer[length],
+ MAX_TCP_LEN - length, &sessionTime, sizeof (uint32_t));
+
+ /* Release Indicator (RADIUS ONLY) */
+ if (aaaProtocol == RADIUS) {
+ length += aaaAddAvp(RELEASE_INDICATOR, &buffer[length],
+ MAX_TCP_LEN - length, &relindicator, sizeof (uint32_t));
+ }
+ packet->length = htonl(length);
+
+ return (sendTCPPacket(buffer, length));
+
+} /* sendAccountingRecord */
+
+void
+aaaSendErrorResponse(uint32_t commandCode, int32_t returnCode, char *mnNAI,
+ size_t mnNAILen, uint32_t handle)
+{
+ unsigned char buffer[MAX_TCP_LEN];
+ AAA_Packet *packet;
+ uint32_t length;
+
+ /*
+ * If we don't already have a handle, look for it.
+ */
+ if (handle == 0) {
+ /* Lookup the NAI to make sure it exists. */
+ handle = aaaLookupHandle(mnNAI, mnNAILen);
+ if (((int)handle) < 0) {
+ syslog(LOG_ERR, "Error: NAI not found!");
+ handle = 0;
+ }
+ }
+
+ /* Build the message */
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ packet = (AAA_Packet *)buffer;
+ /* this function is only used for DIAMETER */
+ packet->protocol = htonl(DIAMETER);
+ packet->commandCode = htonl(commandCode);
+ packet->handle = htonl(handle);
+
+ length = sizeof (AAA_Packet);
+
+ /* Mobile Node NAI */
+ length += aaaAddAvp(MOBILE_NODE_NAI, &buffer[length],
+ MAX_TCP_LEN - length, mnNAI, mnNAILen);
+
+ /* resultCode */
+ length += aaaAddAvp(RESULT_CODE, &buffer[length],
+ MAX_TCP_LEN - length, &returnCode, sizeof (uint32_t));
+
+ packet->length = htonl(length);
+
+ /* Send packet */
+ (void) sendTCPPacket(buffer, length);
+} /* aaaSendErrorResponse */
+
+/*
+ * Function: initTCPSocket
+ *
+ * Arguments: in_port_t port
+ *
+ * Description: This routine binds a TCP socket to the specified port.
+ *
+ * Returns: int (the socket fd)
+ *
+ */
+static int
+initTCPSocket(in_port_t port)
+{
+ struct sockaddr_in sin;
+ int sinLength;
+ static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
+ int rc;
+
+ /*
+ * Make sure only one thread at a time executes this code.
+ */
+
+ rc = pthread_mutex_lock(&lock);
+ if (rc < 0) {
+ /* Wierd error! */
+ syslog(LOG_CRIT, "initTCPSocket: Error: Unable to lock mutex!");
+ return (-1);
+ }
+
+ if (gbl_TCPSocket != -1) {
+ /* We are already initialized. Exit */
+ syslog(LOG_WARNING,
+ "initTCPSocket: Warning: socket already initialized");
+ (void) pthread_mutex_unlock(&lock);
+ return (gbl_TCPSocket);
+ }
+
+ /*
+ * Get a socket.
+ */
+ if ((gbl_TCPSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ syslog(LOG_CRIT,
+ "initTCPSocket: socket failed (%d:%s)", errno,
+ strerror(errno));
+ gbl_TCPSocket = -1;
+ (void) pthread_mutex_unlock(&lock);
+ return (gbl_TCPSocket);
+ }
+
+ /*
+ * Initialize the sockaddr.
+ */
+ sinLength = sizeof (struct sockaddr_in);
+ (void) memset((char *)&sin, '\0', sinLength);
+
+ /*
+ * Get server's listening port number
+ */
+ sin.sin_port = htons(port);
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = inet_addr(gbl_aaaHost);
+
+ if (connect(gbl_TCPSocket, (struct sockaddr *)&sin, sinLength) < 0) {
+ syslog(LOG_ERR,
+ "initTCPSocket: connect failed (%d:%s)", errno,
+ strerror(errno));
+ (void) close(gbl_TCPSocket);
+ gbl_TCPSocket = -1;
+ (void) pthread_mutex_unlock(&lock);
+ return (gbl_TCPSocket);
+ }
+
+ /*
+ * If connected, advBusy flag should be set to True.
+ * This code will only be invoked when the connection
+ * is first started or a reconnection happens. Thus,
+ * advBusy can be set/unset for other future purposes
+ * in mipagent.
+ */
+ if (advBusy == _B_TRUE) {
+ enableService();
+ }
+
+ /* Success! */
+ (void) pthread_mutex_unlock(&lock);
+ return (gbl_TCPSocket);
+
+} /* initTCPSocket */
+
+
+/*
+ * Function: sendTCPPacket
+ *
+ * Arguments: buffer, length
+ *
+ * Description: Sends the buffer on the TCP socket, and adds it to the queue
+ *
+ * Returns: int (zero on success)
+ *
+ */
+static int
+sendTCPPacket(unsigned char *buffer, uint32_t length)
+{
+
+ int rc;
+
+ /* Make sure the socket is open */
+ if (gbl_TCPSocket == -1) {
+ (void) initTCPSocket(gbl_aaaPort);
+ if (gbl_TCPSocket == -1) {
+ return (-1);
+ }
+ }
+
+ /* finally, send it */
+ do {
+ rc = send(gbl_TCPSocket, buffer, length, 0);
+ } while ((rc == -1) && (errno == EINTR));
+
+ if (rc < 0) {
+ syslog(LOG_ERR, "sendTCPPacket: Error: send: !(%d:%s)", errno,
+ strerror(errno));
+ (void) close(gbl_TCPSocket);
+ gbl_TCPSocket = -1; /* Force a re-connect */
+ return (rc);
+ }
+ return (0);
+} /* sendTCPPacket */
+
+/*
+ * Function: readTCPPacket
+ *
+ * Arguments: unsigned char *buffer, uint32_t bufLen
+ *
+ * Description: Reads from the socket, ignoring EINTR, until a record is
+ * read. If it gets an error, it closes the socket, which will
+ * be re-opened on the next read or write.
+ *
+ * Returns: int
+ */
+static int
+readTCPPacket(unsigned char *buffer, uint32_t bufLen)
+{
+ int rc;
+
+ /* Make sure the socket is open */
+ if (gbl_TCPSocket == -1) {
+ (void) initTCPSocket(gbl_aaaPort);
+ if (gbl_TCPSocket == -1) {
+ return (-1);
+ }
+ }
+
+ /* Read, ignoring EINTRs */
+ do {
+ rc = recv(gbl_TCPSocket, buffer, bufLen, 0);
+ } while ((rc == -1) && (errno == EINTR));
+
+ if (rc == -1) {
+ syslog(LOG_ERR, "Error %d reading socket (%d:%s)", rc, errno,
+ strerror(errno));
+ (void) close(gbl_TCPSocket);
+ gbl_TCPSocket = -1; /* Force a re-connect */
+ }
+
+ return (rc);
+} /* readTCPPacket */
+
+
+
+
+#ifdef TEST_AAA
+
+#define TEST_NAI1 "test@sun.com"
+#define TEST_NAI2 "test2@sun.com"
+#define FA_NAI "foreignAgent@agents.everywhere.com"
+
+int
+main(int argc, char *argv[])
+{
+ int rc;
+ unsigned char reqPtr[] = {
+ /* Just some random data for testing */
+ 0x1, 0x3, 0x1, 0x3, 0x1, 0x3, 0x1, 0x3, 0x1, 0x3,
+ 0x1, 0x3, 0x1, 0x3, 0x1, 0x3, 0x1, 0x3, 0x1, 0x3,
+ 0x1, 0x3, 0x1, 0x3, 0x1, 0x3, 0x1, 0x3, 0x1, 0x3,
+ 0x1, 0x3 };
+ unsigned char mnChallenge[] = {
+ 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa
+ };
+ ipaddr_t homeAddress;
+ ipaddr_t homeAgentAddress;
+
+ if (argc != 2) {
+ (void) fprintf(stderr, "USAGE: %s <port>\n", argv[0]);
+ return (-1);
+ }
+
+ homeAddress = inet_addr("192.168.168.2");
+ homeAgentAddress = inet_addr("192.168.168.1");
+ gbl_aaaPort = (in_port_t)atoi(argv[1]);
+
+ (void) strcpy(maNai, FA_NAI);
+
+ if ((rc = startAAATaskThread()) != 0) {
+ (void) fprintf(stderr,
+ "Error: rc = %d when calling startAAATaskThread\n",
+ rc);
+ return (rc);
+ }
+
+ rc = AAAAuthenticateRegReq(reqPtr, sizeof (reqPtr),
+ TEST_NAI1, strlen(TEST_NAI1),
+ mnChallenge, sizeof (mnChallenge),
+ homeAddress, homeAgentAddress, _B_FALSE, NULL);
+ if (rc != 0) {
+ (void) fprintf(stderr,
+ "Error: rc = %d when calling"
+ " AAAAuthenticatateRegReq\n", rc);
+ /* return (rc); */
+ }
+
+ rc = sendAccountingStartRecord((unsigned char *)TEST_NAI1,
+ strlen(TEST_NAI1), 0, 1, 2, 3);
+ if (rc != 0) {
+ (void) fprintf(stderr,
+ "Error: rc = %d when calling"
+ " sendAccountingStartRecord\n",
+ rc);
+ }
+
+ (void) fprintf(stderr, "Sleeping for 1\n"); fflush(stderr);
+ (void) sleep(1);
+ rc = sendAccountingInterimRecord((unsigned char *)TEST_NAI1,
+ strlen(TEST_NAI1), 0, 1, 2, 3);
+ if (rc != 0) {
+ (void) fprintf(stderr, "Error: rc = %d when calling "
+ "sendAccountingInterimRecord\n", rc);
+ }
+
+ rc = sendAccountingInterimRecord((unsigned char *)TEST_NAI2,
+ strlen(TEST_NAI1), 0, 1, 2, 3);
+ if (rc == 0) {
+ (void) fprintf(stderr, "Error: rc = %d when calling "
+ "sendAccountingInterimRecord\n", rc);
+ }
+
+ rc = sendAccountingStopRecord((unsigned char *)TEST_NAI1,
+ strlen(TEST_NAI1), 0, 1, 2, 3);
+ if (rc != 0) {
+ (void) fprintf(stderr, "Error: rc = %d when calling "
+ "sendAccountingStopRecord\n", rc);
+ }
+
+ (void) fprintf(stderr, "Sleeping for a sec . . . ");
+ (void) sleep(2);
+ (void) fprintf(stderr,
+ "Sending a straggling message . . . should be an error\n");
+ rc = sendAccountingInterimRecord((unsigned char *)TEST_NAI1,
+ strlen(TEST_NAI1), 0, 1, 2, 3);
+ if (rc == 0) {
+ (void) fprintf(stderr, "Error: rc = %d when calling "
+ "sendAccountingInterimRecord\n", rc);
+ }
+
+ (void) fprintf(stderr, "Thread hanging . . .\n"); fflush(stderr);
+ for (;;) {
+ (void) sleep(1); fflush(stderr); fflush(stdout);
+ }
+
+ return (rc);
+} /* main */
+
+#endif /* TEST_AAA */
+
+/* fin aaa.c */
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mipagent/aaa.h b/usr/src/cmd/cmd-inet/usr.lib/mipagent/aaa.h
new file mode 100644
index 0000000000..07b01cdad7
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mipagent/aaa.h
@@ -0,0 +1,245 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _AAA_H
+#define _AAA_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * aaa.h -- AAA defines and structures (Diameter interface)
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * If the following is defined, the foreign agent will communicate directly
+ * with the home agent, (using a proxy) but will still use the diameter API
+ * protocol. The only changes to the code will be the handling the different
+ * messages, and the generation of keys.
+ *
+ * #define TEST_DIAMETER
+ */
+
+/*
+ * These constants are used to control where AAA looks for the diameter
+ * server.
+ */
+#define LOOPBACK "127.0.0.1"
+#define AAA_PORT 769
+#define MAX_SERVER_NAME_LEN 1024
+
+#ifndef MIN
+#define MIN(x, y) (((x) > (y))?(y):(x))
+#endif
+
+/*
+ * Maximum lengths of NAIs and challenges
+ */
+#define MAX_NAI_LEN 256
+#define MAX_CHALLENGE_LEN 16
+
+/* Max size of an incomming or outgoing tcp packet. */
+#define MAX_TCP_LEN 4096
+
+/* biggest key to generate (for debug testing) */
+#define MAX_GENERATE_KEY_LEN 16
+
+/*
+ * SPI values to be used with AAA protocols.
+ * Currently only Radius is used.
+ */
+#define RADIUS_SPI 2
+
+/*
+ * Diameter API defines. (from the specification
+ */
+typedef enum {
+ MOBILE_IP_OPEN_SESSION_REQUEST = 1, /* 01 */
+ MOBILE_IP_OPEN_SESSION_ANSWER, /* 02 */
+ MOBILE_IP_OPEN_SESSION_INDICATION, /* 03 */
+ MOBILE_IP_OPEN_SESSION_INDICATION_RESPONSE, /* 04 */
+ MOBILE_IP_ACCOUNTING_START_REQUEST, /* 05 */
+ MOBILE_IP_ACCOUNTING_START_ANSWER, /* 06 */
+ MOBILE_IP_ACCOUNTING_INTERIM_REQUEST, /* 07 */
+ MOBILE_IP_ACCOUNTING_INTERIM_ANSWER, /* 08 */
+ MOBILE_IP_ACCOUNTING_STOP_REQUEST, /* 09 */
+ MOBILE_IP_ACCOUNTING_STOP_ANSWER, /* 10 */
+ MOBILE_IP_CLOSE_SESSION_REQUEST, /* 11 */
+ MOBILE_IP_CLOSE_SESSION_ANSWER /* 12 */
+} AAA_CommandCode;
+
+typedef enum {
+ MOBILE_NODE_NAI = 1, /* 01 */
+ FOREIGN_AGENT_NAI, /* 02 */
+ REGISTRATION_REQUEST, /* 03 */
+ NUMBER_OF_CHALLENGE_BYTES_IN_RR, /* 04 */
+ MOBILE_NODE_RESPONSE, /* 05 */
+ MOBILE_NODE_HOME_ADDRESS, /* 06 */
+ HOME_AGENT_ADDRESS, /* 07 */
+ RESULT_CODE, /* 08 */
+ REGISTRATION_REPLY, /* 09 */
+ MN_FA_SPI, /* 10 */
+ MN_FA_KEY, /* 11 */
+ FA_HA_SPI, /* 12 */
+ FA_HA_KEY, /* 13 */
+ SESSION_TIMEOUT, /* 14 */
+ HA_FA_KEY, /* 15 */
+ FA_MN_KEY, /* 16 */
+ MN_HA_SPI, /* 17 */
+ MN_HA_KEY, /* 18 */
+ HA_MN_KEY, /* 19 */
+ SESSION_TIMEOUT_1, /* 20 */
+ SESSION_TIME, /* 21 */
+ FOREIGN_AGENT_ADDRESS, /* 22 */
+ MN_AAA_SPI, /* 23 */
+ IS_FROM_HA, /* 24 */
+ REV_TUN, /* 25 */
+ MN_HANDLE, /* 26 */
+ RELEASE_INDICATOR, /* 27 */
+ MN_FA_CHALLENGE_VALUE /* 28 */
+} AAA_AVPCode;
+
+/*
+ * This structure is the header of a diameter API message.
+ * All items are sent in network-byte-ordering.
+ */
+typedef struct {
+ uint32_t protocol;
+ uint32_t commandCode;
+ uint32_t handle;
+ uint32_t length;
+} AAA_Packet;
+
+/*
+ * AAA protocols API defines. (from the specification)
+ */
+typedef enum {
+ AAA_NONE = 0, /* 00 */
+ DIAMETER, /* 01 */
+ RADIUS /* 02 */
+} AAA_Protocol_Code;
+
+/*
+ * MIPResultCode is used in the protocol to communicate Mobile-IP
+ * specific errors. Per AAA specification.
+ */
+typedef enum {
+ MIP_SUCCESS = 0,
+ MIP_REASON_UNSPECIFIED = 1,
+ MIP_ADMINISTRATIVELY_PROHIBITED = 2,
+ MIP_INSUFFICIENT_RESOURCES = 3,
+ MIP_FAILED_AUTHENTICATION = 4
+} MIPResultCode;
+
+/*
+ * This structure refers to an Attribute Value Pair. The last field, data
+ * is really a place holder, rather than a data item. This structure is either
+ * allocated without a sizeof: malloc( sizeof(uint32_t) * 2 + dataLen),
+ * or it is used to point to the structure being returned:
+ * avpPtr = (AAA_AVP *)buffer;
+ *
+ * When this is used to point to a buffer, it can be moved along, byte by byte
+ * and the data place holder will be able to retrieve the data. This is
+ * necessary, since packets are variable length:
+ *
+ *
+ * Buffer:
+ *
+ * +-----------------------+
+ * | Header |
+ * +-----------------------+
+ * | First Record |
+ * | |
+ * |_________|-------------|
+ * | Second Record |
+ * |______________|--------|
+ *
+ * So, to parse the above, an AAA_AVP pointer would be set to the first record,
+ * the avpCode and Length would be read, and the data copied form the data
+ * portion of the record. Basically, this method is used so that the data
+ * Can be read as follows:
+ *
+ * AAA_Avp_p = (AAA_AVP *)&buffer[offset];
+ * code = AAA_Avp_p->code;
+ * length = AAA_Avp_p->length);
+ * memcpy(data, AAA_Avp_p->data, length - (2 * sizeof (uint32_t));
+ * offset += length;
+ *
+ * It could have also been done with pointer arrithmetic, but I thought it
+ * was harder to read/understand:
+ *
+ * code = *((uint32_t *)&buffer[offset]);
+ * ofset += sizeof (uint32_t);
+ * length = *((uint32_t *)&buffer[offset]);
+ * offset += sizeof (uint32_t);
+ * memcpy(data, &buffer[offset], length - (2 * sizeof (uint32_t));
+ * offset += length;
+ *
+ * (by the way -- the above code does not take into account byte alignment or
+ * network byte ordering, so don't cut and paste from these comments!)
+ */
+typedef struct {
+ uint32_t avpCode;
+ uint32_t length;
+ unsigned char data[1];
+} AAA_AVP;
+
+/* This enum defines the current state of the hash node */
+typedef enum {
+ Initialized = 0,
+ WaitingForAuthorization,
+ Authorized
+} AAA_State;
+
+
+/* This structure is the structure that is kept in the hash */
+typedef struct {
+ rwlock_t aaaNodeLock;
+ int32_t handle;
+ char mnNAI[MAX_NAI_LEN];
+ AAA_State State;
+ unsigned char mnChallenge[MAX_CHALLENGE_LEN];
+ ipaddr_t homeAddress;
+ ipaddr_t homeAgentAddress;
+ uint32_t timeOut;
+ void *messageHdr; /* RegReq from MN, used for Radius */
+} AAA_HashEntry;
+
+int startAAATaskThread();
+int sendAccountingRecord(AAA_CommandCode, unsigned char *, uint32_t,
+ ipaddr_t, ipaddr_t, ipaddr_t, uint32_t, int32_t);
+int AAAAuthenticateRegReq(unsigned char *, uint32_t, unsigned char *,
+ unsigned int, uint32_t, unsigned char *, uint32_t, uint32_t, ipaddr_t,
+ ipaddr_t, boolean_t, uint32_t, void *, unsigned char *, uint32_t);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _AAA_H */
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mipagent/agent.c b/usr/src/cmd/cmd-inet/usr.lib/mipagent/agent.c
new file mode 100644
index 0000000000..e2c1521bcf
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mipagent/agent.c
@@ -0,0 +1,6816 @@
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 1987 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * file: agent.c
+ *
+ * This file contains the routines used to parse and process the
+ * Mobile-IP registration request and reply, as well as the routines
+ * used to manage the visitor and binding entries.
+ *
+ * This file contains the main mipagent routine.
+ */
+
+#include <stdio.h>
+#include <dlfcn.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <sys/sysmacros.h>
+#include <md5.h>
+#include <locale.h>
+#include <errno.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <sys/dlpi.h>
+#include <stropts.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+#include "mip.h"
+#include "agent.h"
+#ifdef RADIUS_ENABLED
+#include "radlib.h"
+#endif /* RADIUS_ENABLED */
+#include "auth.h"
+#include "pool.h"
+#include "setup.h"
+#include "hash.h"
+#include "agentKernelIntfce.h"
+#include "conflib.h"
+#include "mipagentstat_door.h"
+
+/* Controls verbosity of debug messages when compiled w/ "-D MIP_DEBUG" */
+int logVerbosity = 0;
+
+int IDfreshnessSlack = DEFAULT_FRESHNESS_SLACK;
+/*
+ * We no longer use regLifetime global
+ */
+int advLifetime = DEFAULT_MAX_ADV_TIME;
+int periodicInterval = DEFAULT_GARBAGE_COLLECTION_INTERVAL;
+
+int visitorEntryHighWaterMark = DEFAULT_HIGH_VISITORS;
+int visitorEntryLowWaterMark = DEFAULT_LOW_VISITORS;
+int performanceInterval = 0;
+boolean_t faNAIadv = _B_FALSE; /* Determines whether we advertise */
+ /* our NAI */
+boolean_t faChallengeAdv = _B_FALSE; /* Determines whether we advertise */
+ /* challenges */
+boolean_t mfAuthRequired = _B_FALSE; /* Is the MF Authentication Ext */
+ /* Required? */
+boolean_t fhAuthRequired = _B_FALSE; /* Is the FH Authentication Ext */
+ /* Required? */
+AAA_Protocol_Code aaaProtocol = AAA_NONE; /* AAA_NONE, DIAMETER, RADIUS */
+
+boolean_t shutdown_flag = _B_FALSE; /* Are we shutting down? */
+boolean_t daemonize = _B_TRUE; /* By default, we are a daemon */
+boolean_t disableSNMP = _B_FALSE; /* By default, SNMP is enabled */
+
+/* these are the IPsec install and remove policy commands */
+char *ipsec_policy_action[] = {
+ "/usr/sbin/ipsecconf -a /dev/stdin -q",
+ "/usr/sbin/ipsecconf -r /dev/stdin -q",
+ NULL
+};
+
+/* these expand the same index of policy to what it means to humans */
+char *ipsec_policy_string[] = {
+ "registration request apply policy",
+ "registration reply apply policy",
+ "tunnel apply policy",
+ "reverse tunnel apply policy",
+ "registration request permit policy",
+ "registration reply permit policy",
+ "tunnel permit policy",
+ "reverse tunnel permit policy",
+ NULL
+};
+
+#define BUCKET 0 /* Bucket in hashtable to start enumeration at */
+#define OFFSET 1 /* Offset within BUCKET to start enumeration at */
+#define PERF_MSG_SIZE 256
+
+#ifdef RADIUS_ENABLED
+/* Radius Variables */
+int radiusEnabled = 0;
+#define RADIUS_LOOKUP_TIME 60
+#define RADIUS_DEBUG
+char radiusSharedLibrary[MAX_FN_LEN];
+int (*radInitializeApi)();
+int (*radLookupData)(char **sessionId, char *key, RadData *dest);
+int (*radCloseSession)(char *sessionId, char *key, void *accountingInfo);
+#endif /* RADIUS_ENABLED */
+
+/*
+ * Default Values...
+ */
+uint32_t defaultPool = 0;
+uint32_t defaultNodeSPI = 0;
+
+
+/* ----------------- Common to all mobility agents ------------------- */
+/*
+ * This table stores configuration information about agent
+ * advertisements. There's one entry for each mobility
+ * supporting interface.
+ */
+HashTable maAdvConfigHash;
+
+/*
+ * This table stores all of the Security Violations
+ */
+HashTable mipSecViolationHash;
+
+/*
+ * This table stores all of the Security Assocations
+ */
+HashTable mipSecAssocHash;
+
+/*
+ * This table has one entry for each known Mobility Agent
+ */
+HashTable mipAgentHash;
+
+/*
+ * This table has one entry for each active tunnel number
+ */
+HashTable mipTunlHash;
+
+/*
+ * Counters common to all Mobility Agents
+ */
+CommonCounters commonCounters;
+
+
+char maNai[MAX_NAI_LENGTH];
+
+/*
+ * This table has one entry for each pool defined in the config file
+ */
+HashTable mipPoolHash;
+
+/* ------------------ Specific to foreign agents -------------------- */
+/*
+ * This table stores information about visitors for which this
+ * mobility agent is a foreign agent. Some of the entries may
+ * correspond to unfulfilled registration requests.
+ */
+HashTable faVisitorHash;
+
+/*
+ * Counters maintained by Foreign Agents
+ */
+ForeignAgentCounters faCounters;
+
+/*
+ * We need to keep track of the last two Challenge Values that
+ * we have advertised in order to check for replays.
+ */
+char faLastChallengeIssued[2][ADV_CHALLENGE_LENGTH];
+
+/* ------------------ Specific to home agents -------------------- */
+/*
+ * This table has one entry for each mobile node for which a mobility
+ * agent offers Home Agent services.
+ */
+
+HashTable haMobileNodeHash;
+
+
+/*
+ * Counters maintained by Home Agents
+ */
+HomeAgentCounters haCounters;
+
+/* Security related stuff */
+#ifdef FIREWALL_SUPPORT
+DomainInfo domainInfo;
+#endif /* FIREWALL_SUPPORT */
+/* ----------------------------------------------------------------- */
+
+extern int Initialize(char *configFile);
+extern void printBuffer(unsigned char *, int);
+extern int sendUDPmessage(int, unsigned char *, int, ipaddr_t, in_port_t);
+extern boolean_t HAisIDok(uint32_t, uint32_t, uint32_t, uint32_t, int);
+extern void HAnewID(uint32_t *, uint32_t *, uint32_t, uint32_t, int, boolean_t);
+extern void HAstoreID(uint32_t *, uint32_t *, uint32_t, uint32_t, int,
+ boolean_t);
+extern char *hwAddrWrite(unsigned char *, char *);
+extern char *ntoa(uint32_t, char *);
+extern char *sprintTime(char *, int);
+extern char *sprintRelativeTime(char *, int);
+extern char *err2str(int);
+extern int restoreAgentState(void);
+extern void Finalize(int);
+extern void delFAVEptr(FaVisitorEntry *, boolean_t, uint32_t);
+extern void delHABEent(HaMobileNodeEntry *, HaBindingEntry *);
+extern int startPeriodicTaskThread(void);
+extern int startSNMPTaskThread(void);
+extern int startDispatcherTaskThread(void);
+extern int startStatServer();
+static int startPerfTestServer(void);
+extern int aaaSendRegistrationReply(MessageHdr *, size_t,
+ ipaddr_t, ipaddr_t);
+extern MobilityAgentEntry *findMaeFromIp(ipaddr_t address, int lockType);
+
+void forwardFromFAToHA(MessageHdr *, MaAdvConfigEntry *, boolean_t);
+void rejectFromFAToMN(MessageHdr *, MaAdvConfigEntry *, int);
+void rejectFromICMPToMN(MessageHdr *, ipaddr_t, int);
+extern uint32_t getRandomValue();
+extern int gettunnelno(ipaddr_t, ipaddr_t);
+extern int arpIfadd(ipaddr_t, char *, uint32_t);
+extern int arpIfdel(ipaddr_t, char *, uint32_t);
+extern void ifname2devppa(char *, char *, int *);
+extern int dlattachreq(int, int);
+extern int dlokack(int, char *);
+extern int dlbindreq(int, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t);
+extern int dlbindack(int, char *);
+static int mip_strioctl(int, int, void *, int, int);
+static void HAprocessRegRequestContinue(MessageHdr *, MaAdvConfigEntry *,
+ ipaddr_t *, int, HaMobileNodeEntry *, uint32_t, uint32_t);
+static void HABuildRegReply(MessageHdr *, MaAdvConfigEntry *,
+ int, HaMobileNodeEntry *, uint32_t, MipSecAssocEntry *, int,
+ boolean_t, uint32_t, boolean_t);
+static boolean_t acceptFAVEHashLookup(void *, uint32_t, uint32_t, uint32_t);
+
+int installIPsecPolicy(char *);
+
+#ifdef FIREWALL_SUPPORT
+/*
+ * Function: isInsideProtectedDomain
+ *
+ * Arguments: addr - peer's address.
+ *
+ * Description: Check each interval to see if the given addr is inside
+ * the protected domain
+ *
+ * Returns: boolean - _B_TRUE if the address is inside the protected domain.
+ *
+ */
+boolean_t
+isInsideProtectedDomain(ipaddr_t addr)
+{
+ int i;
+
+ if (domainInfo.addrIntervalCnt == 0)
+ return (_B_TRUE);
+
+ for (i = 0; i < domainInfo.addrIntervalCnt; i++) {
+ if (((addr ^ domainInfo.addr[i]) & domainInfo.netmask[i]) == 0)
+ return (_B_TRUE);
+ }
+
+ return (_B_FALSE);
+}
+#endif /* FIREWALL_SUPPORT */
+
+/*
+ * Function: mkRegExtList
+ *
+ * Arguments: messageHdr - Message Control Block
+ * headerLength - length of the Mobile-IP heaeder
+ *
+ * Description: Examines the packet in the message header starting at the
+ * packet + the size of the header, which is provided as an
+ * argument. This function will place all the Mobile IP
+ * extensions encountered in the message header's extType[]
+ * and their start points in the extIdx[] field. The maximum
+ * size of these two arrays is stored in the message header's
+ * extCnt field. At termination, the extCnt field contains the
+ * number of extensions found in the message.
+ *
+ * Returns: int - 0 if successful.
+ *
+ */
+static int
+mkRegExtList(MessageHdr *messageHdr, size_t headerLength)
+{
+ unsigned char *currentPos;
+ /*
+ * Support for different extension header formats
+ */
+ uint16_t longLength;
+ uint8_t shortLength;
+ size_t bufLength;
+ size_t bytesSeen = 0;
+ size_t extSeen = 0;
+
+ currentPos = (unsigned char *)(((char *)messageHdr->pkt) +
+ headerLength);
+ bufLength = (int)(((char *)messageHdr->pktLen) - headerLength);
+
+ /*
+ * Protection against packets that have no extensions.
+ */
+ if (bufLength == 0) {
+ return (0);
+ }
+
+ mipverbose(("Found extensions:"));
+
+ while ((bytesSeen < (bufLength - 1)) &&
+ (extSeen < MAX_EXPECTED_EXTENSIONS)) {
+ messageHdr->extType[extSeen] = *currentPos;
+ messageHdr->extIdx[extSeen] = currentPos;
+ mipverbose((" <%d, %d, ...>", messageHdr->extType[extSeen],
+ *(messageHdr->extIdx[extSeen] + MIP_EXT_LENGTH)));
+
+ /*
+ * The latest Mobile IP Extensions have a
+ * different extension header, compliant with, or close to,
+ * the MIER specification. The following checks the
+ * actual extension type in order to determine how to
+ * parse the extension header.
+ */
+ switch (messageHdr->extType[extSeen]) {
+ case REG_GEN_AUTH_EXT_TYPE:
+ case REG_GEN_MN_FA_KEY_EXT_TYPE:
+ case REG_GEN_MN_HA_KEY_EXT_TYPE:
+ /*
+ * The following handles the generalized Authentication
+ * extension as well as the generalized keying
+ * extensions.
+ */
+ messageHdr->extSubType[extSeen] =
+ *(currentPos + MIP_EXT_GEN_SUB_TYPE);
+ (void) memcpy(&longLength,
+ currentPos + MIP_EXT_LONG_LENGTH,
+ sizeof (uint16_t));
+ longLength = ntohs(longLength);
+ messageHdr->extHdrLength[extSeen] =
+ sizeof (mierLongExt);
+ messageHdr->extLength[extSeen] = longLength;
+ messageHdr->extData[extSeen] =
+ currentPos + MIP_EXT_LONG_LENGTH_DATA;
+ break;
+
+ case REG_CRIT_VENDOR_SPEC_EXT_TYPE:
+ /*
+ * The Vendor Specific Extension has a
+ * drastically different header, and not supporting
+ * the header would cause us to drop any packets
+ * with such headers. Critical Vendor Specific
+ * Extensions are now supported.
+ */
+ (void) memcpy(&messageHdr->extSubType[extSeen],
+ currentPos + MIP_EXT_CVSE_VENDOR_SUB_TYPE,
+ sizeof (uint16_t));
+ messageHdr->extSubType[extSeen] =
+ ntohs(messageHdr->extSubType[extSeen]);
+ (void) memcpy(&messageHdr->extVendorId[extSeen],
+ currentPos + MIP_EXT_CVSE_VENDOR_ID_TYPE,
+ sizeof (uint32_t));
+ messageHdr->extVendorId[extSeen] =
+ ntohl(messageHdr->extVendorId[extSeen]);
+ (void) memcpy(&longLength,
+ currentPos + MIP_EXT_LONG_LENGTH,
+ sizeof (uint16_t));
+ longLength = ntohs(longLength);
+#ifdef KEY_DISTRIBUTION
+ messageHdr->extHdrLength[extSeen] =
+ sizeof (vendorSpecExt);
+#else /* KEY_DISTRIBUTION */
+ messageHdr->extHdrLength[extSeen] =
+ VENDOR_SPEC_EXT_HDR_LEN;
+#endif /* KEY_DISTRIBUTION */
+ messageHdr->extLength[extSeen] = longLength;
+ messageHdr->extData[extSeen] =
+ currentPos + MIP_EXT_CVSE_VENDOR_ID_DATA;
+ break;
+
+ case REG_NORMAL_VENDOR_SPEC_EXT_TYPE:
+ /*
+ * The Vendor Specific Extension has a
+ * drastically different header, and not supporting
+ * the header would cause us to drop any packets
+ * with such headers. Normal Vendor Specific Extensions
+ * are now supported.
+ */
+ (void) memcpy(&shortLength, currentPos + MIP_EXT_LENGTH,
+ sizeof (uint8_t));
+ messageHdr->extHdrLength[extSeen] = sizeof (regExt);
+ messageHdr->extLength[extSeen] = shortLength;
+ messageHdr->extSubType[extSeen] =
+ *(currentPos + MIP_EXT_NVSE_VENDOR_SUB_TYPE);
+ messageHdr->extVendorId[extSeen] =
+ *(currentPos + MIP_EXT_NVSE_VENDOR_ID_TYPE);
+ messageHdr->extData[extSeen] =
+ currentPos + MIP_EXT_NVSE_VENDOR_ID_DATA;
+ break;
+
+ default:
+ /*
+ * The following code supports the traditional
+ * extensions.
+ */
+ (void) memcpy(&shortLength, currentPos + MIP_EXT_LENGTH,
+ sizeof (uint8_t));
+ messageHdr->extHdrLength[extSeen] = sizeof (regExt);
+ messageHdr->extLength[extSeen] = shortLength;
+ messageHdr->extData[extSeen] = currentPos +
+ MIP_EXT_DATA;
+ break;
+ }
+
+
+ /*
+ * protect against bogus packets.
+ */
+ if ((messageHdr->extLength[extSeen] +
+ messageHdr->extHdrLength[extSeen]) >
+ (bufLength - bytesSeen)) {
+ messageHdr->extCnt = extSeen;
+ return (-1);
+ }
+ bytesSeen += messageHdr->extLength[extSeen] +
+ messageHdr->extHdrLength[extSeen];
+ currentPos += messageHdr->extLength[extSeen] +
+ messageHdr->extHdrLength[extSeen];
+
+ extSeen++;
+ }
+ mipverbose((" (%d exts)\n", messageHdr->extCnt));
+
+
+ if ((bytesSeen < (bufLength - 1)) &&
+ (extSeen >= MAX_EXPECTED_EXTENSIONS)) {
+ syslog(LOG_ERR, "Too many extensions.");
+ return (-1);
+ } else if (bytesSeen != bufLength) {
+ syslog(LOG_ERR, "Extensions buffer too small.");
+ return (-1);
+ } else {
+ messageHdr->extCnt = extSeen;
+ return (0);
+ }
+}
+
+/*
+ * Function: IsPacketFromMnValid
+ *
+ * Arguments: messageHdr - Message Control Block
+ *
+ * Description: This function is called by the Foreign Agent when a
+ * registration request is received from a Mobile Node
+ * and will step through each extension that was stored
+ * in the message header's extType to ensure that the
+ * packet contains the required extensions and that the
+ * message follows the protocol rules.
+ *
+ * The rules are:
+ * 1. If the challenge is being advertised, it MUST be
+ * present in the packet.
+ * 2. If the MN-AAA is present, then the MN-FA, the
+ * NAI and the challenge extensions MUST be present.
+ * 3. If MN-AAA is not present, MN-HA MUST be present.
+ * 4. If the MN-FA is present, it MUST be present after
+ * either the MN-HA or the MN-AAA extension.
+ *
+ * Returns: returns a Mobile IP error code, zero if successful.
+ * return -1 if packet needs to be dropped.
+ *
+ */
+/*
+ * The message parsing routines must be able to return
+ * a variety of Mobile IP error codes in order to support all error
+ * cases.
+ */
+static int
+IsPacketFromMnValid(MessageHdr *messageHdr)
+{
+ regRequest *requestPtr;
+ boolean_t foundMHauth = _B_FALSE;
+ boolean_t foundMFauth = _B_FALSE;
+ boolean_t foundMAauth = _B_FALSE;
+ boolean_t foundNAI = _B_FALSE;
+ boolean_t foundChallenge = _B_FALSE;
+ int i;
+
+ /* LINTED BAD_PTR_CAST_ALIGN */
+ requestPtr = (regRequest *) messageHdr->pkt;
+
+ /*
+ * According to the latest Challenge draft, here is the
+ * rule. If the challenge was advertised, then it MUST
+ * be present in ALL packets. If the Mobile-AAA authentication
+ * extension is present, then the NAI MUST be present.
+ *
+ * Normal RFC 2002 rules apply, with the exception that if
+ * the Mobile-Home authentication is NOT present, then the
+ * Mobile-AAA authentication extension MUST be present.
+ */
+ for (i = 0; i < messageHdr->extCnt; i++) {
+ mipverbose(("IsPacketFromMnValid[%d] = %d\n", i,
+ messageHdr->extType[i]));
+ switch (messageHdr->extType[i]) {
+ case REG_MH_AUTH_EXT_TYPE:
+ /* we should not have seen any other auth extensions */
+ if (foundMHauth == _B_TRUE) {
+ syslog(LOG_ERR,
+ "Multiple MH or MA Authentication Extensions");
+ return (FA_POORLY_FORMED_REQUEST);
+ } else {
+ foundMHauth = _B_TRUE;
+ }
+ break;
+
+ case REG_GEN_AUTH_EXT_TYPE:
+ /*
+ * The Challenge/Response Internet Draft now
+ * supports a generalized authentication extension, similar
+ * to the MIER specification. When we receive the generalized
+ * authentication extension, we need to check the subtype
+ * field in order to determine the actual extension.
+ */
+ switch (messageHdr->extSubType[i]) {
+ case GEN_AUTH_MN_AAA:
+ /*
+ * The challenge extension MUST be present prior to
+ * the MN-AAA Auth Ext.
+ */
+ if (foundChallenge == _B_FALSE) {
+ syslog(LOG_ERR, "Missing Challenge before " \
+ "Mobile-AAA Authentication Extension");
+ /*
+ * If a challenge was expected,
+ * but not received, we must return a missing
+ * challenge error.
+ */
+ return (FA_MISSING_CHALLENGE);
+ }
+
+ /*
+ * The draft states that the NAI SHOULD be present if
+ * the M-A authentication extension is present, so we
+ * will enforce this.
+ */
+
+ if (foundNAI == _B_FALSE) {
+ syslog(LOG_ERR, "Missing NAI before Mobile-AAA "
+ "Authentication Extension");
+ /*
+ * Mipagent didn't return the
+ * error codes specified in the NAI
+ * specification. If an NAI was expected, and
+ * wasn't present, return a MISSING NAI error.
+ */
+ return (FA_MISSING_NAI);
+ }
+ /*
+ * Fixed buffer overrun. mnNAI is not null
+ * terminated.
+ */
+ (void) fprintf(stderr, "Got an NAI of %.*s!\n",
+ messageHdr->mnNAILen, messageHdr->mnNAI);
+
+ /* we should not have seen any other auth extensions */
+ if (foundMAauth == _B_TRUE) {
+ syslog(LOG_ERR,
+ "Multiple MH or MA Authentication Extensions");
+ return (FA_POORLY_FORMED_REQUEST);
+ } else {
+ foundMAauth = _B_TRUE;
+ }
+ break;
+
+ default:
+ syslog(LOG_ERR,
+ "Unknown Generalized Authentication subtype found");
+ return (FA_POORLY_FORMED_REQUEST);
+ }
+ break;
+
+ case REG_MF_AUTH_EXT_TYPE:
+ /* we should have seen MHauth but no MFauth */
+ if (foundMHauth == _B_TRUE || foundMAauth == _B_TRUE) {
+ if (foundMFauth == _B_TRUE) {
+ syslog(LOG_ERR, "Multiple MF Authentication "
+ "Extensions");
+ return (FA_POORLY_FORMED_REQUEST);
+ } else {
+ foundMFauth = _B_TRUE;
+ }
+ } else {
+ syslog(LOG_ERR,
+ "No MH or MA before MF Authentication Extension");
+ return (FA_POORLY_FORMED_REQUEST);
+ }
+ break;
+
+ case REG_MF_CHALLENGE_EXT_TYPE:
+ /*
+ * We should only see the challenge if we've
+ * advertised it.
+ */
+ if (faChallengeAdv == _B_FALSE) {
+ syslog(LOG_ERR, "Challenge should not be present");
+ return (FA_POORLY_FORMED_REQUEST);
+ }
+ if (foundMFauth == _B_TRUE) {
+ syslog(LOG_ERR, "Challenge should be before "
+ "MF Authentication Extension");
+ return (FA_POORLY_FORMED_REQUEST);
+ }
+ if (foundChallenge == _B_TRUE) {
+ syslog(LOG_ERR, "Multiple Challenges");
+ return (FA_POORLY_FORMED_REQUEST);
+ } else {
+ foundChallenge = _B_TRUE;
+ }
+ break;
+
+ case REG_MN_NAI_EXT_TYPE:
+ /*
+ * The draft states that the NAI SHOULD be present if
+ * the M-A authentication extension is present, so we
+ * will enforce this.
+ */
+ if (foundNAI == _B_TRUE) {
+ syslog(LOG_ERR, "Multiple NAIs");
+ return (FA_POORLY_FORMED_REQUEST);
+ } else {
+ /*
+ * Save the pointer to the NAI for future
+ * reference.
+ */
+ messageHdr->mnNAI = messageHdr->extData[i];
+ messageHdr->mnNAILen = messageHdr->extLength[i];
+
+ if (messageHdr->mnNAILen > MAX_NAI_LENGTH) {
+ /*
+ * Protect against buffer overflows...
+ */
+ syslog(LOG_ERR, "Excessively large NAI");
+ /*
+ * Mipagent didn't return the
+ * error codes specified in the NAI
+ * specification. If an NAI was present, but
+ * larger than expected, return a MISSING NAI
+ * code (the draft doesn't actually state
+ * what should be returned here.
+ */
+ return (FA_MISSING_NAI);
+ }
+
+ /*
+ * Fixed buffer overrun. mnNAI is not null
+ * terminated.
+ */
+ (void) fprintf(stderr, "Received NAI (%*.*s)!\n",
+ messageHdr->mnNAILen, messageHdr->mnNAILen,
+ messageHdr->mnNAI);
+ foundNAI = _B_TRUE;
+ }
+ break;
+
+ /* Vendor-specific extension support */
+ case REG_CRIT_VENDOR_SPEC_EXT_TYPE:
+ /*
+ * We only care if we've seen the MN-HA/AAA auth, but
+ * haven't seen the MN-FA auth (yet).
+ */
+ if (foundMHauth == _B_FALSE && foundMAauth == _B_FALSE) {
+ /*
+ * We haven't seen any mobile node authentication
+ * extensions yet, so we're still inside the MN-HA/AAA
+ * authenticator. These aren't for us, so skip them.
+ */
+ break;
+ }
+
+ /* This extension is implicitly for us as FA... */
+ switch (messageHdr->extSubType[i]) {
+ /*
+ * MN-FA auth is not required to be put here by the
+ * MN. By putting this extension after the MN-HA/AAA
+ * authenticator, the implication is the MN wants us
+ * to look it. Even if we waited for an MN-FA auth
+ * we're not likely to be returning anything different.
+ */
+
+ /*
+ * Vendor-specific extensions we understand go here!
+ */
+ default:
+ /*
+ * RFC 3025 says if we understand this
+ * extension type, but we don't understand
+ * the Vendor/Org-ID or Vendor-CVSE-Type,
+ * we MUST send the registration reply with
+ * a HA_UNKNOWN_CVSE_FROM_MN. Make sure,
+ * though, that this is for us as FA...
+ */
+ if ((foundMHauth == _B_TRUE ||
+ foundMAauth == _B_TRUE) &&
+ foundMFauth != _B_TRUE) {
+ /*
+ * We've seen some form of Mobile-Home
+ * authentication, so it's not for the
+ * HA, and we haven't seen a MFauth, so
+ * it implies this is for us as FA.
+ */
+ syslog(LOG_ERR,
+ "Unrecognized CVSE subtype %d "
+ "from Mobile Node",
+ messageHdr->extSubType[i]);
+ return (FA_UNKNOWN_CVSE_FROM_MN);
+ }
+ break;
+ }
+ break;
+
+ case REG_NORMAL_VENDOR_SPEC_EXT_TYPE:
+ /*
+ * We only care if we've seen the MN-HA/AAA auth, but
+ * haven't seen the MN-FA auth (yet).
+ */
+ if (foundMHauth != _B_TRUE && foundMAauth != _B_TRUE) {
+ /*
+ * We haven't seen a mobile-home authenticator yet,
+ * so we're still inside the MN-HA/AAA authenticator.
+ * This isn't for us, so skip them silently.
+ */
+ break;
+ }
+
+ /* This extension is something the MN wants us to look at */
+ switch (messageHdr->extSubType[i]) {
+ /*
+ * As we understand specific vendor extensions,
+ * they go in here!
+ */
+ default:
+ /*
+ * Non-critical vendor specific extensions are
+ * ignored if we don't understand it. Make
+ * sure we don't log this if it's really for
+ * the HA...
+ */
+ if ((foundMHauth == _B_TRUE ||
+ foundMAauth == _B_TRUE) &&
+ foundMFauth != _B_TRUE) {
+ /*
+ * If we saw an MFauth, it'd be bad to
+ * process this. In that case it may
+ * be for someone, just not us...
+ */
+ syslog(LOG_ERR,
+ "Unrecognized NVSE subtype %d "
+ "from Mobile Node, ignoring!",
+ messageHdr->extSubType[i]);
+ }
+ break;
+ }
+ break;
+
+ /*
+ * If the Encapsulating Delivery Style Extension is present,
+ * the MN MUST be requesting reverse tunneling! It MUST also
+ * appear after the MN_HA_AUTHENTICATION extension, (and when
+ * we support it we MUST consume it so it isn't forward it to
+ * the HA (this last part can't be done here as we're just
+ * parsing a list of extensions)).
+ *
+ * Hopefully *everything* after the mn-ha auth in the regreq
+ * the mn sent us is removed somewhere before we add fa things,
+ * then the fa-ha auth (if applicable).
+ *
+ * At this time it's OK for a MN to put more than one of these
+ * in, so there's no need to set a foundEDS flag to be checked
+ * later.
+ *
+ * At this time, only IPv4inIPv4 tunnels are supported. If
+ * there ever comes a time when we support any other type, we
+ * may have to check the tunnel-type request bits to make sure
+ * we support that in the reverse direction too.
+ */
+ case ENCAPSULATING_DELIVERY_TYPE:
+ /*
+ * We should have either found a mn-ha authenticator first,
+ * or a AAA MN authentication first and NOT a
+ * mn-fa authenticator (since this has to be between the two).
+ * Note: it may be nicer to break this into two cases if we
+ * want to provide a more specific syslog message.
+ */
+ if (((foundMHauth != _B_TRUE) && (foundMAauth != _B_TRUE)) ||
+ (foundMFauth == _B_TRUE)) {
+ syslog(LOG_ERR,
+ "Found ENCAPSULATING_DELIVERY_TYPE"
+ " in the wrong location of registration request -"
+ " either before mn-ha or mn AAA authenticator,"
+ " or after mn-fa authenticator.");
+ return (FA_POORLY_FORMED_REQUEST);
+ }
+
+ /* The 'T' bit MUST be set. */
+ if (!(requestPtr->regFlags & REG_REVERSE_TUNNEL)) {
+ /* extension is here, but 'T' bit isn't set */
+ syslog(LOG_ERR,
+ "Found ENCAPSULATING_DELIVERY_TYPE"
+ " but 'T' bit isn't set.");
+ return (FA_POORLY_FORMED_REQUEST);
+ }
+
+ /*
+ * OK, looks good, but we don't support this yet. This is the
+ * only place we can look for this extension, but until we
+ * support type 130 extensions, we'll have to be non-conformant
+ * for this function, too, and return something other than
+ * FA_POORLY_FORMED_REQUEST.
+ */
+ faCounters.faRTEncapUnavailableCnt++;
+ return (FA_DELIVERY_STYLE_UNAVAILABLE);
+
+ default:
+ if (messageHdr->extType[i] <= 127) {
+ /*
+ * Unrecognized extensions in this range cause this
+ * packet be drooped.
+ */
+ syslog(LOG_ERR,
+ "Unrecognized ext (ext[%d]= %d) in range 0-127.",
+ i, messageHdr->extType[i]);
+ return (MA_DROP_PACKET);
+ }
+ /*
+ * Extensions in the range 128-255 should be
+ * skipped.
+ */
+ break;
+ }
+ }
+
+ /*
+ * Acording to the challenge draft, the rules is as follow:
+ * 1. If the Mobile Node does not have a security association
+ * with the foreign agent, it MUST include the MN-AAA auth
+ * ext. The challenge must appear PRIOR to this extension.
+ * 2. If the Mobile Node has a security association with the
+ * foreign agent, it must include the MN-FA auth ext.
+ *
+ * So in order to enforce this, if the challenge is being
+ * advertised, we will ensure that either the MN-AAA is present
+ * OR the MN-HA AND the MN-FA.
+ */
+ if (faChallengeAdv == _B_TRUE) {
+ if (foundMAauth == _B_FALSE && (foundMHauth == _B_FALSE ||
+ foundMFauth == _B_FALSE)) {
+ syslog(LOG_ERR, "When Challenge is present, either "
+ "the MN-AAA or the MN-HA *and* the MN-FA must "
+ "be present");
+ return (FA_POORLY_FORMED_REQUEST);
+ }
+ }
+
+ /*
+ * The registration request must include either:
+ * 1. Home Address, and/or
+ * 2. NAI.
+ */
+ if (requestPtr->homeAddr == INADDR_ANY && foundNAI == _B_FALSE) {
+ syslog(LOG_ERR,
+ "Mobile Node NAI MUST be present if home address "
+ "is set to zero (0)");
+ /*
+ * Mipagent didn't return the error codes
+ * specified in the NAI specification. If an NAI was expected,
+ * and wasn't present, return a MISSING NAI error.
+ */
+ return (FA_MISSING_NAI);
+ }
+
+ /* make sure we have one MHauth */
+ if (foundMAauth == _B_TRUE || foundMHauth == _B_TRUE) {
+ return (MIP_SUCCESSFUL_REGISTRATION);
+ } else {
+ syslog(LOG_ERR, "MH or MA Authentication Extension missing");
+ return (FA_POORLY_FORMED_REQUEST);
+ }
+}
+
+
+/*
+ * Function: IsPacketFromCoaValid
+ *
+ * Arguments: messageHdr - Message Control Block
+ *
+ * Description: This function is called by the Home Agent when a
+ * registration request is received from a Foreign Agent
+ * and will step through each extension that was stored
+ * in the message header's extType to ensure that the
+ * packet contains the required extensions and that the
+ * message follows the protocol rules.
+ *
+ * The rules are:
+ * 1. If the MN-AAA is present, then the MN-FA, the
+ * NAI and the challenge extensions MUST be present.
+ * 3. If MN-AAA is not present, MN-HA MUST be present.
+ * 4. If the FA-HA is present, it must be preceeded by
+ * either the MN-HA or the MN-AAA.
+ *
+ * Returns: returns a Mobile IP error code, zero if successful.
+ * return -1 if packet needs to be dropped.
+ *
+ */
+static int
+IsPacketFromCoaValid(MessageHdr *messageHdr)
+{
+ regRequest *requestPtr;
+ char addrstr[INET_ADDRSTRLEN];
+ boolean_t foundMHauth = _B_FALSE;
+ boolean_t foundFHauth = _B_FALSE;
+ boolean_t foundMAauth = _B_FALSE;
+ boolean_t foundNAI = _B_FALSE;
+ boolean_t foundChallenge = _B_FALSE;
+ int i;
+
+ /* LINTED BAD_PTR_CAST_ALIGN */
+ requestPtr = (regRequest *) messageHdr->pkt;
+
+ /*
+ * According to the latest Challenge draft, here is the
+ * rule. If the challenge was advertised, then it MUST
+ * be present in ALL packets. If the Mobile-AAA authentication
+ * extension is present, then the NAI MUST be present.
+ *
+ * Normal RFC 2002 rules apply, with the exception that if
+ * the Mobile-Home authentication is NOT present, then the
+ * Mobile-AAA authentication extension MUST be present.
+ */
+ for (i = 0; i < messageHdr->extCnt; i++) {
+ mipverbose(("IsPacketFromCoaValid[%d] = %d\n", i,
+ messageHdr->extType[i]));
+ switch (messageHdr->extType[i]) {
+ case REG_MH_AUTH_EXT_TYPE:
+ /* we should not have seen any other auth extensions */
+ if (foundMHauth == _B_TRUE) {
+ syslog(LOG_ERR,
+ "Multiple MH or MA Authentication Extensions");
+ return (HA_POORLY_FORMED_REQUEST);
+ } else {
+ foundMHauth = _B_TRUE;
+ }
+ break;
+
+ case REG_GEN_AUTH_EXT_TYPE:
+ /*
+ * The Challenge/Response Internet Draft now
+ * supports a generalized authentication extension, similar to
+ * the MIER specification. When we receive the generalized
+ * authentication extension, we need to check the subtype
+ * field in order to determine the actual extension.
+ */
+ switch (messageHdr->extSubType[i]) {
+ case GEN_AUTH_MN_AAA:
+ /*
+ * The challenge extension MUST be present prior to
+ * the MN-AAA Auth Ext.
+ */
+ if (foundChallenge == _B_FALSE) {
+ syslog(LOG_ERR, "Missing Challenge before " \
+ "Mobile-AAA Authentication Extension");
+ return (HA_POORLY_FORMED_REQUEST);
+ }
+
+ /*
+ * The draft states that the NAI SHOULD be present if
+ * the M-A authentication extension is present, so we
+ * will enforce this.
+ */
+
+ if (foundNAI == _B_FALSE) {
+ syslog(LOG_ERR, "Missing NAI before Mobile-AAA "
+ "Authentication Extension");
+ /*
+ * Mipagent didn't return the
+ * error codes specified in the NAI
+ * specification. If an NAI was expected, and
+ * wasn't present, return a MISSING NAI error.
+ */
+ return (FA_MISSING_NAI);
+ }
+ /*
+ * Fixed buffer overrun. mnNAI is not null
+ * terminated.
+ */
+ (void) fprintf(stderr, "Got an NAI of (%*.*s)!\n",
+ messageHdr->mnNAILen, messageHdr->mnNAILen,
+ messageHdr->mnNAI);
+
+ /* we should not have seen any other auth extensions */
+ if (foundMAauth == _B_TRUE) {
+ syslog(LOG_ERR,
+ "Multiple MH or MA Authentication Extensions");
+ return (HA_POORLY_FORMED_REQUEST);
+ } else {
+ foundMAauth = _B_TRUE;
+ }
+ break;
+
+ default:
+ syslog(LOG_ERR,
+ "Unknown Generalized Authentication subtype found");
+ return (HA_POORLY_FORMED_REQUEST);
+ }
+ break;
+
+
+ case REG_MF_CHALLENGE_EXT_TYPE:
+ if (foundChallenge == _B_TRUE) {
+ syslog(LOG_ERR, "Multiple Challenges");
+ return (HA_POORLY_FORMED_REQUEST);
+ } else {
+ foundChallenge = _B_TRUE;
+ }
+ break;
+
+ case REG_FH_AUTH_EXT_TYPE:
+ /*
+ * We should at least make sure that we've seen
+ * the Mobile-Home Authentication Extension before
+ * we see this one.
+ */
+ if (foundMAauth == _B_FALSE && foundMHauth == _B_FALSE) {
+ syslog(LOG_ERR,
+ "Missing MH or MA Authentication Extension "
+ "before FHauth");
+ return (HA_POORLY_FORMED_REQUEST);
+ } else {
+ /*
+ * we should not have seen any other auth
+ * extensions
+ */
+ if (foundFHauth == _B_TRUE) {
+ syslog(LOG_ERR, "Multiple FHauth");
+ return (HA_POORLY_FORMED_REQUEST);
+ } else {
+ foundFHauth = _B_TRUE;
+ }
+ }
+ break;
+
+ case REG_MN_NAI_EXT_TYPE:
+ /*
+ * The draft states that the NAI SHOULD be present if
+ * the M-A authentication extension is present, so we
+ * will enforce this.
+ */
+ if (foundNAI == _B_TRUE) {
+ syslog(LOG_ERR, "Multiple NAIs");
+ return (HA_POORLY_FORMED_REQUEST);
+ } else {
+ /*
+ * Save the pointer to the NAI for future
+ * reference.
+ */
+ messageHdr->mnNAI = messageHdr->extData[i];
+ messageHdr->mnNAILen = messageHdr->extLength[i];
+
+ if (messageHdr->mnNAILen > MAX_NAI_LENGTH) {
+ /*
+ * Protect against buffer overflows...
+ */
+ syslog(LOG_ERR, "Excessively large NAI");
+ return (HA_POORLY_FORMED_REQUEST);
+ }
+ foundNAI = _B_TRUE;
+ }
+ break;
+
+ /* Vendor-specific extension support */
+ case REG_CRIT_VENDOR_SPEC_EXT_TYPE:
+ /* Examine the subtype, and process accordingly */
+ switch (messageHdr->extSubType[i]) {
+ /*
+ * As we understand specific vendor extensions,
+ * they go in here!
+ */
+
+ default:
+ /*
+ * RFC 3025 says if we understand this type,
+ * but we don't understand the Vendor/Org-ID
+ * or Vendor-CVSE-Type, we MUST send a
+ * registration reply with either
+ * HA_UNKNOWN_CVSE_FROM_MN if it came from
+ * the mobile node, or HA_UNKNOWN_CVSE_FROM_FA
+ * if it came from the foreign agent.
+ *
+ * What we need to worry about now:
+ *
+ * Did this extension come from the MN, or was
+ * it added by an FA? Note (caveat!): if the
+ * 'D'-bit is set, if an FA was advertising
+ * the 'R'-bit, the MN sent the registration
+ * request to it, so it may have added these
+ * extensions in flight!
+ */
+ if (foundMHauth != _B_TRUE &&
+ foundMAauth != _B_TRUE) {
+ /*
+ * We haven't seen a MN-HA/AAA
+ * authenticator, so this is in the
+ * MN-to-HA portion of the request
+ */
+ syslog(LOG_ERR,
+ "Unrecognized CVSE subtype %d from"
+ " Mobile Node",
+ messageHdr->extSubType[i]);
+ return (HA_UNKNOWN_CVSE_FROM_MN);
+ } else if (foundFHauth != _B_TRUE) {
+ /*
+ * We've seen some form of MN-home
+ * authenticator, so what we're
+ * looking at now appears after it,
+ * and must be from an FA (FA iGs
+ * supposed to strip out anything
+ * after MN-HA/AAA auth from the MN,
+ * so this must have been FA-appended.
+ * That 'if' was a sanity check that we
+ * have NOT seen a FA-HA authenticator,
+ * which would imply this was NOT
+ * added by the FA.
+ */
+ syslog(LOG_ERR,
+ "Unrecognized CVSE subtype %d from"
+ " Foreign Agent",
+ messageHdr->extSubType[i]);
+ return (HA_UNKNOWN_CVSE_FROM_FA);
+ }
+ break;
+ }
+ break;
+
+ case REG_NORMAL_VENDOR_SPEC_EXT_TYPE:
+ /* Examine the subtype, and process accordingly */
+ switch (messageHdr->extSubType[i]) {
+ /*
+ * As we understand specific vendor extensions,
+ * they go in here!
+ */
+
+ default:
+ /*
+ * For non-critical vendor specific extensions,
+ * we ignore the entire extension if we don't
+ * understand the sub-type or vendor-ID.
+ *
+ * We need to know if it came from MN or FA...
+ */
+ if (foundMHauth != _B_TRUE &&
+ foundMAauth != _B_TRUE) {
+ /*
+ * We haven't seen an MN-HA/AAA
+ * authenticator, so it's before it,
+ * and is from the mobile node.
+ */
+ syslog(LOG_ERR,
+ "Unrecognized NVSE subtype %d from"
+ " Mobile Node, ignoring!",
+ messageHdr->extSubType[i]);
+ } else if (foundFHauth != _B_TRUE) {
+ /*
+ * It's after MN-home authenticationon,
+ * but before FA-home authentication,
+ * so the implication is this was put
+ * here by the FA.
+ */
+ syslog(LOG_ERR,
+ "Unrecognized NVSE subtype %d from"
+ " Foreign Agent, ignoring!",
+ messageHdr->extSubType[i]);
+ }
+ break;
+ }
+ break;
+
+ case ENCAPSULATING_DELIVERY_TYPE:
+ /*
+ * If the MN included the Encapsulating Delivery Style
+ * Extension, the FA MUST consume it. If it didn't, we'll be
+ * nice and just log an error (there is no "official" error
+ * code which can help a MN figure this out anyway).
+ *
+ * We could make sure we've seen the MN-HA auth. If not, it's
+ * a poorly formed request (and perhaps why the FA didn't
+ * remove this extension, though it should have denied it with
+ * poorly formed request). Since this is an FA only thing,
+ * and clearly isn't a security problem, I don't think we
+ * should care. Denying is likely to lead to no service, so
+ * are we trying to service this guy, or look for reasons to
+ * deny service?
+ *
+ * The icing here is since it's type 130 it's in the ignore
+ * range. We shouldn't change the way the HA reacts to a FA
+ * only RT extension because it now supports RT.
+ */
+ syslog(LOG_WARNING,
+ "Home Agent found ENCAPSULATING_DELIVERY_TYPE extension."
+ " FA (%s) should have removed it."
+ " Ignoring (as type>127), and processing registration.",
+ ntoa(requestPtr->COAddr, addrstr));
+
+ break;
+
+ default:
+ if (messageHdr->extType[i] <= 127) {
+ /*
+ * Unrecognized extensions in this range cause this
+ * packet be drooped.
+ */
+ syslog(LOG_ERR,
+ "Unrecognized ext (ext[%d]= %d) in range 0-127.",
+ i, messageHdr->extType[i]);
+ return (MA_DROP_PACKET);
+ }
+ /*
+ * Extensions in the range 128-255 should be
+ * skipped.
+ */
+ break;
+ }
+ }
+
+ /*
+ * The registration request must include either:
+ * 1. Home Address, and/or
+ * 2. NAI.
+ */
+ if (requestPtr->homeAddr == INADDR_ANY && foundNAI == _B_FALSE) {
+ syslog(LOG_ERR,
+ "Mobile Node NAI MUST be present if home address "
+ "is set to zero (0)");
+ return (HA_POORLY_FORMED_REQUEST);
+ }
+
+ /* make sure we have one MHauth */
+ if (foundMAauth == _B_TRUE || foundMHauth == _B_TRUE) {
+ return (MIP_SUCCESSFUL_REGISTRATION);
+ } else {
+ syslog(LOG_ERR, "MH or MA Authentication Extension missing");
+ return (HA_POORLY_FORMED_REQUEST);
+ }
+}
+
+
+/*
+ * Function: IsPacketFromHaValid
+ *
+ * Arguments: messageHdr - Message Control Block
+ *
+ * Description: This function is called by the Foreign Agent when a
+ * registration reply is received from a Home Agent
+ * and will step through each extension that was stored
+ * in the message header's extType to ensure that the
+ * packet contains the required extensions and that the
+ * message follows the protocol rules.
+ *
+ * The rules are:
+ * 1. The MN-HA MUST be present.
+ * 2. If the FA-HA is present and the challenge
+ * is being advertised, the challenge MUST be present.
+ * 3. If the FA-HA is present, it MUST appear after the
+ * the MN-HA extension.
+ *
+ * Returns: returns a Mobile IP error code, zero if successful.
+ * return -1 if packet needs to be dropped.
+ *
+ */
+static int
+IsPacketFromHaValid(MessageHdr *messageHdr)
+{
+ regReply *replyPtr;
+ boolean_t foundMHauth = _B_FALSE;
+ boolean_t foundFHauth = _B_FALSE;
+ boolean_t foundNAI = _B_FALSE;
+ boolean_t foundChallenge = _B_FALSE;
+ int i;
+
+ /* LINTED BAD_PTR_CAST_ALIGN */
+ replyPtr = (regReply *) messageHdr->pkt;
+
+ /*
+ * The NAI MAY be necessary, but right now we have no
+ * way of knowing. This check will have to be handled
+ * later.
+ */
+ for (i = 0; i < messageHdr->extCnt; i++) {
+ mipverbose(("IsPacketFromHaValid[%d] = %d\n", i,
+ messageHdr->extType[i]));
+ switch (messageHdr->extType[i]) {
+ case REG_MH_AUTH_EXT_TYPE:
+ /* we should not have seen any other auth extensions */
+ if (foundMHauth == _B_TRUE) {
+ syslog(LOG_ERR,
+ "Multiple MH or MA Authentication Extensions");
+ return (FA_POORLY_FORMED_REPLY);
+ } else {
+ foundMHauth = _B_TRUE;
+ }
+ break;
+
+ case REG_FH_AUTH_EXT_TYPE:
+ /* If we are expecting a challenge, check for it. */
+ if (faChallengeAdv == _B_TRUE && foundChallenge == _B_FALSE) {
+ syslog(LOG_ERR, "Challenge Missing in Reply");
+ /*
+ * If a challenge was expected, but
+ * not received, we must return a missing challenge
+ * error.
+ */
+ return (FA_MISSING_CHALLENGE);
+ }
+
+ /* we should have seen MHauth but no FHauth */
+ if (foundMHauth == _B_FALSE) {
+ syslog(LOG_ERR,
+ "No MH or MA Authentication Extension before FHauth");
+ return (FA_POORLY_FORMED_REPLY);
+ } else if (foundFHauth == _B_TRUE) {
+ syslog(LOG_ERR, "Multiple FHauth");
+ return (FA_POORLY_FORMED_REPLY);
+ } else {
+ foundFHauth = _B_TRUE;
+ }
+ break;
+
+ case REG_MF_CHALLENGE_EXT_TYPE:
+ if (foundChallenge == _B_TRUE) {
+ syslog(LOG_ERR, "Multiple Challenges");
+ return (FA_POORLY_FORMED_REPLY);
+ } else {
+ foundChallenge = _B_TRUE;
+ }
+ break;
+
+ case REG_MN_NAI_EXT_TYPE:
+ /*
+ * The draft states that the NAI SHOULD be present if
+ * the M-A authentication extension is present, so we
+ * will enforce this.
+ */
+ if (foundNAI == _B_TRUE) {
+ syslog(LOG_ERR, "Multiple NAIs");
+ return (FA_POORLY_FORMED_REPLY);
+ } else {
+ /*
+ * Save the pointer to the NAI for future
+ * reference.
+ */
+ messageHdr->mnNAI = messageHdr->extData[i];
+ messageHdr->mnNAILen = messageHdr->extLength[i];
+
+ if (messageHdr->mnNAILen > MAX_NAI_LENGTH) {
+ /*
+ * Protect against buffer overflows...
+ */
+ syslog(LOG_ERR, "Excessively large NAI");
+ return (FA_POORLY_FORMED_REPLY);
+ }
+ foundNAI = _B_TRUE;
+ }
+ break;
+
+ case REG_GEN_MN_FA_KEY_EXT_TYPE:
+ /*
+ * The AAA Keys Internet Draft now supports
+ * a generalized key extension, similar to the MIER
+ * specification. When we receive the generalized key
+ * extension, we need to check the subtype field in order to
+ * determine the actual extension.
+ */
+ switch (messageHdr->extSubType[i]) {
+ case GEN_KEY_MN_FA:
+ /*
+ * We just need to recognize the extension type,
+ * otherwise it will cause an error.
+ */
+ break;
+
+ default:
+ syslog(LOG_ERR,
+ "Unrecognized generalized key subtype %d",
+ messageHdr->extSubType[i]);
+ return (FA_POORLY_FORMED_REPLY);
+ }
+ break;
+
+ case REG_GEN_MN_HA_KEY_EXT_TYPE:
+ /*
+ * The AAA Keys Internet Draft now supports
+ * a generalized key extension, similar to the MIER
+ * specification. When we receive the generalized key
+ * extension, we need to check the subtype field in order to
+ * determine the actual extension.
+ */
+ switch (messageHdr->extSubType[i]) {
+ case GEN_KEY_MN_HA:
+ /*
+ * We just need to recognize the extension type,
+ * otherwise it will cause an error.
+ */
+ break;
+
+ default:
+ syslog(LOG_ERR,
+ "Unrecognized generalized key subtype %d",
+ messageHdr->extSubType[i]);
+ return (FA_POORLY_FORMED_REPLY);
+ }
+ break;
+
+ case REG_CRIT_VENDOR_SPEC_EXT_TYPE:
+ /* Check the subtype and process accordingly */
+ switch (messageHdr->extSubType[i]) {
+#ifdef KEY_DISTRIBUTION
+ /*
+ * KEY_DISTRIBUTION MUST ONLY BE COMPILED FOR TESTING!!!
+ *
+ * This version of mipagent supports a AAA/DIAMETER
+ * interface. The DIAMETER server generates keying
+ * material that is sent to the Home Agent. The keys
+ * sent are both for the Home Agent, and for the Mobile
+ * Node. The keys for the Mobile Nodes are added to the
+ * registration reply, and the keys for the Home Agent
+ * cause the Home Agent to create a local SA.
+ *
+ * Since DIAMETER/AAA is not currently a product, and key
+ * distribution must still be tested, we have added some
+ * test code in mipagent. When KEY_DISTRIBUTION is enabled,
+ * the home agent creates and encrypts session keys for
+ * the Mobile Node (mimicking DIAMETER), and creates local
+ * SAs. Further, since the session keys MUST also be sent
+ * to the Foreign Agent, the session keys are sent in the
+ * clear to the Foreign Agent through Vendor Specific
+ * extensions.
+ *
+ * Again, this code is for testing purpose only and must not
+ * be enabled for production code, since it hasn't been
+ * fully tested.
+ */
+ case REG_MN_FA_KEY_EXT:
+ case REG_FA_HA_KEY_EXT:
+ /*
+ * We just need to recognize the extension type,
+ * otherwise it will cause an error.
+ */
+ break;
+#endif /* KEY_DISTRIBUTION */
+
+ /*
+ * Insert understood REG_CRIT_VENDOR_SPEC_EXT_TYPEs here!
+ */
+
+ default:
+ /*
+ * RFC 3025 says if we don't understand
+ * this subtype we MUST return
+ * FA_UNKNOWN_CVSE_FROM_HA in the
+ * registration reply (to the mobile node,
+ * who'll understand it MUST rereg, and
+ * hopefully do something to change the HA's
+ * mind about replying with that CVSE)!
+ * First, though, make sure it's for us!
+ */
+ if (foundMHauth == _B_TRUE && foundFHauth == _B_FALSE) {
+ /*
+ * we've seen the MHAUTH, so this is
+ * after it, but not an FHauth, so it's
+ * for us from the HA.
+ */
+ syslog(LOG_ERR,
+ "Unrecognized CVSE subtype %d"
+ " from Home Agent.",
+ messageHdr->extSubType[i]);
+ return (FA_UNKNOWN_CVSE_FROM_HA);
+ }
+ }
+ break;
+
+ case REG_NORMAL_VENDOR_SPEC_EXT_TYPE:
+ /* Check subtype, and process accordingly */
+ switch (messageHdr->extSubType[i]) {
+ /*
+ * Understood REG_NORMAL_VENDOR_SPEC_EXT_TYPEs go here!
+ */
+
+ default:
+ /*
+ * RFC3025 says silently ignore, but we should
+ * at least log it if it's for us.
+ */
+ if (foundMHauth == _B_TRUE &&
+ foundFHauth == _B_FALSE) {
+ /*
+ * We've seen the MHauth, so it's not
+ * for the MN, and we haven't seen an
+ * FHauth, so the implication is this
+ * is for us (if we had seen a FAauth,
+ * we'd KNOW it's NOT for us).
+ * Note: there's no check for an
+ * MN-AAAauth in this routine here!
+ */
+ syslog(LOG_ERR,
+ "Unrecognized NVSE subtype %d"
+ " from Home Agent, ignoring.",
+ messageHdr->extSubType[i]);
+ }
+ }
+ break;
+
+ default:
+ if (messageHdr->extType[i] <= 127) {
+ /*
+ * Unrecognized extensions in this range cause this
+ * packet be drooped.
+ */
+ syslog(LOG_ERR,
+ "Unrecognized ext (ext[%d]= %d) in range 0-127.",
+ i, messageHdr->extType[i]);
+ return (MA_DROP_PACKET);
+ }
+ /*
+ * Extensions in the range 128-255 should be
+ * skipped.
+ */
+ break;
+ }
+ }
+
+ /*
+ * The Home Address MUST be provided.
+ */
+ if (replyPtr->homeAddr == INADDR_ANY) {
+ syslog(LOG_ERR, "Mobile Node Home Address MUST be provided");
+ /*
+ * Mipagent didn't return the error
+ * codes specified in the NAI specification. If
+ * the reply didn't include a Home Address, we must return
+ * a MISSING HOME ADDRESS error code.
+ */
+ return (FA_MISSING_HOMEADDR);
+ }
+
+
+ if (foundMHauth == _B_TRUE) { /* make sure we have one MHauth */
+ return (MIP_SUCCESSFUL_REGISTRATION);
+ } else {
+ syslog(LOG_ERR, "MH Authentication Extension missing");
+ return (FA_POORLY_FORMED_REPLY);
+ }
+}
+
+
+#ifdef RADIUS_ENABLED
+HaMobileNodeEntry *
+radiusCheckUpdate(HaMobileNodeEntry *dest, struct hash_table *htbl,
+ ipaddr_t mnAddr)
+{
+ struct hash_entry *p;
+ MipSecAssocEntry *saEntry;
+ RadData result;
+ char ipString[30];
+ int rc;
+ char *sessionId;
+ struct hash_entry *hash;
+ char mipSecKey[MAX_KEY_LEN];
+ int i;
+ time_t currentTime;
+
+ (void) ntoa(mnAddr, ipString);
+ (void) memset(&result, 0, sizeof (result));
+
+ GET_TIME(currentTime);
+
+ /* ToDo: Lock our node */
+
+ if (dest == NULL) {
+#ifdef RADIUS_DEBUG
+ (void) fprintf(stderr,
+ "radiusCheckUpdate: Looking up 0x%08x\n", mnAddr);
+#endif
+ rc = radLookupData(&sessionId, ipString, &result);
+#ifdef RADIUS_DEBUG
+ (void) fprintf(stderr,
+ "radiusCheckUpdate: Finished Looking up 0x%08x rc=%d\n",
+ mnAddr, rc);
+#endif
+ if (rc) {
+ /*
+ * Since we have nothing, we need to retun null . . .
+ */
+ return (NULL);
+ }
+
+ if (hexConvert((char *)mipSecKey,
+ result.mipSecretLen/2, result.mipSecret) < 0) {
+ return (NULL);
+ }
+
+ /*
+ * Now we create the Security Assocation
+ * TODO: This DOES NOT belong here. We want
+ * to take care of this when the SPI is
+ * defined.
+ */
+ saEntry = CreateSecAssocEntry(_B_TRUE, result.mipSPI,
+ result.mipSecretReplayMethod, MD5,
+ PREFIXSUFFIX, result.mipSecretLen/2, mipSecKey, 0);
+
+ if (saEntry == NULL) {
+ syslog(LOG_ERR,
+ "Unable to create MobileNode SA for %d",
+ result.mipMnAddr);
+ return (NULL);
+ }
+
+ /*
+ * And we create the Mobile Node
+ */
+ dest = CreateMobileNodeEntry(_B_TRUE,
+ result.mipMnAddr, NULL, 0, result.mipBindingIfAddress,
+ result.mipSPI, sessionId, 0);
+
+ if (dest == NULL) {
+ syslog(LOG_ERR,
+ "Unable to create MobileNodeEntry for %d",
+ result.mipMnAddr));
+ return (NULL);
+ }
+ } else {
+ /*
+ * We have a node already, check to see if it needs an
+ * update
+ */
+ if (dest->haRadiusLastLookupTime + RADIUS_LOOKUP_TIME <
+ currentTime) {
+ /* Nope, return! */
+ return (dest);
+ }
+#ifdef RADIUS_DEBUG
+ (void) fprintf(stderr, "radiusCheckUpdate: Looking up 0x%08x\n",
+ mnAddr);
+#endif
+ rc = radLookupData(&sessionId, ipString, &result);
+#ifdef RADIUS_DEBUG
+ (void) fprintf(stderr,
+ "radiusCheckUpdate: Finished Looking up 0x%08x rc=%d\n",
+ mnAddr, rc);
+#endif
+ if (rc) {
+ /*
+ * This is either an error, or a timeout . . .either
+ * way, return what we already have.
+ */
+ return (dest);
+ }
+
+ /*
+ * TODO: We need to update the timestamp.
+ */
+ dest->haRadiusLastLookupTime = currentTime;
+
+ /*
+ * TODO: This is broken. if the SPI is the same as what
+ * we already had, then update it, otherwise create
+ * a new Security Association Entry.
+ */
+ }
+
+ /* Now, update the fields -- everything went well. */
+#ifdef RADIUS_DEBUG
+ (void) fprintf(stderr, "radiusCheckUpdate: Updating 0x%p\n", dest);
+#endif
+
+
+ dest->haRadiusState = sessionId;
+ dest->haRadiusLastLookupTime = currentTime;
+
+#ifdef RADIUS_DEBUG
+ (void) fprintf(stderr, "haMnAddr = 0x%08x\n",
+ dest->haMnAddr);
+ (void) fprintf(stderr, "haBindingIfaceAddr = 0x%08x\n",
+ dest->haBindingIfaceAddr);
+ (void) fprintf(stderr, "haMnBindingCnt = %d\n",
+ dest->haMnBindingCnt);
+ (void) fprintf(stderr, "\n");
+ (void) fprintf(stderr,
+ "haRadiusState = 0x%08x\n", dest->haRadiusState);
+ (void) fprintf(stderr,
+ "haRadiusLastLookupTime = 0x%08x\n", dest->haRadiusLastLookupTime);
+
+ (void) fprintf(stderr, "radiusCheckUpdate: exiting\n");
+#endif
+
+ return (dest);
+} /* radiusCheckUpdate */
+#endif /* RADIUS_ENABLED */
+
+#ifdef FIREWALL_SUPPORT
+/*
+ * Find a binding entry for the specified mnAddr and coAddr pair in
+ * Hash Table. If a match is found findHABE returns _B_TRUE else _B_FALSE.
+ */
+int
+findHABE(HashTable *htbl, ipaddr_t mnAddr, ipaddr_t COAddr)
+{
+ boolean_t found = _B_FALSE;
+ HaMobileNodeEntry *hamnePtr;
+ HaBindingEntry *entry;
+ char addrstr1[INET_ADDRSTRLEN];
+
+ if ((hamnePtr = (HaMobileNodeEntry *)findHashTableEntryUint(htbl,
+ mnAddr, LOCK_READ, NULL, 0, 0, 0)) != NULL) {
+
+ entry = hamnePtr->bindingEntries;
+
+ while (entry) {
+ if ((entry->haBindingMN == mnAddr) &&
+ (entry->haBindingCOA == COAddr)) {
+ found = _B_TRUE;
+ break;
+ }
+ entry = entry->next;
+ }
+
+ (void) rw_unlock(&hamnePtr->nodeLock);
+ } else {
+ syslog(LOG_ERR, "Unable to find Mobile Node Entry %s",
+ ntoa(mnAddr, addrstr1));
+ }
+
+ return (found);
+}
+#endif /* FIREWALL_SUPPORT */
+
+/*
+ * Function: findPendingFAVEHashLookup
+ *
+ * Arguments: entry - Pointer to visitor entry
+ * p1 - First parameter to match (low 32-bit ID)
+ * p2 - 2nd parameter to match (whether visitor is accepted)
+ * p3 - 3rd parameter to match (unused)
+ *
+ * Description: This function is used as the Hash Table Helper routine
+ * for findPendingFAVE() when looking for pending visitor
+ * entries in the Hash Table, and will be called by
+ * findHashTableEntryUint() and findHashTableEntryString().
+ *
+ * Returns: _B_TRUE if the entry matches the desired criteria,
+ * otherwise _B_FALSE.
+ */
+/* ARGSUSED */
+static boolean_t
+findPendingFAVEHashLookup(void *entry, uint32_t p1, uint32_t p2, uint32_t p3)
+{
+ FaVisitorEntry *faveEntry = entry;
+
+ if ((faveEntry->faVisitorRegIDLow == p1) &&
+ ((uint32_t)faveEntry->faVisitorRegIsAccepted == p2)) {
+ return (_B_TRUE);
+ }
+
+ return (_B_FALSE);
+}
+
+/*
+ * Function: findPendingFAVE
+ *
+ * Arguments: htbl - Pointer to Hash Table
+ * mnAddr - Mobile Node's Home Address.
+ * mnNAI - Mobile Node's NAI
+ * IDlo - Low order 32 bit identifier used by Mobile Node
+ *
+ * Description: Find a visitor entry with faVisitorHomeAddr equal to mnAddr,
+ * faVisitorRegIDLow equal to IDlo and faVisitorRegIsAccepted
+ * equal to _B_FALSE.
+ *
+ * Note: The entry returned will be write locked.
+ *
+ * Returns: Pointer to FaVisitorEntry. NULL if function failed.
+ *
+ */
+static FaVisitorEntry *
+findPendingFAVE(HashTable *htbl, ipaddr_t mnAddr, unsigned char *mnNAI,
+ uint32_t mnNAILen, uint32_t IDlo)
+{
+ FaVisitorEntry *entry = NULL;
+
+ if (mnAddr) {
+ /*
+ * Let's see if we can find the entry using the Home
+ * Address.
+ */
+ entry = findHashTableEntryUint(htbl, mnAddr, LOCK_WRITE,
+ findPendingFAVEHashLookup, IDlo, _B_FALSE, 0);
+ }
+
+ if (entry == NULL && mnNAI) {
+ /*
+ * Perhaps we will be able to find the visitor entry
+ * using the NAI.
+ */
+ entry = findHashTableEntryString(htbl, mnNAI, mnNAILen,
+ LOCK_WRITE, findPendingFAVEHashLookup, IDlo, _B_FALSE, 0);
+ }
+
+ return (entry);
+}
+
+
+/*
+ * Function: mkPendingFAVEHashLookup
+ *
+ * Arguments: entry - Pointer to visitor entry
+ * p1 - First parameter to match (whether visitor is accepted)
+ * p2 - 2nd parameter to match (inIfindex)
+ * p3 - 3rd parameter to match (unused)
+ *
+ * Description: This function is used as the Hash Table Helper routine
+ * for mkPendingFAVE() when looking for pending visitor
+ * entries in the Hash Table, and will be called by
+ * findHashTableEntryUint() and findHashTableEntryString().
+ *
+ * Returns: _B_TRUE if the entry matches the desired criteria,
+ * otherwise _B_FALSE.
+ */
+/* ARGSUSED */
+boolean_t
+mkPendingFAVEHashLookup(void *entry, uint32_t p1, uint32_t p2, uint32_t p3)
+{
+ FaVisitorEntry *faveEntry = entry;
+
+ if (((uint32_t)faveEntry->faVisitorRegIsAccepted == p1) &&
+ (p2 == 0 || (uint32_t)faveEntry->faVisitorInIfindex == p2)) {
+ return (_B_TRUE);
+ }
+
+ return (_B_FALSE);
+}
+
+
+/*
+ * Function: mkPendingFAVE
+ *
+ * Arguments: htbl - Pointer to Hash Table
+ * messageHdr - Message Control Block
+ * localAddress - Local Interface Address
+ * SPI - Mobile Node's SPI
+ * challenge - The Challenge value found in the Reg. Request
+ * challengeLen - The size of the challenge.
+ *
+ * Description: Create a pending visitor entry in hash table based on
+ * a registration request pointed to in the message header.
+ * The visitor is reachable through the interface iunformation
+ * found in the message header, such as the localAddr and the
+ * request was sent from visitor's port and source address.
+ *
+ * Note that the visitor entry, upon return, will be
+ * write locked.
+ *
+ * Returns: if successful, the function will return a pointer to
+ * a visitor entry, otherwise NULL.
+ *
+ */
+static FaVisitorEntry *
+mkPendingFAVE(HashTable *htbl, MessageHdr *messageHdr, ipaddr_t localAddr,
+ uint32_t SPI, unsigned char *challenge, size_t challengeLen)
+{
+ boolean_t found;
+ regRequest *requestPtr;
+ FaVisitorEntry *entry = NULL;
+ char addrstr1[INET_ADDRSTRLEN];
+ time_t currentTime;
+
+ /* LINTED BAD_PTR_CAST_ALIGN */
+ requestPtr = (regRequest *)messageHdr->pkt;
+
+ found = _B_FALSE;
+
+ /*
+ * First, let's check if we already have a visitor entry for this
+ * mobile node. We must match incoming interface, because we may
+ * have an entry for a MN with overlapping private address
+ */
+ if (requestPtr->homeAddr) {
+ entry = findHashTableEntryUint(htbl, requestPtr->homeAddr,
+ LOCK_WRITE, mkPendingFAVEHashLookup, _B_FALSE,
+ messageHdr->inIfindex, 0);
+ }
+
+ if (entry == NULL && messageHdr->mnNAI) {
+ entry = findHashTableEntryString(htbl, messageHdr->mnNAI,
+ messageHdr->mnNAILen, LOCK_WRITE, mkPendingFAVEHashLookup,
+ _B_FALSE, messageHdr->inIfindex, 0);
+ }
+
+ if (entry == NULL) {
+ /*
+ * Since we did not find a hash entry, we need to
+ * re-initialize p to NULL.
+ */
+ if ((entry =
+ (FaVisitorEntry *)calloc(1, sizeof (FaVisitorEntry)))
+ == NULL) {
+ syslog(LOG_CRIT,
+ "Unable to allocate FaVisitorEntry");
+ return (NULL);
+ }
+
+ if (rwlock_init(&entry->faVisitorNodeLock, USYNC_THREAD,
+ NULL)) {
+ syslog(LOG_ERR, "Unable to init visitor lock");
+ }
+ } else {
+ found = _B_TRUE;
+ }
+
+ entry->faVisitorAddr = messageHdr->src;
+ entry->faVisitorIfaceAddr = localAddr;
+ entry->faVisitorRegIsAccepted = _B_FALSE;
+ entry->faVisitorRegFlags = requestPtr->regFlags;
+ entry->faVisitorPort = messageHdr->srcPort;
+ entry->faVisitorHomeAddr = requestPtr->homeAddr;
+ entry->faVisitorHomeAgentAddr = requestPtr->haAddr;
+ entry->faVisitorCOAddr = requestPtr->COAddr;
+ GET_TIME(currentTime)
+ entry->faVisitorTimeExpires = currentTime +
+ DEFAULT_VISITOR_EXPIRY;
+ entry->faVisitorRegIDHigh = ntohl(requestPtr->IDHigh);
+ entry->faVisitorRegIDLow = ntohl(requestPtr->IDLow);
+ entry->faVisitorSPI = SPI;
+ entry->faVisitorInIfindex = messageHdr->inIfindex;
+ entry->faVisitorIsSllaValid = messageHdr->isSllaValid;
+ if (messageHdr->isSllaValid)
+ (void) memcpy(&entry->faVisitorSlla, &messageHdr->slla,
+ sizeof (struct sockaddr_dl));
+ else
+ (void) memset(&entry->faVisitorSlla, 0,
+ sizeof (struct sockaddr_dl));
+
+ /*
+ * Save the Mobile Node's NAI
+ */
+ entry->faVisitorMnNAILen = messageHdr->mnNAILen;
+ if (messageHdr->mnNAI) {
+ (void) strncpy((char *)entry->faVisitorMnNAI,
+ (char *)messageHdr->mnNAI, messageHdr->mnNAILen);
+ } else {
+ entry->faVisitorMnNAI[0] = 0;
+ }
+
+ /*
+ * If necessary, save the challenge
+ */
+ if (challengeLen) {
+ (void) memcpy(entry->faVisitorChallengeToHA, challenge,
+ challengeLen);
+ }
+ entry->faVisitorChallengeToHALen = challengeLen;
+
+ if (found == _B_FALSE) {
+ if (requestPtr->homeAddr) {
+ /*
+ * We will link the entry in the hash table using
+ * the home address.
+ */
+ if (linkHashTableEntryUint(htbl, requestPtr->homeAddr,
+ entry, LOCK_WRITE)) {
+ syslog(LOG_ERR,
+ "Unable to add visitor entry to hash tabl");
+ (void) rwlock_destroy(
+ &entry->faVisitorNodeLock);
+ free(entry);
+ return (NULL);
+ }
+ mipverbose(("Adding pending visitor entry for %s.%d " \
+ "at pos'n %p.\n",
+ ntoa(requestPtr->homeAddr, addrstr1),
+ messageHdr->srcPort, (void *)entry));
+ } else if (messageHdr->mnNAI) {
+ /*
+ * We will link the entry in the has htable using
+ * the NAI. Note that this is a temporary measure
+ * and we will update the hash table to make use
+ * of the home address when the reply (which includes
+ * the home address) is received from the Home Agent.
+ */
+ if (linkHashTableEntryString(htbl, messageHdr->mnNAI,
+ messageHdr->mnNAILen, entry, LOCK_WRITE)) {
+ syslog(LOG_ERR,
+ "Unable to add visitor entry to "
+ "hash table (NAI)");
+ (void) rwlock_destroy(
+ &entry->faVisitorNodeLock);
+ free(entry);
+ return (NULL);
+ }
+ mipverbose(("Adding pending visitor entry for %.*s (%d)"
+ "at pos'n %p.\n", messageHdr->mnNAILen,
+ messageHdr->mnNAI, messageHdr->mnNAILen,
+ (void *)entry));
+ } else {
+ /*
+ * Well, we need at LEAST a home address or an NAI.
+ */
+ syslog(LOG_ERR, "Unable to add visitor entry "
+ "no Home Address or NAI");
+ return (NULL);
+ }
+
+ }
+
+ return (entry);
+}
+
+/*
+ * Function: isAcceptedVisitorHashLookup
+ *
+ * Arguments: entry - Pointer to visitor entry
+ * p1 - First parameter to match (interface address)
+ * p2 - 2nd parameter to match (whether visitor is accepted)
+ * p3 - 3rd parameter to match (unused)
+ *
+ * Description: This function is used as the Hash Table Helper routine
+ * for isAcceptedVisitor() when looking for accepted visitor
+ * entries in the Hash Table, and will be called by
+ * findHashTableEntryUint() and findHashTableEntryString().
+ *
+ * Returns: _B_TRUE if the entry matches the desired criteria,
+ * otherwise _B_FALSE.
+ */
+/* ARGSUSED */
+static boolean_t
+isAcceptedVisitorHashLookup(void *entry, uint32_t p1, uint32_t p2, uint32_t p3)
+{
+ FaVisitorEntry *faveEntry = entry;
+
+ if ((faveEntry->faVisitorIfaceAddr == p1) &&
+ ((uint32_t)faveEntry->faVisitorRegIsAccepted == p2)) {
+ return (_B_TRUE);
+ }
+
+ return (_B_FALSE);
+}
+
+
+/*
+ * Function: isAcceptedVisitor
+ *
+ * Arguments: htbl - Pointer to hash table.
+ * mnAddr - Mobile Node's Home Address
+ * ifaceAddr - Local Interface Address.
+ *
+ * Description: Check if mnAddr has an accepted visitor entry with
+ * VisitorIfaceAddr equal to IfaceAddr.
+ *
+ * Returns: boolean - _B_TRUE if the entry is in the hash table.
+ *
+ */
+static boolean_t
+isAcceptedVisitor(HashTable *htbl, ipaddr_t mnAddr, ipaddr_t ifaceAddr)
+{
+ FaVisitorEntry *entry;
+
+ entry = findHashTableEntryUint(htbl, mnAddr,
+ LOCK_NONE, isAcceptedVisitorHashLookup, ifaceAddr, _B_TRUE, 0);
+
+ if (entry) {
+ return (_B_TRUE);
+ } else {
+ return (_B_FALSE);
+ }
+}
+
+
+/*
+ * Function: delFAVE
+ *
+ * Arguments: htbl - Pointer to the Hash Table
+ * NAIHash - Pointer to the NAI Hash Table
+ * entry - Pointer to the Visitor Entry
+ * mnAddr - Mobile Node's Home Address
+ *
+ * Description: Delete a visitor entry matching the specified mnAddr
+ * and coAddr in Hash Table. If the coAddr equals mnAddr,
+ * all entries for that mobile node are deleted.
+ *
+ * Returns:
+ */
+void
+delFAVE(HashTable *htbl, FaVisitorEntry **entry, ipaddr_t mnAddr,
+ uint32_t relind)
+{
+ /*
+ * TODO: make this efficient, e.g. we could minimize our stay
+ * in the loop if we figure out that COAddr != mnAddr and we
+ * already deleted one entry.
+ */
+ if ((*entry)->faVisitorMnNAI[0] != '\0' &&
+ (*entry)->faVisitorHomeAddr == INADDR_ANY) {
+
+ if (delHashTableEntryString(htbl, *entry,
+ (*entry)->faVisitorMnNAI, (*entry)->faVisitorMnNAILen,
+ LOCK_NONE)) {
+ /*
+ * Found a match, delete it
+ */
+ delFAVEptr(*entry, _B_TRUE, relind);
+ (void) rw_unlock(&(*entry)->faVisitorNodeLock);
+ (void) rwlock_destroy(&(*entry)->faVisitorNodeLock);
+ free(*entry);
+ *entry = NULL;
+ }
+ } else {
+ if (delHashTableEntryUint(htbl, *entry, mnAddr, LOCK_NONE)) {
+ /*
+ * Found a match, delete it
+ */
+ delFAVEptr(*entry, _B_TRUE, relind);
+ (void) rw_unlock(&(*entry)->faVisitorNodeLock);
+ (void) rwlock_destroy(&(*entry)->faVisitorNodeLock);
+ free(*entry);
+ *entry = NULL;
+ }
+ }
+
+}
+
+
+/*
+ * Function: appendExt
+ *
+ * Arguments: buffer - Pointer to offset in packet where extension is to
+ * be added.
+ * type - Extension type.
+ * data - Extension data.
+ * dataLen - Length of the extension data.
+ *
+ * Description: Append an extension to a registration message contained in
+ * buffer. Returns total number of bytes in the extension.
+ *
+ * Returns: size of the new data added to the packet.
+ */
+static size_t
+appendExt(unsigned char *buffer, int type, unsigned char *data,
+ size_t dataLen)
+{
+ regExt *ext;
+
+ ext = (regExt *)buffer;
+ ext->type = (uint8_t)type;
+ ext->length = dataLen;
+ (void) memcpy(ext + 1, data, dataLen);
+
+ return (dataLen + sizeof (regExt));
+}
+
+/*
+ * Function: appendMierExt
+ *
+ * Arguments: buffer - Pointer to offset in packet where extension is to
+ * be added.
+ * type - Extension type.
+ * subType - MIER Extension subtype
+ * data - Pointer to extension data
+ * dataLen - Length of the data
+ *
+ * Description: Appends a MIER-style extension to the registration message.
+ * Returns total number of bytes in the extension.
+ *
+ * Returns: size of the new data added to the packet.
+ */
+static size_t
+appendMierExt(unsigned char *buffer, int type, int subType,
+ unsigned char *data, uint16_t dataLen)
+{
+ mierLongExt *ext;
+
+ /*
+ * The latest AAA Keys draft now requires that the
+ * key extensions be added as generalized key extensions, so the
+ * code needs to support the MIER-style extension header.
+ */
+ /* LINTED BAD_PTR_CAST_ALIGN */
+ ext = (mierLongExt *)buffer;
+ ext->type = (uint8_t)type;
+ ext->subType = (uint8_t)subType;
+
+ dataLen = htons(dataLen);
+ (void) memcpy(&ext->length, &dataLen, sizeof (uint16_t));
+
+ /*
+ * We simply copy the blob at the end of the MIER structure.
+ */
+ (void) memcpy(ext + 1, data, ntohs(dataLen));
+
+ return (ntohs(dataLen) + sizeof (mierLongExt));
+}
+
+#ifdef KEY_DISTRIBUTION
+/*
+ * Although this function is not intended only for testing purposes, we need
+ * to ifdef it, otherwise we will see some lint warnings. When non ifdef'ed
+ * code uses this function, we can remove the ifdef.
+ */
+/*
+ * Function: appendCritVendorSpecExt
+ *
+ * Arguments: buffer - Pointer to offset in packet where extension is to
+ * be added.
+ * vendorId - Vendor Id
+ * type - Extension type.
+ * data - Pointer to data
+ * keyLen - Length of the data
+ *
+ * Description: Support for vendor specific extensions.
+ *
+ * Returns: size of the new data added to the packet.
+ */
+static size_t
+appendCritVendorSpecExt(unsigned char *buffer, uint32_t vendorId, uint16_t type,
+ unsigned char *data, uint16_t dataLen)
+{
+ vendorSpecExt *ext;
+
+ /*
+ * Grrr. This is ugly -- we should really fix this interface
+ * to ensure that `buffer' is correctly aligned. In the meantime,
+ * at least assert() that it's true and then placate lint.
+ */
+ assert(IS_P2ALIGNED(buffer, sizeof (uintptr_t)));
+ /* LINTED */
+ ext = (vendorSpecExt *)buffer;
+
+ /*
+ * Set the extension type, and the reserved field MUST be set
+ * to zero.
+ */
+ ext->type = (uint8_t)REG_CRIT_VENDOR_SPEC_EXT_TYPE;
+ ext->reserved = 0;
+
+ /*
+ * Set the length, vendor ID and subType. Make sure that they
+ * are in network order.
+ */
+ dataLen = htons(dataLen);
+ (void) memcpy(&ext->length, &dataLen, sizeof (uint16_t));
+
+ vendorId = htonl(vendorId);
+ (void) memcpy(&ext->vendorId, &vendorId, sizeof (uint32_t));
+
+ type = htons(type);
+ (void) memcpy(&ext->vendorType, &type, sizeof (uint16_t));
+
+ /*
+ * We simply copy the blob at the end of the MIER structure.
+ */
+ (void) memcpy(buffer + sizeof (vendorSpecExt), data, ntohs(dataLen));
+
+ /*
+ * We need to return the size of the extension
+ */
+ return (ntohs(dataLen) + sizeof (vendorSpecExt));
+}
+
+/*
+ * The following defines how long a session key is valid for, and is
+ * only used for testing purposes.
+ */
+#define KDC_KEY_LIFETIME 200
+
+/*
+ * Function: createMNKeyExt
+ *
+ * Arguments: buffer - Pointer to offset in packet where extension is to
+ * be added.
+ * type - Extension type.
+ * subType - MIER SubType
+ * key - Pointer to the keys.
+ * keyLen - Length of the keys
+ * nodeSPI - The SPI the Agent will share with the MN
+ * mnAAASPI - SPI shared between the Mobile Node and the AAA
+ * mnNAI - The Mobile Node's NAI, needed for encryption purposes
+ * mnNAILen - Length of the Mobile Node's NAI
+ *
+ * Description: This function takes a previously generated session key for
+ * the Mobile Node, encrypts it as defined in the AAA-Key
+ * Internet-Draft, and adds the MIER-style extension.
+ * Returns total number of bytes in the extension.
+ *
+ * Returns: size of the new data added to the packet.
+ */
+static size_t
+createMNKeyExt(uint8_t *buffer, int type, int subType, uint8_t *key,
+ size_t keyLen, uint32_t nodeSPI, uint32_t mnAAASPI, uint8_t *mnNAI,
+ size_t mnNAILen)
+{
+ keyDataExt *ext;
+ MipSecAssocEntry *msap;
+ MD5_CTX context;
+ time_t currentTime;
+ uint32_t spi;
+ uint32_t lifetime;
+ int length = 0;
+ char mnKey[128];
+ size_t i;
+
+ /*
+ * Allocate a temporary buffer
+ */
+ ext = (keyDataExt *)malloc(sizeof (keyDataExt) + keyLen);
+
+ if (ext == NULL) {
+ syslog(LOG_CRIT, "Unable to allocate memory");
+ return (0);
+ }
+
+
+ /*
+ * Add the AAA SPI to the extension. This SPI is used by
+ * the Mobile Node in order to identify the encryption
+ * method.
+ */
+ mnAAASPI = htonl(mnAAASPI);
+ (void) memcpy(&ext->mnAAASPI, &mnAAASPI, sizeof (uint32_t));
+
+ /*
+ * Add the node SPI, which is the SPI that the Mobile Node
+ * will use to reference this key
+ */
+ spi = nodeSPI;
+ nodeSPI = htonl(nodeSPI);
+ (void) memcpy(&ext->nodeSPI, &nodeSPI, sizeof (uint32_t));
+
+ /*
+ * Add the lifetime of the session key.
+ */
+ GET_TIME(currentTime);
+ lifetime = currentTime + KDC_KEY_LIFETIME;
+ lifetime = htonl(lifetime);
+ (void) memcpy(&ext->lifetime, &lifetime, sizeof (uint32_t));
+
+ /*
+ * Find the AAA SPI that we will use to encrypt the data
+ */
+ if ((msap =
+ findSecAssocFromSPI(mnAAASPI, LOCK_READ)) != NULL) {
+ /*
+ * Create a new Security Association Entry
+ */
+ if (aaaCreateKey(spi, key, keyLen, lifetime) < 0) {
+ syslog(LOG_CRIT, "Unable to create MN SA");
+ free(ext);
+ /* remove the READ lock */
+ (void) rw_unlock(&msap->mipSecNodeLock);
+ return (0);
+ }
+
+ /*
+ * Create the hash...
+ */
+ MD5Init(&context);
+ MD5Update(&context, msap->mipSecKey,
+ (unsigned int) msap->mipSecKeyLen);
+ MD5Update(&context, mnNAI, mnNAILen);
+ MD5Update(&context, msap->mipSecKey,
+ (unsigned int) msap->mipSecKeyLen);
+ MD5Final((uint8_t *)&mnKey, &context);
+
+ /*
+ * XOR the key in the hash output
+ */
+ for (i = 0; i < keyLen; i++) {
+ mnKey[i] ^= key[i];
+ }
+
+ /*
+ * Copy the key and set the length
+ */
+ keyLen = sizeof (mnKey);
+ length = sizeof (keyDataExt) + keyLen;
+ (void) memcpy(ext + sizeof (keyDataExt), mnKey, keyLen);
+
+ /*
+ * Create the MIER extension
+ */
+ length = appendMierExt(buffer, type, subType,
+ (unsigned char *)ext, length);
+
+ /*
+ * Unlock the AAA Security Association
+ */
+ (void) rw_unlock(&msap->mipSecNodeLock);
+ } else {
+ syslog(LOG_ERR, "Failed MN-HA authentication - "
+ "No SPI defined");
+ haCounters.haMNAuthFailureCnt++;
+ }
+
+ /*
+ * Free the previously allocate memory.
+ */
+ free(ext);
+
+ return (length);
+}
+
+/*
+ * Function: createFAKeyExt
+ *
+ * Arguments: buffer - Pointer to offset in packet where extension is to
+ * be added.
+ * vendorId - Vendor Identifier
+ * extId - Extension type.
+ * key - Pointer to the keys.
+ * keyLen - Length of the keys
+ * SPI - The SPI the Agent will share with the MN
+ *
+ * Description: This function takes a previously generated session key for
+ * the Foreign Agent, and adds it as a Critical Vendor Specific
+ * extension. This is only used for testing purposes, since
+ * the normal method for the Foreign Agent to retrieve it's
+ * keys is through the DIAMETER interface.
+ * Returns total number of bytes in the extension.
+ *
+ * Returns: size of the new data added to the packet.
+ */
+static int
+createFAKeyExt(char *buffer, uint32_t vendorId, uint32_t extId,
+ char *key, size_t keyLen, uint32_t SPI)
+{
+ keyDataExt *ext;
+ time_t currentTime;
+ uint32_t spi;
+ uint32_t lifetime;
+ int length = 0;
+
+ /*
+ * Allocate a temporary buffer
+ */
+
+ ext = (keyDataExt *)malloc(sizeof (keyDataExt) + keyLen);
+
+ if (ext == NULL) {
+ syslog(LOG_CRIT, "Unable to allocate memory");
+ return (0);
+ }
+
+ /*
+ * The keys aren't encrypted, so the AAA SPI is set to zero.
+ */
+ spi = htonl(0);
+ (void) memcpy(&ext->mnAAASPI, &spi, sizeof (uint32_t));
+
+ /*
+ * Add the node SPI, which is the SPI that the Mobile Node
+ * will use to reference this key
+ */
+ spi = SPI;
+ SPI = htonl(SPI);
+ (void) memcpy(&ext->nodeSPI, &SPI, sizeof (uint32_t));
+
+ /*
+ * Add the lifetime of the session key.
+ */
+ GET_TIME(currentTime);
+ lifetime = currentTime + KDC_KEY_LIFETIME;
+ lifetime = htonl(lifetime);
+ (void) memcpy(&ext->lifetime, &lifetime, sizeof (uint32_t));
+
+ /*
+ * Copy the key and set the length
+ */
+ length = sizeof (keyDataExt) + keyLen;
+ (void) memcpy(ext + sizeof (keyDataExt), key, keyLen);
+
+ /*
+ * Create the vendor specific extension
+ */
+ length = appendCritVendorSpecExt((uint8_t *)buffer, vendorId, extId,
+ (unsigned char *)ext, length);
+
+ /*
+ * We need to create the SA locally if this is for the FA-HA SA
+ */
+ if (extId == REG_FA_HA_KEY_EXT) {
+ /*
+ * Create a new Security Association Entry
+ */
+ if (aaaCreateKey(spi, (uint8_t *)key, keyLen, lifetime) < 0) {
+ syslog(LOG_CRIT, "Unable to create key");
+ free(ext);
+ return (0);
+ }
+ }
+
+ /*
+ * Free the previously allocate memory.
+ */
+ free(ext);
+
+ return (length);
+}
+#endif /* KEY_DISTRIBUTION */
+
+
+/*
+ * Function: findFAVEHashLookup
+ *
+ * Arguments: entry - Pointer to visitor entry
+ * p1 - First parameter to match (interface address)
+ * p2 - 2nd parameter to match (whether visitor is accepted)
+ * p3 - 3rd parameter to match (unused)
+ *
+ * Description: This function is used as the Hash Table Helper routine
+ * for isAcceptedVisitor() when looking for accepted visitor
+ * entries in the Hash Table, and will be called by
+ * findHashTableEntryUint() and findHashTableEntryString().
+ *
+ * Returns: _B_TRUE if the entry matches the desired criteria,
+ * otherwise _B_FALSE.
+ */
+/* ARGSUSED */
+static boolean_t
+findFAVEHashLookup(void *entry, uint32_t p1, uint32_t p2, uint32_t p3)
+{
+ FaVisitorEntry *faveEntry = entry;
+
+ if ((faveEntry->faVisitorHomeAgentAddr == p1) &&
+ ((uint32_t)faveEntry->faVisitorRegIsAccepted == p2)) {
+ return (_B_TRUE);
+ }
+
+ return (_B_FALSE);
+}
+
+
+/*
+ * Function: findAcceptedFAVE
+ *
+ * Arguments: htbl - Pointer to the Hash Table
+ * mnAddr - Mobile Node's Home Address
+ * haAddr - Home Agent Address
+ *
+ * Description: This function will look for an accepted
+ * visitor entry, and will return the entry.
+ *
+ * Returns: If successful, pointer to visitor entry
+ */
+static FaVisitorEntry *
+findAcceptedFAVE(HashTable *htbl, ipaddr_t mnAddr, ipaddr_t haAddr)
+{
+ FaVisitorEntry *entry;
+
+ /*
+ * Let's see if we can find the entry using the Home
+ * Address. Note that an accepted visitor entry MUST have
+ * a home address, so we do not need to worry about looking
+ * using the NAI.
+ */
+ entry = findHashTableEntryUint(htbl, mnAddr, LOCK_WRITE,
+ findFAVEHashLookup, haAddr, _B_TRUE, 0);
+
+ return (entry);
+}
+
+
+/*
+ * Function: FAprocessRegRequest
+ *
+ * Arguments: messageHdr - Pointer to the Message Control Block
+ * entry - Pointer to the Interface Entry.
+ * inAddr - IP address this request received on
+ *
+ * Description: Process a registration request received at a foreign
+ * agent. If successful, the request will be forwarded to
+ * the Home Agent. If unsuccessful an error will be returned
+ * to the Mobile Node.
+ *
+ * Returns: void
+ */
+void
+FAprocessRegRequest(MessageHdr *messageHdr, MaAdvConfigEntry *entry,
+ ipaddr_t *inAddr)
+{
+ int code = MIP_SUCCESSFUL_REGISTRATION;
+ uint32_t mnSPI;
+ boolean_t forwardToHaFlag = _B_TRUE; /* If FALSE, the request is */
+ /* not forwarded to the Home */
+ /* Agent, but is sent to the */
+ /* AAA infrastructure instead */
+ authExt *mnAuthExt;
+ /*
+ * Support for generalized auth extensions.
+ */
+ regRequest *requestPtr;
+ FaVisitorEntry *favePtr;
+ FaVisitorEntry *acceptedFAVE;
+ char addrstr1[INET_ADDRSTRLEN];
+ char addrstr2[INET_ADDRSTRLEN];
+ char addrstr3[INET_ADDRSTRLEN];
+ char currentTime[MAX_TIME_STRING_SIZE];
+ char relativeTime[MAX_TIME_STRING_SIZE];
+ unsigned char *challenge;
+ int mnAuthExtLen;
+ int index;
+
+ size_t challengeLen = 0;
+
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ requestPtr = (regRequest *) messageHdr->pkt;
+
+ mipverbose(("\n---- %s (%s) ----\n",
+ sprintTime(currentTime, MAX_TIME_STRING_SIZE),
+ sprintRelativeTime(relativeTime, MAX_TIME_STRING_SIZE)));
+ mipverbose(("FA got reg req of length %d [MN %s, HA %s, COA %s]\n",
+ messageHdr->pktLen, ntoa(requestPtr->homeAddr, addrstr1),
+ ntoa(requestPtr->haAddr, addrstr2),
+ ntoa(requestPtr->COAddr, addrstr3)));
+ mipverbose((" [Lifetime %d sec, ID %0#10x : %0#10x]\n",
+ (uint32_t)ntohs(requestPtr->regLifetime),
+ ntohl(requestPtr->IDHigh),
+ ntohl(requestPtr->IDLow)));
+
+ mipverbose(("FAprocessRegRequest called with packet:\n"));
+ if (logVerbosity > 2)
+ printBuffer(messageHdr->pkt, messageHdr->pktLen);
+ mipverbose(("\n"));
+
+ /*
+ * Validate the Care of Address
+ */
+ if ((requestPtr->COAddr != *inAddr) &&
+ (messageHdr->ifType != ON_MCAST_SOCK)) {
+ /*
+ * It is legal for the Care of Address to be set to the
+ * Mobile Node's address, as long as the lifetime field is
+ * set to zero.
+ */
+ if ((requestPtr->homeAddr == requestPtr->COAddr) &&
+ (requestPtr->regLifetime != 0)) {
+ /*
+ * Invalid Care of Address.
+ */
+ code = FA_INVALID_CARE_OF_ADDR;
+ faCounters.faInvalidCareOfAddrCnt++;
+ (void) rejectFromFAToMN(messageHdr, entry, code);
+ return;
+ }
+ }
+
+ /*
+ * If the packet has any of the bits that we do not currently
+ * support, return an error.
+ */
+ if (requestPtr->regFlags & REG_BIT_UNUSED) {
+ code = FA_POORLY_FORMED_REQUEST;
+ faCounters.faPoorlyFormedRequestsCnt++;
+ (void) rejectFromFAToMN(messageHdr, entry, code);
+ return;
+ }
+
+ /*
+ * Support for new MIER-style extension header.
+ *
+ * Are extensions ok and in the right order?
+ */
+ if (mkRegExtList(messageHdr, sizeof (regRequest)) < 0) {
+ code = FA_POORLY_FORMED_REQUEST;
+ faCounters.faPoorlyFormedRequestsCnt++;
+ (void) rejectFromFAToMN(messageHdr, entry, code);
+ return;
+ }
+
+
+ /*
+ * Packet parsing routines now return error codes.
+ *
+ * Is the packet from the Mobile Node valid?
+ */
+ if ((code = IsPacketFromMnValid(messageHdr)) > 0) {
+ /* We support Direct Delivery Style only */
+ if (code != FA_DELIVERY_STYLE_UNAVAILABLE) {
+ /*
+ * We're here because there was a type 130 extension
+ * in the RRQ, and we don't support the encapsulated
+ * delivery style! However, for scalability of the
+ * code, we set the faRTEncapUnavailableCnt counter
+ * in the IsPacketFromMnValid() function. Everything
+ * else increaments faPoorlyFormedRequests counter.
+ * Note: FA_MISSING_* error codes are also considered
+ * as poorly formatted request.
+ */
+ faCounters.faPoorlyFormedRequestsCnt++;
+ }
+ (void) rejectFromFAToMN(messageHdr, entry, code);
+ return;
+ } else if (code == MA_DROP_PACKET) {
+ /* drop the packet */
+ return;
+ }
+
+ /*
+ * We now compare the lifetime
+ * received in the request with the value
+ * configured on the interface.
+ */
+ /* Is lifetime too long? */
+ if (ntohs(requestPtr->regLifetime) >
+ (unsigned short) entry->maAdvMaxRegLifetime) {
+ code = FA_REG_LIFETIME_TOO_LONG;
+ faCounters.faRegLifetimeTooLongCnt++;
+ (void) rejectFromFAToMN(messageHdr, entry, code);
+ return;
+ }
+
+ /* Do we offer the requested encapsulation services? (Minimal Encap) */
+ if ((requestPtr->regFlags & REG_MIN_ENCAP) &&
+ ((entry->maAdvServiceFlags & ADV_MIN_ENCAP) == 0)) {
+ code = FA_ENCAP_UNAVAILABLE;
+ faCounters.faEncapUnavailableCnt++;
+ (void) rejectFromFAToMN(messageHdr, entry, code);
+ return;
+ }
+
+ /* Do we offer the requested encapsulation services? (GRE) */
+ if ((requestPtr->regFlags & REG_GRE_ENCAP) &&
+ ((entry->maAdvServiceFlags & ADV_GRE_ENCAP) == 0)) {
+ code = FA_ENCAP_UNAVAILABLE;
+ faCounters.faEncapUnavailableCnt++;
+ (void) rejectFromFAToMN(messageHdr, entry, code);
+ return;
+ }
+
+ /* Did the mobile request a reverse tunnel... */
+ if (requestPtr->regFlags & REG_REVERSE_TUNNEL) {
+ /* ...but we're not advertising it! */
+ if ((entry->maAdvServiceFlags & ADV_REVERSE_TUNNEL) == 0) {
+ /*
+ * Note: we could have just as easily checked
+ * entry->maReverseTunnelAllowed & RT_FA, but
+ * it seems better to check the wire!
+ * entry->maAdvServiceFlags will be set only if
+ * (entry->maReverseTunnelAllowed & RT_FA).
+ */
+ code = FA_REVERSE_TUNNEL_UNAVAILABLE;
+ faCounters.faReverseTunnelUnavailableCnt++;
+ (void) rejectFromFAToMN(messageHdr, entry, code);
+ return;
+ }
+
+ /*
+ * Is the MN is too far away?
+ *
+ * Some discussion of this is necessary...
+ *
+ * This is a link-local check. Basically, if the TTL of the
+ * packet isn't 255, then the potential is there that some
+ * [other] node is trying to get us to setup a reverse tunnel
+ * to the [registered MN's home] subnet (the MN is
+ * required by RFC2344 to set the TTL to 255).
+ */
+ if (messageHdr->ttl != 255) {
+ code = FA_MN_TOO_DISTANT;
+ faCounters.faMNTooDistantCnt++;
+ (void) rejectFromFAToMN(messageHdr, entry, code);
+ return;
+ }
+ } else if (aaaProtocol != RADIUS) {
+ /*
+ * MN didn't request a Reverse Tunnel - do we require it?
+ * Note: don't make the user change 2 settings to turn off
+ * reverse tunnels. Make sure we're advertising, then
+ * check if it's being required. This way a user can [re]set
+ * reversetunnel=no, and we wont care if reversetunnelrequired
+ * is set (if the fa-bit in reversetunnelrequired is set).
+ */
+ if ((entry->maAdvServiceFlags & ADV_REVERSE_TUNNEL) &&
+ (entry->maReverseTunnelRequired & RT_FA)) {
+ /*
+ * Again, we instead could have checked
+ * entry->maReverseTunnelAllowed & RT_FA instead of
+ * the AdvServiceFlags, but it's better to check the
+ * the wire. entry->maAdvServiceFlags will be set
+ * only if (entry->maReverseTunnelAllowed & RT_FA).
+ */
+ code = FA_REVERSE_TUNNEL_REQUIRED;
+ faCounters.faReverseTunnelRequiredCnt++;
+ (void) rejectFromFAToMN(messageHdr, entry, code);
+ return;
+ }
+ }
+
+ /* Do we offer the requested compression service? */
+ if ((requestPtr->regFlags & REG_VJ_COMPRESSION) &&
+ ((entry->maAdvServiceFlags & ADV_VJ_COMPRESSION) == 0)) {
+ code = FA_VJ_UNAVAILABLE;
+ faCounters.faVJCompUnavailableCnt++;
+ (void) rejectFromFAToMN(messageHdr, entry, code);
+ return;
+ }
+
+ /*
+ * TODO: If the HA address specified is the same as the arriving
+ * interface, we MUST not forward this packet. What about
+ * the case when this address belongs to another interface
+ * on this FA, one on which HA services are being offered.
+ */
+ if (requestPtr->haAddr == requestPtr->COAddr) {
+ syslog(LOG_ERR,
+ "Warning: FA dropping nasty reg req with HAaddr same as COA.");
+ return;
+ }
+
+ /*
+ * Let's retrieve the MN-FA SPI information.
+ */
+ /*
+ * If a Mobile node Foreign agent authentication extension exists
+ * check it.
+ */
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ GET_AUTH_EXT(messageHdr, index, REG_MF_AUTH_EXT_TYPE,
+ mnAuthExt, mnAuthExtLen);
+
+ if (mnAuthExtLen) {
+ GET_SPI(mnSPI, mnAuthExt);
+ } else {
+ mnSPI = 0;
+ }
+
+ GET_EXT_DATA(messageHdr, index, REG_MF_CHALLENGE_EXT_TYPE,
+ challenge, challengeLen);
+
+ /* Try creating a visitor list entry ... */
+ if ((favePtr = mkPendingFAVE(&faVisitorHash, messageHdr,
+ entry->maIfaceAddr, mnSPI, challenge, challengeLen)) == NULL) {
+ code = FA_INSUFFICIENT_RESOURCES;
+ faCounters.faInsufficientResourceCnt++;
+ (void) rejectFromFAToMN(messageHdr, entry, code);
+ return;
+ }
+
+ acceptedFAVE = findAcceptedFAVE(&faVisitorHash, requestPtr->homeAddr,
+ requestPtr->haAddr);
+
+ /*
+ * Support for the latest Challenge/Response I-D.
+ *
+ * Check the message's authentication. This function will set the
+ * forwardToHaFlag field. If disabled, we will silently return
+ * because the Registration Request is being forwarded to the
+ * AAA infrastructure.
+ */
+ code = faCheckRegReqAuth(messageHdr, favePtr, acceptedFAVE, challenge,
+ challengeLen, &forwardToHaFlag);
+
+ if (acceptedFAVE) {
+ (void) rw_unlock(&acceptedFAVE->faVisitorNodeLock);
+ }
+
+ if (code) {
+ if (favePtr->faVisitorRegIsAccepted == _B_FALSE) {
+ /*
+ * Since the visitor entry isn't accepted, and an
+ * error occured, we can delete it. Otherwise, we
+ * will let the visitor entry expire naturally.
+ */
+ delFAVE(&faVisitorHash, &favePtr, requestPtr->homeAddr,
+ REG_REVOKED);
+ } else {
+ (void) rw_unlock(&favePtr->faVisitorNodeLock);
+ }
+ (void) rejectFromFAToMN(messageHdr, entry, code);
+ return;
+ }
+
+ (void) rw_unlock(&favePtr->faVisitorNodeLock);
+
+
+ faCounters.faRegReqRecvdCnt++;
+
+ if (forwardToHaFlag == _B_TRUE) {
+ /* send the registration request to the home agent */
+ (void) forwardFromFAToHA(messageHdr, entry, _B_FALSE);
+ } else {
+ mipverbose(("\n---- %s (%s) ----\n",
+ sprintTime(currentTime, MAX_TIME_STRING_SIZE),
+ sprintRelativeTime(relativeTime, MAX_TIME_STRING_SIZE)));
+ mipverbose(("FA relayed reg req from %s.%d to AAA for %.*s\n",
+ ntoa(messageHdr->src, addrstr1), messageHdr->srcPort,
+ (messageHdr == NULL) ? 1 : messageHdr->mnNAILen,
+ (messageHdr == NULL) ? "-" : (char *)messageHdr->mnNAI));
+
+ mipverbose(("Registration relayed by FA is:\n"));
+ if (logVerbosity > 2)
+ printBuffer(messageHdr->pkt, messageHdr->pktLen);
+ mipverbose(("\n"));
+
+ faCounters.faRegReqRelayedCnt++;
+
+ }
+}
+
+boolean_t
+forcefullyDeregisterMN(ipaddr_t *homeaddr, ipaddr_t COAaddr,
+ ipaddr_t homeAgentaddr)
+{
+ FaVisitorEntry *entry = NULL;
+ char tmp_buf[INET_ADDRSTRLEN];
+ boolean_t result;
+
+
+ /*
+ * NOTE: This function is called by aaaMainThread() and always
+ * expects non-NULL mnNAI, as AAA expects NAI.
+ * But we must search by key homeaddr since acceptFAVE() converts
+ * the indexing from NAI to homeaddr after acceptance of the
+ * mobilenode registration. We have to distinguish two private
+ * overlapping addresses. AAA does not have any notion of incoming
+ * interface index in the AVPcode, thus we are matching with
+ * homeagent address. It is expected that the mobilenode is already
+ * registered.
+ */
+
+ entry = findHashTableEntryUint(&faVisitorHash, *homeaddr,
+ LOCK_WRITE, acceptFAVEHashLookup, COAaddr, _B_TRUE,
+ homeAgentaddr);
+
+
+ if (entry != NULL) {
+ result = delHashTableEntryUint(&faVisitorHash, entry,
+ *homeaddr, LOCK_NONE);
+ if (result == _B_TRUE) {
+ delFAVEptr(entry, _B_FALSE, REG_REVOKED);
+ (void) rw_unlock(&entry->faVisitorNodeLock);
+ (void) rwlock_destroy(&entry->faVisitorNodeLock);
+ free(entry);
+
+ return (_B_TRUE);
+ }
+ }
+
+ mipverbose(("Couldn't find MN with homeaddr %s to close "
+ "session per AAA's request\n",
+ inet_ntop(AF_INET, (const void*)homeaddr, tmp_buf,
+ sizeof (tmp_buf))));
+ return (_B_FALSE);
+}
+
+/*
+ * Function: openDeviceStream
+ *
+ * Arguments: Ifacename- name of the interface (eg: hme0) to which a device
+ * stream is to be opened in raw mode to send M_DATA.
+ *
+ * Description: This function opens a layer 2 raw socket
+ * Returns: -1 on failure, fd on success
+ */
+int openDeviceStream(char *IfaceName) {
+ int fd;
+ char device[MAX_IFNAME_LEN + 5];
+ int ppa;
+ char buf[MAXDLBUF];
+ int rval;
+
+ ifname2devppa(IfaceName, device, &ppa);
+ if ((fd = open(device, (O_RDWR | O_NDELAY))) < 0) {
+ mipverbose(("error opening device %s in openDeviceStream\n",
+ IfaceName));
+ return (-1);
+ }
+
+ /* Attach. */
+ if ((rval = dlattachreq(fd, ppa)) != 0) {
+ mipverbose(("Error attaching to device in openDeviceStream"
+ " with errno %d\n", (-1)*rval));
+ (void) close(fd);
+ return (-1);
+ }
+
+ if ((rval = dlokack(fd, buf)) != 0) {
+ mipverbose(("Error in DLPI ack from device in openDeviceStream"
+ " with errno %d\n", (-1)*rval));
+ (void) close(fd);
+ return (-1);
+ }
+
+ /* Bind. Use sap=0x0800 for IP. */
+ if ((rval = dlbindreq(fd, 0x800, 0, DL_CLDLS, 0, 0)) != 0) {
+ mipverbose(("Error in DLPI bind in openDeviceStream"
+ " with errno %d\n", (-1)*rval));
+ (void) close(fd);
+ return (-1);
+ }
+
+ if ((rval = dlbindack(fd, buf)) != 0) {
+ mipverbose(("Error in DLPI bind ack from devicee in"
+ " openDeviceStream with errno %d\n",
+ (-1)*rval));
+ (void) close(fd);
+ return (-1);
+ }
+
+ /* Issue DLIOCRAW ioctl */
+ if (mip_strioctl(fd, DLIOCRAW, NULL, 0, 0) < 0) {
+ (void) close(fd);
+ mipverbose(("error processing DLIOCRAW ioctl in"
+ " openDeviceStream\n"));
+ return (-1);
+ }
+
+ return (fd);
+}
+
+/*
+ * Function: mip_in_cksum
+ *
+ * Arguments: addr- the starting address (eg: start of IP header)
+ * len- number of bytes over which the checksum is to be computed.
+ *
+ * Description: This function computes the one's complement checksum.
+ * Returns: The one's complement checksum
+ */
+static ushort_t
+mip_in_cksum(ushort_t *addr, int len)
+{
+ int nleft = len;
+ ushort_t *w = addr;
+ ushort_t answer;
+ ushort_t odd_byte = 0;
+ int sum = 0;
+
+ /*
+ * Our algorithm is simple, using a 32 bit accumulator (sum),
+ * we add sequential 16 bit words to it, and at the end, fold
+ * back all the carry bits from the top 16 bits into the lower
+ * 16 bits.
+ */
+ while (nleft > 1) {
+ sum += *w++;
+ nleft -= 2;
+ }
+
+ /* mop up an odd byte, if necessary */
+ if (nleft == 1) {
+ *(uchar_t *)(&odd_byte) = *(uchar_t *)w;
+ sum += odd_byte;
+ }
+
+ /* add back carry outs from top 16 bits to low 16 bits */
+ sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
+ sum += (sum >> 16); /* add carry */
+ answer = ~sum; /* truncate to 16 bits */
+ return (answer);
+}
+
+/*
+ * Function: mip_strioctl
+ *
+ * Arguments: fd- the file descriptor
+ * cmd- the ioctl cmd (eg: DLIOCRAW)
+ * ptr- data pointer
+ * ilen- length of data
+ * olen- lenght of returned data
+ *
+ * Description: wrapper for STREAMS I_STR ioctl
+ * Returns: -1 on failure, 0 on success
+ */
+static int
+mip_strioctl(int fd, int cmd, void *ptr, int ilen, int olen)
+{
+ struct strioctl str;
+
+ str.ic_cmd = cmd;
+ str.ic_timout = 0; /* Use default timer; 15 seconds */
+ str.ic_len = ilen;
+ str.ic_dp = ptr;
+
+ if (ioctl(fd, I_STR, &str) == -1) {
+ return (-1);
+ }
+ if (str.ic_len != olen) {
+ return (-1);
+ }
+ return (0);
+}
+
+
+/*
+ * Function: sendRawPkt
+ *
+ * Arguments: messageHdr - the message control block.
+ * entry - the MAAdvConfigEntry of the interface to send the
+ * registration reply from, and the settings to return
+ * pktlen - length of the Mobile IP packet (eg: sizeof (regReply))
+ *
+ * Description: This function is used when sending a reject to the MN when the
+ * home address of the MN is not yet assigned. In this case, the
+ * packet is sent to an IP level broadcast but since this maps to
+ * a link level broadcast (for ethernet) and we don't want this
+ * message to be misinterpreted by other mobiles nodes (as a
+ * message meant for them), a raw packet is constructed with the
+ * IP destination being a broadcast and the link layer destination
+ * being unicast (i.e the correct mobile nodes L2 addr)
+ * Returns: -1 on failure, 0 on success
+ */
+int
+sendRawPkt(MessageHdr *msgHdr, MaAdvConfigEntry *entry, uint_t pktlen) {
+ int fd;
+ char pkt[MAX_PKT_SIZE], *destaddr;
+ struct ether_header *ethp;
+ struct ip *ipp;
+ struct udphdr *udpp;
+ struct strbuf data;
+ int flags;
+ uchar_t *mipp;
+
+ if ((fd = openDeviceStream(entry->maIfaceName)) < 0)
+ return (-1);
+
+ /* Set up the ethernet header */
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ ethp = (struct ether_header *)pkt;
+ bcopy(msgHdr->slla.sdl_data, ethp->ether_dhost.ether_addr_octet,
+ ETHERADDRL);
+ bcopy(entry->maIfaceHWaddr, ethp->ether_shost.ether_addr_octet,
+ ETHERADDRL);
+ ethp->ether_type = htons(ETHERTYPE_IP);
+
+ /* Set up the IP header */
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ ipp = (struct ip *)(pkt + sizeof (struct ether_header));
+ ipp->ip_v = IPVERSION;
+ ipp->ip_hl = 5; /* 5 32-bit words == 20 bytes */
+ ipp->ip_tos = 0;
+ ipp->ip_len = htons(20 + sizeof (struct udphdr) + pktlen);
+ ipp->ip_id = htons(1);
+ ipp->ip_off = 0;
+ ipp->ip_ttl = 1;
+ ipp->ip_p = IPPROTO_UDP;
+ ipp->ip_sum = mip_in_cksum((ushort_t *)ipp, 20);
+ destaddr = "255.255.255.255"; /* IP broadcast */
+ (void) inet_pton(AF_INET, destaddr, &ipp->ip_dst.s_addr);
+ bcopy(&entry->maIfaceAddr, &ipp->ip_src.s_addr, sizeof (ipaddr_t));
+
+ /* Set up the UDP header */
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ udpp = (struct udphdr *)((uchar_t *)ipp + sizeof (struct ip));
+ udpp->uh_sport = htons(MIP_PORT);
+ udpp->uh_dport = htons(MIP_PORT);
+ udpp->uh_ulen = htons(sizeof (struct udphdr) + pktlen);
+ udpp->uh_sum = 0; /* udp chksum is optional */
+
+ /* Set up the registration reply */
+ mipp = (uchar_t *)((uchar_t *)udpp + sizeof (struct udphdr));
+ bcopy(msgHdr->pkt, mipp, pktlen);
+
+ data.buf = (char *)pkt;
+ data.maxlen = MAX_PKT_SIZE;
+ data.len = sizeof (struct ether_header) + sizeof (struct ip) +
+ + sizeof (struct udphdr) + pktlen;
+ flags = 0;
+
+ if (putmsg(fd, NULL, &data, flags) != 0) {
+ (void) close(fd);
+ return (-1);
+ } else {
+ (void) close(fd);
+ return (0);
+ }
+}
+
+/*
+ * Function: rejectFromFAToMN
+ *
+ * Arguments: messageHdr - the message control block.
+ * entry - the MAAdvConfigEntry of the interface to send the
+ * registration reply from, and the settings to return. If
+ * NULL, indicates there are possibly multiple mobile nodes
+ * to return an error to due to an ICMP error we've received
+ * after attempting to pass a registration request onto an HA.
+ * The registration reply should be returned to all mobile
+ * nodes with a pending entry to the home agent identified by
+ * the destination address of the returned IP packet inside
+ * messageHdr->pkt.
+ * code - the error value to return.
+ *
+ * Description: This function will return a registration reply set with the
+ * error set to code on the interface from entry back to the
+ * address identified as the source address of the original
+ * registration reqeust (except when the home address of the MN is
+ * not assigned as yet i.e 0.0.0.0, in which case the packet is
+ * to an IP level broadcast and link layer unicast (i.e MN's L2
+ * addr)
+ */
+void
+rejectFromFAToMN(MessageHdr *messageHdr, MaAdvConfigEntry *entry, int code)
+{
+ regReply *replyPtr;
+ boolean_t visitor_entryExists;
+ int val, replyLen = sizeof (regReply); /* for now... */
+ char addrstr1[INET_ADDRSTRLEN];
+ char addrstr2[INET_ADDRSTRLEN];
+ char currentTime[MAX_TIME_STRING_SIZE];
+ char relativeTime[MAX_TIME_STRING_SIZE];
+ struct ether_addr ether;
+
+ /*
+ * We will send a denial. Note that we can re-use the same
+ * message header (and buffer) to reply.
+ */
+ /* LINTED BAD_PTR_CAST_ALIGN */
+ replyPtr = (regReply *) messageHdr->pkt;
+ replyPtr->type = REG_REPLY_TYPE;
+ replyPtr->code = (uint8_t)code;
+ if (code == FA_REG_LIFETIME_TOO_LONG)
+ replyPtr->regLifetime = htons(entry->maAdvMaxRegLifetime);
+
+ /*
+ * Copy the identifier from the original Registration Request
+ * into the Registration Reply. Since the headers are different
+ * this requires a bcopy.
+ */
+ bcopy(messageHdr->pkt+16, messageHdr->pkt+12, 8);
+
+ /*
+ * TODO: handle extensions? keep in mind that the request is
+ * poorly formed so we may not be able to do the right thing.
+ */
+ visitor_entryExists = isAcceptedVisitor(&faVisitorHash,
+ replyPtr->homeAddr, entry->maIfaceAddr);
+ if (visitor_entryExists == _B_FALSE) {
+
+ /*
+ * If the source link layer address is valid add an ARP
+ * entry else don't. The entry could be invalid for variety
+ * of reasons (See recvNetworkPacket)
+ */
+ if (messageHdr->isSllaValid &&
+ (replyPtr->homeAddr != INADDR_ANY)) {
+ /*
+ * Add a temporary ARP entry to prevent the FA from
+ * broadcast ARPing.
+ */
+ if ((val = arpIfadd(replyPtr->homeAddr,
+ messageHdr->slla.sdl_data,
+ messageHdr->inIfindex)) < 0) {
+ syslog(LOG_ERR, "SIOCSXARP failed... %s",
+ err2str(val));
+ }
+ }
+ }
+
+ /*
+ * If the request was sent from a mobile node whose home address is not
+ * yet configured i.e 0.0.0.0, then the reply should be sent as an IP
+ * level broadcast but with the mobile nodes link layer address as the
+ * destination L2 i.e link layer unicast. This can be done by opening a
+ * link layer raw socket, constructing the various headers and sending
+ * the packet (cf. RFC 3220 section 3.7.2.3)
+ */
+ if ((replyPtr->homeAddr == INADDR_ANY) && messageHdr->isSllaValid) {
+ if (sendRawPkt(messageHdr, entry, replyLen) == 0) {
+ mipverbose(("\n---- %s (%s) ----\n",
+ sprintTime(currentTime,
+ MAX_TIME_STRING_SIZE),
+ sprintRelativeTime(relativeTime,
+ MAX_TIME_STRING_SIZE)));
+ (void) memcpy(ether.ether_addr_octet,
+ messageHdr->slla.sdl_data, ETHERADDRL);
+ mipverbose(("FA denied reg req from %s.%d for "
+ "MN %s [MAC: %s] (Code %d)\n",
+ ntoa(messageHdr->src, addrstr1),
+ messageHdr->srcPort,
+ ntoa(replyPtr->homeAddr, addrstr2),
+ ether_ntoa(&ether), replyPtr->code));
+ mipverbose(("Registration denial sent by FA is:\n"));
+ if (logVerbosity > 2)
+ printBuffer(messageHdr->pkt, replyLen);
+ mipverbose(("\n"));
+ return;
+ } else {
+ mipverbose(("rejectFromFAtoMN: raw send failed at FA to"
+ " send denial.\n"));
+ return;
+ }
+ }
+ /*
+ * Set socket option IP_XMIT_IF to get the registration reply
+ * unicast to the mobile node...
+ */
+ val = messageHdr->inIfindex;
+ if (setsockopt(entry->maIfaceUnicastSock, IPPROTO_IP, IP_XMIT_IF,
+ &val, sizeof (val)) < 0) {
+ /* There's a problem... */
+ mipverbose(("Can't set IP_XMIT_IF socket option for "
+ "registration reply error message to mobile node %s."
+ "at interface index %d\n",
+ ntoa(messageHdr->src, addrstr2), val));
+ }
+
+
+ if (sendUDPmessage(entry->maIfaceUnicastSock, messageHdr->pkt,
+ replyLen, messageHdr->src, messageHdr->srcPort) == 0) {
+ mipverbose(("\n---- %s (%s) ----\n",
+ sprintTime(currentTime, MAX_TIME_STRING_SIZE),
+ sprintRelativeTime(relativeTime, MAX_TIME_STRING_SIZE)));
+ mipverbose(("FA denied reg req from %s.%d for MN %s (Code %d)\n",
+ ntoa(messageHdr->src, addrstr1), messageHdr->srcPort,
+ ntoa(replyPtr->homeAddr, addrstr2), replyPtr->code));
+
+ mipverbose(("Registration denial sent by FA is:\n"));
+ if (logVerbosity > 2)
+ printBuffer(messageHdr->pkt, replyLen);
+ mipverbose(("\n"));
+
+ } else {
+ mipverbose(("sendto failed at FA while sending denial.\n"));
+ }
+
+ /* Reset IP_XMIT_IF option */
+
+ val = 0;
+ if (setsockopt(entry->maIfaceUnicastSock, IPPROTO_IP, IP_XMIT_IF,
+ &val, sizeof (val)) < 0) {
+ /* There's a problem... */
+ mipverbose(("Can't unset IP_XMIT_IF socket option"
+ "for socket id %d\n", entry->maIfaceUnicastSock));
+ }
+ if (visitor_entryExists == _B_FALSE) {
+
+ if (messageHdr->isSllaValid && (replyPtr->homeAddr !=
+ INADDR_ANY)) {
+ /*
+ * Delete the temporary ARP entry
+ */
+ if ((val = arpIfdel(replyPtr->homeAddr,
+ messageHdr->slla.sdl_data,
+ messageHdr->inIfindex)) < 0) {
+ /*
+ * If the deletion failed bcos there was no
+ * entry then we don't need to report it
+ */
+ if (val != (-1)*ENXIO)
+ syslog(LOG_ERR,
+ "SIOCDXARP failed... %s",
+ err2str(val));
+ }
+ }
+ }
+ mipverbose(("\n\n"));
+}
+
+
+/*
+ * Function: rejectFromICMPToMN
+ *
+ * Arguments: messageHdr - the message control block. Currently contains
+ * in messageHdr->pkt an ICMP error generated by our forward
+ * of a registration request to an unreachable home agent.
+ * haAddr - the address of the home agent that ICMP is telling us
+ * is unreachable.
+ * code - the error value to return to the mobile node.
+ *
+ * Description: This function will return a registration reply with the error
+ * set to code to every *pending* mobile node which has
+ * identified haAddr as that of it's home agent.
+ */
+void
+rejectFromICMPToMN(MessageHdr *messageHdr, ipaddr_t haAddr, int code)
+{
+ FaVisitorEntry *entry = NULL;
+ char srcaddrstr[INET_ADDRSTRLEN];
+ char dstaddrstr[INET_ADDRSTRLEN];
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ regReply *replyPtr = (regReply *)messageHdr->pkt; /* reuse it */
+ int replyLen = sizeof (regReply); /* Size of registration reply */
+ MipSecAssocEntry *mipSAE;
+ uint32_t state[2]; /* For hash-table enumeration */
+ char currentTime[MAX_TIME_STRING_SIZE]; /* syslog messages */
+ char relativeTime[MAX_TIME_STRING_SIZE]; /* syslog messages */
+
+ /*
+ * Rationale: Who do we send the registration reply to?
+ * ----------------------------------------------------
+ *
+ * The ICMP error is known, send the regreP, but to whom? What do we
+ * have? [T]Here's the rub: there's nothing of the forwarded regreQ
+ * returned in the ICMP! RFC792 says ICMP returns the IP header, plus
+ * 64 bits, or in this case just enough to cover the UDP header! We do
+ * have the IP dstaddr that caused the error - the IPaddr the MN says
+ * is it's HA. We have the interface the ICMP came in on - the dstaddr
+ * of the ICMP, but we don't know if the MN was using that as CoA. To
+ * know that we'd need to know if the MN was colocated and using us as
+ * "relay", but simply knowing if the 'R' bit is set on this interface
+ * isn't enough. The MN could have colocated, and still be registering
+ * with us even if we aren't setting the 'R' bit! Therefore, our IPaddr
+ * on this interface doesn't give us anything else to search on.
+ *
+ * Summary: all we have to go on is the identified HAaddr, and that the
+ * MN MUST have (a pending?) registration on this interface (note: if
+ * the MN is reregistering, the FA will have a non-pending entry in its
+ * hash, and also a[nother] *pending* entry [for many a good reason]).
+ *
+ * The only thing we can do is send a registration reply to all the
+ * pending MNs [on this interface] requesting service to this HA. At
+ * least this way bound MNs wont be effected, and those with pending
+ * registrations to another HA are also not effected. All those that
+ * are registering (pending) to this HA on this interface may then have
+ * to reregister, but everything should eventually get worked out. In
+ * practice, the number of pending registrations to a particular HA
+ * should be low (in context), with the notable exception of FA-reset
+ * causing *every* MN that was registered [with this HA] to reregister
+ * simultaneously. Note also, though, that if this HA is unreachable
+ * for one MN, it's likely unreachable for them all!
+ *
+ * One more thing to figure out:
+ *
+ * We need a buffer to send (since we presume there's at least one MN
+ * awaiting our response). This is the only place in the code we don't
+ * have a regreQ to reuse the bits of. messageHdr->pkt is here, and
+ * will be free()'d when screenICMPpkt() return, so if it's long
+ * enough, let's us it!
+ *
+ * First order thoughts:
+ * A regreP takes 20 bytes (we don't need to include UDP/IP headers),
+ * plus we potentially need room for the FA-MN auth (authext and hash)
+ * if required which is another 6 + 16 bringing the total to 42 bytes.
+ *
+ * messageHdr->pkt MUST be enough for outerIPhdr, ICMPhdr,
+ * innerIPhdr, + 64bits [the UDP header], which is *at least*
+ * 20 + 8 + 20 + 8 or 56 bytes.
+ *
+ * At this time it appears any ICMP_UNREACH message that contains a
+ * returned registration request (up to the end of UDP) will be big
+ * enough. Note: at this time challenges don't have to be appended
+ * when there is a registration reply indicating an error.
+ *
+ * The big question is MUST we include an NAIext in this case? RFC2794
+ * etc, are unclear, but to be nice to multihommed MNs there is an
+ * argument to be made for including it, but it would likely mean a
+ * realloc() of messageHdr->pkt! For now, we'll skip it.
+ *
+ * Summary: reply is going to a set of pending MN(s) due to an ICMP
+ * error when attempting to forward a registration request to haAddr.
+ * Parse the FaVisitorHash for pending MNs [registering via this
+ * interface] using this HAaddr.
+ *
+ * OK, we're going with it. Make sure nothing survives of the ICMP!
+ */
+ bzero(&messageHdr->pkt, messageHdr->pktLen);
+
+ /* init the Enumerator State */
+ initEnumeratorState(&state, sizeof (*(state)));
+
+ /* enumerateAllHashTableEntries() - will return NULL when we're out. */
+ while ((entry = enumerateAllHashTableEntries(&faVisitorHash,
+ &(state[BUCKET]), &(state[OFFSET]), LOCK_WRITE)) != NULL) {
+ /* LOCK_WRITE because we may accept this entry for removal! */
+ int val;
+
+ /*
+ * Is it pending to the same HA [on the same iface]? Recall,
+ * the HA is the original ip_dst that wasn't reachable.
+ */
+ if ((entry->faVisitorRegIsAccepted != _B_FALSE) ||
+ (entry->faVisitorHomeAgentAddr != haAddr)) {
+ /* not a MN we need to reply to */
+
+ /* don't forget to unlock this one! */
+ (void) rw_unlock(&entry->faVisitorNodeLock);
+
+ /* read on McDuff. */
+ continue;
+ }
+
+ /* This is someone to reject. */
+ mipverbose(("...found a MN to reply to - IP address %s.\n",
+ ntoa(entry->faVisitorAddr, dstaddrstr)));
+
+ /* Accept the entry for expiration (see delFAVEPtr())! */
+ entry->faVisitorRegIsAccepted = _B_TRUE;
+
+ /* set the important information */
+ replyPtr->type = REG_REPLY_TYPE;
+ replyPtr->code = (uint8_t)code;
+ replyPtr->regLifetime = 0;
+ replyPtr->homeAddr = entry->faVisitorHomeAddr;
+ replyPtr->haAddr = entry->faVisitorHomeAgentAddr;
+ replyPtr->IDHigh = entry->faVisitorRegIDHigh;
+ replyPtr->IDLow = entry->faVisitorRegIDLow;
+
+ /*
+ * The jury's out as to whether we want/should/need/MUST do
+ * this. For now, let's also include the NAI if it'll fit.
+ */
+ if ((entry->faVisitorMnNAILen) &&
+ (messageHdr->pktLen - replyLen >=
+ sizeof (entry->faVisitorMnNAILen))) {
+ replyLen += appendExt((messageHdr->pkt + replyLen),
+ REG_MN_NAI_EXT_TYPE,
+ (unsigned char *)entry->faVisitorMnNAI,
+ entry->faVisitorMnNAILen);
+ }
+
+ /*
+ * Don't forget the FA-MN authenticator (if configured).
+ * Note appendAuthExt() assumes the authenticator is always
+ * going to be AUTHENTICATOR_LEN bytes! If you change
+ * auth.c:appendAuthExt(), change this!!!
+ */
+ if ((messageHdr->pktLen - replyLen >= AUTHENTICATOR_LEN) &&
+ ((mipSAE = findSecAssocFromSPI(entry->faVisitorSPI,
+ LOCK_READ)) != NULL)) {
+ /* formulate an authenticator... */
+ replyLen += appendAuthExt(messageHdr->pkt,
+ replyLen, REG_MF_AUTH_EXT_TYPE, mipSAE);
+
+ /* remove the READ lock */
+ (void) rw_unlock(&mipSAE->mipSecNodeLock);
+ }
+
+ /*
+ * If the source link layer address is valid add an ARP
+ * entry else don't. The entry could be invalid for variety
+ * of reasons (See recvNetworkPacket)
+ */
+ if (messageHdr->isSllaValid) {
+ /*
+ * Add a temporary ARP entry to prevent the FA from
+ * broadcast ARPing.
+ */
+ if ((val = arpIfadd(replyPtr->homeAddr,
+ messageHdr->slla.sdl_data,
+ messageHdr->inIfindex)) < 0) {
+ syslog(LOG_ERR, "SIOCSXARP failed... %s",
+ err2str(val));
+ }
+ }
+
+ /*
+ * Note: we're not including a challenge! That may mean we'll
+ * run out of buffer, and it's also NOT required by spec.
+ */
+
+ /*
+ * Set socket option IP_XMIT_IF to get the regreP unicast
+ * to the mobile node...
+ */
+ val = entry->faVisitorInIfindex;
+ if (setsockopt(messageHdr->ifEntry->maIfaceUnicastSock,
+ IPPROTO_IP, IP_XMIT_IF, &val, sizeof (val)) < 0) {
+ /* There's a problem... */
+ syslog(LOG_ERR,
+ "Can't set IP_XMIT_IF socket option for"
+ " registration reply to mobile node %s.",
+ ntoa(messageHdr->src, srcaddrstr));
+ }
+
+ /* may as well try anyway (we do this elsewhere) */
+ if (sendUDPmessage(messageHdr->ifEntry->maIfaceUnicastSock,
+ messageHdr->pkt, messageHdr->pktLen, entry->faVisitorAddr,
+ entry->faVisitorPort) == 0) {
+ mipverbose(("\n---- %s (%s) ----\n",
+ sprintTime(currentTime,
+ MAX_TIME_STRING_SIZE),
+ sprintRelativeTime(relativeTime,
+ MAX_TIME_STRING_SIZE)));
+ mipverbose(("FA sent ICMP-reply to %s.%d (Code %d)\n",
+ ntoa(entry->faVisitorAddr, dstaddrstr),
+ entry->faVisitorPort, replyPtr->code));
+ mipverbose(("FA sent ICMP-reply packet:\n"));
+ if (logVerbosity > 2)
+ printBuffer(messageHdr->pkt,
+ messageHdr->pktLen);
+ mipverbose(("\n"));
+
+ } else {
+ syslog(LOG_ERR,
+ "sendto failed while sending ICMP-reply.");
+ }
+
+ /* Reset IP_XMIT_IF option on socket */
+ val = 0;
+ if (setsockopt(messageHdr->ifEntry->maIfaceUnicastSock,
+ IPPROTO_IP, IP_XMIT_IF, &val, sizeof (val)) < 0) {
+ syslog(LOG_ERR,
+ "Can't unset socket option IP_XMIT_IF"
+ "which was set for interface index %d",
+ entry->faVisitorInIfindex);
+ }
+
+ /* cleanup */
+ if (messageHdr->isSllaValid) {
+ /*
+ * Delete the temporary ARP entry
+ */
+ if ((val = arpIfdel(replyPtr->homeAddr,
+ messageHdr->slla.sdl_data,
+ messageHdr->inIfindex)) < 0) {
+ /*
+ * If the deletion failed
+ * because there was no entry
+ * entry then we don't need to
+ * report it.
+ */
+ if (val != (-1)*ENXIO) {
+ syslog(LOG_ERR,
+ "SIOCDXARP failed %s",
+ err2str(val));
+ }
+ }
+ }
+
+ /*
+ * Should we delete this FAVisitorEntry? The reason to do so
+ * is if there's multiple MNs to reply to, we're going to send
+ * registration replies to all of them at this time. When/if
+ * another ICMP comes back from another's registration request,
+ * it would be sloppy (at best) to generate another set of
+ * registration replies. Generating multiple registration
+ * replies to the same MN in response to a set of registration
+ * requests (in this case ICMPs) may also be considered a
+ * literal violation of 2002-bis:
+ *
+ * A foreign agent MUST NOT transmit a Registration
+ * Reply except when relaying a Registration Reply
+ * received from a mobile node's home agent, or when
+ * replying to a Registration Request received from
+ * a mobile node in the case in which the foreign
+ * agent is denying service to the mobile node.
+ *
+ * The counter-argument to NOT delete this entry, is (bug?)
+ * delFAVEptr() doesn't do anything for pending MNs; we could
+ * let the timers kill off these pending FAVE entries. These
+ * MNs are likely to re-regreQ anyway, so there's likely some
+ * [performance, etc.] advantage to leaving them around.
+ */
+
+ /*
+ * Think: should we also generate an accounting stop request
+ * (_B_TRUE for now)? The MN is likely going to reregister
+ * either with the same HA (hopefully in time-fallback), or
+ * another. Deleting the record is likely only going to
+ * create more work for us when this happens, though it's
+ * unclear if leaving it around may cause AAA auditing issues.
+ * Better to spend the time to clear it up, which comes with
+ * deleting this pending entry. REASON_UNKNOWN is because
+ * there's no accounting reason for it.
+ */
+ delFAVEptr(entry, _B_TRUE, REASON_UNKNOWN);
+
+ /* don't forget to unlock this one! */
+ (void) rw_unlock(&entry->faVisitorNodeLock);
+
+ } /* fin looping through pending entries */
+
+
+ /* we're out of MNs, so we're done reacting to the ICMP */
+ return;
+
+} /* rejectFromICMPToMN() */
+
+
+void
+forwardFromFAToHA(MessageHdr *messageHdr, MaAdvConfigEntry *entry,
+ boolean_t triggeredByRadius)
+{
+ regRequest *requestPtr;
+ /* LINTED E_FUNC_SET_NOT_USED */
+ authExt *mnAuthExt;
+ MipSecAssocEntry *mipSecAssocEntry;
+ MobilityAgentEntry *mae;
+ int mnAuthExtLen = 0;
+ int index, challengeindex;
+ char addrstr1[INET_ADDRSTRLEN];
+ char addrstr2[INET_ADDRSTRLEN];
+ char addrstr3[INET_ADDRSTRLEN];
+ char currentTime[MAX_TIME_STRING_SIZE];
+ char relativeTime[MAX_TIME_STRING_SIZE];
+ int code;
+ int maAuthExtLen;
+ /* LINTED E_FUNC_SET_NOT_USED */
+ genAuthExt *maAuthExt;
+ /* LINTED E_FUNC_SET_NOT_USED */
+ unsigned char *challenge;
+ size_t challengeLen = 0;
+
+ mipverbose(("forwardFromFAToHA..."));
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ requestPtr = (regRequest *) messageHdr->pkt;
+
+ /*
+ * RFC3012:
+ *
+ * Valid cases: MH FAC MN-AAA
+ * FAC MN-AAA
+ * MH FAC MF
+ * MH
+ */
+
+ /*
+ * Is Foreign Agent Challenge present?
+ */
+ GET_EXT_DATA(messageHdr, index, REG_MF_CHALLENGE_EXT_TYPE,
+ challenge, challengeLen);
+ challengeindex = index;
+
+ if (!triggeredByRadius) {
+ if (challengeLen == 0) {
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ GET_AUTH_EXT(messageHdr, index, REG_MH_AUTH_EXT_TYPE,
+ mnAuthExt, mnAuthExtLen);
+ }
+ }
+ if (mnAuthExtLen == 0 || triggeredByRadius) {
+ /*
+ * Support for the generalized
+ * authentication extension.
+ */
+
+ /* LINTED BAD_PTR_CAST_ALIGN */
+ GET_GEN_AUTH_EXT(messageHdr, index,
+ GEN_AUTH_MN_AAA, maAuthExt, maAuthExtLen);
+ /*
+ * Case where we have:
+ *
+ * MH FAC MF
+ */
+ if (maAuthExtLen == 0 && challengeLen != 0) {
+ index = challengeindex;
+ }
+ }
+
+ /*
+ * Support for different extension header
+ * format.
+ *
+ * Initialize the basic length of the message, which is
+ * everything up to and including the MN Authentication
+ * Extension (index is assumed to be set from above).
+ */
+ messageHdr->pktLen = (messageHdr->extIdx[index] - messageHdr->pkt);
+
+ /*
+ * Increase the packet length.
+ */
+ messageHdr->pktLen += messageHdr->extHdrLength[index] +
+ messageHdr->extLength[index];
+
+ /*
+ * Do we need to add the FA-HA Auth Extension? Let's not
+ * forget the entry will be locked upon the return, so
+ * it's our responsibility to unlock the security
+ * assocation entry.
+ */
+ if ((mipSecAssocEntry =
+ findSecAssocFromIp(requestPtr->haAddr,
+ LOCK_READ)) != NULL) {
+
+ messageHdr->pktLen += appendAuthExt(messageHdr->pkt,
+ messageHdr->pktLen, REG_FH_AUTH_EXT_TYPE,
+ mipSecAssocEntry);
+
+ /*
+ * As promised, we unlock the SA
+ */
+ (void) rw_unlock(&mipSecAssocEntry->mipSecNodeLock);
+ } else {
+ /*
+ * Was authentication required?
+ */
+ if (fhAuthRequired) {
+ syslog(LOG_ERR, "Error: No Sa for Home Agent");
+ code = HA_FA_AUTH_FAILURE;
+ faCounters.faHAAuthFailureCnt++;
+ rejectFromFAToMN(messageHdr, entry, code);
+ return;
+ }
+ }
+
+ /*
+ * We may need to install an "IPsecRequest apply ..." policy that will
+ * protect the registration in the way our SA tells us to, as well as
+ * any "IPsecReply permit ..." policy in anticipation of a response.
+ * Since mobile nodes can share the same HA, we may have already done
+ * this, so check to make sure it's not already in place.
+ */
+ if ((mae = findMaeFromIp(requestPtr->haAddr, LOCK_READ)) != NULL) {
+ if (((mae->maIPsecFlags & IPSEC_REQUEST_APPLY) == 0) &&
+ (IPSEC_REQUEST_ANY(mae->maIPsecSAFlags[IPSEC_APPLY]))) {
+ /* Policy hasn't been installed for this agent-peer */
+ char peerAddr[IPv4_ADDR_LEN];
+
+ (void) ntoa(mae->maAddr, peerAddr);
+
+ /* do whatever we do to install the ipsec policy */
+ if (installIPsecPolicy(mae->maIPsecRequest[IPSEC_APPLY])
+ < 0) {
+ /* failed, log it */
+ syslog(LOG_CRIT,
+ "Could not install %s for [Address %s]: %s",
+ IPSEC_POLICY_STRING(IPSEC_REQUEST_APPLY),
+ peerAddr, mae->maIPsecRequest[IPSEC_APPLY]);
+
+ /* unlock */
+ (void) rw_unlock(&mae->maNodeLock);
+
+ /* never ignore SAs - fail */
+ return;
+ }
+
+ /* set the flag */
+ mae->maIPsecFlags |= IPSEC_REQUEST_APPLY;
+ }
+
+ /*
+ * We're forwarding the registration request to the home agent.
+ * We need to set the reply-permit policy in anticipation of a
+ * registration reply from the home agent.
+ */
+ if (((mae->maIPsecFlags & IPSEC_REPLY_PERMIT) == 0) &&
+ (IPSEC_REPLY_ANY(mae->maIPsecSAFlags[IPSEC_PERMIT]))) {
+ /* This SA hasn't been installed for this agent-peer */
+ char peerAddr[IPv4_ADDR_LEN];
+
+ /* this is for error reporting */
+ (void) ntoa(mae->maAddr, peerAddr);
+
+ /* have ipsec add it */
+ if (installIPsecPolicy(mae->maIPsecReply[IPSEC_PERMIT])
+ < 0) {
+ /* Failed - log it. */
+ syslog(LOG_CRIT,
+ "Could not install %s for [Address %s]: %s",
+ IPSEC_POLICY_STRING(IPSEC_REPLY_PERMIT),
+ peerAddr, mae->maIPsecReply[IPSEC_PERMIT]);
+
+ /* unlock */
+ (void) rw_unlock(&mae->maNodeLock);
+
+ /* Never send if we can't enforce policy. */
+ return;
+ }
+ /* set the flag */
+ mae->maIPsecFlags |= IPSEC_REPLY_PERMIT;
+ }
+
+ /* we're sending, so this agent is now officially an HA peer */
+ mae->maPeerFlags |= HA_PEER;
+
+ /* unlock */
+ (void) rw_unlock(&mae->maNodeLock);
+ }
+
+ /* forward to home agent */
+ if (sendUDPmessage(entry->maIfaceUnicastSock,
+ messageHdr->pkt, messageHdr->pktLen, requestPtr->haAddr,
+ MIP_PORT) == 0) {
+ mipverbose(("\n---- %s (%s) ----\n",
+ sprintTime(currentTime, MAX_TIME_STRING_SIZE),
+ sprintRelativeTime(relativeTime,
+ MAX_TIME_STRING_SIZE)));
+ mipverbose(("FA relayed reg req from %s.%d to HA "
+ "%s.%d for MN homeaddr %s\n",
+ ntoa(messageHdr->src, addrstr1),
+ messageHdr->srcPort,
+ ntoa(requestPtr->haAddr, addrstr2), MIP_PORT,
+ ntoa(requestPtr->homeAddr, addrstr3)));
+
+ mipverbose(("Registration of length %d relayed by " \
+ "FA is:\n", messageHdr->pktLen));
+ if (logVerbosity > 2) {
+ printBuffer(messageHdr->pkt,
+ messageHdr->pktLen);
+ }
+ mipverbose(("\n"));
+
+ faCounters.faRegReqRelayedCnt++;
+ } else {
+ syslog(LOG_ERR, "sendto failed at FA while relaying "
+ "registration.");
+ }
+
+ mipverbose(("\n\n"));
+}
+
+/*
+ * Function: delHABE
+ *
+ * Arguments: hamnePtr - Pointer to a pointer to a mobile node entry.
+ * mnAddr - Mobile Node's Home Address
+ * COAddr - Mobile Node's care of address.
+ * sessionLifeTime - Duration of the session.
+ *
+ * Description: Delete a binding entry for the specified mnAddr in Hash
+ * Table. If the care-of address COAddr equals mnAddr, all
+ * bindings for the mobile node are deleted.
+ *
+ * Note: It is necessary that the Mobile Node Entry is in a
+ * write locked state when we enter this function.
+ *
+ * Returns: Upon successful completion, the sessionLifeTime argument
+ * will contain the number of session the session was active.
+ */
+static void
+delHABE(HaMobileNodeEntry **hamnePtr, ipaddr_t mnAddr, ipaddr_t COAddr,
+ uint32_t *sessionLifeTime)
+{
+ HaBindingEntry *entry;
+ HaBindingEntry *prev_entry = NULL;
+ time_t currentTime;
+ char addrstr1[INET_ADDRSTRLEN];
+ char addrstr2[INET_ADDRSTRLEN];
+
+ *sessionLifeTime = 0;
+
+ mipverbose(("delHABE called for mnAddr %s COAddr %s\n",
+ ntoa(mnAddr, addrstr1),
+ ntoa(COAddr, addrstr2)));
+
+ entry = (*hamnePtr)->bindingEntries;
+
+ while (entry) {
+ if ((entry->haBindingMN == mnAddr) &&
+ (entry->haBindingCOA == COAddr)) {
+ /* Found a match, delete it */
+ if (prev_entry == NULL) {
+ (*hamnePtr)->bindingEntries =
+ entry->next;
+ } else {
+ prev_entry->next = entry->next;
+ }
+
+ delHABEent(*hamnePtr, entry);
+
+ GET_TIME(currentTime);
+ *sessionLifeTime += currentTime -
+ entry->haBindingTimeGranted;
+ free(entry);
+ break;
+ }
+ prev_entry = entry;
+ entry = entry->next;
+ }
+
+ /*
+ * We need to delete the Mobile Node Entry if the
+ * entry was dynamic and has no more bindings.
+ */
+ if ((*hamnePtr)->haMnBindingCnt == 0 &&
+ (*hamnePtr)->haMnIsEntryDynamic == TRUE) {
+ mipverbose(("Deleting Mobile Node #1"));
+ /* Expired */
+ if (delHashTableEntryUint(&haMobileNodeHash, *hamnePtr,
+ mnAddr, LOCK_NONE) == _B_FALSE) {
+ syslog(LOG_ERR, "Unable to delete Mobile Node Entry");
+ } else {
+ (void) rw_unlock(&(*hamnePtr)->haMnNodeLock);
+ (void) rwlock_destroy(&(*hamnePtr)->haMnNodeLock);
+ free(*hamnePtr);
+ *hamnePtr = NULL;
+ }
+ }
+}
+
+
+/*
+ * Function: addHABE
+ *
+ * Arguments: hamnePtr - Pointer to Mobile Node Entry
+ * src - Source Address of the COA
+ * srcPort - Source Port of the COA
+ * ifEntry - Pointer to the Interface Entry (optional)
+ * regFlags - Registration Flags
+ * homeAddr - Home Address
+ * COAddr - Care of Address
+ * haAddr - Home Agent's Address
+ * lifetime - Registration Lifetime
+ * existingBinding - Boolean that is set to _B_TRUE if the
+ * request is renewing an exiting binding
+ * entry.
+ * sessionLifeTime - If the event of a binding renewal,
+ * this argument will return the number
+ * of seconds the binding entry has
+ * been active.
+ *
+ * Description: Add a binding for the specified pair of mobile node
+ * and care-of address. By the time we get here, we know
+ * that we are processing a registration and coAddr is
+ * not the same as mnAddr. Note that entry is the the
+ * mobility interface on the mobile node's home network.
+ *
+ * Note that this function no longer accepts the
+ * registration request as a parameter, but requires
+ * each individual fields. This is needed in order to
+ * have a single function that creates bindings for both
+ * when a request is received off the wire, and when the
+ * agent attempts to restore bindings from disk.
+ *
+ * Note: The caller MUST have write locked the Mobile Node
+ * Entry prior to calling this function.
+ *
+ * Returns: int - 0 if the binding entry was successfully added.
+ * Upon successful completion, the sessionLifeTime argument
+ * will contain the number of session the session was
+ * active, and the existingBinding field will be set if the
+ * request is renewing an existing binding entry.
+ */
+/* ARGSUSED */
+int
+addHABE(HaMobileNodeEntry *hamnePtr, ipaddr_t src, in_port_t srcPort,
+ MaAdvConfigEntry *ifEntry, uint8_t regFlags, ipaddr_t homeAddr,
+ ipaddr_t COAddr, ipaddr_t haAddr, uint32_t lifetime,
+ boolean_t *existingBinding, uint32_t *sessionLifeTime)
+{
+ int val;
+ time_t currentTime;
+ HaBindingEntry *hentry;
+ HaBindingEntry *prev_hentry = NULL;
+ HaBindingEntry *tmp;
+ char addrstr1[INET_ADDRSTRLEN];
+ char addrstr2[INET_ADDRSTRLEN];
+ char addrstr3[INET_ADDRSTRLEN];
+
+
+ *existingBinding = _B_FALSE;
+ *sessionLifeTime = 0;
+
+#ifdef NO_SIMULTANEOUS
+ /*
+ * If NO_SIMULTANEOUS is defined, we do not allow simultaneous
+ * bindings. This means that a Mobile Node can only have a single
+ * binding. The reason why this feature is not currently supported
+ * is due to the architecture of the new 2.8 tunnel driver. By
+ * clearing this flag, we ensure that all previous bindings will
+ * be cleared
+ */
+ regFlags &= ~REG_SIMULTANEOUS_BINDINGS;
+#endif
+
+ /*
+ * If the interface was not provided, it is because we are trying
+ * to restore a binding entry... Let's find it.
+ */
+ if (ifEntry == NULL) {
+ /*
+ * Get the matching maIfaceAddr from mnAdvConfigTable
+ */
+ if ((ifEntry = (MaAdvConfigEntry *)findHashTableEntryUint(
+ &maAdvConfigHash, hamnePtr->haBindingIfaceAddr,
+ LOCK_NONE, NULL, 0, 0,
+ 0)) == NULL) {
+ syslog(LOG_ERR, "Unable to find interface in hash " \
+ "table");
+ return (-1);
+ }
+ }
+
+ hentry = hamnePtr->bindingEntries;
+
+ while (hentry) {
+ if (hentry->haBindingMN == homeAddr) {
+ if (hentry->haBindingCOA == COAddr) {
+ *existingBinding = _B_TRUE;
+ hentry->haBindingSrcAddr = src;
+ hentry->haBindingHaAddr = haAddr;
+
+ /*
+ * We now compare the lifetime
+ * received in the request with the value
+ * configured on the interface.
+ */
+ GET_TIME(currentTime);
+ if ((ntohs(lifetime) >
+ (unsigned short)
+ ifEntry->maAdvMaxRegLifetime)) {
+ hentry->haBindingTimeExpires =
+ currentTime +
+ (uint32_t)
+ ifEntry->maAdvMaxRegLifetime;
+ } else {
+ hentry->haBindingTimeExpires =
+ currentTime +
+ (uint32_t)ntohs(
+ lifetime);
+ }
+ hentry->haBindingSrcPort = srcPort;
+ hentry->haBindingRegFlags = regFlags;
+
+ mipverbose((
+ "HA renewed binding %s@%s "
+ "for %ld sec (Binding cnt %d).\n",
+ ntoa(homeAddr, addrstr1),
+ ntoa(COAddr, addrstr2),
+ hentry->haBindingTimeGranted,
+ hamnePtr->haMnBindingCnt));
+
+ GET_TIME(currentTime);
+ *sessionLifeTime = currentTime -
+ hentry->haBindingTimeGranted;
+ hentry->haBindingTimeGranted = currentTime;
+ } else {
+ /*
+ * found an entry for this MN with another COA
+ */
+ if ((regFlags & REG_SIMULTANEOUS_BINDINGS)
+ == 0) {
+ /* Found a match, delete it */
+ if (prev_hentry == NULL) {
+ hamnePtr->bindingEntries =
+ hentry->next;
+ } else {
+ prev_hentry->next =
+ hentry->next;
+ }
+
+ tmp = hentry->next;
+
+ delHABEent(hamnePtr, hentry);
+
+ free(hentry);
+ hentry = tmp;
+ continue;
+ }
+ }
+ }
+
+ prev_hentry = hentry;
+ hentry = hentry->next;
+ }
+
+ if (*existingBinding == _B_TRUE)
+ return (0);
+
+ /*
+ * We did not find an entry, now we create one.
+ */
+ if ((hentry =
+ (HaBindingEntry *)calloc(1, sizeof (HaBindingEntry))) == NULL) {
+ return (-1);
+ }
+
+ /*
+ * We need to create a new entry ...
+ * update binding count for this mobile node and total bindings
+ */
+ hamnePtr->haMnBindingCnt++;
+
+ mipverbose(("At %s, creating tunnel for %s through %s %s\n",
+ ntoa(haAddr, addrstr1), ntoa(homeAddr, addrstr2),
+ ntoa(COAddr, addrstr3),
+ (regFlags & REG_SIMULTANEOUS_BINDINGS) ? "(simultaneous)":""));
+
+ if ((val = encapadd(homeAddr, haAddr, COAddr,
+ (uint8_t)regFlags & REG_REVERSE_TUNNEL)) < 0) {
+ syslog(LOG_ERR, "encapadd failed ... %s", err2str(val));
+ }
+
+ /*
+ * Proxy arp only works for non-PPP interfaces.
+ */
+ if (((ifEntry->maIfaceFlags & IFF_POINTOPOINT) == 0) &&
+ (hamnePtr->haMnBindingCnt == 1)) {
+ mipverbose(("Enabling tunneling for %s.\n",
+ ntoa(homeAddr, addrstr1)));
+
+ mipverbose(("Setting proxy arp for %s at %s\n",
+ ntoa(homeAddr, addrstr1),
+ hwAddrWrite(ifEntry->maIfaceHWaddr, addrstr2)));
+
+ if ((val = arpadd(homeAddr,
+ ifEntry->maIfaceHWaddr, ATF_PUBL)) < 0) {
+ syslog(LOG_ERR, "First arpadd (proxy) failed ... %s",
+ err2str(val));
+ }
+
+ /*
+ * Solaris 2.8 automatically sends out a gratuitous ARP
+ * when a publishable ARP entry is created and in another
+ * OS, we expect arpadd to do so explicitly.
+ */
+ haCounters.haGratuitousARPsSentCnt++;
+ }
+
+#ifdef FIREWALL_SUPPORT
+ /*
+ * If the care-of-address is outside the protected domain then
+ * encapsulate and redirect those packets to the firewall's internal
+ * address.
+ * TODO: This will likely change since we should not delete the
+ * tunneling of a COA through FW unless *ALL* mobile nodes using that
+ * COA have their bindings expire, i.e. we need to keep a count of
+ * the number of MNs using that COA. We should be OK as long as an
+ * external COA is not shared by multiple mobile nodes.
+ */
+ if (isInsideProtectedDomain(COAddr) == FALSE) {
+ mipverbose(("At %s, creating tunnel for %s through firewall %s 0\n",
+ ntoa(haAddr, addrstr1), ntoa(COAddr, addrstr2),
+ ntoa(domainInfo.fwAddr[0], addrstr3)));
+
+ if ((val = encapadd(COAddr, haAddr, domainInfo.fwAddr[0],
+ (uint8_t)regFlags & REG_REVERSE_TUNNEL)) < 0) {
+ syslog(LOG_ERR, "encapadd failed ... %s", err2str(val));
+ }
+ }
+#endif /* FIREWALL_SUPPORT */
+
+ hentry->haBindingMN = homeAddr;
+ hentry->haBindingCOA = COAddr;
+ hentry->haBindingSrcAddr = src;
+ hentry->haBindingHaAddr = haAddr;
+
+ /*
+ * We now compare the lifetime received in the request with the value
+ * configured on the interface.
+ */
+ GET_TIME(hentry->haBindingTimeGranted);
+ if (ntohs(lifetime) > (unsigned short) ifEntry->maAdvMaxRegLifetime) {
+ hentry->haBindingTimeExpires =
+ hentry->haBindingTimeGranted +
+ (uint32_t)ifEntry->maAdvMaxRegLifetime;
+ } else {
+ hentry->haBindingTimeExpires =
+ hentry->haBindingTimeGranted +
+ (uint32_t)ntohs(lifetime);
+ }
+ hentry->haBindingSrcPort = srcPort;
+ hentry->haBindingRegFlags = regFlags;
+
+ /*
+ * Add the node to the queue
+ */
+ hentry->next = hamnePtr->bindingEntries;
+ hamnePtr->bindingEntries = hentry;
+
+ mipverbose(("HA added binding %s@%s at entry %p "
+ "for %ld sec (Binding cnt %d).\n",
+ ntoa(homeAddr, addrstr1), ntoa(COAddr, addrstr2), (void *)hentry,
+ hentry->haBindingTimeGranted, hamnePtr->haMnBindingCnt));
+
+ return (0);
+}
+
+#ifdef RADIUS_ENABLED
+void
+radiusCloseSession(HaMobileNodeEntry *hamnePtr)
+{
+ char addr[20];
+#ifdef RADIUS_DEBUG
+ (void) fprintf(stderr, "Closing Session %p\n", hamnePtr->haRadiusState);
+#endif
+ radCloseSession(hamnePtr->haRadiusState,
+ ntoa(hamnePtr->haMnAddr, addr), NULL);
+#ifdef RADIUS_DEBUG
+ (void) fprintf(stderr, "Finished Closing Session %p\n",
+ hamnePtr->haRadiusState);
+#endif
+ hamnePtr->haRadiusState = NULL;
+} /* radiusCloseSession */
+#endif /* RADIUS_ENABLED */
+
+/*
+ * Function: HAdispatchRadius
+ *
+ * Arguments: messageHdr - Pointer to the Message Control Block
+ * entry - Pointer to the Interface Entry.
+ * inAddr - IP address this request received on
+ *
+ * Description: The HA continues with the processing of the
+ * registration request after the Radius client
+ * responded with an ANSWER to the Open Session
+ * request.
+ *
+ * Returns:
+ */
+
+/* ARGSUSED */
+void
+HAdispatchRadius(MessageHdr *messageHdr, MaAdvConfigEntry *entry,
+ ipaddr_t *inAddr)
+{
+ HaMobileNodeEntry *mnEntry = NULL;
+ uint32_t mnSPI = 0;
+ uint32_t faSPI = 0;
+ int code = 0;
+
+ /* If Auth failure from checking radius Answer (e.g., AVP's wrong) */
+ if (messageHdr->aaaResultCode == HA_MN_AUTH_FAILURE) {
+ haCounters.haMNAuthFailureCnt++;
+ code = HA_MN_AUTH_FAILURE;
+ } else if (messageHdr->aaaResultCode == HA_REVERSE_TUNNEL_REQUIRED) {
+ haCounters.haReverseTunnelRequiredCnt++;
+ code = HA_REVERSE_TUNNEL_REQUIRED;
+ }
+ if (code == 0) {
+ /* Assume no hamnePtr - no MobileNodeEntry (mnEntry) so far */
+ code = haCheckRegReqAuthContinue(messageHdr, &mnEntry, &mnSPI,
+ &faSPI);
+ }
+ (void) HAprocessRegRequestContinue(messageHdr, messageHdr->ifEntry,
+ inAddr, code, mnEntry, mnSPI, faSPI);
+}
+/*
+ * Function: HAprocessRegRequest
+ *
+ * Arguments: messageHdr - Pointer to the Message Control Block
+ * entry - Pointer to the Interface Entry.
+ * inAddr - IP address this request received on
+ *
+ * Description: Process a registration request received at a home
+ * agent, and return the registration reply to the
+ * foreign agent.
+ *
+ * If the Mobile Node was not configured locally,
+ * and the Mobile Node node was successfully
+ * authenticated (either by using the default SPI,
+ * or VIA the AAA infrastructure), we will dynamically
+ * create a Mobile Node Entry.
+ *
+ * If the Mobile Node did not specify a Home Address,
+ * by including an NAI in the request and setting the
+ * home address to 0, we will allocate a Home Address
+ * out of an address pool (if configured). If the NAI
+ * was defined in the local configuration file, we will
+ * use the pool defined in the user's profile, otherwise
+ * we will use the default pool.
+ *
+ * If successful and this is a request for a new binding,
+ * we will setup the tunnel.
+ *
+ * If DIAMETER/RADIUS was enabled, we will issue accounting
+ * records to the AAA.
+ *
+ * Returns:
+ */
+/* ARGSUSED */
+void
+HAprocessRegRequest(MessageHdr *messageHdr, MaAdvConfigEntry *entry,
+ ipaddr_t *inAddr)
+{
+ int code = MIP_SUCCESSFUL_REGISTRATION;
+ uint32_t faSPI = 0;
+ uint32_t mnSPI = 0;
+ regRequest *requestPtr;
+ HaMobileNodeEntry *mnEntry = NULL;
+ char addrstr1[INET_ADDRSTRLEN];
+ char addrstr2[INET_ADDRSTRLEN];
+ char addrstr3[INET_ADDRSTRLEN];
+ char currentTime[MAX_TIME_STRING_SIZE];
+ char relativeTime[MAX_TIME_STRING_SIZE];
+ int okID = _B_FALSE;
+
+ /* LINTED BAD_PTR_CAST_ALIGN */
+ requestPtr = (regRequest *) messageHdr->pkt;
+
+ mipverbose(("\n---- %s (%s) ----\n",
+ sprintTime(currentTime, MAX_TIME_STRING_SIZE),
+ sprintRelativeTime(relativeTime, MAX_TIME_STRING_SIZE)));
+ mipverbose(("HA got reg req [MN %s, HA %s, COA %s]\n",
+ ntoa(requestPtr->homeAddr, addrstr1),
+ ntoa(requestPtr->haAddr, addrstr2),
+ ntoa(requestPtr->COAddr, addrstr3)));
+ mipverbose((" [Lifetime %d sec, ID %0#10x : %0#10x]\n",
+ (uint32_t)ntohs(requestPtr->regLifetime),
+ ntohl(requestPtr->IDHigh),
+ ntohl(requestPtr->IDLow)));
+
+ mipverbose(("HAprocessRegRequest called for pkt:\n"));
+ if (logVerbosity > 2)
+ printBuffer(messageHdr->pkt, messageHdr->pktLen);
+ mipverbose(("\n"));
+
+ /*
+ * If we are not the home agent, then reject this request.
+ */
+ if ((requestPtr->haAddr != *inAddr) &&
+ (messageHdr->ifType != ON_BCAST_SOCK)) {
+ code = HA_UNKNOWN_HOME_AGENT;
+ haCounters.haUnknownHACnt++;
+ HABuildRegReply(messageHdr, messageHdr->ifEntry,
+ code, mnEntry, faSPI, NULL, okID, _B_FALSE, 0,
+ _B_FALSE);
+ return;
+ }
+
+ /*
+ * If the packet has the unused bit set, or if the home address
+ * is set to the care of address AND the lifetime is not set to
+ * zero, this packet is bad. The addresses can be the same when
+ * the Mobile Node sends an explicit deregistration, which is does
+ * when it enters its home network.
+ */
+ if ((requestPtr->regFlags & REG_BIT_UNUSED) ||
+ ((requestPtr->homeAddr == requestPtr->COAddr) &&
+ (requestPtr->regLifetime != 0))) {
+ code = HA_POORLY_FORMED_REQUEST;
+ haCounters.haPoorlyFormedRequestsCnt++;
+ HABuildRegReply(messageHdr, messageHdr->ifEntry,
+ code, mnEntry, faSPI, NULL, okID, _B_FALSE, 0,
+ _B_FALSE);
+ return;
+ }
+
+ /*
+ * Checking for an invalid packet, where the Home Agent's Address
+ * and the Care of Address are set to the same value, AND the lifetime
+ * is set to a non-zero address.
+ */
+ if ((requestPtr->haAddr == requestPtr->COAddr) &&
+ (requestPtr->regLifetime != 0)) {
+ code = HA_POORLY_FORMED_REQUEST;
+ haCounters.haPoorlyFormedRequestsCnt++;
+ HABuildRegReply(messageHdr, messageHdr->ifEntry,
+ code, mnEntry, faSPI, NULL, okID, _B_FALSE, 0,
+ _B_FALSE);
+ return;
+ }
+
+ /*
+ * Support for new MIER-style extension header.
+ *
+ * Are extensions ok and in the right order?
+ */
+ if (mkRegExtList(messageHdr, sizeof (regRequest)) < 0) {
+ code = HA_POORLY_FORMED_REQUEST;
+ haCounters.haPoorlyFormedRequestsCnt++;
+ HABuildRegReply(messageHdr, messageHdr->ifEntry,
+ code, mnEntry, faSPI, NULL, okID, _B_FALSE, 0, _B_FALSE);
+ return;
+ }
+
+ /*
+ * Is the packet from Care-of address (FA or colocated MN) valid?
+ */
+ if ((code = IsPacketFromCoaValid(messageHdr)) > 0) {
+ haCounters.haPoorlyFormedRequestsCnt++;
+ HABuildRegReply(messageHdr, messageHdr->ifEntry,
+ code, mnEntry, faSPI, NULL, okID, _B_FALSE,
+ 0, _B_FALSE);
+ return;
+ } else if (code == MA_DROP_PACKET) {
+ /* drop the packet */
+ return;
+ }
+
+ /*
+ * Check the message's authentication. Note that this
+ * function will return the Mobile Node Entry in a write
+ * locked state. Therefore, we need to unlock the node
+ * when we are done with it.
+ */
+ code = haCheckRegReqAuth(messageHdr, &mnEntry, &mnSPI, &faSPI);
+
+ if (code == HA_MN_AUTH_FAILURE) {
+ HABuildRegReply(messageHdr, messageHdr->ifEntry,
+ code, mnEntry, faSPI, NULL, okID, _B_FALSE, 0,
+ _B_FALSE);
+ return;
+ }
+
+ if (aaaProtocol != RADIUS) {
+ HAprocessRegRequestContinue(messageHdr,
+ messageHdr->ifEntry, inAddr, code, mnEntry, mnSPI,
+ faSPI);
+ }
+
+}
+
+/*
+ * Function: HAprocessRegRequestContinue
+ *
+ * Arguments: messageHdr - Pointer to the Message Control Block
+ * entry - Pointer to the Interface Entry.
+ * inAddr - IP address this request received on
+ * code - result code from HAprocessRegRequest().
+ * mnEntry - Mobile Node Entry
+ * mnSPI - Mobile Node SPI
+ * faSPI - FA SPI
+ *
+ * Description: Continue to Process a registration request received
+ * at a home agent, and return the registration reply
+ * to the foreign agent.
+ * If aaaProtocol is Radius, this function is called
+ * after the Radius client returns auth Answer.
+ *
+ * Otherwise, if not using Radius, just continue on from
+ * the HAprocessRegRequest() function until reg reply is
+ * sent.
+ *
+ * If the Mobile Node was not configured locally,
+ * and the Mobile Node node was successfully
+ * authenticated (either by using the default SPI,
+ * or VIA the AAA infrastructure), we will dynamically
+ * create a Mobile Node Entry.
+ *
+ * If the Mobile Node did not specify a Home Address,
+ * by including an NAI in the request and setting the
+ * home address to 0, we will allocate a Home Address
+ * out of an address pool (if configured). If the NAI
+ * was defined in the local configuration file, we will
+ * use the pool defined in the user's profile, otherwise
+ * we will use the default pool.
+ *
+ * If successful and this is a request for a new binding,
+ * we will setup the tunnel.
+ *
+ * If DIAMETER/RADIUS was enabled, we will issue accounting
+ * records to the AAA.
+ *
+ * Returns:
+ */
+
+/* ARGSUSED */
+static void
+HAprocessRegRequestContinue(MessageHdr *messageHdr, MaAdvConfigEntry *entry,
+ ipaddr_t *inAddr, int code, HaMobileNodeEntry *mnEntry, uint32_t mnSPI,
+ uint32_t faSPI)
+{
+ regRequest *requestPtr;
+ boolean_t locallyAssignedAddress = _B_FALSE;
+ MipSecAssocEntry *mnsae = NULL;
+ int okID = _B_FALSE;
+ boolean_t existingBindings = _B_FALSE;
+ uint32_t sessionLifeTime = 0;
+ char addrstr1[INET_ADDRSTRLEN];
+
+ /* LINTED BAD_PTR_CAST_ALIGN */
+ requestPtr = (regRequest *) messageHdr->pkt;
+
+ if (code) {
+ goto reply;
+ }
+
+ /*
+ * If we do not have a Mobile Node entry, let's see if
+ * we can create one.
+ */
+ if (mnEntry == NULL) {
+ if (requestPtr->regLifetime == 0) {
+ /*
+ * A Mobile Node entry is needed for deregistration
+ * purposes. Let's silently discard this one.
+ */
+ syslog(LOG_ERR, "Received a deregistration from an "
+ "unknown Mobile Node");
+ return;
+ }
+
+ /*
+ * Is the Mobile Node requesting an IP Address?
+ */
+ if (defaultPool && requestPtr->homeAddr == INADDR_ANY) {
+ requestPtr->homeAddr =
+ GetAddressFromPool(defaultPool);
+
+ if (requestPtr->homeAddr == INADDR_ANY) {
+ code = HA_INSUFFICIENT_RESOURCES;
+ syslog(LOG_ERR, "Unable to allocate address "
+ "from address pool %d",
+ defaultPool);
+ haCounters.haInsufficientResourceCnt++;
+ goto reply;
+ }
+ locallyAssignedAddress = _B_TRUE;
+ }
+
+ /*
+ * So it's one of those special cases where we need
+ * to create the Mobile Node Entry dynamically. Please
+ * note that the Mobile Node Entry will be write locked
+ * upon return.
+ */
+ mipverbose(("Created a dynamic Mobile Node Entry with SPI %d\n",
+ mnSPI));
+ mnEntry = CreateMobileNodeEntry(_B_TRUE,
+ requestPtr->homeAddr, (char *)messageHdr->mnNAI,
+ messageHdr->mnNAILen, 0, mnSPI, NULL,
+ (locallyAssignedAddress == _B_TRUE) ? defaultPool : 0);
+
+ if (mnEntry == NULL) {
+ /*
+ * We've got some bigger fish to fry...
+ */
+ syslog(LOG_CRIT, "Unable to create dynamic MN Entry");
+ haCounters.haInsufficientResourceCnt++;
+ code = HA_INSUFFICIENT_RESOURCES;
+ goto reply;
+ }
+ } else {
+ /*
+ * Is the Mobile Node requesting an IP Address?
+ */
+ if (mnEntry->haPoolIdentifier &&
+ requestPtr->homeAddr == INADDR_ANY) {
+ /*
+ * If we've already allocated an address, and
+ * the Mobile Node is still requesting one, let's
+ * free what we already have.
+ */
+ if (mnEntry->haMnAddr) {
+ requestPtr->homeAddr = mnEntry->haMnAddr;
+ } else {
+ requestPtr->homeAddr = GetAddressFromPool(
+ mnEntry->haPoolIdentifier);
+
+ if (requestPtr->homeAddr == INADDR_ANY) {
+ code = HA_INSUFFICIENT_RESOURCES;
+ syslog(LOG_ERR, "Unable to allocate "
+ "address from address "
+ "pool %d",
+ mnEntry->haPoolIdentifier);
+ haCounters.
+ haInsufficientResourceCnt++;
+ goto reply;
+ }
+ /*
+ * Save the allocated address
+ */
+ mnEntry->haMnAddr = requestPtr->homeAddr;
+ locallyAssignedAddress = _B_TRUE;
+ }
+ }
+ }
+
+ /*
+ * Was HA a broadcast address?
+ */
+ if (requestPtr->haAddr != entry->maIfaceAddr) {
+ code = HA_UNKNOWN_HOME_AGENT;
+ haCounters.haUnknownHACnt++;
+ goto reply;
+ }
+
+ /*
+ * Check if the number of active bindings exceeds the
+ * maximum allowed.
+ */
+ if ((requestPtr->regFlags & REG_SIMULTANEOUS_BINDINGS) &&
+ (mnEntry->haMnBindingCnt == MAX_SIMULTANEOUS_BINDINGS)) {
+ code = HA_TOO_MANY_SIMULTANEOUS;
+ haCounters.haTooManyBindingsCnt++;
+ goto reply;
+ }
+
+ /*
+ * Get the Mobile Node's SA, remember that the entry
+ * returned will be read locked, so we need to unlock the
+ * entry when we are done.
+ */
+ if ((mnsae = findSecAssocFromSPI(mnEntry->haMnSPI,
+ LOCK_READ)) == NULL) {
+ syslog(LOG_ERR, "Unable to find MN SPI for %s",
+ ntoa(requestPtr->homeAddr, addrstr1));
+ code = HA_MN_AUTH_FAILURE;
+ goto reply;
+ }
+
+ /*
+ * Check ID used for replay protection
+ */
+ okID = HAisIDok(mnEntry->haMnRegIDHigh, mnEntry->haMnRegIDLow,
+ ntohl(requestPtr->IDHigh), ntohl(requestPtr->IDLow),
+ mnsae->mipSecReplayMethod);
+
+ if (okID == _B_FALSE) {
+ code = HA_ID_MISMATCH;
+ haCounters.haIDMismatchCnt++;
+ goto reply;
+ }
+
+
+ if (requestPtr->regLifetime == 0) {
+ /* Do Radius Close Session */
+#ifdef RADIUS_ENABLED
+ if (radiusEnabled)
+ radiusCloseSession(mnEntry);
+#endif /* RADIUS_ENABLED */
+ haCounters.haDeRegReqRecvdCnt++;
+ delHABE(&mnEntry, requestPtr->homeAddr, requestPtr->COAddr,
+ &sessionLifeTime);
+ } else {
+ haCounters.haRegReqRecvdCnt++;
+
+ /*
+ * We need to make sure we're allowing reverse tunneling. The
+ * tunnel is bi-directional by default, but we need to make
+ * sure it's OK to set it up!
+ */
+ if (requestPtr->regFlags & REG_REVERSE_TUNNEL) {
+ /* MNs requesting, are we allowing? */
+ if (!(entry->maReverseTunnelAllowed & RT_HA)) {
+ /*
+ * Note: we do NOT check ADV_REVERSE_TUNNEL
+ * because that's what we're advertising,
+ * which is FA-centric. ReverseTunnelAllowed
+ * is specifically designed to distinguish
+ * between HA allowing but FA isn't (and so
+ * reverseTunnel may not be being advertised).
+ */
+ code = HA_REVERSE_TUNNEL_UNAVAILABLE;
+ haCounters.haReverseTunnelUnavailableCnt++;
+ goto reply;
+ } /* OK to proceed with the usual tunnel setup... */
+
+ } else if (aaaProtocol != RADIUS) {
+ /*
+ * MNs not requesting, are we requiring!
+ * Note: don't check the advertisement bit here!
+ */
+ if (entry->maReverseTunnelRequired & RT_HA) {
+ code = HA_REVERSE_TUNNEL_REQUIRED;
+ haCounters.haReverseTunnelRequiredCnt++;
+ } /* OK to proceed with the usual tunnel setup... */
+
+ }
+
+ if (addHABE(mnEntry, messageHdr->src, messageHdr->srcPort,
+ messageHdr->ifEntry, requestPtr->regFlags,
+ requestPtr->homeAddr, requestPtr->COAddr,
+ requestPtr->haAddr, requestPtr->regLifetime,
+ &existingBindings,
+ &sessionLifeTime) < 0) {
+ code = HA_INSUFFICIENT_RESOURCES;
+ haCounters.haInsufficientResourceCnt++;
+ goto reply;
+ }
+ }
+
+#ifdef NO_SIMULTANEOUS
+ if (requestPtr->regFlags & REG_SIMULTANEOUS_BINDINGS) {
+ /*
+ * If simultaneous bindings was requested, set the appropriate
+ * acceptance code.
+ */
+ code = MIP_SIMULTANEOUS_NOT_SUPPORTED;
+ }
+#endif
+
+reply:
+
+ HABuildRegReply(messageHdr, messageHdr->ifEntry,
+ code, mnEntry, faSPI, mnsae, okID, locallyAssignedAddress,
+ sessionLifeTime, existingBindings);
+}
+
+static void
+HABuildRegReply(MessageHdr *messageHdr, MaAdvConfigEntry *entry,
+ int code, HaMobileNodeEntry *mnEntry, uint32_t faSPI,
+ MipSecAssocEntry *mnsae, int okID, boolean_t locallyAssignedAddress,
+ uint32_t sessionLifeTime, boolean_t existingBindings)
+{
+ regReply *replyPtr;
+ regRequest *requestPtr;
+ unsigned char *challenge;
+ unsigned char challengeBuffer[ADV_MAX_CHALLENGE_LENGTH];
+ size_t challengeLen = 0;
+ int index;
+ ipaddr_t COAddr;
+ ipaddr_t haAddr;
+ ipaddr_t homeAddr;
+ char NAIBuffer[ADV_MAX_NAI_LENGTH];
+ uint32_t IDhi;
+ uint32_t IDlo;
+ time_t localTime;
+ int repLen;
+ MipSecAssocEntry *mipSecAssocEntry;
+ boolean_t ignoreTunnel;
+#ifdef FIREWALL_SUPPORT
+ uint8_t tFlags;
+ boolean_t encapToFw;
+#endif /* FIREWALL_SUPPORT */
+ char currentTime[MAX_TIME_STRING_SIZE];
+ char relativeTime[MAX_TIME_STRING_SIZE];
+ char addrstr1[INET_ADDRSTRLEN];
+ char addrstr2[INET_ADDRSTRLEN];
+ int val;
+ int rc;
+ int result;
+
+ mipverbose(("top of HABuildRegReply...\n"));
+ /* LINTED BAD_PTR_CAST_ALIGN */
+ requestPtr = (regRequest *) messageHdr->pkt;
+
+ /*
+ * Retrieve the Challenge
+ */
+ GET_EXT_DATA(messageHdr, index, REG_MF_CHALLENGE_EXT_TYPE,
+ challenge, challengeLen);
+
+ /*
+ * Copy the challenge into a buffer so that we can manipulate it
+ * later...
+ */
+ if (challengeLen > 0) {
+ if (challengeLen > ADV_MAX_CHALLENGE_LENGTH) {
+ syslog(LOG_ERR, "Challenge length exceeds maximum "
+ "supported length");
+ code = HA_POORLY_FORMED_REQUEST;
+ haCounters.haPoorlyFormedRequestsCnt++;
+ } else {
+ (void) memcpy(challengeBuffer, challenge, challengeLen);
+ }
+ }
+
+ /*
+ * For now, we'll create the new packet in place. But not before we
+ * save some of the fields that could get overwritten.
+ */
+ COAddr = requestPtr->COAddr; /* this is the only one we risk losing */
+ haAddr = requestPtr->haAddr;
+ homeAddr = requestPtr->homeAddr;
+#ifdef FIREWALL_SUPPORT
+ tFlags = requestPtr->regFlags;
+#endif
+ /*
+ * If we have an NAI, save it.
+ */
+ if (messageHdr->mnNAILen) {
+ (void) memcpy(NAIBuffer, messageHdr->mnNAI,
+ messageHdr->mnNAILen);
+ }
+
+ /* LINTED BAD_PTR_CAST_ALIGN */
+ replyPtr = (regReply *) messageHdr->pkt;
+ replyPtr->type = REG_REPLY_TYPE;
+ replyPtr->code = (uint8_t)code;
+ if (ntohs(requestPtr->regLifetime) > entry->maAdvMaxRegLifetime) {
+ replyPtr->regLifetime =
+ htons(entry->maAdvMaxRegLifetime);
+ } else {
+ replyPtr->regLifetime = requestPtr->regLifetime;
+ }
+ replyPtr->homeAddr = homeAddr;
+ replyPtr->haAddr = entry->maIfaceAddr;
+
+ HAnewID(&IDhi, &IDlo, ntohl(requestPtr->IDHigh),
+ ntohl(requestPtr->IDLow),
+ (mnsae == NULL) ? NONE : mnsae->mipSecReplayMethod, okID);
+ if (mnEntry != NULL && mnsae != NULL) {
+ HAstoreID(&(mnEntry->haMnRegIDHigh), &(mnEntry->haMnRegIDLow),
+ IDhi, IDlo, mnsae->mipSecReplayMethod, okID);
+ GET_TIME(localTime);
+ if (code) {
+ mnEntry->haServiceRequestsDeniedCnt++;
+ mnEntry->haRecentServiceDeniedCode = code;
+ mnEntry->haRecentServiceDeniedTime = localTime;
+ } else {
+ mnEntry->haServiceRequestsAcceptedCnt++;
+ mnEntry->haRecentServiceAcceptedTime = localTime;
+ }
+ }
+ replyPtr->IDHigh = htonl(IDhi);
+ replyPtr->IDLow = htonl(IDlo);
+ repLen = sizeof (regReply);
+
+ /*
+ * The MN-HA MUST follow the MN-AAA Session Keys
+ */
+ if (mnsae) {
+ /*
+ * Release the lock on the security association in order
+ * to eliminate a potential deadlock situation.
+ */
+ (void) rw_unlock(&mnsae->mipSecNodeLock);
+ }
+
+ if (messageHdr->mnNAILen) {
+ repLen += appendExt((messageHdr->pkt + repLen),
+ REG_MN_NAI_EXT_TYPE, (unsigned char *) NAIBuffer,
+ messageHdr->mnNAILen);
+ }
+
+ /*
+ * mnEntry is assumed to be set in some of the statements below.
+ * It is not set whenever authentication has failed.
+ */
+ if (mnEntry) {
+#ifdef KEY_DISTRIBUTION
+ if ((messageHdr->pktSource == MIP_PKT_FROM_AAA &&
+ messageHdr->mnHaKeyLen) ||
+ messageHdr->kdcKeysPresent == _B_TRUE) {
+ if (aaaProtocol == AAA_NONE) {
+ /*
+ * Support for the Generalized Key extension.
+ *
+ * We have some keying information to send to the Mobile
+ * Node from the AAA Server.
+ */
+ repLen += createMNKeyExt((messageHdr->pkt + repLen),
+ REG_GEN_MN_HA_KEY_EXT_TYPE, GEN_KEY_MN_HA,
+ messageHdr->mnHaKey, messageHdr->mnHaKeyLen,
+ messageHdr->mnHaSPI, messageHdr->mnAAASPI,
+ (uint8_t *)NAIBuffer, messageHdr->mnNAILen);
+ mnEntry->haMnSPI = messageHdr->mnHaSPI;
+ } else {
+#else /* KEY_DISTRIBUTION */
+ if (messageHdr->pktSource == MIP_PKT_FROM_AAA &&
+ messageHdr->mnHaKeyLen && (aaaProtocol == DIAMETER)) {
+#endif /* KEY_DISTRIBUTION */
+
+ /*
+ * We received some keying information from DIAMETER,
+ * and we need to send this to the mobile node.
+ * Support for the Generalized Key extension.
+ */
+ repLen += appendMierExt((messageHdr->pkt + repLen),
+ REG_GEN_MN_HA_KEY_EXT_TYPE, GEN_KEY_MN_HA,
+ messageHdr->mnHaKey, messageHdr->mnHaKeyLen);
+#ifdef KEY_DISTRIBUTION
+ }
+#endif /* KEY_DISTRIBUTION */
+ }
+
+#ifdef KEY_DISTRIBUTION
+ if ((messageHdr->pktSource == MIP_PKT_FROM_AAA &&
+ messageHdr->mnFaKeyLen) ||
+ messageHdr->kdcKeysPresent == _B_TRUE) {
+ if (aaaProtocol == AAA_NONE) {
+ /*
+ * Support for the Generalized Key extension.
+ *
+ * We have some keying information to send to the Mobile
+ * Node from the AAA Server.
+ */
+ repLen += createMNKeyExt((messageHdr->pkt + repLen),
+ REG_GEN_MN_FA_KEY_EXT_TYPE, GEN_KEY_MN_FA,
+ messageHdr->mnFaKey, messageHdr->mnFaKeyLen,
+ messageHdr->mnFaSPI, messageHdr->mnAAASPI,
+ (uint8_t *)NAIBuffer, messageHdr->mnNAILen);
+ } else {
+#else /* KEY_DISTRIBUTION */
+ if (messageHdr->pktSource == MIP_PKT_FROM_AAA &&
+ messageHdr->mnFaKeyLen && aaaProtocol == DIAMETER) {
+#endif /* KEY_DISTRIBUTION */
+
+ /*
+ * We received some keying information from DIAMETER,
+ * and we need to send this to the mobile node.
+ * Support for the Generalized Key extension.
+ */
+ repLen += appendMierExt((messageHdr->pkt + repLen),
+ REG_GEN_MN_FA_KEY_EXT_TYPE, GEN_KEY_MN_FA,
+ messageHdr->mnFaKey, messageHdr->mnFaKeyLen);
+#ifdef KEY_DISTRIBUTION
+ }
+#endif /* KEY_DISTRIBUTION */
+ }
+
+ /*
+ * The MN-HA MUST follow the MN-AAA Session Keys
+ */
+ if ((mnsae =
+ findSecAssocFromSPI(mnEntry->haMnSPI, LOCK_READ)) != NULL) {
+ repLen += appendAuthExt(messageHdr->pkt, repLen,
+ REG_MH_AUTH_EXT_TYPE, mnsae);
+
+ (void) rw_unlock(&mnsae->mipSecNodeLock);
+ }
+ } /* Matches if (mnEntry) above */
+
+ /*
+ * If a challenge was present, it MUST be added after the MN-HA
+ * and prior to the MN-FA.
+ */
+ if (challengeLen > 0) {
+ repLen += appendExt((messageHdr->pkt + repLen),
+ REG_MF_CHALLENGE_EXT_TYPE, challengeBuffer, challengeLen);
+ }
+
+#ifdef KEY_DISTRIBUTION
+ if (messageHdr->mnFaKeyLen &&
+ messageHdr->kdcKeysPresent == _B_TRUE) {
+ /*
+ * Support for the Generalized Key extension.
+ *
+ * We have some keying information to send to the Mobile
+ * Node from the AAA Server.
+ */
+ repLen +=
+ createFAKeyExt((char *)(messageHdr->pkt + repLen),
+ VENDOR_ID_SUN, REG_MN_FA_KEY_EXT,
+ (char *)&messageHdr->mnFaKey, messageHdr->mnFaKeyLen,
+ messageHdr->mnFaSPI);
+
+ repLen +=
+ createFAKeyExt((char *)(messageHdr->pkt + repLen),
+ VENDOR_ID_SUN, REG_FA_HA_KEY_EXT,
+ (char *)&messageHdr->faHaKey, messageHdr->faHaKeyLen,
+ messageHdr->faHaSPI);
+ faSPI = messageHdr->faHaSPI;
+ }
+#endif /* KEY_DISTRIBUTION */
+
+ if (faSPI) {
+ /*
+ * Now we get the Foreign Agent's Security Association,
+ * which will be read locked upon return.
+ */
+ if ((mipSecAssocEntry =
+ findSecAssocFromSPI(faSPI, LOCK_READ)) == NULL) {
+ syslog(LOG_ERR, "Unable to add FH Auth - SPI (%d) not"
+ "defined", faSPI);
+ replyPtr->code = HA_FA_AUTH_FAILURE;
+
+ } else {
+ repLen += appendAuthExt(messageHdr->pkt, repLen,
+ REG_FH_AUTH_EXT_TYPE, mipSecAssocEntry);
+
+ /*
+ * And now we must unlock the entry.
+ */
+ (void) rw_unlock(&mipSecAssocEntry->mipSecNodeLock);
+ }
+ } else {
+ if (fhAuthRequired) {
+ replyPtr->code = HA_FA_AUTH_FAILURE;
+ haCounters.haFAAuthFailureCnt++;
+ syslog(LOG_ERR, "Cannot add FH Auth - No SA (%d)",
+ faSPI);
+ }
+ }
+
+ /*
+ * First assume nothing special is needed to ensure that unsuccessful
+ * registrations reach the mobile node.
+ */
+#ifdef FIREWALL_SUPPORT
+ encapToFw = _B_FALSE;
+#endif /* FIREWALL_SUPPORT */
+ ignoreTunnel = _B_FALSE;
+
+ /* Is this really the case? */
+ if ((replyPtr->code != MIP_SUCCESSFUL_REGISTRATION) &&
+ (replyPtr->code != MIP_SIMULTANEOUS_NOT_SUPPORTED)) {
+ /*
+ * For "deregister all" requests, ignore the tunnel to
+ * ensure unsuccessful registrations are sent on the
+ * local (home) network.
+ */
+ if ((replyPtr->regLifetime == 0) &&
+ (COAddr == homeAddr) &&
+ mnEntry && (mnEntry->haMnBindingCnt)) {
+ ignoreTunnel = _B_TRUE;
+#ifdef RADIUS_ENABLED
+ /* Do Radius Close Session */
+ if (radiusEnabled)
+ radiusCloseSession(mnEntry);
+#endif /* RADIUS_ENABLED */
+ }
+
+#ifdef FIREWALL_SUPPORT
+ /*
+ * If MN is using an external COA and there isn't a tunnel
+ * to FW already, create one temporarily.
+ */
+ if ((isInsideProtectedDomain(COAddr) == FALSE) &&
+ ((findHABE(&haMobileNodeHash, homeAddr,
+ COAddr) == _B_FALSE))) {
+ encapToFw = _B_TRUE;
+ }
+#endif /* FIREWALL_SUPPORT */
+
+ /*
+ * Do we have an assigned Home Address that we need
+ * to free?
+ */
+ if (mnEntry && locallyAssignedAddress == _B_TRUE) {
+ if (freeAddressFromPool(mnEntry->haPoolIdentifier,
+ mnEntry->haMnAddr) != _B_TRUE) {
+ syslog(LOG_ERR, "Unable to free address "
+ "from pool %d",
+ mnEntry->haPoolIdentifier);
+ }
+ mnEntry->haMnAddr = INADDR_ANY;
+ }
+ }
+
+#ifdef FIREWALL_SUPPORT
+ /*
+ * If needed, set up temporary tunnel to redirect packets for
+ * external COAs at FW.
+ * TODO: Note that this works only when the destination of the
+ * registration request is the COAddr. This is the case
+ * for an MN operating in colocated state so we are fine
+ * for now.
+ */
+ if (encapToFw) {
+ mipverbose(("At %s, creating a temporary tunnel for %s through "
+ "firewall %s 0\n", ntoa(haAddr, addrstr1),
+ ntoa(COAddr, addrstr2),
+ ntoa(domainInfo.fwAddr[0], addrstr3)));
+ if ((val = encapadd(COAddr, haAddr, domainInfo.fwAddr[0],
+ tFlags & REG_REVERSE_TUNNEL)) < 0) {
+ syslog(LOG_ERR, "encapadd failed ... %s",
+ err2str(val));
+ }
+ }
+#endif /* FIREWALL_SUPPORT */
+
+ /* If needed, ignore existing tunnel for MN to COA */
+ if (ignoreTunnel) {
+ /* make sure response will be sent locally */
+ mipverbose(("Temporarily deleting ARP entry for %s\n",
+ ntoa(replyPtr->homeAddr, addrstr1)));
+ if ((val = arpdel(replyPtr->homeAddr)) < 0) {
+ syslog(LOG_ERR, "arpdel failed ... %s", err2str(val));
+ }
+
+ mipverbose(("Temporarily suspending tunneling for %s\n",
+ ntoa(replyPtr->homeAddr, addrstr1)));
+ }
+
+ /* If the packet was from AAA, send it back there. */
+ if (messageHdr->pktSource == MIP_PKT_FROM_AAA) {
+ (void) fprintf(stderr, "AAA Message!\n");
+ rc = aaaSendRegistrationReply(messageHdr,
+ repLen, haAddr, homeAddr);
+ } else {
+ /*
+ * AAA isn't in charge - use IPsec SA's we're configured with.
+ */
+ MobilityAgentEntry *mae;
+
+ if ((mae = findMaeFromIp(COAddr, LOCK_READ)) != NULL) {
+ /*
+ * Is there an IPSEC_REPLY_APPLY policy
+ * configured that isn't already?
+ */
+ if (((mae->maIPsecFlags & IPSEC_REPLY_APPLY) == 0) &&
+ (IPSEC_REPLY_ANY(
+ mae->maIPsecSAFlags[IPSEC_APPLY]))) {
+ /* pass it down */
+ char peerAddr[IPv4_ADDR_LEN];
+
+ (void) ntoa(mae->maAddr, peerAddr);
+
+ if (installIPsecPolicy(
+ mae->maIPsecReply[IPSEC_APPLY]) < 0) {
+ /* problems writing the policy */
+ syslog(LOG_CRIT, "Could not install %s "
+ "for [Address %s]: %s",
+ IPSEC_POLICY_STRING(
+ IPSEC_REPLY_APPLY), peerAddr,
+ mae->maIPsecReply[IPSEC_APPLY]);
+
+ /* unlock */
+ (void) rw_unlock(&mae->maNodeLock);
+
+ /* don't send a regreP in the clear! */
+ return;
+ }
+
+ /* set the flag */
+ mae->maIPsecFlags |= IPSEC_REPLY_APPLY;
+ }
+
+ /*
+ * We're sending a reply, *now* this is an FA-peer.
+ * We didn't do this when setting IPsecRequest permit
+ * because there isn't a binding to this guy at init
+ * (recall, as HA we have to be laying-in-wait for
+ * registration requests from FAs we have an SA with).
+ */
+ mae->maPeerFlags |= FA_PEER;
+
+ /* unlock */
+ (void) rw_unlock(&mae->maNodeLock);
+ }
+
+ rc = sendUDPmessage(entry->maIfaceUnicastSock,
+ messageHdr->pkt, repLen, messageHdr->src,
+ messageHdr->srcPort);
+ }
+ if (rc == 0) {
+
+ mipverbose(("\n---- %s (%s) ----\n",
+ sprintTime(currentTime, MAX_TIME_STRING_SIZE),
+ sprintRelativeTime(relativeTime,
+ MAX_TIME_STRING_SIZE)));
+ mipverbose(("HA sent reg reply to %s.%d (Code %d)\n",
+ ntoa(messageHdr->src, addrstr1),
+ messageHdr->srcPort, replyPtr->code));
+ mipverbose((
+ " "
+ "[Lifetime %d sec, ID %0#10x : %0#10x]\n",
+ (uint32_t)ntohs(replyPtr->regLifetime),
+ ntohl(replyPtr->IDHigh),
+ ntohl(replyPtr->IDLow)));
+
+ if (logVerbosity > 2) {
+ mipverbose(("HA's reply is:\n"));
+ printBuffer((unsigned char *) messageHdr->pkt,
+ repLen);
+ mipverbose(("\n"));
+ }
+
+ if (replyPtr->regLifetime != 0)
+ haCounters.haRegRepliesSentCnt++;
+ else
+ haCounters.haDeRegRepliesSentCnt++;
+
+ } else {
+ syslog(LOG_ERR, "sendto failed at HA while replying.");
+ }
+
+ /*
+ * Undo any temporary changes related to "deregistration all"
+ * messages.
+ * Proxy arp works only for non-PPP interfaces.
+ */
+ if (((entry->maIfaceFlags & IFF_POINTOPOINT) == 0) && ignoreTunnel) {
+ /* restore prior state */
+ mipverbose(("Re-enabling tunneling for %s\n",
+ ntoa(replyPtr->homeAddr, addrstr1)));
+ mipverbose(("Restoring proxy ARP for %s at %s\n",
+ ntoa(replyPtr->homeAddr, addrstr1),
+ hwAddrWrite(entry->maIfaceHWaddr, addrstr2)));
+ if ((val = arpadd(replyPtr->homeAddr,
+ entry->maIfaceHWaddr, ATF_PUBL)) < 0) {
+ syslog(LOG_ERR, "arpadd (proxy) failed ... %s",
+ err2str(val));
+ }
+ }
+
+ /*
+ * If we successfully deregistered all bindings for a MN
+ * we MUST also send out a gratuitous ARP with the MN's correct
+ * mapping taken from our ARP cache.
+ */
+ if ((replyPtr->code == MIP_SUCCESSFUL_REGISTRATION) ||
+ (replyPtr->code == MIP_SIMULTANEOUS_NOT_SUPPORTED)) {
+ /*
+ * A successful request, we must generate the
+ * appropriate accounting record.
+ */
+ if (replyPtr->regLifetime == 0) {
+ if (((entry->maIfaceFlags & IFF_POINTOPOINT) == 0) &&
+ (mnEntry != NULL) &&
+ (mnEntry->haMnBindingCnt == 0)) {
+ if ((val = arprefresh(mnEntry,
+ replyPtr->homeAddr)) < 0)
+ syslog(LOG_ERR,
+ "arprefresh failed ... %s",
+ err2str(val));
+ }
+
+ if (aaaProtocol != AAA_NONE) {
+ /*
+ * An accounting stop record must be sent.
+ */
+ result = sendAccountingRecord(
+ MOBILE_IP_ACCOUNTING_STOP_REQUEST,
+ (unsigned char *)NAIBuffer,
+ messageHdr->mnNAILen,
+ homeAddr, COAddr, haAddr,
+ sessionLifeTime, MN_DEREGISTERED);
+
+ if (result) {
+ syslog(LOG_ERR, "Unable to "
+ "send accounting "
+ "stop record");
+ }
+ }
+ } else {
+ /*
+ * Is this a new session, or an existing one?
+ */
+ if (aaaProtocol != AAA_NONE) {
+ if (existingBindings == _B_TRUE) {
+ /*
+ * An interim accounting
+ * record must be sent.
+ */
+ result =
+ sendAccountingRecord(
+ MOBILE_IP_ACCOUNTING_INTERIM_REQUEST,
+ (unsigned char *)
+ NAIBuffer,
+ messageHdr->mnNAILen,
+ homeAddr, COAddr,
+ haAddr,
+ sessionLifeTime, 0);
+
+ if (result) {
+ /*
+ * PRC: Here we must disconnect
+ * the mobile node since we can
+ * not account for services
+ * rendered.
+ */
+ syslog(LOG_ERR,
+ "Unable to send "
+ "accounting interim "
+ "record");
+ if (mnEntry) {
+ delHABE(&mnEntry,
+ homeAddr,
+ COAddr,
+ &sessionLifeTime);
+ }
+ }
+ } else {
+ /*
+ * An accounting start record must
+ * be sent.
+ */
+ result =
+ sendAccountingRecord(
+ MOBILE_IP_ACCOUNTING_START_REQUEST,
+ (unsigned char *)NAIBuffer,
+ messageHdr->mnNAILen,
+ homeAddr, COAddr, haAddr, 0, 0);
+
+ if (result) {
+ /*
+ * PRC: Here we must disconnect
+ * the mobile node since we can
+ * not account for services
+ * rendered.
+ */
+ syslog(LOG_ERR,
+ "Unable to send "
+ "accounting start "
+ "record");
+ if (mnEntry) {
+ delHABE(&mnEntry,
+ homeAddr,
+ COAddr,
+ &sessionLifeTime);
+ }
+ }
+ }
+ }
+ }
+ } else if (aaaProtocol != AAA_NONE) {
+ /*
+ * An accounting stop record must be sent to log the
+ * failed request.
+ */
+ result = sendAccountingRecord(
+ MOBILE_IP_ACCOUNTING_STOP_REQUEST,
+ (unsigned char *)NAIBuffer,
+ messageHdr->mnNAILen, homeAddr, COAddr, haAddr,
+ sessionLifeTime, REG_EXPIRED);
+
+ if (result) {
+ syslog(LOG_ERR, "Unable to send accounting stop record");
+ }
+ }
+
+ /*
+ * If we've made it this far, if we have a mobile node
+ * entry, we need to unlock it.
+ */
+ if (mnEntry) {
+ (void) rw_unlock(&mnEntry->haMnNodeLock);
+ }
+
+ mipverbose(("\n\n"));
+}
+
+
+
+/*
+ * Function: acceptFAVEHashLookup
+ *
+ * Arguments: entry - Pointer to visitor entry
+ * p1 - First parameter to match (interface address)
+ * p2 - 2nd parameter to match (whether visitor is accepted)
+ * p3 - 3rd parameter to match (homeagentaddr)
+ *
+ * Description: This function is used as the Hash Table Helper routine
+ * for isAcceptedVisitor() when looking for accepted visitor
+ * entries in the Hash Table, and will be called by
+ * findHashTableEntryUint() and findHashTableEntryString().
+ *
+ * Returns: _B_TRUE if the entry matches the desired criteria,
+ * otherwise _B_FALSE.
+ */
+/* ARGSUSED */
+static boolean_t
+acceptFAVEHashLookup(void *entry, uint32_t p1, uint32_t p2, uint32_t p3)
+{
+ FaVisitorEntry *faveEntry = entry;
+
+ if ((faveEntry->faVisitorCOAddr == p1 || p1 == 0) &&
+ ((uint32_t)faveEntry->faVisitorRegIsAccepted == p2) &&
+ (p3 == 0 || faveEntry->faVisitorHomeAgentAddr == p3)) {
+ return (_B_TRUE);
+ }
+
+ return (_B_FALSE);
+}
+
+/*
+ * Function: acceptFAVE
+ *
+ * Arguments: htbl - Pointer to the Hash Table
+ * replyPtr - Pointer to the registration reply
+ * favep - Pointer to a pointer to a visitor entry
+ *
+ * Description: If we find an ACCEPTED visitor entry for this MN-COA
+ * pair, update that entry and delete the pending entry.
+ * Otherwise, change the "pending" entry pointed to by
+ * favep to "accepted" with specified lifetime.
+ *
+ * If accepted, we will add a host specific route to be
+ * able to forward packets to the Mobile Node and we
+ * will create a tunnel interface for the Home Agent.
+ *
+ * NOTE: COA is available in the faVisitorCOAddr field.
+ *
+ * Returns:
+ */
+static void
+acceptFAVE(HashTable *htbl, regReply *replyPtr, FaVisitorEntry **favep)
+{
+ int val;
+ int in_Ifindex = 0;
+ int out_Ifindex = 0;
+ int tun_num;
+ FaVisitorEntry *entry;
+ char addrstr1[INET_ADDRSTRLEN];
+ char addrstr2[INET_ADDRSTRLEN];
+ char tun_name[LIFNAMSIZ];
+ ipaddr_t mnAddr = replyPtr->homeAddr;
+ unsigned short lifetime = ntohs(replyPtr->regLifetime);
+
+ /*
+ * Let's see if we can find the entry using the Home
+ * Address. Note that an accepted visitor entry MUST have
+ * a home address, so we do not need to worry about looking
+ * using the NAI.
+ */
+ entry = findHashTableEntryUint(htbl, mnAddr, LOCK_WRITE,
+ acceptFAVEHashLookup, (*favep)->faVisitorCOAddr,
+ _B_TRUE, (*favep)->faVisitorHomeAgentAddr);
+
+ if (entry) {
+ /*
+ * We've found a Visitor Entry already accepted in the
+ * Hash Table. We will update the old entry, and delete
+ * this new entry (since we only need one).
+ */
+ entry->faVisitorAddr = (*favep)->faVisitorAddr;
+ entry->faVisitorIfaceAddr = (*favep)->faVisitorIfaceAddr;
+ entry->faVisitorRegFlags = (*favep)->faVisitorRegFlags;
+ entry->faVisitorPort = (*favep)->faVisitorPort;
+ entry->faVisitorHomeAgentAddr =
+ (*favep)->faVisitorHomeAgentAddr;
+ GET_TIME(entry->faVisitorTimeGranted);
+ entry->faVisitorTimeExpires =
+ entry->faVisitorTimeGranted + lifetime;
+ mipverbose(("FA renewed visitor %s on iface %s (%d sec).\n",
+ ntoa(entry->faVisitorHomeAddr, addrstr1),
+ ntoa(entry->faVisitorIfaceAddr, addrstr2),
+ lifetime));
+ entry->faVisitorRegIDHigh = (*favep)->faVisitorRegIDHigh;
+ entry->faVisitorRegIDLow = (*favep)->faVisitorRegIDLow;
+ entry->faVisitorInIfindex = (*favep)->faVisitorInIfindex;
+ entry->faVisitorIsSllaValid = (*favep)->faVisitorIsSllaValid;
+ (void) memcpy(&entry->faVisitorSlla, &(*favep)->faVisitorSlla,
+ sizeof (struct sockaddr_dl));
+
+
+ entry->faVisitorChallengeAdvLen =
+ (*favep)->faVisitorChallengeAdvLen;
+ (void) memcpy(&entry->faVisitorChallengeAdv,
+ &(*favep)->faVisitorChallengeAdv,
+ entry->faVisitorChallengeAdvLen);
+
+ /*
+ * Unlocking the entry here could cause deadlock.
+ * (void) rw_unlock(&entry->faVisitorNodeLock);
+ */
+
+ (void) rw_unlock(&(*favep)->faVisitorNodeLock);
+
+ if (delHashTableEntryUint(htbl, *favep, mnAddr, LOCK_NONE)) {
+ /*
+ * Found a match, delete it
+ */
+ delFAVEptr(*favep, _B_TRUE, 0);
+ (void) rw_unlock(&(*favep)->faVisitorNodeLock);
+ (void) rwlock_destroy(&(*favep)->faVisitorNodeLock);
+ free(*favep);
+ *favep = NULL;
+ }
+ /*
+ * Return the pointer to the old entry.
+ */
+ *favep = entry;
+ return;
+ }
+
+ /*
+ * OK, we did not find an existing entry. If this entry was
+ * found using the NAI, we need to update the hash table to
+ * use the Mobile Node's Home Address instead.
+ */
+ if ((*favep)->faVisitorMnNAI[0] != '\0' &&
+ (*favep)->faVisitorHomeAddr == 0) {
+ /*
+ * If our Visitor Entry has been hashed using the Mobile Node's
+ * NAI, we want to change it so we can hash it using the Home
+ * Address. From now on we will be using the Home Agent to
+ * find the Visitor Entry.
+ *
+ * This *was* computing strlen(fanai) +1, which added space
+ * for the null. Since it was not up to spec, and would not
+ * interoperate with other vendors, it was removed.
+ */
+ if (changeHashEntryStringToUint(htbl, *favep,
+ (*favep)->faVisitorMnNAI, (*favep)->faVisitorMnNAILen,
+ mnAddr) == _B_FALSE) {
+ mipverbose(("Could not find our visitor entry in the " \
+ "hash table\n"));
+ return;
+ }
+ /*
+ * Update the visitor entry with the Mobile Node's
+ * Home Address, and add it to the Hash Table.
+ */
+ (*favep)->faVisitorHomeAddr = mnAddr;
+ mipverbose(("Moved pending visitor entry for %.*s to %s " \
+ "at pos'n %p.\n",
+ (*favep)->faVisitorMnNAILen, (*favep)->faVisitorMnNAI,
+ ntoa(mnAddr, addrstr1), (void *)entry));
+ }
+
+
+ /*
+ * We did not find an existing ACCEPTED entry.
+ */
+ GET_TIME((*favep)->faVisitorTimeGranted);
+ (*favep)->faVisitorTimeExpires =
+ (*favep)->faVisitorTimeGranted + lifetime;
+ mipverbose(("FA accepted visitor %s on iface %s (expires %ld).\n",
+ ntoa((*favep)->faVisitorHomeAddr, addrstr1),
+ ntoa((*favep)->faVisitorIfaceAddr, addrstr2),
+ (*favep)->faVisitorTimeExpires));
+ (*favep)->faVisitorRegIsAccepted = _B_TRUE;
+
+ /*
+ * If the source link layer address is valid add an ARP
+ * entry else don't. The entry could be invalid for variety
+ * of reasons (See recvNetworkPacket)
+ */
+ if ((*favep)->faVisitorIsSllaValid) {
+ /*
+ * Add an ARP entry to prevent the FA from broadcast ARPing
+ */
+ if ((val = arpIfadd(mnAddr, (*favep)->faVisitorSlla.sdl_data,
+ (*favep)->faVisitorInIfindex)) < 0) {
+ syslog(LOG_ERR, "SIOCSXARP failed... %s",
+ err2str(val));
+ }
+ }
+
+ mipverbose(("Enabling decapsulation of inner pkts sent to %s\n",
+ ntoa((*favep)->faVisitorHomeAddr, addrstr1)));
+
+ if ((val = decapadd((*favep)->faVisitorHomeAgentAddr,
+ (*favep)->faVisitorCOAddr)) < 0) {
+ syslog(LOG_ERR, "decapadd failed ... %s", err2str(val));
+ /*
+ * The following reply code is a close approximation of why
+ * things might have failed. The main purpose is to let the
+ * MN know.
+ */
+ replyPtr->code = FA_INSUFFICIENT_RESOURCES;
+ return;
+ }
+
+ tun_num = gettunnelno((*favep)->faVisitorHomeAgentAddr,
+ (*favep)->faVisitorCOAddr);
+ if (tun_num < 0) {
+ syslog(LOG_ERR, "gettunnelno returns -1");
+ /*
+ * The following reply code is a close approximation of why
+ * things might have failed. The main purpose is to let the MN
+ * know.
+ */
+ replyPtr->code = FA_INSUFFICIENT_RESOURCES;
+ return;
+ }
+ (void) snprintf(tun_name, sizeof (tun_name), "ip.tun%d", tun_num);
+ in_Ifindex = if_nametoindex(tun_name);
+ if (in_Ifindex == 0) {
+ /* if_nametoindex fails... */
+ syslog(LOG_ERR, "if_nametoindex fails for tunnel %s"
+ "with error %d", tun_name, errno);
+ /*
+ * The following reply code is a close approximation of why
+ * things might have failed. The main purpose is to let the MN
+ * know.
+ */
+ replyPtr->code = FA_INSUFFICIENT_RESOURCES;
+ return;
+ }
+ /*
+ * Create forward route to MN and specify that only
+ * packets from in_Ifindex will be forwarded to MN.
+ */
+
+ mipverbose(("Adding direct, local route for visitor %s through %s.\n",
+ ntoa((*favep)->faVisitorHomeAddr, addrstr1),
+ ntoa((*favep)->faVisitorIfaceAddr, addrstr2)));
+
+ if ((val = routeadd((*favep)->faVisitorHomeAddr,
+ (*favep)->faVisitorIfaceAddr, 0, in_Ifindex,
+ (*favep)->faVisitorInIfindex)) < 0) {
+ syslog(LOG_ERR, "routeadd failed ... :%s: "
+ "for visitor %s from interface index %d",
+ err2str(val), ntoa((*favep)->faVisitorHomeAddr, addrstr1),
+ in_Ifindex);
+ /*
+ * The following reply code is a close approximation of why
+ * things might have failed. The main purpose is to let the MN
+ * know.
+ */
+ replyPtr->code = FA_INSUFFICIENT_RESOURCES;
+ return;
+ }
+
+ /*
+ * If 'T' bit is set, create reverse tunnel route in the MIPRTUN
+ * table. In this routing table the routing selection is based
+ * on visitor's homeaddr and it's incoming interface index.
+ * Outgoing interface index provided in the routeadd function
+ * determines reverse tunnel to visitor's home-agent.
+ */
+ if ((*favep)->faVisitorRegFlags & REG_REVERSE_TUNNEL) {
+
+ /* Do we apply an IPsec policy for the reverse tunnel? */
+ MobilityAgentEntry *mae;
+
+ if ((mae = findMaeFromIp(replyPtr->haAddr, LOCK_READ))
+ != NULL) {
+ /* is there something we shoud set that isn't? */
+ if (IPSEC_REVERSE_TUNNEL_ANY(
+ mae->maIPsecSAFlags[IPSEC_APPLY]))
+ /*
+ * forward and reverse tunnels share a policy
+ * (socket), so we can't support asymmetric
+ * tunnel policies until ipsec supports
+ * multiple socket policies! If we install a
+ * global reverse tunnel policy, it will get
+ * processed before we pass it through our
+ * route table, which will indicate it's to go
+ * into the tunnel, but then it'll get dropped
+ * by the forward tunnel policy (presuming it's
+ * a different policy). Just set the reverse
+ * bit flag to indicate what's being applied.
+ */
+ mae->maIPsecFlags |=
+ IPSEC_REVERSE_TUNNEL_APPLY;
+
+ /* unlock */
+ (void) rw_unlock(&mae->maNodeLock);
+ }
+
+ /* Add reverse tunnel route for MIPRTUN table */
+ mipverbose(("Adding reverse tunnel route for visitor %s at"
+ "interface index %d to tunnel index %d\n",
+ ntoa((*favep)->faVisitorHomeAddr, addrstr1),
+ (*favep)->faVisitorInIfindex, in_Ifindex));
+
+ out_Ifindex = in_Ifindex;
+ if ((val = routeadd(0, 0,
+ (*favep)->faVisitorHomeAddr,
+ (*favep)->faVisitorInIfindex,
+ out_Ifindex)) < 0) {
+ syslog(LOG_ERR, "Reverse Tunnel route-add failed:%s:"
+ " for visitor %s from interface index %d to %d",
+ ntoa((*favep)->faVisitorHomeAddr, addrstr1),
+ (*favep)->faVisitorInIfindex, out_Ifindex,
+ err2str(val));
+ /*
+ * The following reply code is a close approximation
+ * of why things might have failed. The main purpose
+ * is to let the MN know.
+ */
+ replyPtr->code = FA_INSUFFICIENT_RESOURCES;
+ }
+ }
+}
+
+
+/*
+ * Function: FAprocessRegReply
+ *
+ * Arguments: messageHdr - Pointer to the Message Control Block
+ * entry - Pointer to the Interface Entry.
+ *
+ * Description: Process a registration reply received at a foreign
+ * agent, and forward the reply to the Mobile Node.
+ *
+ * If AAA was enabled, we will issue accounting
+ * records to the AAA.
+ *
+ * Returns:
+ */
+void
+FAprocessRegReply(MessageHdr *messageHdr, MaAdvConfigEntry *entry)
+{
+ int code = MIP_SUCCESSFUL_REGISTRATION;
+ int val;
+ int index;
+ int result;
+ uint32_t challengeBuffer[4];
+ /* LINTED E_FUNC_SET_NOT_USED */
+ authExt *mnAuthExt;
+ int mnAuthExtLen;
+ uint32_t sessionLifeTime = 0;
+ ipaddr_t COAddr;
+ time_t localTime;
+ boolean_t visitor_entryExists;
+ regReply *replyPtr;
+ FaVisitorEntry *favePtr;
+ FaVisitorEntry *acceptedFAVE;
+ MipSecAssocEntry *mipSecAssocEntry = NULL;
+ HashTable *htbl = &faVisitorHash;
+ char addrstr1[INET_ADDRSTRLEN];
+ char addrstr2[INET_ADDRSTRLEN];
+ char currentTime[MAX_TIME_STRING_SIZE];
+ char relativeTime[MAX_TIME_STRING_SIZE];
+ char NAIBuffer[ADV_MAX_NAI_LENGTH];
+ struct ether_addr ether;
+
+ /* LINTED BAD_PTR_CAST_ALIGN */
+ replyPtr = (regReply *) messageHdr->pkt;
+
+ mipverbose(("\n---- %s (%s) ----\n",
+ sprintTime(currentTime, MAX_TIME_STRING_SIZE),
+ sprintRelativeTime(relativeTime, MAX_TIME_STRING_SIZE)));
+ mipverbose(("FA got reg reply [MN %s, HA %s, Code %d]\n",
+ ntoa(replyPtr->homeAddr, addrstr1),
+ ntoa(replyPtr->haAddr, addrstr2),
+ replyPtr->code));
+ mipverbose((" [Lifetime %d sec, ID %0#10x : %0#10x]\n",
+ (uint32_t)ntohs(replyPtr->regLifetime), ntohl(replyPtr->IDHigh),
+ ntohl(replyPtr->IDLow)));
+
+ mipverbose(("FAprocessRegReply called for pkt:\n"));
+ if (logVerbosity > 2)
+ printBuffer(messageHdr->pkt, messageHdr->pktLen);
+ mipverbose(("\n"));
+
+ /*
+ * Support for new MIER-style extension header.
+ *
+ * Are extensions ok and in the right order?
+ */
+ if (mkRegExtList(messageHdr, sizeof (regReply)) < 0) {
+ mipverbose(("FAprocessRegReply: poorly formed reply\n"));
+ code = FA_POORLY_FORMED_REQUEST;
+ faCounters.faPoorlyFormedRequestsCnt++;
+ /*
+ * We no longer return here, instead we
+ * continue, and we will end up returning a failed
+ * reply to the mobile node.
+ */
+
+ }
+
+
+ /*
+ * Packet parsing routines now return error codes.
+ *
+ * Is the packet from the Home Agent valid?
+ */
+ if ((code = IsPacketFromHaValid(messageHdr)) > 0) {
+ faCounters.faPoorlyFormedRequestsCnt++;
+ /*
+ * We no longer return here, instead we
+ * continue, and we will end up returning a failed
+ * reply to the mobile node.
+ */
+ } else if (code == MA_DROP_PACKET) {
+ /* drop the packet */
+ return;
+ }
+
+ /*
+ * Now we retrieve the pending Visitor Entry. Note that the
+ * entry will be write locked upon return, and it is our
+ * responsibility to unlock it before we return.
+ */
+ favePtr = findPendingFAVE(&faVisitorHash, replyPtr->homeAddr,
+ messageHdr->mnNAI, messageHdr->mnNAILen, ntohl(replyPtr->IDLow));
+
+ if (favePtr == NULL) {
+ /* we wouldn't likewise find a matching ipsec SA anyway. */
+ syslog(LOG_ERR, "Did not find matching pending request.");
+ return;
+ }
+
+ /*
+ * If the code is set to poor request, we will
+ * return a failed reply to the mobile node
+ */
+ if (code == FA_POORLY_FORMED_REQUEST) {
+ goto reply;
+ }
+
+ /*
+ * If we have an NAI, save it.
+ */
+ if (messageHdr->mnNAILen) {
+ (void) memcpy(NAIBuffer, messageHdr->mnNAI,
+ messageHdr->mnNAILen);
+ }
+
+ /*
+ * Check the message's authentication
+ */
+ code = faCheckRegRepAuth(messageHdr, favePtr);
+
+ if (code) {
+ goto reply;
+ }
+
+ /* ... it is a good reply. No need to change the code in pkt. */
+ faCounters.faRegRepliesRecvdCnt++;
+
+ /*
+ * TODO: Remove everything after the MN-HA auth ext, add any new
+ * ones and relay the request. For now, we simply strip everything
+ * beyond the MN-HA auth.
+ */
+
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ GET_AUTH_EXT(messageHdr, index, REG_MH_AUTH_EXT_TYPE, mnAuthExt,
+ mnAuthExtLen);
+
+ if (mnAuthExtLen) {
+ /*
+ * If any extensions appear following the Mobile-Node
+ * Home Agent Extension, let's remove them.
+ */
+ messageHdr->pktLen = (messageHdr->extIdx[index] -
+ messageHdr->pkt) + messageHdr->extHdrLength[index] +
+ mnAuthExtLen;
+ }
+
+ /*
+ * As per the Challenge/Response Internet Draft, the
+ * Foreign Agent MAY include a new challenge value in the registration
+ * reply, protected by the MN-FA authentication extension (if present).
+ * In our case, if challenge is present, we will always add a new
+ * challenge to the reply.
+ */
+ if (faChallengeAdv == _B_TRUE) {
+ /*
+ * Generate the challenge value.
+ */
+ challengeBuffer[0] = getRandomValue();
+ challengeBuffer[1] = getRandomValue();
+ challengeBuffer[2] = getRandomValue();
+ challengeBuffer[3] = getRandomValue();
+ messageHdr->pktLen += appendExt((messageHdr->pkt +
+ messageHdr->pktLen), REG_MF_CHALLENGE_EXT_TYPE,
+ (unsigned char *)&challengeBuffer, ADV_CHALLENGE_LENGTH);
+
+ (void) memcpy(favePtr->faVisitorChallengeAdv,
+ &challengeBuffer, ADV_CHALLENGE_LENGTH);
+ favePtr->faVisitorChallengeAdvLen = ADV_CHALLENGE_LENGTH;
+ }
+
+ /*
+ * Get our visitor's Security Association, but keep in mind
+ * that the node will be locked upon return.
+ */
+ if ((mipSecAssocEntry =
+ findSecAssocFromSPI(favePtr->faVisitorSPI,
+ LOCK_READ)) == NULL) {
+ /*
+ * TODO: Is this extension required?
+ */
+ if (mfAuthRequired) {
+ syslog(LOG_ERR,
+ "Error: no SA in Visitor Entry");
+ code = FA_MN_AUTH_FAILURE;
+ faCounters.faMNAuthFailureCnt++;
+ goto reply;
+ }
+ } else {
+ messageHdr->pktLen += appendAuthExt(messageHdr->pkt,
+ messageHdr->pktLen, REG_MF_AUTH_EXT_TYPE,
+ mipSecAssocEntry);
+ /*
+ * We need to unlock the node
+ */
+ (void) rw_unlock(&mipSecAssocEntry->mipSecNodeLock);
+ }
+
+ /*
+ * Check the result code in the messageHdr. If it is
+ * non zero, and the registrationRequest result is zero,
+ * then set the registration request's code to the code
+ * in the header.
+ */
+ if (messageHdr->pktSource == MIP_PKT_FROM_AAA ||
+ messageHdr->pktSource == MIP_PKT_FROM_RADIUS &&
+ aaaProtocol != AAA_NONE &&
+ messageHdr->aaaResultCode != 0 &&
+ replyPtr->code == 0 && code == 0) {
+ code = messageHdr->aaaResultCode;
+ }
+
+
+ /* Check for HA returning duplicate homeaddr */
+ acceptedFAVE = findAcceptedFAVE(&faVisitorHash,
+ replyPtr->homeAddr, replyPtr->haAddr);
+ if ((acceptedFAVE != NULL) &&
+ (acceptedFAVE->faVisitorMnNAI[0] != '\0')) {
+ /*
+ * Compare to see that homeaddr and NAI
+ * match and it's not due to some problem
+ * with misbehaving HA assigning duplicate addr
+ */
+ if (strncmp((const char *)acceptedFAVE->faVisitorMnNAI,
+ (const char *)favePtr->faVisitorMnNAI,
+ (size_t)acceptedFAVE->faVisitorMnNAILen)) {
+ if (code == 0) {
+ /* Set to reason unspecified */
+ code = FA_REASON_UNSPECIFIED;
+ faCounters.faReasonUnspecifiedCnt++;
+ }
+ }
+ }
+ if (acceptedFAVE != NULL)
+ (void) rw_unlock(&acceptedFAVE->faVisitorNodeLock);
+
+ /* Check for assigned homeaddr validity */
+ if (replyPtr->homeAddr == INADDR_ANY ||
+ replyPtr->homeAddr == INADDR_LOOPBACK ||
+ replyPtr->homeAddr == INADDR_BROADCAST) {
+ if (code == 0) {
+ /* Set to reason unspecified */
+ code = FA_REASON_UNSPECIFIED;
+ faCounters.faReasonUnspecifiedCnt++;
+ }
+ }
+
+reply:
+ /* Do we need to overwrite the code field? */
+ if (code)
+ replyPtr->code = (uint8_t)code;
+
+ /*
+ * We need to accept the pending entry before sending the reply
+ * to the MN. The reason being: if the acceptance fails (due
+ * to lack of resources for example) then we need to indicate
+ * this to the MN with the appropriate code set in the reply
+ */
+ if (((replyPtr->code == MIP_SUCCESSFUL_REGISTRATION) ||
+ (replyPtr->code == MIP_SIMULTANEOUS_NOT_SUPPORTED)) &&
+ (replyPtr->regLifetime != 0)) {
+ /*
+ * Delete prior accepted entries for this MN and
+ * COA pair and make the "pending" entry "accepted".
+ * The COA is available in the faVisitorCOAddr
+ * field of favePtr.
+ */
+ acceptFAVE(&faVisitorHash, replyPtr, &favePtr);
+ }
+ /*
+ * TODO: Handle extensions properly. For now, we include no
+ * extensions in denials and therefore don't need to change
+ * newLen.
+ */
+
+
+ visitor_entryExists = isAcceptedVisitor(&faVisitorHash,
+ replyPtr->homeAddr, favePtr->faVisitorIfaceAddr);
+
+
+ if (visitor_entryExists == _B_FALSE) {
+
+ /*
+ * If the source link layer address is valid add an ARP
+ * entry else don't. The entry could be invalid for variety
+ * of reasons (See recvNetworkPacket)
+ */
+ if (favePtr->faVisitorIsSllaValid &&
+ (replyPtr->homeAddr != INADDR_ANY)) {
+ /*
+ * Add a temporary ARP entry to prevent the FA from
+ * broadcast ARPing. This entry is deleted when the
+ * MN is removed from the visitors list. Note, the entry
+ * is marked permanent so the FA does not have worry
+ * about ARP blowing the entry away when it refreshes
+ * it's cache.
+ */
+ if ((val = arpIfadd(replyPtr->homeAddr,
+ favePtr->faVisitorSlla.sdl_data,
+ favePtr->faVisitorInIfindex)) < 0) {
+ syslog(LOG_ERR, "SIOCSXARP failed... %s",
+ err2str(val));
+ }
+ }
+ }
+
+ /*
+ * If the request was sent from a mobile node whose home address is not
+ * yet configured i.e 0.0.0.0, then the reply should be sent as an IP
+ * level broadcast but with the mobile nodes link layer address as the
+ * destination L2 i.e link layer unicast. This can be done by opening a
+ * link layer raw socket, constructing the various headers and sending
+ * the packet (cf. RFC 3220 section 3.7.2.3)
+ */
+ if ((replyPtr->homeAddr == INADDR_ANY) &&
+ favePtr->faVisitorIsSllaValid) {
+ if (sendRawPkt(messageHdr, entry, messageHdr->pktLen) == 0) {
+ mipverbose(("\n---- %s (%s) ----\n",
+ sprintTime(currentTime,
+ MAX_TIME_STRING_SIZE),
+ sprintRelativeTime(relativeTime,
+ MAX_TIME_STRING_SIZE)));
+ (void) memcpy(ether.ether_addr_octet,
+ favePtr->faVisitorSlla.sdl_data, ETHERADDRL);
+ mipverbose(("FA relayed reg reply to "
+ "255.255.255.255.%d for MN 0.0.0.0 "
+ "[MAC: %s] (Code %d)\n",
+ favePtr->faVisitorPort,
+ ether_ntoa(&ether),
+ replyPtr->code));
+ mipverbose(("FA relayed reply packet:\n"));
+ if (logVerbosity > 2)
+ printBuffer(messageHdr->pkt,
+ messageHdr->pktLen);
+ mipverbose(("\n"));
+ return;
+ } else {
+ mipverbose(("FAprocessRegReply: raw send failed at FA"
+ " to relay reply.\n"));
+ return;
+ }
+ }
+
+ /*
+ * Set socket option IP_XMIT_IF to get the registration reply
+ * unicast to the mobile node...
+ */
+ val = favePtr->faVisitorInIfindex;
+ if (setsockopt(entry->maIfaceUnicastSock, IPPROTO_IP, IP_XMIT_IF,
+ &val, sizeof (val)) < 0) {
+ /* There's a problem... */
+ syslog(LOG_ERR, "Can't set IP_XMIT_IF socket option for "
+ "registration reply to mobile node %s.",
+ ntoa(messageHdr->src, addrstr1));
+ }
+
+ if (sendUDPmessage(entry->maIfaceUnicastSock, messageHdr->pkt,
+ messageHdr->pktLen, favePtr->faVisitorHomeAddr,
+ favePtr->faVisitorPort) == 0) {
+
+ mipverbose(("\n---- %s (%s) ----\n",
+ sprintTime(currentTime, MAX_TIME_STRING_SIZE),
+ sprintRelativeTime(relativeTime, MAX_TIME_STRING_SIZE)));
+ mipverbose(("FA relayed reg reply to %s.%d (Code %d)\n",
+ ntoa(favePtr->faVisitorHomeAddr, addrstr1),
+ favePtr->faVisitorPort, replyPtr->code));
+ faCounters.faRegRepliesRelayedCnt++;
+
+ mipverbose(("FA relayed reply packet:\n"));
+ if (logVerbosity > 2)
+ printBuffer(messageHdr->pkt, messageHdr->pktLen);
+ mipverbose(("\n"));
+
+ } else {
+ syslog(LOG_ERR, "sendto failed at FA while relaying reply.");
+ }
+
+ /* Reset IP_XMIT_IF option on socket */
+ val = 0;
+ if (setsockopt(entry->maIfaceUnicastSock, IPPROTO_IP, IP_XMIT_IF,
+ &val, sizeof (val)) < 0) {
+ syslog(LOG_ERR, "Can't unset socket option IP_XMIT_IF"
+ "which was set for interface index %d",
+ favePtr->faVisitorInIfindex);
+ }
+ if (visitor_entryExists == _B_FALSE) {
+
+ if (favePtr->faVisitorIsSllaValid) {
+ /*
+ * Delete the temporary ARP entry
+ */
+ if ((val = arpIfdel(replyPtr->homeAddr,
+ favePtr->faVisitorSlla.sdl_data,
+ favePtr->faVisitorInIfindex)) < 0) {
+ /*
+ * If the deletion failed bcos there was no
+ * entry then we don't need to report it
+ */
+ if (val != (-1)*ENXIO) {
+ syslog(LOG_ERR,
+ "SIOCDXARP failed... %s",
+ err2str(val));
+ }
+ }
+ }
+ }
+
+ if ((replyPtr->code == MIP_SUCCESSFUL_REGISTRATION) ||
+ (replyPtr->code == MIP_SIMULTANEOUS_NOT_SUPPORTED)) {
+ if (replyPtr->regLifetime == 0) {
+ COAddr = favePtr->faVisitorCOAddr;
+ GET_TIME(localTime);
+ sessionLifeTime = localTime -
+ favePtr->faVisitorTimeGranted;
+ /*
+ * Deregistration ... delete visitor entries for
+ * this MN
+ */
+ delFAVE(&faVisitorHash, &favePtr, replyPtr->homeAddr,
+ MN_DEREGISTERED);
+
+ if (aaaProtocol != AAA_NONE) {
+ /*
+ * An accounting stop record must be sent.
+ */
+ result = sendAccountingRecord(
+ MOBILE_IP_ACCOUNTING_STOP_REQUEST,
+ (unsigned char *)NAIBuffer,
+ messageHdr->mnNAILen,
+ replyPtr->homeAddr, COAddr,
+ replyPtr->haAddr,
+ sessionLifeTime, MN_DEREGISTERED);
+
+ if (result) {
+ syslog(LOG_ERR, "Unable to send accounting "
+ "stop record");
+ }
+ }
+ } else {
+
+ if (visitor_entryExists == _B_FALSE &&
+ aaaProtocol != AAA_NONE) {
+ /*
+ * An accounting start record must be sent.
+ */
+ result = sendAccountingRecord(
+ MOBILE_IP_ACCOUNTING_START_REQUEST,
+ (unsigned char *)NAIBuffer,
+ messageHdr->mnNAILen,
+ replyPtr->homeAddr,
+ favePtr->faVisitorCOAddr,
+ replyPtr->haAddr, 0, 0);
+
+ if (result) {
+ /*
+ * PRC: Here we must disconnect the
+ * mobile node since we cannot bill
+ * for services rendered, but I am
+ * not sure how this can be done.
+ */
+ syslog(LOG_ERR,
+ "Unable to send accounting "
+ "start record");
+ }
+ } else if (aaaProtocol != AAA_NONE) {
+ /*
+ * An accounting start record must be sent.
+ */
+ GET_TIME(localTime);
+ result =
+ sendAccountingRecord(
+ MOBILE_IP_ACCOUNTING_INTERIM_REQUEST,
+ (unsigned char *)NAIBuffer,
+ messageHdr->mnNAILen,
+ replyPtr->homeAddr,
+ (favePtr) ?
+ favePtr->faVisitorCOAddr : 0,
+ replyPtr->haAddr,
+ localTime -
+ ((favePtr) ?
+ favePtr->faVisitorTimeGranted:
+ 0), 0);
+
+ if (result) {
+ /*
+ * PRC: Here we must disconnect the
+ * mobile node since we cannot bill
+ * for services rendered, but I am
+ * not sure how this can be done.
+ */
+ syslog(LOG_ERR, "Unable to send "
+ "accounting interim record");
+ }
+ }
+ }
+ } else {
+ /*
+ * For denials, simply delete pending entry unless it
+ * corresponds to an HA discovery request; In that case
+ * let the periodic timer delete the request.
+ * TODO: It is better to look at the HA field in the
+ * request but since we don't have the MN's home
+ * netmask, we rely on the returned code.
+ */
+ if (replyPtr->code != HA_UNKNOWN_HOME_AGENT) {
+ COAddr = favePtr->faVisitorCOAddr;
+ if (delHashTableEntryUint(htbl, favePtr,
+ replyPtr->homeAddr, LOCK_NONE)) {
+ /*
+ * Found a match, delete it
+ */
+ delFAVEptr(favePtr, _B_TRUE, 0);
+ (void) rw_unlock(&favePtr->faVisitorNodeLock);
+ (void) rwlock_destroy(
+ &favePtr->faVisitorNodeLock);
+ free(favePtr);
+ favePtr = NULL;
+ }
+
+ if (aaaProtocol != AAA_NONE) {
+ /*
+ * An accounting stop record must be sent
+ * to log a failed request.
+ */
+ result = sendAccountingRecord(
+ MOBILE_IP_ACCOUNTING_STOP_REQUEST,
+ (unsigned char *)NAIBuffer,
+ messageHdr->mnNAILen,
+ replyPtr->homeAddr, COAddr,
+ replyPtr->haAddr, 0, 0);
+
+ if (result) {
+ syslog(LOG_ERR, "Unable to send accounting "
+ "stop record");
+ }
+ }
+ }
+ }
+
+ if (favePtr) {
+ (void) rw_unlock(&favePtr->faVisitorNodeLock);
+ }
+
+ mipverbose(("\n\n"));
+}
+
+#ifdef RADIUS_ENABLED
+void
+loadRadiusLibrary()
+{
+ void *handle;
+ int result;
+ /*
+ * Open the dynamic library
+ */
+ handle = dlopen(radiusSharedLibrary, RTLD_LAZY|RTLD_GLOBAL);
+ if (handle == NULL) {
+ (void) fprintf(stderr,
+ "Unable to open dynamic library %s (%s)\n",
+ radiusSharedLibrary, dlerror());
+ radiusEnabled = 0;
+ return;
+ }
+
+ /*
+ * Setup our function pointers.
+ */
+ radInitializeApi = (int (*)())dlsym(handle, "InitializeAPI");
+ if (radInitializeApi == NULL) {
+ (void) fprintf(stderr,
+ "Unable to resolve initialization function %s (%s)\n",
+ radiusSharedLibrary, dlerror());
+ radiusEnabled = 0;
+ dlclose(handle);
+ return;
+ }
+ radLookupData = (int (*)(char **, char *, RadData *)) dlsym(handle,
+ "LookupData");
+ if (radLookupData == NULL) {
+ (void) fprintf(stderr,
+ "Unable to resolve lookup function %s (%s)\n",
+ radiusSharedLibrary, dlerror());
+ radiusEnabled = 0;
+ dlclose(handle);
+ return;
+ }
+ radCloseSession = (int (*)(char *, char *, void *)) dlsym(handle,
+ "CloseSession");
+ if (radLookupData == NULL) {
+ (void) fprintf(stderr, "Unable to resolve close function %s (%s)\n",
+ radiusSharedLibrary, dlerror());
+ radiusEnabled = 0;
+ dlclose(handle);
+ return;
+ }
+
+ /*
+ * Call the module's initialization function.
+ */
+ result = radInitializeApi();
+ if (result) {
+ (void) fprintf(stderr, "Error initializing dynamic library %s",
+ radiusSharedLibrary);
+ radiusEnabled = 0;
+ dlclose(handle);
+ return;
+ }
+
+ dlclose(handle);
+} /* loadRadiusLibrary */
+#endif /* RADIUS_ENABLED */
+
+
+/*
+ * Function: formIPsecBits(int type, char *nodeID, char *ipsecPolicy_p,
+ * char *Filename)
+ *
+ * Arguments: type - type of IPsec SA we want to install.
+ * nodeID - nodeID this policy is for (dotted ipAddr).
+ * ipsecPolicy_p - a pointer to the policy string. The format's
+ * identical to ipsec's "<action> {properties}"
+ * as set, and parsed, in mipagent.conf.
+ * ipsecPolicy - the storage for the FULL policy, that is
+ * "{<pattern>} action {<properties>}".
+ * ipsecPolicySize - size of the ipsecPolicy buffer
+ * Description: This function builds the complete IPsec Policy for install and
+ * remove functions to ensure consistency between them.
+ * Note: this is the current functional solution until IPsec
+ * supports multiple per-socket policies, or provides an API.
+ *
+ * Returns: -1 if bad pointers were passed, or if their was some other
+ * problem building things. 0 on success, in which case
+ * ipsecPolicy is presumed to contain a good ipsec policy.
+ *
+ * Note: this function will be unnecessary when ipsec has an API.
+ */
+int
+formIPsecBits(int type, char *nodeID, char *ipsecPolicy_p, char *ipsecPolicy,
+ size_t ipsecPolicySize)
+{
+ /* a quick sanity check */
+ if ((nodeID == NULL) || (ipsecPolicy_p == NULL) ||
+ (ipsecPolicy == NULL))
+ /* caller's confused */
+ return (-1);
+
+ /* build the complete ipSec policy */
+ switch (type) {
+
+ case IPSEC_REQUEST_APPLY:
+ /* IPsec Policy - FA apply policy for sending regreQ */
+ (void) snprintf(ipsecPolicy, ipsecPolicySize,
+ "{daddr %s ulp udp dport %d} %s\n",
+ nodeID, MIP_PORT, ipsecPolicy_p);
+ break;
+
+ case IPSEC_REQUEST_PERMIT:
+ /* IPsec Policy - HA permit policy for receiving regreQ */
+ (void) snprintf(ipsecPolicy, ipsecPolicySize,
+ "{saddr %s ulp udp dport %d} %s\n",
+ nodeID, MIP_PORT, ipsecPolicy_p);
+ break;
+
+ case IPSEC_REPLY_APPLY:
+ /* IPsec Policy - HA apply policy for sending regreP */
+ (void) snprintf(ipsecPolicy, ipsecPolicySize,
+ "{daddr %s ulp udp sport %d} %s\n",
+ nodeID, MIP_PORT, ipsecPolicy_p);
+ break;
+
+ case IPSEC_REPLY_PERMIT:
+ /* IPsec Policy - FA permit policy for receiving regreQ */
+ (void) snprintf(ipsecPolicy, ipsecPolicySize,
+ "{saddr %s ulp udp dport %d} %s\n",
+ nodeID, MIP_PORT, ipsecPolicy_p);
+ break;
+
+ /*
+ * tunnel policies are passed directly down via the ipsec_req_t structs
+ * in the MobilityAgentEntry struct and ioctl(). Keep these tags here,
+ * though, for debugging.
+ */
+ case IPSEC_TUNNEL_APPLY:
+ case IPSEC_TUNNEL_PERMIT:
+ case IPSEC_REVERSE_TUNNEL_APPLY:
+ case IPSEC_REVERSE_TUNNEL_PERMIT:
+ /* syslog() in case we're actually trying to do this! */
+ syslog(LOG_WARNING,
+ "Attempt to set global policy for tunnels incorrect.");
+ return (-1);
+
+ /* catch all for anything we don't understand! */
+ default:
+ /* we don't know this type */
+ syslog(LOG_WARNING,
+ "Attempt to set global policy for unknown policy type.");
+ return (-1);
+ }
+
+ return (0);
+}
+
+
+
+/*
+ * Function: installIPsecPolicy
+ *
+ * Arguments: char *policy - a pointer to the policy string. The format's
+ * "{pattern} <action> {properties}" as per ipsec.
+ *
+ * Description: This function does what it takes to install the ipsecPolicy of
+ * the type passed in. Right now, that means calling popen() to
+ * get "ipsecconf -(a | r) -" going (note: this is a S9 option
+ * only, '-' is stdin (I suppose I could use /dev/stdin for this,
+ * but it should be symmetric with 'removeIPsecPolicy()', which
+ * also uses an S9-only option, namely -r, see its description)!
+ * In this way we don't need to create a temporary file only to
+ * delete it. Once "ipsecconf -a -" is up, we write the policy
+ * to it, then pclose().
+ *
+ * Returns: -1 if bad pointers were passed, or if their was some other problem
+ * invoking the ipSec policy. 0 on success.
+ */
+int
+installIPsecPolicy(char *policy) {
+ int ret;
+ FILE *fp;
+
+ if (policy == NULL)
+ return (-1);
+
+ if ((fp = popen(IPSEC(ADD_POLICY), "w")) == NULL) {
+ syslog(LOG_CRIT, "Couldn't start ipsec install process.");
+ return (-1);
+ }
+
+ /* send the policy to fp == stdin */
+ (void) fprintf(fp, "%s", policy);
+
+ /* pclose() will flush, and send the EOF */
+ ret = pclose(fp);
+
+ /* if pclose returned 1, there was a problem */
+ if (ret == 1)
+ /* return in defeat */
+ return (-1);
+
+ /* fin */
+ return (0);
+}
+
+
+/*
+ * Function: removeIPsecPolicy
+ *
+ * Arguments: char *policy - a pointer to the policy string. The format is
+ * identical to ipsec's "<action> {properties}"
+ * as set, and parsed, in mipagent.conf.
+ *
+ * Description: This function does what it takes to remove the ipsecPolicy of
+ * the type passed in. Right now, that means calling popen() to
+ * get ipsecconf running, and waiting for the policy to remove,
+ * then passing the policy to have it deleted from IPsec's
+ * pattern table, and finally calling pclose(). Note: ipsecconf's
+ * -r[emove] option is only supported in S9 or later!
+ *
+ * Returns: -1 if bad pointers were passed, or if their was some other problem
+ * removeing the ipSec policy. 0 on success.
+ */
+int
+removeIPsecPolicy(char *policy) {
+ FILE *fp; /* for file manipulation */
+ int ret;
+
+ if (policy == NULL)
+ /* caller's confused... */
+ return (-1);
+
+ /* pass to ipsec */
+ if ((fp = popen(IPSEC(SUB_POLICY), "w")) == NULL)
+ return (-1);
+
+ /* write policy to fp == stdin */
+ (void) fprintf(fp, "%s", policy);
+
+ /* only write one at a time */
+ ret = fclose(fp);
+
+ if (WEXITSTATUS(ret) != 0) {
+ syslog(LOG_CRIT, "Couldn't remove IPsec policy %s.", policy);
+ return (-1);
+ }
+
+ /* fin */
+ return (0);
+
+} /* removeIPsecPolicy */
+
+
+
+/*
+ * Function: main
+ *
+ * Arguments: argc - Number of runtime arguments
+ * argv - Pointer to runtime arguments
+ *
+ * Description: This function is the main agent routine that gets
+ * called upon startup. This function will:
+ * 1. Read the initialization file.
+ * 2. Start the SNMP sub-agent thread.
+ * 3. Start the periodic task Thread.
+ * 4. Start the AAA thread.
+ * 5. Start the message dispatching Thread.
+ *
+ * This thread will then wait for an incoming signal
+ * (INT and TERM), and will call the shutdown procedure
+ * once such a signal is received.
+ *
+ * Returns: exits
+ */
+#ifndef TEST_AAA
+int
+main(int argc, char *argv[])
+{
+ sigset_t thread_signals;
+ int signal = 0;
+ int c;
+ int rc;
+
+ (void) setlocale(LC_ALL, "");
+#if !defined(TEXT_DOMAIN)
+#define TEXT_DOMAIN "SYS_TEST"
+#endif
+ (void) textdomain(TEXT_DOMAIN);
+
+ while ((c = getopt(argc, argv, "d")) != EOF) {
+ switch (c) {
+ case 'd':
+ /* private debugging argument */
+ daemonize = _B_FALSE;
+ break;
+ default:
+ /* mipagent has no public arguments */
+ (void) fprintf(stderr, "Usage: %s\n", *argv);
+ exit(-1);
+ }
+ }
+
+ /*
+ * Read the config file name and create internal data
+ * structures.
+ */
+ if (Initialize(CONF_FILE_NAME)) {
+ syslog(LOG_CRIT, "Error Initializing.");
+ exit(-1);
+ }
+
+#ifdef RADIUS_ENABLED
+ if (radiusEnabled) {
+ loadRadiusLibrary();
+ }
+#endif /* RADIUS_ENABLED */
+
+ (void) restoreAgentState();
+
+ /*
+ * We need to unblock the signals that we care about.
+ */
+ (void) sigemptyset(&thread_signals);
+ (void) sigaddset(&thread_signals, SIGINT);
+ (void) sigaddset(&thread_signals, SIGTERM);
+ (void) sigaddset(&thread_signals, SIGUSR1);
+
+ if (pthread_sigmask(SIG_BLOCK, &thread_signals, NULL)) {
+ syslog(LOG_ERR, "Unable to set thread signals");
+ exit(-1);
+ }
+
+ if (disableSNMP == _B_FALSE) {
+ /*
+ * Initialize the SNMP Thread.
+ */
+ if (startSNMPTaskThread()) {
+ syslog(LOG_CRIT, "Error Initializing SNMP.");
+ exit(-1);
+ }
+ }
+
+ /*
+ * Start the AAA thread.
+ */
+ if (aaaProtocol != AAA_NONE) {
+ if ((rc = startAAATaskThread()) != 0) {
+ syslog(LOG_CRIT,
+ "Error: rc = %d when calling startAAATaskThread\n",
+ rc);
+ exit(-1);
+ }
+ }
+
+ /*
+ * Start a thread which will handle all periodic tasks.
+ */
+ if (startPeriodicTaskThread()) {
+ syslog(LOG_CRIT, "Unable to start periodic thread");
+ exit(-1);
+ }
+
+ /*
+ * Initialize the multi-thread message dispatcher.
+ */
+ if (startDispatcherTaskThread()) {
+ syslog(LOG_CRIT, "Unable to initialize the message "
+ "dispatcher");
+ exit(-1);
+ }
+
+ /*
+ * If DynamicInterface global variable is set, then
+ * start DynamicInterface process thread
+ */
+ if (DynamicInterface) {
+ /* Make a list of existing interfaces first */
+ if (CreateListOfExistingIntfce() != 0) {
+ syslog(LOG_ERR,
+ "Unable to create a list of interfaces: %m");
+ exit(-1);
+ }
+ if (startDynamicInterfaceThread()) {
+ syslog(LOG_CRIT,
+ "Unable to start Dynamic Interface Thread");
+ exit(-1);
+ }
+ }
+
+
+ /*
+ * Start stat door server.
+ */
+ if (startStatServer()) {
+ syslog(LOG_ERR, "Unable to create stat server door");
+ }
+
+ /*
+ * Let's start the performance test thread
+ */
+ if (performanceInterval) {
+ syslog(LOG_ERR, "Starting the performance checker");
+ if (startPerfTestServer()) {
+ syslog(LOG_ERR, "Unable to create performance server");
+ }
+ }
+
+ /*
+ * We need to unblock the signals that we care about.
+ */
+ (void) sigemptyset(&thread_signals);
+ (void) sigaddset(&thread_signals, SIGINT);
+ (void) sigaddset(&thread_signals, SIGTERM);
+ (void) sigaddset(&thread_signals, SIGUSR1);
+
+ if (pthread_sigmask(SIG_UNBLOCK, &thread_signals, NULL)) {
+ syslog(LOG_ERR, "Unable to set thread signals");
+ exit(-1);
+ }
+
+ (void) sigwait(&thread_signals, &signal);
+
+ Finalize(signal);
+
+ return (0);
+}
+#endif
+
+
+static void
+perf_thread()
+{
+ time_t startTime;
+ time_t stopTime;
+ struct timeval tv;
+ char buffer[PERF_MSG_SIZE];
+
+ while (faCounters.faRegReqRecvdCnt == 0 &&
+ haCounters.haRegReqRecvdCnt == 0) {
+ (void) sleep(1);
+ }
+
+ GET_TIME(startTime);
+ /* CONSTCOND */
+ while (_B_TRUE) {
+ tv.tv_sec = performanceInterval;
+ tv.tv_usec = 0;
+ (void) select(FD_SETSIZE, NULL, NULL, NULL, &tv);
+ GET_TIME(stopTime);
+
+ if (haCounters.haRegReqRecvdCnt) {
+ (void) sprintf(buffer, "HA Packets per second %ld\n",
+ haCounters.haRegReqRecvdCnt /
+ (stopTime - startTime));
+ syslog(LOG_ERR, "%s", buffer);
+ }
+
+ if (faCounters.faRegReqRecvdCnt) {
+ (void) sprintf(buffer, "FA Packets per second %ld\n",
+ faCounters.faRegReqRecvdCnt /
+ (stopTime - startTime));
+ syslog(LOG_ERR, "%s", buffer);
+ }
+ }
+}
+
+
+static int
+startPerfTestServer()
+{
+ pthread_t threadId = 0;
+ pthread_attr_t pthreadAttribute;
+ int result;
+
+ result = pthread_attr_init(&pthreadAttribute);
+
+ if (result) {
+ syslog(LOG_CRIT, "Error Initializing pthread.");
+ return (-1);
+ }
+
+ /*
+ * We now create a thread to deal with all periodic task.
+ */
+ result = pthread_create(&threadId, &pthreadAttribute,
+ (void *(*)()) perf_thread,
+ (void *)NULL);
+
+ if (result) {
+ syslog(LOG_CRIT, "pthread_create() failed.");
+ return (-1);
+ }
+
+ /*
+ * In order for system resources the be properly cleaned up,
+ * we need to detach the thread. Otherwise, we need to wait for
+ * a pthread_join(), which we do not want.
+ */
+ result = pthread_detach(threadId);
+
+ if (result) {
+ syslog(LOG_CRIT, "pthread_detach() failed.");
+ return (-1);
+ }
+
+ return (0);
+
+
+}
+
+/*
+ * Function : ConfigEntryHashLookup
+ *
+ * Description:
+ * This lookup function matches the interface index of the
+ * entry with the passed value.
+ * Returns _B_TRUE if the entry matches the desired criteria
+ * else _B_FALSE
+ */
+
+/* ARGSUSED */
+boolean_t
+ConfigEntryHashLookup(void *entry, uint32_t p1, uint32_t p2, uint32_t p3)
+{
+ MaAdvConfigEntry *maAdvEntry = entry;
+
+ if (maAdvEntry->maIfindex == p1)
+ return (_B_TRUE);
+ else
+ return (_B_FALSE);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mipagent/agent.h b/usr/src/cmd/cmd-inet/usr.lib/mipagent/agent.h
new file mode 100644
index 0000000000..cf8e5ee672
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mipagent/agent.h
@@ -0,0 +1,774 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1999-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _AGENT_H
+#define _AGENT_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * This file contains definitions for structures used by
+ * Mobility Agents (either Home Agents or Foreign Agents)
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <synch.h>
+#include <pthread.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/if_ether.h>
+#include <netinet/in.h>
+#include "mip.h"
+#include "hash.h"
+#include "aaa.h"
+
+/* Hash types */
+#define HASH_IT 0
+#define MD5_HASH 1
+
+#define MAX_FN_LEN 256
+
+#define VERSION_MAJOR 1
+#define VERSION_MINOR 0
+
+
+/*
+ * We do not current support simultaneous bindings
+ * due to the new tunnel driver architecture. This
+ * is something that we may wish to change in the
+ * future.
+ */
+
+#define MAX_SIMULTANEOUS_BINDINGS 7
+#define DEFAULT_MAX_REG_TIME 300
+#define DEFAULT_MAX_ADV_TIME 300
+#define DEFAULT_MAX_INTERVAL 10
+#define DEFAULT_MIN_INTERVAL 1
+
+#define ADV_INIT_COUNT_DEFAULT 1
+#define ADV_INIT_COUNT_MIN 0
+#define DEFAULT_FRESHNESS_SLACK 300
+
+#define DEFAULT_ADVERTISEMENT_INTERVAL 5
+
+#define MIP_PORT 434
+
+/*
+ * These are for the IPsec flags in the MobilityAgentEntry.
+ */
+#define IPSEC_REQUEST 0x01
+#define IPSEC_REPLY 0x02
+#define IPSEC_TUNNEL 0x04
+#define IPSEC_REVERSE_TUNNEL 0x08
+
+#define APPLY(x) (x)
+#define PERMIT(x) ((x) << 4)
+
+/*
+ * We define the other way so we can have indexes into validIPsecAction[],
+ * and the maIPsec*'s in the MobilityAgentEntry struct, too.
+ */
+#define FIRST_IPSEC_ACTION 0
+#define IPSEC_APPLY 0
+#define IPSEC_PERMIT 1
+#define LAST_IPSEC_ACTION 2
+
+#define REQUEST(a) (1 << (((int)(a)) * 4))
+#define REPLY(a) (1 << ((((int)(a)) * 4) + 1))
+#define TUNNEL(a) (1 << ((((int)(a)) * 4) + 2))
+#define REVERSE_TUNNEL(a) (1 << ((((int)(a)) * 4) + 3))
+
+/*
+ * The above pair of 6 macros yield the following:
+ *
+ * bit 1 -> APPLY(IPSEC_REQUEST) = IPSEC_REQUEST_APPLY
+ * bit 2 -> APPLY(IPSEC_REPLY) = IPSEC_REPLY_APPLY
+ * bit 3 -> APPLY(IPSEC_TUNNEL) = IPSEC_TUNNEL_APPLY
+ * bit 4 -> APPLY(IPSEC_REVERSE_TUNNEL) = IPSEC_REVERSE_TUNNEL_APPLY
+ * bit 5 -> PERMIT(IPESC_REQUEST) = IPSEC_REQUEST_PERMIT
+ * bit 6 -> PERMIT(IPSEC_REPLY) = IPSEC_REPLY_PERMIT
+ * bit 7 -> PERMIT(IPSEC_TUNNEL) = IPSEC_TUNNEL_PERMIT
+ * bit 8 -> PERMIT(IPSEC_REVERSE_TUNNEL) = IPSEC_REVERSE_TUNNEL_PERMIT
+ */
+#define IPSEC_REQUEST_APPLY 0x01
+#define IPSEC_REPLY_APPLY 0x02
+#define IPSEC_TUNNEL_APPLY 0x04
+#define IPSEC_REVERSE_TUNNEL_APPLY 0x08
+#define IPSEC_REQUEST_PERMIT 0x10
+#define IPSEC_REPLY_PERMIT 0x20
+#define IPSEC_TUNNEL_PERMIT 0x40
+#define IPSEC_REVERSE_TUNNEL_PERMIT 0x80
+
+/*
+ * Bitmasks for the maIPsecSAFlags[] members of the MobilityAgentEntry struct
+ * that we pass to mipagentstat. As the HA_PEER we use request-permit,
+ * reply-apply, tunnel-apply, and reverse-tunnel permit. This means we have to
+ * mask-in reply and tunnel from maIPsecSAFlags[IPSEC_APPLY], and request and
+ * reverse tunnel from maIPsecSAFlags[IPSEC_PERMIT]. As an FA_PEER we use
+ * request-apply, reply-permit, tunnel-permit, and reverse-tunnel-apply. This
+ * means we have to mask-in request and reverse-tunnel from
+ * maIPsecSAFlags[IPSEC_APPLY], and reply and tunnel from
+ * maIsecSAFlags[IPSEC_PERMIT].
+ */
+ /* type: RQRPFTRT */
+ /* SA : AEAEAEAE */
+#define HA_PEER_APPLY_MASK (IPSEC_REPLY_BOTH | IPSEC_TUNNEL_BOTH)
+ /* 0x3c, 00111100b */
+#define FA_PEER_APPLY_MASK (IPSEC_REQUEST_BOTH | IPSEC_REVERSE_TUNNEL_BOTH)
+ /* 0xc3, 11000011b */
+#define HA_PEER_PERMIT_MASK (IPSEC_REQUEST_BOTH | IPSEC_REVERSE_TUNNEL_BOTH)
+ /* 0xc3, 11000011b */
+#define FA_PEER_PERMIT_MASK (IPSEC_REPLY_BOTH | IPSEC_TUNNEL_BOTH)
+ /* 0x3c, 00111100b */
+
+#define IPSEC_ORDER 2 /* Arrays are 2-by: apply and permit */
+#define MAX_IPSEC_GET_SIZE 1024 /* read()/write() via PF_KEY socket */
+#define FINE_STRUCT_CONST 137 /* Our initial ipsec sequence number */
+#define MAX_IPSEC_POLICY_SIZE 1024 /* Same as MAXLEN in ipsecconf.h */
+
+/* Commands for ipsecconf are "/usr/sbin/ipsecconf -(a|r) /dev/stdin -q" */
+#define IPSEC_CONF_COMMAND_SIZE 37
+
+#define IPv4_ADDR_LEN 16 /* in dotted-decimal */
+#define CONF_FILE_NAME "/etc/inet/mipagent.conf"
+
+/* easy flag to index algorithm (warning: use on flags only!) */
+#define IPSEC_POLICY_STRING(x) ipsec_policy_string[ffs(x) - 1]
+
+/* developer-friendly add/remove policy strings */
+#define ADD_POLICY 0
+#define SUB_POLICY 1
+#define IPSEC(x) ipsec_policy_action[x]
+
+
+/*
+ * Maximum number of visitor entries (accepted + pending) maintained
+ * at a Foreign Agent. A foreign agent sets the busy bit 'B' in its
+ * advertisements when number of visitors reaches DEFAULT_HIGH_VISITORS
+ * and does not reset it till it drops below DEFAULT_LOW_VISITORS.
+ * A pending entry is kept in the visitor table for at most
+ * DEFAULT_VISITOR_EXPIRY seconds.
+ *
+ * The new solaris Mobile-IP does not have any such restrictions,
+ * therefore we will set the visitor entry threshold to some ungodly
+ * large number, which can be overriden by the administrator.
+ */
+#define DEFAULT_HIGH_VISITORS -1
+#define DEFAULT_LOW_VISITORS -5
+#define DEFAULT_VISITOR_EXPIRY 30
+
+#ifdef FIREWALL_SUPPORT
+/* Max number of address intervals for specifying protected domain */
+#define MAX_ADDR_INTERVALS 6
+
+/* Max. number of firewalls */
+#define MAX_FIREWALLS 3
+#endif /* FIREWALL_SUPPORT */
+
+#define MAX_IFNAME_LEN 8
+#define MAX_HWADDR_LEN sizeof (struct ether_addr)
+
+#define MAX_TIME_STRING_SIZE 32
+
+/*
+ * The following bits are set depending on the value of 'ReverseTunnelAllowed
+ * and ReverseTunnelRequired' parameter in the mipagent.conf file. By default
+ * the value for each is RT_NONE. It's a policy set by the configuration file
+ * to check if Reverse Tunnel bit must be present or NOT in the registration
+ * request. For example a FA may only accept registration packet with 'T' bit
+ * on while the HA accepts regReq with or without T bit on. These are for
+ * reverse tunnel settings. We advertise the T-bit on a per-interface level,
+ * so discern only on a per-interface, and not a per-agent-per-interface level
+ * (e.g. advertise the 'T' bit, but only enforce reverse-tunnel-required on
+ * the HA). These will allow us to do that.
+ */
+#define RT_NONE 0x0
+#define RT_HA 0x1
+#define RT_FA 0x2
+#define RT_BOTH 0x3
+
+/*
+ * The following are the minimum and maximum values
+ * that one can configure for garbage collection. Note
+ * that this feature is largely undocumented, and can be
+ * used to alter the frequency that the agent attempts to
+ * clean up expired data structures.
+ */
+#define MIN_GARBAGE_COLLECTION_INTERVAL 5
+#define MAX_GARBAGE_COLLECTION_INTERVAL 120
+#define DEFAULT_GARBAGE_COLLECTION_INTERVAL 15
+
+#define UNSOLICITED_ADV 1
+#define SOLICITED_ADV 2
+
+
+/* One such entry for each mobility-supporting interface */
+typedef struct {
+ /*
+ * The nodeLock field MUST be the first field present in
+ * this structure, and is required by the hashing mobule.
+ */
+ rwlock_t maIfaceNodeLock;
+ ipaddr_t maIfaceAddr;
+ uint32_t maIfaceNetmask;
+ uint8_t maIfaceHWaddr[MAX_HWADDR_LEN]; /* used in proxy-ARPs */
+ int8_t maIfaceName[LIFNAMSIZ];
+ uint64_t maIfaceFlags; /* interface flags */
+ int maIfaceIcmpSock; /* ICMP socket bound to ifaceAddr */
+ int maIfaceUnicastSock; /* UDP socket bound to IfaceAddr */
+ int maIfaceBcastSock; /* ... bound to subnet bcastAddr */
+ int maIfaceDirBcastSock; /* ... to directed bcast Addr */
+ int maIfaceAdvMulticastSock; /* ... bound to mcastAddr */
+ int maIfaceRegMulticastSock; /* ... bound to mcastAddr */
+ uint32_t maAdvMaxRegLifetime;
+ uint32_t maAdvAddr;
+ uint32_t maAdvMaxInterval;
+ uint32_t maAdvMinInterval;
+ uint32_t maAdvMaxAdvLifetime;
+ /* boolean maAdvResponseSolicitationOnly; */
+ unsigned short maAdvSeqNum;
+ uint8_t maAdvServiceFlags; /* RBHFM[GV]T flags */
+ boolean_t maAdvPrefixLenInclusion;
+ uint8_t maReverseTunnelAllowed;
+ uint8_t maReverseTunnelRequired;
+ /* dynamic interface support */
+ boolean_t maAdvDynamicInterface; /* Used in lookup */
+ boolean_t maAdvLimitUnsolicited;
+ uint8_t maAdvInitCount; /* Initial Count when */
+ /* maAdvLimitUnsolicited is true */
+ uint32_t maAdvInterval;
+ time_t maNextAdvTime;
+ uint32_t maIfindex; /* Interface index */
+} MaAdvConfigEntry;
+
+/*
+ * Mobile tunnel specific data
+ */
+typedef struct {
+ rwlock_t TunlNodeLock;
+ uint32_t tunnelno;
+ uint32_t refcnt;
+ ipaddr_t tunnelsrc; /* Tunnel source end-point */
+ uint32_t mux_fd; /* fd associated with tun */
+} MipTunlEntry;
+
+/* One entry for each visiting mobile node at a foreign agent. */
+typedef struct {
+ /*
+ * The nodeLock field MUST be the first field present in
+ * this structure, and is required by the hashing mobule.
+ */
+ rwlock_t faVisitorNodeLock;
+ ipaddr_t faVisitorAddr;
+ ipaddr_t faVisitorIfaceAddr; /* interface addr through */
+ /* which visitor is reachable */
+ boolean_t faVisitorRegIsAccepted;
+ int8_t faVisitorRegFlags;
+ in_port_t faVisitorPort;
+ ipaddr_t faVisitorHomeAddr;
+ ipaddr_t faVisitorHomeAgentAddr;
+ ipaddr_t faVisitorCOAddr; /* COaddr field in request */
+ time_t faVisitorTimeGranted; /* only valid if IsAccepted */
+ time_t faVisitorTimeExpires; /* only valid if IsAccepted */
+ uint32_t faVisitorSPI;
+ uint32_t faVisitorRegIDHigh;
+ uint32_t faVisitorRegIDLow;
+ uint8_t faVisitorMnNAI[MAX_NAI_LENGTH]; /* Mobile Node's NAI */
+ uint32_t faVisitorMnNAILen;
+ uint8_t faVisitorChallengeToHA[ADV_CHALLENGE_LENGTH];
+ uint32_t faVisitorChallengeToHALen;
+ uint8_t faVisitorChallengeAdv[ADV_CHALLENGE_LENGTH];
+ uint32_t faVisitorChallengeAdvLen;
+ uint32_t faVisitorInIfindex; /* interface index on which */
+ /* reg. request is recvd */
+ boolean_t faVisitorIsSllaValid; /* if the MN's SLLA is legit */
+ struct sockaddr_dl faVisitorSlla; /* MN's link layer address */
+} FaVisitorEntry;
+
+
+/*
+ * The Home Agent maintains one such entry for each mobility binding.
+ * A single mobile node may have multiple bindings.
+ */
+typedef struct habindingentry {
+ ipaddr_t haBindingMN; /* Mobile node's home address */
+ ipaddr_t haBindingCOA; /* Mobile node's care-of address */
+ ipaddr_t haBindingSrcAddr;
+ ipaddr_t haBindingHaAddr; /* Home Agent address */
+ time_t haBindingTimeGranted;
+ time_t haBindingTimeExpires;
+ in_port_t haBindingSrcPort;
+ int8_t haBindingRegFlags; /* 8 bit - comes from the wire */
+ struct habindingentry *next; /* Next structure */
+} HaBindingEntry;
+
+/*
+ * The Home Agent maintains one mobile node entry for each supported Mobile
+ * Node. In addition, the Home Agent keeps a MipSecAssocEntry for
+ * for every other node with which it shares a mobility security
+ * association. By using the interface information, the home agent
+ * can filter out broadcast packets that it picks up from subnets
+ * other than the mobile node's home subnet.
+ */
+typedef struct {
+ /*
+ * The nodeLock field MUST be the first field present in
+ * this structure, and is required by the hashing mobule.
+ */
+ rwlock_t haMnNodeLock;
+ boolean_t haMnIsEntryDynamic;
+ ipaddr_t haMnAddr; /* Mobile Node's IP address */
+ uint8_t haMnNAI[MAX_NAI_LENGTH]; /* Mobile Node NAI */
+ uint8_t haMnNAILen; /* Mobile Node NAI Len */
+ ipaddr_t haBindingIfaceAddr; /* interface on the mobile */
+ /* node's home subnet */
+ uint32_t haMnRegIDHigh; /* Stored ID used in replay */
+ /* protection */
+ uint32_t haMnRegIDLow;
+ uint32_t haMnSPI;
+ int haMnBindingCnt; /* Number of current bindings */
+#ifdef RADIUS_ENABLED
+ char *haRadiusState; /* maintains radius state info */
+ time_t haRadiusLastLookupTime;
+#endif /* RADIUS_ENABLED */
+ uint32_t haServiceRequestsAcceptedCnt; /* The number of */
+ /* successful registrations */
+ uint32_t haServiceRequestsDeniedCnt; /* The number of */
+ /* failed registrations */
+ time_t haOverallServiceTime; /* The total amount of */
+ /* service time */
+ time_t haRecentServiceAcceptedTime; /* The last time */
+ /* service was provided */
+ time_t haRecentServiceDeniedTime; /* The last time */
+ /* service was denied */
+ uint32_t haRecentServiceDeniedCode; /* The last failure */
+ /* code */
+ uint32_t haPoolIdentifier;
+ HaBindingEntry *bindingEntries;
+} HaMobileNodeEntry;
+
+
+/*
+ * Mobility Agent Authentication Information.
+ */
+typedef struct {
+ /*
+ * The nodeLock field MUST be the first field present in
+ * this structure, and is required by the hashing mobule.
+ */
+ rwlock_t maNodeLock;
+ boolean_t maIsEntryDynamic;
+ ipaddr_t maAddr;
+ uint32_t maSPI;
+ /*
+ * The following value is used during garbage collection to check
+ * if this Mobility Agent has expired.
+ */
+ time_t maExpiration;
+
+ /*
+ * How is IPsec securing traffic to this agent-peer? Ultimately, all
+ * we should need are the ipsec_req_t structs here, but that wont
+ * happen until either ipsec has an API, or they support multiple
+ * policies per socket. Until then, we need the char strings to pass
+ * to ipsecconf(1M). We do use the ipsec_req_t structs for our tunnel
+ * setup, but not at this time for the registration traffic. Still,
+ * this is setup at init time, so why not parse everything in
+ * anticipation of ipsec support. Note, also, until ipsec supports
+ * multiple policies per socket, we can only support symmetric tunnel
+ * polices (forward tunnel policy = reverse tunnel policy). IPSEC_ORDER
+ * here refers to the 'depth' of ipsec protection, that is how many
+ * different actions do we support. This is done this way for
+ * expandability.
+ */
+ ipsec_req_t maIPsecRequestIPSR[IPSEC_ORDER];
+ ipsec_req_t maIPsecReplyIPSR[IPSEC_ORDER];
+ ipsec_req_t maIPsecTunnelIPSR[IPSEC_ORDER];
+ ipsec_req_t maIPsecReverseTunnelIPSR[IPSEC_ORDER];
+ uint8_t maIPsecFlags; /* what's currently invoked */
+ uint8_t maPeerFlags; /* 2 for IPsec: ha and fa. Rest TBD */
+
+ /*
+ * The SA flags are arranged like this:
+ *
+ * Request Reply Tunnel RTunnel
+ * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
+ * AH ESP AH ESP AH ESP AH ESP
+ *
+ * So bits 0, 1 say whether AH or ESP are protecting requests, but
+ * 2, 3 say whether AH or ESP are protecting replies, etc.
+ * We need one of these SA flag fields for each of our actions.
+ * This way we know which are specific for APPLY = outbound, PERMIT =
+ * inbound, (etc, should there be more someday).
+ */
+ uint8_t maIPsecSAFlags[IPSEC_ORDER];
+
+ /* these are the chars mentioned above that should go away */
+ char maIPsecRequest[IPSEC_ORDER][MAX_IPSEC_POLICY_SIZE];
+ char maIPsecReply[IPSEC_ORDER][MAX_IPSEC_POLICY_SIZE];
+} MobilityAgentEntry;
+
+#ifdef FIREWALL_SUPPORT
+/* Data structure to store the information regarding protected domain */
+typedef struct {
+ ipaddr_t addr[MAX_ADDR_INTERVALS];
+ uint32_t netmask[MAX_ADDR_INTERVALS];
+ uint32_t addrIntervalCnt;
+ uint32_t fwAddr[MAX_FIREWALLS];
+ uint32_t firewallCnt;
+} DomainInfo;
+#endif /* FIREWALL_SUPPORT */
+
+/*
+ * Counters common to all Mobility Agents
+ *
+ * Note: We will not be locking this structure before modifying it,
+ * even though we know that we are multi-threaded. The worst case
+ * scenario is that we will return incorrect statistics to the snmp
+ * requestor. The cost of locking and unlocking this structure is
+ * simply not worth it.
+ */
+typedef struct {
+ uint32_t maAdvSentCnt;
+ uint32_t maAdvSentForSolicitationsCnt;
+ uint32_t maSolicitationsRecvdCnt;
+} CommonCounters;
+
+/*
+ * Counters maintained by Foreign Agents
+ *
+ * Note: We will not be locking this structure before modifying it,
+ * even though we know that we are multi-threaded. The worst case
+ * scenario is that we will return incorrect statistics to the snmp
+ * requestor. The cost of locking and unlocking this structure is
+ * simply not worth it.
+ */
+typedef struct {
+ uint32_t faRegReqRecvdCnt; /* total valid reg requests received */
+ uint32_t faRegReqRelayedCnt; /* total valid reg reqs relayed to HA */
+ uint32_t faReasonUnspecifiedCnt; /* rejected with code 64 */
+ uint32_t faAdmProhibitedCnt; /* rejected with code 65 */
+ uint32_t faInsufficientResourceCnt; /* rejected with code 66 */
+ uint32_t faMNAuthFailureCnt; /* rejected with code 67 */
+ uint32_t faHAAuthFailureCnt; /* rejected with code 68 */
+ uint32_t faRegLifetimeTooLongCnt; /* rejected with code 69 */
+ uint32_t faPoorlyFormedRequestsCnt; /* rejected with code 70 */
+ uint32_t faPoorlyFormedRepliesCnt; /* rejected with code 71 */
+ uint32_t faEncapUnavailableCnt; /* rejected with code 72 */
+ uint32_t faVJCompUnavailableCnt; /* rejected with code 73 */
+ uint32_t faReverseTunnelUnavailableCnt; /* rejected with code 74 */
+ uint32_t faReverseTunnelRequiredCnt; /* rejected with code 75 */
+ uint32_t faMNTooDistantCnt; /* rejected with code 76 */
+ uint32_t faInvalidCareOfAddrCnt; /* rejected with code 77 */
+ uint32_t faRTEncapUnavailableCnt; /* rejected with code 79 */
+ uint32_t faHAUnreachableCnt; /* rejected with codes 80-95 */
+ uint32_t faRegRepliesRecvdCnt; /* well-formed reg replies received */
+ uint32_t faRegRepliesRelayedCnt; /* well-formed regrep relayd */
+ uint32_t faRegRepliesICMPUnreachCnt; /* replies for ICMP_UNREACH */
+ uint32_t faRegRepliesICMPTimxceedCnt; /* replies for ICMP_TIMXCEED */
+ uint32_t faIsBusyCnt; /* number of times we were too busy */
+} ForeignAgentCounters;
+
+/*
+ * Counters maintained by Home Agents
+ *
+ * Note: We will not be locking this structure before modifying it,
+ * even though we know that we are multi-threaded. The worst case
+ * scenario is that we will return incorrect statistics to the snmp
+ * requestor. The cost of locking and unlocking this structure is
+ * simply not worth it.
+ */
+typedef struct {
+ uint32_t haRegAccepted0Cnt; /* reg requests accepted with code 0 */
+ uint32_t haRegAccepted1Cnt; /* reg requests accepted with code 1 */
+ uint32_t haReasonUnspecifiedCnt; /* denied with code 128 */
+ uint32_t haAdmProhibitedCnt; /* denied with code 129 */
+ uint32_t haInsufficientResourceCnt; /* denied with code 130 */
+ uint32_t haMNAuthFailureCnt; /* denied with code 131 */
+ uint32_t haFAAuthFailureCnt; /* denied with code 132 */
+ uint32_t haIDMismatchCnt; /* denied with code 133 */
+ uint32_t haPoorlyFormedRequestsCnt; /* denied with code 134 */
+ uint32_t haTooManyBindingsCnt; /* denied with code 135 */
+ uint32_t haUnknownHACnt; /* denied with code 136 */
+ uint32_t haReverseTunnelUnavailableCnt;
+ /* denied with code 137 */
+ uint32_t haReverseTunnelRequiredCnt; /* denied with code 138 */
+ uint32_t haEncapUnavailableCnt; /* denied with code 139 */
+ uint32_t haGratuitousARPsSentCnt;
+ uint32_t haProxyARPsSentCnt;
+ uint32_t haRegReqRecvdCnt;
+ uint32_t haDeRegReqRecvdCnt;
+ uint32_t haRegRepliesSentCnt;
+ uint32_t haDeRegRepliesSentCnt;
+} HomeAgentCounters;
+
+/*
+ * Mobile-IP is a simple protocol, with limited extensions. Today
+ * the number of extensions in a single message cannot exceed 10.
+ */
+#define MAX_EXPECTED_EXTENSIONS 16
+
+/*
+ * Mobile IP Packets are relatively small, and cannot really
+ * exceed 2k in size.
+ */
+#define MAX_PKT_SIZE 2048
+/* Maximum control/data buffer size (in long's) for getmsg() */
+#define MAXDLBUF 8192
+
+typedef struct messagehdr {
+ enum {
+ MIP_PKT_FROM_FA = 1,
+ MIP_PKT_FROM_AAA,
+ MIP_PKT_FROM_RADIUS
+ } pktSource;
+ enum {
+ PKT_UDP = 1,
+ PKT_ICMP
+ } pktType;
+ unsigned char pkt[MAX_PKT_SIZE];
+ uint32_t pktLen;
+ ipaddr_t src;
+ in_port_t srcPort;
+ uint8_t *mnNAI;
+ unsigned int mnNAILen;
+ size_t extCnt;
+ uint8_t extType[MAX_EXPECTED_EXTENSIONS];
+ uint16_t extSubType[MAX_EXPECTED_EXTENSIONS];
+ uint8_t *extIdx[MAX_EXPECTED_EXTENSIONS];
+ uint8_t *extData[MAX_EXPECTED_EXTENSIONS];
+ size_t extHdrLength[MAX_EXPECTED_EXTENSIONS];
+ size_t extLength[MAX_EXPECTED_EXTENSIONS];
+ uint32_t extVendorId[MAX_EXPECTED_EXTENSIONS];
+ MaAdvConfigEntry *ifEntry;
+ enum {
+ ON_UNICAST_SOCK,
+ ON_BCAST_SOCK,
+ ON_MCAST_SOCK
+ } ifType;
+ boolean_t dontDeleteNow;
+
+ /*
+ * The following is some AAA Stuff, and can ONLY be set
+ * if the packet source is AAA.
+ */
+ uint32_t messageHandle;
+ unsigned char *faNAI;
+ size_t faNAILen;
+ uint32_t mnAAASPI;
+ uint16_t algorithm;
+ uint32_t mnHaSPI;
+ uint8_t mnHaKey[MAX_KEY_LEN];
+ size_t mnHaKeyLen;
+ uint32_t mnFaSPI;
+ uint8_t mnFaKey[MAX_KEY_LEN];
+ size_t mnFaKeyLen;
+ uint32_t faHaSPI;
+ uint8_t faHaKey[MAX_KEY_LEN];
+ size_t faHaKeyLen;
+ uint32_t aaaSessionTimeout;
+ uint32_t aaaResultCode;
+#ifdef KEY_DISTRIBUTION
+ /*
+ * KEY_DISTRIBUTION MUST ONLY BE COMPILED FOR TESTING!!!
+ *
+ * This version of mipagent supports a AAA/DIAMETER
+ * interface. The DIAMETER server generates keying
+ * material that is sent to the Home Agent. The keys
+ * sent are both for the Home Agent, and for the Mobile
+ * Node. The keys for the Mobile Nodes are added to the
+ * registration reply, and the keys for the Home Agent
+ * cause the Home Agent to create a local SA.
+ *
+ * Since DIAMETER/AAA is not currently a product, and key
+ * distribution must still be tested, we have added some
+ * test code in mipagent. When KEY_DISTRIBUTION is enabled,
+ * the home agent creates and encrypts session keys for
+ * the Mobile Node (mimicking DIAMETER), and creates local
+ * SAs. Further, since the session keys MUST also be sent
+ * to the Foreign Agent, the session keys are sent in the
+ * clear to the Foreign Agent through Vendor Specific
+ * extensions.
+ *
+ * Again, this code is for testing purpose only and must not
+ * be enabled for production code, since it hasn't been
+ * fully tested.
+ */
+ boolean_t kdcKeysPresent;
+#endif /* KEY_DISTRIBUTION */
+
+ /*
+ * Ancillary data gleaned from the registration request
+ */
+ boolean_t isSllaValid; /* is SLLA valid/legitimate ? */
+ struct sockaddr_dl slla; /* source link layer address */
+ uint32_t inIfindex; /* Inbound interface index */
+ uint8_t ttl; /* IP TTL of inbound pkt */
+
+ struct messagehdr *next;
+} MessageHdr;
+
+
+/*
+ * This entry holds the dynamic interface type and the common
+ * information that will be applied to all dynamic interfaces
+ * of the same type. For example, for an entry of interfacename
+ * ppp*, there will be one such entry.
+ */
+typedef struct dynamicIfacetype {
+ struct dynamicIfacetype *next;
+ int RegLifetime;
+ int AdvLifetime;
+ uint32_t AdvInterval;
+ int32_t AdvServiceflag;
+ boolean_t AdvLimitUnsolicited;
+ boolean_t AdvPrefixflag;
+ boolean_t advertiseOnBcast;
+ uint8_t AdvInitCount;
+ uint8_t RevtunReqd;
+ uint8_t RevtunAllowed;
+ char dynamicIfcetype[LIFNAMSIZ];
+} DynamicIfaceTypeEntry;
+
+/* DynamicInterface, dynamicIfaceHead variables are set in agentInit.c */
+boolean_t DynamicInterface;
+DynamicIfaceTypeEntry *dynamicIfaceHead;
+
+/*
+ * This data structure keeps track of existing interfaces
+ * at the time of mipagent startup
+ */
+typedef struct staticIface {
+ char ifacename[LIFNAMSIZ];
+ struct staticIface *next;
+} StaticIfaceEntry;
+
+#define GET_EXT_DATA(messageHdr, counter, extId, ptr, len) \
+ for (counter = 0, len = 0, ptr = NULL; \
+ counter < messageHdr->extCnt; counter++) { \
+ if (messageHdr->extType[counter] == extId) { \
+ /* \
+ * Support for non-traditional \
+ * extension header formats \
+ * \
+ * Get a pointer to the data and its length \
+ */ \
+ ptr = messageHdr->extData[counter]; \
+ len = messageHdr->extLength[counter]; \
+ break; \
+ } \
+ } \
+
+#define GET_AUTH_EXT(messageHdr, counter, extId, ptr, len) \
+ for (counter = 0, len = 0, ptr = NULL; \
+ counter < messageHdr->extCnt; counter++) { \
+ if (messageHdr->extType[counter] == extId) { \
+ /* \
+ * Support for non-traditional \
+ * extension header formats \
+ * \
+ * Get a pointer to the data and its length \
+ */ \
+ ptr = (authExt *)messageHdr->extIdx[counter]; \
+ len = messageHdr->extLength[counter]; \
+ break; \
+ } \
+ } \
+
+/*
+ * Support for the latest Challenge/Response I-D
+ */
+#define GET_GEN_AUTH_EXT(messageHdr, counter, extId, ptr, len) \
+ for (counter = 0, len = 0, ptr = NULL; \
+ counter < messageHdr->extCnt; counter++) { \
+ if ((messageHdr->extType[counter] == \
+ REG_GEN_AUTH_EXT_TYPE) && \
+ (messageHdr->extSubType[counter] == extId)) { \
+ /* \
+ * Get a pointer to the data and its length \
+ */ \
+ ptr = (genAuthExt *)messageHdr->extIdx[counter]; \
+ len = messageHdr->extLength[counter]; \
+ break; \
+ } \
+ } \
+
+/*
+ * Support for vendor specific extensions.
+ */
+#define GET_VEND_KEY_EXT(messageHdr, counter, vendId, extId, ptr, len) \
+ for (counter = 0, len = 0, ptr = NULL; \
+ counter < messageHdr->extCnt; counter++) { \
+ if ((messageHdr->extVendorId[counter] == vendId) && \
+ (messageHdr->extSubType[counter] == extId)) { \
+ /* \
+ * Get a pointer to the data and its length \
+ */ \
+ ptr = (keyDataExt *)messageHdr->extData[counter]; \
+ len = messageHdr->extLength[counter]; \
+ break; \
+ } \
+ } \
+
+#define GET_TIME(currentTime) \
+ { \
+ struct timeval timer; \
+ if (gettimeofday(&timer, NULL) == -1) { \
+ currentTime = 0; \
+ } else { \
+ currentTime = timer.tv_sec; \
+ } \
+ }
+
+#define GENERATE_NET_BROADCAST_ADDR(entry) \
+ (entry->maIfaceAddr & entry->maIfaceNetmask) | \
+ ~entry->maIfaceNetmask
+
+/* Common agent functions used by multiple files */
+extern boolean_t ConfigEntryHashLookup(void *, uint32_t, uint32_t, uint32_t);
+extern int CreateListOfExistingIntfce(void);
+extern int startDynamicInterfaceThread(void);
+extern int killDynamicInterfaceThread(void);
+extern int InitSockets(MaAdvConfigEntry *);
+extern void docleanup(void);
+extern void disableService(struct hash_table *);
+extern int aaaCreateKey(int, unsigned char *, size_t, uint32_t);
+extern int haCheckRegReqAuthContinue(MessageHdr *, HaMobileNodeEntry **,
+ uint32_t *, uint32_t *);
+extern int addHABE(HaMobileNodeEntry *, ipaddr_t, in_port_t,
+ MaAdvConfigEntry *, uint8_t, ipaddr_t, ipaddr_t, ipaddr_t, uint32_t,
+ boolean_t *, uint32_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _AGENT_H */
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mipagent/agentID.c b/usr/src/cmd/cmd-inet/usr.lib/mipagent/agentID.c
new file mode 100644
index 0000000000..e69aadc53b
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mipagent/agentID.c
@@ -0,0 +1,216 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999-2000 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * file: agentID.c
+ *
+ * This files contains all of the routines necessary to
+ * manage the Mobile-IP Replay protection mechanisms.
+ */
+
+#include "mip.h"
+#include "agent.h"
+
+/*
+ * IDfreshnessSlack contains the number of seconds that
+ * we allow as a difference between our clock and the
+ * mobile node's clock when timestamp-based replay
+ * protection is used.
+ */
+extern int IDfreshnessSlack;
+extern uint32_t getRandomValue();
+extern uint32_t CurrentTimeNTPSec();
+
+
+/*
+ * Function: HAinitID
+ *
+ * Arguments: IDHigh - High order 32 bit ID
+ * IDLow - Low order 32 bit ID
+ * ReplayStyle - Replay type.
+ *
+ * Description: This function is called by the Home Agent
+ * to initialize a Mobile Node's replay
+ * identifier.
+ *
+ * Returns:
+ */
+void
+HAinitID(uint32_t *IDHigh, uint32_t *IDLow, int ReplayStyle)
+{
+ if (ReplayStyle == TIMESTAMPS) {
+ *IDHigh = CurrentTimeNTPSec() - IDfreshnessSlack;
+ *IDLow = getRandomValue();
+ } else {
+ *IDHigh = 0;
+ *IDLow = 0;
+ }
+}
+
+
+/*
+ * Function: isIDgreater
+ *
+ * Arguments: StoredIDHigh - Locally stored high order 32 bit replay ID
+ * StoredIDLow - Locally stored low order 32 bit replay ID
+ * IDHigh - High order 32 bit replay ID
+ * IDLow - Low order 32 bit replay ID
+ *
+ * Description: This function will return TRUE if the ID received
+ * by the Mobile Node is higher than the value stored
+ * locally.
+ *
+ * Returns: boolean_t, TRUE if value is greater than stored value.
+ */
+static boolean_t
+isIDgreater(uint32_t StoredIDHigh, uint32_t StoredIDLow,
+ uint32_t IDHigh, uint32_t IDLow)
+{
+ if ((IDHigh > StoredIDHigh) ||
+ ((IDHigh == StoredIDHigh) && (IDLow > StoredIDLow)))
+ return (_B_TRUE);
+ else
+ return (_B_FALSE);
+}
+
+
+/*
+ * Function: isIDfresh
+ *
+ * Arguments: IDHigh - High order 32 bit replay ID
+ * IDLow - Low order 32 bit replay ID
+ *
+ * Description: This value will compare the ID received
+ * with the local NTP time. Specifically, we
+ * will check if the time sent by the Mobile Node
+ * is within the current time +/- our configured
+ * clock skew.
+ *
+ * Returns: boolean_t, TRUE if the time is within our window.
+ */
+/* ARGSUSED */
+static boolean_t
+isIDfresh(uint32_t IDHigh, uint32_t IDLow)
+{
+ long diff;
+
+ diff = (long)(IDHigh - CurrentTimeNTPSec());
+
+ if (diff < 0)
+ diff = (0 - diff);
+
+ return ((diff < IDfreshnessSlack) ? _B_TRUE : _B_FALSE);
+}
+
+
+/*
+ * Function: HAisIDok
+ *
+ * Arguments: StoredIDHigh - Locally stored high order 32 bit replay ID
+ * StoredIDLow - Locally stored low order 32 bit replay ID
+ * IDHigh - High order 32 bit replay ID
+ * IDLow - Low order 32 bit replay ID
+ * ReplayStyle - Replay type.
+ *
+ * Description: This routine will validate the Mobile Node's ID
+ * using the replay style configured within the Security
+ * Assocation.
+ *
+ * Returns: boolean_t, TRUE if the ID is valid
+ */
+boolean_t
+HAisIDok(uint32_t StoredIDHigh, uint32_t StoredIDLow,
+ uint32_t IDHigh, uint32_t IDLow, int ReplayStyle)
+{
+ if (ReplayStyle == TIMESTAMPS) {
+ if (isIDgreater(StoredIDHigh, StoredIDLow, IDHigh, IDLow) &&
+ isIDfresh(IDHigh, IDLow))
+ return (_B_TRUE);
+ else
+ return (_B_FALSE);
+ } else if (ReplayStyle == NONE) {
+ return (_B_TRUE);
+ } else {
+ return (_B_FALSE);
+ }
+}
+
+
+/*
+ * Function: HAnewID
+ *
+ * Arguments: newIDHigh - Locally stored high order 32 bit replay ID
+ * newIDLow - Locally stored low order 32 bit replay ID
+ * IDHigh - High order 32 bit replay ID
+ * IDLow - Low order 32 bit replay ID
+ * ReplayStyle - Replay type.
+ * IDmatched - specifies whether the ID provided
+ * should be used.
+ *
+ * Description: This function will update the locally stored ID
+ *
+ * Returns:
+ */
+void
+HAnewID(uint32_t *newIDHigh, uint32_t *newIDLow, uint32_t IDHigh,
+ uint32_t IDLow, int ReplayStyle, boolean_t IDmatched)
+{
+ if (ReplayStyle == TIMESTAMPS) {
+ *newIDHigh = IDmatched ? IDHigh : CurrentTimeNTPSec();
+ } else {
+ *newIDHigh = 0;
+ }
+
+ *newIDLow = IDLow;
+}
+
+
+/*
+ * Function: HAstoreID
+ *
+ * Arguments: newIDHigh - Locally stored high order 32 bit replay ID
+ * newIDLow - Locally stored low order 32 bit replay ID
+ * IDHigh - High order 32 bit replay ID
+ * IDLow - Low order 32 bit replay ID
+ * ReplayStyle - Replay type.
+ * IDmatched - specifies whether the ID provided
+ * should be used.
+ *
+ * Description: This function will store the IDs locally
+ *
+ * Returns:
+ */
+void
+HAstoreID(uint32_t *StoredIDHigh, uint32_t *StoredIDLow, uint32_t IDHigh,
+ uint32_t IDLow, int ReplayStyle, boolean_t IDmatched)
+{
+ if (((ReplayStyle == TIMESTAMPS) && IDmatched)) {
+ *StoredIDHigh = IDHigh;
+ *StoredIDLow = IDLow;
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mipagent/agentInit.c b/usr/src/cmd/cmd-inet/usr.lib/mipagent/agentInit.c
new file mode 100644
index 0000000000..015d6b0517
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mipagent/agentInit.c
@@ -0,0 +1,2798 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * file: agentInit.c
+ *
+ * This file contains the functions necessary to read
+ * and parse the /etc/inet/mipagent.conf configuration
+ * file.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <signal.h>
+#include <errno.h>
+#include <alloca.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <sys/resource.h>
+#include <net/pfkeyv2.h> /* ipsec */
+#include <net/if.h> /* ipsec */
+
+#include <sys/utsname.h>
+
+#include "mip.h"
+#include "agent.h"
+#include "conflib.h"
+#include "pool.h"
+#include "setup.h"
+#include "mipagentstat_door.h"
+
+/* Common to all mobility agents */
+extern struct hash_table maAdvConfigHash;
+extern char maNai[MAX_NAI_LENGTH];
+
+/* Foreign Agent specific data structures. */
+extern struct hash_table faVisitorHash;
+
+/* Home Agent specific data structures. */
+extern struct hash_table haMobileNodeHash;
+
+/* This table stores all of the Security Violations */
+extern struct hash_table mipSecViolationHash;
+
+
+/* Home Agent specific data structures. */
+#ifdef FIREWALL_SUPPORT
+extern DomainInfo domainInfo;
+#endif /* FIREWALL_SUPPORT */
+
+extern uint32_t subagent_addr;
+
+/*
+ * This table stores all of the Security Assocations
+ */
+extern HashTable mipSecAssocHash;
+
+/*
+ * This table has one entry for each known Mobility Agent
+ */
+extern HashTable mipAgentHash;
+
+/*
+ * This table has one entry for each pool defined in the config file
+ */
+extern HashTable mipPoolHash;
+
+/*
+ * This table has one entry for each active tunnel number
+ */
+extern HashTable mipTunlHash;
+
+/* Other external declarations */
+extern int logVerbosity;
+#ifdef RADIUS_ENABLED
+extern int radiusEnabled;
+extern char radiusSharedLibrary[];
+#endif /* RADIUS_ENABLED */
+extern int visitorEntryHighWaterMark;
+extern int visitorEntryLowWaterMark;
+extern int IDfreshnessSlack;
+
+extern int advLifetime;
+extern int periodicInterval;
+
+extern boolean_t faNAIadv;
+extern boolean_t faChallengeAdv;
+extern boolean_t mfAuthRequired;
+extern boolean_t fhAuthRequired;
+extern boolean_t shutdown_flag;
+extern boolean_t daemonize;
+extern int performanceInterval;
+extern boolean_t disableSNMP;
+
+boolean_t ipsec_loaded = _B_FALSE; /* presume we're not secure */
+boolean_t ipsec_ah_loaded = _B_FALSE;
+boolean_t ipsec_esp_loaded = _B_FALSE;
+
+extern char *ipsec_policy_string[];
+extern char *validIPsecAction[];
+
+/* AAA Globals */
+extern unsigned short gbl_aaaPort;
+extern char gbl_aaaHost[];
+extern AAA_Protocol_Code aaaProtocol;
+
+/*
+ * Default Values...
+ */
+extern uint32_t defaultPool;
+extern uint32_t defaultNodeSPI;
+
+extern char *ntoa(uint32_t, char *);
+extern char *sprintTime(char *, int);
+extern char *sprintRelativeTime(char *, int);
+extern void HAinitID(uint32_t *, uint32_t *, int);
+extern int hexConvert(char *, int, char *);
+#ifdef FIREWALL_SUPPORT
+extern void printProtectedDomainInfo(DomainInfo);
+#endif /* FIREWALL_SUPPORT */
+
+/* OS-specific initialization for Mobile IP */
+extern void OScleanup();
+
+/* Called when SIGUSR1 is received to save state. */
+extern int saveAgentState(void);
+
+extern void delFAVEptr(FaVisitorEntry *, boolean_t, uint32_t);
+extern void delHABEent(HaMobileNodeEntry *, HaBindingEntry *);
+
+extern int killDispatcherTaskThread();
+extern int killPeriodicTaskThread();
+extern int killSNMPTaskThread();
+extern int killAAATaskThread();
+extern int killStatServer();
+extern void printMaAdvConfigHash(struct hash_table *);
+
+extern void randomInit();
+extern int InitNet();
+extern void printHaMobileNodeHash(struct hash_table *);
+
+extern int getdomainname(char *, int);
+
+extern int installIPsecPolicy(char *);
+extern int removeIPsecPolicy(char *);
+extern int formIPsecBits(int, char *, char *, char *, size_t);
+
+typedef struct {
+ char *string;
+ uint32_t value;
+} Str2Int;
+
+/* YES(x) simply checks to see if the string begins with a y. */
+#define YES(x) ((tolower(x) == 'y') ? 1 : 0)
+
+/*
+ * FA(x) needs to check if the FA is included in the setting. This happens
+ * if the setting is "yes", "both" or "fa" [and NOT when the setting is
+ * "no", "none", or "ha").
+ */
+#define FA(x) (((tolower(x) == 'y') || \
+ (tolower(x) == 'b') || \
+ (tolower(x) == 'f')) ? 1 : 0)
+
+/*
+ * Macros to produce a quoted string containing the value of a
+ * preprocessor macro. For example, if SIZE is defined to be 256,
+ * VAL2STR(SIZE) is "256". This is used to construct format
+ * strings for scanf-family functions below.
+ */
+#define QUOTE(x) #x
+#define VAL2STR(x) QUOTE(x)
+
+/* The analogous HA(x) is not needed [yet] */
+
+#define MAX_BUFFER_SIZE 1024
+#define MAX_KEY_STRING_LEN 256
+#define MAX_TAG_SIZE 30
+
+
+/*
+ * Function: daemonInit
+ *
+ * Arguments:
+ *
+ * Description: This function will deamonize the agent
+ *
+ * Returns: int, -1 if the deamon could not be started.
+ */
+static int
+daemonInit()
+{
+ switch (fork()) {
+ case -1:
+ perror("mipagent: can not fork");
+ return (-1);
+ case 0:
+ break;
+ default:
+ exit(0);
+ }
+
+ /*
+ * Close existing file descriptors, open "/dev/null" as
+ * standard input, output, and error, and detach from
+ * controlling terminal.
+ */
+ closefrom(0);
+ (void) open("/dev/null", O_RDONLY);
+ (void) open("/dev/null", O_WRONLY);
+ (void) dup(1);
+ (void) setsid();
+
+ (void) chdir("/"); /* change working directory */
+ (void) umask(0); /* clear file mode creation mask */
+ return (0);
+}
+
+
+
+/*
+ * Function: get_ipsec_support
+ *
+ * Parameters: s the PF_KEY socket
+ * struct sadb_msg *msg pointer to message buffer
+ * uint8_t satype which SA type (ah, or esp)
+ * int seq The sequence number to increment/use.
+ *
+ * Description: Builds, and writes an sadb_message to s, then reads until it
+ * gets the appropriate message, and returns the length so the caller
+ * knows how many bytes to parse.
+ *
+ * Returns: -1 on error.
+ * The length of what was put in msg on success.
+ */
+int
+get_ipsec_support(int s, struct sadb_msg *msg, uint8_t satype, int seq)
+{
+ /* Note: parsing the <base, supported> msg requires an ipsec_req_t */
+ int len; /* what we'll return */
+ uint32_t mypid = getpid();
+
+ msg->sadb_msg_version = PF_KEY_V2;
+ msg->sadb_msg_type = SADB_REGISTER;
+ msg->sadb_msg_satype = satype;
+ msg->sadb_msg_len = SADB_8TO64(sizeof (*msg));
+ msg->sadb_msg_reserved = msg->sadb_msg_errno = 0;
+ msg->sadb_msg_seq = seq++;
+ msg->sadb_msg_pid = mypid;
+
+ if ((len = write(s, (void *)msg, sizeof (*msg))) < 0) {
+ syslog(LOG_WARNING, "Can't determine state of ipsec support.");
+ return (-1);
+ }
+
+ /* don't let stale data in msg confuse our read! */
+ bzero(msg, sizeof (*msg));
+
+ /* send a <base> message to the kernel, get back <base, supported> */
+ do {
+ len = read(s, msg, MAX_IPSEC_GET_SIZE);
+ } while (msg->sadb_msg_type != SADB_REGISTER &&
+ msg->sadb_msg_pid != mypid &&
+ msg->sadb_msg_seq != seq);
+
+ return (msg->sadb_msg_errno != 0 ? -1 : len);
+}
+
+/*
+ * Function: IsIPsecLoaded()
+ *
+ * Description: uses a PF_KEY socket to determine if AH and/or ESP are loaded.
+ * the globals ipsec_ah_loaded, and ipsec_esp_loaded are set directly for
+ * reference during init.
+ */
+boolean_t
+IsIPsecLoaded()
+{
+ uint64_t msg_buffer[MAX_IPSEC_GET_SIZE] = { 0 };
+ struct sadb_msg *msg = (struct sadb_msg *)msg_buffer;
+ int s;
+ uint32_t seq = FINE_STRUCT_CONST;
+
+ if ((s = socket(PF_KEY, SOCK_RAW, PF_KEY_V2)) < 0) {
+ /* Curious. We're root, or mipagent would've stopped by now */
+ return (_B_FALSE);
+ }
+
+ /* AH support? */
+ if (get_ipsec_support(s, (void *)msg, SADB_SATYPE_AH, seq) > 0)
+ /* we got something back, so ah is supported */
+ ipsec_ah_loaded = _B_TRUE;
+
+ /* ESP support? */
+ if (get_ipsec_support(s, (void *)msg, SADB_SATYPE_ESP, seq) > 0)
+ /* we got something back, so esp is supported */
+ ipsec_esp_loaded = _B_TRUE;
+
+ (void) close(s);
+ return (_B_TRUE);
+}
+
+
+
+/*
+ * Funcition: setIPsecSAFlags
+ *
+ * Arguments: ipsr_p - pointer to an ipsec_req_t containing the ipsec policy.
+ * type - the type of service this policy is for (request, ...).
+ *
+ * Description: setIPsecSAFlags looks at the details of the ipsec policy, and
+ * returns a bit-field of what the policy is requiring in terms of
+ * AH and ESP support.
+ *
+ * Returns: The bit field indicating what of AH and ESP type is requiring.
+ */
+uint8_t
+setIPsecSAFlags(ipsec_req_t *ipsr_p, int type)
+{
+ uint8_t bitfield = 0;
+
+ /* type defines how we set our bits. For now use low-order bits */
+ if (ipsr_p->ipsr_auth_alg)
+ bitfield |= IPSEC_REQUEST_AH;
+
+ if ((ipsr_p->ipsr_esp_alg) || (ipsr_p->ipsr_esp_auth_alg))
+ bitfield |= IPSEC_REQUEST_ESP;
+
+ /* Shift depending on the actual type */
+ switch (type) {
+ case IPSEC_REQUEST:
+ return (bitfield);
+
+ case IPSEC_REPLY:
+ return (bitfield << 2);
+
+ case IPSEC_TUNNEL:
+ return (bitfield << 4);
+
+ case IPSEC_REVERSE_TUNNEL:
+ return (bitfield << 6);
+
+ default:
+ syslog(LOG_WARNING, "setIPsecSAFlags: unknown IPsec "
+ "configuration type %d\n", type);
+ break;
+ }
+
+ return (0);
+}
+
+/*
+ * ParseAgentIPsecPolicy
+ *
+ * Arguments: mae - A pointer to a MobilityAgentEntry struct containing
+ * information about an agent-peer with which we may have
+ * to add IPsec security associations.
+ * ipsType - Identifies the type of IPsec Policy we're parsing.
+ * ipsp - The string that may contain one or two IPsec Policies.
+ * Keywords to look for are 'apply' and 'permit', and if
+ * both appear, the policies MUST be separated by a ':'.
+ *
+ * Description: We need to sanity check the policies pointed to by ipsp, and
+ * put them into the correct placeholder in the MobilityAgentEntry
+ * structure. We return the relavent bits in mpIPsecFlags so the
+ * caller knows which policies were found.
+ *
+ * Note: there can never be more than two bits set at once, one
+ * for the permit, and one for the apply of this ipsType,
+ * so a return of -1 is quite revealing!
+ */
+int
+ParseAgentIPsecPolicy(MobilityAgentEntry *mae, int ipsType, char *ipsp)
+{
+ uint8_t ipsFlags = 0; /* (will become) what we'll return */
+ int i;
+ char *p, *freeP, *lasts;
+ char *ipsPolicy[IPSEC_ORDER] = {NULL, NULL};
+ int policies_found = 0;
+
+ /* Oh, sanity... */
+ if ((mae == NULL) || (ipsp == NULL))
+ return (-1);
+
+ if ((p = strdup(ipsp)) == NULL)
+ return (-1);
+
+ freeP = p; /* strtok_r() is destructive! */
+
+ /* lets just deal with lower case */
+ for (i = 0; i < strlen(ipsp); i++)
+ ipsp[i] = tolower(ipsp[i]);
+
+ /*
+ * ipsec policies can only be passed one-at-a-time, so thunk if needed.
+ *
+ * Note: we don't know which order the user's put his actions in,
+ * so we have to always start at the begining of validIPsecAction[]s.
+ */
+ while (strtok_r(p, IPSP_SEPARATOR, &lasts) != NULL) {
+
+ if (++policies_found > IPSEC_ORDER) {
+ char peerAddr[IPv4_ADDR_LEN];
+ (void) ntoa(mae->maAddr, peerAddr);
+
+ /* Tell the user they have too many */
+ syslog(LOG_CRIT, "[Address %s] %s: too many actions.",
+ peerAddr, IPSEC_POLICY_STRING(ipsType));
+ (void) free(freeP);
+ return (-1);
+ }
+
+ for (i = 0; validIPsecAction[i] != NULL; i++) {
+ /*
+ * Fill a corresponding empty array with a policy
+ * containing this validIPsecAction[].
+ */
+ if ((ipsPolicy[i] == NULL) &&
+ ((ipsPolicy[i] = strstr(p, validIPsecAction[i]))
+ != NULL)) {
+ /* something new to parse */
+ char peerAddr[IPv4_ADDR_LEN];
+
+ (void) ntoa(mae->maAddr, peerAddr);
+
+ switch (ipsType) {
+ case IPSEC_REQUEST:
+ if (isIPsecPolicyValid(ipsPolicy[i],
+ &(mae->maIPsecRequestIPSR[i]))
+ != _B_TRUE) {
+ (void) free(freeP);
+ return (-1);
+ }
+
+ /* policy is OK (and parsed)! */
+ if (formIPsecBits(REQUEST(i),
+ peerAddr, ipsPolicy[i],
+ &mae->maIPsecRequest[i][0],
+ sizeof (mae->maIPsecRequest[i]))
+ < 0) {
+ (void) free(freeP);
+ return (-1);
+ }
+
+ /* set SA flags */
+ mae->maIPsecSAFlags[i] |=
+ setIPsecSAFlags(
+ &mae->maIPsecRequestIPSR[i],
+ ipsType);
+
+ /* Remember we did this */
+ ipsFlags |= REQUEST(i);
+ break;
+
+ case IPSEC_REPLY:
+ if (isIPsecPolicyValid(ipsPolicy[i],
+ &(mae->maIPsecReplyIPSR[i]))
+ != _B_TRUE) {
+ (void) free(freeP);
+ return (-1);
+ }
+ /* policy is OK (and parsed)! */
+ if (formIPsecBits(REPLY(i),
+ peerAddr, ipsPolicy[i],
+ &mae->maIPsecReply[i][0],
+ sizeof (mae->maIPsecReply[i]))
+ < 0) {
+ (void) free(freeP);
+ return (-1);
+ }
+
+ /* set SA flags */
+ mae->maIPsecSAFlags[i] |=
+ setIPsecSAFlags(
+ &mae->maIPsecReplyIPSR[i],
+ ipsType);
+
+ /* Remember we did this */
+ ipsFlags |= REPLY(i);
+ break;
+
+ /*
+ * For the tunnel policies, we pass the
+ * IPSR bits into the ioctl(), so we
+ * don't need to form the ASCII policy.
+ */
+ case IPSEC_TUNNEL:
+ if (isIPsecPolicyValid(ipsPolicy[i],
+ &(mae->maIPsecTunnelIPSR[i]))
+ != _B_TRUE) {
+ (void) free(freeP);
+ return (-1);
+ }
+ /* policy is OK (and parsed)! */
+
+ /* set SA flags */
+ mae->maIPsecSAFlags[i] |=
+ setIPsecSAFlags(
+ &mae->maIPsecTunnelIPSR[i],
+ ipsType);
+
+ /* Remember we did this */
+ ipsFlags |= TUNNEL(i);
+ break;
+
+ case IPSEC_REVERSE_TUNNEL:
+ if (isIPsecPolicyValid(ipsPolicy[i],
+ &(mae->maIPsecReverseTunnelIPSR[i]))
+ != _B_TRUE) {
+ (void) free(freeP);
+ return (-1);
+ }
+ /* policy is OK (and parsed)! */
+
+ /* set SA flags */
+ mae->maIPsecSAFlags[i] |=
+ setIPsecSAFlags(
+ &mae->maIPsecReverseTunnelIPSR[i],
+ ipsType);
+
+ /* Remember we did this */
+ ipsFlags |= REVERSE_TUNNEL(i);
+ break;
+
+ }
+ }
+ }
+
+ /* setup for the next policy */
+ p = NULL;
+ }
+
+ /* we're out of policies */
+ (void) free(freeP);
+
+ /* reveal what we found */
+ return (ipsFlags);
+
+}
+
+
+/*
+ * Function: setupSPI
+ *
+ * Arguments: configFile - Pointer to config file
+ * SPIstr - Pointer to the section
+ *
+ * Description: This function will process an [SPI x] section.
+ * If the section is valid, we will create the
+ * static security assocation entry.
+ *
+ * Returns: int - 0 if successful.
+ */
+static int
+setupSPI(char *configFile, char *SPIstr)
+{
+ int rc;
+ int i;
+ char buffer[MAX_BUFFER_SIZE+1];
+ /* Agent Node */
+ char Key[MAX_KEY_STRING_LEN];
+ int32_t SPI, replayMethod = NONE;
+ char mipSecKey[MAX_KEY_LEN];
+ MipSecAssocEntry *entry;
+ Str2Int replayMethods[] = {
+ { "none", NONE },
+ { "timestamps", TIMESTAMPS },
+ { NULL, NULL }};
+
+ /* SPI Definition */
+ rc = sscanf(SPIstr, "%" VAL2STR(MAX_BUFFER_SIZE) "s %d", buffer, &SPI);
+ if (rc != 2) {
+ syslog(LOG_CRIT, "Error: Invalid SPI Section [%s]", SPIstr);
+ return (-1);
+ }
+ rc = GetPrivateProfileString(SPIstr, "ReplayMethod",
+ "", buffer, MAX_KEY_STRING_LEN, configFile);
+ /*
+ * GetPrivateProfileString() returns "-2" for all
+ * configuration file loading and parsing errors.
+ * Hence, to catch those errors, there is a check
+ * for rc == -2 here.
+ */
+ if (rc == -2) {
+ syslog(LOG_ERR, " Unable to read %s tags :%s", SPIstr,
+ ErrorString);
+ return (-1);
+ }
+ if (*buffer == '\0') {
+ syslog(LOG_CRIT, "Problem reading SPI <%d>. No ReplayMethod", SPI);
+ return (-1);
+ }
+
+ /* Check the replay method */
+ for (i = 0; replayMethods[i].string; i++) {
+ if (!strcasecmp(buffer, replayMethods[i].string)) {
+ replayMethod = replayMethods[i].value;
+ break;
+ }
+ }
+ if (replayMethods[i].string == NULL) {
+ syslog(LOG_CRIT, "Error: Invalid replay method in section [%s]."
+ "Possible values are (none or timestamps)", SPIstr);
+ return (-1);
+ }
+
+ rc = GetPrivateProfileString(SPIstr, "Key", "", Key,
+ MAX_KEY_STRING_LEN, configFile);
+ if (rc == -2) {
+ syslog(LOG_ERR, " Unable to read %s tags :%s", SPIstr,
+ ErrorString);
+ return (-1);
+ }
+
+ if (Key[0] == 0) {
+ syslog(LOG_CRIT, "Problem reading SPI <%d>. No Key", SPI);
+ return (-1);
+ }
+
+ if (hexConvert((char *)mipSecKey,
+ strlen(Key)/2, Key) < 0) {
+ syslog(LOG_CRIT, "Problem Converting key <%s>", Key);
+ return (-2);
+ }
+
+ /*
+ * Now we create the Security Assocation
+ */
+ entry = CreateSecAssocEntry(_B_FALSE, SPI, replayMethod, MD5,
+ PREFIXSUFFIX, strlen(Key)/2, mipSecKey, 0);
+
+ if (entry == NULL) {
+ syslog(LOG_CRIT, "Unable to create SPI %d", SPI);
+ return (-2);
+ }
+
+ /*
+ * The Create function ends up locking the node, so
+ * we need to free it.
+ */
+ (void) rw_unlock(&entry->mipSecNodeLock);
+
+ return (0);
+
+} /* setupSPI */
+
+/*
+ * Function: setupAddress
+ *
+ * Arguments: configFile - Pointer to config file
+ * Address - Pointer to the section
+ *
+ * Description: This function will process an [Address x] section.
+ * If the section is valid, we will create the
+ * static Mobile Node or Mobility Agent entry.
+ *
+ * This function supports three different types
+ * of addresses:
+ * x.x.x.x - Normal IP Address format
+ * xxx@yyy - Network Access Identifier (up to 256 characters)
+ * Default-Node - Default Mobile Node Config
+ *
+ * Returns: int - 0 if successful.
+ */
+static int
+setupAddress(char *configFile, char *Address)
+{
+ int32_t rc;
+ char buffer[MAX_BUFFER_SIZE+1];
+ char nodeID[MAX_NAI_LENGTH+1];
+ int32_t SPI, Pool;
+ char *NAI = NULL;
+
+ /* Check what we were passed!!! */
+ if ((configFile == NULL) || (Address == NULL)) {
+ syslog(LOG_CRIT,
+ "Error: Invalid Address passed to setupAddress.");
+ return (-1);
+ }
+
+ /*
+ * Mobile Node Definition -
+ */
+ rc = sscanf(Address,
+ "%" VAL2STR(MAX_BUFFER_SIZE) "s "
+ "%" VAL2STR(MAX_NAI_LENGTH) "s",
+ buffer, nodeID);
+ if (rc != 2) {
+ syslog(LOG_CRIT, "Error: Invalid Address Section <%s>.", Address);
+ return (-1);
+ }
+
+ if (strlen(nodeID) >= MAX_NAI_LENGTH) {
+ syslog(LOG_CRIT,
+ "Error: NAI too long in [%s...] section of config file.\n",
+ Address);
+ return (-1);
+ }
+
+ rc = GetPrivateProfileString(Address, "Type", "", buffer,
+ MAX_TAG_SIZE, configFile);
+ if (rc == -2) {
+ syslog(LOG_ERR, " Unable to read %s tags: %s", Address,
+ ErrorString);
+ return (-1);
+ }
+ if (!*buffer) {
+ syslog(LOG_CRIT, "Error: Must specify \"Type\" in addresses."
+ "Error in section [%s]", Address);
+ return (-1);
+ }
+
+ /* Figure out what type of address this is */
+ if (!strncasecmp(nodeID, "Node-Default", 12)) {
+ /*
+ * This is a default entry.... Fun and Joy!!
+ */
+ SPI = GetPrivateProfileInt(Address, "SPI", -1, configFile);
+ if (SPI == -1) {
+ syslog(LOG_CRIT,
+ "Problem reading MobileNode <%s>. No SPI", Address);
+ return (-1);
+ }
+ Pool = GetPrivateProfileInt(Address, "Pool", -1,
+ configFile);
+
+ /*
+ * The default SPI MUST be present, however the Pool is
+ * optional.
+ */
+ defaultNodeSPI = SPI;
+ if (Pool != -1) {
+ defaultPool = Pool;
+ }
+ /* Figure out what type of address this is */
+ } else if (!strncasecmp(buffer, "Node", 4)) {
+ HaMobileNodeEntry *entry;
+
+ SPI = GetPrivateProfileInt(Address, "SPI", -1, configFile);
+ if (SPI == -1) {
+ syslog(LOG_CRIT,
+ "Problem reading MobileNode <%s>. No SPI",
+ Address);
+ return (-1);
+ }
+
+ NAI = strchr(nodeID, '@');
+ Pool = GetPrivateProfileInt(Address, "Pool", -1, configFile);
+
+ /*
+ * We don't need a pool every time we have an NAI (FA-only
+ * config, for example; NAIs are used for more than obtaining
+ * an address these days). If we end up being this node's HA
+ * and it needs an address, but there's none to give it (that
+ * is, we have a broken config, read: user error), then we'll
+ * reply with 130 "insufficient resources", and (hopefully)
+ * log the appropriate error in the user-feedback nicities.
+ */
+ if (Pool != -1) {
+ /*
+ * If the MN will need a pool, it implies we'll need
+ * an NAI for identification!
+ */
+ if (NAI == NULL) {
+ syslog(LOG_CRIT,
+ "Problem reading MobileNode <%s>. "
+ "Pool invalid for non-NAI addresses", Address);
+ return (-1);
+ }
+ } else {
+ Pool = 0; /* don't pass -1 into Create...(). */
+ }
+
+ /*
+ * And we create the Mobile Node
+ */
+ if (NAI != NULL) {
+ entry = CreateMobileNodeEntry(_B_FALSE, INADDR_ANY,
+ nodeID, strlen(nodeID), 0, SPI, NULL, Pool);
+ } else {
+ entry = CreateMobileNodeEntry(_B_FALSE,
+ inet_addr(nodeID), NULL, 0, 0, SPI, NULL, Pool);
+ }
+
+ if (entry == NULL) {
+ syslog(LOG_CRIT,
+ "Unable to create MN-HA for %s", nodeID);
+ return (-2);
+ }
+
+ /*
+ * A successful Create...() function ends up locking the node,
+ * so we need to unlock it.
+ */
+ (void) rw_unlock(&entry->haMnNodeLock);
+
+ } else if (strncasecmp(buffer, "Agent", 5) == 0) {
+ /* Agent Node */
+ int32_t SPI;
+ char IPSPolicy[MAX_IPSEC_POLICY_SIZE] = "";
+ MobilityAgentEntry *maEntry;
+
+ NAI = strchr(nodeID, '@');
+ if (NAI) {
+ syslog(LOG_CRIT,
+ "Error: NAIs invalid for Agent Addresses");
+ return (-1);
+ }
+
+ /*
+ * The assumption, then, is nodeID is a dotted-decimal
+ * address. Even if not, we can use it to identify to
+ * the user which [Address *] section we're flagging.
+ *
+ * The SPI is for identifying the usual MD5 agent-agent
+ * SA/authentication, and has nothing to do with the SPI
+ * used by ipsec!
+ */
+ SPI = GetPrivateProfileInt(Address, "SPI", -1, configFile);
+ if (SPI == -1) {
+ syslog(LOG_CRIT,
+ "Problem reading FA-HA-auth Node <%s>. No SPI",
+ nodeID);
+ return (-1);
+ }
+
+ maEntry = CreateMobilityAgentEntry(_B_FALSE,
+ (ipaddr_t)inet_addr(nodeID), SPI, 0);
+
+ if (maEntry == NULL) {
+ syslog(LOG_CRIT,
+ "Unable to create HA-FA for %s", nodeID);
+ return (-2);
+ }
+
+ /* Check to see if there are any IPsec policies to apply */
+ if (GetPrivateProfileString(Address, "IPsecRequest",
+ "", IPSPolicy, MAX_IPSEC_POLICY_SIZE, configFile) == -2) {
+ syslog(LOG_CRIT,
+ "Catastrophic failure while trying to get "
+ "IPsecRequest configuration in the %s section "
+ "of %s. Please verify file integrity.",
+ Address, configFile);
+ return (-1);
+ }
+
+ if (*IPSPolicy) {
+ /*
+ * The first time we could have found an IPSec policy
+ * as restoring our state is done after we init. Note:
+ * we don't support NAIs, or MA-MA authenticators.
+ */
+ if (ParseAgentIPsecPolicy(maEntry, IPSEC_REQUEST,
+ IPSPolicy) < 0) {
+ /* syslog the error, and fail */
+ syslog(LOG_CRIT, "Problem parsing "
+ "IPsecRequest in [%s] section of %s.",
+ Address, configFile);
+
+ /* mipagent fails in these cases. */
+ return (-1);
+ }
+ }
+
+ if (GetPrivateProfileString(Address, "IPsecReply",
+ "", IPSPolicy, MAX_IPSEC_POLICY_SIZE, configFile) == -2) {
+ syslog(LOG_CRIT,
+ "Catastrophic failure while trying to get "
+ "IPsecReply configuration in the %s section "
+ "of %s. Please verify file integrity.",
+ Address, configFile);
+ return (-1);
+ }
+
+ if (*IPSPolicy) {
+ /* parse it into it's individual properties */
+ if (ParseAgentIPsecPolicy(maEntry, IPSEC_REPLY,
+ IPSPolicy) < 0) {
+ /* syslog the error, and fail. */
+ syslog(LOG_CRIT, "Problem parsing IPsecReply"
+ " policy in [%s] section of %s",
+ Address, configFile);
+
+ /* mipagent fails in these cases. */
+ return (-1);
+ }
+ }
+
+ if (GetPrivateProfileString(Address, "IPsecTunnel",
+ "", IPSPolicy, MAX_IPSEC_POLICY_SIZE, configFile) == -2) {
+ syslog(LOG_CRIT,
+ "Catastrophic failure while trying to get "
+ "IPsecTunnel configuration in the %s section of %s."
+ " Please verify file integrity.",
+ Address, configFile);
+ return (-1);
+ }
+
+
+ if (*IPSPolicy) {
+ /* parse it into it's individual properties */
+ if (ParseAgentIPsecPolicy(maEntry, IPSEC_TUNNEL,
+ IPSPolicy) < 0) {
+ /* syslog the error, and fail. */
+ syslog(LOG_CRIT, "Problem parsing IPsecTunnel"
+ " policy in [%s] section of %s",
+ Address, configFile);
+
+ /* mipagent fails in these cases. */
+ return (-1);
+ }
+ }
+
+ /*
+ * ipsec only supports symmetric tunnel policies, so we can't
+ * support asymmetric tunnel policies yet. When ipsec supports
+ * multiple per-socket policies, that will (likely) change.
+ * Note that if this GetPrivateProfileString() returns -2, we
+ * don't care.
+ */
+ (void) GetPrivateProfileString(Address, "IPsecReverseTunnel",
+ "", IPSPolicy, MAX_IPSEC_POLICY_SIZE, configFile);
+
+ if (*IPSPolicy)
+ /*
+ * If the user tried the obvious tag, then at least
+ * warn them asymmetric tunnel policies are not
+ * supported at this time.
+ */
+ syslog(LOG_WARNING, "Found [%s] setting for "
+ "IPSecReverseTunnel. Asymmetric IPsec tunnel "
+ "policies are not supported at this time. Setting"
+ " reverse tunnel policies to conform to forward"
+ " tunnel settings.", Address);
+
+ /* Make the tunnel policies symmetric (for mipagentstat) */
+ if (maEntry->maIPsecSAFlags[IPSEC_APPLY] & IPSEC_TUNNEL_AH)
+ /* tunnel apply = HA, so we set reverse tunnel permit */
+ maEntry->maIPsecSAFlags[IPSEC_PERMIT] |=
+ IPSEC_REVERSE_TUNNEL_AH;
+
+ if (maEntry->maIPsecSAFlags[IPSEC_APPLY] & IPSEC_TUNNEL_ESP)
+ /* tunnel apply = HA, so we set reverse tunnel permit */
+ maEntry->maIPsecSAFlags[IPSEC_PERMIT] |=
+ IPSEC_REVERSE_TUNNEL_ESP;
+
+ if (maEntry->maIPsecSAFlags[IPSEC_PERMIT] & IPSEC_TUNNEL_AH)
+ /* tunnel permit = FA, so we set reverse tunnel apply */
+ maEntry->maIPsecSAFlags[IPSEC_APPLY] |=
+ IPSEC_REVERSE_TUNNEL_AH;
+
+ if (maEntry->maIPsecSAFlags[IPSEC_PERMIT] & IPSEC_TUNNEL_ESP)
+ /* tunnel permit = FA, so we set reverse tunnel apply */
+ maEntry->maIPsecSAFlags[IPSEC_APPLY] |=
+ IPSEC_REVERSE_TUNNEL_ESP;
+
+ /* if the user wants to use ipsec, see if we can */
+ if ((ipsec_loaded == _B_FALSE) &&
+ (maEntry->maIPsecSAFlags[IPSEC_APPLY] ||
+ maEntry->maIPsecSAFlags[IPSEC_PERMIT])) {
+ /* load, if not loaded */
+ if (IsIPsecLoaded() == _B_FALSE)
+ syslog(LOG_WARNING,
+ "Can't determine ipsec state. Configured "
+ "[%s] IPsec policies may fail to install!",
+ Address);
+ else
+ ipsec_loaded = _B_TRUE;
+ }
+
+ /* Is AH or ESP protection requested, but not offered? */
+ if ((!ipsec_ah_loaded) &&
+ IPSEC_ANY_AH(maEntry->maIPsecSAFlags[IPSEC_APPLY] |
+ maEntry->maIPsecSAFlags[IPSEC_PERMIT]))
+ /* --><-- user wants, not available (now) */
+ syslog(LOG_WARNING, "[%s] is configured to use "
+ "IPsec AH protections, but AH is not "
+ "provided/loaded at this time.", Address);
+
+ if ((!ipsec_esp_loaded) &&
+ IPSEC_ANY_ESP(maEntry->maIPsecSAFlags[IPSEC_APPLY] |
+ maEntry->maIPsecSAFlags[IPSEC_PERMIT]))
+ /* --><-- user wants, not available (now) */
+ syslog(LOG_WARNING, "[%s] is configured to use "
+ "IPsec ESP protection, but ESP is not"
+ "provided/loaded at this time.", Address);
+
+ /*
+ * "IPsecRequest permit {properties}" are for HAs receiving
+ * registration requests from FAs, and so we need to be
+ * ready to receive these (they'll be unannounced).
+ * Pass down any of these policies now. Note: we don't check
+ * to see if it's been installed because we just read the
+ * policy, and we haven't called AgentRestoreState() yet!
+ */
+ if (IPSEC_REQUEST_ANY(maEntry->maIPsecSAFlags[IPSEC_PERMIT])) {
+ /*
+ * We found an IPsecRequest permit. Do whatever
+ * we do to install the ipsec policy.
+ */
+ if (installIPsecPolicy(
+ maEntry->maIPsecRequest[IPSEC_PERMIT]) < 0) {
+ syslog(LOG_CRIT,
+ "Could not install %s for [Address %s]: %s",
+ IPSEC_POLICY_STRING(IPSEC_REQUEST_PERMIT),
+ nodeID,
+ maEntry->maIPsecRequest[IPSEC_PERMIT]);
+
+ /* we're exiting, but unlock anyway */
+ (void) rw_unlock(&maEntry->maNodeLock);
+
+ /* mipagent fails to load at these times */
+ return (-1);
+ } else
+ /*
+ * success, set the flag. Note: this is NOT
+ * technically an agent-peer until we have
+ * a mobile node registered with it!
+ */
+ maEntry->maIPsecFlags |= IPSEC_REQUEST_PERMIT;
+ }
+
+ /* done processing type = agent, UNLOCK! */
+ (void) rw_unlock(&maEntry->maNodeLock);
+
+ return (0);
+ } else {
+ syslog(LOG_CRIT,
+ "Error: invalid type in section [%s]", Address);
+ return (-1);
+ }
+
+ return (0);
+
+} /* setupAddress */
+
+
+/*
+ * Function: setupInterface
+ *
+ * Arguments: configFile - Pointer to config file
+ * Interface - Pointer to the interface section
+ *
+ * Description: This function will process an [Interface x] section.
+ * If the section is valid, we will create the
+ * static Interface entry.
+ *
+ * Returns: int - 0 if successful.
+ */
+static int
+setupInterface(char *configFile, char *Interface)
+{
+ int32_t rc;
+ char buffer[MAX_BUFFER_SIZE];
+ /* Interface definition */
+ char dev[LIFNAMSIZ+1];
+ char devAddr[INET_ADDRSTRLEN+1];
+ int32_t ServicesFlags = ADV_IS_HOME_AGENT | ADV_IS_FOREIGN_AGENT;
+ uint8_t reverseTunnelAllowed = RT_NONE;
+ uint8_t reverseTunnelRequired = RT_NONE;
+ boolean_t PrefixFlags = _B_TRUE;
+ boolean_t advertiseOnBcast = _B_FALSE;
+ uint8_t advInitCount = 1;
+ int advInterval;
+ boolean_t advLimitUnsolicited = _B_FALSE;
+ int i;
+ /*
+ * We need a local regLifetime variable
+ */
+ int regLifetime;
+
+ Str2Int flagTable[] = {
+ { "homeAgent", ADV_IS_HOME_AGENT},
+ { "foreignAgent", ADV_IS_FOREIGN_AGENT},
+ { "registrationRequired", ADV_REGISTRATION_REQUIRED},
+#ifdef ENABLE_ALL_FLAGS
+ { "minEncap", ADV_MIN_ENCAP},
+ { "greEncap", ADV_GRE_ENCAP},
+ { "vjCompression", ADV_VJ_COMPRESSION},
+#endif
+ { "reverseTunnel", ADV_REVERSE_TUNNEL},
+ { NULL, 0 }};
+
+
+
+ /* Search the file for Advertisements tags */
+ for (i = 0; flagTable[i].string; i++) {
+ rc = GetPrivateProfileString(Interface, flagTable[i].string,
+ "", buffer, MAX_FN_LEN, configFile);
+ if (rc == -2) {
+ syslog(LOG_ERR, "Unable to read advertisement tags :%s",
+ ErrorString);
+ return (-1);
+ }
+
+ if (*buffer) {
+ /*
+ * If we're talking "reverseTunnel", then we only
+ * advertise what the setting for the FA is. This is
+ * because the only MNs that care about what's in these
+ * becons are those that are visiting this subnet, so
+ * if the setting for HA is no, but the setting for FA
+ * is yes, advertise them.
+ */
+ if (strcmp(flagTable[i].string, "reverseTunnel") == 0) {
+ if (FA(*buffer)) {
+ ServicesFlags |= flagTable[i].value;
+ } else {
+ ServicesFlags &= ~flagTable[i].value;
+ }
+ continue;
+ }
+
+ /* every other setting is simply yes/no... */
+ if (YES(*buffer)) {
+ ServicesFlags |= flagTable[i].value;
+ } else {
+ ServicesFlags &= ~flagTable[i].value;
+ }
+ }
+ }
+
+ /* regLifetime */
+ rc = GetPrivateProfileInt(Interface, "regLifetime", -1, configFile);
+ if (rc != -1)
+ regLifetime = rc;
+
+ /* advLifetime */
+ rc = GetPrivateProfileInt(Interface, "advLifetime", -1, configFile);
+ if (rc != -1)
+ advLifetime = rc;
+
+ /* periodicInterval */
+ rc = GetPrivateProfileInt(Interface, "advFrequency",
+ DEFAULT_ADVERTISEMENT_INTERVAL, configFile);
+
+ advInterval = rc;
+
+ if (advInterval < DEFAULT_MIN_INTERVAL)
+ advInterval = DEFAULT_ADVERTISEMENT_INTERVAL;
+ else if (advInterval > (int)(advLifetime/3)) {
+ syslog(LOG_WARNING,
+ "advFrequency value exceeds recommended value: "
+ "less than or equal to %d",
+ (int)(advLifetime/3));
+ }
+ /* AdvInitCount */
+ advInitCount = GetPrivateProfileInt(Interface,
+ "advInitCount", 1, configFile);
+ if (advInitCount < (uint8_t)ADV_INIT_COUNT_MIN)
+ advInitCount = ADV_INIT_COUNT_DEFAULT;
+
+ /* AdvLimitUnsolicited */
+ rc = GetPrivateProfileString(Interface, "advLimitUnsolicited",
+ "no", buffer, MAX_BUFFER_SIZE, configFile);
+ if (rc == -2) {
+ syslog(LOG_ERR, "Unable to read advertisement tags: %s",
+ ErrorString);
+ return (-1);
+ }
+
+ if (strcasecmp(buffer, "yes") == 0) {
+ advLimitUnsolicited = _B_TRUE;
+ }
+ if (advLimitUnsolicited == _B_FALSE)
+ advInitCount = 1;
+
+ /* Is Reverse Tunneling allowed on this interface? */
+ rc = GetPrivateProfileString(Interface, "reverseTunnel",
+ "no", buffer, MAX_FN_LEN, configFile);
+ if (rc == -2) {
+ syslog(LOG_ERR, "Unable to read advertisement tags: %s",
+ ErrorString);
+ return (-1);
+ }
+
+ /* We support differenct HA and FA reverseTunnelAllowed settings. */
+ if (!strcasecmp(buffer, "no")) {
+ reverseTunnelAllowed = RT_NONE;
+ }
+
+ if (!strcasecmp(buffer, "none")) {
+ reverseTunnelAllowed = RT_NONE;
+ }
+
+ if (!strcasecmp(buffer, "ha")) {
+ reverseTunnelAllowed = RT_HA;
+ }
+
+ if (!strcasecmp(buffer, "fa")) {
+ reverseTunnelAllowed = RT_FA;
+ }
+
+ if (!strcasecmp(buffer, "yes")) {
+ reverseTunnelAllowed = RT_BOTH;
+ }
+ if (!strcasecmp(buffer, "both")) {
+ reverseTunnelAllowed = RT_BOTH;
+ }
+
+ /* Is Reverse Tunneling required on this interface? */
+ rc = GetPrivateProfileString(Interface, "reverseTunnelRequired",
+ "no", buffer, MAX_FN_LEN, configFile);
+ if (rc == -2) {
+ syslog(LOG_ERR, "Unable to read advertisement tags: %s",
+ ErrorString);
+ return (-1);
+ }
+
+ /* We support differenct HA and FA reverseTunnelRequired settings */
+ if (!strcasecmp(buffer, "no")) {
+ reverseTunnelRequired = RT_NONE;
+ }
+
+ if (!strcasecmp(buffer, "none")) {
+ reverseTunnelRequired = RT_NONE;
+ }
+
+ if (!strcasecmp(buffer, "ha")) {
+ reverseTunnelRequired = RT_HA;
+ }
+
+ if (!strcasecmp(buffer, "fa")) {
+ reverseTunnelRequired = RT_FA;
+ }
+
+ if (!strcasecmp(buffer, "yes")) {
+ reverseTunnelRequired = RT_BOTH;
+ }
+ if (!strcasecmp(buffer, "both")) {
+ reverseTunnelRequired = RT_BOTH;
+ }
+
+ rc = sscanf(Interface,
+ "%" VAL2STR(LIFNAMSIZ) "s "
+ "%" VAL2STR(INET_ADDRSTRLEN) "s",
+ devAddr, dev);
+ if (rc != 2) {
+ syslog(LOG_CRIT, "Error: Invalid Advertisement Section <%s>",
+ Interface);
+ return (-1);
+ }
+
+ rc = GetPrivateProfileString(Interface, "prefixLengthExt",
+ "", buffer, MAX_FN_LEN, configFile);
+ if (rc == -2) {
+ syslog(LOG_ERR, "Unable to read advertisement tags: %s",
+ ErrorString);
+ return (-1);
+ }
+ if (*buffer) {
+ if (YES(*buffer)) {
+ PrefixFlags = _B_TRUE;
+ } else {
+ PrefixFlags = _B_FALSE;
+ }
+ }
+
+
+ /* advertiseOnBcast */
+ rc = GetPrivateProfileString(Interface, "advertiseOnBcast",
+ "", buffer, MAX_FN_LEN, configFile);
+ if (rc == -2) {
+ syslog(LOG_ERR, "Unable to read advertisement tags: %s",
+ ErrorString);
+ return (-1);
+ }
+ if (*buffer) {
+ if (YES(*buffer)) {
+ advertiseOnBcast = _B_TRUE;
+ } else {
+ advertiseOnBcast = _B_FALSE;
+ }
+ }
+
+ /*
+ * Note that the CreateInterfaceEntry does NOT lock the node
+ * and simply returns an int. Last argument is B_FALSE for static
+ * entries
+ */
+ if (CreateInterfaceEntry(dev, regLifetime, advertiseOnBcast,
+ DEFAULT_MIN_INTERVAL, DEFAULT_MAX_INTERVAL, advLifetime,
+ 0, ServicesFlags, PrefixFlags, reverseTunnelAllowed,
+ reverseTunnelRequired, advLimitUnsolicited, advInitCount,
+ advInterval, _B_FALSE)) {
+ syslog(LOG_CRIT, "Unable to create Interface");
+ return (-2);
+ }
+
+ return (0);
+
+} /* setupInterface */
+
+/*
+ * Function: setupPool
+ *
+ * Arguments: configFile - Pointer to config file
+ * Poolstr - Pointer to the section
+ *
+ * Description: This function will process an [Pool x] section.
+ * If the section is valid, we will create the
+ * static Pool entry.
+ *
+ * Returns: int - 0 if successful.
+ */
+static int
+setupPool(char *configFile, char *Poolstr)
+{
+ Pool *entry;
+ int32_t rc;
+ char buffer[MAX_BUFFER_SIZE+1];
+ uint32_t poolId;
+ uint32_t poolBaseAddr;
+ int32_t poolSize;
+
+ /* SPI Definition */
+ rc = sscanf(Poolstr, "%" VAL2STR(MAX_BUFFER_SIZE) "s %u",
+ buffer, &poolId);
+ if (rc != 2) {
+ syslog(LOG_CRIT, "Error: Invalid Pool Section [%s]", Poolstr);
+ return (-1);
+ }
+
+ rc = GetPrivateProfileString(Poolstr, "BaseAddress",
+ "", buffer, MAX_KEY_STRING_LEN, configFile);
+ if (rc == -2) {
+ syslog(LOG_ERR, "Unable to read %s tags: %s", Poolstr,
+ ErrorString);
+ return (-1);
+ }
+ if (*buffer == '\0') {
+ syslog(LOG_CRIT,
+ "Problem reading Pool <%d>. No BaseAddress", poolId);
+ return (-1);
+ }
+ poolBaseAddr = inet_addr(buffer);
+
+ poolSize = GetPrivateProfileInt(Poolstr, "Size", -1, configFile);
+ if (poolSize == -1) {
+ syslog(LOG_CRIT, "Problem reading Pool <%d>. No Pool Size", poolId);
+ return (-1);
+ }
+
+ /*
+ * Now we create the Security Assocation
+ */
+ if ((entry = CreateAddressPool(poolId, poolBaseAddr,
+ poolSize)) == NULL) {
+ syslog(LOG_CRIT, "Unable to create Pool %d", poolId);
+ return (-2);
+ }
+
+ /*
+ * The Create function ends up locking the node, so
+ * we need to free it.
+ */
+ (void) rw_unlock(&entry->poolNodeLock);
+
+ return (0);
+
+} /* setupPool */
+
+/*
+ * Function: readGSPs
+ *
+ * Arguments: configFile - Pointer to config file
+ *
+ * Description: This function will parse the
+ * [GlobalSecurityParameters] section in the
+ * config file.
+ *
+ * Returns: void
+ */
+static void
+readGSPs(char *configFile)
+{
+ int32_t rc;
+ char buffer[MAX_BUFFER_SIZE];
+ char *GSP = "GlobalSecurityParameters";
+ int32_t strlenmax;
+
+ /* IDfreshnessSlack */
+ rc = GetPrivateProfileInt(GSP, "maxClockSkew", -1, configFile);
+ if (rc != -1)
+ IDfreshnessSlack = rc;
+
+ /*
+ * Is inter-Mobility-Agent Authentication Required?
+ */
+ rc = GetPrivateProfileString(GSP, "HA-FAauth",
+ "", buffer, MAX_FN_LEN, configFile);
+ if (rc == -2) {
+ syslog(LOG_ERR, "Unable to read %s tags: %s", GSP,
+ ErrorString);
+ exit(1);
+ }
+ if (*buffer) {
+ if (YES(*buffer))
+ fhAuthRequired = _B_TRUE;
+ else
+ fhAuthRequired = _B_FALSE;
+ }
+
+ /*
+ * Is Authentication between the Mobile Node and the Foreign
+ * agent required?
+ */
+ rc = GetPrivateProfileString(GSP, "MN-FAauth",
+ "", buffer, MAX_FN_LEN, configFile);
+ if (rc == -2) {
+ syslog(LOG_ERR, "Unable to read %s tags: %s", GSP,
+ ErrorString);
+ exit(1);
+ }
+ if (*buffer) {
+ if (YES(*buffer))
+ mfAuthRequired = _B_TRUE;
+ else
+ mfAuthRequired = _B_FALSE;
+ }
+
+ /*
+ * Should we be advertising the challenge?
+ */
+ rc = GetPrivateProfileString(GSP, "Challenge",
+ "", buffer, MAX_FN_LEN, configFile);
+ if (rc == -2) {
+ syslog(LOG_ERR, "Unable to read %s tags: %s", GSP,
+ ErrorString);
+ exit(1);
+ }
+ if (*buffer) {
+ if (YES(*buffer))
+ faChallengeAdv = _B_TRUE;
+ else
+ faChallengeAdv = _B_FALSE;
+ }
+
+ /*
+ * What is our key distribution strategy?
+ */
+ rc = GetPrivateProfileString(GSP, "KeyDistribution",
+ "", buffer, MAX_FN_LEN, configFile);
+ if (rc == -2) {
+ syslog(LOG_ERR, "Unable to read %s tags: %s", GSP,
+ ErrorString);
+ exit(1);
+ }
+ strlenmax = MAX_FN_LEN - 1;
+ if (!strncasecmp(buffer, "diameter", strlenmax)) {
+ aaaProtocol = DIAMETER;
+ } else if (!strncasecmp(buffer, "radius", strlenmax)) {
+ aaaProtocol = RADIUS;
+ }
+
+#ifdef RADIUS_ENABLED
+ /* Radius Library */
+ rc = GetPrivateProfileString(GSP, "RadiusSharedLibrary",
+ "", radiusSharedLibrary, MAX_FN_LEN, configFile);
+ if (rc == -2) {
+ syslog(LOG_ERR, "Unable to read %s tags: %s", GSP,
+ ErrorString);
+ return (-1);
+ }
+ if (*radiusSharedLibrary) {
+ syslog(LOG_CRIT, "ERROR!!! --- RADIUS SUPPORT BROKEN!");
+ radiusEnabled = 0;
+ }
+#endif /* RADIUS_ENABLED */
+ return;
+
+} /* readGSPs */
+
+/*
+ * Function: readAAASettings
+ *
+ * Arguments: configFile - Pointer to config file
+ *
+ * Description: This function will parse the
+ * [AAASettings] section in the config file.
+ *
+ * Returns: void
+ */
+
+/* ARGSUSED */
+static void
+readAAASettings(char *configFile)
+{
+#ifdef TEST_DIAMETER
+ char buffer[MAX_BUFFER_SIZE];
+ char *AAASettings = "AAASettings";
+ int rc;
+#endif
+
+ /* First, set the defaults */
+ gbl_aaaPort = AAA_PORT;
+ (void) strcpy(gbl_aaaHost, LOOPBACK);
+
+ /*
+ * The only time we would ever want these configurable is during
+ * testing. So, only enable them when TEST_DIAMETER is defined.
+ */
+
+#ifdef TEST_DIAMETER
+
+ /* Server */
+ (void) GetPrivateProfileString(AAASettings, "Server",
+ "", buffer, MAX_FN_LEN, configFile);
+ if (rc == -2) {
+ syslog(LOG_ERR, "Unable to read AAASettings tags :%s",
+ ErrorString);
+ return (-1);
+ }
+ if (*buffer) {
+ (void) strncpy(gbl_aaaHost, buffer,
+ MIN(MAX_SERVER_NAME_LEN,
+ MAX_BUFFER_SIZE));
+ gbl_aaaHost[MAX_SERVER_NAME_LEN - 1] = 0;
+ }
+
+ /* Port */
+ rc = GetPrivateProfileInt(AAASettings, "Port", -1,
+ configFile);
+ if (rc != -1)
+ gbl_aaaPort = (short)rc;
+
+ mipverbose(("WARNING: Changing DIAMETER host/port to"
+ " %s:%d for testing!\n",
+ gbl_aaaHost, gbl_aaaPort));
+#endif
+ return;
+
+} /* readGSPs */
+
+/*
+ * Function: readGeneral
+ *
+ * Arguments: configFile - Pointer to config file
+ *
+ * Description: This function will parse the
+ * [General] section in the config file.
+ *
+ * Returns: int - 0 if successful.
+ */
+static int
+readGeneral(char *configFile)
+{
+ int32_t rc;
+ char buffer[MAX_BUFFER_SIZE];
+ char *General = "General";
+ int i;
+ Str2Int debugLevels[] = {
+ { "quiet", 0 },
+ { "low", 1 },
+ { "norm", 2 },
+ { "all", 3 },
+ { NULL, NULL }};
+
+ /* Debug Level */
+ rc = GetPrivateProfileString(General, "logVerbosity", "", buffer,
+ MAX_TAG_SIZE, configFile);
+ if (rc == -2) {
+ syslog(LOG_ERR, "Unable to read General tags :%s",
+ ErrorString);
+ return (-1);
+ }
+ if (*buffer) {
+ for (i = 0; debugLevels[i].string; i++) {
+ if (!strcasecmp(buffer, debugLevels[i].string)) {
+ logVerbosity = debugLevels[i].value;
+ break;
+ }
+ }
+ if (!debugLevels[i].string) {
+ /* Broke out of loop! */
+ syslog(LOG_CRIT, "Bad logVerbosity level. Should be"
+ " one of (quiet, low, norm, or all)");
+ return (-1);
+ }
+ }
+
+ /*
+ * Should we be advertising our NAI?
+ */
+ rc = GetPrivateProfileString(General, "AdvertiseNAI", "", buffer,
+ MAX_FN_LEN, configFile);
+ if (rc == -2) {
+ syslog(LOG_ERR, "Unable to read General tags :%s",
+ ErrorString);
+ return (-1);
+ }
+ if (*buffer) {
+ if (YES(*buffer))
+ faNAIadv = _B_TRUE;
+ else
+ faNAIadv = _B_FALSE;
+ }
+
+ /* Visitor Entry High Water Mark */
+ rc = GetPrivateProfileInt(General, "visitorHighWaterMark", -1,
+ configFile);
+ if (rc != -1)
+ visitorEntryHighWaterMark = rc;
+
+ /* Visitor Entry Low Water Mark */
+ rc = GetPrivateProfileInt(General, "visitorLowWaterMark", -1,
+ configFile);
+ if (rc != -1)
+ visitorEntryLowWaterMark = rc;
+
+ /* periodicInterval - This one is undocumented. */
+ rc = GetPrivateProfileInt(General, "GarbageCollectionFrequency",
+ -1, configFile);
+ if (rc != -1) {
+ /*
+ * Magic numbers. Given that this is a faily dangerous
+ * feature, we need to restrict the values one can
+ * configure.
+ */
+ if (rc < MIN_GARBAGE_COLLECTION_INTERVAL) {
+ rc = MIN_GARBAGE_COLLECTION_INTERVAL;
+ } else if (rc > MAX_GARBAGE_COLLECTION_INTERVAL) {
+ rc = MAX_GARBAGE_COLLECTION_INTERVAL;
+ }
+ periodicInterval = rc;
+ }
+
+ /* performance checking interval - This one is undocumented. */
+ rc = GetPrivateProfileInt(General, "PerformanceCheckInterval",
+ -1, configFile);
+ if (rc != -1) {
+ performanceInterval = rc;
+ }
+
+
+ /*
+ * Should we be advertising our NAI?
+ */
+ rc = GetPrivateProfileString(General, "Daemonize", "", buffer,
+ MAX_FN_LEN, configFile);
+ if (rc == -2) {
+ syslog(LOG_ERR, "Unable to read General tags :%s",
+ ErrorString);
+ return (-1);
+ }
+ if (*buffer) {
+ if (YES(*buffer))
+ daemonize = _B_TRUE;
+ else
+ daemonize = _B_FALSE;
+ }
+
+ /*
+ * Should SNMP be disabled?
+ */
+ rc = GetPrivateProfileString(General, "DisableSNMP", "", buffer,
+ MAX_FN_LEN, configFile);
+ if (rc == -2) {
+ syslog(LOG_ERR, "Unable to read General tags :%s",
+ ErrorString);
+ return (-1);
+ }
+ if (*buffer) {
+ if (YES(*buffer))
+ disableSNMP = _B_TRUE;
+ else
+ disableSNMP = _B_FALSE;
+ }
+
+ return (0);
+} /* readGeneral */
+
+
+/*
+ * Function: memswp
+ *
+ * Arguments: A - Pointer to buffer
+ * B - Pointer to buffer
+ * length - length
+ *
+ * Description: This function will swap the contents
+ * of A and B.
+ *
+ * Returns: int - 0 if successful.
+ */
+static void
+memswp(char *A, char *B, int length)
+{
+ char *temp;
+ temp = alloca(length);
+ if (!temp) {
+ syslog(LOG_CRIT, "Unable to allocate from the heap");
+ return;
+ }
+
+ (void) memcpy(temp, A, length);
+ (void) memcpy(A, B, length);
+ (void) memcpy(B, temp, length);
+}
+
+
+/*
+ * Function: sortSections
+ *
+ * Arguments: Sections - Pointer to sections
+ * numSections - Number of sections
+ * sectionSize - Size of section
+ *
+ * Description: Since we have to have the Pools and SPIs defined
+ * before we add an address, move all of the addresses to the end
+ * of the sections. Use a single pass, starting at the end, and
+ * doing a direct swap.
+ *
+ * Returns:
+ */
+static void
+sortSections(char *Sections, int numSections, int sectionSize)
+{
+ int i;
+ int j = -1;
+
+ for (i = numSections-1; i >= 0; i--) {
+ if (strncasecmp(&Sections[i*sectionSize], "Address", 7)) {
+ /* Ok, we're stuck at a non-address, find one */
+ /* to exchange with us */
+ if (j < 0)
+ j = i-1;
+
+ for (; j >= 0; j--) {
+ if (!strncasecmp(&Sections[j*sectionSize],
+ "Address", 7)) {
+ /* Found one! Swap it */
+ memswp(&Sections[i*sectionSize],
+ &Sections[j*sectionSize],
+ sectionSize);
+ /* break out of inner for loop */
+ break;
+ }
+ }
+ /* Check to see if we're finished */
+ if (j < 0)
+ return;
+ }
+ }
+} /* sortSections */
+
+/*
+ * Function: readConfigInfo
+ *
+ * Arguments: configFile - Pointer to filename
+ *
+ * Description: Read configuration information for the mobility
+ * agent from file
+ *
+ * Returns: int - 0 if successful
+ */
+static int
+readConfigInfo(char *configFile)
+{
+ char *Sections;
+ int numSections, sectionSize;
+
+#ifdef FIREWALL_SUPPORT
+ domainInfo.addrIntervalCnt = 0;
+ domainInfo.firewallCnt = 0;
+#endif /* FIREWALL_SUPPORT */
+
+ if (readGeneral(configFile)) {
+ return (-1);
+ }
+
+ readGSPs(configFile);
+
+ readAAASettings(configFile);
+
+ /*
+ * Ok, now is when it gets a little complex. Read in all the
+ * section names, then add Interfaces and Mobile nodes as they
+ * come up.
+ */
+ Sections = IniListSections(configFile, &numSections, &sectionSize);
+
+ /*
+ * We need to check for NULL return from iniListSections,
+ * otherwise we would be accessing a NULL pointer.
+ */
+ if (Sections != NULL) {
+ int i;
+
+ sortSections(Sections, numSections, sectionSize);
+
+ for (i = 0; i < numSections; i++) {
+ if (!strncasecmp(&Sections[i*sectionSize],
+ "Advertisements", 14)) {
+ if (setupInterface(configFile,
+ &Sections[i*sectionSize]))
+ return (-1);
+ } else if (!strncasecmp(&Sections[i*sectionSize],
+ "Address", 7)) {
+ if (setupAddress(configFile,
+ &Sections[i*sectionSize]))
+ return (-1);
+ } else if (!strncasecmp(&Sections[i*sectionSize],
+ "SPI", 3)) {
+ if (setupSPI(configFile,
+ &Sections[i*sectionSize]))
+ return (-1);
+ } else if (!strncasecmp(&Sections[i*sectionSize],
+ "Pool", 4)) {
+ if (setupPool(configFile,
+ &Sections[i*sectionSize]))
+ return (-1);
+ }
+#ifdef FIREWALL_SUPPORT
+ /*
+ * XXX: Does this section need some enhancement ?.
+ */
+ else if (!strncasecmp(&Sections[i*sectionSize],
+ "Firewall", 8)) {
+ char Firewall[INET_ADDRSTRLEN+1];
+ sscanf(&Sections[i*sectionSize],
+ "%*s %" VAL2STR(INET_ADDRSTRLEN) "s",
+ Firewall);
+
+ domainInfo.fwAddr[domainInfo.firewallCnt++] =
+ inet_addr(Firewall);
+ } /* end If */
+#endif /* FIREWALL_SUPPORT */
+
+ } /* End for() each section */
+ }
+
+ if (Sections != NULL) {
+ free(Sections);
+ }
+
+ return (0);
+
+} /* readConfigInfo */
+
+
+/*
+ * Function: InitSockets
+ *
+ * Arguments: pointer to MaAdvConfigEntry
+ *
+ * Description: This function will open the ICMP, BCAST and
+ * the multicast socket, and will bind on all
+ * interfaces configured. A specific Join must
+ * be done for multicast on each interface.
+ *
+ * Returns: int, 0 if successful
+ */
+int
+InitSockets(MaAdvConfigEntry *entry)
+{
+ int sid;
+ int enable = 1;
+ unsigned int ifceno;
+ char addrstr1[40];
+ struct sockaddr_in sa;
+ struct ip_mreq imr;
+ struct in_addr ifaddr;
+
+ mipverbose(("Creating ICMP socket.\n"));
+
+
+ /*
+ * First off, let's get the interface number.
+ */
+ ifceno = if_nametoindex(entry->maIfaceName);
+
+ if (ifceno == 0) {
+ syslog(LOG_ERR,
+ "Unable to get interface number for %s",
+ entry->maIfaceName);
+ return (-1);
+ }
+
+ /* Get a socket to receive ICMPs on. */
+ if ((sid = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) {
+ syslog(LOG_CRIT,
+ "socket()Error: Couldn't create ICMP socket " \
+ "in InitSockets.");
+ return (-1);
+ }
+
+ /*
+ * Just in case we advertise on 255.255.255.255, enable
+ * bcast
+ */
+ enable = 1;
+ if (setsockopt(sid, SOL_SOCKET, SO_BROADCAST,
+ (char *)&enable, sizeof (int)) < 0) {
+ syslog(LOG_CRIT, "SO_BROADCAST on ICMPsock failed");
+ return (-1);
+
+ }
+
+ if (setsockopt(sid, IPPROTO_IP, IP_BOUND_IF, (char *)&ifceno,
+ sizeof (char *))) {
+ syslog(LOG_ERR,
+ "Unable to bind socket to interface %d", ifceno);
+ return (-1);
+ }
+
+ /*
+ * Enable IP_XMIT_IF here so that we don't need to
+ * turn it on sendICMPmessage everytime we send
+ * multicast adv. IP_XMIT_IF will have to be set
+ * for all PPP interfaces, because there may be
+ * cases when PPP local and remote end both have
+ * non-unique addresses ( ex: private address)
+ */
+ if (entry->maIfaceFlags & IFF_POINTOPOINT) {
+ /*
+ * set IP_XMIT_IF
+ */
+ if (setsockopt(sid, IPPROTO_IP, IP_XMIT_IF,
+ &ifceno, sizeof (ifceno)) < 0) {
+ syslog(LOG_ERR,
+ "setsockopt() couldn't set IP_XMIT_IF"
+ "on Adv socket for interface id %d",
+ ifceno);
+ return (-1);
+ }
+ } else {
+ /*
+ * For non-pointtopoint interfaces we are still
+ * setting IP_MULTICAST_IF, as we expect to have
+ * unique ifaddr in this case.
+ * We can take advantage of cached routing entry
+ * too.
+ */
+ ifaddr.s_addr = entry->maIfaceAddr;
+ if (setsockopt(sid, IPPROTO_IP, IP_MULTICAST_IF,
+ (char *)&ifaddr, sizeof (ifaddr)) == -1) {
+ syslog(LOG_ERR,
+ "setsockopt() Couldn't set multicast"
+ "on socket");
+ return (-1);
+ }
+ }
+ entry->maIfaceIcmpSock = sid;
+
+
+ /*
+ * Create and bind the socket to monitor the
+ * unicast interface addr
+ */
+
+ if ((sid = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ syslog(LOG_CRIT,
+ "socket()Error: Couldn't create unicast UDP " \
+ "socket - to monitor unicast interface addr");
+ return (-1);
+ }
+
+ if (setsockopt(sid, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&enable, sizeof (int)) < 0) {
+ syslog(LOG_NOTICE, "setsockopt() failed." \
+ "socket - to monitor unicast interface addr");
+ }
+
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons(MIP_PORT);
+ sa.sin_addr.s_addr = entry->maIfaceAddr;
+
+ mipverbose(("Binding UDP socket to %s port %d.\n",
+ ntoa(sa.sin_addr.s_addr, addrstr1), ntohs(sa.sin_port)));
+ if (bind(sid, (struct sockaddr *)&sa, sizeof (sa)) < 0) {
+ syslog(LOG_CRIT,
+ "bind() Error: Could not bind unicast socket." \
+ "(maIfaceaddr): %m");
+ return (-1);
+ }
+ /*
+ * ToDO: Verify whether IP_RECV[IF|SLLA|TTL] socket
+ * options to be set here. This socket is used for
+ * broadcasting advertisements.
+ */
+
+ if (setsockopt(sid, IPPROTO_IP, IP_RECVIF, &enable,
+ sizeof (int)) < 0) {
+ syslog(LOG_ERR, "setsockopt IP_RECVIF "
+ "(maIfaceAddr):%m");
+ return (-1);
+ }
+
+
+ if (setsockopt(sid, IPPROTO_IP, IP_RECVSLLA, &enable,
+ sizeof (int)) < 0) {
+ syslog(LOG_ERR, "setsockopt IP_RECVSLLA " \
+ "(maIfaceAddr): %m");
+ return (-1);
+ }
+
+ if (setsockopt(sid, IPPROTO_IP, IP_RECVTTL, &enable,
+ sizeof (int)) < 0) {
+ syslog(LOG_ERR, "setsockopt IP_RECVTTL " \
+ "(maIfaceAddr): %m");
+ return (-1);
+ }
+ if (setsockopt(sid, IPPROTO_IP, IP_BOUND_IF, (char *)&ifceno,
+ sizeof (char *))) {
+ syslog(LOG_ERR,
+ "Unable to bind socket to interface %d", ifceno);
+ return (-1);
+ }
+
+ entry->maIfaceUnicastSock = sid;
+
+ /*
+ * PPP interfaces would not receive any broadcast packets.
+ * Therefore we don't need to bind to broadcast addresses.
+ */
+ if ((entry->maIfaceFlags & IFF_POINTOPOINT) == 0) {
+ /*
+ * Create and bind the socket to monitor
+ * the bcast interface addr
+ */
+ if ((sid = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ syslog(LOG_CRIT,
+ "socket()Error:Couldn't create broadcast "
+ "UDP socket");
+ return (-1);
+ }
+
+ if (setsockopt(sid, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&enable, sizeof (int)) < 0) {
+ syslog(LOG_NOTICE, "setsockopt() failed " \
+ "on the broadcast UDP socket");
+ }
+
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons(MIP_PORT);
+ sa.sin_addr.s_addr = inet_addr(LINK_BCAST_ADDR);
+
+ mipverbose(("Binding UDP socket to %s port %d.\n",
+ ntoa(sa.sin_addr.s_addr, addrstr1),
+ ntohs(sa.sin_port)));
+ if (bind(sid, (struct sockaddr *)&sa,
+ sizeof (sa)) < 0) {
+ syslog(LOG_CRIT,
+ "bind Error: Could not bind broadcast "
+ "socket - to monitor the bcast interface "
+ "addr");
+ return (-1);
+ }
+
+ /*
+ * TODO: Verify whether IP_RECV* sockets are useful
+ * in this socket which binds itself to link_broadcast
+ * address.
+ */
+
+ if (setsockopt(sid, IPPROTO_IP, IP_RECVIF, &enable,
+ sizeof (int)) < 0) {
+ syslog(LOG_ERR, "setsockopt IP_RECVIF failed " \
+ "(LINK_BCAST_ADDR): %m");
+ return (-1);
+ }
+
+ if (setsockopt(sid, IPPROTO_IP, IP_RECVSLLA, &enable,
+ sizeof (int)) < 0) {
+ syslog(LOG_ERR,
+ "setsockopt IP_RECVSLLA failed "
+ "(LINK_BCAST_ADDR): %m");
+ return (-1);
+ }
+
+ if (setsockopt(sid, IPPROTO_IP, IP_RECVTTL, &enable,
+ sizeof (int)) < 0) {
+ syslog(LOG_ERR,
+ "setsockopt IP_RECVTTL failed "
+ "(LINK_BCAST_ADDR): %m");
+ return (-1);
+ }
+
+ if (setsockopt(sid, IPPROTO_IP, IP_BOUND_IF,
+ (char *)&ifceno, sizeof (char *))) {
+ (void) fprintf(stderr,
+ "Unable to bind socket to interface %d",
+ ifceno);
+ return (-1);
+ }
+
+ entry->maIfaceBcastSock = sid;
+
+ /*
+ * Create and bind the socket to monitor
+ * the directed bcast interface addr
+ */
+ if ((sid = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ syslog(LOG_CRIT,
+ "socket()Error:Couldn't create broadcast UDP "
+ "socket");
+ return (-1);
+ }
+
+ if (setsockopt(sid, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&enable, sizeof (int)) < 0) {
+ syslog(LOG_NOTICE, "setsockopt() failed " \
+ "on broadcast UDP socket");
+ }
+
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons(MIP_PORT);
+ sa.sin_addr.s_addr = GENERATE_NET_BROADCAST_ADDR(entry);
+
+ mipverbose(("Binding UDP socket to %s port %d.\n",
+ ntoa(sa.sin_addr.s_addr, addrstr1),
+ ntohs(sa.sin_port)));
+ if (bind(sid, (struct sockaddr *)&sa,
+ sizeof (sa)) < 0) {
+ syslog(LOG_CRIT,
+ "bind Error: Could not bind broadcast "
+ "socket.");
+ return (-1);
+ }
+
+ /*
+ * Setting option on socket bound to NET_BROADCAST_ADDR
+ */
+ if (setsockopt(sid, IPPROTO_IP, IP_RECVIF, &enable,
+ sizeof (int)) < 0) {
+ syslog(LOG_ERR, "setsockopt IP_RECVIF "
+ "(NET_BROADCAST_ADDR): %m");
+ return (-1);
+ }
+
+
+ if (setsockopt(sid, IPPROTO_IP, IP_RECVSLLA, &enable,
+ sizeof (int)) < 0) {
+ syslog(LOG_ERR, "setsockopt IP_RECVSLLA ",
+ "(NET_BROADCAST_ADDR): %m");
+ return (-1);
+ }
+
+
+ if (setsockopt(sid, IPPROTO_IP, IP_RECVTTL, &enable,
+ sizeof (int)) < 0) {
+ syslog(LOG_ERR, "setsockopt IP_RECVTTL "
+ "(NET_BROADCAST_ADDR): %m");
+ return (-1);
+ }
+
+ if (setsockopt(sid, IPPROTO_IP, IP_BOUND_IF,
+ (char *)&ifceno, sizeof (char *))) {
+ syslog(LOG_ERR,
+ "Unable to bind socket to interface %d",
+ ifceno);
+ return (-1);
+ }
+
+ enable = 1;
+ if (setsockopt(sid, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&enable, sizeof (int)) < 0) {
+ syslog(LOG_NOTICE, "setsockopt() " \
+ "(NET_BROADCAST_ADDR): %m");
+ }
+
+ entry->maIfaceDirBcastSock = sid;
+ } else {
+ /* Set fd to -1 */
+ entry->maIfaceBcastSock = -1;
+ entry->maIfaceDirBcastSock = -1;
+ }
+
+ /*
+ * Create and bind the socket to monitor
+ * the multicast advertisement traffic (224.0.0.1
+ * and 224.0.0.2)
+ */
+
+ if ((sid = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) {
+ syslog(LOG_CRIT,
+ "socket() Error: Couldn't create ICMP socket " \
+ "in InitSockets to monitor mcast advertisement.");
+ return (-1);
+ }
+
+ if (setsockopt(sid, IPPROTO_IP, IP_RECVIF, &enable,
+ sizeof (int)) < 0) {
+ syslog(LOG_ERR, "setsockopt IP_RECVIF failed...%s",
+ strerror(errno));
+ return (-1);
+ }
+
+ if (setsockopt(sid, IPPROTO_IP, IP_BOUND_IF, (char *)&ifceno,
+ sizeof (char *))) {
+ syslog(LOG_ERR,
+ "Unable to bind socket to interface %d", ifceno);
+ return (-1);
+ }
+
+ /*
+ * Join multicast groups so we can receive ICMP messages
+ * on this interface sent to 224.0.0.1
+ */
+ imr.imr_multiaddr.s_addr = inet_addr(LINK_MCAST_ADV_ADDR);
+ imr.imr_interface.s_addr = entry->maIfaceAddr;
+
+ if (setsockopt(sid, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ (char *)&imr, sizeof (struct ip_mreq)) < 0) {
+ syslog(LOG_NOTICE,
+ "Could not join multicast group 224.0.0.1");
+ } else {
+ mipverbose(("Joined %s on interface %s.\n",
+ LINK_MCAST_ADV_ADDR,
+ ntoa(entry->maIfaceAddr, addrstr1)));
+ }
+
+ /*
+ * Join multicast groups so we can receive ICMP messages
+ * on this interface sent to 224.0.0.2.
+ */
+ imr.imr_multiaddr.s_addr = inet_addr(LINK_MCAST_ADV_ADDR2);
+ imr.imr_interface.s_addr = entry->maIfaceAddr;
+ if (setsockopt(sid, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ (char *)&imr, sizeof (struct ip_mreq)) < 0) {
+ syslog(LOG_NOTICE,
+ "Could not join multicast group 224.0.0.2");
+ } else {
+ mipverbose(("Joined %s on interface %s.\n",
+ LINK_MCAST_ADV_ADDR2,
+ ntoa(entry->maIfaceAddr, addrstr1)));
+ }
+
+
+ entry->maIfaceAdvMulticastSock = sid;
+
+ /*
+ * Create and bind the socket to monitor
+ * the multicast registration traffic (224.0.0.11)
+ */
+ if ((sid = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ syslog(LOG_CRIT,
+ "socket()Error:Couldn't create broadcast UDP socket " \
+ "to monitor multicast registration traffic.");
+ return (-1);
+ }
+
+ if (setsockopt(sid, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&enable, sizeof (int)) < 0) {
+ syslog(LOG_NOTICE, "setsockopt() failed for broadcast " \
+ "UDP socket to monitor mcast registration traffic.");
+ }
+
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons(MIP_PORT);
+ sa.sin_addr.s_addr = inet_addr(LINK_MCAST_REG_ADDR);
+
+ mipverbose(("Binding UDP socket to %s port %d.\n",
+ ntoa(sa.sin_addr.s_addr, addrstr1), ntohs(sa.sin_port)));
+ if (bind(sid, (struct sockaddr *)&sa, sizeof (sa)) < 0) {
+ syslog(LOG_CRIT,
+ "bind Error: Could not bind broadcast socket.");
+ return (-1);
+ }
+ /* Set socket option for socket bound to 224.0.0.11 */
+
+ if (setsockopt(sid, IPPROTO_IP, IP_RECVIF, &enable,
+ sizeof (int)) < 0) {
+ syslog(LOG_ERR, "setsockopt IP_RECVIF " \
+ "(LINK_MCAST_REG_ADDR): %m");
+ return (-1);
+ }
+
+
+ if (setsockopt(sid, IPPROTO_IP, IP_RECVSLLA, &enable,
+ sizeof (int)) < 0) {
+ syslog(LOG_ERR, "setsockopt IP_RECVSLLA " \
+ "(LINK_MCAST_REG_ADDR): %m");
+ return (-1);
+ }
+
+ if (setsockopt(sid, IPPROTO_IP, IP_RECVTTL, &enable,
+ sizeof (int)) < 0) {
+ syslog(LOG_ERR, "setsockopt IP_RECVTTL " \
+ "(LINK_MCAST_REG_ADDR): %m");
+ return (-1);
+ }
+ if (setsockopt(sid, IPPROTO_IP, IP_BOUND_IF, (char *)&ifceno,
+ sizeof (char *))) {
+ syslog(LOG_ERR,
+ "Unable to bind socket to interface %d", ifceno);
+ return (-1);
+ }
+
+
+ /*
+ * Join 224.0.0.11 so we can receive Registration Requests
+ * from the multicast address.
+ */
+ imr.imr_multiaddr.s_addr = inet_addr(LINK_MCAST_REG_ADDR);
+ imr.imr_interface.s_addr = entry->maIfaceAddr;
+ if (setsockopt(sid, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ (char *)&imr, sizeof (struct ip_mreq)) < 0) {
+ syslog(LOG_NOTICE,
+ "Could not join multicast group 224.0.0.11");
+ } else {
+ mipverbose(("Joined %s on interface %s.\n",
+ LINK_MCAST_REG_ADDR,
+ ntoa(entry->maIfaceAddr, addrstr1)));
+
+ }
+
+ entry->maIfaceRegMulticastSock = sid;
+
+
+ return (0);
+}
+
+
+/*
+ * Function: deleteMobileNodeEntryHashHelper
+ *
+ * Arguments: entry - Pointer to Mobile Node Entry
+ * p1 - First parameter to match (current time)
+ *
+ * Description: This function is used as the Hash Table Helper routine
+ * for Finalize() when we need to delete all
+ * mobile node entries, and will be called by
+ * getAllHashTableEntries().
+ *
+ * Returns: _B_TRUE if the entry matches the desired criteria,
+ * otherwise _B_FALSE.
+ */
+/* ARGSUSED */
+static boolean_t
+deleteMobileNodeEntryHashHelper(void *entry, uint32_t p1)
+{
+ HaMobileNodeEntry *hentry = entry;
+ HaBindingEntry *bindingEntry;
+ HaBindingEntry *next_entry;
+
+ bindingEntry = hentry->bindingEntries;
+ while (bindingEntry) {
+ next_entry = bindingEntry->next;
+ delHABEent(hentry, bindingEntry);
+ free(bindingEntry);
+ bindingEntry = next_entry;
+ }
+
+
+ return (_B_FALSE);
+}
+
+/*
+ * Function: deleteVisitorEntryHashHelper
+ *
+ * Arguments: entry - Pointer to Mobile Node Entry
+ * p1 - First parameter to match (current time)
+ *
+ * Description: This function is used as the Hash Table Helper routine
+ * for Finalize() when we need to delete all
+ * visitor entries, and will be called by
+ * getAllHashTableEntries().
+ *
+ * Returns: _B_TRUE if the entry matches the desired criteria,
+ * otherwise _B_FALSE.
+ */
+/* ARGSUSED */
+static boolean_t
+deleteVisitorEntryHashHelper(void *entry, uint32_t p1)
+{
+ FaVisitorEntry *faEntry = entry;
+
+ delFAVEptr(faEntry, _B_TRUE, REASON_UNKNOWN);
+ free(faEntry);
+ return (_B_FALSE);
+}
+
+/*
+ * This funciton cleans up whatever registration policy is
+ * flagged as remaining. We don't clean up tunnels policy
+ * because that is done when the tunnel in encaprem() or
+ * decaprem(). We return _B_FALSE since we want the entry
+ * to be freed.
+ */
+/* ARGSUSED */
+static boolean_t
+deleteIPsecSAHashHelper(void *entry, uint32_t p1)
+{
+ MobilityAgentEntry *mae = entry;
+ int i;
+
+ for (i = FIRST_IPSEC_ACTION; i < LAST_IPSEC_ACTION; i++) {
+ if (mae->maIPsecFlags & REQUEST(i))
+ (void) removeIPsecPolicy(mae->maIPsecRequest[i]);
+
+ if (mae->maIPsecFlags & REPLY(i))
+ (void) removeIPsecPolicy(mae->maIPsecReply[i]);
+ }
+
+ return (_B_FALSE);
+}
+
+
+/*
+ * Function: Finalize
+ *
+ * Arguments: signo - signal
+ *
+ * Description: This function is the signal handler and is
+ * called when the agent is terminating. This
+ * function will destroy all of the threads,
+ * and free all of the binding and visitor entries
+ * which will clean up the Tunnel interfaces and the
+ * routing entries.
+ *
+ * Returns:
+ */
+/* ARGSUSED */
+void
+Finalize(int signo)
+{
+ char relativeTime[MAX_TIME_STRING_SIZE];
+ char currentTime[MAX_TIME_STRING_SIZE];
+
+ syslog(LOG_INFO, "---- %s (%s) ----",
+ sprintTime(currentTime, MAX_TIME_STRING_SIZE),
+ sprintRelativeTime(relativeTime, MAX_TIME_STRING_SIZE));
+
+ shutdown_flag = _B_TRUE;
+
+ /*
+ * First we need to kill all of the threads to ensure that no one
+ * will try to step on our toes. This will ensure that no other
+ * threads try to access any of the HashTables that we will need
+ * next.
+ */
+ syslog(LOG_INFO, "<<< Shutting down threads >>>");
+
+ mipverbose(("<<< Shutting down threads >>>\n"));
+ if (DynamicInterface)
+ (void) killDynamicInterfaceThread();
+ (void) killDispatcherTaskThread();
+ (void) killPeriodicTaskThread();
+ (void) killSNMPTaskThread();
+ (void) killAAATaskThread();
+ (void) killStatServer();
+
+ /*
+ * Save the state of the entire agent (mobile node entries,
+ * (dynamic) security associations, IPsec SAs, and binding
+ * entries. This allows subsequent restoration of the state.
+ */
+ (void) saveAgentState();
+
+ /*
+ * Let's delete all of the Mobile Node binding entries
+ * so we end up cleaning up our tunnel interfaces. Note that
+ * since we are coming down, we will not request any locks,
+ * since that could get the signal handler in a deadlock
+ * situation.
+ */
+ syslog(LOG_INFO, "<<< Cleaning up mobility bindings >>>");
+ getAllHashTableEntries(&haMobileNodeHash,
+ deleteMobileNodeEntryHashHelper, LOCK_NONE, 0, _B_TRUE);
+
+
+ /*
+ * Let's delete all of the Mobile Node binding entries
+ * so we end up cleaning up our routes. Note that
+ * since we are coming down, we will not request any locks,
+ * since that could get the signal handler in a deadlock
+ * situation.
+ */
+ syslog(LOG_INFO, "<<< Cleaning up accepted visitor entries >>>");
+ getAllHashTableEntries(&faVisitorHash,
+ deleteVisitorEntryHashHelper, LOCK_NONE, 0, _B_TRUE);
+
+ /*
+ * Finally clean up any IPsec policies we have installed! We don't
+ * do this above, because cleaning out the FA removed those
+ * specific to the FA, now we'll clean up the rest.
+ */
+ syslog(LOG_INFO, "<<< Cleaning up all remaining IPsec policies >>>");
+ getAllHashTableEntries(&mipAgentHash, deleteIPsecSAHashHelper,
+ LOCK_NONE, 0, _B_TRUE);
+
+ OScleanup();
+
+ syslog(LOG_INFO, "Terminated by signal (SIGTERM or SIGINT)");
+
+ exit(1);
+}
+
+/*
+ * Function: docleanup
+ *
+ * Arguments:
+ *
+ * Description: This function is called when the AAA connection
+ * to mipagent is down. The function will
+ * free all of the binding and visitor entries
+ * which will clean up the Tunnel interfaces and the
+ * routing entries.
+ * Returns:
+ */
+void
+docleanup(void)
+{
+ char relativeTime[MAX_TIME_STRING_SIZE];
+ char currentTime[MAX_TIME_STRING_SIZE];
+
+ syslog(LOG_INFO, "---- %s (%s) ----",
+ sprintTime(currentTime, MAX_TIME_STRING_SIZE),
+ sprintRelativeTime(relativeTime, MAX_TIME_STRING_SIZE));
+ mipverbose(("mipagent-AAA connection is down\n"));
+
+ /*
+ * Let's delete all of the Mobile Node binding entries
+ * so we end up cleaning up our tunnel interfaces.
+ */
+ syslog(LOG_INFO, "<<< Cleaning up mobility bindings >>>");
+ getAllHashTableEntries(&haMobileNodeHash,
+ deleteMobileNodeEntryHashHelper, LOCK_NONE, 0, _B_TRUE);
+
+ /*
+ * Let's delete all of the Mobile Node binding entries
+ * so we end up cleaning up our routes.
+ */
+ syslog(LOG_INFO, "<<< Cleaning up accepted visitor entries >>>");
+ getAllHashTableEntries(&faVisitorHash,
+ deleteVisitorEntryHashHelper, LOCK_NONE, 0, _B_TRUE);
+}
+
+
+/*
+ * Function: SetSigHandlers
+ *
+ * Arguments: Set up handlers for various signals
+ *
+ * Description:
+ *
+ * Returns:
+ */
+static void
+SetSigHandlers()
+{
+ if (signal(SIGINT, Finalize) == SIG_ERR) {
+ syslog(LOG_CRIT,
+ "signal() Error: failed to set handler for SIGINT.");
+ }
+
+ if (signal(SIGTERM, Finalize) == SIG_ERR) {
+ syslog(LOG_CRIT,
+ "signal() Error: failed to set handler for SIGTERM.");
+ }
+}
+
+
+/*
+ * Function: showConfigInfo
+ *
+ * Arguments:
+ *
+ * Description: This function will print out the current
+ * configuration of the agent.
+ *
+ * Returns:
+ */
+static void
+showConfigInfo()
+{
+
+ mipverbose(("LogLevel : %d\n", logVerbosity));
+#ifdef RADIUS_ENABLED
+ mipverbose(("RadiusEnabled : %d\n", radiusEnabled));
+ if (radiusEnabled)
+ mipverbose((" RadiusSharedLibrary : %s\n",
+ radiusSharedLibrary));
+#endif /* RADIUS_ENABLED */
+ mipverbose(("PeriodicInterval : %d\n", periodicInterval));
+/*
+ * No longer used
+ * mipverbose(("AdvLifetime : %d\n", advLifetime));
+ * mipverbose(("RegLifetime : %d\n", regLifetime));
+ */
+ mipverbose(("IDfreshnessSlack : %d\n", IDfreshnessSlack));
+ mipverbose(("\n"));
+ printMaAdvConfigHash(&maAdvConfigHash);
+ mipverbose(("\n"));
+ printHaMobileNodeHash(&haMobileNodeHash);
+ mipverbose(("\n"));
+#ifdef FIREWALL_SUPPORT
+ printProtectedDomainInfo(domainInfo);
+#endif /* FIREWALL_SUPPORT */
+ mipverbose(("\n"));
+}
+
+
+/*
+ * Function: Initialize
+ *
+ * Arguments: configFile - Pointer to the config file name
+ *
+ * Description: This is the main initialization function. This
+ * function will initialize the hash tables, retrieve
+ * our NAI, read the config file and initialize the
+ * network interfaces.
+ *
+ * Returns: int, 0 if successful
+ */
+int
+Initialize(char *configFile)
+{
+ struct utsname name;
+ char domainname[MAX_NAI_LENGTH];
+ char currentTime[MAX_TIME_STRING_SIZE];
+ char relativeTime[MAX_TIME_STRING_SIZE];
+ struct hash_table *htbl;
+ struct hash_entry *pentry;
+ int i;
+
+ /* Make sure we are running as root. */
+ if (getuid()) {
+ (void) fprintf(stderr,
+ "mipagent: Error: must be run by root\n");
+ return (-1);
+ }
+
+ openlog("mipagent", LOG_CONS, LOG_DAEMON);
+
+ syslog(LOG_INFO, "Mobile IP agent started ...");
+ syslog(LOG_INFO, "---- %s (%s) ----",
+ sprintTime(currentTime, MAX_TIME_STRING_SIZE),
+ sprintRelativeTime(relativeTime, MAX_TIME_STRING_SIZE));
+
+ /* Initiate global DynamicInterface variables here */
+ DynamicInterface = _B_FALSE;
+ dynamicIfaceHead = NULL;
+
+ /* Can we find, and read our config file? */
+ if (access(configFile, R_OK) < 0) {
+ /*
+ * config file non existent, or can't be read...
+ * keel over verbosely (is there an other way?)
+ */
+ syslog(LOG_CRIT, "Config file non-existent, or not readable.");
+ syslog(LOG_CRIT, "Cannot load initialization settings, "
+ "access() error %m.");
+ syslog(LOG_CRIT, "-> See mipagent(1M).");
+
+ /* Spit a critical startup error to the user, too */
+ (void) fprintf(stderr, "Error: config file non-existent, "
+ "or not readable, cannot initialize.\n");
+ (void) fprintf(stderr, "-> See mipagent(1M) for more info.\n");
+
+ /* tell the calling function it can keel over now. */
+ return (-1);
+ }
+
+ /* Initialize random number generator */
+ randomInit();
+
+ /* Get the hosts' Network Access Identifier */
+ if (uname(&name) < 0) {
+ syslog(LOG_CRIT, "Error 1: Unable to get our own identity.");
+ return (-1);
+ }
+
+ errno = 0;
+ (void) getdomainname(domainname, sizeof (domainname));
+ if (errno != 0) {
+ syslog(LOG_CRIT, "Error 2: Unable to get our own identity.");
+ return (-1);
+ }
+
+ (void) strcpy(maNai, name.nodename);
+ (void) strcat(maNai, "@");
+ (void) strcat(maNai, domainname);
+
+ syslog(LOG_INFO, "Our NAI is %s", maNai);
+
+ /*
+ * Initialize the hash tables
+ */
+ if (InitHash(&faVisitorHash)) {
+ syslog(LOG_CRIT, "Unable to initialize visitor hash table");
+ return (-1);
+ }
+
+ if (InitHash(&maAdvConfigHash)) {
+ syslog(LOG_CRIT, "Unable to initialize interface hash table");
+ return (-1);
+ }
+
+ if (InitHash(&haMobileNodeHash)) {
+ syslog(LOG_CRIT, "Unable to initialize mobile node hash table");
+ return (-1);
+ }
+
+ if (InitHash(&mipSecAssocHash)) {
+ syslog(LOG_CRIT,
+ "Unable to initialize security associations hash table");
+ return (-1);
+ }
+
+ if (InitHash(&mipAgentHash)) {
+ syslog(LOG_CRIT, "Unable to initialize agents hash table");
+ return (-1);
+ }
+
+ if (InitHash(&mipSecViolationHash)) {
+ syslog(LOG_CRIT,
+ "Unable to initialize security violation hash table");
+ return (-1);
+ }
+
+ if (InitHash(&mipPoolHash)) {
+ syslog(LOG_CRIT, "Unable to initialize pool hash table");
+ return (-1);
+ }
+
+ if (InitHash(&mipTunlHash)) {
+ syslog(LOG_CRIT, "Unable to initialize tunnel hash table");
+ return (-1);
+ }
+
+ /* Initialize maAdvConfigTable and haMobileNodeTable from file */
+ if (readConfigInfo(configFile) < 0) {
+ syslog(LOG_CRIT, "Error: Start up configuration unsuccessful.");
+ return (-1);
+ }
+
+
+ /*
+ * Initialize itself as a daemon
+ */
+ if (daemonize == _B_TRUE) {
+ if (daemonInit() == -1)
+ exit(1);
+ }
+
+ showConfigInfo();
+
+ /* OS-specific initialization of tunneling module etc */
+ if (InitNet() == -1) {
+ syslog(LOG_CRIT, "InitNet failed");
+ return (-1);
+ }
+
+ /* OS-neutral, socket initialization */
+ htbl = &maAdvConfigHash;
+ for (i = 0; i < HASH_TBL_SIZE && htbl->size; i++) {
+ pentry = htbl->buckets[i];
+ while (pentry != NULL) {
+ if (InitSockets((MaAdvConfigEntry *)pentry->data) < 0) {
+ syslog(LOG_CRIT, "InitSockets failed.");
+ return (-1);
+ }
+ pentry = pentry->next;
+ }
+ }
+
+ /* Set up periodic house keeping and other chores */
+ SetSigHandlers();
+
+ return (0);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mipagent/agentKernelIntfce.c b/usr/src/cmd/cmd-inet/usr.lib/mipagent/agentKernelIntfce.c
new file mode 100644
index 0000000000..9136568ab3
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mipagent/agentKernelIntfce.c
@@ -0,0 +1,2727 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1999-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Routines in this program implement OS-specific portions of
+ * a Mobile-IP agent (RFC 2002).
+ *
+ */
+
+#ifndef _REENTRANT
+#error "Error! Reentrant must be defined!"
+#endif
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <errno.h>
+#include <assert.h>
+#include <synch.h>
+#include <syslog.h>
+#include <stropts.h>
+#include <sys/dlpi.h>
+#include <sys/types.h>
+#include <sys/stream.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/errno.h>
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/stropts.h>
+#include <sys/stropts.h>
+#include <sys/types.h>
+
+#include <inet/common.h>
+#include <inet/ip.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <net/if_dl.h>
+#include <net/pfkeyv2.h> /* ipsec alg values, etc. */
+
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include <arpa/inet.h>
+
+#include "agent.h"
+#include "mip.h"
+#include "agentKernelIntfce.h"
+#include "conflib.h"
+
+/* Check for bad os version, and compile anyway xxx WORK -- remove this */
+#ifndef IFF_NORTEXCH
+#define IFF_NORTEXCH 0
+#define IFF_MIPRUNNING 1
+#define RTF_PRIVATE 2
+#endif
+
+/*
+ * Routing socket message len for creating route for
+ * reverse tunnel
+ */
+
+#define MIP_RTUN_RTM_MSGLEN sizeof (struct rt_msghdr) + \
+ sizeof (struct sockaddr_in) + \
+ sizeof (struct sockaddr_in) + \
+ sizeof (struct sockaddr_in) + \
+ sizeof (struct sockaddr_dl) + \
+ sizeof (struct sockaddr_in) + \
+ sizeof (struct sockaddr_dl)
+
+/*
+ * Routing socket message len for normal route
+ * created to send reg reply to MN
+ */
+#define MIP_RTM_MSGLEN sizeof (struct rt_msghdr) + \
+ sizeof (struct sockaddr_in) + \
+ sizeof (struct sockaddr_in) + \
+ sizeof (struct sockaddr_in)
+
+/* Common to all mobility agents */
+extern struct hash_table haMobileNodeHash;
+extern struct hash_table maAdvConfigHash;
+extern struct hash_table mipTunlHash;
+extern int logVerbosity;
+
+struct dynamicIfacetype *dynamicIfaceHead;
+
+static int ioctl_sockid;
+static int rtsock;
+static rwlock_t gbl_tunnelLock;
+static int first_tun_to_check = 0;
+static struct staticIface *StaticIntfaceHead = NULL;
+
+char *err2str(int);
+
+#define DEVICEDIR "/dev/"
+#ifndef ETH_ALEN
+#define ETH_ALEN sizeof (struct ether_addr)
+#endif
+
+/* Definitions needed for refresh_mn_arp() module */
+#define ABS(x) ((x) < 0 ? -(x) : (x))
+#define BITSPERBYTE 8
+#define IPADDRL sizeof (struct in_addr)
+#define DEVICEDIR "/dev/"
+#define BCAST_HW_ADDR "ff:ff:ff:ff:ff:ff"
+
+#define MAX_ERR 2048
+#define INVALID_PPA (MAX_ERR + 1)
+#define INVALID_STRING (MAX_ERR + 2)
+#define DLOKACK_SHORT_RESPONSE (MAX_ERR + 3)
+#define DLOKACK_NOT_M_PCPROTO (MAX_ERR + 4)
+#define ERR_MORECTL (MAX_ERR + 5)
+#define ERR_MOREDATA (MAX_ERR + 6)
+#define ERR_MORECTLDATA (MAX_ERR + 7)
+#define DL_PRIMITIVE_ERROR (MAX_ERR + 8)
+#define SHORT_CONTROL_PORTION (MAX_ERR + 9)
+#define INVALID_ADDR (MAX_ERR + 10)
+#define MN_ENTRY_ABSENT (MAX_ERR + 11)
+#define NO_MAPPING (MAX_ERR + 12)
+
+/* Tunnel related definitions */
+#define MAX_TUNNEL_SUPPORTED 256
+#define NO_FREE_TUNNEL (MAX_ERR + 1)
+#define TUNNEL_NOT_FOUND (MAX_ERR + 2)
+#define DEV_NAME_NOT_FOUND ENXIO
+#define INVALID_IP_ADDR ENXIO
+#define MN_ENTRY_ABSENT (MAX_ERR + 11)
+#define NO_MAPPING (MAX_ERR + 12)
+
+
+/* Table for mapping tunnelno with mobile-node address */
+/*
+ * WORK -- this will be removed once the tunneling interface returns us
+ * a unique tunnel id.
+ */
+static struct {
+ rwlock_t tunlLock;
+ ipaddr_t mnaddr;
+ ipaddr_t tunnelsrc; /* only used on FA side */
+ uint32_t refcnt;
+} mnaddr_tunl[MAX_TUNNEL_SUPPORTED + 1];
+
+
+#define MAXWAIT 15
+/* Maximum address buffer length */
+#define MAXDLADDR 1024
+/* Handy macro. */
+#define OFFADDR(s, n) (unsigned char *)((char *)(s) + (int)(n))
+
+
+/* Internal Prototypes */
+static int settaddr(int tnum, ipaddr_t ifaddr1, ipaddr_t ifaddr2,
+ ipaddr_t saddr, ipaddr_t daddr, ipsec_req_t *);
+static int garp(char *, uint32_t, unsigned char *);
+static int plumb_one_tun(int tnum);
+static int unplumb_one_tun(int muxfd);
+static int setifflags(int tnum, int value);
+static int strgetmsg(int fd, struct strbuf *ctlp, struct strbuf *datap,
+ int *flagsp);
+static int expecting(int prim, union DL_primitives *dlp);
+static int arpgetHWaddr(ipaddr_t, unsigned char *);
+int gettunnelno(ipaddr_t, ipaddr_t);
+
+int arpIfadd(ipaddr_t, char *, uint32_t);
+int arpIfdel(ipaddr_t, char *, uint32_t);
+
+/* External Prototypes */
+extern MipTunlEntry *CreateTunlEntry(int tnum, ipaddr_t target, ipaddr_t tsrc,
+ int muxfd);
+extern char *ntoa(uint32_t addr_long, char *addr_string);
+extern MobilityAgentEntry *findMaeFromIp(ipaddr_t, int);
+
+/*
+ * Function: InitTunnelModule
+ *
+ * Arguments: none
+ *
+ * Description: Initialize our globals. Currently, initalize the global
+ * tunnel lock.
+ *
+ * Returns: int (zero on success)
+ */
+static int
+InitTunnelModule()
+{
+ int result;
+
+ result = rwlock_init(&gbl_tunnelLock, USYNC_THREAD, NULL);
+
+ return (result);
+} /* InitTunnelModule */
+
+/*
+ * Function: newtunnel
+ *
+ * Arguments: ipaddr_t addr
+ * ipaddr_t tsrc_addr
+ * int *tunnels_scanned
+ *
+ * Description: This function returns the available tunnel number in the
+ * range 0 through MAX_TUNNEL_SUPPORTED. If none are available
+ * it returns NO_FREE_TUNNEL. tunnels_scanned is used by the caller
+ * to figure out if all the tunnel numbers are scanned before
+ * concluding that there's no free tunnel left.
+ *
+ * Returns: int (tunnel number, or -1 on error)
+ */
+static int
+newtunnel(ipaddr_t addr, ipaddr_t tsrc_addr, int *tunnels_scanned)
+{
+ int i;
+ boolean_t found = _B_FALSE;
+ int tun_num;
+
+ /* WARNING: check this xxx WORK */
+ (void) rw_wrlock(&gbl_tunnelLock);
+ *tunnels_scanned = 0;
+
+ for (i = 0; i < MAX_TUNNEL_SUPPORTED; i++) {
+ /* start scanning tunnel numbers where we were left last time */
+ tun_num = (i + first_tun_to_check) % MAX_TUNNEL_SUPPORTED;
+
+ /* is this an available tunnel? */
+ if (mnaddr_tunl[tun_num].mnaddr == 0) {
+ mnaddr_tunl[tun_num].mnaddr = addr;
+ mnaddr_tunl[tun_num].tunnelsrc = tsrc_addr;
+ found = _B_TRUE;
+ break;
+ }
+ }
+
+ /*
+ * Send back the number of tunnels scanned, so that caller can
+ * give up after scanning MAX_TUNNEL_SUPPORTED many tunnels.
+ * Also save where we were left, so that next time we start scanning
+ * from that point on.
+ */
+ if (found) {
+ *tunnels_scanned = i + 1;
+ first_tun_to_check = (tun_num + 1) % MAX_TUNNEL_SUPPORTED;
+ } else {
+ *tunnels_scanned = MAX_TUNNEL_SUPPORTED;
+ /*
+ * First_tun_to_check stays same. We did a one full round of
+ * scanning tunnel numbers and couldn't find any available.
+ * Next time we come here to find another available, we'll
+ * start from the same tunnel number.
+ */
+ }
+
+ (void) rw_unlock(&gbl_tunnelLock);
+
+
+ if (found) {
+ return (tun_num);
+ } else {
+ return (-1);
+ }
+} /* newtunnel */
+
+
+/*
+ * Function: freetunnel
+ *
+ * Arguments: int tnum
+ *
+ * Description: This function will free the current tunnel resource. This
+ * function will disappear with the new kernel tunnel interface.
+ *
+ * Returns: void
+ */
+static void
+freetunnel(int tnum)
+{
+
+ (void) rw_wrlock(&gbl_tunnelLock);
+
+ assert(mnaddr_tunl[tnum].refcnt == 0);
+
+ mnaddr_tunl[tnum].mnaddr = 0;
+ mipverbose(("Freeing tunnel module number %d\n", tnum));
+
+ (void) rw_unlock(&gbl_tunnelLock);
+} /* freetunnel */
+
+
+/*
+ * Gets the tunnel number corresponding to given
+ * mobile node address .
+ * If the corresponding tunnel number exist then it return s the tunnel no.
+ * else return s -(TUNNEL_NOT_FOUND)
+ */
+/*
+ * Function: gettunnelno
+ *
+ * Arguments: ipaddr_t mnaddr
+ * ipaddr_t tsrc_addr
+ *
+ * Description: Returns the tunnel number corresponding to a given mobile
+ * node address. If the corresponding tunnel number exists, then
+ * it returns the tunnel number. Otherwise it returns (-1)
+ * When called from HA mnaddr=mnaddr.
+ * when called from FA mnaddr=haaddr
+ * Tunnel source end-point addr=tsrc_addr
+ *
+ * Returns: int (Tunnel Number, -1 on failure)
+ */
+int
+gettunnelno(ipaddr_t mnaddr, ipaddr_t tsrc_addr)
+{
+ int i;
+ int found = _B_FALSE;
+
+ (void) rw_rdlock(&gbl_tunnelLock);
+
+ for (i = 0; i < MAX_TUNNEL_SUPPORTED; i++) {
+ if (mnaddr_tunl[i].mnaddr == mnaddr &&
+ mnaddr_tunl[i].tunnelsrc == tsrc_addr) {
+ found = _B_TRUE;
+ break;
+ }
+ }
+
+ (void) rw_unlock(&gbl_tunnelLock);
+
+ if (found == _B_TRUE) {
+ return (i);
+ } else {
+ return (-1);
+ }
+} /* gettunnelno */
+
+#if 0
+/*
+ * Function: printtunnels
+ *
+ * Arguments: none
+ *
+ * Description: This function prints out all the tunnels. It is used for
+ * debugging.
+ *
+ * Returns: void
+ */
+void
+printtunnels()
+{
+ int i;
+ char mnaddr[INET_ADDRSTRLEN];
+
+ (void) rw_rdlock(&gbl_tunnelLock);
+
+ for (i = 0; i < MAX_TUNNEL_SUPPORTED; i++) {
+ if (mnaddr_tunl[i].mnaddr != 0) {
+ mipverbose(("Tunnel %d is for %s with refcnt %d\n",
+ i, ntoa(mnaddr_tunl[i].mnaddr, mnaddr),
+ mnaddr_tunl[i].refcnt));
+ }
+ }
+
+ (void) rw_unlock(&gbl_tunnelLock);
+} /* printtunnels */
+#endif
+
+
+/* Convert a negative error value into a human readable error message */
+/*
+ * Function: err2str
+ *
+ * Arguments: int errval
+ *
+ * Description: This function tries to return an error message based on
+ * an error code, or errno. It first checks for special errors,
+ * then checks the strerror string.
+ * WARNING: If the error is not found, then it returns a
+ * pointer to a static string. This function is NOT thread safe.
+ *
+ * Returns: char * (error string)
+ */
+char *
+err2str(int errval)
+{
+ int err = (-1 * errval);
+ static char tmp[30];
+
+ switch (err) {
+ case INVALID_PPA:
+ return ("Invalid PPA");
+
+ case INVALID_STRING:
+ return ("Invalid input string");
+
+ case DLOKACK_SHORT_RESPONSE:
+ return ("Short DLOKACK response");
+
+ case DLOKACK_NOT_M_PCPROTO:
+ return ("DLOKACK is not M_PCPROTO");
+
+ case ERR_MORECTL:
+ return ("MORECTL");
+
+ case ERR_MOREDATA:
+ return ("MOREDATA");
+
+ case ERR_MORECTLDATA:
+ return ("MORECTL or MOREDATA");
+
+ case SHORT_CONTROL_PORTION:
+ return ("Control portion is short");
+
+ case DL_PRIMITIVE_ERROR:
+ return ("DL primitive error");
+
+ case INVALID_ADDR:
+ return ("Invalid Address");
+
+ case MN_ENTRY_ABSENT:
+ return ("Missing Mobile node entry");
+
+ case NO_MAPPING:
+ return ("Bad ARP mapping for mobile node");
+
+ default:
+ {
+ /* Check for errno error */
+ char *reason;
+
+ reason = strerror(err);
+
+ if (reason != NULL) {
+ return (reason);
+ } else {
+ (void) sprintf(tmp, "Reason unclear : %d",
+ err);
+ return (tmp);
+ }
+ }
+ } /* switch */
+} /* err2str */
+
+
+/*
+ * Function: ifname2devppa
+ *
+ * Arguments: char *ifname, char *devname, int *ppa
+ *
+ * Description: Parse the interface name(e.g. "le0" or "hme1") and place the
+ * corresponding device name(e.g. "/dev/le", "/dev/hme") in
+ * devname and the ppa(e.g. 0 or 1 for the examples above) in ppa.
+ * It expects that the caller will pass adequate buffer in
+ * 'devname' such that it can hold the full devicename.
+ *
+ * Returns: void
+ */
+void
+ifname2devppa(char *ifname, char *devname, int *ppa)
+{
+ char *p;
+ int i, j, val;
+
+ val = 0;
+ j = strlen(DEVICEDIR);
+ (void) strncpy(devname, DEVICEDIR, j);
+ for (i = 0, p = ifname; i < strlen(ifname); i++, p++) {
+ if (*p >= '0' && *p <= '9')
+ val = 10*val + (*p - '0');
+ else
+ devname[j++] = *p;
+ }
+ devname[j] = '\0';
+ *ppa = val;
+} /* ifname2devppa */
+
+
+/*
+ * Function: mkpt2pt
+ *
+ * Arguments: char *ifname, ipaddr_t srcaddr, ipaddr_t dstaddr
+ *
+ * Description: Configure the point-to-point interface named ifname with the
+ * given address. The source address being 'srcaddr' and
+ * destination address being 'dstaddr'
+ *
+ * Returns: int
+ */
+static int
+mkpt2pt(char *ifname, ipaddr_t srcaddr, ipaddr_t dstaddr)
+{
+ struct lifreq lifr;
+ struct sockaddr_in *sin;
+
+ /* set the interface address */
+ (void) memset((char *)&lifr, 0, sizeof (lifr));
+ (void) strncpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
+ sin = (struct sockaddr_in *)&lifr.lifr_addr;
+ (void) memset(sin, 0, sizeof (*sin));
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = srcaddr;
+
+ if (ioctl(ioctl_sockid, SIOCSLIFADDR, (caddr_t)&lifr) < 0) {
+ syslog(LOG_ERR, "SIOCSLIFADDR failed");
+ return (-1);
+ }
+
+ /* set the remote/peer address */
+ (void) memset((char *)&lifr, 0, sizeof (lifr));
+ (void) strncpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
+ sin = (struct sockaddr_in *)&lifr.lifr_addr;
+ (void) memset(sin, 0, sizeof (*sin));
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = dstaddr;
+
+ if (dstaddr && (ioctl(ioctl_sockid, SIOCSLIFDSTADDR,
+ (caddr_t)&lifr) < 0)) {
+ syslog(LOG_ERR, "SIOCSIFDSTADDR failed");
+ return (-1);
+ }
+
+ return (0);
+} /* mkpt2pt */
+
+/*
+ * Function: InitNet
+ *
+ * Arguments: none
+ *
+ * Description: Network-related initialization, e.g. loading tunneling module
+ * etc. Information about each mobility supporting interface is
+ * available in maAdvConfigHash
+ *
+ * Returns: int (zero on success)
+ */
+int
+InitNet()
+{
+ /*
+ * Enable forwarding, disable ICMP redirects in kernel, e.g. FA
+ * should not redirect visiting mobile nodes to another router).
+ */
+ (void) system(
+ "/usr/sbin/ndd -set /dev/ip "
+ "ip_forwarding 1 > /dev/null 2>&1\n");
+ (void) system(
+ "/usr/sbin/ndd -set /dev/ip "
+ "ip_send_redirects 0 > /dev/null 2>&1\n");
+
+ mipverbose(("IP forwarding on, ICMP redirects off.\n"));
+
+ /* Initialize the tunnel management module */
+ if (InitTunnelModule() < 0) {
+ syslog(LOG_ERR, "InitTunnelModule failed.");
+ return (-1);
+ }
+
+ /* Make a socket for the various SIOCxxxx ioctl commands */
+ if ((ioctl_sockid = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ syslog(LOG_ERR,
+ "Error: could not create socket for SIOCxxxx commands.");
+ return (-1);
+ }
+
+ /* Open a routing socket for passing route commands */
+ if ((rtsock = socket(AF_ROUTE, SOCK_RAW, AF_INET)) < 0) {
+ syslog(LOG_ERR,
+ "Error: could not create socket for Route commands.");
+ return (-1);
+ }
+ return (0);
+} /* InitNet */
+
+/*
+ * Function: encapadd
+ *
+ * Arguments: ipaddr_t target, ipaddr_t tsrc, uint32_t tdst, uint8_t tflags
+ *
+ * Description: Enables encapsulation of pkts meant for target addr to the
+ * tunnel's destination addr (tdst) with the source address
+ * being that of the specified agent (tsrc). To configure an
+ * IP-in-IP tunnel, first get an unused tunnel number and
+ * then plumb it. Next, invoke any ipsec policies to ensure outbound
+ * tunnel packets to, and incomming tunnel packets from the agent-peer are
+ * protected (or we, or they will discard)! We do NOT set the peer-flag
+ * here as it's unnecessary, and should have been done anyway when we got
+ * the registration request (peer status isn't, after all, just about
+ * having a security association). Next set up a point-to-point
+ * interface between the target addr (target) - this is
+ * usually the mobile node address and address of the specified
+ * agent (tsrc) - this is usually the home agent address;
+ * the routine also sets up the tunnel with the source address
+ * of the tunnel being the specified agent address (tsrc)
+ * and the destination address of the tunnel being the
+ * address (tdst) - this usually is the foreign agent address.
+ * Next a number of interface specific flags are set and
+ * the tunnel is enabled. The tunnel specific entry data is
+ * next added to the hash table, keying on target address.
+ *
+ * If a tunnel entry already exists for the target addr
+ * increase the entry reference count.
+ *
+ * For now, tflags only identify if we're to protect a reverse tunnel,
+ * but in the future it can be used to identify when a tunnel other than
+ * an IP in IP tunnel should be set up.
+ *
+ * Returns: int
+ */
+int
+encapadd(ipaddr_t target, ipaddr_t tsrc, uint32_t tdst, uint8_t tflags)
+{
+ int tnum, muxfd;
+ MipTunlEntry *entry;
+ int tunnels_scanned; /* no. of tunnels scanned in a */
+ /* single newtunnel() call. */
+ int total_tunnels_scanned; /* total no. of tunnels scanned */
+ /* in this encapadd() call. */
+ MobilityAgentEntry *mae; /* for IPsec Tunnel SA */
+ ipsec_req_t *ipsr_p = NULL; /* for ipsec tunnel policy */
+
+ /*
+ * We don't need a MipTunlEntryLookup here to match
+ * with destination endpoint address as we know that
+ * the target(mnaddr) is unique per HA
+ */
+ if ((entry = (MipTunlEntry *)findHashTableEntryUint(
+ &mipTunlHash, target, LOCK_WRITE, NULL, 0, 0,
+ 0)) != NULL) {
+ entry->refcnt++;
+ (void) rw_unlock(&entry->TunlNodeLock);
+ return (0);
+ }
+
+ total_tunnels_scanned = 0;
+ muxfd = -1;
+
+ while ((total_tunnels_scanned < MAX_TUNNEL_SUPPORTED) &&
+ (muxfd == -1)) {
+ if ((tnum = newtunnel(target, tsrc, &tunnels_scanned)) < 0) {
+ syslog(LOG_ERR, "encapadd: couldnot find free tnum");
+ return (-1);
+ }
+ total_tunnels_scanned += tunnels_scanned;
+
+ if ((muxfd = plumb_one_tun(tnum)) == -1)
+ freetunnel(tnum);
+ }
+ if (muxfd == -1) {
+ syslog(LOG_ERR, "encapadd: couldnot find free tnum");
+ return (-1);
+ }
+
+ /*
+ * Before we can call settaddr, we need to see if we should pass down
+ * any IPSEC SAs so treqs can be set.
+ */
+ if ((mae = findMaeFromIp(tdst, LOCK_READ)) != NULL) {
+ /* for encapadd, we're an HA using "ipSecTunnel apply ..." */
+
+ if (IPSEC_TUNNEL_ANY(mae->maIPsecSAFlags[IPSEC_APPLY])) {
+ /* pass down what we've parsed */
+ ipsr_p = &(mae->maIPsecTunnelIPSR[IPSEC_APPLY]);
+ mae->maIPsecFlags |= IPSEC_TUNNEL_APPLY;
+
+ /* Symetric tunnels: should we set the reverse bit? */
+ if (tflags & REG_REVERSE_TUNNEL)
+ mae->maIPsecFlags |=
+ IPSEC_REVERSE_TUNNEL_PERMIT;
+ }
+
+ /* unlock */
+ (void) rw_unlock(&mae->maNodeLock);
+ }
+
+
+ if (settaddr(tnum, tsrc, target, tsrc, tdst, ipsr_p) == -1) {
+ syslog(LOG_ERR, "encapadd: settaddr failed");
+ (void) unplumb_one_tun(muxfd);
+ mipverbose(("unplumb tunnel with number %d\n", tnum));
+ freetunnel(tnum);
+ return (-1);
+ }
+
+ if (setifflags(tnum, IFF_UP | IFF_NORTEXCH | IFF_MIPRUNNING |
+ IFF_PRIVATE) == -1) {
+ syslog(LOG_ERR, "encapadd: setifflags failed");
+ mipverbose(("unplumb tunnel with number %d\n", tnum));
+ (void) unplumb_one_tun(muxfd);
+ freetunnel(tnum);
+ return (-1);
+ }
+
+ if ((entry = CreateTunlEntry(tnum, target, tsrc, muxfd)) == NULL) {
+ syslog(LOG_ERR, "encapadd: CreateTunlEntry failed");
+ mipverbose(("unplumb tunnel with number %d\n", tnum));
+ (void) unplumb_one_tun(muxfd);
+ freetunnel(tnum);
+ return (-1);
+ }
+
+ entry->refcnt++;
+
+ (void) rw_unlock(&entry->TunlNodeLock);
+
+ return (0);
+} /* encapadd */
+
+/*
+ * Function: encaprem
+ *
+ * Arguments: ipaddr_t target
+ *
+ * Description: Terminates encapulation service for target addr.
+ * First find the tunnel entry in the hash table.
+ * If this is the last reference count to the tunnel entry
+ * then unplumb the tunnel and break the tunnel association
+ * between the foreign agent and home agent for
+ * encapsulation of packets destined for the target address.
+ * Next free the tunnel and the tunnel entry from hash table.
+ *
+ * Returns: int -1 on failure, the tunnel reference count on success.
+ */
+int
+encaprem(ipaddr_t target)
+{
+ int tnum, muxfd;
+ MipTunlEntry *entry;
+
+ /*
+ * NOTE: We do not need to call MipTunlEntryLookup here
+ * because we assume MN home address(target) is unique per HA.
+ */
+ if ((entry = (MipTunlEntry *)findHashTableEntryUint(
+ &mipTunlHash, target, LOCK_WRITE, NULL, 0, 0, 0)) == NULL) {
+ syslog(LOG_ERR, "encaprem: Target entry %x missing", target);
+ return (-1);
+ }
+
+ tnum = entry->tunnelno;
+ muxfd = entry->mux_fd;
+
+ if (entry->refcnt == 1) {
+ (void) setifflags(entry->tunnelno,
+ -IFF_UP | -IFF_NORTEXCH | -IFF_MIPRUNNING);
+ mipverbose(("unplumb tunnel with number %d\n", tnum));
+ if (unplumb_one_tun(muxfd) == -1) {
+ (void) rw_unlock(&entry->TunlNodeLock);
+ syslog(LOG_ERR,
+ "encaprem: unplumb of tunnel %d failed", tnum);
+ return (-1);
+ }
+ }
+
+ /* if refcnt is 0, this encapsulation point just went away */
+ if (--entry->refcnt == 0) {
+ /* WORK:Todo: This should call delHashTableEntry */
+ int index = HASHIT(target);
+ HashTable *htbl = &mipTunlHash;
+ HashEntry *p, *q;
+
+ freetunnel(tnum);
+
+ (void) rw_wrlock(&htbl->bucketLock[index]);
+ p = htbl->buckets[index];
+
+ while (p) {
+ if (p->key == target)
+ break;
+ q = p;
+ p = p->next;
+ }
+
+ if (p == htbl->buckets[index])
+ htbl->buckets[index] = p->next;
+ else
+ q->next = p->next;
+
+ (void) rw_unlock(&entry->TunlNodeLock);
+ (void) rwlock_destroy(&entry->TunlNodeLock);
+
+ free(entry);
+ free(p);
+
+ (void) rw_wrlock(&htbl->hashLock);
+ htbl->size--;
+ (void) rw_unlock(&htbl->hashLock);
+
+ (void) rw_unlock(&htbl->bucketLock[index]);
+ } else {
+ /* Release the lock held in findHashTableEntryUint */
+ (void) rw_unlock(&entry->TunlNodeLock);
+ }
+
+ return (entry->refcnt);
+} /* encaprem */
+
+
+/*
+ * Function: decapadd
+ *
+ * Arguments: ipaddr_t ipipsrc, ipaddr_t ipipdst
+ *
+ * Description: Enable decapsulation service for target addr. To configure
+ * an IP-in-IP tunnel, first get an unused tunnel number and
+ * then plumb it. Next set up a point-to-point interface
+ * between the ipipdst addr - this is usually the foreign agent
+ * address and a dummy address ("0.0.0.0"); the routine
+ * also sets up the tunnel with the source address
+ * of the tunnel being ipipdst - usually foreign agent address
+ * and the destination address of the tunnel being the address
+ * ipipsrc - this usually is the home agent address. Next a
+ * number of interface specific flags are set and the tunnel is
+ * enabled. The tunnel specific entry data is next added to
+ * the hash table, keying on ipipsrc address.
+ *
+ * If a tunnel entry already exists for the ipipsrc addr
+ * increase the entry reference count.
+ * This function is called at the foreign agent end of tunnel.
+ *
+ * Returns: int (zero on success)
+ */
+int
+decapadd(ipaddr_t ipipsrc, ipaddr_t ipipdst)
+{
+ int tnum, muxfd;
+ MipTunlEntry *entry;
+ int tunnels_scanned; /* no. of tunnels scanned in a */
+ /* single newtunnel() call. */
+ int total_tunnels_scanned; /* total no. of tunnels scanned */
+ /* in this decapadd() call. */
+ MobilityAgentEntry *mae; /* to check for IPsec policy */
+ ipsec_req_t *ipsr_p = NULL; /* for ipsec tunnel policy */
+
+ if ((entry = (MipTunlEntry *)findHashTableEntryUint(
+ &mipTunlHash, ipipsrc, LOCK_WRITE, MipTunlEntryLookup,
+ (uint32_t)ipipdst, 0, 0)) != NULL) {
+ entry->refcnt++;
+ (void) rw_unlock(&entry->TunlNodeLock);
+ return (0);
+ }
+
+ total_tunnels_scanned = 0;
+ muxfd = -1;
+
+ while ((total_tunnels_scanned < MAX_TUNNEL_SUPPORTED) &&
+ (muxfd == -1)) {
+ if ((tnum = newtunnel(ipipsrc, ipipdst,
+ &tunnels_scanned)) < 0) {
+ syslog(LOG_ERR, "decapadd: couldnot find free tnum");
+ return (-1);
+ }
+ total_tunnels_scanned += tunnels_scanned;
+
+ if ((muxfd = plumb_one_tun(tnum)) == -1)
+ freetunnel(tnum);
+ }
+ if (muxfd == -1) {
+ syslog(LOG_ERR, "decapadd: couldnot find free tnum");
+ return (-1);
+ }
+
+ /*
+ * Before tunnel is plumbed, and the interface is created, see if we
+ * have an IPsecPolicy. If so, point at it for settaddr().
+ */
+ if ((mae = findMaeFromIp(ipipsrc, LOCK_READ)) != NULL) {
+ /*
+ * for decapadd, we're an FA using "IPsecTunnel permit ..."
+ * Note that we set the IPSEC_REVERSE_TUNNEL_PERMIT flag when
+ * processing for a reverse-tunnel request.
+ * Note that we don't check to see if the IPSEC_TUNNEL_PERMIT
+ * flag is set because we always want to make sure the tunnel's
+ * protected correctly.
+ */
+ if (IPSEC_TUNNEL_ANY(mae->maIPsecSAFlags[IPSEC_PERMIT])) {
+ /* pass down what we've parsed */
+ ipsr_p = &(mae->maIPsecTunnelIPSR[IPSEC_PERMIT]);
+
+ /* set the invoked bit in case we have to restore */
+ mae->maIPsecFlags |= IPSEC_TUNNEL_PERMIT;
+ }
+
+ /* unlock */
+ (void) rw_unlock(&mae->maNodeLock);
+ }
+
+ /*
+ * Tunnels in Solaris are bi-directional, with the obvious caveat that
+ * the dst address must be set. For security reasons, we only do this
+ * if the MN is requesting a reverse tunnel. If so, ipipsrc should be
+ * the MN's home agent address. ipsr contains our ipsec values.
+ * From FA end the parameters are : tsrc=COA, tdst=HAA, dstaddr=0.0.0.0
+ * srcaddr=COA
+ */
+ if (settaddr(tnum, ipipdst, inet_addr("0.0.0.0"), ipipdst,
+ ipipsrc, ipsr_p) == -1) {
+ syslog(LOG_ERR, "decapadd: settaddr failed");
+ mipverbose(("unplumb tunnel with number %d\n", tnum));
+ (void) unplumb_one_tun(muxfd);
+ freetunnel(tnum);
+ return (-1);
+ }
+
+ if (setifflags(tnum, IFF_UP | IFF_NORTEXCH | IFF_MIPRUNNING) == -1) {
+ syslog(LOG_ERR, "decapadd: setifflags failed");
+ mipverbose(("unplumb tunnel with number %d\n", tnum));
+ (void) unplumb_one_tun(muxfd);
+ freetunnel(tnum);
+ return (-1);
+ }
+
+ /* Entry will be locked after CreateTunlEntry */
+ if ((entry = CreateTunlEntry(tnum, ipipsrc, ipipdst, muxfd)) == NULL) {
+ syslog(LOG_ERR, "decapadd: CreateTunlEntry failed");
+ mipverbose(("unplumb tunnel with number %d\n", tnum));
+ (void) unplumb_one_tun(muxfd);
+ freetunnel(tnum);
+ return (-1);
+ }
+
+ entry->refcnt++;
+ (void) rw_unlock(&entry->TunlNodeLock);
+
+ return (0);
+} /* decapadd */
+
+/*
+ * Function: decaprem
+ *
+ * Arguments: ipaddr_t target : Tunnel outer destination IP address at FA
+ * ipaddr_t tsrc : Tunnel outer source IP address at FA
+ *
+ * Description: Terminates decapulation service for target address.
+ * First find the tunnel entry in the hash table.
+ * If this is the last reference count to the tunnel entry
+ * then unplumb the tunnel and break the tunnel association
+ * between the foreign agent and home agent for
+ * decapsulation of packets. Next free the tunnel and the
+ * tunnel entry from hash table.
+ *
+ * Returns: int (zero on success)
+ */
+int
+decaprem(ipaddr_t target, ipaddr_t tsrc)
+{
+ int tnum, muxfd;
+ MipTunlEntry *entry;
+
+ if ((entry = (MipTunlEntry *)findHashTableEntryUint(
+ &mipTunlHash, target, LOCK_WRITE, MipTunlEntryLookup,
+ (uint32_t)tsrc, 0, 0)) == NULL) {
+ syslog(LOG_ERR, "encaprem: Target entry %x missing", target);
+ return (-1);
+ }
+
+ tnum = entry->tunnelno;
+ muxfd = entry->mux_fd;
+
+ if (entry->refcnt == 1) {
+ (void) setifflags(entry->tunnelno,
+ -IFF_UP | -IFF_NORTEXCH | -IFF_MIPRUNNING);
+ mipverbose(("unplumb tunnel with number %d\n", tnum));
+ if (unplumb_one_tun(muxfd) == -1) {
+ (void) rw_unlock(&entry->TunlNodeLock);
+ syslog(LOG_ERR,
+ "decaprem: unplumb of tunnel %d failed", tnum);
+ return (-1);
+ }
+ }
+
+
+ if (--entry->refcnt == 0) {
+ int index = HASHIT(target);
+ HashTable *htbl = &mipTunlHash;
+ HashEntry *p, *q;
+ MipTunlEntry *Tunentry;
+
+ freetunnel(tnum);
+
+ (void) rw_wrlock(&htbl->bucketLock[index]);
+ p = htbl->buckets[index];
+
+ while (p) {
+ Tunentry = (MipTunlEntry *)p->data;
+ if (p->key == target && Tunentry->tunnelsrc == tsrc)
+ break;
+ q = p;
+ p = p->next;
+ }
+
+ if (p == htbl->buckets[index])
+ htbl->buckets[index] = p->next;
+ else
+ q->next = p->next;
+
+ (void) rw_unlock(&entry->TunlNodeLock);
+ (void) rwlock_destroy(&entry->TunlNodeLock);
+
+ free(entry);
+ free(p);
+
+ (void) rw_wrlock(&htbl->hashLock);
+ htbl->size--;
+ (void) rw_unlock(&htbl->hashLock);
+
+ (void) rw_unlock(&htbl->bucketLock[index]);
+ } else {
+ /*
+ * Release the lock. Other mobile node(s) may be
+ * using this tunnel.
+ */
+ (void) rw_unlock(&entry->TunlNodeLock);
+ }
+
+ return (entry->refcnt);
+}
+
+
+/*
+ * Function: MipTunlEntryLookup
+ * Arguments: entry - Pointer to MipTunl entry
+ * p1- First parameter to match (Tunnel src-endpoint IPaddr)
+ * p2- Second Parameter to match (unused)
+ * p3- Second Parameter to match (unused)
+ * Description:
+ * This function is used to lookup a tunnel entry which is hashed
+ * by the tunnel destination endpoint address and matched with
+ * it's source end point address. This matching is necessary
+ * to support multihomed foreign agent with more than one COAs
+ */
+/* ARGSUSED */
+boolean_t
+MipTunlEntryLookup(void *entry, uint32_t p1, uint32_t p2, uint32_t p3)
+{
+ MipTunlEntry *Tunentry = entry;
+
+ if (Tunentry->tunnelsrc == (ipaddr_t)p1)
+ return (_B_TRUE);
+ return (_B_FALSE);
+}
+
+/*
+ * Function: arpadd
+ *
+ * Arguments: ipaddr_t host, unsigned char *eaddr, char *flag
+ *
+ * Description: Adds an arp entry with ip address set to host and hardware
+ * address set to eaddr with approriate flags specified by flag
+ * argument, e.g. arpadd(inet_addr("129.146.122.121"),
+ * "08:20:AB:FE:33:11", "pub") creates a proxy ARP entry for
+ * 129.146.122.121.
+ *
+ * Returns: int
+ */
+int
+arpadd(ipaddr_t host, unsigned char *eaddr, unsigned int flags)
+{
+ struct arpreq ar;
+ struct sockaddr_in *sin;
+
+ bzero((caddr_t)&ar, sizeof (ar));
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ sin = (struct sockaddr_in *)&ar.arp_pa;
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = (host);
+
+ (void) memcpy(ar.arp_ha.sa_data, eaddr, ETH_ALEN);
+
+ ar.arp_flags = ATF_PERM | flags;
+#if 0
+ if (strncmp(flag, "temp", 4) == 0)
+ ar.arp_flags &= ~ATF_PERM;
+ if (strncmp(flag, "pub", 3) == 0)
+ ar.arp_flags |= ATF_PUBL;
+ if (strncmp(flag, "trail", 5) == 0)
+ ar.arp_flags |= ATF_USETRAILERS;
+#endif
+
+ if (ioctl(ioctl_sockid, SIOCSARP, (caddr_t)&ar) < 0) {
+ if (errno != EEXIST)
+ return ((-1) * errno);
+ }
+
+ return (0);
+} /* arpadd */
+
+
+/*
+ * Function: arpdel
+ *
+ * Arguments: ipaddr_t host
+ *
+ * Description: Deletes an arp entry for ip address set to host
+ *
+ * Returns: int (zero on success)
+ */
+int
+arpdel(ipaddr_t host)
+{
+ struct arpreq ar;
+ struct sockaddr_in *sin;
+
+ bzero((caddr_t)&ar, sizeof (ar));
+ ar.arp_pa.sa_family = AF_INET;
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ sin = (struct sockaddr_in *)&ar.arp_pa;
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = (host);
+
+ if (ioctl(ioctl_sockid, SIOCDARP, (caddr_t)&ar) < 0) {
+ if (errno != ENXIO)
+ return ((-1) * errno);
+ }
+ return (0);
+} /* arpdel */
+
+
+/*
+ * Function: arpgetHWaddr
+ *
+ * Arguments: ipaddr_t mnaddr, unsigned char *mnetheraddr
+ *
+ * Description: Get the hardware address corresponding to mnaddr from
+ * the arp table.
+ *
+ * Returns: int (zero on success)
+ */
+static int
+arpgetHWaddr(ipaddr_t mnaddr, unsigned char *mnetheraddr)
+{
+ struct arpreq ar;
+ struct sockaddr_in *sin;
+
+ bzero((caddr_t)&ar, sizeof (ar));
+ ar.arp_pa.sa_family = AF_INET;
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ sin = (struct sockaddr_in *)&ar.arp_pa;
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = mnaddr;
+
+ if (ioctl(ioctl_sockid, SIOCGARP, (caddr_t)&ar) < 0)
+ return (-1 * (errno));
+ else {
+ (void) memcpy(mnetheraddr, ar.arp_ha.sa_data, ETH_ALEN);
+ return (0);
+ }
+} /* arpgetHWaddr */
+
+
+/*
+ * Function: arprefresh
+ *
+ * Arguments: HaMobileNodeEntry *hentry, ipaddr_t mnaddr
+ *
+ * Description: Sends gratuitous arp for the mnaddr using the hardware address
+ * found for mnaddr in its own ARP cache (making sure that the
+ * hardware address is not HA's own). The home agent calls
+ * arprefresh when a mobile node returns home and successfully
+ * deregisters itself.
+ *
+ * Returns: int (zero on success)
+ */
+int
+arprefresh(HaMobileNodeEntry *hentry, ipaddr_t mnaddr)
+{
+ int ret;
+ ipaddr_t ifaceaddr;
+ char devname[LIFNAMSIZ + 1];
+ unsigned char hainterfaceaddr[ETH_ALEN + 1];
+ unsigned char mnetheraddr[ETH_ALEN + 1];
+ unsigned char zero_addr[ETH_ALEN + 1] = {0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00};
+ MaAdvConfigEntry *mentry;
+
+ mipverbose(("Arp refresh called for %d.%d.%d.%d\n",
+ (unsigned char) ((ntohl(mnaddr) >> 24) & 0xff),
+ (unsigned char) ((ntohl(mnaddr) >> 16) & 0xff),
+ (unsigned char) ((ntohl(mnaddr) >> 8) & 0xff),
+ (unsigned char) (ntohl(mnaddr) & 0xff)));
+
+ ifaceaddr = hentry->haBindingIfaceAddr;
+
+ /* Get the matching maIfaceAddr from mnAdvConfigTable */
+ if ((mentry = (MaAdvConfigEntry *)findHashTableEntryUint(
+ &maAdvConfigHash, ifaceaddr, LOCK_NONE, NULL, 0, 0, 0)) == NULL) {
+ syslog(LOG_ERR, "Unable to find interface in hash table");
+ return (-1 * MN_ENTRY_ABSENT); /* Unlikely to occur */
+ }
+
+ (void) strcpy(devname, mentry->maIfaceName);
+ (void) memcpy(hainterfaceaddr, mentry->maIfaceHWaddr, ETH_ALEN);
+
+ if ((ret = arpgetHWaddr(mnaddr, mnetheraddr)) < 0)
+ return (ret);
+
+ if ((memcmp(hainterfaceaddr, mnetheraddr, ETH_ALEN) == 0) ||
+ (memcmp(zero_addr, mnetheraddr, ETH_ALEN) == 0))
+ return (-1 * NO_MAPPING);
+ else
+ return (garp(devname, mnaddr, mnetheraddr));
+} /* arprefresh */
+
+/*
+ * Function: routemodify
+ *
+ * Arguments: ipaddr_t dst, ipaddr_t gw,
+ * ipaddr_t insrc, int in_if, int out_if,
+ * unsigned int cmd
+ *
+ * Description: Add or Delete route depending on 'cmd' argument.
+ * 'cmd' argument can be either ADDRT or DELRT.
+ * For adding/deleting route from registration
+ * process and reply functions, only dst,gw args
+ * are required. Thus that is defined as simple route.
+ * NOTE: simple route is not used by mipagent registration
+ * reply process as it uses IP_XMIT_IF socket option
+ * instead of simple route. Simple route can not distinguish
+ * between two different mobile nodes with same private
+ * addresses. But the code still contains simple route
+ * section, in case it's needed in future for any purpose.
+ * After the visitor is accepted at FA, the
+ * forward route created from FA to MN to relay the
+ * packet from tunnel from home agent is defined
+ * as 'ftun_route'. This route must have in_if and out_if
+ * index arguments. For reverse tunnel route, this
+ * function expects a valid in_if and a valid out_if
+ * value and a non-zero source address(insrc). For ftun_route
+ * insrc must be zero.
+ *
+ * To set up forward route to reach MN:
+ * dst= MN's home addr, gw= COA, in_if=tun's if_index
+ * out_if = FA's interface index on which MN is attached.
+ * insrc = 0.0.0.0
+ *
+ * To set up reverse tunnel route:
+ * dst = 0.0.0.0 gw = 0.0.0.0 in_if = FA's interface index on
+ * which MN is attached, out_if = tunnel's interface index.
+ * insrc = MN's homeaddr
+ *
+ * Returns: int (zero on success)
+ */
+int
+routemodify(ipaddr_t dst, ipaddr_t gw, ipaddr_t insrc, int in_if,
+ int out_if, unsigned int cmd)
+{
+ struct rt_msghdr *rt_msg;
+ struct sockaddr_in *dstaddr;
+ struct sockaddr_in *gwaddr;
+ struct sockaddr_in *insrcaddr;
+ struct sockaddr_dl *rta_ifp;
+ struct sockaddr_dl *rta_srcifp;
+ char *cp;
+ static int rtmseq;
+ int rlen;
+ int flags = RTF_STATIC | RTF_UP;
+ boolean_t rtun_route = _B_FALSE; /* when insrc!=0, in_if, out_if !=0 */
+ boolean_t ftun_route = _B_FALSE; /* when insrc==0, in_if!=0 */
+
+ if (insrc == INADDR_ANY && in_if == 0 && out_if == 0) {
+ /* Simple route case dst<->gw: not used by mipagent */
+ flags = RTF_HOST | RTF_PRIVATE;
+ rlen = MIP_RTM_MSGLEN;
+ } else if (insrc == INADDR_ANY && in_if != 0 && out_if != 0) {
+ /* Forward route to MN from tunnel */
+ ftun_route = _B_TRUE;
+ flags = RTF_HOST | RTF_PRIVATE;
+ rlen = MIP_RTM_MSGLEN + 2 * (sizeof (struct sockaddr_dl));
+ } else if (insrc != INADDR_ANY && in_if != 0 && out_if != 0) {
+ /* Reverse tunnel route: insrc != 0 */
+ rtun_route = _B_TRUE;
+ flags = RTF_GATEWAY | RTF_PRIVATE;
+ rlen = MIP_RTUN_RTM_MSGLEN;
+ } else {
+ /* Invalid Call */
+ return (-1 * EINVAL);
+ }
+
+ rt_msg = (struct rt_msghdr *)malloc(rlen);
+ if (rt_msg == NULL) {
+ syslog(LOG_ERR, "route_modify: Cannot allocate memory");
+ return (-1 * (errno));
+ }
+
+ bzero(rt_msg, rlen);
+ rt_msg->rtm_msglen = rlen;
+ rt_msg->rtm_version = RTM_VERSION;
+ rt_msg->rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
+ rt_msg->rtm_pid = getpid();
+ if (cmd == ADDRT)
+ rt_msg->rtm_type = RTM_ADD;
+ else
+ rt_msg->rtm_type = RTM_DELETE;
+
+ rt_msg->rtm_seq = ++rtmseq;
+ rt_msg->rtm_flags = flags;
+
+ cp = (char *)rt_msg + sizeof (struct rt_msghdr);
+
+ /* DST */
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ dstaddr = (struct sockaddr_in *)cp;
+ dstaddr->sin_family = AF_INET;
+ if (!rtun_route)
+ dstaddr->sin_addr.s_addr = dst;
+ else
+ dstaddr->sin_addr.s_addr = INADDR_ANY;
+
+ /* GATEWAY */
+ cp += sizeof (struct sockaddr_in);
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ gwaddr = (struct sockaddr_in *)cp;
+ gwaddr->sin_family = AF_INET;
+ gwaddr->sin_addr.s_addr = gw;
+
+ /* NETMASK */
+ cp += sizeof (struct sockaddr_in);
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ dstaddr = (struct sockaddr_in *)cp;
+ dstaddr->sin_family = AF_INET;
+
+ if (!rtun_route) {
+ dstaddr->sin_addr.s_addr = IP_HOST_MASK;
+ } else {
+ dstaddr->sin_addr.s_addr = INADDR_ANY;
+ }
+
+ /* Check if ftun_route or rtun_route is set, else it's simple_route */
+
+ if (ftun_route) {
+ /*
+ * We need to set both RTA_IFP and RTA_SRCIFP
+ * in order to support Lucent PPP interfaces to
+ * mobile nodes. Since there may not be an interface
+ * route for the dynamically plumbed PPP interfaces
+ * which are used in 3Gwireless technology to connect
+ * to the mobile node from the PDSN (Packet Data Service
+ * Network, IS-835, TIA document), thus the foreign agent
+ * (PDSN) end of PPP interface may have non-unique address
+ * (for example, all these special PPP interfaces may have
+ * same address, COA in the PDSN end). So it is not
+ * possible to derive interface index from the supplied
+ * gateway address. Hence the caller of this function
+ * must provide both outgoing and incoming interface
+ * index when creating the forward tunnel.
+ */
+ rt_msg->rtm_addrs |= RTA_IFP | RTA_SRCIFP;
+
+ /* IFP */
+ cp += sizeof (struct sockaddr_in);
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ rta_ifp = (struct sockaddr_dl *)cp;
+ rta_ifp->sdl_family = AF_LINK;
+ rta_ifp->sdl_index = out_if;
+
+ /* SRCIFP */
+ cp += sizeof (struct sockaddr_dl);
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ rta_srcifp = (struct sockaddr_dl *)cp;
+ rta_srcifp->sdl_family = AF_LINK;
+ rta_srcifp->sdl_index = in_if;
+
+ } else if (rtun_route) {
+
+ /* it's a reverse tunnel route */
+
+ rt_msg->rtm_addrs |= (RTA_IFP | RTA_SRC | RTA_SRCIFP);
+
+ /* IFP */
+ cp += sizeof (struct sockaddr_in);
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ rta_ifp = (struct sockaddr_dl *)cp;
+ rta_ifp->sdl_family = AF_LINK;
+ rta_ifp->sdl_index = out_if;
+
+ /* SRC */
+ cp += sizeof (struct sockaddr_dl);
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ insrcaddr = (struct sockaddr_in *)cp;
+ insrcaddr->sin_family = AF_INET;
+ insrcaddr->sin_addr.s_addr = insrc;
+
+ /* SRCIFP */
+ cp += sizeof (struct sockaddr_in);
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ rta_srcifp = (struct sockaddr_dl *)cp;
+ rta_srcifp->sdl_family = AF_LINK;
+ rta_srcifp->sdl_index = in_if;
+ }
+
+ /* Send the routing message */
+ rlen = write(rtsock, rt_msg, rt_msg->rtm_msglen);
+ if (rlen > 0) {
+ if (cmd == ADDRT)
+ mipverbose(("Added route\n"));
+ else
+ mipverbose(("Deleted route\n"));
+ }
+ /* Free rt_msg now */
+ free((void *)rt_msg);
+
+ if (rlen < 0)
+ return ((-1) * (errno));
+
+ return (0);
+} /* routemodify */
+
+/* Routines for refresh_mn_arp() */
+/* -------- Start of dlcommon routines */
+/*
+ * Common (shared) DLPI test routines.
+ * Mostly pretty boring boilerplate sorta stuff.
+ * These can be split into individual library routines later
+ * but it's just convenient to keep them in a single file
+ * while they're being developed.
+ *
+ * Not supported:
+ * Connection Oriented stuff
+ * QOS stuff
+ */
+
+
+/*
+ * Function: dlinforeq
+ *
+ * Arguments: fd
+ *
+ * Description:
+ *
+ * Returns: int
+ */
+static int
+dlinforeq(int fd)
+{
+ dl_info_req_t info_req;
+ struct strbuf ctl;
+
+ info_req.dl_primitive = DL_INFO_REQ;
+
+ ctl.maxlen = 0;
+ ctl.len = sizeof (info_req);
+ ctl.buf = (char *)&info_req;
+
+ if (putmsg(fd, &ctl, (struct strbuf *)NULL, RS_HIPRI) < 0) {
+ return (-1 * errno);
+ }
+ return (0);
+} /* dlinforeq */
+
+
+/*
+ * Function: dlinfoack
+ *
+ * Arguments: fd, bufp
+ *
+ * Description:
+ *
+ * Returns: int
+ */
+static int
+dlinfoack(int fd, char *bufp)
+{
+ union DL_primitives *dlp;
+ struct strbuf ctl;
+ int flags;
+ int ret;
+
+ ctl.maxlen = MAXDLBUF;
+ ctl.len = 0;
+ ctl.buf = bufp;
+
+ (void) strgetmsg(fd, &ctl, (struct strbuf *)NULL, &flags);
+
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ dlp = (union DL_primitives *)ctl.buf;
+
+ if ((ret = expecting(DL_INFO_ACK, dlp)) < 0) {
+ (void) close(fd);
+ return (ret);
+ }
+
+ if (ctl.len < sizeof (dl_info_ack_t)) {
+ return (-1 * DLOKACK_SHORT_RESPONSE);
+ }
+
+ if (flags != RS_HIPRI) {
+ return (-1 * DLOKACK_NOT_M_PCPROTO);
+ }
+
+ if (ctl.len < sizeof (dl_info_ack_t)) {
+ return (-1 * DLOKACK_SHORT_RESPONSE);
+ }
+
+ return (0);
+} /* dlinfoack */
+
+
+/*
+ * Function: dlattachreq
+ *
+ * Arguments: fd, ppa
+ *
+ * Description:
+ *
+ * Returns: int
+ */
+int
+dlattachreq(int fd, int ppa)
+{
+ dl_attach_req_t attach_req;
+ struct strbuf ctl;
+
+ attach_req.dl_primitive = DL_ATTACH_REQ;
+ attach_req.dl_ppa = ppa;
+
+ ctl.maxlen = 0;
+ ctl.len = sizeof (attach_req);
+ ctl.buf = (char *)&attach_req;
+
+ if (putmsg(fd, &ctl, (struct strbuf *)NULL, 0) < 0)
+ return (-1 * errno);
+
+ return (0);
+} /* dlattachreq */
+
+
+/*
+ * Function: dlbindreq
+ *
+ * Arguments: fd, sap, max_conind, service_mode, conn_mgmt, xidtest
+ *
+ * Description:
+ *
+ * Returns: int
+ */
+int
+dlbindreq(int fd, uint32_t sap, uint32_t max_conind, uint32_t service_mode,
+ uint32_t conn_mgmt, uint32_t xidtest)
+{
+ dl_bind_req_t bind_req;
+ struct strbuf ctl;
+
+ bind_req.dl_primitive = DL_BIND_REQ;
+ bind_req.dl_sap = sap;
+ bind_req.dl_max_conind = max_conind;
+ bind_req.dl_service_mode = service_mode;
+ bind_req.dl_conn_mgmt = conn_mgmt;
+ bind_req.dl_xidtest_flg = xidtest;
+
+ ctl.maxlen = 0;
+ ctl.len = sizeof (bind_req);
+ ctl.buf = (char *)&bind_req;
+
+ if (putmsg(fd, &ctl, (struct strbuf *)NULL, 0) < 0)
+ return (-1 * errno);
+
+ return (0);
+} /* dlbindreq */
+
+/*
+ * Function: dlunitdatareq
+ *
+ * Arguments: int fd, unsigned char *addrp, int addrlen, ulong_t minpri
+ * ulong_t maxpri, unsigned char *datap, int datalen)
+ *
+ * Description:
+ *
+ * Returns: int
+ */
+static int
+dlunitdatareq(int fd, unsigned char *addrp, int addrlen, ulong_t minpri,
+ ulong_t maxpri, unsigned char *datap, int datalen)
+{
+ long buf[MAXDLBUF];
+ union DL_primitives *dlp;
+ struct strbuf data, ctl;
+
+ dlp = (union DL_primitives *)buf;
+
+ dlp->unitdata_req.dl_primitive = DL_UNITDATA_REQ;
+ dlp->unitdata_req.dl_dest_addr_length = addrlen;
+ dlp->unitdata_req.dl_dest_addr_offset = sizeof (dl_unitdata_req_t);
+ dlp->unitdata_req.dl_priority.dl_min = minpri;
+ dlp->unitdata_req.dl_priority.dl_max = maxpri;
+
+ (void) memcpy(OFFADDR(dlp, sizeof (dl_unitdata_req_t)), addrp,
+ addrlen);
+
+ ctl.maxlen = 0;
+ ctl.len = sizeof (dl_unitdata_req_t) + addrlen;
+ ctl.buf = (char *)buf;
+
+ data.maxlen = 0;
+ data.len = datalen;
+ data.buf = (char *)datap;
+
+ if (putmsg(fd, &ctl, &data, 0) < 0)
+ return (-1 * errno);
+
+ return (0);
+} /* dlunitdatareq */
+
+
+/*
+ * Function: dlokack
+ *
+ * Arguments: int fd, char *bufp
+ *
+ * Description:
+ *
+ * Returns: int
+ */
+int
+dlokack(int fd, char *bufp)
+{
+ union DL_primitives *dlp;
+ struct strbuf ctl;
+ int flags;
+ int ret;
+
+ ctl.maxlen = MAXDLBUF;
+ ctl.len = 0;
+ ctl.buf = bufp;
+
+ (void) strgetmsg(fd, &ctl, (struct strbuf *)NULL, &flags);
+
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ dlp = (union DL_primitives *)ctl.buf;
+
+ if ((ret = expecting(DL_OK_ACK, dlp)) < 0)
+ return (ret);
+
+ if (ctl.len < sizeof (dl_ok_ack_t))
+ return (-1 * DLOKACK_SHORT_RESPONSE);
+
+ if (flags != RS_HIPRI)
+ return (-1 * DLOKACK_NOT_M_PCPROTO);
+
+ if (ctl.len < sizeof (dl_ok_ack_t))
+ return (-1 * DLOKACK_SHORT_RESPONSE);
+
+ return (0);
+} /* dlokack */
+
+
+/*
+ * Function: dlbindack
+ *
+ * Arguments: int fd, char *bufp
+ *
+ * Description:
+ *
+ * Returns: int
+ */
+int
+dlbindack(int fd, char *bufp)
+{
+ union DL_primitives *dlp;
+ struct strbuf ctl;
+ int flags;
+ int ret;
+
+ ctl.maxlen = MAXDLBUF;
+ ctl.len = 0;
+ ctl.buf = bufp;
+
+ (void) strgetmsg(fd, &ctl, (struct strbuf *)NULL, &flags);
+
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ dlp = (union DL_primitives *)ctl.buf;
+
+ if ((ret = expecting(DL_BIND_ACK, dlp)) < 0)
+ return (ret);
+
+ if (flags != RS_HIPRI)
+ return (-1 * DLOKACK_NOT_M_PCPROTO);
+
+ if (ctl.len < sizeof (dl_bind_ack_t))
+ return (-1 * DLOKACK_SHORT_RESPONSE);
+
+ return (0);
+} /* dlbindack */
+
+
+/*
+ * Function: strgetmsg
+ *
+ * Arguments: int fd, struct strbuf *ctlp, struct strbuf *datap, int *flagsp,
+ * char *caller
+ *
+ * Description:
+ *
+ * Returns: int
+ */
+static int
+strgetmsg(int fd, struct strbuf *ctlp, struct strbuf *datap, int *flagsp)
+{
+ int rc;
+
+ /*
+ * Set flags argument and issue getmsg().
+ */
+ *flagsp = 0;
+ if ((rc = getmsg(fd, ctlp, datap, flagsp)) < 0) {
+ return (-1 * errno);
+ }
+
+ /*
+ * Check for MOREDATA and/or MORECTL.
+ */
+ if ((rc & (MORECTL | MOREDATA)) == (MORECTL | MOREDATA))
+ return (-1 * ERR_MORECTLDATA);
+
+ if (rc & MORECTL)
+ return (-1 * ERR_MORECTL);
+
+ if (rc & MOREDATA)
+ return (-1 * ERR_MOREDATA);
+
+ /*
+ * Check for at least sizeof (long) control data portion.
+ */
+ if (ctlp->len < sizeof (long))
+ return (-1 * SHORT_CONTROL_PORTION);
+
+ return (0);
+} /* strgetmsg */
+
+
+/*
+ * Function: expecting
+ *
+ * Arguments: int prim, union DL_primitives *dlp
+ *
+ * Description:
+ *
+ * Returns: int (zero on success)
+ */
+static int
+expecting(int prim, union DL_primitives *dlp)
+{
+ if (dlp->dl_primitive != (ulong_t)prim)
+ return (-1 * DL_PRIMITIVE_ERROR);
+
+ return (0);
+} /* expecting */
+
+
+/*
+ * Function: MAaddrtostring
+ *
+ * Arguments: unsigned char *addr, ulong_t length, unsigned char *s
+ *
+ * Description: return hardware address as string.
+ *
+ * Returns: void
+ */
+static void
+MAaddrtostring(unsigned char *addr, ulong_t length, unsigned char *s)
+{
+ int i;
+
+ for (i = 0; i < length; i++) {
+ (void) sprintf((char *)s, "%x:", addr[i] & 0xff);
+ s = s + strlen((char *)s);
+ }
+ if (length)
+ *(--s) = '\0';
+} /* MAaddrtostring */
+
+#if 0
+/*
+ * Function: stringtoaddr
+ *
+ * Arguments: char *sp, char *addr
+ *
+ * Description: This function converts the string to an address.
+ *
+ * Returns: int (length of address)
+ */
+int
+stringtoaddr(char *sp, char *addr)
+{
+ int n = 0;
+ char *p;
+ unsigned int val;
+
+ p = sp;
+ while (p = strtok(p, ":")) {
+ if (sscanf(p, "%x", &val) != 1)
+ return (-1 * INVALID_STRING);
+ if (val > 0xff)
+ return (-1 * INVALID_STRING);
+ *addr++ = val;
+ n++;
+ p = NULL;
+ }
+
+ return (n);
+} /* stringtoaddr */
+
+#endif
+/* -------- End of dlcommon routines */
+
+/*
+ * The parms are as follows:
+ * device String definition of the device (e.g. "/dev/le").
+ * ppa Int for the instance of the device (e.g. 3, for "le3").
+ * phys Byte String for the dst MAC address of this packet.
+ * This is just the bcast address("ff:ff:ff:ff:ff:ff")
+ * physlen Length (int) of the mac address (not the string). E.g:
+ * 6 for enet.
+ * ipaddr long for the ip address of the host we are advertising
+ * our mac address for.
+ * ether_addr Ether address we want to set for the ipaddress we are
+ * impersonating
+ * NOTE: The mac addr of this system is not passed in, it is obtained
+ * directly from the dlpi info ack structure.
+ * Steps executed :-
+ * -----------------
+ * Open datalink provider.
+ * Attach to PPA.
+ * Bind to sap
+ * Send arp request as DL_UNITDATA_REQ msg
+ *
+ */
+static int send_gratuitous_arp(char *device, int ppa, unsigned char *phys,
+ int physlen, ipaddr_t ipaddr, unsigned char *ether_addr)
+{
+ int saplen;
+ int size = sizeof (struct ether_arp);
+ int sapval = ETHERTYPE_ARP;
+ int localsap = ETHERTYPE_ARP;
+ int fd;
+ char buf[MAXDLBUF];
+ unsigned char sap[MAXDLADDR];
+ unsigned char addr[MAXDLADDR];
+ int addrlen;
+ struct ether_arp req;
+ union DL_primitives *dlp;
+ int i, ret;
+ ipaddr_t target_ipaddr = ipaddr;
+
+
+ /* initialize buf[] */
+ for (i = 0; i < MAXDLBUF; i++)
+ buf[i] = (unsigned char) i & 0xff;
+
+ /* Open the device. */
+ if ((fd = open(device, 2)) < 0)
+ return (-1 * errno);
+
+ /* Attach. */
+ if ((ret = dlattachreq(fd, ppa)) < 0) {
+ (void) close(fd);
+ return (ret);
+ }
+
+ if ((ret = dlokack(fd, buf)) < 0) {
+ (void) close(fd);
+ return (ret);
+ }
+
+ /* Bind. */
+ if ((ret = dlbindreq(fd, localsap, 0, DL_CLDLS, 0, 0)) < 0) {
+ (void) close(fd);
+ return (ret);
+ }
+
+ if ((ret = dlbindack(fd, buf)) < 0) {
+ (void) close(fd);
+ return (ret);
+ }
+
+ /* Get info */
+ if ((ret = dlinforeq(fd)) < 0) {
+ (void) close(fd);
+ return (ret);
+ }
+
+ if ((ret = dlinfoack(fd, buf)) < 0) {
+ (void) close(fd);
+ return (ret);
+ }
+
+ /*
+ * Verify sap and phys address lengths.
+ */
+
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ dlp = (union DL_primitives *)buf;
+
+ MAaddrtostring(OFFADDR(dlp, dlp->info_ack.dl_addr_offset),
+ dlp->info_ack.dl_addr_length, addr);
+
+ saplen = ABS(dlp->info_ack.dl_sap_length);
+
+ /*
+ * Convert destination address string to address.
+ */
+ for (i = 0; i < saplen; ++i) {
+ int rev_index = saplen - 1 -i;
+ sap[i] = (sapval >> (rev_index * BITSPERBYTE)) & 0xff;
+ }
+
+ /*
+ * printdlprim(dlp);
+ */
+ if (physlen != (dlp->info_ack.dl_addr_length - saplen)) {
+ (void) close(fd);
+ return (-1 * INVALID_ADDR);
+ }
+
+ addrlen = saplen + physlen;
+
+ /*
+ * Construct destination address.
+ */
+ if (dlp->info_ack.dl_sap_length > 0) { /* order is sap+phys */
+ (void) memcpy(addr, sap, saplen);
+ (void) memcpy(addr + saplen, phys, physlen);
+
+ /* obtain our MAC address */
+ /*
+ * (void) memcpy((char *)my_etheraddr,
+ * (char *)OFFADDR(dlp,
+ * dlp->info_ack.dl_addr_offset + saplen),
+ * physlen);
+ */
+ } else { /* order is phys+sap */
+ (void) memcpy(addr, phys, physlen);
+ (void) memcpy(addr + physlen, sap, saplen);
+
+ /* Obtain our MAC address */
+ /*
+ * (void) memcpy((char *)my_etheraddr,
+ * (char *)OFFADDR(dlp, dlp->info_ack.dl_addr_offset), physlen);
+ */
+ }
+
+ /* create arp request */
+ (void) memset(&req, 0, sizeof (req));
+ req.arp_hrd = htons(ARPHRD_ETHER);
+ req.arp_pro = htons(ETHERTYPE_IP);
+ req.arp_hln = ETHERADDRL;
+ req.arp_pln = IPADDRL;
+ req.arp_op = htons(ARPOP_REQUEST);
+ (void) memcpy(&req.arp_sha, ether_addr, ETHERADDRL);
+ (void) memcpy(&req.arp_spa, &ipaddr, IPADDRL);
+ (void) memcpy(&req.arp_tpa, &target_ipaddr, IPADDRL);
+
+ /* Transmit it. */
+
+ if ((ret =
+ dlunitdatareq(fd, addr, addrlen, 0, 0, (unsigned char *)&req,
+ size)) < 0) {
+ (void) close(fd);
+ return (ret);
+ }
+
+ (void) close(fd);
+ return (0);
+} /* send_gratuitous_arp() */
+
+
+/*
+ * SYNOPSIS:
+ * garp interface ipaddr ether_addr
+ *
+ * RETURN VALUE :
+ * Returns 0 on success else -(error code)
+ * "interface" is the interface to send the grarp out on.
+ * "interface" is expressed in ifname convention(e.g. "le0", "bf2"),
+ * and it will be converted by ifname2device_ppa() into the dlpi convention
+ * (e.g. "/dev/le" + "0" and "/dev/bf" + "2").
+ * "ipaddr" is the ipaddr of the system we're "impersonating"
+ * ether_addr is the ethernet address to which we want to be impersonated.
+ * Sends a gratuitous arp to hw addr ff:ff:ff:ff:ff:ff.
+ *
+ * The arp packet fields are filled in as follows:
+ * (To be in conformance gratuitous arp described in RFC2002)
+ * In the gratuitous arp packet for Mobile - IP :
+ * Ethernet header :
+ * Source address : Its own hardware address
+ * Destination address : ff:ff:ff:ff:ff:ff(Broadcast address)
+ * Frame Type : 0x0806 Arp Request/Reply
+ *
+ * Arp Packet :
+ * Format of hardware address : ARPHRD_ETHER 1
+ * Format of protocol address : 0x0800
+ * Length of hardware address : ETH_ALEN 6
+ * Length of protocol address : 4
+ * ARP opcode : ARPOP_REQUEST 1
+ * Sender hardware address : Ethernet address to which to be updated.
+ * Destination harware address : XXXX Don't Care
+ * Sender IP address : IP Address(Cache entry to be updated)
+ * Target IP address : Sender IP Address.
+ * For an ARP Request Packet :
+ *
+ * Note : For ARP Reply packet target IP address should be set same
+ * as source IP address
+ *
+ * Modified from gabriels's arp module which was
+ * blatantly stolen from dlunitdatareq.c by Neal Nuckolls. Just added
+ * stuff to compose an arp packet.
+ */
+int
+garp(char *dev, ipaddr_t mnaddr, unsigned char *mnetheraddr)
+{
+ int ppa;
+ unsigned char phys[MAXDLADDR];
+ char device[MAX_INPUT];
+
+ ifname2devppa(dev, device, &ppa);
+ (void) memset(phys, 0xff, ETH_ALEN);
+
+ /* Validate arguments. */
+ if (ppa < 0)
+ return (-1 * INVALID_PPA);
+
+ return (send_gratuitous_arp(device, ppa, phys, ETH_ALEN,
+ mnaddr, mnetheraddr));
+} /* garp */
+
+
+/*
+ * Function: OScleanup
+ *
+ * Arguments: none
+ *
+ * Description: Close filedescriptors for shutdown.
+ * Also cleans up the dynamic interface and static
+ * interface list entries.
+ *
+ * Returns: void
+ */
+void
+OScleanup()
+{
+ (void) close(ioctl_sockid);
+ /* Cleanup static existing interface table */
+ if (dynamicIfaceHead != NULL) {
+ struct dynamicIfacetype *dp;
+ struct dynamicIfacetype *savep;
+
+ dp = dynamicIfaceHead;
+ while (dp != NULL) {
+ savep = dp->next;
+ dp->next = NULL;
+ free(dp);
+ dp = savep;
+ }
+ }
+ if (StaticIntfaceHead != NULL) {
+ struct staticIface *sp;
+ struct staticIface *save_sp;
+
+ sp = StaticIntfaceHead;
+ while (sp != NULL) {
+ save_sp = sp->next;
+ sp->next = NULL;
+ free(sp);
+ sp = save_sp;
+ }
+ }
+ dynamicIfaceHead = NULL;
+ StaticIntfaceHead = NULL;
+ ioctl_sockid = -1;
+} /* OScleanup */
+
+/*
+ * Function: plumb_one_tun
+ *
+ * Arguments: int tnum
+ *
+ * Description: Plumb the tunnel interface by opening the
+ * associated devices and pushing the required modules eg.
+ * 'tunl' module and others and then create persistent links.
+ *
+ * Returns: -1 on error and mux_fd is returned upon success.
+ * mux_fd will be used by unplumb_one_tun to destroy
+ * the tunnel.
+ */
+static int
+plumb_one_tun(int tnum)
+{
+ struct lifreq lifr;
+ int ip_fd, mux_fd, ip_muxid;
+ char name[LIFNAMSIZ];
+
+ mipverbose(("plumb_one_tun: tunnel number %d\n", tnum));
+
+ if ((ip_fd = open("/dev/ip", O_RDWR)) < 0) {
+ syslog(LOG_ERR, "open of /dev/ip failed");
+ return (-1);
+ }
+
+ if (ioctl(ip_fd, I_PUSH, "tun") < 0) {
+ syslog(LOG_ERR, "I_PUSH of tun failed");
+ (void) close(ip_fd);
+ return (-1);
+ }
+
+ if ((mux_fd = open("/dev/udp", O_RDWR)) < 0) {
+ syslog(LOG_ERR, "open of /dev/ip failed");
+ (void) close(ip_fd);
+ return (-1);
+ }
+
+ if (ioctl(ip_fd, I_PUSH, "ip") == -1) {
+ syslog(LOG_ERR, "I_PUSH of ip failed");
+ (void) close(ip_fd);
+ (void) close(mux_fd);
+ return (-1);
+ }
+
+ /* Get the existing flags for this stream */
+ (void) memset(&lifr, 0, sizeof (lifr));
+ lifr.lifr_name[0] = '\0';
+ if (ioctl(ip_fd, SIOCGLIFFLAGS, (char *)&lifr) == -1) {
+ syslog(LOG_ERR, "plumb_one_tun: SIOCGLIFFLAGS");
+ (void) close(ip_fd);
+ (void) close(mux_fd);
+ return (-1);
+ }
+
+ lifr.lifr_ppa = tnum;
+ (void) sprintf(name, "ip.tun%d", tnum);
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+ if (ioctl(ip_fd, SIOCSLIFNAME, (char *)&lifr) == -1) {
+ (void) close(ip_fd);
+ (void) close(mux_fd);
+ return (-1);
+ }
+
+ if ((ip_muxid = ioctl(mux_fd, I_LINK, ip_fd)) == -1) {
+ syslog(LOG_ERR, "I_LINK for ip failed");
+ (void) close(ip_fd);
+ (void) close(mux_fd);
+ return (-1);
+ }
+
+ lifr.lifr_ip_muxid = ip_muxid;
+
+ /*
+ * Tell IP the muxids of the LINKed interface
+ * streams so that they can be closed down at a later
+ * time by an unplumb operation.
+ */
+ if (ioctl(mux_fd, SIOCSLIFMUXID, (caddr_t)&lifr) < 0) {
+ syslog(LOG_ERR, "plumb_one_tun: SIOCSLIFMUXID failed");
+ (void) close(ip_fd);
+ (void) close(mux_fd);
+ return (-1);
+ }
+
+ (void) close(ip_fd);
+
+ return (mux_fd);
+} /* plumb_one_tun */
+
+
+/*
+ * Function: unplumb_one_tun
+ *
+ * Arguments: int mux_fd
+ *
+ * Description: Unplumb the tunnel interface. Destroy all streams
+ * associated with this tunnel and close it.
+ *
+ * Returns: int
+ */
+static int
+unplumb_one_tun(int mux_fd)
+{
+ int retval;
+
+ retval = close(mux_fd);
+ return (retval);
+}
+
+
+/*
+ * Function: setifflags
+ *
+ * Arguments: int tnum, int value
+ *
+ * Description: Set the interface specific flags indicated in
+ * the argument 'value' for the given tunnel interface whose
+ * tunnel number is the argument 'tnum'.
+ *
+ * Returns: int
+ */
+static int
+setifflags(int tnum, int value)
+{
+
+
+ struct lifreq lifr;
+ char name[LIFNAMSIZ];
+
+ (void) sprintf(name, "ip.tun%d", tnum);
+ mipverbose(("setifflags %s\n", name));
+
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+ if (ioctl(ioctl_sockid, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0) {
+ syslog(LOG_ERR, "setifflags: SIOCGLIFFLAGS failed");
+ return (-1);
+ }
+ if (value < 0) {
+ value = -value;
+ lifr.lifr_flags &= ~value;
+ } else
+ lifr.lifr_flags |= value;
+
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+ if (ioctl(ioctl_sockid, SIOCSLIFFLAGS, (caddr_t)&lifr) < 0) {
+ syslog(LOG_ERR, "setifflags: SIOCSLIFFLAGS failed");
+ return (-1);
+ }
+ return (0);
+}
+
+
+/*
+ * Function: settaddr
+ *
+ * Arguments: int tnum, ipaddr_t ifaddr1, ipaddr_t ifaddr2,
+ * ipaddr_t saddr, ipaddr_t daddr, struct ipsec_req_t *ipsr
+ *
+ * Description: First forms a point-to-point interface between
+ * ifaddr1 and ifaddr2 addresses. Next the source address of
+ * the tunnel is set to address 'saddr'. This is the source
+ * address of an outer encapsulating IP header and it must be
+ * the address of an interface that has already been configured.
+ * The destination address of the tunnel is set to 'daddr'.
+ *
+ * Returns: static int
+ */
+static int
+settaddr(int tnum, ipaddr_t ifaddr1, ipaddr_t ifaddr2,
+ ipaddr_t saddr, ipaddr_t daddr, ipsec_req_t *ipsr_p)
+{
+
+ struct sockaddr_storage laddr1, laddr2;
+ struct iftun_req treq;
+ char name[LIFNAMSIZ];
+ struct sockaddr_in *sin;
+
+ (void) sprintf(name, "ip.tun%d", tnum);
+ mipverbose(("settaddr %s\n", name));
+
+
+ if (mkpt2pt(name, ifaddr1, ifaddr2) < 0) {
+ syslog(LOG_ERR, "settaddr: mkpt2pt failed");
+ return (-1);
+ }
+
+ bzero(&treq, sizeof (struct iftun_req));
+ treq.ifta_vers = IFTUN_VERSION;
+ (void) strncpy(treq.ifta_lifr_name, name,
+ sizeof (treq.ifta_lifr_name));
+ if (ioctl(ioctl_sockid, SIOCGTUNPARAM, (caddr_t)&treq) < 0) {
+ syslog(LOG_ERR, "Not a tunnel");
+ return (-1);
+ }
+
+ if (treq.ifta_lower != IFTAP_IPV4) {
+ syslog(LOG_ERR, "Unknown lower tunnel");
+ return (-1);
+ }
+
+ sin = (struct sockaddr_in *)&laddr1;
+ (void) memset(&laddr1, 0, sizeof (laddr1));
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = saddr;
+
+ sin = (struct sockaddr_in *)&laddr2;
+ (void) memset(&laddr2, 0, sizeof (laddr2));
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = daddr;
+
+ treq.ifta_saddr = laddr1;
+ treq.ifta_daddr = laddr2;
+ treq.ifta_flags = IFTUN_SRC|IFTUN_DST;
+ (void) strncpy(treq.ifta_lifr_name, name,
+ sizeof (treq.ifta_lifr_name));
+ treq.ifta_vers = IFTUN_VERSION;
+
+ /* non-null means there's tunnel protection to be added! */
+ if (ipsr_p != NULL) {
+ /* finally set the ipsec protection bits! */
+ (void) memcpy(&treq.ifta_secinfo, ipsr_p, sizeof (ipsec_req_t));
+
+ /* set the flag so the kernel sets up the security! */
+ treq.ifta_flags |= IFTUN_SECURITY;
+ }
+
+ if (ioctl(ioctl_sockid, SIOCSTUNPARAM, (caddr_t)&treq) < 0) {
+ syslog(LOG_ERR, "set tunnel addr failed");
+ return (-1);
+ }
+
+ return (0);
+} /* settaddr */
+
+/*
+ * Function: getEthernetAddr
+ *
+ * Arguments: char *ename, unsigned char *eaddr
+ *
+ * Description: Get the hardware address for specified interface name.
+ *
+ * Returns: int
+ */
+int
+getEthernetAddr(char *ename, unsigned char *eaddr)
+{
+ int saplen;
+ int localsap = ETHERTYPE_ARP;
+ int fd;
+ char buf[MAXDLBUF];
+ unsigned char addr[MAXDLADDR];
+ union DL_primitives *dlp;
+ int i, ret;
+ int physlen = ETHERADDRL;
+ int ppa;
+ char device[LIFNAMSIZ + 5];
+ char *lasts;
+
+ /*
+ * If this is a virtual interface, remove the ':' character (and
+ * everything following that character. We do not really care
+ * about it since the MAC address is the same as the physical
+ * interface.
+ */
+ ename = strtok_r(ename, ":", &lasts);
+ if (ename == NULL) {
+ return (-1);
+ }
+
+ ifname2devppa(ename, device, &ppa);
+
+ /* initialize buf[] */
+ for (i = 0; i < MAXDLBUF; i++)
+ buf[i] = (unsigned char) i & 0xff;
+
+ /* Open the device. */
+ if ((fd = open(device, (O_RDWR | O_NDELAY))) < 0)
+ return (-1 * errno);
+
+ /* Attach. */
+ if ((ret = dlattachreq(fd, ppa)) < 0) {
+ (void) close(fd);
+ return (ret);
+ }
+
+ if ((ret = dlokack(fd, buf)) < 0) {
+ (void) close(fd);
+ return (ret);
+ }
+
+ /* Bind. */
+ if ((ret = dlbindreq(fd, localsap, 0, DL_CLDLS, 0, 0)) < 0) {
+ (void) close(fd);
+ return (ret);
+ }
+
+ if ((ret = dlbindack(fd, buf)) < 0) {
+ (void) close(fd);
+ return (ret);
+ }
+
+ /* Get info */
+ if ((ret = dlinforeq(fd)) < 0) {
+ (void) close(fd);
+ return (ret);
+ }
+
+ /* WORK -- check this error message, and send to mohanp@eng */
+ (void) sleep(1);
+
+ if ((ret = dlinfoack(fd, buf)) < 0) {
+ (void) close(fd);
+ return (ret);
+ }
+
+ /* Verify sap and phys address lengths */
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ dlp = (union DL_primitives *)buf;
+
+ MAaddrtostring(OFFADDR(dlp, dlp->info_ack.dl_addr_offset),
+ dlp->info_ack.dl_addr_length, addr);
+
+ saplen = ABS(dlp->info_ack.dl_sap_length);
+
+ /* Construct destination address. */
+ if (dlp->info_ack.dl_sap_length > 0) { /* order is sap+phys */
+ /* obtain our MAC address */
+ (void) memcpy(eaddr, OFFADDR(dlp,
+ dlp->info_ack.dl_addr_offset + saplen),
+ physlen);
+ } else { /* order is phys+sap */
+ /* Obtain our MAC address */
+ (void) memcpy(eaddr, OFFADDR(dlp,
+ dlp->info_ack.dl_addr_offset),
+ physlen);
+ }
+
+ (void) close(fd);
+ return (0);
+} /* getEthernetAddr */
+
+
+/*
+ * Function: getIfaceInfo
+ *
+ * Arguments: char *ifaceName, ipaddr_t *addr, ipaddr_t *mask, uint64_t *flags
+ * uint32_t *ifindex
+ *
+ * Description: Gets the interface information given the name.
+ *
+ * Returns: int
+ */
+int
+getIfaceInfo(char *ifaceName, ipaddr_t *addr, ipaddr_t *mask,
+ uint64_t *flags, uint32_t *ifindex)
+{
+ struct lifreq lifr;
+ struct sockaddr_in *sin;
+ int ioc_sockid;
+
+ bzero((char *)&lifr, sizeof (lifr));
+ (void) strncpy(lifr.lifr_name, ifaceName, sizeof (lifr.lifr_name));
+
+ ioc_sockid = socket(AF_INET, SOCK_DGRAM, 0);
+ if (ioc_sockid < 0) {
+ syslog(LOG_ERR,
+ "Could not open socket for ioctls in getIfaceInfo()");
+ return (-1);
+ }
+
+ if (ioctl(ioc_sockid, SIOCGLIFADDR, (caddr_t)&lifr) < 0) {
+ syslog(LOG_ERR, "Could not read IP address for %s", ifaceName);
+ (void) close(ioc_sockid);
+ return (-1);
+ }
+
+ sin = (struct sockaddr_in *)&lifr.lifr_addr;
+ *addr = sin->sin_addr.s_addr;
+
+ if (ioctl(ioc_sockid, SIOCGLIFNETMASK, (caddr_t)&lifr) < 0) {
+ syslog(LOG_ERR, "Could not read netmask for %s", ifaceName);
+ (void) close(ioc_sockid);
+ return (-1);
+ }
+
+ sin = (struct sockaddr_in *)&lifr.lifr_addr;
+ *mask = sin->sin_addr.s_addr;
+
+ (void) strncpy(lifr.lifr_name, ifaceName, sizeof (lifr.lifr_name));
+ if (ioctl(ioc_sockid, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0) {
+ syslog(LOG_ERR, "Could not read flags for %s", ifaceName);
+ (void) close(ioc_sockid);
+ return (-1);
+ }
+
+ *flags = lifr.lifr_flags;
+
+ if (ioctl(ioc_sockid, SIOCGLIFINDEX, (char *)&lifr) < 0) {
+ syslog(LOG_ERR, "Can't read IFINDEX %s", ifaceName);
+ (void) close(ioc_sockid);
+ return (-1);
+ }
+ *ifindex = lifr.lifr_index;
+
+ (void) close(ioc_sockid);
+
+ return (0);
+} /* getIfaceInfo */
+
+
+/*
+ * Function: arpIfadd
+ *
+ * Arguments: vaddr - visitor MN's IP address
+ * inIfindex - inbound interface index
+ * slla - MN's source link layer addr
+ *
+ * Description: Add an ARP entry given the interface index. Invokes
+ * SIOCSXARP with sdl_data array filled with interface
+ * name (without null terminator) followed by address.
+ * This code assumes it is being invoked on Ethernet.
+ *
+ * Returns: 0 - sucesss; errno - failure
+ */
+int
+arpIfadd(ipaddr_t vaddr, char *slla, uint32_t inIfindex)
+{
+ struct xarpreq ar;
+ char *etheraddr;
+ int val;
+ char addrstr1[INET_ADDRSTRLEN];
+ struct ether_addr ether;
+
+ (void) memset(&ar, 0, sizeof (struct xarpreq));
+ if (if_indextoname(inIfindex, ar.xarp_ha.sdl_data) == NULL) {
+ syslog(LOG_ERR, "if_indextoname returned NULL\n");
+ return ((-1) * errno);
+ }
+
+ /*
+ * Mark this entry permanent to prevent ARP from blowing this
+ * away.
+ */
+ ar.xarp_flags = ATF_PERM;
+ ((struct sockaddr_in *)&ar.xarp_pa)->sin_addr.s_addr = vaddr;
+ ((struct sockaddr_in *)&ar.xarp_pa)->sin_family = AF_INET;
+
+ (void) memcpy(ether.ether_addr_octet, slla, ETHERADDRL);
+ etheraddr = ether_ntoa(&ether);
+ mipverbose(("Adding temporary ARP entry for visitor %s,"
+ " hardware address %s on interface %s [index %d]\n",
+ ntoa(vaddr, addrstr1), etheraddr, ar.xarp_ha.sdl_data, inIfindex));
+ ar.xarp_ha.sdl_nlen = strlen(ar.xarp_ha.sdl_data);
+ ar.xarp_ha.sdl_alen = ETHERADDRL;
+ ar.xarp_ha.sdl_family = AF_LINK;
+ (void) memcpy(LLADDR(&ar.xarp_ha), slla, ar.xarp_ha.sdl_alen);
+
+ val = ioctl(ioctl_sockid, SIOCSXARP, (caddr_t)&ar);
+
+ if (val < 0)
+ return ((-1) * errno);
+ else
+ return (val);
+}
+
+/*
+ * Function: arpIfdel
+ *
+ * Arguments: vaddr - visitor MN's IP address
+ * slla - MN's source link layer addr (used here for mipverbose)
+ * inIfindex - inbound interface index
+ *
+ * Description: Delete an ARP entry based on the interface index. Invokes
+ * SIOCDXARP with sdl_data array filled with interface
+ * name. This code assumes it is being invoked on Ethernet.
+ *
+ * Returns: 0 - sucesss; errno - failure
+ */
+int
+arpIfdel(ipaddr_t vaddr, char *slla, uint32_t inIfindex)
+{
+ struct xarpreq ar;
+ int val;
+ char *etheraddr;
+ char addrstr1[INET_ADDRSTRLEN];
+ struct ether_addr ether;
+
+ (void) memset(&ar, 0, sizeof (struct xarpreq));
+ if (if_indextoname(inIfindex, ar.xarp_ha.sdl_data) == NULL) {
+ syslog(LOG_ERR, "if_indextoname returned NULL\n");
+ return ((-1) * errno);
+ }
+
+ ar.xarp_flags = ATF_PERM;
+ ((struct sockaddr_in *)&ar.xarp_pa)->sin_addr.s_addr = vaddr;
+ ((struct sockaddr_in *)&ar.xarp_pa)->sin_family = AF_INET;
+
+ (void) memcpy(ether.ether_addr_octet, slla, ETHERADDRL);
+ etheraddr = ether_ntoa(&ether);
+ mipverbose(("Deleting temporary ARP entry for visitor %s,"
+ " hardware address %s on interface %s [index %d]\n",
+ ntoa(vaddr, addrstr1), etheraddr, ar.xarp_ha.sdl_data, inIfindex));
+ ar.xarp_ha.sdl_nlen = strlen(ar.xarp_ha.sdl_data);
+ ar.xarp_ha.sdl_family = AF_LINK;
+
+ /*
+ * Delete an ARP entry in the ARP cache
+ */
+ val = ioctl(ioctl_sockid, SIOCDXARP, (caddr_t)&ar);
+
+ if (val < 0)
+ return ((-1) * errno);
+ else
+ return (0);
+}
+
+/*
+ * Function: CreateListOfExistingIntfce
+ * This function stores a list of existing interfaces into a
+ * static interface entry table when the mipagent is started.
+ * The existing interface list does not include any IPv6,
+ * loopback and logical interfaces.
+ * Return value : 0 on success, -1 on failure
+ */
+int
+CreateListOfExistingIntfce(void) {
+ struct lifnum lifn;
+ struct lifconf lifc;
+ struct lifreq lifr;
+ struct lifreq *lifrp;
+ int numifs;
+ int bufsize;
+ int iocsock;
+ int n;
+ char *buf;
+ StaticIfaceEntry *ifce_ptr;
+ StaticIfaceEntry *saveptr = NULL;
+
+ iocsock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (iocsock < 0) {
+ syslog(LOG_ERR, "Can't open IOCTL socket: %m");
+ return (-1);
+ }
+
+ lifn.lifn_family = AF_INET;
+ lifn.lifn_flags = 0;
+ if (ioctl(iocsock, SIOCGLIFNUM, (char *)&lifn) < 0) {
+ syslog(LOG_ERR, "SIOCGLIFNUM failed: %m");
+ return (-1);
+ }
+ numifs = lifn.lifn_count;
+ bufsize = numifs * sizeof (struct lifreq);
+ buf = malloc(bufsize);
+ if (buf == NULL) {
+ syslog(LOG_ERR,
+ "Can't create existing interface list: Out of memory: %m");
+ return (-1);
+ }
+
+ lifc.lifc_family = AF_INET;
+ lifc.lifc_flags = 0;
+ lifc.lifc_len = bufsize;
+ lifc.lifc_buf = buf;
+ if (ioctl(iocsock, SIOCGLIFCONF, (char *)&lifc) < 0) {
+ syslog(LOG_ERR,
+ "Can't get existing system interface configuration: %m");
+ free(buf);
+ return (-1);
+ }
+
+
+ StaticIntfaceHead = NULL;
+ lifrp = lifc.lifc_req;
+ for (n = lifc.lifc_len / sizeof (struct lifreq); n > 0; n--, lifrp++) {
+ (void) strncpy(lifr.lifr_name, lifrp->lifr_name,
+ sizeof (lifr.lifr_name));
+ if (strchr(lifr.lifr_name, ':') != NULL)
+ continue;
+ if (ioctl(iocsock, SIOCGLIFFLAGS, (char *)&lifr) < 0) {
+ syslog(LOG_ERR,
+ "Can't get flag information for %s: %m",
+ lifr.lifr_name);
+ continue;
+ }
+ if (lifr.lifr_flags & IFF_LOOPBACK)
+ continue;
+ /* Create or add interface list */
+ ifce_ptr = (struct staticIface *)
+ malloc(sizeof (struct staticIface));
+ if (ifce_ptr == NULL) {
+ syslog(LOG_ERR,
+ "malloc: Can't create existing interface list: %m");
+ free(buf);
+ return (-1);
+ }
+ (void) strncpy(ifce_ptr->ifacename, lifr.lifr_name,
+ sizeof (lifr.lifr_name));
+ if (StaticIntfaceHead == NULL)
+ StaticIntfaceHead = ifce_ptr;
+ if (saveptr != NULL) {
+ saveptr->next = ifce_ptr;
+ }
+ ifce_ptr->next = NULL;
+ saveptr = ifce_ptr;
+ }
+ (void) close(iocsock);
+ free(buf);
+ return (0);
+}
+
+/*
+ * This function returns true or false based on matching entries
+ * from StaticIfaceEntry list
+ */
+boolean_t
+existingStaticInterface(const char *ifname) {
+ struct staticIface *Sptr;
+
+ Sptr = StaticIntfaceHead;
+ while (Sptr != NULL) {
+ if (strcmp(Sptr->ifacename, ifname) == 0) {
+ mipverbose(("existingStaticInterface:"
+ "Found a static existing entry\n"));
+ return (_B_TRUE);
+ }
+ Sptr = Sptr->next;
+ }
+ return (_B_FALSE);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mipagent/agentKernelIntfce.h b/usr/src/cmd/cmd-inet/usr.lib/mipagent/agentKernelIntfce.h
new file mode 100644
index 0000000000..a8637098e1
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mipagent/agentKernelIntfce.h
@@ -0,0 +1,73 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _AGENTKERNELINTERFACE_H
+#define _AGENTKERNELINTERFACE_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Prototypes for routines that interface with routing
+ * engine and tunnel driver.
+ */
+
+#include <sys/sockio.h>
+#include <net/if_arp.h>
+#include <net/if_dl.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ETHER_STR_LEN 18
+
+#define ADDRT 1
+#define DELRT 0
+
+
+#define routeadd(dst, gw, insrc, in_if, out_if) \
+ routemodify(dst, gw, insrc, in_if, out_if, ADDRT)
+#define routedel(dst, gw, insrc, in_if, out_if) \
+ routemodify(dst, gw, insrc, in_if, out_if, DELRT)
+
+int encapadd(ipaddr_t, ipaddr_t, uint32_t, uint8_t);
+int encaprem(ipaddr_t);
+int decapadd(ipaddr_t, ipaddr_t);
+int decaprem(ipaddr_t, ipaddr_t);
+int arpadd(ipaddr_t, unsigned char *, unsigned int);
+int arpdel(ipaddr_t);
+int arprefresh(HaMobileNodeEntry *, ipaddr_t);
+int routemodify(ipaddr_t, ipaddr_t, ipaddr_t, int, int, unsigned int);
+int getEthernetAddr(char *, unsigned char *);
+int getIfaceInfo(char *, ipaddr_t *, ipaddr_t *, uint64_t *, uint32_t *);
+boolean_t MipTunlEntryLookup(void *, uint32_t, uint32_t, uint32_t);
+boolean_t existingStaticInterface(const char *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _AGENTKERNELINTERFACE_H */
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mipagent/agentNet.c b/usr/src/cmd/cmd-inet/usr.lib/mipagent/agentNet.c
new file mode 100644
index 0000000000..defae18ba6
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mipagent/agentNet.c
@@ -0,0 +1,2035 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1999-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * file: agentNet.c
+ *
+ * This file contains all routines used to interact with the
+ * network, such as reading and writing.
+ *
+ * This file also contains the event dispatcher, which submits
+ * packets for processing to a pool of threads.
+ */
+
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/fcntl.h>
+#include <sys/poll.h>
+#include <sys/sockio.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/udp.h>
+#include <arpa/inet.h>
+#include <net/route.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <syslog.h>
+#include <assert.h>
+#include <ctype.h>
+#include <fcntl.h>
+
+#include "mip.h"
+#include "agent.h"
+#include "thq.h"
+#include "setup.h"
+#include "agentKernelIntfce.h"
+
+#define MIN_IP_HDR_LEN 20
+#define INFTIM -1
+
+/*
+ * The following is the max number of Message Headers we will
+ * allocate in a single chunk. The bigger the value, the more
+ * memory we allocate, the smaller, the more often we malloc().
+ */
+#define MAX_MSG_HDR_NUM 512
+
+#define MIP_MAX_THREADS 64
+#define MIP_MIN_THREADS 4
+
+
+static char *ifTypeName[] = {
+ "Unicast",
+ "Broadcast",
+ "Multicast"
+};
+
+static tqTp messageQueue = NULL;
+static rwlock_t msgQueueLock;
+static MessageHdr *msgHdrQueue = NULL;
+static int allocatedMsgHdr = 0;
+static pthread_t dispatchThreadId = 0;
+static pthread_t DynamicThreadId = 0;
+static fd_set saved_fdvec;
+static struct rt_msghdr *rt_msg;
+static struct if_msghdr *ifm;
+static int ioc_sock;
+
+/* Counters common to all Mobility Agents */
+extern CommonCounters commonCounters;
+
+/* Foreign Agent specific data structures. */
+extern struct hash_table faVisitorHash;
+extern ForeignAgentCounters faCounters;
+
+extern int logVerbosity;
+extern struct hash_table maAdvConfigHash;
+extern boolean_t faBusy;
+extern int visitorEntryHighWaterMark;
+extern int visitorEntryLowWaterMark;
+
+static int lookup_existing_entries(ushort_t, ipaddr_t);
+static void *processMsgHdr();
+static void *getAndDispatchNetworkPacket(void);
+static void *doDynamicInterfaceProcess(void *);
+static void process_rtsock_msg(int);
+static void processIncomingMessage(HashTable *, fd_set *);
+static void *find_ancillary(struct msghdr *msg, int cmsg_type);
+static void delmaAdvConfigEntry(uint32_t);
+static DynamicIfaceTypeEntry *match_dynamic_table(char *);
+
+extern char *ntoa(uint32_t, char *);
+extern void maSendAdvertisement(MaAdvConfigEntry *entry, ipaddr_t dst,
+ int advType, boolean_t faBusy);
+extern void FAprocessRegRequest(MessageHdr *, MaAdvConfigEntry *, ipaddr_t *);
+extern void HAprocessRegRequest(MessageHdr *, MaAdvConfigEntry *, ipaddr_t *);
+extern void FAprocessRegReply(MessageHdr *, MaAdvConfigEntry *);
+
+extern void rejectFromICMPToMN(MessageHdr *, ipaddr_t, int);
+
+extern void HAdispatchRadius(MessageHdr *messageHdr, MaAdvConfigEntry *entry,
+ ipaddr_t *inAddr);
+
+/*
+ * Function: AllocateMessageHdrBlock
+ *
+ * Arguments:
+ *
+ * Description: This function will pre-allocate a block of
+ * MAX_MSG_HDR_NUM Message Control Blocks.
+ *
+ * Returns: int - 0 if successful.
+ */
+static boolean_t
+AllocateMessageHdrBlock()
+{
+ MessageHdr *messageHdr;
+ int i;
+
+ /*
+ * Now we create the message control blocks...
+ */
+#if 0
+ msgHdrQueue = (MessageHdr *)calloc(1, sizeof (MessageHdr) *
+ MAX_MSG_HDR_NUM);
+#else
+ msgHdrQueue = (MessageHdr *)calloc(MAX_MSG_HDR_NUM,
+ sizeof (MessageHdr));
+#endif
+
+ if (msgHdrQueue == NULL) {
+ syslog(LOG_CRIT, "Unable to allocate message header queue");
+ return (_B_TRUE);
+ }
+
+ for (i = 0, messageHdr = msgHdrQueue; i < MAX_MSG_HDR_NUM; i++) {
+ messageHdr->next = messageHdr + 1;
+ }
+ messageHdr->next = NULL;
+
+ return (_B_FALSE);
+}
+
+/*
+ * Function: AllocateMessageHdr
+ *
+ * Arguments:
+ *
+ * Description: This function will return one of the Message
+ * Control Blocks from the queue. If none are
+ * available, we will attempt to allocate another
+ * chunk of control blocks.
+ *
+ * Returns: Pointer to Messge Control Block. NULL if failed.
+ */
+MessageHdr *
+AllocateMessageHdr()
+{
+ MessageHdr *messageHdr = NULL;
+
+ /*
+ * Lock the queue
+ */
+ (void) rw_wrlock(&msgQueueLock);
+
+ /*
+ * If there are no items on the queue, we will try to allocate
+ * another chunk.
+ */
+ if (msgHdrQueue == NULL) {
+ if (AllocateMessageHdrBlock()) {
+ syslog(LOG_CRIT, "Unable to allocate more message queues");
+ return (NULL);
+ }
+ }
+
+ /*
+ * Now get the message header to return
+ */
+ messageHdr = msgHdrQueue;
+ msgHdrQueue = msgHdrQueue->next;
+ allocatedMsgHdr++;
+
+ /*
+ * Unlock the queue
+ */
+ (void) rw_unlock(&msgQueueLock);
+
+ /*
+ * Initialize the NAI stuff in the message header.
+ */
+ messageHdr->mnNAILen = 0;
+ messageHdr->mnNAI = NULL;
+
+ messageHdr->faNAILen = 0;
+ messageHdr->faNAI = NULL;
+
+ messageHdr->dontDeleteNow = _B_FALSE;
+
+#ifdef KEY_DISTRIBUTION
+ /*
+ * KEY_DISTRIBUTION MUST ONLY BE COMPILED FOR TESTING!!!
+ *
+ * This version of mipagent supports a AAA/DIAMETER
+ * interface. The DIAMETER server generates keying
+ * material that is sent to the Home Agent. The keys
+ * sent are both for the Home Agent, and for the Mobile
+ * Node. The keys for the Mobile Nodes are added to the
+ * registration reply, and the keys for the Home Agent
+ * cause the Home Agent to create a local SA.
+ *
+ * Since DIAMETER/AAA is not currently a product, and key
+ * distribution must still be tested, we have added some
+ * test code in mipagent. When KEY_DISTRIBUTION is enabled,
+ * the home agent creates and encrypts session keys for
+ * the Mobile Node (mimicking DIAMETER), and creates local
+ * SAs. Further, since the session keys MUST also be sent
+ * to the Foreign Agent, the session keys are sent in the
+ * clear to the Foreign Agent through Vendor Specific
+ * extensions.
+ *
+ * Again, this code is for testing purpose only and must not
+ * be enabled for production code, since it hasn't been
+ * fully tested.
+ */
+ messageHdr->mnHaKeyLen = 0;
+ messageHdr->mnFaKeyLen = 0;
+ messageHdr->faHaKeyLen = 0;
+ messageHdr->kdcKeysPresent = _B_FALSE;
+#endif /* KEY_DISTRIBUTION */
+ return (messageHdr);
+}
+
+/*
+ * Function: FreeMessageHdr
+ *
+ * Arguments: messageHdr - Pointer to a pointer to a
+ * Message Control Block
+ *
+ * Description: Puts a Message Control Block back on the
+ * free list.
+ *
+ * Returns:
+ */
+void
+FreeMessageHdr(MessageHdr *messageHdr)
+{
+ /*
+ * When FA uses Radius to authenticate the RegReq, it sends a reg
+ * to Radius server and waits for the reply. Meanwhile, we want to
+ * preserve the messageHdr, because FA is going to need it. So,
+ * let's prevent messageHdr from being deleted, until FA is done and
+ * it explicitely calls FreeMessageHdr() with dontDeleteNow == FALSE
+ */
+ if (messageHdr->dontDeleteNow)
+ return;
+
+ /* Free up faNAI space malloc-ed in aaa.c */
+ if (messageHdr->faNAI != NULL)
+ free(messageHdr->faNAI);
+ /*
+ * Lock and put the item back on the queue
+ */
+ (void) rw_wrlock(&msgQueueLock);
+ messageHdr->next = msgHdrQueue;
+ msgHdrQueue = messageHdr;
+ allocatedMsgHdr--;
+ (void) rw_unlock(&msgQueueLock);
+}
+
+/*
+ * Function: startDispatcherTaskThread
+ *
+ * Arguments:
+ *
+ * Description: This function will allocate the thread queue,
+ * which is used to dispatch objects to be processed
+ * by threads created by the thread management module
+ * (thq.c). The function will also allocate the initial
+ * chunk of MAX_MSG_HDR_NUM Message Control Blocks and
+ * start the dispatching thread.
+ *
+ * Returns: int - 0 if successful.
+ */
+int
+startDispatcherTaskThread(void)
+{
+ pthread_attr_t pthreadAttribute;
+ int result;
+
+ messageQueue = tq_alloc(processMsgHdr, NULL,
+ (void *) NULL, NULL, MIP_MAX_THREADS, MIP_MIN_THREADS, FALSE);
+
+ if (!messageQueue) {
+ syslog(LOG_CRIT, "Unable to create thread queue");
+ return (-1);
+ }
+
+ (void) rw_wrlock(&msgQueueLock);
+
+ if (AllocateMessageHdrBlock()) {
+ (void) rw_unlock(&msgQueueLock);
+ syslog(LOG_CRIT, "Unable to allocate message Queues");
+ tq_shutdown(messageQueue, 1);
+ return (-1);
+ }
+
+ (void) rw_unlock(&msgQueueLock);
+
+ result = pthread_attr_init(&pthreadAttribute);
+
+ if (result) {
+ syslog(LOG_CRIT, "Error Initializing pthread.");
+ tq_shutdown(messageQueue, 1);
+ return (-1);
+ }
+
+ /*
+ * We now create a thread to deal with all periodic task.
+ */
+ result = pthread_create(&dispatchThreadId, &pthreadAttribute,
+ (void *(*)()) getAndDispatchNetworkPacket,
+ (void *)NULL);
+
+ if (result) {
+ syslog(LOG_CRIT, "pthread_create() failed.");
+ tq_shutdown(messageQueue, 1);
+ return (-1);
+ }
+
+ /*
+ * In order for system resources to be properly cleaned up,
+ * we need to detach the thread. Otherwise, we need to wait for
+ * a pthread_join(), which we do not want.
+ */
+ result = pthread_detach(dispatchThreadId);
+
+ if (result) {
+ syslog(LOG_CRIT, "pthread_detach() failed.");
+ (void) pthread_cancel(dispatchThreadId);
+ tq_shutdown(messageQueue, 1);
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * Function: killDispatcherTaskThread
+ *
+ * Arguments:
+ *
+ * Description: This function is used to kill the dispatch thread,
+ * the threads that are created by the thread queue
+ * management sub-system as well as the thread queue.
+ *
+ * Returns: int - 0 if sucessful.
+ */
+int
+killDispatcherTaskThread()
+{
+ int result;
+
+ if (dispatchThreadId) {
+ /*
+ * Next we need to kill the dispatching thread.
+ */
+ result = pthread_cancel(dispatchThreadId);
+
+ if (result) {
+ /*
+ * Well, there's not much we can do here..
+ */
+ syslog(LOG_CRIT, "Unable to kill dispatching thread");
+ }
+ }
+
+ if (messageQueue) {
+ /*
+ * First we kill all of the message handling threads.
+ */
+ tq_shutdown(messageQueue, 1);
+ }
+
+ return (0);
+}
+
+/*
+ * Function: startDynamicInterfaceThread
+ * Arguments: None
+ * Description:
+ * This function is called only when
+ * the configuration file indicates dynamic interface
+ * and DynamicInterface variable is true.
+ *
+ * Returns 0 on success.
+ */
+int
+startDynamicInterfaceThread(void)
+{
+ pthread_attr_t pthreadAttribute;
+ int result;
+
+ result = pthread_attr_init(&pthreadAttribute);
+
+ if (result != 0) {
+ syslog(LOG_CRIT, "Error Initializing pthread: %m");
+ return (-1);
+ }
+
+ /*
+ * We now create a thread to deal with processing new interfaces
+ */
+ result = pthread_create(&DynamicThreadId, &pthreadAttribute,
+ doDynamicInterfaceProcess, NULL);
+
+ if (result != 0) {
+ syslog(LOG_CRIT, "pthread_create() failed: %m");
+ return (-1);
+ }
+
+ /*
+ * In order for system resources the be properly cleaned up,
+ * we need to detach the thread. Otherwise, we need to wait for
+ * a pthread_join(), which we do not want.
+ */
+ result = pthread_detach(DynamicThreadId);
+
+ if (result != 0) {
+ syslog(LOG_CRIT, "pthread_detach() failed: %m");
+ return (-1);
+ }
+
+ return (0);
+}
+
+
+
+/*
+ * Function: killDynamicInterfaceThread
+ * Arguments:
+ * Description: This function is used for thread cleanup
+ * Returns 0 on success
+ */
+int
+killDynamicInterfaceThread(void)
+{
+ int result;
+
+ if (DynamicThreadId) {
+ /*
+ * Next we need to kill the dispatching thread.
+ */
+ result = pthread_cancel(DynamicThreadId);
+
+ if (result != 0) {
+ /*
+ * Well, there's not much we can do here..
+ */
+ syslog(LOG_CRIT, "Unable to kill Dynamic thread: %m");
+ return (-1);
+ }
+ DynamicThreadId = 0;
+ }
+ return (0);
+}
+
+/*
+ * Function: dispatchMsgToThread
+ *
+ * Arguments: messageHdr - Pointer to a pointer to a
+ * Message Control Block
+ *
+ * Description: This function will submit a Message Control
+ * Block for processing to a child thread using
+ * the thread management sub-system.
+ *
+ * Returns: int - 0 if successfully dispatched to thread.
+ */
+int
+dispatchMsgToThread(MessageHdr **messageHdr)
+{
+
+ if (*messageHdr == NULL) {
+ return (-1);
+ }
+
+ if (tq_queue(messageQueue, *messageHdr)) {
+ syslog(LOG_CRIT, "Unable to dispatch message to thread");
+ return (-1);
+ }
+
+ /*
+ * Since we've queued the packet for a thread, we will
+ * NULL out the pointer so that it does not get re-used.
+ */
+ *messageHdr = NULL;
+
+ return (0);
+}
+
+
+/*
+ * Function: sendUDPmessage
+ *
+ * Arguments: sock - Socket
+ * pkt - Packet to send
+ * pktLen - packet Length
+ * dst - Destination IP Address
+ * dstPort - Destination IP Port
+ *
+ * Description: Send a UDP message from sock, to dst address and port
+ * dstPort. The pkt is contained in pkt and is of length
+ * pktLen. dst is already in network byte order.
+ *
+ * Returns:
+ */
+int
+sendUDPmessage(int sock, unsigned char *pkt, int pktLen,
+ ipaddr_t dst, in_port_t dstPort)
+{
+ struct sockaddr_in sa;
+
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons(dstPort);
+ sa.sin_addr.s_addr = dst;
+
+ /* send the message */
+ if (sendto(sock, (char *)pkt, pktLen, 0, (struct sockaddr *)&sa,
+ sizeof (struct sockaddr_in)) < 0) {
+ syslog(LOG_ERR, "sendto() Sendto failed in sendUDPmessage.");
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * Function: sendICMPmessage
+ *
+ * Arguments: s - socket
+ * dst - Destination Address
+ * data - Data to send
+ * len - length of data to send
+ *
+ * Description: Send an ICMP message contained in data to dst
+ * from src
+ *
+ * Returns:
+ */
+void
+sendICMPmessage(int s, ipaddr_t dst,
+ unsigned char data[], int len)
+{
+ struct sockaddr_in sa;
+
+ sa.sin_family = AF_INET;
+ sa.sin_port = 0;
+ sa.sin_addr.s_addr = dst;
+
+ /* send the message */
+ if (sendto(s, (char *)data, len, 0, (struct sockaddr *)&sa,
+ sizeof (struct sockaddr_in)) < 0) {
+ syslog(LOG_ERR, "sendto() Sendto failed in sendICMPmessage.");
+ }
+}
+
+
+/*
+ * Function: screenICMPpkt
+ *
+ * Arguments: messageHdr - Pointer to a Message Control Block
+ *
+ * Description: Pkt contains a complete IP datagram(including IP
+ * header) received on a socket monitoring ICMP packets.
+ *
+ * Returns:
+ */
+static void
+screenICMPpkt(MessageHdr *messageHdr)
+{
+ icmph *icmpPtr;
+ struct ip *ipPtr, *inneripPtr; /* ICMPs outer and inner IP headers */
+ struct udphdr *innerudpPtr; /* perchance the innerIP is UDP */
+ char addrstr1[INET_ADDRSTRLEN];
+ char addrstr2[INET_ADDRSTRLEN];
+ uint16_t ipHdrLen, iError;
+ ipaddr_t *ipDst;
+
+ if (messageHdr->pktLen < sizeof (struct ip) + sizeof (icmph)) {
+ syslog(LOG_ERR, "ICMP Packet received too small");
+ return;
+ }
+
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ ipPtr = (struct ip *)&messageHdr->pkt;
+
+ ipHdrLen = (uint16_t)(ipPtr->ip_hl << 2);
+
+ if (messageHdr->pktLen < ipHdrLen + sizeof (icmph)) {
+ syslog(LOG_ERR, "Packet received is smaller than reported");
+ return;
+ }
+
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ icmpPtr = (icmph *)((char *)messageHdr->pkt + ipHdrLen);
+
+ ipDst = (ipaddr_t *)&ipPtr->ip_dst;
+
+ /*
+ * Note: we don't have to check the ICMP size, and/or the ICMP
+ * checksum because the kernel takes care of this. As far as dst
+ * addr goes, the understanding is traffic is checked for forwarding
+ * before the kernel's parser mechanism gets it for passing up.
+ */
+
+ if (icmpPtr->type == ICMP_ROUTERSOLICIT) {
+ mipverbose((
+ "Got ICMP solicitation <type %d, code %d> from %s to %s.\n",
+ icmpPtr->type, icmpPtr->code,
+ ntoa(messageHdr->src, addrstr1),
+ ntoa(*ipDst, addrstr2)));
+
+ if (icmpPtr->code == 0) {
+ /* Currently, the only code for type 10 solicits. */
+ commonCounters.maSolicitationsRecvdCnt++;
+
+ /*
+ * Restrict advertisement to the particular interface
+ * on which the solicitation was received.
+ */
+ if (((messageHdr->ifEntry->maIfaceFlags &
+ IFF_POINTOPOINT) == 0) ||
+ (messageHdr->src == INADDR_ANY)) {
+ /*
+ * Since we don't have ARP information at this
+ * point, advertise on Bcast or Mcast addr.
+ */
+ maSendAdvertisement(messageHdr->ifEntry,
+ messageHdr->ifEntry->maAdvAddr,
+ SOLICITED_ADV, faBusy);
+ } else {
+ /*
+ * Solicitation from PPP interface with a
+ * non-zero source address.
+ */
+ maSendAdvertisement(messageHdr->ifEntry,
+ messageHdr->src, SOLICITED_ADV, faBusy);
+ }
+
+ /*
+ * should actually be manipulated inside
+ * maSendAdvertisement()
+ */
+ commonCounters.maAdvSentForSolicitationsCnt++;
+ }
+
+ /* There can be only one reason we're in here, so we're done. */
+ return;
+ }
+
+ /*
+ * If this ICMP is in response to a forwarded registration request,
+ * the IP dst addr should be ours...
+ */
+ if (ipPtr->ip_dst.s_addr != messageHdr->ifEntry->maIfaceAddr) {
+ /* not for us */
+ return;
+ }
+
+ /*
+ * A response to a packet we sent, check if it's a undelivered regreQ.
+ * We'd better have gotten enough for a returned UDP header.
+ */
+ if (messageHdr->pktLen < sizeof (struct ip) + sizeof (icmph) +
+ sizeof (struct udphdr)) {
+ /* if we can't get to the udp port info, we can't continue */
+ syslog(LOG_ERR,
+ "Recieved ICMP error allegedly for a packet we sent,"
+ " but it's too small to do anything with!\n");
+ return;
+ }
+
+ /*
+ * Note: the belief is before the kernel sends up the ICMP, it's
+ * checked things like length, checksum, etc.
+ */
+
+ /* It's time to look at the returned IP packet. */
+ inneripPtr = (struct ip *)(icmpPtr+1);
+
+ /* We should be the sender of the inner packet */
+ if (inneripPtr->ip_src.s_addr != messageHdr->ifEntry->maIfaceAddr) {
+ /* We're not responsible for sending that packet */
+ return;
+ }
+
+ /* UDP? */
+ if (inneripPtr->ip_p != IPPROTO_UDP) {
+ /* protocol in returned IP header isn't UDP - <zap> */
+ return;
+ }
+
+ /* OK, the encased IP packet allegedly carries UDP info - go there */
+ ipHdrLen = (uint16_t)(inneripPtr->ip_hl << 2);
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ innerudpPtr = (struct udphdr *)((char *)inneripPtr + ipHdrLen);
+
+ /*
+ * Swapping madness for the port value because this piece is RAW off
+ * the network. Note: htons() is a no-op on SPARC.
+ */
+ if (htons(innerudpPtr->uh_dport) != MIP_PORT) {
+ /* dst port of returned UDP packet isn't MIP_PORT <zzzt>. */
+ return;
+ }
+
+
+ /*
+ * Looks like we were returned a UDP packet to port 434, so lets
+ * find out why delivery failed, then let the mobile node know.
+ *
+ * There are two reasons we could be geting an ICMP unreachable. The
+ * most obvious comes from bad addresses, but another possibility is
+ * the simple timeout.
+ */
+ if (icmpPtr->type == ICMP_UNREACH) {
+
+ /* All that info implies it was for something we sent out */
+ mipverbose((
+ "Received ICMP error in response to regreQ from MN: "));
+ mipverbose(("Type: UNREACH, ICMPcode: %d.", icmpPtr->code));
+
+ /* The error to the MN is based on the ICMP code... */
+ switch (icmpPtr->code) {
+ case ICMP_UNREACH_NET:
+ case ICMP_UNREACH_NET_UNKNOWN:
+ case ICMP_UNREACH_NET_PROHIB:
+ /* return error 80 home network unreachable */
+ mipverbose(("Home Network Unreachable.\n"));
+ iError = FA_HA_NET_UNREACHABLE;
+ break;
+
+ case ICMP_UNREACH_HOST:
+ case ICMP_UNREACH_HOST_UNKNOWN:
+ case ICMP_UNREACH_HOST_PROHIB:
+ /* return error 81 home agent host unreachable */
+ mipverbose(("Home Agent IP Unreachable.\n"));
+ iError = FA_HA_HOST_UNREACHABLE;
+ break;
+
+ case ICMP_UNREACH_PORT:
+ /* return error 82 home agent port unreachable */
+ mipverbose(("Home Agent Port Unreachable.\n"));
+ iError = FA_HA_PORT_UNREACHABLE;
+ break;
+
+ case ICMP_UNREACH_ISOLATED:
+ case ICMP_SOURCEQUENCH:
+ /*
+ * Notes for the case of sourceQuench - the packet
+ * was lost somewhere (what else can we assume?)
+ * Two choices: send an unreachable error to the
+ * MN, or resend the registration request, but it's
+ * NOT contained in the ICMP error message. If it
+ * cares, the MN will regenerate, perhaps to a
+ * fallback HA.
+ *
+ * Return error 88 home agent unreachable.
+ */
+ mipverbose(("Home Agent Unreachable.\n"));
+ iError = FA_HA_UNREACHABLE;
+ break;
+
+ default:
+ /*
+ * E.g. ICMP_UNREACH_PROTOCOL, ICMP_UNREACH_NEEDFRAG,
+ * ICMP_UNREACH_SRCFAIL - catch them here (OK, the last
+ * two are a bit of a reach). Also, anything "new" to
+ * ICMP will have to return something generic for now.
+ *
+ * Return error 88 home agent unreachable.
+ */
+ mipverbose(("Unreachable default:"
+ " Home Agent Unreachable.\n"));
+ iError = FA_HA_UNREACHABLE;
+ break;
+
+ } /* switch(icmpPtr->code) */
+
+ /* bump our counters */
+ faCounters.faRegRepliesICMPUnreachCnt++;
+
+ /* only one ICMP type, so... */
+ (void) rejectFromICMPToMN(messageHdr,
+ inneripPtr->ip_dst.s_addr, iError);
+
+ /* There can be only one reason, we're done */
+ return;
+ }
+
+ /* Don't forget timeouts */
+ if (icmpPtr->type == ICMP_TIMXCEED) {
+ mipverbose((
+ "Received ICMP error in response to regreQ from MN: "));
+
+ mipverbose(("Type: TIMEOUT, ICMPcode: %d.", icmpPtr->code));
+
+ /* The error is based on the ICMP message... */
+ switch (icmpPtr->code) {
+ case ICMP_TIMXCEED_INTRANS:
+ case ICMP_TIMXCEED_REASS:
+ mipverbose(("timeout-88: Home Agent Unreachable.\n"));
+ iError = FA_HA_UNREACHABLE;
+ break;
+
+ default:
+ /* Not the format of an ICMP_TIMXCEED, but still... */
+ mipverbose(("timeout: default"
+ "88: Home Agent Unreachable.\n"));
+ iError = FA_HA_UNREACHABLE;
+ break;
+ }
+
+ /* bump our counters */
+ faCounters.faRegRepliesICMPTimxceedCnt++;
+
+ /* only one reason to reject... */
+ (void) rejectFromICMPToMN(messageHdr,
+ inneripPtr->ip_dst.s_addr, iError);
+ }
+
+ /*
+ * Think: ICMP_REDIRECT{NET,HOST,TOSNET,TOSHOST} should be handled
+ * in-stack before we see it. ^^^^^^ ^^^^^^^
+ */
+
+ /* If we made it here, we don't care about this ICMP type; done... */
+ return;
+
+} /* screenICMPpkt() */
+
+
+/*
+ * Function: screenUDPpkt
+ *
+ * Arguments: messageHdr - Pointer to a Message Control Block
+ *
+ * Description: Pkt contains a complete UDP datagram received on
+ * a socket monitoring UDP packets.
+ *
+ * Returns:
+ */
+static void
+screenUDPpkt(MessageHdr *messageHdr)
+{
+ regRequest *regReqPtr;
+ unsigned char *cp;
+ char addrstr1[INET_ADDRSTRLEN];
+ char addrstr2[INET_ADDRSTRLEN];
+ ipaddr_t inAddr;
+ boolean_t ha_match, fa_match;
+ boolean_t is_ha, is_fa;
+
+ switch (messageHdr->ifType) {
+ case ON_UNICAST_SOCK:
+ inAddr = messageHdr->ifEntry->maIfaceAddr;
+ break;
+
+ case ON_MCAST_SOCK:
+ inAddr = inet_addr(LINK_MCAST_REG_ADDR);
+ break;
+
+ case ON_BCAST_SOCK:
+ inAddr = GENERATE_NET_BROADCAST_ADDR(messageHdr->ifEntry);
+ break;
+ default:
+ syslog(LOG_ERR, "screenUDPpkt: Unknown socket type \n");
+ break;
+
+ }
+
+ cp = messageHdr->pkt;
+
+ switch (*cp) {
+
+ case REG_REQUEST_TYPE:
+
+ if (messageHdr->pktLen < sizeof (regRequest)) {
+ syslog(LOG_ERR, "Received registration request is " \
+ "too short.");
+ return;
+ }
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ regReqPtr = (regRequest *) messageHdr->pkt;
+
+ /*
+ * Here is what we need to do. If the Home Agent Address
+ * matches our interface address, or the packet is a broadcast,
+ * then we process it as a Home Agent. However, if the packet's
+ * care of address matches our address, or if it is a
+ * multicast, then we process the packet as a Foreign Agent.
+ */
+ is_ha = (messageHdr->ifEntry->maAdvServiceFlags &
+ ADV_IS_HOME_AGENT);
+ ha_match = (is_ha && ((regReqPtr->haAddr == inAddr) ||
+ (messageHdr->ifType == ON_BCAST_SOCK)));
+
+ is_fa = (messageHdr->ifEntry->maAdvServiceFlags &
+ ADV_IS_FOREIGN_AGENT);
+ fa_match = (is_fa && ((regReqPtr->COAddr == inAddr) ||
+ (messageHdr->ifType == ON_MCAST_SOCK)));
+
+ mipverbose(("req.haddr=0x%08x, inAddr=0x%08x, coAddr=0x%08x, ",
+ regReqPtr->haAddr, inAddr, regReqPtr->COAddr));
+ mipverbose(("HAFlag = %d, FAFlag = %d, ", is_ha ? 1 : 0,
+ is_fa ? 1 : 0));
+ if (ha_match)
+ mipverbose((" we are *the* HA\n"));
+ if (fa_match)
+ mipverbose((" we are *the* FA\n"));
+
+ /*
+ * Check out the matching criterias to determine if we are
+ * acting as the HA or FA for this request. If we match the
+ * request as the HA, process it as HA. If we match the request
+ * as the FA, process it as FA. In case we are acting as HA but
+ * the request doesn't match neither our HA nor FA, let's go
+ * ahead and process this request as an HA and deny it.
+ * Similarly, in case we are acting as FA but the request
+ * doesn't match neither our FA nor HA, let's go ahead and
+ * process this request as an FA and deny it.
+ */
+ if (messageHdr->pktSource == MIP_PKT_FROM_RADIUS) {
+ /*
+ * This occurs when the HA address in the registration
+ * request is invalid, which occurs when AAA is used
+ * and the Home Agent's address is set to either zero
+ * or 0xffffffff in the registration Request. In this
+ * case, we need to update the Home Agent's address
+ * in the Registration Reply.
+ *
+ * MIP_PKT_FROM_RADIUS is Radius specific. In this
+ * case a call is made to HAprocessRegRequestRadius.
+ */
+ regReqPtr->haAddr = messageHdr->ifEntry->maIfaceAddr;
+ (void) HAdispatchRadius(messageHdr,
+ messageHdr->ifEntry, &inAddr);
+ } else if (fa_match || (is_fa && !ha_match)) {
+ /* process as FA */
+ FAprocessRegRequest(messageHdr, messageHdr->ifEntry,
+ &inAddr);
+ } else if (ha_match || (is_ha && !fa_match)) {
+ /* process as HA */
+ HAprocessRegRequest(messageHdr, messageHdr->ifEntry,
+ &inAddr);
+ } else if (messageHdr->pktSource == MIP_PKT_FROM_AAA) {
+ /*
+ * MIP_PKT_FROM_AAA - for all other aaa protocols.
+ * This also occurs when the HA address in the reg
+ * request is invalid, which occurs when AAA is used
+ * and the Home Agent's address is set to either zero
+ * or 0xffffffff in the registration Request. In this
+ * case, we need to update the Home Agent's address
+ * in the Registration Reply.
+ */
+ regReqPtr->haAddr = messageHdr->ifEntry->maIfaceAddr;
+ HAprocessRegRequest(messageHdr, messageHdr->ifEntry,
+ &inAddr);
+ } else {
+ syslog(LOG_ERR,
+ "Not configured to handle this reg req on addr " \
+ "%s from %s.",
+ ntoa(inAddr, addrstr1), ntoa(messageHdr->src,
+ addrstr2));
+ }
+ break;
+
+ case REG_REPLY_TYPE:
+ if (messageHdr->pktLen < sizeof (regReply)) {
+ syslog(LOG_ERR, "Received registration reply is too " \
+ "short.");
+ return;
+ }
+ FAprocessRegReply(messageHdr, messageHdr->ifEntry);
+ break;
+
+ default:
+ syslog(LOG_ERR,
+ "Unknown UDP message (first byte 0x%x) from %s.",
+ *cp, ntoa(messageHdr->src, addrstr1));
+ }
+}
+
+/*
+ * Function: processMsgHdr
+ *
+ * Description: This is the child thread that is called by
+ * the thread management sub-system to handle
+ * a specific Message Control Block. This function
+ * will remain in a while loop waiting for messages
+ * to handle.
+ *
+ * This function never returns, but can be killed by
+ * another thread calling tq_shutdown().
+ *
+ * Returns: void pointer of NULL. Note this is not really necessary,
+ * but makes thq.c/h prototypes happy.
+ */
+static void *
+processMsgHdr()
+{
+ MessageHdr *messageHdr;
+
+ /*
+ * Main while loop... should never exit
+ */
+ /* CONSTCOND */
+ while (_B_TRUE) {
+ messageHdr = (MessageHdr *) tq_dequeue(messageQueue, 0);
+
+ if (messageHdr == NULL)
+ continue;
+
+ switch (messageHdr->pktType) {
+ case PKT_UDP:
+ screenUDPpkt(messageHdr);
+ break;
+ case PKT_ICMP:
+ screenICMPpkt(messageHdr);
+ break;
+ default:
+ syslog(LOG_ERR, "processMsgHdr: Unknown Pkt type\n");
+ break;
+ }
+
+ FreeMessageHdr(messageHdr);
+ }
+
+ return (NULL);
+}
+
+
+/*
+ * Function: getAndDispatchNetworkPacket
+ *
+ * Arguments:
+ *
+ * Description: This is the main dispatch thread. This
+ * function has two main functions. First it will
+ * use select to determine if any data has been
+ * received on our sockets. Second, the select
+ * function is called with a timeout, which is
+ * used to determine when we need to send router
+ * advertisements.
+ *
+ * This function never returns, but will be killed
+ * if killDispatcherTaskThread() is called.
+ *
+ * Returns:
+ */
+static void *
+getAndDispatchNetworkPacket()
+{
+ int i, nentry;
+ int result;
+ static fd_set fdvec;
+ static struct timeval tv;
+ time_t nextAdvTime = 0;
+ time_t currentTime;
+ time_t lowestNextAdvTime = 0;
+ struct hash_table *htbl;
+ MaAdvConfigEntry *entry;
+ struct hash_entry *p;
+
+ htbl = &maAdvConfigHash;
+
+ /*
+ * Setup the initial advertisement time.
+ */
+ GET_TIME(currentTime);
+ nextAdvTime = currentTime;
+
+ /*
+ * We will save the file descriptor vector for future use.
+ * This saves us the trouble of having to skip through the
+ * while interface entry each time.
+ *
+ * NOTE: Since this block of code is called during initial static
+ * interface configuration, we do not need to lock the config
+ * entries here as the sequence at startup is startDispatcherTaskThread
+ * followed by startDynamicInterfaceThread.
+ */
+ for (i = 0, nentry = 0;
+ i < HASH_TBL_SIZE && (nentry < htbl->size); i++) {
+ p = htbl->buckets[i];
+ while (p != NULL) {
+ nentry++;
+ entry = (MaAdvConfigEntry *)p->data;
+ FD_SET(entry->maIfaceUnicastSock, &saved_fdvec);
+ if ((entry->maIfaceFlags & IFF_POINTOPOINT) == 0) {
+ FD_SET(entry->maIfaceDirBcastSock,
+ &saved_fdvec);
+ FD_SET(entry->maIfaceBcastSock,
+ &saved_fdvec);
+ }
+ FD_SET(entry->maIfaceAdvMulticastSock, &saved_fdvec);
+ FD_SET(entry->maIfaceRegMulticastSock, &saved_fdvec);
+ FD_SET(entry->maIfaceIcmpSock, &saved_fdvec);
+ /* Update maNextAdvtime to advertise now */
+ entry->maNextAdvTime = currentTime;
+ p = p->next;
+ }
+ }
+
+ for (;;) {
+ /*
+ * Take a snapshot of the file descriptors.
+ */
+ fdvec = saved_fdvec;
+
+ /*
+ * What time is it?
+ */
+ GET_TIME(currentTime);
+
+ /*
+ * Wait on select for next adv or input or when
+ * there is no static interface entry in the config file
+ */
+ if ((currentTime < nextAdvTime) || (nentry == 0)) {
+ tv.tv_sec = nextAdvTime - currentTime;
+ tv.tv_usec = 0;
+
+ /*
+ * Caveat: Currently, if one static/dynamic interface
+ * adv n sec later, then advertisement for new
+ * interface waits until select times out. So, in
+ * some cases, the first adv does not happen instantly.
+ * Recommended AdvFrequency is 2-3 sec for dynamic
+ * interfaces.
+ */
+ result = select(FD_SETSIZE,
+ &fdvec, NULL, NULL, &tv);
+
+ if (result < 0) {
+ if ((errno != EINTR) && (errno != ERESTART) &&
+ (errno != EBADF)) {
+ /*
+ * EBADF can be often set
+ * from select in situations when
+ * the socket fd's corresponding
+ * to the dynamic interface is gone
+ * while select was in sleep.
+ * This situation can happen often
+ * as the connection comes and goes.
+ * Assuming the mipagent sets
+ * FD_SET correctly, at this point
+ * it is best not to print syslog
+ * error for the EBADF case to avoid
+ * plenty of such messages in the
+ * console.
+ */
+ syslog(LOG_ERR,
+ "select failed with error: %m");
+ }
+ continue;
+ }
+
+ /*
+ * Now that we've slept, let's get our current time.
+ */
+ GET_TIME(currentTime);
+ } else {
+ /*
+ * It looks like it's time to advertise, so we set
+ * result to zero. This will ensure that we will not
+ * attempt to receive a network packet.
+ */
+ result = 0;
+ }
+
+ /*
+ * Let's check if it is time to start advertising
+ */
+ if (currentTime >= nextAdvTime) {
+ /*
+ * Check each entry to see if it's time for at least
+ * one entry to advertise. lowestNextAdvTime keeps
+ * track of lowest NextAdvTime value of the traversed
+ * entries. During eachtime we check the entries to
+ * advertise, lowestNextAdvTime is updated to a
+ * lower value if the current entry's nextAdvTime is
+ * lower than the nextAdvTime local variable.
+ */
+ lowestNextAdvTime = 0;
+ for (i = 0, nentry = 0;
+ i < HASH_TBL_SIZE && (nentry < htbl->size); i++) {
+ p = htbl->buckets[i];
+ while (p) {
+ nentry++;
+ entry = (MaAdvConfigEntry *)p->data;
+ (void) rw_rdlock(
+ &entry->maIfaceNodeLock);
+ if (currentTime >=
+ entry->maNextAdvTime) {
+ /* Advertise now */
+ if (faVisitorHash.size <=
+ visitorEntryLowWaterMark) {
+ faBusy = _B_FALSE;
+ } else if (faVisitorHash.size >=
+ visitorEntryHighWaterMark) {
+ faBusy = _B_TRUE;
+ }
+ if (entry->maAdvInitCount > 0) {
+ maSendAdvertisement(
+ entry,
+ entry->maAdvAddr,
+ UNSOLICITED_ADV,
+ faBusy);
+ }
+ entry->maNextAdvTime =
+ currentTime +
+ entry->maAdvInterval;
+ }
+ /*
+ * Set next timeout equal to the minimum
+ * time left for next advertisement for
+ * an entry. The following comparison is
+ * true when nextAdvTime is updated by
+ * interval and there is another entry
+ * in the list which needs to advertise
+ * before nextAdvTime.
+ */
+ if (entry->maAdvInitCount > 0) {
+ nextAdvTime =
+ entry->maNextAdvTime;
+
+ if (lowestNextAdvTime == 0) {
+ /* First pass */
+ lowestNextAdvTime =
+ nextAdvTime;
+ }
+ if (nextAdvTime >
+ lowestNextAdvTime) {
+ nextAdvTime =
+ lowestNextAdvTime;
+ } else {
+ lowestNextAdvTime =
+ nextAdvTime;
+ }
+ }
+ p = p->next;
+ (void) rw_unlock(
+ &entry->maIfaceNodeLock);
+ }
+ }
+ /*
+ * If for some reason there is no entry in the
+ * Confighash table and nextAdvTime was not
+ * updated at all, then set the nextAdvTime to
+ * to default value to avoid looping. This block
+ * can also be exercized if there is no periodic
+ * advertisment is done as we may have set
+ * AdvLimitUnsolicited to all advertising interfaces.
+ */
+ if (currentTime >= nextAdvTime) {
+ nextAdvTime = currentTime +
+ DEFAULT_MIN_INTERVAL;
+ }
+ } else if (result > 0) {
+ processIncomingMessage(htbl, &fdvec);
+ }
+ }
+ /* LINTED E_STMT_NOT_REACHED */
+ return (NULL);
+}
+
+/*
+ * Function: find_ancillary
+ * Arguments: msg - contains the ancillary information
+ * cmsg_type - type of ancillary data
+ * Description: Return a pointer to the specified option buffer.
+ * If not found return NULL.
+ */
+static void *
+find_ancillary(struct msghdr *msg, int cmsg_type)
+{
+ struct cmsghdr *cmsg;
+
+ for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL;
+ cmsg = CMSG_NXTHDR(msg, cmsg)) {
+ if (cmsg->cmsg_level == IPPROTO_IP &&
+ cmsg->cmsg_type == cmsg_type) {
+ return (CMSG_DATA(cmsg));
+ }
+ }
+ return (NULL);
+}
+
+/*
+ * Function: recvNetworkPacket
+ *
+ * Arguments: socket - Socket on which data is pending
+ * entry - Interface on which the data is pending
+ * ifType - Whether the interface if UNICAST, BCAST or MCAST
+ * packetType - Wether the packet is UDP or ICMP
+ *
+ * Description: This function is called if data is pending
+ * on one of our sockets. This function will
+ * allocate a Messge Control Block, receive
+ * the data and call the function that dispatches
+ * the control block to a thread.
+ *
+ * Returns:
+ */
+static void
+recvNetworkPacket(int socket, MaAdvConfigEntry *entry, uint32_t ifType,
+ uint32_t packetType)
+{
+ MessageHdr *messageHdr;
+ struct sockaddr_in from;
+ static uint64_t in_packet[(MAX_PKT_SIZE + 1)/8];
+ static uint64_t ancillary_data[(MAX_PKT_SIZE + 1)/8];
+ struct msghdr msg;
+ struct iovec iov;
+ uchar_t *opt;
+
+ iov.iov_base = (char *)in_packet;
+ iov.iov_len = sizeof (in_packet);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_name = (struct sockaddr *)&from;
+ msg.msg_namelen = sizeof (from);
+ msg.msg_control = ancillary_data;
+ msg.msg_controllen = sizeof (ancillary_data);
+
+ /*
+ * Allocate a Message Header.
+ */
+ if ((messageHdr = AllocateMessageHdr()) == NULL) {
+ syslog(LOG_CRIT,
+ "Unable to allocate a message header");
+ return;
+ }
+
+ if ((messageHdr->pktLen = recvmsg(socket, &msg, 0))
+ == (unsigned int) (-1)) {
+ FreeMessageHdr(messageHdr);
+ syslog(LOG_ERR,
+ "recvmsg failed for UDP on "
+ "%s socket...%s", ifTypeName[ifType], strerror(errno));
+ } else {
+ unsigned char *cp;
+
+ (void) memcpy(messageHdr->pkt, in_packet, MAX_PKT_SIZE);
+
+ cp = messageHdr->pkt;
+ /*
+ * If it's a registration request then we need to get the
+ * ancillary information.
+ * TODO: 1. Fix this section of code to IP_RECVIF
+ * for ICMP packets too.
+ * 2. Add kernel support for IP_RECVSLLA for ICMP
+ * packets. This is required for response to
+ * unicast address for agent solicitation
+ */
+ if ((packetType == PKT_UDP) && (*cp == REG_REQUEST_TYPE)) {
+
+ /*
+ * If this is an PPP interface, don't attempt to
+ * get the slla.
+ */
+ if (entry->maIfaceFlags & IFF_POINTOPOINT) {
+ messageHdr->isSllaValid = _B_FALSE;
+ } else {
+ /* try to extract slla from the packet */
+ opt = find_ancillary(&msg, IP_RECVSLLA);
+ if (opt == NULL) {
+ syslog(LOG_ERR,
+ "receving IP_RECVSLLA ancillary"
+ "failed...%s", strerror(errno));
+ /*
+ * IP_RECVSLLA could fail for various
+ * reasons:
+ * a> the interface is no-resolver type
+ * (eg PPP)
+ * b> kernel ran out of memory
+ * c> the information the driver passed
+ * to IP is bogus
+ * we could return from here but this
+ * info (i.e SLLA) is non-critical, so
+ * we don't. Instead we mark the entry
+ * as invalid so it's not used in the
+ * code. Currently the entry is used to
+ * prevent the FA from broadcast ARPing
+ */
+ messageHdr->isSllaValid = _B_FALSE;
+ } else {
+ messageHdr->isSllaValid = _B_TRUE;
+ bcopy(opt, &messageHdr->slla,
+ sizeof (struct sockaddr_dl));
+ }
+ }
+
+ opt = find_ancillary(&msg, IP_RECVIF);
+ if (opt == NULL) {
+ syslog(LOG_ERR, "receving IP_RECVIF ancillary"
+ "failed");
+ FreeMessageHdr(messageHdr);
+ return;
+ }
+
+ /* LINTED BAD_PTR_CAST_ALIGN */
+ messageHdr->inIfindex = *(uint_t *)opt;
+
+ opt = find_ancillary(&msg, IP_RECVTTL);
+ if (opt == NULL) {
+ syslog(LOG_ERR, "receving IP_RECVTTL ancillary"
+ "failed");
+ FreeMessageHdr(messageHdr);
+ return;
+ }
+
+ messageHdr->ttl = *(uint8_t *)opt;
+ } else {
+
+ /*
+ * Make sure they are initialized, just in case
+ */
+ messageHdr->inIfindex = 0;
+ messageHdr->isSllaValid = _B_FALSE;
+ }
+
+ messageHdr->src =
+ from.sin_addr.s_addr;
+ messageHdr->srcPort =
+ ntohs(from.sin_port);
+ messageHdr->ifEntry = entry;
+ messageHdr->ifType = ifType;
+ messageHdr->pktType = packetType;
+ messageHdr->pktSource = MIP_PKT_FROM_FA;
+ assert(!messageHdr->mnNAILen);
+ (void) dispatchMsgToThread(&messageHdr);
+ }
+}
+
+/*
+ * Function: getFirstInterface
+ *
+ * Arguments: none
+ *
+ * Description: This function will return the first interface in the hash
+ * table. Todo: Pick the closest interface
+ *
+ * Returns: MaAdvConfigEntry *
+ */
+MaAdvConfigEntry *
+getFirstInterface()
+{
+ HashTable *htbl;
+ MaAdvConfigEntry *entry;
+ HashEntry *p;
+ int i, nentry;
+
+ /*
+ * for each mobility supporting interface ...
+ */
+ htbl = &maAdvConfigHash;
+
+ for (i = 0, nentry = 0;
+ i < HASH_TBL_SIZE && (nentry < htbl->size); i++) {
+ p = htbl->buckets[i];
+ if (p) {
+ nentry++;
+ entry = (MaAdvConfigEntry *)p->data;
+ return (entry);
+ }
+ }
+ return (NULL);
+} /* getFirstInterface */
+
+
+/*
+ * Function: getClosestInterfaceAddr
+ *
+ * Arguments: ipaddr_t dest
+ *
+ * Description: This routine will return the nerest interface for reaching
+ * the dest. (For now, it will simply lookup the ip address
+ * of our first mobility interface.) Todo!
+ *
+ * Returns: ipaddr_t (0 on error)
+ */
+ipaddr_t
+/* LINTED E_FUNC_ARG_UNUSED */
+getClosestInterfaceAddr(ipaddr_t dest)
+{
+ MaAdvConfigEntry *entry;
+
+ /*
+ * for each mobility supporting interface ...
+ */
+
+ entry = getFirstInterface();
+ if (entry)
+ return (entry->maIfaceAddr);
+ else
+ return (0);
+} /* getClosestInterfaceAddr */
+
+/*
+ * Function: processIncomingMessage
+ *
+ * Arguments: htbl - Pointer to the Hash Table
+ * fdvec - Pointer to file descriptor
+ *
+ * Description: This function is called if data is pending
+ * on one of our sockets. This function loop
+ * through the interfaces looking for the socket
+ * on which data is pending.
+ *
+ * Returns:
+ */
+static void
+processIncomingMessage(HashTable *htbl, fd_set *fdvec)
+{
+ MaAdvConfigEntry *entry;
+ HashEntry *p;
+ int i, nentry;
+
+ /*
+ * for each mobility supporting interface ...
+ */
+ htbl = &maAdvConfigHash;
+
+ for (i = 0, nentry = 0;
+ i < HASH_TBL_SIZE && (nentry < htbl->size); i++) {
+ p = htbl->buckets[i];
+ while (p) {
+ nentry++;
+ entry = (MaAdvConfigEntry *)p->data;
+
+ /*
+ * process data received on ICMP socket
+ */
+ if (FD_ISSET(entry->maIfaceIcmpSock, fdvec)) {
+ recvNetworkPacket(entry->maIfaceIcmpSock,
+ entry, ON_BCAST_SOCK, PKT_ICMP);
+ }
+
+ if (FD_ISSET(entry->maIfaceAdvMulticastSock, fdvec)) {
+ recvNetworkPacket(
+ entry->maIfaceAdvMulticastSock,
+ entry, ON_MCAST_SOCK, PKT_ICMP);
+ }
+
+ /*
+ * ... process data on UDP socket bound to
+ * unicast address
+ */
+ if (FD_ISSET(entry->maIfaceUnicastSock, fdvec)) {
+ recvNetworkPacket(entry->maIfaceUnicastSock,
+ entry, ON_UNICAST_SOCK, PKT_UDP);
+ }
+
+ /*
+ * mipagent doesn't listen to broadcast sockets if
+ * this is a PPP interface.
+ */
+ if ((entry->maIfaceFlags & IFF_POINTOPOINT) == 0) {
+ /*
+ * ... process data on UDP socket bound to
+ * directed broadcast address
+ */
+ if (FD_ISSET(entry->maIfaceDirBcastSock,
+ fdvec)) {
+ recvNetworkPacket(
+ entry->maIfaceDirBcastSock,
+ entry, ON_BCAST_SOCK, PKT_UDP);
+ }
+
+ /*
+ * ... process data on UDP socket bound to
+ * broadcast address
+ */
+ if (FD_ISSET(entry->maIfaceBcastSock, fdvec)) {
+ recvNetworkPacket(
+ entry->maIfaceBcastSock,
+ entry, ON_BCAST_SOCK, PKT_UDP);
+ }
+ }
+
+ /*
+ * ... process data on UDP socket bound to
+ * multicast address
+ */
+ if (FD_ISSET(entry->maIfaceRegMulticastSock, fdvec)) {
+ recvNetworkPacket(
+ entry->maIfaceRegMulticastSock,
+ entry, ON_MCAST_SOCK, PKT_UDP);
+ }
+ p = p->next;
+ }
+ }
+}
+
+
+/*
+ * Function: doDynamicInterfaceProcess
+ * Argument:
+ * Description:
+ * This process is launched by dynamic interface thread. It's purpose
+ * is to hang around and poll to check if any new interface comes up.
+ * It calls process_rtsock_msg() for any valid RTM_INFO.
+ *
+ * Returns : NULL
+ */
+
+/* ARGSUSED */
+static void *
+doDynamicInterfaceProcess(void * arg)
+{
+ int s; /* Routing socket id */
+ int ret;
+ struct pollfd pollfds[1];
+
+ /* Open a socket to send ICOTL cmd down by rtsock_process_msg */
+ ioc_sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (ioc_sock < 0) {
+ syslog(LOG_CRIT, "Can't open IOC socket: %m");
+ return (NULL);
+ }
+ s = socket(PF_ROUTE, SOCK_RAW, AF_INET);
+ if (s == -1) {
+ syslog(LOG_CRIT,
+ "unable to open Routing socket for dynamic interface: %m");
+ (void) close(ioc_sock);
+ return (NULL);
+ }
+ ret = fcntl(s, F_SETFL, O_NDELAY|O_NONBLOCK);
+ if (ret < 0) {
+ syslog(LOG_CRIT,
+ "fcntl failed on routing socket: %m");
+ (void) close(ioc_sock);
+ (void) close(s);
+ return (NULL);
+ }
+
+ pollfds->fd = s;
+ pollfds->events = POLLIN;
+
+ for (;;) {
+ if (poll(pollfds, 1, INFTIM) < 0 ||
+ (pollfds->revents & POLLERR)) {
+ if (errno == EINTR)
+ continue;
+ syslog(LOG_CRIT,
+ "Poll failed: %m");
+ (void) close(s);
+ (void) close(ioc_sock);
+ return (NULL);
+ }
+ if (!pollfds->revents & POLLIN)
+ continue;
+ if (pollfds->fd == s) {
+ mipverbose(("doDynamicInterfaceProcess: "
+ "received message on RTsocket\n"));
+ process_rtsock_msg(s);
+ }
+ }
+ /* LINTED E_STMT_NOT_REACHED */
+ return (NULL); /* Never reached */
+}
+
+
+/*
+ * Function: process_rtsock_msg
+ * Argument : int
+ * Description :
+ * process_rtsock_msg processes the RTM_INFO messages. It checks
+ * if the flag is IFF_UP for the specified interface index. It also
+ * checks that this is IPV4, non-loopback, non-logical interface. It
+ * makes all other necessary checks before establishing the new
+ * interface as mobility interface. When an interface is plumbed it
+ * receives two RTM_IFINFO messages per interface.
+ */
+
+static void
+process_rtsock_msg(int rtsock)
+{
+
+#define RTSOCK_MAX_MSG 2048
+
+ int64_t msg[RTSOCK_MAX_MSG / sizeof (int64_t)];
+ int num;
+ struct lifreq lifr;
+ ipaddr_t addr;
+ struct sockaddr_in *sin;
+ DynamicIfaceTypeEntry *d_entry;
+ MaAdvConfigEntry *ifentry;
+ uint64_t ifflags;
+ time_t currentTime;
+
+ num = (int)read(rtsock, msg, sizeof (msg));
+ if (num <= 0) {
+ /* No messages */
+ return;
+ }
+ rt_msg = (struct rt_msghdr *)msg;
+ if (rt_msg->rtm_version != RTM_VERSION) {
+ syslog(LOG_CRIT, "Bad RTM version: %d", rt_msg->rtm_version);
+ return;
+ }
+
+ if (rt_msg->rtm_type != RTM_IFINFO)
+ return;
+
+ /* Now process the RTM_INFO message */
+ ifm = (struct if_msghdr *)rt_msg;
+ mipverbose(("process_rtsock_msg: RTM_IFINFO for if_index %d\n",
+ ifm->ifm_index));
+
+ (void) memset(&lifr, 0, sizeof (struct lifreq));
+ if (if_indextoname(ifm->ifm_index, lifr.lifr_name) == NULL) {
+ /*
+ * Interface unplumbed ?
+ * Make sure we delete any unused entry.
+ */
+ (void) delmaAdvConfigEntry(ifm->ifm_index);
+ return;
+ }
+ /* Found the ifname, check if it's new */
+
+ if (strchr(lifr.lifr_name, ':') != NULL) {
+ /* We don't support logical interfaces */
+ mipverbose(("process_rtsock_msg: logical interface %s\n",
+ lifr.lifr_name));
+ return;
+ }
+
+ /*
+ * Check if this entry belongs to any existing interfaces
+ * that were already configured at the time of mipagent
+ * startup. If any of those interfaces ifconfiged down and then
+ * ifconfig'ed up, we don't consider them as dynamic interface.
+ */
+ if (existingStaticInterface(lifr.lifr_name))
+ return;
+
+ /* Check for IPv4 and multicast flag */
+ if ((int)ioctl(ioc_sock, SIOCGLIFFLAGS, (char *)&lifr) < 0) {
+ /* Interface disappeared ? */
+ mipverbose(("process_rtsock_msg: SIOCGLIFFLAGS failed\n"));
+ return;
+ }
+ if (!(lifr.lifr_flags & IFF_IPV4) ||
+ !(lifr.lifr_flags & IFF_MULTICAST)) {
+ mipverbose(("process_rtsock_msg: flag not IPV4 MULTICAST\n"));
+ return;
+ }
+
+ ifflags = lifr.lifr_flags;
+
+ if (ioctl(ioc_sock, SIOCGLIFADDR, (caddr_t)&lifr) < 0) {
+ syslog(LOG_ERR, "Could not read IP address for"
+ "%s: %m", lifr.lifr_name);
+ return;
+ }
+ sin = (struct sockaddr_in *)&lifr.lifr_addr;
+ addr = sin->sin_addr.s_addr;
+
+ /* Now lookup for existing dynamic interface entries */
+ num = lookup_existing_entries(ifm->ifm_index, addr);
+ /*
+ * num = 1 means this entry is a static one.
+ * num = 0 means this entry is a dynamic existing entry
+ * We only care about dynamic entries here.
+ */
+ if (num == 1) {
+ mipverbose(("process_rtsock_msg: Uninteresting "
+ "static entry\n"));
+ return;
+ } else if (num == 0) {
+ if (!(ifflags & IFF_UP)) {
+ /* The entry is down ? Is it unplumbed ? */
+ (void) delmaAdvConfigEntry(ifm->ifm_index);
+ }
+ return;
+ }
+ if (!(ifflags & IFF_UP)) {
+ mipverbose(("process_rtsock_msg: Interface is down?\n"));
+ return;
+ }
+
+ if ((d_entry = match_dynamic_table(lifr.lifr_name)) != NULL) {
+ /*
+ * Create the dynamic interface entry into the
+ * ConfigHash table
+ */
+
+ if (CreateInterfaceEntry(lifr.lifr_name,
+ d_entry->RegLifetime, d_entry->advertiseOnBcast,
+ DEFAULT_MIN_INTERVAL, DEFAULT_MAX_INTERVAL,
+ d_entry->AdvLifetime, 0, d_entry->AdvServiceflag,
+ d_entry->AdvPrefixflag, d_entry->RevtunAllowed,
+ d_entry->RevtunReqd, d_entry->AdvLimitUnsolicited,
+ d_entry->AdvInitCount, d_entry->AdvInterval,
+ _B_TRUE) != 0) {
+ syslog(LOG_ERR,
+ "Unable to create dynamic Interface: %m");
+ return;
+ }
+ /* Find my entry */
+
+ if ((ifentry = (MaAdvConfigEntry *)findHashTableEntryUint(
+ &maAdvConfigHash, addr, LOCK_WRITE,
+ ConfigEntryHashLookup, (uint32_t)ifm->ifm_index, 0,
+ 0)) == NULL) {
+ syslog(LOG_CRIT,
+ "Can't find dynamic entry in Hash table");
+ mipverbose(("process_rtsock_msg: Can't find dynamic "
+ "entry in Hash table\n"));
+ return;
+ }
+ if (InitSockets(ifentry) != 0) {
+ syslog(LOG_CRIT, "InitSockets failed for dynamic"
+ " Interface %s", ifentry->maIfaceName);
+ (void) rw_unlock(&ifentry->maIfaceNodeLock);
+ return;
+ }
+ /* Now set the FDs for advertisements */
+ GET_TIME(currentTime);
+ ifentry->maNextAdvTime = currentTime;
+ FD_SET(ifentry->maIfaceUnicastSock, &saved_fdvec);
+ if (!(ifflags & IFF_POINTOPOINT)) {
+ FD_SET(ifentry->maIfaceDirBcastSock, &saved_fdvec);
+ FD_SET(ifentry->maIfaceBcastSock, &saved_fdvec);
+ }
+ FD_SET(ifentry->maIfaceAdvMulticastSock, &saved_fdvec);
+ FD_SET(ifentry->maIfaceRegMulticastSock, &saved_fdvec);
+ FD_SET(ifentry->maIfaceIcmpSock, &saved_fdvec);
+ (void) rw_unlock(&ifentry->maIfaceNodeLock);
+ return;
+ }
+ mipverbose(("process_rtsock_msg: entry %s not found\n",
+ lifr.lifr_name));
+}
+
+
+/*
+ * Function: Lookup_existing_entries
+ * Description: Looks for any existing entry in config table.
+ * returns : 1 if finds a static entry
+ * 0 if finds a dynamic entry
+ * -1 if none found
+ */
+
+static int
+lookup_existing_entries(ushort_t index, ipaddr_t ifaddr)
+{
+ MaAdvConfigEntry * ifentry;
+
+ ifentry = (MaAdvConfigEntry *)findHashTableEntryUint(
+ &maAdvConfigHash, ifaddr, LOCK_READ, ConfigEntryHashLookup,
+ (uint32_t)index, 0, 0);
+
+ if (ifentry == NULL) {
+ mipverbose(("lookup_existing_entries: "
+ "Can't find entry in Hash table for index %d\n",
+ index));
+ return (-1);
+ }
+ /* found an existing entry */
+ if (ifentry->maAdvDynamicInterface == _B_TRUE) {
+ (void) rw_unlock(&ifentry->maIfaceNodeLock);
+ return (0);
+ } else {
+ (void) rw_unlock(&ifentry->maIfaceNodeLock);
+ return (1);
+ }
+}
+
+
+/*
+ * Function: match_dynamic_table
+ * Argument : interface name
+ *
+ * Description:
+ * Match the dynamic interface table to see if this interface
+ * is valid to become a dynamic mobility interface.
+ * dynamicIface list is handled by a single thread, thus
+ * we are not locking here.
+ * returns : DynamicIfceTypeEntry pointer
+ */
+
+static DynamicIfaceTypeEntry *
+match_dynamic_table(char *ifname)
+{
+ DynamicIfaceTypeEntry *dynentry;
+ int typelen;
+
+
+ dynentry = dynamicIfaceHead;
+ while (dynentry != NULL) {
+ typelen = strlen(dynentry->dynamicIfcetype);
+ if ((strncmp(ifname, dynentry->dynamicIfcetype,
+ (size_t)typelen) == 0) &&
+ ((int)isdigit(ifname[typelen]) != 0)) {
+ /* Matches devicename with dynamicIfacetype */
+ mipverbose(("match_dynamic_table: matched entry\n"));
+ return (dynentry);
+ }
+ dynentry = dynentry->next;
+ }
+
+ return (NULL);
+}
+
+
+/*
+ * Function: delmaAdvConfigEntry
+ * Argument: Interface index
+ * Description: It can only take index, as there may be situation when
+ * it's too late to find name/addr info because the interface
+ * is gone (ENXIO error). So, in those cases we have no other
+ * info in hand and thus we have to go through the whole
+ * table and delete the entry.
+ */
+static void
+delmaAdvConfigEntry(uint32_t index)
+{
+
+ MaAdvConfigEntry *ifentry;
+ struct hash_entry *hash_entry;
+ struct hash_table *conf_htbl;
+ int count;
+ boolean_t found = _B_FALSE;
+ ipaddr_t addr;
+
+
+ conf_htbl = &maAdvConfigHash;
+
+ /*
+ * The following search does not provide high performance
+ * result. The confighash table needs to be hashed with key
+ * <interface-index>. Currently all hashtables are hashed by
+ * address. But when delmaAdvConfigEntry() is called from
+ * process_rtsock_msg() routine, interface might be unplumbed
+ * already and thus the addr cannot be known. So we have to do
+ * a long search to compare the interface index for each entry.
+ * There could be two solutions in future to address this:
+ * 1. hash configHash table with interface index
+ * 2. Or modify routing socket to return addr and flag etc with
+ * the routing socket message.
+ */
+
+ for (count = 0; count < HASH_TBL_SIZE && !found; count++) {
+ hash_entry = conf_htbl->buckets[count];
+ while (hash_entry != NULL) {
+ if (ConfigEntryHashLookup(hash_entry->data,
+ index, 0, 0)) {
+ (void) rw_wrlock((rwlock_t *)hash_entry->data);
+ found = _B_TRUE;
+ break;
+ }
+ hash_entry = hash_entry->next;
+ }
+ }
+ if (found) {
+ ifentry = hash_entry->data;
+ addr = ifentry->maIfaceAddr;
+ } else {
+ mipverbose(("delmaAdvConfigEntry: "
+ "Can't find dynamic entry in Hash table\n"));
+ return;
+ }
+ /* close all sockets */
+ FD_CLR(ifentry->maIfaceUnicastSock, &saved_fdvec);
+ FD_CLR(ifentry->maIfaceAdvMulticastSock, &saved_fdvec);
+ FD_CLR(ifentry->maIfaceRegMulticastSock, &saved_fdvec);
+ FD_CLR(ifentry->maIfaceIcmpSock, &saved_fdvec);
+ (void) close(ifentry->maIfaceUnicastSock);
+ if (ifentry->maIfaceDirBcastSock > -1) {
+ FD_CLR(ifentry->maIfaceDirBcastSock, &saved_fdvec);
+ (void) close(ifentry->maIfaceDirBcastSock);
+ }
+ if (ifentry->maIfaceBcastSock > -1) {
+ FD_CLR(ifentry->maIfaceBcastSock, &saved_fdvec);
+ (void) close(ifentry->maIfaceBcastSock);
+ }
+ (void) close(ifentry->maIfaceAdvMulticastSock);
+ (void) close(ifentry->maIfaceRegMulticastSock);
+ (void) close(ifentry->maIfaceIcmpSock);
+
+ if (delHashTableEntryUint(&maAdvConfigHash, ifentry, addr,
+ LOCK_NONE)) {
+ /* Success: Entry has been taken out of the table */
+ (void) rw_unlock(&(ifentry->maIfaceNodeLock));
+ (void) rwlock_destroy(&(ifentry->maIfaceNodeLock));
+ free(ifentry);
+ mipverbose(("delmaAdvConfigEntry: deleted config entry\n"));
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mipagent/agentPeriodic.c b/usr/src/cmd/cmd-inet/usr.lib/mipagent/agentPeriodic.c
new file mode 100644
index 0000000000..7efe4421b0
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mipagent/agentPeriodic.c
@@ -0,0 +1,1190 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1999-2002 Sun Microsystems, Inc.
+ * All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * file: agentPeriodic.c
+ *
+ * This file includes the routines that are periodically called
+ * for agent advertisement and garbage collection of data structures
+ * such as visitor entries, mobile node entries, security
+ * assocations, etc.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <assert.h>
+
+#include "mip.h"
+#include "agent.h"
+#include "pool.h"
+#include "agentKernelIntfce.h"
+#include "conflib.h"
+#include "mipagentstat_door.h"
+
+static pthread_t periodicThreadId = 0;
+/* global variables declared here */
+boolean_t faBusy = _B_FALSE;
+/*
+ * if need to disable mobility service, set
+ * advBusy so that agent advertisement B(usy)
+ * Bit is set. This informs MNs not to make
+ * a registration request per RFC2002.
+ */
+boolean_t advBusy = _B_FALSE;
+
+extern boolean_t faNAIadv; /* Determines whether we advertise */
+ /* our NAI */
+extern boolean_t faChallengeAdv; /* Determines whether we advertise */
+ /* challenges */
+
+/*
+ * Common to all mobility agents
+ */
+extern struct hash_table maAdvConfigHash;
+
+/* Counters common to all Mobility Agents */
+extern CommonCounters commonCounters;
+
+extern char maNai[MAX_NAI_LENGTH];
+
+/* Foreign Agent specific data structures. */
+
+extern struct hash_table faVisitorHash;
+
+/* Counters maintained by Foreign Agents */
+extern ForeignAgentCounters faCounters;
+
+/* The last two challenges advertised */
+extern char faLastChallengeIssued[2][ADV_CHALLENGE_LENGTH];
+
+/* Home Agent specific data structures. */
+extern struct hash_table haMobileNodeHash;
+
+/*
+ * This table has one entry for each known Mobility Agent
+ */
+extern struct hash_table mipAgentHash;
+
+/*
+ * This table stores all of the Security Associations
+ */
+extern HashTable mipSecAssocHash;
+
+#ifdef FIREWALL_SUPPORT
+extern DomainInfo domainInfo;
+#endif /* FIREWALL_SUPPORT */
+
+/* Other external declarations */
+extern int logVerbosity;
+extern int advLifetime;
+extern int periodicInterval;
+extern AAA_Protocol_Code aaaProtocol;
+extern unsigned short inChecksum(unsigned short *, int);
+extern char *ntoa(uint32_t, char *);
+extern void sendICMPmessage(int, ipaddr_t, unsigned char *, int);
+extern char *err2str(int);
+#ifdef FIREWALL_SUPPORT
+extern boolean_t isOutsideProtectedDomain(uint32_t);
+#endif /* FIREWALL_SUPPORT */
+extern uint32_t getRandomValue();
+extern int prefixLen(uint32_t);
+extern int arpIfdel(ipaddr_t, char *, uint32_t);
+extern int gettunnelno(ipaddr_t, ipaddr_t);
+extern MobilityAgentEntry *findMaeFromIp(ipaddr_t, int);
+extern int removeIPsecPolicy(char *);
+
+#define LOWEST_ROUTER_PRIORITY 0x80000000
+
+static void doPeriodicTask();
+void maSendAdvertisement(MaAdvConfigEntry *, ipaddr_t, int, boolean_t faBusy);
+
+/*
+ * Function: disableService
+ *
+ * Arguments:
+ *
+ *
+ * Description: Function will set a flag to indicate
+ * that all agent advertisements on all
+ * interfaces will have the B(usy) Bit
+ * set. In addition, the Agent Advertisement
+ * sequence number for all interfaces will
+ * be set to zero. This function is called
+ * to disable mobility service while keeping
+ * the mobility agent up and running. NOTE: A
+ * corresponding function enableService()
+ * is called to reenable mobility service.
+ *
+ * Returns:
+ */
+void
+disableService(struct hash_table *htbl)
+{
+ int index, nentry;
+ MaAdvConfigEntry *entry;
+ struct hash_entry *p;
+
+ advBusy = _B_TRUE;
+ for (index = 0, nentry = 0;
+ index < HASH_TBL_SIZE && (nentry < htbl->size); index++) {
+ p = htbl->buckets[index];
+ while (p) {
+ nentry++;
+ entry = (MaAdvConfigEntry *)p->data;
+ entry->maAdvSeqNum = 0;
+ p = p->next;
+ }
+ }
+}
+
+/*
+ * Function: enableService
+ *
+ * Arguments:
+ *
+ * Description: Function will set a flag to indicate
+ * that all agent advertisements on all
+ * interfaces should not have the
+ * B(usy) bit set.
+ *
+ * Returns:
+ */
+void
+enableService()
+{
+ advBusy = _B_FALSE;
+}
+
+/*
+ * Function: maSendAdvertisement
+ *
+ * Arguments: entry - Pointer to MaAdvConfigEntry
+ * dst - Destination addr of the advertisement
+ * AdvType - Type of advertisement -solicited or
+ * unsolicited
+ * faBusy - boolean dictating whether we are
+ * busy.
+ *
+ * Description: Prepare and send agent advertisement on
+ * specified interface.
+ *
+ * Returns:
+ */
+void
+maSendAdvertisement(MaAdvConfigEntry *entry, ipaddr_t dst, int advType,
+ boolean_t faBusy)
+{
+ int j;
+ static unsigned char advPkt[512];
+ int naiLength;
+ uint32_t randomValue;
+ unsigned char *randomValuePtr;
+ int len;
+ icmph *icmpPtr;
+ ipaddr_t *addrPtr;
+ advExt *advExtPtr;
+ unsigned char *cp;
+
+
+ /* We don't do unsolicited adv if AdvInitCount = 0 */
+ if (entry->maAdvInitCount == 0 && advType == UNSOLICITED_ADV) {
+ mipverbose(("maSendAdvertisement: zero InitCount\n"));
+ return;
+ }
+
+
+ /* Fill out the ICMP header for a router advertisement */
+
+ /* LINTED BAD_PTR_CAST_ALIGN */
+ icmpPtr = (icmph *) advPkt;
+ icmpPtr->type = ICMP_ROUTERADVERT;
+ icmpPtr->code = 16;
+ icmpPtr->icmpAdvNumAddr = 0;
+ icmpPtr->icmpAdvAddrEntrySize = 2;
+ icmpPtr->icmpAdvLifetime = htons((uint16_t)advLifetime);
+
+ /* create advertisement ... */
+ len = sizeof (icmph);
+ /* LINTED BAD_PTR_CAST_ALIGN */
+ addrPtr = (uint32_t *)(advPkt + sizeof (icmph));
+
+ /*
+ * ... fill (optional) addresses in the RFC 1256 portion
+ * we only fill out one address.
+ */
+ icmpPtr->icmpAdvNumAddr = 1;
+ icmpPtr->icmpAdvAddrEntrySize = 2;
+ *addrPtr++ = htonl(entry->maIfaceAddr);
+ len += sizeof (uint32_t);
+ *addrPtr++ = htonl(LOWEST_ROUTER_PRIORITY);
+ len += sizeof (uint32_t);
+
+ /*
+ * ... fill out the mobility agent advertisement extension
+ * we only advertise one address
+ */
+ /* LINTED BAD_PTR_CAST_ALIGN */
+ advExtPtr = (advExt *) (advPkt + len);
+ advExtPtr->type = ADV_EXT_TYPE;
+ advExtPtr->length = 10;
+ advExtPtr->seqNum = htons(entry->maAdvSeqNum);
+
+ /*
+ * We set the advertisement sequence number. Note that this
+ * value wraps around at 255. This is used by Mobile Nodes
+ * to determine whether the Foreign Agent has rebooted.
+ */
+ if (++entry->maAdvSeqNum == 0)
+ entry->maAdvSeqNum = 256;
+ advExtPtr->regLifetime = htons(entry->maAdvMaxRegLifetime);
+ advExtPtr->advFlags = entry->maAdvServiceFlags;
+
+ /* set the busy bit, if appropriate */
+ if ((advBusy) || (faBusy && (advExtPtr->advFlags &
+ ADV_IS_FOREIGN_AGENT))) {
+ faCounters.faIsBusyCnt++;
+ advExtPtr->advFlags |= ADV_IS_BUSY;
+ }
+
+ advExtPtr->reserved = 0;
+ len += sizeof (advExt);
+
+ /* fill out the advertised care-of address */
+ /* LINTED BAD_PTR_CAST_ALIGN */
+ addrPtr = (uint32_t *)(advPkt + len);
+ *addrPtr++ = htonl(entry->maIfaceAddr);
+ len += sizeof (uint32_t);
+
+ /*
+ * We only advertise our NAI if we were
+ * configured to do so.
+ */
+ if (faNAIadv == _B_TRUE) {
+ /* Add the Mobility Agent NAI */
+ naiLength = strlen(maNai);
+ cp = advPkt + len;
+ *cp++ = ADV_AGENT_NAI_EXT_TYPE;
+ *cp++ = (uchar_t)naiLength;
+ len += 2;
+ (void) strncpy((char *)cp, maNai, naiLength);
+ len += naiLength;
+ }
+
+ /*
+ * We only advertise challenges
+ * if we were configured to do so.
+ */
+ if (faChallengeAdv == _B_TRUE) {
+ if (advType == UNSOLICITED_ADV) {
+
+ /* Add the Foreign Agent Challenge Value */
+ cp = advPkt + len;
+ *cp++ = ADV_CHALLENGE_EXT_TYPE;
+ *cp++ = ADV_CHALLENGE_LENGTH;
+ len += 2;
+
+ /*
+ * Save the last Challenge issued
+ */
+ (void) memcpy(faLastChallengeIssued[1],
+ faLastChallengeIssued[0],
+ ADV_CHALLENGE_LENGTH);
+
+ randomValuePtr = cp;
+
+ /* We want a 16 octet (128 bit) challenge */
+ for (j = 0; j < ADV_CHALLENGE_LENGTH; j += 4) {
+ randomValue = getRandomValue();
+ (void) strncpy((char *)cp, (char *)&randomValue,
+ sizeof (randomValue));
+ len += sizeof (randomValue);
+ cp += sizeof (randomValue);
+ }
+
+ /* We want a 16 octet (128 bit) challenge */
+ /*
+ * Save the New Challenge issued
+ */
+ (void) memcpy(faLastChallengeIssued[0], randomValuePtr,
+ ADV_CHALLENGE_LENGTH);
+ } else if (advType == SOLICITED_ADV) {
+
+ /* Add the Foreign Agent Challenge Value */
+ cp = advPkt + len;
+ *cp++ = ADV_CHALLENGE_EXT_TYPE;
+ *cp++ = ADV_CHALLENGE_LENGTH;
+ len += 2;
+ /* We use the last advertised challenge */
+ (void) memcpy(cp, faLastChallengeIssued[0],
+ ADV_CHALLENGE_LENGTH);
+ len += ADV_CHALLENGE_LENGTH;
+ cp += ADV_CHALLENGE_LENGTH;
+ }
+ }
+
+ /* fill out prefix length extension, if required */
+ if ((entry->maAdvPrefixLenInclusion == _B_TRUE) &&
+ icmpPtr->icmpAdvNumAddr) {
+ cp = advPkt + len;
+ *cp++ = ADV_PREFIX_EXT_TYPE;
+ *cp++ = icmpPtr->icmpAdvNumAddr;
+ len += 2;
+ /* fill out prefix len for each addr in RFC1256 portion */
+ for (j = 0; j < icmpPtr->icmpAdvNumAddr; j++) {
+ *cp++ = prefixLen(entry->maIfaceNetmask);
+ len++;
+ }
+ }
+
+ /* pad ICMP message to even length, if required */
+ if (len % 2) {
+ *cp++ = ADV_PADDING_EXT_TYPE;
+ len++;
+ }
+
+ /* fill out ICMP checksum */
+ icmpPtr->checksum = 0;
+ icmpPtr->checksum = inChecksum((unsigned short *) icmpPtr, len);
+
+ /* ship it! */
+ commonCounters.maAdvSentCnt++;
+
+ /*
+ * IP_XMIT_IF or IP_MULTICAST_IF are set on this socket
+ * during initiation time in InitSockets().
+ */
+ sendICMPmessage(entry->maIfaceIcmpSock, dst, advPkt, len);
+
+
+ /*
+ * Now update maAdvInitCount for limited unsolicited advertisement
+ * case. Note, we need to update this value to make sure that
+ * we don't send unsolicited advertisements beyond maAdvInitCount
+ * value specified in the mipagent.conf file.
+ * I am updating this value, otherwise we need to keep another
+ * data structure for each dynamic interface, which can grow large
+ * with the number of interfaces. Besides, there is no reason I find
+ * at present, to preserve it's initial value.
+ */
+ if (entry->maAdvLimitUnsolicited && entry->maAdvInitCount > 0) {
+ /* update maAdvInitCount */
+ entry->maAdvInitCount--;
+ mipverbose(("maSendAdvertisement: updating maAdvInitCount "
+ " to %d\n", entry->maAdvInitCount));
+ }
+
+ /*
+ * Note that the mipverbose format is different here.
+ * We want to just print the interface index with each
+ * advertisement.
+ */
+ mipverbose(("+[%d]", entry->maIfindex));
+}
+
+
+/*
+ * Function: delHABEent
+ *
+ * Arguments: mnePtr - Pointer to a Mobile Node Entry
+ * entry - Pointer to a Binding Entry
+ *
+ * Description: This function will delete a binding entry.
+ * If a Home Address was assigned to the Mobile
+ * Node via a local pool, and the number of
+ * bindings is set to zero (0), we will free
+ * the Home Address.
+ *
+ * Note that the caller MUST have locked the
+ * Mobile Node Entry prior to calling this
+ * function.
+ *
+ * Returns:
+ */
+void
+delHABEent(HaMobileNodeEntry *mnePtr, HaBindingEntry *entry)
+{
+ ipaddr_t mnAddr, coAddr;
+ char addrstr1[INET_ADDRSTRLEN];
+ char addrstr2[INET_ADDRSTRLEN];
+ int val;
+ time_t localTime;
+ time_t sessionLifeTime;
+ int result;
+
+ mnAddr = entry->haBindingMN;
+ coAddr = entry->haBindingCOA;
+
+ /*
+ * Disable encapsulation to this coaddr. Decrease
+ * binding count for MN. If zero, cancel proxy ARP & route
+ */
+
+#ifdef FIREWALL_SUPPORT
+ /*
+ * If the care-of-address was outside protected domain remove
+ * the encapsulation through the firewall's internal address.
+ * TODO: This works only if we assume that a single external
+ * COAddr is never shared by multiple mobile nodes. For now,
+ * that's always the case.
+ */
+ if (isOutsideProtectedDomain(coAddr)) {
+ mipverbose(("Removing tunnel for %s through FW %s\n",
+ ntoa(coAddr, addrstr1),
+ ntoa(domainInfo.fwAddr[0], addrstr2)));
+ if ((val = encaprem(coAddr)) < 0) {
+ syslog(LOG_ERR, "encaprem failed ... %s", err2str(val));
+ }
+ }
+#endif /* FIREWALL_SUPPORT */
+
+ /*
+ * Terminate encapsulation and decapsulation of MN's
+ * packets to COA. For now, this also handles reverse
+ * tunneling
+ */
+ mipverbose(("Removing tunnel for %s through %s\n",
+ ntoa(mnAddr, addrstr1), ntoa(coAddr, addrstr2)));
+
+ if ((val = encaprem(mnAddr)) < 0) {
+ syslog(LOG_ERR, "encaprem failed ... %s", err2str(val));
+ }
+
+ /* if encaprem() returned 0, there are no more MNs using this tunnel */
+ if (val == 0) {
+ /* kill off any ipsec SA we have with this agent-peer */
+ MobilityAgentEntry *mae;
+
+ /* note: this should work for colocated MNs too! */
+ if ((mae = findMaeFromIp(coAddr, LOCK_READ)) != NULL) {
+ /*
+ * We're the HA, so we look for IPSEC_REPLY_APPLY to
+ * remove. NOTE: we NEVER remove IPSEC_REQUEST_PERMIT,
+ * (unless shutting down), as these come unannounced.
+ * See agentInit.c: deleteIPsecSAHashHelper()
+ */
+ if (mae->maIPsecFlags & IPSEC_REPLY_APPLY) {
+ /* off it */
+ if (removeIPsecPolicy(
+ mae->maIPsecReply[IPSEC_APPLY]) < 0) {
+ /* whine about it */
+ char peerAddr[IPv4_ADDR_LEN];
+
+ (void) ntoa(coAddr, peerAddr);
+ syslog(LOG_CRIT, "Could not remove %s"
+ "ipsec regisration reply apply.",
+ peerAddr);
+ } else
+ /* it's gone, unset the flag bit */
+ mae->maIPsecFlags &=
+ ~IPSEC_REPLY_APPLY;
+ }
+
+ /* The tunnels are down, so just unset those bits */
+ mae->maIPsecFlags &= ~IPSEC_TUNNEL_APPLY;
+ mae->maIPsecFlags &= ~IPSEC_REVERSE_TUNNEL_PERMIT;
+
+ /* more importantly, this is no longer an agent peer */
+ mae->maPeerFlags &= ~FA_PEER;
+
+ /* unlock */
+ (void) rw_unlock(&mae->maNodeLock);
+ }
+ }
+
+ if (--mnePtr->haMnBindingCnt == 0) {
+
+ /*
+ * Notify AAA server that the MN is going away.
+ */
+ if ((aaaProtocol == RADIUS || aaaProtocol == DIAMETER)) {
+ GET_TIME(localTime);
+ sessionLifeTime = localTime -
+ entry->haBindingTimeGranted;
+ result = sendAccountingRecord(
+ MOBILE_IP_ACCOUNTING_STOP_REQUEST,
+ (unsigned char *)mnePtr->haMnNAI,
+ mnePtr->haMnNAILen, mnAddr, coAddr,
+ entry->haBindingHaAddr,
+ sessionLifeTime, MN_DEREGISTERED);
+ if (result) {
+ syslog(LOG_ERR, "Unable to send accounting "
+ "stop record");
+ }
+ }
+
+ mipverbose(("Terminating arp service for %s\n",
+ ntoa(mnAddr, addrstr1)));
+
+ /*
+ * TODO:
+ * Solaris gets confused if it has two ARP entries
+ * for the same host on different interfaces. This
+ * needs further looking into(at least it tries to
+ * keep a separate ARP table for each interface).
+ */
+ mipverbose(("Removing proxy ARP entry for %s\n",
+ ntoa(mnAddr, addrstr1)));
+ if ((val = arpdel(mnAddr)) < 0) {
+ syslog(LOG_ERR, "arpdel failed ... %s", err2str(val));
+ }
+
+ /*
+ * Do we have an assigned Home Address that we need
+ * to free?
+ */
+ if (mnePtr->haPoolIdentifier && mnePtr->haMnAddr) {
+ if (freeAddressFromPool(mnePtr->haPoolIdentifier,
+ mnePtr->haMnAddr) != _B_TRUE) {
+ syslog(LOG_ERR,
+ "Unable to free address from pool %d",
+ mnePtr->haPoolIdentifier);
+ }
+ mnePtr->haMnAddr = 0;
+ }
+ }
+
+ mipverbose((
+ "HA binding removed for %s@%s at entry %p (Binding Cnt %d).\n",
+ ntoa(mnAddr, addrstr1), ntoa(coAddr, addrstr2), (void *)entry,
+ mnePtr->haMnBindingCnt));
+}
+
+
+/*
+ * Function: delFAVEptr
+ *
+ * Arguments: entry - Foreign Agent Visitor Entry
+ * sendASR - whether to send Accounting Stop Request
+ *
+ * Description: This function will delete a Foreign Agent's
+ * Visitor entry. If the visitor entry was in
+ * the ACCEPTED state (meaning that service was
+ * provided to the Mobile Node), we must remove
+ * the route, delete the tunnel interface and
+ * the MN's ARP entry.
+ *
+ * Note that the caller MUST have locked the
+ * Mobile Node Entry prior to calling this
+ * function.
+ *
+ * Returns:
+ */
+void
+delFAVEptr(FaVisitorEntry *entry, boolean_t sendASR, uint32_t releaseindicator)
+{
+ int val;
+ int tun_num;
+ int in_index;
+ char tun_name[LIFNAMSIZ];
+ char addrstr1[INET_ADDRSTRLEN];
+ char addrstr2[INET_ADDRSTRLEN];
+ int result;
+ time_t localTime;
+ time_t sessionLifeTime;
+
+
+#if 0
+ /*
+ * Of course, this causes us a heart ache if we are called from
+ * an interrupt handler, since we will not be locking
+ */
+
+ /*
+ * Let's just make sure that the caller did indeed
+ * lock the visitor entry...
+ */
+ if (rw_trywrlock(&entry->nodeLock) == 0) {
+ assert(0);
+ }
+
+#endif
+
+ mipverbose(("FA expired %s visitor entry for %s\n",
+ (entry->faVisitorRegIsAccepted ?
+ "accepted" : "pending"),
+ ntoa(entry->faVisitorHomeAddr, addrstr1)));
+
+ /* For accepted requests, kill route and disable decapsulation. */
+
+ if (entry->faVisitorRegIsAccepted == _B_TRUE) {
+
+ tun_num = gettunnelno(entry->faVisitorHomeAgentAddr,
+ entry->faVisitorIfaceAddr);
+ if (tun_num < 0) {
+ syslog(LOG_ERR, "gettunnelno returns -1");
+ return;
+ }
+ (void) snprintf(tun_name, sizeof (tun_name), "ip.tun%d",
+ tun_num);
+ in_index = if_nametoindex(tun_name);
+ if (in_index == 0) {
+ /* if_nametoindex fails... */
+ syslog(LOG_ERR, "if_nametoindex failed for tunnel %s",
+ tun_name);
+ }
+ mipverbose((
+ "Removing direct, local route for visitor %s thru %s\n",
+ ntoa(entry->faVisitorHomeAddr, addrstr1),
+ ntoa(entry->faVisitorIfaceAddr, addrstr2)));
+
+ if ((val = routedel(entry->faVisitorHomeAddr,
+ entry->faVisitorIfaceAddr, 0, in_index,
+ entry->faVisitorInIfindex)) < 0) {
+ syslog(LOG_ERR,
+ "routedel failed ...for visitor %s: %s\n",
+ ntoa(entry->faVisitorHomeAddr, addrstr1),
+ err2str(val));
+ }
+
+ if (entry->faVisitorRegFlags & REG_REVERSE_TUNNEL) {
+
+ /* delete Reverse Tunnel route */
+ mipverbose((
+ "Removing Reverse tunnel route for visitor %s at"
+ " interface index %d\n",
+ ntoa(entry->faVisitorHomeAddr, addrstr2),
+ entry->faVisitorInIfindex));
+
+ if ((val = routedel(0, 0,
+ entry->faVisitorHomeAddr,
+ entry->faVisitorInIfindex,
+ in_index)) < 0) {
+ syslog(LOG_ERR,
+ "Reverse Tunnel route delete failed: %s:"
+ " for visitor %s at interface index %d",
+ err2str(val),
+ ntoa(entry->faVisitorHomeAddr, addrstr1),
+ entry->faVisitorInIfindex);
+ }
+ }
+ /*
+ * Notify AAA server that the MN is going away.
+ * In the case we are here because of a Close Session Request
+ * received from AAA, AAA already knows it should stop
+ * accounting.
+ */
+ if ((aaaProtocol == RADIUS || aaaProtocol == DIAMETER) &&
+ (sendASR == _B_TRUE)) {
+ GET_TIME(localTime);
+ sessionLifeTime = localTime -
+ entry->faVisitorTimeGranted;
+ result = sendAccountingRecord(
+ MOBILE_IP_ACCOUNTING_STOP_REQUEST,
+ (unsigned char *)entry->faVisitorMnNAI,
+ entry->faVisitorMnNAILen,
+ entry->faVisitorHomeAddr,
+ entry->faVisitorCOAddr,
+ entry->faVisitorHomeAgentAddr,
+ sessionLifeTime, releaseindicator);
+ if (result) {
+ syslog(LOG_ERR, "Unable to send accounting "
+ "stop record");
+ }
+ }
+ /*
+ * TODO: this behavior is incorrect if multiple coaddrs can be
+ * registered for a mobile node at a FA. Decaprem should occur
+ * ONLY after a mobile node has NO accepted registrations.
+ */
+ mipverbose((
+ "Disabling decapsulation of inner pkts sent to %s\n",
+ ntoa(entry->faVisitorHomeAddr, addrstr1)));
+ if ((val = decaprem(entry->faVisitorHomeAgentAddr,
+ entry->faVisitorIfaceAddr)) < 0) {
+ syslog(LOG_ERR, "decaprem failed: %s", err2str(val));
+ }
+
+ /* if decaprem() returns 0 there are no MNs using the tunnel */
+ if (val == 0) {
+ /* kill any tunnel ipsec SAs we have with this peer */
+ MobilityAgentEntry *mae;
+
+ if ((mae =
+ findMaeFromIp(entry->faVisitorHomeAgentAddr,
+ LOCK_READ)) != NULL) {
+ /*
+ * We're the FA, so we look for
+ * IPSEC_REPLY_PERMIT, and
+ * IPSEC_REQUEST_APPLY policies.
+ */
+ if (mae->maIPsecFlags & IPSEC_REPLY_PERMIT) {
+ /* off it */
+ if (removeIPsecPolicy(
+ mae->maIPsecReply[IPSEC_PERMIT])
+ < 0) {
+ /* whine about it */
+ char peerAddr[IPv4_ADDR_LEN];
+ (void) ntoa(entry->
+ faVisitorHomeAgentAddr,
+ peerAddr);
+ syslog(LOG_CRIT,
+ "Could not remove %s's "
+ "ipsec reply permit.",
+ peerAddr);
+ } else
+ /* unset the flag bit */
+ mae->maIPsecFlags &=
+ ~IPSEC_REPLY_PERMIT;
+ }
+
+ /*
+ * The tunnel policy itself is 'freed'
+ * when the tunnel is unplumbed, but
+ * we need to unset the flag bits.
+ *
+ * We don't care if *this* MN's got a reverse
+ * tunnel, but if *any* happenned to get one.
+ */
+ mae->maIPsecFlags &= ~IPSEC_TUNNEL_PERMIT;
+ mae->maIPsecFlags &=
+ ~IPSEC_REVERSE_TUNNEL_APPLY;
+
+ /* this is no longer an agent peer */
+ mae->maPeerFlags &= ~HA_PEER;
+
+ /* unlock */
+ (void) rw_unlock(&mae->maNodeLock);
+ }
+ }
+
+ /*
+ * If the entry is accepted we should have set up an ARP
+ * cache entry (marked with the permanent flag). Since this
+ * is the common entry point to deleting the FAVE, we delete
+ * the ARP entry here too. The other deletions are done in
+ * the registration request and reply processing code on the
+ * FA side. The ARP cache entry was set up to prevent the FA
+ * from broadcast ARPing. PVTODO: May need some special
+ * handling for 2 hosts with same addr (private overlapping
+ * address) on the same interface index, eg: specify link
+ * layer address here too. May need to modify ARP to delete
+ * based on ether addr too.
+ */
+ if (entry->faVisitorIsSllaValid &&
+ ((val = arpIfdel(entry->faVisitorHomeAddr,
+ entry->faVisitorSlla.sdl_data,
+ entry->faVisitorInIfindex)) < 0)) {
+ /*
+ * If deletion failed bcos there was'nt an entry
+ * mipagent need not report it
+ */
+ if (val != (-1)*ENXIO)
+ syslog(LOG_ERR, "arpIfdel failed ...%s\n",
+ err2str(val));
+ }
+ }
+}
+
+
+/*
+ * Function: haAgeBindingsHashHelper
+ *
+ * Arguments: entry - Pointer to Mobile Node Entry
+ * p1 - First parameter to match (current time)
+ *
+ * Description: This function is used as the Hash Table Helper routine
+ * for haAgeBinding() when looking for binding entries
+ * in the Hash Table that have expired, and will be
+ * called by getAllHashTableEntries().
+ *
+ * Returns: _B_TRUE if the entry matches the desired criteria,
+ * otherwise _B_FALSE.
+ */
+static boolean_t
+haAgeBindingsHashHelper(void *entry, uint32_t p1)
+{
+ HaMobileNodeEntry *hentry = entry;
+ HaBindingEntry *bindingEntry;
+ HaBindingEntry *prev_entry = NULL;
+ HaBindingEntry *next_entry;
+
+ bindingEntry = hentry->bindingEntries;
+ while (bindingEntry) {
+ next_entry = bindingEntry->next;
+ if (bindingEntry->haBindingTimeExpires > p1) {
+ /*
+ * We want to keep this one, so setup the pointer.
+ */
+ prev_entry = bindingEntry;
+ } else {
+ if (prev_entry) {
+ prev_entry->next = bindingEntry->next;
+ } else {
+ hentry->bindingEntries = bindingEntry->next;
+ }
+ delHABEent(hentry, bindingEntry);
+ free(bindingEntry);
+ }
+ bindingEntry = next_entry;
+ }
+
+ /*
+ * We need to delete the Mobile Node Entry if the
+ * entry was dynamic and has no more bindings.
+ */
+ if (hentry->haMnBindingCnt == 0 &&
+ hentry->haMnIsEntryDynamic == TRUE) {
+ (void) rw_unlock(&hentry->haMnNodeLock);
+ (void) rwlock_destroy(&hentry->haMnNodeLock);
+ free(hentry);
+
+ /*
+ * Returning _B_FALSE here informs the caller that
+ * the entry was freed.
+ */
+ return (_B_FALSE);
+ }
+ return (_B_TRUE);
+}
+
+/*
+ * Function: haAgeBindings
+ *
+ * Arguments: htbl - Pointer to Hash Table
+ * currentTime - The current Time (absolute)
+ *
+ * Description: This function will step through each
+ * Mobile Node's Binding Entries and will
+ * delete expired entries.
+ *
+ * If the Mobile Node Entry is marked as a
+ * DYNAMIC entry, and the number of binding
+ * entries is set to zero (0), we will free
+ * the Mobile Node Entry as well.
+ *
+ * Returns:
+ */
+static void
+haAgeBindings(struct hash_table *htbl, time_t currentTime)
+{
+ getAllHashTableEntries(htbl, haAgeBindingsHashHelper, LOCK_WRITE,
+ currentTime, _B_FALSE);
+}
+
+
+/*
+ * Function: faAgeVisitorsHashHelper
+ *
+ * Arguments: entry - Pointer to Mobile Node Entry
+ * p1 - First parameter to match (current time)
+ *
+ * Description: This function is used as the Hash Table Helper routine
+ * for faAgeVisitors() when looking for visitor entries
+ * in the Hash Table that have expired, and will be
+ * called by getAllHashTableEntries().
+ *
+ * Returns: _B_TRUE if the entry matches the desired criteria,
+ * otherwise _B_FALSE.
+ */
+static boolean_t
+faAgeVisitorsHashHelper(void *entry, uint32_t p1)
+{
+ FaVisitorEntry *faEntry = entry;
+
+ if (faEntry->faVisitorTimeExpires <= p1) {
+ (void) fprintf(stderr, "Deleting visitor entry!!\n");
+ /* Expired */
+ delFAVEptr(faEntry, _B_TRUE, REG_EXPIRED);
+ (void) rw_unlock(&faEntry->faVisitorNodeLock);
+ (void) rwlock_destroy(&faEntry->faVisitorNodeLock);
+ free(faEntry);
+ return (_B_FALSE);
+ }
+
+ return (_B_TRUE);
+}
+
+/*
+ * Function: faAgeVisitors
+ *
+ * Arguments: htbl - Pointer to Hash Table
+ * currentTime - The current Time (absolute)
+ *
+ * Description: This function will step through each
+ * Foreign Agent Visitor Entries and will
+ * delete expired entries.
+ *
+ * Returns:
+ */
+static void
+faAgeVisitors(struct hash_table *htbl, time_t currentTime)
+{
+ getAllHashTableEntries(htbl, faAgeVisitorsHashHelper, LOCK_WRITE,
+ currentTime, _B_FALSE);
+}
+
+
+/*
+ * Function: mipAgeSecAssocHashHelper
+ *
+ * Arguments: entry - Pointer to Mobile Node Entry
+ * p1 - First parameter to match (current time)
+ *
+ * Description: This function is used as the Hash Table Helper routine
+ * for mipAgeSecAssoc() when looking for visitor entries
+ * in the Hash Table that have expired, and will be
+ * called by getAllHashTableEntries().
+ *
+ * Returns: _B_TRUE if the entry matches the desired criteria,
+ * otherwise _B_FALSE.
+ */
+static boolean_t
+mipAgeSecAssocHashHelper(void *entry, uint32_t p1)
+{
+ MipSecAssocEntry *saEntry = entry;
+
+ if (saEntry->mipSecIsEntryDynamic == TRUE &&
+ saEntry->mipSecKeyLifetime <= p1) {
+ (void) fprintf(stderr,
+ "Deleting Security Assocation entry!!\n");
+ /* Expired */
+ (void) rw_unlock(&saEntry->mipSecNodeLock);
+ (void) rwlock_destroy(&saEntry->mipSecNodeLock);
+ free(saEntry);
+ return (_B_FALSE);
+ }
+
+ return (_B_TRUE);
+}
+
+/*
+ * Function: mipAgeSecAssoc
+ *
+ * Arguments: htbl - Pointer to Hash Table
+ * currentTime - The current Time (absolute)
+ *
+ * Description: This function will step through each
+ * Security Association Entries and will
+ * delete the SAs marked as DYNAMIC that
+ * have expired.
+ *
+ * Returns:
+ */
+static void
+mipAgeSecAssoc(struct hash_table *htbl, time_t currentTime)
+{
+ getAllHashTableEntries(htbl, mipAgeSecAssocHashHelper,
+ LOCK_WRITE, currentTime, _B_FALSE);
+}
+
+/*
+ * Function: mipAgeMobilityAgentsHashHelper
+ *
+ * Arguments: entry - Pointer to Mobile Node Entry
+ * p1 - First parameter to match (current time)
+ *
+ * Description: This function is used as the Hash Table Helper routine
+ * for mipAgeMobilityAgents() when looking for visitor entries
+ * in the Hash Table that have expired, and will be
+ * called by getAllHashTableEntries().
+ *
+ * Returns: _B_TRUE if the entry matches the desired criteria,
+ * otherwise _B_FALSE.
+ */
+static boolean_t
+mipAgeMobilityAgentsHashHelper(void *entry, uint32_t p1)
+{
+ MobilityAgentEntry *maEntry = entry;
+
+ if (maEntry->maIsEntryDynamic == TRUE &&
+ maEntry->maExpiration <= p1) {
+ (void) fprintf(stderr, "Deleting Mobility Agent entry!!\n");
+ /* Expired */
+ (void) rw_unlock(&maEntry->maNodeLock);
+ (void) rwlock_destroy(&maEntry->maNodeLock);
+ free(maEntry);
+ return (_B_FALSE);
+ }
+
+ return (_B_TRUE);
+}
+
+/*
+ * Function: mipAgeMobilityAgents
+ *
+ * Arguments: htbl - Pointer to Hash Table
+ * currentTime - The current Time (absolute)
+ *
+ * Description: This function will step through each
+ * Mobility Agent Entries and will
+ * delete the ones marked as DYNAMIC that
+ * have expired.
+ *
+ * Returns:
+ */
+static void
+mipAgeMobilityAgents(struct hash_table *htbl, time_t currentTime)
+{
+ getAllHashTableEntries(htbl, mipAgeMobilityAgentsHashHelper,
+ LOCK_WRITE, currentTime, _B_FALSE);
+}
+
+/*
+ * Function: startPeriodicTaskThread
+ *
+ * Arguments:
+ *
+ * Description: This function is called to start the
+ * periodic task handling thread.
+ *
+ * Returns: int, 0 if successful.
+ */
+int
+startPeriodicTaskThread(void)
+{
+ pthread_attr_t pthreadAttribute;
+ int result;
+
+ result = pthread_attr_init(&pthreadAttribute);
+
+ if (result) {
+ syslog(LOG_CRIT, "Error Initializing pthread.");
+ return (-1);
+ }
+
+ /*
+ * We now create a thread to deal with all periodic task.
+ */
+ result = pthread_create(&periodicThreadId, &pthreadAttribute,
+ (void *(*)()) doPeriodicTask,
+ (void *)NULL);
+
+ if (result) {
+ syslog(LOG_CRIT, "pthread_create() failed.");
+ return (-1);
+ }
+
+ /*
+ * In order for system resources the be properly cleaned up,
+ * we need to detach the thread. Otherwise, we need to wait for
+ * a pthread_join(), which we do not want.
+ */
+ result = pthread_detach(periodicThreadId);
+
+ if (result) {
+ syslog(LOG_CRIT, "pthread_detach() failed.");
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * Function: killPeriodicTaskThread
+ *
+ * Arguments:
+ *
+ * Description: This function is called during the agent
+ * shutdown procedure in order to kill the
+ * periodic task handling thread.
+ *
+ * Returns:
+ */
+int
+killPeriodicTaskThread()
+{
+ int result;
+
+ if (periodicThreadId) {
+ /*
+ * Next we need to kill the dispatching thread.
+ */
+ result = pthread_cancel(periodicThreadId);
+
+ if (result) {
+ /*
+ * Well, there's not much we can do here..
+ */
+ syslog(LOG_CRIT, "Unable to kill periodic thread");
+ return (-1);
+ }
+ }
+ return (0);
+
+}
+
+/*
+ * Function: doPeriodicTask
+ *
+ * Arguments: void
+ *
+ * Description: This function is called by a thread
+ * and will periodically call the functions
+ * that free any expired control blocks, such
+ * as visitor entries, bindings, etc.
+ *
+ * The frequency that these clean up tasks
+ * get called is configurable through the
+ * configuration file, but this feature is
+ * undocumented.
+ *
+ * This function never returns, but will be
+ * killed if the master thread calls
+ * killPeriodicTaskThread()
+ *
+ * Returns: never!
+ */
+static void
+doPeriodicTask(void)
+{
+ struct timeval tv;
+ time_t nextPeriodicTime = 0;
+ time_t currentTime;
+
+ /* CONSTCOND */
+ while (_B_TRUE) {
+ GET_TIME(currentTime);
+
+ if (currentTime < nextPeriodicTime) {
+ tv.tv_sec = nextPeriodicTime - currentTime;
+ tv.tv_usec = 0;
+ (void) select(FD_SETSIZE, NULL, NULL, NULL, &tv);
+ GET_TIME(currentTime);
+ }
+
+ if (currentTime >= nextPeriodicTime) {
+ haAgeBindings(&haMobileNodeHash, currentTime);
+ faAgeVisitors(&faVisitorHash, currentTime);
+ (void) mipAgeSecAssoc(&mipSecAssocHash, currentTime);
+ mipAgeMobilityAgents(&mipAgentHash, currentTime);
+ nextPeriodicTime = currentTime + periodicInterval;
+ mipverbose(("*"));
+ (void) fflush(stdout);
+ }
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mipagent/agentSaveState.c b/usr/src/cmd/cmd-inet/usr.lib/mipagent/agentSaveState.c
new file mode 100644
index 0000000000..2580ab2150
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mipagent/agentSaveState.c
@@ -0,0 +1,1079 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Functions used to save and read the binding table for persistence across
+ * mipagent reboots.
+ * Saving should be invoked by sending mipagent the proper signal.
+ * After the signal is trapped, the functions here are invoked and save
+ * the state into the binary file:
+ * "/var/inet/mipagent_state"
+ * The file
+ * "/var/inet/mipagent_state.lock"
+ * is used as a mutex.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <thread.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+
+#include "impl.h"
+
+#include "agent.h"
+#include "setup.h"
+#include "hash.h"
+#include "conflib.h"
+
+#define MAXLINELENGTH 200
+/* file to save all mobility bindings to capture state across reboots */
+#define SAVE_BINDINGS_FILE "/var/inet/mipagent_state"
+#ifdef LOCKFILE
+#define SAVE_BINDINGS_LOCK_FILE "/var/inet/mipagent_state.lock"
+#endif /* LOCKFILE */
+#define ENVVAR "MIPAGENT_SAVE_STATE"
+
+/* Controls verbosity of debug messages when compiled w/ "-D MIP_DEBUG" */
+extern int logVerbosity;
+
+/*
+ * Counters maintained by Home Agents
+ */
+extern HomeAgentCounters haCounters;
+
+/* Home Agent specific data structures. */
+extern HashTable haMobileNodeHash;
+extern HashTable mipSecAssocHash;
+extern HashTable mipAgentHash;
+
+extern int installIPsecPolicy(char *);
+extern MobilityAgentEntry *findMaeFromIp(ipaddr_t, int);
+
+static FILE *agentStateFile; /* file ptr for the state file */
+#ifdef LOCKFILE
+static int lockFile; /* lock file file descriptor */
+#endif /* LOCKFILE */
+
+static int preFileAccess(boolean_t forReading);
+static int postFileAccess(void);
+static void removeStateFile(void);
+static size_t saveOneEntry(void *ptr, size_t size);
+static size_t readOneEntry(void *ptr, size_t size);
+int restoreIPsecPolicies(MobilityAgentEntry *, MobilityAgentEntry *);
+
+extern char *validIPsecAction[];
+extern char *ntoa(uint32_t, char *);
+
+/*
+ * Function: saveAgentState
+ *
+ * Arguments: None.
+ *
+ * Description: Save mobile node binding entries. ret 1 if ok, 0 otherwise.
+ *
+ * Note: This routine must be called only when everything
+ * is quiescent. Otherwise inconsistent state may result
+ * if new security associations, mobile node entries
+ * or binding entries are being created as the state
+ * is being saved.
+ *
+ * Returns: 1 if ok, 0 otherwise.
+ */
+
+int
+saveAgentState(void)
+{
+ HashEntry *hashEntry;
+ HaMobileNodeEntry *hamne;
+ HaBindingEntry *habe;
+ MipSecAssocEntry *sae;
+ MobilityAgentEntry *mae;
+ int EntriesSaved, numBindings;
+ int i;
+
+ /* Prepare for security association file access. */
+ if (preFileAccess(_B_FALSE) == -1) {
+ mipverbose(("Could not prepare state file for writing.\n"));
+ return (0);
+ }
+
+ /*
+ * Only save the *dynamic* security associations.
+ * First: count them, and write that number.
+ * Then: do the actual saves.
+ */
+ EntriesSaved = 0;
+ for (i = 0; i < HASH_TBL_SIZE; i++) {
+ if (mipSecAssocHash.buckets[i]) {
+ for (hashEntry = mipSecAssocHash.buckets[i];
+ hashEntry != NULL;
+ hashEntry = hashEntry->next) {
+ sae = hashEntry->data;
+ if (sae == NULL)
+ continue;
+ if (! sae->mipSecIsEntryDynamic)
+ continue;
+ ++EntriesSaved;
+ }
+ }
+ }
+
+ /*
+ * First item to write out: the number of dynamic security
+ * associations which follow.
+ */
+ if (saveOneEntry((void *) &EntriesSaved, sizeof (EntriesSaved)) != 1) {
+ mipverbose(("Problem saving the number of dynamic of"
+ "security associations.\n"));
+ (void) postFileAccess();
+ (void) rw_unlock(&sae->mipSecNodeLock);
+ (void) rw_unlock(&mipSecAssocHash.bucketLock[i]);
+ return (0);
+ }
+
+ /*
+ * Now: write out the actual dynamic security
+ * associations.
+ */
+ for (i = 0; (EntriesSaved > 0) && (i < HASH_TBL_SIZE); i++) {
+ if (mipSecAssocHash.buckets[i]) {
+ for (hashEntry = mipSecAssocHash.buckets[i];
+ hashEntry != NULL;
+ hashEntry = hashEntry->next) {
+
+ sae = hashEntry->data;
+ if (sae == NULL)
+ continue;
+
+ /*
+ * If the entry is static there is
+ * no need to save it, as it will
+ * get restored out of the configuration
+ * file.
+ */
+ if (! sae->mipSecIsEntryDynamic)
+ continue;
+ /*
+ * Now it's safe to write out the sec
+ * association entry.
+ */
+ if (saveOneEntry((void *) sae,
+ sizeof (MipSecAssocEntry)) != 1) {
+ mipverbose(("Problem saving a mobile "
+ "node entry.\n"));
+ (void) postFileAccess();
+ return (0);
+ }
+ --EntriesSaved;
+ }
+ }
+ }
+
+ /*
+ * Now we'll save the agent-peer entries.
+ * First: count them, and write that number.
+ * Then: do the actual saves.
+ */
+ EntriesSaved = 0;
+ for (i = 0; i < HASH_TBL_SIZE; i++) {
+ if (mipAgentHash.buckets[i]) {
+ for (hashEntry = mipAgentHash.buckets[i];
+ hashEntry != NULL;
+ hashEntry = hashEntry->next) {
+ mae = hashEntry->data;
+ if (mae == NULL)
+ continue;
+ ++EntriesSaved;
+ }
+ }
+ }
+
+ /*
+ * Now write out the number of agent-peer entries which follow
+ */
+ if (saveOneEntry((void *) &EntriesSaved, sizeof (EntriesSaved)) != 1) {
+ mipverbose(("Problem saving the number of "
+ "agent-peer entries.\n"));
+ (void) postFileAccess();
+ (void) rw_unlock(&mipAgentHash.bucketLock[i]);
+ return (0);
+ }
+
+ /*
+ * Now: write out the actual agent-peer entries.
+ */
+ for (i = 0; (EntriesSaved > 0) && (i < HASH_TBL_SIZE); i++) {
+ if (mipAgentHash.buckets[i]) {
+ for (hashEntry = mipAgentHash.buckets[i];
+ hashEntry != NULL;
+ hashEntry = hashEntry->next) {
+
+ mae = hashEntry->data;
+
+ if (mae == NULL)
+ continue;
+
+ /*
+ * Write out the agent-peer entry
+ */
+ if (saveOneEntry((void *) mae,
+ sizeof (MobilityAgentEntry)) != 1) {
+ mipverbose(("Problem saving an agent-"
+ "peer entry.\n"));
+ (void) postFileAccess();
+ return (0);
+ }
+ --EntriesSaved;
+ }
+ }
+ }
+
+
+ /*
+ * Now save each mobile node entry.
+ */
+ for (i = 0; i < HASH_TBL_SIZE; i++) {
+ if (haMobileNodeHash.buckets[i]) {
+
+ for (hashEntry = haMobileNodeHash.buckets[i];
+ hashEntry != NULL;
+ hashEntry = hashEntry->next) {
+
+ hamne = hashEntry->data;
+ /*
+ * This should have been != NULL, instead
+ * of == NULL. The issue is that we would
+ * never properly reload existing registrations
+ * if the agent was re-started.
+ */
+ if (hamne != NULL)
+ /*
+ * Instead of trusting it to really
+ * know how many bindings it has, i'll
+ * go ahead and count them just to really
+ * make sure. Otherwise we could end up with
+ * null pointers, core dumps, etc... it's
+ * one more traversal, but most of the time
+ * there's zero or one binding, and the max
+ * is small anyway... Little extra price to pay
+ * to be able to sleep at night...
+ * This way, when we read the mn entry we
+ * know we can trust the number of bindings to
+ * know how many binding entries to read after
+ * that.
+ */
+ for (numBindings = 0,
+ habe = hamne->bindingEntries;
+ habe != NULL &&
+ (numBindings <
+ MAX_SIMULTANEOUS_BINDINGS);
+ ++numBindings, habe = habe->next);
+
+ /*
+ * finished counting the number of
+ * bindings...
+ */
+ if (numBindings != hamne->haMnBindingCnt) {
+ mipverbose(("Miscounted number of "
+ "bindings! (fixing...)\n"));
+ hamne->haMnBindingCnt = numBindings;
+ }
+
+ if (numBindings != 0) {
+ /*
+ * Now it's safe to write out the
+ * mobile node entry.
+ */
+ if (saveOneEntry((void *) hamne,
+ sizeof (HaMobileNodeEntry)) != 1) {
+ mipverbose(("Problem saving "
+ "a mobile node entry.\n"));
+ (void) postFileAccess();
+ return (0);
+ }
+
+#ifdef RADIUS_ENABLED
+ /*
+ * Save any haRadiusState as
+ * len:radiusState
+ */
+
+ if (MobileNodeEntry->haRadiusState !=
+ NULL) {
+ size_t len = strlen(
+ hamne->haRadiusState);
+ if (1 != fwrite((void *) &len,
+ sizeof (size_t), 1,
+ agentStateFile)) {
+ mipverbose((
+ "Problem saving "
+ "length %d of "
+ "radius state"
+ "%s.\n", len,
+ hamne->
+ haRadiusState));
+ (void) postFileAccess();
+ return (0);
+ }
+ if (len != fwrite((void *)
+ hamne->haRadiusState,
+ sizeof (char),
+ len, agentStateFile)) {
+ mipverbose((
+ "Problem saving "
+ "radius state %s."
+ "\n",
+ hamne->
+ haRadiusState));
+ (void) postFileAccess();
+ return (0);
+ }
+ }
+#endif /* RADIUS_ENABLED */
+
+ /*
+ * 2nd traversal of the bindings chain:
+ * save each binding
+ */
+ for (habe = hamne->bindingEntries;
+ habe != NULL;
+ habe = habe->next) {
+
+ if (saveOneEntry((void *) habe,
+ sizeof (HaBindingEntry))
+ != 1) {
+ mipverbose(("Problem "
+ "saving a binding"
+ "entry.\n"));
+ (void) postFileAccess();
+ return (0);
+ }
+ }
+ }
+ }
+ }
+}
+
+if (postFileAccess() == -1)
+ return (0);
+ return (1);
+ } /* saveAgentState(void) */
+
+/* returns 1 if ok, 0 otherwise. */
+static int
+restoreAllMobilityBindings(void)
+{
+ HaMobileNodeEntry hamne, *mnEntry = NULL;
+ HaBindingEntry habe;
+ MipSecAssocEntry sa, *sap;
+ MobilityAgentEntry ma, *map;
+ time_t currentTime;
+ time_t timeout;
+ boolean_t existing;
+ uint32_t sessionLifetime;
+ int EntriesSaved = 0;
+ int i;
+
+ if (preFileAccess(_B_TRUE) == -1) {
+ mipverbose(("Could not prepare state file for reading.\n"));
+ return (0);
+ }
+
+ /*
+ * First: how many dynamic security assoc's are there?
+ */
+
+ if ((int)readOneEntry((void *) &EntriesSaved, sizeof (EntriesSaved))
+ != 1) {
+ mipverbose(("Problem reading the number of dynamic sa's.\n"));
+ (void) postFileAccess();
+ return (0);
+ }
+
+ for (i = 0; i < EntriesSaved; i++) {
+ if ((int)readOneEntry((void *) &sa,
+ sizeof (MipSecAssocEntry)) != 1) {
+ mipverbose(("Problem reading a security assoc.\n"));
+ (void) postFileAccess();
+ return (0);
+ }
+
+ /*
+ * Create the relevant security association
+ * (locked upon return).
+ * NOTE: Must rewrite the key lifetime as
+ * the create routing clobbers the previous
+ * value.
+ */
+ sap = CreateSecAssocEntry(sa.mipSecIsEntryDynamic,
+ sa.mipSecSPI,
+ sa.mipSecReplayMethod,
+ sa.mipSecAlgorithmType,
+ sa.mipSecAlgorithmMode,
+ sa.mipSecKeyLen,
+ (char *)&sa.mipSecKey[0],
+ sa.mipSecKeyLifetime);
+
+ if (sap == NULL) {
+ mipverbose(("Unable to create dynamic SA Entry\n"));
+ haCounters.haInsufficientResourceCnt++;
+ (void) postFileAccess();
+ return (0);
+ }
+
+ sap->mipSecKeyLifetime = sa.mipSecKeyLifetime;
+ mipverbose(("Created a dynamic Mobile Node Entry\n"));
+
+ /*
+ * The Create function ends up locking the sa, so
+ * we need to free it.
+ */
+ (void) rw_unlock(&sap->mipSecNodeLock);
+ }
+
+ /*
+ * the next entry should indicate the number of agent-peer entries.
+ */
+ if ((int)readOneEntry((void *) &EntriesSaved, sizeof (EntriesSaved))
+ != 1) {
+ mipverbose(("Problem reading the number of agent-peers.\n"));
+ (void) postFileAccess();
+ return (0);
+ }
+
+ /* now restore that many agent-peer entries */
+ for (i = 0; i < EntriesSaved; i++) {
+ if ((int)readOneEntry((void *) &ma,
+ sizeof (MobilityAgentEntry)) != 1) {
+ mipverbose(("Problem reading an agent-peer entry.\n"));
+ (void) postFileAccess();
+ return (0);
+ }
+
+ /*
+ * Agent entries are read from the config file BEFORE this
+ * function is called. Therefore, any we find here that
+ * aren't in the config file must have been deleted, and
+ * hence we don't have a SA with them any longer. Conversely
+ * if there are new agent's configured, any SA that's
+ * configued with them is obviously not currently active.
+ */
+ if ((map = findMaeFromIp(ma.maAddr, LOCK_READ)) == NULL) {
+ /*
+ * This is the former case - we saved this agent,
+ * but it hasn't been read from the config file,
+ * and so was delted. We no longer have to worry
+ * about this agent-peer, but let the user know
+ * in case the deletion was accidental.
+ */
+ char peerAddr[IPv4_ADDR_LEN];
+
+ (void) ntoa(ma.maAddr, peerAddr);
+ mipverbose(("agent-peer entry for %s "
+ "no longer configured\n", peerAddr));
+ continue;
+ }
+
+ /* Restore the active IPsec Policies */
+ (void) restoreIPsecPolicies(map, &ma);
+
+ /*
+ * The Create function ends up locking the map, so
+ * we need to free it.
+ */
+ (void) rw_unlock(&map->maNodeLock);
+ }
+
+ /*
+ * Read the mobile node entries till eof.
+ */
+ /* CONSTCOND */
+ while (_B_TRUE) {
+ rwlock_t nl;
+
+ if ((int)readOneEntry((void *) &hamne,
+ sizeof (HaMobileNodeEntry)) != 1) {
+ if (ferror(agentStateFile)) {
+ mipverbose((
+ "Problem reading a mobile node entry.\n"));
+ (void) postFileAccess();
+ return (0);
+ } else if (feof(agentStateFile)) {
+ mipverbose(("Done restoring agent state.\n"));
+ clearerr(agentStateFile);
+ }
+ return ((postFileAccess() == -1) ? 0 : 1);
+ }
+
+ /*
+ * if its a dynamic type, we must explicitly create it here
+ * Otherwise, it was created upon initialization.
+ * Note: creation produces a *locked* node.
+ */
+
+ if (hamne.haMnIsEntryDynamic) {
+ /* First, create the mobile node. */
+
+ mnEntry = CreateMobileNodeEntry(_B_TRUE,
+ hamne.haMnAddr,
+ (char *)&hamne.haMnNAI[0],
+ MAX_NAI_LENGTH,
+ hamne.haBindingIfaceAddr,
+ hamne.haMnSPI,
+ NULL,
+ hamne.haPoolIdentifier);
+
+ if (mnEntry == NULL) {
+ mipverbose((
+ "Unable to create dynamic MN Entry\n"));
+ haCounters.haInsufficientResourceCnt++;
+ (void) postFileAccess();
+ return (0);
+ }
+ mipverbose(("Created a dynamic Mobile Node Entry\n"));
+ } else {
+ mnEntry = findHashTableEntryUint(&haMobileNodeHash,
+ hamne.haMnAddr, LOCK_WRITE, NULL, 0, 0, 0);
+
+ if (mnEntry == NULL) {
+ /*
+ * Search for the MobileNodeEntry based on the
+ * NAI.
+ */
+ mnEntry = findHashTableEntryString(
+ &haMobileNodeHash,
+ (unsigned char *)&hamne.haMnNAI,
+ strlen((char *)&hamne.haMnNAI),
+ LOCK_WRITE,
+ NULL, 0, 0, 0);
+
+ if (mnEntry == NULL) {
+ mipverbose(("Unable to find a "
+ "static MN Entry\n"));
+ (void) postFileAccess();
+ return (0);
+ }
+ }
+ mipverbose(("Found a static Mobile Node Entry\n"));
+ }
+
+ /*
+ * Copy all the fields to the (locked) mobile node entry.
+ * Must make sure the lock info does not get clobbered.
+ * Also, make sure we explicitly clobber the bindingEntries
+ * pointer, as it has no significance until we restore those
+ * entries one by one.
+ * Must also clobber the haMnBindingCnt and let restoreHABE
+ * increment that upon successful restoration of each binding
+ * entry.
+ */
+
+ nl = mnEntry->haMnNodeLock;
+ (void) memcpy((void *)mnEntry, (void *)&hamne,
+ sizeof (HaMobileNodeEntry));
+ mnEntry->haMnNodeLock = nl;
+ mnEntry->bindingEntries = NULL;
+ mnEntry->haMnBindingCnt = 0;
+
+#if 0
+ /* This is not persistent friendly yet. */
+ /* read the radiusState string if needed. */
+ if (mnEntry->haRadiusState != NULL) {
+ size_t len;
+ if (1 != fread((void *) &len, sizeof (size_t), 1,
+ agentStateFile)) {
+ mipverbose((
+ "Problem reading radius state length.\n"));
+ (void) postFileAccess();
+ (void) rw_unlock(&mnEntry->haMnNodeLock);
+ return (0);
+ }
+
+ /* allocate some space for the radius state */
+ if (len != fread((void *) mnEntry->haRadiusState,
+ sizeof (char), len, agentStateFile)) {
+ mipverbose((
+ "Problem reading radius state.\n"));
+ (void) postFileAccess();
+ (void) rw_unlock(&mnEntry->haMnNodeLock);
+ return (0);
+ }
+ }
+ mnEntry->haRadiusState = NULL;
+#endif
+
+ /*
+ * Read all binding entries for this
+ * mn entry.
+ */
+ for (i = 0; i < (int)hamne.haMnBindingCnt; i++) {
+ if ((int)readOneEntry((void *) &habe,
+ sizeof (HaBindingEntry)) != 1) {
+ mipverbose(("Problem reading a "
+ "binding entry.\n"));
+ (void) postFileAccess();
+ (void) rw_unlock(&mnEntry->haMnNodeLock);
+ return (0);
+ }
+
+ /*
+ * Figure out when the entry should expire.
+ */
+ GET_TIME(currentTime);
+
+ if (habe.haBindingTimeExpires > currentTime) {
+ timeout = habe.haBindingTimeExpires -
+ currentTime;
+ /* create the relevant binding entry */
+ (void) addHABE(mnEntry, habe.haBindingSrcAddr,
+ habe.haBindingSrcPort, NULL,
+ habe.haBindingRegFlags,
+ habe.haBindingMN,
+ habe.haBindingCOA,
+ mnEntry->haBindingIfaceAddr, timeout,
+ &existing, &sessionLifetime);
+ }
+ }
+
+ /*
+ * Done with this mn entry, but before
+ * moving on to the next, let's unlock it.
+ */
+ (void) rw_unlock(&mnEntry->haMnNodeLock);
+ }
+
+ return (0);
+} /* restoreAllMobilityBindings(void) */
+
+int
+restoreAgentState(void)
+{
+ (void) restoreAllMobilityBindings();
+ removeStateFile();
+ return (0);
+}
+
+/* ------------------------------------------------------------ */
+/* FILE FUNCTIONS */
+/* ------------------------------------------------------------ */
+
+/*
+ * Prepares the file to write out the state.
+ */
+
+static char host_file[MAXLINELENGTH] = "";
+#ifdef LOCKFILE
+static char lock_file[MAXLINELENGTH] = "";
+#endif /* LOCKFILE */
+
+/* returns -1 if error, 0 if ok */
+
+static int
+preFileAccess(boolean_t forReading)
+{
+ char *envres;
+ mode_t mask;
+
+ /* find out which files we should be using */
+
+ if ((envres = getenv(ENVVAR)) == NULL) {
+ (void) strcpy(host_file, SAVE_BINDINGS_FILE);
+#ifdef LOCKFILE
+ (void) strcpy(lock_file, SAVE_BINDINGS_LOCK_FILE);
+#endif /* LOCKFILE */
+ } else {
+ (void) strcpy(host_file, envres);
+#ifdef LOCKFILE
+ (void) strcpy(lock_file, envres);
+ (void) strcat(lock_file, ".lock");
+#endif /* LOCKFILE */
+ }
+
+#ifdef LOCKFILE
+ /* open the lock file */
+
+ if ((lockFile = open(lock_file, O_RDWR | O_CREAT)) == -1) {
+ mipverbose(("preFileAccess: cannot open lock file\n"));
+ return (-1);
+ }
+
+ /* lock it; block if already locked */
+
+ if ((lockf(lockFile, F_LOCK, 0L)) == -1) {
+ mipverbose(("preFileAccess: cannot lock %s\n",
+ lock_file));
+ return (-1);
+ }
+#endif /* LOCKFILE */
+
+ /*
+ * Must first set umask so as not to allow anybody else
+ * to read these files. They may have keyeing material.
+ */
+ mask = umask(~(S_IRUSR|S_IWUSR));
+
+ /* open for reading/writing the file */
+ if ((agentStateFile = fopen(host_file, (forReading ? "r" : "w")))
+ == NULL) {
+ mipverbose(("preFileAccess: can't open %s for %s\n",
+ host_file, (forReading ? "reading" : "writing")));
+#ifdef LOCKFILE
+ if ((lockf(lockFile, F_ULOCK, 0L)) == -1) {
+ mipverbose(("preFileAccess: can't unlock %s\n",
+ lock_file));
+ }
+ (void) close(lockFile);
+#endif /* LOCKFILE */
+ (void) umask(mask);
+ return (-1);
+ }
+ (void) umask(mask);
+ return (0);
+} /* preFileAccess() */
+
+/*
+ * Called after file access.
+ * Returns 0 if ok, -1 if error.
+ */
+static int
+postFileAccess(void)
+{
+ int ret = 0;
+
+ (void) fclose(agentStateFile);
+
+#ifdef LOCKFILE
+ /* unlock the lock file */
+
+ if ((lockf(lockFile, F_ULOCK, 0L)) == -1) {
+ mipverbose(("postFileAccess can't unlock %s\n",
+ lock_file));
+ ret = -1;
+ }
+ (void) close(lockFile);
+#endif /* LOCKFILE */
+ return (ret);
+} /* postFileAccess() */
+
+/*
+ * Delete the state and lock file after restoring.
+ */
+static void
+removeStateFile(void)
+{
+ (void) unlink(host_file);
+#ifdef LOCKFILE
+ (void) unlink(lock_file);
+#endif /* LOCKFILE */
+} /* removeStateFile() */
+
+/* returns 0 if error, otherwise 1 (nitems) */
+static size_t
+saveOneEntry(void *ptr, size_t size)
+{
+ size_t nitems = fwrite(ptr, size, 1, agentStateFile);
+
+ if (nitems != 1) {
+ if (feof(agentStateFile)) {
+ mipverbose(("saveOneEntry: eof! \n"));
+ (void) postFileAccess();
+ return (0);
+ }
+ if (ferror(agentStateFile)) {
+ mipverbose(("saveOneEntry: error! \n"));
+ (void) postFileAccess();
+ return (0);
+ }
+ }
+ return (nitems);
+}
+
+/* returns 0 if error, otherwise 1 (nitems) */
+static size_t
+readOneEntry(void *ptr, size_t size)
+{
+ size_t nitems = fread(ptr, size, 1, agentStateFile);
+
+ if (nitems != 1) {
+ if (feof(agentStateFile)) {
+ mipverbose(("readOneEntry: eof! \n"));
+ (void) postFileAccess();
+ return (0);
+ }
+ if (ferror(agentStateFile)) {
+ mipverbose(("readOneEntry: error! \n"));
+ (void) postFileAccess();
+ return (0);
+ }
+ }
+ return (nitems);
+}
+
+/*
+ * Function: restoreIPsecPolicies()
+ *
+ * Arguments: new - Pointer to the new MobilityAgentEntry info.
+ * This is what we've just read from the config file.
+ * saved - Pointer to what we read as our exit config.
+ * This is how we were configured, and what IPsec
+ * policies were in place when we were told to shutdown.
+ *
+ * Description: Compares what was parsed from the config file with how we were
+ * configured when we exited. Config file settings, and hence
+ * IPsec policies can change between executions. We want to
+ * restore those that were installed with the new settings,
+ * informing the user when policies have changed, and which have
+ * been restored - especially when those that were in place have
+ * been removed.
+ *
+ * Returns: 1 if OK, 0 if not (like the others called from
+ * restoreAllMobilityBindings()).
+ */
+int
+restoreIPsecPolicies(MobilityAgentEntry *new, MobilityAgentEntry *saved)
+{
+ /*
+ * Check the new policies, and restore any that were installed. We're
+ * really just doing this as a convenience for the user. Those that
+ * were installed because of bound mobile nodes will again be installed
+ * even if the policies are changed (we do NOT second-guess that sort
+ * of thing). However, since this sort of thing can be confusing
+ * when it fails, we'll log the new policies so the user has a place
+ * to start if there are problems.
+ */
+ int action;
+ char peerAddr[IPv4_ADDR_LEN];
+
+ (void) ntoa(new->maAddr, peerAddr);
+
+ /*
+ * maPeerFlags will be restored when we restore the MN entries.
+ *
+ * We don't copy maIPsecFlags because the tunnel flags will be restored
+ * when the MN entries are restored, and the registration flags will
+ * be set as we [re]install each one.
+ *
+ * The maIPsecSAFlags[] aren't overwritten since they've been parsed
+ * based on the potentially new settings in conf-land!
+ *
+ * For readability, we first run through the policies, and see what's
+ * changed, then restore whatever was installed.
+ */
+ for (action = FIRST_IPSEC_ACTION;
+ action < LAST_IPSEC_ACTION;
+ action++) {
+ /* check all for IPSEC_APPLY, then IPSEC_PERMIT */
+ if (memcmp(&new->maIPsecRequestIPSR[action],
+ &saved->maIPsecRequestIPSR[action],
+ sizeof (ipsec_req_t)) != 0) {
+ /* SA has changed, tell the user. */
+ mipverbose(("IPsecRequest %s policy for %s changed.\n",
+ validIPsecAction[action], peerAddr));
+
+ /* was the policy active? */
+ if (saved->maIPsecFlags & REQUEST(action)) {
+ /* A problem if the user *unconfigured* it! */
+ if (*new->maIPsecRequest[action] != 0)
+ /* tell the user we know */
+ mipverbose(("new IPsecRequest %s policy"
+ " will be installed.\n",
+ validIPsecAction[action]));
+ }
+ }
+
+ if (memcmp(&new->maIPsecReplyIPSR[action],
+ &saved->maIPsecReplyIPSR[action],
+ sizeof (ipsec_req_t)) != 0) {
+ /* SA has changed, tell the user. */
+ mipverbose(("IPsecReply %s policy for %s changed.\n",
+ validIPsecAction[action], peerAddr));
+
+ /* was the policy active? */
+ if (saved->maIPsecFlags & REPLY(action)) {
+ /* A problem if the user *unconfigured* it! */
+ if (*new->maIPsecReply[action] != 0)
+ /* tell the user we know */
+ mipverbose(("new IPsecReply %s policy "
+ "will be installed.\n",
+ validIPsecAction[action]));
+ }
+ }
+
+ if (memcmp(&new->maIPsecTunnelIPSR[action],
+ &saved->maIPsecTunnelIPSR[action],
+ sizeof (ipsec_req_t)) != 0) {
+ /* SA has changed, tell the user. */
+ mipverbose(("IPsecTunnel %s policy for %s changed.\n",
+ validIPsecAction[action], peerAddr));
+
+ /* was the policy active? */
+ if (saved->maIPsecFlags & TUNNEL(action)) {
+ /* A problem if the user *unconfigured* it! */
+ if (!IPSEC_TUNNEL_ANY(
+ new->maIPsecSAFlags[action])) {
+ /* tell the user we know */
+ mipverbose(("new IPsecTunnel %s policy "
+ "will be installed.\n",
+ validIPsecAction[action]));
+ }
+ }
+ }
+
+ if (memcmp(&new->maIPsecReverseTunnelIPSR[action],
+ &saved->maIPsecReverseTunnelIPSR[action],
+ sizeof (ipsec_req_t)) != 0) {
+ /* SA has changed, tell the user. */
+ mipverbose((
+ "IPsecReverseTunnel %s policy for %s changed.\n",
+ validIPsecAction[action], peerAddr));
+
+ /* was the policy active? */
+ if (saved->maIPsecFlags & REVERSE_TUNNEL(action)) {
+ /* A problem if the user *unconfigured* it! */
+ if (!IPSEC_REVERSE_TUNNEL_ANY(
+ new->maIPsecSAFlags[action]))
+ /* tell the user we know. */
+ mipverbose((
+ "new IPsecReverseTunnel %s policy "
+ "will be installed.\n",
+ validIPsecAction[action]));
+ }
+ }
+ }
+
+ /*
+ * Restore those that were installed. Warn the user if any of these
+ * went away!
+ */
+ for (action = FIRST_IPSEC_ACTION;
+ action < LAST_IPSEC_ACTION;
+ action++) {
+ /* restore for IPSEC_APPLY, then IPSEC_PERMIT */
+
+ /*
+ * note: there is a potential problem with restoring REQUESTs.
+ * Before we restore, we parse the config file, which is when
+ * IPSEC_REQUEST_PERMIT policies need to be installed. That
+ * means if this is what we're restoring, it's been done (and
+ * it's the new = correct policy).
+ */
+ if ((action != IPSEC_PERMIT) &&
+ (saved->maIPsecFlags & REQUEST(action))) {
+ /* restore, IFF still configured */
+ if (*new->maIPsecRequest[action] != 0) {
+ if (installIPsecPolicy(
+ new->maIPsecRequest[action]) < 0) {
+ /* we wont be able to communicate */
+ mipverbose((
+ "Can't restore %s's ipsec %s"
+ "registration request policy.\n",
+ validIPsecAction[action],
+ peerAddr));
+ return (0);
+ }
+
+ /* set the installed flag */
+ new->maIPsecFlags |= REQUEST(action);
+
+ } else {
+ /* one WAS installed, but isn't configured */
+ mipverbose(("WARNING: IPsecRequest %s policy "
+ "for %s was installed but is no longer "
+ "configured! Registration request will be "
+ "in the clear!", validIPsecAction[action],
+ peerAddr));
+ return (0);
+ }
+ }
+
+ if (saved->maIPsecFlags & REPLY(action)) {
+ /* restore, IFF still configured */
+ if (*new->maIPsecReply[action] != 0) {
+ if (installIPsecPolicy(
+ new->maIPsecReply[action]) < 0) {
+ /* we wont be able to communicate */
+ mipverbose((
+ "Can't restore %s's ipsec %s"
+ "registration reply policy.\n",
+ validIPsecAction[action],
+ peerAddr));
+ return (0);
+ }
+
+ /* set the installed flag */
+ new->maIPsecFlags |= REPLY(action);
+
+ } else {
+ /* one WAS installed, but isn't configured */
+ mipverbose(("WARNING: IPsecReply %s policy "
+ "for %s was installed but is no longer "
+ "configured! Registration reply will be "
+ "in the clear!", validIPsecAction[action],
+ peerAddr));
+ return (0);
+ }
+ }
+
+ /*
+ * Note:
+ * tunnel entries are added when we restore the MNs still
+ * registered with us. An existing MN causes addHABE() to be
+ * called, which calls encapadd() where the correct ipsec_req_t
+ * is passed to ioctl() via settaddr() - exactly as if the MN
+ * registered. At this time, warn the user if any policies
+ * went away, yet we're likely to restore the policy with a MN!
+ */
+ if (saved->maIPsecFlags & TUNNEL(action)) {
+ if (!IPSEC_TUNNEL_ANY(new->maIPsecSAFlags[action]))
+ /* tell the user the config's gone */
+ mipverbose((
+ "WARNING: IPsecTunnel %s policy for "
+ " %s was installed, but is no longer "
+ "configured. Restoring with no policy!",
+ validIPsecAction[action], peerAddr));
+ }
+
+ if (saved->maIPsecFlags & REVERSE_TUNNEL(action)) {
+ if (!IPSEC_REVERSE_TUNNEL_ANY(
+ new->maIPsecSAFlags[action]))
+ /* tell the user the config's gone */
+ mipverbose((
+ "WARNING: IPsecReverseTunnel %s policy for "
+ " %s was installed, but is no longer "
+ "configured. Restoring with no policy!",
+ validIPsecAction[action], peerAddr));
+ }
+ }
+
+ return (1);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mipagent/auth.c b/usr/src/cmd/cmd-inet/usr.lib/mipagent/auth.c
new file mode 100644
index 0000000000..6672556174
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mipagent/auth.c
@@ -0,0 +1,1661 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * file: auth.c
+ *
+ * This function contains all of the routines
+ * necessary to authenticate Registration Requests,
+ * including the functions which add and validate
+ * the authentication extensions, the SPI lookup
+ * routines, etc.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "agent.h"
+#include "mip.h"
+#include "md5.h"
+#include "auth.h"
+
+#include <sys/types.h>
+#include <netinet/in.h>
+
+
+extern char msg[];
+extern AAA_Protocol_Code aaaProtocol;
+extern boolean_t faChallengeAdv;
+extern boolean_t mfAuthRequired;
+extern boolean_t fhAuthRequired;
+extern int logVerbosity;
+
+/*
+ * Default Values...
+ */
+extern uint32_t defaultPool;
+extern uint32_t defaultNodeSPI;
+
+#ifdef RADIUS_ENABLED
+HaMobileNodeEntry *radiusCheckUpdate(HaMobileNodeEntry *dest,
+ struct hash_table *htbl, ipaddr_t mnAddr);
+#endif /* RADIUS_ENABLED */
+
+static boolean_t isAuthExtOk(unsigned char *, int, MipSecAssocEntry *);
+extern int hexdump(char *, unsigned char *, int);
+extern uint32_t getRandomValue();
+
+/* ----------------- Common to all mobility agents ------------------- */
+/*
+ * This table stores all of the Security Assocations
+ */
+extern HashTable mipSecAssocHash;
+
+/*
+ * This table has one entry for each known Mobility Agent
+ */
+extern HashTable mipAgentHash;
+
+/*
+ * This table has one entry for each pool defined in the config file
+ */
+extern HashTable mipPoolHash;
+
+/* ------------------ Specific to foreign agents -------------------- */
+
+/*
+ * Counters maintained by Foreign Agents
+ */
+extern ForeignAgentCounters faCounters;
+
+/*
+ * The last two challenges advertised
+ */
+extern char faLastChallengeIssued[2][ADV_CHALLENGE_LENGTH];
+
+
+/* ------------------ Specific to home agents -------------------- */
+/*
+ * This table has one entry for each mobile node for which a mobility
+ * agent offers Home Agent services.
+ */
+extern HashTable haMobileNodeHash;
+
+/*
+ * Counters maintained by Home Agents
+ */
+extern HomeAgentCounters haCounters;
+
+/*
+ * Compute authenticator for the data in buffer based on the
+ * Mobile IP Security Association pointed to by msaEntry.
+ */
+
+/*
+ * Function: computeAuth
+ *
+ * Arguments: buffer - Pointer to buffer
+ * buflen - Length of data in the buffer
+ * authenticator - Pointer to the output buffer
+ * authenticationlen - length of the output buffer
+ * msa - Pointer to the security association entry.
+ *
+ * Description: Compute authenticator for the data in buffer based on the
+ * Mobile IP Security Association pointed to by msaEntry.
+ *
+ * Returns: void
+ */
+static void
+computeAuth(unsigned char buffer[], int buflen,
+ unsigned char authenticator[], int *authenticatorlen,
+ MipSecAssocEntry *msa)
+{
+ MD5_CTX context;
+
+ /*
+ * Initialize the length.
+ */
+
+ if (msa) {
+ switch (msa->mipSecAlgorithmType) {
+ case MD5:
+ if (*authenticatorlen < AUTHENTICATOR_LEN) {
+ syslog(LOG_ERR,
+ "Not enough space for MD5 authenticator.");
+ } else if (msa->mipSecAlgorithmMode != PREFIXSUFFIX) {
+ syslog(LOG_ERR, "Unknown mode specified " \
+ "for MD5 algorithm.");
+ } else {
+ /*
+ * No longer print MD5 results.
+ */
+ *authenticatorlen = 0;
+ MD5Init(&context);
+ MD5Update(&context, msa->mipSecKey,
+ (unsigned int) msa->mipSecKeyLen);
+ MD5Update(&context, buffer,
+ (unsigned int) buflen);
+ MD5Update(&context, msa->mipSecKey,
+ (unsigned int) msa->mipSecKeyLen);
+ MD5Final(authenticator, &context);
+ *authenticatorlen = AUTHENTICATOR_LEN;
+ }
+ break;
+
+ case NONE:
+ /* we leave the contents of authenticator unchanged */
+ *authenticatorlen = AUTHENTICATOR_LEN;
+ break;
+
+ default:
+ /*
+ * Any other authentication transform, which we
+ * currentlydo not support.
+ */
+ syslog(LOG_ERR, "Invalid Authentication Type " \
+ "requested");
+ }
+ }
+}
+
+
+/*
+ * Function: appendAuthExt
+ *
+ * Arguments: buffer - Pointer to the packet
+ * buflen - Offset in the packet where extension is
+ * to be added.
+ * type - Extension Id
+ * msa - Pointer to the Security Assocation Entry
+ *
+ * Description: Append authentication extension to a registration
+ * reply contained in buffer based on the Mobile IP
+ * Security Association pointed to by MSA. Returns
+ * total number of bytes in the extension.
+ *
+ * Returns: The number of bytes added to the packet.
+ */
+int
+appendAuthExt(unsigned char *buffer, size_t buflen, uint8_t type,
+ MipSecAssocEntry *msa)
+{
+ int authlen = 0;
+ authExt *aep;
+ uint32_t SPI;
+ uint16_t tempShort;
+
+ if (msa) {
+ /*
+ * TODO:
+ * The length of authenticator actually depends on the
+ * algorithm. For now, we assume AUTHENTICATOR_LEN.
+ */
+
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ aep = (authExt *)(buffer + buflen);
+ aep->type = type;
+
+ /*
+ * We need to set to length to the sizeof the SPI plus the
+ * length of the authenticator.
+ */
+ aep->length = (sizeof (SPI) + AUTHENTICATOR_LEN);
+ SPI = (msa == NULL) ? 0 : msa->mipSecSPI;
+ tempShort = htons(SPI >> AUTHENTICATOR_LEN);
+ (void) memcpy(&aep->SPIhi, &tempShort, sizeof (uint16_t));
+ tempShort = htons(SPI & 0xffff);
+ (void) memcpy(&aep->SPIlo, &tempShort, sizeof (uint16_t));
+
+ authlen = AUTHENTICATOR_LEN;
+ computeAuth(buffer, buflen + sizeof (authExt),
+ buffer + buflen + sizeof (authExt), &authlen, msa);
+
+ if (authlen) {
+ authlen += sizeof (authExt);
+ }
+ }
+
+ return (authlen);
+}
+
+
+/*
+ * Function: isAuthOk
+ *
+ * Arguments: buffer - Pointer to buffer
+ * buflen - Length of data in the buffer
+ * authenticator - Pointer to hash to compare against
+ * authenticationlen - length of hash buffer
+ * msa - Pointer to the security association entry
+ *
+ * Description: This function computes a hash using the buffer and
+ * compares the result with the data in the authenticator.
+ * If both values match, the packet is considered
+ * authenticated.
+ *
+ * Returns: boolean_t - _B_FALSE if packet is not authenticated.
+ */
+static boolean_t
+isAuthOk(unsigned char buffer[], int buflen,
+ unsigned char authenticator[], int authenticatorlen,
+ MipSecAssocEntry *msa)
+{
+ static unsigned char newAuth[32];
+ int newAuthLen = 32;
+ int i;
+
+ if (msa == NULL) {
+ return (_B_FALSE);
+ }
+
+ switch (msa->mipSecAlgorithmType) {
+ case MD5:
+ computeAuth(buffer, buflen, newAuth, &newAuthLen, msa);
+ mipverbose(("authenticator = %d bytes, newAuth = %d bytes\n",
+ authenticatorlen, newAuthLen));
+ if (newAuthLen != authenticatorlen)
+ return (_B_FALSE);
+
+ for (i = 0; i < newAuthLen; i++) {
+ if (newAuth[i] != authenticator[i]) {
+ syslog(LOG_ERR,
+ "isAuthOk: bad key at position %d (%02X <> %02X)\n",
+ i, authenticator[i], newAuth[i]);
+ return (_B_FALSE);
+ }
+ }
+
+ break;
+
+ case NONE:
+ /* No checks required */
+ break;
+ }
+
+ return (_B_TRUE);
+}
+
+
+/*
+ * Function: isAuthExtOk
+ *
+ * Arguments: buffer - Pointer to a packet
+ * buflen - Offset in the packet where the authentication
+ * extension can be found.
+ * msa - Pointer to a Security Assocation Entry
+ *
+ * Description: Buffer contains buflen bytes of a registration request
+ * and an immediately following authentication extension.
+ * Check if the authentication extension is correct
+ * according to the given msa.
+ *
+ * Returns: boolean_t - _B_TRUE if authentication extension was
+ * computed using the protocol security assocation.
+ */
+static boolean_t
+isAuthExtOk(unsigned char buffer[], int buflen, MipSecAssocEntry *msa)
+{
+ int authLen;
+ boolean_t result = _B_FALSE;
+ authExt *aep;
+ genAuthExt *genAep;
+ uint32_t SPI;
+
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ aep = (authExt *) (buffer + buflen);
+
+ /*
+ * Support for the latest Challenge/response draft, which
+ * requires support for the generalized authentication header.
+ */
+ switch (aep->type) {
+ case REG_MH_AUTH_EXT_TYPE:
+ case REG_MF_AUTH_EXT_TYPE:
+ case REG_FH_AUTH_EXT_TYPE:
+ GET_SPI(SPI, aep);
+
+ if (SPI != msa->mipSecSPI) {
+ mipverbose(("Type: %d, Length %d\n", aep->type,
+ aep->length));
+ mipverbose(("SPI mismatch got %d had %d.\n",
+ SPI, msa->mipSecSPI));
+ result = _B_FALSE;
+ } else {
+ /* this length includes 4 bytes SPI */
+ authLen = aep->length - 4;
+ result = isAuthOk(buffer, buflen + sizeof (authExt),
+ (buffer + buflen + sizeof (authExt)),
+ authLen, msa);
+ }
+ break;
+
+ case REG_GEN_AUTH_EXT_TYPE:
+ genAep = (genAuthExt *) aep;
+
+ GET_GEN_AUTH_SPI(SPI, genAep);
+
+ if (SPI != msa->mipSecSPI) {
+ mipverbose(("Type: %d, subType: %d, Length %d\n",
+ genAep->type, genAep->subType, genAep->length));
+ mipverbose(("SPI mismatch got %d had %d.\n",
+ SPI, msa->mipSecSPI));
+ result = _B_FALSE;
+ } else {
+ /* This length includes 4 bytes SPI */
+ authLen = ntohs(genAep->length) - 4;
+ result = isAuthOk(buffer, buflen + sizeof (genAuthExt),
+ (buffer + buflen + sizeof (genAuthExt)),
+ authLen, msa);
+ }
+ break;
+
+ default:
+ /*
+ * Unknown authentication type.... reject
+ */
+ result = _B_FALSE;
+ break;
+ }
+
+ return (result);
+}
+
+#ifdef TEST_AUTH
+/*
+ * Function: main
+ *
+ * Arguments: argc - Number of command line parameters
+ * argv - Pointer to command line arguments
+ *
+ * Description: This function is used to validate our MD5 implementation.
+ * It is no longer in use, but is kept in case one needs
+ * to test the authentication computation in this file
+ * stand-alone.
+ *
+ * Returns: exits
+ */
+int
+main(int argc, char *argv[])
+{
+ int i;
+ unsigned char digest[AUTHENTICATOR_LEN];
+ int digestlen = AUTHENTICATOR_LEN;
+ MipSecAssocEntry msae = { { 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ 1, 0, MD5, PREFIXSUFFIX, 16,
+ { 0x11, 0x11, 0x11, 0x11,
+ 0x11, 0x11, 0x11, 0x11,
+ 0x11, 0x11, 0x11, 0x11,
+ 0x11, 0x11, 0x11, 0x11
+ },
+ TIMESTAMPS, 0 };
+
+ unsigned char packet[30] = {
+ /*
+ * 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ * 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ */
+ 0x01, 0x00, 0x00, 0x28, 0x81, 0x92, 0x7a, 0xcc,
+ 0x81, 0x92, 0x7a, 0xbf, 0x81, 0x92, 0xc9, 0x09,
+ 0x00, 0x00, 0x00, 0x00, 0xe4, 0xa8, 0x5f, 0xcb,
+ 0x20, 0x14, 0x00, 0x00, 0x00, 0x01
+ /*
+ * 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ * 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
+ */
+ };
+ /*
+ * 0x01, 0x00, 0x01, 0x2c, 0x81, 0x92, 0x7a, 0xc0,
+ * 0x81, 0x92, 0x7a, 0x7b, 0x81, 0x92, 0xc9, 0x09,
+ * 0x00, 0x00, 0x00, 0x00, 0x17, 0xe1, 0x2c, 0x23,
+ * 0x20, 0x14, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ * 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ * 0x11, 0x11
+ * };
+ * unsigned char packet[] = {'J', 'i', 'm'};
+ */
+
+ printMSAE(&msae);
+ computeAuth(packet, 30, digest, &digestlen, &msae);
+ (void) printf("Authenticator for packet = ");
+ printBuffer(digest, digestlen);
+ (void) printf("\n");
+
+ /* Simulate data corruption or incorrect MSAE */
+ msae.mipSecKeyLen = 2;
+ packet[2] = 0x00;
+ msae.mipSecKey[0] = 'A';
+ printMSAE(&msae);
+ if (isAuthOk(packet, 3, digest, digestlen, &msae))
+ (void) printf("Check succeeded.\n");
+
+}
+#endif /* TEST_AUTH */
+
+/*
+ * Function: findSecAssocFromSPI
+ *
+ * Arguments: SPI - The Security Parameter Index value
+ * lockType - The Lock type, which can be:
+ * LOCK_NONE - No Lock
+ * LOCK_READ - Read Lock
+ * LOCK_WRITE - Write Lock
+ *
+ * Description: This function will look for a security assocation
+ * entry in the hash table matching on the SPI. If
+ * a lock type was requested, upon return the node
+ * will be locked. The caller will be responsible
+ * for unlocking the node when it no longer needs
+ * the security association entry.
+ *
+ * If a security association entry was found, and
+ * the entry is marked as dynamic and if the key
+ * has expired, a NULL value will be returned.
+ *
+ * Returns: If successful, a pointer to a Security Association
+ * Entry will be returned, otherwise NULL.
+ */
+MipSecAssocEntry *
+findSecAssocFromSPI(uint32_t SPI, int lockType)
+{
+ MipSecAssocEntry *saEntry = NULL;
+ time_t currentTime;
+
+ /*
+ * Let's see if we can find the SA using the SPI.
+ */
+ if ((saEntry = findHashTableEntryUint(&mipSecAssocHash,
+ SPI, lockType, NULL, 0, 0, 0)) != NULL) {
+ /*
+ * Keys has a defined lifetime, which is set by the
+ * home AAA Server. We need to check whether the
+ * key being used is still valid.
+ */
+
+ GET_TIME(currentTime);
+ if (saEntry->mipSecIsEntryDynamic == TRUE &&
+ saEntry->mipSecKeyLifetime < currentTime) {
+ /*
+ * The Security Association has expired.
+ * If the node was locked, we need to
+ * unlock it. We will be returning a NULL
+ * to the caller since this is no longer
+ * valid.
+ */
+ if (lockType != LOCK_NONE) {
+ (void) rw_unlock(&saEntry->mipSecNodeLock);
+ }
+ saEntry = NULL;
+ }
+ }
+
+ return (saEntry);
+}
+
+
+/*
+ * Note that upon return of a security association entry, the
+ * node will be locked. The caller must unlock the node once
+ * it is finished with the entry.
+ */
+/*
+ * Function: findSecAssocFromIp
+ *
+ * Arguments: address - Peer's IP Address
+ * lockType - The Lock type, which can be:
+ * LOCK_NONE - No Lock
+ * LOCK_READ - Read Lock
+ * LOCK_WRITE - Write Lock
+ *
+ * Description: This function will look for a security assocation
+ * entry in the hash table matching on the IP Address.
+ * If a lock type was requested, upon return the node
+ * will be locked. The caller will be responsible
+ * for unlocking the node when it no longer needs
+ * the security association entry.
+ *
+ * If a security association entry was found, and
+ * the entry is marked as dynamic, if the key has
+ * expired
+ *
+ * Returns: If successful, a pointer to a Security Association
+ * Entry will be returned
+ * If not successful, NULL is returned.
+ */
+MipSecAssocEntry *
+findSecAssocFromIp(ipaddr_t address, int lockType)
+{
+ MobilityAgentEntry *maEntry;
+ MipSecAssocEntry *saEntry = NULL;
+
+ /*
+ * First we need to find the MA structure to get
+ * the SPI value.
+ */
+ if ((maEntry = findHashTableEntryUint(&mipAgentHash,
+ address, LOCK_READ, NULL, 0, 0, 0)) != NULL) {
+ /*
+ * Good, now let's find the SA itself.
+ */
+ saEntry = findSecAssocFromSPI(maEntry->maSPI, lockType);
+
+ (void) rw_unlock(&maEntry->maNodeLock);
+ }
+
+ return (saEntry);
+}
+
+/*
+ * Function: findMaeFromIp
+ *
+ * Arguments: address - Peer's IP Address
+ * lockType - The Lock type, which can be:
+ * LOCK_NONE - No Lock
+ * LOCK_READ - Read Lock
+ * LOCK_WRITE - Write Lock
+ *
+ * Description: This function will look for an IPsec Policy
+ * entry in the hash table matching on the IP Address.
+ * If a lock type was requested, upon return the node
+ * will be locked. The caller will be responsible
+ * for unlocking the node when it no longer needs
+ * the security association entry.
+ *
+ * Returns: If successful, a pointer to a Security Association
+ * Entry will be returned, otherwise NULL.
+ */
+MobilityAgentEntry *
+findMaeFromIp(ipaddr_t address, int lockType)
+{
+ MobilityAgentEntry *maEntry;
+
+ /*
+ * First we need to find the MA structure to get
+ * the SPI value.
+ */
+ if ((maEntry = findHashTableEntryUint(&mipAgentHash,
+ address, lockType, NULL, 0, 0, 0)) == NULL)
+ return (0);
+
+ return (maEntry);
+}
+
+
+#ifdef KEY_DISTRIBUTION
+/*
+ * KEY_DISTRIBUTION MUST ONLY BE COMPILED FOR TESTING!!!
+ *
+ * This version of mipagent supports a AAA/DIAMETER
+ * interface. The DIAMETER server generates keying
+ * material that is sent to the Home Agent. The keys
+ * sent are both for the Home Agent, and for the Mobile
+ * Node. The keys for the Mobile Nodes are added to the
+ * registration reply, and the keys for the Home Agent
+ * cause the Home Agent to create a local SA.
+ *
+ * Since DIAMETER/AAA is not currently a product, and key
+ * distribution must still be tested, we have added some
+ * test code in mipagent. When KEY_DISTRIBUTION is enabled,
+ * the home agent creates and encrypts session keys for
+ * the Mobile Node (mimicking DIAMETER), and creates local
+ * SAs. Further, since the session keys MUST also be sent
+ * to the Foreign Agent, the session keys are sent in the
+ * clear to the Foreign Agent through Vendor Specific
+ * extensions.
+ *
+ * Again, this code is for testing purpose only and must not
+ * be enabled for production code, since it hasn't been
+ * fully tested.
+ */
+#define MAX_SESSION_KEY_LEN 16
+/*
+ * Function: createSessionKey
+ *
+ * Arguments: key - Pointer to session key buffer
+ * keyLen - Pointer to Length of session key
+ * spi - Pointer to SPI
+ *
+ * Description: This function is used to create pseudo-random
+ * session keys, and SPI values. Note that the
+ * keys created here are by no means random, and
+ * this function is only used for testing purposes.
+ *
+ * Returns: none
+ */
+static void
+createSessionKey(uint8_t *key, uint32_t *keyLen, uint32_t *spi)
+{
+ int i;
+
+ /*
+ * First we create the SPI value.
+ */
+ *spi = htonl(getRandomValue());
+
+ /*
+ * Now we create the session key
+ */
+ for (i = 0; i < MAX_SESSION_KEY_LEN; i++) {
+ key[i] = getRandomValue();
+ }
+
+ /*
+ * Set the length
+ */
+ *keyLen = MAX_SESSION_KEY_LEN;
+
+}
+
+/*
+ * Function: createSessionKey
+ *
+ * Arguments: keyData - Pointer to Genrealized key extension
+ * keyLen - Length of key extension
+ * spi - Pointer to SPI
+ *
+ * Description: This function will create a local Security
+ * association, using the information found in
+ * a generalized key extension. Note that this
+ * function is only used for testing purposes.
+ *
+ * Returns: _B_TRUE if successful
+ */
+static boolean_t
+createSecAssocFromKeyData(keyDataExt *keyData, int keyDataLen, uint32_t *SPI)
+{
+ uint32_t spi;
+ uint32_t lifetime;
+
+ /*
+ * Extract the information from the generalized key ext
+ */
+ (void) memcpy(&spi, &keyData->nodeSPI, sizeof (uint32_t));
+ (void) memcpy(&lifetime, &keyData->lifetime, sizeof (uint32_t));
+
+ /*
+ * Create a new Security Association Entry
+ */
+ if (aaaCreateKey(ntohl(spi), (uint8_t *)((char *)keyData) +
+ sizeof (keyDataExt), keyDataLen - sizeof (keyDataExt),
+ ntohl(lifetime))) {
+ syslog(LOG_CRIT, "Unable to create SA from keydata");
+ return (_B_FALSE);
+ }
+
+ /*
+ * Set the SPI value
+ */
+ *SPI = ntohl(spi);
+
+ return (_B_TRUE);
+}
+
+#endif /* KEY_DISTRIBUTION */
+
+/*
+ * Function: faCheckRegReqAuth
+ *
+ * Arguments: messageHdr - Pointer to Message Control Block
+ * mnSPI - Pointer to Mobile Node's SPI
+ * mnChallenge - Pointer to the FA Challenge
+ * mnChallengeLen - Length of the Challenge
+ * forwardFlag - Pointer to the forward msg flag.
+ *
+ * Description: This function is responsible for authenticating
+ * the Registration Request. First, the function
+ * will check whether the Challenge is present, which
+ * MUST be if the agent is configured to advertise
+ * challenges. Next, the Mobile-Foreign is checked
+ * to ensure that the message is authenticated. If
+ * the extension was not found, and the agent is
+ * configured to require this extension, authentication
+ * will fail.
+ *
+ * Lastly, if the Mobile-AAA authentication extension
+ * is present, we will send a request to the AAA
+ * infrastructure. If this request is successfully sent,
+ * the forward flag will be set to _B_FALSE to ensure that the
+ * caller does not forward the Registration Request to
+ * the Home Agent.
+ *
+ * Returns: int - 0 if successful, otherwise the Mobile-IP error code
+ * is returned. The forwardFlag will be set to _B_FALSE if
+ * the message is being sent to the AAA infrastructure.
+ */
+/* ARGSUSED */
+int
+faCheckRegReqAuth(MessageHdr *messageHdr, FaVisitorEntry *favePtr,
+ FaVisitorEntry *acceptedFave, unsigned char *mnChallenge,
+ uint32_t mnChallengeLen, boolean_t *forwardFlag)
+{
+ regRequest *reqPtr;
+ MipSecAssocEntry *mipSecAssocEntry;
+ authExt *mnAuthExt;
+ /*
+ * Support for the latest Challenge/response draft.
+ */
+ genAuthExt *mnAAAAuthExt;
+ uint32_t SPI;
+ uint32_t aaaSPI;
+ size_t length;
+ int mnAAAAuthExtLen;
+ int mnAuthExtLen;
+ int index;
+ int result;
+
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ reqPtr = (regRequest *)messageHdr->pkt;
+ *forwardFlag = _B_TRUE;
+
+ /*
+ * We have two different authentication types that we need
+ * to worry about. First, and foremost, we need to be able
+ * to support the case where a Mobile-Foreign Security
+ * Association exists. In the AAA world, an authenticated
+ * and authorized Mobile Node would have the keys setup in
+ * the mnFaNodeHash, and this case would apply.
+ *
+ * The next case is when we are unaware of the Mobile Node.
+ * In this case, the Mobile Node should have include the
+ * Challenge/Response extensions which are used for AAA
+ * purposes. When this is found, we issue a call to the local
+ * AAA daemon to authenticate and authorize the MN.
+ */
+
+ if (faChallengeAdv) {
+ if (mnChallengeLen == 0) {
+ syslog(LOG_ERR,
+ "Missing Challenge Extention");
+ faCounters.faMNAuthFailureCnt++;
+ /*
+ * Support for the latest Challenge/response draft.
+ * If the challenge was expected, and found present,
+ * return a missing challenge error.
+ */
+ return (FA_MISSING_CHALLENGE);
+ }
+
+ /*
+ * Obviously, we need to validate the challenge.
+ */
+ if (memcmp(faLastChallengeIssued[0], mnChallenge,
+ ADV_CHALLENGE_LENGTH) != 0) {
+ /*
+ * Let's try our backup.
+ */
+ if (memcmp(faLastChallengeIssued[1],
+ mnChallenge, ADV_CHALLENGE_LENGTH) != 0) {
+ /*
+ * If the visitor entry is present, then we
+ * can ALSO check whether a challenge was
+ * previously issued to the mobile node
+ * in a registration reply.
+ */
+ if (acceptedFave &&
+ acceptedFave->faVisitorChallengeAdvLen) {
+ if (memcmp(
+ acceptedFave->faVisitorChallengeAdv,
+ mnChallenge,
+ acceptedFave-> \
+ faVisitorChallengeAdvLen) != 0) {
+ syslog(LOG_ERR,
+ "Invalid Challenge Extention");
+ faCounters.faMNAuthFailureCnt++;
+ return (FA_UNKNOWN_CHALLENGE);
+ }
+ } else {
+ syslog(LOG_ERR,
+ "Invalid Challenge Extention");
+ faCounters.faMNAuthFailureCnt++;
+ return (FA_UNKNOWN_CHALLENGE);
+ }
+ }
+ }
+ }
+
+
+ mipverbose(("Checking authext"));
+
+ /*
+ * Support for the latest Challenge/Response I-D
+ */
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ GET_GEN_AUTH_EXT(messageHdr, index,
+ GEN_AUTH_MN_AAA, mnAAAAuthExt, mnAAAAuthExtLen);
+
+ /*
+ * If a Mobile node Foreign agent authentication extension exists
+ * check it.
+ */
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ GET_AUTH_EXT(messageHdr, index, REG_MF_AUTH_EXT_TYPE,
+ mnAuthExt, mnAuthExtLen);
+
+ if (mnAuthExtLen) {
+ GET_SPI(SPI, mnAuthExt);
+
+ /*
+ * Remember that the node will be locked upon return.
+ * We need to unlock it when we are done...
+ */
+ if ((mipSecAssocEntry =
+ findSecAssocFromSPI(SPI, LOCK_READ)) == NULL) {
+ syslog(LOG_ERR, "Error: No SA for Mobile Node");
+ faCounters.faMNAuthFailureCnt++;
+ return (FA_MN_AUTH_FAILURE);
+ }
+
+ if (isAuthExtOk(messageHdr->pkt,
+ (messageHdr->extIdx[index] - messageHdr->pkt),
+ mipSecAssocEntry) == _B_FALSE) {
+ (void) rw_unlock(&mipSecAssocEntry->mipSecNodeLock);
+ syslog(LOG_ERR, "Failed MN-FA authentication");
+ faCounters.faMNAuthFailureCnt++;
+ return (FA_MN_AUTH_FAILURE);
+ }
+
+ /*
+ * ... and now we are done, let's unlock it.
+ */
+ (void) rw_unlock(&mipSecAssocEntry->mipSecNodeLock);
+
+ /*
+ * Support for differing extension header formats.
+ *
+ * Remove the extension by playing with the
+ * packet's length.
+ */
+ messageHdr->pktLen = (messageHdr->extIdx[index-1] -
+ messageHdr->pkt) + messageHdr->extHdrLength[index-1] +
+ messageHdr->extLength[index-1];
+ } else {
+ /*
+ * If we are advertising challenges, and the Mobile-AAA
+ * authentication extension is present, then the
+ * Mobile-Foreign does not need to be present. If the
+ * Mobile-AAA is NOT present, and we are configured to
+ * require Mobile-Foreign, then we will fail
+ * authentication.
+ */
+ if (faChallengeAdv == _B_TRUE && mnChallengeLen &&
+ mnAAAAuthExtLen) {
+ mipverbose(("Found a challenge and response\n"));
+
+ /*
+ * First, it is necessary for us to have the NAI in
+ * order to interact with the AAA.
+ */
+ if (messageHdr->mnNAI == NULL) {
+ syslog(LOG_ERR,
+ "MN-AAA present without an NAI");
+ faCounters.faMNAuthFailureCnt++;
+ return (FA_MN_AUTH_FAILURE);
+ }
+
+ /*
+ * Is AAA Enabled?
+ */
+ if (aaaProtocol == AAA_NONE) {
+ /* WORK -- why is this here? (PRC?) */
+ return (0);
+#if 0
+ syslog(LOG_ERR,
+ "Not configured to interact with AAA");
+ faCounters.faMNAuthFailureCnt++;
+ return (FA_MN_AUTH_FAILURE);
+#endif
+ }
+
+ if (mnAAAAuthExtLen) {
+ /*
+ * If the MN-AAA Authentication extension was
+ * present, retrieve the SPI value.
+ */
+ GET_GEN_AUTH_SPI(aaaSPI, mnAAAAuthExt);
+ }
+
+ /*
+ * If we are using Radius only, then the SPI must be 2.
+ */
+ if (aaaProtocol == RADIUS && aaaSPI != RADIUS_SPI) {
+ syslog(LOG_ERR, "Failed MN-FA authentication "
+ "- wrong SPI");
+ faCounters.faMNAuthFailureCnt++;
+ return (FA_MN_AUTH_FAILURE);
+ }
+
+ /*
+ * Good, we've made it this far. Now let's go
+ * check with the AAA Infrastructure, if this
+ * was configured.
+ */
+ /*
+ * Support for the latest Challenge response I-D
+ */
+ length = ((char *)mnAAAAuthExt) -
+ ((char *)messageHdr->pkt) + sizeof (genAuthExt);
+
+ /*
+ * If using Radius, we'd like to preserve messageHdr
+ * until we get a reply from the Radius server
+ */
+ if (aaaProtocol == RADIUS)
+ messageHdr->dontDeleteNow = _B_TRUE;
+
+ result = AAAAuthenticateRegReq(messageHdr->pkt,
+ messageHdr->pktLen, messageHdr->mnNAI,
+ messageHdr->mnNAILen, aaaSPI,
+ (unsigned char *)&mnAAAAuthExt[1],
+ mnAAAAuthExtLen - sizeof (mnAAAAuthExt), length,
+ reqPtr->homeAddr, reqPtr->haAddr, _B_FALSE,
+ messageHdr->inIfindex,
+ (void *)messageHdr, mnChallenge, mnChallengeLen);
+
+ if (result) {
+ /*
+ * Now we look at the result code to determine
+ * what the error was.
+ */
+ faCounters.faMNAuthFailureCnt++;
+ return (FA_MN_AUTH_FAILURE);
+ } else {
+ /*
+ * Make sure that we notify the caller that we
+ * should not forward the request to the Home
+ * Agent since:
+ * - if diameter: it is being done via
+ * diameter server.
+ * - if radius: we need to wait for the auth
+ * answer.
+ */
+ *forwardFlag = _B_FALSE;
+ }
+ } else if (mfAuthRequired) {
+ syslog(LOG_ERR, "Failed MN-FA authentication - No Ext");
+ faCounters.faMNAuthFailureCnt++;
+ return (FA_MN_AUTH_FAILURE);
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * Function: faCheckRegRepAuth
+ *
+ * Arguments: messageHdr - Pointer to the Message Control Block
+ * favePtr - Pointer to the Visitor Entry
+ *
+ * Description: This function is used to authenticate a Registration
+ * Reply. If the agent is configured to advertise
+ * challenges, we will make sure that the challenge was
+ * returned by the Home Agent, and that the challenge
+ * value is identical to the value that was used by the
+ * Mobile Node.
+ *
+ * Next, if the Foreign-Home Authentication extension
+ * is present, it is authenticated. If it is present and
+ * the agent is configured to require it, we will fail
+ * authentication.
+ *
+ * Returns: int - 0 if successful, otherwise the Mobile-IP error code
+ * is returned.
+ */
+int
+faCheckRegRepAuth(MessageHdr *messageHdr, FaVisitorEntry *favePtr)
+{
+ authExt *haAuthExt;
+ MipSecAssocEntry *mipSecAssocEntry;
+#ifdef KEY_DISTRIBUTION
+ keyDataExt *keyData;
+ uint32_t keyDataLen;
+#endif /* KEY_DISTRIBUTION */
+ unsigned char *challenge;
+ uint32_t SPI;
+ int haAuthExtLen;
+ int challengeLen;
+ int index;
+
+ /*
+ * If a Challenge was received by the Mobile Node (due to our
+ * advertisement, let's make sure that the same challenge is
+ * present in the response.
+ */
+ if (favePtr->faVisitorChallengeToHALen) {
+ /*
+ * Retrieve the Challenge
+ */
+ GET_EXT_DATA(messageHdr, index, REG_MF_CHALLENGE_EXT_TYPE,
+ challenge, challengeLen);
+
+ if (challengeLen == 0 || challengeLen > ADV_CHALLENGE_LENGTH) {
+ /*
+ * Protect against buffer overflows...
+ */
+ syslog(LOG_ERR,
+ "excessively large or missing Challenge");
+ faCounters.faPoorlyFormedRepliesCnt++;
+ /*
+ * Support for the latest Challenge/response I-D.
+ * If the challenge was expected and not present,
+ * return a missing challenge error.
+ */
+ return (FA_MISSING_CHALLENGE);
+ }
+
+ if (memcmp(challenge, favePtr->faVisitorChallengeToHA,
+ challengeLen) != 0) {
+ /*
+ * Protect against buffer overflows...
+ */
+ syslog(LOG_ERR,
+ "invalid Challenge in Registration Reply");
+ faCounters.faPoorlyFormedRepliesCnt++;
+ /*
+ * Support for the latest Challenge/response I-D.
+ * If the challenge was invalid, return
+ * an unknown challenge error.
+ */
+ return (FA_UNKNOWN_CHALLENGE);
+ }
+ }
+
+#ifdef KEY_DISTRIBUTION
+ /*
+ * If KEY_DISTRIBUTION is defined (testing purpose only), we will
+ * extract the FA session keys from the registration reply.
+ */
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ GET_VEND_KEY_EXT(messageHdr, index, VENDOR_ID_SUN, REG_MN_FA_KEY_EXT,
+ keyData, keyDataLen);
+
+ if (keyDataLen) {
+ /*
+ * We have a key!!! Let's create our security assoc.
+ */
+ if (createSecAssocFromKeyData(keyData, keyDataLen, &SPI) ==
+ _B_FALSE) {
+ syslog(LOG_ERR,
+ "unable to create dynamic MN-FA SA");
+ return (HA_FA_AUTH_FAILURE);
+ }
+ favePtr->faVisitorSPI = SPI;
+ }
+
+ /*
+ * If KEY_DISTRIBUTION is defined (testing purpose only), we will
+ * extract the FA session keys from the registration reply.
+ */
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ GET_VEND_KEY_EXT(messageHdr, index, VENDOR_ID_SUN, REG_FA_HA_KEY_EXT,
+ keyData, keyDataLen);
+
+ if (keyDataLen) {
+ /*
+ * We have a key!!! Let's create our security assoc.
+ */
+ if (createSecAssocFromKeyData(keyData, keyDataLen, &SPI) ==
+ _B_FALSE) {
+ syslog(LOG_ERR,
+ "unable to create dynamic FA-HA SA");
+ return (HA_FA_AUTH_FAILURE);
+ }
+ }
+#endif /* KEY_DISTRIBUTION */
+
+ /*
+ * If a Home agent Foreign agent authentication extension exists
+ * check it.
+ */
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ GET_AUTH_EXT(messageHdr, index, REG_FH_AUTH_EXT_TYPE, haAuthExt,
+ haAuthExtLen);
+
+ if (haAuthExtLen) {
+ GET_SPI(SPI, haAuthExt);
+
+ /*
+ * Remember that the node will be locked upon return.
+ * We need to unlock it when we are done...
+ */
+ if ((mipSecAssocEntry =
+ findSecAssocFromSPI(SPI, LOCK_READ)) == NULL) {
+ syslog(LOG_ERR, "Error: No Home Agent SA (%d)", SPI);
+ faCounters.faHAAuthFailureCnt++;
+ return (HA_FA_AUTH_FAILURE);
+ }
+
+ if (isAuthExtOk(messageHdr->pkt,
+ (messageHdr->extIdx[index] - messageHdr->pkt),
+ mipSecAssocEntry) == _B_FALSE) {
+ syslog(LOG_ERR, "Failed FA-HA authentication");
+ faCounters.faHAAuthFailureCnt++;
+ (void) rw_unlock(&mipSecAssocEntry->mipSecNodeLock);
+ return (HA_FA_AUTH_FAILURE);
+ }
+ (void) rw_unlock(&mipSecAssocEntry->mipSecNodeLock);
+
+ /*
+ * Remove the extension by playing with the
+ * packet's length.
+ */
+ messageHdr->pktLen = (messageHdr->extIdx[index-1] -
+ messageHdr->pkt) + messageHdr->extHdrLength[index-1] +
+ messageHdr->extLength[index-1];
+
+ if (aaaProtocol == DIAMETER &&
+ messageHdr->pktSource == MIP_PKT_FROM_AAA) {
+ /*
+ * We probably ended up getting a new SPI from the
+ * AAA Server. Update the Visitor Entry with the
+ * new SPI.
+ */
+ favePtr->faVisitorSPI = messageHdr->mnFaSPI;
+ }
+ } else {
+ if (fhAuthRequired) {
+ faCounters.faHAAuthFailureCnt++;
+ return (HA_FA_AUTH_FAILURE);
+ }
+ }
+
+
+ return (0);
+}
+
+
+/*
+ * Function: haCheckRegReqAuth
+ *
+ * Arguments: messageHdr - Pointer to the Message Control Block
+ * hamnePtr - Pointer to a pointer to a mobile node entry
+ * mnSPI - Pointer to the Mobile Node SPI
+ * faSPI - Pointe to the Foreign Agent SPI
+ *
+ * Description: This function is used to authenticate a Registration
+ * request on the Home Agent. First we attempt to find
+ * the Mobile Node Entry using the Home Address. If the
+ * Home Address in the request was set to zero, we will
+ * attempt to find it using the Mobile Node's NAI.
+ *
+ * Next we will ensure that either the Mobile-Home or the
+ * Mobile-AAA autentication extension is present. If none
+ * is present, and the packet was marked as being received
+ * by the Foreign Agent (as opposed to the AAA), we will
+ * make sure that the unknown Mobile Node is attempting
+ * to authenticate using the default SPI. If the packet
+ * was received via the AAA infrastructure, we will
+ * not require any authentication from the Mobile Node.
+ *
+ * Lastly, we will check the Foreign-Home Authentication
+ * extension. If one was not found, and the packet was
+ * not received by the AAA, we will fail authentication.
+ *
+ * Note that if a Mobile Node Entry pointer is returned
+ * by this function, the node will be locked. The caller
+ * is responsible to unlock the node.
+ *
+ * Returns: int - 0 if successful, otherwise the Mobile-IP error code
+ * is returned.
+ */
+int
+haCheckRegReqAuth(MessageHdr *messageHdr, HaMobileNodeEntry **hamnePtr,
+ uint32_t *mnSPI, uint32_t *faSPI)
+{
+ regRequest *reqPtr;
+ int code = 0;
+ int result;
+ MipSecAssocEntry *mipSecAssocEntry;
+ genAuthExt *maAuthExt = NULL;
+ authExt *mnAuthExt = NULL;
+ authExt *faAuthExt = NULL;
+ int mnAuthExtLen;
+ int faAuthExtLen;
+ int index;
+ uint32_t SPI;
+
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ reqPtr = (regRequest *)messageHdr->pkt;
+ *hamnePtr = NULL;
+
+#ifdef RADIUS_ENABLED
+ if (radiusEnabled) {
+ /*
+ * We always have to force a RADIUS lookup.
+ */
+ *hamnePtr = radiusCheckUpdate(*hamnePtr,
+ &haMobileNodeHash,
+ reqPtr->homeAddr);
+ }
+#endif /* RADIUS_ENABLED */
+
+ /*
+ * If a Foreign agent Home Agent authentication extension exists
+ * check it.
+ */
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ GET_AUTH_EXT(messageHdr, index, REG_FH_AUTH_EXT_TYPE, faAuthExt,
+ faAuthExtLen);
+
+ if (faAuthExtLen) {
+ GET_SPI(SPI, faAuthExt);
+ *faSPI = SPI;
+ /*
+ * if an SA is returned, the node will be locked so we
+ * need to unlock it when we are done.
+ */
+ if ((mipSecAssocEntry =
+ findSecAssocFromSPI(SPI, LOCK_READ)) == NULL) {
+ syslog(LOG_ERR,
+ "Failed FA-HA authentication - SPI (%d) "
+ "defined", SPI);
+ haCounters.haFAAuthFailureCnt++;
+ return (HA_FA_AUTH_FAILURE);
+ }
+
+ if (isAuthExtOk(messageHdr->pkt,
+ (messageHdr->extIdx[index] - messageHdr->pkt),
+ mipSecAssocEntry) == _B_FALSE) {
+ syslog(LOG_ERR, "Failed FA-HA authentication");
+ haCounters.haFAAuthFailureCnt++;
+ (void) rw_unlock(
+ &mipSecAssocEntry->mipSecNodeLock);
+ return (HA_FA_AUTH_FAILURE);
+ }
+
+ (void) rw_unlock(&mipSecAssocEntry->mipSecNodeLock);
+
+ } else if ((messageHdr->pktSource != MIP_PKT_FROM_AAA) &&
+ (messageHdr->pktSource != MIP_PKT_FROM_RADIUS)) {
+ /*
+ * If the packet comes from the AAA, we do not need
+ * the FA-HA auth, otherwise we may be configured
+ * to require it.
+ */
+ if (fhAuthRequired) {
+ haCounters.haFAAuthFailureCnt++;
+ return (HA_FA_AUTH_FAILURE);
+ }
+ } else {
+ *faSPI = messageHdr->faHaSPI;
+ }
+
+ /*
+ * If a Mobile Node Home Agent authentication extension exists
+ * check it.
+ */
+
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ GET_AUTH_EXT(messageHdr, index, REG_MH_AUTH_EXT_TYPE, mnAuthExt,
+ mnAuthExtLen);
+
+ if (mnAuthExtLen) {
+ GET_SPI(SPI, mnAuthExt);
+ /*
+ * if an SA is returned, the node will be locked so we
+ * need to unlock it when we are done.
+ */
+ if ((mipSecAssocEntry =
+ findSecAssocFromSPI(SPI, LOCK_READ)) == NULL) {
+ syslog(LOG_ERR, "Failed MN-HA authentication - "
+ "No SPI defined");
+ haCounters.haMNAuthFailureCnt++;
+ return (HA_MN_AUTH_FAILURE);
+ }
+ if ((messageHdr->pktSource == MIP_PKT_FROM_AAA) ||
+ (messageHdr->pktSource == MIP_PKT_FROM_RADIUS)) {
+ if (mipSecAssocEntry->mipSecIsEntryDynamic !=
+ TRUE) {
+ /*
+ * So the packet came from the AAA. We
+ * need to ensure that the key being
+ * used is in fact a dynamic key.
+ * Otherwise we are leaving a security
+ * hole wide open.
+ */
+ (void) rw_unlock(
+ &mipSecAssocEntry->mipSecNodeLock);
+ syslog(LOG_WARNING, "A AAA Mobile "
+ "Node is attempting to use a "
+ "static key - security violation!");
+ haCounters.haMNAuthFailureCnt++;
+ return (HA_MN_AUTH_FAILURE);
+ }
+ } else if (isAuthExtOk(messageHdr->pkt,
+ (messageHdr->extIdx[index] -
+ messageHdr->pkt), mipSecAssocEntry) ==
+ _B_FALSE) {
+ syslog(LOG_ERR, "Failed MN-HA "
+ "authentication");
+ haCounters.haMNAuthFailureCnt++;
+ (void) rw_unlock(
+ &mipSecAssocEntry->mipSecNodeLock);
+ return (HA_MN_AUTH_FAILURE);
+ }
+
+ (void) rw_unlock(&mipSecAssocEntry->mipSecNodeLock);
+
+ }
+ if (aaaProtocol == RADIUS) {
+
+ /*
+ * Validate MN_AAA ext exists before AAA call
+ * This way if an error we don't need
+ * to call on AAA.
+ */
+
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ GET_GEN_AUTH_EXT(messageHdr, index,
+ GEN_AUTH_MN_AAA, maAuthExt, mnAuthExtLen);
+
+ if (mnAuthExtLen == 0) {
+ syslog(LOG_ERR, "Missing MN AAA Ext");
+ haCounters.haMNAuthFailureCnt++;
+ return (HA_MN_AUTH_FAILURE);
+ }
+ /*
+ * Get the MN-AAA SPI
+ */
+ GET_GEN_AUTH_SPI(*mnSPI, maAuthExt);
+
+ /*
+ * Make sure SPI used is the Radius SPI (2).
+ */
+ if (*mnSPI != RADIUS_SPI) {
+ haCounters.haMNAuthFailureCnt++;
+ return (HA_MN_AUTH_FAILURE);
+ }
+
+ /*
+ * If using Radius, we'd like to preserve messageHdr
+ * until we get a reply from the Radius server
+ */
+
+ messageHdr->dontDeleteNow = _B_TRUE;
+
+ result = AAAAuthenticateRegReq(messageHdr->pkt,
+ messageHdr->pktLen, messageHdr->mnNAI,
+ messageHdr->mnNAILen, *mnSPI,
+ (unsigned char *)NULL, 0, 0,
+ reqPtr->homeAddr, reqPtr->haAddr, _B_TRUE,
+ 0, (void *)messageHdr, NULL, 0);
+
+ if (result) {
+ /*
+ * Now we look at the result code to determine
+ * what the error was.
+ */
+ haCounters.haMNAuthFailureCnt++;
+ return (HA_MN_AUTH_FAILURE);
+ }
+
+ }
+
+ /*
+ * If talking to RADIUS client, you must wait for a reponse
+ * back (from AAAAuthenticateRegReq() call) before
+ * continuing on with haCheckRegReqAuthContinue().
+ * As such, wait until RADIUS responds then continue on
+ * with authentication process.
+ */
+
+ if (aaaProtocol != RADIUS) {
+ code = haCheckRegReqAuthContinue(messageHdr, hamnePtr,
+ mnSPI, faSPI);
+ return (code);
+ }
+ return (0);
+}
+
+/*
+ * Function: haCheckRegReqAuthContinue
+ *
+ * Arguments: messageHdr - Pointer to the Message Control Block
+ * hamnePtr - Pointer to a pointer to a mobile node entry
+ * mnSPI - Pointer to the Mobile Node SPI
+ * faSPI - Pointe to the Foreign Agent SPI
+ *
+ * Description: This function is used to authenticate a Registration
+ * request on the Home Agent. First we attempt to find
+ * the Mobile Node Entry using the Home Address. If the
+ * Home Address in the request was set to zero, we will
+ * attempt to find it using the Mobile Node's NAI.
+ *
+ * if aaaProtocol is RADIUS, this funtion will be called after
+ * an ANSWER is received from the Radius client.
+ * Otherwise, the processing will continue from
+ * haCheckRegReqAuth() function.
+ *
+ * Next we will ensure that either the Mobile-Home or the
+ * Mobile-AAA autentication extension is present. If none
+ * is present, and the packet was marked as being received
+ * by the Foreign Agent (as opposed to the AAA), we will
+ * make sure that the unknown Mobile Node is attempting
+ * to authenticate using the default SPI. If the packet
+ * was received via the AAA infrastructure, we will
+ * not require any authentication from the Mobile Node.
+ *
+ * Lastly, we will check the Foreign-Home Authentication
+ * extension. If one was not found, and the packet was
+ * not received by the AAA, we will fail authentication.
+ *
+ * Note that if a Mobile Node Entry pointer is returned
+ * by this function, the node will be locked. The caller
+ * is responsible to unlock the node.
+ *
+ * Returns: int - 0 if successful, otherwise the Mobile-IP error code
+ * is returned.
+ */
+/* ARGSUSED */
+int
+haCheckRegReqAuthContinue(MessageHdr *messageHdr, HaMobileNodeEntry **hamnePtr,
+ uint32_t *mnSPI, uint32_t *faSPI)
+{
+ regRequest *reqPtr;
+ authExt *mnAuthExt = NULL;
+ genAuthExt *maAuthExt = NULL;
+ MipSecAssocEntry *mipSecAssocEntry;
+ uint32_t SPI;
+ int index;
+ int mnAuthExtLen;
+
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ reqPtr = (regRequest *)messageHdr->pkt;
+
+ if (reqPtr->homeAddr) {
+ /*
+ * Find the Mobile Node. Remember that this node will
+ * be locked upon return. As it turns out, the caller
+ * to this function will have to unlock the node since
+ * we are passing it the pointer as part of an argument.
+ */
+ *hamnePtr = findHashTableEntryUint(&haMobileNodeHash,
+ reqPtr->homeAddr, LOCK_WRITE, NULL, 0, 0, 0);
+ }
+
+ if (*hamnePtr == NULL) {
+ /*
+ * Search for the MobileNodeEntry based on the
+ * NAI.
+ */
+
+ *hamnePtr = findHashTableEntryString(&haMobileNodeHash,
+ messageHdr->mnNAI, messageHdr->mnNAILen, LOCK_WRITE,
+ NULL, 0, 0, 0);
+ }
+
+
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ GET_AUTH_EXT(messageHdr, index, REG_MH_AUTH_EXT_TYPE, mnAuthExt,
+ mnAuthExtLen);
+
+ /*
+ * If aaaProtocol != RADIUS/DIAMETER, then the home agent CAN
+ * accept Mobile-AAA Authentication extensions, so if we cannot
+ * find the Authentication Extension, find the MN-AAA.
+ *
+ * Happy Dave?
+ */
+ if ((mnAuthExtLen == 0) && (aaaProtocol == AAA_NONE)) {
+ /*
+ * Support for the latest Challenge/Response I-D
+ *
+ * This code does not belong in the HA, this is
+ * really targetted to the AAA Server. We will
+ * include it to fully support the protocol.
+ */
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ GET_GEN_AUTH_EXT(messageHdr, index,
+ GEN_AUTH_MN_AAA, maAuthExt, mnAuthExtLen);
+
+ if (mnAuthExtLen == 0) {
+ syslog(LOG_ERR, "Missing Challenge or Response");
+ haCounters.haMNAuthFailureCnt++;
+ return (HA_MN_AUTH_FAILURE);
+ }
+ /*
+ * Get the MN-AAA SPI
+ */
+ GET_GEN_AUTH_SPI(SPI, maAuthExt);
+#ifdef KEY_DISTRIBUTION
+ /*
+ * If this code is compiled, the Home Agent will provide AAA
+ * like functionality by creating Session Keys for:
+ * MN-HA
+ * MN-FA
+ * FA-HA
+ * The last one is normally not seen by the Home Agent when
+ * keys are received from DIAMETER, but since we are providing
+ * this functionality (mostly for testing purposes) we will
+ * send it to the Foreign Agent as a vendor specific extension.
+ */
+ createSessionKey(messageHdr->mnHaKey, &messageHdr->mnHaKeyLen,
+ &messageHdr->mnHaSPI);
+
+ createSessionKey(messageHdr->mnFaKey, &messageHdr->mnFaKeyLen,
+ &messageHdr->mnFaSPI);
+
+ createSessionKey(messageHdr->faHaKey, &messageHdr->faHaKeyLen,
+ &messageHdr->faHaSPI);
+
+ messageHdr->kdcKeysPresent = _B_TRUE;
+ messageHdr->mnAAASPI = SPI;
+#endif /* KEY_DISTRIBUTION */
+ } else if (mnAuthExt) {
+ /*
+ * Get the MN-HA SPI
+ */
+ GET_SPI(SPI, mnAuthExt);
+ } else {
+ SPI = 0;
+ }
+
+ if (*hamnePtr == NULL) {
+ /*
+ * So we have a couple of options here. The first being
+ * where the packet is received by the AAA. In this
+ * case, the Mobile Node will not exist locally, and the
+ * key would have been installed as a dynamic key.
+ * The second option is where the default node is being
+ * used. When this occurs, it is mandatory that the
+ * mobile node use the default SPI.
+ */
+ if (messageHdr->pktSource == MIP_PKT_FROM_FA) {
+ /*
+ * So, it looks like we don't know who this is. If a
+ * default SA is setup, we will check if we can
+ * create a dynamic Mobile Node Entry.
+ */
+ if (defaultNodeSPI == 0 || defaultNodeSPI != SPI) {
+ syslog(LOG_ERR,
+ "As far as I'm concerned, this "
+ "mobile node doesn't exist");
+ return (HA_ADM_PROHIBITED);
+ }
+ } else {
+ SPI = messageHdr->mnHaSPI;
+ }
+ } else {
+ if ((messageHdr->pktSource == MIP_PKT_FROM_AAA) ||
+ (messageHdr->pktSource == MIP_PKT_FROM_RADIUS)) {
+ SPI = messageHdr->mnHaSPI;
+ (*hamnePtr)->haMnSPI = SPI;
+ } else {
+ /*
+ * Did the Mobile Node specify the correct SPI?
+ */
+ if ((*hamnePtr)->haMnSPI != SPI) {
+ syslog(LOG_ERR, "Failed MN-HA authentication - "
+ "Invalid SPI requested %d, looking for %d",
+ SPI, (*hamnePtr)->haMnSPI);
+ haCounters.haMNAuthFailureCnt++;
+ return (HA_MN_AUTH_FAILURE);
+ }
+ }
+ }
+ *mnSPI = SPI;
+
+ /*
+ * if an SA is returned, the node will be locked so we
+ * need to unlock it when we are done.
+ */
+ if ((mipSecAssocEntry =
+ findSecAssocFromSPI(SPI, LOCK_READ)) == NULL) {
+ syslog(LOG_ERR, "Failed MN-HA authentication - "
+ "No SPI defined");
+ haCounters.haMNAuthFailureCnt++;
+ return (HA_MN_AUTH_FAILURE);
+ }
+ if ((messageHdr->pktSource == MIP_PKT_FROM_AAA) ||
+ (messageHdr->pktSource == MIP_PKT_FROM_RADIUS)) {
+ if (mipSecAssocEntry->mipSecIsEntryDynamic != TRUE) {
+ /*
+ * So the packet came from the AAA. We need to
+ * ensure that the key being used is in fact a
+ * dynamic key. Otherwise we are leaving a security
+ * hole wide open.
+ */
+ (void) rw_unlock(&mipSecAssocEntry->mipSecNodeLock);
+ syslog(LOG_WARNING, "A AAA Mobile Node is attempting"
+ " to use a static key - security violation!");
+ haCounters.haMNAuthFailureCnt++;
+ return (HA_MN_AUTH_FAILURE);
+ }
+ } else if (isAuthExtOk(messageHdr->pkt,
+ (messageHdr->extIdx[index] - messageHdr->pkt),
+ mipSecAssocEntry) == _B_FALSE) {
+ syslog(LOG_ERR, "Failed MN-HA authentication");
+ haCounters.haMNAuthFailureCnt++;
+ (void) rw_unlock(&mipSecAssocEntry->mipSecNodeLock);
+ return (HA_MN_AUTH_FAILURE);
+ }
+
+ (void) rw_unlock(&mipSecAssocEntry->mipSecNodeLock);
+
+ return (0);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mipagent/auth.h b/usr/src/cmd/cmd-inet/usr.lib/mipagent/auth.h
new file mode 100644
index 0000000000..e134dc1882
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mipagent/auth.h
@@ -0,0 +1,86 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _AUTH_H
+#define _AUTH_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * auth.h : Data structures and prototypes used by a Mobile IP agent
+ * for authentication purposes.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int appendAuthExt(uint8_t *, size_t, uint8_t, MipSecAssocEntry *);
+
+/*
+ * Routines to find Security Associatios
+ */
+MipSecAssocEntry *findSecAssocFromIp(uint32_t, int);
+MipSecAssocEntry *findSecAssocFromSPI(uint32_t, int);
+
+/*
+ * Support for the latest Challenge/Response I-D
+ */
+int faCheckRegReqAuth(MessageHdr *, FaVisitorEntry *, FaVisitorEntry *,
+ unsigned char *, uint32_t, boolean_t *);
+int faCheckRegRepAuth(MessageHdr *, FaVisitorEntry *);
+int haCheckRegReqAuth(MessageHdr *, HaMobileNodeEntry **, uint32_t *,
+ uint32_t *);
+
+#define AUTHENTICATOR_LEN 16
+
+#define GET_SPI(SPI, authExt) \
+ { \
+ uint16_t SPIlo; \
+ uint16_t SPIhi; \
+ \
+ (void) memcpy(&SPIhi, &authExt->SPIhi, sizeof (SPIhi)); \
+ (void) memcpy(&SPIlo, &authExt->SPIlo, sizeof (SPIlo)); \
+ SPI = ntohs(SPIhi) << AUTHENTICATOR_LEN | ntohs(SPIlo); \
+ }
+
+/*
+ * Support for the latest Challenge/Response I-D
+ */
+#define GET_GEN_AUTH_SPI(SPI, genAuthExt) \
+ { \
+ uint16_t SPIlo; \
+ uint16_t SPIhi; \
+ \
+ (void) memcpy(&SPIhi, &genAuthExt->SPIhi, sizeof (SPIhi)); \
+ (void) memcpy(&SPIlo, &genAuthExt->SPIlo, sizeof (SPIlo)); \
+ SPI = ntohs(SPIhi) << AUTHENTICATOR_LEN | ntohs(SPIlo); \
+ }
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _AUTH_H */
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mipagent/fakeDiameter.pl b/usr/src/cmd/cmd-inet/usr.lib/mipagent/fakeDiameter.pl
new file mode 100644
index 0000000000..518244c620
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mipagent/fakeDiameter.pl
@@ -0,0 +1,632 @@
+#!/usr/local/bin/perl -w
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 1999 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+# This script will fake a diameter server for testing.
+#
+
+
+$DEFAULT_PORT = 1234;
+
+use Socket;
+use Sys::Hostname;
+use Fcntl;
+
+require "errno.ph";
+
+#define MOBILE_IP_OPEN_SESSION_REQUEST 1
+#define MOBILE_IP_OPEN_SESSION_ANSWER 2
+#define MOBILE_IP_OPEN_SESSION_INDICATOIN 3
+#define MOBILE_IP_OPEN_SESSION_INDICATION_RESPONSE 4
+#define MOBILE_IP_ACCOUNTING_START_REQUEST 5
+#define MOBILE_IP_ACCOUNTING_START_ANSWER 6
+#define MOBILE_IP_ACCOUNTING_INTERIM_REQUEST 7
+#define MOBILE_IP_ACCOUNTING_INTERIM_ANSWER 8
+#define MOBILE_IP_ACCOUNTING_STOP_REQUEST 9
+#define MOBILE_IP_ACCOUNTING_STOP_ANSWER 10
+#define MOBILE_IP_CLOSE_SESSION_REQUEST 11
+#define MOBILE_IP_CLOSE_SESSION_ANSWER 12
+
+#define MOBILE_NODE_NAI 1
+#define FOREIGN_AGENT_NAI 2
+#define REGISTRATION_REQUEST 3
+#define NUMBER_OF_CHALLENGE_BYTES_IN_RR 4
+#define MOBILE_NODE_RESPONSE 5
+#define MOBILE_NODE_HOME_ADDRESS 6
+#define HOME_AGENT_ADDRESS 7
+#define RESULT_CODE 8
+#define REGISTRATION_REPLY 9
+#define MN_FA_SPI 10
+#define MN_FA_KEY 11
+#define FA_HA_SPI 12
+#define FA_HA_KEY 13
+#define SESSION_TIMEOUT 14
+#define HA_FA_KEY 15
+#define FA_MN_KEY 16
+#define MN_HA_SPI 17
+#define MN_HA_KEY 18
+#define HA_MN_KEY 19
+#define SESSION_TIMEOUT_1 20
+#define SESSION_TIME 21
+
+my $CONVERT_FROM = 0;
+my $CONVERT_TO = 1;
+
+my %commandCodes;
+$commandCodes{MOBILE_IP_OPEN_SESSION_REQUEST} = 1;
+$commandCodes{MOBILE_IP_OPEN_SESSION_ANSWER} = 2;
+$commandCodes{MOBILE_IP_OPEN_SESSION_INDICATOIN} = 3;
+$commandCodes{MOBILE_IP_OPEN_SESSION_INDICATION_RESPONSE} = 4;
+$commandCodes{MOBILE_IP_ACCOUNTING_START_REQUEST} = 5;
+$commandCodes{MOBILE_IP_ACCOUNTING_START_ANSWER} = 6;
+$commandCodes{MOBILE_IP_ACCOUNTING_INTERIM_REQUEST} = 7;
+$commandCodes{MOBILE_IP_ACCOUNTING_INTERIM_ANSWER} = 8;
+$commandCodes{MOBILE_IP_ACCOUNTING_STOP_REQUEST} = 9;
+$commandCodes{MOBILE_IP_ACCOUNTING_STOP_ANSWER} = 10;
+$commandCodes{MOBILE_IP_CLOSE_SESSION_REQUEST} = 11;
+$commandCodes{MOBILE_IP_CLOSE_SESSION_ANSWER} = 12;
+
+my @commandCodesRev;
+$commandCodesRev[1] = { name => "MOBILE_IP_OPEN_SESSION_REQUEST",
+ func => \&processOpenSessionRequest };
+$commandCodesRev[2] = { name => "MOBILE_IP_OPEN_SESSION_ANSWER",
+ func => \&niy };
+$commandCodesRev[3] = { name => "MOBILE_IP_OPEN_SESSION_INDICATOIN",
+ func => \&niy };
+$commandCodesRev[4] = { name => "MOBILE_IP_OPEN_SESSION_INDICATION_RESPONSE",
+ func => \&niy };
+$commandCodesRev[5] = { name => "MOBILE_IP_ACCOUNTING_START_REQUEST",
+ func => \&processAccountingStart };
+$commandCodesRev[6] = { name => "MOBILE_IP_ACCOUNTING_START_ANSWER",
+ func => \&niy };
+$commandCodesRev[7] = { name => "MOBILE_IP_ACCOUNTING_INTERIM_REQUEST",
+ func => \&processAccountingInterim };
+$commandCodesRev[8] = { name => "MOBILE_IP_ACCOUNTING_INTERIM_ANSWER",
+ func => \&niy };
+$commandCodesRev[9] = { name => "MOBILE_IP_ACCOUNTING_STOP_REQUEST",
+ func => \&processAccountingStop };
+$commandCodesRev[10] = { name => "MOBILE_IP_ACCOUNTING_STOP_ANSWER",
+ func => \&niy };
+$commandCodesRev[11] = { name => "MOBILE_IP_CLOSE_SESSION_REQUEST",
+ func => \&processCloseSession };
+$commandCodesRev[12] = { name => "MOBILE_IP_CLOSE_SESSION_ANSWER",
+ func => \&niy };
+
+my %avpCodes;
+$avpCodes{MOBILE_NODE_NAI} = 1;
+$avpCodes{FOREIGN_AGENT_NAI} = 2;
+$avpCodes{REGISTRATION_REQUEST} = 3;
+$avpCodes{NUMBER_OF_CHALLENGE_BYTES_IN_RR} = 4;
+$avpCodes{MOBILE_NODE_RESPONSE} = 5;
+$avpCodes{MOBILE_NODE_HOME_ADDRESS} = 6;
+$avpCodes{HOME_AGENT_ADDRESS} = 7;
+$avpCodes{RESULT_CODE} = 8;
+$avpCodes{REGISTRATION_REPLY} = 9;
+$avpCodes{MN_FA_SPI} = 10;
+$avpCodes{MN_FA_KEY} = 11;
+$avpCodes{FA_HA_SPI} = 12;
+$avpCodes{FA_HA_KEY} = 13;
+$avpCodes{SESSION_TIMEOUT} = 14;
+$avpCodes{HA_FA_KEY} = 15;
+$avpCodes{FA_MN_KEY} = 16;
+$avpCodes{MN_HA_SPI} = 17;
+$avpCodes{MN_HA_KEY} = 18;
+$avpCodes{HA_MN_KEY} = 19;
+$avpCodes{SESSION_TIMEOUT_1} = 20;
+$avpCodes{SESSION_TIME} = 21;
+
+my @avpCodesRev;
+$avpCodesRev[1] = { name => "MOBILE_NODE_NAI",
+ func => \&ConvertString };
+$avpCodesRev[2] = { name => "FOREIGN_AGENT_NAI",
+ func => \&ConvertString };
+$avpCodesRev[3] = { name => "REGISTRATION_REQUEST",
+ func => \&ConvertData };
+$avpCodesRev[4] = { name => "NUMBER_OF_CHALLENGE_BYTES_IN_RR",
+ func => \&ConvertInteger };
+$avpCodesRev[5] = { name => "MOBILE_NODE_RESPONSE",
+ func => \&ConvertData };
+$avpCodesRev[6] = { name => "MOBILE_NODE_HOME_ADDRESS",
+ func => \&ConvertIpAddr };
+$avpCodesRev[7] = { name => "HOME_AGENT_ADDRESS",
+ func => \&ConvertIpAddr };
+$avpCodesRev[8] = { name => "RESULT_CODE",
+ func => \&ConvertInteger };
+$avpCodesRev[9] = { name => "REGISTRATION_REPLY",
+ func => \&ConvertData };
+$avpCodesRev[10] = { name => "MN_FA_SPI",
+ func => \&ConvertInteger };
+$avpCodesRev[11] = { name => "MN_FA_KEY",
+ func => \&ConvertData };
+$avpCodesRev[12] = { name => "FA_HA_SPI",
+ func => \&ConvertInteger };
+$avpCodesRev[13] = { name => "FA_HA_KEY",
+ func => \&ConvertData };
+$avpCodesRev[14] = { name => "SESSION_TIMEOUT",
+ func => \&ConvertInteger };
+$avpCodesRev[15] = { name => "HA_FA_KEY",
+ func => \&ConvertData };
+$avpCodesRev[16] = { name => "FA_MN_KEY",
+ func => \&ConvertData };
+$avpCodesRev[17] = { name => "MN_HA_SPI",
+ func => \&ConvertInteger };
+$avpCodesRev[18] = { name => "MN_HA_KEY",
+ func => \&ConvertData };
+$avpCodesRev[19] = { name => "HA_MN_KEY",
+ func => \&ConvertData };
+$avpCodesRev[20] = { name => "SESSION_TIMEOUT_1",
+ func => \&ConvertInteger };
+$avpCodesRev[21] = { name => "SESSION_TIME",
+ func => \&ConvertInteger };
+
+
+my $CurrentHandle = 1;
+
+# Data Handling routines
+
+# Takes a NULL terminated string, and unpacks it
+sub ConvertString($;$;) {
+ my ($data,$fromOrTo) = @_;
+ if ($fromOrTo == $CONVERT_FROM) {
+ return unpack("A*", $data);
+ } else {
+ return pack("A*",$data);
+ }
+} # ConvertString
+
+#Converts Data to a printable string
+sub ConvertData($;) {
+ my ($data,$fromOrTo) = @_;
+ my $buffer;
+ my $returnBuffer="";
+ my $i;
+
+ if ($fromOrTo == $CONVERT_FROM) {
+ for ($i=0;$i < length($data); $i ++) {
+ $char = unpack("c", substr($data,$i,1));
+ $char = $char & 0xff;
+ $buffer = sprintf("0x%02x ", $char);
+ $returnBuffer = $returnBuffer . $buffer;
+ }
+ # remove the final space
+ return substr($returnBuffer,0,length($returnBuffer) -1 );
+ } else {
+ # Assume it's already binary.
+ return $data;
+ }
+} # ConvertData
+
+sub ConvertInteger($;) {
+ my ($data,$fromOrTo) = @_;
+
+ if ($fromOrTo == $CONVERT_FROM) {
+ if (length($data) != 4) {
+ print "Error: Integer is not 4 bytes long . . treating as data";
+ return ConvertData($data);
+ }
+ return unpack("N", $data);
+ } else {
+
+ return pack("N", $data);
+ }
+} #ConvertInteger
+sub ConvertIpAddr($;) {
+ my ($data,$fromOrTo) = @_;
+
+ if ($fromOrTo == $CONVERT_FROM) {
+ if (length($data) != 4) {
+ print "Error: IPAddr is not 4 bytes long . . treating as data";
+ return ConvertData($data);
+ }
+ return inet_ntoa($data);
+ } else {
+ return inet_aton($data);
+ }
+
+} # ConvertIpAddr
+
+sub initSocket {
+ my $port = shift;
+ $port = $DEFAULT_PORT if (!defined $port);
+
+ my $proto = getprotobyname('tcp');
+ my $paddr = sockaddr_in($port,INADDR_ANY) ;
+
+ socket(SOCKET,PF_INET,SOCK_STREAM,$proto) or die "socket: $!\n";
+ setsockopt(SOCKET, SOL_SOCKET, SO_REUSEADDR,
+ pack("l", 1)) || die "setsockopt: $!";
+ bind (SOCKET,$paddr) or die "bind: $!\n";
+
+ # make socket non-blocking
+ select SOCKET; $|=1; select STDOUT;
+
+ my $fh = \*SOCKET;
+ return $fh;
+} # initSocket
+
+sub REAPER {
+ $waitedpid = wait;
+ $SIG{CHLD} = \&REAPER; # loathe sysV
+ print "reaped $waitedpid" . ($? ? " with exit $?" : '') . "\n";
+} # REAPER
+
+sub spawn {
+ my $pid;
+ if (!defined($pid = fork)) {
+ warn "cannot fork: $!\n";
+ return;
+ } elsif ($pid) {
+ warn "begat $pid\n";
+ return; # I'm the parent
+ }
+ # else I'm the child -- go spawn
+ sleep (1);
+ exit &processMessages(\*Client);
+} # spawn
+
+# seperate the avps out into a hash containing
+# the data (indexed by AVP name)
+sub processAVPs($;) {
+ my $avpBuffer = shift;
+ my ($avpCode, $avpLength, $data);
+ my %entry;
+ my $avpName;
+ my %ReturnHash;
+ my %EntryHash;
+ my $entry;
+
+ print "DEBUG: AVPS:\n";
+ while (length($avpBuffer) > 0) {
+ ($avpCode,$avpLength) = unpack("N N", $avpBuffer);
+ # Now validate the code.
+ $entry = $avpCodesRev[$avpCode];
+ if (!defined($entry)) {
+ print "ERROR: Invalid AVP Code: $avpCode\n";
+ } else {
+ %EntryHash = %$entry;
+ # Valid Avp . . . add it
+ $avpName = $EntryHash{name};
+ $ReturnHash{$avpName} = convertAvp($avpName,
+ substr($avpBuffer,4+4, $avpLength - (4+4)),
+ $CONVERT_FROM);
+ print "DEBUG: $avpName(" . $ReturnHash{$avpName} . ")\n";
+ }
+ # And, fix the buffer
+ $avpBuffer = substr($avpBuffer,$avpLength);
+ }
+
+ return \%ReturnHash;
+
+} # processAVPs
+
+sub processMessages {
+ my $fh = shift;
+ my $buffer;
+ my $bytesLeftToRead;
+ my ($commandCode, $handle, $length);
+ my ($avpCode,$avpLength);
+ my $avpBytesLeftToRead;
+ my $rc;
+ my $response;
+
+ print "processMessages: executing!\n";
+
+ while ( 1 ) {
+# print "Reading in 4+4+4 bytes!\n";
+ $rc = recv ($fh,$buffer,4+4+4,0);
+ die "Unable to read in header ($!)\n" if (!defined $rc);
+ return (0) if (!length($buffer));
+# print "DEBUG: read " . length($buffer) . " bytes when expecing 4+4+4\n";
+ ($commandCode, $handle, $length) = unpack("N N N", $buffer);
+ print "Received commandCode $commandCode, handle $handle" .
+ ", length $length\n";
+
+ $bytesLeftToRead = $length - (4+4+4);
+
+ my $offset=0;
+ my $bytesRead = 0;
+ # And, read in the rest of the packet.
+ while ($bytesLeftToRead) {
+ $rc = recv($fh,$buffer,$bytesLeftToRead,$offset);
+ die "Unable to read AVPs" if (!defined $rc);
+
+ $bytesRead = length($buffer) - $offset;
+ return (0) if ($bytesRead == 0);
+# print "DEBUG: readChunk " . $bytesRead . " bytes\n";
+ $bytesLeftToRead -= $bytesRead;
+ $offset += $bytesRead;
+ }
+
+ print "DEBUG: read " . length($buffer) . " bytes total.\n";
+
+ # Ok, now take apart the packets
+ $response = processMessage($commandCode, $handle, $length, $buffer);
+ print $fh $response;
+ }
+} # processMessages
+
+sub processMessage($;$;$;$;) {
+ my ($commandCode, $handle, $length, $avpBuffer) = @_;
+
+ my $entry = $commandCodesRev[$commandCode];
+ if (!defined $entry) {
+ print "IllegalCommand Code! <" . $commandCode . ">\n";
+ sendError();
+ return 0;
+ }
+ my %hash = %$entry;
+ print "Recieved " . $hash{name} . "\n";
+
+ # Now that we have a good entry, take apart the AVPs
+ my $avpRef = processAVPs($avpBuffer);
+ my %avps = %$avpRef;
+
+ my $func = $hash{func};
+ return &$func($hash{name}, $handle, \%avps);
+} # processMessage
+
+
+sub convertAvp($;$;$;) {
+ my ($avpName,$data,$fromOrTo) = @_;
+ my $avpCode = $avpCodes{$avpName};
+ my $entry = $avpCodesRev[$avpCode];
+ my %entryHash;
+ my $conversionFunc;
+
+ if (!defined($entry)) {
+ print "ERROR: Invalid AVP: $avpName\n";
+ return undef;
+ } else {
+ %entryHash = %$entry;
+ # Valid Avp . . . add it
+ $conversionFunc = $entryHash{func};
+ return
+ &$conversionFunc($data, $fromOrTo);
+ }
+} #addAvp
+
+sub processOpenSessionRequest($;$;$;) {
+ my ($name, $handle, $avpRef) = @_;
+ my %InAvpHash = %$avpRef;
+ my %OutAvpHash;
+
+ print "OpenSessionRequest($name)\n";
+
+ # MobileNode NAI
+ $OutAvpHash{MOBILE_NODE_NAI} = convertAvp("MOBILE_NODE_NAI",
+ $InAvpHash{MOBILE_NODE_NAI},
+ $CONVERT_TO);
+
+ # ResultCode
+ $OutAvpHash{RESULT_CODE} = convertAvp("RESULT_CODE",0,$CONVERT_TO);
+
+ # ForeignAgentNAI
+ $OutAvpHash{FOREIGN_AGENT_NAI} = convertAvp("FOREIGN_AGENT_NAI",
+ $InAvpHash{FOREIGN_AGENT_NAI},
+ $CONVERT_TO);
+
+ # Registration Reply
+ # WORK
+
+ # MN-FA SPI
+ $OutAvpHash{MN_FA_SPI} = convertAvp("MN_FA_SPI",257,$CONVERT_TO);
+ # WORK
+
+ # MN-FA Key
+ # WORK
+
+ # FA-HA SPI
+ $OutAvpHash{FA_HA_SPI} = convertAvp("FA_HA_SPI",258,$CONVERT_TO);
+ # WORK
+
+ # FA-HA Key
+ # WORK
+
+ # HomeAgentAddress
+ $OutAvpHash{HOME_AGENT_ADDRESS} = convertAvp("HOME_AGENT_ADDRESS",
+ "192.168.168.1",
+ $CONVERT_TO);
+
+ # Mobile Node Home ADdress
+ $OutAvpHash{MOBILE_NODE_HOME_ADDRESS} = convertAvp("MOBILE_NODE_HOME_ADDRESS",
+ "192.168.168.2",
+ $CONVERT_TO);
+
+ # Session Timeout
+ $OutAvpHash{SESSION_TIMEOUT} = convertAvp("SESSION_TIMEOUT",10,$CONVERT_TO);
+
+ # Send back a changing hancle of one.
+ return &buildResponse(\%OutAvpHash,$commandCodes{MOBILE_IP_OPEN_SESSION_ANSWER},
+ $CurrentHandle++);
+
+
+ return 0;
+
+} #processOpenSessionRequest
+
+sub processAccountingStart($;$;$;) {
+ my ($name, $handle, $avpRef) = @_;
+ my %InAvpHash = %$avpRef;
+ my %OutAvpHash;
+
+ print "ProcessAccountingStart($name) handle = $handle\n";
+
+ # MobileNode NAI
+ $OutAvpHash{MOBILE_NODE_NAI} = convertAvp("MOBILE_NODE_NAI",
+ $InAvpHash{MOBILE_NODE_NAI},
+ $CONVERT_TO);
+ # ResultCode
+ $OutAvpHash{RESULT_CODE} = convertAvp("RESULT_CODE",0,$CONVERT_TO);
+
+ # ForeignAgentNAI
+ $OutAvpHash{FOREIGN_AGENT_NAI} = convertAvp("FOREIGN_AGENT_NAI",
+ $InAvpHash{FOREIGN_AGENT_NAI},
+ $CONVERT_TO);
+ # Send back the passed in handle.
+ return &buildResponse(\%OutAvpHash,$commandCodes{MOBILE_IP_ACCOUNTING_START_ANSWER},
+ $handle);
+
+} #processAccountingStart
+
+sub processAccountingInterim($;$;$;) {
+ my ($name, $handle, $avpRef) = @_;
+ my %InAvpHash = %$avpRef;
+ my %OutAvpHash;
+
+ print "ProcessAccountingInterim($name)\n";
+
+ # MobileNode NAI
+ $OutAvpHash{MOBILE_NODE_NAI} = convertAvp("MOBILE_NODE_NAI",
+ $InAvpHash{MOBILE_NODE_NAI},
+ $CONVERT_TO);
+ # ResultCode
+ $OutAvpHash{RESULT_CODE} = convertAvp("RESULT_CODE",0,$CONVERT_TO);
+
+ # ForeignAgentNAI
+ $OutAvpHash{FOREIGN_AGENT_NAI} = convertAvp("FOREIGN_AGENT_NAI",
+ $InAvpHash{FOREIGN_AGENT_NAI},
+ $CONVERT_TO);
+ # Send back the passed in handle.
+ return &buildResponse(\%OutAvpHash,$commandCodes{MOBILE_IP_ACCOUNTING_INTERIM_ANSWER},
+ $handle);
+
+} #processAccountingInterim
+
+sub processAccountingStop($;$;$;) {
+ my ($name, $handle, $avpRef) = @_;
+ my %InAvpHash = %$avpRef;
+ my %OutAvpHash;
+
+ print "ProcessAccountingStop($name)\n";
+
+ # MobileNode NAI
+ $OutAvpHash{MOBILE_NODE_NAI} = convertAvp("MOBILE_NODE_NAI",
+ $InAvpHash{MOBILE_NODE_NAI},
+ $CONVERT_TO);
+ # ResultCode
+ $OutAvpHash{RESULT_CODE} = convertAvp("RESULT_CODE",0,$CONVERT_TO);
+
+ # ForeignAgentNAI
+ $OutAvpHash{FOREIGN_AGENT_NAI} = convertAvp("FOREIGN_AGENT_NAI",
+ $InAvpHash{FOREIGN_AGENT_NAI},
+ $CONVERT_TO);
+ # Send back the passed in handle.
+ return &buildResponse(\%OutAvpHash,$commandCodes{MOBILE_IP_ACCOUNTING_STOP_ANSWER},
+ $handle);
+
+} #processAccountingStop
+
+sub processCloseSession($;$;$;) {
+ my ($name, $handle, $avpRef) = @_;
+ my %InAvpHash = %$avpRef;
+ my %OutAvpHash;
+
+ print "ProcessCloseSession($name)\n";
+
+ # MobileNode NAI
+ $OutAvpHash{MOBILE_NODE_NAI} = convertAvp("MOBILE_NODE_NAI",
+ $InAvpHash{MOBILE_NODE_NAI},
+ $CONVERT_TO);
+ # ResultCode
+ $OutAvpHash{RESULT_CODE} = convertAvp("RESULT_CODE",0,$CONVERT_TO);
+
+ # ForeignAgentNAI
+ $OutAvpHash{FOREIGN_AGENT_NAI} = convertAvp("FOREIGN_AGENT_NAI",
+ $InAvpHash{FOREIGN_AGENT_NAI},
+ $CONVERT_TO);
+ # Send back the passed in handle.
+ return &buildResponse(\%OutAvpHash,$commandCodes{MOBILE_IP_CLOSE_SESSION_ANSWER},
+ $handle);
+
+} #processCloseSession
+
+# This routine will package up the passed in AVPS
+sub buildResponse($;$;$;) {
+ my ($avpRef,$code,$handle) = @_;
+ my %avps = %$avpRef;
+
+ my $header;
+ my $body = "";
+
+ my $avp;
+ my $key;
+
+ foreach $key (keys(%avps)) {
+ print "Adding Key = $key length = " . (4+4+length($avps{$key})) .
+ " to response\n";
+ $avp = pack("N N", $avpCodes{$key}, (4+4+length($avps{$key}))) .
+ $avps{$key};
+ $body = $body . $avp;
+ }
+
+ print "Building response with code = $code, handle = $handle, length = " .
+ (length($body) +4 +4 +4) . "\n";
+
+ # Now build header
+ $header = pack ("N N N",$code, $handle, length($body)+4+4+4);
+
+ return $header . $body;
+
+} #buildResponse
+
+sub niy($;$;$;$) {
+ my ($name, $handle, $length, $avpBuffer) = @_;
+ print "Error: I don't know how to handle $name (Not Implemented Yet)\n";
+} #niy
+
+ # Main ( multithreaded )
+
+
+my $socket = initSocket();
+
+listen($socket,5);
+
+my $paddr;
+
+$SIG{CHLD} = \&REAPER; # Catch zombies
+
+print "Waiting for connections to port $DEFAULT_PORT\n";
+
+while (1) {
+ $paddr = accept(Client,$socket);
+ if (defined($paddr)) {
+ my ($port,$iaddr) = sockaddr_in($paddr);
+ my $name = gethostbyaddr($iaddr,AF_INET);
+ print "connection from $name [" . inet_ntoa($iaddr) .
+ "] at port $port\n";
+ select Client; $|=1; select STDOUT;
+
+ &spawn;
+ } else {
+ if ($! != &EINTR) {
+ die "Error on accept! $! != ". &EINTR . " ($!)\n";
+ }
+ }
+} # main for
+
+
+
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mipagent/hash.c b/usr/src/cmd/cmd-inet/usr.lib/mipagent/hash.c
new file mode 100644
index 0000000000..7b6daed7e7
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mipagent/hash.c
@@ -0,0 +1,1013 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * file: hash.c
+ *
+ * This file contains all routines used to manage hash tables.
+ *
+ * The locking strategy behind hash.c is actually quite simple,
+ * but does need some explaining. Basically, we have three different
+ * locks to worry about; one at the hash table level, one at the
+ * bucket level and one at the node level.
+ *
+ * +===================+
+ * | Hash | Hash | +---------+ +---------+
+ * | Table | Bucket |<--->|HashEntry|<--->|HashEntry|
+ * | | | +---------+ +---------+
+ * | * | * | / \ / \
+ * | | | | |
+ * | | | \ / \ /
+ * | | | +---------+ +---------+
+ * | | | | * Node | | * Node |
+ * +===================+ +---------+ +---------+
+ *
+ * * = Lock is present
+ *
+ * When we walk through the Hash Table to add an item, it first
+ * gets the hash bucket index, and locks the whole row (or bucket)
+ * with a write lock. a HashEntry is then added at the head of the
+ * bucket queue. If a node lock was requested, via a function
+ * parameter, the node is locked. This is particularly useful if
+ * additional work will be done to the node. The Hash Table lock
+ * is then locked with a write lock, and the table counter is
+ * incremented, then the lock is unlocked. Lastly, the hash
+ * bucket is unlocked, and the function returns.
+ *
+ * When the code needs to find a node in the hash table, the
+ * hash bucket (row) is locked, and all nodes in the row are
+ * compare with the key provided. The caller may also provide
+ * a pointer to a helper routine, which is used to further
+ * qualify searches. If a match is found, the node is locked
+ * (if requested), and the pointer to the node is retruned to
+ * the caller.
+ *
+ * If a node is to be deleted from the table, the row is write
+ * locked, the row is searched for a match, based on the key and
+ * the data to the pointer. If a match is found, the HashEntry
+ * is deleted. The table lock is then write locked, decremented
+ * and unlocked. Finally, the bucket lock is released.
+ *
+ */
+#include <stdio.h>
+#include <syslog.h>
+#include <stdlib.h>
+#include <string.h>
+#include "md5.h"
+#include "mip.h"
+#include "agent.h"
+
+extern boolean_t shutdown_flag;
+
+/*
+ * The following macro is used to lock a node.
+ */
+#define LOCK_NODE(lockType, data) \
+ switch (lockType) { \
+ case LOCK_WRITE: \
+ (void) rw_wrlock((rwlock_t *)data); \
+ break; \
+ case LOCK_READ: \
+ (void) rw_rdlock((rwlock_t *)data); \
+ break; \
+ case LOCK_NONE: \
+ default: \
+ break; \
+ }
+
+/*
+ * Function: InitHash
+ *
+ * Arguments: htbl - Pointer to Hash Table
+ *
+ * Description: This function will memset the Hash Table and
+ * initialize the hash table's read/write locks.
+ * We do not need to destroy the locks since
+ * Hash Tables are never released until the daemon
+ * is shutdown.
+ *
+ * Returns: int, 0 if successful
+ */
+int
+InitHash(struct hash_table *htbl)
+{
+ int i;
+
+ (void) memset(htbl, 0, sizeof (struct hash_table));
+ if (rwlock_init(&htbl->hashLock, USYNC_THREAD, NULL)) {
+ syslog(LOG_CRIT, "Unable to initialize read/write lock");
+ return (-1);
+ }
+ for (i = 0; i < HASH_TBL_SIZE; i++) {
+ if (rwlock_init(&htbl->bucketLock[i], USYNC_THREAD, NULL)) {
+ syslog(LOG_CRIT, "Unable to initialize read/write lock");
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * Function: hashStr
+ *
+ * Arguments: str - String to be hashed
+ * length - Length of string
+ *
+ * Description: This function will generate a hash
+ * based on the string provided to
+ * allow for strings to be used as keys
+ * in the hashing functions.
+ *
+ * Note that we currently use MD5, and a
+ * more suitable algorithm will be implemented
+ * in the future (i.e. patricia)
+ *
+ * Returns: unsigned char containing the hash value.
+ */
+static unsigned char
+hashStr(unsigned char *str, int length)
+{
+#define TRY_EFFICIENT_HASH
+#ifdef TRY_EFFICIENT_HASH
+ int h = 0;
+ int i;
+
+ for (i = 0; i < length; i++)
+ h = (64 * h + str[i]) % 256; /* Keep it within a char */
+ return (h);
+#else
+ MD5_CTX context;
+ unsigned char authenticator[16];
+
+ if (str == NULL || length <= 0)
+ return (0);
+
+ MD5Init(&context);
+ MD5Update(&context, str, length);
+ MD5Final(authenticator, &context);
+
+ return (authenticator[0]);
+#endif
+
+} /* hashStr */
+
+/*
+ * Function: linkHashTableEntryUint
+ *
+ * Arguments: htbl - Pointer to Hash Table
+ * key - key to be used for bucket generation
+ * data - Pointer to the data
+ * lockType - The Lock type, which can be:
+ * LOCK_NONE - No Lock
+ * LOCK_READ - Read Lock
+ * LOCK_WRITE - Write Lock
+ *
+ * Description: This function will allocate a Hash Entry,
+ * find the appropriate bucket based on the key
+ * provided, and add the node to the bucket's
+ * queue.
+ *
+ * If a lock type was selected, the node's will
+ * be locked upon return. The caller is then
+ * responsible for unlocking the node when it is
+ * finished with the data.
+ *
+ * Returns: int, 0 if successful
+ */
+int
+linkHashTableEntryUint(HashTable *htbl, uint32_t key, void *data, int lockType)
+{
+ HashEntry *p;
+ HashEntry *q;
+ int index;
+
+ if ((p = (HashEntry *)malloc(sizeof (HashEntry))) == NULL) {
+ syslog(LOG_CRIT, "FATAL: Unable to allocate HashEntry");
+ return (-1);
+ }
+
+ index = HASHIT(key);
+
+ (void) rw_wrlock(&htbl->bucketLock[index]);
+
+ q = htbl->buckets[index];
+
+ /*
+ * If the unique flag is set, make sure the entry
+ * is not in this bucket.
+ */
+ if (htbl->uniqueData) {
+ HashEntry *r;
+ /* Make sure value is not already in the table */
+ for (r = q; r != NULL; r = r->next)
+ if (r->key == key) {
+ /* Error! It is here! */
+ syslog(LOG_ERR, "ERROR: Key already exists!");
+ (void) rw_unlock(&htbl->bucketLock[index]);
+ return (-2);
+ }
+ } /* end if unuque */
+
+ htbl->buckets[index] = p;
+ p->next = q;
+ p->data = (void *)data;
+ p->key = key;
+ p->hashKeyType = HASH_INT_KEY;
+
+ /*
+ * Lock and increment the counter.
+ */
+ (void) rw_wrlock(&htbl->hashLock);
+ htbl->size++;
+ (void) rw_unlock(&htbl->hashLock);
+
+ /*
+ * We now lock the data structure using the locking type
+ * specified by the caller.
+ */
+ LOCK_NODE(lockType, data);
+
+ (void) rw_unlock(&htbl->bucketLock[index]);
+
+
+ return (0);
+}
+
+/*
+ * Function: linkHashTableEntryString
+ *
+ * Arguments: htbl - Pointer to Hash Table
+ * p - Pointer to Hash Entry (if available)
+ * key - Pointer to the keying information
+ * keyLen - Length of the keying information
+ * data - Pointer to the data
+ * lockType - The Lock type, which can be:
+ * LOCK_NONE - No Lock
+ * LOCK_READ - Read Lock
+ * LOCK_WRITE - Write Lock
+ *
+ * Description: This function will allocate a Hash Entry,
+ * find the appropriate bucket based on the key
+ * provided, and add the node to the bucket's
+ * queue.
+ *
+ * It is possible to provide a HashEntry pointer,
+ * and if one is provided this function will not
+ * allocate a new one. This allows for the caller
+ * to move a Hash Entry from one Hash Table to
+ * another.
+ *
+ * If a lock type was selected, the node's will
+ * be locked upon return. The caller is then
+ * responsible for unlocking the node when it is
+ * finished with the data.
+ *
+ * Returns: int, 0 if successful
+ */
+int
+linkHashTableEntryString(HashTable *htbl, unsigned char *key, uint32_t keyLen,
+ void *data, int lockType)
+{
+ HashEntry *p;
+ HashEntry *q;
+ int index;
+
+ if (keyLen > 255) {
+ syslog(LOG_ERR, "FATAL: Keys must be 255 chars or less");
+ return (-1);
+ }
+
+ if ((p = (HashEntry *)malloc(sizeof (HashEntry))) == NULL) {
+ syslog(LOG_CRIT, "FATAL: Unable to allocate HashEntry");
+ return (-1);
+ }
+
+ if ((p->keyData = (unsigned char *)malloc(keyLen)) == NULL) {
+ syslog(LOG_CRIT, "FATAL: Unable to allocate HashEntry");
+ free(p);
+ return (-1);
+ }
+
+
+ index = hashStr(key, keyLen);
+
+ (void) rw_wrlock(&htbl->bucketLock[index]);
+
+ q = htbl->buckets[index];
+
+ /*
+ * If the unique flag is set, make sure the entry
+ * is not in this bucket.
+ */
+ if (htbl->uniqueData) {
+ HashEntry *r;
+ /* Make sure value is not already in the table */
+ for (r = q; r != NULL; r = r->next)
+ if (r->keyLen == keyLen &&
+ strncmp((const char *)r->keyData,
+ (const char *)key,
+ keyLen) == 0) {
+ /* Error! It is here! */
+ syslog(LOG_ERR, "ERROR: Key already exists!");
+ (void) rw_unlock(&htbl->bucketLock[index]);
+ return (-2);
+ }
+ } /* end if unuque */
+
+ htbl->buckets[index] = p;
+ p->next = q;
+ p->data = (void *)data;
+ p->key = 0;
+ p->hashKeyType = HASH_STR_KEY;
+ (void) memcpy(p->keyData, key, keyLen);
+ p->keyLen = keyLen;
+
+ /*
+ * Lock and increment the counter.
+ */
+ (void) rw_wrlock(&htbl->hashLock);
+ htbl->size++;
+ (void) rw_unlock(&htbl->hashLock);
+
+ /*
+ * We now lock the data structure using the locking type
+ * specified by the caller.
+ */
+ LOCK_NODE(lockType, data);
+
+ (void) rw_unlock(&htbl->bucketLock[index]);
+
+ return (0);
+}
+
+/*
+ * Function: delHashTableEntryUint
+ *
+ * Arguments: htbl - Pointer to Hash Table
+ * data - Pointer to the data
+ * key - key to be used for bucket generation
+ * lockType - The Lock type, which can be:
+ * LOCK_NONE - No Lock
+ * LOCK_READ - Read Lock
+ * LOCK_WRITE - Write Lock
+ *
+ * Description: This function will find the node in the
+ * hash table, and delete the entry.
+ *
+ * If a lock type was selected, the node's will
+ * be locked upon return. The caller is then
+ * responsible for unlocking the node when it is
+ * finished with the data.
+ *
+ * Returns: int, 0 if successful
+ */
+boolean_t
+delHashTableEntryUint(HashTable *htbl, void *data, uint32_t key,
+ int lockType)
+{
+ HashEntry *p, *tmp;
+ HashEntry *q = NULL;
+ int index;
+ int found = _B_FALSE;
+
+ index = HASHIT(key);
+
+ (void) rw_wrlock(&htbl->bucketLock[index]);
+
+ p = htbl->buckets[index];
+ while (p) {
+ if (p->hashKeyType == HASH_INT_KEY &&
+ p->data == data && p->key == key) {
+ if (p == htbl->buckets[index])
+ htbl->buckets[index] = p->next;
+ else
+ q->next = p->next;
+
+ tmp = p;
+ p = p->next;
+
+ free(tmp);
+
+ /*
+ * We delete the entry, let's decrement the counter.
+ */
+ (void) rw_wrlock(&htbl->hashLock);
+ htbl->size--;
+ (void) rw_unlock(&htbl->hashLock);
+
+ found = _B_TRUE;
+
+ break;
+ } else {
+ q = p;
+ p = p->next;
+ }
+ }
+
+ /*
+ * We now lock the data structure using the locking type
+ * specified by the caller.
+ */
+ LOCK_NODE(lockType, data);
+
+ (void) rw_unlock(&htbl->bucketLock[index]);
+
+ return (found);
+}
+
+
+/*
+ * Function: delHashTableEntryString
+ *
+ * Arguments: htbl - Pointer to Hash Table
+ * data - Pointer to the data
+ * key - key to be used for bucket generation
+ * lockType - The Lock type, which can be:
+ * LOCK_NONE - No Lock
+ * LOCK_READ - Read Lock
+ * LOCK_WRITE - Write Lock
+ *
+ * Description: This function will find the node in the
+ * hash table, and delete the entry.
+ *
+ * If a lock type was selected, the node's will
+ * be locked upon return. The caller is then
+ * responsible for unlocking the node when it is
+ * finished with the data.
+ *
+ * Returns: int, 0 if successful
+ */
+boolean_t
+delHashTableEntryString(HashTable *htbl, void *data, unsigned char *key,
+ uint32_t keyLen, int lockType)
+{
+ HashEntry *p, *tmp;
+ HashEntry *q = NULL;
+ int index;
+ int found = _B_FALSE;
+
+ index = hashStr(key, keyLen);
+
+ /*
+ * Lock the bucket
+ */
+ (void) rw_wrlock(&htbl->bucketLock[index]);
+
+ p = htbl->buckets[index];
+ while (p) {
+ if (p->hashKeyType == HASH_STR_KEY && p->data == data &&
+ p->keyLen == keyLen &&
+ !memcmp(p->keyData, key, keyLen)) {
+ if (p == htbl->buckets[index])
+ htbl->buckets[index] = p->next;
+ else
+ q->next = p->next;
+
+ tmp = p;
+ p = p->next;
+
+ /*
+ * Free the key data AND the Hash Entry.
+ */
+ free(tmp->keyData);
+ free(tmp);
+
+ /*
+ * We delete the entry, let's decrement the counter.
+ */
+ (void) rw_wrlock(&htbl->hashLock);
+ htbl->size--;
+ (void) rw_unlock(&htbl->hashLock);
+
+ found = _B_TRUE;
+
+ break;
+ } else {
+ q = p;
+ p = p->next;
+ }
+ }
+
+ /*
+ * We now lock the data structure using the locking type
+ * specified by the caller.
+ */
+ LOCK_NODE(lockType, data);
+
+ (void) rw_unlock(&htbl->bucketLock[index]);
+
+ return (found);
+}
+
+
+/*
+ * Function: findHashTableEntryUint
+ *
+ * Arguments: htbl - Pointer to Hash Table
+ * key - key to be used for bucket generation
+ * lockType - The Lock type, which can be:
+ * LOCK_NONE - No Lock
+ * LOCK_READ - Read Lock
+ * LOCK_WRITE - Write Lock
+ * fcnt() - Function Pointer
+ * p1 - First parameter to match
+ * p2 - Second parameter to match
+ * p3 - Third parameter to match
+ *
+ * Description: This function will find the node in the
+ * hash table using the key, and return the
+ * data associated with the entry. If a function
+ * pointer was passed, this function will call
+ * the pointer to further qualify the search.
+ *
+ * If the function called returns _B_TRUE, this
+ * function will assume that the hash entry in
+ * question is the one we were looking for.
+ *
+ * If a lock type was selected, the node's will
+ * be locked upon return. The caller is then
+ * responsible for unlocking the node when it is
+ * finished with the data.
+ *
+ * Returns: int, 0 if successful
+ */
+void *
+findHashTableEntryUint(HashTable *htbl, uint32_t key, int lockType,
+ boolean_t (*fcnt)(void *, uint32_t, uint32_t, uint32_t), uint32_t p1,
+ uint32_t p2, uint32_t p3)
+{
+ HashEntry *p;
+ int index;
+
+ index = HASHIT(key);
+
+ /*
+ * Lock the bucket
+ */
+ (void) rw_rdlock(&htbl->bucketLock[index]);
+
+ p = htbl->buckets[index];
+ while (p) {
+ if (p->hashKeyType == HASH_INT_KEY && p->key == key) {
+ if ((fcnt == NULL) ||
+ (fcnt && fcnt(p->data, p1, p2, p3))) {
+ /*
+ * We now lock the data structure using the
+ * locking type specified by the caller.
+ */
+ LOCK_NODE(lockType, p->data);
+ break;
+ }
+ }
+ p = p->next;
+ }
+
+ /*
+ * Unlock the bucket
+ */
+ (void) rw_unlock(&htbl->bucketLock[index]);
+
+ if (p) {
+ return (p->data);
+ } else {
+ return (NULL);
+ }
+}
+
+
+/*
+ * Function: findHashTableEntryString
+ *
+ * Arguments: htbl - Pointer to Hash Table
+ * key - Pointer to the keying information
+ * keyLen - Length of the keying information
+ * lockType - The Lock type, which can be:
+ * LOCK_NONE - No Lock
+ * LOCK_READ - Read Lock
+ * LOCK_WRITE - Write Lock
+ * fcnt() - Function Pointer
+ * p1 - First parameter to match
+ * p2 - Second parameter to match
+ * p3 - Third parameter to match
+ *
+ * Description: This function will find the node in the
+ * hash table using the key, which is a string,
+ * and return the data associated with the entry.
+ *
+ * If a lock type was selected, the node's will
+ * be locked upon return. The caller is then
+ * responsible for unlocking the node when it is
+ * finished with the data.
+ *
+ * Returns: int, 0 if successful
+ */
+void *
+findHashTableEntryString(HashTable *htbl, unsigned char *key,
+ uint32_t keyLen, int lockType,
+ boolean_t (*fcnt)(void *, uint32_t, uint32_t, uint32_t), uint32_t p1,
+ uint32_t p2, uint32_t p3)
+{
+ HashEntry *p;
+ int index;
+
+ index = hashStr(key, keyLen);
+
+ /*
+ * Lock the bucket
+ */
+ (void) rw_rdlock(&htbl->bucketLock[index]);
+
+ p = htbl->buckets[index];
+ while (p) {
+ if (p->hashKeyType == HASH_STR_KEY && p->keyLen == keyLen &&
+ !memcmp(p->keyData, key, keyLen)) {
+ if ((fcnt == NULL) ||
+ (fcnt && fcnt(p->data, p1, p2, p3))) {
+ /*
+ * We now lock the data structure using the
+ * locking type specified by the caller.
+ */
+ LOCK_NODE(lockType, p->data);
+ break;
+ }
+ }
+ p = p->next;
+ }
+
+ /*
+ * Unlock the bucket
+ */
+ (void) rw_unlock(&htbl->bucketLock[index]);
+
+ if (p) {
+ return (p->data);
+ } else {
+ return (NULL);
+ }
+} /* findHashTableEntryString */
+
+/*
+ * Function: findHashTableEntryString
+ *
+ * Arguments: htbl - Pointer to Hash Table
+ * data - Pointer to the data
+ * key - Pointer to the keying information
+ * keyLen - Length of the keying information
+ * newkey - key of type uint32_t used to find new bucket
+ *
+ * Description: This function is used to move a node that
+ * was previously hashed based on a string to
+ * a new bucket, now hashed with a 32 bit integer.
+ *
+ * Returns: boolean_t, _B_TRUE if successful
+ */
+boolean_t
+changeHashEntryStringToUint(HashTable *htbl, void *data, unsigned char *key,
+ uint32_t keyLen, uint32_t newKey)
+{
+ HashEntry *p;
+ HashEntry *q = NULL;
+ HashEntry *pToMove = NULL;
+ int index;
+
+ /*
+ * First let's remove the Hash Entry from the old bucket
+ */
+ index = hashStr(key, keyLen);
+
+ (void) rw_wrlock(&htbl->bucketLock[index]);
+
+ p = htbl->buckets[index];
+ while (p) {
+ if (p->data == data && p->keyLen == keyLen &&
+ !memcmp(p->keyData, key, keyLen) &&
+ p->hashKeyType == HASH_STR_KEY) {
+ if (p == htbl->buckets[index])
+ htbl->buckets[index] = p->next;
+ else
+ q->next = p->next;
+
+ /*
+ * Free the keyData since this one was
+ * a string.
+ */
+ p->keyLen = 0;
+ free(p->keyData);
+ p->keyData = NULL;
+
+ pToMove = p;
+
+ break;
+ } else {
+ q = p;
+ p = p->next;
+ }
+ }
+
+ /*
+ * Unlock the bucket
+ */
+ (void) rw_unlock(&htbl->bucketLock[index]);
+
+ if (pToMove) {
+ /*
+ * Cool, we've found it. Now let's move it to
+ * a new bucket, index on an uint32_t instead.
+ * If uniqueness is requested, we need to make
+ * the check here. XXX
+ */
+ index = HASHIT(newKey);
+
+ (void) rw_wrlock(&htbl->bucketLock[index]);
+
+ q = htbl->buckets[index];
+
+ htbl->buckets[index] = pToMove;
+
+ pToMove->next = q;
+
+ pToMove->hashKeyType = HASH_INT_KEY;
+ pToMove->key = newKey;
+
+ (void) rw_unlock(&htbl->bucketLock[index]);
+ }
+
+ return (pToMove != NULL);
+} /* changeHashEntryStringToUint */
+
+/*
+ * Function: getAllHashTableEntries
+ *
+ * Arguments: htbl - Pointer to Hash Table
+ * fcnt() - Function Pointer
+ * lockType - The Lock type, which can be:
+ * LOCK_NONE - No Lock
+ * LOCK_READ - Read Lock
+ * LOCK_WRITE - Write Lock
+ * p1 - First parameter to match
+ * shutdown_flag - If set, we are shutting down
+ *
+ * Description: This function is mostly used by the gargabe collected
+ * to get all the items in the hash table, and perform
+ * a check. The function provided MAY delete the node, and
+ * will inform this function by returning a _B_FALSE. If
+ * such a return code is seen, we will delete the Hash
+ * Entry.
+ *
+ * This function is also called by the shutdown routine,
+ * so the shutdown flag is used to determine if we need
+ * to lock the buckets and the hash table.
+ *
+ * Returns: int, 0 if successful
+ */
+void
+getAllHashTableEntries(HashTable *htbl, boolean_t (*fcnt)(void *, uint32_t),
+ int lockType, uint32_t p1, boolean_t shutdown_flag)
+{
+ HashEntry *p;
+ HashEntry *q = NULL;
+ HashEntry *tmp;
+ int i;
+ int nentry;
+ int result;
+
+ /*
+ * If we are shutting down, we do not want to lock.
+ */
+ if (shutdown_flag == _B_TRUE) {
+ lockType = LOCK_NONE;
+ }
+
+ for (i = 0, nentry = 0;
+ i < HASH_TBL_SIZE && (nentry < htbl->size); i++) {
+
+ /*
+ * If we are shutting down, we do not want to lock.
+ */
+ if (shutdown_flag == _B_FALSE) {
+ (void) rw_wrlock(&htbl->bucketLock[i]);
+ }
+
+ p = htbl->buckets[i];
+ while (p) {
+ nentry++;
+ /*
+ * The calling function is responsible for unlocking
+ * the node!!!! Note that since this function is
+ * mostly called by the garbage collector, we do not
+ * REALLY need to lock right away. If the lock
+ * request fails, we can try later.
+ */
+ switch (lockType) {
+ case LOCK_WRITE:
+ result = rw_trywrlock((rwlock_t *)p->data);
+ break;
+ case LOCK_READ:
+ result = rw_tryrdlock((rwlock_t *)p->data);
+ break;
+ case LOCK_NONE:
+ default:
+ result = 0;
+ break;
+ }
+
+ if (result == 0 && (fcnt(p->data, p1) == _B_FALSE)) {
+ /*
+ * If a failure was returned, we need to
+ * free this one.
+ */
+ if (p == htbl->buckets[i])
+ htbl->buckets[i] = p->next;
+ else
+ q->next = p->next;
+
+ tmp = p;
+ p = p->next;
+
+ free(tmp);
+
+ if (shutdown_flag == _B_FALSE) {
+ /*
+ * We delete the entry, let's decrement
+ * the counter.
+ */
+ (void) rw_wrlock(&htbl->hashLock);
+ htbl->size--;
+ (void) rw_unlock(&htbl->hashLock);
+ } else {
+ htbl->size--;
+ }
+ /*
+ * We need to reduce the entry
+ * table size, otherwise resources aren't
+ * released properly.
+ */
+ nentry--;
+ } else {
+ if (result == 0) {
+ switch (lockType) {
+ case LOCK_WRITE:
+ case LOCK_READ:
+ (void) rw_unlock(
+ (rwlock_t *)p->data);
+ break;
+ }
+ }
+
+ q = p;
+ p = p->next;
+ }
+ }
+
+ if (shutdown_flag == _B_FALSE) {
+ /*
+ * Unlock the bucket
+ */
+ (void) rw_unlock(&htbl->bucketLock[i]);
+ }
+ }
+}
+
+/*
+ * Function: enumerateAllHashTableEntries
+ *
+ * Arguments: table - Pointer to Hash Table
+ * bucket - IN/OUT determines which bucket to start
+ * enumerating from
+ * offset - IN/OUT determines which offset from bucket
+ * the last enumeration operation reached
+ * lockType - IN The Lock type, which can be:
+ * LOCK_NONE - No Lock
+ * LOCK_READ - Read Lock
+ * LOCK_WRITE - Write Lock
+ *
+ * Description: Enumerates synchronously the entire HashTable
+ * refered to by table; each call will set bucket
+ * and offset appropriately, and return the next
+ * HashEntry in the table. The caller should
+ * generally not need to mess with bucket and offset,
+ * except to ensure that the enumerator cookie
+ * (an array of uint32_ts) has been initialized
+ * the first time around with initEnumeratorState.
+ *
+ * If a lock type was selected, the node's will
+ * be locked upon return. The caller is then
+ * responsible for unlocking the node when it is
+ * finished with the data.
+ *
+ * This function is similar to getAllHashTableEntries,
+ * except that entries are returned to the caller
+ * directly, rather than through a callback, and
+ * the entries are not freed after enumeration (hence
+ * this enumeration function is read-only WRT the
+ * HashTable).
+ *
+ * Note that this function has snapshot semantics --
+ * that is, it returns the next entry in the table
+ * as the table state is when the next enumeration
+ * call is made, not when the enumeration started.
+ * Hence if entries are removed or added while the
+ * the enumeration is proceeding, they may or may not
+ * show up in the enumeration. As such, this function
+ * is most useful for gathering stats for mechanisms
+ * like SNMP and mipagentstat.
+ *
+ * Returns: void * on success (this implies more entries to come)
+ * NULL on enumeration completion
+ */
+void *enumerateAllHashTableEntries(HashTable *table,
+ uint32_t *bucket,
+ uint32_t *offset,
+ int lockType) {
+ HashEntry *p;
+ void *answer = NULL;
+ int i;
+
+ /*
+ * Traverse the buckets of the hashtable, starting at the given
+ * bucket. Increment the offset here as well.
+ */
+ for (; *bucket < HASH_TBL_SIZE; (*bucket)++) {
+ (void) rw_rdlock(&(table->bucketLock[*bucket]));
+ p = table->buckets[*bucket];
+
+ /*
+ * Walk down the bucket's chain until either we find the
+ * next offset, or the chain ends.
+ */
+ for (i = 0; p; p = p->next, i++) {
+ if (i == *offset) {
+ /*
+ * got it; lock the node if requested, and
+ * bump the offset.
+ */
+ LOCK_NODE(lockType, p->data);
+
+ (*offset)++;
+ answer = p->data;
+ break;
+ }
+ }
+
+ (void) rw_unlock(&(table->bucketLock[*bucket]));
+
+ if (answer != NULL) {
+ return (answer);
+ }
+
+ /*
+ * If we get here, the offset given was at the end of
+ * the chain. Reset it now to zero and move on to the
+ * next bucket.
+ */
+ *offset = 0;
+ }
+
+ /*
+ * If we get here, there are no more elements in the table,
+ * so the enumeration is finished. Inform the caller by
+ * returning NULL;
+ */
+ return (NULL);
+}
+
+/*
+ * Function: initEnumeratorState
+ *
+ * Arguments: state - IN/OUT a pointer to the state cookie
+ * statelen - IN size of the state cookie
+ *
+ * Description: Initializes the enumerator state used by
+ * enumerateAllHashTableEntries such that when this
+ * state cookie is passed to enumerateAllHashTableEntries,
+ * the enumeration will commence at the first entry.
+ */
+void initEnumeratorState(void *state, size_t statelen) {
+ (void) memset(state, 0, statelen);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mipagent/hash.h b/usr/src/cmd/cmd-inet/usr.lib/mipagent/hash.h
new file mode 100644
index 0000000000..fef4ce9470
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mipagent/hash.h
@@ -0,0 +1,156 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999, 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _HASH_H
+#define _HASH_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * hash.h: Hash Table structures, defines, and prototypes.
+ *
+ * The locking strategy behind hash.c is actually quite simple,
+ * but does need some explaining. Basically, we have three different
+ * locks to worry about; one at the hash table level, one at the
+ * bucket level and one at the node level.
+ *
+ * +===================+
+ * | Hash | Hash | +---------+ +---------+
+ * | Table | Bucket |<--->|HashEntry|<--->|HashEntry|
+ * | | | +---------+ +---------+
+ * | * | * | / \ / \
+ * | | | | |
+ * | | | \ / \ /
+ * | | | +---------+ +---------+
+ * | | | | * Node | | * Node |
+ * +===================+ +---------+ +---------+
+ *
+ * * = Lock is present
+ *
+ * When we walk through the Hash Table to add an item, it first
+ * gets the hash bucket index, and locks the whole row (or bucket)
+ * with a write lock. a HashEntry is then added at the head of the
+ * bucket queue. If a node lock was requested, via a function
+ * parameter, the node is locked. This is particularly useful if
+ * additional work will be done to the node. The Hash Table lock
+ * is then locked with a write lock, and the table counter is
+ * incremented, then the lock is unlocked. Lastly, the hash
+ * bucket is unlocked, and the function returns.
+ *
+ * When the code needs to find a node in the hash table, the
+ * hash bucket (row) is locked, and all nodes in the row are
+ * compare with the key provided. The caller may also provide
+ * a pointer to a helper routine, which is used to further
+ * qualify searches. If a match is found, the node is locked
+ * (if requested), and the pointer to the node is retruned to
+ * the caller.
+ *
+ * If a node is to be deleted from the table, the row is write
+ * locked, the row is searched for a match, based on the key and
+ * the data to the pointer. If a match is found, the HashEntry
+ * is deleted. The table lock is then write locked, decremented
+ * and unlocked. Finally, the bucket lock is released.
+ *
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum {
+ LOCK_NONE,
+ LOCK_READ,
+ LOCK_WRITE
+};
+
+typedef struct hash_entry {
+ struct hash_entry *next;
+ enum {
+ HASH_INT_KEY,
+ HASH_STR_KEY
+ } hashKeyType;
+ void *data;
+ uint32_t key; /* Hopefully somewhere in *data */
+ unsigned char keyLen; /* keyLen can't be more than 255 chars now */
+ unsigned char *keyData;
+} HashEntry;
+
+#define HASH_TBL_SIZE 256
+
+/*
+ * If uniqueData is set to non-zero, then the bucket will be searched for
+ * for uniqueness.
+ */
+typedef struct hash_table {
+ size_t size;
+ int uniqueData;
+ rwlock_t hashLock;
+ rwlock_t bucketLock[HASH_TBL_SIZE];
+ HashEntry *buckets[HASH_TBL_SIZE];
+} HashTable;
+
+#define HASH_BIT_COUNT 8
+
+#ifdef _BIG_ENDIAN
+#define HASHIT(key) \
+ ((uint32_t)((key >> HASH_BIT_COUNT) ^ (key)) \
+ & ~(~0 << HASH_BIT_COUNT))
+#else
+#define HASHIT(key) \
+ ((uint32_t)((key << HASH_BIT_COUNT) ^ (key)) >> \
+ (uint32_t)(32 - HASH_BIT_COUNT))
+#endif
+
+extern int InitHash(HashTable *);
+extern int linkHashTableEntryUint(HashTable *, uint32_t, void *, int);
+extern int linkHashTableEntryString(HashTable *, unsigned char *, uint32_t,
+ void *, int);
+extern boolean_t delHashTableEntryUint(HashTable *, void *,
+ uint32_t, int);
+extern boolean_t delHashTableEntryString(HashTable *, void *, unsigned char *,
+ uint32_t, int);
+extern void *findHashTableEntryUint(HashTable *, uint32_t, int,
+ boolean_t (*fcnt)(void *, uint32_t, uint32_t, uint32_t), uint32_t,
+ uint32_t,
+ uint32_t);
+extern void *findHashTableEntryString(HashTable *, unsigned char *, uint32_t,
+ int, boolean_t (*fcnt)(void *, uint32_t, uint32_t, uint32_t), uint32_t,
+ uint32_t, uint32_t);
+extern boolean_t changeHashEntryStringToUint(HashTable *, void *,
+ unsigned char *, uint32_t, uint32_t);
+extern void getAllHashTableEntries(HashTable *, boolean_t (*fcnt)(void *,
+ uint32_t), int, uint32_t, boolean_t shutdown_flag);
+extern void *enumerateAllHashTableEntries(HashTable *,
+ uint32_t *,
+ uint32_t *,
+ int);
+extern void initEnumeratorState(void *, size_t);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _HASH_H */
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mipagent/inc.flg b/usr/src/cmd/cmd-inet/usr.lib/mipagent/inc.flg
new file mode 100644
index 0000000000..a555ad557f
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mipagent/inc.flg
@@ -0,0 +1,34 @@
+#!/bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 1999-2002 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+
+find_files "s.*" usr/src/cmd/cmd-inet/common
+echo_file usr/src/cmd/agents/snmp/agent/node.h
+echo_file usr/src/cmd/agents/snmp/agent/pagent.h
+echo_file usr/src/cmd/agents/snmp/snmplib/impl.h
+echo_file usr/src/cmd/agents/snmp/snmplib/snmp.h
+echo_file usr/src/cmd/agents/snmp/snmplib/asn1.h
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mipagent/mip.h b/usr/src/cmd/cmd-inet/usr.lib/mipagent/mip.h
new file mode 100644
index 0000000000..c16c0a90c1
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mipagent/mip.h
@@ -0,0 +1,432 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _MIP_H
+#define _MIP_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * This file contains definitions for structures used in all
+ * Mobile IP-aware entities.
+ */
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <synch.h>
+#include <sys/socket.h>
+#include <net/if.h>
+
+#define mipverbose(a) if (logVerbosity) (void)printf a
+
+#define MAX_KEY_LEN 32
+#define MAX_NAI_LENGTH 256
+
+/* Reason why a MN session terminates */
+#define REASON_UNKNOWN 0
+#define REG_EXPIRED 1
+#define REG_REVOKED 2
+#define MN_DEREGISTERED 3
+
+/* Flag to indicate if Reverse Tunneling is Required */
+#define REVTUN_NOTREQUIRED 0
+#define REVTUN_REQUIRED 1
+
+#ifdef LINUX
+/*
+ * For linux only.
+ */
+typedef char boolean_t;
+#endif
+
+/*
+ * The MipSecAssocEntry structure contains information necessary
+ * to authenticate messages. Each peer has one security association,
+ * which includes a key and replay information.
+ */
+typedef struct {
+ rwlock_t mipSecNodeLock;
+ boolean_t mipSecIsEntryDynamic;
+ uint32_t mipSecSPI;
+ int mipSecAlgorithmType;
+ int mipSecAlgorithmMode;
+ int mipSecKeyLen;
+ unsigned char mipSecKey[MAX_KEY_LEN];
+ int mipSecReplayMethod;
+ time_t mipSecKeyLifetime;
+} MipSecAssocEntry;
+
+
+/*
+ * The MipSecViolationEntry structure is not currently used,
+ * but is intended to contain information about security
+ * failures from peers.
+ */
+typedef struct {
+ rwlock_t mipSecNodeLock;
+ ipaddr_t mipSecViolatorAddr;
+ uint32_t mipSecViolationCounter;
+ int mipSecRecentViolationSPI;
+ time_t mipSecRecentViolationTime;
+ int mipSecRecentViolationIDLow;
+ int mipSecRecentViolationIDHigh;
+ int mipSecRecentViolationReason;
+} MipSecViolationEntry;
+
+/* Flags used in mobility agent advertisements */
+#define ADV_REVERSE_TUNNEL 0x01
+#define ADV_VJ_COMPRESSION 0x02
+#define ADV_GRE_ENCAP 0x04
+#define ADV_MIN_ENCAP 0x08
+#define ADV_IS_FOREIGN_AGENT 0x10
+#define ADV_IS_HOME_AGENT 0x20
+#define ADV_IS_BUSY 0x40
+#define ADV_REGISTRATION_REQUIRED 0x80
+
+/* Possible addresses for agent advertisements */
+#define LINK_MCAST_ADV_ADDR "224.0.0.1"
+#define LINK_MCAST_ADV_ADDR2 "224.0.0.2"
+#define LINK_BCAST_ADDR "255.255.255.255"
+#define LINK_MCAST_REG_ADDR "224.0.0.11"
+
+/* Boolean values */
+#define TRUE 1
+#define FALSE 0
+
+/* Flags used in registrations */
+#define REG_BIT_UNUSED 0x01
+#define REG_REVERSE_TUNNEL 0x02
+#define REG_VJ_COMPRESSION 0x04
+#define REG_GRE_ENCAP 0x08
+#define REG_MIN_ENCAP 0x10
+#define REG_DECAPSULATION_BY_MN 0x20
+#define REG_FWD_BROADCASTS 0x40
+#define REG_SIMULTANEOUS_BINDINGS 0x80
+
+/* Successful Mobile-IP Codes */
+#define MIP_SUCCESSFUL_REGISTRATION 0
+/*
+ * Successful, but an indication that simultaneous bindings
+ * is not supported.
+ */
+#define MIP_SIMULTANEOUS_NOT_SUPPORTED 1
+
+/* Drop the signalling packet due to unknown extension */
+#define MA_DROP_PACKET -1
+
+/* Rejection codes from Foreign Agent */
+#define FA_REASON_UNSPECIFIED 64
+#define FA_ADM_PROHIBITED 65
+#define FA_INSUFFICIENT_RESOURCES 66
+#define FA_MN_AUTH_FAILURE 67
+#define FA_HA_AUTH_FAILURE 68
+#define FA_REG_LIFETIME_TOO_LONG 69
+#define FA_POORLY_FORMED_REQUEST 70
+#define FA_POORLY_FORMED_REPLY 71
+#define FA_ENCAP_UNAVAILABLE 72
+#define FA_VJ_UNAVAILABLE 73
+#define FA_REVERSE_TUNNEL_UNAVAILABLE 74
+#define FA_REVERSE_TUNNEL_REQUIRED 75
+#define FA_MN_TOO_DISTANT 76
+#define FA_INVALID_CARE_OF_ADDR 77
+#define FA_DELIVERY_STYLE_UNAVAILABLE 79
+#define FA_HA_NET_UNREACHABLE 80
+#define FA_HA_HOST_UNREACHABLE 81
+#define FA_HA_PORT_UNREACHABLE 82
+#define FA_HA_UNREACHABLE 88
+/*
+ * Support for the error codes defined in the latest
+ * challenge/response and NAI I-D.
+ */
+#define FA_NONZERO_HOMEADDR_REQD 96
+#define FA_MISSING_NAI 97
+#define FA_MISSING_HOME_AGENT 98
+#define FA_MISSING_HOMEADDR 99
+#define FA_UNKNOWN_CVSE_FROM_MN 100 /* MN extension error at FA */
+#define FA_UNKNOWN_CVSE_FROM_HA 101 /* HA extension error at FA */
+#define FA_UNKNOWN_CHALLENGE 104
+#define FA_MISSING_CHALLENGE 105
+#define FA_STALE_CHALLENGE 106
+#define FA_MISSING_MN_FA_KEY 107
+
+/* Rejection codes from Home Agent */
+#define HA_REASON_UNSPECIFIED 128
+#define HA_ADM_PROHIBITED 129
+#define HA_INSUFFICIENT_RESOURCES 130
+#define HA_MN_AUTH_FAILURE 131
+#define HA_FA_AUTH_FAILURE 132
+#define HA_ID_MISMATCH 133
+#define HA_POORLY_FORMED_REQUEST 134
+#define HA_TOO_MANY_SIMULTANEOUS 135
+#define HA_UNKNOWN_HOME_AGENT 136
+#define HA_REVERSE_TUNNEL_UNAVAILABLE 137
+#define HA_REVERSE_TUNNEL_REQUIRED 138
+#define HA_ENCAPSULATION_UNAVAILABLE 139 /* Used for Reverse Tunnel */
+#define HA_UNKNOWN_CVSE_FROM_MN 140 /* MN extension error at HA */
+#define HA_UNKNOWN_CVSE_FROM_FA 141 /* FA extension error at HA */
+
+#define NONE 0
+/* Authentication algorithm types */
+#define MD5 1
+
+/* Authentication algorithm modes */
+#define PREFIXSUFFIX 1
+
+/* Replay method style */
+#define TIMESTAMPS 1
+
+/* Encapsulation style */
+#define IPIP 1
+#define GRE 2
+#define MINIMAL 3
+
+#ifdef LINUX
+/* ICMP messages (we define them here for portability) */
+#define ICMP_UNREACH_PORT 3
+#define ICMP_ROUTERADVERT 9
+#define ICMP_ROUTERSOLICIT 10
+#endif
+
+/*
+ * We need to redefine the ICMP header here because we need
+ * the Mobile-IP router advertisement extension, which is not
+ * currently in ip_icmp.h. This should be added in the future.
+ */
+typedef struct icmphdr {
+ unsigned char type;
+ unsigned char code;
+ unsigned short checksum;
+ union {
+ struct {
+ unsigned char u_adv_num_addr;
+ unsigned char u_adv_addr_entry_size;
+ unsigned short u_adv_lifetime;
+ } u_adv;
+ uint32_t u_unused;
+ } icmphdr_u;
+} icmph;
+
+#define icmpAdvNumAddr icmphdr_u.u_adv.u_adv_num_addr
+#define icmpAdvAddrEntrySize icmphdr_u.u_adv.u_adv_addr_entry_size
+#define icmpAdvLifetime icmphdr_u.u_adv.u_adv_lifetime
+
+/* Mobile IP Agent Advertisement Extension */
+
+#define ADV_EXT_TYPE 16
+#define ADV_PREFIX_EXT_TYPE 19
+#define ADV_PADDING_EXT_TYPE 0
+#define ADV_CHALLENGE_EXT_TYPE 24
+#define ADV_AGENT_NAI_EXT_TYPE 25
+
+/*
+ * The length of our challenges, and the maximum
+ * challenge size our Home Agent will accept.
+ */
+#define ADV_CHALLENGE_LENGTH 16
+#define ADV_MAX_CHALLENGE_LENGTH 256
+#define ADV_MAX_NAI_LENGTH 256
+
+typedef struct aaext {
+ uint8_t type;
+ uint8_t length;
+ uint16_t seqNum;
+ uint16_t regLifetime;
+ uint8_t advFlags;
+ uint8_t reserved;
+} advExt;
+
+/* Mobile IP Registration Request and Reply */
+
+#define REG_REQUEST_TYPE 1
+#define REG_REPLY_TYPE 3
+#define REG_MH_AUTH_EXT_TYPE 32
+#define REG_MF_AUTH_EXT_TYPE 33
+#define REG_FH_AUTH_EXT_TYPE 34
+/*
+ * Support for the latest challenge/response,
+ * Vendor Specific and AAA Keys I-D.
+ */
+#define REG_GEN_AUTH_EXT_TYPE 36
+#define REG_CRIT_VENDOR_SPEC_EXT_TYPE 38
+#define REG_GEN_MN_FA_KEY_EXT_TYPE 40
+#define REG_GEN_MN_HA_KEY_EXT_TYPE 42
+#define ENCAPSULATING_DELIVERY_TYPE 130 /* for reverse tunneling */
+#define REG_MN_NAI_EXT_TYPE 131
+#define REG_MF_CHALLENGE_EXT_TYPE 132
+#define REG_NORMAL_VENDOR_SPEC_EXT_TYPE 134
+
+typedef struct rreq {
+ uint8_t type;
+ uint8_t regFlags;
+ uint16_t regLifetime;
+ uint32_t homeAddr;
+ uint32_t haAddr;
+ uint32_t COAddr;
+ uint32_t IDHigh;
+ uint32_t IDLow;
+} regRequest;
+
+typedef struct rrep {
+ uint8_t type;
+ uint8_t code;
+ uint16_t regLifetime;
+ uint32_t homeAddr;
+ uint32_t haAddr;
+ uint32_t IDHigh;
+ uint32_t IDLow;
+} regReply;
+
+#define MIP_EXT_LENGTH 1
+#define MIP_EXT_DATA 2
+
+typedef struct rrext {
+ uint8_t type;
+ uint8_t length;
+} regExt;
+
+typedef struct authext {
+ uint8_t type;
+ uint8_t length;
+ uint16_t SPIhi;
+ uint16_t SPIlo;
+} authExt;
+
+#define KEY_ALG_NONE 0
+#define KEY_ALG_MD5_PREFIXSUFFIX 2
+#define KEY_ALG_HMAC_MD5 3
+
+typedef struct keydataext {
+ /*
+ * Key data is a MIER extension, and contains a lifetime
+ */
+ uint32_t lifetime;
+ uint32_t mnAAASPI;
+ uint32_t nodeSPI;
+}keyDataExt;
+
+/*
+ * Support for the latest challenge/response,
+ * Vendor Specific and AAA Keys I-D.
+ */
+typedef struct keyext {
+ uint8_t type;
+ uint8_t subType;
+ uint16_t length;
+ keyDataExt keyData;
+} keyExt;
+
+#define GEN_KEY_MN_FA 7
+#define GEN_KEY_MN_HA 1
+
+typedef struct mierlongext {
+ uint8_t type;
+ uint8_t subType;
+ uint16_t length;
+} mierLongExt;
+
+/*
+ * The following are the offsets in the
+ * extension header for mier style extensions.
+ */
+#define MIP_EXT_GEN_SUB_TYPE 1
+#define MIP_EXT_LONG_LENGTH 2
+#define MIP_EXT_LONG_LENGTH_DATA 4
+
+/*
+ * The following structure is the Generalized
+ * Authentication Extension, specified in the
+ * Challenge/Response I-D.
+ */
+typedef struct genauthext {
+ uint8_t type;
+ uint8_t subType;
+ uint16_t length;
+ uint16_t SPIhi;
+ uint16_t SPIlo;
+} genAuthExt;
+
+#define GEN_AUTH_MN_AAA 1
+
+#ifdef KEY_DISTRIBUTION
+/*
+ * Support for vendor specific extensions.
+ *
+ * The following is the definition of the vendor
+ * specific extension. Although we don't really care
+ * about this draft, we define it so that we do
+ * recognize the critical vendor specific extension,
+ * which has a two octet length.
+ */
+typedef
+struct vendorspecext {
+ uint8_t type;
+ uint8_t reserved;
+ uint16_t length;
+ uint32_t vendorId;
+ uint16_t vendorType;
+} vendorSpecExt;
+#else /* KEY_DISTRIBUTION */
+#define VENDOR_SPEC_EXT_HDR_LEN 10
+#endif /* KEY_DISTRIBUTION */
+/*
+ * The following are the offsets in the
+ * extension header for CVSE style extensions.
+ */
+#define MIP_EXT_CVSE_VENDOR_ID_TYPE 4
+#define MIP_EXT_CVSE_VENDOR_SUB_TYPE 8
+#define MIP_EXT_CVSE_VENDOR_ID_DATA 10
+
+/*
+ * The following are the offsets in the
+ * extension header for NVSE style extensions.
+ */
+#define MIP_EXT_NVSE_VENDOR_ID_TYPE 3
+#define MIP_EXT_NVSE_VENDOR_SUB_TYPE 4
+#define MIP_EXT_NVSE_VENDOR_ID_DATA 9
+
+/*
+ * And a few vendor Id's for your convenience.
+ */
+#define VENDOR_ID_CISCO 9
+#define VENDOR_ID_SUN 42
+#define VENDOR_ID_3COM 43
+
+/*
+ * And lastly, here are a few vendor specific
+ * extension numbers
+ */
+#define REG_MN_FA_KEY_EXT 1
+#define REG_FA_HA_KEY_EXT 2
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MIP_H */
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mipagent/mipagent.acl b/usr/src/cmd/cmd-inet/usr.lib/mipagent/mipagent.acl
new file mode 100644
index 0000000000..36ba40037e
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mipagent/mipagent.acl
@@ -0,0 +1,108 @@
+#
+#pragma ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Configuration file of an SNMP agent
+#
+
+
+##################
+# access control #
+##################
+
+# The list of community names needed for read/write access
+# to the entire MIB.
+# If the list is empty, the only valid community name is "public"
+# and its access type is read-only
+
+acl = {
+ {
+ communities = public, private
+ access = read-write
+ managers = *
+ }
+}
+
+
+#communities = {
+# public read-write
+# private read-write
+#}
+
+# The list of hosts that can send SNMP queries.
+# If this list is empty, all the hosts are allowed to
+# send SNMP queries.
+
+#managers = {
+# hellcat
+#}
+
+
+###################
+# trap parameters #
+###################
+
+trap = {
+# {
+# trap-community = SNMP-trap
+# hosts = corsair
+# {
+# enterprise = "atlp"
+# trap-num = 0-4
+# }
+# {
+# enterprise = "sun"
+# trap-num = 0, 1, 2-5, 6-16
+# }
+# {
+# enterprise = "snmp"
+# trap-num = 0-5
+# }
+# }
+# {
+# trap-community = jerry-trap
+# hosts = jerry, nanak, hubble
+# {
+# enterprise = "sun"
+# trap-num = 1, 3
+# }
+# {
+# enterprise = "snmp"
+# trap-num = 1-3
+# }
+# }
+}
+
+# The community name to be used in traps.
+
+#trap-community = SNMP-trap
+
+# The list of hosts where traps should be sent.
+
+#trap-recipients =
+#{
+# corsair
+#}
+
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mipagent/mipagent.reg b/usr/src/cmd/cmd-inet/usr.lib/mipagent/mipagent.reg
new file mode 100644
index 0000000000..f7abd0d6c7
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mipagent/mipagent.reg
@@ -0,0 +1,46 @@
+#
+#pragma ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Configuration file of the SNMP Relay
+# for the SNMP agent bundled with SNM
+#
+
+
+##########
+# agents #
+##########
+
+agents =
+{
+ {
+ name = "mipagent"
+ subtrees = { 1.3.6.1.2.1.44 }
+ timeout = 2000000
+ watch-dog-time = 86400
+ }
+}
+
+
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mipagent/mipagentstat_server.c b/usr/src/cmd/cmd-inet/usr.lib/mipagent/mipagentstat_server.c
new file mode 100644
index 0000000000..0aa3f8bf81
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mipagent/mipagentstat_server.c
@@ -0,0 +1,446 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <door.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <sys/socket.h>
+#include "mipagentstat_door.h"
+#include "conflib.h"
+#include "mip.h"
+#include "hash.h"
+#include "agent.h"
+
+/*
+ * Server for mipagentstat. This uses the protocol defined in
+ * "mipagentstat_door.h" to act as a door server for home and
+ * foreign agent statistics.
+ *
+ * The door server is created with the entry point dispatch;
+ * the door itself is created and bound to the rendezvous point
+ * on the filesystem by start_stat_server. dispatch simply
+ * sets up the enumeration if the operation is FIRST_ENT, and
+ * dispatches the enumeration call and it parameters to either
+ * the home or foreign agent enumeration function.
+ *
+ * This module and the hash module both use the enumeration state
+ * passed to and from the client. The are currently two different
+ * types of hash tables to enumerate: the faVisitorHash is two
+ * dimensional, the first being the hash buckets, and the second
+ * being the chains on each bucket; the haMobileNodeHash is three
+ * dimensional, the first and second dimensions being the same
+ * as faVisitorHash, and the extra third dimension being chains
+ * of bindings off each node in the hash chain.
+ *
+ * We use the notion of dimensions to optimize hash table
+ * enumeration. The first dimension corresponds to the hash
+ * table bucket, the second to the offset from that bucket,
+ * and the third (in the case of haMobileNodeHash) to the
+ * binding (which is an offset from the second offset). The state
+ * is decomposed into four 32-bit counters, of which only the
+ * first three are currently used. The first holds the bucket
+ * counter, the second the offset counter, and the third the
+ * binding offset counter. enumerateAllHashTableEntries handles
+ * the bucket and offset state, which enumerateHABindings handles
+ * the binding offset state.
+ *
+ * The counters are used as follows: we first jump to the bucket
+ * indicated by the bucket counter; next we count off into the
+ * chain until we reach the node following the node indicated by
+ * the offset counter, and finally, in the case of haMobileNodeHash,
+ * we count off from that node into the bindings list up to the
+ * node following the binding counter. If either the offset or
+ * binding offset counter reaches the end of its chain, we reset
+ * it to zero, and proceed with the next bucket or chain. This
+ * algorithm has the same computational complexity as the hash
+ * algorithm itself, with respect to finding the next node.
+ *
+ * The following #defines specify which 32-bit words in the enumeration
+ * state are used for what:
+ */
+
+#define BUCKET 0
+#define OFFSET 1
+#define BINDING 2
+
+extern HashTable faVisitorHash; /* Foreign agent hashtable */
+extern HashTable haMobileNodeHash; /* Home agent hashtable */
+extern HashTable mipAgentHash; /* Mobility agent peers */
+
+extern int logVerbosity;
+
+static int did = -1; /* file-descriptor for stat server door; -1 means unset */
+
+/*
+ * Function: enumerateHABindings
+ *
+ * Arguments: state - IN/OUT 128 bits of enumeration state
+ * lck - IN/OUT lock which protects the binding
+ * entry returned. If an entry was returned,
+ * lck will point to the node lock which
+ * the caller MUST unlock when done.
+ *
+ * Description: This function gets the next mobile node binding
+ * from the hash table haMobileNodeHash. The HA
+ * hastable is three dimensional: The first is the
+ * hashtable bucket array, the second is the linked
+ * list of HashEntries off each bucket, and the
+ * third is the linked list of bindings associated
+ * with each HaMobileNodeEntry. Hence we need three
+ * state counters for this enumeration.
+ *
+ * The enumeration state is contained in the state
+ * parameter, which is here cast to an array
+ * of four 32-bit unsigned integers. This function
+ * only used the first three words; the first two
+ * are used as enumeration state for
+ * enumerateAllHashTableEntries, while the third is
+ * used to find the next HaBindingEntry.
+ *
+ * Returns: a HaBindingEntry on success
+ * NULL if there are no more entries in the table
+ */
+static HaBindingEntry *enumerateHABindings(uint32_t state[4], rwlock_t **lck) {
+ HaMobileNodeEntry *hamne;
+ HaBindingEntry *habe;
+ uint32_t i;
+
+ /* Find the next HaMobileNodeEntry */
+ while ((hamne = enumerateAllHashTableEntries(&haMobileNodeHash,
+ state + BUCKET,
+ state + OFFSET,
+ LOCK_READ)) != NULL) {
+ habe = hamne->bindingEntries;
+
+ /* Find the next HaBindingEntry */
+ for (i = 0; habe; habe = habe->next, i++) {
+ if (i == state[BINDING]) {
+ /* got it */
+ (state[BINDING])++;
+
+ /* Pass the nodeLock back to the caller to unlock */
+ *lck = &(hamne->haMnNodeLock);
+ return (habe);
+ }
+ }
+
+ /* If we got here, there are no more bindings for this node */
+ state[BINDING] = 0;
+
+ (void) rw_unlock(&(hamne->haMnNodeLock));
+ }
+
+ /* If we got here, we have enumerated the whole table */
+ return (NULL);
+}
+
+/*
+ * Function: enumerateHAStats
+ *
+ * Arguments: args - IN/OUT The stat call/reply buffer
+ *
+ * Description: This function uses enumerateHABindings to retrieve the
+ * next binding in the enumeration and then extracts
+ * the data needed for the stats call into the
+ * DoorStatArgs args.
+ *
+ * Returns: 1 on success, more entries to come
+ * 0 on success, no more entries
+ */
+static int enumerateHAStats(DoorStatArgs *args) {
+ HaBindingEntry *habe;
+ /*LINTED pointer cast may result in improper alignment*/
+ uint32_t *state = (uint32_t *)args->enum_state;
+ rwlock_t *nodeLock = NULL;
+
+ if ((habe = enumerateHABindings(state, &nodeLock)) == NULL) {
+ /* enumeration has completed */
+ return (0);
+ }
+
+ /* copy out mobile node's address */
+ args->node_af = AF_INET;
+ (void) memcpy(args->node,
+ &(habe->haBindingMN),
+ sizeof (habe->haBindingMN));
+ /* copy out foreign agent's address */
+ args->agent_af = AF_INET;
+ (void) memcpy(args->agent,
+ &(habe->haBindingCOA),
+ sizeof (habe->haBindingCOA));
+ /* Copy out time granted and remaining */
+ args->granted = (uint32_t)habe->haBindingTimeGranted;
+ args->expires = (uint32_t)habe->haBindingTimeExpires;
+ /* Finally, copy in the flags! */
+ args->service_flags = (uint8_t)habe->haBindingRegFlags;
+
+ (void) rw_unlock(nodeLock);
+ return (1);
+}
+
+/*
+ * Function: enumerateFAStats
+ *
+ * Arguments: args - IN/OUT The stat call/reply buffer
+ *
+ * Description: This function uses enumerateAllHashTableEntries
+ * to retrieve the next FaVisitorEntry in the enumeration
+ * and then extracts the data needed for the stats call
+ * into the DoorStatArgs args.
+ *
+ * Returns: 1 on success, more entries to come
+ * 0 on success, no more entries
+ */
+static int enumerateFAStats(DoorStatArgs *args) {
+ FaVisitorEntry *fave;
+ /*LINTED pointer cast may result in improper alignment*/
+ uint32_t *state = (uint32_t *)args->enum_state;
+
+ if ((fave = enumerateAllHashTableEntries(&faVisitorHash,
+ state + BUCKET,
+ state + OFFSET,
+ LOCK_READ)) == NULL) {
+ /* enumeration has completed */
+ return (0);
+ }
+
+ /* copy out mobile node's home address */
+ args->node_af = AF_INET;
+ (void) memcpy(args->node,
+ &(fave->faVisitorHomeAddr),
+ sizeof (fave->faVisitorHomeAddr));
+ /* copy out home agent's address */
+ args->agent_af = AF_INET;
+ (void) memcpy(args->agent,
+ &(fave->faVisitorHomeAgentAddr),
+ sizeof (fave->faVisitorHomeAgentAddr));
+ /* Copy out time granted and remaining */
+ args->granted = fave->faVisitorTimeGranted;
+ args->expires = fave->faVisitorTimeExpires;
+ /* Finally, copy in the flags! */
+ args->service_flags = (uint8_t)fave->faVisitorRegFlags;
+
+ (void) rw_unlock(&(fave->faVisitorNodeLock));
+ return (1);
+}
+
+
+/*
+ * Function: enumerateAgentPeerStats
+ *
+ * Arguments: args - IN/OUT The stat call/reply buffer
+ *
+ * Description: This function uses enumerateAllHashTableEntres to
+ * retrieve the next MobilityAgentEntry in mipAgentHash,
+ * and then extracts the data needed for the stats call
+ * into the DoorStatArgs args.
+ *
+ * Returns: 1 on success, more entries to come
+ * 0 on success, no more entries
+ */
+int enumerateAgentPeerStats(DoorStatArgs *args, uint8_t flags) {
+ MobilityAgentEntry *mae;
+
+ /* LINTED pointer cast may result in improper alignment */
+ uint32_t *state = (uint32_t *)args->enum_state;
+
+ /* skip the agent-peers that we're not looking for */
+ do {
+ mae = enumerateAllHashTableEntries(&mipAgentHash,
+ state + BUCKET, state + OFFSET, LOCK_READ);
+
+ if (mae == NULL)
+ return (0);
+
+ } while ((mae->maPeerFlags & flags) == 0);
+
+ /* copy out agent-peer's address */
+ args->agent_af = AF_INET;
+ (void) memcpy(args->agent, &(mae->maAddr), sizeof (mae->maAddr));
+
+ /* Copy the flags */
+ if (flags == FA_PEER)
+ /*
+ * User wants FA_PEER SAs, so this is us as the HA. Pass
+ * the SA bits which are relavent to us as HA peer, namely:
+ * request apply, and tunnel apply, and reply permit and
+ * reverse tunnel permit. Also make sure we're only showing
+ * what's invoked (not just what's configured)!
+ */
+ args->service_flags = (mae->maIPsecFlags &
+ ((mae->maIPsecSAFlags[IPSEC_APPLY] & HA_PEER_APPLY_MASK) | \
+ (mae->maIPsecSAFlags[IPSEC_PERMIT] & HA_PEER_PERMIT_MASK)));
+ else
+ /*
+ * For us as FA peer, we pass: request apply, reply permit,
+ * tunnel permit, and reverse tunnel apply.
+ */
+ args->service_flags = (mae->maIPsecFlags &
+ ((mae->maIPsecSAFlags[IPSEC_APPLY] & FA_PEER_APPLY_MASK) | \
+ (mae->maIPsecSAFlags[IPSEC_PERMIT] & FA_PEER_PERMIT_MASK)));
+
+ /* fin */
+ (void) rw_unlock(&(mae->maNodeLock));
+ return (1);
+}
+
+/*
+ * Function: dispatch
+ *
+ * Arguments: see door_create(3x) for the description of the
+ * arguments passed to this function. The DoorStatArgs
+ * structure used for the IPC is in argp.
+ *
+ * Description: Sets up the enumeration if the operation is FIRST_ENT,
+ * and then dispatches to either enumerateHAStats or
+ * enumerateFAStats. This is entry point to the door
+ * created by start_stat_server.
+ */
+/*ARGSUSED*/
+static void dispatch(void *cookie, char *argp, size_t argsize,
+ door_desc_t *dp, size_t ndesc)
+{
+
+ /*LINTED pointer cast may result in improper alignment*/
+ DoorStatArgs *args = (DoorStatArgs *)argp;
+
+ if (argsize < sizeof (*args)) {
+ mipverbose(("stats server: call buffer too small\n"));
+ (void) door_return(NULL, 0, NULL, 0);
+ }
+
+ /* Set up the enumeration operation */
+ if (args->op == FIRST_ENT) {
+ initEnumeratorState(args->enum_state,
+ sizeof (*(args->enum_state)));
+ } else if (args->op != NEXT_ENT) {
+ /* sanity check: if the op isn't FIRST_ENT, it must be NEXT_ENT */
+ mipverbose(("stats server: Unknown enumeration operation\n"));
+ (void) door_return(NULL, 0, NULL, 0);
+ }
+
+ /* Dispatch to the HA or FA stat function */
+ switch (args->type) {
+ case HOME_AGENT:
+ if (enumerateHAStats(args) == 0)
+ (void) door_return(NULL, 0, NULL, 0);
+ break;
+
+ case FOREIGN_AGENT:
+ if (enumerateFAStats(args) == 0)
+ (void) door_return(NULL, 0, NULL, 0);
+ break;
+
+ case HOME_AGENT_PEER:
+ if (enumerateAgentPeerStats(args, HA_PEER) == 0)
+ (void) door_return(NULL, 0, NULL, 0);
+ break;
+
+ case FOREIGN_AGENT_PEER:
+ if (enumerateAgentPeerStats(args, FA_PEER) == 0)
+ (void) door_return(NULL, 0, NULL, 0);
+ break;
+
+ default:
+ mipverbose(("stats server: Unknown agent type requested\n"));
+ (void) door_return(NULL, 0, NULL, 0);
+ }
+
+ (void) door_return((char *)args, argsize, NULL, 0);
+}
+
+/*
+ * Function: startStatServer
+ *
+ * Description: Creates the server door for receiving stat requests.
+ * If the door rendezvous file does not exist, creates it.
+ * This is the only entry point from mipagent into this
+ * module.
+ *
+ * Returns: 1 on error
+ * 0 on success
+ */
+int startStatServer() {
+ struct stat buf;
+
+ if (did != -1) {
+ /* Door server is already running */
+ return (0);
+ }
+
+ /* Create the filesystem rendezvous point if not already there */
+ if (stat(MIPAGENTSTAT_DOOR, &buf) < 0) {
+ int fd;
+ if ((fd = creat(MIPAGENTSTAT_DOOR, 0444)) < 0) {
+ syslog(LOG_ERR, "Cannot create %s", MIPAGENTSTAT_DOOR);
+ return (1);
+ }
+ (void) close(fd);
+ }
+
+ /* Create the door ... */
+ if ((did = door_create(dispatch, NULL,
+ DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) < 0) {
+ syslog(LOG_ERR, "door_create failed: %s", strerror(errno));
+ return (1);
+ }
+
+ /*
+ * And attach it to the rendezvous point, cleaning up any
+ * stale associations first.
+ */
+ (void) fdetach(MIPAGENTSTAT_DOOR);
+
+ if (fattach(did, MIPAGENTSTAT_DOOR) < 0) {
+ syslog(LOG_ERR, "Cannot attach door to %s: %s",
+ MIPAGENTSTAT_DOOR, strerror(errno));
+ return (1);
+ }
+
+ return (0);
+}
+
+/*
+ * Function: killStatServer
+ *
+ * Description: This function is used to shut down the stat
+ * door server.
+ *
+ * Returns: 0 if successful, -1 on failure
+ */
+int killStatServer() {
+ int err = door_revoke(did);
+ did = -1;
+ return (err);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mipagent/pool.c b/usr/src/cmd/cmd-inet/usr.lib/mipagent/pool.c
new file mode 100644
index 0000000000..28cc5cccff
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mipagent/pool.c
@@ -0,0 +1,241 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * file: pool.c
+ *
+ * This file contains all of the routines that manage
+ * the Home Agent's Home Address pools. This is used
+ * when a Mobile Node requests a Home Address by including
+ * a Home Address of zero (0) in the Registration
+ * request.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <syslog.h>
+#include <stdlib.h>
+
+#include "mip.h"
+#include "agent.h"
+#include "pool.h"
+
+/*
+ * This table has one entry for each pool defined in the config file
+ */
+extern HashTable mipPoolHash;
+
+extern int logVerbosity;
+extern char *ntoa(uint32_t, char *);
+
+/*
+ * Function: CreateAddressPool
+ *
+ * Arguments: poolIdentifier - Numerical Pool Identifier
+ * baseAddress - The base IP address of the pool
+ * length - The number of addresses in the pool
+ *
+ * Description: This function will create a Pool structure
+ * and add it to the Hash Table. Pools are used
+ * to allocate Home Agent addresses to Mobile
+ * Nodes that request one by inserting a zero (0)
+ * Home Address in the Registration Request.
+ *
+ * The Pool entry will be locked upon return.
+ * The caller is responsible for unlocking the
+ * node when it is finished with it.
+ *
+ * Returns: if successful a pointer to a Pool structure
+ * is returned, otherwise NULL
+ */
+Pool *
+CreateAddressPool(uint32_t poolIdentifier, ipaddr_t baseAddress,
+ uint32_t length)
+{
+ Pool *pool;
+ int poolSize;
+ int i;
+
+ /*
+ * First, let's see if we already have this pool.
+ */
+ if (findHashTableEntryUint(&mipPoolHash, poolIdentifier,
+ LOCK_NONE, NULL, 0, 0, 0) != NULL) {
+ syslog(LOG_ERR, "Pool %d already defined\n", poolIdentifier);
+ return (NULL);
+ }
+
+ /*
+ * First we allocate the memory
+ */
+ poolSize = sizeof (Pool) + (sizeof (PoolEntry) * length);
+ pool = (Pool *)calloc(1, poolSize);
+
+ if (pool == NULL) {
+ syslog(LOG_CRIT, "FATAL: Unable to allocate address pool");
+ return (NULL);
+ }
+
+ /*
+ * Initialize the pool's parameters.
+ */
+ if (rwlock_init(&pool->poolNodeLock, USYNC_THREAD, NULL)) {
+ syslog(LOG_ERR, "Unable to initialize read/write lock");
+ free(pool);
+ return (NULL);
+ }
+
+ pool->poolIdentifier = poolIdentifier;
+ pool->poolBaseAddress = baseAddress;
+ pool->poolLength = length;
+
+ /*
+ * Setup each Pool Entry within the Pool structure (one
+ * per address).
+ */
+ for (i = 0; i < length; i++) {
+ /* Setup the entry's address. */
+ pool->poolEntry[i].poolStatus = POOL_FREE;
+ pool->poolEntry[i].poolHomeAddress = baseAddress + htonl(i);
+ }
+
+ /*
+ * Add it to the Hash Table.
+ */
+ if (linkHashTableEntryUint(&mipPoolHash, poolIdentifier, pool,
+ LOCK_WRITE)) {
+ syslog(LOG_ERR, "FATAL: Unable to add pool to hash table");
+ free(pool);
+ return (NULL);
+ }
+
+ return (pool);
+}
+
+/*
+ * Function: GetAddressFromPool
+ *
+ * Arguments: poolIdentifier - Pool Identifier
+ *
+ * Description: This function will step through the Pool
+ * entry identified via the Pool Identifier
+ * and will return the first available
+ * Home Address.
+ *
+ * Returns: int, Home Address if successful, otherwise
+ * zero (0).
+ */
+uint32_t
+GetAddressFromPool(uint32_t poolIdentifier)
+{
+
+ Pool *pool;
+ int i;
+ ipaddr_t homeAddress = 0;
+ char buffer[INET_ADDRSTRLEN];
+
+ /*
+ * Let's get the pool entry, using the pool identifier.
+ */
+ if ((pool = findHashTableEntryUint(&mipPoolHash,
+ poolIdentifier, LOCK_WRITE, NULL, 0, 0, 0)) == NULL) {
+ mipverbose(("Pool %d not found\n", poolIdentifier));
+ return (0);
+ }
+
+ for (i = 0; i < pool->poolLength; i++) {
+ if (pool->poolEntry[i].poolStatus == POOL_FREE) {
+ /*
+ * This one is free, let's allocate it.
+ */
+ pool->poolEntry[i].poolStatus = POOL_TAKEN;
+ mipverbose(("allocated %s from pool %d\n",
+ ntoa(pool->poolEntry[i].poolHomeAddress, buffer),
+ poolIdentifier));
+ homeAddress = pool->poolEntry[i].poolHomeAddress;
+ break;
+ }
+ }
+
+ /*
+ * And now we unlock the node...
+ */
+ (void) rw_unlock(&pool->poolNodeLock);
+
+ return (homeAddress);
+}
+
+/*
+ * Function: freeAddressFromPool
+ *
+ * Arguments: poolIdentifier - Pool Identifier
+ * homeAddr - Home Address
+ *
+ * Description: This function will put a Home Address
+ * back into the address pool by marking
+ * the entry as being free.
+ *
+ * Returns: boolean, _B_TRUE if the Home Address was freed.
+ */
+boolean_t
+freeAddressFromPool(uint32_t poolIdentifier, ipaddr_t homeAddr)
+{
+ Pool *pool;
+ int i;
+ boolean_t found = _B_FALSE;
+ char buffer[INET_ADDRSTRLEN];
+
+ /*
+ * First, let's see if we already have this pool.
+ */
+ if ((pool = findHashTableEntryUint(&mipPoolHash,
+ poolIdentifier, LOCK_WRITE, NULL, 0, 0, 0)) == NULL) {
+ mipverbose(("Pool %d not found\n", poolIdentifier));
+ return (found);
+ }
+
+ for (i = 0; i < pool->poolLength; i++) {
+ if (pool->poolEntry[i].poolStatus == POOL_TAKEN &&
+ pool->poolEntry[i].poolHomeAddress == homeAddr) {
+ mipverbose(("Freed %s from pool %d\n",
+ ntoa(homeAddr, buffer), poolIdentifier));
+ pool->poolEntry[i].poolStatus = POOL_FREE;
+ found = _B_TRUE;
+ break;
+ }
+ }
+
+ /*
+ * And now we unlock the node...
+ */
+ (void) rw_unlock(&pool->poolNodeLock);
+
+ return (found);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mipagent/pool.h b/usr/src/cmd/cmd-inet/usr.lib/mipagent/pool.h
new file mode 100644
index 0000000000..aecd62f561
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mipagent/pool.h
@@ -0,0 +1,72 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _POOL_H
+#define _POOL_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * pool.h : Data structures and prototypes used by a Mobile IP agent
+ * to support Address Pools.
+ */
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+typedef struct {
+ enum {
+ POOL_FREE = 0,
+ POOL_TAKEN
+ } poolStatus;
+ ipaddr_t poolHomeAddress;
+ char poolMnNAI[MAX_NAI_LENGTH];
+} PoolEntry;
+
+typedef struct {
+ rwlock_t poolNodeLock;
+ uint32_t poolIdentifier;
+ ipaddr_t poolBaseAddress;
+ uint32_t poolLength;
+ PoolEntry poolEntry[1];
+} Pool;
+
+
+Pool *CreateAddressPool(uint32_t, ipaddr_t, uint32_t);
+
+uint32_t GetAddressFromPool(uint32_t);
+
+boolean_t freeAddressFromPool(uint32_t, ipaddr_t);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _POOL_H */
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mipagent/setup.c b/usr/src/cmd/cmd-inet/usr.lib/mipagent/setup.c
new file mode 100644
index 0000000000..91c048a181
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mipagent/setup.c
@@ -0,0 +1,749 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * file: setup.c
+ *
+ * This file contains the routines used to create data
+ * structures, such as Mobile Nodes, Visitor Entries,
+ * interfaces and security association.
+ */
+#include <stdio.h>
+#include <syslog.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <limits.h>
+
+#include <errno.h>
+#include "agent.h"
+#include "mip.h"
+#include "setup.h"
+#include "agentKernelIntfce.h"
+
+/* ----------------- Common to all mobility agents ------------------- */
+extern struct hash_table maAdvConfigHash;
+
+/*
+ * This table stores all of the Security Associations
+ */
+extern HashTable mipSecAssocHash;
+
+/*
+ * This table has one entry for each known Mobility Agent
+ */
+extern HashTable mipAgentHash;
+
+/*
+ * This table has one entry for each pool defined in the config file
+ */
+extern HashTable mipPoolHash;
+
+/*
+ * This table has one entry for each active tunnel number
+ */
+extern HashTable mipTunlHash;
+
+/* Home Agent specific data structures. */
+extern struct hash_table haMobileNodeHash;
+
+/* Other external declarations */
+extern int logVerbosity;
+
+/* MipTunlEntryLookup function defined in agentKernelIntfce.c */
+extern boolean_t MipTunlEntryLookup(void *, ipaddr_t, uint32_t, uint32_t);
+/*
+ * Given that we are acting as an SNMP Sub-Agent, we will need
+ * to register our address with the SNMP Master Agent. The
+ * subagent_addr variable is used for this purpose.
+ */
+ipaddr_t subagent_addr = 0;
+static ipaddr_t haAddr = 0;
+
+
+extern void HAinitID(uint32_t *, uint32_t *, int);
+#define VNI "vni"
+#define VNISTRLEN 3
+
+/*
+ * Function: CreateMobileNodeEntry
+ *
+ * Arguments: mnEntryType - Whether the entry is dynamic or
+ * static
+ * homeAddr - The Mobile Node's Home Address
+ * mnNAI - The Mobile Node's NAI
+ * mnNAILen - The length of the NAI
+ * homeAgentAddr - The Mobile Node's Home Agent
+ * SPI - The Mobile Node's SPI
+ * state - A RADIUS thing. not needed for now
+ * poolIdentifier - The Pool Identifier
+ *
+ * Description: This function is used to create a Mobile Node
+ * entry, which is added to the hash table. The
+ * SPI provided MUST have been previously defined
+ * as is the pool (if a non-zero value was provided
+ * as the pool id).
+ *
+ * If a Home Address was provided, we will add the
+ * node to the hash table based on the Home Address
+ * otherwise the NAI will be used.
+ *
+ * The Mobile Node entry will be locked upon return.
+ * The caller is responsible for unlocking the
+ * node when it is finished with it.
+ *
+ * Returns: upon successful return, this function will
+ * return the Mobile Node Entry pointer, otherwise
+ * NULL.
+ *
+ */
+/* ARGSUSED */
+HaMobileNodeEntry *
+CreateMobileNodeEntry(boolean_t isDynamic, ipaddr_t homeAddr, char *mnNAI,
+ uint32_t mnNaiLen, ipaddr_t homeAgentAddr, uint32_t SPI, char *state,
+ uint32_t poolIdentifier)
+{
+ HaMobileNodeEntry *entry = NULL;
+ MipSecAssocEntry *saEntry;
+#ifdef RADIUS_ENABLED
+ time_t currentTime;
+#endif /* RADIUS_ENABLED */
+
+ if (homeAddr == INADDR_ANY && (mnNAI == NULL || mnNaiLen == 0)) {
+ syslog(LOG_ERR, "Home Address OR NAI Must be specified");
+ return (NULL);
+ }
+ /*
+ * First, let's make sure that we already have
+ * this SPI defined.
+ */
+ if ((saEntry = findHashTableEntryUint(&mipSecAssocHash,
+ SPI, LOCK_NONE, NULL, 0, 0, 0)) == NULL) {
+ syslog(LOG_ERR, "SPI entry %d not found", SPI);
+ return (NULL);
+ }
+
+ /*
+ * Let's see if we already have this pool.
+ */
+ if (poolIdentifier) {
+ if (findHashTableEntryUint(&mipPoolHash,
+ poolIdentifier, LOCK_NONE, NULL, 0, 0, 0) == NULL) {
+ syslog(LOG_CRIT, "Pool entry %d not found.",
+ poolIdentifier);
+ return (NULL);
+ }
+ }
+
+ /*
+ * If an NAI is provided, make sure that it is legal
+ */
+ if (mnNAI) {
+ if (mnNaiLen > MAX_NAI_LENGTH) {
+ syslog(LOG_ERR, "Error: Mobile Node NAI too long");
+ return (NULL);
+ }
+ }
+
+
+ entry = (HaMobileNodeEntry *)calloc(1, sizeof (HaMobileNodeEntry));
+
+ if (!entry) {
+ syslog(LOG_CRIT, "FATAL: Unable to allocate Mobile Node Entry");
+ return (NULL);
+ }
+
+ /* Now add our values */
+ entry->haMnIsEntryDynamic = isDynamic;
+ entry->haMnAddr = homeAddr;
+ if (homeAgentAddr == 0) {
+ entry->haBindingIfaceAddr = haAddr;
+ } else {
+ entry->haBindingIfaceAddr = homeAgentAddr;
+ }
+ entry->haMnBindingCnt = 0;
+#ifdef RADIUS_ENABLED
+ GET_TIME(currentTime);
+ entry->haRadiusState = state;
+ entry->haRadiusLastLookupTime = currentTime;
+#endif /* RADIUS_ENABLED */
+ entry->haMnSPI = SPI;
+ entry->haPoolIdentifier = poolIdentifier;
+
+ if (mnNAI) {
+ (void) strncpy((char *)entry->haMnNAI, mnNAI, mnNaiLen);
+ entry->haMnNAI[mnNaiLen] = '\0';
+ entry->haMnNAILen = mnNaiLen;
+ }
+
+ HAinitID(&entry->haMnRegIDHigh, &entry->haMnRegIDLow,
+ saEntry->mipSecReplayMethod);
+
+ if (rwlock_init(&entry->haMnNodeLock, USYNC_THREAD, NULL)) {
+ syslog(LOG_ERR, "Unable to initialize read/write lock");
+ free(entry);
+ return (NULL);
+ }
+
+ if (poolIdentifier && mnNaiLen) {
+ /* Add the entry to the NAI hash */
+ if (linkHashTableEntryString(&haMobileNodeHash,
+ (unsigned char *)entry->haMnNAI, mnNaiLen,
+ entry, LOCK_WRITE)) {
+ syslog(LOG_ERR, "Unable to add Mobile Node entry to "
+ "hash table");
+ (void) rwlock_destroy(&entry->haMnNodeLock);
+ free(entry);
+ return (NULL);
+ }
+ } else {
+ if (linkHashTableEntryUint(&haMobileNodeHash, entry->haMnAddr,
+ entry, LOCK_WRITE)) {
+ syslog(LOG_ERR, "Unable to add Mobile Node entry to "
+ "hash table");
+ (void) rwlock_destroy(&entry->haMnNodeLock);
+ free(entry);
+ return (NULL);
+ }
+ }
+
+ return (entry);
+}
+
+/*
+ * Function: CreateMobilityAgentEntry
+ *
+ * Arguments: maEntryType - Whether the entry is dynamic or
+ * static
+ * address - The Mobility Agent's IP Address
+ * SPI - The Mobility Agent's SPI
+ * lifetime - The lifetime of the entry (for
+ * dynamic entries only)
+ *
+ * Description: This function will create the Mobility Agent
+ * Entry, and will add it to the Hash table.
+ * The SPI provided MUST have been previously
+ * defined, otherwise an error will occur.
+ *
+ * If the node is created as a dynamic entry,
+ * we will setup the entry's expiration time.
+ *
+ * The entry will be locked upon return.
+ * The caller is responsible for unlocking the
+ * node when it is finished with it.
+ *
+ * Returns: upon successful return, this function will
+ * return the Mobility Agent Entry pointer,
+ * otherwise NULL.
+ */
+MobilityAgentEntry *
+CreateMobilityAgentEntry(boolean_t isDynamic, ipaddr_t address,
+ uint32_t SPI, uint32_t lifetime)
+{
+ MobilityAgentEntry *entry = NULL;
+ time_t currentTime;
+
+ /*
+ * First, let's make sure that we do not already have
+ * this SPI defined.
+ */
+ if (findHashTableEntryUint(&mipSecAssocHash,
+ SPI, LOCK_NONE, NULL, 0, 0, 0) == NULL) {
+ syslog(LOG_ERR, "SPI entry %d not found", SPI);
+ return (NULL);
+ }
+
+ entry = (MobilityAgentEntry *)calloc(1, sizeof (MobilityAgentEntry));
+
+ if (!entry) {
+ syslog(LOG_CRIT, "FATAL: Unable to allocate MIP Agent Entry");
+ return (NULL);
+ }
+
+ /* Now add our values */
+ entry->maAddr = address;
+ entry->maSPI = SPI;
+ entry->maIsEntryDynamic = isDynamic;
+
+ if (isDynamic) {
+ /*
+ * Setup when the key expires...
+ */
+ GET_TIME(currentTime);
+ entry->maExpiration = currentTime + lifetime;
+ } else {
+ entry->maExpiration = TIME_INFINITY;
+ }
+
+ if (rwlock_init(&entry->maNodeLock, USYNC_THREAD, NULL)) {
+ syslog(LOG_ERR, "Unable to initialize read/write lock");
+ free(entry);
+ return (NULL);
+ }
+
+ if (linkHashTableEntryUint(&mipAgentHash, entry->maAddr, entry,
+ LOCK_WRITE)) {
+ syslog(LOG_ERR, "Unable to add MIP Agent entry to hash "
+ "table");
+ (void) rwlock_destroy(&entry->maNodeLock);
+ free(entry);
+ return (NULL);
+ }
+
+ return (entry);
+}
+
+/*
+ * Function: CreateSecAssocEntry
+ *
+ * Arguments: SPI - Security Parameter Index
+ * SaType - Whether the entry is dynamic or
+ * static
+ * ReplayProtection - The replay protection
+ * type
+ * AlgorithmType - The authentication algorithm
+ * type
+ * AlgorithmMode - The mode used for the
+ * algorithm.
+ * keyLength - The length of the key
+ * key - A pointer to the key
+ * lifetime - The lifetime of the entry (for
+ * dynamic entries only)
+ *
+ * Description: This function will create a Security Association
+ * entry, and will add it to the hash table. If the
+ * SPI already exists, we will return an error.
+ *
+ * If the node is created as a dynamic entry,
+ * we will setup the entry's expiration time.
+ *
+ * Also, if the SA is dynamic and a duplicate SA
+ * is received, it is updated, and no error is
+ * returned. (if the SA is *not* dynamic, an
+ * error *is* returned.)
+ *
+ * The entry will be locked upon return.
+ * The caller is responsible for unlocking the
+ * node when it is finished with it.
+ *
+ * Returns: upon successful return, this function will
+ * return the Security Association Entry
+ * pointer, otherwise NULL.
+ */
+MipSecAssocEntry *
+CreateSecAssocEntry(boolean_t isDynamic, uint32_t SPI, int ReplayProtection,
+ int AlgorithmType, int AlgorithmMode, int keyLength, char *key,
+ int lifetime)
+{
+ MipSecAssocEntry *entry = NULL;
+ time_t currentTime;
+ boolean_t alreadyInserted = _B_FALSE;
+
+ /*
+ * First, let's make sure that we do not already have
+ * this SPI defined.
+ */
+ if ((entry = findHashTableEntryUint(&mipSecAssocHash, SPI,
+ LOCK_WRITE, NULL, 0, 0, 0)) != NULL) {
+ if (isDynamic == _B_FALSE) {
+ syslog(LOG_ERR, "Duplicate SPI entry requested %d",
+ SPI);
+ (void) rw_unlock(&entry->mipSecNodeLock);
+ return (NULL);
+ } else {
+ alreadyInserted = _B_TRUE;
+ (void) fprintf(stderr, "Updating SPI %d\n", SPI);
+ }
+ }
+
+ /* Entry is set if dynamic and already found */
+ if (!entry) {
+ entry = (MipSecAssocEntry *)calloc(1,
+ sizeof (MipSecAssocEntry));
+ if (!entry) {
+ syslog(LOG_CRIT,
+ "FATAL: Unable to allocate Sec Assoc Entry");
+ return (NULL);
+ }
+ }
+
+ entry->mipSecSPI = SPI;
+ entry->mipSecReplayMethod = ReplayProtection;
+ entry->mipSecAlgorithmType = AlgorithmType;
+ entry->mipSecAlgorithmMode = AlgorithmMode;
+ entry->mipSecKeyLen = keyLength;
+ entry->mipSecIsEntryDynamic = isDynamic;
+
+ if (isDynamic) {
+ /*
+ * Setup when the key expires...
+ */
+ GET_TIME(currentTime);
+ entry->mipSecKeyLifetime = currentTime + lifetime;
+ } else {
+ entry->mipSecKeyLifetime = TIME_INFINITY;
+ }
+
+ (void) memcpy(entry->mipSecKey, key, keyLength);
+
+ /* If it is already in table, we already own it with the RWLOCK set */
+ if (!alreadyInserted) {
+ if (rwlock_init(&entry->mipSecNodeLock, USYNC_THREAD, NULL)) {
+ syslog(LOG_ERR, "Unable to initialize "
+ "read/write lock");
+ free(entry);
+ return (NULL);
+ }
+
+ if (linkHashTableEntryUint(&mipSecAssocHash, SPI,
+ entry, LOCK_WRITE)) {
+ syslog(LOG_ERR, "Unable to add Security "
+ "Assoc. entry to hash tabl");
+ (void) rwlock_destroy(&entry->mipSecNodeLock);
+ free(entry);
+ return (NULL);
+ }
+ }
+ return (entry);
+}
+
+/*
+ * Function: CreateInterfaceEntry
+ *
+ * Arguments: dev - A pointer to the device name
+ * regLifetime - The maximum registration lifetime that
+ * are willing to accept.
+ * advertiseOnBcast - Whether we will advertise on the
+ * broadcast address.
+ * minInterval - The minimum interval between adv.
+ * maxInterval - The maximum interval between adv.
+ * advLifetime - The maximum advertisement lifetime
+ * lifetime that we will advertise on this
+ * interface.
+ * advSeqNum - The sequence number that we will
+ * initially advertise.
+ * servicesFlags - The flags that we will advertise
+ * on the interface.
+ * prefixFlags - determines whether we will advertise
+ * the prefix length extension.
+ * reverseTunnelAllowed - are we going to allow MN's to
+ * request the reverse tunnel, and thereby send
+ * FA_REVERSE_TUNNEL_UNAVAILABLE errors to MN's
+ * requesting a reverse tunnel? Note, this is set to
+ * RT_NONE, RT_FA, RT_HA, or RT_BOTH depending on which
+ * of our agents is allowing the reverse tunnel.
+ * reverseTunnelRequired - are we going to require MN's to
+ * request the reverse tunnel, and thereby send
+ * FA_REVERSE_TUNNEL_REQUIRED errors to MN's not
+ * requesting a reverse tunnel? Note, this is set to
+ * RT_NONE, RT_FA, RT_HA, or RT_BOTH depending on which
+ * of our agents is requiring the reverse tunnel.
+ * advInterval - Advertisement interval for this interface
+ *
+ * Description: This function will create an interface entry,
+ * and will add it to the hash table. We will
+ * directly retrieve the interface's IP address and
+ * MAC address.
+ *
+ * Returns: int, 0 if successful.
+ *
+ * Comment: This function takes too many arguments. If this function
+ * ever needs a major change, passing a structure with all
+ * arguments should be considered.
+ *
+ */
+int
+CreateInterfaceEntry(char *dev, int regLifetime, boolean_t advertiseOnBcast,
+ int minInterval, int maxInterval, int advLifetime, uint16_t advSeqNum,
+ int servicesFlags, boolean_t prefixFlags, uint8_t reverseTunnelAllowed,
+ uint8_t reverseTunnelRequired, boolean_t advLimitUnsolicited, uint8_t
+ advInitCount, uint32_t advInterval, boolean_t isDynamic)
+{
+ MaAdvConfigEntry *entry;
+ char *cp;
+
+ /* If a virtual network interface is specified, warn the user */
+ if (dev != NULL) {
+ if ((strncmp(dev, VNI, VNISTRLEN) == 0)) {
+ cp = dev + VNISTRLEN;
+ cp += strspn(cp, "0123456789");
+ if (*cp == '\0' || *cp == ':' || *cp == '*')
+ syslog(LOG_WARNING, "%s specified. vni is a"
+ " virtual interface that does not transmit"
+ " or receive packets. See vni(7D)", dev);
+ }
+ }
+
+ /* Let's check for dynamic interface entry */
+ if (strchr(dev, '*') == NULL) {
+ entry = (MaAdvConfigEntry *) calloc(1,
+ sizeof (MaAdvConfigEntry));
+ if (entry == NULL) {
+ syslog(LOG_CRIT, "FATAL: Unable to allocate "
+ "AdvConfigEntry");
+ return (-2);
+ }
+ } else {
+ int len;
+ DynamicIfaceTypeEntry *dyn_entry;
+ DynamicIfaceTypeEntry *save_entry;
+
+ /*
+ * Since devicename contains '*', it must be an entry
+ * for dynamic interface.For dynamic interface entry
+ * in the config file, we do not create entry in the
+ * MaAdvConfigEntry[], rather we keep a linked list
+ * of dynamic interface types. For each type of dynamic
+ * interface entry, the attributes will be common and
+ * saved in DynamicIfaceTypeEntry data structure.
+ * When mipagent detects one new interface that matches
+ * with the same type and that is not an exisisting one
+ * that went through down-up cycle, then it creates a new
+ * entry and attaches to the MaAdvConfigEntry list
+ */
+ len = strlen(dev);
+ if (len > 0 && dev[len - 1] != '*') {
+ syslog(LOG_ERR,
+ "Invalid dynamic interface %s in mipagent.conf",
+ dev);
+ return (-1);
+ }
+ /* Replace '*' with null character */
+ dev[len -1] = '\0';
+ mipverbose(("CreateInterfaceEntry: dynamic device %s\n",
+ dev));
+
+
+ if (dynamicIfaceHead == NULL) {
+ dynamicIfaceHead = (DynamicIfaceTypeEntry *) calloc(1,
+ sizeof (DynamicIfaceTypeEntry));
+ dyn_entry = dynamicIfaceHead;
+ } else {
+
+ /* search if this type exists already */
+ dyn_entry = dynamicIfaceHead;
+ while (dyn_entry != NULL) {
+ if (strcmp(dyn_entry->dynamicIfcetype, dev)
+ == 0) {
+ mipverbose(("CreateInterfaceEntry:"
+ " Dynamic Entry already exists"
+ " %s\n", dev));
+ return (0);
+ }
+ save_entry = dyn_entry;
+ dyn_entry = dyn_entry->next;
+ }
+
+ dyn_entry = (DynamicIfaceTypeEntry *) calloc(1,
+ sizeof (DynamicIfaceTypeEntry));
+ if (dyn_entry != NULL) {
+ /* Link to the dynamicEntry list */
+ save_entry->next = dyn_entry;
+ }
+ }
+
+ /* Fill in the structure with the parameter values */
+ (void) strncpy(dyn_entry->dynamicIfcetype, dev, LIFNAMSIZ);
+ dyn_entry->AdvLimitUnsolicited = advLimitUnsolicited;
+ dyn_entry->AdvInitCount = advInitCount;
+ dyn_entry->AdvInterval = advInterval;
+ dyn_entry->AdvServiceflag = servicesFlags;
+ dyn_entry->AdvPrefixflag = prefixFlags;
+ dyn_entry->RevtunReqd = reverseTunnelRequired;
+ dyn_entry->RevtunAllowed = reverseTunnelAllowed;
+ dyn_entry->RegLifetime = regLifetime;
+ dyn_entry->AdvLifetime = advLifetime;
+ dyn_entry->advertiseOnBcast = advertiseOnBcast;
+ dyn_entry->next = NULL;
+
+ /* Set the global variable DynamicInterface */
+ DynamicInterface = _B_TRUE;
+ return (0);
+ } /* else dynamic entry */
+
+ /* Now add our interface values */
+ (void) strncpy(entry->maIfaceName, dev, (LIFNAMSIZ -1));
+
+ /*
+ * Simplify the configuration effort, read IP address, netmask
+ * and hardware address directly from the interface.
+ */
+ if (getIfaceInfo(entry->maIfaceName, &entry->maIfaceAddr,
+ &entry->maIfaceNetmask, &entry->maIfaceFlags,
+ &entry->maIfindex)) {
+ free(entry);
+ syslog(LOG_ERR, "Unable to get interface information: %m");
+ return (-1);
+ }
+
+ /*
+ * Save the first address usable for registering with the
+ * SNMP master.
+ */
+ if (subagent_addr != 0)
+ subagent_addr = entry->maIfaceAddr;
+
+ /*
+ * Don't attempt to get the hardware address if it's a point-to-point
+ * interface.
+ */
+ if ((entry->maIfaceFlags & IFF_POINTOPOINT) == 0) {
+ if (getEthernetAddr(entry->maIfaceName, entry->maIfaceHWaddr)) {
+ syslog(LOG_ERR, "Unable to get interface address "
+ "information on %s (%s)", dev, strerror(errno));
+ mipverbose(("Unable to get interface address "
+ "information on %s (%s)\n", dev, strerror(errno)));
+ free(entry);
+ return (-1);
+ }
+ }
+
+ entry->maAdvMaxRegLifetime = regLifetime;
+
+ /*
+ * TODO: Under Solaris, pkts for LINK_BCAST_ADDR seem to
+ * be sent on all of a host's interfaces. Don't know if this
+ * can be controlled using some options similar to
+ * IP_MULTICAST_IF.
+ */
+ if (advertiseOnBcast == 1) {
+ entry->maAdvAddr = inet_addr(LINK_BCAST_ADDR);
+ } else {
+ entry->maAdvAddr = inet_addr(LINK_MCAST_ADV_ADDR);
+ }
+
+ entry->maAdvMaxInterval = maxInterval;
+ entry->maAdvMinInterval = minInterval;
+ entry->maAdvMaxAdvLifetime = advLifetime;
+ entry->maAdvSeqNum = advSeqNum;
+ entry->maAdvServiceFlags = (char)servicesFlags;
+ entry->maAdvPrefixLenInclusion = prefixFlags;
+ entry->maReverseTunnelAllowed = reverseTunnelAllowed;
+ entry->maReverseTunnelRequired = reverseTunnelRequired;
+ entry->maAdvLimitUnsolicited = advLimitUnsolicited;
+ if (advLimitUnsolicited == _B_FALSE)
+ entry->maAdvInitCount = 1;
+ else
+ entry->maAdvInitCount = advInitCount;
+ entry->maAdvInterval = advInterval;
+ /* Set maNextAdvTime in getAndDispatchNetwork */
+ entry->maNextAdvTime = LONG_MAX;
+ /* The follwoing is always set false in this routine */
+ entry->maAdvDynamicInterface = isDynamic;
+
+ if (rwlock_init(&entry->maIfaceNodeLock, USYNC_THREAD, NULL)) {
+ syslog(LOG_ERR, "Unable to initialize read/write lock");
+ free(entry);
+ return (NULL);
+ }
+
+ /*
+ * Ok, this is just a temp hack, but we need to save the
+ * local address of the interface. Otherwise we will need to
+ * configure the home agent for each Mobile Node, which is
+ * just too much config for everybody. Given that we really
+ * only work with one interface, this is not a big deal, but
+ * this DOES need to be cleaned up.
+ */
+ haAddr = entry->maIfaceAddr;
+
+ /*
+ * We do not request that the node be locked in this case since
+ * we do not need the pointer to be returned. We are just calling
+ * and ensuring that a pointer was in fact returned.
+ */
+ if (linkHashTableEntryUint(&maAdvConfigHash, entry->maIfaceAddr, entry,
+ LOCK_NONE)) {
+ syslog(LOG_ERR, "Unable to add interface entry to hash table");
+ (void) rwlock_destroy(&entry->maIfaceNodeLock);
+ free(entry);
+ return (-1);
+ }
+
+ return (0);
+}
+
+
+/*
+ * Function: CreateTunlEntry
+ *
+ * Arguments: tnum - Tunnel number
+ * target - target address
+ * tunsrc - Tunnel source endpoint address
+ * muxfd - file desc of the stream that is
+ * associated with the tunnel.
+ *
+ * Description: This function will create a Tunnel
+ * entry, and will add it to the hash table.
+ *
+ * Returns: upon successful return, this function will
+ * return the Tunnel Entry pointer, otherwise NULL.
+ */
+
+MipTunlEntry *
+CreateTunlEntry(int tnum, ipaddr_t target, ipaddr_t tunsrc, int muxfd)
+{
+ MipTunlEntry *entry = NULL;
+
+ /*
+ * First, let's make sure that we do not already have
+ * this target defined.
+ */
+ if ((findHashTableEntryUint(&mipTunlHash, target,
+ LOCK_NONE, MipTunlEntryLookup, tunsrc, 0, 0)) != NULL) {
+ syslog(LOG_ERR, "Duplicate target entry requested %x", target);
+ return (NULL);
+ }
+
+ entry = (MipTunlEntry *)calloc(1, sizeof (MipTunlEntry));
+ if (!entry) {
+ syslog(LOG_CRIT, "FATAL: Unable to allocate Tunl Entry");
+ return (NULL);
+ }
+
+ entry->tunnelno = tnum;
+ entry->tunnelsrc = tunsrc;
+ entry->mux_fd = muxfd;
+
+ if (rwlock_init(&entry->TunlNodeLock, USYNC_THREAD, NULL)) {
+ syslog(LOG_ERR, "Unable to initialize read/write lock");
+ free(entry);
+ return (NULL);
+ }
+
+
+ if (linkHashTableEntryUint(&mipTunlHash, target, entry, LOCK_WRITE)) {
+ syslog(LOG_ERR, "Unable to add Tunnel entry to hash table");
+ (void) rwlock_destroy(&entry->TunlNodeLock);
+ free(entry);
+ return (NULL);
+ }
+
+ return (entry);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mipagent/setup.h b/usr/src/cmd/cmd-inet/usr.lib/mipagent/setup.h
new file mode 100644
index 0000000000..0cde6dd4cf
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mipagent/setup.h
@@ -0,0 +1,57 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _SETUP_H
+#define _SETUP_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * setup.h : Data structures and prototypes used by a Mobile IP agent
+ * to create data structures.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define TIME_INFINITY -1
+
+HaMobileNodeEntry *CreateMobileNodeEntry(boolean_t, ipaddr_t, char *,
+ uint32_t, ipaddr_t, uint32_t, char *, uint32_t);
+MobilityAgentEntry *CreateMobilityAgentEntry(boolean_t, ipaddr_t, uint32_t,
+ uint32_t);
+MipSecAssocEntry *CreateSecAssocEntry(boolean_t, uint32_t, int, int, int,
+ int, char *, int);
+int CreateInterfaceEntry(char *, int, boolean_t, int, int, int, uint16_t, int,
+ boolean_t, uint8_t, uint8_t, boolean_t, uint8_t, uint32_t, boolean_t);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SETUP_H */
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mipagent/snmp_appl.c b/usr/src/cmd/cmd-inet/usr.lib/mipagent/snmp_appl.c
new file mode 100644
index 0000000000..a28dd89456
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mipagent/snmp_appl.c
@@ -0,0 +1,409 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * file: mipagentsnmp_appl.c
+ *
+ * This file contains the main SNMP routines used for
+ * initialization and shutdown.
+ */
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <syslog.h>
+
+#include <impl.h>
+#include <pagent.h>
+
+#include "snmp_stub.h"
+#include "mip.h"
+
+/* GLOBAL VARIABLES */
+
+#ifndef lint
+char default_config_file[] = "/etc/snmp/conf/mipagent.reg";
+char default_sec_config_file[] = "/etc/snmp/conf/mipagent.acl";
+char default_error_file[] = "/var/snmp/mipagent.log";
+#endif
+
+static pthread_t snmpThreadId = 0;
+
+typedef struct {
+ int argc;
+ char *argv;
+} StartupArgs;
+
+static void mipagent_snmp_thread(StartupArgs *args);
+
+#define DEFAULT_SNMP_PORT 161
+
+extern ipaddr_t subagent_addr; /* this was set in setup.c */
+
+extern int SSASubagentOpen(int, char *);
+extern int SSARegSubagent(Agent *);
+extern int SSAMain(int, char **);
+
+/*
+ * Function: register_with_master
+ *
+ * Arguments: subagent_addr - Sub-Agent Address
+ *
+ * Description: This function will register the SNMP
+ * sub-agent with the master SNMP agent.
+ *
+ * Returns: int, 0 if successful
+ */
+static int
+/* LINTED E_FUNC_ARG_UNUSED */
+register_with_master(ipaddr_t subagent_addr)
+{
+ /* LINTED E_FUNC_VAR_UNUSED */
+ Agent subagent;
+
+ return (0);
+
+#if 0
+ /* NOTREACHED */
+
+ agentid = SSASubagentOpen(0, "mipagent");
+ (void) memset(&subagent, 0, sizeof (Agent));
+ subagent.agent_id = agentid;
+
+ /*
+ * Tell master we are ready
+ */
+ subagent.agent_status = SSA_OPER_STATUS_ACTIVE;
+ subagent.address.sin_family = AF_INET;
+ subagent.address.sin_port = DEFAULT_SNMP_PORT;
+ subagent.address.sin_addr.s_addr = subagent_addr;
+ if ((SSARegSubagent(&subagent)) == 0) {
+ return (-1);
+ }
+ return (0);
+#endif
+}
+
+/*
+ * Function: deregister_with_master
+ *
+ * Arguments: subagent_addr - Sub-Agent Address
+ *
+ * Description: This function will inform the
+ * SNMP Master agent that we are
+ * going away.
+ *
+ * Returns: int, 0 if successful
+ */
+static int
+/* LINTED E_FUNC_ARG_UNUSED */
+deregister_with_master(ipaddr_t subagent_addr)
+{
+ /* LINTED E_FUNC_VAR_UNUSED */
+ Agent subagent;
+
+ return (0);
+#if 0
+ /* NOTREACHED */
+
+ if (agentid) {
+ (void) memset(&subagent, 0, sizeof (Agent));
+ subagent.agent_id = agentid;
+
+ /*
+ * Tell master we are going away
+ */
+ subagent.agent_status = SSA_OPER_STATUS_DESTROY;
+ subagent.address.sin_family = AF_INET;
+ subagent.address.sin_port = DEFAULT_SNMP_PORT;
+ subagent.address.sin_addr.s_addr = subagent_addr;
+ if ((SSARegSubagent(&subagent)) == 0) {
+ return (-1);
+ }
+
+ agentid = 0;
+ }
+ return (0);
+#endif
+}
+
+#ifndef lint
+/*
+ * Function: agent_init
+ *
+ * Arguments:
+ *
+ * Description: Stub provided for the SNMP Sub-Agent
+ * initialization. We do not have anything
+ * to do here.
+ *
+ * Returns:
+ */
+void
+agent_init()
+{
+}
+
+
+/*
+ * Function: agent_end
+ *
+ * Arguments:
+ *
+ * Description: Stub provided for the SNMP Sub-Agent
+ * shutdown. We do not have anything
+ * to do here.
+ *
+ * Returns:
+ */
+void
+agent_end()
+{
+}
+
+
+/*
+ * Function: agent_loop
+ *
+ * Arguments:
+ *
+ * Description: The SNMP Loop function, which is
+ * provided because the sub-agent needs it.
+ *
+ * Returns:
+ */
+void
+agent_loop()
+{
+}
+
+
+/*
+ * Function: agent_select_info
+ *
+ * Arguments: fdset - File Descriptor set
+ * numfds - number of file descriptors
+ *
+ * Description: Another stub provided for the SNMP
+ * sub-agent.
+ *
+ * Returns:
+ */
+/* ARGSUSED */
+void
+agent_select_info(fd_set *fdset, int *numfds)
+{
+}
+
+
+/*
+ * Function: agent_select_callback
+ *
+ * Arguments: fdset - File Descriptor set
+ *
+ * Description: Callback routine for the SNMP Sub-Agent,
+ * which we are providing as a stub.
+ *
+ * Returns:
+ */
+/* ARGSUSED */
+void
+agent_select_callback(fd_set *fdset)
+{
+}
+#endif
+/*
+ * Function: startSNMPTaskThread
+ *
+ * Arguments: none
+ *
+ * Description: This function will start the SNMP
+ * sub-agent thread, and register with
+ * the Master SNMP thread.
+ *
+ * Returns: int, 0 if successful
+ */
+int
+startSNMPTaskThread(void)
+{
+ StartupArgs *args;
+ static char *all_args[3];
+ static char arg0[] = "mipagent";
+ static char arg1[] = "-d";
+ static char arg2[] = "1";
+ pthread_attr_t pthreadAttribute;
+ int result;
+
+ result = pthread_attr_init(&pthreadAttribute);
+ if (result) {
+ syslog(LOG_CRIT, "Error Initializing pthread.");
+ return (-1);
+ }
+
+ args = (StartupArgs *) malloc(sizeof (StartupArgs));
+ if (args == NULL) {
+ syslog(LOG_CRIT, "Unable to allocate memory.");
+ return (-1);
+ }
+
+ /*
+ * Call the SSAMain() with "-d 1" argument, so that it doesn't
+ * daemonize the process. "-d 1" attempts to generate debug output.
+ * When mipagent is daemonized, since the stdout is closed, this
+ * will have no effect. When mipagent is not deamonized for debugging
+ * purposes, this will generate useful debug info for SNMP.
+ */
+ all_args[0] = (char *)arg0;
+ all_args[1] = (char *)arg1;
+ all_args[2] = (char *)arg2;
+
+ args->argc = 3;
+ args->argv = (char *)all_args;
+
+ /*
+ * The thread is then started It is mandatory that all
+ * applications have a slaveThread() function declared,
+ * which will be called with the TCB.
+ */
+ result = pthread_create(&snmpThreadId, &pthreadAttribute,
+ (void *(*)()) mipagent_snmp_thread,
+ (void *)args);
+
+ if (result) {
+ syslog(LOG_CRIT, "pthread_create() failed.");
+ free(args);
+ return (-1);
+ }
+
+ /*
+ * In order for system resources the be properly cleaned up,
+ * we need to detach the thread. Otherwise, we need to wait for
+ * a pthread_join(), which we do not want.
+ */
+ result = pthread_detach(snmpThreadId);
+
+ if (result) {
+ syslog(LOG_CRIT, "pthread_detach() failed.");
+ free(args);
+ return (-1);
+ }
+
+ return (0);
+
+}
+
+/*
+ * Function: killSNMPTaskThread
+ *
+ * Arguments:
+ *
+ * Description: This function is used to shutdown the
+ * SNMP Sub-Agent thread.
+ *
+ * Returns:
+ */
+int
+killSNMPTaskThread()
+{
+ int result;
+
+ if (snmpThreadId) {
+ if (deregister_with_master(subagent_addr)) {
+ syslog(LOG_CRIT, "Unable to deregister with sub-agent");
+ }
+
+ /*
+ * Next we need to kill the dispatching thread.
+ */
+ result = pthread_cancel(snmpThreadId);
+
+ if (result) {
+ /*
+ * Well, there's not much we can do here..
+ */
+ syslog(LOG_CRIT, "Unable to kill snmp thread");
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * Function: mipagent_snmp_thread
+ *
+ * Arguments: args - Command Line Arguments
+ *
+ * Description: This function is the main SNMP Sub-Agent
+ * thread. The function will call SSAMain, which
+ * never returns (unless an error occured).
+ *
+ * This thread will remain active until either
+ * the SNMP Sub-Agent main routines returns or
+ * the killSNMPTaskThread() function is called.
+ *
+ * Returns:
+ */
+static void
+mipagent_snmp_thread(StartupArgs *args)
+{
+ int argc;
+ int result;
+ char **argv;
+
+ /*
+ * Register ourselves as a sub-agent with the master SNMP
+ * agent.
+ */
+ result = register_with_master(subagent_addr);
+
+ if (result) {
+ syslog(LOG_CRIT, "Could not register with master SNMP agent.");
+ free(args);
+ pthread_exit(NULL);
+ }
+
+ /*
+ * Let's do the SSAMain thingy... Note that this function should,
+ * in theory, never return. This function will interface with the
+ * SNMP Master Agent.
+ */
+ argc = args->argc;
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ argv = (char **)args->argv;
+
+ SSAMain(argc, argv);
+
+ free(args);
+
+ if (deregister_with_master(subagent_addr)) {
+ syslog(LOG_CRIT, "Unable to deregister with sub-agent");
+ }
+
+ pthread_exit(NULL);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mipagent/snmp_faCOAEntry.c b/usr/src/cmd/cmd-inet/usr.lib/mipagent/snmp_faCOAEntry.c
new file mode 100644
index 0000000000..df378ec69d
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mipagent/snmp_faCOAEntry.c
@@ -0,0 +1,94 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * file: mipagentsnmp_faCOAEntry.c
+ *
+ * This file contains the SNMP routines used to retrieve
+ * the Foreign Agent's Care of Address Information.
+ */
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+
+#include <impl.h>
+
+#include "snmp_stub.h"
+#include "agent.h"
+
+/* faCOAEntry */
+
+/*
+ * Function: get_faCOAEntry
+ *
+ * Arguments: search_type - The type of search (first, next, exact)
+ * faCOAEntry_data - Pointer to a pointer
+ * which will contain the COA entry upon
+ * successful completion.
+ * index - Pointer to the current index
+ *
+ * Description: Since we currently do not support this, we will
+ * simply return and end of table SNMP error code.
+ *
+ * Returns: END_OF_TABLE, meaning there are no more entries
+ * in our table.
+ */
+/* ARGSUSED */
+extern int
+get_faCOAEntry(int search_type, FaCOAEntry_t **faCOAEntry_data,
+ IndexType *index)
+{
+ /*
+ * Perhaps one day we will support Care of Addresses, but
+ * for now we will return an end of table error.
+ */
+ return (END_OF_TABLE);
+
+}
+
+
+/*
+ * Function: free_faCOAEntry
+ *
+ * Arguments: faCOAEntry - Pointer to a previously
+ * allocated SNMP COA entry
+ *
+ * Description: This function is called to free a previously
+ * allocated SNMP COA entry.
+ *
+ * Returns:
+ */
+void
+free_faCOAEntry(FaCOAEntry_t *faCOAEntry)
+{
+ if (faCOAEntry) {
+ free(faCOAEntry);
+ faCOAEntry = NULL;
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mipagent/snmp_faVisitorEntry.c b/usr/src/cmd/cmd-inet/usr.lib/mipagent/snmp_faVisitorEntry.c
new file mode 100644
index 0000000000..58dc49b19f
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mipagent/snmp_faVisitorEntry.c
@@ -0,0 +1,356 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1999-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * file: mipagentsnmp_faVisitorEntry.c
+ *
+ * This file contains the SNMP routines used to retrieve
+ * the Foreign Agent's Visitor Entry.
+ */
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <memory.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include <impl.h>
+#include <snmp.h>
+
+#include "snmp_stub.h"
+#include "agent.h"
+
+extern HashTable faVisitorHash;
+extern char *ntoa(uint32_t, char *);
+
+/* faVisitorEntry */
+
+/*
+ * Function: get_faVisitorEntry
+ *
+ * Arguments: search_type - The type of search (first, next, exact)
+ * faVisitorEntry_data - Pointer to a pointer which
+ * will contain the visitor entry upon
+ * successful completion.
+ * index - Pointer to the current index
+ *
+ * Description: This function will allocate the memory required for
+ * the visitor entry, and will find the appropriate
+ * visitor entry based on the index provided. If the
+ * search type is set to FIRST_ENTRY, we will return
+ * the first visitor entry in the Hash Table, otherwise
+ * the index is used.
+ *
+ * The visitor entry is then setup with the values found
+ * in the entry from the hash table and returned to the
+ * caller.
+ *
+ * Note, the caller is responsible for either freeing the
+ * memory, or calling free_faVisitorEntry()
+ *
+ * Returns: int, 0 if successful
+ */
+int
+get_faVisitorEntry(int search_type, FaVisitorEntry_t **faVisitorEntry_data,
+ IndexType *index)
+{
+ Integer *integer;
+ String *string;
+ FaVisitorEntry *faVisitorEntry = NULL;
+ HashEntry *hashEntry;
+ int i;
+ int j;
+ int return_code = SNMP_ERR_NOERROR;
+ time_t currentTime;
+ char buffer[258];
+ boolean_t found = _B_FALSE;
+ struct ether_addr ether;
+ char *tmp_buf;
+
+ /*
+ * Allocate some memory to handle the request.
+ */
+ *faVisitorEntry_data =
+ (FaVisitorEntry_t *)calloc(1, sizeof (FaVisitorEntry_t));
+
+ if (faVisitorEntry_data == NULL) {
+ return (SNMP_ERR_GENERR);
+ }
+
+ /*
+ * In the case, the search_type is FIRST_ENTRY or NEXT_ENTRY
+ * this function should modify the index argument to the
+ * appropriate value
+ */
+ switch (search_type) {
+ case FIRST_ENTRY:
+ /*
+ * We are looking for the first entry in the list.
+ */
+ index->value[0] = 1;
+ index->len = 1;
+ break;
+ case NEXT_ENTRY:
+ /*
+ * Increment the index value.
+ */
+ index->value[0]++;
+ break;
+ case EXACT_ENTRY:
+ /*
+ * We don't need to play around with the
+ * index for this search type.
+ */
+ break;
+ default:
+ return_code = SNMP_ERR_GENERR;
+ goto the_end;
+ }
+
+ /*
+ * Now search for the entry...
+ */
+ for (i = 0, j = 1; i < HASH_TBL_SIZE && found == _B_FALSE; i++) {
+ if (faVisitorHash.buckets[i]) {
+ /*
+ * Lock the bucket
+ */
+ (void) rw_rdlock(&faVisitorHash.bucketLock[i]);
+
+ /*
+ * Look for the entry we need
+ */
+ hashEntry = faVisitorHash.buckets[i];
+ while (hashEntry != NULL) {
+ if (j == index->value[0]) {
+ faVisitorEntry = hashEntry->data;
+ found = _B_TRUE;
+
+ /*
+ * Lock the node
+ */
+ (void) rw_rdlock(&faVisitorEntry->
+ faVisitorNodeLock);
+
+ break;
+ }
+ hashEntry = hashEntry->next;
+ j++;
+ }
+
+ /*
+ * Unlock the bucket
+ */
+ (void) rw_unlock(&faVisitorHash.bucketLock[i]);
+ }
+ }
+
+ if (faVisitorEntry == NULL) {
+ return_code = END_OF_TABLE;
+ goto the_end;
+ }
+
+ /*
+ * Get the visitor entry's IP Address.
+ */
+ (void) ntoa(faVisitorEntry->faVisitorAddr, buffer);
+ string = &((*faVisitorEntry_data)->faVisitorIPAddress);
+ string->chars = (unsigned char *)strdup(buffer);
+ if (string->chars == NULL) {
+ return_code = SNMP_ERR_GENERR;
+ goto the_end;
+ }
+ string->len = strlen(buffer);
+
+ /*
+ * Get the visitor entry's link layer address.
+ */
+ (void) memcpy(ether.ether_addr_octet,
+ faVisitorEntry->faVisitorSlla.sdl_data, ETHERADDRL);
+ tmp_buf = ether_ntoa(&ether);
+ if (tmp_buf == NULL) {
+ return_code = SNMP_ERR_GENERR;
+ goto the_end;
+ }
+ string = &((*faVisitorEntry_data)->faVisitorSlla);
+ string->chars = (unsigned char *)strdup(tmp_buf);
+ if (string->chars == NULL) {
+ return_code = SNMP_ERR_GENERR;
+ goto the_end;
+ }
+ string->len = strlen(tmp_buf);
+
+ /*
+ * Let's get the visitor entry's Home Agent Address
+ */
+ (void) ntoa(faVisitorEntry->faVisitorHomeAgentAddr, buffer);
+ string = &((*faVisitorEntry_data)->faVisitorHomeAgentAddress);
+ string->chars = (unsigned char *)strdup(buffer);
+ if (string->chars == NULL) {
+ return_code = SNMP_ERR_GENERR;
+ goto the_end;
+ }
+ string->len = strlen(buffer);
+
+ /*
+ * Let's get the visitor entry's Home Address
+ */
+ (void) ntoa(faVisitorEntry->faVisitorHomeAddr, buffer);
+ string = &((*faVisitorEntry_data)->faVisitorHomeAddress);
+
+ string->chars = (unsigned char *)strdup(buffer);
+ if (string->chars == NULL) {
+ return_code = SNMP_ERR_GENERR;
+ goto the_end;
+ }
+ string->len = strlen(buffer);
+
+ /*
+ * And now we return the amount of time that was granted to
+ * the visitor.
+ */
+ integer = &((*faVisitorEntry_data)->faVisitorTimeGranted);
+ *integer = faVisitorEntry->faVisitorTimeGranted;
+
+ /*
+ * And now we return the amount of time remaining for
+ * the visitor.
+ */
+ integer = &((*faVisitorEntry_data)->faVisitorTimeRemaining);
+ GET_TIME(currentTime);
+ *integer = currentTime -
+ faVisitorEntry->faVisitorTimeExpires;
+
+ /*
+ * And now we return the Registration Flags.
+ */
+ integer = &((*faVisitorEntry_data)->faVisitorRegFlags);
+ *integer = (int)faVisitorEntry->faVisitorRegFlags;
+
+ /*
+ * And now we return the lower 32-bits of the visitor's ID.
+ */
+ integer = &((*faVisitorEntry_data)->faVisitorRegIDLow);
+ *integer = (int)faVisitorEntry->faVisitorRegIDLow;
+
+ /*
+ * And now we return the high 32-bits of the visitor's ID.
+ */
+ integer = &((*faVisitorEntry_data)->faVisitorRegIDHigh);
+ *integer = (int)faVisitorEntry->faVisitorRegIDHigh;
+
+
+ /*
+ * And now we return the high 32-bits of the visitor's ID.
+ */
+ integer = &((*faVisitorEntry_data)->faVisitorRegIsAccepted);
+ if (faVisitorEntry->faVisitorRegIsAccepted == _B_TRUE) {
+ *integer = 1;
+ } else {
+ *integer = 2;
+ }
+
+ /*
+ * And now we return the inbound interface index on which the
+ * registration request was received
+ */
+ integer = &((*faVisitorEntry_data)->faVisitorInIfindex);
+ *integer = (int)faVisitorEntry->faVisitorInIfindex;
+
+the_end:
+ if (faVisitorEntry != NULL) {
+ /*
+ * Unlock the bucket
+ */
+ (void) rw_unlock(&faVisitorEntry->faVisitorNodeLock);
+ }
+
+ if (return_code != SNMP_ERR_NOERROR) {
+ free_faVisitorEntry(*faVisitorEntry_data);
+ *faVisitorEntry_data = NULL;
+ }
+
+ return (return_code);
+}
+
+
+/*
+ * Function: free_faVisitorEntry
+ *
+ * Arguments: faVisitorEntry - Pointer to a previously
+ * allocated SNMP visitor entry
+ *
+ * Description: This function is called to free a previously
+ * allocated SNMP visitor entry.
+ *
+ * Returns:
+ */
+void
+free_faVisitorEntry(FaVisitorEntry_t *faVisitorEntry)
+{
+ String *string;
+
+ if (faVisitorEntry) {
+ /*
+ * The template generates code that checks both
+ * the pointer, and the length. I am not sure
+ * if this is excessive, so I will leave it as is.
+ */
+ string = &(faVisitorEntry->faVisitorIPAddress);
+ if (string->chars != NULL && string->len != 0) {
+ free(string->chars);
+ string->len = 0;
+ }
+
+ string = &(faVisitorEntry->faVisitorHomeAddress);
+ if (string->chars != NULL && string->len != 0) {
+ free(string->chars);
+ string->len = 0;
+ }
+
+ string = &(faVisitorEntry->faVisitorHomeAgentAddress);
+ if (string->chars != NULL && string->len != 0) {
+ free(string->chars);
+ string->len = 0;
+ }
+
+ string = &(faVisitorEntry->faVisitorSlla);
+ if (string->chars != NULL && string->len != 0) {
+ free(string->chars);
+ string->len = 0;
+ }
+
+ free(faVisitorEntry);
+ faVisitorEntry = NULL;
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mipagent/snmp_haCounterEntry.c b/usr/src/cmd/cmd-inet/usr.lib/mipagent/snmp_haCounterEntry.c
new file mode 100644
index 0000000000..8b3654a1e1
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mipagent/snmp_haCounterEntry.c
@@ -0,0 +1,239 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * file: mipagentsnmp_haCounterEntry.c
+ *
+ * This file contains the SNMP routines used to retrieve
+ * the Home Agent's Mobile Node Counter Information.
+ */
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+
+#include <impl.h>
+#include <snmp.h>
+
+#include "snmp_stub.h"
+#include "agent.h"
+
+extern HashTable haMobileNodeHash;
+
+
+/*
+ * Function: get_haCounterEntry
+ *
+ * Arguments: search_type - The type of search (first, next, exact)
+ * haCounterEntry_data - Pointer to a pointer which
+ * will contain the Mobile Node counter entry
+ * upon successful completion.
+ * index - Pointer to the current index
+ *
+ * Description: This function will allocate the memory required for
+ * the Mobile Node counter entry, and will find the
+ * appropriate Mobile Node counter based on the index
+ * provided. If the search type is set to FIRST_ENTRY,
+ * we will return the first Mobile Node counter in the
+ * Hash Table, otherwise the index is used.
+ *
+ * The Mobile Node counter entry is then setup with the
+ * values found in the entry from the hash table and
+ * returned to the caller.
+ *
+ * Note, the caller is responsible for either freeing the
+ * memory, or calling free_haCounterEntry()
+ *
+ * Returns: int, 0 if successful
+ */
+extern int
+get_haCounterEntry(int search_type,
+ HaCounterEntry_t **haCounterEntry_data, IndexType *index)
+{
+
+ Integer *integer;
+ HaMobileNodeEntry *haMobileNodeEntry = NULL;
+ HashEntry *hashEntry;
+ int i;
+ int j;
+ int return_code = SNMP_ERR_NOERROR;
+ boolean_t found = _B_FALSE;
+
+ *haCounterEntry_data =
+ (HaCounterEntry_t *)calloc(1, sizeof (HaCounterEntry_t));
+
+ if (haCounterEntry_data == NULL) {
+ return (SNMP_ERR_GENERR);
+ }
+
+ /*
+ * In the case, the search_type is FIRST_ENTRY or NEXT_ENTRY
+ * this function should modify the index argument to the
+ * appropriate value
+ */
+ switch (search_type) {
+ case FIRST_ENTRY:
+ /*
+ * We are looking for the first entry in the list.
+ */
+ index->value[0] = 1;
+ index->len = 1;
+ break;
+ case NEXT_ENTRY:
+ /*
+ * Increment the index value.
+ */
+ index->value[0]++;
+ break;
+ case EXACT_ENTRY:
+ /*
+ * We don't need to play around with the
+ * index for this search type.
+ */
+ break;
+ default:
+ return_code = SNMP_ERR_GENERR;
+ goto the_end;
+ }
+
+ /*
+ * Now search for the entry...
+ */
+ for (i = 0, j = 1; i < HASH_TBL_SIZE && found == _B_FALSE; i++) {
+ if (haMobileNodeHash.buckets[i]) {
+ /*
+ * Lock the bucket
+ */
+ (void) rw_rdlock(&haMobileNodeHash.bucketLock[i]);
+
+ hashEntry = haMobileNodeHash.buckets[i];
+ while (hashEntry != NULL) {
+ if (j == index->value[0]) {
+ haMobileNodeEntry = hashEntry->data;
+ found = _B_TRUE;
+
+ /*
+ * Lock the node
+ */
+ (void) rw_rdlock(&haMobileNodeEntry->
+ haMnNodeLock);
+
+ break;
+ }
+ hashEntry = hashEntry->next;
+ j++;
+ }
+
+ /*
+ * Unlock the bucket
+ */
+ (void) rw_unlock(&haMobileNodeHash.bucketLock[i]);
+ }
+ }
+
+ if (haMobileNodeEntry == NULL) {
+ return_code = END_OF_TABLE;
+ goto the_end;
+ }
+
+ /*
+ * And now we return the number of times service was accepted for the
+ * Mobile Node.
+ */
+ integer = &((*haCounterEntry_data)->haServiceRequestsAccepted);
+ *integer = (int)haMobileNodeEntry->haServiceRequestsAcceptedCnt;
+
+ /*
+ * And now we return the number of times service was denied for the
+ * Mobile Node.
+ */
+ integer = &((*haCounterEntry_data)->haServiceRequestsDenied);
+ *integer = (int)haMobileNodeEntry->haServiceRequestsDeniedCnt;
+
+ /*
+ * And now we return the total amount of time (in seconds) that
+ * service was provided to the Mobile Node.
+ */
+ integer = &((*haCounterEntry_data)->haOverallServiceTime);
+ *integer = (int)haMobileNodeEntry->haOverallServiceTime;
+
+ /*
+ * And now we return the last time service was accepted.
+ */
+ integer = &((*haCounterEntry_data)->haRecentServiceAcceptedTime);
+ *integer = (int)haMobileNodeEntry->haRecentServiceAcceptedTime;
+
+ /*
+ * And now we return the last time service was denied.
+ */
+ integer = &((*haCounterEntry_data)->haRecentServiceDeniedTime);
+ *integer = (int)haMobileNodeEntry->haRecentServiceDeniedTime;
+
+ /*
+ * And now we return the reason for the last denial of service.
+ */
+ integer = &((*haCounterEntry_data)->haRecentServiceDeniedCode);
+ *integer = (int)haMobileNodeEntry->haRecentServiceDeniedCode;
+
+the_end:
+ if (haMobileNodeEntry != NULL) {
+ /*
+ * Unlock the node
+ */
+ (void) rw_unlock(&haMobileNodeEntry->haMnNodeLock);
+ }
+
+ if (return_code != SNMP_ERR_NOERROR) {
+ free_haCounterEntry(*haCounterEntry_data);
+ *haCounterEntry_data = NULL;
+ }
+
+ return (return_code);
+}
+
+
+/*
+ * Function: free_haCounterEntry
+ *
+ * Arguments: haCounterEntry - Pointer to a previously
+ * allocated SNMP Mobile Node counter
+ * entry
+ *
+ * Description: This function is called to free a previously
+ * allocated SNMP Mobile Node counter entry.
+ *
+ * Returns:
+ */
+void
+free_haCounterEntry(HaCounterEntry_t *haCounterEntry)
+{
+ if (haCounterEntry) {
+ free(haCounterEntry);
+ haCounterEntry = NULL;
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mipagent/snmp_haMobilityBindingEntry.c b/usr/src/cmd/cmd-inet/usr.lib/mipagent/snmp_haMobilityBindingEntry.c
new file mode 100644
index 0000000000..8d1e7fbcab
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mipagent/snmp_haMobilityBindingEntry.c
@@ -0,0 +1,322 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1999-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * file: mipagentsnmp_haMobilityBindingEntry.c
+ *
+ * This file contains the SNMP routines used to retrieve
+ * the Home Agent's Mobile Node Binding Information.
+ */
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <impl.h>
+#include <snmp.h>
+
+#include "snmp_stub.h"
+#include "agent.h"
+
+extern HashTable haMobileNodeHash;
+extern char *ntoa(uint32_t, char *);
+
+/*
+ * Function: get_haMobilityBindingEntry
+ *
+ * Arguments: search_type - The type of search (first, next, exact)
+ * haMobilityBindingEntry_data - Pointer to a pointer
+ * which will contain the binding entry upon
+ * successful completion.
+ * index - Pointer to the current index
+ *
+ * Description: This function will allocate the memory required for
+ * the binding entry, and will find the appropriate
+ * binding entry based on the index provided. If the
+ * search type is set to FIRST_ENTRY, we will return
+ * the first binding entry in the Hash Table, otherwise
+ * the index is used.
+ *
+ * The binding entry is then setup with the values found
+ * in the entry from the hash table and returned to the
+ * caller.
+ *
+ * Note, the caller is responsible for either freeing the
+ * memory, or calling free_haMobilityBindingEntry()
+ *
+ * Returns: int, 0 if successful
+ */
+extern int
+get_haMobilityBindingEntry(int search_type,
+ HaMobilityBindingEntry_t **haMobilityBindingEntry_data,
+ IndexType *index)
+{
+ Integer *integer;
+ String *string;
+ HaMobileNodeEntry *haMobileNodeEntry = NULL;
+ HaBindingEntry *haBindingEntry = NULL;
+ HashEntry *hashEntry;
+ int i;
+ int j;
+ int return_code = SNMP_ERR_NOERROR;
+ time_t currentTime;
+ char buffer[258];
+ boolean_t found = _B_FALSE;
+
+ *haMobilityBindingEntry_data =
+ (HaMobilityBindingEntry_t *)calloc(1,
+ sizeof (HaMobilityBindingEntry_t));
+
+ if (haMobilityBindingEntry_data == NULL) {
+ return (SNMP_ERR_GENERR);
+ }
+
+ /*
+ * In the case, the search_type is FIRST_ENTRY or NEXT_ENTRY
+ * this function should modify the index argument to the
+ * appropriate value
+ */
+ switch (search_type) {
+ case FIRST_ENTRY:
+ /*
+ * We are looking for the first entry in the list.
+ */
+ index->value[0] = 1;
+ index->len = 1;
+ break;
+ case NEXT_ENTRY:
+ /*
+ * Increment the index value.
+ */
+ index->value[0]++;
+ break;
+ case EXACT_ENTRY:
+ /*
+ * We don't need to play around with the
+ * index for this search type.
+ */
+ break;
+ default:
+ return_code = SNMP_ERR_GENERR;
+ goto the_end;
+ }
+
+ /*
+ * Now search for the entry...
+ */
+ for (i = 0, j = 1; i < HASH_TBL_SIZE && found == _B_FALSE; i++) {
+ if (haMobileNodeHash.buckets[i]) {
+ /*
+ * Lock the bucket
+ */
+ (void) rw_rdlock(&haMobileNodeHash.bucketLock[i]);
+
+ hashEntry = haMobileNodeHash.buckets[i];
+ while (hashEntry != NULL) {
+ if (j == index->value[0]) {
+ haMobileNodeEntry = hashEntry->data;
+ found = _B_TRUE;
+
+ /*
+ * Lock the node
+ */
+ (void) rw_rdlock(&haMobileNodeEntry->
+ haMnNodeLock);
+
+ break;
+ }
+ hashEntry = hashEntry->next;
+ j++;
+ }
+
+ /*
+ * Unlock the bucket
+ */
+ (void) rw_unlock(&haMobileNodeHash.bucketLock[i]);
+ }
+ }
+
+ if (haMobileNodeEntry == NULL) {
+ return_code = END_OF_TABLE;
+ goto the_end;
+ }
+
+ haBindingEntry = haMobileNodeEntry->bindingEntries;
+
+ if (haBindingEntry != NULL) {
+ /*
+ * Get the Mobile Node's IP Address.
+ */
+ (void) ntoa(haBindingEntry->haBindingMN, buffer);
+
+ string =
+ &((*haMobilityBindingEntry_data)->haMobilityBindingMN);
+ string->chars = (unsigned char *)malloc(strlen(buffer) + 1);
+ if (string->chars == NULL) {
+ return_code = SNMP_ERR_GENERR;
+ goto the_end;
+ }
+ (void) strcpy((char *)string->chars, buffer);
+ string->len = strlen(buffer);
+
+ /*
+ * Get the Care of Address.
+ */
+ (void) ntoa(haBindingEntry->haBindingCOA, buffer);
+ string =
+ &((*haMobilityBindingEntry_data)->haMobilityBindingCOA);
+
+ string->chars = (unsigned char *)malloc(strlen(buffer) + 1);
+ if (string->chars == NULL) {
+ return_code = SNMP_ERR_GENERR;
+ goto the_end;
+ }
+ (void) strcpy((char *)string->chars, buffer);
+ string->len = strlen(buffer);
+
+ /*
+ * Get the Binding Source Address.
+ */
+ (void) ntoa(haBindingEntry->haBindingSrcAddr, buffer);
+
+ string =
+ &((*haMobilityBindingEntry_data)->
+ haMobilityBindingSourceAddress);
+
+ string->chars = (unsigned char *)malloc(strlen(buffer) + 1);
+ if (string->chars == NULL) {
+ return_code = SNMP_ERR_GENERR;
+ goto the_end;
+ }
+ (void) strcpy((char *)string->chars, buffer);
+ string->len = strlen(buffer);
+
+ /*
+ * And now we return the Registration Flags.
+ */
+ integer =
+ &((*haMobilityBindingEntry_data)->
+ haMobilityBindingRegFlags);
+ *integer = (int)haBindingEntry->haBindingRegFlags;
+
+ /*
+ * And now we return the Low order Registration ID.
+ */
+ integer = &((*haMobilityBindingEntry_data)->
+ haMobilityBindingRegIDLow);
+ *integer = (int)haMobileNodeEntry->haMnRegIDLow;
+
+ /*
+ * And now we return the High order Registration ID.
+ */
+ integer =
+ &((*haMobilityBindingEntry_data)->
+ haMobilityBindingRegIDHigh);
+ *integer = (int)haMobileNodeEntry->haMnRegIDHigh;
+
+ /*
+ * And now we return the Registration Time Granted.
+ */
+ integer =
+ &((*haMobilityBindingEntry_data)->
+ haMobilityBindingTimeGranted);
+ *integer = (int)haBindingEntry->haBindingTimeGranted;
+
+ /*
+ * And now we return the Registration Time Remaining.
+ */
+ integer =
+ &((*haMobilityBindingEntry_data)->
+ haMobilityBindingTimeRemaining);
+ GET_TIME(currentTime);
+ *integer = currentTime -
+ (int)haBindingEntry->haBindingTimeExpires;
+ }
+
+the_end:
+ if (haMobileNodeEntry != NULL) {
+ /*
+ * Unlock the node
+ */
+ (void) rw_unlock(&haMobileNodeEntry->haMnNodeLock);
+ }
+
+ if (return_code != SNMP_ERR_NOERROR) {
+ free_haMobilityBindingEntry(*haMobilityBindingEntry_data);
+ *haMobilityBindingEntry_data = NULL;
+ }
+
+ return (return_code);
+}
+
+
+/*
+ * Function: free_haMobilityBindingEntry
+ *
+ * Arguments: haMobilityBindingEntry - Pointer to a previously
+ * allocated SNMP binding entry
+ *
+ * Description: This function is called to free a previously
+ * allocated SNMP binding entry.
+ *
+ * Returns:
+ */
+void
+free_haMobilityBindingEntry(HaMobilityBindingEntry_t *haMobilityBindingEntry)
+{
+ String *string;
+
+ if (haMobilityBindingEntry) {
+ /*
+ * The template generates code that checks both
+ * the pointer, and the length. I am not sure
+ * if this is excessive, so I will leave it as is.
+ */
+ string = &(haMobilityBindingEntry->haMobilityBindingMN);
+ if (string->chars != NULL && string->len != 0) {
+ free(string->chars);
+ string->len = 0;
+ }
+
+ string = &(haMobilityBindingEntry->haMobilityBindingCOA);
+ if (string->chars != NULL && string->len != 0) {
+ free(string->chars);
+ string->len = 0;
+ }
+
+ string =
+ &(haMobilityBindingEntry->haMobilityBindingSourceAddress);
+ if (string->chars != NULL && string->len != 0) {
+ free(string->chars);
+ string->len = 0;
+ }
+ free(haMobilityBindingEntry);
+ haMobilityBindingEntry = NULL;
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mipagent/snmp_maAdvConfigEntry.c b/usr/src/cmd/cmd-inet/usr.lib/mipagent/snmp_maAdvConfigEntry.c
new file mode 100644
index 0000000000..8952083e18
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mipagent/snmp_maAdvConfigEntry.c
@@ -0,0 +1,285 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * file: mipagentsnmp_maAdvConfigEntry.c
+ *
+ * This file contains the SNMP routines used to retrieve
+ * the Mobility Agent's Interface Information.
+ */
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <impl.h>
+#include <snmp.h>
+
+#include "snmp_stub.h"
+#include "agent.h"
+
+extern HashTable maAdvConfigHash;
+extern char *ntoa(uint32_t, char *);
+
+/*
+ * Function: get_maAdvConfigEntry
+ *
+ * Arguments: search_type - The type of search (first, next, exact)
+ * maAdvConfigEntry_data - Pointer to a pointer which
+ * will contain the interface entry upon
+ * successful completion.
+ * index - Pointer to the current index
+ *
+ * Description: This function will allocate the memory required for
+ * the interface entry, and will find the appropriate
+ * interface entry based on the index provided. If the
+ * search type is set to FIRST_ENTRY, we will return
+ * the first interface entry in the Hash Table, otherwise
+ * the index is used.
+ *
+ * The interface entry is then setup with the values found
+ * in the entry from the hash table and returned to the
+ * caller.
+ *
+ * Note, the caller is responsible for either freeing the
+ * memory, or calling free_maAdvConfigEntry()
+ *
+ * Returns: int, 0 if successful
+ */
+extern int
+get_maAdvConfigEntry(int search_type,
+ MaAdvConfigEntry_t **maAdvConfigEntry_data,
+ IndexType *index)
+{
+ Integer *integer;
+ String *string;
+ MaAdvConfigEntry *maAdvConfigEntry = NULL;
+ HashEntry *hashEntry;
+ int i;
+ int j;
+ int len;
+ int return_code = SNMP_ERR_NOERROR;
+ char buffer[258];
+ boolean_t found = _B_FALSE;
+
+ *maAdvConfigEntry_data =
+ (MaAdvConfigEntry_t *)calloc(1, sizeof (MaAdvConfigEntry_t));
+
+ if (maAdvConfigEntry_data == NULL) {
+ return (SNMP_ERR_GENERR);
+ }
+
+ /*
+ * In the case, the search_type is FIRST_ENTRY or NEXT_ENTRY
+ * this function should modify the index argument to the
+ * appropriate value
+ */
+ switch (search_type) {
+ case FIRST_ENTRY:
+ /*
+ * We are looking for the first entry in the list.
+ */
+ index->value[0] = 1;
+ index->len = 1;
+ break;
+ case NEXT_ENTRY:
+ /*
+ * Increment the index value.
+ */
+ index->value[0]++;
+ break;
+ case EXACT_ENTRY:
+ /*
+ * We don't need to play around with the
+ * index for this search type.
+ */
+ break;
+ default:
+ return_code = SNMP_ERR_GENERR;
+ goto the_end;
+ }
+
+ /*
+ * Now search for the entry...
+ */
+ for (i = 0, j = 1; i < HASH_TBL_SIZE && found == _B_FALSE; i++) {
+ if (maAdvConfigHash.buckets[i]) {
+ /*
+ * Lock the bucket
+ */
+ (void) rw_rdlock(&maAdvConfigHash.bucketLock[i]);
+
+ hashEntry = maAdvConfigHash.buckets[i];
+ while (hashEntry != NULL) {
+ if (j == index->value[0]) {
+ maAdvConfigEntry = hashEntry->data;
+ found = _B_TRUE;
+
+ /*
+ * Lock the node
+ */
+ (void) rw_rdlock(&maAdvConfigEntry->
+ maIfaceNodeLock);
+
+ break;
+ }
+ hashEntry = hashEntry->next;
+ j++;
+ }
+
+ /*
+ * Unlock the bucket
+ */
+ (void) rw_unlock(&maAdvConfigHash.bucketLock[i]);
+ }
+ }
+
+ if (maAdvConfigEntry == NULL) {
+ return_code = END_OF_TABLE;
+ goto the_end;
+ }
+
+ /*
+ * And now we return the Maximum Registration Lifetime.
+ */
+ integer = &((*maAdvConfigEntry_data)->maAdvMaxRegLifetime);
+ *integer = (int)maAdvConfigEntry->maAdvMaxRegLifetime;
+
+ /*
+ * And now we return the Prefix Length Inclusion.
+ */
+ integer = &((*maAdvConfigEntry_data)->maAdvPrefixLengthInclusion);
+ if (maAdvConfigEntry->maAdvPrefixLenInclusion) {
+ *integer = 1;
+ } else {
+ /* I need to validate this one. Should this be 2 or 0 ? */
+ *integer = 2;
+ }
+
+ /*
+ * Get the Advertised Address.
+ */
+ (void) ntoa(maAdvConfigEntry->maAdvAddr, buffer);
+ len = strlen(buffer);
+ string = &((*maAdvConfigEntry_data)->maAdvAddress);
+ string->chars = (unsigned char *)calloc(1, len);
+ if (string == NULL) {
+ return_code = SNMP_ERR_GENERR;
+ goto the_end;
+ }
+ (void) memcpy(string->chars, buffer, len);
+ string->len = len;
+
+ /*
+ * And now we return the Maximum Advertisement Interval.
+ */
+ integer = &((*maAdvConfigEntry_data)->maAdvMaxInterval);
+ *integer = (int)maAdvConfigEntry->maAdvMaxInterval;
+
+ /*
+ * And now we return the Minimum Advertisement Interval.
+ */
+ integer = &((*maAdvConfigEntry_data)->maAdvMinInterval);
+ *integer = (int)maAdvConfigEntry->maAdvMinInterval;
+
+#if 0
+ /*
+ * And now we return whether we only advertise if we receive
+ * a soliciation.
+ */
+ integer = &((*maAdvConfigEntry_data)->maAdvResponseSolicitationOnly);
+
+ if (maAdvConfigEntry->maAdvResponseSolicitationOnly) {
+ *integer = 1;
+ } else {
+ /* I need to validate this one. Should this be 2 or 0 ? */
+ *integer = 2;
+ }
+#endif
+
+ /*
+ * And now we return whether the entry is active. In our case,
+ * all entries are active, so we can hardcode the value.
+ */
+ integer = &((*maAdvConfigEntry_data)->maAdvStatus);
+ *integer = 1;
+
+the_end:
+ if (maAdvConfigEntry != NULL) {
+ /*
+ * Unlock the node
+ */
+ (void) rw_unlock(&maAdvConfigEntry->maIfaceNodeLock);
+ }
+
+ if (return_code != SNMP_ERR_NOERROR) {
+ free_maAdvConfigEntry(*maAdvConfigEntry_data);
+ *maAdvConfigEntry_data = NULL;
+ }
+
+ (void) fprintf(stderr, "get_maAdvConfigEntry: returning %d (%p)\n",
+ return_code, (void *)*maAdvConfigEntry_data);
+
+ return (return_code);
+}
+
+
+/*
+ * Function: free_maAdvConfigEntry
+ *
+ * Arguments: maAdvConfigEntry - Pointer to a previously
+ * allocated SNMP interface entry
+ *
+ * Description: This function is called to free a previously
+ * allocated SNMP interface entry.
+ *
+ * Returns:
+ */
+void
+free_maAdvConfigEntry(MaAdvConfigEntry_t *maAdvConfigEntry)
+{
+ String *string;
+
+ if (maAdvConfigEntry) {
+ /*
+ * The template generates code that checks both
+ * the pointer, and the length. I am not sure
+ * if this is excessive, so I will leave it as is.
+ */
+ string = &(maAdvConfigEntry->maAdvAddress);
+ if (string->chars != NULL && string->len != 0) {
+ free(string->chars);
+ string->len = 0;
+ }
+
+ free(maAdvConfigEntry);
+ maAdvConfigEntry = NULL;
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mipagent/snmp_mipSecAssocEntry.c b/usr/src/cmd/cmd-inet/usr.lib/mipagent/snmp_mipSecAssocEntry.c
new file mode 100644
index 0000000000..47419f18bd
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mipagent/snmp_mipSecAssocEntry.c
@@ -0,0 +1,226 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * file: mipagentsnmp_mipSecAssocEntry.c
+ *
+ * This file contains the SNMP routines used to retrieve
+ * the Mobility Agent's Security Association Information.
+ */
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+
+#include <impl.h>
+#include <snmp.h>
+
+#include "snmp_stub.h"
+#include "agent.h"
+
+/*
+ * This table has one entry for each mobile node for which a mobility
+ * agent offers Home Agent services.
+ */
+extern HashTable mipSecAssocHash;
+
+/*
+ * Function: get_mipSecAssocEntry
+ *
+ * Arguments: search_type - The type of search (first, next, exact)
+ * mipSecAssocEntry_data - Pointer to a pointer which
+ * will contain the Security Association entry
+ * upon successful completion.
+ * index - Pointer to the current index
+ *
+ * Description: This function will allocate the memory required for
+ * the Security Association entry, and will find the
+ * appropriate Security Association based on the index
+ * provided. If the search type is set to FIRST_ENTRY,
+ * we will return the first Security Association in the
+ * Hash Table, otherwise the index is used.
+ *
+ * The Security Association entry is then setup with the
+ * values found in the entry from the hash table and
+ * returned to the caller.
+ *
+ * Note, the caller is responsible for either freeing the
+ * memory, or calling free_mipSecAssocEntry()
+ *
+ * Returns: int, 0 if successful
+ */
+extern int
+get_mipSecAssocEntry(int search_type,
+ MipSecAssocEntry_t **mipSecAssocEntry_data, IndexType *index)
+{
+ Integer *integer;
+ String *string;
+ MipSecAssocEntry *mipSecAssocEntry = NULL;
+ HashEntry *hashEntry;
+ int i;
+ int j;
+ boolean_t found = _B_FALSE;
+
+ /*
+ * Allocate some memory to handle the request.
+ */
+ *mipSecAssocEntry_data =
+ (MipSecAssocEntry_t *)calloc(1, sizeof (MipSecAssocEntry_t));
+
+ if (mipSecAssocEntry_data == NULL) {
+ return (SNMP_ERR_GENERR);
+ }
+
+ /*
+ * In the case, the search_type is FIRST_ENTRY or NEXT_ENTRY
+ * this function should modify the index argument to the
+ * appropriate value
+ */
+ switch (search_type) {
+ case FIRST_ENTRY:
+ /*
+ * We are looking for the first entry in the list.
+ */
+ index->value[0] = 1;
+ index->len = 1;
+ break;
+ case NEXT_ENTRY:
+ /*
+ * Increment the index value.
+ */
+ index->value[0]++;
+ break;
+ case EXACT_ENTRY:
+ /*
+ * We don't need to play around with the
+ * index for this search type.
+ */
+ break;
+ default:
+ free_mipSecAssocEntry(*mipSecAssocEntry_data);
+ return (SNMP_ERR_GENERR);
+ }
+
+ /*
+ * Now search for the entry... the problem here is that
+ * the Security Association Information spans three different
+ * tables, so we need to look through each one.
+ */
+ for (i = 0, j = 1; i < HASH_TBL_SIZE && found == _B_FALSE; i++) {
+ if (mipSecAssocHash.buckets[i]) {
+ /*
+ * Lock the bucket
+ */
+ (void) rw_rdlock(&mipSecAssocHash.bucketLock[i]);
+
+ hashEntry = mipSecAssocHash.buckets[i];
+ while (hashEntry != NULL) {
+ if (j == index->value[0]) {
+ mipSecAssocEntry = hashEntry->data;
+ found = _B_TRUE;
+
+ /*
+ * Lock the node
+ */
+ (void) rw_rdlock(&mipSecAssocEntry->
+ mipSecNodeLock);
+
+ break;
+ }
+ hashEntry = hashEntry->next;
+ j++;
+ }
+
+ /*
+ * Unlock the bucket
+ */
+ (void) rw_unlock(&mipSecAssocHash.bucketLock[i]);
+ }
+ }
+
+ if (mipSecAssocEntry == NULL) {
+ free_mipSecAssocEntry(*mipSecAssocEntry_data);
+ *mipSecAssocEntry_data = NULL;
+ return (END_OF_TABLE);
+ }
+
+ /*
+ * And now we return the Algorithm type.
+ */
+ integer = &((*mipSecAssocEntry_data)->mipSecAlgorithmType);
+ *integer = (int)mipSecAssocEntry->mipSecAlgorithmType;
+
+ /*
+ * And now we return the Algorithm mode.
+ */
+ integer = &((*mipSecAssocEntry_data)->mipSecAlgorithmMode);
+ *integer = (int)mipSecAssocEntry->mipSecAlgorithmMode;
+
+ /*
+ * And now we return the Key. Note that RFC2006 clearly states
+ * that this will always return 0 (then why does it exist in
+ * the MIB?!?).
+ */
+ string = &((*mipSecAssocEntry_data)->mipSecKey);
+ string->len = 0;
+
+ /*
+ * And now we return the Replay Method.
+ */
+ integer = &((*mipSecAssocEntry_data)->mipSecReplayMethod);
+ *integer = (int)mipSecAssocEntry->mipSecReplayMethod;
+
+ /*
+ * Unlock the node
+ */
+ (void) rw_unlock(&mipSecAssocEntry->mipSecNodeLock);
+
+ return (SNMP_ERR_NOERROR);
+}
+
+
+/*
+ * Function: free_mipSecAssocEntry
+ *
+ * Arguments: mipSecAssocEntry - Pointer to a previously
+ * allocated SNMP Security Association
+ * entry
+ *
+ * Description: This function is called to free a previously
+ * allocated SNMP Security Association entry.
+ *
+ * Returns:
+ */
+void
+free_mipSecAssocEntry(MipSecAssocEntry_t *mipSecAssocEntry)
+{
+ if (mipSecAssocEntry) {
+ free(mipSecAssocEntry);
+ mipSecAssocEntry = NULL;
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mipagent/snmp_mipSecViolationEntry.c b/usr/src/cmd/cmd-inet/usr.lib/mipagent/snmp_mipSecViolationEntry.c
new file mode 100644
index 0000000000..939ef97610
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mipagent/snmp_mipSecViolationEntry.c
@@ -0,0 +1,277 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1999-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * file: mipagentsnmp_mipSecViolationEntry.c
+ *
+ * This file contains the SNMP routines used to retrieve
+ * the Mobility Agent's Security Violation Information.
+ */
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <impl.h>
+#include <snmp.h>
+
+#include "snmp_stub.h"
+#include "agent.h"
+
+extern char *ntoa(uint32_t, char *);
+
+/*
+ * This table stores all of the Security Violations
+ */
+HashTable mipSecViolationHash;
+
+/*
+ * Function: get_mipSecViolationEntry
+ *
+ * Arguments: search_type - The type of search (first, next, exact)
+ * mipSecViolationEntry_data - Pointer to a pointer which
+ * will contain the Security Violation entry
+ * upon successful completion.
+ * index - Pointer to the current index
+ *
+ * Description: This function will allocate the memory required for
+ * the Security Violation entry, and will find the
+ * appropriate Security Violation based on the index
+ * provided. If the search type is set to FIRST_ENTRY,
+ * we will return the first Security Assocication in the
+ * Hash Table, otherwise the index is used.
+ *
+ * The Security Violation entry is then setup with the
+ * values found in the entry from the hash table and
+ * returned to the caller.
+ *
+ * Note, the caller is responsible for either freeing the
+ * memory, or calling free_mipSecViolationEntry()
+ *
+ * Returns: int, 0 if successful
+ */
+extern int
+get_mipSecViolationEntry(int search_type,
+ MipSecViolationEntry_t **mipSecViolationEntry_data, IndexType *index)
+{
+ Integer *integer;
+ String *string;
+ MipSecViolationEntry *mipSecViolationEntry = NULL;
+ HashEntry *hashEntry;
+ int i;
+ int j;
+ int return_code = SNMP_ERR_NOERROR;
+ char buffer[258];
+ boolean_t found = _B_FALSE;
+
+ /*
+ * Allocate some memory to handle the request.
+ */
+ *mipSecViolationEntry_data = (MipSecViolationEntry_t *)calloc(1,
+ sizeof (MipSecViolationEntry_t));
+
+ if (mipSecViolationEntry_data == NULL) {
+ return (SNMP_ERR_GENERR);
+ }
+
+ /*
+ * In the case, the search_type is FIRST_ENTRY or NEXT_ENTRY
+ * this function should modify the index argument to the
+ * appropriate value
+ */
+ switch (search_type) {
+ case FIRST_ENTRY:
+ /*
+ * We are looking for the first entry in the list.
+ */
+ index->value[0] = 1;
+ index->len = 1;
+ break;
+ case NEXT_ENTRY:
+ /*
+ * Increment the index value.
+ */
+ index->value[0]++;
+ break;
+ case EXACT_ENTRY:
+ /*
+ * We don't need to play around with the
+ * index for this search type.
+ */
+ break;
+ default:
+ return_code = SNMP_ERR_GENERR;
+ goto the_end;
+ }
+
+ /*
+ * Now search for the entry... the problem here is that
+ * the Security Association Information spans three different
+ * tables, so we need to look through each one.
+ */
+ for (i = 0, j = 1; i < HASH_TBL_SIZE && found == _B_FALSE; i++) {
+ if (mipSecViolationHash.buckets[i]) {
+ /*
+ * Lock the bucket
+ */
+ (void) rw_rdlock(&mipSecViolationHash.bucketLock[i]);
+
+ hashEntry = mipSecViolationHash.buckets[i];
+ while (hashEntry != NULL) {
+ if (j == index->value[0]) {
+ mipSecViolationEntry = hashEntry->data;
+ found = _B_TRUE;
+
+ /*
+ * Lock the node
+ */
+ (void) rw_rdlock(
+ &mipSecViolationEntry->
+ mipSecNodeLock);
+
+ break;
+ }
+ hashEntry = hashEntry->next;
+ j++;
+ }
+
+ /*
+ * Unlock the bucket
+ */
+ (void) rw_unlock(&mipSecViolationHash.bucketLock[i]);
+ }
+ }
+
+ if (mipSecViolationEntry == NULL) {
+ return_code = END_OF_TABLE;
+ goto the_end;
+ }
+
+ /*
+ * Return the address of the offender.
+ */
+ (void) ntoa(mipSecViolationEntry->mipSecViolatorAddr, buffer);
+
+ string = &((*mipSecViolationEntry_data)->mipSecViolatorAddress);
+
+ string->chars = (unsigned char *)malloc(strlen(buffer) + 1);
+ if (string->chars == NULL) {
+ return_code = SNMP_ERR_GENERR;
+ goto the_end;
+ }
+ (void) strcpy((char *)string->chars, buffer);
+ string->len = strlen(buffer);
+
+ /*
+ * And now we return the number of security violations
+ * for this entry.
+ */
+ integer = &((*mipSecViolationEntry_data)->mipSecViolationCounter);
+ *integer = (int)mipSecViolationEntry->mipSecViolationCounter;
+
+ /*
+ * And now we return the SPI used.
+ */
+ integer = &((*mipSecViolationEntry_data)->mipSecRecentViolationSPI);
+ *integer = (int)mipSecViolationEntry->mipSecRecentViolationSPI;
+
+ /*
+ * And now we return time of the last violation.
+ */
+ integer =
+ &((*mipSecViolationEntry_data)->mipSecRecentViolationTime);
+ *integer = (int)mipSecViolationEntry->mipSecRecentViolationTime;
+
+ /*
+ * And now we return the low order bits of the Identifier used.
+ */
+ integer =
+ &((*mipSecViolationEntry_data)->mipSecRecentViolationIDLow);
+ *integer = (int)mipSecViolationEntry->mipSecRecentViolationIDLow;
+
+ /*
+ * And now we return the high order bits of the Identifier used.
+ */
+ integer =
+ &((*mipSecViolationEntry_data)->mipSecRecentViolationIDHigh);
+ *integer = (int)mipSecViolationEntry->mipSecRecentViolationIDHigh;
+
+ /*
+ * And finally, the reason for the violation.
+ */
+ integer =
+ &((*mipSecViolationEntry_data)->mipSecRecentViolationReason);
+ *integer = (int)mipSecViolationEntry->mipSecRecentViolationReason;
+
+the_end:
+ if (mipSecViolationEntry != NULL) {
+ /*
+ * Unlock the node
+ */
+ (void) rw_unlock(&mipSecViolationEntry->mipSecNodeLock);
+ }
+
+ if (return_code != SNMP_ERR_NOERROR) {
+ free_mipSecViolationEntry(*mipSecViolationEntry_data);
+ *mipSecViolationEntry_data = NULL;
+ }
+
+ return (return_code);
+}
+
+
+/*
+ * Function: free_mipSecViolationEntry
+ *
+ * Arguments: mipSecViolationEntry - Pointer to a previously
+ * allocated SNMP Security Violation
+ * entry
+ *
+ * Description: This function is called to free a previously
+ * allocated SNMP Security Violation entry.
+ *
+ * Returns:
+ */
+void
+free_mipSecViolationEntry(MipSecViolationEntry_t *mipSecViolationEntry)
+{
+ String *string;
+
+ if (mipSecViolationEntry) {
+ string =
+ &(mipSecViolationEntry->mipSecViolatorAddress);
+ if (string->chars != NULL && string->len != 0) {
+ free(string->chars);
+ string->len = 0;
+ }
+
+ free(mipSecViolationEntry);
+ mipSecViolationEntry = NULL;
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mipagent/snmp_stub.c b/usr/src/cmd/cmd-inet/usr.lib/mipagent/snmp_stub.c
new file mode 100644
index 0000000000..14fef4d2e2
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mipagent/snmp_stub.c
@@ -0,0 +1,1083 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1999-2002 Sun Microsystems, Inc.
+ * All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * file: snmp_stub.c
+ *
+ * This file contains the SNMP routines used to retrieve
+ * the Mobility Agent's various counter and configurable
+ * information.
+ */
+
+#include <sys/types.h>
+#include <netinet/in.h>
+
+#include <impl.h>
+#include <snmp.h>
+
+#include "agent.h"
+
+/* Counters common to all Mobility Agents */
+extern CommonCounters commonCounters;
+
+/* Counters maintained by Foreign Agents */
+extern ForeignAgentCounters faCounters;
+
+/* Counters maintained by Home Agents */
+extern HomeAgentCounters haCounters;
+
+
+#define MIP_MOBILE_NODE 0x1
+#define MIP_FOREIGN_AGENT 0x2
+#define MIP_HOME_AGENT 0x4
+
+#define MIP_ENABLE 1
+#define MIP_DISABLE 2
+
+#define MIP_ENCAP_IP_IN_IP 0x1
+#define MIP_ENCAP_GRE 0x2
+#define MIP_ENCAP_MIN_ENCAP 0x4
+#define MIP_OTHER 0x8
+
+/*
+ * Function: get_mipEntities
+ *
+ * Arguments: mipEntities - Pointer to Integer
+ *
+ * Description: Returns whether the agent is running
+ * as a Foreign and/or Home agent.
+ *
+ * Returns: int, 0 if successful
+ */
+int
+get_mipEntities(Integer *mipEntities)
+{
+ /*
+ * We can only be both Mobility Agents
+ */
+ *mipEntities = (MIP_FOREIGN_AGENT | MIP_HOME_AGENT);
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * Function: get_mipEnable
+ *
+ * Arguments: mipEnable - Pointer to Integer
+ *
+ * Description: This function is called to determine if
+ * if the Mobile-IP Agent is in the
+ * active mode.
+ *
+ * Returns: int, 0 if successful
+ */
+int
+get_mipEnable(Integer *mipEnable)
+{
+ /*
+ * If we get this far, then we are enabled :)
+ */
+ *mipEnable = MIP_ENABLE;
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * Function: set_mipEnable
+ *
+ * Arguments: pass - The SNMP Pass
+ * mipEnable - Pointer to an Integer
+ *
+ * Description: This function is called to start the
+ * SNMP Mobile-IP Agent. We do not currently
+ * support this option.
+ *
+ * Returns: int, 0 if successful
+ */
+int
+set_mipEnable(int pass, Integer *mipEnable)
+{
+ switch (pass) {
+ case FIRST_PASS:
+ /*
+ * Check whether the argument provided was valid to begin with
+ */
+ if (*mipEnable != MIP_ENABLE && *mipEnable != MIP_DISABLE) {
+ return (SNMP_ERR_GENERR);
+ }
+ return (SNMP_ERR_NOERROR);
+
+ case SECOND_PASS:
+ /*
+ * Here is where we allow the value to be changed. We do
+ * not allow this at this time.
+ */
+ return (SNMP_ERR_READONLY);
+ }
+
+ return (SNMP_ERR_GENERR);
+}
+
+/*
+ * Function: get_mipEncapsulationSupported
+ *
+ * Arguments: mipEncapsulationSupported - Pointer to Integer
+ *
+ * Description: This function is called to retrieve the
+ * encapsulation types supported by the
+ * agent.
+ *
+ * Returns: int, 0 if successful
+ */
+int
+get_mipEncapsulationSupported(Integer *mipEncapsulationSupported)
+{
+ /*
+ * We currently only support IP in IP
+ */
+ *mipEncapsulationSupported = MIP_ENCAP_IP_IN_IP;
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * Function: get_mipSecTotalViolations
+ *
+ * Arguments: mipSecTotalViolations - Pointer to Integer
+ *
+ * Description: This function is called to retrieve the
+ * number of security violations since start-up.
+ *
+ * Returns: int, 0 if successful
+ */
+int
+get_mipSecTotalViolations(Integer *mipSecTotalViolations)
+{
+ /*
+ * TODO: Return the total number of un-authenticated requests
+ */
+ *mipSecTotalViolations = haCounters.haMNAuthFailureCnt +
+ haCounters.haFAAuthFailureCnt + faCounters.faMNAuthFailureCnt +
+ faCounters.faHAAuthFailureCnt;
+
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * Function: get_maAdvertisementsSent
+ *
+ * Arguments: maAdvertisementsSent - Pointer to Integer
+ *
+ * Description: This function is called to retrieve the
+ * number of advertisements transmitted.
+ *
+ * Returns: int, 0 if successful
+ */
+int
+get_maAdvertisementsSent(Integer *maAdvertisementsSent)
+{
+ /*
+ * Return the total number of advertisements sent
+ */
+ *maAdvertisementsSent = commonCounters.maAdvSentCnt;
+
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * Function: get_maAdvsSentForSolicitation
+ *
+ * Arguments: maAdvsSentForSolicitation - Pointer to Integer
+ *
+ * Description: This function is called to retrieve the
+ * number of advertisements transmitted as a
+ * result of a solicitation received.
+ *
+ * Returns: int, 0 if successful
+ */
+int
+get_maAdvsSentForSolicitation(Integer *maAdvsSentForSolicitation)
+{
+ /*
+ * Return the total number of advertisements we've sent
+ * due to a solicitation.
+ */
+ *maAdvsSentForSolicitation =
+ commonCounters.maAdvSentForSolicitationsCnt;
+
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * Function: get_maSolicitationsReceived
+ *
+ * Arguments: maSolicitationsReceived - Pointer to Integer
+ *
+ * Description: This function is called to retrieve the
+ * number of solicitations received.
+ *
+ * Returns: int, 0 if successful
+ */
+int
+get_maSolicitationsReceived(Integer *maSolicitationsReceived)
+{
+ /*
+ * Return the total number of solicitations received.
+ */
+ *maSolicitationsReceived = commonCounters.maSolicitationsRecvdCnt;
+
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * Function: get_faIsBusy
+ *
+ * Arguments: faIsBusy - Pointer to Integer
+ *
+ * Description: This function is called to retrieve the
+ * number of times we've advertised the fact
+ * that we were busy.
+ *
+ * Returns: int, 0 if successful
+ */
+int
+get_faIsBusy(Integer *faIsBusy)
+{
+ /*
+ * Return the number of times that the foreign agent has
+ * responded as being too busy.
+ */
+ *faIsBusy = faCounters.faIsBusyCnt;
+
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * Function: get_faRegistrationRequired
+ *
+ * Arguments: faRegistrationRequired - Pointer to Integer
+ *
+ * Description: This function is called to determine whether
+ * we require registrations to provide service.
+ *
+ * Returns: int, 0 if successful
+ */
+int
+get_faRegistrationRequired(Integer *faRegistrationRequired)
+{
+ /*
+ * States whether the Foreign Agent REQUIRES that
+ * all Mobile Nodes register with it. We currently
+ * have no way of enforcing this (short of setting
+ * up a filter on the Foreign Agent).
+ */
+ *faRegistrationRequired = _B_FALSE;
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * Function: set_faRegistrationRequired
+ *
+ * Arguments: pass - The SNMP Pass
+ * faRegistrationRequired - Pointer to an Integer
+ *
+ * Description: This function is called to require the agent
+ * to receive a registration in order to provide
+ * service. We do not currently allow this to
+ * be set via SNMP.
+ *
+ * Returns: int, 0 if successful
+ */
+int
+set_faRegistrationRequired(int pass, Integer *faRegistrationRequired)
+{
+ switch (pass) {
+ case FIRST_PASS:
+ /*
+ * Check whether a valid argument was passed.
+ */
+ if (*faRegistrationRequired == _B_TRUE ||
+ *faRegistrationRequired == _B_FALSE) {
+ return (SNMP_ERR_NOERROR);
+ }
+ return (SNMP_ERR_GENERR);
+
+ case SECOND_PASS:
+ /*
+ * Sorry, we do not allow this at this time.
+ */
+ return (SNMP_ERR_READONLY);
+ }
+
+ return (SNMP_ERR_GENERR);
+}
+
+/*
+ * Function: get_faRegRequestsReceived
+ *
+ * Arguments: faRegRequestsReceived - Pointer to Integer
+ *
+ * Description: This function is called to return the
+ * number of Registration Requests we've received.
+ *
+ * Returns: int, 0 if successful
+ */
+int
+get_faRegRequestsReceived(Integer *faRegRequestsReceived)
+{
+ /*
+ * Return the total number of Registration Requests we've
+ * received.
+ */
+ *faRegRequestsReceived = faCounters.faRegReqRecvdCnt;
+
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * Function: get_faRegRequestsRelayed
+ *
+ * Arguments: faRegRequestsRelayed - Pointer to Integer
+ *
+ * Description: This function is called to return the
+ * number of Registration Requests we've
+ * relayed to a Home Agent.
+ *
+ * Returns: int, 0 if successful
+ */
+int
+get_faRegRequestsRelayed(Integer *faRegRequestsRelayed)
+{
+ /*
+ * Return the total number of Registration Requests we've
+ * replayed.
+ */
+ *faRegRequestsRelayed = faCounters.faRegReqRelayedCnt;
+
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * Function: get_faReasonUnspecified
+ *
+ * Arguments: faReasonUnspecified - Pointer to Integer
+ *
+ * Description: This function is called to return the
+ * number of Registration Requests we've
+ * failed with the error set to reason
+ * unspecified.
+ *
+ * Returns: int, 0 if successful
+ */
+int
+get_faReasonUnspecified(Integer *faReasonUnspecified)
+{
+ /*
+ * Return the total number of Registration Requests we've
+ * rejected for unspecified reasons (error 64).
+ */
+ *faReasonUnspecified = faCounters.faReasonUnspecifiedCnt;
+
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * Function: get_faAdmProhibited
+ *
+ * Arguments: faAdmProhibited - Pointer to Integer
+ *
+ * Description: This function is called to return the
+ * number of Registration Requests we've
+ * failed with the error set to administratively
+ * prohibited.
+ *
+ * Returns: int, 0 if successful
+ */
+int
+get_faAdmProhibited(Integer *faAdmProhibited)
+{
+ /*
+ * Return the total number of Registration Requests we've
+ * rejected for administrative purposes (error 65).
+ */
+ *faAdmProhibited = faCounters.faAdmProhibitedCnt;
+
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * Function: get_faInsufficientResource
+ *
+ * Arguments: faInsufficientResource - Pointer to Integer
+ *
+ * Description: This function is called to return the
+ * number of Registration Requests we've
+ * failed with the error set to insufficient
+ * resources.
+ *
+ * Returns: int, 0 if successful
+ */
+int
+get_faInsufficientResource(Integer *faInsufficientResource)
+{
+ /*
+ * Return the total number of Registration Requests we've
+ * rejected due to insufficient resources (error 66).
+ */
+ *faInsufficientResource = faCounters.faInsufficientResourceCnt;
+
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * Function: get_faMNAuthenticationFailure
+ *
+ * Arguments: faMNAuthenticationFailure - Pointer to Integer
+ *
+ * Description: This function is called to return the
+ * number of Registration Requests we've
+ * failed with the error set to MN-FA
+ * Authentication Failure.
+ *
+ * Returns: int, 0 if successful
+ */
+int
+get_faMNAuthenticationFailure(Integer *faMNAuthenticationFailure)
+{
+ /*
+ * Return the number of Mobile Node authentication
+ * failures.
+ */
+ *faMNAuthenticationFailure = faCounters.faMNAuthFailureCnt;
+
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * Function: get_faRegLifetimeTooLong
+ *
+ * Arguments: faRegLifetimeTooLong - Pointer to Integer
+ *
+ * Description: This function is called to return the
+ * number of Registration Requests we've
+ * failed with the error set to lifetime
+ * too long.
+ *
+ * Returns: int, 0 if successful
+ */
+int
+get_faRegLifetimeTooLong(Integer *faRegLifetimeTooLong)
+{
+ /*
+ * Return the number of failed Registration Requests
+ * due to a lifetime too long.
+ */
+ *faRegLifetimeTooLong = faCounters.faRegLifetimeTooLongCnt;
+
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * Function: get_faPoorlyFormedRequests
+ *
+ * Arguments: faPoorlyFormedRequests - Pointer to Integer
+ *
+ * Description: This function is called to return the
+ * number of Registration Requests we've
+ * failed with the error set to poorly
+ * formed request.
+ *
+ * Returns: int, 0 if successful
+ */
+int
+get_faPoorlyFormedRequests(Integer *faPoorlyFormedRequests)
+{
+ /*
+ * Returns the number of poorly formed requests we've
+ * received.
+ */
+ *faPoorlyFormedRequests = faCounters.faPoorlyFormedRequestsCnt;
+
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * Function: get_faEncapsulationUnavailable
+ *
+ * Arguments: faEncapsulationUnavailable - Pointer to Integer
+ *
+ * Description: This function is called to return the
+ * number of Registration Requests we've
+ * failed with the error set to encapsulation
+ * unavailable.
+ *
+ * Returns: int, 0 if successful
+ */
+int
+get_faEncapsulationUnavailable(Integer *faEncapsulationUnavailable)
+{
+ /*
+ * Returns the number of times we've rejected a request
+ * due to an unsupported encapsulation.
+ */
+ *faEncapsulationUnavailable = faCounters.faEncapUnavailableCnt;
+
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * Function: get_faVJCompressionUnavailable
+ *
+ * Arguments: faVJCompressionUnavailable - Pointer to Integer
+ *
+ * Description: This function is called to return the
+ * number of Registration Requests we've
+ * failed with the error set to VJ compression
+ * unavailable.
+ *
+ * Returns: int, 0 if successful
+ */
+int
+get_faVJCompressionUnavailable(Integer *faVJCompressionUnavailable)
+{
+ /*
+ * Returns the number of times we've returned an error
+ * stating that compression was unavailable.
+ */
+ *faVJCompressionUnavailable = faCounters.faVJCompUnavailableCnt;
+
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * Function: get_faHAUnreachable
+ *
+ * Arguments: faHAUnreachable - Pointer to Integer
+ *
+ * Description: This function is called to return the
+ * number of Registration Requests we've
+ * failed with the error set to HA
+ * unreachable.
+ *
+ * Returns: int, 0 if successful
+ */
+int
+get_faHAUnreachable(Integer *faHAUnreachable)
+{
+ /*
+ * Returns the number of times that we've replied to a
+ * mobile node stating that the home agent was unreachable.
+ */
+ *faHAUnreachable = faCounters.faHAUnreachableCnt;
+
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * Function: get_faRegRepliesRecieved
+ *
+ * Arguments: faRegRepliesRecieved - Pointer to Integer
+ *
+ * Description: This function is called to return the
+ * number of Registration Replies we've
+ * received.
+ *
+ * Returns: int, 0 if successful
+ */
+int
+get_faRegRepliesRecieved(Integer *faRegRepliesRecieved)
+{
+ /*
+ * Returns the number of Registration Replies we've received.
+ */
+ *faRegRepliesRecieved = faCounters.faRegRepliesRecvdCnt;
+
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * Function: get_faRegRepliesRelayed
+ *
+ * Arguments: faRegRepliesRelayed - Pointer to Integer
+ *
+ * Description: This function is called to return the
+ * number of Registration Replies we've
+ * relayed.
+ *
+ * Returns: int, 0 if successful
+ */
+int
+get_faRegRepliesRelayed(Integer *faRegRepliesRelayed)
+{
+
+ /*
+ * Returns the number of Registration Replies we've relayed.
+ */
+ *faRegRepliesRelayed = faCounters.faRegRepliesRelayedCnt;
+
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * Function: get_faHAAuthenticationFailure
+ *
+ * Arguments: faHAAuthenticationFailure - Pointer to Integer
+ *
+ * Description: This function is called to return the
+ * number of Registration Replies we've
+ * relayed with the error FA-HA Auth Failure.
+ *
+ * Returns: int, 0 if successful
+ */
+int
+get_faHAAuthenticationFailure(Integer *faHAAuthenticationFailure)
+{
+ /*
+ * Returns the number of times that we've encountered an
+ * FA-HA Authentication failure.
+ */
+ *faHAAuthenticationFailure = faCounters.faHAAuthFailureCnt;
+
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * Function: get_faPoorlyFormedReplies
+ *
+ * Arguments: faPoorlyFormedReplies - Pointer to Integer
+ *
+ * Description: This function is called to return the
+ * number of Registration Replies we've
+ * relayed with the error poorly formed reply.
+ *
+ * Returns: int, 0 if successful
+ */
+int
+get_faPoorlyFormedReplies(Integer *faPoorlyFormedReplies)
+{
+ /*
+ * Return the number of times that we've received a poorly
+ * formed reply.
+ */
+ *faPoorlyFormedReplies = faCounters.faPoorlyFormedRepliesCnt;
+
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * Function: get_haRegistrationAccepted
+ *
+ * Arguments: haRegistrationAccepted - Pointer to Integer
+ *
+ * Description: This function is called to return the
+ * number of Registration Requests we've
+ * accepted.
+ *
+ * Returns: int, 0 if successful
+ */
+int
+get_haRegistrationAccepted(Integer *haRegistrationAccepted)
+{
+ /*
+ * Return the number of Registrations that we've accepted.
+ */
+ *haRegistrationAccepted = haCounters.haRegAccepted0Cnt;
+
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * Function: get_haMultiBindingUnsupported
+ *
+ * Arguments: haMultiBindingUnsupported - Pointer to Integer
+ *
+ * Description: This function is called to return the
+ * number of Registration Replies we've sent
+ * with the error set to multiple bindings
+ * unsupported.
+ *
+ * Returns: int, 0 if successful
+ */
+int
+get_haMultiBindingUnsupported(Integer *haMultiBindingUnsupported)
+{
+ /*
+ * Ret urn the number of Registrations that we've denied with
+ * code 1.
+ */
+ *haMultiBindingUnsupported = haCounters.haRegAccepted1Cnt;
+
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * Function: get_haReasonUnspecified
+ *
+ * Arguments: haReasonUnspecified - Pointer to Integer
+ *
+ * Description: This function is called to return the
+ * number of Registration Replies we've sent
+ * with the error set to reason unspecified.
+ *
+ * Returns: int, 0 if successful
+ */
+int
+get_haReasonUnspecified(Integer *haReasonUnspecified)
+{
+ /*
+ * Return the number of Registrations that we've denied with
+ * code 128.
+ */
+ *haReasonUnspecified = haCounters.haReasonUnspecifiedCnt;
+
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * Function: get_haAdmProhibited
+ *
+ * Arguments: haAdmProhibited - Pointer to Integer
+ *
+ * Description: This function is called to return the
+ * number of Registration Replies we've sent
+ * with the error set to administratively
+ * prohibited.
+ *
+ * Returns: int, 0 if successful
+ */
+int
+get_haAdmProhibited(Integer *haAdmProhibited)
+{
+ /*
+ * Return the number of Registrations that we've denied with
+ * code 129.
+ */
+ *haAdmProhibited = haCounters.haAdmProhibitedCnt;
+
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * Function: get_haInsufficientResource
+ *
+ * Arguments: haInsufficientResource - Pointer to Integer
+ *
+ * Description: This function is called to return the
+ * number of Registration Replies we've sent
+ * with the error set to insufficient
+ * resources.
+ *
+ * Returns: int, 0 if successful
+ */
+int
+get_haInsufficientResource(Integer *haInsufficientResource)
+{
+ /*
+ * Return the number of Registrations that we've denied with
+ * code 130.
+ */
+ *haInsufficientResource = haCounters.haInsufficientResourceCnt;
+
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * Function: get_haMNAuthenticationFailure
+ *
+ * Arguments: haMNAuthenticationFailure - Pointer to Integer
+ *
+ * Description: This function is called to return the
+ * number of Registration Replies we've sent
+ * with the error set to MN-HA Authentication
+ * failed.
+ *
+ * Returns: int, 0 if successful
+ */
+int
+get_haMNAuthenticationFailure(Integer *haMNAuthenticationFailure)
+{
+
+ /*
+ * Return the number of Registrations that we've denied with
+ * code 131.
+ */
+ *haMNAuthenticationFailure = haCounters.haMNAuthFailureCnt;
+
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * Function: get_haFAAuthenticationFailure
+ *
+ * Arguments: haFAAuthenticationFailure - Pointer to Integer
+ *
+ * Description: This function is called to return the
+ * number of Registration Replies we've sent
+ * with the error set to FA-HA Authentication
+ * failed.
+ *
+ * Returns: int, 0 if successful
+ */
+int
+get_haFAAuthenticationFailure(Integer *haFAAuthenticationFailure)
+{
+ /*
+ * Return the number of Registrations that we've denied with
+ * code 132.
+ */
+ *haFAAuthenticationFailure = haCounters.haFAAuthFailureCnt;
+
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * Function: get_haIDMismatch
+ *
+ * Arguments: haIDMismatch - Pointer to Integer
+ *
+ * Description: This function is called to return the
+ * number of Registration Replies we've sent
+ * with the error set to ID Mismatch.
+ *
+ * Returns: int, 0 if successful
+ */
+int
+get_haIDMismatch(Integer *haIDMismatch)
+{
+ /*
+ * Return the number of Registrations that we've denied with
+ * code 133.
+ */
+ *haIDMismatch = haCounters.haIDMismatchCnt;
+
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * Function: get_haPoorlyFormedRequest
+ *
+ * Arguments: haPoorlyFormedRequest - Pointer to Integer
+ *
+ * Description: This function is called to return the
+ * number of Registration Replies we've sent
+ * with the error set to poorly formed
+ * request.
+ *
+ * Returns: int, 0 if successful
+ */
+int
+get_haPoorlyFormedRequest(Integer *haPoorlyFormedRequest)
+{
+ /*
+ * Return the number of Registrations that we've denied with
+ * code 134.
+ */
+ *haPoorlyFormedRequest = haCounters.haPoorlyFormedRequestsCnt;
+
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * Function: get_haTooManyBindings
+ *
+ * Arguments: haTooManyBindings - Pointer to Integer
+ *
+ * Description: This function is called to return the
+ * number of Registration Replies we've sent
+ * with the error set to too many bindings.
+ *
+ * Returns: int, 0 if successful
+ */
+int
+get_haTooManyBindings(Integer *haTooManyBindings)
+{
+ /*
+ * Return the number of Registrations that we've denied with
+ * code 135.
+ */
+ *haTooManyBindings = haCounters.haTooManyBindingsCnt;
+
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * Function: get_haUnknownHA
+ *
+ * Arguments: haUnknownHA - Pointer to Integer
+ *
+ * Description: This function is called to return the
+ * number of Registration Replies we've sent
+ * with the error set to unknown Home Agent.
+ *
+ * Returns: int, 0 if successful
+ */
+int
+get_haUnknownHA(Integer *haUnknownHA)
+{
+ /*
+ * Return the number of Registrations that we've denied with
+ * code 136.
+ */
+ *haUnknownHA = haCounters.haUnknownHACnt;
+
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * Function: get_haGratuitiousARPsSent
+ *
+ * Arguments: haGratuitiousARPsSent - Pointer to Integer
+ *
+ * Description: This function is called to return the
+ * number of gratuitious ARPs sent.
+ *
+ * Returns: int, 0 if successful
+ */
+int
+get_haGratuitiousARPsSent(Integer *haGratuitiousARPsSent)
+{
+ /*
+ * Return the total number of gratuitious ARPs sent
+ */
+ *haGratuitiousARPsSent = haCounters.haGratuitousARPsSentCnt;
+
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * Function: get_haProxyARPsSent
+ *
+ * Arguments: haProxyARPsSent - Pointer to Integer
+ *
+ * Description: This function is called to return the
+ * number of proxy ARPs sent.
+ *
+ * Returns: int, 0 if successful
+ */
+int
+get_haProxyARPsSent(Integer *haProxyARPsSent)
+{
+ /*
+ * Return the total number of proxy ARPs sent
+ */
+ *haProxyARPsSent = haCounters.haProxyARPsSentCnt;
+
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * Function: get_haRegRequestsReceived
+ *
+ * Arguments: haRegRequestsReceived - Pointer to Integer
+ *
+ * Description: This function is called to return the
+ * number of registration requests received.
+ *
+ * Returns: int, 0 if successful
+ */
+int
+get_haRegRequestsReceived(Integer *haRegRequestsReceived)
+{
+ /*
+ * Return the total number of Registration Requests received
+ */
+ *haRegRequestsReceived = haCounters.haRegReqRecvdCnt;
+
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * Function: get_haDeRegRequestsReceived
+ *
+ * Arguments: haDeRegRequestsReceived - Pointer to Integer
+ *
+ * Description: This function is called to return the
+ * number of deregistration requests received.
+ *
+ * Returns: int, 0 if successful
+ */
+int
+get_haDeRegRequestsReceived(Integer *haDeRegRequestsReceived)
+{
+ /*
+ * Return the total number of Deregistration Requests received
+ */
+ *haDeRegRequestsReceived = haCounters.haDeRegReqRecvdCnt;
+
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * Function: get_haRegRepliesSent
+ *
+ * Arguments: haRegRepliesSent - Pointer to Integer
+ *
+ * Description: This function is called to return the
+ * number of registration replies sent.
+ *
+ * Returns: int, 0 if successful
+ */
+int
+get_haRegRepliesSent(Integer *haRegRepliesSent)
+{
+ /*
+ * Return the total number of Registration Replies we've
+ * sent
+ */
+ *haRegRepliesSent = haCounters.haRegRepliesSentCnt;
+
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * Function: get_haDeRegRepliesSent
+ *
+ * Arguments: haDeRegRepliesSent - Pointer to Integer
+ *
+ * Description: This function is called to return the
+ * number of deregistration replies sent.
+ *
+ * Returns: int, 0 if successful
+ */
+int
+get_haDeRegRepliesSent(Integer *haDeRegRepliesSent)
+{
+ /*
+ * Return the total number of Registration Replies we've
+ * received
+ */
+ *haDeRegRepliesSent = haCounters.haDeRegRepliesSentCnt;
+
+ return (SNMP_ERR_NOERROR);
+}
+
+int
+get_i_dont_think_so(Integer *bogus)
+{
+ *bogus = 1;
+
+ return (SNMP_ERR_NOERROR);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mipagent/snmp_stub.h b/usr/src/cmd/cmd-inet/usr.lib/mipagent/snmp_stub.h
new file mode 100644
index 0000000000..211d5ec3e0
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mipagent/snmp_stub.h
@@ -0,0 +1,211 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _MIPAGENTSNMP_STUB_H_
+#define _MIPAGENTSNMP_STUB_H_
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct _MipSecAssocEntry_t {
+ Integer mipSecAlgorithmType;
+ Integer mipSecAlgorithmMode;
+ String mipSecKey;
+ Integer mipSecReplayMethod;
+} MipSecAssocEntry_t;
+
+typedef struct _MipSecViolationEntry_t {
+ String mipSecViolatorAddress;
+ Integer mipSecViolationCounter;
+ Integer mipSecRecentViolationSPI;
+ Integer mipSecRecentViolationTime;
+ Integer mipSecRecentViolationIDLow;
+ Integer mipSecRecentViolationIDHigh;
+ Integer mipSecRecentViolationReason;
+} MipSecViolationEntry_t;
+
+typedef struct _MaAdvConfigEntry_t {
+ Integer maAdvMaxRegLifetime;
+ Integer maAdvPrefixLengthInclusion;
+ String maAdvAddress;
+ Integer maAdvMaxInterval;
+ Integer maAdvMinInterval;
+ Integer maAdvMaxAdvLifetime;
+ Integer maAdvResponseSolicitationOnly;
+ Integer maAdvStatus;
+} MaAdvConfigEntry_t;
+
+typedef struct _FaCOAEntry_t {
+ Integer faCOAStatus;
+} FaCOAEntry_t;
+
+typedef struct _FaVisitorEntry_t {
+ String faVisitorIPAddress;
+ String faVisitorHomeAddress;
+ String faVisitorHomeAgentAddress;
+ Integer faVisitorTimeGranted;
+ Integer faVisitorTimeRemaining;
+ Integer faVisitorRegFlags;
+ Integer faVisitorRegIDLow;
+ Integer faVisitorRegIDHigh;
+ Integer faVisitorRegIsAccepted;
+ Integer faVisitorInIfindex;
+ String faVisitorSlla;
+} FaVisitorEntry_t;
+
+typedef struct _HaMobilityBindingEntry_t {
+ String haMobilityBindingMN;
+ String haMobilityBindingCOA;
+ String haMobilityBindingSourceAddress;
+ Integer haMobilityBindingRegFlags;
+ Integer haMobilityBindingRegIDLow;
+ Integer haMobilityBindingRegIDHigh;
+ Integer haMobilityBindingTimeGranted;
+ Integer haMobilityBindingTimeRemaining;
+} HaMobilityBindingEntry_t;
+
+typedef struct _HaCounterEntry_t {
+ Integer haServiceRequestsAccepted;
+ Integer haServiceRequestsDenied;
+ Integer haOverallServiceTime;
+ Integer haRecentServiceAcceptedTime;
+ Integer haRecentServiceDeniedTime;
+ Integer haRecentServiceDeniedCode;
+} HaCounterEntry_t;
+
+/*
+ * Prototype for functions in mipagentsnmp_stub.c
+ */
+extern int get_mipEntities(Integer *mipEntities);
+extern int get_mipEnable(Integer *mipEnable);
+extern int set_mipEnable(int pass, Integer* mipEnable);
+extern int get_mipEncapsulationSupported(Integer *mipEncapsulationSupported);
+
+extern int get_mipSecTotalViolations(Integer *mipSecTotalViolations);
+extern int get_maAdvertisementsSent(Integer *maAdvertisementsSent);
+extern int get_maAdvsSentForSolicitation(Integer *maAdvsSentForSolicitation);
+extern int get_maSolicitationsReceived(Integer *maSolicitationsReceived);
+extern int get_faIsBusy(Integer *faIsBusy);
+extern int get_faRegistrationRequired(Integer *faRegistrationRequired);
+extern int set_faRegistrationRequired(int pass,
+ Integer *faRegistrationRequired);
+extern int get_faRegRequestsReceived(Integer *faRegRequestsReceived);
+extern int get_faRegRequestsRelayed(Integer *faRegRequestsRelayed);
+extern int get_faReasonUnspecified(Integer *faReasonUnspecified);
+extern int get_faAdmProhibited(Integer *faAdmProhibited);
+extern int get_faInsufficientResource(Integer *faInsufficientResource);
+extern int get_faMNAuthenticationFailure(Integer *faMNAuthenticationFailure);
+extern int get_faRegLifetimeTooLong(Integer *faRegLifetimeTooLong);
+extern int get_faPoorlyFormedRequests(Integer *faPoorlyFormedRequests);
+extern int get_faEncapsulationUnavailable(Integer *faEncapsulationUnavailable);
+extern int get_faVJCompressionUnavailable(Integer *faVJCompressionUnavailable);
+extern int get_faHAUnreachable(Integer *faHAUnreachable);
+extern int get_faRegRepliesRecieved(Integer *faRegRepliesRecieved);
+extern int get_faRegRepliesRelayed(Integer *faRegRepliesRelayed);
+extern int get_faHAAuthenticationFailure(Integer *faHAAuthenticationFailure);
+extern int get_faPoorlyFormedReplies(Integer *faPoorlyFormedReplies);
+
+extern int get_haRegistrationAccepted(Integer *haRegistrationAccepted);
+extern int get_haMultiBindingUnsupported(Integer *haMultiBindingUnsupported);
+extern int get_haReasonUnspecified(Integer *haReasonUnspecified);
+extern int get_haAdmProhibited(Integer *haAdmProhibited);
+extern int get_haInsufficientResource(Integer *haInsufficientResource);
+extern int get_haMNAuthenticationFailure(Integer *haMNAuthenticationFailure);
+extern int get_haFAAuthenticationFailure(Integer *haFAAuthenticationFailure);
+extern int get_haIDMismatch(Integer *haIDMismatch);
+extern int get_haPoorlyFormedRequest(Integer *haPoorlyFormedRequest);
+extern int get_haTooManyBindings(Integer *haTooManyBindings);
+extern int get_haUnknownHA(Integer *haUnknownHA);
+extern int get_haGratuitiousARPsSent(Integer *haGratuitiousARPsSent);
+extern int get_haProxyARPsSent(Integer *haProxyARPsSent);
+extern int get_haRegRequestsReceived(Integer *haRegRequestsReceived);
+extern int get_haDeRegRequestsReceived(Integer *haDeRegRequestsReceived);
+extern int get_haRegRepliesSent(Integer *haRegRepliesSent);
+extern int get_haDeRegRepliesSent(Integer *haDeRegRepliesSent);
+
+extern int get_mipSecAssocEntry(int search_type,
+ MipSecAssocEntry_t **mipSecAssocEntry_data, IndexType *index);
+extern void free_mipSecAssocEntry(MipSecAssocEntry_t *mipSecAssocEntry);
+
+/*
+ * Prototype for functions in mipagentsnmp_mipSecViolationEntry.c
+ */
+extern int get_mipSecViolationEntry(int search_type,
+ MipSecViolationEntry_t **mipSecViolationEntry_data, IndexType *index);
+extern void free_mipSecViolationEntry(MipSecViolationEntry_t
+ *mipSecViolationEntry);
+
+
+/*
+ * Prototype for functions in mipagentsnmp_maAdvConfigEntry.c
+ */
+extern int get_maAdvConfigEntry(int search_type,
+ MaAdvConfigEntry_t **maAdvConfigEntry_data, IndexType *index);
+extern void free_maAdvConfigEntry(MaAdvConfigEntry_t *maAdvConfigEntry);
+
+
+/*
+ * Prototype for functions in mipagentsnmp_faCOAEntry.c
+ */
+extern int get_faCOAEntry(int search_type,
+ FaCOAEntry_t **faCOAEntry_data, IndexType *index);
+extern void free_faCOAEntry(FaCOAEntry_t *faCOAEntry);
+
+
+/*
+ * Prototype for functions in mipagentsnmp_faVisitorEntry.c
+ */
+extern int get_faVisitorEntry(int search_type,
+ FaVisitorEntry_t **faVisitorEntry_data, IndexType *index);
+extern void free_faVisitorEntry(FaVisitorEntry_t *faVisitorEntry);
+
+
+/*
+ * Prototype for functions in mipagentsnmp_haMobilityBindingEntry.c
+ */
+extern int get_haMobilityBindingEntry(int search_type,
+ HaMobilityBindingEntry_t **haMobilityBindingEntry_data, IndexType *index);
+extern void free_haMobilityBindingEntry(HaMobilityBindingEntry_t
+ *haMobilityBindingEntry);
+
+
+/*
+ * Prototype for functions in mipagentsnmp_haCounterEntry.c
+ */
+extern int get_haCounterEntry(int search_type,
+ HaCounterEntry_t **haCounterEntry_data, IndexType *index);
+extern void free_haCounterEntry(HaCounterEntry_t *haCounterEntry);
+
+extern int SSAGetTrapPort();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MIPAGENTSNMP_STUB_H_ */
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mipagent/snmp_trap.c b/usr/src/cmd/cmd-inet/usr.lib/mipagent/snmp_trap.c
new file mode 100644
index 0000000000..4214a2fde4
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mipagent/snmp_trap.c
@@ -0,0 +1,50 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * file: mipagentsnmp_trap.c
+ *
+ * This file contains the SNMP trap routines.
+ */
+
+#include <sys/types.h>
+#include <netinet/in.h>
+
+#include <impl.h>
+#include <node.h>
+
+#include "snmp_stub.h"
+
+#ifndef lint
+struct CallbackItem genCallItem[10];
+int genNumCallItem = 0;
+int genNumTrapElem = 0;
+int genTrapTableMap[10];
+struct TrapHndlCxt genTrapBucket[10];
+struct TrapAnyEnterpriseInfo genTrapAnyEnterpriseInfo[10];
+#endif
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mipagent/snmp_tree.c b/usr/src/cmd/cmd-inet/usr.lib/mipagent/snmp_tree.c
new file mode 100644
index 0000000000..79c0c70a9c
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mipagent/snmp_tree.c
@@ -0,0 +1,715 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+
+#include <impl.h>
+#include <node.h>
+
+#include "snmp_stub.h"
+
+int get_i_dont_think_so();
+
+static Subid subid_table[] = {
+/* 0 */ 1, 3, 6, 1, 2, 1, 44, 1, 1, 1,
+/* 10 */ 1, 3, 6, 1, 2, 1, 44, 1, 1, 2,
+/* 20 */ 1, 3, 6, 1, 2, 1, 44, 1, 1, 3,
+/* 30 */ 1, 3, 6, 1, 2, 1, 44, 1, 2, 1, 1, 1,
+/* 42 */ 1, 3, 6, 1, 2, 1, 44, 1, 2, 1, 1, 3,
+/* 54 */ 1, 3, 6, 1, 2, 1, 44, 1, 2, 1, 1, 4,
+/* 66 */ 1, 3, 6, 1, 2, 1, 44, 1, 2, 1, 1, 5,
+/* 78 */ 1, 3, 6, 1, 2, 1, 44, 1, 2, 1, 1, 6,
+/* 90 */ 1, 3, 6, 1, 2, 1, 44, 1, 2, 2,
+/* 100 */ 1, 3, 6, 1, 2, 1, 44, 1, 2, 3, 1, 1,
+/* 112 */ 1, 3, 6, 1, 2, 1, 44, 1, 2, 3, 1, 2,
+/* 124 */ 1, 3, 6, 1, 2, 1, 44, 1, 2, 3, 1, 3,
+/* 136 */ 1, 3, 6, 1, 2, 1, 44, 1, 2, 3, 1, 4,
+/* 148 */ 1, 3, 6, 1, 2, 1, 44, 1, 2, 3, 1, 5,
+/* 160 */ 1, 3, 6, 1, 2, 1, 44, 1, 2, 3, 1, 6,
+/* 172 */ 1, 3, 6, 1, 2, 1, 44, 1, 2, 3, 1, 7,
+/* 184 */ 1, 3, 6, 1, 2, 1, 44, 1, 3, 1, 1, 1, 1,
+/* 197 */ 1, 3, 6, 1, 2, 1, 44, 1, 3, 1, 1, 1, 2,
+/* 210 */ 1, 3, 6, 1, 2, 1, 44, 1, 3, 1, 1, 1, 3,
+/* 223 */ 1, 3, 6, 1, 2, 1, 44, 1, 3, 1, 1, 1, 4,
+/* 236 */ 1, 3, 6, 1, 2, 1, 44, 1, 3, 1, 1, 1, 5,
+/* 249 */ 1, 3, 6, 1, 2, 1, 44, 1, 3, 1, 1, 1, 6,
+/* 262 */ 1, 3, 6, 1, 2, 1, 44, 1, 3, 1, 1, 1, 7,
+/* 275 */ 1, 3, 6, 1, 2, 1, 44, 1, 3, 1, 1, 1, 8,
+/* 288 */ 1, 3, 6, 1, 2, 1, 44, 1, 3, 1, 1, 1, 9,
+/* 301 */ 1, 3, 6, 1, 2, 1, 44, 1, 3, 1, 2,
+/* 312 */ 1, 3, 6, 1, 2, 1, 44, 1, 3, 1, 3,
+/* 323 */ 1, 3, 6, 1, 2, 1, 44, 1, 3, 1, 4,
+/* 334 */ 1, 3, 6, 1, 2, 1, 44, 1, 4, 1, 1, 1, 1,
+/* 347 */ 1, 3, 6, 1, 2, 1, 44, 1, 4, 1, 1, 1, 2,
+/* 360 */ 1, 3, 6, 1, 2, 1, 44, 1, 4, 2, 1,
+/* 371 */ 1, 3, 6, 1, 2, 1, 44, 1, 4, 2, 2,
+/* 382 */ 1, 3, 6, 1, 2, 1, 44, 1, 4, 3, 1, 1, 1,
+/* 395 */ 1, 3, 6, 1, 2, 1, 44, 1, 4, 3, 1, 1, 2,
+/* 408 */ 1, 3, 6, 1, 2, 1, 44, 1, 4, 3, 1, 1, 3,
+/* 421 */ 1, 3, 6, 1, 2, 1, 44, 1, 4, 3, 1, 1, 4,
+/* 434 */ 1, 3, 6, 1, 2, 1, 44, 1, 4, 3, 1, 1, 5,
+/* 447 */ 1, 3, 6, 1, 2, 1, 44, 1, 4, 3, 1, 1, 6,
+/* 460 */ 1, 3, 6, 1, 2, 1, 44, 1, 4, 3, 1, 1, 7,
+/* 473 */ 1, 3, 6, 1, 2, 1, 44, 1, 4, 3, 1, 1, 8,
+/* 486 */ 1, 3, 6, 1, 2, 1, 44, 1, 4, 3, 1, 1, 9,
+/* 499 */ 1, 3, 6, 1, 2, 1, 44, 1, 4, 3, 2,
+/* 510 */ 1, 3, 6, 1, 2, 1, 44, 1, 4, 3, 3,
+/* 521 */ 1, 3, 6, 1, 2, 1, 44, 1, 4, 3, 4,
+/* 532 */ 1, 3, 6, 1, 2, 1, 44, 1, 4, 3, 5,
+/* 543 */ 1, 3, 6, 1, 2, 1, 44, 1, 4, 3, 6,
+/* 554 */ 1, 3, 6, 1, 2, 1, 44, 1, 4, 3, 7,
+/* 565 */ 1, 3, 6, 1, 2, 1, 44, 1, 4, 3, 8,
+/* 576 */ 1, 3, 6, 1, 2, 1, 44, 1, 4, 3, 9,
+/* 587 */ 1, 3, 6, 1, 2, 1, 44, 1, 4, 3, 10,
+/* 598 */ 1, 3, 6, 1, 2, 1, 44, 1, 4, 3, 11,
+/* 609 */ 1, 3, 6, 1, 2, 1, 44, 1, 4, 3, 12,
+/* 620 */ 1, 3, 6, 1, 2, 1, 44, 1, 4, 3, 13,
+/* 631 */ 1, 3, 6, 1, 2, 1, 44, 1, 4, 3, 14,
+/* 642 */ 1, 3, 6, 1, 2, 1, 44, 1, 4, 3, 15,
+/* 653 */ 1, 3, 6, 1, 2, 1, 44, 1, 4, 3, 16,
+/* 664 */ 1, 3, 6, 1, 2, 1, 44, 1, 5, 3, 1, 1, 1,
+/* 677 */ 1, 3, 6, 1, 2, 1, 44, 1, 5, 3, 1, 1, 2,
+/* 690 */ 1, 3, 6, 1, 2, 1, 44, 1, 5, 3, 1, 1, 3,
+/* 703 */ 1, 3, 6, 1, 2, 1, 44, 1, 5, 3, 1, 1, 4,
+/* 716 */ 1, 3, 6, 1, 2, 1, 44, 1, 5, 3, 1, 1, 5,
+/* 729 */ 1, 3, 6, 1, 2, 1, 44, 1, 5, 3, 1, 1, 6,
+/* 742 */ 1, 3, 6, 1, 2, 1, 44, 1, 5, 3, 1, 1, 7,
+/* 755 */ 1, 3, 6, 1, 2, 1, 44, 1, 5, 3, 1, 1, 8,
+/* 768 */ 1, 3, 6, 1, 2, 1, 44, 1, 5, 3, 2, 1, 2,
+/* 781 */ 1, 3, 6, 1, 2, 1, 44, 1, 5, 3, 2, 1, 3,
+/* 794 */ 1, 3, 6, 1, 2, 1, 44, 1, 5, 3, 2, 1, 4,
+/* 807 */ 1, 3, 6, 1, 2, 1, 44, 1, 5, 3, 2, 1, 5,
+/* 820 */ 1, 3, 6, 1, 2, 1, 44, 1, 5, 3, 2, 1, 6,
+/* 833 */ 1, 3, 6, 1, 2, 1, 44, 1, 5, 3, 2, 1, 7,
+/* 846 */ 1, 3, 6, 1, 2, 1, 44, 1, 5, 3, 3,
+/* 857 */ 1, 3, 6, 1, 2, 1, 44, 1, 5, 3, 4,
+/* 868 */ 1, 3, 6, 1, 2, 1, 44, 1, 5, 3, 5,
+/* 879 */ 1, 3, 6, 1, 2, 1, 44, 1, 5, 3, 6,
+/* 890 */ 1, 3, 6, 1, 2, 1, 44, 1, 5, 3, 7,
+/* 901 */ 1, 3, 6, 1, 2, 1, 44, 1, 5, 3, 8,
+/* 912 */ 1, 3, 6, 1, 2, 1, 44, 1, 5, 3, 9,
+/* 923 */ 1, 3, 6, 1, 2, 1, 44, 1, 5, 3, 10,
+/* 934 */ 1, 3, 6, 1, 2, 1, 44, 1, 5, 3, 11,
+/* 945 */ 1, 3, 6, 1, 2, 1, 44, 1, 5, 3, 12,
+/* 956 */ 1, 3, 6, 1, 2, 1, 44, 1, 5, 3, 13,
+/* 967 */ 1, 3, 6, 1, 2, 1, 44, 1, 5, 3, 14,
+/* 978 */ 1, 3, 6, 1, 2, 1, 44, 1, 5, 3, 15,
+/* 989 */ 1, 3, 6, 1, 2, 1, 44, 1, 5, 3, 16,
+/* 1000 */ 1, 3, 6, 1, 2, 1, 44, 1, 5, 3, 17,
+/* 1011 */ 1, 3, 6, 1, 2, 1, 44, 1, 5, 3, 18,
+/* 1022 */ 1, 3, 6, 1, 2, 1, 44, 1, 5, 3, 19,
+0
+};
+int subid_table_size = 1033;
+
+Enum enum_table[] = {
+/* 0 */ { &enum_table[ 1], "mobileNode", 0 },
+/* 1 */ { &enum_table[ 2], "foreignAgent", 1 },
+/* 2 */ { NULL, "homeAgent", 2 },
+/* 3 */ { &enum_table[ 4], "enabled", 1 },
+/* 4 */ { NULL, "disabled", 2 },
+/* 5 */ { &enum_table[ 6], "ipInIp", 0 },
+/* 6 */ { &enum_table[ 7], "gre", 1 },
+/* 7 */ { &enum_table[ 8], "minEnc", 2 },
+/* 8 */ { NULL, "other", 3 },
+/* 9 */ { &enum_table[ 10], "other", 1 },
+/* 10 */ { NULL, "md5", 2 },
+/* 11 */ { &enum_table[ 12], "other", 1 },
+/* 12 */ { NULL, "prefixSuffix", 2 },
+/* 13 */ { &enum_table[ 14], "other", 1 },
+/* 14 */ { &enum_table[ 15], "timestamps", 2 },
+/* 15 */ { NULL, "nonces", 3 },
+/* 16 */ { &enum_table[ 17], "noMobilitySecurityAssociation", 1 },
+/* 17 */ { &enum_table[ 18], "badAuthenticator", 2 },
+/* 18 */ { &enum_table[ 19], "badIdentifier", 3 },
+/* 19 */ { &enum_table[ 20], "badSPI", 4 },
+/* 20 */ { &enum_table[ 21], "missingSecurityExtension", 5 },
+/* 21 */ { NULL, "other", 6 },
+/* 22 */ { &enum_table[ 23], "vjCompression", 0 },
+/* 23 */ { &enum_table[ 24], "gre", 1 },
+/* 24 */ { &enum_table[ 25], "minEnc", 2 },
+/* 25 */ { &enum_table[ 26], "decapsulationbyMN", 3 },
+/* 26 */ { &enum_table[ 27], "broadcastDatagram", 4 },
+/* 27 */ { NULL, "simultaneousBindings", 5 },
+/* 28 */ { &enum_table[ 29], "vjCompression", 0 },
+/* 29 */ { &enum_table[ 30], "gre", 1 },
+/* 30 */ { &enum_table[ 31], "minEnc", 2 },
+/* 31 */ { &enum_table[ 32], "decapsulationbyMN", 3 },
+/* 32 */ { &enum_table[ 33], "broadcastDatagram", 4 },
+/* 33 */ { NULL, "simultaneousBindings", 5 },
+/* 34 */ { &enum_table[ 35], "reasonUnspecified", 128 },
+/* 35 */ { &enum_table[ 36], "admProhibited", 129 },
+/* 36 */ { &enum_table[ 37], "insufficientResource", 130 },
+/* 37 */ { &enum_table[ 38], "mnAuthenticationFailure", 131 },
+/* 38 */ { &enum_table[ 39], "faAuthenticationFailure", 132 },
+/* 39 */ { &enum_table[ 40], "idMismatch", 133 },
+/* 40 */ { &enum_table[ 41], "poorlyFormedRequest", 134 },
+/* 41 */ { &enum_table[ 42], "tooManyBindings", 135 },
+/* 42 */ { NULL, "unknownHA", 136 },
+{ NULL, NULL, 0 }
+};
+int enum_table_size = 43;
+
+Object object_table[] = {
+/* 0 */ { { &subid_table[0], 10 }, INTEGER, &enum_table[0],
+ READ_FLAG, 1, get_mipEntities, NULL, NULL },
+/* 1 */ { { &subid_table[10], 10 }, INTEGER, &enum_table[3],
+ READ_FLAG | WRITE_FLAG, 1, get_mipEnable, set_mipEnable, NULL },
+/* 2 */ { { &subid_table[20], 10 }, INTEGER, &enum_table[5],
+ READ_FLAG, 1, get_mipEncapsulationSupported, NULL, NULL },
+/* 3 */ { { &subid_table[90], 10 }, COUNTER, NULL,
+ READ_FLAG, 1, get_mipSecTotalViolations, NULL, NULL },
+/* 4 */ { { &subid_table[301], 11 }, COUNTER, NULL,
+ READ_FLAG, 1, get_maAdvertisementsSent, NULL, NULL },
+/* 5 */ { { &subid_table[312], 11 }, COUNTER, NULL,
+ READ_FLAG, 1, get_maAdvsSentForSolicitation, NULL, NULL },
+/* 6 */ { { &subid_table[323], 11 }, COUNTER, NULL,
+ READ_FLAG, 1, get_maSolicitationsReceived, NULL, NULL },
+/* 7 */ { { &subid_table[360], 11 }, COUNTER, NULL,
+ READ_FLAG, 1, get_faIsBusy, NULL, NULL },
+/* 8 */ { { &subid_table[371], 11 }, COUNTER, NULL,
+ READ_FLAG | WRITE_FLAG, 1, get_faRegistrationRequired,
+ set_faRegistrationRequired, NULL },
+/* 9 */ { { &subid_table[499], 11 }, COUNTER, NULL,
+ READ_FLAG, 1, get_faRegRequestsReceived, NULL, NULL },
+/* 10 */ { { &subid_table[510], 11 }, COUNTER, NULL,
+ READ_FLAG, 1, get_faRegRequestsRelayed, NULL, NULL },
+/* 11 */ { { &subid_table[521], 11 }, COUNTER, NULL,
+ READ_FLAG, 1, get_faReasonUnspecified, NULL, NULL },
+/* 12 */ { { &subid_table[532], 11 }, COUNTER, NULL,
+ READ_FLAG, 1, get_faAdmProhibited, NULL, NULL },
+/* 13 */ { { &subid_table[543], 11 }, COUNTER, NULL,
+ READ_FLAG, 1, get_faInsufficientResource, NULL, NULL },
+/* 14 */ { { &subid_table[554], 11 }, COUNTER, NULL,
+ READ_FLAG, 1, get_faMNAuthenticationFailure, NULL, NULL },
+/* 15 */ { { &subid_table[565], 11 }, COUNTER, NULL,
+ READ_FLAG, 1, get_faRegLifetimeTooLong, NULL, NULL },
+/* 16 */ { { &subid_table[576], 11 }, COUNTER, NULL,
+ READ_FLAG, 1, get_faPoorlyFormedRequests, NULL, NULL },
+/* 17 */ { { &subid_table[587], 11 }, COUNTER, NULL,
+ READ_FLAG, 1, get_faEncapsulationUnavailable, NULL, NULL },
+/* 18 */ { { &subid_table[598], 11 }, COUNTER, NULL,
+ READ_FLAG, 1, get_faVJCompressionUnavailable, NULL, NULL },
+/* 19 */ { { &subid_table[609], 11 }, COUNTER, NULL,
+ READ_FLAG, 1, get_faHAUnreachable, NULL, NULL },
+/* 20 */ { { &subid_table[620], 11 }, COUNTER, NULL,
+ READ_FLAG, 1, get_faRegRepliesRecieved, NULL, NULL },
+/* 21 */ { { &subid_table[631], 11 }, COUNTER, NULL,
+ READ_FLAG, 1, get_faRegRepliesRelayed, NULL, NULL },
+/* 22 */ { { &subid_table[642], 11 }, COUNTER, NULL,
+ READ_FLAG, 1, get_faHAAuthenticationFailure, NULL, NULL },
+/* 23 */ { { &subid_table[653], 11 }, COUNTER, NULL,
+ READ_FLAG, 1, get_faPoorlyFormedReplies, NULL, NULL },
+/* 24 */ { { &subid_table[846], 11 }, COUNTER, NULL,
+ READ_FLAG, 1, get_haRegistrationAccepted, NULL, NULL },
+/* 25 */ { { &subid_table[857], 11 }, COUNTER, NULL,
+ READ_FLAG, 1, get_haMultiBindingUnsupported, NULL, NULL },
+/* 26 */ { { &subid_table[868], 11 }, COUNTER, NULL,
+ READ_FLAG, 1, get_haReasonUnspecified, NULL, NULL },
+/* 27 */ { { &subid_table[879], 11 }, COUNTER, NULL,
+ READ_FLAG, 1, get_haAdmProhibited, NULL, NULL },
+/* 28 */ { { &subid_table[890], 11 }, COUNTER, NULL,
+ READ_FLAG, 1, get_haInsufficientResource, NULL, NULL },
+/* 29 */ { { &subid_table[901], 11 }, COUNTER, NULL,
+ READ_FLAG, 1, get_haMNAuthenticationFailure, NULL, NULL },
+/* 30 */ { { &subid_table[912], 11 }, COUNTER, NULL,
+ READ_FLAG, 1, get_haFAAuthenticationFailure, NULL, NULL },
+/* 31 */ { { &subid_table[923], 11 }, COUNTER, NULL,
+ READ_FLAG, 1, get_haIDMismatch, NULL, NULL },
+/* 32 */ { { &subid_table[934], 11 }, COUNTER, NULL,
+ READ_FLAG, 1, get_haPoorlyFormedRequest, NULL, NULL },
+/* 33 */ { { &subid_table[945], 11 }, COUNTER, NULL,
+ READ_FLAG, 1, get_haTooManyBindings, NULL, NULL },
+/* 34 */ { { &subid_table[956], 11 }, COUNTER, NULL,
+ READ_FLAG, 1, get_haUnknownHA, NULL, NULL },
+/* 35 */ { { &subid_table[967], 11 }, COUNTER, NULL,
+ READ_FLAG, 1, get_haGratuitiousARPsSent, NULL, NULL },
+/* 36 */ { { &subid_table[978], 11 }, COUNTER, NULL,
+ READ_FLAG, 1, get_haProxyARPsSent, NULL, NULL },
+/* 37 */ { { &subid_table[989], 11 }, COUNTER, NULL,
+ READ_FLAG, 1, get_haRegRequestsReceived, NULL, NULL },
+/* 38 */ { { &subid_table[1000], 11 }, COUNTER, NULL,
+ READ_FLAG, 1, get_haDeRegRequestsReceived, NULL, NULL },
+/* 39 */ { { &subid_table[1011], 11 }, COUNTER, NULL,
+ READ_FLAG, 1, get_haRegRepliesSent, NULL, NULL },
+/* 40 */ { { &subid_table[1022], 11 }, COUNTER, NULL,
+ READ_FLAG, 1, get_haDeRegRepliesSent, NULL, NULL },
+{ { NULL, 0}, 0, NULL, 0, NULL, NULL }
+};
+int object_table_size = 41;
+
+Index index_table[] = {
+/* 0 */ { &index_table[ 1], "mipSecPeerAddress", 5, 0, &node_table[16] },
+/* 1 */ { NULL, "mipSecSPI", 0, 0, &node_table[17] },
+/* 2 */ { NULL, "mipSecViolatorAddress", 5, 0, &node_table[25] },
+/* 3 */ { NULL, "maInterfaceAddress", 5, 0, &node_table[36] },
+/* 4 */ { NULL, "faSupportedCOA", 5, 0, &node_table[52] },
+/* 5 */ { NULL, "faVisitorIPAddress", 5, 0, &node_table[60] },
+/* 6 */ { &index_table[ 7], "haMobilityBindingMN", 5, 0, &node_table[88] },
+/* 7 */ { NULL, "haMobilityBindingCOA", 5, 0, &node_table[89] },
+/* 8 */ { NULL, "haMobilityBindingMN", 5, 0, &node_table[88] },
+{ NULL, NULL, NULL }
+};
+int index_table_size = 9;
+
+Entry entry_table[] = {
+/* 0 */ { &index_table[0], 2, get_mipSecAssocEntry, free_mipSecAssocEntry },
+/* 1 */ { &index_table[2], 1, get_mipSecViolationEntry,
+ free_mipSecViolationEntry },
+/* 2 */ { &index_table[3], 1, get_maAdvConfigEntry, free_maAdvConfigEntry },
+/* 3 */ { &index_table[4], 1, get_faCOAEntry, free_faCOAEntry },
+/* 4 */ { &index_table[5], 1, get_faVisitorEntry, free_faVisitorEntry },
+/* 5 */ { &index_table[6], 2, get_haMobilityBindingEntry,
+ free_haMobilityBindingEntry },
+/* 6 */ { &index_table[8], 1, get_haCounterEntry, free_haCounterEntry },
+{ NULL, 0, NULL }
+};
+int entry_table_size = 7;
+
+Column column_table[] = {
+/* 0 */ { { &subid_table[30], 12 }, IPADDRESS, NULL,
+ 0, 2, NULL, NULL, &entry_table[0], 0 },
+/* 1 */ { { &subid_table[42], 12 }, INTEGER, &enum_table[9],
+ READ_FLAG, 2, get_i_dont_think_so, NULL, &entry_table[0], 0 },
+/* 2 */ { { &subid_table[54], 12 }, INTEGER, &enum_table[11],
+ READ_FLAG, 2, get_i_dont_think_so, NULL, &entry_table[0], 4 },
+/* 3 */ { { &subid_table[66], 12 }, STRING, NULL,
+ READ_FLAG, 2, get_i_dont_think_so, NULL, &entry_table[0], 8 },
+/* 4 */ { { &subid_table[78], 12 }, INTEGER, &enum_table[13],
+ READ_FLAG, 2, get_i_dont_think_so, NULL, &entry_table[0], 16 },
+/* 5 */ { { &subid_table[100], 12 }, IPADDRESS, NULL,
+ READ_FLAG, 2, get_i_dont_think_so, NULL, &entry_table[1], 0 },
+/* 6 */ { { &subid_table[112], 12 }, COUNTER, NULL,
+ READ_FLAG, 2, get_i_dont_think_so, NULL, &entry_table[1], 8 },
+/* 7 */ { { &subid_table[124], 12 }, COUNTER, NULL,
+ READ_FLAG, 2, get_i_dont_think_so, NULL, &entry_table[1], 12 },
+/* 8 */ { { &subid_table[136], 12 }, TIMETICKS, NULL,
+ READ_FLAG, 2, get_i_dont_think_so, NULL, &entry_table[1], 16 },
+/* 9 */ { { &subid_table[148], 12 }, COUNTER, NULL,
+ READ_FLAG, 2, get_i_dont_think_so, NULL, &entry_table[1], 20 },
+/* 10 */ { { &subid_table[160], 12 }, COUNTER, NULL,
+ READ_FLAG, 2, get_i_dont_think_so, NULL, &entry_table[1], 24 },
+/* 11 */ { { &subid_table[172], 12 }, INTEGER, &enum_table[16],
+ READ_FLAG, 2, get_i_dont_think_so, NULL, &entry_table[1], 28 },
+/* 12 */ { { &subid_table[184], 13 }, IPADDRESS, NULL, 0, 2,
+ NULL, NULL, &entry_table[2], 0 },
+/* 13 */ { { &subid_table[197], 13 }, COUNTER, NULL,
+ READ_FLAG, 2, get_i_dont_think_so, NULL, &entry_table[2], 0 },
+/* 14 */ { { &subid_table[210], 13 }, COUNTER, NULL,
+ READ_FLAG, 2, get_i_dont_think_so, NULL, &entry_table[2], 4 },
+/* 15 */ { { &subid_table[223], 13 }, IPADDRESS, NULL,
+ READ_FLAG, 2, get_i_dont_think_so, NULL, &entry_table[2], 8 },
+/* 16 */ { { &subid_table[236], 13 }, COUNTER, NULL,
+ READ_FLAG, 2, get_i_dont_think_so, NULL, &entry_table[2], 16 },
+/* 17 */ { { &subid_table[249], 13 }, COUNTER, NULL,
+ READ_FLAG, 2, get_i_dont_think_so, NULL, &entry_table[2], 20 },
+/* 18 */ { { &subid_table[262], 13 }, COUNTER, NULL,
+ READ_FLAG, 2, get_i_dont_think_so, NULL, &entry_table[2], 24 },
+/* 19 */ { { &subid_table[275], 13 }, COUNTER, NULL,
+ READ_FLAG, 2, get_i_dont_think_so, NULL, &entry_table[2], 28 },
+/* 20 */ { { &subid_table[288], 13 }, COUNTER, NULL,
+ READ_FLAG, 2, get_i_dont_think_so, NULL, &entry_table[2], 32 },
+/* 21 */ { { &subid_table[334], 13 }, IPADDRESS, NULL, 0, 2,
+ NULL, NULL, &entry_table[3], 0 },
+/* 22 */ { { &subid_table[347], 13 }, COUNTER, NULL,
+ READ_FLAG, 2, get_i_dont_think_so, NULL, &entry_table[3], 0 },
+/* 23 */ { { &subid_table[382], 13 }, IPADDRESS, NULL,
+ READ_FLAG, 2, get_i_dont_think_so, NULL, &entry_table[4], 0 },
+/* 24 */ { { &subid_table[395], 13 }, IPADDRESS, NULL,
+ READ_FLAG, 2, get_i_dont_think_so, NULL, &entry_table[4], 8 },
+/* 25 */ { { &subid_table[408], 13 }, IPADDRESS, NULL,
+ READ_FLAG, 2, get_i_dont_think_so, NULL, &entry_table[4], 16 },
+/* 26 */ { { &subid_table[421], 13 }, COUNTER, NULL,
+ READ_FLAG, 2, get_i_dont_think_so, NULL, &entry_table[4], 24 },
+/* 27 */ { { &subid_table[434], 13 }, GAUGE, NULL,
+ READ_FLAG, 2, get_i_dont_think_so, NULL, &entry_table[4], 28 },
+/* 28 */ { { &subid_table[447], 13 }, INTEGER, &enum_table[22],
+ READ_FLAG, 2, get_i_dont_think_so, NULL, &entry_table[4], 32 },
+/* 29 */ { { &subid_table[460], 13 }, COUNTER, NULL,
+ READ_FLAG, 2, get_i_dont_think_so, NULL, &entry_table[4], 36 },
+/* 30 */ { { &subid_table[473], 13 }, COUNTER, NULL,
+ READ_FLAG, 2, get_i_dont_think_so, NULL, &entry_table[4], 40 },
+/* 31 */ { { &subid_table[486], 13 }, COUNTER, NULL,
+ READ_FLAG, 2, get_i_dont_think_so, NULL, &entry_table[4], 44 },
+/* 32 */ { { &subid_table[664], 13 }, IPADDRESS, NULL,
+ READ_FLAG, 2, get_i_dont_think_so, NULL, &entry_table[5], 0 },
+/* 33 */ { { &subid_table[677], 13 }, IPADDRESS, NULL,
+ READ_FLAG, 2, get_i_dont_think_so, NULL, &entry_table[5], 8 },
+/* 34 */ { { &subid_table[690], 13 }, IPADDRESS, NULL,
+ READ_FLAG, 2, get_i_dont_think_so, NULL, &entry_table[5], 16 },
+/* 35 */ { { &subid_table[703], 13 }, INTEGER, &enum_table[28],
+ READ_FLAG, 2, get_i_dont_think_so, NULL, &entry_table[5], 24 },
+/* 36 */ { { &subid_table[716], 13 }, COUNTER, NULL,
+ READ_FLAG, 2, get_i_dont_think_so, NULL, &entry_table[5], 28 },
+/* 37 */ { { &subid_table[729], 13 }, COUNTER, NULL,
+ READ_FLAG, 2, get_i_dont_think_so, NULL, &entry_table[5], 32 },
+/* 38 */ { { &subid_table[742], 13 }, COUNTER, NULL,
+ READ_FLAG, 2, get_i_dont_think_so, NULL, &entry_table[5], 36 },
+/* 39 */ { { &subid_table[755], 13 }, GAUGE, NULL,
+ READ_FLAG, 2, get_i_dont_think_so, NULL, &entry_table[5], 40 },
+/* 40 */ { { &subid_table[768], 13 }, COUNTER, NULL,
+ READ_FLAG, 2, get_i_dont_think_so, NULL, &entry_table[6], 0 },
+/* 41 */ { { &subid_table[781], 13 }, COUNTER, NULL,
+ READ_FLAG, 2, get_i_dont_think_so, NULL, &entry_table[6], 4 },
+/* 42 */ { { &subid_table[794], 13 }, GAUGE, NULL,
+ READ_FLAG, 2, get_i_dont_think_so, NULL, &entry_table[6], 8 },
+/* 43 */ { { &subid_table[807], 13 }, TIMETICKS, NULL,
+ READ_FLAG, 2, get_i_dont_think_so, NULL, &entry_table[6], 12 },
+/* 44 */ { { &subid_table[820], 13 }, TIMETICKS, NULL,
+ READ_FLAG, 2, get_i_dont_think_so, NULL, &entry_table[6], 16 },
+/* 45 */ { { &subid_table[833], 13 }, INTEGER, &enum_table[34],
+ READ_FLAG, 2, get_i_dont_think_so, NULL, &entry_table[6], 20 },
+{ { NULL, 0 }, 0, NULL, 0, NULL, NULL, 0 }
+};
+int column_table_size = 46;
+
+Node node_table[] = {
+/* 0 */ { NULL, &node_table[ 1],
+ NULL, &node_table[ 10], "iso", 1, NODE, NULL },
+/* 1 */ { &node_table[ 0], &node_table[ 2],
+ NULL, &node_table[ 10], "org", 3, NODE, NULL },
+/* 2 */ { &node_table[ 1], &node_table[ 3],
+ NULL, &node_table[ 10], "dod", 6, NODE, NULL },
+/* 3 */ { &node_table[ 2], &node_table[ 4],
+ NULL, &node_table[ 10], "internet", 1, NODE, NULL },
+/* 4 */ { &node_table[ 3],
+ NULL, &node_table[ 5], &node_table[ 10], "directory", 1,
+ NODE, NULL },
+/* 5 */ { &node_table[ 3], &node_table[ 6], &node_table[ 140],
+ &node_table[ 10], "mgmt", 2, NODE, NULL },
+/* 6 */ { &node_table[ 5], &node_table[ 7],
+ NULL, &node_table[ 10], "mib-2", 1, NODE, NULL },
+/* 7 */ { &node_table[ 6], &node_table[ 8],
+ NULL, &node_table[ 10], "mipMIB", 44, NODE, NULL },
+/* 8 */ { &node_table[ 7], &node_table[ 9], &node_table[ 121],
+ &node_table[ 10], "mipMIBObjects", 1, NODE, NULL },
+/* 9 */ { &node_table[ 8], &node_table[ 10], &node_table[ 13],
+ &node_table[ 10], "mipSystem", 1, NODE, NULL },
+/* 10 */ { &node_table[ 9],
+ NULL, &node_table[ 11], &node_table[ 11], "mipEntities", 1,
+ OBJECT, (void *) &object_table[0] },
+/* 11 */ { &node_table[ 9],
+ NULL, &node_table[ 12], &node_table[ 12], "mipEnable", 2,
+ OBJECT, (void *) &object_table[1] },
+/* 12 */ { &node_table[ 9],
+ NULL, NULL, &node_table[18], "mipEncapsulationSupported", 3,
+ OBJECT, (void *) &object_table[2] },
+/* 13 */ { &node_table[ 8], &node_table[ 14], &node_table[ 32],
+ &node_table[ 18], "mipSecurity", 2, NODE, NULL },
+/* 14 */ { &node_table[ 13], &node_table[ 15], &node_table[ 22],
+ &node_table[ 18], "mipSecAssocTable", 1, NODE, NULL },
+/* 15 */ { &node_table[ 14], &node_table[ 16], NULL, &node_table[ 18],
+ "mipSecAssocEntry", 1, NODE, NULL },
+/* 16 */ { &node_table[ 15], NULL, &node_table[ 17], &node_table[ 18],
+ "mipSecPeerAddress", 1, COLUMN, (void *) &column_table[0] },
+/* 17 */ { &node_table[ 15], NULL, &node_table[ 18], &node_table[ 18],
+ "mipSecSPI", 2, NODE, NULL },
+/* 18 */ { &node_table[ 15], NULL, &node_table[ 19], &node_table[ 19],
+ "mipSecAlgorithmType", 3, COLUMN, (void *) &column_table[1] },
+/* 19 */ { &node_table[ 15], NULL, &node_table[ 20], &node_table[ 20],
+ "mipSecAlgorithmMode", 4, COLUMN, (void *) &column_table[2] },
+/* 20 */ { &node_table[ 15], NULL, &node_table[ 21], &node_table[ 21],
+ "mipSecKey", 5, COLUMN, (void *) &column_table[3] },
+/* 21 */ { &node_table[ 15], NULL, NULL, &node_table[ 22],
+ "mipSecReplayMethod", 6, COLUMN, (void *) &column_table[4] },
+/* 22 */ { &node_table[ 13], NULL, &node_table[ 23], &node_table[ 25],
+ "mipSecTotalViolations", 2, OBJECT, (void *) &object_table[3] },
+/* 23 */ { &node_table[ 13], &node_table[ 24], NULL, &node_table[ 25],
+ "mipSecViolationTable", 3, NODE, NULL },
+/* 24 */ { &node_table[ 23], &node_table[ 25], NULL, &node_table[ 25],
+ "mipSecViolationEntry", 1, NODE, NULL },
+/* 25 */ { &node_table[ 24], NULL, &node_table[ 26], &node_table[ 26],
+ "mipSecViolatorAddress", 1, COLUMN, (void *) &column_table[5] },
+/* 26 */ { &node_table[ 24], NULL, &node_table[ 27], &node_table[ 27],
+ "mipSecViolationCounter", 2, COLUMN, (void *) &column_table[6] },
+/* 27 */ { &node_table[ 24], NULL, &node_table[ 28], &node_table[ 28],
+ "mipSecRecentViolationSPI", 3, COLUMN, (void *) &column_table[7] },
+/* 28 */ { &node_table[ 24], NULL, &node_table[ 29], &node_table[ 29],
+ "mipSecRecentViolationTime", 4, COLUMN, (void *) &column_table[8] },
+/* 29 */ { &node_table[ 24], NULL, &node_table[ 30], &node_table[ 30],
+ "mipSecRecentViolationIDLow", 5, COLUMN, (void *) &column_table[9] },
+/* 30 */ { &node_table[ 24], NULL, &node_table[ 31], &node_table[ 31],
+ "mipSecRecentViolationIDHigh", 6, COLUMN, (void *) &column_table[10] },
+/* 31 */ { &node_table[ 24], NULL, NULL, &node_table[ 37],
+ "mipSecRecentViolationReason", 7, COLUMN, (void *) &column_table[11] },
+/* 32 */ { &node_table[ 8], &node_table[ 33], &node_table[ 48],
+ &node_table[ 37], "mipMA", 3, NODE, NULL },
+/* 33 */ { &node_table[ 32], &node_table[ 34], NULL, &node_table[ 37],
+ "maAdvertisement", 1, NODE, NULL },
+/* 34 */ { &node_table[ 33], &node_table[ 35], &node_table[ 45],
+ &node_table[ 37], "maAdvConfigTable", 1, NODE, NULL },
+/* 35 */ { &node_table[ 34], &node_table[ 36], NULL, &node_table[ 37],
+ "maAdvConfigEntry", 1, NODE, NULL },
+/* 36 */ { &node_table[ 35], NULL, &node_table[ 37], &node_table[ 37],
+ "maInterfaceAddress", 1, COLUMN, (void *) &column_table[12] },
+/* 37 */ { &node_table[ 35], NULL, &node_table[ 38], &node_table[ 38],
+ "maAdvMaxRegLifetime", 2, COLUMN, (void *) &column_table[13] },
+/* 38 */ { &node_table[ 35], NULL, &node_table[ 39], &node_table[ 39],
+ "maAdvPrefixLengthInclusion", 3, COLUMN, (void *) &column_table[14] },
+/* 39 */ { &node_table[ 35], NULL, &node_table[ 40], &node_table[ 40],
+ "maAdvAddress", 4, COLUMN, (void *) &column_table[15] },
+/* 40 */ { &node_table[ 35], NULL, &node_table[ 41], &node_table[ 41],
+ "maAdvMaxInterval", 5, COLUMN, (void *) &column_table[16] },
+/* 41 */ { &node_table[ 35], NULL, &node_table[ 42], &node_table[ 42],
+ "maAdvMinInterval", 6, COLUMN, (void *) &column_table[17] },
+/* 42 */ { &node_table[ 35], NULL, &node_table[ 43], &node_table[ 43],
+ "maAdvMaxAdvLifetime", 7, COLUMN, (void *) &column_table[18] },
+/* 43 */ { &node_table[ 35], NULL, &node_table[ 44], &node_table[ 44],
+ "maAdvResponseSolicitationOnly", 8, COLUMN,
+ (void *) &column_table[19] },
+/* 44 */ { &node_table[ 35], NULL, NULL, &node_table[ 45],
+ "maAdvStatus", 9, COLUMN, (void *) &column_table[20] },
+/* 45 */ { &node_table[ 33], NULL, &node_table[ 46], &node_table[ 46],
+ "maAdvertisementsSent", 2, OBJECT, (void *) &object_table[4] },
+/* 46 */ { &node_table[ 33], NULL, &node_table[ 47], &node_table[ 47],
+ "maAdvsSentForSolicitation", 3, OBJECT, (void *) &object_table[5] },
+/* 47 */ { &node_table[ 33], NULL, NULL, &node_table[ 53],
+ "maSolicitationsReceived", 4, OBJECT, (void *) &object_table[6] },
+/* 48 */ { &node_table[ 8], &node_table[ 49], &node_table[ 84],
+ &node_table[ 53], "mipFA", 4, NODE, NULL },
+/* 49 */ { &node_table[ 48], &node_table[ 50], &node_table[ 54],
+ &node_table[ 53], "faSystem", 1, NODE, NULL },
+/* 50 */ { &node_table[ 49], &node_table[ 51], NULL, &node_table[ 53],
+ "faCOATable", 1, NODE, NULL },
+/* 51 */ { &node_table[ 50], &node_table[ 52], NULL, &node_table[ 53],
+ "faCOAEntry", 1, NODE, NULL },
+/* 52 */ { &node_table[ 51], NULL, &node_table[ 53], &node_table[ 53],
+ "faSupportedCOA", 1, COLUMN, (void *) &column_table[21] },
+/* 53 */ { &node_table[ 51], NULL, NULL, &node_table[ 55],
+ "faCOAStatus", 2, COLUMN, (void *) &column_table[22] },
+/* 54 */ { &node_table[ 48], &node_table[ 55], &node_table[ 57],
+ &node_table[ 55], "faAdvertisement", 2, NODE, NULL },
+/* 55 */ { &node_table[ 54], NULL, &node_table[ 56], &node_table[ 56],
+ "faIsBusy", 1, OBJECT, (void *) &object_table[7] },
+/* 56 */ { &node_table[ 54], NULL, NULL, &node_table[ 60],
+ "faRegistrationRequired", 2, OBJECT, (void *) &object_table[8] },
+/* 57 */ { &node_table[ 48], &node_table[ 58], NULL, &node_table[ 60],
+ "faRegistration", 3, NODE, NULL },
+/* 58 */ { &node_table[ 57], &node_table[ 59], &node_table[ 69],
+ &node_table[ 60], "faVisitorTable", 1, NODE, NULL },
+/* 59 */ { &node_table[ 58], &node_table[ 60], NULL, &node_table[ 60],
+ "faVisitorEntry", 1, NODE, NULL },
+/* 60 */ { &node_table[ 59], NULL, &node_table[ 61], &node_table[ 61],
+ "faVisitorIPAddress", 1, COLUMN, (void *) &column_table[23] },
+/* 61 */ { &node_table[ 59], NULL, &node_table[ 62], &node_table[ 62],
+ "faVisitorHomeAddress", 2, COLUMN, (void *) &column_table[24] },
+/* 62 */ { &node_table[ 59], NULL, &node_table[ 63], &node_table[ 63],
+ "faVisitorHomeAgentAddress", 3, COLUMN, (void *) &column_table[25] },
+/* 63 */ { &node_table[ 59], NULL, &node_table[ 64], &node_table[ 64],
+ "faVisitorTimeGranted", 4, COLUMN, (void *) &column_table[26] },
+/* 64 */ { &node_table[ 59], NULL, &node_table[ 65], &node_table[ 65],
+ "faVisitorTimeRemaining", 5, COLUMN, (void *) &column_table[27] },
+/* 65 */ { &node_table[ 59], NULL, &node_table[ 66], &node_table[ 66],
+ "faVisitorRegFlags", 6, COLUMN, (void *) &column_table[28] },
+/* 66 */ { &node_table[ 59], NULL, &node_table[ 67], &node_table[ 67],
+ "faVisitorRegIDLow", 7, COLUMN, (void *) &column_table[29] },
+/* 67 */ { &node_table[ 59], NULL, &node_table[ 68], &node_table[ 68],
+ "faVisitorRegIDHigh", 8, COLUMN, (void *) &column_table[30] },
+/* 68 */ { &node_table[ 59], NULL, NULL, &node_table[ 69],
+ "faVisitorRegIsAccepted", 9, COLUMN, (void *) &column_table[31] },
+/* 69 */ { &node_table[ 57], NULL, &node_table[ 70], &node_table[ 70],
+ "faRegRequestsReceived", 2, OBJECT, (void *) &object_table[9] },
+/* 70 */ { &node_table[ 57], NULL, &node_table[ 71], &node_table[ 71],
+ "faRegRequestsRelayed", 3, OBJECT, (void *) &object_table[10] },
+/* 71 */ { &node_table[ 57], NULL, &node_table[ 72], &node_table[ 72],
+ "faReasonUnspecified", 4, OBJECT, (void *) &object_table[11] },
+/* 72 */ { &node_table[ 57], NULL, &node_table[ 73], &node_table[ 73],
+ "faAdmProhibited", 5, OBJECT, (void *) &object_table[12] },
+/* 73 */ { &node_table[ 57], NULL, &node_table[ 74], &node_table[ 74],
+ "faInsufficientResource", 6, OBJECT, (void *) &object_table[13] },
+/* 74 */ { &node_table[ 57], NULL, &node_table[ 75], &node_table[ 75],
+ "faMNAuthenticationFailure", 7, OBJECT, (void *) &object_table[14] },
+/* 75 */ { &node_table[ 57], NULL, &node_table[ 76], &node_table[ 76],
+ "faRegLifetimeTooLong", 8, OBJECT, (void *) &object_table[15] },
+/* 76 */ { &node_table[ 57], NULL, &node_table[ 77], &node_table[ 77],
+ "faPoorlyFormedRequests", 9, OBJECT, (void *) &object_table[16] },
+/* 77 */ { &node_table[ 57], NULL, &node_table[ 78], &node_table[ 78],
+ "faEncapsulationUnavailable", 10, OBJECT, (void *) &object_table[17] },
+/* 78 */ { &node_table[ 57], NULL, &node_table[ 79], &node_table[ 79],
+ "faVJCompressionUnavailable", 11, OBJECT, (void *) &object_table[18] },
+/* 79 */ { &node_table[ 57], NULL, &node_table[ 80], &node_table[ 80],
+ "faHAUnreachable", 12, OBJECT, (void *) &object_table[19] },
+/* 80 */ { &node_table[ 57], NULL, &node_table[ 81], &node_table[ 81],
+ "faRegRepliesRecieved", 13, OBJECT, (void *) &object_table[20] },
+/* 81 */ { &node_table[ 57], NULL, &node_table[ 82], &node_table[ 82],
+ "faRegRepliesRelayed", 14, OBJECT, (void *) &object_table[21] },
+/* 82 */ { &node_table[ 57], NULL, &node_table[ 83], &node_table[ 83],
+ "faHAAuthenticationFailure", 15, OBJECT, (void *) &object_table[22] },
+/* 83 */ { &node_table[ 57], NULL, NULL, &node_table[ 88],
+ "faPoorlyFormedReplies", 16, OBJECT, (void *) &object_table[23] },
+/* 84 */ { &node_table[ 8], &node_table[ 85], NULL, &node_table[ 88],
+ "mipHA", 5, NODE, NULL },
+/* 85 */ { &node_table[ 84], &node_table[ 86], NULL, &node_table[ 88],
+ "haRegistration", 3, NODE, NULL },
+/* 86 */ { &node_table[ 85], &node_table[ 87], &node_table[ 96],
+ &node_table[ 88], "haMobilityBindingTable", 1, NODE, NULL },
+/* 87 */ { &node_table[ 86], &node_table[ 88], NULL, &node_table[ 88],
+ "haMobilityBindingEntry", 1, NODE, NULL },
+/* 88 */ { &node_table[ 87], NULL, &node_table[ 89], &node_table[ 89],
+ "haMobilityBindingMN", 1, COLUMN, (void *) &column_table[32] },
+/* 89 */ { &node_table[ 87], NULL, &node_table[ 90], &node_table[ 90],
+ "haMobilityBindingCOA", 2, COLUMN, (void *) &column_table[33] },
+/* 90 */ { &node_table[ 87], NULL, &node_table[ 91], &node_table[ 91],
+ "haMobilityBindingSourceAddress", 3, COLUMN,
+ (void *) &column_table[34] },
+/* 91 */ { &node_table[ 87], NULL, &node_table[ 92], &node_table[ 92],
+ "haMobilityBindingRegFlags", 4, COLUMN, (void *) &column_table[35] },
+/* 92 */ { &node_table[ 87], NULL, &node_table[ 93], &node_table[ 93],
+ "haMobilityBindingRegIDLow", 5, COLUMN, (void *) &column_table[36] },
+/* 93 */ { &node_table[ 87], NULL, &node_table[ 94], &node_table[ 94],
+ "haMobilityBindingRegIDHigh", 6, COLUMN, (void *) &column_table[37] },
+/* 94 */ { &node_table[ 87], NULL, &node_table[ 95], &node_table[ 95],
+ "haMobilityBindingTimeGranted", 7, COLUMN, (void *) &column_table[38] },
+/* 95 */ { &node_table[ 87], NULL, NULL, &node_table[ 98],
+ "haMobilityBindingTimeRemaining", 8, COLUMN,
+ (void *) &column_table[39] },
+/* 96 */ { &node_table[ 85], &node_table[ 97], &node_table[ 104],
+ &node_table[ 98], "haCounterTable", 2, NODE, NULL },
+/* 97 */ { &node_table[ 96], &node_table[ 98], NULL, &node_table[ 98],
+ "haCounterEntry", 1, NODE, NULL },
+/* 98 */ { &node_table[ 97], NULL, &node_table[ 99], &node_table[ 99],
+ "haServiceRequestsAccepted", 2, COLUMN, (void *) &column_table[40] },
+/* 99 */ { &node_table[ 97], NULL, &node_table[ 100], &node_table[ 100],
+ "haServiceRequestsDenied", 3, COLUMN, (void *) &column_table[41] },
+/* 100 */ { &node_table[ 97], NULL, &node_table[ 101], &node_table[ 101],
+ "haOverallServiceTime", 4, COLUMN, (void *) &column_table[42] },
+/* 101 */ { &node_table[ 97], NULL, &node_table[ 102], &node_table[ 102],
+ "haRecentServiceAcceptedTime", 5, COLUMN, (void *) &column_table[43] },
+/* 102 */ { &node_table[ 97], NULL, &node_table[ 103], &node_table[ 103],
+ "haRecentServiceDeniedTime", 6, COLUMN,
+ (void *) &column_table[44] },
+/* 103 */ { &node_table[ 97], NULL, NULL, &node_table[ 104],
+ "haRecentServiceDeniedCode", 7, COLUMN, (void *) &column_table[45] },
+/* 104 */ { &node_table[ 85], NULL, &node_table[ 105], &node_table[ 105],
+ "haRegistrationAccepted", 3, OBJECT, (void *) &object_table[24] },
+/* 105 */ { &node_table[ 85], NULL, &node_table[ 106], &node_table[ 106],
+ "haMultiBindingUnsupported", 4, OBJECT, (void *) &object_table[25] },
+/* 106 */ { &node_table[ 85], NULL, &node_table[ 107], &node_table[ 107],
+ "haReasonUnspecified", 5, OBJECT, (void *) &object_table[26] },
+/* 107 */ { &node_table[ 85], NULL, &node_table[ 108], &node_table[ 108],
+ "haAdmProhibited", 6, OBJECT, (void *) &object_table[27] },
+/* 108 */ { &node_table[ 85], NULL, &node_table[ 109], &node_table[ 109],
+ "haInsufficientResource", 7, OBJECT, (void *) &object_table[28] },
+/* 109 */ { &node_table[ 85], NULL, &node_table[ 110], &node_table[ 110],
+ "haMNAuthenticationFailure", 8, OBJECT, (void *) &object_table[29] },
+/* 110 */ { &node_table[ 85], NULL, &node_table[ 111], &node_table[ 111],
+ "haFAAuthenticationFailure", 9, OBJECT, (void *) &object_table[30] },
+/* 111 */ { &node_table[ 85], NULL, &node_table[ 112], &node_table[ 112],
+ "haIDMismatch", 10, OBJECT, (void *) &object_table[31] },
+/* 112 */ { &node_table[ 85], NULL, &node_table[ 113], &node_table[ 113],
+ "haPoorlyFormedRequest", 11, OBJECT,
+ (void *) &object_table[32] },
+/* 113 */ { &node_table[ 85], NULL, &node_table[ 114], &node_table[ 114],
+ "haTooManyBindings", 12, OBJECT, (void *) &object_table[33] },
+/* 114 */ { &node_table[ 85], NULL, &node_table[ 115], &node_table[ 115],
+ "haUnknownHA", 13, OBJECT, (void *) &object_table[34] },
+/* 115 */ { &node_table[ 85], NULL, &node_table[ 116], &node_table[ 116],
+ "haGratuitiousARPsSent", 14, OBJECT, (void *) &object_table[35] },
+/* 116 */ { &node_table[ 85], NULL, &node_table[ 117], &node_table[ 117],
+ "haProxyARPsSent", 15, OBJECT, (void *) &object_table[36] },
+/* 117 */ { &node_table[ 85], NULL, &node_table[ 118], &node_table[ 118],
+ "haRegRequestsReceived", 16, OBJECT, (void *) &object_table[37] },
+/* 118 */ { &node_table[ 85], NULL, &node_table[ 119], &node_table[ 119],
+ "haDeRegRequestsReceived", 17, OBJECT, (void *) &object_table[38] },
+/* 119 */ { &node_table[ 85], NULL, &node_table[ 120], &node_table[ 120],
+ "haRegRepliesSent", 18, OBJECT, (void *) &object_table[39] },
+/* 120 */ { &node_table[ 85], NULL, NULL, NULL, "haDeRegRepliesSent", 19,
+ OBJECT, (void *) &object_table[40] },
+/* 121 */ { &node_table[ 7], &node_table[ 122], &node_table[ 124],
+ NULL, "mipMIBNotificationPrefix", 2, NODE, NULL },
+/* 122 */ { &node_table[ 121], &node_table[ 123], NULL,
+ NULL, "mipMIBNotifications", 0, NODE, NULL },
+/* 123 */ { &node_table[ 122], NULL, NULL,
+ NULL, "mipAuthFailure", 1, NODE, NULL },
+/* 124 */ { &node_table[ 7], &node_table[ 125], NULL,
+ NULL, "mipMIBConformance", 3, NODE, NULL },
+/* 125 */ { &node_table[ 124], &node_table[ 126], &node_table[ 138],
+ NULL, "mipGroups", 1, NODE, NULL },
+/* 126 */ { &node_table[ 125], NULL, &node_table[ 127],
+ NULL, "mipSystemGroup", 1, NODE, NULL },
+/* 127 */ { &node_table[ 125], NULL, &node_table[ 128],
+ NULL, "mipSecAssociationGroup", 2, NODE, NULL },
+/* 128 */ { &node_table[ 125], NULL, &node_table[ 129],
+ NULL, "mipSecViolationGroup", 3, NODE, NULL },
+/* 129 */ { &node_table[ 125], NULL, &node_table[ 130],
+ NULL, "mnSystemGroup", 4, NODE, NULL },
+/* 130 */ { &node_table[ 125], NULL, &node_table[ 131],
+ NULL, "mnDiscoveryGroup", 5, NODE, NULL },
+/* 131 */ { &node_table[ 125], NULL, &node_table[ 132],
+ NULL, "mnRegistrationGroup", 6, NODE, NULL },
+/* 132 */ { &node_table[ 125], NULL, &node_table[ 133],
+ NULL, "maAdvertisementGroup", 7, NODE, NULL },
+/* 133 */ { &node_table[ 125], NULL, &node_table[ 134],
+ NULL, "faSystemGroup", 8, NODE, NULL },
+/* 134 */ { &node_table[ 125], NULL, &node_table[ 135],
+ NULL, "faAdvertisementGroup", 9, NODE, NULL },
+/* 135 */ { &node_table[ 125], NULL, &node_table[ 136],
+ NULL, "faRegistrationGroup", 10, NODE, NULL },
+/* 136 */ { &node_table[ 125], NULL, &node_table[ 137],
+ NULL, "haRegistrationGroup", 11, NODE, NULL },
+/* 137 */ { &node_table[ 125], NULL, NULL,
+ NULL, "haRegNodeCountersGroup", 12, NODE, NULL },
+/* 138 */ { &node_table[ 124], &node_table[ 139], NULL,
+ NULL, "mipCompliances", 2, NODE, NULL },
+/* 139 */ { &node_table[ 138], NULL, NULL,
+ NULL, "mipCompliance", 1, NODE, NULL },
+/* 140 */ { &node_table[ 3], NULL, &node_table[ 141],
+ NULL, "experimental", 3, NODE, NULL },
+/* 141 */ { &node_table[ 3], &node_table[ 142], &node_table[ 151],
+ NULL, "private", 4, NODE, NULL },
+/* 142 */ { &node_table[ 141], &node_table[ 143],
+ NULL, NULL, "enterprises", 1, NODE, NULL },
+/* 143 */ { &node_table[ 142], &node_table[ 144],
+ NULL, NULL, "sun", 42, NODE, NULL },
+/* 144 */ { &node_table[ 143], &node_table[ 145],
+ NULL, NULL, "products", 2, NODE, NULL },
+/* 145 */ { &node_table[ 144], &node_table[ 146],
+ NULL, NULL, "messaging", 8, NODE, NULL },
+/* 146 */ { &node_table[ 145], &node_table[ 147], &node_table[ 150],
+ NULL, "agents", 1, NODE, NULL },
+/* 147 */ { &node_table[ 146], NULL, &node_table[ 148],
+ NULL, "snmpx400d", 1, NODE, NULL },
+/* 148 */ { &node_table[ 146], NULL, &node_table[ 149],
+ NULL, "snmpxapiad", 2, NODE, NULL },
+/* 149 */ { &node_table[ 146], NULL, NULL,
+ NULL, "snmpx500d", 3, NODE, NULL },
+/* 150 */ { &node_table[ 145], NULL, NULL,
+ NULL, "private-mibs", 2, NODE, NULL },
+/* 151 */ { &node_table[ 3], NULL, &node_table[ 152],
+ NULL, "security", 5, NODE, NULL },
+/* 152 */ { &node_table[ 3], &node_table[ 153], NULL,
+ NULL, "snmpV2", 6, NODE, NULL },
+/* 153 */ { &node_table[ 152], NULL, &node_table[ 154],
+ NULL, "snmpDomains", 1, NODE, NULL },
+/* 154 */ { &node_table[ 152], NULL, &node_table[ 155],
+ NULL, "snmpProxys", 2, NODE, NULL },
+/* 155 */ { &node_table[ 152], NULL, NULL,
+ NULL, "snmpModules", 3, NODE, NULL },
+{ NULL, NULL, NULL, NULL, NULL, 0, 0, NULL }
+};
+int node_table_size = 156;
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mipagent/thq.c b/usr/src/cmd/cmd-inet/usr.lib/mipagent/thq.c
new file mode 100644
index 0000000000..84fb3689c7
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mipagent/thq.c
@@ -0,0 +1,645 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1999-2002 Sun Microsystems, Inc.
+ * All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * file: thq.c
+ *
+ * This file contains all of the routines used for thread
+ * queue management. Thread queue management provides an
+ * interface for an application to dispatch processing of a
+ * data object to a pool of threads.
+ */
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <pthread.h>
+#include <unistd.h>
+
+#include "thq.h"
+
+#define DEFAULT_MIN_THREADS 10
+#define DEFAULT_MAX_THREADS 100
+
+/*
+ * Function: tq_alloc
+ *
+ * Arguments: action - Pointer to the function that new threads
+ * must call.
+ * end - Pointer to a function that will be called
+ * if threads are being killed (optional).
+ * arg - Pointer that will be passed as an argument
+ * to new threads created (optional).
+ * shutdown - Pointer to the shutdown flag. If this
+ * is provided, if the pointer contains a
+ * non-zero value, the thread queueing system
+ * will assume that the process is shutting
+ * down (optional).
+ * max - The maximum number of threads
+ * min - The minimum number of threads
+ *
+ * Description: This function is used to create the thread
+ * queue. The end, arg and shutdown parameters
+ * MAY be set to NULL. If the max and min are
+ * not provided, we will use our own defaults.
+ *
+ * Once the thread queue has been allocated, we
+ * will initialize it, and init the the lock and
+ * condition variables.
+ *
+ * Returns: Upon successful completion, the function will
+ * return a pointer to a thread queue, otherwise
+ * NULL
+ */
+tqTp
+tq_alloc(PFP action, PFP2 end, void *arg, int *shutdown, int max, int min,
+ boolean_t cleanup)
+{
+ tqTp queue;
+ if (action == NULL) {
+ /* no function passed, error */
+ errno = EINVAL;
+ return (NULL);
+ } /* end if */
+ /* if those parameters are not passed, take default ones */
+ if (max <= 0) max = DEFAULT_MAX_THREADS;
+ if (min <= 0) min = DEFAULT_MIN_THREADS;
+ queue = (tqTp) calloc(1, sizeof (tqT));
+ if (queue == NULL) {
+ return (NULL);
+ } /* end if */
+ if (shutdown == NULL) {
+ shutdown = (int *)calloc(1, sizeof (int));
+ if (shutdown == NULL) {
+ free(queue);
+ return (NULL);
+ } /* end if */
+ queue->shutalloc = 1;
+ }
+ if (queue != NULL && shutdown != NULL) {
+ /*
+ * The wd_mask mechanism is used to accelerate the search of
+ * a "hole" in the queue->tid[] structure whenever we needed
+ * to create a new thread and to accelerate the search of a
+ * tid in the queue->tid[] structure whenever we needed to
+ * end a thread. The previous code(i.e., wd_mask = 0) was
+ * always parsing this structure sequentially with the
+ * consequent problems:
+ *
+ * + in thr creations, "holes" where not uniformly distributed
+ * through the queue->tid[] structure BUT condensed at the end
+ * of it.
+ *
+ * + in thr_ ends, finding the thread tid associated to the
+ * thread to be ended was not optimal
+ *
+ * With the wd_mask mechanism, on the contrary:
+ *
+ * + in thr creations, the new assigned tid is itself used
+ * for the indexation on the queue->tid[] and stored at the
+ * first "hole" found from that position. If we get to
+ * queue->tid[MAX], we round in circle and come back to
+ * queue->tid[0]
+ *
+ * + in thr_deletions, the old pid is itself used for the
+ * indexation on the queue->tid[], accelerating the search.
+ *
+ * This mechanism is much faster than the old one, specially
+ * considering that, as tid assignments are(were! when not
+ * detached) sequential, if the queue->tid[] contains 256
+ * elements, by the time the thread with tid 259 will be
+ * created there are enormous chances that the thread(259-256
+ * = 3) with tid 3 has already ended, hence, finding a
+ * "hole" in the queue->tid[] structure.
+ */
+ queue->wd_mask = 0;
+ queue->doit = action;
+ queue->endit = end;
+ queue->arg = arg;
+ queue->shutdown = shutdown;
+ queue->max_thr = max;
+ queue->min_thr = min;
+ queue->cleanup = cleanup;
+
+ if ((max << 1) < 32) {
+ queue->tids = (pthread_t *)calloc(32,
+ sizeof (pthread_t));
+ queue->wd_mask = 0x1F;
+ } else if ((max << 1) < 64) {
+ queue->tids = (pthread_t *)calloc(64,
+ sizeof (pthread_t));
+ queue->wd_mask = 0x3F;
+ } else if ((max << 1) < 128) {
+ queue->tids = (pthread_t *)calloc(128,
+ sizeof (pthread_t));
+ queue->wd_mask = 0x7F;
+ } else if ((max << 1) < 256) {
+ queue->tids = (pthread_t *)calloc(256,
+ sizeof (pthread_t));
+ queue->wd_mask = 0xFF;
+ } else if ((max << 1) < 512) {
+ queue->tids = (pthread_t *)calloc(512,
+ sizeof (pthread_t));
+ queue->wd_mask = 0x01FF;
+ } else if ((max << 1) < 1024) {
+ queue->tids = (pthread_t *)calloc(1024,
+ sizeof (pthread_t));
+ queue->wd_mask = 0x03FF;
+ } else if ((max << 1) < 2048) {
+ queue->tids = (pthread_t *)calloc(2048,
+ sizeof (pthread_t));
+ queue->wd_mask = 0x07FF;
+ } else if (max < 4096) {
+ queue->tids = (pthread_t *)calloc(4096,
+ sizeof (pthread_t));
+ queue->wd_mask = 0x0FFF;
+ } else if (max < 8192) {
+ queue->tids = (pthread_t *)calloc(8192,
+ sizeof (pthread_t));
+ queue->wd_mask = 0x1FFF;
+ } else if (max < 16384) {
+ queue->tids = (pthread_t *)calloc(16384,
+ sizeof (pthread_t));
+ queue->wd_mask = 0x3FFF;
+ } else {
+ queue->tids = (pthread_t *)calloc(max,
+ sizeof (pthread_t));
+ }
+
+ if (queue->tids == NULL) {
+ if (queue->shutalloc) {
+ free(shutdown);
+ } /* end if */
+ free(queue);
+ return (NULL);
+ } /* end if */
+ (void) pthread_mutex_init(&(queue->lock), NULL);
+ (void) pthread_cond_init(&(queue->cond), NULL);
+ } /* end if */
+ return (queue);
+}
+
+
+/*
+ * Function: dump_queue
+ *
+ * Arguments: queue - Pointer to the thread queue
+ *
+ * Description: This function is provided for debugging purposes
+ * but is not currently used in the code, and will
+ * print out the contents of the thread queue.
+ *
+ * Returns:
+ */
+#ifndef lint
+void
+dump_queue(tqTp queue)
+{
+ tq_listTp cur;
+ (void) printf("first %p, last %p, ", (void *)queue->first,
+ (void *)queue->last);
+ for (cur = queue->first; cur; cur = cur->next) {
+ (void) printf("%p:%p ", (void *)cur, (void *)cur->arg);
+ } /* end for */
+ (void) printf("\n");
+}
+#endif
+
+/*
+ * Function: timedthread
+ *
+ * Arguments: arg - Pointer to an argument that will be
+ * provided to the new thread.
+ *
+ * Description: This strange function will sleep for 4 seconds
+ * before calling the thread callback routine.
+ *
+ * Returns: should never return.
+ */
+static void
+timedthread(void * arg)
+{
+ tqTp queue = (tqTp)arg;
+
+ (void) sleep(4);
+ (void) (*(queue->doit))(queue->arg);
+}
+
+/*
+ * Function: create_new_thread
+ *
+ * Arguments: queue - Pointer to the thread queue
+ * timer - Determines whether we need to
+ * wait before we call the new
+ * thread.
+ *
+ * Description: This function is used to create a new
+ * thread. If the number of threads waiting
+ * is smaller than the number of items in
+ * our queue, and we have not reached our
+ * maximum number of threads, a new thread
+ * will be created.
+ *
+ * Returns: int, 0 if successful
+ */
+static int
+create_new_thread(tqTp queue, int timer)
+{
+ int i;
+ pthread_t tid;
+
+ if (queue->thr_waiting < queue->queue_size &&
+ queue->nb_thr < queue->max_thr) {
+ /* create a thread to manage this request */
+ ++(queue->nb_thr);
+ if (timer) {
+ if (pthread_create(&tid, NULL,
+ (void *(*)())timedthread, queue) != 0) {
+ return (-1);
+ } /* end if */
+ } /* end if */
+ else if (pthread_create(&tid, NULL,
+ (void *(*)())queue->doit, queue->arg) != 0) {
+ return (-1);
+ } /* end if */
+
+ if (pthread_detach(tid)) {
+ /*
+ * Unable to detach the thread, let's kill it.
+ */
+ (void) pthread_cancel(tid);
+ }
+
+ /*
+ * put the thread id into the saved array for the
+ * watchdog mechanism
+ */
+ if (queue->wd_mask) {
+ int htid = tid & queue->wd_mask;
+ for (i = htid; i < queue->wd_mask; ++i) {
+ if (queue->tids[i] == 0) {
+ queue->tids[i] = tid;
+ break;
+ } /* end if */
+ } /* end for */
+ if (i == queue->wd_mask) {
+ for (i = 0; i < htid; ++i) {
+ if (queue->tids[i] == 0) {
+ queue->tids[i] = tid;
+ break;
+ } /* end if */
+ } /* end for */
+ }
+ } else {
+ for (i = 0; i < queue->max_thr; ++i) {
+ if (queue->tids[i] == 0) {
+ queue->tids[i] = tid;
+ break;
+ } /* end if */
+ } /* end for */
+ }
+ } /* end if */
+ return (0);
+}
+
+/*
+ * Function: tq_queue
+ *
+ * Arguments: queue - Pointer to the thread queue
+ * arg - Pointer to object to be processed
+ *
+ * Description: queue an action to be done in the arg queue
+ * and signal any waiting thread that something
+ * is to be processed. If we notice that we've
+ * reached our maximum number of threads, and no
+ * threads are in the waiting state, we will
+ * make sure that all threads stil exist.
+ *
+ * if arg is null, special case, just send the signal
+ * (probably in case of shutdown)
+ *
+ * Returns: int, 0 if successful
+ */
+int
+tq_queue(tqTp queue, void *arg)
+{
+ tq_listTp cur;
+ int i;
+ static int times_called = 0;
+ static int gc = 0;
+
+ if (!gc) {
+ if ((++times_called) >= (queue->max_thr >> 1)) {
+ times_called = 0;
+ gc = 1;
+ }
+ }
+
+ if (queue == NULL) {
+ return (0);
+ } /* end if */
+ if (*(queue->shutdown) || queue->stopping) {
+ /*
+ * shutdown condition, broadcast to all the threads waiting to
+ * terminate them
+ * and refuse new stuff in the queue
+ */
+ (void) pthread_cond_broadcast(&(queue->cond));
+ return (-1);
+ }
+ if (arg) {
+ if ((cur = (tq_listTp)calloc(1, sizeof (tq_listT))) == NULL) {
+ return (-1);
+ } /* end if */
+ cur->arg = arg;
+ (void) pthread_mutex_lock(&(queue->lock));
+ if (queue->last) {
+ queue->last->next = cur;
+ } /* end if */
+ queue->last = cur;
+ cur->next = 0;
+ if (queue->first == NULL) {
+ queue->first = cur;
+ } /* end if */
+ ++(queue->queue_size);
+ if (queue->thr_waiting == 0 &&
+ queue->nb_thr == queue->max_thr && gc) {
+ /*
+ * Since it is possible that some threads have exited
+ * by themselves, if we reach the maximum number of
+ * threads, we will kill with signal 0, which simply
+ * ensures that the thread is still valid. If the
+ * thread is no longer valid, we will free the entry
+ * in the queue.
+ */
+ int max;
+ gc = 0;
+
+ max = (queue->wd_mask) ? queue->wd_mask :
+ queue->max_thr;
+
+ for (i = 0; i < max; ++i) {
+ if (queue->tids[i] != 0) {
+ if (pthread_kill(queue->tids[i], 0)) {
+ queue->tids[i] = 0;
+ --(queue->nb_thr);
+ } /* end if */
+ } /* end if */
+ } /* end for */
+ } /* end if */
+ /*
+ * if size of the queue > nb of thread waiting and we
+ * didn't reach the maximum number of threads, create a
+ * new one
+ */
+ if (create_new_thread(queue, 0)) {
+ return (-1);
+ }
+ } /* end if */
+ else
+ (void) pthread_mutex_lock(&(queue->lock));
+ /* just signal one of the waiting threads */
+ (void) pthread_cond_signal(&(queue->cond));
+ (void) pthread_mutex_unlock(&(queue->lock));
+ return (0);
+}
+
+/*
+ * Function: tq_end_thread
+ *
+ * Arguments: queue - Pointer to the thread queue
+ * endit_arg - parameter to be passed to
+ * the thread shutdown function.
+ *
+ * Description: This function is called when a thread
+ * needs to be shutdown. if a termination
+ * function was provided during tq_alloc(),
+ * we will call it with the argument provided.
+ *
+ * If the shutdown flag is not set, we will
+ * create another thread, otherwise we will
+ * send a broadcast that we are shutting down.
+ *
+ * Returns:
+ */
+static void
+tq_end_thread(tqTp queue, void * endit_arg)
+{
+ pthread_t tid = pthread_self();
+ int i;
+ /*
+ * call the finish function of the thread
+ */
+ if (queue->endit) {
+ (void) (*(queue->endit))(queue->arg, endit_arg);
+ } /* end if */
+
+ if (queue->wd_mask) {
+ int htid = tid & queue->wd_mask;
+ for (i = htid; i < queue->wd_mask; ++i) {
+ if (queue->tids[i] == tid) {
+ queue->tids[i] = 0;
+ break;
+ } /* end if */
+ } /* end for */
+ if (i == queue->wd_mask) {
+ for (i = 0; i < htid; ++i) {
+ if (queue->tids[i] == tid) {
+ queue->tids[i] = 0;
+ break;
+ } /* end if */
+ } /* end for */
+ }
+ } else {
+ for (i = 0; i < queue->max_thr; ++i) {
+ if (queue->tids[i] == tid) {
+ queue->tids[i] = 0;
+ break;
+ } /* end if */
+ } /* end for */
+ }
+
+ /*
+ * It is possible that while we started to shutdown, a new item
+ * showed up on the queue. By calling create_new_thread() we
+ * will create such a thread, only if necessary.
+ */
+ if (!*(queue->shutdown))
+ (void) create_new_thread(queue, 1);
+ /*
+ * and terminate the thread
+ */
+ if (*(queue->shutdown)) {
+ /* shutdown condition, warn the waiting function if any */
+ (void) pthread_cond_broadcast(&(queue->cond));
+ }
+ /*
+ * update number of active threads
+ */
+ --(queue->thr_waiting);
+ --(queue->nb_thr);
+ /*
+ * unlock the queue
+ */
+ (void) pthread_mutex_unlock(&(queue->lock));
+ pthread_exit(0);
+} /* end static void tq_end_thread */
+
+/*
+ * Function: tq_dequeue
+ *
+ * Arguments: queue - Pointer to the thread queue
+ * endit_arg - parameter to be passed to
+ * the thread shutdown function.
+ *
+ * Description: This function is called by the threads
+ * to retrieve an object to process. If
+ * the caller ends up waiting for 30 seconds
+ * without processing anything, we will
+ * terminate the thread.
+ *
+ * Returns: a pointer to an object to process taken
+ * from the queue.
+ */
+void *
+tq_dequeue(tqTp queue, void * endit_arg)
+{
+ tq_listTp cur;
+ void * arg;
+
+ if (queue == NULL) {
+ return (NULL);
+ } /* end if */
+ (void) pthread_mutex_lock(&(queue->lock));
+ ++(queue->thr_waiting);
+ /* dump_queue(queue); */
+ while (!*(queue->shutdown)) {
+ /*
+ * if something in the queue, dequeue it and return the
+ * action to be done
+ */
+ if (queue->first) {
+ cur = queue->first;
+ queue->first = queue->first->next;
+ if (queue->first == NULL) {
+ queue->last = NULL;
+ if (queue->stopping) {
+ (void) pthread_cond_broadcast(
+ &(queue->cond));
+ } /* end if */
+ } /* end if */
+ --(queue->queue_size);
+ --(queue->thr_waiting);
+ (void) pthread_mutex_unlock(&(queue->lock));
+ arg = cur->arg;
+ free(cur);
+ return (arg);
+ } else {
+ timestruc_t to;
+ int rc;
+ to.tv_sec = 30;
+ to.tv_nsec = 0;
+ /*
+ * wait for the condition arg_loop to be set
+ * or a signal occurs, or a timeout
+ */
+ rc = pthread_cond_reltimedwait_np(&(queue->cond),
+ &(queue->lock), &to);
+ if ((rc == ETIME || rc == ETIMEDOUT) &&
+ queue->nb_thr > queue->min_thr &&
+ queue->cleanup == _B_TRUE) {
+ /*
+ * we don't want to keep this thread alive
+ * call shutdown function, release the lock
+ * and exit
+ */
+ tq_end_thread(queue, endit_arg);
+ }
+ } /* end if */
+ } /* end while */
+ /*
+ * shutdown condition
+ *
+ * end the thread
+ * call shutdown function, release the lock and exit
+ */
+ tq_end_thread(queue, endit_arg);
+ return (NULL); /* never called, just here for the compiler */
+}
+
+/*
+ * Function: tq_shutdown
+ *
+ * Arguments: queue - Pointer to the thread queue
+ * immediate - shutdown immediately flag
+ *
+ * Description: This function is called to free the thread
+ * queue, and to kill all threads that were
+ * allocated as a result of the creation of the
+ * thread queue. If the immediate flag is set
+ * to non-zero, we will kill all threads
+ * immediately, otherwise we will wait for all
+ * threads to shutdown.
+ *
+ * Returns:
+ */
+void
+tq_shutdown(tqTp queue, int immediate)
+{
+ timestruc_t to;
+ (void) pthread_mutex_lock(&(queue->lock));
+ queue->stopping = 1;
+ if (!immediate) {
+ while (queue->first != NULL && *(queue->shutdown) == 0) {
+ to.tv_sec = 1;
+ to.tv_nsec = 0;
+ (void) pthread_cond_reltimedwait_np(&(queue->cond),
+ &(queue->lock),
+ &to);
+ }
+ } /* end if */
+
+ /*
+ * shutdown condition, broadcast to all the threads waiting to
+ * terminate them
+ */
+ *(queue->shutdown) = 1;
+ while (queue->nb_thr > 0) {
+ (void) pthread_cond_broadcast(&(queue->cond));
+ to.tv_sec = 1;
+ to.tv_nsec = 0;
+ (void) pthread_cond_reltimedwait_np(&(queue->cond),
+ &(queue->lock), &to);
+ } /* end while */
+ free(queue->tids);
+ if (queue->shutalloc) {
+ free(queue->shutdown);
+ } /* end if */
+ free(queue);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mipagent/thq.h b/usr/src/cmd/cmd-inet/usr.lib/mipagent/thq.h
new file mode 100644
index 0000000000..90c190ece7
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mipagent/thq.h
@@ -0,0 +1,111 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _THQ_H
+#define _THQ_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * This file contains the definitions for the thread
+ * management module (thq.h).
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <pthread.h>
+
+typedef void * (*PFP)(void *);
+
+typedef void * (*PFP2)(void *, void *);
+
+typedef struct tq_listS {
+ void * arg;
+ struct tq_listS *next;
+} tq_listT, * tq_listTp;
+
+typedef struct {
+ tq_listTp first; /* first element in the queue */
+ tq_listTp last; /* last element in the queue */
+ pthread_mutex_t lock; /* queue mutex */
+ pthread_cond_t cond; /* queue condition to signal new elements */
+ int *shutdown; /* variable to test for shutdown condition */
+ int shutalloc; /* was shutdown variable allocated locally */
+ int stopping; /* queue is currently stopping */
+ int queue_size; /* current size of the queue */
+ int nb_thr; /* current nb of threads pocessing the queue */
+ int thr_waiting; /* current nb of threads waiting */
+ int max_thr; /* max allowed threads to process the queue */
+ int min_thr; /* min nb of threads to keep alive */
+ int wd_mask; /* Watchdog mask */
+ boolean_t cleanup; /* Will we kill inactive threads */
+ PFP doit; /* function to call to process the queue */
+ PFP2 endit; /* function called before to end the thread */
+ void *arg; /* argument to pass to the doit/endit func. */
+ pthread_t *tids; /* array of thread ids for watchdog */
+} tqT, * tqTp;
+
+extern tqTp tq_alloc(PFP, /* function to process the queue */
+ PFP2, /* function called before to end */
+ void *, /* arg passed to both functions */
+ int *, /* shutdown variable to test */
+ int, /* max allowed threads */
+ int, /* min allowed threads */
+ boolean_t); /* If TRUE all inactive threads are killed */
+
+extern int tq_queue(tqTp, /* pointer to the queue */
+ void *); /* element to be queued */
+
+/*
+ * tq_dequeue returns the first "arg" passed to tq_queue
+ */
+extern void * tq_dequeue(tqTp, /* pointer to the queue */
+ void *); /* pointer to "shutdown" arguments */
+
+/*
+ * tq_shutdown, shutdown the queue (alternate way to shutdown if you don't
+ * have a global shutdown integer
+ *
+ * shutdown can be immediate (1) or delayed until there is nothing more
+ * in the queue (immediate = 0)
+ *
+ * when you call this function, no more argument can be queued using
+ * tq_queue.
+ *
+ * when tq_dequeue returns, the queue pointer is not allocated anymore
+ *
+ */
+extern void tq_shutdown(tqTp, /* pointer to the queue */
+ int); /* 1: don't wait, 0: wait for queue */
+ /* to be empty */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _THQ_H */
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mipagent/utils.c b/usr/src/cmd/cmd-inet/usr.lib/mipagent/utils.c
new file mode 100644
index 0000000000..c5e08f525a
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mipagent/utils.c
@@ -0,0 +1,913 @@
+/*
+ * Copyright (c) 1999-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+/*
+ * Copyright (c) 1987 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * file: utils.c
+ *
+ * This file contains the miscellaneous routines
+ * that don't seem to belong anywhere else, such
+ * as randomizers, address conversion, character
+ * manipulation, etc.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/time.h>
+#include <syslog.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <ctype.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include "mip.h"
+#include "agent.h"
+#include "auth.h"
+
+#define SECONDS_IN_A_DAY 86400
+#define NTP_ADJUSTMENT (SECONDS_IN_A_DAY *(((uint32_t)365*70) + 17))
+
+#define _POSIX_THREAD_SAFE_FUNCTIONS 1
+
+extern int logVerbosity;
+
+/* --------------------- debug utils ------------------------- */
+/*
+ * Function: hexdump
+ *
+ * Arguments: message, buffer, length
+ *
+ * Description: This function will dump out a buffer to stderr in hex and
+ * ascii. It is usefull in debugging.
+ *
+ * Returns: void
+ *
+ */
+int
+hexdump(char *message, unsigned char *buffer, int length)
+{
+ int i;
+ char text[17];
+ char TmpBuf[30];
+ char output[200];
+ int offset = 0;
+ int currBytes;
+ int result;
+ static pthread_mutex_t hexdumpMutex = PTHREAD_MUTEX_INITIALIZER;
+
+ if (!length) {
+ /* don't waste our time with empty buffers */
+ return (0);
+ }
+
+ result = pthread_mutex_lock(&hexdumpMutex);
+ if (result) {
+ (void) fprintf(stderr, "Hexdump: Unable to unlock mutex");
+ return (-1);
+ }
+
+ text[16] = 0; /* Null our buffer, so we only have to do it once */
+ output[0] = 0;
+
+ (void) fprintf(stderr, "%s:\n", message);
+
+ /* for each line . . */
+ if (length <= 16)
+ currBytes = length;
+ else
+ currBytes = 16;
+ length -= currBytes;
+
+ while (currBytes > 0) {
+ (void) sprintf(TmpBuf, "0x%08x: ", offset);
+ (void) strcat(output, TmpBuf);
+
+ for (i = 0; i < currBytes; i++) {
+ (void) sprintf(TmpBuf, "%02x ", buffer[offset+i]);
+ (void) strcat(output, TmpBuf);
+ if (i == 7)
+ (void) strcat(output, " ");
+ text[i] = isprint(buffer[offset+i]) ?
+ buffer[offset + i] : '.';
+ }
+
+ for (i = currBytes; i < 16; i++) {
+ (void) strcat(output, " ");
+ if (i == 7)
+ (void) strcat(output, " ");
+ text[i] = ' ';
+ }
+
+ (void) strcat(output, "| ");
+ (void) strcat(output, text);
+ (void) fprintf(stderr, "%s\n", output);
+ output[0] = 0;
+ offset += currBytes;
+
+ if (length <= 16)
+ currBytes = length;
+ else
+ currBytes = 16;
+ length -= currBytes;
+ }
+
+ result = pthread_mutex_unlock(&hexdumpMutex);
+ if (result) {
+ (void) fprintf(stderr, "Hexdump: Unable to unlock mutex");
+ }
+
+ return (0);
+} /* hexdump */
+
+
+
+/* --------------------- Platform-specific utilities -------------------- */
+
+/*
+ * Function: randomInit
+ *
+ * Arguments:
+ *
+ * Description: initialize the random number generator
+ *
+ * Returns:
+ */
+void
+randomInit()
+{
+ struct timeval time;
+
+ if (gettimeofday(&time, 0) < 0) {
+ syslog(LOG_INFO,
+ "Gettimeofday failed in randominit(), using fixed seed.");
+ srand48(181067);
+ } else {
+ srand48(time.tv_sec);
+ }
+}
+
+
+/*
+ * Function: randomLong
+ *
+ * Arguments:
+ *
+ * Description: This function is used to retrieve a random
+ * 32-bit value.
+ *
+ * Returns: random value
+ */
+uint32_t
+getRandomValue()
+{
+ return ((unsigned long) lrand48());
+}
+
+/*
+ * Function: CurrentTimeNTPSec
+ *
+ * Arguments:
+ *
+ * Description: Returns the number of seconds elapsed since
+ * Jan 1, 1990. Only the higher 32 bits of the 64-bit
+ * quantity representing current time in NTP format.
+ *
+ * Returns: uint32_t - the number of seconds since Jan 1, 1990.
+ */
+uint32_t
+CurrentTimeNTPSec()
+{
+ struct timeval time;
+
+ if (gettimeofday(&time, 0) < 0) {
+ syslog(LOG_ERR,
+ "Error: gettimeofday failed in CurrentTimeNTPSec()");
+ return (0);
+ } else {
+ return ((uint32_t)(time.tv_sec + NTP_ADJUSTMENT));
+ }
+}
+
+
+/*
+ * Function: sprintTime
+ *
+ * Arguments: buffer - Pointer to output buffer
+ * buflen - Length of output buffer
+ *
+ * Description: Print the current time in buffer. If buffer is large
+ * enough, the current time is printed in the format:
+ * Fri Sep 13 00:00:00 1986\0
+ *
+ * This function is used for debugging purposes only.
+ *
+ * Returns: If successful, a pointer to the buffer is returned, otherwise
+ * NULL is returned.
+ */
+char *
+sprintTime(char *buffer, int buflen)
+{
+ struct timeval clock;
+
+ if (buflen >= 26) {
+ (void) gettimeofday(&clock, 0);
+#ifndef lint
+ /* Lint has a problem picking the right one */
+#ifdef _POSIX_THREAD_SAFE_FUNCTIONS
+ ctime_r((time_t *)&(clock.tv_sec), buffer);
+#else
+ ctime_r((time_t *)&(clock.tv_sec), buffer, buflen);
+#endif /* _POSIX_THREAD_SAFE_FUNCTIONS */
+#endif /* lint */
+ buffer[24] = '\0';
+ return (buffer);
+ } else {
+ return ("");
+ }
+}
+
+
+/*
+ * Function: sprintRelativeTime
+ *
+ * Arguments: buffer - Pointer to output buffer
+ * buflen - Length of output buffer
+ *
+ * Description: Print relative time since first call to this routine
+ * in the form:
+ * seconds.xxxxxx\0
+ *
+ * This function is used for debugging purposes only.
+ *
+ * Returns: If successful, a pointer to the buffer is returned, otherwise
+ * NULL is returned.
+ */
+char
+*sprintRelativeTime(char *buffer, int buflen)
+{
+ static int i = 0;
+ static struct timeval starttime;
+ static struct timeval now;
+ time_t diff_sec;
+ useconds_t diff_usec;
+
+ if (buflen < 20) {
+ return ("");
+ }
+
+ if (i == 0) {
+ (void) gettimeofday(&starttime, 0);
+ (void) sprintf(buffer, "%d.%06d", 0, 0);
+ i = 1;
+ } else {
+ (void) gettimeofday(&now, 0);
+ diff_sec = now.tv_sec - starttime.tv_sec;
+ if (now.tv_usec < starttime.tv_usec) {
+ diff_usec = 1000000 + now.tv_usec - starttime.tv_usec;
+ diff_sec -= 1;
+ } else {
+ diff_usec = now.tv_usec - starttime.tv_usec;
+ }
+ (void) sprintf(buffer, "%ld.%06d", diff_sec, diff_usec);
+ }
+
+ return (buffer);
+}
+
+
+/* --------------------- Platform-independent utilities -------------------- */
+
+/*
+ * Function: inChecksum
+ *
+ * Arguments: addr - Pointer to address
+ * len - length of address
+ *
+ * Description: Compute the internet checksum for len number of
+ * bytes starting at addr
+ *
+ * Returns: a unsigned short containing the checksum.
+ */
+unsigned short
+inChecksum(unsigned short *addr, int len)
+{
+ register int nleft = len;
+ register unsigned short *w = addr;
+ register unsigned short answer;
+ unsigned short odd_byte = 0;
+ register int sum = 0;
+
+ /*
+ * Our algorithm is simple, using a 32 bit accumulator (sum),
+ * we add sequential 16 bit words to it, and at the end, fold
+ * back all the carry bits from the top 16 bits into the lower
+ * 16 bits.
+ */
+ while (nleft > 1) {
+ sum += *w++;
+ nleft -= 2;
+ }
+
+ /* mop up an odd byte, if necessary */
+ if (nleft == 1) {
+ *(unsigned char *)(&odd_byte) = *(unsigned char *)w;
+ sum += odd_byte;
+ }
+
+ /*
+ * add back carry outs from top 16 bits to low 16 bits
+ */
+ sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
+ sum += (sum >> 16); /* add carry */
+ answer = ~sum; /* truncate to 16 bits */
+ return (answer);
+}
+
+
+/*
+ * Function: hwAddrWrite
+ *
+ * Arguments: hwaddr - Pointer to the string containing
+ * the MAC address
+ * hwStr - Pointer to the output buffer
+ *
+ * Description: This function will convert a MAC address
+ * to a text format (e.g. aa:bb:cc:dd:ee:ff).
+ *
+ * Returns: a pointer to the output buffer
+ */
+char *
+hwAddrWrite(unsigned char hwaddr[], char *hwStr) {
+
+ (void) sprintf(hwStr, "%x:%x:%x:%x:%x:%x",
+ (unsigned char) hwaddr[0],
+ (unsigned char) hwaddr[1],
+ (unsigned char) hwaddr[2],
+ (unsigned char) hwaddr[3],
+ (unsigned char) hwaddr[4],
+ (unsigned char) hwaddr[5]);
+
+ return (hwStr);
+}
+
+
+/*
+ * Function: ntoa
+ *
+ * Arguments: addr_long - Address
+ * addr_string - Pointer to the output buffer
+ *
+ * Description: Converts the long value in addr_long to an internet
+ * address of the type a.b.c.d in addr_string and
+ * returns a pointer to addr_string.
+ *
+ * NOTE: addr_long is in network byte order and
+ * addr_string is assumed to be long enough (at
+ * least INET_ADDRSTRLEN in length).
+ *
+ * By having the caller supply its own buffer, we try
+ * to avoid problems associated with multiple threads
+ * writing the same buffer concurrently.
+ *
+ * Returns: the pointer to the output buffer
+ */
+char *
+ntoa(uint32_t addr_long, char *addr_string)
+{
+ uint32_t temp;
+
+ temp = ntohl(addr_long);
+ (void) sprintf(addr_string, "%d.%d.%d.%d",
+ ((temp >> 24) & 0xff), ((temp >> 16) & 0xff),
+ ((temp >> 8) & 0xff), (temp & 0xff));
+ return (addr_string);
+}
+
+
+
+
+/*
+ * Function: hexDigit
+ *
+ * Arguments: c - character
+ *
+ * Description: This function is used to determine if a character
+ * is a valid hex digit, and returns the decimal
+ * equivalent.
+ *
+ * Returns: int, -1 if the character is not a valid hex digit.
+ * Otherwise, the decimal value is returned.
+ */
+static int
+hexDigit(int c)
+{
+ if (c >= '0' && c <= '9')
+ return (c - '0');
+ if (c >= 'a' && c <= 'f')
+ return (c - 'a' + 10);
+ if (c >= 'A' && c <= 'F')
+ return (c - 'A' + 10);
+
+ return (-1);
+}
+
+
+/*
+ * Function: hexConvert
+ *
+ * Arguments: key - Pointer to output buffer
+ * len - length of key
+ * keystr - Pointer to input buffer containing the key.
+ *
+ * Description: Convert a string of hexadecimal digits into an array
+ * of len bytes each containing a byte represented by a
+ * pair of hex digits, e.g. keystr="12abC4De" is converted
+ * to key[]={0x12, 0xab, 0xC4, 0xDe} key has space for
+ * len bytes only.
+ *
+ * Returns: int, Returns -1 if the key has invalid data
+ */
+int
+hexConvert(char *key, int len, char *keystr)
+{
+ int i, d1, d2;
+
+ for (i = 0; i < len; ++i) {
+ if ((d1 = hexDigit(*keystr++)) == -1)
+ return (-1);
+ if ((d2 = hexDigit(*keystr++)) == -1)
+ return (-1);
+ *key++ = d1*16 + d2;
+ }
+
+ return (0);
+}
+
+
+/*
+ * Function: prefixLen
+ *
+ * Arguments: netmask - Network Mask
+ *
+ * Description: Computes prefix length of a network mask. Assumes
+ * it is the same as the number of bits set to one.
+ *
+ * Returns: int, containing the netmask bits.
+ */
+int
+prefixLen(uint32_t netmask)
+{
+ int len = 0;
+
+ while (netmask) {
+ len++;
+ netmask &= (netmask - 1);
+ }
+
+ return (len);
+}
+
+/*
+ * Function: printBuffer
+ *
+ * Arguments: buffer - Pointer to input buffer
+ * buflen - Length of data in buffer
+ *
+ * Description: Prints a buffer in hexadecimal
+ *
+ * Returns:
+ */
+void
+printBuffer(unsigned char buffer[], int buflen)
+{
+ unsigned int i;
+
+ if (buflen == 6) {
+ /* Is likely an ethernet address */
+ for (i = 0; i < buflen; i++) {
+ mipverbose(("%02x", buffer[i]));
+ if (i < (buflen-1))
+ mipverbose((":"));
+ }
+ } else {
+ /* Short enough to be printed in one shot */
+ if (buflen <= 16) {
+ for (i = 0; i < buflen; i++)
+ mipverbose(("%02x", buffer[i]));
+ } else {
+ /* Possibly a packet */
+ mipverbose(("\t"));
+ for (i = 0; i < buflen; i++) {
+ mipverbose(("%02x", buffer[i]));
+ if ((i % 2) == 1)
+ mipverbose((" "));
+ if ((i % 16) == 15)
+ mipverbose(("\n\t"));
+ }
+ }
+ }
+
+ (void) fflush(stdout);
+}
+
+
+/*
+ * Function: printMaAdvConfigEntry
+ *
+ * Arguments: macep - Pointer to the interface entry.
+ * p1 - unused
+ *
+ * Description: Prints the contents of a MaAdvConfigEntry
+ *
+ * Returns:
+ */
+/* ARGSUSED */
+static boolean_t
+printMaAdvConfigEntry(void *entry, uint32_t p1)
+{
+ MaAdvConfigEntry *macep = entry;
+ char addr[20];
+
+ mipverbose(("MaAdvConfigEntry contents:\n"));
+ mipverbose(("\tInterface: %s\n", macep->maIfaceName));
+ mipverbose(("\tAddress : %s\n", ntoa(macep->maIfaceAddr, addr)));
+ mipverbose(("\tNetmask : %s\n", ntoa(macep->maIfaceNetmask, addr)));
+ if ((macep->maIfaceFlags & IFF_POINTOPOINT) == 0) {
+ mipverbose(("\tHWaddr : "));
+ if (logVerbosity > 2) printBuffer(macep->maIfaceHWaddr, 6);
+ mipverbose(("\n"));
+ }
+ mipverbose(("\tAdv seq# : %d\n", macep->maAdvSeqNum));
+ mipverbose(("\tFlags : %x (RBHFMGV_)\n", macep->maAdvServiceFlags));
+ mipverbose(("\tPrefix : %s\n",
+ macep->maAdvPrefixLenInclusion ? "Yes" : "No"));
+
+ return (_B_TRUE);
+}
+
+#ifdef FIREWALL_SUPPORT
+/*
+ * Function: printProtectedDomainInfo
+ *
+ * Arguments: domainInfo - Pointer to the Domain Info
+ * entry.
+ *
+ * Description: Prints information about a DomainInfo
+ *
+ * Returns:
+ */
+void
+printProtectedDomainInfo(DomainInfo domainInfo)
+{
+ int i;
+ char addrstr1[INET_ADDRSTRLEN], addrstr2[INET_ADDRSTRLEN];
+
+ if (domainInfo.addrIntervalCnt > 0) {
+ mipverbose(("Protected Domain Info: Address - Netmask pairs\n"));
+ for (i = 0; i < domainInfo.addrIntervalCnt; i++) {
+ mipverbose((" %s/%s\n",
+ ntoa(domainInfo.addr[i], addr1),
+ ntoa(domainInfo.netmask[i], addr2)));
+ }
+ }
+
+ if (domainInfo.firewallCnt > 0) {
+ mipverbose(("No. of firewalls : %d\n", domainInfo.firewallCnt));
+ mipverbose(("List of firewall addresses : "));
+ for (i = 0; i < domainInfo.firewallCnt; i++) {
+ mipverbose(("%s ", ntoa(domainInfo.fwAddr[i], addr1)));
+ }
+ }
+
+ mipverbose(("\n"));
+ fflush(stdout);
+}
+#endif /* FIREWALL_SUPPORT */
+
+/*
+ * Function: printMaAdvConfigHash
+ *
+ * Arguments: htbl - Pointer to the Hash Table
+ *
+ * Description: This function will print information about
+ * all interfaces in the hash.
+ *
+ * Returns:
+ */
+void
+printMaAdvConfigHash(struct hash_table *htbl)
+{
+
+ mipverbose(("------ MaAdvConfigHash contents ------\n"));
+
+ getAllHashTableEntries(htbl, printMaAdvConfigEntry, LOCK_READ, 0,
+ _B_FALSE);
+}
+
+#if 0
+/*
+ * Function: printFaVisitorEntry
+ *
+ * Arguments: favep - Pointer to the visitor entry
+ *
+ * Description: Prints the contents of a FaVisitorEntry. This
+ * function is not currently in use today, but will
+ * if static visitor entries are implemented.
+ *
+ * Returns:
+ */
+/* ARGSUSED */
+static boolean_t
+printFaVisitorEntry(void *entry, uint32_t p1)
+{
+ FaVisitorEntry *favep = entry;
+ time_t currentTime;
+ char addr[INET_ADDRSTRLEN];
+ struct ether_addr ether;
+
+ mipverbose(("FaVisitorEntry contents:\n"));
+ mipverbose(("\tVisitor : %s\n", ntoa(favep->faVisitorAddr, addr)));
+ mipverbose(("\tIface : %s\n",
+ ntoa(favep->faVisitorIfaceAddr, addr)));
+ mipverbose(("\tStatus : %s\n", (favep->faVisitorRegIsAccepted ?
+ "Accepted" : "Pending")));
+ mipverbose(("\tTimeGrant: %ld\n", favep->faVisitorTimeGranted));
+ GET_TIME(currentTime);
+ mipverbose(("\tTimeLeft : %ld\n",
+ currentTime - favep->faVisitorTimeExpires));
+ mipverbose(("\tHomeAddr : %s\n", ntoa(favep->faVisitorHomeAddr, addr)));
+ mipverbose((
+ "\tHomeAgent: %s\n", ntoa(favep->faVisitorHomeAgentAddr, addr)));
+ mipverbose(("\tCOAddr : %s\n", ntoa(favep->faVisitorCOAddr, addr)));
+ mipverbose(("\tReg Flag : %x (SBDMGV__)\n", favep->faVisitorRegFlags));
+ mipverbose(("\tID High : %x\n", favep->faVisitorRegIDHigh));
+ mipverbose(("\tID Low : %x\n", favep->faVisitorRegIDLow));
+ mipverbose(("\tIf. idx : %d\n", favep->faVisitorInIfindex));
+ if (faevp->faVisitorSlla.sdl_data != NULL) {
+ (void) memcpy(ether.ether_addr_octet,
+ faevp->faVisitorSlla.sdl_data, ETHERADDRL);
+ mipverbose(("\tMN SLLA : %s\n", ether_ntoa(&ether)));
+ } else
+ mipverbose(("\tMN SLLA : unknown\n"));
+ return (_B_TRUE);
+}
+
+
+/*
+ * Function: printFaVisitorHash
+ *
+ * Arguments: htbl - Pointer to the Hash Table
+ *
+ * Description: Prints information about all visitor entries
+ * in the hash table. This function is not
+ * currently in use today, but will if static
+ * visitor entries are implemented.
+ *
+ * Returns:
+ */
+void
+printFaVisitorHash(struct hash_table *htbl)
+{
+ mipverbose(("------ FaVisitorHash contents ------\n"));
+
+ getAllHashTableEntries(htbl, printFaVisitorEntry, LOCK_READ, 0,
+ _B_FALSE);
+
+}
+
+
+/*
+ * Function: printFaUnreachableEntry
+ *
+ * Arguments: fauep - Pointer to the unreachable entry.
+ *
+ * Description: Prints the contents of a FaUnreachableEntry.
+ * This function is not currently in use, but could
+ * be if a feature was implemented that allowed
+ * blocking of access (statically) to a Home Agent.
+ *
+ * Returns:
+ */
+/* ARGSUSED */
+static boolean_t
+printFaUnreachableEntry(void *entry, uint32_t p1)
+{
+ FaUnreachableEntry *fauep = entry;
+ char addr[INET_ADDRSTRLEN];
+
+ mipverbose(("FaUnreachableEntry contents:\n"));
+ mipverbose(("\tAddr : %s\n", ntoa(fauep->faUnreachableAddr, addr)));
+ mipverbose(("\tExpires : %ld\n", fauep->faUnreachableTimeExpires));
+ return (_B_TRUE);
+}
+
+
+/*
+ * Function: printFaUnreachableHash
+ *
+ * Arguments: htbl - Pointer to the Hash Table.
+ *
+ * Description: Prints out information about all unreachable
+ * entries in the hash.
+ *
+ * This function is not currently in use, but could
+ * be if a feature was implemented that allowed
+ * blocking of access (statically) to a Home Agent.
+ *
+ * Returns:
+ */
+void
+printFaUnreachableHash(struct hash_table *htbl)
+{
+ mipverbose(("------ FaUnreachableHash contents ------\n"));
+
+ getAllHashTableEntries(htbl, printFaUnreachableEntry, LOCK_READ, 0,
+ _B_FALSE);
+}
+#endif
+
+/* Prints the contents of a HaBindingEntry. */
+/*
+ * Function: printHaBindingEntry
+ *
+ * Arguments: habep - Pointer to the Binding Entry
+ *
+ * Description: This function will print out the contents
+ * of a binding entry. This function is not currently
+ * in use, and could be if static bindings were
+ * implemented.
+ *
+ * Returns:
+ */
+#ifndef lint
+boolean_t
+printHaBindingEntry(void *entry, uint32_t p1)
+{
+ HaBindingEntry *habep = entry;
+ char addr[INET_ADDRSTRLEN];
+ time_t currentTime;
+
+ mipverbose(("HaBindingEntry contents:\n"));
+ mipverbose(("\tMN addr : %s\n", ntoa(habep->haBindingMN, addr)));
+ mipverbose(("\tCO addr : %s\n", ntoa(habep->haBindingCOA, addr)));
+ mipverbose(("\tSrc addr : %s\n", ntoa(habep->haBindingSrcAddr, addr)));
+ mipverbose(("\tSrc port : %d\n", habep->haBindingSrcPort));
+ mipverbose(("\tTimeGrant: %ld\n", habep->haBindingTimeGranted));
+ GET_TIME(currentTime);
+ mipverbose(("\tExpires : %ld\n",
+ currentTime - habep->haBindingTimeExpires));
+ mipverbose((
+ "\tReg Flag : %x (SBDMGV__)\n", habep->haBindingRegFlags));
+
+ return (_B_TRUE);
+}
+
+/*
+ * Function: printHaBindingHash
+ *
+ * Arguments: htbl - Pointer to the Hash Table
+ *
+ * Description: This function will print out the contents
+ * of all binding entries in the hash. This
+ * function is not currently in use, and could
+ * be if static bindings were implemented.
+ *
+ * Returns:
+ */
+void
+printHaBindingHash(struct hash_table *htbl)
+{
+
+ mipverbose(("------ HaBindingHash contents ------\n"));
+
+ getAllHashTableEntries(htbl, printHaBindingEntry, LOCK_READ, 0,
+ _B_FALSE);
+}
+
+#endif /* lint */
+/*
+ * Function: printMSAE
+ *
+ * Arguments: msae - Pointer to the Security Assoc Entry
+ *
+ * Description: This function will print out the contents of
+ * a security association entry.
+ *
+ * Returns:
+ */
+static void
+printMSAE(MipSecAssocEntry *msae)
+{
+ mipverbose(("\tMipSecAssocEntry contents:\n"));
+ mipverbose(("\tSPI %d\n", msae->mipSecSPI));
+ mipverbose(("\tAlgo type %d\n", msae->mipSecAlgorithmType));
+ mipverbose(("\tAlgo Mode %d\n", msae->mipSecAlgorithmMode));
+ mipverbose(("\tKey Len %d\n", msae->mipSecKeyLen));
+ mipverbose(("\tReplay method %d\n", msae->mipSecReplayMethod));
+}
+
+
+/*
+ * Function: printHaMobileNodeEntry
+ *
+ * Arguments: hamne - Pointer to the Mobile Node Entry
+ *
+ * Description: This function will print the contents of a
+ * Mobile Node Entry.
+ *
+ * Returns:
+ */
+static boolean_t
+printHaMobileNodeEntry(void *entry, uint32_t p1)
+{
+ HaMobileNodeEntry *hamne = entry;
+ HaBindingEntry *habep;
+ MipSecAssocEntry *mnsae;
+ char addr[INET_ADDRSTRLEN];
+
+ mipverbose(("HaMobileNodeEntry contents:\n"));
+ mipverbose(("\tMN addr %s\n", ntoa(hamne->haMnAddr, addr)));
+ mipverbose((
+ "\tHA's Iface Addr %s\n", ntoa(hamne->haBindingIfaceAddr, addr)));
+ mipverbose(("\tID High: 0x%x\n", hamne->haMnRegIDHigh));
+ mipverbose(("\tID Low : 0x%x\n", hamne->haMnRegIDLow));
+ mipverbose(("\tMN binding count %d\n", hamne->haMnBindingCnt));
+
+ habep = hamne->bindingEntries;
+
+ while (habep) {
+ mipverbose(("\tHaMobileNodeEntry BindingEntry contents:\n"));
+ mipverbose(("\t\tMN addr : %s\n", ntoa(habep->haBindingMN,
+ addr)));
+ mipverbose(("\t\tCO addr : %s\n", ntoa(habep->haBindingCOA,
+ addr)));
+ mipverbose(("\t\tSrc addr : %s\n", ntoa(habep->haBindingSrcAddr,
+ addr)));
+ mipverbose(("\t\tSrc port : %d\n", habep->haBindingSrcPort));
+ mipverbose((
+ "\t\tTimeGrant: %ld\n", habep->haBindingTimeGranted));
+ mipverbose(("\t\tExpires : %ld\n",
+ p1 - habep->haBindingTimeExpires));
+ mipverbose(("\t\tReg Flag : %x (SBDMGV__)\n",
+ habep->haBindingRegFlags));
+ habep = habep->next;
+ }
+
+ if ((mnsae = findSecAssocFromSPI(hamne->haMnSPI,
+ LOCK_READ)) != NULL) {
+ printMSAE(mnsae);
+ (void) rw_unlock(&mnsae->mipSecNodeLock);
+ }
+
+ return (_B_TRUE);
+}
+
+
+/*
+ * Print HaMobileNodeHash contents
+ */
+/*
+ * Function: printHaMobileNodeHash
+ *
+ * Arguments: htbl - Pointer to the Hash Table
+ *
+ * Description: This function will print the contents
+ * of all Mobile Node Entries in the hash.
+ *
+ * Returns:
+ */
+void
+printHaMobileNodeHash(struct hash_table *htbl)
+{
+ time_t currentTime;
+
+ mipverbose(("------ HaMobileNodeHash contents ------\n"));
+
+ GET_TIME(currentTime);
+
+ getAllHashTableEntries(htbl, printHaMobileNodeEntry, LOCK_READ,
+ currentTime, _B_FALSE);
+} /* printHaMobileNodeHash */
diff --git a/usr/src/cmd/cmd-inet/usr.lib/ncaconfd/Makefile b/usr/src/cmd/cmd-inet/usr.lib/ncaconfd/Makefile
new file mode 100644
index 0000000000..40216bd60d
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/ncaconfd/Makefile
@@ -0,0 +1,52 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 2000 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+# cmd/cmd-inet/usr.lib/ncad/Makefile
+#
+
+PROG = ncaconfd
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+include ../../../Makefile.cmd
+include ../Makefile.lib
+
+LDLIBS += -lsocket -lnsl
+CPPFLAGS += -I$(SRC)/uts/common/inet/nca
+
+POFILE = ${PROG}.po
+
+install: all $(ROOTLIBINETPROG)
+
+clean:
+ $(RM) $(PROG)
+
+include ../../../Makefile.targ
+
+lint: lint_PROG
diff --git a/usr/src/cmd/cmd-inet/usr.lib/ncaconfd/ncaconfd.c b/usr/src/cmd/cmd-inet/usr.lib/ncaconfd/ncaconfd.c
new file mode 100644
index 0000000000..be2461b276
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/ncaconfd/ncaconfd.c
@@ -0,0 +1,1438 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/tihdr.h>
+#include <stropts.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <string.h>
+#include <strings.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <libintl.h>
+#include <locale.h>
+#include <unistd.h>
+#include <sys/varargs.h>
+
+#include <netinet/in.h>
+#include <sys/ethernet.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/sysmacros.h>
+#include <net/if.h>
+#include <inet/mib2.h>
+#include <inet/ip.h>
+#include <net/route.h>
+#include <arpa/inet.h>
+#include "ncaconf.h"
+
+/* NCA does not support IPv6... */
+#ifndef IP_DEV_NAME
+#define IP_DEV_NAME "/dev/ip"
+#endif
+
+#ifndef IP_MOD_NAME
+#define IP_MOD_NAME "ip"
+#endif
+
+#ifndef UDP_DEV_NAME
+#define UDP_DEV_NAME "/dev/udp"
+#endif
+
+#ifndef NCA_MOD_NAME
+#define NCA_MOD_NAME "nca"
+#endif
+
+#ifndef ARP_MOD_NAME
+#define ARP_MOD_NAME "arp"
+#endif
+
+#define IF_SEPARATOR ':'
+
+#define ping_prog "/usr/sbin/ping"
+
+/* Structure to hold info about each network interface. */
+typedef struct nif_s {
+ char name[LIFNAMSIZ+1];
+ struct in_addr local_addr;
+ struct in_addr router_addr;
+ uchar_t router_ether_addr[ETHERADDRL];
+} nif_t;
+
+typedef struct mib_item_s {
+ struct mib_item_s *next_item;
+ int group;
+ int mib_id;
+ int length;
+ char *valp;
+} mib_item_t;
+
+/* The network interface array. */
+static nif_t *nif_list;
+/* Number of network interface to process. */
+static int num_nif;
+
+/* Interface request to IP. */
+static struct lifreq lifr;
+
+/* True if syslog is to be used. */
+static boolean_t logging;
+/* True if additional debugging messages are printed. */
+static boolean_t debug;
+
+/* File descriptor to the routing socket. */
+static int rt_fd;
+
+static void logperror(char *);
+static void logwarn(char *, ...);
+static void logdebug(char *, ...);
+static int ip_domux2fd(int *, int *);
+static void ip_plink(int, int);
+static int find_nca_pos(int);
+static int nca_set_nif(int, struct in_addr, uchar_t *);
+static void nca_setup(boolean_t *);
+static int get_if_ip_addr(void);
+static mib_item_t *mibget(int);
+static int ire_process(mib2_ipRouteEntry_t *, size_t, boolean_t *);
+static int arp_process(mib2_ipNetToMediaEntry_t *, size_t, boolean_t *);
+static int get_router_ip_addr(mib_item_t *, boolean_t *);
+static int get_router_ether_addr(mib_item_t *, boolean_t *);
+static int get_if_info(boolean_t *);
+static void daemon_init(void);
+static void daemon_work(void);
+static void ping_them(void);
+
+/*
+ * Print out system error messages, either to syslog or stderr. Note that
+ * syslog() should print out system error messages in the correct language
+ * used. There is no need to use gettext().
+ */
+static void
+logperror(char *str)
+{
+ if (logging) {
+ syslog(LOG_ERR, "%s: %m\n", str);
+ } else {
+ (void) fprintf(stderr, "ncaconfd: %s: %s\n", str,
+ strerror(errno));
+ }
+}
+
+/*
+ * Print out warning messages. The caller should use gettext() to have
+ * the message printed out in the correct language.
+ */
+/*PRINTFLIKE1*/
+static void
+logwarn(char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ if (logging) {
+ vsyslog(LOG_WARNING, fmt, ap);
+ } else {
+ (void) fprintf(stderr, "ncaconfd: ");
+ (void) vfprintf(stderr, fmt, ap);
+ }
+ va_end(ap);
+}
+
+/*
+ * Print out debugging info. Note that syslogd(1M) should be configured to
+ * take ordinary debug info for it to get this kind of info.
+ */
+/*PRINTFLIKE1*/
+static void
+logdebug(char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ if (logging) {
+ vsyslog(LOG_WARNING, fmt, ap);
+ } else {
+ (void) fprintf(stderr, "ncaconfd: ");
+ (void) vfprintf(stderr, fmt, ap);
+ }
+ va_end(ap);
+}
+
+/*
+ * Helper function for nca_setup(). It gets a fd to the lower IP
+ * stream and I_PUNLINK's the lower stream. It also initializes the
+ * global variable lifr.
+ *
+ * Param:
+ * int *udp_fd: (referenced) fd to /dev/udp (upper IP stream).
+ * int *fd: (referenced) fd to the lower IP stream.
+ *
+ * Return:
+ * -1 if operation fails, 0 otherwise.
+ */
+static int
+ip_domux2fd(int *udp_fd, int *fd)
+{
+ int ip_fd;
+
+ if ((ip_fd = open(IP_DEV_NAME, O_RDWR)) < 0) {
+ logperror("Cannot open IP");
+ return (-1);
+ }
+ if ((*udp_fd = open(UDP_DEV_NAME, O_RDWR)) < 0) {
+ logperror("Cannot open UDP");
+ (void) close(ip_fd);
+ return (-1);
+ }
+ if (ioctl(ip_fd, SIOCGLIFMUXID, (caddr_t)&lifr) < 0) {
+ logperror("ioctl(SIOCGLIFMUXID) failed");
+ (void) close(ip_fd);
+ return (-1);
+ }
+ if (debug) {
+ logdebug("ARP_muxid %d IP_muxid %d\n", lifr.lifr_arp_muxid,
+ lifr.lifr_ip_muxid);
+ }
+ if ((*fd = ioctl(*udp_fd, _I_MUXID2FD, lifr.lifr_ip_muxid)) < 0) {
+ logperror("ioctl(_I_MUXID2FD) failed");
+ (void) close(ip_fd);
+ (void) close(*udp_fd);
+ return (-1);
+ }
+ (void) close(ip_fd);
+ return (0);
+}
+
+/*
+ * Helper function for nca_setup(). It I_PLINK's back the upper and
+ * lower IP streams. Note that this function must be called after
+ * ip_domux2fd(). In ip_domux2fd(), the global variable lifr is initialized
+ * and ip_plink() needs information in lifr. So ip_domux2fd() and ip_plink()
+ * must be called in pairs.
+ *
+ * Param:
+ * int udp_fd: fd to /dev/udp (upper IP stream).
+ * int fd: fd to the lower IP stream.
+ */
+static void
+ip_plink(int udp_fd, int fd)
+{
+ int mux_id;
+
+ if ((mux_id = ioctl(udp_fd, I_PLINK, fd)) < 0) {
+ logperror("ioctl(I_PLINK) failed");
+ return;
+ }
+ if (debug > 0) {
+ logdebug("New IP_muxid %d\n", mux_id);
+ }
+ lifr.lifr_ip_muxid = mux_id;
+ if (ioctl(udp_fd, SIOCSLIFMUXID, (caddr_t)&lifr) < 0) {
+ logperror("ioctl(SIOCSLIFMUXID) failed");
+ }
+}
+
+#define FOUND_NCA -1
+#define FOUND_NONE -2
+/*
+ * Find the proper position to insert NCA, which is just below IP.
+ *
+ * Param:
+ * int fd: fd to the lower IP stream.
+ *
+ * Return:
+ * If positive, it is the position to insert NCA.
+ * FOUND_NCA: found NCA! So skip this one for plumbing. But we
+ * still keep it in the interface list.
+ * FOUND_NONE: could not find IP or encounter other errors. Remove
+ * this interface from the list.
+ */
+static int
+find_nca_pos(int fd)
+{
+ int num_mods;
+ int i, pos;
+ struct str_list strlist;
+ boolean_t found_ip = B_FALSE;
+ boolean_t found_nca = B_FALSE;
+
+ if ((num_mods = ioctl(fd, I_LIST, NULL)) < 0) {
+ logperror("ioctl(I_LIST) failed");
+ return (FOUND_NONE);
+ } else {
+ strlist.sl_nmods = num_mods;
+ strlist.sl_modlist = calloc(num_mods,
+ sizeof (struct str_mlist));
+ if (strlist.sl_modlist == NULL) {
+ logperror("cannot malloc");
+ return (FOUND_NONE);
+ } else {
+ if (ioctl(fd, I_LIST, (caddr_t)&strlist) < 0) {
+ logperror("ioctl(I_LIST) failed");
+ } else {
+ for (i = 0; i < strlist.sl_nmods; i++) {
+ if (strcmp(IP_MOD_NAME,
+ strlist.sl_modlist[i].l_name)
+ == 0) {
+ found_ip = B_TRUE;
+ /*
+ * NCA should be just below
+ * IP.
+ */
+ pos = i + 1;
+ } else if (strncmp(NCA_MOD_NAME,
+ strlist.sl_modlist[i].l_name,
+ strlen(NCA_MOD_NAME)) == 0) {
+ found_nca = B_TRUE;
+ }
+ }
+ }
+ free(strlist.sl_modlist);
+ }
+ }
+ if (found_nca) {
+ return (FOUND_NCA);
+ } else if (found_ip) {
+ if (debug) {
+ logdebug("NCA is at position %d in the stream.\n", pos);
+ }
+ return (pos);
+ } else {
+ if (debug) {
+ logdebug("Cannot find IP??\n");
+ }
+ return (FOUND_NONE);
+ }
+}
+
+/*
+ * To set the local IP address and default router ethernet address.
+ *
+ * Param:
+ * int fd: the fd to the lower IP stream.
+ * struct in_addr local_addr: the IP address for this interface.
+ * uchar_t *ether_addr: the ethernet address of the default router for
+ * for this interface.
+ *
+ * Return:
+ * -1 if the system does not support this NCA ioctl(), 0 otherwise.
+ */
+static int
+nca_set_nif(int fd, struct in_addr local_addr, uchar_t *ether_addr)
+{
+ struct nca_set_ioctl nca_ioctl;
+ struct strioctl strioc;
+ int len;
+ uchar_t *dst;
+
+ strioc.ic_cmd = NCA_SET_IF;
+ strioc.ic_timout = INFTIM;
+ strioc.ic_len = sizeof (nca_ioctl);
+ strioc.ic_dp = (char *)&nca_ioctl;
+
+ nca_ioctl.local_addr = local_addr.s_addr;
+ dst = nca_ioctl.router_ether_addr;
+ for (len = ETHERADDRL; len > 0; len--)
+ *dst++ = *ether_addr++;
+ nca_ioctl.action = ADD_DEF_ROUTE;
+
+ if (ioctl(fd, I_STR, &strioc) < 0) {
+ logperror("ioctl(NCA_SET_IF) failed");
+ if (errno == EINVAL)
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * To setup the NCA stream. First insert NCA into the proper position.
+ * Then tell NCA the local IP address and default router by using the
+ * NCA_SET_IF ioctl.
+ *
+ * Param:
+ * boolean_t *active: (referenced) B_TRUE if NCA is setup to do active
+ * connection. If NCA does not support active connection,
+ * in return, active will be set to B_FALSE.
+ */
+static void
+nca_setup(boolean_t *active)
+{
+ int i;
+ int udp_fd;
+ int fd;
+ struct strmodconf mod;
+ /* 128 is enough because interface name can only be LIFNAMSIZ long. */
+ char err_buf[128];
+
+ mod.mod_name = NCA_MOD_NAME;
+ lifr.lifr_addr.ss_family = AF_INET;
+ for (i = 0; i < num_nif; i++) {
+ if (debug) {
+ logdebug("Plumbing NCA for %s\n", nif_list[i].name);
+ }
+ /* This interface does not exist according to IP. */
+ if (nif_list[i].local_addr.s_addr == 0) {
+ continue;
+ }
+ (void) strlcpy(lifr.lifr_name, nif_list[i].name,
+ sizeof (lifr.lifr_name));
+
+ if (ip_domux2fd(&udp_fd, &fd) < 0) {
+ continue;
+ }
+ if (ioctl(udp_fd, I_PUNLINK, lifr.lifr_ip_muxid) < 0) {
+ (void) snprintf(err_buf, sizeof (err_buf),
+ "ioctl(I_PUNLINK) for %s failed", nif_list[i].name);
+ logperror(err_buf);
+ (void) close(udp_fd);
+ (void) close(fd);
+ continue;
+ }
+ if ((mod.pos = find_nca_pos(fd)) < 0) {
+ if (mod.pos == FOUND_NCA) {
+ if (debug) {
+ logdebug("Find NCA in the %s"
+ " stream\n", nif_list[i].name);
+ }
+ /* Just skip plumbing NCA. */
+ goto set_nif;
+ }
+ if (debug) {
+ logdebug("Cannot find pos for %s\n",
+ nif_list[i].name);
+ }
+ goto clean_up;
+ }
+ if (ioctl(fd, _I_INSERT, (caddr_t)&mod) < 0) {
+ (void) snprintf(err_buf, sizeof (err_buf),
+ "ioctl(_I_INSERT) for %s failed", nif_list[i].name);
+ logperror(err_buf);
+ goto clean_up;
+ }
+
+ /*
+ * Only do the following if NCA is also used to make
+ * outgoing connections, and all necessary info is
+ * there.
+ */
+set_nif:
+ if (*active && nif_list[i].router_addr.s_addr != 0) {
+ if (nca_set_nif(fd, nif_list[i].local_addr,
+ nif_list[i].router_ether_addr) < 0) {
+ /*
+ * The system does not support this ioctl()!
+ * Skip all active stack processing but
+ * continue to plumb NCA.
+ */
+ logwarn("NCA does not support active stack!");
+ *active = B_FALSE;
+ }
+ }
+clean_up:
+ ip_plink(udp_fd, fd);
+ (void) close(udp_fd);
+ (void) close(fd);
+ }
+}
+
+/*
+ * To get IP address of network interface from IP.
+ */
+static int
+get_if_ip_addr(void)
+{
+ int sock;
+ struct lifnum lifn;
+ struct lifconf lifc;
+ struct lifreq *lifr;
+ struct sockaddr_in *sin;
+ char *buf;
+ int num_lifr;
+ int i, j;
+
+ /* NCA only supports IPv4... */
+ if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ logperror(gettext("Cannot open socket"));
+ return (-1);
+ }
+ lifn.lifn_family = AF_UNSPEC;
+ lifn.lifn_flags = 0;
+ if (ioctl(sock, SIOCGLIFNUM, (char *)&lifn) < 0) {
+ logperror(gettext("ioctl(SIOCGLIFNUM) failed"));
+ (void) close(sock);
+ return (-1);
+ }
+ buf = (char *)calloc(lifn.lifn_count, sizeof (struct lifreq));
+ if (buf == NULL) {
+ logperror(gettext("calloc() failed"));
+ (void) close(sock);
+ return (-1);
+ }
+
+ lifc.lifc_family = AF_UNSPEC;
+ lifc.lifc_flags = 0;
+ lifc.lifc_len = lifn.lifn_count * sizeof (struct lifreq);
+ lifc.lifc_buf = buf;
+
+ if (ioctl(sock, SIOCGLIFCONF, (char *)&lifc) < 0) {
+ /*
+ * NCA is set up after all the interfaces have been
+ * plumbed. So normally we should not get any error.
+ * Just abort if we encounter an error.
+ */
+ logperror(gettext("ioctl(SIOCGLIFCONF) failed"));
+ free(buf);
+ (void) close(sock);
+ return (-1);
+ }
+ num_lifr = lifc.lifc_len / sizeof (struct lifreq);
+ /* Find the interface and copy the local IP address. */
+ for (i = 0; i < num_nif; i++) {
+ lifr = (struct lifreq *)lifc.lifc_req;
+ for (j = num_lifr; j > 0; j--, lifr++) {
+ /* Again, NCA only supports IPv4. */
+ if (lifr->lifr_addr.ss_family != AF_INET)
+ continue;
+ if (strncmp(nif_list[i].name, lifr->lifr_name,
+ strlen(nif_list[i].name)) == 0) {
+ sin = (struct sockaddr_in *)&lifr->lifr_addr;
+ nif_list[i].local_addr = sin->sin_addr;
+ if (debug) {
+ logdebug("IP address of %s: %s\n",
+ nif_list[i].name,
+ inet_ntoa(sin->sin_addr));
+ }
+ break;
+ }
+ }
+ if (j == 0) {
+ /*
+ * The interface does not exist according to IP!
+ * Log a warning and go on.
+ */
+ logwarn(gettext("Network interface %s"
+ " does not exist!\n"), nif_list[i].name);
+ /*
+ * Set local_addr to 0 so that nca_setup() will
+ * not do anything for this interface.
+ */
+ nif_list[i].local_addr.s_addr = 0;
+ }
+ }
+ free(buf);
+ (void) close(sock);
+ return (0);
+}
+
+/*
+ * Get MIB2 info from IP.
+ *
+ * Param:
+ * int sd: descriptor to IP to send down mib request.
+ */
+static mib_item_t *
+mibget(int sd)
+{
+ char buf[1024];
+ int flags;
+ int i, j, getcode;
+ struct strbuf ctlbuf, databuf;
+ /* LINTED */
+ struct T_optmgmt_req *tor = (struct T_optmgmt_req *)buf;
+ /* LINTED */
+ struct T_optmgmt_ack *toa = (struct T_optmgmt_ack *)buf;
+ /* LINTED */
+ struct T_error_ack *tea = (struct T_error_ack *)buf;
+ struct opthdr *req;
+ mib_item_t *first_item = (mib_item_t *)0;
+ mib_item_t *last_item = (mib_item_t *)0;
+ mib_item_t *temp;
+
+ tor->PRIM_type = T_SVR4_OPTMGMT_REQ;
+ tor->OPT_offset = sizeof (struct T_optmgmt_req);
+ tor->OPT_length = sizeof (struct opthdr);
+ tor->MGMT_flags = T_CURRENT;
+ req = (struct opthdr *)&tor[1];
+ req->level = MIB2_IP; /* any MIB2_xxx value ok here */
+ req->name = 0;
+ req->len = 0;
+
+ ctlbuf.buf = buf;
+ ctlbuf.len = tor->OPT_length + tor->OPT_offset;
+ flags = 0;
+ if (putmsg(sd, &ctlbuf, (struct strbuf *)0, flags) == -1) {
+ logperror("mibget: putmsg(ctl) failed");
+ goto error_exit;
+ }
+
+ /*
+ * Each reply consists of a ctl part for one fixed structure
+ * or table, as defined in mib2.h. The format is a T_OPTMGMT_ACK,
+ * containing an opthdr structure. level/name identify the entry,
+ * len is the size of the data part of the message.
+ */
+ req = (struct opthdr *)&toa[1];
+ ctlbuf.maxlen = sizeof (buf);
+ j = 1;
+ for (;;) {
+ flags = 0;
+ getcode = getmsg(sd, &ctlbuf, (struct strbuf *)0, &flags);
+ if (getcode == -1) {
+ logperror("mibget getmsg(ctl) failed");
+ if (debug) {
+ logdebug("# level name len\n");
+ i = 0;
+ for (last_item = first_item; last_item;
+ last_item = last_item->next_item)
+ (void) printf("%d %4d %5d %d\n",
+ ++i,
+ last_item->group,
+ last_item->mib_id,
+ last_item->length);
+ }
+ goto error_exit;
+ }
+ if (getcode == 0 &&
+ ctlbuf.len >= sizeof (struct T_optmgmt_ack) &&
+ toa->PRIM_type == T_OPTMGMT_ACK &&
+ toa->MGMT_flags == T_SUCCESS &&
+ req->len == 0) {
+ if (debug) {
+ logdebug("mibget getmsg() %d returned "
+ "EOD (level %ld, name %ld)\n",
+ j, req->level, req->name);
+ }
+ return (first_item); /* this is EOD msg */
+ }
+
+ if (ctlbuf.len >= sizeof (struct T_error_ack) &&
+ tea->PRIM_type == T_ERROR_ACK) {
+ logwarn("mibget %d gives T_ERROR_ACK: TLI_error ="
+ " 0x%lx, UNIX_error = 0x%lx\n",
+ j, tea->TLI_error, tea->UNIX_error);
+ errno = (tea->TLI_error == TSYSERR) ?
+ tea->UNIX_error : EPROTO;
+ goto error_exit;
+ }
+
+ if (getcode != MOREDATA ||
+ ctlbuf.len < sizeof (struct T_optmgmt_ack) ||
+ toa->PRIM_type != T_OPTMGMT_ACK ||
+ toa->MGMT_flags != T_SUCCESS) {
+ logwarn("mibget getmsg(ctl) %d returned %d, "
+ "ctlbuf.len = %d, PRIM_type = %ld\n",
+ j, getcode, ctlbuf.len, toa->PRIM_type);
+ if (toa->PRIM_type == T_OPTMGMT_ACK) {
+ logwarn("T_OPTMGMT_ACK: "
+ "MGMT_flags = 0x%lx, req->len = %ld\n",
+ toa->MGMT_flags, req->len);
+ }
+ errno = ENOMSG;
+ goto error_exit;
+ }
+
+ temp = (mib_item_t *)malloc(sizeof (mib_item_t));
+ if (!temp) {
+ logperror("mibget malloc failed");
+ goto error_exit;
+ }
+ if (last_item)
+ last_item->next_item = temp;
+ else
+ first_item = temp;
+ last_item = temp;
+ last_item->next_item = (mib_item_t *)0;
+ last_item->group = req->level;
+ last_item->mib_id = req->name;
+ last_item->length = req->len;
+ last_item->valp = malloc((int)req->len);
+
+ databuf.maxlen = last_item->length;
+ databuf.buf = last_item->valp;
+ databuf.len = 0;
+ flags = 0;
+ getcode = getmsg(sd, (struct strbuf *)0, &databuf, &flags);
+ if (getcode == -1) {
+ logperror("mibget getmsg(data) failed");
+ goto error_exit;
+ } else if (getcode != 0) {
+ logwarn("mibget getmsg(data) returned %d, "
+ "databuf.maxlen = %d, databuf.len = %d\n",
+ getcode, databuf.maxlen, databuf.len);
+ goto error_exit;
+ }
+ j++;
+ }
+
+error_exit:;
+ while (first_item) {
+ last_item = first_item;
+ first_item = first_item->next_item;
+ free(last_item);
+ }
+ return (first_item);
+}
+
+/*
+ * Examine the IPv4 routing table for default routers. For each interface,
+ * find its default router.
+ *
+ * Param:
+ * mib2_ipRouteEntry_t *buf: the mib info buffer.
+ * size_t len: length of buffer.
+ * boolean_t *changed (referenced): set to B_TRUE if there is a change
+ * in router info.
+ *
+ * Return:
+ * number of default router found.
+ */
+static int
+ire_process(mib2_ipRouteEntry_t *buf, size_t len, boolean_t *changed)
+{
+ mib2_ipRouteEntry_t *rp;
+ mib2_ipRouteEntry_t *rp1;
+ mib2_ipRouteEntry_t *rp2;
+ struct in_addr nexthop_v4;
+ mib2_ipRouteEntry_t *endp;
+ char ifname[LIFNAMSIZ + 1];
+ char *cp;
+ int i;
+ int ifname_len;
+ boolean_t found;
+ int num_found = 0;
+
+ if (len == 0)
+ return (0);
+ endp = buf + (len / sizeof (mib2_ipRouteEntry_t));
+
+ for (i = 0; i < num_nif; i++) {
+ /*
+ * Loop thru the routing table entries. Process any
+ * IRE_DEFAULT ire. Ignore the others. For each such
+ * ire, get the nexthop gateway address.
+ */
+ found = B_FALSE;
+ for (rp = buf; rp < endp; rp++) {
+ /*
+ * NCA is only interested in default routes associated
+ * with an interface.
+ */
+ if (!(rp->ipRouteInfo.re_ire_type & IRE_DEFAULT)) {
+ continue;
+ }
+ /* Get the nexthop address. */
+ nexthop_v4.s_addr = rp->ipRouteNextHop;
+
+ /*
+ * Right now, not all IREs have the interface name
+ * it is associated with.
+ */
+ if (rp->ipRouteIfIndex.o_length == 0) {
+ /*
+ * We don't have the outgoing interface in
+ * this case. Get the nexthop address. Then
+ * determine the outgoing interface, by
+ * examining all interface IREs, and
+ * picking the match.
+ */
+ for (rp1 = buf; rp1 < endp; rp1++) {
+
+ if (!(rp1->ipRouteInfo.re_ire_type &
+ IRE_INTERFACE)) {
+ continue;
+ }
+
+ /*
+ * Determine the interface IRE that
+ * matches the nexthop. i.e.
+ * (IRE addr & IRE mask) ==
+ * (nexthop & IRE mask)
+ */
+ if ((rp1->ipRouteDest & rp1->ipRouteMask) ==
+ (nexthop_v4.s_addr & rp1->ipRouteMask)) {
+ /*
+ * We found the interface to go to
+ * the default router. Check the
+ * interface name.
+ */
+ /* Can this be possible?? */
+ if (rp1->ipRouteIfIndex.o_length == 0)
+ continue;
+ rp2 = rp1;
+ break;
+ }
+
+ } /* End inner for loop. */
+ } else {
+ rp2 = rp;
+ }
+
+ ifname_len = MIN(rp2->ipRouteIfIndex.o_length,
+ sizeof (ifname) - 1);
+ (void) memcpy(ifname, rp2->ipRouteIfIndex.o_bytes,
+ ifname_len);
+ ifname[ifname_len] = '\0';
+ if (ifname[0] == '\0')
+ continue;
+ cp = strchr(ifname, IF_SEPARATOR);
+ if (cp != NULL)
+ *cp = '\0';
+
+ /* We are sure both are NULL terminated. */
+ if (strcmp(nif_list[i].name, ifname) == 0) {
+ /* No change, do not do anything. */
+ if (nexthop_v4.s_addr ==
+ nif_list[i].router_addr.s_addr) {
+ found = B_TRUE;
+ break;
+ }
+ nif_list[i].router_addr.s_addr =
+ nexthop_v4.s_addr;
+ if (debug) {
+ logdebug("Get default"
+ " router for %s: %s\n", ifname,
+ inet_ntoa(nexthop_v4));
+ }
+ found = B_TRUE;
+ *changed = B_TRUE;
+ break;
+ }
+
+ }
+ if (!found) {
+ /*
+ * The interface does not have a default router.
+ * Log a warning and go on.
+ */
+ logwarn(gettext("Network interface %s"
+ " does not have a default router.\n"),
+ nif_list[i].name);
+ /*
+ * Set router_addr to 0 so that we will
+ * not do anything for this interface.
+ */
+ nif_list[i].router_addr.s_addr = 0;
+ } else {
+ num_found++;
+ }
+ }
+ return (num_found);
+}
+
+/*
+ * Examine the ARP table to find ethernet address for default routers.
+ *
+ * Param:
+ * mib2_ipNetToMdeiaEntry_t *buf: the mib info buffer.
+ * size_t len: length of buffer.
+ * boolean_t *changed (referenced): set to B_TRUE if there is any change
+ * in ethernet address for any default router.
+ *
+ * Return:
+ * number of ethernet address found.
+ */
+static int
+arp_process(mib2_ipNetToMediaEntry_t *buf, size_t len, boolean_t *changed)
+{
+ mib2_ipNetToMediaEntry_t *rp;
+ mib2_ipNetToMediaEntry_t *endp;
+ int i;
+ boolean_t found;
+ int num_found = 0;
+ uchar_t *src, *dst;
+
+ if (len == 0)
+ return (0);
+ endp = buf + (len / sizeof (mib2_ipNetToMediaEntry_t));
+
+ for (i = 0; i < num_nif; i++) {
+ /*
+ * Loop thru the arp table entries and find the ethernet
+ * address of those default routers.
+ */
+ if (nif_list[i].router_addr.s_addr == 0)
+ continue;
+ found = B_FALSE;
+ for (rp = buf; rp < endp; rp++) {
+ if (rp->ipNetToMediaNetAddress ==
+ nif_list[i].router_addr.s_addr) {
+ /*
+ * Sanity check. Make sure that this
+ * default router is only reachable thru this
+ * interface.
+ */
+ if (rp->ipNetToMediaIfIndex.o_length !=
+ strlen(nif_list[i].name) ||
+ strncmp(rp->ipNetToMediaIfIndex.o_bytes,
+ nif_list[i].name,
+ rp->ipNetToMediaIfIndex.o_length) !=
+ 0) {
+ break;
+ }
+ /* No change, do not do anything. */
+ if (bcmp(nif_list[i].router_ether_addr,
+ rp->ipNetToMediaPhysAddress.o_bytes,
+ ETHERADDRL) == 0) {
+ found = B_TRUE;
+ continue;
+ }
+ dst = nif_list[i].router_ether_addr;
+ src = (uchar_t *)
+ rp->ipNetToMediaPhysAddress.o_bytes;
+ for (len = ETHERADDRL; len > 0; len--)
+ *dst++ = *src++;
+ if (debug) {
+ int j;
+ uchar_t *cp;
+ char err_buf[128];
+
+ (void) snprintf(err_buf,
+ sizeof (err_buf),
+ "Get address for %s: ",
+ inet_ntoa(nif_list[i].router_addr));
+ cp = (uchar_t *)
+ nif_list[i].router_ether_addr;
+ for (j = 0; j < ETHERADDRL; j++) {
+ (void) sprintf(err_buf +
+ strlen(err_buf),
+ "%02x:", 0xff & cp[j]);
+ }
+ (void) sprintf(err_buf +
+ strlen(err_buf) - 1, "\n");
+ logdebug(err_buf);
+ }
+ found = B_TRUE;
+ *changed = B_TRUE;
+ }
+ }
+ if (!found) {
+ logwarn("Cannot reach %s using %s\n",
+ inet_ntoa(nif_list[i].router_addr),
+ nif_list[i].name);
+ /* Clear this default router. */
+ nif_list[i].router_addr.s_addr = 0;
+ } else {
+ num_found++;
+ }
+ }
+ return (num_found);
+}
+
+/*
+ * Get IP address of default routers for each interface.
+ *
+ * Param:
+ * mib_item_t *item: the mib info buffer.
+ * boolean_t *changed (referenced): set to B_TRUE if there is any change
+ * in router info.
+ *
+ * Return:
+ * -1 if there is no router found, 0 otherwise.
+ */
+static int
+get_router_ip_addr(mib_item_t *item, boolean_t *changed)
+{
+ int found = 0;
+
+ for (; item != NULL; item = item->next_item) {
+ /* NCA does not support IPv6... */
+ if (!(item->group == MIB2_IP && item->mib_id == MIB2_IP_ROUTE))
+ continue;
+ /* LINTED */
+ found += ire_process((mib2_ipRouteEntry_t *)item->valp,
+ item->length, changed);
+ }
+ if (found == 0)
+ return (-1);
+ else
+ return (0);
+}
+
+/*
+ * Get Ethernet address for each default router from ARP.
+ *
+ * Param:
+ * mib_item_t *item: the mib info buffer.
+ * boolean_t *changed (referenced): set to B_TRUE if there is any change
+ * in ethernet address of router.
+ *
+ * Return:
+ * -1 if there is no ethernet address found, 0 otherwise.
+ */
+static int
+get_router_ether_addr(mib_item_t *item, boolean_t *changed)
+{
+ int found = 0;
+
+ for (; item != NULL; item = item->next_item) {
+ /* NCA does not support IPv6... */
+ if (!(item->group == MIB2_IP && item->mib_id == MIB2_IP_MEDIA))
+ continue;
+ /* LINTED */
+ found += arp_process((mib2_ipNetToMediaEntry_t *)item->valp,
+ item->length, changed);
+ }
+ if (found == 0)
+ return (-1);
+ else
+ return (0);
+}
+
+/*
+ * Ping all default routers. It just uses system(3F) to call
+ * ping(1M) to do the job...
+ */
+static void
+ping_them(void)
+{
+ int i;
+ char ping_cmd[128];
+
+ for (i = 0; i < num_nif; i++) {
+ if (nif_list[i].router_addr.s_addr != 0) {
+ (void) snprintf(ping_cmd, sizeof (ping_cmd),
+ "%s %s > /dev/null 2>&1",
+ ping_prog,
+ inet_ntoa(nif_list[i].router_addr));
+ (void) system(ping_cmd);
+ }
+ }
+}
+
+/*
+ * To get default router info (both IP address and ethernet address) for
+ * each configured interface from IP.
+ *
+ * Param:
+ * boolean_t *changed (referenced): set to B_TRUE if there is any change
+ * of info.
+ *
+ * Return:
+ * -1 if there is any error, 0 if everything is fine.
+ */
+static int
+get_if_info(boolean_t *changed)
+{
+ int mib_fd;
+ mib_item_t *item;
+ boolean_t ip_changed = B_FALSE;
+ boolean_t ether_changed = B_FALSE;
+
+ if ((mib_fd = open(IP_DEV_NAME, O_RDWR)) < 0) {
+ logperror("cannot open ip to get router info");
+ return (-1);
+ }
+ if (ioctl(mib_fd, I_PUSH, ARP_MOD_NAME) == -1) {
+ logperror("cannot push arp");
+ goto err;
+ }
+
+ if ((item = mibget(mib_fd)) == NULL) {
+ goto err;
+ }
+
+ if (get_router_ip_addr(item, &ip_changed) < 0) {
+ goto err;
+ }
+ /*
+ * Ping every routers to make sure that ARP has all their ethernet
+ * addresses.
+ */
+ ping_them();
+ /*
+ * If the router IP address is not changed, its ethernet address
+ * should not be changed. But just in case there is some IP
+ * failover going on...
+ */
+ if (get_router_ether_addr(item, &ether_changed) < 0) {
+ goto err;
+ }
+ (void) close(mib_fd);
+ *changed = ip_changed || ether_changed;
+ return (0);
+err:
+ (void) close(mib_fd);
+ return (-1);
+}
+
+/*
+ * To remove the default router from an interface.
+ *
+ * Param:
+ * struct in_addr gw_addr: the IP address of the default router to be
+ * removed.
+ */
+static void
+nca_del_nif(struct in_addr gw_addr)
+{
+ struct nca_set_ioctl nca_ioctl;
+ struct strioctl strioc;
+ int i;
+ int udp_fd, fd;
+
+ /* Search for the interface for this router. */
+ for (i = 0; i < num_nif; i++) {
+ if (nif_list[i].router_addr.s_addr == gw_addr.s_addr)
+ break;
+ }
+ if (i == num_nif)
+ return;
+
+ if (ip_domux2fd(&udp_fd, &fd) < 0) {
+ logwarn(gettext("Removing interface %s from the"
+ " configuration list.\n"), nif_list[i].name);
+ nif_list[i].name[0] = 0;
+ return;
+ }
+ if (ioctl(udp_fd, I_PUNLINK, lifr.lifr_ip_muxid) < 0) {
+ logwarn(gettext("Removing interface %s from the"
+ " configuration list.\n"), nif_list[i].name);
+ nif_list[i].name[0] = 0;
+ (void) close(udp_fd);
+ (void) close(fd);
+ return;
+ }
+
+ strioc.ic_cmd = NCA_SET_IF;
+ strioc.ic_timout = INFTIM;
+ strioc.ic_len = sizeof (nca_ioctl);
+ strioc.ic_dp = (char *)&nca_ioctl;
+
+ nca_ioctl.local_addr = 0;
+ (void) memset(nca_ioctl.router_ether_addr, 0, ETHERADDRL);
+ nca_ioctl.action = DEL_DEF_ROUTE;
+
+ if (ioctl(fd, I_STR, &strioc) < 0) {
+ logperror("ioctl(NCA_SET_IF) failed");
+ }
+ ip_plink(udp_fd, fd);
+ (void) close(udp_fd);
+ (void) close(fd);
+
+ /* Clear the fields for this interface. */
+ nif_list[i].router_addr.s_addr = 0;
+ (void) memset(nif_list[i].router_ether_addr, 0, ETHERADDRL);
+}
+
+/*
+ * Wait for any changes in the routing table. If there are changes to
+ * IP address or router ethernet address, send down the info to NCA.
+ */
+static void
+daemon_work(void)
+{
+ int n;
+ int i;
+ int udp_fd;
+ int fd;
+ int64_t msg[2048/8];
+ struct rt_msghdr *rtm;
+ boolean_t changed;
+ struct sockaddr_in *sin;
+ struct in_addr gw_addr;
+ uchar_t *cp;
+
+ /* Loop forever waiting for any routing changes. */
+ for (;;) {
+ if (debug) {
+ logdebug("Waiting to read routing info...\n");
+ }
+ n = read(rt_fd, msg, sizeof (msg));
+ /* Don't die... Reinitialize socket and listen again. */
+ if (n <= 0) {
+ if (debug) {
+ logdebug("Routing socket read error.\n");
+ }
+ (void) close(rt_fd);
+ rt_fd = socket(PF_ROUTE, SOCK_RAW, AF_INET);
+ i = 0;
+ while (rt_fd < 0) {
+ if (i++ == 0) {
+ logperror(gettext("cannot reinitialize"
+ " routing socket"));
+ } else if (i > 5) {
+ logwarn(gettext("Give up on trying to"
+ " reinitializing routing"
+ " socket\n"));
+ exit(1);
+ }
+ /* May be a transient error... */
+ (void) sleep(10);
+ rt_fd = socket(PF_ROUTE, SOCK_RAW, AF_INET);
+ }
+ } else {
+ rtm = (struct rt_msghdr *)msg;
+ if (rtm->rtm_version != RTM_VERSION) {
+ logwarn(gettext("Do non understand routing"
+ " socket info.\n"));
+ continue;
+ }
+ if (debug) {
+ logdebug("Get routing info.\n");
+ }
+ switch (rtm->rtm_type) {
+ case RTM_DELETE:
+ case RTM_OLDDEL:
+ sin = (struct sockaddr_in *)(rtm + 1);
+ cp = (uchar_t *)sin;
+ /* Only handle default route deletion. */
+ if ((rtm->rtm_addrs & RTA_DST) &&
+ (sin->sin_addr.s_addr == 0)) {
+ if (!(rtm->rtm_addrs & RTA_GATEWAY)) {
+ break;
+ }
+ cp += sizeof (struct sockaddr_in);
+ /* LINTED */
+ sin = (struct sockaddr_in *)cp;
+ gw_addr = sin->sin_addr;
+ if (debug) {
+ logdebug("Get default route "
+ "removal notice: gw %s\n",
+ inet_ntoa(gw_addr));
+ }
+ nca_del_nif(gw_addr);
+ }
+ break;
+ case RTM_ADD:
+ case RTM_OLDADD:
+ case RTM_CHANGE:
+ changed = B_FALSE;
+ if (get_if_info(&changed) < 0) {
+ /* May be a transient error... */
+ (void) sleep(10);
+ break;
+ }
+ /* Nothing is changed, do nothing. */
+ if (!changed) {
+ if (debug) {
+ logdebug("Get route change "
+ "notice, but nothing is "
+ "changed for us!");
+ }
+ break;
+ }
+ lifr.lifr_addr.ss_family = AF_INET;
+ for (i = 0; i < num_nif; i++) {
+ int ret;
+
+ /*
+ * If name is NULL, it means that
+ * we have encontered some problems
+ * when configurating the interface.
+ * So we remove it from the list.
+ */
+ if (nif_list[i].name[0] == 0 ||
+ nif_list[i].local_addr.s_addr == 0)
+ continue;
+ (void) strlcpy(lifr.lifr_name,
+ nif_list[i].name,
+ sizeof (lifr.lifr_name));
+ if (ip_domux2fd(&udp_fd, &fd) < 0) {
+ logwarn(gettext("Removing"
+ " interface %s from the"
+ " configuration list.\n"),
+ nif_list[i].name);
+ nif_list[i].name[0] = 0;
+ continue;
+ }
+ if (ioctl(udp_fd, I_PUNLINK,
+ lifr.lifr_ip_muxid) < 0) {
+ logwarn(gettext("Removing"
+ " interface %s from the"
+ " configuration list.\n"),
+ nif_list[i].name);
+ nif_list[i].name[0] = 0;
+ (void) close(udp_fd);
+ (void) close(fd);
+ continue;
+ }
+ if (debug) {
+ logdebug("Configuring"
+ " %s\n", nif_list[i].name);
+ }
+ ret = nca_set_nif(fd,
+ nif_list[i].local_addr,
+ nif_list[i].router_ether_addr);
+ ip_plink(udp_fd, fd);
+ if (ret < 0) {
+ /*
+ * This should not be possible
+ * since if NCA does not
+ * support the ioctl, the
+ * active flag should be
+ * cleared already and this
+ * function should not have
+ * been called at all!
+ */
+ logwarn("Daemon dies\n");
+ exit(1);
+ }
+ (void) close(udp_fd);
+ (void) close(fd);
+ }
+ break;
+ default:
+ continue;
+ }
+ }
+ }
+}
+
+/*
+ * Make us a daemon.
+ */
+static void
+daemon_init(void)
+{
+ pid_t pid;
+
+ if ((pid = fork()) == -1) {
+ /* Write directly to terminal, instead of syslog. */
+ (void) fprintf(stderr, gettext("ncaconfd: cannot fork: %s\n"),
+ strerror(errno));
+ exit(1);
+ }
+ if (pid != 0)
+ exit(0);
+ (void) setsid();
+ /* Fork again so that we will never get a controlling terminal. */
+ if ((pid = fork()) == -1) {
+ /* Write directly to terminal, instead of syslog. */
+ (void) fprintf(stderr, gettext("ncaconfd: cannot fork: %s\n"),
+ strerror(errno));
+ exit(1);
+ }
+ if (pid != 0)
+ exit(0);
+ (void) chdir("/");
+ (void) umask(0);
+ (void) fclose(stdin);
+ (void) fclose(stdout);
+ (void) fclose(stderr);
+}
+
+int
+main(int argc, char **argv)
+{
+ int i, j;
+ int c;
+ boolean_t active = B_FALSE;
+ boolean_t as_daemon = B_TRUE;
+
+ if (argc == 1) {
+ (void) fprintf(stderr, gettext("Usage: %s [-al]"
+ " [interface1 interface2 ...]\n"), argv[0]);
+ return (1);
+ }
+
+ (void) setlocale(LC_ALL, "");
+#if !defined(TEXT_DOMAIN)
+#define TEXT_DOMAIN "SYS_TEST"
+#endif
+ (void) textdomain(TEXT_DOMAIN);
+
+ while ((c = getopt(argc, argv, "adcl")) != EOF) {
+ switch (c) {
+ case 'a':
+ active = B_TRUE;
+ break;
+ case 'd':
+ debug = B_TRUE;
+ break;
+ case 'c':
+ /* Don't run as daemon. */
+ as_daemon = B_FALSE;
+ break;
+ case 'l':
+ logging = B_TRUE;
+ break;
+ default:
+ /* -d and -c are "undocumented" options. */
+ (void) fprintf(stderr, gettext("Usage: %s [-al]"
+ " [interface1 interface2 ...]\n"), argv[0]);
+ return (1);
+ }
+ }
+ num_nif = argc - optind;
+ if (num_nif == 0) {
+ /* No network interface to proces... */
+ (void) fprintf(stderr, gettext("Usage: %s [-al]"
+ " [interface1 interface2 ...]\n"), argv[0]);
+ return (0);
+ }
+ nif_list = calloc(num_nif, sizeof (nif_t));
+ if (nif_list == NULL) {
+ (void) fprintf(stderr, gettext("ncaconfd: Cannot malloc: %s\n"),
+ strerror(errno));
+ return (1);
+ }
+ for (i = 0, j = optind; i < num_nif; i++, j++) {
+ (void) strlcpy(nif_list[i].name, argv[j], LIFNAMSIZ+1);
+ }
+
+ /* Get IP address info for all the intefaces. */
+ if (get_if_ip_addr() < 0) {
+ if (debug) {
+ (void) fprintf(stderr, "ncaconfd: Cannot get IP"
+ " addresses for interfaces.\n");
+ }
+ return (1);
+ }
+ if (logging)
+ openlog("ncaconfd", LOG_PID, LOG_DAEMON);
+ /* No need to run as daemon if NCA is not making active connections. */
+ if (active && as_daemon)
+ daemon_init();
+ if (active) {
+ boolean_t changed;
+
+ /* NCA does not support IPv6... */
+ if ((rt_fd = socket(PF_ROUTE, SOCK_RAW, AF_INET)) < 0) {
+ logperror("Cannot open routing socket");
+ return (1);
+ }
+ /*
+ * At boot up time, the default router may not have been
+ * found. So ignore the error and check later.
+ */
+ if (get_if_info(&changed) < 0) {
+ if (debug) {
+ (void) logwarn("Cannot get"
+ " information from network interface.\n");
+ }
+ }
+ }
+ /* Do the set up as daemon (if we are) to save time at boot up... */
+ nca_setup(&active);
+ if (active)
+ daemon_work();
+ return (0);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/pppoe/Makefile b/usr/src/cmd/cmd-inet/usr.lib/pppoe/Makefile
new file mode 100644
index 0000000000..09987928be
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/pppoe/Makefile
@@ -0,0 +1,68 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2000-2002 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# cmd/cmd-inet/usr.lib/pppoe/Makefile
+#
+
+include ../../../Makefile.cmd
+
+PROG= pppoec pppoed
+CLIENT_OBJS= pppoec.o common.o logging.o
+DAEMON_OBJS= pppoed.o options.o logging.o common.o
+
+CPPFLAGS += -I$(SRC)/uts/common
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+OBJS= $(CLIENT_OBJS) $(DAEMON_OBJS)
+
+LDLIBS += -lsocket -lnsl
+
+.PARALLEL: $(OBJS)
+
+pppoec: $(CLIENT_OBJS)
+ $(LINK.c) $(CLIENT_OBJS) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+pppoed: $(DAEMON_OBJS)
+ $(LINK.c) $(DAEMON_OBJS) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+include ../Makefile.lib
+
+install: all .WAIT $(PROG) $(ROOTLIBINETPROG)
+
+clean:
+ $(RM) $(OBJS)
+
+lint:
+ $(LINT.c) $(CLIENT_OBJS:%.o=%.c) $(LDLIBS)
+ $(LINT.c) $(DAEMON_OBJS:%.o=%.c) $(LDLIBS)
+
+include ../../../Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/usr.lib/pppoe/common.c b/usr/src/cmd/cmd-inet/usr.lib/pppoe/common.c
new file mode 100644
index 0000000000..295fe65074
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/pppoe/common.c
@@ -0,0 +1,394 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * PPPoE common utilities and data.
+ *
+ * Copyright 2000-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <netdb.h>
+#include <assert.h>
+#include <stropts.h>
+#include <sys/types.h>
+#include <inet/common.h>
+#include <netinet/in.h>
+#include <net/sppptun.h>
+#include <net/pppoe.h>
+#include <arpa/inet.h>
+
+#include "common.h"
+
+/* Not all functions are used by all applications. Let lint know this. */
+/*LINTLIBRARY*/
+
+/* Common I/O buffers */
+uint32_t pkt_input[PKT_INPUT_LEN / sizeof (uint32_t)];
+uint32_t pkt_octl[PKT_OCTL_LEN / sizeof (uint32_t)];
+uint32_t pkt_output[PKT_OUTPUT_LEN / sizeof (uint32_t)];
+
+const char tunnam[] = "/dev/" PPP_TUN_NAME;
+
+const ether_addr_t ether_bcast = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+
+/*
+ * Wrapper for standard strerror() function -- the standard allows
+ * that routine to return NULL, and that's inconvenient to handle.
+ * This function never returns NULL.
+ */
+const char *
+mystrerror(int err)
+{
+ const char *estr;
+ static char ebuf[64];
+
+ if ((estr = strerror(err)) != NULL)
+ return (estr);
+ (void) snprintf(ebuf, sizeof (ebuf), "Error:%d", err);
+ return (ebuf);
+}
+
+/*
+ * Wrapper for standard perror() function -- the standard definition
+ * of perror doesn't include the program name in the output and is
+ * thus inconvenient to use.
+ */
+void
+myperror(const char *emsg)
+{
+ (void) fprintf(stderr, "%s: %s: %s\n", myname, emsg,
+ mystrerror(errno));
+}
+
+/*
+ * Wrapper for standard getmsg() function. Completely discards any
+ * fragmented messages because we don't expect ever to see these from
+ * a properly functioning tunnel driver. Returns flags
+ * (MORECTL|MOREDATA) as seen by interface.
+ */
+int
+mygetmsg(int fd, struct strbuf *ctrl, struct strbuf *data, int *flags)
+{
+ int retv;
+ int hadflags;
+
+ hadflags = getmsg(fd, ctrl, data, flags);
+ if (hadflags <= 0 || !(hadflags & (MORECTL | MOREDATA)))
+ return (hadflags);
+
+ do {
+ if (flags != NULL)
+ *flags = 0;
+ retv = getmsg(fd, ctrl, data, flags);
+ } while (retv > 0 || (retv < 0 && errno == EINTR));
+
+ /*
+ * What remains at this point is the tail end of the
+ * truncated message. Toss it.
+ */
+
+ return (retv < 0 ? retv : hadflags);
+}
+
+/*
+ * Common wrapper function for STREAMS I_STR ioctl. Returns -1 on
+ * failure, 0 for success.
+ */
+int
+strioctl(int fd, int cmd, void *ptr, int ilen, int olen)
+{
+ struct strioctl str;
+
+ str.ic_cmd = cmd;
+ str.ic_timout = 0; /* Default timeout; 15 seconds */
+ str.ic_len = ilen;
+ str.ic_dp = ptr;
+
+ if (ioctl(fd, I_STR, &str) == -1) {
+ return (-1);
+ }
+ if (str.ic_len != olen) {
+ errno = EINVAL;
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * Format a PPPoE header in the user's buffer. The returned pointer
+ * is either identical to the first argument, or is NULL if it's not
+ * usable. On entry, dptr should point to the first byte after the
+ * Ethertype field, codeval should be one of the POECODE_* values, and
+ * sessionid should be the assigned session ID number or one of the
+ * special POESESS_* values.
+ */
+poep_t *
+poe_mkheader(void *dptr, uint8_t codeval, int sessionid)
+{
+ poep_t *poep;
+
+ /* Discard obvious junk. */
+ assert(dptr != NULL && IS_P2ALIGNED(dptr, sizeof (poep_t *)));
+
+ /* Initialize the header */
+ poep = (poep_t *)dptr;
+ poep->poep_version_type = POE_VERSION;
+ poep->poep_code = codeval;
+ poep->poep_session_id = htons(sessionid);
+ poep->poep_length = htons(0);
+ return (poep);
+}
+
+/*
+ * Validate that a given tag is intact. This is intended to be used
+ * in tag-parsing loops before attempting to access the tag data.
+ */
+boolean_t
+poe_tagcheck(const poep_t *poep, int length, const uint8_t *tptr)
+{
+ int plen;
+ const uint8_t *tstart, *tend;
+
+ if (poep == NULL || !IS_P2ALIGNED(poep, sizeof (uint16_t)) ||
+ tptr == NULL || length < sizeof (*poep))
+ return (B_FALSE);
+
+ plen = poe_length(poep);
+ if (plen + sizeof (*poep) > length)
+ return (B_FALSE);
+
+ tstart = (const uint8_t *)(poep+1);
+ tend = tstart + plen;
+
+ /*
+ * Note careful dereference of tptr; it might be near the end
+ * already, so we have to range check it before dereferencing
+ * to get the actual tag length. Yes, it looks like we have
+ * duplicate array end checks. No, they're not duplicates.
+ */
+ if (tptr < tstart || tptr+POET_HDRLEN > tend ||
+ tptr+POET_HDRLEN+POET_GET_LENG(tptr) > tend)
+ return (B_FALSE);
+ return (B_TRUE);
+}
+
+static int
+poe_tag_insert(poep_t *poep, uint16_t ttype, const void *data, size_t dlen)
+{
+ int plen;
+ uint8_t *dp;
+
+ plen = poe_length(poep);
+ if (data == NULL)
+ dlen = 0;
+ if (sizeof (*poep) + plen + POET_HDRLEN + dlen > PPPOE_MSGMAX)
+ return (-1);
+ dp = (uint8_t *)(poep + 1) + plen;
+ POET_SET_TYPE(dp, ttype);
+ POET_SET_LENG(dp, dlen);
+ if (dlen > 0)
+ (void) memcpy(POET_DATA(dp), data, dlen);
+ poep->poep_length = htons(plen + POET_HDRLEN + dlen);
+ return (0);
+}
+
+/*
+ * Add a tag with text string data to a PPPoE packet being
+ * constructed. Returns -1 if it doesn't fit, or 0 for success.
+ */
+int
+poe_add_str(poep_t *poep, uint16_t ttype, const char *str)
+{
+ return (poe_tag_insert(poep, ttype, str, strlen(str)));
+}
+
+/*
+ * Add a tag with 32-bit integer data to a PPPoE packet being
+ * constructed. Returns -1 if it doesn't fit, or 0 for success.
+ */
+int
+poe_add_long(poep_t *poep, uint16_t ttype, uint32_t val)
+{
+ val = htonl(val);
+ return (poe_tag_insert(poep, ttype, &val, sizeof (val)));
+}
+
+/*
+ * Add a tag with two 32-bit integers to a PPPoE packet being
+ * constructed. Returns -1 if it doesn't fit, or 0 for success.
+ */
+int
+poe_two_longs(poep_t *poep, uint16_t ttype, uint32_t val1, uint32_t val2)
+{
+ uint32_t vals[2];
+
+ vals[0] = htonl(val1);
+ vals[1] = htonl(val2);
+ return (poe_tag_insert(poep, ttype, vals, sizeof (vals)));
+}
+
+/*
+ * Copy a single tag and its data from one PPPoE packet to a PPPoE
+ * packet being constructed. Returns -1 if it doesn't fit, or 0 for
+ * success.
+ */
+int
+poe_tag_copy(poep_t *poep, const uint8_t *tagp)
+{
+ int tlen;
+ int plen;
+
+ tlen = POET_GET_LENG(tagp) + POET_HDRLEN;
+ plen = poe_length(poep);
+ if (sizeof (*poep) + plen + tlen > PPPOE_MSGMAX)
+ return (-1);
+ (void) memcpy((uint8_t *)(poep + 1) + plen, tagp, tlen);
+ poep->poep_length = htons(tlen + plen);
+ return (0);
+}
+
+struct tag_list {
+ int tl_type;
+ const char *tl_name;
+};
+
+/* List of PPPoE data tag types. */
+static const struct tag_list tag_list[] = {
+ { POETT_END, "End-Of-List" },
+ { POETT_SERVICE, "Service-Name" },
+ { POETT_ACCESS, "AC-Name" },
+ { POETT_UNIQ, "Host-Uniq" },
+ { POETT_COOKIE, "AC-Cookie" },
+ { POETT_VENDOR, "Vendor-Specific" },
+ { POETT_RELAY, "Relay-Session-Id" },
+ { POETT_NAMERR, "Service-Name-Error" },
+ { POETT_SYSERR, "AC-System-Error" },
+ { POETT_GENERR, "Generic-Error" },
+ { POETT_MULTI, "Multicast-Capable" },
+ { POETT_HURL, "Host-URL" },
+ { POETT_MOTM, "Message-Of-The-Minute" },
+ { POETT_RTEADD, "IP-Route-Add" },
+ { 0, NULL }
+};
+
+/* List of PPPoE message code numbers. */
+static const struct tag_list code_list[] = {
+ { POECODE_DATA, "Data" },
+ { POECODE_PADO, "Active Discovery Offer" },
+ { POECODE_PADI, "Active Discovery Initiation" },
+ { POECODE_PADR, "Active Discovery Request" },
+ { POECODE_PADS, "Active Discovery Session-confirmation" },
+ { POECODE_PADT, "Active Discovery Terminate" },
+ { POECODE_PADM, "Active Discovery Message" },
+ { POECODE_PADN, "Active Discovery Network" },
+ { 0, NULL }
+};
+
+/*
+ * Given a tag type number, return a pointer to a string describing
+ * the tag.
+ */
+const char *
+poe_tagname(uint16_t tagtype)
+{
+ const struct tag_list *tlp;
+ static char tname[32];
+
+ for (tlp = tag_list; tlp->tl_name != NULL; tlp++)
+ if (tagtype == tlp->tl_type)
+ return (tlp->tl_name);
+ (void) sprintf(tname, "Tag%d", tagtype);
+ return (tname);
+}
+
+/*
+ * Given a PPPoE message code number, return a pointer to a string
+ * describing the message.
+ */
+const char *
+poe_codename(uint8_t codetype)
+{
+ const struct tag_list *tlp;
+ static char tname[32];
+
+ for (tlp = code_list; tlp->tl_name != NULL; tlp++)
+ if (codetype == tlp->tl_type)
+ return (tlp->tl_name);
+ (void) sprintf(tname, "Code%d", codetype);
+ return (tname);
+}
+
+/*
+ * Given a tunnel driver address structure, return a pointer to a
+ * string naming that Ethernet host.
+ */
+const char *
+ehost2(const struct ether_addr *ea)
+{
+ static char hbuf[MAXHOSTNAMELEN+1];
+
+ if (ea == NULL)
+ return ("NULL");
+ if (ether_ntohost(hbuf, ea) == 0)
+ return (hbuf);
+ return (ether_ntoa(ea));
+}
+
+const char *
+ehost(const ppptun_atype *pap)
+{
+ return (ehost2((const struct ether_addr *)pap));
+}
+
+/*
+ * Given an Internet address (in network byte order), return a pointer
+ * to a string naming the host.
+ */
+const char *
+ihost(uint32_t haddr)
+{
+ struct hostent *hp;
+ struct sockaddr_in sin;
+
+ (void) memset(&sin, '\0', sizeof (sin));
+ sin.sin_addr.s_addr = haddr;
+ hp = gethostbyaddr((const char *)&sin, sizeof (sin), AF_INET);
+ if (hp != NULL)
+ return (hp->h_name);
+ return (inet_ntoa(sin.sin_addr));
+}
+
+int
+hexdecode(char chr)
+{
+ if (chr >= '0' && chr <= '9')
+ return ((int)(chr - '0'));
+ if (chr >= 'a' && chr <= 'F')
+ return ((int)(chr - 'a' + 10));
+ return ((int)(chr - 'A' + 10));
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/pppoe/common.h b/usr/src/cmd/cmd-inet/usr.lib/pppoe/common.h
new file mode 100644
index 0000000000..a0400a2a44
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/pppoe/common.h
@@ -0,0 +1,97 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * PPPoE common utilities and data.
+ *
+ * Copyright (c) 2000-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef PPPOE_COMMON_H
+#define PPPOE_COMMON_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/sppptun.h>
+#include <net/pppoe.h>
+#include <netinet/if_ether.h>
+
+#define PKT_INPUT_LEN PPPOE_MSGMAX
+#define PKT_OCTL_LEN (sizeof (struct ppptun_control) + 1)
+#define PKT_OUTPUT_LEN PPPOE_MSGMAX
+
+/* Common buffers */
+extern uint32_t pkt_input[];
+extern uint32_t pkt_octl[];
+extern uint32_t pkt_output[];
+
+/* Name of PPPoE tunnel driver */
+extern const char tunnam[];
+
+/* Name of application (from argv[0]) */
+extern char *myname;
+
+/* Ethernet broadcast address */
+extern const ether_addr_t ether_bcast;
+
+/* General purpose utility functions. */
+struct strbuf;
+extern int strioctl(int fd, int cmd, void *ptr, int ilen, int olen);
+extern const char *ehost(const ppptun_atype *pap);
+extern const char *ehost2(const struct ether_addr *ea);
+extern const char *ihost(uint32_t haddr);
+extern int hexdecode(char chr);
+extern const char *mystrerror(int err);
+extern void myperror(const char *emsg);
+extern int mygetmsg(int fd, struct strbuf *ctrl, struct strbuf *data,
+ int *flags);
+
+/* PPPoE-specific functions. */
+extern poep_t *poe_mkheader(void *dptr, uint8_t codeval, int sessionid);
+extern boolean_t poe_tagcheck(const poep_t *poep, int length,
+ const uint8_t *tptr);
+extern int poe_add_str(poep_t *poep, uint16_t ttype, const char *str);
+extern int poe_add_long(poep_t *poep, uint16_t ttype, uint32_t val);
+extern int poe_two_longs(poep_t *poep, uint16_t ttype, uint32_t val1,
+ uint32_t val2);
+extern int poe_tag_copy(poep_t *poep, const uint8_t *tagp);
+extern const char *poe_tagname(uint16_t tagtype);
+extern const char *poe_codename(uint8_t codetype);
+
+/* These are here in case access wrappers are desired. */
+#define poe_version_type(p) ((p)->poep_version_type)
+#define poe_code(p) ((p)->poep_code)
+#define poe_session_id(p) ntohs((p)->poep_session_id)
+#define poe_length(p) ntohs((p)->poep_length)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PPPOE_COMMON_H */
diff --git a/usr/src/cmd/cmd-inet/usr.lib/pppoe/logging.c b/usr/src/cmd/cmd-inet/usr.lib/pppoe/logging.c
new file mode 100644
index 0000000000..50692b1232
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/pppoe/logging.c
@@ -0,0 +1,355 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * PPPoE Server-mode daemon log file support.
+ *
+ * Copyright (c) 2000-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <alloca.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <syslog.h>
+#include <assert.h>
+#include <sys/types.h>
+
+#include "common.h"
+#include "logging.h"
+
+/* Not all functions are used by all applications. Let lint know this. */
+/*LINTLIBRARY*/
+
+const char *prog_name = "none"; /* Subsystem name for syslog */
+int log_level; /* Higher number for more detail. */
+
+static int curlogfd = -1; /* Current log file */
+static const char *curfname; /* Name of current log file */
+static const char *stderr_name = "stderr";
+
+#define SMALLSTR 254 /* Don't allocate for most strings. */
+
+/*
+ * Returns -1 on error (with errno set), 0 on blocked write (file
+ * system full), or N (buffer length) on success.
+ */
+static int
+dowrite(int fd, const void *buf, int len)
+{
+ int retv;
+ const uint8_t *bp = (uint8_t *)buf;
+
+ while (len > 0) {
+ retv = write(fd, bp, len);
+ if (retv == 0) {
+ break;
+ }
+ if (retv == -1) {
+ if (errno != EINTR)
+ break;
+ } else {
+ bp += retv;
+ len -= retv;
+ }
+ }
+ if (len <= 0)
+ return (bp - (uint8_t *)buf);
+ return (retv);
+}
+
+/* A close that avoids closing stderr */
+static int
+doclose(void)
+{
+ int retval = 0;
+
+ if (curlogfd == -1)
+ return (0);
+ if ((curlogfd != STDERR_FILENO) || (curfname != stderr_name))
+ retval = close(curlogfd);
+ curlogfd = -1;
+ return (retval);
+}
+
+/*
+ * Log levels are 0 for no messages, 1 for errors, 2 for warnings, 3
+ * for informational messages, and 4 for debugging messages.
+ */
+static void
+vlogat(int loglev, const char *fmt, va_list args)
+{
+ char timbuf[64];
+ char regbuf[SMALLSTR+2];
+ char *ostr;
+ int timlen;
+ int slen;
+ char *nstr;
+ int err1, err2;
+ int sloglev;
+ int retv;
+ va_list args2;
+ static int xlate_loglev[] = {
+ LOG_ERR, LOG_WARNING, LOG_INFO, LOG_DEBUG
+ };
+
+ if (loglev >= log_level)
+ return;
+
+ timbuf[0] = '\0';
+ timlen = 0;
+ if (curlogfd >= 0) {
+ time_t now = time(NULL);
+
+ /*
+ * Form a time/date string for file (non-syslog) logging.
+ * Caution: string broken in two so that SCCS doesn't mangle
+ * the %-T-% sequence.
+ */
+ timlen = strftime(timbuf, sizeof (timbuf), "%Y/%m/%d %T"
+ "%Z: ", localtime(&now));
+ }
+
+ /* Try formatting once into the small buffer. */
+ va_copy(args2, args);
+ slen = vsnprintf(regbuf, SMALLSTR, fmt, args);
+ if (slen < SMALLSTR) {
+ ostr = regbuf;
+ } else {
+ /*
+ * Length returned by vsnprintf doesn't include null,
+ * and may also be missing a terminating \n.
+ */
+ ostr = alloca(slen + 2);
+ slen = vsnprintf(ostr, slen + 1, fmt, args2);
+ }
+
+ /* Don't bother logging empty lines. */
+ if (slen <= 0)
+ return;
+
+ /* Tack on a \n if needed. */
+ if (ostr[slen - 1] != '\n') {
+ ostr[slen++] = '\n';
+ ostr[slen] = '\0';
+ }
+
+ /* Translate our log levels into syslog standard values */
+ assert(loglev >= 0 && loglev < Dim(xlate_loglev));
+ sloglev = xlate_loglev[loglev];
+
+ /* Log each line separately */
+ for (; *ostr != '\0'; ostr = nstr + 1) {
+ nstr = strchr(ostr, '\n');
+
+ /* Ignore zero-length lines. */
+ if (nstr == ostr)
+ continue;
+
+ slen = nstr - ostr + 1;
+
+ /*
+ * If we're supposed to be logging to a file, then try
+ * that first. Ditch the file and revert to syslog if
+ * any errors occur.
+ */
+ if (curlogfd >= 0) {
+ if ((retv = dowrite(curlogfd, timbuf, timlen)) > 0)
+ retv = dowrite(curlogfd, ostr, slen);
+
+ /*
+ * If we've successfully logged this line,
+ * then go do the next one.
+ */
+ if (retv > 0)
+ continue;
+
+ /* Save errno (if any) and close log file */
+ err1 = errno;
+ if (doclose() == -1)
+ err2 = errno;
+ else
+ err2 = 0;
+
+ /*
+ * Recursion is safe here because we cleared
+ * out curlogfd above.
+ */
+ if (retv == -1)
+ logerr("write log %s: %s", curfname,
+ mystrerror(err1));
+ else
+ logerr("cannot write %s", curfname);
+ if (err2 == 0)
+ logdbg("closed log %s", curfname);
+ else
+ logerr("closing log %s: %s", curfname,
+ mystrerror(err2));
+ }
+ syslog(sloglev, "%.*s", slen, ostr);
+ }
+}
+
+/* Log at debug level */
+void
+logdbg(const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ vlogat(LOGLVL_DBG, fmt, args);
+ va_end(args);
+}
+
+/* Log informational messages */
+void
+loginfo(const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ vlogat(LOGLVL_INFO, fmt, args);
+ va_end(args);
+}
+
+/* Log warning messages */
+void
+logwarn(const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ vlogat(LOGLVL_WARN, fmt, args);
+ va_end(args);
+}
+
+/* Log error messages */
+void
+logerr(const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ vlogat(LOGLVL_ERR, fmt, args);
+ va_end(args);
+}
+
+/* Log a strerror message */
+void
+logstrerror(const char *emsg)
+{
+ logerr("%s: %s\n", emsg, mystrerror(errno));
+}
+
+void
+log_to_stderr(int dbglvl)
+{
+ log_level = dbglvl;
+ if (curlogfd >= 0)
+ close_log_files();
+ curlogfd = STDERR_FILENO;
+ curfname = stderr_name;
+}
+
+/*
+ * Set indicated log file and debug level.
+ */
+void
+log_for_service(const char *fname, int dbglvl)
+{
+ int err1, err2;
+ boolean_t closed;
+
+ log_level = dbglvl;
+ if (fname != NULL &&
+ (*fname == '\0' || strcasecmp(fname, "syslog") == 0))
+ fname = NULL;
+ if (fname == NULL && curfname == NULL)
+ return;
+ err1 = err2 = 0;
+ closed = B_FALSE;
+ if (curlogfd >= 0) {
+ if (fname == curfname ||
+ (fname != NULL && strcmp(fname, curfname) == 0)) {
+ curfname = fname;
+ return;
+ }
+ if (doclose() == -1)
+ err1 = errno;
+ closed = B_TRUE;
+ }
+ if (fname != NULL) {
+ curlogfd = open(fname, O_WRONLY|O_APPEND|O_CREAT, 0600);
+ if (curlogfd == -1)
+ err2 = errno;
+ }
+ if (closed) {
+ if (err1 == 0)
+ logdbg("closed log %s", curfname);
+ else
+ logerr("closing log %s: %s", curfname,
+ mystrerror(err1));
+ }
+ if (fname != NULL) {
+ if (err2 == 0)
+ logdbg("opened log %s", fname);
+ else
+ logerr("opening log %s: %s", fname, mystrerror(err2));
+ }
+ curfname = fname;
+}
+
+/*
+ * Close any open log file. This is used for SIGHUP (to support log
+ * file rotation) and when execing.
+ */
+void
+close_log_files(void)
+{
+ int err = 0;
+
+ if (curlogfd >= 0) {
+ if (doclose() == -1)
+ err = errno;
+ if (err == 0)
+ logdbg("closed log %s", curfname);
+ else
+ logerr("closing log %s: %s", curfname,
+ mystrerror(err));
+ }
+}
+
+/*
+ * Reopen syslog connection; in case it was closed.
+ */
+void
+reopen_log(void)
+{
+ openlog(prog_name, LOG_PID | LOG_NDELAY | LOG_NOWAIT, LOG_DAEMON);
+ /* I control the log level */
+ (void) setlogmask(LOG_UPTO(LOG_DEBUG));
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/pppoe/logging.h b/usr/src/cmd/cmd-inet/usr.lib/pppoe/logging.h
new file mode 100644
index 0000000000..9c5ad2aead
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/pppoe/logging.h
@@ -0,0 +1,70 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * PPPoE Server-mode daemon logging functions.
+ *
+ * Copyright (c) 2000-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef PPPOE_LOGGING_H
+#define PPPOE_LOGGING_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LOGLVL_DBG 3
+#define LOGLVL_INFO 2
+#define LOGLVL_WARN 1
+#define LOGLVL_ERR 0
+
+/* Functions in logging.c */
+extern void logdbg(const char *fmt, ...);
+extern void loginfo(const char *fmt, ...);
+extern void logwarn(const char *fmt, ...);
+extern void logerr(const char *fmt, ...);
+extern void logstrerror(const char *emsg);
+extern void log_for_service(const char *fname, int dbglvl);
+extern void log_to_stderr(int dbglvl);
+extern void close_log_files(void);
+extern void reopen_log(void);
+
+/* Data in logging.c */
+extern const char *prog_name;
+extern int log_level;
+
+/* Functions in options.c */
+extern void global_logging(void);
+
+/* A handy macro. */
+#ifndef Dim
+#define Dim(x) (sizeof (x) / sizeof (*(x)))
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PPPOE_LOGGING_H */
diff --git a/usr/src/cmd/cmd-inet/usr.lib/pppoe/options.c b/usr/src/cmd/cmd-inet/usr.lib/pppoe/options.c
new file mode 100644
index 0000000000..a5cbd9261a
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/pppoe/options.c
@@ -0,0 +1,2334 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * PPPoE Server-mode daemon option parsing.
+ *
+ * Copyright 2000-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <assert.h>
+#include <ctype.h>
+#include <string.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <grp.h>
+#include <errno.h>
+#include <netdb.h>
+#include <stropts.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <net/sppptun.h>
+
+#include "common.h"
+#include "logging.h"
+
+#define MAX_KEYWORD 4096 /* Maximum token length */
+#define MAX_NEST 32 /* Maximum ${$sub} nesting */
+#define MAXARGS 256 /* Maximum number of pppd arguments */
+
+/*
+ * Client filter entry. These are linked in *reverse* order so that
+ * the DAG created by file inclusion nesting works as expected. Since
+ * the administrator who wrote the configuration expects "first
+ * match," this means that tests against the filter list must actually
+ * use "last match."
+ */
+struct filter_entry {
+ struct filter_entry *fe_prev; /* Previous filter in list */
+ struct ether_addr fe_mac; /* MAC address */
+ struct ether_addr fe_mask; /* Mask for above address test */
+ uchar_t fe_isexcept; /* invert sense; exclude matching clients */
+ uchar_t fe_prevcopy; /* fe_prev points to copied list */
+ uchar_t fe_unused[2]; /* padding */
+};
+
+/*
+ * Note: I would like to make the strings and filters here const, but
+ * I can't because they have to be passed to free() during parsing. I
+ * could work around this with offsetof() or data copies, but it's not
+ * worth the effort.
+ */
+struct service_entry {
+ const char *se_name; /* Name of service */
+ struct filter_entry *se_flist; /* Pointer to list of client filters */
+ uint_t se_flags; /* SEF_* flags (below) */
+ int se_debug; /* Debug level (0=nodebug) */
+ char *se_server; /* Server (AC) name */
+ char *se_pppd; /* Options for pppd */
+ char *se_path; /* Path to pppd executable */
+ char *se_extra; /* Extra options */
+ char *se_log; /* Log file */
+ uid_t se_uid; /* User ID */
+ gid_t se_gid; /* Group ID */
+};
+
+#define SEF_WILD 0x00000001 /* Offer in wildcard reply */
+#define SEF_NOWILD 0x00000002 /* Don't offer in wildcard */
+#define SEF_CFLIST 0x00000004 /* se_flist copied from global */
+#define SEF_CSERVER 0x00000008 /* se_server copied from global */
+#define SEF_CPPPD 0x00000010 /* se_pppd copied from global */
+#define SEF_CPATH 0x00000020 /* se_path copied from global */
+#define SEF_CEXTRA 0x00000040 /* se_extra copied from global */
+#define SEF_CLOG 0x00000080 /* se_log copied from global */
+#define SEF_UIDSET 0x00000100 /* se_uid has been set */
+#define SEF_GIDSET 0x00000200 /* se_gid has been set */
+#define SEF_DEBUGCLR 0x00000400 /* do not add se_debug from global */
+#define SEF_CDEV 0x00000800 /* copied devs (parse only) */
+
+/*
+ * One of these is allocated per lower-level stream (device) that is
+ * referenced by the configuration files. The queries are received
+ * per device, and this structure allows us to find all of the
+ * services that correspond to that device.
+ */
+struct device_entry {
+ const char *de_name;
+ const struct service_entry **de_services;
+ int de_nservices;
+};
+
+/*
+ * This is the parsed configuration. While a new configuration is
+ * being read, this is kept around until the new configuration is
+ * ready, and then it is discarded in one operation. It has an array
+ * of device entries (as above) -- one per referenced lower stream --
+ * and a pointer to the allocated parser information. The latter is
+ * kept around because we reuse pointers rather than reallocating and
+ * copying the data. There are thus multiple aliases to the dynamic
+ * data, and the "owner" (for purposes of freeing the storage) is
+ * considered to be this 'junk' list.
+ */
+struct option_state {
+ const struct device_entry *os_devices;
+ int os_ndevices;
+ struct per_file *os_pfjunk; /* Kept for deallocation */
+ char **os_evjunk; /* ditto */
+};
+
+/*
+ * This is the root pointer to the current parsed options.
+ * This cannot be const because it's passed to free() when reparsing
+ * options.
+ */
+static struct option_state *cur_options;
+
+/* Global settings for module-wide options. */
+static struct service_entry glob_svc;
+
+/*
+ * *******************************************************************
+ * Data structures generated during parsing.
+ */
+
+/* List of device names attached to one service */
+struct device_list {
+ struct device_list *dl_next;
+ const char *dl_name; /* Name of one device */
+};
+
+/* Entry for a single defined service. */
+struct service_list {
+ struct service_entry sl_entry; /* Parsed service data */
+ struct service_list *sl_next; /* Next service entry */
+ struct parse_state *sl_parse; /* Back pointer to state */
+ struct device_list *sl_dev; /* List of devices */
+ int sl_serial; /* Serial number (conflict resolve) */
+};
+#define SESERIAL(x) ((struct service_list *)&(x))->sl_serial
+#define ISGLOBAL(x) ((x) == &(x)->sl_parse->ps_cfile->pf_global)
+
+/*
+ * Structure allocated for each file opened. File nesting is chained
+ * in reverse order so that global option scoping works as expected.
+ */
+struct per_file {
+ struct per_file *pf_prev; /* Back chain */
+ struct service_list pf_global; /* Global (default) service context */
+ struct service_list *pf_svc; /* List of services */
+ struct service_list *pf_svc_last;
+ FILE *pf_input; /* File for input */
+ const char *pf_name; /* File name */
+ int pf_nsvc; /* Count of services */
+};
+
+/* State of parser */
+enum key_state {
+ ksDefault, ksService, ksDevice, ksClient, ksClientE, ksServer,
+ ksPppd, ksFile, ksPath, ksExtra, ksLog, ksUser, ksGroup
+};
+
+/*
+ * Global parser state. There is one of these structures, and it
+ * exists only while actively parsing configuration files.
+ */
+struct parse_state {
+ enum key_state ps_state; /* Parser state */
+ int ps_serial; /* Service serial number */
+ struct per_file *ps_files; /* Parsed files */
+ struct per_file *ps_cfile; /* Current file */
+ struct service_list *ps_csvc; /* Current service */
+ struct device_list *ps_star; /* Wildcard device */
+ int ps_flags; /* PSF_* below */
+ char **ps_evlist; /* allocated environment variables */
+ int ps_evsize; /* max length; for realloc */
+};
+
+#define PSF_PERDEV 0x0001 /* In a per-device file */
+#define PSF_SETLEVEL 0x0002 /* Set log level along the way */
+
+/* Should be in a library somewhere. */
+static char *
+strsave(const char *str)
+{
+ char *newstr;
+
+ if (str == NULL)
+ return (NULL);
+ newstr = (char *)malloc(strlen(str) + 1);
+ if (newstr != NULL)
+ (void) strcpy(newstr, str);
+ return (newstr);
+}
+
+/*
+ * Stop defining current service and revert to global definition.
+ * This resolves any implicit references to global options by copying
+ * ("inheriting") from the current global state.
+ */
+static void
+close_service(struct service_list *slp)
+{
+ struct parse_state *psp;
+ struct per_file *cfile;
+ struct service_entry *sep;
+ struct service_entry *sedefp;
+ struct filter_entry *fep;
+
+ assert(slp != NULL);
+ psp = slp->sl_parse;
+ cfile = psp->ps_cfile;
+
+ /* If no current file, then nothing to close. */
+ if (cfile == NULL)
+ return;
+
+ sep = &slp->sl_entry;
+
+ /*
+ * Fix up filter pointers to make DAG. First, locate
+ * the end of the filter list.
+ */
+ if (sep->se_flags & SEF_CFLIST) {
+ sep->se_flist = fep = NULL;
+ } else {
+ for (fep = sep->se_flist; fep != NULL; fep = fep->fe_prev)
+ if (fep->fe_prev == NULL || fep->fe_prevcopy) {
+ fep->fe_prev = NULL;
+ break;
+ }
+ }
+ if (slp == &cfile->pf_global) {
+ /*
+ * If we're in a global context, then we're about to
+ * open a new service, so it's time to fix up the
+ * filter list so that it's usable as a reference.
+ * Loop through files from which we were included, and
+ * link up filters. Note: closure may occur more than
+ * once here.
+ */
+ /* We don't inherit from ourselves. */
+ cfile = cfile->pf_prev;
+ while (cfile != NULL) {
+ if (fep == NULL) {
+ sep->se_flist = fep =
+ cfile->pf_global.sl_entry.se_flist;
+ sep->se_flags |= SEF_CFLIST;
+ } else if (fep->fe_prev == NULL) {
+ fep->fe_prev =
+ cfile->pf_global.sl_entry.se_flist;
+ fep->fe_prevcopy = 1;
+ }
+ cfile = cfile->pf_prev;
+ }
+ } else {
+ /*
+ * Loop through default options in current and all
+ * enclosing include files. Inherit options.
+ */
+ logdbg("service %s ends", slp->sl_entry.se_name);
+ while (cfile != NULL) {
+ /* Inherit from global service options. */
+ if (slp->sl_dev == NULL) {
+ slp->sl_dev = cfile->pf_global.sl_dev;
+ sep->se_flags |= SEF_CDEV;
+ }
+ sedefp = &cfile->pf_global.sl_entry;
+ if (fep == NULL) {
+ sep->se_flist = fep = sedefp->se_flist;
+ sep->se_flags |= SEF_CFLIST;
+ } else if (fep->fe_prev == NULL) {
+ fep->fe_prev = sedefp->se_flist;
+ fep->fe_prevcopy = 1;
+ }
+ if (sep->se_server == NULL) {
+ sep->se_server = sedefp->se_server;
+ sep->se_flags |= SEF_CSERVER;
+ }
+ if (sep->se_pppd == NULL) {
+ sep->se_pppd = sedefp->se_pppd;
+ sep->se_flags |= SEF_CPPPD;
+ }
+ if (sep->se_path == NULL) {
+ sep->se_path = sedefp->se_path;
+ sep->se_flags |= SEF_CPATH;
+ }
+ if (sep->se_extra == NULL) {
+ sep->se_extra = sedefp->se_extra;
+ sep->se_flags |= SEF_CEXTRA;
+ }
+ if (sep->se_log == NULL) {
+ sep->se_log = sedefp->se_log;
+ sep->se_flags |= SEF_CLOG;
+ }
+ if (!(sep->se_flags & SEF_UIDSET) &&
+ (sedefp->se_flags & SEF_UIDSET)) {
+ sep->se_uid = sedefp->se_uid;
+ sep->se_flags |= SEF_UIDSET;
+ }
+ if (!(sep->se_flags & SEF_GIDSET) &&
+ (sedefp->se_flags & SEF_GIDSET)) {
+ sep->se_gid = sedefp->se_gid;
+ sep->se_flags |= SEF_GIDSET;
+ }
+ if (!(sep->se_flags & (SEF_WILD|SEF_NOWILD)))
+ sep->se_flags |= sedefp->se_flags &
+ (SEF_WILD|SEF_NOWILD);
+ if (!(sep->se_flags & SEF_DEBUGCLR)) {
+ sep->se_debug += sedefp->se_debug;
+ sep->se_flags |= sedefp->se_flags &
+ SEF_DEBUGCLR;
+ }
+ cfile = cfile->pf_prev;
+ }
+ }
+ /* Revert to global definitions. */
+ psp->ps_csvc = &psp->ps_cfile->pf_global;
+}
+
+/* Discard a dynamic device list */
+static void
+free_device_list(struct device_list *dlp)
+{
+ struct device_list *dln;
+
+ while (dlp != NULL) {
+ dln = dlp->dl_next;
+ free(dlp);
+ dlp = dln;
+ }
+}
+
+/*
+ * Handle "service <name>" -- finish up previous service definition
+ * (if any) by copying from global state where necessary, and start
+ * defining new service.
+ */
+static int
+set_service(struct service_list *slp, const char *str)
+{
+ struct parse_state *psp;
+ struct per_file *cfile;
+
+ /* Finish current service */
+ close_service(slp);
+
+ /* Start new service */
+ psp = slp->sl_parse;
+ slp = (struct service_list *)calloc(sizeof (*slp) + strlen(str) + 1,
+ 1);
+ if (slp == NULL) {
+ logerr("no memory for service \"%s\"", str);
+ return (-1);
+ }
+
+ /* Add to end of list */
+ cfile = psp->ps_cfile;
+ if (cfile->pf_svc_last == NULL)
+ cfile->pf_svc = slp;
+ else
+ cfile->pf_svc_last->sl_next = slp;
+ cfile->pf_svc_last = slp;
+ cfile->pf_nsvc++;
+
+ /* Fill in initial service entry */
+ slp->sl_entry.se_name = (const char *)(slp+1);
+ (void) strcpy((char *)(slp+1), str);
+ logdbg("service %s begins", slp->sl_entry.se_name);
+ slp->sl_serial = psp->ps_serial++;
+ slp->sl_parse = psp;
+
+ /* This is now the current service that we're defining. */
+ psp->ps_csvc = slp;
+ return (0);
+}
+
+/*
+ * Handle both "wildcard" and "nowildcard" options.
+ */
+static int
+set_wildcard(struct service_list *slp, const char *str)
+{
+ /* Allow global context to switch back and forth without error. */
+ if (!ISGLOBAL(slp) &&
+ (slp->sl_entry.se_flags & (SEF_WILD|SEF_NOWILD))) {
+ logdbg("%s: extra \"%s\" ignored",
+ slp->sl_parse->ps_cfile->pf_name, str);
+ return (0);
+ }
+ slp->sl_entry.se_flags =
+ (slp->sl_entry.se_flags & ~(SEF_WILD|SEF_NOWILD)) |
+ (*str == 'n' ? SEF_NOWILD : SEF_WILD);
+ return (0);
+}
+
+/*
+ * Handle "debug" option.
+ */
+/*ARGSUSED*/
+static int
+set_debug(struct service_list *slp, const char *str)
+{
+ slp->sl_entry.se_debug++;
+ if (ISGLOBAL(slp) && (slp->sl_parse->ps_flags & PSF_SETLEVEL)) {
+ log_level = slp->sl_entry.se_debug;
+ }
+ return (0);
+}
+
+/*
+ * Handle "nodebug" option.
+ */
+/*ARGSUSED*/
+static int
+set_nodebug(struct service_list *slp, const char *str)
+{
+ slp->sl_entry.se_flags |= SEF_DEBUGCLR;
+ slp->sl_entry.se_debug = 0;
+ if (ISGLOBAL(slp) && (slp->sl_parse->ps_flags & PSF_SETLEVEL)) {
+ log_level = slp->sl_entry.se_debug;
+ }
+ return (0);
+}
+
+/*
+ * Handle all plain string options; "server", "pppd", "path", "extra",
+ * and "log".
+ */
+static int
+set_string(struct service_list *slp, const char *str)
+{
+ char **cpp;
+
+ assert(!(slp->sl_entry.se_flags &
+ (SEF_CSERVER|SEF_CPPPD|SEF_CPATH|SEF_CEXTRA|SEF_CLOG)));
+ switch (slp->sl_parse->ps_state) {
+ case ksServer:
+ cpp = &slp->sl_entry.se_server;
+ break;
+ case ksPppd:
+ cpp = &slp->sl_entry.se_pppd;
+ break;
+ case ksPath:
+ cpp = &slp->sl_entry.se_path;
+ break;
+ case ksExtra:
+ cpp = &slp->sl_entry.se_extra;
+ break;
+ case ksLog:
+ cpp = &slp->sl_entry.se_log;
+ break;
+ default:
+ assert(0);
+ return (-1);
+ }
+ if (*cpp != NULL)
+ free(*cpp);
+ *cpp = strsave(str);
+ return (0);
+}
+
+/*
+ * Handle "file <name>" option. Close out current service (if any)
+ * and begin parsing from new file.
+ */
+static int
+set_file(struct service_list *slp, const char *str)
+{
+ FILE *fp;
+ struct per_file *pfp;
+ struct parse_state *psp;
+
+ close_service(slp);
+
+ if ((fp = fopen(str, "r")) == NULL) {
+ logwarn("%s: %s: %s", slp->sl_parse->ps_cfile->pf_name, str,
+ mystrerror(errno));
+ return (-1);
+ }
+ pfp = (struct per_file *)calloc(sizeof (*pfp) + strlen(str) + 1, 1);
+ if (pfp == NULL) {
+ logerr("no memory for parsing file %s", str);
+ (void) fclose(fp);
+ return (-1);
+ }
+ logdbg("config file %s open", str);
+
+ /* Fill in new file structure. */
+ pfp->pf_name = (const char *)(pfp+1);
+ (void) strcpy((char *)(pfp+1), str);
+ pfp->pf_input = fp;
+ psp = slp->sl_parse;
+ pfp->pf_prev = psp->ps_cfile;
+ psp->ps_cfile = pfp;
+
+ /* Start off in global context for this file. */
+ psp->ps_csvc = &pfp->pf_global;
+ pfp->pf_global.sl_parse = psp;
+ pfp->pf_global.sl_entry.se_name = "<global>";
+ return (0);
+}
+
+/*
+ * Handle "device <list>" option.
+ */
+static int
+set_device(struct service_list *slp, const char *str)
+{
+ struct parse_state *psp = slp->sl_parse;
+ struct device_list *dlp;
+ struct device_list *dln;
+ struct device_list **dlpp;
+ const char *cp;
+ int len;
+
+ /* Can't use this option in the per-device files. */
+ if (psp->ps_flags & PSF_PERDEV) {
+ logerr("\"device %s\" ignored in %s", str,
+ psp->ps_cfile->pf_name);
+ return (0);
+ }
+
+ if (strcmp(str, "*") == 0 || strcmp(str, "all") == 0) {
+ if (!(slp->sl_entry.se_flags & SEF_CDEV))
+ free_device_list(slp->sl_dev);
+ slp->sl_dev = psp->ps_star;
+ slp->sl_entry.se_flags |= SEF_CDEV;
+ } else {
+ dlpp = &dlp;
+ for (;;) {
+ while (isspace(*str) || *str == ',')
+ str++;
+ if (*str == '\0')
+ break;
+ cp = str;
+ while (*str != '\0' && !isspace(*str) && *str != ',')
+ str++;
+ len = str - cp;
+ if ((len == 1 && *cp == '*') ||
+ (len == 3 && strncmp(cp, "all", 3) == 0)) {
+ logerr("%s: cannot use %.*s in device list",
+ psp->ps_cfile->pf_name, len, cp);
+ continue;
+ }
+ dln = (struct device_list *)malloc(sizeof (*dln) +
+ len + 1);
+ if (dln == NULL) {
+ logerr("no memory for device name");
+ break;
+ }
+ dln->dl_name = (const char *)(dln + 1);
+ /* Cannot use strcpy because cp isn't terminated. */
+ (void) memcpy(dln + 1, cp, len);
+ ((char *)(dln + 1))[len] = '\0';
+ logdbg("%s: device %s", psp->ps_cfile->pf_name,
+ dln->dl_name);
+ *dlpp = dln;
+ dlpp = &dln->dl_next;
+ }
+ *dlpp = NULL;
+
+ dlpp = &slp->sl_dev;
+ if (!(slp->sl_entry.se_flags & SEF_CDEV))
+ while (*dlpp != NULL)
+ dlpp = &(*dlpp)->dl_next;
+ *dlpp = dlp;
+ slp->sl_entry.se_flags &= ~SEF_CDEV;
+ }
+
+ return (0);
+}
+
+/*
+ * Handle <list> portion of "client [except] <list>" option. Attach
+ * to list of filters in reverse order.
+ */
+static int
+set_client(struct service_list *slp, const char *str)
+{
+ struct parse_state *psp = slp->sl_parse;
+ struct filter_entry *fep;
+ struct filter_entry *fen;
+ const char *cp;
+ int len;
+ char hbuf[MAXHOSTNAMELEN];
+ struct ether_addr ea;
+ struct ether_addr mask;
+ uchar_t *ucp;
+ uchar_t *mcp;
+
+ /* Head of list. */
+ fep = slp->sl_entry.se_flist;
+ for (;;) {
+ while (isspace(*str) || *str == ',')
+ str++;
+ if (*str == '\0')
+ break;
+ cp = str;
+ while (*str != '\0' && !isspace(*str) && *str != ',')
+ str++;
+ len = str - cp;
+ (void) memcpy(hbuf, cp, len);
+ hbuf[len] = '\0';
+ mcp = mask.ether_addr_octet;
+ mcp[0] = mcp[1] = mcp[2] = mcp[3] = mcp[4] = mcp[5] = 0xFF;
+ if (ether_hostton(hbuf, &ea) != 0) {
+ ucp = ea.ether_addr_octet;
+ while (cp < str) {
+ if (ucp >= ea.ether_addr_octet + sizeof (ea))
+ break;
+ if (*cp == '*') {
+ *mcp++ = *ucp++ = 0;
+ cp++;
+ } else {
+ if (!isxdigit(*cp))
+ break;
+ *ucp = hexdecode(*cp++);
+ if (cp < str && isxdigit(*cp)) {
+ *ucp = (*ucp << 4) |
+ hexdecode(*cp++);
+ }
+ ucp++;
+ *mcp++ = 0xFF;
+ }
+ if (cp < str) {
+ if (*cp != ':' || cp + 1 == str)
+ break;
+ cp++;
+ }
+ }
+ if (cp < str) {
+ logerr("%s: illegal Ethernet address %.*s",
+ psp->ps_cfile->pf_name, len, cp);
+ continue;
+ }
+ }
+ fen = (struct filter_entry *)malloc(sizeof (*fen));
+ if (fen == NULL) {
+ logerr("unable to allocate memory for filter");
+ break;
+ }
+ fen->fe_isexcept = psp->ps_state == ksClientE;
+ fen->fe_prevcopy = 0;
+ (void) memcpy(&fen->fe_mac, &ea, sizeof (fen->fe_mac));
+ (void) memcpy(&fen->fe_mask, &mask, sizeof (fen->fe_mask));
+ fen->fe_prev = fep;
+ fep = fen;
+ }
+ slp->sl_entry.se_flist = fep;
+ return (0);
+}
+
+/*
+ * Handle "user <name>" option.
+ */
+static int
+set_user(struct service_list *slp, const char *str)
+{
+ struct passwd *pw;
+ char *cp;
+ uid_t myuid, uid;
+
+ if ((pw = getpwnam(str)) == NULL) {
+ uid = (uid_t)strtol(str, &cp, 0);
+ if (str == cp || *cp != '\0') {
+ logerr("%s: bad user name \"%s\"",
+ slp->sl_parse->ps_cfile->pf_name, str);
+ return (0);
+ }
+ } else {
+ uid = pw->pw_uid;
+ }
+ slp->sl_entry.se_uid = uid;
+ myuid = getuid();
+ if (myuid != 0) {
+ if (myuid == uid)
+ return (0);
+ logdbg("%s: not root; ignoring attempt to set UID %d (%s)",
+ slp->sl_parse->ps_cfile->pf_name, uid, str);
+ return (0);
+ }
+ slp->sl_entry.se_flags |= SEF_UIDSET;
+ return (0);
+}
+
+/*
+ * Handle "group <name>" option.
+ */
+static int
+set_group(struct service_list *slp, const char *str)
+{
+ struct group *gr;
+ char *cp;
+ gid_t gid;
+
+ if ((gr = getgrnam(str)) == NULL) {
+ gid = (gid_t)strtol(str, &cp, 0);
+ if (str == cp || *cp != '\0') {
+ logerr("%s: bad group name \"%s\"",
+ slp->sl_parse->ps_cfile->pf_name, str);
+ return (0);
+ }
+ } else {
+ gid = gr->gr_gid;
+ }
+ slp->sl_entry.se_gid = gid;
+ if (getuid() != 0) {
+ logdbg("%s: not root; ignoring attempt to set GID %d (%s)",
+ slp->sl_parse->ps_cfile->pf_name, gid, str);
+ return (0);
+ }
+ slp->sl_entry.se_flags |= SEF_GIDSET;
+ return (0);
+}
+
+/*
+ * This state machine is used to parse the configuration files. The
+ * "kwe_in" is the state in which the keyword is recognized. The
+ * "kwe_out" is the state that the keyword produces.
+ */
+struct kw_entry {
+ const char *kwe_word;
+ enum key_state kwe_in;
+ enum key_state kwe_out;
+ int (*kwe_func)(struct service_list *slp, const char *str);
+};
+
+static const struct kw_entry key_list[] = {
+ { "service", ksDefault, ksService, NULL },
+ { "device", ksDefault, ksDevice, NULL },
+ { "client", ksDefault, ksClient, NULL },
+ { "except", ksClient, ksClientE, NULL },
+ { "wildcard", ksDefault, ksDefault, set_wildcard },
+ { "nowildcard", ksDefault, ksDefault, set_wildcard },
+ { "server", ksDefault, ksServer, NULL },
+ { "pppd", ksDefault, ksPppd, NULL },
+ { "debug", ksDefault, ksDefault, set_debug },
+ { "nodebug", ksDefault, ksDefault, set_nodebug },
+ { "file", ksDefault, ksFile, NULL },
+ { "path", ksDefault, ksPath, NULL },
+ { "extra", ksDefault, ksExtra, NULL },
+ { "log", ksDefault, ksLog, NULL },
+ { "user", ksDefault, ksUser, NULL },
+ { "group", ksDefault, ksGroup, NULL },
+ /* Wildcards only past this point. */
+ { "", ksService, ksDefault, set_service },
+ { "", ksDevice, ksDefault, set_device },
+ { "", ksClient, ksDefault, set_client },
+ { "", ksClientE, ksDefault, set_client },
+ { "", ksServer, ksDefault, set_string },
+ { "", ksPppd, ksDefault, set_string },
+ { "", ksFile, ksDefault, set_file },
+ { "", ksPath, ksDefault, set_string },
+ { "", ksExtra, ksDefault, set_string },
+ { "", ksLog, ksDefault, set_string },
+ { "", ksUser, ksDefault, set_user },
+ { "", ksGroup, ksDefault, set_group },
+ { NULL, ksDefault, ksDefault, NULL }
+};
+
+/*
+ * Produce a string for the keyword that would have gotten us into the
+ * current state.
+ */
+static const char *
+after_key(enum key_state kstate)
+{
+ const struct kw_entry *kep;
+
+ for (kep = key_list; kep->kwe_word != NULL; kep++)
+ if (kep->kwe_out == kstate)
+ return (kep->kwe_word);
+ return ("nothing");
+}
+
+/*
+ * Handle end-of-file processing -- close service, close file, revert
+ * to global context in previous include file nest level.
+ */
+static void
+file_end(struct parse_state *psp)
+{
+ struct per_file *pfp;
+
+ /* Must not be in the middle of parsing a multi-word sequence now. */
+ if (psp->ps_state != ksDefault) {
+ logerr("%s ends with \"%s\"", psp->ps_cfile->pf_name,
+ after_key(psp->ps_state));
+ psp->ps_state = ksDefault;
+ }
+ close_service(psp->ps_csvc);
+ if ((pfp = psp->ps_cfile) != NULL) {
+ /* Put this file on the list of finished files. */
+ psp->ps_cfile = pfp->pf_prev;
+ pfp->pf_prev = psp->ps_files;
+ psp->ps_files = pfp;
+ if (pfp->pf_input != NULL) {
+ logdbg("file %s closed", pfp->pf_name);
+ (void) fclose(pfp->pf_input);
+ pfp->pf_input = NULL;
+ }
+
+ /* Back up to previous file, if any, and set global context. */
+ if ((pfp = psp->ps_cfile) != NULL)
+ psp->ps_csvc = &pfp->pf_global;
+ }
+}
+
+/*
+ * Dispatch a single keyword against the parser state machine or
+ * handle an environment variable assignment. The input is a string
+ * containing the single word to be dispatched.
+ */
+static int
+dispatch_keyword(struct parse_state *psp, const char *keybuf)
+{
+ const struct kw_entry *kep;
+ int retv;
+ char *cp;
+ char *env;
+ char **evlist;
+ int len;
+
+ retv = 0;
+ for (kep = key_list; kep->kwe_word != NULL; kep++) {
+ if (kep->kwe_in == psp->ps_state &&
+ (*kep->kwe_word == '\0' ||
+ strcasecmp(kep->kwe_word, keybuf) == 0)) {
+ if (kep->kwe_func != NULL)
+ retv = (*kep->kwe_func)(psp->ps_csvc, keybuf);
+ psp->ps_state = kep->kwe_out;
+ return (retv);
+ }
+ }
+ if (strchr(keybuf, '=') != NULL) {
+ if ((cp = strsave(keybuf)) == NULL) {
+ logerr("no memory to save %s", keybuf);
+ return (0);
+ }
+ len = (strchr(cp, '=') - cp) + 1;
+ if ((evlist = psp->ps_evlist) == NULL) {
+ psp->ps_evlist = evlist =
+ (char **)malloc(8 * sizeof (*evlist));
+ if (evlist == NULL) {
+ logerr("no memory for evlist");
+ free(cp);
+ return (0);
+ }
+ psp->ps_evsize = 8;
+ evlist[0] = evlist[1] = NULL;
+ } else {
+ while ((env = *evlist) != NULL) {
+ if (strncmp(cp, env, len) == 0)
+ break;
+ evlist++;
+ }
+ if (env == NULL &&
+ evlist-psp->ps_evlist >= psp->ps_evsize-1) {
+ evlist = (char **)realloc(psp->ps_evlist,
+ (psp->ps_evsize + 8) * sizeof (*evlist));
+ if (evlist == NULL) {
+ logerr("cannot realloc evlist to %d",
+ psp->ps_evsize + 8);
+ free(cp);
+ return (0);
+ }
+ psp->ps_evlist = evlist;
+ evlist += psp->ps_evsize - 1;
+ psp->ps_evsize += 8;
+ evlist[1] = NULL;
+ }
+ }
+ logdbg("setenv \"%s\"", cp);
+ if (*evlist != NULL)
+ free(*evlist);
+ *evlist = cp;
+ return (0);
+ }
+ logerr("%s: unknown keyword '%s'", psp->ps_cfile->pf_name, keybuf);
+ return (-1);
+}
+
+/*
+ * Modified version of standard getenv; looks in locally-stored
+ * environment first. This function exists because we need to be able
+ * to revert to the original environment during a reread (SIGHUP), and
+ * the putenv() function overwrites that environment.
+ */
+static char *
+my_getenv(struct parse_state *psp, char *estr)
+{
+ char **evlist, *ent;
+ int elen;
+
+ if (psp != NULL && (evlist = psp->ps_evlist) != NULL) {
+ elen = strlen(estr);
+ while ((ent = *evlist++) != NULL) {
+ if (strncmp(ent, estr, elen) == 0 &&
+ ent[elen] == '=')
+ return (ent + elen + 1);
+ }
+ }
+ return (getenv(estr));
+}
+
+/*
+ * Expand an environment variable at the end of current buffer and
+ * return pointer to next spot in buffer for character append. psp
+ * context may be null.
+ */
+static char *
+env_replace(struct parse_state *psp, char *keybuf, char kwstate)
+{
+ char *cpe;
+ char *cp;
+
+ if ((cp = strrchr(keybuf, kwstate)) != NULL) {
+ if ((cpe = my_getenv(psp, cp + 1)) != NULL) {
+ *cp = '\0';
+ (void) strncat(cp, cpe,
+ MAX_KEYWORD - (cp - keybuf) - 1);
+ keybuf[MAX_KEYWORD - 1] = '\0';
+ cp += strlen(cp);
+ } else {
+ logerr("unknown variable \"%s\"", cp + 1);
+ }
+ } else {
+ /* Should not occur. */
+ cp = keybuf + strlen(keybuf);
+ }
+ return (cp);
+}
+
+/*
+ * Given a character-at-a-time input function, get a delimited keyword
+ * from the input. This function handles the usual escape sequences,
+ * quoting, commenting, and environment variable expansion.
+ *
+ * The standard wordexp(3C) function isn't used here because the POSIX
+ * definition is hard to use, and the Solaris implementation is
+ * resource-intensive and insecure. The "hard-to-use" part is that
+ * wordexp expands only variables from the environment, and can't
+ * handle an environment overlay. Instead, the caller must use the
+ * feeble putenv/getenv interface, and rewinding to the initial
+ * environment without leaking storage is hard. The Solaris
+ * implementation invokes an undocumented extensions via
+ * fork/exec("/bin/ksh -\005 %s") for every invocation, and gathers
+ * the expanded result with pipe. This makes it slow to execute and
+ * exposes the string being expanded to users with access to "ps -f."
+ *
+ * psp may be null; it's used only for environment variable expansion.
+ * Input "flag" is 1 to ignore EOL, '#', and '$'; 0 for normal file parsing.
+ *
+ * Returns:
+ * 0 - keyword parsed.
+ * 1 - end of file; no keyword.
+ * 2 - end of file after this keyword.
+ */
+static int
+getkeyword(struct parse_state *psp, char *keybuf, int keymax,
+ int (*nextchr)(void *), void *arg, int flag)
+{
+ char varnest[MAX_NEST];
+ char *kbp;
+ char *vnp;
+ char chr;
+ int ichr;
+ char kwstate;
+ static const char escstr[] = "a\ab\bf\fn\nr\r";
+ const char *cp;
+
+ keymax--; /* Account for trailing NUL byte */
+
+ kwstate = '\0';
+ kbp = keybuf;
+ vnp = varnest;
+ for (;;) {
+ ichr = (*nextchr)(arg);
+ chr = (char)ichr;
+ tryagain:
+ switch (kwstate) {
+ case '\\': /* Start of unquoted escape sequence */
+ case '|': /* Start of escape sequence in double quotes */
+ case '~': /* Start of escape sequence in single quotes */
+ /* Convert the character if we can. */
+ if (chr == '\n')
+ chr = '\0';
+ else if (isalpha(chr) &&
+ (cp = strchr(escstr, chr)) != NULL)
+ chr = cp[1];
+ /* Revert to previous state */
+ switch (kwstate) {
+ case '\\':
+ kwstate = 'A';
+ break;
+ case '|':
+ kwstate = '"';
+ break;
+ case '~':
+ kwstate = '\'';
+ break;
+ }
+ break;
+ case '"': /* In double-quote string */
+ if (!flag && chr == '$') {
+ /* Handle variable expansion. */
+ kwstate = '%';
+ chr = '\0';
+ break;
+ }
+ /* FALLTHROUGH */
+ case '\'': /* In single-quote string */
+ if (chr == '\\') {
+ /* Handle start of escape sequence */
+ kwstate = kwstate == '"' ? '|' : '~';
+ chr = '\0';
+ break;
+ }
+ if (chr == kwstate) {
+ /* End of quoted string; revert to normal */
+ kwstate = 'A';
+ chr = '\0';
+ }
+ break;
+ case '$': /* Start of unquoted variable name */
+ case '%': /* Start of variable name in quoted string */
+ if (chr == '{') {
+ /* Variable name is bracketed. */
+ kwstate = chr =
+ kwstate == '$' ? '{' : '[';
+ break;
+ }
+ *kbp++ = kwstate = kwstate == '$' ? '+' : '*';
+ /* FALLTHROUGH */
+ case '+': /* Gathering unquoted variable name */
+ case '*': /* Gathering variable name in quoted string */
+ if (chr == '$' &&
+ vnp < varnest + sizeof (varnest)) {
+ *vnp++ = kwstate;
+ kwstate = '$';
+ chr = '\0';
+ break;
+ }
+ if (!isalnum(chr) && chr != '_' &&
+ chr != '.' && chr != '-') {
+ *kbp = '\0';
+ kbp = env_replace(psp, keybuf, kwstate);
+ if (vnp > varnest)
+ kwstate = *--vnp;
+ else
+ kwstate = kwstate == '+' ?
+ 'A' : '"';
+ /* Go reinterpret in new context */
+ goto tryagain;
+ }
+ break;
+ case '{': /* Gathering bracketed, unquoted var name */
+ case '[': /* Gathering bracketed, quoted var name */
+ if (chr == '}') {
+ *kbp = '\0';
+ kbp = env_replace(psp, keybuf, kwstate);
+ kwstate = kwstate == '{' ? 'A' : '"';
+ chr = '\0';
+ }
+ break;
+ case '#': /* Comment before word state */
+ case '@': /* Comment after word state */
+ if (chr == '\n' || chr == '\r' || ichr == EOF) {
+ /* At end of line, revert to previous state */
+ kwstate = kwstate == '#' ? '\0' : ' ';
+ chr = '\0';
+ break;
+ }
+ chr = '\0';
+ break;
+ case '\0': /* Initial state; no word seen yet. */
+ if (ichr == EOF || isspace(chr)) {
+ chr = '\0'; /* Skip over leading spaces */
+ break;
+ }
+ if (chr == '#') {
+ kwstate = '#';
+ chr = '\0'; /* Skip over comments */
+ break;
+ }
+ /* Start of keyword seen. */
+ kwstate = 'A';
+ /* FALLTHROUGH */
+ default: /* Middle of keyword parsing. */
+ if (ichr == EOF)
+ break;
+ if (isspace(chr)) { /* Space terminates word */
+ kwstate = ' ';
+ break;
+ }
+ if (chr == '"' || chr == '\'' || chr == '\\') {
+ kwstate = chr; /* Begin quote or escape */
+ chr = '\0';
+ break;
+ }
+ if (flag) /* Allow ignore; for string reparse */
+ break;
+ if (chr == '#') { /* Comment terminates word */
+ kwstate = '@'; /* Must consume comment also */
+ chr = '\0';
+ break;
+ }
+ if (chr == '$') {
+ kwstate = '$'; /* Begin variable expansion */
+ chr = '\0';
+ }
+ break;
+ }
+ /*
+ * If we've reached a space at the end of the word,
+ * then we're done.
+ */
+ if (ichr == EOF || kwstate == ' ')
+ break;
+ /*
+ * If there's a character to store and space
+ * available, then add it to the string
+ */
+ if (chr != '\0' && kbp < keybuf + keymax)
+ *kbp++ = (char)chr;
+ }
+
+ *kbp = '\0';
+
+ if (ichr == EOF) {
+ return (kwstate == '\0' ? 1 : 2);
+ }
+ return (0);
+}
+
+/*
+ * Fetch words from current file until all files are closed. Handles
+ * include files.
+ */
+static void
+parse_from_file(struct parse_state *psp)
+{
+ char keybuf[MAX_KEYWORD];
+ int retv;
+
+ while (psp->ps_cfile != NULL && psp->ps_cfile->pf_input != NULL) {
+ retv = getkeyword(psp, keybuf, sizeof (keybuf),
+ (int (*)(void *))fgetc, (void *)psp->ps_cfile->pf_input,
+ 0);
+
+ if (retv != 1)
+ (void) dispatch_keyword(psp, keybuf);
+
+ if (retv != 0)
+ file_end(psp);
+ }
+}
+
+/*
+ * Open and parse named file. This is for the predefined
+ * configuration files in /etc/ppp -- it's not an error if any of
+ * these are missing.
+ */
+static void
+parse_file(struct parse_state *psp, const char *fname)
+{
+ struct stat sb;
+
+ /* It's ok if any of these files are missing. */
+ if (stat(fname, &sb) == -1 && errno == ENOENT)
+ return;
+ if (set_file(psp->ps_csvc, fname) == 0)
+ parse_from_file(psp);
+}
+
+/*
+ * Dispatch keywords from command line. Handles any files included
+ * from there.
+ */
+static void
+parse_arg_list(struct parse_state *psp, int argc, char **argv)
+{
+ /* The first argument (program name) can be null. */
+ if (--argc <= 0)
+ return;
+ while (--argc >= 0) {
+ (void) dispatch_keyword(psp, *++argv);
+ if (psp->ps_cfile->pf_input != NULL)
+ parse_from_file(psp);
+ }
+}
+
+/* Count length of dynamic device list */
+static int
+count_devs(struct device_list *dlp)
+{
+ int ndevs;
+
+ ndevs = 0;
+ for (; dlp != NULL; dlp = dlp->dl_next)
+ ndevs++;
+ return (ndevs);
+}
+
+/* Count number of devices named in entire file. */
+static int
+count_per_file(struct per_file *pfp)
+{
+ struct service_list *slp;
+ int ndevs = 0;
+
+ for (; pfp != NULL; pfp = pfp->pf_prev) {
+ ndevs += count_devs(pfp->pf_global.sl_dev);
+ for (slp = pfp->pf_svc; slp != NULL; slp = slp->sl_next)
+ if (!(slp->sl_entry.se_flags & SEF_CDEV))
+ ndevs += count_devs(slp->sl_dev);
+ }
+ return (ndevs);
+}
+
+/* Write device names into linear array. */
+static const char **
+devs_to_list(struct device_list *dlp, const char **dnames)
+{
+ for (; dlp != NULL; dlp = dlp->dl_next)
+ *dnames++ = dlp->dl_name;
+ return (dnames);
+}
+
+/* Write all device names from file into a linear array. */
+static const char **
+per_file_to_list(struct per_file *pfp, const char **dnames)
+{
+ struct service_list *slp;
+
+ for (; pfp != NULL; pfp = pfp->pf_prev) {
+ dnames = devs_to_list(pfp->pf_global.sl_dev, dnames);
+ for (slp = pfp->pf_svc; slp != NULL; slp = slp->sl_next)
+ if (!(slp->sl_entry.se_flags & SEF_CDEV))
+ dnames = devs_to_list(slp->sl_dev, dnames);
+ }
+ return (dnames);
+}
+
+/* Compare device names; used with qsort */
+static int
+devcmp(const void *d1, const void *d2)
+{
+ return (strcmp(*(const char **)d1, *(const char **)d2));
+}
+
+/*
+ * Get sorted list of unique device names among all defined and
+ * partially defined services in all files.
+ */
+static const char **
+get_unique_devs(struct parse_state *psp)
+{
+ int ndevs;
+ const char **dnames;
+ const char **dnp;
+ const char **dnf;
+
+ /*
+ * Count number of explicitly referenced devices among all
+ * services (including duplicates).
+ */
+ ndevs = count_per_file(psp->ps_files);
+ ndevs += count_per_file(psp->ps_cfile);
+ if (ndevs <= 0) {
+ return (NULL);
+ }
+
+ /* Sort and trim out duplicate devices. */
+ dnames = (const char **)malloc((ndevs+1) * sizeof (const char *));
+ if (dnames == NULL) {
+ logerr("unable to allocate space for %d devices", ndevs + 1);
+ return (NULL);
+ }
+ dnp = per_file_to_list(psp->ps_files, dnames);
+ (void) per_file_to_list(psp->ps_cfile, dnp);
+ qsort(dnames, ndevs, sizeof (const char *), devcmp);
+ for (dnf = (dnp = dnames) + 1; dnf < dnames+ndevs; dnf++)
+ if (strcmp(*dnf, *dnp) != 0)
+ *++dnp = *dnf;
+ *++dnp = NULL;
+
+ /* Return array of pointers to names. */
+ return (dnames);
+}
+
+/*
+ * Convert data structures created by parsing process into data
+ * structures used by service dispatch. This gathers the unique
+ * device (lower stream) names and attaches the services available on
+ * each device to a list while triming duplicate services.
+ */
+static struct option_state *
+organize_state(struct parse_state *psp)
+{
+ struct per_file *pfp;
+ struct per_file *pftopp;
+ struct service_list *slp;
+ struct device_list *dlp;
+ int ndevs;
+ int nsvcs;
+ const char **dnames;
+ const char **dnp;
+ struct device_entry *dep;
+ struct option_state *osp;
+ struct service_entry **sepp;
+ struct service_entry **sebpp;
+ struct service_entry **se2pp;
+
+ /*
+ * Parsing is now done.
+ */
+ close_service(psp->ps_csvc);
+ psp->ps_csvc = NULL;
+ if ((pfp = psp->ps_cfile) != NULL) {
+ pfp->pf_prev = psp->ps_files;
+ psp->ps_files = pfp;
+ psp->ps_cfile = NULL;
+ }
+
+ /* Link the services from all files together for easy referencing. */
+ pftopp = psp->ps_files;
+ for (pfp = pftopp->pf_prev; pfp != NULL; pfp = pfp->pf_prev)
+ if (pfp->pf_svc != NULL) {
+ if (pftopp->pf_svc_last == NULL)
+ pftopp->pf_svc = pfp->pf_svc;
+ else
+ pftopp->pf_svc_last->sl_next = pfp->pf_svc;
+ pftopp->pf_svc_last = pfp->pf_svc_last;
+ pfp->pf_svc = pfp->pf_svc_last = NULL;
+ }
+
+ /*
+ * Count up number of services per device, including
+ * duplicates but not including defaults.
+ */
+ nsvcs = 0;
+ for (slp = psp->ps_files->pf_svc; slp != NULL; slp = slp->sl_next)
+ for (dlp = slp->sl_dev; dlp != NULL; dlp = dlp->dl_next)
+ nsvcs++;
+
+ /*
+ * Get the unique devices referenced by all services.
+ */
+ dnames = get_unique_devs(psp);
+ if (dnames == NULL) {
+ logdbg("no devices referenced by any service");
+ return (NULL);
+ }
+ ndevs = 0;
+ for (dnp = dnames; *dnp != NULL; dnp++)
+ ndevs++;
+
+ /*
+ * Allocate room for main structure, device records, and
+ * per-device lists. Worst case is all devices having all
+ * services; that's why we allocate for nsvcs * ndevs.
+ */
+ osp = (struct option_state *)malloc(sizeof (*osp) +
+ ndevs * sizeof (*dep) + nsvcs * ndevs * sizeof (*sepp));
+ if (osp == NULL) {
+ logerr("unable to allocate option state structure");
+ free(dnames);
+ return (NULL);
+ }
+
+ /* We're going to succeed now, so steal these over. */
+ osp->os_devices = dep = (struct device_entry *)(osp+1);
+ osp->os_pfjunk = psp->ps_files;
+ psp->ps_files = NULL;
+ osp->os_evjunk = psp->ps_evlist;
+ psp->ps_evlist = NULL;
+
+ /* Loop over devices, install services, remove duplicates. */
+ sepp = (struct service_entry **)(dep + ndevs);
+ for (dnp = dnames; *dnp != NULL; dnp++) {
+ dep->de_name = *dnp;
+ dep->de_services = (const struct service_entry **)sepp;
+ sebpp = sepp;
+ for (slp = osp->os_pfjunk->pf_svc; slp != NULL;
+ slp = slp->sl_next)
+ for (dlp = slp->sl_dev; dlp != NULL;
+ dlp = dlp->dl_next) {
+ if (dlp->dl_name == *dnp ||
+ strcmp(dlp->dl_name, *dnp) == 0) {
+ for (se2pp = sebpp; se2pp < sepp;
+ se2pp++)
+ if ((*se2pp)->se_name ==
+ slp->sl_entry.se_name ||
+ strcmp((*se2pp)->
+ se_name,
+ slp->sl_entry.
+ se_name) == 0)
+ break;
+ /*
+ * We retain a service if it's
+ * unique or if its serial
+ * number (position in the
+ * file) is greater than than
+ * any other.
+ */
+ if (se2pp >= sepp)
+ *sepp++ = &slp->sl_entry;
+ else if (SESERIAL(**se2pp) <
+ SESERIAL(slp->sl_entry))
+ *se2pp = &slp->sl_entry;
+ }
+ }
+ /* Count up the services on this device. */
+ dep->de_nservices = (const struct service_entry **)sepp -
+ dep->de_services;
+ /* Ignore devices having no services at all. */
+ if (dep->de_nservices > 0)
+ dep++;
+ }
+ /* Count up the devices. */
+ osp->os_ndevices = dep - osp->os_devices;
+ /* Free the list of device names */
+ free(dnames);
+ return (osp);
+}
+
+/*
+ * Free storage unique to a given service. Pointers copied from other
+ * services are ignored.
+ */
+static void
+free_service(struct service_list *slp)
+{
+ struct filter_entry *fep;
+ struct filter_entry *fen;
+
+ if (!(slp->sl_entry.se_flags & SEF_CDEV))
+ free_device_list(slp->sl_dev);
+ if (!(slp->sl_entry.se_flags & SEF_CFLIST)) {
+ fep = slp->sl_entry.se_flist;
+ while (fep != NULL) {
+ fen = fep->fe_prevcopy ? NULL : fep->fe_prev;
+ free(fep);
+ fep = fen;
+ }
+ }
+ if (!(slp->sl_entry.se_flags & SEF_CPPPD) &&
+ slp->sl_entry.se_pppd != NULL)
+ free(slp->sl_entry.se_pppd);
+ if (!(slp->sl_entry.se_flags & SEF_CSERVER) &&
+ slp->sl_entry.se_server != NULL)
+ free(slp->sl_entry.se_server);
+ if (!(slp->sl_entry.se_flags & SEF_CPATH) &&
+ slp->sl_entry.se_path != NULL)
+ free(slp->sl_entry.se_path);
+ if (!(slp->sl_entry.se_flags & SEF_CEXTRA) &&
+ slp->sl_entry.se_extra != NULL)
+ free(slp->sl_entry.se_extra);
+ if (!(slp->sl_entry.se_flags & SEF_CLOG) &&
+ slp->sl_entry.se_log != NULL)
+ free(slp->sl_entry.se_log);
+}
+
+/*
+ * Free a linked list of services.
+ */
+static void
+free_service_list(struct service_list *slp)
+{
+ struct service_list *sln;
+
+ while (slp != NULL) {
+ free_service(slp);
+ sln = slp->sl_next;
+ free(slp);
+ slp = sln;
+ }
+}
+
+/*
+ * Free a linked list of files and all services in those files.
+ */
+static void
+free_file_list(struct per_file *pfp)
+{
+ struct per_file *pfn;
+
+ while (pfp != NULL) {
+ free_service(&pfp->pf_global);
+ free_service_list(pfp->pf_svc);
+ pfn = pfp->pf_prev;
+ free(pfp);
+ pfp = pfn;
+ }
+}
+
+/*
+ * Free an array of local environment variables.
+ */
+static void
+free_env_list(char **evlist)
+{
+ char **evp;
+ char *env;
+
+ if ((evp = evlist) != NULL) {
+ while ((env = *evp++) != NULL)
+ free(env);
+ free(evlist);
+ }
+}
+
+/*
+ * Add a new device (lower stream) to the list for which we're the
+ * PPPoE server.
+ */
+static void
+add_new_dev(int tunfd, const char *dname)
+{
+ union ppptun_name ptn;
+
+ (void) snprintf(ptn.ptn_name, sizeof (ptn.ptn_name), "%s:pppoed",
+ dname);
+ if (strioctl(tunfd, PPPTUN_SCTL, &ptn, sizeof (ptn), 0) < 0) {
+ logerr("PPPTUN_SCTL %s: %s", ptn.ptn_name, mystrerror(errno));
+ } else {
+ logdbg("added %s", ptn.ptn_name);
+ }
+}
+
+/*
+ * Remove an existing device (lower stream) from the list for which we
+ * were the PPPoE server.
+ */
+static void
+rem_old_dev(int tunfd, const char *dname)
+{
+ union ppptun_name ptn;
+
+ (void) snprintf(ptn.ptn_name, sizeof (ptn.ptn_name), "%s:pppoed",
+ dname);
+ if (strioctl(tunfd, PPPTUN_DCTL, &ptn, sizeof (ptn), 0) < 0) {
+ logerr("PPPTUN_DCTL %s: %s", ptn.ptn_name, mystrerror(errno));
+ } else {
+ logdbg("removed %s", ptn.ptn_name);
+ }
+}
+
+/*
+ * Get a list of all of the devices currently plumbed for PPPoE. This
+ * is used for supporting the "*" and "all" device aliases.
+ */
+static void
+get_device_list(struct parse_state *psp, int tunfd)
+{
+ struct device_list *dlp;
+ struct device_list **dlpp;
+ struct device_list *dlalt;
+ struct device_list **dl2pp;
+ struct device_list *dla;
+ int i;
+ union ppptun_name ptn;
+ char *cp;
+
+ /* First pass; just allocate space for all *:pppoe* devices */
+ dlpp = &psp->ps_star;
+ dl2pp = &dlalt;
+ for (i = 0; ; i++) {
+ ptn.ptn_index = i;
+ if (strioctl(tunfd, PPPTUN_GNNAME, &ptn, sizeof (ptn),
+ sizeof (ptn)) < 0) {
+ logerr("PPPTUN_GNNAME %d: %s", i, mystrerror(errno));
+ break;
+ }
+ if (ptn.ptn_name[0] == '\0')
+ break;
+ if ((cp = strchr(ptn.ptn_name, ':')) == NULL ||
+ strncmp(cp, ":pppoe", 6) != 0 ||
+ (cp[6] != '\0' && strcmp(cp+6, "d") != 0))
+ continue;
+ *cp = '\0';
+ dlp = (struct device_list *)malloc(sizeof (*dlp) +
+ strlen(ptn.ptn_name) + 1);
+ if (dlp == NULL)
+ break;
+ dlp->dl_name = (const char *)(dlp + 1);
+ (void) strcpy((char *)(dlp + 1), ptn.ptn_name);
+ if (cp[6] == '\0') {
+ *dlpp = dlp;
+ dlpp = &dlp->dl_next;
+ } else {
+ *dl2pp = dlp;
+ dl2pp = &dlp->dl_next;
+ }
+ }
+ *dlpp = NULL;
+ *dl2pp = NULL;
+
+ /* Second pass; eliminate improperly plumbed devices */
+ for (dlpp = &psp->ps_star; (dlp = *dlpp) != NULL; ) {
+ for (dla = dlalt; dla != NULL; dla = dla->dl_next)
+ if (strcmp(dla->dl_name, dlp->dl_name) == 0)
+ break;
+ if (dla == NULL) {
+ *dlpp = dlp->dl_next;
+ free(dlp);
+ } else {
+ dlpp = &dlp->dl_next;
+ }
+ }
+ free_device_list(dlalt);
+
+ /* Add in "*" so we can always handle dynamic plumbing. */
+ dlp = (struct device_list *)malloc(sizeof (*dlp) + 2);
+ if (dlp != NULL) {
+ dlp->dl_name = (const char *)(dlp + 1);
+ (void) strcpy((char *)(dlp + 1), "*");
+ dlp->dl_next = psp->ps_star;
+ psp->ps_star = dlp;
+ }
+}
+
+/*
+ * Set logging subsystem back to configured global default values.
+ */
+void
+global_logging(void)
+{
+ log_for_service(glob_svc.se_log, glob_svc.se_debug);
+}
+
+/*
+ * Handle SIGHUP -- reparse command line and all configuration files.
+ * When reparsing is complete, free old parsed data and replace with
+ * new.
+ */
+void
+parse_options(int tunfd, int argc, char **argv)
+{
+ struct parse_state pstate;
+ struct per_file *argpf;
+ struct option_state *newopt;
+ const char **dnames;
+ const char **dnp;
+ const struct device_entry *newdep, *newmax;
+ const struct device_entry *olddep, *oldmax;
+ int cmpval;
+ struct service_entry newglobsvc, *mainsvc;
+
+ /* Note that all per_file structures must be freeable */
+ argpf = (struct per_file *)calloc(sizeof (*argpf), 1);
+ if (argpf == NULL) {
+ return;
+ }
+ (void) memset(&pstate, '\0', sizeof (pstate));
+ pstate.ps_state = ksDefault;
+ pstate.ps_cfile = argpf;
+ pstate.ps_csvc = &argpf->pf_global;
+ argpf->pf_global.sl_parse = &pstate;
+ argpf->pf_name = "command line";
+
+ /* Default is 1 -- errors only */
+ argpf->pf_global.sl_entry.se_debug++;
+ argpf->pf_global.sl_entry.se_name = "<global>";
+
+ /* Get list of all devices */
+ get_device_list(&pstate, tunfd);
+
+ /* Parse options from command line and main configuration file. */
+ pstate.ps_flags |= PSF_SETLEVEL;
+ parse_arg_list(&pstate, argc, argv);
+ parse_file(&pstate, "/etc/ppp/pppoe");
+ pstate.ps_flags &= ~PSF_SETLEVEL;
+
+ /*
+ * At this point, global options from the main configuration
+ * file are pointed to by ps_files, and options from command
+ * line are in argpf. We need to pull three special options
+ * from these -- wildcard, debug, and log. Note that the main
+ * options file overrides the command line. This is
+ * intentional. The semantics are such that the system
+ * behaves as though the main configuration file were
+ * "included" from the command line, and thus options there
+ * override the command line. This may seem odd, but at least
+ * it's self-consistent.
+ */
+ newglobsvc = argpf->pf_global.sl_entry;
+ if (pstate.ps_files != NULL) {
+ mainsvc = &pstate.ps_files->pf_global.sl_entry;
+ if (mainsvc->se_log != NULL)
+ newglobsvc.se_log = mainsvc->se_log;
+ if (mainsvc->se_flags & (SEF_WILD|SEF_NOWILD))
+ newglobsvc.se_flags =
+ (newglobsvc.se_flags & ~(SEF_WILD|SEF_NOWILD)) |
+ (mainsvc->se_flags & (SEF_WILD|SEF_NOWILD));
+ if (mainsvc->se_flags & SEF_DEBUGCLR)
+ newglobsvc.se_debug = 0;
+ newglobsvc.se_debug += mainsvc->se_debug;
+ }
+ glob_svc = newglobsvc;
+ global_logging();
+
+ /* Get the list of devices referenced by configuration above. */
+ dnames = get_unique_devs(&pstate);
+ if (dnames != NULL) {
+ /* Read per-device configuration files. */
+ pstate.ps_flags |= PSF_PERDEV;
+ for (dnp = dnames; *dnp != NULL; dnp++)
+ parse_file(&pstate, *dnp);
+ pstate.ps_flags &= ~PSF_PERDEV;
+ free(dnames);
+ }
+ file_end(&pstate);
+
+ /*
+ * Convert parsed data structures into per-device structures.
+ * (Invert the table.)
+ */
+ newopt = organize_state(&pstate);
+
+ /* If we're going to free the file name, then stop logging there. */
+ if (newopt == NULL && glob_svc.se_log != NULL) {
+ glob_svc.se_log = NULL;
+ global_logging();
+ }
+
+ /*
+ * Unless an error has occurred, these pointers are normally
+ * all NULL. Nothing is freed until the file is re-read.
+ */
+ free_file_list(pstate.ps_files);
+ free_file_list(pstate.ps_cfile);
+ free_device_list(pstate.ps_star);
+ free_env_list(pstate.ps_evlist);
+
+ /*
+ * Match up entries on device list. Detach devices no longer
+ * referenced. Attach ones now referenced. (The use of null
+ * pointers here may look fishy, but it actually works.
+ * NULL>=NULL is always true.)
+ */
+ if (newopt != NULL) {
+ newdep = newopt->os_devices;
+ newmax = newdep + newopt->os_ndevices;
+ } else {
+ newdep = newmax = NULL;
+ }
+ if (cur_options != NULL) {
+ olddep = cur_options->os_devices;
+ oldmax = olddep + cur_options->os_ndevices;
+ } else {
+ olddep = oldmax = NULL;
+ }
+ while ((newdep != NULL && newdep < newmax) ||
+ (olddep != NULL && olddep < oldmax)) {
+ if (newdep < newmax) {
+ if (olddep >= oldmax) {
+ add_new_dev(tunfd, newdep->de_name);
+ newdep++;
+ } else {
+ cmpval = strcmp(newdep->de_name,
+ olddep->de_name);
+ if (cmpval < 0) {
+ /* Brand new device seen. */
+ add_new_dev(tunfd, newdep->de_name);
+ newdep++;
+ } else if (cmpval == 0) {
+ /* Existing device; skip it. */
+ newdep++;
+ olddep++;
+ }
+ /* No else clause -- removal is below */
+ }
+ }
+ if (olddep < oldmax) {
+ if (newdep >= newmax) {
+ rem_old_dev(tunfd, olddep->de_name);
+ olddep++;
+ } else {
+ cmpval = strcmp(newdep->de_name,
+ olddep->de_name);
+ if (cmpval > 0) {
+ /* Old device is gone */
+ rem_old_dev(tunfd, olddep->de_name);
+ olddep++;
+ } else if (cmpval == 0) {
+ /* Existing device; skip it. */
+ newdep++;
+ olddep++;
+ }
+ /* No else clause -- insert handled above */
+ }
+ }
+ }
+
+ /* Discard existing parsed data storage. */
+ if (cur_options != NULL) {
+ free_file_list(cur_options->os_pfjunk);
+ free_env_list(cur_options->os_evjunk);
+ free(cur_options);
+ }
+ /* Install new. */
+ cur_options = newopt;
+}
+
+/*
+ * Check if configured filters permit requesting client to use a given
+ * service. Note -- filters are stored in reverse order in order to
+ * make file-inclusion work as expected. Thus, the "first match"
+ * filter rule becomes "last match" here.
+ */
+static boolean_t
+allow_service(const struct service_entry *sep, const ppptun_atype *pap)
+{
+ const struct filter_entry *fep;
+ const struct filter_entry *lmatch;
+ boolean_t anynonexcept = B_FALSE;
+ const uchar_t *upt;
+ const uchar_t *macp;
+ const uchar_t *maskp;
+ int i;
+
+ lmatch = NULL;
+ for (fep = sep->se_flist; fep != NULL; fep = fep->fe_prev) {
+ anynonexcept |= !fep->fe_isexcept;
+ upt = pap->pta_pppoe.ptma_mac;
+ macp = fep->fe_mac.ether_addr_octet;
+ maskp = fep->fe_mask.ether_addr_octet;
+ for (i = sizeof (pap->pta_pppoe.ptma_mac); i > 0; i--)
+ if (((*macp++ ^ *upt++) & *maskp++) != 0)
+ break;
+ if (i <= 0)
+ lmatch = fep;
+ }
+
+ if (lmatch == NULL) {
+ /*
+ * Assume reject by default if any positive-match
+ * (non-except) filters are given. Otherwise, if
+ * there are no positive-match filters, then
+ * non-matching means accept by default.
+ */
+ return (!anynonexcept);
+ }
+ return (!lmatch->fe_isexcept);
+}
+
+/*
+ * Locate available service(s) based on client request. Assumes that
+ * outp points to a buffer of at least size PPPOE_MSGMAX. Creates a
+ * PPPoE response message in outp. Returns count of matched services
+ * and (through *srvp) a pointer to the last (or only) service. If
+ * some error is found in the request, an error string is added and -1
+ * is returned; the caller should just send the message without
+ * alteration.
+ */
+int
+locate_service(poep_t *poep, int plen, const char *iname, ppptun_atype *pap,
+ uint32_t *outp, void **srvp)
+{
+ poep_t *opoe;
+ const uint8_t *tagp;
+ const char *cp;
+ int ttyp;
+ int tlen;
+ int nsvcs;
+ const struct device_entry *dep, *depe;
+ const struct device_entry *wdep;
+ const struct service_entry **sepp, **seppe;
+ const struct service_entry *sep;
+ char *str;
+ boolean_t ispadi;
+
+ ispadi = poep->poep_code == POECODE_PADI;
+ opoe = poe_mkheader(outp, ispadi ? POECODE_PADO : POECODE_PADS, 0);
+
+ *srvp = NULL;
+ if (cur_options == NULL)
+ return (0);
+
+ /* Search for named device (lower stream) in tables. */
+ dep = cur_options->os_devices;
+ depe = dep + cur_options->os_ndevices;
+ wdep = NULL;
+ if ((cp = strchr(iname, ':')) != NULL)
+ tlen = cp - iname;
+ else
+ tlen = strlen(iname);
+ for (; dep < depe; dep++)
+ if (strncmp(iname, dep->de_name, tlen) == 0 &&
+ dep->de_name[tlen] == '\0')
+ break;
+ else if (dep->de_name[0] == '*' && dep->de_name[1] == '\0')
+ wdep = dep;
+ if (dep >= depe)
+ dep = wdep;
+ /*
+ * Return if interface not found. Zero-service case can't
+ * occur, since devices with no services aren't included in
+ * the list, but the code is just being safe here.
+ */
+ if (dep == NULL || dep->de_services == NULL || dep->de_nservices <= 0)
+ return (0);
+
+ /*
+ * Loop over tags in client message and process them.
+ * Services must be matched against our list. Host-Uniq and
+ * Relay-Session-Id must be copied to the reply. All others
+ * must be discarded.
+ */
+ nsvcs = 0;
+ sepp = dep->de_services;
+ tagp = (const uint8_t *)(poep + 1);
+ while (poe_tagcheck(poep, plen, tagp)) {
+ ttyp = POET_GET_TYPE(tagp);
+ if (ttyp == POETT_END)
+ break;
+ tlen = POET_GET_LENG(tagp);
+ switch (ttyp) {
+ case POETT_SERVICE: /* Service-Name */
+ /*
+ * Allow only one. (Note that this test works
+ * because there's always at least one service
+ * per device; otherwise, the device is
+ * removed from the list.)
+ */
+ if (sepp != dep->de_services) {
+ if (nsvcs != -1)
+ (void) poe_add_str(opoe, POETT_NAMERR,
+ "Too many Service-Name tags");
+ nsvcs = -1;
+ break;
+ }
+ seppe = sepp + dep->de_nservices;
+ /* Clients's requested service must appear in reply. */
+ if (tlen != 0 || (ispadi &&
+ !(glob_svc.se_flags & SEF_NOWILD)))
+ (void) poe_tag_copy(opoe, tagp);
+ if (tlen == 0) {
+ /*
+ * If config specifies "nowild" in a
+ * global context, then we don't
+ * respond to wildcard PADRs. The
+ * client must know the exact service
+ * name to get access.
+ */
+
+ if (!ispadi && (glob_svc.se_flags & SEF_NOWILD))
+ sepp = seppe;
+ while (sepp < seppe) {
+ sep = *sepp++;
+ if ((ispadi || !(sep->se_flags &
+ SEF_NOWILD)) &&
+ allow_service(sep, pap)) {
+ nsvcs++;
+ *srvp = (void *)sep;
+ if (poep->poep_code ==
+ POECODE_PADR)
+ break;
+ if (sep->se_name[0] == '\0')
+ continue;
+ (void) poe_add_str(opoe,
+ POETT_SERVICE,
+ sep->se_name);
+ }
+ }
+ } else {
+ /* Requested specific service; find it. */
+ cp = (char *)POET_DATA(tagp);
+ while (sepp < seppe) {
+ sep = *sepp++;
+ if (strlen(sep->se_name) == tlen &&
+ strncasecmp(sep->se_name, cp,
+ tlen) == 0) {
+ if (allow_service(sep, pap)) {
+ nsvcs++;
+ *srvp = (void *)sep;
+ }
+ break;
+ }
+ }
+ }
+ /*
+ * Allow service definition to override
+ * AC-Name (concentrator [server] name) field.
+ */
+ if (*srvp != NULL) {
+ sep = (const struct service_entry *)*srvp;
+ log_for_service(sep->se_log, sep->se_debug);
+ str = "Solaris PPPoE";
+ if (sep->se_server != NULL)
+ str = sep->se_server;
+ (void) poe_add_str(opoe, POETT_ACCESS, str);
+ }
+ break;
+ /* Ones we should discard */
+ case POETT_ACCESS: /* AC-Name */
+ case POETT_COOKIE: /* AC-Cookie */
+ case POETT_NAMERR: /* Service-Name-Error */
+ case POETT_SYSERR: /* AC-System-Error */
+ case POETT_GENERR: /* Generic-Error */
+ case POETT_HURL: /* Host-URL */
+ case POETT_MOTM: /* Message-Of-The-Minute */
+ case POETT_RTEADD: /* IP-Route-Add */
+ case POETT_VENDOR: /* Vendor-Specific */
+ case POETT_MULTI: /* Multicast-Capable */
+ default:
+ break;
+ /* Ones we should copy */
+ case POETT_UNIQ: /* Host-Uniq */
+ case POETT_RELAY: /* Relay-Session-Id */
+ (void) poe_tag_copy(opoe, tagp);
+ break;
+ }
+ tagp = POET_NEXT(tagp);
+ }
+ return (nsvcs);
+}
+
+/*
+ * Like fgetc, but reads from a string.
+ */
+static int
+sgetc(void *arg)
+{
+ char **cpp = (char **)arg;
+ if (**cpp == '\0')
+ return (EOF);
+ return (*(*cpp)++);
+}
+
+/*
+ * Given a service structure, launch pppd. Called by handle_input()
+ * in pppoed.c if locate_service() [above] finds exactly one service
+ * matching a PADR.
+ */
+int
+launch_service(int tunfd, poep_t *poep, void *srvp, struct ppptun_control *ptc)
+{
+ const struct service_entry *sep = (const struct service_entry *)srvp;
+ const char *path;
+ const char *extra;
+ const char *pppd;
+ const char *cp;
+ pid_t pidv;
+ int newtun;
+ struct ppptun_peer ptp;
+ union ppptun_name ptn;
+ const char *args[MAXARGS];
+ struct strbuf ctrl;
+ struct strbuf data;
+ const char **cpp;
+ char *sptr;
+ char *spv;
+ int slen;
+ int retv;
+ char keybuf[MAX_KEYWORD];
+
+ assert(sep != NULL);
+
+ /* Get tunnel driver connection for new PPP session. */
+ newtun = open(tunnam, O_RDWR);
+ if (newtun == -1)
+ goto syserr;
+
+ /* Set this session up for standard PPP and client's address. */
+ (void) memset(&ptp, '\0', sizeof (ptp));
+ ptp.ptp_style = PTS_PPPOE;
+ ptp.ptp_address = ptc->ptc_address;
+ if (strioctl(newtun, PPPTUN_SPEER, &ptp, sizeof (ptp), sizeof (ptp)) <
+ 0)
+ goto syserr;
+ ptp.ptp_rsessid = ptp.ptp_lsessid;
+ if (strioctl(newtun, PPPTUN_SPEER, &ptp, sizeof (ptp), sizeof (ptp)) <
+ 0)
+ goto syserr;
+
+ /* Attach the requested lower stream. */
+ cp = strchr(ptc->ptc_name, ':');
+ if (cp == NULL)
+ cp = ptc->ptc_name + strlen(ptc->ptc_name);
+ (void) snprintf(ptn.ptn_name, sizeof (ptn.ptn_name), "%.*s:pppoe",
+ cp-ptc->ptc_name, ptc->ptc_name);
+ if (strioctl(newtun, PPPTUN_SDATA, &ptn, sizeof (ptn), 0) < 0)
+ goto syserr;
+ (void) snprintf(ptn.ptn_name, sizeof (ptn.ptn_name), "%.*s:pppoed",
+ cp-ptc->ptc_name, ptc->ptc_name);
+ if (strioctl(newtun, PPPTUN_SCTL, &ptn, sizeof (ptn), 0) < 0)
+ goto syserr;
+
+ pidv = fork();
+ if (pidv == (pid_t)-1)
+ goto syserr;
+
+ if (pidv == (pid_t)0) {
+ /*
+ * Use syslog only in order to avoid mixing log messages
+ * in regular files.
+ */
+ close_log_files();
+
+ if ((path = sep->se_path) == NULL)
+ path = "/usr/bin/pppd";
+ if ((extra = sep->se_extra) == NULL)
+ extra = "plugin pppoe.so directtty";
+ if ((pppd = sep->se_pppd) == NULL)
+ pppd = "";
+
+ /* Concatenate these. */
+ slen = strlen(path) + strlen(extra) + strlen(pppd) + 3;
+ if ((sptr = (char *)malloc(slen)) == NULL)
+ goto bail_out;
+ (void) strcpy(sptr, path);
+ (void) strcat(sptr, " ");
+ (void) strcat(sptr, extra);
+ (void) strcat(sptr, " ");
+ (void) strcat(sptr, pppd);
+
+ /* Parse out into arguments */
+ cpp = args;
+ spv = sptr;
+ while (cpp < args + MAXARGS - 1) {
+ retv = getkeyword(NULL, keybuf, sizeof (keybuf), sgetc,
+ (void *)&spv, 1);
+ if (retv != 1)
+ *cpp++ = strsave(keybuf);
+ if (retv != 0)
+ break;
+ }
+ *cpp = NULL;
+ if (cpp == args)
+ goto bail_out;
+
+ /*
+ * Fix tunnel device on stdin/stdout and error file on
+ * stderr.
+ */
+ if (newtun != 0 && dup2(newtun, 0) < 0)
+ goto bail_out;
+ if (newtun != 1 && dup2(newtun, 1) < 0)
+ goto bail_out;
+ if (newtun > 1)
+ (void) close(newtun);
+ if (tunfd > 1)
+ (void) close(tunfd);
+ (void) close(2);
+ (void) open("/etc/ppp/pppoe-errors", O_WRONLY | O_APPEND |
+ O_CREAT, 0600);
+
+ /*
+ * Change GID first, for obvious reasons. Note that
+ * we log any problems to syslog, not the errors file.
+ * The errors file is intended for problems in the
+ * exec'd program.
+ */
+ if ((sep->se_flags & SEF_GIDSET) &&
+ setgid(sep->se_gid) == -1) {
+ cp = mystrerror(errno);
+ reopen_log();
+ logerr("setgid(%d): %s", sep->se_gid, cp);
+ goto logged;
+ }
+ if ((sep->se_flags & SEF_UIDSET) &&
+ setuid(sep->se_uid) == -1) {
+ cp = mystrerror(errno);
+ reopen_log();
+ logerr("setuid(%d): %s", sep->se_uid, cp);
+ goto logged;
+ }
+
+ /* Run pppd */
+ path = args[0];
+ cp = strrchr(args[0], '/');
+ if (cp != NULL && cp[1] != '\0')
+ args[0] = cp+1;
+ errno = 0;
+ (void) execv(path, (char * const *)args);
+ newtun = 0;
+
+ /*
+ * Exec failure; attempt to log the problem and send a
+ * PADT to the client so that he knows the session
+ * went south.
+ */
+ bail_out:
+ cp = mystrerror(errno);
+ reopen_log();
+ logerr("\"%s\": %s", (sptr == NULL ? path : sptr), cp);
+ logged:
+ poep = poe_mkheader(pkt_output, POECODE_PADT, ptp.ptp_lsessid);
+ poep->poep_session_id = htons(ptp.ptp_lsessid);
+ (void) poe_add_str(poep, POETT_SYSERR, cp);
+ (void) sleep(1);
+ ctrl.len = sizeof (*ptc);
+ ctrl.buf = (caddr_t)ptc;
+ data.len = poe_length(poep) + sizeof (*poep);
+ data.buf = (caddr_t)poep;
+ if (putmsg(newtun, &ctrl, &data, 0) < 0) {
+ logerr("putmsg %s: %s", ptc->ptc_name,
+ mystrerror(errno));
+ }
+ exit(1);
+ }
+
+ (void) close(newtun);
+
+ /* Give session ID to client in reply. */
+ poep->poep_session_id = htons(ptp.ptp_lsessid);
+ return (1);
+
+syserr:
+ /* Peer doesn't know session ID yet; hope for the best. */
+ retv = errno;
+ if (newtun >= 0)
+ (void) close(newtun);
+ (void) poe_add_str(poep, POETT_SYSERR, mystrerror(retv));
+ return (0);
+}
+
+/*
+ * This is pretty awful -- it uses recursion to print a simple list.
+ * It's just for debug, though, and does a reasonable job of printing
+ * the filters in the right order.
+ */
+static void
+print_filter_list(FILE *fp, struct filter_entry *fep)
+{
+ if (fep->fe_prev != NULL)
+ print_filter_list(fp, fep->fe_prev);
+ (void) fprintf(fp, "\t\t MAC %s", ehost2(&fep->fe_mac));
+ (void) fprintf(fp, ", mask %s%s\n", ehost2(&fep->fe_mask),
+ (fep->fe_isexcept ? ", except" : ""));
+}
+
+/*
+ * Write summary of parsed configuration data to given file.
+ */
+void
+dump_configuration(FILE *fp)
+{
+ const struct device_entry *dep;
+ const struct service_entry *sep, **sepp;
+ struct per_file *pfp;
+ int i, j;
+
+ (void) fprintf(fp, "Will%s respond to wildcard queries.\n",
+ (glob_svc.se_flags & SEF_NOWILD) ? " not" : "");
+ (void) fprintf(fp,
+ "Global debug level %d, log to %s; current level %d\n",
+ glob_svc.se_debug,
+ ((glob_svc.se_log == NULL || *glob_svc.se_log == '\0') ?
+ "syslog" : glob_svc.se_log),
+ log_level);
+ if (cur_options == NULL) {
+ (void) fprintf(fp, "No current configuration.\n");
+ return;
+ }
+ (void) fprintf(fp, "Current configuration:\n");
+ (void) fprintf(fp, " %d device(s):\n", cur_options->os_ndevices);
+ dep = cur_options->os_devices;
+ for (i = 0; i < cur_options->os_ndevices; i++, dep++) {
+ (void) fprintf(fp, "\t%s: %d service(s):\n",
+ dep->de_name, dep->de_nservices);
+ sepp = dep->de_services;
+ for (j = 0; j < dep->de_nservices; j++, sepp++) {
+ sep = *sepp;
+ (void) fprintf(fp, "\t %s: debug level %d",
+ sep->se_name, sep->se_debug);
+ if (sep->se_flags & SEF_UIDSET)
+ (void) fprintf(fp, ", UID %ld", sep->se_uid);
+ if (sep->se_flags & SEF_GIDSET)
+ (void) fprintf(fp, ", GID %ld", sep->se_gid);
+ if (sep->se_flags & SEF_WILD)
+ (void) fprintf(fp, ", wildcard");
+ else if (sep->se_flags & SEF_NOWILD)
+ (void) fprintf(fp, ", nowildcard");
+ else
+ (void) fprintf(fp, ", wildcard (default)");
+ (void) putc('\n', fp);
+ if (sep->se_server != NULL)
+ (void) fprintf(fp, "\t\tserver \"%s\"\n",
+ sep->se_server);
+ if (sep->se_pppd != NULL)
+ (void) fprintf(fp, "\t\tpppd \"%s\"\n",
+ sep->se_pppd);
+ if (sep->se_path != NULL)
+ (void) fprintf(fp, "\t\tpath \"%s\"\n",
+ sep->se_path);
+ if (sep->se_extra != NULL)
+ (void) fprintf(fp, "\t\textra \"%s\"\n",
+ sep->se_extra);
+ if (sep->se_log != NULL)
+ (void) fprintf(fp, "\t\tlog \"%s\"\n",
+ sep->se_log);
+ if (sep->se_flist != NULL) {
+ (void) fprintf(fp, "\t\tfilter list:\n");
+ print_filter_list(fp, sep->se_flist);
+ }
+ }
+ }
+ (void) fprintf(fp, "\nConfiguration read from:\n");
+ for (pfp = cur_options->os_pfjunk; pfp != NULL; pfp = pfp->pf_prev) {
+ (void) fprintf(fp, " %s: %d service(s)\n", pfp->pf_name,
+ pfp->pf_nsvc);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/pppoe/pppoec.c b/usr/src/cmd/cmd-inet/usr.lib/pppoe/pppoec.c
new file mode 100644
index 0000000000..f306b718e4
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/pppoe/pppoec.c
@@ -0,0 +1,1673 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * PPPoE Client-mode "chat" utility for use with Solaris PPP 4.0.
+ *
+ * Copyright 2000-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <strings.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <stropts.h>
+#include <netdb.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include <net/sppptun.h>
+#include <net/pppoe.h>
+
+#include "common.h"
+#include "logging.h"
+
+/*
+ * This value, currently set to the characters "POE1," is used to
+ * distinguish among control messages from multiple lower streams
+ * under /dev/sppp. This feature is needed to support PPP translation
+ * (LAC-like behavior), but isn't currently used.
+ */
+#define PPPOE_DISCRIM 0x504F4531
+
+/* milliseconds between retries */
+#define PADI_RESTART_TIME 500
+#define PADR_RESTART_TIME 2000
+
+/* default inquiry mode timer in milliseconds. */
+#define PADI_INQUIRY_DWELL 3000
+
+/* maximum timer value in milliseconds */
+#define RESTART_LIMIT 5000
+
+char *myname; /* copy of argv[0] for error messages */
+static int verbose; /* -v flag given */
+static int onlyflag; /* keyword "only" at end of command line */
+static char *service = ""; /* saved service name from command line */
+
+static int pado_wait_time = 0; /* see main() */
+static int pads_wait_time = PADR_RESTART_TIME;
+
+static int tunfd; /* open connection to sppptun driver */
+
+static struct timeval tvstart; /* time of last PADI/PADR transmission */
+
+struct server_filter {
+ struct server_filter *sf_next; /* Next filter in list */
+ struct ether_addr sf_mac; /* Ethernet address */
+ struct ether_addr sf_mask; /* Mask (0 or 0xFF in each byte) */
+ const char *sf_name; /* String for AC-Name compare */
+ boolean_t sf_hasmac; /* Set if string could be MAC */
+ boolean_t sf_isexcept; /* Ignore server if matching */
+};
+
+/* List of filters defined on command line. */
+static struct server_filter *sfhead, *sftail;
+
+/*
+ * PPPoE Client State Machine
+ */
+
+/* Client events */
+#define PCSME_CLOSE 0 /* User close */
+#define PCSME_OPEN 1 /* User open */
+#define PCSME_TOP 2 /* Timeout+ (counter non-zero) */
+#define PCSME_TOM 3 /* Timeout- (counter zero) */
+#define PCSME_RPADT 4 /* Receive PADT (unexpected here) */
+#define PCSME_RPADOP 5 /* Receive desired PADO */
+#define PCSME_RPADO 6 /* Receive ordinary PADO */
+#define PCSME_RPADS 7 /* Receive PADS */
+#define PCSME_RPADSN 8 /* Receive bad (errored) PADS */
+#define PCSME__MAX 9
+
+/* Client states */
+#define PCSMS_DEAD 0 /* Initial state */
+#define PCSMS_INITSENT 1 /* PADI sent */
+#define PCSMS_OFFRRCVD 2 /* PADO received */
+#define PCSMS_REQSENT 3 /* PADR sent */
+#define PCSMS_CONVERS 4 /* Conversational */
+#define PCSMS__MAX 5
+
+/* Client actions */
+#define PCSMA_NONE 0 /* Do nothing */
+#define PCSMA_FAIL 1 /* Unrecoverable error */
+#define PCSMA_SPADI 2 /* Send PADI */
+#define PCSMA_ADD 3 /* Add ordinary server to list */
+#define PCSMA_SPADR 4 /* Send PADR to top server */
+#define PCSMA_SPADRP 5 /* Send PADR to this server (make top) */
+#define PCSMA_SPADRN 6 /* Send PADR to next (or terminate) */
+#define PCSMA_OPEN 7 /* Start PPP */
+#define PCSMA__MAX 8
+
+static uint8_t client_next_state[PCSMS__MAX][PCSME__MAX] = {
+/* 0 PCSMS_DEAD Initial state */
+ {
+ PCSMS_DEAD, /* PCSME_CLOSE User close */
+ PCSMS_INITSENT, /* PCSME_OPEN User open */
+ PCSMS_DEAD, /* PCSME_TOP Timeout+ */
+ PCSMS_DEAD, /* PCSME_TOM Timeout- */
+ PCSMS_DEAD, /* PCSME_RPADT Receive PADT */
+ PCSMS_DEAD, /* PCSME_RPADOP Receive desired PADO */
+ PCSMS_DEAD, /* PCSME_RPADO Receive ordinary PADO */
+ PCSMS_DEAD, /* PCSME_RPADS Receive PADS */
+ PCSMS_DEAD, /* PCSME_RPADSN Receive bad PADS */
+ },
+/* 1 PCSMS_INITSENT PADI sent */
+ {
+ PCSMS_DEAD, /* PCSME_CLOSE User close */
+ PCSMS_INITSENT, /* PCSME_OPEN User open */
+ PCSMS_INITSENT, /* PCSME_TOP Timeout+ */
+ PCSMS_DEAD, /* PCSME_TOM Timeout- */
+ PCSMS_DEAD, /* PCSME_RPADT Receive PADT */
+ PCSMS_REQSENT, /* PCSME_RPADOP Receive desired PADO */
+ PCSMS_OFFRRCVD, /* PCSME_RPADO Receive ordinary PADO */
+ PCSMS_INITSENT, /* PCSME_RPADS Receive PADS */
+ PCSMS_INITSENT, /* PCSME_RPADSN Receive bad PADS */
+ },
+/* 2 PCSMS_OFFRRCVD PADO received */
+ {
+ PCSMS_DEAD, /* PCSME_CLOSE User close */
+ PCSMS_INITSENT, /* PCSME_OPEN User open */
+ PCSMS_REQSENT, /* PCSME_TOP Timeout+ */
+ PCSMS_REQSENT, /* PCSME_TOM Timeout- */
+ PCSMS_DEAD, /* PCSME_RPADT Receive PADT */
+ PCSMS_REQSENT, /* PCSME_RPADOP Receive desired PADO */
+ PCSMS_OFFRRCVD, /* PCSME_RPADO Receive ordinary PADO */
+ PCSMS_OFFRRCVD, /* PCSME_RPADS Receive PADS */
+ PCSMS_OFFRRCVD, /* PCSME_RPADSN Receive bad PADS */
+ },
+/* 3 PCSMS_REQSENT PADR sent */
+ {
+ PCSMS_DEAD, /* PCSME_CLOSE User close */
+ PCSMS_INITSENT, /* PCSME_OPEN User open */
+ PCSMS_REQSENT, /* PCSME_TOP Timeout+ */
+ PCSMS_REQSENT, /* PCSME_TOM Timeout- */
+ PCSMS_DEAD, /* PCSME_RPADT Receive PADT */
+ PCSMS_REQSENT, /* PCSME_RPADOP Receive desired PADO */
+ PCSMS_REQSENT, /* PCSME_RPADO Receive ordinary PADO */
+ PCSMS_CONVERS, /* PCSME_RPADS Receive PADS */
+ PCSMS_REQSENT, /* PCSME_RPADSN Receive bad PADS */
+ },
+/* 4 PCSMS_CONVERS Conversational */
+ {
+ PCSMS_DEAD, /* PCSME_CLOSE User close */
+ PCSMS_INITSENT, /* PCSME_OPEN User open */
+ PCSMS_CONVERS, /* PCSME_TOP Timeout+ */
+ PCSMS_CONVERS, /* PCSME_TOM Timeout- */
+ PCSMS_DEAD, /* PCSME_RPADT Receive PADT */
+ PCSMS_CONVERS, /* PCSME_RPADOP Receive desired PADO */
+ PCSMS_CONVERS, /* PCSME_RPADO Receive ordinary PADO */
+ PCSMS_CONVERS, /* PCSME_RPADS Receive PADS */
+ PCSMS_CONVERS, /* PCSME_RPADSN Receive bad PADS */
+ },
+};
+
+static uint8_t client_action[PCSMS__MAX][PCSME__MAX] = {
+/* 0 PCSMS_DEAD Initial state */
+ {
+ PCSMA_NONE, /* PCSME_CLOSE User close */
+ PCSMA_SPADI, /* PCSME_OPEN User open */
+ PCSMA_NONE, /* PCSME_TOP Timeout+ */
+ PCSMA_NONE, /* PCSME_TOM Timeout- */
+ PCSMA_NONE, /* PCSME_RPADT Receive PADT */
+ PCSMA_NONE, /* PCSME_RPADOP Receive desired PADO */
+ PCSMA_NONE, /* PCSME_RPADO Receive ordinary PADO */
+ PCSMA_NONE, /* PCSME_RPADS Receive PADS */
+ PCSMA_NONE, /* PCSME_RPADSN Receive bad PADS */
+ },
+/* 1 PCSMS_INITSENT PADI sent */
+ {
+ PCSMA_FAIL, /* PCSME_CLOSE User close */
+ PCSMA_SPADI, /* PCSME_OPEN User open */
+ PCSMA_SPADI, /* PCSME_TOP Timeout+ */
+ PCSMA_FAIL, /* PCSME_TOM Timeout- */
+ PCSMA_FAIL, /* PCSME_RPADT Receive PADT */
+ PCSMA_SPADRP, /* PCSME_RPADOP Receive desired PADO */
+ PCSMA_ADD, /* PCSME_RPADO Receive ordinary PADO */
+ PCSMA_NONE, /* PCSME_RPADS Receive PADS */
+ PCSMA_NONE, /* PCSME_RPADSN Receive bad PADS */
+ },
+/* 2 PCSMS_OFFRRCVD PADO received */
+ {
+ PCSMA_FAIL, /* PCSME_CLOSE User close */
+ PCSMA_SPADI, /* PCSME_OPEN User open */
+ PCSMA_SPADR, /* PCSME_TOP Timeout+ */
+ PCSMA_SPADR, /* PCSME_TOM Timeout- */
+ PCSMA_FAIL, /* PCSME_RPADT Receive PADT */
+ PCSMA_SPADRP, /* PCSME_RPADOP Receive desired PADO */
+ PCSMA_ADD, /* PCSME_RPADO Receive ordinary PADO */
+ PCSMA_NONE, /* PCSME_RPADS Receive PADS */
+ PCSMA_NONE, /* PCSME_RPADSN Receive bad PADS */
+ },
+/* 3 PCSMS_REQSENT PADR sent */
+ {
+ PCSMA_FAIL, /* PCSME_CLOSE User close */
+ PCSMA_SPADI, /* PCSME_OPEN User open */
+ PCSMA_SPADR, /* PCSME_TOP Timeout+ */
+ PCSMA_SPADRN, /* PCSME_TOM Timeout- */
+ PCSMA_FAIL, /* PCSME_RPADT Receive PADT */
+ PCSMA_ADD, /* PCSME_RPADOP Receive desired PADO */
+ PCSMA_ADD, /* PCSME_RPADO Receive ordinary PADO */
+ PCSMA_OPEN, /* PCSME_RPADS Receive PADS */
+ PCSMA_SPADRN, /* PCSME_RPADSN Receive bad PADS */
+ },
+/* 4 PCSMS_CONVERS Conversational */
+ {
+ PCSMA_FAIL, /* PCSME_CLOSE User close */
+ PCSMA_SPADI, /* PCSME_OPEN User open */
+ PCSMA_FAIL, /* PCSME_TOP Timeout+ */
+ PCSMA_FAIL, /* PCSME_TOM Timeout- */
+ PCSMA_FAIL, /* PCSME_RPADT Receive PADT */
+ PCSMA_NONE, /* PCSME_RPADOP Receive desired PADO */
+ PCSMA_NONE, /* PCSME_RPADO Receive ordinary PADO */
+ PCSMA_NONE, /* PCSME_RPADS Receive PADS */
+ PCSMA_NONE, /* PCSME_RPADSN Receive bad PADS */
+ },
+};
+
+/*
+ * PPPoE Message structure -- holds data from a received PPPoE
+ * message. These are copied and saved when queuing offers from
+ * possible servers.
+ */
+typedef struct poesm_s {
+ struct poesm_s *poemsg_next; /* Next message in list */
+ const poep_t *poemsg_data; /* Pointer to PPPoE packet */
+ int poemsg_len; /* Length of packet */
+ ppptun_atype poemsg_sender; /* Address of sender */
+ const char *poemsg_iname; /* Name of input interface */
+} poemsg_t;
+
+/*
+ * PPPoE State Machine structure -- holds state of PPPoE negotiation;
+ * currently, there's exactly one of these per pppoec instance.
+ */
+typedef struct {
+ int poesm_state; /* PCSMS_* */
+ int poesm_timer; /* Milliseconds to next TO */
+ int poesm_count; /* Retry countdown */
+ int poesm_interval; /* Reload value */
+ uint32_t poesm_sequence; /* Sequence for PADR */
+
+ poemsg_t *poesm_firstoff; /* Queue of valid offers; */
+ poemsg_t *poesm_lastoff; /* first is best offer */
+ poemsg_t *poesm_tried; /* Tried and failed offers */
+
+ int poesm_localid; /* Local session ID (driver) */
+} poesm_t;
+
+/*
+ * Convert an internal PPPoE event code number into a printable
+ * string.
+ */
+static const char *
+poe_event(int event)
+{
+ static const char *poeevent[PCSME__MAX] = {
+ "Close", "Open", "TO+", "TO-", "rPADT",
+ "rPADO+", "rPADO", "rPADS", "rPADS-"
+ };
+
+ if (event < 0 || event >= PCSME__MAX) {
+ return ("?");
+ }
+ return (poeevent[event]);
+}
+
+/*
+ * Convert an internal PPPoE state number into a printable string.
+ */
+static const char *
+poe_state(int state)
+{
+ static const char *poestate[PCSMS__MAX] = {
+ "Dead", "InitSent", "OffrRcvd", "ReqSent", "Convers",
+ };
+
+ if (state < 0 || state >= PCSMS__MAX) {
+ return ("?");
+ }
+ return (poestate[state]);
+}
+
+/*
+ * Convert an internal PPPoE action number into a printable string.
+ */
+static const char *
+poe_action(int act)
+{
+ static const char *poeaction[PCSMA__MAX] = {
+ "None", "Fail", "SendPADI", "Add", "SendPADR",
+ "SendPADR+", "SendPADR-", "Open"
+ };
+
+ if (act < 0 || act >= PCSMA__MAX) {
+ return ("?");
+ }
+ return (poeaction[act]);
+}
+
+/*
+ * This calls mygetmsg (which discards partial messages as needed) and
+ * logs errors as appropriate.
+ */
+static int
+pppoec_getmsg(int fd, struct strbuf *ctrl, struct strbuf *data, int *flags)
+{
+ int retv;
+
+ for (;;) {
+ retv = mygetmsg(fd, ctrl, data, flags);
+ if (retv == 0)
+ break;
+ if (retv < 0) {
+ if (errno == EINTR)
+ continue;
+ logstrerror("getmsg");
+ break;
+ }
+ if (verbose) {
+ if (!(retv & (MORECTL | MOREDATA)))
+ logerr("%s: discard: "
+ "unexpected status %d\n", myname, retv);
+ else
+ logerr("%s: discard: "
+ "truncated %s%smessage\n", myname,
+ retv & MORECTL ? "control " : "",
+ retv & MOREDATA ? "data " : "");
+ }
+ }
+ return (retv);
+}
+
+/*
+ * Connect the control path to the lower stream of interest. This
+ * must be called after opening the tunnel driver in order to
+ * establish the interface to be used for signaling. Returns local
+ * session ID number.
+ */
+static int
+set_control(const char *dname)
+{
+ struct ppptun_peer ptp;
+ union ppptun_name ptn;
+
+ /* Fetch the local session ID first. */
+ (void) memset(&ptp, '\0', sizeof (ptp));
+ ptp.ptp_style = PTS_PPPOE;
+ if (strioctl(tunfd, PPPTUN_SPEER, &ptp, sizeof (ptp), sizeof (ptp)) <
+ 0) {
+ logstrerror("PPPTUN_SPEER");
+ exit(1);
+ }
+
+ /* Connect to lower stream. */
+ (void) snprintf(ptn.ptn_name, sizeof (ptn.ptn_name), "%s:pppoed",
+ dname);
+ if (strioctl(tunfd, PPPTUN_SCTL, &ptn, sizeof (ptn), 0) < 0) {
+ logerr("%s: PPPTUN_SCTL %s: %s\n", myname,
+ ptn.ptn_name, mystrerror(errno));
+ exit(1);
+ }
+ return (ptp.ptp_lsessid);
+}
+
+/*
+ * Check if standard input is actually a viable connection to the
+ * tunnel driver. This is the normal mode of operation with pppd; the
+ * tunnel driver is opened by pppd as the tty and pppoec is exec'd as
+ * the connect script.
+ */
+static void
+check_stdin(void)
+{
+ struct ppptun_info pti;
+ union ppptun_name ptn;
+
+ if (strioctl(0, PPPTUN_GDATA, &ptn, 0, sizeof (ptn)) < 0) {
+ if (errno == EINVAL)
+ logerr("%s: PPPoE operation requires "
+ "the use of a tunneling device\n", myname);
+ else
+ logstrerror("PPPTUN_GDATA");
+ exit(1);
+ }
+ if (ptn.ptn_name[0] != '\0') {
+ if (strioctl(0, PPPTUN_GINFO, &pti, 0, sizeof (pti)) < 0) {
+ logstrerror("PPPTUN_GINFO");
+ exit(1);
+ }
+ if (pti.pti_style != PTS_PPPOE) {
+ logerr("%s: Cannot connect to server "
+ "using PPPoE; stream already set to style %d\n",
+ myname, pti.pti_style);
+ exit(1);
+ }
+ if (verbose)
+ logerr("%s: Warning: PPPoE data link "
+ "already connected\n", myname);
+ exit(0);
+ }
+ /* Standard input is the tunnel driver; use it. */
+ tunfd = 0;
+}
+
+/*
+ * Write a summary of a PPPoE message to the given file. This is used
+ * for logging and to display received offers in the inquiry (-i) mode.
+ */
+static void
+display_pppoe(FILE *out, const poep_t *poep, int plen, const ppptun_atype *pap)
+{
+ int ttyp;
+ int tlen;
+ const uint8_t *tagp;
+ const uint8_t *dp;
+ const char *str;
+ poer_t poer;
+ uint32_t mask;
+
+ if (out == stderr)
+ logerr(" "); /* Give us a timestamp */
+ /* Print name of sender. */
+ (void) fprintf(out, "%-16s ", ehost(pap));
+
+ /* Loop through tags and print each. */
+ tagp = (const uint8_t *)(poep + 1);
+ while (poe_tagcheck(poep, plen, tagp)) {
+ ttyp = POET_GET_TYPE(tagp);
+ if (ttyp == POETT_END)
+ break;
+ tlen = POET_GET_LENG(tagp);
+ dp = POET_DATA(tagp);
+ str = NULL;
+ switch (ttyp) {
+ case POETT_SERVICE: /* Service-Name */
+ str = "Svc";
+ break;
+ case POETT_ACCESS: /* AC-Name */
+ str = "Name";
+ break;
+ case POETT_UNIQ: /* Host-Uniq */
+ str = "Uniq";
+ break;
+ case POETT_COOKIE: /* AC-Cookie */
+ str = "Cookie";
+ break;
+ case POETT_VENDOR: /* Vendor-Specific */
+ break;
+ case POETT_RELAY: /* Relay-Session-Id */
+ str = "Relay";
+ break;
+ case POETT_NAMERR: /* Service-Name-Error */
+ str = "SvcNameErr";
+ break;
+ case POETT_SYSERR: /* AC-System-Error */
+ str = "SysErr";
+ break;
+ case POETT_GENERR: /* Generic-Error */
+ str = "GenErr";
+ break;
+ case POETT_MULTI: /* Multicast-Capable */
+ break;
+ case POETT_HURL: /* Host-URL */
+ str = "URL";
+ break;
+ case POETT_MOTM: /* Message-Of-The-Minute */
+ str = "Mesg";
+ break;
+ case POETT_RTEADD: /* IP-Route-Add */
+ break;
+ }
+ switch (ttyp) {
+ case POETT_NAMERR: /* Service-Name-Error */
+ case POETT_SYSERR: /* AC-System-Error */
+ if (tlen > 0 && *dp == '\0')
+ tlen = 0;
+ /* FALLTHROUGH */
+ case POETT_SERVICE: /* Service-Name */
+ case POETT_ACCESS: /* AC-Name */
+ case POETT_GENERR: /* Generic-Error */
+ case POETT_MOTM: /* Message-Of-The-Minute */
+ case POETT_HURL: /* Host-URL */
+ (void) fprintf(out, "%s:\"%.*s\" ", str, tlen, dp);
+ break;
+ case POETT_UNIQ: /* Host-Uniq */
+ case POETT_COOKIE: /* AC-Cookie */
+ case POETT_RELAY: /* Relay-Session-Id */
+ (void) fprintf(out, "%s:", str);
+ while (--tlen >= 0)
+ (void) fprintf(out, "%02X", *dp++);
+ (void) putc(' ', out);
+ break;
+ case POETT_VENDOR: /* Vendor-Specific */
+ (void) fputs("Vendor:", out);
+ if (tlen >= 4) {
+ if (*dp++ != 0) {
+ (void) fprintf(out, "(%02X?)", dp[-1]);
+ }
+ (void) fprintf(out, "%x-%x-%x:", dp[0], dp[1],
+ dp[2]);
+ tlen -= 4;
+ dp += 3;
+ }
+ while (--tlen >= 0)
+ (void) fprintf(out, "%02X", *dp++);
+ (void) putc(' ', out);
+ break;
+ case POETT_MULTI: /* Multicast-Capable */
+ (void) fprintf(out, "Multi:%d ", *dp);
+ break;
+ case POETT_RTEADD: /* IP-Route-Add */
+ if (tlen != sizeof (poer)) {
+ (void) fprintf(out, "RTE%d? ", tlen);
+ break;
+ }
+ (void) memcpy(&poer, dp, sizeof (poer));
+ (void) fputs("RTE:", out);
+ if (poer.poer_dest_network == 0)
+ (void) fputs("default", out);
+ else
+ (void) fputs(ihost(poer.poer_dest_network),
+ out);
+ mask = ntohl(poer.poer_subnet_mask);
+ if (mask != 0 && mask != (uint32_t)~0) {
+ if ((~mask & (~mask + 1)) == 0)
+ (void) fprintf(out, "/%d",
+ sizeof (struct in_addr) * NBBY +
+ 1 - ffs(mask));
+ else
+ (void) fprintf(out, "/%s",
+ ihost(poer.poer_subnet_mask));
+ }
+ (void) fprintf(out, ",%s,%u ",
+ ihost(poer.poer_gateway), ntohl(poer.poer_metric));
+ break;
+ default:
+ (void) fprintf(out, "%s:%d ", poe_tagname(ttyp), tlen);
+ break;
+ }
+ tagp = POET_NEXT(tagp);
+ }
+ (void) putc('\n', out);
+}
+
+/*
+ * Transmit a PPPoE message to the indicated destination. Used for
+ * PADI and PADR messages.
+ */
+static int
+send_pppoe(const poep_t *poep, const char *msgname,
+ const ppptun_atype *destaddr)
+{
+ struct strbuf ctrl;
+ struct strbuf data;
+ struct ppptun_control *ptc;
+
+ /* Set up the control data expected by the driver. */
+ ptc = (struct ppptun_control *)pkt_octl;
+ (void) memset(ptc, '\0', sizeof (*ptc));
+ ptc->ptc_discrim = PPPOE_DISCRIM;
+ ptc->ptc_action = PTCA_CONTROL;
+ ptc->ptc_address = *destaddr;
+ ctrl.len = sizeof (*ptc);
+ ctrl.buf = (caddr_t)ptc;
+ data.len = poe_length(poep) + sizeof (*poep);
+ data.buf = (caddr_t)poep;
+ if (verbose)
+ logerr("%s: Sending %s to %s: %d bytes\n",
+ myname, msgname, ehost(destaddr), data.len);
+ if (putmsg(tunfd, &ctrl, &data, 0) < 0) {
+ logstrerror("putmsg");
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * Create and transmit a PPPoE Active Discovery Initiation packet.
+ * This is broadcasted to all hosts on the LAN.
+ */
+static int
+send_padi(int localid)
+{
+ poep_t *poep;
+ ppptun_atype destaddr;
+
+ poep = poe_mkheader(pkt_output, POECODE_PADI, 0);
+ (void) poe_add_str(poep, POETT_SERVICE, "");
+ (void) poe_add_long(poep, POETT_UNIQ, localid);
+ (void) memset(&destaddr, '\0', sizeof (destaddr));
+ (void) memcpy(destaddr.pta_pppoe.ptma_mac, ether_bcast,
+ sizeof (destaddr.pta_pppoe.ptma_mac));
+ return (send_pppoe(poep, "PADI", &destaddr));
+}
+
+/*
+ * This is used by the procedure below -- when the alarm goes off,
+ * just exit. (This was once a dummy procedure and used the EINTR
+ * side-effect to terminate the loop, but that's not reliable, since
+ * the EINTR could be caught and ignored by the calls to standard
+ * output.)
+ */
+/* ARGSUSED */
+static void
+alarm_hand(int dummy)
+{
+ exit(0);
+}
+
+/*
+ * Send out a single PADI and listen for servers. This implements the
+ * "inquiry" (-i) mode.
+ */
+static void
+find_all_servers(int localid)
+{
+ struct strbuf ctrl;
+ struct strbuf data;
+ poep_t *poep;
+ int flags;
+ struct sigaction act;
+ struct ppptun_control *ptc;
+
+ /* Set a default 3-second timer */
+ (void) memset(&act, '\0', sizeof (act));
+ act.sa_handler = alarm_hand;
+ (void) sigaction(SIGALRM, &act, NULL);
+ (void) alarm((pado_wait_time + 999) / 1000);
+
+ /* Broadcast a single request. */
+ if (send_padi(localid) != 0)
+ return;
+
+ /* Loop over responses and print them. */
+ for (;;) {
+ ctrl.maxlen = PKT_OCTL_LEN;
+ ctrl.buf = (caddr_t)pkt_octl;
+ data.maxlen = PKT_INPUT_LEN;
+ data.buf = (caddr_t)pkt_input;
+ flags = 0;
+
+ if (pppoec_getmsg(tunfd, &ctrl, &data, &flags) < 0)
+ break;
+
+ /* Ignore unwanted responses from the driver. */
+ if (ctrl.len != sizeof (*ptc)) {
+ if (verbose)
+ logerr("%s: unexpected %d byte"
+ " control message from driver.\n", myname,
+ ctrl.len);
+ continue;
+ }
+ ptc = (struct ppptun_control *)pkt_octl;
+ poep = (poep_t *)pkt_input;
+
+ /* If it's an offer, then print it out. */
+ if (poe_code(poep) == POECODE_PADO) {
+ display_pppoe(stdout, poep, data.len,
+ &ptc->ptc_address);
+ }
+ }
+}
+
+/*
+ * Parse a server filter from the command line. The passed-in string
+ * must be allocated and unchanged, since a pointer to it is saved in
+ * the filter data structure. The string is also parsed for a MAC
+ * address, if possible.
+ */
+static void
+parse_filter(const char *str, int exceptflag)
+{
+ struct server_filter *sfnew;
+ const char *cp;
+ const char *wordstart;
+ const char *wordend;
+ int len;
+ char hbuf[MAXHOSTNAMELEN];
+ uchar_t *ucp;
+ uchar_t *mcp;
+
+ /* Allocate the new filter structure. */
+ sfnew = (struct server_filter *)calloc(1, sizeof (*sfnew));
+ if (sfnew == NULL) {
+ logstrerror("filter allocation");
+ exit(1);
+ }
+
+ /* Save the string for AC-Name comparison. */
+ sfnew->sf_name = str;
+
+ sfnew->sf_isexcept = exceptflag == 0 ? 0 : 1;
+
+ /* Extract just one word. */
+ cp = str;
+ while (isspace(*cp))
+ cp++;
+ wordstart = cp;
+ while (*cp != '\0' && !isspace(*cp))
+ cp++;
+ wordend = cp;
+ if ((len = wordend - wordstart) >= sizeof (hbuf))
+ len = sizeof (hbuf) - 1;
+ (void) strlcpy(hbuf, wordstart, len);
+ hbuf[len] = '\0';
+
+ /* Try to translate this as an Ethernet host or address. */
+ mcp = sfnew->sf_mask.ether_addr_octet;
+ if (ether_hostton(hbuf, &sfnew->sf_mac) == 0) {
+ mcp[0] = mcp[1] = mcp[2] = mcp[3] = mcp[4] = mcp[5] = 0xFF;
+ sfnew->sf_hasmac = 1;
+ } else {
+ ucp = sfnew->sf_mac.ether_addr_octet;
+ len = wordend - wordstart;
+ cp = wordstart;
+ while (cp < wordend) {
+ if (ucp >= sfnew->sf_mac.ether_addr_octet +
+ sizeof (sfnew->sf_mac))
+ break;
+ if (*cp == '*') {
+ *mcp++ = *ucp++ = 0;
+ cp++;
+ } else {
+ if (!isxdigit(*cp))
+ break;
+ *ucp = hexdecode(*cp++);
+ if (cp < wordend && isxdigit(*cp)) {
+ *ucp = (*ucp << 4) |
+ hexdecode(*cp++);
+ }
+ ucp++;
+ *mcp++ = 0xFF;
+ }
+ if (cp < wordend) {
+ if (*cp != ':' || cp + 1 == wordend)
+ break;
+ cp++;
+ }
+ }
+ if (cp >= wordend)
+ sfnew->sf_hasmac = 1;
+ else if (verbose)
+ logerr("%s: treating '%.*s' as server "
+ "name only, not MAC address\n", myname, len,
+ wordstart);
+ }
+
+ /* Add to end of list. */
+ if (sftail == NULL)
+ sfhead = sfnew;
+ else
+ sftail->sf_next = sfnew;
+ sftail = sfnew;
+}
+
+/*
+ * Create a copy of a given PPPoE message. This is used for enqueuing
+ * received PADO (offers) from possible servers.
+ */
+static poemsg_t *
+save_message(const poemsg_t *pmsg)
+{
+ poemsg_t *newmsg;
+ char *cp;
+
+ newmsg = (poemsg_t *)malloc(sizeof (*pmsg) + pmsg->poemsg_len +
+ strlen(pmsg->poemsg_iname) + 1);
+ if (newmsg != NULL) {
+ newmsg->poemsg_next = NULL;
+ newmsg->poemsg_data = (const poep_t *)(newmsg + 1);
+ (void) memcpy(newmsg + 1, pmsg->poemsg_data, pmsg->poemsg_len);
+ newmsg->poemsg_len = pmsg->poemsg_len;
+ cp = (char *)newmsg->poemsg_data + pmsg->poemsg_len;
+ newmsg->poemsg_iname = (const char *)cp;
+ (void) strcpy(cp, pmsg->poemsg_iname);
+ (void) memcpy(&newmsg->poemsg_sender, &pmsg->poemsg_sender,
+ sizeof (newmsg->poemsg_sender));
+ }
+ return (newmsg);
+}
+
+/*
+ * Create and send a PPPoE Active Discovery Request (PADR) message to
+ * the sender of the given PADO. Some tags -- Service-Name,
+ * AC-Cookie, and Relay-Session-Id -- must be copied from PADO to
+ * PADR. Others are not. The Service-Name must be selected from the
+ * offered services in the PADO based on the user's requested service
+ * name. If the server offered "wildcard" service, then we ask for
+ * this only if we can't find the user's requested service.
+ *
+ * Returns 1 if we can't send a valid PADR in response to the given
+ * PADO. The offer should be ignored and the next one tried.
+ */
+static int
+send_padr(poesm_t *psm, const poemsg_t *pado)
+{
+ poep_t *poep;
+ boolean_t haswild;
+ boolean_t hassvc;
+ const uint8_t *tagp;
+ int ttyp;
+ int tlen;
+
+ /*
+ * Increment sequence number for PADR so that we don't mistake
+ * old replies for valid ones if the server is very slow.
+ */
+ psm->poesm_sequence++;
+
+ poep = poe_mkheader(pkt_output, POECODE_PADR, 0);
+ (void) poe_two_longs(poep, POETT_UNIQ, psm->poesm_localid,
+ psm->poesm_sequence);
+
+ haswild = B_FALSE;
+ hassvc = B_FALSE;
+ tagp = (const uint8_t *)(pado->poemsg_data + 1);
+ while (poe_tagcheck(pado->poemsg_data, pado->poemsg_len, tagp)) {
+ ttyp = POET_GET_TYPE(tagp);
+ if (ttyp == POETT_END)
+ break;
+ tlen = POET_GET_LENG(tagp);
+ switch (ttyp) {
+ case POETT_SERVICE: /* Service-Name */
+ /* Allow only one */
+ if (hassvc)
+ break;
+ if (tlen == 0) {
+ haswild = B_TRUE;
+ break;
+ }
+ if (service[0] == '\0' ||
+ (tlen == strlen(service) &&
+ memcmp(service, POET_DATA(tagp), tlen) == 0)) {
+ (void) poe_tag_copy(poep, tagp);
+ hassvc = B_TRUE;
+ }
+ break;
+ /* Ones we should discard */
+ case POETT_ACCESS: /* AC-Name */
+ case POETT_UNIQ: /* Host-Uniq */
+ case POETT_NAMERR: /* Service-Name-Error */
+ case POETT_SYSERR: /* AC-System-Error */
+ case POETT_GENERR: /* Generic-Error */
+ case POETT_HURL: /* Host-URL */
+ case POETT_MOTM: /* Message-Of-The-Minute */
+ case POETT_RTEADD: /* IP-Route-Add */
+ case POETT_VENDOR: /* Vendor-Specific */
+ case POETT_MULTI: /* Multicast-Capable */
+ default: /* Anything else we don't understand */
+ break;
+ /* Ones we should copy */
+ case POETT_COOKIE: /* AC-Cookie */
+ case POETT_RELAY: /* Relay-Session-Id */
+ (void) poe_tag_copy(poep, tagp);
+ break;
+ }
+ tagp = POET_NEXT(tagp);
+ }
+ if (!hassvc) {
+ if (haswild)
+ (void) poe_add_str(poep, POETT_SERVICE, "");
+ else
+ return (1);
+ }
+
+ return (send_pppoe(poep, "PADR", &pado->poemsg_sender));
+}
+
+/*
+ * ********************************************************************
+ * act_* functions implement the actions driven by the state machine
+ * tables. See "action_table" below.
+ *
+ * All action routines must return the next state value.
+ * ********************************************************************
+ */
+
+/* ARGSUSED */
+static int
+act_none(poesm_t *psm, poemsg_t *pmsg, int event, int nextst)
+{
+ return (nextst);
+}
+
+/* ARGSUSED */
+static int
+act_fail(poesm_t *psm, poemsg_t *pmsg, int event, int nextst)
+{
+ if (verbose)
+ logerr("%s: unrecoverable error\n", myname);
+ return (PCSMS_DEAD);
+}
+
+/* ARGSUSED */
+static int
+act_spadi(poesm_t *psm, poemsg_t *pmsg, int event, int nextst)
+{
+ if (send_padi(psm->poesm_localid) != 0)
+ return (PCSMS_DEAD);
+ /*
+ * If this is the first time, then initialize the retry count
+ * and interval.
+ */
+ if (psm->poesm_state == PCSMS_DEAD) {
+ psm->poesm_count = 3;
+ psm->poesm_interval = pado_wait_time;
+ } else {
+ if ((psm->poesm_interval <<= 1) > RESTART_LIMIT)
+ psm->poesm_interval = RESTART_LIMIT;
+ }
+ psm->poesm_timer = psm->poesm_interval;
+ (void) gettimeofday(&tvstart, NULL);
+ return (nextst);
+}
+
+/* ARGSUSED */
+static int
+act_add(poesm_t *psm, poemsg_t *pmsg, int event, int nextst)
+{
+ pmsg = save_message(pmsg);
+ if (pmsg != NULL) {
+ if (psm->poesm_lastoff == NULL)
+ psm->poesm_firstoff = pmsg;
+ else
+ psm->poesm_lastoff->poemsg_next = pmsg;
+ psm->poesm_lastoff = pmsg;
+ }
+ return (nextst);
+}
+
+/* ARGSUSED */
+static int
+act_spadr(poesm_t *psm, poemsg_t *pmsg, int event, int nextst)
+{
+ poemsg_t *msgp;
+ int retv;
+
+ for (;;) {
+ if ((msgp = psm->poesm_firstoff) == NULL)
+ return (PCSMS_DEAD);
+ retv = send_padr(psm, msgp);
+ if (retv < 0)
+ return (PCSMS_DEAD);
+ if (retv == 0)
+ break;
+ /* Can't send this request; try looking at next offer. */
+ psm->poesm_firstoff = msgp->poemsg_next;
+ msgp->poemsg_next = psm->poesm_tried;
+ psm->poesm_tried = msgp;
+ }
+ if (psm->poesm_state != PCSMS_REQSENT) {
+ psm->poesm_count = 3;
+ psm->poesm_interval = pads_wait_time;
+ } else {
+ if ((psm->poesm_interval <<= 1) > RESTART_LIMIT)
+ psm->poesm_interval = RESTART_LIMIT;
+ }
+ psm->poesm_timer = psm->poesm_interval;
+ (void) gettimeofday(&tvstart, NULL);
+ return (nextst);
+}
+
+/* ARGSUSED */
+static int
+act_spadrp(poesm_t *psm, poemsg_t *pmsg, int event, int nextst)
+{
+ int retv;
+
+ retv = send_padr(psm, pmsg);
+ if (retv < 0)
+ return (PCSMS_DEAD);
+ pmsg = save_message(pmsg);
+ if (retv > 0) {
+ /*
+ * Cannot use this one; mark as tried and continue as
+ * if we never saw it.
+ */
+ pmsg->poemsg_next = psm->poesm_tried;
+ psm->poesm_tried = pmsg;
+ return (psm->poesm_state);
+ }
+ pmsg->poemsg_next = psm->poesm_firstoff;
+ psm->poesm_firstoff = pmsg;
+ if (psm->poesm_lastoff == NULL)
+ psm->poesm_lastoff = pmsg;
+ psm->poesm_count = 3;
+ psm->poesm_timer = psm->poesm_interval = pads_wait_time;
+ (void) gettimeofday(&tvstart, NULL);
+ return (nextst);
+}
+
+/* ARGSUSED */
+static int
+act_spadrn(poesm_t *psm, poemsg_t *pmsg, int event, int nextst)
+{
+ poemsg_t *msgp;
+ int retv;
+
+ if ((msgp = psm->poesm_firstoff) == NULL)
+ return (PCSMS_DEAD);
+ do {
+ psm->poesm_firstoff = msgp->poemsg_next;
+ msgp->poemsg_next = psm->poesm_tried;
+ psm->poesm_tried = msgp;
+ if ((msgp = psm->poesm_firstoff) == NULL)
+ return (PCSMS_DEAD);
+ retv = send_padr(psm, msgp);
+ if (retv < 0)
+ return (PCSMS_DEAD);
+ } while (retv != 0);
+ psm->poesm_count = 3;
+ psm->poesm_timer = psm->poesm_interval = pads_wait_time;
+ (void) gettimeofday(&tvstart, NULL);
+ return (nextst);
+}
+
+/*
+ * For safety -- remove end of line from strings passed back to pppd.
+ */
+static void
+remove_eol(char *str, size_t len)
+{
+ while (len > 0) {
+ if (*str == '\n')
+ *str = '$';
+ str++;
+ len--;
+ }
+}
+
+/* ARGSUSED */
+static int
+act_open(poesm_t *psm, poemsg_t *pmsg, int event, int nextst)
+{
+ struct ppptun_peer ptp;
+ union ppptun_name ptn;
+ const char *cp;
+ FILE *fp;
+ const uint8_t *tagp, *vp;
+ int tlen, ttyp;
+ char *access;
+ uint32_t val;
+ size_t acc_len, serv_len;
+
+ /*
+ * The server has now assigned its session ID for the data
+ * (PPP) portion of this tunnel. Send that ID down to the
+ * driver.
+ */
+ (void) memset(&ptp, '\0', sizeof (ptp));
+ ptp.ptp_lsessid = psm->poesm_localid;
+ ptp.ptp_rsessid = poe_session_id(pmsg->poemsg_data);
+ (void) memcpy(&ptp.ptp_address, &pmsg->poemsg_sender,
+ sizeof (ptp.ptp_address));
+ ptp.ptp_style = PTS_PPPOE;
+ if (strioctl(tunfd, PPPTUN_SPEER, &ptp, sizeof (ptp), sizeof (ptp)) <
+ 0) {
+ logstrerror("PPPTUN_SPEER");
+ return (PCSMS_DEAD);
+ }
+
+ /*
+ * Data communication is now possible on this session.
+ * Connect the data portion to the correct lower stream.
+ */
+ if ((cp = strchr(pmsg->poemsg_iname, ':')) == NULL)
+ cp = pmsg->poemsg_iname + strlen(pmsg->poemsg_iname);
+ (void) snprintf(ptn.ptn_name, sizeof (ptn.ptn_name), "%.*s:pppoe",
+ cp - pmsg->poemsg_iname, pmsg->poemsg_iname);
+ if (strioctl(tunfd, PPPTUN_SDATA, &ptn, sizeof (ptn), 0) < 0) {
+ logerr("%s: PPPTUN_SDATA %s: %s\n",
+ myname, ptn.ptn_name, mystrerror(errno));
+ return (PCSMS_DEAD);
+ }
+ if (verbose)
+ logerr("%s: Connection open; session %04X on "
+ "%s\n", myname, ptp.ptp_rsessid, ptn.ptn_name);
+
+ /*
+ * Walk through the PADS message to get the access server name
+ * and the service. If there are multiple instances of either
+ * tag, then take the last access server and the first
+ * non-null service.
+ */
+ access = "";
+ acc_len = 0;
+ serv_len = strlen(service);
+ tagp = (const uint8_t *)(pmsg->poemsg_data + 1);
+ while (poe_tagcheck(pmsg->poemsg_data, pmsg->poemsg_len, tagp)) {
+ ttyp = POET_GET_TYPE(tagp);
+ if (ttyp == POETT_END)
+ break;
+ tlen = POET_GET_LENG(tagp);
+ if (ttyp == POETT_ACCESS) {
+ access = (char *)POET_DATA(tagp);
+ acc_len = tlen;
+ }
+ if (serv_len == 0 && ttyp == POETT_SERVICE && tlen != 0) {
+ service = (char *)POET_DATA(tagp);
+ serv_len = tlen;
+ }
+ tagp = POET_NEXT(tagp);
+ }
+
+ /*
+ * Remove end of line to make sure that integrity of values
+ * passed back to pppd can't be compromised by the PPPoE
+ * server.
+ */
+ remove_eol(service, serv_len);
+ remove_eol(access, acc_len);
+
+ /*
+ * pppd has given us a pipe as fd 3, and we're expected to
+ * write out the values of the following environment
+ * variables:
+ * IF_AND_SERVICE
+ * SERVICE_NAME
+ * AC_NAME
+ * AC_MAC
+ * SESSION_ID
+ * VENDOR_SPECIFIC_1 ... N
+ * See usr.bin/pppd/plugins/pppoe.c for more information.
+ */
+ if ((fp = fdopen(3, "w")) != NULL) {
+ (void) fprintf(fp, "%.*s:%.*s\n",
+ cp - pmsg->poemsg_iname, pmsg->poemsg_iname, serv_len,
+ service);
+ (void) fprintf(fp, "%.*s\n", serv_len, service);
+ (void) fprintf(fp, "%.*s\n", acc_len, access);
+ (void) fprintf(fp, "%s\n", ehost(&pmsg->poemsg_sender));
+ (void) fprintf(fp, "%d\n", poe_session_id(pmsg->poemsg_data));
+ tagp = (const uint8_t *)(pmsg->poemsg_data + 1);
+ while (poe_tagcheck(pmsg->poemsg_data, pmsg->poemsg_len,
+ tagp)) {
+ ttyp = POET_GET_TYPE(tagp);
+ if (ttyp == POETT_END)
+ break;
+ tlen = POET_GET_LENG(tagp);
+ if (ttyp == POETT_VENDOR && tlen >= 4) {
+ (void) memcpy(&val, POET_DATA(tagp), 4);
+ (void) fprintf(fp, "%08lX:",
+ (unsigned long)ntohl(val));
+ tlen -= 4;
+ vp = POET_DATA(tagp) + 4;
+ while (--tlen >= 0)
+ (void) fprintf(fp, "%02X", *vp++);
+ (void) putc('\n', fp);
+ }
+ tagp = POET_NEXT(tagp);
+ }
+ (void) fclose(fp);
+ }
+
+ return (nextst);
+}
+
+static int (* const action_table[PCSMA__MAX])(poesm_t *psm, poemsg_t *pmsg,
+ int event, int nextst) = {
+ act_none, act_fail, act_spadi, act_add, act_spadr, act_spadrp,
+ act_spadrn, act_open
+};
+
+/*
+ * Dispatch an event and a corresponding message on a given state
+ * machine.
+ */
+static void
+handle_event(poesm_t *psm, int event, poemsg_t *pmsg)
+{
+ int nextst;
+
+ if (verbose)
+ logerr("%s: PPPoE Event %s (%d) in state %s "
+ "(%d): action %s (%d)\n", myname, poe_event(event), event,
+ poe_state(psm->poesm_state), psm->poesm_state,
+ poe_action(client_action[psm->poesm_state][event]),
+ client_action[psm->poesm_state][event]);
+
+ nextst = (*action_table[client_action[psm->poesm_state][event]])(psm,
+ pmsg, event, client_next_state[psm->poesm_state][event]);
+
+ if (verbose)
+ logerr("%s: PPPoE State change %s (%d) -> %s (%d)\n", myname,
+ poe_state(psm->poesm_state), psm->poesm_state,
+ poe_state(nextst), nextst);
+
+ psm->poesm_state = nextst;
+
+ /*
+ * Life-altering states are handled here. If we reach dead
+ * state again after starting, then we failed. If we reach
+ * conversational state, then we're open.
+ */
+ if (nextst == PCSMS_DEAD) {
+ if (verbose)
+ logerr("%s: action failed\n", myname);
+ exit(1);
+ }
+ if (nextst == PCSMS_CONVERS) {
+ if (verbose)
+ logerr("%s: connected\n", myname);
+ exit(0);
+ }
+}
+
+/*
+ * Check for error message tags in the PPPoE packet. We must ignore
+ * offers that merely report errors, and need to log errors in any
+ * case.
+ */
+static int
+error_check(poemsg_t *pmsg)
+{
+ const uint8_t *tagp;
+ int ttyp;
+
+ tagp = (const uint8_t *)(pmsg->poemsg_data + 1);
+ while (poe_tagcheck(pmsg->poemsg_data, pmsg->poemsg_len, tagp)) {
+ ttyp = POET_GET_TYPE(tagp);
+ if (ttyp == POETT_END)
+ break;
+ if (ttyp == POETT_NAMERR || ttyp == POETT_SYSERR ||
+ ttyp == POETT_GENERR) {
+ if (verbose)
+ display_pppoe(stderr, pmsg->poemsg_data,
+ pmsg->poemsg_len, &pmsg->poemsg_sender);
+ return (-1);
+ }
+ tagp = POET_NEXT(tagp);
+ }
+ return (0);
+}
+
+/*
+ * Extract sequence number, if any, from PADS message, so that we can
+ * relate it to the PADR that we sent.
+ */
+static uint32_t
+get_sequence(const poemsg_t *pmsg)
+{
+ const uint8_t *tagp;
+ int ttyp;
+ uint32_t vals[2];
+
+ tagp = (const uint8_t *)(pmsg->poemsg_data + 1);
+ while (poe_tagcheck(pmsg->poemsg_data, pmsg->poemsg_len, tagp)) {
+ ttyp = POET_GET_TYPE(tagp);
+ if (ttyp == POETT_END)
+ break;
+ if (ttyp == POETT_UNIQ) {
+ if (POET_GET_LENG(tagp) < sizeof (vals))
+ break;
+ (void) memcpy(vals, POET_DATA(tagp), sizeof (vals));
+ return (ntohl(vals[1]));
+ }
+ tagp = POET_NEXT(tagp);
+ }
+ return (0);
+}
+
+/*
+ * Server filter cases:
+ *
+ * No filters -- all servers generate RPADO+ event; select the
+ * first responding server.
+ *
+ * Only "except" filters -- matching servers are RPADO, others
+ * are RPADO+.
+ *
+ * Mix of filters -- those matching "pass" are RPADO+, those
+ * matching "except" are RPADO, and all others are also RPADO.
+ *
+ * If the "only" keyword was given, then RPADO becomes -1; only RPADO+
+ * events occur.
+ */
+static int
+use_server(poemsg_t *pado)
+{
+ struct server_filter *sfp;
+ const uchar_t *sndp;
+ const uchar_t *macp;
+ const uchar_t *maskp;
+ int i;
+ int passmatched;
+ const uint8_t *tagp;
+ int ttyp;
+
+ /*
+ * If no service mentioned in offer, then we can't use it.
+ */
+ tagp = (const uint8_t *)(pado->poemsg_data + 1);
+ ttyp = POETT_END;
+ while (poe_tagcheck(pado->poemsg_data, pado->poemsg_len, tagp)) {
+ ttyp = POET_GET_TYPE(tagp);
+ if (ttyp == POETT_END || ttyp == POETT_SERVICE)
+ break;
+ tagp = POET_NEXT(tagp);
+ }
+ if (ttyp != POETT_SERVICE)
+ return (-1);
+
+ passmatched = 0;
+ for (sfp = sfhead; sfp != NULL; sfp = sfp->sf_next) {
+ passmatched |= !sfp->sf_isexcept;
+ if (sfp->sf_hasmac) {
+ sndp = pado->poemsg_sender.pta_pppoe.ptma_mac;
+ macp = sfp->sf_mac.ether_addr_octet;
+ maskp = sfp->sf_mask.ether_addr_octet;
+ i = sizeof (pado->poemsg_sender.pta_pppoe.ptma_mac);
+ for (; i > 0; i--)
+ if (((*macp++ ^ *sndp++) & *maskp++) != 0)
+ break;
+ if (i <= 0)
+ break;
+ }
+ }
+
+ if (sfp == NULL) {
+ /*
+ * No match encountered; if only exclude rules have
+ * been seen, then accept this offer.
+ */
+ if (!passmatched)
+ return (PCSME_RPADOP);
+ } else {
+ if (!sfp->sf_isexcept)
+ return (PCSME_RPADOP);
+ }
+ if (onlyflag)
+ return (-1);
+ return (PCSME_RPADO);
+}
+
+/*
+ * This is the normal event loop. It initializes the state machine
+ * and sends in an Open event to kick things off. Then it drops into
+ * a loop to dispatch events for the state machine.
+ */
+static void
+find_server(int localid)
+{
+ poesm_t psm;
+ struct pollfd pfd[1];
+ struct timeval tv, tvnow;
+ int retv;
+ poemsg_t pmsg;
+ struct strbuf ctrl;
+ struct strbuf data;
+ poep_t *poep;
+ int flags;
+ uint32_t seqval;
+ struct ppptun_control *ptc;
+
+ (void) memset(&psm, '\0', sizeof (psm));
+
+ /*
+ * Initialize the sequence number with something handy. It
+ * doesn't need to be absolutely unique, since the localid
+ * value actually demultiplexes everything. This just makes
+ * the operation a little safer.
+ */
+ psm.poesm_sequence = getpid() << 16;
+ psm.poesm_localid = localid;
+
+ /* Start the state machine */
+ handle_event(&psm, PCSME_OPEN, NULL);
+
+ /* Enter event polling loop. */
+ pfd[0].fd = tunfd;
+ pfd[0].events = POLLIN;
+ for (;;) {
+ /* Wait for timeout or message */
+ retv = poll(pfd, 1, psm.poesm_timer > 0 ? psm.poesm_timer :
+ INFTIM);
+ if (retv < 0) {
+ logstrerror("poll");
+ break;
+ }
+
+ /* Handle a timeout */
+ if (retv == 0) {
+ psm.poesm_timer = 0;
+ handle_event(&psm, --psm.poesm_count > 0 ? PCSME_TOP :
+ PCSME_TOM, NULL);
+ continue;
+ }
+
+ /* Adjust the timer for the time we slept. */
+ if (psm.poesm_timer > 0) {
+ (void) gettimeofday(&tvnow, NULL);
+ tv = tvnow;
+ if ((tv.tv_sec -= tvstart.tv_sec) < 0) {
+ /* Darn */
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ } else if ((tv.tv_usec -= tvstart.tv_usec) < 0) {
+ tv.tv_usec += 1000000;
+ if (--tv.tv_sec < 0)
+ tv.tv_sec = 0;
+ }
+ psm.poesm_timer -= tv.tv_sec*1000 + tv.tv_usec/1000;
+ tvstart = tvnow;
+ }
+
+ /* Read in the message from the server. */
+ ctrl.maxlen = PKT_OCTL_LEN;
+ ctrl.buf = (caddr_t)pkt_octl;
+ data.maxlen = PKT_INPUT_LEN;
+ data.buf = (caddr_t)pkt_input;
+ flags = 0;
+
+ if (pppoec_getmsg(tunfd, &ctrl, &data, &flags) < 0)
+ break;
+
+ if (ctrl.len != sizeof (*ptc)) {
+ if (verbose)
+ logerr("%s: discard: ctrl len %d\n", myname,
+ ctrl.len);
+ continue;
+ }
+ poep = (poep_t *)pkt_input;
+ (void) memset(&pmsg, '\0', sizeof (pmsg));
+ pmsg.poemsg_next = NULL;
+ pmsg.poemsg_data = poep;
+ pmsg.poemsg_len = data.len;
+ ptc = (struct ppptun_control *)pkt_octl;
+ if (ptc->ptc_action != PTCA_CONTROL) {
+ if (verbose)
+ logerr("%s: discard: unexpected action %d\n",
+ myname, ptc->ptc_action);
+ continue;
+ }
+ pmsg.poemsg_iname = ptc->ptc_name;
+ if (verbose)
+ logerr("%s: Received %s from %s/%s\n",
+ myname, poe_codename(poep->poep_code),
+ ehost(&ptc->ptc_address), pmsg.poemsg_iname);
+ pmsg.poemsg_sender = ptc->ptc_address;
+
+ /* Check for messages from unexpected peers. */
+ if ((poep->poep_code == POECODE_PADT ||
+ poep->poep_code == POECODE_PADS) &&
+ (psm.poesm_firstoff == NULL ||
+ memcmp(&psm.poesm_firstoff->poemsg_sender,
+ &pmsg.poemsg_sender,
+ sizeof (pmsg.poemsg_sender)) != 0)) {
+ if (verbose) {
+ logerr("%s: Unexpected peer %s", myname,
+ ehost(&ptc->ptc_address));
+ logerr(" != %s\n",
+ ehost(&psm.poesm_firstoff->poemsg_sender));
+ }
+ continue;
+ }
+
+ /* Eliminate stale PADS responses. */
+ if (poep->poep_code == POECODE_PADS) {
+ seqval = get_sequence(&pmsg);
+ if (seqval != psm.poesm_sequence) {
+ if (verbose) {
+ if (seqval == 0)
+ logerr(
+ "%s: PADS has no sequence "
+ "number.\n", myname);
+ else
+ logerr(
+ "%s: PADS has sequence "
+ "%08X instead of %08X.\n",
+ myname, seqval,
+ psm.poesm_sequence);
+ }
+ continue;
+ }
+ }
+
+ /* Dispatch message event. */
+ retv = error_check(&pmsg);
+ switch (poep->poep_code) {
+ case POECODE_PADT:
+ handle_event(&psm, PCSME_RPADT, &pmsg);
+ break;
+ case POECODE_PADS:
+ /*
+ * Got a PPPoE Active Discovery Session-
+ * confirmation message. It's a PADS event if
+ * everything's in order. It's a PADS- event
+ * if the message is merely reporting an
+ * error.
+ */
+ handle_event(&psm, retv != 0 ? PCSME_RPADSN :
+ PCSME_RPADS, &pmsg);
+ break;
+ case POECODE_PADO:
+ /* Ignore offers that merely report errors. */
+ if (retv != 0)
+ break;
+ /* Ignore offers from servers we don't want. */
+ if ((retv = use_server(&pmsg)) < 0)
+ break;
+ /* Dispatch either RPADO or RAPDO+ event. */
+ handle_event(&psm, retv, &pmsg);
+ break;
+
+ default:
+ if (verbose)
+ logerr("%s: Unexpected code %s (%d)\n", myname,
+ poe_codename(poep->poep_code),
+ poep->poep_code);
+ break;
+ }
+ }
+ exit(1);
+}
+
+static void
+usage(void)
+{
+ logerr("Usage:\n"
+ "\t%s [-os#] [-v] <dev> [<service> [<server> [only]]]\n\n"
+ " or\n\n"
+ "\t%s [-o#] [-v] -i <dev>\n", myname, myname);
+ exit(1);
+}
+
+/*
+ * In addition to the usual 0-2 file descriptors, pppd will leave fd 3
+ * open on a pipe to receive the environment variables. See
+ * pppoe_device_pipe() in pppd/plugins/pppoe.c and device_pipe_hook in
+ * pppd/main.c.
+ */
+int
+main(int argc, char **argv)
+{
+ int inquiry_mode, exceptflag, arg, localid;
+ char *cp;
+
+ log_to_stderr(LOGLVL_DBG);
+
+ if ((myname = *argv) == NULL)
+ myname = "pppoec";
+
+ inquiry_mode = 0;
+ while ((arg = getopt(argc, argv, "io:s:v")) != EOF)
+ switch (arg) {
+ case 'i':
+ inquiry_mode++;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case 'o':
+ pado_wait_time = strtol(optarg, &cp, 0);
+ if (pado_wait_time <= 0 || *cp != '\0' ||
+ cp == optarg) {
+ logerr("%s: illegal PADO wait time: %s\n",
+ myname, optarg);
+ exit(1);
+ }
+ break;
+ case 's':
+ pads_wait_time = strtol(optarg, &cp, 0);
+ if (pads_wait_time <= 0 || *cp != '\0' ||
+ cp == optarg) {
+ logerr("%s: illegal PADS wait time: %s\n",
+ myname, optarg);
+ exit(1);
+ }
+ break;
+ case '?':
+ usage();
+ }
+
+ /* Handle inquiry mode. */
+ if (inquiry_mode) {
+ if (optind != argc-1)
+ usage();
+ if (pado_wait_time == 0)
+ pado_wait_time = PADI_INQUIRY_DWELL;
+
+ /* Invoked by user; open the tunnel driver myself. */
+ tunfd = open(tunnam, O_RDWR | O_NOCTTY);
+ if (tunfd == -1) {
+ logstrerror(tunnam);
+ exit(1);
+ }
+
+ /*
+ * Set up the control stream for PPPoE negotiation
+ * (set_control), then broadcast a query for all servers
+ * and listen for replies (find_all_servers).
+ */
+ find_all_servers(set_control(argv[optind]));
+ return (0);
+ }
+
+ if (pado_wait_time == 0)
+ pado_wait_time = PADI_RESTART_TIME;
+
+ if (optind >= argc)
+ usage();
+
+ /* Make sure we've got a usable tunnel driver on stdin. */
+ check_stdin();
+
+ /* Set up the control stream for PPPoE negotiation. */
+ localid = set_control(argv[optind++]);
+
+ /* Pick the service, if any. */
+ if (optind < argc)
+ service = argv[optind++];
+
+ /* Parse out the filters. */
+ if (optind < argc) {
+ if (strcasecmp(argv[argc - 1], "only") == 0) {
+ argc--;
+ onlyflag = 1;
+ }
+ exceptflag = 0;
+ for (; optind < argc; optind++) {
+ if (!exceptflag &&
+ strcasecmp(argv[optind], "except") == 0) {
+ exceptflag = 1;
+ } else {
+ parse_filter(argv[optind], exceptflag);
+ exceptflag = 0;
+ }
+ }
+ }
+
+ /* Enter the main loop. */
+ find_server(localid);
+
+ return (0);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/pppoe/pppoed.c b/usr/src/cmd/cmd-inet/usr.lib/pppoe/pppoed.c
new file mode 100644
index 0000000000..294a04d824
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/pppoe/pppoed.c
@@ -0,0 +1,443 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * PPPoE Server-mode daemon for use with Solaris PPP 4.0.
+ *
+ * Copyright (c) 2000-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <stropts.h>
+#include <wait.h>
+#include <sys/resource.h>
+#include <netinet/in.h>
+#include <net/sppptun.h>
+#include <net/pppoe.h>
+
+#include "common.h"
+#include "pppoed.h"
+#include "logging.h"
+
+static int tunfd; /* Global connection to tunnel device */
+
+char *myname; /* Copied from argv[0] for logging */
+static int main_argc; /* Saved for reparse on SIGHUP */
+static char **main_argv; /* Saved for reparse on SIGHUP */
+
+static time_t time_started; /* Time daemon was started; for debug */
+static time_t last_reread; /* Last time configuration was read. */
+
+/* Various operational statistics. */
+static unsigned long input_packets, padi_packets, padr_packets;
+static unsigned long output_packets;
+static unsigned long sessions_started;
+
+static sigset_t sigmask; /* Global signal mask */
+
+/*
+ * Used for handling errors that occur before we daemonize.
+ */
+static void
+early_error(const char *str)
+{
+ const char *cp;
+
+ cp = mystrerror(errno);
+ if (isatty(2)) {
+ (void) fprintf(stderr, "%s: %s: %s\n", myname, str, cp);
+ } else {
+ reopen_log();
+ logerr("%s: %s", str, cp);
+ }
+ exit(1);
+}
+
+/*
+ * Open the sppptun driver.
+ */
+static void
+open_tunnel_dev(void)
+{
+ struct ppptun_peer ptp;
+
+ tunfd = open(tunnam, O_RDWR);
+ if (tunfd == -1) {
+ early_error(tunnam);
+ }
+
+ /*
+ * Tell the device driver that I'm a daemon handling inbound
+ * connections, not a PPP session.
+ */
+ (void) memset(&ptp, '\0', sizeof (ptp));
+ ptp.ptp_style = PTS_PPPOE;
+ ptp.ptp_flags = PTPF_DAEMON;
+ (void) memcpy(ptp.ptp_address.pta_pppoe.ptma_mac, ether_bcast,
+ sizeof (ptp.ptp_address.pta_pppoe.ptma_mac));
+ if (strioctl(tunfd, PPPTUN_SPEER, &ptp, sizeof (ptp), sizeof (ptp)) <
+ 0) {
+ myperror("PPPTUN_SPEER");
+ exit(1);
+ }
+}
+
+/*
+ * Callback function for fdwalk. Closes everything but the tunnel
+ * file descriptor when becoming daemon. (Log file must be reopened
+ * manually, since syslog file descriptor, if any, is unknown.)
+ */
+/*ARGSUSED*/
+static int
+fdcloser(void *arg, int fd)
+{
+ if (fd != tunfd)
+ (void) close(fd);
+ return (0);
+}
+
+/*
+ * Become a daemon.
+ */
+static void
+daemonize(void)
+{
+ pid_t cpid;
+
+ /*
+ * A little bit of magic here. By the first fork+setsid, we
+ * disconnect from our current controlling terminal and become
+ * a session group leader. By forking again without setsid,
+ * we make certain that we're not the session group leader and
+ * can never reacquire a controlling terminal.
+ */
+ if ((cpid = fork()) == (pid_t)-1) {
+ early_error("fork 1");
+ }
+ if (cpid != 0) {
+ (void) wait(NULL);
+ _exit(0);
+ }
+ if (setsid() == (pid_t)-1) {
+ early_error("setsid");
+ }
+ if ((cpid = fork()) == (pid_t)-1) {
+ early_error("fork 2");
+ }
+ if (cpid != 0) {
+ /* Parent just exits */
+ (void) printf("%d\n", (int)cpid);
+ (void) fflush(stdout);
+ _exit(0);
+ }
+ (void) chdir("/");
+ (void) umask(0);
+ (void) fdwalk(fdcloser, NULL);
+ reopen_log();
+}
+
+/*
+ * Handle SIGHUP -- close and reopen non-syslog log files and reparse
+ * options.
+ */
+/*ARGSUSED*/
+static void
+handle_hup(int sig)
+{
+ close_log_files();
+ global_logging();
+ last_reread = time(NULL);
+ parse_options(tunfd, main_argc, main_argv);
+}
+
+/*
+ * Handle SIGINT -- write current daemon status to /tmp.
+ */
+/*ARGSUSED*/
+static void
+handle_int(int sig)
+{
+ FILE *fp;
+ char dumpname[MAXPATHLEN];
+ time_t now;
+ struct rusage rusage;
+
+ (void) snprintf(dumpname, sizeof (dumpname), "/tmp/pppoed.%ld",
+ getpid());
+ if ((fp = fopen(dumpname, "w+")) == NULL) {
+ logerr("%s: %s", dumpname, mystrerror(errno));
+ return;
+ }
+ now = time(NULL);
+ (void) fprintf(fp, "pppoed running %s", ctime(&now));
+ (void) fprintf(fp, "Started on %s", ctime(&time_started));
+ if (last_reread != 0)
+ (void) fprintf(fp, "Last reconfig %s", ctime(&last_reread));
+ (void) putc('\n', fp);
+ if (getrusage(RUSAGE_SELF, &rusage) == 0) {
+ (void) fprintf(fp,
+ "CPU usage: user %ld.%06ld, system %ld.%06ld\n",
+ rusage.ru_utime.tv_sec, rusage.ru_utime.tv_usec,
+ rusage.ru_stime.tv_sec, rusage.ru_stime.tv_usec);
+ }
+ (void) fprintf(fp, "Packets: %lu received (%lu PADI, %lu PADR), ",
+ input_packets, padi_packets, padr_packets);
+ (void) fprintf(fp, "%lu transmitted\n", output_packets);
+ (void) fprintf(fp, "Sessions started: %lu\n\n", sessions_started);
+ dump_configuration(fp);
+ (void) fclose(fp);
+}
+
+static void
+add_signal_handlers(void)
+{
+ struct sigaction sa;
+
+ (void) sigemptyset(&sigmask);
+ (void) sigaddset(&sigmask, SIGHUP);
+ (void) sigaddset(&sigmask, SIGCHLD);
+ (void) sigaddset(&sigmask, SIGINT);
+ (void) sigprocmask(SIG_BLOCK, &sigmask, NULL);
+
+ sa.sa_mask = sigmask;
+ sa.sa_flags = 0;
+
+ /* Signals to handle */
+ sa.sa_handler = handle_hup;
+ if (sigaction(SIGHUP, &sa, NULL) < 0)
+ early_error("sigaction HUP");
+ sa.sa_handler = handle_int;
+ if (sigaction(SIGINT, &sa, NULL) < 0)
+ early_error("sigaction INT");
+
+ /*
+ * Signals to ignore. Ignoring SIGCHLD in this way makes the
+ * children exit without ever creating zombies. (No wait(2)
+ * call required.)
+ */
+ sa.sa_handler = SIG_IGN;
+ if (sigaction(SIGPIPE, &sa, NULL) < 0)
+ early_error("sigaction PIPE");
+ sa.sa_flags = SA_NOCLDWAIT;
+ if (sigaction(SIGCHLD, &sa, NULL) < 0)
+ early_error("sigaction CHLD");
+}
+
+/*
+ * Dispatch a message from the tunnel driver. It could be an actual
+ * PPPoE message or just an event notification.
+ */
+static void
+handle_input(uint32_t *ctrlbuf, int ctrllen, uint32_t *databuf, int datalen)
+{
+ poep_t *poep = (poep_t *)databuf;
+ union ppptun_name ptn;
+ int retv;
+ struct strbuf ctrl;
+ struct strbuf data;
+ void *srvp;
+ boolean_t launch;
+ struct ppptun_control *ptc;
+
+ if (ctrllen != sizeof (*ptc)) {
+ logdbg("bogus %d byte control message from driver",
+ ctrllen);
+ return;
+ }
+ ptc = (struct ppptun_control *)ctrlbuf;
+
+ /* Switch out on event notifications. */
+ switch (ptc->ptc_action) {
+ case PTCA_TEST:
+ logdbg("test reply for discriminator %X", ptc->ptc_discrim);
+ return;
+
+ case PTCA_CONTROL:
+ break;
+
+ case PTCA_DISCONNECT:
+ logdbg("session %d disconnected on %s; send PADT",
+ ptc->ptc_rsessid, ptc->ptc_name);
+ poep = poe_mkheader(pkt_output, POECODE_PADT,
+ ptc->ptc_rsessid);
+ ptc->ptc_action = PTCA_CONTROL;
+ ctrl.len = sizeof (*ptc);
+ ctrl.buf = (caddr_t)ptc;
+ data.len = poe_length(poep) + sizeof (*poep);
+ data.buf = (caddr_t)poep;
+ if (putmsg(tunfd, &ctrl, &data, 0) < 0) {
+ logerr("putmsg PADT: %s", mystrerror(errno));
+ } else {
+ output_packets++;
+ }
+ return;
+
+ case PTCA_UNPLUMB:
+ logdbg("%s unplumbed", ptc->ptc_name);
+ return;
+
+ default:
+ logdbg("unexpected code %d from driver", ptc->ptc_action);
+ return;
+ }
+
+ /* Only PPPoE control messages get here. */
+
+ input_packets++;
+ if (datalen < sizeof (*poep)) {
+ logdbg("incomplete PPPoE message from %s/%s",
+ ehost(&ptc->ptc_address), ptc->ptc_name);
+ return;
+ }
+
+ /* Server handles only PADI and PADR; all others are ignored. */
+ if (poep->poep_code == POECODE_PADI) {
+ padi_packets++;
+ } else if (poep->poep_code == POECODE_PADR) {
+ padr_packets++;
+ } else {
+ loginfo("unexpected %s from %s",
+ poe_codename(poep->poep_code), ehost(&ptc->ptc_address));
+ return;
+ }
+ logdbg("Recv from %s/%s: %s", ehost(&ptc->ptc_address), ptc->ptc_name,
+ poe_codename(poep->poep_code));
+
+ /* Parse out service and formulate template reply. */
+ retv = locate_service(poep, datalen, ptc->ptc_name, &ptc->ptc_address,
+ pkt_output, &srvp);
+
+ /* Continue formulating reply */
+ launch = B_FALSE;
+ if (retv != 1) {
+ /* Ignore initiation if we don't offer a service. */
+ if (retv <= 0 && poep->poep_code == POECODE_PADI) {
+ logdbg("no services; no reply");
+ return;
+ }
+ if (retv == 0)
+ (void) poe_add_str((poep_t *)pkt_output, POETT_NAMERR,
+ "No such service.");
+ } else {
+ /* Exactly one service chosen; if it's PADR, then we start. */
+ if (poep->poep_code == POECODE_PADR) {
+ launch = B_TRUE;
+ }
+ }
+ poep = (poep_t *)pkt_output;
+
+ /* Select control interface for output. */
+ (void) strncpy(ptn.ptn_name, ptc->ptc_name, sizeof (ptn.ptn_name));
+ if (strioctl(tunfd, PPPTUN_SCTL, &ptn, sizeof (ptn), 0) < 0) {
+ logerr("PPPTUN_SCTL %s: %s", ptn.ptn_name, mystrerror(errno));
+ return;
+ }
+
+ /* Launch the PPP service */
+ if (launch && launch_service(tunfd, poep, srvp, ptc))
+ sessions_started++;
+
+ /* Send the reply. */
+ ctrl.len = sizeof (*ptc);
+ ctrl.buf = (caddr_t)ptc;
+ data.len = poe_length(poep) + sizeof (*poep);
+ data.buf = (caddr_t)poep;
+ if (putmsg(tunfd, &ctrl, &data, 0) < 0) {
+ logerr("putmsg %s: %s", ptc->ptc_name, mystrerror(errno));
+ } else {
+ output_packets++;
+ logdbg("Send to %s/%s: %s", ehost(&ptc->ptc_address),
+ ptc->ptc_name, poe_codename(poep->poep_code));
+ }
+}
+
+static void
+main_loop(void)
+{
+ struct strbuf ctrl;
+ struct strbuf data;
+ int flags;
+ int rc;
+ int err;
+
+ for (;;) {
+ ctrl.maxlen = PKT_OCTL_LEN;
+ ctrl.buf = (caddr_t)pkt_octl;
+ data.maxlen = PKT_INPUT_LEN;
+ data.buf = (caddr_t)pkt_input;
+ /* Allow signals only while idle */
+ (void) sigprocmask(SIG_UNBLOCK, &sigmask, NULL);
+ errno = 0;
+ flags = 0;
+ rc = mygetmsg(tunfd, &ctrl, &data, &flags);
+ err = errno;
+ /*
+ * Block signals -- data structures must not change
+ * while we're busy dispatching the client's request
+ */
+ (void) sigprocmask(SIG_BLOCK, &sigmask, NULL);
+ if (rc == -1) {
+ if (err == EAGAIN || err == EINTR)
+ continue;
+ logerr("%s getmsg: %s", tunnam, mystrerror(err));
+ exit(1);
+ }
+ if (rc > 0)
+ logwarn("%s returned truncated data", tunnam);
+ else
+ handle_input(pkt_octl, ctrl.len, pkt_input, data.len);
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ prog_name = "pppoed";
+ log_level = 1; /* Default to error messages only at first */
+
+ time_started = time(NULL);
+
+ if ((myname = argv[0]) == NULL)
+ myname = "pppoed";
+
+ main_argc = argc;
+ main_argv = argv;
+
+ open_tunnel_dev();
+ add_signal_handlers();
+ daemonize();
+
+ parse_options(tunfd, argc, argv);
+ main_loop();
+
+ return (0);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/pppoe/pppoed.h b/usr/src/cmd/cmd-inet/usr.lib/pppoe/pppoed.h
new file mode 100644
index 0000000000..03e57a5329
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/pppoe/pppoed.h
@@ -0,0 +1,53 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * PPPoE Server-mode daemon option parsing.
+ *
+ * Copyright (c) 2000-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef PPPOED_H
+#define PPPOED_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include "common.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Functions in options.c */
+extern void parse_options(int tunfd, int argc, char **argv);
+extern int locate_service(poep_t *poep, int plen, const char *iname,
+ ppptun_atype *pap, uint32_t *outp, void **srvp);
+extern int launch_service(int tunfd, poep_t *poep, void *srvp,
+ struct ppptun_control *ptc);
+extern void dump_configuration(FILE *fp);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PPPOED_H */
diff --git a/usr/src/cmd/cmd-inet/usr.lib/req.flg b/usr/src/cmd/cmd-inet/usr.lib/req.flg
new file mode 100644
index 0000000000..54adcef540
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/req.flg
@@ -0,0 +1,29 @@
+#!/bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright (c) 1999 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+echo_file usr/src/cmd/cmd-inet/usr.lib/Makefile.lib
diff --git a/usr/src/cmd/cmd-inet/usr.lib/slpd/Makefile b/usr/src/cmd/cmd-inet/usr.lib/slpd/Makefile
new file mode 100644
index 0000000000..bbad20743c
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/slpd/Makefile
@@ -0,0 +1,54 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+PROG= slpd
+MANIFEST= slp.xml
+
+include ../../../Makefile.cmd
+ROOTMANIFESTDIR= $(ROOTSVCNETWORK)
+ROOTMETHOD= $(ROOTLIBSVCMETHOD)/slp
+$(ROOTMANIFEST) := FILEMODE= 444
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+include ../Makefile.lib
+
+CPPFLAGS += -D_REENTRANT
+LDLIBS += -lsocket -lslp
+
+install: all $(ROOTLIBINETPROG) $(ROOTMANIFEST) $(ROOTMETHOD)
+
+check: $(CHKMANIFEST)
+
+clean:
+
+include ../../../Makefile.targ
+
+lint: lint_PROG
diff --git a/usr/src/cmd/cmd-inet/usr.lib/slpd/slp b/usr/src/cmd/cmd-inet/usr.lib/slpd/slp
new file mode 100644
index 0000000000..1fd7565897
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/slpd/slp
@@ -0,0 +1,80 @@
+#!/sbin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Service Method Support Script for the SLP service
+#
+# - operates a proxy for slpd which brings up the JVM to run slpd
+# when required by a client; this prevents keeping JVM's alive
+# when the service is not in use.
+#
+
+. /lib/svc/share/smf_include.sh
+
+CONF=/etc/inet/slp.conf
+JAVA_BIN=/usr/j2se/bin/java
+CLASSPATH=/usr/share/lib/slp/slpd.jar
+MAIN_CLASS=com.sun.slp.slpd
+SLPD_HOME=/usr/lib/inet
+SLPD=slpd
+SLPD_BIN=$SLPD_HOME/$SLPD
+
+case "$1" in
+'start')
+ # Start slpd proxy (as a fragile dependency, conf file must exist)
+ $SLPD_BIN -f $CONF >/dev/msglog 2>&1 &
+ ;;
+
+'stop')
+ # Kill the slpd proxy.
+ /usr/bin/pkill -x -u 0 -P 1 -z `/sbin/zonename` $SLPD
+
+ # If a configuration file exists signal a shutdown to the real slpd.
+ [ -f $CONF ] && {
+ $JAVA_BIN -classpath $CLASSPATH \
+ $MAIN_CLASS stop -f $CONF >/dev/msglog 2>&1 &
+
+ # Give the above slpd instance a chance to signal
+ # a shutdown to the real slpd instance. If after
+ # this time it has hung kill it.
+ sleep 5
+
+ # The pattern must not exceed 80 chars!
+ /usr/bin/pkill -x -f -u 0 -P 1,$$ -z `/sbin/zonename` \
+ "${JAVA_BIN}.*-classpath ${CLASSPATH} .*"
+ }
+
+ # Kill the slpd proxy service contract
+ smf_kill_contract $2 TERM 1
+ [ $? -ne 0 ] && exit 1
+ ;;
+*)
+ echo "Usage: $0 { start | stop }"
+ exit 1
+ ;;
+esac
+exit 0
diff --git a/usr/src/cmd/cmd-inet/usr.lib/slpd/slp.xml b/usr/src/cmd/cmd-inet/usr.lib/slpd/slp.xml
new file mode 100644
index 0000000000..37b6499da3
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/slpd/slp.xml
@@ -0,0 +1,107 @@
+<?xml version="1.0"?>
+<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
+<!--
+ Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ NOTE: This service manifest is not editable; its contents will
+ be overwritten by package or patch operations, including
+ operating system upgrade. Make customizations in a different
+ file.
+
+ ident "%Z%%M% %I% %E% SMI"
+-->
+
+<service_bundle type='manifest' name='SUNWslpr:slpd'>
+
+<service
+ name='network/slp'
+ type='service'
+ version='1'>
+
+ <create_default_instance enabled='false' />
+
+ <single_instance />
+
+ <dependency
+ name='paths'
+ grouping='require_all'
+ restart_on='error'
+ type='path'>
+ <service_fmri value='file://localhost/etc/inet/slp.conf' />
+ </dependency>
+
+ <dependency
+ name='loopback'
+ grouping='require_any'
+ restart_on='error'
+ type='service'>
+ <service_fmri value='svc:/network/loopback' />
+ </dependency>
+
+ <dependency
+ name='network'
+ grouping='optional_all'
+ restart_on='error'
+ type='service'>
+ <service_fmri value='svc:/milestone/network' />
+ </dependency>
+
+ <dependency
+ name='milestone'
+ grouping='require_all'
+ restart_on='none'
+ type='service'>
+ <service_fmri value='svc:/milestone/sysconfig' />
+ </dependency>
+
+ <exec_method
+ type='method'
+ name='start'
+ exec='/lib/svc/method/slp start'
+ timeout_seconds='60' />
+
+ <exec_method
+ type='method'
+ name='stop'
+ exec='/lib/svc/method/slp stop %{restarter/contract}'
+ timeout_seconds='60' />
+
+ <stability value='Unstable' />
+
+ <template>
+
+ <common_name>
+ <loctext xml:lang='C'>Service Location Protocol (SLP)
+ </loctext>
+ </common_name>
+ <documentation>
+ <manpage title='slpd' section='1M' />
+ <manpage title='slp' section='7P' />
+ <manpage title='slp.conf' section='4' />
+ </documentation>
+
+ </template>
+</service>
+
+</service_bundle>
diff --git a/usr/src/cmd/cmd-inet/usr.lib/slpd/slpd.c b/usr/src/cmd/cmd-inet/usr.lib/slpd/slpd.c
new file mode 100644
index 0000000000..ff4010488c
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/slpd/slpd.c
@@ -0,0 +1,226 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+
+/*
+ * This is a proxy daemon for the real Java slpd. This deamon starts
+ * at boot time, and listens for any incoming SLP messages only on
+ * loopback -- this way, only local processes can start the real
+ * daemon. When a message comes in, the proxy daemon dups the message
+ * fds onto fds 0, 1, and 2, and execs the real Java slpd. The purpose
+ * of this approach is for performance: boot time performance is
+ * not degraded by cranking off the (huge) JVM, and systems take
+ * the JVM resource hit only if they actually use SLP.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/byteorder.h>
+#include <sys/resource.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#include <slp.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <syslog.h>
+
+/* This is an index which points into the args array at the conf file arg */
+#define CONF_INDEX 6
+
+/* Location of the Java Virtual Machine */
+#define JAVA_VM "/usr/java/jre/bin/java"
+
+static char *slpd_args[] = {
+ JAVA_VM,
+ "-Xmx128m",
+ "-classpath",
+ "/usr/share/lib/slp/slpd.jar",
+ "com.sun.slp.slpd",
+ "-f",
+ "/etc/inet/slp.conf",
+ 0
+};
+
+/*
+ * These are global so they can be easily accessed from a signal
+ * handler for cleanup.
+ */
+
+static void
+run_slpd(void)
+{
+ closelog();
+
+ if (execv(*slpd_args, slpd_args) == -1) {
+ openlog("slpd", LOG_PID, LOG_DAEMON);
+ syslog(LOG_ERR, "execv failed: %s", strerror(errno));
+ closelog();
+ }
+}
+
+/*
+ * If an alternate config file was specified with -f, make sure slpd
+ * uses that config file. Also, force libslp.so to use that new config
+ * file when checking to see if slpd is a DA. If any other arguments
+ * are given, they are ignored.
+ */
+static void
+do_args(int argc, char *const *argv)
+{
+ int c;
+ char *conf = NULL;
+
+ while ((c = getopt(argc, argv, "f:")) != EOF)
+ switch (c) {
+ case 'f':
+ conf = optarg;
+ break;
+ default:
+ break;
+ }
+
+ if (conf != NULL) {
+ char *prefix = "SLP_CONF_FILE=";
+ int env_size;
+ char *conf_env;
+
+ env_size = strlen(prefix) + strlen(conf) + 1;
+ if ((conf_env = malloc(env_size)) == NULL) {
+ syslog(LOG_ERR, "no memory");
+ exit(1);
+ }
+ (void) strlcpy(conf_env, prefix, env_size);
+ (void) strlcat(conf_env, conf, env_size);
+
+ (void) putenv(conf_env);
+
+ slpd_args[CONF_INDEX] = conf;
+ }
+}
+
+static void
+detachfromtty(void) {
+ switch (fork()) {
+ case -1:
+ perror("slpd: can not fork");
+ exit(1);
+ /*NOTREACHED*/
+ case 0:
+ break;
+ default:
+ exit(0);
+ }
+
+ /*
+ * Close existing file descriptors, open "/dev/null" as
+ * standard input, output, and error, and detach from
+ * controlling terminal.
+ */
+ closefrom(0);
+ (void) open("/dev/null", O_RDONLY);
+ (void) open("/dev/null", O_WRONLY);
+ (void) dup(1);
+ (void) setsid();
+}
+
+static void
+cleanup_and_exit(int retval)
+{
+ closelog();
+ exit(retval);
+}
+
+int
+main(int argc, char *const *argv)
+{
+ struct sockaddr_in bindaddr;
+ socklen_t addrlen;
+ const char *isDA;
+ const char *proxyReg;
+ int connfd;
+ int lfd;
+ const int on = 1;
+
+ detachfromtty();
+
+ openlog("slpd", LOG_PID, LOG_DAEMON);
+
+ do_args(argc, argv);
+
+ /* If slpd has been configured to run as a DA, start it and exit */
+ isDA = SLPGetProperty("net.slp.isDA");
+ proxyReg = SLPGetProperty("net.slp.serializedRegURL");
+ if ((isDA && (strcasecmp(isDA, "true") == 0)) || proxyReg) {
+ run_slpd();
+ return (1);
+ }
+
+ if ((lfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ syslog(LOG_ERR, "socket failed: %s", strerror(errno));
+ cleanup_and_exit(1);
+ }
+
+ (void) setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on));
+
+ (void) memset((void *)&bindaddr, 0, sizeof (bindaddr));
+ bindaddr.sin_family = AF_INET;
+ bindaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ bindaddr.sin_port = htons(427);
+
+ if (bind(lfd, (const struct sockaddr *)&bindaddr, sizeof (bindaddr))
+ < 0) {
+ syslog(LOG_ERR, "bind failed: %s", strerror(errno));
+ cleanup_and_exit(1);
+ }
+
+ if (listen(lfd, 1) < 0) {
+ syslog(LOG_ERR, "listen failed: %s", strerror(errno));
+ cleanup_and_exit(1);
+ }
+
+ addrlen = sizeof (bindaddr);
+ if ((connfd = accept(lfd, (struct sockaddr *)&bindaddr, &addrlen))
+ < 0) {
+ syslog(LOG_ERR, "accept failed: %s", strerror(errno));
+ cleanup_and_exit(1);
+ }
+
+ (void) close(lfd);
+
+ (void) dup2(connfd, 0);
+ (void) close(connfd);
+ (void) dup2(0, 1);
+ (void) dup2(0, 2);
+
+ run_slpd();
+
+ return (1);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/wanboot/Makefile b/usr/src/cmd/cmd-inet/usr.lib/wanboot/Makefile
new file mode 100644
index 0000000000..2d7d04478f
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/wanboot/Makefile
@@ -0,0 +1,79 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+include $(SRC)/cmd/Makefile.cmd
+
+SUBDIR_SCR= bootlog-cgi
+SUBDIR_PGMS= wanboot-cgi\
+ keygen \
+ keymgmt \
+ hmac \
+ encr \
+ ickey \
+ p12split \
+ netbootinfo
+
+SUBDIRS= $(SUBDIR_SCR) $(SUBDIR_PGMS)
+
+MSGFILES= encr/encr.c hmac/hmac.c ickey/ickey.c keygen/keygen.c \
+ keymgmt/keymgmt.c p12split/p12split.c netbootinfo/netbootinfo.c
+
+POFILE= wanboot.po
+XGETFLAGS += -a -x wanboot.xcl
+
+all:= TARGET= all
+install:= TARGET= install
+clean:= TARGET= clean
+clobber:= TARGET= clobber
+lint:= TARGET= lint
+_msg:= TARGET= _msg
+
+.KEEP_STATE:
+.PARALLEL: $(SUBDIRS)
+
+all install: $(SUBDIRS)
+
+lint: $(SUBDIR_PGMS)
+
+clean: $(SUBDIR_PGMS)
+
+clobber: $(SUBDIR_PGMS) local_clobber
+
+local_clobber:
+ $(RM) $(CLOBBERFILES)
+
+$(POFILE): pofile_MSGFILES
+
+_msg: $(MSGDOMAINPOFILE)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
+
+include $(SRC)/Makefile.msg.targ
diff --git a/usr/src/cmd/cmd-inet/usr.lib/wanboot/Makefile.com b/usr/src/cmd/cmd-inet/usr.lib/wanboot/Makefile.com
new file mode 100644
index 0000000000..b7472d34ae
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/wanboot/Makefile.com
@@ -0,0 +1,34 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+include $(SRC)/cmd/Makefile.cmd
+ROOTCMDDIR = $(ROOT)/usr/lib/inet/wanboot
+
+CMNCRYPTDIR = ../../../../../common/net/wanboot/crypt
+
+.KEEP_STATE:
diff --git a/usr/src/cmd/cmd-inet/usr.lib/wanboot/bootlog-cgi/Makefile b/usr/src/cmd/cmd-inet/usr.lib/wanboot/bootlog-cgi/Makefile
new file mode 100644
index 0000000000..b84272bc89
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/wanboot/bootlog-cgi/Makefile
@@ -0,0 +1,37 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+include ../Makefile.com
+
+PROG= bootlog-cgi
+
+all: $(PROG)
+
+install: all $(ROOTCMD)
+
+include ../../../../Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/usr.lib/wanboot/bootlog-cgi/bootlog-cgi b/usr/src/cmd/cmd-inet/usr.lib/wanboot/bootlog-cgi/bootlog-cgi
new file mode 100644
index 0000000000..0e4317de55
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/wanboot/bootlog-cgi/bootlog-cgi
@@ -0,0 +1,59 @@
+#! /usr/bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+# cgi script to handle bootlog messages
+# formats the message:
+# replaces <time> placemarker by local time
+# removes '\' inserted by cgi gateway
+# writes the message to a log file
+
+BOOTLOG_PATH="/tmp/"
+BOOTLOG_FILE_PREFIX="bootlog"
+hostname=$2
+
+# disable filename globbing
+set -f
+
+bootlog_file=${BOOTLOG_PATH}${BOOTLOG_FILE_PREFIX}.$hostname
+
+# write date in bootlog format
+echo "`date '+%b %d %H:%M:%S'` \c" >> $bootlog_file
+# remove backslashes inserted by CGI gateway
+# and remove time placeholder
+echo $* | sed 's/\\//g;s/<time>//g' >> $bootlog_file
+
+# Do not change these lines vvv
+echo Content-type: text/plain
+echo Content-length: 32
+echo
+
+echo CGI/1.0 bootlog script report:
+echo
+# Do not change these lines ^^^
+
diff --git a/usr/src/cmd/cmd-inet/usr.lib/wanboot/encr/Makefile b/usr/src/cmd/cmd-inet/usr.lib/wanboot/encr/Makefile
new file mode 100644
index 0000000000..4d57d0ad1e
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/wanboot/encr/Makefile
@@ -0,0 +1,44 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+include ../Makefile.com
+
+PROG = encr
+LDLIBS += -lwanbootutil
+
+CPPFLAGS += -I$(CMNCRYPTDIR)
+
+all: $(PROG)
+
+install: all $(ROOTCMD)
+
+clean:
+
+lint: lint_PROG
+
+include ../../../../Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/usr.lib/wanboot/encr/encr.c b/usr/src/cmd/cmd-inet/usr.lib/wanboot/encr/encr.c
new file mode 100644
index 0000000000..730f5b8101
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/wanboot/encr/encr.c
@@ -0,0 +1,420 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <libintl.h>
+#include <locale.h>
+#include <sys/des.h>
+#include <strings.h>
+#include <errno.h>
+#include <wanbootutil.h>
+#include <sys/sysmacros.h>
+#include <sys/wanboot_impl.h>
+
+/* Return codes */
+#define ENCR_SUCCESS 0
+#define ENCR_NOKEY 1
+#define ENCR_ERROR 2
+
+/* Private buffer length */
+#define ENCR_BUF_LEN 1024
+
+/* Encryption algorithm suboption. */
+#define TYPE 0
+
+static char *opts[] = { "type", NULL };
+
+/*
+ * This routine is used to parse the suboptions of '-o' option.
+ *
+ * The option should be of the form: type=<3des|aes>
+ *
+ * This routine will pass the value of the suboption back in the
+ * supplied arguments, 'ka'.
+ *
+ * Returns:
+ * ENCR_SUCCESS or ENCR_ERROR.
+ */
+static int
+process_option(char *arg, wbku_key_attr_t *ka)
+{
+ char *value;
+ wbku_retcode_t ret;
+
+ while (*arg != '\0') {
+ switch (getsubopt(&arg, opts, &value)) {
+ case TYPE:
+ /*
+ * Key type.
+ */
+ ret = wbku_str_to_keyattr(value, ka, WBKU_ENCR_KEY);
+ if (ret != WBKU_SUCCESS) {
+ wbku_printerr("%s\n", wbku_retmsg(ret));
+ return (ENCR_ERROR);
+ }
+ break;
+ default:
+ wbku_printerr("Invalid option %s\n", value);
+ return (ENCR_ERROR);
+ }
+ }
+
+ return (ENCR_SUCCESS);
+}
+
+/*
+ * This routine is used to find the key of type defined by 'ka' and
+ * return it in 'key'. The key file should have been opened by the
+ * caller and the handle passed in 'key_fp'.
+ *
+ * Returns:
+ * ENCR_SUCCESS, ENCR_ERROR or ENCR_NOKEY.
+ */
+static int
+get_key(FILE *key_fp, wbku_key_attr_t *ka, uint8_t *key)
+{
+ wbku_retcode_t ret;
+
+ /*
+ * Find the client key, if it exists.
+ */
+ ret = wbku_find_key(key_fp, NULL, ka, key, B_FALSE);
+ if (ret != WBKU_SUCCESS) {
+ wbku_printerr("%s\n", wbku_retmsg(ret));
+ if (ret == WBKU_NOKEY)
+ return (ENCR_NOKEY);
+ else
+ return (ENCR_ERROR);
+ }
+ return (ENCR_SUCCESS);
+}
+
+/*
+ * This routine is the common encryption routine used to encrypt data
+ * using the CBC handle initialized by the calling routine. The data
+ * to be encrypted is read from stdin and the encrypted data is written to
+ * stdout.
+ *
+ * Returns:
+ * ENCR_SUCCESS or ENCR_ERROR.
+ */
+static int
+encr_gen(cbc_handle_t *ch)
+{
+ uint8_t iv[WANBOOT_MAXBLOCKLEN];
+ uint8_t buf[ENCR_BUF_LEN];
+ uint8_t *bufp;
+ int read_size;
+ ssize_t i, j, k;
+
+ /*
+ * Use a random number as the IV
+ */
+ if (wbio_nread_rand(iv, ch->blocklen) != 0) {
+ wbku_printerr("Cannot generate initialization vector");
+ return (ENCR_ERROR);
+ }
+
+ /*
+ * Output the IV to stdout.
+ */
+ if (wbio_nwrite(STDOUT_FILENO, iv, ch->blocklen) != 0) {
+ wbku_printerr("Write error encountered\n");
+ return (ENCR_ERROR);
+ }
+
+ /*
+ * Try to read in multiple of block_size as CBC requires
+ * that data be encrypted in block_size chunks.
+ */
+ read_size = ENCR_BUF_LEN / ch->blocklen * ch->blocklen;
+ while ((i = read(STDIN_FILENO, buf, read_size)) > 0) {
+ /*
+ * If data received is not a multiple of the block size,
+ * try to receive more. If reach EOF, pad the rest with
+ * 0.
+ */
+ if ((j = i % ch->blocklen) != 0) {
+ /*
+ * Determine how more data need to be received to
+ * fill out the buffer so that it contains a
+ * multiple of block_size chunks.
+ */
+ j = ch->blocklen - j;
+ bufp = buf + i;
+ k = j;
+
+ /*
+ * Try to fill the gap.
+ *
+ */
+ while ((j = read(STDIN_FILENO, bufp, j)) != k &&
+ j != 0) {
+ bufp += j;
+ k -= j;
+ j = k;
+ }
+
+ /*
+ * This is the total length of the buffer.
+ */
+ i = (i + ch->blocklen) - (i % ch->blocklen);
+
+ if (j == 0) {
+ /* EOF, do padding. */
+ (void) memset(bufp, 0, k);
+ (void) cbc_encrypt(ch, buf, i, iv);
+ } else if (j > 0) {
+ /* The gap has been filled in */
+ (void) cbc_encrypt(ch, buf, i, iv);
+ } else {
+ /* Oops. */
+ wbku_printerr("Input error");
+ return (ENCR_ERROR);
+ }
+ } else {
+ /* A multiple of the block size was received */
+ (void) cbc_encrypt(ch, buf, i, iv);
+ }
+ if (wbio_nwrite(STDOUT_FILENO, buf, i) != 0) {
+ wbku_printerr("Write error encountered\n");
+ return (ENCR_ERROR);
+ }
+ }
+
+ return (ENCR_SUCCESS);
+}
+
+/*
+ * This routine initializes a CBC handle for 3DES and calls the
+ * common encryption routine to encrypt data.
+ *
+ * Returns:
+ * ENCR_SUCCESS or ENCR_ERROR.
+ */
+static int
+encr_gen_3des(const wbku_key_attr_t *ka, const uint8_t *key)
+{
+ cbc_handle_t ch;
+ void *eh;
+ int ret;
+
+ /*
+ * Initialize a 3DES handle.
+ */
+ if (des3_init(&eh) != 0) {
+ return (ENCR_ERROR);
+ }
+ des3_key(eh, key);
+
+ /*
+ * Initialize the CBC handle.
+ */
+ cbc_makehandle(&ch, eh, ka->ka_len, DES3_BLOCK_SIZE,
+ DES3_IV_SIZE, des3_encrypt, des3_decrypt);
+
+ /*
+ * Encrypt the data.
+ */
+ ret = encr_gen(&ch);
+
+ /*
+ * Free the 3DES resources.
+ */
+ des3_fini(eh);
+
+ return (ret);
+}
+
+/*
+ * This routine initializes a CBC handle for AES and calls the
+ * common encryption routine to encrypt data.
+ *
+ * Returns:
+ * ENCR_SUCCESS or ENCR_ERROR.
+ */
+static int
+encr_gen_aes(const wbku_key_attr_t *ka, const uint8_t *key)
+{
+ cbc_handle_t ch;
+ void *eh;
+ int ret;
+
+ /*
+ * Initialize an AES handle.
+ */
+ if (aes_init(&eh) != 0) {
+ return (ENCR_ERROR);
+ }
+ aes_key(eh, key, ka->ka_len);
+
+ /*
+ * Initialize the CBC handle.
+ */
+ cbc_makehandle(&ch, eh, ka->ka_len, AES_BLOCK_SIZE,
+ AES_IV_SIZE, aes_encrypt, aes_decrypt);
+
+ /*
+ * Encrypt the data.
+ */
+ ret = encr_gen(&ch);
+
+ /*
+ * Free the AES resources.
+ */
+ aes_fini(eh);
+
+ return (ret);
+}
+
+/*
+ * Prints usage().
+ */
+static void
+usage(const char *cmd)
+{
+ (void) fprintf(stderr,
+ gettext("Usage: %s -o type=<%s|%s> -k key_file\n"),
+ cmd, WBKU_KW_3DES, WBKU_KW_AES_128);
+}
+
+/*
+ * This program is used to encrypt data read from stdin and print it to
+ * stdout. The path to the key file and the algorithm to use are
+ * provided by the user.
+ *
+ * Returns:
+ * ENCR_SUCCESS, ENCR_ERROR or ENCR_NOKEY.
+ */
+int
+main(int argc, char **argv)
+{
+ uint8_t key[WANBOOT_MAXKEYLEN];
+ int c;
+ char *keyfile_name = NULL;
+ wbku_key_attr_t ka;
+ FILE *key_fp;
+ int ret;
+
+ /*
+ * Do the necessary magic for localization support.
+ */
+ (void) setlocale(LC_ALL, "");
+#if !defined(TEXT_DOMAIN)
+#define TEXT_DOMAIN "SYS_TEST"
+#endif
+ (void) textdomain(TEXT_DOMAIN);
+
+ /*
+ * Initialize program name for use by wbku_printerr().
+ */
+ wbku_errinit(argv[0]);
+
+ /*
+ * Should be five arguments.
+ */
+ if (argc < 5) {
+ usage(argv[0]);
+ return (ENCR_ERROR);
+ }
+
+ /*
+ * Parse the options.
+ */
+ ka.ka_type = WBKU_KEY_UNKNOWN;
+ while ((c = getopt(argc, argv, "o:k:")) != EOF) {
+ switch (c) {
+ case 'o':
+ /*
+ * Suboptions.
+ */
+ ret = process_option(optarg, &ka);
+ if (ret != ENCR_SUCCESS) {
+ usage(argv[0]);
+ return (ret);
+ }
+ break;
+ case 'k':
+ /*
+ * Path to key file.
+ */
+ keyfile_name = optarg;
+ break;
+ default:
+ usage(argv[0]);
+ return (ENCR_ERROR);
+ }
+ }
+
+ /*
+ * Gotta have a key file.
+ */
+ if (keyfile_name == NULL) {
+ wbku_printerr("Must specify the key_file\n");
+ return (ENCR_ERROR);
+ }
+
+ /*
+ * Gotta have a key type.
+ */
+ if (ka.ka_type == WBKU_KEY_UNKNOWN) {
+ wbku_printerr("Unsupported encryption algorithm\n");
+ return (ENCR_ERROR);
+ }
+
+ /*
+ * Open the key file for reading.
+ */
+ if ((key_fp = fopen(keyfile_name, "r")) == NULL) {
+ wbku_printerr("Cannot open %s", keyfile_name);
+ return (ENCR_ERROR);
+ }
+
+ /*
+ * Get the key from the key file and call the right
+ * encryption routine.
+ */
+ ret = get_key(key_fp, &ka, key);
+ if (ret == ENCR_SUCCESS) {
+ switch (ka.ka_type) {
+ case WBKU_KEY_3DES:
+ ret = encr_gen_3des(&ka, key);
+ break;
+ case WBKU_KEY_AES_128:
+ ret = encr_gen_aes(&ka, key);
+ break;
+ default:
+ ret = ENCR_ERROR; /* Internal error only */
+ }
+ }
+
+ (void) fclose(key_fp);
+ return (ret);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/wanboot/hmac/Makefile b/usr/src/cmd/cmd-inet/usr.lib/wanboot/hmac/Makefile
new file mode 100644
index 0000000000..2b1733c3cc
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/wanboot/hmac/Makefile
@@ -0,0 +1,44 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+include ../Makefile.com
+
+PROG= hmac
+LDLIBS += -lwanbootutil
+
+CPPFLAGS += -I$(CMNCRYPTDIR)
+
+all: $(PROG)
+
+install: all $(ROOTCMD)
+
+clean:
+
+lint: lint_PROG
+
+include ../../../../Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/usr.lib/wanboot/hmac/hmac.c b/usr/src/cmd/cmd-inet/usr.lib/wanboot/hmac/hmac.c
new file mode 100644
index 0000000000..320550cf90
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/wanboot/hmac/hmac.c
@@ -0,0 +1,237 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <libintl.h>
+#include <locale.h>
+#include <string.h>
+#include <errno.h>
+#include <wanbootutil.h>
+#include <sys/wanboot_impl.h>
+
+/* Return codes */
+#define HMAC_SUCCESS 0
+#define HMAC_NOKEY 1
+#define HMAC_ERROR 2
+
+/* Private buffer length */
+#define HMAC_BUF_LEN 1024
+
+/*
+ * This routine is used to compute a hash digest for the file represented
+ * by the file descirptor, 'fd'. The key, 'hmac_key', and key type, 'ka',
+ * will be provided by the caller. The resulting hash digest will be
+ * written to stdout.
+ *
+ * Returns:
+ * HMAC_SUCCESS or HMAC_ERROR.
+ */
+static int
+hash_gen(int in_fd, const wbku_key_attr_t *ka, const uint8_t *hmac_key)
+{
+ SHA1_CTX ctx;
+ uint8_t buf[HMAC_BUF_LEN];
+ ssize_t i;
+ uint8_t digest[HMAC_DIGEST_LEN];
+
+ /*
+ * Initialize the computation.
+ */
+ HMACInit(&ctx, hmac_key, ka->ka_len);
+
+ /*
+ * Read the data to hash.
+ */
+ while ((i = read(in_fd, buf, HMAC_BUF_LEN)) > 0) {
+ HMACUpdate(&ctx, buf, i);
+ }
+ if (i < 0) {
+ wbku_printerr("Cannot read input_file");
+ return (HMAC_ERROR);
+ }
+
+ /*
+ * Finalize the digest.
+ */
+ HMACFinal(&ctx, hmac_key, ka->ka_len, digest);
+
+ /*
+ * Write the digest to stdout.
+ */
+ if (wbio_nwrite(STDOUT_FILENO, digest, sizeof (digest)) != 0) {
+ wbku_printerr("Cannot output digest");
+ return (HMAC_ERROR);
+ }
+
+ /*
+ * Success.
+ */
+ return (HMAC_SUCCESS);
+}
+
+/*
+ * Prints usage().
+ */
+static void
+usage(const char *cmd)
+{
+ (void) fprintf(stderr,
+ gettext("Usage: %s [-i input_file] -k key_file\n"), cmd);
+}
+
+/*
+ * This program is used to compute a hash digest for data read in from
+ * stdin or optionally, a file. The resulting hash digest will be written
+ * to stdout.
+ *
+ * Returns:
+ * HMAC_SUCCESS, HMAC_ERROR or HMAC_NOKEY.
+ */
+int
+main(int argc, char **argv)
+{
+ uint8_t hmac_key[WANBOOT_HMAC_KEY_SIZE];
+ int c;
+ char *infile_name = NULL;
+ char *keyfile_name = NULL;
+ int in_fd = -1;
+ FILE *key_fp = NULL;
+ wbku_key_attr_t ka;
+ wbku_retcode_t wbkuret;
+ int ret = HMAC_ERROR;
+
+ /*
+ * Do the necessary magic for localization support.
+ */
+ (void) setlocale(LC_ALL, "");
+#if !defined(TEXT_DOMAIN)
+#define TEXT_DOMAIN "SYS_TEST"
+#endif
+ (void) textdomain(TEXT_DOMAIN);
+
+ /*
+ * Initialize program name for use by wbku_printerr().
+ */
+ wbku_errinit(argv[0]);
+
+ /*
+ * Should be at least three arguments.
+ */
+ if (argc < 3) {
+ usage(argv[0]);
+ return (HMAC_ERROR);
+ }
+
+ /*
+ * Parse the options.
+ */
+ while ((c = getopt(argc, argv, "i:k:")) != EOF) {
+ switch (c) {
+ case 'i':
+ /*
+ * Optional input file.
+ */
+ infile_name = optarg;
+ break;
+ case 'k':
+ /*
+ * Path to key file.
+ */
+ keyfile_name = optarg;
+ break;
+ default:
+ usage(argv[0]);
+ return (HMAC_ERROR);
+ }
+ }
+
+ /*
+ * A key file must be defined.
+ */
+ if (keyfile_name == NULL) {
+ wbku_printerr("Must specify the key_file\n");
+ return (HMAC_ERROR);
+ }
+
+ /*
+ * If the user did not provide an input file for the data,
+ * then use stdin as the source.
+ */
+ if (infile_name == NULL) {
+ in_fd = STDIN_FILENO;
+ } else {
+ in_fd = open(infile_name, O_RDONLY);
+ if (in_fd < 0) {
+ wbku_printerr("Cannot open input_file");
+ return (HMAC_ERROR);
+ }
+ }
+
+ /*
+ * Open the key file for reading.
+ */
+ if ((key_fp = fopen(keyfile_name, "r")) == NULL) {
+ wbku_printerr("Cannot open %s", keyfile_name);
+ goto out;
+ }
+
+ /*
+ * Create a SHA1 key attribute structure. It's the only hash
+ * type we support.
+ */
+ wbkuret = wbku_str_to_keyattr(WBKU_KW_HMAC_SHA1, &ka, WBKU_HASH_KEY);
+ if (wbkuret != WBKU_SUCCESS) {
+ wbku_printerr("%s\n", wbku_retmsg(wbkuret));
+ goto out;
+ }
+
+ /*
+ * Find the client key, if it exists.
+ */
+ wbkuret = wbku_find_key(key_fp, NULL, &ka, hmac_key, B_FALSE);
+ if (wbkuret != WBKU_SUCCESS) {
+ wbku_printerr("%s\n", wbku_retmsg(wbkuret));
+ ret = (wbkuret == WBKU_NOKEY) ? HMAC_NOKEY : HMAC_ERROR;
+ } else {
+ ret = hash_gen(in_fd, &ka, hmac_key);
+ }
+out:
+ /*
+ * Cleanup.
+ */
+ if (in_fd != -1) {
+ (void) close(in_fd);
+ }
+ if (key_fp != NULL) {
+ (void) fclose(key_fp);
+ }
+
+ return (ret);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/wanboot/ickey/Makefile b/usr/src/cmd/cmd-inet/usr.lib/wanboot/ickey/Makefile
new file mode 100644
index 0000000000..b6cefe2737
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/wanboot/ickey/Makefile
@@ -0,0 +1,45 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2002-2003 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+include ../Makefile.com
+
+PROG = ickey
+LDLIBS += -linetutil -lwanbootutil
+
+COMMON_DIR = $(SRC)/common
+CPPFLAGS += -I$(COMMON_DIR)/net/wanboot -I$(CMNCRYPTDIR)
+
+all: $(PROG)
+
+install: all $(ROOTCMD)
+
+clean:
+
+lint: lint_PROG
+
+include ../../../../Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/usr.lib/wanboot/ickey/ickey.c b/usr/src/cmd/cmd-inet/usr.lib/wanboot/ickey/ickey.c
new file mode 100644
index 0000000000..4a8529f56d
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/wanboot/ickey/ickey.c
@@ -0,0 +1,287 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2002-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/wanboot_impl.h>
+#include <libinetutil.h>
+#include <wanbootutil.h>
+#include <libintl.h>
+#include <locale.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <assert.h>
+#include <sys/openpromio.h>
+
+#define TYPE 0
+static char *progopts[] = {
+ "type",
+ NULL
+};
+
+/*
+ * The key's handle is the name by which a user knows the key (i.e. the
+ * name specified on the command line. The keyname is the name this
+ * utility uses to store the keys and the name OBP and wanboot use to
+ * retrieve them.
+ */
+static struct keylist {
+ const char *handle;
+ const char *keyname;
+ const int keysize; /* size of hex string representation */
+} keylist[] = {
+ WBKU_KW_3DES, WANBOOT_DES3_KEY_NAME,
+ (DES3_KEY_SIZE * 2),
+ WBKU_KW_AES_128, WANBOOT_AES_128_KEY_NAME,
+ (AES_128_KEY_SIZE * 2),
+ WBKU_KW_HMAC_SHA1, WANBOOT_HMAC_SHA1_KEY_NAME,
+ (WANBOOT_HMAC_KEY_SIZE * 2)
+};
+
+static const struct keylist *knownkeytype(char *);
+static char *getkey(const struct keylist *);
+static void deletekey(const struct keylist *);
+static void installkey(const struct keylist *);
+static void usage(const char *);
+
+static boolean_t delete = B_FALSE;
+
+int
+main(int ac, char **av)
+{
+ int i;
+ const struct keylist *k;
+ char *typestring = NULL;
+ char *options;
+ char *value;
+
+ /*
+ * Do the necessary magic for localization support.
+ */
+ (void) setlocale(LC_ALL, "");
+#if !defined(TEXT_DOMAIN)
+#define TEXT_DOMAIN "SYS_TEST"
+#endif
+ (void) textdomain(TEXT_DOMAIN);
+
+ /*
+ * Initialize program name for use by wbku_printerr().
+ */
+ wbku_errinit(av[0]);
+
+ while ((i = getopt(ac, av, "do:")) != -1)
+ switch (i) {
+ case 'd':
+ delete = B_TRUE;
+ break;
+
+ case 'o':
+ options = optarg;
+ while (*options != '\0') {
+ switch (getsubopt(&options, progopts,
+ &value)) {
+ case TYPE:
+ typestring = value;
+ break;
+
+ default:
+ /* unknown token */
+ usage(*av);
+ /* NOTREACHED */
+ }
+ }
+ break;
+
+ case '?':
+ usage(*av);
+ /* NOTREACHED */
+ }
+
+ if ((optind >= ac) && (typestring != NULL) &&
+ ((k = knownkeytype(typestring)) != NULL)) {
+ if (delete == B_TRUE)
+ deletekey(k);
+ else
+ installkey(k);
+ return (0);
+ } else {
+ usage(*av);
+ /* NOTREACHED */
+ }
+}
+
+static const struct keylist *
+knownkeytype(char *type)
+{
+ int i;
+
+ for (i = 0; i < sizeof (keylist)/sizeof (keylist[0]); i++) {
+ if (strcmp(keylist[i].handle, type) == 0)
+ return (&keylist[i]);
+ }
+
+ return (NULL);
+}
+
+static void
+deletekey(const struct keylist *k)
+{
+ int fd;
+ struct wankeyio wkio;
+ struct openpromio *oio;
+
+ (void) strlcpy(wkio.wk_keyname, k->keyname, WANBOOT_MAXKEYNAMELEN);
+ wkio.wk_keysize = 0; /* zero key size indicates a deletion */
+
+ oio = malloc(sizeof (struct openpromio) + sizeof (struct wankeyio));
+ if (oio == NULL) {
+ wbku_printerr("openpromio malloc (%d) failed\n",
+ sizeof (struct openpromio) +
+ sizeof (struct wankeyio));
+ exit(1);
+ }
+ oio->oprom_size = sizeof (struct wankeyio);
+ bcopy(&wkio, oio->oprom_array, sizeof (struct wankeyio));
+ fd = open("/dev/openprom", O_RDWR);
+ if (fd == -1) {
+ wbku_printerr("open: /dev/openprom");
+ exit(1);
+ }
+
+ if (ioctl(fd, WANBOOT_SETKEY, oio) == -1) {
+ wbku_printerr("setkey: ioctl");
+ exit(1);
+ }
+
+ (void) close(fd);
+}
+
+static void
+installkey(const struct keylist *k)
+{
+ char *keyptr;
+ int fd;
+ struct wankeyio wkio;
+ struct openpromio *oio;
+ uint_t rawkeysize;
+ int err;
+
+ (void) strlcpy(wkio.wk_keyname, k->keyname, WANBOOT_MAXKEYNAMELEN);
+ assert((k->keysize % 2) == 0);
+ wkio.wk_keysize = k->keysize / 2;
+
+ if ((keyptr = getkey(k)) != NULL) {
+ rawkeysize = sizeof (wkio.wk_u);
+ if ((err = hexascii_to_octet(keyptr, strlen(keyptr),
+ wkio.wk_u.key, &rawkeysize)) != 0) {
+ wbku_printerr(
+ "internal error: hexascii_to_octet returned %d\n",
+ err);
+ exit(1);
+ } else if (rawkeysize != wkio.wk_keysize) {
+ wbku_printerr("internal error: key size mismatch\n");
+ exit(1);
+ }
+
+ oio = malloc(sizeof (struct openpromio) +
+ sizeof (struct wankeyio));
+ if (oio == NULL) {
+ wbku_printerr("openpromio malloc (%d) failed\n",
+ sizeof (struct openpromio) +
+ sizeof (struct wankeyio));
+ exit(1);
+ }
+ oio->oprom_size = sizeof (struct wankeyio);
+ bcopy(&wkio, oio->oprom_array, sizeof (struct wankeyio));
+ fd = open("/dev/openprom", O_RDWR);
+ if (fd == -1) {
+ wbku_printerr("open: /dev/openprom");
+ exit(1);
+ }
+
+ if (ioctl(fd, WANBOOT_SETKEY, oio) == -1) {
+ wbku_printerr("setkey: ioctl");
+ exit(1);
+ }
+
+ (void) close(fd);
+ } else {
+ wbku_printerr("getpassphrase"); /* getpassphrase() failed */
+ exit(1);
+ }
+}
+
+static char *
+getkey(const struct keylist *k)
+{
+ char prompt[BUFSIZ];
+ char *p;
+ char *q;
+ int len;
+
+ (void) snprintf(prompt, sizeof (prompt),
+ gettext("Enter %s key: "), k->handle);
+ p = getpassphrase(prompt);
+ if (p) {
+ /* skip over initial "0[xX]" */
+ if ((p[0] == '0') && (p[1] == 'x' || p[1] == 'X'))
+ p += 2;
+ len = strlen(p);
+ if (len != k->keysize) {
+ wbku_printerr(
+ "key length mismatch (expected %d, got %d)\n",
+ k->keysize, len);
+ exit(1);
+ }
+ for (q = p; q < p + len; q++)
+ if (!isxdigit(*q)) {
+ wbku_printerr(
+ "non-hexadecimal characters in key\n");
+ exit(1);
+ }
+ }
+
+ return (p);
+}
+
+static void
+usage(const char *progname)
+{
+ int i;
+
+ (void) fprintf(stderr, gettext(
+ "usage: %s [ -d ] -o type=keytype\nwhere keytype is one of "),
+ progname);
+ for (i = 0; i < sizeof (keylist)/sizeof (keylist[0]); i++)
+ (void) fprintf(stderr, "%s ", keylist[i].handle);
+ (void) fputc('\n', stderr);
+ exit(1);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/wanboot/keygen/Makefile b/usr/src/cmd/cmd-inet/usr.lib/wanboot/keygen/Makefile
new file mode 100644
index 0000000000..2baf2fe7ff
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/wanboot/keygen/Makefile
@@ -0,0 +1,43 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+include ../Makefile.com
+
+PROG= keygen
+LDLIBS += -lnsl -lgen -lwanbootutil
+CPPFLAGS += -I$(CMNCRYPTDIR)
+
+all: $(PROG)
+
+install: all $(ROOTCMD)
+
+clean:
+
+lint: lint_PROG
+
+include ../../../../Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/usr.lib/wanboot/keygen/keygen.c b/usr/src/cmd/cmd-inet/usr.lib/wanboot/keygen/keygen.c
new file mode 100644
index 0000000000..fc4a868b77
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/wanboot/keygen/keygen.c
@@ -0,0 +1,746 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <strings.h>
+#include <libintl.h>
+#include <locale.h>
+#include <limits.h>
+#include <libgen.h>
+#include <errno.h>
+#include <assert.h>
+#include <wanbootutil.h>
+#include <sys/sysmacros.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wanboot_impl.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+/* Return codes */
+#define KEYGEN_SUCCESS 0
+#define KEYGEN_ERROR 1
+
+/* Defaults */
+static char default_net[] = "0.0.0.0";
+static char default_cid[] = "00000000000000";
+
+/* Suboption. */
+#define NET 0
+#define CID 1
+#define TYPE 2
+
+static char *opts[] = { "net", "cid", "type", NULL };
+
+/*
+ * This routine is used to parse the suboptions of '-o' option.
+ *
+ * The option should be of the form:
+ * net=<addr>,cid=<cid>,type=<3des|aes|sha1|rsa>
+ *
+ * This routine will pass the values of each of the suboptions back in the
+ * supplied arguments, 'net', 'cid' and 'ka'.
+ *
+ * Returns:
+ * KEYGEN_SUCCESS or KEYGEN_ERROR.
+ */
+static int
+process_option(char *arg, char **net, char **cid, wbku_key_attr_t *ka)
+{
+ char *value;
+ wbku_retcode_t ret;
+
+ while (*arg != '\0') {
+ switch (getsubopt(&arg, opts, &value)) {
+ case NET:
+ /*
+ * Network number.
+ */
+ *net = value;
+ break;
+ case CID:
+ /*
+ * Client ID.
+ */
+ *cid = value;
+ break;
+ case TYPE:
+ /*
+ * Key type.
+ */
+ ret = wbku_str_to_keyattr(value, ka, WBKU_ANY_KEY);
+ if (ret != WBKU_SUCCESS) {
+ wbku_printerr("%s\n", wbku_retmsg(ret));
+ return (KEYGEN_ERROR);
+ }
+ break;
+ default:
+ wbku_printerr("%s is not a valid option\n", value);
+ return (KEYGEN_ERROR);
+ }
+ }
+
+ /*
+ * Sanity checks
+ */
+ if (*net != NULL && **net == '\0') {
+ wbku_printerr("Missing net option value\n");
+ return (KEYGEN_ERROR);
+ }
+ if (*cid != NULL && **cid == '\0') {
+ wbku_printerr("Missing cid option value\n");
+ return (KEYGEN_ERROR);
+ }
+ if (*cid != NULL && *net == NULL) {
+ wbku_printerr(
+ "The cid option requires net option specification\n");
+ return (KEYGEN_ERROR);
+ }
+ if (ka->ka_type == WBKU_KEY_UNKNOWN) {
+ wbku_printerr("Missing key type option value\n");
+ return (KEYGEN_ERROR);
+ }
+
+ return (KEYGEN_SUCCESS);
+}
+
+/*
+ * This routine parses a buffer to determine whether or not it
+ * contains a hexascii string. If the buffer contains any characters
+ * that are not hexascii, then it is not a hexascii string. Since
+ * this function is used to validate a CID value (which is then used
+ * to identify a directory in the filesystem), no evaluation of the
+ * string is performed. That is, hex strings are not padded (e.g. "A"
+ * is not padded to "0A").
+ *
+ * Returns:
+ * B_TRUE or B_FALSE
+ */
+static boolean_t
+isxstring(const char *buf)
+{
+ if ((strlen(buf) % 2) != 0) {
+ return (B_FALSE);
+ }
+
+ for (; *buf != '\0'; ++buf) {
+ if (!isxdigit(*buf)) {
+ return (B_FALSE);
+ }
+ }
+ return (B_TRUE);
+}
+
+/*
+ * This routine uses the 'net' and the 'cid' to generate the client's
+ * keystore filename and, if requested, creates the directory path to
+ * the file if any of the directories do not exist. If directory path
+ * creation is not requested and any of the directories do not exist,
+ * then an error is returned.
+ *
+ * Returns:
+ * KEYGEN_SUCCESS or KEYGEN_ERROR.
+ */
+static int
+create_client_filename(char *filename, size_t len, const char *net,
+ const char *cid, boolean_t create)
+{
+ struct in_addr addr;
+ size_t size;
+
+ if (net == NULL) {
+ size = snprintf(filename, len, "%s", CLIENT_KEY_DIR);
+ } else if (inet_pton(AF_INET, net, &addr) != 1) {
+ wbku_printerr("%s is not a valid network address\n", net);
+ return (KEYGEN_ERROR);
+ } else if (cid == NULL) {
+ size = snprintf(filename, len, "%s/%s", CLIENT_KEY_DIR, net);
+ } else if (!isxstring(cid)) {
+ wbku_printerr(
+ "%s must be an even number of hexadecimal characters\n",
+ cid);
+ return (KEYGEN_ERROR);
+ } else {
+ size = snprintf(filename, len, "%s/%s/%s", CLIENT_KEY_DIR,
+ net, cid);
+ }
+
+ /*
+ * Shouldn't be a problem, but make sure buffer was big enough.
+ */
+ if (size >= len) {
+ wbku_printerr("Keystore path too long\n");
+ return (KEYGEN_ERROR);
+ }
+
+ /*
+ * If directory creation is allowed, then try to create it.
+ * If the directory already exists, then march on.
+ */
+ if (create) {
+ if (mkdirp(filename, S_IRWXU) == -1 && errno != EEXIST) {
+ wbku_printerr("Cannot create client keystore");
+ return (KEYGEN_ERROR);
+ }
+ }
+
+ /*
+ * Append the filename.
+ */
+ if (strlcat(filename, "/keystore", len) >= len) {
+ wbku_printerr("Keystore path too long\n");
+ return (KEYGEN_ERROR);
+ }
+
+ return (KEYGEN_SUCCESS);
+}
+
+/*
+ * This routine generates a random key of the type defined by 'ka'.
+ * The key value is returned in 'rand_key' and the buffer pointed to
+ * by 'rand_key' is assumed to be of the correct size.
+ *
+ * Note:
+ * If 'ka' has a non-NULL keycheck value, then the routine will
+ * generate randon keys until a non-weak key is generated.
+ *
+ * Returns:
+ * KEYGEN_SUCCESS or KEYGEN_ERROR.
+ */
+static int
+gen_key(const wbku_key_attr_t *ka, uint8_t *rand_key)
+{
+ /*
+ * Generate key, until non-weak key generated.
+ */
+ for (;;) {
+ if (wbio_nread_rand(rand_key, ka->ka_len) != 0) {
+ wbku_printerr("Cannot generate random number");
+ return (KEYGEN_ERROR);
+ }
+
+ if (ka->ka_keycheck == NULL || ka->ka_keycheck(rand_key)) {
+ return (KEYGEN_SUCCESS);
+ }
+ }
+}
+
+/*
+ * This routine generates a random master key of the type (currently only
+ * HMAC SHA1 supported) defined by 'ka' and stores it in the master key
+ * file.
+ *
+ * Returns:
+ * KEYGEN_SUCCESS or KEYGEN_ERROR.
+ */
+static int
+master_gen_key(wbku_key_attr_t *ka)
+{
+ uint8_t mas_key[WANBOOT_HMAC_KEY_SIZE];
+ int fd;
+ FILE *fp = NULL;
+ fpos_t pos;
+ wbku_retcode_t ret;
+ boolean_t exists = B_FALSE;
+
+ /*
+ * If the file already exists (possibly via keymgmt), then open
+ * the file for update. Otherwise create it and open it for
+ * for writing.
+ */
+ fd = open(MASTER_KEY_FILE, O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR);
+ if (fd < 0) {
+ if (errno == EEXIST) {
+ fp = fopen(MASTER_KEY_FILE, "r+");
+ exists = B_TRUE;
+ }
+ } else {
+ if ((fp = fdopen(fd, "w")) == NULL) {
+ (void) close(fd);
+ }
+ }
+
+ if (fp == NULL) {
+ wbku_printerr("Cannot open master keystore", MASTER_KEY_FILE);
+ return (KEYGEN_ERROR);
+ }
+
+ /*
+ * If the file already exists, then see if a master key already
+ * exists. We will not overwrite it if it does.
+ */
+ ret = WBKU_NOKEY;
+ if (exists) {
+ ret = wbku_find_key(fp, NULL, ka, NULL, B_TRUE);
+ if (ret != WBKU_NOKEY) {
+ if (ret == WBKU_SUCCESS) {
+ wbku_printerr("The master %s key already "
+ "exists and will not be overwritten\n",
+ ka->ka_str);
+ } else {
+ wbku_printerr("%s\n", wbku_retmsg(ret));
+ }
+ (void) fclose(fp);
+ return (KEYGEN_ERROR);
+ }
+ }
+
+ /*
+ * If wbku_find_key() did not find the key position for us
+ * (expected behavior), then we should set position to
+ * the end of the file.
+ */
+ if (ret == WBKU_NOKEY &&
+ (fseek(fp, 0, SEEK_END) != 0 || fgetpos(fp, &pos) != 0)) {
+ wbku_printerr("Internal error");
+ (void) fclose(fp);
+ return (KEYGEN_ERROR);
+ }
+
+ /*
+ * Generate a key and write it.
+ */
+ if (gen_key(ka, mas_key) != KEYGEN_SUCCESS) {
+ (void) fclose(fp);
+ return (KEYGEN_ERROR);
+ }
+
+ ret = wbku_write_key(fp, &pos, ka, mas_key, B_TRUE);
+ (void) fclose(fp);
+ if (ret != WBKU_SUCCESS) {
+ wbku_printerr("%s\n", wbku_retmsg(ret));
+ return (KEYGEN_ERROR);
+ }
+
+ (void) printf(gettext("The master %s key has been generated\n"),
+ ka->ka_str);
+ return (KEYGEN_SUCCESS);
+}
+
+/*
+ * This routine generates a random client key of the type
+ * defined by 'ka' and stores it in the client keystore.
+ * file.
+ *
+ * Returns:
+ * KEYGEN_SUCCESS or KEYGEN_ERROR.
+ */
+static int
+client_gen_key(const char *filename, wbku_key_attr_t *ka, const char *net,
+ const char *cid)
+{
+ int fd;
+ FILE *cli_fp = NULL;
+ FILE *mas_fp;
+ fpos_t pos;
+ uint8_t cli_key[WANBOOT_MAXKEYLEN];
+ uint8_t mas_key[WANBOOT_HMAC_KEY_SIZE];
+ SHA1_CTX ctx;
+ char cid_buf[PATH_MAX];
+ boolean_t exists = B_FALSE;
+ wbku_retcode_t ret;
+
+ /*
+ * If the file already exists (possibly via keymgmt), then open
+ * the file for update. Otherwise create it and open it for
+ * for writing.
+ */
+ fd = open(filename, O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR);
+ if (fd < 0) {
+ if (errno == EEXIST) {
+ cli_fp = fopen(filename, "r+");
+ exists = B_TRUE;
+ }
+ } else {
+ if ((cli_fp = fdopen(fd, "w")) == NULL) {
+ (void) close(fd);
+ }
+ }
+
+ if (cli_fp == NULL) {
+ wbku_printerr("Cannot open client keystore");
+ return (KEYGEN_ERROR);
+ }
+
+ /*
+ * Generate the key. Encryption keys can be generated by simply
+ * calling gen_key(). An HMAC SHA1 key will be generated by
+ * hashing the master key.
+ */
+ switch (ka->ka_type) {
+ case WBKU_KEY_3DES:
+ case WBKU_KEY_AES_128:
+ if (gen_key(ka, cli_key) != KEYGEN_SUCCESS) {
+ (void) fclose(cli_fp);
+ return (KEYGEN_ERROR);
+ }
+ break;
+ case WBKU_KEY_HMAC_SHA1:
+ /*
+ * Follow RFC 3118 Appendix A's algorithm to generate
+ * the HMAC/SHA1 client key.
+ */
+
+ /*
+ * Open the master keystore for reading only.
+ */
+ if ((mas_fp = fopen(MASTER_KEY_FILE, "r")) == NULL) {
+ wbku_printerr("Cannot open master keystore");
+ (void) fclose(cli_fp);
+ return (KEYGEN_ERROR);
+ }
+
+ /*
+ * Find the master key.
+ */
+ ret = wbku_find_key(mas_fp, NULL, ka, mas_key, B_TRUE);
+ if (ret != WBKU_SUCCESS) {
+ if (ret == WBKU_NOKEY) {
+ wbku_printerr("Cannot create a client key "
+ "without first creating a master key\n");
+ } else {
+ wbku_printerr("%s\n", wbku_retmsg(ret));
+ }
+ (void) fclose(mas_fp);
+ (void) fclose(cli_fp);
+ return (KEYGEN_ERROR);
+ }
+ (void) fclose(mas_fp);
+
+ /*
+ * Now generate the client's unique ID buffer.
+ */
+ if (strlcpy(cid_buf, net, PATH_MAX) >= PATH_MAX ||
+ strlcat(cid_buf, cid, PATH_MAX) >= PATH_MAX) {
+ wbku_printerr("Unique id for client is too big\n");
+ (void) fclose(cli_fp);
+ return (KEYGEN_ERROR);
+ }
+
+ /*
+ * Hash the buffer to create the client key.
+ */
+ HMACInit(&ctx, mas_key, WANBOOT_HMAC_KEY_SIZE);
+ HMACUpdate(&ctx, (uint8_t *)cid_buf, strlen(cid_buf));
+ HMACFinal(&ctx, mas_key, WANBOOT_HMAC_KEY_SIZE, cli_key);
+
+ break;
+ case WBKU_KEY_RSA:
+ wbku_printerr("Cannot generate RSA key using keygen\n");
+ (void) fclose(cli_fp);
+ return (KEYGEN_ERROR);
+ default:
+ wbku_printerr("Internal error\n");
+ (void) fclose(cli_fp);
+ return (KEYGEN_ERROR);
+ }
+
+ /*
+ * Look to see if a client key of this type exists and if
+ * it does note its position in the file.
+ */
+ ret = WBKU_NOKEY;
+ if (exists) {
+ ret = wbku_find_key(cli_fp, &pos, ka, NULL, B_FALSE);
+ if (ret != WBKU_SUCCESS && ret != WBKU_NOKEY) {
+ wbku_printerr("%s\n", wbku_retmsg(ret));
+ (void) fclose(cli_fp);
+ return (KEYGEN_ERROR);
+ }
+ }
+
+ /*
+ * If wbku_find_key() did not find the key position for us,
+ * then we should set position to the end of the file.
+ */
+ if (ret == WBKU_NOKEY &&
+ (fseek(cli_fp, 0, SEEK_END) != 0 || fgetpos(cli_fp, &pos) != 0)) {
+ wbku_printerr("Internal error");
+ (void) fclose(cli_fp);
+ return (KEYGEN_ERROR);
+ }
+
+ /*
+ * Write the key.
+ */
+ ret = wbku_write_key(cli_fp, &pos, ka, cli_key, B_FALSE);
+ if (ret != WBKU_SUCCESS) {
+ wbku_printerr("%s\n", wbku_retmsg(ret));
+ (void) fclose(cli_fp);
+ return (KEYGEN_ERROR);
+ }
+ (void) fclose(cli_fp);
+
+ (void) printf(gettext("A new client %s key has been generated\n"),
+ ka->ka_str);
+
+ return (KEYGEN_SUCCESS);
+}
+
+/*
+ * This routine is used to print a hexascii version of a key.
+ * The hexascii version of the key will be twice the length
+ * of 'datalen'.
+ */
+static void
+keydump(const char *key, int keylen)
+{
+ uint16_t *p16;
+
+ assert(IS_P2ALIGNED(key, sizeof (uint16_t)));
+/*LINTED aligned*/
+ for (p16 = (uint16_t *)key; keylen > 0; keylen -= 2) {
+ (void) printf("%04x", htons(*p16++));
+ }
+ (void) printf("\n");
+}
+
+/*
+ * This routine is used to print a key of the type
+ * described by 'ka'. If 'master' is true, then the
+ * key to display is the master key. Otherwise, it's a
+ * client key.
+ *
+ * Returns:
+ * KEYGEN_SUCCESS or KEYGEN_ERROR.
+ */
+static int
+display_key(const char *filename, wbku_key_attr_t *ka, boolean_t master)
+{
+ uint8_t key[WANBOOT_MAXKEYLEN];
+ FILE *fp;
+ wbku_retcode_t ret;
+
+ /*
+ * Open the keystore for reading only.
+ */
+ if ((fp = fopen(filename, "r")) == NULL) {
+ wbku_printerr("Cannot open keystore");
+ return (KEYGEN_ERROR);
+ }
+
+ /*
+ * Find the key.
+ */
+ ret = wbku_find_key(fp, NULL, ka, key, master);
+ if (ret != WBKU_SUCCESS) {
+ if (ret == WBKU_NOKEY) {
+ wbku_printerr("The %s %s key does not exist\n",
+ (master ? "master" : "client"), ka->ka_str);
+ } else {
+ wbku_printerr("%s\n", wbku_retmsg(ret));
+ }
+ (void) fclose(fp);
+ return (KEYGEN_ERROR);
+ }
+ (void) fclose(fp);
+
+ /*
+ * Dump the key in hex.
+ */
+ keydump((char *)key, ka->ka_len);
+
+ return (KEYGEN_SUCCESS);
+}
+
+/*
+ * Prints usage().
+ */
+static void
+usage(const char *cmd)
+{
+ (void) fprintf(stderr, gettext("Usage: %s [-m | -c "
+ "-o net=<addr>,cid=<cid>,type=<%s|%s|%s>]\n"
+ " %s -d [-m | -c -o net=<addr>,cid=<cid>,"
+ "type=<%s|%s|%s|%s>]\n"),
+ cmd, WBKU_KW_3DES, WBKU_KW_AES_128, WBKU_KW_HMAC_SHA1,
+ cmd, WBKU_KW_3DES, WBKU_KW_AES_128, WBKU_KW_HMAC_SHA1, WBKU_KW_RSA);
+}
+
+/*
+ * This program is used to generate and display WAN boot encryption and
+ * hash keys. The paths to the keystores are predetermined. That is, the
+ * master keystore (used to store a master HMAC SHA1 key) will always
+ * reside in the default location, MASTER_KEY_FILE. The client keystores
+ * will always reside in default locations that are computed using their
+ * network number and cid values.
+ *
+ * Note:
+ * The master keystore can store client keys too. This program
+ * cannot be used to insert client keys into the master keystore.
+ * However, it must not corrupt any client keystore inserted into
+ * the file by other means (keymgmt).
+ *
+ * We do not do any file locking scheme. This means that if two
+ * keygen commands are run concurrently, results can be disastrous.
+ *
+ * Returns:
+ * KEYGEN_SUCCESS or KEYGEN_ERROR.
+ */
+int
+main(int argc, char **argv)
+{
+ char filename[PATH_MAX];
+ char *filenamep;
+ int c;
+ boolean_t is_client = B_FALSE;
+ boolean_t is_master = B_FALSE;
+ boolean_t display = B_FALSE;
+ char *net = NULL;
+ char *cid = NULL;
+ wbku_key_attr_t ka;
+ wbku_retcode_t ret;
+
+ /*
+ * Do the necessary magic for localization support.
+ */
+ (void) setlocale(LC_ALL, "");
+#if !defined(TEXT_DOMAIN)
+#define TEXT_DOMAIN "SYS_TEST"
+#endif
+ (void) textdomain(TEXT_DOMAIN);
+
+ /*
+ * Initialize program name for use by wbku_printerr().
+ */
+ wbku_errinit(argv[0]);
+
+ /*
+ * At the very least, we'll need one arg.
+ */
+ if (argc < 2) {
+ usage(argv[0]);
+ return (KEYGEN_ERROR);
+ }
+
+ /*
+ * Parse the options.
+ */
+ ka.ka_type = WBKU_KEY_UNKNOWN;
+ while ((c = getopt(argc, argv, "dcmo:")) != EOF) {
+ switch (c) {
+ case 'd':
+ /*
+ * Display a key.
+ */
+ display = B_TRUE;
+ break;
+ case 'o':
+ /*
+ * Suboptions.
+ */
+ if (process_option(optarg, &net, &cid, &ka) != 0) {
+ usage(argv[0]);
+ return (KEYGEN_ERROR);
+ }
+ break;
+ case 'c':
+ is_client = B_TRUE;
+ break;
+ case 'm':
+ is_master = B_TRUE;
+ break;
+ default:
+ usage(argv[0]);
+ return (KEYGEN_ERROR);
+ }
+ }
+
+ /*
+ * Must be operating on a master or client key and if
+ * it's a client key, then type must have been given.
+ */
+ if ((is_client == is_master) ||
+ (is_client && ka.ka_type == WBKU_KEY_UNKNOWN)) {
+ usage(argv[0]);
+ return (KEYGEN_ERROR);
+ }
+
+ /*
+ * If operating on the master key, then it is an HMAC SHA1
+ * key. Build the correct 'ka'. If we're working on a client
+ * key, the 'ka' was already built as part of option parsing.
+ */
+ if (is_master) {
+ ret = wbku_str_to_keyattr(WBKU_KW_HMAC_SHA1, &ka,
+ WBKU_HASH_KEY);
+ if (ret != WBKU_SUCCESS) {
+ wbku_printerr("Internal error\n");
+ return (KEYGEN_ERROR);
+ }
+ filenamep = MASTER_KEY_FILE;
+ } else {
+ /*
+ * Build the path to the client keystore.
+ */
+ if (create_client_filename(filename, sizeof (filename), net,
+ cid, !display) != KEYGEN_SUCCESS) {
+ return (KEYGEN_ERROR);
+ }
+ filenamep = filename;
+ }
+
+ /*
+ * If display chosen, go do it.
+ */
+ if (display) {
+ return (display_key(filenamep, &ka, is_master));
+ }
+
+ /*
+ * Can't generate RSA key here.
+ */
+ if (ka.ka_type == WBKU_KEY_RSA) {
+ wbku_printerr("keygen cannot create RSA key\n");
+ return (KEYGEN_ERROR);
+ }
+
+ /*
+ * If generating a master key, go do it.
+ */
+ if (is_master) {
+ return (master_gen_key(&ka));
+ }
+
+ /*
+ * Must be generating a client key, go do it.
+ */
+ if (net == NULL) {
+ net = default_net;
+ }
+ if (cid == NULL) {
+ cid = default_cid;
+ }
+ if (client_gen_key(filename, &ka, net, cid) != KEYGEN_SUCCESS) {
+ return (KEYGEN_ERROR);
+ }
+
+ return (KEYGEN_SUCCESS);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/wanboot/keymgmt/Makefile b/usr/src/cmd/cmd-inet/usr.lib/wanboot/keymgmt/Makefile
new file mode 100644
index 0000000000..560f08233c
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/wanboot/keymgmt/Makefile
@@ -0,0 +1,43 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+include ../Makefile.com
+
+PROG= keymgmt
+LDLIBS += -lwanbootutil
+CPPFLAGS += -I$(CMNCRYPTDIR)
+
+all: $(PROG)
+
+install: all $(ROOTCMD)
+
+clean:
+
+lint: lint_PROG
+
+include ../../../../Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/usr.lib/wanboot/keymgmt/keymgmt.c b/usr/src/cmd/cmd-inet/usr.lib/wanboot/keymgmt/keymgmt.c
new file mode 100644
index 0000000000..10e8e0dd6c
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/wanboot/keymgmt/keymgmt.c
@@ -0,0 +1,520 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <alloca.h>
+#include <unistd.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <libintl.h>
+#include <locale.h>
+#include <limits.h>
+#include <libgen.h>
+#include <errno.h>
+#include <ctype.h>
+#include <wanbootutil.h>
+#include <sys/sysmacros.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wanboot_impl.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+/* Return codes */
+#define KEYMGMT_SUCCESS 0
+#define KEYMGMT_ERROR 1
+
+/* Suboption. */
+#define TYPE 0
+
+static char *opts[] = { "type", NULL };
+
+/*
+ * This routine is used to parse the suboptions of '-o' option.
+ *
+ * The option should be of the form: type=<3des|aes|sha1|rsa>
+ *
+ * This routine will pass the value of the suboption back in the
+ * supplied arguments, 'ka'.
+ *
+ * Returns:
+ * KEYMGMT_SUCCESS or KEYMGMT_ERROR.
+ */
+static int
+process_option(char *arg, wbku_key_attr_t *ka)
+{
+ char *value;
+ wbku_retcode_t ret;
+
+ while (*arg != '\0') {
+ switch (getsubopt(&arg, opts, &value)) {
+ case TYPE:
+ /*
+ * Key type.
+ */
+ ret = wbku_str_to_keyattr(value, ka, WBKU_ANY_KEY);
+ if (ret != WBKU_SUCCESS) {
+ wbku_printerr("%s\n", wbku_retmsg(ret));
+ return (KEYMGMT_ERROR);
+ }
+ break;
+ default:
+ wbku_printerr("%s is not a valid option\n", value);
+ return (KEYMGMT_ERROR);
+ }
+ }
+
+ /*
+ * Success.
+ */
+ return (KEYMGMT_SUCCESS);
+}
+
+/*
+ * This routine extracts a key of type 'ka' from the keystore named
+ * 'keystore_name' and writes it the the file identified by 'name'.
+ *
+ * Returns:
+ * KEYMGMT_SUCCESS or KEYMGMT_ERROR.
+ */
+static int
+process_extract(const char *keystore_name, const char *name,
+ wbku_key_attr_t *ka)
+{
+ size_t i;
+ uint8_t ex_key[WANBOOT_MAXKEYLEN];
+ FILE *keystore_fp;
+ FILE *fp;
+ wbku_retcode_t ret;
+
+ /*
+ * Open the keystore for reading.
+ */
+ if ((keystore_fp = fopen(keystore_name, "r")) == NULL) {
+ wbku_printerr("Cannot open %s", keystore_name);
+ return (KEYMGMT_ERROR);
+ }
+
+ /*
+ * Find the client key.
+ */
+ ret = wbku_find_key(keystore_fp, NULL, ka, ex_key, B_FALSE);
+ if (ret != WBKU_SUCCESS) {
+ if (ret == WBKU_NOKEY) {
+ wbku_printerr("The client %s key does not exist\n",
+ ka->ka_str);
+ } else {
+ wbku_printerr("%s\n", wbku_retmsg(ret));
+ }
+ (void) fclose(keystore_fp);
+ return (KEYMGMT_ERROR);
+ }
+ (void) fclose(keystore_fp);
+
+ /*
+ * Open the output file.
+ */
+ if ((fp = fopen(name, "w")) == NULL) {
+ wbku_printerr("Cannot open %s", name);
+ (void) fclose(keystore_fp);
+ return (KEYMGMT_ERROR);
+ }
+
+ /*
+ * Dump the key to the output file.
+ */
+ i = fwrite(ex_key, sizeof (uint8_t), ka->ka_len, fp);
+ if (i != ka->ka_len) {
+ wbku_printerr("Error writing to %s", name);
+ (void) fclose(fp);
+ return (KEYMGMT_ERROR);
+ }
+ (void) fclose(fp);
+
+ /*
+ * Success.
+ */
+ return (KEYMGMT_SUCCESS);
+}
+
+/*
+ * There is a key which needs to be removed from the keystore. Given basic
+ * information about the key to be deleted, go through the keystore and
+ * remove it. The steps are:
+ * 1) create a temp file in the same directory as the keystore.
+ * 2) copy the existing keystore to the temp file, omitting the key being
+ * removed.
+ * 3) shuffle files. Close the keystore and move it aside. Close the
+ * temp file and move in to the keystore.
+ *
+ * Returns:
+ * B_TRUE on success
+ * B_FALSE on error
+ */
+static boolean_t
+compress_keystore(const char *keystore_name, FILE *fp,
+ const wbku_key_attr_t *ka)
+{
+ char *tmp_path;
+ FILE *tmp_fp;
+ int tmp_fd;
+ int len;
+ wbku_retcode_t ret;
+
+ /*
+ * Allocate storage for the temporary path from the stack.
+ */
+ len = strlen(keystore_name) + sizeof (".XXXXXX");
+ tmp_path = alloca(len);
+ (void) snprintf(tmp_path, len, "%s.XXXXXX", keystore_name);
+
+ /*
+ * Make the temp working file where a new store will be created.
+ */
+ if ((tmp_fd = mkstemp(tmp_path)) == -1) {
+ wbku_printerr("Error creating %s\n", tmp_path);
+ return (B_FALSE);
+ }
+
+ /*
+ * Need to reference this file as a stream.
+ */
+ if ((tmp_fp = fdopen(tmp_fd, "w")) == NULL) {
+ wbku_printerr("Error opening %s", tmp_path);
+ (void) close(tmp_fd);
+ (void) unlink(tmp_path);
+ return (B_FALSE);
+ }
+
+ /*
+ * Copy the existing keystore to the temp one, omitting the
+ * key being deleted.
+ */
+ ret = wbku_delete_key(fp, tmp_fp, ka);
+ (void) fclose(tmp_fp);
+ if (ret != WBKU_SUCCESS) {
+ wbku_printerr("%s\n", wbku_retmsg(ret));
+ (void) unlink(tmp_path);
+ return (B_FALSE);
+ }
+
+ /*
+ * Shuffle files.
+ */
+ if (rename(tmp_path, keystore_name) == -1) {
+ wbku_printerr("Error moving new keystore file from %s to %s",
+ tmp_path, keystore_name);
+ (void) unlink(tmp_path);
+ return (B_FALSE);
+ }
+
+ return (B_TRUE);
+}
+
+/*
+ * This routine reads a key of type 'ka' from the file identified 'name' and
+ * inserts it into the keystore named 'keystore_name'.
+ *
+ * Returns:
+ * KEYMGMT_SUCCESS or KEYMGMT_ERROR.
+ */
+static int
+process_insert(const char *keystore_name, const char *name,
+ wbku_key_attr_t *ka)
+{
+ int fd;
+ FILE *keystore_fp = NULL;
+ FILE *fp;
+ fpos_t pos;
+ uint8_t rd_key[WANBOOT_MAXKEYLEN];
+ int inlen;
+ boolean_t newfile = B_TRUE;
+ wbku_retcode_t ret;
+
+ /*
+ * If the file already exists, then open the file for update.
+ * Otherwise, create it and open it for writing.
+ */
+ fd = open(keystore_name, O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR);
+ if (fd < 0) {
+ if (errno == EEXIST) {
+ keystore_fp = fopen(keystore_name, "r+");
+ newfile = B_FALSE;
+ }
+ } else {
+ if ((keystore_fp = fdopen(fd, "w")) == NULL) {
+ (void) close(fd);
+ }
+ }
+
+ if (keystore_fp == NULL) {
+ wbku_printerr("Cannot open %s", keystore_name);
+ return (KEYMGMT_ERROR);
+ }
+
+ /*
+ * Open the input file.
+ */
+ fp = fopen(name, "r");
+ if (fp == NULL) {
+ wbku_printerr("Cannot open %s", name);
+ (void) fclose(keystore_fp);
+ return (KEYMGMT_ERROR);
+ }
+
+ /*
+ * Read the key from the file.
+ */
+ inlen = fread(rd_key, sizeof (uint8_t), ka->ka_maxlen, fp);
+ if (inlen == 0 && ferror(fp) != 0) {
+ wbku_printerr("Error reading %s", name);
+ (void) fclose(fp);
+ (void) fclose(keystore_fp);
+ return (KEYMGMT_ERROR);
+ }
+ (void) fclose(fp);
+
+ if ((inlen < ka->ka_minlen) || (inlen > ka->ka_maxlen)) {
+ wbku_printerr("Key length is not valid\n");
+ (void) fclose(keystore_fp);
+ return (KEYMGMT_ERROR);
+ }
+
+ /*
+ * If the keystore exists, search for a key of the type
+ * being inserted. If found, note its file position.
+ */
+ ret = WBKU_NOKEY;
+ if (!newfile) {
+ ret = wbku_find_key(keystore_fp, &pos, ka, NULL, B_FALSE);
+ if (ret != WBKU_SUCCESS && ret != WBKU_NOKEY) {
+ wbku_printerr("%s\n", wbku_retmsg(ret));
+ (void) fclose(keystore_fp);
+ return (KEYMGMT_ERROR);
+ }
+
+ /*
+ * Unfortuantely, RSA keys have variable lengths. If
+ * the one being inserted is a different length than
+ * than the one that already exists in the file, then
+ * the key must be removed from the keystore and then
+ * readded.
+ */
+ if (ret == WBKU_SUCCESS && inlen != ka->ka_len) {
+ if (!compress_keystore(keystore_name,
+ keystore_fp, ka)) {
+ wbku_printerr("Insertion required compression"
+ " of keystore, but compression failed\n"
+ "Key was not inserted\n");
+ (void) fclose(keystore_fp);
+ return (KEYMGMT_ERROR);
+ }
+
+ /*
+ * The original keystore is history. Close the
+ * stream and open a stream to the new keystore.
+ */
+ (void) fclose(keystore_fp);
+ keystore_fp = fopen(keystore_name, "r+");
+ if (keystore_fp == NULL) {
+ wbku_printerr("Cannot open %s", keystore_name);
+ return (KEYMGMT_ERROR);
+ }
+
+ /* Force new key to end of file */
+ ret = WBKU_NOKEY;
+ }
+ }
+ ka->ka_len = inlen;
+
+ /*
+ * If wbku_find_key() did not find the key position for us,
+ * then we should set position to the end of the file.
+ */
+ if (ret == WBKU_NOKEY && (fseek(keystore_fp, 0, SEEK_END) != 0 ||
+ fgetpos(keystore_fp, &pos) != 0)) {
+ wbku_printerr("Internal error");
+ (void) fclose(keystore_fp);
+ return (KEYMGMT_ERROR);
+ }
+
+ /*
+ * Write the key to the keystore.
+ */
+ ret = wbku_write_key(keystore_fp, &pos, ka, rd_key, B_FALSE);
+ (void) fclose(keystore_fp);
+ if (ret != WBKU_SUCCESS) {
+ wbku_printerr("%s\n", wbku_retmsg(ret));
+ return (KEYMGMT_ERROR);
+ }
+
+ (void) printf(gettext("The client's %s key has been set\n"),
+ ka->ka_str);
+ /*
+ * Success.
+ */
+ return (KEYMGMT_SUCCESS);
+}
+
+/*
+ * Prints usage().
+ */
+static void
+usage(const char *cmd)
+{
+ (void) fprintf(stderr, gettext("Usage: %s"
+ " -i -k <key_file> -s <keystore> -o type=<%s|%s|%s|%s>\n"
+ " %s -x -f <out_file> -s <keystore> -o"
+ " type=<%s|%s|%s|%s>\n"),
+ cmd, WBKU_KW_3DES, WBKU_KW_AES_128, WBKU_KW_HMAC_SHA1, WBKU_KW_RSA,
+ cmd, WBKU_KW_3DES, WBKU_KW_AES_128, WBKU_KW_HMAC_SHA1, WBKU_KW_RSA);
+}
+
+/*
+ * This program is used to insert and extract WAN boot encryption and
+ * hash keys into and from keystores. The paths to the keystores are
+ * provided by the user as are the input and output files.
+ *
+ * Note:
+ * This program assumes all keys being inserted or extracted
+ * are client keys. There is no way for a user to insert or
+ * extract a master key using this program.
+ *
+ * We do not do any file locking scheme. This means that if two
+ * keymgmt commands are run concurrently, results can be disastrous.
+ *
+ * Returns:
+ * KEYMGMT_SUCCESS or KEYMGMT_ERROR.
+ */
+int
+main(int argc, char **argv)
+{
+ int c;
+ boolean_t is_insert = B_FALSE;
+ boolean_t is_extract = B_FALSE;
+ char *keystore_name = NULL;
+ char *filename = NULL;
+ wbku_key_attr_t ka;
+ int ret;
+
+ /*
+ * Do the necessary magic for localization support.
+ */
+ (void) setlocale(LC_ALL, "");
+#if !defined(TEXT_DOMAIN)
+#define TEXT_DOMAIN "SYS_TEST"
+#endif
+ (void) textdomain(TEXT_DOMAIN);
+
+ /*
+ * Initialize program name for use by wbku_printerr().
+ */
+ wbku_errinit(argv[0]);
+
+ /*
+ * At the very least, we'll need one arg.
+ */
+ if (argc < 2) {
+ usage(argv[0]);
+ return (KEYMGMT_ERROR);
+ }
+
+ /*
+ * Parse the options.
+ */
+ ka.ka_type = WBKU_KEY_UNKNOWN;
+ while ((c = getopt(argc, argv, "ixf:k:s:o:")) != EOF) {
+ switch (c) {
+ case 'i':
+ is_insert = B_TRUE;
+ break;
+ case 'x':
+ is_extract = B_TRUE;
+ break;
+ case 'o':
+ /*
+ * Suboptions.
+ */
+ if (process_option(optarg, &ka) != KEYMGMT_SUCCESS) {
+ usage(argv[0]);
+ return (KEYMGMT_ERROR);
+ }
+ break;
+ case 's':
+ /*
+ * Keystore path.
+ */
+ keystore_name = optarg;
+ break;
+ case 'f':
+ /*
+ * Input file.
+ */
+ if (is_insert || filename != NULL) {
+ usage(argv[0]);
+ return (KEYMGMT_ERROR);
+ }
+ filename = optarg;
+ break;
+ case 'k':
+ /*
+ * Input file.
+ */
+ if (is_extract || filename != NULL) {
+ usage(argv[0]);
+ return (KEYMGMT_ERROR);
+ }
+ filename = optarg;
+ break;
+ default:
+ usage(argv[0]);
+ return (KEYMGMT_ERROR);
+ }
+ }
+
+ /*
+ * Must be inserting or extracting a key and we must have a
+ * key type, keystore filename and an input or output filename.
+ */
+ if ((is_insert == is_extract) || keystore_name == NULL ||
+ filename == NULL || ka.ka_type == WBKU_KEY_UNKNOWN) {
+ usage(argv[0]);
+ return (KEYMGMT_ERROR);
+ }
+
+ /*
+ * Insert or extract the key.
+ */
+ if (is_insert) {
+ ret = process_insert(keystore_name, filename, &ka);
+ } else {
+ ret = process_extract(keystore_name, filename, &ka);
+ }
+
+ return (ret);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/wanboot/netbootinfo/Makefile b/usr/src/cmd/cmd-inet/usr.lib/wanboot/netbootinfo/Makefile
new file mode 100644
index 0000000000..bf63ad605f
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/wanboot/netbootinfo/Makefile
@@ -0,0 +1,47 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+include ../Makefile.com
+include $(SRC)/lib/openssl/Makefile.openssl
+
+PROG = netbootinfo
+
+# The OpenSSL libraries need to be linked against in order to resolve
+# references made to them by libwanboot.
+LDLIBS += -lwanbootutil -lwanboot $(OPENSSL_LDFLAGS)
+CPPFLAGS += -I$(CMNCRYPTDIR)
+
+all: $(PROG)
+
+install: all $(ROOTCMD)
+
+clean:
+
+lint: lint_PROG
+
+include ../../../../Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/usr.lib/wanboot/netbootinfo/netbootinfo.c b/usr/src/cmd/cmd-inet/usr.lib/wanboot/netbootinfo/netbootinfo.c
new file mode 100644
index 0000000000..9df8510f46
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/wanboot/netbootinfo/netbootinfo.c
@@ -0,0 +1,123 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * This program extracts network interface parameters from the information
+ * passed to the kernel from the bootstrap (i.e. as properties of /chosen).
+ *
+ * Returns:
+ * = 0 - success
+ * > 0 - error (see exit codes below)
+ */
+
+#include <libintl.h>
+#include <locale.h>
+#include <stdio.h>
+#include <strings.h>
+#include <parseURL.h>
+#include <wanbootutil.h>
+#include <bootinfo.h>
+
+/*
+ * Exit codes:
+ */
+#define NETBOOTINFO_SUCCESS 0
+#define NETBOOTINFO_UNKNOWN_PARAM 1
+#define NETBOOTINFO_BOOTINFO_ERR 2
+#define NETBOOTINFO_USAGE 3
+
+int
+main(int argc, char **argv)
+{
+ int i;
+
+ /*
+ * Do the necessary magic for localization support.
+ */
+ (void) setlocale(LC_ALL, "");
+#if !defined(TEXT_DOMAIN)
+#define TEXT_DOMAIN "SYS_TEST"
+#endif /* !defined(TEXT_DOMAIN) */
+ (void) textdomain(TEXT_DOMAIN);
+
+ /*
+ * Initialize program name for use by wbku_printerr().
+ */
+ wbku_errinit(argv[0]);
+
+ /*
+ * Check usage is legal.
+ */
+ if (argc < 2) {
+ (void) fprintf(stderr,
+ gettext("Usage: %s param [ param ... ]\n"), argv[0]);
+ return (NETBOOTINFO_USAGE);
+ }
+
+ /*
+ * Initialize bootinfo.
+ */
+ if (!bootinfo_init()) {
+ wbku_printerr("Internal error\n");
+ return (NETBOOTINFO_BOOTINFO_ERR);
+ }
+
+ /*
+ * Retrieve and print parameter value(s).
+ */
+ for (i = 1; i < argc; ++i) {
+ char *name = argv[i];
+ char valbuf[URL_MAX_STRLEN];
+ size_t vallen = sizeof (valbuf);
+
+ /*
+ * Call get_bootinfo() to fetch it's value.
+ */
+ switch (bootinfo_get(name, valbuf, &vallen, NULL)) {
+ case BI_E_SUCCESS:
+ break;
+
+ case BI_E_NOVAL:
+ (void) strlcpy(valbuf, "none", sizeof (valbuf));
+ break;
+
+ case BI_E_ILLNAME:
+ wbku_printerr("Unknown parameter %s\n", name);
+ bootinfo_end();
+ return (NETBOOTINFO_UNKNOWN_PARAM);
+
+ default:
+ wbku_printerr("Internal error\n");
+ bootinfo_end();
+ return (NETBOOTINFO_BOOTINFO_ERR);
+ }
+ (void) printf("%s\n", valbuf);
+ }
+ bootinfo_end();
+
+ return (NETBOOTINFO_SUCCESS);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/wanboot/p12split/Makefile b/usr/src/cmd/cmd-inet/usr.lib/wanboot/p12split/Makefile
new file mode 100644
index 0000000000..cc3689e88d
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/wanboot/p12split/Makefile
@@ -0,0 +1,45 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+include ../Makefile.com
+include $(SRC)/lib/openssl/Makefile.openssl
+
+PROG= p12split
+LDLIBS += -lwanboot -linetutil -lwanbootutil $(OPENSSL_LDFLAGS) -lcrypto
+LDFLAGS += $(OPENSSL_DYNFLAGS)
+CPPFLAGS = $(OPENSSL_CPPFLAGS) -I$(CMNCRYPTDIR) $(CPPFLAGS.master)
+
+all: $(PROG)
+
+install: all $(ROOTCMD)
+
+clean:
+
+lint: lint_PROG
+
+include ../../../../Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/usr.lib/wanboot/p12split/p12split.c b/usr/src/cmd/cmd-inet/usr.lib/wanboot/p12split/p12split.c
new file mode 100644
index 0000000000..600f7ffd33
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/wanboot/p12split/p12split.c
@@ -0,0 +1,657 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2002-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <libintl.h>
+#include <locale.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wanboot_impl.h>
+#include <unistd.h>
+#include <string.h>
+#include <libinetutil.h>
+#include <wanbootutil.h>
+
+#include <openssl/crypto.h>
+#include <openssl/buffer.h>
+#include <openssl/bio.h>
+#include <openssl/err.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+#include <openssl/pkcs12.h>
+#include <openssl/evp.h>
+#include <p12aux.h>
+
+static boolean_t verbose = B_FALSE; /* When nonzero, do in verbose mode */
+
+/* The following match/cert values require PKCS12 */
+static int matchty; /* Type of matching do to on input */
+static char *k_matchval; /* localkeyid value to match */
+static uint_t k_len; /* length of k_matchval */
+
+#define IO_KEYFILE 1 /* Have a separate key file or data */
+#define IO_CERTFILE 2 /* Have a separate cert file or data */
+#define IO_TRUSTFILE 4 /* Have a separate trustanchor file */
+
+static char *input = NULL; /* Consolidated input file */
+static char *key_out = NULL; /* Key file to be output */
+static char *cert_out = NULL; /* Cert file to be output */
+static char *trust_out = NULL; /* Trust anchor file to be output */
+static uint_t outfiles; /* What files are there for output */
+static char *progname;
+
+/* Returns from time_check */
+typedef enum {
+ CHK_TIME_OK = 0, /* Cert in effect and not expired */
+ CHK_TIME_BEFORE_BAD, /* not_before field is invalid */
+ CHK_TIME_AFTER_BAD, /* not_after field is invalid */
+ CHK_TIME_IS_BEFORE, /* Cert not yet in force */
+ CHK_TIME_HAS_EXPIRED /* Cert has expired */
+} time_errs_t;
+
+static int parse_keyid(const char *);
+static int do_certs(void);
+static int read_files(STACK_OF(X509) **, X509 **, EVP_PKEY **);
+static void check_certs(STACK_OF(X509) *, X509 **);
+static time_errs_t time_check_print(X509 *);
+static time_errs_t time_check(X509 *);
+static int write_files(STACK_OF(X509) *, X509 *, EVP_PKEY *);
+static int get_ifile(char *, char *, EVP_PKEY **, X509 **, STACK_OF(X509) **);
+static int do_ofile(char *, EVP_PKEY *, X509 *, STACK_OF(X509) *);
+static void usage(void);
+static const char *cryptoerr(void);
+
+int
+main(int argc, char **argv)
+{
+ int i;
+
+ /*
+ * Do the necessary magic for localization support.
+ */
+ (void) setlocale(LC_ALL, "");
+#if !defined(TEXT_DOMAIN)
+#define TEXT_DOMAIN "SYS_TEST"
+#endif
+ (void) textdomain(TEXT_DOMAIN);
+
+ progname = strrchr(argv[0], '/');
+ if (progname != NULL)
+ progname++;
+ else
+ progname = argv[0];
+
+ wbku_errinit(progname);
+
+ matchty = DO_FIRST_PAIR;
+ while ((i = getopt(argc, argv, "vc:i:k:l:t:")) != -1) {
+ switch (i) {
+ case 'v':
+ verbose = B_TRUE;
+ break;
+
+ case 'l':
+ if (parse_keyid(optarg) < 0)
+ return (EXIT_FAILURE);
+ matchty = DO_FIND_KEYID;
+ break;
+
+ case 'c':
+ cert_out = optarg;
+ outfiles |= IO_CERTFILE;
+ break;
+
+ case 'k':
+ key_out = optarg;
+ outfiles |= IO_KEYFILE;
+ break;
+
+ case 't':
+ trust_out = optarg;
+ outfiles |= IO_TRUSTFILE;
+ break;
+
+ case 'i':
+ input = optarg;
+ break;
+
+ default:
+ usage();
+ }
+ }
+
+ if (input == NULL) {
+ wbku_printerr("no input file specified\n");
+ usage();
+ }
+
+ /*
+ * Need output files.
+ */
+ if (outfiles == 0) {
+ wbku_printerr("at least one output file must be specified\n");
+ usage();
+ }
+
+ if (do_certs() < 0)
+ return (EXIT_FAILURE);
+
+ return (EXIT_SUCCESS);
+}
+
+static int
+parse_keyid(const char *keystr)
+{
+ const char *rp;
+ char *wp;
+ char *nkeystr;
+ uint_t nkeystrlen;
+
+ /*
+ * In the worst case, we'll need one additional character in our
+ * output string -- e.g. "A\0" -> "0A\0"
+ */
+ nkeystrlen = strlen(keystr) + 2;
+ k_len = (nkeystrlen + 1) / 2;
+ nkeystr = malloc(nkeystrlen);
+ k_matchval = malloc(k_len);
+ if (nkeystr == NULL || k_matchval == NULL) {
+ free(nkeystr);
+ free(k_matchval);
+ wbku_printerr("cannot allocate keyid");
+ return (-1);
+ }
+
+ /*
+ * For convenience, we allow the user to put spaces between each digit
+ * when entering it on the command line. As a result, we need to
+ * process it into a format that hexascii_to_octet() can handle. Note
+ * that we're careful to map strings like "AA B CC D" to "AA0BCC0D".
+ */
+ for (rp = keystr, wp = nkeystr; *rp != '\0'; rp++) {
+ if (*rp == ' ')
+ continue;
+
+ if (rp[1] == ' ' || rp[1] == '\0') {
+ *wp++ = '0'; /* one character sequence; prepend 0 */
+ *wp++ = *rp;
+ } else {
+ *wp++ = *rp++;
+ *wp++ = *rp;
+ }
+ }
+ *wp = '\0';
+
+ if (hexascii_to_octet(nkeystr, wp - nkeystr, k_matchval, &k_len) != 0) {
+ free(nkeystr);
+ free(k_matchval);
+ wbku_printerr("invalid keyid `%s'\n", keystr);
+ return (-1);
+ }
+
+ free(nkeystr);
+ return (0);
+}
+
+static int
+do_certs(void)
+{
+ char *bufp;
+ STACK_OF(X509) *ta_in = NULL;
+ EVP_PKEY *pkey_in = NULL;
+ X509 *xcert_in = NULL;
+
+ sunw_crypto_init();
+
+ if (read_files(&ta_in, &xcert_in, &pkey_in) < 0)
+ return (-1);
+
+ if (verbose) {
+ if (xcert_in != NULL) {
+ (void) printf(gettext("\nMain cert:\n"));
+
+ /*
+ * sunw_subject_attrs() returns a pointer to
+ * memory allocated on our behalf. The same
+ * behavior is exhibited by sunw_issuer_attrs().
+ */
+ bufp = sunw_subject_attrs(xcert_in, NULL, 0);
+ if (bufp != NULL) {
+ (void) printf(gettext(" Subject: %s\n"),
+ bufp);
+ OPENSSL_free(bufp);
+ }
+
+ bufp = sunw_issuer_attrs(xcert_in, NULL, 0);
+ if (bufp != NULL) {
+ (void) printf(gettext(" Issuer: %s\n"), bufp);
+ OPENSSL_free(bufp);
+ }
+
+ (void) sunw_print_times(stdout, PRNT_BOTH, NULL,
+ xcert_in);
+ }
+
+ if (ta_in != NULL) {
+ X509 *x;
+ int i;
+
+ for (i = 0; i < sk_X509_num(ta_in); i++) {
+ /* LINTED */
+ x = sk_X509_value(ta_in, i);
+ (void) printf(
+ gettext("\nTrust Anchor cert %d:\n"), i);
+
+ /*
+ * sunw_subject_attrs() returns a pointer to
+ * memory allocated on our behalf. We get the
+ * same behavior from sunw_issuer_attrs().
+ */
+ bufp = sunw_subject_attrs(x, NULL, 0);
+ if (bufp != NULL) {
+ (void) printf(
+ gettext(" Subject: %s\n"), bufp);
+ OPENSSL_free(bufp);
+ }
+
+ bufp = sunw_issuer_attrs(x, NULL, 0);
+ if (bufp != NULL) {
+ (void) printf(
+ gettext(" Issuer: %s\n"), bufp);
+ OPENSSL_free(bufp);
+ }
+
+ (void) sunw_print_times(stdout, PRNT_BOTH,
+ NULL, x);
+ }
+ }
+ }
+
+ check_certs(ta_in, &xcert_in);
+ if (xcert_in != NULL && pkey_in != NULL) {
+ if (sunw_check_keys(xcert_in, pkey_in) == 0) {
+ wbku_printerr("warning: key and certificate do "
+ "not match\n");
+ }
+ }
+
+ return (write_files(ta_in, xcert_in, pkey_in));
+}
+
+static int
+read_files(STACK_OF(X509) **t_in, X509 **c_in, EVP_PKEY **k_in)
+{
+ char *i_pass;
+
+ i_pass = getpassphrase(gettext("Enter key password: "));
+
+ if (get_ifile(input, i_pass, k_in, c_in, t_in) < 0)
+ return (-1);
+
+ /*
+ * If we are only interested in getting a trust anchor, and if there
+ * is no trust anchor but is a regular cert, use it instead. Do this
+ * to handle the insanity with openssl, which requires a matching cert
+ * and key in order to write a PKCS12 file.
+ */
+ if (outfiles == IO_TRUSTFILE) {
+ if (c_in != NULL && *c_in != NULL && t_in != NULL) {
+ if (*t_in == NULL) {
+ if ((*t_in = sk_X509_new_null()) == NULL) {
+ wbku_printerr("out of memory\n");
+ return (-1);
+ }
+ }
+
+ if (sk_X509_num(*t_in) == 0) {
+ if (sk_X509_push(*t_in, *c_in) == 0) {
+ wbku_printerr("out of memory\n");
+ return (-1);
+ }
+ *c_in = NULL;
+ }
+ }
+ }
+
+ if ((outfiles & IO_KEYFILE) && *k_in == NULL) {
+ wbku_printerr("no matching key found\n");
+ return (-1);
+ }
+ if ((outfiles & IO_CERTFILE) && *c_in == NULL) {
+ wbku_printerr("no matching certificate found\n");
+ return (-1);
+ }
+ if ((outfiles & IO_TRUSTFILE) && *t_in == NULL) {
+ wbku_printerr("no matching trust anchor found\n");
+ return (-1);
+ }
+
+ return (0);
+}
+
+static void
+check_certs(STACK_OF(X509) *ta_in, X509 **c_in)
+{
+ X509 *curr;
+ time_errs_t ret;
+ int i;
+ int del_expired = (outfiles != 0);
+
+ if (c_in != NULL && *c_in != NULL) {
+ ret = time_check_print(*c_in);
+ if ((ret != CHK_TIME_OK && ret != CHK_TIME_IS_BEFORE) &&
+ del_expired) {
+ (void) fprintf(stderr, gettext(" Removing cert\n"));
+ X509_free(*c_in);
+ *c_in = NULL;
+ }
+ }
+
+ if (ta_in == NULL)
+ return;
+
+ for (i = 0; i < sk_X509_num(ta_in); ) {
+ /* LINTED */
+ curr = sk_X509_value(ta_in, i);
+ ret = time_check_print(curr);
+ if ((ret != CHK_TIME_OK && ret != CHK_TIME_IS_BEFORE) &&
+ del_expired) {
+ (void) fprintf(stderr, gettext(" Removing cert\n"));
+ /* LINTED */
+ curr = sk_X509_delete(ta_in, i);
+ X509_free(curr);
+ continue;
+ }
+ i++;
+ }
+}
+
+static time_errs_t
+time_check_print(X509 *cert)
+{
+ char buf[256];
+ int ret;
+
+ ret = time_check(cert);
+ if (ret == CHK_TIME_OK)
+ return (CHK_TIME_OK);
+
+ (void) fprintf(stderr, gettext(" Subject: %s"),
+ sunw_subject_attrs(cert, buf, sizeof (buf)));
+ (void) fprintf(stderr, gettext(" Issuer: %s"),
+ sunw_issuer_attrs(cert, buf, sizeof (buf)));
+
+ switch (ret) {
+ case CHK_TIME_BEFORE_BAD:
+ (void) fprintf(stderr,
+ gettext("\n Invalid cert 'not before' field\n"));
+ break;
+
+ case CHK_TIME_AFTER_BAD:
+ (void) fprintf(stderr,
+ gettext("\n Invalid cert 'not after' field\n"));
+ break;
+
+ case CHK_TIME_HAS_EXPIRED:
+ (void) sunw_print_times(stderr, PRNT_NOT_AFTER,
+ gettext("\n Cert has expired\n"), cert);
+ break;
+
+ case CHK_TIME_IS_BEFORE:
+ (void) sunw_print_times(stderr, PRNT_NOT_BEFORE,
+ gettext("\n Warning: cert not yet valid\n"), cert);
+ break;
+
+ default:
+ break;
+ }
+
+ return (ret);
+}
+
+static time_errs_t
+time_check(X509 *cert)
+{
+ int i;
+
+ i = X509_cmp_time(X509_get_notBefore(cert), NULL);
+ if (i == 0)
+ return (CHK_TIME_BEFORE_BAD);
+ if (i > 0)
+ return (CHK_TIME_IS_BEFORE);
+ /* After 'not before' time */
+
+ i = X509_cmp_time(X509_get_notAfter(cert), NULL);
+ if (i == 0)
+ return (CHK_TIME_AFTER_BAD);
+ if (i < 0)
+ return (CHK_TIME_HAS_EXPIRED);
+ return (CHK_TIME_OK);
+}
+
+static int
+write_files(STACK_OF(X509) *t_out, X509 *c_out, EVP_PKEY *k_out)
+{
+ if (key_out != NULL) {
+ if (verbose)
+ (void) printf(gettext("%s: writing key\n"), progname);
+ if (do_ofile(key_out, k_out, NULL, NULL) < 0)
+ return (-1);
+ }
+
+ if (cert_out != NULL) {
+ if (verbose)
+ (void) printf(gettext("%s: writing cert\n"), progname);
+ if (do_ofile(cert_out, NULL, c_out, NULL) < 0)
+ return (-1);
+ }
+
+ if (trust_out != NULL) {
+ if (verbose)
+ (void) printf(gettext("%s: writing trust\n"),
+ progname);
+ if (do_ofile(trust_out, NULL, NULL, t_out) < 0)
+ return (-1);
+ }
+
+ return (0);
+}
+
+static int
+get_ifile(char *name, char *pass, EVP_PKEY **tmp_k, X509 **tmp_c,
+ STACK_OF(X509) **tmp_t)
+{
+ PKCS12 *p12;
+ FILE *fp;
+ int ret;
+ struct stat sbuf;
+
+ if (stat(name, &sbuf) == 0 && !S_ISREG(sbuf.st_mode)) {
+ wbku_printerr("%s is not a regular file\n", name);
+ return (-1);
+ }
+
+ if ((fp = fopen(name, "r")) == NULL) {
+ wbku_printerr("cannot open input file %s", name);
+ return (-1);
+ }
+
+ p12 = d2i_PKCS12_fp(fp, NULL);
+ if (p12 == NULL) {
+ wbku_printerr("cannot read file %s: %s\n", name, cryptoerr());
+ (void) fclose(fp);
+ return (-1);
+ }
+ (void) fclose(fp);
+
+ ret = sunw_PKCS12_parse(p12, pass, matchty, k_matchval, k_len,
+ NULL, tmp_k, tmp_c, tmp_t);
+ if (ret <= 0) {
+ if (ret == 0)
+ wbku_printerr("cannot find matching cert and key\n");
+ else
+ wbku_printerr("cannot parse %s: %s\n", name,
+ cryptoerr());
+ PKCS12_free(p12);
+ return (-1);
+ }
+ return (0);
+}
+
+static int
+do_ofile(char *name, EVP_PKEY *pkey, X509 *cert, STACK_OF(X509) *ta)
+{
+ STACK_OF(EVP_PKEY) *klist = NULL;
+ STACK_OF(X509) *clist = NULL;
+ PKCS12 *p12 = NULL;
+ int ret = 0;
+ FILE *fp;
+ struct stat sbuf;
+
+ if (stat(name, &sbuf) == 0 && !S_ISREG(sbuf.st_mode)) {
+ wbku_printerr("%s is not a regular file\n", name);
+ return (-1);
+ }
+
+ if ((fp = fopen(name, "w")) == NULL) {
+ wbku_printerr("cannot open output file %s", name);
+ return (-1);
+ }
+
+ if ((clist = sk_X509_new_null()) == NULL ||
+ (klist = sk_EVP_PKEY_new_null()) == NULL) {
+ wbku_printerr("out of memory\n");
+ ret = -1;
+ goto cleanup;
+ }
+
+ if (cert != NULL && sk_X509_push(clist, cert) == 0) {
+ wbku_printerr("out of memory\n");
+ ret = -1;
+ goto cleanup;
+ }
+
+ if (pkey != NULL && sk_EVP_PKEY_push(klist, pkey) == 0) {
+ wbku_printerr("out of memory\n");
+ ret = -1;
+ goto cleanup;
+ }
+
+ p12 = sunw_PKCS12_create(WANBOOT_PASSPHRASE, klist, clist, ta);
+ if (p12 == NULL) {
+ wbku_printerr("cannot create %s: %s\n", name, cryptoerr());
+ ret = -1;
+ goto cleanup;
+ }
+
+ if (i2d_PKCS12_fp(fp, p12) == 0) {
+ wbku_printerr("cannot write %s: %s\n", name, cryptoerr());
+ ret = -1;
+ goto cleanup;
+ }
+
+cleanup:
+ (void) fclose(fp);
+ if (p12 != NULL)
+ PKCS12_free(p12);
+ /*
+ * Put the cert and pkey off of the stack so that they won't
+ * be freed two times. (If they get left in the stack then
+ * they will be freed with the stack.)
+ */
+ if (clist != NULL) {
+ if (cert != NULL && sk_X509_num(clist) == 1) {
+ /* LINTED */
+ (void) sk_X509_delete(clist, 0);
+ }
+ sk_X509_pop_free(clist, X509_free);
+ }
+ if (klist != NULL) {
+ if (pkey != NULL && sk_EVP_PKEY_num(klist) == 1) {
+ /* LINTED */
+ (void) sk_EVP_PKEY_delete(klist, 0);
+ }
+ sk_EVP_PKEY_pop_free(klist, sunw_evp_pkey_free);
+ }
+
+ return (ret);
+}
+
+static void
+usage(void)
+{
+ (void) fprintf(stderr,
+ gettext("usage:\n"
+ " %s -i <file> -c <file> -k <file> -t <file> [-l <keyid> -v]\n"
+ "\n"),
+ progname);
+ (void) fprintf(stderr,
+ gettext(" where:\n"
+ " -i - input file to be split into component parts and put in\n"
+ " files given by -c, -k and -t\n"
+ " -c - output file for the client certificate\n"
+ " -k - output file for the client private key\n"
+ " -t - output file for the remaining certificates (assumed\n"
+ " to be trust anchors)\n"
+ "\n Files are assumed to be pkcs12-format files.\n\n"
+ " -v - verbose\n"
+ " -l - value of 'localkeyid' attribute in client cert and\n"
+ " private key to be selected from the input file.\n\n"));
+ exit(EXIT_FAILURE);
+}
+
+/*
+ * Return a pointer to a static buffer that contains a listing of crypto
+ * errors. We presume that the user doesn't want more than 8KB of error
+ * messages :-)
+ */
+static const char *
+cryptoerr(void)
+{
+ static char errbuf[8192];
+ ulong_t err;
+ const char *pfile;
+ int line;
+ unsigned int nerr = 0;
+
+ errbuf[0] = '\0';
+ while ((err = ERR_get_error_line(&pfile, &line)) != 0) {
+ if (++nerr > 1)
+ (void) strlcat(errbuf, "\n\t", sizeof (errbuf));
+
+ if (err == (ulong_t)-1) {
+ (void) strlcat(errbuf, strerror(errno),
+ sizeof (errbuf));
+ break;
+ }
+ (void) strlcat(errbuf, ERR_reason_error_string(err),
+ sizeof (errbuf));
+ }
+
+ return (errbuf);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/wanboot/wanboot-cgi/Makefile b/usr/src/cmd/cmd-inet/usr.lib/wanboot/wanboot-cgi/Makefile
new file mode 100644
index 0000000000..0a5c292ea3
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/wanboot/wanboot-cgi/Makefile
@@ -0,0 +1,46 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+include ../Makefile.com
+include $(SRC)/lib/openssl/Makefile.openssl
+
+PROG = wanboot-cgi
+LDLIBS += -lgen -lnsl -lwanbootutil -lnvpair -lwanboot \
+ $(OPENSSL_LDFLAGS) -lcrypto
+LDFLAGS += $(OPENSSL_DYNFLAGS)
+CPPFLAGS = $(OPENSSL_CPPFLAGS) -I$(CMNCRYPTDIR) $(CPPFLAGS.master)
+
+all: $(PROG)
+
+install: all $(ROOTCMD)
+
+clean:
+
+lint: lint_PROG
+
+include ../../../../Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/usr.lib/wanboot/wanboot-cgi/wanboot-cgi.c b/usr/src/cmd/cmd-inet/usr.lib/wanboot/wanboot-cgi/wanboot-cgi.c
new file mode 100644
index 0000000000..d18366398c
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/wanboot/wanboot-cgi/wanboot-cgi.c
@@ -0,0 +1,1903 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <string.h>
+#include <libgen.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <netdb.h>
+#include <libnvpair.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/sysmacros.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/wanboot_impl.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <openssl/crypto.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+#include <openssl/pem.h>
+#include <openssl/pkcs12.h>
+#include <openssl/evp.h>
+#include <openssl/err.h>
+
+#include <p12aux.h>
+
+#include <parseURL.h>
+/*
+ * These can be replaced with wanbootutil.h once the openssl interfaces
+ * are moved to libwanboot.
+ */
+#include <wanboot/key_util.h>
+#include <wanboot/key_xdr.h>
+#include <hmac_sha1.h>
+
+#include <netboot_paths.h>
+#include <wanboot_conf.h>
+
+/*
+ * Exit status:
+ */
+#define WBCGI_STATUS_OK 0
+#define WBCGI_STATUS_ERR 1
+
+#define WBCGI_FILE_EXISTS(file, statbuf) \
+ (stat(file, &statbuf) == 0 && S_ISREG(statbuf.st_mode))
+
+#define WBCGI_DIR_EXISTS(dir, statbuf) \
+ (stat(dir, &statbuf) == 0 && S_ISDIR(statbuf.st_mode))
+
+#define WBCGI_HMAC_PATH "/usr/lib/inet/wanboot/hmac"
+#define WBCGI_ENCR_PATH "/usr/lib/inet/wanboot/encr"
+#define WBCGI_KEYMGMT_PATH "/usr/lib/inet/wanboot/keymgmt"
+#define WBCGI_MKISOFS_PATH "/bin/mkisofs"
+
+#define WBCGI_DEV_URANDOM "/dev/urandom"
+
+#define WBCGI_CONTENT_TYPE "Content-Type: "
+#define WBCGI_CONTENT_LENGTH "Content-Length: "
+#define WBCGI_WANBOOT_BNDTXT "WANBoot_Part_Boundary"
+#define WBCGI_CRNL "\r\n"
+
+#define WBCGI_CNSTR "CN="
+#define WBCGI_CNSTR_LEN (sizeof (WBCGI_CNSTR) - 1)
+#define WBCGI_NAMESEP ",/\n\r"
+
+#define WBCGI_MAXBUF 256
+
+/*
+ * Possible return values from netboot_ftw():
+ */
+#define WBCGI_FTW_CBOK 2 /* CB terminated walk OK */
+#define WBCGI_FTW_CBCONT 1 /* CB wants walk should continue */
+#define WBCGI_FTW_DONE 0 /* Walk terminated without CBERR/CBOK */
+#define WBCGI_FTW_CBERR -1 /* CB terminated walk with err */
+
+/*
+ * getsubopt() is used to map one of the contents[] keywords
+ * to one of these types
+ */
+#define WBCGI_CONTENT_ERROR -1
+#define WBCGI_CONTENT_BOOTFILE 0
+#define WBCGI_CONTENT_BOOTFS 1
+#define WBCGI_CONTENT_ROOTFS 2
+
+static char *contents[] =
+ { "bootfile", "bootfs", "rootfs", NULL };
+
+/*
+ * getsubopt() is used to parse the query string for
+ * the keywords defined by queryopts[]
+ */
+#define WBCGI_QUERYOPT_CONTENT 0
+#define WBCGI_QUERYOPT_NET 1
+#define WBCGI_QUERYOPT_CID 2
+#define WBCGI_QUERYOPT_NONCE 3
+
+static char *queryopts[] =
+ { "CONTENT", "IP", "CID", "NONCE", NULL };
+
+static bc_handle_t bc_handle;
+
+
+static char *
+status_msg(int status)
+{
+ char *msg;
+
+ switch (status) {
+ case 400:
+ msg = "Bad Request";
+ break;
+ case 403:
+ msg = "Forbidden";
+ break;
+ case 500:
+ msg = "Internal Server Error";
+ break;
+ default:
+ msg = "Unknown status";
+ break;
+ }
+
+ return (msg);
+}
+
+static void
+print_status(int status, const char *spec_msg)
+{
+ if (spec_msg == NULL) {
+ spec_msg = "";
+ }
+
+ (void) fprintf(stdout, "Status: %d %s %s%s", status,
+ status_msg(status), spec_msg, WBCGI_CRNL);
+}
+
+static char *
+make_path(const char *root, const char *suffix)
+{
+ char path[MAXPATHLEN];
+ char *ptr = NULL;
+ int chars;
+
+ if ((chars = snprintf(path, sizeof (path),
+ "%s/%s", root, suffix)) < 0 || chars > sizeof (path) ||
+ (ptr = strdup(path)) == NULL) {
+ print_status(500, "(error making path)");
+ }
+
+ return (ptr);
+}
+
+static void
+free_path(char **pathp)
+{
+ if (*pathp != NULL) {
+ free(*pathp);
+ *pathp = NULL;
+ }
+}
+
+static char *
+gen_tmppath(const char *prefix, const char *net, const char *cid)
+{
+ pid_t pid;
+ time_t secs;
+ int chars;
+ char path[MAXPATHLEN];
+ char *ptr = NULL;
+
+ if ((pid = getpid()) < 0 || (secs = time(NULL)) < 0 ||
+ (chars = snprintf(path, sizeof (path), "/tmp/%s_%s_%s_%ld_%ld",
+ prefix, net, cid, pid, secs)) < 0 || chars > sizeof (path) ||
+ (ptr = strdup(path)) == NULL) {
+ print_status(500, "(error creating temporary filename)");
+ }
+
+ return (ptr);
+}
+
+/*
+ * File I/O stuff:
+ */
+static boolean_t
+write_buffer(int fd, const void *buffer, size_t buflen)
+{
+ size_t nwritten;
+ ssize_t nbytes;
+ const char *buf = buffer;
+
+ for (nwritten = 0; nwritten < buflen; nwritten += nbytes) {
+ nbytes = write(fd, &buf[nwritten], buflen - nwritten);
+ if (nbytes <= 0) {
+ return (B_FALSE);
+ }
+ }
+
+ return (B_TRUE);
+}
+
+static boolean_t
+write_file(int ofd, const char *filename, size_t size)
+{
+ boolean_t ret = B_TRUE;
+ int ifd;
+ char buf[1024];
+ size_t rlen;
+ ssize_t wlen;
+
+ if ((ifd = open(filename, O_RDONLY)) < 0) {
+ return (B_FALSE);
+ }
+
+ for (; size != 0; size -= wlen) {
+ rlen = (size < sizeof (buf)) ? size : sizeof (buf);
+
+ if ((wlen = read(ifd, buf, rlen)) < 0 ||
+ !write_buffer(ofd, buf, wlen)) {
+ ret = B_FALSE;
+ break;
+ }
+ }
+ (void) close(ifd);
+
+ return (ret);
+}
+
+static boolean_t
+copy_file(const char *src, const char *dest)
+{
+ boolean_t ret = B_FALSE;
+ char message[WBCGI_MAXBUF];
+ const size_t chunksize = 16 * PAGESIZE;
+ size_t validsize;
+ size_t nwritten = 0;
+ size_t nbytes = 0;
+ off_t roff;
+ int mflags = MAP_PRIVATE;
+ char *buf = NULL;
+ struct stat st;
+ int rfd = -1;
+ int wfd = -1;
+ int chars;
+
+ if ((rfd = open(src, O_RDONLY)) < 0 ||
+ (wfd = open(dest, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR)) < 0 ||
+ fstat(rfd, &st) == -1) {
+ goto cleanup;
+ }
+
+ for (nbytes = st.st_size, roff = 0; nwritten < nbytes;
+ nwritten += validsize, roff += validsize) {
+ buf = mmap(buf, chunksize, PROT_READ, mflags, rfd, roff);
+ if (buf == MAP_FAILED) {
+ goto cleanup;
+ }
+ mflags |= MAP_FIXED;
+
+ validsize = MIN(chunksize, nbytes - nwritten);
+ if (!write_buffer(wfd, buf, validsize)) {
+ (void) munmap(buf, chunksize);
+ goto cleanup;
+ }
+
+ }
+ if (buf != NULL) {
+ (void) munmap(buf, chunksize);
+ }
+
+ ret = B_TRUE;
+cleanup:
+ if (ret == B_FALSE) {
+ if ((chars = snprintf(message, sizeof (message),
+ "error copying %s to %s", src, dest)) > 0 &&
+ chars <= sizeof (message)) {
+ print_status(500, message);
+ } else {
+ print_status(500, NULL);
+ }
+ }
+ if (rfd != -1) {
+ (void) close(rfd);
+ }
+ if (wfd != -1) {
+ (void) close(wfd);
+ }
+
+ return (ret);
+}
+
+static boolean_t
+create_nonce(const char *noncepath, const char *nonce)
+{
+ boolean_t ret = B_TRUE;
+ int fd;
+
+ if ((fd = open(noncepath,
+ O_WRONLY|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR)) == -1 ||
+ !write_buffer(fd, nonce, strlen(nonce))) {
+ print_status(500, "(error creating nonce file)");
+ ret = B_FALSE;
+ }
+ if (fd != -1) {
+ (void) close(fd);
+ }
+
+ return (ret);
+}
+
+static boolean_t
+create_timestamp(const char *timestamppath, const char *timestamp)
+{
+ boolean_t ret = B_TRUE;
+ int fd;
+
+ if ((fd = open(timestamppath,
+ O_WRONLY|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR)) == -1 ||
+ !write_buffer(fd, timestamp, strlen(timestamp))) {
+ print_status(500, "(error creating timestamp file)");
+ ret = B_FALSE;
+ }
+ if (fd != -1) {
+ (void) close(fd);
+ }
+
+ return (ret);
+}
+
+static boolean_t
+create_urandom(const char *urandompath)
+{
+ boolean_t ret = B_TRUE;
+ int fd;
+
+ if ((fd = open(urandompath,
+ O_WRONLY|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR)) == -1 ||
+ !write_file(fd, WBCGI_DEV_URANDOM, 32 * 1024)) {
+ print_status(500, "(error creating urandom file)");
+ ret = B_FALSE;
+ }
+ if (fd != -1) {
+ (void) close(fd);
+ }
+
+ return (ret);
+}
+
+static boolean_t
+create_null_hash(const char *hashpath)
+{
+ boolean_t ret = B_TRUE;
+ int fd;
+ static char null_hash[HMAC_DIGEST_LEN];
+
+ if ((fd = open(hashpath,
+ O_WRONLY|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR)) == -1 ||
+ !write_buffer(fd, null_hash, sizeof (null_hash))) {
+ print_status(500, "(error creating null hash)");
+ ret = B_FALSE;
+ }
+ if (fd != -1) {
+ (void) close(fd);
+ }
+
+ return (ret);
+}
+
+
+static char *
+determine_doc_root(void)
+{
+ char *doc_root;
+
+ /*
+ * If DOCUMENT_ROOT is valid, use that.
+ */
+ if ((doc_root = getenv("DOCUMENT_ROOT")) == NULL ||
+ strlen(doc_root) == 0) {
+ /*
+ * No DOCUMENT_ROOT - try PATH_TRANSLATED.
+ */
+ if ((doc_root = getenv("PATH_TRANSLATED")) == NULL ||
+ strlen(doc_root) == 0) {
+ /*
+ * Can't determine the document root.
+ */
+ return (NULL);
+ }
+ }
+
+ return (doc_root);
+}
+
+static boolean_t
+get_request_info(int *contentp, char **netp, char **cidp, char **noncep,
+ char **docrootp)
+{
+ char *method;
+ char *query_string;
+ char *value;
+ char *junk;
+ int i;
+
+ if ((method = getenv("REQUEST_METHOD")) == NULL ||
+ strncasecmp(method, "GET", strlen("GET") != 0)) {
+ print_status(403, "(GET method expected)");
+ return (B_FALSE);
+ }
+
+ if ((query_string = getenv("QUERY_STRING")) == NULL) {
+ print_status(400, "(empty query string)");
+ return (B_FALSE);
+ }
+
+ for (i = 0; i < strlen(query_string); i++) {
+ if (query_string[i] == '&') {
+ query_string[i] = ',';
+ }
+ }
+
+ *contentp = WBCGI_CONTENT_ERROR;
+ *netp = *cidp = *noncep = NULL;
+
+ if ((*docrootp = determine_doc_root()) == NULL) {
+ print_status(400, "(unable to determine document root)");
+ return (B_FALSE);
+ }
+
+ while (*query_string != '\0') {
+ switch (getsubopt(&query_string, queryopts, &value)) {
+ case WBCGI_QUERYOPT_CONTENT:
+ *contentp = getsubopt(&value, contents, &junk);
+ break;
+ case WBCGI_QUERYOPT_NET:
+ *netp = value;
+ break;
+ case WBCGI_QUERYOPT_CID:
+ *cidp = value;
+ break;
+ case WBCGI_QUERYOPT_NONCE:
+ *noncep = value;
+ break;
+ default:
+ print_status(400, "(illegal query string)");
+ return (B_FALSE);
+ break;
+ }
+ }
+
+ switch (*contentp) {
+ default:
+ print_status(400, "(missing or illegal CONTENT)");
+ return (B_FALSE);
+
+ case WBCGI_CONTENT_BOOTFS:
+ if (*netp == NULL || *cidp == NULL || *noncep == NULL) {
+ print_status(400,
+ "(CONTENT, IP, CID and NONCE required)");
+ return (B_FALSE);
+ }
+ break;
+
+ case WBCGI_CONTENT_BOOTFILE:
+ case WBCGI_CONTENT_ROOTFS:
+ if (*netp == NULL || *cidp == NULL || *docrootp == NULL) {
+ print_status(400,
+ "(CONTENT, IP, CID and DOCUMENT_ROOT required)");
+ return (B_FALSE);
+ }
+ break;
+ }
+
+ return (B_TRUE);
+}
+
+static boolean_t
+encrypt_payload(const char *payload, const char *encr_payload,
+ const char *keyfile, const char *encryption_type)
+{
+ struct stat sbuf;
+ int chars;
+ char cmd[MAXPATHLEN];
+ FILE *fp;
+ int status;
+ char msg[WBCGI_MAXBUF];
+
+ if (!WBCGI_FILE_EXISTS(payload, sbuf)) {
+ print_status(500, "(encrypt_payload: missing payload)");
+ return (B_FALSE);
+ }
+
+ if ((chars = snprintf(cmd, sizeof (cmd),
+ "%s -o type=%s -k %s < %s > %s", WBCGI_ENCR_PATH,
+ encryption_type, keyfile, payload, encr_payload)) < 0 ||
+ chars > sizeof (cmd)) {
+ print_status(500, "(encrypt_payload: buffer overflow)");
+ return (B_FALSE);
+ }
+
+ if ((fp = popen(cmd, "w")) == NULL) {
+ print_status(500, "(encrypt_payload: missing/file error)");
+ return (B_FALSE);
+ }
+ if ((status = WEXITSTATUS(pclose(fp))) != 0) {
+ (void) snprintf(msg, sizeof (msg),
+ "(encrypt_payload: failed, status=%d)", status);
+ print_status(500, msg);
+ return (B_FALSE);
+ }
+
+ if (!WBCGI_FILE_EXISTS(encr_payload, sbuf)) {
+ print_status(500, "(encrypt_payload: bad encrypted file)");
+ return (B_FALSE);
+ }
+
+ return (B_TRUE);
+}
+
+static boolean_t
+hash_payload(const char *payload, const char *payload_hash,
+ const char *keyfile)
+{
+ struct stat sbuf;
+ int chars;
+ char cmd[MAXPATHLEN];
+ FILE *fp;
+ int status;
+ char msg[WBCGI_MAXBUF];
+
+ if (!WBCGI_FILE_EXISTS(payload, sbuf)) {
+ print_status(500, "(hash_payload: missing payload)");
+ return (B_FALSE);
+ }
+
+ if ((chars = snprintf(cmd, sizeof (cmd), "%s -i %s -k %s > %s",
+ WBCGI_HMAC_PATH, payload, keyfile, payload_hash)) < 0 ||
+ chars > sizeof (cmd)) {
+ print_status(500, "(hash_payload: buffer overflow)");
+ return (B_FALSE);
+ }
+
+ if ((fp = popen(cmd, "w")) == NULL) {
+ print_status(500, "(hash_payload: missing/file error)");
+ return (B_FALSE);
+ }
+ if ((status = WEXITSTATUS(pclose(fp))) != 0) {
+ (void) snprintf(msg, sizeof (msg),
+ "(hash_payload: failed, status=%d)", status);
+ print_status(500, msg);
+ return (B_FALSE);
+ }
+
+ if (!WBCGI_FILE_EXISTS(payload_hash, sbuf) ||
+ sbuf.st_size < HMAC_DIGEST_LEN) {
+ print_status(500, "(hash_payload: bad signature file)");
+ return (B_FALSE);
+ }
+
+ return (B_TRUE);
+}
+
+static boolean_t
+extract_keystore(const char *path, const char *keystorepath)
+{
+ struct stat sbuf;
+ int chars;
+ char cmd[MAXPATHLEN];
+ FILE *fp;
+ int status;
+ char msg[WBCGI_MAXBUF];
+
+ if (!WBCGI_FILE_EXISTS(path, sbuf)) {
+ print_status(500, "(extract_keystore: missing keystore)");
+ return (B_FALSE);
+ }
+
+ if ((chars = snprintf(cmd, sizeof (cmd),
+ "%s -x -f %s -s %s -o type=rsa",
+ WBCGI_KEYMGMT_PATH, keystorepath, path)) < 0 ||
+ chars > sizeof (cmd)) {
+ print_status(500, "(extract_keystore: buffer overflow)");
+ return (B_FALSE);
+ }
+
+ if ((fp = popen(cmd, "w")) == NULL) {
+ print_status(500, "(extract_keystore: missing/file error)");
+ return (B_FALSE);
+ }
+ if ((status = WEXITSTATUS(pclose(fp))) != 0) {
+ (void) snprintf(msg, sizeof (msg),
+ "(extract_keystore: failed, status=%d)", status);
+ print_status(500, msg);
+ return (B_FALSE);
+ }
+
+ if (!WBCGI_FILE_EXISTS(keystorepath, sbuf)) {
+ print_status(500, "(extract_keystore: failed to create)");
+ return (B_FALSE);
+ }
+
+ return (B_TRUE);
+}
+
+static boolean_t
+mkisofs(const char *image_dir, const char *image)
+{
+ struct stat sbuf;
+ int chars;
+ char cmd[MAXPATHLEN];
+ FILE *fp;
+ int status;
+ char msg[WBCGI_MAXBUF];
+
+ if (!WBCGI_DIR_EXISTS(image_dir, sbuf)) {
+ print_status(500, "(mksiofs: missing image_dir)");
+ return (B_FALSE);
+ }
+
+ if ((chars = snprintf(cmd, sizeof (cmd), "%s -quiet -o %s -r %s",
+ WBCGI_MKISOFS_PATH, image, image_dir)) < 0 ||
+ chars > sizeof (cmd)) {
+ print_status(500, "(mkisofs: buffer overflow)");
+ return (B_FALSE);
+ }
+
+ if ((fp = popen(cmd, "w")) == NULL) {
+ print_status(500, "(mkisofs: missing/file error)");
+ return (B_FALSE);
+ }
+ if ((status = WEXITSTATUS(pclose(fp))) != 0) {
+ (void) snprintf(msg, sizeof (msg),
+ "(mkisofs: failed, status=%d)", status);
+ print_status(500, msg);
+ return (B_FALSE);
+ }
+
+ if (!WBCGI_FILE_EXISTS(image, sbuf)) {
+ print_status(500, "(mksiofs: failed to create image)");
+ return (B_FALSE);
+ }
+
+ return (B_TRUE);
+}
+
+/*
+ * This function, when invoked with a file name, optional network and
+ * client * ID strings, and callback function will walk the directory
+ * hierarchy between the concatenation of NB_NETBOOT_ROOT, the network
+ * number, and client ID, invoking the callback function each time it
+ * finds a file with the specified name until the hierarchy walk
+ * terminates or the callback function returns a value other than
+ * WBCGI_FTW_CBCONT.
+ *
+ * Arguments:
+ * filename - Name of file to search for.
+ * net - Optional network number to include in search hierarchy.
+ * cid - Optional client ID to include in search hierarchy.
+ * cb - Callback function to be called when file is found.
+ * arg - Argument to be supplied to the callback funtion.
+ *
+ * Returns:
+ * WBCGI_FTW_DONE, WBCGI_FTW_CBOK or WBCGI_FTW_CBERR.
+ */
+static int
+netboot_ftw(const char *filename, const char *net, const char *cid,
+ int (*cb)(const char *, void *arg), void *arg)
+{
+ char ckpath[MAXPATHLEN];
+ char *ptr;
+ int ret;
+ struct stat buf;
+
+ /*
+ * All searches start at the root.
+ */
+ if (strlcpy(ckpath, NB_NETBOOT_ROOT,
+ sizeof (ckpath)) >= sizeof (ckpath)) {
+ return (WBCGI_FTW_CBERR);
+ }
+
+ /*
+ * Remaining part of path depends on 'net' and 'cid'. Note that
+ * it is not valid to have a NULL 'net', but non-NULL 'cid'.
+ */
+ if (net == NULL && cid != NULL) {
+ return (WBCGI_FTW_CBERR);
+ }
+ if (net != NULL) {
+ if (strlcat(ckpath, net, sizeof (ckpath)) >= sizeof (ckpath) ||
+ strlcat(ckpath, "/", sizeof (ckpath)) >= sizeof (ckpath)) {
+ return (WBCGI_FTW_CBERR);
+ }
+ }
+ if (cid != NULL) {
+ if (strlcat(ckpath, cid, sizeof (ckpath)) >= sizeof (ckpath) ||
+ strlcat(ckpath, "/", sizeof (ckpath)) >= sizeof (ckpath)) {
+ return (WBCGI_FTW_CBERR);
+ }
+ }
+
+ /*
+ * Loop through hierarchy and check for file existence.
+ */
+ for (;;) {
+ if (strlcat(ckpath, filename,
+ sizeof (ckpath)) >= sizeof (ckpath)) {
+ return (WBCGI_FTW_CBERR);
+ }
+ if (WBCGI_FILE_EXISTS(ckpath, buf)) {
+ if ((ret = cb(ckpath, arg)) != WBCGI_FTW_CBCONT) {
+ return (ret);
+ }
+ }
+
+ /*
+ * Remove last component (which would be the
+ * filename). If this leaves the root, then
+ * hierarchy search has been completed. Otherwise,
+ * remove the trailing directory and go try again.
+ */
+ ptr = strrchr(ckpath, '/');
+ *++ptr = '\0';
+ if (strcmp(NB_NETBOOT_ROOT, ckpath) == 0) {
+ return (WBCGI_FTW_DONE);
+ } else {
+ *--ptr = '\0';
+ ptr = strrchr(ckpath, '/');
+ *++ptr = '\0';
+ }
+ }
+}
+
+/*ARGSUSED*/
+static int
+noact_cb(const char *path, void *arg)
+{
+ return (WBCGI_FTW_CBOK);
+}
+
+static int
+set_pathname(const char *path, void *pathname)
+{
+ *(char **)pathname = strdup((char *)path);
+ return (WBCGI_FTW_CBOK);
+}
+
+static int
+create_keystore(const char *path, void *keystorepath)
+{
+ if (!extract_keystore(path, (char *)keystorepath)) {
+ return (WBCGI_FTW_CBERR);
+ }
+ return (WBCGI_FTW_CBOK);
+}
+
+static int
+copy_certstore(const char *path, void *certstorepath)
+{
+ if (!copy_file(path, (char *)certstorepath)) {
+ return (WBCGI_FTW_CBERR);
+ }
+ return (WBCGI_FTW_CBOK);
+}
+
+/*
+ * Add the certs found in the trustfile found in path (a trust store) to
+ * the file found at bootfs_dir/truststore. If necessary, create the
+ * output file.
+ */
+static int
+build_trustfile(const char *path, void *truststorepath)
+{
+ int ret = WBCGI_FTW_CBERR;
+ STACK_OF(X509) *i_anchors = NULL;
+ STACK_OF(X509) *o_anchors = NULL;
+ char message[WBCGI_MAXBUF];
+ PKCS12 *p12 = NULL;
+ FILE *rfp = NULL;
+ FILE *wfp = NULL;
+ struct stat i_st;
+ struct stat o_st;
+ X509 *x = NULL;
+ int errtype = 0;
+ int wfd = -1;
+ int chars;
+ int i;
+
+ if (!WBCGI_FILE_EXISTS(path, i_st)) {
+ goto cleanup;
+ }
+
+ if (WBCGI_FILE_EXISTS((char *)truststorepath, o_st)) {
+ /*
+ * If we are inadvertantly writing to the input file.
+ * return success.
+ * XXX Pete: how can this happen, and why success?
+ */
+ if (i_st.st_ino == o_st.st_ino) {
+ ret = WBCGI_FTW_CBCONT;
+ goto cleanup;
+ }
+ if ((wfp = fopen((char *)truststorepath, "r+")) == NULL) {
+ goto cleanup;
+ }
+ /*
+ * Read what's already there, so that new information
+ * can be added.
+ */
+ if ((p12 = d2i_PKCS12_fp(wfp, NULL)) == NULL) {
+ errtype = 1;
+ goto cleanup;
+ }
+ i = sunw_PKCS12_parse(p12, WANBOOT_PASSPHRASE, DO_NONE, NULL,
+ 0, NULL, NULL, NULL, &o_anchors);
+ if (i <= 0) {
+ errtype = 1;
+ goto cleanup;
+ }
+
+ PKCS12_free(p12);
+ p12 = NULL;
+ } else {
+ if (errno != ENOENT) {
+ chars = snprintf(message, sizeof (message),
+ "(error accessing file %s, error %s)",
+ path, strerror(errno));
+ if (chars > 0 && chars < sizeof (message))
+ print_status(500, message);
+ else
+ print_status(500, NULL);
+ return (WBCGI_FTW_CBERR);
+ }
+
+ /*
+ * Note: We could copy the file to the new trustfile, but
+ * we can't verify the password that way. Therefore, copy
+ * it by reading it.
+ */
+ if ((wfd = open((char *)truststorepath,
+ O_CREAT|O_EXCL|O_RDWR, 0700)) < 0) {
+ goto cleanup;
+ }
+ if ((wfp = fdopen(wfd, "w+")) == NULL) {
+ goto cleanup;
+ }
+ o_anchors = sk_X509_new_null();
+ if (o_anchors == NULL) {
+ goto cleanup;
+ }
+ }
+
+ if ((rfp = fopen(path, "r")) == NULL) {
+ goto cleanup;
+ }
+ if ((p12 = d2i_PKCS12_fp(rfp, NULL)) == NULL) {
+ errtype = 1;
+ goto cleanup;
+ }
+ i = sunw_PKCS12_parse(p12, WANBOOT_PASSPHRASE, DO_NONE, NULL, 0, NULL,
+ NULL, NULL, &i_anchors);
+ if (i <= 0) {
+ errtype = 1;
+ goto cleanup;
+ }
+ PKCS12_free(p12);
+ p12 = NULL;
+
+ /*
+ * Merge the two stacks of pkcs12 certs.
+ */
+ for (i = 0; i < sk_X509_num(i_anchors); i++) {
+ /* LINTED */
+ x = sk_X509_delete(i_anchors, i);
+ (void) sk_X509_push(o_anchors, x);
+ }
+
+ /*
+ * Create the pkcs12 structure from the modified input stack and
+ * then write out that structure.
+ */
+ p12 = sunw_PKCS12_create((const char *)WANBOOT_PASSPHRASE, NULL, NULL,
+ o_anchors);
+ if (p12 == NULL) {
+ goto cleanup;
+ }
+ rewind(wfp);
+ if (i2d_PKCS12_fp(wfp, p12) == 0) {
+ goto cleanup;
+ }
+
+ ret = WBCGI_FTW_CBCONT;
+cleanup:
+ if (ret == WBCGI_FTW_CBERR) {
+ if (errtype == 1) {
+ chars = snprintf(message, sizeof (message),
+ "(internal PKCS12 error while copying %s to %s)",
+ path, (char *)truststorepath);
+ } else {
+ chars = snprintf(message, sizeof (message),
+ "(error copying %s to %s)",
+ path, (char *)truststorepath);
+ }
+ if (chars > 0 && chars <= sizeof (message)) {
+ print_status(500, message);
+ } else {
+ print_status(500, NULL);
+ }
+ }
+ if (rfp != NULL) {
+ (void) fclose(rfp);
+ }
+ if (wfp != NULL) {
+ /* Will also close wfd */
+ (void) fclose(wfp);
+ }
+ if (p12 != NULL) {
+ PKCS12_free(p12);
+ }
+ if (i_anchors != NULL) {
+ sk_X509_pop_free(i_anchors, X509_free);
+ }
+ if (o_anchors != NULL) {
+ sk_X509_pop_free(o_anchors, X509_free);
+ }
+
+ return (ret);
+}
+
+static boolean_t
+check_key_type(const char *keyfile, const char *keytype, int flag)
+{
+ boolean_t ret = B_FALSE;
+ FILE *key_fp = NULL;
+ wbku_key_attr_t ka;
+
+ /*
+ * Map keytype into the ka structure
+ */
+ if (wbku_str_to_keyattr(keytype, &ka, flag) != WBKU_SUCCESS) {
+ goto cleanup;
+ }
+
+ /*
+ * Open the key file for reading.
+ */
+ if ((key_fp = fopen(keyfile, "r")) == NULL) {
+ goto cleanup;
+ }
+
+ /*
+ * Find the valid client key, if it exists.
+ */
+ if (wbku_find_key(key_fp, NULL, &ka, NULL, B_FALSE) != WBKU_SUCCESS) {
+ goto cleanup;
+ }
+
+ ret = B_TRUE;
+cleanup:
+ if (key_fp != NULL) {
+ (void) fclose(key_fp);
+ }
+
+ return (ret);
+}
+
+static boolean_t
+resolve_hostname(const char *hostname, nvlist_t *nvl, boolean_t may_be_crap)
+{
+ struct sockaddr_in sin;
+ struct hostent *hp;
+
+ if (((hp = gethostbyname(hostname)) == NULL) ||
+ (hp->h_addrtype != AF_INET) ||
+ (hp->h_length != sizeof (struct in_addr))) {
+ if (!may_be_crap) {
+ print_status(500, "(error resolving hostname)");
+ }
+ return (may_be_crap);
+ }
+ (void) memcpy(&sin.sin_addr, hp->h_addr, hp->h_length);
+
+ if (nvlist_add_string(nvl,
+ (char *)hostname, inet_ntoa(sin.sin_addr)) != 0) {
+ print_status(500, "(error adding hostname to nvlist)");
+ return (B_FALSE);
+ }
+
+ return (B_TRUE);
+}
+
+/*
+ * one_name() is called for each certificate found and is passed the string
+ * that X509_NAME_oneline() returns. Its job is to find the common name and
+ * determine whether it is a host name; if it is then a line suitable for
+ * inclusion in /etc/inet/hosts is written to that file.
+ */
+static boolean_t
+one_name(const char *namestr, nvlist_t *nvl)
+{
+ boolean_t ret = B_TRUE;
+ char *p;
+ char *q;
+ char c;
+
+ if (namestr != NULL &&
+ (p = strstr(namestr, WBCGI_CNSTR)) != NULL) {
+ p += WBCGI_CNSTR_LEN;
+
+ if ((q = strpbrk(p, WBCGI_NAMESEP)) != NULL) {
+ c = *q;
+ *q = '\0';
+ ret = resolve_hostname(p, nvl, B_TRUE);
+ *q = c;
+ } else {
+ ret = resolve_hostname(p, nvl, B_TRUE);
+ }
+ }
+
+ return (ret);
+}
+
+/*
+ * Loop through the certificates in a file
+ */
+static int
+get_hostnames(const char *path, void *nvl)
+{
+ int ret = WBCGI_FTW_CBERR;
+ STACK_OF(X509) *certs = NULL;
+ PKCS12 *p12 = NULL;
+ char message[WBCGI_MAXBUF];
+ char buf[WBCGI_MAXBUF + 1];
+ FILE *rfp = NULL;
+ X509 *x = NULL;
+ int errtype = 0;
+ int chars;
+ int i;
+
+ if ((rfp = fopen(path, "r")) == NULL) {
+ goto cleanup;
+ }
+
+ if ((p12 = d2i_PKCS12_fp(rfp, NULL)) == NULL) {
+ errtype = 1;
+ goto cleanup;
+ }
+ i = sunw_PKCS12_parse(p12, WANBOOT_PASSPHRASE, DO_NONE, NULL, 0, NULL,
+ NULL, NULL, &certs);
+ if (i <= 0) {
+ errtype = 1;
+ goto cleanup;
+ }
+
+ PKCS12_free(p12);
+ p12 = NULL;
+
+ for (i = 0; i < sk_X509_num(certs); i++) {
+ /* LINTED */
+ x = sk_X509_value(certs, i);
+ if (!one_name(sunw_issuer_attrs(x, buf, sizeof (buf) - 1),
+ nvl)) {
+ goto cleanup;
+ }
+ }
+
+ ret = WBCGI_FTW_CBCONT;
+cleanup:
+ if (ret == WBCGI_FTW_CBERR) {
+ if (errtype == 1) {
+ chars = snprintf(message, sizeof (message),
+ "(internal PKCS12 error reading %s)", path);
+ } else {
+ chars = snprintf(message, sizeof (message),
+ "error reading %s", path);
+ }
+ if (chars > 0 && chars <= sizeof (message)) {
+ print_status(500, message);
+ } else {
+ print_status(500, NULL);
+ }
+ }
+ if (rfp != NULL) {
+ (void) fclose(rfp);
+ }
+ if (p12 != NULL) {
+ PKCS12_free(p12);
+ }
+ if (certs != NULL) {
+ sk_X509_pop_free(certs, X509_free);
+ }
+
+ return (ret);
+}
+
+/*
+ * Create a hosts file by extracting hosts from client and truststore
+ * files. Use the CN. Then we should copy that file to the inet dir.
+ */
+static boolean_t
+create_hostsfile(const char *hostsfile, const char *net, const char *cid)
+{
+ boolean_t ret = B_FALSE;
+ nvlist_t *nvl;
+ nvpair_t *nvp;
+ FILE *hostfp = NULL;
+ int hostfd = -1;
+ int i;
+ char *hostslist;
+ const char *bc_urls[] = { BC_ROOT_SERVER, BC_BOOT_LOGGER, NULL };
+
+ /*
+ * Allocate nvlist handle to store our hostname/IP pairs.
+ */
+ if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {
+ print_status(500, "(error allocating hostname nvlist)");
+ goto cleanup;
+ }
+
+ /*
+ * Extract and resolve hostnames from CNs.
+ */
+ if (netboot_ftw(NB_CLIENT_CERT, net, cid,
+ get_hostnames, nvl) == WBCGI_FTW_CBERR ||
+ netboot_ftw(NB_CA_CERT, net, cid,
+ get_hostnames, nvl) == WBCGI_FTW_CBERR) {
+ goto cleanup;
+ }
+
+ /*
+ * Extract and resolve hostnames from any URLs in bootconf.
+ */
+ for (i = 0; bc_urls[i] != NULL; ++i) {
+ char *urlstr;
+ url_t url;
+
+ if ((urlstr = bootconf_get(&bc_handle, bc_urls[i])) != NULL &&
+ url_parse(urlstr, &url) == URL_PARSE_SUCCESS) {
+ if (!resolve_hostname(url.hport.hostname,
+ nvl, B_FALSE)) {
+ goto cleanup;
+ }
+ }
+ }
+
+ /*
+ * If there is a resolve-hosts list in bootconf, resolve those
+ * hostnames too.
+ */
+ if ((hostslist = bootconf_get(&bc_handle, BC_RESOLVE_HOSTS)) != NULL) {
+ char *hostname;
+
+ for (hostname = strtok(hostslist, ","); hostname != NULL;
+ hostname = strtok(NULL, ",")) {
+ if (!resolve_hostname(hostname, nvl, B_FALSE)) {
+ goto cleanup;
+ }
+ }
+ }
+
+ /*
+ * Now write the hostname/IP pairs gathered to the hosts file.
+ */
+ if ((hostfd = open(hostsfile,
+ O_RDWR|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR)) == -1 ||
+ (hostfp = fdopen(hostfd, "w+")) == NULL) {
+ print_status(500, "(error creating hosts file)");
+ goto cleanup;
+ }
+ for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL;
+ nvp = nvlist_next_nvpair(nvl, nvp)) {
+ char *hostname;
+ char *ipstr;
+
+ hostname = nvpair_name(nvp);
+ if (nvpair_value_string(nvp, &ipstr) != 0) {
+ print_status(500, "(nvl error writing hosts file)");
+ goto cleanup;
+ }
+
+ if (fprintf(hostfp, "%s\t%s\n", ipstr, hostname) < 0) {
+ print_status(500, "(error writing hosts file)");
+ goto cleanup;
+ }
+ }
+
+ ret = B_TRUE;
+cleanup:
+ if (nvl != NULL) {
+ nvlist_free(nvl);
+ }
+ if (hostfp != NULL) {
+ /*
+ * hostfd is automatically closed as well.
+ */
+ (void) fclose(hostfp);
+ }
+
+ return (ret);
+}
+
+static boolean_t
+bootfile_payload(const char *docroot, char **bootpathp)
+{
+ boolean_t ret = B_FALSE;
+ char *boot_file;
+ struct stat sbuf;
+
+ if ((boot_file = bootconf_get(&bc_handle, BC_BOOT_FILE)) == NULL) {
+ print_status(500, "(boot_file must be specified)");
+ goto cleanup;
+ }
+ if ((*bootpathp = make_path(docroot, boot_file)) == NULL) {
+ goto cleanup;
+ }
+ if (!WBCGI_FILE_EXISTS(*bootpathp, sbuf)) {
+ print_status(500, "(boot_file missing)");
+ goto cleanup;
+ }
+
+ ret = B_TRUE;
+cleanup:
+ return (ret);
+}
+
+/*
+ * Create the wanboot file system whose contents are determined by the
+ * security configuration specified in bootconf.
+ */
+static boolean_t
+wanbootfs_payload(const char *net, const char *cid, const char *nonce,
+ const char *bootconf, char **wanbootfs_imagep)
+{
+ int ret = B_FALSE;
+
+ char *server_authentication;
+ char *client_authentication;
+ char *scf;
+
+ char *bootfs_dir = NULL;
+ char *bootfs_etc_dir = NULL;
+ char *bootfs_etc_inet_dir = NULL;
+ char *bootfs_dev_dir = NULL;
+
+ char *systemconf = NULL;
+ char *keystorepath = NULL;
+ char *certstorepath = NULL;
+ char *truststorepath = NULL;
+ char *bootconfpath = NULL;
+ char *systemconfpath = NULL;
+ char *urandompath = NULL;
+ char *noncepath = NULL;
+ char *hostspath = NULL;
+ char *etc_hostspath = NULL;
+ char *timestamppath = NULL;
+
+ boolean_t authenticate_client;
+ boolean_t authenticate_server;
+
+ struct stat sbuf;
+
+ /*
+ * Initialize SSL stuff.
+ */
+ sunw_crypto_init();
+
+ /*
+ * Get the security strategy values.
+ */
+ client_authentication = bootconf_get(&bc_handle,
+ BC_CLIENT_AUTHENTICATION);
+ authenticate_client = (client_authentication != NULL &&
+ strcmp(client_authentication, "yes") == 0);
+ server_authentication = bootconf_get(&bc_handle,
+ BC_SERVER_AUTHENTICATION);
+ authenticate_server = (server_authentication != NULL &&
+ strcmp(server_authentication, "yes") == 0);
+
+ /*
+ * Make a temporary directory structure for the wanboot file system.
+ */
+ if ((bootfs_dir = gen_tmppath("bootfs_dir", net, cid)) == NULL ||
+ (bootfs_etc_dir = make_path(bootfs_dir, "etc")) == NULL ||
+ (bootfs_etc_inet_dir = make_path(bootfs_etc_dir, "inet")) == NULL ||
+ (bootfs_dev_dir = make_path(bootfs_dir, "dev")) == NULL) {
+ goto cleanup;
+ }
+ if (mkdirp(bootfs_dir, 0700) ||
+ mkdirp(bootfs_etc_dir, 0700) ||
+ mkdirp(bootfs_etc_inet_dir, 0700) ||
+ mkdirp(bootfs_dev_dir, 0700)) {
+ print_status(500, "(error creating wanbootfs dir structure)");
+ goto cleanup;
+ }
+
+ if (authenticate_client) {
+ /*
+ * Add the client private key.
+ */
+ if ((keystorepath = make_path(bootfs_dir,
+ NB_CLIENT_KEY)) == NULL ||
+ netboot_ftw(NB_CLIENT_KEY, net, cid,
+ create_keystore, keystorepath) != WBCGI_FTW_CBOK) {
+ goto cleanup;
+ }
+
+ /*
+ * Add the client certificate.
+ */
+ if ((certstorepath = make_path(bootfs_dir,
+ NB_CLIENT_CERT)) == NULL ||
+ netboot_ftw(NB_CLIENT_CERT, net, cid,
+ copy_certstore, certstorepath) != WBCGI_FTW_CBOK) {
+ goto cleanup;
+ }
+ }
+
+ if (authenticate_client || authenticate_server) {
+ /*
+ * Add the trustfile; at least one truststore must exist.
+ */
+ if ((truststorepath = make_path(bootfs_dir,
+ NB_CA_CERT)) == NULL) {
+ goto cleanup;
+ }
+ if (netboot_ftw(NB_CA_CERT, net, cid,
+ noact_cb, NULL) != WBCGI_FTW_CBOK) {
+ print_status(500, "(truststore not found)");
+ }
+ if (netboot_ftw(NB_CA_CERT, net, cid,
+ build_trustfile, truststorepath) == WBCGI_FTW_CBERR) {
+ goto cleanup;
+ }
+
+ /*
+ * Create the /dev/urandom file.
+ */
+ if ((urandompath = make_path(bootfs_dev_dir,
+ "urandom")) == NULL ||
+ !create_urandom(urandompath)) {
+ goto cleanup;
+ }
+ }
+
+ /*
+ * Add the wanboot.conf(4) file.
+ */
+ if ((bootconfpath = make_path(bootfs_dir, NB_WANBOOT_CONF)) == NULL ||
+ !copy_file(bootconf, bootconfpath)) {
+ goto cleanup;
+ }
+
+ /*
+ * Add the system_conf file if present.
+ */
+ if ((scf = bootconf_get(&bc_handle, BC_SYSTEM_CONF)) != NULL) {
+ if (netboot_ftw(scf, net, cid,
+ set_pathname, &systemconf) != WBCGI_FTW_CBOK) {
+ print_status(500, "(system_conf file not found)");
+ goto cleanup;
+ }
+ if ((systemconfpath = make_path(bootfs_dir,
+ NB_SYSTEM_CONF)) == NULL ||
+ !copy_file(systemconf, systemconfpath)) {
+ goto cleanup;
+ }
+ }
+
+ /*
+ * Create the /nonce file.
+ */
+ if ((noncepath = make_path(bootfs_dir, "nonce")) == NULL ||
+ !create_nonce(noncepath, nonce)) {
+ goto cleanup;
+ }
+
+ /*
+ * Create an /etc/inet/hosts file by extracting hostnames from CN,
+ * URLs in bootconf and resolve-hosts in bootconf.
+ */
+ if ((hostspath = make_path(bootfs_etc_inet_dir, "hosts")) == NULL ||
+ !create_hostsfile(hostspath, net, cid)) {
+ goto cleanup;
+ }
+
+ /*
+ * We would like to create a symbolic link etc/hosts -> etc/inet/hosts,
+ * but unfortunately the HSFS support in the standalone doesn't handle
+ * symlinks.
+ */
+ if ((etc_hostspath = make_path(bootfs_etc_dir, "hosts")) == NULL ||
+ !copy_file(hostspath, etc_hostspath)) {
+ goto cleanup;
+ }
+
+ /*
+ * Create the /timestamp file.
+ */
+ if ((timestamppath = make_path(bootfs_dir, "timestamp")) == NULL ||
+ !create_timestamp(timestamppath, "timestamp")) {
+ goto cleanup;
+ }
+
+ /*
+ * Create an HSFS file system for the directory.
+ */
+ if ((*wanbootfs_imagep = gen_tmppath("wanbootfs", net, cid)) == NULL ||
+ !mkisofs(bootfs_dir, *wanbootfs_imagep)) {
+ goto cleanup;
+ }
+
+ ret = B_TRUE;
+cleanup:
+ /*
+ * Clean up temporary files and directories.
+ */
+ if (keystorepath != NULL &&
+ WBCGI_FILE_EXISTS(keystorepath, sbuf)) {
+ (void) unlink(keystorepath);
+ }
+ if (certstorepath != NULL &&
+ WBCGI_FILE_EXISTS(certstorepath, sbuf)) {
+ (void) unlink(certstorepath);
+ }
+ if (truststorepath != NULL &&
+ WBCGI_FILE_EXISTS(truststorepath, sbuf)) {
+ (void) unlink(truststorepath);
+ }
+ if (bootconfpath != NULL &&
+ WBCGI_FILE_EXISTS(bootconfpath, sbuf)) {
+ (void) unlink(bootconfpath);
+ }
+ if (systemconfpath != NULL &&
+ WBCGI_FILE_EXISTS(systemconfpath, sbuf)) {
+ (void) unlink(systemconfpath);
+ }
+ if (urandompath != NULL &&
+ WBCGI_FILE_EXISTS(urandompath, sbuf)) {
+ (void) unlink(urandompath);
+ }
+ if (noncepath != NULL &&
+ WBCGI_FILE_EXISTS(noncepath, sbuf)) {
+ (void) unlink(noncepath);
+ }
+ if (hostspath != NULL &&
+ WBCGI_FILE_EXISTS(hostspath, sbuf)) {
+ (void) unlink(hostspath);
+ }
+ if (etc_hostspath != NULL &&
+ WBCGI_FILE_EXISTS(etc_hostspath, sbuf)) {
+ (void) unlink(etc_hostspath);
+ }
+ if (timestamppath != NULL &&
+ WBCGI_FILE_EXISTS(timestamppath, sbuf)) {
+ (void) unlink(timestamppath);
+ }
+
+ if (bootfs_etc_inet_dir != NULL &&
+ WBCGI_DIR_EXISTS(bootfs_etc_inet_dir, sbuf)) {
+ (void) rmdir(bootfs_etc_inet_dir);
+ }
+ if (bootfs_etc_dir != NULL &&
+ WBCGI_DIR_EXISTS(bootfs_etc_dir, sbuf)) {
+ (void) rmdir(bootfs_etc_dir);
+ }
+ if (bootfs_dev_dir != NULL &&
+ WBCGI_DIR_EXISTS(bootfs_dev_dir, sbuf)) {
+ (void) rmdir(bootfs_dev_dir);
+ }
+ if (bootfs_dir != NULL &&
+ WBCGI_DIR_EXISTS(bootfs_dir, sbuf)) {
+ (void) rmdir(bootfs_dir);
+ }
+
+ /*
+ * Free allocated memory.
+ */
+ free_path(&bootfs_dir);
+ free_path(&bootfs_etc_dir);
+ free_path(&bootfs_etc_inet_dir);
+ free_path(&bootfs_dev_dir);
+
+ free_path(&systemconf);
+ free_path(&keystorepath);
+ free_path(&certstorepath);
+ free_path(&truststorepath);
+ free_path(&bootconfpath);
+ free_path(&systemconfpath);
+ free_path(&urandompath);
+ free_path(&noncepath);
+ free_path(&hostspath);
+ free_path(&etc_hostspath);
+ free_path(&timestamppath);
+
+ return (ret);
+}
+
+static boolean_t
+miniroot_payload(const char *net, const char *cid, const char *docroot,
+ char **rootpathp, char **rootinfop, boolean_t *https_rootserverp)
+{
+ boolean_t ret = B_FALSE;
+ char *root_server;
+ char *root_file;
+ url_t url;
+ struct stat sbuf;
+ char sizebuf[WBCGI_MAXBUF];
+ int chars;
+ int fd = -1;
+
+ if ((root_server = bootconf_get(&bc_handle, BC_ROOT_SERVER)) == NULL) {
+ print_status(500, "(root_server must be specified)");
+ goto cleanup;
+ }
+ if (url_parse(root_server, &url) != URL_PARSE_SUCCESS) {
+ print_status(500, "(root_server URL is invalid)");
+ }
+ *https_rootserverp = url.https;
+
+ if ((root_file = bootconf_get(&bc_handle, BC_ROOT_FILE)) == NULL) {
+ print_status(500, "(rootfile must be specified)");
+ goto cleanup;
+ }
+ if ((*rootpathp = make_path(docroot, root_file)) == NULL) {
+ goto cleanup;
+ }
+ if (!WBCGI_FILE_EXISTS(*rootpathp, sbuf)) {
+ print_status(500, "(root filesystem image missing)");
+ goto cleanup;
+ }
+
+ if ((*rootinfop = gen_tmppath("mrinfo", net, cid)) == NULL) {
+ goto cleanup;
+ }
+ if ((chars = snprintf(sizebuf, sizeof (sizebuf), "%ld",
+ sbuf.st_size)) < 0 || chars > sizeof (sizebuf) ||
+ (fd = open(*rootinfop,
+ O_RDWR|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR)) == -1 ||
+ !write_buffer(fd, sizebuf, strlen(sizebuf))) {
+ print_status(500, "(error creating miniroot info file)");
+ goto cleanup;
+ }
+
+ ret = B_TRUE;
+cleanup:
+ if (fd != -1) {
+ (void) close(fd);
+ }
+
+ return (ret);
+}
+
+static boolean_t
+deliver_payload(const char *payload, const char *payload_hash)
+{
+ int fd = fileno(stdout);
+ struct stat payload_buf, hash_buf;
+ int chars;
+ char main_header[WBCGI_MAXBUF];
+ char multi_header[WBCGI_MAXBUF];
+ char multi_header1[WBCGI_MAXBUF];
+ char multi_header2[WBCGI_MAXBUF];
+ char multi_end[WBCGI_MAXBUF];
+ size_t msglen;
+
+ if (!WBCGI_FILE_EXISTS(payload, payload_buf) ||
+ !WBCGI_FILE_EXISTS(payload_hash, hash_buf)) {
+ print_status(500, "(payload/hash file(s) missing)");
+ return (B_FALSE);
+ }
+
+ /*
+ * Multi-part header.
+ */
+ if ((chars = snprintf(multi_header, sizeof (multi_header),
+ "%s--%s%s%sapplication/octet-stream%s%s", WBCGI_CRNL,
+ WBCGI_WANBOOT_BNDTXT, WBCGI_CRNL, WBCGI_CONTENT_TYPE, WBCGI_CRNL,
+ WBCGI_CONTENT_LENGTH)) < 0 || chars > sizeof (multi_header)) {
+ print_status(500, "(error creating multi_header)");
+ return (B_FALSE);
+ }
+
+ /*
+ * Multi-part header for part one.
+ */
+ if ((chars = snprintf(multi_header1, sizeof (multi_header1),
+ "%s%ld%s%s", multi_header, payload_buf.st_size, WBCGI_CRNL,
+ WBCGI_CRNL)) < 0 || chars > sizeof (multi_header1)) {
+ print_status(500, "(error creating multi_header1)");
+ return (B_FALSE);
+ }
+
+ /*
+ * Multi-part header for part two.
+ */
+ if ((chars = snprintf(multi_header2, sizeof (multi_header2),
+ "%s%ld%s%s", multi_header, hash_buf.st_size, WBCGI_CRNL,
+ WBCGI_CRNL)) < 0 || chars > sizeof (multi_header2)) {
+ print_status(500, "(error creating multi_header2)");
+ return (B_FALSE);
+ }
+
+ /*
+ * End-of-parts Trailer.
+ */
+ if ((chars = snprintf(multi_end, sizeof (multi_end),
+ "%s--%s--%s", WBCGI_CRNL, WBCGI_WANBOOT_BNDTXT,
+ WBCGI_CRNL)) < 0 || chars > sizeof (multi_end)) {
+ print_status(500, "(error creating multi_end)");
+ return (B_FALSE);
+ }
+
+ /*
+ * Message header.
+ */
+ msglen = payload_buf.st_size + hash_buf.st_size +
+ strlen(multi_header1) + strlen(multi_header2) + strlen(multi_end);
+
+ if ((chars = snprintf(main_header, sizeof (main_header),
+ "%s%u%s%smultipart/mixed; boundary=%s%s%s", WBCGI_CONTENT_LENGTH,
+ msglen, WBCGI_CRNL, WBCGI_CONTENT_TYPE, WBCGI_WANBOOT_BNDTXT,
+ WBCGI_CRNL, WBCGI_CRNL)) < 0 || chars > sizeof (main_header)) {
+ print_status(500, "(error creating main_header)");
+ return (B_FALSE);
+ }
+
+ /*
+ * Write the message out. If things fall apart during this then
+ * there's no way to report the error back to the client.
+ */
+ if (!write_buffer(fd, main_header, strlen(main_header)) ||
+ !write_buffer(fd, multi_header1, strlen(multi_header1)) ||
+ !write_file(fd, payload, payload_buf.st_size) ||
+ !write_buffer(fd, multi_header2, strlen(multi_header2)) ||
+ !write_file(fd, payload_hash, hash_buf.st_size) ||
+ !write_buffer(fileno(stdout), multi_end, strlen(multi_end))) {
+ return (B_FALSE);
+ }
+
+ return (B_TRUE);
+}
+
+
+/*ARGSUSED*/
+int
+main(int argc, char **argv)
+{
+ int ret = WBCGI_STATUS_ERR;
+ struct stat sbuf;
+ int content;
+ char *net;
+ char *cid;
+ char *nonce;
+ char *docroot;
+ char *payload;
+ char *signature_type;
+ char *encryption_type;
+ char *bootconf = NULL;
+ char *keyfile = NULL;
+ char *bootpath = NULL;
+ char *wanbootfs_image = NULL;
+ char *rootpath = NULL;
+ char *miniroot_info = NULL;
+ char *encr_payload = NULL;
+ char *payload_hash = NULL;
+ boolean_t https_rootserver;
+
+ /*
+ * Process the query string.
+ */
+ if (!get_request_info(&content, &net, &cid, &nonce, &docroot)) {
+ goto cleanup;
+ }
+
+ /*
+ * Sanity check that the netboot directory exists.
+ */
+ if (!WBCGI_DIR_EXISTS(NB_NETBOOT_ROOT, sbuf)) {
+ print_status(500, "(" NB_NETBOOT_ROOT " does not exist)");
+ goto cleanup;
+ }
+
+ /*
+ * Get absolute bootconf pathname.
+ */
+ if (netboot_ftw(NB_WANBOOT_CONF, net, cid,
+ set_pathname, &bootconf) != WBCGI_FTW_CBOK) {
+ print_status(500, "(wanboot.conf not found)");
+ goto cleanup;
+ }
+
+ /*
+ * Initialize bc_handle from the given wanboot.conf file.
+ */
+ if (bootconf_init(&bc_handle, bootconf) != BC_SUCCESS) {
+ char message[WBCGI_MAXBUF];
+ int chars;
+
+ chars = snprintf(message, sizeof (message),
+ "(wanboot.conf error: %s)", bootconf_errmsg(&bc_handle));
+ if (chars > 0 && chars < sizeof (message))
+ print_status(500, message);
+ else
+ print_status(500, "(wanboot.conf error)");
+ goto cleanup;
+ }
+
+ /*
+ * Get and check signature and encryption types,
+ * presence of helper utilities, keystore, etc.
+ */
+ if ((signature_type = bootconf_get(&bc_handle,
+ BC_SIGNATURE_TYPE)) != NULL) {
+ if (!WBCGI_FILE_EXISTS(WBCGI_HMAC_PATH, sbuf)) {
+ print_status(500, "(hmac utility not found)");
+ goto cleanup;
+ }
+ if (keyfile == NULL &&
+ netboot_ftw(NB_CLIENT_KEY, net, cid,
+ set_pathname, &keyfile) != WBCGI_FTW_CBOK) {
+ print_status(500, "(keystore not found)");
+ goto cleanup;
+ }
+ if (!check_key_type(keyfile, signature_type, WBKU_HASH_KEY)) {
+ print_status(500, "(hash key not found)");
+ goto cleanup;
+ }
+ }
+ if ((encryption_type = bootconf_get(&bc_handle,
+ BC_ENCRYPTION_TYPE)) != NULL) {
+ if (signature_type == NULL) {
+ print_status(500, "(encrypted but not signed)");
+ goto cleanup;
+ }
+ if (!WBCGI_FILE_EXISTS(WBCGI_ENCR_PATH, sbuf)) {
+ print_status(500, "(encr utility not found)");
+ goto cleanup;
+ }
+ if (keyfile == NULL &&
+ netboot_ftw(NB_CLIENT_KEY, net, cid,
+ set_pathname, &keyfile) != WBCGI_FTW_CBOK) {
+ print_status(500, "(keystore not found)");
+ goto cleanup;
+ }
+ if (!check_key_type(keyfile, encryption_type, WBKU_ENCR_KEY)) {
+ print_status(500, "(encr key not found)");
+ goto cleanup;
+ }
+ }
+
+ /*
+ * Determine/create our payload.
+ */
+ switch (content) {
+ case WBCGI_CONTENT_BOOTFILE:
+ if (!bootfile_payload(docroot, &bootpath)) {
+ goto cleanup;
+ }
+ payload = bootpath;
+
+ break;
+
+ case WBCGI_CONTENT_BOOTFS:
+ if (!wanbootfs_payload(net, cid, nonce,
+ bootconf, &wanbootfs_image)) {
+ goto cleanup;
+ }
+ payload = wanbootfs_image;
+
+ break;
+
+ case WBCGI_CONTENT_ROOTFS:
+ if (!miniroot_payload(net, cid, docroot,
+ &rootpath, &miniroot_info, &https_rootserver)) {
+ goto cleanup;
+ }
+ payload = rootpath;
+
+ break;
+ }
+
+ /*
+ * Encrypt the payload if necessary.
+ */
+ if (content != WBCGI_CONTENT_BOOTFILE &&
+ content != WBCGI_CONTENT_ROOTFS &&
+ encryption_type != NULL) {
+ if ((encr_payload = gen_tmppath("encr", net, cid)) == NULL) {
+ goto cleanup;
+ }
+
+ if (!encrypt_payload(payload, encr_payload, keyfile,
+ encryption_type)) {
+ goto cleanup;
+ }
+
+ payload = encr_payload;
+ }
+
+ /*
+ * Compute the hash (actual or null).
+ */
+ if ((payload_hash = gen_tmppath("hash", net, cid)) == NULL) {
+ goto cleanup;
+ }
+
+ if (signature_type != NULL &&
+ (content != WBCGI_CONTENT_ROOTFS || !https_rootserver)) {
+ if (!hash_payload(payload, payload_hash, keyfile)) {
+ goto cleanup;
+ }
+ } else {
+ if (!create_null_hash(payload_hash)) {
+ goto cleanup;
+ }
+ }
+
+ /*
+ * For the rootfs the actual payload transmitted is the file
+ * containing the size of the rootfs (as a string of ascii digits);
+ * point payload at this instead.
+ */
+ if (content == WBCGI_CONTENT_ROOTFS) {
+ payload = miniroot_info;
+ }
+
+ /*
+ * Finally, deliver the payload and hash as a multipart message.
+ */
+ if (!deliver_payload(payload, payload_hash)) {
+ goto cleanup;
+ }
+
+ ret = WBCGI_STATUS_OK;
+cleanup:
+ /*
+ * Clean up temporary files.
+ */
+ if (wanbootfs_image != NULL &&
+ WBCGI_FILE_EXISTS(wanbootfs_image, sbuf)) {
+ (void) unlink(wanbootfs_image);
+ }
+ if (miniroot_info != NULL &&
+ WBCGI_FILE_EXISTS(miniroot_info, sbuf)) {
+ (void) unlink(miniroot_info);
+ }
+ if (encr_payload != NULL &&
+ WBCGI_FILE_EXISTS(encr_payload, sbuf)) {
+ (void) unlink(encr_payload);
+ }
+ if (payload_hash != NULL &&
+ WBCGI_FILE_EXISTS(payload_hash, sbuf)) {
+ (void) unlink(payload_hash);
+ }
+
+ /*
+ * Free up any allocated strings.
+ */
+ free_path(&bootconf);
+ free_path(&keyfile);
+ free_path(&bootpath);
+ free_path(&wanbootfs_image);
+ free_path(&rootpath);
+ free_path(&miniroot_info);
+ free_path(&encr_payload);
+ free_path(&payload_hash);
+
+ bootconf_end(&bc_handle);
+
+ return (ret);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/wanboot/wanboot.xcl b/usr/src/cmd/cmd-inet/usr.lib/wanboot/wanboot.xcl
new file mode 100644
index 0000000000..bdc2e166d0
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/wanboot/wanboot.xcl
@@ -0,0 +1,61 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+msgid "type"
+msgid "%s\n"
+msgid ""
+msgid "o:k:"
+msgid "r"
+msgid "i:k:"
+msgid "do:"
+msgid "/dev/openprom"
+msgid "open: /dev/openprom"
+msgid "setkey: ioctl"
+msgid "getpassphrase"
+msgid "%s "
+msgid "0.0.0.0"
+msgid "00000000000000"
+msgid "net"
+msgid "cid"
+msgid "%s"
+msgid "%s/%s"
+msgid "%s/%s/%s"
+msgid "/keystore"
+msgid "r+"
+msgid "w"
+msgid "%04x"
+msgid "\n"
+msgid "master"
+msgid "client"
+msgid "dcmo:"
+msgid ".XXXXXX"
+msgid "%s.XXXXXX"
+msgid "\n\t"
+msgid "ixf:k:s:o:"
+msgid "vc:i:k:l:t:"
+msgid "none"
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/Makefile b/usr/src/cmd/cmd-inet/usr.sadm/Makefile
new file mode 100644
index 0000000000..f17361b63e
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/Makefile
@@ -0,0 +1,67 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 1999-2003 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# cmd/cmd-inet/usr.sadm/Makefile
+
+SUBDIRS1= dhcpmgr
+SUBDIRS2= scripts
+
+include ../../Makefile.cmd
+#
+# Message catalog
+#
+POFILES= dhcpmgr/lib/dhcpmgr.po
+POFILE= usr.sadm.po
+
+all:= TARGET= all
+install:= TARGET= install
+clean:= TARGET= clean
+clobber:= TARGET= clobber
+lint:= TARGET= lint
+_msg:= TARGET= _msg
+
+.KEEP_STATE:
+
+.PARALLEL: $(SUBDIRS1) $(SUBDIRS2)
+
+all clean clobber lint: $(SUBDIRS1)
+
+install: $(SUBDIRS1) $(SUBDIRS2)
+
+#
+# message catalog
+#
+_msg: $(SUBDIRS1) .WAIT $(POFILE)
+
+$(POFILE): $(POFILES)
+ $(RM) $@
+ cat $(POFILES) > $@
+
+$(SUBDIRS1) $(SUBDIRS2): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/Makefile b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/Makefile
new file mode 100644
index 0000000000..4e43682c0b
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/Makefile
@@ -0,0 +1,180 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 1999-2002 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# cmd/cmd-inet/usr.sadm/dhcpmgr/Makefile
+
+include $(SRC)/Makefile.master
+
+SUBDIRS = com lib bin help
+
+all := TARGET= all
+install := TARGET= install
+clean := TARGET= clean
+clobber := TARGET= clobber
+lint := TARGET= lint
+_msg := TARGET= _msg
+
+GUI_JAR= dhcpmgr.jar
+GUI_JARFILES= com/sun/dhcpmgr/client/*.class \
+ com/sun/dhcpmgr/ui/*.class \
+ com/sun/dhcpmgr/ui/*.gif \
+ com/sun/dhcpmgr/client/*.properties \
+ com/sun/dhcpmgr/ui/*.properties
+
+SERVER_JAR= dhcpsvc.jar
+SERVER_JARFILES= com/sun/dhcpmgr/server/*.class \
+ com/sun/dhcpmgr/bridge/*.class \
+ com/sun/dhcpmgr/server/*.properties \
+ com/sun/dhcpmgr/bridge/*.properties
+
+
+CLI_JAR= dhcpcli.jar
+CLI_JARFILES= com/sun/dhcpmgr/cli/*/*.class \
+ com/sun/dhcpmgr/cli/*/*.properties
+
+COMMON_JAR= dhcpcommon.jar
+COMMON_JARFILES= com/sun/dhcpmgr/data/*.class \
+ com/sun/dhcpmgr/data/qualifier/*.class \
+ com/sun/dhcpmgr/common/*.class \
+ com/sun/dhcpmgr/data/*.properties \
+ com/sun/dhcpmgr/common/*.properties
+
+SUNWFILES_JAR= SUNWfiles.jar
+SUNWFILES_JARFILES= com/sun/dhcpmgr/client/SUNWfiles/*.class \
+ com/sun/dhcpmgr/client/SUNWfiles/*.properties
+SUNWFILES_MANIFEST= SUNWfiles.manifest
+
+SUNWBINFILES_JAR= SUNWbinfiles.jar
+SUNWBINFILES_JARFILES= com/sun/dhcpmgr/client/SUNWbinfiles/*.class \
+ com/sun/dhcpmgr/client/SUNWbinfiles/*.properties
+SUNWBINFILES_MANIFEST= SUNWbinfiles.manifest
+
+SUNWNISPLUS_JAR= SUNWnisplus.jar
+SUNWNISPLUS_JARFILES= com/sun/dhcpmgr/client/SUNWnisplus/*.class \
+ com/sun/dhcpmgr/client/SUNWnisplus/*.properties
+SUNWNISPLUS_MANIFEST= SUNWnisplus.manifest
+
+CLI_JARS=${CLI_JAR} ${SERVER_JAR} ${COMMON_JAR}
+GUI_JARS=$(GUI_JAR) ${SUNWFILES_JAR} ${SUNWBINFILES_JAR} ${SUNWNISPLUS_JAR}
+ALL_JARS= ${GUI_JARS} ${CLI_JARS}
+
+MANIFEST_FILES= $(SUNWFILES_MANIFEST) $(SUNWBINFILES_MANIFEST) $(SUNWNISPLUS_MANIFEST)
+
+ROOTGUIDIR = $(ROOT)/usr/sadm/admin/dhcpmgr
+ROOTCLIDIR = $(ROOT)/usr/lib/inet/dhcp/svcadm
+ROOTDIRS= $(ROOT)/usr/sadm/admin $(ROOTGUIDIR) $(ROOTCLIDIR)
+
+ROOTCLIFILES = ${CLI_JARS}
+ROOTGUIFILES = ${GUI_JARS}
+ROOTCLIDIRJAR = $(ROOTCLIFILES:%=$(ROOTCLIDIR)/%)
+ROOTGUIDIRJAR = $(ROOTGUIFILES:%=$(ROOTGUIDIR)/%)
+
+JAVADOC_PKGS= com.sun.dhcpmgr.client \
+ com.sun.dhcpmgr.ui \
+ com.sun.dhcpmgr.data \
+ com.sun.dhcpmgr.data.qualifier \
+ com.sun.dhcpmgr.server \
+ com.sun.dhcpmgr.bridge \
+ com.sun.dhcpmgr.cli.common \
+ com.sun.dhcpmgr.cli.dhcpconfig \
+ com.sun.dhcpmgr.cli.dhtadm \
+ com.sun.dhcpmgr.cli.pntadm \
+ com.sun.dhcpmgr.common
+
+FILEMODE = 0444
+DIRMODE = 0755
+
+.KEEP_STATE:
+
+all: $(SUBDIRS) $(ALL_JARS)
+
+install: all $(ROOTDIRS) $(SUBDIRS) $(ROOTCLIDIRJAR) $(ROOTGUIDIRJAR)
+
+clean clobber: $(SUBDIRS)
+ -$(RM) $(MANIFEST_FILES)
+ -$(RM) $(ALL_JARS)
+
+lint: lib
+
+_msg: $(SUBDIRS)
+
+strip:
+
+#
+# The javadocs target is non-standard; used for creating API reference docs
+# The javadocs will be placed in $(CODEMGR_WS}/javadocs
+#
+javadocs:
+ $(RM) -r $(CODEMGR_WS)/$@; mkdir $(CODEMGR_WS)/$@
+ $(JAVADOC) -classpath $(CLASSPATH) -sourcepath . -d $(CODEMGR_WS)/$@ $(JAVADOC_PKGS) -windowtitle "DHCP Administration packages"
+
+$(GUI_JAR): FRC
+ $(JAR) cf $@ $(GUI_JARFILES)
+
+$(SERVER_JAR): FRC
+ $(JAR) cf $@ $(SERVER_JARFILES)
+
+$(CLI_JAR): FRC
+ $(JAR) cf $@ $(CLI_JARFILES)
+
+$(COMMON_JAR): FRC
+ $(JAR) cf $@ $(COMMON_JARFILES)
+
+$(SUNWFILES_JAR): $(SUNWFILES_MANIFEST) FRC
+ $(JAR) cmf $(SUNWFILES_MANIFEST) $@ $(SUNWFILES_JARFILES)
+
+$(SUNWBINFILES_JAR): $(SUNWBINFILES_MANIFEST) FRC
+ $(JAR) cmf $(SUNWBINFILES_MANIFEST) $@ $(SUNWBINFILES_JARFILES)
+
+$(SUNWNISPLUS_JAR): $(SUNWNISPLUS_MANIFEST) FRC
+ $(JAR) cmf $(SUNWNISPLUS_MANIFEST) $@ $(SUNWNISPLUS_JARFILES)
+
+$(SUNWFILES_MANIFEST):
+ @$(ECHO) "Name: com/sun/dhcpmgr/client/SUNWfiles/SUNWfiles.class" > $@;
+ @$(ECHO) "Java-Bean: True" >> $@;
+
+$(SUNWBINFILES_MANIFEST):
+ @$(ECHO) "Name: com/sun/dhcpmgr/client/SUNWbinfiles/SUNWbinfiles.class" > $@;
+ @$(ECHO) "Java-Bean: True" >> $@;
+
+$(SUNWNISPLUS_MANIFEST):
+ @$(ECHO) "Name: com/sun/dhcpmgr/client/SUNWnisplus/SUNWnisplus.class" > $@;
+ @$(ECHO) "Java-Bean: True" >> $@;
+
+$(ROOTCLIDIR)/%: %
+ $(INS.file)
+
+$(ROOTGUIDIR)/%: %
+ $(INS.file)
+
+$(ROOTDIRS):
+ $(INS.dir)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/bin/Makefile b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/bin/Makefile
new file mode 100644
index 0000000000..484b38b599
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/bin/Makefile
@@ -0,0 +1,52 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%W% %E% SMI"
+#
+# Copyright (c) 1999 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+# cmd/cmd-inet/usr.sadm/dhcpmgr/bin/Makefile
+#
+include $(SRC)/cmd/Makefile.cmd
+
+PROG = dhcpmgr
+
+ROOTBIN = $(ROOT)/usr/sadm/admin/bin
+
+ROOTBINPROG = $(PROG:%=$(ROOTBIN)/%)
+
+.KEEP_STATE:
+
+all: $(PROG)
+install: all $(ROOTBIN) $(ROOTBINPROG)
+
+$(ROOTBIN)/%: %
+ $(INS.file)
+
+$(ROOTBIN):
+ $(INS.dir)
+
+clean clobber:
+ -$(RM) $(PROG)
+
+lint _msg:
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/bin/dhcpmgr.sh b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/bin/dhcpmgr.sh
new file mode 100644
index 0000000000..489ddf8f27
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/bin/dhcpmgr.sh
@@ -0,0 +1,44 @@
+#!/bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+DMDIR=/usr/sadm/admin/dhcpmgr
+DLDIR=/usr/lib/inet/dhcp/svcadm
+L10NDIR=/usr/share/lib/locale
+WBEMDIR=/usr/sadm/lib/wbem
+
+CLASSPATH=${L10NDIR}:${DMDIR}/dhcpmgr.jar:${DMDIR}/SUNWfiles.jar:${DMDIR}/SUNWbinfiles.jar:${DMDIR}/SUNWnisplus.jar:${DLDIR}/dhcpsvc.jar:${DLDIR}/dhcpcommon.jar:${WBEMDIR}/providerutility.jar
+export CLASSPATH
+
+LD_LIBRARY_PATH=${WBEMDIR}
+export LD_LIBRARY_PATH
+
+# add /usr/dt/bin so sdtwebclient will be available for help
+PATH=${PATH}:/usr/dt/bin
+export PATH
+
+exec /usr/java/bin/java -DDHCPBEANSDIR=${DMDIR} com.sun.dhcpmgr.client.DhcpmgrApplet
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/Makefile b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/Makefile
new file mode 100644
index 0000000000..6d183ff92a
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/Makefile
@@ -0,0 +1,48 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%W% %E% SMI"
+#
+# Copyright (c) 1999 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+# cmd/cmd-inet/usr.sadm/dhcpmgr/com/Makefile
+
+include $(SRC)/cmd/Makefile.cmd
+
+SUBDIRS = sun
+
+all := TARGET= all
+install := TARGET= install
+clean := TARGET= clean
+clobber := TARGET= clobber
+lint := TARGET= lint
+_msg := TARGET= _msg
+
+.KEEP_STATE:
+
+all install clean clobber lint _msg: $(SUBDIRS)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/Makefile b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/Makefile
new file mode 100644
index 0000000000..541df6ff42
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/Makefile
@@ -0,0 +1,48 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%W% %E% SMI"
+#
+# Copyright (c) 1999 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+# cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/Makefile
+
+include $(SRC)/cmd/Makefile.cmd
+
+SUBDIRS = dhcpmgr
+
+all := TARGET= all
+install := TARGET= install
+clean := TARGET= clean
+clobber := TARGET= clobber
+lint := TARGET= lint
+_msg := TARGET= _msg
+
+.KEEP_STATE:
+
+all install clean clobber lint _msg: $(SUBDIRS)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/Makefile b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/Makefile
new file mode 100644
index 0000000000..dce7d79fc5
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/Makefile
@@ -0,0 +1,48 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 1999-2002 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/Makefile
+
+include $(SRC)/cmd/Makefile.cmd
+
+SUBDIRS = data bridge server client ui cli common
+
+all := TARGET= all
+install := TARGET= install
+clean := TARGET= clean
+clobber := TARGET= clobber
+lint := TARGET= lint
+_msg := TARGET= _msg
+
+.KEEP_STATE:
+
+all install clean clobber lint _msg: $(SUBDIRS)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/Bridge.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/Bridge.java
new file mode 100644
index 0000000000..b725e98212
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/Bridge.java
@@ -0,0 +1,113 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1998-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+package com.sun.dhcpmgr.bridge;
+
+import java.util.Hashtable;
+import com.sun.dhcpmgr.data.*;
+
+/**
+ * Bridge supplies access to the native functions in libdhcp and the
+ * dhcpmgr shared object which actually interact with the data stores
+ * used by DHCP.
+ */
+
+public class Bridge {
+ public native DhcpDatastore getDataStore(String resource)
+ throws BridgeException;
+ public native DhcpDatastore [] getDataStores() throws BridgeException;
+ public native Option [] getInittabOptions(byte context)
+ throws BridgeException;
+ public native Macro getMacro(String key, DhcpDatastore datastore)
+ throws BridgeException;
+ public native Option getOption(String key, DhcpDatastore datastore)
+ throws BridgeException;
+ public native void createDhcptabRecord(DhcptabRecord rec,
+ DhcpDatastore datastore) throws BridgeException;
+ public native void modifyDhcptabRecord(DhcptabRecord oldRecord,
+ DhcptabRecord newRecord, DhcpDatastore datastore)
+ throws BridgeException;
+ public native void deleteDhcptabRecord(DhcptabRecord rec,
+ DhcpDatastore datastore) throws BridgeException;
+ public native void cvtDhcptab(DhcpDatastore datastore)
+ throws BridgeException;
+ public native Option createOption(String name, String value)
+ throws BridgeException;
+ public native Option [] getOptions(DhcpDatastore datastore)
+ throws BridgeException;
+ public native Macro [] getMacros(DhcpDatastore datastore)
+ throws BridgeException;
+ public native Network [] getNetworks(DhcpDatastore datastore)
+ throws BridgeException;
+ public native Network getNetwork(String network)
+ throws BridgeException;
+ public native void cvtNetwork(String table,
+ DhcpDatastore datastore) throws BridgeException;
+ public native DhcpClientRecord [] loadNetwork(String table,
+ DhcpDatastore datastore) throws BridgeException;
+ public native void createDhcpClientRecord(DhcpClientRecord rec,
+ String table, DhcpDatastore datastore) throws BridgeException;
+ public native void modifyDhcpClientRecord(DhcpClientRecord oldRecord,
+ DhcpClientRecord newRecord, String table, DhcpDatastore datastore)
+ throws BridgeException;
+ public native void deleteDhcpClientRecord(DhcpClientRecord rec,
+ String table, DhcpDatastore datastore) throws BridgeException;
+ public native DhcpClientRecord getDhcpClientRecord(DhcpClientRecord rec,
+ String table, DhcpDatastore datastore) throws BridgeException;
+ public native DhcpdOptions readDefaults() throws BridgeException;
+ public native void writeDefaults(DhcpdOptions defs)
+ throws BridgeException;
+ public native void removeDefaults() throws BridgeException;
+ public native void startup() throws BridgeException;
+ public native void shutdown() throws BridgeException;
+ public native void reload() throws BridgeException;
+ public native IPInterface [] getInterfaces() throws BridgeException;
+ public native String [] getArguments(String line) throws BridgeException;
+ public native String getStringOption(short code, String arg)
+ throws BridgeException;
+ public native IPAddress [] getIPOption(short code, String arg)
+ throws BridgeException;
+ public native long [] getNumberOption(short code, String arg)
+ throws BridgeException;
+ public native void createDhcptab(DhcpDatastore datastore)
+ throws BridgeException;
+ public native void deleteDhcptab(DhcpDatastore datastore)
+ throws BridgeException;
+ public native void createDhcpNetwork(String net, DhcpDatastore datastore)
+ throws BridgeException;
+ public native void deleteDhcpNetwork(String net, DhcpDatastore datastore)
+ throws BridgeException;
+ public native void makeLocation(DhcpDatastore datastore)
+ throws BridgeException;
+ public native boolean isServerRunning() throws BridgeException;
+ public native boolean isVersionCurrent() throws BridgeException;
+ static {
+
+ System.load("/usr/sadm/admin/dhcpmgr/dhcpmgr.so.1");
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/BridgeException.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/BridgeException.java
new file mode 100644
index 0000000000..1668247e8a
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/BridgeException.java
@@ -0,0 +1,104 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1998-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+package com.sun.dhcpmgr.bridge;
+
+import java.text.MessageFormat;
+
+/**
+ * This class is the superclass for all exceptions (other than core java
+ * exceptions) thrown by the server and JNI routines.
+ */
+public class BridgeException extends Exception {
+
+ /**
+ * Arguments to use when formatting the message for the exception.
+ */
+ protected Object [] args = null;
+
+ /**
+ * Simplest constructor.
+ */
+ public BridgeException() {
+ super("internal_error");
+ } // constructor
+
+ /**
+ * Constructor that provides a msgid.
+ * @param msgid ResourceBundle id to link to this exception.
+ */
+ public BridgeException(String msgid) {
+ super(msgid);
+ } // constructor
+
+ /**
+ * Constructor that provides a msgid and an argument to the message.
+ * @param msgid ResourceBundle id to link to this exception.
+ * @param args array of arguments to be used in format of message.
+ */
+ public BridgeException(String msgid, Object [] args) {
+ super(msgid);
+ this.args = args;
+ } // constructor
+
+ /**
+ * Constructor that provides a msgid and an argument to the message.
+ * @param msgid ResourceBundle id to link to this exception.
+ * @param arg argument to be used in format of exception message.
+ */
+ public BridgeException(String msgid, String arg) {
+ super(msgid);
+ args = new Object[1];
+ args[0] = arg;
+ } // constructor
+
+ /**
+ * Override of superclass getMessage(). Builds a message using the
+ * msgid and args (if any) that were provided at instantiation.
+ * @return message for the exception.
+ */
+ public String getMessage() {
+ String message = null;
+ String messageId = super.getMessage();
+
+ try {
+ if (args != null) {
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString(messageId));
+ message = form.format(args);
+ } else {
+ message = ResourceStrings.getString(messageId);
+ }
+ } catch (Throwable e) {
+ message = messageId;
+ }
+
+ return (message);
+ } // getMessage
+
+} // BridgeException
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/DsymException.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/DsymException.java
new file mode 100644
index 0000000000..48389131d9
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/DsymException.java
@@ -0,0 +1,45 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 2000-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+package com.sun.dhcpmgr.bridge;
+
+/**
+ * This class is the exception class used by symbol parsing related errors.
+ */
+public class DsymException extends BridgeException {
+
+ /**
+ * Constructor that provides a msgid and an argument to the message.
+ * @param msgid ResourceBundle id to link to this exception.
+ * @param args array of arguments to be used in format of message.
+ */
+ public DsymException(String msgid, Object [] args) {
+ super(msgid, args);
+ } // constructor
+
+} // DsymException
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/ExistsException.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/ExistsException.java
new file mode 100644
index 0000000000..3ff69f3e8c
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/ExistsException.java
@@ -0,0 +1,52 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1999-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.bridge;
+
+/**
+ * The exception that occurs if an object already exists.
+ */
+public class ExistsException extends BridgeException {
+
+ /**
+ * The simplest constructor.
+ * @param entry the name of the object
+ */
+ public ExistsException(String entry) {
+ super("exists_exception", entry);
+ } // constructor
+
+ /**
+ * Constructor used by JNI code.
+ * @param ignored this argument will be ignored
+ * @param args array of arguments to be used in format of message.
+ */
+ public ExistsException(String ignored, Object [] args) {
+ super("exists_exception", args);
+ } // constructor
+
+}// ExistsException
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/HostExistsException.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/HostExistsException.java
new file mode 100644
index 0000000000..1812079b0f
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/HostExistsException.java
@@ -0,0 +1,41 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1999-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.bridge;
+
+/**
+ * The exception that occurs if host already exists.
+ */
+public class HostExistsException extends BridgeException {
+ /**
+ * The one and only constructor
+ * @param host the name of the host
+ */
+ public HostExistsException(String host) {
+ super("host_exists_exception", host);
+ } // constructor
+} // HostExistsException
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/InvalidPathException.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/InvalidPathException.java
new file mode 100644
index 0000000000..b4820b1d58
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/InvalidPathException.java
@@ -0,0 +1,44 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 2000-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.bridge;
+
+/**
+ * The exception that occurs if a data store path is not valid.
+ */
+public class InvalidPathException extends BridgeException {
+
+ /**
+ * Constructor used by JNI code.
+ * @param ignored this argument will be ignored
+ * @param args array of arguments to be used in format of message.
+ */
+ public InvalidPathException(String ignored, Object [] args) {
+ super("invalid_path_exception", args);
+ } // constructor
+
+} // InvalidPathException
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/InvalidRsrcException.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/InvalidRsrcException.java
new file mode 100644
index 0000000000..9476907c75
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/InvalidRsrcException.java
@@ -0,0 +1,44 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 2000-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.bridge;
+
+/**
+ * The exception that occurs if a data store resource is not valid.
+ */
+public class InvalidRsrcException extends BridgeException {
+
+ /**
+ * Constructor used by JNI code.
+ * @param ignored this argument will be ignored
+ * @param args array of arguments to be used in format of message.
+ */
+ public InvalidRsrcException(String ignored, Object [] args) {
+ super("invalid_rsrc_exception", args);
+ } // constructor
+
+} // InvalidRsrcException
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/Makefile b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/Makefile
new file mode 100644
index 0000000000..b1b7821f2d
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/Makefile
@@ -0,0 +1,92 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/Makefile
+#
+
+JNIHDR_FILES = com_sun_dhcpmgr_bridge_Bridge.h
+
+CLASSFILES = Bridge.class \
+ BridgeException.class \
+ HostExistsException.class \
+ NoHostsEntryException.class \
+ NoEntryException.class \
+ ExistsException.class \
+ NoTableException.class \
+ TableExistsException.class \
+ NoDefaultsException.class \
+ NotRunningException.class \
+ InvalidRsrcException.class \
+ InvalidPathException.class \
+ DsymException.class \
+ WordexpException.class \
+ ResourceStrings.class
+
+JNICLASSFILES = Bridge.class
+
+include $(SRC)/lib/Makefile.lib
+
+CLASSPATH= $(SRC)/cmd/cmd-inet/usr.sadm/dhcpmgr
+
+JAVAFILES = $(CLASSFILES:.class=.java)
+
+MSGDIR = $(ROOT)/usr/share/lib/locale/com/sun/dhcpmgr/bridge
+MSGDIRS = $(ROOT)/usr/share/lib/locale \
+ $(ROOT)/usr/share/lib/locale/com \
+ $(ROOT)/usr/share/lib/locale/com/sun \
+ $(ROOT)/usr/share/lib/locale/com/sun/dhcpmgr \
+ $(MSGDIR)
+
+MSGFILES = ResourceBundle.properties
+MSGS = $(MSGFILES:%=$(MSGDIR)/%)
+
+CLEANFILES= *.class $(JNIHDR_FILES)
+CLOBBERFILES=
+
+.KEEP_STATE:
+
+all: $(CLASSFILES) $(JNIHDR_FILES)
+
+install: all
+
+#
+# Build jni header file
+# Use $@ instead of the "unreliable" $*
+#
+$(JNIHDR_FILES): $(JNICLASSFILES)
+ $(JAVAH) -jni -classpath $(CLASSPATH) \
+ `$(ECHO) $@ | sed 's/.h$$//' | tr _ .`
+
+_msg: $(MSGDIRS) $(MSGS)
+
+$(MSGDIR)/%: %
+ $(INS.file)
+
+$(MSGDIRS):
+ $(INS.dir)
+
+include $(SRC)/lib/Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/NoDefaultsException.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/NoDefaultsException.java
new file mode 100644
index 0000000000..e320c6ee62
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/NoDefaultsException.java
@@ -0,0 +1,51 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 2000-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.bridge;
+
+/**
+ * The exception that occurs if no DHCP defaults file exists.
+ */
+public class NoDefaultsException extends BridgeException {
+
+ /**
+ * The basic constructor.
+ */
+ public NoDefaultsException() {
+ super("no_defaults_exception");
+ } // constructor
+
+ /**
+ * Constructor used by JNI code.
+ * @param ignored this argument will be ignored
+ * @param args this argument will be ignored as well.
+ */
+ public NoDefaultsException(String ignored, Object [] args) {
+ this();
+ } // constructor
+
+} // NoDefaultsException
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/NoEntryException.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/NoEntryException.java
new file mode 100644
index 0000000000..9d04b5e8ff
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/NoEntryException.java
@@ -0,0 +1,52 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1999-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.bridge;
+
+/**
+ * The exception that occurs if an object does not exist.
+ */
+public class NoEntryException extends BridgeException {
+
+ /**
+ * The simplest constructor.
+ * @param entry the name of the object
+ */
+ public NoEntryException(String entry) {
+ super("no_entry_exception", entry);
+ } // constructor
+
+ /**
+ * Constructor used by JNI code.
+ * @param ignored this argument will be ignored
+ * @param args array of arguments to be used in format of message.
+ */
+ public NoEntryException(String ignored, Object [] args) {
+ super("no_entry_exception", args);
+ } // constructor
+
+} // NoEntryException
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/NoHostsEntryException.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/NoHostsEntryException.java
new file mode 100644
index 0000000000..a57b7ac827
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/NoHostsEntryException.java
@@ -0,0 +1,41 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1999-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.bridge;
+
+/**
+ * The exception that occurs if a host does not exist.
+ */
+public class NoHostsEntryException extends BridgeException {
+ /**
+ * The one and only constructor
+ * @param host the name of the host
+ */
+ public NoHostsEntryException(String host) {
+ super("no_hosts_entry_exception", host);
+ } // constructor
+} // NoHostsEntryException
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/NoTableException.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/NoTableException.java
new file mode 100644
index 0000000000..5ba7d28ef5
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/NoTableException.java
@@ -0,0 +1,44 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1999-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.bridge;
+
+/**
+ * The exception that occurs if a table does not exist.
+ */
+public class NoTableException extends BridgeException {
+
+ /**
+ * Constructor used by JNI code.
+ * @param ignored this argument will be ignored
+ * @param args array of arguments to be used in format of message.
+ */
+ public NoTableException(String ignored, Object [] args) {
+ super("no_table_exception", args);
+ } // constructor
+
+} // NoTableException
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/NotRunningException.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/NotRunningException.java
new file mode 100644
index 0000000000..d53019b71a
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/NotRunningException.java
@@ -0,0 +1,51 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1999-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.bridge;
+
+/**
+ * The exception that occurs if the DHCP server is not running.
+ */
+public class NotRunningException extends BridgeException {
+
+ /**
+ * The simplest constructor.
+ */
+ public NotRunningException() {
+ super("not_running_exception");
+ } // constructor
+
+ /**
+ * Constructor used by JNI code.
+ * @param ignored this argument will be ignored
+ * @param args this argument will be ignored as well.
+ */
+ public NotRunningException(String ignored, Object [] args) {
+ this();
+ } // constructor
+
+} // NotRunningException
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/ResourceBundle.properties b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/ResourceBundle.properties
new file mode 100644
index 0000000000..5c075abccc
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/ResourceBundle.properties
@@ -0,0 +1,79 @@
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+internal_error=An internal 'bridge' error has occurred.
+
+no_defaults_exception=The DHCP defaults file does not exist or cannot be read.
+invalid_path_exception={0} is not a valid path.
+invalid_rsrc_exception={0} is not a valid resource.
+host_exists_exception=Host {0} already exists in hosts table.
+no_hosts_entry_exception=Host {0} does not exist in hosts table.
+not_running_exception=DHCP server is not running.
+exists_exception={0} already exists.
+table_exists_exception=Table for {0} already exists.
+no_entry_exception={0} does not exist.
+no_table_exception=Table for {0} does not exist.
+inittab_cat_not_found=Requested inittab category does not exist.
+wordexp_exception=wordexp(3c) returned {0}.
+
+dsvc_exists_exception=Already exists.
+dsvc_access_exception=Access denied.
+dsvc_credential_exception=No underlying service credential.
+dsvc_no_ent_exception=Does not exist.
+dsvc_busy_exception=Busy, try again.
+dsvc_invalid_args_exception=Internal error, invalid arguments.
+dsvc_internal_exception=Internal error.
+dsvc_unavailable_exception=Underlying service required by public module is unavailable.
+dsvc_collision_exception=Update collision.
+dsvc_unsupported_exception=Operation unsupported.
+dsvc_no_memory_exception=Virtual memory exhausted.
+dsvc_no_resources_exception=Non-memory resources unavailable
+dsvc_bad_resource_exception=Malformed/missing RESOURCE default.
+dsvc_bad_path_exception=Malformed/missing PATH default.
+dsvc_bad_authtoken_exception=Malformed/missing AUTHTOKEN default.
+dsvc_mod_version_exception=Public module version mismatch.
+dsvc_mod_err_exception=Error in public module.
+dsvc_mod_load_err_exception=Error loading public module.
+dsvc_mod_unload_err_exception=Error unloading public module.
+dsvc_synch_err_exception=Error in synchronization protocol.
+dsvc_no_lockmgr_exception=Cannot contact lock manager.
+dsvc_no_location_exception=Location nonexistent.
+dsvc_bad_conver_exception=Malformed/missing CONVER default.
+dsvc_no_table_exception=Table does not exist.
+dsvc_table_exists_exception=Table already exists.
+
+dsym_too_few_fields_exception={0} option definition does not contain enough fields.
+dsym_too_many_fields_exception={0} option definition contains too many fields.
+dsym_syntax_exception={0} is syntactically incorrect.
+dsym_code_out_of_range_exception={0} option codes must be between {1} and {2}.
+dsym_value_out_of_range_exception=The value {0} is outside the valid range.
+dsym_invalid_cat_exception={0} is not a valid option category.
+dsym_invalid_type_exception={0} is not a valid option type.
+dsym_exceeds_class_size_exception=The option vendor definition contains classes that exceed the maximum of {0} characters.
+dsym_exceeds_max_class_size_exception=The option vendor definition exceeds the maximum of {0} characters.
+dsym_no_memory_exception=Virtual memory exhausted.
+dsym_internal_exception=Internal error occurred while parsing dhcptab options.
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/ResourceStrings.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/ResourceStrings.java
new file mode 100644
index 0000000000..fddc89bc17
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/ResourceStrings.java
@@ -0,0 +1,64 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.bridge;
+
+import java.util.*;
+
+/**
+ * This class provides a static handle to the strings in the "bridge"
+ * ResourceBundle.
+ */
+public class ResourceStrings {
+
+ /**
+ * The handle to the ResourceBundle.
+ */
+ private static ResourceBundle bundle = null;
+
+ /**
+ * This method retrieves a string from the ResourceBundle.
+ * @param key the ResourceBundle key
+ * @return the message from he ResourceBundle
+ */
+ public static String getString(String key) {
+ String msg = null;
+ try {
+ if (bundle == null) {
+ bundle = ResourceBundle.getBundle(
+ "com.sun.dhcpmgr.bridge.ResourceBundle",
+ Locale.getDefault());
+ }
+ msg = bundle.getString(key);
+ } catch (Throwable e) {
+ msg = new String(key);
+ }
+ return msg;
+
+ } // getString
+
+} // ResourceStrings
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/TableExistsException.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/TableExistsException.java
new file mode 100644
index 0000000000..8493939eb1
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/TableExistsException.java
@@ -0,0 +1,44 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1999-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.bridge;
+
+/**
+ * The exception that occurs if a table already exists.
+ */
+public class TableExistsException extends BridgeException {
+
+ /**
+ * Constructor used by JNI code.
+ * @param ignored this argument will be ignored
+ * @param args array of arguments to be used in format of message.
+ */
+ public TableExistsException(String ignored, Object [] args) {
+ super("table_exists_exception", args);
+ } // constructor
+
+} // TableExistsException
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/WordexpException.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/WordexpException.java
new file mode 100644
index 0000000000..7e59601558
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/WordexpException.java
@@ -0,0 +1,45 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+package com.sun.dhcpmgr.bridge;
+
+/**
+ * The exception class used for wordexp(3c) errors.
+ */
+public class WordexpException extends BridgeException {
+
+ /**
+ * Constructor used by JNI code.
+ * @param ignored this argument will be ignored.
+ * @param args array of arguments to be used in format of message.
+ */
+ public WordexpException(String ignored, Object [] args) {
+ super("wordexp_exception", args);
+ } // constructor
+
+} // WordexpException
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/Makefile b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/Makefile
new file mode 100644
index 0000000000..fce34554a0
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/Makefile
@@ -0,0 +1,48 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2001-2002 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/Makefile
+
+include $(SRC)/cmd/Makefile.cmd
+
+SUBDIRS = dhcpconfig dhtadm pntadm dhcpbatch common
+
+all := TARGET= all
+install := TARGET= install
+clean := TARGET= clean
+clobber := TARGET= clobber
+lint := TARGET= lint
+_msg := TARGET= _msg
+
+.KEEP_STATE:
+
+all install clean clobber lint _msg: $(SUBDIRS)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/Console.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/Console.java
new file mode 100644
index 0000000000..6f69ed30e5
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/Console.java
@@ -0,0 +1,108 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+package com.sun.dhcpmgr.cli.common;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.io.IOException;
+
+/**
+ * This class provides convenient methods to read from System.in.
+ */
+public class Console
+{
+ /**
+ * Prompt the user for a yes/no reply and read the reply.
+ * @param prompt the prompt message
+ * @param trueValue the value that represents an affirmative response
+ * @param falseValue the value that represents a negative response
+ * @param defaultReturn indicates whether affirmative or negative is
+ * the default (true means affirmative, false means negative)
+ * @return true if affirmative response, false if negative
+ */
+ public static boolean promptUser(String prompt,
+ String trueValue, String falseValue, boolean defaultReturn) {
+
+ boolean done = false;
+ boolean result = defaultReturn;
+
+ StringBuffer buffer = new StringBuffer(prompt);
+ buffer.append(" (");
+ if (defaultReturn) {
+ buffer.append('[');
+ buffer.append(trueValue);
+ buffer.append("]/");
+ buffer.append(falseValue);
+ } else {
+ buffer.append(trueValue);
+ buffer.append("/[");
+ buffer.append(falseValue);
+ buffer.append(']');
+ }
+ buffer.append("): ");
+
+ while (!done) {
+ System.out.print(buffer.toString());
+ System.out.flush();
+ String line = readLine();
+ if (line == null || line.length() == 0) {
+ done = true;
+ } else if (line.equalsIgnoreCase(trueValue)) {
+ result = true;
+ done = true;
+ } else if (line.equalsIgnoreCase(falseValue)) {
+ result = false;
+ done = true;
+ }
+ }
+
+ return (result);
+
+ } // promptUser
+
+ /**
+ * Read a line from System.in.
+ * @return the line or null in case of exception
+ */
+ public static String readLine() {
+
+ String line = null;
+
+ try {
+ BufferedReader reader =
+ new BufferedReader(new InputStreamReader(System.in));
+
+ line = reader.readLine();
+ } catch (IOException e) {
+ // ignore and return null
+ }
+
+ return (line);
+ } // readLine
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/DhcpCliFunction.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/DhcpCliFunction.java
new file mode 100644
index 0000000000..850506fc27
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/DhcpCliFunction.java
@@ -0,0 +1,269 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.cli.common;
+
+import com.sun.dhcpmgr.data.DhcpDatastore;
+import com.sun.dhcpmgr.data.Option;
+import com.sun.dhcpmgr.data.StandardOptions;
+import com.sun.dhcpmgr.server.DhcpMgr;
+import com.sun.dhcpmgr.server.DhcpMgrImpl;
+import com.sun.dhcpmgr.server.DhcpServiceMgr;
+import com.sun.dhcpmgr.server.DhcpNetMgr;
+import com.sun.dhcpmgr.server.DhcptabMgr;
+import com.sun.dhcpmgr.bridge.*;
+
+import java.lang.IllegalArgumentException;
+
+/**
+ * This class is the base class extended by the DHCP CLI subcommand classes.
+ */
+public abstract class DhcpCliFunction {
+
+ /**
+ * The options for the function.
+ */
+ protected DhcpCliOptions options = null;
+ protected int validOptions[];
+
+ /**
+ * Handles to the managers.
+ */
+ static private DhcpMgr dhcpMgr = null;
+ static private DhcpNetMgr netMgr = null;
+ static private DhcptabMgr dhcptabMgr = null;
+ static private DhcpServiceMgr svcMgr = null;
+
+ /**
+ * The DhcpDatastore to be used for this function. A value of 'null'
+ * means to just use DHCP defaults.
+ */
+ private DhcpDatastore datastore = null;
+
+ /**
+ * Constructor.
+ */
+ public DhcpCliFunction() {
+ dhcpMgr = new DhcpMgrImpl();
+ } // constructor
+
+ /**
+ * Get a handle to the DhcpNetMgr
+ * @return an instance of DhcpNetMgr
+ */
+ public static DhcpMgr getDhcpMgr() {
+ return dhcpMgr;
+ } // getDhcpMgr
+
+ /**
+ * Get a handle to the DhcpNetMgr
+ * @return an instance of DhcpNetMgr
+ */
+ public static DhcpNetMgr getNetMgr() {
+ if (netMgr == null) {
+ netMgr = dhcpMgr.getNetMgr();
+ }
+ return netMgr;
+ } // getNetMgr
+
+ /**
+ * Get a handle to the DhcptabMgr
+ * @return an instance of DhcptabMgr
+ */
+ public static DhcptabMgr getDhcptabMgr() {
+ if (dhcptabMgr == null) {
+ dhcptabMgr = dhcpMgr.getDhcptabMgr();
+ }
+ return dhcptabMgr;
+ } // getDhcptabMgr
+
+ /**
+ * Get a handle to the DhcpServiceMgr
+ * @return an instance of DhcpServiceMgr
+ */
+ public static DhcpServiceMgr getSvcMgr() {
+ if (svcMgr == null) {
+ svcMgr = dhcpMgr.getDhcpServiceMgr();
+ }
+ return svcMgr;
+ } // getSvcMgr
+
+ /**
+ * Used to execute the subcommand functionality.
+ */
+ public abstract int execute()
+ throws IllegalArgumentException;
+
+ /**
+ * Returns the option flag for this function.
+ * @returns the option flag for this function.
+ */
+ public abstract int getFunctionFlag();
+
+ /**
+ * Returns the DhcpDatastore associated with this function.
+ * @returns the DhcpDatastore associated with this function.
+ */
+ public DhcpDatastore getDhcpDatastore() {
+ return datastore;
+ } // getDhcpDatastore
+
+ /**
+ * Sets the DhcpDatastore associated with this function.
+ * @param ds the data store
+ */
+ public void setDhcpDatastore(DhcpDatastore ds) {
+ datastore = ds;
+ } // setDhcpDatastore
+
+ /**
+ * Sets the DhcpDatastore associated with this function.
+ * @param r the data store resource
+ * @param l the data store location
+ * @param a the data store config
+ */
+ public void setDhcpDatastore(String r, String l, String a) {
+ datastore = createDhcpDatastore(r, l, a);
+ } // setDhcpDatastore
+
+ /**
+ * Create a DhcpDatastore with the given argument values as attributes.
+ * @param r the data store resource
+ * @param l the data store location
+ * @param a the data store config
+ * @returns a DhcpDatastore with the given argument values as attributes
+ * or null if all arguments are null.
+ */
+ public DhcpDatastore createDhcpDatastore(String r, String l, String a) {
+ return this.createDhcpDatastore(r, l, a, -1);
+ } // createDhcpDatastore
+
+ /**
+ * Create a DhcpDatastore with the given argument values as attributes.
+ * @param r the data store resource
+ * @param l the data store location
+ * @param a the data store config
+ * @param v the data store resource version
+ * @returns a DhcpDatastore with the given argument values as attributes
+ * or null if all arguments are null.
+ */
+ public DhcpDatastore createDhcpDatastore(String r, String l, String a,
+ int v) {
+
+ DhcpDatastore datastore = null;
+
+ if (r != null || l != null || a != null || v != -1) {
+ datastore = new DhcpDatastore(r, l, a, v);
+ }
+
+ return datastore;
+ } // createDhcpDatastore
+
+ /**
+ * Used to determine whether or not the data store version is valid.
+ */
+ public boolean isVersionValid(boolean ignoreAbsentDefaults) {
+
+ boolean isValid = false;
+ try {
+ isValid = getSvcMgr().isVersionCurrent();
+
+ if (!isValid) {
+ printCmnErrMessage("need_to_convert_datastore");
+ }
+ } catch (NoDefaultsException e) {
+ if (!ignoreAbsentDefaults) {
+ printCmnErrMessage("no_conf_warning");
+ }
+ isValid = true;
+ } catch (Throwable e) {
+ printCmnErrMessage(e.getMessage());
+ }
+
+ return isValid;
+
+ } // isVersionValid
+
+ /**
+ * Used to set the read the STANDARD options from the DHCP inittab and
+ * to initialize the StandardOptions table with these options.
+ */
+ public void setStandardOptions() {
+ try {
+ StandardOptions.setAllOptions(getSvcMgr().getInittabOptions(
+ Option.ctxts[Option.STANDARD].getCode()));
+ } catch (Throwable e) {
+ printCmnErrMessage(e.getMessage());
+ }
+ } // setStandardOptions
+
+ /**
+ * Used to set the options for this function. Validates that the options
+ * received were only options that are legitimate for this subcommand.
+ * @param options the options object built as a result of parsing
+ * command line input.
+ */
+ public void setOptions(DhcpCliOptions options)
+ throws IllegalArgumentException {
+
+ options.validate(validOptions);
+ this.options = options;
+
+ } // setOptions
+
+ /**
+ * Uses the resourceKey to retrieve a string from a ResourceBundle (if
+ * one exists) and prints the string to the console.
+ * @param resourceKey the ResourceBundle key value.
+ */
+ public void printCmnMessage(String resourceKey) {
+ DhcpCliPrint.printMessage(ResourceStrings.getString(resourceKey));
+ } // printMessage
+
+ /**
+ * Uses the resourceKey to retrieve a string from a ResourceBundle (if
+ * one exists) and prints the string to the console error stream.
+ * @param resourceKey the ResourceBundle key value.
+ */
+ public void printCmnErrMessage(String resourceKey) {
+ DhcpCliPrint.printErrMessage(ResourceStrings.getString(resourceKey));
+ } // printErrMessage
+
+ /**
+ * Given a Throwable object, returns a message for it.
+ * @param e the Throwable object.
+ * @returns a message.
+ */
+ public static String getMessage(Throwable e) {
+ String message = e.getMessage();
+ if (message == null) {
+ message = e.toString();
+ }
+ return message;
+ } // getMessage
+
+} // DhcpCliFunction
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/DhcpCliOption.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/DhcpCliOption.java
new file mode 100644
index 0000000000..667cd64d6d
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/DhcpCliOption.java
@@ -0,0 +1,156 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.cli.common;
+
+/**
+ * This class is used to represent one command line option.
+ */
+public class DhcpCliOption {
+
+ /**
+ * The option key.
+ */
+ private int option;
+
+ /**
+ * The value of the option.
+ */
+ private String value;
+
+ /**
+ * Constructor most likely used to create a boolean option.
+ * @param option the option key value
+ */
+ public DhcpCliOption(int option) {
+
+ this(option, null);
+
+ } // constructor
+
+ /**
+ * Constructor most likely used to create a String valued option.
+ * @param option the option key value
+ */
+ public DhcpCliOption(int option, String value) {
+
+ this.option = option;
+ this.value = value;
+
+ } // constructor
+
+ /**
+ * Returns the option key value.
+ * @return the option key value
+ */
+ public int getOption() {
+
+ return option;
+
+ } // getOption
+
+ /**
+ * Set the option key value.
+ * @param option the option key value
+ */
+ public void setOption(int option) {
+
+ this.option = option;
+
+ } // setOption
+
+ /**
+ * Returns the option key value as a Character.
+ * @param option the key value which needs converting.
+ * @return the option key value as a Character.
+ */
+ public static Character getOptionCharacter(int option) {
+
+ return (new Character((char)option));
+
+ } // getOptionCharacter
+
+ /**
+ * Returns the option key value as a Character.
+ * @return the option key value as a Character.
+ */
+ public Character getOptionCharacter() {
+
+ return (getOptionCharacter(option));
+
+ } // getOptionCharacter
+
+ /**
+ * Returns the option value.
+ * @return the option value
+ */
+ public String getValue() {
+
+ return value;
+
+ } // getValue
+
+ /**
+ * Set the option value.
+ * @param option the option value
+ */
+ public void setValue(String value) {
+
+ this.value = value;
+
+ } // setValue
+
+ /**
+ * Compare for equality against another object.
+ * @return true if the object is another DhcpCliOption instance and
+ * they are both have the same key value. The value of 'value' is
+ * irrelevant. This is primarily used by the indexOf method of the
+ * ArrayList used to store the options in DhcpCliOptions.
+ */
+ public boolean equals(Object o) {
+
+ if (o instanceof DhcpCliOption) {
+ DhcpCliOption op = (DhcpCliOption)o;
+ return (option == op.getOption());
+ } else {
+ return false;
+ }
+
+ } // equals
+
+ /**
+ * The obligatory toString() method that returns a string representation
+ * of an object of this class.
+ * @return a string representation of an object of this class.
+ */
+ public String toString() {
+
+ return option + "=" + value;
+
+ } // toString
+
+} // DhcpCliOption
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/DhcpCliOptions.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/DhcpCliOptions.java
new file mode 100644
index 0000000000..f7ae59a93e
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/DhcpCliOptions.java
@@ -0,0 +1,140 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.cli.common;
+
+import java.lang.IllegalArgumentException;
+import java.util.ArrayList;
+import java.text.MessageFormat;
+
+/**
+ * This class is used to represent all the command line options.
+ */
+public class DhcpCliOptions {
+
+ /**
+ * Actual container for the options.
+ */
+ ArrayList options;
+
+ /**
+ * Basic constructor.
+ */
+ public DhcpCliOptions() {
+
+ options = new ArrayList();
+
+ } // constructor
+
+ /**
+ * Creates a new option and adds it to the list.
+ * @param flag the option key value
+ * @param value the option value
+ */
+ public void setOption(int flag, String value)
+ throws IllegalArgumentException {
+
+ DhcpCliOption option = new DhcpCliOption(flag, value);
+ int i = options.indexOf(option);
+ if (i == -1) {
+ options.add(option);
+ } else {
+ String format =
+ ResourceStrings.getString("repeated_option");
+ Object[] args = new Object[1];
+ args[0] = option.getOptionCharacter();
+ String msg = MessageFormat.format(format, args);
+ throw new IllegalArgumentException(msg);
+ }
+
+ } // setOption
+
+ /**
+ * Reports as to whether or not an option exists in the list.
+ * @param flag the option key value
+ * @return true if the option is in the list, false if not
+ */
+ public boolean isSet(int flag) {
+
+ DhcpCliOption option = new DhcpCliOption(flag);
+ return options.contains(option);
+
+ } // isSet
+
+ /**
+ * Returns the value of an option.
+ * @param flag the option key value
+ * @return the value of the option or null if the option is not
+ * in the list.
+ */
+ public String valueOf(int flag) {
+
+ DhcpCliOption option = new DhcpCliOption(flag);
+
+ int i = options.indexOf(option);
+ if (i != -1) {
+ return ((DhcpCliOption)options.get(i)).getValue();
+ } else {
+ return null;
+ }
+ } // valueOf
+
+ /**
+ * Given a list of supported options, validates that there are no
+ * options in the in the option list that are not supported.
+ * @param supportedOptions array of option key values
+ * @return true if there are no options in the option list that are
+ * not in the supportedOptions, false otherwise.
+ */
+ public void validate(int [] supportedOptions)
+ throws IllegalArgumentException {
+
+ boolean result = true;
+ int option = 0;
+
+ for (int i = 0; i < options.size() && result; i++) {
+ option = ((DhcpCliOption)options.get(i)).getOption();
+ result = false;
+ for (int j = 0; j < supportedOptions.length && !result; j++) {
+ if (supportedOptions[j] == option) {
+ result = true;
+ }
+ }
+ }
+
+ if (!result) {
+ String format =
+ ResourceStrings.getString("bad_function_option");
+ Object[] args = new Object[1];
+ args[0] = DhcpCliOption.getOptionCharacter(option);
+ String msg = MessageFormat.format(format, args);
+ throw new IllegalArgumentException(msg);
+ }
+
+ } // validate
+
+} // DhcpCliOptions
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/DhcpCliPrint.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/DhcpCliPrint.java
new file mode 100644
index 0000000000..c0a4ae8735
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/DhcpCliPrint.java
@@ -0,0 +1,52 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.cli.common;
+
+/**
+ * This class provides methods that may be used to retrieve strings from
+ * ResourceBundles and print resulting messages to the console.
+ */
+public class DhcpCliPrint {
+
+ /**
+ * Prints a message to standard output stream.
+ * @param msg the message to print.
+ */
+ public static void printMessage(String msg) {
+ System.out.println(msg);
+ } // printMessage
+
+ /**
+ * Prints a message to the standard error stream.
+ * @param msg the message to print.
+ */
+ public static void printErrMessage(String msg) {
+ System.err.println(msg);
+ } // printErrMessage
+
+} // DhcpCliPrint
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/DhcpCliProgram.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/DhcpCliProgram.java
new file mode 100644
index 0000000000..f266eee112
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/DhcpCliProgram.java
@@ -0,0 +1,114 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.cli.common;
+
+import java.text.MessageFormat;
+
+/**
+ * This class is the base class extended by the DHCP CLI program classes.
+ */
+public abstract class DhcpCliProgram {
+
+ /**
+ * Defines the functionality to be performed.
+ */
+ protected DhcpCliFunction function = null;
+ protected DhcpCliOptions options = null;
+
+ /**
+ * Program arguments.
+ */
+ protected String [] args = null;
+
+ /**
+ * Return codes.
+ */
+ public static final int FAILURE = -1;
+ public static final int SUCCESS = 0;
+ public static final int EXISTS = 1;
+ public static final int ENOENT = 2;
+ public static final int WARNING = 3;
+ public static final int CRITICAL = 4;
+
+ /**
+ * Returns the manpage signature for the program.
+ * @return the manpage signature for the program.
+ */
+ public abstract String getManPage();
+
+ /**
+ * Uninitializes the program function.
+ */
+ protected void clearFunction() {
+ function = null;
+ }
+
+ /**
+ * Sets the function that the user has requested.
+ * @param function user requested function.
+ */
+ protected void setFunction(DhcpCliFunction function)
+ throws IllegalArgumentException {
+
+ if (this.function != null) {
+ Object [] args = new Object[2];
+ args[0] = DhcpCliOption.getOptionCharacter(
+ this.function.getFunctionFlag());
+ args[1] = DhcpCliOption.getOptionCharacter(
+ function.getFunctionFlag());
+ String msg = ResourceStrings.getString("multiple_functions");
+ MessageFormat form = new MessageFormat(msg);
+ throw new IllegalArgumentException(form.format(args));
+ }
+
+ this.function = function;
+ }
+
+ /**
+ * Checks to see if the user has permission to run the program.
+ * @return true if the user has permission to execute, false otherwise.
+ */
+ protected boolean isValidUser() {
+
+ // Must be root to run.
+ //
+ if (!System.getProperty("user.name").equals("root")) {
+
+ Object [] args = new Object[1];
+ args[0] = getManPage();
+ String msg = ResourceStrings.getString("user_not_allowed");
+ MessageFormat form = new MessageFormat(msg);
+ DhcpCliPrint.printErrMessage(form.format(args));
+
+ return false;
+ }
+ return true;
+
+ } // isValidUser
+
+} // DhcpCliProgram
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/Format.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/Format.java
new file mode 100644
index 0000000000..f53c12cb24
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/Format.java
@@ -0,0 +1,608 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+/*
+ * Cay S. Horstmann & Gary Cornell, Core Java
+ * Published By Sun Microsystems Press/Prentice-Hall
+ * Copyright (C) 1997 Sun Microsystems Inc.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this
+ * software and its documentation for NON-COMMERCIAL purposes
+ * and without fee is hereby granted provided that this
+ * copyright notice appears in all copies.
+ *
+ * THE AUTHORS AND PUBLISHER MAKE NO REPRESENTATIONS OR
+ * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, EITHER
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. THE AUTHORS
+ * AND PUBLISHER SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED
+ * BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
+ * THIS SOFTWARE OR ITS DERIVATIVES.
+ */
+
+/**
+ * A class for formatting numbers that follows printf conventions.
+ * Also implements C-like atoi and atof functions
+ * @version 1.01 15 Feb 1996
+ * @author Cay Horstmann
+ */
+
+package com.sun.dhcpmgr.cli.common;
+
+import java.io.*;
+
+public class Format
+
+{
+ /**
+ * Formats the number following printf conventions.
+ * Main limitation: Can only handle one format parameter at a time
+ * Use multiple Format objects to format more than one number
+ * @param s the format string following printf conventions
+ * The string has a prefix, a format code and a suffix. The prefix and
+ * suffix become part of the formatted output. The format code directs the
+ * formatting of the (single) parameter to be formatted. The code has the
+ * following structure
+ * <ul>
+ * <li> a % (required)
+ * <li> a modifier (optional)
+ * <dl>
+ * <dt> + <dd> forces display of + for positive numbers
+ * <dt> 0 <dd> show leading zeroes
+ * <dt> - <dd> align left in the field
+ * <dt> space <dd> prepend a space in front of positive numbers
+ * <dt> # <dd> use "alternate" format. Add 0 or 0x for octal or hexadecimal
+ * numbers. Don't suppress trailing zeroes in general floating point format.
+ * </dl>
+ * <li> an integer denoting field width (optional)
+ * <li> a period followed by an integer denoting precision (optional)
+ * <li> a format descriptor (required)
+ * <dl>
+ * <dt>f <dd> floating point number in fixed format
+ * <dt>e, E <dd> floating point number in exponential notation (scientific
+ * format). The E format results in an uppercase E for the exponent
+ * (1.14130E+003), the e format in a lowercase e.
+ * <dt>g, G <dd> floating point number in general format (fixed format for
+ * small numbers, exponential format for large numbers). Trailing zeroes
+ * are suppressed. The G format results in an uppercase E for the exponent
+ * (if any), the g format in a lowercase e.
+ * <dt>d, i <dd> integer in decimal
+ * <dt>x <dd> integer in hexadecimal
+ * <dt>o <dd> integer in octal
+ * <dt>s <dd> string
+ * <dt>c <dd> character
+ * </dl>
+ * </ul>
+ * @exception IllegalArgumentException if bad format
+ */
+ public Format(String s) {
+ width = 0;
+ precision = -1;
+ pre = "";
+ post = "";
+ leading_zeroes = false;
+ show_plus = false;
+ alternate = false;
+ show_space = false;
+ left_align = false;
+ fmt = ' ';
+
+ int state = 0;
+ int length = s.length();
+ int parse_state = 0;
+ // 0 = prefix, 1 = flags, 2 = width, 3 = precision,
+ // 4 = format, 5 = end
+ int i = 0;
+
+ while (parse_state == 0) {
+ if (i >= length) {
+ parse_state = 5;
+ } else if (s.charAt(i) == '%') {
+ if (i < length - 1) {
+ if (s.charAt(i + 1) == '%') {
+ pre = pre + '%';
+ i++;
+ } else {
+ parse_state = 1;
+ }
+ } else {
+ throw new java.lang.IllegalArgumentException();
+ }
+ } else {
+ pre = pre + s.charAt(i);
+ }
+ i++;
+ }
+
+ while (parse_state == 1) {
+ if (i >= length) {
+ parse_state = 5;
+ } else if (s.charAt(i) == ' ') {
+ show_space = true;
+ } else if (s.charAt(i) == '-') {
+ left_align = true;
+ } else if (s.charAt(i) == '+') {
+ show_plus = true;
+ } else if (s.charAt(i) == '0') {
+ leading_zeroes = true;
+ } else if (s.charAt(i) == '#') {
+ alternate = true;
+ } else {
+ parse_state = 2; i--;
+ }
+ i++;
+ }
+
+ while (parse_state == 2) {
+ if (i >= length) {
+ parse_state = 5;
+ } else if ('0' <= s.charAt(i) && s.charAt(i) <= '9') {
+ width = width * 10 + s.charAt(i) - '0';
+ i++;
+ } else if (s.charAt(i) == '.') {
+ parse_state = 3;
+ precision = 0;
+ i++;
+ } else {
+ parse_state = 4;
+ }
+ }
+
+ while (parse_state == 3) {
+ if (i >= length) {
+ parse_state = 5;
+ } else if ('0' <= s.charAt(i) && s.charAt(i) <= '9') {
+ precision = precision * 10 + s.charAt(i) - '0';
+ i++;
+ } else {
+ parse_state = 4;
+ }
+ }
+
+ if (parse_state == 4) {
+ if (i >= length) {
+ parse_state = 5;
+ } else {
+ fmt = s.charAt(i);
+ }
+ i++;
+ }
+
+ if (i < length) {
+ post = s.substring(i, length);
+ }
+ }
+
+ /**
+ * prints a formatted number following printf conventions
+ * @param s a PrintStream
+ * @param fmt the format string
+ * @param x the double to print
+ */
+ public static void print(java.io.PrintStream s, String fmt, double x) {
+ s.print(new Format(fmt).form(x));
+ }
+
+ /**
+ * prints a formatted number following printf conventions
+ * @param s a PrintStream
+ * @param fmt the format string
+ * @param x the long to print
+ */
+ public static void print(java.io.PrintStream s, String fmt, long x) {
+ s.print(new Format(fmt).form(x));
+ }
+
+ /**
+ * prints a formatted number following printf conventions
+ * @param s a PrintStream
+ * @param fmt the format string
+ * @param x the character to
+ */
+ public static void print(java.io.PrintStream s, String fmt, char x) {
+ s.print(new Format(fmt).form(x));
+ }
+
+ /**
+ * prints a formatted number following printf conventions
+ * @param s a PrintStream, fmt the format string
+ * @param x a string that represents the digits to print
+ */
+ public static void print(java.io.PrintStream s, String fmt, String x) {
+ s.print(new Format(fmt).form(x));
+ }
+
+ /**
+ * Converts a string of digits(decimal, octal or hex) to an integer
+ * @param s a string
+ * @return the numeric value of the prefix of s representing a base
+ * 10 integer
+ */
+ public static int atoi(String s) {
+ return (int)atol(s);
+ }
+
+ /**
+ * Converts a string of digits(decimal, octal or hex) to a long integer
+ * @param s a string
+ * @return the numeric value of the prefix of s representing a base
+ * 10 integer
+ */
+ public static long atol(String s) {
+ int i = 0;
+
+ while (i < s.length() && Character.isWhitespace(s.charAt(i))) {
+ i++;
+ }
+
+ if (i < s.length() && s.charAt(i) == '0') {
+ if (i + 1 < s.length() && (s.charAt(i + 1) == 'x' ||
+ s.charAt(i + 1) == 'X')) {
+ return parseLong(s.substring(i + 2), 16);
+ } else {
+ return parseLong(s, 8);
+ }
+ } else {
+ return parseLong(s, 10);
+ }
+ }
+
+ private static long parseLong(String s, int base) {
+ int i = 0;
+ int sign = 1;
+ long r = 0;
+
+ while (i < s.length() && Character.isWhitespace(s.charAt(i))) {
+ i++;
+ }
+
+ if (i < s.length() && s.charAt(i) == '-') {
+ sign = -1; i++;
+ } else if (i < s.length() && s.charAt(i) == '+') {
+ i++;
+ }
+
+ while (i < s.length()) {
+ char ch = s.charAt(i);
+ if ('0' <= ch && ch < '0' + base) {
+ r = r * base + ch - '0';
+ } else if ('A' <= ch && ch < 'A' + base - 10) {
+ r = r * base + ch - 'A' + 10;
+ } else if ('a' <= ch && ch < 'a' + base - 10) {
+ r = r * base + ch - 'a' + 10;
+ } else {
+ return r * sign;
+ }
+ i++;
+ }
+ return r * sign;
+ }
+
+ /**
+ * Converts a string of digits to an double
+ * @param s a string
+ */
+ public static double atof(String s) {
+ int i = 0;
+ int sign = 1;
+ double r = 0; // integer part
+ double f = 0; // fractional part
+ double p = 1; // exponent of fractional part
+ int state = 0; // 0 = int part, 1 = frac part
+
+ while (i < s.length() && Character.isWhitespace(s.charAt(i))) {
+ i++;
+ }
+
+ if (i < s.length() && s.charAt(i) == '-') {
+ sign = -1; i++;
+ } else if (i < s.length() && s.charAt(i) == '+') {
+ i++;
+ }
+
+ while (i < s.length()) {
+ char ch = s.charAt(i);
+ if ('0' <= ch && ch <= '9') {
+ if (state == 0) {
+ r = r * 10 + ch - '0';
+ } else if (state == 1) {
+ p = p / 10;
+ r = r + p * (ch - '0');
+ }
+ } else if (ch == '.') {
+ if (state == 0) {
+ state = 1;
+ } else {
+ return sign * r;
+ }
+ } else if (ch == 'e' || ch == 'E') {
+ long e = (int)parseLong(s.substring(i + 1), 10);
+ return sign * r * Math.pow(10, e);
+ } else {
+ return sign * r;
+ }
+ i++;
+ }
+ return sign * r;
+ }
+
+ /**
+ * Formats a double into a string (like sprintf in C)
+ * @param x the number to format
+ * @return the formatted string
+ * @exception IllegalArgumentException if bad argument
+ */
+ public String form(double x) {
+ String r;
+ if (precision < 0) {
+ precision = 6;
+ }
+
+ int s = 1;
+ if (x < 0) {
+ x = -x;
+ s = -1;
+ }
+
+ if (fmt == 'f') {
+ r = fixed_format(x);
+ } else if (fmt == 'e' || fmt == 'E' || fmt == 'g' || fmt == 'G') {
+ r = exp_format(x);
+ } else {
+ throw new java.lang.IllegalArgumentException();
+ }
+
+ return pad(sign(s, r));
+ }
+
+ /**
+ * Formats a long integer into a string (like sprintf in C)
+ * @param x the number to format
+ * @return the formatted string
+ */
+ public String form(long x) {
+ String r;
+ int s = 0;
+ if (fmt == 'd' || fmt == 'i') {
+ s = 1;
+ if (x < 0) {
+ x = -x; s = -1;
+ }
+ r = "" + x;
+ } else if (fmt == 'o') {
+ r = convert(x, 3, 7, "01234567");
+ } else if (fmt == 'x') {
+ r = convert(x, 4, 15, "0123456789abcdef");
+ } else if (fmt == 'X') {
+ r = convert(x, 4, 15, "0123456789ABCDEF");
+ } else {
+ throw new java.lang.IllegalArgumentException();
+ }
+ return pad(sign(s, r));
+ }
+
+ /**
+ * Formats a character into a string (like sprintf in C)
+ * @param x the value to format
+ * @return the formatted string
+ */
+ public String form(char c) {
+ if (fmt != 'c') {
+ throw new java.lang.IllegalArgumentException();
+ }
+ String r = "" + c;
+ return pad(r);
+ }
+
+ /**
+ * Formats a string into a larger string (like sprintf in C)
+ * @param x the value to format
+ * @return the formatted string
+ */
+ public String form(String s) {
+ if (fmt != 's') {
+ throw new java.lang.IllegalArgumentException();
+ }
+
+ if (precision >= 0) {
+ s = s.substring(0, precision);
+ }
+ return pad(s);
+ }
+
+ private static String repeat(char c, int n) {
+ if (n <= 0) {
+ return "";
+ }
+ StringBuffer s = new StringBuffer(n);
+ for (int i = 0; i < n; i++) {
+ s.append(c);
+ }
+ return s.toString();
+ }
+
+ private static String convert(long x, int n, int m, String d) {
+ if (x == 0) {
+ return "0";
+ }
+ String r = "";
+ while (x != 0) {
+ r = d.charAt((int)(x & m)) + r;
+ x = x >>> n;
+ }
+ return r;
+ }
+
+ private String pad(String r) {
+ String p = repeat(' ', width - r.length());
+ if (left_align) {
+ return pre + r + p + post;
+ } else {
+ return pre + p + r + post;
+ }
+ }
+
+ private String sign(int s, String r) {
+ String p = "";
+ if (s < 0) {
+ p = "-";
+ } else if (s > 0) {
+ if (show_plus) {
+ p = "+";
+ } else if (show_space) {
+ p = " ";
+ }
+ } else {
+ if (fmt == 'o' && alternate && r.length() > 0 &&
+ r.charAt(0) != '0') {
+ p = "0";
+ } else if (fmt == 'x' && alternate) {
+ p = "0x";
+ } else if (fmt == 'X' && alternate) {
+ p = "0X";
+ }
+ }
+
+ int w = 0;
+ if (leading_zeroes) {
+ w = width;
+ } else if ((fmt == 'd' || fmt == 'i' || fmt == 'x' ||
+ fmt == 'X' || fmt == 'o') && precision > 0) {
+ w = precision;
+ }
+
+ return p + repeat('0', w - p.length() - r.length()) + r;
+ }
+
+
+ private String fixed_format(double d) {
+ String f = "";
+
+ if (d > 0x7FFFFFFFFFFFFFFFL) {
+ return exp_format(d);
+ }
+
+ long l = (long)(precision == 0 ? d + 0.5 : d);
+ f = f + l;
+
+ double fr = d - l; // fractional part
+ if (fr >= 1 || fr < 0) {
+ return exp_format(d);
+ }
+
+ return f + frac_part(fr);
+ }
+
+ private String frac_part(double fr) {
+ // precondition: 0 <= fr < 1
+ String z = "";
+ if (precision > 0) {
+ double factor = 1;
+ String leading_zeroes = "";
+ for (int i = 1; i <= precision && factor <= 0x7FFFFFFFFFFFFFFFL;
+ i++) {
+ factor *= 10;
+ leading_zeroes = leading_zeroes + "0";
+ }
+ long l = (long) (factor * fr + 0.5);
+
+ z = leading_zeroes + l;
+ z = z.substring(z.length() - precision, z.length());
+ }
+
+
+ if (precision > 0 || alternate) {
+ z = "." + z;
+ }
+
+ if ((fmt == 'G' || fmt == 'g') && !alternate) {
+ // remove trailing zeroes and decimal point
+ int t = z.length() - 1;
+ while (t >= 0 && z.charAt(t) == '0') {
+ t--;
+ }
+ if (t >= 0 && z.charAt(t) == '.') {
+ t--;
+ }
+ z = z.substring(0, t + 1);
+ }
+ return z;
+ }
+
+ private String exp_format(double d) {
+ String f = "";
+ int e = 0;
+ double dd = d;
+ double factor = 1;
+
+ while (dd > 10) {
+ e++; factor /= 10; dd = dd / 10;
+ }
+
+ while (dd < 1) {
+ e--; factor *= 10; dd = dd * 10;
+ }
+
+ if ((fmt == 'g' || fmt == 'G') && e >= -4 && e < precision) {
+ return fixed_format(d);
+ }
+
+ d = d * factor;
+ f = f + fixed_format(d);
+
+ if (fmt == 'e' || fmt == 'g') {
+ f = f + "e";
+ } else {
+ f = f + "E";
+ }
+
+ String p = "000";
+ if (e >= 0) {
+ f = f + "+";
+ p = p + e;
+ } else {
+ f = f + "-";
+ p = p + (-e);
+ }
+
+ return f + p.substring(p.length() - 3, p.length());
+ }
+
+ private int width;
+ private int precision;
+ private String pre;
+ private String post;
+ private boolean leading_zeroes;
+ private boolean show_plus;
+ private boolean alternate;
+ private boolean show_space;
+ private boolean left_align;
+ private char fmt; // one of cdeEfgGiosxXos
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/GetOpt.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/GetOpt.java
new file mode 100644
index 0000000000..4aae63cd4a
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/GetOpt.java
@@ -0,0 +1,244 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 1998-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.cli.common;
+
+import java.lang.IllegalArgumentException;
+import java.text.MessageFormat;
+
+/**
+ * This class provides the functionality for parsing command line
+ * arguments (similar to getopt(3C)).
+ * After constructing an instance of it, getNextOption() can be used
+ * to get the next option. getOptionArg() can be used to get the argument for
+ * that option. getNextOptionIndex() returns how many arguments are already
+ * processed from the arguments list.
+ *
+ * This code was "borrowed" from the Viper project. We have our own copy of
+ * this code, because the Viper project has deprecated their class and we
+ * have had to modify the original code.
+ */
+public class GetOpt {
+ protected int optind = 0;
+ protected String optarg = null;
+ protected String argv[] = null;
+ protected int argc = 0;
+ protected String optionString = null;
+
+ // e.g in -v (- is in 0th position)
+ //
+ int MINUS_POSITION = 0;
+
+ // e.g. in -v (v is in 1st position )
+ //
+ int OPTION_POSITION = 1;
+
+ // e.g. in -vGoldrush (G is in 2nd position )
+ //
+ int AFTER_OPTION_POSITION = 2;
+
+ /**
+ * Constructor
+ * @parameter argv -- Array of string arguments.
+ * @parameter optionString -- contains the option letters that
+ * will be recognized;
+ * if a letter is followed by a colon,
+ * the option is expected to have an argument.
+ * if a letter is followed by a semi-colon,
+ * the argument to the letter is optional.
+ * e.g. abdf:e
+ * legal arguments are a, b, d, f, e.
+ * f option requires a argument.
+ */
+ public GetOpt(String argv[], String optionString) {
+ this.argv = argv;
+ this.optionString = optionString;
+ this.argc = argv.length;
+ }
+
+ /*
+ * Returns the next valid option.
+ * Throws an IllegalArgumentException
+ * a) if option is not valid or
+ * b) an option required an argument and is not provided
+ * Returns -1 if no more options left.
+ */
+ public int getNextOption() throws IllegalArgumentException {
+ char currentOption;
+ optarg = null;
+
+ // ------------------------------------------------
+ // Find out if option exists
+
+ if (optind >= argc || (argv[optind].length() < 2) ||
+ argv[optind].charAt(MINUS_POSITION) != '-') {
+
+ return -1;
+ }
+
+ // ---------------------------------------------------
+ // So see if it is a legal option
+
+ currentOption = argv[optind].charAt(OPTION_POSITION);
+
+ if (!isValidOption(currentOption)) {
+ optind = optind + 1;
+ String format = ResourceStrings.getString("getopt_illegal_option");
+ Object[] args = new Object[1];
+ args[0] = new Character((char)currentOption);
+ String msg = MessageFormat.format(format, args);
+ throw new IllegalArgumentException(msg);
+ }
+
+ // ------------------------------------------------------------
+ // We have a legal option now, find out if it expected to have optarg.
+
+ if (isOptionArgAllowedByOption(currentOption) &&
+ OPTION_POSITION == 1) {
+
+ // -------------------------------------
+ // Case when optarg is given with the option itself,
+ // like -hlastgas.east. Then extract the optarg out.
+
+ if (argv[optind].length() != 2) {
+ optarg = argv[optind].substring(AFTER_OPTION_POSITION);
+ optind++;
+ }
+ // ------------------------------------------
+ // Case when optarg is not provided, return error if it was
+ // mandatory
+
+ else if (optind+1 >= argc) {
+ optind++;
+ if (isOptionArgMandatoryByOption(currentOption)) {
+ String format =
+ ResourceStrings.getString("getopt_requires_argument");
+ Object[] args = new Object[1];
+ args[0] = new Character((char)currentOption);
+ String msg = MessageFormat.format(format, args);
+ throw new IllegalArgumentException(msg);
+ }
+ }
+ // ------------------------------------------------
+ // Case when there is a argument that could have been used
+ // as a optarg, but actually it is just another option.
+
+ else if ((argv[optind+1].length() > MINUS_POSITION) &&
+ (argv[optind+1].charAt(MINUS_POSITION) == '-') &&
+ (isValidOption(argv[optind+1].charAt(OPTION_POSITION)))) {
+ optind++;
+ if (isOptionArgMandatoryByOption(currentOption)) {
+ String format =
+ ResourceStrings.getString("getopt_requires_argument");
+ Object[] args = new Object[1];
+ args[0] = new Character((char)currentOption);
+ String msg = MessageFormat.format(format, args);
+ throw new IllegalArgumentException(msg);
+ }
+ }
+
+ // --------------------------------------------
+ // Finally the good case
+
+ else {
+ optarg = argv[++optind];
+ optind++;
+ }
+
+ OPTION_POSITION = 1;
+
+ } else if (isOptionArgMandatoryByOption(currentOption)) {
+ String format = ResourceStrings.getString("getopt_cannot_group");
+ Object[] args = new Object[1];
+ args[0] = new Character((char)currentOption);
+ String msg = MessageFormat.format(format, args);
+ throw new IllegalArgumentException(msg);
+ } else if (argv[optind].length() == OPTION_POSITION + 1) {
+ OPTION_POSITION = 1;
+ optind++;
+ } else { // illegal argument supplied for option
+ OPTION_POSITION++;
+ }
+ return currentOption;
+ }
+
+ /**
+ * Returns the argument for the option being handled.
+ */
+ public String getOptionArg() {
+ return optarg;
+ }
+
+ /**
+ * Returns true if option is a valid option
+ */
+ private boolean isValidOption(char c) {
+ if ((c == ':') || (optionString.indexOf(c) == -1)) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ * Returns true if option provided needs a argument.
+ * throws exception if option is not a valid option at first place.
+ */
+ private boolean isOptionArgMandatoryByOption(char option) {
+ int x = option;
+ if (isValidOption(option)
+ && (optionString.length() > optionString.indexOf(option) + 1)
+ && (optionString.charAt(optionString.indexOf(option) + 1) == ':'))
+ return true;
+ else
+ return false;
+ }
+
+ /**
+ * Returns how many arguments are already processed by the getNextOption()
+ * function. The other way to look at it is what argument is going to be
+ * processed by getNextOption() method next.
+ */
+ public int getNextOptionIndex() {
+ return optind;
+ }
+
+ /**
+ * Returns true if option provided allows a argument.
+ * throws exception if option is not a valid option at first place.
+ */
+ private boolean isOptionArgAllowedByOption(char option) {
+ int x = option;
+ if (isValidOption(option)
+ && (optionString.length() > optionString.indexOf(option) + 1)
+ && ((optionString.charAt(optionString.indexOf(option) + 1) == ':') ||
+ (optionString.charAt(optionString.indexOf(option) + 1) == ';')))
+ return true;
+ else
+ return false;
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/GetSubOpt.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/GetSubOpt.java
new file mode 100644
index 0000000000..7f634f45fd
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/GetSubOpt.java
@@ -0,0 +1,249 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 1998-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.cli.common;
+
+import java.lang.IllegalArgumentException;
+import java.text.MessageFormat;
+
+/**
+ * This class provides the functionality for parsing command line
+ * arguments (similar to getsubopt(3C)).
+ * <br>
+ * After constructing an instance of it, getNextSubOption() can be used
+ * to get the next suboption. getSubOptionArg() can be used to get the argument
+ * for that option.
+ *
+ */
+public class GetSubOpt {
+
+ /**
+ * Delimiter between suboptions.
+ */
+ public final static String OPTION_DELIM = ",";
+
+ /**
+ * Delimiter between suboption and suboption argument.
+ */
+ public final static String VALUE_DELIM = "=";
+
+ /**
+ * List of suboptions.
+ */
+ protected String subOptions;
+
+ /**
+ * The length of the suboptions.
+ */
+ private final int subOptionsLen;
+
+ /**
+ * Current position with in suboptions.
+ */
+ protected int index;
+
+ /**
+ * Last suboption found.
+ */
+ protected String subOption;
+
+ /**
+ * Last suboption argument found.
+ */
+ protected String value;
+
+ /**
+ * Prepare a GetSubOpt object.
+ *
+ * @param subOptions
+ * String containing the list of suboptions. This value was most likely
+ * returned as an option argument by <i>GetOpt</i>.
+ */
+ public GetSubOpt(String subOptions) {
+ if (subOptions == null) {
+ this.subOptions = null;
+ } else {
+ this.subOptions = subOptions.trim();
+ }
+
+ index = 0;
+ subOptionsLen = subOptions.length();
+ }
+
+ /**
+ * Get the next suboption. The suboptions arguement is available from
+ * calling <i>getSubOptionArg()</i>.
+ *
+ * @return
+ * The next suboption.
+ * @exception IllegalArgumentException
+ * Thrown when no more suboptions remain.
+ */
+ public String getNextSubOption() throws IllegalArgumentException {
+
+ subOption = null;
+ value = null;
+
+ while (subOption == null || subOption.length() == 0) {
+
+ if (!hasMoreSubOptions()) {
+ String format =
+ ResourceStrings.getString("getsubopt_end_of_optionarg");
+ Object[] args = new Object[0];
+ String msg = MessageFormat.format(format, args);
+ throw new IllegalArgumentException(msg);
+ }
+
+ int optionIndex = subOptions.indexOf(OPTION_DELIM, index);
+ int valueIndex = subOptions.indexOf(VALUE_DELIM, index);
+
+ if (optionIndex == -1 && valueIndex == -1) {
+ // Last suboption and no value.
+ subOption = subOptions.substring(index);
+ index = subOptionsLen;
+ } else if (valueIndex == -1 ||
+ (optionIndex != -1 && optionIndex < valueIndex)) {
+ // Suboption has no value.
+ subOption = subOptions.substring(index, optionIndex);
+ index = optionIndex + OPTION_DELIM.length();
+ } else {
+ // Suboption with value.
+ subOption = subOptions.substring(index, valueIndex);
+ index = valueIndex + VALUE_DELIM.length();
+
+ boolean quoted = false;
+ int endIndex;
+
+ if (index < subOptionsLen &&
+ (subOptions.charAt(index) == '\"' ||
+ subOptions.charAt(index) == '\'')) {
+ // Value is quoted.
+ endIndex =
+ subOptions.indexOf(subOptions.charAt(index), index + 1);
+
+ // Missing close quote.
+ if (endIndex == -1) {
+ String format =
+ ResourceStrings.getString(
+ "getsubopt_missing_close_quote");
+ Object[] args = new Object[1];
+ args[0] = subOption;
+ String msg = MessageFormat.format(format, args);
+ throw new IllegalArgumentException(msg);
+ }
+
+ quoted = true;
+ index++;
+ } else {
+ // Value is not quoted.
+ endIndex = subOptions.indexOf(OPTION_DELIM, index);
+
+ if (endIndex == -1) {
+ endIndex = subOptionsLen;
+ }
+ }
+
+ value = subOptions.substring(index, endIndex);
+ index = endIndex;
+
+ // Skip closing quote.
+ if (quoted) {
+ index++;
+ }
+
+ /*
+ * Ensure that either the end of the suboptions has been
+ * reached or the next suboption is ready for parsing. For
+ * example, quoted values must not contain characters between
+ * the closing quote and OPTION_DELIM.
+ */
+ if (optionIndex >= 0) {
+ if (index < subOptionsLen &&
+ !subOptions.startsWith(OPTION_DELIM, index)) {
+ String format =
+ ResourceStrings.getString(
+ "getsubopt_malformed_value");
+ Object[] args = new Object[1];
+ args[0] = subOption;
+ String msg = MessageFormat.format(format, args);
+ throw new IllegalArgumentException(msg);
+ }
+
+ index += OPTION_DELIM.length();
+ }
+ }
+ }
+
+ return subOption;
+ }
+
+ /**
+ * Indicates whether more suboptions exist.
+ *
+ * @return
+ * True if at least one more suboption exists, otherwise false.
+ */
+ public boolean hasMoreSubOptions() throws IllegalArgumentException {
+ if (subOptions == null) {
+ return false;
+ }
+
+ // Skip over leading OPTION_DELIMs.
+ while (index < subOptionsLen &&
+ subOptions.indexOf(OPTION_DELIM, index) == index) {
+ index += OPTION_DELIM.length();
+ }
+
+ /*
+ * Ensure that there really is a suboption present. If a
+ * VALUE_DELIM has been found the suboption string is missing.
+ */
+ if (index < subOptionsLen &&
+ subOptions.indexOf(VALUE_DELIM, index) == index) {
+ String format =
+ ResourceStrings.getString(
+ "getsubopt_value_without_suboption");
+ Object[] args = new Object[0];
+ String msg = MessageFormat.format(format, args);
+ throw new IllegalArgumentException(msg);
+ }
+
+ return (index < subOptionsLen);
+ }
+
+ /**
+ * Get the current suboptions argument, or null if no argument is present.
+ *
+ * @return
+ * String containing the current suboptions argument, or null if the
+ * no argument is present.
+ */
+ public String getSubOptionArg() {
+ return value;
+ }
+}
+
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/Makefile b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/Makefile
new file mode 100644
index 0000000000..8479a9974d
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/Makefile
@@ -0,0 +1,84 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2001-2002 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/Makefile
+#
+
+CLASSFILES = \
+ Console.class \
+ DhcpCliProgram.class \
+ DhcpCliFunction.class \
+ DhcpCliOption.class \
+ DhcpCliOptions.class \
+ DhcpCliPrint.class \
+ Format.class \
+ GetOpt.class \
+ GetSubOpt.class \
+ ResourceStrings.class \
+ Util.class
+
+include $(SRC)/Makefile.master
+
+CLASSPATH = $(SRC)/cmd/cmd-inet/usr.sadm/dhcpmgr
+
+JAVAFILES = $(CLASSFILES:.class=.java)
+
+MSGDIR = $(ROOT)/usr/share/lib/locale/com/sun/dhcpmgr/cli/common
+MSGDIRS = $(ROOT)/usr/share/lib/locale \
+ $(ROOT)/usr/share/lib/locale/com \
+ $(ROOT)/usr/share/lib/locale/com/sun \
+ $(ROOT)/usr/share/lib/locale/com/sun/dhcpmgr \
+ $(ROOT)/usr/share/lib/locale/com/sun/dhcpmgr/cli \
+ $(MSGDIR)
+
+
+MSGFILES = ResourceBundle.properties
+MSGS= $(MSGFILES:%=$(MSGDIR)/%)
+
+CLEANFILES= *.class
+CLOBBERFILES=
+
+.KEEP_STATE:
+
+all: $(CLASSFILES)
+
+install: all
+
+_msg: $(MSGDIRS) $(MSGS)
+
+$(MSGDIR)/%: %
+ $(INS.file)
+
+$(MSGDIRS):
+ $(INS.dir)
+
+lint:
+
+clean:
+ $(RM) $(CLEANFILES)
+
+clobber: clean
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/ResourceBundle.properties b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/ResourceBundle.properties
new file mode 100644
index 0000000000..a5544c5439
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/ResourceBundle.properties
@@ -0,0 +1,71 @@
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Need to convert data store.
+#
+need_to_convert_datastore=\nThere is a version mismatch between the DHCP management software and the DHCP\ndata stores configured for the server.\n\nTo correct this situation run 'dhcpconfig -C' to convert the data stores to\nthe new version.
+
+#
+# No configuration file warning.
+#
+no_conf_warning=\nWarning: No DHCP configuration file exists. There is no way to verify the\ndata store version for this operation.\n
+
+#
+# Bad option for function
+#
+bad_function_option=Illegal option for this function: {0}
+
+#
+# Multiple functions requested
+#
+multiple_functions=Options {0} and {1} cannot be used together.
+
+#
+# Repeated option
+#
+repeated_option=Specify option {0} once per command.
+
+#
+# User is not authorized
+#
+user_not_allowed=User does not have authorization to perform operation. See {0}\nfor more information.
+
+#
+# GetOpt messages
+#
+getopt_illegal_option=Illegal Option: {0}
+getopt_requires_argument=Option requires an argument: {0}
+getopt_cannot_group=Option cannot be grouped with other options: {0}
+
+#
+# GetSubOpt messages
+#
+getsubopt_end_of_optionarg=No more suboptions.
+getsubopt_missing_close_quote=Missing closing quote in value for suboption {0}.
+getsubopt_value_without_suboption=Malformed suboptions - found value without suboption.
+getsubopt_malformed_value=Malformed value - garbled value for suboption {0}.
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/ResourceStrings.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/ResourceStrings.java
new file mode 100644
index 0000000000..8278cad4e9
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/ResourceStrings.java
@@ -0,0 +1,64 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.cli.common;
+
+import java.util.*;
+
+/**
+ * This class provides a convenient method to retrieve resources for
+ * the common package.
+ */
+public class ResourceStrings {
+
+ /**
+ * The handle to the resource bundle for the module.
+ */
+ private static ResourceBundle bundle = null;
+
+ /**
+ * Return a string from the resource bundle.
+ * @param key the key to the resource bundle string.
+ * @return the resource bundle string.
+ */
+ public static String getString(String key) {
+ String msg = null;
+ try {
+ if (bundle == null) {
+ bundle = ResourceBundle.getBundle(
+ "com.sun.dhcpmgr.cli.common.ResourceBundle",
+ Locale.getDefault());
+ }
+ msg = bundle.getString(key);
+ } catch (Throwable e) {
+ msg = new String(key);
+ }
+ return msg;
+
+ } // getString
+
+} // ResourceStrings
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/Util.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/Util.java
new file mode 100644
index 0000000000..3225d3761c
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/Util.java
@@ -0,0 +1,67 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+package com.sun.dhcpmgr.cli.common;
+
+/**
+ * This class contains common utilites used by the CLI programs.
+ */
+public class Util
+{
+ /**
+ * Array of hex characters, used by for translations.
+ */
+ private static final char hexChars[] = {'0', '1', '2', '3', '4', '5', '6',
+ '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+
+ /**
+ * Converts an ascii string into a string containing its hex value.
+ * @param ascii the ascii representation
+ * @return the hex representation
+ */
+ public static String asciiToHex(String ascii) {
+
+ StringBuffer hex = new StringBuffer();
+
+ if (ascii == null) {
+ return null;
+ }
+
+ for (int i = 0; i < ascii.length(); i++) {
+ char aChar = ascii.charAt(i);
+ int ndx = (aChar >> 4) & 0x000f;
+ hex.append(hexChars[ndx]);
+ ndx = aChar & 0x000f;
+ hex.append(hexChars[ndx]);
+ }
+
+ return hex.toString();
+
+ } // asciiToHex
+
+} // Util
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpbatch/DhcpBatch.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpbatch/DhcpBatch.java
new file mode 100644
index 0000000000..60c90d0416
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpbatch/DhcpBatch.java
@@ -0,0 +1,262 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.cli.dhcpbatch;
+
+import java.io.*;
+import java.text.MessageFormat;
+
+import com.sun.dhcpmgr.bridge.BridgeException;
+import com.sun.dhcpmgr.cli.common.DhcpCliPrint;
+import com.sun.dhcpmgr.cli.common.DhcpCliProgram;
+import com.sun.dhcpmgr.cli.common.DhcpCliFunction;
+
+/**
+ * This class represents the entry point to the DHCP CLI batch
+ * administration.
+ */
+public class DhcpBatch
+ extends DhcpCliProgram {
+
+ /**
+ * The program signature.
+ */
+ public static final String SIGNATURE = "dhcpbatch: ";
+
+ /**
+ * The source of the batch input. Either a fullpath to an
+ * input file or null if standard input is the source.
+ */
+ String inputSource = null;
+
+ /**
+ * Flag indicating whether or not the batch processing
+ * should be verbose.
+ */
+ boolean verbose = false;
+
+ /**
+ * The constructor used by the DHCP CLIs.
+ * @param inputSource a filepath or null if STDIN is to be used.
+ */
+ public DhcpBatch(String inputSource) {
+ this();
+ this.inputSource = inputSource;
+ } // constructor
+
+ /**
+ * The easy constructor that does nothing for now.
+ */
+ public DhcpBatch() {
+ } // constructor
+
+ /**
+ * Required by DhcpCliProgram.
+ * @return null
+ */
+ public String getManPage() {
+ return null;
+ }
+
+ /**
+ * Returns a localized string for this function
+ * @param key the resource bundle string identifier
+ * @return string from resource bundle.
+ */
+ public String getString(String key) {
+ return ResourceStrings.getString(key);
+ } // getString
+
+ /**
+ * Sets the inputSource value.
+ * @param inputSource the input source.
+ */
+ public void setInputSource(String inputSource) {
+ this.inputSource = inputSource;
+ } // setInputSource
+
+ /**
+ * Sets the verbose value.
+ * @param value the new value for the verbose attribute.
+ */
+ public void setVerbose(boolean value) {
+ verbose = value;
+ } // setVerbose
+
+ /**
+ * Executes the batch.
+ * @return return code as returned by batched program
+ */
+ public int execute() {
+
+ int returnCode = SUCCESS;
+
+ // By default read input from standard input.
+ //
+ InputStream in = System.in;
+
+ // If an argument was was provided, then it must be the batch file.
+ // Read input from the file rather than standard input.
+ //
+ if (inputSource != null) {
+ try {
+ in = new FileInputStream(inputSource);
+ } catch (FileNotFoundException e) {
+ Object [] arguments = new Object[1];
+ arguments[0] = inputSource;
+ printErrMessage(getString("dhcpbatch_file_not_found"),
+ arguments);
+ return (CRITICAL);
+ } catch (Throwable e) {
+ Object [] arguments = new Object[1];
+ arguments[0] = inputSource;
+ printErrMessage(getString("dhcpbatch_open_failed"), arguments);
+ printErrMessage(DhcpCliFunction.getMessage(e));
+ return (CRITICAL);
+ }
+ }
+
+ // Really just want to read lines at a time so, a BufferedReader
+ // will do the trick.
+ //
+ BufferedReader bufferedIn =
+ new BufferedReader(new InputStreamReader(in));
+
+ // Read a line at a time and exec the appropriate DHCP CLI command.
+ //
+ StringBuffer line = new StringBuffer(200);
+ line.append("> ");
+ DhcpCommand command = new DhcpCommand();
+ for (boolean end = false; end != true; ) {
+ try {
+ // Read a line. End of file seems to result in null line.
+ //
+ String input = bufferedIn.readLine();
+ if (input == null) {
+ // eof
+ end = true;
+ continue;
+ } else if (input.length() == 0) {
+ // empty line
+ } else if (input.charAt(0) == '#' && line.length() == 2) {
+ // comment
+ continue;
+ } else {
+ line.append(input);
+ if (input.charAt(input.length() - 1) == '\\') {
+ // continuation
+ continue;
+ }
+ }
+
+ if (verbose) {
+ DhcpCliPrint.printMessage(line.toString());
+ }
+ command.init(line.substring(2));
+ command.execute();
+ } catch (BridgeException e) {
+ // Failed to process command; print message and continue on
+ Object [] arguments = new Object[2];
+ arguments[0] = line.substring(2);
+ arguments[1] = DhcpCliFunction.getMessage(e);
+ printErrMessage(getString("dhcpbatch_cmd_error"), arguments);
+ returnCode = CRITICAL;
+ } catch (EOFException e) {
+ // Don't ever seem to get this, but just in case.
+ //
+ end = true;
+ } catch (IOException e) {
+ Object [] arguments = new Object[1];
+ arguments[0] = inputSource;
+ printErrMessage(getString("dhcpbatch_read_failed"), arguments);
+ printErrMessage(DhcpCliFunction.getMessage(e));
+ end = true;
+ returnCode = CRITICAL;
+ } finally {
+ // Reset buffer to process next command
+ line.setLength(2);
+ }
+ }
+
+ // Close the stream. Ignore errors as we're exiting anyway.
+ //
+ try {
+ bufferedIn.close();
+ } catch (IOException e) {
+ // Ignore it.
+ }
+
+ return (returnCode);
+
+ } // execute
+
+ /**
+ * Prints an error message.
+ * @param msg the message to print.
+ */
+ public static void printErrMessage(String msg) {
+ StringBuffer fullmsg = new StringBuffer(SIGNATURE);
+ fullmsg.append(msg);
+ DhcpCliPrint.printErrMessage(fullmsg.toString());
+ } // printErrMessage
+
+ /**
+ * Prints an error message.
+ * @param msg the message to print.
+ */
+ public static void printErrMessage(String msg, Object [] arguments) {
+ StringBuffer fullmsg = new StringBuffer(SIGNATURE);
+ fullmsg.append(msg);
+ MessageFormat form = new MessageFormat(fullmsg.toString());
+ DhcpCliPrint.printErrMessage(form.format(arguments));
+ } // printErrMessage
+
+ /**
+ * The entry point for the program.
+ * @param args the program arguments
+ */
+ public static void main(String[] args) {
+
+ DhcpBatch dhcpbatch = new DhcpBatch();
+
+ // Check usage.
+ //
+ String source = null;
+ if (args.length == 1) {
+ source = args[0];
+ } else if (args.length > 1) {
+ DhcpCliPrint.printErrMessage(
+ dhcpbatch.getString("dhcpbatch_usage"));
+ return;
+ }
+
+ dhcpbatch.setInputSource(source);
+ dhcpbatch.execute();
+
+ } // main
+
+} // DhcpBatch
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpbatch/DhcpCommand.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpbatch/DhcpCommand.java
new file mode 100644
index 0000000000..e7b33d8669
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpbatch/DhcpCommand.java
@@ -0,0 +1,146 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.cli.dhcpbatch;
+
+import com.sun.dhcpmgr.bridge.BridgeException;
+import com.sun.dhcpmgr.cli.common.DhcpCliPrint;
+import com.sun.dhcpmgr.cli.common.DhcpCliFunction;
+import com.sun.dhcpmgr.cli.common.DhcpCliProgram;
+import com.sun.dhcpmgr.cli.dhtadm.DhtAdm;
+import com.sun.dhcpmgr.cli.pntadm.PntAdm;
+
+/**
+ * This class represents a DHCP CLI command.
+ */
+public class DhcpCommand {
+
+ /**
+ * The dhtadm CLI command.
+ */
+ public static String DHTADM = "dhtadm";
+ public static DhtAdm dhtadm = null;
+
+ /**
+ * The pntadm CLI command.
+ */
+ public static String PNTADM = "pntadm";
+ public static PntAdm pntadm = null;
+
+ private String [] arglist;
+
+ /**
+ * The constructor for a DhcpCommand.
+ */
+ public DhcpCommand() {
+ // nothing to do.
+ } // constructor
+
+ /**
+ * Sets the arglist for the command.
+ * @param input the batch input line.
+ */
+ public void init(String input) throws BridgeException {
+ arglist = DhcpCliFunction.getSvcMgr().getArguments(input);
+ } // reset
+
+ /**
+ * Returns a localized string for this function
+ * @param key the resource bundle string identifier
+ * @return string from resource bundle.
+ */
+ public String getString(String key) {
+ return ResourceStrings.getString(key);
+ } // getString
+
+ /**
+ * Get the name of the CLI program.
+ * @return the name of the CLI for this command.
+ */
+ public String getProgram() {
+
+ String program = null;
+ if (arglist.length != 0) {
+ program = arglist[0];
+ }
+
+ return program;
+
+ } // getProgram
+
+ /**
+ * Returns the arguments for the CLI.
+ * @return the arguments.
+ */
+ public String [] getArgs() {
+
+ String [] args = new String[arglist.length - 1];
+
+ System.arraycopy(arglist, 1, args, 0, arglist.length - 1);
+ return args;
+
+ } // getArgs
+
+ /**
+ * Executes the CLI.
+ * @return return code as returned by caller.
+ */
+ public int execute() {
+ String program = getProgram();
+ String [] args = getArgs();
+ int returnCode = DhcpCliProgram.SUCCESS;
+
+ if (program == null) {
+ Object [] arguments = new Object[1];
+ arguments[0] = program;
+ DhcpBatch.printErrMessage(getString("dhcpcommand_invalid_command"),
+ arguments);
+ } else if (program.equals(PNTADM)) {
+ if (pntadm == null) {
+ pntadm = new PntAdm(args);
+ } else {
+ pntadm.reset(args);
+ }
+ returnCode = pntadm.execute();
+ } else if (program.equals(DHTADM)) {
+ if (dhtadm == null) {
+ dhtadm = new DhtAdm(args);
+ } else {
+ dhtadm.reset(args);
+ }
+ returnCode = dhtadm.execute();
+ } else {
+ Object [] arguments = new Object[1];
+ arguments[0] = program;
+ DhcpBatch.printErrMessage(getString("dhcpcommand_invalid_command"),
+ arguments);
+ returnCode = DhcpCliProgram.FAILURE;
+ }
+ return (returnCode);
+ } // execute
+
+} // DhcpCommand
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpbatch/Makefile b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpbatch/Makefile
new file mode 100644
index 0000000000..0b45d7a49f
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpbatch/Makefile
@@ -0,0 +1,75 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+# cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpbatch/Makefile
+#
+
+CLASSFILES = \
+ DhcpBatch.class \
+ DhcpCommand.class \
+ ResourceStrings.class
+
+include $(SRC)/Makefile.master
+
+CLASSPATH= $(SRC)/cmd/cmd-inet/usr.sadm/dhcpmgr
+
+JAVAFILES = $(CLASSFILES:.class=.java)
+
+MSGDIR= $(ROOT)/usr/share/lib/locale/com/sun/dhcpmgr/cli/dhcpbatch
+MSGDIRS = $(ROOT)/usr/share/lib/locale \
+ $(ROOT)/usr/share/lib/locale/com \
+ $(ROOT)/usr/share/lib/locale/com/sun \
+ $(ROOT)/usr/share/lib/locale/com/sun/dhcpmgr \
+ $(ROOT)/usr/share/lib/locale/com/sun/dhcpmgr/cli \
+ $(MSGDIR)
+
+MSGFILES= ResourceBundle.properties
+MSGS= $(MSGFILES:%=$(MSGDIR)/%)
+
+CLEANFILES= *.class
+CLOBBERFILES=
+
+.KEEP_STATE:
+
+all: $(CLASSFILES)
+
+install: all
+
+_msg: $(MSGDIRS) $(MSGS)
+
+$(MSGDIR)/%: %
+ $(INS.file)
+
+$(MSGDIRS):
+ $(INS.dir)
+
+lint:
+
+clean:
+ $(RM) $(CLEANFILES)
+
+clobber: clean
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpbatch/ResourceBundle.properties b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpbatch/ResourceBundle.properties
new file mode 100644
index 0000000000..b62656aaf6
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpbatch/ResourceBundle.properties
@@ -0,0 +1,36 @@
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# DhcpBatch messages.
+#
+dhcpbatch_usage=dhcpbatch [batchfile]
+dhcpbatch_file_not_found=File not found - {0}
+dhcpbatch_open_failed=Error opening batch file - {0}
+dhcpbatch_read_failed=Error reading batch file - {0}
+dhcpbatch_cmd_error=Error processing command: {0}\nMessage is: {1}
+dhcpcommand_invalid_command=Invalid command - {0}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpbatch/ResourceStrings.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpbatch/ResourceStrings.java
new file mode 100644
index 0000000000..f0941e09c7
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpbatch/ResourceStrings.java
@@ -0,0 +1,64 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.cli.dhcpbatch;
+
+import java.util.*;
+
+/**
+ * This class provides a convenient method to retrieve resources for
+ * the dhcpbatch package.
+ */
+public class ResourceStrings {
+
+ /**
+ * The handle to the resource bundle for the module.
+ */
+ private static ResourceBundle bundle = null;
+
+ /**
+ * Return a string from the resource bundle.
+ * @param key the key to the resource bundle string.
+ * @return the resource bundle string.
+ */
+ public static String getString(String key) {
+ String msg = null;
+ try {
+ if (bundle == null) {
+ bundle = ResourceBundle.getBundle(
+ "com.sun.dhcpmgr.cli.dhcpbatch.ResourceBundle",
+ Locale.getDefault());
+ }
+ msg = bundle.getString(key);
+ } catch (Throwable e) {
+ msg = new String(key);
+ }
+ return msg;
+
+ } // getString
+
+} // ResourceStrings
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ConfigureBootp.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ConfigureBootp.java
new file mode 100644
index 0000000000..c667cfc1f6
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ConfigureBootp.java
@@ -0,0 +1,135 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.cli.dhcpconfig;
+
+import com.sun.dhcpmgr.cli.common.DhcpCliFunction;
+import com.sun.dhcpmgr.data.DhcpdOptions;
+import com.sun.dhcpmgr.data.ValidationException;
+
+/**
+ * The main class for the "configure BOOTP relay" functionality of dhcpconfig.
+ */
+public class ConfigureBootp extends DhcpCfgFunction {
+
+ /**
+ * The valid options associated with configuring a BOOTP relay agent.
+ */
+ static final int supportedOptions[] = {
+ };
+
+ /**
+ * The addresses of servers for which to serve as a relay.
+ */
+ String addresses;
+
+ /**
+ * Constructs a ConfigureBootp object.
+ * @param addresses the addresses of servers for which to serve as a relay.
+ */
+ public ConfigureBootp(String addresses) {
+
+ validOptions = supportedOptions;
+ this.addresses = addresses;
+
+ } // constructor
+
+ /**
+ * Returns the option flag for this function.
+ * @returns the option flag for this function.
+ */
+ public int getFunctionFlag() {
+ return (DhcpCfg.CONFIGURE_BOOTP);
+ }
+
+ /**
+ * Executes the "configure BOOTP relay" functionality.
+ * @return DhcpCfg.SUCCESS or DhcpCfg.FAILURE
+ */
+ public int execute() {
+
+ // Check to see if DHCP or BOOTP relay is already configured.
+ //
+ boolean isServer = false;
+ boolean isRelay = false;
+ try {
+ DhcpdOptions opts = getSvcMgr().readDefaults();
+ if (opts.isRelay()) {
+ isRelay = true;
+ } else {
+ isServer = true;
+ }
+ } catch (Throwable e) {
+ // this is to be expected
+ }
+
+ if (isServer) {
+ printErrMessage(getString("config_dhcp_configured_error"));
+ return (DhcpCfg.FAILURE);
+ }
+
+ if (isRelay) {
+ printErrMessage(getString("config_bootp_configured_error"));
+ return (DhcpCfg.FAILURE);
+ }
+
+ // Write the information to the DHCP configuration file.
+ //
+ try {
+ IPAddressList list = new IPAddressList(addresses);
+ DhcpdOptions options = new DhcpdOptions();
+ options.setDaemonEnabled(true);
+ options.setRelay(true, list.toString());
+ getSvcMgr().writeDefaults(options);
+ printMessage(getString("config_create_conf_progress"));
+ } catch (ValidationException e) {
+ printErrMessage(getMessage(e));
+ return (DhcpCfg.FAILURE);
+ } catch (Throwable e) {
+ Object [] arguments = new Object[1];
+ arguments[0] = getMessage(e);
+ printErrMessage(getString("config_writing_conf_error"), arguments);
+ return (DhcpCfg.FAILURE);
+ }
+
+ // Start it up.
+ //
+ try {
+ getSvcMgr().startup();
+ printMessage(getString("config_startup_progress"));
+ } catch (Throwable e) {
+ Object [] arguments = new Object[1];
+ arguments[0] = getMessage(e);
+ printErrMessage(getString("config_startup_error"), arguments);
+ return (DhcpCfg.FAILURE);
+ }
+
+ return (DhcpCfg.SUCCESS);
+
+ } // execute
+
+} // ConfigureBootp
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ConfigureDhcp.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ConfigureDhcp.java
new file mode 100644
index 0000000000..026e5fece2
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ConfigureDhcp.java
@@ -0,0 +1,330 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.cli.dhcpconfig;
+
+import com.sun.dhcpmgr.cli.common.DhcpCliFunction;
+import com.sun.dhcpmgr.data.DhcpdOptions;
+import com.sun.dhcpmgr.data.DhcpDatastore;
+import com.sun.dhcpmgr.data.ValidationException;
+import com.sun.dhcpmgr.data.StandardOptions;
+import com.sun.dhcpmgr.bridge.ExistsException;
+import com.sun.dhcpmgr.bridge.TableExistsException;
+
+import java.net.InetAddress;
+import java.lang.IllegalArgumentException;
+
+/**
+ * The main class for the "configure DHCP server" functionality of dhcpconfig.
+ */
+public class ConfigureDhcp extends DhcpCfgFunction {
+
+ /**
+ * The valid options associated with configuring a DHCP server.
+ */
+ static final int supportedOptions[] = {
+ DhcpCfg.NON_NEGOTIABLE_LEASE,
+ DhcpCfg.HOSTS_RESOURCE,
+ DhcpCfg.HOSTS_DOMAIN,
+ DhcpCfg.LEASE_LENGTH,
+ DhcpCfg.DNS_ADDRESSES,
+ DhcpCfg.DNS_DOMAIN,
+ DhcpCfg.RESOURCE,
+ DhcpCfg.RESOURCE_CONFIG,
+ DhcpCfg.PATH
+ };
+
+ /**
+ * Constructs a ConfigureDhcp object.
+ */
+ public ConfigureDhcp() {
+
+ validOptions = supportedOptions;
+
+ } // constructor
+
+ /**
+ * Returns the option flag for this function.
+ * @returns the option flag for this function.
+ */
+ public int getFunctionFlag() {
+ return (DhcpCfg.CONFIGURE_DHCP);
+ }
+
+ /**
+ * Executes the "configure DHCP server" functionality.
+ * @return DhcpCfg.SUCCESS or DhcpCfg.FAILURE
+ */
+ public int execute() throws IllegalArgumentException {
+
+ // Check to see if DHCP or BOOTP relay is already configured.
+ //
+ boolean isServer = false;
+ boolean isRelay = false;
+ try {
+ DhcpdOptions opts = getSvcMgr().readDefaults();
+ if (opts.isRelay()) {
+ isRelay = true;
+ } else {
+ isServer = true;
+ }
+ } catch (Throwable e) {
+ // this is to be expected
+ }
+
+ if (isServer) {
+ printErrMessage(getString("config_dhcp_configured_error"));
+ return (DhcpCfg.FAILURE);
+ }
+
+ if (isRelay) {
+ printErrMessage(getString("config_bootp_configured_error"));
+ return (DhcpCfg.FAILURE);
+ }
+
+ // User must define both resource and path.
+ //
+ if (options.valueOf(DhcpCfg.RESOURCE) == null ||
+ options.valueOf(DhcpCfg.PATH) == null) {
+ String msg = getString("config_null_datastore_error");
+ throw new IllegalArgumentException(msg);
+ }
+
+ try {
+ setDhcpDatastore(getSvcMgr().getDataStore(
+ options.valueOf(DhcpCfg.RESOURCE)));
+ getDhcpDatastore().setLocation(options.valueOf(DhcpCfg.PATH));
+ getDhcpDatastore().setConfig(
+ options.valueOf(DhcpCfg.RESOURCE_CONFIG));
+ } catch (Throwable e) {
+ // resource will not be valid
+ }
+
+
+ if (getDhcpDatastore() == null || !getDhcpDatastore().isEnabled()) {
+ Object [] arguments = new Object[1];
+ arguments[0] = getDhcpDatastore().getResource();
+ printErrMessage(getString("config_invalid_resource_error"),
+ arguments);
+ return (DhcpCfg.FAILURE);
+ }
+
+ // Retrieve the hosts resource and domain options and validate.
+ //
+ String hostsResource = options.valueOf(DhcpCfg.HOSTS_RESOURCE);
+ String hostsDomain = options.valueOf(DhcpCfg.HOSTS_DOMAIN);
+
+ if (hostsResource == null) {
+ if (hostsDomain != null) {
+ String msg = getString("config_requires_hosts_resource_error");
+ throw new IllegalArgumentException(msg);
+ }
+ } else if (hostsResource.equals(DhcpdOptions.DSVC_CV_NISPLUS) ||
+ hostsResource.equals(DhcpdOptions.DSVC_CV_DNS)) {
+ if (hostsDomain == null) {
+ Object [] arguments = new Object[1];
+ arguments[0] = hostsResource;
+ printErrMessage(
+ getString("config_requires_hosts_domain_error"),
+ arguments);
+ return (DhcpCfg.FAILURE);
+ }
+ } else if (hostsResource.equals(DhcpdOptions.DSVC_CV_FILES)) {
+ if (hostsDomain != null) {
+ Object [] arguments = new Object[1];
+ arguments[0] = hostsResource;
+ printErrMessage(getString("config_hosts_domain_ignored_error"),
+ arguments);
+ return (DhcpCfg.FAILURE);
+ }
+ } else {
+ Object [] arguments = new Object[1];
+ arguments[0] = hostsResource;
+ printErrMessage(getString("config_invalid_hosts_resource_error"),
+ arguments);
+ return (DhcpCfg.FAILURE);
+ }
+
+ // Retrieve the leaseLength option and check its validity.
+ // The default (3600*24 = 1 day) should be defined as static somewhere.
+ //
+ Integer leaseLength = new Integer(3600*24);
+ if (options.isSet(DhcpCfg.LEASE_LENGTH)) {
+ try {
+ leaseLength =
+ new Integer(options.valueOf(DhcpCfg.LEASE_LENGTH));
+ } catch (Throwable e) {
+ printErrMessage(getString("config_lease_error"));
+ return (DhcpCfg.FAILURE);
+ }
+
+ if (leaseLength.intValue() == 0) {
+ printErrMessage(getString("config_lease_zero_error"));
+ return (DhcpCfg.FAILURE);
+ }
+ }
+
+ // Are leases negotiable
+ //
+ boolean leaseNegotiable =
+ !options.isSet(DhcpCfg.NON_NEGOTIABLE_LEASE);
+
+ // Get the DNS information.
+ //
+ String dnsDomain = options.valueOf(DhcpCfg.DNS_DOMAIN);
+ String dnsServers = options.valueOf(DhcpCfg.DNS_ADDRESSES);
+ if ((dnsDomain == null) != (dnsServers == null)) {
+ String msg = getString("config_dns_error");
+ throw new IllegalArgumentException(msg);
+ }
+
+ IPAddressList dnsAddresses = null;
+ try {
+ if (dnsDomain == null) {
+ dnsDomain = getSvcMgr().getStringOption(
+ StandardOptions.CD_DNSDOMAIN, "");
+ }
+ if (dnsServers != null) {
+ dnsAddresses = new IPAddressList(dnsServers);
+ } else {
+ dnsAddresses = new IPAddressList(
+ getSvcMgr().getIPOption(StandardOptions.CD_DNSSERV, ""));
+ }
+ } catch (ValidationException e) {
+ Object [] arguments = new Object[1];
+ arguments[0] = getMessage(e);
+ printErrMessage(getString("config_dns_server_error"), arguments);
+ return (DhcpCfg.FAILURE);
+ } catch (Throwable e) {
+ // Ignore, DNS info will not be configured in the server macro.
+ }
+
+ // Create the location if it does not exist.
+ //
+ try {
+ getSvcMgr().makeLocation(getDhcpDatastore());
+ } catch (ExistsException e) {
+ // this is o.k.
+ } catch (Throwable e) {
+ Object [] arguments = new Object[1];
+ arguments[0] = getDhcpDatastore().getLocation();
+ printErrMessage(getString("config_make_location_error"),
+ arguments);
+ return (DhcpCfg.FAILURE);
+ }
+
+ // Create the DHCP configuration file
+ //
+ DhcpdOptions dhcpdOptions = new DhcpdOptions();
+ dhcpdOptions.setDaemonEnabled(true);
+ dhcpdOptions.setDhcpDatastore(getDhcpDatastore());
+ if (hostsResource != null) {
+ dhcpdOptions.setHostsResource(hostsResource);
+ }
+ if (hostsDomain != null) {
+ dhcpdOptions.setHostsDomain(hostsDomain);
+ }
+ try {
+ getSvcMgr().writeDefaults(dhcpdOptions);
+ printMessage(getString("config_create_conf_progress"));
+ } catch (Throwable e) {
+ Object [] arguments = new Object[1];
+ arguments[0] = getMessage(e);
+ printErrMessage(getString("config_writing_conf_error"), arguments);
+ return (DhcpCfg.FAILURE);
+ }
+
+ // Create the dhcptab
+ //
+ try {
+ getDhcptabMgr().createDhcptab(getDhcpDatastore());
+ printMessage(getString("config_dhcptab_progress"));
+ } catch (TableExistsException e) {
+ // Not an error; some data stores are shared by multiple servers
+ printMessage(getString("config_dhcptab_exists_progress"));
+ } catch (Throwable e) {
+ Object [] arguments = new Object[1];
+ arguments[0] = getMessage(e);
+ printErrMessage(getString("config_dhcptab_error"), arguments);
+ return (DhcpCfg.FAILURE);
+ }
+
+ // Create the locale macro
+ //
+ try {
+ getDhcptabMgr().createLocaleMacro();
+ printMessage(getString("config_locale_progress"));
+ } catch (ExistsException e) {
+ /*
+ * Ignore this error, if one's already there we'll assume
+ * it's correct
+ */
+ } catch (Throwable e) {
+ Object [] arguments = new Object[1];
+ arguments[0] = getMessage(e);
+ printErrMessage(getString("config_locale_error"), arguments);
+ return (DhcpCfg.FAILURE);
+ }
+
+ // Create the Server macro
+ //
+ String svrName = null;
+ try {
+ svrName = getSvcMgr().getShortServerName();
+ InetAddress svrAddress = getSvcMgr().getServerAddress();
+ getDhcptabMgr().createServerMacro(svrName, svrAddress,
+ leaseLength.intValue(), leaseNegotiable, dnsDomain,
+ dnsAddresses);
+ Object [] arguments = new Object[1];
+ arguments[0] = svrName;
+ printMessage(getString("config_server_macro_progress"), arguments);
+ } catch (Throwable e) {
+ // Couldn't create it; inform user because this is serious
+ Object [] arguments = new Object[2];
+ arguments[1] = svrName;
+ arguments[0] = getMessage(e);
+ printErrMessage(getString("config_server_macro_error"), arguments);
+ return (DhcpCfg.FAILURE);
+ }
+
+ // Start it up.
+ //
+ try {
+ getSvcMgr().startup();
+ printMessage(getString("config_startup_progress"));
+ } catch (Throwable e) {
+ Object [] arguments = new Object[1];
+ arguments[0] = getMessage(e);
+ printErrMessage(getString("config_startup_error"), arguments);
+ return (DhcpCfg.FAILURE);
+ }
+
+ return (DhcpCfg.SUCCESS);
+
+ } // execute
+
+} // ConfigureDhcp
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ConfigureNetwork.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ConfigureNetwork.java
new file mode 100644
index 0000000000..5b2ab38bc4
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ConfigureNetwork.java
@@ -0,0 +1,219 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2001-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.cli.dhcpconfig;
+
+import com.sun.dhcpmgr.cli.common.DhcpCliFunction;
+import com.sun.dhcpmgr.data.IPAddress;
+import com.sun.dhcpmgr.data.Network;
+import com.sun.dhcpmgr.data.ValidationException;
+import com.sun.dhcpmgr.data.StandardOptions;
+
+/**
+ * The main class for the "configure network" functionality of dhcpconfig.
+ */
+public class ConfigureNetwork extends DhcpCfgFunction {
+
+ /**
+ * The valid options associated with configuring a network.
+ */
+ static final int supportedOptions[] = {
+ DhcpCfg.SUBNET_MASK,
+ DhcpCfg.POINT_TO_POINT,
+ DhcpCfg.ROUTER_ADDRESSES,
+ DhcpCfg.NIS_DOMAIN,
+ DhcpCfg.NIS_ADDRESSES,
+ DhcpCfg.SIGHUP
+ };
+
+ /**
+ * The address of the network to configure.
+ */
+ String address;
+
+ /**
+ * Constructs a ConfigureNetwork object.
+ * @param address the address of the network to configure
+ */
+ public ConfigureNetwork(String address) {
+
+ validOptions = supportedOptions;
+ this.address = address;
+
+ } // constructor
+
+ /**
+ * Returns the option flag for this function.
+ * @returns the option flag for this function.
+ */
+ public int getFunctionFlag() {
+ return (DhcpCfg.CONFIGURE_NETWORK);
+ }
+
+ /**
+ * Executes the "configure network" functionality.
+ * @return DhcpCfg.SUCCESS or DhcpCfg.FAILURE
+ */
+ public int execute() {
+
+ // Make sure that server is configured as a DHCP server.
+ //
+ if (!isServerConfigured()) {
+ return (DhcpCfg.FAILURE);
+ }
+
+ // Check the validity of the data store version.
+ //
+ if (!isVersionValid(false)) {
+ return (DhcpCfg.FAILURE);
+ }
+
+ // Validate the network address
+ //
+ Network network;
+ try {
+ network = getNetMgr().getNetwork(address);
+ } catch (Throwable e) {
+ Object [] arguments = new Object[1];
+ arguments[0] = address;
+ printErrMessage(getString("cfgnet_invalid_network_error"),
+ arguments);
+ return (DhcpCfg.FAILURE);
+ }
+
+ // Determine/validate the subnet mask.
+ //
+ IPAddress netmask = null;
+ String mask = options.valueOf(DhcpCfg.SUBNET_MASK);
+ if (mask != null) {
+ try {
+ netmask = new IPAddress(mask);
+ network.setMask(netmask);
+ } catch (ValidationException e) {
+ Object [] arguments = new Object[1];
+ arguments[0] = address;
+ printErrMessage(getString("cfgnet_invalid_ip_error"),
+ arguments);
+ return (DhcpCfg.FAILURE);
+ }
+ }
+
+ boolean isLan = !options.isSet(DhcpCfg.POINT_TO_POINT);
+
+ // Get the list of router addresses
+ //
+ IPAddressList routers = null;
+ if (options.isSet(DhcpCfg.ROUTER_ADDRESSES)) {
+ try {
+ String addrs = options.valueOf(DhcpCfg.ROUTER_ADDRESSES);
+ routers = new IPAddressList(addrs);
+ } catch (ValidationException e) {
+ printErrMessage(getMessage(e));
+ return (DhcpCfg.FAILURE);
+ }
+ }
+
+ // Get the NIS info.
+ //
+ String nisDomain = options.valueOf(DhcpCfg.NIS_DOMAIN);
+ String nisServers = options.valueOf(DhcpCfg.NIS_ADDRESSES);
+ if ((nisDomain == null) != (nisServers == null)) {
+ String msg = getString("cfgnet_nis_error");
+ throw new IllegalArgumentException(msg);
+ }
+
+ IPAddressList nisAddresses = null;
+ try {
+ if (nisDomain == null) {
+ nisDomain = getSvcMgr().getStringOption(
+ StandardOptions.CD_NIS_DOMAIN, "");
+ }
+ if (nisServers != null) {
+ nisAddresses = new IPAddressList(nisServers);
+ } else {
+ nisAddresses = new IPAddressList(
+ getSvcMgr().getIPOption(StandardOptions.CD_NIS_SERV, ""));
+ }
+ } catch (ValidationException e) {
+ Object [] arguments = new Object[1];
+ arguments[0] = getMessage(e);
+ printErrMessage(getString("cfgnet_nis_server_error"), arguments);
+ return (DhcpCfg.FAILURE);
+ } catch (Throwable e) {
+ // Ignore, NIS info will not be configured in the network macro.
+ }
+
+ // Create the network macro in the dhcptab
+ //
+ try {
+ IPAddress[] routersArray = null;
+ if (routers != null) {
+ routersArray = routers.toIPAddressArray();
+ }
+ getDhcptabMgr().createNetworkMacro(network, routersArray,
+ isLan, nisDomain, nisAddresses, null, null);
+ Object [] arguments = new Object[1];
+ arguments[0] = network.toString();
+ printMessage(getString("cfgnet_network_macro_progress"),
+ arguments);
+ } catch (Throwable e) {
+ Object [] arguments = new Object[1];
+ arguments[0] = getMessage(e);
+ printErrMessage(getString("cfgnet_network_macro_error"),
+ arguments);
+ return (DhcpCfg.FAILURE);
+ }
+
+
+ // Create the network table for this network
+ //
+ try {
+ getNetMgr().createNetwork(network.toString());
+ printMessage(getString("cfgnet_network_table_progress"));
+ } catch (Throwable e) {
+ Object [] arguments = new Object[1];
+ arguments[0] = getMessage(e);
+ printErrMessage(getString("cfgnet_network_table_error"),
+ arguments);
+ return (DhcpCfg.FAILURE);
+ }
+
+ // Signal the server if the user asked us to
+ try {
+ if (options.isSet(DhcpCfg.SIGHUP)) {
+ getSvcMgr().reload();
+ }
+ } catch (Throwable e) {
+ printErrMessage(getString("sighup_failed"));
+ return (DhcpCfg.FAILURE);
+ }
+
+ return (DhcpCfg.SUCCESS);
+
+ } // execute
+
+} // ConfigureNetwork
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ConfigureService.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ConfigureService.java
new file mode 100644
index 0000000000..c3c5c48852
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ConfigureService.java
@@ -0,0 +1,362 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.cli.dhcpconfig;
+
+import com.sun.dhcpmgr.cli.common.DhcpCliFunction;
+import com.sun.dhcpmgr.data.DhcpdOptions;
+import com.sun.dhcpmgr.data.qualifier.*;
+import com.sun.dhcpmgr.server.*;
+import com.sun.dhcpmgr.bridge.BridgeException;
+
+/**
+ * Functions for handling all dhcpconfig options that manage the DHCP server
+ * parameters.
+ */
+public class ConfigureService extends DhcpCfgFunction {
+
+ /**
+ * Return codes.
+ *
+ * These supplement the inherited SUCCESS and FAILURE return codes.
+ * Note that these values are binary!!! The next value would be 4.
+ */
+ public static final int ENABLED = 1;
+ public static final int DISABLED = 0;
+ public static final int RUNNING = 2;
+ public static final int STOPPED = 0;
+
+ /**
+ * Options that this DhcpCfgFunction will accept.
+ */
+ static final int supportedOptions[] = {
+ DhcpCfg.SERVICE_ENABLE,
+ DhcpCfg.SERVICE_DISABLE,
+ DhcpCfg.SERVICE_REENABLE,
+ DhcpCfg.SERVICE_QUERY,
+ };
+
+ public ConfigureService() {
+ validOptions = supportedOptions;
+ } // constructor
+
+ /**
+ * Returns the option flag for this function.
+ * @returns
+ * The option flag for this function.
+ */
+ public int getFunctionFlag() {
+ return DhcpCfg.CONFIGURE_SERVICE;
+ } // getFunctionFlag
+
+ /**
+ * Parse and execute the options for this function.
+ * @return
+ * DhcpCfg.SUCCESS for success and DhcpCfg.FAILURE for failure.
+ */
+ public int execute() throws IllegalArgumentException {
+ Action action = null;
+ boolean enableOptionSet;
+ boolean disableOptionSet;
+ boolean reenableOptionSet;
+ boolean queryOptionSet;
+ boolean[] possibleOptions = {
+ enableOptionSet = options.isSet(DhcpCfg.SERVICE_ENABLE),
+ disableOptionSet = options.isSet(DhcpCfg.SERVICE_DISABLE),
+ reenableOptionSet = options.isSet(DhcpCfg.SERVICE_REENABLE),
+ queryOptionSet = options.isSet(DhcpCfg.SERVICE_QUERY)
+ };
+
+ int numOptionsSet = 0;
+ for (int index = 0; index < possibleOptions.length; index++) {
+ if (possibleOptions[index]) {
+ numOptionsSet++;
+ }
+ }
+
+ if (numOptionsSet != 1) {
+ String msg = getString("config_service_bad_action_error");
+ throw new IllegalArgumentException(msg);
+ }
+
+ if (enableOptionSet) {
+ action = new ActionEnable();
+ } else if (disableOptionSet) {
+ action = new ActionDisable();
+ } else if (reenableOptionSet) {
+ action = new ActionReenable();
+ } else {
+ action = new ActionQuery();
+ }
+
+ return action.execute();
+ } // execute
+
+ /**
+ * All functions are carried out through a specific action sub-classed
+ * from this class.
+ */
+ private interface Action {
+ /**
+ * Execute the action.
+ *
+ * @return
+ * DhcpCfg.SUCCESS for success and DhcpCfg.FAILURE for failure.
+ */
+ public int execute();
+ }
+
+ /**
+ * Shared super class for actions.
+ */
+ private abstract class ActionImpl implements Action {
+ /**
+ * Server parameters.
+ */
+ protected DhcpdOptions dhcpdOptions;
+
+ /**
+ * Service manager.
+ */
+ protected DhcpServiceMgr dhcpServiceMgr;
+
+ /**
+ * Validate and execute the action. A sub-classed action is passed
+ * execution control via the doExecute() callback method.
+ *
+ * @return
+ * DhcpCfg.SUCCESS for success and DhcpCfg.FAILURE for failure.
+ */
+ public final int execute() {
+ dhcpServiceMgr = getSvcMgr();
+
+ try {
+ dhcpdOptions = dhcpServiceMgr.readDefaults();
+ } catch (BridgeException be) {
+ printErrMessage(
+ getString("config_service_failed_read_params_error"));
+ return DhcpCfg.FAILURE;
+ }
+
+ dhcpdOptions.clearDirty();
+
+ int result = doExecute();
+
+ if (result != DhcpCfg.FAILURE) {
+ if (dhcpdOptionsWrite() == DhcpCfg.FAILURE) {
+ return DhcpCfg.FAILURE;
+ }
+ }
+
+ return result;
+ } // execute
+
+ /**
+ * Sub-classed action callback method. Once validation has been
+ * performed execution is continued in the action sub-class by calling
+ * this method.
+ *
+ * @return
+ * DhcpCfg.SUCCESS for success, otherwise DhcpCfg.FAILURE for failure.
+ */
+ protected abstract int doExecute();
+
+ /**
+ * Ensures that changes to the parameter are written back to the data
+ * store.
+ *
+ * @return
+ * DhcpCfg.SUCCESS for success, otherwise DhcpCfg.FAILURE for failure.
+ */
+ protected int dhcpdOptionsWrite() {
+ if (dhcpdOptions.isDirty()) {
+ try {
+ dhcpServiceMgr.writeDefaults(dhcpdOptions);
+ } catch (BridgeException e) {
+ printErrMessage(getString(
+ "config_service_failed_write_params_error"));
+ return DhcpCfg.FAILURE;
+ }
+
+ dhcpdOptions.clearDirty();
+ }
+
+ return DhcpCfg.SUCCESS;
+ }
+ }
+
+ /**
+ * Enable the DHCP service.
+ */
+ private class ActionEnable extends ActionImpl {
+ /**
+ * Enable the DHCP service.
+ *
+ * @return
+ * DhcpCfg.SUCCESS for success, otherwise DhcpCfg.FAILURE for failure.
+ */
+ protected int doExecute() {
+ if (!dhcpdOptions.isDaemonEnabled()) {
+ dhcpdOptions.setDaemonEnabled(true);
+ if (dhcpdOptionsWrite() == DhcpCfg.FAILURE) {
+ return DhcpCfg.FAILURE;
+ }
+ printMessage(getString("config_service_state_enabled"));
+ }
+
+ try {
+ if (!dhcpServiceMgr.isServerRunning()) {
+ dhcpServiceMgr.startup();
+ printMessage(getString("config_service_state_startup"));
+ }
+ } catch (BridgeException e) {
+ printErrMessage(
+ getString("config_service_failed_startup_error"));
+ return DhcpCfg.FAILURE;
+ }
+
+ return DhcpCfg.SUCCESS;
+ } // doExecute
+ }
+
+ /**
+ * Disable the DHCP service.
+ */
+ private class ActionDisable extends ActionImpl {
+ /**
+ * Disable the DHCP service.
+ *
+ * @return
+ * DhcpCfg.SUCCESS for success, otherwise DhcpCfg.FAILURE for failure.
+ */
+ protected int doExecute() {
+ if (dhcpdOptions.isDaemonEnabled()) {
+ dhcpdOptions.setDaemonEnabled(false);
+ if (dhcpdOptionsWrite() == DhcpCfg.FAILURE) {
+ return DhcpCfg.FAILURE;
+ }
+ printMessage(getString("config_service_state_disabled"));
+ }
+
+ try {
+ if (dhcpServiceMgr.isServerRunning()) {
+ dhcpServiceMgr.shutdown();
+ printMessage(getString("config_service_state_shutdown"));
+ }
+ } catch (BridgeException e) {
+ printErrMessage(
+ getString("config_service_failed_shutdown_error"));
+ return DhcpCfg.FAILURE;
+ }
+
+ return DhcpCfg.SUCCESS;
+ } // doExecute
+ }
+
+ /**
+ * Reenable the DHCP service.
+ */
+ private class ActionReenable extends ActionImpl {
+ /**
+ * Reenable the DHCP service.
+ *
+ * @return
+ * DhcpCfg.SUCCESS for success, otherwise DhcpCfg.FAILURE for failure.
+ */
+ protected int doExecute() {
+ try {
+ if (dhcpServiceMgr.isServerRunning()) {
+ dhcpServiceMgr.shutdown();
+ printMessage(getString("config_service_state_shutdown"));
+ }
+ } catch (BridgeException e) {
+ printErrMessage(
+ getString("config_service_failed_shutdown_error"));
+ return DhcpCfg.FAILURE;
+ }
+
+ if (!dhcpdOptions.isDaemonEnabled()) {
+ dhcpdOptions.setDaemonEnabled(true);
+ if (dhcpdOptionsWrite() == DhcpCfg.FAILURE) {
+ return DhcpCfg.FAILURE;
+ }
+ printMessage(getString("config_service_state_enabled"));
+ }
+
+ try {
+ dhcpServiceMgr.startup();
+ printMessage(getString("config_service_state_startup"));
+ } catch (BridgeException e) {
+ printErrMessage(
+ getString("config_service_failed_startup_error"));
+ return DhcpCfg.FAILURE;
+ }
+
+ return DhcpCfg.SUCCESS;
+ } // doExecute
+ }
+
+ /**
+ * Query the DHCP service.
+ */
+ private class ActionQuery extends ActionImpl {
+ /**
+ * Query the DHCP service.
+ *
+ * @return
+ * DhcpCfg.SUCCESS for success, otherwise DhcpCfg.FAILURE for failure.
+ */
+ protected int doExecute() {
+ int serviceState = DhcpCfg.SUCCESS;
+
+ if (dhcpdOptions.isDaemonEnabled()) {
+ printMessage(getString("config_service_state_enabled"));
+ serviceState += ENABLED;
+ } else {
+ printMessage(getString("config_service_state_disabled"));
+ serviceState += DISABLED;
+ }
+
+ try {
+ if (dhcpServiceMgr.isServerRunning()) {
+ printMessage(getString("config_service_state_running"));
+ serviceState += RUNNING;
+ } else {
+ printMessage(getString("config_service_state_stopped"));
+ serviceState += STOPPED;
+ }
+ } catch (BridgeException e) {
+ printErrMessage(getString("config_service_failed_query_error"));
+ return DhcpCfg.FAILURE;
+ }
+
+ return serviceState;
+ } // doExecute
+ }
+
+
+} // ConfigureService
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ConvertDataStore.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ConvertDataStore.java
new file mode 100644
index 0000000000..4db430ef91
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ConvertDataStore.java
@@ -0,0 +1,266 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.cli.dhcpconfig;
+
+import com.sun.dhcpmgr.cli.common.DhcpCliFunction;
+import com.sun.dhcpmgr.cli.common.Console;
+import com.sun.dhcpmgr.data.DhcpdOptions;
+import com.sun.dhcpmgr.data.DhcpDatastore;
+import com.sun.dhcpmgr.data.Network;
+import com.sun.dhcpmgr.bridge.ExistsException;
+
+public class ConvertDataStore extends DhcpCfgFunction {
+
+ static final int supportedOptions[] = {
+ DhcpCfg.FORCE,
+ DhcpCfg.KEEP_TABLES,
+ DhcpCfg.RESOURCE,
+ DhcpCfg.RESOURCE_CONFIG,
+ DhcpCfg.PATH
+ };
+
+ public ConvertDataStore() {
+
+ validOptions = supportedOptions;
+
+ } // constructor
+
+ /**
+ * Returns the option flag for this function.
+ * @returns the option flag for this function.
+ */
+ public int getFunctionFlag() {
+ return (DhcpCfg.CONVERT_DATA_STORE);
+ }
+
+ public int execute() throws IllegalArgumentException {
+
+ // User must define both resource and path.
+ //
+ if (options.valueOf(DhcpCfg.RESOURCE) == null ||
+ options.valueOf(DhcpCfg.PATH) == null) {
+ String msg = getString("convert_null_datastore_error");
+ throw new IllegalArgumentException(msg);
+ }
+
+ try {
+ setDhcpDatastore(getSvcMgr().getDataStore(
+ options.valueOf(DhcpCfg.RESOURCE)));
+ getDhcpDatastore().setLocation(options.valueOf(DhcpCfg.PATH));
+ getDhcpDatastore().setConfig(
+ options.valueOf(DhcpCfg.RESOURCE_CONFIG));
+ } catch (Throwable e) {
+ // resource will not be valid
+ }
+
+
+ if (getDhcpDatastore() == null || !getDhcpDatastore().isEnabled()) {
+ Object [] arguments = new Object[1];
+ arguments[0] = getDhcpDatastore().getResource();
+ printErrMessage(getString("convert_invalid_resource_error"),
+ arguments);
+ return (DhcpCfg.FAILURE);
+ }
+
+ // Should we delete the dhcptab and the network tables after
+ // they have been converted?
+ //
+ boolean deleteTables = !options.isSet(DhcpCfg.KEEP_TABLES);
+
+ // Get the old configuration values.
+ //
+ DhcpdOptions dhcpdOptions = null;
+ try {
+ dhcpdOptions = getSvcMgr().readDefaults();
+ } catch (Throwable e) {
+ Object [] arguments = new Object[1];
+ arguments[0] = getMessage(e);
+ printErrMessage(getString("convert_conf_read_error"), arguments);
+ return (DhcpCfg.FAILURE);
+ }
+
+ // If the source, destination, and version are all the same
+ // then there is nothing to do ... report it as an error.
+ //
+ DhcpDatastore oldDatastore = dhcpdOptions.getDhcpDatastore();
+ if (getDhcpDatastore().equals(oldDatastore)) {
+ printErrMessage(getString("convert_same_datastore_error"));
+ return (DhcpCfg.FAILURE);
+ }
+
+ // Confirm?
+ //
+ if (!options.isSet(DhcpCfg.FORCE)) {
+ printMessage(getString("convert_explanation"));
+ String confirmationMsg = getString("convert_confirmation");
+ String affirmative = getString("affirmative");
+ String negative = getString("negative");
+ boolean doit = Console.promptUser(confirmationMsg, affirmative,
+ negative, true);
+ if (!doit) {
+ return (DhcpCfg.FAILURE);
+ }
+ }
+
+ // Create the location if it does not exist.
+ //
+ try {
+ getSvcMgr().makeLocation(getDhcpDatastore());
+ } catch (ExistsException e) {
+ // this is o.k.
+ } catch (Throwable e) {
+ Object [] arguments = new Object[1];
+ arguments[0] = getDhcpDatastore().getLocation();
+ printErrMessage(getString("convert_make_location_error"),
+ arguments);
+ return (DhcpCfg.FAILURE);
+ }
+
+ // Shut down the server if it is running
+ //
+ try {
+ if (getSvcMgr().isServerRunning()) {
+ getSvcMgr().shutdown();
+ printMessage(getString("convert_shutdown_progress"));
+ } else {
+ printMessage(getString("convert_no_shutdown_progress"));
+ }
+ } catch (Throwable e) {
+ Object [] arguments = new Object[1];
+ arguments[0] = getMessage(e);
+ printErrMessage(getString("convert_shutdown_error"), arguments);
+ }
+
+ // Convert the dhcptab.
+ //
+ try {
+ getDhcptabMgr().cvtDhcptab(getDhcpDatastore());
+ printMessage(getString("convert_dhcptab_progress"));
+ } catch (Throwable e) {
+ Object [] arguments = new Object[1];
+ arguments[0] = getMessage(e);
+ printErrMessage(getString("convert_dhcptab_error"), arguments);
+ deleteTables = false;
+ }
+
+ // Go get a list of the network tables to convert.
+ //
+ Network[] networks = new Network[0];
+ try {
+ networks = getNetMgr().getNetworks(oldDatastore);
+ if (networks == null) {
+ networks = new Network[0];
+ }
+ } catch (Throwable e) {
+ Object [] arguments = new Object[1];
+ arguments[0] = getMessage(e);
+ printErrMessage(getString("convert_get_nets_error"), arguments);
+ deleteTables = false;
+ }
+
+ // Convert the network tables
+ //
+ for (int i = 0; i < networks.length; ++i) {
+ String netString = networks[i].toString();
+ try {
+ getNetMgr().cvtNetwork(netString, getDhcpDatastore());
+ Object [] arguments = new Object[1];
+ arguments[0] = netString;
+ printMessage(getString("convert_network_progress"), arguments);
+ } catch (Throwable e) {
+ Object [] arguments = new Object[2];
+ arguments[0] = netString;
+ arguments[1] = getMessage(e);
+ printErrMessage(getString("convert_network_error"),
+ arguments);
+ }
+ }
+
+ dhcpdOptions.setDhcpDatastore(getDhcpDatastore());
+ try {
+ getSvcMgr().writeDefaults(dhcpdOptions);
+ printMessage(getString("convert_conf_update_progress"));
+ } catch (Throwable e) {
+ Object [] arguments = new Object[1];
+ arguments[0] = getMessage(e);
+ printErrMessage(getString("convert_conf_write_error"), arguments);
+ deleteTables = false;
+ }
+
+ if (deleteTables) {
+ // Delete the network tables
+ //
+ for (int i = 0; i < networks.length; ++i) {
+ String netString = networks[i].toString();
+ try {
+ getNetMgr().deleteNetwork(netString, false, false,
+ oldDatastore);
+ Object [] arguments = new Object[1];
+ arguments[0] = netString;
+ printMessage(getString("convert_delete_network_progress"),
+ arguments);
+ } catch (Throwable e) {
+ Object [] arguments = new Object[2];
+ arguments[0] = netString;
+ arguments[1] = getMessage(e);
+ printErrMessage(getString("convert_delete_network_error"),
+ arguments);
+ }
+ }
+
+ // Delete the dhcptab
+ //
+ try {
+ getDhcptabMgr().deleteDhcptab(oldDatastore);
+ printMessage(getString("convert_delete_dhcptab_progress"));
+ } catch (Throwable e) {
+ Object [] arguments = new Object[1];
+ arguments[0] = getMessage(e);
+ printErrMessage(getString("convert_delete_dhcptab_error"),
+ arguments);
+ }
+ }
+
+ // Start it up.
+ //
+ if (dhcpdOptions.isDaemonEnabled()) {
+ try {
+ getSvcMgr().startup();
+ printMessage(getString("convert_startup_progress"));
+ } catch (Throwable e) {
+ Object [] arguments = new Object[1];
+ arguments[0] = getMessage(e);
+ printErrMessage(getString("convert_startup_error"), arguments);
+ }
+ }
+
+ return (DhcpCfg.SUCCESS);
+
+ } // execute
+
+} // ConvertDataStore
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/DhcpCfg.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/DhcpCfg.java
new file mode 100644
index 0000000000..1e1d76f52c
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/DhcpCfg.java
@@ -0,0 +1,243 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2001-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.cli.dhcpconfig;
+
+import com.sun.dhcpmgr.cli.common.*;
+
+import java.lang.IllegalArgumentException;
+import java.text.MessageFormat;
+
+/**
+ * This class represents the entry point to the DHCP CLI dhcp configuration
+ * administration.
+ */
+public class DhcpCfg
+ extends DhcpCliProgram {
+
+ /**
+ * The program signature.
+ */
+ public static final String SIGNATURE = "dhcpconfig: ";
+
+ /**
+ * The valid options for all DhcpCfg administration.
+ */
+ private static String optString =
+ "DUCnfxbkgh;I:R:N:X:r;p:u:l:d;a:m:t:y:s:o:P;Seq";
+
+ public static final int CONFIGURE_DHCP = 'D';
+ public static final int CONFIGURE_BOOTP = 'R';
+ public static final int UNCONFIGURE_DHCP = 'U';
+ public static final int CONFIGURE_NETWORK = 'N';
+ public static final int CONVERT_DATA_STORE = 'C';
+ public static final int EXPORT_DATA = 'X';
+ public static final int IMPORT_DATA = 'I';
+ public static final int CONFIGURE_SERVER_PARAMETER = 'P';
+ public static final int CONFIGURE_SERVICE = 'S';
+
+ public static final int NON_NEGOTIABLE_LEASE = 'n';
+ public static final int FORCE = 'f';
+ public static final int DELETE_DATA = 'x';
+ public static final int DELETE_TABLES = 'x';
+ public static final int KEEP_TABLES = 'k';
+ public static final int POINT_TO_POINT = 'b';
+ public static final int HOSTS_RESOURCE = 'h';
+ public static final int DELETE_HOSTS = 'h';
+ public static final int RESOURCE = 'r';
+ public static final int RESOURCE_CONFIG = 'u';
+ public static final int PATH = 'p';
+ public static final int LEASE_LENGTH = 'l';
+ public static final int DNS_DOMAIN = 'd';
+ public static final int SERVICE_DISABLE = 'd';
+ public static final int DNS_ADDRESSES = 'a';
+ public static final int NIS_ADDRESSES = 'a';
+ public static final int NETWORK_ADDRESSES = 'a';
+ public static final int SUBNET_MASK = 'm';
+ public static final int MACRO_LIST = 'm';
+ public static final int OPTION_LIST = 'o';
+ public static final int ROUTER_ADDRESSES = 't';
+ public static final int NIS_DOMAIN = 'y';
+ public static final int HOSTS_DOMAIN = 'y';
+ public static final int SIGHUP = 'g';
+ public static final int SERVICE_ENABLE = 'e';
+ public static final int SERVICE_REENABLE = 'r';
+ public static final int SERVICE_QUERY = 'q';
+
+ /**
+ * Constructs a dhcpconfig command.
+ * @param args the options to the command.
+ */
+ public DhcpCfg(String args[]) {
+
+ // Set the options.
+ //
+ options = new DhcpCliOptions();
+ this.args = args;
+
+ } // constructor
+
+ /**
+ * Returns the manpage signature for the program.
+ * @return the manpage signature for the program.
+ */
+ public String getManPage() {
+ return "dhcpconfig(1M)";
+ }
+
+ /**
+ * Displays program usage.
+ */
+ public void usage() {
+
+ DhcpCliPrint.printErrMessage(getString("dhcpcfg_usage"));
+
+ } // usage
+
+ /**
+ * Executes the program function.
+ * @return SUCCESS or FAILURE
+ */
+ public int execute() {
+
+ int returnCode = SUCCESS;
+
+ // Get the options and go exec the correct function.
+ //
+ GetOpt getopt = new GetOpt(args, optString);
+ try {
+ int option;
+ while ((option = getopt.getNextOption()) != -1) {
+ processArg(option, getopt.getOptionArg());
+ }
+
+ int lastIndex = getopt.getNextOptionIndex();
+ if (args.length != lastIndex) {
+ Object [] arguments = new Object[1];
+ arguments[0] = args[lastIndex];
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString("dhcpcfg_invalid_arg"));
+ throw new IllegalArgumentException(form.format(arguments));
+ }
+
+ if (function == null) {
+ String msg = getString("dhcpcfg_no_function_error");
+ throw new IllegalArgumentException(msg);
+ }
+
+ function.setOptions(options);
+ function.setStandardOptions();
+ returnCode = function.execute();
+
+ } catch (IllegalArgumentException e) {
+ StringBuffer msg = new StringBuffer(SIGNATURE);
+ msg.append(DhcpCliFunction.getMessage(e));
+ DhcpCliPrint.printErrMessage(msg.toString());
+ DhcpCliPrint.printErrMessage("");
+ usage();
+ returnCode = FAILURE;
+ } catch (Throwable e) {
+ StringBuffer msg = new StringBuffer(SIGNATURE);
+ msg.append(DhcpCliFunction.getMessage(e));
+ DhcpCliPrint.printErrMessage(msg.toString());
+ returnCode = FAILURE;
+ }
+
+ return (returnCode);
+
+ } // execute
+
+ /**
+ * Processes one program argument.
+ * @param option the option flag
+ * @param value the option value(if any)
+ * @exception IllegalArgumentException if an invalid argument was entered
+ */
+ public void processArg(int option, String value)
+ throws IllegalArgumentException {
+
+ switch (option) {
+ case CONFIGURE_DHCP:
+ setFunction(new ConfigureDhcp());
+ break;
+ case CONFIGURE_BOOTP:
+ setFunction(new ConfigureBootp(value));
+ break;
+ case UNCONFIGURE_DHCP:
+ setFunction(new UnconfigureDhcp());
+ break;
+ case CONFIGURE_NETWORK:
+ setFunction(new ConfigureNetwork(value));
+ break;
+ case CONVERT_DATA_STORE:
+ setFunction(new ConvertDataStore());
+ break;
+ case EXPORT_DATA:
+ setFunction(new ExportData(value));
+ break;
+ case IMPORT_DATA:
+ setFunction(new ImportData(value));
+ break;
+ case CONFIGURE_SERVER_PARAMETER:
+ setFunction(new ServerParameter(value));
+ break;
+ case CONFIGURE_SERVICE:
+ setFunction(new ConfigureService());
+ break;
+ default:
+ options.setOption(option, value);
+ }
+
+ } // processArg
+
+ /**
+ * Returns a localized string for this function
+ * @param key the resource bundle string identifier
+ * @return string from resource bundle.
+ */
+ public String getString(String key) {
+
+ return ResourceStrings.getString(key);
+
+ } // getString
+
+ /**
+ * The entry point for the program.
+ * @param args the program arguments
+ */
+ public static void main(String[] args) {
+
+ DhcpCfg dhcpconfig = new DhcpCfg(args);
+ int returnCode = DhcpCfg.FAILURE;
+ if (dhcpconfig.isValidUser()) {
+ returnCode = dhcpconfig.execute();
+ }
+ System.exit(returnCode);
+
+ } // main
+
+} // DhcpConfig
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/DhcpCfgFunction.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/DhcpCfgFunction.java
new file mode 100644
index 0000000000..f9c568267b
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/DhcpCfgFunction.java
@@ -0,0 +1,116 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.cli.dhcpconfig;
+
+import java.text.MessageFormat;
+
+import com.sun.dhcpmgr.cli.common.DhcpCliFunction;
+import com.sun.dhcpmgr.cli.common.DhcpCliPrint;
+import com.sun.dhcpmgr.data.DhcpdOptions;
+
+/**
+ * Abstract class implemented by all the dhcpconfig "function" classes.
+ */
+public abstract class DhcpCfgFunction
+ extends DhcpCliFunction {
+
+ /**
+ * Returns a localized string for this function
+ * @param key the resource bundle string identifier
+ * @return string from resource bundle.
+ */
+ public String getString(String key) {
+
+ return ResourceStrings.getString(key);
+
+ } // getString
+
+ /**
+ * Determines whether or not the DHCP service is configured.
+ * @return true if configured, false if not.
+ */
+ public boolean isServerConfigured() {
+
+ boolean result = false;
+
+ try {
+ DhcpdOptions opts = getSvcMgr().readDefaults();
+ if (!opts.isRelay()) {
+ result = true;
+ }
+ } catch (Throwable e) {
+ // nothing to do.
+ }
+
+ if (!result) {
+ printErrMessage(getString("dhcpcfg_func_not_configured_error"));
+ }
+
+ return (result);
+
+ } // isServerConfigured
+
+ /**
+ * Prints a message to the console.
+ * @param msg the message to print.
+ */
+ public void printMessage(String msg) {
+ DhcpCliPrint.printMessage(msg);
+ } // printMessage
+
+ /**
+ * Prints a message to the console..
+ * @param msg the message to print.
+ */
+ public void printMessage(String msg, Object [] arguments) {
+ MessageFormat form = new MessageFormat(msg);
+ DhcpCliPrint.printMessage(form.format(arguments));
+ } // printMessage
+
+ /**
+ * Prints an error message.
+ * @param msg the message to print.
+ */
+ public void printErrMessage(String msg) {
+ StringBuffer fullmsg = new StringBuffer(DhcpCfg.SIGNATURE);
+ fullmsg.append(msg);
+ DhcpCliPrint.printErrMessage(fullmsg.toString());
+ } // printErrMessage
+
+ /**
+ * Prints an error message.
+ * @param msg the message to print.
+ */
+ public void printErrMessage(String msg, Object [] arguments) {
+ StringBuffer fullmsg = new StringBuffer(DhcpCfg.SIGNATURE);
+ fullmsg.append(msg);
+ MessageFormat form = new MessageFormat(fullmsg.toString());
+ DhcpCliPrint.printErrMessage(form.format(arguments));
+ } // printErrMessage
+
+} // DhcpCfgFunction
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ExportData.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ExportData.java
new file mode 100644
index 0000000000..b4a4a2b163
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ExportData.java
@@ -0,0 +1,276 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2001-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.cli.dhcpconfig;
+
+import java.util.ArrayList;
+import java.util.StringTokenizer;
+import java.text.MessageFormat;
+
+import com.sun.dhcpmgr.cli.common.DhcpCliFunction;
+import com.sun.dhcpmgr.cli.common.DhcpCliPrint;
+import com.sun.dhcpmgr.data.*;
+import com.sun.dhcpmgr.bridge.BridgeException;
+import com.sun.dhcpmgr.bridge.ExistsException;
+
+import com.sun.dhcpmgr.common.ExportController;
+import com.sun.dhcpmgr.common.Exporter;
+
+/**
+ * The main class for the "export move data" functionality of dhcpconfig.
+ */
+public class ExportData extends DhcpCfgFunction implements Exporter {
+
+ /**
+ * The valid options associated with exporting data.
+ */
+ private static final int supportedOptions[] = {
+ DhcpCfg.MACRO_LIST,
+ DhcpCfg.OPTION_LIST,
+ DhcpCfg.NETWORK_ADDRESSES,
+ DhcpCfg.DELETE_DATA,
+ DhcpCfg.FORCE,
+ DhcpCfg.SIGHUP
+ };
+
+ /**
+ * Keyword that may be used to define all options, all macros, or all
+ * network tables.
+ */
+ private static final String ALL = "ALL";
+
+ /**
+ * The name of the export file.
+ */
+ private String exportFile;
+
+ /**
+ * Simple constructor
+ */
+ public ExportData(String exportFile) {
+
+ validOptions = supportedOptions;
+ this.exportFile = exportFile;
+
+ } // constructor
+
+ /**
+ * Returns the option flag for this function.
+ * @returns the option flag for this function.
+ */
+ public int getFunctionFlag() {
+ return (DhcpCfg.EXPORT_DATA);
+ }
+
+ /**
+ * Executes the "export move data" functionality.
+ * @return DhcpCfg.SUCCESS or DhcpCfg.FAILURE
+ */
+ public int execute() {
+
+ // Make sure that server is configured as a DHCP server.
+ //
+ if (!isServerConfigured()) {
+ return (DhcpCfg.FAILURE);
+ }
+
+ // Check the validity of the data store version.
+ if (!isVersionValid(false)) {
+ return (DhcpCfg.FAILURE);
+ }
+
+ // Should export file be overwritten if it exists?
+ boolean force = options.isSet(DhcpCfg.FORCE);
+
+ // Should exported data be deleted or just copied?
+ boolean deleteData = options.isSet(DhcpCfg.DELETE_DATA);
+
+ // Create the export controller
+ ExportController controller = new ExportController(this, getDhcpMgr());
+ controller.setFile(exportFile);
+ // Store user name
+ controller.setUser(System.getProperty("user.name"));
+
+ /*
+ * Get the macro list. The keyword of "ALL" means that all macros in
+ * the dhcptab should be exported.
+ */
+ if (options.isSet(DhcpCfg.MACRO_LIST)) {
+ String macroNames = options.valueOf(DhcpCfg.MACRO_LIST);
+ if (ALL.equals(macroNames)) {
+ controller.setAllMacros();
+ } else {
+ // Parse macro list and give to controller
+ controller.setMacros(argsToArray(macroNames));
+ }
+ }
+
+ /*
+ * Get the option list. Keyword of "ALL" means that all options in the
+ * dhcptab should be exported.
+ */
+ if (options.isSet(DhcpCfg.OPTION_LIST)) {
+ String optionNames = options.valueOf(DhcpCfg.OPTION_LIST);
+ if (ALL.equals(optionNames)) {
+ controller.setAllOptions();
+ } else {
+ controller.setOptions(argsToArray(optionNames));
+ }
+ }
+
+ /*
+ * Get the list of network addresses. If the option is set to the
+ * keyword "ALL", all network tables should be exported.
+ */
+ if (options.isSet(DhcpCfg.NETWORK_ADDRESSES)) {
+ String addrs = options.valueOf(DhcpCfg.NETWORK_ADDRESSES);
+ if (ALL.equals(addrs)) {
+ controller.setAllNetworks();
+ } else {
+ // Parse network list
+ IPAddressList networkAddresses;
+ try {
+ networkAddresses = new IPAddressList(addrs);
+ } catch (ValidationException e) {
+ printErrMessage(getMessage(e));
+ printErrMessage(getString("export_abort"));
+ return (DhcpCfg.FAILURE);
+ }
+
+ // Now ensure all networks specified exist
+ ArrayList netList = new ArrayList();
+ try {
+ Network [] nets = getNetMgr().getNetworks();
+
+ for (int i = 0;
+ !networkAddresses.isEmpty() && i < nets.length;
+ ++i) {
+ int index = networkAddresses.indexOf(
+ nets[i].getNetworkNumber());
+ // Found; remove from search list, add to export list
+ if (index != -1) {
+ netList.add(nets[i]);
+ networkAddresses.remove(index);
+ }
+ }
+ } catch (BridgeException e) {
+ e.printStackTrace();
+ }
+
+ if (!networkAddresses.isEmpty()) {
+ // One of the networks was not valid
+ System.err.print(networkAddresses.firstElement());
+ System.err.println(" is not a valid network");
+ return (DhcpCfg.FAILURE);
+ }
+
+ controller.setNetworks(
+ (Network [])netList.toArray(new Network[0]));
+ }
+ }
+
+ // Do the export
+ try {
+ // result should affect exit status once that's implemented
+ if (!controller.exportData(deleteData, force)) {
+ return (DhcpCfg.FAILURE);
+ }
+ } catch (ExistsException e) {
+ // File already exists, print error and exit
+ Object [] arguments = new Object[1];
+ arguments[0] = exportFile;
+ printErrMessage(getString("export_file_exist_error"), arguments);
+ printErrMessage(getString("export_abort"));
+ return (DhcpCfg.FAILURE);
+ }
+
+ // Signal server if requested by user
+ try {
+ if (options.isSet(DhcpCfg.SIGHUP)) {
+ getSvcMgr().reload();
+ }
+ } catch (Throwable e) {
+ printErrMessage(getString("sighup_failed"));
+ return (DhcpCfg.FAILURE);
+ }
+
+ return (DhcpCfg.SUCCESS);
+
+ } // execute
+
+ /**
+ * Convert a comma-separated list of arguments to an array of tokens
+ */
+ private String [] argsToArray(String arg) {
+ ArrayList argList = new ArrayList();
+ StringTokenizer st = new StringTokenizer(arg, ",");
+ while (st.hasMoreTokens()) {
+ argList.add(st.nextToken());
+ }
+ return (String [])argList.toArray(new String[0]);
+ }
+
+ /**
+ * Initialize progress. A no-op for us.
+ */
+ public void initializeProgress(int length) {
+ // Do nothing
+ }
+
+ /**
+ * Display progress. Just print the message.
+ */
+ public void updateProgress(int done, String message) {
+ printMessage(message);
+ }
+
+ /**
+ * Display an error.
+ */
+ public void displayError(String message) {
+ Object [] arguments = new Object[1];
+ arguments[0] = message;
+ printErrMessage(getString("export_err_message"), arguments);
+ }
+
+ /**
+ * Display a set of errors during export
+ */
+ public void displayErrors(String msg, String label, ActionError [] errs) {
+ printErrMessage(msg);
+ Object [] args = new Object[3];
+ args[0] = label;
+ MessageFormat form =
+ new MessageFormat(getString("export_action_error"));
+ for (int i = 0; i < errs.length; ++i) {
+ args[1] = errs[i].getName();
+ args[2] = errs[i].getException().getMessage();
+ printErrMessage(form.format(args));
+ }
+ }
+
+} // ExportData
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/IPAddressList.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/IPAddressList.java
new file mode 100644
index 0000000000..e5b18c75fb
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/IPAddressList.java
@@ -0,0 +1,112 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.cli.dhcpconfig;
+
+import com.sun.dhcpmgr.data.IPAddress;
+import com.sun.dhcpmgr.data.ValidationException;
+
+import java.util.Vector;
+import java.util.Enumeration;
+import java.util.StringTokenizer;
+import java.text.MessageFormat;
+
+/**
+ * Class that builds a vector of IPAddress objects.
+ */
+public class IPAddressList extends Vector {
+
+ /**
+ * Construct a IPAddressList from a String list of IP addresses
+ * and/or host names.
+ * @param addresses a String list of IP addresses
+ */
+ public IPAddressList(String addresses) throws ValidationException {
+
+ removeAllElements();
+
+ if (addresses == null) {
+ return;
+ }
+
+ StringTokenizer st = new StringTokenizer(addresses, ",");
+ while (st.hasMoreTokens()) {
+ String address = st.nextToken();
+ try {
+ addElement(new IPAddress(address.trim()));
+ } catch (ValidationException e) {
+ Object [] args = new Object[1];
+ args[0] = address;
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString("invalid_ip_address"));
+ throw new ValidationException(form.format(args));
+ }
+ }
+ } // constructor
+
+ /**
+ * Construct a IPAddressList from an array of IPAddress objects.
+ * @param addresses array of IPAddress objects
+ */
+ public IPAddressList(IPAddress [] addresses) {
+
+ removeAllElements();
+
+ if (addresses == null) {
+ return;
+ }
+
+ for (int i = 0; i < addresses.length; i++) {
+ addElement(addresses[i]);
+ }
+ } // constructor
+
+ /**
+ * Returns an array of IP Addresses.
+ * @return an array of IP Addresses.
+ */
+ public IPAddress [] toIPAddressArray() {
+ return ((IPAddress [])toArray(new IPAddress[size()]));
+ } // toIPAddressArray
+
+ /**
+ * Returns a comma separated list of IP Addresses.
+ * @return a comma separated list of IP Addresses.
+ */
+ public String toString() {
+ StringBuffer b = new StringBuffer();
+ Enumeration en = elements();
+ while (en.hasMoreElements()) {
+ if (b.length() != 0) {
+ b.append(',');
+ }
+ b.append(((IPAddress)en.nextElement()).getHostAddress());
+ }
+ return b.toString();
+ } // toString
+
+} // IPAddressList
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ImportData.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ImportData.java
new file mode 100644
index 0000000000..44455fde7f
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ImportData.java
@@ -0,0 +1,147 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2001-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.cli.dhcpconfig;
+
+import java.text.MessageFormat;
+
+import com.sun.dhcpmgr.cli.common.DhcpCliFunction;
+import com.sun.dhcpmgr.cli.common.DhcpCliPrint;
+import com.sun.dhcpmgr.data.*;
+import com.sun.dhcpmgr.bridge.BridgeException;
+import com.sun.dhcpmgr.bridge.ExistsException;
+
+import com.sun.dhcpmgr.common.Importer;
+import com.sun.dhcpmgr.common.ImportController;
+
+/**
+ * The main class for the "import move data" functionality of dhcpconfig.
+ */
+public class ImportData extends DhcpCfgFunction implements Importer {
+
+ /**
+ * The valid options associated with importing data.
+ */
+ private static final int supportedOptions[] = {
+ DhcpCfg.FORCE,
+ DhcpCfg.SIGHUP
+ };
+
+ /**
+ * The name of the import file.
+ */
+ private String importFile;
+
+ /**
+ * Simple constructor
+ */
+ public ImportData(String importFile) {
+
+ validOptions = supportedOptions;
+ this.importFile = importFile;
+
+ } // constructor
+
+ /**
+ * Returns the option flag for this function.
+ * @returns the option flag for this function.
+ */
+ public int getFunctionFlag() {
+ return (DhcpCfg.IMPORT_DATA);
+ }
+
+ /**
+ * Executes the "import move data" functionality.
+ * @return DhcpCfg.SUCCESS or DhcpCfg.FAILURE
+ */
+ public int execute() {
+
+ // Make sure that server is configured as a DHCP server.
+ //
+ if (!isServerConfigured()) {
+ return (DhcpCfg.FAILURE);
+ }
+
+ // Check the validity of the data store version.
+ //
+ if (!isVersionValid(false)) {
+ return (DhcpCfg.FAILURE);
+ }
+
+ // Shall we overwrite any existing, conflicting data?
+ boolean force = options.isSet(DhcpCfg.FORCE);
+
+ // Create import controller and do the import
+ ImportController controller = new ImportController(this, getDhcpMgr());
+ controller.setFile(importFile);
+ if (!controller.importData(force)) {
+ return (DhcpCfg.FAILURE);
+ }
+
+ // Signal server if user requested
+ try {
+ if (options.isSet(DhcpCfg.SIGHUP)) {
+ getSvcMgr().reload();
+ }
+ } catch (Throwable e) {
+ printErrMessage(getString("sighup_failed"));
+ return (DhcpCfg.FAILURE);
+ }
+
+ return (DhcpCfg.SUCCESS);
+
+ } // execute
+
+ public void initializeProgress(int length) {
+ // Do nothing
+ }
+
+ public void updateProgress(int done, String message) {
+ // Just print the message
+ printMessage(message);
+ }
+
+ public void displayError(String message) {
+ Object [] arguments = new Object[1];
+ arguments[0] = message;
+ printErrMessage(getString("import_error_msg"), arguments);
+ }
+
+ public void displayErrors(String msg, String label, ActionError [] errs) {
+ printErrMessage(msg);
+ String [] args = new String[3];
+ args[0] = label;
+ MessageFormat form =
+ new MessageFormat(getString("import_action_error"));
+ for (int i = 0; i < errs.length; ++i) {
+ args[1] = errs[i].getName();
+ args[2] = errs[i].getException().getMessage();
+ printErrMessage(form.format(args));
+ }
+ }
+
+} // ImportData
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/Makefile b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/Makefile
new file mode 100644
index 0000000000..155552263d
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/Makefile
@@ -0,0 +1,86 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2001-2002 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/Makefile
+#
+
+# Place high-level classes first to minimize build time.
+CLASSFILES = DhcpCfg.class \
+ ConfigureBootp.class \
+ ConfigureDhcp.class \
+ ConfigureNetwork.class \
+ ConvertDataStore.class \
+ ExportData.class \
+ IPAddressList.class \
+ ImportData.class \
+ UnconfigureDhcp.class \
+ ServerParameter.class \
+ ConfigureService.class \
+ DhcpCfgFunction.class \
+ ResourceStrings.class
+
+
+include $(SRC)/Makefile.master
+
+CLASSPATH= $(SRC)/cmd/cmd-inet/usr.sadm/dhcpmgr
+
+JAVAFILES = $(CLASSFILES:.class=.java)
+
+MSGDIR= $(ROOT)/usr/share/lib/locale/com/sun/dhcpmgr/cli/dhcpconfig
+MSGDIRS = $(ROOT)/usr/share/lib/locale \
+ $(ROOT)/usr/share/lib/locale/com \
+ $(ROOT)/usr/share/lib/locale/com/sun \
+ $(ROOT)/usr/share/lib/locale/com/sun/dhcpmgr \
+ $(ROOT)/usr/share/lib/locale/com/sun/dhcpmgr/cli \
+ $(MSGDIR)
+
+MSGFILES= ResourceBundle.properties
+MSGS= $(MSGFILES:%=$(MSGDIR)/%)
+
+CLEANFILES= *.class
+CLOBBERFILES=
+
+.KEEP_STATE:
+
+all: $(CLASSFILES)
+
+install: all
+
+_msg: $(MSGDIRS) $(MSGS)
+
+$(MSGDIR)/%: %
+ $(INS.file)
+
+$(MSGDIRS):
+ $(INS.dir)
+
+lint:
+
+clean:
+ $(RM) $(CLEANFILES)
+
+clobber: clean
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ResourceBundle.properties b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ResourceBundle.properties
new file mode 100644
index 0000000000..0abeffd883
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ResourceBundle.properties
@@ -0,0 +1,179 @@
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# ConfigureDhcp & ConfigureBootp messages.
+#
+config_dhcp_configured_error=Error - DHCP service is already configured on this server.
+config_bootp_configured_error=Error - server is already configured as a BOOTP relay agent.
+config_null_datastore_error=Error - cannot configure without resource and path options.
+config_invalid_resource_error=Error - {0} is not a valid resource.
+config_make_location_error=Error - creating location: {0}.
+config_requires_hosts_domain_error=Error - host resource requires a host domain.
+config_hosts_domain_ignored_error=Error - host domain does not make sense for a host resource type - {0}
+config_invalid_hosts_resource_error=Error - host resource is invalid - {0}
+config_requires_hosts_resource_error=Error - host domain requires a host resource.
+config_lease_error=Error - the lease value specified is not valid.
+config_lease_zero_error=Error - a lease value of zero is not valid.
+config_dns_error=Error - DNS domain or server information is incomplete.
+config_dns_server_error=Error - building the DNS server list. {0}
+config_startup_progress=DHCP server started.
+config_startup_error=Error - starting the DHCP server. {0}
+config_create_conf_progress=Created DHCP configuration file.
+config_writing_conf_error=Error - could not write to the DHCP configuration file. {0}
+config_dhcptab_progress=Created dhcptab.
+config_dhcptab_exists_progress=Dhcptab already exists.
+config_dhcptab_error=Error - creating dhcptab. {0}
+config_locale_progress=Added "Locale" macro to dhcptab.
+config_locale_error=Error - adding locale macro to dhcptab. {0}
+config_server_macro_progress=Added server macro to dhcptab - {0}.
+config_server_macro_error=Error - creating server macro for server {0}. {1}
+
+#
+# ConfigureNetwork messages.
+#
+cfgnet_invalid_network_error=Error - {0} is not a valid network.
+cfgnet_invalid_ip_error=Error - {0} is an invalid address.
+cfgnet_nis_error=Error - NIS domain or server information is incomplete.
+cfgnet_nis_server_error=Error - building the NIS server list. {0}
+cfgnet_network_macro_progress=Added network macro to dhcptab - {0}.
+cfgnet_network_macro_error=Error - creating network macro. {0}
+cfgnet_network_table_progress=Created network table.
+cfgnet_network_table_error=Error - creating network table. {0}
+
+#
+# UnconfigureDhcp messages.
+#
+unconfigure_read_conf_error=Error - reading DHCP configuration file. {0}
+unconfigure_shutdown_progress=DHCP server shutdown.
+unconfigure_no_shutdown_progress=DHCP server not running.
+unconfigure_shutdown_error=Error - shutting down DHCP server. {0}
+unconfigure_server_macro_progress=Deleted the server macro from dhcptab.
+unconfigure_server_macro_error=Error - deleting server macro. {0}
+unconfigure_get_nets_error=Error - determining list of networks. {0}
+unconfigure_network_progress=Deleted table for network {0}.
+unconfigure_network_error=Error - deleting table for network {0}. {1}
+unconfigure_dhcptab_progress=Deleted the dhcptab.
+unconfigure_dhcptab_error=Error - deleting dhcptab. {0}
+unconfigure_remove_conf_progress=Deleted the DHCP configuration file.
+unconfigure_remove_conf_error=Error - deleting DHCP configuration file. {0}
+unconfigure_confirmation=Unconfigure will stop the DHCP service and remove the DHCP configuration file.\nAre you SURE you want to unconfigure the DHCP service?
+
+#
+# ConvertDataStore errors.
+#
+convert_null_datastore_error=Error - cannot convert without resource and path options.
+convert_same_datastore_error=Error - the source data store is the same as destination data store.
+convert_invalid_resource_error=Error - {0} is not a valid resource.
+convert_make_location_error=Error - creating location - {0}.
+convert_dhcptab_progress=Converted dhcptab.
+convert_dhcptab_error=Error - converting dhcptab. {0}
+convert_get_nets_error=Error - failure getting list of networks. {0}
+convert_network_progress=Converted table for network {0}.
+convert_network_error=Error - converting table for network {0}. {1}
+convert_conf_read_error=Error - reading DHCP configuration file. {0}
+convert_conf_write_error=Error - could not write to the DHCP configuration file. {0}
+convert_conf_update_progress=Updated DHCP configuration file with new resource and path.
+convert_delete_network_progress=Deleted old table for network {0}.
+convert_delete_network_error=Error - deleting old table for network {0}. {1}
+convert_delete_dhcptab_progress=Deleted old dhcptab table.
+convert_delete_dhcptab_error=Error - deleting old dhcptab table. {0}
+convert_explanation=\nConverting your data stores will result in your DHCP service being\nstopped, the data stores converted, and the DHCP service restarted.\n
+convert_confirmation=Are you SURE you want to convert the DHCP data stores?
+convert_shutdown_progress=DHCP server shutdown.
+convert_no_shutdown_progress=DHCP server not running.
+convert_shutdown_error=Error - shutting down DHCP server. {0}
+convert_startup_progress=DHCP server started.
+convert_startup_error=Error - starting the DHCP server. {0}
+
+#
+# ExportData messages.
+#
+export_err_message=Export error - {0}
+export_abort=Export aborted!
+export_file_exist_error=Error - file already exists and will not be overwritten - {0}
+export_action_error={0} {1} - {2}
+
+#
+# ImportData messages.
+#
+import_error_msg=Import error - {0}
+import_action_error={0} {1} - {2}
+
+#
+# DhcpCfgFunction messages.
+#
+dhcpcfg_func_not_configured_error=Error - DHCP service is not configured on this server.\nMust execute 'dhcpconfig -D' first.
+
+#
+# Common messages.
+#
+invalid_ip_address={0} is not a valid IP address.
+affirmative=Y
+negative=N
+sighup_failed=Unable to signal the daemon to reload the dhcptab
+
+#
+# DhcpCfg messages.
+#
+dhcpcfg_invalid_arg=Invalid argument - {0}
+dhcpcfg_no_function_error=Error - must specify one of 'D', 'R', 'U', 'N', 'C', 'X', 'I', 'P' or 'S'.
+dhcpcfg_usage=dhcpconfig (options)\n\nWhere (options) is one of:\n\n -D Configure the DHCP service Sub-options:\n [-r (resource)]\n [-p (path)]\n [-u (uninterpreted data)]\n [-h (host resource)]\n [-y (host domain)]\n [-l (lease length)]\n [-n] Leases are not negotiable\n [-d (DNS domain)]\n [-a (DNS servers)]\n\n -R (server addresses) Configure BOOTP relay service.\n\n -U UnConfigure DHCP or BOOTP relay service. Sub-options:\n [-f] No confirmation prompt\n [-x] Delete dhcptab and network tables\n [-h] Delete host entries\n\n -N (network address) Configure network. Sub-options:\n [-m (subnet mask)]\n [-t (router addresses)]\n [-y (NIS domain)]\n [-a (NIS addreses)]\n [-b] Network is point-to-point\n [-g] Signal daemon\n\n -C Convert to new data store. Sub-options:\n [-r (resource)]\n [-p (path)]\n [-u (uninterpreted data)]\n [-f] No confirmation prompt\n [-k] Do not delete original files\n\n -X (export filename) Export data. Sub-options:\n [-m (macro list)]\n [-o (option list)]\n [-a (network addresses)]\n [-f] Overwrite an existing export file\n [-x] Delete exported data\n [-g] Signal daemon\n\n -I (import filename) Import data. Sub-options:\n [-f] Overwrite existing data\n [-g] Signal daemon\n\n -P Configure DHCP server options. Sub-options:\n [-k (option)]\n [-v (value)]\n\n -S Control the DHCP server. Sub-options:\n [-e] Enable and start the DHCP server\n [-d] Disable and stop the DHCP server\n [-r] Re-enable the DHCP server\n [-q] Query the DHCP server status\n
+
+#
+# ServerParameter errors.
+#
+server_parameter_keyword_missing_error=Error - server parameter missing.
+server_parameter_failed_read_params_error=Error - failed to read DHCP server parameters.
+server_parameter_failed_write_params_error=Error - failed to write DHCP server parameters.
+server_parameter_keyword_bad_keyword_error=Error - invalid DHCP server parameter {0}.
+server_parameter_keyword_set_read_only_error=Error - the DHCP server parameter {0} cannot be changed.
+server_parameter_keyword_set_bad_value_error=Error - the value {1} is unacceptable for DHCP server parameter {0}.
+server_parameter_keyword_delete_read_only_error=Error - the DHCP server parameter {0} cannot be deleted.
+server_parameter_keyword_getall_bad_keyword_error=Error - invalid DHCP server parameter {0}.
+server_parameter_keyword_not_set_error=DHCP server parameter {0} is not set.
+server_parameter_get_value={0}
+server_parameter_get_keyword_value={0} {1}
+
+#
+# ConfigureService errors.
+#
+config_service_failed_read_params_error=Error - failed to read DHCP server parameters.
+config_service_failed_write_params_error=Error - failed to write DHCP server parameters.
+config_service_bad_action_error=Error - must specify one of 's', 'e', 'r' or 'q'.
+config_service_failed_enabled_error=Error - failed to enable DHCP server.
+config_service_failed_disabled_error=Error - failed to disable DHCP server.
+config_service_failed_startup_error=Error - failed to start DHCP server.
+config_service_failed_shutdown_error=Error - failed to stop DHCP server.
+config_service_failed_query_error=Error - failed to query DHCP server.
+config_service_state_startup=DHCP server started.
+config_service_state_shutdown=DHCP server shutdown.
+config_service_state_enabled=DHCP server enabled.
+config_service_state_disabled=DHCP server disabled.
+config_service_state_running=DHCP server running.
+config_service_state_stopped=DHCP server stopped.
+
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ResourceStrings.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ResourceStrings.java
new file mode 100644
index 0000000000..395b9a1e39
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ResourceStrings.java
@@ -0,0 +1,64 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.cli.dhcpconfig;
+
+import java.util.*;
+
+/**
+ * This class provides a convenient method to retrieve resources for
+ * the dhcpconfig package.
+ */
+public class ResourceStrings {
+
+ /**
+ * The handle to the resource bundle for the module.
+ */
+ private static ResourceBundle bundle = null;
+
+ /**
+ * Return a string from the resource bundle.
+ * @param key the key to the resource bundle string.
+ * @return the resource bundle string.
+ */
+ public static String getString(String key) {
+ String msg = null;
+ try {
+ if (bundle == null) {
+ bundle = ResourceBundle.getBundle(
+ "com.sun.dhcpmgr.cli.dhcpconfig.ResourceBundle",
+ Locale.getDefault());
+ }
+ msg = bundle.getString(key);
+ } catch (Throwable e) {
+ msg = new String(key);
+ }
+ return msg;
+
+ } // getString
+
+} // ResourceStrings
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ServerParameter.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ServerParameter.java
new file mode 100644
index 0000000000..6421ff071b
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ServerParameter.java
@@ -0,0 +1,581 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.cli.dhcpconfig;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import com.sun.dhcpmgr.cli.common.GetSubOpt;
+import com.sun.dhcpmgr.cli.common.DhcpCliFunction;
+import com.sun.dhcpmgr.data.DhcpdOptions;
+import com.sun.dhcpmgr.data.DhcpResource;
+import com.sun.dhcpmgr.data.qualifier.*;
+import com.sun.dhcpmgr.bridge.BridgeException;
+
+/**
+ * Functions for handling all dhcpconfig options that manage the DHCP server
+ * parameters.
+ */
+public class ServerParameter extends DhcpCfgFunction {
+
+ /**
+ * Options that this DhcpCfgFunction will accept.
+ */
+ static final int supportedOptions[] = {
+ };
+
+ /**
+ * List of suboptions.
+ */
+ private String subOptions;
+
+ /**
+ * Simple constructor
+ */
+ public ServerParameter(String subOptions) {
+ validOptions = supportedOptions;
+ this.subOptions = subOptions;
+ } // constructor
+
+ /**
+ * Returns the option flag for this function.
+ * @returns
+ * The option flag for this function.
+ */
+ public int getFunctionFlag() {
+ return DhcpCfg.CONFIGURE_SERVER_PARAMETER;
+ } // getFunctionFlag
+
+ /**
+ * Parse and execute the options for this function.
+ * @return
+ * DhcpCfg.SUCCESS for success and DhcpCfg.FAILURE for failure.
+ */
+ public int execute() throws IllegalArgumentException {
+ List actions = new ArrayList();
+
+ if (subOptions == null || subOptions.length() == 0) {
+ actions.add(new ActionGetAll());
+ } else {
+ GetSubOpt getSubOpt = new GetSubOpt(subOptions);
+
+ while (getSubOpt.hasMoreSubOptions()) {
+ String keyword = getSubOpt.getNextSubOption();
+ String value = getSubOpt.getSubOptionArg();
+
+ boolean valueSet = (value != null);
+
+ if (value == null) {
+ actions.add(new ActionGet(keyword));
+ } else {
+ if (value.equals("")) {
+ actions.add(new ActionDelete(keyword));
+ } else {
+ actions.add(new ActionSet(keyword, value));
+ }
+ }
+ }
+ }
+
+ DhcpdOptions dhcpdOptions;
+ boolean atLeastOneActionFailed;
+ Iterator iterator;
+
+ try {
+ dhcpdOptions = getSvcMgr().readDefaults();
+ } catch (BridgeException be) {
+ printErrMessage(
+ getString("server_parameter_failed_read_params_error"));
+ return DhcpCfg.FAILURE;
+ }
+
+ atLeastOneActionFailed = false;
+
+ // Initialise the actions.
+ iterator = actions.iterator();
+ while (iterator.hasNext()) {
+ Action action = (Action) iterator.next();
+
+ if (action.init(dhcpdOptions) != DhcpCfg.SUCCESS) {
+ atLeastOneActionFailed = true;
+ }
+ }
+
+ if (atLeastOneActionFailed) {
+ return DhcpCfg.FAILURE;
+ }
+
+ dhcpdOptions.clearDirty();
+
+ atLeastOneActionFailed = false;
+
+ // Execute the actions.
+ iterator = actions.iterator();
+ while (iterator.hasNext()) {
+ Action action = (Action) iterator.next();
+
+ if (action.execute() != DhcpCfg.SUCCESS) {
+ atLeastOneActionFailed = true;
+ }
+ }
+
+ if (atLeastOneActionFailed) {
+ return DhcpCfg.FAILURE;
+ }
+
+ if (dhcpdOptions.isDirty()) {
+ try {
+ getSvcMgr().writeDefaults(dhcpdOptions);
+ } catch (BridgeException e) {
+ printErrMessage(
+ getString(
+ "server_parameter_failed_write_params_error"));
+ return DhcpCfg.FAILURE;
+ }
+
+ dhcpdOptions.clearDirty();
+ }
+
+ return DhcpCfg.SUCCESS;
+ } // execute
+
+ /**
+ * All functions are carried out through a specific action sub-classed
+ * from this class.
+ */
+ private interface Action {
+ /**
+ * Initialise the action.
+ *
+ * @param dhcpdOptions
+ * The server options that an action manipulates.
+ *
+ * @return
+ * DhcpCfg.SUCCESS for success and DhcpCfg.FAILURE for failure.
+ */
+ public int init(DhcpdOptions dhcpdOptions);
+
+ /**
+ * Execute the action.
+ *
+ * @return
+ * DhcpCfg.SUCCESS for success and DhcpCfg.FAILURE for failure.
+ */
+ public int execute();
+ }
+
+ /**
+ * Shared super class for actions.
+ */
+ private abstract class ActionImpl implements Action {
+
+ /**
+ * The server parameter this action works upon. Could be null.
+ */
+ protected String keyword;
+
+ /**
+ * The value this action works upon. Could be null.
+ */
+ protected String value;
+
+ /**
+ * The qualifier for the server parameter. If the keyword is null
+ * the qualifier will be null. The general rule is that if keyword is
+ * not null, qualifier must not be null. If it is then the keyword
+ * is not a recognised server parameter.
+ */
+ protected Qualifier qualifier;
+
+ /**
+ * Server parameters.
+ */
+ protected DhcpdOptions dhcpdOptions;
+
+ /**
+ * Construct an action for the given server parameter keyword and
+ * value. The constructor will find the appropriate qualifier that
+ * matches the keyword if the keyword is not null. Note that no
+ * checking on the validity of the keyword contents is made at this
+ * point.
+ *
+ * @param keyword
+ * The server parameter this action works upon. Could be null.
+ * @param value
+ * The value this action works upon. Could be null.
+ */
+ protected ActionImpl(String keyword, String value) {
+ this.keyword = keyword;
+ this.value = value;
+ } // constructor
+
+ /**
+ * Get the keyword.
+ *
+ * @return
+ * The keyword this action operates upon.
+ */
+ public String getKeyword() {
+ return keyword;
+ }
+
+ /**
+ * Validate and initialise the action. A sub-classed action is passed
+ * execution control via the doExecute() callback method.
+ *
+ * @return
+ * DhcpCfg.SUCCESS for success and DhcpCfg.FAILURE for failure.
+ */
+ public final int init(DhcpdOptions dhcpdOptions) {
+ if (dhcpdOptions == null) {
+ return DhcpCfg.FAILURE;
+ }
+
+ this.dhcpdOptions = dhcpdOptions;
+
+ if (keyword != null) {
+ qualifier = dhcpdOptions.getQualifier(keyword);
+
+ if (qualifier == null) {
+ Object[] arguments = new Object[1];
+ arguments[0] = keyword;
+ printErrMessage(
+ getString(
+ "server_parameter_keyword_bad_keyword_error"),
+ arguments);
+ return DhcpCfg.FAILURE;
+ }
+ }
+
+ return doInit();
+ } // execute
+
+ /**
+ * Sub-classed action callback method. Once validation has been
+ * performed initialisation is continued in the action sub-class
+ * by calling this method.
+ *
+ * @return
+ * DhcpCfg.SUCCESS for success, otherwise DhcpCfg.FAILURE for failure.
+ */
+ protected abstract int doInit();
+
+ /**
+ * Validate and execute the action. A sub-classed action is passed
+ * execution control via the doExecute() callback method.
+ *
+ * @return
+ * DhcpCfg.SUCCESS for success and DhcpCfg.FAILURE for failure.
+ */
+ public final int execute() {
+ if (dhcpdOptions == null) {
+ return DhcpCfg.FAILURE;
+ }
+
+ return doExecute();
+ } // execute
+
+ /**
+ * Sub-classed action callback method. Once validation has been
+ * performed execution is continued in the action sub-class by calling
+ * this method.
+ *
+ * @return
+ * DhcpCfg.SUCCESS for success, otherwise DhcpCfg.FAILURE for failure.
+ */
+ protected abstract int doExecute();
+ }
+
+ /**
+ * Set a parameter action.
+ */
+ private class ActionSet extends ActionImpl {
+
+ /**
+ * Construct an add action.
+ *
+ * @param keyword
+ * The server parameter this action works upon.
+ * @param value
+ * The value this action works upon.
+ */
+ public ActionSet(String keyword, String value) {
+ super(keyword, value);
+ } // constructor
+
+ protected int doInit() {
+ if (qualifier.isReadOnly()) {
+ Object[] arguments = new Object[1];
+ arguments[0] = keyword;
+ printErrMessage(
+ getString(
+ "server_parameter_keyword_set_read_only_error"),
+ arguments);
+ return DhcpCfg.FAILURE;
+ }
+
+ QualifierType qualifierType = qualifier.getType();
+
+ if (qualifierType.parseValue(value) == null) {
+ Object[] arguments = new Object[2];
+ arguments[0] = keyword;
+ arguments[1] = value;
+ printErrMessage(
+ getString(
+ "server_parameter_keyword_set_bad_value_error"),
+ arguments);
+ return DhcpCfg.FAILURE;
+ }
+
+ return DhcpCfg.SUCCESS;
+ }
+
+ /**
+ * Set the parameters value.
+ *
+ * @return
+ * DhcpCfg.SUCCESS for success, otherwise DhcpCfg.FAILURE for failure.
+ */
+ protected int doExecute() {
+ QualifierType qualifierType = qualifier.getType();
+
+ dhcpdOptions.set(keyword, qualifierType.formatValue(value));
+ dhcpdOptions.set(keyword, qualifierType.formatValue(value));
+
+ return DhcpCfg.SUCCESS;
+ } // doExecute
+
+ }
+
+ /**
+ * Get a parameter action.
+ */
+ private class ActionGet extends ActionImpl {
+
+ /**
+ * This field controls the displaying of the keyword in addition
+ * to the keywords value. The default is to not show the keyword.
+ */
+ protected boolean showKeyword = false;
+
+ /**
+ * Construct a get action
+ *
+ * @param keyword
+ * The server parameter this action works upon.
+ */
+ public ActionGet(String keyword) {
+ super(keyword, null);
+ } // constructor
+
+ protected int doInit() {
+ if (!dhcpdOptions.isSet(keyword)) {
+ Object[] arguments = new Object[1];
+ arguments[0] = keyword;
+ printErrMessage(
+ getString("server_parameter_keyword_not_set_error"),
+ arguments);
+
+ return DhcpCfg.FAILURE;
+ }
+
+ return DhcpCfg.SUCCESS;
+ }
+
+ /**
+ * Get the parameters value.
+ *
+ * @return
+ * DhcpCfg.SUCCESS for success, otherwise DhcpCfg.FAILURE for failure.
+ */
+ protected int doExecute() {
+ Object[] arguments;
+
+ if (showKeyword) {
+ arguments = new Object[2];
+ arguments[0] = keyword;
+ arguments[1] = dhcpdOptions.valueOf(keyword);
+ printMessage(
+ getString("server_parameter_get_keyword_value"),
+ arguments);
+ } else {
+ arguments = new Object[1];
+ arguments[0] = dhcpdOptions.valueOf(keyword);
+ printMessage(getString("server_parameter_get_value"),
+ arguments);
+ }
+
+ return DhcpCfg.SUCCESS;
+
+ } // doExecute
+
+ /**
+ * This method controls the displaying of the keyword in addition
+ * to the keywords value.
+ *
+ * @param showKeyword
+ * If true the keyword is shown with the value, otherwise only the
+ * the value is shown.
+ */
+ public void setShowKeyword(boolean showKeyword) {
+ this.showKeyword = showKeyword;
+ }
+
+ }
+
+ /**
+ * Get all parameters action.
+ */
+ private class ActionGetAll extends ActionImpl {
+
+ protected List subActions;
+
+ /**
+ * Construct a get all action.
+ */
+ public ActionGetAll() {
+ super(null, null);
+ subActions = new ArrayList();
+ } // constructor
+
+ protected int doInit() {
+ Object[] parameters = dhcpdOptions.getAll();
+ boolean atLeastOneSubActionFailed = false;
+
+ for (int index = 0; index < parameters.length; index++) {
+ ActionGet subAction;
+ String parameter = ((DhcpResource) parameters[index]).getKey();
+ qualifier = dhcpdOptions.getQualifier(parameter);
+
+ if (qualifier == null) {
+ Object[] arguments = new Object[1];
+ arguments[0] = keyword;
+ printErrMessage(
+ getString(
+ "server_parameter_keyword_bad_keyword_error"),
+ arguments);
+ continue;
+ }
+
+ if (qualifier.isHidden()) {
+ continue;
+ }
+
+ subAction = new ActionGet(parameter);
+ subAction.setShowKeyword(true);
+ subActions.add(subAction);
+
+ if (subAction.init(dhcpdOptions) == DhcpCfg.FAILURE) {
+ atLeastOneSubActionFailed = true;
+ }
+ }
+
+ if (atLeastOneSubActionFailed) {
+ return DhcpCfg.FAILURE;
+ } else {
+ return DhcpCfg.SUCCESS;
+ }
+ }
+
+ /**
+ * Get all the parameters and their values.
+ *
+ * @return
+ * DhcpCfg.SUCCESS for success, otherwise DhcpCfg.FAILURE for failure.
+ */
+ protected int doExecute() {
+ Iterator iterator = subActions.iterator();
+
+ while (iterator.hasNext()) {
+ ActionGet subAction = (ActionGet) iterator.next();
+ String parameter = subAction.getKeyword();
+ qualifier = dhcpdOptions.getQualifier(parameter);
+
+ if (subAction.execute() == DhcpCfg.FAILURE) {
+ return DhcpCfg.FAILURE;
+ }
+ }
+
+ return DhcpCfg.SUCCESS;
+ } // doExecute
+
+ }
+
+ /**
+ * Delete a parameter action.
+ */
+ private class ActionDelete extends ActionImpl {
+
+ /**
+ * Construct a get delete action
+ *
+ * @param keyword
+ * The server parameter this action works upon.
+ */
+ public ActionDelete(String keyword) {
+ super(keyword, null);
+ } // constructor
+
+ protected int doInit() {
+ if (!dhcpdOptions.isSet(keyword)) {
+ Object[] arguments = new Object[1];
+ arguments[0] = keyword;
+ printErrMessage(
+ getString("server_parameter_keyword_not_set_error"),
+ arguments);
+ return DhcpCfg.FAILURE;
+ }
+
+ if (qualifier.isReadOnly()) {
+ Object[] arguments = new Object[1];
+ arguments[0] = keyword;
+ printErrMessage(
+ getString(
+ "server_parameter_keyword_delete_read_only_error"),
+ arguments);
+ return DhcpCfg.FAILURE;
+ }
+
+ return DhcpCfg.SUCCESS;
+ }
+
+ /**
+ * Delete the parameter and its value.
+ *
+ * @return
+ * DhcpCfg.SUCCESS for success, otherwise DhcpCfg.FAILURE for failure.
+ */
+ protected int doExecute() {
+ dhcpdOptions.clear(keyword);
+
+ return DhcpCfg.SUCCESS;
+ } // doExecute
+
+ }
+
+} // ServerParameter
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/UnconfigureDhcp.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/UnconfigureDhcp.java
new file mode 100644
index 0000000000..76f02f2eb4
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/UnconfigureDhcp.java
@@ -0,0 +1,212 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.cli.dhcpconfig;
+
+import com.sun.dhcpmgr.cli.common.DhcpCliFunction;
+import com.sun.dhcpmgr.cli.common.Console;
+import com.sun.dhcpmgr.data.ValidationException;
+import com.sun.dhcpmgr.data.Network;
+import com.sun.dhcpmgr.data.Macro;
+import com.sun.dhcpmgr.data.DhcpdOptions;
+
+/**
+ * The main class for the "unconfigure DHCP server" functionality of
+ * dhcpconfig.
+ */
+public class UnconfigureDhcp extends DhcpCfgFunction {
+
+ /**
+ * The valid options associated with unconfiguring a DHCP server.
+ */
+ static final int supportedOptions[] = {
+ DhcpCfg.FORCE,
+ DhcpCfg.DELETE_TABLES,
+ DhcpCfg.DELETE_HOSTS
+ };
+
+ /**
+ * Constructs a UnconfigureDhcp object.
+ */
+ public UnconfigureDhcp() {
+
+ validOptions = supportedOptions;
+
+ } // constructor
+
+ /**
+ * Returns the option flag for this function.
+ * @returns the option flag for this function.
+ */
+ public int getFunctionFlag() {
+ return (DhcpCfg.UNCONFIGURE_DHCP);
+ }
+
+ /**
+ * Executes the "unconfigure DHCP server" functionality.
+ * @return DhcpCfg.SUCCESS or DhcpCfg.FAILURE
+ */
+ public int execute() {
+
+ // Confirm?
+ //
+ if (!options.isSet(DhcpCfg.FORCE)) {
+ String confirmationMsg = getString("unconfigure_confirmation");
+ String affirmative = getString("affirmative");
+ String negative = getString("negative");
+ boolean doit = Console.promptUser(confirmationMsg, affirmative,
+ negative, true);
+ if (!doit) {
+ return (DhcpCfg.FAILURE);
+ }
+ }
+
+ // Retrieve the configuration values from the server.
+ //
+ boolean isRelay = false;
+ try {
+ DhcpdOptions opts = getSvcMgr().readDefaults();
+ isRelay = opts.isRelay();
+ } catch (Throwable e) {
+ Object [] arguments = new Object[1];
+ arguments[0] = getMessage(e);
+ printErrMessage(getString("unconfigure_read_conf_error"),
+ arguments);
+ return (DhcpCfg.FAILURE);
+ }
+
+ // Shut down the server if it is running
+ //
+ try {
+ if (getSvcMgr().isServerRunning()) {
+ getSvcMgr().shutdown();
+ printMessage(getString("unconfigure_shutdown_progress"));
+ } else {
+ printMessage(getString("unconfigure_no_shutdown_progress"));
+ }
+ } catch (Throwable e) {
+ Object [] arguments = new Object[1];
+ arguments[0] = getMessage(e);
+ printErrMessage(getString("unconfigure_shutdown_error"),
+ arguments);
+ }
+
+ // Should we delete the dhcptab and the network tables?
+ //
+ boolean deleteTables = options.isSet(DhcpCfg.DELETE_TABLES);
+
+ // Should we delete the host entries?
+ //
+ boolean deleteHosts = options.isSet(DhcpCfg.DELETE_HOSTS);
+
+ // If this server is just acting as a relay then we don't need to
+ // clean up the dhcptab or the networks.
+ //
+ if (!isRelay) {
+ // Delete the server macro.
+ //
+ try {
+ Macro serverMacro =
+ new Macro(getSvcMgr().getShortServerName());
+ getDhcptabMgr().deleteRecord(serverMacro, false);
+ printMessage(getString("unconfigure_server_macro_progress"));
+ } catch (Throwable e) {
+ Object [] arguments = new Object[1];
+ arguments[0] = getMessage(e);
+ printErrMessage(getString("unconfigure_server_macro_error"),
+ arguments);
+ }
+
+ // Delete the dhcptab and the network tables if requested.
+ //
+ if (deleteTables) {
+ // Go get a list of the network tables to delete.
+ //
+ Network[] networks = new Network[0];
+ try {
+ networks = getNetMgr().getNetworks();
+ if (networks == null) {
+ networks = new Network[0];
+ }
+ } catch (Throwable e) {
+ Object [] arguments = new Object[1];
+ arguments[0] = getMessage(e);
+ printErrMessage(getString("unconfigure_get_nets_error"),
+ arguments);
+ }
+
+ // Delete the network tables
+ //
+ for (int i = 0; i < networks.length; ++i) {
+ String netString = networks[i].toString();
+ try {
+ getNetMgr().deleteNetwork(netString, false,
+ deleteHosts);
+ Object [] arguments = new Object[1];
+ arguments[0] = netString;
+ printMessage(getString("unconfigure_network_progress"),
+ arguments);
+ } catch (Throwable e) {
+ Object [] arguments = new Object[2];
+ arguments[0] = netString;
+ arguments[1] = getMessage(e);
+ printErrMessage(getString("unconfigure_network_error"),
+ arguments);
+ }
+ }
+
+ // Delete the dhcptab
+ //
+ try {
+ getDhcptabMgr().deleteDhcptab();
+ printMessage(getString("unconfigure_dhcptab_progress"));
+ } catch (Throwable e) {
+ Object [] arguments = new Object[1];
+ arguments[0] = getMessage(e);
+ printErrMessage(getString("unconfigure_dhcptab_error"),
+ arguments);
+ }
+ }
+ }
+
+ // Remove the configuration file
+ //
+ try {
+ getSvcMgr().removeDefaults();
+ printMessage(getString("unconfigure_remove_conf_progress"));
+ } catch (Throwable e) {
+ Object [] arguments = new Object[1];
+ arguments[0] = getMessage(e);
+ printErrMessage(getString("unconfigure_remove_conf_error"),
+ arguments);
+ }
+
+ return (DhcpCfg.SUCCESS);
+
+ } // execute
+
+} // UnconfigureDhcp
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/AddEntry.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/AddEntry.java
new file mode 100644
index 0000000000..3cd93c6171
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/AddEntry.java
@@ -0,0 +1,145 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2001-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.cli.dhtadm;
+
+import com.sun.dhcpmgr.cli.common.DhcpCliFunction;
+import com.sun.dhcpmgr.data.DhcptabRecord;
+import com.sun.dhcpmgr.data.Macro;
+import com.sun.dhcpmgr.data.Option;
+import com.sun.dhcpmgr.data.OptionsTable;
+import com.sun.dhcpmgr.bridge.ExistsException;
+
+import java.lang.IllegalArgumentException;
+
+/**
+ * The main class for the "add entry" functionality of dhtadm.
+ */
+public class AddEntry extends DhtAdmFunction {
+
+ /**
+ * The valid options associated with adding an entry.
+ */
+ static final int supportedOptions[] = {
+ DhtAdm.MACRONAME,
+ DhtAdm.SYMBOLNAME,
+ DhtAdm.DEFINITION,
+ DhtAdm.RESOURCE,
+ DhtAdm.RESOURCE_CONFIG,
+ DhtAdm.PATH,
+ DhtAdm.SIGHUP
+ };
+
+ /**
+ * Constructs a AddEntry object.
+ */
+ public AddEntry() {
+
+ validOptions = supportedOptions;
+
+ } // constructor
+
+ /**
+ * Returns the option flag for this function.
+ * @returns the option flag for this function.
+ */
+ public int getFunctionFlag() {
+ return (DhtAdm.ADD_ENTRY);
+ }
+
+ /**
+ * Executes the "add entry" functionality.
+ * @return DhtAdm.SUCCESS, DhtAdm.EXISTS, DhtAdm.WARNING, or
+ * DhtAdm.CRITICAL
+ */
+ public int execute()
+ throws IllegalArgumentException {
+
+ int returnCode = DhtAdm.SUCCESS;
+
+ // Get macro or symbol name. One and only one should be set.
+ //
+ String macroName = options.valueOf(DhtAdm.MACRONAME);
+ String symbolName = options.valueOf(DhtAdm.SYMBOLNAME);
+
+ if (macroName != null && symbolName != null) {
+ String msg = getString("two_keys_error");
+ throw new IllegalArgumentException(msg);
+ }
+
+ if (macroName == null && symbolName == null) {
+ String msg = getString("no_keys_error");
+ throw new IllegalArgumentException(msg);
+ }
+
+ // Get the definition. It's an error if it does not exist.
+ //
+ String definition = options.valueOf(DhtAdm.DEFINITION);
+ if (definition == null) {
+ String msg = getString("no_definition_error");
+ throw new IllegalArgumentException(msg);
+ }
+
+ // Create a DhcptabRecord.
+ //
+ try {
+ DhcptabRecord dhcptabRecord = null;
+ if (macroName != null) {
+ OptionsTable optionsTable = OptionsTable.getTable();
+ optionsTable.add(
+ getDhcptabMgr().getOptions(getDhcpDatastore()));
+ Macro macro = new Macro(macroName);
+ macro.setValue(definition, false, true);
+ dhcptabRecord = macro;
+ } else if (symbolName != null) {
+ Option option =
+ getDhcptabMgr().createOption(symbolName, definition);
+ dhcptabRecord = option;
+ } else {
+ printErrMessage(getString("internal_error"));
+ returnCode = DhtAdm.CRITICAL;
+ }
+
+ // Add the entry.
+ //
+ if (returnCode == DhtAdm.SUCCESS) {
+ getDhcptabMgr().createRecord(dhcptabRecord, false,
+ getDhcpDatastore());
+ }
+ } catch (ExistsException e) {
+ printErrMessage(getMessage(e));
+ returnCode = DhtAdm.EXISTS;
+ } catch (Throwable e) {
+ printErrMessage(getMessage(e));
+ returnCode = DhtAdm.WARNING;
+ }
+
+ return (returnCode);
+
+ } // execute
+
+} // AddEntry
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/CreateTable.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/CreateTable.java
new file mode 100644
index 0000000000..8f709c1889
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/CreateTable.java
@@ -0,0 +1,93 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2001-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.cli.dhtadm;
+
+import com.sun.dhcpmgr.cli.common.DhcpCliFunction;
+import com.sun.dhcpmgr.bridge.TableExistsException;
+
+import java.lang.IllegalArgumentException;
+
+/**
+ * The main class for the "create table" functionality of dhtadm.
+ */
+public class CreateTable extends DhtAdmFunction {
+
+ /**
+ * The valid options associated with creating the table.
+ */
+ static final int supportedOptions[] = {
+ DhtAdm.RESOURCE,
+ DhtAdm.RESOURCE_CONFIG,
+ DhtAdm.PATH,
+ DhtAdm.SIGHUP
+ };
+
+ /**
+ * Constructs a CreateTable object.
+ */
+ public CreateTable() {
+
+ validOptions = supportedOptions;
+
+ } // constructor
+
+ /**
+ * Returns the option flag for this function.
+ * @returns the option flag for this function.
+ */
+ public int getFunctionFlag() {
+ return (DhtAdm.CREATE_TABLE);
+ }
+
+ /**
+ * Executes the "create table" functionality.
+ * @return DhtAdm.SUCCESS, DhtAdm.EXISTS, DhtAdm.WARNING, or
+ * DhtAdm.CRITICAL
+ */
+ public int execute()
+ throws IllegalArgumentException {
+
+ int returnCode = DhtAdm.SUCCESS;
+
+ // Create the table.
+ //
+ try {
+ getDhcptabMgr().createDhcptab(getDhcpDatastore());
+ } catch (TableExistsException e) {
+ printErrMessage(getMessage(e));
+ returnCode = DhtAdm.EXISTS;
+ } catch (Throwable e) {
+ printErrMessage(getMessage(e));
+ returnCode = DhtAdm.WARNING;
+ }
+
+ return (returnCode);
+
+ } // execute
+
+} // CreateTable
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/DeleteEntry.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/DeleteEntry.java
new file mode 100644
index 0000000000..dc84b17416
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/DeleteEntry.java
@@ -0,0 +1,130 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2001-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.cli.dhtadm;
+
+import com.sun.dhcpmgr.cli.common.DhcpCliFunction;
+import com.sun.dhcpmgr.data.DhcptabRecord;
+import com.sun.dhcpmgr.data.Macro;
+import com.sun.dhcpmgr.data.Option;
+import com.sun.dhcpmgr.bridge.NoEntryException;
+
+import java.lang.IllegalArgumentException;
+
+/**
+ * The main class for the "delete entry" functionality of dhtadm.
+ */
+public class DeleteEntry extends DhtAdmFunction {
+
+ /**
+ * The valid options associated with deleting an entry.
+ */
+ static final int supportedOptions[] = {
+ DhtAdm.MACRONAME,
+ DhtAdm.SYMBOLNAME,
+ DhtAdm.RESOURCE,
+ DhtAdm.RESOURCE_CONFIG,
+ DhtAdm.PATH,
+ DhtAdm.SIGHUP
+ };
+
+ /**
+ * Constructs a DeleteEntry object.
+ */
+ public DeleteEntry() {
+
+ validOptions = supportedOptions;
+
+ } // constructor
+
+ /**
+ * Returns the option flag for this function.
+ * @returns the option flag for this function.
+ */
+ public int getFunctionFlag() {
+ return (DhtAdm.DELETE_ENTRY);
+ }
+
+ /**
+ * Executes the "delete entry" functionality.
+ * @return DhtAdm.SUCCESS, DhtAdm.ENOENT, DhtAdm.WARNING, or
+ * DhtAdm.CRITICAL
+ */
+ public int execute()
+ throws IllegalArgumentException {
+
+ int returnCode = DhtAdm.SUCCESS;
+
+ // Get macro or symbol name. One and only one should be set.
+ //
+ String macroName = options.valueOf(DhtAdm.MACRONAME);
+ String symbolName = options.valueOf(DhtAdm.SYMBOLNAME);
+
+ if (macroName != null && symbolName != null) {
+ String msg = getString("two_keys_error");
+ throw new IllegalArgumentException(msg);
+ }
+
+ if (macroName == null && symbolName == null) {
+ String msg = getString("no_keys_error");
+ throw new IllegalArgumentException(msg);
+ }
+
+ try {
+ // Get the DhcptabRecord.
+ //
+ DhcptabRecord dhcptabRecord = null;
+ if (macroName != null) {
+ dhcptabRecord = new Macro(macroName);
+ } else if (symbolName != null) {
+ dhcptabRecord = new Option();
+ dhcptabRecord.setKey(symbolName);
+ } else {
+ printErrMessage(getString("internal_error"));
+ returnCode = DhtAdm.CRITICAL;
+ }
+
+ // Delete the entry.
+ //
+ if (returnCode == DhtAdm.SUCCESS) {
+ getDhcptabMgr().deleteRecord(dhcptabRecord, false,
+ getDhcpDatastore());
+ }
+
+ } catch (NoEntryException e) {
+ printErrMessage(getMessage(e));
+ returnCode = DhtAdm.ENOENT;
+ } catch (Throwable e) {
+ printErrMessage(getMessage(e));
+ returnCode = DhtAdm.WARNING;
+ }
+
+ return (returnCode);
+
+ } // execute
+
+} // DeleteEntry
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/DhtAdm.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/DhtAdm.java
new file mode 100644
index 0000000000..05f171fc0e
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/DhtAdm.java
@@ -0,0 +1,257 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2001-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.cli.dhtadm;
+
+import com.sun.dhcpmgr.cli.common.*;
+import com.sun.dhcpmgr.server.*;
+
+import java.lang.IllegalArgumentException;
+import java.text.MessageFormat;
+
+/**
+ * This class represents the entry point to the DHCP CLI dhcptab
+ * administration.
+ */
+public class DhtAdm
+ extends DhcpCliProgram {
+
+ /**
+ * The program signature.
+ */
+ public static final String SIGNATURE = "dhtadm: ";
+
+ /**
+ * The valid options for all DhtAdm administration.
+ */
+ private static String optString = "ACDIMPRvgr:p:u:s:m:n:e:d:B;";
+
+ public static final int ADD_ENTRY = 'A';
+ public static final int MODIFY_ENTRY = 'M';
+ public static final int DELETE_ENTRY = 'D';
+ public static final int CREATE_TABLE = 'C';
+ public static final int REMOVE_TABLE = 'R';
+ public static final int DISPLAY_TABLE = 'P';
+ public static final int BATCH_EXECUTION = 'B';
+
+ public static final int MACRONAME = 'm';
+ public static final int SYMBOLNAME = 's';
+ public static final int NEWNAME = 'n';
+ public static final int DEFINITION = 'd';
+ public static final int EDITSYMBOL = 'e';
+ public static final int RESOURCE = 'r';
+ public static final int RESOURCE_CONFIG = 'u';
+ public static final int PATH = 'p';
+ public static final int VERBOSE = 'v';
+ public static final int SIGHUP = 'g';
+
+ /**
+ * Constructs a dhtadm command.
+ * @param args the options to the command.
+ */
+ public DhtAdm(String [] args) {
+
+ reset(args);
+ // Set the options.
+ //
+ options = new DhcpCliOptions();
+ this.args = args;
+
+ } // constructor
+
+ /**
+ * Resets a DhtAdm for reuse. Used by DhcpBatch program.
+ * @param args the options to the command.
+ */
+ public void reset(String [] args) {
+
+ clearFunction();
+ options = new DhcpCliOptions();
+ this.args = args;
+
+ }
+
+ /**
+ * Returns the manpage signature for the program.
+ * @return the manpage signature for the program.
+ */
+ public String getManPage() {
+ return "dhtadm(1M)";
+ }
+
+ /**
+ * Displays program usage.
+ */
+ public void usage() {
+
+ DhcpCliPrint.printErrMessage(getString("usage"));
+
+ } // usage
+
+ /**
+ * Executes the program function.
+ * @return SUCCESS, EXISTS, ENOENT, WARNING, or CRITICAL
+ */
+ public int execute() {
+
+ int returnCode = SUCCESS;
+
+ // Get the options and go exec the correct function.
+ //
+ GetOpt getopt = new GetOpt(args, optString);
+ try {
+ int option;
+ while ((option = getopt.getNextOption()) != -1) {
+ processArg(option, getopt.getOptionArg());
+ }
+
+ int lastIndex = getopt.getNextOptionIndex();
+ if (args.length != lastIndex) {
+ Object [] arguments = new Object[1];
+ arguments[0] = args[lastIndex];
+ MessageFormat form =
+ new MessageFormat(getString("invalid_arg"));
+ throw new IllegalArgumentException(form.format(arguments));
+ }
+
+ if (function == null) {
+ String msg = getString("no_function_error");
+ throw new IllegalArgumentException(msg);
+ }
+
+ // Check the validity of the data store version.
+ //
+ if (!function.isVersionValid(false)) {
+ return (CRITICAL);
+ }
+
+ // Create a DHCP datastore object with the user specified objects.
+ //
+ function.setDhcpDatastore(options.valueOf(RESOURCE),
+ options.valueOf(PATH), options.valueOf(RESOURCE_CONFIG));
+
+ function.setOptions(options);
+ function.setStandardOptions();
+ returnCode = function.execute();
+
+ } catch (IllegalArgumentException e) {
+ StringBuffer msg = new StringBuffer(SIGNATURE);
+ msg.append(DhcpCliFunction.getMessage(e));
+ DhcpCliPrint.printErrMessage(msg.toString());
+ DhcpCliPrint.printErrMessage("");
+ usage();
+ returnCode = CRITICAL;
+ } catch (Throwable e) {
+ StringBuffer msg = new StringBuffer(SIGNATURE);
+ msg.append(DhcpCliFunction.getMessage(e));
+ DhcpCliPrint.printErrMessage(msg.toString());
+ returnCode = CRITICAL;
+ }
+
+ // Signal server if requested by user and main operation successful
+ if (returnCode == SUCCESS && options.isSet(SIGHUP)) {
+ try {
+ DhcpMgr dhcpMgr = new DhcpMgrImpl();
+ dhcpMgr.getDhcpServiceMgr().reload();
+ } catch (Throwable e) {
+ returnCode = WARNING;
+ // Display warning
+ StringBuffer msg = new StringBuffer(SIGNATURE);
+ msg.append(getString("sighup_failed"));
+ DhcpCliPrint.printErrMessage(msg.toString());
+ }
+ }
+
+ return (returnCode);
+
+ } // execute
+
+ /**
+ * Processes one program argument.
+ * @param option the option flag
+ * @param value the option value(if any)
+ * @exception IllegalArgumentException if an invalid argument was entered
+ */
+ public void processArg(int option, String value)
+ throws IllegalArgumentException {
+
+ switch (option) {
+
+ case ADD_ENTRY:
+ setFunction(new AddEntry());
+ break;
+ case MODIFY_ENTRY:
+ setFunction(new ModifyEntry());
+ break;
+ case DELETE_ENTRY:
+ setFunction(new DeleteEntry());
+ break;
+ case CREATE_TABLE:
+ setFunction(new CreateTable());
+ break;
+ case REMOVE_TABLE:
+ setFunction(new RemoveTable());
+ break;
+ case DISPLAY_TABLE:
+ setFunction(new DisplayTable());
+ break;
+ case BATCH_EXECUTION:
+ setFunction(new DhtAdmBatch(value));
+ break;
+ default:
+ options.setOption(option, value);
+ }
+
+ } // processArg
+
+ /**
+ * Returns a localized string for this function
+ * @param key the resource bundle string identifier
+ * @return string from resource bundle.
+ */
+ public String getString(String key) {
+
+ return ResourceStrings.getString(key);
+
+ } // getString
+
+ /**
+ * The entry point for the program.
+ * @param args the program arguments
+ */
+ public static void main(String[] args) {
+
+ DhtAdm dhtadm = new DhtAdm(args);
+ int returnCode = DhtAdm.CRITICAL;
+ if (dhtadm.isValidUser()) {
+ returnCode = dhtadm.execute();
+ }
+ System.exit(returnCode);
+
+ } // main
+
+} // DhtAdm
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/DhtAdmBatch.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/DhtAdmBatch.java
new file mode 100644
index 0000000000..c4a78106fe
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/DhtAdmBatch.java
@@ -0,0 +1,80 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2001-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.cli.dhtadm;
+
+import com.sun.dhcpmgr.cli.dhcpbatch.*;
+
+import java.lang.IllegalArgumentException;
+
+/**
+ * The main class for the batch functionality of dhtadm.
+ */
+public class DhtAdmBatch extends DhtAdmFunction {
+
+ /**
+ * The valid options associated with batching.
+ */
+ static final int supportedOptions[] = {
+ DhtAdm.VERBOSE,
+ DhtAdm.SIGHUP
+ };
+
+ private String inputSource = null;
+
+ /**
+ * Constructs a DhtAdmBatch object.
+ */
+ public DhtAdmBatch(String inputSource) {
+
+ this.inputSource = inputSource;
+ validOptions = supportedOptions;
+
+ } // constructor
+
+ /**
+ * Returns the option flag for this function.
+ * @returns the option flag for this function.
+ */
+ public int getFunctionFlag() {
+ return (DhtAdm.BATCH_EXECUTION);
+ }
+
+ /**
+ * Executes the batch functionality.
+ * @return one of the DhtAdm return codes.
+ */
+ public int execute()
+ throws IllegalArgumentException {
+
+ DhcpBatch batch = new DhcpBatch(inputSource);
+ batch.setVerbose(options.isSet(DhtAdm.VERBOSE));
+ return (batch.execute());
+
+ } // execute
+
+} // DhtAdmBatch
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/DhtAdmFunction.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/DhtAdmFunction.java
new file mode 100644
index 0000000000..92c8ab42ea
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/DhtAdmFunction.java
@@ -0,0 +1,59 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.cli.dhtadm;
+
+import com.sun.dhcpmgr.cli.common.DhcpCliFunction;
+import com.sun.dhcpmgr.cli.common.DhcpCliPrint;
+
+/**
+ * Abstract class implemented by all the dhtadm "function" classes.
+ */
+public abstract class DhtAdmFunction
+ extends DhcpCliFunction {
+
+ /**
+ * Returns a localized string for this function
+ * @param key the resource bundle string identifier
+ */
+ public String getString(String key) {
+
+ return ResourceStrings.getString(key);
+
+ } // getString
+
+ /**
+ * Prints an error message.
+ * @param msg the message to print.
+ */
+ public void printErrMessage(String msg) {
+ StringBuffer fullmsg = new StringBuffer(DhtAdm.SIGNATURE);
+ fullmsg.append(msg);
+ DhcpCliPrint.printErrMessage(fullmsg.toString());
+ } // printErrMessage
+
+} // DhtAdmFunction
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/DisplayTable.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/DisplayTable.java
new file mode 100644
index 0000000000..262832e2cc
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/DisplayTable.java
@@ -0,0 +1,122 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2001-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.cli.dhtadm;
+
+import com.sun.dhcpmgr.cli.common.DhcpCliFunction;
+import com.sun.dhcpmgr.cli.common.Format;
+import com.sun.dhcpmgr.data.Macro;
+import com.sun.dhcpmgr.data.Option;
+import com.sun.dhcpmgr.bridge.NoTableException;
+
+import java.lang.IllegalArgumentException;
+
+/**
+ * The main class for the "display table" functionality of dhtadm.
+ */
+public class DisplayTable extends DhtAdmFunction {
+
+ /**
+ * The valid options associated with displaying the table.
+ */
+ static final int supportedOptions[] = {
+ DhtAdm.RESOURCE,
+ DhtAdm.RESOURCE_CONFIG,
+ DhtAdm.PATH,
+ DhtAdm.SIGHUP
+ };
+
+ /**
+ * Constructs a DisplayTable object.
+ */
+ public DisplayTable() {
+
+ validOptions = supportedOptions;
+
+ } // constructor
+
+ /**
+ * Returns the option flag for this function.
+ * @returns the option flag for this function.
+ */
+ public int getFunctionFlag() {
+ return (DhtAdm.DISPLAY_TABLE);
+ }
+
+ /**
+ * Executes the "display table" functionality.
+ * @return DhtAdm.SUCCESS, DhtAdm.ENOENT, DhtAdm.WARNING, or
+ * DhtAdm.CRITICAL
+ */
+ public int execute()
+ throws IllegalArgumentException {
+
+ int returnCode = DhtAdm.SUCCESS;
+
+ // Retrieve the table contents. Table consists of macros
+ // and options ... go get them.
+ //
+ Macro [] macros = null;
+ Option [] options = null;
+ try {
+ macros = getDhcptabMgr().getMacros(getDhcpDatastore());
+ options = getDhcptabMgr().getOptions(getDhcpDatastore());
+ } catch (NoTableException e) {
+ printErrMessage(getMessage(e));
+ returnCode = DhtAdm.ENOENT;
+ } catch (Throwable e) {
+ printErrMessage(getMessage(e));
+ returnCode = DhtAdm.WARNING;
+ }
+
+ if (returnCode == DhtAdm.SUCCESS) {
+ Format.print(System.out, "%-20s\t", getString("Name"));
+ Format.print(System.out, "%-8s\t", getString("Type"));
+ Format.print(System.out, "%s\n", getString("Value"));
+ Format.print(System.out, "%s\n",
+ "==================================================");
+
+ for (int i = 0; macros != null && i < macros.length; i++) {
+ Macro macro = macros[i];
+ Format.print(System.out, "%-20s\t", macro.getKey());
+ Format.print(System.out, "%-8s\t", getString("Macro"));
+ Format.print(System.out, "%s\n", macro.getValue());
+ }
+
+ for (int i = 0; options != null && i < options.length; i++) {
+ Option option = options[i];
+ Format.print(System.out, "%-20s\t", option.getKey());
+ Format.print(System.out, "%-8s\t", getString("Symbol"));
+ Format.print(System.out, "%s\n", option.getValue());
+ }
+ }
+
+ return (returnCode);
+
+ } // execute
+
+} // DisplayTable
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/Makefile b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/Makefile
new file mode 100644
index 0000000000..2e76fe1f98
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/Makefile
@@ -0,0 +1,82 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2001-2002 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/Makefile
+#
+
+# Place high-level classes first to minimize build time.
+CLASSFILES = DhtAdm.class \
+ AddEntry.class \
+ CreateTable.class \
+ DeleteEntry.class \
+ DhtAdmBatch.class \
+ DisplayTable.class \
+ ModifyEntry.class \
+ RemoveTable.class \
+ DhtAdmFunction.class \
+ ResourceStrings.class
+
+include $(SRC)/Makefile.master
+
+CLASSPATH= $(SRC)/cmd/cmd-inet/usr.sadm/dhcpmgr
+
+JAVAFILES = $(CLASSFILES:.class=.java)
+
+MSGDIR= $(ROOT)/usr/share/lib/locale/com/sun/dhcpmgr/cli/dhtadm
+MSGDIRS = $(ROOT)/usr/share/lib/locale \
+ $(ROOT)/usr/share/lib/locale/com \
+ $(ROOT)/usr/share/lib/locale/com/sun \
+ $(ROOT)/usr/share/lib/locale/com/sun/dhcpmgr \
+ $(ROOT)/usr/share/lib/locale/com/sun/dhcpmgr/cli \
+ $(MSGDIR)
+
+MSGFILES= ResourceBundle.properties
+MSGS= $(MSGFILES:%=$(MSGDIR)/%)
+
+CLEANFILES= *.class
+CLOBBERFILES=
+
+.KEEP_STATE:
+
+all: $(CLASSFILES)
+
+install: all
+
+_msg: $(MSGDIRS) $(MSGS)
+
+$(MSGDIR)/%: %
+ $(INS.file)
+
+$(MSGDIRS):
+ $(INS.dir)
+
+lint:
+
+clean:
+ $(RM) $(CLEANFILES)
+
+clobber: clean
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/ModifyEntry.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/ModifyEntry.java
new file mode 100644
index 0000000000..d46385ff73
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/ModifyEntry.java
@@ -0,0 +1,204 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2001-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.cli.dhtadm;
+
+import com.sun.dhcpmgr.cli.common.DhcpCliFunction;
+import com.sun.dhcpmgr.data.DhcptabRecord;
+import com.sun.dhcpmgr.data.Macro;
+import com.sun.dhcpmgr.data.Option;
+import com.sun.dhcpmgr.bridge.NoEntryException;
+
+import java.lang.IllegalArgumentException;
+
+/**
+ * The main class for the "modify entry" functionality of dhtadm.
+ */
+public class ModifyEntry extends DhtAdmFunction {
+
+ /**
+ * The valid options associated with modifying an entry.
+ */
+ static final int supportedOptions[] = {
+ DhtAdm.MACRONAME,
+ DhtAdm.SYMBOLNAME,
+ DhtAdm.NEWNAME,
+ DhtAdm.DEFINITION,
+ DhtAdm.EDITSYMBOL,
+ DhtAdm.RESOURCE,
+ DhtAdm.RESOURCE_CONFIG,
+ DhtAdm.PATH,
+ DhtAdm.SIGHUP
+ };
+
+ /**
+ * Constructs a ModifyEntry object.
+ */
+ public ModifyEntry() {
+
+ validOptions = supportedOptions;
+
+ } // constructor
+
+ /**
+ * Returns the option flag for this function.
+ * @returns the option flag for this function.
+ */
+ public int getFunctionFlag() {
+ return (DhtAdm.MODIFY_ENTRY);
+ }
+
+ /**
+ * Executes the "modify entry" functionality.
+ * @return DhtAdm.SUCCESS, DhtAdm.ENOENT, DhtAdm.WARNING, or
+ * DhtAdm.CRITICAL
+ */
+ public int execute()
+ throws IllegalArgumentException {
+
+ int returnCode = DhtAdm.SUCCESS;
+
+ // Get macro or symbol name. One and only one should be set.
+ //
+ String macroName = options.valueOf(DhtAdm.MACRONAME);
+ String symbolName = options.valueOf(DhtAdm.SYMBOLNAME);
+
+ if (macroName != null && symbolName != null) {
+ String msg = getString("two_keys_error");
+ throw new IllegalArgumentException(msg);
+ }
+
+ if (macroName == null && symbolName == null) {
+ String msg = getString("no_keys_error");
+ throw new IllegalArgumentException(msg);
+ }
+
+ // Get the modify "sub-functions"
+ // One and only one of the options should be set.
+ //
+ int count = 0;
+ String newName = options.valueOf(DhtAdm.NEWNAME);
+ if (newName != null) {
+ count++;
+ }
+
+ String definition = options.valueOf(DhtAdm.DEFINITION);
+ if (definition != null) {
+ count++;
+ }
+
+ String editSymbol = options.valueOf(DhtAdm.EDITSYMBOL);
+ if (editSymbol != null) {
+ if (symbolName != null) {
+ printErrMessage(getString("symbol_edit_error"));
+ return (DhtAdm.CRITICAL);
+ }
+ count++;
+ }
+
+ if (count != 1) {
+ printErrMessage(getString("modify_no_function_error"));
+ return (DhtAdm.CRITICAL);
+ }
+
+ // Get macro or symbol name. One and only one should be set.
+ // Then go get the instance of the macro or symbol.
+ //
+ try {
+ DhcptabRecord oldDhcptabRecord = null;
+ if (macroName != null) {
+ oldDhcptabRecord =
+ getDhcptabMgr().getMacro(macroName, getDhcpDatastore());
+ } else if (symbolName != null) {
+ oldDhcptabRecord =
+ getDhcptabMgr().getOption(symbolName,
+ getDhcpDatastore());
+ } else {
+ printErrMessage(getString("internal_error"));
+ return (DhtAdm.CRITICAL);
+ }
+
+ // Identify the function and create the newDhcptabRecord
+ // given the function.
+ //
+ DhcptabRecord newDhcptabRecord = null;
+
+ if (definition != null) {
+ if (macroName != null) {
+ Macro newMacro = new Macro(macroName);
+ newMacro.setValue(definition, false, true);
+ newDhcptabRecord = newMacro;
+ } else if (symbolName != null) {
+ newDhcptabRecord =
+ getDhcptabMgr().createOption(symbolName, definition);
+ } else {
+ printErrMessage(getString("internal_error"));
+ return (DhtAdm.CRITICAL);
+ }
+
+
+ } else if (newName != null) {
+ definition = oldDhcptabRecord.getValue().toString();
+ if (macroName != null) {
+ Macro newMacro = new Macro(newName);
+ newMacro.setValue(definition, false, true);
+ newDhcptabRecord = newMacro;
+ } else if (symbolName != null) {
+ newDhcptabRecord =
+ getDhcptabMgr().createOption(newName, definition);
+ } else {
+ printErrMessage(getString("internal_error"));
+ return (DhtAdm.CRITICAL);
+ }
+
+ } else if (editSymbol != null) {
+
+ Macro oldMacro = (Macro)oldDhcptabRecord;
+ Macro newMacro = (Macro)oldMacro.clone();
+ newMacro.editOption(editSymbol);
+ newDhcptabRecord = newMacro;
+
+ } else {
+ printErrMessage(getString("internal_error"));
+ return (DhtAdm.CRITICAL);
+ }
+
+ getDhcptabMgr().modifyRecord(oldDhcptabRecord, newDhcptabRecord,
+ false, getDhcpDatastore());
+ } catch (NoEntryException e) {
+ printErrMessage(getMessage(e));
+ returnCode = DhtAdm.ENOENT;
+ } catch (Throwable e) {
+ printErrMessage(getMessage(e));
+ returnCode = DhtAdm.WARNING;
+ }
+
+ return (returnCode);
+
+ } // execute
+
+} // ModifyEntry
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/RemoveTable.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/RemoveTable.java
new file mode 100644
index 0000000000..91cdf21013
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/RemoveTable.java
@@ -0,0 +1,93 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2001-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.cli.dhtadm;
+
+import com.sun.dhcpmgr.cli.common.DhcpCliFunction;
+import com.sun.dhcpmgr.bridge.NoTableException;
+
+import java.lang.IllegalArgumentException;
+
+/**
+ * The main class for the "remove table" functionality of dhtadm.
+ */
+public class RemoveTable extends DhtAdmFunction {
+
+ /**
+ * The valid options associated with removing the table.
+ */
+ static final int supportedOptions[] = {
+ DhtAdm.RESOURCE,
+ DhtAdm.RESOURCE_CONFIG,
+ DhtAdm.PATH,
+ DhtAdm.SIGHUP
+ };
+
+ /**
+ * Constructs a RemoveTable object.
+ */
+ public RemoveTable() {
+
+ validOptions = supportedOptions;
+
+ } // constructor
+
+ /**
+ * Returns the option flag for this function.
+ * @returns the option flag for this function.
+ */
+ public int getFunctionFlag() {
+ return (DhtAdm.REMOVE_TABLE);
+ }
+
+ /**
+ * Executes the "remove table" functionality.
+ * @return DhtAdm.SUCCESS, DhtAdm.ENOENT, DhtAdm.WARNING, or
+ * DhtAdm.CRITICAL
+ */
+ public int execute()
+ throws IllegalArgumentException {
+
+ int returnCode = DhtAdm.SUCCESS;
+
+ // Remove the table.
+ //
+ try {
+ getDhcptabMgr().deleteDhcptab(getDhcpDatastore());
+ } catch (NoTableException e) {
+ printErrMessage(getMessage(e));
+ returnCode = DhtAdm.ENOENT;
+ } catch (Throwable e) {
+ printErrMessage(getMessage(e));
+ returnCode = DhtAdm.WARNING;
+ }
+
+ return (returnCode);
+
+ } // execute
+
+} // RemoveTable
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/ResourceBundle.properties b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/ResourceBundle.properties
new file mode 100644
index 0000000000..77daefee59
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/ResourceBundle.properties
@@ -0,0 +1,89 @@
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Headers for display
+#
+Name=Name
+Type=Type
+Value=Value
+
+#
+# Dhcptab flags
+#
+Macro=Macro
+Symbol=Symbol
+
+#
+# Internal error
+#
+internal_error=Program internal error
+
+#
+# Specifying both macro name and symbol name error
+#
+two_keys_error=Cannot specify both macro name and symbol name
+
+#
+# Specifying neither macro name and symbol name error
+#
+no_keys_error=Must specify either macro name or symbol name
+
+#
+# Specifying no definition error
+#
+no_definition_error=Must specify macro or symbol definition
+
+#
+# Cannot edit symbol error
+#
+symbol_edit_error=Cannot edit symbols
+
+#
+# Cannot edit symbol error
+#
+modify_no_function_error=Only one of '-d', '-e', '-n' can be defined
+
+#
+# No function defined error
+#
+no_function_error=Must specify one of 'C', 'A', 'M', 'D', 'R', 'P', or 'B'
+
+#
+# An invalid argument was input to the program.
+#
+invalid_arg=Invalid argument - {0}
+
+#
+# Couldn't signal daemon
+#
+sighup_failed=Unable to signal the daemon to reload the dhcptab
+
+#
+# Usage
+#
+usage=dhtadm [-r (resource)] [-p (path)] [-u (uninterpreted data)] [-g] (options)\n\nWhere (options) is one of:\n\n-C Create the dhcptab\n\n-A Add symbol or macro. Sub-options:\n { -s (symbol name) | -m (macro name) } -d (definition)\n\n-M Modify symbol or macro. Sub-options:\n -s (old symbol name) {-n (new name) | -d (definition)}\n Or\n -m (old macro name) {-n (new name) | -d (definition) | -e (symbol = value)}\n\n-D Delete symbol or macro definition. Sub-options:\n -s ( symbol name ) | -m ( macro name )\n\n-R Remove the dhcptab\n\n-P Display the dhcptab\n\n-B [batchfile] Run command in batch input mode. Sub-options:\n [-v] Output commands as they are processed.\n
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/ResourceStrings.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/ResourceStrings.java
new file mode 100644
index 0000000000..e1c7eb3b2b
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/ResourceStrings.java
@@ -0,0 +1,64 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.cli.dhtadm;
+
+import java.util.*;
+
+/**
+ * This class provides a convenient method to retrieve resources for
+ * the dhtadm package.
+ */
+public class ResourceStrings {
+
+ /**
+ * The handle to the resource bundle for the module.
+ */
+ private static ResourceBundle bundle = null;
+
+ /**
+ * Return a string from the resource bundle.
+ * @param key the key to the resource bundle string.
+ * @return the resource bundle string.
+ */
+ public static String getString(String key) {
+ String msg = null;
+ try {
+ if (bundle == null) {
+ bundle = ResourceBundle.getBundle(
+ "com.sun.dhcpmgr.cli.dhtadm.ResourceBundle",
+ Locale.getDefault());
+ }
+ msg = bundle.getString(key);
+ } catch (Throwable e) {
+ msg = new String(key);
+ }
+ return msg;
+
+ } // getString
+
+} // ResourceStrings
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/AddClientEntry.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/AddClientEntry.java
new file mode 100644
index 0000000000..f5d342a76f
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/AddClientEntry.java
@@ -0,0 +1,194 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2001-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.cli.pntadm;
+
+import com.sun.dhcpmgr.cli.common.Util;
+import com.sun.dhcpmgr.data.DhcpClientRecord;
+import com.sun.dhcpmgr.data.Macro;
+import com.sun.dhcpmgr.data.Network;
+import com.sun.dhcpmgr.bridge.BridgeException;
+import com.sun.dhcpmgr.bridge.ExistsException;
+
+import java.lang.IllegalArgumentException;
+
+/**
+ * The main class for the "add client" functionality of pntadm.
+ */
+public class AddClientEntry extends PntAdmFunction {
+
+ /**
+ * The valid options associated with adding a client entry.
+ */
+ static final int supportedOptions[] = {
+ PntAdm.COMMENT,
+ PntAdm.LEASE_EXPIRATION,
+ PntAdm.FLAGS,
+ PntAdm.HOST_NAME,
+ PntAdm.CLIENTID,
+ PntAdm.CONVERT_CLIENTID,
+ PntAdm.MACRO_NAME,
+ PntAdm.VERIFY_MACRO,
+ PntAdm.SERVER,
+ PntAdm.RESOURCE,
+ PntAdm.RESOURCE_CONFIG,
+ PntAdm.PATH
+ };
+
+ /**
+ * The client entry to add.
+ */
+ String clientIP;
+
+ /**
+ * Constructs a AddClientEntry object for the client, clientIP.
+ * @param clientIP the client name or IP address.
+ */
+ public AddClientEntry(String clientIP) {
+
+ this.clientIP = clientIP;
+ validOptions = supportedOptions;
+
+ } // constructor
+
+ /**
+ * Returns the option flag for this function.
+ * @returns the option flag for this function.
+ */
+ public int getFunctionFlag() {
+ return (PntAdm.ADD_CLIENT_ENTRY);
+ }
+
+ /**
+ * Executes the "add client" functionality.
+ * @return PntAdm.SUCCESS, PntAdm.EXISTS, PntAdm.WARNING, or
+ * PntAdm.CRITICAL
+ */
+ public int execute()
+ throws IllegalArgumentException {
+
+ int returnCode = PntAdm.SUCCESS;
+
+ // Build up a DhcpClientRecord from the user specified options.
+ //
+ try {
+ DhcpClientRecord dhcpClientRecord = new DhcpClientRecord(clientIP);
+
+ String clientId = options.valueOf(PntAdm.CLIENTID);
+ boolean convertClientId = options.isSet(PntAdm.CONVERT_CLIENTID);
+ if (convertClientId) {
+ if (clientId == null) {
+ String msg = getString("no_clientid_specified");
+ throw new IllegalArgumentException(msg);
+ }
+ clientId = Util.asciiToHex(clientId);
+ }
+ if (clientId != null) {
+ dhcpClientRecord.setClientId(clientId);
+ }
+
+ String flags = options.valueOf(PntAdm.FLAGS);
+ if (flags != null) {
+ dhcpClientRecord.setFlags(flags);
+ }
+
+ String clientName = options.valueOf(PntAdm.HOST_NAME);
+ if (clientName != null) {
+ if (isHostsManaged()) {
+ dhcpClientRecord.setClientName(clientName);
+ } else {
+ returnCode = PntAdm.WARNING;
+ }
+ }
+
+ String serverIP = options.valueOf(PntAdm.SERVER);
+ if (serverIP == null) {
+ serverIP = getSvcMgr().getServerName();
+ }
+ dhcpClientRecord.setServerIP(serverIP);
+
+ String expiration = options.valueOf(PntAdm.LEASE_EXPIRATION);
+ if (expiration != null) {
+ dhcpClientRecord.setExpiration(shortFormat, expiration);
+ }
+
+ boolean verifyMacro = options.isSet(PntAdm.VERIFY_MACRO);
+ String macro = options.valueOf(PntAdm.MACRO_NAME);
+ if (verifyMacro) {
+ if (macro == null) {
+ String msg = getString("no_macro_specified");
+ throw new IllegalArgumentException(msg);
+ }
+
+ // Create a Macro entry so that we can check to see if it
+ // exists in the dhcptab.
+ //
+ try {
+ Macro existingMacro = getDhcptabMgr().getMacro(macro);
+ }
+ catch (BridgeException e) {
+ printErrMessage(getString("macro_not_found"));
+ return (PntAdm.WARNING);
+ }
+ }
+ if (macro != null) {
+ dhcpClientRecord.setMacro(macro);
+ }
+
+ String comment = options.valueOf(PntAdm.COMMENT);
+ if (comment != null) {
+ dhcpClientRecord.setComment(comment);
+ }
+
+ // Create a Network object.
+ //
+ Network network = getNetMgr().getNetwork(networkName);
+ if (network == null) {
+ printErrMessage(getString("network_name_error"));
+ return (PntAdm.WARNING);
+ }
+
+ // Add the client. The host will be added to the hosts table
+ // if necessary.
+ //
+ getNetMgr().addClient(dhcpClientRecord, network.toString(),
+ getDhcpDatastore());
+ } catch (IllegalArgumentException e) {
+ throw e;
+ } catch (ExistsException e) {
+ printErrMessage(getMessage(e));
+ returnCode = PntAdm.EXISTS;
+ } catch (Throwable e) {
+ printErrMessage(getMessage(e));
+ returnCode = PntAdm.WARNING;
+ }
+
+ return (returnCode);
+
+ } // execute
+
+} // AddClientEntry
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/CreateNetworkTable.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/CreateNetworkTable.java
new file mode 100644
index 0000000000..0b34f7783c
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/CreateNetworkTable.java
@@ -0,0 +1,99 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.cli.pntadm;
+
+import com.sun.dhcpmgr.data.Network;
+import com.sun.dhcpmgr.bridge.TableExistsException;
+
+import java.lang.IllegalArgumentException;
+
+/**
+ * The main class for the "create network table" functionality
+ * of pntadm.
+ */
+public class CreateNetworkTable extends PntAdmFunction {
+
+ /**
+ * The valid options associated with creating a network table.
+ */
+ static final int supportedOptions[] = {
+ PntAdm.RESOURCE,
+ PntAdm.RESOURCE_CONFIG,
+ PntAdm.PATH
+ };
+
+ /**
+ * Constructs a CreateNetworkTable object.
+ */
+ public CreateNetworkTable() {
+
+ validOptions = supportedOptions;
+
+ } // constructor
+
+ /**
+ * Returns the option flag for this function.
+ * @returns the option flag for this function.
+ */
+ public int getFunctionFlag() {
+ return (PntAdm.CREATE_NETWORK_TABLE);
+ }
+
+ /**
+ * Executes the "create network table" functionality.
+ * @return PntAdm.SUCCESS, PntAdm.EXISTS, PntAdm.WARNING, or
+ * PntAdm.CRITICAL
+ */
+ public int execute()
+ throws IllegalArgumentException {
+
+ int returnCode = PntAdm.SUCCESS;
+
+ // Create a Network object.
+ //
+ try {
+ Network network = getNetMgr().getNetwork(networkName);
+ if (network == null) {
+ printErrMessage(getString("network_name_error"));
+ return (PntAdm.WARNING);
+ }
+
+ getNetMgr().createNetwork(network.toString(), getDhcpDatastore());
+ } catch (TableExistsException e) {
+ printErrMessage(getMessage(e));
+ returnCode = PntAdm.EXISTS;
+ } catch (Throwable e) {
+ printErrMessage(getMessage(e));
+ returnCode = PntAdm.WARNING;
+ }
+
+ return (returnCode);
+
+ } // execute
+
+} // CreateNetworkTable
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/DeleteClientEntry.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/DeleteClientEntry.java
new file mode 100644
index 0000000000..c7a66f9b74
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/DeleteClientEntry.java
@@ -0,0 +1,125 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.cli.pntadm;
+
+import com.sun.dhcpmgr.data.DhcpClientRecord;
+import com.sun.dhcpmgr.data.Network;
+import com.sun.dhcpmgr.bridge.BridgeException;
+import com.sun.dhcpmgr.bridge.NoEntryException;
+
+import java.lang.IllegalArgumentException;
+
+/**
+ * The main class for the "delete client" functionality of pntadm.
+ */
+public class DeleteClientEntry extends PntAdmFunction {
+
+ /**
+ * The valid options associated with deleting a client entry.
+ */
+ static final int supportedOptions[] = {
+ PntAdm.DELETE_HOST,
+ PntAdm.RESOURCE,
+ PntAdm.RESOURCE_CONFIG,
+ PntAdm.PATH
+ };
+
+ /**
+ * The client entry to delete.
+ */
+ String clientIP;
+
+ /**
+ * Constructs a DeleteClientEntry object for the client, clientIP.
+ * @param clientIP the client name or IP address.
+ */
+ public DeleteClientEntry(String clientIP) {
+
+ this.clientIP = clientIP;
+ validOptions = supportedOptions;
+
+ } // constructor
+
+ /**
+ * Returns the option flag for this function.
+ * @returns the option flag for this function.
+ */
+ public int getFunctionFlag() {
+ return (PntAdm.DELETE_CLIENT_ENTRY);
+ }
+
+ /**
+ * Executes the "delete client" functionality.
+ * @return PntAdm.SUCCESS, PntAdm.ENOENT, PntAdm.WARNING, or
+ * PntAdm.CRITICAL
+ */
+ public int execute()
+ throws IllegalArgumentException {
+
+ int returnCode = PntAdm.SUCCESS;
+
+ // Check to see if the user wants the host deleted.
+ //
+ boolean deleteHost = options.isSet(PntAdm.DELETE_HOST);
+ if (deleteHost && !isHostsManaged()) {
+ deleteHost = false;
+ returnCode = PntAdm.WARNING;
+ }
+
+ // Build up a DhcpClientRecord from the user specified options.
+ //
+ try {
+ DhcpClientRecord dhcpClientRecord = new DhcpClientRecord(clientIP);
+
+ // Create a Network object.
+ //
+ Network network = getNetMgr().getNetwork(networkName);
+ if (networkName == null) {
+ printErrMessage(getString("network_name_error"));
+ return (PntAdm.WARNING);
+ }
+
+ // Delete the client and remove host from the hosts table
+ // if requested.
+ //
+ getNetMgr().deleteClient(dhcpClientRecord, network.toString(),
+ deleteHost, getDhcpDatastore());
+
+ } catch (NoEntryException e) {
+ printErrMessage(getMessage(e));
+ returnCode = PntAdm.ENOENT;
+ } catch (Throwable e) {
+ printErrMessage(getMessage(e));
+ returnCode = PntAdm.WARNING;
+ }
+
+ return (returnCode);
+
+ } // execute
+
+} // DeleteClientEntry
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/DisplayNetworkTable.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/DisplayNetworkTable.java
new file mode 100644
index 0000000000..6db4d65ba5
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/DisplayNetworkTable.java
@@ -0,0 +1,183 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2001-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.cli.pntadm;
+
+import com.sun.dhcpmgr.cli.common.Format;
+import com.sun.dhcpmgr.data.DhcpClientRecord;
+import com.sun.dhcpmgr.data.Network;
+import com.sun.dhcpmgr.bridge.NoEntryException;
+import com.sun.dhcpmgr.bridge.NoTableException;
+
+import java.util.Date;
+import java.lang.IllegalArgumentException;
+
+/**
+ * The main class for the "display network table" functionality
+ * of pntadm.
+ */
+public class DisplayNetworkTable extends PntAdmFunction {
+
+ /**
+ * The valid options associated with displaying a network table.
+ */
+ static final int supportedOptions[] = {
+ PntAdm.VERBOSE,
+ PntAdm.RAW,
+ PntAdm.RESOURCE,
+ PntAdm.RESOURCE_CONFIG,
+ PntAdm.PATH
+ };
+
+ /**
+ * Constructs a DisplayNetworkTable object.
+ */
+ public DisplayNetworkTable() {
+
+ validOptions = supportedOptions;
+
+ } // constructor
+
+ /**
+ * Returns the option flag for this function.
+ * @returns the option flag for this function.
+ */
+ public int getFunctionFlag() {
+ return (PntAdm.DISPLAY_NETWORK_TABLE);
+ }
+
+ /**
+ * Executes the "display network table" functionality.
+ * @return PntAdm.SUCCESS, PntAdm.ENOENT, PntAdm.WARNING, or
+ * PntAdm.CRITICAL
+ */
+ public int execute()
+ throws IllegalArgumentException {
+
+ int returnCode = PntAdm.SUCCESS;
+
+ // Is this a verbose display?
+ //
+ boolean verbose = false;
+ if (options.isSet(PntAdm.VERBOSE)) {
+ verbose = true;
+ }
+
+ // Is this a raw display?
+ //
+ boolean raw = false;
+ if (options.isSet(PntAdm.RAW)) {
+ raw = true;
+ }
+
+ if (verbose && raw) {
+ String msg = getString("display_mode_error");
+ throw new IllegalArgumentException(msg);
+ }
+
+ // Display the network table.
+ DhcpClientRecord [] dhcpClientRecords = null;
+ try {
+
+ Network network = getNetMgr().getNetwork(networkName);
+ if (network == null) {
+ printErrMessage(getString("network_name_error"));
+ return (PntAdm.WARNING);
+ }
+
+ dhcpClientRecords =
+ getNetMgr().loadNetwork(network.toString(),
+ getDhcpDatastore());
+ } catch (NoTableException e) {
+ printErrMessage(getMessage(e));
+ return (PntAdm.ENOENT);
+ } catch (Throwable e) {
+ printErrMessage(getMessage(e));
+ return (PntAdm.WARNING);
+ }
+
+ Format.print(System.out, "%-8s\t", getString("Client_ID"));
+ Format.print(System.out, "%-4s\t", getString("Flags"));
+ Format.print(System.out, "%-8s\t", getString("Client_IP"));
+ Format.print(System.out, "%-8s\t", getString("Server_IP"));
+ Format.print(System.out, "%-25s\t", getString("Lease_Expiration"));
+ Format.print(System.out, "%-8s\t", getString("Macro"));
+ Format.print(System.out, "%s\n\n", getString("Comment"));
+
+ DhcpClientRecord dhcpClientRecord;
+ for (int i = 0;
+ dhcpClientRecords != null && i < dhcpClientRecords.length;
+ i++) {
+
+ dhcpClientRecord = dhcpClientRecords[i];
+ Format.print(System.out, "%-8s\t", dhcpClientRecord.getClientId());
+ Format.print(System.out, "%-4s\t",
+ dhcpClientRecord.getFlagString(verbose));
+
+ String client;
+ if (verbose) {
+ client = dhcpClientRecord.getClientIP().getHostName();
+ } else {
+ client = dhcpClientRecord.getClientIP().toString();
+ }
+ Format.print(System.out, "%-8s\t", client);
+
+ String server;
+ if (verbose) {
+ server = dhcpClientRecord.getServerIP().getHostName();
+ } else {
+ server = dhcpClientRecord.getServerIP().toString();
+ }
+ Format.print(System.out, "%-8s\t", server);
+
+ String lease;
+ Date expiration = dhcpClientRecord.getExpiration();
+ if (raw) {
+ // Print date in seconds since the epoch
+ lease = Long.toString(expiration.getTime()/1000);
+ } else if (expiration == null || expiration.getTime() == 0) {
+ lease = getString("Zero");
+ } else if (expiration.getTime() < 0) {
+ lease = getString("Forever");
+ } else if (verbose) {
+ // Print date and time for lease in locale format
+ lease = verboseFormat.format(expiration);
+ } else {
+ // Print just the lease date in short format for locale
+ lease = shortFormat.format(expiration);
+ }
+ Format.print(System.out, "%-25s\t", lease);
+
+ Format.print(System.out, "%-8s\t", dhcpClientRecord.getMacro());
+ Format.print(System.out, "%s\n", dhcpClientRecord.getComment());
+ }
+
+ return (returnCode);
+
+ } // execute
+
+} // DisplayNetworkTable
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/ListNetworkTables.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/ListNetworkTables.java
new file mode 100644
index 0000000000..1fa24ff217
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/ListNetworkTables.java
@@ -0,0 +1,100 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.cli.pntadm;
+
+import com.sun.dhcpmgr.data.Network;
+import com.sun.dhcpmgr.bridge.NoEntryException;
+import com.sun.dhcpmgr.bridge.NoDefaultsException;
+
+import java.lang.IllegalArgumentException;
+
+/**
+ * The main class for the "list network tables" functionality
+ * of pntadm.
+ */
+public class ListNetworkTables extends PntAdmFunction {
+
+ /**
+ * The valid options associated with listing network tables.
+ */
+ static final int supportedOptions[] = {
+ PntAdm.RESOURCE,
+ PntAdm.RESOURCE_CONFIG,
+ PntAdm.PATH
+ };
+
+ /**
+ * Constructs a ListNetworkTables object.
+ */
+ public ListNetworkTables() {
+
+ validOptions = supportedOptions;
+
+ } // constructor
+
+ /**
+ * Returns the option flag for this function.
+ * @returns the option flag for this function.
+ */
+ public int getFunctionFlag() {
+ return (PntAdm.LIST_NETWORK_TABLES);
+ }
+
+ /**
+ * Executes the "list network tables" functionality.
+ * @return PntAdm.SUCCESS, PntAdm.ENOENT, PntAdm.WARNING, or
+ * PntAdm.CRITICAL
+ */
+ public int execute()
+ throws IllegalArgumentException {
+
+ int returnCode = PntAdm.SUCCESS;
+
+ // Get the list of networks.
+ //
+ Network [] networks = null;
+ try {
+ networks = getNetMgr().getNetworks(getDhcpDatastore());
+ } catch (NoEntryException e) {
+ // No network tables
+ } catch (Throwable e) {
+ printErrMessage(getMessage(e));
+ return (PntAdm.WARNING);
+ }
+
+ if (networks != null) {
+ for (int i = 0; i < networks.length; i++) {
+ System.out.println(networks[i].toString());
+ }
+ }
+
+ return (returnCode);
+
+ } // execute
+
+} // ListNetworkTables
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/Makefile b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/Makefile
new file mode 100644
index 0000000000..47c30961aa
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/Makefile
@@ -0,0 +1,82 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2001-2002 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/Makefile
+#
+
+CLASSFILES = PntAdm.class \
+ AddClientEntry.class \
+ CreateNetworkTable.class \
+ DeleteClientEntry.class \
+ DisplayNetworkTable.class \
+ ListNetworkTables.class \
+ ModifyClientEntry.class \
+ PntAdmBatch.class \
+ RemoveNetworkTable.class \
+ PntAdmFunction.class \
+ ResourceStrings.class
+
+include $(SRC)/Makefile.master
+
+CLASSPATH= $(SRC)/cmd/cmd-inet/usr.sadm/dhcpmgr
+
+JAVAFILES = $(CLASSFILES:.class=.java)
+
+MSGDIR= $(ROOT)/usr/share/lib/locale/com/sun/dhcpmgr/cli/pntadm
+MSGDIRS = $(ROOT)/usr/share/lib/locale \
+ $(ROOT)/usr/share/lib/locale/com \
+ $(ROOT)/usr/share/lib/locale/com/sun \
+ $(ROOT)/usr/share/lib/locale/com/sun/dhcpmgr \
+ $(ROOT)/usr/share/lib/locale/com/sun/dhcpmgr/cli \
+ $(MSGDIR)
+
+MSGFILES= ResourceBundle.properties
+MSGS= $(MSGFILES:%=$(MSGDIR)/%)
+
+CLEANFILES= *.class
+CLOBBERFILES=
+
+.KEEP_STATE:
+
+all: $(CLASSFILES)
+
+install: all
+
+_msg: $(MSGDIRS) $(MSGS)
+
+$(MSGDIR)/%: %
+ $(INS.file)
+
+$(MSGDIRS):
+ $(INS.dir)
+
+lint:
+
+clean:
+ $(RM) $(CLEANFILES)
+
+clobber: clean
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/ModifyClientEntry.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/ModifyClientEntry.java
new file mode 100644
index 0000000000..d92c43426c
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/ModifyClientEntry.java
@@ -0,0 +1,215 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2001-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.cli.pntadm;
+
+import com.sun.dhcpmgr.cli.common.Util;
+import com.sun.dhcpmgr.data.DhcpClientRecord;
+import com.sun.dhcpmgr.data.Macro;
+import com.sun.dhcpmgr.data.Network;
+import com.sun.dhcpmgr.bridge.BridgeException;
+import com.sun.dhcpmgr.bridge.NoEntryException;
+
+import java.lang.IllegalArgumentException;
+
+/**
+ * The main class for the "modify client" functionality of pntadm.
+ */
+public class ModifyClientEntry extends PntAdmFunction {
+
+ /**
+ * The valid options associated with modifying a client entry.
+ */
+ static final int supportedOptions[] = {
+ PntAdm.NEW_IP,
+ PntAdm.COMMENT,
+ PntAdm.LEASE_EXPIRATION,
+ PntAdm.FLAGS,
+ PntAdm.HOST_NAME,
+ PntAdm.CLIENTID,
+ PntAdm.CONVERT_CLIENTID,
+ PntAdm.MACRO_NAME,
+ PntAdm.VERIFY_MACRO,
+ PntAdm.SERVER,
+ PntAdm.RESOURCE,
+ PntAdm.RESOURCE_CONFIG,
+ PntAdm.PATH
+ };
+
+ /**
+ * The client entry to modify.
+ */
+ String clientIP;
+
+ /**
+ * Constructs a ModifyClientEntry object for the client, clientIP.
+ * @param clientIP the client name or IP address.
+ */
+ public ModifyClientEntry(String clientIP) {
+
+ this.clientIP = clientIP;
+ validOptions = supportedOptions;
+
+ } // constructor
+
+ /**
+ * Returns the option flag for this function.
+ * @returns the option flag for this function.
+ */
+ public int getFunctionFlag() {
+ return (PntAdm.MODIFY_CLIENT_ENTRY);
+ }
+
+ /**
+ * Executes the "modify client" functionality.
+ * @return PntAdm.SUCCESS, PntAdm.ENOENT, PntAdm.WARNING, or
+ * PntAdm.CRITICAL
+ */
+ public int execute()
+ throws IllegalArgumentException {
+
+ int returnCode = PntAdm.SUCCESS;
+
+ // Build up a DhcpClientRecord so that we can retrieve the current
+ // client record from the network table.
+ //
+ try {
+ DhcpClientRecord oldDhcpClientRecord = new DhcpClientRecord();
+ oldDhcpClientRecord.setClientIP(clientIP);
+
+ // Create a Network object.
+ //
+ Network network = getNetMgr().getNetwork(networkName);
+ if (network == null) {
+ printErrMessage(getString("network_name_error"));
+ return (PntAdm.WARNING);
+ }
+
+ // Go and get the current client record from the network table.
+ //
+ oldDhcpClientRecord =
+ getNetMgr().getClient(oldDhcpClientRecord, network.toString(),
+ getDhcpDatastore());
+
+ // Build up the new DhcpClientRecord from the original and the
+ // user specified options.
+ //
+ DhcpClientRecord newDhcpClientRecord =
+ (DhcpClientRecord)oldDhcpClientRecord.clone();
+
+ String newClientIP = options.valueOf(PntAdm.NEW_IP);
+ if (newClientIP != null) {
+ newDhcpClientRecord.setClientIP(newClientIP);
+ } else {
+ newDhcpClientRecord.setClientIP(clientIP);
+ }
+
+ String clientId = options.valueOf(PntAdm.CLIENTID);
+ boolean convertClientId = options.isSet(PntAdm.CONVERT_CLIENTID);
+ if (convertClientId) {
+ if (clientId == null) {
+ String msg = getString("no_clientid_specified");
+ throw new IllegalArgumentException(msg);
+ }
+ clientId = Util.asciiToHex(clientId);
+ }
+ if (clientId != null) {
+ newDhcpClientRecord.setClientId(clientId);
+ }
+
+ String flags = options.valueOf(PntAdm.FLAGS);
+ if (flags != null) {
+ newDhcpClientRecord.setFlags(flags);
+ }
+
+ String clientName = options.valueOf(PntAdm.HOST_NAME);
+ if (clientName != null) {
+ if (isHostsManaged()) {
+ newDhcpClientRecord.setClientName(clientName);
+ } else {
+ returnCode = PntAdm.WARNING;
+ }
+ }
+
+ String serverIP = options.valueOf(PntAdm.SERVER);
+ if (serverIP == null) {
+ serverIP = getSvcMgr().getServerName();
+ }
+ newDhcpClientRecord.setServerIP(serverIP);
+
+ String expiration = options.valueOf(PntAdm.LEASE_EXPIRATION);
+ if (expiration != null) {
+ newDhcpClientRecord.setExpiration(shortFormat, expiration);
+ }
+
+ boolean verifyMacro = options.isSet(PntAdm.VERIFY_MACRO);
+ String macro = options.valueOf(PntAdm.MACRO_NAME);
+ if (verifyMacro) {
+ if (macro == null) {
+ String msg = getString("no_macro_specified");
+ throw new IllegalArgumentException(msg);
+ }
+
+ // Create a Macro entry so that we can check to see if it
+ // exists in the dhcptab.
+ //
+ try {
+ Macro existingMacro =
+ getDhcptabMgr().getMacro(macro);
+ }
+ catch (BridgeException e) {
+ printErrMessage(getString("macro_not_found"));
+ return (PntAdm.WARNING);
+ }
+ }
+ if (macro != null) {
+ newDhcpClientRecord.setMacro(macro);
+ }
+
+ String comment = options.valueOf(PntAdm.COMMENT);
+ if (comment != null) {
+ newDhcpClientRecord.setComment(comment);
+ }
+
+ // Modify the client and adds host if necessary.
+ //
+ getNetMgr().modifyClient(oldDhcpClientRecord, newDhcpClientRecord,
+ network.toString(), getDhcpDatastore());
+
+ } catch (NoEntryException e) {
+ printErrMessage(getMessage(e));
+ returnCode = PntAdm.ENOENT;
+ } catch (Throwable e) {
+ printErrMessage(getMessage(e));
+ returnCode = PntAdm.WARNING;
+ }
+
+ return (returnCode);
+
+ } // execute
+
+} // ModifyClientEntry
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/PntAdm.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/PntAdm.java
new file mode 100644
index 0000000000..ca78306375
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/PntAdm.java
@@ -0,0 +1,260 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.cli.pntadm;
+
+import com.sun.dhcpmgr.cli.common.*;
+
+import java.lang.IllegalArgumentException;
+
+/**
+ * This class represents the entry point to the DHCP CLI network tables
+ * administration.
+ */
+public class PntAdm
+ extends DhcpCliProgram {
+
+ /**
+ * The program signature.
+ */
+ public static final String SIGNATURE = "pntadm: ";
+
+ /**
+ * The valid options for all PntAdm administration.
+ */
+ private static String optString = "LPCRyavxA:D:M:r:p:u:s:i:f:e:h:m:c:n:B;";
+
+ public static final int ADD_CLIENT_ENTRY = 'A';
+ public static final int MODIFY_CLIENT_ENTRY = 'M';
+ public static final int DELETE_CLIENT_ENTRY = 'D';
+ public static final int CREATE_NETWORK_TABLE = 'C';
+ public static final int REMOVE_NETWORK_TABLE = 'R';
+ public static final int DISPLAY_NETWORK_TABLE = 'P';
+ public static final int LIST_NETWORK_TABLES = 'L';
+ public static final int BATCH_EXECUTION = 'B';
+
+ public static final int VERIFY_MACRO = 'y';
+ public static final int DELETE_HOST = 'y';
+ public static final int CONVERT_CLIENTID = 'a';
+ public static final int RAW = 'x';
+ public static final int VERBOSE = 'v';
+ public static final int RESOURCE = 'r';
+ public static final int RESOURCE_CONFIG = 'u';
+ public static final int PATH = 'p';
+ public static final int SERVER = 's';
+ public static final int CLIENTID = 'i';
+ public static final int FLAGS = 'f';
+ public static final int LEASE_EXPIRATION = 'e';
+ public static final int HOST_NAME = 'h';
+ public static final int MACRO_NAME = 'm';
+ public static final int COMMENT = 'c';
+ public static final int NEW_IP = 'n';
+
+ /**
+ * Constructs a pntadm command.
+ * @param args the options to the command.
+ */
+ public PntAdm(String [] args) {
+ reset(args);
+ } // constructor
+
+ /**
+ * Resets a PntAdm for reuse. Used by DhcpBatch program.
+ * @param args the options to the command.
+ */
+ public void reset(String [] args) {
+
+ clearFunction();
+ options = new DhcpCliOptions();
+ this.args = args;
+
+ }
+
+ /**
+ * Returns the manpage signature for the program.
+ * @return the manpage signature for the program.
+ */
+ public String getManPage() {
+ return "pntadm(1M)";
+ }
+
+ /**
+ * Displays program usage.
+ */
+ public void usage() {
+
+ DhcpCliPrint.printErrMessage(getString("usage"));
+
+ } // usage
+
+ /**
+ * Executes the program function.
+ * @return SUCCESS, EXISTS, ENOENT, WARNING, or CRITICAL
+ */
+ public int execute() {
+
+ int returnCode = SUCCESS;
+
+ // Get the options and go exec the correct function.
+ //
+ GetOpt getopt = new GetOpt(args, optString);
+ try {
+ int option;
+ while ((option = getopt.getNextOption()) != -1) {
+ processArg(option, getopt.getOptionArg());
+ }
+
+ int networkIndex = getopt.getNextOptionIndex();
+ String network = null;
+
+ if (args.length == (networkIndex + 1)) {
+ network = args[networkIndex];
+ } else if (args.length >= networkIndex + 1) {
+ throw new IllegalArgumentException(
+ ResourceStrings.getString("invalid_args"));
+ }
+
+ if (function == null) {
+ String msg = getString("no_function_error");
+ throw new IllegalArgumentException(msg);
+ }
+
+ // Check the validity of the data store version.
+ //
+ if (!function.isVersionValid(false)) {
+ return (CRITICAL);
+ }
+
+ // Not all functions accept network arguments.
+ //
+ if (function instanceof ListNetworkTables ||
+ function instanceof PntAdmBatch) {
+ if (network != null) {
+ String msg = getString("network_specified");
+ throw new IllegalArgumentException(msg);
+ }
+ } else {
+ if (network == null) {
+ String msg = getString("no_network_specified");
+ throw new IllegalArgumentException(msg);
+ }
+ }
+
+ // Create a DHCP datastore object with the user specified objects.
+ //
+ function.setDhcpDatastore(options.valueOf(RESOURCE),
+ options.valueOf(PATH), options.valueOf(RESOURCE_CONFIG));
+
+ function.setOptions(options);
+ ((PntAdmFunction)function).setNetworkName(network);
+ returnCode = function.execute();
+
+ } catch (IllegalArgumentException e) {
+ StringBuffer msg = new StringBuffer(SIGNATURE);
+ msg.append(DhcpCliFunction.getMessage(e));
+ DhcpCliPrint.printErrMessage(msg.toString());
+ DhcpCliPrint.printErrMessage("");
+ usage();
+ returnCode = CRITICAL;
+ } catch (Throwable e) {
+ StringBuffer msg = new StringBuffer(SIGNATURE);
+ msg.append(DhcpCliFunction.getMessage(e));
+ DhcpCliPrint.printErrMessage(msg.toString());
+ returnCode = CRITICAL;
+ }
+
+ return (returnCode);
+
+ } // execute
+
+ /**
+ * Processes one program argument.
+ * @param option the option flag
+ * @param value the option value(if any)
+ * @exception IllegalArgumentException if an invalid argument was entered
+ */
+ public void processArg(int option, String value)
+ throws IllegalArgumentException {
+
+ switch (option) {
+ case ADD_CLIENT_ENTRY:
+ setFunction(new AddClientEntry(value));
+ break;
+ case MODIFY_CLIENT_ENTRY:
+ setFunction(new ModifyClientEntry(value));
+ break;
+ case DELETE_CLIENT_ENTRY:
+ setFunction(new DeleteClientEntry(value));
+ break;
+ case CREATE_NETWORK_TABLE:
+ setFunction(new CreateNetworkTable());
+ break;
+ case REMOVE_NETWORK_TABLE:
+ setFunction(new RemoveNetworkTable());
+ break;
+ case DISPLAY_NETWORK_TABLE:
+ setFunction(new DisplayNetworkTable());
+ break;
+ case LIST_NETWORK_TABLES:
+ setFunction(new ListNetworkTables());
+ break;
+ case BATCH_EXECUTION:
+ setFunction(new PntAdmBatch(value));
+ break;
+ default:
+ options.setOption(option, value);
+ }
+
+ } // processArg
+
+ /**
+ * Returns a localized string for this function
+ * @param key the resource bundle string identifier
+ * @return string from resource bundle.
+ */
+ public String getString(String key) {
+
+ return ResourceStrings.getString(key);
+
+ } // getString
+
+ /**
+ * The entry point for the program.
+ * @param args the program arguments
+ */
+ public static void main(String[] args) {
+
+ PntAdm pntadm = new PntAdm(args);
+ int returnCode = PntAdm.CRITICAL;
+ if (pntadm.isValidUser()) {
+ returnCode = pntadm.execute();
+ }
+ System.exit(returnCode);
+
+ } // main
+
+} // PntAdm
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/PntAdmBatch.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/PntAdmBatch.java
new file mode 100644
index 0000000000..d5c20e2118
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/PntAdmBatch.java
@@ -0,0 +1,79 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.cli.pntadm;
+
+import com.sun.dhcpmgr.cli.dhcpbatch.*;
+
+import java.lang.IllegalArgumentException;
+
+/**
+ * The main class for the batch functionality of pntadm.
+ */
+public class PntAdmBatch extends PntAdmFunction {
+
+ /**
+ * The valid options associated with batching.
+ */
+ static final int supportedOptions[] = {
+ PntAdm.VERBOSE
+ };
+
+ private String inputSource = null;
+
+ /**
+ * Constructs a PntAdmBatch object.
+ */
+ public PntAdmBatch(String inputSource) {
+
+ this.inputSource = inputSource;
+ validOptions = supportedOptions;
+
+ } // constructor
+
+ /**
+ * Returns the option flag for this function.
+ * @returns the option flag for this function.
+ */
+ public int getFunctionFlag() {
+ return (PntAdm.BATCH_EXECUTION);
+ }
+
+ /**
+ * Executes the batch functionality.
+ * @return one of the PntAdm return codes.
+ */
+ public int execute()
+ throws IllegalArgumentException {
+
+ DhcpBatch batch = new DhcpBatch(inputSource);
+ batch.setVerbose(options.isSet(PntAdm.VERBOSE));
+ return (batch.execute());
+
+ } // execute
+
+} // PntAdmBatch
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/PntAdmFunction.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/PntAdmFunction.java
new file mode 100644
index 0000000000..a89dd71550
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/PntAdmFunction.java
@@ -0,0 +1,114 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2001-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.cli.pntadm;
+
+import com.sun.dhcpmgr.cli.common.DhcpCliFunction;
+import com.sun.dhcpmgr.cli.common.DhcpCliPrint;
+import com.sun.dhcpmgr.data.DhcpdOptions;
+import com.sun.dhcpmgr.bridge.BridgeException;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+
+/**
+ * Abstract class implemented by all the pntadm "function" classes.
+ */
+public abstract class PntAdmFunction
+ extends DhcpCliFunction {
+
+ /**
+ * Short date format for printing/parsing lease expiration
+ */
+ DateFormat shortFormat = new SimpleDateFormat("MM/dd/yyyy");
+
+ /**
+ * Verbose date format for printing/parsing lease expiration.
+ */
+ DateFormat verboseFormat =
+ DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.LONG);
+
+ /**
+ * The network on which the functions should "operate"
+ */
+ String networkName = null;
+
+ /**
+ * Sets the networkName.
+ * @param network name of the network
+ */
+ public void setNetworkName(String network) {
+
+ networkName = network;
+
+ } // setNetworkName
+
+ /**
+ * Returns a localized string for this function
+ * @param key the resource bundle string identifier
+ */
+ public String getString(String key) {
+
+ return ResourceStrings.getString(key);
+
+ } // getString
+
+ /**
+ * Returns whether or not hosts table is manageable.
+ * @return whether or not hosts table is manageable.
+ */
+ public boolean isHostsManaged() {
+
+ boolean result = false;
+
+ try {
+ DhcpdOptions opts =
+ getSvcMgr().readDefaults();
+ if (opts.getHostsResource() != null) {
+ result = true;
+ } else {
+ throw new BridgeException();
+ }
+ } catch (BridgeException e) {
+ printErrMessage(getString("no_host_resource_warning"));
+ }
+
+ return result;
+
+ } // isHostsManaged
+
+ /**
+ * Prints an error message.
+ * @param msg the message to print.
+ */
+ public void printErrMessage(String msg) {
+ StringBuffer fullmsg = new StringBuffer(PntAdm.SIGNATURE);
+ fullmsg.append(msg);
+ DhcpCliPrint.printErrMessage(fullmsg.toString());
+ } // printErrMessage
+
+} // PntAdmFunction
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/RemoveNetworkTable.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/RemoveNetworkTable.java
new file mode 100644
index 0000000000..561d8ac087
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/RemoveNetworkTable.java
@@ -0,0 +1,104 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.cli.pntadm;
+
+import com.sun.dhcpmgr.data.DhcpClientRecord;
+import com.sun.dhcpmgr.data.Network;
+import com.sun.dhcpmgr.bridge.BridgeException;
+import com.sun.dhcpmgr.bridge.NoTableException;
+
+import java.lang.IllegalArgumentException;
+
+/**
+ * The main class for the "remove network table" functionality
+ * of pntadm.
+ */
+public class RemoveNetworkTable extends PntAdmFunction {
+
+ /**
+ * The valid options associated with removing a network table.
+ */
+ static final int supportedOptions[] = {
+ PntAdm.RESOURCE,
+ PntAdm.RESOURCE_CONFIG,
+ PntAdm.PATH
+ };
+
+ /**
+ * Constructs a RemoveNetworkTable object.
+ */
+ public RemoveNetworkTable() {
+
+ validOptions = supportedOptions;
+
+ } // constructor
+
+ /**
+ * Returns the option flag for this function.
+ * @returns the option flag for this function.
+ */
+ public int getFunctionFlag() {
+ return (PntAdm.REMOVE_NETWORK_TABLE);
+ }
+
+ /**
+ * Executes the "remove network table" functionality.
+ * @return PntAdm.SUCCESS, PntAdm.ENOENT, PntAdm.WARNING, or
+ * PntAdm.CRITICAL
+ */
+ public int execute()
+ throws IllegalArgumentException {
+
+ int returnCode = PntAdm.SUCCESS;
+
+ // Create a Network object.
+ //
+ try {
+ Network network = getNetMgr().getNetwork(networkName);
+ if (network == null) {
+ printErrMessage(getString("network_name_error"));
+ return (PntAdm.WARNING);
+ }
+
+ // Delete the network table.
+ //
+ getNetMgr().deleteNetwork(network.toString(), false, false,
+ getDhcpDatastore());
+ } catch (NoTableException e) {
+ printErrMessage(getMessage(e));
+ returnCode = PntAdm.ENOENT;
+ } catch (Throwable e) {
+ printErrMessage(getMessage(e));
+ returnCode = PntAdm.WARNING;
+ }
+
+ return (returnCode);
+
+ } // execute
+
+} // RemoveNetworkTable
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/ResourceBundle.properties b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/ResourceBundle.properties
new file mode 100644
index 0000000000..334edb9073
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/ResourceBundle.properties
@@ -0,0 +1,98 @@
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Cannot create Network
+#
+network_name_error=Could not determine correct network table name.
+
+#
+# Specifying both verbose and raw display mode error
+#
+display_mode_error=Cannot specify both raw and verbose display modes.
+
+#
+# No network specified error
+#
+no_network_specified=This function requires a network to be specified.
+
+#
+# No network specified error
+#
+network_specified=This function does not accept a network argument.
+
+#
+# No client ID specified error
+#
+no_clientid_specified=Cannot verify undefined client ID
+
+#
+# No macro specified error
+#
+no_macro_specified=Cannot verify undefined macro.
+
+#
+# No hosts resource warning
+#
+no_host_resource_warning=Warning - No hosts resource value found in DHCP configuration file.\nOperation succeeded, but hosts table was not updated.
+
+#
+# Display Network Table Headers
+#
+Client_ID=Client ID
+Flags=Flags
+Client_IP=Client IP
+Server_IP=Server IP
+Lease_Expiration=Lease Expiration
+Macro=Macro
+Comment=Comment
+
+#
+# Lease literals
+#
+Zero=Zero
+Forever= Forever
+
+#
+# Macro does not exist error message
+#
+macro_not_found=Cannot find macro in dhcptab.
+
+#
+# No function defined error
+#
+no_function_error=Must specify one of 'C', 'A', 'M', 'D', 'R', 'P', 'L' or 'B'.
+
+#
+# An invalid argument was input to the program.
+#
+invalid_args=Invalid arguments on command line.
+
+#
+# Usage
+#
+usage=pntadm [-r (resource)] [-p (path)] [-u (uninterpreted data)] (options) [(network ip or name)]\n\nWhere (options) is one of:\n\n -C Create the named table\n\n -A (client ip or name) Add client entry. Sub-options:\n [-c (comment)]\n [-e (lease expiration)]\n [-f (flags)]\n [-h (client host name)]\n [-i (client identifier)[-a]]\n [-m (dhcptab macro reference)[-y]]\n [-s (server ip or name)]\n\n -M (client ip or name) Modify client entry. Sub-options:\n [-c (new comment)]\n [-e (new lease expiration)]\n [-f (new flags)]\n [-h (new client host name)]\n [-i (new client identifier)[-a]]\n [-m (new dhcptab macro reference)[-y]]\n [-n (new client ip)]\n [-s (new server ip or name)]\n\n -D (client ip or name) Delete client entry. Sub-options:\n [-y] Remove hosts table entry\n\n -R Remove the named table\n\n -P Display the named table. Sub-options:\n [-v] Display lease time in full format.\n [-x] Display lease time in raw format.\n\n -L List the configured DHCP networks\n\n -B [batchfile] Run command in batch input mode. Sub-options:\n [-v] Output commands as they are processed.\n\n The network ip or name argument is required for all options except -L and -B\n
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/ResourceStrings.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/ResourceStrings.java
new file mode 100644
index 0000000000..e8c1753640
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/ResourceStrings.java
@@ -0,0 +1,64 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.cli.pntadm;
+
+import java.util.*;
+
+/**
+ * This class provides a convenient method to retrieve resources for
+ * the pntadm package.
+ */
+public class ResourceStrings {
+
+ /**
+ * The handle to the resource bundle for the module.
+ */
+ private static ResourceBundle bundle = null;
+
+ /**
+ * Return a string from the resource bundle.
+ * @param key the key to the resource bundle string.
+ * @return the resource bundle string.
+ */
+ public static String getString(String key) {
+ String msg = null;
+ try {
+ if (bundle == null) {
+ bundle = ResourceBundle.getBundle(
+ "com.sun.dhcpmgr.cli.pntadm.ResourceBundle",
+ Locale.getDefault());
+ }
+ msg = bundle.getString(key);
+ } catch (Throwable e) {
+ msg = new String(key);
+ }
+ return msg;
+
+ } // getString
+
+} // ResourceStrings
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/cfgboot.san b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/cfgboot.san
new file mode 100644
index 0000000000..3af808a428
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/cfgboot.san
@@ -0,0 +1,123 @@
+#!/usr/bin/ksh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+
+#
+# Source the utilities.
+#
+DIRNAME=`dirname $0`
+. ${DIRNAME}/utilities.san
+
+#
+# Process the input arguments.
+#
+VALIDOPTS=d
+process_args $@
+
+#
+# In case the tester wants to see script output, allow them
+# to run in debug mode.
+#
+TESTNAME=`basename $0`
+if [ ! -z "${DEBUG}" ]
+then
+ OUTFILE=/tmp/${TESTNAME}.$$
+ echo "Output from test: ${TESTNAME}" >${OUTFILE}
+ echo >>${OUTFILE}
+ echo "debug output can be found at ${OUTFILE}"
+else
+ OUTFILE=/dev/null
+fi
+
+#
+# Set variables.
+#
+SRVNAME=`uname -n`
+SRVADDR=`get_server_ip`
+DHCP_CONFIG=/etc/inet/dhcpsvc.conf
+
+#
+# If the DHCP server is running, kill it.
+#
+pkill -x -u 0 in.dhcpd
+
+#
+# Make sure to clean up before we configure.
+#
+rm -f ${DHCP_CONFIG} >>${OUTFILE} 2>&1
+
+#
+# Config.
+#
+/usr/sbin/dhcpconfig -R ${SRVADDR} >>${OUTFILE} 2>&1
+
+#
+# Verify that the dhcp config file was created.
+#
+if [ ! -f ${DHCP_CONFIG} ]
+then
+ echo "${DHCP_CONFIG} not created."
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+#
+# Verify that the run mode was defined in the config file
+#
+DEFLINE=`grep "RUN_MODE=relay" ${DHCP_CONFIG}`
+if [ -z "${DEFLINE}" ]
+then
+ echo "RUN_MODE not set correctly in ${DHCP_CONFIG}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+#
+# Verify that the relay destinations was defined in the config file
+#
+DEFLINE=`grep "RELAY_DESTINATIONS=${SRVADDR}" ${DHCP_CONFIG}`
+if [ -z "${DEFLINE}" ]
+then
+ echo "RELAY_DESTINATIONS not set correctly in ${DHCP_CONFIG}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+#
+# Verify that the dhcp server was started.
+#
+PID=`pgrep -x -u 0 in.dhcpd`
+if [ -z "${PID}" ]
+then
+ echo "DHCP Server was not started."
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+echo "${TESTNAME} - Test passed."
+exit 0
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/cfgdhcp.san b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/cfgdhcp.san
new file mode 100644
index 0000000000..c100676a0d
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/cfgdhcp.san
@@ -0,0 +1,309 @@
+#!/usr/bin/ksh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+
+#
+# Source the utilities.
+#
+DIRNAME=`dirname $0`
+. ${DIRNAME}/utilities.san
+
+#
+# Process the input arguments.
+#
+VALIDOPTS=dr:p:
+process_args $@
+
+#
+# In case the tester wants to see script output, allow them
+# to run in debug mode.
+#
+TESTNAME=`basename $0`
+if [ ! -z "${DEBUG}" ]
+then
+ OUTFILE=/tmp/${TESTNAME}.$$
+ echo "Output from test: ${TESTNAME}" >${OUTFILE}
+ echo >>${OUTFILE}
+ echo "debug output can be found at ${OUTFILE}"
+else
+ OUTFILE=/dev/null
+fi
+
+#
+# Set DNSSERV and DNSDMAIN if server has a resolv.conf. Return 0 if it does,
+# nonzero otherwise.
+#
+get_dns_parms()
+{
+ if [ ! -f /etc/resolv.conf ]
+ then
+ return 1
+ fi
+
+ nsr=0
+ dmn=0
+ DNSRV=""
+ DNAME=""
+ for item in `cat /etc/resolv.conf`
+ do
+ if [ "${item}" = "nameserver" ]
+ then
+ nsr=1
+ continue
+ fi
+ if [ "${item}" = "domain" ]
+ then
+ dmn=1
+ continue
+ fi
+ if [ ${nsr} -eq 1 ]
+ then
+ if [ -z "${DNSRV}" ]
+ then
+ DNSRV=${item}
+ else
+ DNSRV="${DNSRV} ${item}"
+ fi
+ nsr=0
+ continue
+ fi
+ if [ ${dmn} -eq 1 ]
+ then
+ DNAME="${item}"
+ dmn=0
+ continue
+ fi
+ done
+ if [ ! -z "${DNSRV}" ]
+ then
+ DNSSERV="DNSserv=${DNSRV}"
+ fi
+ if [ ! -z "${DNAME}" ]
+ then
+ DNSDMAIN="DNSdmain=\"${DNAME}\""
+ fi
+ if [ ! -z "${DNSSERV}" -o ! -z "${DNSDMAIN}" ]
+ then
+ return 0
+ fi
+ return 1
+}
+
+#
+# Set variables.
+#
+SRVNAME=`uname -n`
+SRVADDR=`get_server_ip`
+DHCPHOSTS_RSRC=files
+DHCP_DEFAULTS=/etc/inet/dhcpsvc.conf
+
+#
+# If the DHCP server is running, kill it.
+#
+pkill -x -u 0 in.dhcpd
+
+#
+# Make sure to clean up before we configure.
+#
+/usr/sbin/dhcpconfig -U -f -x >>${OUTFILE} 2>&1
+rm -f ${DHCP_DEFAULTS} >>${OUTFILE} 2>&1
+
+#
+# Config.
+#
+/usr/sbin/dhcpconfig -D -r ${DHCPRSRC} -p ${DHCPPATH} -h ${DHCPHOSTS_RSRC} >>${OUTFILE} 2>&1
+RET=$?
+if [ "${RET}" != "0" ]
+then
+ echo "Error configuring DHCP = ${RET}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+#
+# Verify that the dhcp defaults file was created.
+#
+if [ ! -f ${DHCP_DEFAULTS} ]
+then
+ echo "${DHCP_DEFAULTS} not created."
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+#
+# Verify that the run mode was defined in the defaults file
+#
+DEFLINE=`grep "RUN_MODE=server" ${DHCP_DEFAULTS}`
+if [ -z "${DEFLINE}" ]
+then
+ echo "RUN_MODE not set correctly in ${DHCP_DEFAULTS}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+#
+# Verify that the datastore resource was defined in the defaults file
+#
+DEFLINE=`grep "RESOURCE=${DHCPRSRC}" ${DHCP_DEFAULTS}`
+if [ -z "${DEFLINE}" ]
+then
+ echo "RESOURCE not set correctly in ${DHCP_DEFAULTS}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+#
+# Verify that the datastore path was defined in the defaults file
+#
+DEFLINE=`grep "PATH=${DHCPPATH}" ${DHCP_DEFAULTS}`
+if [ -z "${DEFLINE}" ]
+then
+ echo "PATH not set correctly in ${DHCP_DEFAULTS}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+#
+# Verify that the hosts resource was defined in the defaults file
+#
+DEFLINE=`grep "HOSTS_RESOURCE=${DHCPHOSTS_RSRC}" ${DHCP_DEFAULTS}`
+if [ -z "${DEFLINE}" ]
+then
+ echo "HOSTS_RESOURCE not set correctly in ${DHCP_DEFAULTS}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+#
+# Verify that the dhcptab data was created.
+#
+DATAFILE=/tmp/${TESTNAME}.data.$$
+/usr/sbin/dhtadm -r ${DHCPRSRC} -p ${DHCPPATH} -P >${DATAFILE} 2>>${OUTFILE}
+RET=$?
+if [ "${RET}" != "0" ]
+then
+ rm ${DATAFILE}
+ echo "Error displaying dhcptab = ${RET}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+#
+# Verify that the Locale symbol is defined in the dhcptab.
+#
+LOCALE=`grep "^Locale" ${DATAFILE}`
+if [ -z "${LOCALE}" ]
+then
+ rm ${DATAFILE}
+ echo "Locale macro does not exist in dhcptab"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+#
+# Verify that the server macro is defined in the dhcptab.
+#
+SERVER_MACRO=`grep "^${SRVNAME}" ${DATAFILE}`
+if [ -z "${SERVER_MACRO}" ]
+then
+ rm ${DATAFILE}
+ echo "Server macro does not exist in dhcptab"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+rm ${DATAFILE}
+
+#
+# Grab the server macro definition
+#
+MACRO_DEFINITION=$(get_value ${SERVER_MACRO})
+
+#
+# Verify that the Locale symbol is defined as part of the macro definition
+#
+SRCH=":Include=Locale:"
+macro_find_and_replace
+
+#
+# Verify that the LeaseTim symbol is defined as part of the macro definition
+#
+SRCH=":LeaseTim=86400:"
+macro_find_and_replace
+
+#
+# Verify that the LeaseNeg symbol is defined as part of the macro definition
+#
+SRCH=":LeaseNeg:"
+macro_find_and_replace
+
+#
+# Verify that the Timeserv symbol is defined as part of the macro definition
+#
+SRCH=":Timeserv=${SRVADDR}:"
+macro_find_and_replace
+
+#
+# Go get the DNS settings
+#
+get_dns_parms
+
+#
+# Verify that the DNSdmain symbol is defined as part of the macro definition
+#
+SRCH=:${DNSDMAIN}:
+macro_find_and_replace
+
+#
+# Verify that the DNSserv symbol is defined as part of the macro definition
+#
+SRCH=:${DNSSERV}:
+macro_find_and_replace
+
+#
+# Verify that all symbols have been accounted for
+#
+if [ "${MACRO_DEFINITION}" != ":" ]
+then
+ echo "Server macro definition has invalid extra symbols: ${MACRO_DEFINITION}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+#
+# Verify that the dhcp server was started.
+#
+PID=`pgrep -x -u 0 in.dhcpd`
+if [ -z "${PID}" ]
+then
+ echo "DHCP Server was not started."
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+echo "${TESTNAME} - Test passed."
+exit 0
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/cfgnet.san b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/cfgnet.san
new file mode 100644
index 0000000000..d05d68ef1e
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/cfgnet.san
@@ -0,0 +1,229 @@
+#!/usr/bin/ksh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+
+#
+# Source the utilities.
+#
+DIRNAME=`dirname $0`
+. ${DIRNAME}/utilities.san
+
+#
+# Process the input arguments.
+#
+VALIDOPTS=dr:p:
+process_args $@
+
+#
+# In case the tester wants to see script output, allow them
+# to run in debug mode.
+#
+TESTNAME=`basename $0`
+if [ ! -z "${DEBUG}" ]
+then
+ OUTFILE=/tmp/${TESTNAME}.$$
+ echo "Output from test: ${TESTNAME}" >${OUTFILE}
+ echo >>${OUTFILE}
+ echo "debug output can be found at ${OUTFILE}"
+else
+ OUTFILE=/dev/null
+fi
+
+#
+# Set NISSERV and NISDMAIN if NIS or NIS+ in YP compat mode.
+# Returns 0 if these variables are set, nonzero otherwise.
+#
+get_nis_parms()
+{
+ unset NISSERV
+ unset NISDMAIN
+ NSSHOSTS=`grep '^hosts:' /etc/nsswitch.conf`
+ for TMP in ${NSSHOSTS}
+ do
+ case "${TMP}" in
+ "nis")
+ ypwhich >/dev/null 2>&1
+ if [ ${?} -eq 0 ]
+ then
+ X=`ypwhich -m hosts`
+ NISSERV=""
+ for NSERV in `ypmatch ${X} hosts.byname | awk '{ print $1 }'`
+ do
+ if [ -z "${NISSERV}" ]
+ then
+ NISSERV=${NSERV}
+ else
+ NISSERV="${NISSERV} ${NSERV}"
+ fi
+ done
+ unset NSERV
+ NISDMAIN=`domainname`
+ fi
+ ;;
+ esac
+ unset TMP
+ done
+}
+
+#
+# Set variables.
+#
+SRVNAME=`uname -n`
+SRVADDR=`get_server_ip`
+
+#
+# Determine subnet, netmask, and broadcast address.
+#
+get_default_class ${SRVADDR} | read DEFNET DEFMASK
+SUBNET=`get_netmask ${SRVADDR}`
+if [ -z "${SUBNET}" ]
+then
+ if [ "${DEFNET}" != "${SRVADDR}" ]
+ then
+ # likely subnetted/supernetted.
+ print - "\n\n###\tWarning\t###\n"
+ print - "Network ${SRVADDR} is netmasked, but no entry was found in the 'netmasks'\ntable; please update the 'netmasks' table in the appropriate\nnameservice before continuing (see /etc/nsswitch.conf).\n" >&2
+ return 1
+ else
+ # use the default.
+ SUBNET="${DEFMASK}"
+ fi
+fi
+
+BCAST=`get_bcast_addr ${SRVADDR} ${SUBNET}`
+SUBNETADDR=`get_subnet_addr ${SRVADDR} ${SUBNET}`
+
+#
+# Make sure to clean up before we configure.
+#
+/usr/sbin/pntadm -r ${DHCPRSRC} -p ${DHCPPATH} -R ${SUBNETADDR} >>${OUTFILE} 2>&1
+/usr/sbin/dhtadm -r ${DHCPRSRC} -p ${DHCPPATH} -D -m ${SUBNETADDR} >>${OUTFILE} 2>&1
+
+#
+# Configure.
+#
+/usr/sbin/dhcpconfig -N ${SUBNETADDR} >>${OUTFILE} 2>&1
+RET=$?
+if [ "${RET}" != "0" ]
+then
+ echo "Error configuring ${SUBNETADDR} table = ${RET}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+#
+# Verify that the network table was created.
+#
+NETTABS=`/usr/sbin/pntadm -r ${DHCPRSRC} -p ${DHCPPATH} -L 2>>${OUTFILE}`
+NETTAB=`echo ${NETTABS} | fgrep ${SUBNETADDR}`
+if [ -z ${NETTAB} ]
+then
+ echo "Network table for ${SUBNETADDR} not created."
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+#
+# Verify that the network macro was created.
+#
+DATAFILE=/tmp/${TESTNAME}.data.$$
+/usr/sbin/dhtadm -r ${DHCPRSRC} -p ${DHCPPATH} -P >${DATAFILE} 2>>${OUTFILE}
+RET=$?
+if [ "${RET}" != "0" ]
+then
+ rm ${DATAFILE}
+ echo "Error displaying dhcptab = ${RET}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+NETWORK_MACRO=`grep "^${SUBNETADDR}" ${DATAFILE}`
+if [ -z "${NETWORK_MACRO}" ]
+then
+ rm ${DATAFILE}
+ echo "Server macro does not exist in dhcptab"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+rm ${DATAFILE}
+
+#
+# Grab the server macro definition
+#
+MACRO_DEFINITION=$(get_value ${NETWORK_MACRO})
+
+#
+# Verify that the Subnet symbol is defined as part of the macro definition
+#
+SRCH=":Subnet=${SUBNET}:"
+macro_find_and_replace
+
+#
+# Verify that the RDiscvyF symbol is defined as part of the macro definition
+#
+SRCH=":RDiscvyF=1:"
+macro_find_and_replace
+
+#
+# Verify that the Broadcst symbol is defined as part of the macro definition
+#
+SRCH=":Broadcst=${BCAST}:"
+macro_find_and_replace
+
+#
+# Get the NIS info
+#
+get_nis_parms
+
+#
+# Verify that the DNSdmain symbol is defined as part of the macro definition
+#
+if [ ! -z "${NISDMAIN}" ]
+then
+ SRCH=":NISdmain=\"${NISDMAIN}\":"
+ macro_find_and_replace
+fi
+
+if [ ! -z "${NISSERV}" ]
+then
+ SRCH=":NISservs=${NISSERV}:"
+ macro_find_and_replace
+fi
+
+#
+# Verify that all symbols have been accounted for
+#
+if [ "${MACRO_DEFINITION}" != ":" ]
+then
+ echo "Network macro definition has invalid extra symbols: ${MACRO_DEFINITION}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+echo "${TESTNAME} - Test passed."
+exit 0
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/createaddr.san b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/createaddr.san
new file mode 100644
index 0000000000..35d8518958
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/createaddr.san
@@ -0,0 +1,199 @@
+#!/usr/bin/ksh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+
+#
+# Source the utilities.
+#
+DIRNAME=`dirname $0`
+. ${DIRNAME}/utilities.san
+
+#
+# Process the input arguments.
+#
+VALIDOPTS=dr:p:
+process_args $@
+
+#
+# In case the tester wants to see script output, allow them
+# to run in debug mode.
+#
+TESTNAME=`basename $0`
+if [ ! -z "${DEBUG}" ]
+then
+ OUTFILE=/tmp/${TESTNAME}.debug.$$
+ echo "Output from test: ${TESTNAME}" >${OUTFILE}
+ echo >>${OUTFILE}
+ echo "debug output can be found at ${OUTFILE}"
+else
+ OUTFILE=/dev/null
+fi
+
+#
+# Set variables.
+#
+SRVNAME=`uname -n`
+SRVADDR=`get_server_ip`
+
+#
+# Determine subnet, netmask, and broadcast address.
+#
+get_default_class ${SRVADDR} | read DEFNET DEFMASK
+SUBNET=`get_netmask ${SRVADDR}`
+if [ -z "${SUBNET}" ]
+then
+ if [ "${DEFNET}" != "${SRVADDR}" ]
+ then
+ # likely subnetted/supernetted.
+ print - "\n\n###\tWarning\t###\n"
+ print - "Network ${SRVADDR} is netmasked, but no entry was found in the 'netmasks'\ntable; please update the 'netmasks' table in the appropriate\nnameservice before continuing (see /etc/nsswitch.conf).\n" >&2
+ return 1
+ else
+ # use the default.
+ SUBNET="${DEFMASK}"
+ fi
+fi
+
+SUBNETADDR=`get_subnet_addr ${SRVADDR} ${SUBNET}`
+
+#
+# Verify that the network table exists.
+#
+NETTABS=`/usr/sbin/pntadm -r ${DHCPRSRC} -p ${DHCPPATH} -L 2>>${OUTFILE}`
+NETTAB=`echo ${NETTABS} | fgrep ${SUBNETADDR}`
+if [ -z ${NETTAB} ]
+then
+ echo "Network table for ${SUBNETADDR} does not exist."
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+#
+# Create the client entry.
+#
+CLIENTID="01AABBCCDDEEFF"
+FLAGS="00"
+CADDR=${SRVADDR}
+SADDR=${SRVADDR}
+EXPIRE="03/15/2000"
+MACRO="UNKNOWN"
+HOST="TESTCLIENT"
+
+/usr/sbin/pntadm -r ${DHCPRSRC} -p ${DHCPPATH} -A ${SRVADDR} -e ${EXPIRE} -i ${CLIENTID} -s ${SRVADDR} ${SUBNETADDR} >>${OUTFILE} 2>&1
+RET=$?
+if [ "${RET}" != "0" ]
+then
+ echo "Error adding entry ${SRVADDR} to table ${SUBNETADDR} = ${RET}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+#
+# Verify that entry was added.
+#
+DATAFILE=/tmp/${TESTNAME}.data.$$
+/usr/sbin/pntadm -r ${DHCPRSRC} -p ${DHCPPATH} -P ${SUBNETADDR} >${DATAFILE} 2>>${OUTFILE}
+
+ENTRY=`grep "^${CLIENTID}" ${DATAFILE}`
+if [ -z "${ENTRY}" ]
+then
+ rm ${DATAFILE}
+ echo "Client entry not added to ${SUBNETADDR}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+rm ${DATAFILE}
+
+#
+# Verify that the entry fields are correct.
+#
+FIELD=`echo $ENTRY | cut -d " " -f1,1`
+if [ "${FIELD}" != "${CLIENTID}" ]
+then
+ echo "ClientID of entry was not added correctly to ${SUBNETADDR}"
+ echo "${FIELD} != ${CLIENTID}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+FIELD=`echo $ENTRY | cut -d " " -f2,2`
+if [ "${FIELD}" != "${FLAGS}" ]
+then
+ echo "Flags of entry was not added correctly to ${SUBNETADDR}"
+ echo "${FIELD} != ${FLAGS}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+FIELD=`echo $ENTRY | cut -d " " -f3,3`
+if [ "${FIELD}" != "${CADDR}" ]
+then
+ echo "Client address of entry was not added correctly to ${SUBNETADDR}"
+ echo "${FIELD} != ${CADDR}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+FIELD=`echo $ENTRY | cut -d " " -f4,4`
+if [ "${FIELD}" != "${SADDR}" ]
+then
+ echo "Server address of entry was not added correctly to ${SUBNETADDR}"
+ echo "${FIELD} != ${SADDR}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+FIELD=`echo $ENTRY | cut -d " " -f5,5`
+if [ "${FIELD}" != "${EXPIRE}" ]
+then
+ echo "Lease expiration of entry was not added correctly to ${SUBNETADDR}"
+ echo "${FIELD} != ${EXPIRE}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+FIELD=`echo $ENTRY | cut -d " " -f6,6`
+if [ "${FIELD}" != "${MACRO}" ]
+then
+ echo "Macro of entry was not added correctly to ${SUBNETADDR}"
+ echo "${FIELD} != ${MACRO}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+FIELD=`echo $ENTRY | cut -d " " -f7,7`
+if [ ! -z "${FIELD}" ]
+then
+ echo "Comment field of entry was not added correctly to ${SUBNETADDR}"
+ echo "Should have no comment"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+echo "${TESTNAME} - Test passed."
+exit 0
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/createdhcp.san b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/createdhcp.san
new file mode 100644
index 0000000000..09b033ec83
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/createdhcp.san
@@ -0,0 +1,75 @@
+#!/usr/bin/ksh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+
+#
+# Source the utilities.
+#
+DIRNAME=`dirname $0`
+. ${DIRNAME}/utilities.san
+
+#
+# Process the input arguments.
+#
+VALIDOPTS=dr:p:
+process_args $@
+
+#
+# In case the tester wants to see script output, allow them
+# to run in debug mode.
+#
+TESTNAME=`basename $0`
+if [ ! -z "${DEBUG}" ]
+then
+ OUTFILE=/tmp/${TESTNAME}.$$
+ echo "Output from test: ${TESTNAME}" >${OUTFILE}
+ echo >>${OUTFILE}
+ echo "debug output can be found at ${OUTFILE}"
+else
+ OUTFILE=/dev/null
+fi
+
+#
+# Make sure to clean up before we configure.
+#
+/usr/sbin/dhtadm -r ${DHCPRSRC} -p ${DHCPPATH} -R >>${OUTFILE} 2>&1
+
+#
+# Create the table.
+#
+/usr/sbin/dhtadm -r ${DHCPRSRC} -p ${DHCPPATH} -C >>${OUTFILE} 2>&1
+RET=$?
+if [ "${RET}" != "0" ]
+then
+ echo "Error creating the dhcptab = ${RET}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+echo "${TESTNAME} - Test passed."
+exit 0
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/createmac.san b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/createmac.san
new file mode 100644
index 0000000000..d44d3385c2
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/createmac.san
@@ -0,0 +1,138 @@
+#!/usr/bin/ksh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+
+#
+# Source the utilities.
+#
+DIRNAME=`dirname $0`
+. ${DIRNAME}/utilities.san
+
+#
+# Process the input arguments.
+#
+VALIDOPTS=dr:p:
+process_args $@
+
+#
+# In case the tester wants to see script output, allow them
+# to run in debug mode.
+#
+TESTNAME=`basename $0`
+if [ ! -z "${DEBUG}" ]
+then
+ OUTFILE=/tmp/${TESTNAME}.$$
+ echo "Output from test: ${TESTNAME}" >${OUTFILE}
+ echo >>${OUTFILE}
+ echo "debug output can be found at ${OUTFILE}"
+else
+ OUTFILE=/dev/null
+fi
+
+#
+# Verify that the macro is not already defined.
+#
+DATAFILE=/tmp/${TESTNAME}.data.$$
+/usr/sbin/dhtadm -r ${DHCPRSRC} -p ${DHCPPATH} -P >${DATAFILE} 2>>${OUTFILE}
+RET=$?
+if [ "${RET}" != "0" ]
+then
+ rm ${DATAFILE}
+ echo "Error displaying dhcptab = ${RET}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+MACRO=`grep "^TestMac" ${DATAFILE}`
+if [ ! -z "${MACRO}" ]
+then
+ rm ${DATAFILE}
+ echo "Macro already exists in dhcptab"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+rm ${DATAFILE}
+
+#
+# Create the macro.
+#
+/usr/sbin/dhtadm -r ${DHCPRSRC} -p ${DHCPPATH} -A -m TestMac -d ':Rootpath=/usr:' >>${OUTFILE} 2>&1
+RET=$?
+if [ "${RET}" != "0" ]
+then
+ echo "Error creating macro = ${RET}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+#
+# Verify that the macro is defined in the dhcptab.
+#
+/usr/sbin/dhtadm -r ${DHCPRSRC} -p ${DHCPPATH} -P >${DATAFILE} 2>>${OUTFILE}
+RET=$?
+if [ "${RET}" != "0" ]
+then
+ rm ${DATAFILE}
+ echo "Error displaying dhcptab = ${RET}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+MACRO=`grep "^TestMac" ${DATAFILE}`
+if [ -z "${MACRO}" ]
+then
+ rm ${DATAFILE}
+ echo "Macro not added to dhcptab."
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+rm ${DATAFILE}
+
+#
+# Grab the macro definition from the dhcptab
+#
+MACRO_DEFINITION=$(get_value ${MACRO})
+
+#
+# Verify that the defintion was defined correctly
+#
+SRCH=":Rootpath=\/usr:"
+macro_find_and_replace
+
+#
+# Verify that there was nothing else in the definition
+#
+if [ "${MACRO_DEFINITION}" != ":" ]
+then
+ echo "Macro definition has invalid extra symbols: ${MACRO_DEFINITION}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+echo "${TESTNAME} - Test passed."
+exit 0
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/createnet.san b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/createnet.san
new file mode 100644
index 0000000000..f8edb794b9
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/createnet.san
@@ -0,0 +1,114 @@
+#!/usr/bin/ksh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+
+#
+# Source the utilities.
+#
+DIRNAME=`dirname $0`
+. ${DIRNAME}/utilities.san
+
+#
+# Process the input arguments.
+#
+VALIDOPTS=dr:p:
+process_args $@
+
+#
+# In case the tester wants to see script output, allow them
+# to run in debug mode.
+#
+TESTNAME=`basename $0`
+if [ ! -z "${DEBUG}" ]
+then
+ OUTFILE=/tmp/${TESTNAME}.$$
+ echo "Output from test: ${TESTNAME}" >${OUTFILE}
+ echo >>${OUTFILE}
+ echo "debug output can be found at ${OUTFILE}"
+else
+ OUTFILE=/dev/null
+fi
+
+#
+# Set variables.
+#
+SRVNAME=`uname -n`
+SRVADDR=`get_server_ip`
+
+#
+# Determine subnet, netmask, and broadcast address.
+#
+get_default_class ${SRVADDR} | read DEFNET DEFMASK
+SUBNET=`get_netmask ${SRVADDR}`
+if [ -z "${SUBNET}" ]
+then
+ if [ "${DEFNET}" != "${SRVADDR}" ]
+ then
+ # likely subnetted/supernetted.
+ print - "\n\n###\tWarning\t###\n"
+ print - "Network ${SRVADDR} is netmasked, but no entry was found in the 'netmasks'\ntable; please update the 'netmasks' table in the appropriate\nnameservice before continuing (see /etc/nsswitch.conf).\n" >&2
+ return 1
+ else
+ # use the default.
+ SUBNET="${DEFMASK}"
+ fi
+fi
+
+SUBNETADDR=`get_subnet_addr ${SRVADDR} ${SUBNET}`
+
+#
+# Make sure to clean up before we configure.
+#
+/usr/sbin/pntadm -r ${DHCPRSRC} -p ${DHCPPATH} -R ${SUBNETADDR} >>${OUTFILE} 2>&1
+
+#
+# Create the table.
+#
+/usr/sbin/pntadm -r ${DHCPRSRC} -p ${DHCPPATH} -C ${SUBNETADDR} >>${OUTFILE} 2>&1
+RET=$?
+if [ "${RET}" != "0" ]
+then
+ echo "Error creating table for ${SUBNETADDR} = ${RET}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+#
+# Verify that the network table was created.
+#
+NETTABS=`/usr/sbin/pntadm -r ${DHCPRSRC} -p ${DHCPPATH} -L 2>>${OUTFILE}`
+NETTAB=`echo ${NETTABS} | fgrep ${SUBNETADDR}`
+if [ -z ${NETTAB} ]
+then
+ echo "Network table for ${SUBNETADDR} not created."
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+echo "${TESTNAME} - Test passed."
+exit 0
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/createopt.san b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/createopt.san
new file mode 100644
index 0000000000..ad420c007d
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/createopt.san
@@ -0,0 +1,128 @@
+#!/usr/bin/ksh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+
+#
+# Source the utilities.
+#
+DIRNAME=`dirname $0`
+. ${DIRNAME}/utilities.san
+
+#
+# Process the input arguments.
+#
+VALIDOPTS=dr:p:
+process_args $@
+
+#
+# In case the tester wants to see script output, allow them
+# to run in debug mode.
+#
+TESTNAME=`basename $0`
+if [ ! -z "${DEBUG}" ]
+then
+ OUTFILE=/tmp/${TESTNAME}.$$
+ echo "Output from test: ${TESTNAME}" >${OUTFILE}
+ echo >>${OUTFILE}
+ echo "debug output can be found at ${OUTFILE}"
+else
+ OUTFILE=/dev/null
+fi
+
+#
+# Verify that the option is not already defined.
+#
+DATAFILE=/tmp/${TESTNAME}.data.$$
+/usr/sbin/dhtadm -r ${DHCPRSRC} -p ${DHCPPATH} -P >${DATAFILE} 2>>${OUTFILE}
+RET=$?
+if [ "${RET}" != "0" ]
+then
+ rm ${DATAFILE}
+ echo "Error displaying dhcptab = ${RET}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+OPTION=`grep "^TestOpt" ${DATAFILE}`
+if [ ! -z "${OPTION}" ]
+then
+ rm ${DATAFILE}
+ echo "Option already exists in dhcptab"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+rm ${DATAFILE}
+
+#
+# Create the option.
+#
+/usr/sbin/dhtadm -r ${DHCPRSRC} -p ${DHCPPATH} -A -s TestOpt -d 'Vendor=the_test_class,11,ASCII,1,0' >>${OUTFILE} 2>&1
+RET=$?
+if [ "${RET}" != "0" ]
+then
+ echo "Error creating option = ${RET}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+#
+# Verify that the option is defined in the dhcptab.
+#
+/usr/sbin/dhtadm -r ${DHCPRSRC} -p ${DHCPPATH} -P >${DATAFILE} 2>>${OUTFILE}
+RET=$?
+if [ "${RET}" != "0" ]
+then
+ rm ${DATAFILE}
+ echo "Error displaying dhcptab = ${RET}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+OPTION=`grep "^TestOpt" ${DATAFILE}`
+if [ -z "${OPTION}" ]
+then
+ rm ${DATAFILE}
+ echo "Option not added to dhcptab."
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+rm ${DATAFILE}
+
+#
+# Verify that the defintion was defined correctly
+#
+OPTION_DEFINITION=$(get_value ${OPTION})
+if [ "${OPTION_DEFINITION}" != "Vendor=the_test_class,11,ASCII,1,0" ]
+then
+ echo "Option definition is not valid: *${OPTION_DEFINITION}*"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+echo "${TESTNAME} - Test passed."
+exit 0
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/cvtdhcp.san b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/cvtdhcp.san
new file mode 100644
index 0000000000..e6825f20a4
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/cvtdhcp.san
@@ -0,0 +1,143 @@
+#!/usr/bin/ksh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+
+#
+# Source the utilities.
+#
+DIRNAME=`dirname $0`
+. ${DIRNAME}/utilities.san
+
+#
+# Process the input arguments.
+#
+VALIDOPTS=dr:p:R:P:
+process_args $@
+
+#
+# In case the tester wants to see script output, allow them
+# to run in debug mode.
+#
+TESTNAME=`basename $0`
+if [ ! -z "${DEBUG}" ]
+then
+ OUTFILE=/tmp/${TESTNAME}.$$
+ echo "Output from test: ${TESTNAME}" >${OUTFILE}
+ echo >>${OUTFILE}
+ echo "debug output can be found at ${OUTFILE}"
+else
+ OUTFILE=/dev/null
+fi
+
+#
+# Set variables.
+#
+
+SRVNAME=`uname -n`
+SRVADDR=`get_server_ip`
+DHCP_DEFAULTS=/etc/inet/dhcpsvc.conf
+
+#
+# Convert.
+#
+/usr/sbin/dhcpconfig -C -f -r ${DHCPRSRC_NEW} -p ${DHCPPATH_NEW} >>${OUTFILE} 2>&1
+RET=$?
+if [ "${RET}" != "0" ]
+then
+ echo "Error converting datastore = ${RET}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+#
+# Verify that the datastore resource was defined in the defaults file
+#
+DEFLINE=`grep "RESOURCE=${DHCPRSRC_NEW}" ${DHCP_DEFAULTS}`
+if [ -z "${DEFLINE}" ]
+then
+ echo "New RESOURCE not set correctly in ${DHCP_DEFAULTS}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+#
+# Verify that the datastore path was defined in the defaults file
+#
+DEFLINE=`grep "PATH=${DHCPPATH_NEW}" ${DHCP_DEFAULTS}`
+if [ -z "${DEFLINE}" ]
+then
+ echo "New PATH not set correctly in ${DHCP_DEFAULTS}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+#
+# Verify that the dhcptab was created.
+#
+DATAFILE=/tmp/${TESTNAME}.data.$$
+/usr/sbin/dhtadm -r ${DHCPRSRC_NEW} -p ${DHCPPATH_NEW} -P >${DATAFILE} 2>>${OUTFILE}
+RET=$?
+if [ "${RET}" != "0" ]
+then
+ rm ${DATAFILE}
+ echo "Error displaying dhcptab = ${RET}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+rm ${DATAFILE}
+
+#
+# Verify that the network table was created.
+#
+SUBNET=`get_netmask ${SRVADDR}`
+SUBNETADDR=`get_subnet_addr ${SRVADDR} ${SUBNET}`
+/usr/sbin/pntadm -r ${DHCPRSRC_NEW} -p ${DHCPPATH_NEW} -L >${DATAFILE} 2>>${OUTFILE}
+RET=$?
+if [ "${RET}" != "0" ]
+then
+ rm ${DATAFILE}
+ echo "Error listing network tables = ${RET}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+ENTRY=`grep "^${SUBNETADDR}$" ${DATAFILE}`
+if [ -z "${ENTRY}" ]
+then
+ rm ${DATAFILE}
+ echo "Did not find network table, ${SUBNETADDR}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+rm ${DATAFILE}
+
+echo "${TESTNAME} - Test passed."
+exit 0
+
+
+
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/deleteaddr.san b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/deleteaddr.san
new file mode 100644
index 0000000000..c20954869d
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/deleteaddr.san
@@ -0,0 +1,128 @@
+#!/usr/bin/ksh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+
+#
+# Source the utilities.
+#
+DIRNAME=`dirname $0`
+. ${DIRNAME}/utilities.san
+
+#
+# Process the input arguments.
+#
+VALIDOPTS=dr:p:
+process_args $@
+
+#
+# In case the tester wants to see script output, allow them
+# to run in debug mode.
+#
+TESTNAME=`basename $0`
+if [ ! -z "${DEBUG}" ]
+then
+ OUTFILE=/tmp/${TESTNAME}.debug.$$
+ echo "Output from test: ${TESTNAME}" >${OUTFILE}
+ echo >>${OUTFILE}
+ echo "debug output can be found at ${OUTFILE}"
+else
+ OUTFILE=/dev/null
+fi
+
+#
+# Set variables.
+#
+SRVNAME=`uname -n`
+SRVADDR=`get_server_ip`
+
+#
+# Determine subnet, netmask, and broadcast address.
+#
+get_default_class ${SRVADDR} | read DEFNET DEFMASK
+SUBNET=`get_netmask ${SRVADDR}`
+if [ -z "${SUBNET}" ]
+then
+ if [ "${DEFNET}" != "${SRVADDR}" ]
+ then
+ # likely subnetted/supernetted.
+ print - "\n\n###\tWarning\t###\n"
+ print - "Network ${SRVADDR} is netmasked, but no entry was found in the 'netmasks'\ntable; please update the 'netmasks' table in the appropriate\nnameservice before continuing (see /etc/nsswitch.conf).\n" >&2
+ return 1
+ else
+ # use the default.
+ SUBNET="${DEFMASK}"
+ fi
+fi
+
+SUBNETADDR=`get_subnet_addr ${SRVADDR} ${SUBNET}`
+
+#
+# Verify that the network table exists.
+#
+NETTABS=`/usr/sbin/pntadm -r ${DHCPRSRC} -p ${DHCPPATH} -L 2>>${OUTFILE}`
+NETTAB=`echo ${NETTABS} | fgrep ${SUBNETADDR}`
+if [ -z ${NETTAB} ]
+then
+ echo "Network table for ${SUBNETADDR} does not exist."
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+#
+# Delete the client entry from the table.
+#
+CLIENTID="01AABBCCDDEEFF"
+CADDR=${SRVADDR}
+HOST="TESTCLIENT"
+
+/usr/sbin/pntadm -r ${DHCPRSRC} -p ${DHCPPATH} -D ${CADDR} ${SUBNETADDR} >>${OUTFILE} 2>&1
+RET=$?
+if [ "${RET}" != "0" ]
+then
+ echo "Error deleting entry ${CADDR} from table ${SUBNETADDR} = ${RET}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+#
+# Verify that entry was deleted.
+#
+DATAFILE=/tmp/${TESTNAME}.data.$$
+/usr/sbin/pntadm -r ${DHCPRSRC} -p ${DHCPPATH} -P ${SUBNETADDR} >${DATAFILE} 2>>${OUTFILE}
+ENTRY=`grep "^${CLIENTID}" ${DATAFILE}`
+if [ ! -z "${ENTRY}" ]
+then
+ rm ${DATAFILE}
+ echo "Client entry still found in ${SUBNETADDR}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+rm ${DATAFILE}
+
+echo "${TESTNAME} - Test passed."
+exit 0
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/deletemac.san b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/deletemac.san
new file mode 100644
index 0000000000..3a49c70d6f
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/deletemac.san
@@ -0,0 +1,118 @@
+#!/usr/bin/ksh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+
+#
+# Source the utilities.
+#
+DIRNAME=`dirname $0`
+. ${DIRNAME}/utilities.san
+
+#
+# Process the input arguments.
+#
+VALIDOPTS=dr:p:
+process_args $@
+
+#
+# In case the tester wants to see script output, allow them
+# to run in debug mode.
+#
+TESTNAME=`basename $0`
+if [ ! -z "${DEBUG}" ]
+then
+ OUTFILE=/tmp/${TESTNAME}.$$
+ echo "Output from test: ${TESTNAME}" >${OUTFILE}
+ echo >>${OUTFILE}
+ echo "debug output can be found at ${OUTFILE}"
+else
+ OUTFILE=/dev/null
+fi
+
+#
+# Verify that macro is already defined in the dhcptab
+#
+DATAFILE=/tmp/${TESTNAME}.data.$$
+/usr/sbin/dhtadm -r ${DHCPRSRC} -p ${DHCPPATH} -P >${DATAFILE} 2>>${OUTFILE}
+RET=$?
+if [ "${RET}" != "0" ]
+then
+ rm ${DATAFILE}
+ echo "Error displaying dhcptab = ${RET}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+MACRO=`grep "^TestMac" ${DATAFILE}`
+MACRO_DEFINITION=$(get_value ${MACRO})
+if [ "${MACRO_DEFINITION}" != ":TestOpt=129.148.11.240:" ]
+then
+ rm ${DATAFILE}
+ echo "Macro definition should have been defined as: :TestOpt=129.148.11.240:"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+rm ${DATAFILE}
+
+#
+# Delete the macro
+#
+/usr/sbin/dhtadm -r ${DHCPRSRC} -p ${DHCPPATH} -D -m TestMac >>${OUTFILE} 2>&1
+RET=$?
+if [ "${RET}" != "0" ]
+then
+ echo "Error deleting macro = ${RET}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+#
+# Verify that the macro has been removed from the dhcptab.
+#
+/usr/sbin/dhtadm -r ${DHCPRSRC} -p ${DHCPPATH} -P >${DATAFILE} 2>>${OUTFILE}
+RET=$?
+if [ "${RET}" != "0" ]
+then
+ rm ${DATAFILE}
+ echo "Error displaying dhcptab = ${RET}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+MACRO=`grep "^TestMac" ${DATAFILE}`
+if [ ! -z "${MACRO}" ]
+then
+ rm ${DATAFILE}
+ echo "Macro was not removed from the dhcptab"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+rm ${DATAFILE}
+
+echo "${TESTNAME} - Test passed."
+exit 0
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/deleteopt.san b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/deleteopt.san
new file mode 100644
index 0000000000..c9a71cb155
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/deleteopt.san
@@ -0,0 +1,118 @@
+#!/usr/bin/ksh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+
+#
+# Source the utilities.
+#
+DIRNAME=`dirname $0`
+. ${DIRNAME}/utilities.san
+
+#
+# Process the input arguments.
+#
+VALIDOPTS=dr:p:
+process_args $@
+
+#
+# In case the tester wants to see script output, allow them
+# to run in debug mode.
+#
+TESTNAME=`basename $0`
+if [ ! -z "${DEBUG}" ]
+then
+ OUTFILE=/tmp/${TESTNAME}.$$
+ echo "Output from test: ${TESTNAME}" >${OUTFILE}
+ echo >>${OUTFILE}
+ echo "debug output can be found at ${OUTFILE}"
+else
+ OUTFILE=/dev/null
+fi
+
+#
+# Verify that option is already defined in the dhcptab
+#
+DATAFILE=/tmp/${TESTNAME}.data.$$
+/usr/sbin/dhtadm -r ${DHCPRSRC} -p ${DHCPPATH} -P >${DATAFILE} 2>>${OUTFILE}
+RET=$?
+if [ "${RET}" != "0" ]
+then
+ rm ${DATAFILE}
+ echo "Error displaying dhcptab = ${RET}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+OPTION=`grep "^TestOpt" ${DATAFILE}`
+OPTION_DEFINITION=$(get_value ${OPTION})
+if [ "${OPTION_DEFINITION}" != "Site,130,IP,1,0" ]
+then
+ rm ${DATAFILE}
+ echo "Option definition should have been defined as: Site,130,IP,1,0"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+rm ${DATAFILE}
+
+#
+# Delete the option.
+#
+/usr/sbin/dhtadm -r ${DHCPRSRC} -p ${DHCPPATH} -D -s TestOpt >>${OUTFILE} 2>&1
+RET=$?
+if [ "${RET}" != "0" ]
+then
+ echo "Error deleting option = ${RET}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+#
+# Verify that the option was removed from the dhcptab.
+#
+/usr/sbin/dhtadm -r ${DHCPRSRC} -p ${DHCPPATH} -P >${DATAFILE} 2>>${OUTFILE}
+RET=$?
+if [ "${RET}" != "0" ]
+then
+ rm ${DATAFILE}
+ echo "Error displaying dhcptab = ${RET}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+OPTION=`grep "^TestOpt" ${DATAFILE}`
+if [ ! -z "${OPTION}" ]
+then
+ rm ${DATAFILE}
+ echo "Option was not removed from dhcptab"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+rm ${DATAFILE}
+
+echo "${TESTNAME} - Test passed."
+exit 0
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/dhcpconfig.san b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/dhcpconfig.san
new file mode 100644
index 0000000000..c837a42d42
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/dhcpconfig.san
@@ -0,0 +1,89 @@
+#!/usr/bin/ksh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+
+#
+# Source the utilities.
+#
+DIRNAME=`dirname $0`
+. ${DIRNAME}/utilities.san
+
+#
+# Process the input arguments.
+#
+VALIDOPTS=dr:p:R:P:
+process_args $@
+
+ksh ${DIRNAME}/cfgboot.san ${DEBUG}
+if [ $? != 0 ]
+then
+ echo "`basename $0` - aborted!"
+ exit 1
+fi
+
+ksh ${DIRNAME}/cfgdhcp.san ${DEBUG} -r ${DHCPRSRC} -p ${DHCPPATH}
+if [ $? != 0 ]
+then
+ echo "`basename $0` - aborted!"
+ exit 1
+fi
+
+ksh ${DIRNAME}/cfgnet.san ${DEBUG} -r ${DHCPRSRC} -p ${DHCPPATH}
+if [ $? != 0 ]
+then
+ echo "`basename $0` - aborted!"
+ exit 1
+fi
+
+ksh ${DIRNAME}/export.san ${DEBUG} -r ${DHCPRSRC} -p ${DHCPPATH}
+if [ $? != 0 ]
+then
+ echo "`basename $0` - aborted!"
+ exit 1
+fi
+
+ksh ${DIRNAME}/import.san ${DEBUG} -r ${DHCPRSRC} -p ${DHCPPATH}
+if [ $? != 0 ]
+then
+ echo "`basename $0` - aborted!"
+ exit 1
+fi
+
+ksh ${DIRNAME}/cvtdhcp.san ${DEBUG} -r ${DHCPRSRC} -p ${DHCPPATH} -R ${DHCPRSRC_NEW} -P ${DHCPPATH_NEW}
+if [ $? != 0 ]
+then
+ echo "`basename $0` - aborted!"
+ exit 1
+fi
+
+ksh ${DIRNAME}/ucfgdhcp.san ${DEBUG} -R ${DHCPRSRC_NEW} -P ${DHCPPATH_NEW}
+if [ $? != 0 ]
+then
+ echo "`basename $0` - aborted!"
+ exit 1
+fi
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/dhtadm.san b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/dhtadm.san
new file mode 100644
index 0000000000..7670712f24
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/dhtadm.san
@@ -0,0 +1,105 @@
+#!/usr/bin/ksh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+
+#
+# Source the utilities.
+#
+DIRNAME=`dirname $0`
+. ${DIRNAME}/utilities.san
+
+#
+# Process the input arguments.
+#
+VALIDOPTS=dr:p:
+process_args $@
+
+ksh ${DIRNAME}/createdhcp.san $@
+if [ $? != 0 ]
+then
+ echo "`basename $0` - aborted!"
+ exit 1
+fi
+
+ksh ${DIRNAME}/createmac.san $@
+if [ $? != 0 ]
+then
+ echo "`basename $0` - aborted!"
+ exit 1
+fi
+
+ksh ${DIRNAME}/createopt.san $@
+if [ $? != 0 ]
+then
+ echo "`basename $0` - aborted!"
+ exit 1
+fi
+
+ksh ${DIRNAME}/modifyopt.san $@
+if [ $? != 0 ]
+then
+ echo "`basename $0` - aborted!"
+ exit 1
+fi
+
+ksh ${DIRNAME}/modifymac.san $@
+if [ $? != 0 ]
+then
+ echo "`basename $0` - aborted!"
+ exit 1
+fi
+
+ksh ${DIRNAME}/displaydhcp.san $@
+if [ $? != 0 ]
+then
+ echo "`basename $0` - aborted!"
+ exit 1
+fi
+
+ksh ${DIRNAME}/deleteopt.san $@
+if [ $? != 0 ]
+then
+ echo "`basename $0` - aborted!"
+ exit 1
+fi
+
+ksh ${DIRNAME}/deletemac.san $@
+if [ $? != 0 ]
+then
+ echo "`basename $0` - aborted!"
+ exit 1
+fi
+
+ksh ${DIRNAME}/removedhcp.san $@
+if [ $? != 0 ]
+then
+ echo "`basename $0` - aborted!"
+ exit 1
+fi
+
+exit 0
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/displaydhcp.san b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/displaydhcp.san
new file mode 100644
index 0000000000..fb0accd77b
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/displaydhcp.san
@@ -0,0 +1,124 @@
+#!/usr/bin/ksh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+
+#
+# Source the utilities.
+#
+DIRNAME=`dirname $0`
+. ${DIRNAME}/utilities.san
+
+#
+# Process the input arguments.
+#
+VALIDOPTS=dr:p:
+process_args $@
+
+#
+# Set variables.
+#
+TESTNAME=`basename $0`
+if [ ! -z "${DEBUG}" ]
+then
+ OUTFILE=/tmp/${TESTNAME}.$$
+ echo "Output from test: ${TESTNAME}" >${OUTFILE}
+ echo >>${OUTFILE}
+ echo "debug output can be found at ${OUTFILE}"
+else
+ OUTFILE=/dev/null
+fi
+
+#
+# Display the table.
+#
+DATAFILE=/tmp/${TESTNAME}.data.$$
+/usr/sbin/dhtadm -r ${DHCPRSRC} -p ${DHCPPATH} -P >${DATAFILE} 2>>${OUTFILE}
+RET=$?
+if [ "${RET}" != "0" ]
+then
+ rm ${DATAFILE}
+ echo "Error displaying dhcptab = ${RET}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+#
+# Verify that the option is in the displayed output.
+#
+OPTION=`grep "^TestOpt" ${DATAFILE}`
+if [ -z "${OPTION}" ]
+then
+ rm ${DATAFILE}
+ echo "Option, TestOpt, not found in dhcptab"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+#
+# Verify that the defintion was defined correctly
+#
+OPTION_DEFINITION=$(get_value ${OPTION})
+if [ "${OPTION_DEFINITION}" != "Site,130,IP,1,0" ]
+then
+ echo "Option definition is not valid: ${OPTION_DEFINITION}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+#
+# Verify that the macro is in the displayed output.
+#
+MACRO=`grep "^TestMac" ${DATAFILE}`
+if [ -z "${MACRO}" ]
+then
+ rm ${DATAFILE}
+ echo "Macro, TestMac, not found in dhcptab"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+rm ${DATAFILE}
+
+#
+# Verify that the defintion was defined correctly
+#
+MACRO_DEFINITION=$(get_value ${MACRO})
+SRCH=":TestOpt=129.148.11.240:"
+macro_find_and_replace
+
+#
+# Verify that there was nothing else in the definition
+#
+if [ "${MACRO_DEFINITION}" != ":" ]
+then
+ echo "Macro definition has invalid extra symbols: ${MACRO_DEFINITION}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+echo "${TESTNAME} - Test passed."
+exit 0
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/displaynet.san b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/displaynet.san
new file mode 100644
index 0000000000..f38dc58adb
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/displaynet.san
@@ -0,0 +1,196 @@
+#!/usr/bin/ksh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+
+#
+# Source the utilities.
+#
+DIRNAME=`dirname $0`
+. ${DIRNAME}/utilities.san
+
+#
+# Process the input arguments.
+#
+VALIDOPTS=dr:p:
+process_args $@
+
+#
+# In case the tester wants to see script output, allow them
+# to run in debug mode.
+#
+TESTNAME=`basename $0`
+if [ ! -z "${DEBUG}" ]
+then
+ OUTFILE=/tmp/${TESTNAME}.$$
+ echo "Output from test: ${TESTNAME}" >${OUTFILE}
+ echo >>${OUTFILE}
+ echo "debug output can be found at ${OUTFILE}"
+else
+ OUTFILE=/dev/null
+fi
+
+#
+# Set variables.
+#
+TESTNAME=`basename $0`
+SRVNAME=`uname -n`
+SRVADDR=`get_server_ip`
+
+#
+# Determine subnet, netmask, and broadcast address.
+#
+get_default_class ${SRVADDR} | read DEFNET DEFMASK
+SUBNET=`get_netmask ${SRVADDR}`
+if [ -z "${SUBNET}" ]
+then
+ if [ "${DEFNET}" != "${SRVADDR}" ]
+ then
+ # likely subnetted/supernetted.
+ print - "\n\n###\tWarning\t###\n"
+ print - "Network ${SRVADDR} is netmasked, but no entry was found in the 'netmasks'\ntable; please update the 'netmasks' table in the appropriate\nnameservice before continuing (see /etc/nsswitch.conf).\n" >&2
+ return 1
+ else
+ # use the default.
+ SUBNET="${DEFMASK}"
+ fi
+fi
+
+SUBNETADDR=`get_subnet_addr ${SRVADDR} ${SUBNET}`
+
+#
+# Verify that the network table exists.
+#
+NETTABS=`/usr/sbin/pntadm -r ${DHCPRSRC} -p ${DHCPPATH} -L 2>>${OUTFILE}`
+NETTAB=`echo ${NETTABS} | fgrep ${SUBNETADDR}`
+if [ -z ${NETTAB} ]
+then
+ echo "Network table for ${SUBNETADDR} does not exist."
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+#
+# Display the table.
+#
+CLIENTID="01AABBCCDDEEFF"
+FLAGS="01"
+CADDR=${SRVADDR}
+SADDR=${SRVADDR}
+EXPIRE="03/15/2001"
+MACRO="UNKNOWN"
+COMMENT=Modified
+DATAFILE=/tmp/${TESTNAME}.data.$$
+
+/usr/sbin/pntadm -r ${DHCPRSRC} -p ${DHCPPATH} -P ${SUBNETADDR} >${DATAFILE} 2>>${OUTFILE}
+RET=$?
+if [ "${RET}" != "0" ]
+then
+ rm ${DATAFILE}
+ echo "Error listing table for ${SUBNETADDR} = ${RET}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+ENTRY=`grep "^${CLIENTID}" ${DATAFILE}`
+if [ -z "${ENTRY}" ]
+then
+ rm ${DATAFILE}
+ echo "Client entry not found in ${SUBNETADDR}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+rm ${DATAFILE}
+
+#
+# Verify that the entry fields are correct.
+#
+FIELD=`echo $ENTRY | cut -d " " -f1,1`
+if [ "${FIELD}" != "${CLIENTID}" ]
+then
+ echo "ClientID of entry is not correct in ${SUBNETADDR}"
+ echo "${FIELD} != ${CLIENTID}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+FIELD=`echo $ENTRY | cut -d " " -f2,2`
+if [ "${FIELD}" != "${FLAGS}" ]
+then
+ echo "Flags of entry is not correct in ${SUBNETADDR}"
+ echo "${FIELD} != ${FLAGS}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+FIELD=`echo $ENTRY | cut -d " " -f3,3`
+if [ "${FIELD}" != "${CADDR}" ]
+then
+ echo "Client address of entry is not correct in ${SUBNETADDR}"
+ echo "${FIELD} != ${CADDR}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+FIELD=`echo $ENTRY | cut -d " " -f4,4`
+if [ "${FIELD}" != "${SADDR}" ]
+then
+ echo "Server address of entry is not correct in ${SUBNETADDR}"
+ echo "${FIELD} != ${SADDR}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+FIELD=`echo $ENTRY | cut -d " " -f5,5`
+if [ "${FIELD}" != "${EXPIRE}" ]
+then
+ echo "Lease expiration of entry is not correct in ${SUBNETADDR}"
+ echo "${FIELD} != ${EXPIRE}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+FIELD=`echo $ENTRY | cut -d " " -f6,6`
+if [ "${FIELD}" != "${MACRO}" ]
+then
+ echo "Macro of entry is not correct in ${SUBNETADDR}"
+ echo "${FIELD} != ${MACRO}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+FIELD=`echo $ENTRY | cut -d " " -f7,7`
+if [ "${FIELD}" != "${COMMENT}" ]
+then
+ echo "Comment field of entry is not correct in ${SUBNETADDR}"
+ echo "${FIELD} != ${COMMENT}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+echo "${TESTNAME} - Test passed."
+exit 0
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/export.san b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/export.san
new file mode 100644
index 0000000000..d702f81407
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/export.san
@@ -0,0 +1,136 @@
+#!/usr/bin/ksh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+
+#
+# Source the utilities.
+#
+DIRNAME=`dirname $0`
+. ${DIRNAME}/utilities.san
+
+#
+# Process the input arguments.
+#
+VALIDOPTS=dr:p:
+process_args $@
+
+#
+# In case the tester wants to see script output, allow them
+# to run in debug mode.
+#
+TESTNAME=`basename $0`
+if [ ! -z "${DEBUG}" ]
+then
+ OUTFILE=/tmp/${TESTNAME}.$$
+ echo "Output from test: ${TESTNAME}" >${OUTFILE}
+ echo >>${OUTFILE}
+ echo "debug output can be found at ${OUTFILE}"
+else
+ OUTFILE=/dev/null
+fi
+
+#
+# Set variables.
+#
+
+SRVNAME=`uname -n`
+SRVADDR=`get_server_ip`
+DHCPCONFIG=/usr/sbin/dhcpconfig
+EXPORTFILE=/tmp/move.zip
+
+#
+# Export.
+#
+${DHCPCONFIG} -X ${EXPORTFILE} -m ALL -o ALL -a ALL -f -x >>${OUTFILE} 2>&1
+RET=$?
+if [ "${RET}" != "0" ]
+then
+ echo "Error exporting DHCP data = ${RET}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+#
+# Verify that the export file exists.
+#
+if [ ! -f ${EXPORTFILE} ]
+then
+ echo "${EXPORTFILE} does not exist"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+#
+# Verify that all symbols and macros was removed from the dhcptab.
+#
+DATAFILE=/tmp/${TESTNAME}.data.$$
+/usr/sbin/dhtadm -r ${DHCPRSRC} -p ${DHCPPATH} -P >${DATAFILE} 2>>${OUTFILE}
+RET=$?
+if [ "${RET}" != "0" ]
+then
+ rm ${DATAFILE}
+ echo "Error displaying dhcptab = ${RET}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+ENTRIES=`wc -l ${DATAFILE} | sed 's/^ *//' | cut -d " " -f1,1`
+if [ "${ENTRIES}" != "2" ]
+then
+ rm ${DATAFILE}
+ echo "The dhcptab is not empty as it should be."
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+rm ${DATAFILE}
+
+#
+# Verify that the network tables were removed.
+#
+/usr/sbin/pntadm -r ${DHCPRSRC} -p ${DHCPPATH} -L >${DATAFILE} 2>>${OUTFILE}
+RET=$?
+if [ "${RET}" != "0" ]
+then
+ rm ${DATAFILE}
+ echo "Error listing networks = ${RET}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+ENTRIES=`wc -l ${DATAFILE} | sed 's/^ *//' | cut -d " " -f1,1`
+if [ "${ENTRIES}" != "0" ]
+then
+ rm ${DATAFILE}
+ echo "Some network tables still exist and they should not."
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+rm ${DATAFILE}
+
+echo "${TESTNAME} - Test passed."
+exit 0
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/import.san b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/import.san
new file mode 100644
index 0000000000..134645190a
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/import.san
@@ -0,0 +1,129 @@
+#!/usr/bin/ksh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+
+#
+# Source the utilities.
+#
+DIRNAME=`dirname $0`
+. ${DIRNAME}/utilities.san
+
+#
+# Process the input arguments.
+#
+VALIDOPTS=dr:p:
+process_args $@
+
+#
+# In case the tester wants to see script output, allow them
+# to run in debug mode.
+#
+TESTNAME=`basename $0`
+if [ ! -z "${DEBUG}" ]
+then
+ OUTFILE=/tmp/${TESTNAME}.$$
+ echo "Output from test: ${TESTNAME}" >${OUTFILE}
+ echo >>${OUTFILE}
+ echo "debug output can be found at ${OUTFILE}"
+else
+ OUTFILE=/dev/null
+fi
+
+#
+# Set variables.
+#
+
+SRVNAME=`uname -n`
+SRVADDR=`get_server_ip`
+DHCPCONFIG=/usr/sbin/dhcpconfig
+IMPORTFILE=/tmp/move.zip
+
+#
+# Import.
+#
+${DHCPCONFIG} -I ${IMPORTFILE} >>${OUTFILE} 2>&1
+RET=$?
+if [ "${RET}" != "0" ]
+then
+ echo "Error importing DHCP data = ${RET}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+#
+# Verify that the Locale macro was added to the dhcptab.
+#
+DATAFILE=/tmp/${TESTNAME}.data.$$
+/usr/sbin/dhtadm -r ${DHCPRSRC} -p ${DHCPPATH} -P >${DATAFILE} 2>>${OUTFILE}
+RET=$?
+if [ "${RET}" != "0" ]
+then
+ rm ${DATAFILE}
+ echo "Error displaying dhcptab = ${RET}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+LOCALE=`grep "^Locale" ${DATAFILE}`
+if [ -z "${LOCALE}" ]
+then
+ rm ${DATAFILE}
+ echo "Locale macro was not added to the dhcptab"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+#
+# Verify that the server macro was added to the dhcptab.
+#
+SERVER_MACRO=`grep "^${SRVNAME}" ${DATAFILE}`
+if [ -z "${SERVER_MACRO}" ]
+then
+ rm ${DATAFILE}
+ echo "Server macro was not removed from the dhcptab"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+rm ${DATAFILE}
+
+#
+# Verify that the network table was created.
+#
+SUBNET=`get_netmask ${SRVADDR}`
+SUBNETADDR=`get_subnet_addr ${SRVADDR} ${SUBNET}`
+NETTABS=`/usr/sbin/pntadm -r ${DHCPRSRC} -p ${DHCPPATH} -L 2>>${OUTFILE}`
+NETTAB=`echo ${NETTABS} | fgrep ${SUBNETADDR}`
+if [ -z ${NETTAB} ]
+then
+ echo "${SUBNETADDR} was not imported."
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+echo "${TESTNAME} - Test passed."
+exit 0
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/listnet.san b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/listnet.san
new file mode 100644
index 0000000000..2f1cea7238
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/listnet.san
@@ -0,0 +1,110 @@
+#!/usr/bin/ksh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+
+#
+# Source the utilities.
+#
+DIRNAME=`dirname $0`
+. ${DIRNAME}/utilities.san
+
+#
+# Process the input arguments.
+#
+VALIDOPTS=dr:p:
+process_args $@
+
+#
+# In case the tester wants to see script output, allow them
+# to run in debug mode.
+#
+TESTNAME=`basename $0`
+if [ ! -z "${DEBUG}" ]
+then
+ OUTFILE=/tmp/${TESTNAME}.$$
+ echo "Output from test: ${TESTNAME}" >${OUTFILE}
+ echo >>${OUTFILE}
+ echo "debug output can be found at ${OUTFILE}"
+else
+ OUTFILE=/dev/null
+fi
+
+#
+# Set variables.
+#
+TESTNAME=`basename $0`
+SRVNAME=`uname -n`
+SRVADDR=`get_server_ip`
+
+#
+# Determine subnet, netmask, and broadcast address.
+#
+get_default_class ${SRVADDR} | read DEFNET DEFMASK
+SUBNET=`get_netmask ${SRVADDR}`
+if [ -z "${SUBNET}" ]
+then
+ if [ "${DEFNET}" != "${SRVADDR}" ]
+ then
+ # likely subnetted/supernetted.
+ print - "\n\n###\tWarning\t###\n"
+ print - "Network ${SRVADDR} is netmasked, but no entry was found in the 'netmasks'\ntable; please update the 'netmasks' table in the appropriate\nnameservice before continuing (see /etc/nsswitch.conf).\n" >&2
+ return 1
+ else
+ # use the default.
+ SUBNET="${DEFMASK}"
+ fi
+fi
+
+SUBNETADDR=`get_subnet_addr ${SRVADDR} ${SUBNET}`
+
+#
+# Display the table.
+#
+DATAFILE=/tmp/${TESTNAME}.data.$$
+/usr/sbin/pntadm -r ${DHCPRSRC} -p ${DHCPPATH} -L >${DATAFILE} 2>>${OUTFILE}
+RET=$?
+if [ "${RET}" != "0" ]
+then
+ rm ${DATAFILE}
+ echo "Error listing ${SUBNETADDR} table = ${RET}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+ENTRY=`grep "^${SUBNETADDR}$" ${DATAFILE}`
+if [ -z "${ENTRY}" ]
+then
+ rm ${DATAFILE}
+ echo "Did not find network table, ${SUBNETADDR}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+rm ${DATAFILE}
+
+echo "${TESTNAME} - Test passed."
+exit 0
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/modifyaddr.san b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/modifyaddr.san
new file mode 100644
index 0000000000..629dc19db9
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/modifyaddr.san
@@ -0,0 +1,199 @@
+#!/usr/bin/ksh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+
+#
+# Source the utilities.
+#
+DIRNAME=`dirname $0`
+. ${DIRNAME}/utilities.san
+
+#
+# Process the input arguments.
+#
+VALIDOPTS=dr:p:
+process_args $@
+
+#
+# In case the tester wants to see script output, allow them
+# to run in debug mode.
+#
+TESTNAME=`basename $0`
+if [ ! -z "${DEBUG}" ]
+then
+ OUTFILE=/tmp/${TESTNAME}.debug.$$
+ echo "Output from test: ${TESTNAME}" >${OUTFILE}
+ echo >>${OUTFILE}
+ echo "debug output can be found at ${OUTFILE}"
+else
+ OUTFILE=/dev/null
+fi
+
+#
+# Set variables.
+#
+SRVNAME=`uname -n`
+SRVADDR=`get_server_ip`
+
+#
+# Determine subnet, netmask, and broadcast address.
+#
+get_default_class ${SRVADDR} | read DEFNET DEFMASK
+SUBNET=`get_netmask ${SRVADDR}`
+if [ -z "${SUBNET}" ]
+then
+ if [ "${DEFNET}" != "${SRVADDR}" ]
+ then
+ # likely subnetted/supernetted.
+ print - "\n\n###\tWarning\t###\n"
+ print - "Network ${SRVADDR} is netmasked, but no entry was found in the 'netmasks'\ntable; please update the 'netmasks' table in the appropriate\nnameservice before continuing (see /etc/nsswitch.conf).\n" >&2
+ return 1
+ else
+ # use the default.
+ SUBNET="${DEFMASK}"
+ fi
+fi
+
+SUBNETADDR=`get_subnet_addr ${SRVADDR} ${SUBNET}`
+
+#
+# Verify that the network table exists.
+#
+NETTABS=`/usr/sbin/pntadm -r ${DHCPRSRC} -p ${DHCPPATH} -L 2>>${OUTFILE}`
+NETTAB=`echo ${NETTABS} | fgrep ${SUBNETADDR}`
+if [ -z ${NETTAB} ]
+then
+ echo "Network table for ${SUBNETADDR} does not exist."
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+#
+# Modify the client entry.
+#
+CLIENTID="01AABBCCDDEEFF"
+FLAGS="01"
+CADDR=${SRVADDR}
+SADDR=${SRVADDR}
+EXPIRE="03/15/2001"
+MACRO="UNKNOWN"
+COMMENT=Modified
+
+/usr/sbin/pntadm -r ${DHCPRSRC} -p ${DHCPPATH} -M ${SRVADDR} -e ${EXPIRE} -c ${COMMENT} -f ${FLAGS} ${SUBNETADDR} >>${OUTFILE} 2>&1
+RET=$?
+if [ "${RET}" != "0" ]
+then
+ echo "Error modifying entry ${SRVADDR} to table ${SUBNETADDR} = ${RET}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+#
+# Get the entry.
+#
+DATAFILE=/tmp/${TESTNAME}.data.$$
+/usr/sbin/pntadm -r ${DHCPRSRC} -p ${DHCPPATH} -P ${SUBNETADDR} >${DATAFILE} 2>>${OUTFILE}
+
+ENTRY=`grep "^${CLIENTID}" ${DATAFILE}`
+if [ -z "${ENTRY}" ]
+then
+ rm ${DATAFILE}
+ echo "Client entry not found in ${SUBNETADDR}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+rm ${DATAFILE}
+
+#
+# Verify that the entry fields are correct.
+#
+FIELD=`echo $ENTRY | cut -d " " -f1,1`
+if [ "${FIELD}" != "${CLIENTID}" ]
+then
+ echo "ClientID of entry is not correct in ${SUBNETADDR}"
+ echo "${FIELD} != ${CLIENTID}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+FIELD=`echo $ENTRY | cut -d " " -f2,2`
+if [ "${FIELD}" != "${FLAGS}" ]
+then
+ echo "Flags of entry is not correct in ${SUBNETADDR}"
+ echo "${FIELD} != ${FLAGS}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+FIELD=`echo $ENTRY | cut -d " " -f3,3`
+if [ "${FIELD}" != "${CADDR}" ]
+then
+ echo "Client address of entry is not correct in ${SUBNETADDR}"
+ echo "${FIELD} != ${CADDR}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+FIELD=`echo $ENTRY | cut -d " " -f4,4`
+if [ "${FIELD}" != "${SADDR}" ]
+then
+ echo "Server address of entry is not correct in ${SUBNETADDR}"
+ echo "${FIELD} != ${SADDR}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+FIELD=`echo $ENTRY | cut -d " " -f5,5`
+if [ "${FIELD}" != "${EXPIRE}" ]
+then
+ echo "Lease expiration of entry is not correct in ${SUBNETADDR}"
+ echo "${FIELD} != ${EXPIRE}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+FIELD=`echo $ENTRY | cut -d " " -f6,6`
+if [ "${FIELD}" != "${MACRO}" ]
+then
+ echo "Macro of entry is not correct in ${SUBNETADDR}"
+ echo "${FIELD} != ${MACRO}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+FIELD=`echo $ENTRY | cut -d " " -f7,7`
+if [ "${FIELD}" != "${COMMENT}" ]
+then
+ echo "Comment field of entry is not correct in ${SUBNETADDR}"
+ echo "${FIELD} != ${COMMENT}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+echo "${TESTNAME} - Test passed."
+exit 0
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/modifymac.san b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/modifymac.san
new file mode 100644
index 0000000000..e45225ea48
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/modifymac.san
@@ -0,0 +1,135 @@
+#!/usr/bin/ksh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+
+#
+# Source the utilities.
+#
+DIRNAME=`dirname $0`
+. ${DIRNAME}/utilities.san
+
+#
+# Process the input arguments.
+#
+VALIDOPTS=dr:p:
+process_args $@
+
+#
+# In case the tester wants to see script output, allow them
+# to run in debug mode.
+#
+TESTNAME=`basename $0`
+if [ ! -z "${DEBUG}" ]
+then
+ OUTFILE=/tmp/${TESTNAME}.$$
+ echo "Output from test: ${TESTNAME}" >${OUTFILE}
+ echo >>${OUTFILE}
+ echo "debug output can be found at ${OUTFILE}"
+else
+ OUTFILE=/dev/null
+fi
+
+#
+# Verify that macro is already defined in the dhcptab
+#
+DATAFILE=/tmp/${TESTNAME}.data.$$
+/usr/sbin/dhtadm -r ${DHCPRSRC} -p ${DHCPPATH} -P >${DATAFILE} 2>>${OUTFILE}
+RET=$?
+if [ "${RET}" != "0" ]
+then
+ rm ${DATAFILE}
+ echo "Error displaying dhcptab = ${RET}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+MACRO=`grep "^TestMac" ${DATAFILE}`
+MACRO_DEFINITION=$(get_value ${MACRO})
+if [ "${MACRO_DEFINITION}" != ":Rootpath=/usr:" ]
+then
+ rm ${DATAFILE}
+ echo "Macro definition should have been defined as: :Rootpath=/usr:"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+rm ${DATAFILE}
+
+#
+# Modify the macro
+#
+/usr/sbin/dhtadm -r ${DHCPRSRC} -p ${DHCPPATH} -M -m TestMac -d ':TestOpt=129.148.11.240:' >>${OUTFILE} 2>&1
+RET=$?
+if [ "${RET}" != "0" ]
+then
+ echo "Error modifying macro = ${RET}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+#
+# Verify that the macro is defined in the dhcptab.
+#
+/usr/sbin/dhtadm -r ${DHCPRSRC} -p ${DHCPPATH} -P >${DATAFILE} 2>>${OUTFILE}
+RET=$?
+if [ "${RET}" != "0" ]
+then
+ rm ${DATAFILE}
+ echo "Error displaying dhcptab = ${RET}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+MACRO=`grep "^TestMac" ${DATAFILE}`
+if [ -z "${MACRO}" ]
+then
+ rm ${DATAFILE}
+ echo "Macro not modified in dhcptab"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+rm ${DATAFILE}
+
+#
+# Verify that the defintion was defined correctly
+#
+MACRO_DEFINITION=$(get_value ${MACRO})
+SRCH=":TestOpt=129.148.11.240:"
+macro_find_and_replace
+
+#
+# Verify that there was nothing else in the definition
+#
+if [ "${MACRO_DEFINITION}" != ":" ]
+then
+ echo "Macro definition has invalid extra symbols: ${MACRO_DEFINITION}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+echo "${TESTNAME} - Test passed."
+exit 0
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/modifyopt.san b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/modifyopt.san
new file mode 100644
index 0000000000..30593a48d1
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/modifyopt.san
@@ -0,0 +1,129 @@
+#!/usr/bin/ksh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+
+#
+# Source the utilities.
+#
+DIRNAME=`dirname $0`
+. ${DIRNAME}/utilities.san
+
+#
+# Process the input arguments.
+#
+VALIDOPTS=dr:p:
+process_args $@
+
+#
+# In case the tester wants to see script output, allow them
+# to run in debug mode.
+#
+TESTNAME=`basename $0`
+if [ ! -z "${DEBUG}" ]
+then
+ OUTFILE=/tmp/${TESTNAME}.$$
+ echo "Output from test: ${TESTNAME}" >${OUTFILE}
+ echo >>${OUTFILE}
+ echo "debug output can be found at ${OUTFILE}"
+else
+ OUTFILE=/dev/null
+fi
+
+#
+# Verify that option is already defined in the dhcptab
+#
+DATAFILE=/tmp/${TESTNAME}.data.$$
+/usr/sbin/dhtadm -r ${DHCPRSRC} -p ${DHCPPATH} -P >${DATAFILE} 2>>${OUTFILE}
+RET=$?
+if [ "${RET}" != "0" ]
+then
+ rm ${DATAFILE}
+ echo "Error displaying dhcptab = ${RET}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+OPTION=`grep "^TestOpt" ${DATAFILE}`
+OPTION_DEFINITION=$(get_value ${OPTION})
+if [ "${OPTION_DEFINITION}" != "Vendor=the_test_class,11,ASCII,1,0" ]
+then
+ rm ${DATAFILE}
+ echo "Option definition should have been defined as: Vendor=the_test_class,11,ASCII,1,0"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+rm ${DATAFILE}
+
+#
+# Modify the option.
+#
+/usr/sbin/dhtadm -r ${DHCPRSRC} -p ${DHCPPATH} -M -s TestOpt -d 'Site,130,IP,1,0' >>${OUTFILE} 2>&1
+RET=$?
+if [ "${RET}" != "0" ]
+then
+ echo "Error modifying option = ${RET}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+#
+# Verify that the option is defined in the dhcptab.
+#
+/usr/sbin/dhtadm -r ${DHCPRSRC} -p ${DHCPPATH} -P >${DATAFILE} 2>>${OUTFILE}
+RET=$?
+if [ "${RET}" != "0" ]
+then
+ rm ${DATAFILE}
+ echo "Error displaying dhcptab = ${RET}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+OPTION=`grep "^TestOpt" ${DATAFILE}`
+if [ -z "${OPTION}" ]
+then
+ rm ${DATAFILE}
+ echo "Option not modified in dhcptab"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+rm ${DATAFILE}
+
+#
+# Verify that the defintion was defined correctly
+#
+OPTION_DEFINITION=$(get_value ${OPTION})
+if [ "${OPTION_DEFINITION}" != "Site,130,IP,1,0" ]
+then
+ echo "Option definition is not valid: ${OPTION_DEFINITION}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+echo "${TESTNAME} - Test passed."
+exit 0
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/pntadm.san b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/pntadm.san
new file mode 100644
index 0000000000..7000cc22ca
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/pntadm.san
@@ -0,0 +1,90 @@
+#!/usr/bin/ksh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+
+#
+# Source the utilities.
+#
+DIRNAME=`dirname $0`
+. ${DIRNAME}/utilities.san
+
+#
+# Process the input arguments.
+#
+VALIDOPTS=dr:p:
+process_args $@
+
+ksh ${DIRNAME}/createnet.san $@
+if [ $? != 0 ]
+then
+ echo "`basename $0` - aborted!"
+ exit 1
+fi
+
+ksh ${DIRNAME}/createaddr.san $@
+if [ $? != 0 ]
+then
+ echo "`basename $0` - aborted!"
+ exit 1
+fi
+
+ksh ${DIRNAME}/modifyaddr.san $@
+if [ $? != 0 ]
+then
+ echo "`basename $0` - aborted!"
+ exit 1
+fi
+
+ksh ${DIRNAME}/displaynet.san $@
+if [ $? != 0 ]
+then
+ echo "`basename $0` - aborted!"
+ exit 1
+fi
+
+ksh ${DIRNAME}/deleteaddr.san $@
+if [ $? != 0 ]
+then
+ echo "`basename $0` - aborted!"
+ exit 1
+fi
+
+ksh ${DIRNAME}/listnet.san $@
+if [ $? != 0 ]
+then
+ echo "`basename $0` - aborted!"
+ exit 1
+fi
+
+ksh ${DIRNAME}/removenet.san $@
+if [ $? != 0 ]
+then
+ echo "`basename $0` - aborted!"
+ exit 1
+fi
+
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/removedhcp.san b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/removedhcp.san
new file mode 100644
index 0000000000..50ea84c6cf
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/removedhcp.san
@@ -0,0 +1,70 @@
+#!/usr/bin/ksh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+
+#
+# Source the utilities.
+#
+DIRNAME=`dirname $0`
+. ${DIRNAME}/utilities.san
+
+#
+# Process the input arguments.
+#
+VALIDOPTS=dr:p:
+process_args $@
+
+#
+# In case the tester wants to see script output, allow them
+# to run in debug mode.
+#
+TESTNAME=`basename $0`
+if [ ! -z "${DEBUG}" ]
+then
+ OUTFILE=/tmp/${TESTNAME}.$$
+ echo "Output from test: ${TESTNAME}" >${OUTFILE}
+ echo >>${OUTFILE}
+ echo "debug output can be found at ${OUTFILE}"
+else
+ OUTFILE=/dev/null
+fi
+
+#
+# Remove the table.
+#
+/usr/sbin/dhtadm -r ${DHCPRSRC} -p ${DHCPPATH} -R >>${OUTFILE} 2>&1
+RET=$?
+if [ "${RET}" != "0" ]
+then
+ echo "Error removing the dhcptab = ${RET}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+echo "${TESTNAME} - Test passed."
+exit 0
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/removenet.san b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/removenet.san
new file mode 100644
index 0000000000..e539d70437
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/removenet.san
@@ -0,0 +1,121 @@
+#!/usr/bin/ksh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+
+#
+# Source the utilities.
+#
+DIRNAME=`dirname $0`
+. ${DIRNAME}/utilities.san
+
+#
+# Process the input arguments.
+#
+VALIDOPTS=dr:p:
+process_args $@
+
+#
+# In case the tester wants to see script output, allow them
+# to run in debug mode.
+#
+TESTNAME=`basename $0`
+if [ ! -z "${DEBUG}" ]
+then
+ OUTFILE=/tmp/${TESTNAME}.$$
+ echo "Output from test: ${TESTNAME}" >${OUTFILE}
+ echo >>${OUTFILE}
+ echo "debug output can be found at ${OUTFILE}"
+else
+ OUTFILE=/dev/null
+fi
+
+#
+# Set variables.
+#
+SRVNAME=`uname -n`
+SRVADDR=`get_server_ip`
+
+#
+# Determine subnet, netmask, and broadcast address.
+#
+get_default_class ${SRVADDR} | read DEFNET DEFMASK
+SUBNET=`get_netmask ${SRVADDR}`
+if [ -z "${SUBNET}" ]
+then
+ if [ "${DEFNET}" != "${SRVADDR}" ]
+ then
+ # likely subnetted/supernetted.
+ print - "\n\n###\tWarning\t###\n"
+ print - "Network ${SRVADDR} is netmasked, but no entry was found in the 'netmasks'\ntable; please update the 'netmasks' table in the appropriate\nnameservice before continuing (see /etc/nsswitch.conf).\n" >&2
+ return 1
+ else
+ # use the default.
+ SUBNET="${DEFMASK}"
+ fi
+fi
+
+SUBNETADDR=`get_subnet_addr ${SRVADDR} ${SUBNET}`
+
+#
+# Verify that the network table exists.
+#
+NETTABS=`/usr/sbin/pntadm -r ${DHCPRSRC} -p ${DHCPPATH} -L 2>>${OUTFILE}`
+NETTAB=`echo ${NETTABS} | fgrep ${SUBNETADDR}`
+if [ -z ${NETTAB} ]
+then
+ echo "Network table for ${SUBNETADDR} does not exist."
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+#
+# Remove the table.
+#
+/usr/sbin/pntadm -r ${DHCPRSRC} -p ${DHCPPATH} -R ${SUBNETADDR} >>${OUTFILE} 2>&1
+RET=$?
+if [ "${RET}" != "0" ]
+then
+ echo "Error removing ${SUBNETADDR} table = ${RET}"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+#
+# Verify that the network table was removed.
+#
+NETTABS=`/usr/sbin/pntadm -r ${DHCPRSRC} -p ${DHCPPATH} -L 2>>${OUTFILE}`
+NETTAB=`echo ${NETTABS} | fgrep ${SUBNETADDR}`
+if [ ! -z ${NETTAB} ]
+then
+ echo "${SUBNETADDR} not removed."
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+echo "${TESTNAME} - Test passed."
+exit 0
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/ucfgdhcp.san b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/ucfgdhcp.san
new file mode 100644
index 0000000000..1e9f4760b2
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/ucfgdhcp.san
@@ -0,0 +1,108 @@
+#!/usr/bin/ksh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+
+#
+# Source the utilities.
+#
+DIRNAME=`dirname $0`
+. ${DIRNAME}/utilities.san
+
+#
+# Process the input arguments.
+#
+VALIDOPTS=dR:P:
+process_args $@
+
+#
+# In case the tester wants to see script output, allow them
+# to run in debug mode.
+#
+TESTNAME=`basename $0`
+if [ ! -z "${DEBUG}" ]
+then
+ OUTFILE=/tmp/${TESTNAME}.$$
+ echo "Output from test: ${TESTNAME}" >${OUTFILE}
+ echo >>${OUTFILE}
+ echo "debug output can be found at ${OUTFILE}"
+else
+ OUTFILE=/dev/null
+fi
+
+#
+# Set variables.
+#
+
+SRVNAME=`uname -n`
+SRVADDR=`get_server_ip`
+DHCPHOSTS_RSRC=files
+DHCP_DEFAULTS=/etc/inet/dhcpsvc.conf
+
+#
+# Unconfig.
+#
+/usr/sbin/dhcpconfig -U -f -x >>${OUTFILE} 2>&1
+
+#
+# Verify that the dhcp defaults file was removed.
+#
+if [ -f ${DHCP_DEFAULTS} ]
+then
+ echo "${DHCP_DEFAULTS} was not removed."
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+#
+# Verify that the dhcptab was removed.
+#
+DATAFILE=/tmp/${TESTNAME}.data.$$
+/usr/sbin/dhtadm -r ${DHCPRSRC_NEW} -p ${DHCPPATH_NEW} -P >${DATAFILE} 2>>${OUTFILE}
+RET=$?
+if [ "${RET}" == "0" ]
+then
+ rm ${DATAFILE}
+ echo "Error the dhcptab still exists?"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+rm ${DATAFILE}
+
+#
+# Verify that the dhcp server was stopped.
+#
+PID=`pgrep -x -u 0 in.dhcpd`
+if [ ! -z "${PID}" ]
+then
+ echo "DHCP Server was not stopped."
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+fi
+
+echo "${TESTNAME} - Test passed."
+exit 0
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/utilities.san b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/utilities.san
new file mode 100644
index 0000000000..8fd3db1208
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/tests/utilities.san
@@ -0,0 +1,285 @@
+#!/usr/bin/ksh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+
+#
+# Process arguments.
+#
+process_args()
+{
+ DEBUG=
+ DHCPRSRC=
+ DHCPPATH=
+ DHCPRSRC_NEW=
+ DHCPPATH_NEW=
+ ERR=
+ USAGE="Usage: %s: [-d]"
+ while getopts ${VALIDOPTS} name
+ do
+ case $name in
+ d) DEBUG="-d";;
+ r) DHCPRSRC="${OPTARG}";;
+ p) DHCPPATH="${OPTARG}";;
+ R) DHCPRSRC_NEW="${OPTARG}";;
+ P) DHCPPATH_NEW="${OPTARG}";;
+ ?) ERR=1;;
+ esac
+ done
+
+ #
+ # If the resource is a valid option, then it is a required one
+ #
+ echo ${VALIDOPTS} | grep r: >/dev/null
+ if [ $? == 0 ]
+ then
+ USAGE="${USAGE} -r resource"
+ if [ -z "${DHCPRSRC}" ]
+ then
+ ERR=1
+ fi
+ fi
+
+ #
+ # If the path is a valid option, then it is a required one
+ #
+ echo ${VALIDOPTS} | grep p: >/dev/null
+ if [ $? == 0 ]
+ then
+ USAGE="${USAGE} -p path"
+ if [ -z "${DHCPPATH}" ]
+ then
+ ERR=1
+ fi
+ fi
+
+ #
+ # If the conversion resource is a valid option, then it is a required one
+ #
+ echo ${VALIDOPTS} | grep R: >/dev/null
+ if [ $? == 0 ]
+ then
+ USAGE="${USAGE} -R resource"
+ if [ -z "${DHCPRSRC_NEW}" ]
+ then
+ ERR=1
+ fi
+ fi
+
+ #
+ # If the conversion path is a valid option, then it is a required one
+ #
+ echo ${VALIDOPTS} | grep P: >/dev/null
+ if [ $? == 0 ]
+ then
+ USAGE="${USAGE} -P path"
+ if [ -z "${DHCPPATH_NEW}" ]
+ then
+ ERR=1
+ fi
+ fi
+
+ if [ ! -z "${ERR}" ]
+ then
+ printf "$USAGE\n" $0
+ exit -1
+ fi
+}
+
+#
+# Return the primary interface's IP address.
+#
+get_server_ip()
+{
+ awk '$1 ~ /^[0-9]/ && $2 == "'${SRVNAME}'" { printf "%s", $1; exit }' /etc/inet/hosts
+}
+
+#
+# Based on the network specification, determine whether or not network is
+# subnetted or supernetted.
+# Given a dotted IP network number, convert it to the default class
+# network.(used to detect subnetting). Requires one argument, the
+# network number. (e.g. 10.0.0.0) Echos the default network and default
+# mask for success, null if error.
+#
+get_default_class()
+{
+ NN01=${1%%.*}
+ tmp=${1#*.}
+ NN02=${tmp%%.*}
+ tmp=${tmp#*.}
+ NN03=${tmp%%.*}
+ tmp=${tmp#*.}
+ NN04=${tmp%%.*}
+ RETNET=""
+ RETMASK=""
+
+ typeset -i16 ONE=10#${1%%.*}
+ typeset -i10 X=$((${ONE}&16#f0))
+ if [ ${X} -eq 224 ]
+ then
+ # Multicast
+ typeset -i10 TMP=$((${ONE}&16#f0))
+ RETNET="${TMP}.0.0.0"
+ RETMASK="240.0.0.0"
+ fi
+ typeset -i10 X=$((${ONE}&16#80))
+ if [ -z "${RETNET}" -a ${X} -eq 0 ]
+ then
+ # Class A
+ RETNET="${NN01}.0.0.0"
+ RETMASK="255.0.0.0"
+ fi
+ typeset -i10 X=$((${ONE}&16#c0))
+ if [ -z "${RETNET}" -a ${X} -eq 128 ]
+ then
+ # Class B
+ RETNET="${NN01}.${NN02}.0.0"
+ RETMASK="255.255.0.0"
+ fi
+ typeset -i10 X=$((${ONE}&16#e0))
+ if [ -z "${RETNET}" -a ${X} -eq 192 ]
+ then
+ # Class C
+ RETNET="${NN01}.${NN02}.${NN03}.0"
+ RETMASK="255.255.255.0"
+ fi
+ print - ${RETNET} ${RETMASK}
+ unset NNO1 NNO2 NNO3 NNO4 RETNET RETMASK X ONE
+}
+
+#
+# Based on the nsswitch setting, query the netmasks table for a netmask.
+# Accepts one argument, a dotted IP address.
+#
+get_netmask()
+{
+ MTMP=`getent netmasks ${1} | awk '{ print $2 }'`
+ if [ ! -z "${MTMP}" ]
+ then
+ print - ${MTMP}
+ fi
+}
+
+# Given a network number and subnetmask, return the broadcast address.
+get_bcast_addr()
+{
+ typeset -i16 NNO1=10#${1%%.*}
+ tmp=${1#*.}
+ typeset -i16 NNO2=10#${tmp%%.*}
+ tmp=${tmp#*.}
+ typeset -i16 NNO3=10#${tmp%%.*}
+ tmp=${tmp#*.}
+ typeset -i16 NNO4=10#${tmp%%.*}
+
+ typeset -i16 NMO1=10#${2%%.*}
+ tmp=${2#*.}
+ typeset -i16 NMO2=10#${tmp%%.*}
+ tmp=${tmp#*.}
+ typeset -i16 NMO3=10#${tmp%%.*}
+ tmp=${tmp#*.}
+ typeset -i16 NMO4=10#${tmp%%.*}
+
+ typeset -i16 ONE
+ typeset -i16 TWO
+ typeset -i16 THREE
+ typeset -i16 FOUR
+ let ONE=\~${NMO1}\|${NNO1}
+ let ONE=${ONE}\&16#ff
+ let TWO=\~${NMO2}\|${NNO2}
+ let TWO=${TWO}\&16#ff
+ let THREE=\~${NMO3}\|${NNO3}
+ let THREE=${THREE}\&16#ff
+ let FOUR=\~${NMO4}\|${NNO4}
+ let FOUR=${FOUR}\&16#ff
+ typeset -i10 ONE
+ typeset -i10 TWO
+ typeset -i10 THREE
+ typeset -i10 FOUR
+ print - "${ONE}.${TWO}.${THREE}.${FOUR}"
+}
+
+# Given a network number and subnetmask, return the subnet address.
+get_subnet_addr()
+{
+ typeset -i16 NNO1=10#${1%%.*}
+ tmp=${1#*.}
+ typeset -i16 NNO2=10#${tmp%%.*}
+ tmp=${tmp#*.}
+ typeset -i16 NNO3=10#${tmp%%.*}
+ tmp=${tmp#*.}
+ typeset -i16 NNO4=10#${tmp%%.*}
+
+ typeset -i16 NMO1=10#${2%%.*}
+ tmp=${2#*.}
+ typeset -i16 NMO2=10#${tmp%%.*}
+ tmp=${tmp#*.}
+ typeset -i16 NMO3=10#${tmp%%.*}
+ tmp=${tmp#*.}
+ typeset -i16 NMO4=10#${tmp%%.*}
+
+ typeset -i16 ONE
+ typeset -i16 TWO
+ typeset -i16 THREE
+ typeset -i16 FOUR
+ let ONE=${NMO1}\&${NNO1}
+ let TWO=${NMO2}\&${NNO2}
+ let THREE=${NMO3}\&${NNO3}
+ let FOUR=${NMO4}\&${NNO4}
+ typeset -i10 ONE
+ typeset -i10 TWO
+ typeset -i10 THREE
+ typeset -i10 FOUR
+ print - "${ONE}.${TWO}.${THREE}.${FOUR}"
+}
+
+#
+# Given a macro definition defined in MACRO_DEFINITION and a symbol/value string
+# defined in SRCH, search the macro definition for the string. If not found, abort.
+# If found, remove the string from the definition.
+#
+macro_find_and_replace()
+{
+ VAL=`expr "${MACRO_DEFINITION}" : .*"${SRCH}".*`
+ if [ "${VAL}" = "0" ]
+ then
+ echo "${SRCH} not defined as part of the macro definition"
+ echo "${TESTNAME} - Test failed!"
+ exit 1
+ fi
+
+ SRCH=`echo "${SRCH}" | cut -c2-`
+ MACRO_DEFINITION=`echo "${MACRO_DEFINITION}" | sed s/"${SRCH}"//`
+}
+
+#
+# Given a macro or option definition, returns its value.
+#
+function get_value
+{
+ echo $* | cut -d ' ' -f 3-
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/AddressView.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/AddressView.java
new file mode 100644
index 0000000000..04f7d5f37d
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/AddressView.java
@@ -0,0 +1,805 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.client;
+
+import javax.swing.*;
+import javax.swing.event.*;
+import javax.swing.table.*;
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+import java.text.*;
+import java.net.*;
+
+import com.sun.dhcpmgr.ui.*;
+import com.sun.dhcpmgr.server.*;
+import com.sun.dhcpmgr.data.*;
+import com.sun.dhcpmgr.bridge.BridgeException;
+import com.sun.dhcpmgr.bridge.NoEntryException;
+
+/**
+ * Address View displays the networks currently under DHCP management, and
+ * as a network is selected from the list its addresses are displayed.
+ */
+public class AddressView implements View {
+ private JPanel displayPanel;
+ protected static AutosizingTable addressTable;
+ private JScrollPane addressPane;
+ private boolean firstActivation = true;
+ private NetworkListModel networkListModel;
+ private JList networkList;
+ protected static AddressTableModel addressTableModel = null;
+ private TableSorter sortedTableModel;
+ private JCheckBoxMenuItem showGrid;
+ private JCheckBoxMenuItem showAddresses;
+ private JMenuItem addAddrs;
+ private JMenuItem releaseAddrs;
+ private JMenuItem addNet;
+ private JMenuItem deleteNets;
+ private JMenuItem addressHelp;
+ private Vector[] menuItems;
+ private Frame myFrame;
+ private Vector selectionListeners = new Vector();
+ private int sortModelIndex = -1;
+ private static final String NO_NETWORKS =
+ ResourceStrings.getString("no_networks");
+
+ // Model class for the network list
+ class NetworkListModel extends AbstractListModel {
+ private Object currentValue;
+ private Network data[] = null;
+
+ public void load() {
+ try {
+ MainFrame.setStatusText(
+ ResourceStrings.getString("loading_networks"));
+ data = DataManager.get().getNetworks(true);
+ } catch (Throwable e) {
+ e.printStackTrace();
+ } finally {
+ int len = 0;
+ if (data != null) {
+ len = data.length;
+ }
+ MainFrame.setStatusText(
+ MessageFormat.format(
+ ResourceStrings.getString("networks_loaded"), len));
+ }
+ }
+
+ public void reload() {
+ load();
+ fireContentsChanged(this, -1, -1);
+ }
+
+ public int getSize() {
+ if (data == null) {
+ load();
+ }
+ if (data == null) {
+ return 0;
+ } else {
+ return data.length;
+ }
+ }
+
+ public Object getElementAt(int index) {
+ if (data == null) {
+ load();
+ }
+ if (data == null || index >= data.length) {
+ return "";
+ } else {
+ return data[index].toString();
+ }
+ }
+
+ public Network getNetworkAt(int index) {
+ if (data == null || index >= data.length) {
+ return null;
+ } else {
+ return data[index];
+ }
+ }
+ }
+
+ // Container class for the address data
+ class AddressTableModel extends AbstractTableModel {
+ private DhcpClientRecord [] data;
+ private String network;
+ private boolean showAddresses;
+ private boolean firstLoad;
+
+ public AddressTableModel() {
+ data = null;
+ network = "";
+ showAddresses = false;
+ firstLoad = true;
+ }
+
+ public void load(String network) {
+ data = null;
+ fireTableDataChanged();
+ if (network.length() == 0) {
+ // No network number supplied, so can't load
+ return;
+ }
+ this.network = network;
+
+ // Update the status line
+ Object [] objs = {network};
+ String s = MessageFormat.format(
+ ResourceStrings.getString("loading_addresses"), objs);
+ MainFrame.setStatusText(s);
+
+ // Kick off background loading of addresses
+ AddressLoader loader = new AddressLoader();
+
+ }
+
+ // Loading is done, re-sort and tell the view to repaint
+ protected void doneLoading() {
+ sortedTableModel.reallocateIndexes();
+ if (firstLoad) {
+ sortedTableModel.sortByColumn(0);
+ firstLoad = false;
+ }
+ fireTableDataChanged();
+ }
+
+ protected String getNetwork() {
+ return network;
+ }
+
+ protected void setData(DhcpClientRecord [] newdata) {
+ data = newdata;
+ }
+
+ public void setShowAddresses(boolean state) {
+ showAddresses = state;
+ fireTableStructureChanged();
+ sortedTableModel.sortByColumn(sortModelIndex);
+ }
+
+ public int getRowCount() {
+ if (data == null) {
+ return 0;
+ } else {
+ return data.length;
+ }
+ }
+
+ public int getColumnCount() {
+ return 7;
+ }
+
+ public Object getValueAt(int row, int column) {
+ switch (column) {
+ case 0:
+ if (showAddresses) {
+ return data[row].getClientIP();
+ } else {
+ return data[row].getClientName();
+ }
+ case 1:
+ if (data[row].isUnusable()) {
+ return ResourceStrings.getString("unusable");
+ } else if (data[row].isBootp()) {
+ return ResourceStrings.getString("bootp");
+ } else if (data[row].isManual()) {
+ return ResourceStrings.getString("manual");
+ } else if (data[row].isPermanent()) {
+ return ResourceStrings.getString("permanent");
+ } else {
+ return ResourceStrings.getString("dynamic");
+ }
+ case 2:
+ return data[row].getExpiration();
+ case 3:
+ if (showAddresses) {
+ return data[row].getServerIP();
+ } else {
+ return data[row].getServerName();
+ }
+ case 4:
+ return data[row].getMacro();
+ case 5:
+ return data[row].getClientId();
+ case 6:
+ return data[row].getComment();
+ default:
+ return null;
+ }
+ }
+
+ public Class getColumnClass(int column) {
+ switch (column) {
+ case 0:
+ case 3:
+ if (showAddresses) {
+ return IPAddress.class;
+ } else {
+ return String.class;
+ }
+ case 2:
+ return Date.class;
+ case 1:
+ case 4:
+ case 5:
+ case 6:
+ return String.class;
+ default:
+ return super.getColumnClass(column);
+ }
+ }
+
+ public String getColumnName(int column) {
+ switch (column) {
+ case 0:
+ if (showAddresses) {
+ return ResourceStrings.getString("address_column");
+ } else {
+ return ResourceStrings.getString("client_name_column");
+ }
+ case 1:
+ return ResourceStrings.getString("flags_column");
+ case 2:
+ return ResourceStrings.getString("expires_column");
+ case 3:
+ return ResourceStrings.getString("server_column");
+ case 4:
+ return ResourceStrings.getString("macro_column");
+ case 5:
+ return ResourceStrings.getString("client_column");
+ case 6:
+ return ResourceStrings.getString("comment_column");
+ default:
+ return super.getColumnName(column);
+ }
+ }
+
+ protected DhcpClientRecord getClientAt(int row) {
+ return data[row];
+ }
+ }
+
+ // Background loader for addresses.
+ class AddressLoader extends SwingWorker {
+ public Object construct() {
+ try {
+ String net = addressTableModel.getNetwork();
+ return DataManager.get().getClients(net, true);
+ } catch (final BridgeException e) {
+ // Since we're in a background thread, ask Swing to run ASAP.
+ SwingUtilities.invokeLater(new Runnable() {
+ Object [] args = new Object[] { e.getMessage() };
+ public void run() {
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString("error_loading_addrs"));
+ JOptionPane.showMessageDialog(null, form.format(args),
+ ResourceStrings.getString("server_error_title"),
+ JOptionPane.ERROR_MESSAGE);
+ }
+ });
+ }
+ return null;
+ }
+
+ public void finished() {
+ addressTableModel.setData((DhcpClientRecord [])get());
+ addressTableModel.doneLoading();
+ MainFrame.setStatusText(
+ MessageFormat.format(
+ ResourceStrings.getString("address_status_message"),
+ addressTableModel.getRowCount()));
+ addressTable.clearSelection();
+ }
+ }
+
+ // Renderer class used to make unusable addresses bold in the display
+ class AddressTableCellRenderer extends ExtendedCellRenderer {
+ public Component getTableCellRendererComponent(JTable table,
+ Object value, boolean isSelected, boolean hasFocus, int row,
+ int column) {
+ Component c = super.getTableCellRendererComponent(table, value,
+ isSelected, hasFocus, row, column);
+ int modelRow = sortedTableModel.mapRowAt(row);
+ if (modelRow != -1) {
+ if (addressTableModel.getClientAt(modelRow).isUnusable()) {
+ Font f = c.getFont();
+ c.setFont(new Font(f.getName(), Font.BOLD, f.getSize()));
+ }
+ }
+ return c;
+ }
+ }
+
+ // Recipient of update messages sent when the editing dialogs exit
+ class DialogListener implements ActionListener {
+ public void actionPerformed(ActionEvent e) {
+ // Don't reload if cancel happened
+ if (!e.getActionCommand().equals(DialogActions.CANCEL)) {
+ AddressView.this.reload();
+ }
+ }
+ }
+
+ public AddressView() {
+ displayPanel = new JPanel(new BorderLayout());
+
+ // Create network selection list, tie it to table
+ networkListModel = new NetworkListModel();
+ networkList = new JList(networkListModel);
+ networkList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+ networkList.addListSelectionListener(new ListSelectionListener() {
+ public void valueChanged(ListSelectionEvent e) {
+ // Ignore all but the last in a series of these events
+ if (e.getValueIsAdjusting()) {
+ return;
+ }
+ String net = "";
+ int index = networkList.getSelectedIndex();
+ if (index != -1) {
+ net = (String)networkListModel.getElementAt(
+ networkList.getSelectedIndex());
+ }
+ if (net.length() == 0) {
+ /*
+ * No networks are selected; disable menu items
+ */
+ deleteNets.setEnabled(false);
+ addAddrs.setEnabled(false);
+ showAddresses.setEnabled(false);
+ showGrid.setEnabled(false);
+ } else {
+ deleteNets.setEnabled(true);
+ addAddrs.setEnabled(true);
+ showAddresses.setEnabled(true);
+ showGrid.setEnabled(true);
+ }
+ addressTableModel.load(net);
+ }
+ });
+
+ // Use a prototype value as a performance enhancement
+ networkList.setPrototypeCellValue("222.222.222.222");
+ JScrollPane networkPane = new JScrollPane(networkList);
+ JPanel networkPanel = new JPanel(new BorderLayout());
+ networkPanel.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 5));
+
+ Mnemonic mnNetwork = new Mnemonic(ResourceStrings.getString("network"));
+ JLabel nwLbl = new JLabel(mnNetwork.getString());
+ nwLbl.setLabelFor(networkPanel);
+ nwLbl.setToolTipText(mnNetwork.getString());
+ networkPanel.add(nwLbl, BorderLayout.NORTH);
+ nwLbl.setDisplayedMnemonic(mnNetwork.getMnemonic());
+
+ networkPanel.add(networkPane, BorderLayout.CENTER);
+ displayPanel.add(networkPanel, BorderLayout.WEST);
+
+ // Create table to display in data area
+ addressTableModel = new AddressTableModel();
+ sortedTableModel = new TableSorter(addressTableModel);
+ addressTable = new AutosizingTable(sortedTableModel);
+ sortedTableModel.addMouseListenerToHeaderInTable(addressTable);
+ addressTable.getTableHeader().setReorderingAllowed(true);
+ addressTable.getTableHeader().setResizingAllowed(true);
+ addressTable.setSelectionMode(
+ ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
+
+ sortedTableModel.addActionListener(new ActionListener() {
+ private SortedHeaderRenderer sortedRenderer =
+ new SortedHeaderRenderer(addressTable);
+ private TableCellRenderer savedRenderer;
+ public void actionPerformed(ActionEvent e) {
+ // Clear the selection when sorting is changed
+ addressTable.clearSelection();
+ /*
+ * Change the header rendering to show which column is
+ * being used for sorting of the data.
+ */
+ int modelIndex = Integer.parseInt(e.getActionCommand());
+ int viewIndex =
+ addressTable.convertColumnIndexToView(modelIndex);
+ if (sortModelIndex != -1) {
+ int sortViewIndex =
+ addressTable.convertColumnIndexToView(sortModelIndex);
+ addressTable.getColumnModel().getColumn(
+ sortViewIndex).setHeaderRenderer(savedRenderer);
+ }
+ /*
+ * Save the column currently being sorted so we can restore
+ * the renderer later. We save model columns rather than
+ * view columns because model columns are invariant while
+ * view columns can be reordered with confusion resulting.
+ */
+ TableColumn c =
+ addressTable.getColumnModel().getColumn(viewIndex);
+ savedRenderer = c.getHeaderRenderer();
+ c.setHeaderRenderer(sortedRenderer);
+ sortModelIndex = modelIndex;
+ }
+ });
+
+ // Make double-clicks the same as Edit->Properties
+ addressTable.addMouseListener(new MouseAdapter() {
+ public void mouseClicked(MouseEvent e) {
+ if (e.getClickCount() == 2) {
+ handleProperties();
+ }
+ }
+ });
+
+ // Install custom renderer to bold the entries which are unusable
+ TableCellRenderer renderer = new AddressTableCellRenderer();
+ addressTable.setDefaultRenderer(String.class, renderer);
+ addressTable.setDefaultRenderer(IPAddress.class, renderer);
+ addressTable.setDefaultRenderer(Date.class, renderer);
+
+ // Wrap it in a scroll pane
+ addressPane = new JScrollPane(addressTable);
+
+ displayPanel.add(addressPane, BorderLayout.CENTER);
+
+ // Create menu items
+ Mnemonic mnShowAddrs =
+ new Mnemonic(ResourceStrings.getString("show_addresses"));
+ showAddresses = new JCheckBoxMenuItem(mnShowAddrs.getString(),
+ false);
+ showAddresses.setMnemonic(mnShowAddrs.getMnemonic());
+ showAddresses.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ addressTableModel.setShowAddresses(showAddresses.getState());
+ }
+ });
+
+ Mnemonic mnShowGrid =
+ new Mnemonic(ResourceStrings.getString("show_grid"));
+ showGrid = new JCheckBoxMenuItem(mnShowGrid.getString(),
+ true);
+ showGrid.setMnemonic(mnShowGrid.getMnemonic());
+ showGrid.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ addressTable.setShowGrid(showGrid.getState());
+ }
+ });
+
+ Mnemonic mnAddNet =
+ new Mnemonic(ResourceStrings.getString("add_network"));
+ addNet = new JMenuItem(mnAddNet.getString());
+ addNet.setMnemonic(mnAddNet.getMnemonic());
+ addNet.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ ConfigWizard wiz = new ConfigWizard(myFrame,
+ ResourceStrings.getString("net_wiz_title"), false);
+ wiz.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ if (e.getActionCommand().equals("finished")) {
+ reload();
+ }
+ }
+ });
+ wiz.pack();
+ wiz.setVisible(true);
+ }
+ });
+
+
+ Mnemonic mnDelNets =
+ new Mnemonic(ResourceStrings.getString("delete_networks"));
+ deleteNets = new JMenuItem(mnDelNets.getString());
+ deleteNets.setMnemonic(mnDelNets.getMnemonic());
+ deleteNets.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ DeleteNetworksDialog d = new DeleteNetworksDialog(myFrame);
+ d.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ if (e.getActionCommand().equals(DialogActions.OK)) {
+ reload();
+ }
+ }
+ });
+ d.pack();
+ d.setVisible(true);
+ }
+ });
+
+ Mnemonic mnAddAddr =
+ new Mnemonic(ResourceStrings.getString("add_addresses"));
+ addAddrs = new JMenuItem(mnAddAddr.getString());
+ addAddrs.setMnemonic(mnAddAddr.getMnemonic());
+ addAddrs.setEnabled(false); // Start out disabled
+ addAddrs.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ AddressWizard wiz = new AddressWizard(myFrame,
+ networkListModel.getNetworkAt(
+ networkList.getSelectedIndex()));
+ wiz.addActionListener(new DialogListener());
+ wiz.pack();
+ wiz.setVisible(true);
+ }
+ });
+
+ Mnemonic mnRelAddr =
+ new Mnemonic(ResourceStrings.getString("release_addresses"));
+ releaseAddrs = new JMenuItem(mnRelAddr.getString());
+ releaseAddrs.setMnemonic(mnRelAddr.getMnemonic());
+ releaseAddrs.setEnabled(false); // Start out disabled
+ releaseAddrs.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ int [] rows = addressTable.getSelectedRows();
+ if (rows.length == 0) {
+ return;
+ }
+ DhcpClientRecord [] recs = new DhcpClientRecord[rows.length];
+ for (int i = 0; i < rows.length; ++i) {
+ recs[i] = addressTableModel.getClientAt(
+ sortedTableModel.mapRowAt(rows[i]));
+ }
+ ReleaseAddressDialog d = new ReleaseAddressDialog(myFrame, recs,
+ (String)networkListModel.getElementAt(
+ networkList.getSelectedIndex()),
+ showAddresses.isSelected());
+ d.addActionListener(new DialogListener());
+ d.pack();
+ d.setVisible(true);
+ }
+ });
+
+ Mnemonic mnOnAddrs =
+ new Mnemonic(ResourceStrings.getString("on_addresses_item"));
+ addressHelp = new JMenuItem(mnOnAddrs.getString());
+ addressHelp.setMnemonic(mnOnAddrs.getMnemonic());
+ addressHelp.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ DhcpmgrApplet.showHelp("addresses_reference");
+ }
+ });
+
+ /*
+ * Construct the menu lists
+ */
+ menuItems = new Vector[MainFrame.MENU_COUNT];
+ for (int i = 0; i < menuItems.length; ++i) {
+ menuItems[i] = new Vector();
+ }
+ menuItems[MainFrame.VIEW_MENU].addElement(showAddresses);
+ menuItems[MainFrame.VIEW_MENU].addElement(showGrid);
+ menuItems[MainFrame.EDIT_MENU].addElement(addAddrs);
+ menuItems[MainFrame.EDIT_MENU].addElement(releaseAddrs);
+ menuItems[MainFrame.EDIT_MENU].addElement(addNet);
+ menuItems[MainFrame.EDIT_MENU].addElement(deleteNets);
+ menuItems[MainFrame.HELP_MENU].addElement(addressHelp);
+
+ // Listen for selections events, manipulate menu item state as needed
+ addressTable.getSelectionModel().addListSelectionListener(
+ new ListSelectionListener() {
+ public void valueChanged(ListSelectionEvent e) {
+ if (addressTable.getSelectionModel().isSelectionEmpty()) {
+ // Disable menu items
+ releaseAddrs.setEnabled(false);
+ } else {
+ // Enable menu items
+ releaseAddrs.setEnabled(true);
+ }
+ // Notify listeners that our selection state may have changed
+ notifySelectionListeners();
+ }
+ });
+ }
+
+ public String getName() {
+ return ResourceStrings.getString("address_view_name");
+ }
+
+ // Return custom menus for this view, which is nothing at this time
+ public Enumeration menus() {
+ return null;
+ }
+
+ // Return custom menu items for each menu as requested.
+ public Enumeration menuItems(int menu) {
+ return menuItems[menu].elements();
+ }
+
+ public Component getDisplay() {
+ return displayPanel;
+ }
+
+ public void setActive(boolean state) {
+ if (state) {
+ if (firstActivation) {
+ // Find frame we're in for use when creating dialogs
+ myFrame = (Frame)SwingUtilities.getAncestorOfClass(
+ MainFrame.class, addressTable);
+ if (networkListModel.getSize() != 0) {
+ networkList.setSelectedIndex(0);
+ }
+ firstActivation = false;
+ } else {
+ // Clear any messages left from other views
+ MainFrame.setStatusText("");
+ }
+ }
+ }
+
+ // Handle a find
+ public void find(String s) {
+ int startRow = addressTable.getSelectedRow() + 1;
+ for (int i = startRow; i < sortedTableModel.getRowCount(); ++i) {
+ DhcpClientRecord rec =
+ addressTableModel.getClientAt(sortedTableModel.mapRowAt(i));
+ if (rec.getClientName().indexOf(s) != -1 ||
+ rec.toString().indexOf(s) != -1) {
+ addressTable.setRowSelectionInterval(i, i);
+ addressTable.scrollRectToVisible(
+ addressTable.getCellRect(i, 0, false));
+ return;
+ }
+ }
+ // Got to the end, wrap around
+ for (int i = 0; i < startRow; ++i) {
+ DhcpClientRecord rec =
+ addressTableModel.getClientAt(sortedTableModel.mapRowAt(i));
+ if (rec.getClientName().indexOf(s) != -1 ||
+ rec.toString().indexOf(s) != -1) {
+ addressTable.setRowSelectionInterval(i, i);
+ addressTable.scrollRectToVisible(
+ addressTable.getCellRect(i, 0, false));
+ return;
+ }
+ }
+ }
+
+ public void handleCreate() {
+ if (networkList.getSelectedIndex() == -1) {
+ // Tell user to use Network Wizard
+ JOptionPane.showMessageDialog(myFrame,
+ ResourceStrings.getString("run_network_wizard"),
+ ResourceStrings.getString("error_message"),
+ JOptionPane.ERROR_MESSAGE);
+ } else {
+ CreateAddressDialog d = new CreateAddressDialog(myFrame,
+ CreateAddressDialog.CREATE, new DhcpClientRecord(),
+ networkListModel.getNetworkAt(networkList.getSelectedIndex()));
+ d.addActionListener(new DialogListener());
+ d.pack();
+ d.setVisible(true);
+ }
+ }
+
+ public void handleDelete() {
+ int [] rows = addressTable.getSelectedRows();
+ if (rows.length == 0) {
+ return;
+ }
+ DhcpClientRecord [] recs = new DhcpClientRecord[rows.length];
+ for (int i = 0; i < rows.length; ++i) {
+ recs[i] = addressTableModel.getClientAt(
+ sortedTableModel.mapRowAt(rows[i]));
+ }
+ DeleteAddressDialog d = new DeleteAddressDialog(myFrame, recs,
+ (String)networkListModel.getElementAt(
+ networkList.getSelectedIndex()));
+ d.addActionListener(new DialogListener());
+ d.pack();
+ d.setVisible(true);
+ }
+
+ public void handleDuplicate() {
+ int row = addressTable.getSelectedRow();
+ if (row == -1) {
+ return;
+ }
+ DhcpClientRecord rec =
+ addressTableModel.getClientAt(sortedTableModel.mapRowAt(row));
+ if (rec == null) {
+ return;
+ }
+ CreateAddressDialog d = new CreateAddressDialog(myFrame,
+ CreateAddressDialog.DUPLICATE, (DhcpClientRecord)rec.clone(),
+ networkListModel.getNetworkAt(networkList.getSelectedIndex()));
+ d.addActionListener(new DialogListener());
+ d.pack();
+ d.setVisible(true);
+ }
+
+ public void handleProperties() {
+ int [] rows = addressTable.getSelectedRows();
+ if (rows.length == 0) {
+ return;
+ }
+ DhcpClientRecord [] recs = new DhcpClientRecord[rows.length];
+ for (int i = 0; i < rows.length; ++i) {
+ recs[i] =
+ addressTableModel.getClientAt(
+ sortedTableModel.mapRowAt(rows[i]));
+ }
+ if (recs.length == 1) {
+ // Edit a single address
+ CreateAddressDialog d = new CreateAddressDialog(myFrame,
+ CreateAddressDialog.EDIT, (DhcpClientRecord)recs[0].clone(),
+ networkListModel.getNetworkAt(networkList.getSelectedIndex()));
+ d.addActionListener(new DialogListener());
+ d.pack();
+ d.setVisible(true);
+ } else {
+ // Edit a group of addresses
+ ModifyAddressesDialog d = new ModifyAddressesDialog(myFrame, recs,
+ (String)networkListModel.getElementAt(
+ networkList.getSelectedIndex()));
+ d.addActionListener(new DialogListener());
+ d.pack();
+ d.setVisible(true);
+ }
+ }
+
+ public void handleUpdate() {
+ reload();
+ }
+
+ protected void reload() {
+ Object value = networkList.getSelectedValue();
+ networkListModel.reload();
+ networkList.clearSelection();
+ networkList.setSelectedValue(value, true);
+ if (networkListModel.getSize() != 0 &&
+ networkList.getSelectedIndex() == -1) {
+ // Didn't get selected, must be gone. Select first item in list
+ networkList.setSelectedIndex(0);
+ }
+ }
+
+ public void addSelectionListener(SelectionListener listener) {
+ selectionListeners.addElement(listener);
+ }
+
+ public void removeSelectionListener(SelectionListener listener) {
+ selectionListeners.removeElement(listener);
+ }
+
+ private void notifySelectionListeners() {
+ Enumeration en = selectionListeners.elements();
+ while (en.hasMoreElements()) {
+ SelectionListener l = (SelectionListener)en.nextElement();
+ l.valueChanged();
+ }
+ }
+
+ public boolean isSelectionEmpty() {
+ return addressTable.getSelectionModel().isSelectionEmpty();
+ }
+
+ public boolean isSelectionMultiple() {
+ return (addressTable.getSelectedRowCount() > 1);
+ }
+
+ public void startAddressWizard() {
+ addAddrs.doClick();
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/AddressWizard.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/AddressWizard.java
new file mode 100644
index 0000000000..2ebcab6d1c
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/AddressWizard.java
@@ -0,0 +1,1114 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.client;
+
+import com.sun.dhcpmgr.ui.*;
+import com.sun.dhcpmgr.data.*;
+import com.sun.dhcpmgr.bridge.BridgeException;
+import com.sun.dhcpmgr.bridge.NoEntryException;
+import com.sun.dhcpmgr.bridge.ExistsException;
+import com.sun.dhcpmgr.bridge.HostExistsException;
+import com.sun.dhcpmgr.bridge.NoTableException;
+import com.sun.dhcpmgr.server.DhcpNetMgr;
+
+import javax.swing.*;
+import javax.swing.table.*;
+import javax.swing.event.*;
+import javax.swing.border.*;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+import java.text.MessageFormat;
+import java.net.*;
+
+/**
+ * A wizard to configure a group of addresses.
+ */
+public class AddressWizard extends Wizard {
+ private Network network;
+ private int number = 10;
+ private String comment = "";
+ private String server = DataManager.get().getShortServerName();
+ private IPAddress serverIP;
+ private IPAddress startAddress;
+ private boolean generateNames = false;
+ private String baseName = DataManager.get().getShortServerName();
+ private String macro = DataManager.get().getShortServerName();
+ private boolean unusable = false;
+ private boolean dynamic = true;
+ private WizardTableModel addressTableModel;
+ private Macro noMacro;
+
+ class Address {
+ IPAddress addr;
+ String name;
+
+ public Address() {
+ addr = null;
+ name = "";
+ }
+
+ public Address(int a, String n) {
+ name = n;
+ setAddr(a);
+ }
+
+ public Address(String a, String n) {
+ name = n;
+ setAddr(a);
+ }
+
+ public void setAddr(int a) {
+ addr = new IPAddress(a);
+ }
+
+ public void setAddr(String a) {
+ try {
+ addr = new IPAddress(a);
+ } catch (ValidationException e) {
+ // Do nothing
+ }
+ }
+
+ public String toString() {
+ return addr.getHostAddress();
+ }
+ }
+
+ class WizardTableModel extends AbstractTableModel {
+ private Vector addrs = new Vector();
+
+ public int getRowCount() {
+ return addrs.size();
+ }
+
+ public int getColumnCount() {
+ return 2;
+ }
+
+ public Object getValueAt(int row, int column) {
+ if (column == 0) {
+ return ((Address)addrs.elementAt(row)).addr;
+ } else {
+ return ((Address)addrs.elementAt(row)).name;
+ }
+ }
+
+ public Class getColumnClass(int column) {
+ if (column == 0) {
+ return IPAddress.class;
+ } else {
+ return String.class;
+ }
+ }
+
+ public String getColumnName(int column) {
+ if (column == 0) {
+ return ResourceStrings.getString("address_column");
+ } else {
+ return ResourceStrings.getString("client_name_column");
+ }
+ }
+
+ public long generateAddresses() {
+ if (!network.containsAddress(startAddress)) {
+ return 0;
+ }
+
+ int net = network.getAddress().intValue();
+ int mask = network.getMask().intValue();
+ int start = startAddress.intValue();
+
+ addrs.removeAllElements();
+ long max = (long)(net + ~mask) & 0xffffffffL;
+ int count = 0;
+ int index = start - net;
+ if (index == 0) {
+ // Don't try allocating the network address as a client address
+ ++index;
+ }
+ DhcpClientRecord [] clients = null;
+ try {
+ /*
+ * Sort the data so we can generate the list of addresses
+ * with a minimal number of comparisons here. First, though,
+ * clone the array so that sorting won't affect the original
+ * data set and throw off the main display.
+ */
+ clients = (DhcpClientRecord [])DataManager.get().getClients(
+ network.getAddress().toString(), false).clone();
+ Arrays.sort(clients);
+ } catch (Throwable e) {
+ // XXX What to do here???
+ e.printStackTrace();
+ }
+ int base = 0;
+ long searchAddress = 0;
+ while (count < number) {
+ long address = (long)(net + index) & 0xffffffffL;
+ if (address == max) {
+ // We finished searching before satisfying the request
+ break;
+ }
+ /*
+ * If clients == null then this is an empty network,
+ * so searching for holes is unnecessary
+ */
+ if (clients != null) {
+ // Advance search pointer past lower-numbered addresses
+ while ((base < clients.length)
+ && ((searchAddress =
+ clients[base].getBinaryAddress()) < address)) {
+ ++base;
+ }
+ }
+ if (searchAddress != address) {
+ // found an empty slot; create the address
+ Address addr;
+ if (generateNames) {
+ addr = new Address((int)address,
+ baseName + "-" + String.valueOf(index));
+ } else {
+ addr = new Address((int)address, "");
+ }
+ addrs.addElement(addr);
+ ++count;
+ }
+ ++index;
+ }
+
+ // Inform UI that the data is ready.
+ fireTableDataChanged();
+ return count;
+ }
+
+ public Address getAddressAt(int index) {
+ return (Address)addrs.elementAt(index);
+ }
+ }
+
+ // This step selects the number of addresses and a comment
+ class NumberStep implements WizardStep {
+ private Box stepBox;
+ private IntegerField addressCount;
+ private JTextField commentField;
+
+ public NumberStep() {
+ stepBox = Box.createVerticalBox();
+
+ // Explanatory text at the top
+ stepBox.add(Wizard.createTextArea(
+ ResourceStrings.getString("add_wiz_explain"), 4, 45));
+ stepBox.add(Box.createVerticalStrut(10));
+ stepBox.add(Box.createVerticalGlue());
+
+ // Get the number of addresses to create
+ stepBox.add(Wizard.createTextArea(
+ ResourceStrings.getString("add_wiz_count_explain"), 1, 45));
+
+ Mnemonic mnCount =
+ new Mnemonic(ResourceStrings.getString("add_wiz_count_label"));
+ JLabel label = new JLabel(mnCount.getString());
+ addressCount = new IntegerField(); // Ensure numeric input
+ addressCount.setMaximumSize(addressCount.getPreferredSize());
+
+ label.setLabelFor(addressCount);
+ label.setToolTipText(mnCount.getString());
+ label.setDisplayedMnemonic(mnCount.getMnemonic());
+
+ Box box = Box.createHorizontalBox();
+ box.add(Box.createHorizontalStrut(10));
+ box.add(label);
+ box.add(Box.createHorizontalStrut(5));
+ box.add(addressCount);
+ box.add(Box.createHorizontalGlue());
+ stepBox.add(box);
+
+ stepBox.add(Box.createVerticalStrut(10));
+ stepBox.add(Box.createVerticalGlue());
+ stepBox.add(Wizard.createTextArea(
+ ResourceStrings.getString("add_wiz_comment_explain"), 2, 45));
+
+ // Let user supply a comment
+ Mnemonic mnComm =
+ new Mnemonic(ResourceStrings.getString(
+ "add_wiz_comment_label"));
+ label = new JLabel(mnComm.getString());
+ commentField = new JTextField("", 20);
+
+ label.setLabelFor(commentField);
+ label.setToolTipText(mnComm.getString());
+ label.setDisplayedMnemonic(mnComm.getMnemonic());
+
+ commentField.setMaximumSize(commentField.getPreferredSize());
+ box = Box.createHorizontalBox();
+ box.add(Box.createHorizontalStrut(10));
+ box.add(label);
+ box.add(Box.createHorizontalStrut(5));
+ box.add(commentField);
+ stepBox.add(box);
+ stepBox.add(Box.createVerticalGlue());
+
+ /*
+ * This listener ensures that the forward button is enabled only
+ * when there is a count of addresses in the addressCount field.
+ */
+ addressCount.getDocument().addDocumentListener(
+ new DocumentListener() {
+ public void insertUpdate(DocumentEvent e) {
+ setForwardEnabled(e.getDocument().getLength() != 0);
+ }
+ public void changedUpdate(DocumentEvent e) {
+ insertUpdate(e);
+ }
+ public void removeUpdate(DocumentEvent e) {
+ insertUpdate(e);
+ }
+ });
+ }
+
+ public String getDescription() {
+ return ResourceStrings.getString("add_wiz_number_desc");
+ }
+
+ public Component getComponent() {
+ return stepBox;
+ }
+
+ public void setActive(int direction) {
+ addressCount.setValue(number);
+ commentField.setText(comment);
+ setForwardEnabled(addressCount.getValue() != 0);
+ }
+
+ public boolean setInactive(int direction) {
+ number = addressCount.getValue();
+ if (number == 0) {
+ /*
+ * Going forward with 0 addresses makes no sense,
+ * display error and veto the move.
+ */
+ JOptionPane.showMessageDialog(AddressWizard.this,
+ ResourceStrings.getString("add_wiz_count_error"),
+ ResourceStrings.getString("error_message"),
+ JOptionPane.ERROR_MESSAGE);
+ return false;
+ }
+ comment = commentField.getText();
+ return true;
+ }
+ }
+
+ // This step selects the server and starting address
+ class ServerStep implements WizardStep {
+ private Box stepBox;
+ private IPAddressField startField;
+ private HostnameField baseNameField;
+ private JCheckBox generateNamesBox;
+ private HostnameField serverField;
+
+ public ServerStep() {
+ stepBox = Box.createVerticalBox();
+
+ // Explanatory text at the top
+ stepBox.add(Wizard.createTextArea(
+ ResourceStrings.getString("add_wiz_server_explain"), 1, 45));
+
+ // Server to own these addresses
+ Mnemonic mnMan =
+ new Mnemonic(ResourceStrings.getString("add_wiz_server_label"));
+ JLabel jl = new JLabel(mnMan.getString());
+ Box box = Box.createHorizontalBox();
+ box.add(jl);
+ box.add(Box.createHorizontalStrut(5));
+ serverField = new HostnameField("", 15);
+ jl.setLabelFor(serverField);
+ jl.setToolTipText(mnMan.getString());
+ jl.setDisplayedMnemonic(mnMan.getMnemonic());
+
+ serverField.setMaximumSize(serverField.getPreferredSize());
+ box.add(serverField);
+ box.add(Box.createHorizontalGlue());
+ stepBox.add(box);
+
+ // Add some spacing
+ stepBox.add(Box.createVerticalStrut(5));
+ stepBox.add(Box.createVerticalGlue());
+
+ // Starting address
+ stepBox.add(Wizard.createTextArea(
+ ResourceStrings.getString("add_wiz_start_explain"), 2, 45));
+ box = Box.createHorizontalBox();
+
+ Mnemonic mnStart =
+ new Mnemonic(ResourceStrings.getString("add_wiz_start_label"));
+ JLabel startLbl = new JLabel(mnStart.getString());
+ box.add(startLbl);
+ box.add(Box.createHorizontalStrut(5));
+ startField = new IPAddressField(); // Ensure it's an IP address
+ startLbl.setLabelFor(startField);
+ startLbl.setToolTipText(mnStart.getString());
+ startLbl.setDisplayedMnemonic(mnStart.getMnemonic());
+
+ startField.setMaximumSize(startField.getPreferredSize());
+ box.add(startField);
+ stepBox.add(box);
+
+ // Add some more spacing, and an explanation of generating names
+ stepBox.add(Box.createVerticalStrut(5));
+ stepBox.add(Box.createVerticalGlue());
+ stepBox.add(Wizard.createTextArea(
+ ResourceStrings.getString("add_wiz_generate_explain"), 4, 45));
+
+ // Control to indicate whether names should be generated
+ JPanel panel = new JPanel(new GridLayout(2, 1, 0, 0));
+ generateNamesBox = new JCheckBox(
+ ResourceStrings.getString("add_wiz_generate_label"));
+ generateNamesBox.setToolTipText(
+ ResourceStrings.getString("add_wiz_generate_label"));
+ panel.add(generateNamesBox);
+
+ generateNamesBox.setEnabled(true);
+ try {
+ DhcpdOptions opts =
+ DataManager.get().getDhcpServiceMgr().readDefaults();
+ if (opts.getHostsResource() == null) {
+ generateNamesBox.setEnabled(false);
+ }
+ } catch (BridgeException e) {
+ // Assume set
+ }
+
+ baseNameField = new HostnameField();
+ baseNameField.setEnabled(false);
+ baseNameField.setMaximumSize(baseNameField.getPreferredSize());
+ box = Box.createHorizontalBox();
+ box.add(Box.createHorizontalStrut(17));
+
+ Mnemonic mnRoot =
+ new Mnemonic(ResourceStrings.getString(
+ "add_wiz_rootname_label"));
+ JLabel rootNameLbl = new JLabel(mnRoot.getString());
+ box.add(rootNameLbl);
+ rootNameLbl.setLabelFor(baseNameField);
+ rootNameLbl.setToolTipText(mnRoot.getString());
+ rootNameLbl.setDisplayedMnemonic(mnRoot.getMnemonic());
+
+ box.add(Box.createHorizontalStrut(5));
+ box.add(baseNameField);
+ panel.add(box);
+ stepBox.add(panel);
+ stepBox.add(Box.createVerticalGlue());
+
+ // Only enable the text input if name generation is requested
+ generateNamesBox.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ baseNameField.setEnabled(generateNamesBox.isSelected());
+ }
+ });
+
+ DocumentListener docListener = new DocumentListener() {
+ public void insertUpdate(DocumentEvent e) {
+ setForwardEnabled((startField.getText().length() != 0)
+ && (serverField.getText().length() != 0));
+ }
+ public void changedUpdate(DocumentEvent e) {
+ insertUpdate(e);
+ }
+ public void removeUpdate(DocumentEvent e) {
+ insertUpdate(e);
+ }
+ };
+
+ startField.getDocument().addDocumentListener(docListener);
+ serverField.getDocument().addDocumentListener(docListener);
+ }
+
+ public String getDescription() {
+ return ResourceStrings.getString("add_wiz_server_desc");
+ }
+
+ public Component getComponent() {
+ return stepBox;
+ }
+
+ public void setActive(int direction) {
+ serverField.setText(server);
+ startField.setValue(startAddress);
+ baseNameField.setText(baseName);
+ generateNamesBox.setSelected(generateNames);
+ setForwardEnabled(true);
+ }
+
+ public boolean setInactive(int direction) {
+ if (direction == FORWARD) {
+ // Validate that address is on the network we're working on
+ IPAddress a = startField.getValue();
+ if (a == null) {
+ // Not a valid address at all
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString("add_wiz_invalid_address"));
+ Object [] args = new Object[1];
+ args[0] = startField.getText();
+ JOptionPane.showMessageDialog(AddressWizard.this,
+ form.format(args),
+ ResourceStrings.getString("input_error"),
+ JOptionPane.ERROR_MESSAGE);
+ return false;
+ } else if (!network.containsAddress(a)) {
+ // Address is not on network
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString("bad_network_address"));
+ Object [] args = new Object[2];
+ args[0] = startField.getText();
+ args[1] = network.getAddress();
+ JOptionPane.showMessageDialog(AddressWizard.this,
+ form.format(args),
+ ResourceStrings.getString("input_error"),
+ JOptionPane.ERROR_MESSAGE);
+ return false;
+ }
+ try {
+ serverIP = new IPAddress(serverField.getText());
+ } catch (Throwable e) {
+ /*
+ * Unknown hostname, probably, so put up the message and
+ * decline to continue
+ */
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString("bad_server_name"));
+ Object [] args = new Object[1];
+ args[0] = serverField.getText();
+ JOptionPane.showMessageDialog(AddressWizard.this,
+ form.format(args),
+ ResourceStrings.getString("error_message"),
+ JOptionPane.ERROR_MESSAGE);
+ return false;
+ }
+ }
+ server = serverField.getText();
+ startAddress = startField.getValue();
+ generateNames = generateNamesBox.isSelected();
+ baseName = baseNameField.getText();
+ return true;
+ }
+ }
+
+ // This step confirms the list of addresses to be generated
+ class ConfirmStep implements WizardStep {
+ private JPanel stepPanel;
+ private JTable addressTable;
+
+ public ConfirmStep() {
+ stepPanel = new JPanel(new BorderLayout(10, 10));
+
+ // Explanatory text at the top
+ stepPanel.add(Wizard.createTextArea(
+ ResourceStrings.getString("add_wiz_confirm_explain"), 3, 45),
+ BorderLayout.NORTH);
+
+ // Label the table
+ JPanel panel = new JPanel(new BorderLayout());
+ panel.setBorder(BorderFactory.createEmptyBorder(0, 15, 0, 15));
+ Mnemonic mnIP =
+ new Mnemonic(ResourceStrings.getString(
+ "add_wiz_confirm_label"));
+ JLabel label = new JLabel(mnIP.getString());
+ panel.add(label, BorderLayout.NORTH);
+
+ // Display the addresses in a table
+ addressTable = new JTable(addressTableModel);
+
+ label.setLabelFor(addressTable);
+ label.setToolTipText(mnIP.getString());
+ label.setDisplayedMnemonic(mnIP.getMnemonic());
+
+ addressTable.setDefaultRenderer(IPAddress.class,
+ new ExtendedCellRenderer());
+
+ // Table is not selectable in any way
+ addressTable.setRowSelectionAllowed(false);
+ addressTable.setColumnSelectionAllowed(false);
+ addressTable.setCellSelectionEnabled(false);
+
+ // Wrap in a scroll pane so column headings display
+ JScrollPane scrollPane = new JScrollPane(addressTable);
+ panel.add(scrollPane, BorderLayout.CENTER);
+ stepPanel.add(panel, BorderLayout.CENTER);
+ }
+
+ public String getDescription() {
+ return ResourceStrings.getString("add_wiz_confirm_desc");
+ }
+
+ public Component getComponent() {
+ return stepPanel;
+ }
+
+ public void setActive(int direction) {
+ /*
+ * If we're activating coming from the previous step,
+ * generate the address list
+ */
+ if (direction == FORWARD) {
+ long count = addressTableModel.generateAddresses();
+ // Error if no addresses could be generated
+ if (count == 0) {
+ JOptionPane.showMessageDialog(AddressWizard.this,
+ ResourceStrings.getString("add_wiz_none_available"),
+ ResourceStrings.getString("error_message"),
+ JOptionPane.ERROR_MESSAGE);
+ setForwardEnabled(false);
+ } else {
+ if (count != number) {
+ /*
+ * Warn if we couldn't generate the number of addresses
+ * requested
+ */
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString(
+ "generate_addresses_warning"));
+ Object [] args = new Object[2];
+ args[0] = new Long(count);
+ args[1] = new Long(number);
+ JOptionPane.showMessageDialog(AddressWizard.this,
+ form.format(args),
+ ResourceStrings.getString("warning"),
+ JOptionPane.WARNING_MESSAGE);
+ }
+ setForwardEnabled(true);
+ }
+ } else {
+ setForwardEnabled(true);
+ }
+ }
+
+ public boolean setInactive(int direction) {
+ return true; // Nothing to do when leaving
+ }
+ }
+
+ // This step selects the macro and flags
+ class ConfigureStep implements WizardStep {
+
+ // Model class for the macro list
+ class MacroListModel extends AbstractListModel
+ implements ComboBoxModel {
+ private Object currentValue;
+ private Macro data[] = null;
+
+ public int getSize() {
+ if (data == null) {
+ try {
+ // If we don't have data yet, grab currently cached list
+ data = DataManager.get().getMacros(false);
+ } catch (NoTableException e) {
+ // can function without table
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+ }
+
+ if (data == null || data.length == 0) {
+ return 1;
+ } else {
+ return data.length+1;
+ }
+ }
+
+ public Object getElementAt(int index) {
+ if (data == null) {
+ try {
+ // If we don't have data yet, grab currently cached list
+ data = DataManager.get().getMacros(false);
+ } catch (NoTableException e) {
+ // can function without table
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+ }
+ if (index == 0) {
+ return noMacro.getKey();
+ } else {
+ return data[index-1].getKey();
+ }
+ }
+
+ public void setSelectedItem(Object anItem) {
+ currentValue = noMacro.getKey();
+ for (int i = 0; data != null && i < data.length; i++) {
+ if (((String)(anItem)).equals(((Macro)data[i]).getKey())) {
+ currentValue = anItem;
+ }
+ }
+ fireContentsChanged(this, -1, -1);
+ }
+
+ public Object getSelectedItem() {
+ return currentValue;
+ }
+
+ public Macro getMacroAt(int index) {
+ if (index == 0) {
+ return noMacro;
+ } else {
+ return data[index-1];
+ }
+ }
+ }
+
+ private Box stepBox;
+ private JComboBox macroBox;
+ private MacroListModel macroBoxModel;
+ private JButton viewButton;
+ private JCheckBox unusableBox;
+
+ public ConfigureStep() {
+ stepBox = Box.createVerticalBox();
+
+ // Start with some explanatory text
+ JComponent component = Wizard.createTextArea(
+ ResourceStrings.getString("add_wiz_macro_explain"), 3, 45);
+ component.setAlignmentX(Component.LEFT_ALIGNMENT);
+ stepBox.add(component);
+ // Add some spacing
+ stepBox.add(Box.createVerticalStrut(10));
+
+ // Let 'em select the macro to use
+ Mnemonic mnConf =
+ new Mnemonic(ResourceStrings.getString("add_wiz_macro_label"));
+ JLabel label = new JLabel(mnConf.getString());
+ label.setAlignmentX(Component.LEFT_ALIGNMENT);
+ stepBox.add(label);
+ JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT));
+ macroBoxModel = new MacroListModel();
+ macroBox = new JComboBox(macroBoxModel);
+
+ label.setLabelFor(macroBox);
+ label.setToolTipText(mnConf.getString());
+ label.setDisplayedMnemonic(mnConf.getMnemonic());
+
+ panel.add(macroBox);
+ // Button to view the contents of the selected macro
+
+ Mnemonic mnView =
+ new Mnemonic(ResourceStrings.getString("add_wiz_view_button"));
+ viewButton = new JButton(mnView.getString());
+ viewButton.setToolTipText(mnView.getString());
+ viewButton.setMnemonic(mnView.getMnemonic());
+
+ panel.add(viewButton);
+ panel.setAlignmentX(Component.LEFT_ALIGNMENT);
+ stepBox.add(panel);
+
+ // Give the option to mark them unusable for now
+ component = Wizard.createTextArea(
+ ResourceStrings.getString("add_wiz_flag_explain"), 2, 45);
+ component.setAlignmentX(Component.LEFT_ALIGNMENT);
+ stepBox.add(component);
+ unusableBox = new JCheckBox(
+ ResourceStrings.getString("add_wiz_unusable_label"));
+ unusableBox.setAlignmentX(Component.LEFT_ALIGNMENT);
+ stepBox.add(unusableBox);
+ stepBox.add(Box.createVerticalGlue());
+
+ // When user presses View, show the macro's contents
+ viewButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ /*
+ * viewButton is passed as component relative to which the
+ * dialog should be displayed, keeping user more in
+ * context with the task.
+ */
+ ViewMacroDialog d = new ViewMacroDialog(
+ AddressWizard.this, viewButton,
+ macroBoxModel.getMacroAt(macroBox.getSelectedIndex()));
+ d.pack();
+ d.setVisible(true);
+ }
+ });
+ }
+
+ public String getDescription() {
+ return ResourceStrings.getString("add_wiz_configure_desc");
+ }
+
+ public Component getComponent() {
+ return stepBox;
+ }
+
+ public void setActive(int direction) {
+ macroBox.setSelectedItem(macro);
+ unusableBox.setSelected(unusable);
+ setForwardEnabled(true);
+ }
+
+ public boolean setInactive(int direction) {
+ macro = (String)macroBox.getSelectedItem();
+ unusable = unusableBox.isSelected();
+ return true;
+ }
+ }
+
+ // This step selects the lease type
+ class LeaseStep implements WizardStep {
+ private Box stepBox;
+ private JRadioButton dynamicButton, permanentButton;
+ private ButtonGroup buttonGroup;
+
+ public LeaseStep() {
+ stepBox = Box.createVerticalBox();
+
+ // Start with explanatory text
+ JComponent component = Wizard.createTextArea(
+ ResourceStrings.getString("add_wiz_lease_explain"), 0, 45);
+ component.setAlignmentX(Component.LEFT_ALIGNMENT);
+ stepBox.add(component);
+ stepBox.add(Box.createVerticalStrut(10));
+
+ // User has choice of dynamic or permanent leases
+ Mnemonic mnLease =
+ new Mnemonic(ResourceStrings.getString("add_wiz_lease_label"));
+ JLabel label = new JLabel(mnLease.getString());
+ JPanel panel = new JPanel(new FieldLayout(10, 0));
+ label.setToolTipText(mnLease.getString());
+ label.setDisplayedMnemonic(mnLease.getMnemonic());
+
+ panel.add(FieldLayout.LABEL, label);
+ buttonGroup = new ButtonGroup();
+ dynamicButton = new JRadioButton(
+ ResourceStrings.getString("dynamic"), true);
+ buttonGroup.add(dynamicButton);
+ permanentButton = new JRadioButton(
+ ResourceStrings.getString("permanent"), false);
+ buttonGroup.add(permanentButton);
+ label.setLabelFor(dynamicButton);
+ panel.add(FieldLayout.FIELD, dynamicButton);
+ panel.add(FieldLayout.LABEL, new JLabel(""));
+ panel.add(FieldLayout.FIELD, permanentButton);
+ panel.setAlignmentX(Component.LEFT_ALIGNMENT);
+ stepBox.add(panel);
+ stepBox.add(Box.createVerticalGlue());
+ }
+
+ public String getDescription() {
+ return ResourceStrings.getString("add_wiz_lease_desc");
+ }
+
+ public Component getComponent() {
+ return stepBox;
+ }
+
+ public void setActive(int direction) {
+ dynamicButton.setSelected(dynamic);
+ setForwardEnabled(true);
+ }
+
+ public boolean setInactive(int direction) {
+ dynamic = dynamicButton.isSelected();
+ return true;
+ }
+ }
+
+ // Last chance to check work before committing to it
+ class ReviewStep implements WizardStep {
+ private Box stepBox;
+ private JPanel panel;
+ private JTable addressTable;
+ private JLabel numberLabel;
+ private JLabel commentLabel;
+ private JLabel serverLabel;
+ private JLabel macroLabel;
+ private JLabel flagLabel;
+ private JLabel leaseLabel;
+
+ public ReviewStep() {
+ stepBox = Box.createVerticalBox();
+ stepBox.add(Wizard.createTextArea(
+ ResourceStrings.getString("add_wiz_review_explain"), 4, 45));
+
+ panel = new JPanel(new FieldLayout());
+ JLabel tmpL;
+
+ tmpL = addLabelMnemonic("add_wiz_count_label");
+ numberLabel = addField("20");
+ tmpL.setLabelFor(numberLabel);
+
+ tmpL = addLabelMnemonic("add_wiz_comment_label");
+ commentLabel = addField("Marketing");
+ tmpL.setLabelFor(commentLabel);
+
+ tmpL = addLabelMnemonic("add_wiz_server_label");
+ serverLabel = addField("atlantic");
+ tmpL.setLabelFor(serverLabel);
+
+ tmpL = addLabelMnemonic("add_wiz_macro_label");
+ macroLabel = addField("atlantic");
+ tmpL.setLabelFor(macroLabel);
+
+ tmpL = addLabel("add_wiz_review_unusable");
+ flagLabel = addField("Yes");
+ tmpL.setLabelFor(flagLabel);
+ tmpL.setToolTipText(
+ ResourceStrings.getString("add_wiz_review_unusable"));
+
+ tmpL = addLabelMnemonic("add_wiz_lease_label");
+ leaseLabel = addField(ResourceStrings.getString("dynamic"));
+ tmpL.setLabelFor(leaseLabel);
+
+ panel.setAlignmentX(Component.LEFT_ALIGNMENT);
+ stepBox.add(panel);
+
+ stepBox.add(Box.createVerticalStrut(5));
+
+ Mnemonic mnAdd =
+ new Mnemonic(ResourceStrings.getString(
+ "add_wiz_confirm_label"));
+ JLabel label = new JLabel(mnAdd.getString());
+ stepBox.add(label);
+ stepBox.add(Box.createVerticalStrut(2));
+ addressTable = new JTable(addressTableModel);
+ label.setLabelFor(addressTable);
+ label.setToolTipText(mnAdd.getString());
+ label.setDisplayedMnemonic(mnAdd.getMnemonic());
+
+ addressTable.setDefaultRenderer(IPAddress.class,
+ new ExtendedCellRenderer());
+
+ // Table should not be selectable in any way
+ addressTable.setRowSelectionAllowed(false);
+ addressTable.setColumnSelectionAllowed(false);
+ addressTable.setCellSelectionEnabled(false);
+ JScrollPane scrollPane = new JScrollPane(addressTable);
+ Dimension d = addressTable.getPreferredScrollableViewportSize();
+ d.height = 50;
+ addressTable.setPreferredScrollableViewportSize(d);
+ stepBox.add(scrollPane);
+ stepBox.add(Box.createVerticalGlue());
+ }
+
+ private JLabel addLabel(String s) {
+ JLabel l = new JLabel(ResourceStrings.getString(s));
+ panel.add(FieldLayout.LABEL, l);
+ return l;
+ }
+
+ private JLabel addLabelMnemonic(String s) {
+ Mnemonic mnStr =
+ new Mnemonic(ResourceStrings.getString(s));
+ JLabel l = new JLabel(mnStr.getString());
+ l.setToolTipText(mnStr.getString());
+ panel.add(FieldLayout.LABEL, l);
+ return l;
+ }
+
+ private JLabel addField(String s) {
+ JLabel l = new JLabel(s);
+ l.setForeground(Color.black);
+ panel.add(FieldLayout.FIELD, l);
+ return l;
+ }
+
+ public String getDescription() {
+ return ResourceStrings.getString("add_wiz_review_desc");
+ }
+
+ public Component getComponent() {
+ return stepBox;
+ }
+
+ public void setActive(int direction) {
+ // Use number of addresses actually generated, not requested
+ numberLabel.setText(
+ String.valueOf(addressTableModel.getRowCount()));
+ commentLabel.setText(comment);
+ serverLabel.setText(server);
+ macroLabel.setText(macro);
+ if (unusable) {
+ flagLabel.setText(ResourceStrings.getString("yes"));
+ } else {
+ flagLabel.setText(ResourceStrings.getString("no"));
+ }
+ if (dynamic) {
+ leaseLabel.setText(ResourceStrings.getString("dynamic"));
+ } else {
+ leaseLabel.setText(ResourceStrings.getString("permanent"));
+ }
+ setFinishEnabled(true);
+ }
+
+ public boolean setInactive(int direction) {
+ // Nothing to do
+ return true;
+ }
+ }
+
+ public AddressWizard(Frame owner, Network net) {
+ super(owner, "");
+ setTitle(MessageFormat.format(
+ ResourceStrings.getString("address_wizard_title"), net.toString()));
+
+ network = net;
+ startAddress = network.getAddress();
+
+ try {
+ noMacro = new Macro(ResourceStrings.getString("no_macro_item"));
+ } catch (ValidationException e) {
+ // this should never happen!
+ System.err.println(e.getMessage());
+ }
+ addressTableModel = new WizardTableModel();
+
+ // Create steps in order of appearance
+ addStep(new NumberStep());
+ addStep(new ServerStep());
+ addStep(new ConfirmStep());
+ addStep(new ConfigureStep());
+ addStep(new LeaseStep());
+ addStep(new ReviewStep());
+ showFirstStep();
+ }
+
+ public void doFinish() {
+ /*
+ * Method here is as follows:
+ * 1. Create a ProgressManager which will apprise user of our progress
+ * 2. Create a background thread to execute the add operations
+ * 3. Within the background thread, update the progress monitor
+ * as each address is created.
+ * 4. At completion, the background thread displays the error
+ * output, if any, before it invokes one last runnable which pops
+ * down and cleans up.
+ */
+ // final so that ProgressUpdater can access it
+ final ProgressManager progress = new ProgressManager(this,
+ ResourceStrings.getString("add_wiz_progress"), "", 0,
+ addressTableModel.getRowCount());
+ final Runnable finisher = new Runnable() {
+ public void run() {
+ reallyFinish();
+ }
+ };
+
+ // Here's the thread which does the adds
+ Thread addThread = new Thread() {
+ public void run() {
+ DhcpNetMgr server = DataManager.get().getDhcpNetMgr();
+ // Create a template object which we'll use for all the adds
+ DhcpClientRecord rec = new DhcpClientRecord();
+ rec.setExpiration(new Date(0));
+ rec.setUnusable(unusable);
+ rec.setPermanent(!dynamic);
+ try {
+ rec.setServerIP(serverIP);
+ } catch (ValidationException e) {
+ // Should never happen as we have a valid IP already
+ }
+ if (macro.equals(noMacro.getKey())) {
+ rec.setMacro("");
+ } else {
+ rec.setMacro(macro);
+ }
+ rec.setComment(comment);
+
+ // This is final so it can be used in the errorDisplay Runnable
+ final ErrorTable failedTable = new ErrorTable(
+ ResourceStrings.getString("address_column"),
+ IPAddress.class);
+
+ /*
+ * For each address, create a client record and possibly a
+ * hosts record, log any errors for later consumption.
+ */
+ for (int i = 0; i < addressTableModel.getRowCount(); ++i) {
+ Address addr = addressTableModel.getAddressAt(i);
+ try {
+ rec.setClientIP(addr.addr);
+ rec.setClientName(addr.name);
+ server.addClient(rec, network.toString());
+ progress.update(i+1, addr.addr.toString());
+ } catch (InterruptedException e) {
+ SwingUtilities.invokeLater(finisher);
+ return;
+ } catch (Throwable e) {
+ // Pick the best message for the exception thrown
+ String msg;
+ if (e instanceof ExistsException) {
+ msg = ResourceStrings.getString("address_exists");
+ } else if (e instanceof HostExistsException) {
+ msg = ResourceStrings.getString("host_exists");
+ } else {
+ msg = e.getMessage();
+ }
+ failedTable.addError(addr.addr, msg);
+ }
+ }
+
+ // If any errors occurred, display them all at once.
+ if (!failedTable.isEmpty()) {
+ Runnable errorDisplay = new Runnable() {
+ public void run() {
+ Object [] objs = new Object[2];
+ objs[0] =
+ ResourceStrings.getString("add_wiz_error");
+ JScrollPane scrollPane =
+ new JScrollPane(failedTable);
+ // Resize the table to something kind of small
+ Dimension d =
+ failedTable.
+ getPreferredScrollableViewportSize();
+ d.height = 80;
+ failedTable.setPreferredScrollableViewportSize(d);
+ objs[1] = scrollPane;
+ JOptionPane.showMessageDialog(AddressWizard.this,
+ objs,
+ ResourceStrings.getString("server_error_title"),
+ JOptionPane.ERROR_MESSAGE);
+ }
+ };
+ try {
+ SwingUtilities.invokeAndWait(errorDisplay);
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+ }
+ SwingUtilities.invokeLater(finisher);
+ }
+ };
+ addThread.start();
+ }
+
+ protected void reallyFinish() {
+ super.doFinish();
+ }
+
+ public void doHelp() {
+ DhcpmgrApplet.showHelp("address_wizard");
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ConfigWizard.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ConfigWizard.java
new file mode 100644
index 0000000000..04d87387cb
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ConfigWizard.java
@@ -0,0 +1,1696 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.client;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+import java.text.MessageFormat;
+import java.net.*;
+
+import javax.swing.*;
+import javax.swing.table.*;
+import javax.swing.event.*;
+import javax.swing.border.*;
+
+import com.sun.dhcpmgr.ui.*;
+import com.sun.dhcpmgr.data.*;
+import com.sun.dhcpmgr.server.*;
+import com.sun.dhcpmgr.bridge.BridgeException;
+import com.sun.dhcpmgr.bridge.ExistsException;
+
+/**
+ * This wizard configures the DHCP service. It also has a mode switch so
+ * that it is also usable for just adding a single network, so that in
+ * the tool it actually performs the Network Wizard function as well.
+ */
+public class ConfigWizard extends DSWizard {
+ private boolean fullConfig;
+ private DhcpServiceMgr server;
+ private int leaseLength = 3600*24;
+ private boolean leaseNegotiable = true;
+ private String dnsDomain;
+ private Vector dnsServs;
+ private Network network;
+ private boolean isLan = true;
+ private boolean routerDiscovery = true;
+ private IPAddress router = null;
+ private String nisDomain;
+ private Vector nisServs;
+ private String nisplusDomain;
+ private Vector nisplusServs;
+ private static final String [] unitChoices = {
+ ResourceStrings.getString("cfg_wiz_hours"),
+ ResourceStrings.getString("cfg_wiz_days"),
+ ResourceStrings.getString("cfg_wiz_weeks") };
+ private static final int [] unitMultiples = { 60*60, 24*60*60, 7*24*60*60 };
+ private HostResource hostResource = null;
+
+ /**
+ * This class defines a host resource component.
+ */
+ private class HostResource extends Box {
+
+ /**
+ * The host resource(eg., files, nisplus, dns).
+ */
+ private String resource = null;
+
+ /**
+ * The description of the resource.
+ */
+ private String description = null;
+
+ /**
+ * The button for the resource.
+ */
+ private HostButton hostButton = null;
+
+ /**
+ * The domain field for the resource (if any)
+ */
+ private NoSpaceField domainField = null;
+
+ /**
+ * The constructor.
+ * @param resource the resource value for the config file
+ * @param description description of the resource
+ * @param defaultdomain default domain (if any) for the resource
+ * @param enabled determines whether resource is selectable
+ */
+ public HostResource(String resource, String description,
+ String defaultDomain, String domainDescription, boolean enabled) {
+
+ super(BoxLayout.X_AXIS);
+
+ this.resource = resource;
+ this.description = description;
+
+ // Every host resource needs a button even if the resource
+ // isn't one that will be selectable.
+ //
+ hostButton = new HostButton(this, false);
+ hostButton.setAlignmentX(Component.LEFT_ALIGNMENT);
+ add(hostButton);
+ if (!enabled) {
+ hostButton.setEnabled(false);
+ defaultDomain = new String();
+ }
+
+ // If the defaultDomain is null, then the host resource
+ // does not require a domain. Otherwise, the resource
+ // must have a text field so that the user can supply
+ // a domain.
+ //
+ if (defaultDomain != null) {
+ add(Box.createHorizontalStrut(20));
+
+ Box domainBox = Box.createHorizontalBox();
+
+ JLabel label = new JLabel(domainDescription);
+ label.setForeground(Color.black);
+ domainBox.add(label);
+
+ domainField = new NoSpaceField(defaultDomain, 10);
+ domainField.setEnabled(false);
+ domainField.setMaximumSize(domainField.getPreferredSize());
+
+ label.setLabelFor(domainField);
+ domainBox.add(domainField);
+ label.setToolTipText(description);
+
+ add(domainBox);
+
+ if (!enabled) {
+ domainField.setEditable(false);
+ label.setEnabled(false);
+ } else {
+ // Disable the forward button if domain empty.
+ DocumentListener listener = new DocumentListener() {
+ public void insertUpdate(DocumentEvent e) {
+ setForwardEnabled(
+ domainField.getText().length() != 0);
+ }
+ public void changedUpdate(DocumentEvent e) {
+ insertUpdate(e);
+ }
+ public void removeUpdate(DocumentEvent e) {
+ insertUpdate(e);
+ }
+ };
+ domainField.getDocument().addDocumentListener(listener);
+ }
+
+ }
+
+ } // constructor
+
+ /**
+ * Sets or unsets the host resource.
+ * @param isSelected if true, sets the resource, else unsets it
+ */
+ public void setSelected(boolean isSelected) {
+ if (isSelected) {
+ setHostResource(this);
+ if (!hostButton.isSelected()) {
+ hostButton.setSelected(true);
+ }
+ if (domainField != null) {
+ domainField.setEnabled(true);
+ setForwardEnabled(domainField.getText().length() != 0);
+ } else {
+ setForwardEnabled(true);
+ }
+ } else {
+ if (domainField != null) {
+ domainField.setEnabled(false);
+ }
+ }
+ } // setSelected
+
+ /**
+ * Returns the host resource.
+ * @return the host resource.
+ */
+ public String getResource() {
+ return resource;
+ } // getResource
+
+ /**
+ * Returns the resource description.
+ * @return the resource description.
+ */
+ public String getDescription() {
+ return description;
+ } // getDescription
+
+ /**
+ * Returns the domain for this component.
+ * @return the domain for this component.
+ */
+ public String getDomain() {
+ if (domainField == null) {
+ return null;
+ } else {
+ return domainField.getText();
+ }
+ } // getDomain
+
+ /**
+ * Returns the HostButton contained in this component.
+ * @return the HostButton contained in this component.
+ */
+ public HostButton getHostButton() {
+ return hostButton;
+ } // getHostButton
+
+ } // hostResource
+
+ /**
+ * This class maps a radio button to its HostResource
+ */
+ private class HostButton extends JRadioButton {
+
+ /**
+ * The HostResource to link to the radio button.
+ */
+ private HostResource hostResource = null;
+
+ /**
+ * Constructs a HostButton from a HostResource and determines
+ * whether the button should be selected using the boolean argument.
+ * @param hostResource the HostResource to map to the radio button.
+ * @param selected select the radio button?
+ */
+ public HostButton(HostResource hostResource, boolean selected) {
+ super(hostResource.getDescription(), selected);
+ this.hostResource = hostResource;
+ } // constructor
+
+ /**
+ * Returns the HostResource mapped to the radio button.
+ * @return the HostResource mapped to the radio button.
+ */
+ public HostResource getHostResource() {
+ return hostResource;
+ } // getHostResource
+
+ } // HostButton
+
+ // Select where host data will be stored.
+ class HostDataStep implements WizardStep {
+
+ /**
+ * The component provided to the wizard.
+ */
+ private Box stepBox;
+
+ /**
+ * The basic constructor for the wizard step.
+ */
+ public HostDataStep() {
+
+ stepBox = Box.createVerticalBox();
+
+ // Explanatory step text
+ //
+ JComponent c = Wizard.createTextArea(
+ ResourceStrings.getString("cfg_wiz_host_explain"), 2, 45);
+ c.setAlignmentX(Component.LEFT_ALIGNMENT);
+ stepBox.add(c);
+ stepBox.add(Box.createVerticalStrut(5));
+
+ // Create button listener, that will set the selected
+ // host resource when the button is selected.
+ //
+ ChangeListener buttonListener = new ChangeListener() {
+ public void stateChanged(ChangeEvent e) {
+ HostButton button = (HostButton)e.getSource();
+ HostResource hostResource = button.getHostResource();
+ hostResource.setSelected(button.isSelected());
+ }
+ };
+
+ // Create panel that will contain the buttons.
+ //
+ JPanel boxPanel = new JPanel();
+ boxPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
+ boxPanel.setLayout(new GridLayout(4, 1));
+
+ // List the host resource choices.
+ //
+ ButtonGroup buttonGroup = new ButtonGroup();
+
+ // The "do not manage hosts" option.
+ //
+ String hostDescription =
+ ResourceStrings.getString("cfg_wiz_no_host_management");
+ HostResource hostResource = new HostResource(null,
+ hostDescription, null, null, true);
+ HostButton hbMgt = hostResource.getHostButton();
+ hbMgt.setToolTipText(hostDescription);
+ hostResource.setSelected(true);
+ hostResource.getHostButton().addChangeListener(buttonListener);
+ buttonGroup.add(hostResource.getHostButton());
+ boxPanel.add(hostResource);
+
+ // The "files" option.
+ //
+ hostDescription =
+ ResourceStrings.getString("cfg_wiz_files");
+ hostResource = new HostResource(DhcpConfigOpts.DSVC_CV_FILES,
+ hostDescription, null, null, true);
+ HostButton hb = hostResource.getHostButton();
+ hb.setToolTipText(hostDescription);
+ hostResource.getHostButton().addChangeListener(buttonListener);
+ buttonGroup.add(hostResource.getHostButton());
+ boxPanel.add(hostResource);
+
+ // The "nisplus" option. Only enabled if it can be managed
+ // from the selected server.
+ //
+ String domainDefault = null;
+ boolean enabled = false;
+ try {
+ // If we can obtain a server list, then nisplus is enabled.
+ server.getIPOption(StandardOptions.CD_NISPLUS_SERVS, "");
+ enabled = true;
+ domainDefault = server.getStringOption(
+ StandardOptions.CD_NISPLUS_DMAIN, "");
+ } catch (Throwable e) {
+ domainDefault = new String();
+ }
+
+ hostDescription =
+ ResourceStrings.getString("cfg_wiz_nisplus");
+ String domainDescription =
+ ResourceStrings.getString("cfg_wiz_domain") + " ";
+ hostResource = new HostResource(DhcpConfigOpts.DSVC_CV_NISPLUS,
+ hostDescription, domainDefault, domainDescription, enabled);
+ HostButton hbNISPlus = hostResource.getHostButton();
+ hbNISPlus.setToolTipText(hostDescription);
+ hostResource.getHostButton().addChangeListener(buttonListener);
+ buttonGroup.add(hostResource.getHostButton());
+ boxPanel.add(hostResource);
+
+ // The "dns" option. Only enabled if it can be managed
+ // from the selected server.
+ //
+ try {
+ domainDefault =
+ server.getStringOption(StandardOptions.CD_DNSDOMAIN, "");
+ } catch (Throwable e) {
+ domainDefault = new String();
+ }
+
+ try {
+ enabled =
+ server.isHostsValid(DhcpConfigOpts.DSVC_CV_DNS, "");
+ } catch (Throwable e) {
+ enabled = false;
+ }
+
+ hostDescription =
+ ResourceStrings.getString("cfg_wiz_dns");
+
+ hostResource = new HostResource(DhcpConfigOpts.DSVC_CV_DNS,
+ hostDescription, domainDefault, domainDescription, enabled);
+ HostButton hbDNS = hostResource.getHostButton();
+ hbDNS.setToolTipText(hostDescription);
+ hostResource.getHostButton().addChangeListener(buttonListener);
+ buttonGroup.add(hostResource.getHostButton());
+ boxPanel.add(hostResource);
+
+ // Add the panel to the stepBox component.
+ //
+ stepBox.add(boxPanel);
+ stepBox.add(Box.createVerticalStrut(10));
+ stepBox.add(Box.createVerticalGlue());
+
+ } // constructor
+
+ public String getDescription() {
+ return ResourceStrings.getString("cfg_wiz_hostdata_desc");
+ } // getDescription
+
+ public Component getComponent() {
+ return stepBox;
+ } // getComponent
+
+ public void setActive(int direction) {
+ setForwardEnabled(true);
+ } // setActive
+
+ public boolean setInactive(int direction) {
+
+ // If moving forward, validate that the host resource/domain
+ // input by the user is manageable from the selected server.
+ //
+ boolean valid = true;
+ if (direction == FORWARD) {
+ String resource = getHostResource().getResource();
+ String domain = getHostResource().getDomain();
+ if (resource != null) {
+ try {
+ valid = server.isHostsValid(resource, domain);
+ } catch (Throwable e) {
+ valid = false;
+ }
+ }
+ if (!valid) {
+ JOptionPane.showMessageDialog(ConfigWizard.this,
+ ResourceStrings.getString("cfg_wiz_invalid_host"),
+ ResourceStrings.getString("input_error"),
+ JOptionPane.ERROR_MESSAGE);
+ }
+ }
+ return (valid);
+ } // setInactive
+
+ } // HostDataStep
+
+ // This step specifies lease length and renewal policies for the server
+ class LeaseStep implements WizardStep {
+ private IntegerField length;
+ private JComboBox units;
+ private JCheckBox negotiable;
+ private Box stepBox;
+
+ public LeaseStep() {
+ stepBox = Box.createVerticalBox();
+
+ // Explanatory text
+ stepBox.add(Wizard.createTextArea(
+ ResourceStrings.getString("cfg_wiz_lease_explain"), 3, 45));
+
+ // Need to input a number together with units
+ JPanel flowPanel = new JPanel();
+
+ Mnemonic mnLease =
+ new Mnemonic(ResourceStrings.getString("cfg_wiz_lease_length"));
+ JLabel lblLeaseLen = new JLabel(
+ mnLease.getString());
+
+ flowPanel.add(lblLeaseLen);
+
+ // Use a box for the value and units to keep together in layout
+ Box leaseBox = Box.createHorizontalBox();
+ length = new IntegerField();
+ leaseBox.add(length);
+ leaseBox.add(Box.createHorizontalStrut(5));
+
+ lblLeaseLen.setLabelFor(length);
+ lblLeaseLen.setToolTipText(mnLease.getString());
+ lblLeaseLen.setDisplayedMnemonic(mnLease.getMnemonic());
+
+ units = new JComboBox(unitChoices);
+ leaseBox.add(units);
+ flowPanel.add(leaseBox);
+ stepBox.add(flowPanel);
+ stepBox.add(Box.createVerticalStrut(10));
+
+ // Explain negotiable, provide selection for it
+ stepBox.add(Wizard.createTextArea(
+ ResourceStrings.getString("cfg_wiz_negotiable_explain"), 6,
+ 45));
+
+ negotiable = new JCheckBox(
+ ResourceStrings.getString("cfg_wiz_negotiable"), true);
+ negotiable.setToolTipText(
+ ResourceStrings.getString("cfg_wiz_negotiable"));
+ negotiable.setAlignmentX(Component.CENTER_ALIGNMENT);
+ stepBox.add(negotiable);
+ stepBox.add(Box.createVerticalGlue());
+ }
+
+ public String getDescription() {
+ return ResourceStrings.getString("cfg_wiz_lease_desc");
+ }
+
+ public Component getComponent() {
+ return stepBox;
+ }
+
+ public void setActive(int direction) {
+ setForwardEnabled(true);
+ // Set the units field to the maximum unit this value expresses
+ int lengthVal = 0;
+ int i;
+ for (i = unitMultiples.length - 1; i >= 0; --i) {
+ lengthVal = leaseLength / unitMultiples[i];
+ if (lengthVal != 0) {
+ if (leaseLength % unitMultiples[i] == 0) {
+ break;
+ }
+ }
+ }
+ if (i == -1) {
+ i = 0;
+ }
+ units.setSelectedIndex(i);
+ length.setValue(lengthVal);
+ negotiable.setSelected(leaseNegotiable);
+ }
+
+ public boolean setInactive(int direction) {
+ // Leases cannot be zero length
+ long lease = (long)length.getValue();
+ if (lease == 0) {
+ JOptionPane.showMessageDialog(ConfigWizard.this,
+ ResourceStrings.getString("cfg_wiz_zero_lease"),
+ ResourceStrings.getString("input_error"),
+ JOptionPane.ERROR_MESSAGE);
+ return false;
+ }
+ int multiplier = unitMultiples[units.getSelectedIndex()];
+ lease *= multiplier;
+ if (lease > Integer.MAX_VALUE) {
+ // Value is too large
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString("cfg_wiz_lease_overflow"));
+ Object args = new Object[] {
+ new Integer(Integer.MAX_VALUE / multiplier),
+ units.getSelectedItem()
+ };
+ JOptionPane.showMessageDialog(ConfigWizard.this,
+ form.format(args), ResourceStrings.getString("input_error"),
+ JOptionPane.ERROR_MESSAGE);
+ return false;
+ }
+ leaseLength = (int)lease;
+ leaseNegotiable = negotiable.isSelected();
+ return true;
+ }
+ }
+
+ // Step to configure DNS
+ class DnsStep implements WizardStep {
+ private NoSpaceField domain;
+ private IPAddressList serverList;
+ private Box stepBox;
+ private boolean firstActive = true;
+
+ public DnsStep() {
+ stepBox = Box.createVerticalBox();
+
+ // Explanatory text
+ stepBox.add(Wizard.createTextArea(
+ ResourceStrings.getString("cfg_wiz_dns_explain"), 5, 45));
+ stepBox.add(Box.createVerticalStrut(10));
+
+ // Domain first
+ JPanel fieldPanel = new JPanel(new FieldLayout());
+
+ Mnemonic mnDNS =
+ new Mnemonic(ResourceStrings.getString("cfg_wiz_dns_domain"));
+ JLabel jlDNSDomain = new JLabel(mnDNS.getString());
+ fieldPanel.add(FieldLayout.LABEL, jlDNSDomain);
+
+ domain = new NoSpaceField();
+ jlDNSDomain.setLabelFor(domain);
+ jlDNSDomain.setToolTipText(mnDNS.getString());
+ jlDNSDomain.setDisplayedMnemonic(mnDNS.getMnemonic());
+ fieldPanel.add(FieldLayout.FIELD, domain);
+ stepBox.add(fieldPanel);
+
+ serverList = new IPAddressList();
+ Border tb = BorderFactory.createTitledBorder(
+ BorderFactory.createLineBorder(Color.black),
+ ResourceStrings.getString("cfg_wiz_dns_servers"));
+ serverList.setBorder(BorderFactory.createCompoundBorder(tb,
+ BorderFactory.createEmptyBorder(5, 5, 5, 5)));
+ stepBox.add(serverList);
+ }
+
+ public String getDescription() {
+ return ResourceStrings.getString("cfg_wiz_dns_desc");
+ }
+
+ public Component getComponent() {
+ return stepBox;
+ }
+
+ public void setActive(int direction) {
+ setForwardEnabled(true);
+
+ // First time through, ask the server for the defaults
+ if (firstActive) {
+ firstActive = false;
+ try {
+ domain.setText(
+ server.getStringOption(StandardOptions.CD_DNSDOMAIN,
+ ""));
+ serverList.setAddressList(
+ server.getIPOption(StandardOptions.CD_DNSSERV, ""));
+ } catch (Throwable e) {
+ // Ignore errors, we're just supplying defaults
+ }
+ }
+ }
+
+ public boolean setInactive(int direction) {
+ if (direction == FORWARD) {
+ /*
+ * Either must supply both a domain and a list of servers, or
+ * neither
+ */
+ if ((domain.getText().length() == 0)
+ != (serverList.getListSize() == 0)) {
+ JOptionPane.showMessageDialog(ConfigWizard.this,
+ ResourceStrings.getString("cfg_wiz_dns_both"),
+ ResourceStrings.getString("input_error"),
+ JOptionPane.ERROR_MESSAGE);
+ return false;
+ }
+ }
+ dnsDomain = domain.getText();
+ dnsServs = serverList.getAddressList();
+ return true;
+ }
+ }
+
+ // Select the network to configure
+ class NetworkStep implements WizardStep {
+ private JComboBox networkBox;
+ private NetworkListModel networkListModel;
+ private IPAddressField mask;
+ private Box stepBox;
+ private boolean firstActive = true;
+ private Hashtable maskTable;
+
+ // Model for the list of networks
+ class NetworkListModel extends AbstractListModel
+ implements ComboBoxModel {
+ private Object currentValue;
+ private String [] data = null;
+
+ public int getSize() {
+ if (data == null) {
+ return 0;
+ } else {
+ return data.length;
+ }
+ }
+
+ public Object getElementAt(int index) {
+ if (data == null) {
+ return null;
+ } else {
+ return data[index];
+ }
+ }
+
+ public void setSelectedItem(Object anItem) {
+ currentValue = anItem;
+ fireContentsChanged(this, -1, -1);
+ }
+
+ public Object getSelectedItem() {
+ return currentValue;
+ }
+
+ public void setData(Vector addrs) {
+ data = new String[addrs.size()];
+ addrs.copyInto(data);
+ fireContentsChanged(this, 0, data.length);
+ }
+ }
+
+ /*
+ * Editor for the Network combo box, ensures that a valid IP address
+ * is entered. This implementation cribbed from Swing's
+ * BasicComboBoxEditor in plaf/basic
+ */
+ class NetworkComboBoxEditor implements ComboBoxEditor, FocusListener {
+ private IPAddressField editor;
+
+ public NetworkComboBoxEditor() {
+ editor = new IPAddressField();
+ editor.addFocusListener(this);
+ }
+
+ public Component getEditorComponent() {
+ return editor;
+ }
+
+ public void setItem(Object obj) {
+ if (obj != null) {
+ editor.setText((String)obj);
+ } else {
+ editor.setText("");
+ }
+ }
+
+ public Object getItem() {
+ return editor.getText();
+ }
+
+ public void selectAll() {
+ editor.selectAll();
+ editor.requestFocus();
+ }
+
+ public void focusGained(FocusEvent e) {
+ }
+
+ public void focusLost(FocusEvent e) {
+ }
+
+ public void addActionListener(ActionListener l) {
+ editor.addActionListener(l);
+ }
+
+ public void removeActionListener(ActionListener l) {
+ editor.removeActionListener(l);
+ }
+ }
+
+ public NetworkStep() {
+ stepBox = Box.createVerticalBox();
+
+ // Start with intro text, depending on mode.
+ if (fullConfig) {
+ stepBox.add(Wizard.createTextArea(
+ ResourceStrings.getString("cfg_wiz_network_explain"), 4,
+ 45));
+ } else {
+ stepBox.add(Wizard.createTextArea(
+ ResourceStrings.getString("net_wiz_net_explain"), 6, 45));
+ }
+ stepBox.add(Box.createVerticalStrut(10));
+
+ JPanel panel = new JPanel(new FieldLayout());
+ Mnemonic mnAddr =
+ new Mnemonic(ResourceStrings.getString("cfg_wiz_network"));
+ JLabel jlNetworkAddr = new JLabel(mnAddr.getString());
+ panel.add(FieldLayout.LABEL, jlNetworkAddr);
+ networkListModel = new NetworkListModel();
+ networkBox = new JComboBox(networkListModel);
+ networkBox.setEditable(true);
+ networkBox.setEditor(new NetworkComboBoxEditor());
+ panel.add(FieldLayout.FIELD, networkBox);
+ jlNetworkAddr.setLabelFor(networkBox);
+ jlNetworkAddr.setToolTipText(mnAddr.getString());
+ jlNetworkAddr.setDisplayedMnemonic(mnAddr.getMnemonic());
+
+ // Label and text field for subnet mask
+ Mnemonic mnMask =
+ new Mnemonic(ResourceStrings.getString("cfg_wiz_mask"));
+ JLabel addrLbl =
+ new JLabel(mnMask.getString());
+ addrLbl.setToolTipText(mnMask.getString());
+ panel.add(FieldLayout.LABEL, addrLbl);
+ mask = new IPAddressField();
+ addrLbl.setLabelFor(mask);
+ addrLbl.setDisplayedMnemonic(mnMask.getMnemonic());
+
+ panel.add(FieldLayout.FIELD, mask);
+ stepBox.add(panel);
+
+ stepBox.add(Box.createVerticalStrut(10));
+
+ if (fullConfig) {
+ stepBox.add(Wizard.createTextArea(
+ ResourceStrings.getString("cfg_wiz_network_explainmore"), 4,
+ 45));
+ }
+ stepBox.add(Box.createVerticalGlue());
+
+ /*
+ * Listen to selection changes on the network box and change the
+ * netmask accordingly.
+ */
+ networkBox.addItemListener(new ItemListener() {
+ public void itemStateChanged(ItemEvent e) {
+ if (e.getStateChange() == ItemEvent.SELECTED) {
+ String s = (String)e.getItem();
+ IPAddress a = (IPAddress)maskTable.get(s);
+ if (a != null) {
+ // We know the correct value, so set it
+ mask.setValue(a);
+ }
+ }
+ }
+ });
+ }
+
+ public String getDescription() {
+ return ResourceStrings.getString("cfg_wiz_network_desc");
+ }
+
+ public Component getComponent() {
+ return stepBox;
+ }
+
+ public void setActive(int direction) {
+ setForwardEnabled(true);
+ if (firstActive) {
+ firstActive = false;
+ maskTable = new Hashtable();
+ try {
+ /*
+ * Initialize list to all networks directly attached to
+ * the server
+ */
+ IPInterface[] ifs = new IPInterface[0];
+ try {
+ ifs = server.getInterfaces();
+ } catch (BridgeException e) {
+ // we're not configured yet, apparently
+ ifs = null;
+ }
+ Vector addrs = new Vector();
+
+ // Get list of already-configured networks
+ Network [] nets = new Network[0];
+ try {
+ nets = DataManager.get().getNetworks(true);
+ } catch (BridgeException e) {
+ // Ignore; we're not configured yet, apparently
+ }
+ /*
+ * Now filter the list so only unconfigured networks
+ * show up in the selection list.
+ */
+ if (ifs != null) {
+ for (int i = 0; i < ifs.length; ++i) {
+ boolean alreadyConfigured = false;
+ for (int j = 0; j < nets.length; ++j) {
+ if (ifs[i].getNetwork().equals(nets[j])) {
+ alreadyConfigured = true;
+ break;
+ }
+ }
+ if (!alreadyConfigured) {
+ // Add to list
+ String s = ifs[i].getNetwork().
+ getNetworkNumber().getHostAddress();
+ addrs.addElement(s);
+ // Save netmask for retrieval later
+ maskTable.put(s, ifs[i].getNetwork().getMask());
+ }
+ }
+ }
+ networkListModel.setData(addrs);
+ if (networkBox.getItemCount() > 0) {
+ networkBox.setSelectedIndex(0);
+ }
+ } catch (Throwable e) {
+ // Do nothing, we're just setting defaults
+ e.printStackTrace();
+ }
+ }
+ }
+
+ public boolean setInactive(int direction) {
+ if (direction == FORWARD) {
+ try {
+ network = new Network((String)networkBox.getSelectedItem());
+ if (mask.getValue() == null) {
+ /*
+ * Check for empty, in which case we just let the
+ * default happen
+ */
+ if (mask.getText().length() != 0) {
+ // Not a valid subnet mask
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString("cfg_wiz_bad_mask"));
+ Object [] args = new Object[1];
+ args[0] = mask.getText();
+ JOptionPane.showMessageDialog(ConfigWizard.this,
+ form.format(args),
+ ResourceStrings.getString("input_error"),
+ JOptionPane.ERROR_MESSAGE);
+ return false;
+ }
+ } else {
+ network.setMask(mask.getValue());
+ }
+
+ // Check for network already configured, error if so
+ Network [] nets = new Network[0];
+ try {
+ nets = DataManager.get().getNetworks(false);
+ } catch (BridgeException e) {
+ // Ignore; must not be configured yet
+ }
+ for (int i = 0; i < nets.length; ++i) {
+ if (network.equals(nets[i])) {
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString(
+ "cfg_wiz_network_configured"));
+ Object [] args = new Object[1];
+ args[0] = network.getAddress().getHostAddress();
+ JOptionPane.showMessageDialog(ConfigWizard.this,
+ form.format(args),
+ ResourceStrings.getString("input_error"),
+ JOptionPane.ERROR_MESSAGE);
+ return false;
+ }
+ }
+ } catch (ValidationException e) {
+ // Not a valid IP address
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString("cfg_wiz_bad_network"));
+ Object [] args = new Object[1];
+ args[0] = (String)networkBox.getSelectedItem();
+ if (args[0] == null) {
+ args[0] = "";
+ }
+ JOptionPane.showMessageDialog(ConfigWizard.this,
+ form.format(args),
+ ResourceStrings.getString("input_error"),
+ JOptionPane.ERROR_MESSAGE);
+ return false;
+ } catch (Throwable e) {
+ e.printStackTrace();
+ // Ignore other exceptions
+ }
+ }
+ return true;
+ }
+ }
+
+ // Get the type of network and routing policy
+ class NetTypeStep implements WizardStep {
+ private JRadioButton lan, ptp;
+ private ButtonGroup typeGroup, routingGroup;
+ private JRadioButton discover, specify;
+ private IPAddressField address;
+ private Box stepBox;
+ private boolean firstTime = true;
+
+ public NetTypeStep() {
+ stepBox = Box.createVerticalBox();
+
+ // Explanatory text at the top
+ stepBox.add(Wizard.createTextArea(
+ ResourceStrings.getString("cfg_wiz_nettype_explain"), 2, 45));
+ stepBox.add(Box.createVerticalStrut(10));
+
+ // Label and radio buttons for type of network
+ JPanel panel = new JPanel(new GridLayout(2, 1));
+ /*
+ * Create a compound border with empty space on the outside and
+ * a line border on the inside, then title it amd put a space
+ * around the outside.
+ */
+ Border b = BorderFactory.createCompoundBorder(
+ BorderFactory.createEmptyBorder(0, 5, 0, 5),
+ BorderFactory.createLineBorder(Color.black));
+ Border tb = BorderFactory.createTitledBorder(b,
+ ResourceStrings.getString("cfg_wiz_nettype_label"));
+ panel.setBorder(BorderFactory.createCompoundBorder(tb,
+ BorderFactory.createEmptyBorder(0, 5, 0, 5)));
+
+ lan = new JRadioButton(ResourceStrings.getString("cfg_wiz_lan"),
+ true);
+ lan.setToolTipText(ResourceStrings.getString("cfg_wiz_lan"));
+ typeGroup = new ButtonGroup();
+ typeGroup.add(lan);
+ panel.add(lan);
+ ptp = new JRadioButton(ResourceStrings.getString("cfg_wiz_point"),
+ false);
+ ptp.setToolTipText(ResourceStrings.getString("cfg_wiz_point"));
+ typeGroup.add(ptp);
+ panel.add(ptp);
+ stepBox.add(panel);
+ stepBox.add(Box.createVerticalStrut(20));
+
+ // Routing policy
+ panel = new JPanel(new GridLayout(2, 1));
+ tb = BorderFactory.createTitledBorder(b,
+ ResourceStrings.getString("cfg_wiz_routing_label"));
+ panel.setBorder(BorderFactory.createCompoundBorder(tb,
+ BorderFactory.createEmptyBorder(0, 5, 0, 5)));
+
+ discover = new JRadioButton(
+ ResourceStrings.getString("cfg_wiz_router_discovery"), true);
+ discover.setToolTipText(ResourceStrings.getString(
+ "cfg_wiz_router_discovery"));
+ routingGroup = new ButtonGroup();
+ routingGroup.add(discover);
+ panel.add(discover);
+
+ Box routerBox = Box.createHorizontalBox();
+ specify = new JRadioButton(
+ ResourceStrings.getString("cfg_wiz_router_specify"), false);
+ specify.setToolTipText(ResourceStrings.getString(
+ "cfg_wiz_router_specify"));
+ routingGroup.add(specify);
+ routerBox.add(specify);
+ routerBox.add(Box.createHorizontalStrut(2));
+ address = new IPAddressField();
+ address.setEnabled(false); // Start off disabled
+ address.setMaximumSize(address.getPreferredSize());
+
+ // Box is sensitive to alignment, make sure they all agree
+ address.setAlignmentY(specify.getAlignmentY());
+
+ routerBox.add(address);
+ panel.add(routerBox);
+ stepBox.add(panel);
+
+ stepBox.add(Box.createVerticalStrut(10));
+ stepBox.add(Box.createVerticalGlue());
+
+ /*
+ * Enable forward if router discovery, or if specifying router and
+ * address is not empty.
+ */
+ specify.addChangeListener(new ChangeListener() {
+ public void stateChanged(ChangeEvent e) {
+ address.setEnabled(specify.isSelected());
+ setForwardEnabled(!specify.isSelected()
+ || (address.getText().length() != 0));
+ }
+ });
+
+ // Enable forward when address is not empty.
+ address.getDocument().addDocumentListener(new DocumentListener() {
+ public void insertUpdate(DocumentEvent e) {
+ setForwardEnabled(address.getText().length() != 0);
+ }
+ public void changedUpdate(DocumentEvent e) {
+ insertUpdate(e);
+ }
+ public void removeUpdate(DocumentEvent e) {
+ insertUpdate(e);
+ }
+ });
+ }
+
+ public String getDescription() {
+ return ResourceStrings.getString("cfg_wiz_nettype_desc");
+ }
+
+ public Component getComponent() {
+ return stepBox;
+ }
+
+ public void setActive(int direction) {
+ setForwardEnabled(true);
+ lan.setSelected(isLan);
+ discover.setSelected(routerDiscovery);
+ address.setValue(router);
+ }
+
+ public boolean setInactive(int direction) {
+ isLan = lan.isSelected();
+ if (direction == FORWARD) {
+ routerDiscovery = discover.isSelected();
+ if (!routerDiscovery) {
+ IPAddress addr = address.getValue();
+ if (addr == null) {
+ // Invalid IP address
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString(
+ "cfg_wiz_router_addr_err"));
+ Object [] args = new Object[1];
+ args[0] = address.getText();
+ JOptionPane.showMessageDialog(ConfigWizard.this,
+ form.format(args),
+ ResourceStrings.getString("input_error"),
+ JOptionPane.ERROR_MESSAGE);
+ return false;
+ } else if (!network.containsAddress(addr)) {
+ // Router is not on the network we're configuring
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString(
+ "cfg_wiz_router_net_err"));
+ Object [] args = new Object[2];
+ args[0] = address.getText();
+ args[1] = network.toString();
+ JOptionPane.showMessageDialog(ConfigWizard.this,
+ form.format(args),
+ ResourceStrings.getString("input_error"),
+ JOptionPane.ERROR_MESSAGE);
+ return false;
+ }
+ router = addr;
+ }
+ }
+ return true;
+ }
+ }
+
+ // Get the NIS configuration
+ class NisStep implements WizardStep {
+ private NoSpaceField domain;
+ private Box stepBox;
+ private IPAddressField address;
+ private JButton add, delete, moveUp, moveDown;
+ private IPAddressList serverList;
+ boolean firstActive = true;
+
+ public NisStep() {
+ stepBox = Box.createVerticalBox();
+
+ stepBox.add(Wizard.createTextArea(
+ ResourceStrings.getString("cfg_wiz_nis_explain"), 6, 45));
+ stepBox.add(Box.createVerticalStrut(10));
+
+ JPanel fieldPanel = new JPanel(new FieldLayout());
+ Mnemonic mnNis =
+ new Mnemonic(ResourceStrings.getString("cfg_wiz_nis_domain"));
+ JLabel jlNISDomain =
+ new JLabel(mnNis.getString());
+ fieldPanel.add(FieldLayout.LABEL, jlNISDomain);
+ domain = new NoSpaceField();
+ jlNISDomain.setLabelFor(domain);
+ jlNISDomain.setToolTipText(mnNis.getString());
+ jlNISDomain.setDisplayedMnemonic(mnNis.getMnemonic());
+ fieldPanel.add(FieldLayout.FIELD, domain);
+ stepBox.add(fieldPanel);
+
+ serverList = new IPAddressList();
+ Border tb = BorderFactory.createTitledBorder(
+ BorderFactory.createLineBorder(Color.black),
+ ResourceStrings.getString("cfg_wiz_nis_servers"));
+ serverList.setBorder(BorderFactory.createCompoundBorder(tb,
+ BorderFactory.createEmptyBorder(5, 5, 5, 5)));
+ stepBox.add(serverList);
+ }
+
+ public String getDescription() {
+ return ResourceStrings.getString("cfg_wiz_nis_desc");
+ }
+
+ public Component getComponent() {
+ return stepBox;
+ }
+
+ public void setActive(int direction) {
+ setForwardEnabled(true);
+ if (firstActive) {
+ firstActive = false;
+ try {
+ /*
+ * Order here is important; do the servers first because if
+ * there's an error, we don't retrieve a domain name, which
+ * appears to never fail.
+ */
+ serverList.setAddressList(
+ server.getIPOption(StandardOptions.CD_NIS_SERV, ""));
+ domain.setText(
+ server.getStringOption(StandardOptions.CD_NIS_DOMAIN,
+ ""));
+ } catch (Throwable e) {
+ // Do nothing, just setting defaults
+ }
+ }
+ }
+
+ public boolean setInactive(int direction) {
+ if (direction == FORWARD) {
+ /*
+ * Either must supply both a domain and a list of servers, or
+ * neither
+ */
+ if ((domain.getText().length() == 0)
+ != (serverList.getListSize() == 0)) {
+ JOptionPane.showMessageDialog(ConfigWizard.this,
+ ResourceStrings.getString("cfg_wiz_nis_both"),
+ ResourceStrings.getString("input_error"),
+ JOptionPane.ERROR_MESSAGE);
+ return false;
+ }
+ }
+ nisDomain = domain.getText();
+ nisServs = serverList.getAddressList();
+ return true;
+ }
+ }
+
+ // Get the NIS+ configuration
+ class NisplusStep implements WizardStep {
+ private NoSpaceField domain;
+ private IPAddressList serverList;
+ private Box stepBox;
+ boolean firstActive = true;
+
+ public NisplusStep() {
+ stepBox = Box.createVerticalBox();
+
+ stepBox.add(Wizard.createTextArea(
+ ResourceStrings.getString("cfg_wiz_nisplus_explain"), 6, 45));
+ stepBox.add(Box.createVerticalStrut(10));
+
+ JPanel fieldPanel = new JPanel(new FieldLayout());
+ Mnemonic mnNisPlus =
+ new Mnemonic(ResourceStrings.getString(
+ "cfg_wiz_nisplus_domain"));
+ JLabel lblNisPlus = new JLabel(mnNisPlus.getString());
+ fieldPanel.add(FieldLayout.LABEL, lblNisPlus);
+ domain = new NoSpaceField();
+ fieldPanel.add(FieldLayout.FIELD, domain);
+ stepBox.add(fieldPanel);
+ lblNisPlus.setLabelFor(domain);
+ lblNisPlus.setToolTipText(mnNisPlus.getString());
+ lblNisPlus.setDisplayedMnemonic(mnNisPlus.getMnemonic());
+
+ serverList = new IPAddressList();
+ Border tb = BorderFactory.createTitledBorder(
+ BorderFactory.createLineBorder(Color.black),
+ ResourceStrings.getString("cfg_wiz_nisplus_servers"));
+ serverList.setBorder(BorderFactory.createCompoundBorder(tb,
+ BorderFactory.createEmptyBorder(5, 5, 5, 5)));
+ stepBox.add(serverList);
+ }
+
+ public String getDescription() {
+ return ResourceStrings.getString("cfg_wiz_nisplus_desc");
+ }
+
+ public Component getComponent() {
+ return stepBox;
+ }
+
+ public void setActive(int direction) {
+ setForwardEnabled(true);
+ if (firstActive) {
+ firstActive = false;
+ try {
+ /*
+ * Order is important; the domain will always be returned
+ * even if NIS+ is not configured, so try finding a server
+ * first and if that fails we will end up with both fields
+ * empty.
+ */
+ serverList.setAddressList(
+ server.getIPOption(StandardOptions.CD_NISPLUS_SERVS,
+ ""));
+ domain.setText(server.getStringOption(
+ StandardOptions.CD_NISPLUS_DMAIN, ""));
+ } catch (Throwable e) {
+ // Do nothing, just setting defaults
+ }
+ }
+ }
+
+ public boolean setInactive(int direction) {
+ if (direction == FORWARD) {
+ /*
+ * Either must supply both a domain and a list of servers,
+ * or neither
+ */
+ if ((domain.getText().length() == 0)
+ != (serverList.getListSize() == 0)) {
+ JOptionPane.showMessageDialog(ConfigWizard.this,
+ ResourceStrings.getString("cfg_wiz_nisplus_both"),
+ ResourceStrings.getString("input_error"),
+ JOptionPane.ERROR_MESSAGE);
+ return false;
+ }
+ }
+ nisplusDomain = domain.getText();
+ nisplusServs = serverList.getAddressList();
+ return true;
+ }
+ }
+
+ class ReviewStep implements WizardStep {
+ private JLabel storeLabel;
+ private JLabel hostLabel;
+ private JLabel leaseLabel;
+ private JLabel networkLabel;
+ private JLabel netTypeLabel;
+ private JLabel netmaskLabel;
+ private JLabel routerLabel;
+ private JLabel dnsLabel;
+ private JLabel dnsServLabel;
+ private JLabel nisLabel;
+ private JLabel nisServLabel;
+ private JLabel nisplusLabel;
+ private JLabel nisplusServLabel;
+ private JPanel panel;
+ private JScrollPane scrollPane;
+
+ public ReviewStep() {
+ Box stepBox = Box.createVerticalBox();
+ if (fullConfig) {
+ stepBox.add(Wizard.createTextArea(
+ ResourceStrings.getString("cfg_wiz_review_explain"), 3,
+ 45));
+ } else {
+ stepBox.add(Wizard.createTextArea(
+ ResourceStrings.getString("net_wiz_review_explain"), 3,
+ 45));
+ }
+
+ panel = new JPanel(new FieldLayout());
+ JLabel jlTmp;
+
+ if (fullConfig) {
+ addLabel("cfg_wiz_datastore");
+ storeLabel = addField("uninitialized");
+
+ addLabel("cfg_wiz_hosts_resource");
+ hostLabel = addField("uninitialized");
+
+ jlTmp = addLabelMnemonic("cfg_wiz_lease_length");
+ leaseLabel = addField("1 day");
+
+ jlTmp = addLabelMnemonic("cfg_wiz_dns_domain");
+ dnsLabel = addField("Bar.Sun.COM");
+
+ addLabel("cfg_wiz_dns_servers");
+ dnsServLabel = addField("109.151.1.15, 109.148.144.2");
+ }
+
+ jlTmp = addLabelMnemonic("cfg_wiz_network");
+ networkLabel = addField("109.148.21.0");
+ jlTmp.setLabelFor(networkLabel);
+
+ jlTmp = addLabelMnemonic("cfg_wiz_mask");
+ netmaskLabel = addField("255.255.255.0");
+ jlTmp.setLabelFor(netmaskLabel);
+
+ addLabel("cfg_wiz_nettype");
+ netTypeLabel = addField(ResourceStrings.getString("cfg_wiz_lan"));
+
+ addLabel("cfg_wiz_router");
+ routerLabel = addField(
+ ResourceStrings.getString("cfg_wiz_router_discovery"));
+
+ jlTmp = addLabelMnemonic("cfg_wiz_nis_domain");
+ nisLabel = addField("Foo.Bar.Sun.COM");
+ jlTmp.setLabelFor(nisLabel);
+
+ addLabel("cfg_wiz_nis_servers");
+ nisServLabel = addField("109.148.21.21, 109.148.21.44");
+
+ jlTmp = addLabelMnemonic("cfg_wiz_nisplus_domain");
+ nisplusLabel = addField("spg.Foo.Bar.Sun.COM");
+ jlTmp.setLabelFor(nisplusLabel);
+
+ addLabel("cfg_wiz_nisplus_servers");
+ nisplusServLabel = addField("109.148.21.1");
+
+ stepBox.add(panel);
+ stepBox.add(Box.createVerticalGlue());
+
+ scrollPane = new JScrollPane(stepBox,
+ JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
+ JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
+ }
+
+ private void addLabel(String s) {
+ JLabel jl;
+ jl = new JLabel(ResourceStrings.getString(s));
+ panel.add(FieldLayout.LABEL, jl);
+ jl.setLabelFor(panel);
+ jl.setToolTipText(ResourceStrings.getString(s));
+ }
+
+ private JLabel addLabelMnemonic(String s) {
+ JLabel jl;
+ Mnemonic mnStr =
+ new Mnemonic(ResourceStrings.getString(s));
+ jl = new JLabel(mnStr.getString());
+ panel.add(FieldLayout.LABEL, jl);
+ jl.setToolTipText(mnStr.getString());
+ return jl;
+ }
+
+ private JLabel addField(String s) {
+ JLabel l = new JLabel(s);
+ l.setForeground(Color.black);
+ panel.add(FieldLayout.FIELD, l);
+ l.setLabelFor(panel);
+ l.setToolTipText(s);
+ return l;
+ }
+
+ public String getDescription() {
+ return ResourceStrings.getString("cfg_wiz_review_desc");
+ }
+
+ public Component getComponent() {
+ return scrollPane;
+ }
+
+ public void setActive(int direction) {
+ StringBuffer b = new StringBuffer();
+ setFinishEnabled(true);
+ if (fullConfig) {
+ storeLabel.setText(getDsconf().getModule().getDescription());
+ hostLabel.setText(getHostResource().getDescription());
+
+ // Display lease length, reducing to largest units possible
+ int lengthVal = 0;
+ int i;
+ for (i = unitMultiples.length - 1; i >= 0; --i) {
+ lengthVal = leaseLength / unitMultiples[i];
+ if ((lengthVal != 0)
+ && (leaseLength % unitMultiples[i] == 0)) {
+ break;
+ }
+ }
+ if (i == -1) {
+ i = 0;
+ }
+ Object [] objs = new Object[3];
+ objs[0] = new Integer(lengthVal);
+ objs[1] = unitChoices[i];
+ if (leaseNegotiable) {
+ objs[2] = ResourceStrings.getString("cfg_wiz_renewable");
+ } else {
+ objs[2] = ResourceStrings.getString("cfg_wiz_nonrenewable");
+ }
+ leaseLabel.setText(MessageFormat.format(
+ ResourceStrings.getString("cfg_wiz_lease_fmt"), objs));
+
+ // Set DNS info
+ dnsLabel.setText(dnsDomain);
+ b.setLength(0);
+ Enumeration en = dnsServs.elements();
+ while (en.hasMoreElements()) {
+ IPAddress a = (IPAddress)en.nextElement();
+ if (b.length() != 0) {
+ b.append(", ");
+ }
+ b.append(a.getHostAddress());
+ }
+ dnsServLabel.setText(b.toString());
+ }
+
+ // Set network address
+ networkLabel.setText(network.toString());
+ // Set subnet mask
+ netmaskLabel.setText(network.getMask().getHostAddress());
+
+ // Set network type
+ if (isLan) {
+ netTypeLabel.setText(ResourceStrings.getString("cfg_wiz_lan"));
+ } else {
+ netTypeLabel.setText(
+ ResourceStrings.getString("cfg_wiz_point"));
+ }
+
+ // Set router
+ if (routerDiscovery) {
+ routerLabel.setText(
+ ResourceStrings.getString("cfg_wiz_router_discovery"));
+ } else {
+ routerLabel.setText(router.getHostAddress());
+ }
+
+ // Set NIS info
+ nisLabel.setText(nisDomain);
+ b.setLength(0);
+ Enumeration en = nisServs.elements();
+ while (en.hasMoreElements()) {
+ IPAddress a = (IPAddress)en.nextElement();
+ if (b.length() != 0) {
+ b.append(", ");
+ }
+ b.append(a.getHostAddress());
+ }
+ nisServLabel.setText(b.toString());
+
+ // Set NIS+ info
+ nisplusLabel.setText(nisplusDomain);
+ b.setLength(0);
+ en = nisplusServs.elements();
+ while (en.hasMoreElements()) {
+ IPAddress a = (IPAddress)en.nextElement();
+ if (b.length() != 0) {
+ b.append(", ");
+ }
+ b.append(a.getHostAddress());
+ }
+ nisplusServLabel.setText(b.toString());
+ }
+
+ public boolean setInactive(int direction) {
+ return true;
+ }
+ }
+
+ public ConfigWizard(Frame owner, String title, boolean fullConfig) {
+ super(owner, title);
+
+ try {
+ server = DataManager.get().getDhcpServiceMgr();
+ if (fullConfig) {
+ dsconfList = new DSConfList();
+ dsconfList.init(server);
+ }
+ } catch (Throwable e) {
+ e.printStackTrace(); // XXX Need to do something to handle this...
+ return;
+ }
+
+ this.fullConfig = fullConfig;
+
+ // If running as Config Wizard, put in the initial steps.
+ if (fullConfig) {
+ addStep(new DatastoreStep(
+ ResourceStrings.getString("cfg_wiz_explain"),
+ ResourceStrings.getString("cfg_wiz_store_explain")));
+ addStep(new DatastoreModuleStep());
+ addStep(new HostDataStep());
+ addStep(new LeaseStep());
+ addStep(new DnsStep());
+ }
+ // Now the steps that are common to both wizards.
+ addStep(new NetworkStep());
+ addStep(new NetTypeStep());
+ addStep(new NisStep());
+ addStep(new NisplusStep());
+ addStep(new ReviewStep());
+ showFirstStep();
+ }
+
+ public void doFinish() {
+ /*
+ * To activate the server, we have to do the following items:
+ * 1. Create the location/path if necessary.
+ * 2. Create the defaults file.
+ * 3. Create the dhcptab; ignore errors if it already exists
+ * (as in NIS+ case)
+ * 4. Create the Locale macro; ignore the error if it already exists
+ * 5. Create the server macro; if it exists we just overwrite it
+ * 6. Create the network macro;
+ * 7. Create the network table
+ * 8. Start the service
+ */
+ if (fullConfig) {
+ getDsconf().setConfig();
+ getDsconf().setLocation();
+ // Create the location/path.
+ try {
+ server.makeLocation(getDsconf().getDS());
+ } catch (ExistsException e) {
+ // this is o.k.
+ } catch (Throwable e) {
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString("create_location_error"));
+ Object [] args = new Object[1];
+ args[0] = getDsconf().getDS().getLocation();
+ String msg = form.format(args);
+ JOptionPane.showMessageDialog(ConfigWizard.this,
+ msg,
+ ResourceStrings.getString("server_error_title"),
+ JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+
+ // Create the defaults file.
+ DhcpdOptions options = new DhcpdOptions();
+ options.setDaemonEnabled(true);
+ options.setDhcpDatastore(getDsconf().getDS());
+ if (getHostResource().getResource() != null) {
+ options.setHostsResource(getHostResource().getResource());
+ }
+ if (getHostResource().getDomain() != null) {
+ options.setHostsDomain(getHostResource().getDomain());
+ }
+ try {
+ server.writeDefaults(options);
+ } catch (Throwable e) {
+ e.printStackTrace();
+ return;
+ }
+
+ // Create the dhcptab
+ try {
+ DataManager.get().getDhcptabMgr().createDhcptab();
+ } catch (Throwable e) {
+ // Not an error; some data stores are shared by multiple servers
+ }
+ }
+
+ if (fullConfig) {
+ try {
+ DataManager.get().getDhcptabMgr().createLocaleMacro();
+ } catch (Throwable e) {
+ /*
+ * Ignore this error, if one's already there we'll assume
+ * it's correct
+ */
+ }
+
+ // Create the Server macro
+ try {
+ String svrName =
+ DataManager.get().getDhcpServiceMgr().getShortServerName();
+ InetAddress svrAddress =
+ DataManager.get().getDhcpServiceMgr().getServerAddress();
+ DataManager.get().getDhcptabMgr().createServerMacro(svrName,
+ svrAddress, leaseLength, leaseNegotiable, dnsDomain,
+ dnsServs);
+ } catch (Throwable e) {
+ // Couldn't create it; inform user because this is serious
+ Object [] args = new Object[2];
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString("create_macro_error"));
+ args[0] = DataManager.get().getShortServerName();
+ args[1] = e.getMessage();
+ JOptionPane.showMessageDialog(this, form.format(args),
+ ResourceStrings.getString("server_error_title"),
+ JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+ }
+
+ // Create the network macro
+ IPAddress [] routers = null;
+ if (router != null) {
+ routers = new IPAddress[] { router };
+ }
+ try {
+ DataManager.get().getDhcptabMgr().createNetworkMacro(network,
+ routers, isLan, nisDomain, nisServs, nisplusDomain,
+ nisplusServs);
+ } catch (Throwable e) {
+ // Ignore this error? dhcpconfig gives a merge option
+ }
+
+ // Create the network table
+ try {
+ DataManager.get().getDhcpNetMgr().createNetwork(network.toString());
+ } catch (BridgeException e) {
+ // This indicates table existed; no error
+ } catch (Throwable e) {
+ Object [] args = new Object[2];
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString("create_network_table_error"));
+ args[0] = network.toString();
+ args[1] = e.getMessage();
+ JOptionPane.showMessageDialog(this, form.format(args),
+ ResourceStrings.getString("server_error_title"),
+ JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+
+ // Start the server in the initial configuration case
+ if (fullConfig) {
+ try {
+ DataManager.get().getDhcpServiceMgr().startup();
+ } catch (Throwable e) {
+ // Just warn user; this isn't disastrous
+ Object [] args = new Object[1];
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString("startup_server_error"));
+ args[0] = e.getMessage();
+ JOptionPane.showMessageDialog(this, form.format(args),
+ ResourceStrings.getString("server_error_title"),
+ JOptionPane.WARNING_MESSAGE);
+ }
+ }
+
+ super.doFinish();
+ }
+
+ public void doHelp() {
+ if (fullConfig) {
+ DhcpmgrApplet.showHelp("config_wizard");
+ } else {
+ DhcpmgrApplet.showHelp("network_wizard");
+ }
+ }
+
+ /**
+ * Sets hostResource.
+ * @param hostResource the host resource value.
+ */
+ public void setHostResource(HostResource hostResource) {
+ this.hostResource = hostResource;
+ } // setHostResource
+
+ /**
+ * Returns the hostResource.
+ * @return the hostResource.
+ */
+ public HostResource getHostResource() {
+ return hostResource;
+ } // getHostResource
+
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ConfigureChoiceDialog.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ConfigureChoiceDialog.java
new file mode 100644
index 0000000000..6cd7ec9df0
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ConfigureChoiceDialog.java
@@ -0,0 +1,131 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.client;
+
+import javax.swing.*;
+import javax.swing.border.*;
+import java.awt.event.*;
+import java.awt.*;
+
+import com.sun.dhcpmgr.ui.*;
+
+/**
+ * Dialog to select which type of server we want: full DHCP or just a BOOTP
+ * relay. This is implemented as a singleton modal so the caller can't do
+ * anything until it returns a selection.
+ */
+public class ConfigureChoiceDialog extends JDialog
+ implements ButtonPanelListener {
+ private ButtonPanel buttonPanel;
+ private ButtonGroup buttonGroup;
+ private JRadioButton dhcp, bootp;
+ /**
+ * Returned if user decides choice is "none of the above"
+ */
+ public static final int CANCELLED = 0;
+ /**
+ * Return value if user wants DHCP service
+ */
+ public static final int DHCP = 1;
+ /**
+ * Return value if user wants BOOTP relay
+ */
+ public static final int BOOTP = 2;
+ private static int value = CANCELLED;
+
+ // Must use the showDialog method to get one of these.
+ private ConfigureChoiceDialog(Frame f) {
+ super(f, true);
+ setTitle(ResourceStrings.getString("configure_choice_title"));
+ setLocationRelativeTo(f);
+
+ getContentPane().setLayout(new BorderLayout());
+
+ JPanel panel = new JPanel(new BorderLayout());
+ panel.setBorder(
+ BorderFactory.createCompoundBorder(
+ BorderFactory.createEtchedBorder(),
+ BorderFactory.createEmptyBorder(10, 20, 10, 20)));
+
+ // Explanatory text at the top
+ panel.add(Wizard.createTextArea(
+ ResourceStrings.getString("configure_choice_explain"), 5, 30),
+ BorderLayout.NORTH);
+
+ // Just show the choices as a set of radio buttons
+ buttonGroup = new ButtonGroup();
+ dhcp = new JRadioButton(
+ ResourceStrings.getString("configure_dhcp_server"), true);
+ dhcp.setToolTipText(ResourceStrings.getString("configure_dhcp_server"));
+ buttonGroup.add(dhcp);
+ Box box = Box.createVerticalBox();
+ box.add(dhcp);
+ box.add(Box.createVerticalStrut(5));
+ bootp = new JRadioButton(
+ ResourceStrings.getString("configure_bootp_relay"), false);
+ bootp.setToolTipText(
+ ResourceStrings.getString("configure_bootp_relay"));
+ buttonGroup.add(bootp);
+ box.add(bootp);
+ panel.add(box, BorderLayout.SOUTH);
+ getContentPane().add(panel, BorderLayout.NORTH);
+
+ buttonPanel = new ButtonPanel(false);
+ buttonPanel.addButtonPanelListener(this);
+ buttonPanel.setOkEnabled(true);
+ getContentPane().add(buttonPanel, BorderLayout.SOUTH);
+ }
+
+ public void buttonPressed(int buttonId) {
+ switch (buttonId) {
+ case OK:
+ if (dhcp.isSelected()) {
+ value = DHCP;
+ } else {
+ value = BOOTP;
+ }
+ setVisible(false);
+ break;
+ case CANCEL:
+ value = CANCELLED;
+ setVisible(false);
+ break;
+ case HELP:
+ DhcpmgrApplet.showHelp("server_config");
+ break;
+ }
+ }
+
+ public static int showDialog(Frame f) {
+ ConfigureChoiceDialog d = new ConfigureChoiceDialog(f);
+ d.pack();
+ d.setVisible(true);
+ d.dispose();
+ return value;
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ConfigureRelayDialog.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ConfigureRelayDialog.java
new file mode 100644
index 0000000000..f92f17129c
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ConfigureRelayDialog.java
@@ -0,0 +1,121 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1998-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.client;
+
+import javax.swing.*;
+import javax.swing.table.*;
+import javax.swing.border.*;
+import javax.swing.event.*;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.text.*;
+import java.util.*;
+import java.net.*;
+
+import com.sun.dhcpmgr.server.*;
+import com.sun.dhcpmgr.data.*;
+import com.sun.dhcpmgr.ui.*;
+
+/**
+ * Dialog to configure the server as a BOOTP relay.
+ */
+public class ConfigureRelayDialog extends DhcpmgrDialog {
+ private IPAddressList serverList;
+
+ public ConfigureRelayDialog(Frame f) {
+ super(f, false);
+
+ setTitle(ResourceStrings.getString("configure_relay_title"));
+ buttonPanel.setOkEnabled(true);
+ }
+
+ protected JPanel getMainPanel() {
+ JPanel panel = new JPanel(new BorderLayout());
+ panel.setBorder(
+ BorderFactory.createCompoundBorder(
+ BorderFactory.createEtchedBorder(),
+ BorderFactory.createEmptyBorder(10, 20, 10, 20)));
+
+ // Put some explanatory text at the top.
+ panel.add(Wizard.createTextArea(
+ ResourceStrings.getString("configure_relay_explain"), 3, 30),
+ BorderLayout.NORTH);
+
+ // Control for entering a list of servers
+ serverList = new IPAddressList();
+ Border tb = BorderFactory.createTitledBorder(
+ BorderFactory.createLineBorder(Color.black),
+ ResourceStrings.getString("dhcp_servers"));
+ serverList.setBorder(BorderFactory.createCompoundBorder(tb,
+ BorderFactory.createEmptyBorder(5, 5, 5, 5)));
+ panel.add(serverList, BorderLayout.SOUTH);
+
+ return panel;
+ }
+
+ protected void doOk() {
+ if (serverList.getListSize() == 0) {
+ // Must enter at least one DHCP server
+ JOptionPane.showMessageDialog(this,
+ ResourceStrings.getString("configure_relay_err_server_list"),
+ ResourceStrings.getString("input_error"),
+ JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+ DhcpdOptions options = new DhcpdOptions();
+ options.setDaemonEnabled(true);
+ options.setRelay(true, serverList.getAddressListString());
+ /*
+ * Now write the options to the defaults file, enable the relay,
+ * and start it up.
+ */
+ try {
+ DataManager.get().getDhcpServiceMgr().writeDefaults(options);
+ DataManager.get().getDhcpServiceMgr().startup();
+ fireActionPerformed(this, DialogActions.OK);
+ setVisible(false);
+ dispose();
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+ }
+
+ protected void doCancel() {
+ fireActionPerformed(this, DialogActions.CANCEL);
+ super.doCancel();
+ }
+
+ protected String getHelpKey() {
+ return "configure_relay";
+ }
+
+ protected void fireActionPerformed() {
+ // Do nothing; this is here just to satisfy the abstractness in super
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ConvertWizard.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ConvertWizard.java
new file mode 100644
index 0000000000..980c7fd55f
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ConvertWizard.java
@@ -0,0 +1,674 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2001-2002 by Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.client;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+import java.text.MessageFormat;
+import java.net.*;
+
+import javax.swing.*;
+import javax.swing.table.*;
+import javax.swing.event.*;
+import javax.swing.border.*;
+
+import com.sun.dhcpmgr.ui.*;
+import com.sun.dhcpmgr.data.*;
+import com.sun.dhcpmgr.server.*;
+import com.sun.dhcpmgr.bridge.BridgeException;
+import com.sun.dhcpmgr.bridge.ExistsException;
+
+/**
+ * This wizard converts the DHCP service data store.
+ */
+public class ConvertWizard extends DSWizard {
+
+ /**
+ * Handle to the service server.
+ */
+ private DhcpServiceMgr svcServer;
+
+ /**
+ * Handle to the dhcptab table server.
+ */
+ private DhcptabMgr dhcptabServer;
+
+ /**
+ * Handle to the network tables server.
+ */
+ private DhcpNetMgr netServer;
+
+ /**
+ * The DHCP defaults.
+ */
+ private DhcpdOptions dhcpdOptions;
+
+ /**
+ * The old data store config.
+ */
+ private DSConf defaultDsconf;
+
+ /**
+ * The save tables wizard step
+ */
+ private SaveTablesStep saveTablesStep;
+
+ /**
+ * List of networks to be converted.
+ */
+ private Network[] networks = null;
+
+ /**
+ * This class is the wizard step that presents the user with
+ * the option to save the tables after conversion.
+ */
+ protected class SaveTablesStep implements WizardStep {
+
+ /**
+ * The component provided to the wizard.
+ */
+ private Box stepBox;
+
+ /**
+ * The checkbox that determines whether the DHCP tables should be
+ * saved after conversion.
+ */
+ private JCheckBox saveTables;
+
+ /**
+ * Basic constructor.
+ */
+ public SaveTablesStep() {
+
+ stepBox = Box.createVerticalBox();
+
+ // Explanatory text at the top
+ //
+ stepBox.add(Wizard.createTextArea(
+ ResourceStrings.getString("cvt_wiz_save_explain"), 4, 45));
+ stepBox.add(Box.createVerticalStrut(10));
+ stepBox.add(Box.createVerticalGlue());
+
+ // Add the checkbox.
+ //
+ saveTables = new JCheckBox(
+ ResourceStrings.getString("cvt_wiz_save_label"), false);
+ saveTables.setToolTipText(
+ ResourceStrings.getString("cvt_wiz_save_label"));
+ saveTables.setAlignmentX(Component.LEFT_ALIGNMENT);
+ stepBox.add(saveTables);
+ stepBox.add(Box.createVerticalGlue());
+
+ stepBox.add(Wizard.createTextArea(
+ ResourceStrings.getString("cvt_wiz_save_note"), 4, 45));
+ stepBox.add(Box.createVerticalStrut(10));
+ stepBox.add(Box.createVerticalGlue());
+
+ } // constructor
+
+ public String getDescription() {
+ return ResourceStrings.getString("cvt_wiz_save_tables_desc");
+ } // getDescription
+
+ public Component getComponent() {
+ return stepBox;
+ } // getComponent
+
+ public void setActive(int direction) {
+ setForwardEnabled(true);
+ } // setActive
+
+ public boolean setInactive(int direction) {
+ return true;
+ } // setInactive
+
+ public boolean isSaveTablesSelected() {
+ return saveTables.isSelected();
+ }
+
+ } // SaveTablesStep
+
+ /**
+ * This class provides the review step for the conversion wizard.
+ */
+ class ReviewStep implements WizardStep {
+
+ /**
+ * The label for the old data store.
+ */
+ private JLabel oldStoreLabel;
+
+ /**
+ * The label for the new data store.
+ */
+ private JLabel newStoreLabel;
+
+ /**
+ * The label for saving tables.
+ */
+ private JLabel saveLabel;
+
+ /**
+ * The component to provide to the conversion wizard.
+ */
+ private Box stepBox;
+
+ /**
+ * The panel used to create the review information.
+ */
+ private JPanel panel;
+
+ /**
+ * The constructor for the step.
+ */
+ public ReviewStep() {
+
+ stepBox = Box.createVerticalBox();
+ stepBox.add(Wizard.createTextArea(
+ ResourceStrings.getString("cvt_wiz_review_explain"),
+ 3, 45));
+
+ panel = new JPanel(new FieldLayout());
+
+ addLabel("cvt_wiz_old_datastore").setToolTipText(
+ ResourceStrings.getString("cvt_wiz_old_datastore"));
+ oldStoreLabel = addField("uninitialized");
+
+ addLabel("cvt_wiz_new_datastore").setToolTipText(
+ ResourceStrings.getString("cvt_wiz_new_datastore"));
+ newStoreLabel = addField("uninitialized");
+
+ addLabel("cvt_wiz_save_tables").setToolTipText(
+ ResourceStrings.getString("cvt_wiz_save_tables"));
+ saveLabel = addField("uninitialized");
+
+ stepBox.add(panel);
+ stepBox.add(Box.createVerticalGlue());
+
+ stepBox.add(Wizard.createTextArea(
+ ResourceStrings.getString("cvt_wiz_review_note"), 4, 45));
+ stepBox.add(Box.createVerticalStrut(10));
+ stepBox.add(Box.createVerticalGlue());
+
+ } // constructor
+
+ /**
+ * Adds a label to the review panel.
+ * @param s the label string.
+ */
+ private JLabel addLabel(String s) {
+ JLabel addLbl =
+ new JLabel(ResourceStrings.getString(s));
+ panel.add(FieldLayout.LABEL, addLbl);
+ return addLbl;
+ } // addLabel
+
+ /**
+ * Adds a field to the review panel.
+ * @param s the field value.
+ * @return the label of which the field consists.
+ */
+ private JLabel addField(String s) {
+ JLabel l = new JLabel(s);
+ l.setForeground(Color.black);
+ panel.add(FieldLayout.FIELD, l);
+ return l;
+ } // addField
+
+ public String getDescription() {
+ return ResourceStrings.getString("cvt_wiz_review_desc");
+ } // getDescription
+
+ public Component getComponent() {
+ return stepBox;
+ } // getComponent
+
+ public void setActive(int direction) {
+
+ setFinishEnabled(true);
+
+ /**
+ * If no bean exists for the default data store, then use the
+ * name of the data store as the description.
+ */
+ String description = null;
+ if (defaultDsconf != null) {
+ description = defaultDsconf.getModule().getDescription();
+ } else {
+ description = dhcpdOptions.getResource();
+ }
+
+ oldStoreLabel.setText(description);
+ newStoreLabel.setText(getDsconf().getModule().getDescription());
+
+ String message = null;
+ if (saveTablesStep.isSaveTablesSelected()) {
+ message = ResourceStrings.getString("yes");
+ } else {
+ message = ResourceStrings.getString("no");
+ }
+ saveLabel.setText(message);
+
+ } // setActive
+
+ public boolean setInactive(int direction) {
+ return true;
+ } // setInactive
+
+ } // ReviewStep
+
+ /**
+ * Constructor for the ConvertWizard.
+ * @param owner owner of the wizard.
+ * @param title title of the wizard.
+ */
+ public ConvertWizard(Frame owner, String title) {
+
+ super(owner, title);
+
+ // Go ahead and grab handles to the different servers and
+ // read the server defaults.
+ //
+ try {
+ svcServer = DataManager.get().getDhcpServiceMgr();
+ dhcptabServer = DataManager.get().getDhcptabMgr();
+ netServer = DataManager.get().getDhcpNetMgr();
+ dhcpdOptions = svcServer.readDefaults();
+ } catch (Throwable e) {
+ e.printStackTrace();
+ return;
+ }
+
+ // Create the DSConfList and determine the default. Note that
+ // if the current data store has no management bean, then the
+ // defaultDsconf is null.
+ //
+ dsconfList = new DSConfList();
+ dsconfList.init(svcServer);
+ defaultDsconf =
+ dsconfList.findDsconf(dhcpdOptions.getResource());
+
+ // If no bean exists for the default data store, then use the
+ // name of the data store as the description.
+ //
+ String description = null;
+ if (defaultDsconf != null) {
+ description = defaultDsconf.getModule().getDescription();
+ } else {
+ description = dhcpdOptions.getResource();
+ }
+
+
+ // Build the wizard explanation message.
+ //
+ Object [] args = new Object[1];
+ args[0] = description;
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString("cvt_wiz_explain"));
+ String wizExplain = form.format(args);
+
+ // Add the steps for the wizard.
+ //
+ DatastoreStep datastoreStep = new DatastoreStep(wizExplain,
+ ResourceStrings.getString("cvt_wiz_store_explain"));
+ addStep(datastoreStep);
+ addStep(new DatastoreModuleStep());
+ addStep(saveTablesStep = new SaveTablesStep());
+ addStep(new ReviewStep());
+ showFirstStep();
+ }
+
+ public void doFinish() {
+ /*
+ * To convert the data store, we have to do the following items:
+ * 1. Create the new location/path
+ * 2. Convert the dhcptab
+ * 3. Convert the network tables
+ * 4. Modify the DHCP defaults
+ * 5. Delete old tables if necessary
+ */
+
+ getDsconf().setLocation();
+ getDsconf().setConfig();
+ final DhcpDatastore newDhcpDatastore = getDsconf().getDS();
+ final DhcpDatastore oldDhcpDatastore = dhcpdOptions.getDhcpDatastore();
+
+ if (newDhcpDatastore.equals(oldDhcpDatastore)) {
+ JOptionPane.showMessageDialog(ConvertWizard.this,
+ ResourceStrings.getString("cvt_wiz_same_datastore_error"),
+ ResourceStrings.getString("cvt_wiz_error"),
+ JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+
+ // Create the new location if it does not exist.
+ //
+ try {
+ svcServer.makeLocation(newDhcpDatastore);
+ } catch (ExistsException e) {
+ // this is o.k.
+ } catch (Throwable e) {
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString("cvt_wiz_location_error"));
+ Object [] args = new Object[1];
+ args[0] = newDhcpDatastore.getLocation();
+ String msg = form.format(args);
+ JOptionPane.showMessageDialog(ConvertWizard.this,
+ msg,
+ ResourceStrings.getString("cvt_wiz_error"),
+ JOptionPane.ERROR_MESSAGE);
+
+ return;
+ }
+
+ // Go get a list of the network tables to convert.
+ //
+ try {
+ networks = netServer.getNetworks(oldDhcpDatastore);
+ if (networks == null) {
+ networks = new Network[0];
+ }
+ } catch (Throwable e) {
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString("cvt_wiz_networks_error"));
+ Object [] args = new Object[1];
+ args[0] = e.getMessage();
+ String msg = form.format(args);
+ JOptionPane.showMessageDialog(ConvertWizard.this,
+ msg,
+ ResourceStrings.getString("cvt_wiz_error"),
+ JOptionPane.ERROR_MESSAGE);
+
+ reallyFinish();
+ return;
+ }
+
+ // Add 1 to tables count for dhcptab
+ final int tables = networks.length + 1;
+ // One update per table, plus one per table if deleting
+ int updates =
+ !saveTablesStep.isSaveTablesSelected() ? tables * 2 : tables;
+ // Add: one for shutdown, one for updating dhcpsvc.conf
+ updates += 2;
+ // If daemon will be restarted, then add 1 for start
+ if (dhcpdOptions.isDaemonEnabled()) {
+ ++updates;
+ }
+ final ProgressManager progress = new ProgressManager(this,
+ ResourceStrings.getString("cvt_wiz_progress"),
+ "", 0, updates);
+
+ // Called when doFinish() is really finished
+ // (i.e., the thread completes).
+ //
+ final Runnable finisher = new Runnable() {
+ public void run() {
+ reallyFinish();
+ }
+ };
+
+
+ // Here's the thread which does the conversion.
+ //
+ Thread convertThread = new Thread() {
+ public void run() {
+
+ String message = null;
+ MessageFormat form;
+ MessageFormat errForm;
+ Object [] args = new Object[1];
+ boolean saveTables =
+ saveTablesStep.isSaveTablesSelected();
+
+ // This is final so it can be used in the
+ // errorDisplay Runnable.
+ //
+ final ErrorTable failedTable = new ErrorTable(
+ ResourceStrings.getString("cvt_wiz_table"),
+ String.class);
+
+ // Shutdown the server.
+ //
+ int counter = 0;
+ try {
+ svcServer.shutdown();
+ message = ResourceStrings.getString(
+ "cvt_wiz_server_shutdown");
+ } catch (Throwable e) {
+ message =
+ ResourceStrings.getString("cvt_wiz_shutdown_err");
+ failedTable.addError("", e.getMessage());
+ saveTables = true;
+ } finally {
+ try {
+ progress.update(++counter, message);
+ } catch (InterruptedException e) {
+ SwingUtilities.invokeLater(finisher);
+ return;
+ }
+ }
+
+ // Convert the dhcptab.
+ //
+ try {
+ dhcptabServer.cvtDhcptab(newDhcpDatastore);
+ message = ResourceStrings.getString(
+ "cvt_wiz_progress_dhcptab_cvt");
+ } catch (Throwable e) {
+ message = ResourceStrings.getString(
+ "cvt_wiz_progress_dhcptab_cvt_err");
+ failedTable.addError(ResourceStrings.getString(
+ "cvt_wiz_dhcptab"), e.getMessage());
+ saveTables = true;
+ } finally {
+ try {
+ progress.update(++counter, message);
+ } catch (InterruptedException e) {
+ SwingUtilities.invokeLater(finisher);
+ return;
+ }
+ }
+
+ // Convert the network tables.
+ //
+ form = new MessageFormat(ResourceStrings.getString(
+ "cvt_wiz_progress_network_cvt"));
+ errForm = new MessageFormat(ResourceStrings.getString(
+ "cvt_wiz_progress_network_cvt_err"));
+
+ for (int i = 0; i < networks.length; ++i) {
+ String netString = networks[i].toString();
+ args[0] = netString;
+ try {
+ netServer.cvtNetwork(netString, newDhcpDatastore);
+ message = form.format(args);
+ } catch (Throwable e) {
+ message = errForm.format(args);
+ failedTable.addError(netString, e.getMessage());
+ saveTables = true;
+ } finally {
+ try {
+ progress.update(++counter, message);
+ } catch (InterruptedException e) {
+ SwingUtilities.invokeLater(finisher);
+ return;
+ }
+ }
+ }
+
+ // Update the DHCP defaults file with the new values.
+ //
+ dhcpdOptions.setDhcpDatastore(newDhcpDatastore);
+ try {
+ svcServer.writeDefaults(dhcpdOptions);
+ message = ResourceStrings.getString(
+ "cvt_wiz_progress_defaults");
+ } catch (Throwable e) {
+ message = ResourceStrings.getString(
+ "cvt_wiz_progress_defaults_err");
+ failedTable.addError(ResourceStrings.getString(
+ "cvt_wiz_defaults"), e.getMessage());
+ saveTables = true;
+ } finally {
+ try {
+ progress.update(++counter, message);
+ } catch (InterruptedException e) {
+ SwingUtilities.invokeLater(finisher);
+ return;
+ }
+ }
+
+ if (!saveTables) {
+ // Delete the network tables
+ //
+ form = new MessageFormat(ResourceStrings.getString(
+ "cvt_wiz_progress_network_del"));
+ errForm = new MessageFormat(ResourceStrings.getString(
+ "cvt_wiz_progress_network_del_err"));
+
+ for (int i = 0; i < networks.length; ++i) {
+ String netString = networks[i].toString();
+ args[0] = netString;
+ try {
+ netServer.deleteNetwork(netString, false, false,
+ oldDhcpDatastore);
+ message = form.format(args);
+ } catch (Throwable e) {
+ message = errForm.format(args);
+ failedTable.addError(netString, e.getMessage());
+ } finally {
+ try {
+ progress.update(++counter, message);
+ } catch (InterruptedException e) {
+ SwingUtilities.invokeLater(finisher);
+ return;
+ }
+ }
+ }
+
+ // Delete the dhcptab
+ //
+ try {
+ dhcptabServer.deleteDhcptab(oldDhcpDatastore);
+ message = ResourceStrings.getString(
+ "cvt_wiz_progress_dhcptab_del");
+ } catch (Throwable e) {
+ message = ResourceStrings.getString(
+ "cvt_wiz_progress_dhcptab_del_err");
+ failedTable.addError(ResourceStrings.getString(
+ "cvt_wiz_dhcptab"), e.getMessage());
+ } finally {
+ try {
+ progress.update(++counter, message);
+ } catch (InterruptedException e) {
+ SwingUtilities.invokeLater(finisher);
+ return;
+ }
+ }
+ } else if (!saveTablesStep.isSaveTablesSelected()) {
+ try {
+ counter += tables;
+ progress.update(counter, "");
+ } catch (InterruptedException e) {
+ SwingUtilities.invokeLater(finisher);
+ return;
+ }
+ }
+
+ // Start the server.
+ //
+ if (dhcpdOptions.isDaemonEnabled()) {
+ try {
+ svcServer.startup();
+ message = ResourceStrings.getString(
+ "cvt_wiz_server_started");
+ } catch (Throwable e) {
+ message =
+ ResourceStrings.getString("cvt_wiz_start_err");
+ failedTable.addError("", e.getMessage());
+ } finally {
+ try {
+ progress.update(++counter, message);
+ } catch (InterruptedException e) {
+ SwingUtilities.invokeLater(finisher);
+ return;
+ }
+ }
+ }
+
+ // If any errors occurred, display them all at once.
+ //
+ if (!failedTable.isEmpty()) {
+ Runnable errorDisplay = new Runnable() {
+ public void run() {
+ Object [] objs = new Object[2];
+ objs[0] =
+ ResourceStrings.getString("cvt_wiz_errors");
+ JScrollPane scrollPane =
+ new JScrollPane(failedTable);
+
+ // Resize the table to something kind of small
+ //
+ Dimension d =
+ failedTable.
+ getPreferredScrollableViewportSize();
+ d.height = 80;
+ failedTable.setPreferredScrollableViewportSize(d);
+ objs[1] = scrollPane;
+ JOptionPane.showMessageDialog(ConvertWizard.this,
+ objs,
+ ResourceStrings.getString("server_error_title"),
+ JOptionPane.ERROR_MESSAGE);
+ }
+ };
+ try {
+ SwingUtilities.invokeAndWait(errorDisplay);
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+ }
+ SwingUtilities.invokeLater(finisher);
+ }
+ };
+ convertThread.start();
+ }
+
+ public void doHelp() {
+ DhcpmgrApplet.showHelp("convert_wizard");
+ }
+
+ /**
+ * Called by the worker thread upon completion to exec the Wizard
+ * doFinish().
+ */
+ protected void reallyFinish() {
+ super.doFinish();
+ } // reallyFinish
+
+} // ConvertWizard
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/CreateAddressDialog.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/CreateAddressDialog.java
new file mode 100644
index 0000000000..ab83ce99fc
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/CreateAddressDialog.java
@@ -0,0 +1,656 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 1998-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.client;
+
+import javax.swing.*;
+import javax.swing.border.*;
+import javax.swing.event.*;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.text.*;
+import java.util.*;
+import java.net.*;
+
+import com.sun.dhcpmgr.server.*;
+import com.sun.dhcpmgr.data.*;
+import com.sun.dhcpmgr.ui.*;
+import com.sun.dhcpmgr.bridge.ExistsException;
+import com.sun.dhcpmgr.bridge.NoEntryException;
+import com.sun.dhcpmgr.bridge.HostExistsException;
+import com.sun.dhcpmgr.bridge.NoHostsEntryException;
+import com.sun.dhcpmgr.bridge.NoTableException;
+import com.sun.dhcpmgr.bridge.BridgeException;
+
+/**
+ * This dialog is used to create/duplicate/modify a DHCP address record.
+ */
+public class CreateAddressDialog extends JDialog
+ implements ButtonPanelListener {
+
+ // Model class for the drop-down list of macros user may select from
+ class MacroListModel extends AbstractListModel implements ComboBoxModel {
+ private Object currentValue;
+ private Macro [] data = null;
+ private String noMacro;
+
+ public MacroListModel() {
+ try {
+ noMacro = ResourceStrings.getString("no_macro_item");
+ DhcptabMgr server = DataManager.get().getDhcptabMgr();
+ data = server.getMacros();
+ } catch (NoTableException e) {
+ // can function without table
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+ }
+
+ public int getSize() {
+ if (data == null)
+ return 1;
+ else
+ return data.length+1;
+ }
+
+ public Object getElementAt(int index) {
+ if (index == 0) {
+ return noMacro;
+ } else {
+ return data[index-1].getKey();
+ }
+ }
+
+ public void setSelectedItem(Object anItem) {
+ currentValue = anItem;
+ fireContentsChanged(this, -1, -1);
+ }
+
+ public Object getSelectedItem() {
+ return currentValue;
+ }
+ }
+
+ public static final int CREATE = 0;
+ public static final int EDIT = 1;
+ public static final int DUPLICATE = 2;
+
+ private int mode = EDIT;
+ private Network network;
+ private IPAddressField address;
+ private HostnameField name;
+ private JTextField server;
+ private JComboBox macro;
+ private JTextField clientId;
+ private JTextField comment;
+ private JTextField expirationDate;
+ private JCheckBox unusable;
+ private JCheckBox bootp;
+ private JCheckBox manual;
+ private JRadioButton temporary;
+ private JRadioButton permanent;
+ private ButtonGroup buttonGroup;
+ private ButtonPanel buttonPanel;
+ private DhcpClientRecord client, originalClient;
+ private Vector listeners;
+ private DateFormat dateFormat =
+ DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);
+
+ public CreateAddressDialog(Frame f, int mode, DhcpClientRecord rec,
+ Network net) {
+ super(f);
+ setLocationRelativeTo(f);
+
+ listeners = new Vector();
+ this.mode = mode;
+ network = net;
+ switch (mode) {
+ case CREATE:
+ setTitle(ResourceStrings.getString("create_address_title"));
+ break;
+ case EDIT:
+ setTitle(ResourceStrings.getString("edit_address_title"));
+ break;
+ case DUPLICATE:
+ setTitle(ResourceStrings.getString("duplicate_address_title"));
+ break;
+ default:
+ break;
+ }
+
+ getContentPane().setLayout(new BorderLayout());
+
+ JTabbedPane tabbedPane = new JTabbedPane();
+
+ GridBagLayout bag = new GridBagLayout();
+ JPanel mainPanel = new JPanel(bag);
+ GridBagConstraints c = new GridBagConstraints();
+ c.gridx = c.gridy = 0;
+ c.gridwidth = c.gridheight = 1;
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.insets = new Insets(5, 5, 5, 5);
+ c.weightx = c.weighty = 1.0;
+
+ // Label and text field for address
+ Mnemonic mnIP =
+ new Mnemonic(ResourceStrings.getString("ip_address_label"));
+ JLabel l = new JLabel(mnIP.getString(), SwingConstants.RIGHT);
+ bag.setConstraints(l, c);
+ mainPanel.add(l);
+ address = new IPAddressField();
+ l.setLabelFor(address);
+ l.setToolTipText(mnIP.getString());
+ l.setDisplayedMnemonic(mnIP.getMnemonic());
+
+ if (mode == EDIT) {
+ address.setEditable(false);
+ }
+ ++c.gridx;
+ bag.setConstraints(address, c);
+ mainPanel.add(address);
+
+ // Label and text field for name
+ Mnemonic mnClient =
+ new Mnemonic(ResourceStrings.getString("hostname_label"));
+ l = new JLabel(mnClient.getString(), SwingConstants.RIGHT);
+ ++c.gridy;
+ c.gridx = 0;
+ bag.setConstraints(l, c);
+ mainPanel.add(l);
+ name = new HostnameField();
+
+ l.setLabelFor(name);
+ l.setToolTipText(mnClient.getString());
+ l.setDisplayedMnemonic(mnClient.getMnemonic());
+
+ ++c.gridx;
+ bag.setConstraints(name, c);
+ mainPanel.add(name);
+
+ name.setEditable(true);
+ try {
+ DhcpdOptions opts =
+ DataManager.get().getDhcpServiceMgr().readDefaults();
+ if (opts.getHostsResource() == null) {
+ name.setEditable(false);
+ }
+ } catch (BridgeException e) {
+ // Assume set
+ }
+
+ // label and field for owning server
+ Mnemonic mnOwn =
+ new Mnemonic(ResourceStrings.getString("owning_server_label"));
+ l = new JLabel(mnOwn.getString(), SwingConstants.RIGHT);
+ ++c.gridy;
+ c.gridx = 0;
+ bag.setConstraints(l, c);
+ mainPanel.add(l);
+ server = new JTextField(20);
+
+ l.setLabelFor(server);
+ l.setToolTipText(mnOwn.getString());
+ l.setDisplayedMnemonic(mnOwn.getMnemonic());
+
+ ++c.gridx;
+ bag.setConstraints(server, c);
+ mainPanel.add(server);
+
+ // label and combo box for macro
+ Mnemonic mnMacro =
+ new Mnemonic(ResourceStrings.getString("config_macro_label"));
+ l = new JLabel(mnMacro.getString(), SwingConstants.RIGHT);
+ ++c.gridy;
+ c.gridx = 0;
+ bag.setConstraints(l, c);
+ mainPanel.add(l);
+ MacroListModel macroListModel = new MacroListModel();
+ macro = new JComboBox(macroListModel);
+
+ l.setLabelFor(macro);
+ l.setToolTipText(mnMacro.getString());
+ l.setDisplayedMnemonic(mnMacro.getMnemonic());
+
+ macro.setEditable(false);
+ ++c.gridx;
+ bag.setConstraints(macro, c);
+ mainPanel.add(macro);
+
+ // Comment
+ Mnemonic mnComm =
+ new Mnemonic(ResourceStrings.getString("comment_label"));
+ l = new JLabel(mnComm.getString(), SwingConstants.RIGHT);
+ ++c.gridy;
+ c.gridx = 0;
+ bag.setConstraints(l, c);
+ mainPanel.add(l);
+ comment = new JTextField(20);
+
+ l.setLabelFor(comment);
+ l.setToolTipText(mnComm.getString());
+ l.setDisplayedMnemonic(mnComm.getMnemonic());
+
+ ++c.gridx;
+ bag.setConstraints(comment, c);
+ mainPanel.add(comment);
+
+ // Create first panel of tabs
+ tabbedPane.addTab(ResourceStrings.getString("address_tab_label"),
+ mainPanel);
+
+ mainPanel = new JPanel(new BorderLayout(5, 5));
+
+ // Client ID
+ Mnemonic mnID =
+ new Mnemonic(ResourceStrings.getString("client_id_label"));
+ JPanel idPanel = new JPanel();
+ l = new JLabel(mnID.getString());
+ idPanel.add(l);
+ clientId = new JTextField(20);
+
+ l.setLabelFor(clientId);
+ l.setToolTipText(mnID.getString());
+ l.setDisplayedMnemonic(mnID.getMnemonic());
+ idPanel.add(clientId);
+
+ manual = new JCheckBox(ResourceStrings.getString("manual_checkbox"));
+ idPanel.add(manual);
+ manual.setToolTipText(
+ ResourceStrings.getString("manual_checkbox"));
+
+ mainPanel.add(idPanel, BorderLayout.NORTH);
+
+ // radio buttons for lease state
+ bag = new GridBagLayout();
+ JPanel leasePanel = new JPanel(bag);
+ /*
+ * Create a compound border with empty space on the outside and line
+ * border on the inside, then title it.
+ */
+ Border b = BorderFactory.createCompoundBorder(
+ BorderFactory.createEmptyBorder(0, 5, 0, 5),
+ BorderFactory.createLineBorder(Color.black));
+ leasePanel.setBorder(BorderFactory.createTitledBorder(b,
+ ResourceStrings.getString("lease_policy_label")));
+
+ // Reset constraints
+ c.gridx = c.gridy = 0;
+ c.gridwidth = 1;
+
+ buttonGroup = new ButtonGroup();
+ temporary = new JRadioButton();
+ buttonGroup.add(temporary);
+ c.weightx = 0.0;
+ bag.setConstraints(temporary, c);
+ leasePanel.add(temporary);
+
+ Mnemonic mnDyn =
+ new Mnemonic(ResourceStrings.getString("leased_label"));
+ l = new JLabel(mnDyn.getString());
+ ++c.gridx;
+ c.weightx = 1.0;
+ bag.setConstraints(l, c);
+ leasePanel.add(l);
+
+ expirationDate = new JTextField(30);
+
+ l.setLabelFor(expirationDate);
+ l.setToolTipText(mnDyn.getString());
+ l.setDisplayedMnemonic(mnDyn.getMnemonic());
+
+ ++c.gridy;
+ bag.setConstraints(expirationDate, c);
+ leasePanel.add(expirationDate);
+
+ permanent = new JRadioButton();
+ buttonGroup.add(permanent);
+ ++c.gridy;
+ c.gridx = 0;
+ c.weightx = 0.0;
+ bag.setConstraints(permanent, c);
+ leasePanel.add(permanent);
+
+ Mnemonic mnPerm =
+ new Mnemonic(ResourceStrings.getString("permanent_label"));
+ l = new JLabel(mnPerm.getString());
+ l.setLabelFor(leasePanel);
+ l.setToolTipText(mnPerm.getString());
+ l.setDisplayedMnemonic(mnPerm.getMnemonic());
+
+ ++c.gridx;
+ c.weightx = 1.0;
+ bag.setConstraints(l, c);
+ leasePanel.add(l);
+
+ mainPanel.add(leasePanel, BorderLayout.CENTER);
+
+ // Flag checkboxes
+ JPanel southPanel = new JPanel(new BorderLayout(5, 5));
+ southPanel.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 5));
+ bootp = new JCheckBox(ResourceStrings.getString("bootp_checkbox"));
+
+ bootp.setToolTipText(
+ ResourceStrings.getString("bootp_checkbox"));
+
+ bootp.setHorizontalAlignment(SwingConstants.LEFT);
+ southPanel.add(bootp, BorderLayout.CENTER);
+
+ unusable = new JCheckBox(
+ ResourceStrings.getString("unusable_checkbox"));
+
+ unusable.setToolTipText(
+ ResourceStrings.getString("unusable_checkbox"));
+
+ unusable.setHorizontalAlignment(SwingConstants.LEFT);
+ southPanel.add(unusable, BorderLayout.SOUTH);
+
+ mainPanel.add(southPanel, BorderLayout.SOUTH);
+
+ tabbedPane.addTab(ResourceStrings.getString("lease_tab_label"),
+ mainPanel);
+ JPanel borderPanel = new JPanel(new BorderLayout());
+ borderPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
+ borderPanel.add(tabbedPane, BorderLayout.CENTER);
+
+ getContentPane().add(borderPanel, BorderLayout.CENTER);
+
+ buttonPanel = new ButtonPanel(true);
+ buttonPanel.addButtonPanelListener(this);
+ getContentPane().add(buttonPanel, BorderLayout.SOUTH);
+
+ setClient(rec);
+
+ DocumentListener docListener = new DocumentListener() {
+ public void insertUpdate(DocumentEvent e) {
+ buttonPanel.setOkEnabled(address.getDocument().getLength() != 0
+ && server.getDocument().getLength() != 0);
+ }
+ public void changedUpdate(DocumentEvent e) {
+ insertUpdate(e);
+ }
+ public void removeUpdate(DocumentEvent e) {
+ insertUpdate(e);
+ }
+ };
+
+ address.getDocument().addDocumentListener(docListener);
+ server.getDocument().addDocumentListener(docListener);
+
+ if (mode == EDIT) {
+ buttonPanel.setOkEnabled(true);
+ }
+ }
+
+ public void setClient(DhcpClientRecord c) {
+ originalClient = (DhcpClientRecord)c.clone();
+ client = c;
+ resetValues();
+ }
+
+ private void resetValues() {
+ if (mode == DUPLICATE) {
+ address.setText("");
+ name.setText("");
+ } else {
+ String a = client.getClientIPAddress();
+ String n = client.getClientName();
+ address.setText(a);
+ if (a.equals(n)) {
+ // If name == address, there is no name, so leave it blank
+ name.setText("");
+ } else {
+ name.setText(n);
+ }
+ }
+ if (mode == CREATE && (client.getServerName() == null ||
+ client.getServerName().length() == 0)) {
+ server.setText(DataManager.get().getShortServerName());
+ } else {
+ server.setText(client.getServerName());
+ }
+ if (mode == CREATE) {
+ macro.setSelectedItem(DataManager.get().getShortServerName());
+ } else {
+ macro.setSelectedItem(client.getMacro());
+ }
+ comment.setText(client.getComment());
+ clientId.setText(client.getClientId());
+ manual.setSelected(client.isManual());
+ if (client.isPermanent()) {
+ permanent.setSelected(true);
+ } else {
+ temporary.setSelected(true);
+ }
+ bootp.setSelected(client.isBootp());
+ unusable.setSelected(client.isUnusable());
+ Date d = client.getExpiration();
+ if (d == null || d.getTime() == 0) {
+ expirationDate.setText("");
+ } else {
+ expirationDate.setText(dateFormat.format(d));
+ }
+ }
+
+ public void buttonPressed(int buttonId) {
+ switch (buttonId) {
+ case OK:
+ IPAddress addr = address.getValue();
+ if (addr == null) {
+ // Bad IP address
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString("invalid_address"));
+ Object [] args = new Object[] { address.getText() };
+ JOptionPane.showMessageDialog(this, form.format(args),
+ ResourceStrings.getString("input_error"),
+ JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+ if (!network.containsAddress(addr)) {
+ // Address is not on the network we're editing
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString("bad_network_address"));
+ Object [] args = new Object[] {
+ addr.getHostAddress(),
+ network.getAddress()
+ };
+ JOptionPane.showMessageDialog(this, form.format(args),
+ ResourceStrings.getString("input_error"),
+ JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+ try {
+ client.setClientIP(address.getValue());
+ } catch (ValidationException e) {
+ // This shouldn't happen, should have caught any problem already
+ }
+
+ // This logic is needed because if the original client name
+ // was equal to its IP address, then this really means that
+ // that the name was not set. If this is the case and the
+ // name field is empty, then no change was made. In all other
+ // cases we can be assured that the client name was changed
+ // or is valid.
+ //
+ if (!(name.getText().length() == 0 &&
+ originalClient.getClientIPAddress().equals(
+ originalClient.getClientName()))) {
+ client.setClientName(name.getText());
+ }
+ try {
+ if (!server.getText().equals(client.getServerName())) {
+ // Don't bother resetting if it hasn't changed
+ client.setServerIP(new IPAddress(server.getText()));
+ }
+ } catch (ValidationException e) {
+ // Bad server name
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString("invalid_server"));
+ Object [] args = new Object[] { server.getText() };
+ JOptionPane.showMessageDialog(this, form.format(args),
+ ResourceStrings.getString("input_error"),
+ JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+ int i = macro.getSelectedIndex();
+ if (i == 0) {
+ client.setMacro("");
+ } else {
+ client.setMacro((String)macro.getItemAt(i));
+ }
+ client.setComment(comment.getText());
+ try {
+ client.setClientId(clientId.getText());
+ } catch (ValidationException e) {
+ // Bad client ID
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString("invalid_client_id"));
+ Object [] args = new Object[] { clientId.getText() };
+ JOptionPane.showMessageDialog(this, form.format(args),
+ ResourceStrings.getString("input_error"),
+ JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+ client.setManual(manual.isSelected());
+ client.setPermanent(permanent.isSelected());
+ client.setBootp(bootp.isSelected());
+ client.setUnusable(unusable.isSelected());
+ try {
+ if (expirationDate.getText().length() == 0) {
+ client.setExpiration(new Date(0));
+ } else {
+ Date d = dateFormat.parse(expirationDate.getText());
+ client.setExpiration(d);
+ }
+ } catch (ParseException e) {
+ // Bad date/time entered
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString("invalid_date"));
+ Object [] args = new Object[] {
+ expirationDate.getText(),
+ dateFormat.format(new Date())
+ };
+ JOptionPane.showMessageDialog(this, form.format(args),
+ ResourceStrings.getString("input_error"),
+ JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+ // Got all the data, now update the data store
+ try {
+ DhcpNetMgr server = DataManager.get().getDhcpNetMgr();
+ if (mode == EDIT) {
+ server.modifyClient(originalClient, client,
+ network.toString());
+ } else {
+ server.addClient(client, network.toString());
+ }
+ fireActionPerformed();
+ setVisible(false);
+ dispose();
+ } catch (Exception e) {
+ /*
+ * Display an error message dialog. However, if the error
+ * related to editing the hosts table, we merely consider it
+ * a warning as the network table stuff actually was done.
+ */
+ String msg = e.getMessage();
+ int msgType = JOptionPane.ERROR_MESSAGE;
+ if (e instanceof ExistsException) {
+ msg = ResourceStrings.getString("address_exists");
+ } else if (e instanceof NoEntryException) {
+ msg = ResourceStrings.getString("address_missing");
+ } else if (e instanceof HostExistsException) {
+ msg = ResourceStrings.getString("host_exists");
+ msgType = JOptionPane.ERROR_MESSAGE;
+ } else if (e instanceof NoHostsEntryException) {
+ msg = ResourceStrings.getString("host_missing");
+ msgType = JOptionPane.WARNING_MESSAGE;
+ }
+ JOptionPane.showMessageDialog(this, msg,
+ ResourceStrings.getString("server_error_title"), msgType);
+ if (msgType == JOptionPane.WARNING_MESSAGE) {
+ fireActionPerformed();
+ setVisible(false);
+ dispose();
+ }
+ }
+ break;
+ case CANCEL:
+ setVisible(false);
+ dispose();
+ break;
+ case HELP:
+ String helpTag = null;
+ switch (mode) {
+ case CREATE:
+ helpTag = "create_address";
+ break;
+ case DUPLICATE:
+ helpTag = "duplicate_address";
+ break;
+ case EDIT:
+ helpTag = "modify_address";
+ break;
+ }
+ DhcpmgrApplet.showHelp(helpTag);
+ break;
+ case RESET:
+ setClient(originalClient);
+ break;
+ }
+ }
+
+ public void addActionListener(ActionListener l) {
+ listeners.addElement(l);
+ }
+
+ public void removeActionListener(ActionListener l) {
+ listeners.removeElement(l);
+ }
+
+ protected void fireActionPerformed() {
+ String command = null;
+ switch (mode) {
+ case CREATE:
+ command = DialogActions.CREATE;
+ break;
+ case DUPLICATE:
+ command = DialogActions.DUPLICATE;
+ break;
+ case EDIT:
+ command = DialogActions.EDIT;
+ break;
+ }
+ ActionEvent e = new ActionEvent(this, ActionEvent.ACTION_PERFORMED,
+ command);
+ Enumeration en = listeners.elements();
+ while (en.hasMoreElements()) {
+ ActionListener l = (ActionListener)en.nextElement();
+ l.actionPerformed(e);
+ }
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/CreateMacroDialog.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/CreateMacroDialog.java
new file mode 100644
index 0000000000..fbf35a9376
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/CreateMacroDialog.java
@@ -0,0 +1,763 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.client;
+
+import javax.swing.*;
+import javax.swing.border.*;
+import javax.swing.event.*;
+import javax.swing.table.*;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.text.MessageFormat;
+import java.util.*;
+
+import com.sun.dhcpmgr.server.*;
+import com.sun.dhcpmgr.data.*;
+import com.sun.dhcpmgr.ui.*;
+import com.sun.dhcpmgr.bridge.NotRunningException;
+
+/**
+ * Dialog to create/duplicate/edit a macro.
+ */
+public class CreateMacroDialog extends JDialog implements ButtonPanelListener {
+
+ // Model for the table that displays the macro's contents
+ class MacroTableModel extends AbstractTableModel {
+ Macro macro;
+
+ public MacroTableModel() {
+ setMacro(new Macro());
+ }
+
+ public MacroTableModel(Macro m) {
+ super();
+ setMacro(m);
+ }
+
+ public void setMacro(Macro m) {
+ macro = m;
+ fireTableDataChanged();
+ }
+
+ public int getRowCount() {
+ return macro.optionCount();
+ }
+
+ public int getColumnCount() {
+ return 2;
+ }
+
+ public Object getValueAt(int row, int column) {
+ OptionValue v = null;
+ try {
+ v = macro.getOptionAt(row);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ return null;
+ }
+ if (v == null) {
+ return null;
+ }
+ switch (column) {
+ case 0:
+ return v.getName();
+ case 1:
+ return v.getValue();
+ default:
+ return null;
+ }
+ }
+
+ public Class getColumnClass(int column) {
+ switch (column) {
+ case 0:
+ case 1:
+ return String.class;
+ default:
+ return super.getColumnClass(column);
+ }
+ }
+
+ public String getColumnName(int column) {
+ switch (column) {
+ case 0:
+ return ResourceStrings.getString("option_column");
+ case 1:
+ return ResourceStrings.getString("value_column");
+ default:
+ super.getColumnName(column);
+ }
+ return null;
+ }
+
+ public boolean isCellEditable(int row, int column) {
+ return false;
+ }
+
+ public void moveRowUp(int row) {
+ OptionValue v = macro.getOptionAt(row);
+ macro.deleteOptionAt(row);
+ macro.insertOptionAt(v, row-1);
+ fireTableRowsUpdated(row-1, row);
+ }
+
+ public void moveRowDown(int row) {
+ OptionValue v = macro.getOptionAt(row);
+ macro.deleteOptionAt(row);
+ macro.insertOptionAt(v, row+1);
+ fireTableRowsUpdated(row, row+1);
+ }
+
+ public void deleteRow(int row) {
+ macro.deleteOptionAt(row);
+ fireTableRowsDeleted(row, row);
+ }
+
+ public void setOptionAt(OptionValue v, int row) {
+ macro.setOptionAt(v, row);
+ fireTableDataChanged();
+ }
+
+ public int findRowForOption(String opt) {
+ for (int i = 0; i < getRowCount(); ++i) {
+ OptionValue v = macro.getOptionAt(i);
+ if (opt.equals(v.getName())) {
+ return i;
+ }
+ }
+ return -1;
+ }
+ }
+
+ public static final int CREATE = 0;
+ public static final int EDIT = 1;
+ public static final int DUPLICATE = 2;
+
+ private int mode = CREATE;
+ private Macro originalMacro = null;
+ private MacroNameField name;
+ private JTextField optionName;
+ private JTextField optionValue;
+ private AutosizingTable macroTable;
+ private MacroTableModel macroTableModel;
+ private ButtonPanel buttonPanel;
+ private JButton deleteButton, addButton, modifyButton, selectButton;
+ private UpButton upButton;
+ private DownButton downButton;
+ private JCheckBox signalBox;
+ private Vector listeners;
+ private String savedOptionName = "";
+
+ /*
+ * Listener for the editing buttons which are used to manipulate the
+ * table's contents
+ */
+ ActionListener listener = new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ int row = macroTable.getSelectedRow();
+ int lastRow = macroTable.getRowCount() - 1;
+
+ Object src = e.getSource();
+ if (src == upButton) {
+ if (row == 0) {
+ return; // Can't move the first row up
+ }
+ macroTableModel.moveRowUp(row);
+ /*
+ * Keep the row we moved selected so that repeated move up's
+ * affect the same data
+ */
+ macroTable.clearSelection();
+ macroTable.addRowSelectionInterval(row-1, row-1);
+ } else if (src == downButton) {
+ if (row == lastRow) {
+ return; // Can't move the last row down
+ }
+ macroTableModel.moveRowDown(row);
+ /*
+ * Keep the row we moved selected so that repeated move down's
+ * affect the same data
+ */
+ macroTable.clearSelection();
+ macroTable.addRowSelectionInterval(row+1, row+1);
+ } else if (src == deleteButton) {
+ macroTableModel.deleteRow(row);
+ /*
+ * Keep the same row selected so that repeated delete presses
+ * can be used to delete a series of options
+ */
+ macroTable.clearSelection();
+ if (row == lastRow) {
+ row = macroTableModel.getRowCount()-1;
+ }
+ if (macroTableModel.getRowCount() > 0) {
+ macroTable.addRowSelectionInterval(row, row);
+ }
+ if (macroTableModel.getRowCount() <= 0) {
+ modifyButton.setEnabled(false);
+ }
+ } else if (src == selectButton) {
+ // Show dialog that allows selection of options
+ String s = SelectOptionDialog.showDialog(selectButton);
+
+ /*
+ * User selected something, put it in the name field
+ * set the focus to the value field
+ */
+ if (s != null) {
+ optionName.setText(s);
+ optionValue.requestFocus();
+ }
+ } else if ((src == addButton) || (src == modifyButton)) {
+ // Update the table from the field contents
+ OptionValue v = null;
+ v = OptionValueFactory.newOptionValue(optionName.getText());
+ if (v instanceof BogusOptionValue) {
+ optionName.requestFocus();
+ // bad option name
+ MessageFormat form = null;
+ Object [] args = new Object[1];
+ args[0] = optionName.getText();
+ form = new MessageFormat(
+ ResourceStrings.getString("bad_option_name"));
+ JOptionPane.showMessageDialog(macroTable, form.format(args),
+ ResourceStrings.getString("input_error"),
+ JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+ try {
+ /*
+ * Catch an empty value field, which is only legal for
+ * a boolean option
+ */
+ String s = optionValue.getText();
+ if (s.length() == 0 && !(v instanceof BooleanOptionValue)) {
+ throw new ValidationException();
+ }
+ v.setValue(s);
+ } catch (ValidationException ex) {
+ // bad option value
+ optionValue.requestFocus();
+ MessageFormat form = null;
+ Object [] args = new Object[2];
+ form = new MessageFormat(
+ ResourceStrings.getString("bad_option_value"));
+ args[0] = optionValue.getText();
+ args[1] = optionName.getText();
+ JOptionPane.showMessageDialog(macroTable, form.format(args),
+ ResourceStrings.getString("input_error"),
+ JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+ /*
+ * Don't allow a second instance of any option other than
+ * Include in a macro, but only check if we're doing an add
+ * or if it's a modify and the name has changed.
+ */
+ if ((!(v instanceof IncludeOptionValue)
+ && (src == addButton))
+ || ((src == modifyButton)
+ && !savedOptionName.equals(v.getName()))) {
+ if (macroTableModel.macro.getOption(v.getName()) != null) {
+ optionName.requestFocus();
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString("macro_contains_option"));
+ Object [] args = new Object[1];
+ args[0] = v.getName();
+ JOptionPane.showMessageDialog(macroTable,
+ form.format(args),
+ ResourceStrings.getString("input_error"),
+ JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+ }
+ // If adding, append it at the end
+ if (src == addButton) {
+ row = macroTableModel.getRowCount();
+ }
+ macroTableModel.setOptionAt(v, row);
+ macroTable.clearSelection();
+ macroTable.addRowSelectionInterval(row, row);
+ macroTable.scrollRectToVisible(
+ macroTable.getCellRect(row, 0, false));
+ }
+ }
+ };
+
+ public CreateMacroDialog(Frame f, int mode) {
+ super(f);
+ setLocationRelativeTo(f);
+
+ listeners = new Vector();
+
+ this.mode = mode;
+ switch (mode) {
+ case CREATE:
+ setTitle(ResourceStrings.getString("create_macro_title"));
+ break;
+ case EDIT:
+ setTitle(ResourceStrings.getString("edit_macro_title"));
+ break;
+ case DUPLICATE:
+ setTitle(ResourceStrings.getString("duplicate_macro_title"));
+ break;
+ default:
+ break;
+ }
+
+ getContentPane().setLayout(new BoxLayout(getContentPane(),
+ BoxLayout.Y_AXIS));
+ JPanel mainPanel = new JPanel(new BorderLayout());
+ mainPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
+
+ // Name cannot have blanks in it so use a control which disallows them
+ name = new MacroNameField("", 30);
+ JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT));
+
+ Mnemonic mnName =
+ new Mnemonic(ResourceStrings.getString("md_name_label"));
+ JLabel nameLbl = new JLabel(mnName.getString());
+ nameLbl.setLabelFor(name);
+ nameLbl.setToolTipText(mnName.getString());
+ nameLbl.setDisplayedMnemonic(mnName.getMnemonic());
+ panel.add(nameLbl);
+
+ panel.add(name);
+ mainPanel.add(panel, BorderLayout.NORTH);
+
+ JPanel contentsPanel = new JPanel();
+ contentsPanel.setLayout(new BorderLayout());
+ // Put a titled border on the contents panel
+ Border b = BorderFactory.createCompoundBorder(
+ BorderFactory.createLineBorder(Color.black),
+ BorderFactory.createEmptyBorder(5, 10, 5, 10));
+ contentsPanel.setBorder(BorderFactory.createTitledBorder(b,
+ ResourceStrings.getString("contents_label")));
+
+ /*
+ * Create a panel using a couple of text fields to edit the options
+ * included in the macro
+ */
+ JPanel fieldPanel = new JPanel(new FieldLayout());
+ // Field for option name
+
+ panel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0));
+ optionName = new JTextField("", 20);
+ Mnemonic mnOptName =
+ new Mnemonic(ResourceStrings.getString("md_option_name"));
+ JLabel optNameLbl =
+ new JLabel(mnOptName.getString());
+ fieldPanel.add(FieldLayout.LABEL, optNameLbl);
+ optNameLbl.setLabelFor(optionName);
+ optNameLbl.setToolTipText(mnOptName.getString());
+ optNameLbl.setDisplayedMnemonic(mnOptName.getMnemonic());
+
+ panel.add(optionName);
+ panel.add(Box.createHorizontalStrut(5));
+
+ Mnemonic mnSelect =
+ new Mnemonic(ResourceStrings.getString("select"));
+ selectButton = new JButton(mnSelect.getString());
+ selectButton.setToolTipText(mnSelect.getString());
+ selectButton.setMnemonic(mnSelect.getMnemonic());
+
+ selectButton.addActionListener(listener);
+ panel.add(selectButton);
+ fieldPanel.add(FieldLayout.FIELD, panel);
+
+ // Field for option value
+
+ optionValue = new JTextField();
+
+ Mnemonic mnOptVal =
+ new Mnemonic(ResourceStrings.getString("md_option_value"));
+ JLabel optValLbl = new JLabel(mnOptVal.getString());
+ fieldPanel.add(FieldLayout.LABEL, optValLbl);
+ optValLbl.setLabelFor(optionValue);
+ optValLbl.setToolTipText(mnOptVal.getString());
+ optValLbl.setDisplayedMnemonic(mnOptVal.getMnemonic());
+
+ fieldPanel.add(FieldLayout.FIELD, optionValue);
+
+ // Buttons for add/modify
+
+ Mnemonic mnAdd =
+ new Mnemonic(ResourceStrings.getString("add"));
+ addButton = new JButton(mnAdd.getString());
+ addButton.setToolTipText(mnAdd.getString());
+ addButton.setMnemonic(mnAdd.getMnemonic());
+
+ addButton.addActionListener(listener);
+ addButton.setEnabled(false);
+
+ Mnemonic mnModify =
+ new Mnemonic(ResourceStrings.getString("modify"));
+ modifyButton = new JButton(mnModify.getString());
+ modifyButton.setToolTipText(mnModify.getString());
+ modifyButton.setMnemonic(mnModify.getMnemonic());
+
+ modifyButton.addActionListener(listener);
+ modifyButton.setEnabled(false);
+ panel = new JPanel(new VerticalButtonLayout());
+ panel.add(addButton);
+ panel.add(modifyButton);
+
+ JPanel editPanel = new JPanel(new BorderLayout());
+ editPanel.add(fieldPanel, BorderLayout.WEST);
+ editPanel.add(panel, BorderLayout.EAST);
+ contentsPanel.add(editPanel, BorderLayout.NORTH);
+
+ // Use a table to display the contents of the macro
+ macroTableModel = new MacroTableModel();
+ macroTable = new AutosizingTable(macroTableModel);
+ macroTable.getTableHeader().setReorderingAllowed(false);
+ macroTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+ JScrollPane macroTablePane = new JScrollPane(macroTable);
+ // Resize table as otherwise it asks for a huge area
+ Dimension d = macroTable.getPreferredScrollableViewportSize();
+ d.height = 100;
+ d.width = 300;
+ macroTable.setPreferredScrollableViewportSize(d);
+
+ contentsPanel.add(macroTablePane, BorderLayout.CENTER);
+
+ // Create buttons for controlling table
+ JPanel editButtonPanel = new JPanel(new VerticalButtonLayout());
+
+ upButton = new UpButton();
+ upButton.setEnabled(false);
+ upButton.addActionListener(listener);
+ editButtonPanel.add(upButton);
+
+ downButton = new DownButton();
+ downButton.setEnabled(false);
+ downButton.addActionListener(listener);
+ editButtonPanel.add(downButton);
+
+ Mnemonic mnDelete =
+ new Mnemonic(ResourceStrings.getString("delete"));
+ deleteButton = new JButton(mnDelete.getString());
+ deleteButton.setToolTipText(mnDelete.getString());
+ deleteButton.setMnemonic(mnDelete.getMnemonic());
+
+ deleteButton.setEnabled(false);
+ deleteButton.addActionListener(listener);
+ editButtonPanel.add(deleteButton);
+ contentsPanel.add(editButtonPanel, BorderLayout.EAST);
+
+ mainPanel.add(contentsPanel, BorderLayout.CENTER);
+
+ signalBox = new JCheckBox(ResourceStrings.getString("signal_server"),
+ true);
+ signalBox.setToolTipText(
+ ResourceStrings.getString("signal_server"));
+ signalBox.setHorizontalAlignment(SwingConstants.CENTER);
+ mainPanel.add(signalBox, BorderLayout.SOUTH);
+
+ getContentPane().add(mainPanel);
+ getContentPane().add(new JSeparator());
+
+ buttonPanel = new ButtonPanel(true);
+ buttonPanel.addButtonPanelListener(this);
+ getContentPane().add(buttonPanel);
+
+ // Listen to table selection state and set state of buttons accordingly
+ macroTable.getSelectionModel().addListSelectionListener(
+ new ListSelectionListener() {
+ public void valueChanged(ListSelectionEvent e) {
+ int index = macroTable.getSelectedRow();
+ if (index == -1) {
+ // Nothing selected, disable all
+ upButton.setEnabled(false);
+ downButton.setEnabled(false);
+ deleteButton.setEnabled(false);
+ // Clear the option name we saved
+ savedOptionName = "";
+ } else {
+ if (macroTable.getRowCount() > 0) {
+ /*
+ * Only allow deleteButton to be activated when
+ * the table is not empty regardless of selection
+ * method, mouse or keyboard
+ */
+ deleteButton.setEnabled(true);
+ }
+ if (index == 0) {
+ // First row can't move up
+ upButton.setEnabled(false);
+ } else {
+ upButton.setEnabled(true);
+ }
+ if (index == (macroTable.getRowCount() - 1)) {
+ // Last row can't move down
+ downButton.setEnabled(false);
+ } else {
+ if (macroTable.getRowCount() > 0) {
+ /*
+ * Only allow downButton to be activated when the
+ * table is not empty regardless of selection
+ * method, mouse or keyboard
+ */
+ downButton.setEnabled(true);
+ }
+ }
+ // Save editing name so we can detect name change
+ savedOptionName =
+ (String)macroTableModel.getValueAt(index, 0);
+ optionName.setText(savedOptionName);
+ optionValue.setText(
+ (String)macroTableModel.getValueAt(index, 1));
+ }
+ }
+ });
+
+ // Only enable OK if the name is not empty
+ name.getDocument().addDocumentListener(new DocumentListener() {
+ public void insertUpdate(DocumentEvent e) {
+ buttonPanel.setOkEnabled(e.getDocument().getLength() != 0);
+ }
+ public void changedUpdate(DocumentEvent e) {
+ insertUpdate(e);
+ }
+ public void removeUpdate(DocumentEvent e) {
+ insertUpdate(e);
+ }
+ });
+
+ // Only enable add/modify when option name is non-empty
+ optionName.getDocument().addDocumentListener(new DocumentListener() {
+ public void insertUpdate(DocumentEvent e) {
+ boolean state = (optionName.getDocument().getLength() != 0);
+ addButton.setEnabled(state);
+ if (state == false) {
+ modifyButton.setEnabled(state);
+ } else if (macroTable.getSelectedRowCount() > 0) {
+ modifyButton.setEnabled(state);
+ }
+ }
+ public void changedUpdate(DocumentEvent e) {
+ insertUpdate(e);
+ }
+ public void removeUpdate(DocumentEvent e) {
+ insertUpdate(e);
+ }
+ });
+
+ if (mode == EDIT) {
+ buttonPanel.setOkEnabled(true);
+ }
+ setMacro(new Macro());
+ }
+
+ /**
+ * Display this dialog, and auto-validate its contents to start with
+ */
+ public void setVisible(boolean visible) {
+ super.setVisible(visible);
+ /*
+ * If we're being hidden, then just return
+ */
+ if (!visible)
+ return;
+ /*
+ * Validate the current contents of the macro so we can tell the user
+ * about any syntax errors in the existing definition.
+ */
+ try {
+ macroTableModel.macro.validate();
+ } catch (ValidationException e) {
+ // Errors; advise user by putting up a dialog
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString("bad_option_value"));
+ Object [] args = new Object[2];
+ OptionValue ov = macroTableModel.macro.getOption(e.getMessage());
+ if (ov == null) {
+ args[0] = "";
+ } else {
+ args[0] = ov.getValue();
+ }
+ args[1] = e.getMessage();
+ JOptionPane.showMessageDialog(this, form.format(args),
+ ResourceStrings.getString("server_error_title"),
+ JOptionPane.ERROR_MESSAGE);
+ int row = macroTableModel.findRowForOption(e.getMessage());
+ if (row != -1) {
+ macroTable.clearSelection();
+ macroTable.addRowSelectionInterval(row, row);
+ macroTable.scrollRectToVisible(
+ macroTable.getCellRect(row, 0, false));
+ }
+ }
+ }
+
+ public void setMacro(Macro m) {
+ originalMacro = (Macro)m.clone(); // Keep a copy so we can do a reset
+ if (mode != DUPLICATE) {
+ name.setText(m.getKey());
+ }
+ macroTableModel.setMacro(m);
+ }
+
+ public void buttonPressed(int buttonId) {
+ switch (buttonId) {
+ case OK:
+ // A macro with no options is not useful, so don't allow it
+ if (macroTableModel.getRowCount() == 0) {
+ JOptionPane.showMessageDialog(this,
+ ResourceStrings.getString("empty_macro_error"),
+ ResourceStrings.getString("server_error_title"),
+ JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+ try {
+ macroTableModel.macro.setKey(name.getText());
+ } catch (ValidationException e) {
+ // Not a valid macro name
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString("bad_macro_name"));
+ Object [] args = new Object[] { name.getText() };
+ JOptionPane.showMessageDialog(this, form.format(args),
+ ResourceStrings.getString("server_error_title"),
+ JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+ try {
+ // Validate the macro
+ macroTableModel.macro.validate();
+ DhcptabMgr server = DataManager.get().getDhcptabMgr();
+ if ((mode == CREATE) || (mode == DUPLICATE)) {
+ server.createRecord(macroTableModel.macro,
+ signalBox.isSelected());
+ } else if (mode == EDIT) {
+ server.modifyRecord(originalMacro, macroTableModel.macro,
+ signalBox.isSelected());
+ }
+ fireActionPerformed();
+ setVisible(false);
+ dispose();
+ } catch (ValidationException ve) {
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString("bad_option_value"));
+ Object [] args = new Object[2];
+ OptionValue ov =
+ macroTableModel.macro.getOption(ve.getMessage());
+ if (ov == null) {
+ args[0] = "";
+ } else {
+ args[0] = ov.getValue();
+ }
+ args[1] = ve.getMessage();
+ JOptionPane.showMessageDialog(this, form.format(args),
+ ResourceStrings.getString("server_error_title"),
+ JOptionPane.ERROR_MESSAGE);
+ } catch (NotRunningException e) {
+ // Server not running, put up a warning
+ JOptionPane.showMessageDialog(this,
+ ResourceStrings.getString("server_not_running"),
+ ResourceStrings.getString("warning"),
+ JOptionPane.WARNING_MESSAGE);
+ fireActionPerformed();
+ setVisible(false);
+ dispose();
+ } catch (Exception e) {
+ MessageFormat form = null;
+ Object [] args = new Object[2];
+ switch (mode) {
+ case CREATE:
+ case DUPLICATE:
+ form = new MessageFormat(
+ ResourceStrings.getString("create_macro_error"));
+ args[0] = macroTableModel.macro.getKey();
+ break;
+ case EDIT:
+ form = new MessageFormat(
+ ResourceStrings.getString("edit_macro_error"));
+ args[0] = originalMacro.getKey();
+ break;
+ }
+ args[1] = e.getMessage();
+ JOptionPane.showMessageDialog(this, form.format(args),
+ ResourceStrings.getString("server_error_title"),
+ JOptionPane.ERROR_MESSAGE);
+ }
+ break;
+ case CANCEL:
+ setVisible(false);
+ dispose();
+ break;
+ case HELP:
+ String helpTag = null;
+ switch (mode) {
+ case CREATE:
+ helpTag = "create_macro";
+ break;
+ case DUPLICATE:
+ helpTag = "duplicate_macro";
+ break;
+ case EDIT:
+ helpTag = "modify_macro";
+ break;
+ }
+ DhcpmgrApplet.showHelp(helpTag);
+ break;
+ case RESET:
+ setMacro(originalMacro);
+ signalBox.setSelected(true);
+ break;
+ }
+ }
+
+ public void addActionListener(ActionListener l) {
+ listeners.addElement(l);
+ }
+
+ public void removeActionListener(ActionListener l) {
+ listeners.removeElement(l);
+ }
+
+ protected void fireActionPerformed() {
+ String command = null;
+ switch (mode) {
+ case CREATE:
+ command = DialogActions.CREATE;
+ case DUPLICATE:
+ command = DialogActions.DUPLICATE;
+ break;
+ case EDIT:
+ command = DialogActions.EDIT;
+ break;
+ }
+ ActionEvent e = new ActionEvent(this, ActionEvent.ACTION_PERFORMED,
+ command);
+ Enumeration en = listeners.elements();
+ while (en.hasMoreElements()) {
+ ActionListener l = (ActionListener)en.nextElement();
+ l.actionPerformed(e);
+ }
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/CreateOptionDialog.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/CreateOptionDialog.java
new file mode 100644
index 0000000000..f485cbf448
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/CreateOptionDialog.java
@@ -0,0 +1,716 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 1998-2002 by Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.client;
+
+import javax.swing.*;
+import javax.swing.border.*;
+import javax.swing.event.*;
+import javax.swing.table.*;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.text.MessageFormat;
+import java.util.*;
+
+import com.sun.dhcpmgr.server.*;
+import com.sun.dhcpmgr.data.*;
+import com.sun.dhcpmgr.ui.*;
+import com.sun.dhcpmgr.bridge.NotRunningException;
+
+
+/**
+ * Dialog to create/duplicate/edit an option.
+ */
+public class CreateOptionDialog extends JDialog implements ButtonPanelListener {
+ public static final int CREATE = 0;
+ public static final int EDIT = 1;
+ public static final int DUPLICATE = 2;
+
+ private int mode = CREATE;
+ private OptionNameField name;
+ private JComboBox category;
+ private IntegerField code;
+ private JComboBox type;
+ private JList classList;
+ private JTextField clientClass;
+ private IntegerField granularity;
+ private IntegerField maximum;
+ private JCheckBox signalBox;
+ private Vector listeners;
+ private Option option, originalOption;
+ private ButtonPanel buttonPanel;
+ private ClassListModel classListModel;
+ private JButton add, delete;
+ private UpButton moveUp;
+ private DownButton moveDown;
+ private OptionContext [] categories = {
+ Option.ctxts[Option.EXTEND],
+ Option.ctxts[Option.VENDOR],
+ Option.ctxts[Option.SITE]
+ };
+
+ // Model for the list of vendor classes
+ class ClassListModel extends AbstractListModel {
+
+ public ClassListModel() {
+ super();
+ }
+
+ public int getSize() {
+ return option.getVendorCount();
+ }
+
+ public Object getElementAt(int index) {
+ return option.getVendorAt(index);
+ }
+
+ public void addElement(String v) throws ValidationException {
+ option.addVendor(v);
+ fireIntervalAdded(this, option.getVendorCount()-1,
+ option.getVendorCount()-1);
+ }
+
+ public void removeElementAt(int index) {
+ option.removeVendorAt(index);
+ fireIntervalRemoved(this, index, index);
+ }
+
+ public void moveUp(int index) {
+ String t = (String)option.getVendorAt(index-1);
+ option.setVendorAt(option.getVendorAt(index), index-1);
+ option.setVendorAt(t, index);
+ fireContentsChanged(this, index-1, index);
+ }
+
+ public void moveDown(int index) {
+ String t = (String)option.getVendorAt(index+1);
+ option.setVendorAt(option.getVendorAt(index), index+1);
+ option.setVendorAt(t, index);
+ fireContentsChanged(this, index, index+1);
+ }
+
+ public void reset() {
+ fireContentsChanged(this, 0, getSize());
+ }
+ }
+
+ public CreateOptionDialog(Frame f, int mode) {
+ super(f);
+ setLocationRelativeTo(f);
+ JPanel classPanel;
+
+ listeners = new Vector();
+
+ this.mode = mode;
+ switch (mode) {
+ case CREATE:
+ setTitle(ResourceStrings.getString("create_option_title"));
+ break;
+ case EDIT:
+ setTitle(ResourceStrings.getString("edit_option_title"));
+ break;
+ case DUPLICATE:
+ setTitle(ResourceStrings.getString("duplicate_option_title"));
+ break;
+ default:
+ break;
+ }
+
+ getContentPane().setLayout(new BoxLayout(getContentPane(),
+ BoxLayout.Y_AXIS));
+
+ JPanel mainPanel = new JPanel(new BorderLayout());
+ mainPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
+
+ JPanel fieldPanel = new JPanel();
+ GridBagLayout bag = new GridBagLayout();
+ fieldPanel.setLayout(bag);
+
+ // Initialize constraints
+ GridBagConstraints c = new GridBagConstraints();
+ c.gridx = c.gridy = 0;
+ c.gridwidth = c.gridheight = 1;
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.insets = new Insets(5, 5, 5, 5);
+ c.weightx = c.weighty = 1.0;
+
+ // Label and text field for name
+ Mnemonic mnOname =
+ new Mnemonic(ResourceStrings.getString("op_name"));
+ JLabel l = new JLabel(mnOname.getString(), SwingConstants.RIGHT);
+
+ bag.setConstraints(l, c);
+ fieldPanel.add(l);
+ name = new OptionNameField("");
+
+ l.setLabelFor(name);
+ l.setToolTipText(mnOname.getString());
+ l.setDisplayedMnemonic(mnOname.getMnemonic());
+
+ l.setLabelFor(name);
+ ++c.gridx;
+ bag.setConstraints(name, c);
+ fieldPanel.add(name);
+
+ // Label and combo box for category
+ Mnemonic mnCat =
+ new Mnemonic(ResourceStrings.getString("category_label"));
+ l = new JLabel(mnCat.getString(), SwingConstants.RIGHT);
+
+ c.gridx = 0;
+ ++c.gridy;
+ bag.setConstraints(l, c);
+ fieldPanel.add(l);
+ category = new JComboBox(categories);
+
+ l.setLabelFor(category);
+ l.setToolTipText(mnCat.getString());
+ l.setDisplayedMnemonic(mnCat.getMnemonic());
+
+ category.setEditable(false);
+ ++c.gridx;
+ bag.setConstraints(category, c);
+ fieldPanel.add(category);
+
+ // Label and text field for code
+ Mnemonic mnCode =
+ new Mnemonic(ResourceStrings.getString("option_code_label"));
+ l = new JLabel(mnCode.getString(), SwingConstants.RIGHT);
+
+ c.gridx = 0;
+ ++c.gridy;
+ bag.setConstraints(l, c);
+ fieldPanel.add(l);
+ code = new IntegerField();
+
+ l.setLabelFor(code);
+ l.setToolTipText(mnCode.getString());
+ l.setDisplayedMnemonic(mnCode.getMnemonic());
+
+ ++c.gridx;
+ bag.setConstraints(code, c);
+ fieldPanel.add(code);
+
+ // Label and combo box for data type
+ Mnemonic mnType =
+ new Mnemonic(ResourceStrings.getString("data_type_label"));
+ l = new JLabel(mnType.getString(), SwingConstants.RIGHT);
+
+ c.gridx = 0;
+ ++c.gridy;
+ bag.setConstraints(l, c);
+ fieldPanel.add(l);
+ type = new JComboBox(Option.types);
+
+ l.setLabelFor(type);
+ l.setToolTipText(mnType.getString());
+ l.setDisplayedMnemonic(mnType.getMnemonic());
+
+ type.setEditable(false);
+ ++c.gridx;
+ bag.setConstraints(type, c);
+ fieldPanel.add(type);
+
+ // Label and text field for granularity
+ Mnemonic mnGran =
+ new Mnemonic(ResourceStrings.getString("granularity_label"));
+ l = new JLabel(mnGran.getString(), SwingConstants.RIGHT);
+
+ c.gridx = 0;
+ ++c.gridy;
+ bag.setConstraints(l, c);
+ fieldPanel.add(l);
+ granularity = new IntegerField(5);
+
+ l.setLabelFor(granularity);
+ l.setToolTipText(mnGran.getString());
+ l.setDisplayedMnemonic(mnGran.getMnemonic());
+
+ ++c.gridx;
+ bag.setConstraints(granularity, c);
+ fieldPanel.add(granularity);
+
+ // Label and text field for maximum
+ Mnemonic mnMax =
+ new Mnemonic(ResourceStrings.getString("maximum_label"));
+ l = new JLabel(mnMax.getString(), SwingConstants.RIGHT);
+
+ c.gridx = 0;
+ ++c.gridy;
+ bag.setConstraints(l, c);
+ fieldPanel.add(l);
+ maximum = new IntegerField(5);
+
+ l.setLabelFor(maximum);
+ l.setToolTipText(mnMax.getString());
+ l.setDisplayedMnemonic(mnMax.getMnemonic());
+
+ ++c.gridx;
+ bag.setConstraints(maximum, c);
+ fieldPanel.add(maximum);
+
+ mainPanel.add(fieldPanel, BorderLayout.WEST);
+
+ // Editing controls for client classes
+ bag = new GridBagLayout();
+ classPanel = new JPanel(bag);
+ Border tb = BorderFactory.createTitledBorder(
+ BorderFactory.createLineBorder(Color.black),
+ ResourceStrings.getString("client_classes_label"));
+ classPanel.setBorder(BorderFactory.createCompoundBorder(tb,
+ BorderFactory.createEmptyBorder(5, 5, 5, 5)));
+
+ c = new GridBagConstraints();
+ c.gridx = c.gridy = 0;
+ c.weightx = c.weighty = 1.0;
+ c.gridheight = 1;
+ c.gridwidth = 1;
+
+ // Field to type in new classes
+ clientClass = new JTextField("", 20);
+ c.fill = GridBagConstraints.HORIZONTAL;
+ bag.setConstraints(clientClass, c);
+ classPanel.add(clientClass);
+
+ // Button for Add operation
+ Mnemonic mnAdd =
+ new Mnemonic(ResourceStrings.getString("add"));
+ add = new JButton(mnAdd.getString());
+ add.setToolTipText(mnAdd.getString());
+ add.setMnemonic(mnAdd.getMnemonic());
+
+ c.fill = GridBagConstraints.NONE;
+ ++c.gridx;
+ c.weightx = 0.5;
+ bag.setConstraints(add, c);
+ classPanel.add(add);
+
+ // List for classes
+ classListModel = new ClassListModel();
+ classList = new JList(classListModel);
+
+ // Make sure it's approximately wide enough for our purposes, 20 chars
+ classList.setPrototypeCellValue("abcdefghijklmnopqrst");
+ classList.setSelectionMode(
+ ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
+ JScrollPane scrollPane = new JScrollPane(classList);
+ c.fill = GridBagConstraints.BOTH;
+ c.gridx = 0;
+ ++c.gridy;
+ c.weightx = 1.0;
+ bag.setConstraints(scrollPane, c);
+ classPanel.add(scrollPane);
+
+ // Buttons to manipulate the list contents
+ JPanel editButtonPanel = new JPanel(new VerticalButtonLayout());
+ moveUp = new UpButton();
+ editButtonPanel.add(moveUp);
+ moveDown = new DownButton();
+ editButtonPanel.add(moveDown);
+
+ Mnemonic mnDelete =
+ new Mnemonic(ResourceStrings.getString("delete"));
+ delete = new JButton(mnDelete.getString());
+ delete.setToolTipText(mnDelete.getString());
+ delete.setMnemonic(mnDelete.getMnemonic());
+
+ editButtonPanel.add(delete);
+ ++c.gridx;
+ c.weightx = 0.5;
+ bag.setConstraints(editButtonPanel, c);
+ classPanel.add(editButtonPanel);
+
+ /*
+ * Disable all buttons to start; selection changes will adjust button
+ * state as necessary
+ */
+ add.setEnabled(false);
+ delete.setEnabled(false);
+ moveUp.setEnabled(false);
+ moveDown.setEnabled(false);
+
+ // Create listener for button presses, take action as needed
+ ActionListener al = new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ if (e.getSource() == add || e.getSource() == clientClass) {
+ try {
+ classListModel.addElement(clientClass.getText());
+ } catch (ValidationException ex) {
+ // Something wrong with class name
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString("invalid_client_class"));
+ Object [] args = new Object[] { clientClass.getText() };
+ JOptionPane.showMessageDialog(CreateOptionDialog.this,
+ form.format(args),
+ ResourceStrings.getString("input_error"),
+ JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+ } else if (e.getSource() == delete) {
+ int [] indices = classList.getSelectedIndices();
+ if (indices.length > 1) {
+ /*
+ * Need to sort them so that the delete's don't
+ * interfere with each other
+ */
+ for (int i = 0; i < indices.length; ++i) {
+ for (int j = i; j < indices.length; ++j) {
+ if (indices[i] > indices[j]) {
+ int k = indices[i];
+ indices[i] = indices[j];
+ indices[j] = k;
+ }
+ }
+ }
+ }
+ // Now delete from high index to low
+ for (int i = indices.length - 1; i >= 0; --i) {
+ classListModel.removeElementAt(indices[i]);
+ }
+ if (indices.length > 1) {
+ // Clear selection if multiple deleted
+ classList.clearSelection();
+ /*
+ * XXX We don't get a selection event for some reason,
+ * make it work for now
+ */
+ delete.setEnabled(false);
+ } else {
+ // Make sure to select something in the list
+ if (classListModel.getSize() == 0) {
+ // List is empty, so disable delete
+ delete.setEnabled(false);
+ } else if (indices[0] >= classListModel.getSize()) {
+ // Select last one if we're off the end
+ classList.setSelectedIndex(
+ classListModel.getSize()-1);
+ } else {
+ // Select next one in list
+ classList.setSelectedIndex(indices[0]);
+ }
+ }
+ } else if (e.getSource() == moveUp) {
+ int i = classList.getSelectedIndex();
+ classListModel.moveUp(i);
+ // Keep item selected so repeated moveUp's affect same item
+ classList.setSelectedIndex(i-1);
+ } else if (e.getSource() == moveDown) {
+ int i = classList.getSelectedIndex();
+ classListModel.moveDown(i);
+ // Keep item selected so repeated moveDowns affect same item
+ classList.setSelectedIndex(i+1);
+ }
+ }
+ };
+ clientClass.addActionListener(al);
+ add.addActionListener(al);
+ delete.addActionListener(al);
+ moveUp.addActionListener(al);
+ moveDown.addActionListener(al);
+
+ // Put a selection listener on the list to enable buttons appropriately
+ classList.addListSelectionListener(new ListSelectionListener() {
+ public void valueChanged(ListSelectionEvent e) {
+ int [] indices = classList.getSelectedIndices();
+ switch (indices.length) {
+ case 0:
+ // Nothing selected; disable them all
+ delete.setEnabled(false);
+ moveUp.setEnabled(false);
+ moveDown.setEnabled(false);
+ break;
+ case 1:
+ delete.setEnabled(true);
+ // Can't move first one up
+ moveUp.setEnabled(indices[0] != 0);
+ // Can't move last one down
+ if (indices[0] == (classListModel.getSize() - 1)) {
+ moveDown.setEnabled(false);
+ } else {
+ moveDown.setEnabled(true);
+ }
+ break;
+ default:
+ // More than one; only delete is allowed
+ delete.setEnabled(true);
+ moveUp.setEnabled(false);
+ moveDown.setEnabled(false);
+ }
+ }
+ });
+ // Enable Add when class is not empty.
+ clientClass.getDocument().addDocumentListener(new DocumentListener() {
+ public void insertUpdate(DocumentEvent e) {
+ add.setEnabled(clientClass.getText().length() != 0);
+ }
+ public void changedUpdate(DocumentEvent e) {
+ insertUpdate(e);
+ }
+ public void removeUpdate(DocumentEvent e) {
+ insertUpdate(e);
+ }
+ });
+
+ mainPanel.add(classPanel, BorderLayout.CENTER);
+
+ signalBox = new JCheckBox(ResourceStrings.getString("signal_server"),
+ true);
+ signalBox.setToolTipText(
+ ResourceStrings.getString("signal_server"));
+ signalBox.setHorizontalAlignment(SwingConstants.CENTER);
+ JPanel signalPanel = new JPanel();
+ signalPanel.add(signalBox);
+ mainPanel.add(signalPanel, BorderLayout.SOUTH);
+
+ getContentPane().add(mainPanel);
+ getContentPane().add(new JSeparator());
+
+ buttonPanel = new ButtonPanel(true);
+ buttonPanel.addButtonPanelListener(this);
+ getContentPane().add(buttonPanel);
+
+ setOption(new Option());
+
+ if (mode == EDIT) {
+ buttonPanel.setOkEnabled(true);
+ }
+
+ // Enable OK when there is data in the name field
+ name.getDocument().addDocumentListener(new DocumentListener() {
+ public void insertUpdate(DocumentEvent e) {
+ buttonPanel.setOkEnabled(e.getDocument().getLength() != 0);
+ }
+ public void changedUpdate(DocumentEvent e) {
+ insertUpdate(e);
+ }
+ public void removeUpdate(DocumentEvent e) {
+ insertUpdate(e);
+ }
+ });
+
+ // If category != VENDOR you can't mess with the client class data
+ category.addItemListener(new ItemListener() {
+ public void itemStateChanged(ItemEvent e) {
+ OptionContext ctxt = categories[category.getSelectedIndex()];
+ boolean isVendor =
+ (ctxt.getCode() == Option.ctxts[Option.VENDOR].getCode());
+ if (!isVendor) {
+ option.clearVendors();
+ clientClass.setText("");
+ }
+ clientClass.setEnabled(isVendor);
+ classList.setEnabled(isVendor);
+ }
+ });
+
+ // Update state of granularity & maximum depending on data type selected
+ type.addItemListener(new ItemListener() {
+ public void itemStateChanged(ItemEvent e) {
+ OptionType stype = Option.types[type.getSelectedIndex()];
+ byte code = stype.getCode();
+ // Set granularity to correct minimum for type
+ if (code == Option.types[Option.BOOLEAN].getCode()) {
+ granularity.setText("0");
+ } else if ("0".equals(granularity.getText())) {
+ granularity.setText("1");
+ }
+ // Now set editability of the granularity and max fields
+ if (code == Option.types[Option.ASCII].getCode() ||
+ code == Option.types[Option.OCTET].getCode()) {
+ granularity.setEditable(false);
+ maximum.setEditable(true);
+ } else if (code == Option.types[Option.BOOLEAN].getCode()) {
+ granularity.setEditable(false);
+ // Also reset maximum value in this case
+ maximum.setText("0");
+ maximum.setEditable(false);
+ } else if (code == Option.types[Option.NUMBER].getCode() ||
+ code == Option.types[Option.UNUMBER8].getCode() ||
+ code == Option.types[Option.UNUMBER16].getCode() ||
+ code == Option.types[Option.UNUMBER32].getCode() ||
+ code == Option.types[Option.UNUMBER64].getCode() ||
+ code == Option.types[Option.SNUMBER8].getCode() ||
+ code == Option.types[Option.SNUMBER16].getCode() ||
+ code == Option.types[Option.SNUMBER32].getCode() ||
+ code == Option.types[Option.SNUMBER64].getCode() ||
+ code == Option.types[Option.IP].getCode()) {
+ granularity.setEditable(true);
+ maximum.setEditable(true);
+ }
+ }
+ });
+ }
+
+ public void setOption(Option o) {
+ originalOption = o; // Keep a copy so reset will work
+ option = (Option)o.clone();
+ resetValues();
+ }
+
+ private void resetValues() {
+ if (mode == DUPLICATE) {
+ name.setText("");
+ } else {
+ name.setText(option.getKey());
+ }
+ for (int i = 0; i < categories.length; i++) {
+ if (categories[i].getCode() == option.getContext()) {
+ category.setSelectedIndex(i);
+ break;
+ }
+ }
+
+ for (int i = 0; i < Option.types.length; i++) {
+ if (Option.types[i].getCode() == option.getType()) {
+ type.setSelectedIndex(i);
+ break;
+ }
+ }
+
+ code.setValue(option.getCode());
+ granularity.setValue(option.getGranularity());
+ maximum.setValue(option.getMaximum());
+ classListModel.reset();
+ signalBox.setSelected(true);
+ }
+
+ public void buttonPressed(int buttonId) {
+ switch (buttonId) {
+ case OK:
+ try {
+ OptionContext sctxt = categories[category.getSelectedIndex()];
+ OptionType stype = Option.types[type.getSelectedIndex()];
+ option.setKey(name.getText());
+ option.setContext(sctxt.getCode());
+ option.setCode((short)code.getValue());
+ option.setType(stype.getCode());
+ option.setGranularity(granularity.getValue());
+ option.setMaximum(maximum.getValue());
+ if (sctxt.getCode() == Option.ctxts[Option.VENDOR].getCode() &&
+ option.getVendorCount() == 0) {
+ JOptionPane.showMessageDialog(this,
+ ResourceStrings.getString("empty_vendor_error"),
+ ResourceStrings.getString("server_error_title"),
+ JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+ DhcptabMgr server = DataManager.get().getDhcptabMgr();
+ if ((mode == CREATE) || (mode == DUPLICATE)) {
+ server.createRecord(option, signalBox.isSelected());
+ } else if (mode == EDIT) {
+ server.modifyRecord(originalOption, option,
+ signalBox.isSelected());
+ }
+ fireActionPerformed();
+ setVisible(false);
+ dispose();
+ } catch (NotRunningException e) {
+ // Server not running, put up a warning
+ JOptionPane.showMessageDialog(this,
+ ResourceStrings.getString("server_not_running"),
+ ResourceStrings.getString("warning"),
+ JOptionPane.WARNING_MESSAGE);
+ fireActionPerformed();
+ setVisible(false);
+ dispose();
+ } catch (Exception e) {
+ MessageFormat form = null;
+ Object [] args = new Object[2];
+ switch (mode) {
+ case CREATE:
+ case DUPLICATE:
+ form = new MessageFormat(
+ ResourceStrings.getString("create_option_error"));
+ args[0] = option.getKey();
+ break;
+ case EDIT:
+ form = new MessageFormat(
+ ResourceStrings.getString("edit_option_error"));
+ args[0] = originalOption.getKey();
+ break;
+ }
+ args[1] = e.getMessage();
+ JOptionPane.showMessageDialog(this, form.format(args),
+ ResourceStrings.getString("server_error_title"),
+ JOptionPane.ERROR_MESSAGE);
+ }
+ break;
+ case CANCEL:
+ setVisible(false);
+ dispose();
+ break;
+ case HELP:
+ String helpTag = null;
+ switch (mode) {
+ case CREATE:
+ helpTag = "create_option";
+ break;
+ case DUPLICATE:
+ helpTag = "duplicate_option";
+ break;
+ case EDIT:
+ helpTag = "modify_option";
+ break;
+ }
+ DhcpmgrApplet.showHelp(helpTag);
+ break;
+ case RESET:
+ setOption(originalOption);
+ break;
+ }
+ }
+
+ public void addActionListener(ActionListener l) {
+ listeners.addElement(l);
+ }
+
+ public void removeActionListener(ActionListener l) {
+ listeners.removeElement(l);
+ }
+
+ protected void fireActionPerformed() {
+ String command = null;
+ switch (mode) {
+ case CREATE:
+ command = DialogActions.CREATE;
+ case DUPLICATE:
+ command = DialogActions.DUPLICATE;
+ break;
+ case EDIT:
+ command = DialogActions.EDIT;
+ break;
+ }
+ ActionEvent e = new ActionEvent(this, ActionEvent.ACTION_PERFORMED,
+ command);
+ Enumeration en = listeners.elements();
+ while (en.hasMoreElements()) {
+ ActionListener l = (ActionListener)en.nextElement();
+ l.actionPerformed(e);
+ }
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DSModule.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DSModule.java
new file mode 100644
index 0000000000..b4999bb1b4
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DSModule.java
@@ -0,0 +1,145 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.client;
+
+import java.awt.*;
+import java.util.*;
+import javax.swing.event.EventListenerList;
+
+/**
+ * This class provides a skeletal implementation of the data store module
+ * management interface to minimize the effort required to implement a
+ * data store management class.
+ */
+public abstract class DSModule {
+
+ /**
+ * Listeners registered with the DSModule.
+ */
+ private EventListenerList DSMListeners = new EventListenerList();
+
+ /**
+ * Attribute that signifies whether or not the data store has been
+ * configured well enough to allow the data store to be managed.
+ */
+ private boolean forwardEnabled = false;
+
+ /**
+ * Returns the path that is used by the data store (i.e., the PATH value
+ * in the DHCP config file).
+ * @return the path that is used by the data store (i.e., the PATH value
+ * in the DHCP config file) or null if not set.
+ */
+ public abstract String getPath();
+
+ /**
+ * Returns additional datastore specific information (i.e., the
+ * RESOURCE_CONFIG value in the DHCP config file).
+ * @return additional datastore specific information (i.e., the
+ * RESOURCE_CONFIG value in the DHCP config file) or null if not set.
+ */
+ public abstract String getAdditionalInfo();
+
+ /**
+ * Returns the description that will be used by the DHCP configuration
+ * wizard when adding the data store to the list of data store radio
+ * buttons.
+ * @return the description that will be used by the DHCP configuration
+ * wizard when adding the data store to the list of data store radio
+ * buttons.
+ */
+ public abstract String getDescription();
+
+ /**
+ * Returns the component that will be used by the DHCP configuration
+ * wizard to manage obtaining the data store parameters.
+ * @return the component that will be used by the DHCP configuration
+ * wizard to manage obtaining the data store parameters.
+ */
+ public abstract Component getComponent();
+
+ /**
+ * Adds a listener to the DSModule listener list.
+ * @param l the listener.
+ */
+ public void addDSMListener(DSModuleListener l) {
+ DSMListeners.add(DSModuleListener.class, l);
+ } // addDSMListener
+
+ /**
+ * Removes a listener from the DSModule listener list.
+ * @param l the listener.
+ */
+ public void removeDSMListener(DSModuleListener l) {
+ DSMListeners.remove(DSModuleListener.class, l);
+ } // removeDSMListener
+
+ /**
+ * Fires a DSModuleEvent to all DSModule listeners on the listener list.
+ * @param e the DSModuleEvent to be fired.
+ */
+ private void fireDSMEvent(DSModuleEvent e) {
+ // Guaranteed to return a non-null array
+ Object[] listeners = DSMListeners.getListenerList();
+
+ // Process the listeners last to first, notifying
+ // those that are interested in this event
+ for (int i = listeners.length - 2; i >= 0; i -= 2) {
+ if (listeners[i] == DSModuleListener.class) {
+ ((DSModuleListener)listeners[i + 1]).stateChanged(e);
+ }
+ }
+ } // fireDSMEvent
+
+ /**
+ * Returns the modules readiness state (i.e., can the DHCP config wizard
+ * continue forward if the user wishes).
+ * @return the modules readiness state
+ */
+ public final boolean getForwardEnabled() {
+ return forwardEnabled;
+ } // getForwardEnabled
+
+ /**
+ * Sets the forwardEnabled attribute and fires a DSModuleEvent to the DHCP
+ * configuration wizard to let it know that the module's state has changed.
+ * @param enable value to which forwardEnabled should be set.
+ */
+ public final void setForwardEnabled(boolean enable) {
+
+ forwardEnabled = enable;
+
+ int state = DSModuleEvent.DATA_VALID;
+ if (!enable) {
+ state = DSModuleEvent.DATA_INVALID;
+ }
+
+ fireDSMEvent(new DSModuleEvent(this, state));
+ } // setForwardEnabled
+
+} // DSModule
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DSModuleEvent.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DSModuleEvent.java
new file mode 100644
index 0000000000..35b43a041c
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DSModuleEvent.java
@@ -0,0 +1,71 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.client;
+
+import java.util.*;
+
+/**
+ * This class defines a DSModule event. These events are the means by which
+ * a DSModule communicates with the DHCP configuration wizard.
+ */
+public class DSModuleEvent extends EventObject {
+
+ /**
+ * Signifies that the DSModule configuration data is valid.
+ */
+ public static final int DATA_VALID = 0;
+
+ /**
+ * Signifies that the DSModule configuration data is not valid.
+ */
+ public static final int DATA_INVALID = 1;
+
+ /**
+ * Set to DATA_VALID or DATA_INVALID.
+ */
+ private int state;
+
+ /**
+ * Constructs a DSModuleEvent from a source and state.
+ * @param source module that is source of the event.
+ * @param state DATA_VALID or DATA_INVALID.
+ */
+ public DSModuleEvent(Object source, int state) {
+ super(source);
+ this.state = state;
+ } // constructor
+
+ /**
+ * Returns the state of the event.
+ * @return the state of the event.
+ */
+ public int getState() {
+ return state;
+ }// getState
+
+} // DSModuleEvent
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DSModuleListener.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DSModuleListener.java
new file mode 100644
index 0000000000..785b11eb93
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DSModuleListener.java
@@ -0,0 +1,44 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.client;
+
+import java.util.*;
+
+/**
+ * This class defines a listener interface such that events may be received
+ * from the DSModule classes.
+ */
+public interface DSModuleListener extends EventListener {
+
+ /**
+ * Method called when the state of a DSModule changes.
+ * @param e the event
+ */
+ public void stateChanged(DSModuleEvent e);
+
+} // DSModuleListner
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DSWizard.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DSWizard.java
new file mode 100644
index 0000000000..2ab789a1ce
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DSWizard.java
@@ -0,0 +1,436 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.client;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+import java.text.MessageFormat;
+
+import javax.swing.*;
+
+import com.sun.dhcpmgr.data.DhcpDatastore;
+import com.sun.dhcpmgr.server.*;
+import com.sun.dhcpmgr.ui.*;
+
+/**
+ * This class defines a Wizard that configures a data store.
+ */
+public abstract class DSWizard extends Wizard implements DSModuleListener {
+
+ /**
+ * The collection of valid DSConf objects.
+ */
+ protected DSConfList dsconfList = null;
+
+ /**
+ * The selected DSConf
+ */
+ private DSConf dsconf = null;
+
+ /**
+ * This class is a holder for the set of DSConf objects.
+ */
+ protected class DSConfList extends ArrayList {
+
+ /**
+ * Initializes the set of supported DSConfList.
+ * @param server handle to a service manager server
+ */
+ public void init(DhcpServiceMgr server) {
+
+ DhcpDatastore [] dsArray = null;
+
+ try {
+ dsArray = server.getDataStores();
+ } catch (Throwable e) {
+ // ignore for now
+ }
+
+ for (int i = 0;
+ dsArray != null && i < dsArray.length; i++) {
+ String dsResource = dsArray[i].getResource();
+ try {
+
+ String className = server.getDataStoreClassname(dsResource);
+ DSConf dsconf = new DSConf(dsArray[i], className);
+ dsconfList.add(dsconf);
+ } catch (Throwable e) {
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString("ds_wiz_init_error"));
+ Object args = new Object[] {
+ dsResource,
+ e.getMessage()
+ };
+ JOptionPane.showMessageDialog(DSWizard.this,
+ form.format(args),
+ ResourceStrings.getString("server_error_title"),
+ JOptionPane.ERROR_MESSAGE);
+ }
+ }
+ } // init
+
+ /**
+ * Finds and returns a DSConf by resource.
+ * @param resource the resource of the DSConf.
+ * @return the DSConf if found or null if not found.
+ */
+ public DSConf findDsconf(String resource) {
+
+ DSConf entry = null;
+ for (int i = 0; i < size(); i++) {
+ DSConf dsconf = (DSConf) get(i);
+ if (dsconf.getDS().getResource().equals(resource)) {
+ entry = dsconf;
+ break;
+ }
+ }
+
+ return entry;
+
+ } // findDSConf
+
+ } // DSConfList
+
+ /**
+ * This class is a simple holder for a data store
+ * and the module used to manage the data store.
+ */
+ protected class DSConf {
+
+ /**
+ * DHCP datastore information.
+ */
+ private DhcpDatastore ds = null;
+
+ /**
+ * The module used to manage the data store.
+ */
+ private DSModule dsm = null;
+
+ /**
+ * Constructs a DSConf from a name and a classname.
+ * @param ds DHCP data store.
+ * @param className of the DSModule classname.
+ */
+ public DSConf(DhcpDatastore ds, String className)
+ throws Exception {
+
+ Class dataStoreClass = Class.forName(className);
+
+ dsm = (DSModule)dataStoreClass.newInstance();
+ this.ds = ds;
+ } // constructor
+
+ /**
+ * Returns the DhcpDatastore for this DSConf
+ * @return the DhcpDatastore for this DSConf
+ */
+ public DhcpDatastore getDS() {
+ return ds;
+ } // getDS
+
+ /**
+ * Returns the module used to manage the data store.
+ * @return the module used to manage the data store.
+ */
+ public DSModule getModule() {
+ return dsm;
+ } // getModule
+
+
+ /**
+ * Sets the location from the module into the DhcpDatastore.
+ */
+ public void setLocation() {
+ ds.setLocation(dsm.getPath());
+ } // setLocation
+
+ /**
+ * Sets the location from the module into the DhcpDatastore.
+ */
+ public void setConfig() {
+ ds.setConfig(dsm.getAdditionalInfo());
+ } // setConfig
+
+ } // DSConf
+
+ /**
+ * This class maps a radio button and a DSConf.
+ */
+ private class DSConfButton extends JRadioButton {
+
+ /**
+ * The data store to link to the radio button.
+ */
+ DSConf dsconf = null;
+
+ /**
+ * Constructs a DSConfButton from a DSConf and determines
+ * whether the button should be selected using the boolean argument.
+ * @param dsconf the data store to map to the radio button.
+ * @param selected select the radio button?
+ */
+ public DSConfButton(DSConf dsconf, boolean selected) {
+ super(dsconf.getModule().getDescription(), selected);
+ setEnabled(dsconf.getDS().isEnabled());
+ this.dsconf = dsconf;
+ } // constructor
+
+ /**
+ * Returns the DSConf mapped to the radio button.
+ * @return the DSConf mapped to the radio button.
+ */
+ public DSConf getDsconf() {
+ return dsconf;
+ } // getDsconf
+
+ } // DSConfButton
+
+
+ /**
+ * This class is the wizard step that presents the choice of
+ * data stores to the user for selection.
+ */
+ protected class DatastoreStep implements WizardStep {
+
+ /**
+ * The component provided to the wizard.
+ */
+ private Box stepBox;
+
+ /**
+ * The group of DSConfButton objects.
+ */
+ private ButtonGroup buttonGroup;
+
+ /**
+ * The basic constructor for the wizard step.
+ * @param wizardText the main explanatory text for the wizard.
+ * @param stepText the explanatory text for the step.
+ */
+ public DatastoreStep(String wizardText, String stepText) {
+
+ super();
+
+ stepBox = Box.createVerticalBox();
+
+ // Explanatory wizard intro text
+ //
+ JComponent c = Wizard.createTextArea(wizardText, 2, 45);
+ c.setAlignmentX(Component.LEFT_ALIGNMENT);
+ stepBox.add(c);
+ stepBox.add(Box.createVerticalStrut(5));
+
+ // Explanatory step text
+ //
+ c = Wizard.createTextArea(stepText, 3, 45);
+ c.setAlignmentX(Component.LEFT_ALIGNMENT);
+ stepBox.add(c);
+ stepBox.add(Box.createVerticalStrut(5));
+
+ // Create button listener, that will set the selected
+ // data store when the button is selected.
+ //
+ ActionListener buttonListener = new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ DSConfButton button = (DSConfButton)e.getSource();
+ if (button.isSelected()) {
+ setDsconf(button.getDsconf());
+ }
+ }
+ };
+
+ // Create panel that will contain the buttons.
+ //
+ JPanel boxPanel = new JPanel();
+ boxPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
+ boxPanel.setLayout(new BoxLayout(boxPanel, BoxLayout.Y_AXIS));
+ boxPanel.setBorder(BorderFactory.createEmptyBorder(0, 20, 0, 20));
+
+ // List data store choices.
+ //
+ buttonGroup = new ButtonGroup();
+ for (int i = 0; i < dsconfList.size(); ++i) {
+ DSConf dsconf = (DSConf)dsconfList.get(i);
+ DSConfButton radioButton =
+ new DSConfButton(dsconf, false);
+ radioButton.setAlignmentX(Component.LEFT_ALIGNMENT);
+ radioButton.addActionListener(buttonListener);
+ buttonGroup.add(radioButton);
+ boxPanel.add(radioButton);
+ }
+
+ // Add the panel to the stepBox component.
+ //
+ stepBox.add(boxPanel);
+ stepBox.add(Box.createVerticalStrut(20));
+ stepBox.add(Box.createVerticalGlue());
+
+ } // constructor
+
+ public String getDescription() {
+ return ResourceStrings.getString("ds_wiz_datastore_desc");
+ } // getDescription
+
+ public Component getComponent() {
+ return stepBox;
+ } // getComponent
+
+ public void setActive(int direction) {
+ if (getDsconf() != null) {
+ setForwardEnabled(true);
+ } else {
+ setForwardEnabled(false);
+ }
+ } // setActive
+
+ public boolean setInactive(int direction) {
+ return true;
+ } // setInactive
+
+
+ public void enableButton(String resource, boolean enable) {
+
+ DSConfButton button = null;
+ Enumeration en = buttonGroup.getElements();
+ while (en.hasMoreElements()) {
+ DSConfButton enButton = (DSConfButton)en.nextElement();
+ DSConf DSConf = enButton.getDsconf();
+ if (dsconf.getDS().getResource().equals(resource)) {
+ button = enButton;
+ break;
+ }
+ }
+
+ if (button != null) {
+ button.setEnabled(enable);
+ }
+
+ } // enableButton
+
+ } // DatastoreStep
+
+ /**
+ * This class is the wizard step that presents the data store module
+ * bean to the user for data store configuration.
+ */
+ protected class DatastoreModuleStep implements WizardStep {
+
+ /**
+ * The component provided to the wizard.
+ */
+ private Box stepBox;
+
+ /**
+ * Basic constructor. The component for the step will actually be
+ * built in the setActive method, as this step is dependant upon
+ * the data store selection made by the user in the DatastoreStep
+ * wizard step.
+ */
+ public DatastoreModuleStep() {
+ stepBox = Box.createVerticalBox();
+ stepBox.add(Box.createVerticalGlue());
+ } // constructor
+
+ public String getDescription() {
+ return ResourceStrings.getString("ds_wiz_datastore_parm_desc");
+ } // getDescription
+
+ public Component getComponent() {
+ return stepBox;
+ } // getComponent
+
+ public void setActive(int direction) {
+ if (direction > 0) {
+ stepBox.removeAll();
+ Component component =
+ getDsconf().getModule().getComponent();
+ if (component != null) {
+ stepBox.add(component);
+ stepBox.add(Box.createVerticalGlue());
+ validate();
+ }
+ }
+
+ if (getDsconf().getModule().getForwardEnabled()) {
+ setForwardEnabled(true);
+ } else {
+ setForwardEnabled(false);
+ }
+ } // setActive
+
+ public boolean setInactive(int direction) {
+ return true;
+ } // setInactive
+
+ } // DatastoreModuleStep
+
+ /**
+ * Simple constructor.
+ * @param owner frame for wizard.
+ * @param title title of the wizard.
+ */
+ public DSWizard(Frame owner, String title) {
+ super(owner, title);
+ } // constructor
+
+ /**
+ * Sets dsconf.
+ * @param dsconf the data store config value.
+ */
+ public void setDsconf(DSConf dsconf) {
+ if (this.dsconf != null) {
+ this.dsconf.getModule().removeDSMListener(this);
+ }
+ setForwardEnabled(true);
+ this.dsconf = dsconf;
+ this.dsconf.getModule().addDSMListener(this);
+ } // setDsconf
+
+ /**
+ * Returns the dsconf.
+ * @return the dsconf.
+ */
+ public DSConf getDsconf() {
+ return dsconf;
+ } // getDsconf
+
+ /**
+ * Invoked when the DSModule has changed its state.
+ * @param e the event.
+ */
+ public void stateChanged(DSModuleEvent e) {
+ if (e.getState() == DSModuleEvent.DATA_VALID) {
+ setForwardEnabled(true);
+ } else {
+ setForwardEnabled(false);
+ }
+ } // stateChanged
+
+} // DSWizard
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DataManager.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DataManager.java
new file mode 100644
index 0000000000..34360778a0
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DataManager.java
@@ -0,0 +1,234 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1998-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.client;
+
+import java.util.Arrays;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.text.MessageFormat;
+
+import javax.swing.*;
+
+import com.sun.dhcpmgr.bridge.*;
+import com.sun.dhcpmgr.data.*;
+import com.sun.dhcpmgr.server.*;
+
+/**
+ * DataManager implements a central point of contact between the client and the
+ * server components; the client obtains all server object references through
+ * this class which allows us to cache information as much as possible,
+ * improving users' perception of the performance of the tool.
+ */
+public class DataManager {
+ private DhcpMgr server;
+ private DhcpNetMgr netMgr;
+ private DhcptabMgr dhcptabMgr;
+ private DhcpServiceMgr dhcpServiceMgr;
+ private Network [] networks;
+ private DhcpClientRecord [] clients;
+ private String clientNet;
+ private Macro [] macros;
+ private Option [] options;
+ private String serverName;
+ private String shortServerName;
+ private InetAddress serverAddress;
+ private static DataManager mgr = null;
+
+ private DataManager() {
+ reset();
+ }
+
+ public synchronized void reset() {
+ // Clear the data references; used by the applet to re-initialize
+ server = new DhcpMgrImpl();
+ netMgr = null;
+ dhcptabMgr = null;
+ dhcpServiceMgr = null;
+ /*
+ * The arrays aren't nulled so that we can use them as the lock objects
+ * in the synchronized blocks below
+ */
+ networks = new Network[0];
+ clients = new DhcpClientRecord[0];
+ clientNet = null;
+ macros = new Macro[0];
+ options = new Option[0];
+ try {
+ serverAddress = InetAddress.getLocalHost();
+ setServerName(serverAddress.getHostName());
+ } catch (UnknownHostException e) {
+ serverName = shortServerName = "";
+ }
+
+ try {
+ StandardOptions.setAllOptions(
+ getDhcpServiceMgr().getInittabOptions(
+ Option.ctxts[Option.STANDARD].getCode()));
+ } catch (Throwable e) {
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString("stdopts_init_error"));
+ Object [] args = new Object[1];
+ if (e instanceof BridgeException) {
+ args[0] = e.getMessage();
+ } else {
+ args[0] = e.toString();
+ }
+ JOptionPane.showMessageDialog(null,
+ form.format(args),
+ ResourceStrings.getString("init_error"),
+ JOptionPane.ERROR_MESSAGE);
+ }
+ }
+
+ private void setServerName(String name) {
+ serverName = name;
+ int i = serverName.indexOf('.');
+ if (i == -1) {
+ shortServerName = serverName;
+ } else {
+ shortServerName = serverName.substring(0, i);
+ }
+ }
+
+ /*
+ * Threading note: the following methods are all synchronized on the
+ * class so that server references and names are always set & retrieved
+ * in a consistent state.
+ */
+ public synchronized static DataManager get() {
+ if (mgr == null) {
+ mgr = new DataManager();
+ }
+ return mgr;
+ }
+
+ public synchronized void setServer(String name) throws Exception {
+ setServerName(name);
+ serverAddress = null;
+ }
+
+ public synchronized DhcpMgr getServer() {
+ return server;
+ }
+
+ public synchronized String getServerName() {
+ return serverName;
+ }
+
+ public synchronized String getShortServerName() {
+ return shortServerName;
+ }
+
+ public synchronized InetAddress getServerAddress() {
+ return serverAddress;
+ }
+
+ public synchronized DhcpNetMgr getDhcpNetMgr() {
+ if (netMgr == null) {
+ netMgr = getServer().getNetMgr();
+ }
+ return netMgr;
+ }
+
+ public synchronized DhcptabMgr getDhcptabMgr() {
+ if (dhcptabMgr == null) {
+ dhcptabMgr = getServer().getDhcptabMgr();
+ }
+ return dhcptabMgr;
+ }
+
+ public synchronized DhcpServiceMgr getDhcpServiceMgr() {
+ if (dhcpServiceMgr == null) {
+ dhcpServiceMgr = getServer().getDhcpServiceMgr();
+ }
+ return dhcpServiceMgr;
+ }
+
+ /*
+ * End of class-synchronized methods. Remaining methods are synchronized
+ * at a data item level.
+ */
+
+ public Network [] getNetworks(boolean forceUpdate) throws BridgeException {
+ synchronized (networks) {
+ if (forceUpdate || networks.length == 0) {
+ networks = getDhcpNetMgr().getNetworks();
+ if (networks == null) {
+ networks = new Network[0];
+ } else {
+ Arrays.sort(networks);
+ }
+ }
+ }
+ return networks;
+ }
+
+ public DhcpClientRecord [] getClients(String net, boolean forceUpdate)
+ throws BridgeException {
+ synchronized (clients) {
+ if (forceUpdate || clients.length == 0 || !net.equals(clientNet)) {
+ clients = getDhcpNetMgr().loadNetwork(net);
+ if (clients == null) {
+ clients = new DhcpClientRecord[0];
+ }
+ clientNet = net;
+ }
+ }
+ return clients;
+ }
+
+ public Macro [] getMacros(boolean forceUpdate) throws BridgeException {
+ synchronized (macros) {
+ if (forceUpdate || macros.length == 0) {
+ macros = getDhcptabMgr().getMacros();
+ if (macros == null) {
+ macros = new Macro[0];
+ }
+ }
+ }
+ return macros;
+ }
+
+ public Option [] getOptions(boolean forceUpdate) throws BridgeException {
+ synchronized (options) {
+ if (forceUpdate || options.length == 0) {
+ options = getDhcptabMgr().getOptions();
+ if (options == null) {
+ options = new Option[0];
+ }
+ /*
+ * Reload the site/vendor options portion of the global options
+ * table with the data we just loaded so that macro validation
+ * can be handled properly.
+ */
+ OptionsTable.getTable().add(options);
+ }
+ }
+ return options;
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DeleteAddressDialog.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DeleteAddressDialog.java
new file mode 100644
index 0000000000..fff51a2071
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DeleteAddressDialog.java
@@ -0,0 +1,215 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 1998-2002 by Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.client;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.text.MessageFormat;
+import java.util.*;
+import javax.swing.*;
+import javax.swing.table.*;
+
+import com.sun.dhcpmgr.server.*;
+import com.sun.dhcpmgr.data.*;
+import com.sun.dhcpmgr.ui.*;
+import com.sun.dhcpmgr.bridge.BridgeException;
+
+/**
+ * This dialog is used to delete one or more addresses from a network
+ */
+
+public class DeleteAddressDialog extends MultipleOperationDialog {
+ private JCheckBox hostsBox;
+ private DhcpClientRecord [] recs;
+ private String table;
+
+ // Model for the list of addresses to be deleted
+ class AddressTableModel extends AbstractTableModel {
+ public int getRowCount() {
+ if (recs == null) {
+ return 0;
+ } else {
+ return recs.length;
+ }
+ }
+
+ public int getColumnCount() {
+ return 2;
+ }
+
+ public Object getValueAt(int row, int column) {
+ if (column == 0) {
+ return recs[row].getClientIP();
+ } else {
+ if (recs[row].getClientName().equals(
+ recs[row].getClientIPAddress())) {
+ // Name returned is IP address, so there is no name
+ return "";
+ } else {
+ return recs[row].getClientName();
+ }
+ }
+ }
+
+ public Class getColumnClass(int column) {
+ if (column == 0) {
+ return IPAddress.class;
+ } else {
+ return String.class;
+ }
+ }
+
+ public String getColumnName(int column) {
+ if (column == 0) {
+ return ResourceStrings.getString("address_column");
+ } else {
+ return ResourceStrings.getString("client_name_column");
+ }
+ }
+ }
+
+ public DeleteAddressDialog(Frame f, DhcpClientRecord [] clients,
+ String table) {
+ // Create the dialog without a reset button
+ super(f, false);
+ recs = clients;
+ this.table = table;
+ }
+
+ public String getTitle() {
+ return ResourceStrings.getString("delete_address_title");
+ }
+
+ protected JPanel getMainPanel() {
+ JPanel mainPanel = new JPanel(new BorderLayout(10, 10));
+ mainPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
+
+ // Place a message at the top of the display
+ JLabel message = new JLabel(
+ ResourceStrings.getString("delete_address_confirm"));
+
+ message.setLabelFor(mainPanel);
+ message.setToolTipText(
+ ResourceStrings.getString("delete_address_confirm"));
+
+ mainPanel.add(message, BorderLayout.NORTH);
+
+ // Now show the list of addresses to be deleted in a table in the middle
+ JTable addressTable = new JTable(new AddressTableModel());
+ JScrollPane scrollPane = new JScrollPane(addressTable);
+ Dimension d = addressTable.getPreferredScrollableViewportSize();
+ d.height = 100;
+ addressTable.setPreferredScrollableViewportSize(d);
+ addressTable.setDefaultRenderer(IPAddress.class,
+ new ExtendedCellRenderer());
+ mainPanel.add(scrollPane, BorderLayout.CENTER);
+
+ // Allow user to specify if hosts records will be deleted, too
+ hostsBox = new JCheckBox(
+ ResourceStrings.getString("delete_hosts_checkbox"), true);
+ hostsBox.setToolTipText(
+ ResourceStrings.getString("delete_hosts_checkbox"));
+
+ hostsBox.setHorizontalAlignment(SwingConstants.CENTER);
+ mainPanel.add(hostsBox, BorderLayout.SOUTH);
+
+ hostsBox.setEnabled(true);
+ try {
+ DhcpdOptions opts =
+ DataManager.get().getDhcpServiceMgr().readDefaults();
+ if (opts.getHostsResource() == null) {
+ hostsBox.setEnabled(false);
+ hostsBox.setSelected(false);
+ }
+ } catch (BridgeException e) {
+ // Assume set
+ }
+
+ buttonPanel.setOkEnabled(true);
+ return mainPanel;
+ }
+
+ protected String getProgressMessage() {
+ return ResourceStrings.getString("delete_addr_progress");
+ }
+
+ protected int getProgressLength() {
+ return recs.length;
+ }
+
+ protected String getErrorHeading() {
+ return ResourceStrings.getString("address_column");
+ }
+
+ protected Class getErrorClass() {
+ return IPAddress.class;
+ }
+
+ protected Thread getOperationThread() {
+ // Create the thread we'll use
+ return new Thread() {
+ public void run() {
+ DhcpNetMgr server = DataManager.get().getDhcpNetMgr();
+ boolean deleteHosts = hostsBox.isSelected();
+ for (int i = 0; i < recs.length; ++i) {
+ try {
+ server.deleteClient(recs[i], table, deleteHosts);
+ updateProgress(i+1, recs[i].getClientIPAddress());
+ } catch (InterruptedException e) {
+ // User asked us to stop
+ closeDialog();
+ return;
+ } catch (Throwable e) {
+ if (e.getMessage().equals("hosts")) {
+ // Failure was in deleting hosts entry
+ addError(recs[i].getClientIP(),
+ ResourceStrings.getString(
+ "hosts_entry_missing"));
+ } else {
+ addError(recs[i].getClientIP(), e.getMessage());
+ }
+ }
+ }
+ // Errors occurred, display them
+ if (errorsOccurred()) {
+ displayErrors(
+ ResourceStrings.getString("delete_address_error"));
+ }
+ closeDialog();
+ }
+ };
+ }
+
+ protected String getHelpKey() {
+ return "delete_address";
+ }
+
+ protected void fireActionPerformed() {
+ fireActionPerformed(this, DialogActions.DELETE);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DeleteMacroDialog.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DeleteMacroDialog.java
new file mode 100644
index 0000000000..b43b95d0e5
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DeleteMacroDialog.java
@@ -0,0 +1,116 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.client;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.text.MessageFormat;
+import java.util.*;
+import javax.swing.*;
+
+import com.sun.dhcpmgr.server.*;
+import com.sun.dhcpmgr.data.*;
+import com.sun.dhcpmgr.ui.*;
+import com.sun.dhcpmgr.bridge.NotRunningException;
+
+/**
+ * This dialog allows the user to delete a macro.
+ */
+public class DeleteMacroDialog extends DhcpmgrDialog {
+ private JCheckBox signalBox;
+ private Macro macro;
+ private JLabel message;
+
+ public DeleteMacroDialog(Frame f, Macro m) {
+ super(f, false);
+ setTitle(ResourceStrings.getString("delete_macro_title"));
+ macro = m;
+ message.setText(MessageFormat.format(
+ ResourceStrings.getString("delete_macro_confirm"), macro.getKey()));
+ message.setToolTipText(MessageFormat.format(
+ ResourceStrings.getString("delete_macro_confirm"), macro.getKey()));
+ }
+
+ public JPanel getMainPanel() {
+ JPanel mainPanel = new JPanel(new BorderLayout());
+
+ JPanel flowPanel = new JPanel();
+
+ message = new JLabel();
+ flowPanel.add(message);
+ mainPanel.add(flowPanel, BorderLayout.NORTH);
+
+ flowPanel = new JPanel();
+ signalBox = new JCheckBox(ResourceStrings.getString("signal_server"),
+ true);
+ signalBox.setToolTipText(
+ ResourceStrings.getString("signal_server"));
+ flowPanel.add(signalBox);
+ mainPanel.add(flowPanel, BorderLayout.CENTER);
+
+ buttonPanel.setOkEnabled(true);
+ return mainPanel;
+ }
+
+ protected void doOk() {
+ try {
+ DhcptabMgr server = DataManager.get().getDhcptabMgr();
+ server.deleteRecord(macro, signalBox.isSelected());
+ fireActionPerformed();
+ setVisible(false);
+ dispose();
+ } catch (NotRunningException e) {
+ // Server not running, put up a warning
+ JOptionPane.showMessageDialog(this,
+ ResourceStrings.getString("server_not_running"),
+ ResourceStrings.getString("warning"),
+ JOptionPane.WARNING_MESSAGE);
+ fireActionPerformed();
+ setVisible(false);
+ dispose();
+ } catch (Exception e) {
+ MessageFormat form = null;
+ Object [] args = new Object[2];
+ form = new MessageFormat(
+ ResourceStrings.getString("delete_macro_error"));
+ args[0] = macro.getKey();
+ args[1] = e.getMessage();
+ JOptionPane.showMessageDialog(this, form.format(args),
+ ResourceStrings.getString("server_error_title"),
+ JOptionPane.ERROR_MESSAGE);
+ }
+ }
+
+ protected String getHelpKey() {
+ return "delete_macro";
+ }
+
+ protected void fireActionPerformed() {
+ fireActionPerformed(this, DialogActions.DELETE);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DeleteNetworksDialog.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DeleteNetworksDialog.java
new file mode 100644
index 0000000000..36db3c36a7
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DeleteNetworksDialog.java
@@ -0,0 +1,290 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 1998-2002 by Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.client;
+
+import javax.swing.*;
+import javax.swing.border.*;
+import javax.swing.event.*;
+import javax.swing.table.*;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.text.*;
+import java.util.*;
+
+import com.sun.dhcpmgr.server.*;
+import com.sun.dhcpmgr.data.*;
+import com.sun.dhcpmgr.ui.*;
+import com.sun.dhcpmgr.bridge.BridgeException;
+
+/**
+ * A dialog to remove one or more networks from the DHCP configuration.
+ */
+public class DeleteNetworksDialog extends MultipleOperationDialog {
+
+ class NetworkListModel extends AbstractListModel {
+ private Vector networks;
+
+ public NetworkListModel() {
+ networks = new Vector();
+ }
+
+ public void setNetworks(Network [] nets) {
+ networks.removeAllElements();
+ addNetworks(nets);
+ }
+
+ public void addNetworks(Object [] nets) {
+ if (nets != null) {
+ for (int i = 0; i < nets.length; ++i) {
+ networks.addElement((Network)nets[i]);
+ }
+ }
+ fireContentsChanged(this, 0, networks.size()-1);
+ }
+
+ public void deleteNetworks(Object [] nets) {
+ for (int i = 0; i < nets.length; ++i) {
+ networks.removeElement((Network)nets[i]);
+ }
+ fireContentsChanged(this, 0, networks.size()-1);
+ }
+
+ public Object getElementAt(int index) {
+ return networks.elementAt(index);
+ }
+
+ public int getSize() {
+ return networks.size();
+ }
+ }
+
+ private JList keepNets, deleteNets;
+ private JCheckBox deleteHosts;
+ private LeftButton leftButton;
+ private RightButton rightButton;
+
+ public DeleteNetworksDialog(Frame f) {
+ // We want a reset button
+ super(f, true);
+ }
+
+ public String getTitle() {
+ return ResourceStrings.getString("delete_networks_title");
+ }
+
+ protected JPanel getMainPanel() {
+ JPanel mainPanel = new JPanel(new BorderLayout());
+ mainPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
+
+ JPanel netBox = new JPanel(new ProportionalLayout());
+ JPanel panel = new JPanel(new BorderLayout(5, 5));
+
+ Mnemonic mnKeep =
+ new Mnemonic(ResourceStrings.getString("delete_networks_keep"));
+ JLabel delNetsLbl = new JLabel(mnKeep.getString());
+ panel.add(delNetsLbl, BorderLayout.NORTH);
+ delNetsLbl.setToolTipText(mnKeep.getString());
+ keepNets = new JList(new NetworkListModel());
+ delNetsLbl.setLabelFor(keepNets);
+ delNetsLbl.setDisplayedMnemonic(mnKeep.getMnemonic());
+
+ keepNets.setSelectionMode(
+ ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
+ JScrollPane scrollPane = new JScrollPane(keepNets);
+ panel.add(scrollPane, BorderLayout.CENTER);
+ netBox.add("2", panel);
+
+ panel = new JPanel(new VerticalButtonLayout());
+ leftButton = new LeftButton();
+ rightButton = new RightButton();
+ rightButton.setEnabled(false);
+ leftButton.setEnabled(false);
+ panel.add(rightButton);
+ panel.add(leftButton);
+ netBox.add("1", panel);
+
+ panel = new JPanel(new BorderLayout(5, 5));
+
+ Mnemonic mnDel =
+ new Mnemonic(ResourceStrings.getString("delete_networks_delete"));
+ JLabel delNets = new JLabel(mnDel.getString());
+ panel.add(delNets, BorderLayout.NORTH);
+
+ deleteNets = new JList(new NetworkListModel());
+ deleteNets.setSelectionMode(
+ ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
+
+ delNets.setLabelFor(deleteNets);
+ delNets.setToolTipText(mnDel.getString());
+ delNets.setDisplayedMnemonic(mnDel.getMnemonic());
+
+ scrollPane = new JScrollPane(deleteNets);
+ panel.add(scrollPane, BorderLayout.CENTER);
+ netBox.add("2", panel);
+
+ mainPanel.add(netBox, BorderLayout.CENTER);
+
+ deleteHosts = new JCheckBox(
+ ResourceStrings.getString("delete_networks_delete_hosts"));
+ deleteHosts.setToolTipText(
+ ResourceStrings.getString("delete_networks_delete_hosts"));
+
+ panel = new JPanel();
+ panel.add(deleteHosts);
+ mainPanel.add(panel, BorderLayout.SOUTH);
+
+ deleteHosts.setEnabled(true);
+ try {
+ DhcpdOptions opts =
+ DataManager.get().getDhcpServiceMgr().readDefaults();
+ if (opts.getHostsResource() == null) {
+ deleteHosts.setEnabled(false);
+ }
+ } catch (BridgeException e) {
+ // Assume set
+ }
+
+ // Handle enable and disable of buttons based on selection state
+ keepNets.addListSelectionListener(new ListSelectionListener() {
+ public void valueChanged(ListSelectionEvent e) {
+ rightButton.setEnabled(!keepNets.isSelectionEmpty());
+ if (!keepNets.isSelectionEmpty()) {
+ deleteNets.clearSelection();
+ }
+ }
+ });
+
+ deleteNets.addListSelectionListener(new ListSelectionListener() {
+ public void valueChanged(ListSelectionEvent e) {
+ leftButton.setEnabled(!deleteNets.isSelectionEmpty());
+ if (!deleteNets.isSelectionEmpty()) {
+ keepNets.clearSelection();
+ }
+ }
+ });
+
+ // Handle button presses
+ rightButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ Object [] nets = keepNets.getSelectedValues();
+ ((NetworkListModel)deleteNets.getModel()).addNetworks(nets);
+ ((NetworkListModel)keepNets.getModel()).deleteNetworks(nets);
+ if (deleteNets.getModel().getSize() != 0) {
+ buttonPanel.setOkEnabled(true);
+ }
+ /*
+ * Clear the selection; prevents exceptions from selection
+ * having been deleted
+ */
+ keepNets.clearSelection();
+ }
+ });
+
+ leftButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ Object [] nets = deleteNets.getSelectedValues();
+ ((NetworkListModel)keepNets.getModel()).addNetworks(nets);
+ ((NetworkListModel)deleteNets.getModel()).deleteNetworks(nets);
+ /*
+ * Clear the selection; prevents exceptions from selection
+ * having been deleted
+ */
+ deleteNets.clearSelection();
+ if (deleteNets.getModel().getSize() == 0) {
+ buttonPanel.setOkEnabled(false);
+ }
+ }
+ });
+
+ doReset();
+
+ return mainPanel;
+ }
+
+ protected void doReset() {
+ try {
+ buttonPanel.setOkEnabled(false);
+ deleteHosts.setSelected(false);
+ ((NetworkListModel)deleteNets.getModel()).setNetworks(null);
+ ((NetworkListModel)keepNets.getModel()).setNetworks(
+ DataManager.get().getNetworks(false));
+ } catch (Throwable e) {
+ e.printStackTrace();
+ // Do nothing
+ }
+ }
+
+ protected String getProgressMessage() {
+ return ResourceStrings.getString("delete_networks_progress");
+ }
+
+ protected int getProgressLength() {
+ return deleteNets.getModel().getSize();
+ }
+
+ protected String getErrorHeading() {
+ return ResourceStrings.getString("network_column");
+ }
+
+ protected Thread getOperationThread() {
+ return new Thread() {
+ public void run() {
+ NetworkListModel model =
+ (NetworkListModel)deleteNets.getModel();
+ for (int i = 0; i < model.getSize(); ++i) {
+ Network net = (Network)model.getElementAt(i);
+ try {
+ DataManager.get().getDhcpNetMgr().deleteNetwork(
+ net.toString(), true, deleteHosts.isSelected());
+ updateProgress(i+1, net.toString());
+ } catch (InterruptedException e) {
+ // User asked us to stop
+ closeDialog();
+ return;
+ } catch (Throwable e) {
+ addError(net.toString(), e.getMessage());
+ }
+ }
+ if (errorsOccurred()) {
+ displayErrors(
+ ResourceStrings.getString("delete_networks_error"));
+ }
+ closeDialog();
+ }
+ };
+ }
+
+ protected String getHelpKey() {
+ return "delete_network";
+ }
+
+ protected void fireActionPerformed() {
+ fireActionPerformed(this, DialogActions.OK);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DeleteOptionDialog.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DeleteOptionDialog.java
new file mode 100644
index 0000000000..27a4b059ff
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DeleteOptionDialog.java
@@ -0,0 +1,115 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.client;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.text.MessageFormat;
+import java.util.*;
+import javax.swing.*;
+
+import com.sun.dhcpmgr.server.*;
+import com.sun.dhcpmgr.data.*;
+import com.sun.dhcpmgr.ui.*;
+import com.sun.dhcpmgr.bridge.NotRunningException;
+
+/**
+ * This dialog allows the user to delete an option definition
+ */
+public class DeleteOptionDialog extends DhcpmgrDialog {
+ private JCheckBox signalBox;
+ private Option option;
+ private JLabel message;
+
+ public DeleteOptionDialog(Frame f, Option o) {
+ super(f, false);
+ setTitle(ResourceStrings.getString("delete_option_title"));
+ option = o;
+ message.setText(MessageFormat.format(
+ ResourceStrings.getString("delete_option_confirm"), o.getKey()));
+ message.setToolTipText(MessageFormat.format(
+ ResourceStrings.getString("delete_option_confirm"), o.getKey()));
+ }
+
+ protected JPanel getMainPanel() {
+ JPanel mainPanel = new JPanel(new BorderLayout());
+ JPanel flowPanel = new JPanel();
+
+ message = new JLabel();
+ flowPanel.add(message);
+ mainPanel.add(flowPanel, BorderLayout.NORTH);
+
+ flowPanel = new JPanel();
+ signalBox = new JCheckBox(ResourceStrings.getString("signal_server"),
+ true);
+ signalBox.setToolTipText(
+ ResourceStrings.getString("signal_server"));
+ flowPanel.add(signalBox);
+ mainPanel.add(flowPanel, BorderLayout.CENTER);
+
+ buttonPanel.setOkEnabled(true);
+ return mainPanel;
+ }
+
+ protected void doOk() {
+ try {
+ DhcptabMgr server = DataManager.get().getDhcptabMgr();
+ server.deleteRecord(option, signalBox.isSelected());
+ fireActionPerformed();
+ setVisible(false);
+ dispose();
+ } catch (NotRunningException e) {
+ // Server not running, put up a warning
+ JOptionPane.showMessageDialog(this,
+ ResourceStrings.getString("server_not_running"),
+ ResourceStrings.getString("warning"),
+ JOptionPane.WARNING_MESSAGE);
+ fireActionPerformed();
+ setVisible(false);
+ dispose();
+ } catch (Exception e) {
+ MessageFormat form = null;
+ Object [] args = new Object[2];
+ form = new MessageFormat(
+ ResourceStrings.getString("delete_option_error"));
+ args[0] = option.getKey();
+ args[1] = e.getMessage();
+ JOptionPane.showMessageDialog(this, form.format(args),
+ ResourceStrings.getString("server_error_title"),
+ JOptionPane.ERROR_MESSAGE);
+ }
+ }
+
+ protected String getHelpKey() {
+ return "delete_option";
+ }
+
+ protected void fireActionPerformed() {
+ fireActionPerformed(this, DialogActions.DELETE);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DhcpmgrApplet.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DhcpmgrApplet.java
new file mode 100644
index 0000000000..ea7e7d6676
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DhcpmgrApplet.java
@@ -0,0 +1,662 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.client;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+import java.net.*;
+import java.rmi.RemoteException;
+import java.rmi.NotBoundException;
+import javax.swing.*;
+import java.text.MessageFormat;
+import java.applet.AppletContext;
+
+import com.sun.dhcpmgr.ui.*;
+import com.sun.dhcpmgr.data.*;
+import com.sun.dhcpmgr.server.DhcpMgr;
+import com.sun.dhcpmgr.server.DhcpServiceMgr;
+import com.sun.dhcpmgr.bridge.BridgeException;
+import com.sun.dhcpmgr.bridge.NotRunningException;
+
+/**
+ * Main class for DHCP Manager. It is theoretically possible to run this
+ * application as a command managing the local system, a command managing a
+ * remote system using RMI, or as an applet managing the system from which it
+ * was downloaded. We presently only support the first option, but there is
+ * vestigial code here from when the other options were supported as they may
+ * be again. That's why we extend JApplet.
+ */
+public class DhcpmgrApplet extends JApplet {
+ private static MainFrame frame = null;
+ private JButton button;
+ public static boolean modeIsRelay;
+ private static HelpIds helpIds = null;
+ private static URL docBase = null;
+ private static AppletContext appletContext = null;
+ private AddressView addressView;
+ private RestartAction restartAction;
+ private StopAction stopAction;
+ private StartAction startAction;
+ private DisableAction disableAction;
+ private EnableAction enableAction;
+
+ // Handler for Help->Overview menu item
+ class OverviewAction extends MnemonicAction {
+ public OverviewAction() {
+ super(ResourceStrings.getString("overview_item"));
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ showHelp("overview");
+ }
+ }
+
+ // Handler for Help->How To menu item
+ class HowToAction extends MnemonicAction {
+ public HowToAction() {
+ super(ResourceStrings.getString("howto_item"));
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ showHelp("howto");
+ }
+ }
+
+ // Handler for Help->Index menu item
+ class IndexAction extends MnemonicAction {
+ public IndexAction() {
+ super(ResourceStrings.getString("index_item"));
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ showHelp("index");
+ }
+ }
+
+ // Handler for Help->On Service menu item
+ class ServiceAction extends MnemonicAction {
+ public ServiceAction() {
+ super(ResourceStrings.getString("on_service_item"));
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ showHelp("service_reference");
+ }
+ }
+
+ // Handler for the Service->Restart menu item
+ class RestartAction extends MnemonicAction {
+ public RestartAction() {
+ super(ResourceStrings.getString("restart_item"));
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ try {
+ DataManager.get().getDhcpServiceMgr().reload();
+ frame.setStatusText(
+ ResourceStrings.getString("service_restarted"));
+ } catch (NotRunningException ex) {
+ // Server not running, ignore the error and just start it
+ startAction.actionPerformed(e);
+ } catch (Throwable t) {
+ Object [] args = new Object[1];
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString("restart_server_error"));
+ args[0] = t.getMessage();
+ JOptionPane.showMessageDialog(frame, form.format(args),
+ ResourceStrings.getString("server_error_title"),
+ JOptionPane.ERROR_MESSAGE);
+ }
+ }
+ }
+
+ // Handler for the Service->Stop menu item
+ class StopAction extends MnemonicAction {
+ public StopAction() {
+ super(ResourceStrings.getString("stop_item"));
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ try {
+ DataManager.get().getDhcpServiceMgr().shutdown();
+ frame.setStatusText(
+ ResourceStrings.getString("service_stopped"));
+ startAction.setEnabled(true);
+ restartAction.setEnabled(false);
+ setEnabled(false);
+ } catch (Throwable t) {
+ Object [] args = new Object[1];
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString("shutdown_server_error"));
+ args[0] = t.getMessage();
+ JOptionPane.showMessageDialog(frame, form.format(args),
+ ResourceStrings.getString("server_error_title"),
+ JOptionPane.ERROR_MESSAGE);
+ }
+ }
+ }
+
+ // Handler for the Service->Restart menu item
+ class StartAction extends MnemonicAction {
+ public StartAction() {
+ super(ResourceStrings.getString("start_item"));
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ try {
+ DataManager.get().getDhcpServiceMgr().startup();
+ frame.setStatusText(
+ ResourceStrings.getString("service_started"));
+ stopAction.setEnabled(true);
+ restartAction.setEnabled(true);
+ setEnabled(false);
+ } catch (Throwable t) {
+ Object [] args = new Object[1];
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString("startup_server_error"));
+ args[0] = t.getMessage();
+ JOptionPane.showMessageDialog(frame, form.format(args),
+ ResourceStrings.getString("server_error_title"),
+ JOptionPane.ERROR_MESSAGE);
+ }
+ }
+ }
+
+ // handler for the Service->Disable service menu item
+ class DisableAction extends MnemonicAction {
+ public DisableAction() {
+ super(ResourceStrings.getString("disable_item"));
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ DisableServiceDialog d = new DisableServiceDialog(frame, true);
+ d.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ // Update menu item state once we've disabled it
+ enableAction.setEnabled(true);
+ disableAction.setEnabled(false);
+ stopAction.setEnabled(false);
+ startAction.setEnabled(false);
+ restartAction.setEnabled(false);
+ }
+ });
+ d.pack();
+ d.setVisible(true);
+ }
+ }
+
+ // handler for the Service->Enable service menu item
+ class EnableAction extends MnemonicAction {
+ public EnableAction() {
+ super(ResourceStrings.getString("enable_item"));
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ DisableServiceDialog d = new DisableServiceDialog(frame, false);
+ d.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ // Update menu item state once we've enabled it
+ disableAction.setEnabled(true);
+ enableAction.setEnabled(false);
+ stopAction.setEnabled(true);
+ startAction.setEnabled(false);
+ restartAction.setEnabled(true);
+ }
+ });
+ d.pack();
+ d.setVisible(true);
+ }
+ }
+
+ // handler for the Service->Modify service menu item
+ class ModifyServiceAction extends MnemonicAction {
+ public ModifyServiceAction() {
+ super(ResourceStrings.getString("modify_service_item"));
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ try {
+ DhcpdOptions opts =
+ DataManager.get().getDhcpServiceMgr().readDefaults();
+ ServerOptionsDialog d = new ServerOptionsDialog(frame, opts);
+ d.pack();
+ d.setVisible(true);
+ } catch (BridgeException ex) {
+ // Error reading options
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString("err_reading_options"));
+ Object [] args = new Object[] { ex.getMessage() };
+ JOptionPane.showMessageDialog(frame, form.format(args),
+ ResourceStrings.getString("server_error_title"),
+ JOptionPane.ERROR_MESSAGE);
+ }
+ }
+ }
+
+ // handler for the Service->Convert service menu item
+ class ConvertAction extends MnemonicAction {
+ public ConvertAction() {
+ super(ResourceStrings.getString("cvt_service_item"));
+
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ ConvertWizard wiz = new ConvertWizard(frame,
+ ResourceStrings.getString("cvt_wiz_title"));
+ wiz.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ if (e.getActionCommand().equals("finished")) {
+ frame.refreshAllViews();
+ showFrame();
+ }
+ }
+ });
+ wiz.pack();
+ wiz.setModal(true);
+ wiz.setVisible(true);
+ }
+
+ public void setEnabled(boolean b) {
+ if (!modeIsRelay) {
+ super.setEnabled(b);
+ } else {
+ super.setEnabled(false);
+ }
+ }
+ }
+
+ // handler for the Service->Unconfigure service menu item
+ class UnconfigureServiceAction extends MnemonicAction {
+ public UnconfigureServiceAction() {
+ super(ResourceStrings.getString("unconfigure_service_item"));
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ UnconfigureDialog d = new UnconfigureDialog(frame);
+ d.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ if (e.getActionCommand().equals(DialogActions.OK)) {
+ /*
+ * User unconfigured the service; there's nothing
+ * else to do so just get rid of the frame which
+ * will as a side effect shut us down.
+ */
+ frame.setVisible(false);
+ frame.dispose();
+ frame = null;
+ }
+ }
+ });
+ d.pack();
+ d.setVisible(true);
+ }
+ }
+
+ // Action for Service->Export data
+ class ExportAction extends MnemonicAction {
+ public ExportAction() {
+ super(ResourceStrings.getString("export_item"));
+ }
+ public void actionPerformed(ActionEvent e) {
+ ExportWizard wiz = new ExportWizard(frame);
+ wiz.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ if (e.getActionCommand().equals("finished")) {
+ frame.refreshAllViews();
+ showFrame();
+ }
+ }
+ });
+ wiz.pack();
+ wiz.setVisible(true);
+ }
+ }
+
+ // Action for Service->Import data
+ class ImportAction extends MnemonicAction {
+ public ImportAction() {
+ super(ResourceStrings.getString("import_item"));
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ ImportWizard wiz = new ImportWizard(frame);
+ wiz.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ if (e.getActionCommand().equals("finished")) {
+ frame.refreshAllViews();
+ showFrame();
+ }
+ }
+ });
+ wiz.pack();
+ wiz.setVisible(true);
+ }
+ }
+
+ /*
+ * This class provides a transition dialog which allows the user
+ * to initiate the address wizard immediately upon startup. It's
+ * done this way so that the startMeUp() method can use invokeLater()
+ * to cause it to be displayed after the config wizard exit event
+ * has been processed rather than during that event's processing;
+ * otherwise the wizard doesn't disappear until after the user presses
+ * Yes or No in this dialog.
+ */
+ class WizardTransition implements Runnable {
+ public void run() {
+ // Now transition to configuring addresses
+ int status = JOptionPane.showConfirmDialog(frame,
+ ResourceStrings.getString("start_address_wizard"),
+ ResourceStrings.getString("start_address_wizard_title"),
+ JOptionPane.YES_NO_OPTION);
+ if (status == JOptionPane.YES_OPTION) {
+ addressView.startAddressWizard();
+ }
+ }
+ }
+
+ /*
+ * This class provides a transition dialog which allows the user
+ * to initiate the conversion wizard immediately upon startup if a
+ * version mismatch exists (a data store upgrade is necessary).
+ */
+ class ConversionTransition implements Runnable {
+ public void run() {
+ try {
+ DhcpServiceMgr svcMgr = DataManager.get().getDhcpServiceMgr();
+ while (!svcMgr.isVersionCurrent()) {
+ int status = JOptionPane.showConfirmDialog(frame,
+ ResourceStrings.getString("start_cvt_wizard"),
+ ResourceStrings.getString("start_cvt_wizard_title"),
+ JOptionPane.YES_NO_OPTION);
+ if (status == JOptionPane.YES_OPTION) {
+ ConvertAction converter = new ConvertAction();
+ ActionEvent e = new ActionEvent(this,
+ ActionEvent.ACTION_PERFORMED, "");
+ converter.actionPerformed(e);
+ } else {
+ frame = null;
+ DataManager.get().reset();
+ requestExit();
+ }
+ }
+ } catch (Throwable e) {
+ System.err.println(
+ ResourceStrings.getString("err_initializing_program"));
+ System.err.println(e.getMessage());
+ requestExit();
+ }
+ }
+ }
+
+ // Create the frame within which the UI will live
+ private void createFrame() {
+ if (frame == null) {
+
+ frame = new MainFrame(ResourceStrings.getString("dhcp_manager"));
+
+ // Create the views for this tool
+ if (modeIsRelay) {
+ frame.addView(new RelayView(), true);
+ } else {
+ addressView = new AddressView();
+ frame.addView(addressView, true);
+ frame.addView(new MacroView(), false);
+ frame.addView(new OptionView(), false);
+ }
+
+ // Set up the services menu
+ frame.addMenuAction(MainFrame.ACTIONS_MENU,
+ (restartAction = new RestartAction()));
+ frame.addMenuAction(MainFrame.ACTIONS_MENU,
+ (stopAction = new StopAction()));
+ frame.addMenuAction(MainFrame.ACTIONS_MENU,
+ (startAction = new StartAction()));
+ frame.addMenuAction(MainFrame.ACTIONS_MENU,
+ (disableAction = new DisableAction()));
+ frame.addMenuAction(MainFrame.ACTIONS_MENU,
+ (enableAction = new EnableAction()));
+ frame.addMenuAction(MainFrame.ACTIONS_MENU,
+ new ModifyServiceAction());
+ if (!modeIsRelay) {
+ frame.addMenuAction(MainFrame.ACTIONS_MENU,
+ new ExportAction());
+ frame.addMenuAction(MainFrame.ACTIONS_MENU,
+ new ImportAction());
+ frame.addMenuAction(MainFrame.ACTIONS_MENU,
+ new ConvertAction());
+ }
+ frame.addMenuAction(MainFrame.ACTIONS_MENU,
+ new UnconfigureServiceAction());
+
+ // Set up the Help menu
+ frame.addMenuAction(MainFrame.HELP_MENU, new OverviewAction());
+ frame.addMenuAction(MainFrame.HELP_MENU, new HowToAction());
+ frame.addMenuAction(MainFrame.HELP_MENU, new IndexAction());
+ frame.addMenuAction(MainFrame.HELP_MENU, new ServiceAction());
+
+ // In relay mode, let it size itself (quite small)
+ if (modeIsRelay) {
+ frame.pack();
+ } else {
+ /*
+ * Normal mode set it to a reasonable size. This ought to be
+ * a user preference, but until we run as something other than
+ * root it's not really a useful idea.
+ */
+ frame.setSize(800, 600);
+ }
+
+ // Listen for closing events
+ frame.addWindowListener(new WindowAdapter() {
+ public void windowClosing(WindowEvent e) {
+ /*
+ * This is here to work around the Close selection frame
+ * menu on Solaris not causing the closed function to be
+ * called
+ */
+ windowClosed(e);
+ }
+ public void windowClosed(WindowEvent e) {
+ // Dispose of all data and exit when window goes away.
+ frame = null;
+ DataManager.get().reset();
+ requestExit();
+ }
+ });
+ }
+ }
+
+ // Show the frame
+ private void showFrame() {
+ if (frame == null) {
+ createFrame();
+ }
+ frame.initialize();
+ if (modeIsRelay) {
+ // Disable edit & view menus in the relay case
+ frame.setMenuEnabled(MainFrame.EDIT_MENU, false);
+ frame.setMenuEnabled(MainFrame.VIEW_MENU, false);
+ }
+ try {
+ // Set status of service menu options based on server state
+ DhcpdOptions opts =
+ DataManager.get().getDhcpServiceMgr().readDefaults();
+ boolean enabled = opts.isDaemonEnabled();
+ enableAction.setEnabled(!enabled);
+ disableAction.setEnabled(enabled);
+ boolean running =
+ DataManager.get().getDhcpServiceMgr().isServerRunning();
+ restartAction.setEnabled(running && enabled);
+ stopAction.setEnabled(running);
+ startAction.setEnabled(!running && enabled);
+ } catch (Throwable e) {
+ // Enable all the menu items, as something went wrong
+ restartAction.setEnabled(true);
+ stopAction.setEnabled(true);
+ startAction.setEnabled(true);
+ enableAction.setEnabled(true);
+ disableAction.setEnabled(true);
+ }
+ frame.setVisible(true);
+ }
+
+ /*
+ * main startup code; checks whether server is already configured, and if
+ * not runs through the config wizard sequence in order to get the server
+ * configured.
+ */
+ private void startMeUp() {
+ try {
+ if (DataManager.get().getServer() == null) {
+ DataManager.get().setServer(getCodeBase().getHost());
+ }
+
+ // See if server is already configured, and start up
+ DhcpServiceMgr svcMgr = DataManager.get().getDhcpServiceMgr();
+ DhcpdOptions opts = svcMgr.readDefaults();
+ modeIsRelay = opts.isRelay();
+ // If server mode, ensure RESOURCE and PATH were set
+ if (!modeIsRelay) {
+ if ((opts.getResource() == null) || (opts.getPath() == null)) {
+ System.err.println(
+ ResourceStrings.getString("err_initializing_options"));
+ requestExit();
+ }
+ }
+
+ showFrame();
+
+ // Check to make sure that the data store version is up to date.
+ // If not, inform the user and present them with the conversion
+ // wizard so that they can upgrade.
+ if (!modeIsRelay && !svcMgr.isVersionCurrent()) {
+ SwingUtilities.invokeLater(new ConversionTransition());
+ }
+
+ } catch (BridgeException e) {
+ // Let user select which type of service to configure
+ int choice = ConfigureChoiceDialog.showDialog(frame);
+ if (choice == ConfigureChoiceDialog.DHCP) {
+ // DHCP; run the wizard
+ ConfigWizard wiz = new ConfigWizard(frame,
+ ResourceStrings.getString("cfg_wiz_title"), true);
+ wiz.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ if (e.getActionCommand().equals("finished")) {
+ // Service config completed, start up
+ modeIsRelay = false;
+ showFrame();
+ // Now transition to configuring addresses
+ SwingUtilities.invokeLater(new WizardTransition());
+ } else {
+ // User cancelled the wizard, exit
+ requestExit();
+ }
+ }
+ });
+ wiz.pack();
+ wiz.setVisible(true);
+ } else if (choice == ConfigureChoiceDialog.BOOTP) {
+ // Wants to configure a relay, show the dialog for that
+ ConfigureRelayDialog d = new ConfigureRelayDialog(frame);
+ d.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ if (e.getActionCommand().equals(DialogActions.OK)) {
+ // Relay configuration completed, start up
+ modeIsRelay = true;
+ showFrame();
+ } else {
+ // User cancelled, exit
+ requestExit();
+ }
+ }
+ });
+ d.pack();
+ d.setVisible(true);
+ } else {
+ // User cancelled; exit
+ requestExit();
+ }
+ } catch (Throwable e) {
+ // Couldn't really get started, dump the stack and exit
+ System.err.println(
+ ResourceStrings.getString("err_initializing_program"));
+ System.err.println(e.getMessage());
+ e.printStackTrace();
+ requestExit();
+ }
+ }
+
+ // Show a help file referenced by tag
+ public static void showHelp(String helpId) {
+ // If help tag mapping table not loaded yet, then load it
+ if (helpIds == null) {
+ try {
+ helpIds = new HelpIds("com.sun.dhcpmgr.client.HelpBundle");
+ } catch (Throwable e) {
+ // Error initializing help system
+ JOptionPane.showMessageDialog(frame,
+ ResourceStrings.getString("err_initializing_help"),
+ ResourceStrings.getString("server_error_title"),
+ JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+ }
+ // Ask browser to display
+ try {
+ Runtime.getRuntime().exec(
+ "/usr/sfw/bin/mozilla file:"
+ + helpIds.getFilePath(helpId));
+ } catch (java.io.IOException e) {
+ JOptionPane.showMessageDialog(frame,
+ ResourceStrings.getString("err_starting_help"),
+ ResourceStrings.getString("server_error_title"),
+ JOptionPane.ERROR_MESSAGE);
+ }
+ }
+
+ // Exit the application
+ private void requestExit() {
+ System.exit(0);
+ }
+
+ // Main function when we're run as an application
+ public static void main(String [] args) {
+
+ // Ensure that we're running as root; exit if not
+ if (!System.getProperty("user.name").equals("root")) {
+ System.err.println(ResourceStrings.getString("err_must_be_root"));
+ System.exit(0);
+ }
+
+ DhcpmgrApplet applet = new DhcpmgrApplet();
+ applet.startMeUp();
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DhcpmgrDialog.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DhcpmgrDialog.java
new file mode 100644
index 0000000000..10736c2881
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DhcpmgrDialog.java
@@ -0,0 +1,155 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.client;
+
+import javax.swing.JDialog;
+import javax.swing.BoxLayout;
+import javax.swing.JSeparator;
+import javax.swing.JPanel;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.Frame;
+import java.util.Vector;
+import java.util.Enumeration;
+
+import com.sun.dhcpmgr.ui.ButtonPanel;
+import com.sun.dhcpmgr.ui.ButtonPanelListener;
+
+/**
+ * This abstract class provides a framework for building all of the dialogs
+ * used by DHCP Mgr. Subclasses must implement the abstract methods defined
+ * here as well as override any other appropriate methods. Most of these
+ * methods are declared protected because they are implementation details
+ * which need not be visible outside the dialog we're actually implementing.
+ */
+public abstract class DhcpmgrDialog extends JDialog
+ implements ButtonPanelListener {
+ // Listeners receive action events when user presses OK
+ private Vector listeners;
+ // ButtonPanel is protected so subclasses can manipulate directly
+ protected ButtonPanel buttonPanel;
+
+ public DhcpmgrDialog(Frame f, boolean allowsReset) {
+ super(f);
+ listeners = new Vector();
+ // Layout is subclass main panel, then a separator, then buttons
+ getContentPane().setLayout(new BoxLayout(getContentPane(),
+ BoxLayout.Y_AXIS));
+ // Create buttonPanel first so subclasses can modify it if need be
+ buttonPanel = new ButtonPanel(allowsReset);
+ buttonPanel.addButtonPanelListener(this);
+ getContentPane().add(getMainPanel());
+ getContentPane().add(new JSeparator());
+ getContentPane().add(buttonPanel);
+ // Position relative to our owning frame
+ setLocationRelativeTo(f);
+ }
+
+ /**
+ * Return the main display panel
+ */
+ protected abstract JPanel getMainPanel();
+
+ public void addActionListener(ActionListener l) {
+ listeners.addElement(l);
+ }
+
+ public void removeActionListener(ActionListener l) {
+ listeners.removeElement(l);
+ }
+
+ /**
+ * fire the action event
+ */
+ protected abstract void fireActionPerformed();
+
+ protected void fireActionPerformed(Object source, String command) {
+ ActionEvent e = new ActionEvent(source, ActionEvent.ACTION_PERFORMED,
+ command);
+ Enumeration en = listeners.elements();
+ while (en.hasMoreElements()) {
+ ActionListener l = (ActionListener)en.nextElement();
+ l.actionPerformed(e);
+ }
+ }
+
+ /**
+ * Handle user clicks on the dialog buttons
+ */
+ public void buttonPressed(int buttonId) {
+ switch (buttonId) {
+ case OK:
+ doOk();
+ break;
+ case CANCEL:
+ doCancel();
+ break;
+ case HELP:
+ doHelp();
+ break;
+ case RESET:
+ doReset();
+ break;
+ default:
+ break;
+ }
+ }
+
+ /**
+ * Handle user pressing OK button
+ */
+ protected abstract void doOk();
+
+ /**
+ * Handle user pressing Cancel button, default is to disappear
+ */
+ protected void doCancel() {
+ setVisible(false);
+ dispose();
+ }
+
+ /**
+ * Handle user pressing Reset; this default implementation does
+ * nothing, subclasses should override.
+ */
+ protected void doReset() {
+ // Do nothing
+ }
+
+ /**
+ * Handle user pressing Help
+ */
+ protected void doHelp() {
+ DhcpmgrApplet.showHelp(getHelpKey());
+ }
+
+ /**
+ * Return the help key
+ */
+ protected abstract String getHelpKey();
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DhcptabNameField.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DhcptabNameField.java
new file mode 100644
index 0000000000..b18f11e2b0
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DhcptabNameField.java
@@ -0,0 +1,84 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1999-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.client;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import javax.swing.text.*;
+
+/**
+ * A text field which enforces the syntax rules for the name field in the
+ * dhcptab. At present, all characters must be printable ASCII, but not the
+ * comment introduction character '#'.
+ */
+public class DhcptabNameField extends JTextField {
+
+ /**
+ * Constructs a field initialized to the provided text. Defaults to
+ * 20 characters wide.
+ * @param text the text to display initially
+ */
+ public DhcptabNameField(String text) {
+ this(text, 20);
+ }
+
+ /**
+ * Constructs a field initialized to the provided text with the requested
+ * size.
+ * @param text the text to display initially
+ * @param length the length in characters the field should size itself to
+ */
+ public DhcptabNameField(String text, int length) {
+ super(text, length);
+ }
+
+ protected Document createDefaultModel() {
+ return new DhcptabNameDocument();
+ }
+}
+
+/**
+ * This is the recommended way to validate input, as opposed to trapping
+ * KeyEvents because this will actually catch paste operations as well.
+ */
+class DhcptabNameDocument extends PlainDocument {
+ public void insertString(int offs, String str, AttributeSet a)
+ throws BadLocationException {
+ if (str != null) {
+ char [] chars = str.toCharArray();
+ for (int i = 0; i < chars.length; ++i) {
+ // Must be a printable ASCII char, but not the comment character
+ if (chars[i] < ' ' || chars[i] > '~' || chars[i] == '#') {
+ throw new BadLocationException("", offs);
+ }
+ }
+ }
+ super.insertString(offs, str, a);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DialogActions.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DialogActions.java
new file mode 100644
index 0000000000..9b535453a6
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DialogActions.java
@@ -0,0 +1,43 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.client;
+
+/**
+ * This interface defines the strings used to differentiate the
+ * action events generated by the dialogs.
+ */
+public interface DialogActions {
+ public static final String OK = "ok";
+ public static final String CANCEL = "cancel";
+ public static final String CREATE = "create";
+ public static final String DUPLICATE = "duplicate";
+ public static final String EDIT = "edit";
+ public static final String DELETE = "delete";
+ public static final String DISABLE = "disable";
+ public static final String ENABLE = "enable";
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DisableServiceDialog.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DisableServiceDialog.java
new file mode 100644
index 0000000000..a411a6f021
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DisableServiceDialog.java
@@ -0,0 +1,152 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.client;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.text.MessageFormat;
+import java.util.*;
+import javax.swing.*;
+
+import com.sun.dhcpmgr.server.*;
+import com.sun.dhcpmgr.data.*;
+import com.sun.dhcpmgr.ui.*;
+
+/**
+ * This dialog handles enabling and disabling the service, with user
+ * confirmation
+ */
+public class DisableServiceDialog extends DhcpmgrDialog {
+ private boolean disable;
+ private JLabel message;
+
+ public DisableServiceDialog(Frame f, boolean disable) {
+ super(f, false);
+
+ this.disable = disable;
+
+ String name = DataManager.get().getShortServerName();
+ if (disable) {
+ setTitle(ResourceStrings.getString("disable_service_title"));
+ if (DhcpmgrApplet.modeIsRelay) {
+ message.setText(MessageFormat.format(
+ ResourceStrings.getString("disable_relay_confirm"), name));
+ message.setToolTipText(MessageFormat.format(
+ ResourceStrings.getString("disable_relay_confirm"), name));
+ } else {
+ message.setText(MessageFormat.format(
+ ResourceStrings.getString("disable_service_confirm"),
+ name));
+ message.setToolTipText(MessageFormat.format(
+ ResourceStrings.getString("disable_service_confirm"),
+ name));
+ }
+ } else {
+ setTitle(ResourceStrings.getString("enable_service_title"));
+ if (DhcpmgrApplet.modeIsRelay) {
+ message.setText(MessageFormat.format(
+ ResourceStrings.getString("enable_relay_confirm"), name));
+ message.setToolTipText(MessageFormat.format(
+ ResourceStrings.getString("enable_relay_confirm"), name));
+ } else {
+ message.setText(MessageFormat.format(
+ ResourceStrings.getString("enable_service_confirm"), name));
+ message.setToolTipText(MessageFormat.format(
+ ResourceStrings.getString("enable_service_confirm"), name));
+ }
+ }
+ }
+
+ protected JPanel getMainPanel() {
+ JPanel panel = new JPanel();
+ panel.setBorder(BorderFactory.createEmptyBorder(20, 10, 20, 10));
+ message = new JLabel();
+ panel.add(message);
+
+ buttonPanel.setOkEnabled(true);
+ return panel;
+ }
+
+ protected void doOk() {
+ try {
+ DhcpServiceMgr server = DataManager.get().getDhcpServiceMgr();
+ DhcpdOptions opts =
+ DataManager.get().getDhcpServiceMgr().readDefaults();
+ if (disable) {
+ // Shutdown the server and disable the daemon.
+ server.shutdown();
+ opts.setDaemonEnabled(false);
+ DataManager.get().getDhcpServiceMgr().writeDefaults(opts);
+ } else {
+ // Enable = reverse the process
+ opts.setDaemonEnabled(true);
+ DataManager.get().getDhcpServiceMgr().writeDefaults(opts);
+ server.startup();
+ }
+ fireActionPerformed();
+ setVisible(false);
+ dispose();
+ } catch (Exception e) {
+ e.printStackTrace();
+ MessageFormat form = null;
+ Object [] args = new Object[1];
+ if (disable) {
+ form = new MessageFormat(
+ ResourceStrings.getString("disable_service_error"));
+ } else {
+ form = new MessageFormat(
+ ResourceStrings.getString("enable_service_error"));
+ }
+ args[0] = e.getMessage();
+ JOptionPane.showMessageDialog(this, form.format(args),
+ ResourceStrings.getString("server_error_title"),
+ JOptionPane.ERROR_MESSAGE);
+ }
+ }
+
+ protected String getHelpKey() {
+ if (disable) {
+ return "disable_service";
+ } else {
+ return "enable_service";
+ }
+ }
+
+ /**
+ * Notify listeners that enable or disable has been done.
+ */
+ protected void fireActionPerformed() {
+ String cmd;
+ if (disable) {
+ cmd = DialogActions.DISABLE;
+ } else {
+ cmd = DialogActions.ENABLE;
+ }
+ fireActionPerformed(this, cmd);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ErrorTable.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ErrorTable.java
new file mode 100644
index 0000000000..1cb70f1139
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ErrorTable.java
@@ -0,0 +1,122 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1999-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.client;
+
+import javax.swing.table.AbstractTableModel;
+import java.util.Vector;
+import java.util.Date;
+
+import com.sun.dhcpmgr.ui.AutosizingTable;
+import com.sun.dhcpmgr.ui.ExtendedCellRenderer;
+import com.sun.dhcpmgr.data.IPAddress;
+import com.sun.dhcpmgr.data.ActionError;
+
+/*
+ * The model for the error table.
+ */
+class ErrorTableModel extends AbstractTableModel {
+ private String column0Label;
+ private Class column0Class;
+ private Vector rows;
+
+ public ErrorTableModel(String column0Label, Class column0Class) {
+ this.column0Label = column0Label;
+ this.column0Class = column0Class;
+ rows = new Vector();
+ }
+
+ public int getRowCount() {
+ return rows.size();
+ }
+
+ public int getColumnCount() {
+ return 2;
+ }
+
+ public Object getValueAt(int row, int column) {
+ Object [] ro = (Object [])rows.elementAt(row);
+ return ro[column];
+ }
+
+ public Class getColumnClass(int column) {
+ if (column == 0) {
+ return column0Class;
+ } else {
+ return String.class;
+ }
+ }
+
+ public String getColumnName(int column) {
+ if (column == 0) {
+ return column0Label;
+ } else {
+ return ResourceStrings.getString("error_message");
+ }
+ }
+
+ public void addError(Object o, String msg) {
+ Object [] row = new Object[] { o, msg };
+ rows.addElement(row);
+ }
+}
+
+/**
+ * A table for displaying errors which occurred while acting on multiple
+ * objects.
+ */
+public class ErrorTable extends AutosizingTable {
+ ErrorTableModel model;
+
+ public ErrorTable(String column0Label, Class column0Class) {
+ super();
+ model = new ErrorTableModel(column0Label, column0Class);
+ setModel(model);
+ ExtendedCellRenderer renderer = new ExtendedCellRenderer();
+ setDefaultRenderer(Date.class, renderer);
+ setDefaultRenderer(IPAddress.class, renderer);
+ }
+
+ public ErrorTable(String column0Label) {
+ this(column0Label, String.class);
+ }
+
+ public void addError(Object o, String msg) {
+ model.addError(o, msg);
+ }
+
+ public void setErrors(ActionError [] errs) {
+ for (int i = 0; i < errs.length; ++i) {
+ model.addError(errs[i].getName(),
+ errs[i].getException().getMessage());
+ }
+ }
+
+ public boolean isEmpty() {
+ return (model.getRowCount() == 0);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ExportWizard.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ExportWizard.java
new file mode 100644
index 0000000000..b9c6db116d
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ExportWizard.java
@@ -0,0 +1,630 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2001-2002 by Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+package com.sun.dhcpmgr.client;
+
+import javax.swing.*;
+import javax.swing.event.*;
+import java.awt.*;
+import java.text.MessageFormat;
+import java.lang.reflect.InvocationTargetException;
+
+import com.sun.dhcpmgr.server.DhcpMgr;
+import com.sun.dhcpmgr.ui.*;
+import com.sun.dhcpmgr.data.*;
+import com.sun.dhcpmgr.common.*;
+import com.sun.dhcpmgr.bridge.ExistsException;
+
+/**
+ * ExportWizard provides an easy-to-use interface for exporting the data
+ * from one DHCP server to be later imported by another DHCP server, typically
+ * because the administrator wishes to repartition the workload among DHCP
+ * servers.
+ */
+public class ExportWizard extends Wizard {
+
+ // Step to collect the networks to be exported
+ class NetworkStep implements WizardStep {
+ Box stepBox;
+ ListPair networkLists;
+
+ public NetworkStep() {
+ stepBox = Box.createVerticalBox();
+ stepBox.add(Wizard.createTextArea(
+ ResourceStrings.getString("exp_wiz_net_explain"), 9, 45));
+ stepBox.add(Box.createVerticalStrut(10));
+ // XXX This try/catch goes away at Snakebite integration
+ try {
+ networkLists = new ListPair(
+ ResourceStrings.getString("exp_wiz_dont_export"),
+ DataManager.get().getNetworks(false),
+ ResourceStrings.getString("exp_wiz_export"), networks);
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
+ stepBox.add(networkLists);
+ stepBox.add(Box.createVerticalGlue());
+ }
+
+ public String getDescription() {
+ return ResourceStrings.getString("exp_wiz_net_desc");
+ }
+
+ public Component getComponent() {
+ return stepBox;
+ }
+
+ public void setActive(int direction) {
+ setForwardEnabled(true);
+ }
+
+ public boolean setInactive(int direction) {
+ networks =
+ (Network [])networkLists.getRightContents(new Network[0]);
+ return true;
+ }
+ }
+
+ // Step to collect the macros to be exported
+ class MacroStep implements WizardStep {
+ Box stepBox;
+ ListPair macroLists;
+
+ public MacroStep() {
+ stepBox = Box.createVerticalBox();
+ stepBox.add(Wizard.createTextArea(
+ ResourceStrings.getString("exp_wiz_macros_explain"), 4, 45));
+ stepBox.add(Box.createVerticalStrut(10));
+ // XXX This try/catch will go away at Snakebite integration
+ try {
+ Macro [] macros = DataManager.get().getMacros(false);
+ macroNames = new String[macros.length];
+ for (int i = 0; i < macros.length; ++i) {
+ macroNames[i] = macros[i].getKey();
+ }
+ macroLists = new ListPair(
+ ResourceStrings.getString("exp_wiz_dont_export"), macroNames,
+ ResourceStrings.getString("exp_wiz_export"), null);
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
+ stepBox.add(macroLists);
+ stepBox.add(Box.createVerticalGlue());
+ }
+
+ public String getDescription() {
+ return ResourceStrings.getString("exp_wiz_macro_desc");
+ }
+
+ public Component getComponent() {
+ return stepBox;
+ }
+
+ public void setActive(int direction) {
+ setForwardEnabled(true);
+ }
+
+ public boolean setInactive(int direction) {
+ macroNames = (String [])macroLists.getRightContents(new String[0]);
+ return true;
+ }
+ }
+
+ // Step to collect the options to be exported
+ class OptionStep implements WizardStep {
+ Box stepBox;
+ ListPair optionLists;
+
+ public OptionStep() {
+ stepBox = Box.createVerticalBox();
+ stepBox.add(Wizard.createTextArea(
+ ResourceStrings.getString("exp_wiz_options_explain"), 4, 45));
+ stepBox.add(Box.createVerticalStrut(10));
+ // XXX This try/catch will go away at Snakebite integration
+ try {
+ Option[] options = DataManager.get().getOptions(false);
+ optionNames = new String[options.length];
+ for (int i = 0; i < options.length; ++i) {
+ optionNames[i] = options[i].getKey();
+ }
+ optionLists = new ListPair(
+ ResourceStrings.getString("exp_wiz_dont_export"), optionNames,
+ ResourceStrings.getString("exp_wiz_export"), null);
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
+ stepBox.add(optionLists);
+ stepBox.add(Box.createVerticalGlue());
+ }
+
+ public String getDescription() {
+ return ResourceStrings.getString("exp_wiz_option_desc");
+ }
+
+ public Component getComponent() {
+ return stepBox;
+ }
+
+ public void setActive(int direction) {
+ setForwardEnabled(true);
+ }
+
+ public boolean setInactive(int direction) {
+ optionNames =
+ (String [])optionLists.getRightContents(new String[0]);
+ return true;
+ }
+ }
+
+ // Step to collect the name of the file to which data is exported
+ class FileStep implements WizardStep {
+ JPanel stepPanel;
+ NoSpaceField pathField;
+ JCheckBox deleteBox;
+
+ public FileStep() {
+ GridBagLayout bag = new GridBagLayout();
+ GridBagConstraints con = new GridBagConstraints();
+ con.insets = new Insets(2, 2, 2, 2);
+ con.gridx = con.gridy = 0;
+ con.gridwidth = 2;
+ con.gridheight = 1;
+ con.weightx = 0;
+ con.weighty = 0.5;
+ con.fill = GridBagConstraints.BOTH;
+ con.anchor = GridBagConstraints.NORTHWEST;
+ stepPanel = new JPanel(bag);
+ Component c = Wizard.createTextArea(
+ ResourceStrings.getString("exp_wiz_file_explain"), 4, 45);
+ bag.setConstraints(c, con);
+ stepPanel.add(c);
+
+ Mnemonic mnFile =
+ new Mnemonic(ResourceStrings.getString("exp_wiz_file_label"));
+ JLabel l = new JLabel(mnFile.getString());
+ l.setLabelFor(stepPanel);
+ l.setToolTipText(mnFile.getString());
+ l.setDisplayedMnemonic(mnFile.getMnemonic());
+
+ con.gridwidth = con.gridheight = 1;
+ con.fill = GridBagConstraints.NONE;
+ con.weighty = 0;
+ ++con.gridy;
+ bag.setConstraints(l, con);
+ stepPanel.add(l);
+
+ pathField = new NoSpaceField(exportPath);
+ l.setLabelFor(pathField);
+ ++con.gridx;
+ con.weightx = 1.0;
+ con.fill = GridBagConstraints.HORIZONTAL;
+ bag.setConstraints(pathField, con);
+ stepPanel.add(pathField);
+
+ c = Box.createVerticalStrut(10);
+ ++con.gridy;
+ con.gridx = 0;
+ con.weightx = 0;
+ bag.setConstraints(c, con);
+ stepPanel.add(c);
+
+ c = Wizard.createTextArea(
+ ResourceStrings.getString("exp_wiz_delete_explain"), 4, 45);
+ ++con.gridy;
+ con.gridx = 0;
+ con.weightx = 1.0;
+ con.weighty = 0.5;
+ con.gridwidth = 2;
+ con.fill = GridBagConstraints.BOTH;
+ bag.setConstraints(c, con);
+ stepPanel.add(c);
+
+ deleteBox = new JCheckBox(
+ ResourceStrings.getString("exp_wiz_delete_exported"), false);
+ deleteBox.setToolTipText(
+ ResourceStrings.getString("exp_wiz_delete_exported"));
+ con.gridx = 0;
+ ++con.gridy;
+ con.fill = GridBagConstraints.HORIZONTAL;
+ bag.setConstraints(deleteBox, con);
+ stepPanel.add(deleteBox);
+
+ c = Box.createVerticalGlue();
+ ++con.gridy;
+ con.weighty = 1.0;
+ bag.setConstraints(c, con);
+ stepPanel.add(c);
+
+ // Enable forward only if something is entered in the file field
+ pathField.getDocument().addDocumentListener(new DocumentListener() {
+ public void insertUpdate(DocumentEvent e) {
+ setForwardEnabled(pathField.getText().length() != 0);
+ }
+ public void changedUpdate(DocumentEvent e) {
+ insertUpdate(e);
+ }
+ public void removeUpdate(DocumentEvent e) {
+ insertUpdate(e);
+ }
+ });
+
+ }
+
+ public String getDescription() {
+ return ResourceStrings.getString("exp_wiz_file_desc");
+ }
+
+ public Component getComponent() {
+ return stepPanel;
+ }
+
+ public void setActive(int direction) {
+ pathField.setText(exportPath);
+ deleteBox.setSelected(deleteData);
+ }
+
+ public boolean setInactive(int direction) {
+ exportPath = pathField.getText();
+ deleteData = deleteBox.isSelected();
+ return true;
+ }
+ }
+
+ // Review everything before doing it
+ class ReviewStep implements WizardStep {
+ JPanel mainPanel;
+ JList networkList, macroList, optionList;
+ JLabel exportLabel, deleteLabel;
+
+ public ReviewStep() {
+ mainPanel = new JPanel(new BorderLayout());
+ mainPanel.add(Wizard.createTextArea(
+ ResourceStrings.getString("exp_wiz_review_explain"), 6, 45),
+ BorderLayout.NORTH);
+ JPanel fieldPanel = new JPanel(new FieldLayout());
+
+ Mnemonic mnNets =
+ new Mnemonic(ResourceStrings.getString("exp_wiz_review_nets"));
+ JLabel revNetsLbl = new JLabel(mnNets.getString());
+ revNetsLbl.setToolTipText(mnNets.getString());
+ fieldPanel.add(revNetsLbl, FieldLayout.LABELTOP);
+ networkList = new JList();
+ revNetsLbl.setLabelFor(networkList);
+ JScrollPane scrollPane = new JScrollPane(networkList);
+ fieldPanel.add(scrollPane, FieldLayout.FIELD);
+ revNetsLbl.setDisplayedMnemonic(mnNets.getMnemonic());
+
+ Mnemonic mnMacros =
+ new Mnemonic(ResourceStrings.getString(
+ "exp_wiz_review_macros"));
+ JLabel revMacLbl = new JLabel(mnMacros.getString());
+ revMacLbl.setToolTipText(mnMacros.getString());
+ fieldPanel.add(revMacLbl, FieldLayout.LABELTOP);
+ macroList = new JList();
+ revMacLbl.setLabelFor(macroList);
+ scrollPane = new JScrollPane(macroList);
+ fieldPanel.add(scrollPane, FieldLayout.FIELD);
+ revMacLbl.setDisplayedMnemonic(mnMacros.getMnemonic());
+
+ Mnemonic mnOpt =
+ new Mnemonic(ResourceStrings.getString(
+ "exp_wiz_review_options"));
+ JLabel optLbl = new JLabel(mnOpt.getString());
+ fieldPanel.add(optLbl, FieldLayout.LABELTOP);
+ optLbl.setToolTipText(mnOpt.getString());
+ optionList = new JList();
+ optLbl.setLabelFor(optionList);
+ scrollPane = new JScrollPane(optionList);
+ fieldPanel.add(scrollPane, FieldLayout.FIELD);
+ optLbl.setDisplayedMnemonic(mnOpt.getMnemonic());
+
+ Mnemonic mnFileRvw =
+ new Mnemonic(ResourceStrings.getString("exp_wiz_file_label"));
+ JLabel fileLbl =
+ new JLabel(mnFileRvw.getString());
+ fileLbl.setLabelFor(fieldPanel);
+ fileLbl.setToolTipText(mnFileRvw.getString());
+ fieldPanel.add(fileLbl, FieldLayout.LABEL);
+ exportLabel = new JLabel();
+ exportLabel.setForeground(Color.black);
+ fieldPanel.add(exportLabel, FieldLayout.FIELD);
+
+ JLabel delLbl =
+ new JLabel(ResourceStrings.getString("exp_wiz_delete_label"));
+ delLbl.setLabelFor(fieldPanel);
+ delLbl.setToolTipText(ResourceStrings.getString(
+ "exp_wiz_delete_label"));
+ fieldPanel.add(delLbl, FieldLayout.LABEL);
+
+ deleteLabel = new JLabel();
+ deleteLabel.setForeground(Color.black);
+ fieldPanel.add(deleteLabel, FieldLayout.FIELD);
+
+ mainPanel.add(fieldPanel, BorderLayout.CENTER);
+ }
+
+ public String getDescription() {
+ return ResourceStrings.getString("exp_wiz_review_desc");
+ }
+
+ public Component getComponent() {
+ return mainPanel;
+ }
+
+ public void setActive(int direction) {
+ networkList.setListData(networks);
+ macroList.setListData(macroNames);
+ optionList.setListData(optionNames);
+ exportLabel.setText(exportPath);
+ if (deleteData) {
+ deleteLabel.setText(ResourceStrings.getString("yes"));
+ } else {
+ deleteLabel.setText(ResourceStrings.getString("no"));
+ }
+ setFinishEnabled(true);
+ }
+
+ public boolean setInactive(int direction) {
+ // Nothing to do here
+ return true;
+ }
+ }
+
+ /*
+ * Display an error message in its own thread. This allows a task running
+ * in a non-GUI thread to get the message displayed by the toolkit.
+ */
+ class ErrorDisplay implements Runnable {
+ Object [] objs;
+
+ public ErrorDisplay(Object [] objs) {
+ this.objs = objs;
+ }
+
+ public void run() {
+ JOptionPane.showMessageDialog(ExportWizard.this, objs,
+ ResourceStrings.getString("server_error_title"),
+ JOptionPane.ERROR_MESSAGE);
+ }
+ }
+
+ /*
+ * Display a warning message in its own thread. This allows a task running
+ * in a non-GUI thread to get the message displayed by the toolkit.
+ */
+ class WarningDisplay implements Runnable {
+ Object [] objs;
+
+ public WarningDisplay(Object [] objs) {
+ this.objs = objs;
+ }
+
+ public void run() {
+ JOptionPane.showMessageDialog(ExportWizard.this, objs,
+ ResourceStrings.getString("server_warning_title"),
+ JOptionPane.WARNING_MESSAGE);
+ }
+ }
+
+ /*
+ * Class to ask the user whether the export file should be forcibly
+ * overwritten if it already exists. We default to not overwriting
+ * export files. This is a Runnable in order to allow it to be displayed
+ * by the export thread, which is a non-GUI thread.
+ */
+ class OverwritePrompter implements Runnable {
+ /*
+ * overwrite member is public so we can access it directly since run()
+ * can't return the user's input.
+ */
+ public boolean overwrite;
+
+ public OverwritePrompter() {
+ overwrite = false;
+ }
+
+ public void run() {
+ int ret = JOptionPane.showConfirmDialog(
+ ExportWizard.this,
+ ResourceStrings.getString("exp_overwrite"),
+ ResourceStrings.getString("exp_overwrite_title"),
+ JOptionPane.YES_NO_OPTION,
+ JOptionPane.QUESTION_MESSAGE);
+ // Return true if the user clicked Yes
+ overwrite = (ret == JOptionPane.YES_OPTION);
+ }
+ }
+
+ private Network [] networks = new Network[0];
+ private String [] macroNames = new String[0];
+ private String [] optionNames = new String[0];
+ private boolean deleteData = false;
+ private String exportPath = "";
+
+ public ExportWizard(Frame owner) {
+ super(owner, "");
+ setTitle(ResourceStrings.getString("export_wiz_title"));
+
+ // Insert steps in order of execution
+ addStep(new NetworkStep());
+ addStep(new MacroStep());
+ addStep(new OptionStep());
+ addStep(new FileStep());
+ addStep(new ReviewStep());
+
+ showFirstStep();
+ }
+
+ /*
+ * Execute the export. This is relatively complicated because we want to
+ * run the actual export in a background thread so that the whole GUI isn't
+ * tied up during the export. Also, the actual export logic is implemented
+ * by the ExportController, and we provide an Exporter implementation
+ * which allows it to interact with the user. So, the ExportController is
+ * executed in a background thread, and the callbacks implemented in the
+ * Exporter must each use SwingUtilities.invoke* methods to ask for the
+ * UI updates to happen. In the case of the progress display, the
+ * ProgressManager class already implements that logic so it's simpler
+ * than the rest of the interactions.
+ */
+ public void doFinish() {
+ /*
+ * This runnable serves merely to allow the background thread used for
+ * export to tell the GUI that it's done.
+ */
+ final Runnable finisher = new Runnable() {
+ public void run() {
+ reallyFinish();
+ }
+ };
+
+ /*
+ * Create callback interface used by ExportController to interact with
+ * the user.
+ */
+ Exporter exporter = new Exporter() {
+ ProgressManager progress;
+ String [] errObjs = new String[] {
+ ResourceStrings.getString("exp_error_occurred"),
+ ""
+ };
+
+ // Set up the progress display
+ public void initializeProgress(int length) {
+ progress = new ProgressManager(ExportWizard.this,
+ ResourceStrings.getString("exp_progress_title"), "", 0,
+ length);
+ }
+
+ // Update progress to current point, updating message
+ public void updateProgress(int done, String message)
+ throws InterruptedException {
+ progress.update(done, message);
+ }
+
+ // Display an error message
+ public void displayError(String message) {
+ errObjs[1] = message;
+ try {
+ SwingUtilities.invokeAndWait(new ErrorDisplay(errObjs));
+ } catch (InvocationTargetException ex2) {
+ // ErrorDisplay threw an exception; give up!
+ ex2.printStackTrace();
+ } catch (InterruptedException ex2) {
+ // ErrorDisplay was interrupted; give up!
+ ex2.printStackTrace();
+ }
+ }
+
+ // Display a bunch of error messages in a table
+ public void displayErrors(String msg, String label,
+ ActionError [] errs) {
+ ErrorTable errTable = new ErrorTable(label);
+ errTable.setErrors(errs);
+ JScrollPane scrollPane = new JScrollPane(errTable);
+ Object [] warnObjs = new Object [] { msg, scrollPane };
+ try {
+ SwingUtilities.invokeAndWait(new WarningDisplay(warnObjs));
+ } catch (InvocationTargetException e) {
+ // WarningDisplay threw an exception; just dump it
+ e.printStackTrace();
+ } catch (InterruptedException e) {
+ // WarningDisplay was interrupted; just dump it
+ e.printStackTrace();
+ }
+ }
+ };
+
+ /*
+ * Create the export controller and set parameters. Use final so
+ * that the exportThread can reference it.
+ */
+ final ExportController exportController = new ExportController(exporter,
+ DataManager.get().getServer());
+ exportController.setUser(System.getProperty("user.name"));
+ exportController.setFile(exportPath);
+ exportController.setOptions(optionNames);
+ exportController.setMacros(macroNames);
+ exportController.setNetworks(networks);
+
+ // Now create the thread that does the exporting
+ Thread exportThread = new Thread() {
+ public void run() {
+ OverwritePrompter prompter = new OverwritePrompter();
+ while (true) {
+ try {
+ /*
+ * Controller will return true if it completed
+ * successfully, in which case we want to exit the
+ * wizard; if it returns false, just exit this
+ * thread but leave the wizard up.
+ */
+ if (exportController.exportData(deleteData,
+ prompter.overwrite)) {
+ SwingUtilities.invokeLater(finisher);
+ }
+ return;
+ } catch (ExistsException e) {
+ // Export file already existed and overwrite was false
+ try {
+ SwingUtilities.invokeAndWait(prompter);
+ /*
+ * If user said not to overwrite, then exit
+ * this thread but leave wizard up. Otherwise just
+ * let the while loop try the export again.
+ */
+ if (!prompter.overwrite) {
+ return;
+ }
+ } catch (Throwable t) {
+ /*
+ * We can get an interrupt or prompter could
+ * throw an exception; the only reasonable
+ * thing to do at this point is just display
+ * the stack and return.
+ */
+ t.printStackTrace();
+ return;
+ }
+ }
+ }
+ }
+ };
+
+ // Now run the export thread
+ exportThread.start();
+ }
+
+ protected void reallyFinish() {
+ super.doFinish();
+ }
+
+ public void doHelp() {
+ DhcpmgrApplet.showHelp("export_wizard");
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/HelpBundle.properties b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/HelpBundle.properties
new file mode 100644
index 0000000000..c7f99b95fe
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/HelpBundle.properties
@@ -0,0 +1,107 @@
+#
+#ident "%W% %E% SMI"
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Resources for locating the correct help file for a particular window
+#
+# L10N instructions
+# -----------------
+# Each line of this file is of the form:
+#
+# key=value
+#
+# The key portion must not be localized, as it is a constant in the program.
+# The value portion should be modified appropriately for each locale to point
+# to the location of the localized version of the html file relative to the
+# directory:
+#
+# /usr/share/lib/locale/com/sun/dhcpmgr/client/help
+#
+# So that for the "de" locale the first entry in this file should look like:
+#
+# create_address=de/dhcp_addr_create.html
+#
+# resulting in that help file being located at:
+#
+# /usr/share/lib/locale/com/sun/dhcpmgr/client/help/de/dhcp_addr_create.html
+#
+# And the corresponding name of THIS file would be:
+#
+# /usr/share/lib/locale/com/sun/dhcpmgr/client/HelpBundle_de.properties
+#
+# At the present time it is a requirement that the above convention be followed
+# with respect to the relative path name as the l10n code in HelpIds.java
+# uses the presence of a '/' in the value as the indicator of whether a
+# localized instance of this file is being used. See bug 4177489 if you
+# want to understand why this is so.
+#
+create_address=dhcp_addr_create.html
+delete_address=dhcp_addr_del.html
+duplicate_address=dhcp_addr_dup.html
+XXX1=dhcp_addr_how.html
+modify_address=dhcp_addr_mod.html
+modify_multiple_addresses=dhcp_addr_multi.html
+addresses_reference=dhcp_addr_ref.html
+release_addresses=dhcp_addr_rel.html
+XXX3=dhcp_addr_view.html
+address_wizard=dhcp_addr_wiz.html
+config_wizard=dhcp_config_wiz.html
+create_macro=dhcp_macro_create.html
+delete_macro=dhcp_macro_del.html
+duplicate_macro=dhcp_macro_dup.html
+XXX4=dhcp_macro_how.html
+modify_macro=dhcp_macro_mod.html
+macros_reference=dhcp_macro_ref.html
+XXX6=dhcp_macro_view.html
+XXX7=dhcp_macros_about.html
+XXX8=dhcp_main_hlp.html
+howto=dhcp_main_how.html
+index=dhcp_main_idx.html
+XXX11=dhcp_main_menus.html
+overview=dhcp_main_top.html
+delete_network=dhcp_net_del.html
+XXX13=dhcp_net_ref.html
+network_wizard=dhcp_net_wiz.html
+create_option=dhcp_option_create.html
+delete_option=dhcp_option_del.html
+duplicate_option=dhcp_option_dup.html
+XXX14=dhcp_option_how.html
+modify_option=dhcp_option_mod.html
+options_reference=dhcp_option_ref.html
+XXX16=dhcp_option_view.html
+server_config=dhcp_relay_choose.html
+configure_relay=dhcp_relay_config.html
+XXX17=dhcp_relay_how.html
+service_reference=dhcp_relay_ref.html
+modify_relay=dhcp_relay_serv.html
+unconfigure_relay=dhcp_relay_unconfig.html
+modify_server=dhcp_server_serv.html
+unconfigure_server=dhcp_server_unconfig.html
+XXX19=dhcp_solaris_about.html
+enable_service=dhcp_relay_enable.html
+disable_service=dhcp_relay_dis.html
+convert_wizard=dhcp_convert_wiz.html
+export_wizard=dhcp_export_wiz.html
+import_wizard=dhcp_import_wiz.html
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ImportWizard.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ImportWizard.java
new file mode 100644
index 0000000000..6d3d51d0a4
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ImportWizard.java
@@ -0,0 +1,437 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2001-2002 by Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+package com.sun.dhcpmgr.client;
+
+import javax.swing.*;
+import javax.swing.event.*;
+import java.awt.*;
+import java.util.Date;
+import java.text.SimpleDateFormat;
+import java.text.MessageFormat;
+import java.lang.reflect.InvocationTargetException;
+import java.io.FileNotFoundException;
+
+import com.sun.dhcpmgr.ui.*;
+import com.sun.dhcpmgr.data.ActionError;
+import com.sun.dhcpmgr.data.ExportHeader;
+import com.sun.dhcpmgr.common.*;
+
+/**
+ * ImportWizard provides an easy-to-use interface for importing configuration
+ * data from one DHCP server to another.
+ *
+ * @see ExportWizard
+ */
+public class ImportWizard extends Wizard {
+
+ // Step to get the location of the export file
+ class LocationStep implements WizardStep {
+ JPanel stepPanel;
+ JTextField pathField;
+ JCheckBox overrideBox;
+
+ public LocationStep() {
+ GridBagLayout bag = new GridBagLayout();
+ GridBagConstraints con = new GridBagConstraints();
+ con.insets = new Insets(2, 2, 2, 2);
+ con.gridx = con.gridy = 0;
+ con.gridwidth = 2;
+ con.gridheight = 1;
+ con.weightx = 0;
+ con.weighty = 1.0;
+ con.fill = GridBagConstraints.BOTH;
+ con.anchor = GridBagConstraints.NORTHWEST;
+
+ stepPanel = new JPanel(bag);
+
+ Component c = Wizard.createTextArea(
+ ResourceStrings.getString("imp_wiz_location_explain"), 8, 45);
+ bag.setConstraints(c, con);
+ stepPanel.add(c);
+
+ Mnemonic mnFile =
+ new Mnemonic(ResourceStrings.getString("imp_wiz_file_label"));
+ JLabel l = new JLabel(mnFile.getString());
+ l.setToolTipText(mnFile.getString());
+ l.setDisplayedMnemonic(mnFile.getMnemonic());
+
+ ++con.gridy;
+ con.gridwidth = 1;
+ con.weighty = 0;
+ con.fill = GridBagConstraints.HORIZONTAL;
+ bag.setConstraints(l, con);
+ stepPanel.add(l);
+
+ pathField = new JTextField(importPath);
+ l.setLabelFor(pathField);
+ ++con.gridx;
+ con.weightx = 1.0;
+ bag.setConstraints(pathField, con);
+ stepPanel.add(pathField);
+
+ c = Wizard.createTextArea(
+ ResourceStrings.getString("imp_wiz_override_explain"), 4, 45);
+ con.gridx = 0;
+ ++con.gridy;
+ con.weighty = 0.5;
+ con.weightx = 0;
+ con.fill = GridBagConstraints.BOTH;
+ con.gridwidth = 2;
+ bag.setConstraints(c, con);
+ stepPanel.add(c);
+
+ overrideBox = new JCheckBox(
+ ResourceStrings.getString("imp_wiz_override_data"), false);
+ overrideBox.setToolTipText(
+ ResourceStrings.getString("imp_wiz_override_data"));
+ con.gridx = 0;
+ ++con.gridy;
+ con.gridwidth = 2;
+ con.weighty = 0;
+ con.weightx = 1.0;
+ con.fill = GridBagConstraints.HORIZONTAL;
+ bag.setConstraints(overrideBox, con);
+ stepPanel.add(overrideBox);
+
+ c = Box.createVerticalGlue();
+ ++con.gridy;
+ con.weighty = 1.0;
+ con.weightx = 0;
+ con.fill = GridBagConstraints.VERTICAL;
+ bag.setConstraints(c, con);
+ stepPanel.add(c);
+
+ // Enable forward only if something is entered in the file field
+ pathField.getDocument().addDocumentListener(new DocumentListener() {
+ public void insertUpdate(DocumentEvent e) {
+ setForwardEnabled(pathField.getText().length() != 0);
+ }
+ public void changedUpdate(DocumentEvent e) {
+ insertUpdate(e);
+ }
+ public void removeUpdate(DocumentEvent e) {
+ insertUpdate(e);
+ }
+ });
+ }
+
+ public String getDescription() {
+ return ResourceStrings.getString("imp_wiz_file_desc");
+ }
+
+ public Component getComponent() {
+ return stepPanel;
+ }
+
+ public void setActive(int direction) {
+ pathField.setText(importPath);
+ overrideBox.setSelected(conflictImport);
+ setForwardEnabled(importPath.length() != 0);
+ }
+
+ public boolean setInactive(int direction) {
+ importPath = pathField.getText();
+ conflictImport = overrideBox.isSelected();
+ /*
+ * Read the file header for display in next step; if we can't read
+ * it, display the errors and veto the forward step.
+ */
+ if (direction == FORWARD) {
+ importController.setFile(importPath);
+ try {
+ header = importController.getHeader();
+ if (header == null) {
+ // Something wrong, but controller already displayed err
+ return false;
+ }
+ } catch (FileNotFoundException e) {
+ JOptionPane.showMessageDialog(ImportWizard.this,
+ ResourceStrings.getString("imp_err_file_not_found"),
+ ResourceStrings.getString("server_error_title"),
+ JOptionPane.ERROR_MESSAGE);
+ return false;
+ } catch (Exception e) {
+ String [] msgs = new String [] {
+ ResourceStrings.getString("imp_err_reading_header"),
+ e.getMessage()
+ };
+ JOptionPane.showMessageDialog(ImportWizard.this, msgs,
+ ResourceStrings.getString("server_error_title"),
+ JOptionPane.ERROR_MESSAGE);
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+
+ // Allow user to review summary of file contents before proceeding.
+ class ReviewStep implements WizardStep {
+ private Box stepBox;
+ private JLabel fileLabel, srcLabel, userLabel, dateLabel, overrideLabel;
+ private JLabel infoLabel;
+ private SimpleDateFormat dateFormat = new SimpleDateFormat();
+ private MessageFormat infoFormat =
+ new MessageFormat(ResourceStrings.getString("imp_wiz_review_info"));
+
+ public ReviewStep() {
+ stepBox = Box.createVerticalBox();
+ JComponent jc = Wizard.createTextArea(
+ ResourceStrings.getString("imp_wiz_review_explain"), 6, 45);
+ jc.setAlignmentX(Component.LEFT_ALIGNMENT);
+ stepBox.add(jc);
+
+ Mnemonic mnFl =
+ new Mnemonic(ResourceStrings.getString("imp_wiz_file_label"));
+ JPanel fieldPanel = new JPanel(new FieldLayout());
+ JLabel l = new JLabel(mnFl.getString());
+ l.setLabelFor(fieldPanel);
+ l.setToolTipText(mnFl.getString());
+ fieldPanel.add(l, FieldLayout.LABEL);
+
+ fileLabel = new JLabel();
+ fileLabel.setForeground(Color.black);
+ fieldPanel.add(fileLabel, FieldLayout.FIELD);
+
+ l = new JLabel(
+ ResourceStrings.getString("imp_wiz_review_override"));
+ fieldPanel.add(l, FieldLayout.LABEL);
+ l.setToolTipText(
+ ResourceStrings.getString("imp_wiz_review_override"));
+
+ overrideLabel = new JLabel();
+ l.setLabelFor(overrideLabel);
+ overrideLabel.setForeground(Color.black);
+ fieldPanel.add(overrideLabel, FieldLayout.FIELD);
+
+ fieldPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
+ stepBox.add(fieldPanel);
+
+ stepBox.add(Box.createVerticalStrut(5));
+
+ infoLabel = new JLabel();
+ infoLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
+ stepBox.add(infoLabel);
+
+ l = new JLabel(
+ ResourceStrings.getString("imp_wiz_review_src"));
+ fieldPanel = new JPanel(new FieldLayout());
+ fieldPanel.add(l, FieldLayout.LABEL);
+ l.setToolTipText(ResourceStrings.getString("imp_wiz_review_src"));
+
+ srcLabel = new JLabel();
+ l.setLabelFor(srcLabel);
+ fieldPanel.add(srcLabel, FieldLayout.FIELD);
+
+ l = new JLabel(
+ ResourceStrings.getString("imp_wiz_review_user"));
+ fieldPanel.add(l, FieldLayout.LABEL);
+ l.setToolTipText(ResourceStrings.getString("imp_wiz_review_user"));
+
+ userLabel = new JLabel();
+ l.setLabelFor(userLabel);
+ fieldPanel.add(userLabel, FieldLayout.FIELD);
+
+ l = new JLabel(
+ ResourceStrings.getString("imp_wiz_review_date"));
+ fieldPanel.add(l, FieldLayout.LABEL);
+ l.setToolTipText(ResourceStrings.getString("imp_wiz_review_date"));
+
+ dateLabel = new JLabel(dateFormat.format(new Date()));
+ l.setLabelFor(dateLabel);
+ fieldPanel.add(dateLabel, FieldLayout.FIELD);
+
+ fieldPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
+ stepBox.add(fieldPanel);
+
+ stepBox.add(Box.createVerticalGlue());
+ }
+
+ public String getDescription() {
+ return ResourceStrings.getString("imp_wiz_review_desc");
+ }
+
+ public Component getComponent() {
+ return stepBox;
+ }
+
+ public void setActive(int direction) {
+ fileLabel.setText(importPath);
+ if (conflictImport) {
+ overrideLabel.setText(ResourceStrings.getString("yes"));
+ } else {
+ overrideLabel.setText(ResourceStrings.getString("no"));
+ }
+ Object [] objs = new Object [] { importPath };
+ infoLabel.setText(infoFormat.format(objs));
+ // Set values from file header
+ srcLabel.setText(header.getServer());
+ userLabel.setText(header.getUser());
+ dateLabel.setText(dateFormat.format(header.getDate()));
+
+ setFinishEnabled(true);
+ }
+
+ public boolean setInactive(int direction) {
+ return true;
+ }
+ }
+
+ /*
+ * Display an error message inside a separate thread so that background
+ * threads may interact with the user via SwingUtilities.invoke*
+ */
+ class ErrorDisplay implements Runnable {
+ Object [] objs;
+
+ public ErrorDisplay(Object [] objs) {
+ this.objs = objs;
+ }
+
+ public void run() {
+ JOptionPane.showMessageDialog(ImportWizard.this, objs,
+ ResourceStrings.getString("server_error_title"),
+ JOptionPane.ERROR_MESSAGE);
+ }
+ }
+
+ private String importPath = "";
+ private boolean conflictImport = false;
+ private ImportController importController;
+ private ExportHeader header;
+ /*
+ * The Importer allows the ImportController, which contains all of the
+ * actual import logic, to interact with the user as the import proceeds.
+ * Since we run the import in a background thread to keep the GUI live,
+ * the interactions must use SwingUtilities.invoke* to control the GUI.
+ * Progress updates via ProgressManager don't need special logic here as
+ * ProgressManager already handles the threading work for us.
+ */
+ private Importer importer = new Importer() {
+ ProgressManager progress;
+ String [] errObjs = new String [] {
+ ResourceStrings.getString("imp_error"), ""
+ };
+
+ // Create progress display
+ public void initializeProgress(int length) {
+ progress = new ProgressManager(ImportWizard.this,
+ ResourceStrings.getString("imp_progress_title"), "", 0,
+ length);
+ }
+
+ // Update progress display with current completion level and message
+ public void updateProgress(int done, String message)
+ throws InterruptedException {
+ progress.update(done, message);
+ }
+
+ // Display a single error message
+ public void displayError(String message) {
+ errObjs[1] = message;
+ displayError(errObjs);
+ }
+
+ // Display a group of error messages using a table.
+ public void displayErrors(String msg, String label,
+ ActionError [] errors) {
+ ErrorTable errTable = new ErrorTable(label);
+ errTable.setErrors(errors);
+ JScrollPane scrollPane = new JScrollPane(errTable);
+ Object [] errObjs = new Object [] { msg, scrollPane };
+ displayError(errObjs);
+ }
+
+ // Display an error in the GUI
+ private void displayError(Object [] errObjs) {
+ // If we're on the event dispatch thread already then display now
+ ErrorDisplay ed = new ErrorDisplay(errObjs);
+ if (SwingUtilities.isEventDispatchThread()) {
+ ed.run();
+ } else {
+ try {
+ SwingUtilities.invokeAndWait(ed);
+ } catch (Exception e) {
+ // Errors here are fairly serious; dump the stack
+ e.printStackTrace();
+ }
+ }
+ }
+ };
+
+ public ImportWizard(Frame owner) {
+ super(owner, "");
+ setTitle(ResourceStrings.getString("import_wiz_title"));
+
+ addStep(new LocationStep());
+ addStep(new ReviewStep());
+
+ importController = new ImportController(importer,
+ DataManager.get().getServer());
+ showFirstStep();
+ }
+
+ public void doFinish() {
+ /*
+ * Runnable which the importThread can call to tear down the display
+ * when it's completed.
+ */
+ final Runnable finisher = new Runnable() {
+ public void run() {
+ reallyFinish();
+ }
+ };
+
+ // Create the thread in which to execute the import
+ Thread importThread = new Thread() {
+ public void run() {
+ if (importController.importData(conflictImport)) {
+ // Only exit if import successful
+ SwingUtilities.invokeLater(finisher);
+ }
+ }
+ };
+ // Run the import thread
+ importThread.start();
+ }
+
+ public void doCancel() {
+ // Close file if there is one open
+ importController.closeFile();
+ super.doCancel();
+ }
+
+ protected void reallyFinish() {
+ super.doFinish();
+ }
+
+ public void doHelp() {
+ DhcpmgrApplet.showHelp("import_wizard");
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/MacroNameField.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/MacroNameField.java
new file mode 100644
index 0000000000..476722bdb7
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/MacroNameField.java
@@ -0,0 +1,80 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1999-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.client;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import javax.swing.text.*;
+import com.sun.dhcpmgr.data.Macro;
+
+/**
+ * A text field which enforces the syntax rules for a macro name. These
+ * are all the rules for DhcptabNameField, plus a limit on the length.
+ */
+public class MacroNameField extends DhcptabNameField {
+
+ /**
+ * Constructs a field initialized to the provided text. Defaults to
+ * 20 characters wide.
+ * @param text the text to display initially
+ */
+ public MacroNameField(String text) {
+ this(text, 20);
+ }
+
+ /**
+ * Constructs a field initialized to the provided text with the requested
+ * size.
+ * @param text the text to display initially
+ * @param length the length in characters the field should size itself to
+ */
+ public MacroNameField(String text, int length) {
+ super(text, length);
+ }
+
+ protected Document createDefaultModel() {
+ return new MacroNameDocument();
+ }
+
+ /*
+ * This is the recommended way to validate input, as opposed to trapping
+ * KeyEvents because this will actually catch paste operations as well.
+ */
+ class MacroNameDocument extends DhcptabNameDocument {
+ public void insertString(int offs, String str, AttributeSet a)
+ throws BadLocationException {
+ if (str != null) {
+ if ((getLength() + str.length()) > Macro.MAX_NAME_SIZE) {
+ throw new BadLocationException("", offs);
+ }
+ }
+ super.insertString(offs, str, a);
+ }
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/MacroView.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/MacroView.java
new file mode 100644
index 0000000000..4b587e4dcf
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/MacroView.java
@@ -0,0 +1,673 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.client;
+
+import javax.swing.*;
+import javax.swing.event.*;
+import javax.swing.table.*;
+import javax.swing.tree.*;
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+import java.text.*;
+
+import com.sun.dhcpmgr.ui.*;
+import com.sun.dhcpmgr.server.*;
+import com.sun.dhcpmgr.data.*;
+import com.sun.dhcpmgr.bridge.BridgeException;
+
+// Background thread for doing retrieval while keeping GUI live
+class MacroLoader extends SwingWorker {
+ public Object construct() {
+ try {
+ return DataManager.get().getMacros(true);
+ } catch (final BridgeException e) {
+ // Since we're in a background thread, ask Swing to run ASAP.
+ SwingUtilities.invokeLater(new Runnable() {
+ Object [] args = new Object[] { e.getMessage() };
+ public void run() {
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString("error_loading_macros"));
+ JOptionPane.showMessageDialog(null, form.format(args),
+ ResourceStrings.getString("server_error_title"),
+ JOptionPane.ERROR_MESSAGE);
+ }
+ });
+ }
+ return null;
+ }
+
+ public void finished() {
+ Macro [] macros = (Macro [])get();
+ if (macros == null) {
+ macros = new Macro[0];
+ }
+
+ MacroView.macroTreeModel.getRootNode().setMacros(macros);
+ }
+}
+
+/**
+ * Implements a display of the macro hierarchy contained in the dhcptab
+ */
+public class MacroView implements View {
+
+ // A node in the tree of macros
+ class MacroTreeNode extends DefaultMutableTreeNode {
+ private boolean scannedIncludes = false;
+ private boolean isScanning = false;
+
+ public MacroTreeNode(Object o) {
+ super(o);
+ }
+
+ public String toString() {
+ Macro m = (Macro)getUserObject();
+ return m.getKey();
+ }
+
+ public int getChildCount() {
+ /*
+ * scannedIncludes is used so that we only build the tree once;
+ * isScanning prevents us from recursing infinitely because add()
+ * ends up coming right back here
+ */
+ if (!scannedIncludes && !isScanning) {
+ isScanning = true;
+ Macro m = (Macro)getUserObject();
+ Enumeration e = m.elements();
+ while (e.hasMoreElements()) {
+ OptionValue o = (OptionValue)e.nextElement();
+ if (o instanceof IncludeOptionValue) {
+ Macro m2 =
+ macroTreeModel.getRootNode().getMacro(
+ (String)o.getValue());
+ if (m2 != null) {
+ add(new MacroTreeNode(m2));
+ }
+ }
+ }
+ scannedIncludes = true;
+ isScanning = false;
+ }
+ return super.getChildCount();
+ }
+
+ public Macro getMacro() {
+ return (Macro)getUserObject();
+ }
+ }
+
+ /*
+ * Special class for the root node; this handles retrieving the data from
+ * the server
+ */
+ class MacroTreeRootNode extends MacroTreeNode {
+ private Macro [] macros = null;
+
+ public MacroTreeRootNode() {
+ super(new Macro());
+ }
+
+ public MacroTreeRootNode(Object o) {
+ super(o);
+ }
+
+ protected void setMacros(Macro [] newmacros) {
+ macros = newmacros;
+ if (newmacros != null) {
+ for (int i = 0; i < macros.length; ++i) {
+ add(new MacroTreeNode(macros[i]));
+ }
+ }
+ macroTreeModel.reload();
+ reloadCompleted();
+ }
+
+ public String toString() {
+ return ResourceStrings.getString("macros");
+ }
+
+ public int getChildCount() {
+ if (macros == null) {
+ return 0;
+ }
+ return super.getChildCount();
+ }
+
+ public void load() {
+ // Display starting message and clear tree
+ MainFrame.setStatusText(
+ ResourceStrings.getString("loading_macros"));
+ removeAllChildren();
+ macroTreeModel.reload();
+ // Kick off background loader
+ MacroLoader loader = new MacroLoader();
+ }
+
+ public Macro getMacro() {
+ return null;
+ }
+
+ // Find a particular macro by name
+ public Macro getMacro(String name) {
+ if (macros != null) {
+ for (int i = 0; i < macros.length; ++i) {
+ if (name.equals(macros[i].getKey())) {
+ return macros[i];
+ }
+ }
+ }
+ return null;
+ }
+ }
+
+ // Model for data contained in macro tree
+ class MacroTreeModel extends DefaultTreeModel {
+ public MacroTreeModel(MacroTreeRootNode n) {
+ super(n);
+ }
+
+ public void load() {
+ getRootNode().load();
+ }
+
+ public Macro getMacro(String name) {
+ return getRootNode().getMacro(name);
+ }
+
+ public MacroTreeRootNode getRootNode() {
+ return (MacroTreeRootNode)super.getRoot();
+ }
+ }
+
+ // Model for table displaying contents of a macro
+ class MacroTableModel extends AbstractTableModel {
+ private Macro macro = null;
+
+ public MacroTableModel() {
+ super();
+ }
+
+ public void display(Macro m) {
+ macro = m;
+ fireTableDataChanged();
+ }
+
+ public int getRowCount() {
+ if (macro == null) {
+ return 0;
+ } else {
+ return macro.optionCount();
+ }
+ }
+
+ public int getColumnCount() {
+ return 2;
+ }
+
+ public Object getValueAt(int row, int column) {
+ if (macro == null) {
+ return null;
+ }
+ switch (column) {
+ case 0:
+ return macro.getOptionAt(row).getName();
+ case 1:
+ return macro.getOptionAt(row).getValue();
+ default:
+ return null;
+ }
+ }
+
+ public Class getColumnClass(int column) {
+ switch (column) {
+ case 0:
+ case 1:
+ return String.class;
+ default:
+ super.getColumnClass(column);
+ }
+ return null;
+ }
+
+ public String getColumnName(int column) {
+ switch (column) {
+ case 0:
+ return ResourceStrings.getString("option_column");
+ case 1:
+ return ResourceStrings.getString("value_column");
+ default:
+ super.getColumnName(column);
+ }
+ return null;
+ }
+ }
+
+ // Recipient of update messages sent when the macro editing dialogs exit
+ class DialogListener implements ActionListener {
+ public void actionPerformed(ActionEvent e) {
+ if (!e.getActionCommand().equals(DialogActions.CANCEL)) {
+ reload();
+ }
+ }
+ }
+
+ private JTree macroTree;
+ protected static MacroTreeModel macroTreeModel = null;
+ private AutosizingTable macroTable;
+ private JScrollPane treePane;
+ private JScrollPane macroTablePane;
+ private JSplitPane splitPane;
+ private boolean firstActivation = true;
+ private MacroTableModel macroTableModel;
+ private JCheckBoxMenuItem showGrid;
+ private JMenuItem macroHelp;
+ private Vector[] menuItems;
+ private Frame myFrame;
+ private boolean firstview = true;
+ private Vector selectionListeners = new Vector();
+
+ public MacroView() {
+ // Create tree for macro display
+ macroTreeModel = new MacroTreeModel(new MacroTreeRootNode());
+ macroTree = new JTree(macroTreeModel);
+ // Listen for selection events, load selected macro into table
+ macroTree.addTreeSelectionListener(new TreeSelectionListener() {
+ public void valueChanged(TreeSelectionEvent e) {
+ TreePath selPath = macroTree.getSelectionPath();
+ if (selPath != null) {
+ TreeNode node = (TreeNode)selPath.getLastPathComponent();
+ if (node instanceof MacroTreeNode) {
+ macroTableModel.display(
+ ((MacroTreeNode)node).getMacro());
+ } else {
+ macroTableModel.display(null);
+ }
+ } else {
+ macroTableModel.display(null);
+ }
+ // Notify listeners that our selection state may have changed
+ notifySelectionListeners();
+ }
+ });
+ // Single selection for now
+ macroTree.getSelectionModel().setSelectionMode(
+ TreeSelectionModel.SINGLE_TREE_SELECTION);
+ // Make a scroll pane for it
+ treePane = new JScrollPane();
+ treePane.getViewport().add(macroTree);
+ // Make double-clicks the same as Edit->Properties
+ macroTree.addMouseListener(new MouseAdapter() {
+ public void mouseClicked(MouseEvent e) {
+ if (e.getClickCount() == 2) {
+ // Don't do anything if it's the root node
+ TreePath selPath =
+ macroTree.getClosestPathForLocation(e.getX(), e.getY());
+ macroTree.addSelectionPath(selPath);
+ if (selPath.getPathCount() != 1) {
+ handleProperties();
+ }
+ }
+ }
+ });
+
+ // Create table to display in data area
+ macroTableModel = new MacroTableModel();
+ macroTable = new AutosizingTable(macroTableModel);
+
+ // Can't mess with the column order, or select any data
+ macroTable.getTableHeader().setReorderingAllowed(false);
+ macroTable.setRowSelectionAllowed(false);
+ macroTable.setColumnSelectionAllowed(false);
+
+ // Now wrap it with scrollbars
+ macroTablePane = new JScrollPane(macroTable);
+
+ // Create split pane containing the tree & table, side-by-side
+ splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, treePane,
+ macroTablePane);
+
+ // Create menu items
+ Mnemonic mnShowGrid =
+ new Mnemonic(ResourceStrings.getString("show_grid"));
+ showGrid = new JCheckBoxMenuItem(mnShowGrid.getString(),
+ true);
+ showGrid.setMnemonic(mnShowGrid.getMnemonic());
+ showGrid.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ macroTable.setShowGrid(showGrid.getState());
+ }
+ });
+
+ Mnemonic mnOnMacros =
+ new Mnemonic(ResourceStrings.getString("on_macros_item"));
+ macroHelp = new JMenuItem(mnOnMacros.getString());
+ macroHelp.setMnemonic(mnOnMacros.getMnemonic());
+ macroHelp.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ DhcpmgrApplet.showHelp("macros_reference");
+ }
+ });
+
+
+ /*
+ * Construct lists of menu items custom to this view.
+ */
+ menuItems = new Vector[MainFrame.MENU_COUNT];
+ for (int i = 0; i < menuItems.length; ++i) {
+ menuItems[i] = new Vector();
+ }
+ menuItems[MainFrame.VIEW_MENU].addElement(showGrid);
+ menuItems[MainFrame.HELP_MENU].addElement(macroHelp);
+ }
+
+ public String getName() {
+ return ResourceStrings.getString("macro_view_name");
+ }
+
+ // Return menus that we wish to add to MainFrame, in our case none
+ public Enumeration menus() {
+ return null;
+ }
+
+ // Return menu items for each menu as requested by MainFrame
+ public Enumeration menuItems(int menu) {
+ return menuItems[menu].elements();
+ }
+
+ public Component getDisplay() {
+ return splitPane;
+ }
+
+ private void reload() {
+ macroTreeModel.load();
+ }
+
+ // Callback from model when loading completed to update display
+ private void reloadCompleted() {
+ // Doesn't display correctly without this
+ splitPane.resetToPreferredSizes();
+ String s = MessageFormat.format(
+ ResourceStrings.getString("macro_status_message"),
+ macroTreeModel.getRootNode().getChildCount());
+ MainFrame.setStatusText(s);
+ macroTree.clearSelection();
+ macroTable.clearSelection();
+ /*
+ * Check for syntax errors.
+ */
+ MacroTreeRootNode rootNode = macroTreeModel.getRootNode();
+ Vector errs = new Vector();
+ for (int i = 0; i < rootNode.macros.length; ++i) {
+ try {
+ rootNode.macros[i].validate();
+ } catch (ValidationException e) {
+ errs.addElement(rootNode.macros[i].getKey());
+ }
+ }
+ if (errs.size() != 0) {
+ // Found some errors; warn user
+ Object [] objs = new Object[2];
+ objs[0] = ResourceStrings.getString("macro_validation_warning");
+ JList macroList = new JList(errs);
+ JScrollPane scrollPane = new JScrollPane(macroList);
+ macroList.setVisibleRowCount(4);
+ objs[1] = scrollPane;
+ JOptionPane.showMessageDialog(macroTable, objs,
+ ResourceStrings.getString("warning"),
+ JOptionPane.WARNING_MESSAGE);
+ }
+ macroTree.setSelectionRow(1);
+ }
+
+ public void setActive(boolean state) {
+ if (state) {
+ // We only do an automatic load the first time we're displayed
+ if (firstview) {
+ myFrame = (Frame)SwingUtilities.getAncestorOfClass(
+ MainFrame.class, macroTree);
+ reload();
+ firstview = false;
+ }
+ }
+ }
+
+ /*
+ * Handle a find operation.
+ * Algorithm used here searches nodes in the order they appear in the
+ * displayed tree. This requires traversing the entire tree starting at
+ * an arbitrary point with some special twists.
+ */
+ public void find(String s) {
+ // Clear status if we had an old message lying there
+ MainFrame.setStatusText("");
+ MacroTreeNode startNode =
+ (MacroTreeNode)macroTree.getLastSelectedPathComponent();
+ if (startNode == null) {
+ // Nothing selected so start at root
+ startNode = macroTreeModel.getRootNode();
+ }
+ // Start by searching children of selected node
+ MacroTreeNode result = searchUnderNode(startNode, s);
+ if (result != null) {
+ selectNode(result); // Found one, select it and return
+ return;
+ }
+ // Get all ancestor nodes of selected
+ TreeNode [] path = macroTreeModel.getPathToRoot(startNode);
+ MacroTreeNode pathNode = null;
+
+ // Walk up towards root of tree, at each level search all children
+ for (int i = path.length - 1; i >= 0; --i) {
+ result = searchNodeLevel((MacroTreeNode)path[i], startNode, s);
+ if (result != null) {
+ selectNode(result); // Found one, select it and return
+ return;
+ }
+ // Move up a level
+ startNode = (MacroTreeNode)path[i];
+ /*
+ * If it's not the root node and it matches, remember it. We don't
+ * return immediately because this is actually about last in the
+ * display order.
+ */
+ if (startNode.getMacro() != null) {
+ if (startNode.getMacro().getKey().indexOf(s) != -1) {
+ pathNode = startNode;
+ }
+ }
+ }
+ // We found one on the path to the root, select and return
+ if (pathNode != null) {
+ selectNode(pathNode);
+ return;
+ }
+ // Nothing found; show an error
+ MessageFormat form = null;
+ Object [] args = new Object[1];
+ form = new MessageFormat(ResourceStrings.getString("find_macro_error"));
+ args[0] = s;
+ MainFrame.setStatusText(form.format(args));
+ }
+
+ // Search a particular level of a tree in the order a user would expect
+ private MacroTreeNode searchNodeLevel(MacroTreeNode n,
+ MacroTreeNode startNode, String s) {
+ // Skip all children prior to the startNode in display order
+ Enumeration e = n.children();
+ while (e.hasMoreElements()) {
+ MacroTreeNode cn = (MacroTreeNode)e.nextElement();
+ if (cn == startNode) {
+ break;
+ }
+ }
+ // Now search those after the startNode in the display order
+ while (e.hasMoreElements()) {
+ MacroTreeNode cn = (MacroTreeNode)e.nextElement();
+ MacroTreeNode result = searchNode(cn, s);
+ if (result != null) {
+ return result;
+ }
+ }
+ // Got to the end of this level and didn't find it, so wrap to beginning
+ e = n.children();
+ while (e.hasMoreElements()) {
+ MacroTreeNode cn = (MacroTreeNode)e.nextElement();
+ if (cn == startNode) {
+ break;
+ }
+ MacroTreeNode result = searchNode(cn, s);
+ if (result != null) {
+ return result;
+ }
+ }
+ return null;
+ }
+
+ // Search a node and all its children for a particular string
+ private MacroTreeNode searchNode(MacroTreeNode n, String s) {
+ if (n.getMacro().getKey().indexOf(s) != -1
+ || n.getMacro().getValue().indexOf(s) != -1) {
+ return n;
+ }
+ return searchUnderNode(n, s);
+ }
+
+ // Search all children, recursively, of a node for a particular string
+ private MacroTreeNode searchUnderNode(MacroTreeNode n, String s) {
+ Enumeration e = n.children();
+ while (e.hasMoreElements()) {
+ MacroTreeNode cn = (MacroTreeNode)e.nextElement();
+ if (cn.getMacro().getKey().indexOf(s) != -1
+ || cn.getMacro().getValue().indexOf(s) != -1) {
+ return cn;
+ }
+ MacroTreeNode result = searchUnderNode(cn, s);
+ if (result != null) {
+ return result;
+ }
+ }
+ return null;
+ }
+
+ // Select a node and make sure it's visible
+ private void selectNode(MacroTreeNode n) {
+ macroTree.clearSelection();
+ TreePath selPath = new TreePath(macroTreeModel.getPathToRoot(n));
+ macroTree.addSelectionPath(selPath);
+ macroTree.scrollPathToVisible(selPath);
+ }
+
+ // Return the macro currently selected
+ private Macro getSelectedMacro() {
+ TreePath selPath = macroTree.getSelectionPath();
+ if (selPath != null) {
+ TreeNode node = (TreeNode)selPath.getLastPathComponent();
+ if (node instanceof MacroTreeNode) {
+ return ((MacroTreeNode)node).getMacro();
+ }
+ }
+ return null;
+ }
+
+ public void handleCreate() {
+ CreateMacroDialog d = new CreateMacroDialog(myFrame,
+ CreateMacroDialog.CREATE);
+ d.addActionListener(new DialogListener());
+ d.pack();
+ d.setVisible(true);
+ }
+
+ public void handleDelete() {
+ Macro m = getSelectedMacro();
+ if (m == null) {
+ return;
+ }
+ DeleteMacroDialog d = new DeleteMacroDialog(myFrame, m);
+ d.addActionListener(new DialogListener());
+ d.pack();
+ d.setVisible(true);
+ }
+
+ public void handleDuplicate() {
+ Macro m = getSelectedMacro();
+ if (m == null) {
+ return;
+ }
+ CreateMacroDialog d = new CreateMacroDialog(myFrame,
+ CreateMacroDialog.DUPLICATE);
+ d.addActionListener(new DialogListener());
+ d.setMacro((Macro)m.clone());
+ d.pack();
+ d.setVisible(true);
+ }
+
+ public void handleProperties() {
+ Macro m = getSelectedMacro();
+ if (m == null) {
+ return;
+ }
+ CreateMacroDialog d = new CreateMacroDialog(myFrame,
+ CreateMacroDialog.EDIT);
+ d.addActionListener(new DialogListener());
+ d.setMacro((Macro)m.clone());
+ d.pack();
+ d.setVisible(true);
+ }
+
+ public void handleUpdate() {
+ reload();
+ }
+
+ public void addSelectionListener(SelectionListener listener) {
+ selectionListeners.addElement(listener);
+ }
+
+ public void removeSelectionListener(SelectionListener listener) {
+ selectionListeners.removeElement(listener);
+ }
+
+ private void notifySelectionListeners() {
+ Enumeration en = selectionListeners.elements();
+ while (en.hasMoreElements()) {
+ SelectionListener l = (SelectionListener)en.nextElement();
+ l.valueChanged();
+ }
+ }
+
+ public boolean isSelectionEmpty() {
+ TreePath path = macroTree.getSelectionPath();
+ // If empty or the root of the tree is selected, then we call it empty
+ return ((path == null) || (path.getPathCount() == 1));
+ }
+
+ public boolean isSelectionMultiple() {
+ return false; // We don't allow multiple selection
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/Makefile b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/Makefile
new file mode 100644
index 0000000000..94c48820f9
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/Makefile
@@ -0,0 +1,125 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 1999-2002 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/Makefile
+#
+
+# Place high-level classes first in order to minimize build time.
+CLASSFILES = DhcpmgrApplet.class \
+ AddressView.class \
+ OptionView.class \
+ MacroView.class \
+ RelayView.class \
+ CreateMacroDialog.class \
+ DeleteMacroDialog.class \
+ CreateOptionDialog.class \
+ DeleteOptionDialog.class \
+ CreateAddressDialog.class \
+ DeleteAddressDialog.class \
+ AddressWizard.class \
+ ModifyAddressesDialog.class \
+ ReleaseAddressDialog.class \
+ DisableServiceDialog.class \
+ ConfigureRelayDialog.class \
+ ConfigureChoiceDialog.class \
+ ConfigWizard.class \
+ ServerOptionsDialog.class \
+ DeleteNetworksDialog.class \
+ UnconfigureDialog.class \
+ ExportWizard.class \
+ ImportWizard.class \
+ ConvertWizard.class \
+ DSWizard.class \
+ SUNWModule.class \
+ DSModule.class \
+ DSModuleListener.class \
+ DSModuleEvent.class \
+ MacroNameField.class \
+ OptionNameField.class \
+ DhcptabNameField.class \
+ SelectOptionDialog.class \
+ DataManager.class \
+ MultipleOperationDialog.class \
+ DhcpmgrDialog.class \
+ ViewMacroDialog.class \
+ ErrorTable.class \
+ ResourceStrings.class
+
+SUBDIRS = SUNWbinfiles SUNWfiles SUNWnisplus
+
+all := TARGET= all
+install := TARGET= install
+clean := TARGET= clean
+clobber := TARGET= clobber
+lint := TARGET= lint
+_msg := TARGET= _msg
+
+include $(SRC)/Makefile.master
+
+CLASSPATH= $(SRC)/cmd/cmd-inet/usr.sadm/dhcpmgr
+
+JAVAFILES = $(CLASSFILES:.class=.java)
+
+MSGDIR= $(ROOT)/usr/share/lib/locale/com/sun/dhcpmgr/client
+MSGDIRS = $(ROOT)/usr/share/lib/locale \
+ $(ROOT)/usr/share/lib/locale/com \
+ $(ROOT)/usr/share/lib/locale/com/sun \
+ $(ROOT)/usr/share/lib/locale/com/sun/dhcpmgr \
+ $(MSGDIR)
+
+MSGFILES= ResourceBundle.properties \
+ OptionDescriptions.properties
+MSGS= $(MSGFILES:%=$(MSGDIR)/%)
+
+CLEANFILES= *.class
+CLOBBERFILES=
+
+.KEEP_STATE:
+
+all: $(CLASSFILES) $(SUBDIRS)
+
+install: all $(SUBDIRS)
+
+_msg: $(MSGDIRS) $(MSGS) $(SUBDIRS)
+
+$(MSGDIR)/%: %
+ $(INS.file)
+
+$(MSGDIRS):
+ $(INS.dir)
+
+lint:
+
+clean: $(SUBDIRS) FRC
+ $(RM) $(CLEANFILES)
+
+clobber: clean
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ModifyAddressesDialog.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ModifyAddressesDialog.java
new file mode 100644
index 0000000000..26afcc538a
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ModifyAddressesDialog.java
@@ -0,0 +1,434 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 1998-2002 by Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.client;
+
+import javax.swing.*;
+import javax.swing.table.*;
+import java.util.*;
+import java.awt.*;
+import java.awt.event.*;
+import java.net.*;
+
+import com.sun.dhcpmgr.data.*;
+import com.sun.dhcpmgr.ui.*;
+import com.sun.dhcpmgr.server.DhcpNetMgr;
+
+/**
+ * This dialog allows the user to modify multiple addresses
+ */
+public class ModifyAddressesDialog extends MultipleOperationDialog {
+ private DhcpClientRecord [] recs;
+ private String table;
+ private JLabel numberLabel;
+ private JComboBox server;
+ private JTextField comment;
+ private JComboBox macro;
+ private JRadioButton bootpCurrent, bootpAll, bootpNone;
+ private JRadioButton unusableCurrent, unusableAll, unusableNone;
+ private JRadioButton leaseCurrent, leaseDynamic, leasePermanent;
+ private static final String keepString =
+ ResourceStrings.getString("modify_multiple_keep");
+
+ class ServerListModel extends AbstractListModel implements ComboBoxModel {
+ private String [] servers;
+ private Object currentValue;
+
+ public ServerListModel() {
+ servers = new String[2];
+ servers[0] = keepString;
+ servers[1] = DataManager.get().getShortServerName();
+ }
+
+ public int getSize() {
+ return servers.length;
+ }
+
+ public Object getElementAt(int index) {
+ return servers[index];
+ }
+
+ public void setSelectedItem(Object anItem) {
+ currentValue = anItem;
+ fireContentsChanged(this, -1, -1);
+ }
+
+ public Object getSelectedItem() {
+ return currentValue;
+ }
+ }
+
+ class MacroListModel extends AbstractListModel implements ComboBoxModel {
+ private String [] macros;
+ private Object currentValue;
+
+ public MacroListModel() {
+ Macro [] macs = new Macro[0];
+ try {
+ macs = DataManager.get().getMacros(false);
+ macros = new String[macs.length + 1];
+ } catch (Throwable e) {
+ macros = new String[1];
+ }
+ macros[0] = keepString;
+ for (int i = 0; i < macs.length; ++i) {
+ macros[i+1] = macs[i].getKey();
+ }
+ }
+
+ public int getSize() {
+ return macros.length;
+ }
+
+ public Object getElementAt(int index) {
+ return macros[index];
+ }
+
+ public void setSelectedItem(Object anItem) {
+ currentValue = anItem;
+ fireContentsChanged(this, -1, -1);
+ }
+
+ public Object getSelectedItem() {
+ return currentValue;
+ }
+ }
+
+ public ModifyAddressesDialog(Frame f, DhcpClientRecord [] clients,
+ String table) {
+ // Create dialog with reset button
+ super(f, true);
+
+ this.table = table;
+ recs = clients;
+ numberLabel.setText(String.valueOf(recs.length));
+ }
+
+ public String getTitle() {
+ return ResourceStrings.getString("modify_multiple_addresses");
+ }
+
+ protected JPanel getMainPanel() {
+ JPanel mainPanel = new JPanel();
+ mainPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
+ GridBagLayout bag = new GridBagLayout();
+ mainPanel.setLayout(bag);
+ GridBagConstraints con = new GridBagConstraints();
+ con.gridx = con.gridy = 0;
+ con.weightx = con.weighty = 1.0;
+ con.insets = new Insets(5, 5, 5, 5);
+ con.gridwidth = con.gridheight = 1;
+
+ // Number of addresses
+ Mnemonic mnAddrSel =
+ new Mnemonic(ResourceStrings.getString("modify_multiple_number"));
+ JLabel label = new JLabel(mnAddrSel.getString());
+ label.setLabelFor(mainPanel);
+ label.setDisplayedMnemonic(mnAddrSel.getMnemonic());
+ label.setToolTipText(mnAddrSel.getString());
+
+ con.anchor = GridBagConstraints.EAST;
+ bag.setConstraints(label, con);
+ mainPanel.add(label);
+
+ numberLabel = new JLabel("100");
+ numberLabel.setForeground(Color.black);
+ ++con.gridx;
+ con.anchor = GridBagConstraints.WEST;
+ bag.setConstraints(numberLabel, con);
+ mainPanel.add(numberLabel);
+
+ // Server
+ Mnemonic mnManServ =
+ new Mnemonic(ResourceStrings.getString("modify_multiple_server"));
+ label = new JLabel(mnManServ.getString());
+ label.setDisplayedMnemonic(mnManServ.getMnemonic());
+ label.setToolTipText(mnManServ.getString());
+ con.anchor = GridBagConstraints.EAST;
+ con.gridx = 0;
+ con.gridy += 2;
+ bag.setConstraints(label, con);
+ mainPanel.add(label);
+
+ server = new JComboBox(new ServerListModel());
+ label.setLabelFor(server);
+ server.setEditable(true);
+ con.anchor = GridBagConstraints.WEST;
+ ++con.gridx;
+ bag.setConstraints(server, con);
+ mainPanel.add(server);
+
+ // Comment
+ Mnemonic mnComm =
+ new Mnemonic(ResourceStrings.getString("modify_multiple_comment"));
+ label = new JLabel(mnComm.getString());
+ label.setDisplayedMnemonic(mnComm.getMnemonic());
+ label.setToolTipText(mnComm.getString());
+ con.anchor = GridBagConstraints.EAST;
+ con.gridx = 0;
+ ++con.gridy;
+ bag.setConstraints(label, con);
+ mainPanel.add(label);
+
+ comment = new JTextField(20);
+ label.setLabelFor(comment);
+ con.anchor = GridBagConstraints.WEST;
+ ++con.gridx;
+ bag.setConstraints(comment, con);
+ mainPanel.add(comment);
+
+ // Macro
+ Mnemonic mnMac =
+ new Mnemonic(ResourceStrings.getString("modify_multiple_macro"));
+ label = new JLabel(mnMac.getString());
+ label.setDisplayedMnemonic(mnMac.getMnemonic());
+ label.setToolTipText(mnMac.getString());
+ con.anchor = GridBagConstraints.EAST;
+ con.gridx = 0;
+ ++con.gridy;
+ bag.setConstraints(label, con);
+ mainPanel.add(label);
+
+ macro = new JComboBox(new MacroListModel());
+ label.setLabelFor(macro);
+ con.anchor = GridBagConstraints.WEST;
+ ++con.gridx;
+ bag.setConstraints(macro, con);
+ mainPanel.add(macro);
+
+ // BootP
+ Mnemonic mnBootP =
+ new Mnemonic(ResourceStrings.getString("modify_multiple_bootp"));
+ label = new JLabel(mnBootP.getString());
+ label.setDisplayedMnemonic(mnBootP.getMnemonic());
+ label.setToolTipText(mnBootP.getString());
+ con.anchor = GridBagConstraints.EAST;
+ con.gridx = 0;
+ con.gridy += 2;
+ bag.setConstraints(label, con);
+ mainPanel.add(label);
+
+ ButtonGroup bootpGroup = new ButtonGroup();
+ bootpCurrent = new JRadioButton(keepString);
+ label.setLabelFor(bootpCurrent);
+ bootpCurrent.setToolTipText(keepString);
+ bootpGroup.add(bootpCurrent);
+ con.anchor = GridBagConstraints.WEST;
+ ++con.gridx;
+ bag.setConstraints(bootpCurrent, con);
+ mainPanel.add(bootpCurrent);
+
+ bootpAll = new JRadioButton(
+ ResourceStrings.getString("modify_multiple_bootp_all"));
+ bootpAll.setToolTipText(
+ ResourceStrings.getString("modify_multiple_bootp_all"));
+ bootpGroup.add(bootpAll);
+ ++con.gridy;
+ bag.setConstraints(bootpAll, con);
+ mainPanel.add(bootpAll);
+
+ bootpNone = new JRadioButton(
+ ResourceStrings.getString("modify_multiple_bootp_none"));
+ bootpNone.setToolTipText(
+ ResourceStrings.getString("modify_multiple_bootp_none"));
+ bootpGroup.add(bootpNone);
+ ++con.gridy;
+ bag.setConstraints(bootpNone, con);
+ mainPanel.add(bootpNone);
+
+ // Unusable
+ Mnemonic mnUnusable =
+ new Mnemonic(ResourceStrings.getString("modify_multiple_unusable"));
+ label = new JLabel(mnUnusable.getString());
+ label.setDisplayedMnemonic(mnUnusable.getMnemonic());
+ label.setToolTipText(mnUnusable.getString());
+ con.anchor = GridBagConstraints.EAST;
+ con.gridx = 0;
+ con.gridy += 2;
+ bag.setConstraints(label, con);
+ mainPanel.add(label);
+
+ ButtonGroup unusableGroup = new ButtonGroup();
+ unusableCurrent = new JRadioButton(keepString);
+ label.setLabelFor(unusableCurrent);
+ unusableCurrent.setToolTipText(keepString);
+ unusableGroup.add(unusableCurrent);
+ con.anchor = GridBagConstraints.WEST;
+ ++con.gridx;
+ bag.setConstraints(unusableCurrent, con);
+ mainPanel.add(unusableCurrent);
+
+ unusableAll = new JRadioButton(
+ ResourceStrings.getString("modify_multiple_unusable_all"));
+ unusableAll.setToolTipText(
+ ResourceStrings.getString("modify_multiple_unusable_all"));
+ unusableGroup.add(unusableAll);
+ ++con.gridy;
+ bag.setConstraints(unusableAll, con);
+ mainPanel.add(unusableAll);
+
+ unusableNone = new JRadioButton(
+ ResourceStrings.getString("modify_multiple_unusable_none"));
+ unusableNone.setToolTipText(
+ ResourceStrings.getString("modify_multiple_unusable_none"));
+ unusableGroup.add(unusableNone);
+ ++con.gridy;
+ bag.setConstraints(unusableNone, con);
+ mainPanel.add(unusableNone);
+
+ // Lease
+ Mnemonic mnLease =
+ new Mnemonic(ResourceStrings.getString("modify_multiple_lease"));
+ label = new JLabel(mnLease.getString());
+ label.setDisplayedMnemonic(mnLease.getMnemonic());
+ label.setToolTipText(mnLease.getString());
+ con.anchor = GridBagConstraints.EAST;
+ con.gridx = 0;
+ con.gridy += 2;
+ bag.setConstraints(label, con);
+ mainPanel.add(label);
+
+ ButtonGroup leaseGroup = new ButtonGroup();
+ leaseCurrent = new JRadioButton(keepString);
+ label.setLabelFor(leaseCurrent);
+ leaseCurrent.setToolTipText(keepString);
+ leaseGroup.add(leaseCurrent);
+ con.anchor = GridBagConstraints.WEST;
+ ++con.gridx;
+ bag.setConstraints(leaseCurrent, con);
+ mainPanel.add(leaseCurrent);
+
+ leaseDynamic = new JRadioButton(
+ ResourceStrings.getString("modify_multiple_dynamic"));
+ leaseDynamic.setToolTipText(
+ ResourceStrings.getString("modify_multiple_dynamic"));
+ leaseGroup.add(leaseDynamic);
+ ++con.gridy;
+ bag.setConstraints(leaseDynamic, con);
+ mainPanel.add(leaseDynamic);
+
+ leasePermanent = new JRadioButton(
+ ResourceStrings.getString("modify_multiple_permanent"));
+ leasePermanent.setToolTipText(
+ ResourceStrings.getString("modify_multiple_permanent"));
+ leaseGroup.add(leasePermanent);
+ ++con.gridy;
+ bag.setConstraints(leasePermanent, con);
+ mainPanel.add(leasePermanent);
+
+ buttonPanel.setOkEnabled(true);
+ doReset();
+
+ return mainPanel;
+ }
+
+ protected void doReset() {
+ server.setSelectedIndex(0);
+ comment.setText(keepString);
+ macro.setSelectedIndex(0);
+ bootpCurrent.setSelected(true);
+ unusableCurrent.setSelected(true);
+ leaseCurrent.setSelected(true);
+ }
+
+ protected String getProgressMessage() {
+ return ResourceStrings.getString("modify_multiple_progress");
+ }
+
+ protected int getProgressLength() {
+ return recs.length;
+ }
+
+ protected String getErrorHeading() {
+ return ResourceStrings.getString("address_column");
+ }
+
+ protected Class getErrorClass() {
+ return IPAddress.class;
+ }
+
+ protected Thread getOperationThread() {
+ return new Thread() {
+ public void run() {
+ DhcpNetMgr netMgr = DataManager.get().getDhcpNetMgr();
+ for (int i = 0; i < recs.length; ++i) {
+ DhcpClientRecord client = (DhcpClientRecord)recs[i].clone();
+ try {
+ String s = (String)server.getSelectedItem();
+ if (!s.equals(keepString)) {
+ // Change server if necessary
+ client.setServerIP(new IPAddress(s));
+ }
+ s = comment.getText();
+ if (!s.equals(keepString)) {
+ client.setComment(s);
+ }
+ if (macro.getSelectedIndex() != 0) {
+ client.setMacro((String)macro.getSelectedItem());
+ }
+ if (bootpAll.isSelected()) {
+ client.setBootp(true);
+ } else if (bootpNone.isSelected()) {
+ client.setBootp(false);
+ }
+ if (unusableAll.isSelected()) {
+ client.setUnusable(true);
+ } else if (unusableNone.isSelected()) {
+ client.setUnusable(false);
+ }
+ if (leaseDynamic.isSelected()) {
+ client.setPermanent(false);
+ } else if (leasePermanent.isSelected()) {
+ client.setPermanent(true);
+ }
+ netMgr.modifyClient(recs[i], client, table);
+ updateProgress(i+1, client.getClientIPAddress());
+ } catch (InterruptedException e) {
+ closeDialog();
+ return;
+ } catch (Throwable e) {
+ addError(recs[i].getClientIP(), e.getMessage());
+ }
+ }
+ // Errors occurred, display them
+ if (errorsOccurred()) {
+ displayErrors(
+ ResourceStrings.getString("modify_multiple_error"));
+ }
+ closeDialog();
+ }
+ };
+ }
+
+ protected String getHelpKey() {
+ return "modify_multiple_addresses";
+ }
+
+ protected void fireActionPerformed() {
+ fireActionPerformed(this, DialogActions.OK);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/MultipleOperationDialog.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/MultipleOperationDialog.java
new file mode 100644
index 0000000000..44471b3f45
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/MultipleOperationDialog.java
@@ -0,0 +1,167 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.client;
+
+import javax.swing.SwingUtilities;
+import javax.swing.JScrollPane;
+import javax.swing.JOptionPane;
+import java.awt.Frame;
+import java.awt.Dimension;
+
+import com.sun.dhcpmgr.ui.ProgressManager;
+
+/**
+ * This abstract class provides a common implementation of the functions
+ * shared by the dialogs which perform multiple operations such as adding
+ * and deleting addresses or networks. It provides a framework within
+ * which subclasses may execute such operations in a background thread
+ * and use progress meters and capture and display multiple error messages.
+ * Nearly all of the methods here are defined as protected because they're
+ * really implementation details for the dialogs and need not be visible
+ * outside that context.
+ */
+public abstract class MultipleOperationDialog extends DhcpmgrDialog {
+ // Progress Manager provides convenient handling of progress dialogs
+ protected ProgressManager progManager;
+ // ErrorTable provides a convenient storehouse for sets of errors
+ protected ErrorTable messageTable;
+
+ public MultipleOperationDialog(Frame f, boolean allowsReset) {
+ super(f, allowsReset);
+ }
+
+ /**
+ * Initiate the action in a background thread when the user presses OK;
+ * subclasses should not need to override this, instead they provide
+ * an implementation of getOperationThread() where the actions needed
+ * are executed.
+ */
+ protected void doOk() {
+ progManager = new ProgressManager(this, getProgressMessage(), "", 0,
+ getProgressLength());
+ messageTable = new ErrorTable(getErrorHeading(), getErrorClass());
+ getOperationThread().start();
+ }
+
+ /**
+ * Update progress meter; subclasses should call as each operation is
+ * completed. InterruptedException is thrown by the ProgressManager
+ * if user pressed Cancel in the progress dialog that was popped up.
+ * Typically the subclass should abort its operation thread when this
+ * occurs.
+ */
+ protected void updateProgress(int progress, String msg)
+ throws InterruptedException {
+ progManager.update(progress, msg);
+ }
+
+ /**
+ * Get thread which will perform the operation
+ */
+ protected abstract Thread getOperationThread();
+
+ /**
+ * Get message to display in progress dialog
+ */
+ protected abstract String getProgressMessage();
+
+ /**
+ * Get length of operation
+ */
+ protected abstract int getProgressLength();
+
+ /**
+ * Get rid of dialog when we're done
+ */
+ public void closeDialog() {
+ Runnable finisher = new Runnable() {
+ public void run() {
+ fireActionPerformed();
+ setVisible(false);
+ dispose();
+ }
+ };
+ SwingUtilities.invokeLater(finisher);
+ }
+
+ /**
+ * Display the errors
+ */
+ protected void displayErrors(final String msg) {
+ /*
+ * Use a Runnable and invokeAndWait as we're usually called from
+ * the operation thread, not the AWT thread.
+ */
+ Runnable errorDisplay = new Runnable() {
+ public void run() {
+ JScrollPane scrollPane = new JScrollPane(messageTable);
+ Dimension d = messageTable.getPreferredScrollableViewportSize();
+ d.height = 80;
+ messageTable.setPreferredScrollableViewportSize(d);
+ Object [] objs = new Object[] { msg, scrollPane };
+ JOptionPane.showMessageDialog(MultipleOperationDialog.this,
+ objs, ResourceStrings.getString("server_error_title"),
+ JOptionPane.ERROR_MESSAGE);
+ }
+ };
+ try {
+ SwingUtilities.invokeAndWait(errorDisplay);
+ } catch (Throwable e) {
+ // If this failed we're basically in a bailout situation
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Return the heading for the error table
+ */
+ protected abstract String getErrorHeading();
+
+ /**
+ * Return the class for the error data; default to String, subclass can
+ * override as needed.
+ */
+ protected Class getErrorClass() {
+ return String.class;
+ }
+
+ /**
+ * Add an error to the error table; obj must be of the class returned by
+ * getErrorClass
+ */
+ protected void addError(Object obj, String msg) {
+ messageTable.addError(obj, msg);
+ }
+
+ /**
+ * Test for errors occurred
+ */
+ protected boolean errorsOccurred() {
+ return !messageTable.isEmpty();
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/OptionDescriptions.properties b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/OptionDescriptions.properties
new file mode 100644
index 0000000000..398905e8bd
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/OptionDescriptions.properties
@@ -0,0 +1,109 @@
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Descriptions for DHCP standard options, taken from RFC 2132 & 2242
+# Note that the keys here must correspond exactly to those used in the
+# /etc/dhcp/inittab file which defines the option tags we use for each
+# option code. Note also that not all of the tags in /etc/dhcp/inittab
+# will have descriptions here; only those which are defined to be consumed
+# by the management tools need have descriptions in this file.
+
+Subnet=Client's subnet mask
+UTCoffst=Client's time offset from UTC in seconds
+Router=Routers on the client's subnet
+Timeserv=RFC 868 time servers
+IEN116ns=IEN 166 name servers
+DNSserv=DNS servers
+Logserv=MIT-LCS UDP log servers
+Cookie=RFC 865 cookie servers
+Lprserv=RFC 1179 line printer servers
+Impress=Imagen Impress servers
+Resource=RFC 887 Resource Location servers
+Hostname=Host name of client
+Bootsize=Boot file size in 512 byte blocks
+Dumpfile=Pathname of client's crash dump file
+DNSdmain=DNS domain name
+Swapserv=Swap server address
+Rootpath=Pathname of client's root disk
+ExtendP=BOOTP extensions file
+IpFwdF=IP forwarding flag
+NLrouteF=Non-local source routing flag
+PFilter=Policy filter for non-local source routing
+MaxIpSiz=Maximum IP datagram reassembly size
+IpTTL=Time-to-live for IP datagrams
+PathTO=Path MTU aging timeout in seconds
+PathTbl=Path MTU plateau table
+MTU=Interface MTU
+SameMtuF=All subnets are local flag
+Broadcst=Broadcast address
+MaskDscF=Perform mask discovery flag
+MaskSupF=Mask supplier flag
+RDiscvyF=Router discovery flag
+RSolictS=Router solicitation address
+StaticRt=Static routes
+TrailerF=Trailer encapsulation flag
+ArpTimeO=ARP cache timeout
+EthEncap=Ethernet encapsulation flag
+TcpTTL=TCP default TTL
+TcpKaInt=TCP keepalive interval
+TcpKaGbF=TCP keepalive garbage flag
+NISdmain=Network Information Service domain name
+NISservs=Network Information Service servers
+NTPservs=Network Time Protocol servers
+NetBNms=NetBIOS name servers
+NetBDsts=NetBIOS datagram distribution servers
+NetBNdT=NetBIOS node type
+NetBScop=NetBIOS scope
+XFontSrv=X Window System Font servers
+XDispMgr=X Window System display managers
+LeaseTim=Lease time in seconds
+T1Time=Time until RENEWING state
+T2Time=Time until REBINDING state
+NW_dmain=NetWare/IP domain name
+NWIPOpts=NetWare/IP information
+NIS+dom=Network Information Service+ domain
+NIS+serv=Network Information Service+ servers
+TFTPsrvN=TFTP server name
+OptBootF=Bootfile name
+MblIPAgt=Mobile IP home agent
+SMTPserv=Simple Mail Transport Protocol servers
+POP3serv=Post Office Protocol 3 servers
+NNTPserv=Network News Transport Protocol servers
+WWWservs=World Wide Web servers
+Fingersv=Finger servers
+IRCservs=Internet Relay Chat servers
+STservs=StreetTalk servers
+STDAservs=StreetTalk Directory Assistance servers
+SLP_DA=Service Location Protocol directory agent
+SLP_SS=Service Location Protocol agent scope
+SLP_NA=Service Location Protocol naming authority
+FQDN=Use fully-qualified domain names in DHCP options
+BootFile=Boot file
+BootSrvA=Boot server address
+BootSrvN=Boot server name
+LeaseNeg=Lease negotiable flag
+EchoVC=Echo vendor class to client
+BootPath=Default path for BOOTP client boot files
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/OptionNameField.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/OptionNameField.java
new file mode 100644
index 0000000000..4d3f8fa444
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/OptionNameField.java
@@ -0,0 +1,80 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1999-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.client;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import javax.swing.text.*;
+import com.sun.dhcpmgr.data.Option;
+
+/**
+ * A text field which enforces the syntax rules for an option name. These
+ * are all the rules for DhcptabNameField, plus a limit on the length.
+ */
+public class OptionNameField extends DhcptabNameField {
+
+ /**
+ * Constructs a field initialized to the provided text. Defaults to
+ * 10 characters wide.
+ * @param text the text to display initially
+ */
+ public OptionNameField(String text) {
+ this(text, 10);
+ }
+
+ /**
+ * Constructs a field initialized to the provided text with the requested
+ * size.
+ * @param text the text to display initially
+ * @param length the length in characters the field should size itself to
+ */
+ public OptionNameField(String text, int length) {
+ super(text, length);
+ }
+
+ protected Document createDefaultModel() {
+ return new OptionNameDocument();
+ }
+
+ /*
+ * This is the recommended way to validate input, as opposed to trapping
+ * KeyEvents because this will actually catch paste operations as well.
+ */
+ class OptionNameDocument extends DhcptabNameDocument {
+ public void insertString(int offs, String str, AttributeSet a)
+ throws BadLocationException {
+ if (str != null) {
+ if ((getLength() + str.length()) > Option.MAX_NAME_SIZE) {
+ throw new BadLocationException("", offs);
+ }
+ }
+ super.insertString(offs, str, a);
+ }
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/OptionView.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/OptionView.java
new file mode 100644
index 0000000000..1f3ec2b498
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/OptionView.java
@@ -0,0 +1,469 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.client;
+
+import javax.swing.*;
+import javax.swing.event.*;
+import javax.swing.table.*;
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+import java.text.*;
+
+import com.sun.dhcpmgr.ui.*;
+import com.sun.dhcpmgr.server.*;
+import com.sun.dhcpmgr.data.*;
+import com.sun.dhcpmgr.bridge.BridgeException;
+
+/**
+ * The view for options. Only displays locally defined options.
+ */
+public class OptionView implements View {
+
+ // Model for the table displaying the options.
+ class OptionTableModel extends AbstractTableModel {
+ private Option [] data = null;
+ private boolean firstLoad;
+
+ public OptionTableModel() {
+ firstLoad = true;
+ }
+
+ public void load() {
+ data = null;
+ // Update status line
+ MainFrame.setStatusText(
+ ResourceStrings.getString("loading_options"));
+ fireTableDataChanged();
+
+ // Kick off background loading
+ OptionLoader loader = new OptionLoader();
+ }
+
+ protected void doneLoading() {
+ sortedTableModel.reallocateIndexes();
+ if (firstLoad) {
+ sortedTableModel.sortByColumn(0);
+ firstLoad = false;
+ }
+ // Check for any ill-defined options, tell user about them
+ Vector errs = new Vector();
+ for (int i = 0; i < data.length; ++i) {
+ if (!data[i].isValid()) {
+ errs.addElement(data[i].getKey());
+ }
+ }
+ if (errs.size() != 0) {
+ Object [] objs = new Object[2];
+ objs[0] =
+ ResourceStrings.getString("option_validation_warning");
+ JList optionList = new JList(errs);
+ JScrollPane scrollPane = new JScrollPane(optionList);
+ optionList.setVisibleRowCount(4);
+ objs[1] = scrollPane;
+ JOptionPane.showMessageDialog(optionTable, objs,
+ ResourceStrings.getString("warning"),
+ JOptionPane.WARNING_MESSAGE);
+ }
+ fireTableDataChanged();
+ }
+
+ protected void setData(Option [] newdata) {
+ data = newdata;
+ }
+
+ public int getRowCount() {
+ if (data == null) {
+ return 0;
+ } else {
+ return data.length;
+ }
+ }
+
+ public int getColumnCount() {
+ return 6;
+ }
+
+ public Object getValueAt(int row, int column) {
+ switch (column) {
+ case 0:
+ return data[row].getKey();
+ case 1:
+ return Option.getContextString(data[row].getContext());
+ case 2:
+ return new Integer(data[row].getCode());
+ case 3:
+ return Option.getTypeString(data[row].getType());
+ case 4:
+ return new Integer(data[row].getGranularity());
+ case 5:
+ return new Integer(data[row].getMaximum());
+ default:
+ return null;
+ }
+ }
+
+ public Class getColumnClass(int column) {
+ switch (column) {
+ case 0:
+ case 1:
+ case 3:
+ return String.class;
+ case 2:
+ case 4:
+ case 5:
+ return Integer.class;
+ default:
+ super.getColumnClass(column);
+ }
+ return null;
+ }
+
+ public String getColumnName(int column) {
+ switch (column) {
+ case 0:
+ return ResourceStrings.getString("name_column");
+ case 1:
+ return ResourceStrings.getString("category_column");
+ case 2:
+ return ResourceStrings.getString("code_column");
+ case 3:
+ return ResourceStrings.getString("type_column");
+ case 4:
+ return ResourceStrings.getString("granularity_column");
+ case 5:
+ return ResourceStrings.getString("maximum_column");
+ default:
+ super.getColumnName(column);
+ }
+ return null;
+ }
+
+ public Option getOptionAt(int row) {
+ return data[row];
+ }
+ }
+
+ // Background loader for options.
+ class OptionLoader extends SwingWorker {
+ public Object construct() {
+ try {
+ return DataManager.get().getOptions(true);
+ } catch (final BridgeException e) {
+ // Since we're in a background thread, ask Swing to run ASAP.
+ SwingUtilities.invokeLater(new Runnable() {
+ Object [] args = new Object[] { e.getMessage() };
+ public void run() {
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString("error_loading_options"));
+ JOptionPane.showMessageDialog(null, form.format(args),
+ ResourceStrings.getString("server_error_title"),
+ JOptionPane.ERROR_MESSAGE);
+ }
+ });
+ return null;
+ }
+ }
+
+ public void finished() {
+ Option [] options = (Option [])get();
+ if (options == null) {
+ options = new Option[0];
+ }
+ OptionView.optionTableModel.setData(options);
+ OptionView.optionTableModel.doneLoading();
+ MainFrame.setStatusText(MessageFormat.format(
+ ResourceStrings.getString("option_status_message"),
+ OptionView.optionTableModel.getRowCount()));
+ optionTable.clearSelection();
+ }
+ }
+
+ /*
+ * Reload data when any dialogs this view launches indicate they've
+ * changed the data
+ */
+ class DialogListener implements ActionListener {
+ public void actionPerformed(ActionEvent e) {
+ if (!e.getActionCommand().equals(DialogActions.CANCEL)) {
+ reload();
+ }
+ }
+ }
+
+ private AutosizingTable optionTable;
+ private JScrollPane optionPane;
+ protected static OptionTableModel optionTableModel = null;
+ private JCheckBoxMenuItem showGrid;
+ private JMenuItem optionHelp;
+ private Vector[] menuItems;
+ private Frame myFrame;
+ private static boolean firstview = true;
+ private Vector selectionListeners = new Vector();
+ private TableSorter sortedTableModel;
+
+ public OptionView() {
+ // Create table to display in data area
+ optionTableModel = new OptionTableModel();
+ sortedTableModel = new TableSorter(optionTableModel);
+
+ // Use table which resizes columns to fit data
+ optionTable = new AutosizingTable(sortedTableModel);
+ sortedTableModel.addMouseListenerToHeaderInTable(optionTable);
+ optionTable.getTableHeader().setReorderingAllowed(true);
+ optionTable.getTableHeader().setResizingAllowed(true);
+ optionTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+
+ // Allow clicks in header to adjust sorting column
+ sortedTableModel.addActionListener(new ActionListener() {
+ private int sortModelIndex = -1;
+ private TableCellRenderer savedRenderer;
+ private SortedHeaderRenderer sortedRenderer =
+ new SortedHeaderRenderer(optionTable);
+
+ public void actionPerformed(ActionEvent e) {
+ // Clear the selection if sorting is changed
+ optionTable.clearSelection();
+
+ // Change the header rendering to show sorting column
+ int modelIndex = Integer.parseInt(e.getActionCommand());
+ int viewIndex =
+ optionTable.convertColumnIndexToView(modelIndex);
+ if (sortModelIndex != -1) {
+ int sortViewIndex =
+ optionTable.convertColumnIndexToView(sortModelIndex);
+ optionTable.getColumnModel().getColumn(sortViewIndex).
+ setHeaderRenderer(savedRenderer);
+ }
+ /*
+ * Save the column currently being sorted so we can restore
+ * the renderer later. We save model columns rather than
+ * view columns because model columns are invariant while
+ * view columns can be reordered with confusion resulting.
+ */
+ TableColumn c =
+ optionTable.getColumnModel().getColumn(viewIndex);
+ savedRenderer = c.getHeaderRenderer();
+ c.setHeaderRenderer(sortedRenderer);
+ sortModelIndex = modelIndex;
+ }
+ });
+
+ // Make it scrollable
+ optionPane = new JScrollPane(optionTable);
+
+ // Make double-clicks the same as Edit->Properties
+ optionTable.addMouseListener(new MouseAdapter() {
+ public void mouseClicked(MouseEvent e) {
+ if (e.getClickCount() == 2) {
+ handleProperties();
+ }
+ }
+ });
+
+ // Create menu items
+ Mnemonic mnShowGrid =
+ new Mnemonic(ResourceStrings.getString("show_grid"));
+ showGrid = new JCheckBoxMenuItem(mnShowGrid.getString(),
+ true);
+ showGrid.setMnemonic(mnShowGrid.getMnemonic());
+ showGrid.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ optionTable.setShowGrid(showGrid.getState());
+ }
+ });
+
+ Mnemonic mnOnOptions =
+ new Mnemonic(ResourceStrings.getString("on_options_item"));
+ optionHelp = new JMenuItem(mnOnOptions.getString());
+ optionHelp.setMnemonic(mnOnOptions.getMnemonic());
+ optionHelp.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ DhcpmgrApplet.showHelp("options_reference");
+ }
+ });
+
+ /*
+ * Build the sets of menu items which we'll return to
+ * MainFrame when it asks for them.
+ */
+ menuItems = new Vector[MainFrame.MENU_COUNT];
+ for (int i = 0; i < menuItems.length; ++i) {
+ menuItems[i] = new Vector();
+ }
+ menuItems[MainFrame.VIEW_MENU].addElement(showGrid);
+ menuItems[MainFrame.HELP_MENU].addElement(optionHelp);
+
+ // Listen for selections events, manipulate menu item state as needed
+ optionTable.getSelectionModel().addListSelectionListener(
+ new ListSelectionListener() {
+ public void valueChanged(ListSelectionEvent e) {
+ // Notify listeners that our selection state may have changed
+ notifySelectionListeners();
+ }
+ });
+ }
+
+ public String getName() {
+ return ResourceStrings.getString("option_view_name");
+ }
+
+ // Return custom menus we want added, in our case none.
+ public Enumeration menus() {
+ return null;
+ }
+
+ // Return menu items for each menu as requested by MainFrame
+ public Enumeration menuItems(int menu) {
+ return menuItems[menu].elements();
+ }
+
+ public Component getDisplay() {
+ return optionPane;
+ }
+
+ public void setActive(boolean state) {
+ if (state) {
+ // Things we do only the first time we're displayed
+ if (firstview) {
+ myFrame = (Frame)SwingUtilities.getAncestorOfClass(
+ MainFrame.class, optionTable);
+ optionTableModel.load();
+ String s = MessageFormat.format(
+ ResourceStrings.getString("option_status_message"),
+ sortedTableModel.getRowCount());
+ MainFrame.setStatusText(s);
+ firstview = false;
+ }
+ }
+ }
+
+ public void find(String s) {
+ int startRow = optionTable.getSelectedRow() + 1;
+ for (int i = startRow; i < sortedTableModel.getRowCount(); ++i) {
+ if (optionTableModel.getOptionAt(
+ sortedTableModel.mapRowAt(i)).toString().indexOf(s) != -1) {
+ optionTable.setRowSelectionInterval(i, i);
+ optionTable.scrollRectToVisible(optionTable.getCellRect(i, 0,
+ false));
+ return;
+ }
+ }
+ // Got to the end, wrap around
+ for (int i = 0; i < startRow; ++i) {
+ if (optionTableModel.getOptionAt(
+ sortedTableModel.mapRowAt(i)).toString().indexOf(s) != -1) {
+ optionTable.setRowSelectionInterval(i, i);
+ optionTable.scrollRectToVisible(optionTable.getCellRect(i, 0,
+ false));
+ return;
+ }
+ }
+ }
+
+ public void handleCreate() {
+ CreateOptionDialog d = new CreateOptionDialog(myFrame,
+ CreateOptionDialog.CREATE);
+ d.addActionListener(new DialogListener());
+ d.pack();
+ d.setVisible(true);
+ }
+
+ public void handleDelete() {
+ int selectedRow = optionTable.getSelectedRow();
+ if (selectedRow == -1) {
+ return;
+ }
+ DeleteOptionDialog d = new DeleteOptionDialog(myFrame,
+ optionTableModel.getOptionAt(
+ sortedTableModel.mapRowAt(selectedRow)));
+ d.addActionListener(new DialogListener());
+ d.pack();
+ d.setVisible(true);
+ }
+
+ public void handleDuplicate() {
+ int selectedRow = optionTable.getSelectedRow();
+ if (selectedRow == -1) {
+ return;
+ }
+ CreateOptionDialog d = new CreateOptionDialog(myFrame,
+ CreateOptionDialog.DUPLICATE);
+ d.setOption(optionTableModel.getOptionAt(
+ sortedTableModel.mapRowAt(selectedRow)));
+ d.addActionListener(new DialogListener());
+ d.pack();
+ d.setVisible(true);
+ }
+
+ public void handleProperties() {
+ int selectedRow = optionTable.getSelectedRow();
+ if (selectedRow == -1) {
+ return;
+ }
+ CreateOptionDialog d = new CreateOptionDialog(myFrame,
+ CreateOptionDialog.EDIT);
+ d.setOption(optionTableModel.getOptionAt(
+ sortedTableModel.mapRowAt(selectedRow)));
+ d.addActionListener(new DialogListener());
+ d.pack();
+ d.setVisible(true);
+ }
+
+ public void handleUpdate() {
+ reload();
+ }
+
+ private void reload() {
+ optionTableModel.load();
+ }
+
+ public void addSelectionListener(SelectionListener listener) {
+ selectionListeners.addElement(listener);
+ }
+
+ public void removeSelectionListener(SelectionListener listener) {
+ selectionListeners.removeElement(listener);
+ }
+
+ private void notifySelectionListeners() {
+ Enumeration en = selectionListeners.elements();
+ while (en.hasMoreElements()) {
+ SelectionListener l = (SelectionListener)en.nextElement();
+ l.valueChanged();
+ }
+ }
+
+ public boolean isSelectionEmpty() {
+ return optionTable.getSelectionModel().isSelectionEmpty();
+ }
+
+ public boolean isSelectionMultiple() {
+ return false; // Multiple selection is not allowed.
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/RelayView.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/RelayView.java
new file mode 100644
index 0000000000..28059441af
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/RelayView.java
@@ -0,0 +1,106 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.client;
+
+import java.awt.Component;
+import java.util.Enumeration;
+import javax.swing.*;
+
+import com.sun.dhcpmgr.ui.*;
+
+/**
+ * The view displayed when we're in relay mode.
+ */
+public class RelayView implements View {
+ private Component display;
+
+ public RelayView() {
+ display = Wizard.createTextArea(
+ ResourceStrings.getString("relay_view_text"), 4, 45);
+ }
+
+ public String getName() {
+ return ResourceStrings.getString("relay_view_name");
+ }
+
+ public Enumeration menus() {
+ return null;
+ }
+
+ public Enumeration menuItems(int menu) {
+ return null;
+ }
+
+ public Component getDisplay() {
+ return display;
+ }
+
+ public void find(String s) {
+ // nothing to search
+ }
+
+ public void setActive(boolean state) {
+ // Nothing to do
+ }
+
+ public void handleCreate() {
+ // Nothing to do
+ }
+
+ public void handleDelete() {
+ // Nothing to do
+ }
+
+ public void handleDuplicate() {
+ // Nothing to do
+ }
+
+ public void handleProperties() {
+ // Nothing to do
+ }
+
+ public void handleUpdate() {
+ // Nothing to do
+ }
+
+ public void addSelectionListener(SelectionListener listener) {
+ // Nothing to do
+ }
+
+ public void removeSelectionListener(SelectionListener listener) {
+ // Nothing to do
+ }
+
+ public boolean isSelectionEmpty() {
+ return true; // Nothing to select
+ }
+
+ public boolean isSelectionMultiple() {
+ return false; // Nothing to select
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ReleaseAddressDialog.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ReleaseAddressDialog.java
new file mode 100644
index 0000000000..78ef8d2250
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ReleaseAddressDialog.java
@@ -0,0 +1,211 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 1998-2002 by Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.client;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.text.*;
+import java.util.*;
+import javax.swing.*;
+import javax.swing.table.*;
+
+import com.sun.dhcpmgr.server.*;
+import com.sun.dhcpmgr.data.*;
+import com.sun.dhcpmgr.ui.*;
+
+/**
+ * This dialog is used to release one or more addresses. Release is defined
+ * to mean resetting the client id to 00 and setting the lease date to 0.
+ */
+public class ReleaseAddressDialog extends MultipleOperationDialog {
+ private DhcpClientRecord [] recs;
+ private String table;
+ boolean showAddresses;
+
+ class AddressTableModel extends AbstractTableModel {
+
+ public int getRowCount() {
+ if (recs == null) {
+ return 0;
+ } else {
+ return recs.length;
+ }
+ }
+
+ public int getColumnCount() {
+ return 3;
+ }
+
+ public Object getValueAt(int row, int column) {
+ switch (column) {
+ case 0:
+ if (showAddresses) {
+ return recs[row].getClientIP();
+ } else {
+ return recs[row].getClientName();
+ }
+ case 1:
+ return recs[row].getClientId();
+ case 2:
+ return recs[row].getExpiration();
+ default:
+ return null;
+ }
+ }
+
+ public Class getColumnClass(int column) {
+ switch (column) {
+ case 0:
+ if (showAddresses) {
+ return IPAddress.class;
+ } else {
+ return String.class;
+ }
+ case 1:
+ return String.class;
+ case 2:
+ return Date.class;
+ default:
+ return Object.class;
+ }
+ }
+
+ public String getColumnName(int column) {
+ switch (column) {
+ case 0:
+ if (showAddresses) {
+ return ResourceStrings.getString("address_column");
+ } else {
+ return ResourceStrings.getString("client_name_column");
+ }
+ case 1:
+ return ResourceStrings.getString("client_column");
+ case 2:
+ return ResourceStrings.getString("expires_column");
+ default:
+ return null;
+ }
+ }
+ }
+
+ public ReleaseAddressDialog(Frame f, DhcpClientRecord [] clients,
+ String table, boolean showAddresses) {
+ super(f, false);
+ recs = clients;
+ this.table = table;
+ this.showAddresses = showAddresses;
+ }
+
+ public String getTitle() {
+ return ResourceStrings.getString("release_address_title");
+ }
+
+ protected JPanel getMainPanel() {
+ JPanel mainPanel = new JPanel(new BorderLayout(10, 10));
+ mainPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
+
+ JLabel message = new JLabel(
+ ResourceStrings.getString("release_address_confirm"));
+
+ message.setLabelFor(mainPanel);
+ message.setToolTipText(
+ ResourceStrings.getString("release_address_confirm"));
+
+ mainPanel.add(message, BorderLayout.NORTH);
+
+ JTable addressTable = new JTable(new AddressTableModel());
+ JScrollPane scrollPane = new JScrollPane(addressTable);
+ Dimension d = addressTable.getPreferredScrollableViewportSize();
+ d.height = 100;
+ addressTable.setPreferredScrollableViewportSize(d);
+ ExtendedCellRenderer renderer = new ExtendedCellRenderer();
+ addressTable.setDefaultRenderer(Date.class, renderer);
+ addressTable.setDefaultRenderer(IPAddress.class, renderer);
+
+ mainPanel.add(scrollPane, BorderLayout.CENTER);
+ buttonPanel.setOkEnabled(true);
+ return mainPanel;
+ }
+
+ protected String getProgressMessage() {
+ return ResourceStrings.getString("release_addr_progress");
+ }
+
+ protected int getProgressLength() {
+ return recs.length;
+ }
+
+ protected String getErrorHeading() {
+ return ResourceStrings.getString("address_column");
+ }
+
+ protected Class getErrorClass() {
+ return IPAddress.class;
+ }
+
+ protected Thread getOperationThread() {
+ return new Thread() {
+ public void run() {
+ DhcpNetMgr server = DataManager.get().getDhcpNetMgr();
+ for (int i = 0; i < recs.length; ++i) {
+ DhcpClientRecord client = (DhcpClientRecord)recs[i].clone();
+ Date emptyDate = new Date(0);
+ try {
+ // Clear client id and lease date
+ client.setClientId(DhcpClientRecord.DEFAULT_CLIENT_ID);
+ client.setFlags(DhcpClientRecord.DEFAULT_FLAGS);
+ client.setExpiration(emptyDate);
+ server.modifyClient(recs[i], client, table);
+ // Update progress meter
+ updateProgress(i+1, recs[i].getClientIPAddress());
+ } catch (InterruptedException e) {
+ // User asked to stop
+ closeDialog();
+ return;
+ } catch (Throwable e) {
+ addError(recs[i].getClientIP(), e.getMessage());
+ }
+ }
+ // Errors occurred, display them now
+ if (errorsOccurred()) {
+ displayErrors(
+ ResourceStrings.getString("release_address_error"));
+ }
+ closeDialog();
+ }
+ };
+ }
+
+ protected String getHelpKey() {
+ return "release_addresses";
+ }
+
+ protected void fireActionPerformed() {
+ fireActionPerformed(this, DialogActions.OK);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ResourceBundle.properties b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ResourceBundle.properties
new file mode 100644
index 0000000000..6aa2271c7b
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ResourceBundle.properties
@@ -0,0 +1,692 @@
+#
+#pragma ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# General resources
+#
+yes=Yes
+no=No
+
+delete=De&lete
+add=&Add
+modify=&Modify
+select=&Select
+
+error_message=Error Message
+
+err_must_be_root=You must be root to run this program.
+
+err_initializing_help=Unable to initialize the help system.
+err_starting_help=Could not start browser to view help.
+
+err_initializing_program=The following error occurred while starting this program:
+err_initializing_options=The daemon's defaults file appears to be corrupt. Please see the dhcp(4) man page.
+err_reading_options=The following error occurred while reading the server's options:\n{0}
+
+init_error=DHCP Manager Initialization Error
+
+dhcp_manager=DHCP Manager
+server_error_title=Server Error
+server_warning_title=Server Warning
+server_not_running=The DHCP service is not running, so it could not be notified of this change.
+
+#
+# Standard options initialization
+#
+stdopts_init_error=Error initializing standard options: {0}
+
+#
+# Services menu items
+#
+restart_item=&Restart
+stop_item=S&top
+start_item=St&art
+disable_item=&Disable
+enable_item=E&nable
+modify_service_item=&Modify
+cvt_service_item=&Convert Data Store
+export_item=E&xport Data
+import_item=&Import Data
+
+#
+# Service status messages
+#
+service_restarted=The server has been restarted
+service_stopped=The server has been stopped
+service_started=The server has been started
+
+#
+# Help menu items
+#
+overview_item=&Overview
+howto_item=How &To
+index_item=&Index
+on_service_item=On Se&rvice
+on_addresses_item=On &Addresses
+on_macros_item=On &Macros
+on_options_item=On O&ptions
+
+#
+# Address view resources
+#
+address_view_name=Addresses
+network=Net&work:
+no_networks=No networks
+loading_networks=Loading network list
+networks_loaded=Found {0,number} networks
+address_status_message={0,number} addresses loaded
+loading_addresses=Loading addresses for network {0}
+run_network_wizard=You must configure a network for DHCP service\nusing the Network Wizard before creating any addresses.
+error_loading_addrs=The following error was encountered while reading addresses:\n{0}
+
+address_column=IP Address
+client_name_column=Client Name
+flags_column=Status
+expires_column=Expires
+server_column=Server
+macro_column=Macro
+client_column=Client ID
+comment_column=Comment
+
+show_addresses=Show &Addresses
+show_grid=Show &Grid
+
+#
+# Option view resources
+#
+option_view_name=Options
+loading_options=Loading options
+option_status_message={0,number} options loaded
+error_loading_options=The following error was encountered while reading options:\n{0}
+option_validation_warning=The following options contain errors:
+
+name_column=Name
+category_column=Category
+code_column=Code
+type_column=Type
+granularity_column=Granularity
+maximum_column=Maximum
+
+#
+# Macro view resources
+#
+macro_view_name=Macros
+option_column=Option Name
+value_column=Value
+macros=Macros
+loading_macros=Loading macros
+macro_status_message={0,number} macros loaded
+error_loading_macros=The following error was encountered while reading macros:\n{0}
+macro_validation_warning=The following macros contain invalid option settings:
+
+#
+# Create/duplicate/edit macro
+#
+create_macro_title=Create Macro
+edit_macro_title=Macro Properties
+duplicate_macro_title=Duplicate Macro
+option_name=Option Name:
+option_value=Option Value:
+
+input_error=Input Error
+create_location_error=Error making location: {0}
+create_macro_error=Error creating the macro {0}, message from server was: {1}
+edit_macro_error=Error editing the macro {0}, message from server was: {1}
+empty_macro_error=This macro contains no options
+bad_macro_name={0} is not a legal DHCP macro name
+bad_option_name={0} is not a legal DHCP option
+bad_option_value={0} is not a legal value for the DHCP option {1}
+macro_contains_option=This macro already contains the option {0}
+option_code_exists_error=Option code is already assigned to another option
+
+signal_server=Notify DHCP server of change
+
+#
+# Disable service resources
+#
+disable_service_title=Disable Service
+disable_relay_confirm=OK to disable the BOOTP relay service on server {0}?
+disable_service_confirm=OK to disable the DHCP service on server {0}?
+disable_service_error=Error disabling the service, message from server was: {0}
+
+#
+# Enable service resources
+#
+enable_service_title=Enable Service
+enable_relay_confirm=OK to enable the BOOTP relay service on server {0}?
+enable_service_confirm=OK to enable the DHCP service on server {0}?
+enable_service_error=Error enabling the service, message from server was: {0}
+
+#
+# Delete macro
+#
+delete_macro_title=Delete Macro
+delete_macro_confirm=OK to delete the macro {0}?
+delete_macro_error=Error deleting the macro {0}, message from server was: {1}
+
+#
+# Server state manipulation messages
+#
+startup_server_error=Unable to start the DHCP service, message from server was: {0}
+shutdown_server_error=Unable to stop the DHCP service, message from server was: {0}
+restart_server_error=Unable to restart the DHCP service, message from server was: {0}
+
+#
+# Find messages
+#
+find_macro_error=No macro containing {0} was found
+
+#
+# Create/duplicate/edit option
+#
+create_option_title=Create Option
+edit_option_title=Option Properties
+duplicate_option_title=Duplicate Option
+name_label=&Name:
+contents_label=Contents
+category_label=Categor&y:
+data_type_label=Data &Type:
+granularity_label=&Granularity:
+maximum_label=&Maximum:
+client_classes_label=Vendor Client Classes
+option_code_label=Cod&e:
+create_option_error=Error creating the option {0}: {1}
+edit_option_error=Error editing the option {0}: {1}
+empty_vendor_error=You must supply at least one vendor client class for a vendor option.
+invalid_client_class=The vendor client class you entered, {0}, is not valid.
+
+#
+# Delete option dialog
+#
+delete_option_title=Delete Option
+delete_option_confirm=OK to delete the option {0}?
+delete_option_error=Error deleting the option {0}, message from server was: {1}
+
+bootp=Bootp
+unusable=Unusable
+manual=Reserved
+permanent=Permanent
+dynamic=Dynamic
+
+#
+# Create/duplicate/edit address
+#
+edit_address_title=Address Properties
+duplicate_address_title=Duplicate Address
+create_address_title=Create Address
+address_exists=The address is already defined in the DHCP network table.
+address_missing=The address was not found in the DHCP network table.
+host_exists=A hosts table entry with this name already exists, the DHCP network table was not updated.
+host_missing=There is no hosts table entry with this name, the DHCP network table was not updated.
+invalid_address=The address you entered, {0}, is not a valid IP address.
+invalid_server=The server you entered, {0}, does not exist.
+invalid_client_id=The client ID you entered, {0}, is not valid.
+invalid_date=The expiration date you entered, {0}, is not valid.\nDates must be entered in a format similar to {1}.
+
+address_tab_label=Address
+lease_tab_label=Lease
+ip_address_label=&IP Address:
+hostname_label=C&lient Name:
+owning_server_label=O&wned by Server:
+config_macro_label=Configuration &Macro:
+client_id_label=C&lient ID:
+comment_label=Commen&t:
+unusable_checkbox=Address is unusable
+bootp_checkbox=Assign only to BOOTP clients
+manual_checkbox=Reserved
+leased_label=&Dynamic assignment expiring:
+permanent_label=&Permanent assignment
+lease_policy_label=Lease Policy
+no_macro_item=(no macro assigned)
+
+#
+# Delete address dialog
+#
+delete_address_title=Delete Address
+delete_address_confirm=OK to delete the following addresses?
+delete_hosts_checkbox=Delete from hosts table
+hosts_entry_missing=No entry was found in the server's hosts table, address was not deleted.
+
+delete_address_error=The following errors occurred while deleting addresses:
+delete_addr_progress=Deleting address:
+
+#
+# Address wizard resources
+#
+add_addresses=&Address Wizard
+address_wizard_title=Add Addresses to Network {0}
+
+add_wiz_number_desc=Specify the number of IP addresses.
+add_wiz_explain=This wizard will help you add IP addresses to a DHCP server in one operation. The wizard adds the IP addresses to the selected network table in the DHCP database.
+add_wiz_count_explain=How many addresses do you want to add?
+add_wiz_count_label=Number of IP &Addresses:
+add_wiz_comment_explain=Why are you adding these addresses? Enter a comment, or leave this space blank.
+add_wiz_comment_label=Co&mment:
+add_wiz_count_error=You must enter a non-zero value for the number of IP addresses.
+
+add_wiz_server_desc=Select the server and starting IP address.
+add_wiz_server_explain=Which DHCP server will manage these addresses?
+add_wiz_server_label=&Managed by Server:
+add_wiz_start_explain=What is the first IP number of the range of addresses you want to add?
+add_wiz_start_label=S&tarting IP Address:
+add_wiz_generate_explain=Would you like this program to generate a list of client names for you? For example, if you specify the root name "sales", client names will be sales-1, sales-2, etc.
+add_wiz_generate_label=Generate Client Names
+add_wiz_rootname_label=&Root Name:
+add_wiz_invalid_address={0} is not a valid IP address. Please enter a correctly formatted IP address.
+
+add_wiz_confirm_desc=Confirm the IP address list.
+add_wiz_confirm_explain=Is this the list of addresses you want to add? If not, go back to the previous steps and change the number of addresses or starting address.
+add_wiz_confirm_label=&IP Addresses To Be Added:
+
+add_wiz_configure_desc=Enter client configuration information.
+add_wiz_macro_explain=How do you want to configure the new clients? Choose a configuration macro from the list below. Press "View" to see the contents of the macro.
+add_wiz_macro_label=Configuration &Macro:
+add_wiz_view_button=&View
+add_wiz_flag_explain=Would you like to mark these addresses so they are unusable until you decide otherwise?
+add_wiz_unusable_label=Addresses are unusable
+
+add_wiz_lease_desc=Select the lease type.
+add_wiz_lease_explain=What type of lease do you want?\n\nA dynamic lease means that clients receive IP addresses as they become available, on a first-come, first-served basis. A permanent lease means that a client receives the same IP address every time that it connects to the network.
+add_wiz_lease_label=&Lease Type:
+
+add_wiz_review_desc=Review.
+add_wiz_review_explain=Is the following information correct? If not, you can change entries by going back to the corresponding wizard step.
+add_wiz_review_unusable=Addresses are Unusable:
+
+generate_addresses_warning=Only {0,number} of the {1,number} requested addresses could be generated.
+add_wiz_none_available=None of the addresses you requested are available. Try a different starting address.
+warning=Warning
+
+add_wiz_progress=Adding address:
+
+add_wiz_error=The following errors were encountered while adding addresses:
+
+bad_network_address=The address you entered, {0}, is not part of the network {1}.
+bad_server_name={0} is not a known server. Please enter a valid server name.
+
+#
+# Definitions for the modify multiple addresses dialog
+#
+modify_multiple_addresses=Modify Multiple Addresses
+
+modify_multiple_number=&Number of Addresses Selected:
+modify_multiple_server=Managing &Server:
+modify_multiple_comment=Commen&t:
+modify_multiple_macro=Configuration &Macro:
+modify_multiple_bootp=&BootP:
+modify_multiple_unusable=&Unusable:
+modify_multiple_lease=&Lease Type:
+
+modify_multiple_keep=Keep current settings
+modify_multiple_bootp_all=Assign all addresses only to BootP clients
+modify_multiple_bootp_none=Do not assign addresses to BootP clients
+modify_multiple_unusable_all=Mark all addresses unusable
+modify_multiple_unusable_none=Mark all addresses usable
+modify_multiple_dynamic=Dynamic
+modify_multiple_permanent=Permanent
+
+modify_multiple_error=The following errors occurred while modifying addresses:
+modify_multiple_progress=Modifying address:
+
+#
+# Definitions for release address dialog
+#
+release_addresses=&Release Addresses
+release_address_title=Release Addresses
+release_address_confirm=OK to release the following addresses?
+release_addr_progress=Releasing address:
+release_address_error=The following errors occurred while releasing addresses:
+
+#
+# Datastore Wizard
+#
+ds_wiz_datastore_desc=Select data storage format.
+ds_wiz_datastore_parm_desc=Configure data store.
+ds_wiz_init_error=Error initializing {0} data store: {1}
+
+#
+# Configuration Wizard
+#
+cfg_wiz_title=DHCP Configuration Wizard
+cfg_wiz_explain=This wizard will help you configure the system as a DHCP server.
+cfg_wiz_store_explain=Where would you like to store the DHCP configuration data?
+
+cfg_wiz_host_explain=Which of the following nameservices should be used to store hosts records?
+cfg_wiz_no_host_management=Do not manage hosts records
+cfg_wiz_files=/etc/hosts
+cfg_wiz_nisplus=NIS+
+cfg_wiz_dns=DNS
+cfg_wiz_hostdata_desc=Select hosts nameservice
+cfg_wiz_domain=Domain:
+cfg_wiz_invalid_host=Unable to manage defined hosts table
+
+cfg_wiz_lease_explain=How long can clients use the IP addresses assigned by this server?
+cfg_wiz_lease_length=&Length of Lease:
+cfg_wiz_negotiable_explain=Check the box below to enable clients to renew their leases prior to expiration. If you uncheck it, clients will be forced to reboot to obtain a new address when the lease expires.
+cfg_wiz_negotiable=Clients can renew their leases
+cfg_wiz_lease_desc=Specify lease policy.
+cfg_wiz_hours=hours
+cfg_wiz_days=days
+cfg_wiz_weeks=weeks
+cfg_wiz_zero_lease=You cannot specify a lease length of zero. Please enter a new lease length.
+cfg_wiz_lease_overflow=The lease value you entered is too large.\nLeases must be no more than {0} {1}
+
+cfg_wiz_dns_explain=Edit the information below to supply the correct Domain Name Service (DNS) configuration for DHCP clients of this server.
+cfg_wiz_dns_domain=&DNS Domain:
+cfg_wiz_dns_servers=DNS Servers:
+cfg_wiz_dns_desc=Specify DNS domain and servers.
+cfg_wiz_dns_both=You must either supply both a DNS domain and list of servers, or neither.
+
+cfg_wiz_network_explain=Select a network from the list, or type in a network address, then type in a subnet mask if the displayed default is not correct.
+cfg_wiz_network=Network &Address:
+cfg_wiz_network_explainmore=The DHCP server will be configured to provide IP addresses to clients on this network. Later, you can configure this server to support additional networks with the Network Wizard.
+cfg_wiz_network_desc=Specify network address and subnet mask.
+cfg_wiz_mask=Subnet &Mask:
+cfg_wiz_bad_network=The network you entered, {0}, is not a valid IP network address.
+cfg_wiz_bad_mask=The subnet mask you entered, {0}, is not valid.
+cfg_wiz_network_configured=The network you entered, {0}, is already configured on this server.
+
+cfg_wiz_nettype_explain=Please enter the following information for the network:
+cfg_wiz_nettype=Network Type:
+cfg_wiz_nettype_label=Network Type
+cfg_wiz_routing_label=Routing
+cfg_wiz_lan=Local-Area (LAN)
+cfg_wiz_point=Point-to-Point
+cfg_wiz_router_discovery=Use router discovery protocol
+cfg_wiz_router_specify=Use router:
+cfg_wiz_nettype_desc=Specify network type and router.
+cfg_wiz_router_net_err=The router address you entered, {0}, is not on the network {1}.\nPlease enter a router address on that network.
+cfg_wiz_router_addr_err=The router address you entered, {0}, is not a valid IP address.
+
+cfg_wiz_nis_explain=Edit the information below to supply the correct Network Information Service (NIS) configuration to DHCP clients on this network.
+cfg_wiz_nis_domain=NIS &Domain:
+cfg_wiz_nis_servers=NIS Servers:
+cfg_wiz_nis_desc=Specify NIS domain and servers.
+cfg_wiz_nis_both=You must either supply both a NIS domain and list of servers, or neither.
+
+cfg_wiz_nisplus_explain=Edit the information below to supply the correct Network Information Service Plus (NIS+) configuration to DHCP clients on this network.
+cfg_wiz_nisplus_domain=NIS+ &Domain:
+cfg_wiz_nisplus_servers=NIS+ Servers:
+cfg_wiz_nisplus_desc=Specify NIS+ domain and servers.
+cfg_wiz_nisplus_both=You must either supply both a NIS+ domain and list of servers, or neither.
+
+cfg_wiz_datastore=Data Storage:
+cfg_wiz_hosts_resource=Hosts Nameservice:
+cfg_wiz_router=Router:
+cfg_wiz_review_explain=The system will be configured as a DHCP server\nwith the following settings:
+cfg_wiz_review_desc=Review.
+cfg_wiz_lease_fmt={0,number} {1}, {2}
+cfg_wiz_renewable=renewable
+cfg_wiz_nonrenewable=non-renewable
+
+#
+# Convert Wizard
+#
+cvt_wiz_title=Data Store Conversion
+cvt_wiz_explain=This wizard enables you to convert the DHCP tables from the current data store format, {0}, to the data store format you specify.
+cvt_wiz_store_explain=Select the new data store format:
+cvt_wiz_save_explain=The existing DHCP tables are deleted by default after the data store is successfully converted. If you want to save the tables in the old data store, check the box below.
+cvt_wiz_save_note=Note: If an error occurs during the conversion process, the old data store will not be deleted, and the DHCP server can continue to use it.
+cvt_wiz_save_label=Save old tables?
+cvt_wiz_save_tables_desc=Choose to save existing DHCP tables.
+cvt_wiz_review_desc=Review.
+cvt_wiz_review_explain=The DHCP data store will be converted, using the following settings:
+cvt_wiz_review_note=Note: The DHCP service will be stopped and restarted as part of the conversion process.
+cvt_wiz_old_datastore=Current data store:
+cvt_wiz_new_datastore=New data store:
+cvt_wiz_save_tables=Save old tables:
+cvt_wiz_table=Table
+cvt_wiz_dhcptab=dhcptab
+cvt_wiz_defaults=DHCP defaults
+cvt_wiz_progress=Converting Data Store:
+cvt_wiz_progress_dhcptab_cvt=Converted dhcptab
+cvt_wiz_progress_network_cvt=Converted {0}
+cvt_wiz_progress_dhcptab_del=Deleted dhcptab
+cvt_wiz_progress_network_del=Deleted {0}
+cvt_wiz_progress_defaults=Updated the DHCP defaults file
+cvt_wiz_progress_dhcptab_cvt_err=Error converting dhcptab
+cvt_wiz_progress_network_cvt_err=Error converting {0}
+cvt_wiz_progress_dhcptab_del_err=Error deleting dhcptab
+cvt_wiz_progress_network_del_err=Error deleting {0}
+cvt_wiz_progress_defaults_err=Error updating the DHCP defaults file
+cvt_wiz_error=Error
+cvt_wiz_same_datastore_error=The source data store is the same as destination data store.
+cvt_wiz_location_error=Error making location: {0}
+cvt_wiz_networks_error=Error getting networks: {0}
+cvt_wiz_errors=The following errors were encountered during the conversion:
+cvt_wiz_server_shutdown=Shutdown the service
+cvt_wiz_shutdown_err=Error shutting down DHCP server
+cvt_wiz_server_started=Started the service
+cvt_wiz_start_err=Error starting the DHCP server
+
+#
+# Definitions for the Network Wizard, which is really just a subset of the
+# config wizard with some wording changes
+#
+net_wiz_title=Add a DHCP Network
+add_network=Ne&twork Wizard
+net_wiz_net_explain=This wizard will help you configure additional networks on your DHCP server. For each network, the wizard creates a network macro to supply clients with relevant network information and a network table to record IP address assignments.\n\nSelect a network from the list, or type in a network address, then type in a subnet mask if the displayed default is not correct.
+net_wiz_review_explain=The following network will be added to the DHCP service:
+create_network_table_error=Unable to create the table for network {0}, message from server was: {1}
+
+#
+# Relay configuration
+#
+configure_relay_title=Configure BOOTP Relay
+dhcp_servers=DHCP Servers
+configure_relay_explain=Enter the IP addresses of the DHCP servers to which this BOOTP relay should forward requests.
+configure_relay_err_server_list=You must enter the address of at least one DHCP server to which this relay should forward DHCP traffic.
+
+#
+# Configuration choice dialog
+#
+configure_choice_title=Choose Server Configuration
+configure_choice_explain=This server can be configured as either a DHCP server or a BOOTP relay. If you don't want to do either of these things, press Cancel and this tool will exit.
+configure_dhcp_server=Configure as DHCP server
+configure_bootp_relay=Configure as BOOTP relay
+configure_relay_lookup_error=Unable to find a server named {0}.\nIf you know its IP address, you may enter that instead.
+
+#
+# Service options dialog
+#
+service_options_title=Modify Service Options
+service_options_verbose=Verbose log messages
+service_options_hops=&Maximum number of relay agent hops:
+service_options_bootp_compat=BOOTP Compatibility
+service_options_bootp_none=None
+service_options_bootp_auto=Automatic
+service_options_bootp_manual=Manual
+service_options_detect_duplicates=Detect duplicate IP addresses
+service_options_reload_dhcptab=Reload dhcptab every
+service_options_reload_minutes=m&inutes
+service_options_owner_ip=Act as owner of the following server addresses
+service_options_owner_ip_addresses=Server Addresses
+service_options_update_dns=Update DNS host information upon client request
+service_options_timeout_dns=&Timeout DNS update attempt after
+service_options_cache=C&ache offers for
+service_options_seconds=seconds
+service_options_options=Options
+service_options_interfaces=Interfaces
+service_options_addresses=Addresses
+service_options_restart=Restart Server
+service_options_interface=Interface
+service_options_network=Network
+service_options_monitored=&Monitored Interfaces:
+service_options_ignored=&Ignored Interfaces:
+service_options_log_transactions=Log Transactions to syslog Facility:
+service_options_error=Error modifying service options, message from server was: {0}
+
+#
+# Delete Networks dialog
+#
+delete_networks=Delete &Networks
+delete_networks_title=Delete Networks
+delete_networks_keep=&Keep Networks:
+delete_networks_delete=&Delete Networks:
+delete_networks_delete_hosts=Delete hosts table entries
+delete_networks_error=The following networks were not deleted:
+network_column=Network
+delete_networks_progress=Deleting network:
+
+#
+# Unconfigure dialog
+#
+unconfigure_service_item=&Unconfigure
+unconfigure_title=Unconfigure Service
+unconfigure_dhcp=Are you sure it is OK to unconfigure the DHCP service? If you are using a shared data store such as NIS+, removing the dhcptab and network tables may affect other DHCP servers.
+unconfigure_bootp=Are you sure it is OK to unconfigure the BOOTP relay service? This may cause clients to be unable to access the network.
+unconfigure_shutdown=DHCP Manager will exit once the service has been unconfigured.
+unconfigure_delete_tables=Remove the dhcptab and all DHCP network tables
+unconfigure_delete_hosts=Remove all hosts table entries for DHCP addresses
+
+unconfigure_progress=Unconfiguring the service
+unconfigure_error_heading=Operation
+unconfigure_error_messages=The following errors occurred while performing\nthe operations necessary to unconfigure the service
+
+unconfigure_error_shutdown=Shutdown
+unconfigure_server_shutdown=Shutdown the service
+
+unconfigure_error_defaults=Removing defaults file
+unconfigure_defaults_deleted=Defaults file removed
+
+unconfigure_error_macro=Removing server macro
+unconfigure_macro_deleted=Server macro removed
+
+unconfigure_error_dhcptab=Removing dhcptab
+unconfigure_dhcptab_deleted=Removed dhcptab
+
+unconfigure_error_network=Removing network {0}
+unconfigure_network_progress=Removed network {0}
+
+#
+# Transition dialog from config wizard to address wizard
+#
+start_address_wizard_title=Start Address Wizard?
+start_address_wizard=The DHCP server cannot provide addresses to clients\nuntil you configure it with a list of addresses it may use.\nWould you like to run the Address Wizard\nto configure addresses for the server?
+
+#
+# Transition dialog from config wizard to conversion wizard
+#
+start_cvt_wizard_title=Start Conversion Wizard?
+start_cvt_wizard=A version mismatch exists between the DHCP\nmanagement software and the DHCP data stores\nconfigured for the server. Neither the dhcpmgr nor\n the DHCP server can function properly until the\n data stores are converted to the current version.\n Do you wish to convert the data stores now?
+
+#
+# Dialog to view a macro while in the address wizard
+#
+view_macro_title=View Macro
+ok=&OK
+
+#
+# Dialog to select an option while editing/creating a macro
+#
+select_option_title=Select Option
+description_column=Description
+
+#
+# Relay view
+#
+relay_view_name=BOOTP Relay
+relay_view_text=This server is configured as a BOOTP relay. You may manage the relay service status and configuration through the Service menu.
+
+#
+# Export wizard resources
+#
+export_wiz_title=Export Data
+exp_wiz_dont_export=Do Not Export
+exp_wiz_export=Export
+
+exp_wiz_net_desc=Select networks
+exp_wiz_net_explain=This wizard helps you export some or all of the DHCP data configured on this server to a file that can then be imported into another Solaris DHCP server's configuration.\n\nSelect networks to export from this server's configuration and move them to the Export column.
+
+exp_wiz_macro_desc=Select macros
+exp_wiz_macros_explain=Select the macros to export and move them to the Export column.
+
+exp_wiz_option_desc=Select options
+exp_wiz_options_explain=Select the options to export and move them to the Export column.
+
+exp_wiz_file_desc=Specify export file location
+exp_wiz_file_explain=Type the full path name of the file to which the configuration data should be exported.
+exp_wiz_delete_explain=Do you want to automatically delete the exported data from this server after the export is completed?
+exp_wiz_file_label=&Export file:
+exp_wiz_delete_exported=Delete exported data
+
+exp_wiz_review_desc=Review
+exp_wiz_review_explain=Review your selections below. If you wish to make any changes, go back to the corresponding step and make the necessary corrections.
+exp_wiz_review_nets=&Networks:
+exp_wiz_review_macros=&Macros:
+exp_wiz_review_options=&Options:
+exp_wiz_delete_label=Delete Exported Data:
+
+exp_progress_title=Export Progress
+
+exp_error_occurred=The following error occurred while exporting:
+exp_overwrite=The export file exists; OK to overwrite?
+exp_overwrite_title=Overwrite File
+
+#
+# Import wizard resources
+#
+import_wiz_title=Import Data
+imp_progress_title=Import Progress
+
+imp_wiz_file_desc=Specify import file location
+imp_wiz_location_explain=This wizard helps you import DHCP configuration data previously exported from a Solaris DHCP server.\n\nType the full pathname to the file of exported data, which you want to import to this server.
+imp_wiz_file_label=&Import file:
+imp_wiz_override_explain=Do you want to overwrite existing data on this server that conflicts with the data being imported?
+imp_wiz_override_data=Overwrite existing data
+
+imp_wiz_review_explain=Review your selections below. If you wish to make any changes, go back to the corresponding step and make the necessary corrections.
+imp_wiz_review_info=File {0} was exported
+imp_wiz_review_src=From system:
+imp_wiz_review_user=By user:
+imp_wiz_review_date=On:
+imp_wiz_review_override=Overwrite data:
+imp_wiz_review_desc=Review
+
+imp_error=Import Error
+imp_err_file_not_found=The import file could not be opened.
+imp_err_reading_header=The import file header could not be read; the error message was:\n{0}
+
+
+# Mnemonics added for accessibility conformance. These mnemonics are for
+# labels that are associated with textfields, checkboxes, comboboxes etc.
+# NB: We must take take to make sure that each mnemonic is unique within
+# the dialog eg: OK, Cancel, Reset & help (O, C, R & H) should be avoided
+# as mnemonics for other components in the dialog. Also NB, this is not the
+# complete list of all Mnemonics but a list of additional ones, the original
+# resource strings have been updated to support mnemonics where possible
+# first i.e. `&` added to the string.
+#
+# Create Macro Dialog
+#
+md_name_label=&Name:
+md_option_name=O&ption Name:
+md_option_value=Option &Value:
+
+# Create Option Properties Dialog
+#
+op_name=&Name:
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ResourceStrings.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ResourceStrings.java
new file mode 100644
index 0000000000..f2da6a675f
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ResourceStrings.java
@@ -0,0 +1,64 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1998-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.client;
+
+import java.util.*;
+
+/**
+ * This class provides a convenient method to retrieve resources for
+ * the client package.
+ */
+public class ResourceStrings {
+
+ /**
+ * The handle to the resource bundle for the module.
+ */
+ private static ResourceBundle bundle = null;
+
+ /**
+ * Return a string from the resource bundle.
+ * @param key the key to the resource bundle string.
+ * @return the resource bundle string.
+ */
+ public static String getString(String key) {
+ String msg = null;
+ try {
+ if (bundle == null) {
+ bundle = ResourceBundle.getBundle(
+ "com.sun.dhcpmgr.client.ResourceBundle",
+ Locale.getDefault());
+ }
+ msg = bundle.getString(key);
+ } catch (Throwable e) {
+ msg = new String(key);
+ }
+ return msg;
+
+ } // getString
+
+} // ResourceStrings
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWModule.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWModule.java
new file mode 100644
index 0000000000..1874ae54ae
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWModule.java
@@ -0,0 +1,136 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.client;
+
+import java.awt.*;
+
+import javax.swing.*;
+import javax.swing.text.*;
+import javax.swing.event.*;
+
+import com.sun.dhcpmgr.ui.*;
+
+/**
+ * This class provides a skeletal implementation of a SUNW data store
+ * module to minimize the effort required to implement a SUNW data store.
+ */
+public abstract class SUNWModule extends DSModule {
+
+ /**
+ * The default path for the module.
+ */
+ protected String path;
+
+ /**
+ * The description for the module.
+ */
+ protected String description;
+
+ /**
+ * The component for the module.
+ */
+ protected Box box;
+
+ /**
+ * The text field from which to retrieve the path.
+ */
+ protected JTextField directory;
+
+ /**
+ * The datastore specific stuff.
+ */
+ protected String additionalInfo = null;
+
+ // Defined in DSModule.
+ //
+ public String getDescription() {
+ return description;
+ } // getDescription
+
+ // Defined in DSModule.
+ //
+ public Component getComponent() {
+ return box;
+ } // getComponent
+
+ // Defined in DSModule.
+ //
+ public String getPath() {
+ return directory.getText();
+ } // getPath
+
+ // Defined in DSModule.
+ //
+ public String getAdditionalInfo() {
+ return additionalInfo;
+ } // getAdditionalInfo
+
+ /**
+ * This class implements a listener for the directory text field and sets
+ * the foward enabled button when the text field is valid (non-empty).
+ */
+ protected class PathListener implements DocumentListener {
+
+ /**
+ * Empty constructor.
+ */
+ public PathListener() {
+ } // constructor
+
+ /**
+ * Called when a text update occurs in the text field.
+ * @param e the event.
+ */
+ public void insertUpdate(DocumentEvent e) {
+ Document doc = e.getDocument();
+ int length = doc.getLength();
+ if (length == 0 && getForwardEnabled()) {
+ setForwardEnabled(false);
+ } else if (length != 0 && !getForwardEnabled()) {
+ setForwardEnabled(true);
+ }
+ } // insertUpdate
+
+ /**
+ * Called when a text change occurs in the text field.
+ * @param e the event.
+ */
+ public void changedUpdate(DocumentEvent e) {
+ insertUpdate(e);
+ } // changedUpdate
+
+ /**
+ * Called when a text remove occurs in the text field.
+ * @param e the event.
+ */ public void removeUpdate(DocumentEvent e) {
+ insertUpdate(e);
+ } // insertUpdate
+
+ } // PathListener
+
+} // SUNWModule
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWbinfiles/Makefile b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWbinfiles/Makefile
new file mode 100644
index 0000000000..a02176171a
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWbinfiles/Makefile
@@ -0,0 +1,72 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+# cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWbinfiles/Makefile
+#
+
+CLASSFILES = SUNWbinfiles.class \
+ ResourceStrings.class
+
+include $(SRC)/Makefile.master
+
+CLASSPATH = $(SRC)/cmd/cmd-inet/usr.sadm/dhcpmgr
+
+JAVAFILES = $(CLASSFILES:.class=.java)
+
+MSGDIR= $(ROOT)/usr/share/lib/locale/com/sun/dhcpmgr/client/SUNWbinfiles
+MSGDIRS = $(ROOT)/usr/share/lib/locale \
+ $(ROOT)/usr/share/lib/locale/com \
+ $(ROOT)/usr/share/lib/locale/com/sun \
+ $(ROOT)/usr/share/lib/locale/com/sun/dhcpmgr/client \
+ $(MSGDIR)
+
+MSGFILES= ResourceBundle.properties
+MSGS= $(MSGFILES:%=$(MSGDIR)/%)
+
+CLEANFILES= *.class
+CLOBBERFILES=
+
+.KEEP_STATE:
+
+all: $(CLASSFILES)
+
+install: all
+
+_msg: $(MSGDIRS) $(MSGS)
+
+$(MSGDIR)/%: %
+ $(INS.file)
+
+$(MSGDIRS):
+ $(INS.dir)
+
+lint:
+
+clean:
+ -$(RM) $(CLEANFILES)
+
+clobber: clean
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWbinfiles/ResourceBundle.properties b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWbinfiles/ResourceBundle.properties
new file mode 100644
index 0000000000..dc351652a8
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWbinfiles/ResourceBundle.properties
@@ -0,0 +1,30 @@
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+description=Binary files
+explanation=Specify configuration parameters for the new binary files data store.
+path_label=Path:
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWbinfiles/ResourceStrings.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWbinfiles/ResourceStrings.java
new file mode 100644
index 0000000000..e51e8d4b84
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWbinfiles/ResourceStrings.java
@@ -0,0 +1,64 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.client.SUNWbinfiles;
+
+import java.util.*;
+
+/**
+ * This class provides a convenient method to retrieve resources for
+ * the SUNWbinfiles module.
+ */
+public class ResourceStrings {
+
+ /**
+ * The handle to the resource bundle for the module.
+ */
+ private static ResourceBundle bundle = null;
+
+ /**
+ * Return a string from the resource bundle.
+ * @param key the key to the resource bundle string.
+ * @return the resource bundle string.
+ */
+ public static String getString(String key) {
+ String msg = null;
+ try {
+ if (bundle == null) {
+ bundle = ResourceBundle.getBundle(
+ "com.sun.dhcpmgr.client.SUNWbinfiles.ResourceBundle",
+ Locale.getDefault());
+ }
+ msg = bundle.getString(key);
+ } catch (Throwable e) {
+ msg = new String(key);
+ }
+ return msg;
+
+ } // getString
+
+} // ResourceStrings
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWbinfiles/SUNWbinfiles.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWbinfiles/SUNWbinfiles.java
new file mode 100644
index 0000000000..813ad9879e
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWbinfiles/SUNWbinfiles.java
@@ -0,0 +1,84 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.client.SUNWbinfiles;
+
+import java.awt.*;
+
+import javax.swing.*;
+import javax.swing.text.*;
+import javax.swing.event.*;
+
+import com.sun.dhcpmgr.client.*;
+import com.sun.dhcpmgr.ui.*;
+
+/**
+ * This class makes the SUNWbinfiles data store manageable by the dhcpmgr.
+ */
+public class SUNWbinfiles extends SUNWModule {
+
+ private static final String DEFAULT_PATH = "/var/dhcp";
+
+ /**
+ * The constructor for the SUNWbinfiles module.
+ */
+ public SUNWbinfiles() {
+
+ // Initialize the path and description attributes.
+ //
+ path = new String(DEFAULT_PATH);
+ description = ResourceStrings.getString("description");
+
+ box = Box.createVerticalBox();
+
+ // Explanatory text.
+ //
+ JComponent c = Wizard.createTextArea(
+ ResourceStrings.getString("explanation"), 3, 45);
+ box.add(c);
+ box.add(Box.createVerticalStrut(5));
+
+ // Path entry field.
+ //
+ JPanel fieldPanel = new JPanel(new FieldLayout());
+ fieldPanel.add(FieldLayout.LABEL,
+ new JLabel(ResourceStrings.getString("path_label")));
+ directory = new JTextField(path, 20);
+ fieldPanel.add(FieldLayout.FIELD, directory);
+ box.add(fieldPanel);
+
+ // Add a listener to set forward button (or not).
+ //
+ directory.getDocument().addDocumentListener(new PathListener());
+
+ // By default forward button is enabled for this data store.
+ //
+ setForwardEnabled(true);
+
+ } // constructor
+
+} // SUNWbinfiles
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWfiles/Makefile b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWfiles/Makefile
new file mode 100644
index 0000000000..3c01a7bfb4
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWfiles/Makefile
@@ -0,0 +1,72 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+# cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWfiles/Makefile
+#
+
+CLASSFILES = SUNWfiles.class \
+ ResourceStrings.class
+
+include $(SRC)/Makefile.master
+
+CLASSPATH = $(SRC)/cmd/cmd-inet/usr.sadm/dhcpmgr
+
+JAVAFILES = $(CLASSFILES:.class=.java)
+
+MSGDIR= $(ROOT)/usr/share/lib/locale/com/sun/dhcpmgr/client/SUNWfiles
+MSGDIRS = $(ROOT)/usr/share/lib/locale \
+ $(ROOT)/usr/share/lib/locale/com \
+ $(ROOT)/usr/share/lib/locale/com/sun \
+ $(ROOT)/usr/share/lib/locale/com/sun/dhcpmgr/client \
+ $(MSGDIR)
+
+MSGFILES= ResourceBundle.properties
+MSGS= $(MSGFILES:%=$(MSGDIR)/%)
+
+CLEANFILES= *.class
+CLOBBERFILES=
+
+.KEEP_STATE:
+
+all: $(CLASSFILES)
+
+install: all
+
+_msg: $(MSGDIRS) $(MSGS)
+
+$(MSGDIR)/%: %
+ $(INS.file)
+
+$(MSGDIRS):
+ $(INS.dir)
+
+lint:
+
+clean:
+ -$(RM) $(CLEANFILES)
+
+clobber: clean
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWfiles/ResourceBundle.properties b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWfiles/ResourceBundle.properties
new file mode 100644
index 0000000000..193d0f2242
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWfiles/ResourceBundle.properties
@@ -0,0 +1,30 @@
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+description=Text files
+explanation=Specify configuration parameters for the new text files data store.
+path_label=Path:
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWfiles/ResourceStrings.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWfiles/ResourceStrings.java
new file mode 100644
index 0000000000..1e542e5eaf
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWfiles/ResourceStrings.java
@@ -0,0 +1,64 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.client.SUNWfiles;
+
+import java.util.*;
+
+/**
+ * This class provides a convenient method to retrieve resources for
+ * the SUNWfiles module.
+ */
+public class ResourceStrings {
+
+ /**
+ * The handle to the resource bundle for the module.
+ */
+ private static ResourceBundle bundle = null;
+
+ /**
+ * Return a string from the resource bundle.
+ * @param key the key to the resource bundle string.
+ * @return the resource bundle string.
+ */
+ public static String getString(String key) {
+ String msg = null;
+ try {
+ if (bundle == null) {
+ bundle = ResourceBundle.getBundle(
+ "com.sun.dhcpmgr.client.SUNWfiles.ResourceBundle",
+ Locale.getDefault());
+ }
+ msg = bundle.getString(key);
+ } catch (Throwable e) {
+ msg = new String(key);
+ }
+ return msg;
+
+ } // getString
+
+} // ResourceStrings
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWfiles/SUNWfiles.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWfiles/SUNWfiles.java
new file mode 100644
index 0000000000..f69f8523fb
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWfiles/SUNWfiles.java
@@ -0,0 +1,84 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.client.SUNWfiles;
+
+import java.awt.*;
+
+import javax.swing.*;
+import javax.swing.text.*;
+import javax.swing.event.*;
+
+import com.sun.dhcpmgr.client.*;
+import com.sun.dhcpmgr.ui.*;
+
+/**
+ * This class makes the SUNWfiles data store manageable by the dhcpmgr.
+ */
+public class SUNWfiles extends SUNWModule {
+
+ private static final String DEFAULT_PATH = "/var/dhcp";
+
+ /**
+ * The constructor for the SUNWfiles module.
+ */
+ public SUNWfiles() {
+
+ // Initialize the path and description attributes.
+ //
+ path = new String(DEFAULT_PATH);
+ description = ResourceStrings.getString("description");
+
+ box = Box.createVerticalBox();
+
+ // Explanatory text.
+ //
+ JComponent c = Wizard.createTextArea(
+ ResourceStrings.getString("explanation"), 3, 45);
+ box.add(c);
+ box.add(Box.createVerticalStrut(5));
+
+ // Path entry field.
+ //
+ JPanel fieldPanel = new JPanel(new FieldLayout());
+ fieldPanel.add(FieldLayout.LABEL,
+ new JLabel(ResourceStrings.getString("path_label")));
+ directory = new JTextField(path, 20);
+ fieldPanel.add(FieldLayout.FIELD, directory);
+ box.add(fieldPanel);
+
+ // Add a listener to set forward button (or not).
+ //
+ directory.getDocument().addDocumentListener(new PathListener());
+
+ // By default forward button is enabled for this data store.
+ //
+ setForwardEnabled(true);
+
+ } // constructor
+
+} // SUNWfiles
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWnisplus/Makefile b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWnisplus/Makefile
new file mode 100644
index 0000000000..0b50a2b79b
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWnisplus/Makefile
@@ -0,0 +1,72 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+# cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWnisplus/Makefile
+#
+
+CLASSFILES = SUNWnisplus.class \
+ ResourceStrings.class
+
+include $(SRC)/Makefile.master
+
+CLASSPATH = $(SRC)/cmd/cmd-inet/usr.sadm/dhcpmgr
+
+JAVAFILES = $(CLASSFILES:.class=.java)
+
+MSGDIR= $(ROOT)/usr/share/lib/locale/com/sun/dhcpmgr/client/SUNWnisplus
+MSGDIRS = $(ROOT)/usr/share/lib/locale \
+ $(ROOT)/usr/share/lib/locale/com \
+ $(ROOT)/usr/share/lib/locale/com/sun \
+ $(ROOT)/usr/share/lib/locale/com/sun/dhcpmgr/client \
+ $(MSGDIR)
+
+MSGFILES= ResourceBundle.properties
+MSGS= $(MSGFILES:%=$(MSGDIR)/%)
+
+CLEANFILES= *.class
+CLOBBERFILES=
+
+.KEEP_STATE:
+
+all: $(CLASSFILES)
+
+install: all
+
+_msg: $(MSGDIRS) $(MSGS)
+
+$(MSGDIR)/%: %
+ $(INS.file)
+
+$(MSGDIRS):
+ $(INS.dir)
+
+lint:
+
+clean:
+ -$(RM) $(CLEANFILES)
+
+clobber: clean
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWnisplus/ResourceBundle.properties b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWnisplus/ResourceBundle.properties
new file mode 100644
index 0000000000..4fedc706ee
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWnisplus/ResourceBundle.properties
@@ -0,0 +1,30 @@
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+description=NIS+
+explanation=Specify configuration parameters for the new NIS+ data store.
+path_label=NIS+ Domain:
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWnisplus/ResourceStrings.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWnisplus/ResourceStrings.java
new file mode 100644
index 0000000000..3e27eed21f
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWnisplus/ResourceStrings.java
@@ -0,0 +1,64 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.client.SUNWnisplus;
+
+import java.util.*;
+
+/**
+ * This class provides a convenient method to retrieve resources for
+ * the SUNWnisplus module.
+ */
+public class ResourceStrings {
+
+ /**
+ * The handle to the resource bundle for the module.
+ */
+ private static ResourceBundle bundle = null;
+
+ /**
+ * Return a string from the resource bundle.
+ * @param key the key to the resource bundle string.
+ * @return the resource bundle string.
+ */
+ public static String getString(String key) {
+ String msg = null;
+ try {
+ if (bundle == null) {
+ bundle = ResourceBundle.getBundle(
+ "com.sun.dhcpmgr.client.SUNWnisplus.ResourceBundle",
+ Locale.getDefault());
+ }
+ msg = bundle.getString(key);
+ } catch (Throwable e) {
+ msg = new String(key);
+ }
+ return msg;
+
+ } // getString
+
+} // ResourceStrings
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWnisplus/SUNWnisplus.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWnisplus/SUNWnisplus.java
new file mode 100644
index 0000000000..1fa6545243
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWnisplus/SUNWnisplus.java
@@ -0,0 +1,89 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.client.SUNWnisplus;
+
+import java.awt.*;
+
+import javax.swing.*;
+import javax.swing.text.*;
+import javax.swing.event.*;
+
+import com.sun.dhcpmgr.server.*;
+import com.sun.dhcpmgr.data.*;
+import com.sun.dhcpmgr.client.*;
+import com.sun.dhcpmgr.ui.*;
+
+/**
+ * This class makes the SUNWnisplus data store manageable by the dhcpmgr.
+ */
+public class SUNWnisplus extends SUNWModule {
+
+ /**
+ * The constructor for the SUNWnisplus module.
+ */
+ public SUNWnisplus() {
+
+ // Initialize the path and description attributes.
+ //
+ try {
+ DhcpServiceMgr svr = DataManager.get().getDhcpServiceMgr();
+ path = svr.getStringOption(StandardOptions.CD_NISPLUS_DMAIN, "");
+ } catch (Throwable e) {
+ path = new String("");
+ }
+ description = ResourceStrings.getString("description");
+
+ box = Box.createVerticalBox();
+
+ // Explanatory text.
+ //
+ JComponent c = Wizard.createTextArea(
+ ResourceStrings.getString("explanation"), 3, 45);
+ box.add(c);
+ box.add(Box.createVerticalStrut(5));
+
+ // Path entry field.
+ //
+ JPanel fieldPanel = new JPanel(new FieldLayout());
+ fieldPanel.add(FieldLayout.LABEL,
+ new JLabel(ResourceStrings.getString("path_label")));
+ directory = new JTextField(path, 20);
+ fieldPanel.add(FieldLayout.FIELD, directory);
+ box.add(fieldPanel);
+
+ // Add a listener to set forward button (or not).
+ //
+ directory.getDocument().addDocumentListener(new PathListener());
+
+ // By default forward button is enabled for this data store.
+ //
+ setForwardEnabled(true);
+
+ } // constructor
+
+} // SUNWnisplus
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SelectOptionDialog.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SelectOptionDialog.java
new file mode 100644
index 0000000000..35c5a27466
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SelectOptionDialog.java
@@ -0,0 +1,270 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.client;
+
+import javax.swing.*;
+import javax.swing.border.*;
+import javax.swing.event.*;
+import javax.swing.table.*;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.text.MessageFormat;
+import java.util.*;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeEvent;
+
+import com.sun.dhcpmgr.server.*;
+import com.sun.dhcpmgr.data.*;
+import com.sun.dhcpmgr.ui.*;
+
+/**
+ * Dialog to select an option for inclusion in a macro.
+ */
+public class SelectOptionDialog extends JComponent
+ implements ButtonPanelListener {
+ private JComboBox category;
+ private AutosizingTable optionTable;
+ private ButtonPanel buttonPanel;
+ private OptionTableModel optionTableModel;
+ private TableSorter sortedModel;
+ static final String SELECTED_OPTION = "selected_option";
+ static String value = null;
+ static JDialog dialog;
+ private OptionContext [] categories = {
+ Option.ctxts[Option.STANDARD],
+ Option.ctxts[Option.EXTEND],
+ Option.ctxts[Option.VENDOR],
+ Option.ctxts[Option.SITE]
+ };
+
+ // Model for the table displaying option descriptions
+ class OptionTableModel extends AbstractTableModel {
+ private Option [] data;
+ private ResourceBundle bundle;
+
+ public OptionTableModel() {
+ super();
+ data = new Option[0];
+ // Locate the resource bundle containing the localized descriptions
+ bundle = ResourceBundle.getBundle(
+ "com.sun.dhcpmgr.client.OptionDescriptions",
+ Locale.getDefault());
+ }
+
+ public void setCategory(OptionContext category) {
+ byte code = category.getCode();
+ if (code == Option.ctxts[Option.STANDARD].getCode()) {
+ data = StandardOptions.getAllOptions();
+ } else if (code == Option.ctxts[Option.EXTEND].getCode() ||
+ code == Option.ctxts[Option.SITE].getCode() ||
+ code == Option.ctxts[Option.VENDOR].getCode()) {
+ try {
+ // Get all locally defined options from DataManager
+ Option [] allOptions = DataManager.get().getOptions(false);
+ Vector v = new Vector();
+ // Now filter by the selected type
+ for (int i = 0; i < allOptions.length; ++i) {
+ if (allOptions[i].getContext() == code) {
+ v.addElement(allOptions[i]);
+ }
+ }
+ // Convert to an array
+ data = new Option[v.size()];
+ v.copyInto(data);
+ } catch (Exception e) {
+ data = new Option[0];
+ }
+ }
+ // Tell the sorter things changed
+ sortedModel.reallocateIndexes();
+ fireTableDataChanged();
+ }
+
+ public int getRowCount() {
+ return data.length;
+ }
+
+ public int getColumnCount() {
+ return 2;
+ }
+
+ public Object getValueAt(int row, int column) {
+ if (column == 0) {
+ return data[row].getKey();
+ } else {
+ try {
+ /**
+ * Look up descriptions in the properties file indexed by
+ * option name
+ */
+ return bundle.getString(data[row].getKey());
+ } catch (Exception e) {
+ // Ignore; we just don't have a description for this one
+ return null;
+
+ }
+ }
+ }
+
+ public Class getColumnClass(int column) {
+ return String.class;
+ }
+
+ public String getColumnName(int column) {
+ if (column == 0) {
+ return ResourceStrings.getString("option_column");
+ } else {
+ return ResourceStrings.getString("description_column");
+ }
+ }
+
+ public boolean isCellEditable(int row, int column) {
+ return false;
+ }
+ }
+
+ // Generate the dialog
+ public void createDialog() {
+ dialog = new JDialog((JFrame)null,
+ ResourceStrings.getString("select_option_title"), true);
+
+ dialog.getContentPane().setLayout(new BoxLayout(dialog.getContentPane(),
+ BoxLayout.Y_AXIS));
+
+ // Label and combo box for selecting option category
+ JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT));
+
+ Mnemonic mnCat =
+ new Mnemonic(ResourceStrings.getString("category_label"));
+ JLabel catLbl =
+ new JLabel(mnCat.getString());
+ panel.add(catLbl);
+ category = new JComboBox(categories);
+
+ catLbl.setLabelFor(category);
+ catLbl.setToolTipText(mnCat.getString());
+ catLbl.setDisplayedMnemonic(mnCat.getMnemonic());
+
+ category.setEditable(false);
+ panel.add(category);
+
+ dialog.getContentPane().add(panel);
+
+ // Table for selecting the options in the given category
+ optionTableModel = new OptionTableModel();
+ // Sort options by name, alphabetically
+ sortedModel = new TableSorter(optionTableModel);
+ sortedModel.sortByColumn(0);
+ // Use an auto-sizing table so descriptions get the space they need
+ optionTable = new AutosizingTable(sortedModel);
+ optionTable.getTableHeader().setReorderingAllowed(false);
+ optionTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+ JScrollPane scrollPane = new JScrollPane(optionTable);
+ panel = new JPanel();
+ panel.add(scrollPane);
+ dialog.getContentPane().add(panel);
+
+ // Put in usual separator and buttons
+ dialog.getContentPane().add(new JSeparator());
+ buttonPanel = new ButtonPanel(false, false);
+ buttonPanel.addButtonPanelListener(this);
+ dialog.getContentPane().add(buttonPanel);
+
+ /*
+ * As user changes category selected, update table to view category
+ * contents
+ */
+ category.addItemListener(new ItemListener() {
+ public void itemStateChanged(ItemEvent e) {
+ updateTable();
+ }
+ });
+
+ // Only enable OK when there is an option selected in the table
+ optionTable.getSelectionModel().addListSelectionListener(
+ new ListSelectionListener() {
+ public void valueChanged(ListSelectionEvent e) {
+ if (optionTable.getSelectedRow() == -1) {
+ buttonPanel.setOkEnabled(false);
+ } else {
+ buttonPanel.setOkEnabled(true);
+ }
+ }
+ });
+
+ // Ensure table displays data for initial selection
+ updateTable();
+ }
+
+ /**
+ * Update the table to the current category selection.
+ */
+ private void updateTable() {
+ optionTableModel.setCategory(categories[category.getSelectedIndex()]);
+ optionTable.clearSelection();
+ }
+
+ public void buttonPressed(int buttonId) {
+ switch (buttonId) {
+ case OK:
+ firePropertyChange(SELECTED_OPTION, null,
+ (String)optionTableModel.getValueAt(
+ sortedModel.mapRowAt(optionTable.getSelectedRow()), 0));
+ break;
+ case CANCEL:
+ firePropertyChange(SELECTED_OPTION, null, null);
+ break;
+ }
+ }
+
+ /**
+ * Here's the way to display this dialog modally and retrieve the value
+ * selected
+ * @param c a component relative to which the dialog should be displayed
+ */
+ public static String showDialog(Component c) {
+ SelectOptionDialog d = new SelectOptionDialog();
+ d.createDialog();
+ /*
+ * When user presses OK or Cancel, retrieve the value and kill the
+ * dialog
+ */
+ d.addPropertyChangeListener(new PropertyChangeListener() {
+ public void propertyChange(PropertyChangeEvent e) {
+ dialog.setVisible(false);
+ dialog.dispose();
+ value = (String)e.getNewValue();
+ }
+ });
+ dialog.setLocationRelativeTo(c);
+ dialog.pack();
+ dialog.setVisible(true);
+ return value;
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ServerOptionsDialog.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ServerOptionsDialog.java
new file mode 100644
index 0000000000..82d31b3d52
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ServerOptionsDialog.java
@@ -0,0 +1,875 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.client;
+
+import javax.swing.*;
+import javax.swing.border.*;
+import javax.swing.event.*;
+import javax.swing.table.*;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.text.*;
+import java.util.*;
+
+import com.sun.dhcpmgr.server.*;
+import com.sun.dhcpmgr.data.*;
+import com.sun.dhcpmgr.ui.*;
+import com.sun.dhcpmgr.bridge.BridgeException;
+
+/**
+ * Dialog to edit the options for the server as stored in the startup script.
+ */
+public class ServerOptionsDialog extends DhcpmgrDialog {
+ private static int DEFAULT_RESCAN_INTERVAL = 60;
+ private DhcpdOptions options, originalOptions;
+ private JCheckBox verboseLogging, detectDuplicates, restartServer,
+ logTransactions, reloadEnabled, owneripEnabled, dnsUpdateEnabled;
+ private IntegerField relayHops, reloadInterval, cacheTime, dnsTimeout;
+ private JRadioButton noBootp, autoBootp, manualBootp;
+ private IPAddressList serverList;
+ private IPAddressList owneripList;
+ private JComboBox logFacility;
+ private JTable monitoredTable, ignoredTable;
+ private LeftButton leftButton;
+ private RightButton rightButton;
+
+ /*
+ * Model for the tables which are used to edit the lists of
+ * interfaces which are monitored and ignored.
+ */
+ class InterfaceTableModel extends AbstractTableModel {
+ private Vector interfaces;
+
+ public InterfaceTableModel() {
+ interfaces = new Vector();
+ }
+
+ // Initialize the list of interfaces
+ public void setInterfaceList(IPInterface [] ifs) {
+ interfaces.removeAllElements();
+ if (ifs != null) {
+ for (int i = 0; i < ifs.length; ++i) {
+ interfaces.addElement(ifs[i]);
+ }
+ }
+ fireTableDataChanged();
+ }
+
+ // Retrieve the interfacess as a comma-separated list
+ public String getInterfaceList() {
+ StringBuffer b = new StringBuffer();
+ Enumeration e = interfaces.elements();
+ while (e.hasMoreElements()) {
+ if (b.length() != 0) {
+ b.append(',');
+ }
+ IPInterface ipif = (IPInterface)e.nextElement();
+ b.append(ipif.getName());
+ }
+ return b.toString();
+ }
+
+ // Retrieve interface object for named interface
+ public IPInterface getInterface(String name) {
+ Enumeration e = interfaces.elements();
+ while (e.hasMoreElements()) {
+ IPInterface ipif = (IPInterface)e.nextElement();
+ if (name.equals(ipif.getName())) {
+ return ipif;
+ }
+ }
+ return null;
+ }
+
+ // Retrieve the interface object at a particular row in the table
+ public IPInterface getInterfaceAt(int row) {
+ return (IPInterface)interfaces.elementAt(row);
+ }
+
+ // Add an interface to the table
+ public void addInterface(IPInterface ipif) {
+ interfaces.addElement(ipif);
+ fireTableDataChanged();
+ }
+
+ // Delete an interface from the table
+ public void deleteInterface(IPInterface ipif) {
+ interfaces.removeElement(ipif);
+ fireTableDataChanged();
+ }
+
+ // Return number of rows
+ public int getRowCount() {
+ return interfaces.size();
+ }
+
+ // Always two columns: interface name and network
+ public int getColumnCount() {
+ return 2;
+ }
+
+ // Return cell value at a particular coordinate
+ public Object getValueAt(int row, int column) {
+ IPInterface ipif = (IPInterface)interfaces.elementAt(row);
+ if (column == 0) {
+ return ipif.getName();
+ } else {
+ return ipif.getNetwork().toString();
+ }
+ }
+
+ // All data is strings from the display's point of view
+ public Class getColumnClass(int column) {
+ return String.class;
+ }
+
+ // Get headings for each column
+ public String getColumnName(int column) {
+ if (column == 0) {
+ return ResourceStrings.getString("service_options_interface");
+ } else {
+ return ResourceStrings.getString("service_options_network");
+ }
+ }
+ }
+
+ public ServerOptionsDialog(Frame f, DhcpdOptions opts) {
+ super(f, true); // We want a reset button
+ setOptions(opts);
+ resetValues();
+ }
+
+ /**
+ * Provide a title to be displayed for the dialog
+ */
+ public String getTitle() {
+ return ResourceStrings.getString("service_options_title");
+ }
+
+ /**
+ * Construct and return the main display for this dialog.
+ */
+ protected JPanel getMainPanel() {
+
+ JPanel mainPanel = new JPanel(new BorderLayout());
+
+ /*
+ * Start with a tabbed view; the top tab is the options for the
+ * daemon, the lower tab is the interfaces to be monitored.
+ */
+ JTabbedPane tabbedPane = new JTabbedPane();
+ JPanel optionsPanel = new JPanel();
+ optionsPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
+
+ // Use a gridbag with equal weights all around, cells anchored to west
+ GridBagLayout bag = new GridBagLayout();
+ GridBagConstraints con = new GridBagConstraints();
+ con.gridx = con.gridy = 0;
+ con.weightx = con.weighty = 1.0;
+ con.anchor = GridBagConstraints.WEST;
+ con.insets = new Insets(2, 2, 2, 2);
+
+ optionsPanel.setLayout(bag);
+
+ // Add control for number of hops allowed
+ Box box = Box.createHorizontalBox();
+
+ Mnemonic mnHops =
+ new Mnemonic(ResourceStrings.getString("service_options_hops"));
+ JLabel label = new JLabel(mnHops.getString());
+ label.setToolTipText(mnHops.getString());
+ label.setDisplayedMnemonic(mnHops.getMnemonic());
+
+ label.setForeground(Color.black);
+ box.add(label);
+ box.add(Box.createHorizontalStrut(5));
+ relayHops = new IntegerField();
+ label.setLabelFor(relayHops);
+ box.add(relayHops);
+ bag.setConstraints(box, con);
+ optionsPanel.add(box);
+
+ // Add control for verbose logging
+ verboseLogging = new JCheckBox(
+ ResourceStrings.getString("service_options_verbose"), false);
+ verboseLogging.setToolTipText(
+ ResourceStrings.getString("service_options_verbose"));
+ ++con.gridy;
+ bag.setConstraints(verboseLogging, con);
+ optionsPanel.add(verboseLogging);
+
+ // Add control for transaction logging on/off and facility to use
+ box = Box.createHorizontalBox();
+ logTransactions = new JCheckBox(
+ ResourceStrings.getString("service_options_log_transactions"),
+ false);
+ logTransactions.setToolTipText(
+ ResourceStrings.getString("service_options_log_transactions"));
+ logTransactions.setAlignmentY((float)0.5);
+ box.add(logTransactions);
+ box.add(Box.createHorizontalStrut(5));
+ logFacility = new JComboBox(DhcpdOptions.getLoggingFacilities());
+ logFacility.setAlignmentY((float)0.5);
+ box.add(logFacility);
+ ++con.gridy;
+ bag.setConstraints(box, con);
+ optionsPanel.add(box);
+ // Enable logging facility choices only when logging is turned on
+ logTransactions.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ logFacility.setEnabled(logTransactions.isSelected());
+ }
+ });
+
+ /*
+ * The main tab has two different displays depending on whether it's
+ * a relay or a full-fledged server.
+ */
+ if (!DhcpmgrApplet.modeIsRelay) {
+ // Add control for duplicate detection using ICMP
+ detectDuplicates = new JCheckBox(
+ ResourceStrings.getString("service_options_detect_duplicates"),
+ true);
+ detectDuplicates.setToolTipText(
+ ResourceStrings.getString("service_options_detect_duplicates"));
+ ++con.gridy;
+ bag.setConstraints(detectDuplicates, con);
+ optionsPanel.add(detectDuplicates);
+
+ // Add control for automatic reload of dhcptab and period
+ box = Box.createHorizontalBox();
+ reloadEnabled = new JCheckBox(
+ ResourceStrings.getString("service_options_reload_dhcptab"));
+ reloadEnabled.setToolTipText(ResourceStrings.getString(
+ "service_options_reload_dhcptab"));
+ reloadEnabled.setAlignmentY((float)0.5);
+ box.add(reloadEnabled);
+ box.add(Box.createHorizontalStrut(5));
+ reloadInterval = new IntegerField();
+ reloadInterval.setAlignmentY((float)0.5);
+ box.add(reloadInterval);
+ box.add(Box.createHorizontalStrut(5));
+
+ Mnemonic mnMins =
+ new Mnemonic(ResourceStrings.getString(
+ "service_options_reload_minutes"));
+ label = new JLabel(mnMins.getString());
+ label.setLabelFor(reloadInterval);
+ label.setToolTipText(mnMins.getString());
+ label.setDisplayedMnemonic(mnMins.getMnemonic());
+
+ label.setForeground(Color.black);
+ label.setAlignmentY((float)0.5);
+ box.add(label);
+ ++con.gridy;
+ bag.setConstraints(box, con);
+ optionsPanel.add(box);
+
+ // Add control for DNS dynamic update and timeout value
+ box = Box.createHorizontalBox();
+ dnsUpdateEnabled = new JCheckBox(
+ ResourceStrings.getString("service_options_update_dns"));
+ dnsUpdateEnabled.setToolTipText(
+ ResourceStrings.getString("service_options_update_dns"));
+ dnsUpdateEnabled.setAlignmentY((float)0.5);
+ box.add(dnsUpdateEnabled);
+ box.add(Box.createHorizontalStrut(5));
+ ++con.gridy;
+ bag.setConstraints(box, con);
+ optionsPanel.add(box);
+
+ box = Box.createHorizontalBox();
+ dnsTimeout = new IntegerField();
+ dnsTimeout.setAlignmentY((float)0.10);
+ box.add(Box.createHorizontalStrut(25));
+
+ Mnemonic mnDNS =
+ new Mnemonic(ResourceStrings.getString(
+ "service_options_timeout_dns"));
+ label = new JLabel(mnDNS.getString());
+ label.setLabelFor(dnsTimeout);
+ label.setToolTipText(mnDNS.getString());
+ label.setDisplayedMnemonic(mnDNS.getMnemonic());
+
+ label.setForeground(Color.black);
+ label.setAlignmentY((float)0.5);
+ box.add(label);
+ box.add(Box.createHorizontalStrut(5));
+ box.add(dnsTimeout);
+ box.add(Box.createHorizontalStrut(5));
+ label = new JLabel(
+ ResourceStrings.getString("service_options_seconds"));
+ label.setLabelFor(box);
+ label.setToolTipText(ResourceStrings.getString(
+ "service_options_seconds"));
+ label.setForeground(Color.black);
+ label.setAlignmentY((float)0.5);
+ box.add(label);
+ ++con.gridy;
+ bag.setConstraints(box, con);
+ optionsPanel.add(box);
+
+ // Add control for length of time to cache offers
+ box = Box.createHorizontalBox();
+
+ Mnemonic mnCache =
+ new Mnemonic(ResourceStrings.getString(
+ "service_options_cache"));
+ label = new JLabel(mnCache.getString());
+ label.setToolTipText(mnCache.getString());
+ label.setDisplayedMnemonic(mnCache.getMnemonic());
+
+ label.setForeground(Color.black);
+ box.add(label);
+ box.add(Box.createHorizontalStrut(5));
+ cacheTime = new IntegerField();
+ label.setLabelFor(cacheTime);
+ box.add(cacheTime);
+ box.add(Box.createHorizontalStrut(5));
+ label = new JLabel(
+ ResourceStrings.getString("service_options_seconds"));
+ label.setLabelFor(box);
+ label.setToolTipText(ResourceStrings.getString(
+ "service_options_seconds"));
+ label.setForeground(Color.black);
+ box.add(label);
+ ++con.gridy;
+ bag.setConstraints(box, con);
+ optionsPanel.add(box);
+
+ // Add choices for BOOTP compatibility behavior: none, auto, manual
+ JPanel panel = new JPanel();
+ panel.setLayout(new GridLayout(3, 1));
+ Border b = BorderFactory.createTitledBorder(
+ BorderFactory.createLineBorder(Color.black),
+ ResourceStrings.getString("service_options_bootp_compat"));
+ panel.setBorder(BorderFactory.createCompoundBorder(b,
+ BorderFactory.createEmptyBorder(5, 5, 5, 5)));
+
+ ButtonGroup bootpCompat = new ButtonGroup();
+
+ noBootp = new JRadioButton(
+ ResourceStrings.getString("service_options_bootp_none"), true);
+ noBootp.setToolTipText(
+ ResourceStrings.getString("service_options_bootp_none"));
+ bootpCompat.add(noBootp);
+ panel.add(noBootp);
+
+ autoBootp = new JRadioButton(
+ ResourceStrings.getString("service_options_bootp_auto"), false);
+ autoBootp.setToolTipText(
+ ResourceStrings.getString("service_options_bootp_auto"));
+ bootpCompat.add(autoBootp);
+ panel.add(autoBootp);
+
+ manualBootp = new JRadioButton(
+ ResourceStrings.getString("service_options_bootp_manual"),
+ false);
+ manualBootp.setToolTipText(
+ ResourceStrings.getString("service_options_bootp_manual"));
+ bootpCompat.add(manualBootp);
+ panel.add(manualBootp);
+
+ ++con.gridy;
+ con.fill = GridBagConstraints.HORIZONTAL;
+ bag.setConstraints(panel, con);
+ optionsPanel.add(panel);
+
+ // Enable reload interval only when reload option is checked
+ reloadEnabled.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ reloadInterval.setEnabled(reloadEnabled.isSelected());
+ }
+ });
+ // Enable DNS timeout only when DNS update option is checked
+ dnsUpdateEnabled.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ dnsTimeout.setEnabled(dnsUpdateEnabled.isSelected());
+ }
+ });
+ } else {
+ /*
+ * In relay mode the only other thing we can control is list of
+ * servers which we forward requests to.
+ */
+ serverList = new IPAddressList();
+ Border tb = BorderFactory.createTitledBorder(
+ BorderFactory.createLineBorder(Color.black),
+ ResourceStrings.getString("dhcp_servers"));
+ serverList.setBorder(BorderFactory.createCompoundBorder(tb,
+ BorderFactory.createEmptyBorder(5, 5, 5, 5)));
+ ++con.gridy;
+ bag.setConstraints(serverList, con);
+ optionsPanel.add(serverList);
+ }
+
+ tabbedPane.addTab(ResourceStrings.getString("service_options_options"),
+ optionsPanel);
+
+ // Panel for interfaces
+ monitoredTable = new JTable(new InterfaceTableModel());
+ ignoredTable = new JTable(new InterfaceTableModel());
+ monitoredTable.setSelectionMode(
+ ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
+ ignoredTable.setSelectionMode(
+ ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
+
+ Box interfaceBox = Box.createHorizontalBox();
+
+ // The list of interfaces we're monitoring goes on the left
+ JPanel panel = new JPanel(new BorderLayout(5, 5));
+
+ Mnemonic mnMon =
+ new Mnemonic(ResourceStrings.getString(
+ "service_options_monitored"));
+ JLabel servOptsLbl = new JLabel(mnMon.getString());
+ servOptsLbl.setLabelFor(monitoredTable);
+ servOptsLbl.setToolTipText(mnMon.getString());
+ servOptsLbl.setDisplayedMnemonic(mnMon.getMnemonic());
+
+ panel.add(servOptsLbl, BorderLayout.NORTH);
+
+ JScrollPane scrollPane = new JScrollPane(monitoredTable);
+ Dimension d = monitoredTable.getPreferredScrollableViewportSize();
+ d.height = 100;
+ d.width = 210;
+ monitoredTable.setPreferredScrollableViewportSize(d);
+ panel.add(scrollPane, BorderLayout.CENTER);
+ interfaceBox.add(panel);
+ interfaceBox.add(Box.createHorizontalStrut(10));
+
+ // The buttons to move items between the lists go in the middle
+ panel = new JPanel(new VerticalButtonLayout());
+ leftButton = new LeftButton();
+ rightButton = new RightButton();
+ rightButton.setEnabled(false);
+ leftButton.setEnabled(false);
+ panel.add(rightButton);
+ panel.add(leftButton);
+ interfaceBox.add(panel);
+ interfaceBox.add(Box.createHorizontalStrut(10));
+
+ // The list of interfaces to ignore is on the right
+ panel = new JPanel(new BorderLayout(5, 5));
+
+ Mnemonic mnIg =
+ new Mnemonic(ResourceStrings.getString("service_options_ignored"));
+ JLabel optsIgnLbl = new JLabel(mnIg.getString());
+ optsIgnLbl.setLabelFor(ignoredTable);
+ optsIgnLbl.setToolTipText(mnIg.getString());
+ optsIgnLbl.setDisplayedMnemonic(mnIg.getMnemonic());
+
+ panel.add(optsIgnLbl, BorderLayout.NORTH);
+
+ scrollPane = new JScrollPane(ignoredTable);
+ d = ignoredTable.getPreferredScrollableViewportSize();
+ d.height = 100;
+ d.width = 210;
+ ignoredTable.setPreferredScrollableViewportSize(d);
+ panel.add(scrollPane, BorderLayout.CENTER);
+ interfaceBox.add(panel);
+
+ // Now create the tab for the interface manipulation
+ panel = new JPanel(new BorderLayout());
+ panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
+ panel.add(interfaceBox, BorderLayout.CENTER);
+ tabbedPane.addTab(
+ ResourceStrings.getString("service_options_interfaces"), panel);
+
+ // tab for Addresses
+ if (!DhcpmgrApplet.modeIsRelay) {
+ JPanel addrsPanel = new JPanel(new BorderLayout());
+ addrsPanel.setBorder(
+ BorderFactory.createEmptyBorder(10, 10, 10, 10));
+
+ // Add control for DHCP OWNER_IP option and values
+ box = Box.createHorizontalBox();
+ owneripEnabled = new JCheckBox(
+ ResourceStrings.getString("service_options_owner_ip"));
+ owneripEnabled.setToolTipText(
+ ResourceStrings.getString("service_options_owner_ip"));
+ owneripEnabled.setAlignmentY((float)0.5);
+ box.add(owneripEnabled);
+ box.add(Box.createHorizontalStrut(5));
+ addrsPanel.add(box, BorderLayout.NORTH);
+
+ owneripList = new IPAddressList();
+ Border tb = BorderFactory.createTitledBorder(
+ BorderFactory.createLineBorder(Color.black),
+ ResourceStrings.getString(
+ "service_options_owner_ip_addresses"));
+ owneripList.setBorder(BorderFactory.createCompoundBorder(tb,
+ BorderFactory.createEmptyBorder(5, 5, 5, 5)));
+ addrsPanel.add(owneripList, BorderLayout.CENTER);
+ // Enable OWNER_IP Addresses only when owner_ip option is checked
+ owneripEnabled.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ owneripList.setEnabled(owneripEnabled.isSelected());
+ }
+ });
+ // now add the tab for Addresses
+ tabbedPane.addTab(
+ ResourceStrings.getString("service_options_addresses"),
+ addrsPanel);
+ }
+
+ JPanel borderPanel = new JPanel(new BorderLayout());
+ borderPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
+ borderPanel.add(tabbedPane, BorderLayout.CENTER);
+
+ mainPanel.add(borderPanel, BorderLayout.NORTH);
+
+ /*
+ * Allow them to specify server should be restarted when these changes
+ * are applied
+ */
+ restartServer = new JCheckBox(
+ ResourceStrings.getString("service_options_restart"));
+ restartServer.setToolTipText(
+ ResourceStrings.getString("service_options_restart"));
+ panel = new JPanel(new FlowLayout(FlowLayout.LEFT));
+ panel.add(restartServer);
+ mainPanel.add(panel, BorderLayout.CENTER);
+
+ buttonPanel.setOkEnabled(true);
+
+ // Handle enable and disable of buttons based on selection state
+ monitoredTable.getSelectionModel().addListSelectionListener(
+ new ListSelectionListener() {
+ public void valueChanged(ListSelectionEvent e) {
+ if (monitoredTable.getSelectedRowCount() != 0 &&
+ monitoredTable.getRowCount() > 0) {
+ rightButton.setEnabled(true);
+ ignoredTable.getSelectionModel().clearSelection();
+ } else {
+ rightButton.setEnabled(false);
+ }
+ }
+ });
+
+ ignoredTable.getSelectionModel().addListSelectionListener(
+ new ListSelectionListener() {
+ public void valueChanged(ListSelectionEvent e) {
+ if (ignoredTable.getSelectedRowCount() != 0 &&
+ ignoredTable.getRowCount() > 0) {
+ leftButton.setEnabled(true);
+ monitoredTable.getSelectionModel().clearSelection();
+ } else {
+ leftButton.setEnabled(false);
+ }
+ }
+ });
+
+ // Handle button presses
+ rightButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ int [] rows = monitoredTable.getSelectedRows();
+ if (rows == null) {
+ return;
+ }
+ InterfaceTableModel monitoredModel =
+ (InterfaceTableModel)monitoredTable.getModel();
+ InterfaceTableModel ignoredModel =
+ (InterfaceTableModel)ignoredTable.getModel();
+ /*
+ * Now do the adds, then the removes; otherwise the row numbers
+ * we just got might be wrong
+ */
+ Vector removals = new Vector();
+ for (int i = 0; i < rows.length; ++i) {
+ IPInterface ipif = monitoredModel.getInterfaceAt(rows[i]);
+ ignoredModel.addInterface(ipif);
+ removals.addElement(ipif);
+ }
+ Enumeration en = removals.elements();
+ while (en.hasMoreElements()) {
+ monitoredModel.deleteInterface(
+ (IPInterface)en.nextElement());
+ }
+ /*
+ * Clear the selection; this prevents exceptions from selection
+ * pointing at rows that are gone
+ */
+ monitoredTable.getSelectionModel().clearSelection();
+ }
+ });
+
+ leftButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ int [] rows = ignoredTable.getSelectedRows();
+ if (rows == null) {
+ return;
+ }
+ InterfaceTableModel monitoredModel =
+ (InterfaceTableModel)monitoredTable.getModel();
+ InterfaceTableModel ignoredModel =
+ (InterfaceTableModel)ignoredTable.getModel();
+ /*
+ * Now do the adds, then the removes; otherwise the row numbers
+ * we just got might be wrong
+ */
+ Vector removals = new Vector();
+ for (int i = 0; i < rows.length; ++i) {
+ IPInterface ipif = ignoredModel.getInterfaceAt(rows[i]);
+ monitoredModel.addInterface(ipif);
+ removals.addElement(ipif);
+ }
+ Enumeration en = removals.elements();
+ while (en.hasMoreElements()) {
+ ignoredModel.deleteInterface((IPInterface)en.nextElement());
+ }
+ /*
+ * Clear the selection; this prevents exceptions from selection
+ * pointing at rows that are gone
+ */
+ ignoredTable.getSelectionModel().clearSelection();
+ }
+ });
+
+
+ return mainPanel;
+ }
+
+ // Save a copy of the option settings so reset can work
+ private void setOptions(DhcpdOptions o) {
+ originalOptions = (DhcpdOptions)o.clone();
+ options = o;
+ }
+
+ // Reset all controls to initial values
+ private void resetValues() {
+ // Main tab parameters; first verbose logging
+ verboseLogging.setSelected(options.isVerbose());
+ // Relay hops
+ if (options.isRelayHops()) {
+ relayHops.setValue(options.getRelayHops());
+ } else {
+ relayHops.setValue(DhcpdOptions.DSVC_CV_HOPS);
+ }
+ // Set logging controls
+ logTransactions.setSelected(options.isLogging());
+ logFacility.setEnabled(options.isLogging());
+ if (options.isLogging()) {
+ logFacility.setSelectedItem(options.getLogging());
+ } else {
+ logFacility.setSelectedIndex(0);
+ }
+
+ if (!DhcpmgrApplet.modeIsRelay) {
+ // Set bootp compat. controls
+ noBootp.setSelected(!options.isBootpCompatible());
+ if (options.isBootpCompatible()) {
+ autoBootp.setSelected(options.isBootpAutomatic());
+ manualBootp.setSelected(!options.isBootpAutomatic());
+ }
+ detectDuplicates.setSelected(options.isICMPVerify());
+ reloadEnabled.setSelected(options.isRescan());
+ reloadInterval.setEnabled(options.isRescan());
+ owneripEnabled.setSelected(options.isOwnerip());
+ owneripList.setEnabled(options.isOwnerip());
+ dnsUpdateEnabled.setSelected(options.isDnsUpdated());
+ dnsTimeout.setEnabled(options.isDnsUpdated());
+
+ // Set rescan interval to default if it's not specified
+ if (options.isRescan()) {
+ reloadInterval.setValue(options.getRescan());
+ } else {
+ reloadInterval.setValue(DEFAULT_RESCAN_INTERVAL);
+ }
+
+ // Set owner_ip to default if it's not specified
+ if (options.isOwnerip()) {
+ owneripList.setAddressList(options.getOwnerip());
+ }
+ // Set DNS timeout to default if it's not specified
+ if (options.isDnsUpdated()) {
+ dnsTimeout.setValue(options.getDnsTimeout());
+ } else {
+ dnsTimeout.setValue(DhcpdOptions.DSVC_CV_NSU_TO);
+ }
+ if (options.isOfferTtl()) {
+ cacheTime.setValue(options.getOfferTtl());
+ } else {
+ cacheTime.setValue(DhcpdOptions.DSVC_CV_OFFER_TTL);
+ }
+ } else {
+ // In relay case only the server list is available
+ serverList.setAddressList(options.getRelay());
+ }
+
+ // Interfaces tab
+ try {
+ IPInterface[] interfaces = new IPInterface[0];
+ try {
+ interfaces =
+ DataManager.get().getDhcpServiceMgr().getInterfaces();
+ } catch (BridgeException e) {
+ // we're not configured yet, apparently
+ interfaces = null;
+ }
+ InterfaceTableModel monitoredModel =
+ (InterfaceTableModel)monitoredTable.getModel();
+ InterfaceTableModel ignoredModel =
+ (InterfaceTableModel)ignoredTable.getModel();
+ if (options.isInterfaces()) {
+ ignoredModel.setInterfaceList(interfaces);
+ monitoredModel.setInterfaceList(null);
+ StringTokenizer st =
+ new StringTokenizer(options.getInterfaces(), ",");
+ while (st.hasMoreTokens()) {
+ IPInterface ipif =
+ ignoredModel.getInterface(st.nextToken());
+ if (ipif != null) {
+ monitoredModel.addInterface(ipif);
+ ignoredModel.deleteInterface(ipif);
+ }
+ }
+ } else {
+ monitoredModel.setInterfaceList(interfaces);
+ ignoredModel.setInterfaceList(null);
+ }
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+
+ // Default to restarting server
+ restartServer.setSelected(true);
+ }
+
+ /**
+ * User pressed OK, do what we think is necessary
+ */
+ protected void doOk() {
+ try {
+ options.setVerbose(verboseLogging.isSelected());
+ if (relayHops.getValue() != DhcpdOptions.DSVC_CV_HOPS) {
+ options.setRelayHops(true, new Integer(relayHops.getValue()));
+ } else {
+ options.setRelayHops(false, null);
+ }
+ options.setLogging(logTransactions.isSelected(),
+ (Integer)logFacility.getSelectedItem());
+
+ if (!DhcpmgrApplet.modeIsRelay) {
+ options.setBootpCompatible(!noBootp.isSelected(),
+ autoBootp.isSelected());
+ options.setICMPVerify(detectDuplicates.isSelected());
+ if (reloadEnabled.isSelected() &&
+ reloadInterval.getValue() != 0) {
+ options.setRescan(true,
+ new Integer(reloadInterval.getValue()));
+ } else {
+ options.setRescan(false, null);
+ }
+
+ if (owneripEnabled.isSelected()) {
+ options.setOwnerip(true,
+ owneripList.getAddressListString());
+ } else {
+ options.setOwnerip(false, null);
+ }
+
+ if (dnsUpdateEnabled.isSelected()) {
+ options.setDnsTimeout(true,
+ new Integer(dnsTimeout.getValue()));
+ } else {
+ options.setDnsTimeout(false, null);
+ }
+ if (cacheTime.getValue() != DhcpdOptions.DSVC_CV_OFFER_TTL) {
+ options.setOfferTtl(true,
+ new Integer(cacheTime.getValue()));
+ } else {
+ options.setOfferTtl(false, null);
+ }
+ } else {
+ options.setRelay(true, serverList.getAddressListString());
+ }
+ if (monitoredTable.getRowCount() == 0) {
+ // XXX Need to disable OK when this is the case
+ return;
+ }
+ if (ignoredTable.getRowCount() != 0) {
+ /*
+ * If nothing is ignored then let server default to all
+ * interfaces
+ */
+ options.setInterfaces(true, ((InterfaceTableModel)
+ monitoredTable.getModel()).getInterfaceList());
+ } else {
+ options.setInterfaces(false, null);
+ }
+ DataManager.get().getDhcpServiceMgr().writeDefaults(options);
+ if (restartServer.isSelected()) {
+ DataManager.get().getDhcpServiceMgr().shutdown();
+ // Wait 5 secs for server to try to shutdown
+ Thread.sleep(5000);
+ DataManager.get().getDhcpServiceMgr().startup();
+ }
+ fireActionPerformed();
+ setVisible(false);
+ dispose();
+ } catch (Exception e) {
+ MessageFormat form = null;
+ Object [] args = new Object[2];
+ form = new MessageFormat(
+ ResourceStrings.getString("service_options_error"));
+ args[0] = e.getMessage();
+ JOptionPane.showMessageDialog(this, form.format(args),
+ ResourceStrings.getString("server_error_title"),
+ JOptionPane.ERROR_MESSAGE);
+ }
+ }
+
+ /**
+ * Return help system lookup key
+ */
+ protected String getHelpKey() {
+ if (DhcpmgrApplet.modeIsRelay) {
+ return "modify_relay";
+ } else {
+ return "modify_server";
+ }
+ }
+
+ /**
+ * User pressed reset; go back to starting value
+ */
+ protected void doReset() {
+ setOptions(originalOptions);
+ resetValues();
+ }
+
+ /**
+ * Notify our invoker that we're done
+ */
+ protected void fireActionPerformed() {
+ fireActionPerformed(this, DialogActions.OK);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/UnconfigureDialog.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/UnconfigureDialog.java
new file mode 100644
index 0000000000..09567d7fdd
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/UnconfigureDialog.java
@@ -0,0 +1,282 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 1998-2002 by Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.client;
+
+import javax.swing.*;
+import javax.swing.border.*;
+import javax.swing.event.*;
+import javax.swing.table.*;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.text.*;
+import java.util.*;
+import java.net.*;
+
+import com.sun.dhcpmgr.bridge.*;
+import com.sun.dhcpmgr.server.*;
+import com.sun.dhcpmgr.data.*;
+import com.sun.dhcpmgr.ui.*;
+
+/**
+ * A dialog to confirm the user's request to unconfigure the service.
+ */
+public class UnconfigureDialog extends MultipleOperationDialog {
+ private JCheckBox deleteTables, deleteHosts;
+ private int networkCount = 0;
+ private Network [] nets = new Network[0];
+
+ public UnconfigureDialog(Frame f) {
+ // No reset button for us
+ super(f, false);
+ }
+
+ public String getTitle() {
+ return ResourceStrings.getString("unconfigure_title");
+ }
+
+ protected JPanel getMainPanel() {
+
+ JPanel mainPanel = new JPanel();
+ mainPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
+
+ Box box = Box.createVerticalBox();
+
+ if (!DhcpmgrApplet.modeIsRelay) {
+ JComponent c = Wizard.createTextArea(
+ ResourceStrings.getString("unconfigure_dhcp"), 4, 30);
+ c.setAlignmentX(Component.LEFT_ALIGNMENT);
+ box.add(c);
+ box.add(Box.createVerticalStrut(10));
+ deleteTables = new JCheckBox(
+ ResourceStrings.getString("unconfigure_delete_tables"), false);
+ deleteTables.setToolTipText(
+ ResourceStrings.getString("unconfigure_delete_tables"));
+ deleteTables.setAlignmentX(Component.LEFT_ALIGNMENT);
+ box.add(deleteTables);
+ box.add(Box.createVerticalStrut(10));
+ deleteHosts = new JCheckBox(
+ ResourceStrings.getString("unconfigure_delete_hosts"), false);
+ deleteHosts.setToolTipText(
+ ResourceStrings.getString("unconfigure_delete_hosts"));
+ deleteHosts.setAlignmentX(Component.LEFT_ALIGNMENT);
+ deleteHosts.setEnabled(false);
+ box.add(deleteHosts);
+ deleteTables.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+
+ if (!deleteTables.isSelected()) {
+ deleteHosts.setEnabled(false);
+ return;
+ }
+
+ // If the host resource is set in the configuration
+ // file (or we can't tell), then enable the deleteHosts
+ // checkbox.
+ try {
+ DhcpServiceMgr serviceMgr =
+ DataManager.get().getDhcpServiceMgr();
+ DhcpdOptions opts = serviceMgr.readDefaults();
+ if (opts.getHostsResource() != null) {
+ deleteHosts.setEnabled(true);
+ }
+ } catch (BridgeException ex) {
+ // Assume set
+ deleteHosts.setEnabled(true);
+ }
+ }
+ });
+ } else {
+ JComponent c = Wizard.createTextArea(
+ ResourceStrings.getString("unconfigure_bootp"), 4, 30);
+ c.setAlignmentX(Component.LEFT_ALIGNMENT);
+ box.add(c);
+ }
+ box.add(Box.createVerticalStrut(10));
+ JComponent c = Wizard.createTextArea(
+ ResourceStrings.getString("unconfigure_shutdown"), 2, 30);
+ c.setAlignmentX(Component.LEFT_ALIGNMENT);
+ box.add(c);
+ mainPanel.add(box);
+ buttonPanel.setOkEnabled(true);
+ return mainPanel;
+ }
+
+ protected String getProgressMessage() {
+ return ResourceStrings.getString("unconfigure_progress");
+ }
+
+ protected int getProgressLength() {
+ // Initialize to number of ops required even if only a relay
+ int length = 2;
+ if (!DhcpmgrApplet.modeIsRelay) {
+ // Add one for deleting defaults file, and one for deleting macro
+ length += 2;
+ if (deleteTables.isSelected()) {
+ try {
+ nets = DataManager.get().getNetworks(false);
+ } catch (Throwable t) {
+ // Ignore
+ }
+ length += nets.length + 1; // Add one for dhcptab
+ }
+ }
+ return length;
+ }
+
+ protected String getErrorHeading() {
+ return ResourceStrings.getString("unconfigure_error_heading");
+ }
+
+ protected Thread getOperationThread() {
+ return new Thread() {
+ public void run() {
+ int checkpoint = 0;
+ DhcpServiceMgr serviceMgr =
+ DataManager.get().getDhcpServiceMgr();
+ // Shut down the server
+ try {
+ serviceMgr.shutdown();
+ } catch (Throwable e) {
+ addError(
+ ResourceStrings.getString("unconfigure_error_shutdown"),
+ e.getMessage());
+ }
+ try {
+ updateProgress(++checkpoint, ResourceStrings.getString(
+ "unconfigure_server_shutdown"));
+ } catch (InterruptedException e) {
+ closeDialog();
+ return;
+ }
+
+ // If this was a relay we're done
+ if (!DhcpmgrApplet.modeIsRelay) {
+ // Remove the server macro
+ try {
+ DataManager.get().getDhcptabMgr().deleteRecord(
+ new Macro(DataManager.get().getShortServerName()),
+ false);
+ } catch (Throwable e) {
+ addError(ResourceStrings.getString(
+ "unconfigure_error_macro"),
+ e.getMessage());
+ }
+ try {
+ updateProgress(++checkpoint, ResourceStrings.getString(
+ "unconfigure_macro_deleted"));
+ } catch (InterruptedException e) {
+ closeDialog();
+ return;
+ }
+
+ // Delete all the network tables and the dhcptab
+ if (deleteTables.isSelected()) {
+ if (nets != null && nets.length != 0) {
+ MessageFormat errForm = new MessageFormat(
+ ResourceStrings.getString(
+ "unconfigure_error_network"));
+ MessageFormat progForm = new MessageFormat(
+ ResourceStrings.getString(
+ "unconfigure_network_progress"));
+ Object [] args = new Object[1];
+ for (int i = 0; i < nets.length; ++i) {
+ String netString = nets[i].toString();
+ args[0] = netString;
+ try {
+ DataManager.get().getDhcpNetMgr().
+ deleteNetwork(netString, true,
+ deleteHosts.isSelected());
+ } catch (Throwable e) {
+ addError(errForm.format(args),
+ e.getMessage());
+ }
+ try {
+ updateProgress(++checkpoint,
+ progForm.format(args));
+ } catch (InterruptedException e) {
+ closeDialog();
+ return;
+ }
+ }
+ }
+ try {
+ DataManager.get().getDhcptabMgr().deleteDhcptab();
+ } catch (Throwable e) {
+ addError(ResourceStrings.getString(
+ "unconfigure_error_dhcptab"),
+ e.getMessage());
+ }
+ try {
+ updateProgress(++checkpoint,
+ ResourceStrings.getString(
+ "unconfigure_dhcptab_deleted"));
+ } catch (InterruptedException e) {
+ closeDialog();
+ return;
+ }
+ }
+ }
+
+ // Remove the defaults file last, else stuff above may fail.
+ try {
+ serviceMgr.removeDefaults();
+ } catch (Throwable e) {
+ addError(ResourceStrings.getString(
+ "unconfigure_error_defaults"), e.getMessage());
+ }
+ try {
+ updateProgress(++checkpoint, ResourceStrings.getString(
+ "unconfigure_defaults_deleted"));
+ } catch (InterruptedException e) {
+ closeDialog();
+ return;
+ }
+
+ if (errorsOccurred()) {
+ displayErrors(ResourceStrings.getString(
+ "unconfigure_error_messages"));
+ }
+ closeDialog();
+ }
+ };
+ }
+
+ protected String getHelpKey() {
+ if (DhcpmgrApplet.modeIsRelay) {
+ return "unconfigure_relay";
+ } else {
+ return "unconfigure_server";
+ }
+ }
+
+ protected void fireActionPerformed() {
+ fireActionPerformed(this, DialogActions.OK);
+ }
+
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ViewMacroDialog.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ViewMacroDialog.java
new file mode 100644
index 0000000000..1fcbde3bf1
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ViewMacroDialog.java
@@ -0,0 +1,200 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 1998-2002 by Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.client;
+
+import javax.swing.*;
+import javax.swing.border.*;
+import javax.swing.event.*;
+import javax.swing.table.*;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+
+import com.sun.dhcpmgr.data.*;
+import com.sun.dhcpmgr.ui.*;
+
+/**
+ * This dialog allows the user to view the contents of a macro,
+ */
+public class ViewMacroDialog extends JDialog {
+
+ class MacroTableModel extends AbstractTableModel {
+ private Macro macro;
+
+ public MacroTableModel() {
+ setMacro(new Macro());
+ }
+
+ public MacroTableModel(Macro m) {
+ super();
+ setMacro(m);
+ }
+
+ public void setMacro(Macro m) {
+ macro = m;
+ fireTableDataChanged();
+ }
+
+ public int getRowCount() {
+ return macro.optionCount();
+ }
+
+ public int getColumnCount() {
+ return 2;
+ }
+
+ public Object getValueAt(int row, int column) {
+ OptionValue v = null;
+ try {
+ v = macro.getOptionAt(row);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ return null;
+ }
+ if (v == null) {
+ return null;
+ }
+ switch (column) {
+ case 0:
+ return v.getName();
+ case 1:
+ return v.getValue();
+ default:
+ return null;
+ }
+ }
+
+ public Class getColumnClass(int column) {
+ switch (column) {
+ case 0:
+ case 1:
+ return String.class;
+ default:
+ super.getColumnClass(column);
+ }
+ return null;
+ }
+
+ public String getColumnName(int column) {
+ switch (column) {
+ case 0:
+ return ResourceStrings.getString("option_column");
+ case 1:
+ return ResourceStrings.getString("value_column");
+ default:
+ super.getColumnName(column);
+ }
+ return null;
+ }
+ }
+
+ private JTextField name;
+ private AutosizingTable macroTable;
+ private MacroTableModel macroTableModel;
+ private JButton closeButton;
+
+ /**
+ * Construct the dialog.
+ * @arg owner The owning dialog
+ * @arg c The component relative to which we should be positioned
+ * @arg macro The macro we're viewing
+ */
+ public ViewMacroDialog(Dialog owner, Component c, Macro macro) {
+ super(owner);
+ setLocationRelativeTo(c);
+
+ setTitle(ResourceStrings.getString("view_macro_title"));
+
+ getContentPane().setLayout(new BoxLayout(getContentPane(),
+ BoxLayout.Y_AXIS));
+ JPanel mainPanel = new JPanel(new BorderLayout());
+ mainPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
+
+ name = new JTextField(30);
+ name.setEditable(false);
+ JPanel panel = new JPanel();
+
+ Mnemonic mnName =
+ new Mnemonic(ResourceStrings.getString("name_label"));
+ JLabel nLbl = new JLabel(mnName.getString());
+ nLbl.setLabelFor(panel);
+ nLbl.setToolTipText(mnName.getString());
+ nLbl.setDisplayedMnemonic(mnName.getMnemonic());
+ panel.add(nLbl);
+ panel.add(name);
+ mainPanel.add(panel, BorderLayout.NORTH);
+
+ JPanel contentsPanel = new JPanel();
+ contentsPanel.setLayout(new BorderLayout());
+ Border b = BorderFactory.createCompoundBorder(
+ BorderFactory.createLineBorder(Color.black),
+ BorderFactory.createEmptyBorder(5, 10, 5, 10));
+ contentsPanel.setBorder(BorderFactory.createTitledBorder(b,
+ ResourceStrings.getString("contents_label")));
+ contentsPanel.setToolTipText(
+ ResourceStrings.getString("contents_label"));
+ macroTableModel = new MacroTableModel();
+ macroTable = new AutosizingTable(macroTableModel);
+ macroTable.getTableHeader().setReorderingAllowed(false);
+ macroTable.getTableHeader().setResizingAllowed(false);
+
+ JScrollPane macroTablePane = new JScrollPane(macroTable);
+ // Resize table as otherwise it asks for a huge area
+ Dimension d = macroTable.getPreferredScrollableViewportSize();
+ d.height = 100;
+ d.width = 300;
+ macroTable.setPreferredScrollableViewportSize(d);
+
+ contentsPanel.add(macroTablePane, BorderLayout.CENTER);
+ mainPanel.add(contentsPanel, BorderLayout.CENTER);
+
+ getContentPane().add(mainPanel);
+ getContentPane().add(new JSeparator());
+
+ JPanel buttonPanel = new JPanel();
+
+ Mnemonic mnOK =
+ new Mnemonic(ResourceStrings.getString("ok"));
+ closeButton = new JButton(mnOK.getString());
+ closeButton.setToolTipText(mnOK.getString());
+ closeButton.setMnemonic(mnOK.getMnemonic());
+
+ buttonPanel.add(closeButton);
+ closeButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ setVisible(false);
+ dispose();
+ }
+ });
+
+ getContentPane().add(buttonPanel);
+
+ name.setText(macro.getKey());
+ macroTableModel.setMacro(macro);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/common/ExportController.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/common/ExportController.java
new file mode 100644
index 0000000000..c90bb3ddf8
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/common/ExportController.java
@@ -0,0 +1,428 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.common;
+
+import java.io.IOException;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+
+import com.sun.dhcpmgr.bridge.BridgeException;
+import com.sun.dhcpmgr.bridge.ExistsException;
+import com.sun.dhcpmgr.data.Network;
+import com.sun.dhcpmgr.data.ActionError;
+import com.sun.dhcpmgr.server.DhcpMgr;
+
+/**
+ * ExportController contains the logic to export the server's data to
+ * a file for later import on either this server or some other server.
+ * Users of this class must implement the Exporter interface in order
+ * to provide communication.
+ * @see Exporter
+ * @see ImportController
+ */
+public class ExportController {
+ Exporter exporter;
+ DhcpMgr server;
+ String user;
+ String file;
+ private boolean allNets = false, allMacros = false, allOptions = false;
+ // Statics used to ensure we never have null array references
+ private static final Network [] emptyNets = new Network[0];
+ private static final String [] emptyMacros = new String[0];
+ private static final String [] emptyOptions = new String[0];
+ Network [] networks = emptyNets;
+ String [] macros = emptyMacros;
+ String [] options = emptyOptions;
+ /*
+ * The following constants are heuristics used to estimate the time
+ * required to complete each step of the export process; they're used to
+ * allow a GUI progress meter to pop up and behave relatively correctly.
+ * We can't afford from a performance point of view to be precise as it
+ * would defeat the purpose of our export architecture, so we try to
+ * make sure the user gets at least some idea of where we are in the
+ * process. The *OPS constants indicate an estimate of how expensive
+ * the delete operations are relative to the export operations. YMMV.
+ */
+ private static final int OPTION_DELETE_OPS = 2;
+ private static final int MACRO_DELETE_OPS = 2;
+ private static final int NETWORK_DELETE_OPS = 2;
+ private static final int DEFAULT_OPTION_COUNT = 10;
+ private static final int DEFAULT_MACRO_COUNT = 50;
+ private static final int DEFAULT_CLIENTS_PER_NET = 150;
+
+ /**
+ * Construct an ExportController with the given Exporter and a
+ * server-side to use to perform the export. Don't pass in "null" for
+ * either argument; the implementation does not validate these inputs.
+ * @param exporter The exporting object
+ * @param server The server which will do the work for us.
+ */
+ public ExportController(Exporter exporter, DhcpMgr server) {
+ this.exporter = exporter;
+ this.server = server;
+ }
+
+ /**
+ * Set the name of the user performing the export. This is
+ * recorded in the export file for reference at import.
+ * @param user The name of the user
+ */
+ public void setUser(String user) {
+ this.user = user;
+ }
+
+ /**
+ * Set the name of the file to which to export.
+ * @param file the full pathname of the file to export into
+ */
+ public void setFile(String file) {
+ this.file = file;
+ }
+
+ /**
+ * Specify that all networks are to be exported
+ */
+ public void setAllNetworks() {
+ allNets = true;
+ networks = emptyNets;
+ }
+
+ /**
+ * Specify the networks to be exported.
+ * @param networks An array of Network objects which should be exported
+ */
+ public void setNetworks(Network [] networks) {
+ allNets = false;
+ // Never allow networks to be null
+ if (networks != null) {
+ this.networks = networks;
+ } else {
+ this.networks = emptyNets;
+ }
+ }
+
+ /**
+ * Specify that all macros should be exported.
+ */
+ public void setAllMacros() {
+ allMacros = true;
+ macros = emptyMacros;
+ }
+
+ /**
+ * Specify the macros to be exported.
+ * @param macros An array of macro names
+ */
+ public void setMacros(String [] macros) {
+ allMacros = false;
+ // Never allow macros to be null
+ if (macros != null) {
+ this.macros = macros;
+ } else {
+ this.macros = emptyMacros;
+ }
+ }
+
+ /**
+ * Specify that all options should be exported
+ */
+ public void setAllOptions() {
+ allOptions = true;
+ options = emptyOptions;
+ }
+
+ /**
+ * Specify the options to be exported.
+ * @param options An array of option names
+ */
+ public void setOptions(String [] options) {
+ allOptions = false;
+ // Never allow options to be null
+ if (options != null) {
+ this.options = options;
+ } else {
+ this.options = emptyOptions;
+ }
+ }
+
+ /**
+ * Perform the actual export.
+ * @param deleteData True if data should be deleted after a successful
+ * export.
+ * @param overwrite True if file should be forcibly overwritten. An
+ * ExistsException will be thrown if the file exists and overwrite is
+ * false.
+ * @return true if the export succeeded, false on failure.
+ */
+ public boolean exportData(boolean deleteData, boolean overwrite)
+ throws ExistsException {
+
+ Object ref = null;
+
+ // Value to return; default to false for failure
+ boolean retval = false;
+
+ // Default to deleting the file on any errors
+ boolean deleteFile = true;
+
+ if (allNets) {
+ try {
+ // Load network list
+ setNetworks(server.getNetMgr().getNetworks());
+ } catch (Exception e) {
+ displayException(e,
+ ResourceStrings.getString("exp_err_loading_networks"));
+ return false;
+ }
+ }
+
+ /*
+ * Number of records in the export file is number of networks, plus 1
+ * for options, plus 1 for macros.
+ */
+ int recCount = networks.length + 2;
+
+ // Calculate total number of estimated ops for progress
+ int optionOps = allOptions ? DEFAULT_OPTION_COUNT : options.length;
+ int optionDelOps = 0;
+ int macroOps = allMacros ? DEFAULT_MACRO_COUNT : macros.length;
+ int macroDelOps = 0;
+ int netOps = DEFAULT_CLIENTS_PER_NET * networks.length;
+ int netDelOps = 0;
+ int totalOps = optionOps + macroOps + netOps;
+ if (totalOps == 0) {
+ // Nothing to export!!!
+ exporter.displayError(ResourceStrings.getString("exp_err_no_data"));
+ return false;
+ }
+ // If user wants to delete, add to number of ops required
+ if (deleteData) {
+ optionDelOps = optionOps * OPTION_DELETE_OPS;
+ macroDelOps = macroOps * MACRO_DELETE_OPS;
+ netDelOps = netOps * NETWORK_DELETE_OPS;
+ totalOps += optionDelOps + macroDelOps + netDelOps;
+ }
+
+ /*
+ * Open the file; catch IO errors, but if we get an ExistsException we
+ * just let that through to the caller, who's supposed to deal with it
+ * appropriately.
+ */
+ try {
+ ref = server.openExportFile(file, user, recCount, networks,
+ overwrite);
+ // If lock couldn't be obtained, display error and abort
+ if (ref == null) {
+ String [] args = new String[2];
+ args[0] = server.getDhcpServiceMgr().getServerName();
+ args[1] = server.getLockPath();
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString("lock_error"));
+ exporter.displayError(form.format(args));
+ return false;
+ }
+ } catch (IOException e) {
+ displayException(e, ResourceStrings.getString("exp_err_io"));
+ return false;
+ }
+
+ try {
+
+ // Initialize progress with our expected number of operations
+ exporter.initializeProgress(totalOps);
+ int progress = 0;
+
+ // Now export the options
+ if (optionOps != 0) {
+ // Only update progress if we're actually doing something here
+ exporter.updateProgress(progress,
+ ResourceStrings.getString("exp_exporting_options"));
+ }
+ try {
+ server.exportOptions(ref, allOptions, options);
+ } catch (BridgeException e) {
+ displayException(e,
+ ResourceStrings.getString("exp_err_exporting_options"));
+ throw new InterruptedException();
+ }
+ progress += optionOps;
+
+ if (macroOps != 0) {
+ // Only update progress if we're actually doing something here
+ exporter.updateProgress(progress,
+ ResourceStrings.getString("exp_exporting_macros"));
+ }
+
+ // Now export the macros
+ try {
+ server.exportMacros(ref, allMacros, macros);
+ } catch (BridgeException e) {
+ displayException(e,
+ ResourceStrings.getString("exp_err_exporting_macros"));
+ throw new InterruptedException();
+ }
+ progress += macroOps;
+
+ // Set up for progress messages
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString("exp_exporting_network"));
+ String [] nets = new String[1];
+
+ // Now export each network in turn
+ for (int i = 0; i < networks.length; ++i) {
+ // Export the network
+ try {
+ nets[0] = networks[i].toString();
+ exporter.updateProgress(progress, form.format(nets));
+ server.exportNetwork(ref, networks[i]);
+ progress += DEFAULT_CLIENTS_PER_NET;
+ } catch (BridgeException e) {
+ MessageFormat fmt = new MessageFormat(
+ ResourceStrings.getString("exp_err_exporting_network"));
+ String [] args = new String [] { nets[0], e.getMessage() };
+ exporter.displayError(fmt.format(args));
+ throw new InterruptedException();
+ }
+ }
+
+ // Success; don't delete the file
+ deleteFile = false;
+
+ // If user wants data deleted, too, then do it now
+ if (deleteData) {
+ ActionError [] optionErrs, macroErrs;
+
+ // Delete options
+ if (optionDelOps != 0) {
+ // Only update progress if something to delete
+ exporter.updateProgress(progress,
+ ResourceStrings.getString("exp_deleting_options"));
+ }
+ if (allOptions) {
+ try {
+ optionErrs = server.getDhcptabMgr().deleteAllOptions();
+ } catch (BridgeException e) {
+ optionErrs = new ActionError[1];
+ optionErrs[0] = new ActionError(
+ ResourceStrings.getString("all_options"), e);
+ }
+ } else {
+ optionErrs = server.getDhcptabMgr().deleteOptions(options);
+ }
+ progress += optionDelOps;
+
+ // Delete macros
+ if (macroDelOps != 0) {
+ // Only update progress if something to delete
+ exporter.updateProgress(progress,
+ ResourceStrings.getString("exp_deleting_macros"));
+ }
+ if (allMacros) {
+ try {
+ macroErrs = server.getDhcptabMgr().deleteAllMacros();
+ } catch (BridgeException e) {
+ macroErrs = new ActionError[1];
+ macroErrs[0] = new ActionError(
+ ResourceStrings.getString("all_macros"), e);
+ }
+ } else {
+ macroErrs = server.getDhcptabMgr().deleteMacros(macros);
+ }
+ progress += macroDelOps;
+
+ // Delete each network in turn
+ form = new MessageFormat(
+ ResourceStrings.getString("exp_deleting_network"));
+ ArrayList errList = new ArrayList();
+ for (int i = 0; i < networks.length; ++i) {
+ nets[0] = networks[i].toString();
+ exporter.updateProgress(progress, form.format(nets));
+ try {
+ server.getNetMgr().deleteNetwork(nets[0], false, true);
+ } catch (BridgeException e) {
+ errList.add(new ActionError(nets[0], e));
+ }
+ progress += DEFAULT_CLIENTS_PER_NET * NETWORK_DELETE_OPS;
+ }
+
+ // This update informs caller we're done
+ exporter.updateProgress(progress,
+ ResourceStrings.getString("export_completed"));
+ // Now display whatever errors happened during delete
+ if (optionErrs != null && optionErrs.length > 0) {
+ exporter.displayErrors(
+ ResourceStrings.getString("exp_err_deleting_options"),
+ ResourceStrings.getString("exp_option"),
+ optionErrs);
+ }
+
+ if (macroErrs != null && macroErrs.length > 0) {
+ exporter.displayErrors(
+ ResourceStrings.getString("exp_err_deleting_macros"),
+ ResourceStrings.getString("exp_macro"),
+ macroErrs);
+ }
+
+ if (!errList.isEmpty()) {
+ exporter.displayErrors(
+ ResourceStrings.getString("exp_err_deleting_networks"),
+ ResourceStrings.getString("exp_network"),
+ (ActionError [])errList.toArray(new ActionError[0]));
+ }
+ }
+ retval = true;
+ } catch (InterruptedException e) {
+ /*
+ * User wanted to cancel, or some serious failure occurred; in the
+ * former case no need to display anything, in the latter it
+ * was already displayed before we got here, so just return.
+ */
+ retval = false;
+ } catch (Exception e) {
+ // I/O error of some sort. Display it before returning.
+ displayException(e, ResourceStrings.getString("exp_err_io"));
+ retval = false;
+ } finally {
+ // Always close before leaving; display any resulting errors
+ try {
+ server.closeExportFile(ref, deleteFile);
+ } catch (IOException e) {
+ displayException(e,
+ ResourceStrings.getString("exp_err_closing_file"));
+ }
+ }
+ return retval;
+ }
+
+ // Utility method to display an error message for an exception
+ private void displayException(Exception e, String format) {
+ MessageFormat form = new MessageFormat(format);
+ String [] args = new String [] { e.getMessage() };
+ exporter.displayError(form.format(args));
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/common/Exporter.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/common/Exporter.java
new file mode 100644
index 0000000000..910c34682a
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/common/Exporter.java
@@ -0,0 +1,66 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.common;
+
+import com.sun.dhcpmgr.data.ActionError;
+
+/**
+ * This interface is implemented by users of the ExportController to
+ * allow it to communicate progress information during the export process.
+ * @see ExportController
+ */
+public interface Exporter {
+ /**
+ * Callback to initialize the exporter's progress display.
+ * @param length The number of steps expected for the export process.
+ */
+ public void initializeProgress(int length);
+
+ /**
+ * Callback to update progress display.
+ * @param done The number of steps completed.
+ * @param message The message corresponding to this step.
+ */
+ public void updateProgress(int done, String message)
+ throws InterruptedException;
+
+ /**
+ * Callback to display a single error message.
+ * @param message The message to display.
+ */
+ public void displayError(String message);
+
+ /**
+ * Callback to display a set of errors from the delete process.
+ * @param contextMsg Message identifying the context for the errors
+ * @param label The type of objects for which the errors occurred
+ * @param errs An array of errors to be displayed.
+ */
+ public void displayErrors(String contextMsg, String label,
+ ActionError [] errs);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/common/ImportController.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/common/ImportController.java
new file mode 100644
index 0000000000..0fc6bf79ed
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/common/ImportController.java
@@ -0,0 +1,236 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2001-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.common;
+
+import java.io.IOException;
+import java.text.MessageFormat;
+
+import com.sun.dhcpmgr.data.ExportHeader;
+import com.sun.dhcpmgr.data.ActionError;
+import com.sun.dhcpmgr.data.Network;
+import com.sun.dhcpmgr.server.DhcpMgr;
+import com.sun.dhcpmgr.bridge.BridgeException;
+
+/**
+ * ImportController contains the logic to import data from a file to the
+ * server's data store. The file must have been written using the export
+ * procedure defined by ExportController. Users of this class must implement
+ * the Importer interface, which allows this class to communicate with the
+ * user.
+ * @see Importer
+ * @see ExportController
+ */
+public class ImportController {
+ Importer importer;
+ DhcpMgr server;
+ String file;
+ Object ref = null;
+ ExportHeader header = null;
+ /*
+ * The following constants are heuristics used to estimate the time
+ * required to complete each step of the import process; they're used to
+ * allow a GUI progress meter to pop up and behave relatively correctly.
+ * We can't afford from a performance point of view to be precise as it
+ * would defeat the purpose of our import architecture, so we try to
+ * make sure the user gets at least some idea of where we are in the
+ * process. The *OPS constants indicate an estimate of how expensive
+ * the various operations are relative to each other in a "typical"
+ * import, the assumption being that there are 5 macros exported to every
+ * option exported, and that there are around 150 clients per network.
+ * Obviously these can vary widely, but it gets the idea across pretty well.
+ */
+ private static final int OPTION_OPS = 1;
+ private static final int MACRO_OPS = 5;
+ private static final int NET_OPS = 150;
+
+ /**
+ * Construct an ImportController with the given Importer and server
+ * implementation to use for the import process. Don't pass in "null"
+ * for either argument; the implementation does not validate these inputs.
+ * @param importer The importing object
+ * @param server The server which will perform the work
+ */
+ public ImportController(Importer importer, DhcpMgr server) {
+ this.importer = importer;
+ this.server = server;
+ }
+
+ /**
+ * Set the name of the file to be used for the import
+ * @param file The name of the file.
+ */
+ public void setFile(String file) {
+ // We can only have one file open at a time; close any currently open.
+ closeFile();
+ this.file = file;
+ }
+
+ /**
+ * Close the file and clean up references
+ */
+ public void closeFile() {
+ if (ref != null) {
+ try {
+ // We *never* delete the file here
+ server.closeImportFile(ref, false);
+ } catch (IOException e) {
+ displayError(ResourceStrings.getString("imp_err_io"),
+ e.getMessage());
+ }
+ }
+ ref = null;
+ header = null;
+ }
+ /**
+ * Retrieve the header from the file.
+ * @return the header record from the file
+ */
+ public ExportHeader getHeader()
+ throws ClassNotFoundException, IOException {
+ // If header not already read, then read it
+ if (header == null) {
+ // If file not yet open, then open it now
+ if (ref == null) {
+ ref = server.openImportFile(file);
+ if (ref == null) {
+ // Import/export lock not available, display error and abort
+ String [] args = new String[2];
+ args[0] = server.getDhcpServiceMgr().getServerName();
+ args[1] = server.getLockPath();
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString("lock_error"));
+ importer.displayError(form.format(args));
+ return null;
+ }
+ }
+ header = server.getExportHeader(ref);
+ }
+ return header;
+ }
+
+ /**
+ * Import the data, optionally overwriting any conflicting data
+ * @param overwrite true if conflicting objects should be overwritten.
+ * @return true if the import completed successfully, false if not
+ */
+ public boolean importData(boolean overwrite) {
+ // Default return is that import did not complete
+ boolean retval = false;
+ int totalOps = 0;
+ try {
+ // Ensure file is open and header has been read
+ if (getHeader() == null) {
+ // Couldn't get header; abort
+ return false;
+ }
+
+ /*
+ * Initialize progress display; recCount is number of networks +
+ * one for macros and one for options.
+ */
+ int recCount = header.getRecCount();
+ totalOps = OPTION_OPS + MACRO_OPS + NET_OPS * (recCount - 2);
+ importer.initializeProgress(totalOps);
+ int progress = 0;
+
+ // Update progress, and import the options
+ importer.updateProgress(progress,
+ ResourceStrings.getString("importing_options"));
+ ActionError [] result = server.importOptions(ref, overwrite);
+ if (result.length > 0) {
+ importer.displayErrors(
+ ResourceStrings.getString("imp_err_importing_options"),
+ ResourceStrings.getString("imp_option"), result);
+ }
+
+ // Update progress and import the macros
+ progress += OPTION_OPS;
+ importer.updateProgress(progress,
+ ResourceStrings.getString("importing_macros"));
+ result = server.importMacros(ref, overwrite);
+ if (result.length > 0) {
+ importer.displayErrors(
+ ResourceStrings.getString("imp_err_importing_macros"),
+ ResourceStrings.getString("imp_macro"), result);
+ }
+
+ // Set up for network progress messages
+ progress += MACRO_OPS;
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString("importing_network"));
+ String [] args = new String[1];
+
+ /*
+ * Get list of networks from the header; ExportController never
+ * writes a null reference, always a zero-length array at worst.
+ */
+ Network [] nets = header.getNetworks();
+ for (int i = 0; i < nets.length; ++i) {
+ // For each network, update progress and import it
+ args[0] = nets[i].toString();
+ importer.updateProgress(progress, form.format(args));
+ result = server.importNetwork(nets[i], ref, overwrite);
+ if (result.length > 0) {
+ MessageFormat errFmt = new MessageFormat(
+ ResourceStrings.getString("imp_err_importing_net"));
+ importer.displayErrors(errFmt.format(args),
+ ResourceStrings.getString("imp_address"), result);
+ }
+ progress += NET_OPS;
+ }
+ retval = true;
+ } catch (InterruptedException e) {
+ // User asked us to stop; nothing to do but let it fall through
+ } catch (ClassNotFoundException e) {
+ // Bad version of file
+ displayError(ResourceStrings.getString("imp_err_file_fmt"),
+ e.getMessage());
+ } catch (Exception e) {
+ // Error reading the file
+ displayError(ResourceStrings.getString("imp_err_io"),
+ e.getMessage());
+ } finally {
+ // Finish progress
+ try {
+ importer.updateProgress(totalOps,
+ ResourceStrings.getString("import_completed"));
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+ // Always close import file
+ closeFile();
+ }
+ return retval;
+ }
+
+ private void displayError(String format, String data) {
+ MessageFormat form = new MessageFormat(format);
+ String [] args = new String [] { data };
+ importer.displayError(form.format(args));
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/common/Importer.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/common/Importer.java
new file mode 100644
index 0000000000..cc0f37dba1
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/common/Importer.java
@@ -0,0 +1,65 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.common;
+
+import com.sun.dhcpmgr.data.ActionError;
+
+/**
+ * This interface is implemented by users of the ImportController to
+ * allow it to communicate progress information during the import process.
+ * @see ImportController
+ */
+public interface Importer {
+ /**
+ * Callback to initialize the importer's progress display
+ * @param length The number of steps expected for the import process.
+ */
+ public void initializeProgress(int length);
+
+ /**
+ * Callback to update progress display
+ * @param done The number of steps completed.
+ * @param message The message corresponding to this step.
+ */
+ public void updateProgress(int done, String message)
+ throws InterruptedException;
+
+ /**
+ * Callback to display an error message.
+ * @param message The message to display
+ */
+ public void displayError(String message);
+
+ /**
+ * Callback to display a list of error messages.
+ * @param msg Message identifying contect for the errors
+ * @param label The type of objects for which the errors occurred
+ * @param errors An array of ActionError
+ */
+ public void displayErrors(String msg, String label, ActionError [] errors);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/common/Makefile b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/common/Makefile
new file mode 100644
index 0000000000..76ecf25086
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/common/Makefile
@@ -0,0 +1,76 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2001-2002 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/common/Makefile
+#
+
+# Place high-level classes first to minimize build time
+CLASSFILES = ExportController.class \
+ ImportController.class \
+ ResourceStrings.class
+
+include $(SRC)/Makefile.master
+
+CLASSPATH= $(SRC)/cmd/cmd-inet/usr.sadm/dhcpmgr
+
+JAVAFILES = $(CLASSFILES:.class=.java)
+
+MSGDIR= $(ROOT)/usr/share/lib/locale/com/sun/dhcpmgr/common
+MSGDIRS = $(ROOT)/usr/share/lib/locale \
+ $(ROOT)/usr/share/lib/locale/com \
+ $(ROOT)/usr/share/lib/locale/com/sun \
+ $(ROOT)/usr/share/lib/locale/com/sun/dhcpmgr \
+ $(MSGDIR)
+
+MSGFILES= ResourceBundle.properties
+MSGS= $(MSGFILES:%=$(MSGDIR)/%)
+
+CLEANFILES= *.class
+CLOBBERFILES=
+
+.KEEP_STATE:
+
+all: $(CLASSFILES)
+
+install: all
+
+_msg: $(MSGDIRS) $(MSGS)
+
+$(MSGDIR)/%: %
+ $(INS.file)
+
+$(MSGDIRS):
+ $(INS.dir)
+
+lint:
+
+clean: FRC
+ $(RM) $(CLEANFILES)
+
+clobber: clean
+
+FRC:
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/common/ResourceBundle.properties b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/common/ResourceBundle.properties
new file mode 100644
index 0000000000..0f39bb26af
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/common/ResourceBundle.properties
@@ -0,0 +1,73 @@
+#
+#pragma ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+# Common lock error
+lock_error=The lock file, {1},\nhas been detected on server, {0}.\nThis indicates that another export or import is already running.\nPlease try again later.
+
+# Export resources
+exp_exporting_options=Exporting options...
+exp_exporting_macros=Exporting macros...
+exp_exporting_network=Exporting network {0}...
+
+exp_deleting_options=Deleting options...
+exp_deleting_macros=Deleting macros...
+exp_deleting_network=Deleting network {0}...
+
+export_completed=Export completed
+
+exp_err_loading_networks=Unable to load the list of networks; error was:\n{0}
+exp_err_no_data=No data was specified for export.
+exp_err_io=I/O Error while exporting, message was:\n{0}
+exp_err_closing_file=Error while closing the export file, mesasge was:\n{0}
+exp_err_exporting_options=Error while exporting options, message was:\n{0}
+exp_err_exporting_macros=Error while exporting macros, message was:\n{0}
+exp_err_exporting_network=Error while exporting network {0}, message was:\n{1}
+exp_err_deleting_options=The following errors occurred while deleting options:
+exp_err_deleting_macros=The following errors occurred while deleting macros:
+exp_err_deleting_networks=The following errors occurred while deleting networks:
+
+all_options=All Options
+all_macros=All Macros
+exp_option=Option
+exp_macro=Macro
+exp_network=Network
+
+# Import resources
+importing_options=Importing Options...
+importing_macros=Importing Macros...
+importing_network=Importing Network {0}...
+import_completed=Import completed
+
+imp_err_importing_options=The following errors occurred while importing options:
+imp_err_importing_macros=The following errors occurred while importing macros:
+imp_err_importing_net=The following errors occurred while importing network {0}:
+imp_err_file_fmt=The import file contained an unknown class; the detailed message was:\n{0}
+imp_err_io=The file could not be imported due to the following input error:\n{0}
+
+imp_option=Option
+imp_macro=Macro
+imp_address=Address
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/common/ResourceStrings.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/common/ResourceStrings.java
new file mode 100644
index 0000000000..3f89b74ec1
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/common/ResourceStrings.java
@@ -0,0 +1,64 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.common;
+
+import java.util.*;
+
+/**
+ * This class provides a convenient method to retrieve resources for
+ * the common package.
+ */
+public class ResourceStrings {
+
+ /**
+ * The handle to the resource bundle for the module.
+ */
+ private static ResourceBundle bundle = null;
+
+ /**
+ * Return a string from the resource bundle.
+ * @param key the key to the resource bundle string.
+ * @return the resource bundle string.
+ */
+ public static String getString(String key) {
+ String msg = null;
+ try {
+ if (bundle == null) {
+ bundle = ResourceBundle.getBundle(
+ "com.sun.dhcpmgr.common.ResourceBundle",
+ Locale.getDefault());
+ }
+ msg = bundle.getString(key);
+ } catch (Throwable e) {
+ msg = new String(key);
+ }
+ return msg;
+
+ } // getString
+
+} // ResourceStrings
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/ActionError.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/ActionError.java
new file mode 100644
index 0000000000..c0ddb78777
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/ActionError.java
@@ -0,0 +1,90 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+package com.sun.dhcpmgr.data;
+
+import java.io.Serializable;
+
+/**
+ * This class represents an error which occurred during some action
+ */
+public class ActionError implements Serializable {
+
+ /**
+ * The name of the element that was acted upon
+ */
+ private String name;
+
+ /**
+ * The exception that occurred while acting on the named element
+ */
+ private Exception e;
+
+ /**
+ * Basic constructor.
+ * @param name the element name.
+ */
+ public ActionError(String name) {
+ this.name = name;
+ e = null;
+ } // constructor
+
+ /**
+ * Create a fully formed versoin of this object
+ * @param name The name of the element
+ * @param exception The exception which occurred
+ */
+ public ActionError(String name, Exception exception) {
+ this.name = name;
+ e = exception;
+ }
+
+ /**
+ * Returns the element name.
+ * @return the element name.
+ */
+ public String getName() {
+ return name;
+ } // getName
+
+ /**
+ * Sets the exception.
+ * @param exception the exception to associate with the element.
+ */
+ public void setException(Exception e) {
+ this.e = e;
+ } // setException
+
+ /**
+ * Returns the exception.
+ * @return the exception.
+ */
+ public Exception getException() {
+ return e;
+ } // getException
+} // ActionError
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/AsciiOptionValue.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/AsciiOptionValue.java
new file mode 100644
index 0000000000..bf6df4dada
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/AsciiOptionValue.java
@@ -0,0 +1,132 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 1999-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.data;
+
+import java.util.Vector;
+import java.util.Enumeration;
+
+public class AsciiOptionValue extends OptionValue {
+ private String name;
+ private String value;
+ private boolean valid;
+
+ // Serialization id for this class
+ static final long serialVersionUID = -4937655446360683504L;
+
+ protected AsciiOptionValue(String name) {
+ this.name = name;
+ value = null;
+ valid = false;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getValue() {
+ // Before we return the value, we go through and escape special chars.
+ StringBuffer retValue = new StringBuffer();
+ char [] c = value.toCharArray();
+ for (int i = 0; i < c.length; ++i) {
+ if (c[i] == '\\' || c[i] == '"') {
+ retValue.append('\\');
+ }
+ retValue.append(c[i]);
+ }
+ return retValue.toString();
+ }
+
+ public void setValue(Object value) throws ValidationException {
+ // Find option in option definition table in order to validate the data
+ Option option = OptionsTable.getTable().get(name);
+ if (option == null) {
+ Object [] args = { name };
+ throwException("invalid_option", args);
+ }
+ if (value instanceof String) {
+ String newValue = (String)value;
+ // Either quoted, or not, but must balance
+ if (newValue.startsWith("\"") ^ newValue.endsWith("\"")) {
+ Object [] args = { name,
+ Option.getTypeDhcptabString(option.getType()) };
+ throwException("invalid_option_value", args);
+ }
+ if (newValue.startsWith("\"")) {
+ newValue = newValue.substring(1, newValue.length() - 1);
+ }
+ if (newValue.length() == 0) {
+ // Empty strings are not acceptable
+ Object [] args = { name,
+ Option.getTypeDhcptabString(option.getType()) };
+ throwException("invalid_option_value", args);
+ }
+ // Check that the resulting length is OK
+ if ((option.getMaximum() != 0)
+ && (newValue.length() > option.getMaximum())) {
+ Object [] args = { name,
+ Integer.toString(option.getMaximum()) };
+ throwException("invalid_option_maximum", args);
+ }
+ this.value = newValue;
+ valid = true;
+ } else if (value instanceof Vector) {
+ /*
+ * We generate the value by creating a blank-separated list of
+ * tokens; each token is the product of a toString() on the
+ * vector's elements.
+ */
+ StringBuffer b = new StringBuffer();
+ Enumeration en = ((Vector)value).elements();
+ while (en.hasMoreElements()) {
+ if (b.length() != 0) {
+ b.append(' ');
+ }
+ b.append(en.nextElement().toString());
+ }
+ setValue(b.toString());
+ } else {
+ // Anything else should just tell us what it looks like as a string.
+ setValue(value.toString());
+ }
+ }
+
+ public String toString() {
+ return (getName() + "=\"" + getValue() + "\"");
+ }
+
+ public boolean isValid() {
+ return valid;
+ }
+
+ public Object clone() {
+ AsciiOptionValue v = new AsciiOptionValue(name);
+ v.value = value;
+ v.valid = valid;
+ return v;
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/BogusOptionValue.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/BogusOptionValue.java
new file mode 100644
index 0000000000..56f4c5b189
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/BogusOptionValue.java
@@ -0,0 +1,102 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 1999-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+package com.sun.dhcpmgr.data;
+
+import java.util.Vector;
+import java.util.Enumeration;
+
+/**
+ * This class provides a way for us to handle errors in the dhcptab which
+ * may have been introduced through the command line or direct editing of
+ * the table. The idea is for the OptionValueFactory to trap bad option
+ * names or values and store them in an instance of this class so that the
+ * user can then be told about the error and allowed to fix it.
+ */
+public class BogusOptionValue extends OptionValue {
+ private String name;
+ private String value;
+
+ // Serialization id for this class
+ static final long serialVersionUID = 8573418100554161901L;
+
+ protected BogusOptionValue(String name) {
+ this.name = name;
+ value = null;
+ }
+
+ protected BogusOptionValue(String name, Object value) {
+ this.name = name;
+ setValue(value);
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(Object value) {
+ if (value instanceof Vector) {
+ /*
+ * We generate the value by creating a blank-separated list of
+ * tokens; each token is the product of a toString() on the
+ * vector's elements.
+ */
+ StringBuffer b = new StringBuffer();
+ Enumeration en = ((Vector)value).elements();
+ while (en.hasMoreElements()) {
+ if (b.length() != 0) {
+ b.append(' ');
+ }
+ b.append(en.nextElement().toString());
+ }
+ setValue(b.toString());
+ } else if (value instanceof String) {
+ this.value = (String)value;
+ } else {
+ // Anything else should just tell us what it looks like as a string.
+ setValue(value.toString());
+ }
+ }
+
+ public String toString() {
+ return (getName() + "=\"" + getValue() + "\"");
+ }
+
+ public boolean isValid() {
+ // This kind of option is never valid
+ return false;
+ }
+
+ public Object clone() {
+ return new BogusOptionValue(name, value);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/BooleanOptionValue.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/BooleanOptionValue.java
new file mode 100644
index 0000000000..8aca6da304
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/BooleanOptionValue.java
@@ -0,0 +1,71 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 1999-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.data;
+
+import java.util.Vector;
+
+public class BooleanOptionValue extends OptionValue {
+ private String name;
+
+ // Serialization id for this class
+ static final long serialVersionUID = 5379063769810230706L;
+
+ protected BooleanOptionValue(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getValue() {
+ return "";
+ }
+
+ public void setValue(Object value) throws ValidationException {
+ // Booleans must have an empty value
+ Option option = OptionsTable.getTable().get(name);
+ if (value != null && value.toString().length() != 0) {
+ Object [] args = { name,
+ Option.getTypeDhcptabString(option.getType()) };
+ throwException("invalid_option_value", args);
+ }
+ }
+
+ public String toString() {
+ return getName();
+ }
+
+ public boolean isValid() {
+ return true;
+ }
+
+ public Object clone() {
+ return new BooleanOptionValue(name);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/DhcpClientFlagType.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/DhcpClientFlagType.java
new file mode 100644
index 0000000000..4564775994
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/DhcpClientFlagType.java
@@ -0,0 +1,67 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1999-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+package com.sun.dhcpmgr.data;
+
+/**
+ * This class represents a flag type for a record in a DHCP network table.
+ */
+public class DhcpClientFlagType {
+ private byte numericVal;
+ private char charVal;
+ private String keyword;
+
+ public DhcpClientFlagType() {
+ numericVal = 0;
+ charVal = '0';
+ keyword = "";
+ }
+
+ public DhcpClientFlagType(byte numericVal, char charVal, String keyword) {
+
+ this.numericVal = numericVal;
+ this.charVal = charVal;
+ this.keyword = keyword;
+ }
+
+ public byte getNumericVal() {
+ return numericVal;
+ }
+
+ public char getCharVal() {
+ return charVal;
+ }
+
+ public String getKeyword() {
+ return keyword;
+ }
+
+ public boolean isSet(byte flags) {
+ return ((flags & numericVal) != 0);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/DhcpClientFlagTypes.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/DhcpClientFlagTypes.java
new file mode 100644
index 0000000000..e70c23f652
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/DhcpClientFlagTypes.java
@@ -0,0 +1,50 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+package com.sun.dhcpmgr.data;
+
+/**
+ * This class contains definitiion for all the valid DHCP client flags.
+ */
+public class DhcpClientFlagTypes {
+
+ public static final DhcpClientFlagType BOOTP =
+ new DhcpClientFlagType((byte)8, 'B', new String("BOOTP"));
+
+ public static final DhcpClientFlagType UNUSABLE =
+ new DhcpClientFlagType((byte)4, 'U', new String("UNUSABLE"));
+
+ public static final DhcpClientFlagType MANUAL =
+ new DhcpClientFlagType((byte)2, 'M', new String("MANUAL"));
+
+ public static final DhcpClientFlagType PERMANENT =
+ new DhcpClientFlagType((byte)1, 'P', new String("PERMANENT"));
+
+ public static final DhcpClientFlagType DYNAMIC =
+ new DhcpClientFlagType((byte)0, 'D', new String("DYNAMIC"));
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/DhcpClientRecord.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/DhcpClientRecord.java
new file mode 100644
index 0000000000..483225a38b
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/DhcpClientRecord.java
@@ -0,0 +1,674 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 1998-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+package com.sun.dhcpmgr.data;
+
+import java.util.Date;
+import java.text.SimpleDateFormat;
+import java.text.DateFormat;
+import java.util.StringTokenizer;
+import java.io.Serializable;
+
+/**
+ * This class represents a record in a DHCP network table. It can also be used
+ * to manage an associated hosts record by setting the client name; that effect
+ * is not part of this class, but rather is provided by the DhcpNetMgr.
+ */
+public class DhcpClientRecord implements Serializable, Comparable, Cloneable {
+
+ /**
+ * Default values for class attributes.
+ */
+ public static final String DEFAULT_CLIENT_ID = new String("00");
+ public static final String DEFAULT_FLAGS = new String("00");
+ public static final String DEFAULT_CLIENT_NAME = new String();
+ public static final String DEFAULT_EXPIRATION = new String("0");
+ public static final String DEFAULT_SIGNATURE = new String("0");
+ public static final String DEFAULT_MACRO = new String("UNKNOWN");
+ public static final String DEFAULT_COMMENT = new String();
+
+ /**
+ * Expiration special values.
+ */
+ private static final String EXPIRATION_ZERO = new String("0");
+ private static final String EXPIRATION_FOREVER = new String("-1");
+
+ private String clientId;
+ private byte flags;
+ private IPAddress clientIP;
+ private IPAddress serverIP;
+ private Date expiration;
+ private String signature = DEFAULT_SIGNATURE;
+ private String macro;
+ private String comment;
+ private String clientName = null;
+ private String serverName = null;
+
+ // Serialization id of this class
+ static final long serialVersionUID = 5007310554198923085L;
+
+ /**
+ * Constructs a basic, empty client record.
+ */
+ public DhcpClientRecord() {
+ clientId = DEFAULT_CLIENT_ID;
+ macro = comment = null;
+ flags = 0;
+ clientIP = serverIP = null;
+ expiration = null;
+ signature = DEFAULT_SIGNATURE;
+ }
+
+ /**
+ * Constructs a client record with a client IP.
+ * @param clientIP the client IP address for the record.
+ */
+ public DhcpClientRecord(String clientIP) throws ValidationException {
+ setDefaults();
+ setClientIP(new IPAddress(clientIP));
+ }
+
+ /**
+ * Constructs a fully specified client record
+ * @param clientId Client's unique identifier
+ * @param flags Status flags for the record
+ * @param clientIP Client's IP address
+ * @param serverIP IP address of owning server
+ * @param expiration Lease expiration time in seconds since Unix epoch
+ * @param macro Configuration macro associated with this record
+ * @param comment User notes on this record
+ */
+ public DhcpClientRecord(String clientId, String flags, String clientIP,
+ String serverIP, String expiration, String macro,
+ String comment) throws ValidationException {
+
+ this(clientId, flags, clientIP, serverIP, expiration, macro,
+ comment, DEFAULT_SIGNATURE);
+ }
+
+ /**
+ * Constructs a fully specified client record
+ * @param clientId Client's unique identifier
+ * @param flags Status flags for the record
+ * @param clientIP Client's IP address
+ * @param serverIP IP address of owning server
+ * @param expiration Lease expiration time in seconds since Unix epoch
+ * @param macro Configuration macro associated with this record
+ * @param comment User notes on this record
+ * @param signature Opaque signature
+ */
+ public DhcpClientRecord(String clientId, String flags, String clientIP,
+ String serverIP, String expiration, String macro,
+ String comment, String signature)
+ throws ValidationException {
+ setClientId(clientId);
+ this.flags = Byte.parseByte(flags);
+ setClientIP(new IPAddress(clientIP));
+ setServerIP(new IPAddress(serverIP));
+ setExpiration(expiration);
+ this.macro = macro;
+ this.comment = comment;
+ this.signature = signature;
+ }
+
+ /**
+ * Make a copy of this record
+ */
+ public Object clone() {
+ DhcpClientRecord newrec = new DhcpClientRecord();
+ newrec.clientId = clientId;
+ newrec.flags = flags;
+ if (clientIP != null) {
+ newrec.clientIP = (IPAddress)clientIP.clone();
+ }
+ if (serverIP != null) {
+ newrec.serverIP = (IPAddress)serverIP.clone();
+ }
+ if (expiration != null) {
+ newrec.expiration = (Date)expiration.clone();
+ }
+ newrec.macro = macro;
+ newrec.comment = comment;
+ newrec.clientName = clientName;
+ newrec.serverName = serverName;
+ newrec.signature = signature;
+ return newrec;
+ }
+
+ /**
+ * Fully specifies the defaults for a client record
+ */
+ public void setDefaults()
+ throws ValidationException {
+ setClientId(DEFAULT_CLIENT_ID);
+ setFlags(DEFAULT_FLAGS);
+ setClientName(DEFAULT_CLIENT_NAME);
+ setExpiration(DEFAULT_EXPIRATION);
+ setMacro(DEFAULT_MACRO);
+ setComment(DEFAULT_COMMENT);
+ }
+
+ /**
+ * Retrieve the client ID
+ * @return Client ID as a String
+ */
+ public String getClientId() {
+ return clientId;
+ }
+
+ /**
+ * Set the client ID. See dhcp_network(4) for the rules about client
+ * ID syntax which are implemented here.
+ * @param clientId Client's unique identifier
+ */
+ public void setClientId(String clientId) throws ValidationException {
+ if (clientId.length() > 128 || clientId.length() % 2 != 0) {
+ // Must be even number of characters, no more than 128 characters
+ String msg = ResourceStrings.getString("dcr_invalid_clientid");
+ throw new ValidationException(msg);
+ }
+ char [] c = clientId.toCharArray();
+ for (int i = 0; i < c.length; ++i) {
+ if ((c[i] < '0' || c[i] > '9') && (c[i] < 'A' || c[i] > 'F')) {
+ String msg = ResourceStrings.getString("dcr_invalid_clientid");
+ throw new ValidationException(msg);
+ }
+ }
+ this.clientId = clientId;
+ if (this.clientId.length() == 0) {
+ this.clientId = DEFAULT_CLIENT_ID;
+ }
+ }
+
+ /**
+ * Get the flags byte
+ * @return A <code>byte</code> containing the record's status flags
+ */
+ public byte getFlags() {
+ return flags;
+ }
+
+ /**
+ * Get the flags as a string
+ * @return The flag byte converted to a String
+ */
+ public String getFlagString() {
+ return getFlagString(false);
+ }
+
+ public String getFlagString(boolean verbose) {
+
+ StringBuffer b = new StringBuffer();
+ if (!verbose) {
+ b.append(flags);
+ // Make sure we always have a 2-character representation.
+ if (flags < 10) {
+ b.insert(0, 0);
+ }
+ }
+ else {
+ if (flags == 0) {
+ b.append(DhcpClientFlagTypes.DYNAMIC.getCharVal());
+ } else {
+ if (isPermanent()) {
+ b.append(DhcpClientFlagTypes.PERMANENT.getCharVal());
+ }
+ if (isManual()) {
+ b.append(DhcpClientFlagTypes.MANUAL.getCharVal());
+ }
+ if (isUnusable()) {
+ b.append(DhcpClientFlagTypes.UNUSABLE.getCharVal());
+ }
+ if (isBootp()) {
+ b.append(DhcpClientFlagTypes.BOOTP.getCharVal());
+ }
+ }
+ }
+ return b.toString();
+ }
+
+ /**
+ * Test for setting of unusable flag
+ * @return <code>true</code> if the unusable flag is set,
+ * <code>false</code> if not.
+ */
+ public boolean isUnusable() {
+ return DhcpClientFlagTypes.UNUSABLE.isSet(flags);
+ }
+
+ /**
+ * Set/reset the unusable flag.
+ * @param state <code>true</code> if address is to be unusable
+ */
+ public void setUnusable(boolean state) {
+ if (state) {
+ flags |= DhcpClientFlagTypes.UNUSABLE.getNumericVal();
+ } else {
+ flags &= ~DhcpClientFlagTypes.UNUSABLE.getNumericVal();
+ }
+ }
+
+ /**
+ * Test for setting of bootp flag
+ * @return <code>true</code> if the bootp flag is set,
+ * <code>false</code> if not.
+ */
+ public boolean isBootp() {
+ return DhcpClientFlagTypes.BOOTP.isSet(flags);
+ }
+
+ /**
+ * Set/reset the bootp flag
+ * @param state <code>true</code> if address is reserved for BOOTP clients
+ */
+ public void setBootp(boolean state) {
+ if (state) {
+ flags |= DhcpClientFlagTypes.BOOTP.getNumericVal();
+ } else {
+ flags &= ~DhcpClientFlagTypes.BOOTP.getNumericVal();
+ }
+ }
+
+ /**
+ * Test for setting of manual assignment flag
+ * @return <code>true</code> if address is manually assigned,
+ * <code>false</code> if not.
+ */
+ public boolean isManual() {
+ return DhcpClientFlagTypes.MANUAL.isSet(flags);
+ }
+
+ /**
+ * Set/reset the manual assignment flag
+ * @param state <code>true</code> if the address is manually assigned
+ */
+ public void setManual(boolean state) {
+ if (state) {
+ flags |= DhcpClientFlagTypes.MANUAL.getNumericVal();
+ } else {
+ flags &= ~DhcpClientFlagTypes.MANUAL.getNumericVal();
+ }
+ }
+
+ /**
+ * Test for setting of permanent assignment flag
+ * @return <code>true</code> if lease is permanent,
+ * <code>false</code> if dynamic
+ */
+ public boolean isPermanent() {
+ return DhcpClientFlagTypes.PERMANENT.isSet(flags);
+ }
+
+ /**
+ * Set/reset the permanent assignment flag
+ * @param state <code>true</code> if the address is permanently leased
+ */
+ public void setPermanent(boolean state) {
+ if (state) {
+ flags |= DhcpClientFlagTypes.PERMANENT.getNumericVal();
+ } else {
+ flags &= ~DhcpClientFlagTypes.PERMANENT.getNumericVal();
+ }
+ }
+
+ /**
+ * Set the flags as a unit
+ * @param flags a <code>byte</code> setting for the flags
+ */
+ public void setFlags(String flags) throws ValidationException {
+ if (flags.charAt(0) >= '0' && flags.charAt(0) <= '9') {
+ this.flags = Byte.parseByte(flags);
+ } else {
+ this.flags = 0;
+ StringTokenizer flagTokenizer = new StringTokenizer(flags, "+");
+ while (flagTokenizer.hasMoreTokens()) {
+ String keyword = flagTokenizer.nextToken();
+ if (keyword.equalsIgnoreCase(
+ DhcpClientFlagTypes.DYNAMIC.getKeyword())) {
+ // nothing to do, default is Dynamic.
+ } else if (keyword.equalsIgnoreCase(
+ DhcpClientFlagTypes.PERMANENT.getKeyword())) {
+ this.flags |= DhcpClientFlagTypes.PERMANENT.getNumericVal();
+ } else if (keyword.equalsIgnoreCase(
+ DhcpClientFlagTypes.MANUAL.getKeyword())) {
+ this.flags |= DhcpClientFlagTypes.MANUAL.getNumericVal();
+ } else if (keyword.equalsIgnoreCase(
+ DhcpClientFlagTypes.UNUSABLE.getKeyword())) {
+ this.flags |= DhcpClientFlagTypes.UNUSABLE.getNumericVal();
+ } else if (keyword.equalsIgnoreCase(
+ DhcpClientFlagTypes.BOOTP.getKeyword())) {
+ this.flags |= DhcpClientFlagTypes.BOOTP.getNumericVal();
+ } else {
+ String msg = ResourceStrings.getString("dcr_invalid_flags");
+ throw new ValidationException(msg);
+ }
+ }
+ }
+ }
+
+ /**
+ * Set the flags as a unit
+ * @param flags a <code>byte</code> setting for the flags
+ */
+ public void setFlags(byte flags) {
+ this.flags = flags;
+ }
+
+ /**
+ * Retrieve the client's IP address
+ * @return the client's IP address
+ */
+ public IPAddress getClientIP() {
+ return clientIP;
+ }
+
+ /**
+ * Retrieve a string version of the client's IP address
+ * @return A <code>String</code> containing the dotted decimal IP address.
+ */
+ public String getClientIPAddress() {
+ if (clientIP == null) {
+ return "";
+ } else {
+ return clientIP.getHostAddress();
+ }
+ }
+
+ /**
+ * Set the client's IP address
+ * @param clientIP A String representation of the <code>IPAddress</code>
+ * to assign from this record.
+ */
+ public void setClientIP(String clientIP) throws ValidationException {
+ if (clientIP == null) {
+ String msg = ResourceStrings.getString("dcr_invalid_null_clientip");
+ throw new ValidationException(msg);
+ }
+
+ try {
+ setClientIP(new IPAddress(clientIP));
+ } catch (Throwable e) {
+ String msg = ResourceStrings.getString("dcr_invalid_clientip");
+ throw new ValidationException(msg);
+ }
+ }
+
+ /**
+ * Set the client's IP address
+ * @param clientIP An <code>IPAddress</code> to assign from this record.
+ */
+ public void setClientIP(IPAddress clientIP) throws ValidationException {
+ if (clientIP == null) {
+ String msg = ResourceStrings.getString("dcr_invalid_null_clientip");
+ throw new ValidationException(msg);
+ }
+ this.clientIP = clientIP;
+ }
+
+ /**
+ * Retrieve the IP address of the owning server.
+ * @return An <code>IPAddress</code> for the server controlling this record.
+ */
+ public IPAddress getServerIP() {
+ return serverIP;
+ }
+
+ /**
+ * Retrieve a string version of the owning server's IP address
+ * @return The server's dotted decimal IP address as a <code>String</code>
+ */
+ public String getServerIPAddress() {
+ if (serverIP == null) {
+ return "";
+ } else {
+ return serverIP.getHostAddress();
+ }
+ }
+
+ /**
+ * Set the server's IP address
+ * @param serverIP A String representation of the <code>IPAddress</code>
+ * to assign from this record.
+ */
+ public void setServerIP(String serverIP) throws ValidationException {
+ if (serverIP == null) {
+ String msg = ResourceStrings.getString("dcr_invalid_null_serverip");
+ throw new ValidationException(msg);
+ }
+
+ try {
+ setServerIP(new IPAddress(serverIP));
+ } catch (Throwable e) {
+ String msg = ResourceStrings.getString("dcr_invalid_serverip");
+ throw new ValidationException(msg);
+ }
+ }
+
+ /**
+ * Assign this address to a server denoted by its IP address
+ * @param serverIP The <code>IPAddress</code> of the owning server.
+ */
+ public void setServerIP(IPAddress serverIP) throws ValidationException {
+ if (serverIP == null) {
+ String msg = ResourceStrings.getString("dcr_invalid_null_serverip");
+ throw new ValidationException(msg);
+ }
+ this.serverIP = serverIP;
+ }
+
+ /**
+ * @return The expiration time of this record's lease as a <code>Date</code>
+ */
+ public Date getExpiration() {
+ return expiration;
+ }
+
+ /**
+ * @return The expiration time of this record's lease in seconds
+ * since the epoch, as a <code>String</code>
+ */
+ public String getExpirationTime() {
+ if (expiration == null) {
+ return null;
+ }
+ if (expiration.getTime() == Long.parseLong(EXPIRATION_FOREVER)) {
+ return EXPIRATION_FOREVER;
+ } else {
+ return String.valueOf((expiration.getTime()/(long)1000));
+ }
+ }
+
+ /**
+ * Set the lease expiration date.
+ * @param expiration Lease expiration time in seconds since Unix epoch
+ */
+ public void setExpiration(String expiration) {
+ this.expiration = new Date((long)(Long.parseLong(expiration)*1000));
+ }
+
+ /**
+ * Set the lease expiration date.
+ * @param expiration The <code>Date</code> when the lease expires.
+ */
+ public void setExpiration(Date expiration) {
+ this.expiration = expiration;
+ }
+
+ /**
+ * Set the lease expiration date by parsing a formatted string. Also
+ * provides special handling of the "0" and "-1" values.
+ * @param dateFormat A DateFormat used to parse the expiration date
+ * @param date Lease expiration in desired format.
+ */
+ public void setExpiration(DateFormat dateFormat, String date)
+ throws ValidationException {
+
+ if (date == null) {
+ setExpiration(date);
+ } else if (date.equals(EXPIRATION_ZERO)) {
+ setExpiration(date);
+ } else if (date.equals(EXPIRATION_FOREVER)) {
+ setExpiration(date);
+ } else {
+ try {
+ expiration = dateFormat.parse(date);
+ } catch (Exception ex) {
+ String msg =
+ ResourceStrings.getString("dcr_invalid_expiration");
+ throw new ValidationException(msg);
+ }
+ }
+ }
+
+ /**
+ * @return The name of the macro used to explicitly configure this address
+ */
+ public String getMacro() {
+ return macro;
+ }
+
+ /**
+ * Set the name of the macro used to explicitly configure this address
+ */
+ public void setMacro(String macro) {
+ this.macro = macro;
+ }
+
+ /**
+ * @return The descriptive comment for this record
+ */
+ public String getComment() {
+ return comment;
+ }
+
+ /**
+ * Set a descriptive comment for this record
+ * @param comment The comment
+ */
+ public void setComment(String comment) {
+ this.comment = comment;
+ }
+
+ /**
+ * @return The signature for this record
+ */
+ public String getSignature() {
+ return signature;
+ }
+
+ /**
+ * Set the signature for this record
+ * @param signature The new signature value
+ */
+ public void setSignature(String signature) {
+ this.signature = signature;
+ }
+
+ /**
+ * Perform comparisons to another DhcpClientRecord instance. This is used
+ * for sorting a network table by client address.
+ * @param o A <code>DhcpClientRecord</code> to compare against.
+ * @return 0 if the objects have the same address,
+ * a negative number if this record has a lower IP address than the
+ * supplied record, a positive number if this record has a higher IP
+ * address than the supplied record.
+ */
+ public int compareTo(Object o) {
+ DhcpClientRecord r = (DhcpClientRecord)o;
+ return (int)(getBinaryAddress() - r.getBinaryAddress());
+ }
+
+ /**
+ * Retrieve the IP address as a number suitable for arithmetic operations.
+ * We use a <code>long</code> rather than an <code>int</code> in order to
+ * be able to treat it as an unsigned value, since all Java types are
+ * signed.
+ * @return The IP address as a <code>long</code>.
+ */
+ public long getBinaryAddress() {
+ return (clientIP.getBinaryAddress());
+ }
+
+ /**
+ * @return The client's hostname
+ */
+ public String getClientName() {
+ if (clientName == null && clientIP != null) {
+ clientName = clientIP.getHostName();
+ }
+ return clientName;
+ }
+
+ /**
+ * @param name The hostname for the client.
+ */
+ public void setClientName(String name) {
+ clientName = name;
+ }
+
+ /**
+ * @return The server's hostname
+ */
+ public String getServerName() {
+ if (serverName == null && serverIP != null) {
+ serverName = serverIP.getHostName();
+ }
+ return serverName;
+ }
+
+ /**
+ * @param name The server's hostname
+ */
+ public void setServerName(String name) {
+ serverName = name;
+ }
+
+ public String toString() {
+
+ String server = null;
+ if (serverIP != null) {
+ server = serverIP.getHostAddress();
+ }
+
+ String client = null;
+ if (clientIP != null) {
+ client = clientIP.getHostAddress();
+ }
+
+ String expiration = null;
+ if (this.expiration != null) {
+ expiration = this.expiration.toString();
+ }
+
+ String s = clientId + " " + String.valueOf(flags) + " "
+ + client + " " + server
+ + " " + expiration + " " + signature
+ + " " + macro + " " + comment;
+ return s;
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/DhcpDatastore.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/DhcpDatastore.java
new file mode 100644
index 0000000000..a5064107c9
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/DhcpDatastore.java
@@ -0,0 +1,211 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1998-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+package com.sun.dhcpmgr.data;
+
+import java.io.Serializable;
+
+public class DhcpDatastore implements Serializable {
+
+ private String resource;
+ private String location;
+ private String config;
+ private int version;
+ private boolean enabled;
+
+ /**
+ * Simplest constructor.
+ */
+ public DhcpDatastore() {
+ this(null, null, null, -1, true);
+ } // constructor
+
+ /**
+ * Constructor.
+ * @param r the data store 'resource' value
+ * @param v the data store 'version' value
+ * @param e the data store 'enabled' value
+ */
+ public DhcpDatastore(String r, int v, boolean e) {
+ this(r, null, null, v, e);
+ } // constructor
+
+ /**
+ * Constructor.
+ * @param r the data store 'resource' value
+ * @param l the data store 'location' value
+ * @param a the data store 'config' value
+ */
+ public DhcpDatastore(String r, String l, String a) {
+ this(r, l, a, -1, true);
+ } // constructor
+
+ /**
+ * Constructor.
+ * @param r the data store 'resource' value
+ * @param l the data store 'location' value
+ * @param a the data store 'config' value
+ * @param v the data store 'version' value
+ */
+ public DhcpDatastore(String r, String l, String a, int v) {
+ this(r, l, a, v, true);
+ } // constructor
+
+ /**
+ * Constructor.
+ * @param r the data store 'resource' value
+ * @param l the data store 'location' value
+ * @param a the data store 'config' value
+ * @param v the data store 'version' value
+ * @param e the data store 'enabled' value
+ */
+ public DhcpDatastore(String r, String l, String a, int v, boolean e) {
+ setResource(r);
+ setLocation(l);
+ setConfig(a);
+ setVersion(v);
+ setEnabled(e);
+ } // constructor
+
+ /**
+ * Returns the data store 'resource' value.
+ * @returns the data store 'resource' value.
+ */
+ public String getResource() {
+ return resource;
+ } // getResource
+
+ /**
+ * Sets the data store 'resource' value.
+ * @param s the data store 'resource' value.
+ */
+ public void setResource(String s) {
+ resource = s;
+ } // setResource
+
+ /**
+ * Returns the data store 'location' value.
+ * @returns the data store 'location' value.
+ */
+ public String getLocation() {
+ return location;
+ } // getLocation
+
+ /**
+ * Sets the data store 'location' value.
+ * @param s the data store 'location' value.
+ */
+ public void setLocation(String s) {
+ location = s;
+ } // setLocation
+
+ /**
+ * Returns the data store 'config' value.
+ * @returns the data store 'config' value.
+ */
+ public String getConfig() {
+ return config;
+ } // getConfig
+
+ /**
+ * Sets the data store 'config' value.
+ * @param s the data store 'config' value.
+ */
+ public void setConfig(String s) {
+ config = s;
+ } // setConfig
+
+ /**
+ * Returns the data store 'version' value.
+ * @returns the data store 'version' value.
+ */
+ public int getVersion() {
+ return version;
+ } // getVersion
+
+ /**
+ * Sets the data store 'version' value.
+ * @param v the data store 'version' value.
+ */
+ public void setVersion(int v) {
+ version = v;
+ } // setVersion
+
+ /**
+ * Returns the data store 'enabled' value.
+ * @returns the data store 'enabled' value.
+ */
+ public boolean isEnabled() {
+ return enabled;
+ } // isEnables
+
+ /**
+ * Sets the data store 'enabled' value.
+ * @param e the data store 'enabled' value.
+ */
+ public void setEnabled(boolean e) {
+ enabled = e;
+ } // setEnabled
+
+ /**
+ * Indicates whether some other object "is equal" to this one.
+ * @param o the object with which to compare.
+ * @returns true if the objects are equal, false otherwise.
+ */
+ public boolean equals(Object o) {
+ if (o instanceof DhcpDatastore) {
+ DhcpDatastore d = (DhcpDatastore)o;
+
+ return (version == d.getVersion() &&
+ stringsEqual(resource, d.getResource()) &&
+ stringsEqual(location, d.getLocation()) &&
+ stringsEqual(config, d.getConfig()));
+
+ } else {
+ return false;
+ }
+
+ } // equals
+
+ /**
+ * Compares two strings for equality.
+ * @param s1 one of the strings to compare.
+ * @param s2 the other string to compare against.
+ * @returns true if the strings are equal, false otherwise.
+ */
+ private boolean stringsEqual(String s1, String s2) {
+ if (s1 == s2) {
+ return true;
+ } else if (s1 == null || s2 == null) {
+ return false;
+ } else {
+ return s1.equals(s2);
+ }
+ } // stringsEqual
+
+} // DhcpDatastore
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/DhcpResource.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/DhcpResource.java
new file mode 100644
index 0000000000..a4032c4c8f
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/DhcpResource.java
@@ -0,0 +1,104 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1998-1999 Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+package com.sun.dhcpmgr.data;
+
+import java.io.Serializable;
+
+/**
+ * DhcpResource represents a record in the /etc/defaults/dhcp file. Each
+ * record is either an option setting for the DHCP daemon or a comment.
+ */
+public class DhcpResource implements Serializable {
+ private String key;
+ private String value;
+ private boolean comment;
+
+ public DhcpResource() {
+ this("", "", false);
+ }
+
+ public DhcpResource(String key, String value) {
+ this(key, value, false);
+ }
+
+ public DhcpResource(String key, String value, boolean comment) {
+ this.key = key;
+ this.value = value;
+ this.comment = comment;
+ }
+
+ public boolean isComment() {
+ return comment;
+ }
+
+ public void setComment(boolean comment) {
+ this.comment = comment;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public void setKey(String key) {
+ this.key = key;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ /**
+ * Compare for equality against another object.
+ * @return true if the object is another DhcpResource instance and
+ * they are both comments or normal resources and the key value is
+ * identical. The value of 'value' is irrelevant. This is primarily
+ * used by the indexOf method of the ArrayList used to store the options
+ * in DhcpdOptions.
+ */
+ public boolean equals(Object o) {
+ if (o instanceof DhcpResource) {
+ DhcpResource r = (DhcpResource)o;
+ return (comment == r.isComment()) && key.equals(r.getKey());
+ } else {
+ return false;
+ }
+ }
+
+ public String toString() {
+ if (comment) {
+ return "#" + key;
+ } else {
+ return key + "=" + value;
+ }
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/DhcpdOptions.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/DhcpdOptions.java
new file mode 100644
index 0000000000..1085acc6ad
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/DhcpdOptions.java
@@ -0,0 +1,944 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.data;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.io.Serializable;
+import com.sun.dhcpmgr.data.qualifier.*;
+
+/**
+ * DhcpdOptions models the option settings for the in.dhcpd daemon. We read
+ * and write the option settings in the daemon's defaults file.
+ * Getter and setter methods are provided for each individual option.
+ */
+
+public class DhcpdOptions implements DhcpConfigOpts, Serializable {
+ /* The list of facility choices the user may select from for logging */
+ private static final Integer [] loggingFacilities = {
+ new Integer(0), new Integer(1), new Integer(2), new Integer(3),
+ new Integer(4), new Integer(5), new Integer(6), new Integer(7)
+ };
+
+ // Store the option settings here
+ private ArrayList options;
+
+ /**
+ * Dirty flag. Set after a clear or a set on an option.
+ */
+ private boolean dirty = false;
+
+ public DhcpdOptions() {
+ options = new ArrayList();
+ }
+
+ public DhcpdOptions(DhcpResource [] opts) {
+ options = new ArrayList(Arrays.asList(opts));
+ }
+
+ public Object clone() {
+ DhcpdOptions o = new DhcpdOptions();
+ o.options = (ArrayList)options.clone();
+ return o;
+ }
+
+ /**
+ * Test whether a particular option is set.
+ * @param key
+ * The name of the option to set.
+ * @return
+ * True if the option is set, otherwise false.
+ */
+ public boolean isSet(String key) {
+ return internalIsSet(key);
+ }
+
+ /**
+ * Set an option to a supplied value.
+ * @param key
+ * The name of the option to set.
+ * @param value
+ * The value of the option.
+ */
+ public void set(String key, String value) {
+ set(key, value, false);
+ }
+
+ /**
+ * Set an option to a supplied value, or add a comment line to the
+ * table.
+ * @param key
+ * The name of the option to set.
+ * @param value
+ * The value of the option.
+ * @param comment
+ * true if this is a comment, in which case value is ignored
+ * and the comment text is contained entirely in key.
+ */
+ public void set(String key, String value, boolean comment) {
+ internalSet(key, value, comment);
+
+ /*
+ * Ensure that the run mode is kept consistent with the
+ * configuration parameters. Mgt. tools rely on this!
+ */
+ if (key.equals(DSVC_CK_PATH)) {
+ internalSet(DSVC_CK_RUN_MODE, DSVC_CV_SERVER);
+ } else if (key.equals(DSVC_CK_RELAY_DESTINATIONS)) {
+ internalSet(DSVC_CK_RUN_MODE, DSVC_CV_RELAY);
+ } else if (key.equals(DSVC_CK_RESOURCE)) {
+ internalSet(DSVC_CK_RUN_MODE, DSVC_CV_SERVER);
+ }
+ }
+
+ /**
+ * Clear an option.
+ * @param key
+ * The name of the option to clear.
+ */
+ public void clear(String key) {
+ internalClear(key);
+
+ if (key.equals(DSVC_CK_RELAY_DESTINATIONS)) {
+ internalSet(DSVC_CK_RUN_MODE, DSVC_CV_SERVER);
+ }
+ }
+
+ /**
+ * Return the value of an option setting; null if it's not set.
+ * @param key
+ * The option whose value is to be retrieved.
+ * @return
+ * The value of an option setting; null if it's not set.
+ */
+ public String valueOf(String key) {
+ return internalValueOf(key);
+ }
+
+ // Test whether a particular option is set
+ private boolean internalIsSet(String key) {
+ DhcpResource res = new DhcpResource();
+ res.setKey(key);
+ return options.contains(res);
+ }
+
+ // Set an option to a supplied value
+ private void internalSet(String key, String value) {
+ internalSet(key, value, false);
+ }
+
+ /**
+ * Set an option to a supplied value, or add a comment line to the
+ * table.
+ * @param key The name of the option to set
+ * @param value The value of the option
+ * @param comment true if this is a comment, in which case value is ignored
+ * and the comment text is contained entirely in key.
+ */
+ private void internalSet(String key, String value, boolean comment) {
+ DhcpResource res = new DhcpResource(key, value, comment);
+ int i = options.indexOf(res);
+ if (i != -1) {
+ DhcpResource existing = (DhcpResource)options.get(i);
+
+ if (!existing.getValue().equals(res.getValue())) {
+ options.set(i, res);
+ dirty = true;
+ }
+ } else {
+ options.add(res);
+ dirty = true;
+ }
+ }
+
+ // Clear an option
+ private void internalClear(String key) {
+ DhcpResource res = new DhcpResource();
+ res.setKey(key);
+ int i = options.indexOf(res);
+ if (i != -1) {
+ options.remove(i);
+ dirty = true;
+ }
+ }
+
+ /**
+ * Return the value of an option setting; null if it's not set
+ * @param key The option whose value is to be retrieved
+ * @return The value of an option setting; null if it's not set
+ */
+ private String internalValueOf(String key) {
+ DhcpResource res = new DhcpResource();
+ res.setKey(key);
+ int i = options.indexOf(res);
+ if (i != -1) {
+ return ((DhcpResource)options.get(i)).getValue();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Return all of the option settings as an array
+ * @return An array of Objects which will all be DhcpResources
+ */
+ public Object [] getAll() {
+ return options.toArray();
+ }
+
+ /**
+ * Test to see whether or not the daemon is enabled.
+ * @return true if daemon is enabled, false if not.
+ */
+ public boolean isDaemonEnabled() {
+ return DSVC_CV_TRUE.equals(valueOf(DSVC_CK_DAEMON_ENABLED));
+ }
+
+ /**
+ * Set daemon enabled or disabled
+ * @param state true for enabled, false for disabled
+ */
+ public void setDaemonEnabled(boolean state) {
+ set(DSVC_CK_DAEMON_ENABLED, state ? DSVC_CV_TRUE : DSVC_CV_FALSE);
+ }
+
+ /**
+ * Set the DhcpDatastore attributes.
+ * @param resource the data store resource attribute
+ * @param location the data store location attribute
+ * @param config the data store config attribute
+ * @param version the data store version attribute
+ */
+ public void setDhcpDatastore(String resource, String location,
+ String config, int version) {
+ setResource(resource);
+ setPath(location);
+ setConfig(config);
+ setResourceVersion(version);
+ } // setDhcpDatastore
+
+ /**
+ * Set the DhcpDatastore attributes.
+ * @param datastore a datastore object whose attributes
+ * are the desired attributes.
+ */
+ public void setDhcpDatastore(DhcpDatastore datastore) {
+ setResource(datastore.getResource());
+ setPath(datastore.getLocation());
+ setConfig(datastore.getConfig());
+ setResourceVersion(datastore.getVersion());
+ } // setDhcpDatastore
+
+ /**
+ * Set the DhcpDatastore attributes.
+ * @param resource the data store resource attribute
+ * @param location the data store location attribute
+ * @param config the data store config attribute
+ * @param version the data store version attribute
+ */
+ public DhcpDatastore getDhcpDatastore() {
+ return (new DhcpDatastore(getResource(), getPath(),
+ getConfig(), getResourceVersion()));
+ } // getDhcpDatastore
+
+ /**
+ * Set the resource (aka data store) in which DHCP data is stored.
+ * This automatically also sets the run mode to server.
+ * @param s Unique name of resource
+ */
+ public void setResource(String s) {
+ set(DSVC_CK_RESOURCE, s);
+ }
+
+ /**
+ * Retrieve the name of the resource/data store used for DHCP.
+ * @return The unique name of the resource; null if not set
+ */
+ public String getResource() {
+ return valueOf(DSVC_CK_RESOURCE);
+ }
+
+ /**
+ * Set the version of the resource in which DHCP data is stored.
+ * @param i version number
+ */
+ public void setResourceVersion(int i) {
+ set(DSVC_CK_CONVER, Integer.toString(i));
+ }
+
+ /**
+ * Retrieve the version of the resource/data store used for DHCP.
+ * @return The version number or -1 if not valid.
+ */
+ public int getResourceVersion() {
+ try {
+ return Integer.parseInt(valueOf(DSVC_CK_CONVER));
+ } catch (NumberFormatException e) {
+ return -1;
+ }
+ }
+
+ /**
+ * Set the path within the resource in which to place the tables.
+ * For files, this is a Unix pathname; for NIS+, the directory name.
+ * @param s The path
+ */
+ public void setPath(String s) {
+ set(DSVC_CK_PATH, s);
+ }
+
+ /**
+ * Get the path used for data storage.
+ * @return The path within the resource; null if not set
+ */
+ public String getPath() {
+ return valueOf(DSVC_CK_PATH);
+ }
+
+ /**
+ * Set the config for the resource.
+ * @param s The config
+ */
+ public void setConfig(String s) {
+ if (s != null) {
+ set(DSVC_CK_RESOURCE_CONFIG, s);
+ } else {
+ clear(DSVC_CK_RESOURCE_CONFIG);
+ }
+ }
+
+ /**
+ * Get the config for data store.
+ * @return The config; null if not set
+ */
+ public String getConfig() {
+ return valueOf(DSVC_CK_RESOURCE_CONFIG);
+ }
+
+ /**
+ * Set the hosts resource (aka data store) in which host data is stored.
+ * @param s Unique name of resource
+ */
+ public void setHostsResource(String s) {
+ set(DSVC_CK_HOSTS_RESOURCE, s);
+ }
+
+ /**
+ * Retrieve the name of the resource/data store used for hosts.
+ * @return The unique name of the resource; null if not set
+ */
+ public String getHostsResource() {
+ return valueOf(DSVC_CK_HOSTS_RESOURCE);
+ }
+
+ /**
+ * Set the domain within the hosts resource in which to place the tables.
+ * For files resource, this value is meaningless.
+ * @param s The domain
+ */
+ public void setHostsDomain(String s) {
+ set(DSVC_CK_HOSTS_DOMAIN, s);
+ }
+
+ /**
+ * Get the domain used for hosts data storage.
+ * @return The domain within the resource; null if not set
+ */
+ public String getHostsDomain() {
+ return valueOf(DSVC_CK_HOSTS_DOMAIN);
+ }
+
+ /**
+ * Test whether BOOTP compatibility is enabled.
+ * @return true if BOOTP compatibility is enabled.
+ */
+ public boolean isBootpCompatible() {
+ return isSet(DSVC_CK_BOOTP_COMPAT);
+ }
+
+ /**
+ * Enable or disable BOOTP compatibility.
+ * @param state true if BOOTP compatibility is enabled, false if not.
+ * @param isAutomatic true if automatic allocation is allowed.
+ */
+ public void setBootpCompatible(boolean state, boolean isAutomatic) {
+ if (state) {
+ if (isAutomatic) {
+ set(DSVC_CK_BOOTP_COMPAT, DSVC_CV_AUTOMATIC);
+ } else {
+ set(DSVC_CK_BOOTP_COMPAT, DSVC_CV_MANUAL);
+ }
+ } else {
+ clear(DSVC_CK_BOOTP_COMPAT);
+ }
+ }
+
+ /**
+ * Test whether BOOTP compatibility is automatic or manual
+ * @return true if BOOTP compatibility is automatic.
+ */
+ public boolean isBootpAutomatic() {
+ return DSVC_CV_AUTOMATIC.equals(valueOf(DSVC_CK_BOOTP_COMPAT));
+ }
+
+ /**
+ * Test whether relay hop limit is set.
+ * @return true if the limit is set, false if default value is used.
+ */
+ public boolean isRelayHops() {
+ return isSet(DSVC_CK_RELAY_HOPS);
+ }
+
+ /**
+ * Set the relay hop limit.
+ * @param state true if hop limit should be set, false if not
+ * @param hops Number of hops to limit forwarding to
+ */
+ public void setRelayHops(boolean state, Integer hops) {
+ if (state) {
+ set(DSVC_CK_RELAY_HOPS, hops.toString());
+ } else {
+ clear(DSVC_CK_RELAY_HOPS);
+ }
+ }
+
+ /**
+ * Get the relay hop limit.
+ * @return The number of hops currently set, or null if this isn't set.
+ */
+ public Integer getRelayHops() {
+ String hops = valueOf(DSVC_CK_RELAY_HOPS);
+ if (hops != null) {
+ return new Integer(hops);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Test whether a network interface list is set; failure to set an interface
+ * list implies that all interfaces will be monitored.
+ * @return true if an interface list is set
+ */
+ public boolean isInterfaces() {
+ return isSet(DSVC_CK_INTERFACES);
+ }
+
+ /**
+ * Set the network interface list.
+ * @param state true if interface list is to be set, false if it should be
+ * cleared
+ * @param list A comma-separated list of interface names
+ */
+ public void setInterfaces(boolean state, String list) {
+ if (state) {
+ set(DSVC_CK_INTERFACES, list);
+ } else {
+ clear(DSVC_CK_INTERFACES);
+ }
+ }
+
+ /**
+ * Get the list of network interfaces.
+ * @return The comma-separated list of interfaces; null if not set
+ */
+ public String getInterfaces() {
+ return valueOf(DSVC_CK_INTERFACES);
+ }
+
+ /**
+ * Test whether ICMP address verification is enabled
+ * @return true if ICMP verification is performed
+ */
+ public boolean isICMPVerify() {
+ /*
+ * Use this double-inverse comparison so that the default behavior of
+ * ICMP enabled is handled correctly.
+ */
+ return !DSVC_CV_FALSE.equals(valueOf(DSVC_CK_ICMP_VERIFY));
+ }
+
+ /**
+ * Set ICMP verification
+ * @param state true if verification should be done, false otherwise
+ */
+ public void setICMPVerify(boolean state) {
+ set(DSVC_CK_ICMP_VERIFY, state ? DSVC_CV_TRUE : DSVC_CV_FALSE);
+ }
+
+ /**
+ * Test whether offer cache timeout is set
+ * @return true if it is set
+ */
+ public boolean isOfferTtl() {
+ return isSet(DSVC_CK_OFFER_CACHE_TIMEOUT);
+ }
+
+ /**
+ * Set offer cache timeout value
+ * @param state true if offer cache timeout value is set, false if server's
+ * default will be used instead
+ * @param time Number of seconds to hold offers in the cache
+ */
+ public void setOfferTtl(boolean state, Integer time) {
+ if (state) {
+ set(DSVC_CK_OFFER_CACHE_TIMEOUT, time.toString());
+ } else {
+ clear(DSVC_CK_OFFER_CACHE_TIMEOUT);
+ }
+ }
+
+ /**
+ * Get the offer cache timeout value
+ * @return timeout value set, or null if server default is used
+ */
+ public Integer getOfferTtl() {
+ String s = valueOf(DSVC_CK_OFFER_CACHE_TIMEOUT);
+ if (s != null) {
+ return new Integer(s);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Test whether server is running in relay mode
+ * @return true if running as relay
+ */
+ public boolean isRelay() {
+ return DSVC_CV_RELAY.equals(valueOf(DSVC_CK_RUN_MODE));
+ }
+
+ /**
+ * Set relay mode and server list
+ * @param state true if relay mode is desired, false for normal server
+ * @param servers list of servers to which requests should be forwarded
+ */
+ public void setRelay(boolean state, String servers) {
+ if (state) {
+ set(DSVC_CK_RELAY_DESTINATIONS, servers);
+ } else {
+ clear(DSVC_CK_RELAY_DESTINATIONS);
+ }
+ }
+
+ /**
+ * Get list of server targets for relay
+ * @return list of relay targets; null if not set
+ */
+ public String getRelay() {
+ return valueOf(DSVC_CK_RELAY_DESTINATIONS);
+ }
+
+ /**
+ * Test for server automatic reload of dhcptab
+ * @return true if server is rescanning dhcptab
+ */
+ public boolean isRescan() {
+ return isSet(DSVC_CK_RESCAN_INTERVAL);
+ }
+
+ /**
+ * Set the rescan interval
+ * @param state true if rescanning is enabled, false if not
+ * @param interval number of minutes between rescans
+ */
+ public void setRescan(boolean state, Integer interval) {
+ if (state) {
+ set(DSVC_CK_RESCAN_INTERVAL, interval.toString());
+ } else {
+ clear(DSVC_CK_RESCAN_INTERVAL);
+ }
+ }
+
+ /**
+ * Get the rescan interval
+ * @return the rescan interval in minutes, or null if rescan is not enabled
+ */
+ public Integer getRescan() {
+ String s = valueOf(DSVC_CK_RESCAN_INTERVAL);
+ if (s != null) {
+ return new Integer(s);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Test whether ownerip
+ * @return true if ownerip
+ */
+ public boolean isOwnerip() {
+ return isSet(DSVC_CK_OWNER_IP);
+ }
+
+ /**
+ * Set ownerip server list
+ * @param state true if ownerip is desired, false for normal server
+ * @param ownerips list of servers ownerips
+ */
+ public void setOwnerip(boolean state, String ownerips) {
+ if (state) {
+ set(DSVC_CK_OWNER_IP, ownerips);
+ } else {
+ clear(DSVC_CK_OWNER_IP);
+ }
+ }
+
+ /**
+ * Get list of server targets for ownerip
+ * @return list of ownerip targets; null if not set
+ */
+ public String getOwnerip() {
+ return valueOf(DSVC_CK_OWNER_IP);
+ }
+
+
+ /**
+ * Test for server dynamic DNS updates
+ * @return true if server is updating DNS
+ */
+ public boolean isDnsUpdated() {
+ return isSet(DSVC_CK_NSU_TIMEOUT);
+ }
+
+ /**
+ * Set the DNS update timeout value
+ * @param state true if DNS updates are enabled, false if not
+ * @param timeout number of seconds before timeout
+ */
+ public void setDnsTimeout(boolean state, Integer timeout) {
+ if (state) {
+ set(DSVC_CK_NSU_TIMEOUT, timeout.toString());
+ } else {
+ clear(DSVC_CK_NSU_TIMEOUT);
+ }
+ }
+
+ /**
+ * Get the DNS update timeout value
+ * @return the timeout in seconds, or null if DNS updates are not enabled
+ */
+ public Integer getDnsTimeout() {
+ String s = valueOf(DSVC_CK_NSU_TIMEOUT);
+ if (s != null) {
+ return new Integer(s);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Test for verbose logging mode
+ * @return true if verbose logging, false for normal
+ */
+ public boolean isVerbose() {
+ return DSVC_CV_TRUE.equals(valueOf(DSVC_CK_VERBOSE));
+ }
+
+ /**
+ * Set verbose logging mode
+ * @param state true for verbose, false for normal
+ */
+ public void setVerbose(boolean state) {
+ set(DSVC_CK_VERBOSE, state ? DSVC_CV_TRUE : DSVC_CV_FALSE);
+ }
+
+
+ /**
+ * Test for transaction logging mode.
+ * @return true if transaction logging is enabled
+ */
+ public boolean isLogging() {
+ return isSet(DSVC_CK_LOGGING_FACILITY);
+ }
+
+ /**
+ * Get the syslog facility number used for transaction logging
+ * @return facility number, which will be between 0 and 7
+ */
+ public Integer getLogging() {
+ String s = valueOf(DSVC_CK_LOGGING_FACILITY);
+ if (s != null) {
+ return new Integer(s);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Set transaction logging
+ * @param state true to enable transaction logging, false to disable
+ * @param value syslog facility number 0-7 used for logging
+ */
+ public void setLogging(boolean state, Integer value) {
+ if (state) {
+ set(DSVC_CK_LOGGING_FACILITY, value.toString());
+ } else {
+ clear(DSVC_CK_LOGGING_FACILITY);
+ }
+ }
+
+ /**
+ * Get the list of logging facility choices
+ * @return an array of facility numbers
+ */
+ public static Integer [] getLoggingFacilities() {
+ return loggingFacilities;
+ }
+
+ /**
+ * Return an indicator of whether the parameters may have been changed
+ * by a set() or a clear() since the last call to clearDirty().
+ *
+ * @return
+ * True if a set() or a clear() has occurred since the last call to
+ * clearDirty(), otherwise false.
+ */
+ public boolean isDirty() {
+ return dirty;
+ }
+
+ /**
+ * Set the dirty indicator to false. Any subsequent calls to set() or
+ * clear() may set the dirty flag.
+ */
+ public void clearDirty() {
+ dirty = false;
+ }
+
+ /**
+ * Get the parameters qualifier.
+ *
+ * @param key
+ * Parameters keyword.
+ * @return
+ * The qualifier for the parameter if one exists, otherwise null.
+ */
+ public Qualifier getQualifier(String key) {
+ Qualifier qualifier = null;
+
+ if (key.equals(DSVC_CK_BOOTP_COMPAT)) {
+ qualifier =
+ new QualifierImpl(DSVC_CK_BOOTP_COMPAT, false, false,
+ new QualifierStringEnum(
+ new String[] {
+ DSVC_CV_AUTOMATIC,
+ DSVC_CV_MANUAL
+ }));
+ } else if (key.equals(DSVC_CK_CACHE_TIMEOUT)) {
+ qualifier =
+ new QualifierImpl(DSVC_CK_CACHE_TIMEOUT, false, false,
+ new QualifierIntegerRange(
+ 0,
+ Integer.MAX_VALUE));
+
+ } else if (key.equals(DSVC_CK_CONVER)) {
+ qualifier =
+ new QualifierImpl(DSVC_CK_CONVER, true, false,
+ new QualifierInteger());
+
+ } else if (key.equals(DSVC_CK_DAEMON_ENABLED)) {
+ qualifier =
+ new QualifierImpl(DSVC_CK_DAEMON_ENABLED, true, false,
+ new QualifierBoolean(
+ DSVC_CV_TRUE,
+ DSVC_CV_FALSE));
+
+ } else if (key.equals(DSVC_CK_DBG_MEMORY_NET)) {
+ qualifier =
+ new QualifierImpl(DSVC_CK_DBG_MEMORY_NET, false, true,
+ new QualifierIntegerRange(
+ 0,
+ Integer.MAX_VALUE));
+
+ } else if (key.equals(DSVC_CK_DBG_PORT_OFFSET)) {
+ qualifier =
+ new QualifierImpl(DSVC_CK_DBG_PORT_OFFSET, false, true,
+ new QualifierIntegerRange(
+ 0,
+ Integer.MAX_VALUE));
+
+ } else if (key.equals(DSVC_CK_HOSTS_DOMAIN)) {
+ qualifier =
+ new QualifierImpl(DSVC_CK_HOSTS_DOMAIN, true, false,
+ new QualifierStringEnum(
+ new String[] {
+ DSVC_CV_NISPLUS,
+ DSVC_CV_DNS
+ }));
+ } else if (key.equals(DSVC_CK_HOSTS_RESOURCE)) {
+ qualifier =
+ new QualifierImpl(DSVC_CK_HOSTS_RESOURCE, true, false,
+ new QualifierStringEnum(
+ new String[] {
+ DSVC_CV_NISPLUS,
+ DSVC_CV_DNS,
+ DSVC_CV_FILES
+ }));
+ } else if (key.equals(DSVC_CK_ICMP_VERIFY)) {
+ qualifier =
+ new QualifierImpl(DSVC_CK_ICMP_VERIFY, false, false,
+ new QualifierBoolean(
+ DSVC_CV_TRUE,
+ DSVC_CV_FALSE));
+
+ } else if (key.equals(DSVC_CK_INTERFACES)) {
+ qualifier =
+ new QualifierImpl(DSVC_CK_INTERFACES, false, false,
+ new QualifierArray(
+ new QualifierString()));
+
+ } else if (key.equals(DSVC_CK_LEASE_MIN_LRU)) {
+ qualifier =
+ new QualifierImpl(DSVC_CK_LEASE_MIN_LRU, false, true,
+ new QualifierIntegerRange(
+ 0,
+ Integer.MAX_VALUE));
+
+ } else if (key.equals(DSVC_CK_LOGGING_FACILITY)) {
+ qualifier =
+ new QualifierImpl(DSVC_CK_LOGGING_FACILITY, false, false,
+ new QualifierIntegerRange(
+ DSVC_CV_LOGGING_FACILITY_MIN,
+ DSVC_CV_LOGGING_FACILITY_MAX));
+
+ } else if (key.equals(DSVC_CK_MAX_CLIENTS)) {
+ qualifier =
+ new QualifierImpl(DSVC_CK_MAX_CLIENTS, false, true,
+ new QualifierIntegerRange(
+ -1,
+ Integer.MAX_VALUE));
+
+ } else if (key.equals(DSVC_CK_MAX_THREADS)) {
+ qualifier =
+ new QualifierImpl(DSVC_CK_MAX_THREADS, false, true,
+ new QualifierIntegerRange(
+ -1,
+ Integer.MAX_VALUE));
+
+ } else if (key.equals(DSVC_CK_OFFER_CACHE_TIMEOUT)) {
+ qualifier =
+ new QualifierImpl(DSVC_CK_OFFER_CACHE_TIMEOUT, false, false,
+ new QualifierIntegerRange(
+ 0,
+ Integer.MAX_VALUE));
+
+ } else if (key.equals(DSVC_CK_PATH)) {
+ qualifier =
+ new QualifierImpl(DSVC_CK_PATH, true, false,
+ new QualifierString());
+
+ } else if (key.equals(DSVC_CK_RELAY_DESTINATIONS)) {
+ qualifier =
+ new QualifierImpl(DSVC_CK_RELAY_DESTINATIONS, false, false,
+ new QualifierArray(
+ new QualifierOr(
+ new QualifierFQDN(),
+ new QualifierIPv4())));
+
+ } else if (key.equals(DSVC_CK_RELAY_HOPS)) {
+ qualifier =
+ new QualifierImpl(DSVC_CK_RELAY_HOPS, false, false,
+ new QualifierIntegerRange(
+ 0,
+ Integer.MAX_VALUE));
+
+ } else if (key.equals(DSVC_CK_RESCAN_INTERVAL)) {
+ qualifier =
+ new QualifierImpl(DSVC_CK_RESCAN_INTERVAL, false, false,
+ new QualifierIntegerRange(
+ 0, Integer.MAX_VALUE));
+
+ } else if (key.equals(DSVC_CK_OWNER_IP)) {
+ qualifier =
+ new QualifierImpl(DSVC_CK_OWNER_IP, false, false,
+ new QualifierArray(
+ new QualifierIPv4()));
+
+ } else if (key.equals(DSVC_CK_RESOURCE)) {
+ qualifier =
+ new QualifierImpl(DSVC_CK_RESOURCE, true, false,
+ new QualifierString());
+
+ } else if (key.equals(DSVC_CK_RESOURCE_CONFIG)) {
+ qualifier =
+ new QualifierImpl(DSVC_CK_RESOURCE_CONFIG, true, false,
+ new QualifierString());
+
+ } else if (key.equals(DSVC_CK_RUN_MODE)) {
+ qualifier =
+ new QualifierImpl(DSVC_CK_RUN_MODE, true, false,
+ new QualifierStringEnum(
+ new String[] {
+ DSVC_CV_SERVER,
+ DSVC_CV_RELAY
+ }));
+ } else if (key.equals(DSVC_CK_RENOG_INTERVAL)) {
+ qualifier =
+ new QualifierImpl(DSVC_CK_RENOG_INTERVAL, false, false,
+ new QualifierIntegerRange(
+ 0,
+ Integer.MAX_VALUE));
+
+ } else if (key.equals(DSVC_CK_NSU_TIMEOUT)) {
+ qualifier =
+ new QualifierImpl(DSVC_CK_NSU_TIMEOUT, false, false,
+ new QualifierIntegerRange(
+ -1,
+ Integer.MAX_VALUE));
+
+ } else if (key.equals(DSVC_CK_VERBOSE)) {
+ qualifier =
+ new QualifierImpl(DSVC_CK_VERBOSE, false, false,
+ new QualifierBoolean(
+ DSVC_CV_TRUE,
+ DSVC_CV_FALSE));
+ }
+
+ return qualifier;
+ }
+
+ /**
+ * Convert this object to a String representation
+ */
+ public String toString() {
+ StringBuffer b = new StringBuffer();
+ for (int i = 0; i < options.size(); ++i) {
+ DhcpResource res = (DhcpResource)options.get(i);
+ b.append(res.getKey());
+ String s = res.getValue();
+ if (s != null) {
+ b.append('=');
+ b.append(s);
+ }
+ b.append('\n');
+ }
+ return b.toString();
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/DhcptabRecord.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/DhcptabRecord.java
new file mode 100644
index 0000000000..e0c6508acd
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/DhcptabRecord.java
@@ -0,0 +1,98 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 1998-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+package com.sun.dhcpmgr.data;
+
+import java.io.Serializable;
+
+public abstract class DhcptabRecord implements Serializable, Cloneable {
+ public static final String MACRO = "m";
+ public static final String OPTION = "s";
+
+ public static final String DEFAULT_SIGNATURE = "0";
+
+ protected String key;
+ protected String flag;
+ protected String value;
+ protected String signature = DEFAULT_SIGNATURE;
+
+ // Serialization id for this class
+ static final long serialVersionUID = -1734667901914072052L;
+
+ public DhcptabRecord() {
+ key = flag = value = "";
+ signature = DEFAULT_SIGNATURE;
+ }
+
+ public DhcptabRecord(String k, String f, String v) {
+ this(k, f, v, DEFAULT_SIGNATURE);
+ }
+
+ public DhcptabRecord(String k, String f, String v, String sig) {
+ key = k;
+ flag = f;
+ value = v;
+ signature = sig;
+ }
+
+ public void setKey(String k) throws ValidationException {
+ key = k;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public void setFlag(String f) throws ValidationException {
+ flag = f;
+ }
+
+ public String getFlag() {
+ return flag;
+ }
+
+ public void setValue(String v) throws ValidationException {
+ value = v;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setSignature(String sig) {
+ signature = sig;
+ }
+
+ public String getSignature() {
+ return signature;
+ }
+
+ public String toString() {
+ return new String(key + " " + flag + " " + signature + " " + value);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/ExportHeader.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/ExportHeader.java
new file mode 100644
index 0000000000..7089a71162
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/ExportHeader.java
@@ -0,0 +1,122 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2001-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+package com.sun.dhcpmgr.data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * This class defines the header for the export file.
+ */
+public class ExportHeader implements Serializable {
+
+ /**
+ * The name of the server from which the data originated.
+ */
+ private String server;
+ /**
+ * Date of export
+ */
+ private Date date;
+ /**
+ * User who requested export
+ */
+ private String user;
+ /**
+ * Number of records in this file.
+ */
+ private int recCount;
+ /**
+ * Networks exported in this file
+ */
+ private Network [] networks;
+
+ // Serialization id for this class
+ static final long serialVersionUID = -3581829760827739278L;
+
+ /**
+ * Simple constructor.
+ * @param server name of the server from which the server was exported
+ * @param user name of the user who performed the export
+ * @param recCount number of records which will be exported
+ * @param networks list of networks exported
+ */
+ public ExportHeader(String server, String user, int recCount,
+ Network [] networks) {
+
+ this.server = server;
+ this.user = user;
+ this.recCount = recCount;
+ this.networks = networks;
+ date = new Date();
+
+ } // constructor
+
+ /**
+ * Get the server value.
+ * @return returns the server name
+ */
+ public String getServer() {
+
+ return server;
+
+ } // getServer
+
+ /**
+ * Retrieve exporting user name
+ * @return name of user
+ */
+ public String getUser() {
+ return user;
+ }
+
+ /**
+ * Retrieve date of export
+ * @return date & time of export
+ */
+ public Date getDate() {
+ return date;
+ }
+
+ /**
+ * Retrieve the number of records in the file.
+ * @return the number of records contained.
+ */
+ public int getRecCount() {
+ return recCount;
+ }
+
+ /**
+ * Retrieve the list of networks which are exported in this file.
+ * @return An array of networks
+ */
+ public Network [] getNetworks() {
+ return networks;
+ }
+} // ExportHeader
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/IPAddress.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/IPAddress.java
new file mode 100644
index 0000000000..595ef69e8a
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/IPAddress.java
@@ -0,0 +1,251 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 1998-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.data;
+
+import java.net.InetAddress;
+import java.util.StringTokenizer;
+import java.io.Serializable;
+import java.text.MessageFormat;
+
+/**
+ * This class provides a container for holding IP Addresses. It is different
+ * from java.net.InetAddress in that it allows for constructing an arbitrary IP
+ * address, rather than forcing you to use InetAddress.getByName, which can
+ * generate all sorts of nasty exception conditions in applets, especially when
+ * doing a configuration-type app which handles addresses which are not
+ * necessarily resolvable in any nameservice.
+ */
+public class IPAddress implements Cloneable, Serializable {
+ private byte [] bytes = new byte[] { 0, 0, 0, 0 };
+
+ // Serialization id for this class
+ static final long serialVersionUID = -7439646692550395242L;
+
+ /**
+ * Construct an empty address
+ */
+ public IPAddress() {
+ }
+
+ /**
+ * Construct an address for the dotted-decimal <code>String</code> supplied.
+ *
+ */
+ public IPAddress(String s) throws ValidationException {
+ try {
+ // If the input looks like a valid format, try parsing it
+ char [] chars = s.toCharArray();
+ int dots = 0; // Count number of periods
+ for (int i = 0; i < chars.length; ++i) {
+ if (Character.isDigit(chars[i])) {
+ continue;
+ }
+ if ((chars[i] == '.')) {
+ if (++dots > 3) {
+ // Too many; we're done
+ throwException(s);
+ }
+ continue;
+ }
+ /*
+ * Can't be an address, so let InetAdddress try to resolve it
+ * as a name
+ */
+ InetAddress a = InetAddress.getByName(s);
+ bytes = a.getAddress();
+ return;
+ }
+ // Looks like an IP address; parse it
+ StringTokenizer st = new StringTokenizer(s, ".");
+ int b = 0;
+ while (st.hasMoreTokens()) {
+ /*
+ * Byte won't parse anything larger than 127 since it thinks
+ * everything's signed, so use Short instead
+ */
+ short shortVal = Short.parseShort(st.nextToken());
+ if (shortVal > 255 || shortVal < 0) {
+ throwException(s);
+ }
+ bytes[b++] = (byte)shortVal;
+ }
+ if (b < 4) {
+ // Not a fully specified address; don't read caller's mind
+ throwException(s);
+ }
+ } catch (ValidationException e) {
+ // Just re-throw it
+ throw e;
+ } catch (Throwable e) {
+ // Convert other exceptions to ValidationException
+ throw new ValidationException(e.getMessage());
+ }
+ }
+
+ /**
+ * Construct an IPAddress from an InetAddress
+ * @param a The InetAddress to convert
+ */
+ public IPAddress(InetAddress a) {
+ bytes = a.getAddress();
+ }
+
+ /**
+ * Construct an IP address from an arbitrary 32-bit value
+ * @param addr The value to use
+ */
+ public IPAddress(int addr) {
+ bytes = new byte[4];
+ for (int i = 0; i < 4; ++i) {
+ // Careful; must mask to fight sign-extension
+ bytes[i] = (byte)((addr >> (8 * (3 - i))) & 0xff);
+ }
+ }
+
+ public String toString() {
+ StringBuffer b = new StringBuffer();
+ for (int i = 0; i < 4; ++i) {
+ if (i != 0) {
+ b.append('.');
+ }
+ // Careful; must mask to fight sign-extension
+ b.append((int)bytes[i] & 0xff);
+ }
+ return b.toString();
+ }
+
+ /**
+ * Convert this address to an <code>int</code>
+ * @return The address as an <code>int</code>
+ */
+ public int intValue() {
+ int i = 0;
+ for (int j = 0; j < 4; ++j) {
+ // Careful; must mask to fight sign-extension
+ i |= ((int)bytes[j] & 0xff) << (8 * (3 - j));
+ }
+ return i;
+ }
+
+ /**
+ * Try to convert to a name
+ * @return The hostname for this address, if one can be found. Otherwise,
+ * dotted-decimal form of this address is returned.
+ */
+ public String getHostName() {
+ try {
+ return InetAddress.getByName(toString()).getHostName();
+ } catch (Throwable e) {
+ return toString();
+ }
+ }
+
+ /**
+ * Provide the individual bytes of the address a la InetAddress
+ * @return A byte array of the address
+ */
+ public byte [] getAddress() {
+ return bytes;
+ }
+
+ /**
+ * @return the dotted-decimal string representing this address
+ */
+ public String getHostAddress() {
+ return toString();
+ }
+
+ /**
+ * Compare this IP address to either another IP address or a
+ * <code>java.net.InetAddress</code>.
+ * @return <code>true</code> if the addresses are the same.
+ */
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ }
+
+ byte [] ba;
+
+ if (obj instanceof InetAddress) {
+ ba = ((InetAddress)obj).getAddress();
+ } else if (obj instanceof IPAddress) {
+ ba = ((IPAddress)obj).getAddress();
+ } else {
+ return false;
+ }
+
+ if (ba.length != bytes.length) {
+ return false;
+ }
+
+ for (int i = 0; i < bytes.length; ++i) {
+ if (ba[i] != bytes[i]) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Make a copy of this address
+ * @return a new IPAddress
+ */
+ public Object clone() {
+ IPAddress a = new IPAddress();
+ a.bytes = (byte [])bytes.clone();
+ return a;
+ }
+
+ /**
+ * Retrieve the IP address as a number suitable for arithmetic operations.
+ * We use a <code>long</code> rather than an <code>int</code> in order to
+ * be able to treat it as an unsigned value, since all Java types are
+ * signed.
+ * @return The IP address as a <code>long</code>.
+ */
+ public long getBinaryAddress() {
+ byte [] bytes = getAddress();
+ long result = 0;
+ for (int i = 0; i < bytes.length; ++i) {
+ // Defeat sign extension with cast & mask
+ result |= ((long)bytes[i] & 0xff) << (24-(i*8));
+ }
+ return result;
+ }
+
+ private void throwException(String s)
+ throws ValidationException {
+ Object [] args = { s };
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString("invalid_ip_address"));
+ String msg = form.format(args);
+ throw new ValidationException(msg);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/IPInterface.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/IPInterface.java
new file mode 100644
index 0000000000..dee117887e
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/IPInterface.java
@@ -0,0 +1,85 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1998-1999 Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+package com.sun.dhcpmgr.data;
+
+import java.io.Serializable;
+
+/**
+ * This class represents an IP network interface on the server. It consists
+ * of the interface's device name (short form) and the network it's attached to.
+ */
+public class IPInterface implements Serializable {
+ private String name;
+ private Network net;
+
+ /**
+ * Construct a new interface with the given name, address, and subnet mask.
+ * @param name Interface name
+ * @param addr The address
+ * @param mask The subnet mask
+ */
+ public IPInterface(String name, IPAddress addr, IPAddress mask) {
+ this.name = name;
+ net = new Network(addr, mask);
+ }
+
+ /**
+ * Construct a new interface with the given name, address, and subnet mask.
+ * @param name The interface name
+ * @param addr The IP address as a dotted-decimal <code>String</code>
+ * @param mask The subnet mask as a dotted-decimal <code>String</code>
+ */
+ public IPInterface(String name, String addr, String mask)
+ throws ValidationException {
+ this.name = name;
+ net = new Network(addr, mask);
+ }
+
+ /**
+ * @return Interface's device name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Set the interface's device name
+ * @param name Name of interface
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * @return Network the device is attached to.
+ */
+ public Network getNetwork() {
+ return net;
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/IPOptionValue.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/IPOptionValue.java
new file mode 100644
index 0000000000..c276707924
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/IPOptionValue.java
@@ -0,0 +1,146 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 1998-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.data;
+
+import java.net.*;
+import java.util.*;
+
+public class IPOptionValue extends OptionValue {
+ private String name;
+ private Vector addrs;
+ private boolean valid;
+
+ // Serialization id for this class
+ static final long serialVersionUID = -7894568061270794048L;
+
+ protected IPOptionValue(String name) {
+ this.name = name;
+ addrs = null;
+ valid = false;
+ }
+
+ public void setValue(Object value) throws ValidationException {
+ // Find option in option definition table in order to validate the data
+ Option option = OptionsTable.getTable().get(name);
+ if (option == null) {
+ Object [] args = { name };
+ throwException("invalid_option", args);
+ }
+ Vector newAddrs = new Vector();
+ if (value instanceof String) {
+ if (((String)value).length() == 0) {
+ // Empty strings aren't acceptable
+ Object [] args = { name,
+ Option.getTypeDhcptabString(option.getType()) };
+ throwException("invalid_option_value", args);
+ }
+ /*
+ * Break string apart at whitespace and use it to construct
+ * a vector of IPAddresses
+ */
+ StringTokenizer st = new StringTokenizer((String)value, " ");
+ while (st.hasMoreTokens()) {
+ newAddrs.addElement(new IPAddress(st.nextToken()));
+ }
+ } else if (value instanceof InetAddress) {
+ newAddrs.addElement(value);
+ } else if (value instanceof IPAddress) {
+ newAddrs.addElement(value);
+ } else if (!(value instanceof Vector)) {
+ // Can't handle anything else but a vector of addresses
+ Object [] args = { name,
+ Option.getTypeDhcptabString(option.getType()) };
+ throwException("invalid_option_value", args);
+ } else {
+ // Make sure vector only contains InetAddresses or IPAddresses
+ newAddrs = (Vector)value;
+ for (Enumeration en = newAddrs.elements(); en.hasMoreElements(); ) {
+ Object o = en.nextElement();
+ if (!(o instanceof InetAddress) && !(o instanceof IPAddress)) {
+ Object [] args = { name,
+ Option.getTypeDhcptabString(option.getType()) };
+ throwException("invalid_option_value", args);
+ }
+ }
+ }
+ if ((newAddrs.size() % option.getGranularity()) != 0) {
+ Object [] args = { name,
+ Integer.toString(option.getGranularity()) };
+ throwException("invalid_option_granularity", args);
+ }
+ if ((option.getMaximum() != 0) &&
+ (newAddrs.size() / option.getGranularity()) >
+ option.getMaximum()) {
+ Object [] args = { name, Integer.toString(option.getMaximum()) };
+ throwException("invalid_option_maximum", args);
+ }
+
+ addrs = newAddrs;
+ valid = true;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getValue() {
+ if (addrs == null || addrs.size() == 0) {
+ return "";
+ }
+ StringBuffer buf = new StringBuffer();
+ for (Enumeration en = addrs.elements(); en.hasMoreElements(); ) {
+ Object o = en.nextElement();
+ if (buf.length() != 0) {
+ buf.append(' ');
+ }
+ if (o instanceof IPAddress) {
+ buf.append(((IPAddress)o).getHostAddress());
+ } else {
+ buf.append(((InetAddress)o).getHostAddress());
+ }
+ }
+ return buf.toString();
+ }
+
+ public String toString() {
+ return (getName() + "=" + getValue());
+ }
+
+ public boolean isValid() {
+ return valid;
+ }
+
+ public Object clone() {
+ IPOptionValue v = new IPOptionValue(name);
+ if (addrs != null) {
+ v.addrs = (Vector)addrs.clone();
+ }
+ v.valid = valid;
+ return v;
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/IncludeOptionValue.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/IncludeOptionValue.java
new file mode 100644
index 0000000000..f24b85c8e3
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/IncludeOptionValue.java
@@ -0,0 +1,79 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 1999-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.data;
+
+import java.util.Vector;
+
+public class IncludeOptionValue extends OptionValue {
+ private String value;
+ private boolean valid;
+
+ // Serialization id for this class
+ static final long serialVersionUID = -1764994428959712447L;
+
+ protected IncludeOptionValue() {
+ value = "";
+ valid = false;
+ }
+
+ public void setValue(Object value) throws ValidationException {
+ if (value instanceof String) {
+ if (((String)value).length() == 0) {
+ // Empty values are not acceptable
+ throwException("invalid_include_option", null);
+ }
+ this.value = (String)value;
+ valid = true;
+ } else {
+ setValue(value.toString());
+ }
+ }
+
+ public String getName() {
+ return "Include";
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public String toString() {
+ return (getName() + "=" + getValue());
+ }
+
+ public boolean isValid() {
+ return valid;
+ }
+
+ public Object clone() {
+ IncludeOptionValue v = new IncludeOptionValue();
+ v.value = value;
+ v.valid = valid;
+ return v;
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/Macro.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/Macro.java
new file mode 100644
index 0000000000..534a35b5af
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/Macro.java
@@ -0,0 +1,421 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 1998-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+package com.sun.dhcpmgr.data;
+
+import java.util.*;
+import java.text.MessageFormat;
+
+/**
+ * Macro is a simple data class which encapsulates a macro record in
+ * the dhcptab. See dhcptab(4) for the gory details on macros.
+ * @see DhcptabRecord
+ * @see Option
+ */
+public class Macro extends DhcptabRecord implements Cloneable {
+
+ private boolean valueClean = false;
+ private Vector options;
+
+ // Definition for attribute limits
+ public final static short MAX_NAME_SIZE = 128;
+
+ // Value used to edit a boolean symbol of a macro
+ public final static String BOOLEAN_EDIT_VALUE = "_NULL_VALUE_";
+
+ // Serialization id for this class
+ static final long serialVersionUID = -5255083189703724489L;
+
+ public Macro() {
+ super("", DhcptabRecord.MACRO, "");
+ options = new Vector();
+ }
+
+ public Macro(String name) throws ValidationException {
+ this();
+ setKey(name);
+ }
+
+ public Macro(String name, String expansion) throws ValidationException {
+ this(name, expansion, DhcptabRecord.DEFAULT_SIGNATURE);
+ }
+
+ public Macro(String name, String expansion, String signature)
+ throws ValidationException {
+ this();
+ setKey(name);
+ setValue(expansion, false, false);
+ setSignature(signature);
+ }
+
+ public void setKey(String name) throws ValidationException {
+ if (name.length() > MAX_NAME_SIZE) {
+ Object [] args = new Object[1];
+ args[0] = new Short(MAX_NAME_SIZE);
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString("macro_key_length"));
+ String msg = form.format(args);
+ throw new ValidationException(msg);
+ }
+ super.setKey(name);
+ }
+
+ public void setValue(String expansion, boolean edit, boolean validate)
+ throws ValidationException {
+
+ StringBuffer symbol = new StringBuffer();
+ StringBuffer value = new StringBuffer();
+ boolean inQuote = false;
+ boolean inEscape = false;
+ char c;
+
+ // State list for parsing machine
+ int START = 0;
+ int NAME = 1;
+ int VALUE = 2;
+ int state = !edit ? START : NAME;
+
+ for (int i = 0; i < expansion.length(); ++i) {
+ c = expansion.charAt(i);
+ if (!edit && (state == START)) {
+ // Start of expansion
+ if (c != ':' || expansion.length() == 1) {
+ Object [] args = new Object[1];
+ args[0] = getKey();
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString("mac_syntax_error"));
+ String msg = form.format(args);
+ throw new ValidationException(msg);
+ }
+ state = NAME;
+ } else if (state == NAME) {
+ // Name of symbol
+ if (c == '=') {
+ state = VALUE;
+ } else if (!edit && (c == ':')) {
+ if (!validate) {
+ storeOption(symbol.toString(), value.toString());
+ } else {
+ setOption(symbol.toString(), value.toString(), false);
+ }
+ symbol.setLength(0);
+ value.setLength(0);
+ state = NAME;
+ } else {
+ symbol.append(c);
+ }
+ } else if (state == VALUE) {
+ // Value of symbol
+ if (inEscape) {
+ value.append(c);
+ inEscape = false;
+ } else if (c == '\\') {
+ inEscape = true;
+ } else if (c == '"') {
+ inQuote = !inQuote;
+ value.append(c);
+ } else if (inQuote) {
+ value.append(c);
+ } else if (!edit && (c == ':')) {
+ if (!validate) {
+ storeOption(symbol.toString(), value.toString());
+ } else {
+ setOption(symbol.toString(), value.toString(), false);
+ }
+ symbol.setLength(0);
+ value.setLength(0);
+ state = NAME;
+ } else {
+ value.append(c);
+ }
+ }
+ }
+
+ if (edit) {
+ setOption(symbol.toString(), value.toString(), true);
+
+ valueClean = false;
+ } else {
+ if (state != NAME) {
+ Object [] args = new Object[1];
+ args[0] = getKey();
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString("mac_syntax_error"));
+ String msg = form.format(args);
+ throw new ValidationException(msg);
+ }
+ super.setValue(expansion);
+ valueClean = true;
+ }
+ }
+
+ public void editOption(String expansion)
+ throws ValidationException {
+ setValue(expansion, true, false);
+ }
+
+ /**
+ * Common method used to set an option value for the macro.
+ * @param symbol name of the option
+ * @param value the option value(if any)
+ * @param edit flag indicating that this is an edit of and existing option
+ */
+ private void setOption(String symbol, String value, boolean edit)
+ throws ValidationException {
+
+ int index = getOptionIndex(symbol);
+
+ if (value.length() == 0) {
+
+ if (edit) {
+ if (index == -1) {
+ Object [] args = new Object[1];
+ args[0] = symbol;
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString("invalid_option"));
+ String msg = form.format(args);
+ throw new ValidationException(msg);
+ }
+ deleteOptionAt(index);
+ } else {
+ OptionValue option =
+ OptionValueFactory.newOptionValue(symbol, new String());
+
+ if (option instanceof BogusOptionValue) {
+ Object [] args = new Object[1];
+ args[0] = symbol;
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString("invalid_option"));
+ String msg = form.format(args);
+ throw new ValidationException(msg);
+ } else if (!(option instanceof BooleanOptionValue)) {
+ Object [] args = new Object[1];
+ args[0] = symbol;
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString("not_boolean_option"));
+ String msg = form.format(args);
+ throw new ValidationException(msg);
+ }
+ }
+
+ } else if (edit && value.equals(BOOLEAN_EDIT_VALUE)) {
+
+ OptionValue option =
+ OptionValueFactory.newOptionValue(symbol, new String());
+
+ if (option instanceof BogusOptionValue) {
+ Object [] args = new Object[1];
+ args[0] = symbol;
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString("invalid_option"));
+ String msg = form.format(args);
+ throw new ValidationException(msg);
+ } else if (!(option instanceof BooleanOptionValue)) {
+ Object [] args = new Object[1];
+ args[0] = symbol;
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString("not_boolean_option"));
+ String msg = form.format(args);
+ throw new ValidationException(msg);
+ }
+
+ if (index == -1) {
+ storeOption(option);
+ } else {
+ // nothing to do - option already turned on
+ }
+
+ } else {
+
+ OptionValue option =
+ OptionValueFactory.newOptionValue(symbol);
+ if (option instanceof BogusOptionValue) {
+ Object [] args = new Object[1];
+ args[0] = symbol;
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString("invalid_option"));
+ String msg = form.format(args);
+ throw new ValidationException(msg);
+ } else if (edit && option instanceof BooleanOptionValue) {
+ Object [] args = new Object[1];
+ args[0] = symbol;
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString("boolean_option"));
+ String msg = form.format(args);
+ throw new ValidationException(msg);
+ }
+
+ if (index == -1) {
+ storeOption(option);
+ } else {
+ option = getOptionAt(index);
+ }
+ option.setValue(value);
+
+ }
+ }
+
+ public void storeOption(OptionValue option)
+ throws ValidationException {
+ options.addElement(option);
+ }
+
+ public void storeOption(String option, Object value)
+ throws ValidationException {
+ options.addElement(OptionValueFactory.newOptionValue(option, value));
+ }
+
+ // Useful for creating options when standard code values are known.
+ //
+ // XXX
+ // NOTE!!! Do not use this method for now. We need to resolve whether or
+ // not all standard options are going to have unique codes. Today, they do
+ // not. We include internal options as standard and set their codes to 0.
+ //
+ public void storeOption(int code, Object value) throws ValidationException {
+ options.addElement(
+ OptionValueFactory.newOptionValue(StandardOptions.nameForCode(code),
+ value));
+ }
+
+ public String getValue() {
+ boolean first;
+ if (!valueClean) {
+ // Construct a new value
+ StringBuffer buf = new StringBuffer();
+ for (Enumeration e = options.elements(); e.hasMoreElements(); ) {
+ OptionValue v = (OptionValue)e.nextElement();
+ if (v == null) {
+ continue; // Ignore an empty position
+ }
+ buf.append(':');
+ buf.append(v.toString());
+ }
+ buf.append(':');
+ try {
+ super.setValue(buf.toString());
+ } catch (ValidationException ex) {
+ // Shouldn't happen; ignore it
+ }
+ valueClean = true;
+ }
+ return super.getValue();
+ }
+
+ public Enumeration elements() {
+ return options.elements();
+ }
+
+ public OptionValue [] getOptions() {
+ OptionValue [] optArray = new OptionValue[options.size()];
+ options.copyInto(optArray);
+ return optArray;
+ }
+
+ public OptionValue getOption(String name) {
+ for (Enumeration en = options.elements(); en.hasMoreElements(); ) {
+ OptionValue v = (OptionValue)en.nextElement();
+ if (name.equals(v.getName())) {
+ return v;
+ }
+ }
+ return null;
+ }
+
+ public int getOptionIndex(String name) {
+ int index = -1;
+ boolean found = false;
+
+ Enumeration en = options.elements();
+ while (en.hasMoreElements() && !found) {
+ index++;
+ OptionValue v = (OptionValue)en.nextElement();
+ if (name.equals(v.getName())) {
+ found = true;
+ }
+ }
+ if (!found) {
+ index = -1;
+ }
+
+ return index;
+ }
+
+ public OptionValue getOptionAt(int index) {
+ return (OptionValue)options.elementAt(index);
+ }
+
+ public void setOptionAt(OptionValue v, int index) {
+ if (index >= options.size()) {
+ options.setSize(index + 1); // Grow vector if necessary
+ }
+ options.setElementAt(v, index);
+ }
+
+ public int optionCount() {
+ return options.size();
+ }
+
+ public void deleteOptionAt(int index) {
+ if (index >= options.size()) {
+ return;
+ }
+ options.removeElementAt(index);
+ }
+
+ public void insertOptionAt(OptionValue v, int index) {
+ options.insertElementAt(v, index);
+ }
+
+ // Make a copy of this macro
+ public Object clone() {
+ Macro m = new Macro();
+ m.key = key;
+ m.options = new Vector();
+ for (Enumeration en = options.elements(); en.hasMoreElements(); ) {
+ OptionValue v = (OptionValue)en.nextElement();
+ m.options.addElement((OptionValue)v.clone());
+ }
+ m.signature = signature;
+ return m;
+ }
+
+ public String toString() {
+ return (getKey() + " m " + getValue());
+ }
+
+ // Verify that the options contained in this macro are all valid
+ public void validate() throws ValidationException {
+ for (Enumeration en = options.elements(); en.hasMoreElements(); ) {
+ OptionValue v = (OptionValue)en.nextElement();
+ if (!v.isValid()) {
+ throw new ValidationException(v.getName());
+ }
+ }
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/Makefile b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/Makefile
new file mode 100644
index 0000000000..d8e5af00e4
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/Makefile
@@ -0,0 +1,118 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 1999-2002 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/Makefile
+#
+
+CONFIG_INTERFACE = DhcpConfigOpts.java
+
+CLASSFILES = Macro.class \
+ OptionValueFactory.class \
+ AsciiOptionValue.class \
+ BogusOptionValue.class \
+ BooleanOptionValue.class \
+ IncludeOptionValue.class \
+ IPOptionValue.class \
+ NumberOptionValue.class \
+ OctetOptionValue.class \
+ OptionsTable.class \
+ StandardOptions.class \
+ Option.class \
+ DhcptabRecord.class \
+ DhcpClientRecord.class \
+ DhcpClientFlagTypes.class \
+ IPInterface.class \
+ ExportHeader.class \
+ Network.class \
+ IPAddress.class \
+ DhcpdOptions.class \
+ DhcpDatastore.class \
+ DhcpResource.class \
+ OptionContext.class \
+ OptionType.class \
+ ActionError.class \
+ DhcpClientFlagType.class \
+ DhcpConfigOpts.class \
+ ResourceStrings.class \
+ ValidationException.class
+
+SUBDIRS = qualifier
+
+all := TARGET= all
+install := TARGET= install
+clean := TARGET= clean
+clobber := TARGET= clobber
+lint := TARGET= lint
+_msg := TARGET= _msg
+
+include $(SRC)/Makefile.master
+
+CLASSPATH= $(SRC)/cmd/cmd-inet/usr.sadm/dhcpmgr
+
+JAVAFILES = $(CLASSFILES:.class=.java)
+
+MSGDIR= $(ROOT)/usr/share/lib/locale/com/sun/dhcpmgr/data
+MSGDIRS = $(ROOT)/usr/share/lib/locale \
+ $(ROOT)/usr/share/lib/locale/com \
+ $(ROOT)/usr/share/lib/locale/com/sun \
+ $(ROOT)/usr/share/lib/locale/com/sun/dhcpmgr \
+ $(MSGDIR)
+
+MSGFILES= ResourceBundle.properties
+MSGS= $(MSGFILES:%=$(MSGDIR)/%)
+
+CLEANFILES= *.class $(CONFIG_INTERFACE)
+CLOBBERFILES=
+
+.KEEP_STATE:
+
+all: $(SUBDIRS) $(CONFIG_INTERFACE) $(CLASSFILES)
+
+install: $(SUBDIRS) all
+
+$(CONFIG_INTERFACE): $(SRC)/lib/libdhcpsvc/head/dhcp_svc_confkey.m4
+ $(M4) -Djava $? > $@
+
+_msg: $(SUBDIRS) $(MSGDIRS) $(MSGS)
+
+$(MSGDIR)/%: %
+ $(INS.file)
+
+$(MSGDIRS):
+ $(INS.dir)
+
+lint:
+
+clean: $(SUBDIRS) FRC
+ $(RM) $(CLEANFILES)
+
+clobber: clean
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/Network.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/Network.java
new file mode 100644
index 0000000000..d85a53f4a7
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/Network.java
@@ -0,0 +1,265 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 1998-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.data;
+
+import java.io.Serializable;
+import java.util.StringTokenizer;
+import java.text.MessageFormat;
+
+/**
+ * A representation of an IP network from DHCP's point of view; we're
+ * primarily interested in the address and subnet mask.
+ */
+public class Network implements Serializable, Comparable {
+ private IPAddress address;
+ private IPAddress netmask;
+
+ // Serialization id for this class
+ static final long serialVersionUID = 7221738570228102243L;
+
+ /**
+ * Construct an empty network object
+ */
+ public Network() {
+ address = new IPAddress();
+ netmask = new IPAddress();
+ }
+
+ /**
+ * Construct a network with the supplied address and a default mask
+ * @param addr The IP address of the network
+ */
+ public Network(IPAddress addr) {
+ initialize(addr);
+ }
+
+ // Common initialization routine
+ private void initialize(IPAddress addr) {
+ address = addr;
+ // Initialize a default netmask based on address class
+ byte [] b = address.getAddress();
+ int msb = (int)b[0] & 0xff;
+ try {
+ if (msb < 128) {
+ netmask = new IPAddress("255.0.0.0");
+ } else if (msb < 192) {
+ netmask = new IPAddress("255.255.0.0");
+ } else {
+ netmask = new IPAddress("255.255.255.0");
+ }
+ } catch (ValidationException e) {
+ // This shouldn't happen, above masks are all valid IP addrs
+ }
+ }
+
+ /**
+ * Construct a network with the supplied address.
+ * @param addr The IP address of the network.
+ */
+ public Network(String addr) throws ValidationException {
+ try {
+ initialize(new IPAddress(addr));
+ } catch (ValidationException e) {
+ Object [] args = new Object[1];
+ args[0] = addr;
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString("invalid_network"));
+ String msg = form.format(args);
+ throw new ValidationException(msg);
+ }
+ }
+
+ /**
+ * Construct a network with the supplied address and subnet mask
+ * @param addr The IP address of the network as a <code>String</code>
+ * @param mask The subnet mask as an <code>int</code>
+ */
+ public Network(String addr, int mask) throws ValidationException {
+ try {
+ address = new IPAddress(addr);
+ } catch (ValidationException e) {
+ Object [] args = new Object[1];
+ args[0] = addr;
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString("invalid_network"));
+ String msg = form.format(args);
+ throw new ValidationException(msg);
+ }
+
+ netmask = new IPAddress(mask);
+ }
+
+ /**
+ * Construct a network with the supplied address and subnet mask.
+ * @param addr The IP address as an <code>IPAddress</code>
+ * @param mask The subnet mask as an <code>IPAddress</code>
+ */
+ public Network(IPAddress addr, IPAddress mask) {
+ address = addr;
+ netmask = mask;
+ }
+
+ /**
+ * Construct a network with the supplied address and subnet mask.
+ * @param addr The IP address as a dotted decimal <code>String</code>
+ * @param mask The subnet mask as a dotted decimal <code>String</code>
+ */
+ public Network(String addr, String mask) throws ValidationException {
+ try {
+ address = new IPAddress(addr);
+ } catch (ValidationException e) {
+ Object [] args = new Object[1];
+ args[0] = addr;
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString("invalid_network"));
+ String msg = form.format(args);
+ throw new ValidationException(msg);
+ }
+
+ try {
+ netmask = new IPAddress(mask);
+ } catch (ValidationException e) {
+ Object [] args = new Object[1];
+ args[0] = mask;
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString("invalid_netmask"));
+ String msg = form.format(args);
+ throw new ValidationException(msg);
+ }
+ }
+
+ /**
+ * @return The IP address of the network
+ */
+ public IPAddress getAddress() {
+ return address;
+ }
+
+ /**
+ * Return the actual network number, which is the product of applying
+ * the subnet mask to the address supplied.
+ * @return The network number as an <code>IPAddress</code>
+ */
+ public IPAddress getNetworkNumber() {
+ // If netmask is not set then ignore it and return address raw
+ if (netmask.intValue() == 0) {
+ return address;
+ } else {
+ return new IPAddress(address.intValue() & netmask.intValue());
+ }
+ }
+
+ /**
+ * @return The subnet mask of the network
+ */
+ public IPAddress getMask() {
+ return netmask;
+ }
+
+ /**
+ * Set the subnet mask.
+ * @param mask The subnet mask.
+ */
+ public void setMask(IPAddress mask) {
+ netmask = mask;
+ }
+
+ /**
+ * Do the math to evaluate whether an address is part of this network.
+ * @param addr The IP address to evaluate
+ * @return <code>true</code> if the address is on this network,
+ * <code>false</code> if not.
+ */
+ public boolean containsAddress(IPAddress addr) {
+ return ((addr.intValue() & netmask.intValue())
+ == (address.intValue() & netmask.intValue()));
+ }
+
+ /**
+ * Compute the broadcast address for this network and return it.
+ * @return a string representation of the broadcast address.
+ */
+ public String getBroadcastAddress() {
+
+ byte [] netBytes = getAddress().getAddress();
+ byte [] maskBytes = getMask().getAddress();
+ StringBuffer buf = new StringBuffer();
+ for (int i = 0; i < netBytes.length; ++i) {
+ int b = (netBytes[i] | ~maskBytes[i]) & 0xff;
+ if (buf.length() != 0) {
+ buf.append('.');
+ }
+ buf.append(b);
+ }
+
+ return (buf.toString());
+
+ } // getBroadcastAddress
+
+ /**
+ * Compare against another network object for equality.
+ * @param obj The network to compare against.
+ * @return <code>true</code> if the networks have the same network number
+ */
+ public boolean equals(Object obj) {
+ // If object passed isn't of same type, always false.
+ if (!(obj instanceof Network)) {
+ return false;
+ }
+ return getNetworkNumber().equals(((Network)obj).getNetworkNumber());
+ }
+
+ public String toString() {
+ return getNetworkNumber().toString();
+ }
+
+ /**
+ * Perform comparisons to another Network instance. This is used
+ * for sorting a list of network tables.
+ * @param o A <code>Network</code> to compare against.
+ * @return 0 if the objects have the same address,
+ * a negative number if this record has a lower IP address than the
+ * supplied record, a positive number if this record has a higher IP
+ * address than the supplied record.
+ */
+ public int compareTo(Object o) {
+
+ Network n = (Network)o;
+ long result = getNetworkNumber().getBinaryAddress() -
+ n.getNetworkNumber().getBinaryAddress();
+
+ if (result < 0) {
+ return (-1);
+ } else if (result > 0) {
+ return (1);
+ } else {
+ return (0);
+ }
+ }
+
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/NumberOptionValue.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/NumberOptionValue.java
new file mode 100644
index 0000000000..a914c3b099
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/NumberOptionValue.java
@@ -0,0 +1,235 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 1998-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.data;
+
+import java.io.Serializable;
+import java.util.*;
+import java.math.BigInteger;
+
+/**
+ * This class provides a way to retain the radix specified by the user
+ * when entering the data. Currently only support 10 and 16 for radix.
+ */
+class NumberValue implements Serializable {
+ private Number value;
+ private int radix;
+
+ // Serialization id for this class
+ static final long serialVersionUID = -3480903816949402385L;
+
+ public NumberValue(Number value, int radix) {
+ this.value = value;
+ this.radix = radix;
+ }
+
+ public String toString() {
+ if (value instanceof BigInteger) {
+ return ((BigInteger)value).toString(radix);
+ }
+ // Handle hex specially
+ if (radix == 16) {
+ return "0x" + Long.toHexString(value.longValue());
+ } else {
+ return Long.toString(value.longValue());
+ }
+ }
+}
+
+public class NumberOptionValue extends OptionValue {
+ private String name;
+ private Vector nums;
+ private boolean valid;
+ private int radix;
+
+ // Serialization id for this class
+ static final long serialVersionUID = -5824132577553748971L;
+
+ protected NumberOptionValue(String name) {
+ this.name = name;
+ nums = null;
+ valid = false;
+ }
+
+ public void setValue(Object value) throws ValidationException {
+ // Find option in option definition table in order to validate the data
+ Option option = OptionsTable.getTable().get(name);
+ if (option == null) {
+ Object [] args = { name };
+ throwException("invalid_option", args);
+ }
+
+ // The granularity attribute must be interpreted in context
+ // of what kind of number option we're dealing with. If this
+ // is a NUMBER option, then granularity defines the number of
+ // octets the number will contain (in other words the size).
+ // For all other number types it defines how many numbers of
+ // that type make make a valid option of that type. Note that
+ // in the case of NUMBER options that granularity always defaults
+ // to one. XXX Swill code here. Should probably define a new class and
+ // create an array of them and simply loop.
+ byte type = option.getType();
+ boolean isUnsigned = false;
+ int bits = option.getGranularity() * 8;
+ int realGranularity = option.getGranularity();
+
+ if (type == Option.types[Option.NUMBER].getCode()) {
+ realGranularity = 1;
+ } else if (type == Option.types[Option.SNUMBER8].getCode()) {
+ bits = 7;
+ } else if (type == Option.types[Option.UNUMBER8].getCode()) {
+ bits = 8;
+ isUnsigned = true;
+ } else if (type == Option.types[Option.SNUMBER16].getCode()) {
+ bits = 15;
+ } else if (type == Option.types[Option.UNUMBER16].getCode()) {
+ bits = 16;
+ isUnsigned = true;
+ } else if (type == Option.types[Option.SNUMBER32].getCode()) {
+ bits = 31;
+ } else if (type == Option.types[Option.UNUMBER32].getCode()) {
+ bits = 32;
+ isUnsigned = true;
+ } else if (type == Option.types[Option.SNUMBER64].getCode()) {
+ bits = 63;
+ } else if (type == Option.types[Option.UNUMBER64].getCode()) {
+ bits = 64;
+ isUnsigned = true;
+ }
+
+ Vector newNums = new Vector();
+ if (value instanceof String) {
+ if (((String)value).length() == 0) {
+ // Empty strings are not acceptable
+ Object [] args = { name,
+ Option.getTypeDhcptabString(type) };
+ throwException("invalid_option_value", args);
+ }
+ // Parse each token into an object of the correct numeric type
+ StringTokenizer st = new StringTokenizer((String)value, " ");
+ while (st.hasMoreTokens()) {
+ int radix = 10;
+ String s = st.nextToken();
+ if (s.startsWith("0x") || s.startsWith("0X")) {
+ radix = 16;
+ s = s.substring(2);
+ } else if (s.startsWith("0") && (s.length() > 1)) {
+ radix = 8;
+ s = s.substring(1);
+ }
+
+ BigInteger b;
+ try {
+ b = new BigInteger(s, radix);
+ if (b.bitLength() > bits) {
+ Object [] args = { name,
+ Option.getTypeDhcptabString(type) };
+ throwException("invalid_option_value", args);
+ }
+ if (isUnsigned && b.compareTo(BigInteger.ZERO) < 0) {
+ Object [] args = { name,
+ Option.getTypeDhcptabString(type) };
+ throwException("invalid_option_value", args);
+ }
+ newNums.addElement(new NumberValue(b, radix));
+ } catch (NumberFormatException e) {
+ Object [] args = { name,
+ Option.getTypeDhcptabString(type) };
+ throwException("invalid_option_value", args);
+ }
+ }
+ } else if (value instanceof Number) {
+ newNums.addElement(new NumberValue((Number)value, 10));
+ } else if (!(value instanceof Vector)) {
+ Object [] args = { name,
+ Option.getTypeDhcptabString(type) };
+ throwException("invalid_option_value", args);
+ } else {
+ // Caller supplied a vector; make sure each value is a number
+ Enumeration en = ((Vector)value).elements();
+ while (en.hasMoreElements()) {
+ Object o = en.nextElement();
+ if (!(o instanceof Number)) {
+ Object [] args = { name,
+ Option.getTypeDhcptabString(type) };
+ throwException("invalid_option_value", args);
+ } else {
+ newNums.addElement(new NumberValue((Number)o, 10));
+ }
+ }
+ }
+
+ // We now have a vector of numbers; check count against expected
+ if (newNums.size() % realGranularity != 0) {
+ Object [] args = { name, Integer.toString(realGranularity) };
+ throwException("invalid_option_granularity", args);
+ }
+ if ((option.getMaximum() != 0)
+ && (newNums.size() / realGranularity > option.getMaximum())) {
+ Object [] args = { name, Integer.toString(option.getMaximum()) };
+ throwException("invalid_option_maximum", args);
+ }
+
+ nums = newNums;
+ valid = true;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getValue() {
+ if (nums == null || nums.size() == 0) {
+ return "";
+ }
+ StringBuffer buf = new StringBuffer();
+ for (Enumeration en = nums.elements(); en.hasMoreElements(); ) {
+ if (buf.length() != 0) {
+ buf.append(' ');
+ }
+ buf.append(en.nextElement().toString());
+ }
+ return buf.toString();
+ }
+
+ public String toString() {
+ return (getName() + "=" + getValue());
+ }
+
+ public boolean isValid() {
+ return valid;
+ }
+
+ public Object clone() {
+ NumberOptionValue v = new NumberOptionValue(name);
+ if (nums != null) {
+ v.nums = (Vector)nums.clone();
+ }
+ v.valid = valid;
+ return v;
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/OctetOptionValue.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/OctetOptionValue.java
new file mode 100644
index 0000000000..1519ca575b
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/OctetOptionValue.java
@@ -0,0 +1,103 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 1998-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.data;
+
+import java.util.Vector;
+import java.util.Enumeration;
+
+public class OctetOptionValue extends OptionValue {
+ private String name;
+ private String value;
+ private boolean valid;
+
+ // Serialization id for this class
+ static final long serialVersionUID = -3267221437949696358L;
+
+ protected OctetOptionValue(String name) {
+ this.name = name;
+ value = "";
+ valid = false;
+ }
+
+ public void setValue(Object value) throws ValidationException {
+ // Find option in option definition table in order to validate the data
+ Option option = OptionsTable.getTable().get(name);
+ if (option == null) {
+ Object [] args = { name };
+ throwException("invalid_option", args);
+ }
+ if (value instanceof String) {
+ if (((String)value).length() == 0) {
+ // Empty values are not acceptable
+ Object [] args = { name,
+ Option.getTypeDhcptabString(option.getType()) };
+ throwException("invalid_option_value", args);
+ }
+ // Just make a copy of the reference
+ this.value = (String)value;
+ valid = true;
+ } else if (value instanceof Vector) {
+ /*
+ * Generate the value by concatenating toString()'s on the
+ * vector's elements
+ */
+ StringBuffer b = new StringBuffer();
+ Enumeration en = ((Vector)value).elements();
+ while (en.hasMoreElements()) {
+ b.append(en.nextElement().toString());
+ }
+ setValue(b.toString());
+ } else {
+ // Convert anything else to a string
+ setValue(value.toString());
+ }
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public String toString() {
+ return (getName() + "=" + getValue());
+ }
+
+ public boolean isValid() {
+ return valid;
+ }
+
+ public Object clone() {
+ OctetOptionValue v = new OctetOptionValue(name);
+ v.value = value;
+ v.valid = valid;
+ return v;
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/Option.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/Option.java
new file mode 100644
index 0000000000..65830980dd
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/Option.java
@@ -0,0 +1,535 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 1998-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+package com.sun.dhcpmgr.data;
+
+import java.util.*;
+
+/**
+ * Option is a simple data class which encapsulates an option record in
+ * the dhcptab. See dhcptab(4) for the gory details on options (aka symbols).
+ *
+ * @see DhcptabRecord
+ * @see Macro
+ */
+public class Option extends DhcptabRecord implements Cloneable {
+ private byte context;
+ private short code;
+ private byte type;
+ private int granularity;
+ private int maximum;
+ private Vector vendors;
+ private boolean valueClean = false;
+ private boolean validValue = true;
+
+ // The Option attributes that must match their native values.
+
+ // Definition for attribute limits
+ public static short MAX_NAME_SIZE = 128;
+
+ // Option contexts.
+ public static byte STANDARD = 0;
+ public static byte EXTEND = 1;
+ public static byte VENDOR = 2;
+ public static byte SITE = 3;
+ public static byte CONTEXTS = 4;
+ public static OptionContext [] ctxts = {
+ new OptionContext(STANDARD, "Standard", "standard_option"),
+ new OptionContext(EXTEND, "Extend", "extended_option"),
+ new OptionContext(VENDOR, "Vendor=", "vendor_option"),
+ new OptionContext(SITE, "Site", "site_option") };
+
+ // Option types.
+ public static byte ASCII = 0;
+ public static byte OCTET = 1;
+ public static byte IP = 2;
+ public static byte NUMBER = 3;
+ public static byte BOOLEAN = 4;
+ public static byte UNUMBER8 = 5;
+ public static byte UNUMBER16 = 6;
+ public static byte UNUMBER32 = 7;
+ public static byte UNUMBER64 = 8;
+ public static byte SNUMBER8 = 9;
+ public static byte SNUMBER16 = 10;
+ public static byte SNUMBER32 = 11;
+ public static byte SNUMBER64 = 12;
+ public static byte TYPES = 13;
+
+ public static OptionType [] types = {
+ new OptionType((byte)0, "ASCII", "ascii_type"),
+ new OptionType((byte)1, "OCTET", "octet_type"),
+ new OptionType((byte)2, "IP", "ip_type"),
+ new OptionType((byte)3, "NUMBER", "number_type"),
+ new OptionType((byte)4, "BOOL", "boolean_type"),
+ new OptionType((byte)6, "UNUMBER8", "unumber8_type"),
+ new OptionType((byte)7, "UNUMBER16", "unumber16_type"),
+ new OptionType((byte)8, "UNUMBER32", "unumber32_type"),
+ new OptionType((byte)9, "UNUMBER64", "unumber64_type"),
+ new OptionType((byte)10, "SNUMBER8", "snumber8_type"),
+ new OptionType((byte)11, "SNUMBER16", "snumber16_type"),
+ new OptionType((byte)12, "SNUMBER32", "snumber32_type"),
+ new OptionType((byte)13, "SNUMBER64", "snumber64_type") };
+
+ /*
+ * These need to be the same as the definitions in libdhcputil's
+ * parser in dhcp_symbol.c
+ */
+ public static String DSYM_CLASS_DEL_SPACE = " ";
+ public static String DSYM_CLASS_DEL = DSYM_CLASS_DEL_SPACE + "\t\n";
+ public static String DSYM_CLASS_DEL_REGEXP = ".*[" + DSYM_CLASS_DEL + "].*";
+ public static char DSYM_FIELD_DEL = ',';
+ public static char DSYM_QUOTE = '"';
+
+
+ // Serialization id for this class
+ static final long serialVersionUID = 7468266817375654444L;
+
+ /**
+ * Construct an empty instance. Default to Site option, IP type.
+ */
+ public Option() {
+ super("", DhcptabRecord.OPTION, "");
+ valueClean = false;
+ vendors = new Vector();
+ context = ctxts[SITE].getCode();
+ type = types[IP].getCode();
+ granularity = 1;
+ }
+
+ /**
+ * Construct a fully defined instance. Used by the server to create
+ * Options.
+ * @param name the option name/key
+ * @param context the option context/category code
+ * @param vendors the list of vendors (if any)
+ * @param code the option code
+ * @param type the option type code
+ * @param gran the option granularity
+ * @param max the option maximum
+ * @param sig the signature from the dhcptab
+ * @param isValid the flag indicating option definition validity
+ */
+ public Option(String name, byte context, String [] vendors, short code,
+ byte type, int gran, int max, String sig, boolean isValid) {
+
+ super("", DhcptabRecord.OPTION, "");
+
+ valueClean = false;
+ validValue = isValid;
+
+ setKey(name);
+ setContext(context);
+ setVendors(vendors);
+ setCode(code);
+ setType(type);
+ setGranularity(gran);
+ setMaximum(max);
+
+ if (sig != null) {
+ setSignature(sig);
+ }
+ }
+
+ /**
+ * Set the option name as specified.
+ * @param name a string representing the option name.
+ */
+ public void setKey(String name) {
+ try {
+ super.setKey(name);
+ } catch (ValidationException e) {
+ // Can't happen.
+ }
+ }
+
+ /**
+ * Get the context for this option
+ * @return a byte for the option context (context codes are
+ * defined by the OptionContext objects in the ctxts array).
+ */
+ public byte getContext() {
+ return context;
+ }
+
+ /**
+ * Set the context for this option (context codes are defined
+ * by the OptionContext objects in the ctxts array).
+ */
+ public void setContext(byte c) {
+ context = c;
+ valueClean = false;
+ }
+
+ /**
+ * Enumerate the vendor list.
+ * @return an Enumeration of the vendors, which will be empty for
+ * non-vendor options.
+ */
+ public Enumeration getVendors() {
+ return vendors.elements();
+ }
+
+ /**
+ * Get the number of vendors for this option.
+ * @return an int count of the vendors, zero for non-vendor options.
+ */
+ public int getVendorCount() {
+ return vendors.size();
+ }
+
+ /**
+ * Add a vendor to the list for this option.
+ * @param v the vendor name as a String.
+ */
+ public void addVendor(String v) throws ValidationException {
+ if (v.indexOf(DSYM_FIELD_DEL) != -1) {
+ throw new ValidationException(v);
+ }
+ vendors.addElement(v);
+ valueClean = false;
+ }
+
+ /**
+ * Empty the vendor list.
+ */
+ public void clearVendors() {
+ vendors = new Vector();
+ valueClean = false;
+ }
+
+ /**
+ * Remove a vendor from the list.
+ * @param index the position of the vendor to remove in the list of vendors
+ */
+ public void removeVendorAt(int index) {
+ vendors.removeElementAt(index);
+ valueClean = false;
+ }
+
+ /**
+ * Get the vendor at a specified index in the vendor list.
+ * @param index the index of the vendor to retrieve
+ * @return the vendor name
+ */
+ public String getVendorAt(int index) {
+ return (String)vendors.elementAt(index);
+ }
+
+ private void setVendors(String [] vendors) {
+
+ this.vendors = new Vector();
+
+ if (vendors == null) {
+ return;
+ }
+
+ for (int i = 0; i < vendors.length; i++) {
+ this.vendors.addElement(vendors[i]);
+ }
+
+ }
+
+ /**
+ * Set the vendor name at a specified index in the list.
+ * @param vendor the vendor name
+ * @param index the position in the list to set.
+ */
+ public void setVendorAt(String vendor, int index) {
+ if (index >= vendors.size()) {
+ vendors.setSize(index+1);
+ }
+ vendors.setElementAt(vendor, index);
+ valueClean = false;
+ }
+
+ /**
+ * Get the option code.
+ * @return the code as a short.
+ */
+ public short getCode() {
+ return code;
+ }
+
+ /**
+ * Set the option code.
+ * @param c the code to use
+ */
+ public void setCode(short c) {
+ code = c;
+ valueClean = false;
+ }
+
+ /**
+ * Get the type.
+ * @return a byte value for the type (type codes are
+ * defined by the OptionTypes objects in the type array).
+ * OCTET
+ */
+ public byte getType() {
+ return type;
+ }
+
+ /**
+ * Set the type.
+ * @param t the type code (type codes are defined by the
+ * OptionTypes objects in the type array).
+ * or OCTET.
+ */
+ public void setType(byte t) {
+ type = t;
+ valueClean = false;
+ }
+
+ /**
+ * Get the granularity. See dhcptab(4) for an explanation of granularity
+ * interpretations.
+ * @return the granularity as an int
+ */
+ public int getGranularity() {
+ return granularity;
+ }
+
+ /**
+ * Set the granularity. See dhcptab(4) for an explanation of granularity
+ * interpretations.
+ * @param g the granularity as an int.
+ */
+ public void setGranularity(int g) {
+ granularity = g;
+ valueClean = false;
+ }
+
+ /**
+ * Get the maximum. See dhcptab(4) for an explanation of maximum.
+ * @return the maximum as an int.
+ */
+ public int getMaximum() {
+ return maximum;
+ }
+
+ /**
+ * Set the maximum. See dhcptab(4) for an explanation of maximum.
+ * @param m the maximum as an int.
+ */
+ public void setMaximum(int m) {
+ maximum = m;
+ valueClean = false;
+ }
+
+ /**
+ * Return validity of this option.
+ * @return true if the option is correctly defined, false if not
+ */
+ public boolean isValid() {
+ return (validValue);
+ }
+
+ /**
+ * Get the definition as a string in the format specified by dhcptab(4)
+ * @return a String containing the definition
+ */
+ public String getValue() {
+ /* The value string stored is not clean, regenerate */
+ if (!valueClean) {
+ StringBuffer b = new StringBuffer();
+ // Start with context
+ b.append(getContextDhcptabString(context));
+ // Vendor context next adds the vendors, separate by blanks
+ if (context == ctxts[VENDOR].getCode()) {
+ boolean first = true;
+ for (Enumeration e = getVendors(); e.hasMoreElements(); ) {
+ String s = (String)e.nextElement();
+ if (!first) {
+ b.append(DSYM_CLASS_DEL_SPACE);
+ } else {
+ first = false;
+ }
+ // If vendor class contains whitespace, need to quote it
+ boolean needQuoting = s.matches(DSYM_CLASS_DEL_REGEXP);
+ if (needQuoting) {
+ b.append(DSYM_QUOTE);
+ }
+ b.append(s);
+ if (needQuoting) {
+ b.append(DSYM_QUOTE);
+ }
+ }
+ }
+ b.append(DSYM_FIELD_DEL);
+ // Add the code
+ b.append(code);
+ b.append(DSYM_FIELD_DEL);
+ // Add the type
+ b.append(getTypeDhcptabString(type));
+ b.append(DSYM_FIELD_DEL);
+ // Add the granularity
+ b.append(granularity);
+ b.append(DSYM_FIELD_DEL);
+ // Add the maximum
+ b.append(maximum);
+ // Save it and note as such so we can avoid doing this again
+ try {
+ super.setValue(b.toString());
+ } catch (ValidationException e) {
+ // This should never happen!
+ }
+ valueClean = true;
+ }
+ return super.getValue();
+ }
+
+ // Make a copy of this option
+ public Object clone() {
+ Option o = new Option();
+
+ o.setKey(getKey());
+ o.setContext(getContext());
+ o.setCode(getCode());
+ o.vendors = new Vector();
+ for (Enumeration en = vendors.elements(); en.hasMoreElements(); ) {
+ String s = (String)en.nextElement();
+ o.vendors.addElement(new String(s));
+ }
+ o.setType(getType());
+ o.setGranularity(getGranularity());
+ o.setMaximum(getMaximum());
+ o.setSignature(getSignature());
+
+ return o;
+ }
+
+ /**
+ * Returns a string representation of this object.
+ * @return a string representation of this object.
+ */
+ public String toString() {
+ return (getKey() + " s " + getValue());
+ }
+
+ /**
+ * Returns the context definition for the specified context.
+ * @param code the context code.
+ * @return the OptionContext for the context.
+ */
+ public static OptionContext findContext(byte code) {
+
+ OptionContext ctxt = null;
+
+ for (int i = 0; i < CONTEXTS; i++) {
+ if (ctxts[i].getCode() == code) {
+ ctxt = ctxts[i];
+ break;
+ }
+ }
+
+ return (ctxt);
+ }
+
+ /**
+ * Returns the dhcptab string representation of the specified context.
+ * @param code the context code.
+ * @return the dhcptab string representation of the context.
+ */
+ public static String getContextDhcptabString(byte code) {
+
+ OptionContext ctxt = findContext(code);
+
+ if (ctxt == null) {
+ return ("undefined");
+ } else {
+ return (ctxt.getDhcptabString());
+ }
+ }
+
+ /**
+ * Returns the string representation of the specified context.
+ * @param code the context code.
+ * @return a string representation of the context.
+ */
+ public static String getContextString(byte code) {
+
+ OptionContext ctxt = findContext(code);
+
+ if (ctxt == null) {
+ return ("undefined");
+ } else {
+ return (ctxt.toString());
+ }
+ }
+
+ /**
+ * Returns the type definition for the specified type.
+ * @param code the type code.
+ * @return the OptionType for the type.
+ */
+ public static OptionType findType(byte code) {
+
+ OptionType type = null;
+
+ for (int i = 0; i < TYPES; i++) {
+ if (types[i].getCode() == code) {
+ type = types[i];
+ break;
+ }
+ }
+
+ return (type);
+ }
+
+ /**
+ * Returns the dhcptab string representation of the specified type.
+ * @param code the type code.
+ * @return the dhcptab string representation of the type.
+ */
+ public static String getTypeDhcptabString(byte code) {
+
+ OptionType type = findType(code);
+
+ if (type == null) {
+ return ("undefined"); // should never happen
+ } else {
+ return (type.getDhcptabString());
+ }
+ }
+
+ /**
+ * Returns the string representation of the specified type.
+ * @param code the type code.
+ * @return a string representation of the type.
+ */
+ public static String getTypeString(byte code) {
+
+ OptionType type = findType(code);
+
+ if (type == null) {
+ return ("undefined"); // should never happen
+ } else {
+ return (type.toString());
+ }
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/OptionContext.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/OptionContext.java
new file mode 100644
index 0000000000..945c1ec1b1
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/OptionContext.java
@@ -0,0 +1,82 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2001-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+package com.sun.dhcpmgr.data;
+
+import java.io.Serializable;
+
+/**
+ * OptionContext simply defines the attributes that should be associated
+ * with an Option context. Simply a container.
+ */
+public class OptionContext implements Serializable {
+
+ private byte code;
+ private String dhcptabString;
+ private String string;
+
+ // Serialization id for this class
+ static final long serialVersionUID = -8656392905082090762L;
+
+ /**
+ * Constructs a fully defined instance of an OptionContext.
+ * @param code the context code
+ * @param dhcptabString the dhcptab string definition for the context
+ * @param msgid the msgid for the description of the context
+ */
+ public OptionContext(byte code, String dhcptabString, String msgid) {
+ this.code = code;
+ this.dhcptabString = dhcptabString;
+ this.string = ResourceStrings.getString(msgid);
+ } // constructor
+
+ /**
+ * Returns the code for the context
+ * @returns the code for the context
+ */
+ public byte getCode() {
+ return code;
+ } // getCode
+
+ /**
+ * Returns the dhcptab string definition for the context
+ * @returns the dhcptab string definition for the context
+ */
+ public String getDhcptabString() {
+ return dhcptabString;
+ } // getDhcptabString
+
+ /**
+ * Returns a string representation of this object.
+ * @return a string representation of this object.
+ */
+ public String toString() {
+ return (string);
+ } // toString
+
+} // OptionContext
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/OptionType.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/OptionType.java
new file mode 100644
index 0000000000..4b9f0a39c7
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/OptionType.java
@@ -0,0 +1,82 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2001-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+package com.sun.dhcpmgr.data;
+
+import java.io.Serializable;
+
+/**
+ * OptionType simply defines the attributes that should be associated
+ * with an Option type. Simply a container.
+ */
+public class OptionType implements Serializable {
+
+ private byte code;
+ private String dhcptabString;
+ private String string;
+
+ // Serialization id for this class
+ static final long serialVersionUID = -1933732689675276794L;
+
+ /**
+ * Constructs a fully defined instance of an OptionType.
+ * @param code the type code
+ * @param dhcptabString the dhcptab string definition for the type
+ * @param msgid the msgid for the description of the type
+ */
+ public OptionType(byte code, String dhcptabString, String msgid) {
+ this.code = code;
+ this.dhcptabString = dhcptabString;
+ this.string = ResourceStrings.getString(msgid);
+ } // constructor
+
+ /**
+ * Returns the code for the type
+ * @returns the code for the type
+ */
+ public byte getCode() {
+ return code;
+ } // getCode
+
+ /**
+ * Returns the dhcptab string definition for the type
+ * @returns the dhcptab string definition for the type
+ */
+ public String getDhcptabString() {
+ return dhcptabString;
+ } // getDhcptabString
+
+ /**
+ * Returns a string representation of this object.
+ * @return a string representation of this object.
+ */
+ public String toString() {
+ return (string);
+ } // toString
+
+} // OptionType
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/OptionValue.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/OptionValue.java
new file mode 100644
index 0000000000..3f555b8baf
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/OptionValue.java
@@ -0,0 +1,56 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 1998-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.data;
+
+import java.io.Serializable;
+import java.util.Vector;
+import java.text.MessageFormat;
+
+/**
+ * OptionValue is an abstract superclass for all the actual types of options
+ * which may be stored in a macro.
+ */
+public abstract class OptionValue implements Serializable, Cloneable {
+
+ // Serialization id for this class
+ static final long serialVersionUID = -1346853613202192887L;
+
+ public abstract String getName();
+ public abstract String getValue();
+ public abstract void setValue(Object value) throws ValidationException;
+ public abstract boolean isValid();
+ public abstract Object clone();
+
+ protected void throwException(String msgid, Object [] args)
+ throws ValidationException {
+ MessageFormat form = new MessageFormat(
+ ResourceStrings.getString(msgid));
+ String msg = form.format(args);
+ throw new ValidationException(msg);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/OptionValueFactory.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/OptionValueFactory.java
new file mode 100644
index 0000000000..aa3a0c00e1
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/OptionValueFactory.java
@@ -0,0 +1,109 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1998-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.data;
+
+import java.util.Vector;
+
+/**
+ * This class provides the functionality to construct an option value of the
+ * correct type when only the tag we associate with the option value is known.
+ */
+public class OptionValueFactory {
+ private static OptionsTable optionsTable = OptionsTable.getTable();
+
+ /**
+ * Construct an option value given the name, and initialize it to the
+ * provided value.
+ * @param name the name of the option
+ * @param value the initial value for the option
+ * @return an OptionValue of the correct type for this option. If the name
+ * or value supplied is invalid in some way, an instance of
+ * BogusOptionValue is returned and the caller should take appropriate
+ * action.
+ */
+ public static OptionValue newOptionValue(String name, Object value) {
+ OptionValue v;
+ try {
+ v = newOptionValue(name);
+ v.setValue(value);
+ } catch (ValidationException e) {
+ // Not a valid value; put it in the bogus value placeholder
+ v = new BogusOptionValue(name, value);
+ }
+ return v;
+ }
+
+ /**
+ * Construct an empty option value given the name
+ * @param name the name of the option
+ * @return an OptionValue of the correct type for this option.
+ */
+ public static OptionValue newOptionValue(String name) {
+ if (name.length() == 0) {
+ // Empty name is not acceptable
+ return new BogusOptionValue(name);
+ }
+ Option opt = optionsTable.get(name);
+ if (opt == null) {
+ // Include is not in the options table
+ if (name.equals("Include")) {
+ return new IncludeOptionValue();
+ } else {
+ /*
+ * Bogus option name; create a bogus value that callers
+ * can pick up later.
+ */
+ return new BogusOptionValue(name);
+ }
+ }
+
+ byte type = opt.getType();
+ if (type == Option.types[Option.ASCII].getCode()) {
+ return new AsciiOptionValue(name);
+ } else if (type == Option.types[Option.BOOLEAN].getCode()) {
+ return new BooleanOptionValue(name);
+ } else if (type == Option.types[Option.IP].getCode()) {
+ return new IPOptionValue(name);
+ } else if (type == Option.types[Option.OCTET].getCode()) {
+ return new OctetOptionValue(name);
+ } else if (type == Option.types[Option.NUMBER].getCode() ||
+ type == Option.types[Option.UNUMBER8].getCode() ||
+ type == Option.types[Option.UNUMBER16].getCode() ||
+ type == Option.types[Option.UNUMBER32].getCode() ||
+ type == Option.types[Option.UNUMBER64].getCode() ||
+ type == Option.types[Option.SNUMBER8].getCode() ||
+ type == Option.types[Option.SNUMBER16].getCode() ||
+ type == Option.types[Option.SNUMBER32].getCode() ||
+ type == Option.types[Option.SNUMBER64].getCode()) {
+ return new NumberOptionValue(name);
+ } else {
+ // This should never happen
+ return new BogusOptionValue(name);
+ }
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/OptionsTable.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/OptionsTable.java
new file mode 100644
index 0000000000..aadf1d91d1
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/OptionsTable.java
@@ -0,0 +1,118 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1998-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.data;
+
+import java.util.*;
+import java.io.Serializable;
+
+/**
+ * This class provides a global table of all the options currently known.
+ * It is implemented as a singleton as there should be no need for more
+ * than a single instance of this table. It includes both the standard
+ * options and any vendor or site options defined in the current environment.
+ */
+public class OptionsTable implements Serializable {
+ private Hashtable options;
+ private static OptionsTable table = null;
+
+ protected OptionsTable() {
+ // Get the standard options we know about
+ StandardOptions stdopts = new StandardOptions();
+
+ // Initialize hash table with extra size we will probably need.
+ options = new Hashtable(stdopts.size() + 20);
+
+ // Add the standard options to the table
+ add(stdopts.getAllOptions());
+ }
+
+ /**
+ * Add an array of options to the table.
+ * @param opts An array of Options
+ */
+ public void add(Option [] opts) {
+ for (int i = 0; opts != null && i < opts.length; ++i) {
+ add(opts[i]);
+ }
+ }
+
+ /**
+ * Add a single option to the table.
+ * @param o The option to add.
+ */
+ public void add(Option o) {
+ // Don't add unless it is a valid option.
+ if (o.isValid()) {
+ options.put(o.getKey(), o);
+ }
+ }
+
+ /**
+ * Retrieve an option from the table by name
+ * @param opt the name of the option to retrieve
+ * @return the option found, or null if the option is not in the table
+ */
+ public Option get(String opt) {
+ return (Option)options.get(opt);
+ }
+
+ /**
+ * Retrieve an option from the table by its code
+ * @param code the code of the option to retrieve
+ * @return the option found, or null if the option is not in the table
+ */
+ public Option getByCode(short code) {
+
+ Option option = null;
+ for (Enumeration e = elements(); e.hasMoreElements(); ) {
+ if (((Option)e.nextElement()).getCode() == code) {
+ option = (Option)e.nextElement();
+ }
+ }
+ return (option);
+ }
+
+ /**
+ * Enumerate the options in this table for those that might need to walk it.
+ * @return an Enumeration of the options
+ */
+ public Enumeration elements() {
+ return options.elements();
+ }
+
+ /**
+ * Return the global table, create it if not already in existence.
+ * @return the current options table
+ */
+ public static OptionsTable getTable() {
+ if (table == null) {
+ table = new OptionsTable();
+ }
+ return table;
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/ResourceBundle.properties b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/ResourceBundle.properties
new file mode 100644
index 0000000000..10d340f199
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/ResourceBundle.properties
@@ -0,0 +1,69 @@
+#
+# ident "%W% %E% SMI"
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+invalid_ip_address={0} is not a valid IP address
+invalid_option_value=The {0} option requires an {1} value
+invalid_include_option=The Include option requires a value
+invalid_option_maximum=The {0} option value exceeds the maximum limit of {1}
+invalid_option_granularity=The {0} option value must have a granularity of {1}
+
+invalid_option=The {0} option is an invalid option or is not of the correct type
+boolean_option=The {0} option is a boolean option
+not_boolean_option=The {0} option is not a boolean option
+mac_syntax_error=The macro, {0}, contains a definition syntax error
+macro_key_length=Macro names must be no more than {0} characters
+invalid_network={0} is not valid a valid network
+invalid_netmask={0} is not valid netmask
+
+dcr_invalid_null_clientip=The client address must be defined.
+dcr_invalid_clientip=The client address is not a valid address.
+dcr_invalid_clientid=The client id is not a valid client id.
+dcr_invalid_flags=The flags value is not valid.
+dcr_invalid_null_serverip=The server address must be defined.
+dcr_invalid_serverip=The server address is not a valid address.
+dcr_invalid_expiration=The lease expiration date is not in a valid format.
+
+# Note: these msgids are used by the native library.
+standard_option=Standard
+extended_option=Extended
+vendor_option=Vendor
+site_option=Site
+
+# Note: these msgids are used by the native library.
+ascii_type=ASCII text
+octet_type=Octet
+ip_type=IP Address
+number_type=Number
+boolean_type=Boolean
+unumber8_type=Unsigned 8-bit Number
+unumber16_type=Unsigned 16-bit Number
+unumber32_type=Unsigned 32-bit Number
+unumber64_type=Unsigned 64-bit Number
+snumber8_type=Signed 8-bit Number
+snumber16_type=Signed 16-bit Number
+snumber32_type=Signed 32-bit Number
+snumber64_type=Signed 64-bit Number
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/ResourceStrings.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/ResourceStrings.java
new file mode 100644
index 0000000000..5e43a97bfb
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/ResourceStrings.java
@@ -0,0 +1,64 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1998-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.data;
+
+import java.util.*;
+
+/**
+ * This class provides a convenient method to retrieve resources for
+ * the data package.
+ */
+public class ResourceStrings {
+
+ /**
+ * The handle to the resource bundle for the module.
+ */
+ private static ResourceBundle bundle = null;
+
+ /**
+ * Return a string from the resource bundle.
+ * @param key the key to the resource bundle string.
+ * @return the resource bundle string.
+ */
+ public static String getString(String key) {
+ String msg = null;
+ try {
+ if (bundle == null) {
+ bundle = ResourceBundle.getBundle(
+ "com.sun.dhcpmgr.data.ResourceBundle",
+ Locale.getDefault());
+ }
+ msg = bundle.getString(key);
+ } catch (Throwable e) {
+ msg = new String(key);
+ }
+ return msg;
+
+ } // getString
+
+} // ResourceStrings
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/StandardOptions.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/StandardOptions.java
new file mode 100644
index 0000000000..d686301a4b
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/StandardOptions.java
@@ -0,0 +1,121 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1998-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.data;
+
+import java.util.*;
+import java.io.Serializable;
+
+/**
+ * This class defines the set of standard DHCP options we know about.
+ */
+public class StandardOptions implements Serializable {
+
+ /*
+ * The following list of options are the ones that we use
+ * in order to configure DHCP for the user.
+ */
+ public static final String CD_SUBNETMASK = "Subnet";
+ public static final String CD_TIMEOFFSET = "UTCoffst";
+ public static final String CD_ROUTER = "Router";
+ public static final String CD_TIMESERV = "Timeserv";
+ public static final String CD_DNSSERV = "DNSserv";
+ public static final String CD_DNSDOMAIN = "DNSdmain";
+ public static final String CD_BROADCASTADDR = "Broadcst";
+ public static final String CD_ROUTER_DISCVRY_ON = "RDiscvyF";
+ public static final String CD_NIS_DOMAIN = "NISdmain";
+ public static final String CD_NIS_SERV = "NISservs";
+ public static final String CD_LEASE_TIME = "LeaseTim";
+ public static final String CD_NISPLUS_DMAIN = "NIS+dom";
+ public static final String CD_NISPLUS_SERVS = "NIS+serv";
+ public static final String CD_BOOL_LEASENEG = "LeaseNeg";
+
+ /*
+ * Following list of options must be kept in sync with the list in
+ * usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/dhcptab.c in SunOS source tree.
+ */
+ private static Option [] options = null;
+
+ /**
+ * Return the size of this list
+ * @return the number of options known
+ */
+ public static int size() {
+ return (options == null) ? 0 :options.length;
+ }
+
+ /**
+ * Enumerate the options defined here.
+ * @return an Enumeration of the standard options.
+ */
+ public Enumeration enumOptions() {
+ return new Enumeration() {
+ int cursor = 0;
+
+ public boolean hasMoreElements() {
+ return (cursor < size());
+ }
+
+ public Object nextElement() throws NoSuchElementException {
+ if (cursor >= size()) {
+ throw new NoSuchElementException();
+ }
+ return (options[cursor++]);
+ }
+ };
+ }
+
+ /**
+ * Return all options as an array
+ * @return the array of options defined here
+ */
+ public static Option [] getAllOptions() {
+ return options;
+ }
+
+ /**
+ * Set all options as an array
+ * @param options array of STANDARD options
+ */
+ public static void setAllOptions(Option [] ops) {
+ options = ops;
+ }
+
+ /**
+ * Find the option name for a given code. This could be
+ * much faster but not clear that it needs to be yet.
+ * @return the name of the option, or null if that code is unknown.
+ */
+ public static String nameForCode(int code) {
+ for (int i = 0; i < options.length; ++i) {
+ if (options[i].getCode() == code) {
+ return options[i].getKey();
+ }
+ }
+ return null;
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/ValidationException.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/ValidationException.java
new file mode 100644
index 0000000000..521bd5b990
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/ValidationException.java
@@ -0,0 +1,38 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1998-1999 Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.data;
+
+public class ValidationException extends Exception {
+ public ValidationException() {
+ super();
+ }
+
+ public ValidationException(String s) {
+ super(s);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/Makefile b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/Makefile
new file mode 100644
index 0000000000..745e54a335
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/Makefile
@@ -0,0 +1,80 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/Makefile
+#
+
+CLASSFILES = QualifierType.class \
+ QualifierTypeImpl.class \
+ Qualifier.class \
+ QualifierEnum.class \
+ QualifierRange.class \
+ QualifierImpl.class \
+ QualifierBoolean.class \
+ QualifierString.class \
+ QualifierStringEnum.class \
+ QualifierInteger.class \
+ QualifierIntegerEnum.class \
+ QualifierIntegerRange.class \
+ QualifierArray.class \
+ QualifierAnd.class \
+ QualifierOr.class \
+ QualifierFQDN.class \
+ QualifierIPv4.class \
+ QualifierIPv6.class
+
+all := TARGET= all
+install := TARGET= install
+clean := TARGET= clean
+clobber := TARGET= clobber
+lint := TARGET= lint
+_msg := TARGET= _msg
+
+include $(SRC)/Makefile.master
+
+CLASSPATH= $(SRC)/cmd/cmd-inet/usr.sadm/dhcpmgr
+
+JAVAFILES = $(CLASSFILES:.class=.java)
+
+CLEANFILES= *.class
+CLOBBERFILES=
+
+.KEEP_STATE:
+
+all: $(CLASSFILES)
+
+install: all
+
+_msg:
+
+lint:
+
+clean:
+ $(RM) $(CLEANFILES)
+
+clobber: clean
+
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/Qualifier.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/Qualifier.java
new file mode 100644
index 0000000000..98b45d95a0
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/Qualifier.java
@@ -0,0 +1,120 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+package com.sun.dhcpmgr.data.qualifier;
+
+/**
+ * Common interface that all concrete qualifiers can implement. A qualifier
+ * contains information about a given parameter. This information includes the
+ * parameter type and whether the parameter is read only.
+ */
+public interface Qualifier {
+
+ /**
+ * Attribute that indicates the name of the parameter the qualifier is
+ * associated with.
+ */
+ public final static String KEYWORD = "keyword";
+
+ /**
+ * Attribute that indicates the Java type that can store a legal value for
+ * the parameter.
+ */
+ public final static String TYPE = "type";
+
+ /**
+ * Attribute that indicates whether the parameter is designated as read
+ * only.
+ */
+ public final static String READONLY = "readOnly";
+
+ /**
+ * Attribute that indicates whether the parameter is hidden.
+ */
+ public final static String HIDDEN = "hidden";
+
+ /**
+ * Get the named qualifier attribute.
+ *
+ * @param attribute
+ * Attribute to get.
+ * @return
+ * The value of the attribute, or null if the attribute is not set.
+ */
+ public Object getAttribute(String attribute);
+
+ /**
+ * Set the named qualifier attributes value.
+ *
+ * @param attribute
+ * Attribute to set.
+ * @param value
+ * The value to set the attribute to, or null if the attribute is to
+ * be removed.
+ */
+ public void setAttribute(String attribute, Object value);
+
+ /**
+ * Get the name of the parameter this qualifier is connected to.
+ * Convenience method for obtaining the KEYWORD attribute.
+ *
+ * @return
+ * String containing the name of the parameter.
+ */
+ public String getKeyword();
+
+ /**
+ * Indicates whether the parameter is designated as read only.
+ * Convenience method for obtaining the READONLY attribute.
+ *
+ * @return
+ * True if the parameter is read only, otherwise false.
+ */
+ public boolean isReadOnly();
+
+ /**
+ * Indicates whether the parameter is hidden.
+ * Convenience method for obtaining the HIDDEN attribute.
+ *
+ * @return
+ * True if the parameter is hidden, otherwise false.
+ */
+ public boolean isHidden();
+
+ /**
+ * Get the Java type that can store a legal value for the parameter.
+ * Primitive Java types have their counterpart wrapper classes returned.
+ * For example for an int the Integer class is returned.
+ * Convenience method for obtaining the TYPE attribute.
+ *
+ * @return
+ * A class that can store legal parameter values.
+ */
+ public QualifierType getType();
+
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierAnd.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierAnd.java
new file mode 100644
index 0000000000..e1788569c5
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierAnd.java
@@ -0,0 +1,102 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+package com.sun.dhcpmgr.data.qualifier;
+
+/**
+ * This qualifier type allows the logical and of two qualifier types. Care
+ * must be taken that the two qualifier types are suitable. For instance
+ * if 'and'ing two integer ranges together that do not overlap will ensure
+ * that no legal values are ever parsed by parseValue().
+ */
+public class QualifierAnd extends QualifierTypeImpl {
+
+ protected QualifierType typeA;
+
+ protected QualifierType typeB;
+
+ public QualifierAnd(QualifierType typeA, QualifierType typeB) {
+ this.typeA = typeA;
+ this.typeB = typeB;
+ }
+
+ public void setQualifierTypeA(QualifierType typeA) {
+ this.typeA = typeA;
+ }
+
+ public void setQualifierTypeB(QualifierType typeB) {
+ this.typeB = typeB;
+ }
+
+ public QualifierType getQualifierTypeA() {
+ return typeA;
+ }
+
+ public QualifierType getQualifierTypeB() {
+ return typeB;
+ }
+
+ public Object parseValue(String value) {
+ if (!typeA.getJavaType().equals(typeB.getJavaType())) {
+ return null;
+ }
+
+ Object objectA = typeA.parseValue(value);
+ Object objectB = typeB.parseValue(value);
+
+ if (objectA != null && objectB != null) {
+ return (objectA.equals(objectB)) ? objectA : null;
+ } else {
+ return null;
+ }
+ }
+
+ public String formatValue(String value) {
+ if (!typeA.getJavaType().equals(typeB.getJavaType())) {
+ return null;
+ }
+
+ String stringA = typeA.formatValue(value);
+ String stringB = typeB.formatValue(value);
+
+ if (stringA != null && stringB != null) {
+ return (stringA.equals(stringB)) ? stringA : null;
+ } else {
+ return null;
+ }
+ }
+
+ public Class getJavaType() {
+ return typeA.getJavaType();
+ }
+
+ public String toString() {
+ return "(" + typeA + " && " + typeB + ")";
+ }
+
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierArray.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierArray.java
new file mode 100644
index 0000000000..5769c4202c
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierArray.java
@@ -0,0 +1,257 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+package com.sun.dhcpmgr.data.qualifier;
+
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.StringTokenizer;
+
+/**
+ * An an implementation of the qualifier type that provides an array of
+ * qualifier types. The element qualifer type is contained within the array
+ * qualifier type.
+ */
+public class QualifierArray extends QualifierTypeImpl {
+
+ /**
+ * The default String of characters that delimit elements in a String
+ * representation of the array when it is parsed.
+ */
+ public static final String DEFAULT_PARSE_DELIM = ", ";
+
+ /**
+ * The default String of characters that delimit elements in a String
+ * representation of the array when it is formatted.
+ */
+ public static final String DEFAULT_FORMAT_DELIM = ",";
+
+ /**
+ * The type of the arrays elements.
+ */
+ protected QualifierType type;
+
+ /**
+ * The String of characters that delimit elements in a String
+ * representation of the array when it is parsed.
+ */
+ protected String parseDelim = DEFAULT_PARSE_DELIM;
+
+ /**
+ * The String of characters that delimit elements in a String
+ * representation of the array when it is formatted.
+ */
+ protected String formatDelim = DEFAULT_FORMAT_DELIM;
+
+ private QualifierArray() {}
+
+ /**
+ * Construct an array qualifier type.
+ *
+ * @param type
+ * The qualifier type of the arrays elements.
+ */
+ public QualifierArray(QualifierType type) {
+ this(type, DEFAULT_PARSE_DELIM, DEFAULT_FORMAT_DELIM);
+ }
+
+ /**
+ * Construct an array qualifier type.
+ *
+ * @param type
+ * The qualifier type of the arrays elements.
+ * @param delim
+ * The String of characters that delimit elements in a String
+ * representation of the array.
+ */
+ public QualifierArray(QualifierType type,
+ String parseDelim,
+ String formatDelim) {
+ this.type = type;
+ this.parseDelim = parseDelim;
+ this.formatDelim = formatDelim;
+ }
+
+ /**
+ * Get the arrays element qualifier type.
+ *
+ * @areturn
+ * The qualifier type of the arrays elements.
+ */
+ public QualifierType getElementType() {
+ return type;
+ }
+
+ /**
+ * Determine if the given value is a legal value for this type. The
+ * element delimiters are the default or those supplied during
+ * construction of the QualifierArray.
+ *
+ * @param value
+ * The value to test.
+ * @return
+ * Returns a Java type containing the parse value if legal, otherwise
+ * null is returned if the value was illegal.
+ */
+ public Object parseValue(String value) {
+ return parseValue(value, parseDelim);
+ }
+
+ /**
+ * Determine if the given value is a legal value for this type. The
+ * element delimiters provided override the use of the defaults or
+ * those supplied during the construction of the QualifierArray.
+ *
+ * @param value
+ * The value to test.
+ * @param parseDelim
+ * The String of characters that delimit elements in a String
+ * representation of the array.
+ * @return
+ * Returns a Java type containing the parse value if legal, otherwise
+ * null is returned if the value was illegal.
+ */
+ public Object parseValue(String value, String parseDelim) {
+ if (value == null) {
+ return null;
+ }
+
+ StringTokenizer tokenizer = new StringTokenizer(value, parseDelim);
+ ArrayList elements = new ArrayList();
+
+ while (tokenizer.hasMoreTokens()) {
+ String token = tokenizer.nextToken();
+ Object object = type.parseValue(token);
+
+ if (object == null) {
+ return null;
+ }
+
+ elements.add(object);
+ }
+
+ return elements.toArray(
+ (Object[])Array.newInstance(type.getJavaType(), elements.size()));
+ }
+
+ /**
+ * Format the given string if it is a legal value for this type. The
+ * element delimiters are the default or those supplied during
+ * construction of the QualifierArray.
+ *
+ * @param value
+ * The value to format.
+ * @return
+ * Returns a string containing the formatted value if legal, otherwise
+ * null is returned if the value was illegal.
+ */
+ public String formatValue(String value) {
+ return formatValue(value, parseDelim, formatDelim);
+ }
+
+ /**
+ * Format the given string if it is a legal value for this type. The
+ * element delimiters provided override the use of the defaults or
+ * those supplied during the construction of the QualifierArray.
+ *
+ * @param value
+ * The value to format.
+ * @param parseDelim
+ * The String of characters that delimit elements in a String
+ * representation of the array when it is parsed.
+ * @param formatDelim
+ * The String of characters that delimit elements in a String
+ * representation of the array when it is formatted.
+ * @return
+ * Returns a string containing the formatted value if legal, otherwise
+ * null is returned if the value was illegal.
+ */
+ public String formatValue(String value,
+ String parseDelim,
+ String formatDelim) {
+
+ if (value == null) {
+ return null;
+ }
+
+ value = value.trim();
+
+ StringTokenizer tokenizer = new StringTokenizer(value, parseDelim);
+ StringBuffer string = new StringBuffer();
+
+ while (tokenizer.hasMoreTokens()) {
+ String token = tokenizer.nextToken();
+ token = type.formatValue(token);
+
+ if (token == null) {
+ return null;
+ }
+
+ string.append(token);
+
+ if (tokenizer.hasMoreTokens()) {
+ string.append(formatDelim);
+ }
+ }
+
+ return string.toString();
+ }
+
+ /**
+ * Get the String containing the characters that delimit elements in
+ * a String representation of the array when it is parsed.
+ *
+ * @return
+ * Returns a String containing the characters that delimit elements in
+ * a String representation of the array when it is parsed.
+ */
+ public String getParseDelimiters() {
+ return parseDelim;
+ }
+
+ /**
+ * Get the String containing the characters that delimit elements in
+ * a String representation of the array when it is formatted.
+ *
+ * @return
+ * Returns a String containing the characters that delimit elements in
+ * a String representation of the array when it is formatted.
+ */
+ public String getFormatDelimiters() {
+ return formatDelim;
+ }
+
+ public Class getJavaType() {
+ return java.lang.reflect.Array.class;
+ }
+
+ public String toString() {
+ return "[L" + type.getClass().getName() + ";";
+ }
+
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierBoolean.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierBoolean.java
new file mode 100644
index 0000000000..7c5d5ab34d
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierBoolean.java
@@ -0,0 +1,121 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+package com.sun.dhcpmgr.data.qualifier;
+
+/**
+ * An implementation of the qualifier type that provide a boolean type.
+ * The symbolic values for true and false can be set in two ways during
+ * object construction. Either no arguments are given the constructor and the
+ * Java Boolean.TRUE and Boolean.FALSE are used. Alternatively a pair of
+ * Strings can be passed to the constructor, the first representing true
+ * and the second false.
+ */
+public class QualifierBoolean extends QualifierStringEnum {
+
+ /**
+ * True value.
+ */
+ protected String trueValue;
+
+ /**
+ * False value.
+ */
+ protected String falseValue;
+
+ /**
+ * Contruct a boolean qualifier type using the Java string representations
+ * of true and false.
+ */
+ public QualifierBoolean() {
+ this(Boolean.TRUE.toString(), Boolean.FALSE.toString());
+ }
+
+ /**
+ * Contruct a boolean qualifier using the supplied string representations
+ * of true and false.
+ *
+ * @param trueValue
+ * True value.
+ * @param falseValue
+ * False value.
+ */
+ public QualifierBoolean(String trueValue, String falseValue) {
+ super(new String[] {trueValue, falseValue});
+
+ this.trueValue = trueValue;
+ this.falseValue = falseValue;
+ }
+
+ /**
+ * Get the string representing true.
+ *
+ * @return
+ * True value.
+ */
+ public String getTrue() {
+ return trueValue;
+ }
+
+ /**
+ * Get the string representing false.
+ *
+ * @return
+ * False value.
+ */
+ public String getFalse() {
+ return falseValue;
+ }
+
+ public Object parseValue(String value) {
+ if (value == null) {
+ return null;
+ }
+
+ value = value.trim();
+
+ if (value.equals(trueValue) || value.equals(falseValue)) {
+ return new Boolean(value);
+ } else {
+ return null;
+ }
+ }
+
+ public String formatValue(String value) {
+ if (value == null || parseValue(value) == null) {
+ return null;
+ }
+
+ return value.trim();
+ }
+
+ public Class getJavaType() {
+ return Boolean.class;
+ }
+
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierEnum.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierEnum.java
new file mode 100644
index 0000000000..be457acf3c
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierEnum.java
@@ -0,0 +1,34 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+package com.sun.dhcpmgr.data.qualifier;
+
+/**
+ * Marker interface for qualifer enumerated types.
+ */
+public interface QualifierEnum {}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierFQDN.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierFQDN.java
new file mode 100644
index 0000000000..6f1a7fd2cc
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierFQDN.java
@@ -0,0 +1,58 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+package com.sun.dhcpmgr.data.qualifier;
+
+import java.util.regex.*;
+
+/**
+ * An implementation of the qualifier type that provides a string type
+ * where values must be a valid fully qualified domain name.
+ */
+public class QualifierFQDN extends QualifierString {
+
+ private static final String fqdnRegex = "([a-zA-Z][a-zA-Z0-9-]*[.]?)+";
+
+ public Object parseValue(String value) {
+ if (value == null) {
+ return null;
+ }
+
+ value = value.trim();
+
+ Pattern pattern = Pattern.compile(fqdnRegex);
+ Matcher matcher = pattern.matcher(value);
+
+ if (matcher.matches()) {
+ return value;
+ } else {
+ return null;
+ }
+ }
+
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierIPv4.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierIPv4.java
new file mode 100644
index 0000000000..5ae01ff3c1
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierIPv4.java
@@ -0,0 +1,62 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+package com.sun.dhcpmgr.data.qualifier;
+
+import java.util.regex.*;
+
+/**
+ * An implementation of the qualifier type that provides a string type
+ * where values must be a valid IPv4 address.
+ */
+public class QualifierIPv4 extends QualifierString {
+
+ private static final String octetRegex =
+ "0*(1?[0-9]{1,2}|2([0-4][0-9]|5[0-5]))";
+
+ private static final String addressRegex =
+ "(" + octetRegex + "[.]){3}" + octetRegex;
+
+ private static Pattern pattern;
+
+ public Object parseValue(String value) {
+ if (value == null) {
+ return null;
+ }
+
+ value = value.trim();
+
+ Matcher matcher = pattern.matcher(value);
+
+ return (matcher.matches() ? value : null);
+ }
+
+ static {
+ pattern = Pattern.compile(addressRegex);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierIPv6.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierIPv6.java
new file mode 100644
index 0000000000..23f83f29f6
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierIPv6.java
@@ -0,0 +1,106 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+package com.sun.dhcpmgr.data.qualifier;
+
+import java.util.*;
+import java.util.regex.*;
+
+/**
+ * An implementation of the qualifier type that provides a string type
+ * where values must be a valid IPv6 address.
+ */
+public class QualifierIPv6 extends QualifierString {
+
+ private static final String fieldRegex = "0*[0-9a-fA-F]{1,4}";
+
+ private static Pattern pattern;
+
+ public Object parseValue(String value) {
+ if (value == null) {
+ return null;
+ }
+
+ value = value.trim();
+
+ if (value.equals("::")) {
+ return value;
+ }
+
+ QualifierIPv4 ipv4 = new QualifierIPv4();
+ Matcher matcher;
+ int numFields = 0;
+ int numZeroFields = 0;
+ int numAdjacentZeroFields = 0;
+ StringTokenizer tokenizer = new StringTokenizer(value, ":", true);
+
+ while (tokenizer.hasMoreTokens()) {
+ String field = tokenizer.nextToken();
+
+ if (field == null) {
+ return null;
+ } else if (field.equals(":")) {
+ numAdjacentZeroFields++;
+
+ if (numAdjacentZeroFields > 2) {
+ return null;
+ } else if (numAdjacentZeroFields == 2) {
+ numZeroFields++;
+
+ if (numZeroFields > 1) {
+ return null;
+ }
+ }
+ } else {
+ numAdjacentZeroFields = 0;
+ matcher = pattern.matcher(field);
+
+ if (!matcher.matches()) {
+ Object ipv4Field = ipv4.parseValue(field);
+
+ if ((ipv4Field != null && tokenizer.countTokens() > 0) ||
+ ipv4Field == null) {
+ return null;
+ }
+ }
+
+ numFields++;
+ }
+ }
+
+ if ((numFields > 0 && numFields < 9) || value.equals("::")) {
+ return value;
+ } else {
+ return null;
+ }
+ }
+
+ static {
+ pattern = Pattern.compile(fieldRegex);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierImpl.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierImpl.java
new file mode 100644
index 0000000000..ba9f815869
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierImpl.java
@@ -0,0 +1,107 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+package com.sun.dhcpmgr.data.qualifier;
+
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * Super class for qualifier. Provides common methods and fields.
+ */
+public class QualifierImpl implements Qualifier {
+
+ /**
+ * Map of qualifier attributes to values.
+ */
+ protected Map attributes;
+
+ /**
+ * Construct an empty qualifier.
+ */
+ public QualifierImpl() {
+ }
+
+ /**
+ * Construct a qualifier, assigning all the required fields.
+ *
+ * @param keyword
+ * The name of the parameter that the qualifier is associated with.
+ * @param readOnly
+ * Inidicate whether the parameter is to be treated as read only.
+ * @param hidden
+ * Inidicate whether the parameter is hidden.
+ * @param type
+ * The parameter value type.
+ */
+ public QualifierImpl(String keyword,
+ boolean readOnly,
+ boolean hidden,
+ QualifierType type) {
+
+ attributes = new HashMap();
+ attributes.put(KEYWORD, keyword);
+ attributes.put(READONLY, new Boolean(readOnly));
+ attributes.put(HIDDEN, new Boolean(hidden));
+ attributes.put(TYPE, type);
+ }
+
+ public synchronized Object getAttribute(String attribute) {
+ return attributes.get(attribute);
+ }
+
+ public synchronized void setAttribute(String attribute, Object value) {
+ if (value == null) {
+ if (attributes.containsKey(attribute)) {
+ attributes.remove(attribute);
+ }
+ } else {
+ attributes.put(attribute, value);
+ }
+ }
+
+ public String getKeyword() {
+ return (String)attributes.get(KEYWORD);
+ }
+
+ public boolean isReadOnly() {
+ return ((Boolean)attributes.get(READONLY)).booleanValue();
+ }
+
+ public boolean isHidden() {
+ return ((Boolean)attributes.get(HIDDEN)).booleanValue();
+ }
+
+ public QualifierType getType() {
+ return (QualifierType)attributes.get(TYPE);
+ }
+
+ public String toString() {
+ return attributes.toString();
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierInteger.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierInteger.java
new file mode 100644
index 0000000000..f9b64c56fd
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierInteger.java
@@ -0,0 +1,57 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+package com.sun.dhcpmgr.data.qualifier;
+
+/**
+ * An implementation of the qualifier type that provides an integer type.
+ */
+public class QualifierInteger extends QualifierTypeImpl {
+
+ public Object parseValue(String value) {
+ if (value != null) {
+ try {
+ int intValue = Integer.parseInt(value.trim());
+ return new Integer(intValue);
+ } catch (NumberFormatException nfe) {}
+ }
+
+ return null;
+ }
+
+ public String formatValue(String value) {
+ Object object = parseValue(value);
+
+ return (object == null) ? null : object.toString();
+ }
+
+ public Class getJavaType() {
+ return Integer.class;
+ }
+
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierIntegerEnum.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierIntegerEnum.java
new file mode 100644
index 0000000000..7b2e4ce162
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierIntegerEnum.java
@@ -0,0 +1,94 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+package com.sun.dhcpmgr.data.qualifier;
+
+import java.util.Arrays;
+import java.util.ArrayList;
+
+/**
+ * An extension of the integer qualifier type that restricts the integer
+ * values to a given set.
+ */
+public class QualifierIntegerEnum extends QualifierInteger
+ implements QualifierEnum {
+
+ /**
+ * The set of legal integer values.
+ */
+ protected int[] values;
+
+ private QualifierIntegerEnum() {}
+
+ /**
+ * Construct an integer enumerated qualifier type.
+ *
+ * @param values
+ * The set of legal integer values.
+ */
+ public QualifierIntegerEnum(int[] values) {
+ this.values = values;
+ }
+
+ /**
+ * Get the set of legal integer values.
+ *
+ * @return
+ * The set of legal integer values.
+ */
+ public int[] getValues() {
+ return values;
+ }
+
+ public Object parseValue(String value) {
+ Integer intValue = (Integer) super.parseValue(value);
+
+ if (intValue != null) {
+ int i = intValue.intValue();
+
+ for (int index = 0; index < values.length; index++) {
+ if (values[index] == i) {
+ return intValue;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ public String toString() {
+ ArrayList vals = new ArrayList();
+
+ for (int index = 0; index < values.length; index++) {
+ vals.add(new Integer(values[index]));
+ }
+
+ return super.toString() + vals;
+ }
+
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierIntegerRange.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierIntegerRange.java
new file mode 100644
index 0000000000..f05a868279
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierIntegerRange.java
@@ -0,0 +1,101 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+package com.sun.dhcpmgr.data.qualifier;
+
+/**
+ * An extension of the integer qualifier type that restricts the integer
+ * values to a given range, bounded by an inclusive minimum and maximum value.
+ */
+public class QualifierIntegerRange extends QualifierInteger
+ implements QualifierRange {
+
+ /**
+ * Minimum legal value.
+ */
+ protected int min;
+
+ /**
+ * Maximum legal value.
+ */
+ protected int max;
+
+ private QualifierIntegerRange() {}
+
+ /**
+ * Construct an integer range qualifier type.
+ *
+ * @param min
+ * Minimum legal value.
+ * @param max
+ * Maximum legal value.
+ */
+ public QualifierIntegerRange(int min, int max) {
+ this.min = min;
+ this.max = max;
+ }
+
+ /**
+ * Get the minimum boundary.
+ *
+ * @return
+ * Minimum legal value.
+ */
+ public int getMin() {
+ return min;
+ }
+
+ /**
+ * Get the maximum boundary.
+ *
+ * @return
+ * Maximum legal value.
+ */
+ public int getMax() {
+ return max;
+ }
+
+ public Object parseValue(String value) {
+ Integer intValue = (Integer)super.parseValue(value);
+
+ if (intValue != null) {
+ int i = intValue.intValue();
+
+ if (i >= min && i <= max) {
+ return intValue;
+ }
+ }
+
+ return null;
+ }
+
+ public String toString() {
+ return super.toString() + "<" + min + "," + max + ">";
+ }
+
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierOr.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierOr.java
new file mode 100644
index 0000000000..0feb9b36f5
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierOr.java
@@ -0,0 +1,102 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+package com.sun.dhcpmgr.data.qualifier;
+
+/**
+ * This qualifier type allows the logical or of two qualifier types. Care
+ * must be taken that the two qualifier types are suitable. For instance
+ * if 'or'ing a string and an integer may result in parseValue() returning
+ * either a String or an Integer.
+ */
+public class QualifierOr extends QualifierTypeImpl {
+
+ protected QualifierType typeA;
+
+ protected QualifierType typeB;
+
+ public QualifierOr(QualifierType typeA, QualifierType typeB) {
+ this.typeA = typeA;
+ this.typeB = typeB;
+ }
+
+ public void setQualifierTypeA(QualifierType typeA) {
+ this.typeA = typeA;
+ }
+
+ public void setQualifierTypeB(QualifierType typeB) {
+ this.typeB = typeB;
+ }
+
+ public QualifierType getQualifierTypeA() {
+ return typeA;
+ }
+
+ public QualifierType getQualifierTypeB() {
+ return typeB;
+ }
+
+ public Object parseValue(String value) {
+ if (!typeA.getJavaType().equals(typeB.getJavaType())) {
+ return null;
+ }
+
+ Object objectA = typeA.parseValue(value);
+ Object objectB = typeB.parseValue(value);
+
+ if (objectA != null && objectB != null) {
+ return (objectA.equals(objectB)) ? objectA : null;
+ } else {
+ return (objectA == null) ? objectB : objectA;
+ }
+ }
+
+ public String formatValue(String value) {
+ if (!typeA.getJavaType().equals(typeB.getJavaType())) {
+ return null;
+ }
+
+ String stringA = typeA.formatValue(value);
+ String stringB = typeB.formatValue(value);
+
+ if (stringA != null && stringB != null) {
+ return (stringA.equals(stringB)) ? stringA : null;
+ } else {
+ return (stringA == null) ? stringB : stringA;
+ }
+ }
+
+ public Class getJavaType() {
+ return typeA.getJavaType();
+ }
+
+ public String toString() {
+ return "(" + typeA + " || " + typeB + ")";
+ }
+
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierRange.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierRange.java
new file mode 100644
index 0000000000..4fb61824e4
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierRange.java
@@ -0,0 +1,34 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+package com.sun.dhcpmgr.data.qualifier;
+
+/**
+ * Marker interface for qualifer range types.
+ */
+public interface QualifierRange {}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierString.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierString.java
new file mode 100644
index 0000000000..b72a359480
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierString.java
@@ -0,0 +1,48 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+package com.sun.dhcpmgr.data.qualifier;
+
+/**
+ * An implementation of the qualifier type that provides a string type.
+ */
+public class QualifierString extends QualifierTypeImpl {
+
+ public Object parseValue(String value) {
+ return value.trim();
+ }
+
+ public String formatValue(String value) {
+ if (value == null) {
+ return null;
+ }
+
+ return value.trim();
+ }
+
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierStringEnum.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierStringEnum.java
new file mode 100644
index 0000000000..fa5fc8efba
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierStringEnum.java
@@ -0,0 +1,79 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+package com.sun.dhcpmgr.data.qualifier;
+
+import java.util.Arrays;
+
+/**
+ * An extension of the string qualifier type that restricts the string
+ * values to a given set.
+ */
+public class QualifierStringEnum extends QualifierString
+ implements QualifierEnum {
+
+ /**
+ * The set of legal string values.
+ */
+ protected String[] values;
+
+ private QualifierStringEnum() {}
+
+ /**
+ * Construct an string enumerated qualifier type.
+ *
+ * @param values
+ * The set of legal string values.
+ */
+ public QualifierStringEnum(String[] values) {
+ this.values = values;
+ }
+
+ /**
+ * Get the set of legal string values.
+ *
+ * @return
+ * The set of legal string values.
+ */
+ public String[] getValues() {
+ return values;
+ }
+
+ public Object parseValue(String value) {
+ if (value != null && Arrays.asList(values).contains(value)) {
+ return value;
+ } else {
+ return null;
+ }
+ }
+
+ public String toString() {
+ return super.toString() + Arrays.asList(values);
+ }
+
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierType.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierType.java
new file mode 100644
index 0000000000..33bf0cef14
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierType.java
@@ -0,0 +1,74 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+package com.sun.dhcpmgr.data.qualifier;
+
+/**
+ * Common interface for qualifier types.
+ */
+public interface QualifierType {
+
+ /**
+ * Determine if the given string is a legal value for this type.
+ *
+ * @param value
+ * The value to test.
+ * @return
+ * Returns a Java type containing the parsed value if legal, otherwise
+ * null is returned if the value was illegal.
+ */
+ public Object parseValue(String value);
+
+ /**
+ * Format the value into a form that could be offered to parseValue().
+ * The validity of the value is implementation dependent. The value
+ * passed to formatValue() may result in a non-null result but the same
+ * value passed to parseValue() may return null. Also the value returned
+ * by formatValue() passed to parseValue() does not guarantee an non-null
+ * result from parseValue().
+ *
+ * @param value
+ * The value to format.
+ * @return
+ * Returns a String containing the formatted value or null if the
+ * value could not be formatted correctly.
+ */
+ public String formatValue(String value);
+
+ /**
+ * Get the Java class that is suitable for storing values of the qualifier
+ * type.
+ *
+ * @return
+ * Suitable Java type for storing values of the qualifier type.
+ */
+ public Class getJavaType();
+
+ public String toString();
+
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierTypeImpl.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierTypeImpl.java
new file mode 100644
index 0000000000..7b21477b55
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierTypeImpl.java
@@ -0,0 +1,44 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+package com.sun.dhcpmgr.data.qualifier;
+
+/**
+ * Super class for qualifier types. Provides common methods and fields.
+ */
+public abstract class QualifierTypeImpl implements QualifierType {
+
+ public Class getJavaType() {
+ return String.class;
+ }
+
+ public String toString() {
+ return this.getClass().getName();
+ }
+
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/DhcpHostsTable.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/DhcpHostsTable.java
new file mode 100644
index 0000000000..2483a3f4fb
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/DhcpHostsTable.java
@@ -0,0 +1,343 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.server;
+
+import com.sun.dhcpmgr.bridge.*;
+import com.sun.dhcpmgr.data.*;
+import com.sun.wbem.utility.directorytable.*;
+
+import java.net.*;
+
+/**
+ * This class provides the capabilities for managing the hosts table.
+ */
+public class DhcpHostsTable {
+
+ /**
+ * This is the handle to the host table as defined by the config file
+ * Access to this handle is synchronized.
+ */
+ private static DhcpHostsTable cfgHostsTable = null;
+
+ private DirectoryTable hostsTable;
+ private int addrColumn = 0;
+ private int cnameColumn = 0;
+ private int aliasesColumn = 0;
+ private int commentColumn = 0;
+
+ /**
+ * Create a new DhcpHostsTable of type resource in the specified domain
+ * @param resource the host resource(eg., files, dns, nisplus)
+ * @param domain the domain (if any) for the host resource
+ */
+ public DhcpHostsTable(String resource, String domain)
+ throws BridgeException {
+
+ // Determine the local host name. Directory table url requires it.
+ //
+ String server = null;
+ try {
+ server = InetAddress.getLocalHost().getHostName();
+ } catch (Throwable e) {
+ throw new BridgeException(
+ ResourceStrings.getString("get_host_err"));
+ }
+
+ // Build the url.
+ //
+ StringBuffer url = new StringBuffer();
+ if (resource.equals(DhcpConfigOpts.DSVC_CV_FILES)) {
+ url.append("file");
+ } else {
+ url.append(resource);
+ }
+ url.append(":/");
+ url.append(server);
+ url.append("/");
+ if (resource.equals(DhcpConfigOpts.DSVC_CV_FILES)) {
+ url.append(server);
+ } else {
+ url.append(domain);
+ }
+ try {
+ DirectoryTableFactory factory = new DirectoryTableFactory();
+ hostsTable = factory.getDirectoryTableInstance(url.toString());
+
+ TableDefinitions defs = hostsTable.getTableDefinitionsInstance();
+ defs.loadTableDefinitions(TableDefinitions.TN_HOSTS);
+ addrColumn =
+ defs.getColumnNumber(TableDefinitions.CN_HOSTS_ADDR);
+ cnameColumn =
+ defs.getColumnNumber(TableDefinitions.CN_HOSTS_CNAME);
+ aliasesColumn =
+ defs.getColumnNumber(TableDefinitions.CN_HOSTS_ALIASES);
+ commentColumn =
+ defs.getColumnNumber(TableDefinitions.CN_HOSTS_COMMENT);
+ } catch (Throwable e) {
+ throw new BridgeException(
+ ResourceStrings.getString("hosts_access_err"));
+ }
+
+ } // end constructor
+
+ /**
+ * Checks access on the host table.
+ * @param requestedAccess the desired access
+ * @return true if the desired access can be granted
+ */
+ public synchronized boolean canAccessTable(int requestedAccess)
+ throws BridgeException {
+
+ int access = DirectoryTable.NO_ACCESS;
+
+ try {
+ access = hostsTable.access(TableDefinitions.TN_HOSTS);
+ } catch (Throwable e) {
+ // No access apparently
+ }
+
+ return ((access & requestedAccess) == requestedAccess);
+ } // openTable
+
+ /**
+ * Opens the host table.
+ */
+ public synchronized void openTable()
+ throws BridgeException {
+
+ try {
+ hostsTable.open(TableDefinitions.TN_HOSTS);
+ } catch (Throwable e) {
+ throw new BridgeException(
+ ResourceStrings.getString("hosts_open_err"));
+ }
+
+ } // openTable
+
+ /**
+ * Closes the host table.
+ */
+ public synchronized void closeTable()
+ throws BridgeException {
+
+ try {
+ hostsTable.close();
+ } catch (Throwable e) {
+ throw new BridgeException(
+ ResourceStrings.getString("hosts_close_err"));
+ }
+
+ } // closeTable
+
+
+ /**
+ * Finds a host entry by name and returns its address.
+ * @param name host name
+ * @return address of entry or null if entry does not exist.
+ */
+ public synchronized String getHostAddress(String name) {
+
+ String address = null;
+ try {
+ DirectoryRow record = hostsTable.getRowInstance();
+ record.putColumn(addrColumn, "");
+ record.putColumn(cnameColumn, name);
+ record.putColumn(aliasesColumn, "");
+ record.putColumn(commentColumn, "");
+
+ record = hostsTable.getFirstRow(record);
+
+ address = record.getColumn(addrColumn);
+
+ } catch (Throwable e) {
+ // Nothing to do
+ }
+
+ return address;
+ }
+
+ /**
+ * Finds a host entry by address and returns its name.
+ * @param address host address
+ * @return name of entry or null if entry does not exist.
+ */
+ public synchronized String getHostName(String address) {
+
+ String name = null;
+ try {
+ DirectoryRow record = hostsTable.getRowInstance();
+ record.putColumn(addrColumn, address);
+ record.putColumn(cnameColumn, "");
+ record.putColumn(aliasesColumn, "");
+ record.putColumn(commentColumn, "");
+
+ record = hostsTable.getFirstRow(record);
+
+ name = record.getColumn(cnameColumn);
+
+ } catch (Throwable e) {
+ // Nothing to do
+ }
+
+ return name;
+ }
+
+ /**
+ * Add an entry to the hosts table.
+ * @param addr host address
+ * @param name host name
+ * @param comment comment for host entry
+ */
+ public synchronized void createHostsRecord(String addr, String name,
+ String comment) throws BridgeException {
+
+
+ if (getHostName(addr) != null) {
+ throw new HostExistsException(addr);
+ }
+
+ if (getHostAddress(name) != null) {
+ throw new HostExistsException(name);
+ }
+
+ try {
+ DirectoryRow record = hostsTable.getRowInstance();
+
+ record.putColumn(addrColumn, addr);
+ record.putColumn(cnameColumn, name);
+ record.putColumn(aliasesColumn, "");
+ record.putColumn(commentColumn, comment);
+
+ hostsTable.addRow(record);
+ } catch (Throwable e) {
+ throw new BridgeException(
+ ResourceStrings.getString("hosts_add_err"));
+ }
+
+ } // createHostsRecord
+
+ /**
+ * Remove an entry from the hosts table
+ * @param addr host address of entry to remove
+ */
+ public synchronized void deleteHostsRecord(String addr)
+ throws BridgeException {
+
+ try {
+ DirectoryRow record = hostsTable.getRowInstance();
+ record.putColumn(addrColumn, addr);
+ hostsTable.deleteRow(record);
+ } catch (DirectoryTableRowNotFoundException e) {
+ throw new NoHostsEntryException(addr);
+ } catch (Throwable e) {
+ throw new BridgeException(
+ ResourceStrings.getString("hosts_remove_err"));
+ }
+
+ } // deleteHostsRecord
+
+ /**
+ * Modify an entry on the hosts table.
+ * @param oldAddr host address of entry to modify
+ * @param newAddr new host address for entry
+ * @param name new host name
+ * @param comment new comment for host entry
+ */
+ public synchronized void modifyHostsRecord(String oldAddr, String newAddr,
+ String name, String comment) throws BridgeException {
+
+ if (getHostAddress(name) != null) {
+ throw new HostExistsException(name);
+ }
+
+ try {
+ DirectoryRow oldRecord = hostsTable.getRowInstance();
+ oldRecord.putColumn(addrColumn, oldAddr);
+
+ DirectoryRow newRecord = hostsTable.getRowInstance();
+ newRecord.putColumn(addrColumn, newAddr);
+ newRecord.putColumn(cnameColumn, name);
+ newRecord.putColumn(aliasesColumn, "");
+ newRecord.putColumn(commentColumn, comment);
+
+ hostsTable.modifyRow(oldRecord, newRecord);
+ } catch (DirectoryTableRowNotFoundException e) {
+ throw new NoHostsEntryException(oldAddr);
+ } catch (Throwable e) {
+ throw new BridgeException(
+ ResourceStrings.getString("hosts_modify_err"));
+ }
+ } // modifyHostsRecord
+
+ /**
+ * Determines the whether or not the hosts table under a given
+ * name service can be managed.
+ * @param resource name service resource (files, dns, nisplus)
+ * @param domain the name service domain (ignored for files)
+ * @return true if the user can manage the table
+ */
+ public static boolean isHostsValid(String resource, String domain) {
+
+ boolean result = false;
+ try {
+ DhcpHostsTable hostsTable = new DhcpHostsTable(resource, domain);
+ result = hostsTable.canAccessTable(DirectoryTable.MODIFY_ACCESS);
+ } catch (Throwable e) {
+ // No access apparently
+ }
+ return (result);
+ } // isHostsManageable
+
+ /**
+ * Constructs a DhcpHostsTable as defined by the DHCP config file.
+ * This object is a singleton (only one will ever be created and will
+ * be cached).
+ * @param bridge the bridge object to the native library
+ * @return the DhcpHostsTable
+ */
+ public static synchronized DhcpHostsTable getCfgHostsTable(Bridge bridge)
+ throws BridgeException {
+
+ if (cfgHostsTable == null) {
+ try {
+ DhcpdOptions options = bridge.readDefaults();
+ String resource = options.getHostsResource();
+ if (resource != null) {
+ String domain = options.getHostsDomain();
+ cfgHostsTable = new DhcpHostsTable(resource, domain);
+ }
+ } catch (Throwable e) {
+ throw new BridgeException(e.getMessage());
+ }
+ }
+ return cfgHostsTable;
+
+ } // getCfgHostsTable
+
+} // DhcpHostsTable
+
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/DhcpMgr.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/DhcpMgr.java
new file mode 100644
index 0000000000..fe4d3fdd1c
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/DhcpMgr.java
@@ -0,0 +1,66 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1998-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.server;
+
+import java.io.IOException;
+import java.io.OptionalDataException;
+
+import com.sun.dhcpmgr.data.*;
+import com.sun.dhcpmgr.bridge.*;
+
+public interface DhcpMgr {
+ public DhcpNetMgr getNetMgr();
+ public DhcptabMgr getDhcptabMgr();
+ public DhcpServiceMgr getDhcpServiceMgr();
+ public String getLockPath();
+ public Object openExportFile(String name, String user, int recCount,
+ Network [] nets, boolean overWrite)
+ throws ExistsException, IOException;
+ public Object openImportFile(String name)
+ throws IOException;
+ public ExportHeader getExportHeader(Object ref)
+ throws IOException, ClassNotFoundException;
+ public void exportMacros(Object ref, boolean allMacros, String [] names)
+ throws BridgeException, IOException;
+ public void exportOptions(Object ref, boolean allOptions, String [] names)
+ throws BridgeException, IOException;
+ public void exportNetwork(Object ref, Network net)
+ throws BridgeException, IOException;
+ public ActionError [] importOptions(Object ref, boolean overwrite)
+ throws IOException, OptionalDataException, ClassNotFoundException;
+ public ActionError [] importMacros(Object ref, boolean overwrite)
+ throws IOException, OptionalDataException, ClassNotFoundException;
+ public ActionError [] importNetwork(Network net, Object ref,
+ boolean overwrite)
+ throws IOException, OptionalDataException, ClassNotFoundException,
+ BridgeException;
+ public void closeExportFile(Object ref, boolean deleteFile)
+ throws IOException;
+ public void closeImportFile(Object ref, boolean deleteFile)
+ throws IOException;
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/DhcpMgrImpl.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/DhcpMgrImpl.java
new file mode 100644
index 0000000000..9e7cdfeed5
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/DhcpMgrImpl.java
@@ -0,0 +1,510 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1998-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.server;
+
+import java.io.*;
+import java.util.zip.*;
+import java.util.*;
+
+import com.sun.dhcpmgr.bridge.*;
+import com.sun.dhcpmgr.data.*;
+
+public class DhcpMgrImpl implements DhcpMgr {
+ private Bridge bridge;
+ private DhcpNetMgrImpl netMgr;
+ private DhcptabMgrImpl dtMgr;
+ private DhcpServiceMgrImpl srvMgr;
+ // Global lock file used to ensure only one export or import is running
+ private static final String LOCK_FILE = "/var/run/dhcp_import_export_lock";
+ private static final File lockFile = new File(LOCK_FILE);
+ private File currentlyOpenFile = null;
+ private Object currentStream = null;
+
+ public DhcpMgrImpl() {
+ bridge = new Bridge();
+ }
+
+ public DhcpNetMgr getNetMgr() {
+ if (netMgr == null) {
+ netMgr = new DhcpNetMgrImpl(bridge);
+ }
+ return netMgr;
+ }
+
+ public DhcptabMgr getDhcptabMgr() {
+ if (dtMgr == null) {
+ dtMgr = new DhcptabMgrImpl(bridge);
+ }
+ return dtMgr;
+ }
+
+ public DhcpServiceMgr getDhcpServiceMgr() {
+ if (srvMgr == null) {
+ srvMgr = new DhcpServiceMgrImpl(bridge);
+ }
+ return srvMgr;
+ }
+
+ /**
+ * Set the file which is currently open.
+ */
+ private synchronized File setFile(String name) throws IOException {
+ // Some other file is currently listed as open; deny the request
+ if (currentlyOpenFile != null) {
+ return null;
+ }
+
+ // Get system-wide lock by atomically creating lockfile
+ if (!lockFile.createNewFile()) {
+ return null;
+ }
+ currentlyOpenFile = new File(name);
+ currentStream = null;
+ return currentlyOpenFile;
+ }
+
+ private synchronized void clearFile(File file) {
+ // If this is truly the currently open file, then clear our reference
+ if (isFileOpen(file)) {
+ currentlyOpenFile = null;
+ currentStream = null;
+ // Release system-wide lock by deleting lockfile
+ lockFile.delete();
+ }
+ }
+
+ /**
+ * Get the file which is currently open.
+ */
+ private synchronized File getFile() {
+ return currentlyOpenFile;
+ }
+
+ /**
+ * Test whether a file is currently open.
+ */
+ private synchronized boolean isFileOpen(File file) {
+ return (file == currentlyOpenFile);
+ }
+
+ /**
+ * Returns the fullpath to the lock file.
+ * @return the fullpath to the lock file.
+ */
+ public String getLockPath() {
+ return (LOCK_FILE);
+ }
+
+ /**
+ * Opens an export file and writes a header record to it.
+ * @param fileName the fullpath to the export file to create.
+ * @param user the name of the user creating this file
+ * @param nets an array of networks which will be exported
+ * @param overwrite true if file should be forcible overwritten
+ * @return a reference key for this file instance, or null if there is
+ * another file already open for import or export
+ */
+ public Object openExportFile(String fileName, String user,
+ int recCount, Network [] nets, boolean overwrite)
+ throws ExistsException, IOException {
+
+ // Grab the lock
+ File file = setFile(fileName);
+
+ if (file != null) {
+ // File exists and not supposed to overwrite, throw exception
+ if (!overwrite && file.exists()) {
+ clearFile(file);
+ throw new ExistsException(fileName);
+ }
+
+ try {
+ // Open a stream to write on
+ ObjectOutputStream export = new ObjectOutputStream(
+ new GZIPOutputStream(new FileOutputStream(file)));
+
+ // Construct a header record, and write it
+ ExportHeader header = new ExportHeader(
+ getDhcpServiceMgr().getServerName(), user, recCount, nets);
+ export.writeObject(header);
+
+ // Save stream reference
+ currentStream = export;
+ } catch (IOException e) {
+ // Something went wrong, release the lock and re-throw
+ clearFile(file);
+ throw e;
+ }
+ }
+ // Give caller a reference to use for writing additional data
+ return file;
+ } // openExportFile
+
+ /**
+ * Close an export file, delete it if need be
+ * @param ref Reference to the open file, returned from openExportFile
+ * @param delete true if file is to be deleted on close, false otherwise.
+ */
+ public void closeExportFile(Object ref, boolean delete) throws IOException {
+ if (!isFileOpen((File)ref)) {
+ throw new FileNotFoundException(((File)ref).getName());
+ }
+ try {
+ ObjectOutputStream oos = (ObjectOutputStream)currentStream;
+ oos.flush();
+ oos.close();
+ if (delete) {
+ ((File)ref).delete();
+ }
+ } catch (IOException e) {
+ // Just re-throw, let finally block clean up
+ throw e;
+ } finally {
+ /*
+ * Always release the lock, we consider the file no longer useful
+ * no matter the outcome above.
+ */
+ clearFile((File)ref);
+ }
+ }
+
+ /**
+ * Open an import file
+ * @param fileName Name of file to open
+ * @return A reference to the opened import file, or null if another
+ * export or import is already in progress.
+ */
+ public Object openImportFile(String fileName) throws IOException {
+ File file = setFile(fileName);
+ if (file != null) {
+ if (!file.exists()) {
+ clearFile(file);
+ throw new FileNotFoundException(fileName);
+ }
+
+ try {
+ currentStream = new ObjectInputStream(new GZIPInputStream(
+ new FileInputStream(file)));
+ } catch (IOException e) {
+ clearFile(file);
+ throw e;
+ }
+ }
+ // Return reference caller can use to actually do the import
+ return file;
+ }
+
+ /**
+ * Close an import file, delete it if need be
+ * @param ref Reference to the open file, returned from openImportFile
+ * @param delete true if file is to be deleted on close, false otherwise.
+ */
+ public void closeImportFile(Object ref, boolean delete) throws IOException {
+ if (!isFileOpen((File)ref)) {
+ throw new FileNotFoundException(((File)ref).getName());
+ }
+ try {
+ ((ObjectInputStream)currentStream).close();
+ if (delete) {
+ ((File)ref).delete();
+ }
+ } catch (IOException e) {
+ // Just re-throw and let finally do the cleanup
+ throw e;
+ } finally {
+ clearFile((File)ref);
+ }
+ }
+
+ /**
+ * Retrieve the export header for the import file
+ * @param ref Reference to the file we're reading from
+ * @return The ExportHeader written at export time.
+ */
+ public ExportHeader getExportHeader(Object ref)
+ throws IOException, ClassNotFoundException {
+ if (!isFileOpen((File)ref)) {
+ // No such file open; throw an exception
+ throw new FileNotFoundException(((File)ref).getName());
+ } else {
+ ObjectInputStream ois = (ObjectInputStream)currentStream;
+ ExportHeader rec = (ExportHeader)ois.readObject();
+ return rec;
+ }
+ }
+
+ // Get the desired records out of an array
+ private ArrayList getSelectedRecs(String [] names, DhcptabRecord [] recs)
+ throws NoEntryException {
+ // Grab only the ones we want
+ TreeSet nameSet = new TreeSet(Arrays.asList(names));
+ ArrayList recArr = new ArrayList();
+ for (int i = 0; i < recs.length; ++i) {
+ if (nameSet.contains(recs[i].getKey())) {
+ recArr.add(recs[i]);
+ nameSet.remove(recs[i].getKey());
+ }
+ }
+ if (!nameSet.isEmpty()) {
+ // We didn't find one of the requested records
+ throw new NoEntryException((String)nameSet.first());
+ }
+ return recArr;
+ }
+
+ /**
+ * Export a list of macros specified by name to a file.
+ * @param ref A reference to the file, acquired from openExportFile()
+ * @param allMacros true if all macros are to be exported
+ * @param names names of macros to be exported if allMacros is false
+ */
+ public void exportMacros(Object ref, boolean allMacros, String [] names)
+ throws BridgeException, IOException {
+ if (!isFileOpen((File)ref)) {
+ // throw an exception that this is a bad reference
+ throw new FileNotFoundException(((File)ref).getName());
+ }
+
+ Macro [] macros = getDhcptabMgr().getMacros();
+ if (!allMacros) {
+ // Grab only the ones we want
+ ArrayList macArr = getSelectedRecs(names, macros);
+ macros = (Macro [])macArr.toArray(new Macro[0]);
+ }
+
+ ObjectOutputStream oos = (ObjectOutputStream)currentStream;
+ oos.writeObject(macros);
+ }
+
+ /**
+ * Export a list of options specified by name to a file.
+ * @param ref A reference to the file, acquired from openExportFile()
+ * @param allOptions true if all options are to be exported
+ * @param names names of options to be exported if allOptions is false
+ */
+ public void exportOptions(Object ref, boolean allOptions, String [] names)
+ throws BridgeException, IOException {
+ if (!isFileOpen((File)ref)) {
+ // throw an exception that this is a bad reference
+ throw new FileNotFoundException(((File)ref).getName());
+ }
+
+ Option [] options = getDhcptabMgr().getOptions();
+ if (!allOptions) {
+ // Grab only the ones we want
+ ArrayList optArr = getSelectedRecs(names, options);
+ options = (Option [])optArr.toArray(new Option[0]);
+ }
+
+ ObjectOutputStream oos = (ObjectOutputStream)currentStream;
+ oos.writeObject(options);
+ }
+
+ /**
+ * Export a network and its client records to a file
+ * @param ref A reference to the file, acquired from openExportFile()
+ * @param net Network to be exported
+ */
+ public void exportNetwork(Object ref, Network net)
+ throws BridgeException, IOException {
+ if (!isFileOpen((File)ref)) {
+ // throw an exception that this is a bad reference
+ throw new FileNotFoundException(((File)ref).getName());
+ }
+
+ // Get clients from database
+ DhcpClientRecord [] clients =
+ getNetMgr().loadNetworkCompletely(net.toString());
+
+ // Now write client array for this net
+ ObjectOutputStream oos = (ObjectOutputStream)currentStream;
+ oos.writeObject(clients);
+ }
+
+ /**
+ * Import dhcptab records from an export file into the configuration.
+ * @param recType The type of record to import; must be either
+ * DhcptabRecord.MACRO or DhcptabRecord.OPTION
+ * @param ref The file reference returned by openImportFile()
+ * @param overwrite true if this data should overwrite existing data
+ * @return An array of import results; empty if all records were imported.
+ */
+ private ActionError [] importDhcptabRecs(String recType, Object ref,
+ boolean overwrite)
+ throws IOException, OptionalDataException, ClassNotFoundException {
+
+ ArrayList resultList = new ArrayList();
+ DhcptabRecord [] recs = new DhcptabRecord[0];
+
+ if (!isFileOpen((File)ref)) {
+ // No such file open; throw an exception
+ throw new FileNotFoundException(((File)ref).getName());
+ }
+ ObjectInputStream ois = (ObjectInputStream)currentStream;
+ recs = (DhcptabRecord [])ois.readObject();
+ // Try to cast to appropriate type to ensure data is OK.
+ if (recType.equals(DhcptabRecord.MACRO)) {
+ Macro [] macros = (Macro []) recs;
+ } else {
+ Option [] options = (Option []) recs;
+ }
+
+ DhcptabMgr mgr = getDhcptabMgr();
+ for (int i = 0; recs != null && i < recs.length; ++i) {
+ try {
+ if (overwrite) {
+ /*
+ * Hack alert! We reset the signature to a default value
+ * that the datastores will not interpret. This allows us
+ * to forcibly delete the record, even if it came from a
+ * previous attempt to import this record. Without this
+ * step, the datastore may (correctly) signal an update
+ * collision and refuse to perform the delete. An
+ * alternative that might be used is to mark the signature
+ * member of DhcptabRecord as transient; however, that would
+ * have the future undesirable effect of dropping that
+ * field when we put a remote communication method
+ * in the mix which uses serialization, such as RMI.
+ */
+ recs[i].setSignature(DhcptabRecord.DEFAULT_SIGNATURE);
+ mgr.deleteRecord(recs[i], false);
+ }
+ } catch (Throwable t) {
+ // Do nothing; we'll probably have an error on the create
+ }
+ try {
+ mgr.createRecord(recs[i], false);
+ } catch (Exception e) {
+ // Record the error, we try all of them no matter what
+ resultList.add(new ActionError(recs[i].getKey(), e));
+ }
+ }
+
+ return (ActionError [])resultList.toArray(new ActionError[0]);
+ }
+
+ /**
+ * Import options from an export file.
+ * @param ref Reference to import file returned by openImportFile
+ * @param overwrite true if existing data should be overwritten.
+ * @return An array of errors in the import process; empty if all OK
+ */
+ public ActionError [] importOptions(Object ref, boolean overwrite)
+ throws IOException, OptionalDataException, ClassNotFoundException {
+ return importDhcptabRecs(DhcptabRecord.OPTION, ref, overwrite);
+ }
+
+ /**
+ * Import macros from an export file.
+ * @param ref Reference to import file returned by openImportFile
+ * @param overwrite true if existing data should be overwritten.
+ * @return An array of errors in the import process; empty if all OK
+ */
+ public ActionError [] importMacros(Object ref, boolean overwrite)
+ throws IOException, OptionalDataException, ClassNotFoundException {
+ return importDhcptabRecs(DhcptabRecord.MACRO, ref, overwrite);
+ }
+
+
+
+ /**
+ * Import network records from an export file into the configuration.
+ * @param net The network which is expected to be imported
+ * @param ref The file reference returned by openImportFile()
+ * @param overwrite true if this data should overwrite existing data
+ * @return An array of import results; empty if all records were imported.
+ */
+ public ActionError [] importNetwork(Network net, Object ref,
+ boolean overwrite) throws IOException, OptionalDataException,
+ ClassNotFoundException, BridgeException {
+
+ if (!isFileOpen((File)ref)) {
+ // No such file open; throw an exception
+ throw new FileNotFoundException(((File)ref).getName());
+ }
+
+ ArrayList resultList = new ArrayList();
+ DhcpClientRecord [] clients = null;
+ ObjectInputStream ois = (ObjectInputStream)currentStream;
+ clients = (DhcpClientRecord [])ois.readObject();
+
+ String networkName = net.toString();
+ DhcpNetMgr mgr = getNetMgr();
+
+ // Create the network table. It may already exist.
+ boolean netExisted = false;
+ try {
+ mgr.createNetwork(networkName);
+ } catch (TableExistsException e) {
+ /*
+ * This is o.k. no matter whether overwrite is true or not;
+ * however, record the fact that it existed so that we don't
+ * optimize out the delete in the loop below.
+ */
+ netExisted = true;
+ }
+
+ // Add the addresses to the table and record any exceptions.
+ for (int i = 0; clients != null && i < clients.length; ++i) {
+ /*
+ * If we're supposed to overwrite and the network table
+ * existed before we started, then try to delete the client
+ */
+ if (overwrite && netExisted) {
+ try {
+ /*
+ * Hack alert! We reset the signature to a default value
+ * that the datastores will not interpret. This allows us
+ * to forcibly delete the record, even if it came from a
+ * previous attempt to import this record. Without this
+ * step, the datastore may "correctly" signal an update
+ * collision and refuse to perform the delete. An
+ * alternative that might be used is to mark the signature
+ * member of DhcptabRecord as transient; however, that would
+ * have the future undesirable effect of dropping that
+ * field when we put a remote communication method
+ * in the mix which uses serialization, such as RMI.
+ */
+ clients[i].setSignature(DhcpClientRecord.DEFAULT_SIGNATURE);
+ mgr.deleteClient(clients[i], networkName, true);
+ } catch (Throwable t) {
+ // Ignore delete error, we'll probably have an error on add
+ }
+ }
+ try {
+ // Now add the client
+ mgr.addClient(clients[i], networkName);
+ } catch (Exception e) {
+ String address = clients[i].getClientIPAddress();
+ resultList.add(new ActionError(address, e));
+ }
+ }
+
+ return (ActionError [])resultList.toArray(new ActionError[0]);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/DhcpNetMgr.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/DhcpNetMgr.java
new file mode 100644
index 0000000000..645a128194
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/DhcpNetMgr.java
@@ -0,0 +1,78 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1998-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.server;
+
+import com.sun.dhcpmgr.bridge.*;
+import com.sun.dhcpmgr.data.*;
+
+/**
+ * This class defines the methods available to manage the DHCP network
+ * tables and hosts table.
+ */
+public interface DhcpNetMgr {
+ public Network getNetwork(String networkName)
+ throws BridgeException;
+ public Network [] getNetworks(DhcpDatastore datastore)
+ throws BridgeException;
+ public Network [] getNetworks()
+ throws BridgeException;
+ public DhcpClientRecord [] loadNetwork(String network,
+ DhcpDatastore datastore) throws BridgeException;
+ public DhcpClientRecord [] loadNetwork(String network)
+ throws BridgeException;
+ public DhcpClientRecord [] loadNetworkCompletely(String network)
+ throws BridgeException;
+ public void modifyClient(DhcpClientRecord oldClient,
+ DhcpClientRecord newClient, String table)
+ throws BridgeException;
+ public void modifyClient(DhcpClientRecord oldClient,
+ DhcpClientRecord newClient, String table,
+ DhcpDatastore datastore) throws BridgeException;
+ public void addClient(DhcpClientRecord client, String table,
+ DhcpDatastore datastore) throws BridgeException;
+ public void addClient(DhcpClientRecord client, String table)
+ throws BridgeException;
+ public void deleteClient(DhcpClientRecord client, String table,
+ boolean deleteHosts, DhcpDatastore datastore)
+ throws BridgeException;
+ public void deleteClient(DhcpClientRecord client, String table,
+ boolean deleteHosts) throws BridgeException;
+ public DhcpClientRecord getClient(DhcpClientRecord client, String table,
+ DhcpDatastore datastore) throws BridgeException;
+ public void cvtNetwork(String network,
+ DhcpDatastore datastore) throws BridgeException;
+ public void createNetwork(String network,
+ DhcpDatastore datastore) throws BridgeException;
+ public void createNetwork(String network)
+ throws BridgeException;
+ public void deleteNetwork(String network, boolean deleteMacro,
+ boolean deleteHosts, DhcpDatastore datastore)
+ throws BridgeException;
+ public void deleteNetwork(String network, boolean deleteMacro,
+ boolean deleteHosts) throws BridgeException;
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/DhcpNetMgrImpl.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/DhcpNetMgrImpl.java
new file mode 100644
index 0000000000..4a74ed4dae
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/DhcpNetMgrImpl.java
@@ -0,0 +1,424 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1998-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.server;
+
+import com.sun.dhcpmgr.bridge.*;
+import com.sun.dhcpmgr.data.*;
+
+import com.sun.wbem.utility.directorytable.*;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+/**
+ * This class provides the functionality to manage the DHCP network tables and
+ * the hosts table.
+ */
+public class DhcpNetMgrImpl implements DhcpNetMgr {
+ private Bridge bridge;
+
+ public DhcpNetMgrImpl(Bridge bridge) {
+ this.bridge = bridge;
+ }
+
+ /**
+ * Return the Network corresponding to the network string
+ * @return a Network
+ */
+ public Network getNetwork(String network)
+ throws BridgeException {
+
+ return bridge.getNetwork(network);
+ }
+
+ /**
+ * Return the list of networks currently known to DHCP
+ * @return an array of Networks
+ */
+ public Network [] getNetworks() throws BridgeException {
+ return getNetworks(null);
+ }
+
+ public Network [] getNetworks(DhcpDatastore datastore)
+ throws BridgeException {
+ return bridge.getNetworks(datastore);
+ }
+
+ /**
+ * Return the list of addresses managed by DHCP on a given network
+ * @param network the dotted-decimal representation of the network address
+ * @return an array of records for the addresses defined on that network
+ */
+ public DhcpClientRecord [] loadNetwork(String network)
+ throws BridgeException {
+ return loadNetwork(network, null);
+ }
+
+ /**
+ * Return the list of addresses managed by DHCP on a given network
+ * @param network the dotted-decimal representation of the network address
+ * @param datastore user-supplied datastore attributes
+ * @return an array of records for the addresses defined on that network
+ */
+ public DhcpClientRecord [] loadNetwork(String network,
+ DhcpDatastore datastore) throws BridgeException {
+ return bridge.loadNetwork(network, datastore);
+ }
+
+ /**
+ * Return the list of addresses managed by DHCP on a given network, with
+ * the hostnames for each client looked up, too.
+ * @param network the dotted-decimal representation of the network address
+ * @return an array of records for the addresses defined on that network
+ */
+ public DhcpClientRecord [] loadNetworkCompletely(String network)
+ throws BridgeException {
+ DhcpClientRecord [] clients = loadNetwork(network);
+ // Force loading of client name for each client
+ for (int i = 0; i < clients.length; ++i) {
+ clients[i].getClientName();
+ }
+ return clients;
+ }
+
+ /**
+ * Modify an existing client record, and update the associated hosts
+ * record if needed.
+ * @param oldClient the existing record
+ * @param newClient the new record
+ * @param table the network on which the record is defined
+ */
+ public void modifyClient(DhcpClientRecord oldClient,
+ DhcpClientRecord newClient, String table) throws BridgeException {
+
+ modifyClient(oldClient, newClient, table, null);
+ }
+
+ /**
+ * Modify an existing client record, and update the associated hosts
+ * record if needed.
+ * @param oldClient the existing record
+ * @param newClient the new record
+ * @param table the network on which the record is defined
+ * @param datastore user-supplied datastore attributes
+ */
+ public void modifyClient(DhcpClientRecord oldClient,
+ DhcpClientRecord newClient, String table, DhcpDatastore datastore)
+ throws BridgeException {
+
+ boolean nameChanged = !oldClient.getClientName().equals(
+ newClient.getClientName());
+ boolean commentChanged = !oldClient.getComment().equals(
+ newClient.getComment());
+ /*
+ * If the name changed, need to update hosts. If comment changed,
+ * hosts is only updated if there was already a hosts record.
+ */
+ if (nameChanged) {
+ /*
+ * If new name is empty, delete the hosts entry. Otherwise
+ * try to modify it.
+ */
+ if (newClient.getClientName().length() == 0) {
+ try {
+ deleteHostsRecord(newClient.getClientIPAddress());
+ } catch (Throwable e) {
+ throw new NoHostsEntryException(
+ newClient.getClientIPAddress());
+ }
+ } else {
+ try {
+ modifyHostsRecord(oldClient.getClientIPAddress(),
+ newClient.getClientIPAddress(),
+ newClient.getClientName(), newClient.getComment());
+ } catch (NoHostsEntryException e) {
+ // Must not be one, so create it instead
+ createHostsRecord(newClient.getClientIPAddress(),
+ newClient.getClientName(), newClient.getComment());
+ }
+ }
+ } else if (commentChanged) {
+ // Try to modify, but toss all exceptions as this isn't a big deal
+ try {
+ modifyHostsRecord(oldClient.getClientIPAddress(),
+ newClient.getClientIPAddress(), newClient.getClientName(),
+ newClient.getComment());
+ } catch (Throwable e) {
+ // Ignore
+ }
+ }
+
+ // Update the network table record
+ bridge.modifyDhcpClientRecord(oldClient, newClient,
+ table, datastore);
+
+ }
+
+ /**
+ * Create a new record in the given table, and create a hosts record.
+ * @param client the client to create
+ * @param table the network on which to create the client
+ */
+ public void addClient(DhcpClientRecord client, String table)
+ throws BridgeException {
+
+ addClient(client, table, null);
+ }
+
+ /**
+ * Create a new record in the given table, and create a hosts record.
+ * @param client the client to create
+ * @param table the network on which to create the client
+ * @param datastore user-supplied datastore attributes
+ */
+ public void addClient(DhcpClientRecord client, String table,
+ DhcpDatastore datastore) throws BridgeException {
+
+ /*
+ * If a name was supplied and we can't resolve it to this address,
+ * create a hosts record.
+ */
+ if (client.getClientName().length() != 0
+ && !client.getClientName().equals(client.getClientIPAddress())) {
+ createHostsRecord(client.getClientIPAddress(),
+ client.getClientName(), client.getComment());
+ }
+
+ // Create the record in the per-network table
+ bridge.createDhcpClientRecord(client, table, datastore);
+
+ }
+
+ /**
+ * Delete a record from the given table, and delete the associated hosts
+ * record if requested.
+ * @param client the client to delete
+ * @param table the network to delete the client from
+ * @param deleteHosts true if the hosts record should be removed as well
+ */
+ public void deleteClient(DhcpClientRecord client, String table,
+ boolean deleteHosts) throws BridgeException {
+
+ deleteClient(client, table, deleteHosts, null);
+ }
+
+ /**
+ * Delete a record from the given table, and delete the associated hosts
+ * record if requested.
+ * @param client the client to delete
+ * @param table the network to delete the client from
+ * @param deleteHosts true if the hosts record should be removed as well
+ * @param datastore user-supplied datastore attributes
+ */
+ public void deleteClient(DhcpClientRecord client, String table,
+ boolean deleteHosts, DhcpDatastore datastore)
+ throws BridgeException {
+
+ // Delete the client record from the per-network table
+ bridge.deleteDhcpClientRecord(client, table, datastore);
+
+ // Delete hosts if requested
+ if (deleteHosts) {
+ try {
+ deleteHostsRecord(client.getClientIPAddress());
+ } catch (NoEntryException e) {
+ throw new NoEntryException("hosts");
+ }
+ }
+ }
+
+
+ /**
+ * Retrieve a client record from the given table.
+ * @param client the client to delete
+ * @param table the network to delete the client from
+ * @param datastore user-supplied datastore attributes
+ */
+ public DhcpClientRecord getClient(DhcpClientRecord client,
+ String table, DhcpDatastore datastore) throws BridgeException {
+
+ // Retrieve the client record from the per-network table
+ DhcpClientRecord clientRecord =
+ bridge.getDhcpClientRecord(client, table, datastore);
+
+ return clientRecord;
+ }
+
+ /**
+ * Create a new per-network table for the given network by converting the
+ * one from the server's data store into a new data store.
+ * @param network the network number in dotted-decimal form.
+ * @param datastore user-supplied datastore attributes
+ */
+ public void cvtNetwork(String network,
+ DhcpDatastore datastore) throws BridgeException {
+ bridge.cvtNetwork(network, datastore);
+ }
+
+ /**
+ * Create a new per-network table for the given network.
+ * @param network the network number in dotted-decimal form.
+ */
+ public void createNetwork(String network)
+ throws BridgeException {
+
+ createNetwork(network, null);
+ }
+
+ /**
+ * Create a new per-network table for the given network.
+ * @param network the network number in dotted-decimal form.
+ * @param datastore user-supplied datastore attributes
+ */
+ public void createNetwork(String network,
+ DhcpDatastore datastore) throws BridgeException {
+ bridge.createDhcpNetwork(network, datastore);
+ }
+
+ /**
+ * Delete a per-network table, the macro associated with the network number,
+ * and optionally deleting the associated hosts records.
+ * @param network the network number in dotted-decimal form.
+ * @param deleteMacro true if the network macro should be deleted
+ * @param deleteHosts true if the associated hosts records should be deleted
+ */
+ public void deleteNetwork(String network, boolean deleteMacro,
+ boolean deleteHosts) throws BridgeException {
+ deleteNetwork(network, deleteMacro, deleteHosts, null);
+ }
+
+ /**
+ * Delete a per-network table, the macro associated with the network number,
+ * and optionally deleting the associated hosts records.
+ * @param network the network number in dotted-decimal form.
+ * @param deleteMacro true if the network macro should be deleted
+ * @param deleteHosts true if the associated hosts records should be deleted
+ * @param datastore user-supplied datastore attributes
+ */
+ public void deleteNetwork(String network, boolean deleteMacro,
+ boolean deleteHosts, DhcpDatastore datastore)
+ throws BridgeException {
+
+ // If we're supposed to clean up hosts, do so
+ if (deleteHosts) {
+ DhcpClientRecord [] recs =
+ bridge.loadNetwork(network, datastore);
+ if (recs != null) {
+ for (int i = 0; i < recs.length; ++i) {
+ try {
+ deleteHostsRecord(recs[i].getClientIPAddress());
+ } catch (Throwable e) {
+ // Ignore errors here; they're not important
+ }
+ }
+ }
+ }
+
+ // Delete network table, then the macro for the network
+ bridge.deleteDhcpNetwork(network, datastore);
+ try {
+ if (deleteMacro) {
+ bridge.deleteDhcptabRecord(new Macro(network),
+ datastore);
+ }
+ } catch (Throwable e) {
+ // All the errors here are ignorable
+ }
+ }
+
+ /**
+ * Add a record to the hosts table.
+ * @param addr address of entry to add to the hosts table
+ * @param name alias for the entry
+ * @param comment comment for the entry
+ */
+ private void createHostsRecord(String addr, String name,
+ String comment) throws BridgeException {
+
+ DhcpHostsTable hostsTable = null;
+ try {
+ hostsTable = DhcpHostsTable.getCfgHostsTable(bridge);
+ if (hostsTable != null) {
+ hostsTable.openTable();
+ hostsTable.createHostsRecord(addr, name, comment);
+ }
+ } finally {
+ if (hostsTable != null) {
+ hostsTable.closeTable();
+ }
+ }
+
+ } // createHostsRecord
+
+ /**
+ * Delete a record from the hosts table.
+ * @param addr address of entry to remove from the hosts table
+ */
+ private void deleteHostsRecord(String addr)
+ throws BridgeException {
+
+ DhcpHostsTable hostsTable = null;
+ try {
+ hostsTable = DhcpHostsTable.getCfgHostsTable(bridge);
+ if (hostsTable != null) {
+ hostsTable.openTable();
+ hostsTable.deleteHostsRecord(addr);
+ }
+ } finally {
+ if (hostsTable != null) {
+ hostsTable.closeTable();
+ }
+ }
+
+ } // deleteHostsRecord
+
+ /**
+ * Modify a record in the hosts table.
+ * @param oldAddr address of entry to modify in the hosts table
+ * @param newAddr new address of entry
+ * @param name alias for the entry
+ * @param comment comment for the entry
+ */
+ private void modifyHostsRecord(String oldAddr,
+ String newAddr, String name, String comment) throws BridgeException {
+
+ DhcpHostsTable hostsTable = null;
+ try {
+ hostsTable = DhcpHostsTable.getCfgHostsTable(bridge);
+ if (hostsTable != null) {
+ hostsTable.openTable();
+ hostsTable.modifyHostsRecord(oldAddr, newAddr, name, comment);
+ }
+ } finally {
+ if (hostsTable != null) {
+ hostsTable.closeTable();
+ }
+ }
+
+ } // modifyHostsRecord
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/DhcpServiceMgr.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/DhcpServiceMgr.java
new file mode 100644
index 0000000000..58affe47c7
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/DhcpServiceMgr.java
@@ -0,0 +1,69 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1998-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.server;
+
+import java.util.*;
+import java.net.InetAddress;
+
+import com.sun.dhcpmgr.bridge.*;
+import com.sun.dhcpmgr.data.*;
+
+/**
+ * This interface defines the methods available for managing the basic
+ * service parameters which are not stored in the dhcptab or network tables.
+ */
+public interface DhcpServiceMgr {
+ public String getServerName();
+ public String getShortServerName();
+ public InetAddress getServerAddress();
+ public void makeLocation(DhcpDatastore datastore)
+ throws BridgeException;
+ public DhcpDatastore getDataStore(String resource) throws BridgeException;
+ public DhcpDatastore [] getDataStores() throws BridgeException;
+ public Option [] getInittabOptions(byte context) throws BridgeException;
+ public String getDataStoreClassname(String dataStoreName)
+ throws BridgeException;
+ public DhcpdOptions readDefaults() throws BridgeException;
+ public void writeDefaults(DhcpdOptions defs) throws BridgeException;
+ public void removeDefaults() throws BridgeException;
+ public void startup() throws BridgeException;
+ public void shutdown() throws BridgeException;
+ public void reload() throws BridgeException;
+ public IPInterface [] getInterfaces() throws BridgeException;
+ public String [] getArguments(String line) throws BridgeException;
+ public String getStringOption(String optionName, String arg)
+ throws BridgeException;
+ public IPAddress [] getIPOption(String optionName, String arg)
+ throws BridgeException;
+ public long [] getNumberOption(String optionName, String arg)
+ throws BridgeException;
+ public boolean isServerRunning() throws BridgeException;
+ public boolean isVersionCurrent() throws BridgeException;
+ public boolean isHostsValid(String resource, String domain)
+ throws BridgeException;
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/DhcpServiceMgrImpl.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/DhcpServiceMgrImpl.java
new file mode 100644
index 0000000000..f093d87efd
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/DhcpServiceMgrImpl.java
@@ -0,0 +1,255 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1998-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.server;
+
+import java.util.*;
+import java.util.jar.*;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+import com.sun.dhcpmgr.bridge.*;
+import com.sun.dhcpmgr.data.*;
+
+/**
+ * This class provides the capabilities for managing the the basic service
+ * parameters which are not stored in the dhcptab or per-network tables.
+ */
+public class DhcpServiceMgrImpl implements DhcpServiceMgr {
+ private Bridge bridge;
+
+ private String serverName;
+ private String shortServerName;
+ private InetAddress serverAddress;
+
+ public DhcpServiceMgrImpl(Bridge bridge) {
+ this.bridge = bridge;
+
+ try {
+ serverAddress = InetAddress.getLocalHost();
+ serverName = serverAddress.getHostName();
+
+ int i = serverName.indexOf('.');
+ if (i == -1) {
+ shortServerName = serverName;
+ } else {
+ shortServerName = serverName.substring(0, i);
+ }
+ } catch (UnknownHostException e) {
+ serverName = shortServerName = "";
+ }
+ }
+
+ public String getServerName() {
+ return serverName;
+ }
+
+ public String getShortServerName() {
+ return shortServerName;
+ }
+
+ public InetAddress getServerAddress() {
+ return serverAddress;
+ }
+
+ public void makeLocation(DhcpDatastore datastore)
+ throws BridgeException {
+ bridge.makeLocation(datastore);
+ }
+
+ public DhcpDatastore getDataStore(String resource) throws BridgeException {
+ return bridge.getDataStore(resource);
+ }
+
+ /**
+ * Retrieve the list of possible data stores for this server
+ * @return an array of data store module names.
+ */
+ public DhcpDatastore [] getDataStores() throws BridgeException {
+ return bridge.getDataStores();
+ }
+
+ /**
+ * Retrieve a list of options from the DHCP inittab.
+ * @return an array of options
+ */
+ public Option [] getInittabOptions(byte context) throws BridgeException {
+ return bridge.getInittabOptions(context);
+ }
+
+ public String getDataStoreClassname(String dataStoreName)
+ throws BridgeException {
+
+ String beansDirectory = new String("/usr/sadm/admin/dhcpmgr/");
+ String jarPath = beansDirectory.concat(dataStoreName).concat(".jar");
+ String className = null;
+ try {
+ JarFile jarFile = new JarFile(jarPath);
+ Manifest manifest = jarFile.getManifest();
+ if (manifest == null) {
+ throw new BridgeException();
+ }
+ Attributes attrs = manifest.getMainAttributes();
+ if (attrs == null) {
+ throw new BridgeException();
+ }
+ className = attrs.getValue("Name");
+ if (!className.endsWith(".class")) {
+ throw new BridgeException();
+ }
+ className = className.substring(0, className.length() - 6);
+ className = className.replace('/', '.');
+ } catch (Throwable e) {
+ throw new BridgeException();
+ }
+
+ return className;
+ }
+
+ /**
+ * Retrieve the contents of the DHCP config file.
+ * @return the config settings
+ */
+ public DhcpdOptions readDefaults() throws BridgeException {
+ return bridge.readDefaults();
+ }
+
+ /**
+ * Write new settings to the DHCP config file.
+ * @param cfgs the new config settings
+ */
+ public void writeDefaults(DhcpdOptions cfgs) throws BridgeException {
+ bridge.writeDefaults(cfgs);
+ }
+
+ /**
+ * Remove the DHCP config file.
+ */
+ public void removeDefaults() throws BridgeException {
+ bridge.removeDefaults();
+ }
+
+ /**
+ * Start the server
+ */
+ public void startup() throws BridgeException {
+ bridge.startup();
+ }
+
+ /**
+ * Stop the server
+ */
+ public void shutdown() throws BridgeException {
+ bridge.shutdown();
+ }
+
+ /**
+ * Send the server a SIGHUP to re-read the dhcptab
+ */
+ public void reload() throws BridgeException {
+ bridge.reload();
+ }
+
+ /**
+ * Get the list of possible interfaces for the server to monitor
+ * @return an array of interfaces
+ */
+ public IPInterface [] getInterfaces() throws BridgeException {
+ return bridge.getInterfaces();
+ }
+
+ /**
+ * Break up a line into a list of arguments
+ * @param input line
+ * @return an array of arguments
+ */
+ public String [] getArguments(String line) throws BridgeException {
+ return bridge.getArguments(line);
+ }
+
+ /**
+ * Get the default value for an option which would take a string
+ * @param optionName name of the option
+ * @param arg additional information needed for this code
+ */
+ public synchronized String getStringOption(String optionName, String arg)
+ throws BridgeException {
+ Option option = OptionsTable.getTable().get(optionName);
+ return bridge.getStringOption(option.getCode(), arg);
+ }
+
+ /**
+ * Get the default value for an option which would take one or more IP addrs
+ * @param optionName name of the option
+ * @param arg additional information needed for this code
+ */
+ public synchronized IPAddress [] getIPOption(String optionName, String arg)
+ throws BridgeException {
+ Option option = OptionsTable.getTable().get(optionName);
+ return bridge.getIPOption(option.getCode(), arg);
+ }
+
+ /**
+ * Get the default value for an option which would take one or more numbers
+ * @param optionName name of the option
+ * @param arg additional information needed for this code
+ */
+ public synchronized long [] getNumberOption(String optionName, String arg)
+ throws BridgeException {
+ Option option = OptionsTable.getTable().get(optionName);
+ return bridge.getNumberOption(option.getCode(), arg);
+ }
+
+ /**
+ * Check if the datastore version is current.
+ * @return true if the datastore version if current.
+ */
+ public boolean isVersionCurrent() throws BridgeException {
+ return bridge.isVersionCurrent();
+ }
+
+ /**
+ * Check if the server is currently running
+ * @return true if the server process is started
+ */
+ public boolean isServerRunning() throws BridgeException {
+ return bridge.isServerRunning();
+ }
+
+ /**
+ * Determines the whether or not the hosts table under a given
+ * name service can be managed.
+ * @param resource name service resource (files, dns, nisplus)
+ * @param domain the name service domain (ignored for files)
+ * @return true if the user can manage the table
+ */
+ public boolean isHostsValid(String resource, String domain) {
+
+ return DhcpHostsTable.isHostsValid(resource, domain);
+
+ } // isHostsManageable
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/DhcptabMgr.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/DhcptabMgr.java
new file mode 100644
index 0000000000..c565a4072e
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/DhcptabMgr.java
@@ -0,0 +1,104 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1998-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.server;
+
+import java.util.*;
+import java.net.InetAddress;
+
+import com.sun.dhcpmgr.bridge.*;
+import com.sun.dhcpmgr.data.*;
+
+/**
+ * This interface defines the methods available for managing the dhcptab.
+ */
+public interface DhcptabMgr {
+ public Option createOption(String name, String value)
+ throws BridgeException;
+ public Option [] getOptions(DhcpDatastore datastore)
+ throws BridgeException;
+ public Option [] getOptions()
+ throws BridgeException;
+ public Macro [] getMacros(DhcpDatastore datastore)
+ throws BridgeException;
+ public Macro [] getMacros()
+ throws BridgeException;
+ public Macro getMacro(String key, DhcpDatastore datastore)
+ throws BridgeException;
+ public Macro getMacro(String key)
+ throws BridgeException;
+ public Option getOption(String key, DhcpDatastore datastore)
+ throws BridgeException;
+ public Option getOption(String key)
+ throws BridgeException;
+ public void createRecord(DhcptabRecord rec, boolean signalServer,
+ DhcpDatastore datastore) throws BridgeException;
+ public void createRecord(DhcptabRecord rec, boolean signalServer)
+ throws BridgeException;
+ public void modifyRecord(DhcptabRecord oldRec, DhcptabRecord newRec,
+ boolean signalServer, DhcpDatastore datastore)
+ throws BridgeException;
+ public void modifyRecord(DhcptabRecord oldRec, DhcptabRecord newRec,
+ boolean signalServer) throws BridgeException;
+ public void deleteRecord(DhcptabRecord rec, boolean signalServer,
+ DhcpDatastore datastore) throws BridgeException;
+ public void deleteRecord(DhcptabRecord rec, boolean signalServer)
+ throws BridgeException;
+ public ActionError [] deleteAllMacros() throws BridgeException;
+ public ActionError [] deleteAllOptions() throws BridgeException;
+ public ActionError [] deleteMacros(String [] macroNames);
+ public ActionError [] deleteOptions(String [] optionNames);
+ public void cvtDhcptab(DhcpDatastore datastore)
+ throws BridgeException;
+ public void createDhcptab(DhcpDatastore datastore)
+ throws BridgeException;
+ public void createDhcptab()
+ throws BridgeException;
+ public void deleteDhcptab(DhcpDatastore datastore)
+ throws BridgeException;
+ public void deleteDhcptab()
+ throws BridgeException;
+ public void createLocaleMacro()
+ throws BridgeException, ValidationException;
+ public void createLocaleMacro(DhcpDatastore datastore)
+ throws BridgeException, ValidationException;
+ public void createServerMacro(String svrName, InetAddress svrAddress,
+ int leaseLength, boolean leaseNegotiable, String dnsDomain,
+ Vector dnsServs) throws BridgeException, ValidationException;
+ public void createServerMacro(String svrName, InetAddress svrAddress,
+ int leaseLength, boolean leaseNegotiable, String dnsDomain,
+ Vector dnsServs, DhcpDatastore datastore)
+ throws BridgeException, ValidationException;
+ public void createNetworkMacro(Network network, IPAddress [] routers,
+ boolean isLan, String nisDomain, Vector nisServs,
+ String nisplusDomain, Vector nisplusServs)
+ throws BridgeException, ValidationException;
+ public void createNetworkMacro(Network network, IPAddress [] routers,
+ boolean isLan, String nisDomain, Vector nisServs, String nisplusDomain,
+ Vector nisplusServs, DhcpDatastore datastore)
+ throws BridgeException, ValidationException;
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/DhcptabMgrImpl.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/DhcptabMgrImpl.java
new file mode 100644
index 0000000000..5b1b458517
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/DhcptabMgrImpl.java
@@ -0,0 +1,475 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1998-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.server;
+
+import java.util.*;
+import java.net.InetAddress;
+
+import com.sun.dhcpmgr.bridge.*;
+import com.sun.dhcpmgr.data.*;
+
+/**
+ * This class provides methods to manage the contents of the dhcptab.
+ */
+
+public class DhcptabMgrImpl implements DhcptabMgr {
+ private Bridge bridge;
+
+ /**
+ * Create a new DhcptabMgr using the provided native bridge.
+ * @param bridge the native bridge class which actually does the work.
+ */
+ public DhcptabMgrImpl(Bridge bridge) {
+ this.bridge = bridge;
+ }
+
+ /**
+ * Create an option.
+ * @param name the name of the option.
+ * @param value the value for the option in dhcptab(4) format.
+ * @return the Option.
+ */
+ public Option createOption(String name, String value)
+ throws BridgeException {
+ return bridge.createOption(name, value);
+ }
+
+ /**
+ * Retrieve all options currently defined in the dhcptab.
+ * @return an array of Options
+ */
+ public Option [] getOptions() throws BridgeException {
+ return getOptions(null);
+ }
+
+ /**
+ * Retrieve all options currently defined in the dhcptab.
+ * @param datastore user-supplied datastore attributes
+ * @return an array of Options
+ */
+ public Option [] getOptions(DhcpDatastore datastore)
+ throws BridgeException {
+ return bridge.getOptions(datastore);
+ }
+
+ /**
+ * Retrieve all the macros currently defined in the dhcptab.
+ * @return an array of Macros
+ */
+ public Macro [] getMacros() throws BridgeException {
+ return getMacros(null);
+ }
+
+ /**
+ * Retrieve all the macros currently defined in the dhcptab.
+ * @param datastore user-supplied datastore attributes
+ * @return an array of Macros
+ */
+ public Macro [] getMacros(DhcpDatastore datastore)
+ throws BridgeException {
+ /*
+ * Load the vendor and site options before loading the macros
+ * so we can validate correctly, adding them to the standard options
+ * table.
+ */
+ OptionsTable optionsTable = OptionsTable.getTable();
+ optionsTable.add(bridge.getOptions(datastore));
+ return bridge.getMacros(datastore);
+ }
+
+ /**
+ * Create a given record in the dhcptab, and signal the server to
+ * reload the dhcptab if so requested.
+ * @param rec the record to add to the table
+ * @param signalServer true if the server is to be sent a SIGHUP
+ */
+ public void createRecord(DhcptabRecord rec, boolean signalServer)
+ throws BridgeException {
+ createRecord(rec, signalServer, null);
+ }
+
+ /**
+ * Create a given record in the dhcptab, and signal the server to
+ * reload the dhcptab if so requested.
+ * @param rec the record to add to the table
+ * @param signalServer true if the server is to be sent a SIGHUP
+ * @param datastore user-supplied datastore attributes
+ */
+ public void createRecord(DhcptabRecord rec, boolean signalServer,
+ DhcpDatastore datastore) throws BridgeException {
+ bridge.createDhcptabRecord(rec, datastore);
+ if (signalServer) {
+ bridge.reload();
+ }
+ }
+
+ /**
+ * Modify a given record in the dhcptab, and signal the server to reload
+ * the dhcptab if so requested
+ * @param oldRec the current record to modify
+ * @param newRec the new record to be placed in the table
+ * @param signalServer true if the server is to be sent a SIGHUP
+ */
+ public void modifyRecord(DhcptabRecord oldRec, DhcptabRecord newRec,
+ boolean signalServer) throws BridgeException {
+ modifyRecord(oldRec, newRec, signalServer, null);
+ }
+
+ /**
+ * Modify a given record in the dhcptab, and signal the server to reload
+ * the dhcptab if so requested
+ * @param oldRec the current record to modify
+ * @param newRec the new record to be placed in the table
+ * @param signalServer true if the server is to be sent a SIGHUP
+ * @param datastore user-supplied datastore attributes
+ */
+ public void modifyRecord(DhcptabRecord oldRec, DhcptabRecord newRec,
+ boolean signalServer, DhcpDatastore datastore)
+ throws BridgeException {
+ bridge.modifyDhcptabRecord(oldRec, newRec, datastore);
+ if (signalServer) {
+ bridge.reload();
+ }
+ }
+
+ /**
+ * Delete a given record from the dhcptab, and signal the server to reload
+ * the dhcptab if so requested
+ * @param rec the record to delete
+ * @param signalServer true if the server is to be sent a SIGHUP
+ */
+ public void deleteRecord(DhcptabRecord rec, boolean signalServer)
+ throws BridgeException {
+ deleteRecord(rec, signalServer, null);
+ }
+
+ /**
+ * Delete a given record from the dhcptab, and signal the server to reload
+ * the dhcptab if so requested
+ * @param rec the record to delete
+ * @param signalServer true if the server is to be sent a SIGHUP
+ * @param datastore user-supplied datastore attributes
+ */
+ public void deleteRecord(DhcptabRecord rec, boolean signalServer,
+ DhcpDatastore datastore) throws BridgeException {
+ bridge.deleteDhcptabRecord(rec, datastore);
+ if (signalServer) {
+ bridge.reload();
+ }
+ }
+
+ /**
+ * Delete a record by name and type
+ * @param key The key for the record
+ * @param type The type of record; either MACRO or OPTION
+ */
+ private void deleteRecord(String name, String type) throws BridgeException {
+ DhcptabRecord rec = null;
+ if (type.equals(DhcptabRecord.MACRO)) {
+ rec = getMacro(name);
+ } else {
+ rec = getOption(name);
+ }
+ deleteRecord(rec, false);
+ }
+
+ /**
+ * Delete a set of records.
+ * @return An array of ActionError, one error for each record not deleted
+ */
+ private ActionError [] deleteRecords(DhcptabRecord [] recs) {
+ ArrayList errorList = new ArrayList();
+
+ for (int i = 0; i < recs.length; ++i) {
+ try {
+ deleteRecord(recs[i], false);
+ } catch (BridgeException e) {
+ errorList.add(new ActionError(recs[i].getKey(), e));
+ }
+ }
+
+ return (ActionError[])errorList.toArray(new ActionError[0]);
+ }
+
+ private ActionError [] deleteAllRecords(String type)
+ throws BridgeException {
+ DhcptabRecord [] recs;
+ if (type.equals(DhcptabRecord.MACRO)) {
+ recs = getMacros();
+ } else {
+ recs = getOptions();
+ }
+ return deleteRecords(recs);
+ }
+
+ /**
+ * Delete all macros
+ * @return An array of ActionError, one error for each macro not deleted
+ */
+ public ActionError [] deleteAllMacros() throws BridgeException {
+ return deleteAllRecords(DhcptabRecord.MACRO);
+ }
+
+ /**
+ * Delete all options
+ * @return An array of ActionError, one error for each option not deleted
+ */
+ public ActionError [] deleteAllOptions() throws BridgeException {
+ return deleteAllRecords(DhcptabRecord.OPTION);
+ }
+
+ /**
+ * Delete a list of macros identified by name
+ * @param macroNames Names of the macros to delete
+ * @return An array of ActionError, one element per macro not deleted
+ */
+ public ActionError [] deleteMacros(String [] macroNames) {
+ ArrayList errorList = new ArrayList();
+
+ for (int i = 0; i < macroNames.length; ++i) {
+ try {
+ deleteRecord(macroNames[i], DhcptabRecord.MACRO);
+ } catch (BridgeException e) {
+ errorList.add(new ActionError(macroNames[i], e));
+ }
+ }
+
+ return (ActionError [])errorList.toArray(new ActionError[0]);
+ }
+
+ /**
+ * Delete a list of options identified by name
+ * @param optionNames Names of options to delete
+ * @return An array of ActionError, one element per option not deleted
+ */
+ public ActionError [] deleteOptions(String [] optionNames) {
+ ArrayList errorList = new ArrayList();
+
+ for (int i = 0; i < optionNames.length; ++i) {
+ try {
+ deleteRecord(optionNames[i], DhcptabRecord.OPTION);
+ } catch (BridgeException e) {
+ errorList.add(new ActionError(optionNames[i], e));
+ }
+ }
+
+ return (ActionError [])errorList.toArray(new ActionError[0]);
+ }
+
+ /**
+ * Retrieve a given macro from the dhcptab.
+ * @param key the key of the record to retrieve
+ * @return the Macro for the given key
+ */
+ public Macro getMacro(String key)
+ throws BridgeException {
+ return getMacro(key, null);
+ }
+
+ /**
+ * Retrieve a given macro from the dhcptab.
+ * @param key the key of the record to retrieve
+ * @param datastore user-supplied datastore attributes
+ * @return the Macro for the given key
+ */
+ public Macro getMacro(String key, DhcpDatastore datastore)
+ throws BridgeException {
+ OptionsTable optionsTable = OptionsTable.getTable();
+ optionsTable.add(bridge.getOptions(datastore));
+ return bridge.getMacro(key, datastore);
+ }
+
+ /**
+ * Retrieve a given option from the dhcptab.
+ * @param key the key of the record to retrieve
+ * @return the Option for the given key
+ */
+ public Option getOption(String key)
+ throws BridgeException {
+ return getOption(key, null);
+ }
+
+ /**
+ * Retrieve a given option from the dhcptab.
+ * @param key the key of the record to retrieve
+ * @param datastore user-supplied datastore attributes
+ * @return the Option for the given key
+ */
+ public Option getOption(String key, DhcpDatastore datastore)
+ throws BridgeException {
+ return bridge.getOption(key, datastore);
+ }
+
+ /**
+ * Create a new dhcptab converting the one in the server's data store,
+ * into a new data store.
+ * @param datastore user-supplied datastore attributes
+ */
+ public void cvtDhcptab(DhcpDatastore datastore)
+ throws BridgeException {
+ bridge.cvtDhcptab(datastore);
+ }
+
+ /**
+ * Create a new empty dhcptab in the server's data store, which must
+ * already be configured.
+ */
+ public void createDhcptab() throws BridgeException {
+ createDhcptab(null);
+ }
+
+ /**
+ * Create a new empty dhcptab in the server's data store, which must
+ * already be configured.
+ * @param datastore user-supplied datastore attributes
+ */
+ public void createDhcptab(DhcpDatastore datastore)
+ throws BridgeException {
+ bridge.createDhcptab(datastore);
+ }
+
+ /**
+ * Delete the server's dhcptab in the current data store.
+ */
+ public void deleteDhcptab() throws BridgeException {
+ deleteDhcptab(null);
+ }
+
+ /**
+ * Delete the server's dhcptab in the current data store.
+ * @param datastore user-supplied datastore attributes
+ */
+ public void deleteDhcptab(DhcpDatastore datastore)
+ throws BridgeException {
+ bridge.deleteDhcptab(datastore);
+ }
+
+ public void createLocaleMacro()
+ throws BridgeException, ValidationException {
+ createLocaleMacro(null);
+ }
+
+ public void createLocaleMacro(DhcpDatastore datastore)
+ throws BridgeException, ValidationException {
+
+ Macro macro = new Macro();
+ macro.setKey("Locale");
+ macro.storeOption(StandardOptions.CD_TIMEOFFSET,
+ String.valueOf(TimeZone.getDefault().getRawOffset()/1000));
+
+ createRecord(macro, false);
+ }
+
+ public void createServerMacro(String svrName,
+ InetAddress svrAddress, int leaseLength,
+ boolean leaseNegotiable, String dnsDomain, Vector dnsServs)
+ throws BridgeException, ValidationException {
+
+ createServerMacro(svrName, svrAddress, leaseLength, leaseNegotiable,
+ dnsDomain, dnsServs, null);
+ }
+
+ public void createServerMacro(String svrName,
+ InetAddress svrAddress, int leaseLength,
+ boolean leaseNegotiable, String dnsDomain, Vector dnsServs,
+ DhcpDatastore datastore)
+ throws BridgeException, ValidationException {
+
+ Macro macro = new Macro();
+ macro.setKey(svrName);
+ macro.storeOption("Include", "Locale");
+ macro.storeOption(StandardOptions.CD_TIMESERV, svrAddress);
+ macro.storeOption(StandardOptions.CD_LEASE_TIME,
+ String.valueOf(leaseLength));
+ if (leaseNegotiable) {
+ macro.storeOption(StandardOptions.CD_BOOL_LEASENEG, null);
+ }
+ if (dnsDomain != null && dnsDomain.length() != 0 &&
+ dnsServs != null && dnsServs.size() != 0) {
+ macro.storeOption(StandardOptions.CD_DNSDOMAIN, dnsDomain);
+ macro.storeOption(StandardOptions.CD_DNSSERV, dnsServs);
+ }
+ // First delete it in case it's already there
+ try {
+ deleteRecord(macro, false);
+ } catch (Throwable e) {
+ // Ignore any error
+ }
+
+ createRecord(macro, false);
+ }
+
+ public synchronized void createNetworkMacro(Network network,
+ IPAddress [] routers, boolean isLan, String nisDomain, Vector nisServs,
+ String nisplusDomain, Vector nisplusServs)
+ throws BridgeException, ValidationException {
+
+ createNetworkMacro(network, routers, isLan, nisDomain, nisServs,
+ nisplusDomain, nisplusServs, null);
+ }
+
+ public void createNetworkMacro(Network network,
+ IPAddress [] routers, boolean isLan, String nisDomain, Vector nisServs,
+ String nisplusDomain, Vector nisplusServs, DhcpDatastore datastore)
+ throws BridgeException, ValidationException {
+
+ Macro macro = new Macro();
+ macro.setKey(network.toString());
+ macro.storeOption(StandardOptions.CD_SUBNETMASK, network.getMask());
+ if (routers == null) {
+ macro.storeOption(StandardOptions.CD_ROUTER_DISCVRY_ON, "1");
+ } else {
+ for (int i = 0; i < routers.length; i++) {
+ macro.storeOption(StandardOptions.CD_ROUTER, routers[i]);
+ }
+ }
+
+ if (isLan) {
+ macro.storeOption(StandardOptions.CD_BROADCASTADDR,
+ network.getBroadcastAddress());
+ }
+
+ // NIS config
+ if (nisDomain != null && nisDomain.length() != 0 &&
+ nisServs != null && nisServs.size() != 0) {
+ macro.storeOption(StandardOptions.CD_NIS_DOMAIN, nisDomain);
+ macro.storeOption(StandardOptions.CD_NIS_SERV, nisServs);
+ }
+
+ // NIS+ config
+ if (nisplusDomain != null && nisplusDomain.length() != 0 &&
+ nisplusServs != null && nisplusServs.size() != 0) {
+ macro.storeOption(StandardOptions.CD_NISPLUS_DMAIN,
+ nisplusDomain);
+ macro.storeOption(StandardOptions.CD_NISPLUS_SERVS,
+ nisplusServs);
+ }
+
+ createRecord(macro, false);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/Makefile b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/Makefile
new file mode 100644
index 0000000000..17ff6e3fe2
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/Makefile
@@ -0,0 +1,75 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 1999-2002 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/Makefile
+#
+
+
+# Place high-level classes first in order to minimize build time.
+CLASSFILES = DhcpMgrImpl.class \
+ DhcpNetMgrImpl.class \
+ DhcpServiceMgrImpl.class \
+ DhcptabMgrImpl.class \
+ DhcpHostsTable.class \
+ ResourceStrings.class
+
+
+include $(SRC)/lib/Makefile.lib
+
+DIRTABLE_JAR = /usr/sadm/lib/wbem/providerutility.jar
+CLASSPATH= $(SRC)/cmd/cmd-inet/usr.sadm/dhcpmgr:$(DIRTABLE_JAR)
+
+JAVAFILES = $(CLASSFILES:.class=.java)
+
+MSGDIR = $(ROOT)/usr/share/lib/locale/com/sun/dhcpmgr/server
+MSGDIRS = $(ROOT)/usr/share/lib/locale \
+ $(ROOT)/usr/share/lib/locale/com \
+ $(ROOT)/usr/share/lib/locale/com/sun \
+ $(ROOT)/usr/share/lib/locale/com/sun/dhcpmgr \
+ $(MSGDIR)
+
+MSGFILES = ResourceBundle.properties
+MSGS = $(MSGFILES:%=$(MSGDIR)/%)
+
+CLEANFILES= *.class
+CLOBBERFILES=
+
+.KEEP_STATE:
+
+all: $(CLASSFILES)
+
+install: all
+
+_msg: $(MSGDIRS) $(MSGS)
+
+$(MSGDIR)/%: %
+ $(INS.file)
+
+$(MSGDIRS):
+ $(INS.dir)
+
+include $(SRC)/lib/Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/ResourceBundle.properties b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/ResourceBundle.properties
new file mode 100644
index 0000000000..31154d9a5b
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/ResourceBundle.properties
@@ -0,0 +1,34 @@
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+get_host_err=Internal error, obtaining local host name
+hosts_access_err=Error accessing host table
+hosts_open_err=Error opening host table
+hosts_close_err=Error closing host table
+hosts_add_err=Error adding entry to hosts table
+hosts_remove_err=Error removing entry from hosts table
+hosts_modify_err=Error modifying hosts table table entry
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/ResourceStrings.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/ResourceStrings.java
new file mode 100644
index 0000000000..643b251a64
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/ResourceStrings.java
@@ -0,0 +1,64 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.server;
+
+import java.util.*;
+
+/**
+ * This class provides a convenient method to retrieve resources for
+ * the server package.
+ */
+public class ResourceStrings {
+
+ /**
+ * The handle to the resource bundle for the module.
+ */
+ private static ResourceBundle bundle = null;
+
+ /**
+ * Return a string from the resource bundle.
+ * @param key the key to the resource bundle string.
+ * @return the resource bundle string.
+ */
+ public static String getString(String key) {
+ String msg = null;
+ try {
+ if (bundle == null) {
+ bundle = ResourceBundle.getBundle(
+ "com.sun.dhcpmgr.server.ResourceBundle",
+ Locale.getDefault());
+ }
+ msg = bundle.getString(key);
+ } catch (Throwable e) {
+ msg = new String(key);
+ }
+ return msg;
+
+ } // getString
+
+} // ResourceStrings
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ALIGNMENT.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ALIGNMENT.java
new file mode 100644
index 0000000000..b21e9132f6
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ALIGNMENT.java
@@ -0,0 +1,42 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1996-1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.ui;
+
+/**
+ * Enumeration for <CODE>ALIGNMENT</CODE> values.
+ */
+public class ALIGNMENT {
+
+ public static final ALIGNMENT CENTER = new ALIGNMENT();
+ public static final ALIGNMENT LEFT = new ALIGNMENT();
+ public static final ALIGNMENT RIGHT = new ALIGNMENT();
+ public static final ALIGNMENT TOP = new ALIGNMENT();
+ public static final ALIGNMENT BOTTOM = new ALIGNMENT();
+
+ private ALIGNMENT() { }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/AutosizingTable.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/AutosizingTable.java
new file mode 100644
index 0000000000..51c052c6a3
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/AutosizingTable.java
@@ -0,0 +1,141 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1999-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.ui;
+
+import javax.swing.JTable;
+import javax.swing.table.*;
+import javax.swing.event.TableModelEvent;
+import java.awt.Component;
+import java.util.Date;
+
+import com.sun.dhcpmgr.data.IPAddress;
+import com.sun.dhcpmgr.data.ValidationException;
+
+/**
+ * A subclass of Swing's JTable which automatically resizes its columns to
+ * fit the data being displayed in them.
+ */
+public class AutosizingTable extends JTable {
+ Date aDate = null;
+ IPAddress longIP = null;
+
+ /**
+ * Construct the table
+ */
+ public AutosizingTable() {
+ super();
+ }
+
+ /**
+ * Construct the table with the given model.
+ * @param model the TableModel to be used.
+ */
+ public AutosizingTable(TableModel model) {
+ super(model);
+ }
+
+ /**
+ * The table has changed; we'll resize the columns to contain the new data
+ * as best they can.
+ */
+ public void tableChanged(TableModelEvent e) {
+ /*
+ * Let JTable do its thing, which probably includes wiping out
+ * the old columns and creating new ones
+ */
+ super.tableChanged(e);
+ TableModel model = getModel();
+ if (model.getRowCount() == 0) {
+ // No data, so just skip all the gymnastics
+ return;
+ }
+ /*
+ * Set column widths by first finding largest value in each column
+ * and then sizing accordingly
+ */
+ for (int i = 0; i < getColumnCount(); ++i) {
+ TableColumn col = getColumnModel().getColumn(i);
+
+ // Get the width of the header for this column
+ TableCellRenderer r = col.getHeaderRenderer();
+ int headerWidth = 0;
+ Component c;
+ if (r == null)
+ r = getTableHeader().getDefaultRenderer();
+ if (r != null) {
+ c = r.getTableCellRendererComponent(this, col.getHeaderValue(),
+ false, false, 0, 0);
+ headerWidth = c.getPreferredSize().width;
+ }
+ Object maxVal = null;
+
+ if (model.getColumnClass(i) == String.class) {
+ // Column contains strings; find the longest one
+ String maxString = "";
+ for (int j = 0; j < model.getRowCount(); ++j) {
+ String s = (String)model.getValueAt(j, i);
+ if (s != null) {
+ if (maxString.length() < s.length()) {
+ maxString = s;
+ }
+ }
+ }
+ maxVal = maxString;
+ } else if (model.getColumnClass(i) == IPAddress.class) {
+ // Column contains IP addresses; one long one is as good as any
+ if (longIP == null) {
+ try {
+ longIP = new IPAddress("222.222.222.222");
+ } catch (ValidationException ex) {
+ // This should never happen!
+ }
+ }
+ maxVal = longIP;
+ } else if (model.getColumnClass(i) == Date.class) {
+ // Column contains dates; now is as good a time as any other.
+ if (aDate == null) {
+ aDate = new Date();
+ }
+ maxVal = aDate;
+ }
+ // Now compute the width of the cell containing the longest value
+ c = getDefaultRenderer(
+ model.getColumnClass(i)).getTableCellRendererComponent(
+ this, maxVal, false, false, 0, i);
+ int cellWidth = c.getPreferredSize().width;
+
+ // Set preferred width to the greater of the header & cell widths
+ col.setPreferredWidth(Math.max(headerWidth, cellWidth));
+ }
+ // Now force the resizing we just did to be displayed
+ sizeColumnsToFit(-1);
+
+ // Force header to repaint itself, otherwise it won't align correctly
+ getTableHeader().resizeAndRepaint();
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ButtonLayout.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ButtonLayout.java
new file mode 100644
index 0000000000..230e6b30d2
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ButtonLayout.java
@@ -0,0 +1,243 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1996-1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.ui;
+
+import java.awt.*;
+
+/**
+ * <CODE>ButtonLayout</CODE> is used to layout buttons in a
+ * <CODE>Panel</CODE>. It will arrange buttons left to right
+ * until no more buttons fit on the same line. Each line is
+ * centered. All buttons are set to an equal size.<P>
+ *
+ * While <CODE>ButtonLayout</CODE> was designed for <CODE>Buttons</CODE>,
+ * any component can be added to the layout. All components are
+ * set to an equal size.<P>
+ *
+ */
+public class ButtonLayout implements LayoutManager {
+
+ ALIGNMENT align;
+ int hgap;
+ int vgap;
+
+ /**
+ * Constructs a new <CODE>ButtonLayout</CODE> with a centered alignment.
+ */
+ public ButtonLayout() {
+ this(ALIGNMENT.CENTER, 5, 5);
+ }
+
+ /**
+ * Constructs a new <CODE>ButtonLayout</CODE> with the specified alignment.
+ * @param <VAR>align</VAR> The alignment value.
+ * @see ALIGNMENT
+ */
+ public ButtonLayout(ALIGNMENT align) {
+ this(align, 5, 5);
+ }
+
+ /**
+ * Constructs a new <CODE>ButtonLayout</CODE> with the specified
+ * alignment and gap values.
+ * @param <VAR>align</VAR> The alignment value.
+ * @param <VAR>hgap</VAR> The horizontal gap variable.
+ * @param <VAR>vgap</VAR> The vertical gap variable.
+ * @see ALIGNMENT
+ */
+ public ButtonLayout(ALIGNMENT align, int hgap, int vgap) {
+ this.align = align;
+ this.hgap = hgap;
+ this.vgap = vgap;
+ }
+
+ /**
+ * Adds the specified component to the layout. This is not
+ *used by this class.
+ * @param <VAR>name</VAR> The name of the component.
+ * @param <VAR>comp</VAR> The component to be added.
+ */
+ public void addLayoutComponent(String name, Component comp) {
+ }
+
+ /**
+ * Removes the specified component from the layout. This
+ * is not used by this class.
+ * @param <VAR>comp</VAR> The component to remove.
+ */
+ public void removeLayoutComponent(Component comp) {
+ }
+
+ /**
+ * Returns the preferred dimensions for this layout given
+ * the components in the specified target container.
+ * @param <VAR>target</VAR> The component that needs to be laid out.
+ * @see java.awt.Container
+ * @see #minimumLayoutSize
+ */
+ public Dimension preferredLayoutSize(Container target) {
+ Dimension dim = new Dimension(0, 0);
+ int nmembers = target.getComponentCount();
+
+ for (int i = 0; i < nmembers; i++) {
+ Component m = target.getComponent(i);
+ if (m.isVisible()) {
+ Dimension d = m.getPreferredSize();
+ dim.height = Math.max(dim.height, d.height);
+ dim.width = Math.max(dim.width, d.width);
+ }
+ }
+ dim.width = (dim.width*nmembers) + (hgap*nmembers-1);
+ Insets insets = target.getInsets();
+ dim.width += insets.left + insets.right + hgap*2;
+ dim.height += insets.top + insets.bottom + vgap*2;
+ return dim;
+ }
+
+ /**
+ * Returns the minimum dimensions needed to layout the components
+ * contained in the specified target container.
+ * @param <VAR>target</VAR> The component that needs to be laid out
+ * @see #preferredLayoutSize
+ */
+ public Dimension minimumLayoutSize(Container target) {
+ Dimension dim = new Dimension(0, 0);
+ int nmembers = target.getComponentCount();
+
+ for (int i = 0; i < nmembers; i++) {
+ Component m = target.getComponent(i);
+ if (m.isVisible()) {
+ Dimension d = m.getMinimumSize();
+ dim.height = Math.max(dim.height, d.height);
+ dim.width = Math.max(dim.width, d.width);
+ }
+ }
+ dim.width = (dim.width*nmembers) + (hgap*nmembers-1);
+ Insets insets = target.getInsets();
+ dim.width += insets.left + insets.right + hgap*2;
+ dim.height += insets.top + insets.bottom + vgap*2;
+ return dim;
+ }
+
+ /**
+ * Centers the elements in the specified row, if there is any slack.
+ * @param <VAR>target</VAR> The component which needs to be moved.
+ * @param <VAR>x</VAR> The x coordinate.
+ * @param <VAR>y</VAR> The y coordinate.
+ * @param <VAR>width</VAR> The width dimensions.
+ * @param <VAR>height</VAR> The height dimensions.
+ * @param <VAR>rowStart</VAR> Index of the first component in the row.
+ * @param <VAR>rowEnd</VAR> Index of the last component in the row.
+ */
+ private void moveComponents(Container target, int x, int y, int width,
+ int height, int rowStart, int rowEnd) {
+ Dimension dim;
+
+ if (align == ALIGNMENT.LEFT) {
+ // do nothing
+ } else if (align == ALIGNMENT.CENTER) {
+ x += width / 2;
+ } else if (align == ALIGNMENT.RIGHT) {
+ x += width;
+ }
+ for (int i = rowStart; i < rowEnd; i++) {
+ Component m = target.getComponent(i);
+ if (m.isVisible()) {
+ dim = m.getSize();
+ m.setLocation(x, y + (height - dim.height) / 2);
+ x += hgap + dim.width;
+ }
+ }
+ }
+
+ /**
+ * Lays out the container. This method will actually reshape the
+ * components in the target in order to satisfy the constraints of
+ * the <CODE>BorderLayout</CODE> object.
+ * @param <VAR>target</VAR> The specified component being laid out.
+ * @see java.awt.Container
+ */
+ public void layoutContainer(Container target) {
+ Insets insets = target.getInsets();
+ Dimension tdim = target.getSize();
+ int maxwidth = tdim.width - (insets.left + insets.right + hgap*2);
+ int nmembers = target.getComponentCount();
+ int x = 0, y = insets.top + vgap;
+ int rowh = 0, start = 0;
+ Dimension dim = new Dimension(0, 0);
+
+ for (int i = 0; i < nmembers; i++) {
+ Component m = target.getComponent(i);
+ if (m.isVisible()) {
+ Dimension d = m.getMinimumSize();
+ dim.width = Math.max(dim.width, d.width);
+ }
+ }
+ for (int i = 0; i < nmembers; i++) {
+ Component m = target.getComponent(i);
+ if (m.isVisible()) {
+ Dimension d = m.getPreferredSize();
+ m.setSize(dim.width, d.height);
+
+ if ((x == 0) || ((x + dim.width) <= maxwidth)) {
+ if (x > 0) {
+ x += hgap;
+ }
+ x += dim.width;
+ rowh = Math.max(rowh, d.height);
+ } else {
+ moveComponents(target, insets.left + hgap, y, maxwidth - x,
+ rowh, start, i);
+ x = dim.width;
+ y += vgap + rowh;
+ rowh = d.height;
+ start = i;
+ }
+ }
+ }
+ moveComponents(target, insets.left + hgap, y, maxwidth - x, rowh,
+ start, nmembers);
+ }
+
+ /**
+ * Returns the <CODE>String</CODE> representation of this
+ * <CODE>ButtonLayout</CODE>'s values.
+ */
+ public String toString() {
+ String str = "";
+ if (align == ALIGNMENT.LEFT) {
+ str = ",align=left";
+ } else if (align == ALIGNMENT.RIGHT) {
+ str = ",align=right";
+ } else if (align == ALIGNMENT.CENTER) {
+ str = ",align=center";
+ }
+ return getClass().getName()
+ + "[hgap=" + hgap + ",vgap=" + vgap + str + "]";
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ButtonPanel.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ButtonPanel.java
new file mode 100644
index 0000000000..7a2d734da8
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ButtonPanel.java
@@ -0,0 +1,151 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 1998-2002 by Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.ui;
+
+import javax.swing.*;
+import java.awt.event.*;
+import java.util.*;
+
+/**
+ * A simple panel with the buttons commonly used in a dialog. Register as
+ * a ButtonPanelListener in order to receive the events from this class.
+ * @see ButtonPanelListener
+ */
+public class ButtonPanel extends JPanel {
+
+ // Convert action events on each button to button panel notifications
+ class ButtonAdaptor implements ActionListener {
+ public void actionPerformed(ActionEvent e) {
+ int buttonId = -1;
+ Object source = e.getSource();
+ if (source == okButton) {
+ buttonId = ButtonPanelListener.OK;
+ } else if (source == resetButton) {
+ buttonId = ButtonPanelListener.RESET;
+ } else if (source == cancelButton) {
+ buttonId = ButtonPanelListener.CANCEL;
+ } else if (source == helpButton) {
+ buttonId = ButtonPanelListener.HELP;
+ }
+ Enumeration en = listeners.elements();
+ while (en.hasMoreElements()) {
+ ButtonPanelListener l = (ButtonPanelListener)en.nextElement();
+ l.buttonPressed(buttonId);
+ }
+ }
+ }
+
+ JButton okButton, resetButton, cancelButton, helpButton;
+ ButtonAdaptor adaptor;
+ Vector listeners;
+
+ /**
+ * Construct a ButtonPanel with OK, Cancel, and Help buttons, and
+ * the reset button display controlled by the parameter.
+ * @param showReset true if Reset button is to be included
+ */
+ public ButtonPanel(boolean showReset) {
+ this(showReset, true);
+ }
+
+ /**
+ * Construct a ButtonPanel with reset and help buttons controlled
+ * by the parameters passed, and always showing OK and Cancel.
+ * @param showReset true if Reset button is to be included
+ * @param showHelp true if Help button is to be included
+ */
+ public ButtonPanel(boolean showReset, boolean showHelp) {
+ super();
+ setLayout(new ButtonLayout(ALIGNMENT.RIGHT));
+ // Create event handler
+ adaptor = new ButtonAdaptor();
+ listeners = new Vector();
+
+ Mnemonic mn = new Mnemonic(ResourceStrings.getString("ok_button"));
+ okButton = new JButton(mn.getString());
+ okButton.setToolTipText(mn.getString());
+ okButton.setMnemonic(mn.getMnemonic());
+
+ okButton.setEnabled(false);
+ okButton.addActionListener(adaptor);
+ add(okButton);
+
+ // Only show reset if requested
+ if (showReset) {
+ Mnemonic mnReset =
+ new Mnemonic(ResourceStrings.getString("reset_button"));
+ resetButton = new JButton(mnReset.getString());
+ resetButton.setToolTipText(mnReset.getString());
+ resetButton.setMnemonic(mnReset.getMnemonic());
+
+ resetButton.addActionListener(adaptor);
+ add(resetButton);
+ } else {
+ resetButton = null;
+ }
+
+ Mnemonic mnCancel =
+ new Mnemonic(ResourceStrings.getString("cancel_button"));
+ cancelButton = new JButton(mnCancel.getString());
+ cancelButton.setToolTipText(mnCancel.getString());
+ cancelButton.setMnemonic(mnCancel.getMnemonic());
+
+ cancelButton.addActionListener(adaptor);
+ add(cancelButton);
+
+ if (showHelp) {
+ Mnemonic mnHelp =
+ new Mnemonic(ResourceStrings.getString("help_button"));
+ helpButton = new JButton(mnHelp.getString());
+ helpButton.setToolTipText(mnHelp.getString());
+ helpButton.setMnemonic(mnHelp.getMnemonic());
+
+ helpButton.addActionListener(adaptor);
+ add(helpButton);
+ } else {
+ helpButton = null;
+ }
+ }
+
+ public void addButtonPanelListener(ButtonPanelListener l) {
+ listeners.addElement(l);
+ }
+
+ public void removeButtonPanelListener(ButtonPanelListener l) {
+ listeners.removeElement(l);
+ }
+
+ public void setOkEnabled(boolean state) {
+ okButton.setEnabled(state);
+ }
+
+ public boolean isOkEnabled() {
+ return okButton.isEnabled();
+ }
+
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ButtonPanelListener.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ButtonPanelListener.java
new file mode 100644
index 0000000000..c0e434708a
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ButtonPanelListener.java
@@ -0,0 +1,37 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1998-1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.ui;
+
+public interface ButtonPanelListener {
+ public final static int OK = 0;
+ public final static int CANCEL = 1;
+ public final static int HELP = 2;
+ public final static int RESET = 3;
+
+ public void buttonPressed(int buttonId);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/DownButton.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/DownButton.java
new file mode 100644
index 0000000000..3301ac9ea2
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/DownButton.java
@@ -0,0 +1,39 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2002 by Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.ui;
+
+/**
+ * Class to produce a button with an icon pointing down.
+ */
+public class DownButton extends ImageButton {
+ public DownButton() {
+ Mnemonic mnText =
+ new Mnemonic(ResourceStrings.getString("down_button"));
+ setImage(getClass(), "down.gif", mnText);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ExtendedCellRenderer.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ExtendedCellRenderer.java
new file mode 100644
index 0000000000..ec743cc795
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ExtendedCellRenderer.java
@@ -0,0 +1,62 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1998-1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.ui;
+
+import javax.swing.table.DefaultTableCellRenderer;
+import java.util.Date;
+import java.text.DateFormat;
+import java.net.InetAddress;
+import com.sun.dhcpmgr.data.IPAddress;
+
+// Renderer for cells containing Dates, InetAddresses or IPAddresses
+public class ExtendedCellRenderer extends DefaultTableCellRenderer {
+ private DateFormat dateFormat = DateFormat.getInstance();
+
+ protected void setValue(Object value) {
+ if (value != null) {
+ if (value instanceof Date) {
+ long t = ((Date)value).getTime();
+ if (t == 0) {
+ super.setValue(null);
+ } else if (t < 0) {
+ super.setValue(ResourceStrings.getString("never"));
+ } else {
+ super.setValue(dateFormat.format(value));
+ }
+ } else if (value instanceof InetAddress) {
+ super.setValue(((InetAddress)value).getHostAddress());
+ } else if (value instanceof IPAddress) {
+ super.setValue(((IPAddress)value).getHostAddress());
+ } else {
+ super.setValue(value);
+ }
+ } else {
+ super.setValue(value);
+ }
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/FieldLayout.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/FieldLayout.java
new file mode 100644
index 0000000000..4dbf2e09d7
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/FieldLayout.java
@@ -0,0 +1,293 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1996-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.ui;
+
+import java.awt.*;
+import java.util.*;
+
+/**
+ * <CODE>FieldLayout</CODE> treats components as a list of
+ * labeled fields, where each label is placed on the left
+ * edge of the container with its associated field to its
+ * right.<P>
+ *
+ * Two kinds of components may be added: "Label" and "Field."
+ * Labels and Fields must be added in pairs, because there is
+ * a one-to-one correspondence between them.<P>
+ *
+ * When a <CODE>Field</CODE> is added, it is associated with the
+ * last <CODE>Label</CODE> added.<P>
+ */
+public class FieldLayout implements LayoutManager {
+ public static final String LABEL = "Label";
+ public static final String LABELTOP = "LabelTop";
+ public static final String FIELD = "Field";
+
+ class Row {
+ Component label;
+ Component field;
+ double yRatio;
+ boolean center;
+
+ public Row() {
+ label = null;
+ field = null;
+ yRatio = 1;
+ center = true;
+ }
+ }
+
+ Vector rows;
+ int hgap;
+ int vgap;
+
+ /**
+ * Constructs a new <CODE>FieldLayout</CODE> with a centered alignment.
+ */
+ public FieldLayout() {
+ this(5, 5);
+ }
+
+ /**
+ * Constructs a new <CODE>FieldLayout</CODE> with the specified gap values.
+ * @param <VAR>hgap</VAR> The horizontal gap variable.
+ * @param <VAR>vgap</VAR> The vertical gap variable.
+ */
+ public FieldLayout(int hgap, int vgap) {
+ this.hgap = hgap;
+ this.vgap = vgap;
+ rows = new Vector();
+ }
+
+ /**
+ * Adds the specified component to the layout.
+ * @param <VAR>name</VAR> The name of the component.
+ * @param <VAR>comp</VAR> The component to be added.
+ */
+ public void addLayoutComponent(String name, Component comp) {
+ if (LABEL.equals(name)) {
+ Row r = new Row();
+ r.label = comp;
+ r.center = true;
+ rows.addElement(r);
+ } else if (LABELTOP.equals(name)) {
+ Row r = new Row();
+ r.label = comp;
+ r.center = false;
+ rows.addElement(r);
+ } else if (FIELD.equals(name)) {
+ ((Row)rows.lastElement()).field = comp;
+ }
+ }
+
+ /**
+ * Removes the specified component from the layout.
+ * @param <VAR>comp</VAR> The component to remove.
+ */
+ public void removeLayoutComponent(Component comp) {
+ Enumeration en = rows.elements();
+ while (en.hasMoreElements()) {
+ Row r = (Row)en.nextElement();
+ if (comp == r.label || comp == r.field) {
+ rows.removeElement(r);
+ return;
+ }
+ }
+ }
+
+ /**
+ * Returns the preferred dimensions for this layout given the components
+ * in the specified target container.
+ * @param <VAR>target</VAR> The component that needs to be laid out.
+ * @see java.awt.Container
+ * @see #minimumLayoutSize
+ */
+ public Dimension preferredLayoutSize(Container target) {
+ Dimension dim = new Dimension(0, 0);
+ int widestLabel = 0, widestField = 0;
+
+ Enumeration en = rows.elements();
+ while (en.hasMoreElements()) {
+ Row r = (Row)en.nextElement();
+ if (!r.label.isVisible() || !r.field.isVisible()) {
+ continue;
+ }
+ Dimension ld = r.label.getPreferredSize();
+ widestLabel = Math.max(widestLabel, ld.width);
+ Dimension fd = r.field.getPreferredSize();
+ widestField = Math.max(widestField, fd.width);
+ dim.height += Math.max(ld.height, fd.height) + vgap;
+ }
+ dim.width = widestLabel + hgap + widestField;
+ Insets insets = target.getInsets();
+ dim.width += insets.left + insets.right + hgap*2;
+ dim.height += insets.top + insets.bottom + vgap;
+ return dim;
+ }
+
+ /**
+ * Returns the minimum dimensions needed to layout the components
+ * contained in the specified target container.
+ * @param <VAR>target</VAR> The component that needs to be laid out.
+ * @see #preferredLayoutSize
+ */
+ public Dimension minimumLayoutSize(Container target) {
+ Dimension dim = new Dimension(0, 0);
+ int widestLabel = 0, widestField = 0;
+
+ Enumeration en = rows.elements();
+ while (en.hasMoreElements()) {
+ Row r = (Row)en.nextElement();
+ if (!r.label.isVisible() || !r.field.isVisible()) {
+ continue;
+ }
+ Dimension ld = r.label.getMinimumSize();
+ widestLabel = Math.max(widestLabel, ld.width);
+ Dimension fd = r.field.getMinimumSize();
+ widestField = Math.max(widestField, fd.width);
+ dim.height += Math.max(ld.height, fd.height) + vgap;
+ }
+ dim.width = widestLabel + hgap + widestField;
+ Insets insets = target.getInsets();
+ dim.width += insets.left + insets.right + hgap*2;
+ dim.height += insets.top + insets.bottom + vgap;
+ return dim;
+ }
+
+ /**
+ * Performs the layout of the container. Components are treated
+ * either as labels or fields. Labels go on the left (right-aligned),
+ * with their associated fields placed immediately to their right.
+ * @param <VAR>target</VAR> The specified component being laid out.
+ * @see java.awt.Container
+ */
+ public void layoutContainer(Container target) {
+ Insets insets = target.getInsets();
+ Dimension dim = target.getSize();
+ int x = 0, y = insets.top, offset = 0;
+ int widestLabel = 0;
+ int ySlop = 0;
+
+ // Compute whether preferred sizes will fit
+ Dimension pDim = preferredLayoutSize(target);
+ boolean usingPreferred = true;
+ if ((pDim.height > (dim.height - insets.top - insets.bottom)) ||
+ (pDim.width > (dim.width - insets.left - insets.right))) {
+ usingPreferred = false;
+ // Compute leftover vertical space
+ pDim = minimumLayoutSize(target);
+ ySlop = dim.height - insets.top - insets.bottom - pDim.height;
+ if (ySlop < 0) {
+ ySlop = 0;
+ }
+ }
+
+ /*
+ * Find widest label. Our policy on horizontal space is that labels
+ * are fully satisfied and fields get whatever's left.
+ * For vertical space, if there's any leftovers then allocate it
+ * in proportion to demand, which we'll define as the ratio between
+ * preferred size and minimum size.
+ */
+ double sumRatios = 0;
+ Enumeration en = rows.elements();
+ while (en.hasMoreElements()) {
+ Row r = (Row)en.nextElement();
+ if (r.label.isVisible() && r.field.isVisible()) {
+ Dimension d = usingPreferred ? r.label.getPreferredSize() :
+ r.label.getMinimumSize();
+ widestLabel = Math.max(widestLabel, d.width);
+ if (!usingPreferred) {
+ double lRatio = r.label.getPreferredSize().getHeight() /
+ r.label.getMinimumSize().getHeight();
+ double fRatio = r.field.getPreferredSize().getHeight() /
+ r.field.getMinimumSize().getHeight();
+ r.yRatio = Math.max(lRatio, fRatio);
+ }
+ // If there is no demand, then adjust ratio to zero
+ if (r.yRatio == 1.0) {
+ r.yRatio = 0;
+ }
+ sumRatios += r.yRatio;
+ }
+ }
+
+ // lay out rows, right-aligning labels
+ en = rows.elements();
+ while (en.hasMoreElements()) {
+ Row r = (Row)en.nextElement();
+ Component l = r.label;
+ Component f = r.field;
+ // Skip the row if both aren't visible
+ if (!l.isVisible() || !f.isVisible())
+ continue;
+ Dimension ld = usingPreferred ? l.getPreferredSize() :
+ l.getMinimumSize();
+ Dimension fd = usingPreferred ? f.getPreferredSize() :
+ f.getMinimumSize();
+
+ int rowHeight = Math.max(ld.height, fd.height) +
+ (int)(ySlop * r.yRatio / sumRatios);
+
+ x = insets.left;
+ /*
+ * If the field is visible, move it right to line up with
+ * the widest line.
+ */
+ x += Math.max(widestLabel - ld.width, 0);
+ offset = 0;
+ if (r.center) {
+ // center label on field
+ offset = Math.max(0, (rowHeight-ld.height)/2);
+ }
+ int labelHeight = rowHeight;
+ /*
+ * If label doesn't look like it wants extra space, don't give it;
+ * otherwise, JLabels will get drawn centered even if user
+ * specified it as a top alignment when doing the layout.
+ */
+ if (l.getPreferredSize().height == l.getMinimumSize().height) {
+ labelHeight = ld.height;
+ }
+ l.setBounds(x, y+offset, ld.width, labelHeight);
+ x = insets.left + widestLabel + hgap;
+ int w = dim.width-x-hgap;
+ f.setBounds(x, y, w, rowHeight);
+ y += rowHeight + vgap;
+ }
+
+ }
+
+ /**
+ * Returns the <CODE>String</CODE> representation of this
+ * <CODE>FieldLayout</CODE>'s values.
+ */
+ public String toString() {
+ return getClass().getName() + "[hgap=" + hgap + ",vgap=" + vgap + "]";
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/HelpIds.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/HelpIds.java
new file mode 100644
index 0000000000..3cbbe422ab
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/HelpIds.java
@@ -0,0 +1,61 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1998-1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.ui;
+
+import java.util.*;
+
+public class HelpIds {
+ private ResourceBundle bundle;
+
+ public HelpIds(String bundleName) throws MissingResourceException {
+ bundle = ResourceBundle.getBundle(bundleName);
+ }
+
+ public String getFilePath(String key) {
+ try {
+ /*
+ * The original version of this code was:
+ * if (bundle.getLocale().toString().length() == 0) {
+ * bug 4177489 causes that not to work correctly, so for the moment
+ * we *require* that each key in a locale contain a relative
+ * path, otherwise it is assumed we're in the default locale and
+ * proceed with the default location.
+ */
+ String s = bundle.getString(key);
+ if (s.indexOf('/') == -1) {
+ // Not localized, use the default location
+ return "/usr/sadm/admin/dhcpmgr/help/" + s;
+ } else {
+ return "/usr/share/lib/locale/com/sun/dhcpmgr/client/help/" + s;
+ }
+ } catch (Throwable e) {
+ e.printStackTrace();
+ return "";
+ }
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/HostnameField.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/HostnameField.java
new file mode 100644
index 0000000000..912fa180d9
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/HostnameField.java
@@ -0,0 +1,98 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1998-1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.ui;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import javax.swing.text.*;
+
+/**
+ * A text field which limits input to only those characters which can legally
+ * appear in a hostname as specified in RFC1123, i.e. the letters, digits
+ * and '-'.
+ */
+public class HostnameField extends JTextField {
+
+ /**
+ * Constructs an empty field, 20 characters wide.
+ */
+ public HostnameField() {
+ this("");
+ }
+
+ /**
+ * Constructs a field initialized to the provided text. Defaults to 20
+ * characters wide.
+ * @param text the text to display initially
+ */
+ public HostnameField(String text) {
+ this(text, 20);
+ }
+
+ /**
+ * Constructs a field initialized to the provided text with the requested
+ * size.
+ * @param text the text to display initially
+ * @param length the length in characters the field should size itself to
+ */
+ public HostnameField(String text, int length) {
+ super(text, length);
+ }
+
+ protected Document createDefaultModel() {
+ return new HostnameDocument();
+ }
+
+ /*
+ * This is the recommended way to validate/filter input, as opposed to
+ * trapping KeyEvents because this will catch paste operations, too.
+ */
+ static class HostnameDocument extends PlainDocument {
+ public void insertString(int offs, String str, AttributeSet a)
+ throws BadLocationException {
+ if (str != null) {
+ char [] chars = str.toCharArray();
+ if ((chars.length != 0) && (getLength() == 0)) {
+ // First character in field must be a letter or digit
+ if (!Character.isLetterOrDigit(chars[0])) {
+ throw new BadLocationException("", offs);
+ }
+ }
+ // Now validate that everything we're inserting is legal
+ for (int i = 0; i < chars.length; ++i) {
+ if (!Character.isLetterOrDigit(chars[i])
+ && chars[i] != '-') {
+ throw new BadLocationException("", offs);
+ }
+ }
+ }
+ super.insertString(offs, str, a);
+ }
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/IPAddressField.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/IPAddressField.java
new file mode 100644
index 0000000000..aa9bb9b01f
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/IPAddressField.java
@@ -0,0 +1,126 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1998-1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.ui;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import javax.swing.text.*;
+import java.net.InetAddress;
+
+import com.sun.dhcpmgr.data.IPAddress;
+import com.sun.dhcpmgr.data.ValidationException;
+
+/**
+ * A text field which limits input to only those characters which can legally
+ * appear in an IP address, i.e. the digits and '.'.
+ */
+public class IPAddressField extends JTextField {
+
+ /**
+ * Constructs an empty field.
+ */
+ public IPAddressField() {
+ this("");
+ }
+
+ /**
+ * Constructs a field initialized to the provided text.
+ * @param text the text to display initially
+ */
+ public IPAddressField(String text) {
+ super(text, 15);
+ }
+
+ /**
+ * Sets the value to a provided IP address.
+ * @param addr an <code>InetAddress</code> to display
+ */
+ public void setValue(InetAddress addr) {
+ if (addr == null) {
+ setText("");
+ } else {
+ setText(addr.getHostAddress());
+ }
+ }
+
+ /**
+ * Sets the value to the provided IP address. This is our special
+ * <code>IPAddress</code> class.
+ * @param addr a <code>IPAddress</code> to display
+ */
+ public void setValue(IPAddress addr) {
+ if (addr == null) {
+ setText("");
+ } else {
+ setText(addr.getHostAddress());
+ }
+ }
+
+ /**
+ * Return the current value as an <code>IPAddress</code>.
+ * @return the current value as an <code>IPAddress</code>, or null if the
+ * current text is not a valid IP address.
+ */
+ public IPAddress getValue() {
+ IPAddress a = null;
+ try {
+ a = new IPAddress(getText());
+ } catch (ValidationException e) {
+ // Do nothing
+ }
+ return a;
+ }
+
+ protected Document createDefaultModel() {
+ return new IPAddressDocument();
+ }
+
+ /*
+ * This is the recommended way to validate/filter input, as opposed to
+ * trapping KeyEvents because this will catch paste operations, too.
+ */
+ static class IPAddressDocument extends PlainDocument {
+ public void insertString(int offs, String str, AttributeSet a)
+ throws BadLocationException {
+ if (str != null) {
+ char [] chars = str.toCharArray();
+ if ((getLength() + chars.length) > 15) {
+ // IP addresses are limited to 15 characters, period.
+ throw new BadLocationException("", offs);
+ }
+ for (int i = 0; i < chars.length; ++i) {
+ if (!Character.isDigit(chars[i]) && chars[i] != '.') {
+ throw new BadLocationException("", offs);
+ }
+ }
+ }
+ super.insertString(offs, str, a);
+ }
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/IPAddressList.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/IPAddressList.java
new file mode 100644
index 0000000000..b943e67bf7
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/IPAddressList.java
@@ -0,0 +1,364 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2002 by Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.ui;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import javax.swing.event.*;
+import java.util.*;
+
+import com.sun.dhcpmgr.data.IPAddress;
+import com.sun.dhcpmgr.data.ValidationException;
+
+/**
+ * A panel which allows the user to edit a list of IP addresses. Consists of
+ * a text field for entering new data, paired with a list which allows the
+ * addresses entered to be ordered or deleted.
+ */
+public class IPAddressList extends JPanel {
+ IPAddressField address;
+ JList serverList;
+ IPAddressListModel serverListModel;
+ JButton add, delete;
+ UpButton moveUp;
+ DownButton moveDown;
+
+ /**
+ * Construct the address list.
+ */
+ public IPAddressList() {
+ super();
+ GridBagLayout bag = new GridBagLayout();
+ setLayout(bag);
+
+ GridBagConstraints c = new GridBagConstraints();
+ c.gridx = c.gridy = 0;
+ c.weightx = c.weighty = 1.0;
+ c.gridheight = 1;
+ c.gridwidth = 1;
+
+ // Field to type in addresses
+ address = new IPAddressField();
+ address.getAccessibleContext().setAccessibleDescription(
+ ResourceStrings.getString("dhcp_server_address"));
+
+ c.fill = GridBagConstraints.HORIZONTAL;
+ bag.setConstraints(address, c);
+ add(address);
+
+ // Button for Add operation
+ Mnemonic mnAdd = new Mnemonic(ResourceStrings.getString("add"));
+ add = new JButton(mnAdd.getString());
+ add.setToolTipText(mnAdd.getString());
+ add.setMnemonic(mnAdd.getMnemonic());
+
+ c.fill = GridBagConstraints.NONE;
+ ++c.gridx;
+ c.weightx = 0.5;
+ bag.setConstraints(add, c);
+ add(add);
+
+ // List for addresses
+ serverListModel = new IPAddressListModel();
+ serverList = new JList(serverListModel);
+ // Make sure it's wide enough for our purposes
+ serverList.setPrototypeCellValue("222.222.222.222");
+ serverList.setSelectionMode(
+ ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
+ JScrollPane scrollPane = new JScrollPane(serverList);
+ // Don't allow horizontal scrolling here
+ scrollPane.setHorizontalScrollBarPolicy(
+ JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
+ c.fill = GridBagConstraints.BOTH;
+ c.gridx = 0;
+ ++c.gridy;
+ c.weightx = 1.0;
+ bag.setConstraints(scrollPane, c);
+ add(scrollPane);
+
+ // Buttons to manipulate the list contents
+ JPanel buttonPanel = new JPanel(new VerticalButtonLayout());
+ moveUp = new UpButton();
+ moveUp.setToolTipText(ResourceStrings.getString("move_up"));
+ buttonPanel.add(moveUp);
+ moveDown = new DownButton();
+ moveDown.setToolTipText(ResourceStrings.getString("move_down"));
+ buttonPanel.add(moveDown);
+
+ Mnemonic mnDelete = new Mnemonic(ResourceStrings.getString("delete"));
+ delete = new JButton(mnDelete.getString());
+ delete.setToolTipText(mnDelete.getString());
+ delete.setMnemonic(mnDelete.getMnemonic());
+
+ buttonPanel.add(delete);
+ ++c.gridx;
+ c.weightx = 0.5;
+ bag.setConstraints(buttonPanel, c);
+ add(buttonPanel);
+
+ // Disable all buttons to start; selection changes adjust button state
+ add.setEnabled(false);
+ delete.setEnabled(false);
+ moveUp.setEnabled(false);
+ moveDown.setEnabled(false);
+
+ // Create listener for button presses, take action as needed
+ ActionListener l = new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ if (e.getSource() == add || e.getSource() == address) {
+ IPAddress a = address.getValue();
+ if (a != null) {
+ serverListModel.addElement(a);
+ }
+ } else if (e.getSource() == delete) {
+ int [] indices = serverList.getSelectedIndices();
+ if (indices.length > 1) {
+ /*
+ * Need to sort the indices so that the delete's
+ * don't interfere with each other
+ */
+ for (int i = 0; i < indices.length; ++i) {
+ for (int j = i; j < indices.length; ++j) {
+ if (indices[i] > indices[j]) {
+ int k = indices[i];
+ indices[i] = indices[j];
+ indices[j] = k;
+ }
+ }
+ }
+ }
+ // Now delete from high index to low
+ for (int i = indices.length - 1; i >= 0; --i) {
+ serverListModel.removeElementAt(indices[i]);
+ }
+ if (indices.length > 1) {
+ // Clear selection if multiple deleted
+ serverList.clearSelection();
+ /*
+ * We don't get a selection event for some reason
+ * so make it work for now
+ */
+ delete.setEnabled(false);
+ } else {
+ // Make sure to select something in the list
+ if (serverListModel.getSize() == 0) {
+ /*
+ * List is empty, nothing to select so disable
+ * delete
+ */
+ delete.setEnabled(false);
+ } else if (indices[0] >= serverListModel.getSize()) {
+ // Select last one if we're off the end
+ serverList.setSelectedIndex(
+ serverListModel.getSize()-1);
+ } else {
+ // Select next one in list
+ serverList.setSelectedIndex(indices[0]);
+ }
+ }
+ } else if (e.getSource() == moveUp) {
+ int i = serverList.getSelectedIndex();
+ serverListModel.moveUp(i);
+ // Keep item selected so repeated moveUp's affect same item
+ serverList.setSelectedIndex(i-1);
+ } else if (e.getSource() == moveDown) {
+ int i = serverList.getSelectedIndex();
+ serverListModel.moveDown(i);
+ // Keep item selected so repeated moveDowns affect same item
+ serverList.setSelectedIndex(i+1);
+ }
+ }
+ };
+ address.addActionListener(l);
+ add.addActionListener(l);
+ delete.addActionListener(l);
+ moveUp.addActionListener(l);
+ moveDown.addActionListener(l);
+
+ // Put a selection listener on the list to enable buttons appropriately
+ serverList.addListSelectionListener(new ListSelectionListener() {
+ public void valueChanged(ListSelectionEvent e) {
+ int [] indices = serverList.getSelectedIndices();
+ switch (indices.length) {
+ case 0:
+ // Nothing selected; disable them all
+ delete.setEnabled(false);
+ moveUp.setEnabled(false);
+ moveDown.setEnabled(false);
+ break;
+ case 1:
+ delete.setEnabled(true);
+ // Can't move first one up
+ if (indices[0] == 0) {
+ moveUp.setEnabled(false);
+ } else {
+ moveUp.setEnabled(true);
+ }
+ // Can't move last one down
+ if (indices[0] == (serverListModel.getSize() - 1)) {
+ moveDown.setEnabled(false);
+ } else {
+ moveDown.setEnabled(true);
+ }
+ break;
+ default:
+ // More than one; only delete is allowed
+ delete.setEnabled(true);
+ moveUp.setEnabled(false);
+ moveDown.setEnabled(false);
+ }
+ }
+ });
+ // Enable Add when address is not empty.
+ address.getDocument().addDocumentListener(new DocumentListener() {
+ public void insertUpdate(DocumentEvent e) {
+ add.setEnabled(address.getText().length() != 0);
+ }
+ public void changedUpdate(DocumentEvent e) {
+ insertUpdate(e);
+ }
+ public void removeUpdate(DocumentEvent e) {
+ insertUpdate(e);
+ }
+ });
+ }
+
+ /**
+ * Initialize the data in the list
+ */
+ public void setAddressList(IPAddress [] ipAddrs) {
+ serverListModel.setData(ipAddrs);
+ }
+
+ /**
+ * Set the addresses from a comma-delimited string
+ */
+ public void setAddressList(String s) {
+ serverListModel.setData(s);
+ }
+
+ /**
+ * Retrieve the data in the list
+ */
+ public Vector getAddressList() {
+ return serverListModel.getDataVector();
+ }
+
+ /**
+ * Retrieve the list converted to a comma-delimited string
+ */
+ public String getAddressListString() {
+ return serverListModel.getDataString();
+ }
+
+ /**
+ * Return the size of the list
+ */
+ public int getListSize() {
+ return serverListModel.getDataVector().size();
+ }
+
+ class IPAddressListModel extends AbstractListModel {
+ private Vector addrs;
+
+ public IPAddressListModel() {
+ super();
+ addrs = new Vector();
+ }
+
+ public int getSize() {
+ return addrs.size();
+ }
+
+ public Object getElementAt(int index) {
+ return addrs.elementAt(index);
+ }
+
+ public void setData(IPAddress [] ipAddrs) {
+ addrs.removeAllElements();
+ for (int i = 0; i < ipAddrs.length; ++i) {
+ addrs.addElement(ipAddrs[i]);
+ }
+ fireContentsChanged(this, 0, addrs.size()-1);
+ }
+
+ public void setData(String s) {
+ addrs.removeAllElements();
+ StringTokenizer st = new StringTokenizer(s, ",");
+ while (st.hasMoreTokens()) {
+ try {
+ addrs.addElement(new IPAddress(st.nextToken()));
+ } catch (ValidationException e) {
+ // Ignore it, didn't parse for some reason
+ }
+ }
+ fireContentsChanged(this, 0, addrs.size()-1);
+ }
+
+ public void addElement(IPAddress addr) {
+ addrs.addElement(addr);
+ fireIntervalAdded(this, addrs.size()-1, addrs.size()-1);
+ }
+
+ public void removeElementAt(int index) {
+ addrs.removeElementAt(index);
+ fireIntervalRemoved(this, index, index);
+ }
+
+ public void moveUp(int index) {
+ Object t = addrs.elementAt(index-1);
+ addrs.setElementAt(addrs.elementAt(index), index-1);
+ addrs.setElementAt(t, index);
+ fireContentsChanged(this, index-1, index);
+ }
+
+ public void moveDown(int index) {
+ Object t = addrs.elementAt(index+1);
+ addrs.setElementAt(addrs.elementAt(index), index+1);
+ addrs.setElementAt(t, index);
+ fireContentsChanged(this, index, index+1);
+ }
+
+ public Vector getDataVector() {
+ return (Vector)addrs.clone();
+ }
+
+ public String getDataString() {
+ StringBuffer b = new StringBuffer();
+ Enumeration en = addrs.elements();
+ while (en.hasMoreElements()) {
+ if (b.length() != 0) {
+ b.append(',');
+ }
+ b.append(((IPAddress)en.nextElement()).getHostAddress());
+ }
+ return b.toString();
+ }
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ImageButton.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ImageButton.java
new file mode 100644
index 0000000000..1152a1dc9f
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ImageButton.java
@@ -0,0 +1,75 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2002 by Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.ui;
+
+import javax.swing.JButton;
+import javax.swing.ImageIcon;
+import java.io.InputStream;
+import java.io.BufferedInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+/**
+ * A button with an image loaded from a gif file. To use this class, extend it
+ * and in your constructor call setImage().
+ */
+public abstract class ImageButton extends JButton {
+
+ /**
+ * Sets the button's icon to the image loaded from the file; falls back the
+ * specified text if for some reason the icon can't be loaded. Base class
+ * is used to find the gif image in the same directory as the class that's
+ * using it, a convention we use.
+ * @param baseClass the name of the class we're doing this on behalf of
+ * @param file the name of the file the gif image is stored in
+ * @param mnText is the mnemonic/text to be used for the button
+ */
+ public void setImage(Class baseClass, String file, Mnemonic mnText) {
+ try {
+ InputStream resource = baseClass.getResourceAsStream(file);
+ if (resource != null) {
+ BufferedInputStream in = new BufferedInputStream(resource);
+ ByteArrayOutputStream out = new ByteArrayOutputStream(1024);
+ byte [] buffer = new byte[1024];
+ int n;
+ while ((n = in.read(buffer)) > 0) {
+ out.write(buffer, 0, n);
+ }
+ in.close();
+ out.flush();
+ buffer = out.toByteArray();
+ setIcon(new ImageIcon(buffer));
+ }
+ } catch (IOException ioe) {
+ }
+ // Added for accessibility
+ setText(mnText.getString());
+ setToolTipText(mnText.getString());
+ setMnemonic(mnText.getMnemonic());
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/IntegerField.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/IntegerField.java
new file mode 100644
index 0000000000..52d6e76d42
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/IntegerField.java
@@ -0,0 +1,139 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1998-1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.ui;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import javax.swing.text.*;
+
+/**
+ * A text field which limits input to only digits.
+ */
+public class IntegerField extends JTextField {
+
+ /**
+ * Construct an empty field
+ */
+ public IntegerField() {
+ this("");
+ }
+
+ /**
+ * Construct a field initialized to the specified text. Defaults to a width
+ * of 5 characters.
+ * @param text The initial text to display
+ */
+ public IntegerField(String text) {
+ this(text, 5);
+ }
+
+ /**
+ * Construct a field initialized to the specified value. Defaults to a
+ * width of 5 characters.
+ * @param value The initial value to display
+ */
+ public IntegerField(int value) {
+ this();
+ setValue(value);
+ }
+
+ /**
+ * Construct a field initialized to the specified Integer object.
+ * Defaults to a width of 5 characters.
+ * @param value The initial value to display
+ */
+ public IntegerField(Integer value) {
+ this();
+ setValue(value);
+ }
+
+ /**
+ * Construct a field initialized to the specified text and width.
+ * @param text The initial text to display
+ * @param width The width of the field in characters
+ */
+ public IntegerField(String text, int width) {
+ super(text, width);
+ setHorizontalAlignment(RIGHT);
+ }
+
+ /**
+ * Set the value of the field
+ * @param value An <code>int</code> value to set
+ */
+ public void setValue(int value) {
+ setText(String.valueOf(value));
+ }
+
+ /**
+ * Set the value of the field
+ * @param value The value to set as an <code>Integer</code>
+ */
+ public void setValue(Integer value) {
+ if (value != null) {
+ setValue(value.intValue());
+ }
+ }
+
+ /**
+ * Retrieve the entered value
+ * @return The value stored in the field as an <code>int</code>
+ */
+ public int getValue() {
+ String s = getText();
+ if ((s == null) || (s.length() == 0)) {
+ return 0;
+ } else {
+ return Integer.parseInt(getText());
+ }
+ }
+
+ protected Document createDefaultModel() {
+ return new IntegerDocument();
+ }
+
+ /*
+ * This is the recommended way to validate input, as opposed to trapping
+ * KeyEvents because this will actually catch paste operations as well.
+ */
+ static class IntegerDocument extends PlainDocument {
+ public void insertString(int offs, String str, AttributeSet a)
+ throws BadLocationException {
+ if (str != null) {
+ char [] chars = str.toCharArray();
+ for (int i = 0; i < chars.length; ++i) {
+ if (!Character.isDigit(chars[i])) {
+ throw new BadLocationException("", offs);
+ }
+ }
+ }
+ super.insertString(offs, str, a);
+ }
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/LeftButton.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/LeftButton.java
new file mode 100644
index 0000000000..4d64618d88
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/LeftButton.java
@@ -0,0 +1,40 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 1998-2002 by Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.ui;
+
+/**
+ * A button with an icon pointing left
+ */
+public class LeftButton extends ImageButton {
+
+ public LeftButton() {
+ Mnemonic mnText =
+ new Mnemonic(ResourceStrings.getString("left_button"));
+ setImage(getClass(), "back.gif", mnText);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ListPair.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ListPair.java
new file mode 100644
index 0000000000..d831a67035
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ListPair.java
@@ -0,0 +1,344 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2002 by Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.ui;
+
+import java.awt.*;
+import java.awt.event.ActionListener;
+import java.awt.event.ActionEvent;
+import javax.swing.*;
+import javax.swing.event.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/*
+ * This class is a hack to get around the fact that clearSelection()
+ * in DefaultListSelectionModel does not always fire an event to its listeners.
+ * We rely on such an event to enable & disable the arrow buttons which
+ * move data items between lists. See bug 4177723.
+ */
+class FixedSelectionModel extends DefaultListSelectionModel {
+ public void clearSelection() {
+ super.clearSelection();
+ fireValueChanged(getMinSelectionIndex(), getMaxSelectionIndex(), false);
+ }
+}
+
+/*
+ * We implement our own list model rather than use the default so
+ * that we can take advantage of some of the more advanced collection
+ * features not supported by DefaultListModel
+ */
+class OurListModel implements ListModel {
+ ArrayList data = new ArrayList();
+ EventListenerList listenerList = new EventListenerList();
+
+ public OurListModel(Object [] data) {
+ if (data != null) {
+ this.data.addAll(Arrays.asList(data));
+ }
+ }
+
+ public void addListDataListener(ListDataListener l) {
+ listenerList.add(ListDataListener.class, l);
+ }
+
+ public void removeListDataListener(ListDataListener l) {
+ listenerList.remove(ListDataListener.class, l);
+ }
+
+ protected void fireContentsChanged() {
+ ListDataEvent e =
+ new ListDataEvent(this, ListDataEvent.CONTENTS_CHANGED,
+ 0, getSize());
+ Object [] listeners = listenerList.getListenerList();
+ /*
+ * Listener array is formatted as pairs of (class, listener); walk
+ * the array backwards and call each ListDataListener in turn with
+ * the event. See javax.swing.event.EventListenerList for more info.
+ */
+ for (int i = listeners.length - 2; i >= 0; i -= 2) {
+ if (listeners[i] == ListDataListener.class) {
+ ((ListDataListener)listeners[i+1]).contentsChanged(e);
+ }
+ }
+ }
+
+ public Object getElementAt(int index) {
+ return data.get(index);
+ }
+
+ public int getSize() {
+ return data.size();
+ }
+
+ public void addElement(Object o) {
+ data.add(o);
+ fireContentsChanged();
+ }
+
+ public void removeElement(Object o) {
+ data.remove(data.indexOf(o));
+ fireContentsChanged();
+ }
+
+ public Object [] toArray(Object [] arr) {
+ return data.toArray(arr);
+ }
+}
+
+/*
+ * Our own layout manager which keeps the left & right lists the same size.
+ */
+class ListPairLayout implements LayoutManager {
+ Component leftComponent, centerComponent, rightComponent;
+ public static final String LEFT = "left";
+ public static final String CENTER = "center";
+ public static final String RIGHT = "right";
+
+ public ListPairLayout() {
+ leftComponent = centerComponent = rightComponent = null;
+ }
+
+ public void addLayoutComponent(String name, Component comp) {
+ if (name.equals(LEFT)) {
+ leftComponent = comp;
+ } else if (name.equals(CENTER)) {
+ centerComponent = comp;
+ } else if (name.equals(RIGHT)) {
+ rightComponent = comp;
+ }
+ }
+
+ public void layoutContainer(Container target) {
+ // Make left & right components same size, center no smaller than min
+ Insets insets = target.getInsets();
+ Dimension dim = target.getSize();
+ int x = insets.left;
+ int y = insets.top;
+ int totalHeight = dim.height - insets.bottom;
+ int totalWidth = dim.width - insets.right;
+
+ // If preferred sizes don't fit, go to minimum.
+ Dimension cDim = centerComponent.getPreferredSize();
+ Dimension d = preferredLayoutSize(target);
+ if (d.width > totalWidth || d.height > totalHeight) {
+ cDim = centerComponent.getMinimumSize();
+ }
+
+ // Left & right each get half of what's left after center allocated
+ int lrWidth = (totalWidth - cDim.width) / 2;
+
+ // Now place each component
+ leftComponent.setBounds(x, y, lrWidth, totalHeight);
+ centerComponent.setBounds(x + lrWidth, y, cDim.width, totalHeight);
+ rightComponent.setBounds(x + lrWidth + cDim.width, y, lrWidth,
+ totalHeight);
+ }
+
+ public Dimension minimumLayoutSize(Container parent) {
+ Dimension retDim = new Dimension();
+ // Compute minimum width as max(leftwidth, rightwidth) * 2 + centerwidth
+ int lrwidth = Math.max(leftComponent.getMinimumSize().width,
+ rightComponent.getMinimumSize().width);
+ retDim.width = lrwidth * 2 + centerComponent.getMinimumSize().width;
+ // Compute minimum height as max(leftheight, rightheight, centerheight)
+ int lrheight = Math.max(leftComponent.getMinimumSize().height,
+ rightComponent.getMinimumSize().height);
+ retDim.height = Math.max(centerComponent.getMinimumSize().height,
+ lrheight);
+ return retDim;
+ }
+
+ public Dimension preferredLayoutSize(Container parent) {
+ Dimension retDim = new Dimension();
+ // Preferred width is max(leftwidth, rightwidth) * 2 + centerwidth
+ int lrwidth = Math.max(leftComponent.getPreferredSize().width,
+ rightComponent.getPreferredSize().width);
+ retDim.width = lrwidth * 2 + centerComponent.getPreferredSize().width;
+ // Preferred height is max(leftheight, rightheight, centerheight)
+ int lrheight = Math.max(leftComponent.getPreferredSize().height,
+ rightComponent.getPreferredSize().height);
+ retDim.height = Math.max(centerComponent.getPreferredSize().height,
+ lrheight);
+ return retDim;
+ }
+
+ public void removeLayoutComponent(Component comp) {
+ // Do nothing
+ }
+}
+
+/**
+ * A ListPair provides a way to display two lists of objects and to move
+ * objects from one list to another. It is initialized with the contents
+ * of each list, and can be queried at any time for the contents of each list
+ */
+public class ListPair extends JPanel {
+ private JList leftList, rightList;
+ private OurListModel leftModel, rightModel;
+ private ListSelectionModel leftSelectionModel, rightSelectionModel;
+ private LeftButton leftButton = new LeftButton();
+ private RightButton rightButton = new RightButton();
+ private JScrollPane leftPane, rightPane;
+
+ /**
+ * Construct a ListPair with the specified data and captions for each list
+ * @param leftCaption Caption for left list
+ * @param leftData An array of objects to display in the left list
+ * @param rightCaption Caption for right list
+ * @param rightData An array of objects to display in the right list
+ */
+ public ListPair(String leftCaption, Object [] leftData, String rightCaption,
+ Object [] rightData) {
+
+ // Use our custom layout manager
+ setLayout(new ListPairLayout());
+
+ // Store data into the list models
+ leftModel = new OurListModel(leftData);
+ rightModel = new OurListModel(rightData);
+
+ // Now create the lists
+ leftList = new JList(leftModel);
+ rightList = new JList(rightModel);
+ leftList.setSelectionModel(new FixedSelectionModel());
+ rightList.setSelectionModel(new FixedSelectionModel());
+
+ // Now do the layout
+ JPanel leftPanel = new JPanel(new BorderLayout());
+
+ JLabel leftCapLbl = new JLabel(leftCaption);
+ leftCapLbl.setLabelFor(leftPanel);
+ leftCapLbl.setToolTipText(leftCaption);
+ leftPanel.add(leftCapLbl, BorderLayout.NORTH);
+
+ leftPane = new JScrollPane(leftList);
+ leftPanel.add(leftPane, BorderLayout.CENTER);
+ add(leftPanel, ListPairLayout.LEFT);
+
+ JPanel centerPanel = new JPanel(new VerticalButtonLayout());
+ rightButton.setEnabled(false);
+ leftButton.setEnabled(false);
+ centerPanel.add(rightButton);
+ centerPanel.add(leftButton);
+ add(centerPanel, ListPairLayout.CENTER);
+
+ JPanel rightPanel = new JPanel(new BorderLayout());
+
+ JLabel rightCapLbl = new JLabel(rightCaption);
+ rightCapLbl.setLabelFor(rightPanel);
+ rightCapLbl.setToolTipText(rightCaption);
+ rightPanel.add(rightCapLbl, BorderLayout.NORTH);
+
+ rightPane = new JScrollPane(rightList);
+ rightPanel.add(rightPane, BorderLayout.CENTER);
+ add(rightPanel, ListPairLayout.RIGHT);
+
+ // Now create and hook up the listeners for selection state
+ leftSelectionModel = leftList.getSelectionModel();
+ leftSelectionModel.addListSelectionListener(
+ new ListSelectionListener() {
+ public void valueChanged(ListSelectionEvent e) {
+ // Ignore if user is dragging selection state
+ if (e.getValueIsAdjusting()) {
+ return;
+ }
+ // Right enabled only if something is selected in left list
+ rightButton.setEnabled(!leftSelectionModel.isSelectionEmpty());
+ if (!leftSelectionModel.isSelectionEmpty()) {
+ rightSelectionModel.clearSelection();
+ }
+ }
+ });
+
+ rightSelectionModel = rightList.getSelectionModel();
+ rightSelectionModel.addListSelectionListener(
+ new ListSelectionListener() {
+ public void valueChanged(ListSelectionEvent e) {
+ // Ignore if user is dragging selection state
+ if (e.getValueIsAdjusting()) {
+ return;
+ }
+ // Left enabled only if something is selected in the right list
+ leftButton.setEnabled(!rightSelectionModel.isSelectionEmpty());
+ if (!rightSelectionModel.isSelectionEmpty()) {
+ leftSelectionModel.clearSelection();
+ }
+ }
+ });
+
+ // Now add listeners to buttons to move data between lists
+ rightButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ Object [] values = leftList.getSelectedValues();
+ for (int i = 0; i < values.length; ++i) {
+ rightModel.addElement(values[i]);
+ leftModel.removeElement(values[i]);
+ }
+ /*
+ * Clear the selection state; this shouldn't be necessary,
+ * but the selection and data models are unfortunately not
+ * hooked up to handle this automatically
+ */
+ leftSelectionModel.clearSelection();
+ }
+ });
+
+ leftButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ Object [] values = rightList.getSelectedValues();
+ for (int i = 0; i < values.length; ++i) {
+ leftModel.addElement(values[i]);
+ rightModel.removeElement(values[i]);
+ }
+ /*
+ * Clear the selection state; this shouldn't be necessary,
+ * but the selection and data models are unfortunately not
+ * hooked up to handle this automatically
+ */
+ rightSelectionModel.clearSelection();
+ }
+ });
+ }
+
+ /**
+ * Retrieve the contents of the left list
+ * @return the contents as an array of Object
+ */
+ public Object [] getLeftContents(Object [] arr) {
+ return leftModel.toArray(arr);
+ }
+
+ /**
+ * Retrieve the contents of the right list
+ * @return the contents as an array of Object
+ */
+ public Object [] getRightContents(Object [] arr) {
+ return rightModel.toArray(arr);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/MainFrame.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/MainFrame.java
new file mode 100644
index 0000000000..6dd8be3996
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/MainFrame.java
@@ -0,0 +1,429 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 1998-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.ui;
+
+import javax.swing.*;
+import javax.swing.event.*;
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+import java.net.URL;
+
+/**
+ * A main window container for an application which glues together multiple sets
+ * of functionality (called Views) into a whole with a single menu bar,
+ * status bar, and search functionality.
+ * @see View
+ */
+public class MainFrame extends JFrame {
+
+ class StatusBar extends JPanel {
+ private JLabel data;
+ private FindPanel finder;
+
+ public StatusBar() {
+ super(new BorderLayout());
+ setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 0));
+ data = new JLabel("", SwingConstants.LEFT);
+ Font f = data.getFont();
+ Font f2 = new Font(f.getName(), Font.PLAIN, f.getSize());
+ data.setFont(f2);
+ data.setForeground(Color.black);
+ data.setLabelFor(this);
+ add(data, BorderLayout.WEST);
+ finder = new FindPanel();
+ add(finder, BorderLayout.EAST);
+ }
+
+ public void setText(String s) {
+ data.setText(s);
+ data.setToolTipText(s);
+ // Force a relayout to avoid truncating text if longer
+ invalidate();
+ validate();
+ }
+
+ public String getText() {
+ return data.getText();
+ }
+ }
+
+ /**
+ * The panel with the Find control
+ */
+ class FindPanel extends JPanel implements ActionListener {
+ private JTextField text;
+ private JButton button;
+
+ public void actionPerformed(ActionEvent e) {
+ activeView.find(text.getText());
+ }
+
+ public FindPanel() {
+ text = new JTextField("", 20);
+
+ Mnemonic mnNext =
+ new Mnemonic(ResourceStrings.getString("next_button"));
+ button = new JButton(mnNext.getString());
+ button.setToolTipText(mnNext.getString());
+ button.setMnemonic(mnNext.getMnemonic());
+
+ button.addActionListener(this);
+ text.addActionListener(this);
+
+ Mnemonic mnFind =
+ new Mnemonic(ResourceStrings.getString("find_label"));
+ JLabel findLbl = new JLabel(mnFind.getString());
+ findLbl.setLabelFor(text);
+ findLbl.setToolTipText(mnFind.getString());
+ findLbl.setDisplayedMnemonic(mnFind.getMnemonic());
+ add(findLbl);
+
+ add(text);
+ add(button);
+ }
+ }
+
+ // Handler for the File->Exit menu item
+ class ExitAction extends MnemonicAction {
+ public ExitAction() {
+ super(ResourceStrings.getString("exit_item"));
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ setVisible(false);
+ dispose();
+ }
+ }
+
+ // Handler for Edit->Create menu item
+ class CreateAction extends MnemonicAction {
+ public CreateAction() {
+ super(ResourceStrings.getString("create_item"));
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ activeView.handleCreate();
+ }
+ }
+
+ // Handler for the Edit->Delete menu item
+ class DeleteAction extends MnemonicAction {
+ public DeleteAction() {
+ super(ResourceStrings.getString("delete_item"));
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ activeView.handleDelete();
+ }
+ }
+
+ // Handler for the Edit->Duplicate menu item
+ class DuplicateAction extends MnemonicAction {
+ public DuplicateAction() {
+ super(ResourceStrings.getString("duplicate_item"));
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ activeView.handleDuplicate();
+ }
+ }
+
+ // Handler for the Edit->Properties menu item
+ class PropertiesAction extends MnemonicAction {
+ public PropertiesAction() {
+ super(ResourceStrings.getString("properties_item"));
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ activeView.handleProperties();
+ }
+ }
+
+ // Handler for the View->Update menu item
+ class UpdateAction extends MnemonicAction {
+ public UpdateAction() {
+ super(ResourceStrings.getString("update_item"));
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ activeView.handleUpdate();
+ }
+ }
+
+ // Listener for selection events from the views
+ class ViewSelectionListener implements SelectionListener {
+ public void valueChanged() {
+ // Set menu item state on edit menu
+ for (int i = 1; i < menuActions[EDIT_MENU].length; ++i) {
+ if (i == 2) {
+ // Duplicate can only be active for a single selection
+ menuActions[EDIT_MENU][i].setEnabled(
+ !activeView.isSelectionEmpty()
+ && !activeView.isSelectionMultiple());
+ } else {
+ menuActions[EDIT_MENU][i].setEnabled(
+ !activeView.isSelectionEmpty());
+ }
+ }
+ }
+ }
+
+ public static final int FILE_MENU = 0;
+ public static final int EDIT_MENU = 1;
+ public static final int VIEW_MENU = 2;
+ public static final int ACTIONS_MENU = 3;
+ public static final int HELP_MENU = 4;
+ public static final int MENU_COUNT = 5;
+ // The set of menus for the application
+ private static String menuKeys[] = { "file_menu", "edit_menu", "view_menu",
+ "actions_menu", "help_menu" };
+ /*
+ * Table of all the menu actions owned by MainFrame; some end up
+ * delegating to the View.
+ */
+ private Action [] [] menuActions = {
+ // File Menu
+ { new ExitAction() },
+
+ // Edit Menu
+ { new CreateAction(), new DeleteAction(), new DuplicateAction(),
+ new PropertiesAction() },
+ // View Menu
+ { new UpdateAction() },
+
+ // Actions Menu
+ { },
+
+ // Help Menu
+ { }
+ };
+ private JMenuBar menuBar;
+ private JMenu menuList[];
+ private static StatusBar statusBar;
+ private Component display = null;
+ private JTabbedPane displayPanel;
+ private View activeView, initialView;
+ private Vector views;
+ private int [] separatorIndex = new int[MENU_COUNT];
+ private ButtonGroup viewButtonGroup = new ButtonGroup();
+ private ViewSelectionListener viewSelectionListener =
+ new ViewSelectionListener();
+ private boolean initialized = false;
+
+ public MainFrame() {
+ this("");
+ }
+
+ public MainFrame(String title) {
+ super(title);
+ views = new Vector();
+ Container contentPane = getContentPane();
+ // Create basic menu structure
+ menuBar = new JMenuBar();
+ menuList = new JMenu[menuKeys.length];
+ // First the menus
+ for (int i = 0; i < menuList.length; ++i) {
+ Mnemonic m = new Mnemonic(ResourceStrings.getString(menuKeys[i]));
+ menuList[i] = (JMenu)menuBar.add(new JMenu(m.getString()));
+ menuList[i].setMnemonic(m.getMnemonic());
+ }
+ // Now the items on each menu
+ for (int i = 0; i < menuActions.length; ++i) {
+ for (int j = 0; j < menuActions[i].length; ++j) {
+ menuList[i].add(menuActions[i][j]);
+ }
+ }
+
+ // separatorIndex will remember where we automatically put separators
+ for (int i = 0; i < MENU_COUNT; ++i) {
+ separatorIndex[i] = -1;
+ }
+
+ setJMenuBar(menuBar);
+ contentPane.setLayout(new BorderLayout());
+
+ // Status bar for messages
+ statusBar = new StatusBar();
+ contentPane.add(statusBar, BorderLayout.SOUTH);
+
+ // Display panel is the area for the view's main display
+ displayPanel = new JTabbedPane();
+ displayPanel.addChangeListener(new ChangeListener() {
+ public void stateChanged(ChangeEvent e) {
+ /*
+ * Prevent premature activation of a view which would otherwise
+ * happen as a byproduct of adding views to the tabbed pane.
+ */
+ if (initialized) {
+ try {
+ setActiveView((View)views.elementAt(
+ displayPanel.getSelectedIndex()));
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
+ }
+ }
+ });
+ contentPane.add(displayPanel, BorderLayout.CENTER);
+ activeView = null;
+ }
+
+ public static String getStatusText() {
+ return statusBar.getText();
+ }
+
+ public static void setStatusText(String text) {
+ statusBar.setText(text);
+ }
+
+ // Add a global menu
+ public void addMenuAction(int menu, Action action) {
+ menuList[menu].add(action);
+ }
+
+ // Add a view to the system
+ public void addView(View v, boolean isInitial) {
+ views.addElement(v);
+ displayPanel.addTab(v.getName(), v.getDisplay());
+ if (isInitial) {
+ initialView = v;
+ }
+
+ /*
+ * Listen to selection events from the view, update menu state
+ * accordingly
+ */
+ v.addSelectionListener(viewSelectionListener);
+ }
+
+ // Delete a view
+ public void deleteView(View v) {
+ views.removeElement(v);
+ /*
+ * If we're deleting the currently active view, need to activate
+ * another one; default to the initial view, unless that is also
+ * the one we're deleting, in which case just pick the first view.
+ */
+ if (v == activeView) {
+ if (v != initialView) {
+ setActiveView(initialView);
+ } else {
+ setActiveView((View)views.firstElement());
+ }
+ }
+ displayPanel.remove(v.getDisplay());
+ v.removeSelectionListener(viewSelectionListener);
+ }
+
+ // Select the view to be shown
+ public void setActiveView(View v) {
+ if (activeView != null) {
+ activeView.setActive(false);
+ }
+ // Remove custom menus from existing active view
+ for (int i = MENU_COUNT + 1; i < menuBar.getMenuCount(); ++i) {
+ menuBar.remove(i);
+ }
+ /*
+ * Remove menu items on standard menus from existing active view,
+ * add those from new view
+ */
+ for (int i = 0; i < menuList.length; ++i) {
+ JMenu m = menuBar.getMenu(i);
+ if (activeView != null) {
+ Enumeration e = activeView.menuItems(i);
+ if (e != null) {
+ if (separatorIndex[i] != -1) {
+ m.remove(separatorIndex[i]);
+ separatorIndex[i] = -1;
+ }
+ while (e.hasMoreElements()) {
+ JMenuItem mi = (JMenuItem)e.nextElement();
+ if (mi != null) {
+ m.remove((Component)mi);
+ }
+ }
+ }
+ }
+ Enumeration e = v.menuItems(i);
+ if (e != null) {
+ while (e.hasMoreElements()) {
+ /*
+ * This test here so separator is only added if we
+ * actually get a menu item from the view, protecting
+ * against an empty enumeration causing a stray separator.
+ */
+ if (separatorIndex[i] == -1) {
+ separatorIndex[i] = m.getItemCount();
+ m.addSeparator();
+ }
+ JMenuItem mi = (JMenuItem)e.nextElement();
+ if (mi != null) {
+ m.add(mi);
+ }
+ }
+ }
+ }
+
+ // Add view's menus
+ Enumeration e = v.menus();
+ while ((e != null) && e.hasMoreElements()) {
+ JMenu m = (JMenu)e.nextElement();
+ if (m != null) {
+ menuBar.add(m);
+ }
+ }
+ activeView = v;
+ activeView.setActive(true);
+ viewSelectionListener.valueChanged();
+ invalidate();
+ validate();
+ }
+
+ // Call this to get things started
+ public void initialize() {
+ if (initialView != null) {
+ setActiveView(initialView);
+ initialized = true;
+ }
+ }
+
+ // Set enabled/disabled state for menus
+ public void setMenuEnabled(int menu, boolean state) {
+ menuList[menu].setEnabled(state);
+ }
+
+ // Refresh all views, including those not currently active
+ public void refreshAllViews() {
+ for (Enumeration en = views.elements(); en.hasMoreElements(); ) {
+ View v = (View)en.nextElement();
+ v.handleUpdate();
+ }
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/Makefile b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/Makefile
new file mode 100644
index 0000000000..c66f436745
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/Makefile
@@ -0,0 +1,107 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 1999-2002 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/Makefile
+#
+
+# Place high-level classes first in order to minimize build time.
+CLASSFILES = ListPair.class \
+ Wizard.class \
+ TableSorter.class \
+ MainFrame.class \
+ View.class \
+ MnemonicAction.class \
+ IPAddressList.class \
+ ExtendedCellRenderer.class \
+ PreviousButton.class \
+ NextButton.class \
+ RightButton.class \
+ LeftButton.class \
+ UpButton.class \
+ DownButton.class \
+ ImageButton.class \
+ ButtonPanel.class \
+ ButtonPanelListener.class \
+ VerticalButtonLayout.class \
+ ButtonLayout.class \
+ ALIGNMENT.class \
+ AutosizingTable.class \
+ FieldLayout.class \
+ HelpIds.class \
+ HostnameField.class \
+ IntegerField.class \
+ IPAddressField.class \
+ Mnemonic.class \
+ NoSpaceField.class \
+ ProgressManager.class \
+ ProportionalLayout.class \
+ ResourceStrings.class \
+ SelectionListener.class \
+ SortedHeaderRenderer.class \
+ SwingWorker.class \
+ TableMap.class \
+ WizardStep.class
+
+include $(SRC)/Makefile.master
+
+CLASSPATH= $(SRC)/cmd/cmd-inet/usr.sadm/dhcpmgr
+
+JAVAFILES = $(CLASSFILES:.class=.java)
+
+MSGDIR= $(ROOT)/usr/share/lib/locale/com/sun/dhcpmgr/ui
+MSGDIRS = $(ROOT)/usr/share/lib/locale \
+ $(ROOT)/usr/share/lib/locale/com \
+ $(ROOT)/usr/share/lib/locale/com/sun \
+ $(ROOT)/usr/share/lib/locale/com/sun/dhcpmgr \
+ $(MSGDIR)
+
+MSGFILES= ResourceBundle.properties
+MSGS= $(MSGFILES:%=$(MSGDIR)/%)
+
+CLEANFILES= *.class
+CLOBBERFILES=
+
+.KEEP_STATE:
+
+all: $(CLASSFILES)
+
+install: all
+
+_msg: $(MSGDIRS) $(MSGS)
+
+$(MSGDIR)/%: %
+ $(INS.file)
+
+$(MSGDIRS):
+ $(INS.dir)
+
+lint:
+
+clean:
+ $(RM) $(CLEANFILES)
+
+clobber: clean
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/Mnemonic.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/Mnemonic.java
new file mode 100644
index 0000000000..4f56eca41c
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/Mnemonic.java
@@ -0,0 +1,62 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2002 by Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.ui;
+
+import java.util.*;
+
+
+public class Mnemonic {
+
+ public String string = "";
+ public int mnemonic = 0;
+
+ public Mnemonic(String locStr) {
+ string = locStr;
+
+ int i = string.indexOf('&');
+ if (i > -1) {
+ String sUpper = string.toUpperCase();
+ mnemonic = sUpper.charAt(i+1);
+ StringBuffer s = new StringBuffer(string);
+ s.delete(i, i+1);
+ string = s.toString();
+ }
+ }
+
+ public String getString() {
+
+ return string;
+
+ } // getString
+
+ public int getMnemonic() {
+
+ return mnemonic;
+
+ } // getMnemonic
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/MnemonicAction.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/MnemonicAction.java
new file mode 100644
index 0000000000..9713ecc7c4
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/MnemonicAction.java
@@ -0,0 +1,45 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.ui;
+
+import javax.swing.AbstractAction;
+
+/**
+ * MnemonicAction is an extended version of AbstractAction which
+ * allows the name and mnemonic for the action to be specified using
+ * the & syntax in a resource string.
+ */
+public abstract class MnemonicAction extends AbstractAction {
+ private Mnemonic m;
+
+ public MnemonicAction(String s) {
+ m = new Mnemonic(s);
+ putValue(NAME, m.getString());
+ putValue(MNEMONIC_KEY, new Integer(m.getMnemonic()));
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/NextButton.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/NextButton.java
new file mode 100644
index 0000000000..b41f95a18e
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/NextButton.java
@@ -0,0 +1,39 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2002 by Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.ui;
+
+/**
+ * Class to produce a button with an icon pointing right (Next)
+ */
+public class NextButton extends ImageButton {
+ public NextButton() {
+ Mnemonic mnText =
+ new Mnemonic(ResourceStrings.getString("next_button"));
+ setImage(getClass(), "next.gif", mnText);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/NoSpaceField.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/NoSpaceField.java
new file mode 100644
index 0000000000..a525bc1bb3
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/NoSpaceField.java
@@ -0,0 +1,88 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1998-1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.ui;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import javax.swing.text.*;
+
+/**
+ * A text field which disallows whitespace in its input
+ */
+public class NoSpaceField extends JTextField {
+
+ /**
+ * Constructs an empty field, 20 characters wide.
+ */
+ public NoSpaceField() {
+ this("");
+ }
+
+ /**
+ * Constructs a field initialized to the provided text. Defaults to
+ * 20 characters wide.
+ * @param text the text to display initially
+ */
+ public NoSpaceField(String text) {
+ this(text, 20);
+ }
+
+ /**
+ * Constructs a field initialized to the provided text with the requested
+ * size.
+ * @param text the text to display initially
+ * @param length the length in characters the field should size itself to
+ */
+ public NoSpaceField(String text, int length) {
+ super(text, length);
+ }
+
+ protected Document createDefaultModel() {
+ return new NoSpaceDocument();
+ }
+
+ /*
+ * This is the recommended way to validate input, as opposed to trapping
+ * KeyEvents because this will actually catch paste operations as well.
+ */
+ static class NoSpaceDocument extends PlainDocument {
+ public void insertString(int offs, String str, AttributeSet a)
+ throws BadLocationException {
+ if (str != null) {
+ char [] chars = str.toCharArray();
+ for (int i = 0; i < chars.length; ++i) {
+ if (Character.isWhitespace(chars[i])) {
+ throw new BadLocationException("", offs);
+ }
+ }
+ }
+ super.insertString(offs, str, a);
+ }
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/PreviousButton.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/PreviousButton.java
new file mode 100644
index 0000000000..bbbaaa389c
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/PreviousButton.java
@@ -0,0 +1,40 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2002 by Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.ui;
+
+/**
+ * A button with an icon pointing left (Previous)
+ */
+public class PreviousButton extends ImageButton {
+
+ public PreviousButton() {
+ Mnemonic mnText =
+ new Mnemonic(ResourceStrings.getString("previous_button"));
+ setImage(getClass(), "back.gif", mnText);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ProgressManager.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ProgressManager.java
new file mode 100644
index 0000000000..bdf064845f
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ProgressManager.java
@@ -0,0 +1,91 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1999-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.ui;
+
+import javax.swing.ProgressMonitor;
+import javax.swing.SwingUtilities;
+import java.awt.Component;
+
+/**
+ * Provides a framework for doing long-running operations and keeping the
+ * user apprised of the results. Also provides an exception if Cancel is
+ * pressed in the progress dialog.
+ */
+public class ProgressManager {
+ private ProgressMonitor monitor;
+ private int count;
+ private String message;
+ private Runnable progressUpdater;
+
+ /**
+ * Create a new ProgressManager; see ProgressMonitor for description
+ * of the parameters here.
+ * @see javax.swing.ProgressMonitor
+ */
+ public ProgressManager(Component comp, Object msg, String note, int min,
+ int max) {
+ // Initialize count so we can auto-increment
+ count = min;
+ monitor = new ProgressMonitor(comp, msg, note, min, max);
+ // Create background object to update monitor
+ progressUpdater = new Runnable() {
+ public void run() {
+ monitor.setProgress(count);
+ monitor.setNote(message);
+ }
+ };
+ }
+
+ /**
+ * Update the progress display. Throws InterruptedException if user
+ * has pressed the Cancel button on the progress dialog
+ * @param progress the amount of the task that has been completed
+ * @param msg the message to be displayed at this time
+ * @throws java.lang.InterruptedException
+ */
+ public void update(int progress, String msg) throws InterruptedException {
+ count = progress;
+ message = msg;
+ SwingUtilities.invokeLater(progressUpdater);
+ if (monitor.isCanceled()) {
+ throw new InterruptedException();
+ }
+ }
+
+ /**
+ * Update the progress display, automatically incrementing the count
+ * by one. Throws InterruptedException if the user has pressed the
+ * Cancel button in the progress dialog
+ * @param msg the message to be display at this time
+ * @throws java.lang.InterruptedException
+ */
+ public void update(String msg) throws InterruptedException {
+ update(count+1, msg);
+ }
+
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ProportionalLayout.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ProportionalLayout.java
new file mode 100644
index 0000000000..d8391e56f5
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ProportionalLayout.java
@@ -0,0 +1,146 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1998-1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.ui;
+
+import java.awt.*;
+import java.util.Hashtable;
+
+/**
+ * This layout manager provides a layout which allocates space either
+ * horizontally or vertically to components in proportion to a weight assigned
+ * to each component. All components receive the same dimension along the
+ * other axis.
+ */
+public class ProportionalLayout implements LayoutManager {
+ /**
+ * Constant for a horizontal proportional layout.
+ */
+ public static final int HORIZONTAL = 0;
+ /**
+ * Constant for a vertical proportional layout.
+ */
+ public static final int VERTICAL = 1;
+ private int direction;
+ private Hashtable components;
+ private int totalWeight = 0;
+
+ /**
+ * Construct a horizontal version of this layout.
+ */
+ public ProportionalLayout() {
+ this(HORIZONTAL);
+ }
+
+ /**
+ * Construct a proportional layout in the direction specified.
+ * @param direction Must be either HORIZONTAL or VERTICAL
+ */
+ public ProportionalLayout(int direction) {
+ this.direction = direction;
+ components = new Hashtable();
+ }
+
+ public void addLayoutComponent(String name, Component component) {
+ Integer weight;
+ try {
+ weight = Integer.decode(name);
+ } catch (NumberFormatException e) {
+ weight = new Integer(1);
+ }
+ totalWeight += weight.intValue();
+ components.put(component, weight);
+ }
+
+ public void removeLayoutComponent(Component component) {
+ Integer weight = (Integer)components.get(component);
+ totalWeight -= weight.intValue();
+ components.remove(component);
+ }
+
+ private Dimension computeLayoutSize(Container target, boolean minimum) {
+ Dimension dim = new Dimension(0, 0);
+ for (int i = 0; i < target.getComponentCount(); ++i) {
+ Component c = target.getComponent(i);
+ if (c.isVisible()) {
+ Dimension d;
+ if (minimum) {
+ d = c.getMinimumSize();
+ } else {
+ d = c.getPreferredSize();
+ }
+ if (direction == HORIZONTAL) {
+ dim.height = Math.max(dim.height, d.height);
+ dim.width += d.width;
+ } else {
+ dim.height += d.height;
+ dim.width = Math.max(dim.width, d.width);
+ }
+ }
+ }
+ Insets insets = target.getInsets();
+ dim.width += insets.left + insets.right;
+ dim.height += insets.top + insets.bottom;
+ return dim;
+ }
+
+ public Dimension preferredLayoutSize(Container target) {
+ return computeLayoutSize(target, false);
+ }
+
+ public Dimension minimumLayoutSize(Container target) {
+ return computeLayoutSize(target, true);
+ }
+
+ public void layoutContainer(Container target) {
+ Insets insets = target.getInsets();
+ Dimension dim = target.getSize();
+ int x = insets.left;
+ int y = insets.top;
+ int totalHeight = dim.height - insets.bottom;
+ int totalWidth = dim.width - insets.right;
+
+ for (int i = 0; i < target.getComponentCount(); ++i) {
+ Component c = target.getComponent(i);
+ if (c.isVisible()) {
+ if (direction == HORIZONTAL) {
+ float fw = (float)totalWidth
+ * (float)((Integer)components.get(c)).intValue()
+ / (float)totalWeight;
+ c.setBounds(x, y, (int)fw, totalHeight);
+ x += (int)fw;
+ } else {
+ float fh = (float)totalHeight
+ * (float)((Integer)components.get(c)).intValue()
+ / (float)totalWeight;
+ c.setBounds(x, y, totalWidth, (int)fh);
+ y += (int)fh;
+ }
+ }
+ }
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ResourceBundle.properties b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ResourceBundle.properties
new file mode 100644
index 0000000000..2893c37952
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ResourceBundle.properties
@@ -0,0 +1,71 @@
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Resource bundle for ui package
+
+file_menu=&File
+edit_menu=&Edit
+view_menu=&View
+actions_menu=&Service
+help_menu=&Help
+
+exit_item=E&xit
+create_item=&Create
+delete_item=&Delete
+duplicate_item=D&uplicate
+properties_item=&Properties
+update_item=&Refresh
+
+ok_button=&OK
+cancel_button=&Cancel
+reset_button=&Reset
+help_button=&Help
+finish_button=&Finish
+
+find_label=F&ind:
+steps_label=&Steps:
+
+never=Never
+
+add=&Add
+delete=De&lete
+move_up=Move Up
+move_down=Move Down
+
+# Added for accessibility compliance
+dhcp_server_address=DHCP Server Address
+
+# Arrow buttons used in wizards, chooser dialogs
+# and up, down buttons. NB: Next is used also in
+# the dhcpmgr main dialog.
+left_button=Remo&ve
+right_button=&Add
+up_button=&Up
+down_button=Do&wn
+next_button=&Next
+previous_button=&Back
+
+
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ResourceStrings.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ResourceStrings.java
new file mode 100644
index 0000000000..1934ea2397
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ResourceStrings.java
@@ -0,0 +1,64 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1998-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.ui;
+
+import java.util.*;
+
+/**
+ * This class provides a convenient method to retrieve resources for
+ * the ui package.
+ */
+public class ResourceStrings {
+
+ /**
+ * The handle to the resource bundle for the module.
+ */
+ private static ResourceBundle bundle = null;
+
+ /**
+ * Return a string from the resource bundle.
+ * @param key the key to the resource bundle string.
+ * @return the resource bundle string.
+ */
+ public static String getString(String key) {
+ String msg = null;
+ try {
+ if (bundle == null) {
+ bundle = ResourceBundle.getBundle(
+ "com.sun.dhcpmgr.ui.ResourceBundle",
+ Locale.getDefault());
+ }
+ msg = bundle.getString(key);
+ } catch (Throwable e) {
+ msg = new String(key);
+ }
+ return msg;
+
+ } // getString
+
+} // ResourceStrings
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/RightButton.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/RightButton.java
new file mode 100644
index 0000000000..a9cda099ec
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/RightButton.java
@@ -0,0 +1,39 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 1998-2002 by Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.ui;
+
+/**
+ * Class to produce a button with an icon pointing right
+ */
+public class RightButton extends ImageButton {
+ public RightButton() {
+ Mnemonic mnText =
+ new Mnemonic(ResourceStrings.getString("right_button"));
+ setImage(getClass(), "next.gif", mnText);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/SelectionListener.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/SelectionListener.java
new file mode 100644
index 0000000000..f6c0e722fc
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/SelectionListener.java
@@ -0,0 +1,34 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1998-1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.ui;
+
+import java.util.EventListener;
+
+public interface SelectionListener extends EventListener {
+ public void valueChanged();
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/SortedHeaderRenderer.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/SortedHeaderRenderer.java
new file mode 100644
index 0000000000..39ef5e627d
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/SortedHeaderRenderer.java
@@ -0,0 +1,55 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1998-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.ui;
+
+import javax.swing.JTable;
+import javax.swing.table.TableCellRenderer;
+import java.awt.Font;
+import java.awt.Component;
+
+/**
+ * This class provides the functionality to indicate to the user which column
+ * a table is currently being sorted on. At present it merely uses a bold
+ * font to display the column name, and does not indicate the collation order.
+ */
+public class SortedHeaderRenderer implements TableCellRenderer {
+ TableCellRenderer renderer;
+
+ public SortedHeaderRenderer(JTable table) {
+ renderer = table.getTableHeader().getDefaultRenderer();
+ }
+
+ public Component getTableCellRendererComponent(JTable table, Object value,
+ boolean isSelected, boolean hasFocus, int row, int column) {
+ Component c = renderer.getTableCellRendererComponent(table, value,
+ isSelected, hasFocus, row, column);
+ Font f = c.getFont();
+ c.setFont(new Font(f.getName(), Font.BOLD, f.getSize()));
+ return c;
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/SwingWorker.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/SwingWorker.java
new file mode 100644
index 0000000000..b8166787ec
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/SwingWorker.java
@@ -0,0 +1,108 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1998-1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.ui;
+
+import javax.swing.SwingUtilities;
+
+/**
+ * An abstract class that you subclass to perform
+ * GUI-related work in a dedicated thread.
+ * For instructions on using this class, see
+ * http://java.sun.com/products/jfc/swingdoc-current/threads2.html
+ */
+public abstract class SwingWorker {
+ private Object value;
+ private Thread thread;
+
+ /**
+ * Compute the value to be returned by the <code>get</code> method.
+ */
+ public abstract Object construct();
+
+ /**
+ * Called on the event dispatching thread (not on the worker thread)
+ * after the <code>construct</code> method has returned.
+ */
+ public void finished() {
+ }
+
+ /**
+ * A new method that interrupts the worker thread. Call this method
+ * to force the worker to abort what it's doing.
+ */
+ public void interrupt() {
+ Thread t = thread;
+ if (t != null) {
+ t.interrupt();
+ }
+ thread = null;
+ }
+
+ /**
+ * Return the value created by the <code>construct</code> method.
+ */
+ public Object get() {
+ while (true) { // keep trying if we're interrupted
+ Thread t;
+ synchronized (SwingWorker.this) {
+ t = thread;
+ if (t == null) {
+ return value;
+ }
+ }
+ try {
+ t.join();
+ }
+ catch (InterruptedException e) {
+ }
+ }
+ }
+
+ /**
+ * Start a thread that will call the <code>construct</code> method
+ * and then exit.
+ */
+ public SwingWorker() {
+ final Runnable doFinished = new Runnable() {
+ public void run() { finished(); }
+ };
+
+ Runnable doConstruct = new Runnable() {
+ public void run() {
+ synchronized (SwingWorker.this) {
+ value = construct();
+ thread = null;
+ }
+ SwingUtilities.invokeLater(doFinished);
+ }
+ };
+
+ thread = new Thread(doConstruct);
+ thread.start();
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/TableMap.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/TableMap.java
new file mode 100644
index 0000000000..8a6846ee91
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/TableMap.java
@@ -0,0 +1,101 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2000 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * ident "%Z%%M% %I% %E% SMI"
+ */
+package com.sun.dhcpmgr.ui;
+
+/*
+ * @(#)TableMap.java 1.4 97/12/17
+ */
+
+/**
+ * In a chain of data manipulators some behaviour is common. TableMap
+ * provides most of this behavour and can be subclassed by filters
+ * that only need to override a handful of specific methods. TableMap
+ * implements TableModel by routing all requests to its model, and
+ * TableModelListener by routing all events to its listeners. Inserting
+ * a TableMap which has not been subclassed into a chain of table filters
+ * should have no effect.
+ *
+ * @version 1.4 12/17/97
+ * @author Philip Milne */
+
+import javax.swing.table.*;
+import javax.swing.event.TableModelListener;
+import javax.swing.event.TableModelEvent;
+
+public class TableMap extends AbstractTableModel implements TableModelListener
+{
+ protected TableModel model;
+
+ public TableModel getModel() {
+ return model;
+ }
+
+ public void setModel(TableModel model) {
+ this.model = model;
+ model.addTableModelListener(this);
+ }
+
+ // By default, Implement TableModel by forwarding all messages
+ // to the model.
+
+ public Object getValueAt(int aRow, int aColumn) {
+ return model.getValueAt(aRow, aColumn);
+ }
+
+ public void setValueAt(Object aValue, int aRow, int aColumn) {
+ model.setValueAt(aValue, aRow, aColumn);
+ }
+
+ public int getRowCount() {
+ return (model == null) ? 0 : model.getRowCount();
+ }
+
+ public int getColumnCount() {
+ return (model == null) ? 0 : model.getColumnCount();
+ }
+
+ public String getColumnName(int aColumn) {
+ return model.getColumnName(aColumn);
+ }
+
+ public Class getColumnClass(int aColumn) {
+ return model.getColumnClass(aColumn);
+ }
+
+ public boolean isCellEditable(int row, int column) {
+ return model.isCellEditable(row, column);
+ }
+//
+// Implementation of the TableModelListener interface,
+//
+
+ // By default forward all events to all the listeners.
+ public void tableChanged(TableModelEvent e) {
+ fireTableChanged(e);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/TableSorter.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/TableSorter.java
new file mode 100644
index 0000000000..3e9645655e
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/TableSorter.java
@@ -0,0 +1,409 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2000 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * ident "%Z%%M% %I% %E% SMI"
+ */
+package com.sun.dhcpmgr.ui;
+
+/*
+ * @(#)TableSorter.java 1.5 97/12/17
+ */
+
+/**
+ * A sorter for TableModels. The sorter has a model (conforming to TableModel)
+ * and itself implements TableModel. TableSorter does not store or copy
+ * the data in the TableModel, instead it maintains an array of
+ * integers which it keeps the same size as the number of rows in its
+ * model. When the model changes it notifies the sorter that something
+ * has changed eg. "rowsAdded" so that its internal array of integers
+ * can be reallocated. As requests are made of the sorter (like
+ * getValueAt(row, col) it redirects them to its model via the mapping
+ * array. That way the TableSorter appears to hold another copy of the table
+ * with the rows in a different order. The sorting algorthm used is stable
+ * which means that it does not move around rows when its comparison
+ * function returns 0 to denote that they are equivalent.
+ *
+ * @version 1.5 12/17/97
+ * @author Philip Milne
+ */
+
+import java.util.*;
+
+import javax.swing.table.TableModel;
+import javax.swing.event.TableModelEvent;
+
+import com.sun.dhcpmgr.data.IPAddress;
+
+// Imports for picking up mouse events from the JTable.
+
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.InputEvent;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import javax.swing.JTable;
+import javax.swing.table.JTableHeader;
+import javax.swing.table.TableColumn;
+import javax.swing.table.TableColumnModel;
+
+public class TableSorter extends TableMap
+{
+ int indexes[] = new int[0];
+ Vector sortingColumns = new Vector();
+ boolean ascending = true;
+ int compares;
+ Vector listeners = new Vector();
+
+ public TableSorter() {
+ indexes = new int[0]; // For consistency.
+ }
+
+ public TableSorter(TableModel model) {
+ setModel(model);
+ }
+
+ public void setModel(TableModel model) {
+ super.setModel(model);
+ reallocateIndexes();
+ }
+
+ public int compareRowsByColumn(int row1, int row2, int column) {
+ Class type = model.getColumnClass(column);
+ TableModel data = model;
+
+ // Check for nulls
+
+ Object o1 = data.getValueAt(row1, column);
+ Object o2 = data.getValueAt(row2, column);
+
+ // If both values are null return 0
+ if (o1 == null && o2 == null) {
+ return 0;
+ } else if (o1 == null) { // Define null less than everything.
+ return -1;
+ } else if (o2 == null) {
+ return 1;
+ }
+
+ /*
+ * We copy all returned values from the getValue call in case
+ * an optimised model is reusing one object to return many values.
+ * The Number subclasses in the JDK are immutable and so will not be
+ * used in this way but other subclasses of Number might want to do
+ * this to save space and avoid unnecessary heap allocation.
+ */
+ if (type.getSuperclass() == java.lang.Number.class) {
+ Number n1 = (Number)data.getValueAt(row1, column);
+ double d1 = n1.doubleValue();
+ Number n2 = (Number)data.getValueAt(row2, column);
+ double d2 = n2.doubleValue();
+
+ if (d1 < d2)
+ return -1;
+ else if (d1 > d2)
+ return 1;
+ else
+ return 0;
+ } else if (type == java.util.Date.class) {
+ Date d1 = (Date)data.getValueAt(row1, column);
+ long n1 = d1.getTime();
+ Date d2 = (Date)data.getValueAt(row2, column);
+ long n2 = d2.getTime();
+
+ // Handle negatives specially
+ if (n1 < 0) {
+ return 1;
+ } else if (n2 < 0) {
+ return -1;
+ }
+ if (n1 < n2)
+ return -1;
+ else if (n1 > n2)
+ return 1;
+ else
+ return 0;
+ } else if (type == String.class) {
+ String s1 = (String)data.getValueAt(row1, column);
+ String s2 = (String)data.getValueAt(row2, column);
+ int result = s1.compareTo(s2);
+
+ if (result < 0)
+ return -1;
+ else if (result > 0)
+ return 1;
+ else return 0;
+ } else if (type == Boolean.class) {
+ Boolean bool1 = (Boolean)data.getValueAt(row1, column);
+ boolean b1 = bool1.booleanValue();
+ Boolean bool2 = (Boolean)data.getValueAt(row2, column);
+ boolean b2 = bool2.booleanValue();
+
+ if (b1 == b2)
+ return 0;
+ else if (b1) // Define false < true
+ return 1;
+ else
+ return -1;
+ } else if (type == IPAddress.class) {
+ IPAddress addr1 = (IPAddress)data.getValueAt(row1, column);
+ IPAddress addr2 = (IPAddress)data.getValueAt(row2, column);
+ byte [] a1 = addr1.getAddress();
+ byte [] a2 = addr2.getAddress();
+ int c1, c2;
+ for (int i = 0; i < a1.length; ++i) {
+ /*
+ * Promote and mask because bytes are signed and 128-255
+ * will be done wrong
+ */
+ c1 = a1[i] & 0xff;
+ c2 = a2[i] & 0xff;
+ if (c1 < c2) {
+ return -1;
+ } else if (c1 > c2) {
+ return 1;
+ }
+ }
+ return 0;
+ } else {
+ Object v1 = data.getValueAt(row1, column);
+ String s1 = v1.toString();
+ Object v2 = data.getValueAt(row2, column);
+ String s2 = v2.toString();
+ int result = s1.compareTo(s2);
+
+ if (result < 0)
+ return -1;
+ else if (result > 0)
+ return 1;
+ else return 0;
+ }
+ }
+
+ public int compare(int row1, int row2) {
+ compares++;
+ for (int level = 0; level < sortingColumns.size(); level++) {
+ Integer column = (Integer)sortingColumns.elementAt(level);
+ int result = compareRowsByColumn(row1, row2, column.intValue());
+ if (result != 0)
+ return ascending ? result : -result;
+ }
+ return 0;
+ }
+
+ public void reallocateIndexes() {
+ int rowCount = model.getRowCount();
+
+ // Set up a new array of indexes with the right number of elements
+ // for the new data model.
+ indexes = new int[rowCount];
+
+ // Initialise with the identity mapping.
+ for (int row = 0; row < rowCount; row++)
+ indexes[row] = row;
+ }
+
+ public void tableChanged(TableModelEvent e) {
+ // System.out.println("Sorter: tableChanged");
+ reallocateIndexes();
+ sort(this);
+ super.tableChanged(e);
+ }
+
+ public void checkModel() {
+ if (indexes.length != model.getRowCount()) {
+ System.err.println("Sorter not informed of a change in model.");
+ }
+ }
+
+ public void sort(Object sender) {
+ checkModel();
+
+ compares = 0;
+ // n2sort();
+ // qsort(0, indexes.length-1);
+ shuttlesort((int[])indexes.clone(), indexes, 0, indexes.length);
+ // System.out.println("Compares: "+compares);
+ }
+
+ public void n2sort() {
+ for (int i = 0; i < getRowCount(); i++) {
+ for (int j = i+1; j < getRowCount(); j++) {
+ if (compare(indexes[i], indexes[j]) == -1) {
+ swap(i, j);
+ }
+ }
+ }
+ }
+
+ /*
+ * This is a home-grown implementation which we have not had time
+ * to research - it may perform poorly in some circumstances. It
+ * requires twice the space of an in-place algorithm and makes
+ * NlogN assigments shuttling the values between the two
+ * arrays. The number of compares appears to vary between N-1 and
+ * NlogN depending on the initial order but the main reason for
+ * using it here is that, unlike qsort, it is stable.
+ */
+ public void shuttlesort(int from[], int to[], int low, int high) {
+ if (high - low < 2) {
+ return;
+ }
+ int middle = (low + high)/2;
+ shuttlesort(to, from, low, middle);
+ shuttlesort(to, from, middle, high);
+
+ int p = low;
+ int q = middle;
+
+ /*
+ * This is an optional short-cut; at each recursive call,
+ * check to see if the elements in this subset are already
+ * ordered. If so, no further comparisons are needed; the
+ * sub-array can just be copied. The array must be copied rather
+ * than assigned otherwise sister calls in the recursion might
+ * get out of sinc. When the number of elements is three they
+ * are partitioned so that the first set, [low, mid), has one
+ * element and and the second, [mid, high), has two. We skip the
+ * optimisation when the number of elements is three or less as
+ * the first compare in the normal merge will produce the same
+ * sequence of steps. This optimisation seems to be worthwhile
+ * for partially ordered lists but some analysis is needed to
+ * find out how the performance drops to Nlog(N) as the initial
+ * order diminishes - it may drop very quickly.
+ */
+
+ if (high - low >= 4 && compare(from[middle-1], from[middle]) <= 0) {
+ for (int i = low; i < high; i++) {
+ to[i] = from[i];
+ }
+ return;
+ }
+
+ // A normal merge.
+
+ for (int i = low; i < high; i++) {
+ if (q >= high || (p < middle && compare(from[p], from[q]) <= 0)) {
+ to[i] = from[p++];
+ } else {
+ to[i] = from[q++];
+ }
+ }
+ }
+
+ public void swap(int i, int j) {
+ int tmp = indexes[i];
+ indexes[i] = indexes[j];
+ indexes[j] = tmp;
+ }
+
+ /*
+ * The mapping only affects the contents of the data rows.
+ * Pass all requests to these rows through the mapping array: "indexes".
+ */
+
+ public Object getValueAt(int aRow, int aColumn)
+ {
+ checkModel();
+ return model.getValueAt(indexes[aRow], aColumn);
+ }
+
+ public int mapRowAt(int aRow) {
+ checkModel();
+ if (aRow < indexes.length) {
+ return indexes[aRow];
+ } else {
+ return -1;
+ }
+ }
+
+ public void setValueAt(Object aValue, int aRow, int aColumn) {
+ checkModel();
+ model.setValueAt(aValue, indexes[aRow], aColumn);
+ }
+
+ public void sortByColumn(int column) {
+ // Re-sort on this column, but don't change sort order
+ sortByColumn(column, this.ascending);
+ }
+
+ public void sortByColumn(int column, boolean ascending) {
+ this.ascending = ascending;
+ sortingColumns.removeAllElements();
+ sortingColumns.addElement(new Integer(column));
+ sort(this);
+ super.tableChanged(new TableModelEvent(this));
+ fireActionPerformed();
+ }
+
+ /*
+ * There is no-where else to put this.
+ * Add a mouse listener to the Table to trigger a table sort
+ * when a column heading is clicked in the JTable.
+ */
+ public void addMouseListenerToHeaderInTable(JTable table) {
+ final TableSorter sorter = this;
+ final JTable tableView = table;
+ tableView.setColumnSelectionAllowed(false);
+ MouseAdapter listMouseListener = new MouseAdapter() {
+ public void mouseClicked(MouseEvent e) {
+ TableColumnModel columnModel = tableView.getColumnModel();
+ int viewColumn = columnModel.getColumnIndexAtX(e.getX());
+ int column = tableView.convertColumnIndexToModel(viewColumn);
+ if (e.getClickCount() == 1 && column != -1) {
+ // System.out.println("Sorting ...");
+ int shiftPressed = e.getModifiers()&InputEvent.SHIFT_MASK;
+ boolean ascending = (shiftPressed == 0);
+ sorter.sortByColumn(column, ascending);
+ }
+ }
+ };
+ JTableHeader th = tableView.getTableHeader();
+ th.addMouseListener(listMouseListener);
+ }
+
+ // Allow others to be notified when re-sorting is done
+ public void addActionListener(ActionListener l) {
+ listeners.addElement(l);
+ }
+
+ // Take me off the notify list
+ public void removeActionListener(ActionListener l) {
+ listeners.removeElement(l);
+ }
+
+ /*
+ * Notify listeners of sort events; we just use ActionEvent as it's a
+ * good all-purpose event
+ */
+ protected void fireActionPerformed() {
+ ActionEvent e = new ActionEvent(this, ActionEvent.ACTION_PERFORMED,
+ sortingColumns.firstElement().toString());
+ Enumeration en = listeners.elements();
+ while (en.hasMoreElements()) {
+ ActionListener l = (ActionListener)en.nextElement();
+ l.actionPerformed(e);
+ }
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/UpButton.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/UpButton.java
new file mode 100644
index 0000000000..ebf342974a
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/UpButton.java
@@ -0,0 +1,39 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 2002 by Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.ui;
+
+/**
+ * Class to produce a button with an icon pointing up.
+ */
+public class UpButton extends ImageButton {
+ public UpButton() {
+ Mnemonic mnText =
+ new Mnemonic(ResourceStrings.getString("up_button"));
+ setImage(getClass(), "up.gif", mnText);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/VerticalButtonLayout.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/VerticalButtonLayout.java
new file mode 100644
index 0000000000..bb5ea247e3
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/VerticalButtonLayout.java
@@ -0,0 +1,227 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1996-1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.ui;
+
+import java.awt.*;
+
+/**
+ * <CODE>VerticalButtonLayout</CODE> is used to layout buttons in a
+ * <CODE>Panel</CODE>. It will arrange buttons top to bottom
+ * until no more buttons fit in the same column. Each column is
+ * centered vertically. All buttons are set to an equal size.<P>
+ *
+ * While <CODE>VerticalButtonLayout</CODE> was designed for
+ * <CODE>Buttons</CODE>, any component can be added to the layout. All
+ * components are set to an equal size.<P>
+ */
+public class VerticalButtonLayout implements LayoutManager {
+
+ ALIGNMENT align;
+ int hgap;
+ int vgap;
+
+ /**
+ * Constructs a new <CODE>VerticalButtonLayout</CODE> with a centered
+ * alignment.
+ */
+ public VerticalButtonLayout() {
+ this(ALIGNMENT.CENTER, 5, 5);
+ }
+
+ /**
+ * Constructs a new <CODE>VerticalButtonLayout</CODE> with the specified
+ * alignment.
+ * @param <VAR>align</VAR> The alignment value.
+ * @see ALIGNMENT
+ */
+ public VerticalButtonLayout(ALIGNMENT align) {
+ this(align, 5, 5);
+ }
+
+ /**
+ * Constructs a new <CODE>VerticalButtonLayout</CODE> with the specified
+ * alignment and gap values.
+ * @param <VAR>align</VAR> The alignment value.
+ * @param <VAR>hgap</VAR> The horizontal gap variable.
+ * @param <VAR>vgap</VAR> The vertical gap variable.
+ * @see ALIGNMENT
+ */
+ public VerticalButtonLayout(ALIGNMENT align, int hgap, int vgap) {
+ this.align = align;
+ this.hgap = hgap;
+ this.vgap = vgap;
+ }
+
+ /**
+ * Adds the specified component to the layout. This is not
+ * used by this class.
+ * @param <VAR>name</VAR> The name of the component.
+ * @param <VAR>comp</VAR> The component to be added.
+ */
+ public void addLayoutComponent(String name, Component comp) {
+ }
+
+ /**
+ * Removes the specified component from the layout. This
+ * is not used by this class.
+ * @param <VAR>comp</VAR> The component to remove.
+ */
+ public void removeLayoutComponent(Component comp) {
+ }
+
+ /**
+ * Returns the preferred dimensions for this layout given
+ * the components in the specified target container.
+ * @param <VAR>target</VAR> The component that needs to be laid out.
+ * @see java.awt.Container
+ * @see #minimumLayoutSize
+ */
+ public Dimension preferredLayoutSize(Container target) {
+ Dimension dim = new Dimension(0, 0);
+ int nmembers = target.getComponentCount();
+
+ for (int i = 0; i < nmembers; i++) {
+ Component m = target.getComponent(i);
+ if (m.isVisible()) {
+ Dimension d = m.getPreferredSize();
+ dim.height = Math.max(dim.height, d.height);
+ dim.width = Math.max(dim.width, d.width);
+ }
+ }
+ dim.height = (dim.height*nmembers) + (vgap*nmembers-1);
+ Insets insets = target.getInsets();
+ dim.width += insets.left + insets.right + hgap*2;
+ dim.height += insets.top + insets.bottom + vgap*2;
+ return dim;
+ }
+
+ /**
+ * Returns the minimum dimensions needed to layout the components
+ * contained in the specified target container.
+ * @param <VAR>target</VAR> The component that needs to be laid out
+ * @see #preferredLayoutSize
+ */
+ public Dimension minimumLayoutSize(Container target) {
+ Dimension dim = new Dimension(0, 0);
+ int nmembers = target.getComponentCount();
+
+ for (int i = 0; i < nmembers; i++) {
+ Component m = target.getComponent(i);
+ if (m.isVisible()) {
+ Dimension d = m.getMinimumSize();
+ dim.height = Math.max(dim.height, d.height);
+ dim.width = Math.max(dim.width, d.width);
+ }
+ }
+ dim.height = (dim.height*nmembers) + (vgap*nmembers-1);
+ Insets insets = target.getInsets();
+ dim.width += insets.left + insets.right + hgap*2;
+ dim.height += insets.top + insets.bottom + vgap*2;
+ return dim;
+ }
+
+ /**
+ * Centers the elements in the specified column, if there is any slack.
+ * @param <VAR>target</VAR> The component which needs to be moved.
+ * @param <VAR>x</VAR> The x coordinate.
+ * @param <VAR>y</VAR> The y coordinate.
+ * @param <VAR>width</VAR> The width dimensions.
+ * @param <VAR>height</VAR> The height dimensions.
+ */
+ private void moveComponents(Container target, int x, int y, int width,
+ int height) {
+ Dimension dim;
+
+ if (align == ALIGNMENT.LEFT) {
+ // do nothing
+ } else if (align == ALIGNMENT.CENTER) {
+ y += height / 2;
+ } else if (align == ALIGNMENT.RIGHT) {
+ y += height;
+ }
+ for (int i = 0; i < target.getComponentCount(); i++) {
+ Component m = target.getComponent(i);
+ if (m.isVisible()) {
+ dim = m.getSize();
+ m.setLocation(x + (width - dim.width) / 2, y);
+ y += vgap + dim.height;
+ }
+ }
+ }
+
+ /**
+ * Lays out the container. This method will actually reshape the
+ * components in the target in order to satisfy the constraints of
+ * the <CODE>BorderLayout</CODE> object.
+ * @param <VAR>target</VAR> The specified component being laid out.
+ * @see java.awt.Container
+ */
+ public void layoutContainer(Container target) {
+ Insets insets = target.getInsets();
+ Dimension tdim = target.getSize();
+ int maxheight = tdim.height - (insets.top + insets.bottom + vgap*2);
+ int maxwidth = tdim.width - insets.left - insets.right;
+ int nmembers = target.getComponentCount();
+ Dimension dim = new Dimension(0, 0);
+ int y = 0;
+
+ for (int i = 0; i < nmembers; i++) {
+ Component m = target.getComponent(i);
+ if (m.isVisible()) {
+ Dimension d = m.getMinimumSize();
+ dim.height = Math.max(dim.height, d.height);
+ dim.width = Math.max(dim.width, d.width);
+ }
+ }
+ for (int i = 0; i < nmembers; i++) {
+ Component m = target.getComponent(i);
+ if (m.isVisible()) {
+ m.setSize(dim.width, dim.height);
+ y += dim.height;
+ }
+ }
+ moveComponents(target, insets.left, 0, maxwidth, maxheight - y);
+ }
+
+ /**
+ * Returns the <CODE>String</CODE> representation of this
+ * <CODE>VerticalButtonLayout</CODE>'s values.
+ */
+ public String toString() {
+ String str = "";
+ if (align == ALIGNMENT.LEFT) {
+ str = ",align=left";
+ } else if (align == ALIGNMENT.RIGHT) {
+ str = ",align=right";
+ } else if (align == ALIGNMENT.CENTER) {
+ str = ",align=center";
+ }
+ return getClass().getName()
+ + "[hgap=" + hgap + ",vgap=" + vgap + str + "]";
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/View.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/View.java
new file mode 100644
index 0000000000..33292b81d5
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/View.java
@@ -0,0 +1,118 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1998-1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.ui;
+
+import java.util.Enumeration;
+import java.awt.Component;
+
+/**
+ * View is the interface implemented by objects which wish to appear as tabs in
+ * the main window. Each View is given a tab in the tabbed display managed by
+ * MainFrame.
+ * @see MainFrame
+ */
+public interface View {
+ /**
+ * Supply the view's display name, which will be the tab's title.
+ * @return a short name to describe the view
+ */
+ public String getName();
+ /**
+ * the menus the view wishes to add to the interface.
+ * @return an enumeration of JMenus
+ */
+ public Enumeration menus();
+ /**
+ * the menu items to be added to a specific menu
+ * @return an enumeration of JMenuItems
+ */
+ public Enumeration menuItems(int menu);
+ /**
+ * the display to be shown for this view. It will occupy the entire tab.
+ * @return a component to display
+ */
+ public Component getDisplay();
+ /**
+ * view is to search for the next occurrence of the supplied string and
+ * update its display accordingly.
+ * @param s the string to search for
+ */
+ public void find(String s);
+ /**
+ * notification to view that it has been activated or deactivated.
+ * Views may wish to update their display state at this time.
+ * @param state true if view is now active, false if now inactive
+ */
+ public void setActive(boolean state);
+ /**
+ * user has selected Edit->Create menu item. View should provide an
+ * interface to create an instance of its primary object type.
+ */
+ public void handleCreate();
+ /**
+ * user has selected Edit->Delete menu item. View should attempt to delete
+ * any selected objects, probably with a confirmation notice.
+ */
+ public void handleDelete();
+ /**
+ * user has selected Edit->Duplicate menu item. View should provide an
+ * interface which creates a new object with attributes similar to the
+ * currently selected object.
+ */
+ public void handleDuplicate();
+ /**
+ * user has selected Edit->Properties menu item. View should provide an
+ * interface to modify the properties of the selected item.
+ */
+ public void handleProperties();
+ /**
+ * user has selected View->Refresh menu item. View should make its display
+ * current.
+ */
+ public void handleUpdate();
+ /**
+ * add a listener for selection events.
+ * @param listener a SelectionListener
+ */
+ public void addSelectionListener(SelectionListener listener);
+ /**
+ * remove a listener for selection events.
+ * @param listener a SelectionListener
+ */
+ public void removeSelectionListener(SelectionListener listener);
+ /**
+ * listeners query to ascertain whether selection state is empty.
+ * @return true if no objects are selected
+ */
+ public boolean isSelectionEmpty();
+ /**
+ * listeners query to ascertain whether selection state is multiple.
+ * @return true if multiple objects selected
+ */
+ public boolean isSelectionMultiple();
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/Wizard.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/Wizard.java
new file mode 100644
index 0000000000..cbabd4ea6c
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/Wizard.java
@@ -0,0 +1,678 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright 1998-2002 by Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+package com.sun.dhcpmgr.ui;
+
+import javax.swing.*;
+import javax.swing.border.*;
+
+import java.awt.event.*;
+import java.awt.*;
+import java.util.*;
+import java.text.NumberFormat;
+
+// Interface for notification of button events
+interface WizButtonListener {
+ public void buttonPressed(int buttonId);
+ public static final int BACK = 0;
+ public static final int FORWARD = 1;
+ public static final int CANCEL = 2;
+ public static final int HELP = 3;
+ public static final int FINISH = 4;
+}
+
+/**
+ * This is a widget for presenting a multi-step task, such as an installation
+ * sequence or other task with many questions or multiple possible paths. This
+ * component displays as a dialog, with a contents outline on the left and a
+ * panel for display of each step on the right, with a set of control buttons
+ * at the bottom. Typical usage is to subclass this class and implement a set
+ * of WizardStep classes which provide the steps to be executed.
+ *
+ * @see WizardStep
+ */
+public class Wizard extends JDialog {
+
+ // Panel to manage display and processing of buttons at base of window
+ class WizButtonPanel extends JPanel {
+
+ // Adaptor class to catch all button presses and pass along to listeners
+ class ButtonAdaptor implements ActionListener {
+ public void actionPerformed(ActionEvent e) {
+ int buttonId = -1;
+ Object source = e.getSource();
+ if (source == backButton || source == backButton2) {
+ buttonId = WizButtonListener.BACK;
+ } else if (source == forwardButton) {
+ buttonId = WizButtonListener.FORWARD;
+ } else if (source == cancelButton || source == cancelButton2) {
+ buttonId = WizButtonListener.CANCEL;
+ } else if (source == helpButton || source == helpButton2) {
+ buttonId = WizButtonListener.HELP;
+ } else if (source == finishButton) {
+ buttonId = WizButtonListener.FINISH;
+ }
+ Enumeration en = listeners.elements();
+ while (en.hasMoreElements()) {
+ WizButtonListener l = (WizButtonListener)en.nextElement();
+ l.buttonPressed(buttonId);
+ }
+ }
+ }
+
+ PreviousButton backButton, backButton2;
+ NextButton forwardButton;
+ JButton cancelButton, helpButton, finishButton;
+ JButton cancelButton2, helpButton2;
+ Vector listeners;
+ ButtonAdaptor adaptor;
+ JPanel innerPanel;
+
+ public WizButtonPanel() {
+ super();
+
+ setBorder(new EtchedBorder());
+ // Right justify the buttons
+ setLayout(new FlowLayout(FlowLayout.RIGHT));
+ innerPanel = new JPanel(new CardLayout());
+
+ // Create event handler
+ adaptor = new ButtonAdaptor();
+ listeners = new Vector();
+
+ /*
+ * Construct back buttons; need 2 for the separate cards used
+ * in the button panel
+ */
+ backButton = new PreviousButton();
+ backButton2 = new PreviousButton();
+ backButton.addActionListener(adaptor);
+ backButton2.addActionListener(adaptor);
+ backButton.setAlignmentY(Component.CENTER_ALIGNMENT);
+ backButton2.setAlignmentY(Component.CENTER_ALIGNMENT);
+
+ // Construct forward button
+ forwardButton = new NextButton();
+ forwardButton.addActionListener(adaptor);
+ forwardButton.setAlignmentY(Component.CENTER_ALIGNMENT);
+
+ Mnemonic mnFinish =
+ new Mnemonic(ResourceStrings.getString("finish_button"));
+ finishButton = new JButton(mnFinish.getString());
+ finishButton.setToolTipText(mnFinish.getString());
+ finishButton.setMnemonic(mnFinish.getMnemonic());
+ finishButton.addActionListener(adaptor);
+ finishButton.setAlignmentY(Component.CENTER_ALIGNMENT);
+
+ Mnemonic mnCancel =
+ new Mnemonic(ResourceStrings.getString("cancel_button"));
+ cancelButton = new JButton(mnCancel.getString());
+ cancelButton.setToolTipText(mnCancel.getString());
+ cancelButton.setMnemonic(mnCancel.getMnemonic());
+
+ cancelButton.addActionListener(adaptor);
+ cancelButton.setAlignmentY(Component.CENTER_ALIGNMENT);
+
+ cancelButton2 = new JButton(mnCancel.getString());
+ cancelButton2.setToolTipText(mnCancel.getString());
+ cancelButton2.setMnemonic(mnCancel.getMnemonic());
+
+ cancelButton2.addActionListener(adaptor);
+ cancelButton2.setAlignmentY(Component.CENTER_ALIGNMENT);
+
+ Mnemonic mnHelp =
+ new Mnemonic(ResourceStrings.getString("help_button"));
+ helpButton = new JButton(mnHelp.getString());
+ helpButton.setToolTipText(mnHelp.getString());
+ helpButton.setMnemonic(mnHelp.getMnemonic());
+
+ helpButton.addActionListener(adaptor);
+ helpButton.setAlignmentY(Component.CENTER_ALIGNMENT);
+
+ helpButton2 = new JButton(mnHelp.getString());
+ helpButton2.setToolTipText(mnHelp.getString());
+ helpButton2.setMnemonic(mnHelp.getMnemonic());
+
+ helpButton2.addActionListener(adaptor);
+ helpButton2.setAlignmentY(Component.CENTER_ALIGNMENT);
+
+ /*
+ * Now create cards; we created two copies of buttons that
+ * needed to be on both cards
+ */
+ Box box = Box.createHorizontalBox();
+ box.add(Box.createHorizontalGlue());
+ box.add(backButton);
+ box.add(Box.createHorizontalStrut(5));
+ box.add(forwardButton);
+ box.add(Box.createHorizontalStrut(5));
+ box.add(cancelButton);
+ box.add(Box.createHorizontalStrut(5));
+ box.add(helpButton);
+ innerPanel.add(box, "normal");
+
+ // Finish panel replaces the forward button with the finish button
+ box = Box.createHorizontalBox();
+ box.add(Box.createHorizontalGlue());
+ box.add(backButton2);
+ box.add(Box.createHorizontalStrut(5));
+ box.add(finishButton);
+ box.add(Box.createHorizontalStrut(5));
+ box.add(cancelButton2);
+ box.add(Box.createHorizontalStrut(5));
+ box.add(helpButton2);
+ innerPanel.add(box, "finish");
+
+ add(innerPanel);
+ }
+
+ // Show the first step
+ public void showFirst() {
+ backButton.setEnabled(false); // Can't go backwards
+ setForwardEnabled(false);
+ getRootPane().setDefaultButton(forwardButton);
+ forwardButton.requestFocus(true);
+ ((CardLayout)innerPanel.getLayout()).show(innerPanel, "normal");
+ }
+
+ // Show the last step
+ public void showLast() {
+ backButton.setEnabled(true);
+ setFinishEnabled(false);
+ getRootPane().setDefaultButton(finishButton);
+ finishButton.requestFocus(true);
+ ((CardLayout)innerPanel.getLayout()).show(innerPanel, "finish");
+ }
+
+ // Show any other step
+ public void showMiddle() {
+ backButton.setEnabled(true);
+ setForwardEnabled(false);
+ getRootPane().setDefaultButton(forwardButton);
+ cancelButton.requestFocus(true);
+ ((CardLayout)innerPanel.getLayout()).show(innerPanel, "normal");
+ }
+
+ // Allow steps to control when user may advance to next step
+ public void setForwardEnabled(boolean state) {
+ forwardButton.setEnabled(state);
+ }
+
+ // Allow the final step to control when a user may complete the task
+ public void setFinishEnabled(boolean state) {
+ finishButton.setEnabled(state);
+ }
+
+ public void addWizButtonListener(WizButtonListener l) {
+ listeners.addElement(l);
+ }
+
+ public void removeWizButtonListener(WizButtonListener l) {
+ listeners.removeElement(l);
+ }
+ }
+
+ /*
+ * Panel to display the list of steps; we use a very custom JList
+ * for this as it does reasonable rendering with minimal effort on our part.
+ */
+ class WizContentsPanel extends JPanel {
+
+ // Data model for holding the description text displayed
+ class ContentsModel extends AbstractListModel {
+ Vector data;
+
+ public ContentsModel() {
+ data = new Vector();
+ }
+
+ public Object getElementAt(int index) {
+ return data.elementAt(index);
+ }
+
+ public int getSize() {
+ return data.size();
+ }
+
+ public void addItem(String description) {
+ data.addElement(description);
+ fireIntervalAdded(this, data.size()-1, data.size()-1);
+ }
+ }
+
+ // Class to render the cells in the list
+ class ContentsRenderer implements ListCellRenderer {
+ NumberFormat nf;
+
+ public ContentsRenderer() {
+ nf = NumberFormat.getInstance();
+ }
+
+ public Component getListCellRendererComponent(JList list,
+ Object value, int index, boolean isSelected,
+ boolean cellHasFocus) {
+
+ // Format label properly for i18n
+ JTextArea text = new JTextArea((String)value, 2, 15);
+ text.setWrapStyleWord(true);
+ text.setLineWrap(true);
+ text.setOpaque(false);
+ text.setAlignmentY(Component.TOP_ALIGNMENT);
+ JLabel l = new JLabel(nf.format(index + 1));
+ l.setForeground(Color.black);
+ l.setAlignmentY(Component.TOP_ALIGNMENT);
+
+ JPanel stepBox = new JPanel();
+ stepBox.setLayout(new BoxLayout(stepBox, BoxLayout.X_AXIS));
+ stepBox.add(l);
+ stepBox.add(Box.createHorizontalStrut(5));
+ stepBox.add(text);
+ stepBox.setBackground(list.getSelectionBackground());
+ // Selected component is opaque, others transparent
+ stepBox.setOpaque(isSelected);
+ return stepBox;
+ }
+ }
+
+ /*
+ * This class is defined strictly so that we can prevent
+ * focus from ever reaching the steps list as it is a display-only
+ * use of JList, not intended for any sort of input by the user.
+ */
+ class MyList extends JList {
+ public MyList(ListModel m) {
+ super(m);
+ // Don't allow this list to be focused
+ setFocusable(false);
+ }
+ // Ignore mouse clicks so highlighted step can't be changed
+ protected void processMouseEvent(MouseEvent e) {
+ return;
+ }
+ // Ignore mouse drags, which can also change highlighting
+ protected void processMouseMotionEvent(MouseEvent e) {
+ return;
+ }
+ }
+
+ MyList contentsList;
+ ContentsModel model;
+
+ public WizContentsPanel() {
+ setBorder(BorderFactory.createEmptyBorder(10, 5, 10, 10));
+ setBackground(Color.white);
+ setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
+
+ Mnemonic mnSteps =
+ new Mnemonic(ResourceStrings.getString("steps_label"));
+ JLabel l = new JLabel(mnSteps.getString());
+ l.setLabelFor(this);
+ l.setToolTipText(mnSteps.getString());
+ l.setDisplayedMnemonic(mnSteps.getMnemonic());
+
+ l.setForeground(Color.black);
+ l.setAlignmentX(Component.LEFT_ALIGNMENT);
+ add(l);
+
+ model = new ContentsModel();
+ contentsList = new MyList(model);
+ contentsList.setCellRenderer(new ContentsRenderer());
+ /*
+ * Wrap the list with scroll bars, vertical as necessary but
+ * never a horizontal one.
+ */
+ JScrollPane scrollPane = new JScrollPane(contentsList,
+ ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
+ ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
+ scrollPane.setBorder(BorderFactory.createEmptyBorder(15, 10, 0, 0));
+ scrollPane.setAlignmentX(Component.LEFT_ALIGNMENT);
+ scrollPane.setBackground(Color.white);
+ add(scrollPane);
+ add(Box.createVerticalGlue());
+ }
+
+ public void addStep(WizardStep step) {
+ // Index the steps using the description string
+ model.addItem(step.getDescription());
+ }
+
+ public void showStep(WizardStep step) {
+ contentsList.setSelectedValue(step.getDescription(), true);
+ }
+ }
+
+ /*
+ * This class provides a simple card layout to display each step as needed.
+ */
+ class WizStepPanel extends JPanel {
+ CardLayout layout;
+
+ public WizStepPanel() {
+ setLayout(layout = new CardLayout());
+ // Make sure we have some space around the edges.
+ setBorder(BorderFactory.createEmptyBorder(15, 10, 10, 10));
+ }
+
+ public void addStep(WizardStep step) {
+ add(step.getComponent(), step.getDescription());
+ }
+
+ public void showStep(WizardStep step) {
+ layout.show(this, step.getDescription());
+ }
+ }
+
+ WizButtonPanel buttonPanel;
+ WizContentsPanel contentsPanel;
+ WizStepPanel stepPanel;
+ Vector steps;
+ WizardStep activeStep = null;
+ int stepNumber = 0;
+ Vector listeners;
+
+ /**
+ * Constructs a wizard with the specified owning frame and title.
+ *
+ * @param owner the owning frame
+ * @param title the wizard's title string
+ */
+ public Wizard(Frame owner, String title) {
+ super(owner, title);
+
+ setLocationRelativeTo(owner);
+
+ getContentPane().setLayout(new BorderLayout());
+ steps = new Vector();
+ listeners = new Vector();
+
+ // Put the buttons at the bottom.
+ buttonPanel = new WizButtonPanel();
+ getContentPane().add(buttonPanel, BorderLayout.SOUTH);
+
+ /*
+ * The main display is designed to use a 7:11 ratio for the horizontal
+ * area consumed by the contents panel and the display panel,
+ * respectively. There's nothing particularly magical about the
+ * ratio, that's just the way the designer drew it.
+ */
+ JPanel mainPanel = new JPanel(new ProportionalLayout());
+
+ contentsPanel = new WizContentsPanel();
+ mainPanel.add(contentsPanel, "7");
+
+ stepPanel = new WizStepPanel();
+ mainPanel.add(stepPanel, "11");
+
+ // Consume all space not needed for buttons
+ getContentPane().add(mainPanel, BorderLayout.CENTER);
+
+ /*
+ * We manage the button interactions, but the steps get limited veto
+ * power
+ */
+ buttonPanel.addWizButtonListener(new WizButtonListener() {
+ public void buttonPressed(int buttonId) {
+ switch (buttonId) {
+ case BACK:
+ showPreviousStep();
+ break;
+ case FORWARD:
+ showNextStep();
+ break;
+ case FINISH:
+ doFinish();
+ break;
+ case CANCEL:
+ doCancel();
+ break;
+ case HELP:
+ doHelp();
+ break;
+ default:
+ }
+ }
+ });
+ }
+
+ /**
+ * Override of setVisible to control size when displayed. Perhaps this
+ * should be relaxed, but subclasses can always do whatever they want.
+ * @param state make visible or not?
+ */
+ public void setVisible(boolean state) {
+ if (state) {
+ setSize(525, 425);
+ }
+ super.setVisible(state);
+ }
+
+ /**
+ * Adds a step to the wizard. Steps <bold>must</bold> be added in the
+ * sequence they will be displayed when traversing forward.
+ * @param step a <code>WizardStep</code>
+ */
+ public void addStep(WizardStep step) {
+ steps.addElement(step);
+ contentsPanel.addStep(step);
+ stepPanel.addStep(step);
+ }
+
+ private boolean showStep(WizardStep step, int direction) {
+ // Deactivate currently active step
+ if (activeStep != null) {
+ if (!activeStep.setInactive(direction)) {
+ // Step vetoed its deactivation. We honor its wishes.
+ return false;
+ }
+ }
+ /*
+ * Activate new step by updating contents, display area, and possibly
+ * buttons
+ */
+ activeStep = step;
+ contentsPanel.showStep(step);
+ stepPanel.showStep(step);
+ if (step == steps.firstElement()) {
+ buttonPanel.showFirst();
+ } else if (step == steps.lastElement()) {
+ buttonPanel.showLast();
+ } else {
+ buttonPanel.showMiddle();
+ }
+ activeStep.setActive(direction);
+ return true;
+ }
+
+ // Show some arbitrary step indexed by number.
+ private boolean showStep(int index, int direction)
+ throws ArrayIndexOutOfBoundsException {
+ WizardStep ws = (WizardStep)steps.elementAt(index);
+ return showStep(ws, direction);
+ }
+
+ /**
+ * Show the very first step.
+ */
+ public void showFirstStep() {
+ stepNumber = 0;
+ showStep(stepNumber, WizardStep.FORWARD);
+ }
+
+ /**
+ * Show the next step.
+ */
+ public void showNextStep() {
+ ++stepNumber;
+ try {
+ // Handle step vetoing deactivation
+ if (!showStep(stepNumber, WizardStep.FORWARD)) {
+ --stepNumber;
+ }
+ } catch (ArrayIndexOutOfBoundsException e) {
+ --stepNumber;
+ }
+ }
+
+ /**
+ * Show the previous step.
+ */
+ public void showPreviousStep() {
+ --stepNumber;
+ try {
+ if (!showStep(stepNumber, WizardStep.BACKWARD)) {
+ ++stepNumber;
+ }
+ } catch (ArrayIndexOutOfBoundsException e) {
+ ++stepNumber;
+ }
+ }
+
+ /**
+ * Show the last step.
+ */
+ public void showLastStep() {
+ int saveStep = stepNumber;
+ stepNumber = steps.size()-1;
+ try {
+ if (!showStep(stepNumber, WizardStep.FORWARD)) {
+ stepNumber = saveStep;
+ }
+ } catch (ArrayIndexOutOfBoundsException e) {
+ stepNumber = saveStep;
+ }
+ }
+
+ /**
+ * Control state of the forward button.
+ * @param state <code>true</code> to enable the button
+ */
+ public void setForwardEnabled(boolean state) {
+ buttonPanel.setForwardEnabled(state);
+ }
+
+ /**
+ * Control state of the finish button.
+ * @param state <code>true</code> to enable the button
+ */
+ public void setFinishEnabled(boolean state) {
+ buttonPanel.setFinishEnabled(state);
+ }
+
+ /**
+ * Handle user's press of the Cancel button. Subclasses can override for
+ * special cleanup needs.
+ */
+ public void doCancel() {
+ fireActionPerformed("cancelled");
+ dispose();
+ }
+
+ /**
+ * Handle user's press of the Finish button. Subclasses should override
+ * to perform whatever processing needed to complete the wizard's task.
+ */
+ public void doFinish() {
+ fireActionPerformed("finished");
+ dispose();
+ }
+
+ /**
+ * Handle user's press of the Help button. Does nothing by default,
+ * subclasses can override to provide help as desired.
+ */
+ public void doHelp() {
+ }
+
+ /**
+ * Utility function to create a multi-line text display such as for the
+ * explanatory text that most wizard steps use.
+ * @param text the text to display
+ * @param rows the number of rows to use for displaying the text
+ * @param columns the number of columns to wrap text at. 45 is generally a
+ * good number for standard wizards with standard fonts
+ * @return a <code>JComponent</code> displaying the supplied text
+ */
+ public static JComponent createTextArea(
+ String text, int rows, int columns) {
+
+ // We extend JTextArea in order to make this behave more like a label
+ class MyTextArea extends JTextArea {
+ public MyTextArea(String text, int rows, int columns) {
+ /*
+ * Create a text area with word-wrapping, no editing,
+ * and no background.
+ */
+ super(text, rows, columns);
+ setLineWrap(true);
+ setWrapStyleWord(true);
+ setEditable(false);
+ setOpaque(false);
+ setFocusable(false);
+ }
+ }
+
+ MyTextArea area = new MyTextArea(text, rows, columns);
+
+ // Put it in a scrollpane to get sizing to happen
+ JScrollPane scrollPane = new JScrollPane(area,
+ ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER,
+ ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
+
+ // Put empty borders on the subcomponents so that all we get is text
+ Border b = BorderFactory.createEmptyBorder();
+ area.setBorder(b);
+ scrollPane.setBorder(b);
+ return scrollPane;
+ }
+
+ /**
+ * Add a listener for action events. Wizard fires an
+ * <code>ActionEvent</code> when user either cancels or finishes the wizard.
+ */
+ public void addActionListener(ActionListener l) {
+ listeners.addElement(l);
+ }
+
+ /**
+ * Remove an action listener.
+ */
+ public void removeActionListener(ActionListener l) {
+ listeners.removeElement(l);
+ }
+
+ /**
+ * Fire an action event.
+ */
+ protected void fireActionPerformed(String command) {
+ ActionEvent e = new ActionEvent(this, ActionEvent.ACTION_PERFORMED,
+ command);
+ for (Enumeration en = listeners.elements(); en.hasMoreElements(); ) {
+ ActionListener l = (ActionListener)en.nextElement();
+ l.actionPerformed(e);
+ }
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/WizardStep.java b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/WizardStep.java
new file mode 100644
index 0000000000..45a3af697f
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/WizardStep.java
@@ -0,0 +1,80 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ *
+ * Copyright (c) 1998-1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+package com.sun.dhcpmgr.ui;
+
+import java.awt.Component;
+
+/**
+ * The interface implemented by a step in a wizard.
+ *
+ * @see Wizard
+ */
+public interface WizardStep {
+ /**
+ * Constant for traversing forward.
+ */
+ public static final int FORWARD = 1;
+ /**
+ * Constant for traversing backward.
+ */
+ public static final int BACKWARD = -1;
+ /**
+ * Returns the descriptive text which will be displayed for this step in the
+ * contents pane (i.e. the left side of the wizard). The description must
+ * be unique for each step used in a wizard.
+ * @return a <code>String</code> describing the step
+ */
+ public String getDescription();
+ /**
+ * Returns the component to be displayed when the step is active. Usually
+ * this will be some form of a panel. This is displayed in the right half
+ * of the wizard's display area.
+ * @return a <code>Component</code> to display
+ */
+ public Component getComponent();
+ /**
+ * Called when the step transitions from the inactive to active state.
+ * Use the direction to determine which direction user is traversing to
+ * possibly differentiate the initialization actions needed.
+ * @param direction either <code>FORWARD</code> or <code>BACKWARD</code> to
+ * indicate direction of traversal.
+ */
+ public void setActive(int direction);
+ /**
+ * Called when the step transitions from the active to inactive state.
+ * Use the direction to determine which direction user is traversing to
+ * differentiate the input processing needed. Return false if there is a
+ * problem the user needs to correct before being allowed to proceed to the
+ * next step.
+ * @param direction either <code>FORWARD</code> or <code>BACKWARD</code> to
+ * indicate direction of traversal.
+ * @return <code>true</code> if traversal should proceed,
+ * <code>false</code> if not.
+ */
+ public boolean setInactive(int direction);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/back.gif b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/back.gif
new file mode 100644
index 0000000000..cf4492d4a9
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/back.gif
Binary files differ
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/down.gif b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/down.gif
new file mode 100644
index 0000000000..8f6b6fcb37
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/down.gif
Binary files differ
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/next.gif b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/next.gif
new file mode 100644
index 0000000000..3bc001f36c
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/next.gif
Binary files differ
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/up.gif b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/up.gif
new file mode 100644
index 0000000000..86cbb96df6
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/up.gif
Binary files differ
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/Makefile b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/Makefile
new file mode 100644
index 0000000000..1b0dff5898
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/Makefile
@@ -0,0 +1,136 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%W% %E% SMI"
+#
+# Copyright (c) 1999-2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+# cmd/cmd-inet/usr.sadm/dhcpmgr/help/Makefile
+
+
+include $(SRC)/cmd/Makefile.cmd
+
+SUBDIRS = art
+
+all := TARGET= all
+install := TARGET= install
+clean := TARGET= clean
+clobber := TARGET= clobber
+lint := TARGET= lint
+_msg := TARGET= _msg
+
+HELPFILES= dhcp_addr_create.html \
+ dhcp_addr_del.html \
+ dhcp_addr_dup.html \
+ dhcp_addr_how.html \
+ dhcp_addr_mod.html \
+ dhcp_addr_multi.html \
+ dhcp_addr_ref.html \
+ dhcp_addr_rel.html \
+ dhcp_addr_view.html \
+ dhcp_addr_wiz.html \
+ dhcp_config_wiz.html \
+ dhcp_convert_wiz.html \
+ dhcp_export_wiz.html \
+ dhcp_import_wiz.html \
+ dhcp_macro_create.html \
+ dhcp_macro_del.html \
+ dhcp_macro_dup.html \
+ dhcp_macro_how.html \
+ dhcp_macro_mod.html \
+ dhcp_macro_ref.html \
+ dhcp_macro_view.html \
+ dhcp_macros_about.html \
+ dhcp_main_hlp.html \
+ dhcp_main_how.html \
+ dhcp_main_idx.html \
+ dhcp_main_menus.html \
+ dhcp_main_top.html \
+ dhcp_net_del.html \
+ dhcp_net_ref.html \
+ dhcp_net_wiz.html \
+ dhcp_option_create.html \
+ dhcp_option_del.html \
+ dhcp_option_dup.html \
+ dhcp_option_how.html \
+ dhcp_option_mod.html \
+ dhcp_option_ref.html \
+ dhcp_option_tags.html \
+ dhcp_option_view.html \
+ dhcp_relay_choose.html \
+ dhcp_relay_config.html \
+ dhcp_relay_dis.html \
+ dhcp_relay_enable.html \
+ dhcp_relay_how.html \
+ dhcp_relay_ref.html \
+ dhcp_relay_serv.html \
+ dhcp_relay_unconfig.html \
+ dhcp_server_serv.html \
+ dhcp_server_unconfig.html \
+ dhcp_solaris_about.html
+
+ROOTHELPDIR= $(ROOT)/usr/sadm/admin/dhcpmgr/help
+
+ROOTDIRS= $(ROOTHELPDIR)
+
+IHELPFILES= $(HELPFILES:%=$(ROOTHELPDIR)/%)
+
+MSGDIR= $(ROOT)/usr/share/lib/locale/com/sun/dhcpmgr/client/help
+MSGDIRS = $(ROOT)/usr/share/lib/locale \
+ $(ROOT)/usr/share/lib/locale/com \
+ $(ROOT)/usr/share/lib/locale/com/sun \
+ $(ROOT)/usr/share/lib/locale/com/sun/dhcpmgr \
+ $(ROOT)/usr/share/lib/locale/com/sun/dhcpmgr/client \
+ $(MSGDIR)
+
+MSGFILES= $(HELPFILES)
+MSGS= $(MSGFILES:%=$(MSGDIR)/%)
+
+FILEMODE= 0444
+
+$(ROOTHELPDIR)/%: %
+ $(INS.file)
+
+.KEEP_STATE:
+
+all: $(HELPFILES) $(SUBDIRS)
+
+install: all $(ROOTDIRS) $(IHELPFILES) $(SUBDIRS)
+
+clean clobber lint:
+
+_msg: $(MSGDIRS) $(MSGS) $(SUBDIRS)
+
+$(MSGDIR)/%: %
+ $(INS.file)
+
+$(MSGDIRS):
+ $(INS.dir)
+
+$(ROOTDIRS):
+ $(INS.dir)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/art/Makefile b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/art/Makefile
new file mode 100644
index 0000000000..9131e7dd4f
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/art/Makefile
@@ -0,0 +1,81 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%W% %E% SMI"
+#
+# Copyright (c) 1999 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+# cmd/cmd-inet/usr.sadm/dhcpmgr/help/art/Makefile
+
+include $(SRC)/cmd/Makefile.cmd
+
+HELPFILES= bannersmc.gif \
+ dot1.gif \
+ dot2.gif \
+ folder.gif \
+ macro2.gif \
+ macroflow.gif \
+ tip2.gif
+
+ROOTHELPDIR= $(ROOT)/usr/sadm/admin/dhcpmgr/help/art
+
+ROOTDIRS= $(ROOTHELPDIR)
+
+IHELPFILES= $(HELPFILES:%=$(ROOTHELPDIR)/%)
+
+MSGDIR= $(ROOT)/usr/share/lib/locale/com/sun/dhcpmgr/client/help/art
+MSGDIRS = $(ROOT)/usr/share/lib/locale \
+ $(ROOT)/usr/share/lib/locale/com \
+ $(ROOT)/usr/share/lib/locale/com/sun \
+ $(ROOT)/usr/share/lib/locale/com/sun/dhcpmgr \
+ $(ROOT)/usr/share/lib/locale/com/sun/dhcpmgr/client \
+ $(ROOT)/usr/share/lib/locale/com/sun/dhcpmgr/client/help \
+ $(MSGDIR)
+
+MSGFILES= $(HELPFILES)
+MSGS= $(MSGFILES:%=$(MSGDIR)/%)
+
+FILEMODE= 0444
+
+$(ROOTHELPDIR)/%: %
+ $(INS.file)
+
+.KEEP_STATE:
+
+all: $(HELPFILES)
+
+install: all $(ROOTDIRS) $(IHELPFILES)
+
+$(ROOTDIRS):
+ $(INS.dir)
+
+clean clobber lint:
+
+_msg: $(MSGDIRS) $(MSGS)
+
+$(MSGDIR)/%: %
+ $(INS.file)
+
+$(MSGDIRS):
+ $(INS.dir)
+
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/art/bannersmc.gif b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/art/bannersmc.gif
new file mode 100644
index 0000000000..7a02477953
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/art/bannersmc.gif
Binary files differ
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/art/dot1.gif b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/art/dot1.gif
new file mode 100644
index 0000000000..726e2105b7
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/art/dot1.gif
Binary files differ
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/art/dot2.gif b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/art/dot2.gif
new file mode 100644
index 0000000000..e60df401b0
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/art/dot2.gif
Binary files differ
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/art/folder.gif b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/art/folder.gif
new file mode 100644
index 0000000000..6e5d77cdb2
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/art/folder.gif
Binary files differ
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/art/macro2.gif b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/art/macro2.gif
new file mode 100644
index 0000000000..ee1a6d5374
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/art/macro2.gif
Binary files differ
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/art/macroflow.gif b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/art/macroflow.gif
new file mode 100644
index 0000000000..54f0a3c249
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/art/macroflow.gif
Binary files differ
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/art/tip2.gif b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/art/tip2.gif
new file mode 100644
index 0000000000..34f6c24155
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/art/tip2.gif
Binary files differ
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_addr_create.html b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_addr_create.html
new file mode 100644
index 0000000000..9a42649124
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_addr_create.html
@@ -0,0 +1,235 @@
+<!--
+ -- ident "%W% %E% SMI"
+ --
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ -- Copyright (c) 2000-2001 by Sun Microsystems, Inc.
+ -- All rights reserved.
+ -->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<html>
+
+<head>
+<title>DHCP Manager Help: Create Address</title>
+<meta NAME="AUTHOR" CONTENT="smorgan">
+<meta NAME="KEYWORDS" CONTENT="DHCP">
+
+</head>
+
+<body bgcolor="#FFFFFF">
+
+<a name="top"></a>
+
+<table border=0 cellspacing=0 cellpadding=0 width=615>
+
+<!-- Start navigation banner -->
+
+<tr><td colspan=4 WIDTH="615" align="center" valign="top"><IMG SRC="art/bannersmc.gif" WIDTH=615 BORDER=0 ALT="DHCP Manager Help">
+
+ </td>
+ </tr>
+
+<!-- End navigation banner -->
+
+
+<tr>
+
+<!-- Start contents block -->
+
+ <td colspan=1 valign="top" width=105>
+ <P>&nbsp;</P>
+ <A HREF="dhcp_main_top.html"><STRONG>Overview</STRONG></A><P>
+ <A HREF="dhcp_relay_ref.html"><STRONG>Servers and Relay Agents</STRONG></A><P>
+<A HREF="dhcp_addr_ref.html"><STRONG>Addresses</STRONG></A><BR>
+ <IMG SRC="art/tip2.gif" WIDTH=12 HEIGHT=10 BORDER=0 ALT="">Create<BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#ip"><EM>Address</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#name"><EM>Name</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#server"><EM>Server</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#macro"><EM>Macro</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#comment"><EM>Comment</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#cid"><EM>Client ID</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#res"><EM>Reserved</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#lease"><EM>Lease</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#bootp"><EM>BOOTP</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#unusable"><EM>Unusable</EM></a><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_addr_wiz.html">Address Wizard</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_addr_dup.html">Duplicate</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_addr_mod.html">Modify</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_addr_multi.html">Modify Multiple</A><BR>
+
+ &nbsp;&nbsp;<A HREF="dhcp_addr_rel.html">Release</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_addr_del.html">Delete</A><P>
+
+<A HREF="dhcp_macro_ref.html"><STRONG>Macros</STRONG></A><P>
+<A HREF="dhcp_option_ref.html"><STRONG>Options</STRONG></A><P>
+
+<A HREF="dhcp_main_how.html"><STRONG>How To..</STRONG></A><P>
+<A HREF="dhcp_main_menus.html"><STRONG>Menus</STRONG></A><P>
+<A HREF="dhcp_main_idx.html"><STRONG>Index</STRONG></A>
+
+ </td>
+
+<!-- End contents block -->
+
+<!-- Start column rule -->
+
+ <td colspan=1 width=5 bgcolor="#CCCCCC">&nbsp;</td>
+
+<!-- End column spacer -->
+
+<!-- Start column spacer -->
+
+ <td colspan=1 width=10 bgcolor="#FFFFFF">&nbsp;</td>
+
+<!-- End column spacer -->
+
+
+
+<!-- Start topic block -->
+
+<td colspan=1 valign="top" bgcolor="#ffffff">
+
+
+
+<P>&nbsp;</P>
+
+
+<h1>Create Address</h1>
+
+The Create Address dialog box lets you create a new DHCP client entry in the selected
+DHCP network. <p>
+Use the <A HREF="dhcp_addr_wiz.html">Address Wizard</A> to add a block of addresses to the DHCP network. The Address Wizard is available from the Edit menu.<P>
+The settings available in the Create Address dialog box are described below.<p>
+<P><HR NOSHADE><P>
+
+ <table border=0 cellspacing=4 cellpadding=3>
+
+ <tr>
+ <td width=100 valign="top"><a name="ip"><STRONG>IP Address</STRONG></a></td>
+ <td valign="top">Specify the IP address to make available to new
+ DHCP clients. The new address must be unique in the current DHCP network table.</td>
+ </tr>
+
+
+ <tr>
+ <TD WIDTH="100" ALIGN="LEFT" VALIGN="TOP"><A NAME="name"><STRONG>Client Name</STRONG></A> (optional)</td>
+ <td valign="top">Specify the host name of the client. If you use this option, the DHCP Manager will
+attempt to create an entry in the <tt>hosts</tt>
+table.</td>
+ </tr>
+
+ <tr>
+ <TD WIDTH="100" ALIGN="LEFT" VALIGN="TOP"><a name="server"><STRONG>Owned By Server</STRONG></a></td>
+ <td valign="top">Specify the IP address of the DHCP server
+ that owns the new IP address. This server is
+ responsible for initially responding to the DHCP client's request for
+ IP address allocation.</td>
+ </tr>
+
+
+ <tr>
+ <TD WIDTH="100" ALIGN="LEFT" VALIGN="TOP"><a name="macro"><STRONG>Configuration Macro</STRONG></a></td>
+ <td valign="top">Specify an optional macro for the server to
+ use to obtain client configuration parameters from the <tt>dhcptab</tt>. Click the arrow to see a list of macros available on the
+ server. If the macro you want to specify does not already exist on the server, you
+ can create it, using the <a href="dhcp_macro_create.html">Create Macro</A>
+ dialog box.</td>
+ </tr>
+
+ <tr>
+ <TD WIDTH="100" ALIGN="LEFT" VALIGN="TOP"><a name="comment"><STRONG>Comment</STRONG> (optional)</a></td>
+ <td valign="top">Enter an optional text comment.</td>
+ </tr>
+
+
+</table>
+
+
+<P><HR NOSHADE><P>
+
+
+Click Lease to configure the setting for lease status and expiration.
+The following settings are available.
+
+
+ <table border=0 cellspacing=4 cellpadding=3>
+
+ <tr>
+
+ <TD WIDTH="100" ALIGN="LEFT" VALIGN="TOP"><a name="cid"><STRONG>Client ID</STRONG></a></td>
+<td valign="top">A unique indentifier of the DHCP or BOOTP client using this address. The client ID is determined by the vendor of the DHCP client. For non-Solaris DHCP clients, consult your DHCP client documentation for more information.<P> For Solaris DHCP clients, the client ID is a string created by the concatenation of the network hardware type and the client's hardware address. For example, a client with a hardware type of <tt>01</tt> (10-Mb Ethernet) and a hardware address of <tt>8:0:20:11:12:b7</tt> would have a client ID of <tt>010800201112B7</tt>.
+<p>
+ An entry with a Client ID value of 00 indicates that the address is
+ freely available for dynamic allocation to a requesting client.
+ <p></td>
+ </tr>
+
+ <TR>
+ <TD WIDTH="100" ALIGN="LEFT" VALIGN="TOP"><a name="res"><STRONG>Reserved</STRONG></a></td>
+ <TD>The address is reserved for this client and cannot be reassigned
+ by the server.</TD>
+ </tr>
+ <TR>
+ <TD WIDTH="100" ALIGN="LEFT" VALIGN="TOP"><a name="lease"><STRONG>Lease Policy</STRONG></a></td>
+ <td valign="top">
+ <U>Dynamic:</U> The DHCP server is responsible for the allocation
+ and re-allocation of this range of IP addresses. <P>
+ Enter the expiration time of the lease. You can specify an
+ expiration date for the lease in mm/dd/yyyy format.<P>
+ <U>Permanent:</U> The lease does not expire.
+ </td>
+ </tr>
+
+
+
+ <tr>
+ <TD WIDTH="100" ALIGN="LEFT" VALIGN="TOP"><A NAME="bootp"><STRONG>Assign Only to BOOTP Clients</STRONG></A></td>
+ <td valign="top">The selected IP address is reserved for
+ BOOTP clients.</td>
+ </tr>
+
+ <tr>
+ <TD WIDTH="100" ALIGN="LEFT" VALIGN="TOP"><A NAME="unusable"><STRONG>Address is Unusable</STRONG></A></td>
+ <td valign="top">This IP address cannot be used. You can use this setting together
+ with Reserved to prevent a client from booting.</td>
+ </tr>
+
+ </table>
+
+<p>&nbsp;</p>
+
+&nbsp;&nbsp;&nbsp;<A HREF="#top"><small>return to top</small></A>
+
+<!-- Don't go past this line! -->
+
+ </td>
+
+<!-- End topic block -->
+
+</tr>
+
+</table>
+
+
+<!-- End topic table -->
+
+</body>
+</html>
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_addr_del.html b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_addr_del.html
new file mode 100644
index 0000000000..0310e91024
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_addr_del.html
@@ -0,0 +1,137 @@
+<!--
+ -- ident "%W% %E% SMI"
+ --
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ -- Copyright (c) 2000-2001 by Sun Microsystems, Inc.
+ -- All rights reserved.
+ -->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<html>
+
+<head>
+<title>DHCP Manager Help: Delete Address</title>
+<meta NAME="AUTHOR" CONTENT="smorgan">
+<meta NAME="KEYWORDS" CONTENT="DHCP">
+
+</head>
+
+<body bgcolor="#FFFFFF">
+
+<a name="top"></a>
+
+<table border=0 cellspacing=0 cellpadding=0 width=615>
+
+<!-- Start navigation banner -->
+
+<tr><td colspan=4 WIDTH="615" align="center" valign="top"><IMG SRC="art/bannersmc.gif" WIDTH=615 BORDER=0 ALT="DHCP Manager Help">
+
+ </td>
+ </tr>
+
+<!-- End navigation banner -->
+
+
+<tr>
+
+<!-- Start contents block -->
+<td colspan=1 valign="top" width=105>
+ <P>&nbsp;</P>
+
+
+<A HREF="dhcp_main_top.html"><STRONG>Overview</STRONG></A><P>
+
+<A HREF="dhcp_relay_ref.html"><STRONG>Servers and Relay Agents</STRONG></A><P>
+<A HREF="dhcp_addr_ref.html"><STRONG>IP Addresses</STRONG></A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_addr_create.html">Create</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_addr_wiz.html">Address Wizard</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_addr_dup.html">Duplicate</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_addr_mod.html">Modify</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_addr_multi.html">Modify Multi</A><BR>
+
+ &nbsp;&nbsp;<A HREF="dhcp_addr_rel.html">Release</A><BR>
+ <IMG SRC="art/tip2.gif" WIDTH=12 HEIGHT=10 BORDER=0 ALT="">Delete<P>
+
+<A HREF="dhcp_macro_ref.html"><STRONG>Macros</STRONG></A><P>
+
+<A HREF="dhcp_option_ref.html"><STRONG>Options</STRONG></A><P>
+
+<A HREF="dhcp_main_how.html"><STRONG>How To..</STRONG></A><P>
+<A HREF="dhcp_main_menus.html"><STRONG>Menus</STRONG></A><P>
+
+<A HREF="dhcp_main_idx.html"><STRONG>Index</STRONG></A>
+
+
+ </td>
+
+
+
+<!-- End contents block -->
+
+<!-- Start column rule -->
+
+ <td colspan=1 width=5 bgcolor="#CCCCCC">&nbsp;</td>
+
+<!-- End column spacer -->
+
+<!-- Start column spacer -->
+
+ <td colspan=1 width=10 bgcolor="#FFFFFF">&nbsp;</td>
+
+<!-- End column spacer -->
+
+
+
+<!-- Start topic block -->
+
+
+ <td colspan=1 valign="top" bgcolor="#ffffff">
+ <P>&nbsp;</P>
+
+
+
+
+<h1>Delete Address</h1>
+
+The Delete Address dialog box lets you delete one or more addresses
+from the selected DHCP network. <P>
+
+Click OK to remove the selected addresses
+from the network table. <P>
+Click Delete From Hosts Table to also remove the entries associated
+with these addresses from the <tt>hosts</tt> table.
+
+<p>&nbsp;</p>
+
+<!-- Don't go past this line! -->
+
+ </td>
+
+<!-- End topic block -->
+
+</tr>
+
+</table>
+
+<!-- End topic table -->
+
+</body>
+</html>
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_addr_dup.html b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_addr_dup.html
new file mode 100644
index 0000000000..ccdcdc4fda
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_addr_dup.html
@@ -0,0 +1,234 @@
+<!--
+ -- ident "%W% %E% SMI"
+ --
+ Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+ -->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<html>
+
+<head>
+<title>DHCP Manager Help: Duplicate Address</title>
+<meta NAME="AUTHOR" CONTENT="smorgan">
+<meta NAME="KEYWORDS" CONTENT="DHCP">
+
+</head>
+
+<body bgcolor="#FFFFFF">
+
+<a name="top"></a>
+
+<table border=0 cellspacing=0 cellpadding=0 width=615>
+
+<!-- Start navigation banner -->
+
+<tr><td colspan=4 WIDTH="615" align="center" valign="top"><IMG SRC="art/bannersmc.gif" WIDTH=615 BORDER=0 ALT="DHCP Manager Help">
+
+ </td>
+ </tr>
+
+<!-- End navigation banner -->
+
+
+<tr>
+
+<!-- Start contents block -->
+
+ <td colspan=1 valign="top" width=105>
+ <P>&nbsp;</P>
+
+<A HREF="dhcp_main_top.html"><STRONG>Overview</STRONG></A><P>
+
+<A HREF="dhcp_relay_ref.html"><STRONG>Servers and Relay Agents</STRONG></A><P>
+<A HREF="dhcp_addr_ref.html"><STRONG>IP Addresses</STRONG></A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_addr_create.html">Create</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_addr_wiz.html">Address Wizard</A><BR>
+
+ <IMG SRC="art/tip2.gif" WIDTH=12 HEIGHT=10 BORDER=0 ALT="">Duplicate<BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#ip"><EM>Address</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#name"><EM>Name</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#server"><EM>Server</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#macro"><EM>Macro</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#comment"><EM>Comment</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#cid"><EM>Client ID</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#res"><EM>Reserved</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#lease"><EM>Lease</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#bootp"><EM>BOOTP</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#unusable"><EM>Unusable</EM></A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_addr_mod.html">Modify</A><BR>
+
+ &nbsp;&nbsp;<A HREF="dhcp_addr_multi.html">Modify Multiple</A><BR>
+
+
+ &nbsp;&nbsp;<A HREF="dhcp_addr_rel.html">Release</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_addr_del.html">Delete</A><P>
+
+<A HREF="dhcp_macro_ref.html"><STRONG>Macros</STRONG></A><P>
+
+<A HREF="dhcp_option_ref.html"><STRONG>Options</STRONG></A><P>
+
+<A HREF="dhcp_main_how.html"><STRONG>How To..</STRONG></A><P>
+<A HREF="dhcp_main_menus.html"><STRONG>Menus</STRONG></A><P>
+
+<A HREF="dhcp_main_idx.html"><STRONG>Index</STRONG></A>
+
+
+ </td>
+
+
+
+<!-- End contents block -->
+
+<!-- Start column rule -->
+
+ <td colspan=1 width=5 bgcolor="#CCCCCC">&nbsp;</td>
+
+<!-- End column spacer -->
+
+<!-- Start column spacer -->
+
+ <td colspan=1 width=10 bgcolor="#FFFFFF">&nbsp;</td>
+
+<!-- End column spacer -->
+
+
+
+<!-- Start topic block -->
+
+
+ <td colspan=1 valign="top" bgcolor="#ffffff">
+ <P>&nbsp;</P>
+
+<h1>Duplicate Address</h1>
+
+The Duplicate Address dialog box provides a quick way to add a new
+address to the current DHCP network table. Select an address with
+settings that you want to copy to a new address, and then choose
+Duplicate from the Edit menu. <p>
+
+<p>The settings in the Duplicate Address dialog box are described below.
+<P><HR NOSHADE><P>
+
+<a name="one"></a>
+ <table border=0 cellspacing=4 cellpadding=3>
+
+ <tr>
+ <td width=125 valign="top"><a name="ip"><STRONG>IP Address</STRONG></a></td>
+ <td valign="top">The IP address for this record.</td>
+ </tr>
+
+ <tr>
+ <td width=125 valign="top"><a name="name"><STRONG>Client Name</STRONG> (optional)</a></td>
+ <td valign="top">The host name of the client.</td>
+ </tr>
+
+<tr>
+ <td width=125 valign="top"><a name="server"><STRONG>Owned By Server</STRONG></a></td>
+ <td valign="top">The IP address of the DHCP server
+ that owns this record's IP address.</td>
+ </tr>
+
+ <tr>
+ <td width=125 valign="top"><A NAME="macro"><STRONG>Configuration Macro</STRONG></A></td>
+ <td valign="top">An optional macro for the server to
+ use to obtain client configuration parameters from the <tt>dhcptab</tt>. Click the arrow to see a list of macros available on the
+ server. If the macro you want to specify does not already exist on the server,
+ you can create one using the <A HREF="dhcp_macro_create.html">Create Macro</A> dialog box.</td>
+ </tr>
+ <tr>
+ <td width=125 valign="top"><A NAME="comment"><STRONG>Comment</STRONG></A> (optional)</td>
+ <td valign="top">An optional text comment.</td>
+ </tr>
+ </table>
+
+
+ <P><HR NOSHADE><P>
+
+Click Lease to modify settings that affect the type of lease and expiration date.<P>
+
+
+
+ <table border=0 cellspacing=4 cellpadding=3>
+
+ <tr>
+
+<td width=125 valign="top"><a name="cid"><STRONG>Client ID</STRONG></a></td>
+ <td valign="top">A unique indentifier of the DHCP or BOOTP client using this address. The client ID is determined by the vendor of the DHCP client. For non-Solaris DHCP clients, consult your DHCP client documentation for more information.<P> For Solaris DHCP clients, the client ID is a string created by the concatenation of the network hardware type and the client's hardware address. For example, a client with a hardware type of <tt>01</tt> (10-Mb Ethernet) and a hardware address of <tt>8:0:20:11:12:b7</tt> would have a client ID of <tt>010800201112B7</tt>.
+<p>
+ An entry with a Client ID value of 00 indicates that the address is
+ freely available for dynamic allocation to a requesting client.
+ <p></td>
+ </tr>
+
+ <TR>
+ <td width=125 valign="top"><a name="res"><STRONG>Reserved</STRONG></a></td>
+ <TD>The address is reserved for this client and cannot be reclaimed
+ by the server.</TD>
+ </TR>
+
+ <TR>
+ <td width=125 valign="top"><a name="lease"><STRONG>Lease Policy</STRONG></a></td>
+ <td valign="top">
+ <U>Dynamic:</U> The DHCP server is responsible for the allocation
+ and re-allocation of this range of IP addresses. <P>
+ Enter the expiration time of the lease. You can specify an
+ expiration time for the lease in mm/dd/yyyy HH:MM format.<P>
+
+ <U>Permanent:</U> The lease does not expire.
+ </td>
+ </tr>
+
+
+
+ <tr>
+ <td width=100 valign="top"><A NAME="bootp"><STRONG>Assign Only to BOOTP Clients</STRONG></A></td>
+ <td valign="top">The selected IP address is reserved for
+ BOOTP clients.</td>
+ </tr>
+
+ <tr>
+ <td width=100 valign="top"><A NAME="unusable"><STRONG>Address is Unusable</STRONG></A></td>
+ <td valign="top">This IP address cannot be used. You can use this setting together
+ with Reserved to prevent a client from booting.</td>
+ </tr>
+
+ </table>
+
+
+&nbsp;&nbsp;&nbsp;<A HREF="#top"><small>return to top</small></A>
+<p>&nbsp;</p>
+
+<!-- Don't go past this line! -->
+
+ </td>
+
+<!-- End topic block -->
+
+</tr>
+
+</table>
+
+<!-- End topic table -->
+
+</body>
+</html>
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_addr_how.html b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_addr_how.html
new file mode 100644
index 0000000000..d38d1834da
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_addr_how.html
@@ -0,0 +1,327 @@
+<!--
+ -- ident "%W% %E% SMI"
+ --
+ Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+ -->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<html>
+
+<head>
+<title>DHCP Manager Help: How To... IP Addresses</title>
+<meta NAME="AUTHOR" CONTENT="smorgan">
+<meta NAME="KEYWORDS" CONTENT="DHCP">
+
+</head>
+
+<body bgcolor="#FFFFFF">
+
+<a name="top"></a>
+
+<table border=0 cellspacing=0 cellpadding=0 width=615>
+<!-- Start navigation banner -->
+
+<tr><td colspan=4 WIDTH="615" align="center" valign="top"><IMG SRC="art/bannersmc.gif" WIDTH=615 BORDER=0 ALT="DHCP Manager Help">
+
+ </td>
+ </tr>
+
+<!-- End navigation banner -->
+
+
+<tr>
+
+<!-- Start contents block -->
+
+ <td colspan=1 valign="top" width=105>
+ <P>&nbsp;</P>
+
+ <A HREF="dhcp_main_top.html"><STRONG>Overview</STRONG></A><P>
+ <A HREF="dhcp_addr_ref.html"><STRONG>IP Addresses</STRONG></A><P>
+ <A HREF="dhcp_macro_ref.html"><STRONG>Macros</STRONG></A><P>
+ <A HREF="dhcp_option_ref.html"><STRONG>Options</STRONG></A><P>
+ <A HREF="dhcp_main_how.html"><STRONG>How To..</STRONG></A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_relay_how.html">Servers/Relays</A><BR>
+<IMG SRC="art/tip2.gif" WIDTH=12 HEIGHT=10 BORDER=0 ALT="">Addresses<BR>
+&nbsp;&nbsp;&nbsp;<A HREF="#viewtable"><EM>View</EM></A><BR>
+ &nbsp;&nbsp;&nbsp;<A href="#newtable"><EM>Create</EM></A><BR>
+ &nbsp;&nbsp;&nbsp;<A HREF="#copy"><EM>Duplicate</EM></A><BR>
+ &nbsp;&nbsp;&nbsp;<A HREF="#modaddr"><EM>Modify</EM></A><BR>
+ &nbsp;&nbsp;&nbsp;<A HREF="#modmulti"><EM>Modify Multiple</EM></A><BR>
+ &nbsp;&nbsp;&nbsp;<A HREF="#addwiz"><EM>Add Multiple</EM></A><BR>
+ &nbsp;&nbsp;&nbsp;<A HREF="#reladdr"><EM>Release</EM></A><BR>
+ &nbsp;&nbsp;&nbsp;<A HREF="#deltable"><EM>Delete</EM></A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_macro_how.html">Macros</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_option_how.html">Options</A><P>
+
+<A HREF="dhcp_main_menus.html"><STRONG>Menus</STRONG></A><P>
+<A HREF="dhcp_main_idx.html"><STRONG>Index</STRONG></A>
+
+
+ </td>
+
+
+
+<!-- End contents block -->
+
+<!-- Start column rule -->
+
+ <td colspan=1 width=5 bgcolor="#CCCCCC">&nbsp;</td>
+
+<!-- End column spacer -->
+
+<!-- Start column spacer -->
+
+ <td colspan=1 width=10 bgcolor="#FFFFFF">&nbsp;</td>
+
+<!-- End column spacer -->
+
+
+
+<!-- Start topic block -->
+
+
+ <td colspan=1 valign="top" bgcolor="#ffffff">
+ <P>&nbsp;</P>
+ <H1>How To: Addresses</H1>
+
+
+
+
+<strong><big><A NAME="viewtable">View IP Addresses</A></big></strong><p>
+
+ <ol type=1>
+
+ <li><B>Choose the <a href="dhcp_addr_ref.html">Addresses</a>
+ tab in the main window.</B></LI><P>
+
+ <li><strong>Choose a network from the Network list.</strong><BR>
+ DHCP Manager displays a list of client records in the
+ current network, listed by IP address.</li><P>
+</OL>
+To change the way the addresses data is displayed, click in the column header. For example, click the Server column heading to view the records alphabetically in ascending order by server name. Hold down the shift key and click the Server column heading to view the records listed by server in descending order. <P>
+
+<HR NOSHADE>
+
+<strong><big><A NAME="newtable">Create a New IP Address</A></big></strong><p>
+
+ <ol type=1>
+
+<li><B>Choose the <a href="dhcp_addr_ref.html">Addresses</a>
+ tab in the main window.</B></LI><P>
+
+ <li><strong>Choose a network from the Network list.</strong><BR>
+ DHCP Manager displays a list of client records in the
+ current network, listed by IP address.</li><P>
+
+ <LI>Click <a href="dhcp_addr_create.html"><strong>Create</strong></a> in the Edit menu.<br>
+
+ The Create Address dialog box opens.</LI><P>
+
+ <li><strong>Enter the desired <a href="dhcp_addr_ref.html">settings</a> for the new address.</strong><BR>
+ You can enter the following settings in the Address screen:
+ <UL TYPE="DISC">
+ <LI>IP address - the new address</LI>
+ <LI>Client name </LI>
+ <LI>Owning server</LI>
+ <LI>Configuration macro - scroll for available macro</LI>
+ </UL>
+ Click Lease to enter further settings:
+ <UL TYPE="DISC">
+ <LI>Client ID</LI>
+ <LI>Lease status</LI>
+ <LI>Lease expiration</LI>
+ </UL>
+ </LI><P>
+ <LI><strong>Click OK to exit the dialog.</strong><p></li>
+
+ </ol><P>
+
+ <HR NOSHADE>
+
+<strong><big><A NAME="addwiz">Add Multiple IP Addresses</A></big></strong><p>
+
+ <ol type=1>
+
+ <li><B>Choose the <a href="dhcp_addr_ref.html">Addresses</a>
+ tab in the main window.</B></LI><P>
+
+ <li><strong>Choose a network from the Network list.</strong><BR>
+ DHCP Manager displays a list of client records in the
+ current network, listed by IP address.</li><P>
+
+ <LI><strong>Click <A HREF="dhcp_addr_wiz.html">Address Wizard</A> in the Edit menu.</strong><br>
+
+ The Address Wizard starts.</LI><P>
+
+ <li><strong>Follow the directions to add one of more addresses to the selected network.</strong><BR>
+ You can enter values for the following <a href="dhcp_addr_ref.html">settings</a>:
+ <UL TYPE="DISC">
+ <LI>Owning server</LI>
+ <LI>Number of addresses to add</li>
+ <LI>Starting IP address</LI>
+ <LI>Whether to generate host names for clients</LI>
+ <LI>Configuration macro</LI>
+ <LI>Lease status</LI>
+ <LI>Lease type</LI>
+ </UL><P>
+ <LI><strong>Click OK to exit the dialog box.</strong></LI>
+ </ol><P>
+ &nbsp;&nbsp;&nbsp;<A HREF="#top"><small>return to top</small></A>
+
+ <HR NOSHADE>
+
+<strong><big><A NAME="copy">Duplicate an IP Address</A></big></strong><p>
+
+ <ol type=1>
+
+ <li><B>Choose the <a href="dhcp_addr_ref.html">Addresses</a>
+ tab in the main window.</B></LI><P>
+
+ <li><strong>Choose a network from the Network list.</strong><BR>
+ DHCP Manager displays a list of client records in the
+ current network, listed by IP address.</li><P>
+
+ <LI><strong>From the list of addresses, select a client record that
+ you want to copy.</strong></LI><P>
+
+ <LI><strong>Click <a href="dhcp_addr_dup.html">Duplicate</a> from the Edit menu.</strong><br>
+
+ The Duplicate Address dialog box opens. Some of the fields have settings that duplicate those of the selected client record.</LI><P>
+
+ <li><strong>Enter the new IP address in the IP Address field.</strong><br>
+You can change any of the settings listed for this address.</LI><P>
+ <li><strong>Click OK to exit the dialog box.</strong><p></li>
+
+ </ol><P>
+
+
+<HR NOSHADE>
+
+<strong><big><A NAME="modaddr">Modify an IP Address</A></big></strong><p>
+
+ <ol type=1>
+
+ <li><B>Choose the <a href="dhcp_addr_ref.html">Addresses</a>
+ tab in the main window.</B></LI><P>
+
+ <li><strong>Choose a network from the Network list.</strong><BR>
+ DHCP Manager displays a list of client records in the
+ current network, listed by IP address.</li><P>
+
+ <li><strong>Select the client record you want to modify, and then
+ choose <a href="dhcp_addr_mod.html">Properties</a> from the Edit menu (or
+double-click in the table).</strong><br>
+
+ The Address Properties dialog box is displayed.<p></li>
+
+ <li><strong>Enter the desired settings for the addresses, and then
+ click OK to exit the dialog.</strong><p></li>
+
+ </ol><P>
+&nbsp;&nbsp;&nbsp;<A HREF="#top"><small>return to top</small></A>
+<HR NOSHADE>
+
+<strong><big><A NAME="modmulti">Modify Multiple IP Addresses</A></big></strong><p>
+
+ <ol type=1>
+
+ <li><strong>Choose the <a
+href="dhcp_addr_ref.html">Addresses</a> tab in the main window.</strong><P></LI>
+
+ <li><strong>Choose a network from the Network list.</strong><BR>
+ DHCP Manager displays a list of client records in the
+ current network, listed by IP address.</li><P>
+
+ <li><strong>Select the client records you want to modify, and then
+ choose <a href="dhcp_addr_multi.html">Properties</a> from the Edit menu.</strong><BR>
+ You can select a range of addresses by holding down the Shift key
+ as you highlight your choices. <P>
+ To select individual addresses, hold down the Control key and select
+ the addresses you want to modify.<P>
+
+ The Modify Multiple Addresses
+ dialog box is displayed.<p></li>
+
+ <li><strong>Edit this screen to change one or more settings.</strong></LI><P>
+
+ <LI><strong>Click OK to exit the dialog.</strong><p></li>
+
+ </ol>
+
+<HR NOSHADE>
+
+<strong><big><A NAME="reladdr">Release IP Addresses</A></big></strong><p>
+
+ <ol type=1>
+
+ <li><B>Choose the <a href="dhcp_addr_ref.html">Addresses</a>
+ tab in the main window.</B></LI><P>
+
+ <li><strong>Choose a network from the Network list.</strong><BR>
+ DHCP Manager displays a list of client records in the
+ current network, listed by IP address.</li><P>
+
+ <LI><strong>Select one or more addresses to release, and then choose <a href="dhcp_addr_rel.html">Release</a> from the Edit menu.</strong><BR>
+ The Release Address dialog box opens. </LI><P>
+ <li><strong>Click OK to release the selected addresses.</strong><p></li>
+
+ </ol>
+
+<HR NOSHADE>
+
+<strong><big><A NAME="deltable">Delete IP Addresses</A></big></strong><p>
+
+ <ol type=1>
+
+ <li><B>Choose the <a href="dhcp_addr_ref.html">Addresses</a>
+ tab in the main window.</B></LI><P>
+
+ <li><strong>Choose a network from the Network list.</strong><BR>
+ DHCP Manager displays a list of client records in the
+ current network, listed by IP address.</li><P>
+
+ <LI><strong>Select one or more addresses to delete, and then choose <A HREF="dhcp_addr_del.html">Delete</A> from the Edit menu.</strong></LI><P>
+
+ <li><strong>Click OK to delete the selected addresses.</strong><p></li>
+
+ </ol>
+
+
+<p>
+&nbsp;&nbsp;&nbsp;<A HREF="#top"><small>return to top</small></A>
+
+<!-- Don't go past this line! -->
+
+ </td>
+
+<!-- End topic block -->
+
+</tr>
+
+</table>
+
+<!-- End topic table -->
+
+</body>
+</html>
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_addr_mod.html b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_addr_mod.html
new file mode 100644
index 0000000000..41397a6360
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_addr_mod.html
@@ -0,0 +1,253 @@
+<!--
+ -- ident "%W% %E% SMI"
+ --
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ -- Copyright (c) 2000-2001 by Sun Microsystems, Inc.
+ -- All rights reserved.
+ -->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<html>
+
+<head>
+<title>DHCP Manager Help: Modify Address</title>
+<meta NAME="AUTHOR" CONTENT="smorgan">
+<meta NAME="KEYWORDS" CONTENT="DHCP">
+
+</head>
+
+<body bgcolor="#FFFFFF">
+
+<a name="top"></a>
+
+<table border=0 cellspacing=0 cellpadding=0 width=615>
+<!-- Start navigation banner -->
+
+<tr><td colspan=4 WIDTH="615" align="center" valign="top"><IMG SRC="art/bannersmc.gif" WIDTH=615 BORDER=0 ALT="DHCP Manager Help">
+
+ </td>
+ </tr>
+
+<!-- End navigation banner -->
+
+
+<tr>
+
+<!-- Start contents block -->
+
+
+
+<td colspan=1 valign="top" width=105>
+ <P>&nbsp;</P>
+
+ <A HREF="dhcp_main_top.html"><STRONG>Overview</STRONG></A><P>
+ <A HREF="dhcp_relay_ref.html"><STRONG>Servers and Relay Agents</STRONG></A><P>
+
+ <A HREF="dhcp_addr_ref.html"><STRONG>IP Addresses</STRONG></A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_addr_create.html">Create</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_addr_wiz.html">Address Wizard</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_addr_dup.html">Duplicate</A><BR>
+<IMG SRC="art/tip2.gif" WIDTH=12 HEIGHT=10 BORDER=0 ALT="">Modify<BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#ip"><EM>Address</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#name"><EM>Name</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#server"><EM>Server</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#macro"><EM>Macro</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#comment"><EM>Comment</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#cid"><EM>Client ID</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#res"><EM>Reserved</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#lease"><EM>Lease</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#bootp"><EM>BOOTP</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#unusable"><EM>Unusable</EM></A><BR>
+
+ &nbsp;&nbsp;<A HREF="dhcp_addr_multi.html">Modify Multiple</A><BR>
+
+ &nbsp;&nbsp;<A HREF="dhcp_addr_rel.html">Release</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_addr_del.html">Delete</A><P>
+
+<A HREF="dhcp_macro_ref.html"><STRONG>Macros</STRONG></A><P>
+<A HREF="dhcp_option_ref.html"><STRONG>Options</STRONG></A><P>
+<A HREF="dhcp_main_how.html"><STRONG>How To..</STRONG></A><P>
+<A HREF="dhcp_main_menus.html"><STRONG>Menus</STRONG></A><P>
+<A HREF="dhcp_main_idx.html"><STRONG>Index</STRONG></A>
+
+
+ </td>
+
+
+
+<!-- End contents block -->
+
+<!-- Start column rule -->
+
+ <td colspan=1 width=5 bgcolor="#CCCCCC">&nbsp;</td>
+
+<!-- End column spacer -->
+
+<!-- Start column spacer -->
+
+ <td colspan=1 width=10 bgcolor="#FFFFFF">&nbsp;</td>
+
+<!-- End column spacer -->
+
+
+
+<!-- Start topic block -->
+
+
+ <td colspan=1 valign="top" bgcolor="#ffffff">
+ <P>&nbsp;</P>
+
+<h1>Modify Address</h1>
+
+The Address Properties dialog box lets you view and modify the settings for
+an address in the current DHCP network table on the selected DHCP server. <p>
+
+<center>
+
+ <table border=1 cellspacing=3 cellpadding=3>
+
+ <tr>
+ <td valign="top" align="left" BGCOLOR="#EBEBEB">To modify
+ several addresses at once, select the addresses in the View Addresses main window,
+ and then choose Properties from the Edit menu to display the <a
+ href="dhcp_addr_multi.html">Modify Multiple Addresses</a> dialog box.</td>
+ </tr>
+
+ </table>
+
+</center>
+
+<p>The settings in the Address Properties dialog box are described below.<p>
+
+<P><HR NOSHADE><P>
+
+ <table border=0 cellspacing=4 cellpadding=3>
+
+ <tr>
+ <td width=100 valign="top"><a name="ip"><STRONG>IP Address</STRONG></a></td>
+ <td valign="top">Specify the IP address to make available to new
+ DHCP clients. The new address must be unique in the current DHCP network table.</td>
+ </tr>
+
+
+ <tr>
+ <TD WIDTH="100" ALIGN="LEFT" VALIGN="TOP"><A NAME="name"><STRONG>Client Name</STRONG></A> (optional)</td>
+ <td valign="top">Specify the host name of the client. If you use this option, the DHCP Manager will
+attempt to create an entry in the <tt>hosts</tt>
+table.</td>
+ </tr>
+
+ <tr>
+ <TD WIDTH="100" ALIGN="LEFT" VALIGN="TOP"><a name="server"><STRONG>Owned By Server</STRONG></a></td>
+ <td valign="top">Specify the IP address of the DHCP server
+ that owns the new IP address. This server is
+ responsible for initially responding to the DHCP client's request for
+ IP address allocation.</td>
+ </tr>
+
+
+ <tr>
+ <TD WIDTH="100" ALIGN="LEFT" VALIGN="TOP"><a name="macro"><STRONG>Configuration Macro</STRONG></a></td>
+ <td valign="top">Specify an optional macro for the server to
+ use to obtain client configuration parameters from the <tt>dhcptab</tt>. Click the arrow to see a list of macros available on the
+ server. If the macro you want to specify does not already exist on the server, you
+ can create it, using the <a href="dhcp_macro_create.html">Create Macro</A>
+ dialog box.</td>
+ </tr>
+
+ <tr>
+ <TD WIDTH="100" ALIGN="LEFT" VALIGN="TOP"><a name="comment"><STRONG>Comment</STRONG> (optional)</a></td>
+ <td valign="top">Enter an optional text comment.</td>
+ </tr>
+
+
+</table>
+
+
+<P><HR NOSHADE><P>
+
+
+Click Lease to configure the setting for lease status and expiration.
+The following settings are available.
+
+
+ <table border=0 cellspacing=4 cellpadding=3>
+
+ <tr>
+
+ <TD WIDTH="100" ALIGN="LEFT" VALIGN="TOP"><a name="cid"><STRONG>Client ID</STRONG></a></td><td valign="top">A unique indentifier of the DHCP or BOOTP client using this address. The client ID is determined by the vendor of the DHCP client. For non-Solaris DHCP clients, consult your DHCP client documentation for more information.<P> For Solaris DHCP clients, the client ID is a string created by the concatenation of the network hardware type and the client's hardware address. For example, a client with a hardware type of <tt>01</tt> (10-Mb Ethernet) and a hardware address of <tt>8:0:20:11:12:b7</tt> would have a client ID of <tt>010800201112B7</tt>.
+<p>
+ An entry with a Client ID value of 00 indicates that the address is
+ freely available for dynamic allocation to a requesting client.
+ <p></td>
+ </tr>
+
+ <TR>
+ <TD WIDTH="100" ALIGN="LEFT" VALIGN="TOP"><a name="res"><STRONG>Reserved</STRONG></a></td>
+ <TD>The address is reserved for this client and cannot be reclaimed
+ by the server.</TD>
+ </tr>
+ <TR>
+ <TD WIDTH="100" ALIGN="LEFT" VALIGN="TOP"><a name="lease"><STRONG>Lease Policy</STRONG></a></td>
+ <td valign="top">
+ <U>Dynamic:</U> The DHCP server is responsible for the allocation
+ and re-allocation of this range of IP addresses. <P>
+ Enter the expiration time of the lease. You can specify an
+ expiration date for the lease in mm/dd/yyyy format.<P>
+
+ <U>Permanent:</U> The lease does not expire.
+ </td>
+ </tr>
+
+
+
+ <tr>
+ <TD WIDTH="100" ALIGN="LEFT" VALIGN="TOP"><A NAME="bootp"><STRONG>Assign Only to BOOTP Clients</STRONG></A></td>
+ <td valign="top">The selected IP address is reserved for
+ BOOTP clients.</td>
+ </tr>
+
+ <tr>
+ <TD WIDTH="100" ALIGN="LEFT" VALIGN="TOP"><A NAME="unusable"><STRONG>Address is Unusable</STRONG></A></td>
+ <td valign="top">This IP address cannot be used. You can use this setting together with Reserved to prevent a client from booting.</td>
+ </tr>
+
+ </table>
+
+<p>&nbsp;</p>
+
+&nbsp;&nbsp;&nbsp;<A HREF="#top"><small>return to top</small></A>
+
+
+<!-- Don't go past this line! -->
+
+ </td>
+
+<!-- End topic block -->
+
+</tr>
+
+</table>
+
+<!-- End topic table -->
+
+</body>
+</html>
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_addr_multi.html b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_addr_multi.html
new file mode 100644
index 0000000000..f1fdf1ae31
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_addr_multi.html
@@ -0,0 +1,205 @@
+<!--
+ -- ident "%W% %E% SMI"
+ --
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ -- Copyright (c) 2000-2001 by Sun Microsystems, Inc.
+ -- All rights reserved.
+ -->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<html>
+
+<head>
+<title>DHCP Manager Help: Modify Multiple Addresses</title>
+<meta NAME="AUTHOR" CONTENT="smorgan">
+<meta NAME="KEYWORDS" CONTENT="DHCP">
+
+</head>
+
+<body bgcolor="#FFFFFF">
+
+<a name="top"></a>
+
+<table border=0 cellspacing=0 cellpadding=0 width=615>
+
+<!-- Start navigation banner -->
+
+<tr><td colspan=4 WIDTH="615" align="center" valign="top"><IMG SRC="art/bannersmc.gif" WIDTH=615 BORDER=0 ALT="DHCP Manager Help">
+
+ </td>
+ </tr>
+
+<!-- End navigation banner -->
+
+
+<tr>
+
+<!-- Start contents block -->
+
+ <td colspan=1 valign="top" width=105>
+ <P>&nbsp;</P>
+
+ <A HREF="dhcp_main_top.html"><STRONG>Overview</STRONG></A><P>
+ <A HREF="dhcp_relay_ref.html"><STRONG>Servers and Relay Agents</STRONG></A><P>
+
+ <A HREF="dhcp_addr_ref.html"><STRONG>IP Addresses</STRONG></A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_addr_create.html">Create</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_addr_wiz.html">Address Wizard</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_addr_dup.html">Duplicate</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_addr_mod.html">Modify</A><BR>
+ <IMG SRC="art/tip2.gif" WIDTH=12 HEIGHT=10 BORDER=0 ALT="">Modify Multi<BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#server"><EM>Server</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#comment"><EM>Comment</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#macro"><EM>Macro</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#bootp"><EM>BOOTP</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#unusable"><EM>Unusable</EM></A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#lease"><EM>Lease</EM></a><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_addr_rel.html">Release</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_addr_del.html">Delete</A><P>
+
+<A HREF="dhcp_macro_ref.html"><STRONG>Macros</STRONG></A><P>
+<A HREF="dhcp_option_ref.html"><STRONG>Options</STRONG></A><P>
+<A HREF="dhcp_main_how.html"><STRONG>How To..</STRONG></A><P>
+<A HREF="dhcp_main_menus.html"><STRONG>Menus</STRONG></A><P>
+<A HREF="dhcp_main_idx.html"><STRONG>Index</STRONG></A>
+
+
+
+ </td>
+
+
+
+<!-- End contents block -->
+
+<!-- Start column rule -->
+
+ <td colspan=1 width=5 bgcolor="#CCCCCC">&nbsp;</td>
+
+<!-- End column spacer -->
+
+<!-- Start column spacer -->
+
+ <td colspan=1 width=10 bgcolor="#FFFFFF">&nbsp;</td>
+
+<!-- End column spacer -->
+
+
+
+<!-- Start topic block -->
+
+
+ <td colspan=1 valign="top" bgcolor="#ffffff">
+ <P>&nbsp;</P>
+
+
+<h1>Modify Multiple Addresses</h1>
+
+The Modify Multiple Addresses dialog box lets you modify multiple client
+records in the current DHCP network table. <p>
+
+<center>
+
+ <table border=1 cellspacing=3 cellpadding=3 width=400>
+
+ <tr>
+ <td width=400 valign="top" align="left" BGCOLOR="#EBEBEB">To modify one
+ address at a time, select the address in the list of addresses
+ for the selected network, and then choose Properties from the Edit menu
+ to display the <a href="dhcp_addr_mod.html">Address Properties</a>
+ dialog box.</td>
+ </tr>
+
+ </table>
+
+</center>
+
+<p>The settings in the Modify Multiple Addresses dialog box are described below.<p>
+Note that fields labelled Keep Current Settings are not modified in the selected records.
+
+<P><HR NOSHADE><P>
+
+ <table border=0 cellspacing=4 cellpadding=3>
+
+ <tr>
+ <TD WIDTH="100" ALIGN="LEFT" VALIGN="TOP"><a name="server"><STRONG>Managing Server</STRONG></a></td>
+ <td valign="top">Specify the host name or IP address of the DHCP server
+ that owns the IP address. This server is responsible for initially responding to the DHCP client's request for IP address allocation.<P>
+
+Click the arrow to view a list of servers to which you are connected.
+You can enter a the name or address of a server that is not on this list.</td>
+ </tr>
+
+ <tr>
+ <TD WIDTH="100" ALIGN="LEFT" VALIGN="TOP"><a name="comment"><STRONG>Comment</STRONG> (optional)</a></td>
+ <td valign="top">Enter an optional text comment.</td>
+ </tr>
+
+ <tr>
+ <TD WIDTH="100" ALIGN="LEFT" VALIGN="TOP"><a name="macro"><STRONG>Configuration Macro</STRONG></a></td>
+ <td valign="top">The configuration macro is used by the server to
+ obtain client configuration parameters from the <tt>dhcptab</tt>. Click the arrow to see a list of macros available on the
+ server. If the macro you want to specify does not already exist on the server, you can create it, using the <a href="dhcp_macro_create.html">Create Macro</A> dialog box.</td>
+ </tr>
+
+ <tr>
+ <TD WIDTH="100" ALIGN="LEFT" VALIGN="TOP"><a name="bootp"><STRONG>BootP</STRONG></a></td>
+ <td valign="top">You can choose whether or not to assign all the addresses to BOOTP clients.</td>
+ </tr>
+
+ <tr>
+ <TD WIDTH="100" ALIGN="LEFT" VALIGN="TOP"><a name="unusable"><STRONG>Unusable</STRONG></a></td>
+ <td valign="top">You can choose whether or not to mark these addresses as Unusable.</td>
+ </tr>
+
+ <TR>
+ <TD WIDTH="100" ALIGN="LEFT" VALIGN="TOP"><a name="lease"><STRONG>Lease Type</STRONG></a></td>
+ <td valign="top">
+ <U>Dynamic:</U> The DHCP server is responsible for the allocation
+ and re-allocation of this range of IP addresses. <P>
+ Enter the expiration time of the lease. You can specify an
+ expiration date for the lease in mm/dd/yyyy format.<P>
+ <U>Permanent:</U> The lease does not expire.
+
+ </td>
+ </tr>
+
+
+ </table>
+
+<p>&nbsp;</p>
+
+&nbsp;&nbsp;&nbsp;<A HREF="#top"><small>return to top</small></A>
+
+
+<!-- Don't go past this line! -->
+
+ </td>
+
+<!-- End topic block -->
+
+</tr>
+
+</table>
+
+<!-- End topic table -->
+
+</body>
+</html>
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_addr_ref.html b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_addr_ref.html
new file mode 100644
index 0000000000..1e96011ae8
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_addr_ref.html
@@ -0,0 +1,255 @@
+<!--
+ -- ident "%W% %E% SMI"
+ --
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ -- Copyright (c) 2000-2001 by Sun Microsystems, Inc.
+ -- All rights reserved.
+ -->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<html>
+
+<head>
+<title>DHCP Manager Help: IP Addresses </title>
+<meta NAME="AUTHOR" CONTENT="smorgan">
+<meta NAME="KEYWORDS" CONTENT="DHCP">
+
+</head>
+
+<body bgcolor="#FFFFFF">
+
+<a name="top"></a>
+
+<table border=0 cellspacing=0 cellpadding=0 width=615>
+
+<!-- Start navigation banner -->
+
+<tr><td colspan=4 WIDTH="615" align="center" valign="top"><IMG SRC="art/bannersmc.gif" WIDTH=615 BORDER=0 ALT="DHCP Manager Help">
+
+ </td>
+ </tr>
+ </table>
+
+<!-- End navigation banner -->
+
+
+<!-- Start contents block -->
+<table border=0 cellspacing=0 cellpadding=0 width=615>
+<tr>
+ <td colspan=1 valign="top" width=105>
+ <P>&nbsp;</P>
+
+ <A HREF="dhcp_main_top.html"><STRONG>Overview</STRONG></A><P>
+ <A HREF="dhcp_relay_ref.html"><STRONG>Servers and Relays</STRONG></A><P>
+ <IMG SRC="art/tip2.gif" WIDTH=12 HEIGHT=10 BORDER=0 ALT=""><STRONG>Addresses</STRONG><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#ip"><EM>Address</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#status"><EM>Status</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#expires"><EM>Expires</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#server"><EM>Server</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#macro"><EM>Macro</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#cid"><EM>Client ID</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#comment"><EM>Comment</EM></a><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_addr_create.html">Create</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_addr_wiz.html">Address Wizard</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_addr_dup.html">Duplicate</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_addr_mod.html">Modify</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_addr_multi.html">Modify Multiple</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_addr_rel.html">Release</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_addr_del.html">Delete</A><P>
+
+
+<A HREF="dhcp_macro_ref.html"><STRONG>Macros</STRONG></A><P>
+<A HREF="dhcp_option_ref.html"><STRONG>Options</STRONG></A><P>
+
+<A HREF="dhcp_main_how.html"><STRONG>How To..</STRONG></A><P>
+<A HREF="dhcp_main_menus.html"><STRONG>Menus</STRONG></A><P>
+<A HREF="dhcp_main_idx.html"><STRONG>Index</STRONG></A>
+
+
+ </td>
+
+
+
+<!-- End contents block -->
+
+<!-- Start column rule -->
+
+ <td colspan=1 width=5 bgcolor="#CCCCCC">&nbsp;</td>
+
+<!-- End column spacer -->
+
+<!-- Start column spacer -->
+
+ <td colspan=1 width=10 bgcolor="#FFFFFF">&nbsp;</td>
+
+<!-- End column spacer -->
+
+
+
+<!-- Start topic block -->
+
+
+ <td colspan=1 valign="top" bgcolor="#ffffff">
+ <P>&nbsp;</P>
+
+
+<h1>Addresses</h1>
+
+When you select the Addresses tab in the main window, DHCP Manager displays the
+client records in the selected network, sorted by host name. These records are stored in the DHCP network tables maintained by the data store.<P>
+<STRONG>List Addresses</STRONG><BR>
+To view the records sorted by IP address, click Show Address in
+the View menu. Servers and clients are listed by IP address instead of by host name.<P>
+<STRONG>Change Sort</STRONG><BR>
+The client records are sorted by client
+IP address or host name. To change the sort, click
+in the column header. For example, click the Server
+column heading to view the records alphabetically in ascending
+order by server name. Hold down the Shift key and click the
+Server column heading to view the records listed by server
+in descending order.
+<P>
+<STRONG>Add Address</STRONG><BR>
+To add new addresses to the selected network, choose <a
+href="dhcp_addr_wiz.html">the Address Wizard</a> from the Edit menu.
+You can add a single address by selecting
+<A Href="dhcp_addr_create.html">Create</A> from the Edit menu.<P>
+<STRONG>Modify Address</STRONG><BR>
+You can select an address and view the settings assigned to that address,
+<a href="dhcp_addr_how.html#modaddr">modify</a> one or more settings, and <a
+href="dhcp_addr_how.html#reladdr">release</A> or <A HREF="dhcp_addr_how.html#deltable">delete</a> addresses in the list.
+<P><HR NOSHADE><P>
+<STRONG>Address Settings</STRONG><BR>
+The address settings are described in the following table.<p>
+
+
+
+<table border=0 cellspacing=4 cellpadding=3>
+
+ <tr>
+ <td width=100 valign="top"><a name="ip"><STRONG>IP Address</STRONG></a></td>
+ <td valign="top">An IP address in
+the selected network that can be made available to DHCP clients.</td>
+ </tr>
+
+ <tr>
+ <td width=100 valign="top"><a name="status"><STRONG>Status</STRONG></a></td>
+
+ <td valign="top">The lease and address attributes assigned to the client:
+
+<table cellspacing=3 cellpadding=3>
+
+
+ <tr>
+ <td width=90 valign="top"><U>Dynamic</U></td>
+ <td valign="top">DHCP server is responsible for the allocation
+ and re-allocation of IP addresses.</td>
+ </tr>
+
+ <tr>
+ <td width=90 valign="top"><U>Permanent</U></td>
+ <td valign="top">Disable evaluation of the lease setting (lease
+ is permanent).</td>
+ </tr>
+
+ <tr>
+ <td width=90 valign="top"><U>Reserved</U><BR>(Manual)</td>
+ <td valign="top">The address is reserved for this client (manually
+ assigned). The DHCP server cannot assign this address to another
+ client. The address cannot be reclaimed by the DHCP server.</td>
+ </tr>
+
+ <tr>
+ <td width=90 valign="top"><A NAME="bootp"><U>BOOTP</U></A></td>
+ <td valign="top">The selected IP addresses are reserved for
+ BOOTP clients only.</td>
+ </tr>
+
+ <tr>
+ <td width=90 valign="top"><A NAME="unuse"><U>Unusable</U></A></td>
+ <td valign="top">This address is not usable. This setting
+ can also be used in combination with the Reserved setting to
+ prevent the client from booting.</td>
+ </tr>
+ </table>
+</td>
+</tr>
+
+ <tr>
+ <td width=100 valign="top"><a name="expires"><STRONG>Expires</STRONG></a></td>
+
+ <td width=325 valign="top">The date
+the lease expires for this IP address.
+
+ You can specify an expiration date for the lease in
+ mm/dd/yyyy format.
+</td>
+</tr>
+
+ <tr>
+ <td width=100 valign="top"><a name="server"><STRONG>Server</STRONG></a></td>
+ <td valign="top">The IP address of the DHCP server
+ that owns this IP address. This server is
+ responsible for initially responding to the DHCP client's request for
+ an IP address.</td>
+ </tr>
+
+<TR>
+ <td width=100 valign="top"><a name="macro"></a><STRONG>Macro</STRONG></td>
+ <td valign="top">An optional macro for the server to
+ use to obtain client configuration parameters from the <tt>dhcptab</tt>.</td>
+ </tr>
+
+
+ <tr>
+ <td width=100 valign="top"><a name="cid"><STRONG>Client ID</STRONG></a></td>
+ <td valign="top">A unique indentifier of the DHCP or BOOTP client using this address. The client ID is determined by the vendor of the DHCP client. For non-Solaris DHCP clients, consult your DHCP client documentation for more information.<P> For Solaris DHCP clients, the client ID is a string created by the concatenation of the network hardware type and the client's hardware address. For example, a client with a hardware type of <tt>01</tt> (10-Mb Ethernet) and a hardware address of <tt>8:0:20:11:12:b7</tt> would have a client ID of <tt>010800201112B7</tt>.
+<p>
+ An entry with a Client ID value of 00 indicates that the address is
+ freely available for dynamic allocation to a requesting client.
+ <p></td>
+ </tr>
+
+ <tr>
+ <td width=100 valign="top"><a name="comment"></a><STRONG>Comment</STRONG></td>
+ <td valign="top">An optional text comment.</td>
+ </tr>
+ </table>
+
+
+<p>
+&nbsp;&nbsp;&nbsp;<A HREF="#top"><small>return to top</small></A>
+
+<!-- Don't go past this line! -->
+
+ </td>
+
+<!-- End topic block -->
+
+</tr>
+
+</table>
+
+
+<!-- End topic table -->
+
+</body>
+</html>
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_addr_rel.html b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_addr_rel.html
new file mode 100644
index 0000000000..ed5157be9e
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_addr_rel.html
@@ -0,0 +1,138 @@
+<!--
+ -- ident "%W% %E% SMI"
+ --
+ Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+ -->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<html>
+
+<head>
+<title>DHCP Manager Help: Release Address</title>
+<meta NAME="AUTHOR" CONTENT="smorgan">
+<meta NAME="KEYWORDS" CONTENT="DHCP">
+
+</head>
+
+<body bgcolor="#FFFFFF">
+
+<a name="top"></a>
+
+<table border=0 cellspacing=0 cellpadding=0 width=615>
+
+<!-- Start navigation banner -->
+
+<tr><td colspan=4 WIDTH="615" align="center" valign="top"><IMG SRC="art/bannersmc.gif" WIDTH=615 BORDER=0 ALT="DHCP Manager Help">
+
+ </td>
+ </tr>
+
+<!-- End navigation banner -->
+
+
+<tr>
+
+<!-- Start contents block -->
+
+ <td colspan=1 valign="top" width=105>
+ <P>&nbsp;</P>
+
+
+<A HREF="dhcp_main_top.html"><STRONG>Overview</STRONG></A><P>
+
+<A HREF="dhcp_relay_ref.html"><STRONG>Servers and Relay Agents</STRONG></A><P>
+<A HREF="dhcp_addr_ref.html"><STRONG>IP Addresses</STRONG></A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_addr_create.html">Create</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_addr_wiz.html">Address Wizard</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_addr_dup.html">Duplicate</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_addr_mod.html">Modify</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_addr_multi.html">Modify Multiple</A><BR>
+
+<IMG SRC="art/tip2.gif" WIDTH=12 HEIGHT=10 BORDER=0 ALT="">Release<BR>
+ &nbsp;&nbsp;<A HREF="dhcp_addr_del.html">Delete</A><P>
+
+<A HREF="dhcp_macro_ref.html"><STRONG>Macros</STRONG></A><P>
+
+<A HREF="dhcp_option_ref.html"><STRONG>Options</STRONG></A><P>
+
+<A HREF="dhcp_main_how.html"><STRONG>How To..</STRONG></A><P>
+<A HREF="dhcp_main_menus.html"><STRONG>Menus</STRONG></A><P>
+
+<A HREF="dhcp_main_idx.html"><STRONG>Index</STRONG></A>
+
+
+ </td>
+
+
+
+<!-- End contents block -->
+
+<!-- Start column rule -->
+
+ <td colspan=1 width=5 bgcolor="#CCCCCC">&nbsp;</td>
+
+<!-- End column spacer -->
+
+<!-- Start column spacer -->
+
+ <td colspan=1 width=10 bgcolor="#FFFFFF">&nbsp;</td>
+
+<!-- End column spacer -->
+
+
+
+<!-- Start topic block -->
+
+
+ <td colspan=1 valign="top" bgcolor="#ffffff">
+ <P>&nbsp;</P>
+
+<h1>Release Addresses</h1>
+
+The Release Addresses dialog box lets you reclaim one or more addresses in the
+selected DHCP network. Reclaiming the address clears the Client ID and
+Lease fields.<P>
+
+Click OK to release the selected addresses.<P>
+<TABLE WIDTH="500" BORDER="1" CELLSPACING="2" CELLPADDING="2" VALIGN="TOP" BORDERCOLOR="#CCCCCC" BGCOLOR="#DEDEDE">
+<TR>
+<TD><STRONG>Note:</STRONG> Reclaiming the address will not clear the Reserved field. To make a reserved address available to another client, modify the address properties using the Properties dialog box from the Edit menu. Uncheck the box marked Reserved, then select Dynamic and enter a lease expiration date.
+</TD></TR>
+
+</TABLE>
+<p>&nbsp;</p>
+
+<!-- Don't go past this line! -->
+
+ </td>
+
+<!-- End topic block -->
+
+</tr>
+
+</table>
+
+<!-- End topic table -->
+
+</body>
+</html>
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_addr_view.html b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_addr_view.html
new file mode 100644
index 0000000000..a7e358a24c
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_addr_view.html
@@ -0,0 +1,332 @@
+<!--
+ -- ident "%W% %E% SMI"
+ --
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ -- Copyright (c) 2000-2001 by Sun Microsystems, Inc.
+ -- All rights reserved.
+ -->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<html>
+
+<head>
+<title>DHCP Manager Help: Address Menus</title>
+<meta NAME="AUTHOR" CONTENT="smorgan">
+<meta NAME="KEYWORDS" CONTENT="DHCP">
+
+</head>
+
+<body bgcolor="#FFFFFF">
+
+<a name="top"></a>
+
+<table border=0 cellspacing=0 cellpadding=0 width=625>
+
+<!-- Start navigation banner -->
+
+<tr><td colspan=4 WIDTH="615" align="center" valign="top"><IMG SRC="art/bannersmc.gif" WIDTH=615 BORDER=0 ALT="DHCP Manager Help">
+
+ </td>
+ </tr>
+
+<!-- End navigation banner -->
+
+
+<tr>
+
+<!-- Start contents block -->
+
+ <td colspan=1 valign="top" width=105>
+ <P>&nbsp;</P>
+
+ <A HREF="dhcp_main_top.html"><STRONG>Overview</STRONG></A><P>
+ <A HREF="dhcp_relay_ref.html"><STRONG>Servers and Relays</STRONG></A><P>
+ <A HREF="dhcp_addr_ref.html"><STRONG>IP Addresses</STRONG></A><P>
+ <A HREF="dhcp_macro_ref.html"><STRONG>Macros</STRONG></A><P>
+ <A HREF="dhcp_option_ref.html"><STRONG>Options</STRONG></A><P>
+
+ <A HREF="dhcp_main_how.html"><STRONG>How To..</STRONG></A><P>
+ <A HREF="dhcp_main_menus.html"><STRONG>Menus</STRONG></A><BR>
+<IMG SRC="art/tip2.gif" WIDTH=12 HEIGHT=10 BORDER=0 ALT="">Address View<BR>
+&nbsp;&nbsp;<A HREF="dhcp_macro_view.html">Macro View</A><BR>
+&nbsp;&nbsp;<A HREF="dhcp_option_view.html">Option View</A><P>
+<A HREF="dhcp_main_idx.html"><STRONG>Index</STRONG></A>
+
+
+ </td>
+
+
+
+<!-- End contents block -->
+
+<!-- Start column rule -->
+
+ <td colspan=1 width=5 bgcolor="#CCCCCC">&nbsp;</td>
+
+<!-- End column spacer -->
+
+<!-- Start column spacer -->
+
+ <td colspan=1 width=10 bgcolor="#FFFFFF">&nbsp;</td>
+
+<!-- End column spacer -->
+
+
+
+<!-- Start topic block -->
+
+
+ <td colspan=1 valign="top" bgcolor="#ffffff" WIDTH="495">
+
+ <P>&nbsp;</P>
+
+<H1>Address View Menus</H1>
+<table ALIGN="LEFT" BORDER="0" CELLPADDING="3" WIDTH="495">
+
+<TR>
+<td colspan=1 width=20 VALIGN="TOP">&nbsp;</td>
+<TH colspan=1 align=left valign="top" width=150>
+ <FONT SIZE="+1">Titles</FONT><P></TH>
+ <TH align=left valign="top"><FONT SIZE="+1">Description</FONT><P></TH>
+
+</TR>
+
+
+<tr>
+<td colspan=1 width=20>&nbsp;</td>
+ <td colspan=1 align=left valign="top" width=150 bgcolor="#FFFFFF"><U>File Menu</U></td>
+ <Td></Td>
+ </TR>
+
+ <tr><td colspan=1 width=20>&nbsp;</td>
+ <td colspan=1 align=left valign="top" width=150>&nbsp;&nbsp;<STRONG>Exit</STRONG></TD>
+ <TD ALIGN="LEFT" VALIGN="TOP">Exit from the application.</TD>
+ </TR>
+<TR><TD COLSPAN="2"><P>&nbsp;</P></TD></TR>
+<tr>
+<td colspan=1 width=20>&nbsp;</td>
+ <td colspan=1 align=left valign="top" width=150 bgcolor="#FFFFFF"><U>Edit Menu</U></td>
+ <Td></Td>
+ </TR>
+
+ <tr>
+<td colspan=1 width=20>&nbsp;</td>
+
+<TD COLSPAN="1" ALIGN="left" VALIGN="TOP" width="150">&nbsp;&nbsp;<STRONG>Create</STRONG></TD>
+
+ <TD ALIGN="LEFT" VALIGN="TOP">
+Add new address to DHCP server.</TD></TR>
+
+<tr><td colspan=1 width=20>&nbsp;</td>
+<td colspan=1 align=left valign="top">&nbsp;&nbsp;<STRONG>Delete</STRONG></TD>
+<TD ALIGN="LEFT" VALIGN="TOP">Delete the selected address(es).</TD>
+</TR>
+
+<tr>
+<td colspan=1 width=20>&nbsp;</td>
+<td colspan=1 align=left valign="top" width=150>&nbsp;&nbsp;<STRONG>Duplicate</STRONG></TD>
+<TD ALIGN="LEFT" VALIGN="TOP">Copy the selected address entry.</TD>
+</TR>
+
+<tr>
+<td colspan=1 width=20>&nbsp;</td>
+<td colspan=1 align=left valign="top" width=150>&nbsp;&nbsp;<STRONG>Properties</STRONG></TD>
+<TD ALIGN="LEFT" VALIGN="TOP">Modify the properties of the selected address(es).</TD>
+</TR>
+
+<tr><td colspan=1 width=20>&nbsp;</td>
+<td colspan=1 align=left valign="top" width=150>&nbsp;&nbsp;<STRONG>Address Wizard</STRONG></TD>
+<TD ALIGN="LEFT" VALIGN="TOP">Use Address Wizard to add multiple addresses.</TD>
+</TR>
+
+<tr><td colspan=1 width=20>&nbsp;</td>
+<td colspan=1 align=left valign="top">&nbsp;&nbsp;<STRONG>Release Addresses</STRONG></TD>
+<TD ALIGN="LEFT" VALIGN="TOP">Release the selected address(es).</TD>
+</TR>
+
+<tr><td colspan=1 width=20>&nbsp;</td>
+<td colspan=1 align=left valign="top" width=150>&nbsp;&nbsp;<STRONG>Network Wizard</STRONG></TD>
+<TD ALIGN="LEFT" VALIGN="TOP">Configure and add network to the DHCP server's list of networks to monitor.</TD>
+</TR>
+
+<tr><td colspan=1 width=20>&nbsp;</td>
+<td colspan=1 align=left valign="top" width=150>&nbsp;&nbsp;<STRONG>Delete Networks</STRONG></TD>
+<TD ALIGN="LEFT" VALIGN="TOP">Remove selected network from DHCP servers list of networks to monitor.</TD>
+</TR>
+
+<TR><TD COLSPAN="2"><P>&nbsp;</P></TD></TR>
+
+
+<tr><td colspan=1 width=20>&nbsp;</td>
+ <td colspan=1 align=left valign="top" width=150 bgcolor="#FFFFFF">
+<U>View Menu</U><P></td>
+<TD></TD></TR>
+
+<tr><td colspan=1 width=20>&nbsp;</td>
+<td colspan=1 align=left valign="top">&nbsp;&nbsp;<STRONG>Refresh</STRONG></TD>
+<TD ALIGN="LEFT" VALIGN="TOP">Refresh the data from the server.</TD>
+</TR>
+
+<tr><td colspan=1 width=20>&nbsp;</td>
+<td colspan=1 align=left valign="top">&nbsp;&nbsp;<STRONG>Show Addresses</STRONG></TD>
+<TD ALIGN="LEFT" VALIGN="TOP">List clients by IP address instead of by hostname.</TD>
+</TR>
+
+
+
+ <tr><td colspan=1 width=20>&nbsp;</td>
+<td colspan=1 align=left valign="top">&nbsp;&nbsp;<STRONG>Show Grid</STRONG></TD>
+<TD ALIGN="LEFT" VALIGN="TOP">Turn on grid lines in display.</TD>
+</TR>
+<TR><TD COLSPAN="2"><P>&nbsp;</P></TD></TR>
+
+<tr><td colspan=1 width=20>&nbsp;</td>
+ <td colspan=1 align=left valign="top" width=150 bgcolor="#FFFFFF">
+<U>Service Menu</U><P></td>
+ <Td></Td></TR>
+
+ <tr><td colspan=1 width=20>&nbsp;</td>
+<td colspan=1 align=left valign="top">&nbsp;&nbsp;<STRONG>Restart</STRONG></TD>
+<TD ALIGN="LEFT" VALIGN="TOP">Stop and restart the DHCP daemon.</TD>
+</TR>
+
+<tr><td colspan=1 width=20>&nbsp;</td>
+<td colspan=1 align=left valign="top">&nbsp;&nbsp;<STRONG>Stop</STRONG></TD>
+<TD ALIGN="LEFT" VALIGN="TOP">Stop the DHCP daemon.</TD>
+</TR>
+
+<tr><td colspan=1 width=20>&nbsp;</td>
+<td colspan=1 align=left valign="top">&nbsp;&nbsp;<STRONG>Start</STRONG></TD>
+<TD ALIGN="LEFT" VALIGN="TOP">Start the DHCP daemon.</TD>
+</TR>
+
+<tr><td colspan=1 width=20>&nbsp;</td>
+<td colspan=1 align=left valign="top">&nbsp;&nbsp;<STRONG>Disable</STRONG></TD>
+<TD ALIGN="LEFT" VALIGN="TOP">Disable the DHCP server or BOOTP relay agent.</TD>
+</TR>
+
+<tr><td colspan=1 width=20>&nbsp;</td>
+<td colspan=1 align=left valign="top">&nbsp;&nbsp;<STRONG>Enable</STRONG></TD>
+<TD ALIGN="LEFT" VALIGN="TOP">Enable the DHCP server or BOOTP relay agent.</TD>
+</TR>
+
+<tr><td colspan=1 width=20>&nbsp;</td>
+<td colspan=1 align=left valign="top">&nbsp;&nbsp;<STRONG>Modify</STRONG></TD>
+<TD ALIGN="LEFT" VALIGN="TOP">Customize services of DHCP server or BOOTP relay agent.</TD>
+</TR>
+
+<tr><td colspan=1 width=20>&nbsp;</td>
+<td colspan=1 align=left valign="top">&nbsp;&nbsp;<STRONG>Export Data</STRONG></TD>
+<TD ALIGN="LEFT" VALIGN="TOP">Copy from the DHCP data store selected IP addresses, macros, and options to a file for the purpose of importing to a DHCP server.</TD>
+</TR>
+
+<tr><td colspan=1 width=20>&nbsp;</td>
+<td colspan=1 align=left valign="top">&nbsp;&nbsp;<STRONG>Import Data</STRONG></TD>
+<TD ALIGN="LEFT" VALIGN="TOP">Bring into the DHCP data store IP addresses, macros, and options that were exported from a DHCP server. </TD>
+</TR>
+
+<tr><td colspan=1 width=20>&nbsp;</td>
+<td colspan=1 align=left valign="top">&nbsp;&nbsp;<STRONG>Convert Data Store</STRONG></TD>
+<TD ALIGN="LEFT" VALIGN="TOP">Configure the DHCP server to use a new data store and convert existing data to new data store format. </TD>
+</TR>
+
+<tr><td colspan=1 width=20>&nbsp;</td>
+<td colspan=1 align=left valign="top">&nbsp;&nbsp;<STRONG>Unconfigure</STRONG></TD>
+<TD ALIGN="LEFT" VALIGN="TOP">Unconfigure the DHCP server or BOOTP relay agent.<P>&nbsp;</P></TD>
+</TR>
+
+
+<tr><td colspan=1 width=20>&nbsp;</td>
+ <td colspan=1 align=left valign="top" width=150 bgcolor="#FFFFFF">
+<U>Help Menu</U></td>
+ <Td>Display the online help.<P>&nbsp;</P></Td></TR>
+
+
+<tr><td colspan=1 width=20>&nbsp;</td>
+ <td colspan=1 align=left valign="top" width=150 bgcolor="#FFFFFF">
+<U>Network</U></td>
+ <Td>Scroll the list of configured networks. Select which network to display.<P>&nbsp;</P></Td></TR>
+
+
+ <tr><td colspan=1 width=20>&nbsp;</td>
+ <td colspan=1 align=left valign="top" width=150 bgcolor="#FFFFFF">
+<U>Find</U></td>
+ <Td>Search for an address entry.<P>&nbsp;</P></Td></TR>
+
+ <tr><td colspan=1 width=20>&nbsp;</td>
+ <td colspan=1 align=left valign="top" width=150 bgcolor="#FFFFFF">
+<U>Column Entries</U></td>
+ <Td></Td></TR>
+
+ <tr><td colspan=1 width=20>&nbsp;</td>
+ <td colspan=1 align=left valign="top" width=150>&nbsp;&nbsp;<STRONG>IP Address</STRONG>
+</td>
+ <Td>Client IP address</Td></TR>
+
+ <tr><td colspan=1 width=20>&nbsp;</td>
+ <td colspan=1 align=left valign="top" width=150>&nbsp;&nbsp;<STRONG>Status</STRONG></td>
+ <Td>Lease and address attributes</Td></TR>
+
+ <tr><td colspan=1 width=20>&nbsp;</td>
+ <td colspan=1 align=left valign="top" width=150>&nbsp;&nbsp;<STRONG>Expires</STRONG></td>
+ <Td>Expiration date for the lease</Td></TR>
+
+ <tr><td colspan=1 width=20>&nbsp;</td>
+ <td colspan=1 align=left valign="top" width=150>&nbsp;&nbsp;<STRONG>Server</STRONG></td>
+ <Td>IP address of owning server</Td></TR>
+
+ <tr><td colspan=1 width=20>&nbsp;</td>
+ <td colspan=1 align=left valign="top" width=150>&nbsp;&nbsp;<STRONG>Macro</STRONG></td>
+ <Td>Configuration macro</Td></TR>
+
+ <tr><td colspan=1 width=20>&nbsp;</td>
+ <td colspan=1 align=left valign="top" width=150>&nbsp;&nbsp;<STRONG>CI</STRONG></td>
+ <Td>Client ID</Td></TR>
+
+<tr><td colspan=1 width=20>&nbsp;</td>
+<td colspan=1 align=left valign="top" width=150>&nbsp;&nbsp;<STRONG>Comment</STRONG></td>
+ <Td>Optional comment</Td></TR>
+
+ <TR><TD COLSPAN="3"><P>&nbsp;&nbsp;&nbsp;<A HREF="#top"><small>return to top</small></A></P></TD></TR>
+ </TABLE>
+
+
+
+
+
+
+
+
+
+<!-- Don't go past this line! -->
+
+ </td>
+
+<!-- End topic block -->
+
+</tr>
+
+</table>
+
+<!-- End topic table -->
+
+</body>
+</html>
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_addr_wiz.html b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_addr_wiz.html
new file mode 100644
index 0000000000..53c701dd7f
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_addr_wiz.html
@@ -0,0 +1,217 @@
+<!--
+ -- ident "%W% %E% SMI"
+ --
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ -- Copyright (c) 2000-2001 by Sun Microsystems, Inc.
+ -- All rights reserved.
+ -->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<html>
+
+<head>
+<title>DHCP Manager Help: Address Wizard</title>
+<meta NAME="AUTHOR" CONTENT="smorgan">
+<meta NAME="KEYWORDS" CONTENT="DHCP">
+
+</head>
+
+<body bgcolor="#FFFFFF">
+
+<a name="top"></a>
+
+<table border=0 cellspacing=0 cellpadding=0 width=615>
+
+<!-- Start navigation banner -->
+
+<tr><td colspan=4 WIDTH="615" align="center" valign="top"><IMG SRC="art/bannersmc.gif" WIDTH=615 BORDER=0 ALT="DHCP Manager Help">
+
+ </td>
+ </tr>
+
+<!-- End navigation banner -->
+
+
+<tr>
+
+<!-- Start contents block -->
+
+ <td colspan=1 valign="top" width=105>
+ <P>&nbsp;</P>
+
+<A HREF="dhcp_main_top.html"><STRONG>Overview</STRONG></A><P>
+
+<A HREF="dhcp_relay_ref.html"><STRONG>Servers and Relays</STRONG></A><P>
+<A HREF="dhcp_addr_ref.html"><STRONG>Addresses</STRONG></A><BR>
+
+&nbsp;&nbsp;<A HREF="dhcp_addr_create.html">Create</A><BR>
+
+<IMG SRC="art/tip2.gif" WIDTH=12 HEIGHT=10 BORDER=0 ALT="">Address Wizard<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#addrno"><EM>Number</EM></a><BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#ownserv"><EM>Server</EM></a><BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#startaddr"><EM>Start Addr</EM></a><BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#name"><EM>Names</EM></a><BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#confirm"><EM>Confirm</EM></a><BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#config"><EM>Config</EM></a><BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#unusable"><EM>Unusable</EM></a><BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#lease"><EM>Lease</EM></a><BR>
+&nbsp;&nbsp;<A HREF="dhcp_addr_dup.html">Duplicate</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_addr_mod.html">Modify</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_addr_multi.html">Modify Multiple</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_addr_rel.html">Release</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_addr_del.html">Delete</A><P>
+
+
+
+
+<A HREF="dhcp_macro_ref.html"><STRONG>Macros</STRONG></A><P>
+<A HREF="dhcp_option_ref.html"><STRONG>Options</STRONG></A><P>
+<A HREF="dhcp_main_how.html"><STRONG>How To..</STRONG></A><P>
+<A HREF="dhcp_main_menus.html"><STRONG>Menus</STRONG></A><P>
+
+<A HREF="dhcp_main_idx.html"><STRONG>Index</STRONG></A>
+
+
+ </td>
+
+
+
+<!-- End contents block -->
+
+<!-- Start column rule -->
+
+ <td colspan=1 width=5 bgcolor="#CCCCCC">&nbsp;</td>
+
+<!-- End column spacer -->
+
+<!-- Start column spacer -->
+
+ <td colspan=1 width=10 bgcolor="#FFFFFF">&nbsp;</td>
+
+<!-- End column spacer -->
+
+
+
+<!-- Start topic block -->
+
+
+ <td colspan=1 valign="top" bgcolor="#ffffff">
+ <P>&nbsp;</P>
+<h1>Address Wizard</h1>
+
+Use the Address Wizard to add a block of addresses to the server's DHCP network. To start the Address Wizard, select Addresses from the View menu, and then select Address Wizard from the Edit menu.
+<p>
+
+Proceed through the steps by clicking the arrow key at the bottom of
+the screen.
+
+<p>
+To enter information, double-click in the field, enter the desired value, and then press Enter.
+<P>
+If you choose the Address Wizard, you will be asked
+to supply the following information. <P><HR NOSHADE><P>
+
+ <table border=0 cellspacing=4 cellpadding=3>
+
+
+
+ <tr>
+ <td valign="top" width=125><A NAME="addrno"><STRONG>Number of Addresses</STRONG></A></td>
+ <td valign="top">Enter the total number of addresses to add
+ to the DHCP server's network. </td>
+ </tr>
+
+ <tr>
+ <td valign="top" width=125><A NAME="ownserv"><STRONG>Managed by Server</STRONG></A></td>
+ <td valign="top">Enter the IP address of the DHCP server
+ that manages these IP addresses. This server is responsible for initially responding
+ to the DHCP client's request for an IP address.
+ </td>
+ </tr>
+
+ <tr>
+ <td valign="top" width=125><A NAME="startaddr"><STRONG>Starting IP Address</STRONG></A></td>
+ <td valign="top">Enter the first IP address in the series.<P></td>
+ </tr>
+
+ <tr>
+ <td valign="top" width=125><A NAME="name"><STRONG>Generate Names</STRONG></A></td>
+ <td valign="top">Enter a root name for generated client names.<P> You can
+ generate a list of client names for each allocated address. The names will be entered in the server's <tt>hosts</tt> table. Each new
+ client name will use the root name and add an incremental number.<P>
+ </td>
+ </tr>
+
+ <tr>
+ <td valign="top" width=125><A NAME="confirm"><STRONG>Confirm Address List</STRONG></A></td>
+ <td valign="top">You can view the information that you have entered.<P>
+ If you want to add or modify addresses, use the <A HREF="dhcp_addr_create.html">Create Address</A> or <A HREF="dhcp_addr_mod.html">Modify Address</A> options in the Edit menu.<P>
+ <P>
+ </td>
+ </tr>
+
+ <tr>
+ <td valign="top" width=125><A NAME="config"><STRONG>Client Configuration Macro</STRONG></A></td>
+ <td valign="top">Enter the name of a client configuration macro. You can
+ scroll for existing macros. Select a macro from the list and click View to display the contents of the macro.<P>
+If you want to name a macro that does not exist,
+ you can create it with the <A HREF="dhcp_macro_create.html">Create Macro</A>
+ dialog box.<P>
+ </td>
+ </tr>
+
+ <tr>
+ <td valign="top" width=125><A NAME="unusable"><STRONG>Unusable</STRONG></A></td>
+ <td valign="top">You can choose to mark these addresses as <A HREF="dhcp_addr_ref.html#unuse">Unusable</A>. <P>
+ </td>
+ </tr>
+
+ <tr>
+ <td valign="top" width=125><A NAME="lease"><STRONG>Lease Type</STRONG></A></td>
+ <td valign="top">Select Dynamic or Permanent.<p>
+ <U>Dynamic:</U> The DHCP server is responsible for the allocation
+ and re-allocation of this range of IP addresses. Choose Dynamic if you want to
+ make IP addresses available when they are not being used.<P>
+ Enter the expiration time of the lease. You can specify an
+ expiration date in mm/dd/yyyy format. <P>
+ <U>Permanent:</U> The address is permanently assigned to the client.<P>
+ </td>
+ </tr>
+
+ </table>
+&nbsp;&nbsp;&nbsp;<A HREF="#top"><small>return to top</small></A>
+
+<p>&nbsp;</p>
+
+<!-- Don't go past this line! -->
+
+ </td>
+
+<!-- End topic block -->
+
+</tr>
+
+</table>
+
+<!-- End topic table -->
+
+</body>
+</html>
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_config_wiz.html b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_config_wiz.html
new file mode 100644
index 0000000000..570f39645e
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_config_wiz.html
@@ -0,0 +1,314 @@
+<!--
+ -- ident "%W% %E% SMI"
+ --
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ -- Copyright (c) 2000-2001 by Sun Microsystems, Inc.
+ -- All rights reserved.
+ -->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<html>
+
+<head>
+<title>DHCP Manager Help: Configuring DHCP Server</title>
+<meta NAME="AUTHOR" CONTENT="smorgan">
+<meta NAME="KEYWORDS" CONTENT="DHCP">
+
+</head>
+
+<body bgcolor="#ffffff">
+
+<a name="top"></a>
+
+<table border=0 cellspacing=0 cellpadding=0 width=625>
+
+
+<!-- Start navigation banner -->
+
+<tr><td colspan=4 WIDTH="615" align="left" valign="top"><IMG SRC="art/bannersmc.gif" WIDTH=615 BORDER=0 ALT="DHCP Manager Help">
+
+ </td>
+ </tr>
+
+<!-- End navigation banner -->
+
+
+<tr>
+
+<!-- Start contents block -->
+
+ <td colspan=1 valign="top" WIDTH="105">
+ <P>&nbsp;</P>
+ <STRONG><A HREF="dhcp_main_top.html">Overview</A></STRONG><P>
+ <STRONG><A HREF="dhcp_relay_ref.html">Servers and Relays</A></STRONG><BR>
+ <IMG SRC="art/tip2.gif" WIDTH=12 HEIGHT=10 BORDER=0 ALT="">DHCP Config<BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<A HREF="#store"><EM>Data Store</EM></A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<A HREF="#dspath"><EM>Path</EM></A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<A HREF="#hostnmserv"><EM>Hosts Name Service</EM></A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<A HREF="#lease"><EM>Lease Policy</EM></A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<A HREF="#dns"><EM>DNS Domain/Server</EM></A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<A HREF="#net"><EM>Network</EM></A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<A HREF="#router"><EM>Router</EM></A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<A HREF="#nis"><EM>NIS</EM></A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<A HREF="#nisplus"><EM>NIS+</EM></A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_relay_config.html">Relay Config</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_net_wiz.html">Network Config</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_server_serv.html">DHCP Services</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_relay_serv.html">Relay Services</A><P>
+ <A HREF="dhcp_addr_ref.html"><STRONG>Addresses</STRONG></A><P>
+ <A HREF="dhcp_macro_ref.html"><STRONG>Macros</STRONG></A><P>
+ <A HREF="dhcp_option_ref.html"><STRONG>Options</STRONG></A><P>
+ <A HREF="dhcp_main_how.html"><STRONG>How To..</STRONG></A><P>
+ <A HREF="dhcp_main_menus.html"><STRONG>Menus</STRONG></A><P>
+<A HREF="dhcp_main_idx.html"><STRONG>Index</STRONG></A>
+
+ </td>
+
+<!-- End contents block -->
+
+<!-- Start column rule -->
+
+ <td colspan=1 width=5 bgcolor="#CCCCCC">&nbsp;</td>
+
+<!-- End column spacer -->
+
+<!-- Start column spacer -->
+
+ <td colspan=1 width=10 bgcolor="#FFFFFF">&nbsp;</td>
+
+<!-- End column spacer -->
+
+
+
+<!-- Start topic block -->
+
+<td colspan=1 valign="top" bgcolor="#ffffff" width="495">
+<P>&nbsp;</P>
+
+
+<H1>DHCP Configuration Wizard</H1>
+
+The DHCP Configuration Wizard helps you configure a Solaris<small><sup>TM</sup></small>
+system to be a DHCP server and configures the first network. <P>
+<TABLE WIDTH="500" BORDER="1" CELLSPACING="2" CELLPADDING="2" VALIGN="TOP" BORDERCOLOR="#CCCCCC" BGCOLOR="#DEDEDE">
+<TR>
+<TD><STRONG>Note: </STRONG>Please read the "Planning for DHCP Service" chapter in the Solaris <em>DHCP Administration Guide, </em> before configuring a DHCP server.
+</TD></TR>
+</TABLE>
+<P>
+After initial configuration, use the Modify option in the <A HREF="dhcp_server_serv.html">Services</A>
+menu to configure services such as BOOTP compatibility, duplicate address detection,
+and which interfaces to monitor.<P>
+To enter information in the wizard, double-click in the field, enter the desired value, and then press Enter.<P>
+The DHCP Configuration Wizard, asks you to supply the following information.
+<P><HR NOSHADE><P>
+
+<table border=0 cellspacing=4 cellpadding=3 width=490>
+ <tr>
+ <td width=125 valign="top"><A NAME="store"><STRONG>Data Store </STRONG></A><br>
+ </td>
+ <td valign="top">Select the type of data store the DHCP server will use to
+ store configuration data. The choices are:
+
+<table cellspacing=3 cellpadding=3>
+ <tr>
+ <td width=90 valign="top"><em>Text files</em></td>
+ <td valign="top">Data is stored in clear text ASCII files. Suitable for small number of clients, up to 10,000. Data can be shared through NFS among several DHCP servers.</td>
+ </tr>
+<tr>
+ <td width=90 valign="top"><em>NIS+</em></td>
+ <td valign="top">Data is stored in NIS+ tables. Suitable for medium to large numbers of clients, up to 40,000. Data can be shared among several DHCP servers. If the server is not already configured as a NIS+ client, you cannot select the NIS+ option. To use NIS+ as a data store, cancel the wizard, configure the server as a NIS+ client, and run DHCP Manager again.</td>
+ </tr>
+<tr>
+ <td width=90 valign="top"><em>Binary files</em></td>
+ <td valign="top">Data is stored in binary text files. Suitable for large numbers of clients up to 100,000. Data can <em>not</em> be shared among several DHCP servers. </td>
+ </tr>
+</table>
+</td>
+</tr>
+<!-- end data store row -->
+ <tr>
+<td width=125 valign="top"><A NAME="dspath"><STRONG>Data store path</STRONG></A><br>
+</td>
+<td>If you chose text files or binary files as your data store, enter the path to the
+ data (default=<tt>/var/dhcp</tt>).<p>
+ If you chose NIS+, enter the domain of the NIS+ server (default=this server's domain). <P> </td></tr>
+
+
+<tr>
+ <td width=125 valign="top"><A NAME="hostnmserv"><STRONG>Hosts name service</STRONG></A><br>
+ </td>
+ <td valign="top">Select the name service that the DHCP server should use to register host names associated with IP addresses that it allocates to clients.
+ <table cellspacing=3 cellpadding=3>
+ <tr>
+ <td width=90 valign="top"><em>Do not manage hosts records</em></td>
+ <td valign="top">The DHCP server will not attempt to add host name entries to any name service. An administrator should add the names manually to a name service. </td>
+ </tr>
+<tr>
+ <td width=90 valign="top"><em>/etc/hosts</em></td>
+ <td valign="top">The DHCP server will add host name entries to the servers /etc/hosts table. </td>
+ </tr>
+<tr>
+ <td width=90 valign="top"><em>NIS+</em></td>
+ <td valign="top">The DHCP server will add host name entries to NIS+. The DHCP server system must be a NIS+ client. You must supply the NIS+ domain name. </td>
+ </tr>
+<tr>
+ <td width=90 valign="top"><em>DNS</em></td>
+ <td valign="top">The DHCP server will add host name entries to DNS if the DHCP daemon and DNS daemon are running on the same system. You must supply the DNS domain name. </td>
+ </tr>
+</table>
+
+ </td>
+ </tr>
+<!-- End of Hosts name service row -->
+ <tr>
+ <td width=125 valign="top"><A NAME="lease"><STRONG>Lease Policy</STRONG></A><br>
+ </td>
+ <td valign="top">Enter the length of time before a lease expires.
+ The lease is the amount of time a DHCP server grants
+ permission to a DHCP client to use a particular address.
+ You can enter from 1 hour to 3550 weeks. <P>
+ The lease time value should be relatively small, so that expired addresses
+are reclaimed quickly, but large enough so that if your DHCP service becomes
+unavailable, the clients continue to function until the machine(s) running
+the DHCP service can be repaired. A rule of thumb is to specify a time that
+is two times the predicted down time of a server. For example, if it generally
+takes four hours to obtain and replace a defective part and reboot the server,
+you should specify a lease time of eight hours. <P>
+ The default is to allow a client to renegotiate the lease before it expires.
+ A Solaris DHCP client will try to renew the lease when it is halfway
+ through the lease period. <P>
+ If not allowed to renegotiate, clients must issue a new DHCP request
+ in order to obtain a
+ new address when the lease expires. You may choose this option
+ in an environment where there are more clients than there are
+ addresses, and you need to enforce a time limit on the use of an IP
+ address.
+ </td>
+ </tr>
+
+ <tr>
+ <td width=125 valign="top"><A NAME="dns"><STRONG>DNS Domain</STRONG></A></td>
+ <td valign="top">The domain server resolves host names to host
+ addresses. If the server is configured to use DNS, the domain name and address
+ of the DNS server will be displayed.
+ If the fields are empty, you can enter the domain name and address of a
+ DNS domain server. <p>
+ You can enter the address of more than one server. The order in the list
+ determines the order in which the servers are queried.
+ </td>
+
+ </tr>
+ <TR><TD COLSPAN="2">&nbsp;&nbsp;&nbsp;<A HREF="#top"><small>return to top</small></A></TD></TR>
+
+ </table>
+
+<HR noshade size=2>
+
+
+<strong><A NAME="net"><big>Network Configuration</big></A></STRONG><P>
+This section begins the network configuration.
+You can configure the first network using the DHCP Configuration Wizard. Once
+the DHCP server is configured, you can add additional networks using the Network Wizard, which is available from the Edit menu, when the Address view is displayed.<P>
+
+ <table border=0 cellspacing=4 cellpadding=3 width=490>
+ <tr>
+ <td width=125 valign="top"><A NAME="addr"><STRONG>Network Address</STRONG></A></td>
+ <td valign="top">
+ Enter the IP address of the network you are configuring.<p>
+ </td>
+ </tr>
+
+ <tr>
+ <td width=125 valign="top"><A NAME="info"><STRONG>Subnet Mask</STRONG></A><br></td>
+ <td valign="top">
+ Enter the subnet mask for this network. A subnet mask is a way of dividing
+ up the host portion of an Internet address to form local subnetworks.
+ </TD></TR>
+
+ <tr>
+ <td width=125 valign="top"><A NAME="info"><STRONG>Network Type</STRONG></A><br></td>
+ <td valign="top">
+ Specify whether the network is a local area network (LAN) or point-to-point (PPP).<p>
+
+
+ </TD>
+ </tr>
+
+ <tr>
+ <td width=125 valign="top"><A NAME="router"><STRONG>Routing</STRONG></A></td>
+ <td valign="top">A router is a machine with multiple network
+ interfaces that can forward IP packets from one network to
+ another. In most cases, your clients should use router discovery to
+ connect to a router. If you have clients in your network that cannot
+ use router discovery, enter the IP address of a router which
+ they can use to communicate with systems on another network.
+ </td>
+ </tr>
+
+
+ <tr>
+ <td width=125 valign="top"><A NAME="nis"><STRONG>NIS Domain Name</STRONG></A><br>
+ <STRONG>NIS Server Address</STRONG></td>
+ <td valign="top">If the server is configured to use NIS naming service,
+ the NIS server information will be filled in. If not, you can enter the domain
+ name and IP address of one or more NIS name servers.<P>
+
+ The order in which the address appears in the list determines the order in
+ which the servers are queried.
+ </td>
+ </tr>
+
+ <tr>
+ <td width=125 valign="top"><A NAME="nisplus"><STRONG>NIS+ Domain Name</STRONG></A><br>
+ <STRONG>NIS+ Server Address</STRONG></td>
+ <td valign="top">If the server is configured to use NIS+ name service,
+ the NIS+ server information will be filled in. If not, you can enter
+ the domain name and IP address of one or more NIS+ name servers.<P>
+ The order in which the address appears in the list determines the order in
+ which the servers are queried.
+ </td>
+
+ </tr>
+
+
+</table>
+&nbsp;&nbsp;&nbsp;<A HREF="#top"><small>return to top</small></A>
+
+
+
+<p>&nbsp;</p>
+
+
+<!-- Don't go past this line! -->
+
+ </td>
+
+<!-- End topic block -->
+
+</tr>
+
+</table>
+
+<!-- End topic table -->
+
+</body>
+</html>
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_convert_wiz.html b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_convert_wiz.html
new file mode 100644
index 0000000000..45e4d46e42
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_convert_wiz.html
@@ -0,0 +1,172 @@
+<!--
+ -- ident "%Z%%M% %I% %E% SMI"
+ --
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ -- Copyright (c) 2000-2001 by Sun Microsystems, Inc.
+ -- All rights reserved.
+ -->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<html>
+
+<head>
+<title>DHCP Manager Help: Data Store Conversion</title>
+<meta NAME="AUTHOR" CONTENT="smorgan">
+<meta NAME="KEYWORDS" CONTENT="DHCP">
+
+</head>
+
+<body bgcolor="#ffffff">
+
+<a name="top"></a>
+
+<table border=0 cellspacing=0 cellpadding=0 width=625>
+
+
+<!-- Start navigation banner -->
+
+<tr><td colspan=4 WIDTH="615" align="left" valign="top"><IMG SRC="art/bannersmc.gif" WIDTH=615 BORDER=0 ALT="DHCP Manager Help">
+
+ </td>
+ </tr>
+
+<!-- End navigation banner -->
+
+
+<tr>
+
+<!-- Start contents block -->
+
+ <td colspan=1 valign="top" WIDTH="105">
+ <P>&nbsp;</P>
+ <STRONG><A HREF="dhcp_main_top.html">Overview</A></STRONG><P>
+ <STRONG><A HREF="dhcp_relay_ref.html">Servers and Relays</A></STRONG><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_config_wiz.html">DHCP
+Config</a><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_relay_config.html">BOOTP Relay Config</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_net_wiz.html">Network Config</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_server_serv.html">Modify DHCP Service</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_relay_serv.html">Modify BOOTP Service</A><br>
+
+ &nbsp;&nbsp;<A HREF="dhcp_export_wiz.html">Export Config Data</A><br>
+ &nbsp;&nbsp;<A HREF="dhcp_import_wiz.html">Import Config Data</A> <BR>
+ <IMG SRC="art/tip2.gif" WIDTH=12 HEIGHT=10 BORDER=0 ALT=""><A HREF="dhcp_convert_wiz.html">Convert Data Store</A><br><P><A HREF="dhcp_addr_ref.html"><STRONG>Addresses</STRONG></A><P>
+ <A HREF="dhcp_macro_ref.html"><STRONG>Macros</STRONG></A><P>
+ <A HREF="dhcp_option_ref.html"><STRONG>Options</STRONG></A><P>
+ <A HREF="dhcp_main_how.html"><STRONG>How To...</STRONG></A><P>
+ <A HREF="dhcp_main_menus.html"><STRONG>Menus</STRONG></A><P>
+<A HREF="dhcp_main_idx.html"><STRONG>Index</STRONG></A>
+
+ </td>
+
+<!-- End contents block -->
+
+<!-- Start column rule -->
+
+ <td colspan=1 width=5 bgcolor="#CCCCCC">&nbsp;</td>
+
+<!-- End column spacer -->
+
+<!-- Start column spacer -->
+
+ <td colspan=1 width=10 bgcolor="#FFFFFF">&nbsp;</td>
+
+<!-- End column spacer -->
+
+
+
+<!-- Start topic block -->
+
+<td colspan=1 valign="top" bgcolor="#ffffff" width="495">
+<P>&nbsp;</P>
+
+
+<H1>Data Store Conversion</H1>
+
+The Data Store Conversion wizard enables you to convert the Solaris DHCP data store from one format to another. Choose Convert Data Store from the Services menu to start the wizard.
+<P>
+The Data Store Conversion wizard asks you to supply the following information.
+<P><HR NOSHADE><P>
+
+<table border=0 cellspacing=4 cellpadding=3 width=490>
+ <tr>
+ <td width=125 valign="top"><A NAME="store"><STRONG>Data storage type</STRONG></A><br>
+ </td>
+ <td valign="top">Select the new data store type you want to use.
+ <P>
+ Text files are recommended if you are serving a small number of
+ DHCP clients, less than 10,000.
+
+ <P>Binary files are recommended in sites having a very large number
+ of clients and no sharing is required among DHCP servers.
+
+ <P>NIS+ is recommended if you are already using NIS+ for name service,
+ and if you have an environment with multiple DHCP servers, so that the
+ information can be shared by all the DHCP servers in the NIS+ domain.
+ <P>
+<STRONG>Note:</STRONG> If the server is not already configured as a NIS+ client, you cannot select the NIS+ option. To use NIS+ for the data store, cancel the wizard, configure the server as a NIS+ client, and run DHCP Manager again.
+
+ </td>
+ </tr>
+ <tr>
+ <td width=125 valign="top"><A NAME="path"><STRONG>Configure data store</STRONG></A><br>
+ </td>
+ <td valign="top">The information requested here varies according to the data store chosen.
+ If you selected text files or binary files, enter the path to the
+ data (<tt>/var/dhcp</tt> is the default). If you use NIS+, enter the domain
+ of the NIS+ server (this server's domain is the default). <p>
+
+ </td>
+ </tr>
+
+ <tr>
+ <td width=125 valign="top"><A NAME="save"><STRONG>Save old tables</STRONG></A></td>
+ <td valign="top">By default, the DHCP tables that the server had been using before the
+conversion are deleted after it is determined that the conversion was successful. If you
+do not want the tables to be deleted, select Save old tables. Note that if a problem occurs during the conversion,
+the tables will not be deleted even if you did not elect to save the tables.<p>
+
+ </td>
+
+ </tr>
+ <TR><TD COLSPAN="2">&nbsp;&nbsp;&nbsp;<A HREF="#top"><small>return to top</small></A></TD></TR>
+
+ </table>
+
+
+</table>
+
+
+
+<!-- Don't go past this line! -->
+
+ </td>
+
+<!-- End topic block -->
+
+</tr>
+
+</table>
+
+<!-- End topic table -->
+
+</body>
+</html>
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_export_wiz.html b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_export_wiz.html
new file mode 100644
index 0000000000..81085bc3d0
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_export_wiz.html
@@ -0,0 +1,181 @@
+<!--
+ -- ident "%Z%%M% %I% %E% SMI"
+ --
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ -- Copyright (c) 2000-2001 by Sun Microsystems, Inc.
+ -- All rights reserved.
+ -->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<html>
+
+<head>
+<title>DHCP Manager Help: Export Data</title>
+<meta NAME="AUTHOR" CONTENT="smorgan">
+<meta NAME="KEYWORDS" CONTENT="DHCP">
+
+</head>
+
+<body bgcolor="#ffffff">
+
+<a name="top"></a>
+
+<table border=0 cellspacing=0 cellpadding=0 width=625>
+
+
+<!-- Start navigation banner -->
+
+<tr><td colspan=4 WIDTH="615" align="left" valign="top"><IMG
+SRC="art/bannersmc.gif" WIDTH=615 BORDER=0 ALIGN="LEFT" ALT="DHCP Manager Help">
+
+ </td>
+ </tr>
+
+<!-- End navigation banner -->
+
+
+<tr>
+
+<!-- Start contents block -->
+
+ <td colspan=1 valign="top" WIDTH="105">
+ <P>&nbsp;</P>
+ <STRONG><A HREF="dhcp_main_top.html">Overview</A></STRONG><P>
+ <STRONG><A HREF="dhcp_relay_ref.html">Servers and
+Relays</A></STRONG><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_config_wiz.html">DHCP
+Config</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_relay_config.html">BOOTP Relay Config</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_net_wiz.html">Network Config</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_server_serv.html">Modify DHCP Service</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_relay_serv.html">Modify BOOTP Service</A><BR>
+ <IMG SRC="art/tip2.gif" WIDTH=12 HEIGHT=10 BORDER=0 ALT=""><A HREF="dhcp_export_wiz.html">Export Data</A><br>
+ &nbsp;&nbsp;<A HREF="dhcp_import_wiz.html">Import Data</A><br>
+ &nbsp;&nbsp;<A HREF="dhcp_convert_wiz.html">Convert Data Store</A><P> <A HREF="dhcp_addr_ref.html"><STRONG>Addresses</STRONG></A><P>
+ <A HREF="dhcp_macro_ref.html"><STRONG>Macros</STRONG></A><P>
+ <A HREF="dhcp_option_ref.html"><STRONG>Options</STRONG></A><P>
+ <A HREF="dhcp_main_how.html"><STRONG>How To..</STRONG></A><P>
+ <A HREF="dhcp_main_menus.html"><STRONG>Menus</STRONG></A><P>
+<A HREF="dhcp_main_idx.html"><STRONG>Index</STRONG></A>
+
+ </td>
+
+<!-- End contents block -->
+
+<!-- Start column rule -->
+
+ <td colspan=1 width=5 bgcolor="#CCCCCC">&nbsp;</td>
+
+<!-- End column spacer -->
+
+<!-- Start column spacer -->
+
+ <td colspan=1 width=10 bgcolor="#FFFFFF">&nbsp;</td>
+
+<!-- End column spacer -->
+
+
+
+<!-- Start topic block -->
+
+<td colspan=1 valign="top" bgcolor="#ffffff" width="495">
+<P>&nbsp;</P>
+
+
+<H1>Export Data</H1>
+
+The Export Data wizard helps you
+transfer DHCP networks, IP addresses, macros, and
+options from one Solaris DHCP server to another. The
+wizard exports configuration data from this Solaris DHCP
+server so that it can then be imported onto
+another Solaris DHCP server. This effectively moves
+support of a set of DHCP clients from one server to
+another.
+<P>
+After you fill in the requested information in each
+dialog, click the right-arrow button at the bottom of the window to advance to the
+next dialog.
+<P>
+The Export Data wizard asks you for the
+following information.
+<P><HR NOSHADE><P>
+
+<table border=0 cellspacing=4 cellpadding=3 width=490>
+ <tr>
+ <td width=125 valign="top"><A
+ NAME="export_networks"><STRONG>Select networks</STRONG></A><br>
+ </td>
+ <td valign="top">This dialog lists in the Do Not Export column all the networks configured for DHCP service . Any network that you
+transfer to the Export column will be exported from this server. The IP addresses and their associated bindings will be placed in the export file
+so that they can be imported to another server. <P> To transfer a network to the Export column, select it and click the right-arrow button between the columns. <p>
+You can also choose to leave all the networks in the Do Not Export column. This allows you to proceed to selecting macros and options to export without exporting any networks.
+
+ </td>
+ </tr>
+
+ <tr>
+ <td width=125 valign="top"><A
+ NAME="export_macros"><STRONG>Select macros</STRONG></A><br>
+ </td>
+ <td valign="top">The Do Not Export column displays all macros defined for this DHCP server. To export a macro, select it and click the right-arrow button between the columns to transfer the macro to the Export column.
+ </td>
+ </tr>
+<tr><td width=125 valign="top"><A
+ NAME="export_options"><STRONG>Select options</STRONG></A><br>
+ </td>
+ <td valign="top">The Do Not Export column displays all options defined for this DHCP server. To export an option, select it and click the right-arrow button between the columns to transfer the option to the Export column. </td></tr>
+ <tr><td width=125 valign="top"><A
+ NAME="export_file"><STRONG>Export file</STRONG></A><br>
+ </td>
+ <td valign="top">The export file is the file to which the data you selected to export will be copied and compressed. You must provide a full path name to the file. The file should be placed in a shared directory that is accessible to the DHCP server that will be importing the data. </td></tr>
+ <tr><td width=125 valign="top"><A
+ NAME="export_delete"><STRONG>Delete exported data</STRONG></A><br>
+ </td>
+ <td valign="top">If you want the exported data to be deleted from the DHCP server after the export is complete, select this option. This is recommended, especially if you are exporting networks to avoid conflicting ownership of the IP addresses. ???
+</td></tr>
+
+<TR><TD COLSPAN="2">&nbsp;&nbsp;&nbsp;<A HREF="#top"><FONT
+size=2>return to top</FONT></A></TD></TR>
+
+ </table>
+
+<HR noshade size=2>
+
+
+
+<p>&nbsp;</p>
+
+
+<!-- Don't go past this line! -->
+
+ </td>
+
+<!-- End topic block -->
+
+</tr>
+
+</table>
+
+<!-- End topic table -->
+
+</body>
+</html>
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_import_wiz.html b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_import_wiz.html
new file mode 100644
index 0000000000..fde591a04a
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_import_wiz.html
@@ -0,0 +1,173 @@
+<!--
+ -- ident "%Z%%M% %I% %E% SMI"
+ --
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ -- Copyright (c) 2000-2001 by Sun Microsystems, Inc.
+ -- All rights reserved.
+ -->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<html>
+
+<head>
+<title>DHCP Manager Help: Import Data</title>
+<meta NAME="AUTHOR" CONTENT="smorgan">
+<meta NAME="KEYWORDS" CONTENT="DHCP">
+
+</head>
+
+<body bgcolor="#ffffff">
+
+<a name="top"></a>
+
+<table border=0 cellspacing=0 cellpadding=0 width=575>
+
+
+<!-- Start navigation banner -->
+
+<tr><td colspan=4 WIDTH="615" align="left" valign="top"><IMG
+SRC="art/bannersmc.gif" WIDTH=615 BORDER=0 ALIGN="LEFT" ALT="DHCP Manager Help">
+
+ </td>
+ </tr>
+
+<!-- End navigation banner -->
+
+
+<tr>
+
+<!-- Start contents block -->
+
+ <td colspan=1 valign="top" WIDTH="105">
+ <P>&nbsp;</P>
+ <STRONG><A HREF="dhcp_main_top.html">Overview</A></STRONG><P>
+ <STRONG><A HREF="dhcp_relay_ref.html">Servers and
+Relays</A></STRONG><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_config_wiz.html">DHCP
+Config</a><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_relay_config.html">BOOTP Relay Config</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_net_wiz.html">Network Config</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_server_serv.html">Modify DHCP Service</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_relay_serv.html">Modify BOOTP Service</A><br>
+ &nbsp;&nbsp;<A HREF="dhcp_export_wiz.html">Export Data</A><br>
+ <IMG SRC="art/tip2.gif" WIDTH=12 HEIGHT=10 BORDER=0 ALT=""><A HREF="dhcp_import_wiz.html">Import Data</A>
+ &nbsp;&nbsp;<A HREF="dhcp_convert_wiz.html">Convert Data Store</A><br><P>
+ <A HREF="dhcp_addr_ref.html"><STRONG>Addresses</STRONG></A><P>
+ <A HREF="dhcp_macro_ref.html"><STRONG>Macros</STRONG></A><P>
+ <A HREF="dhcp_option_ref.html"><STRONG>Options</STRONG></A><P>
+ <A HREF="dhcp_main_how.html"><STRONG>How To..</STRONG></A><P>
+ <A HREF="dhcp_main_menus.html"><STRONG>Menus</STRONG></A><P>
+<A HREF="dhcp_main_idx.html"><STRONG>Index</STRONG></A>
+
+ </td>
+
+<!-- End contents block -->
+
+<!-- Start column rule -->
+
+ <td colspan=1 width=5 bgcolor="#CCCCC">&nbsp;</td>
+
+<!-- End column rule -->
+
+<!-- Start column spacer -->
+
+ <td colspan=1 width=10 bgcolor="#FFFFFF">&nbsp;</td>
+
+<!-- End column spacer -->
+
+
+
+<!-- Start topic block -->
+
+<td colspan=1 valign="top" bgcolor="#ffffff" width="530">
+<P>&nbsp;</P>
+
+
+<H1>Import Data</H1>
+
+The Import Data wizard helps you
+transfer DHCP networks, IP addresses, macros, and
+options from one Solaris DHCP server to another. The
+wizard imports configuration data previously exported from
+another Solaris DHCP server. This effectively moves
+support of a set of DHCP clients from one server to
+another.
+<P>
+After you fill in the requested information in each
+dialog, click the right-arrow button to advance to the
+next dialog.
+<P>
+The Import Data wizard asks you for the
+following information.
+<P><HR NOSHADE><P>
+
+<table border=0 cellspacing=4 cellpadding=3 width=490>
+ <tr>
+ <td width=125 valign="top"><A
+ NAME="import_file"><STRONG>Import file</STRONG></A><br>
+ </td>
+ <td valign="top">Type the full path to the
+ file containing the data you want to import to
+ this DHCP server. The file must be an export file that
+ was produced by exporting data from a Solaris
+ DHCP server. The file is in a compressed binary data format.
+ </td>
+ </tr>
+
+ <tr>
+ <td width=125 valign="top"><A
+ NAME="overwrite_data"><STRONG>Overwrite existing data</STRONG></A><br>
+ </td>
+ <td valign="top">If the DHCP server on which
+ you are importing data has configuration data
+ for any of the same networks, IP addresses,
+ macros, or options being imported, that data will be
+ replaced with the imported data when
+ you select this option. By default,
+ conflicting data will not be overwritten.
+ </td>
+ </tr>
+
+ <TR><TD COLSPAN="2">&nbsp;&nbsp;&nbsp;<A HREF="#top"><small>return to top</small></A></TD></TR>
+
+ </table>
+
+<HR noshade size=2>
+
+
+
+<p>&nbsp;</p>
+
+
+<!-- Don't go past this line! -->
+
+ </td>
+
+<!-- End topic block -->
+
+</tr>
+
+</table>
+
+<!-- End topic table -->
+
+</body>
+</html>
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_macro_create.html b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_macro_create.html
new file mode 100644
index 0000000000..8bc76d24fd
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_macro_create.html
@@ -0,0 +1,196 @@
+<!--
+ -- ident "%W% %E% SMI"
+ --
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ -- Copyright (c) 2000-2001 by Sun Microsystems, Inc.
+ -- All rights reserved.
+ -->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<html>
+
+<head>
+<title>DHCP Manager Help: Create Macro</title>
+<meta NAME="AUTHOR" CONTENT="smorgan">
+<meta NAME="KEYWORDS" CONTENT="DHCP">
+
+</head>
+
+<body bgcolor="#FFFFFF">
+
+<a name="top"></a>
+
+<table border=0 cellspacing=0 cellpadding=0 width=615>
+
+<!-- Start navigation banner -->
+
+<tr><td colspan=4 WIDTH="615" align="center" valign="top"><IMG SRC="art/bannersmc.gif" WIDTH=615 BORDER=0 ALT="DHCP Manager Help">
+
+ </td>
+ </tr>
+
+<!-- End navigation banner -->
+
+
+<tr>
+
+<!-- Start contents block -->
+
+ <td colspan=1 valign="top" width=105>
+ <P>&nbsp;</P>
+
+ <A HREF="dhcp_main_top.html"><STRONG>Overview</STRONG></A><P>
+ <A HREF="dhcp_relay_ref.html"><STRONG>Servers and Relays</STRONG></A><P>
+ <A HREF="dhcp_addr_ref.html"><STRONG>IP Addresses</STRONG></A><P>
+
+
+<A HREF="dhcp_macro_ref.html"><STRONG>Macros</STRONG></A><BR>
+<IMG SRC="art/tip2.gif" WIDTH=12 HEIGHT=10 BORDER=0 ALT="">Create<BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#name"><EM>Name</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#option"><EM>Option</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#order"><EM>Up/Down</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#del"><EM>Delete</EM></A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_macro_dup.html">Duplicate</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_macro_mod.html">Modify</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_macro_del.html">Delete</A><P>
+
+<A HREF="dhcp_option_ref.html"><STRONG>Options</STRONG></A><P>
+
+<A HREF="dhcp_main_how.html"><STRONG>How To..</STRONG></A><P>
+<A HREF="dhcp_main_menus.html"><STRONG>Menus</STRONG></A><P>
+<A HREF="dhcp_main_idx.html"><STRONG>Index</STRONG></A>
+
+
+ </td>
+
+
+
+<!-- End contents block -->
+
+<!-- Start column rule -->
+
+ <td colspan=1 width=5 bgcolor="#CCCCCC">&nbsp;</td>
+
+<!-- End column spacer -->
+
+<!-- Start column spacer -->
+
+ <td colspan=1 width=10 bgcolor="#FFFFFF">&nbsp;</td>
+
+<!-- End column spacer -->
+
+
+
+<!-- Start topic block -->
+
+
+ <td colspan=1 valign="top" bgcolor="#ffffff">
+ <P>&nbsp;</P>
+
+
+<h1>Create Macro</h1>
+
+The Create Macro dialog box lets you to define a new macro in the <tt>dhcptab</tt> on the selected DHCP server. <p>
+See <a href="dhcp_macros_about.html#using">Using Macros and Options
+</a> for more information about defining DHCP macros.<P>
+
+
+The settings available in the Create Macro dialog box are described below.<p>
+<P><HR NOSHADE><P>
+
+
+ <table border=0 cellspacing=4 cellpadding=3>
+
+ <tr>
+ <td width=105 valign="top"><A NAME="name"><STRONG>Name</STRONG></A></td>
+ <td valign="top">Specify the name of the new macro. Remember, the name you choose determines the <A HREF="dhcp_macros_about.html#macrocat">macro category</a>. Macro names can be
+ a maximum of 128 alphanumeric characters.<P>
+
+ The important thing to remember when naming your macro is to give
+ it the same name as the resource or device in the category you want
+ to control. For example, to create a Client Class macro for
+ configuring an Ultra-5<small><sup>TM</sup></small> (which has the client class name
+ <tt>SUNW,Ultra5_10</tt>), name your macro <tt>SUNW.Ultra5_10</tt>.
+ </td>
+ </tr>
+
+ <tr>
+ <td width=105 valign="top"><A NAME="option"><STRONG>Option</STRONG></A></td>
+ <td valign="top">Specify the names of options and/or macros to include in the macro. The options or macros must
+already exist in the <tt>dhcptab</tt>. If the option does not exist, you
+can create it using the <A HREF="dhcp_option_create.html">Create Option</A> dialog box.
+To browse a list of defined options, right-click in the
+Options column. Options are displayed by category and type.
+To view the list of standard options, see <A HREF="dhcp_option_tags.html">Standard Options</A>.<P>
+
+To include a nested macro, enter Include in the Option column
+and then enter the name of the macro in the Value column.<P>
+To specify an option, enter the name of the option in the Option
+column, and the value of the option in the Value column. <P></TD>
+ </tr>
+
+ <tr>
+ <td width=125 valign="top"><A NAME="order"><STRONG>Move Up/Move Down</STRONG></A></td>
+ <td valign="top">Click Move Up or Move Down to change the processing order of
+ options and nested macros. The processing order is significant, as options and
+ macros processed last supersede conflicting values in those that are
+ processed first. </td>
+ </tr>
+
+ <tr>
+ <td width=125 valign="top"><A NAME="del"><STRONG>Delete</STRONG></A></td>
+ <td valign="top">Select an option and click Delete to remove it from the macro. </td>
+ </tr>
+
+
+ </table>
+
+
+
+<p>
+<strong>Notify DHCP Server of Change:</STRONG> Check the box if
+you want the DHCP server to reload the <tt>dhcptab</tt> after
+the changed macro information is written. Normally, you will want
+the server to reload the <tt>dhcptab</tt> so that clients will
+start receiving the new configuration data immediately.
+However, in some cases, you may wish to delay this reload.
+For example, you may need to modify several macros in order for
+configurations to be correct. <P>
+
+&nbsp;&nbsp;&nbsp;<A HREF="#top"><small>return to top</small></A>
+
+
+<p>&nbsp;</p>
+
+<!-- Don't go past this line! -->
+
+ </td>
+
+<!-- End topic block -->
+
+</tr>
+
+</table>
+
+<!-- End topic table -->
+
+</body>
+</html>
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_macro_del.html b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_macro_del.html
new file mode 100644
index 0000000000..21d9db779e
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_macro_del.html
@@ -0,0 +1,133 @@
+<!--
+ -- ident "%W% %E% SMI"
+ --
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ -- Copyright (c) 2000-2001 by Sun Microsystems, Inc.
+ -- All rights reserved.
+ -->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<html>
+
+<head>
+<title>DHCP Manager Help: Delete Macro</title>
+<meta NAME="AUTHOR" CONTENT="smorgan">
+<meta NAME="KEYWORDS" CONTENT="DHCP">
+
+</head>
+
+<body bgcolor="#FFFFFF">
+
+<a name="top"></a>
+
+<table border=0 cellspacing=0 cellpadding=0 width=615>
+
+<!-- Start navigation banner -->
+
+<tr><td colspan=4 WIDTH="615" align="center" valign="top"><IMG SRC="art/bannersmc.gif" WIDTH=615 BORDER=0 ALT="DHCP Manager Help">
+
+ </td>
+ </tr>
+
+<!-- End navigation banner -->
+
+
+<tr>
+
+<!-- Start contents block -->
+
+ <td colspan=1 valign="top" width=105>
+ <P>&nbsp;</P>
+
+ <A HREF="dhcp_main_top.html"><STRONG>Overview</STRONG></A><P>
+ <A HREF="dhcp_relay_ref.html">Servers and Relays</A><P>
+ <A HREF="dhcp_addr_ref.html"><STRONG>IP Addresses</STRONG></A><P>
+
+
+<A HREF="dhcp_macro_ref.html"><STRONG>Macros</STRONG></A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_macro_create.html">Create</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_macro_dup.html">Duplicate</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_macro_mod.html">Modify</A><BR>
+
+<IMG SRC="art/tip2.gif" WIDTH=12 HEIGHT=10 BORDER=0 ALT="">Delete<P>
+
+<A HREF="dhcp_option_ref.html"><STRONG>Options</STRONG></A><P>
+
+<A HREF="dhcp_main_how.html"><STRONG>How To..</STRONG></A><P>
+<A HREF="dhcp_main_menus.html"><STRONG>Menus</STRONG></A><P>
+<A HREF="dhcp_main_idx.html"><STRONG>Index</STRONG></A>
+
+
+ </td>
+
+
+
+<!-- End contents block -->
+
+<!-- Start column rule -->
+
+ <td colspan=1 width=5 bgcolor="#CCCCCC">&nbsp;</td>
+
+<!-- End column spacer -->
+
+<!-- Start column spacer -->
+
+ <td colspan=1 width=10 bgcolor="#FFFFFF">&nbsp;</td>
+
+<!-- End column spacer -->
+
+
+
+<!-- Start topic block -->
+
+
+ <td colspan=1 valign="top" bgcolor="#ffffff">
+ <P>&nbsp;</P>
+
+
+<h1>Delete Macro</h1>
+Click OK to remove the macro from the <tt>dhcptab</tt>.
+<P>
+
+<strong>Notify DHCP Server of Change:</STRONG> Check the box if
+you want the DHCP server to reload the <tt>dhcptab</tt> after
+the changed macro information is written. Normally, you want
+the server to reload the <tt>dhcptab</tt> so that clients
+start receiving the new configuration data immediately.
+However, in some cases, you may want to delay this reload.
+For example, you may need to make several changes in order for
+configurations to be correct. <P>
+<p>&nbsp;</p>
+
+<!-- Don't go past this line! -->
+
+ </td>
+
+<!-- End topic block -->
+
+</tr>
+
+</table>
+
+<!-- End topic table -->
+
+</body>
+</html>
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_macro_dup.html b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_macro_dup.html
new file mode 100644
index 0000000000..e658cc0c05
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_macro_dup.html
@@ -0,0 +1,181 @@
+<!--
+ -- ident "%W% %E% SMI"
+ --
+ Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+ -->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<html>
+
+<head>
+<title>DHCP Manager Help: Duplicate Macro</title>
+<meta NAME="AUTHOR" CONTENT="smorgan">
+<meta NAME="KEYWORDS" CONTENT="DHCP">
+
+</head>
+
+<body bgcolor="#FFFFFF">
+
+<a name="top"></a>
+
+<table border=0 cellspacing=0 cellpadding=0 width=615>
+
+<!-- Start navigation banner -->
+
+<tr><td colspan=4 WIDTH="615" align="center" valign="top"><IMG SRC="art/bannersmc.gif" WIDTH=615 BORDER=0 ALT="DHCP Manager Help">
+
+ </td>
+ </tr>
+
+<!-- End navigation banner -->
+
+
+<tr>
+
+<!-- Start contents block -->
+
+ <td colspan=1 valign="top" width=105>
+ <P>&nbsp;</P>
+
+ <A HREF="dhcp_main_top.html"><STRONG>Overview</STRONG></A><P>
+ <A HREF="dhcp_relay_ref.html"><STRONG>Servers and Relays</STRONG></A><P>
+ <A HREF="dhcp_addr_ref.html"><STRONG>IP Addresses</STRONG></A><P>
+
+
+<A HREF="dhcp_macro_ref.html"><STRONG>Macros</STRONG></A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_macro_create.html">Create</A><BR>
+
+<IMG SRC="art/tip2.gif" WIDTH=12 HEIGHT=10 BORDER=0 ALT="">Duplicate<BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#name"><EM>Name</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#option"><EM>Option</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#order"><EM>Up/Down</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#del"><EM>Delete</EM></A><BR>
+
+ &nbsp;&nbsp;<A HREF="dhcp_macro_mod.html">Modify</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_macro_del.html">Delete</A><P>
+
+<A HREF="dhcp_option_ref.html"><STRONG>Options</STRONG></A><P>
+
+<A HREF="dhcp_main_how.html"><STRONG>How To..</STRONG></A><P>
+<A HREF="dhcp_main_menus.html"><STRONG>Menus</STRONG></A><P>
+<A HREF="dhcp_main_idx.html"><STRONG>Index</STRONG></A>
+
+
+ </td>
+
+
+
+<!-- End contents block -->
+
+<!-- Start column rule -->
+
+ <td colspan=1 width=5 bgcolor="#CCCCCC">&nbsp;</td>
+
+<!-- End column spacer -->
+
+<!-- Start column spacer -->
+
+ <td colspan=1 width=10 bgcolor="#FFFFFF">&nbsp;</td>
+
+<!-- End column spacer -->
+
+
+
+<!-- Start topic block -->
+
+
+ <td colspan=1 valign="top" bgcolor="#ffffff">
+ <P>&nbsp;</P>
+
+<h1>Duplicate Macro</h1>
+
+The Duplicate Macro dialog box provides a simple way for you to create a
+new macro by copying some or all of the settings of an existing macro.<p>
+
+Refer to <a href="dhcp_macros_about.html">About Macros and Options</a>, for more information about defining DHCP macros.<p>
+
+The settings in the Duplicate Macro dialog box are described below.<p>
+<P><HR NOSHADE><P>
+
+
+ <table border=0 cellspacing=4 cellpadding=3>
+
+ <tr>
+ <td width=105 valign="top"><A NAME="name"><STRONG>Name</STRONG></A></td>
+ <td valign="top">Specify a name for the new macro.
+ Macro names can be a maximum of 128 alphanumeric characters.<br></td>
+ </tr>
+
+ <tr>
+ <td width=105 valign="top"><A NAME="option"><STRONG>Option</STRONG></A></td>
+ <td valign="top">Name of the options and/or other macros included in the macro.
+ You can add or delete options from this list.
+To display a list of defined options, right-click in the Options field. Options are displayed by category and type.
+</TD>
+
+ </tr>
+
+ <tr>
+ <td width=105 valign="top"><A NAME="order"><STRONG>Move Up/Move Down</STRONG></A></td>
+ <td valign="top">Click Move Up or Move Down to change the processing order of
+ options and nested macros. The processing order is significant, as options and
+ macros processed last supersede conflicting values in those that are
+ processed first. </td>
+ </tr>
+
+ <tr>
+ <td width=105 valign="top"><A NAME="del"><STRONG>Delete</STRONG></A></td>
+ <td valign="top">Select an option and click Delete to remove it from the macro. </td>
+ </tr>
+
+
+ </table>
+
+
+<P>
+<strong>Notify DHCP Server of Change:</STRONG> Check the box if
+you want the DHCP server to reload the <tt>dhcptab</tt> after
+the changed macro information is written. Normally, you want
+the server to reload the <tt>dhcptab</tt> so that clients
+start receiving the new configuration data immediately.
+However, in some cases, you may wish to delay this reload.
+For example, you may need to modify several macros in order for
+configurations to be correct. <P>
+&nbsp;&nbsp;&nbsp;<A HREF="#top"><small>return to top</small></A>
+
+<p>&nbsp;</p>
+
+<!-- Don't go past this line! -->
+
+ </td>
+
+<!-- End topic block -->
+
+</tr>
+
+</table>
+
+<!-- End topic table -->
+
+</body>
+</html>
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_macro_how.html b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_macro_how.html
new file mode 100644
index 0000000000..f05336b414
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_macro_how.html
@@ -0,0 +1,235 @@
+<!--
+ -- ident "%W% %E% SMI"
+ --
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ -- Copyright (c) 2000-2001 by Sun Microsystems, Inc.
+ -- All rights reserved.
+ -->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<html>
+
+<head>
+<title>DHCP Manager Help: How To... Macros</title>
+<meta NAME="AUTHOR" CONTENT="smorgan">
+<meta NAME="KEYWORDS" CONTENT="DHCP">
+
+</head>
+
+<body bgcolor="#FFFFFF">
+
+<a name="top"></a>
+
+<table border=0 cellspacing=0 cellpadding=0 width=615>
+
+<!-- Start navigation banner -->
+
+<tr><td colspan=4 WIDTH="615" align="center" valign="top"><IMG SRC="art/bannersmc.gif" WIDTH=615 BORDER=0 ALT="DHCP Manager Help">
+
+ </td>
+ </tr>
+
+<!-- End navigation banner -->
+
+
+<tr>
+
+<!-- Start contents block -->
+
+ <td colspan=1 valign="top" width=105>
+ <P>&nbsp;</P>
+ <A HREF="dhcp_main_top.html"><STRONG>Overview</STRONG></A><P>
+ <A HREF="dhcp_relay_ref.html"><STRONG>Servers and Relay Agents</STRONG></A><P>
+ <A HREF="dhcp_addr_ref.html"><STRONG>IP Addresses</STRONG></A><P>
+ <A HREF="dhcp_macro_ref.html"><STRONG>Macros</STRONG></A><P>
+ <A HREF="dhcp_option_ref.html"><STRONG>Options</STRONG></A><P>
+
+ <A HREF="dhcp_main_how.html"><STRONG>How To..</STRONG></A><BR>
+&nbsp;&nbsp;<A HREF="dhcp_relay_how.html">Servers/Relays</A><BR>
+&nbsp;&nbsp;<A HREF="dhcp_addr_how.html">Addresses</A><BR>
+<IMG SRC="art/tip2.gif" WIDTH=12 HEIGHT=10 BORDER=0 ALT="">Macros<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#viewmacro"><EM>View</EM></a><BR>
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#newmacro"><EM>Create</EM></a><BR>
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#dupmacro"><EM>Duplicate</EM></a><BR>
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#modmacro"><EM>Modify</EM></a><BR>
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#delmacro"><EM>Delete</EM></a><BR>
+
+&nbsp;&nbsp;<A HREF="dhcp_option_how.html">Options</A><P>
+<A HREF="dhcp_main_menus.html"><STRONG>Menus</STRONG></A><P>
+<A HREF="dhcp_main_idx.html"><STRONG>Index</STRONG></A>
+
+ </td>
+
+<!-- End contents block -->
+
+<!-- Start column rule -->
+
+ <td colspan=1 width=5 bgcolor="#CCCCCC">&nbsp;</td>
+
+<!-- End column spacer -->
+
+<!-- Start column spacer -->
+
+ <td colspan=1 width=10 bgcolor="#FFFFFF">&nbsp;</td>
+
+<!-- End column spacer -->
+
+
+
+<!-- Start topic block -->
+
+<td colspan=1 valign="top" bgcolor="#ffffff">
+<P>&nbsp;</P>
+<H1>How To: Macros</H1>
+
+<strong><big><A NAME="viewmacro">View Macros</A></big></strong><p>
+
+<strong>Choose the <A HREF="dhcp_macro_ref.html">Macros</A> tab in the main window.</strong><BR>
+DHCP Manager displays a list of macros that have been configured
+for this network. You can view the relationship between macros. Click on a macro to view its associated options. Click Properties from
+the Edit menu to view a list of options and their values. <P>
+
+<p>
+<hr noshade>
+<strong><big><A NAME="newmacro">Create a New Macro</A></big></strong><p>
+
+ <ol type=1>
+
+ <LI><strong>Choose the <A HREF="dhcp_macro_ref.html">Macros</A> tab in the main window.</strong><BR>
+ The DHCP Manager displays a list of macros that have been configured
+ for this network.</LI><P>
+ <LI><STRONG>Choose Create from the Edit menu.</STRONG><BR>
+ The <A HREF="dhcp_macro_create.html">Create Macro</A> dialog box opens.</LI><P>
+ <LI><strong>Enter a unique name for the new macro.</strong><BR>
+ The name must be unique within the server's DHCP table (<tt>dhcptab</TT>).<br>
+ (See the note about macro names at the end of this procedure.)
+</LI><P>
+ <LI><strong>Enter the definition of the macro.</strong><BR>
+ This can be built by including other macros, or by defining values for
+ options to be included in the macro. The <A HREF="dhcp_option_tags.html">standard options</A> are described in the <tt>dhcp_inittab</tt>(4) man page</LI><P>
+
+ <li><strong>Click OK to accept your settings.</strong><BR>
+
+ The new macro definition is stored in the <tt>dhcptab</tt>.<p></li>
+
+ </ol><P>
+
+<center>
+ <table border=1 cellspacing=3 cellpadding=5 WIDTH="380">
+
+ <tr>
+ <td valign="middle" align="left" BGCOLOR="#EBEBEB"><a name="name">The</a>
+ important thing to remember when naming your macro is to give it the
+ same name as the resource or device in the category you want to control.
+ For example, to create a Client Class macro for configuring Ultra-5s<small><sup>TM</sup></small>
+ (which have the client class name <tt>SUNW,Ultra5_10</tt>), name your
+ macro <tt>SUNW.Ultra5_10</tt>. See <a
+ href="dhcp_macros_about.html#using">Using Macros and Options</a> for more
+ information.</td>
+ </tr>
+
+ </table>
+
+</center>
+<P><HR NOSHADE><P>
+<strong><big><A NAME="dupmacro">Duplicate a Macro</A></big></strong>
+<P>
+The Duplicate command provides a simple way to create a
+new macro with some or all of the properties of an
+existing macro.<P>
+
+ <ol type=1>
+
+ <LI><strong>Choose the <A HREF="dhcp_macro_ref.html">Macros</A> tab in the main window.</strong><BR>
+
+ DHCP Manager displays a list of macros that have been configured for this network.<p></li>
+
+ <li><strong>Select the macro you want to copy, and then choose <A HREF="dhcp_macro_dup.html">Duplicate</A> from the
+ Edit window.</strong><BR>
+ The Duplicate Macro dialog box opens.</li><P>
+
+ <li><strong>Enter a name for the new macro.</strong><br>
+ (See the <a href="#name">note</a> about macro names at the end of the
+previous procedure.)<P></LI>
+ <LI><strong>Modify the macro settings as desired.</strong></LI>
+<P>
+<LI><STRONG>Click OK to accept your
+settings.</strong><BR>
+The new macro definition is stored in the <tt>dhcptab</tt>.</li>
+
+ </ol>
+
+<p><hr noshade><P>
+<strong><big><A NAME="modmacro">Modify a Macro</A></big></strong><p>
+
+ <ol type=1>
+
+ <LI><strong>Choose the <A HREF="dhcp_macro_ref.html">Macros</A> tab in the main window.</strong><BR>
+
+ DHCP Manager displays a list of macros that have been configured for this network.<p></li>
+
+ <li><strong>Select the macro you want to edit, and then choose <A HREF="dhcp_macro_mod.html">Properties</A> from the
+ Edit window.</strong><BR>
+ The Macro Properties dialog box opens.<P></li>
+
+ <li><strong>Modify the macro settings as desired and click OK.</strong></LI>
+ </ol>
+
+
+<p><HR noshade><P>
+<strong><big><A NAME="delmacro">Delete a Macro</A></big></strong><p>
+
+ <ol type=1>
+
+ <LI><strong>Choose the <A HREF="dhcp_macro_ref.html">Macros</A> tab in the main window.</strong><BR>
+ DHCP Manager displays a list of macros that have been configured
+ for this network.</LI><P>
+
+ <li><strong>Select the macro to delete, and then choose <A HREF="dhcp_macro_del.html">Delete</A> from the
+ Edit window.</strong><BR>
+ The Delete Macro dialog box opens.<P></li>
+
+ <LI><strong>Click OK.</strong><BR>
+ The macro definition is deleted from the <tt>dhcptab</tt>
+ database.<p></li>
+
+ </ol>
+
+
+
+&nbsp;&nbsp;&nbsp;<A HREF="#top"><small>return to top</small></A>
+<p>&nbsp;</p>
+
+
+<!-- Don't go past this line! -->
+
+ </td>
+
+<!-- End topic block -->
+
+</tr>
+
+</table>
+
+<!-- End topic table -->
+
+</body>
+</html>
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_macro_mod.html b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_macro_mod.html
new file mode 100644
index 0000000000..9b23fde512
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_macro_mod.html
@@ -0,0 +1,189 @@
+<!--
+ -- ident "%W% %E% SMI"
+ --
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ -- Copyright (c) 2000-2001 by Sun Microsystems, Inc.
+ -- All rights reserved.
+ -->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<html>
+
+<head>
+<title>DHCP Manager Help: Modify Macro</title>
+<meta NAME="AUTHOR" CONTENT="smorgan">
+<meta NAME="KEYWORDS" CONTENT="DHCP">
+
+</head>
+
+<body bgcolor="#FFFFFF">
+
+<a name="top"></a>
+
+<table border=0 cellspacing=0 cellpadding=0 width=615>
+
+<!-- Start navigation banner -->
+
+<tr><td colspan=4 WIDTH="615" align="center" valign="top"><IMG SRC="art/bannersmc.gif" WIDTH=615 BORDER=0 ALT="DHCP Manager Help">
+
+ </td>
+ </tr>
+
+<!-- End navigation banner -->
+
+
+<tr>
+
+<!-- Start contents block -->
+
+ <td colspan=1 valign="top" width=105>
+ <P>&nbsp;</P>
+
+ <A HREF="dhcp_main_top.html"><STRONG>Overview</STRONG></A><P>
+ <A HREF="dhcp_relay_ref.html"><STRONG>Servers and Relays</STRONG></A><P>
+ <A HREF="dhcp_addr_ref.html"><STRONG>IP Addresses</STRONG></A><P>
+
+
+<A HREF="dhcp_macro_ref.html"><STRONG>Macros</STRONG></A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_macro_create.html">Create</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_macro_dup.html">Duplicate</A><BR>
+
+<IMG SRC="art/tip2.gif" WIDTH=12 HEIGHT=10 BORDER=0 ALT="">Modify<BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#name"><EM>Name</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#option"><EM>Option</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#order"><EM>Up/Down</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#del"><EM>Delete</EM></A><BR>
+
+ &nbsp;&nbsp;<A HREF="dhcp_macro_del.html">Delete</A><P>
+
+<A HREF="dhcp_option_ref.html"><STRONG>Options</STRONG></A><P>
+
+<A HREF="dhcp_main_how.html"><STRONG>How To..</STRONG></A><P>
+<A HREF="dhcp_main_menus.html"><STRONG>Menus</STRONG></A><P>
+<A HREF="dhcp_main_idx.html"><STRONG>Index</STRONG></A>
+
+
+ </td>
+
+
+
+<!-- End contents block -->
+
+<!-- Start column rule -->
+
+ <td colspan=1 width=5 bgcolor="#CCCCCC">&nbsp;</td>
+
+<!-- End column spacer -->
+
+<!-- Start column spacer -->
+
+ <td colspan=1 width=10 bgcolor="#FFFFFF">&nbsp;</td>
+
+<!-- End column spacer -->
+
+
+
+<!-- Start topic block -->
+
+
+ <td colspan=1 valign="top" bgcolor="#ffffff">
+ <P>&nbsp;</P>
+
+<h1>Modify Macro</h1>
+
+The Macro Properties dialog box displays the current settings for a macro, and lets you modify these settings in the <tt>dhcptab</tt> on the selected DHCP server. <p>
+<p>
+To modify an entry, double-click the entry, enter the new value, and then press Enter.
+To display a list of defined options, right-click in the Options or Values field. Options are listed by category and type.<P>
+The settings in the Macro Properties menu are described below.
+<P><HR NOSHADE><P>
+
+
+ <table border=0 cellspacing=4 cellpadding=3>
+
+ <tr>
+ <td width=105 valign="top"><a name="name"><STRONG>Name</STRONG></a></td>
+ <td valign="top">The macro name; specify a new name, if
+ desired. Remember, the name you choose determines the <a
+ href="dhcp_macros_about.html#using">macro category</a>. Macro names can be
+ a maximum of 128 alphanumeric characters.<P>
+ The important thing to remember when naming your macro is to give
+ it the same name as the resource or device in the category you want
+ to control. For example, to create a Client Class macro for
+ configuring a Ultra-5<small><sup>TM</sup></small> (which has the client class name
+ <tt>SUNW,Ultra5_10</tt>), name your macro <tt>SUNW.Ultra5_10</tt>.
+</td>
+ </tr>
+
+ <tr>
+ <td width=105 valign="top"><A NAME="option"><STRONG>Option</STRONG></A></td>
+ <td valign="top">The options and/or other macros included in the macro. You can add or delete options, and change the order of options in the list.<P>
+To browse a list of defined options, right-click in the
+Options column. Options are displayed by category and type.<P>
+To include a nested macro, enter Include in the Option column
+and then enter the name of the macro in the Value column.<P>
+To specify an option, enter the name of the option in the Option
+column, and the value of the option in the Value column. <P></td>
+ </tr>
+
+ <tr>
+ <td width=105 valign="top"><A NAME="order"><STRONG>Move Up/Move Down</STRONG></A></td>
+ <td valign="top">Click Move Up or Move Down to change the processing order of
+ options and nested macros. The processing order is significant, as options and
+ macros processed last supersede conflicting values in those that are
+ processed first. </td>
+ </tr>
+
+ <tr>
+ <td width=105 valign="top"><A NAME="del"><STRONG>Delete</STRONG></A></td>
+ <td valign="top">Select an option and click Delete to remove it from the macro. </td>
+ </tr>
+
+
+ </table>
+
+<p>&nbsp;</p>
+<strong>Notify DHCP Server of Change:</STRONG> Check the box if
+you want the DHCP server to reload the <tt>dhcptab</tt> after
+the changed macro information is written. Normally, you want
+the server to reload the <tt>dhcptab</tt> so that clients
+start receiving the new configuration data immediately.
+However, in some cases, you may want to delay this reload.
+For example, you may need to modify several macros in order for
+configurations to be correct. <P>
+&nbsp;&nbsp;&nbsp;<A HREF="#top"><small>return to top</small></A>
+
+
+<p>&nbsp;</p>
+
+<!-- Don't go past this line! -->
+
+ </td>
+
+<!-- End topic block -->
+
+</tr>
+
+</table>
+
+<!-- End topic table -->
+</body>
+</html>
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_macro_ref.html b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_macro_ref.html
new file mode 100644
index 0000000000..fa19afe858
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_macro_ref.html
@@ -0,0 +1,155 @@
+<!--
+ -- ident "%W% %E% SMI"
+ --
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ -- Copyright (c) 2000-2001 by Sun Microsystems, Inc.
+ -- All rights reserved.
+ -->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<html>
+
+<head>
+<title>DHCP Manager Help: Macros</title>
+<meta NAME="AUTHOR" CONTENT="smorgan">
+<meta NAME="KEYWORDS" CONTENT="DHCP">
+
+</head>
+
+<body bgcolor="#FFFFFF">
+
+<a name="top"></a>
+
+<table border=0 cellspacing=0 cellpadding=0 width=615>
+
+<!-- Start navigation banner -->
+
+<tr><td colspan=4 WIDTH="615" align="center" valign="top"><IMG SRC="art/bannersmc.gif" WIDTH=615 BORDER=0 ALT="DHCP Manager Help">
+
+ </td>
+ </tr>
+
+<!-- End navigation banner -->
+
+
+<tr>
+
+<!-- Start contents block -->
+
+ <td colspan=1 valign="top" width=105>
+ <P>&nbsp;</P>
+
+ <A HREF="dhcp_main_top.html"><STRONG>Overview</STRONG></A><P>
+ <A HREF="dhcp_relay_ref.html"><STRONG>Servers and Relays</STRONG></A><P>
+
+ <A HREF="dhcp_addr_ref.html"><STRONG>IP Addresses</STRONG></A><P>
+
+
+<IMG SRC="art/tip2.gif" WIDTH=12 HEIGHT=10 BORDER=0 ALT=""><STRONG>Macros</STRONG><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_macro_create.html">Create</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_macro_dup.html">Duplicate</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_macro_mod.html">Modify</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_macro_del.html">Delete</A><P>
+
+<A HREF="dhcp_option_ref.html"><STRONG>Options</STRONG></A><P>
+
+<A HREF="dhcp_main_how.html"><STRONG>How To..</STRONG></A><P>
+<A HREF="dhcp_main_menus.html"><STRONG>Menus</STRONG></A><P>
+<A HREF="dhcp_main_idx.html"><STRONG>Index</STRONG></A>
+
+
+ </td>
+
+
+
+<!-- End contents block -->
+
+<!-- Start column rule -->
+
+ <td colspan=1 width=5 bgcolor="#CCCCCC">&nbsp;</td>
+
+<!-- End column spacer -->
+
+<!-- Start column spacer -->
+
+ <td colspan=1 width=10 bgcolor="#FFFFFF">&nbsp;</td>
+
+<!-- End column spacer -->
+
+
+
+<!-- Start topic block -->
+
+
+ <td colspan=1 valign="top" bgcolor="#ffffff">
+ <P>&nbsp;</P>
+<h1>Macros</h1>
+
+When you select the Macros tab in the main window, DHCP Manager displays a list of
+macros in the server's <tt>dhcptab</tt>. <p>
+The macros are listed alphabetically by name. Macros that include other macros
+are identified by a folder icon<IMG SRC="art/dot2.gif" WIDTH=14 HEIGHT=13 BORDER=0 ALT=""><IMG SRC="art/folder.gif" WIDTH=25 HEIGHT=15 BORDER=0>. To view the included macro(s), click
+the open button to the left of the folder icon. Click a macro icon <IMG SRC="art/macro2.gif" BORDER=0> to view a list of options assigned to this macro, and their values. <P>
+You can <A HREF="dhcp_macro_create.html">create</A> a new macro, <A HREF="dhcp_macro_mod.html">modify</A>, <A HREF="dhcp_macro_dup.html">copy</A>, or <A HREF="dhcp_macro_del.html">delete</A> an existing macro by selecting it and
+clicking the appropriate entry in the Edit menu.<P>
+For more information about macros and how to use them, see <A HREF="dhcp_macros_about.html">About Macros and Options</A>.<P>
+The column settings are described below.
+<P><HR NOSHADE><P>
+<table border=0 cellspacing=4 cellpadding=3>
+
+
+ <TR>
+ <td width=100 valign="top"><a name="name"><STRONG>Macro</STRONG></a></td>
+ <td valign="top">The name of the macro. A macro may include one or more macros.</td>
+ </tr>
+
+ <tr>
+ <td width=100 valign="top"><a name="option"><STRONG>Option</STRONG></a></td>
+ <td valign="top">The name of the option(s) included in the selected macro. </td>
+ </tr>
+ <tr>
+ <td width=100 valign="top"><a name="val"><STRONG>Value</STRONG></a></td>
+ <td valign="top">The value of the option.</td>
+</tr>
+
+ </table>
+
+
+
+
+
+<p>&nbsp;</p>
+
+
+<!-- Don't go past this line! -->
+
+ </td>
+
+<!-- End topic block -->
+
+</tr>
+
+</table>
+
+<!-- End topic table -->
+
+</body>
+</html>
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_macro_view.html b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_macro_view.html
new file mode 100644
index 0000000000..82e2a67126
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_macro_view.html
@@ -0,0 +1,279 @@
+<!--
+ -- ident "%W% %E% SMI"
+ --
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ -- Copyright (c) 2000-2001 by Sun Microsystems, Inc.
+ -- All rights reserved.
+ -->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<html>
+
+<head>
+<title>DHCP Manager Help: Macro Menus</title>
+<meta NAME="AUTHOR" CONTENT="smorgan">
+<meta NAME="KEYWORDS" CONTENT="DHCP">
+
+</head><body bgcolor="#FFFFFF">
+
+<a name="top"></a>
+
+<table border=0 cellspacing=0 cellpadding=0 width=625>
+
+<!-- Start navigation banner -->
+
+<tr><td colspan=4 WIDTH="615" align="center" valign="top"><IMG SRC="art/bannersmc.gif" WIDTH=615 BORDER=0 ALT="DHCP Manager Help">
+
+ </td>
+ </tr>
+
+<!-- End navigation banner -->
+
+
+<tr>
+
+<!-- Start contents block -->
+
+ <td colspan=1 valign="top" width=105>
+ <P>&nbsp;</P>
+
+ <A HREF="dhcp_main_top.html"><STRONG>Overview</STRONG></A><P>
+ <A HREF="dhcp_relay_ref.html"><STRONG>Servers and Relays</STRONG></A><P>
+ <A HREF="dhcp_addr_ref.html"><STRONG>IP Addresses</STRONG></A><P>
+ <A HREF="dhcp_macro_ref.html"><STRONG>Macros</STRONG></A><P>
+ <A HREF="dhcp_option_ref.html"><STRONG>Options</STRONG></A><P>
+
+ <A HREF="dhcp_main_how.html"><STRONG>How To..</STRONG></A><P>
+ <A HREF="dhcp_main_menus.html"><STRONG>Menus</STRONG></A><BR>
+&nbsp;&nbsp;<A HREF="dhcp_macro_view.html">Address View</A><BR>
+<IMG SRC="art/tip2.gif" WIDTH=12 HEIGHT=10 BORDER=0 ALT="">Macro View<BR>
+&nbsp;&nbsp;<A HREF="dhcp_option_view.html">Option View</A><P>
+<A HREF="dhcp_main_idx.html"><STRONG>Index</STRONG></A>
+
+
+ </td>
+
+
+
+<!-- End contents block -->
+
+<!-- Start column rule -->
+
+ <td colspan=1 width=5 bgcolor="#CCCCCC">&nbsp;</td>
+
+<!-- End column spacer -->
+
+<!-- Start column spacer -->
+
+ <td colspan=1 width=10 bgcolor="#FFFFFF">&nbsp;</td>
+
+<!-- End column spacer -->
+
+
+
+<!-- Start topic block -->
+
+
+ <td colspan=1 valign="top" bgcolor="#ffffff" WIDTH="495">
+
+ <P>&nbsp;</P>
+
+<H1>Macro View Menus</H1>
+
+<table ALIGN="LEFT" BORDER="0" CELLPADDING="3" WIDTH="495">
+
+<TR>
+<td colspan=1 width=20>&nbsp;</td>
+<TH colspan=1 align=left valign="top" width=150>
+ <FONT SIZE="+1">Titles</FONT><P></TH>
+ <TH align=left valign="top"><FONT SIZE="+1">Description</FONT><P></TH>
+
+</TR>
+
+
+<tr>
+<td colspan=1 width=20>&nbsp;</td>
+ <td colspan=1 align=left valign="top" width=150 bgcolor="#FFFFFF"><U>File Menu</U></td>
+ <Td></Td>
+ </TR>
+
+ <tr><td colspan=1 width=20>&nbsp;</td>
+ <td colspan=1 align=left valign="top" width=150>&nbsp;&nbsp;<STRONG>Exit</STRONG></TD>
+ <TD ALIGN="LEFT" VALIGN="TOP">Exit from the application.<P>&nbsp;</P></TD>
+ </TR>
+
+<tr>
+<td colspan=1 width=20>&nbsp;</td>
+ <td colspan=1 align=left valign="top" width=150 bgcolor="#FFFFFF"><U>Edit Menu</U></td>
+ <Td></Td>
+ </TR>
+
+ <tr><td colspan=1 width=20>&nbsp;</td>
+<TD COLSPAN="1" ALIGN="left" VALIGN="TOP" width="150">&nbsp;&nbsp;<STRONG>Create</STRONG></TD>
+
+ <TD COLSPAN="2" ALIGN="LEFT" VALIGN="TOP">
+Create new macro.</TD></TR>
+
+<tr><td colspan=1 width=20>&nbsp;</td>
+<td colspan=1 align=left valign="top">&nbsp;&nbsp;<STRONG>Delete</STRONG></TD>
+<TD COLSPAN="2" ALIGN="LEFT" VALIGN="TOP">Delete the selected macro(s).</TD>
+</TR>
+
+<tr>
+<td colspan=1 width=20>&nbsp;</td>
+<td colspan=1 align=left valign="top" width=150>&nbsp;&nbsp;<STRONG>Duplicate</STRONG></TD>
+<TD COLSPAN="2" ALIGN="LEFT" VALIGN="TOP">Copy the selected macro.</TD>
+</TR>
+
+<tr>
+<td colspan=1 width=20>&nbsp;</td>
+<td colspan=1 align=left valign="top" width=150>&nbsp;&nbsp;<STRONG>Properties</STRONG></TD>
+<TD COLSPAN="2" ALIGN="LEFT" VALIGN="TOP">Modify the properties of the selected macro.
+<P>&nbsp;</P></TD>
+</TR>
+
+
+
+<tr><td colspan=1 width=20>&nbsp;</td>
+ <td colspan=1 align=left valign="top" width=150 bgcolor="#FFFFFF">
+<U>View Menu</U><P></td>
+<TD></TD></TR>
+
+<tr><td colspan=1 width=20>&nbsp;</td>
+<td colspan=1 align=left valign="top">&nbsp;&nbsp;<STRONG>Refresh</STRONG></TD>
+<TD ALIGN="LEFT" VALIGN="TOP">Refresh the data from the server.</TD>
+</TR>
+
+ <tr><td colspan=1 width=20>&nbsp;</td>
+<td colspan=1 align=left valign="top">&nbsp;&nbsp;<STRONG>Show Grid</STRONG></TD>
+<TD ALIGN="LEFT" VALIGN="TOP">Turn on grid lines in display.<P>&nbsp;</P></TD>
+</TR>
+
+
+<tr><td colspan=1 width=20>&nbsp;</td>
+ <td colspan=1 align=left valign="top" width=150 bgcolor="#FFFFFF">
+<U>Service Menu</U><P></td>
+ <Td></Td></TR>
+
+ <tr><td colspan=1 width=20>&nbsp;</td>
+<td colspan=1 align=left valign="top">&nbsp;&nbsp;<STRONG>Restart</STRONG></TD>
+<TD ALIGN="LEFT" VALIGN="TOP">Stop and restart the DHCP daemon.</TD>
+</TR>
+
+<tr><td colspan=1 width=20>&nbsp;</td>
+<td colspan=1 align=left valign="top">&nbsp;&nbsp;<STRONG>Stop</STRONG></TD>
+<TD ALIGN="LEFT" VALIGN="TOP">Stop the DHCP daemon.</TD>
+</TR>
+
+<tr><td colspan=1 width=20>&nbsp;</td>
+<td colspan=1 align=left valign="top">&nbsp;&nbsp;<STRONG>Start</STRONG></TD>
+<TD ALIGN="LEFT" VALIGN="TOP">Start the DHCP daemon.</TD>
+</TR>
+
+<tr><td colspan=1 width=20>&nbsp;</td>
+<td colspan=1 align=left valign="top">&nbsp;&nbsp;<STRONG>Disable</STRONG></TD>
+<TD ALIGN="LEFT" VALIGN="TOP">Disable the DHCP server or BOOTP relay agent.</TD>
+</TR>
+
+<tr><td colspan=1 width=20>&nbsp;</td>
+<td colspan=1 align=left valign="top">&nbsp;&nbsp;<STRONG>Enable</STRONG></TD>
+<TD ALIGN="LEFT" VALIGN="TOP">Enable the DHCP server or BOOTP relay agent.</TD>
+</TR>
+
+<tr><td colspan=1 width=20>&nbsp;</td>
+<td colspan=1 align=left valign="top">&nbsp;&nbsp;<STRONG>Modify</STRONG></TD>
+<TD ALIGN="LEFT" VALIGN="TOP">Customize services of DHCP server or BOOTP relay agent.</TD>
+</TR>
+
+<tr><td colspan=1 width=20>&nbsp;</td>
+<td colspan=1 align=left valign="top">&nbsp;&nbsp;<STRONG>Export Data</STRONG></TD>
+<TD ALIGN="LEFT" VALIGN="TOP">Copy from the DHCP data store selected IP addresses, macros, and options to a file for the purpose of importing to a DHCP server.</TD>
+</TR>
+
+<tr><td colspan=1 width=20>&nbsp;</td>
+<td colspan=1 align=left valign="top">&nbsp;&nbsp;<STRONG>Import Data</STRONG></TD>
+<TD ALIGN="LEFT" VALIGN="TOP">Bring into the DHCP data store IP addresses, macros, and options that were exported from a DHCP server. </TD>
+</TR>
+
+<tr><td colspan=1 width=20>&nbsp;</td>
+<td colspan=1 align=left valign="top">&nbsp;&nbsp;<STRONG>Convert Data Store</STRONG></TD>
+<TD ALIGN="LEFT" VALIGN="TOP">Configure the DHCP server to use a new data store and convert existing data to new data store format. </TD>
+</TR>
+<tr><td colspan=1 width=20>&nbsp;</td>
+<td colspan=1 align=left valign="top">&nbsp;&nbsp;<STRONG>Unconfigure</STRONG></TD>
+<TD ALIGN="LEFT" VALIGN="TOP">Unconfigure the DHCP server or BOOTP relay agent.<P>&nbsp;</P></TD>
+</TR>
+
+
+
+<tr><td colspan=1 width=20>&nbsp;</td>
+ <td colspan=1 align=left valign="top" width=150 bgcolor="#FFFFFF">
+<U>Help Menu</U></td>
+ <Td>Display the online help.<P>&nbsp;</P></Td></TR>
+ <tr><td colspan=1 width=20>&nbsp;</td>
+ </TR>
+
+
+ <tr><td colspan=1 width=20>&nbsp;</td>
+ <td colspan=1 align=left valign="top" width=150 bgcolor="#FFFFFF">
+<U>Find</U></td>
+ <Td>Search for a Macro entry.<P>&nbsp;</P></Td></TR>
+
+ <tr><td colspan=1 width=20>&nbsp;</td>
+ <td colspan=1 align=left valign="top" width=150 bgcolor="#FFFFFF">
+<U>Column Entries</U></td>
+ <Td></Td></TR>
+ <tr><td colspan=1 width=20>&nbsp;</td>
+ <td colspan=1 align=left valign="top" width=150 bgcolor="#FFFFFF">
+&nbsp;&nbsp;<STRONG>Macros</STRONG></td>
+ <Td>Macros are listed by name. Click the macro to view included macros and options.
+ Double-click to display the Macro Properties dialog box.</Td></TR>
+ <tr><td colspan=1 width=20>&nbsp;</td>
+ <td colspan=1 align=left valign="top" width=150 bgcolor="#FFFFFF">
+&nbsp;&nbsp;<STRONG>Option</STRONG></td>
+ <Td>Name of the selected option</Td></TR>
+
+ <tr><td colspan=1 width=20>&nbsp;</td>
+ <td colspan=1 align=left valign="top" width=150 bgcolor="#FFFFFF">
+&nbsp;&nbsp;<STRONG>Value</STRONG></td>
+ <Td>Option value<P>&nbsp;</P></Td></TR><TR>
+ <TD COLSPAN="3">&nbsp;&nbsp;&nbsp;<A HREF="#top"><small>return to top</small></A>
+ </TD></TR>
+ </TABLE>
+
+
+
+
+
+<!-- Don't go past this line! -->
+
+ </td>
+
+<!-- End topic block -->
+
+</tr>
+
+</table>
+
+<!-- End topic table -->
+
+</body>
+</html>
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_macros_about.html b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_macros_about.html
new file mode 100644
index 0000000000..63be531bd5
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_macros_about.html
@@ -0,0 +1,296 @@
+<!--
+ -- ident "%Z%%M% %I% %E% SMI"
+ --
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ -- Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ -- Use is subject to license terms.
+ -->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<html>
+
+<head>
+<title>DHCP Manager Help: About Macros and Options</title>
+<meta NAME="AUTHOR" CONTENT="smorgan">
+<meta NAME="KEYWORDS" CONTENT="DHCP">
+
+</head>
+<body bgcolor="#FFFFFF">
+
+<a name="top"></a>
+
+<table border=0 cellspacing=0 cellpadding=0 width=645>
+
+<!-- Start navigation banner -->
+
+<tr><td colspan=4 WIDTH="615" align="center" valign="top"><IMG SRC="art/bannersmc.gif" WIDTH=615 BORDER=0 ALT="DHCP Manager Help">
+
+ </td>
+ </tr>
+
+<!-- End navigation banner -->
+
+
+<tr>
+
+<!-- Start contents block -->
+
+ <td colspan=1 valign="top" width=105>
+ <P>&nbsp;</P>
+ <A HREF="dhcp_main_top.html"><STRONG>Overview</STRONG></A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_solaris_about.html">Solaris DHCP</A><BR>
+ <IMG SRC="art/tip2.gif" WIDTH=12 HEIGHT=10 BORDER=0 ALT="">About Macros<BR>
+ &nbsp;&nbsp;<A HREF="dhcp_main_hlp.html">Getting Help</A><P>
+ <A HREF="dhcp_relay_ref.html"><STRONG>Servers and Relays</STRONG></A><P>
+ <A HREF="dhcp_net_ref.html"><STRONG>Networks</STRONG></A><P>
+ <A HREF="dhcp_addr_ref.html"><STRONG>Addresses</STRONG></A><P>
+ <A HREF="dhcp_macro_ref.html"><STRONG>Macros</STRONG></A><P>
+ <A HREF="dhcp_option_ref.html"><STRONG>Options</STRONG></A><P>
+ <A HREF="dhcp_main_how.html"><STRONG>How To..</STRONG></A><P>
+ <A HREF="dhcp_main_idx.html"><STRONG>Index</STRONG></A>
+
+ </td>
+
+<!-- End contents block -->
+
+<!-- Start column rule -->
+
+ <td colspan=1 width=5 bgcolor="#CCCCCC">&nbsp;</td>
+
+<!-- End column spacer -->
+
+<!-- Start column spacer -->
+
+ <td colspan=1 width=10 bgcolor="#FFFFFF">&nbsp;</td>
+
+<!-- End column spacer -->
+
+
+
+<!-- Start topic block -->
+
+<td colspan=1 valign="top" bgcolor="#ffffff" width=495>
+
+
+
+<P>&nbsp;</P>
+
+<h1>About Macros and Options</H1>
+
+
+In a DHCP environment, <em>macros</em> are containers for <em>option values</em> that are
+passed as configuration parameters from the DHCP server to the DHCP client. For example,
+you might define a macro that contains options specifying a DNS server and print server
+to be used by all clients using IP addresses owned by a particular DHCP server.<p>
+
+<em>Options</em> specify the format in which option values are
+defined in the DHCP server's <tt>dhcptab</tt> (in which all DHCP
+options and macros are stored). For example, option category, data type, vendor, and granularity all determine the <em>shape</em> of the options
+contained in your macros. Creating and modifying options is described
+further in the help pages for <a href="dhcp_option_create.html">Create Option</a> and
+<a href="dhcp_option_mod.html">Modify Option</a>.<p>
+
+<h1>
+ Macro Processing
+ </h1>
+ <p>
+ Remember the following points when naming DHCP macros and adding
+ options to macros:
+ </p>
+ <ol>
+ <li>
+ <strong>Automatic processing</strong> occurs for Client Class,
+ Network, and Client ID category macros when the macro category
+ matches the client's class, network address, or client
+ identifier.
+ <br>
+ </li>
+ <li>
+ <strong>Macro categories</strong> determine the order in which
+ macros are processed automatically.
+ <br>
+ </li>
+ <li>
+ <strong>Macro names</strong> determine macro categories, with the
+ exception of IP address macros, which are categorized by their
+ assignment to an IP address.<br>
+
+ <p>
+ For most macros, make your macro names <strong>match the names
+ of the resources or devices</strong> to which you want the
+ macros to apply. For example:
+ </p>
+ <br>
+ <table cellpadding="4" cellspacing="4" border="1">
+ <tr>
+ <td bgcolor="#CCCFFF" valign="middle" align="left">
+ <p>
+ For Clients...
+ </p>
+ </td>
+ <td bgcolor="#CCCFFF" valign="middle" align="left">
+ <p>
+ Name Your Macro
+ </p>
+ </td>
+ </tr>
+ <tr>
+ <td valign="top">
+ <p>
+ Of a particular client class
+ </p>
+ </td>
+ <td valign="top">
+ <p>
+ Same name as the DHCP client's client class. The
+ client class is determined by the client vendor. For
+ example, the client class name for a Sun Blade <sup><font
+ size="-2"> TM</font></sup> 150 client is SUNW,Sun-Blade-100 so
+ you must name your macro <tt> SUNW.Sun-Blade-100</tt> . On
+ Solaris DHCP client systems, you
+ can determine the client class by typing the command
+ <tt> uname -i</tt> on the client machine. Note that
+ macro names cannot contain commas; if a comma appears
+ in the client class, replace it with a period in the
+ macro name.
+ </p>
+ </td>
+ </tr>
+ <tr>
+ <td valign="top">
+ <p>
+ On a particular network
+ </p>
+ </td>
+ <td valign="top">
+ <p>
+ Same as the IP address of the network through which the
+ client is connecting; for example, name your macro <tt>
+ 10.0.0.0</tt>.
+ </p>
+ </td>
+ </tr>
+ <tr>
+ <td valign="top">
+ <p>
+ Using a particular IP Address
+ </p>
+ </td>
+ <td valign="top">
+ <p>
+ Any name, but using the DHCP server's hostname or IP
+ address is recommended; for example, name your macro
+ <tt> shiva</tt> or <tt> 125.53.224.45</tt> . This kind
+ of macro is valuable for use as a configuration macro
+ for all clients obtaining configuration information
+ from this DHCP server.
+ </p>
+ </td>
+ </tr>
+ <tr>
+ <td valign="top">
+ <p>
+ Identified by a particular client ID
+ </p>
+ </td>
+ <td valign="top">
+ <p>
+ Same as the unique client identifier; for example,
+ name your macro <tt> 010800201112B7</tt> .
+ </p>
+ </td>
+ </tr>
+ </table>
+ <br>
+ </li>
+ <li>
+ <strong>Order is important.</strong> The order in which macros are
+ processed is significant. If an option is contained in more than
+ one macro, the option value passed to the client is the value
+ assigned in the macro processed last. Macros are processed in the
+ order shown in the illustration.<br>
+ <br>
+
+ <p>
+ <img src="art/macroflow.gif" alt="order of macro processing" vspace="0" hspace="0" border="0"
+ height="225" width="400">
+ </p>
+ <p>
+ As shown in the illustration, macro processing progresses from
+ the general to the specific.
+ </p>
+ <ul type="disc">
+ <li>
+ <p>
+ Client Class macros are processed first.<br>
+ </p>
+ </li>
+ <li>
+ <p>
+ Network macros are processed second, superseding any
+ competing settings in Client Class macros.<br>
+ </p>
+ </li>
+ <li>
+ <p>
+ IP Address macros are processed third, superseding any
+ competing settings in previous macros.<br>
+ </p>
+ </li>
+ <li>
+ <p>
+ Client ID macros are processed last, superseding any
+ competing settings in all previous macros.<br>
+ </p>
+ </li>
+ </ul>
+ </li>
+ <li>
+ <p>
+ <strong>Macros can include other macros.</strong> Regardless of a
+ macro's category, it can include other macros so that, for example,
+ a client class macro could be called from an IP Address macro.<br>
+ <br>
+ </li>
+ </ol>
+ <p>
+  
+ </p>
+ <br>
+ &nbsp;&nbsp;&nbsp;<A HREF="#top"><small>return to top</small></A>
+
+<p>&nbsp;</p>
+
+
+<!-- Don't go past this line! -->
+
+ </td>
+
+<!-- End topic block -->
+
+</tr>
+
+</table>
+
+<!-- End topic table -->
+
+</body>
+</html>
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_main_hlp.html b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_main_hlp.html
new file mode 100644
index 0000000000..e8813f89f1
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_main_hlp.html
@@ -0,0 +1,192 @@
+<!--
+ -- ident "%W% %E% SMI"
+ --
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ -- Copyright (c) 2000-2001 by Sun Microsystems, Inc.
+ -- All rights reserved.
+ -->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<html>
+
+<head>
+<title>DHCP Manager Help: Getting Help</title>
+<meta NAME="AUTHOR" CONTENT="smorgan">
+<meta NAME="KEYWORDS" CONTENT="DHCP">
+
+</head>
+<body bgcolor="#FFFFFF">
+
+<a name="top"></a>
+
+<table border=0 cellspacing=0 cellpadding=0 width=625>
+
+<!-- Start navigation banner -->
+
+<tr><td colspan=4 WIDTH="615" align="center" valign="top"><IMG SRC="art/bannersmc.gif" WIDTH=615 BORDER=0 ALT="DHCP Manager Help">
+
+ </td>
+ </tr>
+
+<!-- End navigation banner -->
+
+
+<tr>
+
+<!-- Start contents block -->
+
+ <td colspan=1 valign="top" width=105>
+ <P>&nbsp;</P>
+ <A HREF="dhcp_main_top.html"><STRONG>Overview</STRONG></A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_solaris_about.html">Solaris DHCP</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_macros_about.html">About Macros</A><BR>
+<IMG SRC="art/tip2.gif" WIDTH=12 HEIGHT=10 BORDER=0 ALT="">Getting Help<P>
+
+ <A HREF="dhcp_relay_ref.html"><STRONG>Servers and Relays</STRONG></A><P>
+ <A HREF="dhcp_addr_ref.html"><STRONG>Addresses</STRONG></A><P>
+ <A HREF="dhcp_macro_ref.html"><STRONG>Macros</STRONG></A><P>
+ <A HREF="dhcp_option_ref.html"><STRONG>Options</STRONG></A><P>
+<A HREF="dhcp_main_how.html"><STRONG>How To..</STRONG></A><P>
+ <A HREF="dhcp_main_menus.html"><STRONG>Menus</STRONG></A><P>
+ <A HREF="dhcp_main_idx.html"><STRONG>Index</STRONG></A>
+
+ </td>
+
+<!-- End contents block -->
+
+<!-- Start column rule -->
+
+ <td colspan=1 width=5 bgcolor="#CCCCCC">&nbsp;</td>
+
+<!-- End column spacer -->
+
+<!-- Start column spacer -->
+
+ <td colspan=1 width=10 bgcolor="#FFFFFF">&nbsp;</td>
+
+<!-- End column spacer -->
+
+
+
+<!-- Start topic block -->
+
+<td colspan=1 valign="top" bgcolor="#ffffff" WIDTH="495">
+
+
+
+<P>&nbsp;</P>
+
+
+<h1>Getting Help</h1>
+
+This help system provides reference information and instructions for using Solaris<small><sup>TM</sup></small> DHCP Manager.<p>
+
+<p>Use the links in the contents area on the left to get to the topics you are interested in. The arrow <IMG SRC="art/tip2.gif" WIDTH=12 HEIGHT=10 BORDER=0 ALT=""> indicates your position in the help system.<p>
+
+The topics in this help system are divided into the following categories.<p>
+
+
+
+ <table border=0 cellspacing=3 cellpadding=3 width=475>
+
+ <tr>
+ <td width=115 valign="top"><a href="dhcp_main_top.html"><STRONG>Overview</STRONG></a></td>
+ <td valign="top">Introduction and background information about DHCP Manager, Solaris DHCP, macros, and options.</td>
+ </tr>
+
+ <tr>
+ <td width=100 valign="top"><a href="dhcp_relay_ref.html"><STRONG>Servers and Relays</STRONG></a></td>
+ <td valign="top">Reference and context-specific topics about how to manage and configure DHCP servers, BOOTP relay agents, and networks; how to convert to a new data store, or move data to another DHCP server.</td>
+ </tr>
+
+ <tr>
+ <td width=100 valign="top"><a href="dhcp_addr_ref.html"><STRONG>Addresses</STRONG></a></td>
+ <td valign="top">Reference and context-specific topics about how to manage and configure IP addresses in the DHCP network.</td>
+ </tr>
+
+ <tr>
+ <td width=100 valign="top"><a href="dhcp_macro_ref.html"><STRONG>Macros</STRONG></a></td>
+ <td valign="top">Reference and context-specific topics about how to define, create, and manage DHCP macros.</td>
+ </tr>
+
+ <tr>
+ <td width=100 valign="top"><a href="dhcp_option_ref.html"><STRONG>Options</STRONG></a></td>
+ <td valign="top">Reference and contex-specific topics about how to define, create, and manage DHCP options or symbols.</td>
+ </tr>
+
+
+
+ <tr>
+ <td width=100 valign="top"><a href="dhcp_main_how.html"><STRONG>How To...</STRONG></a>
+ </td>
+ <td valign="top">Step-by-step instructions
+ for using DHCP Manager.</td>
+ </tr>
+
+ <tr>
+ <td width=100 valign="top"><a href="dhcp_main_menus.html"><STRONG>Menus</STRONG></a>
+ </td>
+ <td valign="top">Descriptions of menus and options in the three views
+displayed by DHCP Manager.</td>
+ </tr>
+
+ <tr>
+ <td width=100 valign="top"><a href="dhcp_main_idx.html"><STRONG>Index</STRONG></a></td>
+ <td valign="top">Index to topics in the help system.</td>
+ </tr>
+
+ </table>
+
+
+<P><HR noshade><P>
+<strong><big><a name="info">Further Reading</a></big></strong><P>
+
+This help system is not meant to be an exhaustive reference on DHCP or network-based
+system management. You may want to check the following resources for further information:<p>
+
+ <ul>
+ <li> <a href="http://docs.sun.com/ab2/coll.47.11/SYSADV3/@Ab2TocView/7898?Ab2Lang=C&Ab2Enc=iso-8859-1"><em>Solaris System Administration Guide, Volume 3</em></a> on Sun's web site for more extensive documentation on Solaris DHCP<p></li>
+
+ <li><a href="http://www.ietf.org/html.charters/dhc-charter.html"><em>IETF DHC Charter</em></a><p></li>
+
+ <li><a href="http://www.dhcp.org"><em>Web site of the DHC Working Group</em> </a><p></li>
+
+ <li> <a href="http://www.rfc-editor.org"><em>Requests for Comments (RFCs)</em></a><p></li>
+
+ </ul>
+
+<p>&nbsp;</p>
+
+&nbsp;&nbsp;&nbsp;<A HREF="#top"><small>return to top</small></A>
+<!-- Don't go past this line! -->
+
+ </td>
+
+<!-- End topic block -->
+
+</tr>
+
+</table>
+
+<!-- End topic table -->
+
+</body>
+</html>
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_main_how.html b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_main_how.html
new file mode 100644
index 0000000000..210c9cf573
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_main_how.html
@@ -0,0 +1,165 @@
+<!--
+ -- ident "%W% %E% SMI"
+ --
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ -- Copyright (c) 2000-2001 by Sun Microsystems, Inc.
+ -- All rights reserved.
+ -->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<html>
+
+<head>
+<title>DHCP Manager Help: How To...</title>
+<meta NAME="AUTHOR" CONTENT="smorgan">
+<meta NAME="KEYWORDS" CONTENT="DHCP">
+
+</head>
+
+<body bgcolor="#FFFFFF">
+
+<a name="top"></a>
+
+<table border=0 cellspacing=0 cellpadding=0 width=615>
+
+<!-- Start navigation banner -->
+
+<tr><td colspan=4 WIDTH="615" align="center" valign="top"><IMG SRC="art/bannersmc.gif" WIDTH=615 BORDER=0 ALT="DHCP Manager Help">
+
+ </td>
+ </tr>
+
+<!-- End navigation banner -->
+
+
+<tr>
+
+<!-- Start contents block -->
+
+ <td colspan=1 valign="top" width=105>
+ <P>&nbsp;</P>
+ <A HREF="dhcp_main_top.html"><STRONG>Overview</STRONG></A><P>
+ <A HREF="dhcp_relay_ref.html"><STRONG>Servers/Relays</STRONG></A><P>
+ <A HREF="dhcp_addr_ref.html"><STRONG>Addresses</STRONG></A><P>
+ <A HREF="dhcp_macro_ref.html"><STRONG>Macros</STRONG></A><P>
+ <A HREF="dhcp_option_ref.html"><STRONG>Options</STRONG></A><P>
+
+<IMG SRC="art/tip2.gif" WIDTH=12 HEIGHT=10 BORDER=0 ALT=""><STRONG>How To..</STRONG><BR>
+&nbsp;&nbsp;<A HREF="dhcp_relay_how.html">Servers/Relays</A><BR>
+&nbsp;&nbsp;<A HREF="dhcp_addr_how.html">Addresses</A><BR>
+&nbsp;&nbsp;<A HREF="dhcp_macro_how.html">Macros</A><BR>
+&nbsp;&nbsp;<A HREF="dhcp_option_how.html">Options</A><P>
+<A HREF="dhcp_main_menus.html"><STRONG>Menus</STRONG></A><P>
+<A HREF="dhcp_main_idx.html"><STRONG>Index</STRONG></A>
+
+ </td>
+
+<!-- End contents block -->
+
+<!-- Start column rule -->
+
+ <td colspan=1 width=5 bgcolor="#CCCCCC">&nbsp;</td>
+
+<!-- End column spacer -->
+
+<!-- Start column spacer -->
+
+ <td colspan=1 width=10 bgcolor="#FFFFFF">&nbsp;</td>
+
+<!-- End column spacer -->
+
+
+
+<!-- Start topic block -->
+
+<td colspan=1 valign="top" bgcolor="#ffffff" WIDTH="495">
+<P>&nbsp;</P>
+<H1>How To...</H1>
+
+<a name="relay"></a><p><strong>Manage DHCP Servers and BOOTP Relay Agents</strong><p>
+
+<ul>
+<li><a href="dhcp_relay_how.html#srvr">Configure a DHCP server</a></li>
+<li><a href="dhcp_relay_how.html#relay">Configure a BOOTP relay agent</a></li>
+<li><a href="dhcp_relay_how.html#adv">Customize DHCP/BOOTP services</a></li>
+<li><a href="dhcp_relay_how.html#ntwrk">Configure a network</a></li>
+<li><a href="dhcp_relay_how.html#delnet">Delete a network</a></li>
+<li><a href="dhcp_relay_how.html#start">Start, stop, or restart a DHCP server or BOOTP relay agent</a></li>
+<li><a href="dhcp_relay_how.html#dis">Enable or disable a DHCP server or BOOTP relay agent</a></li>
+<li><a href="dhcp_relay_how.html#exp">Export and import DHCP data between servers</a></li>
+<li><a href="dhcp_relay_how.html#conv">Convert a DHCP server to use a new data store</a></li>
+<li><a href="dhcp_relay_how.html#uncon">Unconfigure a DHCP server</a></li>
+<li><a href="dhcp_relay_how.html#uncon2">Unconfigure a BOOTP relay agent</a></li>
+</ul>
+<P>
+
+<strong>Manage IP Addresses</strong><p>
+
+<ul>
+<li><a href="dhcp_addr_how.html#viewtable">View IP addresses</a></li>
+<li><a href="dhcp_addr_how.html#newtable">Add a single IP address</a></li>
+<li><a href="dhcp_addr_how.html#addwiz">Add multiple IP addresses</a></li>
+<li><a href="dhcp_addr_how.html#copy">Duplicate an IP address</a></li>
+<li><a href="dhcp_addr_how.html#modaddr">Modify a single IP address</a></li>
+<li><a href="dhcp_addr_how.html#modmulti">Modify multiple IP addresses</a></li>
+<li><a href="dhcp_addr_how.html#reladdr">Release IP addresses</a></li>
+<li><a href="dhcp_addr_how.html#deltable">Delete IP addresses</a></li>
+</ul>
+<P>
+
+<strong>Manage DHCP Client Macros</strong><p>
+
+<ul>
+<li><a href="dhcp_macro_how.html#viewmacro">View macros</a></li>
+<li><a href="dhcp_macro_how.html#newmacro">Create a new macro</a></li>
+<li><a href="dhcp_macro_how.html#dupmacro">Duplicate a macro</a></li>
+<li><a href="dhcp_macro_how.html#modmacro">Modify a macro</a></li>
+<li><a href="dhcp_macro_how.html#delmacro">Delete a macro</a></li>
+</UL>
+<P>
+
+<strong>Manage DHCP Client Options</strong><p>
+
+<UL>
+<li><a href="dhcp_option_how.html#viewoption">View options</a></li>
+<li><a href="dhcp_option_how.html#newoption">Create a new option</a></li>
+<li><a href="dhcp_option_how.html#dupoption">Duplicate an option</a></li>
+<li><a href="dhcp_option_how.html#modoption">Modify an option</a></li>
+<li><a href="dhcp_option_how.html#deloption">Delete an option</a></li>
+</ul>
+<P>
+&nbsp;&nbsp;&nbsp;<A HREF="#top"><small>return to top</small></A>
+<p>&nbsp;</p>
+
+<!-- Don't go past this line! -->
+
+ </td>
+
+<!-- End topic block -->
+
+</tr>
+
+</table>
+
+<!-- End topic table -->
+
+</body>
+</html>
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_main_idx.html b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_main_idx.html
new file mode 100644
index 0000000000..b54661b095
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_main_idx.html
@@ -0,0 +1,624 @@
+<!--
+ -- #ident "%Z%%M% %I% %E% SMI"
+ --
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ -- Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ -- Use is subject to license terms.^M
+ -->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<html>
+
+<head>
+<title>DHCP Manager Help: Index</title>
+<meta NAME="AUTHOR" CONTENT="smorgan">
+<meta NAME="KEYWORDS" CONTENT="DHCP">
+
+</head>
+
+<body bgcolor="#FFFFFF">
+
+<a name="top"></a>
+
+<table border=0 cellspacing=0 cellpadding=0 width=625>
+
+<!-- Start navigation banner -->
+
+<tr><td colspan=4 WIDTH="615" align="center" valign="top"><IMG SRC="art/bannersmc.gif" WIDTH=615 BORDER=0 ALT="DHCP Manager Help">
+
+ </td>
+ </tr>
+
+<!-- End navigation banner -->
+
+
+<tr>
+
+<!-- Start contents block -->
+
+ <td colspan=1 valign="top" width=105>
+ <P>&nbsp;</P>
+
+ <A HREF="dhcp_main_top.html"><STRONG>Overview</STRONG></A><P>
+ <A HREF="dhcp_relay_ref.html"><STRONG>Servers and Relays</STRONG></A><P>
+ <A HREF="dhcp_addr_ref.html"><STRONG>IP Addresses</STRONG></A><P>
+ <A HREF="dhcp_macro_ref.html"><STRONG>Macros</STRONG></A><P>
+ <A HREF="dhcp_option_ref.html"><STRONG>Options</STRONG></A><P>
+
+ <A HREF="dhcp_main_how.html"><STRONG>How To..</STRONG></A><P>
+ <A HREF="dhcp_main_menus.html"><STRONG>Menus</STRONG></A><P>
+<IMG SRC="art/tip2.gif" WIDTH=12 HEIGHT=10 BORDER=0 ALT=""><STRONG>Index</STRONG><BR>
+ <a href="#a-c"><em>A - C</em></a><BR>
+ <a href="#d-g"><em>D - G</em></a><BR>
+ <a href="#h-m"><em>H - M</em></a><BR>
+ <a href="#n-r"><em>N - R</em></a><BR>
+ <a href="#s-z"><em>S - Z</em></a>
+ </td>
+
+
+
+<!-- End contents block -->
+
+<!-- Start column rule -->
+
+ <td colspan=1 width=5 bgcolor="#CCCCCC">&nbsp;</td>
+
+<!-- End column spacer -->
+
+<!-- Start column spacer -->
+
+ <td colspan=1 width=10 bgcolor="#FFFFFF">&nbsp;</td>
+
+<!-- End column spacer -->
+
+
+
+<!-- Start topic block -->
+
+
+ <td colspan=1 valign="top" bgcolor="#ffffff" WIDTH="495">
+
+ <P>&nbsp;</P>
+
+<h1>Index</h1>
+
+
+<!-- Index head -->
+
+<a name="a-c"></a>
+<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="art/dot1.gif" border="0"> <strong><em>A</em></strong> <img src="art/dot1.gif" border="0"><p>
+
+about<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_main_top.html">DHCP Manager</A><BR>
+
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a
+ href="dhcp_macros_about.html">macros and options</a><br>
+
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_solaris_about.html">Solaris
+ DHCP</a><br>
+
+<A HREF="dhcp_addr_wiz.html">Address Wizard</A><BR>
+addresses<BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_addr_wiz.html">adding a block of</A><BR>
+
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_addr_ref.html#macro">configuration macro</A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_addr_how.html#newtable">creating</A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_addr_how.html#deltable">deleting</A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_server_serv.html#dupaddr">detecting duplicate</A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_addr_ref.html">displaying</A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_addr_how.html#copy">duplicating</A><BR>
+
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_addr_how.html#modaddr">modifying</A><BR>
+
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_addr_rel.html">releasing</A><BR>
+
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;type of lease<BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_addr_ref.html#status">BOOTP clients</A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_addr_ref.html#status">dynamic</A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_addr_ref.html#status">manual</A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_addr_ref.html#status">permanent</A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_addr_ref.html#status">reserved</A><BR>
+
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_addr_ref.html#status">unusable</A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_addr_how.html#viewtable">viewing</A><BR>
+
+
+<!-- Index head -->
+
+<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="art/dot1.gif" border="0"> <strong><em>B</em></strong> <img src="art/dot1.gif" border="0"><p>
+
+<A HREF="dhcp_addr_ref.html#status">BOOTP client, reserving address for</A><BR>
+
+<A HREF="dhcp_server_serv.html#compat">BOOTP compatibility</A><BR>
+
+BOOTP relay agent<br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_relay_config.html">configuring</a><br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_relay_serv.html#srvrs">DHCP servers, adding</a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_relay_how.html#dis">enabling/disabling</A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_relay_serv.html#inter">interfaces, monitoring</a><br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_relay_serv.html#log">log messages</A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_relay_serv.html#hops">relay hops</a><br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_relay_how.html#start">restarting</A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_relay_how.html#start">starting/stopping</A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_relay_how.html#uncon2">unconfiguring</A><BR>
+
+
+<!-- Index head -->
+
+<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="art/dot1.gif" border="0"> <strong><em>C</em></strong> <img src="art/dot1.gif" border="0">&nbsp;&nbsp;--<A HREF="#top"><small>return to top</small></A>--<p>
+
+<A HREF="dhcp_server_serv.html#cache">cache data</A><BR>
+
+<A HREF="dhcp_macros_about.html#macrocat">categories, macro</A><BR>
+Client Class macro<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_solaris_about.html" NAME="class">allocating</A><BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_macros_about.html" NAME="class">description of</A><BR>
+Client ID macro<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_solaris_about.html#cid">allocating</A><BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_macros_about.html#cid">description of</A><BR>
+
+<A HREF="dhcp_macros_about.html#cid">client-specific parameters</A><BR>
+clients<br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_relay_ref.html">BOOTP</a><br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_option_ref.html#class">class</a><br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_addr_ref.html">configuring</a><br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_addr_ref.html">host names</A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_addr_ref.html">ID</a><br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_addr_ref.html">records</a><br>
+
+configuration<BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_relay_config.html">BOOTP relay agent</A><br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_addr_how.html">client</A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_config_wiz.html">DHCP server</a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_addr_ref.html#macro">macro</A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_net_wiz.html">network</A><BR>
+
+<A HREF="dhcp_addr_view.html">Create menu option</A><BR>
+
+<!-- Index head -->
+
+<a name="d-g"></a>
+<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="art/dot1.gif" border="0"> <strong><em>D</em></strong> <img src="art/dot1.gif" border="0"><p>
+data store<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_config_wiz.html#store">selecting</A><BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_convert_wiz.html">converting</A><BR>
+database<BR>
+
+
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_solaris_about.html"><tt>dhcptab</tt></a><br>
+
+<A HREF="dhcp_addr_view.html">Delete menu option</A><BR>
+
+<A HREF="dhcp_addr_view.html">Delete Networks menu option</A><BR>
+
+<A HREF="dhcp_addr_view.html">Duplicate menu option</A><BR>
+
+
+DHCP<br>
+
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_solaris_about.html">about
+ Solaris</a><br>
+
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_addr_ref.html">client
+ configuration</a><br>
+
+
+
+DHCP servers<BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_relay_ref.html">about</a><br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_config_wiz.html">configuring</a><br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_relay_how.html#conv">converting data store on</A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_relay_how.html#dis">enabling/disabling</A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_config_wiz.html#lease">lease offer</a><br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_relay_how.html#exp">moving data between</A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_relay_how.html#start">restarting</A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_relay_how.html#start">starting/stopping</A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_relay_how.html#uncon">unconfiguring</A><BR>
+
+<a href="dhcp_main_top.html">DHCP Manager, overview</a><br>
+
+<A HREF="dhcp_main_top.html"><tt>dhtadm</tt> utility</A><BR>
+
+<a href="dhcp_main_top.html"><tt>dhcpconfig</tt> utility</a><br>
+
+<A HREF="dhcp_addr_view.html">Disable menu option</A><BR>
+DNS<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_config_wiz.html#dns">DNS domain server</A><BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_server_serv.html#dnsup">DNS update by DHCP server</A><BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_config_wiz.html#hostnmserv">host name service</A><BR>
+
+<A HREF="dhcp_addr_ref.html#status">dynamic address</A>
+
+
+
+<!-- Index head -->
+
+<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="art/dot1.gif" border="0"> <strong><em>E</em></strong> <img src="art/dot1.gif" border="0">&nbsp;&nbsp;&nbsp;--<A HREF="#top"><small>return to top</small></A>--<p>
+
+<A HREF="dhcp_addr_view.html">Edit menu</A><BR>
+
+<A HREF="dhcp_addr_view.html">Enable menu option</A><BR>
+
+<A HREF="dhcp_addr_view.html">Exit</A><BR>
+
+<A HREF="dhcp_addr_ref.html">expiration, of lease</A><BR>
+
+<A HREF="dhcp_option_ref.html#cat">extended option</A><BR>
+
+
+
+
+<!-- Index head -->
+
+<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="art/dot1.gif" border="0"> <strong><em>F</em></strong> <img src="art/dot1.gif" border="0"><p>
+
+<a href="dhcp_main_hlp.html#info">FAQ, DHCP</a><br>
+
+<A HREF="dhcp_addr_view.html">File menu</A><BR>
+
+<A HREF="dhcp_addr_view.html">Find utility</A><BR>
+
+<a href="dhcp_addr_ref.html#status">flags, lease</a><br>
+
+<a href="dhcp_relay_ref.html">forwarding IP address</a><br>
+
+
+
+<!-- Index head -->
+
+<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="art/dot1.gif" border="0"> <strong><em>G</em></strong> <img src="art/dot1.gif" border="0"><p>
+
+<a href="dhcp_main_top.html">getting started</a><br>
+
+<a href="dhcp_option_ref.html#gran">granularity, option</a><br>
+
+
+
+<!-- Index head -->
+
+<a name="h-m"></a>
+<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="art/dot1.gif" border="0"> <strong><em>H</em></strong> <img src="art/dot1.gif" border="0">&nbsp;&nbsp;&nbsp;--<A HREF="#top"><small>return to top</small></A>--<p>
+
+<A HREF="dhcp_addr_view.html">Help menu</A><BR>
+
+<a href="dhcp_main_hlp.html">help on help</a><br>
+
+<a href="dhcp_relay_serv.html#hops">hops, relay</a><br>
+
+host name<BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_addr_ref.html">addresses listed by</A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_addr_wiz.html#name">generating</A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_config_wiz.html#dns">resolution of</A><BR>
+
+How To...<br>
+
+
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_addr_how.html">manage addresses</a><br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_relay_how.html#adv">manage BOOTP relay agents</a><br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_main_how.html">list of procedures</a><br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_relay_how.html#adv">manage DHCP servers</a><br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_macro_how.html">manage macros</a><br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_relay_how.html#ntwrk">manage networks</a><br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_option_how.html">manage options</a><br>
+
+
+<!-- Index head -->
+
+<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="art/dot1.gif" border="0"> <strong><em>I</em></strong> <img src="art/dot1.gif" border="0"><p>
+
+<a href="dhcp_main_top.html"><tt>in.dhcpd</tt> command</a><br>
+
+interfaces<BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_relay_serv.html#inter">monitored by BOOTP relay agent</A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_server_serv.html#inter">monitored by DHCP server</a><br>
+
+IP address<br>
+
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a
+ href="dhcp_solaris_about.html#ip">allocating</a><br>
+
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a
+ href="dhcp_relay_ref.html">BOOTP forwarding</a><br>
+
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_addr_ref.html">client</a><br>
+
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a
+ href="dhcp_addr_ref.html#status">lease offer</a><br>
+
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_macros_about.html#ip">macro</a><br>
+
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a
+ href="dhcp_addr_ref.html#server">owning server</a><br>
+
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_addr_wiz.html#startaddr">range</a><br>
+
+<!-- Index head -->
+
+<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="art/dot1.gif" border="0"> <strong><em>L</em></strong> <img src="art/dot1.gif" border="0"><p>
+
+lease<br>
+
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_addr_ref.html#status">attributes</a><br>
+
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a
+ href="dhcp_config_wiz.html#lease">policy</a><br>
+
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_addr_ref.html#status">time</a><br>
+
+<a href="dhcp_server_serv.html#log">logging, verbose</a><br>
+
+
+
+<!-- Index head -->
+
+<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="art/dot1.gif" border="0"> <strong><em>M</em></strong> <img src="art/dot1.gif" border="0">&nbsp;&nbsp;&nbsp;--<A HREF="#top"><small>return to top</small></A>--<p>
+
+macros<br>
+
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_macros_about.html">about</a><br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_macros_about.html#macrocat">categories</a><br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_macros_about.html#class">Client Class</a><br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_macros_about.html#cid">client ID</a><br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_macro_how.html#newmacro">creating</a><br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_macro_how.html#delmacro">deleting</a><br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_macro_how.html#dupmacro">duplicating</a><br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_macros_about.html#ip">IP address</a><br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_macro_how.html#modmacro">modifying</a><br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_macro_how.html#newmacro">naming</a><br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_macros_about.html#nest">nesting</A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_macros_about.html#net">network</a><br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_macros_about.html#using">processing order</a><br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_macro_mod.html#name">properties</a><br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_macro_how.html#viewmacro">viewing</a><br>
+
+<A HREF="dhcp_addr_ref.html#status">manual address</A><BR>
+
+<a href="dhcp_option_ref.html#max">maximum, option</a><br>
+menus<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_addr_view.html">in address view</A><BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_macro_view.html">in macro view</A><BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_option_view.html">in option view</A><BR>
+
+<A HREF="dhcp_addr_view.html">Modify menu option</A><BR>
+
+modifying<br>
+
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_addr_how.html#modaddr">addresses</A><BR>
+
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_macro_how.html">macros</a><br>
+
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_addr_how.html#modaddr">network records</a><br>
+
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_option_how.html#modoption">option definitions</a><br>
+
+<!-- Index head -->
+
+<a name="n-r"></a>
+<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="art/dot1.gif" border="0"> <strong><em>N</em></strong> <img src="art/dot1.gif" border="0"><p>
+
+network<br>
+
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_addr_ref.html">DHCP network table</a><br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_relay_how.html#delnet">deleting</A><BR>
+
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_server_serv.html#inter">interfaces</a><br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_solaris_about.html#ntwrk">macros</a><br>
+
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_addr_ref.html">records</a><br>
+network macro<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_solaris_about.html#ntwrk">allocating</A><BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_macros_about.html#net">description of</A><BR>
+
+<A HREF="dhcp_addr_view.html">Network scroll list</A><BR>
+
+<A HREF="dhcp_net_wiz.html">Network Wizard</A><BR>
+
+
+network tables<br>
+
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_addr_create.html">creating</a><br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_addr_ref.html">managing</a><br>
+
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_addr_ref.html">viewing properties</a><br>
+
+<A HREF="dhcp_config_wiz.html#nis">NIS name service, specifying</A><BR>
+
+<a href="dhcp_config_wiz.html#store">NIS+ database</a><br>
+<A HREF="dhcp_config_wiz.html#nisplus">NIS+ name service, specifying</A><BR>
+
+
+<!-- Index head -->
+
+<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="art/dot1.gif" border="0"> <strong><em>O</em></strong> <img src="art/dot1.gif" border="0">&nbsp;&nbsp;&nbsp;--<A HREF="#top"><small>return to top</small></A>--<p>
+
+options<br>
+
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_macros_about.html">about</a><br>
+
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;category<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_option_ref.html#cat">Extended</A><BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_option_ref.html#cat">Site</A><BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_option_ref.html#cat">Standard</A><BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_option_ref.html#cat">Vendor</A><BR>
+
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_option_ref.html#class">class</a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_option_ref.html#code">code</a><br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_option_how.html#newoption">creating</a><br>
+
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;data type<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_option_ref.html#type">ASCII</A><BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_option_ref.html#type">Boolean</A><BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_option_ref.html#type">IP</A><BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_option_ref.html#type">Number</A><BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_option_ref.html#type">Octet</A><BR>
+
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_option_how.html#deloption">deleting</a><br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_macros_about.html"><tt>dhcptab</tt></a><br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_option_how.html#dupoption">duplicating</a><br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_option_ref.html#gran">granularity</a><br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_option_ref.html#max">maximum</a><br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_option_how.html#modoption">modifying</a><br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_option_ref.html#name">naming</A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_option_ref.html">overview</a><br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_option_mod.html">properties</a><br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_option_tags.html">standard, list of</A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_option_ref.html#type">type</a><br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_option_create.html#class">Vendor Client Class</a><br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_option_how.html">viewing</a><br>
+
+<a href="dhcp_macros_about.html#using">order of processing</a><br>
+
+<a href="dhcp_server_serv.html#addrs">ownership of server addresses</a><br>
+
+<a href="dhcp_addr_ref.html#server">owning server</a><br>
+
+<!-- Index head -->
+
+<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="art/dot1.gif" border="0"> <strong><em>P</em></strong> <img src="art/dot1.gif" border="0"><p>
+
+<A HREF="dhcp_addr_ref.html" NAME="status">permanent address</A><BR>
+<a href="dhcp_main_top.html"><tt>pntadm</tt> command</a><br>
+
+<A HREF="dhcp_addr_view.html">Properties menu option</A><BR>
+
+<!-- Index head -->
+
+<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="art/dot1.gif" border="0"> <strong><em>R</em></strong> <img src="art/dot1.gif" border="0">&nbsp;&nbsp;&nbsp;--<A HREF="#top"><small>return to top</small></A>--<p>
+
+<a href="dhcp_addr_ref.html">records, network</a><br>
+
+<A HREF="dhcp_addr_view.html">Refresh menu option</a><br>
+
+<a href="dhcp_main_hlp.html#info">related reading</a><br>
+
+<a href="dhcp_relay_ref.html">relay agents, BOOTP</a><br>
+
+<A HREF="dhcp_addr_how.html#reladdr">release address</A><BR>
+
+<A HREF="dhcp_addr_view.html">Restart menu option</A><BR>
+
+<a href="dhcp_relay_how.html#start">restart server</a><br>
+
+<A HREF="dhcp_config_wiz.html#router">router</A><BR>
+
+
+<!-- Index head -->
+
+<a name="s-z"></a>
+<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="art/dot1.gif" border="0"> <strong><em>S</em></strong> <img src="art/dot1.gif" border="0"><p>
+
+<A HREF="dhcp_addr_view.html">Service menu</A><BR>
+
+<A HREF="dhcp_macros_about.html#ip">server-specific parameters</A><BR>
+
+servers<BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_relay_ref.html">and relay agents</a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="dhcp_addr_ref.html#server">owning</A><BR>
+
+ <A HREF="dhcp_addr_view.html">Show Grid menu option</A><BR>
+
+<A HREF="dhcp_addr_view.html">Show Hostnames menu option</A><BR>
+
+<A HREF="dhcp_option_ref.html#cat">site option</A><BR>
+
+<a href="dhcp_solaris_about.html">Solaris DHCP, about</a><br>
+
+<A HREF="dhcp_option_ref.html">standard option</A><BR>
+
+<A HREF="dhcp_addr_view.html">Start menu option</A><BR>
+
+<A HREF="dhcp_addr_view.html">Stop menu option</A><BR>
+
+<A HREF="dhcp_config_wiz.html#net">subnet mask</A><BR>
+
+<A HREF="dhcp_server_serv.html#log"><TT>syslogd</TT> facility</A><BR>
+
+<!-- Index head -->
+
+<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="art/dot1.gif" border="0"> <strong><em>T</em></strong> <img src="art/dot1.gif" border="0">&nbsp;&nbsp;--<A HREF="#top"><small>return to top</small></A>--<p>
+
+tables<br>
+
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_addr_ref.html">client records</a><br>
+
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_solaris_about.html"><tt>dhcptab</tt></a><br>
+
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_addr_ref.html">network</a><br>
+
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_server_serv.html#rescan">rescan interval</a><br>
+
+
+ <!-- Index head -->
+
+<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="art/dot1.gif" border="0"> <strong><em>U</em></strong> <img src="art/dot1.gif" border="0"><p>
+
+<A HREF="dhcp_addr_view.html">Unconfigure menu option</A><BR>
+
+<A HREF="dhcp_addr_create.html#unusable">unusable address</A><BR>
+
+<A HREF="dhcp_addr_view.html">update server data</A><BR>
+
+
+<!-- Index head -->
+
+<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="art/dot1.gif" border="0"> <strong><em>V</em></strong> <img src="art/dot1.gif" border="0"><p>
+
+<A HREF="dhcp_option_ref.html#cat">vendor option</A><BR>
+
+<a href="dhcp_server_serv.html#log">verbose logging</a><br>
+
+<A HREF="dhcp_addr_view.html">View menu</A><BR>
+
+viewing<br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_addr_ref.html">addresses</a><br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_macro_ref.html">macros</a><br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_option_ref.html">options</a><br>
+
+
+
+
+<!-- Index head -->
+
+<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="art/dot1.gif" border="0"> <strong><em>W</em></strong> <img src="art/dot1.gif" border="0"><p>
+
+wizards<BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_addr_wiz.html">Address Wizard</a><br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_config_wiz.html">DHCP Server Configuration Wizard</a><br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="dhcp_net_wiz.html">Network Wizard</a><br>
+
+<p>&nbsp;</p>
+&nbsp;&nbsp;&nbsp;<A HREF="#top"><small>return to top</small></A>
+
+<!-- Don't go past this line! -->
+
+ </td>
+
+<!-- End topic block -->
+
+</tr>
+
+</table>
+
+<!-- End topic table -->
+
+</body>
+</html>
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_main_menus.html b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_main_menus.html
new file mode 100644
index 0000000000..7064c6c5b4
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_main_menus.html
@@ -0,0 +1,128 @@
+<!--
+ -- ident "%W% %E% SMI"
+ --
+ Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+ -->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<html>
+
+<head>
+<title>DHCP Manager Help: Menus</title>
+<meta NAME="AUTHOR" CONTENT="smorgan">
+<meta NAME="KEYWORDS" CONTENT="DHCP">
+
+</head>
+
+<body bgcolor="#FFFFFF">
+
+<a name="top"></a>
+
+<table border=0 cellspacing=0 cellpadding=0 width=625>
+
+
+<!-- Start navigation banner -->
+
+<tr><td colspan=4 WIDTH="615" align="left" valign="top"><IMG SRC="art/bannersmc.gif" WIDTH=615 BORDER=0 ALT="DHCP Manager Help">
+
+ </td>
+ </tr>
+
+<!-- End navigation banner -->
+
+
+<tr>
+
+<!-- Start contents block -->
+
+ <td colspan=1 valign="top" WIDTH="105">
+ <P>&nbsp;</P>
+ <STRONG><A HREF="dhcp_main_top.html">Overview</A></STRONG><P>
+
+ <A HREF="dhcp_relay_ref.html"><STRONG>Servers and Relays</STRONG></A><P>
+
+ <A HREF="dhcp_addr_ref.html"><STRONG>Addresses</STRONG></A><P>
+ <A HREF="dhcp_macro_ref.html"><STRONG>Macros</STRONG></A><P>
+ <A HREF="dhcp_option_ref.html"><STRONG>Options</STRONG></A><P>
+
+ <A HREF="dhcp_main_how.html"><STRONG>How To..</STRONG></A><P>
+
+ <IMG SRC="art/tip2.gif" WIDTH=12 HEIGHT=10 BORDER=0 ALT=""><STRONG>Menus</STRONG><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_addr_view.html">Address View</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_macro_view.html">Macro View</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_option_view.html">Option View</A><P>
+
+<A HREF="dhcp_main_idx.html"><STRONG>Index</STRONG></A>
+
+ </td>
+
+<!-- End contents block -->
+
+<!-- Start column rule -->
+
+ <td colspan=1 width=5 bgcolor="#CCCCCC">&nbsp;</td>
+
+<!-- End column spacer -->
+
+<!-- Start column spacer -->
+
+ <td colspan=1 width=10 bgcolor="#FFFFFF">&nbsp;</td>
+
+<!-- End column spacer -->
+
+
+
+<!-- Start topic block -->
+
+<td colspan=1 valign="top" bgcolor="#ffffff" WIDTH="495">
+<P>&nbsp;</P>
+
+
+ <H1>Menus</H1>
+The DHCP Manager windows have five menu buttons: File, Edit, View, Service, and Help.
+The menu options differ depending on which view being displayed.
+<UL>
+<LI><A HREF="dhcp_addr_view.html">Address View</A>
+<LI><A HREF="dhcp_macro_view.html">Macro View</A>
+<LI><A HREF="dhcp_option_view.html">Option View</A>
+</UL>
+The Network scroll box is available only with the Address view.
+
+
+<p>&nbsp;</p>
+
+
+<!-- Don't go past this line! -->
+
+ </td>
+
+<!-- End topic block -->
+
+</tr>
+
+</table>
+
+<!-- End topic table -->
+
+</body>
+</html>
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_main_top.html b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_main_top.html
new file mode 100644
index 0000000000..a592cffec6
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_main_top.html
@@ -0,0 +1,182 @@
+<!--
+ -- ident "%W% %E% SMI"
+ --
+ Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+ -->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<html>
+
+<head>
+<title>DHCP Manager Help: Introduction</title>
+<meta NAME="AUTHOR" CONTENT="smorgan">
+<meta NAME="KEYWORDS" CONTENT="DHCP">
+
+</head>
+
+<body bgcolor="#ffffff">
+
+<a name="top"></a>
+
+<table border=0 cellspacing=0 cellpadding=0 width=615>
+
+
+<!-- Start navigation banner -->
+
+<tr><td colspan=4 WIDTH="615" align="left" valign="top"><IMG SRC="art/bannersmc.gif" WIDTH=615 BORDER=0 ALT="DHCP Manager Help">
+
+ </td>
+ </tr>
+
+<!-- End navigation banner -->
+
+
+<tr>
+
+<!-- Start contents block -->
+
+ <td colspan=1 valign="top" WIDTH="105">
+ <P>&nbsp;</P>
+ <IMG SRC="art/tip2.gif" WIDTH=12 HEIGHT=10 BORDER=0 ALT=""><STRONG>Overview</STRONG><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_solaris_about.html">Solaris DHCP</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_macros_about.html">About Macros</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_main_hlp.html">Getting Help</A><P>
+ <A HREF="dhcp_relay_ref.html"><STRONG>Servers and Relays</STRONG></A><P>
+ <A HREF="dhcp_addr_ref.html"><STRONG>Addresses</STRONG></A><P>
+ <A HREF="dhcp_macro_ref.html"><STRONG>Macros</STRONG></A><P>
+ <A HREF="dhcp_option_ref.html"><STRONG>Options</STRONG></A><P>
+ <A HREF="dhcp_main_how.html"><STRONG>How To..</STRONG></A><P>
+ <A HREF="dhcp_main_menus.html"><STRONG>Menus</STRONG></A><P>
+<A HREF="dhcp_main_idx.html"><STRONG>Index</STRONG></A>
+
+ </td>
+
+<!-- End contents block -->
+
+<!-- Start column rule -->
+
+ <td colspan=1 width=5 bgcolor="#CCCCCC">&nbsp;</td>
+
+<!-- End column spacer -->
+
+<!-- Start column spacer -->
+
+ <td colspan=1 width=10 bgcolor="#FFFFFF">&nbsp;</td>
+
+<!-- End column spacer -->
+
+
+
+<!-- Start topic block -->
+
+<td colspan=1 valign="top" bgcolor="#ffffff" width="495">
+
+
+
+<P>&nbsp;</P>
+
+
+<H1>About DHCP Manager</H1>
+
+DHCP Manager provides an integrated Java-based graphical
+interface for Solaris<FONT SIZE="-1"><sup>TM</sup></FONT> DHCP management functions that can also be performed from the Solaris
+command line. DHCP Manager provides functions that are analogous to those available from:<p>
+
+ <ul>
+
+ <li><tt>dhcpconfig</tt> -- DHCP service
+ configuration utility</li>
+ <li><tt>dhtadm</tt> -- DHCP configuration table
+ management utility</li>
+ <li><tt>pntadm</tt> -- DHCP network table
+ management utility</li>
+ <li><tt>in.dhcpd</tt> -- DHCP server and BOOTP relay
+ daemon</li>
+
+ </ul>
+
+<p>You can use DHCP Manager in place of, or in combination with, all command-line-based Solaris DHCP functions.<p>
+
+DHCP Manager provides several benefits, compared with its command-line counterparts:<p>
+
+ <ul>
+
+ <li>A convenient, integrated &quot;point-and-click&quot; interface for the Solaris
+ DHCP server's most sophisticated functions.<p></li>
+
+ <li>A graphical view of the relationships between <tt>dhcptab</tt> macros and options, making it easier for
+ you to determine where to place option values for the most efficient
+ client configurations.<p></li>
+
+ <li>DHCP management wizards that guide you through tasks such as
+ configuring the DHCP server, configuring networks, and adding addresses.<p></li>
+
+ </ul>
+
+Of course, the command-line utilities are still useful for creating shell scripts for batch-processing administrative tasks.<P>
+
+<STRONG>Use DHCP Manager to</STRONG>
+
+<UL>
+<LI><a href="dhcp_relay_ref.html">Manage DHCP servers and BOOTP relay agents</a></LI>
+<LI><a href="dhcp_net_wiz.html">Manage networks</a></LI>
+<LI><a href="dhcp_addr_ref.html">Manage IP addresses</a></LI>
+<LI><a href="dhcp_macro_ref.html">Manage DHCP macros</a></LI>
+<LI><a href="dhcp_option_ref.html">Manage DHCP options</a></LI>
+</UL>
+
+<STRONG>For more information, see</STRONG>
+
+<UL>
+<LI><A HREF="dhcp_solaris_about.html">About Solaris DHCP</A></LI>
+<LI><A HREF="dhcp_macros_about.html">About Macros and Options</A></LI>
+<LI><A HREF="dhcp_main_menus.html">Menus</A></LI>
+<LI><A HREF="dhcp_main_hlp.html">Getting Help</A></LI>
+</UL>
+<P>
+
+&nbsp;&nbsp;&nbsp;<A HREF="#top"><small>return to top</small></A>
+<P>&nbsp;</P>
+
+
+
+
+
+
+
+<!-- Don't go past this line! -->
+ </td>
+
+<!-- End topic block -->
+
+</tr>
+
+
+</table>
+
+
+
+<!-- End topic table -->
+
+</body>
+</html>
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_net_del.html b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_net_del.html
new file mode 100644
index 0000000000..0e762416a0
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_net_del.html
@@ -0,0 +1,119 @@
+<!--
+ -- ident "%W% %E% SMI"
+ --
+ Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+ -->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<html>
+
+<head>
+<title>DHCP Manager Help: Delete Networks</title>
+<meta NAME="AUTHOR" CONTENT="smorgan">
+<meta NAME="KEYWORDS" CONTENT="DHCP">
+
+</head>
+
+<body bgcolor="#FFFFFF">
+
+<a name="top"></a>
+
+<table border=0 cellspacing=0 cellpadding=0 width=615>
+<TR>
+
+<!-- Start navigation banner -->
+<td colspan=4 valign="top" WIDTH="615">
+
+<IMG SRC="art/bannersmc.gif" WIDTH=615 BORDER=0 ALT="DHCP Manager Help"></TD></TR>
+</TABLE>
+
+<!-- End navigation banner -->
+
+<table border=0 cellspacing=0 cellpadding=0 width=615>
+<tr>
+
+<!-- Start contents block -->
+
+
+ <td colspan=1 valign="top" WIDTH="105">
+ <BR>
+
+ <STRONG><A HREF="dhcp_main_top.html">Overview</A></STRONG><P>
+ <A HREF="dhcp_relay_ref.html"><STRONG>Servers and Relays</STRONG></A><P>
+ <A HREF="dhcp_addr_ref.html"><STRONG>Addresses</STRONG></A><P>
+ <A HREF="dhcp_macro_ref.html"><STRONG>Macros</STRONG></A><P>
+ <A HREF="dhcp_option_ref.html"><STRONG>Options</STRONG></A><P>
+ <A HREF="dhcp_main_how.html"><STRONG>How To..</STRONG></A><P>
+ <A HREF="dhcp_main_menus.html"><STRONG>Menus</STRONG></A><P>
+<A HREF="dhcp_main_idx.html"><STRONG>Index</STRONG></A>
+
+ </td>
+
+
+<!-- End contents block -->
+
+<!-- Start column rule -->
+
+ <td colspan=1 width=5 bgcolor="#CCCCCC">&nbsp;</td>
+
+<!-- End column spacer -->
+
+<!-- Start column spacer -->
+
+ <td colspan=1 width=10 bgcolor="#FFFFFF">&nbsp;</td>
+
+<!-- End column spacer -->
+
+
+
+<!-- Start topic block -->
+
+
+
+
+ <td colspan=1 valign="top" bgcolor="#ffffff">
+
+<BR>
+
+<h1>Delete Networks</h1>
+The Keep Networks list displays the networks configured for this server. To delete a network is to remove it from the list of networks for this server to monitor.<P>
+Use the arrow keys to move a network into the Delete Networks list.<P>
+<STRONG>Delete Hosts Table Entries:</STRONG> Check this box to remove from the <tt>hosts</tt> table all host names associated with the deleted network(s).
+<P>
+To restore or add a network to the list of configured networks, you must configure it using the <A HREF="dhcp_net_wiz.html">Network Wizard</A>.
+<p>&nbsp;</p>
+
+<!-- Don't go past this line! -->
+
+ </td>
+
+<!-- End topic block -->
+
+</tr>
+
+</table>
+
+<!-- End topic table -->
+
+</body>
+</html>
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_net_ref.html b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_net_ref.html
new file mode 100644
index 0000000000..7b81dd8f6d
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_net_ref.html
@@ -0,0 +1,121 @@
+<!--
+ -- ident "%W% %E% SMI"
+ --
+ Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+ -->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<html>
+
+<head>
+<title>DHCP Manager Help: Networks</title>
+<meta NAME="AUTHOR" CONTENT="smorgan">
+<meta NAME="KEYWORDS" CONTENT="DHCP">
+
+</head><body bgcolor="#FFFFFF">
+
+<a name="top"></a>
+
+<table border=0 cellspacing=0 cellpadding=0 width=615>
+
+<!-- Start navigation banner -->
+
+<tr><td colspan=4 WIDTH="615" align="center" valign="top"><IMG SRC="art/bannersmc.gif" WIDTH=615 BORDER=0 ALT="DHCP Manager Help">
+
+ </td>
+ </tr>
+
+<!-- End navigation banner -->
+
+
+<tr>
+
+<!-- Start contents block -->
+
+ <td colspan=1 valign="top" width=105>
+ <P>&nbsp;</P>
+
+ <A HREF="dhcp_main_top.html"><STRONG>Overview</STRONG></A><P>
+ <A HREF="dhcp_relay_ref.html"><STRONG>Servers and Relay Agents</STRONG></A><P>
+ <IMG SRC="art/tip2.gif" WIDTH=12 HEIGHT=10 BORDER=0 ALT=""><STRONG>Networks</STRONG><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_net_wiz.html">Network Wizard</A>
+ &nbsp;&nbsp;<A HREF="dhcp_net_del.html">Delete Networks</A><P>
+
+ <A HREF="dhcp_addr_ref.html"><STRONG>IP Addresses</STRONG></A><P>
+ <A HREF="dhcp_macro_ref.html"><STRONG>Macros</STRONG></A><P>
+ <A HREF="dhcp_option_ref.html"><STRONG>Options</STRONG></A><P>
+
+<A HREF="dhcp_main_how.html"><STRONG>How To..</STRONG></A><P>
+<A HREF="dhcp_main_idx.html"><STRONG>Index</STRONG></A>
+
+
+ </td>
+
+
+
+<!-- End contents block -->
+
+<!-- Start column rule -->
+
+ <td colspan=1 width=5 bgcolor="#CCCCCC">&nbsp;</td>
+
+<!-- End column spacer -->
+
+<!-- Start column spacer -->
+
+ <td colspan=1 width=10 bgcolor="#FFFFFF">&nbsp;</td>
+
+<!-- End column spacer -->
+
+
+
+<!-- Start topic block -->
+
+
+ <td colspan=1 valign="top" bgcolor="#ffffff">
+ <P>&nbsp;</P>
+
+
+<h1>Networks</h1>
+The DHCP server is connected to one or more networks. One network is configured when the DHCP server is first configured.<P>
+To add one or more networks to those monitored by the DHCP server, choose the <A HREF="dhcp_net_wiz.html">Network Wizard</A>, available from the Edit menu, when the Address view is displayed.<P>
+To remove configured networks from those monitored by the DHCP server, choose <A HREF="dhcp_net_del.html">Delete Networks</A> from the Edit menu .
+
+
+<p>&nbsp;</p>
+
+
+<!-- Don't go past this line! -->
+
+ </td>
+
+<!-- End topic block -->
+
+</tr>
+
+</table>
+
+<!-- End topic table -->
+
+</body>
+</html>
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_net_wiz.html b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_net_wiz.html
new file mode 100644
index 0000000000..675a020d3a
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_net_wiz.html
@@ -0,0 +1,201 @@
+<!--
+ -- ident "%W% %E% SMI"
+ --
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ -- Copyright (c) 2000-2001 by Sun Microsystems, Inc.
+ -- All rights reserved.
+ -->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<html>
+
+<head>
+<title>DHCP Manager Help: Adding a Network</title>
+<meta NAME="AUTHOR" CONTENT="smorgan">
+<meta NAME="KEYWORDS" CONTENT="DHCP">
+
+</head>
+
+<body bgcolor="#FFFFFF">
+
+<a name="top"></a>
+
+<table border=0 cellspacing=0 cellpadding=0 width=615>
+
+
+<!-- Start navigation banner -->
+
+<tr><td colspan=4 WIDTH="615" align="left" valign="top"><IMG SRC="art/bannersmc.gif" WIDTH=615 BORDER=0 ALT="DHCP Manager Help">
+
+ </td>
+ </tr>
+
+<!-- End navigation banner -->
+
+
+<tr>
+
+<!-- Start contents block -->
+
+ <td colspan=1 valign="top" WIDTH="105">
+ <P>&nbsp;</P>
+ <STRONG><A HREF="dhcp_main_top.html">Overview</A></STRONG><P>
+
+ <A HREF="dhcp_relay_ref.html"><STRONG>Servers and Relays</STRONG></A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_config_wiz.html">DHCP Config</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_relay_config.html">Relay Config</A><BR>
+ <IMG SRC="art/tip2.gif" WIDTH=12 HEIGHT=10 BORDER=0 ALT="">Net Config<BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#addr"><EM>Address</EM></a><br>
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#info"><EM>Net Type</EM></a><br>
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#router"><EM>Router</EM></a><br>
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#nis"><EM>NIS</EM></a><br>
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#nisplus"><EM>NIS+</EM></a><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_server_serv.html">DHCP Services</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_relay_serv.html">Relay Services</A><P>
+ <A HREF="dhcp_addr_ref.html"><STRONG>Addresses</STRONG></A><P>
+ <A HREF="dhcp_macro_ref.html"><STRONG>Macros</STRONG></A><P>
+ <A HREF="dhcp_option_ref.html"><STRONG>Options</STRONG></A><P>
+ <A HREF="dhcp_main_how.html"><STRONG>How To..</STRONG></A><P>
+ <A HREF="dhcp_main_menus.html"><STRONG>Menus</STRONG></A><P>
+<A HREF="dhcp_main_idx.html"><STRONG>Index</STRONG></A>
+
+ </td>
+
+<!-- End contents block -->
+
+<!-- Start column rule -->
+
+ <td colspan=1 width=5 bgcolor="#CCCCCC">&nbsp;</td>
+
+<!-- End column spacer -->
+
+<!-- Start column spacer -->
+
+ <td colspan=1 width=10 bgcolor="#FFFFFF">&nbsp;</td>
+
+<!-- End column spacer -->
+
+
+
+<!-- Start topic block -->
+
+<td colspan=1 valign="top" bgcolor="#ffffff">
+
+
+
+<P>&nbsp;</P>
+
+
+
+<h1>Network Wizard</h1>
+The Network Wizard guides you through the steps to add a new network to the DHCP server's list of configured networks. The Network Wizard is available from the Edit menu, when the Address view is displayed.<P>
+<p>
+
+To enter information, double-click in the field, enter the desired value, and then press Enter.
+
+The following table describes the information required to add a network to the DHCP server.
+<P><HR NOSHADE><P>
+<P>
+
+ <table border=0 cellspacing=3 cellpadding=3>
+ <tr>
+ <td width=125 valign="top"><A NAME="addr"><STRONG>Network Address</STRONG></A></td>
+ <td valign="top">
+ Enter the IP address of the network you are configuring.<p>
+ </td>
+ </tr>
+
+ <tr>
+ <td width=125 valign="top"><A NAME="info"><STRONG>Subnet Mask</STRONG></A><br></td>
+ <td valign="top">
+ Enter the subnet mask for this network. A subnet mask is a way of dividing
+ up the host portion of an Internet address to form local subnetworks.
+ </TD></TR>
+
+ <tr>
+ <td width=125 valign="top"><A NAME="info"><STRONG>Network Type</STRONG></A><br></td>
+ <td valign="top">
+ Specify whether the network is a local area network (LAN) or point-to-point (PPP).<p>
+
+
+ </TD>
+ </tr>
+
+ <tr>
+ <td width=125 valign="top"><A NAME="router"><STRONG>Routing</STRONG></A></td>
+ <td valign="top">A router is a machine with multiple network
+ interfaces that can forward IP packets from one network to
+ another. In most cases, your clients should use router discovery to
+ connect to a router. If you have clients in your network that cannot
+ use router discovery, enter the IP address of a router which
+ they can use to communicate with systems on another network.
+ </td>
+ </tr>
+
+
+
+ <tr>
+ <td width=125 valign="top"><A NAME="nis"><STRONG>NIS Domain</STRONG></A><br>
+ <STRONG>NIS Servers</STRONG></td>
+ <td width=315 valign="top">If the server is configured to use NIS naming service,
+ the NIS server information will be filled in. If not, you can enter the domain
+ name and IP address of one or more NIS name servers.<P>
+
+ The order in which the address appears in the list determines the order in
+ which the servers are queried.
+ </td>
+ </tr>
+
+ <tr>
+ <td width=125 valign="top"><A NAME="nisplus"><STRONG>NIS+ Domain</STRONG></A><br>
+ <STRONG>NIS+ Servers</STRONG></td>
+ <td valign="top">If the server is configured to use NIS+ name service,
+ the NIS+ server information will be filled in. If not, you can enter
+ the domain name and IP address of one or more NIS+ name servers.<P>
+ The order in which the address appears in the list determines the order in
+ which the servers are queried.
+ </td>
+
+ </tr>
+
+
+</table>
+&nbsp;&nbsp;&nbsp;<A HREF="#top"><small>return to top</small></A>
+
+
+
+<p>&nbsp;</p>
+
+
+<!-- Don't go past this line! -->
+
+ </td>
+
+<!-- End topic block -->
+
+</tr>
+
+</table>
+
+<!-- End topic table -->
+
+</body>
+</html>
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_option_create.html b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_option_create.html
new file mode 100644
index 0000000000..7ba6f4b04b
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_option_create.html
@@ -0,0 +1,311 @@
+<!--
+ -- ident "%W% %E% SMI"
+ --
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ -- Copyright (c) 2000-2001 by Sun Microsystems, Inc.
+ -- All rights reserved.
+ -->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<html>
+
+<head>
+<title>DHCP Manager Help: Create Option</title>
+<meta NAME="AUTHOR" CONTENT="smorgan">
+<meta NAME="KEYWORDS" CONTENT="DHCP">
+
+</head>
+
+<body bgcolor="#FFFFFF">
+
+<a name="top"></a>
+
+<table border=0 cellspacing=0 cellpadding=0 width=615>
+
+<!-- Start navigation banner -->
+
+<tr><td colspan=4 WIDTH="615" align="center" valign="top"><IMG SRC="art/bannersmc.gif" WIDTH=615 BORDER=0 ALT="DHCP Manager Help">
+
+ </td>
+ </tr>
+
+<!-- End navigation banner -->
+
+
+<tr>
+
+<!-- Start contents block -->
+
+ <td colspan=1 valign="top" width=105>
+ <P>&nbsp;</P>
+
+ <A HREF="dhcp_main_top.html"><STRONG>Overview</STRONG></A><P>
+ <A HREF="dhcp_relay_ref.html"><STRONG>Servers and Relays</STRONG></A><P>
+ <A HREF="dhcp_addr_ref.html"><STRONG>IP Addresses</STRONG></A><P>
+ <A HREF="dhcp_macro_ref.html"><STRONG>Macros</STRONG></A><P>
+ <A HREF="dhcp_option_ref.html"><STRONG>Options</STRONG></A><BR>
+<IMG SRC="art/tip2.gif" WIDTH=12 HEIGHT=10 BORDER=0 ALT="">Create<BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<A HREF="#name"><EM>Name</EM></A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<A HREF="#cat"><EM>Category</EM></A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<A HREF="#code"><EM>Code</EM></A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<A HREF="#type"><EM>Data Type</EM></A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<A HREF="#gran"><EM>Granularity</EM></A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<A HREF="#max"><EM>Maximum</EM></A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<A HREF="#class"><EM>Class</EM></A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_option_dup.html">Duplicate</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_option_mod.html">Modify</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_option_del.html">Delete</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_option_tags.html">Standard options</A> <P>
+
+<A HREF="dhcp_main_how.html"><STRONG>How To..</STRONG></A><P>
+<A HREF="dhcp_main_menus.html"><STRONG>Menus</STRONG></A><P>
+<A HREF="dhcp_main_idx.html"><STRONG>Index</STRONG></A>
+
+
+ </td>
+
+
+
+<!-- End contents block -->
+
+<!-- Start column rule -->
+
+ <td colspan=1 width=5 bgcolor="#CCCCCC">&nbsp;</td>
+
+<!-- End column spacer -->
+
+<!-- Start column spacer -->
+
+ <td colspan=1 width=10 bgcolor="#FFFFFF">&nbsp;</td>
+
+<!-- End column spacer -->
+
+
+
+<!-- Start topic block -->
+
+
+ <td colspan=1 valign="top" bgcolor="#ffffff">
+ <P>&nbsp;</P>
+
+
+<h1>Create Option</h1>
+
+The Create Option dialog box lets you create new option definitions in the
+<tt>dhcptab</tt> on the selected DHCP server. <p>
+
+See <a href="dhcp_macros_about.html">
+About Macros and Options</a>, for more information about defining DHCP options.<p>
+The settings in the Create Option dialog box are described in the following table.
+<P><HR NOSHADE><P>
+
+
+ <table border=0 cellspacing=4 cellpadding=3>
+
+ <tr>
+ <td width=100 valign="top"><a name="name"><STRONG>Name</STRONG></a></td>
+ <td valign="top">The name of the option.
+ Option names can be a maximum of 128 alphanumeric characters including spaces.</td>
+ </tr>
+
+ <tr>
+ <td width=100 valign="top"><a name="cat"><STRONG>Category</STRONG></a></td>
+ <td valign="top">The category of the option:
+ The category can be one of Site, Extend, or Vendor.<P>
+ <table border=0 cellspacing=3 cellpadding=3>
+
+
+ <tr>
+ <td width=100 valign="top"><U>Site</U></td>
+ <td valign="top">User-defined option definitions, used for
+ unique or custom purposes; accepts <a href="#code">Option
+ Codes</a> <tt>128-254</tt>.</td>
+ </tr>
+
+ <tr>
+ <td width=100 valign="top"><U>Extend</U></td>
+ <td valign="top">Option definitions beyond the standard set,
+ which may already be present in versions of the DHCP protocol
+ being used on your server. Rather than upgrading your DHCP
+ implementation, you can add extended options; accepts Option
+ Codes <tt>77-127</tt>.</td>
+ </tr>
+
+ <tr>
+ <td width=100 valign="top"><U>Vendor</U></td>
+ <td valign="top">Hardware-, vendor-, or platform-specific option
+ definitions; accepts Option Codes <tt>1-254</tt>. Selecting this
+ context enables the <a href="#class">Client Class</a>
+ parameter.</td>
+ </tr>
+
+ </table>
+ </td>
+ </tr>
+
+
+ <tr>
+ <td width=115 valign="top"><a name="code"><STRONG>Code</STRONG></a></td>
+ <td valign="top">A unique numeric code to
+ identify the option. Valid values depend on the option <a
+ href="#cat">category</a>:<P>
+
+ <table border=0 cellspacing=3 cellpadding=3>
+
+ <tr>
+ <td width=100 valign="top"><U>Extend</U></td>
+ <td valign="top">Accepts code values of <tt>77-127</tt><P></TD>
+ </tr>
+
+ <tr>
+ <td width=100 valign="top"><U>Site</U></td>
+ <td valign="top">Accepts code values of <tt>128-254</tt><P></TD>
+ </tr>
+
+ <tr>
+ <td width=100 valign="top"><U>Vendor</U></td>
+ <td valign="top">Accepts code values of <tt>1-254</tt></td>
+ </tr>
+
+</table>
+<P>
+</td>
+
+ </tr>
+
+ <tr>
+ <td width=100 valign="top"><A NAME="type"><STRONG>Data Type</STRONG></A></td>
+ <td valign="top">The type of data that is acceptable
+ for options values:<P>
+
+ <table border=0 cellspacing=3 cellpadding=3>
+
+ <tr>
+ <td width=100 valign="top"><u>Ascii</u></td>
+ <td valign="top">ASCII text.</td>
+ </tr>
+
+ <tr>
+ <td width=100 valign="top"><u>Boolean</u></td>
+ <td valign="top">No value is associated with this data type.
+ Presence of options of this type denote Boolean TRUE; absence
+ denotes FALSE.</td>
+ </tr>
+
+ <tr>
+ <td width=100 valign="top"><u>IP</u></td>
+ <td valign="top">One or more Internet addresses in dotted decimal format.</td>
+ </tr>
+ <tr>
+ <td width=100 valign="top"><u>Octet</u></td>
+ <td valign="top">Uninterpreted ASCII representation of binary
+ data; for example, a client ID.</td>
+ </tr>
+ <tr>
+ <td width=100 valign="top"><u>Unumber8</u><BR><u>Unumber16</u><BR>
+<u>Unumber32</u><BR><u>Unumber64</u><BR>
+
+</td>
+ <td valign="top">Unsigned number, where the trailing number indicates the number of bits in the number.</td>
+ </tr>
+ <tr>
+ <td width=100 valign="top"><u>Snumber8</u><BR><u>Snumber16</u><BR>
+<u>Snumber32</u><BR><u>Snumber64</u><BR>
+</td>
+ <td valign="top">Signed number, where the trailing number indicates the number of bits in the number.</td>
+ </tr>
+
+ </table>
+ </td>
+ </tr>
+
+
+ <tr>
+ <td width=100 valign="top"><a name="gran"><STRONG>Granularity</STRONG></a></td>
+ <td valign="top">Specifies how many values of the given <a
+ href="#type">data type</a> are required to make a complete option value.
+ For example, a data type of <tt>IP</tt> and a granularity of <tt>2</tt> would
+ mean that the option value would need to contain two IP addresses.</td>
+ </tr>
+
+ <tr>
+ <td width=100 valign="top"><a name="max"><STRONG>Maximum</STRONG></a></td>
+ <td valign="top">The maximum number of values of a
+ given granularity that can be specified for the option. Building on the
+ previous example, a maximum of <tt>2</tt>, with a granularity of
+ <tt>2</tt> and a data type of <tt>IP</tt> would mean that the option
+ value could contain a maximum of two pairs of IP addresses.<P>
+
+ <table border=0>
+
+ <tr>
+ <td width=100 valign="top"><U>Unlimited</U></td>
+ <td valign="top">No limit to the number of values; implies a
+ maximum setting of <tt>0</tt>.</td>
+ </tr>
+
+ <tr>
+ <td width=100 valign="top"><U>Specify</U></td>
+ <td valign="top">A specific number indicating the maximum.</td>
+ </tr>
+
+ </table>
+ </td>
+ </tr>
+
+ <tr>
+ <td width=100 valign="top"><A NAME="class"><STRONG>Vendor Client Classes</STRONG></A></td>
+ <td valign="top">This option is enabled only when the option
+ <a href="#cat">category</a> is Vendor. It identifies the client class(es) with which the Vendor option is
+ associated.<P>
+ The Class is an ASCII string representing the client machine type and/or operating system, for example, <TT>SUNW.Ultra5_10</TT>. This type of option makes it possible to define configuration parameters that are passed to all clients of the same class. <P>
+ Enter the client class and then press Enter. You can
+ specify multiple client classes. Only those DHCP clients with a client
+ class value matching one listed here will use the option.</td>
+ </tr>
+
+ </table>
+ <P>
+
+
+<strong>Notify DHCP Server of Change:</STRONG> Check the box if
+you want the DHCP server to reload the <tt>dhcptab</tt> immediately after
+the data is written. Normally, you will want this to
+happen. However, if you are making multiple changes, you may choose
+to wait so that the server reloads only after all the
+changes are written. <P>
+&nbsp;&nbsp;&nbsp;<A HREF="#top"><small>return to top</small></A>
+
+<p>&nbsp;</p>
+<!-- Don't go past this line! -->
+
+ </td>
+
+<!-- End topic block -->
+
+</tr>
+
+</table>
+
+<!-- End topic table -->
+
+</body>
+</html>
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_option_del.html b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_option_del.html
new file mode 100644
index 0000000000..a8d556e7f3
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_option_del.html
@@ -0,0 +1,128 @@
+<!--
+ -- ident "%W% %E% SMI"
+ --
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ -- Copyright (c) 2000-2001 by Sun Microsystems, Inc.
+ -- All rights reserved.
+ -->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<html>
+
+<head>
+<title>DHCP Manager Help: Delete Option</title>
+<meta NAME="AUTHOR" CONTENT="smorgan">
+<meta NAME="KEYWORDS" CONTENT="DHCP">
+
+</head>
+
+<body bgcolor="#FFFFFF">
+
+<a name="top"></a>
+
+<table border=0 cellspacing=0 cellpadding=0 width=615>
+
+<!-- Start navigation banner -->
+
+<tr><td colspan=4 WIDTH="615" align="center" valign="top"><IMG SRC="art/bannersmc.gif" WIDTH=615 BORDER=0 ALT="DHCP Manager Help">
+
+ </td>
+ </tr>
+
+<!-- End navigation banner -->
+
+
+<tr>
+
+<!-- Start contents block -->
+
+ <td colspan=1 valign="top" width=105>
+ <P>&nbsp;</P>
+
+ <A HREF="dhcp_main_top.html"><STRONG>Overview</STRONG></A><P>
+ <A HREF="dhcp_relay_ref.html"><STRONG>Servers and Relays</STRONG></A><P>
+ <A HREF="dhcp_addr_ref.html"><STRONG>IP Addresses</STRONG></A><P>
+ <A HREF="dhcp_macro_ref.html"><STRONG>Macros</STRONG></A><P>
+ <A HREF="dhcp_option_ref.html"><STRONG>Options</STRONG></A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_option_create.html">Create</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_option_mod.html">Modify</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_option_dup.html">Duplicate</A><BR>
+<IMG SRC="art/tip2.gif" WIDTH=12 HEIGHT=10 BORDER=0 ALT="">Delete<P>
+
+<A HREF="dhcp_main_how.html"><STRONG>How To..</STRONG></A><P>
+<A HREF="dhcp_main_menus.html"><STRONG>Menus</STRONG></A><P>
+<A HREF="dhcp_main_idx.html"><STRONG>Index</STRONG></A>
+
+
+ </td>
+
+
+
+<!-- End contents block -->
+
+<!-- Start column rule -->
+
+ <td colspan=1 width=5 bgcolor="#CCCCCC">&nbsp;</td>
+
+<!-- End column spacer -->
+
+<!-- Start column spacer -->
+
+ <td colspan=1 width=10 bgcolor="#FFFFFF">&nbsp;</td>
+
+<!-- End column spacer -->
+
+
+
+<!-- Start topic block -->
+
+
+ <td colspan=1 valign="top" bgcolor="#ffffff">
+ <P>&nbsp;</P>
+
+
+<h1>Delete Option</h1>
+
+Click OK to remove the selected option from the <tt>dhcptab</tt>.
+<P>
+<strong>Notify DHCP Server of Change:</STRONG> Check the box if
+you want the DHCP server to reload the <tt>dhcptab</tt> immediately after
+the data is written. Normally, you will want this to
+happen. However, if you are making multiple changes, you may choose
+to wait so that the server reloads only after all the
+changes are written. <P>
+
+<p>&nbsp;</p>
+
+<!-- Don't go past this line! -->
+
+ </td>
+
+<!-- End topic block -->
+
+</tr>
+
+</table>
+
+<!-- End topic table -->
+
+</body>
+</html>
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_option_dup.html b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_option_dup.html
new file mode 100644
index 0000000000..a9fccd9d41
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_option_dup.html
@@ -0,0 +1,310 @@
+<!--
+ -- ident "%W% %E% SMI"
+ --
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ -- Copyright (c) 2000-2001 by Sun Microsystems, Inc.
+ -- All rights reserved.
+ -->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<html>
+
+<head>
+<title>DHCP Manager Help: Duplicate Option</title>
+<meta NAME="AUTHOR" CONTENT="smorgan">
+<meta NAME="KEYWORDS" CONTENT="DHCP">
+
+</head>
+<body bgcolor="#FFFFFF">
+
+<a name="top"></a>
+
+<table border=0 cellspacing=0 cellpadding=0 width=615>
+
+<!-- Start navigation banner -->
+
+<tr><td colspan=4 WIDTH="615" align="center" valign="top"><IMG SRC="art/bannersmc.gif" WIDTH=615 BORDER=0 ALT="DHCP Manager Help">
+
+ </td>
+ </tr>
+
+<!-- End navigation banner -->
+
+
+<tr>
+
+<!-- Start contents block -->
+
+ <td colspan=1 valign="top" width=105>
+ <P>&nbsp;</P>
+
+ <A HREF="dhcp_main_top.html"><STRONG>Overview</STRONG></A><P>
+ <A HREF="dhcp_relay_ref.html"><STRONG>Servers and Relays</STRONG></A><P>
+ <A HREF="dhcp_addr_ref.html"><STRONG>IP Addresses</STRONG></A><P>
+ <A HREF="dhcp_macro_ref.html"><STRONG>Macros</STRONG></A><P>
+<STRONG>Options</STRONG><BR>
+
+ &nbsp;&nbsp;<A HREF="dhcp_option_create.html">Create</A><BR>
+<IMG SRC="art/tip2.gif" WIDTH=12 HEIGHT=10 BORDER=0 ALT="">Duplicate<BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<A HREF="#name"><EM>Name</EM></A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<A HREF="#cat"><EM>Category</EM></A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<A HREF="#code"><EM>Code</EM></A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<A HREF="#type"><EM>Data Type</EM></A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<A HREF="#gran"><EM>Granularity</EM></A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<A HREF="#max"><EM>Maximum</EM></A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<A HREF="#class"><EM>Class</EM></A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_option_mod.html">Modify</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_option_del.html">Delete</A><P>
+
+<A HREF="dhcp_main_how.html"><STRONG>How To..</STRONG></A><P>
+<A HREF="dhcp_main_menus.html"><STRONG>Menus</STRONG></A><P>
+<A HREF="dhcp_main_idx.html"><STRONG>Index</STRONG></A>
+
+
+ </td>
+
+
+
+<!-- End contents block -->
+
+<!-- Start column rule -->
+
+ <td colspan=1 width=5 bgcolor="#CCCCCC">&nbsp;</td>
+
+<!-- End column spacer -->
+
+<!-- Start column spacer -->
+
+ <td colspan=1 width=10 bgcolor="#FFFFFF">&nbsp;</td>
+
+<!-- End column spacer -->
+
+
+
+<!-- Start topic block -->
+
+
+ <td colspan=1 valign="top" bgcolor="#ffffff">
+ <P>&nbsp;</P>
+
+
+
+<h1>Duplicate Option</h1>
+
+The Duplicate Option dialog box provides a simple way to create a new option by copying some
+or all the properties of an existing option in the DHCP network tables. <p>
+
+See <a href="dhcp_macro_ref.html">
+About Macros and Options</a>, for more information about defining DHCP options.<p>
+The settings in the Duplicate Option dialog box are described in the following table.<P>
+
+<P><HR NOSHADE><P>
+
+<table border=0 cellspacing=4 cellpadding=3>
+
+ <tr>
+ <td width=100 valign="top"><a name="name"><STRONG>Name</STRONG></a></td>
+ <td valign="top">The name of the option.
+ Option names can be a maximum of 128 alphanumeric characters including spaces.</td>
+ </tr>
+
+ <tr>
+ <td width=100 valign="top"><a name="cat"><STRONG>Category</STRONG></a></td>
+ <td valign="top">The category of the option:
+ The category can be one of Site, Extend, or Vendor.<P>
+ <table border=0 cellspacing=3 cellpadding=3>
+
+
+ <tr>
+ <td width=100 valign="top"><U>Site</U></td>
+ <td valign="top">User-defined option definitions, used for
+ unique or custom purposes; accepts <a href="#code">Option
+ Codes</a> <tt>128-254</tt>.</td>
+ </tr>
+
+ <tr>
+ <td width=100 valign="top"><U>Extend</U></td>
+ <td valign="top">Option definitions beyond the standard set,
+ which may already be present in versions of the DHCP protocol
+ being used on your server. Rather than upgrading your DHCP
+ implementation, you can add extended options; accepts Option
+ Codes <tt>77-127</tt>.</td>
+ </tr>
+
+ <tr>
+ <td width=100 valign="top"><U>Vendor</U></td>
+ <td valign="top">Hardware-, vendor-, or platform-specific option
+ definitions; accepts Option Codes <tt>1-254</tt>. Selecting this
+ context enables the <a href="#class">Client Class</a>
+ parameter.</td>
+ </tr>
+
+ </table>
+ </td>
+ </tr>
+
+
+ <tr>
+ <td width=115 valign="top"><a name="code"><STRONG>Code</STRONG></a></td>
+ <td valign="top">A unique numeric code to
+ identify the option. Valid values depend on the option <a
+ href="#cat">category</a>:<P>
+
+ <table border=0 cellspacing=3 cellpadding=3>
+
+ <tr>
+ <td width=100 valign="top"><U>Extend</U></td>
+ <td valign="top">Accepts code values of <tt>77-127</tt><P></TD>
+ </tr>
+
+ <tr>
+ <td width=100 valign="top"><U>Site</U></td>
+ <td valign="top">Accepts code values of <tt>128-254</tt><P></TD>
+ </tr>
+
+ <tr>
+ <td width=100 valign="top"><U>Vendor</U></td>
+ <td valign="top">Accepts code values of <tt>1-254</tt></td>
+ </tr>
+
+</table>
+
+
+<P>
+</td>
+
+ </tr>
+
+ <tr>
+ <td width=100 valign="top"><A NAME="type"><STRONG>Data Type</STRONG></A></td>
+ <td valign="top">The type of data that is acceptable
+ for options values:<P>
+ <table border=0 cellspacing=3 cellpadding=3>
+
+ <tr>
+ <td width=100 valign="top"><u>Ascii</u></td>
+ <td valign="top">ASCII text.</td>
+ </tr>
+
+ <tr>
+ <td width=100 valign="top"><u>Boolean</u></td>
+ <td valign="top">No value is associated with this data type.
+ Presence of options of this type denote Boolean TRUE; absence
+ denotes FALSE.</td>
+ </tr>
+
+ <tr>
+ <td width=100 valign="top"><u>IP</u></td>
+ <td valign="top">One or more Internet addresses in dotted decimal format.</td>
+ </tr>
+ <tr>
+ <td width=100 valign="top"><u>Octet</u></td>
+ <td valign="top">Uninterpreted ASCII representation of binary
+ data; for example, a client ID.</td>
+ </tr>
+ <tr>
+ <td width=100 valign="top"><u>Unumber8</u><BR><u>Unumber16</u><BR>
+<u>Unumber32</u><BR><u>Unumber64</u><BR>
+
+</td>
+ <td valign="top">Unsigned number, where the trailing number indicates the number of bits in the number.</td>
+ </tr>
+ <tr>
+ <td width=100 valign="top"><u>Snumber8</u><BR><u>Snumber16</u><BR>
+<u>Snumber32</u><BR><u>Snumber64</u><BR>
+</td>
+ <td valign="top">Signed number, where the trailing number indicates the number of bits in the number.</td>
+ </tr>
+
+ </table>
+ </td>
+ </tr>
+
+
+ <tr>
+ <td width=100 valign="top"><a name="gran"><STRONG>Granularity</STRONG></a></td>
+ <td valign="top">Specifies how many values of the given <a
+ href="#type">data type</a> are required to make a complete option value.
+ For example, a data type of <tt>IP</tt> and a granularity of <tt>2</tt> would
+ mean that the option value would need to contain two IP addresses.</td>
+ </tr>
+
+ <tr>
+ <td width=100 valign="top"><a name="max"><STRONG>Maximum</STRONG></a></td>
+ <td valign="top">The maximum number of values of a
+ given granularity that can be specified for the option. Building on the
+ previous example, a maximum of <tt>2</tt>, with a granularity of
+ <tt>2</tt> and a data type of <tt>IP</tt> would mean that the option
+ value could contain a maximum of two pairs of IP addresses.<P>
+
+ <table border=0>
+
+ <tr>
+ <td width=100 valign="top"><U>Unlimited</U></td>
+ <td valign="top">No limit to the number of values; implies a
+ maximum setting of <tt>0</tt>.</td>
+ </tr>
+
+ <tr>
+ <td width=100 valign="top"><U>Specify</U></td>
+ <td valign="top">A specific number indicating the maximum.</td>
+ </tr>
+
+ </table>
+ </td>
+ </tr>
+
+ <tr>
+ <td width=100 valign="top"><A NAME="class"><STRONG>Vendor Client Classes</STRONG></A></td>
+ <td valign="top">This option is enabled only when the option
+ <a href="#cat">category</a> is Vendor. It identifies the client class(es) with which the Vendor option is
+ associated.<P>
+ The Class is an ASCII string representing the client machine type and/or operating system, for example, <TT>SUNW.Ultra5_10</TT>. This type of option makes it possible to define configuration parameters that are passed to all clients of the same class. <P>
+ Enter the client class and then press Enter. You can
+ specify multiple client classes. Only those DHCP clients with a client
+ class value matching one listed here will use the option.</td>
+ </tr>
+
+ </table>
+<p>
+<strong>Notify DHCP Server of Change:</STRONG> Check the box if
+you want the DHCP server to reload the <tt>dhcptab</tt> immediately after
+the data is written. Normally, you will want this to
+happen. However, if you are making multiple changes, you may choose
+to wait so that the server reloads only after all the
+changes are written. <P>
+ &nbsp;&nbsp;&nbsp;<A HREF="#top"><small>return to top</small></A>
+
+<p>&nbsp;</p>
+<!-- Don't go past this line! -->
+
+ </td>
+
+<!-- End topic block -->
+
+</tr>
+
+</table>
+
+<!-- End topic table -->
+
+</body>
+</html>
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_option_how.html b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_option_how.html
new file mode 100644
index 0000000000..a958929ac1
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_option_how.html
@@ -0,0 +1,225 @@
+<!--
+ -- ident "%W% %E% SMI"
+ --
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ -- Copyright (c) 2000-2001 by Sun Microsystems, Inc.
+ -- All rights reserved.
+ -->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<html>
+
+<head>
+<title>DHCP Manager Help: How To... Options</title>
+<meta NAME="AUTHOR" CONTENT="smorgan">
+<meta NAME="KEYWORDS" CONTENT="DHCP">
+
+</head>
+
+<body bgcolor="#FFFFFF">
+
+<a name="top"></a>
+
+<table border=0 cellspacing=0 cellpadding=0 width=615>
+
+<!-- Start navigation banner -->
+
+<tr><td colspan=4 WIDTH="615" align="center" valign="top"><IMG SRC="art/bannersmc.gif" WIDTH=615 BORDER=0 ALT="DHCP Manager Help">
+
+ </td>
+ </tr>
+
+<!-- End navigation banner -->
+
+
+<tr>
+
+<!-- Start contents block -->
+
+ <td colspan=1 valign="top" width=105>
+ <P>&nbsp;</P>
+ <A HREF="dhcp_main_top.html"><STRONG>Overview</STRONG></A><P>
+ <A HREF="dhcp_relay_ref.html"><STRONG>Servers and Relay Agents</STRONG></A><P>
+ <A HREF="dhcp_addr_ref.html"><STRONG>IP Addresses</STRONG></A><P>
+ <A HREF="dhcp_macro_ref.html"><STRONG>Macros</STRONG></A><P>
+ <A HREF="dhcp_option_ref.html"><STRONG>Options</STRONG></A><P>
+
+ <A HREF="dhcp_main_how.html"><STRONG>How To..</STRONG></A><BR>
+&nbsp;&nbsp;<A HREF="dhcp_relay_how.html">Servers</A><BR>
+&nbsp;&nbsp;<A HREF="dhcp_addr_how.html">Addresses</A><BR>
+&nbsp;&nbsp;<A HREF="dhcp_macro_how.html">Macros</A><BR>
+<IMG SRC="art/tip2.gif" WIDTH=12 HEIGHT=10 BORDER=0 ALT="">Options<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#viewoption"><EM>View</EM></a><BR>
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#newoption"><EM>Create</EM></a><BR>
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#dupoption"><EM>Duplicate</EM></a><BR>
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#modoption"><EM>Modify</EM></a><BR>
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#deloption"><EM>Delete</EM></a><P>
+<A HREF="dhcp_main_menus.html"><STRONG>Menus</STRONG></A><P>
+<A HREF="dhcp_main_idx.html"><STRONG>Index</STRONG></A>
+
+ </td>
+
+<!-- End contents block -->
+
+<!-- Start column rule -->
+
+ <td colspan=1 width=5 bgcolor="#CCCCCC">&nbsp;</td>
+
+<!-- End column spacer -->
+
+<!-- Start column spacer -->
+
+ <td colspan=1 width=10 bgcolor="#FFFFFF">&nbsp;</td>
+
+<!-- End column spacer -->
+
+
+
+<!-- Start topic block -->
+
+<td colspan=1 valign="top" bgcolor="#ffffff">
+<P>&nbsp;</P>
+<H1>How To: Options</H1>
+
+<strong><big><A NAME="viewoption">View Options</A></big></strong><p>
+
+<strong>Choose the <A HREF="dhcp_option_ref.html">Options</A> tab in the main window.</strong>
+ <p>
+
+ DHCP Manager displays a list of options in the
+ DHCP server's <tt>dhcptab</tt>.<p>
+
+
+<HR NOSHADE>
+
+<strong><big><A NAME="newoption">Create</A> a New Option</big></strong><p>
+
+ <ol type=1>
+
+ <LI><strong>Choose the <A HREF="dhcp_option_ref.html">Options</A> tab in the main window.</strong><BR>
+ DHCP Manager displays a list of options in the DHCP
+database.<P></li>
+ <LI><strong>Choose <a href="dhcp_option_create.html">Create</A> from the Edit menu.</strong><BR>
+
+ The Create Option dialog box opens.<p></li>
+
+ <li><strong>Enter the desired settings for the new
+ option.</strong><BR>
+ You can enter the following settings in the Create Option dialog box:
+<UL TYPE="DISC">
+<LI>Option name</LI>
+<LI>Category</LI>
+<LI>Client Class</LI>
+<LI>Code</LI>
+<LI>Data type</LI>
+<LI>Granularity</LI>
+<LI>Maximum</LI>
+</UL><P>
+ <li><strong>Click OK to accept your settings.</strong><BR>
+
+ The new option definition is stored in the <tt>dhcptab</tt>,
+ and is available to include in a macro.
+ <p></li>
+
+ </ol>
+
+<HR NOSHADE>
+
+<strong><big><A NAME="dupoption">Duplicate an Option Definition</A></big></strong><p>
+
+ <ol type=1>
+
+ <LI><strong>Choose the <A HREF="dhcp_option_ref.html">Options</A> tab in the main window.</strong><BR>
+
+ DHCP Manager displays a list of options.<P></LI>
+
+ <li><strong>Choose the option definition you want to
+ duplicate.</strong></li><P>
+
+ <li><strong>Choose <A HREF="dhcp_option_dup.html">Duplicate</A> from the Edit menu.</strong><BR>
+
+ The Duplicate Option Properties dialog box is displayed. The fields display the settings of the selected option.<p></li>
+
+ <li><strong>Enter a name for the new option.</strong></li><p>
+
+ <li><strong>Modify the option definition settings as desired, and then
+ click OK.</strong><BR>
+ The new option definition is stored in the <tt>dhcptab</tt>.</li>
+</OL>
+
+
+<HR NOSHADE>
+<strong><big><A NAME="modoption">Modify an Option Definition</A></big></strong><p>
+ <ol type=1>
+
+ <LI><strong>Choose the <A HREF="dhcp_option_ref.html">Options</A> tab in the main window.</strong><BR>
+ DHCP Manager displays a list of options.<P></LI>
+
+ <li><strong>Choose the option definition you want to
+ modify.</strong><p></li>
+
+ <li><strong>Double-click the option or choose <A HREF="dhcp_option_mod.html">Properties</A> from the
+ Edit menu.</strong><BR>
+ The Option Properties dialog box is displayed.<p></li>
+
+ <li><strong>Modify the option definition settings as desired, and then
+ click OK.</strong><BR>
+ The new settings are updated in the <tt>dhcptab</tt>.</li>
+
+ </ol>
+
+<HR NOSHADE>
+
+<strong><big><A NAME="deloption">Delete an Option Definition</A></big></strong><p>
+
+ <ol type=1>
+
+ <LI><strong>Choose the <A HREF="dhcp_option_ref.html">Options</A> tab in the main window.</strong><BR>
+
+ DHCP Manager displays the list of defined options. <p></li>
+
+ <li><strong>Choose the option definition you want to
+ delete.</strong><p></li>
+
+ <li><strong>Choose <A HREF="dhcp_option_del.html">Delete</A> from the Edit menu.</strong><BR>
+
+ The option definition is deleted from the <tt>dhcptab</tt>
+ database.<p></li>
+
+ </ol>
+ &nbsp;&nbsp;&nbsp;<A HREF="#top"><small>return to top</small></A>
+
+<p>&nbsp;</p>
+
+<!-- Don't go past this line! -->
+
+ </td>
+
+<!-- End topic block -->
+
+</tr>
+
+</table>
+
+<!-- End topic table -->
+
+</body>
+</html>
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_option_mod.html b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_option_mod.html
new file mode 100644
index 0000000000..f074c4d040
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_option_mod.html
@@ -0,0 +1,316 @@
+<!--
+ -- ident "%W% %E% SMI"
+ --
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ -- Copyright (c) 2000-2001 by Sun Microsystems, Inc.
+ -- All rights reserved.
+ -->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<html>
+
+<head>
+<title>DHCP Manager Help: Modify Option</title>
+<meta NAME="AUTHOR" CONTENT="smorgan">
+<meta NAME="KEYWORDS" CONTENT="DHCP">
+
+</head>
+
+<body bgcolor="#FFFFFF">
+
+<a name="top"></a>
+
+<table border=0 cellspacing=0 cellpadding=0 width=615>
+
+<!-- Start navigation banner -->
+
+<tr><td colspan=4 WIDTH="615" align="center" valign="top"><IMG SRC="art/bannersmc.gif" WIDTH=615 BORDER=0 ALT="DHCP Manager Help">
+
+ </td>
+ </tr>
+
+<!-- End navigation banner -->
+
+
+<tr>
+
+<!-- Start contents block -->
+
+ <td colspan=1 valign="top" width=105>
+ <P>&nbsp;</P>
+
+ <A HREF="dhcp_main_top.html"><STRONG>Overview</STRONG></A><P>
+ <A HREF="dhcp_relay_ref.html"><STRONG>Servers and Relays</STRONG></A><P>
+ <A HREF="dhcp_addr_ref.html"><STRONG>IP Addresses</STRONG></A><P>
+ <A HREF="dhcp_macro_ref.html"><STRONG>Macros</STRONG></A><P>
+ <A HREF="dhcp_option_ref.html"><STRONG>Options</STRONG></A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_option_create.html">Create</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_option_dup.html">Duplicate</A><BR>
+
+ <IMG SRC="art/tip2.gif" WIDTH=12 HEIGHT=10 BORDER=0 ALT="">Modify<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="#name"><EM>Name</EM></A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<A HREF="#cat"><EM>Category</EM></A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<A HREF="#code"><EM>Code</EM></A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<A HREF="#type"><EM>Data Type</EM></A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<A HREF="#gran"><EM>Granularity</EM></A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<A HREF="#max"><EM>Maximum</EM></A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<A HREF="#class"><EM>Class</EM></A><BR>
+
+ &nbsp;&nbsp;<A HREF="dhcp_option_del.html">Delete</A><P>
+
+<A HREF="dhcp_main_how.html"><STRONG>How To..</STRONG></A><P>
+<A HREF="dhcp_main_menus.html"><STRONG>Menus</STRONG></A><P>
+<A HREF="dhcp_main_idx.html"><STRONG>Index</STRONG></A>
+
+
+ </td>
+
+
+
+<!-- End contents block -->
+
+<!-- Start column rule -->
+
+ <td colspan=1 width=5 bgcolor="#CCCCCC">&nbsp;</td>
+
+<!-- End column spacer -->
+
+<!-- Start column spacer -->
+
+ <td colspan=1 width=10 bgcolor="#FFFFFF">&nbsp;</td>
+
+<!-- End column spacer -->
+
+
+
+<!-- Start topic block -->
+
+
+ <td colspan=1 valign="top" bgcolor="#ffffff">
+ <P>&nbsp;</P>
+
+
+
+<h1>Modify Option</h1>
+
+The Option Properties dialog box lets you modify existing settings for an option in the
+DHCP server's network database. <p>
+
+See <a href="dhcp_macros_about.html">
+About Macros and Options</a>, for more information about defining DHCP options.<p>
+The settings in the Modify Option dialog box are described in the following table.<P>
+<P><HR NOSHADE><P>
+
+
+<table border=0 cellspacing=4 cellpadding=3>
+
+ <tr>
+ <td width=100 valign="top"><a name="name"><STRONG>Name</STRONG></a></td>
+ <td valign="top">The name of the option.
+ Option names can be a maximum of 128 alphanumeric characters including spaces.</td>
+ </tr>
+
+ <tr>
+ <td width=100 valign="top"><a name="cat"><STRONG>Category</STRONG></a></td>
+ <td valign="top">The category of the option:
+ The category can be one of Site, Extend, or Vendor.<P>
+ <table border=0 cellspacing=3 cellpadding=3>
+
+
+ <tr>
+ <td width=100 valign="top"><U>Site</U></td>
+ <td valign="top">User-defined option definitions, used for
+ unique or custom purposes; accepts <a href="#code">Option
+ Codes</a> <tt>128-254</tt>.</td>
+ </tr>
+
+ <tr>
+ <td width=100 valign="top"><U>Extend</U></td>
+ <td valign="top">Option definitions beyond the standard set,
+ which may already be present in versions of the DHCP protocol
+ being used on your server. Rather than upgrading your DHCP
+ implementation, you can add extended options; accepts Option
+ Codes <tt>77-127</tt>.</td>
+ </tr>
+
+ <tr>
+ <td width=100 valign="top"><U>Vendor</U></td>
+ <td valign="top">Hardware-, vendor-, or platform-specific option
+ definitions; accepts Option Codes <tt>1-254</tt>. Selecting this
+ context enables the <a href="#class">Client Class</a>
+ parameter.</td>
+ </tr>
+
+ </table>
+ </td>
+ </tr>
+
+
+ <tr>
+ <td width=115 valign="top"><a name="code"><STRONG>Code</STRONG></a></td>
+ <td valign="top">A unique numeric code to
+ identify the option. Valid values depend on the option <a
+ href="#cat">category</a>:<P>
+
+ <table border=0 cellspacing=3 cellpadding=3>
+
+ <tr>
+ <td width=100 valign="top"><U>Extend</U></td>
+ <td valign="top">Accepts code values of <tt>77-127</tt><P></TD>
+ </tr>
+
+ <tr>
+ <td width=100 valign="top"><U>Site</U></td>
+ <td valign="top">Accepts code values of <tt>128-254</tt><P></TD>
+ </tr>
+
+ <tr>
+ <td width=100 valign="top"><U>Vendor</U></td>
+ <td valign="top">Accepts code values of <tt>1-254</tt></td>
+ </tr>
+
+</table>
+
+<P>
+</td>
+
+ </tr>
+
+ <tr>
+ <td width=100 valign="top"><A NAME="type"><STRONG>Data Type</STRONG></A></td>
+ <td valign="top">The type of data that is acceptable
+ for options values:<P>
+
+ <table border=0 cellspacing=3 cellpadding=3>
+
+ <tr>
+ <td width=100 valign="top"><u>Ascii</u></td>
+ <td valign="top">ASCII text.</td>
+ </tr>
+
+ <tr>
+ <td width=100 valign="top"><u>Boolean</u></td>
+ <td valign="top">No value is associated with this data type.
+ Presence of options of this type denote Boolean TRUE; absence
+ denotes FALSE.</td>
+ </tr>
+
+ <tr>
+ <td width=100 valign="top"><u>IP</u></td>
+ <td valign="top">One or more Internet addresses in dotted decimal format.</td>
+ </tr>
+ <tr>
+ <td width=100 valign="top"><u>Octet</u></td>
+ <td valign="top">Uninterpreted ASCII representation of binary
+ data; for example, a client ID.</td>
+ </tr>
+ <tr>
+ <td width=100 valign="top"><u>Unumber8</u><BR><u>Unumber16</u><BR>
+<u>Unumber32</u><BR><u>Unumber64</u><BR>
+
+</td>
+ <td valign="top">Unsigned number, where the trailing number indicates the number of bits in the number.</td>
+ </tr>
+ <tr>
+ <td width=100 valign="top"><u>Snumber8</u><BR><u>Snumber16</u><BR>
+<u>Snumber32</u><BR><u>Snumber64</u><BR>
+</td>
+ <td valign="top">Signed number, where the trailing number indicates the number of bits in the number.</td>
+ </tr>
+
+ </table>
+ </td>
+ </tr>
+
+
+ <tr>
+ <td width=100 valign="top"><a name="gran"><STRONG>Granularity</STRONG></a></td>
+ <td valign="top">Specifies how many values of the given <a
+ href="#type">data type</a> are required to make a complete option value.
+ For example, a data type of <tt>IP</tt> and a granularity of <tt>2</tt> would
+ mean that the option value would need to contain two IP addresses.</td>
+ </tr>
+
+ <tr>
+ <td width=100 valign="top"><a name="max"><STRONG>Maximum</STRONG></a></td>
+ <td valign="top">The maximum number of values of a
+ given granularity that can be specified for the option. Building on the
+ previous example, a maximum of <tt>2</tt>, with a granularity of
+ <tt>2</tt> and a data type of <tt>IP</tt> would mean that the option
+ value could contain a maximum of two pairs of IP addresses.<P>
+
+ <table border=0>
+
+ <tr>
+ <td width=100 valign="top"><U>Unlimited</U></td>
+ <td valign="top">No limit to the number of values; implies a
+ maximum setting of <tt>0</tt>.</td>
+ </tr>
+
+ <tr>
+ <td width=100 valign="top"><U>Specify</U></td>
+ <td valign="top">A specific number indicating the maximum.</td>
+ </tr>
+
+ </table>
+ </td>
+ </tr>
+
+
+ <tr>
+ <td width=100 valign="top"><A NAME="class"><STRONG>Vendor Client Classes</STRONG></A></td>
+ <td valign="top">This option is enabled only when the option
+ <a href="#cat">category</a> is Vendor. It identifies the client class(es) with which the Vendor option is
+ associated.<P>
+ The Class is an ASCII string representing the client machine type and/or operating system, for example, <TT>SUNW.Ultra5_10</TT>. This type of option makes it possible to define configuration parameters that are passed to all clients of the same class. <P>
+ Enter the client class and then press Enter. You can
+ specify multiple client classes. Only those DHCP clients with a client
+ class value matching one listed here will use the option.</td>
+ </tr>
+
+
+ </table>
+ <P>
+
+<strong>Notify DHCP Server of Change:</STRONG> Check the box if
+you want the DHCP server to reload the <tt>dhcptab</tt> immediately after
+the data is written. Normally, you will want this to
+happen. However, if you are making multiple changes, you may choose
+to wait so that the server reloads only after all the
+changes are written. <P>
+&nbsp;&nbsp;&nbsp;<A HREF="#top"><small>return to top</small></A>
+
+<p>&nbsp;</p>
+
+<!-- Don't go past this line! -->
+
+ </td>
+
+<!-- End topic block -->
+
+</tr>
+
+</table>
+
+<!-- End topic table -->
+
+</body>
+</html>
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_option_ref.html b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_option_ref.html
new file mode 100644
index 0000000000..724655b7f2
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_option_ref.html
@@ -0,0 +1,307 @@
+<!--
+ -- ident "%W% %E% SMI"
+ --
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ -- Copyright (c) 2000-2001 by Sun Microsystems, Inc.
+ -- All rights reserved.
+ -->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<html>
+
+<head>
+<title>DHCP Manager Help: Options</title>
+<meta NAME="AUTHOR" CONTENT="smorgan">
+<meta NAME="KEYWORDS" CONTENT="DHCP">
+
+</head>
+
+<body bgcolor="#FFFFFF">
+
+<a name="top"></a>
+
+<table border=0 cellspacing=0 cellpadding=0 width=615>
+
+<!-- Start navigation banner -->
+
+<tr><td colspan=4 WIDTH="615" align="center" valign="top"><IMG SRC="art/bannersmc.gif" WIDTH=615 BORDER=0 ALT="DHCP Manager Help">
+
+ </td>
+ </tr>
+
+<!-- End navigation banner -->
+
+
+<tr>
+
+<!-- Start contents block -->
+
+ <td colspan=1 valign="top" width=105>
+ <P>&nbsp;</P>
+
+ <A HREF="dhcp_main_top.html"><STRONG>Overview</STRONG></A><P>
+ <A HREF="dhcp_relay_ref.html"><STRONG>Servers and Relays</STRONG></A><P>
+ <A HREF="dhcp_addr_ref.html"><STRONG>IP Addresses</STRONG></A><P>
+ <A HREF="dhcp_macro_ref.html"><STRONG>Macros</STRONG></A><P>
+<IMG SRC="art/tip2.gif" WIDTH=12 HEIGHT=10 BORDER=0 ALT=""><STRONG>Options</STRONG><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<A HREF="#name"><EM>Name</EM></A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<A HREF="#cat"><EM>Category</EM></A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<A HREF="#code"><EM>Code</EM></A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<A HREF="#type"><EM>Type</EM></A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<A HREF="#gran"><EM>Granularity</EM></A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<A HREF="#max"><EM>Maximum</EM></A><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<A HREF="#class"><EM>Class</EM></A><BR>
+
+ &nbsp;&nbsp;<A HREF="dhcp_option_create.html">Create</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_option_dup.html">Duplicate</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_option_mod.html">Modify</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_option_del.html">Delete</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_option_tags.html">Standard options</A><P>
+
+<A HREF="dhcp_main_how.html"><STRONG>How To..</STRONG></A><P>
+<A HREF="dhcp_main_menus.html"><STRONG>Menus</STRONG></A><P>
+<A HREF="dhcp_main_idx.html"><STRONG>Index</STRONG></A>
+
+
+ </td>
+
+
+
+<!-- End contents block -->
+
+<!-- Start column rule -->
+
+ <td colspan=1 width=5 bgcolor="#CCCCCC">&nbsp;</td>
+
+<!-- End column spacer -->
+
+<!-- Start column spacer -->
+
+ <td colspan=1 width=10 bgcolor="#FFFFFF">&nbsp;</td>
+
+<!-- End column spacer -->
+
+
+
+<!-- Start topic block -->
+
+
+ <td colspan=1 valign="top" bgcolor="#ffffff">
+ <P>&nbsp;</P>
+
+
+<h1>Options</h1>
+
+Select the Options tab in the main window to see a list of options defined
+for this DHCP server. Options are listed alphabetically by name.<P>
+
+Options (or symbols) define the configuration parameters that the DHCP server passes to the clients. See <A HREF="dhcp_macros_about.html">About Macros and Options</A> for a discussion of how to use macros and options and of the relationship between them.<P>
+You can <A HREF="dhcp_option_create.html">add</A>, <A HREF="dhcp_option_mod.html">modify</A>, or <A HREF="dhcp_option_del.html">delete</A> an option by selecting it and
+clicking the appropriate entry in the Edit menu.<P>
+
+The option settings are described below.
+<P><HR NOSHADE><P>
+
+ <table border=0 cellspacing=4 cellpadding=3>
+
+ <tr>
+ <td width=100 valign="top"><a name="name"><STRONG>Name</STRONG></a></td>
+ <td valign="top">The name of the option.
+ Option names can be a maximum of 128 alphanumeric characters including spaces.</td>
+ </tr>
+
+ <tr>
+ <td width=100 valign="top"><a name="cat"><STRONG>Category</STRONG></a></td>
+ <td valign="top">The category of the option:
+ The category can be one of Site, Extend, or Vendor.<P>
+ <table border=0 cellspacing=3 cellpadding=3>
+
+
+ <tr>
+ <td width=100 valign="top"><U>Site</U></td>
+ <td valign="top">User-defined option definitions, used for
+ unique or custom purposes; accepts <a href="#code">Option
+ Codes</a> <tt>128-254</tt>.</td>
+ </tr>
+
+ <tr>
+ <td width=100 valign="top"><U>Extend</U></td>
+ <td valign="top">Option definitions beyond the standard set,
+ which may already be present in versions of the DHCP protocol
+ being used on your server. Rather than upgrading your DHCP
+ implementation, you can add extended options; accepts Option
+ Codes <tt>77-127</tt>.</td>
+ </tr>
+
+ <tr>
+ <td width=100 valign="top"><U>Vendor</U></td>
+ <td valign="top">Hardware-, vendor-, or platform-specific option
+ definitions; accepts Option Codes <tt>1-254</tt>. Selecting this
+ context enables the <a href="#class">Client Class</a>
+ parameter.</td>
+ </tr>
+
+ </table>
+ </td>
+ </tr>
+
+
+ <tr>
+ <td width=115 valign="top"><a name="code"><STRONG>Code</STRONG></a></td>
+ <td valign="top">A unique numeric code to
+ identify the option. Valid values depend on the option <a
+ href="#cat">category</a>:<P>
+
+ <table border=0 cellspacing=3 cellpadding=3>
+
+ <tr>
+ <td width=100 valign="top"><U>Extend</U></td>
+ <td valign="top">Accepts code values of <tt>77-127</tt><P></TD>
+ </tr>
+
+ <tr>
+ <td width=100 valign="top"><U>Site</U></td>
+ <td valign="top">Accepts code values of <tt>128-254</tt><P></TD>
+ </tr>
+
+ <tr>
+ <td width=100 valign="top"><U>Vendor</U></td>
+ <td valign="top">Accepts code values of <tt>1-254</tt></td>
+ </tr>
+
+</table>
+<P>
+</td>
+
+ </tr>
+
+ <tr>
+ <td width=100 valign="top"><A NAME="type"><STRONG>Data Type</STRONG></A></td>
+ <td valign="top">The type of data that is acceptable
+ for options values:<P>
+
+ <table border=0 cellspacing=3 cellpadding=3>
+
+ <tr>
+ <td width=100 valign="top"><u>Ascii</u></td>
+ <td valign="top">ASCII text.</td>
+ </tr>
+
+ <tr>
+ <td width=100 valign="top"><u>Boolean</u></td>
+ <td valign="top">No value is associated with this data type.
+ Presence of options of this type denote Boolean TRUE; absence
+ denotes FALSE.</td>
+ </tr>
+
+ <tr>
+ <td width=100 valign="top"><u>IP</u></td>
+ <td valign="top">One or more Internet addresses in dotted decimal format.</td>
+ </tr>
+ <tr>
+ <td width=100 valign="top"><u>Octet</u></td>
+ <td valign="top">Uninterpreted ASCII representation of binary
+ data; for example, a client ID.</td>
+ </tr>
+ <tr>
+ <td width=100 valign="top"><u>Unumber8</u><BR><u>Unumber16</u><BR>
+<u>Unumber32</u><BR><u>Unumber64</u><BR>
+
+</td>
+ <td valign="top">Unsigned number, where the trailing number indicates the number of bits in the number.</td>
+ </tr>
+ <tr>
+ <td width=100 valign="top"><u>Snumber8</u><BR><u>Snumber16</u><BR>
+<u>Snumber32</u><BR><u>Snumber64</u><BR>
+</td>
+ <td valign="top">Signed number, where the trailing number indicates the number of bits in the number.</td>
+ </tr>
+
+ </table>
+ </td>
+ </tr>
+
+
+ <tr>
+ <td width=100 valign="top"><a name="gran"><STRONG>Granularity</STRONG></a></td>
+ <td valign="top">Specifies how many values of the given <a
+ href="#type">data type</a> are required to make a complete option value.
+ For example, a data type of <tt>IP</tt> and a granularity of <tt>2</tt> would
+ mean that the option value would need to contain two IP addresses.</td>
+ </tr>
+
+ <tr>
+ <td width=100 valign="top"><a name="max"><STRONG>Maximum</STRONG></a></td>
+ <td valign="top">The maximum number of values of a
+ given granularity that can be specified for the option. Building on the
+ previous example, a maximum of <tt>2</tt>, with a granularity of
+ <tt>2</tt> and a data type of <tt>IP</tt> would mean that the option
+ value could contain a maximum of two pairs of IP addresses.<P>
+
+ <table border=0>
+
+ <tr>
+ <td width=100 valign="top"><U>Unlimited</U></td>
+ <td valign="top">No limit to the number of values; implies a
+ maximum setting of <tt>0</tt>.</td>
+ </tr>
+
+ <tr>
+ <td width=100 valign="top"><U>Specify</U></td>
+ <td valign="top">A specific number indicating the maximum.</td>
+ </tr>
+
+ </table>
+ </td>
+ </tr>
+
+ <tr>
+ <td width=100 valign="top"><A NAME="class"><STRONG>Vendor Client Classes</STRONG></A></td>
+ <td valign="top">This option is enabled only when the option
+ <a href="#cat">category</a> is Vendor. It identifies the client class(es) with which the Vendor option is
+ associated.<P>
+ The Class is an ASCII string representing the client machine type and/or operating system, for example, <TT>SUNW.Ultra5_10</TT>. This type of option makes it possible to define configuration parameters that are passed to all clients of the same class. <P>
+ Enter the client class and then press Enter. You can
+ specify multiple client classes. Only those DHCP clients with a client
+ class value matching one listed here will use the option.</td>
+ </tr>
+ </table>
+
+
+ &nbsp;&nbsp;&nbsp;<A HREF="#top"><small>return to top</small></A>
+
+
+<p>&nbsp;</p>
+
+<!-- Don't go past this line! -->
+
+ </td>
+
+<!-- End topic block -->
+
+</tr>
+
+</table>
+
+<!-- End topic table -->
+
+</body>
+</html>
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_option_tags.html b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_option_tags.html
new file mode 100644
index 0000000000..72228b2fb1
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_option_tags.html
@@ -0,0 +1,542 @@
+<!--
+ -- ident "%W% %E% SMI"
+ --
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ -- Copyright (c) 2000-2001 by Sun Microsystems, Inc.
+ -- All rights reserved.
+ -->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<html>
+
+<head>
+<title>DHCP Manager Help: Options</title>
+<meta NAME="AUTHOR" CONTENT="smorgan">
+<meta NAME="KEYWORDS" CONTENT="DHCP">
+
+</head>
+
+<body bgcolor="#FFFFFF">
+
+<a name="top"></a>
+
+<table border=0 cellspacing=0 cellpadding=0 width=615>
+
+<!-- Start navigation banner -->
+
+<tr><td colspan=4 WIDTH="615" align="center" valign="top"><IMG SRC="art/bannersmc.gif" WIDTH=615 BORDER=0 ALT="DHCP Manager Help">
+
+ </td>
+ </tr>
+
+<!-- End navigation banner -->
+
+
+<tr>
+
+<!-- Start contents block -->
+
+ <td colspan=1 valign="top" width=105>
+ <P>&nbsp;</P>
+
+ <A HREF="dhcp_main_top.html"><STRONG>Overview</STRONG></A><P>
+ <A HREF="dhcp_relay_ref.html"><STRONG>Servers and Relays</STRONG></A><P>
+ <A HREF="dhcp_addr_ref.html"><STRONG>IP Addresses</STRONG></A><P>
+ <A HREF="dhcp_macro_ref.html"><STRONG>Macros</STRONG></A><P>
+ <A HREF="dhcp_option_ref.html"><STRONG>Options</STRONG></A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_option_create.html">Create</A><BR>
+
+ &nbsp;&nbsp;<A HREF="dhcp_option_mod.html">Modify</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_option_del.html">Delete</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_option_dup.html">Duplicate</A><BR>
+<IMG SRC="art/tip2.gif" WIDTH=12 HEIGHT=10 BORDER=0 ALT=""> <A name="#tags">Standard Options</A><P>
+
+
+<A HREF="dhcp_main_how.html"><STRONG>How To..</STRONG></A><P>
+<A HREF="dhcp_main_menus.html"><STRONG>Menus</STRONG></A><P>
+<A HREF="dhcp_main_idx.html"><STRONG>Index</STRONG></A>
+
+
+ </td>
+
+
+
+<!-- End contents block -->
+
+<!-- Start column rule -->
+
+ <td colspan=1 width=5 bgcolor="#CCCCCC">&nbsp;</td>
+
+<!-- End column spacer -->
+
+<!-- Start column spacer -->
+
+ <td colspan=1 width=10 bgcolor="#FFFFFF">&nbsp;</td>
+
+<!-- End column spacer -->
+
+
+
+<!-- Start topic block -->
+
+
+ <td colspan=1 valign="top" bgcolor="#ffffff">
+ <P>&nbsp;</P>
+
+
+<h1>Standard Options</h1>
+
+The standard DHCP options are defined by a short name (or tag), which is a mnemonic. The names are listed below with their code number, name, data type, and description. <P>
+
+<P><HR NOSHADE><P>
+
+ <table border=0 cellspacing=3 cellpadding=3>
+
+ <tr>
+ <td valign="top"><a name="numb"><STRONG>Code</STRONG></a></td>
+<td width=80 valign="top"><a name="tag"><STRONG>Name</STRONG></a></td>
+<td width=50 valign="top"><a name="type"><STRONG>Type</STRONG></a></td>
+ <td valign="top"><a name="descr"><STRONG>Description</STRONG></a></td>
+ </tr>
+
+<tr>
+<td valign="top">1</td>
+<td valign="top">Subnet</td>
+<td valign="top">IP</td>
+<td valign="top">Subnet mask, dotted Internet address</td></TR>
+
+<TR><td valign="top">2</td>
+<td valign="top">UTCoffst</td>
+<td valign="top">Number</td>
+<td valign="top">Coordinated universal Number offset (seconds)</td> </TR>
+
+<TR><td valign="top">3</td>
+<td valign="top">Router</td>
+<td valign="top">IP</td>
+<td valign="top">List of routers</td></TR>
+
+<TR><td valign="top">4 </td>
+<td>Timeserv</td>
+<td valign="top">IP</td>
+<td>List of RFC-868 servers</td></TR>
+
+<TR><td valign="top">5</td>
+<td valign="top">IEN116ns</td>
+<td valign="top">IP</td>
+<td valign="top">List of IEN-116 name servers</td></TR>
+
+<TR><td valign="top">6</td>
+<td valign="top">DNSserv</td><td valign="top">IP</td>
+<td valign="top">List of DNS name servers</td></TR>
+
+<TR><td valign="top">7</td>
+<td valign="top">Logserv </td>
+<td valign="top">IP</td>
+<td valign="top">List of MIT-LCS UDP log servers</td></tr>
+
+<TR><td valign="top">8</td>
+<td valign="top">Cookie</td>
+<td valign="top">IP</td>
+<td valign="top">List of RFC-865 cookie servers</td></tr>
+
+<TR><td valign="top">9</td>
+<td valign="top">Lprserv</td>
+<td valign="top">IP</td>
+<td valign="top">List of RFC-1179 line printer servers</td></tr>
+
+<TR><td valign="top">10</td>
+<td valign="top">Impress</td>
+<td valign="top">IP</td>
+<td valign="top">List of Imagen Impress servers</td></tr>
+
+<TR><td valign="top">11</td>
+<td valign="top">Resource</td>
+<td valign="top">IP</td>
+<td valign="top">List of RFC-887 resource location servers</td></tr>
+
+<TR><td valign="top">12</td>
+<td valign="top">Hostname</td>
+<td valign="top">Boolean</td>
+<td valign="top">Return client's host name, value from hosts database</td></tr>
+
+<TR><td valign="top">13</td>
+<td valign="top">Bootsize</td>
+<td valign="top">Number</td>
+<td valign="top">Number of 512 octet blocks in boot image</td></TR>
+
+<TR><td valign="top">14</td>
+<td valign="top">Dumpfile</td>
+<td valign="top">ASCII</td>
+<td valign="top">Path where core image should be dumped</td></TR>
+
+<TR><td valign="top">15</td>
+<td valign="top">DNSdmain</td>
+<td valign="top">ASCII</td>
+<td valign="top">DNS domain name</td></TR>
+
+<TR><td valign="top">16</td>
+<td valign="top">Swapserv </td>
+<td valign="top">IP</td>
+<td valign="top">Client's swap server</td></TR>
+
+<TR><td valign="top">17</td>
+<td valign="top">Rootpath</td>
+<td valign="top">ASCII</td>
+<td valign="top">Client's root path</td></TR>
+
+<TR><td valign="top">18</td>
+<td valign="top">ExtendP</td>
+<td valign="top">ASCII</td>
+<td valign="top">Extensions path</td></TR>
+
+<TR><td valign="top">19 </td>
+<td valign="top">IpFwdF</TD>
+<TD>Number</td>
+<td valign="top">IP forwarding enable/disable (1/0)</td></TR>
+
+<TR><td valign="top">20</td>
+<td valign="top">NLrouteF</td>
+<td valign="top">Number</td>
+<td valign="top">Non-local source routing</td></TR>
+
+<TR><td valign="top">21</td>
+<td valign="top">PFilter</td>
+<td valign="top">IP</td>
+<td valign="top">Policy filter</td></TR>
+
+<TR><td valign="top">22</td>
+<td valign="top">MaxIpSiz</td>
+<td valign="top">Number</td>
+<td valign="top">Maximum datagram reassembly size</td></TR>
+
+<TR><td width=10 valign="top">23</td>
+<td valign="top">IpTTL</td>
+<td valign="top">Byte</td>
+<td valign="top">Default IP time to live (1=&lt; x &lt;=225)</td></TR>
+
+<TR><td width=10 valign="top">24</td>
+<td valign="top">PathTO</td>
+<td valign="top">Number</td>
+<td valign="top">RFC-1191 Path MTU aging timeout</td></TR>
+
+<TR><td width=10 valign="top">25</td>
+<td valign="top">PathTbl</td>
+<td valign="top">Number</td>
+<td valign="top">RFC-1191 Path MTU plateau table</td></TR>
+
+<TR><td width=10 valign="top">26</td>
+<td valign="top">MTU</td>
+<td valign="top">Number</TD>
+<TD>Interface MTU, (x &lt;=68)</td></TR>
+
+<TR><td valign="top">27</td>
+<td valign="top">SameMtuF</td>
+<td valign="top">Number </td>
+<td valign="top">All subnets are local</td></TR>
+
+<TR><td valign="top">28</td>
+<td valign="top">Broadcst</td>
+<td valign="top">IP</td>
+<td valign="top">Broadcast address</td></TR>
+
+<TR><td valign="top">29</td>
+<td valign="top">MaskDscF</td>
+<td valign="top">Number</td>
+<td valign="top">Perform mask discovery</td></TR>
+
+<TR><td width=10 valign="top">30</td>
+<td valign="top">MaskSupF</td>
+<td valign="top">Number</td>
+<td valign="top">Mask supplier</td></TR>
+
+<TR><td width=10 valign="top">31</td>
+<td valign="top">RDiscvyF</td>
+<td valign="top">Number</td>
+<td valign="top">Perform route discovery</td></TR>
+
+<TR><td valign="top">32</td>
+<td valign="top">RSolictS</td>
+<td valign="top">IP</td>
+<td valign="top">Router solicitation address</td></TR>
+
+<TR><td valign="top">33</td>
+<td valign="top">StaticRt</td>
+<td valign="top">IP</td>
+<td valign="top">Static routes, double IP (network router)</td></TR>
+
+<TR><td valign="top">34</td>
+<td valign="top">TrailerF</td>
+<td valign="top">Number</td>
+<td valign="top">Trailer encapsulation</td></TR>
+
+<TR><td valign="top">35</td>
+<td valign="top">ArpTimeO</td>
+<td valign="top">Number</td>
+<td valign="top">Arp cache timeout</td></TR>
+
+<TR><td valign="top">36</td>
+<td valign="top">EthEncap</td>
+<td valign="top">Number</td>
+<td valign="top">Ethernet encapsulation</td></TR>
+
+<TR><td valign="top">37</td>
+<td valign="top">TcpTTL</td>
+<td valign="top">Number</td>
+<td valign="top">TCP default time to live</td></TR>
+
+<TR><td valign="top">38</td>
+<td valign="top">TcpKaInt</td>
+<td valign="top">Number</td>
+<td valign="top">TCP keep alive interval</td></TR>
+
+<TR><td valign="top">39</td>
+<td valign="top">TcpKaGbF</td>
+<td valign="top">Number</td>
+<td valign="top">Keep alive garbage</td></TR>
+
+<TR><td valign="top">40</td>
+<td valign="top">NISdmain</td>
+<td valign="top">ASCII</td>
+<td valign="top">NIS domain_ name</td></TR>
+
+<TR><td valign="top">41</td>
+<td valign="top">NISservs</td>
+<td valign="top">IP</td>
+<td valign="top">List of NIS servers</td></TR>
+
+<TR><td valign="top">42</td>
+<td valign="top">NTPservs</td>
+<td valign="top">IP</td>
+<td valign="top">List of NTP servers</td></TR>
+
+<TR><td valign="top">44 </td>
+<td valign="top">NetBNms</td>
+<td valign="top">IP</td>
+<td valign="top">List of Netbios name servers</td></TR>
+
+<TR><td valign="top">45</td>
+<td valign="top">NetBDsts </td>
+<td valign="top">IP</td>
+<td valign="top">List of Netbios distribution servers</td></TR>
+
+<TR><td valign="top">46</td>
+<td valign="top"> NetBNdT</td>
+<td valign="top">Number</td>
+<td valign="top">Netbios node type (1=B-node, 2=P, 4=M, 8=H)</td></TR>
+
+<TR><td valign="top">47</td>
+<td valign="top">NetBScop</td>
+<td valign="top">ASCII</td>
+<td valign="top">Netbios scope</td></TR>
+
+<TR><td valign="top">48</td>
+<td valign="top">XFontSrv</td>
+<td valign="top">IP</td>
+<td valign="top">List of X Window font servers</td></TR>
+
+<TR><td valign="top">49</td>
+<td valign="top">XDispMgr</td>
+<td valign="top">IP</td>
+<td valign="top">X Window display managers</td></TR>
+
+
+<TR><td valign="top">51</td>
+<td valign="top">LeaseTim</td>
+<td valign="top">Number</td>
+<td valign="top">Lease time policy</td></TR>
+
+<TR><td valign="top">56</td>
+<td valign="top">Message</td>
+<td valign="top">ASCII</td>
+<td valign="top">Message to be displayed on client</td></TR>
+
+<TR><td valign="top">58</td>
+<td valign="top">T1Time</td>
+<td valign="top">Number</td>
+<td valign="top">Renewal (T1) time</td></TR>
+
+<TR><td valign="top">59</td>
+<td valign="top">T2Time</td>
+<td valign="top">Number</td>
+<td valign="top">Rebinding (T2) time</td></TR>
+
+<TR><td valign="top">62</td>
+<td valign="top">NW_dmain</td>
+<td valign="top">ASCII</td>
+<td valign="top">Netware/IP domain name</td></TR>
+
+<TR><td valign="top">63</td>
+<td valign="top">NWIPOpts</td>
+<td valign="top">Octet</td>
+<td valign="top">Netware/IP options</td></TR>
+
+<TR><td valign="top">64</td>
+<td valign="top">NIS+dom</td>
+<td valign="top">ASCII</td>
+<td valign="top">NIS+ domain name</td></TR>
+
+<TR><td valign="top">65</td>
+<td valign="top">NIS+serv</td>
+<td valign="top">IP</td>
+<td valign="top">List of NIS+ servers</td></TR>
+
+<TR><td valign="top">66</td>
+<td valign="top">TFTPsrvN</td>
+<td valign="top">ASCII</td>
+<td valign="top">Trivial File Transfer Protocol (TFTP) server hostname</td></TR>
+
+<TR><td valign="top">67</td>
+<td valign="top">OptBootF</td>
+<td valign="top">ASCII</td>
+<td valign="top">Optional bootfile name</td></TR>
+
+<TR><td valign="top">68</td>
+<td valign="top">MblIPAgt</td>
+<td valign="top">IP</td>
+<td valign="top">Mobile IP home agents</td></TR>
+
+<TR><td valign="top">69</td>
+<td valign="top">SMTPserv </td>
+<td valign="top">IP</td>
+<td valign="top">Simple Mail Transport Protocol (SMTP) server</td></TR>
+
+<TR><td valign="top">70</td>
+<td valign="top">POP3serv</td>
+<td valign="top">IP</td>
+<td valign="top">Post Office Protocol (POP3) server</td></TR>
+
+<TR><td valign="top">71</td>
+<td valign="top">NNTPserv</td>
+<td valign="top">IP</td>
+<td valign="top">Network News Transport Protocol (NNTP) server</td></TR>
+
+<TR><td valign="top">72</td>
+<td valign="top">WWWservs</td>
+<td valign="top">IP</td>
+<td valign="top">Default World Wide Web server</td></TR>
+
+<TR><td valign="top">73</td>
+<td valign="top">Fingersv</td>
+<td valign="top">IP</td>
+<td valign="top">Default finger server</td></TR>
+
+<TR><td valign="top">74</td>
+<td valign="top">IRCservs</td>
+<td valign="top">IP</td>
+<td valign="top">Internet Relay Chat (IRC) server</td></TR>
+
+<TR><td valign="top">75</td>
+<td valign="top">STservs </td>
+<td valign="top">IP</td>
+<td valign="top">StreetTalk server</td></TR>
+
+<TR><td valign="top">76</td>
+<td valign="top">STDAservs</td>
+<td valign="top">IP</td>
+<td valign="top">StreetTalk directory assistance server</td></TR>
+
+<TR><td valign="top">77</td>
+<td valign="top">UserClas</td>
+<td valign="top">ASCII</td>
+<td valign="top">User class information</td></TR>
+
+<TR><td valign="top">78</td>
+<td valign="top">SLP_DA</td>
+<td valign="top">Octet</td>
+<td valign="top">Directory agent</td></TR>
+
+<TR><td valign="top">79</td>
+<td valign="top">SLP_SS</td>
+<td valign="top">Octet</td>
+<td valign="top">Service scope</td></TR>
+
+<TR><td valign="top">82</td>
+<td valign="top">AgentOpt</td>
+<td valign="top">Octet</td>
+<td valign="top">Agent circuit ID</td></TR>
+
+<TR><td valign="top">89</td>
+<td valign="top">FQDN</td>
+<td valign="top">Octet</td>
+<td valign="top">Fully Qualified Domain Name</td></TR>
+
+<TR><td valign="top">93</td>
+<td valign="top">PXEarch</td>
+<td valign="top">Number</td>
+<td valign="top">Client system architecture</td></TR>
+
+<TR><td valign="top">94</td>
+<td valign="top">PXEnii</td>
+<td valign="top">Octet</td>
+<td valign="top">Client Network Device Interface</td></TR>
+
+<TR><td valign="top">97</td>
+<td valign="top">PXEcid</td>
+<td valign="top">Octet</td>
+<td valign="top">UUID/GUID-based client identifier</td></TR>
+
+<TR><td valign="top">N/A</td>
+<td valign="top">BootFile</td>
+<td valign="top">ASCII</td>
+<td valign="top">File to boot</td></TR>
+
+<TR><td valign="top">N/A</td>
+<td valign="top">BootSrvA</td>
+<td valign="top">IP</td>
+<td valign="top">Boot server</td></TR>
+
+<TR><td valign="top">N/A</td>
+<td valign="top">BootSrvN</td>
+<td valign="top">ASCII</td>
+<td valign="top">Boot server host name</td></TR>
+
+<TR><td valign="top">NA</td>
+<td valign="top">LeaseNeg</td>
+<td valign="top">Boolean</td>
+<td valign="top">Lease is negotiable flag (present=true)</td></TR>
+
+<TR><td valign="top">NA</td>
+<td valign="top">Include</td>
+<td valign="top">ASCII</td>
+<td valign="top">Include listed macro values in this macro</td></TR>
+
+</table>
+
+<p>&nbsp;</p>
+ &nbsp;&nbsp;&nbsp;<A HREF="#top"><small>return to top</small></A>
+
+
+<p>&nbsp;</p>
+
+<!-- Don't go past this line! -->
+
+ </td>
+
+<!-- End topic block -->
+
+</tr>
+
+</table>
+
+<!-- End topic table -->
+
+</body>
+</html>
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_option_view.html b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_option_view.html
new file mode 100644
index 0000000000..3d32c1176e
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_option_view.html
@@ -0,0 +1,293 @@
+<!--
+ -- ident "%W% %E% SMI"
+ --
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ -- Copyright (c) 2000-2001 by Sun Microsystems, Inc.
+ -- All rights reserved.
+ -->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<html>
+
+<head>
+<title>DHCP Manager Help: Option Menus</title>
+<meta NAME="AUTHOR" CONTENT="smorgan">
+<meta NAME="KEYWORDS" CONTENT="DHCP">
+
+</head>
+<body bgcolor="#FFFFFF">
+
+<a name="top"></a>
+
+<table border=0 cellspacing=0 cellpadding=0 width=625>
+
+<!-- Start navigation banner -->
+
+<tr><td colspan=4 WIDTH="615" align="center" valign="top"><IMG SRC="art/bannersmc.gif" WIDTH=615 BORDER=0 ALT="DHCP Manager Help">
+
+ </td>
+ </tr>
+
+<!-- End navigation banner -->
+
+
+<tr>
+
+<!-- Start contents block -->
+
+ <td colspan=1 valign="top" width=105>
+ <P>&nbsp;</P>
+
+ <A HREF="dhcp_main_top.html"><STRONG>Overview</STRONG></A><P>
+ <A HREF="dhcp_relay_ref.html"><STRONG>Servers and Relays</STRONG></A><P>
+ <A HREF="dhcp_addr_ref.html"><STRONG>IP Addresses</STRONG></A><P>
+ <A HREF="dhcp_macro_ref.html"><STRONG>Macros</STRONG></A><P>
+ <A HREF="dhcp_option_ref.html"><STRONG>Options</STRONG></A><P>
+
+<A HREF="dhcp_main_how.html"><STRONG>How To..</STRONG></A><P>
+<A HREF="dhcp_main_menus.html"><STRONG>Menus</STRONG></A><BR>
+&nbsp;&nbsp;<A HREF="dhcp_addr_view.html">Address View</A><BR>
+&nbsp;&nbsp;<A HREF="dhcp_macro_view.html">Macro View</A><BR>
+<IMG SRC="art/tip2.gif" WIDTH=12 HEIGHT=10 BORDER=0 ALT="">Option View<P>
+<A HREF="dhcp_main_idx.html"><STRONG>Index</STRONG></A>
+
+
+ </td>
+
+
+
+<!-- End contents block -->
+
+<!-- Start column rule -->
+
+ <td colspan=1 width=5 bgcolor="#CCCCCC">&nbsp;</td>
+
+<!-- End column spacer -->
+
+<!-- Start column spacer -->
+
+ <td colspan=1 width=10 bgcolor="#FFFFFF">&nbsp;</td>
+
+<!-- End column spacer -->
+
+
+
+<!-- Start topic block -->
+
+
+ <td colspan=1 valign="top" bgcolor="#ffffff">
+
+ <P>&nbsp;</P>
+<H1> Option View Menus</H1>
+
+<table ALIGN="LEFT" BORDER="0" CELLPADDING="3" WIDTH="495">
+<TR>
+<td colspan=1 width=20>&nbsp;</td>
+<th colspan=1 align=left valign="top" width=150>
+ <FONT SIZE="+1">Titles</FONT><P></TH>
+ <TH align=left valign="top"><FONT SIZE="+1">Description</FONT><P></TH>
+
+</TR>
+
+
+<tr>
+<td colspan=1 width=20>&nbsp;</td>
+ <td colspan=1 align=left valign="top" width=150 bgcolor="#FFFFFF"><U>File Menu</U></td>
+ <Td></Td></TR>
+
+ <tr><td colspan=1 width=20>&nbsp;</td>
+ <td colspan=1 align=left valign="top" width=150>&nbsp;&nbsp;<STRONG>Exit</STRONG></TD>
+ <TD ALIGN="LEFT" VALIGN="TOP">Exit from the application.<P>&nbsp;</P></TD>
+
+</TR>
+<tr>
+<td colspan=1 width=20>&nbsp;</td>
+
+
+ <td colspan=1 align=left valign="top" width=150 bgcolor="#FFFFFF">
+<U>Edit Menu</U></td>
+ <Td></Td></TR>
+
+ <tr>
+<td colspan=1 width=20>&nbsp;</td>
+
+<TD ALIGN="left" VALIGN="TOP" width="150">&nbsp;&nbsp;<STRONG>Create</STRONG></TD>
+
+ <TD ALIGN="LEFT" VALIGN="TOP">
+Create new option.</TD></TR>
+
+<tr><td colspan=1 width=20>&nbsp;</td>
+<td colspan=1 align=left valign="top">&nbsp;&nbsp;<STRONG>Delete</STRONG></TD>
+<TD ALIGN="LEFT" VALIGN="TOP">Delete the selected option(s).</TD>
+</TR>
+<tr>
+<td colspan=1 width=20>&nbsp;</td>
+<td colspan=1 align=left valign="top" width=150>&nbsp;&nbsp;<STRONG>Duplicate</STRONG></TD>
+<TD ALIGN="LEFT" VALIGN="TOP">Copy the selected option.</TD>
+</TR>
+
+<tr>
+<td colspan=1 width=20>&nbsp;</td>
+<td colspan=1 align=left valign="top" width=150>&nbsp;&nbsp;<STRONG>Properties</STRONG></TD>
+<TD ALIGN="LEFT" VALIGN="TOP">Modify the properties of the selected option.<P>&nbsp;</P></TD>
+</TR>
+
+
+<tr><td colspan=1 width=20>&nbsp;</td>
+ <td colspan=1 align=left valign="top" width=150 bgcolor="#FFFFFF">
+<U>View Menu</U><P></td>
+<TD></TD></TR>
+
+<tr><td colspan=1 width=20>&nbsp;</td>
+<td colspan=1 align=left valign="top">&nbsp;&nbsp;<STRONG>Refresh</STRONG></TD>
+<TD ALIGN="LEFT" VALIGN="TOP">Refresh the data from the server.</TD>
+</TR>
+
+ <tr><td colspan=1 width=20>&nbsp;</td>
+<td colspan=1 align=left valign="top">&nbsp;&nbsp;<STRONG>Show Grid</STRONG></TD>
+<TD ALIGN="LEFT" VALIGN="TOP">Turn on grid lines in display.<P>&nbsp;</P></TD>
+</TR>
+
+
+<tr><td colspan=1 width=20>&nbsp;</td>
+ <td colspan=1 align=left valign="top" width=150 bgcolor="#FFFFFF">
+<U>Service Menu</U><P></td>
+ <Td></Td></TR>
+
+ <tr><td colspan=1 width=20>&nbsp;</td>
+<td colspan=1 align=left valign="top">&nbsp;&nbsp;<STRONG>Restart</STRONG></TD>
+<TD ALIGN="LEFT" VALIGN="TOP">Stop and restart the DHCP daemon.</TD>
+</TR>
+
+<tr><td colspan=1 width=20>&nbsp;</td>
+<td colspan=1 align=left valign="top">&nbsp;&nbsp;<STRONG>Stop</STRONG></TD>
+<TD ALIGN="LEFT" VALIGN="TOP">Stop the DHCP daemon.</TD>
+</TR>
+
+<tr><td colspan=1 width=20>&nbsp;</td>
+<td colspan=1 align=left valign="top">&nbsp;&nbsp;<STRONG>Start</STRONG></TD>
+<TD ALIGN="LEFT" VALIGN="TOP">Start the DHCP daemon.</TD>
+</TR>
+
+<tr><td colspan=1 width=20>&nbsp;</td>
+<td colspan=1 align=left valign="top">&nbsp;&nbsp;<STRONG>Disable</STRONG></TD>
+<TD ALIGN="LEFT" VALIGN="TOP">Disable the DHCP server or BOOTP relay agent.</TD>
+</TR>
+
+<tr><td colspan=1 width=20>&nbsp;</td>
+<td colspan=1 align=left valign="top">&nbsp;&nbsp;<STRONG>Enable</STRONG></TD>
+<TD ALIGN="LEFT" VALIGN="TOP">Enable the DHCP server or BOOTP relay agent.</TD>
+</TR>
+
+<tr><td colspan=1 width=20>&nbsp;</td>
+<td colspan=1 align=left valign="top">&nbsp;&nbsp;<STRONG>Modify</STRONG></TD>
+<TD ALIGN="LEFT" VALIGN="TOP">Customize services of DHCP server or BOOTP relay agent.</TD>
+</TR>
+
+<tr><td colspan=1 width=20>&nbsp;</td>
+<td colspan=1 align=left valign="top">&nbsp;&nbsp;<STRONG>Export Data</STRONG></TD>
+<TD ALIGN="LEFT" VALIGN="TOP">Copy from the DHCP data store selected IP addresses, macros, and options to a file for the purpose of importing to a DHCP server.</TD>
+</TR>
+
+<tr><td colspan=1 width=20>&nbsp;</td>
+<td colspan=1 align=left valign="top">&nbsp;&nbsp;<STRONG>Import Data</STRONG></TD>
+<TD ALIGN="LEFT" VALIGN="TOP">Bring into the DHCP data store IP addresses, macros, and options that were exported from a DHCP server. </TD>
+</TR>
+
+<tr><td colspan=1 width=20>&nbsp;</td>
+<td colspan=1 align=left valign="top">&nbsp;&nbsp;<STRONG>Convert Data Store</STRONG></TD>
+<TD ALIGN="LEFT" VALIGN="TOP">Configure the DHCP server to use a new data store and convert existing data to new data store format. </TD>
+</TR>
+<tr><td colspan=1 width=20>&nbsp;</td>
+<td colspan=1 align=left valign="top">&nbsp;&nbsp;<STRONG>Unconfigure</STRONG></TD>
+<TD ALIGN="LEFT" VALIGN="TOP">Unconfigure the DHCP server or BOOTP relay agent.<P>&nbsp;</P></TD>
+</TR>
+
+
+
+<tr><td colspan=1 width=20>&nbsp;</td>
+ <td colspan=1 align=left valign="top" width=150 bgcolor="#FFFFFF">
+<U>Help</U></td>
+ <Td>Display the online help.<P>&nbsp;</P></Td></TR>
+
+ <tr><td colspan=1 width=20>&nbsp;</td>
+ <td colspan=1 align=left valign="top" width=150 bgcolor="#FFFFFF">
+<U>Find</U></td>
+ <Td>Search for an Option entry.<P>&nbsp;</P></Td></TR>
+
+
+ <tr><td colspan=1 width=20>&nbsp;</td>
+ <td colspan=1 align=left valign="top" width=150 bgcolor="#FFFFFF">
+<U>Column Entries</U></td>
+ <Td></Td></TR>
+
+ <tr><td colspan=1 width=20>&nbsp;</td>
+ <td colspan=1 align=left valign="top" width=150 bgcolor="#FFFFFF">
+&nbsp;&nbsp;<STRONG>Name</STRONG></td>
+ <Td>Name of the option</Td></TR>
+
+ <tr><td colspan=1 width=20>&nbsp;</td>
+ <td colspan=1 align=left valign="top" width=150 bgcolor="#FFFFFF">
+&nbsp;&nbsp;<STRONG>Category</STRONG></td>
+ <Td>Category of the option</Td></TR>
+
+ <tr><td colspan=1 width=20>&nbsp;</td>
+ <td colspan=1 align=left valign="top" width=150 bgcolor="#FFFFFF">
+&nbsp;&nbsp;<STRONG>Code</STRONG></td>
+ <Td>Numeric code that identifies the option</Td></TR>
+
+ <tr><td colspan=1 width=20>&nbsp;</td>
+
+ <td colspan=1 align=left valign="top" width=150 bgcolor="#FFFFFF">
+&nbsp;&nbsp;<STRONG>Type</STRONG></td>
+ <Td>Data type of the option</Td></TR>
+
+ <tr><td colspan=1 width=20>&nbsp;</td>
+ <td colspan=1 align=left valign="top" width=150 bgcolor="#FFFFFF">
+&nbsp;&nbsp;<STRONG>Granularity</STRONG></td>
+ <Td>Number of values required for the data type</Td></TR>
+
+ <tr><td colspan=1 width=20>&nbsp;</td>
+ <td colspan=1 align=left valign="top" width=150 bgcolor="#FFFFFF">
+&nbsp;&nbsp;<STRONG>Maximum</STRONG></td>
+
+ <Td>Maximum number of values to specify for the option<P></td>
+ </tr>
+ <TR><TD COLSPAN="3">&nbsp;&nbsp;&nbsp;<A HREF="#top"><small>return to top</small></A></TD></TR>
+
+ </TABLE>
+
+
+
+<!-- Don't go past this line! -->
+
+ </td>
+
+<!-- End topic block -->
+
+</tr>
+
+</table>
+
+<!-- End topic table -->
+
+</body>
+</html>
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_relay_choose.html b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_relay_choose.html
new file mode 100644
index 0000000000..677fa30a5c
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_relay_choose.html
@@ -0,0 +1,117 @@
+<!--
+ -- ident "%W% %E% SMI"
+ --
+ Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+ -->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<html>
+
+<head>
+<title>DHCP Manager Help: Choose Configuration</title>
+<meta NAME="AUTHOR" CONTENT="smorgan">
+<meta NAME="KEYWORDS" CONTENT="DHCP">
+
+</head>
+<body bgcolor="#ffffff">
+
+<a name="top"></a>
+
+<table border=0 cellspacing=0 cellpadding=0 width=615>
+
+
+<!-- Start navigation banner -->
+
+<tr><td colspan=4 WIDTH="615" align="left" valign="top"><IMG SRC="art/bannersmc.gif" WIDTH=615 BORDER=0 ALT="DHCP Manager Help">
+
+ </td>
+ </tr>
+
+<!-- End navigation banner -->
+
+
+<tr>
+
+<!-- Start contents block -->
+
+ <td colspan=1 valign="top" WIDTH="105">
+ <P>&nbsp;</P>
+ <STRONG><A HREF="dhcp_main_top.html">Overview</A></STRONG><P>
+<A HREF="dhcp_relay_ref.html"><STRONG>Servers and Relays</STRONG></A><P>
+<A HREF="dhcp_addr_ref.html"><STRONG>Addresses</STRONG></A><P>
+<A HREF="dhcp_macro_ref.html"><STRONG>Macros</STRONG></A><P>
+<A HREF="dhcp_option_ref.html"><STRONG>Options</STRONG></A><P>
+<A HREF="dhcp_main_how.html"><STRONG>How To..</STRONG></A><P>
+<A HREF="dhcp_main_menus.html"><STRONG>Menus</STRONG></A><P>
+<A HREF="dhcp_main_idx.html"><STRONG>Index</STRONG></A>
+
+ </td>
+
+<!-- End contents block -->
+
+<!-- Start column rule -->
+
+ <td colspan=1 width=5 bgcolor="#CCCCCC">&nbsp;</td>
+
+<!-- End column spacer -->
+
+<!-- Start column spacer -->
+
+ <td colspan=1 width=10 bgcolor="#FFFFFF">&nbsp;</td>
+
+<!-- End column spacer -->
+
+
+
+<!-- Start topic block -->
+
+<td colspan=1 valign="top" bgcolor="#ffffff" width="495">
+
+
+
+<P>&nbsp;</P>
+
+
+<H1>Choose Server Configuration</H1>
+
+You can choose to configure your server as either a DHCP server or as a BOOTP relay agent.<P>
+The DHCP server manages the IP address space of networks connected to the server.<P>
+The BOOTP relay agent can receive requests for configuration from a client in a network not directly connected to a DHCP server and forward the request to a DHCP server.
+
+<p>&nbsp;</p>
+
+
+<!-- Don't go past this line! -->
+
+ </td>
+
+<!-- End topic block -->
+
+</tr>
+
+</table>
+
+<!-- End topic table -->
+
+</body>
+</html>
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_relay_config.html b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_relay_config.html
new file mode 100644
index 0000000000..3537433d8f
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_relay_config.html
@@ -0,0 +1,135 @@
+<!--
+ -- ident "%W% %E% SMI"
+ --
+ Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+ -->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<html>
+
+<head>
+<title>DHCP Manager Help: Configuring BOOTP Relay Agent</title>
+<meta NAME="AUTHOR" CONTENT="smorgan">
+<meta NAME="KEYWORDS" CONTENT="DHCP">
+
+</head>
+
+<body bgcolor="#ffffff">
+
+<a name="top"></a>
+
+<table border=0 cellspacing=0 cellpadding=0 width=625>
+
+
+<!-- Start navigation banner -->
+
+<tr><td colspan=4 WIDTH="615" align="left" valign="top"><IMG SRC="art/bannersmc.gif" WIDTH=615 BORDER=0 ALT="DHCP Manager Help">
+
+ </td>
+ </tr>
+
+<!-- End navigation banner -->
+
+
+<tr>
+
+<!-- Start contents block -->
+
+ <td colspan=1 valign="top" WIDTH="105">
+ <P>&nbsp;</P>
+ <STRONG><A HREF="dhcp_main_top.html">Overview</A></STRONG><P>
+ <STRONG><A HREF="dhcp_relay_ref.html">Servers and Relays</A></STRONG><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_config_wiz.html">DHCP Config</A><BR>
+ <IMG SRC="art/tip2.gif" WIDTH=12 HEIGHT=10 BORDER=0 ALT="">Relay Config<BR>
+ &nbsp;&nbsp;<A HREF="dhcp_net_wiz.html">Network Config</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_server_serv.html">DHCP Services</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_relay_serv.html">Relay Services</A><P>
+ <A HREF="dhcp_addr_ref.html"><STRONG>Addresses</STRONG></A><P>
+ <A HREF="dhcp_macro_ref.html"><STRONG>Macros</STRONG></A><P>
+ <A HREF="dhcp_option_ref.html"><STRONG>Options</STRONG></A><P>
+ <A HREF="dhcp_main_how.html"><STRONG>How To..</STRONG></A><P>
+ <A HREF="dhcp_main_menus.html"><STRONG>Menus</STRONG></A><P>
+<A HREF="dhcp_main_idx.html"><STRONG>Index</STRONG></A>
+
+ </td>
+
+<!-- End contents block -->
+
+<!-- Start column rule -->
+
+ <td colspan=1 width=5 bgcolor="#CCCCCC">&nbsp;</td>
+
+<!-- End column spacer -->
+
+<!-- Start column spacer -->
+
+ <td colspan=1 width=10 bgcolor="#FFFFFF">&nbsp;</td>
+
+<!-- End column spacer -->
+
+
+
+<!-- Start topic block -->
+
+<td colspan=1 valign="top" bgcolor="#ffffff" width="495">
+<P>&nbsp;</P>
+
+
+<h1>Configuring a BOOTP Relay Agent</h1>
+
+If you start the DHCP Manager for a system that has not been configured as either a DHCP server or as a BOOTP relay agent, you can choose how to configure the system. <P>
+
+If you choose to configure the system as a BOOTP relay agent, the Configure BOOTP Relay dialog box is displayed.
+<P><HR NOSHADE><P>
+
+
+<TABLE BORDER="0" CELLSPACING="3" CELLPADDING="3" ALIGN="CENTER">
+<TR>
+
+<TD WIDTH="100" VALIGN="TOP"><STRONG>DHCP Servers</STRONG></TD>
+<TD>Enter the IP address(es) or host name(s) of the <A NAME="srvrs">DHCP servers</A> to which this relay agent will forward requests for configuration.
+</TD>
+</TR>
+</TABLE>
+
+&nbsp;<P>
+
+After initial configuration, use the <A HREF="dhcp_relay_serv.html">Modify</A> option in the Service menu to configure additional options, such as the number of relay hops and which network interfaces to monitor.
+
+<p>&nbsp;</p>
+
+
+<!-- Don't go past this line! -->
+
+ </td>
+
+<!-- End topic block -->
+
+</tr>
+
+</table>
+
+<!-- End topic table -->
+
+</body>
+</html>
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_relay_dis.html b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_relay_dis.html
new file mode 100644
index 0000000000..cda8a709f3
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_relay_dis.html
@@ -0,0 +1,114 @@
+<!--
+ -- ident "%W% %E% SMI"
+ --
+ Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+ -->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<html>
+
+<head>
+<title>DHCP Manager Help: Disable Service</title>
+<meta NAME="AUTHOR" CONTENT="smorgan">
+<meta NAME="KEYWORDS" CONTENT="DHCP">
+
+</head>
+
+<body bgcolor="#FFFFFF">
+
+<a name="top"></a>
+
+<table border=0 cellspacing=0 cellpadding=0 width=615>
+
+<!-- Start navigation banner -->
+
+<tr><td colspan=4 WIDTH="615" align="center" valign="top"><IMG SRC="art/bannersmc.gif" WIDTH=615 BORDER=0 ALT="DHCP Manager Help">
+
+ </td>
+ </tr>
+
+<!-- End navigation banner -->
+
+
+<tr>
+
+<!-- Start contents block -->
+
+ <td colspan=1 valign="top" WIDTH="105">
+ <P>&nbsp;</P>
+ <STRONG><A HREF="dhcp_main_top.html">Overview</A></STRONG><P>
+ <A HREF="dhcp_relay_ref.html"><STRONG>Servers and Relays</STRONG></A><P>
+ <A HREF="dhcp_addr_ref.html"><STRONG>Addresses</STRONG></A><P>
+ <A HREF="dhcp_macro_ref.html"><STRONG>Macros</STRONG></A><P>
+ <A HREF="dhcp_option_ref.html"><STRONG>Options</STRONG></A><P>
+ <A HREF="dhcp_main_how.html"><STRONG>How To..</STRONG></A><P>
+ <A HREF="dhcp_main_menus.html"><STRONG>Menus</STRONG></A><P>
+<A HREF="dhcp_main_idx.html"><STRONG>Index</STRONG></A>
+
+ </td>
+
+
+<!-- End contents block -->
+
+<!-- Start column rule -->
+
+ <td colspan=1 width=5 bgcolor="#CCCCCC">&nbsp;</td>
+
+<!-- End column spacer -->
+
+<!-- Start column spacer -->
+
+ <td colspan=1 width=10 bgcolor="#FFFFFF">&nbsp;</td>
+
+<!-- End column spacer -->
+
+
+
+<!-- Start topic block -->
+
+
+ <td colspan=1 valign="top" bgcolor="#ffffff">
+ <P>&nbsp;</P>
+
+
+<h1>Disable Service</h1>
+
+Click OK to remove the DHCP daemon from the boot sequence. When the DHCP service is disabled, it will not start automatically when you reboot the server.<P>
+Choose <A HREF="dhcp_relay_how.html#dis">Enable</A> from the Service menu to restore the DHCP daemon to the boot sequence. <P>
+Choose <A HREF="dhcp_relay_how.html#start">Stop</A> from the Service menu to temporarily halt DHCP service.
+<p>&nbsp;</p>
+
+<!-- Don't go past this line! -->
+
+ </td>
+
+<!-- End topic block -->
+
+</tr>
+
+</table>
+
+<!-- End topic table -->
+
+</body>
+</html>
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_relay_enable.html b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_relay_enable.html
new file mode 100644
index 0000000000..51ae620343
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_relay_enable.html
@@ -0,0 +1,113 @@
+<!--
+ -- ident "%W% %E% SMI"
+ --
+ Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+ -->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<html>
+
+<head>
+<title>DHCP Manager Help: Enable Service</title>
+<meta NAME="AUTHOR" CONTENT="smorgan">
+<meta NAME="KEYWORDS" CONTENT="DHCP">
+
+</head>
+
+<body bgcolor="#FFFFFF">
+
+<a name="top"></a>
+
+<table border=0 cellspacing=0 cellpadding=0 width=615>
+
+<!-- Start navigation banner -->
+
+<tr><td colspan=4 WIDTH="615" align="center" valign="top"><IMG SRC="art/bannersmc.gif" WIDTH=615 BORDER=0 ALT="DHCP Manager Help">
+
+ </td>
+ </tr>
+
+<!-- End navigation banner -->
+
+
+<tr>
+
+<!-- Start contents block -->
+
+ <td colspan=1 valign="top" WIDTH="105">
+ <P>&nbsp;</P>
+ <STRONG><A HREF="dhcp_main_top.html">Overview</A></STRONG><P>
+ <A HREF="dhcp_relay_ref.html"><STRONG>Servers and Relays</STRONG></A><P>
+ <A HREF="dhcp_addr_ref.html"><STRONG>Addresses</STRONG></A><P>
+ <A HREF="dhcp_macro_ref.html"><STRONG>Macros</STRONG></A><P>
+ <A HREF="dhcp_option_ref.html"><STRONG>Options</STRONG></A><P>
+ <A HREF="dhcp_main_how.html"><STRONG>How To..</STRONG></A><P>
+ <A HREF="dhcp_main_menus.html"><STRONG>Menus</STRONG></A><P>
+<A HREF="dhcp_main_idx.html"><STRONG>Index</STRONG></A>
+
+ </td>
+
+
+<!-- End contents block -->
+
+<!-- Start column rule -->
+
+ <td colspan=1 width=5 bgcolor="#CCCCCC">&nbsp;</td>
+
+<!-- End column spacer -->
+
+<!-- Start column spacer -->
+
+ <td colspan=1 width=10 bgcolor="#FFFFFF">&nbsp;</td>
+
+<!-- End column spacer -->
+
+
+
+<!-- Start topic block -->
+
+
+ <td colspan=1 valign="top" bgcolor="#ffffff">
+ <P>&nbsp;</P>
+
+
+<h1>Enable Service</h1>
+
+Choose <A HREF="dhcp_relay_how.html#dis">Enable</A> to restore service that has been disabled. This restores the DHCP daemon to the boot sequence.<P>
+Choose <A HREF="dhcp_relay_how.html#start">Start</A> from the Service menu to start a server that has been stopped or halted temporarily.
+<p>&nbsp;</p>
+
+<!-- Don't go past this line! -->
+
+ </td>
+
+<!-- End topic block -->
+
+</tr>
+
+</table>
+
+<!-- End topic table -->
+
+</body>
+</html>
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_relay_how.html b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_relay_how.html
new file mode 100644
index 0000000000..b5b505f6df
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_relay_how.html
@@ -0,0 +1,326 @@
+<!--
+ -- ident "%W% %E% SMI"
+ --
+ Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+ -- All rights reserved.
+ -->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<html>
+
+<head>
+<title>DHCP Manager Help: How To... Servers and Relays</title>
+<meta NAME="AUTHOR" CONTENT="smorgan">
+<meta NAME="KEYWORDS" CONTENT="DHCP">
+
+</head>
+
+<body bgcolor="#FFFFFF">
+
+<a name="top"></a>
+
+<table border=0 cellspacing=0 cellpadding=0 width=615>
+
+<!-- Start navigation banner -->
+
+<tr><td colspan=4 WIDTH="615" align="center" valign="top"><IMG SRC="art/bannersmc.gif" WIDTH=615 BORDER=0 ALT="DHCP Manager Help">
+
+ </td>
+ </tr>
+
+<!-- End navigation banner -->
+
+
+<tr>
+
+<!-- Start contents block -->
+
+ <td colspan=1 valign="top" width=105>
+ <P>&nbsp;</P>
+ <A HREF="dhcp_main_top.html"><STRONG>Overview</STRONG></A><P>
+ <A HREF="dhcp_relay_ref.html"><STRONG>Servers and Relays</STRONG></A><P>
+ <A HREF="dhcp_addr_ref.html"><STRONG>IP Addresses</STRONG></A><P>
+ <A HREF="dhcp_macro_ref.html"><STRONG>Macros</STRONG></A><P>
+ <A HREF="dhcp_option_ref.html"><STRONG>Options</STRONG></A><P>
+
+ <A HREF="dhcp_main_how.html"><STRONG>How To..</STRONG></A><BR>
+<IMG SRC="art/tip2.gif" WIDTH=12 HEIGHT=10 BORDER=0 ALT="">Servers and Relays<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="#srvr"><EM>Configure DHCP</EM></A><BR>
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#relay"><EM>Configure BOOTP</EM></a><BR>
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#ntwrk"><EM>Configure Network</EM></a><BR>
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#delnet"><EM>Delete Network</EM></a><BR>
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#adv"><EM>Customize Service</EM></a><BR>
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#start"><EM>Start/Stop/Restart</EM></a><BR>
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#dis"><EM>Enable/Disable</EM></a><BR>
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#exp"><EM>Export/Import</EM></a><BR>
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#conv"><EM>Convert Data Store</EM></a><BR>
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#uncon"><EM>Unconfigure DHCP</EM></a><BR>
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#uncon2"><EM>Unconfigure BOOTP</EM></a><BR>
+&nbsp;&nbsp;<A HREF="dhcp_addr_how.html">Addresses</A><BR>
+&nbsp;&nbsp;<A HREF="dhcp_macro_how.html">Macros</A><BR>
+&nbsp;&nbsp;<A HREF="dhcp_option_how.html">Options</A><P>
+
+<A HREF="dhcp_main_idx.html"><STRONG>Index</STRONG></A>
+
+ </td>
+
+<!-- End contents block -->
+
+<!-- Start column rule -->
+
+ <td colspan=1 width=5 bgcolor="#CCCCCC">&nbsp;</td>
+
+<!-- End column spacer -->
+
+<!-- Start column spacer -->
+
+ <td colspan=1 width=10 bgcolor="#FFFFFF">&nbsp;</td>
+
+<!-- End column spacer -->
+
+
+
+<!-- Start topic block -->
+
+<td colspan=1 valign="top" bgcolor="#ffffff">
+
+
+
+<P>&nbsp;</P>
+<H1>How To: Servers and Relays</H1>
+
+<strong><big><a name="srvr">Configure a DHCP Server</A></big></strong><P>
+When you start DHCP Manager on a Solaris system that has not been configured as either a DHCP server or as a BOOTP relay agent, the Choose Server Configuration dialog box is displayed. You can choose to configure a server as either a DHCP server or as a BOOTP relay agent, but not both.<p>
+<OL>
+<LI><STRONG>Select Configure as DHCP Server.</STRONG><BR>
+The DHCP Server Configuration Wizard starts.<P></LI>
+<LI><STRONG>Step through the configuration, providing the <A HREF="dhcp_config_wiz.html">information</A> requested.</STRONG><BR>
+When the configuration is completed, the DHCP server starts.
+</LI>
+</OL>
+<P>
+To change any of the configuration parameters, you can later <a href="dhcp_macro_how.html#modmacro">modify</a> the macros containing the configuration parameters. Alternatively, you can <A href="#uncon">unconfigure</A> the server and then use the Configuration Wizard to reconfigure the server.<P>
+To customize <A HREF="#adv">DHCP services</A>, select Modify from the Services menu.<P>
+
+<HR NOSHADE><P>
+<strong><big><A NAME="relay">Configure a BOOTP Relay Agent</A></big></strong><P>
+When you start DHCP Manager on a Solaris system that has not been configured as either a DHCP server or as a BOOTP relay agent, the Choose Server Configuration dialog box is displayed. You can choose to configure a server as either a DHCP server or as a BOOTP relay agent, but not both.<p>
+<OL>
+<LI><STRONG>Select Configure as BOOTP Relay.</STRONG><BR>
+The Configure BOOTP Relay dialog box is displayed.<P></LI>
+
+<LI><STRONG>Enter DHCP servers.</STRONG><BR>
+Type the names or IP addresses of one or more DHCP servers to which to relay configuration requests, and click OK.
+<p>
+</LI>
+</OL>
+When the configuration is completed, the BOOTP relay agent starts.<P>
+To customize <A HREF="#adv">BOOTP relay services</A>, select Modify from the Services menu.<P>
+<HR NOSHADE>
+<P>
+<big><STRONG><A NAME="ntwrk">Configure a Network</A></STRONG></big><p>
+When you first configure the DHCP server, the Configuration Wizard configures the first network. To add additional networks, follow these steps.<P>
+<OL>
+<LI><STRONG>Choose Network Wizard from the Edit menu of the View Addresses window.</STRONG><P></LI>
+<LI><STRONG>Enter configuration <A HREF="dhcp_net_wiz.html">information</A> for the new network.</STRONG><P></LI>
+<LI><STRONG>Click OK to confirm your action.</STRONG><P></LI>
+</OL>
+<HR NOSHADE>
+<P>
+<big><STRONG><A NAME="delnet">Delete a Network</A></STRONG></big><p>
+To delete a network is to remove it from the list of networks monitored by the DHCP server.
+<P>
+<OL>
+<LI><STRONG>Click the Addresses tab and choose Delete Network from the Edit menu.</STRONG><BR>
+The Delete Network dialog box is displayed. Configured networks are listed in the Keep Networks list. <P></LI>
+<LI><STRONG>Select the network to delete and use the arrow keys to move it to the Delete Networks list.</STRONG><P></LI>
+<LI><STRONG>Click OK to confirm your actions.</STRONG><P></LI>
+</OL>
+You cannot add a network using this dialog box. To add or restore a network to the DHCP server's list of monitored networks, you must <a href="#ntwrk">configure the network</a>. <P>
+<HR NOSHADE>
+<P>
+<big><STRONG><A NAME="adv">Customize Services</A></STRONG></big><p>
+You can configure DHCP or BOOTP services for your server or BOOTP relay agent.
+<P><OL>
+<LI><STRONG>Choose Modify from the Services menu.</STRONG><br></LI>
+The Modify Service Options dialog is displayed.
+<LI><P><STRONG>Enter configuration information</strong><BR>
+Use the Modify Service Options dialog to customize the <A HREF="dhcp_server_serv.html">DHCP</A> or <A HREF="dhcp_relay_serv.html">Relay</A> services.<P>
+You can configure options such as BOOTP compatibility mode (for the DHCP server) or modify the list of DHCP servers to which to forward requests (relay agent).<P></LI>
+<LI><STRONG>Click OK.</STRONG></LI>
+</OL><P>
+
+<HR NOSHADE><P>
+<strong><big><A NAME="start">Start, Stop, or Restart a Server or Relay Agent</A></big></strong><p>
+The DHCP server or BOOTP relay agent starts
+automatically when you have completed the
+initial configuration. You can stop or start the service by
+choosing Stop or Start from the Services menu. This is analogous
+to running the <tt>/etc/init.d/dhcp</tt>
+script with the <tt>stop</tt> or <tt>start</tt> command.<P>
+
+ <ol type=1>
+
+ <li><STRONG>Choose the Services menu in the main window.</STRONG><p></LI>
+ <li><STRONG>Choose Start, Stop, or Restart from the Service
+menu.</STRONG>
+ <TABLE BORDER="0" CELLSPACING="3" CELLPADDING="3" COLS="2">
+
+<TR ALIGN="LEFT" VALIGN="TOP">
+ <TD WIDTH="60" ALIGN="LEFT" VALIGN="TOP"><STRONG>Start</STRONG></TD>
+ <TD ALIGN="LEFT" VALIGN="TOP">Click Start to start DHCP or BOOTP relay
+service that has been stopped.</TD>
+ </TR>
+
+ <TR ALIGN="LEFT" VALIGN="TOP">
+ <TD WIDTH="60" ALIGN="LEFT" VALIGN="TOP"><STRONG>Stop</STRONG></TD>
+ <TD ALIGN="LEFT" VALIGN="TOP">Click Stop to stop the DHCP or
+BOOTP daemon.</TD>
+ </TR>
+
+<TR ALIGN="LEFT" VALIGN="TOP">
+ <TD WIDTH="60" ALIGN="LEFT" VALIGN="TOP"><STRONG>Restart</STRONG></TD>
+ <TD ALIGN="LEFT" VALIGN="TOP">Click Restart to force the DHCP server to reread the <tt>dhcptab</tt>.</TD>
+ </TR>
+
+ </TABLE></LI>
+ <LI><STRONG>Click OK to confirm your actions.</STRONG></LI>
+
+ </ol><P>
+
+ <HR NOSHADE><P>
+<strong><big><A NAME="dis">Enable or Disable a Server or Relay Agent</A></big></strong><p>
+Disabling DHCP stops the DHCP daemon and removes it from the boot sequence. When the DHCP service is disabled, it will not start automatically when you reboot the server.<P>
+Enabling DHCP starts the DHCP daemon and restores it to the boot sequence.<P>
+
+ <ol type=1>
+
+ <li><STRONG>Choose the Services menu in the main window.</STRONG><p></LI>
+ <li><STRONG>Choose Enable or Disable from the Services menu.</STRONG>
+ <TABLE BORDER="0" CELLSPACING="3" CELLPADDING="3" COLS="2">
+
+<TR ALIGN="LEFT" VALIGN="TOP">
+ <TD WIDTH="60" ALIGN="LEFT" VALIGN="TOP"><STRONG>Enable</STRONG></TD>
+ <TD ALIGN="LEFT" VALIGN="TOP">Click Enable to restore a DHCP service that has been disabled.</TD>
+ </TR>
+
+ <TR ALIGN="LEFT" VALIGN="TOP">
+ <TD WIDTH="60" ALIGN="LEFT" VALIGN="TOP"><STRONG>Disable</STRONG></TD>
+ <TD ALIGN="LEFT" VALIGN="TOP">Click Disable to remove DHCP service from the boot sequence.</TD>
+ </TR>
+
+ </TABLE></LI>
+ <LI><STRONG>Click OK to confirm your actions.</STRONG></LI>
+ </OL>
+
+ <HR NOSHADE><P>
+<STRONG><big><a name="exp">Export Data From a DHCP Server</a></BIG></STRONG><p>
+If you want to move IP addresses, macros, or options to another Solaris DHCP server, you must first export the data from the owning DHCP server. <p>
+<ol>
+<li><Strong>From the Services menu, choose Export Data.</strong><BR>
+The Export Data wizard is displayed.<p></li>
+<li><strong>Answer the <a href="dhcp_export_wiz.html">prompts</a> to indicate what you want to export.</strong><p></li>
+<li><strong>Click OK to confirm your actions.</strong></li></ol>
+
+<HR NOSHADE><P>
+<STRONG><big><a name="imp">Import Data to a DHCP Server</a></BIG></STRONG><p>
+After exporting IP addresses, macros, or options from a DHCP server, you can import it to another Solaris DHCP server. <p>
+<ol>
+<li><Strong>From the Services menu, choose Import Data.</strong><BR>
+The Import Data wizard is displayed.<p></li>
+<li><strong>Type the full path to the file containing the data you want to import to
+ this DHCP server. </strong><P>This file must have been created by exporting data from a Solaris DHCP server.<p></li>
+<li><strong>Choose Overwrite Existing Data if you want conflicting information on the new server to be replaced with data being imported.</strong><p>
+If the DHCP server on which
+ you are importing data has configuration data
+ for any of the same networks, IP addresses,
+ macros, or options being imported, that data will be
+ replaced with the imported data when
+ you select this option. By default,
+ conflicting data will not be overwritten.<p>
+<li><strong>Click OK to confirm your actions.</strong></li></ol>
+
+<HR NOSHADE> <P>
+<STRONG><BIG><a name="conv">Convert to a New Data Store</a></BIG></STRONG><p>
+The Solaris DHCP service supports several data stores, which are designed for use by
+sites with varying requirements. <p>
+You may need to convert to a new data store if, for
+example, your number of DHCP clients increases to the point that you need higher
+performance or higher capacity from the DHCP service, or if you want to share the
+DHCP server duties among multiple servers.<p>
+<ol>
+<li><Strong>From the Services menu, choose Convert Data Store.</strong><BR>
+The Data Store Conversion wizard is displayed.<p></li>
+<li><strong>Answer the <a href="dhcp_convert_wiz.html">prompts</a> to indicate what you want to export.</strong><p></li>
+<li><strong>Click OK to confirm your actions.</strong></li></ol>
+
+
+<HR NOSHADE><P>
+<STRONG><BIG><a name="#uncon">Unconfigure a DHCP Server</a></BIG></STRONG><p>
+If you no longer want the DHCP server to run on this machine, you can unconfigure the DHCP server. Be sure that responsibility for the addresses owned by the server has been transferred to another DHCP server before you unconfigure the server. See <a href="#exp">Export Data From a DHCP Server</a><P>
+<OL>
+<LI><STRONG>From the Services menu, choose Unconfigure.</STRONG><BR>
+The Unconfigure Service dialog box is displayed.<p></li>
+
+<LI><STRONG>Choose one or both options.</STRONG>
+
+<TABLE BORDER="0" CELLSPACING="3" CELLPADDING="3" COLS="2">
+
+<TR ALIGN="LEFT" VALIGN="TOP">
+ <TD WIDTH="160" ALIGN="LEFT" VALIGN="TOP"><STRONG>Remove <tt>dhcptab</tt> and all DHCP network tables</STRONG></TD>
+ <TD ALIGN="LEFT" VALIGN="TOP">Use this option with caution. If you use NIS+, removing these tables may cause a problem for other DHCP servers that share these databases.</TD>
+ </TR>
+ <TR ALIGN="LEFT" VALIGN="TOP">
+ <TD WIDTH="160" ALIGN="LEFT" VALIGN="TOP"><STRONG>Remove all hosts table entries for DHCP addresses</STRONG>
+ </TD>
+
+<TD>Check this box to also remove entries from the server's hosts name service.</TD></TR>
+</table>
+</LI>
+<LI><STRONG>Click OK to confirm your actions.</STRONG></LI>
+</OL>
+ <P><HR NOSHADE><P>
+ <STRONG><BIG><a name="uncon2">Unconfigure a BOOTP Relay Agent</a></BIG></STRONG><p>
+<OL>
+<LI><STRONG>From the Services menu, choose Unconfigure.</STRONG><BR>
+The Unconfigure Service dialog box is displayed.<p></li>
+<LI><STRONG>Click OK to confirm your actions.</STRONG></LI>
+</OL>
+ <P>
+&nbsp;&nbsp;&nbsp;<A HREF="#top"><small>return to top</small></A>
+<p>&nbsp;</p>
+
+<!-- Don't go past this line! -->
+
+ </td>
+
+<!-- End topic block -->
+
+</tr>
+
+</table>
+
+<!-- End topic table -->
+
+</body>
+</html>
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_relay_ref.html b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_relay_ref.html
new file mode 100644
index 0000000000..7815d8caae
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_relay_ref.html
@@ -0,0 +1,138 @@
+<!--
+ -- ident "%W% %E% SMI"
+ --
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ -- Copyright (c) 2000-2001 by Sun Microsystems, Inc.
+ -- All rights reserved.
+ -->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<html>
+
+<head>
+<title>DHCP Manager Help: Servers and Relays</title>
+<meta NAME="AUTHOR" CONTENT="smorgan">
+<meta NAME="KEYWORDS" CONTENT="DHCP">
+
+</head>
+<body bgcolor="#ffffff">
+
+<a name="top"></a>
+
+<table border=0 cellspacing=0 cellpadding=0 width=615>
+
+
+<!-- Start navigation banner -->
+
+<tr><td colspan=4 WIDTH="615" align="left" valign="top"><IMG SRC="art/bannersmc.gif" WIDTH=615 BORDER=0 ALT="DHCP Manager Help">
+
+ </td>
+ </tr>
+
+<!-- End navigation banner -->
+
+
+<tr>
+
+<!-- Start contents block -->
+
+ <td colspan=1 valign="top" WIDTH="105">
+ <P>&nbsp;</P>
+ <STRONG><A HREF="dhcp_main_top.html">Overview</A></STRONG><P>
+ <IMG SRC="art/tip2.gif" WIDTH=12 HEIGHT=10 BORDER=0 ALT=""><STRONG>Servers and Relays</STRONG><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_config_wiz.html">DHCP Config</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_relay_config.html">Relay Config</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_net_wiz.html">Network Config</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_server_serv.html">DHCP Services</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_relay_serv.html">Relay Services</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_relay_how.html#start">Start/Stop</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_relay_how.html#dis">Enable/Disable</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_relay_how.html#exp">Export/Import Data</A><BR>
+&nbsp;&nbsp;<A HREF="dhcp_relay_how.html#conv">Convert Data Store</A><BR>
+&nbsp;&nbsp;<A HREF="dhcp_relay_how.html#uncon">Unconfigure</A><P>
+ <A HREF="dhcp_addr_ref.html"><STRONG>Addresses</STRONG></A><P>
+ <A HREF="dhcp_macro_ref.html"><STRONG>Macros</STRONG></A><P>
+ <A HREF="dhcp_option_ref.html"><STRONG>Options</STRONG></A><P>
+
+ <A HREF="dhcp_main_how.html"><STRONG>How To..</STRONG></A><P>
+ <A HREF="dhcp_main_menus.html"><STRONG>Menus</STRONG></A><P>
+<A HREF="dhcp_main_idx.html"><STRONG>Index</STRONG></A>
+
+ </td>
+
+<!-- End contents block -->
+
+<!-- Start column rule -->
+
+ <td colspan=1 width=5 bgcolor="#CCCCCC">&nbsp;</td>
+
+<!-- End column spacer -->
+
+<!-- Start column spacer -->
+
+ <td colspan=1 width=10 bgcolor="#FFFFFF">&nbsp;</td>
+
+<!-- End column spacer -->
+
+
+
+<!-- Start topic block -->
+
+<td colspan=1 valign="top" bgcolor="#ffffff" width="495">
+
+
+
+<P>&nbsp;</P>
+
+
+<H1>DHCP Servers and BOOTP Relay Agents</H1>
+
+You can use DHCP Manager to configure and manage your DHCP server or BOOTP relay agent, and their associated networks.<P>
+<H2>DHCP Server</H2>
+The DHCP server manages the IP address space of networks connected to the server. Use the <A HREF="dhcp_config_wiz.html">DHCP Configuration Wizard</A> to configure your DHCP server.
+The DHCP Configuration Wizard steps through the procedure to configure the system as a DHCP server and configures the first network. The wizard performs the same steps as the Solaris <tt>dhcpconfig</tt> command.<p>
+After initial configuration, you can configure additional options to customize the behavior of the DHCP server using the Modify option from the Service menu to open the <A HREF="dhcp_server_serv.html">Modify Service Options</A> dialog box. Use the Service menu to <A HREF="dhcp_relay_how.html#start"> stop, start,</A> <A HREF="dhcp_relay_how.html#dis">disable, enable, </A>and <A HREF="dhcp_relay_how.html#uncon">unconfigure</A> the DHCP server. The Service menu also provides selections for <A HREF="dhcp_relay_how.html#exp">exporting and importing data</A>, and <A HREF="dhcp_relay_how.html#conv">converting the data store</A>.
+
+<H2>BOOTP Relay Agent</H2>
+The BOOTP relay agent can receive requests for configuration from a client in a network not directly connected to a DHCP server and forward the request to a DHCP server. Use the <A HREF="dhcp_relay_config.html">BOOTP Configuration</A> dialog box for initial configuration. You can configure additional options using the Modify option from the Service menu to open the <A HREF="dhcp_relay_serv.html">Modify Service Options</A> dialog box. Use the Service menu to <A HREF="dhcp_relay_how.html#start"> stop, start,</A> <A HREF="dhcp_relay_how.html#dis">disable, enable, </A>and <A HREF="dhcp_relay_how.html#uncon2">unconfigure</A> the BOOTP relay agent. <P>
+
+<H2>Networks</H2>
+The DHCP server can manage clients and addresses on configured networks. Use the <A HREF="dhcp_config_wiz.html">DHCP Configuration Wizard</A> to configure the first network. After initial configuration, you can configure additional networks with the <A HREF="dhcp_net_wiz.html">Network Wizard</A>, available from the Edit menu.<P>
+Select <A HREF="dhcp_net_del.html">Delete Network</A> from the Edit menu to remove a network from the list of monitored networks.
+
+
+<p>&nbsp;</p>
+
+
+<!-- Don't go past this line! -->
+
+ </td>
+
+<!-- End topic block -->
+
+</tr>
+
+</table>
+
+<!-- End topic table -->
+
+</body>
+</html>
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_relay_serv.html b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_relay_serv.html
new file mode 100644
index 0000000000..0c2bfeed94
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_relay_serv.html
@@ -0,0 +1,161 @@
+<!--
+ -- ident "%W% %E% SMI"
+ --
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ -- Copyright (c) 2000-2001 by Sun Microsystems, Inc.
+ -- All rights reserved.
+ -->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<html>
+
+<head>
+<title>DHCP Manager Help: Relay Agent Services</title>
+<meta NAME="AUTHOR" CONTENT="smorgan">
+<meta NAME="KEYWORDS" CONTENT="DHCP">
+
+</head>
+
+<body bgcolor="#FFFFFF">
+
+<a name="top"></a>
+
+<table border=0 cellspacing=0 cellpadding=0 width=625>
+
+
+<!-- Start navigation banner -->
+
+<tr><td colspan=4 WIDTH="615" align="left" valign="top"><IMG SRC="art/bannersmc.gif" WIDTH=615 BORDER=0 ALT="DHCP Manager Help">
+
+ </td>
+ </tr>
+
+<!-- End navigation banner -->
+
+
+<tr>
+
+<!-- Start contents block -->
+
+ <td colspan=1 valign="top" WIDTH="105">
+ <P>&nbsp;</P>
+ <STRONG><A HREF="dhcp_main_top.html">Overview</A></STRONG><P>
+
+ <A HREF="dhcp_relay_ref.html"><STRONG>Servers and Relays</STRONG></A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_config_wiz.html">DHCP Config</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_relay_config.html">BOOTP Config</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_net_wiz.html">Net Config</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_server_serv.html">DHCP Services</A><BR>
+
+ <IMG SRC="art/tip2.gif" WIDTH=12 HEIGHT=10 BORDER=0 ALT="">BOOTP Services<BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#log"><EM>Verbose</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#hops"><EM>Hops</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#srvrs"><EM>Servers</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#inter"><EM>Interfaces</EM></a><P>
+
+ <A HREF="dhcp_addr_ref.html"><STRONG>Addresses</STRONG></A><P>
+ <A HREF="dhcp_macro_ref.html"><STRONG>Macros</STRONG></A><P>
+ <A HREF="dhcp_option_ref.html"><STRONG>Options</STRONG></A><P>
+ <A HREF="dhcp_main_how.html"><STRONG>How To..</STRONG></A><P>
+ <A HREF="dhcp_main_menus.html"><STRONG>Menus</STRONG></A><P>
+<A HREF="dhcp_main_idx.html"><STRONG>Index</STRONG></A>
+
+ </td>
+
+<!-- End contents block -->
+
+<!-- Start column rule -->
+
+ <td colspan=1 width=5 bgcolor="#CCCCCC">&nbsp;</td>
+
+<!-- End column spacer -->
+
+<!-- Start column spacer -->
+
+ <td colspan=1 width=10 bgcolor="#FFFFFF">&nbsp;</td>
+
+<!-- End column spacer -->
+
+
+
+<!-- Start topic block -->
+
+<td colspan=1 valign="top" bgcolor="#ffffff" WIDTH="495">
+
+<P>&nbsp;</P>
+
+<h1>Modifying Service Options <BR>on a BOOTP Relay Agent</h1>
+
+After the initial configuration, choose Modify from the Service menu to customize additional settings for the BOOTP Relay Agent. <P>
+The Modify Service Options dialog lets you configure the following options.
+<P>
+
+<table border=0 cellspacing=4 cellpadding=3 width=440>
+ <tr>
+ <td width=125 valign="top"><A NAME="log"><STRONG>Verbose Log Messages </STRONG></A></td>
+ <td width=315 valign="top">
+The DHCP daemon writes messages to the <TT>syslogd</TT> facility. <P>In verbose mode, the DHCP daemon displays more messages than in non-verbose mode. This can reduce efficiency due to the time taken to write and display the messages, so it should be reserved for troubleshooting. The default is for non-verbose mode.
+ </TD>
+ </tr>
+
+ <tr>
+ <td width=125 valign="top"><A NAME="hops"><STRONG>Maximum Relay Agent Hops</STRONG></A>
+ </td>
+ <td width=315 valign="top">Specify the maximum number of times the request can be relayed to another server before the receiving BOOTP relay agent drops the request. The default is 4.
+ </td>
+ </tr>
+
+ <tr>
+ <td width=125 valign="top"><A NAME="srvrs"><STRONG>DHCP Servers</STRONG></A>
+ </td>
+ <td width=315 valign="top">Enter the host names or IP addresses of the DHCP servers to which this BOOTP relay agent will forward requests. Use this option to add to the list of servers that were configured during initial configuration. You can also use this option to delete configured servers.<P>
+ </td>
+ </tr>
+
+
+ <tr>
+ <td width=125 valign="top"><A NAME="inter"><STRONG>Interfaces</STRONG></A></td>
+ <td valign="top">Click the Interfaces tab to choose the network interfaces the DHCP server will monitor.<P> The interfaces are listed by device name and IP network address. Use the arrow keys to remove or restore a network interface to the list of monitored interfaces.<P> The default is to allow the server to monitor all network interfaces.
+
+ </td>
+ </TR>
+
+</table>
+
+
+
+<p>&nbsp;</p>
+
+
+<!-- Don't go past this line! -->
+
+ </td>
+
+<!-- End topic block -->
+
+</tr>
+
+</table>
+
+<!-- End topic table -->
+
+</body>
+</html>
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_relay_unconfig.html b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_relay_unconfig.html
new file mode 100644
index 0000000000..2eeb79bcff
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_relay_unconfig.html
@@ -0,0 +1,113 @@
+<!--
+ -- ident "%W% %E% SMI"
+ --
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ -- Copyright (c) 2000-2001 by Sun Microsystems, Inc.
+ -- All rights reserved.
+ -->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<html>
+
+<head>
+<title>DHCP Manager Help: Unconfigure BOOTP Relay Agent</title>
+<meta NAME="AUTHOR" CONTENT="smorgan">
+<meta NAME="KEYWORDS" CONTENT="DHCP">
+
+</head>
+
+<body bgcolor="#FFFFFF">
+
+<a name="top"></a>
+
+<table border=0 cellspacing=0 cellpadding=0 width=615>
+
+<!-- Start navigation banner -->
+
+<tr><td colspan=4 WIDTH="615" align="center" valign="top"><IMG SRC="art/bannersmc.gif" WIDTH=615 BORDER=0 ALT="DHCP Manager Help">
+
+ </td>
+ </tr>
+
+<!-- End navigation banner -->
+
+
+<tr>
+
+<!-- Start contents block -->
+
+ <td colspan=1 valign="top" WIDTH="105">
+ <P>&nbsp;</P>
+ <STRONG><A HREF="dhcp_main_top.html">Overview</A></STRONG><P>
+ <A HREF="dhcp_relay_ref.html"><STRONG>Servers and Relays</STRONG></A><P>
+ <A HREF="dhcp_addr_ref.html"><STRONG>Addresses</STRONG></A><P>
+ <A HREF="dhcp_macro_ref.html"><STRONG>Macros</STRONG></A><P>
+ <A HREF="dhcp_option_ref.html"><STRONG>Options</STRONG></A><P>
+ <A HREF="dhcp_main_how.html"><STRONG>How To..</STRONG></A><P>
+ <A HREF="dhcp_main_menus.html"><STRONG>Menus</STRONG></A><P>
+<A HREF="dhcp_main_idx.html"><STRONG>Index</STRONG></A>
+
+ </td>
+
+
+<!-- End contents block -->
+
+<!-- Start column rule -->
+
+ <td colspan=1 width=5 bgcolor="#CCCCCC">&nbsp;</td>
+
+<!-- End column spacer -->
+
+<!-- Start column spacer -->
+
+ <td colspan=1 width=10 bgcolor="#FFFFFF">&nbsp;</td>
+
+<!-- End column spacer -->
+
+
+
+<!-- Start topic block -->
+
+
+ <td colspan=1 valign="top" bgcolor="#ffffff">
+ <P>&nbsp;</P>
+
+
+<h1>Unconfigure BOOTP Service</h1>
+
+Click OK to unconfigure the BOOTP relay service for this server. The server will cease to relay requests for DHCP configuration. To restore BOOTP service, you must reconfigure the server using the BOOTP configuration dialog box.<P>
+Use <A HREF="dhcp_relay_how.html#start">Stop</A> or <A HREF="dhcp_relay_how.html#dis">Disable</A> from the Service menu to halt or disable DHCP service.
+<p>&nbsp;</p>
+
+<!-- Don't go past this line! -->
+
+ </td>
+
+<!-- End topic block -->
+
+</tr>
+
+</table>
+
+<!-- End topic table -->
+
+</body>
+</html>
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_server_serv.html b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_server_serv.html
new file mode 100644
index 0000000000..505680d924
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_server_serv.html
@@ -0,0 +1,247 @@
+<!--
+ -- #ident "%Z%%M% %I% %E% SMI"
+ --
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ -- Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ -- Use is subject to license terms.^M
+ -->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<html>
+
+<head>
+<title>DHCP Manager Help: DHCP Services</title>
+<meta NAME="AUTHOR" CONTENT="smorgan">
+<meta NAME="KEYWORDS" CONTENT="DHCP">
+
+</head>
+
+<body bgcolor="#FFFFFF">
+
+<a name="top"></a>
+
+<table border=0 cellspacing=0 cellpadding=0 width=625>
+
+
+<!-- Start navigation banner -->
+
+<tr><td colspan=4 WIDTH="615" align="left" valign="top"><IMG SRC="art/bannersmc.gif" WIDTH=615 BORDER=0 ALT="DHCP Manager Help">
+
+ </td>
+ </tr>
+
+<!-- End navigation banner -->
+
+
+<tr>
+
+<!-- Start contents block -->
+
+ <td colspan=1 valign="top" WIDTH="105">
+ <P>&nbsp;</P>
+ <STRONG><A HREF="dhcp_main_top.html">Overview</A></STRONG><P>
+
+ <A HREF="dhcp_relay_ref.html"><STRONG>Servers and Relays</STRONG></A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_config_wiz.html">DHCP Config</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_relay_config.html">BOOTP Config</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_net_wiz.html">Net Config</A><BR>
+ <IMG SRC="art/tip2.gif" WIDTH=12 HEIGHT=10 BORDER=0 ALT="">DHCP Services<BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#hops"><EM>Hops</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#log"><EM>Verbose</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#trans"><EM>Log Trans</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#dupaddr"><EM>Detect Addr</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#rescan"><EM>Reload</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#dnsup"><EM>Update DNS</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#dnstime"><EM>Timeout DNS</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#cache"><EM>Cache</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#compat"><EM>BOOTP</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#inter"><EM>Interfaces</EM></a><BR>
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#addrs"><EM>Addresses</EM></a><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_relay_serv.html">BOOTP Services</A><P>
+ <A HREF="dhcp_addr_ref.html"><STRONG>Addresses</STRONG></A><P>
+ <A HREF="dhcp_macro_ref.html"><STRONG>Macros</STRONG></A><P>
+ <A HREF="dhcp_option_ref.html"><STRONG>Options</STRONG></A><P>
+<A HREF="dhcp_main_how.html"><STRONG>How To..</STRONG></A><P>
+ <A HREF="dhcp_main_menus.html"><STRONG>Menus</STRONG></A><P>
+<A HREF="dhcp_main_idx.html"><STRONG>Index</STRONG></A>
+
+ </td>
+
+<!-- End contents block -->
+
+<!-- Start column rule -->
+
+ <td colspan=1 width=5 bgcolor="#CCCCCC">&nbsp;</td>
+
+<!-- End column spacer -->
+
+<!-- Start column spacer -->
+
+ <td colspan=1 width=10 bgcolor="#FFFFFF">&nbsp;</td>
+
+<!-- End column spacer -->
+
+
+
+<!-- Start topic block -->
+
+<td colspan=1 valign="top" bgcolor="#ffffff" WIDTH="495">
+
+<P>&nbsp;</P>
+
+<h1>Modifying Service Options <BR>on a DHCP Server</h1>
+
+After the initial configuration, choose Modify in the Service menu to customize additional settings for the DHCP server. Changes you make here become the default settings used by the server in this session and each subsequent session.<P>
+Use the Modify Service Options dialog box to configure the following options.
+<P>
+
+<table border=0 cellspacing=4 cellpadding=3 width=430>
+
+ <tr>
+ <td width=125 valign="top"><A NAME="hops"><STRONG>Maximum Relay Agent Hops</STRONG></A><br>
+ </td>
+ <td valign="top">Specify the maximum number of relay hops that can occur before the DHCP server drops the request. The default is 4.
+ </td>
+ </tr>
+
+ <TR>
+ <td width=145 valign="top"><A NAME="log"><STRONG>Verbose Log Messages</STRONG></A><br></td>
+ <td valign="top">
+The DHCP daemon writes messages to the <TT>syslogd</TT> facility.
+In verbose mode, the DHCP daemon displays more messages than in non-verbose mode. This can reduce efficiency due to the time taken to write and display the messages. The default is for non-verbose mode.
+ </TD>
+ </tr>
+
+ <tr>
+ <td VALIGN=TOP WIDTH="145"><a name="trans"><STRONG>Log Transactions to syslog Facility</STRONG></A><br></td>
+
+<td valign="top">
+Select whether to have the DHCP&nbsp;daemon log transactions using
+ <tt>syslogd</tt>, and select an integer from 0 through 7 to
+specify the local syslog facility. The local syslog facility can be used to selectively
+view transaction messages generated by the DHCP daemon. Transactions include
+all DHCP messages between the daemon and clients. This feature may be useful
+in troubleshooting problems with DHCP.</td>
+</tr>
+
+
+
+ <TR><TD VALIGN="top"><A NAME="dupaddr"><STRONG>Detect Duplicate IP Addresses</STRONG></A></TD>
+
+<TD>By default, the DHCP server pings an IP address before offering it to
+a DHCP or BOOTP client to verify that the address is not in use by
+another client. Uncheck the box to disable this action. (Disabling this feature is not recommended.)<BR></TD></TR>
+
+
+ <tr>
+ <td width=125 valign="top"><A NAME="rescan"><STRONG>Reload <tt>dhcptab</tt></STRONG></A></td>
+ <td valign="top">
+Specify the interval, in minutes, that the DHCP server should use to schedule the rereading of the <tt>dhcptab</tt> information.<P>
+The DHCP server reads the <tt>dhcptab</tt> at startup and not again until told to do so.<p>
+Typically, the DHCP Manager causes the server to reread the <tt>dhcptab</tt> whenever you make a change to the data. The default is to turn off this option to avoid needless re-initialization of the <tt>dhcptab</tt>.
+ </td>
+ </tr>
+
+<tr>
+ <td width=125 valign="top"><A NAME="dnsup"><STRONG>Update DNS host information</STRONG></A></td>
+ <td valign="top">
+Select this option if this DHCP server supports clients that can specify their host names, and you want to have those host names added to the DNS tables. The DNS server must be set up to accept update requests from the DHCP server. See the Solaris <em>DHCP Administration Guide</em> for more information.</td>
+ </tr>
+
+<tr> <td width=125 valign="top"><A NAME="dnstime"><STRONG>Timeout DNS update</STRONG></A></td>
+ <td valign="top">
+Specify the number of seconds to wait for a response from the DNS server
+before timing out. The default is 15 seconds. This option is available only if you selected Update DNS host information.</tr>
+ <tr>
+ <td width=125 valign="top"><A NAME="cache"><STRONG>Cache Offers</STRONG></A> <br>
+ </td>
+ <td valign="top">Specify the number of seconds the server will hold open the offer to a client request for configuration. The default is 10 seconds.<P>
+You can increase this time to compensate for slow performance on a slow network.
+ </td>
+ </tr>
+ <tr>
+ <td width=145 valign="top"><A NAME="compat"><STRONG>BOOTP Compatibility</STRONG></A><br>
+ </td>
+ <td valign="top">Select whether to allow the DHCP server to operate in BOOTP compatibility mode. In this mode, the DHCP server will respond to requests for configuration from BOOTP clients.
+
+<table border=0 cellspacing=3 cellpadding=3 width=300>
+<TR>
+<TD width=80 valign="top"><B>None</b></TD>
+<td valign="top">Do not support BOOTP clients.<P></TD>
+</TR>
+
+<TR>
+<TD WIDTH="80" VALIGN="TOP"><strong>Automatic</strong></TD>
+<TD>Automatically allocate permanent IP addresses to any requesting BOOTP clients.</TD>
+</TR>
+
+<TR>
+<TD WIDTH="80" VALIGN="TOP"><b>Manual</b></TD>
+<TD>Respond only to clients that have been assigned a reserved address in the server's network tables.</TD>
+</TR>
+</table>
+
+ </td>
+ </tr>
+
+ <tr>
+ <td width=125 valign="top"><A NAME="inter"><STRONG>Interfaces</STRONG></A></td>
+ <td valign="top">Click the Interfaces tab to choose which network interfaces the DHCP server will monitor.<P> The interfaces are listed by device name and network IP address. Use the arrow keys to remove or restore a network interface to the list of monitored interfaces.<P> The default is to allow the server to monitor all network interfaces.
+
+ </td>
+
+ </tr>
+
+ <tr>
+ <td width=125 valign="top"><A NAME="addrs"><STRONG>Addresses</STRONG></A></td>
+ <td valign="top">Click the Addresses tab to choose which server addresses the DHCP server will own.<P> The DHCP server will manage all client records that are owned by the server addresses listed.<P> The default is to manage records owned by the server's primary IP address.
+
+ </td>
+
+ </tr>
+
+
+
+
+</table>
+&nbsp;&nbsp;&nbsp;<A HREF="#top"><small>return to top</small></A>
+
+
+
+
+<p>&nbsp;</p>
+
+
+
+<!-- Don't go past this line! -->
+
+ </td>
+
+<!-- End topic block -->
+
+</tr>
+
+</table>
+
+<!-- End topic table -->
+
+</body>
+</html>
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_server_unconfig.html b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_server_unconfig.html
new file mode 100644
index 0000000000..df2396c8bf
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_server_unconfig.html
@@ -0,0 +1,122 @@
+<!--
+ -- ident "%W% %E% SMI"
+ --
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ -- Copyright (c) 2000-2001 by Sun Microsystems, Inc.
+ -- All rights reserved.
+ -->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<html>
+
+<head>
+<title>DHCP Manager Help: Unconfigure DHCP Server</title>
+<meta NAME="AUTHOR" CONTENT="smorgan">
+<meta NAME="KEYWORDS" CONTENT="DHCP">
+
+</head>
+
+<body bgcolor="#FFFFFF">
+
+<a name="top"></a>
+
+<table border=0 cellspacing=0 cellpadding=0 width=615>
+
+<!-- Start navigation banner -->
+
+<tr><td colspan=4 WIDTH="615" align="center" valign="top"><IMG SRC="art/bannersmc.gif" WIDTH=615 BORDER=0 ALT="DHCP Manager Help">
+
+ </td>
+ </tr>
+
+<!-- End navigation banner -->
+
+
+<tr>
+
+<!-- Start contents block -->
+
+ <td colspan=1 valign="top" WIDTH="105">
+ <P>&nbsp;</P>
+ <STRONG><A HREF="dhcp_main_top.html">Overview</A></STRONG><P>
+ <A HREF="dhcp_relay_ref.html"><STRONG>Servers and Relays</STRONG></A><P>
+ <A HREF="dhcp_addr_ref.html"><STRONG>Addresses</STRONG></A><P>
+ <A HREF="dhcp_macro_ref.html"><STRONG>Macros</STRONG></A><P>
+ <A HREF="dhcp_option_ref.html"><STRONG>Options</STRONG></A><P>
+ <A HREF="dhcp_main_how.html"><STRONG>How To..</STRONG></A><P>
+ <A HREF="dhcp_main_menus.html"><STRONG>Menus</STRONG></A><P>
+<A HREF="dhcp_main_idx.html"><STRONG>Index</STRONG></A>
+
+ </td>
+
+
+<!-- End contents block -->
+
+<!-- Start column rule -->
+
+ <td colspan=1 width=5 bgcolor="#CCCCCC">&nbsp;</td>
+
+<!-- End column spacer -->
+
+<!-- Start column spacer -->
+
+ <td colspan=1 width=10 bgcolor="#FFFFFF">&nbsp;</td>
+
+<!-- End column spacer -->
+
+
+
+<!-- Start topic block -->
+
+
+ <td colspan=1 valign="top" bgcolor="#ffffff">
+ <P>&nbsp;</P>
+
+
+<h1>Unconfigure DHCP Service</h1>
+
+If you no longer want a system to run the DHCP service, you can unconfigure the DHCP service. If you want the DHCP service to be halted temporarily, use <A HREF="dhcp_relay_how.html#start">Stop</A> or <A HREF="dhcp_relay_how.html#dis">Disable</A> from the Service menu to halt or disable DHCP service.
+<p>
+If you want to unconfigure the DHCP server, but the networks and addresses served by this server are still to be used as DHCP addresses, be sure that you have transferred those addresses to another working DHCP server before unconfiguring the original server. See <a href="dhcp_relay_how.html#exp">instructions for exporting and importing data</a> .<p>
+When you unconfigure the server, you must decide what to do with the server's data: <P>
+<STRONG>Remove all hosts table entries for DHCP addresses:</STRONG>
+Unconfiguring the DHCP server will remove all DHCP tables. Be aware that if you are sharing the DHCP data using NIS+ or through files shared in NFS, you should not remove the DHCP data. <P>
+
+<STRONG>Remove the dhcptab and all DHCP network tables:</STRONG>
+Check this box to also remove entries from the server's hosts resource.<P>
+
+
+<p>&nbsp;</p>
+
+<!-- Don't go past this line! -->
+
+ </td>
+
+<!-- End topic block -->
+
+</tr>
+
+</table>
+
+<!-- End topic table -->
+
+</body>
+</html>
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_solaris_about.html b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_solaris_about.html
new file mode 100644
index 0000000000..a066785c37
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/help/dhcp_solaris_about.html
@@ -0,0 +1,169 @@
+<!--
+ -- ident "%W% %E% SMI"
+ --
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ -- Copyright (c) 2000-2001 by Sun Microsystems, Inc.
+ -- All rights reserved.
+ -->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<html>
+
+<head>
+<title>DHCP Manager Help: About Solaris DHCP</title>
+<meta NAME="AUTHOR" CONTENT="smorgan">
+<meta NAME="KEYWORDS" CONTENT="DHCP">
+
+</head>
+
+<body bgcolor="#FFFFFF">
+
+<a name="top"></a>
+
+<table border=0 cellspacing=0 cellpadding=0 width=625>
+
+<!-- Start navigation banner -->
+
+<tr><td colspan=4 WIDTH="615" align="center" valign="top"><IMG SRC="art/bannersmc.gif" WIDTH=615 BORDER=0 ALT="DHCP Manager Help">
+
+ </td>
+ </tr>
+
+<!-- End navigation banner -->
+
+
+<tr>
+
+<!-- Start contents block -->
+
+ <td colspan=1 valign="top" width=105>
+ <P>&nbsp;</P>
+ <A HREF="dhcp_main_top.html"><STRONG>Overview</STRONG></A><BR>
+ <IMG SRC="art/tip2.gif" WIDTH=12 HEIGHT=10 BORDER=0 ALT="">About Solaris DHCP<BR>
+ &nbsp;&nbsp;<A HREF="dhcp_macros_about.html">About Macros</A><BR>
+ &nbsp;&nbsp;<A HREF="dhcp_main_hlp.html">Getting Help</A><P>
+ <A HREF="dhcp_relay_ref.html"><STRONG>Servers and Relays</STRONG></A><P>
+ <A HREF="dhcp_addr_ref.html"><STRONG>Addresses</STRONG></A><P>
+ <A HREF="dhcp_macro_ref.html"><STRONG>Macros</STRONG></A><P>
+ <A HREF="dhcp_option_ref.html"><STRONG>Options</STRONG></A><P>
+ <A HREF="dhcp_main_how.html"><STRONG>How To..</STRONG></A><P>
+ <A HREF="dhcp_main_menus.html"><STRONG>Menus</STRONG></A><P>
+ <A HREF="dhcp_main_idx.html"><STRONG>Index</STRONG></A>
+
+ </td>
+
+<!-- End contents block -->
+
+<!-- Start column rule -->
+
+ <td colspan=1 width=5 bgcolor="#CCCCCC">&nbsp;</td>
+
+<!-- End column spacer -->
+
+<!-- Start column spacer -->
+
+ <td colspan=1 width=10 bgcolor="#FFFFFF">&nbsp;</td>
+
+<!-- End column spacer -->
+
+
+
+<!-- Start topic block -->
+
+<td colspan=1 valign="top" bgcolor="#ffffff" width=495>
+
+
+
+<P>&nbsp;</P>
+
+
+<H1>About Solaris DHCP</H1>
+
+The Solaris<FONT SIZE="-1"><sup>TM</sup></FONT> DHCP server (<tt>in.dhcpd</tt>) provides DHCP services for DHCP and BOOTP clients. The Solaris DHCP implementation allows flexibility in the assignment of network configuration through the use of <a href="dhcp_macros_about.html">macros </a> containing network parameter values. Macros are processed selectively to assign appropriate parameters to clients.<p>
+
+The following illustration provides a high-level description of the DHCP/BOOTP client boot process, including macro processing, under Solaris DHCP.<p>
+
+<center>
+<a href="dhcp_macro_ref.html"><img src="art/macroflow.gif" border=0></a>
+</center>
+
+<p>In the illustration:<p>
+
+ <ol type=1>
+
+ <li>A DHCP/BOOTP client initiates a DHCP request.<p></li>
+
+ <li>An available DHCP server accepts the request and initiates the DHCP
+ allocation process:<p>
+
+ <ol type=a>
+
+ <li>An available <A NAME="addr"><strong>IP address</strong></A> is found in the
+ DHCP network tables and assigned to the
+ client.<p></li>
+
+ <li> Option values in the <A NAME="class"><strong>Client Class</strong></A> macro
+ (platform/operating system), located in the server's <tt>dhcptab</tt>
+ table, are bundled and passed to the next stage.<p></li>
+
+ <li>Option values in the <A NAME="ntwrk"><strong>Network</strong></A> macro (client
+ network), located in the server's <A NAME="dhcptab"><tt>dhcptab</tt></A>, are bundled and passed to
+ the next stage.<p></li>
+
+ <li>Option values in the <A NAME="ip"><strong>IP Address</strong></A> macro
+ (server-specific), located in the server's <tt>dhcptab</tt>, are bundled and
+ passed to the next stage.<p></li>
+
+ <li> Option values in the <A NAME="cid"><strong>Client ID</strong></A> macro
+ (unique to client, for example, Ethernet/MAC ID), located in the
+ server's <tt>dhcptab</tt>, are bundled and passed to the next stage.<p></li>
+ </ol>
+
+ <p></li>
+
+ <li>All option values are bundled together, along with the IP address
+ assigned by the DHCP server, and passed back to the DHCP client.<p></li>
+
+ <li>The client completes the boot process, using the IP address and
+ values passed from the DHCP server.<p></li>
+ </ol>
+
+Refer to the help pages for more information about <A HREF="dhcp_macro_ref.html">
+macros</A> and <A HREF="dhcp_option_ref.html">options</A>.
+
+<p>&nbsp;</p>
+&nbsp;&nbsp;&nbsp;<A HREF="#top"><FONT SIZE="2">return to top</FONT></A>
+
+<!-- Don't go past this line! -->
+
+
+ </td>
+
+<!-- End topic block -->
+
+</tr>
+
+</table>
+
+<!-- End topic table -->
+
+</body>
+</html>
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/inc.flg b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/inc.flg
new file mode 100644
index 0000000000..9e8d46aefc
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/inc.flg
@@ -0,0 +1,32 @@
+#! /bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 1999-2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+
+find_files "s.*" usr/src/lib/libdhcpsvc
+find_files "s.*" usr/src/cmd/cmd-inet/usr.lib/dhcp
+exec_file usr/src/lib/req.flg
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/Makefile b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/Makefile
new file mode 100644
index 0000000000..ed76f57b7a
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/Makefile
@@ -0,0 +1,65 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+LIBRARY= dhcpmgr.a
+VERS= .1
+OBJECTS= dhcptab.o exception.o network.o optiondefs.o service.o \
+ inittab.o dd_misc.o dd_opt.o class_cache.o
+
+include $(SRC)/lib/Makefile.lib
+
+# Override install location
+ROOTLIBDIR= $(ROOT)/usr/sadm/admin/dhcpmgr
+
+LIBS= $(DYNLIB)
+
+CPPFLAGS += -I../com/sun/dhcpmgr/bridge \
+ -I$(JAVA_ROOT)/include \
+ -I$(JAVA_ROOT)/include/solaris \
+ -I$(SRC)/common/net/dhcp \
+ -I.
+
+LDLIBS += -ldhcpsvc -ldhcputil -linetutil -lscf -lsocket -lresolv -lnsl -lc
+
+# definitions for i18n
+POFILE= $(LIBRARY:%.a=%.po)
+MSGFILES = `$(GREP) -l gettext *.[ch]`
+
+.KEEP_STATE:
+.PARALLEL: $(OBJECTS)
+.WAIT: $(DYNLIB)
+
+all: $(LIBS)
+
+install: all $(ROOTLIBS)
+
+lint: lintcheck
+
+_msg: pofile_MSGFILES
+
+include $(SRC)/lib/Makefile.targ
+include $(SRC)/Makefile.msg.targ
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/class_cache.c b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/class_cache.c
new file mode 100644
index 0000000000..6f83980d08
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/class_cache.c
@@ -0,0 +1,265 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <assert.h>
+#include <synch.h>
+#include <jni.h>
+
+#include "class_cache.h"
+
+/*
+ * Only certain classes are deemed worthy of caching. These be them.
+ */
+#define DCR_NAME "com/sun/dhcpmgr/data/DhcpClientRecord"
+#define DTR_NAME "com/sun/dhcpmgr/data/DhcptabRecord"
+#define NET_NAME "com/sun/dhcpmgr/data/Network"
+#define MAC_NAME "com/sun/dhcpmgr/data/Macro"
+#define OPT_NAME "com/sun/dhcpmgr/data/Option"
+#define DS_NAME "com/sun/dhcpmgr/data/DhcpDatastore"
+#define CFG_NAME "com/sun/dhcpmgr/data/DhcpdOptions"
+#define RES_NAME "com/sun/dhcpmgr/data/DhcpResource"
+#define IP_NAME "com/sun/dhcpmgr/data/IPAddress"
+#define IPIF_NAME "com/sun/dhcpmgr/data/IPInterface"
+
+/*
+ * As with classes, only certain methods are cached.
+ */
+#define DCR_CONS_NAME "<init>"
+#define DCR_GETCID_NAME "getClientId"
+#define DCR_GETFLAG_NAME "getFlagString"
+#define DCR_GETCIP_NAME "getClientIPAddress"
+#define DCR_GETSIP_NAME "getServerIPAddress"
+#define DCR_GETEXP_NAME "getExpirationTime"
+#define DCR_GETSIG_NAME "getSignature"
+#define DCR_GETMAC_NAME "getMacro"
+#define DCR_GETCMT_NAME "getComment"
+#define DTR_GETKEY_NAME "getKey"
+#define DTR_GETFLAG_NAME "getFlag"
+#define DTR_GETSIG_NAME "getSignature"
+#define DTR_GETVAL_NAME "getValue"
+#define NET_CONS_NAME "<init>"
+#define MAC_CONS_NAME "<init>"
+#define OPT_CONS_NAME "<init>"
+#define DS_CONS_NAME "<init>"
+#define DS_GETRSRC_NAME "getResource"
+#define DS_GETLOC_NAME "getLocation"
+#define DS_GETRSRCCFG_NAME "getConfig"
+#define DS_GETVER_NAME "getVersion"
+#define CFG_CONS_NAME "<init>"
+#define CFG_SET_NAME "set"
+#define CFG_GETALL_NAME "getAll"
+#define RES_GETKEY_NAME "getKey"
+#define RES_GETVAL_NAME "getValue"
+#define RES_ISCOM_NAME "isComment"
+#define IP_CONS_NAME "<init>"
+#define IPIF_CONS_NAME "<init>"
+
+/*
+ * Signatures for the methods can be found below.
+ */
+#define DCR_CONS_SIG "(Ljava/lang/String;Ljava/lang/String;"\
+ "Ljava/lang/String;Ljava/lang/String;"\
+ "Ljava/lang/String;Ljava/lang/String;"\
+ "Ljava/lang/String;Ljava/lang/String;)V"
+
+#define DCR_GETCID_SIG "()Ljava/lang/String;"
+#define DCR_GETFLAG_SIG "()Ljava/lang/String;"
+#define DCR_GETCIP_SIG "()Ljava/lang/String;"
+#define DCR_GETSIP_SIG "()Ljava/lang/String;"
+#define DCR_GETEXP_SIG "()Ljava/lang/String;"
+#define DCR_GETSIG_SIG "()Ljava/lang/String;"
+#define DCR_GETMAC_SIG "()Ljava/lang/String;"
+#define DCR_GETCMT_SIG "()Ljava/lang/String;"
+#define DTR_GETKEY_SIG "()Ljava/lang/String;"
+#define DTR_GETFLAG_SIG "()Ljava/lang/String;"
+#define DTR_GETSIG_SIG "()Ljava/lang/String;"
+#define DTR_GETVAL_SIG "()Ljava/lang/String;"
+#define NET_CONS_SIG "(Ljava/lang/String;I)V"
+#define MAC_CONS_SIG "(Ljava/lang/String;Ljava/lang/String;"\
+ "Ljava/lang/String;)V"
+#define OPT_CONS_SIG "(Ljava/lang/String;B[Ljava/lang/String;"\
+ "SBIILjava/lang/String;Z)V"
+#define DS_CONS_SIG "(Ljava/lang/String;IZ)V"
+#define DS_GETRSRC_SIG "()Ljava/lang/String;"
+#define DS_GETLOC_SIG "()Ljava/lang/String;"
+#define DS_GETRSRCCFG_SIG "()Ljava/lang/String;"
+#define DS_GETVER_SIG "()I"
+#define CFG_CONS_SIG "()V"
+#define CFG_SET_SIG "(Ljava/lang/String;Ljava/lang/String;Z)V"
+#define CFG_GETALL_SIG "()[Ljava/lang/Object;"
+#define RES_GETKEY_SIG "()Ljava/lang/String;"
+#define RES_GETVAL_SIG "()Ljava/lang/String;"
+#define RES_ISCOM_SIG "()Z"
+#define IP_CONS_SIG "(Ljava/lang/String;)V"
+#define IPIF_CONS_SIG "(Ljava/lang/String;Ljava/lang/String;"\
+ "Ljava/lang/String;)V"
+/*
+ * Class map.
+ */
+typedef struct {
+ jclass cl_class;
+ char *cl_name;
+} cl_map_t;
+
+/*
+ * Note that the order of the entries in this table must match
+ * exactly with the CC_CLASSMAP_ID enumeration in class_cache.h.
+ */
+static cl_map_t classMap[] = {
+ { NULL, DCR_NAME }, /* DCR_CLASS */
+ { NULL, DTR_NAME }, /* DTR_CLASS */
+ { NULL, NET_NAME }, /* NET_CLASS */
+ { NULL, MAC_NAME }, /* MAC_CLASS */
+ { NULL, OPT_NAME }, /* OPT_CLASS */
+ { NULL, DS_NAME }, /* DS_CLASS */
+ { NULL, CFG_NAME }, /* CFG_CLASS */
+ { NULL, RES_NAME }, /* RES_CLASS */
+ { NULL, IP_NAME }, /* IP_CLASS */
+ { NULL, IPIF_NAME } /* IPIF_CLASS */
+};
+
+/*
+ * Method ID map.
+ */
+typedef struct {
+ jmethodID mi_methodID;
+ char *mi_name;
+ char *mi_signature;
+} mi_map_t;
+
+/*
+ * Note that the order of the entries in this table must match
+ * exactly with the CC_METHODMAP_ID enumeration in class_cache.h.
+ */
+static mi_map_t methodIDMap[] = {
+ { NULL, DCR_CONS_NAME, DCR_CONS_SIG }, /* DCR_CONS */
+ { NULL, DCR_GETCID_NAME, DCR_GETCID_SIG }, /* DCR_GETCID */
+ { NULL, DCR_GETFLAG_NAME, DCR_GETFLAG_SIG }, /* DCR_GETFLAG */
+ { NULL, DCR_GETCIP_NAME, DCR_GETCIP_SIG }, /* DCR_GETCIP */
+ { NULL, DCR_GETSIP_NAME, DCR_GETSIP_SIG }, /* DCR_GETSIP */
+ { NULL, DCR_GETEXP_NAME, DCR_GETEXP_SIG }, /* DCR_GETEXP */
+ { NULL, DCR_GETSIG_NAME, DCR_GETSIG_SIG }, /* DCR_GETSIG */
+ { NULL, DCR_GETMAC_NAME, DCR_GETMAC_SIG }, /* DCR_GETMAC */
+ { NULL, DCR_GETCMT_NAME, DCR_GETCMT_SIG }, /* DCR_GETCMT */
+ { NULL, DTR_GETKEY_NAME, DTR_GETKEY_SIG }, /* DTR_GETKEY */
+ { NULL, DTR_GETFLAG_NAME, DTR_GETFLAG_SIG }, /* DTR_GETFLAG */
+ { NULL, DTR_GETSIG_NAME, DTR_GETSIG_SIG }, /* DTR_GETSIG */
+ { NULL, DTR_GETVAL_NAME, DTR_GETVAL_SIG }, /* DTR_GETVAL */
+ { NULL, NET_CONS_NAME, NET_CONS_SIG }, /* NET_CONS */
+ { NULL, MAC_CONS_NAME, MAC_CONS_SIG }, /* MAC_CONS */
+ { NULL, OPT_CONS_NAME, OPT_CONS_SIG }, /* OPT_CONS */
+ { NULL, DS_CONS_NAME, DS_CONS_SIG }, /* DS_CONS */
+ { NULL, DS_GETRSRC_NAME, DS_GETRSRC_SIG }, /* DS_GETRSRC */
+ { NULL, DS_GETLOC_NAME, DS_GETLOC_SIG }, /* DS_GETLOC */
+ { NULL, DS_GETRSRCCFG_NAME, DS_GETRSRCCFG_SIG }, /* DS_GETRSRCCFG */
+ { NULL, DS_GETVER_NAME, DS_GETVER_SIG }, /* DS_GETVER */
+ { NULL, CFG_CONS_NAME, CFG_CONS_SIG }, /* CFG_CONS */
+ { NULL, CFG_SET_NAME, CFG_SET_SIG }, /* CFG_SET */
+ { NULL, CFG_GETALL_NAME, CFG_GETALL_SIG }, /* CFG_GETALL */
+ { NULL, RES_GETKEY_NAME, RES_GETKEY_SIG }, /* RES_GETKEY */
+ { NULL, RES_GETVAL_NAME, RES_GETVAL_SIG }, /* RES_GETVAL */
+ { NULL, RES_ISCOM_NAME, RES_ISCOM_SIG }, /* RES_ISCOM */
+ { NULL, IP_CONS_NAME, IP_CONS_SIG }, /* IP_CONS */
+ { NULL, IPIF_CONS_NAME, IPIF_CONS_SIG } /* IPIF_CONS */
+};
+
+/*
+ * The locks to protect the class and method maps.
+ */
+static mutex_t cmap_lock;
+static mutex_t mmap_lock;
+
+void
+init_class_cache(void) {
+ (void) mutex_init(&cmap_lock, USYNC_THREAD, NULL);
+ (void) mutex_init(&mmap_lock, USYNC_THREAD, NULL);
+}
+
+/*
+ * Get a dhcpmgr class from the cache.
+ */
+jclass
+find_class(JNIEnv *env, CC_CLASSMAP_ID id) {
+
+ jclass *class;
+
+ assert(id >= 0 && id <= CC_CLASSMAP_NUM);
+
+ /*
+ * If the class has not been cached yet, go find it and cache it.
+ */
+ class = &classMap[id].cl_class;
+ if (*class == NULL) {
+ /*
+ * Check again with the lock held this time.
+ */
+ (void) mutex_lock(&cmap_lock);
+ if (*class == NULL) {
+ char *name = classMap[id].cl_name;
+ jclass local = (*env)->FindClass(env, name);
+ if (local != NULL) {
+ *class = (*env)->NewGlobalRef(env, local);
+ (*env)->DeleteLocalRef(env, local);
+ }
+ }
+ (void) mutex_unlock(&cmap_lock);
+ }
+
+ return (*class);
+}
+
+/*
+ * Get a dhcpmgr class methodid from the cache.
+ */
+jmethodID
+get_methodID(JNIEnv *env, jclass class, CC_METHODMAP_ID id) {
+
+ jmethodID *methodID;
+
+ assert(id >= 0 && id <= CC_METHODMAP_NUM);
+
+ /*
+ * If the methodID has not been cached, go find it and cache it.
+ */
+ methodID = &methodIDMap[id].mi_methodID;
+ if (*methodID == NULL) {
+ /*
+ * Check again with the lock held this time.
+ */
+ (void) mutex_lock(&mmap_lock);
+ if (*methodID == NULL) {
+ char *name = methodIDMap[id].mi_name;
+ char *signature = methodIDMap[id].mi_signature;
+ *methodID = (*env)->GetMethodID(env, class, name,
+ signature);
+ }
+ (void) mutex_unlock(&mmap_lock);
+ }
+
+ return (*methodID);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/class_cache.h b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/class_cache.h
new file mode 100644
index 0000000000..3d2f10ac79
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/class_cache.h
@@ -0,0 +1,61 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _CLASS_CACHE_H
+#define _CLASS_CACHE_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <jni.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+ DCR_CLASS, DTR_CLASS, NET_CLASS, MAC_CLASS, OPT_CLASS, DS_CLASS,
+ CFG_CLASS, RES_CLASS, IP_CLASS, IPIF_CLASS
+} CC_CLASSMAP_ID;
+#define CC_CLASSMAP_NUM IPIF_CLASS + 1
+
+typedef enum {
+ DCR_CONS, DCR_GETCID, DCR_GETFLAG, DCR_GETCIP, DCR_GETSIP, DCR_GETEXP,
+ DCR_GETSIG, DCR_GETMAC, DCR_GETCMT, DTR_GETKEY, DTR_GETFLAG,
+ DTR_GETSIG, DTR_GETVAL, NET_CONS, MAC_CONS, OPT_CONS, DS_CONS,
+ DS_GETRSRC, DS_GETLOC, DS_GETRSRCCFG, DS_GETVER, CFG_CONS, CFG_SET,
+ CFG_GETALL, RES_GETKEY, RES_GETVAL, RES_ISCOM, IP_CONS, IPIF_CONS
+} CC_METHODMAP_ID;
+#define CC_METHODMAP_NUM IPIF_CONS + 1
+
+extern void init_class_cache(void);
+extern jclass find_class(JNIEnv *, CC_CLASSMAP_ID);
+extern jmethodID get_methodID(JNIEnv *, jclass, CC_METHODMAP_ID);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !_CLASS_CACHE_H */
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/dd_misc.c b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/dd_misc.c
new file mode 100644
index 0000000000..137a1ce7ab
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/dd_misc.c
@@ -0,0 +1,680 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <dhcp_svc_private.h>
+#include <dirent.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <libintl.h>
+#include <procfs.h>
+#include <rpcsvc/nis.h>
+#include <malloc.h>
+#include <ctype.h>
+#include <jni.h>
+
+#include "exception.h"
+#include "class_cache.h"
+#include "dd_misc.h"
+
+#define PROCFS_DIR "/proc"
+
+/*
+ * Frees the list of data store strings.
+ */
+static void
+free_enumerated_dd(char **module, int count)
+{
+ while (count-- > 0) {
+ free(module[count]);
+ }
+ free(module);
+}
+
+/*
+ * Determines if a given data store name is valid.
+ */
+static boolean_t
+dd_is_valid_data_store(JNIEnv *env, const char *name)
+{
+ char **module;
+ int count;
+ int ndx;
+ int rcode;
+ boolean_t isValid = B_FALSE;
+
+ if (name != NULL) {
+ rcode = enumerate_dd(&module, &count);
+ if (rcode != DSVC_SUCCESS) {
+ throw_libdhcpsvc_exception(env, rcode);
+ } else {
+ for (ndx = 0; !isValid && ndx < count; ndx++) {
+ if (strcmp(module[ndx], name) == 0) {
+ isValid = B_TRUE;
+ }
+ }
+ free_enumerated_dd(module, count);
+ }
+ }
+ return (isValid);
+}
+
+/*
+ * Call a java object's int getter method.
+ */
+static boolean_t
+dd_get_int_attr(
+ JNIEnv *env,
+ jclass class,
+ int id,
+ jobject obj,
+ int *value) {
+
+ jmethodID methodID;
+ jint jval;
+ boolean_t noException = B_TRUE;
+
+ methodID = get_methodID(env, class, id);
+ if (methodID == NULL) {
+ noException = B_FALSE;
+ } else {
+ jval = (*env)->CallIntMethod(env, obj, methodID);
+ if ((*env)->ExceptionOccurred(env) != NULL) {
+ noException = B_FALSE;
+ } else {
+ *value = jval;
+ }
+ }
+ return (noException);
+}
+
+/*
+ * Convert a possibly multi-byte string to a jstring.
+ */
+jstring
+dd_native_to_jstring(JNIEnv *env, const char *str)
+{
+ jstring result;
+ jbyteArray bytes = 0;
+ int len;
+
+ jclass strclass;
+ jmethodID mid;
+
+ strclass = (*env)->FindClass(env, "java/lang/String");
+ if (strclass == NULL) {
+ /* exception thrown */
+ return (NULL);
+ }
+
+ mid = (*env)->GetMethodID(env, strclass, "<init>", "([B)V");
+ if (mid == NULL) {
+ /* exception thrown */
+ return (NULL);
+ }
+
+ len = strlen(str);
+ bytes = (*env)->NewByteArray(env, len);
+ if (bytes == NULL) {
+ /* exception thrown */
+ return (NULL);
+ }
+
+ (*env)->SetByteArrayRegion(env, bytes, 0, len, (jbyte *) str);
+ result = (*env)->NewObject(env, strclass, mid, bytes);
+
+ (*env)->DeleteLocalRef(env, bytes);
+ return (result);
+}
+
+/*
+ * Convert a jstring to a possibly multi-byte string.
+ */
+char *
+dd_jstring_to_native(JNIEnv *env, jstring jstr) {
+
+ jbyteArray bytes = 0;
+ jint len;
+ char *result;
+
+ jclass strclass;
+ jmethodID mid;
+
+ strclass = (*env)->FindClass(env, "java/lang/String");
+ if (strclass == NULL) {
+ /* exception thrown */
+ return (NULL);
+ }
+
+ mid = (*env)->GetMethodID(env, strclass, "getBytes", "()[B");
+ if (mid == NULL) {
+ /* exception thrown */
+ return (NULL);
+ }
+
+ bytes = (*env)->CallObjectMethod(env, jstr, mid);
+ if ((*env)->ExceptionOccurred(env)) {
+ /* exception thrown */
+ return (NULL);
+ }
+
+ len = (*env)->GetArrayLength(env, bytes);
+ result = (char *)malloc(len + 1);
+ if (result == NULL) {
+ throw_memory_exception(env);
+ (*env)->DeleteLocalRef(env, bytes);
+ return (NULL);
+ }
+
+ (*env)->GetByteArrayRegion(env, bytes, 0, len, (jbyte *) result);
+ result[len] = 0;
+
+ (*env)->DeleteLocalRef(env, bytes);
+ return (result);
+}
+
+/*
+ * Convert a jstring to a UTF-8 string.
+ */
+boolean_t
+dd_jstring_to_UTF(
+ JNIEnv *env,
+ jstring javaString,
+ char **nativeString)
+{
+ boolean_t noException = B_TRUE;
+
+ if (javaString != NULL) {
+ const char *str;
+ str = (*env)->GetStringUTFChars(env, javaString, NULL);
+ if (str == NULL) {
+ noException = B_FALSE;
+ } else {
+ *nativeString = strdup(str);
+ (*env)->ReleaseStringUTFChars(env, javaString, str);
+ if (*nativeString == NULL) {
+ throw_memory_exception(env);
+ noException = B_FALSE;
+ }
+ }
+ } else {
+ *nativeString = NULL;
+ }
+
+ return (noException);
+}
+
+
+/*
+ * Call a java object's string getter method.
+ */
+boolean_t
+dd_get_str_attr(
+ JNIEnv *env,
+ jclass class,
+ int id,
+ jobject obj,
+ char **value) {
+
+ jmethodID methodID;
+ jstring jstr;
+
+ *value = NULL;
+
+ methodID = get_methodID(env, class, id);
+ if (methodID == NULL) {
+ return (B_FALSE);
+ }
+
+ jstr = (*env)->CallObjectMethod(env, obj, methodID);
+ if ((*env)->ExceptionOccurred(env) != NULL) {
+ return (B_FALSE);
+ }
+
+ if (jstr != NULL) {
+ *value = dd_jstring_to_native(env, jstr);
+ (*env)->DeleteLocalRef(env, jstr);
+ if (*value == NULL) {
+ return (B_FALSE);
+ }
+ }
+
+ return (B_TRUE);
+}
+
+/*
+ * Reads the DHCP configuration file and creates a dsvc_datastore_t.
+ */
+boolean_t
+dd_get_conf_datastore_t(JNIEnv *env, dsvc_datastore_t *dsp) {
+
+ dhcp_confopt_t *confopts;
+ int result;
+ boolean_t noException = B_TRUE;
+
+ dsp->d_resource = NULL;
+ dsp->d_location = NULL;
+ dsp->d_config = NULL;
+
+ result = read_dsvc_conf(&confopts);
+ if (result != 0) {
+ throw_no_defaults_exception(env);
+ noException = B_FALSE;
+ } else {
+ result = confopt_to_datastore(confopts, dsp);
+ if (result != DSVC_SUCCESS) {
+ throw_libdhcpsvc_exception(env, result);
+ noException = B_FALSE;
+ }
+ free_dsvc_conf(confopts);
+ }
+ return (noException);
+}
+
+/*
+ * Makes a dsvc_datastore_t using the DHCP configuration file and overriding
+ * the settings with the arguments if they are non-NULL.
+ */
+boolean_t
+dd_make_datastore_t(JNIEnv *env,
+ dsvc_datastore_t *dsp,
+ jobject jdatastore) {
+
+ jclass ds_class;
+ jthrowable e;
+
+ char *resource = NULL;
+ char *location = NULL;
+ char *config = NULL;
+ int version = DSVC_CUR_CONVER;
+
+ dsp->d_resource = NULL;
+ dsp->d_location = NULL;
+ dsp->d_config = NULL;
+
+ /* Locate the class we need */
+ ds_class = find_class(env, DS_CLASS);
+ if (ds_class == NULL) {
+ /* exception thrown */
+ return (B_FALSE);
+ }
+
+ /* Obtain the DHCP config file data store settings */
+ if (!dd_get_conf_datastore_t(env, dsp)) {
+ e = (*env)->ExceptionOccurred(env);
+ (*env)->ExceptionClear(env);
+ if (!is_no_defaults_exception(env, e)) {
+ (*env)->Throw(env, e);
+ return (B_FALSE);
+ }
+ }
+
+ /* Get the resource */
+ if (jdatastore != NULL && !dd_get_str_attr(env, ds_class, DS_GETRSRC,
+ jdatastore, &resource)) {
+ /* exception thrown */
+ dd_free_datastore_t(dsp);
+ return (B_FALSE);
+ }
+
+ /* If resource was passed in, then override config setting */
+ if (resource != NULL) {
+ free(dsp->d_resource);
+ dsp->d_resource = resource;
+ dsp->d_conver = DSVC_CUR_CONVER;
+ }
+
+ /* Validate the resource */
+ if (!dd_is_valid_data_store(env, dsp->d_resource)) {
+ if ((*env)->ExceptionOccurred(env) == NULL) {
+ throw_invalid_resource_exception(env, dsp->d_resource);
+ }
+ dd_free_datastore_t(dsp);
+ return (B_FALSE);
+ }
+
+ /* Get the location */
+ if (jdatastore != NULL && !dd_get_str_attr(env, ds_class, DS_GETLOC,
+ jdatastore, &location)) {
+ /* exception thrown */
+ dd_free_datastore_t(dsp);
+ return (B_FALSE);
+ }
+
+ /* If location was passed in, then override config setting */
+ if (location != NULL) {
+ free(dsp->d_location);
+ dsp->d_location = location;
+ }
+
+ /* Must be defined */
+ if (dsp->d_location == NULL) {
+ throw_invalid_path_exception(env, dsp->d_location);
+ dd_free_datastore_t(dsp);
+ return (B_FALSE);
+ }
+
+ /* Get the config string */
+ if (jdatastore != NULL && !dd_get_str_attr(env, ds_class, DS_GETRSRCCFG,
+ jdatastore, &config)) {
+ /* exception thrown */
+ dd_free_datastore_t(dsp);
+ return (B_FALSE);
+ }
+
+ /* If config string was passed in, then override config setting */
+ if (config != NULL) {
+ free(dsp->d_config);
+ dsp->d_config = config;
+ }
+
+ /* Get the version */
+ if (jdatastore != NULL && !dd_get_int_attr(env, ds_class, DS_GETVER,
+ jdatastore, &version)) {
+ /* exception thrown */
+ dd_free_datastore_t(dsp);
+ return (B_FALSE);
+ }
+
+ /* If version was passed in, then override config setting */
+ if (version != DSVC_CUR_CONVER) {
+ dsp->d_conver = version;
+ }
+
+ return (B_TRUE);
+}
+
+/*
+ * Frees the strings in a dsvc_datastore_t structure.
+ */
+void
+dd_free_datastore_t(dsvc_datastore_t *dsp) {
+ free(dsp->d_resource);
+ free(dsp->d_location);
+ free(dsp->d_config);
+}
+
+/*
+ * Returns the list of possible data stores for DHCP data.
+ * List returned is terminated with a NULL.
+ */
+char **
+dd_data_stores(JNIEnv *env)
+{
+ char **dsl = NULL;
+ char **module;
+ int count;
+ int ndx;
+ int rcode;
+
+ rcode = enumerate_dd(&module, &count);
+ if (rcode != DSVC_SUCCESS) {
+ throw_libdhcpsvc_exception(env, rcode);
+ return (NULL);
+ }
+
+ if (count == 0) {
+ return (NULL);
+ }
+
+ dsl = calloc((count + 1), sizeof (char *));
+ if (dsl == NULL) {
+ free_enumerated_dd(module, count);
+ throw_memory_exception(env);
+ return (NULL);
+ }
+
+ for (ndx = 0; ndx < count; ndx++) {
+ dsl[ndx] = strdup(module[ndx]);
+ if (dsl[ndx] == NULL) {
+ break;
+ }
+ }
+
+ free_enumerated_dd(module, count);
+
+ if (ndx != count) {
+ dd_free_data_stores(dsl);
+ throw_memory_exception(env);
+ dsl = NULL;
+ }
+
+ return (dsl);
+}
+
+/*
+ * Free a data store list created by dd_data_stores().
+ */
+void
+dd_free_data_stores(char **dsl)
+{
+ int i = 0;
+
+ if (dsl != NULL) {
+ for (i = 0; dsl[i] != NULL; ++i) {
+ free(dsl[i]);
+ }
+ free(dsl);
+ }
+}
+
+/*
+ * Send a signal to a process whose command name is as specified
+ */
+int
+dd_signal(char *fname, int sig)
+{
+ pid_t pid;
+
+ pid = dd_getpid(fname);
+ if (pid == (pid_t)-1) {
+ return (-1);
+ }
+
+ if (kill(pid, sig) != 0) {
+ return (errno);
+ } else {
+ return (0);
+ }
+}
+
+/*
+ * Return a process's pid
+ */
+pid_t
+dd_getpid(char *fname)
+{
+ DIR *dirptr;
+ dirent_t *direntptr;
+ psinfo_t psinfo;
+ int proc_fd;
+ char buf[MAXPATHLEN];
+ pid_t retval = (pid_t)-1;
+
+ /*
+ * Read entries in /proc, each one is in turn a directory
+ * containing files relating to the process's state. We read
+ * the psinfo file to get the command name.
+ */
+ dirptr = opendir(PROCFS_DIR);
+ if (dirptr == (DIR *) NULL) {
+ return (retval);
+ }
+ while ((direntptr = readdir(dirptr)) != NULL) {
+ (void) snprintf(buf, sizeof (buf), PROCFS_DIR"/%s/psinfo",
+ direntptr->d_name);
+ if ((proc_fd = open(buf, O_RDONLY)) < 0) {
+ continue; /* skip this one */
+ }
+ if (read(proc_fd, &psinfo, sizeof (psinfo)) > 0) {
+ if (strncmp(psinfo.pr_fname, fname, PRFNSZ) == 0) {
+ retval = psinfo.pr_pid;
+ (void) close(proc_fd);
+ break;
+ }
+ }
+ (void) close(proc_fd);
+ }
+ (void) closedir(dirptr);
+ return (retval);
+}
+
+
+/*
+ * Get list of physical, non-loopback interfaces for the system. Those are
+ * the ones in.dhcpd will support.
+ */
+struct ip_interface **
+dd_get_interfaces()
+{
+ int s;
+ struct ifconf ifc;
+ int num_ifs;
+ int i;
+ struct ifreq *ifr;
+ struct ip_interface **ret = NULL;
+ struct ip_interface **tmpret;
+ int retcnt = 0;
+ struct sockaddr_in *sin;
+
+ /*
+ * Open socket, needed for doing the ioctls. Then get number of
+ * interfaces so we know how much memory to allocate, then get
+ * all the interface configurations.
+ */
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (ioctl(s, SIOCGIFNUM, &num_ifs) < 0) {
+ (void) close(s);
+ return (NULL);
+ }
+ ifc.ifc_len = num_ifs * sizeof (struct ifreq);
+ ifc.ifc_buf = malloc(ifc.ifc_len);
+ if (ifc.ifc_buf == NULL) {
+ (void) close(s);
+ return (NULL);
+ }
+ if (ioctl(s, SIOCGIFCONF, &ifc) < 0) {
+ free(ifc.ifc_buf);
+ (void) close(s);
+ return (NULL);
+ }
+
+ /*
+ * For each interface, stuff its name, address and netmask into the
+ * structure that we return. Filter out loopback and virtual
+ * interfaces as they are of no interest for DHCP.
+ */
+ for (i = 0, ifr = ifc.ifc_req; i < num_ifs; ++i, ++ifr) {
+ if (strchr(ifr->ifr_name, ':') != NULL) {
+ continue; /* Ignore a virtual interface */
+ }
+ if (ioctl(s, SIOCGIFFLAGS, ifr) < 0) {
+ continue; /* Can't get flags? Ignore it. */
+ }
+
+ if ((ifr->ifr_flags & IFF_LOOPBACK) ||
+ !(ifr->ifr_flags & IFF_UP)) {
+ continue; /* Ignore if loopback or down */
+ }
+ /* Get more space to store this in */
+ tmpret = realloc(ret,
+ (retcnt+1)*sizeof (struct ip_interface *));
+ if (tmpret == NULL) {
+ while (retcnt-- > 0)
+ free(ret[retcnt]);
+ free(ret);
+ free(ifc.ifc_buf);
+ (void) close(s);
+ return (NULL);
+ }
+ ret = tmpret;
+ ret[retcnt] = malloc(sizeof (struct ip_interface));
+ if (ret[retcnt] == NULL) {
+ while (retcnt-- > 0)
+ free(ret[retcnt]);
+ free(ret);
+ free(ifc.ifc_buf);
+ (void) close(s);
+ return (NULL);
+ }
+ (void) strcpy(ret[retcnt]->name, ifr->ifr_name);
+ if (ioctl(s, SIOCGIFADDR, ifr) < 0) {
+ (void) close(s);
+ while (retcnt-- > 0) {
+ free(ret[retcnt]);
+ }
+ free(ret);
+ free(ifc.ifc_buf);
+ return (NULL);
+ }
+ /*LINTED - alignment*/
+ sin = (struct sockaddr_in *)&ifr->ifr_addr;
+ ret[retcnt]->addr = sin->sin_addr;
+
+ if (ioctl(s, SIOCGIFNETMASK, ifr) < 0) {
+ (void) close(s);
+ while (retcnt-- > 0) {
+ free(ret[retcnt]);
+ }
+ free(ret);
+ free(ifc.ifc_buf);
+ return (NULL);
+ }
+ /*LINTED - alignment*/
+ sin = (struct sockaddr_in *)&ifr->ifr_addr;
+ ret[retcnt]->mask = sin->sin_addr;
+ ++retcnt;
+ }
+
+ /* Null-terminate the list */
+ if (retcnt > 0) {
+ tmpret = realloc(ret,
+ (retcnt+1)*sizeof (struct ip_interface *));
+ if (tmpret == NULL) {
+ while (retcnt-- > 0)
+ free(ret[retcnt]);
+ free(ret);
+ free(ifc.ifc_buf);
+ (void) close(s);
+ return (NULL);
+ }
+ ret = tmpret;
+ ret[retcnt] = NULL;
+ }
+ (void) close(s);
+ free(ifc.ifc_buf);
+ return (ret);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/dd_misc.h b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/dd_misc.h
new file mode 100644
index 0000000000..fee427dfdb
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/dd_misc.h
@@ -0,0 +1,68 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _DD_MISC_H
+#define _DD_MISC_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <dhcp_svc_private.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <unistd.h>
+#include <jni.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define DAEMON_FNAME "in.dhcpd"
+
+struct ip_interface {
+ char name[IFNAMSIZ];
+ struct in_addr addr;
+ struct in_addr mask;
+};
+
+extern jstring dd_native_to_jstring(JNIEnv *, const char *);
+extern char *dd_jstring_to_native(JNIEnv *, jstring);
+extern boolean_t dd_jstring_to_UTF(JNIEnv *, jstring, char **);
+extern boolean_t dd_get_str_attr(JNIEnv *, jclass, int, jobject, char **);
+extern boolean_t dd_get_conf_datastore_t(JNIEnv *, dsvc_datastore_t *);
+extern boolean_t dd_make_datastore_t(JNIEnv *, dsvc_datastore_t *, jobject);
+extern void dd_free_datastore_t(dsvc_datastore_t *);
+extern char **dd_data_stores(JNIEnv *);
+extern void dd_free_data_stores(char **);
+extern int dd_signal(char *, int);
+extern pid_t dd_getpid(char *);
+extern struct ip_interface **dd_get_interfaces(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !_DD_MISC_H */
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/dd_opt.c b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/dd_opt.c
new file mode 100644
index 0000000000..926bf68933
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/dd_opt.c
@@ -0,0 +1,767 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+#include <string.h>
+#include <malloc.h>
+#include <libintl.h>
+#include <stdio.h>
+#include <netinet/dhcp.h>
+#include <rpcsvc/nis.h>
+#include <netdb.h>
+#include <errno.h>
+#include <sys/sockio.h>
+#include <dirent.h>
+#include <procfs.h>
+#include <netdir.h>
+#include <arpa/inet.h>
+#include <rpcsvc/ypclnt.h>
+
+#include "dd_misc.h"
+#include "dd_opt.h"
+
+#define RDISC_FNAME "in.rdisc"
+
+static struct dhcp_option opt_nomem = { ENOMEM, NULL };
+
+/*
+ * Free an allocated dhcp option structure.
+ */
+void
+dd_freeopt(struct dhcp_option *opt)
+{
+ int i;
+
+ if (opt->error_code == 0) {
+ switch (opt->u.ret.datatype) {
+ case ASCII_OPTION:
+ for (i = 0; i < opt->u.ret.count; ++i) {
+ free(opt->u.ret.data.strings[i]);
+ }
+ free(opt->u.ret.data.strings);
+ break;
+ case BOOLEAN_OPTION:
+ break;
+ case IP_OPTION:
+ for (i = 0; i < opt->u.ret.count; ++i) {
+ free(opt->u.ret.data.addrs[i]);
+ }
+ free(opt->u.ret.data.addrs);
+ break;
+ case NUMBER_OPTION:
+ free(opt->u.ret.data.numbers);
+ break;
+ case OCTET_OPTION:
+ for (i = 0; i < opt->u.ret.count; ++i) {
+ free(opt->u.ret.data.octets[i]);
+ }
+ free(opt->u.ret.data.octets);
+ break;
+ default:
+ return;
+ }
+ }
+ /* Don't free the static no-memory error return */
+ if (opt != &opt_nomem) {
+ free(opt);
+ }
+}
+
+/*
+ * Allocate an option structure.
+ */
+
+static struct dhcp_option *
+newopt(enum option_type ot, ushort_t count)
+{
+ struct dhcp_option *opt;
+
+ opt = malloc(sizeof (struct dhcp_option));
+ if ((opt != NULL) && (ot != ERROR_OPTION)) {
+ opt->error_code = 0;
+ opt->u.ret.datatype = ot;
+ switch (ot) {
+ case ASCII_OPTION:
+ opt->u.ret.data.strings =
+ calloc(count, sizeof (char *));
+ if (opt->u.ret.data.strings == NULL) {
+ free(opt);
+ opt = NULL;
+ } else {
+ opt->u.ret.count = count;
+ }
+ break;
+ case BOOLEAN_OPTION:
+ opt->u.ret.count = count;
+ break;
+ case IP_OPTION:
+ opt->u.ret.data.addrs = calloc(count,
+ sizeof (struct in_addr *));
+ if (opt->u.ret.data.addrs == NULL) {
+ free(opt);
+ opt = NULL;
+ } else {
+ opt->u.ret.count = count;
+ }
+ break;
+ case NUMBER_OPTION:
+ opt->u.ret.data.numbers = calloc(count,
+ sizeof (int64_t));
+ if (opt->u.ret.data.numbers == NULL) {
+ free(opt);
+ opt = NULL;
+ } else {
+ opt->u.ret.count = count;
+ }
+ break;
+ case OCTET_OPTION:
+ opt->u.ret.data.octets = calloc(count,
+ sizeof (uchar_t *));
+ if (opt->u.ret.data.octets == NULL) {
+ free(opt);
+ opt = NULL;
+ } else {
+ opt->u.ret.count = count;
+ }
+ break;
+ default:
+ free(opt);
+ opt = NULL;
+ }
+ }
+ return (opt);
+}
+
+/*
+ * Return an out of memory error
+ */
+static struct dhcp_option *
+malloc_failure()
+{
+ if (opt_nomem.u.msg == NULL) {
+ opt_nomem.u.msg = strerror(opt_nomem.error_code);
+ }
+ return (&opt_nomem);
+}
+
+/*
+ * Return an error based on errno value
+ */
+static struct dhcp_option *
+errno_opt() {
+ struct dhcp_option *opt;
+ int serrno;
+
+ /* Save errno value before allocation attempt */
+ serrno = errno;
+ opt = newopt(ERROR_OPTION, 0);
+ if (opt == NULL) {
+ return (malloc_failure());
+ }
+ opt->error_code = serrno;
+ opt->u.msg = strerror(serrno);
+ return (opt);
+}
+/*
+ * Construct list of default routers.
+ */
+/*ARGSUSED*/
+static struct dhcp_option *
+get_default_routers(const char *arg)
+{
+ struct dhcp_option *opt;
+ FILE *fp;
+ char rbuff[BUFSIZ];
+ struct in_addr **addrs = NULL;
+ struct in_addr **tmpaddrs;
+ int addrcnt = 0;
+ char *cp;
+ int i;
+
+ /*
+ * Method here is completely bogus; read output from netstat and
+ * grab lines with destination of 'default'. Look at the netstat
+ * code if you think there's a better way...
+ */
+ if ((fp = popen("netstat -r -n -f inet", "r")) == NULL) {
+ return (errno_opt());
+ }
+
+ while (fgets(rbuff, BUFSIZ, fp) != NULL) {
+ cp = strtok(rbuff, " \t");
+ if (cp == NULL)
+ continue;
+ if (strcmp(cp, "default") == 0) {
+ /* got one, add to list */
+ tmpaddrs = realloc(addrs,
+ (addrcnt+1) * sizeof (struct in_addr *));
+ if (tmpaddrs == NULL) {
+ opt = errno_opt();
+ for (i = addrcnt - 1; i >= 0; --i) {
+ free(addrs[i]);
+ }
+ free(addrs);
+ (void) pclose(fp);
+ return (opt);
+ }
+ addrs = tmpaddrs;
+ addrs[addrcnt] = malloc(sizeof (struct in_addr));
+ if (addrs[addrcnt] == NULL) {
+ opt = errno_opt();
+ for (i = addrcnt - 1; i >= 0; --i) {
+ free(addrs[i]);
+ }
+ free(addrs);
+ (void) pclose(fp);
+ return (opt);
+ }
+
+ cp = strtok(NULL, " \t");
+ addrs[addrcnt]->s_addr = inet_addr(cp);
+ /* LINTED - comparison */
+ if (addrs[addrcnt]->s_addr == -1) {
+ /* inet_addr didn't like it */
+ opt = newopt(ERROR_OPTION, 0);
+ if (opt != NULL) {
+ opt->error_code = EINVAL;
+ opt->u.msg = strerror(EINVAL);
+ }
+ while (--addrcnt >= 0)
+ free(addrs[addrcnt]);
+ free(addrs);
+ (void) pclose(fp);
+ return (opt);
+ }
+ ++addrcnt;
+ }
+ }
+ (void) pclose(fp);
+ /*
+ * Return all the routers we found.
+ */
+ if (addrcnt != 0) {
+ opt = newopt(IP_OPTION, addrcnt);
+ if (opt == NULL) {
+ for (i = 0; i < addrcnt; ++i) {
+ free(addrs[i]);
+ free(addrs);
+ }
+ return (opt);
+ }
+ for (i = 0; i < addrcnt; ++i) {
+ opt->u.ret.data.addrs[i] = addrs[i];
+ }
+ free(addrs);
+ } else {
+ opt = newopt(ERROR_OPTION, 0);
+ if (opt != NULL) {
+ opt->error_code = 1;
+ opt->u.msg = gettext("No default router found");
+ }
+ }
+ return (opt);
+}
+
+/*ARGSUSED*/
+static struct dhcp_option *
+get_dns_domain(const char *arg)
+{
+ struct dhcp_option *opt;
+ res_state statp;
+
+ statp = calloc(1, sizeof (*statp));
+ if (statp == NULL) {
+ opt = malloc_failure();
+ } else if (res_ninit(statp) == -1) {
+ /* Resolver failed initialization */
+ opt = errno_opt();
+ } else {
+ /* Initialized OK, copy domain name to return structure */
+ opt = newopt(ASCII_OPTION, 1);
+ if (opt != NULL) {
+ /*
+ * If first one is loopback address, we return empty
+ * as this almost certainly means that DNS is not
+ * configured.
+ */
+ if (statp->nsaddr_list[0].sin_family == AF_INET &&
+ statp->nsaddr_list[0].sin_addr.s_addr ==
+ ntohl(INADDR_LOOPBACK))
+ opt->u.ret.data.strings[0] = strdup("");
+ else
+ opt->u.ret.data.strings[0] =
+ strdup(statp->defdname);
+ if (opt->u.ret.data.strings[0] == NULL) {
+ /* Couldn't allocate return memory */
+ dd_freeopt(opt);
+ opt = malloc_failure();
+ }
+ }
+ }
+ free(statp);
+ return (opt);
+}
+
+/*ARGSUSED*/
+static struct dhcp_option *
+get_dns_servers(const char *arg)
+{
+ struct dhcp_option *opt;
+ int i, j;
+ res_state statp;
+
+ statp = calloc(1, sizeof (*statp));
+ if (statp == NULL) {
+ opt = malloc_failure();
+ } else if (res_ninit(statp) == -1) {
+ /* Resolver initialization failed */
+ opt = errno_opt();
+ } else if (statp->nsaddr_list[0].sin_family == AF_INET &&
+ statp->nsaddr_list[0].sin_addr.s_addr == ntohl(INADDR_LOOPBACK)) {
+ /*
+ * If first one is loopback address, we ignore as this
+ * almost certainly means that DNS is not configured.
+ */
+ opt = newopt(IP_OPTION, 0);
+ } else {
+ /* Success, copy the data into our return structure */
+ opt = newopt(IP_OPTION, statp->nscount);
+ if (opt != NULL) {
+ for (i = 0, j = 0; i < statp->nscount; ++i) {
+ if (statp->nsaddr_list[i].sin_family != AF_INET)
+ /* IPv4 only, thanks */
+ continue;
+ opt->u.ret.data.addrs[j] = malloc(
+ sizeof (struct in_addr));
+ if (opt->u.ret.data.addrs[j] == NULL) {
+ /* Out of memory, return immediately */
+ dd_freeopt(opt);
+ free(statp);
+ return (malloc_failure());
+ }
+ *opt->u.ret.data.addrs[j++] =
+ statp->nsaddr_list[i].sin_addr;
+ }
+ /* Adjust number of addresses returned to real count */
+ opt->u.ret.count = j;
+ }
+ }
+ free(statp);
+ return (opt);
+}
+
+/* Get parameters related to a specific interface */
+static struct dhcp_option *
+get_if_param(int code, const char *arg)
+{
+ int s;
+ struct ifconf ifc;
+ int num_ifs;
+ int i;
+ struct ifreq *ifr;
+ struct dhcp_option *opt;
+#define MY_TRUE 1
+#define MY_FALSE 0
+ int found = MY_FALSE;
+ struct sockaddr_in *sin;
+
+ /*
+ * Open socket, needed for doing the ioctls. Then get number of
+ * interfaces so we know how much memory to allocate, then get
+ * all the interface configurations.
+ */
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (ioctl(s, SIOCGIFNUM, &num_ifs) < 0) {
+ return (errno_opt());
+ }
+ ifc.ifc_len = num_ifs * sizeof (struct ifreq);
+ ifc.ifc_buf = malloc(ifc.ifc_len);
+ if (ifc.ifc_buf == NULL) {
+ return (malloc_failure());
+ }
+ if (ioctl(s, SIOCGIFCONF, &ifc) < 0) {
+ opt = errno_opt();
+ free(ifc.ifc_buf);
+ (void) close(s);
+ return (opt);
+ }
+
+ /*
+ * Find the interface which matches the one requested, and then
+ * return the parameter requested.
+ */
+ for (i = 0, ifr = ifc.ifc_req; i < num_ifs; ++i, ++ifr) {
+ if (strcmp(ifr->ifr_name, arg) != 0) {
+ continue;
+ }
+ found = MY_TRUE;
+ switch (code) {
+ case CD_SUBNETMASK:
+ if (ioctl(s, SIOCGIFNETMASK, ifr) < 0) {
+ opt = errno_opt();
+ free(ifc.ifc_buf);
+ (void) close(s);
+ return (opt);
+ }
+ opt = newopt(IP_OPTION, 1);
+ if (opt == NULL) {
+ free(ifc.ifc_buf);
+ (void) close(s);
+ return (malloc_failure());
+ }
+ opt->u.ret.data.addrs[0] =
+ malloc(sizeof (struct in_addr));
+ if (opt->u.ret.data.addrs[0] == NULL) {
+ free(ifc.ifc_buf);
+ (void) close(s);
+ return (malloc_failure());
+ }
+ /*LINTED - alignment*/
+ sin = (struct sockaddr_in *)&ifr->ifr_addr;
+ *opt->u.ret.data.addrs[0] = sin->sin_addr;
+ break;
+ case CD_MTU:
+ if (ioctl(s, SIOCGIFMTU, ifr) < 0) {
+ opt = errno_opt();
+ free(ifc.ifc_buf);
+ (void) close(s);
+ return (opt);
+ }
+ opt = newopt(NUMBER_OPTION, 1);
+ if (opt == NULL) {
+ free(ifc.ifc_buf);
+ (void) close(s);
+ return (malloc_failure());
+ }
+ opt->u.ret.data.numbers[0] = ifr->ifr_metric;
+ break;
+ case CD_BROADCASTADDR:
+ if (ioctl(s, SIOCGIFBRDADDR, ifr) < 0) {
+ opt = errno_opt();
+ free(ifc.ifc_buf);
+ (void) close(s);
+ return (opt);
+ }
+ opt = newopt(IP_OPTION, 1);
+ if (opt == NULL) {
+ free(ifc.ifc_buf);
+ (void) close(s);
+ return (malloc_failure());
+ }
+ opt->u.ret.data.addrs[0] =
+ malloc(sizeof (struct in_addr));
+ if (opt->u.ret.data.addrs[0] == NULL) {
+ free(ifc.ifc_buf);
+ (void) close(s);
+ return (malloc_failure());
+ }
+ /*LINTED - alignment*/
+ sin = (struct sockaddr_in *)&ifr->ifr_addr;
+ *opt->u.ret.data.addrs[0] = sin->sin_addr;
+ break;
+ default:
+ opt = newopt(ERROR_OPTION, 0);
+ opt->error_code = 1;
+ opt->u.msg = gettext("Bad option code in get_if_param");
+ }
+ break;
+ }
+ free(ifc.ifc_buf);
+ (void) close(s);
+ if (found == MY_FALSE) {
+ opt = newopt(ERROR_OPTION, 0);
+ opt->error_code = 1;
+ opt->u.msg = gettext("No such interface");
+ }
+ return (opt);
+}
+
+/*
+ * See if we are using router discovery on this system. Method is to
+ * read procfs and find out if the in.rdisc daemon is running.
+ */
+/*ARGSUSED*/
+static struct dhcp_option *
+get_router_discovery(const char *arg)
+{
+ struct dhcp_option *opt;
+
+ opt = newopt(NUMBER_OPTION, 1);
+ if (opt == NULL) {
+ return (malloc_failure());
+ }
+ if (dd_getpid(RDISC_FNAME) != -1) {
+ opt->u.ret.data.numbers[0] = 1;
+ } else {
+ opt->u.ret.data.numbers[0] = 0;
+ }
+ return (opt);
+}
+
+/*ARGSUSED*/
+static struct dhcp_option *
+get_nis_domain(const char *arg)
+{
+ struct dhcp_option *opt;
+ char *d;
+ int err;
+
+ err = yp_get_default_domain(&d);
+ if (err != 0) {
+ opt = newopt(ERROR_OPTION, 0);
+ if (opt != NULL) {
+ opt->error_code = err;
+ opt->u.msg = gettext("Error in yp_get_default_domain");
+ }
+ } else {
+ opt = newopt(ASCII_OPTION, 1);
+ if (opt == NULL) {
+ return (malloc_failure());
+ }
+ opt->u.ret.data.strings[0] = strdup(d);
+ if (opt->u.ret.data.strings[0] == NULL) {
+ dd_freeopt(opt);
+ return (malloc_failure());
+ }
+ }
+ return (opt);
+}
+
+/*
+ * Provide a default for the NISserv option. We can only reliably
+ * find out the master (as that's the only API) so that's what we provide.
+ */
+/*ARGSUSED*/
+static struct dhcp_option *
+get_nis_servers(const char *arg)
+{
+ struct dhcp_option *opt;
+ int err;
+ char *d;
+ char *m;
+ struct hostent *hent;
+
+ /*
+ * Get the default domain name, ask for master of hosts table,
+ * look master up in hosts table to get address.
+ */
+ err = yp_get_default_domain(&d);
+ if (err != 0) {
+ opt = newopt(ERROR_OPTION, 0);
+ if (opt != NULL) {
+ opt->error_code = err;
+ opt->u.msg = gettext("Error in yp_get_default_domain");
+ }
+ } else if ((err = yp_master(d, "hosts.byname", &m)) != 0) {
+ opt = newopt(ERROR_OPTION, 0);
+ if (opt != NULL) {
+ opt->error_code = err;
+ opt->u.msg = gettext("Error in yp_master");
+ }
+ } else if ((hent = gethostbyname(m)) == NULL) {
+ free(m);
+ opt = newopt(ERROR_OPTION, 0);
+ if (opt != NULL) {
+ opt->error_code = h_errno;
+ opt->u.msg = gettext("Error in gethostbyname()");
+ }
+ } else {
+ free(m);
+ opt = newopt(IP_OPTION, 1);
+ if (opt == NULL) {
+ return (malloc_failure());
+ }
+ opt->u.ret.data.addrs[0] = malloc(sizeof (struct in_addr));
+ if (opt->u.ret.data.addrs[0] == NULL) {
+ dd_freeopt(opt);
+ return (malloc_failure());
+ }
+ /*LINTED - alignment*/
+ *opt->u.ret.data.addrs[0] = *(struct in_addr *)hent->h_addr;
+ }
+ return (opt);
+}
+
+/*ARGSUSED*/
+static struct dhcp_option *
+get_nisplus_domain(const char *arg)
+{
+ struct dhcp_option *opt;
+
+ opt = newopt(ASCII_OPTION, 1);
+ if (opt != NULL) {
+ opt->u.ret.data.strings[0] = strdup(nis_local_directory());
+ if (opt->u.ret.data.strings[0] == NULL) {
+ dd_freeopt(opt);
+ return (malloc_failure());
+ }
+ }
+ return (opt);
+}
+
+/*ARGSUSED*/
+static struct dhcp_option *
+get_nisplus_servers(const char *arg)
+{
+ struct dhcp_option *opt;
+ nis_result *nres;
+ nis_object *nobj;
+ int i;
+ struct netconfig *nc;
+ struct netbuf *nb;
+ int cnt = 0;
+ struct sockaddr_in *sin;
+ struct in_addr **tmpaddrs;
+
+ nres = nis_lookup(nis_local_directory(), FOLLOW_LINKS);
+ if ((NIS_RES_STATUS(nres) != NIS_SUCCESS) &&
+ (NIS_RES_STATUS(nres) != NIS_S_SUCCESS)) {
+ opt = newopt(ERROR_OPTION, 0);
+ if (opt != NULL) {
+ opt->error_code = NIS_RES_STATUS(nres);
+ opt->u.msg = nis_sperrno(NIS_RES_STATUS(nres));
+ }
+ nis_freeresult(nres);
+ return (opt);
+ }
+ nobj = NIS_RES_OBJECT(nres);
+ if (nobj->zo_data.zo_type != NIS_DIRECTORY_OBJ) {
+ opt = newopt(ERROR_OPTION, 0);
+ if (opt != NULL) {
+ opt->error_code = 1;
+ opt->u.msg = gettext("Not a directory object");
+ }
+ nis_freeresult(nres);
+ return (opt);
+ }
+ opt = newopt(IP_OPTION, nobj->DI_data.do_servers.do_servers_len);
+ if (opt == NULL) {
+ nis_freeresult(nres);
+ return (malloc_failure());
+ }
+ for (i = 0; i < nobj->DI_data.do_servers.do_servers_len; ++i) {
+ /* Make sure it's an internet-type address */
+ if (strcmp("inet",
+ nobj->DI_data.do_servers.do_servers_val[i].
+ ep.ep_val[0].family) == 0) {
+ /* Find a translation service */
+ nc = getnetconfigent(nobj->DI_data.do_servers.
+ do_servers_val[i].ep.ep_val[0].proto);
+ if (nc == NULL) {
+ continue;
+ }
+ /* Translate address to normal in_addr structure */
+ nb = uaddr2taddr(nc, nobj->DI_data.do_servers.
+ do_servers_val[i].ep.ep_val[0].uaddr);
+ freenetconfigent(nc);
+ if (nb == NULL) {
+ continue;
+ }
+ opt->u.ret.data.addrs[cnt] = malloc(
+ sizeof (struct in_addr));
+ if (opt->u.ret.data.addrs[cnt] == NULL) {
+ dd_freeopt(opt);
+ nis_freeresult(nres);
+ netdir_free(nb, ND_ADDR);
+ return (malloc_failure());
+ }
+ /*LINTED - alignment*/
+ sin = (struct sockaddr_in *)nb->buf;
+ *opt->u.ret.data.addrs[cnt] = sin->sin_addr;
+ ++cnt;
+ netdir_free(nb, ND_ADDR);
+ }
+ }
+
+ nis_freeresult(nres);
+ /*
+ * Adjust size of returned block to the actual data size, as
+ * the above loop may not have filled in all of the elements
+ * which were allocated.
+ */
+ if (cnt != opt->u.ret.count) {
+ tmpaddrs = realloc(opt->u.ret.data.addrs,
+ cnt * sizeof (struct in_addr *));
+ if (tmpaddrs == NULL) {
+ dd_freeopt(opt);
+ return (malloc_failure());
+ }
+ opt->u.ret.data.addrs = tmpaddrs;
+ opt->u.ret.count = cnt;
+ }
+ return (opt);
+}
+
+/*
+ * Retrieve the default value for a specified DHCP option. Option code is
+ * from the lst in dhcp.h, arg is an option-specific string argument, and
+ * context is a presently unused parameter intended to allow this mechanism
+ * to extend to vendor options in the future. For now, only standard options
+ * are supported. Note that in some cases the returned pointer may be NULL,
+ * so the caller must check for this case.
+ */
+
+/*ARGSUSED*/
+struct dhcp_option *
+dd_getopt(ushort_t code, const char *arg, const char *context)
+{
+ struct dhcp_option *opt;
+
+ switch (code) {
+ case CD_SUBNETMASK:
+ case CD_MTU:
+ case CD_BROADCASTADDR:
+ return (get_if_param(code, arg));
+ case CD_ROUTER:
+ return (get_default_routers(arg));
+ case CD_DNSSERV:
+ return (get_dns_servers(arg));
+ case CD_DNSDOMAIN:
+ return (get_dns_domain(arg));
+ case CD_ROUTER_DISCVRY_ON:
+ return (get_router_discovery(arg));
+ case CD_NIS_DOMAIN:
+ return (get_nis_domain(arg));
+ case CD_NIS_SERV:
+ return (get_nis_servers(arg));
+ case CD_NISPLUS_DMAIN:
+ return (get_nisplus_domain(arg));
+ case CD_NISPLUS_SERVS:
+ return (get_nisplus_servers(arg));
+ default:
+ opt = newopt(ERROR_OPTION, 0);
+ if (opt != NULL) {
+ opt->error_code = 1;
+ opt->u.msg = gettext("Unimplemented option requested");
+ }
+ return (opt);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/dd_opt.h b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/dd_opt.h
new file mode 100644
index 0000000000..aa376ed98d
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/dd_opt.h
@@ -0,0 +1,75 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1998-1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _DD_OPT_H
+#define _DD_OPT_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* The different data types of options supported by the service */
+enum option_type {
+ ERROR_OPTION = -1,
+ ASCII_OPTION,
+ BOOLEAN_OPTION,
+ IP_OPTION,
+ NUMBER_OPTION,
+ OCTET_OPTION
+};
+
+/* Structure returned by dd_getopt */
+struct dhcp_option {
+ int error_code; /* Error indication; 0 == no error */
+ union {
+ char *msg; /* Error message if error_code != 0 */
+ struct {
+ enum option_type datatype; /* Type of option */
+ ushort_t granularity; /* How big is it */
+ ushort_t count; /* How many */
+ union { /* The data, depending on datatype */
+ char **strings;
+ boolean_t value;
+ struct in_addr **addrs;
+ int64_t *numbers;
+ uchar_t **octets;
+ } data;
+ } ret;
+ } u;
+};
+
+extern struct dhcp_option *dd_getopt(ushort_t, const char *, const char *);
+extern void dd_freeopt(struct dhcp_option *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !_DD_OPT_H */
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/dhcptab.c b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/dhcptab.c
new file mode 100644
index 0000000000..9d36e560fd
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/dhcptab.c
@@ -0,0 +1,1014 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1999-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <libintl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <malloc.h>
+#include <jni.h>
+#include <dhcp_svc_private.h>
+#include <dhcp_svc_confkey.h>
+#include <dhcp_symbol.h>
+#include <com_sun_dhcpmgr_bridge_Bridge.h>
+
+#include "exception.h"
+#include "dd_misc.h"
+#include "class_cache.h"
+
+/*
+ * Validate that a symbol definition in dhcptab(4) format is valid.
+ */
+static boolean_t
+validate_OptionValue(JNIEnv *env,
+ const char *key,
+ const char *value) {
+
+ dhcp_symbol_t sym;
+ char **fields;
+ int last = 0;
+ dsym_errcode_t dsymret;
+
+ dsymret = dsym_init_parser(key, value, &fields, &sym);
+ if (dsymret != DSYM_SUCCESS) {
+ throw_dsym_parser_init_exception(env, key, dsymret);
+ return (B_FALSE);
+ }
+
+ dsymret = dsym_parser(fields, &sym, &last, B_FALSE);
+ if (dsymret != DSYM_SUCCESS) {
+ throw_dsym_parser_exception(env, key, fields, last,
+ dsymret);
+ }
+
+ dsym_close_parser(fields, &sym);
+ return (dsymret == DSYM_SUCCESS);
+}
+
+/*
+ * Create a dt_rec from a DhcptabRecord.
+ */
+static dt_rec_t *
+create_dtrec(
+ JNIEnv *env,
+ jobject dhcptabRecord,
+ boolean_t validate)
+{
+ jclass dtr_class;
+ dt_rec_t *dtrec;
+ char *str;
+
+ /* Locate the class we need */
+ dtr_class = find_class(env, DTR_CLASS);
+ if (dtr_class == NULL) {
+ /* exception thrown */
+ return (NULL);
+ }
+
+ dtrec = malloc(sizeof (dt_rec_t));
+ if (dtrec == NULL) {
+ throw_memory_exception(env);
+ return (NULL);
+ }
+
+ if (!dd_get_str_attr(env, dtr_class, DTR_GETKEY, dhcptabRecord,
+ &str)) {
+ /* exception thrown */
+ free_dtrec(dtrec);
+ return (NULL);
+ }
+ (void) strlcpy(dtrec->dt_key, str, sizeof (dtrec->dt_key));
+ free(str);
+
+ if (!dd_get_str_attr(env, dtr_class, DTR_GETFLAG, dhcptabRecord,
+ &str)) {
+ /* exception thrown */
+ free_dtrec(dtrec);
+ return (NULL);
+ }
+ dtrec->dt_type = str[0];
+ free(str);
+
+ if (!dd_get_str_attr(env, dtr_class, DTR_GETSIG, dhcptabRecord,
+ &str)) {
+ /* exception thrown */
+ free_dtrec(dtrec);
+ return (NULL);
+ }
+ dtrec->dt_sig = atoll(str);
+ free(str);
+
+ if (!dd_get_str_attr(env, dtr_class, DTR_GETVAL, dhcptabRecord,
+ &dtrec->dt_value)) {
+ /* exception thrown */
+ free_dtrec(dtrec);
+ return (NULL);
+ }
+
+ if (validate) {
+ if (dtrec->dt_type == DT_SYMBOL) {
+ if (!validate_OptionValue(env, dtrec->dt_key,
+ dtrec->dt_value)) {
+ /* exception thrown */
+ free_dtrec(dtrec);
+ return (NULL);
+ }
+ }
+ }
+ return (dtrec);
+}
+
+/*
+ * Create a Macro from a dt_rec.
+ */
+static jobject
+create_Macro(
+ JNIEnv *env,
+ const dt_rec_t *dtrec)
+{
+ jobject dhcptabRecord;
+ jclass class;
+ jmethodID cons;
+
+ char ascii_sig[UINT64_MAX_CHAR + 1];
+
+ /* Find the class we need */
+ class = find_class(env, MAC_CLASS);
+ if (class == NULL) {
+ /* exception thrown */
+ return (NULL);
+ }
+
+ /* Locate the class constructor we need */
+ cons = get_methodID(env, class, MAC_CONS);
+ if (cons == NULL) {
+ /* exception thrown */
+ return (NULL);
+ }
+
+ (void) sprintf(ascii_sig, "%lld", dtrec->dt_sig);
+ dhcptabRecord = (*env)->NewObject(env, class, cons,
+ (*env)->NewStringUTF(env, dtrec->dt_key),
+ (*env)->NewStringUTF(env, dtrec->dt_value),
+ (*env)->NewStringUTF(env, ascii_sig));
+
+ if ((*env)->ExceptionOccurred(env) != NULL) {
+ dhcptabRecord = NULL;
+ }
+
+ return (dhcptabRecord);
+}
+
+/*
+ * Create an Array of vendor classes.
+ */
+static jobjectArray
+create_vendors(JNIEnv *env,
+ dhcp_classes_t *classes) {
+
+ jclass class;
+ jobjectArray jlist = NULL;
+ int i;
+
+ class = (*env)->FindClass(env, "java/lang/String");
+ if (class == NULL) {
+ /* exception thrown */
+ return (NULL);
+ }
+
+ /* Construct the array */
+ jlist = (*env)->NewObjectArray(env, classes->dc_cnt, class, NULL);
+ if (jlist == NULL) {
+ /* exception thrown */
+ return (NULL);
+ }
+
+ /* For each vendor, create an object and add it to the array */
+ for (i = 0; i < classes->dc_cnt; i++) {
+ (*env)->SetObjectArrayElement(env, jlist, i,
+ (*env)->NewStringUTF(env, classes->dc_names[i]));
+ if ((*env)->ExceptionOccurred(env) != NULL) {
+ jlist = NULL;
+ break;
+ }
+ }
+ return (jlist);
+}
+
+/*
+ * Create an Option from a dt_rec.
+ */
+static jobject
+create_Option(
+ JNIEnv *env,
+ const char *key,
+ const char *value,
+ uint64_t sig,
+ boolean_t force)
+{
+ jobject dhcptabRecord = NULL;
+ jclass class;
+ jmethodID cons;
+
+ char ascii_sig[UINT64_MAX_CHAR + 1];
+
+ dhcp_symbol_t sym;
+ char **fields;
+ int last = 0;
+ dsym_errcode_t ret = DSYM_SUCCESS;
+
+ /* Find the class we need */
+ class = find_class(env, OPT_CLASS);
+ if (class == NULL) {
+ /* exception thrown */
+ return (NULL);
+ }
+
+ /* Locate the class constructor we need */
+ cons = get_methodID(env, class, OPT_CONS);
+ if (cons == NULL) {
+ /* exception thrown */
+ return (NULL);
+ }
+
+ (void) sprintf(ascii_sig, "%lld", sig);
+
+ ret = dsym_init_parser(key, value, &fields, &sym);
+ if (ret != DSYM_SUCCESS) {
+ /* throw exception */
+ throw_dsym_parser_init_exception(env, key, ret);
+ return (NULL);
+ }
+
+ ret = dsym_parser(fields, &sym, &last, force);
+ if (ret == DSYM_SUCCESS || force) {
+ jboolean isValid = (ret == DSYM_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+ dhcptabRecord = (*env)->NewObject(env, class, cons,
+ (*env)->NewStringUTF(env, sym.ds_name),
+ (jbyte)sym.ds_category,
+ create_vendors(env, &sym.ds_classes),
+ (jshort)sym.ds_code,
+ (jbyte)sym.ds_type,
+ (jint)sym.ds_gran,
+ (jint)sym.ds_max,
+ (*env)->NewStringUTF(env, ascii_sig), isValid);
+
+ if ((*env)->ExceptionOccurred(env) != NULL) {
+ dhcptabRecord = NULL;
+ }
+ } else {
+ /* throw exception */
+ throw_dsym_parser_exception(env, key, fields, last, ret);
+ }
+
+ dsym_close_parser(fields, &sym);
+ return (dhcptabRecord);
+}
+
+/*
+ * Retrieve an option from the dhcptab. Returns
+ * the record as a new instance of a Option
+ */
+/*ARGSUSED*/
+JNIEXPORT jobject JNICALL
+Java_com_sun_dhcpmgr_bridge_Bridge_getOption(
+ JNIEnv *env,
+ jobject obj,
+ jstring jkey,
+ jobject jdatastore)
+{
+ dsvc_datastore_t datastore;
+ dsvc_handle_t handle;
+
+ dt_rec_t record;
+ dt_rec_list_t *recordList;
+ uint32_t query;
+ uint32_t count = 0;
+
+ char *key;
+ int rcode;
+
+ jobject dhcptabRecord;
+
+ /* Create a dsvc_datastore_t using args and DHCP config settings */
+ if (!dd_make_datastore_t(env, &datastore, jdatastore)) {
+ /* exception thrown */
+ return (NULL);
+ }
+
+ /* Retrieve the key argument */
+ if (!dd_jstring_to_UTF(env, jkey, &key)) {
+ /* exception thrown */
+ dd_free_datastore_t(&datastore);
+ return (NULL);
+ }
+
+ /* Open the dhcptab */
+ rcode = open_dd(&handle, &datastore, DSVC_DHCPTAB,
+ DT_DHCPTAB, DSVC_READ);
+
+ dd_free_datastore_t(&datastore);
+ if (rcode != DSVC_SUCCESS) {
+ throw_open_dd_exception(env, rcode, DT_DHCPTAB);
+ free(key);
+ return (NULL);
+ }
+
+ /* Get the records */
+ DSVC_QINIT(query);
+ DSVC_QEQ(query, DT_QTYPE | DT_QKEY);
+ (void) strlcpy(record.dt_key, key, sizeof (record.dt_key));
+ record.dt_type = DT_SYMBOL;
+
+ rcode = lookup_dd(handle, B_FALSE, query, 1, &record,
+ (void**)&recordList, &count);
+
+ (void) close_dd(&handle);
+ if (rcode == DSVC_SUCCESS) {
+ if (count == 1) {
+ dhcptabRecord = create_Option(env,
+ recordList->dtl_rec->dt_key,
+ recordList->dtl_rec->dt_value,
+ recordList->dtl_rec->dt_sig, B_TRUE);
+ free_dtrec_list(recordList);
+ } else {
+ throw_noent_exception(env, key);
+ }
+ } else {
+ throw_libdhcpsvc_exception(env, rcode);
+ }
+
+ free(key);
+
+ return (dhcptabRecord);
+}
+
+/*
+ * Use the current datastore to create a dhcptab table in a new datastore.
+ */
+/*ARGSUSED*/
+JNIEXPORT void JNICALL
+Java_com_sun_dhcpmgr_bridge_Bridge_cvtDhcptab(
+ JNIEnv *env,
+ jobject obj,
+ jobject jdatastore)
+{
+
+ dt_rec_t record;
+ dt_rec_list_t *recordList;
+ dt_rec_list_t *originalList = NULL;
+ uint32_t query;
+ uint32_t count = 0;
+
+ dsvc_handle_t curHandle;
+ dsvc_handle_t newHandle;
+ dsvc_datastore_t curDatastore;
+ dsvc_datastore_t newDatastore;
+
+ int rcode;
+ int i;
+
+ /* Get the current data store configuration */
+ if (!dd_get_conf_datastore_t(env, &curDatastore)) {
+ /* exception thrown */
+ return;
+ }
+
+ /* Make a "new" dsvc_datastore_t */
+ if (!dd_make_datastore_t(env, &newDatastore, jdatastore)) {
+ /* exception thrown */
+ dd_free_datastore_t(&curDatastore);
+ return;
+ }
+
+ /* Open the current dhcptab */
+ rcode = open_dd(&curHandle, &curDatastore, DSVC_DHCPTAB,
+ DT_DHCPTAB, DSVC_READ);
+
+ dd_free_datastore_t(&curDatastore);
+ if (rcode != DSVC_SUCCESS) {
+ throw_open_dd_exception(env, rcode, DT_DHCPTAB);
+ dd_free_datastore_t(&newDatastore);
+ return;
+ }
+
+ /* Open the new dhcptab */
+ rcode = open_dd(&newHandle, &newDatastore, DSVC_DHCPTAB, DT_DHCPTAB,
+ DSVC_CREATE | DSVC_READ | DSVC_WRITE);
+
+ dd_free_datastore_t(&newDatastore);
+ if (rcode != DSVC_SUCCESS) {
+ throw_open_dd_exception(env, rcode, DT_DHCPTAB);
+ (void) close_dd(&curHandle);
+ return;
+ }
+
+ /* Get the records */
+ DSVC_QINIT(query);
+ rcode = lookup_dd(curHandle, B_FALSE, query, -1, &record,
+ (void**)&recordList, &count);
+
+ (void) close_dd(&curHandle);
+ if (rcode != DSVC_SUCCESS) {
+ throw_libdhcpsvc_exception(env, rcode);
+ (void) close_dd(&newHandle);
+ return;
+ }
+
+ if (count != 0) {
+ originalList = recordList;
+ }
+
+ /* For each row, write client record to new table */
+ for (i = 0; i < count; i++) {
+ /* Now add the record */
+ rcode = add_dd_entry(newHandle, recordList->dtl_rec);
+
+ if (rcode != DSVC_SUCCESS) {
+ throw_add_dd_entry_exception(env, rcode,
+ recordList->dtl_rec->dt_key);
+ break;
+ }
+
+ recordList = recordList->dtl_next;
+ }
+
+ (void) close_dd(&newHandle);
+
+ if (originalList != NULL) {
+ free_dtrec_list(originalList);
+ }
+
+}
+
+/*
+ * Return all options (aka symbols) currently defined in the dhcptab.
+ * The options are returned as an array of Options.
+ */
+/*ARGSUSED*/
+JNIEXPORT jobjectArray JNICALL
+Java_com_sun_dhcpmgr_bridge_Bridge_getOptions(
+ JNIEnv *env,
+ jobject obj,
+ jobject jdatastore)
+{
+ dsvc_datastore_t datastore;
+ dsvc_handle_t handle;
+
+ dt_rec_t record;
+ dt_rec_list_t *recordList;
+ dt_rec_list_t *originalList = NULL;
+ uint32_t query;
+ uint32_t count = 0;
+ int rcode;
+
+ jclass opt_class;
+ jobjectArray jlist = NULL;
+ jobject dhcptabRecord;
+ int i;
+
+ /* Find the Option class and its constructor */
+ opt_class = find_class(env, OPT_CLASS);
+ if (opt_class == NULL) {
+ /* exception thrown */
+ return (NULL);
+ }
+
+ /* Create a dsvc_datastore_t using args and DHCP config settings */
+ if (!dd_make_datastore_t(env, &datastore, jdatastore)) {
+ /* exception thrown */
+ return (NULL);
+ }
+
+ /* Open the dhcptab */
+ rcode = open_dd(&handle, &datastore, DSVC_DHCPTAB,
+ DT_DHCPTAB, DSVC_READ);
+
+ dd_free_datastore_t(&datastore);
+ if (rcode != DSVC_SUCCESS) {
+ throw_open_dd_exception(env, rcode, DT_DHCPTAB);
+ return (NULL);
+ }
+
+ /* Get the records */
+ DSVC_QINIT(query);
+ DSVC_QEQ(query, DT_QTYPE);
+ record.dt_type = DT_SYMBOL;
+
+ rcode = lookup_dd(handle, B_FALSE, query, -1, &record,
+ (void**)&recordList, &count);
+
+ (void) close_dd(&handle);
+ if (rcode != DSVC_SUCCESS) {
+ throw_libdhcpsvc_exception(env, rcode);
+ return (NULL);
+ }
+
+ if (count != 0) {
+ originalList = recordList;
+ }
+
+ /* Construct the array */
+ jlist = (*env)->NewObjectArray(env, count, opt_class, NULL);
+ if (jlist == NULL) {
+ /* exception thrown */
+ if (originalList != NULL) {
+ free_dtrec_list(originalList);
+ }
+ return (NULL);
+ }
+
+ /* For each option, create an object and add it to the array */
+ for (i = 0; i < count; i++) {
+ dhcptabRecord = create_Option(env,
+ recordList->dtl_rec->dt_key,
+ recordList->dtl_rec->dt_value,
+ recordList->dtl_rec->dt_sig, B_TRUE);
+ if (dhcptabRecord == NULL) {
+ /* exception thrown */
+ break;
+ }
+
+ (*env)->SetObjectArrayElement(env, jlist, i, dhcptabRecord);
+ if ((*env)->ExceptionOccurred(env) != NULL) {
+ break;
+ }
+
+ recordList = recordList->dtl_next;
+ }
+
+ if (originalList != NULL) {
+ free_dtrec_list(originalList);
+ }
+
+ return (jlist);
+}
+
+/*
+ * Retrieve a macro from the dhcptab. Returns
+ * the record as a new instance of a Macro
+ */
+/*ARGSUSED*/
+JNIEXPORT jobject JNICALL
+Java_com_sun_dhcpmgr_bridge_Bridge_getMacro(
+ JNIEnv *env,
+ jobject obj,
+ jstring jkey,
+ jobject jdatastore)
+{
+ dsvc_datastore_t datastore;
+ dsvc_handle_t handle;
+
+ dt_rec_t record;
+ dt_rec_list_t *recordList;
+ uint32_t query;
+ uint32_t count = 0;
+
+ char *key;
+ int rcode;
+
+ jobject dhcptabRecord;
+
+ /* Create a dsvc_datastore_t using args and DHCP config settings */
+ if (!dd_make_datastore_t(env, &datastore, jdatastore)) {
+ /* exception thrown */
+ return (NULL);
+ }
+
+ /* Retrieve the key argument */
+ if (!dd_jstring_to_UTF(env, jkey, &key)) {
+ /* exception thrown */
+ dd_free_datastore_t(&datastore);
+ return (NULL);
+ }
+
+ /* Open the dhcptab */
+ rcode = open_dd(&handle, &datastore, DSVC_DHCPTAB,
+ DT_DHCPTAB, DSVC_READ);
+
+ dd_free_datastore_t(&datastore);
+ if (rcode != DSVC_SUCCESS) {
+ throw_open_dd_exception(env, rcode, DT_DHCPTAB);
+ free(key);
+ return (NULL);
+ }
+
+ /* Get the records */
+ DSVC_QINIT(query);
+ DSVC_QEQ(query, DT_QTYPE | DT_QKEY);
+ (void) strlcpy(record.dt_key, key, sizeof (record.dt_key));
+ record.dt_type = DT_MACRO;
+
+ rcode = lookup_dd(handle, B_FALSE, query, 1, &record,
+ (void**)&recordList, &count);
+
+ (void) close_dd(&handle);
+ if (rcode == DSVC_SUCCESS) {
+ if (count == 1) {
+ dhcptabRecord = create_Macro(env, recordList->dtl_rec);
+ free_dtrec_list(recordList);
+ } else {
+ throw_noent_exception(env, key);
+ }
+ } else {
+ throw_libdhcpsvc_exception(env, rcode);
+ }
+
+ free(key);
+
+ return (dhcptabRecord);
+}
+
+/*
+ * Return all macros defined in the dhcptab. Returned as an array of
+ * Macro objects.
+ */
+/*ARGSUSED*/
+JNIEXPORT jobjectArray JNICALL
+Java_com_sun_dhcpmgr_bridge_Bridge_getMacros(
+ JNIEnv *env,
+ jobject obj,
+ jobject jdatastore)
+{
+ dsvc_datastore_t datastore;
+ dsvc_handle_t handle;
+
+ dt_rec_t record;
+ dt_rec_list_t *recordList;
+ dt_rec_list_t *originalList = NULL;
+ uint32_t query;
+ uint32_t count = 0;
+ int rcode;
+
+ jclass mac_class;
+ jobjectArray jlist = NULL;
+ jobject dhcptabRecord;
+ int i;
+
+ /* Locate the Macro class and its constructor */
+ mac_class = find_class(env, MAC_CLASS);
+ if (mac_class == NULL) {
+ /* exception thrown */
+ return (NULL);
+ }
+
+ /* Create a dsvc_datastore_t using args and DHCP config settings */
+ if (!dd_make_datastore_t(env, &datastore, jdatastore)) {
+ /* exception thrown */
+ return (NULL);
+ }
+
+ /* Open the dhcptab */
+ rcode = open_dd(&handle, &datastore, DSVC_DHCPTAB,
+ DT_DHCPTAB, DSVC_READ);
+
+ dd_free_datastore_t(&datastore);
+ if (rcode != DSVC_SUCCESS) {
+ throw_open_dd_exception(env, rcode, DT_DHCPTAB);
+ return (NULL);
+ }
+
+ /* Get the records */
+ DSVC_QINIT(query);
+ DSVC_QEQ(query, DT_QTYPE);
+ record.dt_type = DT_MACRO;
+
+ rcode = lookup_dd(handle, B_FALSE, query, -1, &record,
+ (void**)&recordList, &count);
+
+ (void) close_dd(&handle);
+ if (rcode != DSVC_SUCCESS) {
+ throw_libdhcpsvc_exception(env, rcode);
+ return (NULL);
+ }
+
+ if (count != 0) {
+ originalList = recordList;
+ }
+
+ /* Construct the array */
+ jlist = (*env)->NewObjectArray(env, count, mac_class, NULL);
+ if (jlist == NULL) {
+ /* exception thrown */
+ if (originalList != NULL) {
+ free_dtrec_list(originalList);
+ }
+ return (NULL);
+ }
+
+ /* For each macro, create an object and add it to the array */
+ for (i = 0; i < count; i++) {
+ dhcptabRecord = create_Macro(env, recordList->dtl_rec);
+ if (dhcptabRecord == NULL) {
+ /* exception thrown */
+ break;
+ }
+
+ (*env)->SetObjectArrayElement(env, jlist, i, dhcptabRecord);
+ if ((*env)->ExceptionOccurred(env) != NULL) {
+ break;
+ }
+
+ recordList = recordList->dtl_next;
+ }
+
+ if (originalList != NULL) {
+ free_dtrec_list(originalList);
+ }
+
+ return (jlist);
+}
+
+/*
+ * Function to create an Option object
+ */
+/*ARGSUSED*/
+JNIEXPORT jobject JNICALL
+Java_com_sun_dhcpmgr_bridge_Bridge_createOption(
+ JNIEnv *env,
+ jobject obj,
+ jstring jkey,
+ jstring jvalue)
+{
+
+ jobject option;
+ char *key;
+ char *value;
+
+ /* Retrieve the key argument */
+ if (!dd_jstring_to_UTF(env, jkey, &key)) {
+ /* exception thrown */
+ return (NULL);
+ }
+
+ /* Retrieve the value argument */
+ if (!dd_jstring_to_UTF(env, jvalue, &value)) {
+ /* exception thrown */
+ free(key);
+ return (NULL);
+ }
+
+ option = create_Option(env, key, value, 0, B_FALSE);
+
+ free(key);
+ free(value);
+
+ return (option);
+}
+
+/*
+ * Function to create a new dhcptab record.
+ */
+/*ARGSUSED*/
+JNIEXPORT void JNICALL
+Java_com_sun_dhcpmgr_bridge_Bridge_createDhcptabRecord(
+ JNIEnv *env,
+ jobject obj,
+ jobject jrec,
+ jobject jdatastore)
+{
+ dsvc_handle_t handle;
+ dsvc_datastore_t datastore;
+ dt_rec_t *dtrec;
+ int rcode;
+
+ /* Create a dsvc_datastore_t using args and DHCP config settings */
+ if (!dd_make_datastore_t(env, &datastore, jdatastore)) {
+ /* exception thrown */
+ return;
+ }
+
+ dtrec = create_dtrec(env, jrec, B_TRUE);
+ if (dtrec == NULL) {
+ /* exception thrown */
+ dd_free_datastore_t(&datastore);
+ return;
+ }
+
+ /* Open the dhcptab */
+ rcode = open_dd(&handle, &datastore, DSVC_DHCPTAB,
+ DT_DHCPTAB, DSVC_WRITE);
+
+ dd_free_datastore_t(&datastore);
+ if (rcode != DSVC_SUCCESS) {
+ throw_open_dd_exception(env, rcode, DT_DHCPTAB);
+ free_dtrec(dtrec);
+ return;
+ }
+
+ /* Now add the record */
+ rcode = add_dd_entry(handle, dtrec);
+
+ (void) close_dd(&handle);
+ if (rcode != DSVC_SUCCESS) {
+ throw_add_dd_entry_exception(env, rcode, dtrec->dt_key);
+ }
+
+ free_dtrec(dtrec);
+
+}
+
+/*
+ * Modify a dhcptab record.
+ */
+/*ARGSUSED*/
+JNIEXPORT void JNICALL
+Java_com_sun_dhcpmgr_bridge_Bridge_modifyDhcptabRecord(
+ JNIEnv *env,
+ jobject obj,
+ jobject joldrec,
+ jobject jnewrec,
+ jobject jdatastore)
+{
+ dsvc_handle_t handle;
+ dsvc_datastore_t datastore;
+ dt_rec_t *dtoldrec;
+ dt_rec_t *dtnewrec;
+ int rcode;
+
+ /* Create a dsvc_datastore_t using args and DHCP config settings */
+ if (!dd_make_datastore_t(env, &datastore, jdatastore)) {
+ /* exception thrown */
+ return;
+ }
+
+ dtoldrec = create_dtrec(env, joldrec, B_FALSE);
+ if (dtoldrec == NULL) {
+ /* exception thrown */
+ dd_free_datastore_t(&datastore);
+ return;
+ }
+
+ dtnewrec = create_dtrec(env, jnewrec, B_TRUE);
+ if (dtnewrec == NULL) {
+ /* exception thrown */
+ dd_free_datastore_t(&datastore);
+ free_dtrec(dtoldrec);
+ return;
+ }
+
+ /* Open the dhcptab */
+ rcode = open_dd(&handle, &datastore, DSVC_DHCPTAB,
+ DT_DHCPTAB, DSVC_WRITE);
+
+ dd_free_datastore_t(&datastore);
+ if (rcode != DSVC_SUCCESS) {
+ throw_open_dd_exception(env, rcode, DT_DHCPTAB);
+ free_dtrec(dtoldrec);
+ free_dtrec(dtnewrec);
+ return;
+ }
+
+ /* Modify the record */
+ rcode = modify_dd_entry(handle, dtoldrec, dtnewrec);
+
+ (void) close_dd(&handle);
+ if (rcode != DSVC_SUCCESS) {
+ throw_modify_dd_entry_exception(env, rcode, dtoldrec->dt_key,
+ dtnewrec->dt_key);
+ }
+
+ free_dtrec(dtnewrec);
+ free_dtrec(dtoldrec);
+
+}
+
+/*
+ * Delete a record from the dhcptab
+ */
+/*ARGSUSED*/
+JNIEXPORT void JNICALL
+Java_com_sun_dhcpmgr_bridge_Bridge_deleteDhcptabRecord(
+ JNIEnv *env,
+ jobject obj,
+ jobject jrec,
+ jobject jdatastore)
+{
+ dsvc_handle_t handle;
+ dsvc_datastore_t datastore;
+ dt_rec_t *dtrec;
+ int rcode;
+
+ /* Create a dsvc_datastore_t using args and DHCP config settings */
+ if (!dd_make_datastore_t(env, &datastore, jdatastore)) {
+ /* exception thrown */
+ return;
+ }
+
+ dtrec = create_dtrec(env, jrec, B_FALSE);
+ if (dtrec == NULL) {
+ /* exception thrown */
+ dd_free_datastore_t(&datastore);
+ return;
+ }
+
+ /* Open the dhcptab */
+ rcode = open_dd(&handle, &datastore, DSVC_DHCPTAB,
+ DT_DHCPTAB, DSVC_WRITE);
+
+ dd_free_datastore_t(&datastore);
+ if (rcode != DSVC_SUCCESS) {
+ throw_open_dd_exception(env, rcode, DT_DHCPTAB);
+ free_dtrec(dtrec);
+ return;
+ }
+
+ /* Delete the record */
+ rcode = delete_dd_entry(handle, dtrec);
+
+ (void) close_dd(&handle);
+ if (rcode != DSVC_SUCCESS) {
+ throw_delete_dd_entry_exception(env, rcode, dtrec->dt_key);
+ }
+
+ free_dtrec(dtrec);
+}
+
+/*
+ * Create the dhcptab.
+ */
+/*ARGSUSED*/
+JNIEXPORT void JNICALL
+Java_com_sun_dhcpmgr_bridge_Bridge_createDhcptab(
+ JNIEnv *env,
+ jobject obj,
+ jobject jdatastore)
+{
+ dsvc_handle_t handle;
+ dsvc_datastore_t datastore;
+ int rcode;
+
+ /* Create a dsvc_datastore_t using args and DHCP config settings */
+ if (!dd_make_datastore_t(env, &datastore, jdatastore)) {
+ /* exception thrown */
+ return;
+ }
+
+ /* Open the dhcptab and in the process create it */
+ rcode = open_dd(&handle, &datastore, DSVC_DHCPTAB, DT_DHCPTAB,
+ DSVC_CREATE | DSVC_READ | DSVC_WRITE);
+
+ dd_free_datastore_t(&datastore);
+
+ /*
+ * If open was successful, then close. Otherwise, if unsuccessful
+ * opening table, then map error to exception.
+ */
+ if (rcode == DSVC_SUCCESS) {
+ (void) close_dd(&handle);
+ } else {
+ throw_open_dd_exception(env, rcode, DT_DHCPTAB);
+ }
+}
+
+/*
+ * Delete the dhcptab.
+ */
+/*ARGSUSED*/
+JNIEXPORT void JNICALL
+Java_com_sun_dhcpmgr_bridge_Bridge_deleteDhcptab(
+ JNIEnv *env,
+ jobject obj,
+ jobject jdatastore)
+{
+ dsvc_datastore_t datastore;
+ int rcode;
+
+ /* Create a dsvc_datastore_t using args and DHCP config settings */
+ if (!dd_make_datastore_t(env, &datastore, jdatastore)) {
+ /* exception thrown */
+ return;
+ }
+
+ rcode = remove_dd(&datastore, DSVC_DHCPTAB, DT_DHCPTAB);
+
+ if (rcode != DSVC_SUCCESS) {
+ throw_remove_dd_exception(env, rcode, DT_DHCPTAB);
+ }
+
+ dd_free_datastore_t(&datastore);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/exception.c b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/exception.c
new file mode 100644
index 0000000000..204c20875a
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/exception.c
@@ -0,0 +1,524 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdarg.h>
+#include <dhcp_svc_private.h>
+#include <dhcp_symbol.h>
+#include <libintl.h>
+#include <jni.h>
+
+#include "dd_misc.h"
+#include "exception.h"
+
+/*
+ * Note: These must match exactly with the message ids defined in the
+ * bridge ResourceBundle.properties file.
+ */
+#define DSVC_EXISTS_EX "dsvc_exists_exception"
+#define DSVC_ACCESS_EX "dsvc_access_exception"
+#define DSVC_CREDENTIAL_EX "dsvc_credential_exception"
+#define DSVC_NO_ENT_EX "dsvc_no_ent_exception"
+#define DSVC_BUSY_EX "dsvc_busy_exception"
+#define DSVC_INVALID_ARGS_EX "dsvc_invalid_args_exception"
+#define DSVC_INTERNAL_EX "dsvc_internal_exception"
+#define DSVC_UNAVAILABLE_EX "dsvc_unavailable_exception"
+#define DSVC_COLLISION_EX "dsvc_collision_exception"
+#define DSVC_UNSUPPORTED_EX "dsvc_unsupported_exception"
+#define DSVC_NO_MEMORY_EX "dsvc_no_memory_exception"
+#define DSVC_NO_RESOURCES_EX "dsvc_no_resources_exception"
+#define DSVC_BAD_RESOURCE_EX "dsvc_bad_resource_exception"
+#define DSVC_BAD_PATH_EX "dsvc_bad_path_exception"
+#define DSVC_MOD_VERSION_EX "dsvc_mod_version_exception"
+#define DSVC_MOD_ERR_EX "dsvc_mod_err_exception"
+#define DSVC_MOD_LOAD_ERR_EX "dsvc_mod_load_err_exception"
+#define DSVC_MOD_UNLOAD_ERR_EX "dsvc_mod_unload_err_exception"
+#define DSVC_MOD_CFG_ERR_EX "dsvc_mod_cfg_err_exception"
+#define DSVC_SYNCH_ERR_EX "dsvc_synch_err_exception"
+#define DSVC_NO_LOCKMGR_EX "dsvc_no_lockmgr_exception"
+#define DSVC_NO_LOCATION_EX "dsvc_no_location_exception"
+#define DSVC_NO_TABLE_EX "dsvc_no_table_exception"
+#define DSVC_TABLE_EXISTS_EX "dsvc_table_exists_exception"
+#define DSVC_BAD_CONVER_EX "dsvc_bad_conver_exception"
+#define DSVC_INTERNAL_ERROR "dsvc_internal_error"
+
+#define DSYM_CODE_OUT_OF_RANGE_EX "dsym_code_out_of_range_exception"
+#define DSYM_EXCEEDS_CLASS_SIZE_EX "dsym_exceeds_class_size_exception"
+#define DSYM_EXCEEDS_MAX_CLASS_SIZE_EX "dsym_exceeds_max_class_size_exception"
+#define DSYM_INTERNAL_EX "dsym_internal_exception"
+#define DSYM_INVALID_CAT_EX "dsym_invalid_cat_exception"
+#define DSYM_INVALID_TYPE_EX "dsym_invalid_type_exception"
+#define DSYM_NO_MEMORY_EX "dsym_no_memory_exception"
+#define DSYM_TOO_FEW_FIELDS_EX "dsym_too_few_fields_exception"
+#define DSYM_SYNTAX_EX "dsym_syntax_exception"
+#define DSYM_TOO_MANY_FIELDS_EX "dsym_too_many_fields_exception"
+#define DSYM_VALUE_OUT_OF_RANGE_EX "dsym_value_out_of_range_exception"
+
+static void
+throw_exception(JNIEnv *env, const char *name, const char *msgid,
+ int nargs, ...)
+{
+ va_list ap;
+
+ jclass class;
+ jmethodID mid;
+ jstring jmsgid = NULL;
+ jobjectArray jlist = NULL;
+ jthrowable throwObj;
+
+ va_start(ap, nargs);
+
+ class = (*env)->FindClass(env, name);
+ if (class == NULL) {
+ /* exception thrown */
+ va_end(ap);
+ return;
+ }
+
+ mid = (*env)->GetMethodID(env, class, "<init>",
+ "(Ljava/lang/String;[Ljava/lang/Object;)V");
+ if (mid == NULL) {
+ /* exception thrown */
+ va_end(ap);
+ return;
+ }
+
+ if (msgid != NULL) {
+ jmsgid = dd_native_to_jstring(env, msgid);
+ if (jmsgid == NULL) {
+ /* exception thrown */
+ va_end(ap);
+ return;
+ }
+ }
+
+ /* The arguments (if any) are arguments to the message */
+ if (nargs != 0) {
+
+ jclass strclass;
+ int i;
+ strclass = (*env)->FindClass(env, "java/lang/String");
+ if (strclass == NULL) {
+ /* exception thrown */
+ va_end(ap);
+ return;
+ }
+
+ jlist = (*env)->NewObjectArray(env, nargs, strclass, NULL);
+ if (jlist == NULL) {
+ /* exception thrown */
+ va_end(ap);
+ return;
+ }
+
+ for (i = 0; i < nargs; i++) {
+ jstring jarg;
+ char *arg;
+
+ if ((arg = va_arg(ap, char *)) == 0) {
+ break;
+ }
+
+ jarg = dd_native_to_jstring(env, arg);
+ if (jarg == NULL) {
+ /* exception thrown */
+ break;
+ }
+
+ (*env)->SetObjectArrayElement(env, jlist, i, jarg);
+ if ((*env)->ExceptionOccurred(env) != NULL) {
+ break;
+ }
+ }
+
+ }
+
+ if ((*env)->ExceptionOccurred(env) == NULL) {
+ throwObj = (jthrowable)(*env)->NewObject(env, class, mid,
+ jmsgid, jlist);
+ if (throwObj == NULL) {
+ /* exception thrown */
+ va_end(ap);
+ return;
+ }
+
+ /* finally! */
+ (*env)->Throw(env, throwObj);
+ }
+
+ va_end(ap);
+}
+
+/* Throw an exception indicating record or file exists */
+static void
+throw_exists_exception(JNIEnv *env, const char *obj)
+{
+ throw_exception(env,
+ "com/sun/dhcpmgr/bridge/ExistsException", NULL, 1, obj);
+}
+
+/* Throw an exception indicating a table already exists */
+static void
+throw_table_exists_exception(JNIEnv *env, const char *obj)
+{
+ throw_exception(env, "com/sun/dhcpmgr/bridge/TableExistsException",
+ NULL, 1, obj);
+}
+
+/* Throw an exception indicating a table does not exist */
+static void
+throw_notable_exception(JNIEnv *env, const char *obj)
+{
+ throw_exception(env,
+ "com/sun/dhcpmgr/bridge/NoTableException", NULL, 1, obj);
+}
+
+/* Throw a generic bridge exception with a specified message */
+void
+throw_bridge_exception(JNIEnv *env, const char *msgid)
+{
+ throw_exception(env,
+ "com/sun/dhcpmgr/bridge/BridgeException", msgid, 0);
+}
+
+/* Throw an exception as a result of an remove_dd() error */
+void
+throw_remove_dd_exception(JNIEnv *env, int rcode, const char *obj)
+{
+ switch (rcode) {
+ case DSVC_NO_TABLE:
+ throw_notable_exception(env, obj);
+ break;
+ default:
+ throw_libdhcpsvc_exception(env, rcode);
+ }
+}
+
+/* Throw an exception as a result of an open_dd() error */
+void
+throw_open_dd_exception(JNIEnv *env, int rcode, const char *obj)
+{
+ switch (rcode) {
+ case DSVC_TABLE_EXISTS:
+ throw_table_exists_exception(env, obj);
+ break;
+ case DSVC_NO_TABLE:
+ throw_notable_exception(env, obj);
+ break;
+ default:
+ throw_libdhcpsvc_exception(env, rcode);
+ }
+}
+
+/* Throw an exception as a result of an add_dd_entry() error */
+void
+throw_add_dd_entry_exception(JNIEnv *env, int rcode, const char *obj)
+{
+ switch (rcode) {
+ case DSVC_EXISTS:
+ throw_exists_exception(env, obj);
+ break;
+ default:
+ throw_libdhcpsvc_exception(env, rcode);
+ }
+}
+
+/* Throw an exception as a result of an delete_dd_entry() error */
+void
+throw_delete_dd_entry_exception(JNIEnv *env, int rcode, const char *obj)
+{
+ switch (rcode) {
+ case DSVC_NOENT:
+ throw_noent_exception(env, obj);
+ break;
+ default:
+ throw_libdhcpsvc_exception(env, rcode);
+ }
+}
+
+/* Throw an exception as a result of an modify_dd_entry() error */
+void
+throw_modify_dd_entry_exception(JNIEnv *env, int rcode, const char *orig,
+ const char *new)
+{
+ switch (rcode) {
+ case DSVC_EXISTS:
+ throw_exists_exception(env, new);
+ break;
+ case DSVC_NOENT:
+ throw_noent_exception(env, orig);
+ break;
+ default:
+ throw_libdhcpsvc_exception(env, rcode);
+ }
+}
+
+/* Throw an out of memory exception */
+void
+throw_memory_exception(JNIEnv *env)
+{
+ throw_libdhcpsvc_exception(env, DSVC_NO_MEMORY);
+}
+
+/* Throw an exception indicating that there is no DHCP config file */
+void
+throw_no_defaults_exception(JNIEnv *env)
+{
+ throw_exception(env,
+ "com/sun/dhcpmgr/bridge/NoDefaultsException", NULL, 0);
+}
+
+/* Throw an exception indicating record or file does not exist */
+void
+throw_noent_exception(JNIEnv *env, const char *obj)
+{
+ throw_exception(env,
+ "com/sun/dhcpmgr/bridge/NoEntryException", NULL, 1, obj);
+}
+
+/* Throw an exception indicating an invalid resource */
+void
+throw_invalid_resource_exception(JNIEnv *env, const char *obj)
+{
+ throw_exception(env, "com/sun/dhcpmgr/bridge/InvalidRsrcException",
+ NULL, 1, obj);
+}
+
+/* Throw an exception indicating an invalid path */
+void
+throw_invalid_path_exception(JNIEnv *env, const char *obj)
+{
+ throw_exception(env, "com/sun/dhcpmgr/bridge/InvalidPathException",
+ NULL, 1, obj);
+}
+
+/* Throw an exception indicating that the service is not currently running */
+void
+throw_not_running_exception(JNIEnv *env)
+{
+ throw_exception(env,
+ "com/sun/dhcpmgr/bridge/NotRunningException", NULL, 0);
+}
+
+/* Throw exception for a libdhcpsvc error that requires no special treatment */
+void
+throw_libdhcpsvc_exception(JNIEnv *env, int rcode)
+{
+ const char *msgid;
+
+ switch (rcode) {
+ case DSVC_SUCCESS:
+ break;
+ case DSVC_EXISTS:
+ msgid = DSVC_EXISTS_EX;
+ break;
+ case DSVC_ACCESS:
+ msgid = DSVC_ACCESS_EX;
+ break;
+ case DSVC_NO_CRED:
+ msgid = DSVC_CREDENTIAL_EX;
+ break;
+ case DSVC_NOENT:
+ msgid = DSVC_NO_ENT_EX;
+ break;
+ case DSVC_BUSY:
+ msgid = DSVC_BUSY_EX;
+ break;
+ case DSVC_INVAL:
+ msgid = DSVC_INVALID_ARGS_EX;
+ break;
+ case DSVC_INTERNAL:
+ msgid = DSVC_INTERNAL_EX;
+ break;
+ case DSVC_UNAVAILABLE:
+ msgid = DSVC_UNAVAILABLE_EX;
+ break;
+ case DSVC_COLLISION:
+ msgid = DSVC_COLLISION_EX;
+ break;
+ case DSVC_UNSUPPORTED:
+ msgid = DSVC_UNSUPPORTED_EX;
+ break;
+ case DSVC_NO_MEMORY:
+ msgid = DSVC_NO_MEMORY_EX;
+ break;
+ case DSVC_NO_RESOURCES:
+ msgid = DSVC_NO_RESOURCES_EX;
+ break;
+ case DSVC_BAD_RESOURCE:
+ msgid = DSVC_BAD_RESOURCE_EX;
+ break;
+ case DSVC_BAD_PATH:
+ msgid = DSVC_BAD_PATH_EX;
+ break;
+ case DSVC_MODULE_VERSION:
+ msgid = DSVC_MOD_VERSION_EX;
+ break;
+ case DSVC_MODULE_ERR:
+ msgid = DSVC_MOD_ERR_EX;
+ break;
+ case DSVC_MODULE_LOAD_ERR:
+ msgid = DSVC_MOD_LOAD_ERR_EX;
+ break;
+ case DSVC_MODULE_UNLOAD_ERR:
+ msgid = DSVC_MOD_UNLOAD_ERR_EX;
+ break;
+ case DSVC_MODULE_CFG_ERR:
+ msgid = DSVC_MOD_CFG_ERR_EX;
+ break;
+ case DSVC_SYNCH_ERR:
+ msgid = DSVC_SYNCH_ERR_EX;
+ break;
+ case DSVC_NO_LOCKMGR:
+ msgid = DSVC_NO_LOCKMGR_EX;
+ break;
+ case DSVC_NO_LOCATION:
+ msgid = DSVC_NO_LOCATION_EX;
+ break;
+ case DSVC_BAD_CONVER:
+ msgid = DSVC_BAD_CONVER_EX;
+ break;
+ default:
+ msgid = DSVC_INTERNAL_ERROR;
+ }
+
+ throw_bridge_exception(env, msgid);
+}
+
+/* Determine whether an exception is a defaults file doesn't exist exception */
+boolean_t
+is_no_defaults_exception(JNIEnv *env, jthrowable e)
+{
+ jclass class;
+ boolean_t result = B_FALSE;
+
+ class = (*env)->FindClass(env,
+ "com/sun/dhcpmgr/bridge/NoDefaultsException");
+ if (class != NULL) {
+ if ((*env)->IsInstanceOf(env, e, class) == JNI_TRUE &&
+ e != NULL) {
+ result = B_TRUE;
+ }
+ }
+
+ return (result);
+}
+
+/* Throw a symbol parsing error */
+/* ARGSUSED [one day we should use the `key' argument in messages] */
+void
+throw_dsym_parser_exception(JNIEnv *env, const char *key, char **fields,
+ int field, dsym_errcode_t rcode)
+{
+ const char *dsym_exception = "com/sun/dhcpmgr/bridge/DsymException";
+
+ char ascii_long_1[ULONG_MAX_CHAR + 1];
+ char ascii_long_2[ULONG_MAX_CHAR + 1];
+ ushort_t min;
+ ushort_t max;
+
+ switch (rcode) {
+ case DSYM_SUCCESS:
+ break;
+ case DSYM_SYNTAX_ERROR:
+ throw_exception(env,
+ dsym_exception, DSYM_SYNTAX_EX, 1, fields[field]);
+ break;
+ case DSYM_CODE_OUT_OF_RANGE:
+ (void) dsym_get_code_ranges(fields[DSYM_CAT_FIELD],
+ &min, &max, B_TRUE);
+ (void) sprintf(ascii_long_1, "%d", min);
+ (void) sprintf(ascii_long_2, "%d", max);
+ throw_exception(env, dsym_exception, DSYM_CODE_OUT_OF_RANGE_EX,
+ 3, fields[DSYM_CAT_FIELD], ascii_long_1, ascii_long_2);
+ break;
+ case DSYM_VALUE_OUT_OF_RANGE:
+ throw_exception(env, dsym_exception,
+ DSYM_VALUE_OUT_OF_RANGE_EX, 1, fields[field]);
+ break;
+ case DSYM_INVALID_CAT:
+ throw_exception(env, dsym_exception,
+ DSYM_INVALID_CAT_EX, 1, fields[DSYM_CAT_FIELD]);
+ break;
+ case DSYM_INVALID_TYPE:
+ throw_exception(env, dsym_exception,
+ DSYM_INVALID_TYPE_EX, 1, fields[DSYM_TYPE_FIELD]);
+ break;
+ case DSYM_EXCEEDS_CLASS_SIZE:
+ (void) sprintf(ascii_long_1, "%d", DSYM_CLASS_SIZE);
+ throw_exception(env, dsym_exception,
+ DSYM_EXCEEDS_CLASS_SIZE_EX, 1, ascii_long_1);
+ break;
+ case DSYM_EXCEEDS_MAX_CLASS_SIZE:
+ (void) sprintf(ascii_long_1, "%d", DSYM_MAX_CLASS_SIZE);
+ throw_exception(env, dsym_exception,
+ DSYM_EXCEEDS_MAX_CLASS_SIZE_EX, 1, ascii_long_1);
+ break;
+ case DSYM_NO_MEMORY:
+ throw_exception(env, dsym_exception, DSYM_NO_MEMORY_EX, 0);
+ break;
+ default:
+ throw_exception(env, dsym_exception, DSYM_INTERNAL_EX, 0);
+ }
+}
+
+/* Throw a symbol init parsing error */
+void
+throw_dsym_parser_init_exception(JNIEnv *env, const char *key,
+ dsym_errcode_t rcode)
+{
+ const char *dsym_exception = "com/sun/dhcpmgr/bridge/DsymException";
+
+ switch (rcode) {
+ case DSYM_SUCCESS:
+ break;
+ case DSYM_NULL_FIELD:
+ throw_exception(env,
+ dsym_exception, DSYM_TOO_FEW_FIELDS_EX, 1, key);
+ break;
+ case DSYM_TOO_MANY_FIELDS:
+ throw_exception(env,
+ dsym_exception, DSYM_TOO_MANY_FIELDS_EX, 1, key);
+ break;
+ case DSYM_NO_MEMORY:
+ throw_exception(env, dsym_exception, DSYM_NO_MEMORY_EX, 0);
+ break;
+ default:
+ throw_exception(env, dsym_exception, DSYM_INTERNAL_EX, 0);
+ }
+}
+
+/* Throw an exception indicating an error in wordexp */
+void
+throw_wordexp_exception(JNIEnv *env, int code)
+{
+ char buf[UINT64_MAX_CHAR + 1];
+
+ (void) snprintf(buf, sizeof (buf), "%d", code);
+ throw_exception(env, "com/sun/dhcpmgr/bridge/WordexpException",
+ NULL, 1, buf);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/exception.h b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/exception.h
new file mode 100644
index 0000000000..269d3ef4b1
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/exception.h
@@ -0,0 +1,73 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _EXCEPTION_H
+#define _EXCEPTION_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <jni.h>
+#include <dhcp_symbol.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * This really doesn't belong here, but rather than create another whole
+ * header file just for this macro, we stuck it here.
+ */
+#define ARRAY_LENGTH(arr, len) for (len = 0; arr[len] != NULL; ++len);
+
+#define ULONG_MAX_CHAR (sizeof ("4294967295"))
+#define UINT64_MAX_CHAR (sizeof ("18446744073709551615"))
+#define IPADDR_MAX_CHAR (sizeof ("255.255.255.255"))
+
+extern void throw_libdhcpsvc_exception(JNIEnv *, int);
+extern void throw_remove_dd_exception(JNIEnv *, int, const char *);
+extern void throw_open_dd_exception(JNIEnv *, int, const char *);
+extern void throw_add_dd_entry_exception(JNIEnv *, int, const char *);
+extern void throw_delete_dd_entry_exception(JNIEnv *, int, const char *);
+extern void throw_modify_dd_entry_exception(JNIEnv *, int, const char *,
+ const char *);
+extern void throw_bridge_exception(JNIEnv *, const char *);
+extern void throw_memory_exception(JNIEnv *);
+extern void throw_no_defaults_exception(JNIEnv *);
+extern void throw_noent_exception(JNIEnv *, const char *);
+extern void throw_not_running_exception(JNIEnv *);
+extern void throw_invalid_resource_exception(JNIEnv *, const char *);
+extern void throw_invalid_path_exception(JNIEnv *, const char *);
+extern void throw_dsym_parser_exception(JNIEnv *, const char *, char **, int,
+ dsym_errcode_t);
+extern void throw_dsym_parser_init_exception(JNIEnv *, const char *,
+ dsym_errcode_t);
+extern void throw_wordexp_exception(JNIEnv *, int);
+extern boolean_t is_no_defaults_exception(JNIEnv *, jthrowable);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !_EXCEPTION_H */
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/inc.flg b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/inc.flg
new file mode 100644
index 0000000000..d7246ea4b0
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/inc.flg
@@ -0,0 +1,30 @@
+#!/bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+find_files "s.*" usr/src/common/net/dhcp
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/inittab.c b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/inittab.c
new file mode 100644
index 0000000000..b05b29392a
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/inittab.c
@@ -0,0 +1,153 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdlib.h>
+#include <jni.h>
+#include <dhcp_inittab.h>
+#include <dhcp_symbol.h>
+#include <exception.h>
+#include <com_sun_dhcpmgr_bridge_Bridge.h>
+#include <dhcp_svc_private.h>
+
+#include "class_cache.h"
+
+/*
+ * Retrieve a list of DHCP options from the dhcp inittab.
+ */
+/*ARGSUSED*/
+JNIEXPORT jobjectArray JNICALL
+Java_com_sun_dhcpmgr_bridge_Bridge_getInittabOptions(
+ JNIEnv *env,
+ jobject obj,
+ jbyte jcategory) {
+
+ jclass opt_class;
+ jmethodID opt_cons;
+ jobjectArray jlist = NULL;
+ jobject jobj;
+ jstring jname;
+ jshort jcode;
+ jbyte jtype;
+ jint jgran;
+ jint jmax;
+
+ uchar_t category;
+ dhcp_symbol_t *entryptr;
+ dhcp_symbol_t *list;
+ dhcp_symbol_t internal;
+ size_t num;
+ int i;
+
+ /* Make sure we have the classes & methods we need */
+ opt_class = find_class(env, OPT_CLASS);
+ if (opt_class == NULL) {
+ /* exception thrown */
+ return (NULL);
+ }
+ opt_cons = get_methodID(env, opt_class, OPT_CONS);
+ if (opt_cons == NULL) {
+ /* exception thrown */
+ return (NULL);
+ }
+
+ /* Translate the dhcpmgr category to the inittab category */
+ if (jcategory == DSYM_STANDARD) {
+ category = ITAB_CAT_STANDARD | ITAB_CAT_INTERNAL |
+ ITAB_CAT_FIELD;
+ } else {
+ category = jcategory;
+ }
+
+ /* Get the list of options */
+ list = inittab_load(category, ITAB_CONS_MANAGER, &num);
+ if (list == NULL) {
+ return (NULL);
+ }
+
+ /* Construct the array */
+ jlist = (*env)->NewObjectArray(env, num, opt_class, NULL);
+ if (jlist == NULL) {
+ /* exception thrown */
+ free(list);
+ return (NULL);
+ }
+
+ /* For each option, create an object and add it to the array */
+ for (i = 0; i < num; ++i) {
+
+ /* Verify the entry. Use the internal if necessary. */
+ if (inittab_verify(&list[i], &internal) == ITAB_FAILURE) {
+ entryptr = &internal;
+ } else {
+ entryptr = &list[i];
+ }
+
+ jtype = entryptr->ds_type;
+ jname = (*env)->NewStringUTF(env, entryptr->ds_name);
+ if (jname == NULL) {
+ /* exception thrown */
+ break;
+ }
+
+ /* HACK. Since the codes for fields can overlap the */
+ /* codes for STANDARD options, we will just set the */
+ /* code to zero and ignore the need for these codes to */
+ /* be unique. We do the same for internal but not */
+ /* for the same reason. For internal we have no need */
+ /* for the actual code and since we expect them to */
+ /* change in the future, we'll just go ahead and */
+ /* set them to zero too. */
+ if (entryptr->ds_category == DSYM_INTERNAL ||
+ entryptr->ds_category == DSYM_FIELD) {
+ jcode = (jshort)0;
+ } else {
+ jcode = entryptr->ds_code;
+ }
+
+ jmax = entryptr->ds_max;
+ jgran = entryptr->ds_gran;
+
+ /* Create an 'Option' */
+ jobj = (*env)->NewObject(env, opt_class, opt_cons, jname,
+ jcategory, NULL, jcode, jtype, jgran, jmax, NULL,
+ JNI_TRUE);
+ if (jobj == NULL) {
+ /* exception thrown */
+ break;
+ }
+
+ (*env)->SetObjectArrayElement(env, jlist, i, jobj);
+ if ((*env)->ExceptionOccurred(env) != NULL) {
+ break;
+ }
+ }
+
+ free(list);
+
+ return (jlist);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/network.c b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/network.c
new file mode 100644
index 0000000000..c61e2b6bf7
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/network.c
@@ -0,0 +1,1102 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <string.h>
+#include <dhcp_svc_private.h>
+#include <dhcp_svc_confkey.h>
+#include <libinetutil.h>
+#include <libintl.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <malloc.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <jni.h>
+#include <com_sun_dhcpmgr_bridge_Bridge.h>
+
+#include "exception.h"
+#include "dd_misc.h"
+#include "class_cache.h"
+
+/*
+ * Create a dn_rec from a DhcpClientRecord.
+ */
+static dn_rec_t *
+create_dnrec(JNIEnv *env,
+ jobject dhcpClientRecord)
+{
+ jclass dcr_class;
+
+ dn_rec_t *dnrec = NULL;
+ char *str;
+ unsigned int cid_len;
+
+ /* Locate the class we need */
+ dcr_class = find_class(env, DCR_CLASS);
+ if (dcr_class == NULL) {
+ /* exception thrown */
+ return (NULL);
+ }
+
+ dnrec = malloc(sizeof (dn_rec_t));
+ if (dnrec == NULL) {
+ throw_memory_exception(env);
+ return (NULL);
+ }
+
+ /*
+ * Get the cid and the cid_len.
+ */
+ if (!dd_get_str_attr(env, dcr_class, DCR_GETCID, dhcpClientRecord,
+ &str)) {
+ /* exception thrown */
+ free_dnrec(dnrec);
+ return (NULL);
+ }
+ cid_len = DN_MAX_CID_LEN;
+ if (hexascii_to_octet(str, strlen(str), dnrec->dn_cid, &cid_len) != 0) {
+ free(str);
+ free_dnrec(dnrec);
+ throw_memory_exception(env);
+ return (NULL);
+ }
+ dnrec->dn_cid_len = cid_len;
+ free(str);
+
+ /*
+ * Get the flags.
+ */
+ if (!dd_get_str_attr(env, dcr_class, DCR_GETFLAG, dhcpClientRecord,
+ &str)) {
+ /* exception thrown */
+ free_dnrec(dnrec);
+ return (NULL);
+ }
+ dnrec->dn_flags = atoi(str);
+ free(str);
+
+ /*
+ * Get the client IP.
+ */
+ if (!dd_get_str_attr(env, dcr_class, DCR_GETCIP, dhcpClientRecord,
+ &str)) {
+ /* exception thrown */
+ free_dnrec(dnrec);
+ return (NULL);
+ }
+ dnrec->dn_cip.s_addr = ntohl(inet_addr(str));
+ free(str);
+
+ /*
+ * Get the server IP.
+ */
+ if (!dd_get_str_attr(env, dcr_class, DCR_GETSIP, dhcpClientRecord,
+ &str)) {
+ /* exception thrown */
+ free_dnrec(dnrec);
+ return (NULL);
+ }
+ dnrec->dn_sip.s_addr = ntohl(inet_addr(str));
+ free(str);
+
+ /*
+ * Get the expiration.
+ */
+ if (!dd_get_str_attr(env, dcr_class, DCR_GETEXP, dhcpClientRecord,
+ &str)) {
+ /* exception thrown */
+ free_dnrec(dnrec);
+ return (NULL);
+ }
+ dnrec->dn_lease = atol(str);
+ free(str);
+
+ /*
+ * Get the signature.
+ */
+ if (!dd_get_str_attr(env, dcr_class, DCR_GETSIG, dhcpClientRecord,
+ &str)) {
+ /* exception thrown */
+ free_dnrec(dnrec);
+ return (NULL);
+ }
+ dnrec->dn_sig = atoll(str);
+ free(str);
+
+ /*
+ * Get the macro.
+ */
+ if (!dd_get_str_attr(env, dcr_class, DCR_GETMAC, dhcpClientRecord,
+ &str)) {
+ /* exception thrown */
+ free_dnrec(dnrec);
+ return (NULL);
+ }
+ (void) strlcpy(dnrec->dn_macro, str, sizeof (dnrec->dn_macro));
+ free(str);
+
+ /*
+ * Get the comment.
+ */
+ if (!dd_get_str_attr(env, dcr_class, DCR_GETCMT, dhcpClientRecord,
+ &str)) {
+ /* exception thrown */
+ free_dnrec(dnrec);
+ return (NULL);
+ }
+ (void) strlcpy(dnrec->dn_comment, str, sizeof (dnrec->dn_comment));
+ free(str);
+
+ return (dnrec);
+}
+
+/*
+ * Create a DhcpClientRecord from a dn_rec.
+ */
+static jobject
+create_DhcpClientRecord(
+ JNIEnv *env,
+ dn_rec_t *dnrec)
+{
+ jclass dcr_class;
+ jmethodID dcr_cons;
+ jobject dhcpClientRecord;
+ struct in_addr tmpaddr;
+
+ char ascii_cid[DN_MAX_CID_LEN * 2 + 1];
+ char ascii_flags[2 + 1];
+ char ascii_cip[IPADDR_MAX_CHAR + 1];
+ char ascii_sip[IPADDR_MAX_CHAR + 1];
+ char ascii_lease[ULONG_MAX_CHAR + 1];
+ char ascii_sig[UINT64_MAX_CHAR + 1];
+ char ascii_macro[DSVC_MAX_MACSYM_LEN + 1];
+ char ascii_comment[DN_MAX_COMMENT_LEN + 1];
+
+ uint_t cid_len;
+ int err;
+
+ /* Find the class */
+ dcr_class = find_class(env, DCR_CLASS);
+ if (dcr_class == NULL) {
+ /* exception thrown */
+ return (NULL);
+ }
+
+ /* Locate the constructor we need */
+ dcr_cons = get_methodID(env, dcr_class, DCR_CONS);
+ if (dcr_cons == NULL) {
+ /* exception thrown */
+ return (NULL);
+ }
+
+ cid_len = DN_MAX_CID_LEN * 2 + 1;
+ err = octet_to_hexascii(dnrec->dn_cid, dnrec->dn_cid_len, ascii_cid,
+ &cid_len);
+ if (err != 0) {
+ throw_bridge_exception(env, strerror(err));
+ return (NULL);
+ }
+ ascii_cid[cid_len] = '\0';
+
+ (void) sprintf(ascii_flags, "%02hu", dnrec->dn_flags);
+
+ tmpaddr.s_addr = htonl(dnrec->dn_cip.s_addr);
+ (void) strcpy(ascii_cip, inet_ntoa(tmpaddr));
+ tmpaddr.s_addr = htonl(dnrec->dn_sip.s_addr);
+ (void) strcpy(ascii_sip, inet_ntoa(tmpaddr));
+
+ (void) sprintf(ascii_lease, "%d", dnrec->dn_lease);
+ (void) sprintf(ascii_sig, "%lld", dnrec->dn_sig);
+
+ (void) strlcpy(ascii_macro, dnrec->dn_macro, sizeof (ascii_macro));
+ (void) strlcpy(ascii_comment, dnrec->dn_comment,
+ sizeof (ascii_comment));
+
+ dhcpClientRecord = (*env)->NewObject(env, dcr_class, dcr_cons,
+ (*env)->NewStringUTF(env, ascii_cid),
+ (*env)->NewStringUTF(env, ascii_flags),
+ (*env)->NewStringUTF(env, ascii_cip),
+ (*env)->NewStringUTF(env, ascii_sip),
+ (*env)->NewStringUTF(env, ascii_lease),
+ (*env)->NewStringUTF(env, ascii_macro),
+ (*env)->NewStringUTF(env, ascii_comment),
+ (*env)->NewStringUTF(env, ascii_sig));
+
+ return (dhcpClientRecord);
+}
+
+/*
+ * Given a network name, find it's IP address.
+ */
+static boolean_t
+getNetIPByName(const char *netname, char *netip) {
+
+ struct netent *ne;
+ ulong_t addr;
+ boolean_t result = B_FALSE;
+
+ if ((ne = getnetbyname(netname)) != NULL &&
+ ne->n_addrtype == AF_INET) {
+
+ int i;
+ ulong_t tl;
+ int count;
+
+ for (i = 0, tl = (ulong_t)0xff000000, count = 0;
+ i < 4; i++, tl >>= 8) {
+
+ if ((ne->n_net & tl) == 0)
+ count += 8;
+ else
+ break;
+ }
+
+ addr = ne->n_net << count;
+ (void) sprintf(netip, "%ld.%ld.%ld.%ld",
+ ((addr & 0xff000000) >> 24), ((addr & 0x00ff0000) >> 16),
+ ((addr & 0x0000ff00) >> 8), (addr & 0x000000ff));
+
+ result = B_TRUE;
+ }
+
+ return (result);
+}
+
+/*
+ * Create a Network object for a network IP address.
+ */
+static jobject
+createNetwork(
+ JNIEnv *env,
+ const char *network)
+{
+ jclass net_class;
+ jmethodID net_cons;
+ jobject net;
+
+ struct in_addr addr;
+ struct in_addr mask;
+ jstring jstr;
+
+ /* Locate the class and methods we need */
+ net_class = find_class(env, NET_CLASS);
+ if (net_class == NULL) {
+ /* exception thrown */
+ return (NULL);
+ }
+ net_cons = get_methodID(env, net_class, NET_CONS);
+ if (net_cons == NULL) {
+ /* exception thrown */
+ return (NULL);
+ }
+
+ addr.s_addr = ntohl(inet_addr(network));
+ get_netmask4(&addr, &mask);
+
+ jstr = (*env)->NewStringUTF(env, network);
+ if (jstr == NULL) {
+ /* exception thrown */
+ return (NULL);
+ }
+
+ net = (*env)->NewObject(env, net_class, net_cons, jstr, mask.s_addr);
+
+ return (net);
+}
+
+
+/*
+ * Get the Network object for the network argument.
+ */
+/*ARGSUSED*/
+JNIEXPORT jobject JNICALL
+Java_com_sun_dhcpmgr_bridge_Bridge_getNetwork(
+ JNIEnv *env,
+ jobject obj,
+ jstring jnet)
+{
+
+ jobject netObj;
+ char *net;
+
+ char *netip = NULL;
+ char ascii_ip[IPADDR_MAX_CHAR + 1];
+
+ /* Retrieve the net argument */
+ if (!dd_jstring_to_UTF(env, jnet, &net)) {
+ /* exception thrown */
+ return (NULL);
+ }
+
+ /*
+ * If net looks like an IP address, assume it is,
+ * otherwise go get its IP
+ */
+ if (!isdigit(*net)) {
+ if (getNetIPByName(net, ascii_ip)) {
+ netip = ascii_ip;
+ }
+ } else {
+ netip = net;
+ }
+
+ /* If we could not find an IP for net, then return NULL object */
+ if (netip == NULL) {
+ free(net);
+ return (NULL);
+ }
+
+ /* Create a Network object */
+ netObj = createNetwork(env, netip);
+ if (netObj == NULL) {
+ /* exception thrown */
+ free(net);
+ return (NULL);
+ }
+
+ /* free up resources */
+ free(net);
+
+ /* return the object */
+ return (netObj);
+}
+
+/*
+ * List the networks currently under DHCP management. Return as an array
+ * of Network objects including the subnet mask for each network.
+ */
+/*ARGSUSED*/
+JNIEXPORT jobjectArray JNICALL
+Java_com_sun_dhcpmgr_bridge_Bridge_getNetworks(
+ JNIEnv *env,
+ jobject obj,
+ jobject jdatastore)
+{
+ jclass net_class;
+ jobjectArray jlist = NULL;
+ jobject net;
+ uint32_t count;
+ char **list = NULL;
+ dsvc_datastore_t datastore;
+ int rcode;
+ int i;
+
+ /* Locate the class. */
+ net_class = find_class(env, NET_CLASS);
+ if (net_class == NULL) {
+ /* exception thrown */
+ return (NULL);
+ }
+
+ /* Create a dsvc_datastore_t using args and DHCP config settings */
+ if (!dd_make_datastore_t(env, &datastore, jdatastore)) {
+ /* exception thrown */
+ return (NULL);
+ }
+
+ /* Get the list of network tables */
+ rcode = list_dd(&datastore, DSVC_DHCPNETWORK, &list, &count);
+
+ dd_free_datastore_t(&datastore);
+
+ if (rcode != DSVC_SUCCESS) {
+ throw_libdhcpsvc_exception(env, rcode);
+ return (NULL);
+ }
+
+ /* Construct the array */
+ jlist = (*env)->NewObjectArray(env, count, net_class, NULL);
+ if (jlist == NULL) {
+ /* exception thrown */
+ for (i = 0; i < count; i++) {
+ free(list[i]);
+ }
+ free(list);
+ return (NULL);
+ }
+
+ /* For each network, create an object and add it to the array */
+ for (i = 0; i < count; i++) {
+ net = createNetwork(env, list[i]);
+ if (net == NULL) {
+ /* exception thrown */
+ break;
+ }
+
+ (*env)->SetObjectArrayElement(env, jlist, i, net);
+ if ((*env)->ExceptionOccurred(env) != NULL) {
+ break;
+ }
+ }
+
+ /*
+ * Free the list now.
+ */
+ for (i = 0; i < count; i++) {
+ free(list[i]);
+ }
+ free(list);
+
+ return (jlist);
+}
+
+/*
+ * Use the current datastore to create a network table in a new datastore.
+ */
+/*ARGSUSED*/
+JNIEXPORT void JNICALL
+Java_com_sun_dhcpmgr_bridge_Bridge_cvtNetwork(
+ JNIEnv *env,
+ jobject obj,
+ jstring jnet,
+ jobject jdatastore)
+{
+ dn_rec_t record;
+ dn_rec_list_t *recordList;
+ dn_rec_list_t *originalList = NULL;
+ uint32_t query;
+ uint32_t count = 0;
+ struct in_addr tmpaddr;
+ char ascii_cip[IPADDR_MAX_CHAR + 1];
+
+ dsvc_handle_t curHandle;
+ dsvc_handle_t newHandle;
+ dsvc_datastore_t curDatastore;
+ dsvc_datastore_t newDatastore;
+
+ char *net;
+ int rcode;
+ int i;
+
+ /* Get the current data store configuration */
+ if (!dd_get_conf_datastore_t(env, &curDatastore)) {
+ /* exception thrown */
+ return;
+ }
+
+ /* Make a "new" dsvc_datastore_t */
+ if (!dd_make_datastore_t(env, &newDatastore, jdatastore)) {
+ /* exception thrown */
+ dd_free_datastore_t(&curDatastore);
+ return;
+ }
+
+ /* Retrieve the net argument */
+ if (!dd_jstring_to_UTF(env, jnet, &net)) {
+ /* exception thrown */
+ dd_free_datastore_t(&curDatastore);
+ dd_free_datastore_t(&newDatastore);
+ return;
+ }
+
+ /* Open the current network table */
+ rcode = open_dd(&curHandle, &curDatastore, DSVC_DHCPNETWORK, net,
+ DSVC_READ);
+
+ dd_free_datastore_t(&curDatastore);
+ if (rcode != DSVC_SUCCESS) {
+ throw_open_dd_exception(env, rcode, net);
+ dd_free_datastore_t(&newDatastore);
+ free(net);
+ return;
+ }
+
+ /* Open the new network table */
+ rcode = open_dd(&newHandle, &newDatastore, DSVC_DHCPNETWORK, net,
+ DSVC_CREATE | DSVC_READ | DSVC_WRITE);
+
+ dd_free_datastore_t(&newDatastore);
+ if (rcode != DSVC_SUCCESS) {
+ throw_open_dd_exception(env, rcode, net);
+ (void) close_dd(&curHandle);
+ free(net);
+ return;
+ }
+ free(net);
+
+ /* Get the records */
+ DSVC_QINIT(query);
+ rcode = lookup_dd(curHandle, B_FALSE, query, -1, &record,
+ (void**)&recordList, &count);
+
+ (void) close_dd(&curHandle);
+ if (rcode != DSVC_SUCCESS) {
+ throw_libdhcpsvc_exception(env, rcode);
+ (void) close_dd(&newHandle);
+ return;
+ }
+
+ if (count != 0) {
+ originalList = recordList;
+ }
+
+ /* For each row, write client record to new table */
+ for (i = 0; i < count; i++) {
+ /* Now add the record */
+ rcode = add_dd_entry(newHandle, recordList->dnl_rec);
+
+ if (rcode != DSVC_SUCCESS) {
+ tmpaddr.s_addr =
+ htonl(recordList->dnl_rec->dn_cip.s_addr);
+ (void) strcpy(ascii_cip, inet_ntoa(tmpaddr));
+ throw_add_dd_entry_exception(env, rcode, ascii_cip);
+ break;
+ }
+
+ recordList = recordList->dnl_next;
+ }
+
+ (void) close_dd(&newHandle);
+
+ if (originalList != NULL) {
+ free_dnrec_list(originalList);
+ }
+}
+
+/*
+ * Retrieve all of the records in a particular network table. Returns an
+ * array of DhcpClientRecord.
+ */
+/*ARGSUSED*/
+JNIEXPORT jobjectArray JNICALL
+Java_com_sun_dhcpmgr_bridge_Bridge_loadNetwork(
+ JNIEnv *env,
+ jobject obj,
+ jstring jnet,
+ jobject jdatastore)
+{
+ jclass dcr_class;
+ jobjectArray jlist = NULL;
+ jobject dhcpClientRecord;
+ int i;
+
+ dsvc_handle_t handle;
+ dsvc_datastore_t datastore;
+
+ dn_rec_t record;
+ dn_rec_list_t *recordList = NULL;
+ dn_rec_list_t *originalList = NULL;
+ uint32_t query;
+ uint32_t count = 0;
+
+ char *net;
+ int rcode;
+
+ /* Locate the class and constructor we need */
+ dcr_class = find_class(env, DCR_CLASS);
+ if (dcr_class == NULL) {
+ /* exception thrown */
+ return (NULL);
+ }
+
+ /* Create a dsvc_datastore_t using args and DHCP config settings */
+ if (!dd_make_datastore_t(env, &datastore, jdatastore)) {
+ /* exception thrown */
+ return (NULL);
+ }
+
+ /* Retrieve the net argument */
+ if (!dd_jstring_to_UTF(env, jnet, &net)) {
+ /* exception thrown */
+ dd_free_datastore_t(&datastore);
+ return (NULL);
+ }
+
+ rcode = open_dd(&handle, &datastore, DSVC_DHCPNETWORK, net, DSVC_READ);
+
+ dd_free_datastore_t(&datastore);
+ if (rcode != DSVC_SUCCESS) {
+ throw_open_dd_exception(env, rcode, net);
+ free(net);
+ return (NULL);
+ }
+ free(net);
+
+ /* Get the records */
+ DSVC_QINIT(query);
+ rcode = lookup_dd(handle, B_FALSE, query, -1, &record,
+ (void**)&recordList, &count);
+
+ (void) close_dd(&handle);
+ if (rcode != DSVC_SUCCESS) {
+ throw_libdhcpsvc_exception(env, rcode);
+ return (NULL);
+ }
+
+ /* Save original pointer so we can free it correctly at end */
+ originalList = recordList;
+
+ /* Construct the array */
+ jlist = (*env)->NewObjectArray(env, count, dcr_class, NULL);
+ if (jlist == NULL) {
+ /* exception thrown */
+ if (originalList != NULL) {
+ free_dnrec_list(originalList);
+ }
+ return (NULL);
+ }
+
+ /* For each client, create an object and add it to the array */
+ for (i = 0; i < count; i++) {
+ dhcpClientRecord = create_DhcpClientRecord(env,
+ recordList->dnl_rec);
+ if (dhcpClientRecord == NULL) {
+ /* exception thrown */
+ break;
+ }
+ recordList = recordList->dnl_next;
+
+ (*env)->SetObjectArrayElement(env, jlist, i, dhcpClientRecord);
+ if ((*env)->ExceptionOccurred(env) != NULL) {
+ break;
+ }
+ }
+
+ if (originalList != NULL) {
+ free_dnrec_list(originalList);
+ }
+
+ return (jlist);
+}
+
+/*
+ * Create a client record
+ */
+/*ARGSUSED*/
+JNIEXPORT void JNICALL
+Java_com_sun_dhcpmgr_bridge_Bridge_createDhcpClientRecord(
+ JNIEnv *env,
+ jobject obj,
+ jobject jrec,
+ jstring jnet,
+ jobject jdatastore)
+{
+ dsvc_handle_t handle;
+ dsvc_datastore_t datastore;
+ dn_rec_t *dnrec;
+ char *net;
+ int rcode;
+
+ struct in_addr tmpaddr;
+ char ascii_cip[IPADDR_MAX_CHAR + 1];
+
+ /* Create a dsvc_datastore_t using args and DHCP config settings */
+ if (!dd_make_datastore_t(env, &datastore, jdatastore)) {
+ /* exception thrown */
+ return;
+ }
+
+ /* Retrieve the net argument */
+ if (!dd_jstring_to_UTF(env, jnet, &net)) {
+ /* exception thrown */
+ dd_free_datastore_t(&datastore);
+ return;
+ }
+
+ dnrec = create_dnrec(env, jrec);
+ if (dnrec == NULL) {
+ /* exception thrown */
+ dd_free_datastore_t(&datastore);
+ free(net);
+ return;
+ }
+
+ rcode = open_dd(&handle, &datastore, DSVC_DHCPNETWORK,
+ net, DSVC_WRITE);
+
+ dd_free_datastore_t(&datastore);
+ if (rcode != DSVC_SUCCESS) {
+ throw_open_dd_exception(env, rcode, net);
+ free(net);
+ free_dnrec(dnrec);
+ return;
+ }
+ free(net);
+
+ /* Now add the record */
+ rcode = add_dd_entry(handle, dnrec);
+
+ (void) close_dd(&handle);
+ if (rcode != DSVC_SUCCESS) {
+ tmpaddr.s_addr = htonl(dnrec->dn_cip.s_addr);
+ (void) strcpy(ascii_cip, inet_ntoa(tmpaddr));
+ throw_add_dd_entry_exception(env, rcode, ascii_cip);
+ }
+
+ free_dnrec(dnrec);
+
+}
+
+/*
+ * Modify a client record. Supply both old and new record and table in
+ * which they're to be modified.
+ */
+/*ARGSUSED*/
+JNIEXPORT void JNICALL
+Java_com_sun_dhcpmgr_bridge_Bridge_modifyDhcpClientRecord(
+ JNIEnv *env,
+ jobject obj,
+ jobject joldrec,
+ jobject jnewrec,
+ jstring jnet,
+ jobject jdatastore)
+{
+ dsvc_handle_t handle;
+ dsvc_datastore_t datastore;
+ dn_rec_t *dnoldrec;
+ dn_rec_t *dnnewrec;
+
+ struct in_addr tmpaddr;
+ char old_ascii_cip[IPADDR_MAX_CHAR + 1];
+ char new_ascii_cip[IPADDR_MAX_CHAR + 1];
+
+ char *net;
+ int rcode;
+
+ /* Create a dsvc_datastore_t using args and DHCP config settings */
+ if (!dd_make_datastore_t(env, &datastore, jdatastore)) {
+ /* exception thrown */
+ return;
+ }
+
+ /* Retrieve the net argument */
+ if (!dd_jstring_to_UTF(env, jnet, &net)) {
+ /* exception thrown */
+ dd_free_datastore_t(&datastore);
+ return;
+ }
+
+ dnoldrec = create_dnrec(env, joldrec);
+ if (dnoldrec == NULL) {
+ /* exception thrown */
+ dd_free_datastore_t(&datastore);
+ free(net);
+ return;
+ }
+
+ dnnewrec = create_dnrec(env, jnewrec);
+ if (dnnewrec == NULL) {
+ /* exception thrown */
+ dd_free_datastore_t(&datastore);
+ free(net);
+ free_dnrec(dnoldrec);
+ return;
+ }
+
+ rcode = open_dd(&handle, &datastore, DSVC_DHCPNETWORK,
+ net, DSVC_WRITE);
+
+ dd_free_datastore_t(&datastore);
+ if (rcode != DSVC_SUCCESS) {
+ throw_open_dd_exception(env, rcode, net);
+ free(net);
+ free_dnrec(dnoldrec);
+ free_dnrec(dnnewrec);
+ return;
+ }
+ free(net);
+
+ /* Modify the record */
+ rcode = modify_dd_entry(handle, dnoldrec, dnnewrec);
+
+ (void) close_dd(&handle);
+ if (rcode != DSVC_SUCCESS) {
+ tmpaddr.s_addr = htonl(dnoldrec->dn_cip.s_addr);
+ (void) strcpy(old_ascii_cip, inet_ntoa(tmpaddr));
+ tmpaddr.s_addr = htonl(dnnewrec->dn_cip.s_addr);
+ (void) strcpy(new_ascii_cip, inet_ntoa(tmpaddr));
+ throw_modify_dd_entry_exception(env, rcode, old_ascii_cip,
+ new_ascii_cip);
+ }
+
+ free_dnrec(dnnewrec);
+ free_dnrec(dnoldrec);
+}
+
+/*
+ * Delete a client record
+ */
+/*ARGSUSED*/
+JNIEXPORT void JNICALL
+Java_com_sun_dhcpmgr_bridge_Bridge_deleteDhcpClientRecord(
+ JNIEnv *env,
+ jobject obj,
+ jobject jrec,
+ jstring jnet,
+ jobject jdatastore)
+{
+ dsvc_handle_t handle;
+ dsvc_datastore_t datastore;
+ dn_rec_t *dnrec;
+
+ struct in_addr tmpaddr;
+ char ascii_cip[IPADDR_MAX_CHAR + 1];
+
+ char *net;
+ int rcode;
+
+ /* Create a dsvc_datastore_t using args and DHCP config settings */
+ if (!dd_make_datastore_t(env, &datastore, jdatastore)) {
+ /* exception thrown */
+ return;
+ }
+
+ /* Retrieve the net argument */
+ if (!dd_jstring_to_UTF(env, jnet, &net)) {
+ /* exception thrown */
+ dd_free_datastore_t(&datastore);
+ return;
+ }
+
+ dnrec = create_dnrec(env, jrec);
+ if (dnrec == NULL) {
+ /* exception thrown */
+ dd_free_datastore_t(&datastore);
+ free(net);
+ return;
+ }
+
+ rcode = open_dd(&handle, &datastore, DSVC_DHCPNETWORK,
+ net, DSVC_WRITE);
+
+ dd_free_datastore_t(&datastore);
+ if (rcode != DSVC_SUCCESS) {
+ throw_open_dd_exception(env, rcode, net);
+ free(net);
+ free_dnrec(dnrec);
+ return;
+ }
+ free(net);
+
+ /* Delete the record */
+ rcode = delete_dd_entry(handle, dnrec);
+
+ (void) close_dd(&handle);
+ if (rcode != DSVC_SUCCESS) {
+ tmpaddr.s_addr = htonl(dnrec->dn_cip.s_addr);
+ (void) strcpy(ascii_cip, inet_ntoa(tmpaddr));
+ throw_delete_dd_entry_exception(env, rcode, ascii_cip);
+ }
+
+ free_dnrec(dnrec);
+}
+
+/*
+ * Retrieve a client record
+ */
+/*ARGSUSED*/
+JNIEXPORT jobject JNICALL
+Java_com_sun_dhcpmgr_bridge_Bridge_getDhcpClientRecord(
+ JNIEnv *env,
+ jobject obj,
+ jobject jrec,
+ jstring jnet,
+ jobject jdatastore)
+{
+ jclass dcr_class;
+ jmethodID dcr_getcip;
+ jobject dhcpClientRecord = NULL;
+ jstring jaddr;
+
+ dsvc_handle_t handle;
+ dsvc_datastore_t datastore;
+
+ char *net;
+ char *addr;
+ int rcode;
+
+ dn_rec_t record;
+ dn_rec_list_t *recordList;
+ uint32_t query;
+ uint32_t count = 0;
+
+ /* Find the class and method we need */
+ dcr_class = find_class(env, DCR_CLASS);
+ if (dcr_class == NULL) {
+ /* exception thrown */
+ return (NULL);
+ }
+
+ /* Locate the method id we need */
+ dcr_getcip = get_methodID(env, dcr_class, DCR_GETCIP);
+ if (dcr_getcip == NULL) {
+ /* exception thrown */
+ return (NULL);
+ }
+
+ /* Get the address from the record */
+ jaddr = (*env)->CallObjectMethod(env, jrec, dcr_getcip);
+ if (jaddr == NULL) {
+ /* exception thrown */
+ return (NULL);
+ }
+
+ /* Create a dsvc_datastore_t using args and DHCP config settings */
+ if (!dd_make_datastore_t(env, &datastore, jdatastore)) {
+ /* exception thrown */
+ return (NULL);
+ }
+
+ /* Retrieve the net argument */
+ if (!dd_jstring_to_UTF(env, jnet, &net)) {
+ /* exception thrown */
+ dd_free_datastore_t(&datastore);
+ return (NULL);
+ }
+
+ /* Convert the address to a native string */
+ if (!dd_jstring_to_UTF(env, jaddr, &addr)) {
+ /* exception thrown */
+ throw_memory_exception(env);
+ dd_free_datastore_t(&datastore);
+ free(net);
+ return (NULL);
+ }
+
+ rcode = open_dd(&handle, &datastore, DSVC_DHCPNETWORK, net, DSVC_READ);
+
+ dd_free_datastore_t(&datastore);
+ if (rcode != DSVC_SUCCESS) {
+ throw_open_dd_exception(env, rcode, net);
+ free(addr);
+ free(net);
+ return (NULL);
+ }
+ free(net);
+
+ /* Get the record */
+ DSVC_QINIT(query);
+ DSVC_QEQ(query, DN_QCIP);
+ record.dn_cip.s_addr = ntohl(inet_addr(addr));
+
+ rcode = lookup_dd(handle, B_FALSE, query, 1, &record,
+ (void **)&recordList, &count);
+
+ (void) close_dd(&handle);
+ if (rcode == DSVC_SUCCESS) {
+ if (count == 1) {
+ dhcpClientRecord = create_DhcpClientRecord(env,
+ recordList->dnl_rec);
+ free_dnrec_list(recordList);
+ } else {
+ throw_noent_exception(env, addr);
+ }
+ } else {
+ throw_libdhcpsvc_exception(env, rcode);
+ }
+
+ free(addr);
+
+
+ return (dhcpClientRecord);
+}
+
+/*
+ * Create a network table.
+ */
+/*ARGSUSED*/
+JNIEXPORT void JNICALL
+Java_com_sun_dhcpmgr_bridge_Bridge_createDhcpNetwork(
+ JNIEnv *env,
+ jobject obj,
+ jstring jnet,
+ jobject jdatastore)
+{
+ dsvc_handle_t handle;
+ dsvc_datastore_t datastore;
+ char *net;
+ int rcode;
+
+ /* Create a dsvc_datastore_t using args and DHCP config settings */
+ if (!dd_make_datastore_t(env, &datastore, jdatastore)) {
+ /* exception thrown */
+ return;
+ }
+
+ /* Retrieve the net argument */
+ if (!dd_jstring_to_UTF(env, jnet, &net)) {
+ /* exception thrown */
+ dd_free_datastore_t(&datastore);
+ return;
+ }
+
+ rcode = open_dd(&handle, &datastore, DSVC_DHCPNETWORK, net,
+ DSVC_CREATE | DSVC_READ | DSVC_WRITE);
+
+ dd_free_datastore_t(&datastore);
+
+ /*
+ * If open was successful, then close. Otherwise, if unsuccessful
+ * opening table, then map error to exception.
+ */
+ if (rcode == DSVC_SUCCESS) {
+ (void) close_dd(&handle);
+ } else {
+ throw_open_dd_exception(env, rcode, net);
+ }
+
+ free(net);
+}
+
+/*
+ * Delete a network table.
+ */
+/*ARGSUSED*/
+JNIEXPORT void JNICALL
+Java_com_sun_dhcpmgr_bridge_Bridge_deleteDhcpNetwork(
+ JNIEnv *env,
+ jobject obj,
+ jstring jnet,
+ jobject jdatastore)
+{
+ dsvc_datastore_t datastore;
+ char *net;
+ int rcode;
+
+ /* Create a dsvc_datastore_t using args and DHCP config settings */
+ if (!dd_make_datastore_t(env, &datastore, jdatastore)) {
+ /* exception thrown */
+ return;
+ }
+
+ /* Retrieve the net argument */
+ if (!dd_jstring_to_UTF(env, jnet, &net)) {
+ /* exception thrown */
+ dd_free_datastore_t(&datastore);
+ return;
+ }
+
+ rcode = remove_dd(&datastore, DSVC_DHCPNETWORK, net);
+
+ dd_free_datastore_t(&datastore);
+ if (rcode != DSVC_SUCCESS) {
+ throw_remove_dd_exception(env, rcode, net);
+ }
+
+ free(net);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/optiondefs.c b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/optiondefs.c
new file mode 100644
index 0000000000..a8e18908c1
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/optiondefs.c
@@ -0,0 +1,236 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <libintl.h>
+#include <arpa/inet.h>
+#include <jni.h>
+#include <com_sun_dhcpmgr_bridge_Bridge.h>
+
+#include "exception.h"
+#include "dd_opt.h"
+#include "class_cache.h"
+
+/*
+ * Retrieve default value for an option with a string value. Returns a
+ * single String.
+ */
+/*ARGSUSED*/
+JNIEXPORT jstring JNICALL
+Java_com_sun_dhcpmgr_bridge_Bridge_getStringOption(
+ JNIEnv *env,
+ jobject obj,
+ jshort code,
+ jstring jarg)
+{
+ jstring jstr;
+ struct dhcp_option *opt;
+ ushort_t scode = (ushort_t)code;
+ const char *arg;
+
+ /* Get the option whose default value we want to generate. */
+ arg = (*env)->GetStringUTFChars(env, jarg, NULL);
+ if (arg == NULL) {
+ /* exception thrown */
+ return (NULL);
+ }
+
+ /* Get the option data */
+ opt = dd_getopt(scode, arg, NULL);
+ (*env)->ReleaseStringUTFChars(env, jarg, arg);
+
+ if (opt == NULL) {
+ throw_memory_exception(env);
+ return (NULL);
+ }
+
+ if (opt->error_code != 0) {
+ throw_bridge_exception(env, opt->u.msg);
+ dd_freeopt(opt);
+ return (NULL);
+ }
+
+ /* Set the return value */
+ jstr = (*env)->NewStringUTF(env, opt->u.ret.data.strings[0]);
+ dd_freeopt(opt);
+ return (jstr);
+}
+
+/*
+ * Get the default value for an option whose value is one or more IP
+ * addresses. Returns an array of IPAddress objects.
+ */
+/*ARGSUSED*/
+JNIEXPORT jobjectArray JNICALL
+Java_com_sun_dhcpmgr_bridge_Bridge_getIPOption(
+ JNIEnv *env,
+ jobject obj,
+ jshort code,
+ jstring jarg)
+{
+ jclass ip_class;
+ jmethodID ip_cons;
+ jobjectArray jlist = NULL;
+ jobject jaddr;
+ jstring jstr;
+ struct dhcp_option *opt;
+ ushort_t scode = (ushort_t)code;
+ int i;
+ const char *arg;
+
+ /* Get classes and methods we need */
+ ip_class = find_class(env, IP_CLASS);
+ if (ip_class == NULL) {
+ /* exception thrown */
+ return (NULL);
+ }
+ ip_cons = get_methodID(env, ip_class, IP_CONS);
+ if (ip_cons == NULL) {
+ /* exception thrown */
+ return (NULL);
+ }
+
+ /* Retrieve option to generate value for */
+ arg = (*env)->GetStringUTFChars(env, jarg, NULL);
+ if (arg == NULL) {
+ /* exception thrown */
+ return (NULL);
+ }
+
+ /* Go get the default value */
+ opt = dd_getopt(scode, arg, NULL);
+ (*env)->ReleaseStringUTFChars(env, jarg, arg);
+
+ if (opt == NULL) {
+ throw_memory_exception(env);
+ return (NULL);
+ }
+
+ if (opt->error_code != 0) {
+ throw_bridge_exception(env, opt->u.msg);
+ dd_freeopt(opt);
+ return (NULL);
+ }
+
+ /* Construct the array */
+ jlist = (*env)->NewObjectArray(env, opt->u.ret.count, ip_class, NULL);
+ if (jlist == NULL) {
+ /* exception thrown */
+ dd_freeopt(opt);
+ return (NULL);
+ }
+
+ /* For each address, create an object and add it to the array */
+ for (i = 0; i < opt->u.ret.count; ++i) {
+ jstr = (*env)->NewStringUTF(env,
+ inet_ntoa(*opt->u.ret.data.addrs[i]));
+ if (jstr == NULL) {
+ /* exception thrown */
+ break;
+ }
+ jaddr = (*env)->NewObject(env, ip_class, ip_cons, jstr);
+ if (jaddr == NULL) {
+ /* exception thrown */
+ break;
+ }
+
+ (*env)->SetObjectArrayElement(env, jlist, i, jaddr);
+ if ((*env)->ExceptionOccurred(env) != NULL) {
+ break;
+ }
+ }
+
+ dd_freeopt(opt);
+ return (jlist);
+}
+
+/*
+ * Generate the default value for an option whose value is a list of numbers.
+ * Returns an array of longs.
+ */
+/*ARGSUSED*/
+JNIEXPORT jlongArray JNICALL
+Java_com_sun_dhcpmgr_bridge_Bridge_getNumberOption(
+ JNIEnv *env,
+ jobject obj,
+ jshort code,
+ jstring jarg)
+{
+ jlongArray list;
+ struct dhcp_option *opt;
+ const char *arg;
+ ushort_t scode = (ushort_t)code;
+ jlong *listel;
+ int i;
+
+ /* Get option to retrieve */
+ arg = (*env)->GetStringUTFChars(env, jarg, NULL);
+ if (arg == NULL) {
+ /* exception thrown */
+ return (NULL);
+ }
+
+ opt = dd_getopt(scode, arg, NULL);
+ (*env)->ReleaseStringUTFChars(env, jarg, arg);
+
+ if (opt == NULL) {
+ throw_memory_exception(env);
+ return (NULL);
+ }
+
+ if (opt->error_code != 0) {
+ throw_bridge_exception(env, opt->u.msg);
+ dd_freeopt(opt);
+ return (NULL);
+ }
+
+ /* Allocate return array */
+ list = (*env)->NewLongArray(env, opt->u.ret.count);
+ if (list == NULL) {
+ /* exception thrown */
+ dd_freeopt(opt);
+ return (NULL);
+ }
+
+ /* Get access to elements of return array, then copy data in */
+ listel = (*env)->GetLongArrayElements(env, list, NULL);
+ if (listel == NULL) {
+ /* exception thrown */
+ dd_freeopt(opt);
+ return (NULL);
+ }
+
+ for (i = 0; i < opt->u.ret.count; ++i) {
+ listel[i] = opt->u.ret.data.numbers[i];
+ }
+
+ /* Tell VM we're done so it can finish putting data back */
+ (*env)->ReleaseLongArrayElements(env, list, listel, 0);
+
+ dd_freeopt(opt);
+ return (list);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/service.c b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/service.c
new file mode 100644
index 0000000000..c71183d129
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/lib/service.c
@@ -0,0 +1,716 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <wordexp.h>
+#include <string.h>
+#include <malloc.h>
+#include <sys/signal.h>
+#include <libintl.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <dhcp_svc_private.h>
+#include <dhcp_svc_confkey.h>
+#include <jni.h>
+#include <libscf.h>
+#include <com_sun_dhcpmgr_bridge_Bridge.h>
+
+#include "exception.h"
+#include "dd_misc.h"
+#include "class_cache.h"
+
+#define DHCP_SERVER_INST "svc:/network/dhcp-server:default"
+
+#define DHCPD_FNAME "in.dhcpd"
+#define CONFOPT_MODE 0644
+
+/*
+ * Gets called when the library is loaded.
+ */
+/*ARGSUSED*/
+JNIEXPORT jint JNICALL
+JNI_OnLoad(
+ JavaVM *jvm,
+ void *reserved)
+{
+ JNIEnv *env;
+
+ if ((*jvm)->GetEnv(jvm, (void**)&env, JNI_VERSION_1_2)) {
+ return (JNI_ERR);
+ }
+
+ init_class_cache();
+ return (JNI_VERSION_1_2);
+}
+
+/*
+ * Determine whether an upgrade of the datastore is necessary.
+ */
+/*ARGSUSED*/
+JNIEXPORT jboolean JNICALL
+Java_com_sun_dhcpmgr_bridge_Bridge_isVersionCurrent(
+ JNIEnv *env,
+ jobject obj)
+{
+ dsvc_datastore_t datastore;
+ int cfgVersion;
+ int curVersion;
+ int rcode;
+ jboolean result = JNI_FALSE;
+
+ /* Get the data store configuration */
+ if (dd_get_conf_datastore_t(env, &datastore)) {
+ cfgVersion = datastore.d_conver;
+
+ datastore.d_conver = DSVC_CUR_CONVER;
+ free(datastore.d_location);
+ datastore.d_location = NULL;
+ rcode = status_dd(&datastore);
+ if (rcode != DSVC_SUCCESS) {
+ throw_libdhcpsvc_exception(env, rcode);
+ } else {
+ curVersion = datastore.d_conver;
+
+ if (curVersion == cfgVersion) {
+ result = JNI_TRUE;
+ }
+ }
+ dd_free_datastore_t(&datastore);
+ }
+
+ return (result);
+}
+
+/*
+ * Retrieve the data store object for the specified resource.
+ */
+/*ARGSUSED*/
+JNIEXPORT jobject JNICALL
+Java_com_sun_dhcpmgr_bridge_Bridge_getDataStore(
+ JNIEnv *env,
+ jobject obj,
+ jstring jresource)
+{
+ jclass ds_class;
+ jmethodID ds_cons;
+ jobject dsObject;
+ jboolean avail;
+ jint version;
+ dsvc_datastore_t datastore;
+ char *resource;
+
+ /* Make sure we have the classes & methods we need */
+ ds_class = find_class(env, DS_CLASS);
+ if (ds_class == NULL) {
+ /* exception thrown */
+ return (NULL);
+ }
+ ds_cons = get_methodID(env, ds_class, DS_CONS);
+ if (ds_cons == NULL) {
+ /* exception thrown */
+ return (NULL);
+ }
+
+ /* Retrieve the resource argument */
+ if (!dd_jstring_to_UTF(env, jresource, &resource)) {
+ /* exception thrown */
+ return (NULL);
+ }
+
+ datastore.d_conver = DSVC_CUR_CONVER;
+ datastore.d_resource = resource;
+ datastore.d_location = NULL;
+ avail = JNI_FALSE;
+ if (status_dd(&datastore) == DSVC_SUCCESS) {
+ avail = JNI_TRUE;
+ version = datastore.d_conver;
+ }
+
+ dsObject = (*env)->NewObject(env, ds_class, ds_cons,
+ jresource, version, avail);
+
+ free(resource);
+ return (dsObject);
+}
+
+/*
+ * Retrieve the list of data stores available for DHCP. Returns an array of
+ * DHCP datastore names.
+ */
+/*ARGSUSED*/
+JNIEXPORT jobjectArray JNICALL
+Java_com_sun_dhcpmgr_bridge_Bridge_getDataStores(
+ JNIEnv *env,
+ jobject obj)
+{
+ jclass ds_class;
+ jmethodID ds_cons;
+ jobjectArray jlist = NULL;
+ jobject jobj;
+ jstring jstr;
+ jboolean avail;
+ jint version;
+ char **list;
+ dsvc_datastore_t datastore;
+ int i, len;
+
+ /* Make sure we have the classes & methods we need */
+ ds_class = find_class(env, DS_CLASS);
+ if (ds_class == NULL) {
+ /* exception thrown */
+ return (NULL);
+ }
+ ds_cons = get_methodID(env, ds_class, DS_CONS);
+ if (ds_cons == NULL) {
+ /* exception thrown */
+ return (NULL);
+ }
+
+ /* Get the list */
+ list = dd_data_stores(env);
+ if ((*env)->ExceptionOccurred(env) != NULL) {
+ return (NULL);
+ }
+
+ /* Compute the length of the array, store in len */
+ ARRAY_LENGTH(list, len);
+
+ /* Construct the array */
+ jlist = (*env)->NewObjectArray(env, len, ds_class, NULL);
+ if (jlist == NULL) {
+ /* exception thrown */
+ dd_free_data_stores(list);
+ return (NULL);
+ }
+
+ /* For each store, create an object and add it to the array */
+ for (i = 0; i < len; ++i) {
+
+ jstr = (*env)->NewStringUTF(env, list[i]);
+ if (jstr == NULL) {
+ /* exception thrown */
+ break;
+ }
+
+ datastore.d_conver = DSVC_CUR_CONVER;
+ datastore.d_resource = list[i];
+ datastore.d_location = NULL;
+ avail = JNI_FALSE;
+ if (status_dd(&datastore) == DSVC_SUCCESS) {
+ avail = JNI_TRUE;
+ version = datastore.d_conver;
+ }
+
+ jobj = (*env)->NewObject(env, ds_class, ds_cons,
+ jstr, version, avail);
+ if (jobj == NULL) {
+ /* exception thrown */
+ break;
+ }
+
+ (*env)->SetObjectArrayElement(env, jlist, i, jobj);
+ if ((*env)->ExceptionOccurred(env) != NULL) {
+ break;
+ }
+ }
+
+ dd_free_data_stores(list);
+ return (jlist);
+}
+
+/*
+ * Read the config file for DHCP and return its contents as a DhcpdOptions
+ * object.
+ */
+/*ARGSUSED*/
+JNIEXPORT jobject JNICALL
+Java_com_sun_dhcpmgr_bridge_Bridge_readDefaults(
+ JNIEnv *env,
+ jobject obj)
+{
+ jclass cfg_class;
+ jmethodID cfg_cons;
+ jmethodID cfg_set;
+ jobject cfgobj;
+ dhcp_confopt_t *cfgs, *tcfgs;
+
+ /* Make sure we have the classes & methods we need */
+ cfg_class = find_class(env, CFG_CLASS);
+ if (cfg_class == NULL) {
+ /* exception thrown */
+ return (NULL);
+ }
+ cfg_cons = get_methodID(env, cfg_class, CFG_CONS);
+ if (cfg_cons == NULL) {
+ /* exception thrown */
+ return (NULL);
+ }
+ cfg_set = get_methodID(env, cfg_class, CFG_SET);
+ if (cfg_set == NULL) {
+ /* exception thrown */
+ return (NULL);
+ }
+
+ /* Get the data */
+ if (read_dsvc_conf(&cfgs) != 0) {
+ throw_bridge_exception(env, strerror(errno));
+ } else {
+ /* Construct returned options object */
+ cfgobj = (*env)->NewObject(env, cfg_class, cfg_cons);
+ if (cfgobj == NULL) {
+ /* exception thrown */
+ free_dsvc_conf(cfgs);
+ return (NULL);
+ }
+
+ /* Load the option settings into the options object */
+ tcfgs = cfgs;
+ for (;;) {
+ if (cfgs->co_type == DHCP_COMMENT) {
+ (*env)->CallVoidMethod(env, cfgobj, cfg_set,
+ (*env)->NewStringUTF(env, cfgs->co_key),
+ (*env)->NewStringUTF(env, ""), JNI_TRUE);
+ } else {
+ if (cfgs->co_key == NULL) {
+ break;
+ }
+ (*env)->CallVoidMethod(env, cfgobj, cfg_set,
+ (*env)->NewStringUTF(env, cfgs->co_key),
+ (*env)->NewStringUTF(env, cfgs->co_value),
+ JNI_FALSE);
+ }
+ if ((*env)->ExceptionOccurred(env) != NULL) {
+ free_dsvc_conf(tcfgs);
+ return (NULL);
+ }
+ ++cfgs;
+ }
+ free_dsvc_conf(tcfgs);
+ }
+ return (cfgobj);
+}
+
+/*
+ * Write the DHCP config file. Takes a DhcpdOptions object as input
+ */
+/*ARGSUSED*/
+JNIEXPORT void JNICALL
+Java_com_sun_dhcpmgr_bridge_Bridge_writeDefaults(
+ JNIEnv *env,
+ jobject obj,
+ jobject jcfgs)
+{
+ jclass cfg_class;
+ jmethodID cfg_getall;
+ jclass res_class;
+ jmethodID res_getkey;
+ jmethodID res_getval;
+ jmethodID res_iscom;
+ jobjectArray resArray;
+ jsize reslen;
+ jobject jobj, resobj;
+ dhcp_confopt_t *cfgs;
+ int i;
+ jboolean comment;
+ const char *tmpstr;
+
+ /* Make sure we can get at the classes we need */
+ cfg_class = find_class(env, CFG_CLASS);
+ if (cfg_class == NULL) {
+ /* exception thrown */
+ return;
+ }
+ cfg_getall = get_methodID(env, cfg_class, CFG_GETALL);
+ if (cfg_getall == NULL) {
+ /* exception thrown */
+ return;
+ }
+ res_class = find_class(env, RES_CLASS);
+ if (res_class == NULL) {
+ /* exception thrown */
+ return;
+ }
+ res_getkey = get_methodID(env, res_class, RES_GETKEY);
+ res_getval = get_methodID(env, res_class, RES_GETVAL);
+ res_iscom = get_methodID(env, res_class, RES_ISCOM);
+ if (res_getkey == NULL || res_getval == NULL || res_iscom == NULL) {
+ /* exception thrown */
+ return;
+ }
+
+ /* Get the resource array from the config object */
+ resArray = (*env)->CallObjectMethod(env, jcfgs, cfg_getall);
+ if ((*env)->ExceptionOccurred(env) != NULL) {
+ return;
+ }
+ reslen = (*env)->GetArrayLength(env, resArray);
+ /* Allocate array to convert into; extra zero'd item to signal end */
+ cfgs = calloc(reslen+1, sizeof (dhcp_confopt_t));
+ if (cfgs == NULL) {
+ throw_memory_exception(env);
+ return;
+ }
+
+ /* Now copy data into local array */
+ for (i = 0; i < reslen; ++i) {
+ jobj = (*env)->GetObjectArrayElement(env, resArray, i);
+ if (jobj == NULL) {
+ /* exception thrown */
+ free_dsvc_conf(cfgs);
+ return;
+ }
+ /* Set record type */
+ comment = (*env)->CallBooleanMethod(env, jobj, res_iscom);
+ if ((*env)->ExceptionOccurred(env) != NULL) {
+ return;
+ }
+ if (comment == JNI_TRUE) {
+ cfgs[i].co_type = DHCP_COMMENT;
+ } else {
+ cfgs[i].co_type = DHCP_KEY;
+ }
+ /*
+ * Get the key from the object, convert to a char *,
+ * and then duplicate into the cfgs array so that
+ * free_dsvc_conf can be used correctly.
+ * Do the same thing for the value.
+ */
+ resobj = (*env)->CallObjectMethod(env, jobj, res_getkey);
+ tmpstr = (*env)->GetStringUTFChars(env, resobj, NULL);
+ if (tmpstr == NULL) {
+ /* exception thrown */
+ free_dsvc_conf(cfgs);
+ throw_bridge_exception(env,
+ gettext("Error converting key"));
+ return;
+ }
+ cfgs[i].co_key = strdup(tmpstr);
+ (*env)->ReleaseStringUTFChars(env, resobj, tmpstr);
+ if (cfgs[i].co_key == NULL) {
+ /* Out of memory, fail */
+ free_dsvc_conf(cfgs);
+ throw_memory_exception(env);
+ return;
+ }
+ resobj = (*env)->CallObjectMethod(env, jobj, res_getval);
+ tmpstr = (*env)->GetStringUTFChars(env, resobj, NULL);
+ if (tmpstr == NULL) {
+ free_dsvc_conf(cfgs);
+ throw_bridge_exception(env,
+ gettext("Error converting value"));
+ return;
+ }
+ cfgs[i].co_value = strdup(tmpstr);
+ (*env)->ReleaseStringUTFChars(env, resobj, tmpstr);
+ if (cfgs[i].co_value == NULL) {
+ /* Out of memory, fail */
+ free_dsvc_conf(cfgs);
+ throw_memory_exception(env);
+ return;
+ }
+ }
+
+ /* Now write the new data */
+ if (write_dsvc_conf(cfgs, CONFOPT_MODE) != 0) {
+ throw_bridge_exception(env, strerror(errno));
+ }
+ free_dsvc_conf(cfgs);
+}
+
+/*
+ * Remove the DHCP config file
+ */
+/*ARGSUSED*/
+JNIEXPORT void JNICALL
+Java_com_sun_dhcpmgr_bridge_Bridge_removeDefaults(
+ JNIEnv *env,
+ jobject obj)
+{
+ if (delete_dsvc_conf() != 0) {
+ throw_bridge_exception(env, strerror(errno));
+ }
+}
+
+/*
+ * Start up the daemon.
+ */
+/*ARGSUSED*/
+JNIEXPORT void JNICALL
+Java_com_sun_dhcpmgr_bridge_Bridge_startup(
+ JNIEnv *env,
+ jobject obj)
+{
+ char *s;
+ int ret;
+
+ /*
+ * We first get the current state of the server according to
+ * svc.startd; if it's "disabled", we can just enable it.
+ * In any other case, we want to send a refresh so that
+ * dependencies are re-evaluated, which will be the case if the
+ * service was marked enabled by the profile, yet the
+ * config file didn't exist to allow it to run.
+ */
+ if ((s = smf_get_state(DHCP_SERVER_INST)) != NULL) {
+ if (strcmp(SCF_STATE_STRING_DISABLED, s) == 0)
+ ret = smf_enable_instance(DHCP_SERVER_INST, 0);
+ else
+ ret = smf_refresh_instance(DHCP_SERVER_INST);
+ free(s);
+ if (ret == 0)
+ return;
+ }
+
+ /* Something wasn't right, return exception with error from smf */
+ throw_bridge_exception(env, scf_strerror(scf_error()));
+}
+
+/*
+ * Shut down the daemon.
+ */
+/*ARGSUSED*/
+JNIEXPORT void JNICALL
+Java_com_sun_dhcpmgr_bridge_Bridge_shutdown(
+ JNIEnv *env,
+ jobject obj)
+{
+ if (smf_disable_instance(DHCP_SERVER_INST, 0) != 0) {
+ throw_bridge_exception(env, scf_strerror(scf_error()));
+ }
+}
+
+/*
+ * Tell the daemon to re-read the dhcptab.
+ */
+/*ARGSUSED*/
+JNIEXPORT void JNICALL
+Java_com_sun_dhcpmgr_bridge_Bridge_reload(
+ JNIEnv *env,
+ jobject obj)
+{
+ int err;
+
+ if ((err = dd_signal(DHCPD_FNAME, SIGHUP)) != 0) {
+ if (err == -1) {
+ /* dd_signal couldn't find in.dhcpd running */
+ throw_not_running_exception(env);
+ } else {
+ throw_bridge_exception(env, strerror(err));
+ }
+ }
+}
+
+/*
+ * Make the resource location.
+ */
+/*ARGSUSED*/
+JNIEXPORT void JNICALL
+Java_com_sun_dhcpmgr_bridge_Bridge_makeLocation(
+ JNIEnv *env,
+ jobject obj,
+ jobject jdatastore)
+{
+ dsvc_datastore_t datastore;
+ int rcode;
+
+ /* Create a dsvc_datastore_t using args and DHCP config file */
+ if (!dd_make_datastore_t(env, &datastore, jdatastore)) {
+ /* exception thrown */
+ return;
+ }
+
+ /* If the location does not already exist, go create it. */
+ if (status_dd(&datastore) != DSVC_SUCCESS) {
+ rcode = mklocation_dd(&datastore);
+ if (rcode != DSVC_SUCCESS) {
+ throw_libdhcpsvc_exception(env, rcode);
+ }
+ }
+
+ dd_free_datastore_t(&datastore);
+}
+
+/*
+ * Check if the server is running; returns true if so, false if not.
+ */
+/*ARGSUSED*/
+JNIEXPORT jboolean JNICALL
+Java_com_sun_dhcpmgr_bridge_Bridge_isServerRunning(
+ JNIEnv *env,
+ jobject obj)
+{
+ if (dd_getpid(DAEMON_FNAME) != (pid_t)-1) {
+ return (JNI_TRUE);
+ } else {
+ return (JNI_FALSE);
+ }
+}
+
+/*
+ * Retrieve the list of interfaces on the system which are candidates for
+ * use by the DHCP daemon. Returns an array of IPInterface objects.
+ */
+/*ARGSUSED*/
+JNIEXPORT jobjectArray JNICALL
+Java_com_sun_dhcpmgr_bridge_Bridge_getInterfaces(
+ JNIEnv *env,
+ jobject obj)
+{
+ jclass ipif_class;
+ jmethodID ipif_cons;
+ jobjectArray jlist = NULL;
+ jobject jobj;
+ jsize len;
+ struct ip_interface **list;
+ int i;
+
+ /* Locate the class and constructor we need */
+ ipif_class = find_class(env, IPIF_CLASS);
+ if (ipif_class == NULL) {
+ /* exception thrown */
+ return (NULL);
+ }
+ ipif_cons = get_methodID(env, ipif_class, IPIF_CONS);
+ if (ipif_cons == NULL) {
+ return (NULL);
+ }
+
+ /* Retrieve interface list */
+ list = dd_get_interfaces();
+ if (list == NULL) {
+ throw_bridge_exception(env,
+ gettext("Error in dd_get_interfaces"));
+ return (NULL);
+ }
+ /* Compute length of list */
+ ARRAY_LENGTH(list, len);
+
+ /* Construct the array */
+ jlist = (*env)->NewObjectArray(env, len, ipif_class, NULL);
+ if (jlist == NULL) {
+ /* exception thrown */
+ for (i = 0; i < len; i++) {
+ free(list[i]);
+ }
+ free(list);
+ return (NULL);
+ }
+
+ /* For each interface, construct an object and add to the array */
+ for (i = 0; i < len; ++i) {
+ jobj = (*env)->NewObject(env, ipif_class, ipif_cons,
+ (*env)->NewStringUTF(env, list[i]->name),
+ (*env)->NewStringUTF(env, inet_ntoa(list[i]->addr)),
+ (*env)->NewStringUTF(env, inet_ntoa(list[i]->mask)));
+
+ if (jobj == NULL) {
+ /* exception thrown */
+ break;
+ }
+
+ (*env)->SetObjectArrayElement(env, jlist, i, jobj);
+ if ((*env)->ExceptionOccurred(env) != NULL) {
+ break;
+ }
+ }
+
+ for (i = 0; i < len; i++) {
+ free(list[i]);
+ }
+ free(list);
+
+ return (jlist);
+}
+
+/*
+ * Parse a line into arguments.
+ */
+/*ARGSUSED*/
+JNIEXPORT jobjectArray JNICALL
+Java_com_sun_dhcpmgr_bridge_Bridge_getArguments(
+ JNIEnv *env,
+ jobject obj,
+ jstring jline)
+{
+ wordexp_t exp;
+ int flags = WRDE_NOCMD;
+ char *line;
+ jclass str_class;
+ jobjectArray jlist = NULL;
+ jstring jarg;
+ int i, ret;
+
+ /* Go ahead and get the class for a String class */
+ str_class = (*env)->GetObjectClass(env, jline);
+ if (str_class == NULL) {
+ /* exception thrown */
+ return (NULL);
+ }
+
+ /* Retrieve the line argument */
+ if (jline != NULL &&
+ (line = dd_jstring_to_native(env, jline)) == NULL) {
+ /* exception thrown */
+ return (NULL);
+ }
+
+ /* Retrieve argument list */
+ ret = wordexp(line, &exp, flags);
+ free(line);
+ if (ret != 0) {
+ throw_wordexp_exception(env, ret);
+ /* Free memory for the one error case where it's allocated */
+ if (ret == WRDE_NOSPACE)
+ wordfree(&exp);
+ return (NULL);
+ }
+
+ /* Construct the array */
+ jlist = (*env)->NewObjectArray(env, exp.we_wordc, str_class, NULL);
+ if (jlist == NULL) {
+ /* exception thrown */
+ wordfree(&exp);
+ return (NULL);
+ }
+
+ /* For each argument, create an object and add it to the array */
+ for (i = 0; i < exp.we_wordc; i++) {
+ jarg = dd_native_to_jstring(env, exp.we_wordv[i]);
+ if (jarg == NULL) {
+ /* exception thrown */
+ break;
+ }
+
+ (*env)->SetObjectArrayElement(env, jlist, i, jarg);
+ if ((*env)->ExceptionOccurred(env) != NULL) {
+ break;
+ }
+ }
+
+ wordfree(&exp);
+ return (jlist);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/scripts/Makefile b/usr/src/cmd/cmd-inet/usr.sadm/scripts/Makefile
new file mode 100644
index 0000000000..87bb00f948
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/scripts/Makefile
@@ -0,0 +1,42 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+CLASS_ACTION_SCRIPTS = i.ipsecalgs r.ipsecalgs
+
+include ../../../Makefile.cmd
+
+.KEEP_STATE:
+
+INSTALL_DIR = $(ROOT)/usr/sadm/install
+CLASS_SCR_DIR = $(INSTALL_DIR)/scripts
+CLASS_SCR_FILES = $(CLASS_ACTION_SCRIPTS:%=$(CLASS_SCR_DIR)/%)
+
+$(CLASS_SCR_DIR)/%: %
+ $(INS.file)
+
+install: $(CLASS_SCR_FILES)
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/scripts/i.ipsecalgs b/usr/src/cmd/cmd-inet/usr.sadm/scripts/i.ipsecalgs
new file mode 100644
index 0000000000..5442fbca1d
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/scripts/i.ipsecalgs
@@ -0,0 +1,77 @@
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Class action script for ipsecalgs class files.
+#
+# This script appends the input file from the package to the
+# /etc/inet/ipsecalgs file.
+#
+
+pkg_start="# Start $PKGINST"
+pkg_end="# End $PKGINST"
+confile=/etc/inet/ipsecalgs
+tmpfile=/tmp/$$ipsecalgs
+error=no
+
+while read src dest
+do
+ [ "$src" = /dev/null ] && continue
+
+ if [ -f "$dest" ]
+ then
+ # If the package has been already installed, remove old entries
+ grep "$pkg_start" $dest > /dev/null
+ if [ $? -eq 0 ]
+ then
+ sed -e "/$pkg_start/,/$pkg_end/d" $dest > $tmpfile \
+ || error=yes
+ else
+ cp $dest $tmpfile || error=yes
+ fi
+
+ # Append the delimiters and entries of this package
+ echo "$pkg_start" >> $tmpfile || error=yes
+ cat $src >> $tmpfile || error=yes
+ echo "$pkg_end" >> $tmpfile || error=yes
+
+ # Install the updated config file and clean up the tmp file
+ if [ "$error" = no ]
+ then
+ mv $tmpfile $dest || error=yes
+ fi
+ rm -f $tmpfile
+ else
+ echo "$0: ERROR - the $confile file doesn't exist."
+ exit 2
+ fi
+done
+
+if [ "$error" = yes ]
+then
+ echo "$0: ERROR - failed to update the $confile file."
+ exit 2
+fi
+exit 0
diff --git a/usr/src/cmd/cmd-inet/usr.sadm/scripts/r.ipsecalgs b/usr/src/cmd/cmd-inet/usr.sadm/scripts/r.ipsecalgs
new file mode 100644
index 0000000000..70093d13fe
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sadm/scripts/r.ipsecalgs
@@ -0,0 +1,62 @@
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Removal class action script for ipsecalgs class files.
+#
+# This script removes entries belonging to the package from the
+# /etc/inet/ipsecalgs file.
+#
+
+pkg_start="# Start $PKGINST"
+pkg_end="# End $PKGINST"
+confile=/etc/inet/ipsecalgs
+tmpfile=/tmp/$$ipsecalgs
+error=no
+
+while read dest
+do
+ # Strip all entries belonging to this package
+ grep "$pkg_start" $dest > /dev/null
+ if [ $? -eq 0 ]
+ then
+ sed -e "/$pkg_start/,/$pkg_end/d" $dest > $tmpfile || error=yes
+ if [ "$error" = no ]
+ then
+ mv $tmpfile $dest || error=yes
+ fi
+ rm -f $tmpfile
+ else
+ echo "$0: WARNING - no entries to be removed from the $confile file"
+ exit 0
+ fi
+done
+
+if [ "$error" = yes ]
+then
+ echo "$0: ERROR - failed to update the $confile file."
+ exit 2
+fi
+exit 0
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/6to4relay.c b/usr/src/cmd/cmd-inet/usr.sbin/6to4relay.c
new file mode 100644
index 0000000000..f0547145b2
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/6to4relay.c
@@ -0,0 +1,425 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/socket.h>
+#include <sys/stream.h>
+#include <sys/param.h>
+
+#include <net/route.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <inet/tun.h>
+
+#include <locale.h>
+
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <string.h>
+#include <stropts.h>
+#include <fcntl.h>
+
+/*
+ * Converts an IPv4 address to a 6to4 /64 route. Address is of the form
+ * 2002:<V4ADDR>:<SUBNETID>::/64 where SUBNETID will always be 0 and V4ADDR
+ * equals the input IPv4 address. IN6_V4ADDR_TO_6TO4(v4, v6) creates an
+ * address of form 2002:<V4ADDR>:<SUBNETID>::<HOSTID>, where SUBNETID equals 0
+ * and HOSTID equals 1. For this route, we are not concerned about the
+ * HOSTID portion of the address, thus it can be set to 0.
+ *
+ * void V4ADDR_TO_6TO4_RT(const struct in_addr *v4, in6_addr_t *v6)
+ */
+#define V4ADDR_TO_6TO4_RT(v4, v6) \
+ (IN6_V4ADDR_TO_6TO4(v4, v6), (v6)->_S6_un._S6_u32[3] = 0)
+
+static void strioctl(int, void *, size_t);
+static void getkstatus(ipaddr_t *);
+static void printkstatus(void);
+static void modifyroute(unsigned int, in6_addr_t *);
+static void setkrraddr(ipaddr_t);
+static void printerror(char *);
+static void usage(void);
+
+/* booleans corresponding to command line flags */
+static boolean_t eflag = B_FALSE;
+static boolean_t dflag = B_FALSE;
+static boolean_t aflag = B_FALSE;
+
+static int fd = -1;
+
+/*
+ * srtioctl(cmd, buf, size)
+ *
+ * Passes the contents of 'buf' using the ioctl specified by 'cmd', by way of
+ * the I_STR ioctl mechanism. The response of the ioctl will be stored in buf
+ * when this function returns. The input 'size' specifies the size of the
+ * buffer to be passed.
+ */
+static void
+strioctl(int cmd, void *buf, size_t size)
+{
+ struct strioctl ioc;
+
+ (void) memset(&ioc, 0, sizeof (ioc));
+
+ ioc.ic_cmd = cmd;
+ ioc.ic_timout = 0;
+ ioc.ic_len = size;
+ ioc.ic_dp = (char *)buf;
+
+ if (ioctl(fd, I_STR, &ioc) < 0) {
+ printerror("ioctl (I_STR)");
+ (void) close(fd);
+ exit(EXIT_FAILURE);
+ /* NOTREACHED */
+ }
+}
+
+
+/*
+ * getkstatus(out_addr)
+ *
+ * Queries the kernel for the 6to4 Relay Router destination address by sending
+ * the SIOCG6TO4TUNRRADDR ioctl to the tunnel module using the I_STR ioctl
+ * mechanism. The value returned, through the ioctl, will be an ipaddr_t
+ * embedded in a strioctl. Output parameter is set with result.
+ */
+static void
+getkstatus(ipaddr_t *out_addr)
+{
+ ipaddr_t an_addr;
+
+ /* Get the Relay Router address from the kernel */
+ strioctl(SIOCG6TO4TUNRRADDR, &an_addr, sizeof (an_addr));
+
+ *out_addr = an_addr; /* set output parameter */
+}
+
+
+/*
+ * printkstatus()
+ *
+ * Queries the kernel for the current 6to4 Relay Router value, prints
+ * a status message based on the value and exits this command.
+ * INADDR_ANY is used to denote that Relay Router communication support is
+ * disabled within the kernel.
+ */
+static void
+printkstatus(void)
+{
+ ipaddr_t rr_addr;
+ char buf[INET6_ADDRSTRLEN];
+
+ getkstatus(&rr_addr); /* get value from kernel */
+ (void) printf("6to4relay: ");
+ if (rr_addr == INADDR_ANY) {
+ (void) printf(gettext("6to4 Relay Router communication "
+ "support is disabled.\n"));
+ } else {
+ (void) printf(gettext("6to4 Relay Router communication "
+ "support is enabled.\n"));
+ (void) printf(gettext("IPv4 destination address of Relay "
+ "Router = "));
+ (void) printf("%s\n",
+ inet_ntop(AF_INET, &rr_addr, buf, sizeof (buf)));
+ }
+}
+
+/*
+ * modifyroute(cmd, in_gw)
+ *
+ * Modifies a default IPv6 route with DST = ::, GATEWAY = in_gw, NETMASK = ::
+ * and flags = <GATEWAY, STATIC>.
+ * This route is to be propagated through the 6to4 site so that 6to4 hosts
+ * can send packets to native IPv6 hosts behind a remote 6to4 Relay Router.
+ */
+static void
+modifyroute(unsigned int cmd, in6_addr_t *in_gw)
+{
+ static int rtmseq;
+ int rtsock;
+ int rlen;
+
+ static struct {
+ struct rt_msghdr rt_hdr;
+ struct sockaddr_in6 rt_dst;
+ struct sockaddr_in6 rt_gate;
+ struct sockaddr_in6 rt_mask;
+ } rt_msg;
+
+ /* Open a routing socket for passing route commands */
+ if ((rtsock = socket(AF_ROUTE, SOCK_RAW, AF_INET)) < 0) {
+ printerror("socket");
+ (void) close(fd);
+ exit(EXIT_FAILURE);
+ /* NOTREACHED */
+ }
+
+ (void) memset(&rt_msg, 0, sizeof (rt_msg));
+ rt_msg.rt_hdr.rtm_msglen = sizeof (rt_msg);
+ rt_msg.rt_hdr.rtm_version = RTM_VERSION;
+ rt_msg.rt_hdr.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
+ rt_msg.rt_hdr.rtm_pid = getpid();
+ rt_msg.rt_hdr.rtm_type = cmd;
+ rt_msg.rt_hdr.rtm_seq = ++rtmseq;
+ rt_msg.rt_hdr.rtm_flags = RTF_STATIC | RTF_GATEWAY;
+
+ /* DST */
+ rt_msg.rt_dst.sin6_family = AF_INET6;
+ (void) memset(&rt_msg.rt_dst.sin6_addr.s6_addr, 0,
+ sizeof (in6_addr_t));
+
+ /* GATEWAY */
+ rt_msg.rt_gate.sin6_family = AF_INET6;
+ bcopy(in_gw->s6_addr, &rt_msg.rt_gate.sin6_addr.s6_addr,
+ sizeof (in6_addr_t));
+
+ /* NETMASK */
+ rt_msg.rt_mask.sin6_family = AF_INET6;
+ (void) memset(&rt_msg.rt_mask.sin6_addr.s6_addr, 0,
+ sizeof (in6_addr_t));
+
+ /* Send the routing message */
+ rlen = write(rtsock, &rt_msg, rt_msg.rt_hdr.rtm_msglen);
+ if (rlen < rt_msg.rt_hdr.rtm_msglen) {
+ if (rlen < 0) {
+ (void) fprintf(stderr,
+ gettext("6to4relay: write to routing socket: %s\n"),
+ strerror(errno));
+ } else {
+ (void) fprintf(stderr, gettext("6to4relay: write to "
+ "routing socket got only %d for rlen\n"), rlen);
+ }
+ }
+ (void) close(rtsock);
+}
+
+/*
+ * setkrraddr(in_addr)
+ *
+ * Sets the 6to4 Relay Router destination address value in the kernel using
+ * the SIOCS6TO4TUNRRADDR ioctl using the I_STR ioctl mechanism.
+ * The address is sent to the kernel, as an ipaddr_t, embedded in an strioctl.
+ */
+static void
+setkrraddr(ipaddr_t in_addr)
+{
+ /* set Relay Router address */
+ strioctl(SIOCS6TO4TUNRRADDR, &in_addr, sizeof (in_addr));
+}
+
+static void
+printerror(char *s)
+{
+ int sverrno = errno;
+
+ (void) fprintf(stderr, "6to4relay: ");
+ if (s != NULL)
+ (void) fprintf(stderr, "%s: ", s);
+ (void) fprintf(stderr, "%s\n", strerror(sverrno));
+}
+
+static void
+usage(void)
+{
+ (void) fprintf(stderr,
+ gettext("usage:\n"
+ "\t6to4relay\n"
+ "\t6to4relay -e [-a <addr>]\n"
+ "\t6to4relay -d\n"
+ "\t6to4relay -h\n"));
+}
+
+int
+main(int argc, char **argv)
+{
+ int ch;
+ char *in_addr = NULL;
+ int ret = EXIT_SUCCESS;
+
+ (void) setlocale(LC_ALL, "");
+
+#if !defined(TEXT_DOMAIN)
+#define TEXT_DOMAIN "SYS_TEST"
+#endif
+ (void) textdomain(TEXT_DOMAIN);
+
+ /* open /dev/ip for use */
+ if ((fd = open("/dev/ip", O_RDWR)) == -1) {
+ printerror(gettext("can't open /dev/ip"));
+ exit(EXIT_FAILURE);
+ }
+
+ if (ioctl(fd, I_PUSH, TUN_NAME) < 0) {
+ printerror("ioctl (I_PUSH)");
+ ret = EXIT_FAILURE;
+ goto done;
+ }
+
+ /* If no args are specified, print status as queried from kernel */
+ if (argc < 2) {
+ printkstatus();
+ goto done;
+ }
+ while ((ch = getopt(argc, argv, "ea:dh")) != EOF) {
+ switch (ch) {
+ case 'e':
+ eflag = B_TRUE;
+ break;
+ case 'd':
+ dflag = B_TRUE;
+ break;
+ case 'a':
+ aflag = B_TRUE;
+ in_addr = optarg;
+ break;
+ case 'h':
+ usage();
+ goto done;
+ default:
+ usage();
+ ret = EXIT_FAILURE;
+ goto done;
+ }
+ }
+ /*
+ * If -a is specified, -e must also be specified. Also, the
+ * combination of -e and -d is illegal. Fail on either case.
+ */
+ if ((aflag && !eflag) || (eflag && dflag)) {
+ usage();
+ ret = EXIT_FAILURE;
+ goto done;
+ }
+
+ /*
+ * Enable Relay Router communication support in the kernel.
+ */
+ if (eflag) {
+ struct in_addr current_addr; /* addr currently set in kernel */
+ struct in_addr new_addr; /* new addr we plan to set */
+ in6_addr_t v6_rt;
+
+ /*
+ * if -a was not specified, the well-known anycast will
+ * be used.
+ */
+ if (!aflag) {
+ new_addr.s_addr = htonl(INADDR_6TO4RRANYCAST);
+
+ } else if (inet_pton(AF_INET, in_addr, &new_addr) <= 0) {
+ (void) fprintf(stderr, gettext("6to4relay: input "
+ "address (%s) is not a valid IPv4 dotted-decimal "
+ "string.\n"), in_addr);
+ ret = EXIT_FAILURE;
+ goto done;
+ }
+
+ /*
+ * INADDR_ANY has special meaning in the kernel, reject this
+ * input and exit.
+ */
+ if (new_addr.s_addr == INADDR_ANY) {
+ (void) fprintf(stderr, gettext("6to4relay: input "
+ "(0.0.0.0) is not a valid IPv4 unicast "
+ "address.\n"));
+ ret = EXIT_FAILURE;
+ goto done;
+ }
+
+ /*
+ * get the current Relay Router address from the kernel.
+ *
+ * 1. If the current address is INADDR_ANY, set the new
+ * address in the kernel and add a default IPv6 route using
+ * the new address.
+ *
+ * 2. If the current address is different than the new address,
+ * set the new address in the kernel, delete the
+ * old default IPv6 route and add a new default IPv6 route
+ * (using the new address).
+ *
+ * 3. If the kernel address is the same as the one we are
+ * adding, no additional processing is needed.
+ */
+ getkstatus(&current_addr.s_addr);
+
+ if (current_addr.s_addr == INADDR_ANY) {
+ setkrraddr(new_addr.s_addr);
+ V4ADDR_TO_6TO4_RT(&new_addr, &v6_rt);
+ modifyroute(RTM_ADD, &v6_rt);
+ } else if (new_addr.s_addr != current_addr.s_addr) {
+ setkrraddr(new_addr.s_addr);
+ /* remove old default IPv6 route */
+ V4ADDR_TO_6TO4_RT(&current_addr, &v6_rt);
+ modifyroute(RTM_DELETE, &v6_rt);
+ /*
+ * Add new default IPv6 route using a 6to4 address
+ * created from the address we just set in the kernel.
+ */
+ V4ADDR_TO_6TO4_RT(&new_addr, &v6_rt);
+ modifyroute(RTM_ADD, &v6_rt);
+ }
+ }
+
+ /*
+ * Disable Relay Router communication support in kernel.
+ */
+ if (dflag) {
+ struct in_addr current_addr; /* addr currently set in kernel */
+ in6_addr_t v6_rt;
+
+ /*
+ * get Relay Router address from the kernel and delete
+ * default IPv6 route that was added for it.
+ */
+ getkstatus(&current_addr.s_addr);
+ if (current_addr.s_addr == INADDR_ANY) {
+ /*
+ * Feature is already disabled in kernel, no
+ * additional processing is needed.
+ */
+ goto done;
+ }
+
+ V4ADDR_TO_6TO4_RT(&current_addr, &v6_rt);
+ modifyroute(RTM_DELETE, &v6_rt);
+
+ /*
+ * INADDR_ANY (0.0.0.0) is used by the kernel to disable Relay
+ * Router communication support.
+ */
+ setkrraddr(INADDR_ANY);
+ }
+done:
+ (void) close(fd);
+ return (ret);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/Makefile b/usr/src/cmd/cmd-inet/usr.sbin/Makefile
new file mode 100644
index 0000000000..68e363bb66
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/Makefile
@@ -0,0 +1,268 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+SYNCPROG= syncinit syncloop syncstat
+DHCPPROG= dhcpconfig dhtadm pntadm
+
+# EXPORT DELETE START
+XMODPROG= wanbootutil
+# EXPORT DELETE END
+
+PROG= 6to4relay arp gettable if_mpadm ikeadm ikecert \
+ in.comsat in.fingerd in.rarpd in.rdisc in.rexecd in.rlogind \
+ in.rshd in.rwhod in.telnetd in.tftpd in.tnamed ipaddrsel \
+ ipsecalgs ipsecconf ipseckey ndd $(SYNCPROG) $(DHCPPROG) \
+ $(XMODPROG)
+
+MANIFEST= rarp.xml telnet.xml comsat.xml finger.xml \
+ login.xml shell.xml rexec.xml tname.xml
+
+ROOTFS_PROG= hostconfig route routeadm soconfig
+SBINLINKS= hostconfig route routeadm
+
+RPCSVCPROG= hostconfig
+AUDITPROG= in.rexecd in.rlogind in.rshd in.telnetd
+PAMPROG= in.rexecd in.rlogind in.rshd in.telnetd
+SOCKETPROG= 6to4relay arp gettable hostconfig if_mpadm in.comsat \
+ in.fingerd in.rarpd in.rdisc in.rexecd in.rlogind in.rshd \
+ in.rwhod in.telnetd in.tftpd in.tnamed ipaddrsel \
+ ipsecalgs ipsecconf ipseckey route routeadm
+NSLPROG= 6to4relay arp gettable hostconfig ikeadm in.comsat in.rarpd \
+ in.rdisc in.rexecd in.rlogind in.rshd in.rwhod in.telnetd \
+ in.tftpd in.tnamed ipaddrsel ipsecalgs ipsecconf ipseckey route
+CMDPROG= in.telnetd
+IPSECUTILPROG= ikeadm ipsecalgs ipsecconf ipseckey
+K5PROGS= in.telnetd in.rlogind in.rshd
+DEFAULTFILES= telnetd
+
+PROGSRCS= $(PROG:%=%.c)
+TFTPDOBJS= in.tftpd.o tftpsubs.o
+OTHERSRC= ../usr.bin/tftp/tftpsubs.c
+K5RLOGINOBJS= in.rlogind.o
+K5RSHDOBJS= in.rshd.o
+SRCS= $(PROGSRCS) $(OTHERSRC)
+
+SUBDIRS= bootconfchk htable ifconfig in.ftpd in.routed \
+ in.talkd inetadm inetconv ipqosconf mipagentconfig \
+ mipagentstat ping snoop sppptun traceroute
+
+MSGSUBDIRS= bootconfchk htable ifconfig in.ftpd in.routed in.talkd \
+ inetadm inetconv ipqosconf mipagentconfig mipagentstat \
+ sppptun snoop
+
+# As programs get lint-clean, add them here and to the 'lint' target.
+# Eventually this hack should go away, and all in PROG should be
+# lint-clean.
+LINTCLEAN= 6to4relay arp ikeadm in.rlogind in.rshd in.telnetd in.tftpd \
+ ipaddrsel ipsecalgs ipseckey ipsecconf route routeadm \
+ in.rarpd $(SYNCPROG)
+# Likewise, as subdirs get lint-clean, add them here. Once
+# they're all clean, replace the dependency of the lint target
+# with SUBDIRS. Also (sigh) deal with the commented-out build lines
+# for the lint rule.
+LINTSUBDIRS= bootconfchk in.routed in.talkd inetadm inetconv ipqosconf \
+ mipagentstat ping sppptun traceroute
+# And as programs are verified not to attempt to write into constants,
+# -xstrconst should be used to ensure they stay that way.
+CONSTCLEAN= ikeadm
+
+include ../../Makefile.cmd
+ROOTMANIFESTDIR= $(ROOTSVCNETWORK)
+$(ROOTMANIFEST) := FILEMODE= 444
+include ../Makefile.cmd-inet
+
+ROOTSBINPROG = $(ROOTFS_PROG:%=$(ROOTSBIN)/%)
+ROOTUSRSBINLINKS = $(SBINLINKS:%=$(ROOTUSRSBIN)/%)
+
+COMMONOBJS= kcmd.o
+COMMONSRCS= $(CMDINETCOMMONDIR)/$(COMMONOBJS:.o=.c)
+SRCS+= $(COMMONSRCS)
+
+#
+# Message catalog
+#
+POFILES= 6to4relay.po if_mpadm.po ikeadm.po in.comsat.po ipaddrsel.po \
+ ipsecalgs.po ipsecconf.po ipseckey.po route.po routeadm.po
+POFILE= usr.sbin.po
+
+all:= TARGET= all
+install:= TARGET= install
+clean:= TARGET= clean
+clobber:= TARGET= clobber
+lint:= TARGET= lint
+_msg:= TARGET= _msg
+
+CLOBBERFILES += $(ROOTFS_PROG) $(PROG)
+CLEANFILES += $(COMMONOBJS) $(K5RLOGINOBJS) $(K5RSHDOBJS) $(TFTPDOBJS)
+
+CPPFLAGS += -DSYSV -DBSD_COMP -I$(CMDINETCOMMONDIR) -I.
+
+include $(SRC)/lib/gss_mechs/mech_krb5/Makefile.mech_krb5
+K5LIBS=
+
+# Eventually just plain CFLAGS should be += -v, but not until all in
+# PROGS are lint clean.
+$(LINTCLEAN) := CFLAGS += $(CCVERBOSE)
+$(CONSTCLEAN) := CFLAGS += $(XSTRCONST)
+
+$(SYNCPROG) := LDLIBS += -ldlpi
+$(SOCKETPROG) := LDLIBS += -lsocket
+$(NSLPROG) := LDLIBS += -lnsl
+$(AUDITPROG) := LDLIBS += -lbsm
+$(PAMPROG) := LDLIBS += -lpam
+$(RPCSVCPROG) := LDLIBS += -lrpcsvc
+$(CMDPROG) := LDLIBS += -lcmd
+$(K5PROGS) := LDFLAGS += $(ZLAZYLOAD) $(KRUNPATH) \
+ -L$(ROOT)$(KLIBDIR_DO) -L$(ROOT)$(KLIBDIR_GL)
+$(K5PROGS) := K5LIBS= -lmech_krb5
+$(K5PROGS) := CPPFLAGS += -I$(SRC)/head \
+ -I$(SRC)/uts/common/ \
+ -I$(SRC)/uts/common/gssapi/mechs/krb5/include \
+ -I$(SRC)/lib/gss_mechs/mech_krb5/include \
+ -I$(SRC)/lib/pam_modules/krb5
+LDLIBS += $(K5LIBS)
+$(IPSECUTILPROG) := LDLIBS += -lipsecutil
+
+in.rarpd := LDLIBS += -linetutil
+route := CPPFLAGS += -DNDEBUG
+
+.KEEP_STATE:
+
+.PARALLEL:
+
+all: $(PROG) $(ROOTFS_PROG) $(SUBDIRS)
+
+#
+# message catalog
+#
+_msg: $(MSGSUBDIRS) $(POFILE)
+
+syncutil: $(SYNCPROG)
+
+$(POFILE): $(POFILES)
+ $(RM) $@
+ cat $(POFILES) > $@
+
+$(COMMONOBJS): $(COMMONSRCS)
+ $(COMPILE.c) $(COMMONSRCS)
+
+in.rlogind: $(K5RLOGINOBJS) $(COMMONOBJS)
+ $(LINK.c) $(K5RLOGINOBJS) $(COMMONOBJS) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+in.rshd: $(K5RSHDOBJS) $(COMMONOBJS)
+ $(LINK.c) $(K5RSHDOBJS) $(COMMONOBJS) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+in.tftpd: $(TFTPDOBJS)
+ $(LINK.c) $(TFTPDOBJS) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+tftpsubs.o: $(OTHERSRC)
+ $(COMPILE.c) $(OTHERSRC) -o $@
+ $(POST_PROCESS_O)
+
+$(ROOTUSRSBINLINKS):
+ -$(RM) $@; $(SYMLINK) ../../sbin/$(@F) $@
+
+$(ROOTETCDEFAULT)/%: %.dfl
+ $(INS.rename)
+
+install: $(PROG) $(ROOTFS_PROG) $(SUBDIRS) .WAIT $(ROOTUSRSBINPROG) \
+ $(ROOTSBINPROG) $(ROOTUSRSBINLINKS) $(ROOTETCDEFAULTFILES) \
+ $(ROOTMANIFEST)
+
+#
+# The reason this rule checks for the existence of the
+# Makefile is that some of the directories do not exist
+# in our exportable source builds.
+#
+$(SUBDIRS): FRC
+ @if [ -f $@/Makefile ]; then \
+ cd $@; pwd; $(MAKE) $(TARGET); \
+ else \
+ true; \
+ fi
+
+FRC:
+
+check: $(CHKMANIFEST)
+
+clean: $(SUBDIRS)
+ -$(RM) $(CLEANFILES)
+
+clobber: $(SUBDIRS)
+ -$(RM) $(CLEANFILES) $(CLOBBERFILES)
+
+lint: $(LINTSUBDIRS)
+ $(LINT.c) 6to4relay.c $(LDLIBS) -lsocket -lnsl
+ $(LINT.c) arp.c $(LDLIBS) -lsocket -lnsl
+ @# $(LINT.c) in.rexecd.c $(LDLIBS) -lbsm -lpam
+ $(LINT.c) -erroff=E_NAME_USED_NOT_DEF2 -erroff=E_NAME_DEF_NOT_USED2 \
+ -I$(SRC)/head -I$(SRC)/uts/common/ \
+ -I$(SRC)/uts/common/gssapi/mechs/krb5/include \
+ -I$(SRC)/lib/gss_mechs/mech_krb5/include \
+ -I$(SRC)/lib/pam_modules/krb5 \
+ in.rlogind.c $(COMMONSRCS) $(LDLIBS) -lbsm -lpam -lsocket -lnsl
+ $(LINT.c) -erroff=E_NAME_USED_NOT_DEF2 -erroff=E_NAME_DEF_NOT_USED2 \
+ -I$(SRC)/head -I$(SRC)/uts/common/ \
+ -I$(SRC)/uts/common/gssapi/mechs/krb5/include \
+ -I$(SRC)/lib/gss_mechs/mech_krb5/include \
+ -I$(SRC)/lib/pam_modules/krb5 \
+ in.rshd.c $(COMMONSRCS) $(LDLIBS) -lbsm -lpam -lsocket -lnsl
+ $(LINT.c) -erroff=E_NAME_USED_NOT_DEF2 \
+ -erroff=E_GLOBAL_COULD_BE_STATIC2 \
+ -I$(SRC)/head -I$(SRC)/uts/common/ \
+ -I$(SRC)/uts/common/gssapi/mechs/krb5/include \
+ -I$(SRC)/lib/gss_mechs/mech_krb5/include \
+ -I$(SRC)/lib/pam_modules/krb5 \
+ in.telnetd.c $(LDLIBS) -lbsm -lpam -lsocket -lnsl
+ $(LINT.c) ipaddrsel.c $(LDLIBS) -lsocket -lnsl
+ $(LINT.c) ipsecalgs.c $(LDLIBS) -lsocket -lnsl -lipsecutil
+ $(LINT.c) ipsecconf.c $(LDLIBS) -lsocket -lnsl -lipsecutil
+ $(LINT.c) ipseckey.c $(LDLIBS) -lsocket -lnsl -lipsecutil
+ $(LINT.c) ikeadm.c $(LDLIBS) -lnsl -lipsecutil
+ $(LINT.c) route.c $(LDLIBS) -lsocket -lnsl
+ $(LINT.c) routeadm.c $(LDLIBS) -lsocket
+ $(LINT.c) syncinit.c $(LDLIBS) -ldlpi
+ $(LINT.c) syncloop.c $(LDLIBS) -ldlpi
+ $(LINT.c) syncstat.c $(LDLIBS) -ldlpi
+ $(LINT.c) -erroff=E_NAME_USED_NOT_DEF2 in.rarpd.c $(LDLIBS) \
+ -lsocket -lnsl
+ $(LINT.c) in.tftpd.c ../usr.bin/tftp/tftpsubs.c $(LDLIBS) \
+ -lsocket -lnsl
+
+# EXPORT DELETE START
+EXPORT_SRC:
+ $(RM) Makefile+
+ sed -e "/^# EXPORT DELETE START/,/^# EXPORT DELETE END/d" \
+ < Makefile > Makefile+
+ $(RM) Makefile
+ $(MV) Makefile+ Makefile
+ $(CHMOD) 444 Makefile
+# EXPORT DELETE END
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/arp.c b/usr/src/cmd/cmd-inet/usr.sbin/arp.c
new file mode 100644
index 0000000000..c028eaf77d
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/arp.c
@@ -0,0 +1,396 @@
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * Copyright (c) 1984 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Sun Microsystems, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * arp - display, set, and delete arp table entries
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <netdb.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <netinet/if_ether.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <net/if_types.h>
+#include <net/if_dl.h>
+
+static int file(char *);
+static int set(int, char *[]);
+static void get(char *);
+static void delete(char *);
+static void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+ int c, nflags = 0, argsleft;
+ int n_flag, a_flag, d_flag, f_flag, s_flag;
+
+ n_flag = a_flag = d_flag = f_flag = s_flag = 0;
+
+#define CASE(x, y) \
+ case x: \
+ if (nflags > 0) { \
+ usage(); \
+ exit(1); \
+ } else \
+ y##_flag = 1; \
+ nflags++; \
+ break
+
+ while ((c = getopt(argc, argv, "nadfs")) != EOF) {
+ switch (c) {
+ case '?':
+ usage();
+ exit(1);
+ /* NOTREACHED */
+ break;
+ case 'n':
+ n_flag = 1;
+ break;
+ CASE('a', a);
+ CASE('d', d);
+ CASE('f', f);
+ CASE('s', s);
+ }
+ }
+
+#undef CASE
+
+ /*
+ * -n only allowed with -a
+ */
+ if (n_flag && !a_flag) {
+ usage();
+ exit(1);
+ }
+
+ argsleft = argc - optind;
+
+ if (a_flag && (argsleft == 0)) {
+ /*
+ * the easiest way to get the complete arp table
+ * is to let netstat, which prints it as part of
+ * the MIB statistics, do it.
+ */
+ (void) execl("/usr/bin/netstat", "netstat",
+ (n_flag ? "-np" : "-p"),
+ "-f", "inet", (char *)0);
+ exit(1);
+
+ } else if (s_flag && (argsleft >= 2)) {
+ if (set(argsleft, &argv[optind]) != 0)
+ exit(1);
+
+ } else if (d_flag && (argsleft == 1)) {
+ delete(argv[optind]);
+
+ } else if (f_flag && (argsleft == 1)) {
+ if (file(argv[optind]) != 0)
+ exit(1);
+
+ } else if ((nflags == 0) && (argsleft == 1)) {
+ get(argv[optind]);
+
+ } else {
+ usage();
+ exit(1);
+ }
+ return (0);
+}
+
+/*
+ * Process a file to set standard arp entries
+ */
+static int file(char *name)
+{
+ /*
+ * A line of input can be:
+ * <hostname> <macaddr> ["temp"] ["pub"] ["trail"]
+ */
+#define MAX_LINE_LEN (MAXHOSTNAMELEN + \
+ sizeof (" xx:xx:xx:xx:xx:xx temp pub trail\n"))
+#define MIN_ARGS 2
+#define MAX_ARGS 5
+
+ FILE *fp;
+ char line[MAX_LINE_LEN];
+ int retval;
+
+ if ((fp = fopen(name, "r")) == NULL) {
+ (void) fprintf(stderr, "arp: cannot open %s\n", name);
+ exit(1);
+ }
+
+ retval = 0;
+ while (fgets(line, MAX_LINE_LEN, fp) != NULL) {
+ char line_copy[MAX_LINE_LEN];
+ char *args[MAX_ARGS];
+ char *start;
+ int i;
+
+ /*
+ * Keep a copy of the un-altered line for error
+ * reporting.
+ */
+ (void) strlcpy(line_copy, line, MAX_LINE_LEN);
+
+ start = line_copy;
+ for (i = 0; i < MAX_ARGS; i++) {
+ if ((args[i] = strtok(start, " \t\n")) == NULL)
+ break;
+
+ start = NULL;
+ }
+
+ if (i < MIN_ARGS) {
+ (void) fprintf(stderr, "arp: bad line: %s\n",
+ line);
+ retval = 1;
+ continue;
+ }
+
+ if (set(i, args) != 0)
+ retval = 1;
+ }
+
+#undef MAX_LINE_LEN
+#undef MIN_ARGS
+#undef MAX_ARGS
+
+ (void) fclose(fp);
+ return (retval);
+}
+
+/*
+ * Set an individual arp entry
+ */
+static int set(int argc, char *argv[])
+{
+ struct xarpreq ar;
+ struct hostent *hp;
+ struct sockaddr_in *sin;
+ uchar_t *ea;
+ int s;
+ char *host = argv[0], *eaddr = argv[1];
+
+ argc -= 2;
+ argv += 2;
+ (void) memset(&ar, 0, sizeof (ar));
+ sin = (struct sockaddr_in *)&ar.xarp_pa;
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = inet_addr(host);
+ if (sin->sin_addr.s_addr == (in_addr_t)-1) {
+ hp = gethostbyname(host);
+ if (hp == NULL) {
+ (void) fprintf(stderr, "arp: %s: unknown host\n",
+ host);
+ return (1);
+ }
+ (void) memcpy(&sin->sin_addr, hp->h_addr,
+ sizeof (sin->sin_addr));
+ }
+ ea = _link_aton(eaddr, &s);
+ if (ea == NULL) {
+ if (s == -1) {
+ (void) fprintf(stderr,
+ "arp: invalid link layer address '%s'\n", eaddr);
+ return (1);
+ }
+ perror("arp: nomem");
+ exit(1);
+ }
+ ar.xarp_ha.sdl_alen = s;
+ (void) memcpy(LLADDR(&ar.xarp_ha), ea, ar.xarp_ha.sdl_alen);
+ free(ea);
+ ar.xarp_ha.sdl_family = AF_LINK;
+ ar.xarp_flags = ATF_PERM;
+ while (argc-- > 0) {
+ if (strncmp(argv[0], "temp", 4) == 0)
+ ar.xarp_flags &= ~ATF_PERM;
+ if (strncmp(argv[0], "pub", 3) == 0)
+ ar.xarp_flags |= ATF_PUBL;
+ if (strncmp(argv[0], "trail", 5) == 0)
+ ar.xarp_flags |= ATF_USETRAILERS;
+ argv++;
+ }
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ perror("arp: socket");
+ exit(1);
+ }
+ if (ioctl(s, SIOCSXARP, (caddr_t)&ar) < 0) {
+ perror(host);
+ exit(1);
+ }
+ (void) close(s);
+ return (0);
+}
+
+
+/*
+ * Display an individual arp entry
+ */
+static void get(char *host)
+{
+ struct xarpreq ar;
+ struct hostent *hp;
+ struct sockaddr_in *sin;
+ uchar_t *ea;
+ int s;
+ char *str = NULL;
+
+ (void) memset(&ar, 0, sizeof (ar));
+ sin = (struct sockaddr_in *)&ar.xarp_pa;
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = inet_addr(host);
+ if (sin->sin_addr.s_addr == (in_addr_t)-1) {
+ hp = gethostbyname(host);
+ if (hp == NULL) {
+ (void) fprintf(stderr, "arp: %s: unknown host\n",
+ host);
+ exit(1);
+ }
+ (void) memcpy(&sin->sin_addr, hp->h_addr,
+ sizeof (sin->sin_addr));
+ }
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ perror("arp: socket");
+ exit(1);
+ }
+ ar.xarp_ha.sdl_family = AF_LINK;
+ if (ioctl(s, SIOCGXARP, (caddr_t)&ar) < 0) {
+ if (errno == ENXIO)
+ (void) printf("%s (%s) -- no entry\n",
+ host, inet_ntoa(sin->sin_addr));
+ else
+ perror("SIOCGXARP");
+ exit(1);
+ }
+ (void) close(s);
+ ea = (uchar_t *)LLADDR(&ar.xarp_ha);
+ if (ar.xarp_flags & ATF_COM) {
+ str = _link_ntoa(ea, str, ar.xarp_ha.sdl_alen, IFT_OTHER);
+ if (str != NULL) {
+ (void) printf("%s (%s) at %s", host,
+ inet_ntoa(sin->sin_addr), str);
+ free(str);
+ } else {
+ perror("arp: nomem");
+ exit(1);
+ }
+ } else {
+ (void) printf("%s (%s) at (incomplete)", host,
+ inet_ntoa(sin->sin_addr));
+ }
+ if (ar.xarp_flags & ATF_PERM)
+ (void) printf(" permanent");
+ if (ar.xarp_flags & ATF_PUBL)
+ (void) printf(" published");
+ if (ar.xarp_flags & ATF_USETRAILERS)
+ (void) printf(" trailers");
+ (void) printf("\n");
+}
+
+/*
+ * Delete an arp entry
+ */
+static void delete(char *host)
+{
+ struct xarpreq ar;
+ struct hostent *hp;
+ struct sockaddr_in *sin;
+ int s;
+
+ (void) memset(&ar, 0, sizeof (ar));
+ sin = (struct sockaddr_in *)&ar.xarp_pa;
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = inet_addr(host);
+ if (sin->sin_addr.s_addr == (in_addr_t)-1) {
+ hp = gethostbyname(host);
+ if (hp == NULL) {
+ (void) fprintf(stderr, "arp: %s: unknown host\n",
+ host);
+ exit(1);
+ }
+ (void) memcpy(&sin->sin_addr, hp->h_addr,
+ sizeof (sin->sin_addr));
+ }
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ perror("arp: socket");
+ exit(1);
+ }
+ ar.xarp_ha.sdl_family = AF_LINK;
+ if (ioctl(s, SIOCDXARP, (caddr_t)&ar) < 0) {
+ if (errno == ENXIO)
+ (void) printf("%s (%s) -- no entry\n",
+ host, inet_ntoa(sin->sin_addr));
+ else
+ perror("SIOCDXARP");
+ exit(1);
+ }
+ (void) close(s);
+ (void) printf("%s (%s) deleted\n", host, inet_ntoa(sin->sin_addr));
+}
+
+static void usage(void)
+{
+ (void) printf("Usage: arp hostname\n");
+ (void) printf(" arp -a [-n]\n");
+ (void) printf(" arp -d hostname\n");
+ (void) printf(" arp -s hostname ether_addr "
+ "[temp] [pub] [trail]\n");
+ (void) printf(" arp -f filename\n");
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/bootconfchk/Makefile b/usr/src/cmd/cmd-inet/usr.sbin/bootconfchk/Makefile
new file mode 100644
index 0000000000..5a1f86f2b5
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/bootconfchk/Makefile
@@ -0,0 +1,50 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+include ../../../Makefile.cmd
+include $(SRC)/lib/openssl/Makefile.openssl
+
+PROG = bootconfchk
+
+# Need to be told where the OpenSSL libraries are because libwanboot is
+# linked to them and they are in a non standard place.
+LDLIBS += -lwanbootutil -lwanboot $(OPENSSL_LDFLAGS)
+
+CPPFLAGS += -I$(SRC)/common/net/wanboot/crypt
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+install: all $(ROOTUSRSBINPROG)
+
+clean:
+
+lint: lint_PROG
+
+include ../../../Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/bootconfchk/bootconfchk.c b/usr/src/cmd/cmd-inet/usr.sbin/bootconfchk/bootconfchk.c
new file mode 100644
index 0000000000..d4e741ac6d
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/bootconfchk/bootconfchk.c
@@ -0,0 +1,92 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * This program parses and validates a wanboot.conf(4) file, and reports
+ * any errors on standard error.
+ *
+ * Returns:
+ * = 0 - success
+ * > 0 - error (see exit codes below)
+ */
+
+#include <libintl.h>
+#include <locale.h>
+#include <stdio.h>
+#include <wanbootutil.h>
+#include <wanboot_conf.h>
+
+/*
+ * Exit codes:
+ */
+#define BOOTCONFCHK_OK 0
+#define BOOTCONFCHK_INVALID 1
+#define BOOTCONFCHK_USAGE 2
+
+int
+main(int argc, char **argv)
+{
+ int ret = BOOTCONFCHK_OK;
+ char *bootconf;
+ bc_handle_t bc_handle;
+
+ /*
+ * Do the necessary magic for localization support.
+ */
+ (void) setlocale(LC_ALL, "");
+#if !defined(TEXT_DOMAIN)
+#define TEXT_DOMAIN "SYS_TEST"
+#endif /* !defined(TEXT_DOMAIN) */
+ (void) textdomain(TEXT_DOMAIN);
+
+ /*
+ * Initialize program name for use by wbku_printerr().
+ */
+ wbku_errinit(argv[0]);
+
+ /*
+ * Check usage is legal.
+ */
+ if (argc != 2) {
+ (void) fprintf(stderr,
+ gettext("Usage: %s bootconf_file\n"), argv[0]);
+ return (BOOTCONFCHK_USAGE);
+ }
+ bootconf = argv[1];
+
+ /*
+ * Parse and validate the given wanboot.conf(4) file.
+ */
+ if (bootconf_init(&bc_handle, bootconf) != BC_SUCCESS) {
+ wbku_printerr("Error parsing/validating %s: %s\n",
+ bootconf, bootconf_errmsg(&bc_handle));
+ ret = BOOTCONFCHK_INVALID;
+ }
+ bootconf_end(&bc_handle);
+
+ return (ret);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/comsat.xml b/usr/src/cmd/cmd-inet/usr.sbin/comsat.xml
new file mode 100644
index 0000000000..30d030b7c8
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/comsat.xml
@@ -0,0 +1,104 @@
+<?xml version="1.0"?>
+<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
+<!--
+ Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ ident "%Z%%M% %I% %E% SMI"
+
+ NOTE: This service manifest is not editable; its contents will
+ be overwritten by package or patch operations, including
+ operating system upgrade. Make customizations in a different
+ file.
+
+ Service manifest for the in.comsat service.
+-->
+
+<service_bundle type='manifest' name='SUNWrcmdr:comsat'>
+
+<service
+ name='network/comsat'
+ type='service'
+ version='1'>
+
+ <create_default_instance enabled='false' />
+
+ <restarter>
+ <service_fmri value='svc:/network/inetd:default' />
+ </restarter>
+
+ <exec_method
+ type='method'
+ name='inetd_start'
+ exec='/usr/sbin/in.comsat'
+ timeout_seconds='0'>
+ <method_context>
+ <method_credential user='root' group='root' />
+ </method_context>
+ </exec_method>
+
+ <exec_method
+ type='method'
+ name='inetd_offline'
+ exec=':kill_process'
+ timeout_seconds='0'>
+ </exec_method>
+
+ <exec_method
+ type='method'
+ name='inetd_disable'
+ exec=':kill'
+ timeout_seconds='0'>
+ </exec_method>
+
+ <property_group name='inetd' type='framework'>
+ <stability value='Evolving' />
+ <propval name='name' type='astring' value='comsat' />
+ <propval name='endpoint_type' type='astring' value='dgram' />
+ <propval name='proto' type='astring' value='udp' />
+ <propval name='wait' type='boolean' value='true' />
+ <propval name='isrpc' type='boolean' value='false' />
+ </property_group>
+
+ <stability value='Unstable' />
+
+ <template>
+ <common_name>
+ <loctext xml:lang='C'>comsat</loctext>
+ </common_name>
+ <description>
+ <loctext xml:lang='C'>
+comsat listens for reports of incoming mail and notifies users who have
+requested to be told when mail arrives.
+ </loctext>
+ </description>
+ <documentation>
+ <manpage title='in.comsat' section='1M'
+ manpath='/usr/share/man' />
+ <manpage title='comsat' section='1M'
+ manpath='/usr/share/man' />
+ </documentation>
+ </template>
+</service>
+
+</service_bundle>
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/dhcpconfig.sh b/usr/src/cmd/cmd-inet/usr.sbin/dhcpconfig.sh
new file mode 100644
index 0000000000..2091831f1c
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/dhcpconfig.sh
@@ -0,0 +1,30 @@
+#!/usr/bin/pfsh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+
+exec /usr/lib/inet/dhcp/svcadm/dhcpconfig "$@"
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/dhtadm.sh b/usr/src/cmd/cmd-inet/usr.sbin/dhtadm.sh
new file mode 100644
index 0000000000..18f7a43d10
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/dhtadm.sh
@@ -0,0 +1,30 @@
+#!/usr/bin/pfsh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+
+exec /usr/lib/inet/dhcp/svcadm/dhtadm "$@"
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/finger.xml b/usr/src/cmd/cmd-inet/usr.sbin/finger.xml
new file mode 100644
index 0000000000..910be817e3
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/finger.xml
@@ -0,0 +1,99 @@
+<?xml version="1.0"?>
+<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
+<!--
+ Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ ident "%Z%%M% %I% %E% SMI"
+
+ NOTE: This service manifest is not editable; its contents will
+ be overwritten by package or patch operations, including
+ operating system upgrade. Make customizations in a different
+ file.
+
+ Service manifest for the in.fingerd service.
+-->
+
+<service_bundle type='manifest' name='SUNWrcmdr:finger'>
+
+<service
+ name='network/finger'
+ type='service'
+ version='1'>
+
+ <create_default_instance enabled='false' />
+
+ <restarter>
+ <service_fmri value='svc:/network/inetd:default' />
+ </restarter>
+
+ <exec_method
+ type='method'
+ name='inetd_start'
+ exec='/usr/sbin/in.fingerd'
+ timeout_seconds='0'>
+ <method_context>
+ <method_credential user='noaccess' group='noaccess' />
+ </method_context>
+ </exec_method>
+
+ <exec_method
+ type='method'
+ name='inetd_disable'
+ exec=':kill'
+ timeout_seconds='0'>
+ </exec_method>
+
+ <property_group name='inetd' type='framework'>
+ <stability value='Evolving' />
+ <propval name='name' type='astring' value='finger' />
+ <propval name='endpoint_type' type='astring' value='stream' />
+ <propval name='proto' type='astring' value='tcp6' />
+ <propval name='wait' type='boolean' value='false' />
+ <propval name='isrpc' type='boolean' value='false' />
+ </property_group>
+
+ <stability value='Unstable' />
+
+ <template>
+ <common_name>
+ <loctext xml:lang='C'>finger</loctext>
+ </common_name>
+ <description>
+ <loctext xml:lang='C'>
+in.fingerd is the remote user information service which implements
+server side of the Name/Finger protocol. This protocol provides a
+remote interface to programs which display information on system
+status and individual users.
+ </loctext>
+ </description>
+ <documentation>
+ <manpage title='in.fingerd' section='1M'
+ manpath='/usr/share/man' />
+ <manpage title='fingerd' section='1M'
+ manpath='/usr/share/man' />
+ </documentation>
+ </template>
+</service>
+
+</service_bundle>
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/gettable.c b/usr/src/cmd/cmd-inet/usr.sbin/gettable.c
new file mode 100644
index 0000000000..01927c606a
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/gettable.c
@@ -0,0 +1,189 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1989 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * Portions of this source code were derived from Berkeley 4.3 BSD
+ * under license from the Regents of the University of California.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+
+#include <stdio.h>
+#include <netdb.h>
+
+#define OUTFILE "hosts.txt" /* default output file */
+#define VERFILE "hosts.ver" /* default version file */
+#define QUERY "ALL\r\n" /* query to hostname server */
+#define VERSION "VERSION\r\n" /* get version number */
+
+#define equaln(s1, s2, n) (!strncmp(s1, s2, n))
+
+#ifdef SYSV
+#define bcopy(a,b,c) memcpy(b,a,c)
+#endif
+
+struct sockaddr_in sin;
+struct sockaddr_in sintmp;
+char buf[BUFSIZ];
+char *outfile = OUTFILE;
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int s;
+ register len;
+ register FILE *sfi, *sfo, *hf;
+ char *host;
+ register struct hostent *hp;
+ struct servent *sp;
+ int version = 0;
+ int beginseen = 0;
+ int endseen = 0;
+
+ argv++, argc--;
+ if (argc > 0 && **argv == '-') {
+ if (argv[0][1] != 'v')
+ fprintf(stderr, "unknown option %s ignored\n", *argv);
+ else
+ version++, outfile = VERFILE;
+ argv++, argc--;
+ }
+ if (argc < 1 || argc > 2) {
+ fprintf(stderr, "usage: gettable [-v] host [ file ]\n");
+ exit(1);
+ }
+ sp = getservbyname("hostnames", "tcp");
+ if (sp == NULL) {
+ fprintf(stderr, "gettable: hostnames/tcp: unknown service\n");
+ exit(3);
+ }
+ host = *argv;
+ argv++, argc--;
+ sintmp.sin_addr.s_addr = inet_addr(host);
+ if (sintmp.sin_addr.s_addr != -1 && sintmp.sin_addr.s_addr != 0) {
+ sin.sin_family = AF_INET;
+ } else {
+ hp = gethostbyname(host);
+ if (hp == NULL) {
+ fprintf(stderr, "gettable: %s: host unknown\n", host);
+ exit(2);
+ } else {
+ sin.sin_family = hp->h_addrtype;
+ host = hp->h_name;
+ }
+ }
+ if (argc > 0)
+ outfile = *argv;
+ s = socket(sin.sin_family, SOCK_STREAM, 0);
+ if (s < 0) {
+ perror("gettable: socket");
+ exit(4);
+ }
+ if (bind(s, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
+ perror("gettable: bind");
+ exit(5);
+ }
+ if (sintmp.sin_addr.s_addr != -1 && sintmp.sin_addr.s_addr != 0)
+ bcopy((char *)&sintmp.sin_addr, (char *)&sin.sin_addr,
+ sizeof(sintmp.sin_addr));
+ else
+ bcopy(hp->h_addr, (char *)&sin.sin_addr, hp->h_length);
+ sin.sin_port = sp->s_port;
+ if (connect(s, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
+ perror("gettable: connect");
+ exit(6);
+ }
+ fprintf(stderr, "Connection to %s opened.\n", host);
+ sfi = fdopen(s, "r");
+ sfo = fdopen(s, "w");
+ if (sfi == NULL || sfo == NULL) {
+ perror("gettable: fdopen");
+ close(s);
+ exit(1);
+ }
+ hf = fopen(outfile, "w");
+ if (hf == NULL) {
+ fprintf(stderr, "gettable: "); perror(outfile);
+ close(s);
+ exit(1);
+ }
+ fprintf(sfo, version ? VERSION : QUERY);
+ fflush(sfo);
+ while (fgets(buf, sizeof(buf), sfi) != NULL) {
+ len = strlen(buf);
+ buf[len-2] = '\0';
+ if (!version && equaln(buf, "BEGIN", 5)) {
+ if (beginseen || endseen) {
+ fprintf(stderr,
+ "gettable: BEGIN sequence error\n");
+ exit(90);
+ }
+ beginseen++;
+ continue;
+ }
+ if (!version && equaln(buf, "END", 3)) {
+ if (!beginseen || endseen) {
+ fprintf(stderr,
+ "gettable: END sequence error\n");
+ exit(91);
+ }
+ endseen++;
+ continue;
+ }
+ if (equaln(buf, "ERR", 3)) {
+ fprintf(stderr,
+ "gettable: hostname service error: %s", buf);
+ exit(92);
+ }
+ fprintf(hf, "%s\n", buf);
+ }
+ fclose(hf);
+ if (!version) {
+ if (!beginseen) {
+ fprintf(stderr, "gettable: no BEGIN seen\n");
+ exit(93);
+ }
+ if (!endseen) {
+ fprintf(stderr, "gettable: no END seen\n");
+ exit(94);
+ }
+ fprintf(stderr, "Host table received.\n");
+ } else
+ fprintf(stderr, "Version number received.\n");
+ close(s);
+ fprintf(stderr, "Connection to %s closed\n", host);
+ exit(0);
+ /* NOTREACHED */
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/hostconfig.c b/usr/src/cmd/cmd-inet/usr.sbin/hostconfig.c
new file mode 100644
index 0000000000..4864a7bc69
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/hostconfig.c
@@ -0,0 +1,519 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1990-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <locale.h>
+#include <sys/utsname.h>
+#include <sys/systeminfo.h>
+#include <netdb.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/errno.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <sys/signal.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/stropts.h>
+#include <sys/resource.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <sys/stream.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netinet/if_ether.h>
+#include <netinet/ip_var.h>
+#include <netinet/udp.h>
+#include <netinet/udp_var.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/bootparam_prot.h>
+
+#define MAXIFS 256
+
+/* command line flags */
+int debug = 0; /* do debug printfs */
+int echo_host = 0; /* just echo hostname, don't set it */
+int verbose = 0; /* do verbose printfs */
+int safe = 0; /* don't change anything */
+int multiple = 0; /* take multiple replies */
+
+static ulong_t if_netmask;
+
+void notsupported(), usage(), bp_whoami();
+int get_ifdata(); /* get IP addr, subnet mask from IF */
+extern char *inet_ntoa();
+extern int getopt(), setdomainname();
+
+struct prototab {
+ char *name;
+ void (*func)();
+} prototab[] = {
+ { "bootparams", bp_whoami },
+ { "bootp", notsupported },
+ { 0, 0 }
+};
+
+
+
+/*
+ * usage: hostconfig [-p <protocol>] [-v] [-n] [-h] [<ifname>] [-f <hostname>]
+ *
+ * options:
+ * -d Debug mode.
+ * -v Verbose mode.
+ * -n Don't change anything.
+ * -h Don't set hostname, just echo to standard out.
+ * -m Wait for multiple answers (best used with the "-n"
+ * and "-v" flags).
+ * -f <hostname> Fake mode - get bootparams for <hostname> (also
+ * best used with the "-n" and "-v" flags).
+ * <ifname> Use IP address of <interface> in whoami request.
+ *
+ * If no interface name is specified, bp_whoami will cycle through the
+ * interfaces, using the IP address of each in turn until an answer is
+ * received. Note that rpc_broadcast() broadcasts the RPC call on all
+ * interfaces, so the <ifname> argument doesn't restrict the request
+ * to that interface, it just uses that interface to determine the IP
+ * address to put into the request. If "-f <hostname>" is specified,
+ * we put the IP address of <hostname> in the whoami request. Otherwise,
+ * we put the IP address of the interface in the whoami request.
+ *
+ */
+
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ struct ifreq *reqbuf;
+ struct ifreq *ifr;
+ struct ifconf ifc;
+ struct in_addr targetaddr;
+ struct hostent *hp;
+ char *targethost = NULL;
+ char *cmdname;
+ int c;
+ int n;
+ struct prototab *ptp;
+ void (*protofunc)() = NULL;
+ int numifs;
+ unsigned bufsize;
+
+ extern char *optarg;
+ extern int optind;
+
+ cmdname = argv[0];
+
+ while ((c = getopt(argc, argv, "dhvnmf:p:")) != -1) {
+
+ switch ((char)c) {
+ case 'd':
+ debug++;
+ break;
+
+ case 'h':
+ echo_host++;
+ break;
+ case 'v':
+ verbose++;
+ break;
+
+ case 'm':
+ multiple++;
+ break;
+
+ case 'n':
+ safe++;
+ break;
+
+ case 'f':
+ targethost = optarg;
+ break;
+
+ case 'p':
+ protofunc = NULL;
+ for (ptp = &prototab[0]; ptp->func; ptp++)
+ if (strcmp(optarg, ptp->name) == 0) {
+ protofunc = ptp->func;
+ break;
+ }
+ if (protofunc == NULL)
+ usage(cmdname);
+ break;
+
+ case '?':
+ usage(cmdname);
+ }
+ }
+
+ if (protofunc == NULL)
+ usage(cmdname);
+
+ if (targethost) {
+ /* we are faking it */
+ if (debug)
+ fprintf(stdout, "targethost = %s\n", targethost);
+
+ if ((hp = gethostbyname(targethost)) == NULL) {
+ if ((targetaddr.s_addr = inet_addr(targethost)) ==
+ (ulong_t)(-1)) {
+ (void) fprintf(stderr,
+ "%s: cannot get IP address for %s\n",
+ cmdname, targethost);
+ return (1);
+ }
+ } else {
+ if (hp->h_length != sizeof (targetaddr)) {
+ (void) fprintf(stderr,
+ "%s: cannot find host entry for %s\n",
+ cmdname, targethost);
+ return (1);
+ } else
+ (void) memcpy(&targetaddr.s_addr, hp->h_addr,
+ sizeof (targetaddr));
+ }
+ } else
+ targetaddr.s_addr = 0;
+
+ if (optind < argc) {
+ /* interface names were specified */
+ for (; optind < argc; optind++) {
+ if (debug)
+ fprintf(stdout, "Trying arg %s\n",
+ argv[optind]);
+ (*protofunc)(argv[optind], targetaddr);
+ }
+ } else {
+ /* no interface names specified - try them all */
+ int ifcount = 0; /* count of useable interfaces */
+ int s;
+
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ perror("socket");
+ return (1);
+ }
+#ifdef SIOCGIFNUM
+ if (ioctl(s, SIOCGIFNUM, (char *)&numifs) < 0) {
+ numifs = MAXIFS;
+ }
+#else
+ numifs = MAXIFS;
+#endif
+ bufsize = numifs * sizeof (struct ifreq);
+ reqbuf = (struct ifreq *)malloc(bufsize);
+ if (reqbuf == NULL) {
+ fprintf(stderr, "out of memory\n");
+ return (1);
+ }
+ ifc.ifc_buf = (caddr_t)&reqbuf[0];
+ ifc.ifc_len = bufsize;
+ if (ioctl(s, SIOCGIFCONF, (char *)&ifc) < 0) {
+ perror("ioctl(SIOCGIFCONF)");
+ return (1);
+ }
+ ifr = ifc.ifc_req;
+ n = ifc.ifc_len/sizeof (struct ifreq);
+ for (; n > 0; n--, ifr++) {
+ if (ioctl(s, SIOCGIFFLAGS, (char *)ifr) < 0) {
+ perror("ioctl(SIOCGIFFLAGS)");
+ return (1);
+ }
+ if ((ifr->ifr_flags & IFF_LOOPBACK) ||
+ !(ifr->ifr_flags & IFF_BROADCAST) ||
+ !(ifr->ifr_flags & IFF_UP) ||
+ (ifr->ifr_flags & IFF_NOARP) ||
+ (ifr->ifr_flags & IFF_POINTOPOINT)) {
+ if (debug)
+ fprintf(stdout, "If %s not suitable\n",
+ ifr->ifr_name);
+ continue;
+ } else {
+ if (debug)
+ fprintf(stdout, "Trying device %s\n",
+ ifr->ifr_name);
+ (*protofunc)(ifr->ifr_name, targetaddr);
+ ifcount++;
+ }
+ }
+ if (verbose && ifcount == 0) {
+ fprintf(stderr, "No useable interfaces found.\n");
+ return (1);
+ }
+ (void) close(s);
+ (void) free((char *)reqbuf);
+ }
+ return (0);
+}
+
+
+void
+add_default_route(router_addr)
+ struct in_addr router_addr;
+{
+ struct rtentry route;
+ struct sockaddr_in *sin;
+ int s;
+
+ (void) memset(&route, 0, sizeof (route));
+
+ /* route destination is "default" - zero */
+ /* LINTED - alignment OK (32bit) */
+ sin = (struct sockaddr_in *)&route.rt_dst;
+ sin->sin_family = AF_INET;
+
+ /* LINTED - alignment OK (32bit) */
+ sin = (struct sockaddr_in *)&route.rt_gateway;
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = router_addr.s_addr;
+
+ route.rt_flags = RTF_GATEWAY | RTF_UP;
+
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ perror("socket");
+ return;
+ }
+ if (ioctl(s, SIOCADDRT, (char *)&route) == -1) {
+ perror("add default route");
+ return;
+ }
+ (void) close(s);
+}
+
+
+int
+bpanswer(res, nb)
+ struct bp_whoami_res *res;
+ struct netbuf *nb;
+{
+ struct in_addr router_addr;
+ static int set;
+ int len;
+ char errbuf[MAX_MACHINE_NAME + 28];
+ /* MAX_MACHINE_NAME + strlen ("sysinfo(SI_SET_HOSTNAME)()") + null */
+
+ (void) memcpy(&router_addr, &res->router_address.bp_address_u.ip_addr,
+ sizeof (router_addr));
+
+ if (verbose) {
+ struct sockaddr_in *addr;
+
+ if (nb) {
+ /* LINTED - alignment (32bit) OK */
+ addr = (struct sockaddr_in *)nb->buf;
+ fprintf(stdout, "From [%s]: ",
+ inet_ntoa(addr->sin_addr));
+ } else {
+ fprintf(stdout, "Reply:\\t\\t");
+ }
+ fprintf(stdout, "hostname = %s\n", res->client_name);
+ fprintf(stdout, "\t\typdomain = %s\n", res->domain_name);
+ fprintf(stdout, "\t\trouter = %s\n", inet_ntoa(router_addr));
+ }
+
+ if (!safe && !set) {
+ /*
+ * Stuff the values from the RPC reply into the kernel.
+ * Only allow one pass through this code; There's no reason
+ * why all replies should tweak the kernel.
+ */
+ set++;
+
+ len = strlen(res->client_name);
+ if (len != 0) {
+ if (!echo_host) {
+ if (sysinfo(SI_SET_HOSTNAME, res->client_name,
+ len) < 0) {
+ (void) snprintf(errbuf, sizeof (errbuf),
+ "sysinfo(SI_SET_HOSTNAME)(%s)",
+ res->client_name);
+ perror(errbuf);
+ }
+ } else
+ (void) fprintf(stdout, "%s\n",
+ res->client_name);
+ }
+
+ len = strlen(res->domain_name);
+ if (len != 0) {
+ if (setdomainname(res->domain_name, len) == -1) {
+ (void) snprintf(errbuf, sizeof (errbuf),
+ "setdomainname(%s)", res->domain_name);
+ perror(errbuf);
+ }
+ }
+
+ /* we really should validate this router value */
+ if (router_addr.s_addr != 0)
+ add_default_route(router_addr);
+ }
+
+ if (multiple)
+ return (NULL);
+
+ /* our job is done */
+ exit(0);
+ /* NOTREACHED */
+}
+
+void
+bp_whoami(device, addr)
+ char *device;
+ struct in_addr addr;
+{
+ struct bp_whoami_arg req;
+ struct bp_whoami_res res;
+ struct in_addr lookupaddr;
+ enum clnt_stat stat;
+ int val = 1;
+
+ if (debug)
+ fprintf(stdout, "bp_whoami on interface %s addr %s\n", device,
+ inet_ntoa(addr));
+
+ if (addr.s_addr == 0) {
+ if (get_ifdata(device, &lookupaddr, &if_netmask) == -1)
+ exit(1);
+ } else
+ (void) memcpy(&lookupaddr, &addr, sizeof (addr));
+
+ lookupaddr.s_addr = ntohl(lookupaddr.s_addr);
+
+ if (debug)
+ fprintf(stdout, "lookup address is %s\n",
+ inet_ntoa(lookupaddr));
+
+ (void) memset(&req, 0, sizeof (req));
+ (void) memset(&res, 0, sizeof (res));
+
+ req.client_address.address_type = IP_ADDR_TYPE;
+ (void) memcpy(&req.client_address.bp_address_u.ip_addr, &lookupaddr,
+ sizeof (lookupaddr));
+
+ /*
+ * Broadcast using portmap version number 2 ONLY to
+ * prevent broadcast storm
+ */
+
+ (void) __rpc_control(CLCR_SET_LOWVERS, &val);
+
+ stat = rpc_broadcast(BOOTPARAMPROG, BOOTPARAMVERS, BOOTPARAMPROC_WHOAMI,
+ xdr_bp_whoami_arg, (caddr_t)&req, xdr_bp_whoami_res, (caddr_t)&res,
+ (resultproc_t)bpanswer, "udp");
+
+ /* Now try version 3 as well */
+
+ val = 0;
+ (void) __rpc_control(CLCR_SET_LOWVERS, &val);
+
+ stat = rpc_broadcast(BOOTPARAMPROG, BOOTPARAMVERS,
+ BOOTPARAMPROC_WHOAMI, xdr_bp_whoami_arg, (caddr_t)&req,
+ xdr_bp_whoami_res, (caddr_t)&res, (resultproc_t)bpanswer, "udp");
+
+ if (stat != RPC_SUCCESS) {
+ clnt_perrno(stat);
+ exit(1);
+ }
+}
+
+
+/*
+ * Get IP address of an interface. As long as we are looking, get the
+ * netmask as well.
+ */
+int
+get_ifdata(dev, ipp, maskp)
+ char *dev;
+ ulong_t *ipp, *maskp;
+{
+ struct ifreq ifr;
+ /* LINTED - alignment OK (32bit) */
+ struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr;
+ int s;
+
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ perror("socket");
+ return (-1);
+ }
+
+ if (strlcpy(ifr.ifr_name, dev, sizeof (ifr.ifr_name)) >=
+ sizeof (ifr.ifr_name)) {
+ (void) fprintf(stderr, "Device name too long %s\n",
+ dev);
+ return (-1);
+ }
+
+ if (ipp) {
+ if (ioctl(s, SIOCGIFADDR, (caddr_t)&ifr) < 0) {
+ perror("ioctl(SIOCGIFADDR)");
+ return (-1);
+ }
+ *ipp = ntohl(sin->sin_addr.s_addr);
+
+ if (debug)
+ (void) fprintf(stderr, "Interface '%s' address %s\n",
+ dev, inet_ntoa(sin->sin_addr));
+ }
+
+ if (maskp) {
+ if (ioctl(s, SIOCGIFNETMASK, (caddr_t)&ifr) < 0) {
+ perror("SIOCGIFNETMASK");
+ return (-1);
+ }
+ *maskp = ntohl(sin->sin_addr.s_addr);
+
+ if (debug)
+ (void) fprintf(stderr,
+ "Interface '%s' subnet mask %s\n", dev,
+ inet_ntoa(sin->sin_addr));
+ }
+
+ (void) close(s);
+ return (0);
+}
+
+void
+notsupported()
+{
+ fprintf(stderr, "requested protocol is not supported\n");
+ exit(1);
+}
+
+void
+usage(cmdname)
+ char *cmdname;
+{
+ (void) fprintf(stderr, "usage: %s [-v] [-n] [-m] [-h] [<ifname>] "
+ "[-f <hostname>] -p bootparams|bootp\n", cmdname);
+ (void) fflush(stderr);
+ exit(1);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/htable/Makefile b/usr/src/cmd/cmd-inet/usr.sbin/htable/Makefile
new file mode 100644
index 0000000000..259836d39b
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/htable/Makefile
@@ -0,0 +1,58 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 1989 by Sun Microsystems, Inc.
+#
+# cmd/cmd-inet/usr.sbin/htable/Makefile
+
+PROG= htable
+OBJS= htable.o parse.o scan.o
+SRCS= htable.c parse.y scan.l
+
+include ../../../Makefile.cmd
+
+CPPFLAGS += -DSYSV -DSTRNET -DBSD_COMP
+LDLIBS += -lsocket -lnsl
+YFLAGS += -d
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+ $(LINK.c) $(OBJS) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+install: all $(ROOTUSRSBINPROG)
+
+clean:
+ $(RM) $(OBJS) y.tab.h y.tab.c parse.c scan.c
+
+lint: lint_PROG
+
+include ../../../Makefile.targ
+
+# explicit target for the NSE
+y.tab.h: parse.y
+ $(YACC.y) $?
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/htable/htable.c b/usr/src/cmd/cmd-inet/usr.sbin/htable/htable.c
new file mode 100644
index 0000000000..0745a1ad79
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/htable/htable.c
@@ -0,0 +1,613 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1989 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * Portions of this source code were derived from Berkeley 4.3 BSD
+ * under license from the Regents of the University of California.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * htable - convert NIC host table into a UNIX format.
+ * DoD Internet host table format is specified in RFC 952, October 1985
+ */
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+#include <netdb.h>
+
+#include "htable.h" /* includes <sys/types.h> */
+
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#define DATELINES 3 /* these lines usually contain the date */
+#define MAXNETS 30 /* array size for local, connected nets */
+
+FILE *hf; /* hosts file */
+FILE *gf; /* gateways file */
+FILE *nf; /* networks file */
+struct gateway *savegateway(), *gatewayto();
+
+int connected_nets[MAXNETS];
+int nconnected;
+int local_nets[MAXNETS];
+int nlocal;
+char *myname;
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int errs;
+
+ infile = "(stdin)";
+ myname = argv[0];
+ argc--;
+ argv++;
+ while (argc--) {
+ if (*argv[0] == '-') {
+ switch (argv[0][1]) {
+ case 'c':
+ nconnected = addlocal(argv[1], connected_nets);
+ argv++;
+ argc--;
+ break;
+ case 'l':
+ nlocal = addlocal(argv[1], local_nets);
+ argv++;
+ argc--;
+ break;
+ default:
+ usage();
+ /*NOTREACHED*/
+ }
+ } else {
+ infile = argv[0];
+ if (freopen(infile, "r", stdin) == NULL) {
+ perror(infile);
+ exit(1);
+ }
+ }
+ argv++;
+ }
+ hf = fopen("hosts", "w");
+ if (hf == NULL) {
+ perror("hosts");
+ exit(1);
+ }
+ copylocal(hf, "localhosts");
+ gf = fopen("gateways", "w");
+ if (gf == NULL) {
+ perror("gateways");
+ exit(1);
+ }
+ copygateways("localgateways");
+ nf = fopen("networks", "w");
+ if (nf == NULL) {
+ perror("networks");
+ exit(1);
+ }
+ copylocal(nf, "localnetworks");
+ copycomments(stdin, hf, DATELINES);
+ errs = yyparse();
+ dogateways();
+ exit(errs);
+ /* NOTREACHED */
+}
+
+usage()
+{
+ fprintf(stderr,
+ "usage: %s [ -c connected-nets ] [-l local-nets ] [ input-file ]\n",
+ myname);
+ exit(1);
+}
+
+/*
+ * Turn a comma-separated list of network names or numbers in dot notation
+ * (e.g. "arpanet, 128.32") into an array of net numbers.
+ */
+addlocal(arg, nets)
+ char *arg;
+ int *nets;
+{
+ register char *p, c;
+ register int nfound = 0;
+
+ do {
+ p = arg;
+ while (*p && *p != ',' && !isspace(*p))
+ p++;
+ c = *p;
+ *p = 0;
+ while (*arg && isspace(*arg))
+ arg++;
+ if (*arg == 0)
+ continue;
+ if (nfound == MAXNETS) {
+ fprintf(stderr, "%s: Too many networks in list\n",
+ myname);
+ return (nfound);
+ }
+ if (getnetaddr(arg, &nets[nfound]))
+ nfound++;
+ else {
+ fprintf(stderr, "%s: %s: unknown network\n",
+ myname, arg);
+ exit(1);
+ }
+ arg = p + 1;
+ } while (c);
+ return (nfound);
+}
+
+struct name *
+newname(str)
+ char *str;
+{
+ char *p;
+ struct name *nm;
+
+ p = malloc(strlen(str) + 1);
+ strcpy(p, str);
+ nm = (struct name *)malloc(sizeof (struct name));
+ nm->name_val = p;
+ nm->name_link = NONAME;
+ return (nm);
+}
+
+char *
+lower(str)
+ char *str;
+{
+ register char *cp = str;
+
+ while (*cp) {
+ if (isupper(*cp))
+ *cp = tolower(*cp);
+ cp++;
+ }
+ return (str);
+}
+
+do_entry(keyword, addrlist, namelist, cputype, opsys, protos)
+ int keyword;
+ struct addr *addrlist;
+ struct name *namelist, *cputype, *opsys, *protos;
+{
+ register struct addr *al, *al2;
+ register struct name *nl;
+ struct addr *connect_addr;
+ char *cp;
+
+ switch (keyword) {
+
+ case KW_NET:
+ nl = namelist;
+ if (nl == NONAME) {
+ fprintf(stderr, "htable: net ");
+ putnet(stderr, inet_netof(addrlist->addr_val));
+ fprintf(stderr, " missing names.\n");
+ break;
+ }
+ fprintf(nf, "%-16s ", lower(nl->name_val));
+ al2 = addrlist;
+ while (al = al2) {
+ char *cp;
+
+ putnet(nf, inet_netof(al->addr_val));
+ cp = "\t%s";
+ while (nl = nl->name_link) {
+ fprintf(nf, cp, lower(nl->name_val));
+ cp = " %s";
+ }
+ putc('\n', nf);
+ al2 = al->addr_link;
+ free((char *)al);
+ }
+ break;
+
+ case KW_GATEWAY:
+ /* locate locally connected address, if one */
+ for (al = addrlist; al; al = al->addr_link)
+ if (connectedto(inet_netof(al->addr_val)))
+ break;
+ if (al == NULL) {
+ /*
+ * Not connected to known networks. Save for later.
+ */
+ struct gateway *gw, *firstgw = (struct gateway *) NULL;
+
+ for (al = addrlist; al; al = al->addr_link) {
+ register int net;
+
+ net = inet_netof(al->addr_val);
+ gw = savegateway(namelist, net,
+ al->addr_val, 0);
+ if (firstgw == (struct gateway *) NULL)
+ firstgw = gw;
+ gw->g_firstent = firstgw;
+ }
+ freeaddrs(addrlist);
+ goto dontfree;
+ }
+ /*
+ * Connected to a known network.
+ * Mark this as the gateway to all other networks
+ * that are on the addrlist (unless we already have
+ * gateways to them).
+ */
+ connect_addr = al;
+ for (al = addrlist; al; al = al->addr_link) {
+ register int net;
+
+ /* suppress duplicates -- not optimal */
+ net = inet_netof(al->addr_val);
+ if (connectedto(net) || gatewayto(net))
+ continue;
+ printgateway(net, namelist->name_val, 1);
+ (void) savegateway(namelist, net, al->addr_val, 1);
+ }
+ /*
+ * Put the gateway in the hosts file.
+ */
+ putaddr(hf, connect_addr->addr_val);
+ cp = "%s";
+ for (nl = namelist; nl; nl = nl->name_link) {
+ fprintf(hf, cp, lower(nl->name_val));
+ cp = " %s";
+ }
+ fprintf(hf, "\t# gateway\n");
+ freeaddrs(addrlist);
+ goto dontfree;
+
+ case KW_HOST:
+ al2 = addrlist;
+ while (al = al2) {
+ if (!local(inet_netof(al->addr_val))) {
+ char *cp;
+
+ putaddr(hf, al->addr_val);
+ cp = "%s";
+ for (nl = namelist; nl; nl = nl->name_link) {
+ fprintf(hf, cp, lower(nl->name_val));
+ cp = " %s";
+ }
+ putc('\n', hf);
+ }
+ al2 = al->addr_link;
+ free((char *)al);
+ }
+ break;
+
+ default:
+ fprintf(stderr, "Unknown keyword: %d.\n", keyword);
+ }
+ freenames(namelist);
+dontfree:
+ freenames(protos);
+}
+
+printgateway(net, name, metric)
+ int net;
+ char *name;
+ int metric;
+{
+ struct netent *np;
+
+ fprintf(gf, "net ");
+ np = getnetbyaddr(net, AF_INET);
+ if (np)
+ fprintf(gf, "%s", np->n_name);
+ else
+ putnet(gf, net);
+ fprintf(gf, " gateway %s metric %d passive\n",
+ lower(name), metric);
+}
+
+copylocal(f, filename)
+ FILE *f;
+ char *filename;
+{
+ register FILE *lhf;
+ register cc;
+ char buf[BUFSIZ];
+ extern int errno;
+
+ lhf = fopen(filename, "r");
+ if (lhf == NULL) {
+ if (errno != ENOENT) {
+ perror(filename);
+ exit(1);
+ }
+ fprintf(stderr, "Warning, no %s file.\n", filename);
+ return;
+ }
+ while (cc = fread(buf, 1, sizeof(buf), lhf))
+ fwrite(buf, 1, cc, f);
+ fclose(lhf);
+}
+
+copygateways(filename)
+ char *filename;
+{
+ register FILE *lhf;
+ struct name *nl;
+ char type[80];
+ char dname[80];
+ char gname[80];
+ char junk[80];
+ char buf[500];
+ struct in_addr addr;
+ int net, metric;
+ extern int errno;
+
+ lhf = fopen(filename, "r");
+ if (lhf == NULL) {
+ if (errno != ENOENT) {
+ perror(filename);
+ exit(1);
+ }
+ fprintf(stderr, "Warning, no %s file.\n", filename);
+ return;
+ }
+ /* format: {net | host} XX gateway XX metric DD [passive]\n */
+ for (;;) {
+ junk[0] = 0;
+ if (fgets(buf, sizeof(buf), lhf) == (char *)NULL)
+ break;
+ fputs(buf, gf);
+ if (buf[0] == '#' ||
+ sscanf(buf, "%s %s gateway %s metric %d %s",
+ type, dname, gname, &metric, junk) < 5)
+ continue;
+ if (strcmp(type, "net"))
+ continue;
+ if (!getnetaddr(dname, &net))
+ continue;
+ if (!gethostaddr(gname, &addr.s_addr))
+ continue;
+ nl = newname(gname);
+ (void) savegateway(nl, net, addr, metric);
+ }
+ fclose(lhf);
+}
+
+getnetaddr(name, addr)
+ char *name;
+ int *addr;
+{
+ struct netent *np = getnetbyname(name);
+
+ if (np == 0) {
+ *addr = inet_network(name);
+ return (*addr != -1);
+ } else {
+ if (np->n_addrtype != AF_INET)
+ return (0);
+ *addr = np->n_net;
+ return (1);
+ }
+}
+
+gethostaddr(name, addr)
+ char *name;
+ u_long *addr;
+{
+ struct hostent *hp;
+
+ hp = gethostbyname(name);
+ if (hp) {
+ *addr = *(u_long *)(hp->h_addr);
+ return (1);
+ }
+ *addr = inet_addr(name);
+ return (*addr != -1);
+}
+
+copycomments(in, out, ccount)
+ FILE *in, *out;
+ int ccount;
+{
+ char buf[BUFSIZ];
+ int count;
+ char *fgets();
+
+ for (count=0; count < ccount; count++) {
+ if ((fgets(buf, sizeof(buf), in) == NULL) || (buf[0] != ';'))
+ return;
+ buf[0] = '#';
+ fputs(buf, out);
+ }
+ return;
+}
+#define UC(b) (((int)(b))&0xff)
+
+/*
+ * Print network number in internet-standard dot notation;
+ * v is in host byte order.
+ */
+putnet(f, v)
+ FILE *f;
+ register int v;
+{
+ if (v < 128)
+ fprintf(f, "%d", v);
+ else if (v < 65536)
+ fprintf(f, "%d.%d", UC(v >> 8), UC(v));
+ else
+ fprintf(f, "%d.%d.%d", UC(v >> 16), UC(v >> 8), UC(v));
+}
+
+putaddr(f, v)
+ FILE *f;
+ struct in_addr v;
+{
+ fprintf(f, "%-16.16s", inet_ntoa(v));
+}
+
+freenames(list)
+ struct name *list;
+{
+ register struct name *nl, *nl2;
+
+ nl2 = list;
+ while (nl = nl2) {
+ nl2 = nl->name_link;
+ free(nl->name_val);
+ free((char *)nl);
+ }
+}
+
+freeaddrs(list)
+ struct addr *list;
+{
+ register struct addr *al, *al2;
+
+ al2 = list;
+ while (al = al2)
+ al2 = al->addr_link, free((char *)al);
+}
+
+struct gateway *gateways = 0;
+struct gateway *lastgateway = 0;
+
+struct gateway *
+gatewayto(net)
+ register int net;
+{
+ register struct gateway *gp;
+
+ for (gp = gateways; gp; gp = gp->g_link)
+ if ((gp->g_net == net) && (gp->g_metric > 0))
+ return (gp);
+ return ((struct gateway *) NULL);
+}
+
+struct gateway *
+savegateway(namelist, net, addr, metric)
+ struct name *namelist;
+ struct in_addr addr;
+ int net, metric;
+{
+ register struct gateway *gp;
+
+ gp = (struct gateway *)malloc(sizeof (struct gateway));
+ if (gp == 0) {
+ fprintf(stderr, "htable: out of memory\n");
+ exit(1);
+ }
+ gp->g_link = (struct gateway *) NULL;
+ if (lastgateway)
+ lastgateway->g_link = gp;
+ else
+ gateways = gp;
+ lastgateway = gp;
+ gp->g_name = namelist;
+ gp->g_net = net;
+ gp->g_addr = addr;
+ gp->g_metric = metric;
+ if (metric == 1)
+ gp->g_dst = gp;
+ return (gp);
+}
+
+connectedto(net)
+ int net;
+{
+ register i;
+
+ for (i = 0; i < nconnected; i++)
+ if (connected_nets[i] == net)
+ return(1);
+ return(0);
+}
+
+local(net)
+ int net;
+{
+ register i;
+
+ for (i = 0; i < nlocal; i++)
+ if (local_nets[i] == net)
+ return(1);
+ return(0);
+}
+
+#define MAXHOPS 10
+
+/*
+ * Go through list of gateways, finding connections for gateways
+ * that are not yet connected.
+ */
+dogateways()
+{
+ register struct gateway *gp, *gw, *ggp;
+ register int hops, changed = 1;
+ struct name *nl;
+ char *cp;
+
+ for (hops = 0; hops < MAXHOPS && changed; hops++, changed = 0) {
+ for (gp = gateways; gp; gp = gp->g_link)
+ if ((gp->g_metric == 0) && (gw = gatewayto(gp->g_net))) {
+ /*
+ * Found a new connection.
+ * For each other network that this gateway is on,
+ * add a new gateway to that network.
+ */
+ changed = 1;
+ gp->g_dst = gw->g_dst;
+ gp->g_metric = gw->g_metric + 1;
+ for (ggp = gp->g_firstent; ggp->g_name == gp->g_name;
+ ggp = ggp->g_link) {
+ if (ggp == gp)
+ continue;
+ if (gatewayto(ggp->g_net))
+ continue;
+ ggp->g_dst = gp->g_dst;
+ ggp->g_metric = gp->g_metric;
+ printgateway(ggp->g_net,
+ gw->g_dst->g_name->name_val, gp->g_metric);
+ }
+ /*
+ * Put the gateway in the hosts file,
+ * using the address for the connected net.
+ */
+ putaddr(hf, gp->g_addr);
+ cp = "%s";
+ for (nl = gp->g_name; nl; nl = nl->name_link) {
+ fprintf(hf, cp, lower(nl->name_val));
+ cp = " %s";
+ }
+ fprintf(hf, "\t# gateway\n");
+ }
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/htable/htable.h b/usr/src/cmd/cmd-inet/usr.sbin/htable/htable.h
new file mode 100644
index 0000000000..695652c609
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/htable/htable.h
@@ -0,0 +1,74 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1989 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * Portions of this source code were derived from Berkeley 4.3 BSD
+ * under license from the Regents of the University of California.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+
+/*
+ * common definitions for htable
+ */
+
+struct addr {
+ struct in_addr addr_val;
+ struct addr *addr_link;
+};
+
+struct name {
+ char *name_val;
+ struct name *name_link;
+};
+
+struct gateway {
+ struct gateway *g_link;
+ struct gateway *g_dst; /* connected gateway if metric > 0 */
+ struct gateway *g_firstent; /* first entry for this gateway */
+ struct name *g_name;
+ int g_net;
+ struct in_addr g_addr; /* address on g_net */
+ int g_metric; /* hops to this net */
+};
+
+#define NOADDR ((struct addr *)0)
+#define NONAME ((struct name *)0)
+
+#define KW_NET 1
+#define KW_GATEWAY 2
+#define KW_HOST 3
+
+struct name *newname();
+
+char *infile; /* Input file name */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/htable/parse.y b/usr/src/cmd/cmd-inet/usr.sbin/htable/parse.y
new file mode 100644
index 0000000000..f3e66580a5
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/htable/parse.y
@@ -0,0 +1,164 @@
+%{
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+%}
+/*
+ * Copyright 1989 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * Portions of this source code were derived from Berkeley 4.3 BSD
+ * under license from the Regents of the University of California.
+ */
+
+%{
+#ident "%Z%%M% %I% %E% SMI"
+
+#include "htable.h"
+%}
+
+%union {
+ int number;
+ struct addr *addrlist;
+ struct name *namelist;
+}
+%start Table
+
+%token END
+%token <number> NUMBER KEYWORD
+%token <namelist> NAME
+
+%type <namelist> Names Cputype Opsys Protos Proto
+%type <addrlist> Addresses Address
+%%
+Table : Entry
+ | Table Entry
+ ;
+
+Entry : KEYWORD ':' Addresses ':' Names ':' END
+ = {
+ do_entry($1, $3, $5, NONAME, NONAME, NONAME);
+ }
+ | KEYWORD ':' Addresses ':' Names ':' Cputype ':' END
+ = {
+ do_entry($1, $3, $5, $7, NONAME, NONAME);
+ }
+ | KEYWORD ':' Addresses ':' Names ':' Cputype ':' Opsys ':' END
+ = {
+ do_entry($1, $3, $5, $7, $9, NONAME);
+ }
+ | KEYWORD ':' Addresses ':' Names ':' Cputype ':' Opsys ':' ':' END
+ = {
+ do_entry($1, $3, $5, $7, $9, NONAME);
+ }
+ | KEYWORD ':' Addresses ':' Names ':' Cputype ':' Opsys ':' Protos ':' END
+ = {
+ do_entry($1, $3, $5, $7, $9, $11);
+ }
+ | error END
+ | END /* blank line */
+ ;
+
+Addresses: Address
+ = {
+ $$ = $1;
+ }
+ | Address ',' Addresses
+ = {
+ $1->addr_link = $3;
+ $$ = $1;
+ }
+ ;
+
+Address : NUMBER '.' NUMBER '.' NUMBER '.' NUMBER
+ = {
+ char *a;
+
+ $$ = (struct addr *)malloc(sizeof (struct addr));
+ a = (char *)&($$->addr_val);
+ a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
+ $$->addr_link = NOADDR;
+ }
+ ;
+
+Names : NAME
+ = {
+ $$ = $1;
+ }
+ | NAME ',' Names
+ = {
+ $1->name_link = $3;
+ $$ = $1;
+ }
+ ;
+
+Cputype : /* empty */
+ = {
+ $$ = NONAME;
+ }
+ | NAME
+ = {
+ $$ = $1;
+ }
+ ;
+
+Opsys : /* empty */
+ = {
+ $$ = NONAME;
+ }
+ | NAME
+ = {
+ $$ = $1;
+ }
+ ;
+
+Protos : Proto
+ = {
+ $$ = $1;
+ }
+ | Proto ',' Protos
+ = {
+ $1->name_link = $3;
+ $$ = $1;
+ }
+ ;
+
+Proto : NAME
+ = {
+ $$ = $1;
+ }
+ ;
+%%
+
+#include <stdio.h>
+
+extern int yylineno;
+
+yyerror(msg)
+ char *msg;
+{
+ fprintf(stderr, "\"%s\", line %d: %s\n", infile, yylineno, msg);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/htable/scan.l b/usr/src/cmd/cmd-inet/usr.sbin/htable/scan.l
new file mode 100644
index 0000000000..8a1dae41a6
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/htable/scan.l
@@ -0,0 +1,109 @@
+%{
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ * Copyright 1989 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+%}
+
+
+%{
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+%}
+%{
+/* All Rights Reserved */
+%}
+
+%{
+/*
+ * Portions of this source code were derived from Berkeley 4.3 BSD
+ * under license from the Regents of the University of California.
+ */
+%}
+
+%{
+#ident "%Z%%M% %I% %E% SMI"
+
+#include "y.tab.h"
+#include "htable.h"
+%}
+
+BLANK [ \t]
+DIGIT [0-9]
+ALPHA [A-Za-z]
+ANUM [0-9A-Za-z]
+NAMECHR [0-9A-Za-z./-]
+
+%%
+"NET" {
+ yylval.number = KW_NET;
+ return (KEYWORD);
+ }
+
+"GATEWAY" {
+ yylval.number = KW_GATEWAY;
+ return (KEYWORD);
+ }
+
+"HOST" {
+ yylval.number = KW_HOST;
+ return (KEYWORD);
+ }
+
+{ALPHA}{NAMECHR}*{ANUM} {
+ yylval.namelist = newname(yytext);
+ return (NAME);
+ }
+
+{ALPHA} {
+ yylval.namelist = newname(yytext);
+ return (NAME);
+ }
+
+{DIGIT}+{ALPHA}{NAMECHR}* {
+ fprintf(stderr, "Warning: nonstandard name \"%s\"\n",
+ yytext);
+ yylval.namelist = newname(yytext);
+ return (NAME);
+ }
+
+{DIGIT}+ {
+ yylval.number = atoi(yytext);
+ return (NUMBER);
+ }
+
+"." return ('.');
+":" return (':');
+"," return (',');
+"/" return ('/');
+";".* ;
+"\n"{BLANK}+ ;
+{BLANK}+ ;
+"\n" return (END);
+. fprintf(stderr, "Illegal char: '%s'\n", yytext);
+
+%%
+
+yywrap()
+{
+ return (1);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/if_mpadm.c b/usr/src/cmd/cmd-inet/usr.sbin/if_mpadm.c
new file mode 100644
index 0000000000..fc703c7b2e
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/if_mpadm.c
@@ -0,0 +1,673 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2000-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <errno.h>
+#include <strings.h>
+#include <ipmp_mpathd.h>
+#include <libintl.h>
+
+static int if_down(int ifsock, struct lifreq *lifr);
+static int if_up(int ifsock, struct lifreq *lifr);
+static void send_cmd(int cmd, char *ifname);
+static int connect_to_mpathd(sa_family_t family);
+static void do_offline(char *ifname);
+static void undo_offline(char *ifname);
+static boolean_t offline_set(char *ifname);
+
+#define IF_SEPARATOR ':'
+#define MAX_RETRIES 3
+
+static void
+usage()
+{
+ fprintf(stderr, "Usage : if_mpadm [-d | -r] <interface_name>\n");
+}
+
+static void
+print_mpathd_error_msg(uint32_t error)
+{
+ switch (error) {
+ case MPATHD_MIN_RED_ERROR:
+ (void) fprintf(stderr, gettext(
+ "Offline failed as there is no other functional "
+ "interface available in the multipathing group "
+ "for failing over the network access.\n"));
+ break;
+
+ case MPATHD_FAILBACK_PARTIAL:
+ (void) fprintf(stderr, gettext(
+ "Offline cannot be undone because multipathing "
+ "configuration is not consistent across all the "
+ "interfaces in the group.\n"));
+ break;
+
+ default:
+ /*
+ * We shouldn't get here. All errors should have a
+ * meaningful error message, as shown in the above
+ * cases. If we get here, someone has made a mistake.
+ */
+ (void) fprintf(stderr, gettext(
+ "Operation returned an unrecognized error: %u\n"),
+ error);
+ break;
+ }
+}
+
+main(int argc, char **argv)
+{
+ char *ifname;
+ int cmd = 0;
+ int c;
+
+#if !defined(TEXT_DOMAIN)
+#define TEXT_DOMAIN "SYS_TEST"
+#endif
+ (void) textdomain(TEXT_DOMAIN);
+
+ while ((c = getopt(argc, argv, "d:r:")) != EOF) {
+ switch (c) {
+ case 'd':
+ ifname = optarg;
+ cmd = MI_OFFLINE;
+ if (offline_set(ifname)) {
+ fprintf(stderr, gettext("Interface already "
+ "offlined\n"));
+ exit(1);
+ }
+ break;
+ case 'r':
+ ifname = optarg;
+ cmd = MI_UNDO_OFFLINE;
+ if (!offline_set(ifname)) {
+ fprintf(stderr, gettext("Interface not "
+ "offlined\n"));
+ exit(1);
+ }
+ break;
+ default :
+ usage();
+ exit(1);
+ }
+ }
+
+ if (cmd == 0) {
+ usage();
+ exit(1);
+ }
+
+ /*
+ * Send the command to in.mpathd which is generic to
+ * both the commands. send_cmd returns only if there
+ * is no error.
+ */
+ send_cmd(cmd, ifname);
+ if (cmd == MI_OFFLINE) {
+ do_offline(ifname);
+ } else {
+ undo_offline(ifname);
+ }
+
+ return (0);
+}
+
+/*
+ * Is IFF_OFFLINE set ?
+ * Returns B_FALSE on failure and B_TRUE on success.
+ */
+boolean_t
+offline_set(char *ifname)
+{
+ struct lifreq lifr;
+ int s4;
+ int s6;
+ int ret;
+
+ s4 = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s4 < 0) {
+ perror("socket");
+ exit(1);
+ }
+ s6 = socket(AF_INET6, SOCK_DGRAM, 0);
+ if (s6 < 0) {
+ perror("socket");
+ exit(1);
+ }
+ (void) strncpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
+ ret = ioctl(s4, SIOCGLIFFLAGS, (caddr_t)&lifr);
+ if (ret < 0) {
+ if (errno != ENXIO) {
+ perror("ioctl: SIOCGLIFFLAGS");
+ exit(1);
+ }
+ ret = ioctl(s6, SIOCGLIFFLAGS, (caddr_t)&lifr);
+ if (ret < 0) {
+ perror("ioctl: SIOCGLIFFLAGS");
+ exit(1);
+ }
+ }
+ (void) close(s4);
+ (void) close(s6);
+ if (lifr.lifr_flags & IFF_OFFLINE)
+ return (B_TRUE);
+ else
+ return (B_FALSE);
+}
+
+/*
+ * Sends the command to in.mpathd. If not successful, prints
+ * an error message and exits.
+ */
+void
+send_cmd(int cmd, char *ifname)
+{
+ struct mi_offline mio;
+ struct mi_undo_offline miu;
+ struct mi_result me;
+ int ret;
+ int cmd_len;
+ int i;
+ int s;
+
+ for (i = 0; i < MAX_RETRIES; i++) {
+ s = connect_to_mpathd(AF_INET);
+ if (s == -1) {
+ s = connect_to_mpathd(AF_INET6);
+ if (s == -1) {
+ fprintf(stderr, gettext("Cannot establish "
+ "communication with in.mpathd.\n"));
+ exit(1);
+ }
+ }
+ switch (cmd) {
+ case MI_OFFLINE :
+ cmd_len = sizeof (struct mi_offline);
+ bzero(&mio, cmd_len);
+ mio.mio_command = cmd;
+ (void) strncpy(mio.mio_ifname, ifname, LIFNAMSIZ);
+ mio.mio_min_redundancy = 1;
+ ret = write(s, &mio, cmd_len);
+ if (ret != cmd_len) {
+ /* errno is set only when ret is -1 */
+ if (ret == -1)
+ perror("write");
+ fprintf(stderr, gettext("Failed to "
+ "successfully send command to "
+ "in.mpathd.\n"));
+ exit(1);
+ }
+ break;
+ case MI_UNDO_OFFLINE:
+ cmd_len = sizeof (struct mi_undo_offline);
+ bzero(&miu, cmd_len);
+ miu.miu_command = cmd;
+ (void) strncpy(miu.miu_ifname, ifname, LIFNAMSIZ);
+ ret = write(s, &miu, cmd_len);
+ if (ret != cmd_len) {
+ /* errno is set only when ret is -1 */
+ if (ret == -1)
+ perror("write");
+ fprintf(stderr, gettext("Failed to "
+ "successfully send command to "
+ "in.mpathd.\n"));
+ exit(1);
+ }
+ break;
+ default :
+ fprintf(stderr, "Unknown command \n");
+ exit(1);
+ }
+
+ /* Read the result from mpathd */
+ ret = read(s, &me, sizeof (me));
+ if (ret != sizeof (me)) {
+ /* errno is set only when ret is -1 */
+ if (ret == -1)
+ perror("read");
+ fprintf(stderr, gettext("Failed to successfully read "
+ "result from in.mpathd.\n"));
+ exit(1);
+ }
+ if (me.me_mpathd_error == 0) {
+ if (i != 0) {
+ /*
+ * We retried at least once. Tell the user
+ * that things succeeded now.
+ */
+ fprintf(stderr, gettext("Retry Successful.\n"));
+ }
+ return; /* Successful */
+ }
+
+ if (me.me_mpathd_error == MPATHD_SYS_ERROR) {
+ if (me.me_sys_error == EAGAIN) {
+ (void) close(s);
+ (void) sleep(1);
+ fprintf(stderr, gettext("Retrying ...\n"));
+ continue; /* Retry */
+ }
+ errno = me.me_sys_error;
+ perror("if_mpadm");
+ } else {
+ print_mpathd_error_msg(me.me_mpathd_error);
+ }
+ exit(1);
+ }
+ /*
+ * We come here only if we retry the operation multiple
+ * times and did not succeed. Let the user try it again
+ * later.
+ */
+ fprintf(stderr, gettext("Device busy. Retry the operation later.\n"));
+ exit(1);
+}
+
+static void
+do_offline(char *ifname)
+{
+ struct lifreq lifr;
+ struct lifreq *lifcr;
+ struct lifnum lifn;
+ struct lifconf lifc;
+ char *buf;
+ int numifs;
+ int n;
+ char pi_name[LIFNAMSIZ + 1];
+ char *cp;
+ int ifsock_v4;
+ int ifsock_v6;
+ int af;
+ int ret;
+
+ /*
+ * Verify whether IFF_OFFLINE is not set as a sanity check.
+ */
+ if (!offline_set(ifname)) {
+ fprintf(stderr, gettext("Operation failed : in.mpathd has "
+ "not set IFF_OFFLINE on %s\n"), ifname);
+ exit(1);
+ }
+ /*
+ * Get both the sockets as we may need to bring both
+ * IPv4 and IPv6 interfaces down.
+ */
+ ifsock_v4 = socket(AF_INET, SOCK_DGRAM, 0);
+ if (ifsock_v4 < 0) {
+ perror("socket");
+ exit(1);
+ }
+ ifsock_v6 = socket(AF_INET6, SOCK_DGRAM, 0);
+ if (ifsock_v6 < 0) {
+ perror("socket");
+ exit(1);
+ }
+ /*
+ * Get all the logicals for "ifname" and mark them down.
+ * There is no easy way of doing this. We get all the
+ * interfaces in the system using SICGLIFCONF and mark the
+ * ones matching the name down.
+ */
+ lifn.lifn_family = AF_UNSPEC;
+ lifn.lifn_flags = 0;
+ if (ioctl(ifsock_v4, SIOCGLIFNUM, (char *)&lifn) < 0) {
+ perror("ioctl : SIOCGLIFNUM");
+ exit(1);
+ }
+ numifs = lifn.lifn_count;
+
+ buf = (char *)calloc(numifs, sizeof (struct lifreq));
+ if (buf == NULL) {
+ perror("calloc");
+ exit(1);
+ }
+
+ lifc.lifc_family = AF_UNSPEC;
+ lifc.lifc_flags = 0;
+ lifc.lifc_len = numifs * sizeof (struct lifreq);
+ lifc.lifc_buf = buf;
+
+ if (ioctl(ifsock_v4, SIOCGLIFCONF, (char *)&lifc) < 0) {
+ perror("ioctl : SIOCGLIFCONF");
+ exit(1);
+ }
+
+ lifcr = (struct lifreq *)lifc.lifc_req;
+ for (n = lifc.lifc_len / sizeof (struct lifreq); n > 0; n--, lifcr++) {
+ af = lifcr->lifr_addr.ss_family;
+ (void) strncpy(pi_name, lifcr->lifr_name,
+ sizeof (pi_name));
+ pi_name[sizeof (pi_name) - 1] = '\0';
+ if ((cp = strchr(pi_name, IF_SEPARATOR)) != NULL)
+ *cp = '\0';
+ if (strcmp(pi_name, ifname) == 0) {
+ /* It matches the interface name that was offlined */
+ (void) strncpy(lifr.lifr_name, lifcr->lifr_name,
+ sizeof (lifr.lifr_name));
+ if (af == AF_INET)
+ ret = if_down(ifsock_v4, &lifr);
+ else
+ ret = if_down(ifsock_v6, &lifr);
+ if (ret != 0) {
+ fprintf(stderr, gettext("Bringing down the "
+ "interfaces failed.\n"));
+ exit(1);
+ }
+ }
+ }
+}
+
+static void
+undo_offline(char *ifname)
+{
+ struct lifreq lifr;
+ struct lifreq *lifcr;
+ struct lifnum lifn;
+ struct lifconf lifc;
+ char *buf;
+ int numifs;
+ int n;
+ char pi_name[LIFNAMSIZ + 1];
+ char *cp;
+ int ifsock_v4;
+ int ifsock_v6;
+ int af;
+ int ret;
+
+ /*
+ * Verify whether IFF_OFFLINE is set as a sanity check.
+ */
+ if (offline_set(ifname)) {
+ fprintf(stderr, gettext("Operation failed : in.mpathd has "
+ "not cleared IFF_OFFLINE on %s\n"), ifname);
+ exit(1);
+ }
+ /*
+ * Get both the sockets as we may need to bring both
+ * IPv4 and IPv6 interfaces UP.
+ */
+ ifsock_v4 = socket(AF_INET, SOCK_DGRAM, 0);
+ if (ifsock_v4 < 0) {
+ perror("socket");
+ exit(1);
+ }
+ ifsock_v6 = socket(AF_INET6, SOCK_DGRAM, 0);
+ if (ifsock_v6 < 0) {
+ perror("socket");
+ exit(1);
+ }
+ /*
+ * Get all the logicals for "ifname" and mark them up.
+ * There is no easy way of doing this. We get all the
+ * interfaces in the system using SICGLIFCONF and mark the
+ * ones matching the name up.
+ */
+ lifn.lifn_family = AF_UNSPEC;
+ lifn.lifn_flags = 0;
+ if (ioctl(ifsock_v4, SIOCGLIFNUM, (char *)&lifn) < 0) {
+ perror("ioctl : SIOCGLIFNUM");
+ exit(1);
+ }
+ numifs = lifn.lifn_count;
+
+ buf = (char *)calloc(numifs, sizeof (struct lifreq));
+ if (buf == NULL) {
+ perror("calloc");
+ exit(1);
+ }
+
+ lifc.lifc_family = AF_UNSPEC;
+ lifc.lifc_flags = 0;
+ lifc.lifc_len = numifs * sizeof (struct lifreq);
+ lifc.lifc_buf = buf;
+
+ if (ioctl(ifsock_v4, SIOCGLIFCONF, (char *)&lifc) < 0) {
+ perror("ioctl : SIOCGLIFCONF");
+ exit(1);
+ }
+
+ lifcr = (struct lifreq *)lifc.lifc_req;
+ for (n = lifc.lifc_len / sizeof (struct lifreq); n > 0; n--, lifcr++) {
+ af = lifcr->lifr_addr.ss_family;
+ (void) strncpy(pi_name, lifcr->lifr_name,
+ sizeof (pi_name));
+ pi_name[sizeof (pi_name) - 1] = '\0';
+ if ((cp = strchr(pi_name, IF_SEPARATOR)) != NULL)
+ *cp = '\0';
+
+ if (strcmp(pi_name, ifname) == 0) {
+ /* It matches the interface name that was offlined */
+ (void) strncpy(lifr.lifr_name, lifcr->lifr_name,
+ sizeof (lifr.lifr_name));
+ if (af == AF_INET)
+ ret = if_up(ifsock_v4, &lifr);
+ else
+ ret = if_up(ifsock_v6, &lifr);
+ if (ret != 0) {
+ fprintf(stderr, gettext("Bringing up the "
+ "interfaces failed.\n"));
+ exit(1);
+ }
+ }
+ }
+}
+
+/*
+ * Returns -1 on failure. Returns the socket file descriptor on
+ * success.
+ */
+static int
+connect_to_mpathd(sa_family_t family)
+{
+ int s;
+ struct sockaddr_storage ss;
+ struct sockaddr_in *sin = (struct sockaddr_in *)&ss;
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&ss;
+ struct in6_addr loopback_addr = IN6ADDR_LOOPBACK_INIT;
+ int addrlen;
+ int ret;
+ int on;
+
+ s = socket(family, SOCK_STREAM, 0);
+ if (s < 0) {
+ perror("socket");
+ return (-1);
+ }
+ bzero((char *)&ss, sizeof (ss));
+ ss.ss_family = family;
+ /*
+ * Need to bind to a privileged port. For non-root, this
+ * will fail. in.mpathd verifies that only commands coming
+ * from privileged ports succeed so that the ordinary user
+ * can't issue offline commands.
+ */
+ on = 1;
+ if (setsockopt(s, IPPROTO_TCP, TCP_ANONPRIVBIND, &on,
+ sizeof (on)) < 0) {
+ perror("setsockopt : TCP_ANONPRIVBIND");
+ exit(1);
+ }
+ switch (family) {
+ case AF_INET:
+ sin->sin_port = 0;
+ sin->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ addrlen = sizeof (struct sockaddr_in);
+ break;
+ case AF_INET6:
+ sin6->sin6_port = 0;
+ sin6->sin6_addr = loopback_addr;
+ addrlen = sizeof (struct sockaddr_in6);
+ break;
+ }
+ ret = bind(s, (struct sockaddr *)&ss, addrlen);
+ if (ret != 0) {
+ perror("bind");
+ return (-1);
+ }
+ switch (family) {
+ case AF_INET:
+ sin->sin_port = htons(MPATHD_PORT);
+ break;
+ case AF_INET6:
+ sin6->sin6_port = htons(MPATHD_PORT);
+ break;
+ }
+ ret = connect(s, (struct sockaddr *)&ss, addrlen);
+ if (ret != 0) {
+ perror("connect");
+ return (-1);
+ }
+ on = 0;
+ if (setsockopt(s, IPPROTO_TCP, TCP_ANONPRIVBIND, &on,
+ sizeof (on)) < 0) {
+ perror("setsockopt : TCP_ANONPRIVBIND");
+ return (-1);
+ }
+ return (s);
+}
+
+/*
+ * Bring down the interface specified by the name lifr->lifr_name.
+ *
+ * Returns -1 on failure. Returns 0 on success.
+ */
+static int
+if_down(int ifsock, struct lifreq *lifr)
+{
+ int ret;
+
+ ret = ioctl(ifsock, SIOCGLIFFLAGS, (caddr_t)lifr);
+ if (ret < 0) {
+ perror("ioctl: SIOCGLIFFLAGS");
+ return (-1);
+ }
+
+ /* IFF_OFFLINE was set to start with. Is it still there ? */
+ if (!(lifr->lifr_flags & (IFF_OFFLINE))) {
+ fprintf(stderr, gettext("IFF_OFFLINE disappeared on %s\n"),
+ lifr->lifr_name);
+ return (-1);
+ }
+ lifr->lifr_flags &= ~IFF_UP;
+ ret = ioctl(ifsock, SIOCSLIFFLAGS, (caddr_t)lifr);
+ if (ret < 0) {
+ perror("ioctl: SIOCSLIFFLAGS");
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * Bring up the interface specified by the name lifr->lifr_name.
+ *
+ * Returns -1 on failure. Returns 0 on success.
+ */
+static int
+if_up(int ifsock, struct lifreq *lifr)
+{
+ int ret;
+ boolean_t zeroaddr = B_FALSE;
+ struct sockaddr_in *addr;
+
+ ret = ioctl(ifsock, SIOCGLIFADDR, lifr);
+ if (ret < 0) {
+ perror("ioctl: SIOCGLIFADDR");
+ return (-1);
+ }
+
+ addr = (struct sockaddr_in *)&lifr->lifr_addr;
+ switch (addr->sin_family) {
+ case AF_INET:
+ zeroaddr = (addr->sin_addr.s_addr == INADDR_ANY);
+ break;
+
+ case AF_INET6:
+ zeroaddr = IN6_IS_ADDR_UNSPECIFIED(
+ &((struct sockaddr_in6 *)addr)->sin6_addr);
+ break;
+
+ default:
+ break;
+ }
+
+ ret = ioctl(ifsock, SIOCGLIFFLAGS, lifr);
+ if (ret < 0) {
+ perror("ioctl: SIOCGLIFFLAGS");
+ return (-1);
+ }
+ /*
+ * Don't affect the state of addresses that failed back.
+ *
+ * XXX Link local addresses that are not marked IFF_NOFAILOVER
+ * will not be brought up. Link local addresses never failover.
+ * When the interface was offlined, we brought the link local
+ * address down. We will not bring it up now if IFF_NOFAILOVER
+ * is not marked. We check for IFF_NOFAILOVER below so that
+ * we want to maintain the state of all other addresses as it
+ * was before offline. Normally link local addresses are marked
+ * IFF_NOFAILOVER and hence this is not an issue. These can
+ * be fixed in future with RCM and it is beyond the scope
+ * of if_mpadm to maintain state and do this correctly.
+ */
+ if (!(lifr->lifr_flags & IFF_NOFAILOVER))
+ return (0);
+
+ /*
+ * When a data address associated with the physical interface itself
+ * is failed over (e.g., qfe0, rather than qfe0:1), the kernel must
+ * fill the ipif data structure for qfe0 with a placeholder entry (the
+ * "replacement ipif"). Replacement ipif's cannot be brought IFF_UP
+ * (nor would it make any sense to do so), so we must be careful to
+ * skip them; thankfully they can be easily identified since they
+ * all have a zeroed address.
+ */
+ if (zeroaddr)
+ return (0);
+
+ /* IFF_OFFLINE was not set to start with. Is it there ? */
+ if (lifr->lifr_flags & IFF_OFFLINE) {
+ fprintf(stderr, gettext("IFF_OFFLINE set wrongly on %s\n"),
+ lifr->lifr_name);
+ return (-1);
+ }
+ lifr->lifr_flags |= IFF_UP;
+ ret = ioctl(ifsock, SIOCSLIFFLAGS, lifr);
+ if (ret < 0) {
+ perror("ioctl: SIOCSLIFFLAGS");
+ return (-1);
+ }
+ return (0);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/ifconfig/Makefile b/usr/src/cmd/cmd-inet/usr.sbin/ifconfig/Makefile
new file mode 100644
index 0000000000..df8ddd5938
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/ifconfig/Makefile
@@ -0,0 +1,80 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+PROG = ifconfig
+ROOTFS_PROG = $(PROG)
+LOCALOBJS= dupl_addr.o ifconfig.o revarp.o
+COMMONOBJS= compat.o
+OBJS= $(LOCALOBJS) $(COMMONOBJS)
+
+include ../../../Makefile.cmd
+include ../../Makefile.cmd-inet
+
+LOCALSRCS= $(LOCALOBJS:%.o=%.c)
+COMMONSRCS= $(CMDINETCOMMONDIR)/$(COMMONOBJS:%.o=%.c)
+SRCS= $(LOCALSRCS) $(COMMONSRCS)
+
+CPPFLAGS += -I$(CMDINETCOMMONDIR) -I$(SRC)/common/net/dhcp
+LDLIBS += -lcmd -ldevinfo -ldhcpagent -linetcfg -ldlpi
+LINTFLAGS += -m
+
+ROOTUSRSBINLINKS = $(PROG:%=$(ROOTUSRSBIN)/%)
+
+# ifconfig uses the ancillary data feature which is available only through
+# UNIX 98 standards version of Socket interface. This interface is supposed to
+# be accessed by -lxnet. In addition -lsocket and -lnsl are used to
+# capture new not-yet-standard interfaces. Someday -lxnet alone should be
+# enough when IPv6 inspired new interfaces are part of standards.
+LDLIBS += -lxnet -lsocket -lnsl
+
+# these #defines are required to use UNIX 98 interfaces
+_D_UNIX98_EXTN= -D_XOPEN_SOURCE=500 -D__EXTENSIONS__
+
+$(OBJS) := CPPFLAGS += $(_D_UNIX98_EXTN)
+
+LINTFLAGS += $(_D_UNIX98_EXTN)
+
+.KEEP_STATE:
+
+all: $(ROOTFS_PROG)
+
+$(PROG): $(OBJS)
+ $(LINK.c) $(OBJS) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+install: all $(ROOTSBINPROG) $(ROOTUSRSBINLINKS)
+
+$(ROOTUSRSBINLINKS):
+ -$(RM) $@; $(SYMLINK) ../../sbin/$(@F) $@
+
+clean:
+ $(RM) $(OBJS)
+
+lint: lint_SRCS
+
+include ../../../Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/ifconfig/defs.h b/usr/src/cmd/cmd-inet/usr.sbin/ifconfig/defs.h
new file mode 100644
index 0000000000..c40dac4648
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/ifconfig/defs.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved. The Berkeley software License Agreement
+ * specifies the terms and conditions for redistribution.
+ */
+
+#ifndef _DEFS_H
+#define _DEFS_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <string.h>
+#include <syslog.h>
+#include <libdevinfo.h>
+#include <zone.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/sockio.h>
+#include <stropts.h>
+
+#include <netinet/in.h>
+#include <netinet/ip6.h>
+#include <netinet/icmp6.h>
+#include <netinet/tcp.h>
+#include <net/if.h>
+#include <net/pfkeyv2.h>
+#include <netinet/if_ether.h>
+#include <net/if_types.h>
+#include <net/if_dl.h>
+
+#include <netinet/dhcp.h>
+#include <dhcpagent_util.h>
+#include <dhcpagent_ipc.h>
+
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <assert.h>
+
+#include <ipmp_mpathd.h>
+#include <inetcfg.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _DEFS_H */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/ifconfig/dupl_addr.c b/usr/src/cmd/cmd-inet/usr.sbin/ifconfig/dupl_addr.c
new file mode 100644
index 0000000000..27d74f788a
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/ifconfig/dupl_addr.c
@@ -0,0 +1,892 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1995-1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+/*
+ * Perform IPv6 duplicate address detection for a given interface
+ * and IPv6 address.
+ */
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "defs.h"
+#include "ifconfig.h"
+#include <netinet/icmp6.h>
+#include <netinet/in_systm.h> /* For IP_MAXPACKET */
+#include <netinet/ip.h> /* For IP_MAXPACKET */
+
+/* XXX extract DupAddrDetectTransmits from LNKINFO? */
+int DupAddrDetectTransmits = 1; /* XXX Make configurable? */
+int RetransTimer = ND_RETRANS_TIMER; /* Milliseconds. */
+
+#define IPV6_MAX_HOPS 255
+
+struct in6_addr all_nodes_mcast = { { 0xff, 0x2, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x1 } };
+
+static void in6_solmulti_addr(struct in6_addr *addr,
+ struct in6_addr *multi);
+static int run_dad(int s, char *phyname, struct sockaddr_in6 *testaddr,
+ struct sockaddr_in6 *solicited_mc, int ifindex);
+static int send_dad_probe(int s, char *phyname,
+ struct sockaddr_in6 *testaddr,
+ struct sockaddr_in6 *solicited_mc);
+static int recv_dad(int s, char *phyname, struct sockaddr_in6 *testaddr,
+ int ifindex);
+static boolean_t verify_opt_len(struct nd_opt_hdr *opt, int optlen,
+ struct sockaddr_in6 *from);
+static void dad_failed(char *phyname, struct sockaddr_in6 *testaddr,
+ int code);
+static void print_na(char *str, char *phyname,
+ struct nd_neighbor_advert *na, int len,
+ struct sockaddr_in6 *addr);
+static void print_ns(char *str, char *phyname,
+ struct nd_neighbor_solicit *ns, int len,
+ struct sockaddr_in6 *addr);
+static void print_opt(struct nd_opt_hdr *opt, int len);
+static char *fmt_lla(char *llabuf, int bufsize, char *lla, int llalen);
+
+
+/*
+ * Performing duplicate address detection.
+ *
+ * Returns 0 if the address is ok, 1 if there is a duplicate,
+ * and -1 (with errno set) if there is some internal error.
+ * As a side effect this does a syslog and a stderr printf
+ * identifying any duplicate.
+ * Note that the state of the interface name is unchanged.
+ */
+int
+do_dad(char *ifname, struct sockaddr_in6 *testaddr)
+{
+ int s;
+ struct lifreq lifr;
+ char *cp;
+ char phyname[LIFNAMSIZ];
+ int ifindex;
+ int64_t saved_flags;
+ int ret = -1; /* Assume error by default */
+ struct sockaddr_in6 solicited_mc;
+
+ /*
+ * Truncate name at ':'. Needed for SIOCGLIFLNKINFO
+ * Keep untruncated ifname for other use.
+ */
+ (void) strncpy(phyname, ifname, sizeof (phyname));
+ cp = strchr(phyname, ':');
+ if (cp != NULL)
+ *cp = '\0';
+
+ /*
+ * Get a socket to use to send and receive neighbor solicitations
+ * for DAD. Also used for ioctls below.
+ */
+ if ((s = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) {
+ Perror0("socket");
+ return (-1);
+ }
+
+ /*
+ * Determine interface index (for IPV6_BOUND_PIF) and
+ * save the flag values so they can be restored on return.
+ */
+ (void) strncpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
+ if (ioctl(s, SIOCGLIFINDEX, (caddr_t)&lifr) < 0) {
+ Perror0("do_dad: SIOCGLIFINDEX");
+ goto done;
+ }
+ ifindex = lifr.lifr_index;
+ if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0) {
+ Perror0("do_dad: SIOCGLIFFLAGS");
+ goto done;
+ }
+ saved_flags = lifr.lifr_flags;
+ if (!(saved_flags & IFF_MULTICAST)) {
+ /* Not possible to do DAD. Pretend it is ok */
+ ret = 0;
+ goto done;
+ }
+ (void) strncpy(lifr.lifr_name, phyname, sizeof (lifr.lifr_name));
+ if (ioctl(s, SIOCGLIFLNKINFO, (caddr_t)&lifr) < 0) {
+ Perror0("do_dad: SIOCGLIFLNKINFO");
+ goto done;
+ }
+ if (lifr.lifr_ifinfo.lir_reachretrans != 0) {
+ RetransTimer = lifr.lifr_ifinfo.lir_reachretrans;
+ }
+
+ /*
+ * Set NOLOCAL and UP flags.
+ * This prevents the use of the interface except when the user binds
+ * to unspecified IPv6 address, and sends to a link local multicast
+ * address.
+ */
+ lifr.lifr_flags = saved_flags | IFF_NOLOCAL | IFF_UP;
+
+ (void) strncpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
+ if (ioctl(s, SIOCSLIFFLAGS, (caddr_t)&lifr) < 0) {
+ Perror0("do_dad: SIOCSLIFFLAGS");
+ goto restore;
+ }
+
+ /*
+ * IPV6_BOUND_PIF prevents load spreading to happen. If we
+ * just do IPV6_BOUND_IF, the packet can go out on a different
+ * interface other than "ifindex", if interface is part of
+ * a group. In that case, we will get back the copy of NS that
+ * we sent and think it is a duplicate(Switch loops back the
+ * copy on all interfaces other than the one we sent the packet on).
+ */
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_BOUND_PIF, (char *)&ifindex,
+ sizeof (ifindex)) < 0) {
+ Perror0("IPV6_BOUND_PIF");
+ goto restore;
+ }
+
+ {
+ int hops = IPV6_MAX_HOPS;
+ int on = 1;
+ int off = 0;
+
+ if (debug > 1)
+ off = 1; /* Force duplicate */
+
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
+ (char *)&hops, sizeof (hops)) < 0) {
+ Perror0("IPV6_MULTICAST_HOPS");
+ goto restore;
+ }
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_UNSPEC_SRC,
+ (char *)&on, sizeof (on)) < 0) {
+ Perror0("IPV6_UNSPEC_SRC");
+ goto restore;
+ }
+
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
+ (char *)&off, sizeof (off)) < 0) {
+ Perror0("IPV6_MULTICAST_LOOP");
+ goto restore;
+ }
+
+ /* Enable receipt of ancillary data */
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVHOPLIMIT,
+ (char *)&on, sizeof (on)) < 0) {
+ Perror0("IPV6_RECVHOPLIMIT");
+ goto restore;
+ }
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO,
+ (char *)&on, sizeof (on)) < 0) {
+ Perror0("IPV6_RECVPKTINFO");
+ goto restore;
+ }
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVRTHDR,
+ (char *)&on, sizeof (on)) < 0) {
+ Perror0("IPV6_RECVRTHDR");
+ goto restore;
+ }
+ }
+
+ /*
+ * Extract the address and determine the solicited node multicast
+ * address to use.
+ */
+ (void) memset(&solicited_mc, 0, sizeof (solicited_mc));
+ solicited_mc.sin6_family = AF_INET6;
+ in6_solmulti_addr(&testaddr->sin6_addr, &solicited_mc.sin6_addr);
+
+ /* Join the solicited node multicast address and all-nodes. */
+ {
+ struct ipv6_mreq v6mcastr;
+
+ v6mcastr.ipv6mr_multiaddr = solicited_mc.sin6_addr;
+ v6mcastr.ipv6mr_interface = ifindex;
+
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_JOIN_GROUP,
+ (char *)&v6mcastr, sizeof (v6mcastr)) < 0) {
+ Perror0("IPV6_JOIN_GROUP");
+ goto restore;
+ }
+
+ v6mcastr.ipv6mr_multiaddr = all_nodes_mcast;
+ v6mcastr.ipv6mr_interface = ifindex;
+
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_JOIN_GROUP,
+ (char *)&v6mcastr, sizeof (v6mcastr)) < 0) {
+ Perror0("IPV6_JOIN_GROUP");
+ goto restore;
+ }
+ }
+
+ ret = run_dad(s, phyname, testaddr, &solicited_mc, ifindex);
+
+restore:
+ /* Restore flags */
+ (void) strncpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
+ lifr.lifr_flags = saved_flags;
+ if (ioctl(s, SIOCSLIFFLAGS, (caddr_t)&lifr) < 0) {
+ Perror0("do_dad: SIOCSLIFFLAGS");
+ ret = -1;
+ goto done;
+ }
+done:
+ (void) close(s);
+ return (ret);
+}
+
+
+/*
+ * Determine the solicited node multicast address for a given address.
+ */
+static void
+in6_solmulti_addr(struct in6_addr *addr, struct in6_addr *multi)
+{
+ struct in6_addr solicited_prefix = {
+ { 0xff, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x1, 0xFF, 0x0, 0x0, 0x0 } };
+ int i;
+
+ *multi = solicited_prefix;
+ for (i = 13; i < 16; i++)
+ multi->s6_addr[i] = addr->s6_addr[i];
+}
+
+static int
+run_dad(int s, char *phyname, struct sockaddr_in6 *testaddr,
+ struct sockaddr_in6 *solicited_mc, int ifindex)
+{
+ int time_left; /* In milliseconds */
+ struct timeval starttime;
+ struct timeval curtime;
+ struct pollfd fds;
+ int i;
+ int ret;
+
+ if (debug)
+ (void) printf("run_dad(%s)\n", phyname);
+
+ /*
+ * Perform duplicate address detection sequence
+ * 1. Send a neighbor solicitation with an unspecified source
+ * address to the solicited node MC address with the testaddr
+ * being the target.
+ * 2. Wait for up to RetransTimer milliseconds for either a
+ * neighbor advertisement (sent to all-nodes) or a DAD neighbor
+ * solicitation for the testaddr.
+ * 3. Perform step 1 and 2 DupAddrDetectTransmits times.
+ */
+
+ /* XXX perform a random delay: 0 - MAX_RTR_SOLICITATION_DELAY */
+ /* XXX use poll+recv logic for the random delay */
+
+ for (i = 0; i < DupAddrDetectTransmits; i++) {
+ if (send_dad_probe(s, phyname, testaddr, solicited_mc) < 0)
+ return (-1);
+
+ /*
+ * Track time to make sure total wait is RetransTimer
+ * even though random packet will awake poll.
+ */
+ (void) gettimeofday(&starttime, NULL);
+ /* CONSTCOND */
+ while (1) {
+ (void) gettimeofday(&curtime, NULL);
+ time_left = RetransTimer -
+ (curtime.tv_sec - starttime.tv_sec) * 1000 -
+ (curtime.tv_usec - starttime.tv_usec) / 1000;
+
+ if (debug) {
+ (void) printf("run_dad: time_left %d ms\n",
+ time_left);
+ }
+ if (time_left <= 0) {
+ if (debug)
+ (void) printf("run_dad: timeout\n");
+ break;
+ }
+ fds.fd = s;
+ fds.events = POLLIN;
+
+ switch (poll(&fds, 1, time_left)) {
+ case -1:
+ Perror0("poll");
+ return (-1);
+ case 0:
+ /* Need loop will break */
+ break;
+ default:
+ /* Huh? */
+ (void) fprintf(stderr, "poll returns > 1!\n");
+ return (-1);
+ case 1:
+ if (fds.revents & POLLIN) {
+ ret = recv_dad(s, phyname, testaddr,
+ ifindex);
+ if (ret < 0)
+ return (-1);
+ if (ret > 0) {
+ dad_failed(phyname, testaddr,
+ ret);
+ return (1);
+ }
+ }
+ break;
+ }
+ }
+ }
+ return (0);
+}
+
+/*
+ * Send a DAD NS packet. Assumes an IPV6_UNSPEC_SRC and an IPV6_BOUND_IF
+ * have been done by the caller.
+ */
+static int
+send_dad_probe(int s, char *phyname, struct sockaddr_in6 *testaddr,
+ struct sockaddr_in6 *solicited_mc)
+{
+ static uint64_t outpack[(IP_MAXPACKET + 1)/8];
+ struct nd_neighbor_solicit *ns = (struct nd_neighbor_solicit *)outpack;
+ int packetlen = 0;
+ int cc;
+
+ ns->nd_ns_type = ND_NEIGHBOR_SOLICIT;
+ ns->nd_ns_code = 0;
+ ns->nd_ns_cksum = 0;
+ ns->nd_ns_reserved = 0;
+ ns->nd_ns_target = testaddr->sin6_addr;
+ packetlen += sizeof (struct nd_neighbor_solicit);
+ cc = sendto(s, (char *)outpack, packetlen, 0,
+ (struct sockaddr *)solicited_mc, sizeof (*solicited_mc));
+ if (cc < 0 || cc != packetlen) {
+ char abuf[INET6_ADDRSTRLEN];
+
+ if (cc < 0) {
+ Perror0("DAD, sendto");
+ return (-1);
+ }
+ (void) inet_ntop(solicited_mc->sin6_family,
+ (void *)&solicited_mc->sin6_addr, abuf, sizeof (abuf));
+
+ (void) fprintf(stderr, "wrote %s %d chars, ret=%d\n",
+ abuf, packetlen, cc);
+ return (-1);
+ }
+ if (debug)
+ print_ns("Sent NS", phyname, ns, packetlen, solicited_mc);
+
+ return (0);
+}
+
+/*
+ * Return a pointer to the specified option buffer.
+ * If not found return NULL.
+ */
+static void *
+find_ancillary(struct msghdr *msg, int cmsg_type)
+{
+ struct cmsghdr *cmsg;
+
+ for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL;
+ cmsg = CMSG_NXTHDR(msg, cmsg)) {
+ if (cmsg->cmsg_level == IPPROTO_IPV6 &&
+ cmsg->cmsg_type == cmsg_type) {
+ return (CMSG_DATA(cmsg));
+ }
+ }
+ return (NULL);
+}
+
+/*
+ * Receive an ICMP packet. If the packet signals a duplicate address for
+ * testaddr then return a positive non-zero number. Otherwise return zero.
+ * Internal errors cause a return of -1.
+ */
+static int
+recv_dad(int s, char *phyname, struct sockaddr_in6 *testaddr, int ifindex)
+{
+ struct sockaddr_in6 from;
+ struct icmp6_hdr *icmp;
+ struct nd_neighbor_solicit *ns;
+ struct nd_neighbor_advert *na;
+ static uint64_t in_packet[(IP_MAXPACKET + 1)/8];
+ static uint64_t ancillary_data[(IP_MAXPACKET + 1)/8];
+ int len;
+ char abuf[INET6_ADDRSTRLEN];
+ struct msghdr msg;
+ struct iovec iov;
+ uchar_t *opt;
+ uint_t hoplimit;
+ struct in6_addr dst;
+ int rcv_ifindex;
+
+ iov.iov_base = (char *)in_packet;
+ iov.iov_len = sizeof (in_packet);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_name = (struct sockaddr *)&from;
+ msg.msg_namelen = sizeof (from);
+ msg.msg_control = ancillary_data;
+ msg.msg_controllen = sizeof (ancillary_data);
+
+ if ((len = recvmsg(s, &msg, 0)) < 0) {
+ Perror0("DAD recvmsg");
+ return (-1);
+ }
+ if (len == 0)
+ return (0);
+
+ if (debug) {
+ (void) inet_ntop(AF_INET6, (void *)&from.sin6_addr,
+ abuf, sizeof (abuf));
+ }
+ /* Ignore packets > 64k or control buffers that don't fit */
+ if (msg.msg_flags & (MSG_TRUNC|MSG_CTRUNC)) {
+ if (debug) {
+ (void) fprintf(stderr,
+ "Truncated message: msg_flags 0x%x from %s\n",
+ msg.msg_flags, abuf);
+ }
+ return (0);
+ }
+
+ icmp = (struct icmp6_hdr *)in_packet;
+
+ if (len < ICMP6_MINLEN) {
+ if (debug) {
+ (void) fprintf(stderr,
+ "Too short ICMP packet: %d bytes from %s\n",
+ len, abuf);
+ }
+ return (0);
+ }
+
+ opt = find_ancillary(&msg, IPV6_HOPLIMIT);
+ if (opt == NULL) {
+ /* Unknown hoplimit - must drop */
+ if (debug) {
+ (void) fprintf(stderr,
+ "Unknown hop limit from %s\n", abuf);
+ }
+ return (0);
+ }
+ hoplimit = *(uint_t *)opt;
+ opt = find_ancillary(&msg, IPV6_PKTINFO);
+ if (opt == NULL) {
+ /* Unknown destination address - must drop */
+ if (debug) {
+ (void) fprintf(stderr,
+ "Unknown destination from %s\n", abuf);
+ }
+ return (0);
+ }
+ dst = ((struct in6_pktinfo *)opt)->ipi6_addr;
+ rcv_ifindex = ((struct in6_pktinfo *)opt)->ipi6_ifindex;
+ opt = find_ancillary(&msg, IPV6_RTHDR);
+ if (opt != NULL) {
+ /* Can't allow routing headers in ND messages */
+ if (debug) {
+ (void) fprintf(stderr,
+ "ND message with routing header from %s\n", abuf);
+ }
+ return (0);
+ }
+
+ switch (icmp->icmp6_type) {
+ case ND_NEIGHBOR_SOLICIT:
+ /*
+ * Assumes that the kernel has verified the AH (if present)
+ * and the ICMP checksum.
+ */
+ if (hoplimit != IPV6_MAX_HOPS) {
+ if (debug) {
+ (void) fprintf(stderr,
+ "NS hop limit: %d from %s\n",
+ hoplimit, abuf);
+ }
+ return (0);
+ }
+
+ if (icmp->icmp6_code != 0) {
+ if (debug) {
+ (void) fprintf(stderr, "NS code: %d from %s\n",
+ icmp->icmp6_code, abuf);
+ }
+ return (0);
+ }
+
+ if (len < sizeof (struct nd_neighbor_solicit)) {
+ if (debug) {
+ (void) fprintf(stderr,
+ "NS too short: %d bytes from %s\n",
+ len, abuf);
+ }
+ return (0);
+ }
+ ns = (struct nd_neighbor_solicit *)icmp;
+ if (IN6_IS_ADDR_MULTICAST(&ns->nd_ns_target)) {
+ if (debug) {
+ char abuf2[INET6_ADDRSTRLEN];
+
+ (void) inet_ntop(AF_INET6,
+ (void *)&ns->nd_ns_target,
+ abuf2, sizeof (abuf2));
+ (void) fprintf(stderr,
+ "NS with multicast target: %s from %s\n",
+ abuf2, abuf);
+ }
+ return (0);
+ }
+
+ if (len > sizeof (struct nd_neighbor_solicit)) {
+ if (!verify_opt_len((struct nd_opt_hdr *)&ns[1],
+ len - sizeof (struct nd_neighbor_solicit), &from))
+ return (0);
+ }
+
+ if (debug)
+ print_ns("Received valid NS", phyname, ns, len, &from);
+ if (!IN6_IS_ADDR_UNSPECIFIED(&from.sin6_addr)) {
+ /* Sender is doing address resolution */
+ return (0);
+ }
+ if (rcv_ifindex != ifindex) {
+ if (debug) {
+ (void) fprintf(stderr,
+ "Received Neighbor solicitation on"
+ " ifindex %d, expecting on %d\n",
+ rcv_ifindex, ifindex);
+ }
+ return (0);
+ }
+ if (IN6_ARE_ADDR_EQUAL(&testaddr->sin6_addr,
+ &ns->nd_ns_target)) {
+ if (debug) {
+ (void) fprintf(stderr,
+ "NS - duplicate from %s\n",
+ abuf);
+ }
+ return (1);
+ }
+ return (0);
+
+ case ND_NEIGHBOR_ADVERT:
+ /*
+ * Assumes that the kernel has verified the AH (if present)
+ * and the ICMP checksum.
+ */
+ if (hoplimit != IPV6_MAX_HOPS) {
+ if (debug) {
+ (void) fprintf(stderr,
+ "NA hop limit: %d from %s\n",
+ hoplimit, abuf);
+ }
+ return (0);
+ }
+
+ if (icmp->icmp6_code != 0) {
+ if (debug) {
+ (void) fprintf(stderr, "NA code: %d from %s\n",
+ icmp->icmp6_code, abuf);
+ }
+ return (0);
+ }
+
+ if (len < sizeof (struct nd_neighbor_advert)) {
+ if (debug) {
+ (void) fprintf(stderr,
+ "NA too short: %d bytes from %s\n",
+ len, abuf);
+ }
+ return (0);
+ }
+ na = (struct nd_neighbor_advert *)icmp;
+ if (IN6_IS_ADDR_MULTICAST(&na->nd_na_target)) {
+ if (debug) {
+ char abuf2[INET6_ADDRSTRLEN];
+
+ (void) inet_ntop(AF_INET6,
+ (void *)&na->nd_na_target,
+ abuf2, sizeof (abuf2));
+ (void) fprintf(stderr,
+ "NA with multicast target: %s from %s\n",
+ abuf2, abuf);
+ }
+ return (0);
+ }
+
+ if (IN6_IS_ADDR_MULTICAST(&dst) &&
+ (na->nd_na_flags_reserved & ND_NA_FLAG_SOLICITED)) {
+ if (debug) {
+ char abuf2[INET6_ADDRSTRLEN];
+
+ (void) inet_ntop(AF_INET6,
+ (void *)&na->nd_na_target,
+ abuf2, sizeof (abuf2));
+ (void) fprintf(stderr,
+ "NA solicited w/ mc target: %s from %s\n",
+ abuf2, abuf);
+ }
+ return (0);
+ }
+
+ if (len > sizeof (struct nd_neighbor_advert)) {
+ if (!verify_opt_len((struct nd_opt_hdr *)&na[1],
+ len - sizeof (struct nd_neighbor_advert), &from))
+ return (0);
+ }
+
+ if (debug)
+ print_na("Received valid NA", phyname, na, len, &from);
+
+ if (IN6_ARE_ADDR_EQUAL(&testaddr->sin6_addr,
+ &na->nd_na_target)) {
+ if (debug) {
+ (void) fprintf(stderr,
+ "NA - duplicate from %s\n",
+ abuf);
+ }
+ return (1);
+ }
+ return (0);
+ default:
+ return (0);
+ }
+}
+
+/*
+ * Verify that all options have a non-zero length and that
+ * the options fit within the total length of the packet (optlen).
+ */
+static boolean_t
+verify_opt_len(struct nd_opt_hdr *opt, int optlen, struct sockaddr_in6 *from)
+{
+ while (optlen > 0) {
+ if (opt->nd_opt_len == 0) {
+ if (debug) {
+ char abuf[INET6_ADDRSTRLEN];
+
+ (void) inet_ntop(AF_INET6,
+ (void *)&from->sin6_addr,
+ abuf, sizeof (abuf));
+
+ (void) fprintf(stderr,
+ "Zero length option type 0x%x from %s\n",
+ opt->nd_opt_type, abuf);
+ }
+ return (_B_FALSE);
+ }
+ optlen -= 8 * opt->nd_opt_len;
+ if (optlen < 0) {
+ if (debug) {
+ char abuf[INET6_ADDRSTRLEN];
+
+ (void) inet_ntop(AF_INET6,
+ (void *)&from->sin6_addr,
+ abuf, sizeof (abuf));
+
+ (void) fprintf(stderr,
+ "Too large option: type 0x%x len %u "
+ "from %s\n",
+ opt->nd_opt_type, opt->nd_opt_len,
+ abuf);
+ }
+ return (_B_FALSE);
+ }
+ opt = (struct nd_opt_hdr *)((char *)opt +
+ 8 * opt->nd_opt_len);
+ }
+ return (_B_TRUE);
+}
+
+
+static void
+dad_failed(char *phyname, struct sockaddr_in6 *testaddr, int code)
+{
+ char abuf[INET6_ADDRSTRLEN];
+
+ (void) inet_ntop(testaddr->sin6_family,
+ (void *)&testaddr->sin6_addr,
+ abuf, sizeof (abuf));
+ (void) fprintf(stderr,
+ "ifconfig: "
+ "Duplicate address detected on link %s for address %s. Code %d\n",
+ phyname, abuf, code);
+
+ openlog("ifconfig", LOG_CONS, LOG_DAEMON);
+ syslog(LOG_CRIT,
+ "Duplicate address detected on link %s for address %s. Code %d\n",
+ phyname, abuf, code);
+ closelog();
+}
+
+/* Printing functions */
+
+static void
+print_ns(char *str, char *phyname,
+ struct nd_neighbor_solicit *ns, int len, struct sockaddr_in6 *addr)
+{
+ struct nd_opt_hdr *opt;
+ char abuf[INET6_ADDRSTRLEN];
+
+ (void) printf("%s %s (%d bytes) on %s\n", str,
+ inet_ntop(addr->sin6_family, (void *)&addr->sin6_addr,
+ abuf, sizeof (abuf)),
+ len, phyname);
+ (void) printf("\ttarget %s\n",
+ inet_ntop(addr->sin6_family, (void *)&ns->nd_ns_target,
+ abuf, sizeof (abuf)));
+ len -= sizeof (*ns);
+ opt = (struct nd_opt_hdr *)&ns[1];
+ print_opt(opt, len);
+}
+
+static void
+print_na(char *str, char *phyname,
+ struct nd_neighbor_advert *na, int len, struct sockaddr_in6 *addr)
+{
+ struct nd_opt_hdr *opt;
+ char abuf[INET6_ADDRSTRLEN];
+
+ (void) printf("%s %s (%d bytes) on %s\n", str,
+ inet_ntop(addr->sin6_family, (void *)&addr->sin6_addr,
+ abuf, sizeof (abuf)),
+ len, phyname);
+ (void) printf("\ttarget %s\n",
+ inet_ntop(addr->sin6_family, (void *)&na->nd_na_target,
+ abuf, sizeof (abuf)));
+ (void) printf("\tRouter: %s\n",
+ (na->nd_na_flags_reserved & ND_NA_FLAG_ROUTER) ?
+ "Set" : "Not set");
+ (void) printf("\tSolicited: %s\n",
+ (na->nd_na_flags_reserved & ND_NA_FLAG_SOLICITED) ?
+ "Set" : "Not set");
+ (void) printf("\tOverride: %s\n",
+ (na->nd_na_flags_reserved & ND_NA_FLAG_OVERRIDE) ?
+ "Set" : "Not set");
+
+ len -= sizeof (*na);
+ opt = (struct nd_opt_hdr *)&na[1];
+ print_opt(opt, len);
+}
+
+static void
+print_opt(struct nd_opt_hdr *opt, int len)
+{
+ struct nd_opt_prefix_info *po;
+ struct nd_opt_mtu *mo;
+ struct nd_opt_lla *lo;
+ int optlen;
+ char abuf[INET6_ADDRSTRLEN];
+ char llabuf[BUFSIZ];
+
+ while (len >= sizeof (struct nd_opt_hdr)) {
+ optlen = opt->nd_opt_len * 8;
+ if (optlen == 0) {
+ if (debug)
+ (void) printf("Zero length option!\n");
+ break;
+ }
+ switch (opt->nd_opt_type) {
+ case ND_OPT_PREFIX_INFORMATION:
+ po = (struct nd_opt_prefix_info *)opt;
+ if (optlen != sizeof (*po) ||
+ optlen > len)
+ break;
+
+ (void) printf("\tOn link flag:%s\n",
+ (po->nd_opt_pi_flags_reserved &
+ ND_OPT_PI_FLAG_ONLINK) ?
+ "Set" : "Not set");
+ (void) printf("\tAuto addrconf flag:%s\n",
+ (po->nd_opt_pi_flags_reserved &
+ ND_OPT_PI_FLAG_AUTO) ?
+ "Set" : "Not set");
+ (void) printf("\tValid time: %u\n",
+ ntohl(po->nd_opt_pi_valid_time));
+ (void) printf("\tPreferred time: %u\n",
+ ntohl(po->nd_opt_pi_preferred_time));
+ (void) printf("\tPrefix: %s/%u\n",
+ inet_ntop(AF_INET6, (void *)&po->nd_opt_pi_prefix,
+ abuf, sizeof (abuf)),
+ po->nd_opt_pi_prefix_len);
+ break;
+ case ND_OPT_MTU:
+ mo = (struct nd_opt_mtu *)opt;
+ if (optlen != sizeof (*mo) ||
+ optlen > len)
+ break;
+ (void) printf("\tMTU: %d\n",
+ ntohl(mo->nd_opt_mtu_mtu));
+ break;
+ case ND_OPT_SOURCE_LINKADDR:
+ lo = (struct nd_opt_lla *)opt;
+ if (optlen < 8 ||
+ optlen > len)
+ break;
+ (void) fmt_lla(llabuf, sizeof (llabuf),
+ (char *)lo->nd_opt_lla_hdw_addr, optlen - 2);
+ (void) printf("\tSource LLA: len %d <%s>\n",
+ optlen-2, llabuf);
+ break;
+ case ND_OPT_TARGET_LINKADDR:
+ lo = (struct nd_opt_lla *)opt;
+ if (optlen < 8||
+ optlen > len)
+ break;
+ (void) fmt_lla(llabuf, sizeof (llabuf),
+ (char *)lo->nd_opt_lla_hdw_addr, optlen - 2);
+ (void) printf("\tTarget LLA: len %d <%s>\n",
+ optlen-2, llabuf);
+ break;
+ case ND_OPT_REDIRECTED_HEADER:
+ (void) printf("\tRedirected header option!\n");
+ break;
+ default:
+ (void) printf("Unkown option %d (0x%x)\n",
+ opt->nd_opt_type, opt->nd_opt_type);
+ break;
+ }
+ opt = (struct nd_opt_hdr *)((char *)opt + optlen);
+ len -= optlen;
+ }
+}
+
+static char *
+fmt_lla(char *llabuf, int bufsize, char *lla, int llalen)
+{
+ int i;
+ char *cp = llabuf;
+
+ for (i = 0; i < llalen; i++) {
+ if (i == llalen - 1)
+ (void) snprintf(cp, bufsize, "%02x", lla[i] & 0xFF);
+ else
+ (void) snprintf(cp, bufsize, "%02x:", lla[i] & 0xFF);
+ bufsize -= strlen(cp);
+ cp += strlen(cp);
+ }
+ return (llabuf);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/ifconfig/ifconfig.c b/usr/src/cmd/cmd-inet/usr.sbin/ifconfig/ifconfig.c
new file mode 100644
index 0000000000..c80b140757
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/ifconfig/ifconfig.c
@@ -0,0 +1,4716 @@
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved. The Berkeley software License Agreement
+ * specifies the terms and conditions for redistribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "defs.h"
+#include "strings.h"
+#include "ifconfig.h"
+#include <compat.h>
+
+#include <sys/dlpi.h>
+#include <libdlpi.h>
+
+#define LOOPBACK_IF "lo0"
+
+#define NONE_STR "none"
+
+#ifndef ARP_MOD_NAME
+#define ARP_MOD_NAME "arp"
+#endif
+
+#ifndef IP_DEV_NAME
+#define IP_DEV_NAME "/dev/ip"
+#endif
+
+#ifndef IP_MOD_NAME
+#define IP_MOD_NAME "ip"
+#endif
+
+#ifndef IP6_DEV_NAME
+#define IP6_DEV_NAME "/dev/ip6"
+#endif
+
+#ifndef UDP_DEV_NAME
+#define UDP_DEV_NAME "/dev/udp"
+#endif
+
+#ifndef UDP6_DEV_NAME
+#define UDP6_DEV_NAME "/dev/udp6"
+#endif
+
+#define ADDRBITS_V4 32 /* number of bits in IPv4 address */
+#define ADDRBITS_V6 128 /* number of bits in IPv6 address */
+
+#define AF_FUNC_STATUS 1 /* af_status func reports status */
+#define AF_FUNC_CONFIGINFO 2 /* af_status func reports configinfo */
+
+typedef struct if_flags {
+ uint64_t iff_value;
+ char *iff_name;
+} if_flags_t;
+
+static if_flags_t if_flags_tbl[] = {
+ { IFF_UP, "UP" },
+ { IFF_BROADCAST, "BROADCAST" },
+ { IFF_DEBUG, "DEBUG" },
+ { IFF_LOOPBACK, "LOOPBACK" },
+ { IFF_POINTOPOINT, "POINTOPOINT" },
+ { IFF_NOTRAILERS, "NOTRAILERS" },
+ { IFF_RUNNING, "RUNNING" },
+ { IFF_NOARP, "NOARP" },
+ { IFF_PROMISC, "PROMISC" },
+ { IFF_ALLMULTI, "ALLMULTI" },
+ { IFF_INTELLIGENT, "INTELLIGENT" },
+ { IFF_MULTICAST, "MULTICAST" },
+ { IFF_MULTI_BCAST, "MULTI_BCAST" },
+ { IFF_UNNUMBERED, "UNNUMBERED" },
+ { IFF_DHCPRUNNING, "DHCP" },
+ { IFF_PRIVATE, "PRIVATE" },
+ { IFF_NOXMIT, "NOXMIT" },
+ { IFF_NOLOCAL, "NOLOCAL" },
+ { IFF_DEPRECATED, "DEPRECATED" },
+ { IFF_ADDRCONF, "ADDRCONF" },
+ { IFF_ROUTER, "ROUTER" },
+ { IFF_NONUD, "NONUD" },
+ { IFF_ANYCAST, "ANYCAST" },
+ { IFF_NORTEXCH, "NORTEXCH" },
+ { IFF_IPV4, "IPv4" },
+ { IFF_IPV6, "IPv6" },
+ { IFF_MIPRUNNING, "MIP" },
+ { IFF_NOFAILOVER, "NOFAILOVER" },
+ { IFF_FAILED, "FAILED" },
+ { IFF_STANDBY, "STANDBY" },
+ { IFF_INACTIVE, "INACTIVE" },
+ { IFF_OFFLINE, "OFFLINE" },
+ { IFF_XRESOLV, "XRESOLV" },
+ { IFF_COS_ENABLED, "CoS" },
+ { IFF_PREFERRED, "PREFERRED" },
+ { IFF_TEMPORARY, "TEMPORARY" },
+ { IFF_FIXEDMTU, "FIXEDMTU" },
+ { IFF_VIRTUAL, "VIRTUAL"}
+};
+
+static struct lifreq lifr;
+/* current interface name a praticular function is accessing */
+static char name[LIFNAMSIZ];
+/* foreach interface saved name */
+static char origname[LIFNAMSIZ];
+static char savedname[LIFNAMSIZ]; /* For addif */
+static int setaddr;
+static char partname[LIFNAMSIZ];
+
+uid_t euid;
+
+/*
+ * Make sure the algorithm variables hold more than the sizeof an algorithm
+ * in PF_KEY. (For now, more than a uint8_t.) The NO_***_?ALG indicates that
+ * there was no algorithm requested, and in the ipsec_req that service should
+ * be disabled. (E.g. if ah_aalg remains NO_AH_AALG, then AH will be
+ * disabled on that tunnel.)
+ */
+#define NO_AH_AALG 256
+#define NO_ESP_AALG 256
+#define NO_ESP_EALG 256
+
+/*
+ * iface_t
+ * used by setifether to create a list of interfaces to mark
+ * down-up when changing the ethernet address of an interface
+ */
+typedef struct iface {
+ struct lifreq lifr;
+ struct iface *next; /* pointer to the next list element */
+} iface_t;
+
+static iface_t *logifs = NULL; /* list of logical interfaces */
+static iface_t *phyif = NULL; /* physical interface */
+
+int s;
+int af = AF_INET; /* default address family */
+int debug = 0;
+int all = 0; /* setifdhcp() needs to know this */
+int verbose = 0;
+int v4compat = 0; /* Compatible printing format */
+boolean_t partial = _B_FALSE;
+
+/*
+ * Function prototypes for command functions.
+ */
+static int addif(char *arg, int64_t param);
+static int inetplumb(char *arg, int64_t param);
+static int inetunplumb(char *arg, int64_t param);
+static int removeif(char *arg, int64_t param);
+static int setdebugflag(char *arg, int64_t param);
+static int setifaddr(char *arg, int64_t param);
+static int setifbroadaddr(char *arg, int64_t param);
+static int setifdstaddr(char *arg, int64_t param);
+static int setifether(char *arg, int64_t param);
+static int setifflags(char *arg, int64_t param);
+static int setifindex(char *arg, int64_t param);
+static int setifmetric(char *arg, int64_t param);
+static int setifmtu(char *arg, int64_t param);
+static int setifnetmask(char *arg, int64_t param);
+static int setifprefixlen(char *arg, int64_t param);
+static int setifrevarp(char *arg, int64_t param);
+static int setifsubnet(char *arg, int64_t param);
+static int setiftdst(char *arg, int64_t param);
+static int setiftoken(char *arg, int64_t param);
+static int setiftsrc(char *arg, int64_t param);
+static int setverboseflag(char *arg, int64_t param);
+static int set_tun_ah_alg(char *arg, int64_t param);
+static int set_tun_esp_auth_alg(char *arg, int64_t param);
+static int set_tun_esp_encr_alg(char *arg, int64_t param);
+static int modlist(char *arg, int64_t param);
+static int modinsert(char *arg, int64_t param);
+static int modremove(char *arg, int64_t param);
+static int setifgroupname(char *arg, int64_t param);
+static int configinfo(char *arg, int64_t param);
+static void print_config_flags(uint64_t flags);
+static void print_flags(uint64_t flags);
+static void print_ifether(char *ifname);
+static int set_tun_encap_limit(char *arg, int64_t param);
+static int clr_tun_encap_limit(char *arg, int64_t param);
+static int set_tun_hop_limit(char *arg, int64_t param);
+static int setzone(char *arg, int64_t param);
+static int setifsrc(char *arg, int64_t param);
+
+#ifdef DEBUG
+static int setnd(char *arg, int64_t param);
+static int delnd(char *arg, int64_t param);
+static int getnd(char *arg, int64_t param);
+#endif /* DEBUG */
+
+/*
+ * Address family specific function prototypes.
+ */
+static void in_getaddr(char *s, struct sockaddr *saddr, int *plenp);
+static void in_status(int force, uint64_t flags);
+static void in_configinfo(int force, uint64_t flags);
+static void in6_getaddr(char *s, struct sockaddr *saddr, int *plenp);
+static void in6_status(int force, uint64_t flags);
+static void in6_configinfo(int force, uint64_t flags);
+
+/*
+ * Misc support functions
+ */
+static int devfs_entry(di_node_t node, di_minor_t minor, void *arg);
+static void foreachinterface(void (*func)(), int argc, char *argv[],
+ int af, int64_t onflags, int64_t offflags,
+ int64_t lifc_flags);
+static void ifconfig(int argc, char *argv[], int af, struct lifreq *lifrp);
+static int ifdad(char *ifname, struct sockaddr_in6 *laddr);
+static boolean_t in_getmask(struct sockaddr_in *saddr);
+static int in_getprefixlen(char *addr, boolean_t slash, int plen);
+static boolean_t in_prefixlentomask(int prefixlen, int maxlen,
+ uchar_t *mask);
+static int settaddr(char *, int (*)(icfg_handle_t,
+ const struct sockaddr *, socklen_t));
+static void status(void);
+static void ifstatus(const char *);
+static void usage(void);
+static int strioctl(int s, int cmd, char *buf, int buflen);
+static int setifdhcp(const char *caller, const char *ifname,
+ int argc, char *argv[]);
+static int ip_domux2fd(int *, int *, int *, int *);
+static int ip_plink(int, int, int, int);
+static int modop(char *arg, char op);
+static int get_lun(char *);
+static void selectifs(int argc, char *argv[], int af,
+ struct lifreq *lifrp);
+static int updownifs(iface_t *ifs, int up);
+
+#define max(a, b) ((a) < (b) ? (b) : (a))
+
+/*
+ * DHCP_EXIT_IF_FAILURE indicates that the operation failed, but if there
+ * are more interfaces to act on (i.e., ifconfig was invoked with -a), keep
+ * on going rather than exit with an error.
+ */
+
+#define DHCP_EXIT_IF_FAILURE -1
+#define DHCP_IPC_MAX_WAIT 15 /* max seconds to wait to start agent */
+
+#define NEXTARG 0xffffff /* command takes an argument */
+#define OPTARG 0xfffffe /* command takes an optional argument */
+#define AF_ANY (-1)
+
+/* Refer to the comments in ifconfig() on the netmask "hack" */
+#define NETMASK_CMD "netmask"
+struct sockaddr_storage g_netmask;
+boolean_t g_netmask_set = _B_FALSE;
+
+struct cmd {
+ char *c_name;
+ int64_t c_parameter; /* NEXTARG means next argv */
+ int (*c_func)(char *, int64_t);
+ int c_abortonfail; /* don't continue parsing args */
+ /* for the current interface */
+ int c_af; /* address family restrictions */
+} cmds[] = {
+ { "up", IFF_UP, setifflags, 0, AF_ANY },
+ { "down", -IFF_UP, setifflags, 0, AF_ANY },
+ { "trailers", -IFF_NOTRAILERS, setifflags, 0, AF_ANY },
+ { "-trailers", IFF_NOTRAILERS, setifflags, 0, AF_ANY },
+ { "arp", -IFF_NOARP, setifflags, 0, AF_INET },
+ { "-arp", IFF_NOARP, setifflags, 0, AF_INET },
+ { "router", IFF_ROUTER, setifflags, 0, AF_ANY },
+ { "-router", -IFF_ROUTER, setifflags, 0, AF_ANY },
+ { "private", IFF_PRIVATE, setifflags, 0, AF_ANY },
+ { "-private", -IFF_PRIVATE, setifflags, 0, AF_ANY },
+ { "xmit", -IFF_NOXMIT, setifflags, 0, AF_ANY },
+ { "-xmit", IFF_NOXMIT, setifflags, 0, AF_ANY },
+ { "-nud", IFF_NONUD, setifflags, 0, AF_INET6 },
+ { "nud", -IFF_NONUD, setifflags, 0, AF_INET6 },
+ { "anycast", IFF_ANYCAST, setifflags, 0, AF_ANY },
+ { "-anycast", -IFF_ANYCAST, setifflags, 0, AF_ANY },
+ { "local", -IFF_NOLOCAL, setifflags, 0, AF_ANY },
+ { "-local", IFF_NOLOCAL, setifflags, 0, AF_ANY },
+ { "deprecated", IFF_DEPRECATED, setifflags, 0, AF_ANY },
+ { "-deprecated", -IFF_DEPRECATED, setifflags, 0, AF_ANY },
+ { "preferred", IFF_PREFERRED, setifflags, 0, AF_INET6 },
+ { "-preferred", -IFF_PREFERRED, setifflags, 0, AF_INET6 },
+ { "debug", 0, setdebugflag, 0, AF_ANY },
+ { "verbose", 0, setverboseflag, 0, AF_ANY },
+ { NETMASK_CMD, NEXTARG, setifnetmask, 0, AF_INET },
+ { "metric", NEXTARG, setifmetric, 0, AF_ANY },
+ { "mtu", NEXTARG, setifmtu, 0, AF_ANY },
+ { "index", NEXTARG, setifindex, 0, AF_ANY },
+ { "broadcast", NEXTARG, setifbroadaddr, 0, AF_INET },
+ { "auto-revarp", 0, setifrevarp, 1, AF_INET },
+ { "plumb", 0, inetplumb, 1, AF_ANY },
+ { "unplumb", 0, inetunplumb, 0, AF_ANY },
+ { "subnet", NEXTARG, setifsubnet, 0, AF_ANY },
+ { "token", NEXTARG, setiftoken, 0, AF_INET6 },
+ { "tsrc", NEXTARG, setiftsrc, 0, AF_ANY },
+ { "tdst", NEXTARG, setiftdst, 0, AF_ANY },
+ { "encr_auth_algs", NEXTARG, set_tun_esp_auth_alg, 0, AF_ANY },
+ { "encr_algs", NEXTARG, set_tun_esp_encr_alg, 0, AF_ANY },
+ { "auth_algs", NEXTARG, set_tun_ah_alg, 0, AF_ANY },
+ { "addif", NEXTARG, addif, 1, AF_ANY },
+ { "removeif", NEXTARG, removeif, 1, AF_ANY },
+ { "modlist", 0, modlist, 1, AF_ANY },
+ { "modinsert", NEXTARG, modinsert, 1, AF_ANY },
+ { "modremove", NEXTARG, modremove, 1, AF_ANY },
+ { "failover", -IFF_NOFAILOVER, setifflags, 1, AF_ANY },
+ { "-failover", IFF_NOFAILOVER, setifflags, 1, AF_ANY },
+ { "standby", IFF_STANDBY, setifflags, 1, AF_ANY },
+ { "-standby", -IFF_STANDBY, setifflags, 1, AF_ANY },
+ { "failed", IFF_FAILED, setifflags, 1, AF_ANY },
+ { "-failed", -IFF_FAILED, setifflags, 1, AF_ANY },
+ { "group", NEXTARG, setifgroupname, 1, AF_ANY },
+ { "configinfo", 0, configinfo, 1, AF_ANY },
+ { "encaplimit", NEXTARG, set_tun_encap_limit, 0,
+ AF_ANY },
+ { "-encaplimit", 0, clr_tun_encap_limit, 0,
+ AF_ANY },
+ { "thoplimit", NEXTARG, set_tun_hop_limit, 0,
+ AF_ANY },
+#ifdef DEBUG
+ { "getnd", NEXTARG, getnd, 0, AF_INET6 },
+ { "setnd", NEXTARG, setnd, 0, AF_INET6 },
+ { "delnd", NEXTARG, delnd, 0, AF_INET6 },
+#endif
+/* XXX for testing SIOCT* ioctls. Remove */
+ { "set", NEXTARG, setifaddr, 0, AF_ANY },
+ { "destination", NEXTARG, setifdstaddr, 0, AF_ANY },
+ { "zone", NEXTARG, setzone, 0, AF_ANY },
+ { "-zone", 0, setzone, 0, AF_ANY },
+ { "ether", OPTARG, setifether, 0, AF_ANY },
+ { "usesrc", NEXTARG, setifsrc, 0, AF_ANY },
+ { 0, 0, setifaddr, 0, AF_ANY },
+ { 0, 0, setifdstaddr, 0, AF_ANY },
+ { 0, 0, 0, 0, 0 },
+};
+
+
+typedef struct if_config_cmd {
+ uint64_t iff_flag;
+ char *iff_name;
+} if_config_cmd_t;
+
+static if_config_cmd_t if_config_cmd_tbl[] = {
+ { IFF_UP, "up" },
+ { IFF_NOTRAILERS, "-trailers" },
+ { IFF_PRIVATE, "private" },
+ { IFF_NOXMIT, "-xmit" },
+ { IFF_ANYCAST, "anycast" },
+ { IFF_NOLOCAL, "-local" },
+ { IFF_DEPRECATED, "deprecated" },
+ { IFF_NOFAILOVER, "-failover" },
+ { IFF_STANDBY, "standby" },
+ { IFF_FAILED, "failed" },
+ { IFF_PREFERRED, "preferred" },
+ { 0, 0 },
+};
+
+typedef struct ni {
+ char ni_name[LIFNAMSIZ];
+ struct ni *ni_next;
+} ni_t;
+
+static ni_t *ni_list = NULL;
+static int num_ni = 0;
+
+/* End defines and structure definitions for ifconfig -a plumb */
+
+/* Known address families */
+struct afswtch {
+ char *af_name;
+ short af_af;
+ void (*af_status)();
+ void (*af_getaddr)();
+ void (*af_configinfo)();
+} afs[] = {
+ { "inet", AF_INET, in_status, in_getaddr, in_configinfo },
+ { "inet6", AF_INET6, in6_status, in6_getaddr, in6_configinfo },
+ { 0, 0, 0, 0, 0 }
+};
+
+#define SOCKET_AF(af) (((af) == AF_UNSPEC) ? AF_INET : (af))
+
+struct afswtch *afp; /* the address family being set or asked about */
+
+int
+main(int argc, char *argv[])
+{
+ /* Include IFF_NOXMIT, IFF_TEMPORARY and all zone interfaces */
+ int64_t lifc_flags = LIFC_NOXMIT | LIFC_TEMPORARY | LIFC_ALLZONES;
+ char *default_ip_str;
+
+ if (argc < 2) {
+ usage();
+ exit(1);
+ }
+ argc--, argv++;
+ if (strlen(*argv) > sizeof (name) - 1) {
+ (void) fprintf(stderr, "%s: interface name too long\n", *argv);
+ exit(1);
+ }
+ (void) strncpy(name, *argv, sizeof (name));
+ name[sizeof (name) - 1] = '\0';
+ (void) strncpy(origname, name, sizeof (origname)); /* For addif */
+ euid = geteuid();
+ default_ip_str = NULL;
+ v4compat = get_compat_flag(&default_ip_str);
+ if (v4compat == DEFAULT_PROT_BAD_VALUE) {
+ (void) fprintf(stderr,
+ "ifconfig: %s: Bad value for %s in %s\n", default_ip_str,
+ DEFAULT_IP, INET_DEFAULT_FILE);
+ free(default_ip_str);
+ exit(2);
+ }
+ free(default_ip_str);
+ argc--, argv++;
+ if (argc > 0) {
+ struct afswtch *myafp;
+
+ for (myafp = afp = afs; myafp->af_name; myafp++) {
+ if (strcmp(myafp->af_name, *argv) == 0) {
+ afp = myafp; argc--; argv++;
+ break;
+ }
+ }
+ af = lifr.lifr_addr.ss_family = afp->af_af;
+ if (af == AF_INET6) {
+ v4compat = 0;
+ }
+ }
+
+ s = socket(SOCKET_AF(af), SOCK_DGRAM, 0);
+ if (s < 0) {
+ Perror0_exit("socket");
+ }
+
+ /*
+ * Special interface names is any combination of these flags.
+ * Note that due to the ifconfig syntax they have to be combined
+ * as a single '-' option.
+ * -a All interfaces
+ * -u "up" interfaces
+ * -d "down" interfaces
+ * -D Interfaces not controlled by DHCP
+ * -4 IPv4 interfaces
+ * -6 IPv6 interfaces
+ * -X Turn on debug (not documented)
+ * -v Turn on verbose
+ * -Z Only interfaces in caller's zone
+ */
+
+ if (name[0] == '-') {
+ /* One or more options */
+ int64_t onflags = 0;
+ int64_t offflags = 0;
+ int c;
+ char *av[2] = { "ifconfig", name };
+
+ while ((c = getopt(2, av, "audDXZ46v")) != -1) {
+ switch ((char)c) {
+ case 'a':
+ all = 1;
+ break;
+ case 'u':
+ onflags |= IFF_UP;
+ break;
+ case 'd':
+ offflags |= IFF_UP;
+ break;
+ case 'D':
+ offflags |= IFF_DHCPRUNNING;
+ break;
+ case 'X':
+ debug += 3;
+ break;
+ case 'Z':
+ lifc_flags &= ~LIFC_ALLZONES;
+ break;
+ case '4':
+ /*
+ * -4 is not a compatable flag, therefore
+ * we assume they want v4compat turned off
+ */
+ v4compat = 0;
+ onflags |= IFF_IPV4;
+ break;
+ case '6':
+ /*
+ * If they want IPv6, well then we'll assume
+ * they don't want IPv4 compat
+ */
+ v4compat = 0;
+ onflags |= IFF_IPV6;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case '?':
+ usage();
+ exit(1);
+ }
+ }
+ if (!all) {
+ (void) fprintf(stderr,
+ "ifconfig: %s: no such interface\n",
+ name);
+ exit(1);
+ }
+ foreachinterface(ifconfig, argc, argv, af, onflags, offflags,
+ lifc_flags);
+ } else {
+ ifconfig(argc, argv, af, (struct lifreq *)NULL);
+ }
+ return (0);
+}
+
+/*
+ * For each interface, call (*func)(argc, argv, af, lifrp).
+ * Only call function if onflags and offflags are set or clear, respectively,
+ * in the interfaces flags field.
+ */
+static void
+foreachinterface(void (*func)(), int argc, char *argv[], int af,
+ int64_t onflags, int64_t offflags, int64_t lifc_flags)
+{
+ int n;
+ char *buf;
+ struct lifnum lifn;
+ struct lifconf lifc;
+ struct lifreq *lifrp;
+ struct lifreq lifrl; /* Local lifreq struct */
+ int numifs;
+ unsigned bufsize;
+ ni_t *nip;
+ int plumball = 0;
+ int save_af = af;
+
+ /*
+ * Special case:
+ * ifconfig -a plumb should find all network interfaces
+ * in the machine by traversing the devinfo tree.
+ * Also, there is no need to SIOCGLIF* ioctls, since
+ * those interfaces have already been plumbed
+ */
+ if (argc > 0 && (strcmp(*argv, "plumb") == 0)) {
+ /*
+ * Look through the kernel's devinfo tree for
+ * network devices
+ */
+ di_node_t root;
+
+ /*
+ * DINFOCACHE is equivalent to DINFOSUBTREE | DINFOMINOR |
+ * DINFOPROP | DINFOFORCE.
+ */
+ if ((root = di_init("/", DINFOCACHE)) == DI_NODE_NIL) {
+ (void) fprintf(stderr, "ifconfig: di_init failed;"
+ " check the devinfo driver.\n");
+ exit(1);
+ }
+
+ (void) di_walk_minor(root, DDI_NT_NET, DI_CHECK_ALIAS, NULL,
+ devfs_entry);
+ di_fini(root);
+
+ /*
+ * Now, translate the linked list into
+ * a struct lifreq buffer
+ */
+ bufsize = num_ni * sizeof (struct lifreq);
+ if ((buf = malloc(bufsize)) == NULL)
+ Perror0_exit("foreachinterface: malloc failed");
+
+ lifc.lifc_family = AF_UNSPEC;
+ lifc.lifc_flags = lifc_flags;
+ lifc.lifc_len = bufsize;
+ lifc.lifc_buf = buf;
+
+ for (n = 0, lifrp = lifc.lifc_req; n < num_ni; n++, lifrp++) {
+ nip = ni_list;
+ (void) strncpy(lifrp->lifr_name, nip->ni_name,
+ sizeof (lifr.lifr_name));
+ ni_list = nip->ni_next;
+ free(nip);
+ }
+
+ plumball = 1;
+ } else {
+ lifn.lifn_family = AF_UNSPEC;
+ lifn.lifn_flags = lifc_flags;
+ if (ioctl(s, SIOCGLIFNUM, (char *)&lifn) < 0) {
+ Perror0_exit("Could not determine number"
+ " of interfaces");
+ }
+ numifs = lifn.lifn_count;
+ if (debug)
+ (void) printf("ifconfig: %d interfaces\n", numifs);
+
+ bufsize = numifs * sizeof (struct lifreq);
+ if ((buf = malloc(bufsize)) == NULL) {
+ Perror0("out of memory\n");
+ (void) close(s);
+ return;
+ }
+
+ lifc.lifc_family = AF_UNSPEC;
+ lifc.lifc_flags = lifc_flags;
+ lifc.lifc_len = bufsize;
+ lifc.lifc_buf = buf;
+
+ if (ioctl(s, SIOCGLIFCONF, (char *)&lifc) < 0) {
+ Perror0("SIOCGLIFCONF");
+ (void) close(s);
+ free(buf);
+ return;
+ }
+ }
+
+ lifrp = lifc.lifc_req;
+ for (n = lifc.lifc_len / sizeof (struct lifreq); n > 0; n--, lifrp++) {
+
+ if (!plumball) {
+ /*
+ * We must close and recreate the socket each time
+ * since we don't know what type of socket it is now
+ * (each status function may change it).
+ */
+
+ (void) close(s);
+
+ af = lifrp->lifr_addr.ss_family;
+ s = socket(SOCKET_AF(af), SOCK_DGRAM, 0);
+ if (s == -1) {
+ /*
+ * Perror0() assumes the name to be in the
+ * globally defined lifreq structure.
+ */
+ (void) strncpy(lifr.lifr_name,
+ lifrp->lifr_name, sizeof (lifr.lifr_name));
+ Perror0_exit("socket");
+ }
+ }
+
+ /*
+ * Only service interfaces that match the on and off
+ * flags masks.
+ */
+ if (onflags || offflags) {
+ (void) memset(&lifrl, 0, sizeof (lifrl));
+ (void) strncpy(lifrl.lifr_name, lifrp->lifr_name,
+ sizeof (lifrl.lifr_name));
+ if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifrl) < 0) {
+ /*
+ * Perror0() assumes the name to be in the
+ * globally defined lifreq structure.
+ */
+ (void) strncpy(lifr.lifr_name,
+ lifrp->lifr_name, sizeof (lifr.lifr_name));
+ Perror0_exit("foreachinterface: SIOCGLIFFLAGS");
+ }
+ if ((lifrl.lifr_flags & onflags) != onflags)
+ continue;
+ if ((~lifrl.lifr_flags & offflags) != offflags)
+ continue;
+ }
+
+ if (!plumball) {
+ (void) strncpy(lifrl.lifr_name, lifrp->lifr_name,
+ sizeof (lifrl.lifr_name));
+ if (ioctl(s, SIOCGLIFADDR, (caddr_t)&lifrl) < 0) {
+ /*
+ * Perror0() assumes the name to be in the
+ * globally defined lifreq structure.
+ */
+ (void) strncpy(lifr.lifr_name,
+ lifrp->lifr_name, sizeof (lifr.lifr_name));
+ Perror0("foreachinterface: SIOCGLIFADDR");
+ continue;
+ }
+ if (lifrl.lifr_addr.ss_family != af) {
+ /* Switch address family */
+ af = lifrl.lifr_addr.ss_family;
+ (void) close(s);
+
+ s = socket(SOCKET_AF(af), SOCK_DGRAM, 0);
+ if (s == -1) {
+ /*
+ * Perror0() assumes the name to be in
+ * the globally defined lifreq
+ * structure.
+ */
+ (void) strncpy(lifr.lifr_name,
+ lifrp->lifr_name,
+ sizeof (lifr.lifr_name));
+ Perror0_exit("socket");
+ }
+ }
+ }
+
+ /*
+ * Reset global state
+ * setaddr: Used by parser to tear apart source and dest
+ * name and origname contain the name of the 'current'
+ * interface.
+ */
+ setaddr = 0;
+ (void) strncpy(name, lifrp->lifr_name, sizeof (name));
+ (void) strncpy(origname, name, sizeof (origname));
+
+ if (partial) {
+ if (debug)
+ (void) fprintf(stderr,
+ "checking partial %s against %s\n",
+ partname, name);
+ if (strncmp(name, partname, strlen(partname)))
+ continue;
+ }
+ (*func)(argc, argv, save_af, lifrp);
+ /* the func could have overwritten origname, so restore */
+ (void) strncpy(name, origname, sizeof (name));
+ }
+ free(buf);
+}
+
+static void
+tun_reality_check(void)
+{
+ struct iftun_req treq;
+ ipsec_req_t *ipsr;
+
+ (void) strncpy(treq.ifta_lifr_name, name, sizeof (treq.ifta_lifr_name));
+ if (strchr(name, ':') != NULL) {
+ /* Return, we don't need to check. */
+ return;
+ }
+ if (ioctl(s, SIOCGTUNPARAM, (caddr_t)&treq) < 0 ||
+ (treq.ifta_flags & IFTUN_SECURITY) == 0) {
+ /*
+ * Either not a tunnel (the SIOCGTUNPARAM fails on
+ * non-tunnels), or the security flag is not set. Either
+ * way, return.
+ */
+ return;
+ }
+
+ ipsr = (ipsec_req_t *)&treq.ifta_secinfo;
+
+ if (ipsr->ipsr_esp_req != 0 &&
+ ipsr->ipsr_esp_auth_alg == SADB_AALG_NONE &&
+ ipsr->ipsr_ah_req == 0)
+ (void) fprintf(stderr, "ifconfig: WARNING - tunnel with "
+ "only ESP and potentially no authentication.\n");
+}
+
+/*
+ * for the specified interface call (*func)(argc, argv, af, lifrp).
+ */
+
+static void
+ifconfig(int argc, char *argv[], int af, struct lifreq *lifrp)
+{
+ static boolean_t scan_netmask = _B_FALSE;
+ int ret;
+
+ if (argc == 0) {
+ status();
+ return;
+ }
+
+ if (strcmp(*argv, "auto-dhcp") == 0 || strcmp(*argv, "dhcp") == 0) {
+ if (af == AF_INET) {
+
+ /*
+ * some errors are ignored in the case where
+ * more than one interface is being operated on.
+ */
+ ret = setifdhcp("ifconfig", name, argc, argv);
+ if (ret == DHCP_EXIT_IF_FAILURE) {
+ if (!all)
+ exit(DHCP_EXIT_FAILURE);
+ } else if (ret != DHCP_EXIT_SUCCESS)
+ exit(ret);
+ } else
+ (void) fprintf(stderr, "ifconfig: dhcp not supported "
+ "for inet6\n");
+ return;
+ }
+
+ /*
+ * The following is a "hack" to get around the existing interface
+ * setting mechanism. Currently, each interface attribute,
+ * such as address, netmask, broadcast, ... is set separately. But
+ * sometimes two or more attributes must be set together. For
+ * example, setting an address without a netmask does not make sense.
+ * Yet they can be set separately for IPv4 address using the current
+ * ifconfig(1M) syntax. The kernel then "infers" the correct netmask
+ * using the deprecated "IP address classes." This is simply not
+ * correct.
+ *
+ * The "hack" below is to go thru the whole command list looking for
+ * the netmask command first. Then use this netmask to set the
+ * address. This does not provide an extensible way to accommodate
+ * future need for setting more than one attributes together.
+ *
+ * ifconfig(1M) is too overloaded and the code is so convoluted
+ * that it is "safer" not to re-architect the code to fix the above
+ * issue, hence this "hack." We may be better off to have a new
+ * command with better syntax for configuring network interface
+ * parameters...
+ */
+ if (!scan_netmask && afp->af_af == AF_INET) {
+ int largc;
+ char **largv;
+
+ /* Only go thru the command list once to find the netmask. */
+ scan_netmask = _B_TRUE;
+
+ /*
+ * Currently, if multiple netmask commands are specified, the
+ * last one will be used as the final netmask. So we need
+ * to scan the whole list to preserve this behavior.
+ */
+ for (largc = argc, largv = argv; largc > 0; largc--, largv++) {
+ if (strcmp(*largv, NETMASK_CMD) == 0) {
+ if (--largc == 0)
+ break;
+ largv++;
+ if (strcmp(*largv, "+") == 0) {
+ if (in_getmask((struct sockaddr_in *)
+ &g_netmask)) {
+ g_netmask_set = _B_TRUE;
+ }
+ } else {
+ in_getaddr(*largv, (struct sockaddr *)
+ &g_netmask, NULL);
+ g_netmask_set = _B_TRUE;
+ }
+ /* Continue the scan. */
+ }
+ }
+ }
+
+ while (argc > 0) {
+ struct cmd *p;
+ boolean_t found_cmd;
+
+ if (debug)
+ (void) printf("ifconfig: argv %s\n", *argv);
+
+ found_cmd = _B_FALSE;
+ for (p = cmds; p->c_func; p++) {
+ if (p->c_name) {
+ if (strcmp(*argv, p->c_name) == 0) {
+ /*
+ * indicate that the command was
+ * found and check to see if
+ * the address family is valid
+ */
+ found_cmd = _B_TRUE;
+ if (p->c_af == AF_ANY ||
+ af == p->c_af)
+ break;
+ }
+ } else {
+ if (p->c_af == AF_ANY ||
+ af == p->c_af)
+ break;
+ }
+ }
+ /*
+ * If we found the keyword, but the address family
+ * did not match spit out an error
+ */
+ if (found_cmd && p->c_name == 0) {
+ (void) fprintf(stderr, "ifconfig: Operation %s not"
+ " supported for %s\n", *argv, afp->af_name);
+ exit(1);
+ }
+ /*
+ * else (no keyword found), we assume it's an address
+ * of some sort
+ */
+ if (p->c_name == 0 && setaddr)
+ p++; /* got src, do dst */
+ if (p->c_func) {
+ if (p->c_af == AF_INET6) {
+ v4compat = 0;
+ }
+ if (p->c_parameter == NEXTARG ||
+ p->c_parameter == OPTARG) {
+ argc--, argv++;
+ if (argc == 0 && p->c_parameter == NEXTARG) {
+ (void) fprintf(stderr,
+ "ifconfig: no argument for %s\n",
+ p->c_name);
+ exit(1);
+ }
+ }
+ /*
+ * Call the function if:
+ *
+ * there's no address family
+ * restriction
+ * OR
+ * we don't know the address yet
+ * (because we were called from
+ * main)
+ * OR
+ * there is a restriction AND
+ * the address families match
+ */
+ if ((p->c_af == AF_ANY) ||
+ (lifrp == (struct lifreq *)NULL) ||
+ (lifrp->lifr_addr.ss_family == p->c_af)) {
+ ret = (*p->c_func)(*argv, p->c_parameter);
+ /*
+ * If c_func failed and we should
+ * abort processing for this
+ * interface on failure, return
+ * now rather than going on to
+ * process other commands for
+ * the same interface.
+ */
+ if (ret != 0 && p->c_abortonfail)
+ return;
+ }
+ }
+ argc--, argv++;
+ }
+
+ /* Check to see if there's a security hole in the tunnel setup. */
+ tun_reality_check();
+}
+
+/* ARGSUSED */
+static int
+setdebugflag(char *val, int64_t arg)
+{
+ debug++;
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+setverboseflag(char *val, int64_t arg)
+{
+ verbose++;
+ return (0);
+}
+
+/*
+ * Set the interface address. Handles <addr>, <addr>/<n> as well as /<n>
+ * syntax for setting the address, the address plus netmask, and just
+ * the netmask respectively.
+ */
+/* ARGSUSED */
+static int
+setifaddr(char *addr, int64_t param)
+{
+ int prefixlen = 0;
+ struct sockaddr_storage laddr;
+ struct sockaddr_storage netmask;
+ struct sockaddr_in6 *sin6;
+ struct sockaddr_in *sin;
+ struct sockaddr_storage sav_netmask;
+
+ if (addr[0] == '/')
+ return (setifprefixlen(addr, 0));
+
+ (*afp->af_getaddr)(addr, (struct sockaddr *)&laddr, &prefixlen);
+
+ (void) memset(&netmask, 0, sizeof (netmask));
+ netmask.ss_family = afp->af_af;
+ switch (prefixlen) {
+ case NO_PREFIX:
+ /* Nothing there - ok */
+ break;
+ case BAD_ADDR:
+ (void) fprintf(stderr, "ifconfig: Bad prefix length in %s\n",
+ addr);
+ exit(1);
+ default:
+ if (afp->af_af == AF_INET6) {
+ sin6 = (struct sockaddr_in6 *)&netmask;
+ if (!in_prefixlentomask(prefixlen, ADDRBITS_V6,
+ (uchar_t *)&sin6->sin6_addr)) {
+ (void) fprintf(stderr, "ifconfig: "
+ "Bad prefix length: %d\n",
+ prefixlen);
+ exit(1);
+ }
+ } else {
+ sin = (struct sockaddr_in *)&netmask;
+ if (!in_prefixlentomask(prefixlen, ADDRBITS_V4,
+ (uchar_t *)&sin->sin_addr)) {
+ (void) fprintf(stderr, "ifconfig: "
+ "Bad prefix length: %d\n",
+ prefixlen);
+ exit(1);
+ }
+ }
+ /*
+ * Just in case of funny setting of both prefix and netmask,
+ * prefix should override the netmask command.
+ */
+ g_netmask_set = _B_FALSE;
+ break;
+ }
+ /* Tell parser that an address was set */
+ setaddr++;
+ /* save copy of netmask to restore in case of error */
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+ if (ioctl(s, SIOCGLIFNETMASK, (caddr_t)&lifr) < 0)
+ Perror0_exit("SIOCGLIFNETMASK");
+ sav_netmask = lifr.lifr_addr;
+
+ /*
+ * Catch set of address for AF_INET6 to perform
+ * duplicate address detection. Check that the interface is
+ * up.
+ */
+ if (afp->af_af == AF_INET6) {
+ if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0) {
+ Perror0_exit("ifsetaddr: SIOCGLIFFLAGS");
+ }
+ if (lifr.lifr_flags & IFF_UP) {
+ if (debug)
+ (void) printf(
+ "setifaddr: Calling ifdad flags %llx\n",
+ lifr.lifr_flags);
+ if (ifdad(name, (struct sockaddr_in6 *)&laddr) == -1)
+ exit(3);
+ }
+ }
+
+ /*
+ * If setting the address and not the mask, clear any existing mask
+ * and the kernel will then assign the default (netmask has been set
+ * to 0 in this case). If setting both (either by using a prefix or
+ * using the netmask command), set the mask first, so the address will
+ * be interpreted correctly.
+ */
+ lifr.lifr_addr = g_netmask_set ? g_netmask : netmask;
+ if (ioctl(s, SIOCSLIFNETMASK, (caddr_t)&lifr) < 0)
+ Perror0_exit("SIOCSLIFNETMASK");
+
+ if (debug) {
+ char abuf[INET6_ADDRSTRLEN];
+ void *addr = (afp->af_af == AF_INET) ?
+ (void *)&((struct sockaddr_in *)&laddr)->sin_addr :
+ (void *)&((struct sockaddr_in6 *)&laddr)->sin6_addr;
+
+ (void) printf("Setting %s af %d addr %s\n",
+ lifr.lifr_name, afp->af_af,
+ inet_ntop(afp->af_af, addr, abuf, sizeof (abuf)));
+ }
+ lifr.lifr_addr = laddr;
+ lifr.lifr_addr.ss_family = afp->af_af;
+ if (ioctl(s, SIOCSLIFADDR, (caddr_t)&lifr) < 0) {
+ /*
+ * Restore the netmask
+ */
+ int saverr = errno;
+
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+ lifr.lifr_addr = sav_netmask;
+ (void) ioctl(s, SIOCSLIFNETMASK, (caddr_t)&lifr);
+ errno = saverr;
+ Perror0_exit("SIOCSLIFADDR");
+ }
+
+ return (0);
+}
+
+/*
+ * The following functions are stolen from the ipseckey(1m) program.
+ * Perhaps they should be somewhere common, but for now, we just maintain
+ * two versions. We do this because of the different semantics for which
+ * algorithms we select ("requested" for ifconfig vs. "actual" for key).
+ */
+
+static ulong_t
+parsenum(char *num)
+{
+ ulong_t rc;
+ char *end = NULL;
+
+ errno = 0;
+ rc = strtoul(num, &end, 0);
+ if (errno != 0 || end == num || *end != '\0') {
+ rc = (ulong_t)-1;
+ }
+
+ return (rc);
+}
+
+/*
+ * Parse and reverse parse possible algorithm values, include numbers.
+ * Mostly stolen from ipseckey.c. See the comments above parsenum() for why
+ * this isn't common to ipseckey.c.
+ *
+ * NOTE: Static buffer in this function for the return value. Since ifconfig
+ * isn't multithreaded, this isn't a huge problem.
+ */
+
+#define NBUF_SIZE 20 /* Enough to print a large integer. */
+
+static char *
+rparsealg(uint8_t alg_value, int proto_num)
+{
+ struct ipsecalgent *alg;
+ static char numprint[128]; /* Enough to hold an algorithm name. */
+
+ /* Special-case 0 to return "<any-none>" */
+ if (alg_value == 0)
+ return ("<any-none>");
+
+ alg = getipsecalgbynum(alg_value, proto_num, NULL);
+ if (alg != NULL) {
+ (void) strlcpy(numprint, alg->a_names[0], sizeof (numprint));
+ freeipsecalgent(alg);
+ } else {
+ (void) snprintf(numprint, sizeof (numprint), "%d", alg_value);
+ }
+
+ return (numprint);
+}
+
+static uint_t
+parsealg(char *algname, int proto_num)
+{
+ struct ipsecalgent *alg;
+ ulong_t invalue;
+
+ if (algname == NULL) {
+ (void) fprintf(stderr, "ifconfig: Unexpected end of command "
+ "line.\n");
+ exit(1);
+ }
+
+ /*
+ * Special-case "none". Use strcasecmp because its length is
+ * bound.
+ */
+ if (strcasecmp("none", algname) == 0) {
+ return ((proto_num == IPSEC_PROTO_ESP) ?
+ NO_ESP_EALG : NO_ESP_AALG);
+ }
+
+ alg = getipsecalgbyname(algname, proto_num, NULL);
+ if (alg != NULL) {
+ invalue = alg->a_alg_num;
+ freeipsecalgent(alg);
+ return ((uint_t)invalue);
+ }
+
+ /*
+ * Since algorithms can be loaded during kernel run-time, check for
+ * numeric algorithm values too.
+ */
+ invalue = parsenum(algname);
+ if ((invalue & (ulong_t)0xff) == invalue)
+ return ((uint_t)invalue);
+
+ (void) fprintf(stderr, "ifconfig: %s algorithm type %s unknown.\n",
+ (proto_num == IPSEC_PROTO_ESP) ?
+ "Encryption" : "Authentication", algname);
+ exit(1);
+ /* NOTREACHED */
+}
+
+/*
+ * Actual ifconfig functions to set tunnel security properties.
+ */
+
+enum ipsec_alg_type { ESP_ENCR_ALG = 1, ESP_AUTH_ALG, AH_AUTH_ALG };
+
+boolean_t first_set_tun = _B_TRUE;
+boolean_t encr_alg_set = _B_FALSE;
+
+static int
+set_tun_algs(int which_alg, int alg)
+{
+ struct iftun_req treq;
+ ipsec_req_t *ipsr;
+
+ (void) strncpy(treq.ifta_lifr_name, name, sizeof (treq.ifta_lifr_name));
+ if (strchr(name, ':') != NULL) {
+ errno = EPERM;
+ Perror0_exit("Tunnel params on logical interfaces");
+ }
+ if (ioctl(s, SIOCGTUNPARAM, (caddr_t)&treq) < 0) {
+ if (errno == EOPNOTSUPP || errno == EINVAL)
+ Perror0_exit("Not a tunnel");
+ else Perror0_exit("SIOCGTUNPARAM");
+ }
+
+ ipsr = (ipsec_req_t *)&treq.ifta_secinfo;
+
+ if (treq.ifta_vers != IFTUN_VERSION) {
+ (void) fprintf(stderr,
+ "Kernel tunnel secinfo version mismatch.\n");
+ exit(1);
+ }
+
+ /*
+ * If I'm just starting off this ifconfig, I want a clean slate,
+ * otherwise, I've captured the current tunnel security settings.
+ * In the case of continuation, I merely add to the settings.
+ */
+ if (first_set_tun) {
+ first_set_tun = _B_FALSE;
+ (void) memset(ipsr, 0, sizeof (*ipsr));
+ }
+
+ treq.ifta_flags = IFTUN_SECURITY;
+
+ switch (which_alg) {
+ case ESP_ENCR_ALG:
+ if (alg == NO_ESP_EALG) {
+ if (ipsr->ipsr_esp_auth_alg == SADB_AALG_NONE)
+ ipsr->ipsr_esp_req = 0;
+ ipsr->ipsr_esp_alg = SADB_EALG_NONE;
+ } else {
+ encr_alg_set = _B_TRUE;
+ ipsr->ipsr_esp_req =
+ IPSEC_PREF_REQUIRED | IPSEC_PREF_UNIQUE;
+ ipsr->ipsr_esp_alg = alg;
+ }
+ break;
+ case ESP_AUTH_ALG:
+ if (alg == NO_ESP_AALG) {
+ if (ipsr->ipsr_esp_alg == SADB_EALG_NONE ||
+ ipsr->ipsr_esp_alg == SADB_EALG_NULL)
+ ipsr->ipsr_esp_req = 0;
+ ipsr->ipsr_esp_auth_alg = SADB_AALG_NONE;
+ } else {
+ ipsr->ipsr_esp_req =
+ IPSEC_PREF_REQUIRED | IPSEC_PREF_UNIQUE;
+ ipsr->ipsr_esp_auth_alg = alg;
+
+ /* Let the user specify NULL encryption implicitly. */
+ if (ipsr->ipsr_esp_alg == SADB_EALG_NONE &&
+ !encr_alg_set)
+ ipsr->ipsr_esp_alg = SADB_EALG_NULL;
+ }
+ break;
+ case AH_AUTH_ALG:
+ if (alg == NO_AH_AALG) {
+ ipsr->ipsr_ah_req = 0;
+ ipsr->ipsr_auth_alg = SADB_AALG_NONE;
+ } else {
+ ipsr->ipsr_ah_req =
+ IPSEC_PREF_REQUIRED | IPSEC_PREF_UNIQUE;
+ ipsr->ipsr_auth_alg = alg;
+ }
+ break;
+ /* Will never hit DEFAULT */
+ }
+
+ if (ioctl(s, SIOCSTUNPARAM, (caddr_t)&treq) < 0) {
+ Perror2_exit("set tunnel security properties",
+ treq.ifta_lifr_name);
+ }
+
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+set_tun_esp_encr_alg(char *addr, int64_t param)
+{
+ return (set_tun_algs(ESP_ENCR_ALG,
+ parsealg(addr, IPSEC_PROTO_ESP)));
+}
+
+/* ARGSUSED */
+static int
+set_tun_esp_auth_alg(char *addr, int64_t param)
+{
+ return (set_tun_algs(ESP_AUTH_ALG,
+ parsealg(addr, IPSEC_PROTO_AH)));
+}
+
+/* ARGSUSED */
+static int
+set_tun_ah_alg(char *addr, int64_t param)
+{
+ return (set_tun_algs(AH_AUTH_ALG,
+ parsealg(addr, IPSEC_PROTO_AH)));
+}
+
+/* ARGSUSED */
+static int
+setifrevarp(char *arg, int64_t param)
+{
+ struct sockaddr_in laddr;
+
+ if (afp->af_af == AF_INET6) {
+ (void) fprintf(stderr,
+ "ifconfig: revarp not possible on IPv6 interface %s\n",
+ name);
+ exit(1);
+ }
+ if (doifrevarp(name, &laddr)) {
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+ laddr.sin_family = AF_INET;
+ (void) memcpy(&lifr.lifr_addr, &laddr, sizeof (laddr));
+ if (ioctl(s, SIOCSLIFADDR, (caddr_t)&lifr) < 0)
+ Perror0_exit("SIOCSLIFADDR");
+ }
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+setifsubnet(char *addr, int64_t param)
+{
+ int prefixlen = 0;
+ struct sockaddr_storage subnet;
+
+ (*afp->af_getaddr)(addr, &subnet, &prefixlen);
+
+ switch (prefixlen) {
+ case NO_PREFIX:
+ (void) fprintf(stderr,
+ "ifconfig: Missing prefix length in subnet %s\n", addr);
+ exit(1);
+ /* NOTREACHED */
+ case BAD_ADDR:
+ (void) fprintf(stderr,
+ "ifconfig: Bad prefix length in %s\n", addr);
+ exit(1);
+ default:
+ break;
+ }
+
+ lifr.lifr_addr = subnet;
+ lifr.lifr_addrlen = prefixlen;
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+ if (ioctl(s, SIOCSLIFSUBNET, (caddr_t)&lifr) < 0)
+ Perror0_exit("SIOCSLIFSUBNET");
+
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+setifnetmask(char *addr, int64_t param)
+{
+ struct sockaddr_in netmask;
+
+ assert(afp->af_af != AF_INET6);
+
+ if (strcmp(addr, "+") == 0) {
+ if (in_getmask(&netmask) == 0)
+ return (0);
+ } else {
+ in_getaddr(addr, (struct sockaddr *)&netmask, NULL);
+ }
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+ (void) memcpy(&lifr.lifr_addr, &netmask, sizeof (netmask));
+ if (ioctl(s, SIOCSLIFNETMASK, (caddr_t)&lifr) < 0)
+ Perror0_exit("SIOCSLIFNETMASK");
+ return (0);
+}
+
+/*
+ * Parse '/<n>' as a netmask.
+ */
+/* ARGSUSED */
+static int
+setifprefixlen(char *addr, int64_t param)
+{
+ int prefixlen;
+ int af = afp->af_af;
+
+ prefixlen = in_getprefixlen(addr, _B_TRUE,
+ (af == AF_INET) ? ADDRBITS_V4 : ADDRBITS_V6);
+ if (prefixlen < 0) {
+ (void) fprintf(stderr,
+ "ifconfig: Bad prefix length in %s\n", addr);
+ exit(1);
+ }
+ (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr));
+ lifr.lifr_addr.ss_family = af;
+ if (af == AF_INET6) {
+ struct sockaddr_in6 *sin6;
+
+ sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
+ if (!in_prefixlentomask(prefixlen, ADDRBITS_V6,
+ (uchar_t *)&sin6->sin6_addr)) {
+ (void) fprintf(stderr, "ifconfig: "
+ "Bad prefix length: %d\n",
+ prefixlen);
+ exit(1);
+ }
+ } else if (af == AF_INET) {
+ struct sockaddr_in *sin;
+
+ sin = (struct sockaddr_in *)&lifr.lifr_addr;
+ if (!in_prefixlentomask(prefixlen, ADDRBITS_V4,
+ (uchar_t *)&sin->sin_addr)) {
+ (void) fprintf(stderr, "ifconfig: "
+ "Bad prefix length: %d\n",
+ prefixlen);
+ exit(1);
+ }
+ } else {
+ (void) fprintf(stderr, "ifconfig: setting prefix only supported"
+ " for address family inet or inet6\n");
+ exit(1);
+ }
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+ if (ioctl(s, SIOCSLIFNETMASK, (caddr_t)&lifr) < 0)
+ Perror0_exit("SIOCSLIFNETMASK");
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+setifbroadaddr(char *addr, int64_t param)
+{
+ struct sockaddr_in broadaddr;
+
+ assert(afp->af_af != AF_INET6);
+
+ if (strcmp(addr, "+") == 0) {
+ /*
+ * This doesn't set the broadcast address at all. Rather, it
+ * gets, then sets the interface's address, relying on the fact
+ * that resetting the address will reset the broadcast address.
+ */
+ (void) strncpy(lifr.lifr_name, name,
+ sizeof (lifr.lifr_name));
+ if (ioctl(s, SIOCGLIFADDR, (caddr_t)&lifr) < 0) {
+ if (errno != EADDRNOTAVAIL)
+ Perror0_exit("SIOCGLIFADDR");
+ return (0);
+ }
+ if (ioctl(s, SIOCSLIFADDR, (caddr_t)&lifr) < 0)
+ Perror0_exit("SIOCGLIFADDR");
+
+ return (0);
+ }
+ in_getaddr(addr, (struct sockaddr *)&broadaddr, NULL);
+
+ (void) memcpy(&lifr.lifr_addr, &broadaddr, sizeof (broadaddr));
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+ if (ioctl(s, SIOCSLIFBRDADDR, (caddr_t)&lifr) < 0)
+ Perror0_exit("SIOCSLIFBRDADDR");
+ return (0);
+}
+
+/*
+ * set interface destination address
+ */
+/* ARGSUSED */
+static int
+setifdstaddr(char *addr, int64_t param)
+{
+ (*afp->af_getaddr)(addr, (struct sockaddr *)&lifr.lifr_addr, NULL);
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+ if (ioctl(s, SIOCSLIFDSTADDR, (caddr_t)&lifr) < 0)
+ Perror0_exit("setifdstaddr: SIOCSLIFDSTADDR");
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+setifflags(char *val, int64_t value)
+{
+ int phyintlen, origphyintlen;
+
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+ if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0)
+ Perror0_exit("setifflags: SIOCGLIFFLAGS");
+
+ if (value == IFF_NOFAILOVER) {
+ /*
+ * Fail if '-failover' is set after a prior addif created the
+ * alias on a different interface. This can happen when the
+ * interface is part of an IPMP group.
+ */
+ phyintlen = strcspn(name, ":");
+ origphyintlen = strcspn(origname, ":");
+ if (phyintlen != origphyintlen ||
+ strncmp(name, origname, phyintlen) != 0) {
+ (void) fprintf(stderr, "ifconfig: can't set -failover "
+ "on failed/standby/offlined interface %s\n",
+ origname);
+ exit(1);
+ }
+ }
+
+ /*
+ * Catch "up" transition for AF_INET6 to perform duplicate address
+ * detection. ifdad checks if an address has been set.
+ */
+ if (afp->af_af == AF_INET6 && !(lifr.lifr_flags & IFF_UP) &&
+ value == IFF_UP) {
+ if (debug)
+ (void) printf(
+ "setifaddr:Calling ifdad flags %llx value 0x%llx\n",
+ lifr.lifr_flags, value);
+ if (ifdad(name, NULL) == -1)
+ exit(1);
+ }
+
+ if (value < 0) {
+ value = -value;
+ lifr.lifr_flags &= ~value;
+ } else
+ lifr.lifr_flags |= value;
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+ if (ioctl(s, SIOCSLIFFLAGS, (caddr_t)&lifr) < 0) {
+ Perror0_exit("setifflags: SIOCSLIFFLAGS");
+ }
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+setifmetric(char *val, int64_t param)
+{
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+ lifr.lifr_metric = atoi(val);
+ if (ioctl(s, SIOCSLIFMETRIC, (caddr_t)&lifr) < 0)
+ Perror0_exit("setifmetric: SIOCSLIFMETRIC");
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+setifmtu(char *val, int64_t param)
+{
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+ lifr.lifr_mtu = atoi(val);
+ if (ioctl(s, SIOCSLIFMTU, (caddr_t)&lifr) < 0)
+ Perror0_exit("setifmtu: SIOCSLIFMTU");
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+setifindex(char *val, int64_t param)
+{
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+ lifr.lifr_index = atoi(val);
+ if (ioctl(s, SIOCSLIFINDEX, (caddr_t)&lifr) < 0)
+ Perror0_exit("setifindex: SIOCSLIFINDEX");
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+setifether(char *addr, int64_t param)
+{
+ uchar_t *ea;
+ iface_t *current;
+ int maclen;
+
+ if (addr == NULL) {
+ ifstatus(name);
+ print_ifether(name);
+ return (0);
+ }
+
+ phyif = NULL;
+ logifs = NULL;
+
+ /*
+ * if the IP interface in the arguments is a logical
+ * interface, exit with an error now.
+ */
+ if (strchr(name, ':') != NULL) {
+ (void) fprintf(stderr, "ifconfig: cannot change"
+ " ethernet address of a logical interface\n");
+ exit(1);
+ }
+
+ ea = _link_aton(addr, &maclen);
+ if (ea == NULL) {
+ if (maclen == -1)
+ (void) fprintf(stderr,
+ "ifconfig: %s: bad address\n", addr);
+ else
+ (void) fprintf(stderr, "ifconfig: malloc() failed\n");
+ exit(1);
+ }
+
+ (void) strncpy(savedname, name, sizeof (savedname));
+
+ /*
+ * Call selectifs only for the IP interfaces that are ipv4.
+ * offflags == IFF_IPV6 because you should not change the
+ * Ethernet address of an ipv6 interface
+ */
+ foreachinterface(selectifs, 0, (char **)NULL, 0, 0, IFF_IPV6, 0);
+
+ /* If physical interface not found, exit now */
+ if (phyif == NULL) {
+ (void) fprintf(stderr,
+ "ifconfig: interface %s not found\n", savedname);
+ exit(1);
+ }
+
+ /* Restore */
+ (void) strncpy(name, savedname, sizeof (name));
+ (void) strncpy(origname, savedname, sizeof (origname));
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+
+ /*
+ * close and reopen the socket
+ * we don't know which type of socket we have now
+ */
+ (void) close(s);
+ s = socket(SOCKET_AF(AF_UNSPEC), SOCK_DGRAM, 0);
+ if (s < 0) {
+ Perror0_exit("socket");
+ }
+
+ /*
+ * mark down the logical interfaces first,
+ * and then the physical interface
+ */
+ if (updownifs(logifs, 0) < 0 || updownifs(phyif, 0) < 0) {
+ Perror0_exit("mark down interface failed");
+ }
+
+ /*
+ * Change the physical address
+ */
+ if (dlpi_set_address(savedname, ea, maclen) == -1) {
+ (void) fprintf(stderr,
+ "ifconfig: failed setting mac address on %s\n",
+ savedname);
+ }
+
+ /*
+ * if any interfaces were marked down before changing the
+ * ethernet address, put them up again.
+ * First the physical interface, then the logical ones.
+ */
+ if (updownifs(phyif, 1) < 0 || updownifs(logifs, 1) < 0) {
+ Perror0_exit("mark down interface failed");
+ }
+
+ /* Free the memory allocated by selectifs */
+ free(phyif);
+ for (current = logifs; current != NULL; current = logifs) {
+ logifs = logifs->next;
+ free(current);
+ }
+
+ return (0);
+}
+
+/*
+ * Print an interface's Ethernet address, if it has one.
+ */
+static void
+print_ifether(char *ifname)
+{
+ int protocol;
+ icfg_if_t interface;
+ icfg_handle_t handle;
+ int fd;
+
+ /*
+ * Loopback interfaces and tunnels can't have MAC addresses,
+ * so filter them out first.
+ */
+ if (strcmp(ifname, LOOPBACK_IF) == 0)
+ return;
+
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+
+ fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd == -1 || ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) {
+ /*
+ * It's possible the interface is only configured for
+ * IPv6; check again with AF_INET6.
+ */
+ (void) close(fd);
+ fd = socket(AF_INET6, SOCK_DGRAM, 0);
+ if (fd == -1 || ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) {
+ (void) close(fd);
+ return;
+ }
+ }
+ (void) close(fd);
+
+ /* Virtual interfaces don't have MAC addresses */
+ if (lifr.lifr_flags & IFF_VIRTUAL)
+ return;
+
+ /*
+ * We must be careful to set if_protocol based on the current
+ * properties of the interface. For instance, if "ip.tun0" is
+ * configured only as an IPv6 tunnel, then if_protocol must be
+ * set to AF_INET6 or icfg_get_tunnel_lower() will fail and
+ * we will falsely conclude that it's not a tunnel.
+ */
+ interface.if_protocol = AF_INET;
+ if (lifr.lifr_flags & IFF_IPV6)
+ interface.if_protocol = AF_INET6;
+
+ (void) strncpy(interface.if_name, ifname, sizeof (interface.if_name));
+
+ if (icfg_open(&handle, &interface) == ICFG_SUCCESS) {
+ if (icfg_get_tunnel_lower(handle, &protocol) == ICFG_SUCCESS) {
+ /* Tunnel op succeeded -- it's a tunnel so skip */
+ icfg_close(handle);
+ return;
+ }
+ icfg_close(handle);
+ }
+
+ dlpi_print_address(ifname);
+}
+
+/*
+ * static void selectifs(int argc, char *argv[], int af, struct lifreq *rp)
+ *
+ * Called inside setifether() to create a list of interfaces to
+ * mark down/up when changing the Ethernet address.
+ * If the current interface is the physical interface passed
+ * as an argument to ifconfig, update phyif.
+ * If the current interface is a logical interface associated
+ * to the physical interface, add it to the logifs list.
+ */
+/* ARGSUSED */
+static void
+selectifs(int argc, char *argv[], int af, struct lifreq *rp)
+{
+ char *colonp;
+ int length;
+ iface_t *current;
+
+ /*
+ * savedname= name of the IP interface to which you want to
+ * change ethernet address
+ * name= name of the current IP interface
+ */
+ colonp = strchr(name, ':');
+ if (colonp == NULL)
+ length = max(strlen(savedname), strlen(name));
+ else
+ length = max(strlen(savedname), colonp - name);
+ if (strncmp(savedname, name, length) == 0) {
+ (void) strcpy(lifr.lifr_name, name);
+ if (ioctl(s, SIOCGLIFFLAGS, &lifr) < 0) {
+ Perror0("selectifs: SIOCGLIFFLAGS");
+ return;
+ }
+
+ if ((current = malloc(sizeof (iface_t))) == NULL) {
+ Perror0_exit("selectifs: malloc failed\n");
+ }
+
+ if (colonp == NULL) {
+ /* this is the physical interface */
+ phyif = current;
+ bcopy(&lifr, &phyif->lifr, sizeof (struct lifreq));
+ phyif->next = NULL;
+ } else {
+ /* this is a logical interface */
+ bcopy(&lifr, &current->lifr, sizeof (struct lifreq));
+ current->next = logifs;
+ logifs = current;
+ }
+ }
+}
+
+/*
+ * static int updownifs(iface_t *ifs, int up)
+ *
+ * It takes in input a list of IP interfaces (ifs)
+ * and a flag (up).
+ * It marks each interface in the list down (up = 0)
+ * or up (up > 0). This is done ONLY if the IP
+ * interface was originally up.
+ *
+ * Return values:
+ * 0 = everything OK
+ * -1 = problem
+ */
+static int
+updownifs(iface_t *ifs, int up)
+{
+ iface_t *current;
+ int ret = 0;
+ int save_errno;
+ char savename[LIFNAMSIZ];
+ uint64_t orig_flags;
+
+ for (current = ifs; current != NULL; current = current->next) {
+ if (current->lifr.lifr_flags & IFF_UP) {
+ orig_flags = current->lifr.lifr_flags;
+ if (!up)
+ current->lifr.lifr_flags &= ~IFF_UP;
+ if (ioctl(s, SIOCSLIFFLAGS, &current->lifr) < 0) {
+ save_errno = errno;
+ (void) strcpy(savename,
+ current->lifr.lifr_name);
+ ret = -1;
+ }
+ if (!up) /* restore the original flags */
+ current->lifr.lifr_flags = orig_flags;
+ }
+ }
+
+ if (ret == -1) {
+ (void) strcpy(lifr.lifr_name, savename);
+ errno = save_errno;
+ }
+ return (ret);
+}
+
+/*
+ * Create the next unused logical interface using the original name
+ * and assign the address (and mask if '/<n>' is part of the address).
+ * Use the new logical interface for subsequent subcommands by updating
+ * the name variable.
+ *
+ * This allows syntax like:
+ * ifconfig le0 addif 109.106.86.130 netmask + up \
+ * addif 109.106.86.131 netmask + up
+ */
+/* ARGSUSED */
+static int
+addif(char *str, int64_t param)
+{
+ int prefixlen = 0;
+ struct sockaddr_storage laddr;
+ struct sockaddr_storage mask;
+
+ (void) strncpy(name, origname, sizeof (name));
+
+ if (strchr(name, ':') != NULL) {
+ (void) fprintf(stderr,
+ "ifconfig: addif: bad physical interface name %s\n",
+ name);
+ exit(1);
+ }
+
+ /*
+ * clear so parser will interpret next address as source followed
+ * by possible dest
+ */
+ setaddr = 0;
+ (*afp->af_getaddr)(str, (struct sockaddr *)&laddr, &prefixlen);
+
+ switch (prefixlen) {
+ case NO_PREFIX:
+ /* Nothing there - ok */
+ break;
+ case BAD_ADDR:
+ (void) fprintf(stderr,
+ "ifconfig: Bad prefix length in %s\n", str);
+ exit(1);
+ default:
+ (void) memset(&mask, 0, sizeof (mask));
+ mask.ss_family = afp->af_af;
+ if (afp->af_af == AF_INET6) {
+ struct sockaddr_in6 *sin6;
+ sin6 = (struct sockaddr_in6 *)&mask;
+ if (!in_prefixlentomask(prefixlen, ADDRBITS_V6,
+ (uchar_t *)&sin6->sin6_addr)) {
+ (void) fprintf(stderr, "ifconfig: "
+ "Bad prefix length: %d\n",
+ prefixlen);
+ exit(1);
+ }
+ } else {
+ struct sockaddr_in *sin;
+
+ sin = (struct sockaddr_in *)&mask;
+ if (!in_prefixlentomask(prefixlen, ADDRBITS_V4,
+ (uchar_t *)&sin->sin_addr)) {
+ (void) fprintf(stderr, "ifconfig: "
+ "Bad prefix length: %d\n",
+ prefixlen);
+ exit(1);
+ }
+ }
+ break;
+ }
+
+ /* Set the address and then the mask (if there was one) */
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+ lifr.lifr_addr = laddr;
+
+ /* Note: no need to do DAD here since the interface isn't up yet. */
+
+ if (ioctl(s, SIOCLIFADDIF, (caddr_t)&lifr) < 0)
+ Perror0_exit("addif: SIOCLIFADDIF");
+
+ (void) printf("Created new logical interface %s\n",
+ lifr.lifr_name);
+ (void) strncpy(name, lifr.lifr_name, sizeof (name));
+
+ if (prefixlen >= 0) {
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+ lifr.lifr_addr = mask;
+ if (ioctl(s, SIOCSLIFNETMASK, (caddr_t)&lifr) < 0)
+ Perror0_exit("addif: SIOCSLIFNETMASK");
+ }
+ /*
+ * let parser know we got a source.
+ * Next address, if given, should be dest
+ */
+ setaddr++;
+ return (0);
+}
+
+/*
+ * Remove a logical interface based on its IP address. Unlike addif
+ * there is no '/<n>' here.
+ * Verifies that the interface is down before it is removed.
+ */
+/* ARGSUSED */
+static int
+removeif(char *str, int64_t param)
+{
+ struct sockaddr_storage laddr;
+
+ if (strchr(name, ':') != NULL) {
+ (void) fprintf(stderr,
+ "ifconfig: removeif: bad physical interface name %s\n",
+ name);
+ exit(1);
+ }
+
+ (*afp->af_getaddr)(str, &laddr, NULL);
+ lifr.lifr_addr = laddr;
+
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+ if (ioctl(s, SIOCLIFREMOVEIF, (caddr_t)&lifr) < 0) {
+ if (errno == EBUSY) {
+ /* This can only happen if ipif_id = 0 */
+ (void) fprintf(stderr,
+ "ifconfig: removeif: can't remove interface: %s\n",
+ name);
+ exit(1);
+ }
+ Perror0_exit("removeif: SIOCLIFREMOVEIF");
+ }
+ return (0);
+}
+
+/*
+ * If laddr is non-NULL it is used - otherwise we use the address on
+ * the interface.
+ */
+/* ARGSUSED */
+static int
+ifdad(char *ifname, struct sockaddr_in6 *laddr)
+{
+ struct sockaddr_in6 testaddr;
+ struct lifreq lifr2; /* Avoid overriting lifr */
+
+ if (debug)
+ (void) printf("ifdad(%s)\n", ifname);
+
+ assert(afp->af_af == AF_INET6);
+
+ /*
+ * Check the address assigned to the interface.
+ * Skip the check if IFF_NOLOCAL, IFF_NONUD, IFF_ANYCAST, or
+ * IFF_LOOPBACK.
+ * Note that IFF_NONUD turns of both NUD and DAD.
+ */
+ (void) strncpy(lifr2.lifr_name, ifname,
+ sizeof (lifr2.lifr_name));
+ if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifr2) < 0) {
+ Perror0_exit("ifdad: SIOCGLIFFLAGS");
+ }
+ if (lifr2.lifr_flags & (IFF_NOLOCAL|IFF_LOOPBACK|IFF_NONUD|IFF_ANYCAST))
+ return (0);
+
+ if (laddr != NULL) {
+ testaddr = *laddr;
+ } else {
+ if (ioctl(s, SIOCGLIFADDR, (caddr_t)&lifr2) < 0) {
+ Perror0_exit("ifdad: SIOCGLIFADDR");
+ }
+ testaddr = *(struct sockaddr_in6 *)&lifr2.lifr_addr;
+ }
+
+ if (IN6_IS_ADDR_UNSPECIFIED(&testaddr.sin6_addr))
+ return (0);
+
+ if (do_dad(name, &testaddr) != 0)
+ return (-1);
+ else
+ return (0);
+}
+
+/*
+ * Set the address token for IPv6.
+ */
+/* ARGSUSED */
+static int
+setiftoken(char *addr, int64_t param)
+{
+ int prefixlen = 0;
+ struct sockaddr_in6 token;
+
+ in6_getaddr(addr, (struct sockaddr *)&token, &prefixlen);
+ switch (prefixlen) {
+ case NO_PREFIX:
+ (void) fprintf(stderr,
+ "ifconfig: Missing prefix length in subnet %s\n", addr);
+ exit(1);
+ /* NOTREACHED */
+ case BAD_ADDR:
+ (void) fprintf(stderr,
+ "ifconfig: Bad prefix length in %s\n", addr);
+ exit(1);
+ default:
+ break;
+ }
+ (void) memcpy(&lifr.lifr_addr, &token, sizeof (token));
+ lifr.lifr_addrlen = prefixlen;
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+ if (ioctl(s, SIOCSLIFTOKEN, (caddr_t)&lifr) < 0) {
+ Perror0_exit("setiftoken: SIOCSLIFTOKEN");
+ }
+ return (0);
+}
+
+/*
+ * Return value: 0 on success, -1 on failure.
+ */
+static int
+connect_to_mpathd(int family)
+{
+ int s;
+ struct sockaddr_storage ss;
+ struct sockaddr_in *sin = (struct sockaddr_in *)&ss;
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&ss;
+ struct in6_addr loopback_addr = IN6ADDR_LOOPBACK_INIT;
+ int addrlen;
+ int ret;
+ int on;
+
+ s = socket(family, SOCK_STREAM, 0);
+ if (s < 0) {
+ Perror0_exit("connect_to_mpathd: socket");
+ }
+ (void) bzero((char *)&ss, sizeof (ss));
+ ss.ss_family = family;
+ /*
+ * Need to bind to a privileged port. For non-root, this
+ * will fail. in.mpathd verifies that only commands coming
+ * from privileged ports succeed so that ordinary users
+ * can't connect and start talking to in.mpathd
+ */
+ on = 1;
+ if (setsockopt(s, IPPROTO_TCP, TCP_ANONPRIVBIND, &on,
+ sizeof (on)) < 0) {
+ Perror0_exit("connect_to_mpathd: setsockopt");
+ }
+ switch (family) {
+ case AF_INET:
+ sin->sin_port = 0;
+ sin->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ addrlen = sizeof (struct sockaddr_in);
+ break;
+ case AF_INET6:
+ sin6->sin6_port = 0;
+ sin6->sin6_addr = loopback_addr;
+ addrlen = sizeof (struct sockaddr_in6);
+ break;
+ }
+ ret = bind(s, (struct sockaddr *)&ss, addrlen);
+ if (ret != 0) {
+ (void) close(s);
+ return (-1);
+ }
+
+ switch (family) {
+ case AF_INET:
+ sin->sin_port = htons(MPATHD_PORT);
+ break;
+ case AF_INET6:
+ sin6->sin6_port = htons(MPATHD_PORT);
+ break;
+ }
+ ret = connect(s, (struct sockaddr *)&ss, addrlen);
+ (void) close(s);
+ return (ret);
+}
+
+/* ARGSUSED */
+static int
+setifgroupname(char *grpname, int64_t param)
+{
+ if (debug) {
+ (void) printf("Setting groupname %s on interface %s\n",
+ grpname, name);
+ }
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+ (void) strncpy(lifr.lifr_groupname, grpname,
+ sizeof (lifr.lifr_groupname));
+ if (ioctl(s, SIOCSLIFGROUPNAME, (caddr_t)&lifr) < 0) {
+ Perror0_exit("setifgroupname: SIOCSLIFGROUPNAME");
+ }
+
+ /*
+ * If the SUNW_NO_MPATHD environment variable is set then don't
+ * bother starting up in.mpathd. See PSARC/2002/249 for the
+ * depressing details on this bit of stupidity.
+ */
+ if (getenv("SUNW_NO_MPATHD") != NULL) {
+ return (0);
+ }
+
+ /*
+ * Try to connect to in.mpathd using IPv4. If we succeed,
+ * we conclude that in.mpathd is running, and quit.
+ */
+ if (connect_to_mpathd(AF_INET) == 0) {
+ /* connect succeeded, mpathd is already running */
+ return (0);
+ }
+ /*
+ * Try to connect to in.mpathd using IPv6. If we succeed,
+ * we conclude that in.mpathd is running, and quit.
+ */
+ if (connect_to_mpathd(AF_INET6) == 0) {
+ /* connect succeeded, mpathd is already running */
+ return (0);
+ }
+
+ /*
+ * in.mpathd may not be running. Start it now. If it is already
+ * running, in.mpathd will take care of handling multiple incarnations
+ * of itself. ifconfig only tries to optimize performance by not
+ * starting another incarnation of in.mpathd.
+ */
+ switch (fork()) {
+
+ case -1:
+ Perror0_exit("setifgroupname: fork");
+ /* NOTREACHED */
+ case 0:
+ (void) execl(MPATHD_PATH, MPATHD_PATH, NULL);
+ _exit(1);
+ /* NOTREACHED */
+ default:
+ return (0);
+ }
+}
+
+
+/*
+ * To list all the modules above a given network interface.
+ */
+/* ARGSUSED */
+static int
+modlist(char *null, int64_t param)
+{
+ int muxfd;
+ int ipfd_lowstr;
+ int arpfd_lowstr;
+ int num_mods;
+ int i;
+ struct str_list strlist;
+ int orig_arpid;
+
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+ if (ip_domux2fd(&muxfd, &ipfd_lowstr, &arpfd_lowstr,
+ &orig_arpid) < 0) {
+ return (-1);
+ }
+ if ((num_mods = ioctl(ipfd_lowstr, I_LIST, NULL)) < 0) {
+ Perror0("cannot I_LIST to get the number of modules");
+ } else {
+ if (debug > 0) {
+ (void) printf("Listing (%d) modules above %s\n",
+ num_mods, name);
+ }
+
+ strlist.sl_nmods = num_mods;
+ strlist.sl_modlist = malloc(sizeof (struct str_mlist) *
+ num_mods);
+ if (strlist.sl_modlist == NULL) {
+ Perror0("cannot malloc");
+ } else {
+ if (ioctl(ipfd_lowstr, I_LIST, (caddr_t)&strlist) < 0) {
+ Perror0("cannot I_LIST for module names");
+ } else {
+ for (i = 0; i < strlist.sl_nmods; i++) {
+ (void) printf("%d %s\n", i,
+ strlist.sl_modlist[i].l_name);
+ }
+ }
+ free(strlist.sl_modlist);
+ }
+ }
+ return (ip_plink(muxfd, ipfd_lowstr, arpfd_lowstr, orig_arpid));
+}
+
+#define MODINSERT_OP 'i'
+#define MODREMOVE_OP 'r'
+
+/*
+ * To insert a module to the stream of the interface. It is just a
+ * wrapper. The real function is modop().
+ */
+/* ARGSUSED */
+static int
+modinsert(char *arg, int64_t param)
+{
+ return (modop(arg, MODINSERT_OP));
+}
+
+/*
+ * To remove a module from the stream of the interface. It is just a
+ * wrapper. The real function is modop().
+ */
+/* ARGSUSED */
+static int
+modremove(char *arg, int64_t param)
+{
+ return (modop(arg, MODREMOVE_OP));
+}
+
+/*
+ * Open a stream on /dev/udp, pop off all undesired modules (note that
+ * the user may have configured autopush to add modules above or below
+ * udp), and push the arp module onto raw IP.
+ */
+static int
+open_arp_on_udp(char *udp_dev_name)
+{
+ int fd;
+ boolean_t popped;
+
+ if ((fd = open(udp_dev_name, O_RDWR)) == -1) {
+ Perror2("open", udp_dev_name);
+ return (-1);
+ }
+ errno = 0;
+ popped = _B_FALSE;
+ while (ioctl(fd, I_POP, 0) != -1)
+ popped = _B_TRUE;
+ if (!popped) {
+ Perror2("cannot pop", udp_dev_name);
+ } else if (errno != EINVAL) {
+ Perror2("pop", udp_dev_name);
+ } else if (ioctl(fd, I_PUSH, ARP_MOD_NAME) == -1) {
+ Perror2("arp PUSH", udp_dev_name);
+ } else {
+ return (fd);
+ }
+ (void) close(fd);
+ return (-1);
+}
+
+/*
+ * Helper function for mod*() functions. It gets a fd to the lower IP
+ * stream and I_PUNLINK's the lower stream. It also initializes the
+ * global variable lifr.
+ *
+ * Param:
+ * int *udp_fd: (referenced) fd to /dev/udp (upper IP stream).
+ * int *fd: (referenced) fd to the lower IP stream.
+ *
+ * Return:
+ * -1 if operation fails, 0 otherwise.
+ *
+ * Please see the big block comment above plumb_one_device()
+ * for the logic of the PLINK/PUNLINK
+ */
+static int
+ip_domux2fd(int *muxfd, int *ipfd_lowstr, int *arpfd_lowstr, int *orig_arpid)
+{
+ int ip_fd;
+ uint64_t flags;
+ char *udp_dev_name;
+ char *ip_dev_name;
+
+ *orig_arpid = 0;
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+ if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0) {
+ Perror0_exit("status: SIOCGLIFFLAGS");
+ }
+ flags = lifr.lifr_flags;
+ if (flags & IFF_IPV4) {
+ udp_dev_name = UDP_DEV_NAME;
+ ip_dev_name = IP_DEV_NAME;
+ } else if (flags & IFF_IPV6) {
+ udp_dev_name = UDP6_DEV_NAME;
+ ip_dev_name = IP6_DEV_NAME;
+ } else {
+ return (-1);
+ }
+
+ if ((ip_fd = open(ip_dev_name, O_RDWR)) < 0) {
+ Perror2("open", ip_dev_name);
+ return (-1);
+ }
+ if (ioctl(ip_fd, SIOCGLIFMUXID, (caddr_t)&lifr) < 0) {
+ Perror2("SIOCGLIFMUXID", ip_dev_name);
+ return (-1);
+ }
+ if (debug > 0) {
+ (void) printf("ARP_muxid %d IP_muxid %d\n",
+ lifr.lifr_arp_muxid, lifr.lifr_ip_muxid);
+ }
+
+ if ((*muxfd = open_arp_on_udp(udp_dev_name)) == -1)
+ return (-1);
+
+ if (lifr.lifr_arp_muxid != 0) {
+ if ((*arpfd_lowstr = ioctl(*muxfd, _I_MUXID2FD,
+ lifr.lifr_arp_muxid)) < 0) {
+ if ((errno == EINVAL) &&
+ (flags & (IFF_NOARP | IFF_IPV6))) {
+ /*
+ * Some plumbing utilities set the muxid to
+ * -1 or some invalid value to signify that
+ * there is no arp stream. Set the muxid to 0
+ * before trying to unplumb the IP stream.
+ * IP does not allow the IP stream to be
+ * unplumbed if it sees a non-null arp muxid,
+ * for consistency of IP-ARP streams.
+ */
+ *orig_arpid = lifr.lifr_arp_muxid;
+ lifr.lifr_arp_muxid = 0;
+ (void) ioctl(*muxfd, SIOCSLIFMUXID,
+ (caddr_t)&lifr);
+ *arpfd_lowstr = -1;
+ } else {
+ Perror0("_I_MUXID2FD");
+ return (-1);
+ }
+ } else if (ioctl(*muxfd, I_PUNLINK,
+ lifr.lifr_arp_muxid) < 0) {
+ Perror2("I_PUNLINK", udp_dev_name);
+ return (-1);
+ }
+ } else {
+ *arpfd_lowstr = -1;
+ }
+
+ if ((*ipfd_lowstr = ioctl(*muxfd, _I_MUXID2FD,
+ lifr.lifr_ip_muxid)) < 0) {
+ Perror0("_I_MUXID2FD");
+ /* Undo any changes we made */
+ if (*orig_arpid != 0) {
+ lifr.lifr_arp_muxid = *orig_arpid;
+ (void) ioctl(*muxfd, SIOCSLIFMUXID, (caddr_t)&lifr);
+ }
+ return (-1);
+ }
+ if (ioctl(*muxfd, I_PUNLINK, lifr.lifr_ip_muxid) < 0) {
+ Perror2("I_PUNLINK", udp_dev_name);
+ /* Undo any changes we made */
+ if (*orig_arpid != 0) {
+ lifr.lifr_arp_muxid = *orig_arpid;
+ (void) ioctl(*muxfd, SIOCSLIFMUXID, (caddr_t)&lifr);
+ }
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * Helper function for mod*() functions. It I_PLINK's back the upper and
+ * lower IP streams. Note that this function must be called after
+ * ip_domux2fd(). In ip_domux2fd(), the global variable lifr is initialized
+ * and ip_plink() needs information in lifr. So ip_domux2fd() and ip_plink()
+ * must be called in pairs.
+ *
+ * Param:
+ * int udp_fd: fd to /dev/udp (upper IP stream).
+ * int fd: fd to the lower IP stream.
+ *
+ * Return:
+ * -1 if operation fails, 0 otherwise.
+ *
+ * Please see the big block comment above plumb_one_device()
+ * for the logic of the PLINK/PUNLINK
+ */
+static int
+ip_plink(int muxfd, int ipfd_lowstr, int arpfd_lowstr, int orig_arpid)
+{
+ int ip_muxid;
+
+ ip_muxid = ioctl(muxfd, I_PLINK, ipfd_lowstr);
+ if (ip_muxid < 0) {
+ Perror2("I_PLINK", UDP_DEV_NAME);
+ return (-1);
+ }
+
+ /*
+ * If there is an arp stream, plink it. If there is no
+ * arp stream, then it is possible that the plumbing
+ * utility could have stored any value in the arp_muxid.
+ * If so, restore it from orig_arpid.
+ */
+ if (arpfd_lowstr != -1) {
+ if (ioctl(muxfd, I_PLINK, arpfd_lowstr) < 0) {
+ Perror2("I_PLINK", UDP_DEV_NAME);
+ return (-1);
+ }
+ } else if (orig_arpid != 0) {
+ /* Undo the changes we did in ip_domux2fd */
+ lifr.lifr_arp_muxid = orig_arpid;
+ lifr.lifr_ip_muxid = ip_muxid;
+ (void) ioctl(muxfd, SIOCSLIFMUXID, (caddr_t)&lifr);
+ }
+
+ return (0);
+}
+
+
+/*
+ * The names of other core TCP/IP stack modules which cannot be removed.
+ */
+#ifndef TUN_NAME
+#define TUN_NAME "tun"
+#endif
+
+#ifndef ATUN_NAME
+#define ATUN_NAME "atun"
+#endif
+
+#ifndef TUN6TO4_NAME
+#define TUN6TO4_NAME "6to4tun"
+#endif
+
+/*
+ * The real function to perform module insertion/removal.
+ *
+ * Param:
+ * char *arg: the argument string module_name@position
+ * char op: operation, either MODINSERT_OP or MODREMOVE_OP.
+ *
+ * Return:
+ * Before doing ip_domux2fd(), this function calls exit(1) in case of
+ * error. After ip_domux2fd() is done, it returns -1 for error, 0
+ * otherwise.
+ */
+static int
+modop(char *arg, char op)
+{
+ char *pos_p;
+ int muxfd;
+ int ipfd_lowstr; /* IP stream (lower stream of mux) to be plinked */
+ int arpfd_lowstr; /* ARP stream (lower stream of mux) to be plinked */
+ struct strmodconf mod;
+ char *at_char = "@";
+ char *arg_str;
+ int orig_arpid;
+
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+
+ /* Need to save the original string for -a option. */
+ if ((arg_str = malloc(strlen(arg) + 1)) == NULL) {
+ Perror0("cannot malloc");
+ return (-1);
+ }
+ (void) strcpy(arg_str, arg);
+
+ if (*arg_str == *at_char) {
+ (void) fprintf(stderr,
+ "ifconfig: must supply a module name\n");
+ exit(1);
+ }
+ mod.mod_name = strtok(arg_str, at_char);
+ if (strlen(mod.mod_name) > FMNAMESZ) {
+ (void) fprintf(stderr, "ifconfig: module name too long: %s\n",
+ mod.mod_name);
+ exit(1);
+ }
+
+ /*
+ * Need to make sure that the core TCP/IP stack modules are not
+ * removed. Otherwise, "bad" things can happen. If a module
+ * is removed and inserted back, it loses its old state. But
+ * the modules above it still have the old state. E.g. IP assumes
+ * fast data path while tunnel after re-inserted assumes that it can
+ * receive M_DATA only in fast data path for which it does not have
+ * any state. This is a general caveat of _I_REMOVE/_I_INSERT.
+ */
+ if (op == MODREMOVE_OP &&
+ (strcmp(mod.mod_name, ARP_MOD_NAME) == 0 ||
+ strcmp(mod.mod_name, IP_MOD_NAME) == 0 ||
+ strcmp(mod.mod_name, TUN_NAME) == 0 ||
+ strcmp(mod.mod_name, ATUN_NAME) == 0 ||
+ strcmp(mod.mod_name, TUN6TO4_NAME) == 0)) {
+ (void) fprintf(stderr, "ifconfig: cannot remove %s\n",
+ mod.mod_name);
+ exit(1);
+ }
+
+ if ((pos_p = strtok(NULL, at_char)) == NULL) {
+ (void) fprintf(stderr, "ifconfig: must supply a position\n");
+ exit(1);
+ }
+ mod.pos = atoi(pos_p);
+
+ if (ip_domux2fd(&muxfd, &ipfd_lowstr, &arpfd_lowstr,
+ &orig_arpid) < 0) {
+ free(arg_str);
+ return (-1);
+ }
+ switch (op) {
+ case MODINSERT_OP:
+ if (debug > 0) {
+ (void) printf("Inserting module %s at %d\n",
+ mod.mod_name, mod.pos);
+ }
+ if (ioctl(ipfd_lowstr, _I_INSERT, (caddr_t)&mod) < 0) {
+ Perror2("fail to insert module", mod.mod_name);
+ }
+ break;
+ case MODREMOVE_OP:
+ if (debug > 0) {
+ (void) printf("Removing module %s at %d\n",
+ mod.mod_name, mod.pos);
+ }
+ if (ioctl(ipfd_lowstr, _I_REMOVE, (caddr_t)&mod) < 0) {
+ Perror2("fail to remove module", mod.mod_name);
+ }
+ break;
+ default:
+ /* Should never get to here. */
+ (void) fprintf(stderr, "Unknown operation\n");
+ break;
+ }
+ free(arg_str);
+ return (ip_plink(muxfd, ipfd_lowstr, arpfd_lowstr, orig_arpid));
+}
+
+#ifdef DEBUG
+
+/* ARGSUSED */
+static int
+getnd(char *addr, int64_t param)
+{
+ struct sockaddr_in6 v6addr;
+ char *str = NULL;
+
+ in6_getaddr(addr, &v6addr, NULL);
+ (void) memcpy(&lifr.lifr_nd.lnr_addr, &v6addr, sizeof (v6addr));
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+ if (ioctl(s, SIOCLIFGETND, (caddr_t)&lifr) < 0) {
+ Perror0_exit("SIOCLIFGETND");
+ }
+ str = _link_ntoa((const unsigned char *)lifr.lifr_nd.lnr_hdw_addr,
+ str, lifr.lifr_nd.lnr_hdw_len, IFT_OTHER);
+ if (str != NULL) {
+ (void) printf("address %s", str);
+ free(str);
+ }
+ (void) printf(" state %d/%d/%d flags %x\n",
+ lifr.lifr_nd.lnr_state_create,
+ lifr.lifr_nd.lnr_state_same_lla,
+ lifr.lifr_nd.lnr_state_diff_lla,
+ lifr.lifr_nd.lnr_flags);
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+delnd(char *addr, int64_t param)
+{
+ struct sockaddr_in6 v6addr;
+
+ in6_getaddr(addr, &v6addr, NULL);
+ (void) memcpy(&lifr.lifr_nd.lnr_addr, &v6addr, sizeof (v6addr));
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+
+ lifr.lifr_nd.lnr_state_create = ND_UNCHANGED;
+ lifr.lifr_nd.lnr_state_same_lla = ND_UNCHANGED;
+ lifr.lifr_nd.lnr_state_diff_lla = ND_UNCHANGED;
+ lifr.lifr_nd.lnr_flags = 0;
+ if (ioctl(s, SIOCLIFDELND, (caddr_t)&lifr) < 0) {
+ Perror0_exit("SIOCLIFDELND");
+ }
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+setnd(char *addr, int64_t param)
+{
+ struct sockaddr_in6 v6addr;
+
+ /*
+ * XXX parse phyaddr? Need syntax to fit in one argv.
+ * XXX <proto addr>/<phyaddr> ??
+ */
+ in6_getaddr(addr, &v6addr, NULL);
+ (void) memcpy(&lifr.lifr_nd.lnr_addr, &v6addr, sizeof (v6addr));
+ (void) memset(lifr.lifr_nd.lnr_hdw_addr, 0x55,
+ sizeof (lifr.lifr_nd.lnr_hdw_addr));
+ lifr.lifr_nd.lnr_hdw_len = 6;
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+
+ lifr.lifr_nd.lnr_state_create = ND_STALE;
+ lifr.lifr_nd.lnr_state_same_lla = ND_UNCHANGED;
+ lifr.lifr_nd.lnr_state_diff_lla = ND_STALE;
+ lifr.lifr_nd.lnr_flags = 0;
+
+ if (ioctl(s, SIOCLIFSETND, (caddr_t)&lifr) < 0) {
+ Perror0_exit("SIOCLIFSETND");
+ }
+ return (0);
+}
+#endif /* DEBUG */
+
+/*
+ * Set tunnel source address
+ */
+/* ARGSUSED */
+static int
+setiftsrc(char *addr, int64_t param)
+{
+ return (settaddr(addr, icfg_set_tunnel_src));
+}
+
+/*
+ * Set tunnel destination address
+ */
+/* ARGSUSED */
+static int
+setiftdst(char *addr, int64_t param)
+{
+ return (settaddr(addr, icfg_set_tunnel_dest));
+}
+
+/*
+ * sets tunnels src|dst address. settaddr() expects the following:
+ * addr: Points to a printable string containing the address to be
+ * set, e.g. 129.153.128.110.
+ * fn: Pointer to a libinetcfg routine that will do the actual work.
+ * The only valid functions are icfg_set_tunnel_src and
+ * icfg_set_tunnel_dest.
+ */
+static int
+settaddr(char *addr,
+ int (*fn)(icfg_handle_t, const struct sockaddr *, socklen_t))
+{
+ icfg_handle_t handle;
+ icfg_if_t interface;
+ struct sockaddr_storage laddr;
+ int lower;
+ int rc;
+
+ if (strchr(name, ':') != NULL) {
+ errno = EPERM;
+ Perror0_exit("Tunnel params on logical interfaces");
+ }
+ (void) strncpy(interface.if_name, name, sizeof (interface.if_name));
+ interface.if_protocol = SOCKET_AF(af);
+
+ /* Open interface. */
+ if ((rc = icfg_open(&handle, &interface)) != ICFG_SUCCESS)
+ Perror0_exit((char *)icfg_errmsg(rc));
+
+ rc = icfg_get_tunnel_lower(handle, &lower);
+ if (rc != ICFG_SUCCESS)
+ Perror0_exit((char *)icfg_errmsg(rc));
+
+ if (lower == AF_INET) {
+ in_getaddr(addr, (struct sockaddr *)&laddr, NULL);
+ } else {
+ in6_getaddr(addr, (struct sockaddr *)&laddr, NULL);
+ }
+
+ /* Call fn to do the real work, and close the interface. */
+ rc = (*fn)(handle, (struct sockaddr *)&laddr,
+ sizeof (struct sockaddr_storage));
+ icfg_close(handle);
+
+ if (rc != ICFG_SUCCESS)
+ Perror0_exit((char *)icfg_errmsg(rc));
+
+ return (0);
+}
+
+/* Set tunnel encapsulation limit. */
+/* ARGSUSED */
+static int
+set_tun_encap_limit(char *arg, int64_t param)
+{
+ short limit;
+ icfg_if_t interface;
+ icfg_handle_t handle;
+ int rc;
+
+ if (strchr(name, ':') != NULL) {
+ errno = EPERM;
+ Perror0_exit("Tunnel params on logical interfaces");
+ }
+
+ if ((sscanf(arg, "%hd", &limit) != 1) || (limit < 0) ||
+ (limit > 255)) {
+ errno = EINVAL;
+ Perror0_exit("Invalid encapsulation limit");
+ }
+
+ /* Open interface for configuration. */
+ (void) strncpy(interface.if_name, name, sizeof (interface.if_name));
+ interface.if_protocol = SOCKET_AF(af);
+ if (icfg_open(&handle, &interface) != ICFG_SUCCESS)
+ Perror0_exit("couldn't open interface");
+
+ rc = icfg_set_tunnel_encaplimit(handle, (int)limit);
+ icfg_close(handle);
+
+ if (rc != ICFG_SUCCESS)
+ Perror0_exit("Could not configure tunnel encapsulation limit");
+
+ return (0);
+}
+
+/* Disable encapsulation limit. */
+/* ARGSUSED */
+static int
+clr_tun_encap_limit(char *arg, int64_t param)
+{
+ icfg_if_t interface;
+ icfg_handle_t handle;
+ int rc;
+
+ if (strchr(name, ':') != NULL) {
+ errno = EPERM;
+ Perror0_exit("Tunnel params on logical interfaces");
+ }
+
+ /* Open interface for configuration. */
+ (void) strncpy(interface.if_name, name, sizeof (interface.if_name));
+ interface.if_protocol = SOCKET_AF(af);
+ if (icfg_open(&handle, &interface) != ICFG_SUCCESS)
+ Perror0_exit("couldn't open interface");
+
+ rc = icfg_set_tunnel_encaplimit(handle, -1);
+ icfg_close(handle);
+
+ if (rc != ICFG_SUCCESS)
+ Perror0_exit((char *)icfg_errmsg(rc));
+
+ return (0);
+}
+
+/* Set tunnel hop limit. */
+/* ARGSUSED */
+static int
+set_tun_hop_limit(char *arg, int64_t param)
+{
+ unsigned short limit;
+ icfg_if_t interface;
+ icfg_handle_t handle;
+ int rc;
+
+ if (strchr(name, ':') != NULL) {
+ errno = EPERM;
+ Perror0_exit("Tunnel params on logical interfaces");
+ }
+
+ /*
+ * Check limit here since it's really only an 8-bit unsigned quantity.
+ */
+ if ((sscanf(arg, "%hu", &limit) != 1) || (limit > 255)) {
+ errno = EINVAL;
+ Perror0_exit("Invalid hop limit");
+ }
+
+ /* Open interface for configuration. */
+ (void) strncpy(interface.if_name, name, sizeof (interface.if_name));
+ interface.if_protocol = SOCKET_AF(af);
+ if (icfg_open(&handle, &interface) != ICFG_SUCCESS)
+ Perror0_exit("couldn't open interface");
+
+ rc = icfg_set_tunnel_hoplimit(handle, (uint8_t)limit);
+ icfg_close(handle);
+
+ if (rc != ICFG_SUCCESS)
+ Perror0_exit("Could not configure tunnel hop limit");
+
+ return (0);
+}
+
+/* Set zone ID */
+static int
+setzone(char *arg, int64_t param)
+{
+ zoneid_t zoneid = GLOBAL_ZONEID;
+
+ if (param == NEXTARG) {
+ /* zone must be active */
+ if ((zoneid = getzoneidbyname(arg)) == -1) {
+ (void) fprintf(stderr,
+ "ifconfig: unknown zone '%s'\n", arg);
+ exit(1);
+ }
+ }
+ (void) strlcpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+ lifr.lifr_zoneid = zoneid;
+ if (ioctl(s, SIOCSLIFZONE, (caddr_t)&lifr) == -1)
+ Perror0_exit("SIOCSLIFZONE");
+ return (0);
+}
+
+/* Set source address to use */
+/* ARGSUSED */
+static int
+setifsrc(char *arg, int64_t param)
+{
+ uint_t ifindex = 0;
+ int rval;
+
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+
+ /*
+ * Argument can be either an interface name or "none". The latter means
+ * that any previous selection is cleared.
+ */
+
+ rval = strcmp(arg, name);
+ if (rval == 0) {
+ (void) fprintf(stderr,
+ "ifconfig: Cannot specify same interface for usesrc"
+ " group\n");
+ exit(1);
+ }
+
+ rval = strcmp(arg, NONE_STR);
+ if (rval != 0) {
+ if ((ifindex = if_nametoindex(arg)) == 0) {
+ (void) strncpy(lifr.lifr_name, arg, LIFNAMSIZ);
+ Perror0_exit("Could not get interface index");
+ }
+ lifr.lifr_index = ifindex;
+ } else {
+ if (ioctl(s, SIOCGLIFUSESRC, (caddr_t)&lifr) != 0)
+ Perror0_exit("Not a valid usesrc consumer");
+ lifr.lifr_index = 0;
+ }
+
+ if (debug)
+ (void) printf("setifsrc: lifr_name %s, lifr_index %d\n",
+ lifr.lifr_name, lifr.lifr_index);
+
+ if (ioctl(s, SIOCSLIFUSESRC, (caddr_t)&lifr) == -1) {
+ if (rval == 0)
+ Perror0_exit("Cannot reset usesrc group");
+ else
+ Perror0_exit("Could not set source interface");
+ }
+
+ return (0);
+}
+
+/*
+ * Print the interface status line associated with `ifname'
+ */
+static void
+ifstatus(const char *ifname)
+{
+ uint64_t flags;
+ char if_usesrc_name[LIFNAMSIZ];
+ char *newbuf;
+ int n, numifs, rval = 0;
+ struct lifreq *lifrp;
+ struct lifsrcof lifs;
+
+ (void) strncpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
+ if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0) {
+ Perror0_exit("status: SIOCGLIFFLAGS");
+ }
+ flags = lifr.lifr_flags;
+
+ /*
+ * In V4 compatibility mode, we don't print the IFF_IPV4 flag or
+ * interfaces with IFF_IPV6 set.
+ */
+ if (v4compat) {
+ flags &= ~IFF_IPV4;
+ if (flags & IFF_IPV6)
+ return;
+ }
+
+ (void) printf("%s: ", ifname);
+ print_flags(flags);
+
+ (void) strncpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
+ if (ioctl(s, SIOCGLIFMETRIC, (caddr_t)&lifr) < 0) {
+ Perror0_exit("status: SIOCGLIFMETRIC");
+ } else {
+ if (lifr.lifr_metric)
+ (void) printf(" metric %d", lifr.lifr_metric);
+ }
+ if (ioctl(s, SIOCGLIFMTU, (caddr_t)&lifr) >= 0)
+ (void) printf(" mtu %d", lifr.lifr_metric);
+
+ /* don't print index or zone when in compatibility mode */
+ if (!v4compat) {
+ if (ioctl(s, SIOCGLIFINDEX, (caddr_t)&lifr) >= 0)
+ (void) printf(" index %d", lifr.lifr_index);
+ if (ioctl(s, SIOCGLIFZONE, (caddr_t)&lifr) >= 0 &&
+ lifr.lifr_zoneid != getzoneid()) {
+ char zone_name[ZONENAME_MAX];
+
+ if (getzonenamebyid(lifr.lifr_zoneid, zone_name,
+ sizeof (zone_name)) < 0) {
+ (void) printf("\n\tzone %d", lifr.lifr_zoneid);
+ } else {
+ (void) printf("\n\tzone %s", zone_name);
+ }
+ }
+ }
+
+ if (ioctl(s, SIOCGLIFINDEX, (caddr_t)&lifr) >= 0) {
+ lifs.lifs_ifindex = lifr.lifr_index;
+
+ /*
+ * Find the number of interfaces that use this interfaces'
+ * address as a source address
+ */
+ lifs.lifs_buf = NULL;
+ lifs.lifs_maxlen = 0;
+ for (;;) {
+ /* The first pass will give the bufsize we need */
+ rval = ioctl(s, SIOCGLIFSRCOF, (char *)&lifs);
+ if (rval < 0) {
+ if (lifs.lifs_buf != NULL) {
+ free(lifs.lifs_buf);
+ lifs.lifs_buf = NULL;
+ }
+ lifs.lifs_len = 0;
+ break;
+ }
+ if (lifs.lifs_len <= lifs.lifs_maxlen)
+ break;
+ /* Use kernel's size + a small margin to avoid loops */
+ lifs.lifs_maxlen = lifs.lifs_len +
+ 5 * sizeof (struct lifreq);
+ /* For the first pass, realloc acts like malloc */
+ newbuf = realloc(lifs.lifs_buf, lifs.lifs_maxlen);
+ if (newbuf == NULL) {
+ if (lifs.lifs_buf != NULL) {
+ free(lifs.lifs_buf);
+ lifs.lifs_buf = NULL;
+ }
+ lifs.lifs_len = 0;
+ break;
+ }
+ lifs.lifs_buf = newbuf;
+ }
+
+
+ numifs = lifs.lifs_len / sizeof (struct lifreq);
+ if (numifs > 0) {
+ lifrp = lifs.lifs_req;
+ (void) printf("\n\tsrcof");
+ for (n = numifs; n > 0; n--, lifrp++) {
+ (void) printf(" %s", lifrp->lifr_name);
+ }
+ }
+
+ if (lifs.lifs_buf != NULL)
+ free(lifs.lifs_buf);
+ }
+
+ /* Find the interface whose source address this interface uses */
+ if (ioctl(s, SIOCGLIFUSESRC, (caddr_t)&lifr) == 0) {
+ if (lifr.lifr_index != 0) {
+ if (if_indextoname(lifr.lifr_index,
+ if_usesrc_name) == NULL) {
+ (void) printf("\n\tusesrc ifIndex %d",
+ lifr.lifr_index);
+ } else {
+ (void) printf("\n\tusesrc %s", if_usesrc_name);
+ }
+ }
+ }
+
+ (void) putchar('\n');
+}
+
+
+/*
+ * Print the status of the interface. If an address family was
+ * specified, show it and it only; otherwise, show them all.
+ */
+static void
+status(void)
+{
+ struct afswtch *p = afp;
+ uint64_t flags;
+
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+ if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0) {
+ Perror0_exit("status: SIOCGLIFFLAGS");
+ }
+
+ flags = lifr.lifr_flags;
+
+ /*
+ * Only print the interface status if the address family matches
+ * the interface family flag.
+ */
+ if (p != NULL) {
+ if (((p->af_af == AF_INET6) && (flags & IFF_IPV4)) ||
+ ((p->af_af == AF_INET) && (flags & IFF_IPV6)))
+ return;
+ }
+
+ /*
+ * In V4 compatibility mode, don't print IFF_IPV6 interfaces.
+ */
+ if (v4compat && (flags & IFF_IPV6))
+ return;
+
+ ifstatus(name);
+
+ if (p != NULL) {
+ (*p->af_status)(1, flags);
+ } else {
+ for (p = afs; p->af_name; p++) {
+ (void) close(s);
+ s = socket(SOCKET_AF(p->af_af), SOCK_DGRAM, 0);
+ /* set global af for use in p->af_status */
+ af = p->af_af;
+ if (s == -1) {
+ Perror0_exit("socket");
+ }
+ (*p->af_status)(0, flags);
+ }
+
+ /*
+ * Historically, 'ether' has been an address family,
+ * so print it here.
+ */
+ print_ifether(name);
+ }
+}
+
+/*
+ * Print the status of the interface in a format that can be used to
+ * reconfigure the interface later. Code stolen from status() above.
+ */
+/* ARGSUSED */
+static int
+configinfo(char *null, int64_t param)
+{
+ struct afswtch *p = afp;
+ uint64_t flags;
+ char phydevname[LIFNAMSIZ];
+ char if_usesrc_name[LIFNAMSIZ];
+ char *cp;
+
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+ if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0) {
+ Perror0_exit("status: SIOCGLIFFLAGS");
+ }
+ flags = lifr.lifr_flags;
+
+ if (debug) {
+ (void) printf("configinfo: name %s flags 0x%llx af_af %d\n",
+ name, flags, p != NULL ? p->af_af : -1);
+ }
+
+ /* remove LIF component */
+ (void) strncpy(phydevname, name, sizeof (phydevname));
+ cp = strchr(phydevname, ':');
+ if (cp) {
+ *cp = 0;
+ }
+ phydevname[sizeof (phydevname) - 1] = '\0';
+
+ /*
+ * if the interface is IPv4
+ * if we have a IPv6 address family restriction return
+ * so it won't print
+ * if we are in IPv4 compatibility mode, clear out IFF_IPV4
+ * so we don't print it.
+ */
+ if (flags & IFF_IPV4) {
+ if (p && p->af_af == AF_INET6)
+ return (-1);
+ if (v4compat)
+ flags &= ~IFF_IPV4;
+
+ (void) printf("%s inet plumb", phydevname);
+ } else if (flags & IFF_IPV6) {
+ /*
+ * else if the interface is IPv6
+ * if we have a IPv4 address family restriction return
+ * or we are in IPv4 compatibiltiy mode, return.
+ */
+ if (p && p->af_af == AF_INET)
+ return (-1);
+ if (v4compat)
+ return (-1);
+
+ (void) printf("%s inet6 plumb", phydevname);
+ }
+
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+ if (ioctl(s, SIOCGLIFMETRIC, (caddr_t)&lifr) < 0) {
+ Perror0_exit("configinfo: SIOCGLIFMETRIC");
+ } else {
+ if (lifr.lifr_metric)
+ (void) printf(" metric %d ", lifr.lifr_metric);
+ }
+ if (((flags & (IFF_VIRTUAL|IFF_LOOPBACK)) != IFF_VIRTUAL) &&
+ ioctl(s, SIOCGLIFMTU, (caddr_t)&lifr) >= 0)
+ (void) printf(" mtu %d", lifr.lifr_metric);
+
+ /* don't print index when in compatibility mode */
+ if (!v4compat) {
+ if (ioctl(s, SIOCGLIFINDEX, (caddr_t)&lifr) >= 0)
+ (void) printf(" index %d", lifr.lifr_index);
+ }
+
+ if (ioctl(s, SIOCGLIFUSESRC, (caddr_t)&lifr) == 0) {
+ if (lifr.lifr_index != 0) {
+ if (if_indextoname(lifr.lifr_index,
+ if_usesrc_name) != NULL) {
+ (void) printf(" usesrc %s", if_usesrc_name);
+ }
+ }
+ }
+
+ if (p != NULL) {
+ (*p->af_configinfo)(1, flags);
+ } else {
+ for (p = afs; p->af_name; p++) {
+ (void) close(s);
+ s = socket(SOCKET_AF(p->af_af), SOCK_DGRAM, 0);
+ /* set global af for use in p->af_configinfo */
+ af = p->af_af;
+ if (s == -1) {
+ Perror0_exit("socket");
+ }
+ (*p->af_configinfo)(0, flags);
+ }
+ }
+
+ (void) printf("\n");
+
+ return (0);
+}
+
+static void
+print_tsec(struct iftun_req *tparams)
+{
+ ipsec_req_t *ipsr;
+
+ (void) printf("\ttunnel security settings ");
+ /*
+ * Deal with versioning, for now just point
+ * an ipsec_req_t at ifta_secinfo. If versions
+ * change, something else will overlay ifta_secinfo.
+ */
+ assert(tparams->ifta_vers == IFTUN_VERSION);
+
+ ipsr = (ipsec_req_t *)(&tparams->ifta_secinfo);
+ if (ipsr->ipsr_ah_req & IPSEC_PREF_REQUIRED) {
+ (void) printf("ah (%s) ",
+ rparsealg(ipsr->ipsr_auth_alg, IPSEC_PROTO_AH));
+ }
+ if (ipsr->ipsr_esp_req & IPSEC_PREF_REQUIRED) {
+ (void) printf("esp (%s",
+ rparsealg(ipsr->ipsr_esp_alg, IPSEC_PROTO_ESP));
+ (void) printf("/%s)",
+ rparsealg(ipsr->ipsr_esp_auth_alg, IPSEC_PROTO_AH));
+ }
+ (void) printf("\n");
+}
+
+static void
+tun_status(void)
+{
+ icfg_if_t interface;
+ int rc;
+ icfg_handle_t handle;
+ int protocol;
+ char srcbuf[INET6_ADDRSTRLEN];
+ char dstbuf[INET6_ADDRSTRLEN];
+ boolean_t tabbed;
+ uint8_t hoplimit;
+ int16_t encaplimit;
+ struct sockaddr_storage taddr;
+ socklen_t socklen = sizeof (taddr);
+
+ (void) strncpy(interface.if_name, name, sizeof (interface.if_name));
+ interface.if_protocol = SOCKET_AF(af);
+ if ((rc = icfg_open(&handle, &interface)) != ICFG_SUCCESS)
+ Perror0_exit((char *)icfg_errmsg(rc));
+
+ /*
+ * only print tunnel info for lun 0. If ioctl fails, assume
+ * we are not a tunnel
+ */
+ if (strchr(name, ':') != NULL ||
+ icfg_get_tunnel_lower(handle, &protocol) != ICFG_SUCCESS) {
+ icfg_close(handle);
+ return;
+ }
+
+ switch (protocol) {
+ case AF_INET:
+ (void) printf("\tinet");
+ break;
+ case AF_INET6:
+ (void) printf("\tinet6");
+ break;
+ default:
+ Perror0_exit("\ttunnel: Illegal lower stream\n\t");
+ break;
+ }
+
+ rc = icfg_get_tunnel_src(handle, (struct sockaddr *)&taddr, &socklen);
+ if (rc == ICFG_NOT_SET) {
+ (void) strlcpy(srcbuf, (protocol == AF_INET) ? "0.0.0.0" :
+ "::", sizeof (srcbuf));
+ } else if (rc != ICFG_SUCCESS) {
+ Perror0_exit((char *)icfg_errmsg(rc));
+ } else {
+ rc = icfg_sockaddr_to_str(protocol, (struct sockaddr *)&taddr,
+ srcbuf, sizeof (srcbuf));
+ if (rc != ICFG_SUCCESS) {
+ Perror0_exit((char *)icfg_errmsg(rc));
+ }
+ }
+
+ (void) printf(" tunnel src %s ", srcbuf);
+
+ rc = icfg_get_tunnel_dest(handle, (struct sockaddr *)&taddr, &socklen);
+ if (rc == ICFG_NOT_SET) {
+ (void) printf("\n");
+ } else {
+ rc = icfg_sockaddr_to_str(protocol, (struct sockaddr *)&taddr,
+ dstbuf, sizeof (dstbuf));
+ if (rc != ICFG_SUCCESS) {
+ Perror0_exit((char *)icfg_errmsg(rc));
+ }
+ (void) printf("tunnel dst %s\n", dstbuf);
+ }
+
+ if (handle->ifh_tunnel_params != NULL &&
+ (handle->ifh_tunnel_params->ifta_flags & IFTUN_SECURITY))
+ print_tsec(handle->ifh_tunnel_params);
+
+ /*
+ * tabbed indicates tabbed and printed. Use it tell us whether
+ * to tab and that we've printed something here, so we need a
+ * newline
+ */
+ tabbed = _B_FALSE;
+
+ if (icfg_get_tunnel_hoplimit(handle, &hoplimit) == ICFG_SUCCESS) {
+ (void) printf("\ttunnel hop limit %d ", hoplimit);
+ tabbed = _B_TRUE;
+ }
+
+ if ((protocol == AF_INET6) &&
+ (icfg_get_tunnel_encaplimit(handle, &encaplimit) ==
+ ICFG_SUCCESS)) {
+ if (!tabbed) {
+ (void) printf("\t");
+ tabbed = _B_TRUE;
+ }
+ if (encaplimit >= 0) {
+ (void) printf("tunnel encapsulation limit %d",
+ encaplimit);
+ } else {
+ (void) printf("tunnel encapsulation limit disabled");
+ }
+ }
+
+ if (tabbed)
+ (void) printf("\n");
+
+ icfg_close(handle);
+}
+
+static void
+in_status(int force, uint64_t flags)
+{
+ struct sockaddr_in *sin, *laddr;
+ struct sockaddr_in netmask = { AF_INET };
+
+ if (debug)
+ (void) printf("in_status(%s) flags 0x%llx\n", name, flags);
+
+ /* only print status for IPv4 interfaces */
+ if (!(flags & IFF_IPV4))
+ return;
+
+ /* if the interface is a tunnel, print the tunnel status */
+ tun_status();
+
+ if (!(flags & IFF_NOLOCAL)) {
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+ if (ioctl(s, SIOCGLIFADDR, (caddr_t)&lifr) < 0) {
+ if (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT ||
+ errno == ENXIO) {
+ if (!force)
+ return;
+ (void) memset(&lifr.lifr_addr, 0,
+ sizeof (lifr.lifr_addr));
+ } else
+ Perror0_exit("in_status: SIOCGLIFADDR");
+ }
+ sin = (struct sockaddr_in *)&lifr.lifr_addr;
+ (void) printf("\tinet %s ", inet_ntoa(sin->sin_addr));
+ laddr = sin;
+ } else {
+ (void) printf("\tinet ");
+ }
+
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+ if (ioctl(s, SIOCGLIFSUBNET, (caddr_t)&lifr) < 0) {
+ if (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT ||
+ errno == ENXIO) {
+ if (!force)
+ return;
+ (void) memset(&lifr.lifr_addr, 0,
+ sizeof (lifr.lifr_addr));
+ } else {
+ Perror0_exit("in_status: SIOCGLIFSUBNET");
+ }
+ }
+ sin = (struct sockaddr_in *)&lifr.lifr_addr;
+ if ((flags & IFF_NOLOCAL) ||
+ sin->sin_addr.s_addr != laddr->sin_addr.s_addr) {
+ (void) printf("subnet %s/%d ", inet_ntoa(sin->sin_addr),
+ lifr.lifr_addrlen);
+ }
+ if (sin->sin_family != AF_INET) {
+ (void) printf("Wrong family: %d\n", sin->sin_family);
+ }
+
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+ if (ioctl(s, SIOCGLIFNETMASK, (caddr_t)&lifr) < 0) {
+ if (errno != EADDRNOTAVAIL)
+ Perror0_exit("in_status: SIOCGLIFNETMASK");
+ (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr));
+ } else
+ netmask.sin_addr =
+ ((struct sockaddr_in *)&lifr.lifr_addr)->sin_addr;
+ if (flags & IFF_POINTOPOINT) {
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+ if (ioctl(s, SIOCGLIFDSTADDR, (caddr_t)&lifr) < 0) {
+ if (errno == EADDRNOTAVAIL)
+ (void) memset(&lifr.lifr_addr, 0,
+ sizeof (lifr.lifr_addr));
+ else
+ Perror0_exit("in_status: SIOCGLIFDSTADDR");
+ }
+ sin = (struct sockaddr_in *)&lifr.lifr_dstaddr;
+ (void) printf("--> %s ", inet_ntoa(sin->sin_addr));
+ }
+ (void) printf("netmask %x ", ntohl(netmask.sin_addr.s_addr));
+ if (flags & IFF_BROADCAST) {
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+ if (ioctl(s, SIOCGLIFBRDADDR, (caddr_t)&lifr) < 0) {
+ if (errno == EADDRNOTAVAIL)
+ (void) memset(&lifr.lifr_addr, 0,
+ sizeof (lifr.lifr_addr));
+ else
+ Perror0_exit("in_status: SIOCGLIFBRDADDR");
+ }
+ sin = (struct sockaddr_in *)&lifr.lifr_addr;
+ if (sin->sin_addr.s_addr != 0) {
+ (void) printf("broadcast %s",
+ inet_ntoa(sin->sin_addr));
+ }
+ }
+ /* If there is a groupname, print it for lun 0 alone */
+ if (strchr(name, ':') == NULL) {
+ (void) memset(lifr.lifr_groupname, 0,
+ sizeof (lifr.lifr_groupname));
+ if (ioctl(s, SIOCGLIFGROUPNAME, (caddr_t)&lifr) >= 0) {
+ if (strlen(lifr.lifr_groupname) > 0) {
+ (void) printf("\n\tgroupname %s",
+ lifr.lifr_groupname);
+ }
+ }
+ }
+ (void) putchar('\n');
+}
+
+static void
+in6_status(int force, uint64_t flags)
+{
+ char abuf[INET6_ADDRSTRLEN];
+ struct sockaddr_in6 *sin6, *laddr6;
+
+ if (debug)
+ (void) printf("in6_status(%s) flags 0x%llx\n", name, flags);
+
+ if (!(flags & IFF_IPV6))
+ return;
+
+ /* if the interface is a tunnel, print the tunnel status */
+ tun_status();
+
+ if (!(flags & IFF_NOLOCAL)) {
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+ if (ioctl(s, SIOCGLIFADDR, (caddr_t)&lifr) < 0) {
+ if (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT ||
+ errno == ENXIO) {
+ if (!force)
+ return;
+ (void) memset(&lifr.lifr_addr, 0,
+ sizeof (lifr.lifr_addr));
+ } else
+ Perror0_exit("in_status6: SIOCGLIFADDR");
+ }
+ sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
+ (void) printf("\tinet6 %s/%d ",
+ inet_ntop(AF_INET6, (void *)&sin6->sin6_addr,
+ abuf, sizeof (abuf)),
+ lifr.lifr_addrlen);
+ laddr6 = sin6;
+ } else {
+ (void) printf("\tinet6 ");
+ }
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+ if (ioctl(s, SIOCGLIFSUBNET, (caddr_t)&lifr) < 0) {
+ if (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT ||
+ errno == ENXIO) {
+ if (!force)
+ return;
+ (void) memset(&lifr.lifr_addr, 0,
+ sizeof (lifr.lifr_addr));
+ } else
+ Perror0_exit("in_status6: SIOCGLIFSUBNET");
+ }
+ sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
+ if ((flags & IFF_NOLOCAL) ||
+ !IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, &laddr6->sin6_addr)) {
+ (void) printf("subnet %s/%d ",
+ inet_ntop(AF_INET6, (void *)&sin6->sin6_addr,
+ abuf, sizeof (abuf)),
+ lifr.lifr_addrlen);
+ }
+ if (sin6->sin6_family != AF_INET6) {
+ (void) printf("Wrong family: %d\n", sin6->sin6_family);
+ }
+ if (flags & IFF_POINTOPOINT) {
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+ if (ioctl(s, SIOCGLIFDSTADDR, (caddr_t)&lifr) < 0) {
+ if (errno == EADDRNOTAVAIL)
+ (void) memset(&lifr.lifr_addr, 0,
+ sizeof (lifr.lifr_addr));
+ else
+ Perror0_exit("in_status6: SIOCGLIFDSTADDR");
+ }
+ sin6 = (struct sockaddr_in6 *)&lifr.lifr_dstaddr;
+ (void) printf("--> %s ",
+ inet_ntop(AF_INET6, (void *)&sin6->sin6_addr,
+ abuf, sizeof (abuf)));
+ }
+ if (verbose) {
+ (void) putchar('\n');
+ (void) putchar('\t');
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+ if (ioctl(s, SIOCGLIFTOKEN, (caddr_t)&lifr) < 0) {
+ if (errno == EADDRNOTAVAIL || errno == EINVAL)
+ (void) memset(&lifr.lifr_addr, 0,
+ sizeof (lifr.lifr_addr));
+ else
+ Perror0_exit("in_status6: SIOCGLIFTOKEN");
+ } else {
+ sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
+ (void) printf("token %s/%d ",
+ inet_ntop(AF_INET6, (void *)&sin6->sin6_addr,
+ abuf, sizeof (abuf)),
+ lifr.lifr_addrlen);
+ }
+ if (ioctl(s, SIOCGLIFLNKINFO, (caddr_t)&lifr) < 0) {
+ if (errno != EINVAL) {
+ Perror0_exit("in_status6: SIOCGLIFLNKINFO");
+ }
+ } else {
+ (void) printf("maxhops %u, reachtime %u ms, "
+ "reachretrans %u ms, maxmtu %u ",
+ lifr.lifr_ifinfo.lir_maxhops,
+ lifr.lifr_ifinfo.lir_reachtime,
+ lifr.lifr_ifinfo.lir_reachretrans,
+ lifr.lifr_ifinfo.lir_maxmtu);
+ }
+ }
+ /* If there is a groupname, print it for lun 0 alone */
+ if (strchr(name, ':') == NULL) {
+ (void) memset(lifr.lifr_groupname, 0,
+ sizeof (lifr.lifr_groupname));
+ if (ioctl(s, SIOCGLIFGROUPNAME, (caddr_t)&lifr) >= 0) {
+ if (strlen(lifr.lifr_groupname) > 0) {
+ (void) printf("\n\tgroupname %s",
+ lifr.lifr_groupname);
+ }
+ }
+ }
+ (void) putchar('\n');
+}
+
+static void
+in_configinfo(int force, uint64_t flags)
+{
+ struct sockaddr_in *sin, *laddr;
+ struct sockaddr_in netmask = { AF_INET };
+
+ if (debug)
+ (void) printf("in_configinfo(%s) flags 0x%llx\n", name, flags);
+
+ /* only configinfo info for IPv4 interfaces */
+ if (!(flags & IFF_IPV4))
+ return;
+
+ if (!(flags & IFF_NOLOCAL)) {
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+ if (ioctl(s, SIOCGLIFADDR, (caddr_t)&lifr) < 0) {
+ if (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT ||
+ errno == ENXIO) {
+ if (!force)
+ return;
+ (void) memset(&lifr.lifr_addr, 0,
+ sizeof (lifr.lifr_addr));
+ } else
+ Perror0_exit("in_configinfo: SIOCGLIFADDR");
+ }
+ sin = (struct sockaddr_in *)&lifr.lifr_addr;
+ if (get_lun(name) != 0) {
+ (void) printf(" addif %s ", inet_ntoa(sin->sin_addr));
+ } else {
+ (void) printf(" set %s ", inet_ntoa(sin->sin_addr));
+ }
+ laddr = sin;
+ }
+
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+ if (ioctl(s, SIOCGLIFSUBNET, (caddr_t)&lifr) < 0) {
+ if (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT ||
+ errno == ENXIO) {
+ if (!force)
+ return;
+ (void) memset(&lifr.lifr_addr, 0,
+ sizeof (lifr.lifr_addr));
+ } else {
+ Perror0_exit("in_configinfo: SIOCGLIFSUBNET");
+ }
+ }
+ sin = (struct sockaddr_in *)&lifr.lifr_addr;
+
+ if ((flags & IFF_NOLOCAL) ||
+ sin->sin_addr.s_addr != laddr->sin_addr.s_addr) {
+ (void) printf(" subnet %s/%d ", inet_ntoa(sin->sin_addr),
+ lifr.lifr_addrlen);
+ }
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+ if (ioctl(s, SIOCGLIFNETMASK, (caddr_t)&lifr) < 0) {
+ if (errno != EADDRNOTAVAIL)
+ Perror0_exit("in_configinfo: SIOCGLIFNETMASK");
+ (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr));
+ } else
+ netmask.sin_addr =
+ ((struct sockaddr_in *)&lifr.lifr_addr)->sin_addr;
+ if (flags & IFF_POINTOPOINT) {
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+ if (ioctl(s, SIOCGLIFDSTADDR, (caddr_t)&lifr) < 0) {
+ if (errno == EADDRNOTAVAIL)
+ (void) memset(&lifr.lifr_addr, 0,
+ sizeof (lifr.lifr_addr));
+ else
+ Perror0_exit("in_configinfo: SIOCGLIFDSTADDR");
+ }
+ sin = (struct sockaddr_in *)&lifr.lifr_dstaddr;
+ (void) printf(" destination %s ", inet_ntoa(sin->sin_addr));
+ }
+ (void) printf(" netmask 0x%x ", ntohl(netmask.sin_addr.s_addr));
+ if (flags & IFF_BROADCAST) {
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+ if (ioctl(s, SIOCGLIFBRDADDR, (caddr_t)&lifr) < 0) {
+ if (errno == EADDRNOTAVAIL)
+ (void) memset(&lifr.lifr_addr, 0,
+ sizeof (lifr.lifr_addr));
+ else
+ Perror0_exit("in_configinfo: SIOCGLIFBRDADDR");
+ }
+ sin = (struct sockaddr_in *)&lifr.lifr_addr;
+ if (sin->sin_addr.s_addr != 0) {
+ (void) printf(" broadcast %s ",
+ inet_ntoa(sin->sin_addr));
+ }
+ }
+
+ /* If there is a groupname, print it for lun 0 alone */
+ if (get_lun(name) == 0) {
+ (void) memset(lifr.lifr_groupname, 0,
+ sizeof (lifr.lifr_groupname));
+ if (ioctl(s, SIOCGLIFGROUPNAME, (caddr_t)&lifr) >= 0) {
+ if (strlen(lifr.lifr_groupname) > 0) {
+ (void) printf(" group %s ",
+ lifr.lifr_groupname);
+ }
+ }
+ }
+
+ /* Print flags to configure */
+ print_config_flags(flags);
+
+ /* IFF_NOARP applies to AF_INET only */
+ if (flags & IFF_NOARP) {
+ (void) printf("-arp ");
+ }
+}
+
+static void
+in6_configinfo(int force, uint64_t flags)
+{
+ char abuf[INET6_ADDRSTRLEN];
+ struct sockaddr_in6 *sin6, *laddr6;
+
+ if (debug)
+ (void) printf("in6_configinfo(%s) flags 0x%llx\n", name,
+ flags);
+
+ if (!(flags & IFF_IPV6))
+ return;
+
+ if (!(flags & IFF_NOLOCAL)) {
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+ if (ioctl(s, SIOCGLIFADDR, (caddr_t)&lifr) < 0) {
+ if (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT ||
+ errno == ENXIO) {
+ if (!force)
+ return;
+ (void) memset(&lifr.lifr_addr, 0,
+ sizeof (lifr.lifr_addr));
+ } else
+ Perror0_exit("in6_configinfo: SIOCGLIFADDR");
+ }
+ sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
+ if (get_lun(name) != 0) {
+ (void) printf(" addif %s/%d ",
+ inet_ntop(AF_INET6, (void *)&sin6->sin6_addr,
+ abuf, sizeof (abuf)),
+ lifr.lifr_addrlen);
+ } else {
+ (void) printf(" set %s/%d ",
+ inet_ntop(AF_INET6, (void *)&sin6->sin6_addr,
+ abuf, sizeof (abuf)),
+ lifr.lifr_addrlen);
+ }
+ laddr6 = sin6;
+ }
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+ if (ioctl(s, SIOCGLIFSUBNET, (caddr_t)&lifr) < 0) {
+ if (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT ||
+ errno == ENXIO) {
+ if (!force)
+ return;
+ (void) memset(&lifr.lifr_addr, 0,
+ sizeof (lifr.lifr_addr));
+ } else
+ Perror0_exit("in6_configinfo: SIOCGLIFSUBNET");
+ }
+ sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
+ if ((flags & IFF_NOLOCAL) ||
+ !IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, &laddr6->sin6_addr)) {
+ (void) printf(" subnet %s/%d ",
+ inet_ntop(AF_INET6, (void *)&sin6->sin6_addr,
+ abuf, sizeof (abuf)),
+ lifr.lifr_addrlen);
+ }
+
+ if (flags & IFF_POINTOPOINT) {
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+ if (ioctl(s, SIOCGLIFDSTADDR, (caddr_t)&lifr) < 0) {
+ if (errno == EADDRNOTAVAIL)
+ (void) memset(&lifr.lifr_addr, 0,
+ sizeof (lifr.lifr_addr));
+ else
+ Perror0_exit("in6_configinfo: SIOCGLIFDSTADDR");
+ }
+ sin6 = (struct sockaddr_in6 *)&lifr.lifr_dstaddr;
+ (void) printf(" destination %s ",
+ inet_ntop(AF_INET6, (void *)&sin6->sin6_addr,
+ abuf, sizeof (abuf)));
+ }
+
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+ if (ioctl(s, SIOCGLIFTOKEN, (caddr_t)&lifr) < 0) {
+ if (errno == EADDRNOTAVAIL || errno == EINVAL)
+ (void) memset(&lifr.lifr_addr, 0,
+ sizeof (lifr.lifr_addr));
+ else
+ Perror0_exit("in6_configinfo: SIOCGLIFTOKEN");
+ } else {
+ sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
+ (void) printf(" token %s/%d ",
+ inet_ntop(AF_INET6, (void *)&sin6->sin6_addr,
+ abuf, sizeof (abuf)),
+ lifr.lifr_addrlen);
+ }
+
+ /* If there is a groupname, print it for lun 0 alone */
+ if (get_lun(name) == 0) {
+ (void) memset(lifr.lifr_groupname, 0,
+ sizeof (lifr.lifr_groupname));
+ if (ioctl(s, SIOCGLIFGROUPNAME, (caddr_t)&lifr) >= 0) {
+ if (strlen(lifr.lifr_groupname) > 0) {
+ (void) printf(" group %s ",
+ lifr.lifr_groupname);
+ }
+ }
+ }
+
+ /* Print flags to configure */
+ print_config_flags(flags);
+
+ /* IFF_NONUD applies to AF_INET6 only */
+ if (flags & IFF_NONUD) {
+ (void) printf("-nud ");
+ }
+}
+
+/* ARGSUSED */
+static int
+get_lun(char *rsrc)
+{
+ char resource[LIFNAMSIZ];
+ char *cp;
+
+ (void) strcpy(resource, rsrc);
+
+ /* remove LIF component */
+ cp = strchr(resource, ':');
+ if (cp) {
+ cp++;
+ return (atoi(cp));
+ }
+
+ return (0);
+}
+
+/*
+ * We need to plink both the arp-device stream and the arp-ip-device stream.
+ * However the muxid is stored only in IP. Plumbing 2 streams individually
+ * is not atomic, and if ifconfig is killed, the resulting plumbing can
+ * be inconsistent. For eg. if only the arp stream is plumbed, we have lost
+ * the muxid, and the half-baked plumbing can neither be unplumbed nor
+ * replumbed, thus requiring a reboot. To avoid the above the following
+ * scheme is used.
+ *
+ * Ifconfig asks IP to enforce atomicity of plumbing the arp and IP streams.
+ * This is done by pushing arp on to the mux (/dev/udp). ARP adds some
+ * extra information in the I_PLINK and I_PUNLINK ioctls to let IP know
+ * that the plumbing/unplumbing has to be done atomically. Ifconfig plumbs
+ * the IP stream first, and unplumbs it last. The kernel (IP) does not
+ * allow IP stream to be unplumbed without unplumbing arp stream. Similarly
+ * it does not allow arp stream to be plumbed before IP stream is plumbed.
+ * There is no need to use SIOCSLIFMUXID, since the whole operation is atomic,
+ * and IP uses the info in the I_PLINK message to get the muxid.
+ *
+ * a. STREAMS does not allow us to use /dev/ip itself as the mux. So we use
+ * /dev/udp[6].
+ * b. SIOCGLIFMUXID returns the muxid corresponding to the V4 or V6 stream
+ * depending on the open i.e. V4 vs V6 open. So we need to use /dev/udp
+ * or /dev/udp6.
+ * c. We need to push ARP in order to get the required kernel support for
+ * atomic plumbings. The actual work done by ARP is explained in arp.c
+ * Without pushing ARP, we will still be able to plumb/unplumb. But
+ * it is not atomic, and is supported by the kernel for backward
+ * compatibility for other utilities like atmifconfig etc. In this case
+ * the utility must use SIOCSLIFMUXID.
+ */
+static void
+plumb_one_device(dlpi_if_attr_t *diap, int ip_fd, int af)
+{
+ int arp_fd = -1;
+ int arp_muxid = -1, ip_muxid;
+ int mux_fd;
+ char *udp_dev_name;
+ dlpi_if_attr_t dia;
+
+ if (debug)
+ (void) printf("plumb_one_device: ifname %s, ppa %d\n",
+ diap->ifname, diap->ppa);
+
+ if (diap->style == DL_STYLE2 && dlpi_detach(ip_fd, -1) < 0)
+ Perror0_exit("dlpi_detach");
+
+ if (ioctl(ip_fd, I_PUSH, IP_MOD_NAME) == -1)
+ Perror2_exit("I_PUSH", IP_MOD_NAME);
+
+ /*
+ * Push the ARP module onto the interface stream. IP uses
+ * this to send resolution requests up to ARP. We need to
+ * do this before the SLIFNAME ioctl is sent down because
+ * the interface becomes publicly known as soon as the SLIFNAME
+ * ioctl completes. Thus some other process trying to bring up
+ * the interface after SLIFNAME but before we have pushed ARP
+ * could hang. We pop the module again later if it is not needed.
+ */
+ if (ioctl(ip_fd, I_PUSH, ARP_MOD_NAME) == -1)
+ Perror2_exit("I_PUSH", ARP_MOD_NAME);
+
+ /*
+ * Set IFF_IPV4/IFF_IPV6 flags.
+ * At this point in time the kernel also allows an
+ * override of the CANTCHANGE flags.
+ */
+
+ lifr.lifr_name[0] = '\0';
+ if (ioctl(ip_fd, SIOCGLIFFLAGS, (char *)&lifr) == -1)
+ Perror0_exit("plumb_one_device: SIOCGLIFFLAGS");
+
+ /* Set the name string and the IFF_IPV* flag */
+ if (af == AF_INET6) {
+ lifr.lifr_flags |= IFF_IPV6;
+ lifr.lifr_flags &= ~(IFF_BROADCAST | IFF_IPV4);
+ } else {
+ lifr.lifr_flags |= IFF_IPV4;
+ lifr.lifr_flags &= ~IFF_IPV6;
+ }
+
+ /* record the device and module names as interface name */
+ lifr.lifr_ppa = diap->ppa;
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+
+ /* set the interface name */
+ if (ioctl(ip_fd, SIOCSLIFNAME, (char *)&lifr) == -1) {
+ if (errno != EEXIST)
+ Perror0_exit("SIOCSLIFNAME for ip");
+ /*
+ * This difference between the way we behave for EEXIST
+ * and that with other errors exists to preserve legacy
+ * behaviour. Earlier when foreachinterface() and matcif()
+ * were doing the duplicate interface name checks, for
+ * already existing interfaces, inetplumb() returned "0".
+ * To preserve this behaviour, Perror0() and return are
+ * called for EEXIST.
+ */
+ Perror0("SIOCSLIFNAME for ip");
+ return;
+ }
+
+ /* Get the full set of existing flags for this stream */
+ if (ioctl(ip_fd, SIOCGLIFFLAGS, (char *)&lifr) == -1)
+ Perror0_exit("plumb_one_device: SIOCFLIFFLAGS");
+
+ if (debug) {
+ (void) printf("plumb_one_device: %s got flags: \n",
+ lifr.lifr_name);
+ print_flags(lifr.lifr_flags);
+ (void) putchar('\n');
+ }
+
+ /* Check if arp is not actually needed */
+ if (lifr.lifr_flags & (IFF_NOARP|IFF_IPV6)) {
+ if (ioctl(ip_fd, I_POP, 0) == -1)
+ Perror2_exit("I_POP", ARP_MOD_NAME);
+ }
+
+ /*
+ * Open "/dev/udp" for use as a multiplexor to PLINK the
+ * interface stream under. We use "/dev/udp" instead of "/dev/ip"
+ * since STREAMS will not let you PLINK a driver under itself,
+ * and "/dev/ip" is typically the driver at the bottom of
+ * the stream for tunneling interfaces.
+ */
+ if (af == AF_INET6)
+ udp_dev_name = UDP6_DEV_NAME;
+ else
+ udp_dev_name = UDP_DEV_NAME;
+ if ((mux_fd = open_arp_on_udp(udp_dev_name)) == -1)
+ exit(EXIT_FAILURE);
+
+ /* Check if arp is not needed */
+ if (lifr.lifr_flags & (IFF_NOARP|IFF_IPV6)) {
+ /*
+ * PLINK the interface stream so that ifconfig can exit
+ * without tearing down the stream.
+ */
+ if ((ip_muxid = ioctl(mux_fd, I_PLINK, ip_fd)) == -1) {
+ Perror0_exit("I_PLINK for ip");
+ }
+ (void) close(mux_fd);
+ return;
+ }
+
+ /*
+ * This interface does use ARP, so set up a separate stream
+ * from the interface to ARP.
+ *
+ * Note: modules specified by the user are pushed
+ * only on the interface stream, not on the ARP stream.
+ */
+
+ if (debug)
+ (void) printf("plumb_one_device: ifname: %s\n",
+ diap->ifname);
+ arp_fd = dlpi_if_open(diap->ifname, &dia, _B_FALSE);
+
+ if (dia.style == DL_STYLE2 && dlpi_detach(arp_fd, -1) < 0)
+ Perror0_exit("dlpi_detach");
+
+ if (ioctl(arp_fd, I_PUSH, ARP_MOD_NAME) == -1)
+ Perror2_exit("I_PUSH", ARP_MOD_NAME);
+
+ /*
+ * Tell ARP the name and unit number for this interface.
+ * Note that arp has no support for transparent ioctls.
+ */
+ if (strioctl(arp_fd, SIOCSLIFNAME, (char *)&lifr,
+ sizeof (lifr)) == -1) {
+ if (errno != EEXIST)
+ Perror0_exit("SIOCSLIFNAME for arp");
+ Perror0("SIOCSLIFNAME for arp");
+ (void) close(arp_fd);
+ (void) close(mux_fd);
+ return;
+ }
+ /*
+ * PLINK the IP and ARP streams so that ifconfig can exit
+ * without tearing down the stream.
+ */
+ if ((ip_muxid = ioctl(mux_fd, I_PLINK, ip_fd)) == -1) {
+ Perror0_exit("I_PLINK for ip");
+ }
+ if ((arp_muxid = ioctl(mux_fd, I_PLINK, arp_fd)) == -1) {
+ (void) ioctl(mux_fd, I_PUNLINK, ip_muxid);
+ Perror0_exit("I_PLINK for arp");
+ }
+
+ if (debug)
+ (void) printf("arp muxid = %d\n", arp_muxid);
+ (void) close(arp_fd);
+ (void) close(mux_fd);
+}
+
+
+/*
+ * If this is a physical interface then remove it.
+ * If it is a logical interface name use SIOCLIFREMOVEIF to
+ * remove it. In both cases fail if it doesn't exist.
+ */
+/* ARGSUSED */
+static int
+inetunplumb(char *arg, int64_t param)
+{
+ int ip_muxid, arp_muxid;
+ int mux_fd;
+ char *udp_dev_name;
+ char *strptr;
+ uint64_t flags;
+ boolean_t changed_arp_muxid = _B_FALSE;
+ int save_errno;
+
+ strptr = strchr(name, ':');
+ if (strptr != NULL || strcmp(name, LOOPBACK_IF) == 0) {
+ /* Can't unplumb logical interface zero */
+ if (strptr != NULL && strcmp(strptr, ":0") == 0) {
+ (void) fprintf(stderr, "ifconfig: unplumb:"
+ " Cannot unplumb %s: Invalid interface\n", name);
+ exit(1);
+ }
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+ (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr));
+
+ if (ioctl(s, SIOCLIFREMOVEIF, (caddr_t)&lifr) < 0)
+ Perror0_exit("unplumb: SIOCLIFREMOVEIF");
+ return (0);
+ }
+
+ /*
+ * We used /dev/udp or udp6 to set up the mux. So we have to use
+ * the same now for PUNLINK also.
+ */
+ if (afp->af_af == AF_INET6)
+ udp_dev_name = UDP6_DEV_NAME;
+ else
+ udp_dev_name = UDP_DEV_NAME;
+
+ if ((mux_fd = open_arp_on_udp(udp_dev_name)) == -1)
+ exit(EXIT_FAILURE);
+
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+ if (ioctl(mux_fd, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0) {
+ Perror0_exit("unplumb: SIOCGLIFFLAGS");
+ }
+ flags = lifr.lifr_flags;
+ if (ioctl(mux_fd, SIOCGLIFMUXID, (caddr_t)&lifr) < 0) {
+ Perror0_exit("unplumb: SIOCGLIFMUXID");
+ }
+ arp_muxid = lifr.lifr_arp_muxid;
+ ip_muxid = lifr.lifr_ip_muxid;
+ /*
+ * We don't have a good way of knowing whether the arp stream is
+ * plumbed. We can't rely on IFF_NOARP because someone could
+ * have turned it off later using "ifconfig xxx -arp".
+ */
+ if (arp_muxid != 0) {
+ if (debug)
+ (void) printf("arp_muxid %d\n", arp_muxid);
+ if (ioctl(mux_fd, I_PUNLINK, arp_muxid) < 0) {
+ if ((errno == EINVAL) &&
+ (flags & (IFF_NOARP | IFF_IPV6))) {
+ /*
+ * Some plumbing utilities set the muxid to
+ * -1 or some invalid value to signify that
+ * there is no arp stream. Set the muxid to 0
+ * before trying to unplumb the IP stream.
+ * IP does not allow the IP stream to be
+ * unplumbed if it sees a non-null arp muxid,
+ * for consistency of IP-ARP streams.
+ */
+ lifr.lifr_arp_muxid = 0;
+ (void) ioctl(mux_fd, SIOCSLIFMUXID,
+ (caddr_t)&lifr);
+ changed_arp_muxid = _B_TRUE;
+ } else {
+ Perror0("I_PUNLINK for arp");
+ }
+ }
+ }
+ if (debug)
+ (void) printf("ip_muxid %d\n", ip_muxid);
+
+ if (ioctl(mux_fd, I_PUNLINK, ip_muxid) < 0) {
+ if (changed_arp_muxid) {
+ /*
+ * Some error occurred, and we need to restore
+ * everything back to what it was.
+ */
+ save_errno = errno;
+ lifr.lifr_arp_muxid = arp_muxid;
+ lifr.lifr_ip_muxid = ip_muxid;
+ (void) ioctl(mux_fd, SIOCSLIFMUXID, (caddr_t)&lifr);
+ errno = save_errno;
+ }
+ Perror0_exit("I_PUNLINK for ip");
+ }
+ (void) close(mux_fd);
+ return (0);
+}
+
+/*
+ * If this is a physical interface then create it unless it is already
+ * present. If it is a logical interface name use SIOCLIFADDIF to
+ * create and (and fail it if already exists.)
+ * As a special case send SIOCLIFADDIF for the loopback interface. This
+ * is needed since there is no other notion of plumbing the loopback
+ * interface.
+ */
+/* ARGSUSED */
+static int
+inetplumb(char *arg, int64_t param)
+{
+ char *strptr;
+ int dev_fd;
+ dlpi_if_attr_t dia;
+ boolean_t islo;
+
+ strptr = strchr(name, ':');
+ islo = (strcmp(name, LOOPBACK_IF) == 0);
+
+ if (strptr != NULL || islo) {
+ (void) memset(&lifr, 0, sizeof (lifr));
+ (void) strlcpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+ if (islo && ioctl(s, SIOCGLIFADDR, (caddr_t)&lifr) >= 0) {
+ if (debug) {
+ (void) fprintf(stderr,
+ "ifconfig: %s already exists\n", name);
+ }
+ return (0);
+ }
+ if (ioctl(s, SIOCLIFADDIF, (caddr_t)&lifr) < 0) {
+ if (errno == EEXIST) {
+ if (debug) {
+ (void) fprintf(stderr,
+ "ifconfig: %s already exists\n",
+ name);
+ }
+ } else {
+ Perror2_exit("plumb: SIOCLIFADDIF", name);
+ }
+ }
+ /*
+ * IP can create the new logical interface on a different
+ * physical interface in the same IPMP group. Take the new
+ * interface into account for further operations.
+ */
+ (void) strncpy(name, lifr.lifr_name, sizeof (name));
+ return (0);
+ }
+
+ if (debug)
+ (void) printf("inetplumb: %s af %d\n", name, afp->af_af);
+
+ if ((dev_fd = dlpi_if_open(name, &dia, _B_FALSE)) < 0) {
+ Perror2_exit("plumb", name);
+ /* NOTREACHED */
+ }
+
+ plumb_one_device(&dia, dev_fd, afp->af_af);
+ (void) close(dev_fd);
+ return (0);
+}
+
+void
+Perror0(char *cmd)
+{
+ int save_errno;
+
+ save_errno = errno;
+ (void) fprintf(stderr, "ifconfig: ");
+ errno = save_errno;
+ switch (errno) {
+
+ case ENXIO:
+ (void) fprintf(stderr, "%s: %s: no such interface\n",
+ cmd, lifr.lifr_name);
+ break;
+
+ case EPERM:
+ (void) fprintf(stderr, "%s: %s: permission denied\n",
+ cmd, lifr.lifr_name);
+ break;
+
+ case EEXIST:
+ (void) fprintf(stderr, "%s: %s: already exists\n",
+ cmd, lifr.lifr_name);
+ break;
+
+ default: {
+ char buf[BUFSIZ];
+
+ (void) snprintf(buf, sizeof (buf), "%s: %s",
+ cmd, lifr.lifr_name);
+ perror(buf);
+ }
+ }
+}
+
+void
+Perror0_exit(char *cmd)
+{
+ Perror0(cmd);
+ exit(1);
+ /* NOTREACHED */
+}
+
+void
+Perror2(char *cmd, char *str)
+{
+ int save_errno;
+
+ save_errno = errno;
+ (void) fprintf(stderr, "ifconfig: ");
+ errno = save_errno;
+ switch (errno) {
+
+ case ENXIO:
+ (void) fprintf(stderr, "%s: %s: no such interface\n",
+ cmd, str);
+ break;
+
+ case EPERM:
+ (void) fprintf(stderr, "%s: %s: permission denied\n",
+ cmd, str);
+ break;
+
+ default: {
+ char buf[BUFSIZ];
+
+ (void) snprintf(buf, sizeof (buf), "%s: %s", cmd, str);
+ perror(buf);
+ }
+ }
+}
+
+/*
+ * Print out error message (Perror2()) and exit
+ */
+void
+Perror2_exit(char *cmd, char *str)
+{
+ Perror2(cmd, str);
+ exit(1);
+ /* NOTREACHED */
+}
+
+/*
+ * If the last argument is non-NULL allow a <addr>/<n> syntax and
+ * pass out <n> in *plenp.
+ * If <n> doesn't parse return BAD_ADDR as *plenp.
+ * If no /<n> is present return NO_PREFIX as *plenp.
+ */
+static void
+in_getaddr(char *s, struct sockaddr *saddr, int *plenp)
+{
+ struct sockaddr_in *sin = (struct sockaddr_in *)saddr;
+ struct hostent *hp;
+ struct netent *np;
+ char str[BUFSIZ];
+ int error_num;
+
+ (void) strncpy(str, s, sizeof (str));
+
+ /*
+ * Look for '/'<n> is plenp
+ */
+ if (plenp != NULL) {
+ char *cp;
+
+ *plenp = in_getprefixlen(str, _B_TRUE, ADDRBITS_V4);
+ if (*plenp == BAD_ADDR)
+ return;
+ cp = strchr(str, '/');
+ if (cp != NULL)
+ *cp = '\0';
+ } else if (strchr(str, '/') != NULL) {
+ (void) fprintf(stderr, "ifconfig: %s: unexpected '/'\n", str);
+ exit(1);
+ }
+
+ (void) memset(sin, 0, sizeof (*sin));
+
+ /*
+ * Try to catch attempts to set the broadcast address to all 1's.
+ */
+ if (strcmp(str, "255.255.255.255") == 0 ||
+ (strtoul(str, (char **)NULL, 0) == 0xffffffffUL)) {
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = 0xffffffff;
+ return;
+ }
+
+ hp = getipnodebyname(str, AF_INET, 0, &error_num);
+ if (hp) {
+ sin->sin_family = hp->h_addrtype;
+ (void) memcpy(&sin->sin_addr, hp->h_addr, hp->h_length);
+ freehostent(hp);
+ return;
+ }
+ np = getnetbyname(str);
+ if (np) {
+ sin->sin_family = np->n_addrtype;
+ sin->sin_addr = inet_makeaddr(np->n_net, INADDR_ANY);
+ return;
+ }
+ if (error_num == TRY_AGAIN) {
+ (void) fprintf(stderr, "ifconfig: %s: bad address "
+ "(try again later)\n", s);
+ } else {
+ (void) fprintf(stderr, "ifconfig: %s: bad address\n", s);
+ }
+ exit(1);
+}
+
+/*
+ * If the last argument is non-NULL allow a <addr>/<n> syntax and
+ * pass out <n> in *plenp.
+ * If <n> doesn't parse return BAD_ADDR as *plenp.
+ * If no /<n> is present return NO_PREFIX as *plenp.
+ */
+static void
+in6_getaddr(char *s, struct sockaddr *saddr, int *plenp)
+{
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)saddr;
+ struct hostent *hp;
+ char str[BUFSIZ];
+ int error_num;
+
+ (void) strncpy(str, s, sizeof (str));
+
+ /*
+ * Look for '/'<n> is plenp
+ */
+ if (plenp != NULL) {
+ char *cp;
+
+ *plenp = in_getprefixlen(str, _B_TRUE, ADDRBITS_V6);
+ if (*plenp == BAD_ADDR)
+ return;
+ cp = strchr(str, '/');
+ if (cp != NULL)
+ *cp = '\0';
+ } else if (strchr(str, '/') != NULL) {
+ (void) fprintf(stderr, "ifconfig: %s: unexpected '/'\n", str);
+ exit(1);
+ }
+
+ (void) memset(sin6, 0, sizeof (*sin6));
+
+ hp = getipnodebyname(str, AF_INET6, 0, &error_num);
+ if (hp) {
+ sin6->sin6_family = hp->h_addrtype;
+ (void) memcpy(&sin6->sin6_addr, hp->h_addr, hp->h_length);
+ freehostent(hp);
+ return;
+ }
+ if (error_num == TRY_AGAIN) {
+ (void) fprintf(stderr, "ifconfig: %s: bad address "
+ "(try again later)\n", s);
+ } else {
+ (void) fprintf(stderr, "ifconfig: %s: bad address\n", s);
+ }
+ exit(1);
+}
+
+/*
+ * If "slash" is zero this parses the whole string as
+ * an integer. With "slash" non zero it parses the tail part as an integer.
+ *
+ * If it is not a valid integer this returns BAD_ADDR.
+ * If there is /<n> present this returns NO_PREFIX.
+ */
+static int
+in_getprefixlen(char *addr, boolean_t slash, int max_plen)
+{
+ int prefixlen;
+ char *str, *end;
+
+ if (slash) {
+ str = strchr(addr, '/');
+ if (str == NULL)
+ return (NO_PREFIX);
+ str++;
+ } else
+ str = addr;
+
+ prefixlen = strtol(str, &end, 10);
+ if (prefixlen < 0)
+ return (BAD_ADDR);
+ if (str == end)
+ return (BAD_ADDR);
+ if (max_plen != 0 && max_plen < prefixlen)
+ return (BAD_ADDR);
+ return (prefixlen);
+}
+
+/*
+ * Convert a prefix length to a mask.
+ * Returns 1 if ok. 0 otherwise.
+ * Assumes the mask array is zero'ed by the caller.
+ */
+static boolean_t
+in_prefixlentomask(int prefixlen, int maxlen, uchar_t *mask)
+{
+ if (prefixlen < 0 || prefixlen > maxlen)
+ return (0);
+
+ while (prefixlen > 0) {
+ if (prefixlen >= 8) {
+ *mask++ = 0xFF;
+ prefixlen -= 8;
+ continue;
+ }
+ *mask |= 1 << (8 - prefixlen);
+ prefixlen--;
+ }
+ return (1);
+}
+
+static void
+print_flags(uint64_t flags)
+{
+ boolean_t first = _B_TRUE;
+ int cnt, i;
+
+ (void) printf("flags=%llx", flags);
+ cnt = sizeof (if_flags_tbl) / sizeof (if_flags_t);
+ for (i = 0; i < cnt; i++) {
+ if (flags & if_flags_tbl[i].iff_value) {
+ if (first) {
+ (void) printf("<");
+ first = _B_FALSE;
+ } else {
+ /*
+ * It has to be here and not with the
+ * printf below because for the last one,
+ * we don't want a comma before the ">".
+ */
+ (void) printf(",");
+ }
+ (void) printf("%s", if_flags_tbl[i].iff_name);
+ }
+ }
+ if (!first)
+ (void) printf(">");
+}
+
+static void
+print_config_flags(uint64_t flags)
+{
+ int cnt, i;
+
+ cnt = sizeof (if_config_cmd_tbl) / sizeof (if_config_cmd_t);
+ for (i = 0; i < cnt; i++) {
+ if (flags & if_config_cmd_tbl[i].iff_flag) {
+ (void) printf("%s ", if_config_cmd_tbl[i].iff_name);
+ }
+ }
+}
+
+/*
+ * Look in the NIS for the network mask.
+ * returns true if we found one to set.
+ */
+static boolean_t
+in_getmask(struct sockaddr_in *saddr)
+{
+ struct sockaddr_in ifaddr;
+
+ /*
+ * Read the address from the interface
+ */
+ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+ if (ioctl(s, SIOCGLIFADDR, (caddr_t)&lifr) < 0) {
+ if (errno != EADDRNOTAVAIL)
+ (void) fprintf(stderr, "Need net number for mask\n");
+ return (_B_FALSE);
+ }
+ ifaddr = *((struct sockaddr_in *)&lifr.lifr_addr);
+ if (getnetmaskbyaddr(ifaddr.sin_addr, &saddr->sin_addr) == 0) {
+ saddr->sin_family = AF_INET;
+ (void) printf("Setting netmask of %s to %s\n", name,
+ inet_ntoa(saddr->sin_addr));
+ return (_B_TRUE);
+ }
+ return (_B_FALSE);
+}
+
+static int
+strioctl(int s, int cmd, char *buf, int buflen)
+{
+ struct strioctl ioc;
+
+ (void) memset(&ioc, 0, sizeof (ioc));
+ ioc.ic_cmd = cmd;
+ ioc.ic_timout = 0;
+ ioc.ic_len = buflen;
+ ioc.ic_dp = buf;
+ return (ioctl(s, I_STR, (char *)&ioc));
+}
+
+static void
+add_ni(char *name)
+{
+ ni_t **pp;
+ ni_t *p;
+
+ for (pp = &ni_list; (p = *pp) != NULL; pp = &(p->ni_next)) {
+ if (strcmp(p->ni_name, name) == 0) {
+ if (debug > 2)
+ (void) fprintf(stderr, "'%s' is a duplicate\n",
+ name);
+ return;
+ }
+ }
+
+ if (debug > 2)
+ (void) fprintf(stderr, "adding '%s'\n",
+ name);
+
+ if ((p = malloc(sizeof (ni_t))) == NULL)
+ return;
+
+ (void) strlcpy(p->ni_name, name, sizeof (p->ni_name));
+ p->ni_next = NULL;
+
+ *pp = p;
+ num_ni++;
+}
+
+/* ARGSUSED2 */
+static int
+devfs_entry(di_node_t node, di_minor_t minor, void *arg)
+{
+ char *provider;
+ int fd;
+ uint_t ppa;
+ dl_info_ack_t dlia;
+ char ifname[LIFNAMSIZ];
+
+ provider = di_minor_name(minor);
+
+ if (strlen(provider) > LIFNAMSIZ)
+ return (DI_WALK_CONTINUE);
+
+ if (debug > 2)
+ fprintf(stderr, "provider = %s\n", provider);
+
+ if ((fd = dlpi_open(provider)) < 0)
+ return (DI_WALK_CONTINUE);
+
+ if (debug > 2)
+ fprintf(stderr, "getting DL_INFO_ACK\n");
+
+ if (dlpi_info(fd, -1, &dlia, NULL, NULL, NULL, NULL, NULL, NULL) < 0)
+ goto done;
+
+ if (dlia.dl_provider_style == DL_STYLE1) {
+ if (debug > 2)
+ fprintf(stderr, "provider is DL_STYLE1\n");
+ add_ni(provider);
+ goto done;
+ }
+
+ if (debug > 2)
+ fprintf(stderr, "provider is DL_STYLE2\n");
+
+ if (di_minor_type(minor) != DDM_ALIAS) {
+ if (debug > 2)
+ (void) fprintf(stderr,
+ "non-alias node, ignoring\n");
+ goto done;
+ }
+
+ if (debug > 2)
+ (void) fprintf(stderr,
+ "alias node, using instance\n");
+ ppa = di_instance(node);
+
+ if (dlpi_attach(fd, -1, ppa) < 0) {
+ if (debug > 2)
+ (void) fprintf(stderr,
+ "non-existent PPA, ignoring\n");
+ goto done;
+ }
+
+ (void) snprintf(ifname, LIFNAMSIZ, "%s%d", provider, ppa);
+ add_ni(ifname);
+
+done:
+ (void) dlpi_close(fd);
+ return (DI_WALK_CONTINUE);
+}
+
+/*
+ * dhcp-related routines
+ */
+
+static int
+setifdhcp(const char *caller, const char *ifname, int argc, char *argv[])
+{
+ dhcp_ipc_request_t *request;
+ dhcp_ipc_reply_t *reply = NULL;
+ int timeout = DHCP_IPC_WAIT_DEFAULT;
+ dhcp_ipc_type_t type = DHCP_START;
+ int error;
+ boolean_t is_primary = _B_FALSE;
+ boolean_t started = _B_FALSE;
+
+ for (argv++; --argc > 0; argv++) {
+
+ if (strcmp(*argv, "primary") == 0) {
+ is_primary = _B_TRUE;
+ continue;
+ }
+
+ if (strcmp(*argv, "wait") == 0) {
+ if (--argc <= 0) {
+ usage();
+ return (DHCP_EXIT_BADARGS);
+ }
+ argv++;
+
+ if (strcmp(*argv, "forever") == 0) {
+ timeout = DHCP_IPC_WAIT_FOREVER;
+ continue;
+ }
+
+ if (sscanf(*argv, "%d", &timeout) != 1) {
+ usage();
+ return (DHCP_EXIT_BADARGS);
+ }
+
+ if (timeout < 0) {
+ usage();
+ return (DHCP_EXIT_BADARGS);
+ }
+ continue;
+ }
+
+ type = dhcp_string_to_request(*argv);
+ if (type == -1) {
+ usage();
+ return (DHCP_EXIT_BADARGS);
+ }
+ }
+
+ /*
+ * Only try to start agent on start or inform; in all other cases it
+ * has to already be running for anything to make sense.
+ */
+ if (type == DHCP_START || type == DHCP_INFORM) {
+ if (dhcp_start_agent(DHCP_IPC_MAX_WAIT) == -1) {
+ (void) fprintf(stderr, "%s: unable to start %s\n",
+ caller, DHCP_AGENT_PATH);
+ return (DHCP_EXIT_FAILURE);
+ }
+ started = _B_TRUE;
+ }
+
+ if (is_primary)
+ type = type | DHCP_PRIMARY;
+
+ request = dhcp_ipc_alloc_request(type, ifname, NULL, 0, DHCP_TYPE_NONE);
+ if (request == NULL) {
+ (void) fprintf(stderr, "%s: out of memory\n", caller);
+ return (DHCP_EXIT_SYSTEM);
+ }
+
+ error = dhcp_ipc_make_request(request, &reply, timeout);
+ if (error != 0) {
+ free(request);
+ /*
+ * Re-map connect error to not under control if we didn't try a
+ * start operation, as this has to be true and results in a
+ * clearer message, not to mention preserving compatibility
+ * with the days when we always started dhcpagent for every
+ * request.
+ */
+ if (error == DHCP_IPC_E_CONNECT && !started)
+ error = DHCP_IPC_E_UNKIF;
+ (void) fprintf(stderr, "%s: %s: %s\n", caller, ifname,
+ dhcp_ipc_strerror(error));
+ return (DHCP_EXIT_FAILURE);
+ }
+
+ error = reply->return_code;
+ if (error != 0) {
+ free(request);
+ free(reply);
+
+ if (error == DHCP_IPC_E_TIMEOUT && timeout == 0)
+ return (DHCP_EXIT_SUCCESS);
+
+ (void) fprintf(stderr, "%s: %s: %s\n", caller, ifname,
+ dhcp_ipc_strerror(error));
+
+ if (error == DHCP_IPC_E_TIMEOUT)
+ return (DHCP_EXIT_TIMEOUT);
+ else
+ return (DHCP_EXIT_IF_FAILURE);
+ }
+
+ if (DHCP_IPC_CMD(type) == DHCP_STATUS) {
+ (void) printf("%s", dhcp_status_hdr_string());
+ (void) printf("%s", dhcp_status_reply_to_string(reply));
+ }
+
+ free(request);
+ free(reply);
+ return (DHCP_EXIT_SUCCESS);
+}
+
+static void
+usage(void)
+{
+ (void) fprintf(stderr,
+ "usage: ifconfig <interface> | -a[ 4 | 6 | D ][ u | d ][ Z ]\n");
+
+ (void) fprintf(stderr, "%s",
+ "\t[ <addr_family> ]\n"
+ "\t[ <address>[/<prefix_length>] [ <dest_address> ] ]\n"
+ "\t[ set [ <address>][/<prefix_length>] ]"
+ " [ <address>/<prefix_length>] ]\n"
+ "\t[ destination <dest_address> ]\n"
+ "\t[ addif <address>[/<prefix_length>]"
+ " [ <dest_address> ] ]\n"
+ "\t[ removeif <address>[/<prefix_length>] ]\n"
+ "\t[ arp | -arp ]\n"
+ "\t[ auto-revarp ]\n"
+ "\t[ broadcast <broad_addr> ]\n"
+ "\t[ index <if_index> ]\n"
+ "\t[ metric <n> ] [ mtu <n> ]\n"
+ "\t[ netmask <mask> ]\n"
+ "\t[ plumb ] [ unplumb ]\n"
+ "\t[ preferred | -preferred ]\n"
+ "\t[ private | -private ]\n"
+ "\t[ local | -local ]\n"
+ "\t[ router | -router ]\n"
+ "\t[ subnet <subnet_address>]\n"
+ "\t[ trailers | -trailers ]\n"
+ "\t[ token <address>/<prefix_length> ]\n"
+ "\t[ tsrc <tunnel_src_address> ]\n"
+ "\t[ tdst <tunnel_dest_address> ]\n"
+ "\t[ auth_algs <tunnel_AH_authentication_algorithm> ]\n"
+ "\t[ encr_algs <tunnel_ESP_encryption_algorithm> ]\n"
+ "\t[ encr_auth_algs <tunnel_ESP_authentication_algorithm> ]\n"
+ "\t[ up ] [ down ]\n"
+ "\t[ xmit | -xmit ]\n"
+ "\t[ modlist ]\n"
+ "\t[ modinsert <module_name@position> ]\n"
+ "\t[ modremove <module_name@position> ]\n"
+ "\t[ group <groupname>] | [ group \"\"]\n"
+ "\t[ deprecated | -deprecated ]\n"
+ "\t[ standby | -standby ]\n"
+ "\t[ failover | -failover ]\n"
+ "\t[ zone <zonename> | -zone ]\n"
+ "\t[ usesrc <interface> ]\n");
+
+ (void) fprintf(stderr, "or\n");
+ (void) fprintf(stderr,
+ "\tifconfig <interface> | -a[ 4 | 6 | D ] [ u | d ]\n");
+
+ (void) fprintf(stderr, "%s", "\tauto-dhcp | dhcp\n"
+ "\t[ wait <time> | forever ]\n\t[ primary ]\n"
+ "\tstart | drop | ping | release | status | inform\n");
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/ifconfig/ifconfig.h b/usr/src/cmd/cmd-inet/usr.sbin/ifconfig/ifconfig.h
new file mode 100644
index 0000000000..9b96dde475
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/ifconfig/ifconfig.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved. The Berkeley software License Agreement
+ * specifies the terms and conditions for redistribution.
+ */
+
+#ifndef _IFCONFIG_H
+#define _IFCONFIG_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <libdlpi.h>
+
+/*
+ * return values for (af_getaddr)() from in_getprefixlen()
+ */
+#define BAD_ADDR -1 /* prefix is invalid */
+#define NO_PREFIX -2 /* no prefix was found */
+
+#define MAX_MODS 9 /* max modules that can be pushed on intr */
+
+extern int debug;
+extern uid_t euid;
+
+extern void Perror0(char *);
+extern void Perror0_exit(char *);
+extern void Perror2(char *, char *);
+extern void Perror2_exit(char *, char *);
+
+extern int doifrevarp(char *, struct sockaddr_in *);
+extern int getnetmaskbyaddr(struct in_addr, struct in_addr *);
+
+extern int dlpi_set_address(char *, uchar_t *, int);
+extern void dlpi_print_address(char *);
+
+extern int do_dad(char *, struct sockaddr_in6 *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _IFCONFIG_H */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/ifconfig/inc.flg b/usr/src/cmd/cmd-inet/usr.sbin/ifconfig/inc.flg
new file mode 100644
index 0000000000..7d1fdef815
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/ifconfig/inc.flg
@@ -0,0 +1,31 @@
+#!/bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+find_files "s.*" usr/src/common/net/dhcp
+find_files "s.*" usr/src/cmd/cmd-inet/common
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/ifconfig/revarp.c b/usr/src/cmd/cmd-inet/usr.sbin/ifconfig/revarp.c
new file mode 100644
index 0000000000..a5892577ce
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/ifconfig/revarp.c
@@ -0,0 +1,539 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "defs.h"
+#include "ifconfig.h"
+#include <sys/types.h>
+#include <sys/dlpi.h>
+#include <libdlpi.h>
+#include <sys/sysmacros.h>
+#include <deflt.h>
+
+#define IPADDRL sizeof (struct in_addr)
+#define RARPRETRIES 5
+
+/*
+ * The following value (8) is determined to work reliably in switched 10/100MB
+ * ethernet environments. Use caution if you plan on decreasing it.
+ */
+#define RARPTIMEOUT 8
+
+static char defaultfile[] = "/etc/inet/rarp";
+static char retries_var[] = "RARP_RETRIES=";
+static int rarp_timeout = RARPTIMEOUT;
+static int rarp_retries = RARPRETRIES;
+
+static int rarp_write(int, struct arphdr *, uchar_t *, size_t, size_t);
+static int rarp_open(char *, t_uscalar_t, size_t *, uchar_t **,
+ uchar_t **);
+
+/* ARGSUSED */
+int
+doifrevarp(char *ifname, struct sockaddr_in *laddr)
+{
+ int if_fd;
+ struct pollfd pfd;
+ int s, flags, ret;
+ char *ctlbuf, *databuf, *cause;
+ struct strbuf ctl, data;
+ struct arphdr *req, *ans;
+ struct in_addr from;
+ struct in_addr answer;
+ union DL_primitives *dlp;
+ struct lifreq lifr;
+ struct timeval senttime;
+ struct timeval currenttime;
+ int waittime;
+ int tries_left;
+ size_t ifaddrlen, ifrarplen;
+ uchar_t *my_macaddr = NULL, *my_broadcast = NULL;
+
+
+ if (ifname[0] == '\0') {
+ (void) fprintf(stderr, "ifconfig: doifrevarp: name not set\n");
+ exit(1);
+ }
+
+ if (debug)
+ (void) printf("doifrevarp interface %s\n", ifname);
+
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ Perror0_exit("socket");
+ }
+ (void) strncpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
+ if (ioctl(s, SIOCGLIFFLAGS, (char *)&lifr) < 0)
+ Perror0_exit("SIOCGLIFFLAGS");
+
+ /* don't try to revarp if we know it won't work */
+ if ((lifr.lifr_flags & IFF_LOOPBACK) ||
+ (lifr.lifr_flags & IFF_NOARP) ||
+ (lifr.lifr_flags & IFF_POINTOPOINT))
+ return (0);
+
+ /* open rarp interface */
+ if_fd = rarp_open(ifname, ETHERTYPE_REVARP, &ifaddrlen, &my_macaddr,
+ &my_broadcast);
+ if (if_fd < 0)
+ return (0);
+
+ /*
+ * RARP looks at /etc/ethers and NIS, which only works
+ * with 6 byte addresses currently.
+ */
+ if (ifaddrlen != ETHERADDRL) {
+ (void) close(if_fd);
+ free(my_macaddr);
+ free(my_broadcast);
+ return (0);
+ }
+
+ ifrarplen = sizeof (struct arphdr) + (2 * IPADDRL) + (2 * ifaddrlen);
+
+ /* look for adjustments to rarp_retries in the RARP defaults file */
+ if (defopen(defaultfile) == 0) {
+ char *cp;
+
+ if (cp = defread(retries_var)) {
+ int ntries;
+
+ ntries = atoi(cp);
+ if (ntries > 0)
+ rarp_retries = ntries;
+ }
+ (void) defopen(NULL); /* close default file */
+ }
+
+ /* allocate request and response buffers */
+ if (((req = (struct arphdr *)malloc(ifrarplen)) == NULL) ||
+ ((ans = (struct arphdr *)malloc(ifrarplen)) == NULL)) {
+ (void) close(if_fd);
+ free(req);
+ free(my_macaddr);
+ free(my_broadcast);
+ return (0);
+ }
+
+ /* create rarp request */
+ (void) memset(req, 0, ifrarplen);
+ req->ar_hrd = htons(ARPHRD_ETHER);
+ req->ar_pro = htons(ETHERTYPE_IP);
+ req->ar_hln = ifaddrlen;
+ req->ar_pln = IPADDRL;
+ req->ar_op = htons(REVARP_REQUEST);
+
+ (void) memcpy((uchar_t *)req + sizeof (struct arphdr), my_macaddr,
+ ifaddrlen);
+ (void) memcpy((uchar_t *)req + sizeof (struct arphdr) + IPADDRL +
+ ifaddrlen, my_macaddr, ifaddrlen);
+
+ tries_left = rarp_retries;
+rarp_retry:
+ /* send the request */
+ if (rarp_write(if_fd, req, my_broadcast, ifaddrlen, ifrarplen) < 0)
+ goto fail;
+
+ gettimeofday(&senttime, NULL);
+
+ if (debug)
+ (void) printf("rarp sent\n");
+
+
+ /* read the answers */
+ if ((databuf = malloc(BUFSIZ)) == NULL) {
+ (void) fprintf(stderr, "ifconfig: malloc() failed\n");
+ goto fail;
+ }
+ if ((ctlbuf = malloc(BUFSIZ)) == NULL) {
+ (void) fprintf(stderr, "ifconfig: malloc() failed\n");
+ goto fail;
+ }
+ for (;;) {
+ ctl.len = 0;
+ ctl.maxlen = BUFSIZ;
+ ctl.buf = ctlbuf;
+ data.len = 0;
+ data.maxlen = BUFSIZ;
+ data.buf = databuf;
+ flags = 0;
+
+ /*
+ * Check to see when the packet was last sent.
+ * If we have not sent a packet in the last
+ * RARP_TIMEOUT seconds, we should send one now.
+ * Note that if some other host on the network is
+ * sending a broadcast packet, poll will return and we
+ * will find out that it does not match the reply we
+ * are waiting for and then go back to poll. If the
+ * frequency of such packets is > rarp_timeout, we don't
+ * want to just go back to poll. We should send out one
+ * more RARP request before blocking in poll.
+ */
+
+ gettimeofday(&currenttime, NULL);
+ waittime = rarp_timeout -
+ (currenttime.tv_sec - senttime.tv_sec);
+
+ if (waittime <= 0) {
+ if (--tries_left > 0) {
+ if (debug)
+ (void) printf("rarp retry\n");
+ goto rarp_retry;
+ } else {
+ if (debug)
+ (void) printf("rarp timeout\n");
+ goto fail;
+ }
+ }
+
+ /* start RARP reply timeout */
+ pfd.fd = if_fd;
+ pfd.events = POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI;
+ if ((ret = poll(&pfd, 1, waittime * 1000)) == 0) {
+ if (--tries_left > 0) {
+ if (debug)
+ (void) printf("rarp retry\n");
+ goto rarp_retry;
+ } else {
+ if (debug)
+ (void) printf("rarp timeout\n");
+ goto fail;
+ }
+ } else if (ret == -1) {
+ perror("ifconfig: RARP reply poll");
+ goto fail;
+ }
+
+ /* poll returned > 0 for this fd so getmsg should not block */
+ if ((ret = getmsg(if_fd, &ctl, &data, &flags)) < 0) {
+ perror("ifconfig: RARP reply getmsg");
+ goto fail;
+ }
+
+ if (debug) {
+ (void) printf("rarp: ret[%d] ctl.len[%d] data.len[%d] "
+ "flags[%d]\n", ret, ctl.len, data.len, flags);
+ }
+ /* Validate DL_UNITDATA_IND. */
+ /* LINTED: malloc returns a pointer aligned for any use */
+ dlp = (union DL_primitives *)ctlbuf;
+ if (debug) {
+ (void) printf("rarp: dl_primitive[%lu]\n",
+ dlp->dl_primitive);
+ if (dlp->dl_primitive == DL_ERROR_ACK) {
+ (void) printf(
+ "rarp: err ak: dl_errno %lu errno %lu\n",
+ dlp->error_ack.dl_errno,
+ dlp->error_ack.dl_unix_errno);
+ }
+ if (dlp->dl_primitive == DL_UDERROR_IND) {
+ (void) printf("rarp: ud err: err[%lu] len[%lu] "
+ "off[%lu]\n",
+ dlp->uderror_ind.dl_errno,
+ dlp->uderror_ind.dl_dest_addr_length,
+ dlp->uderror_ind.dl_dest_addr_offset);
+ }
+ }
+ (void) memcpy(ans, databuf, ifrarplen);
+ cause = NULL;
+ if (ret & MORECTL)
+ cause = "MORECTL flag";
+ else if (ret & MOREDATA)
+ cause = "MOREDATA flag";
+ else if (ctl.len == 0)
+ cause = "missing control part of message";
+ else if (ctl.len < 0)
+ cause = "short control part of message";
+ else if (dlp->dl_primitive != DL_UNITDATA_IND)
+ cause = "not unitdata_ind";
+ else if (ctl.len < DL_UNITDATA_IND_SIZE)
+ cause = "short unitdata_ind";
+
+ else if (data.len < ifrarplen)
+ cause = "short arp";
+ else if (ans->ar_hrd != htons(ARPHRD_ETHER))
+ cause = "hrd";
+ else if (ans->ar_pro != htons(ETHERTYPE_IP))
+ cause = "pro";
+ else if (ans->ar_hln != ifaddrlen)
+ cause = "hln";
+ else if (ans->ar_pln != IPADDRL)
+ cause = "pln";
+ if (cause) {
+ (void) fprintf(stderr,
+ "sanity check failed; cause: %s\n", cause);
+ continue;
+ }
+
+ switch (ntohs(ans->ar_op)) {
+ case ARPOP_REQUEST:
+ if (debug)
+ (void) printf("Got an arp request\n");
+ break;
+
+ case ARPOP_REPLY:
+ if (debug)
+ (void) printf("Got an arp reply.\n");
+ break;
+
+ case REVARP_REQUEST:
+ if (debug)
+ (void) printf("Got a rarp request.\n");
+ break;
+
+ case REVARP_REPLY:
+
+ (void) memcpy(&answer, (uchar_t *)ans +
+ sizeof (struct arphdr) + (2 * ifaddrlen) +
+ IPADDRL, sizeof (answer));
+ (void) memcpy(&from, (uchar_t *)ans +
+ sizeof (struct arphdr) + ifaddrlen, sizeof (from));
+ if (debug) {
+ (void) printf("answer: %s", inet_ntoa(answer));
+ (void) printf(" [from %s]\n", inet_ntoa(from));
+ }
+ laddr->sin_addr = answer;
+ (void) close(if_fd);
+ free(req);
+ free(ans);
+ free(my_macaddr);
+ free(my_broadcast);
+ return (1);
+
+ default:
+ (void) fprintf(stderr,
+ "ifconfig: unknown opcode 0x%xd\n", ans->ar_op);
+ break;
+ }
+ }
+ /* NOTREACHED */
+fail:
+ (void) close(if_fd);
+ free(req);
+ free(ans);
+ free(my_macaddr);
+ free(my_broadcast);
+ return (0);
+}
+
+/*
+ * Open the datalink provider device and bind to the REVARP type.
+ * Return the resulting descriptor.
+ */
+static int
+rarp_open(char *ifname, t_uscalar_t type, size_t *alen, uchar_t **myaddr,
+ uchar_t **mybaddr)
+{
+ int fd, len;
+ char *str;
+ dl_info_ack_t dlinfo;
+ dlpi_if_attr_t dia;
+ int i;
+
+ if (debug)
+ (void) printf("rarp_open %s\n", ifname);
+
+ fd = dlpi_if_open(ifname, &dia, _B_FALSE);
+ if (fd < 0) {
+ (void) fprintf(stderr, "ifconfig: could not open device for "
+ "%s\n", ifname);
+ return (-1);
+ }
+
+ if (dlpi_info(fd, -1, &dlinfo, NULL, NULL, NULL, NULL, NULL,
+ NULL) < 0) {
+ (void) fprintf(stderr, "ifconfig: info req failed\n");
+ goto failed;
+ }
+
+ if ((*mybaddr = malloc(dlinfo.dl_brdcst_addr_length)) == NULL) {
+ (void) fprintf(stderr, "rarp_open: malloc() failed\n");
+ goto failed;
+ }
+
+ if (dlpi_info(fd, -1, &dlinfo, NULL, NULL, NULL, NULL, *mybaddr,
+ NULL) < 0) {
+ (void) fprintf(stderr, "ifconfig: info req failed\n");
+ goto failed;
+ }
+
+ if (debug) {
+ (void) printf("broadcast addr: ");
+ for (i = 0; i < dlinfo.dl_brdcst_addr_length; i++)
+ (void) printf("%02x", (*mybaddr)[i]);
+ (void) printf("\n");
+ }
+
+ len = *alen = dlinfo.dl_addr_length - abs(dlinfo.dl_sap_length);
+
+ if (debug)
+ (void) printf("rarp_open: addr length = %d\n", len);
+
+ if ((*myaddr = malloc(len)) == NULL) {
+ (void) fprintf(stderr, "rarp_open: malloc() failed\n");
+ goto failed;
+ }
+
+ if (dlpi_bind(fd, -1, type, DL_CLDLS, _B_FALSE, NULL, NULL,
+ *myaddr, NULL) < 0) {
+ (void) fprintf(stderr, "rarp_open: dlpi_bind failed\n");
+ goto failed;
+ }
+
+ if (debug) {
+ str = _link_ntoa(*myaddr, str, len, IFT_OTHER);
+ if (str != NULL) {
+ (void) printf("device %s mac address %s\n",
+ ifname, str);
+ free(str);
+ }
+ }
+
+ return (fd);
+
+failed:
+ (void) dlpi_close(fd);
+ free(*mybaddr);
+ free(*myaddr);
+ return (-1);
+}
+
+static int
+rarp_write(int fd, struct arphdr *ahdr, uchar_t *dhost, size_t maclen,
+ size_t rarplen)
+{
+ struct strbuf ctl, data;
+ union DL_primitives *dlp;
+ char *ctlbuf;
+ int ret;
+ ushort_t etype = ETHERTYPE_REVARP;
+
+ /*
+ * Construct DL_UNITDATA_REQ. Allocate at least BUFSIZ bytes.
+ */
+ ctl.len = DL_UNITDATA_REQ_SIZE + maclen + sizeof (ushort_t);
+ if ((ctl.buf = ctlbuf = malloc(ctl.len)) == NULL) {
+ (void) fprintf(stderr, "ifconfig: malloc() failed\n");
+ return (-1);
+ }
+ /* LINTED: malloc returns a pointer aligned for any use */
+ dlp = (union DL_primitives *)ctlbuf;
+ dlp->unitdata_req.dl_primitive = DL_UNITDATA_REQ;
+ dlp->unitdata_req.dl_dest_addr_length = maclen + sizeof (ushort_t);
+ dlp->unitdata_req.dl_dest_addr_offset = DL_UNITDATA_REQ_SIZE;
+ dlp->unitdata_req.dl_priority.dl_min = 0;
+ dlp->unitdata_req.dl_priority.dl_max = 0;
+
+ /*
+ * XXX FIXME Assumes a specific DLPI address format.
+ */
+ (void) memcpy(ctlbuf + DL_UNITDATA_REQ_SIZE, dhost, maclen);
+ (void) memcpy(ctlbuf + DL_UNITDATA_REQ_SIZE + maclen, &etype,
+ sizeof (etype));
+
+ /* Send DL_UNITDATA_REQ. */
+ data.len = rarplen;
+ data.buf = (char *)ahdr;
+ ret = putmsg(fd, &ctl, &data, 0);
+ free(ctlbuf);
+ return (ret);
+}
+
+int
+dlpi_set_address(char *ifname, uchar_t *ea, int length)
+{
+ int fd;
+ dlpi_if_attr_t dia;
+
+ fd = dlpi_if_open(ifname, &dia, _B_FALSE);
+ if (fd < 0) {
+ (void) fprintf(stderr, "ifconfig: could not open device for "
+ "%s\n", ifname);
+ return (-1);
+ }
+
+ if (dlpi_set_phys_addr(fd, -1, ea, length) < 0) {
+ (void) dlpi_close(fd);
+ return (-1);
+ }
+
+ (void) dlpi_close(fd);
+ return (0);
+}
+
+void
+dlpi_print_address(char *ifname)
+{
+ int fd, len;
+ uchar_t *laddr;
+ dl_info_ack_t dl_info;
+ char *str = NULL;
+ dlpi_if_attr_t dia;
+
+ fd = dlpi_if_open(ifname, &dia, _B_FALSE);
+ if (fd < 0) {
+ /* Do not report an error */
+ return;
+ }
+
+ if (dlpi_info(fd, -1, &dl_info, NULL, NULL, NULL, NULL, NULL,
+ NULL) < 0) {
+ (void) fprintf(stderr, "ifconfig: info req failed\n");
+ goto failed;
+ }
+
+ len = dl_info.dl_addr_length - abs(dl_info.dl_sap_length);
+ if ((laddr = malloc(len)) == NULL) {
+ goto failed;
+ }
+
+ if (dlpi_phys_addr(fd, -1, DL_CURR_PHYS_ADDR, laddr, NULL) < 0) {
+ (void) fprintf(stderr, "ifconfig: phys_addr failed\n");
+ goto failed;
+ }
+
+ (void) dlpi_close(fd);
+ str = _link_ntoa(laddr, str, len, IFT_OTHER);
+ if (str != NULL) {
+ switch (dl_info.dl_mac_type) {
+ case DL_IB:
+ (void) printf("\tipib %s \n", str);
+ break;
+ default:
+ (void) printf("\tether %s \n", str);
+ break;
+ }
+ free(str);
+ }
+ free(laddr);
+
+failed:
+ free(laddr);
+ (void) dlpi_close(fd);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/ikeadm.c b/usr/src/cmd/cmd-inet/usr.sbin/ikeadm.c
new file mode 100644
index 0000000000..55cd43ae69
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/ikeadm.c
@@ -0,0 +1,2829 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <sys/sysconf.h>
+#include <string.h>
+#include <strings.h>
+#include <libintl.h>
+#include <locale.h>
+#include <ctype.h>
+#include <time.h>
+#include <sys/sysmacros.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <errno.h>
+#include <assert.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <door.h>
+#include <setjmp.h>
+
+#include <ipsec_util.h>
+#include <ikedoor.h>
+
+static int doorfd = -1;
+
+/*
+ * These are additional return values for the command line parsing
+ * function (parsecmd()). They are specific to this utility, but
+ * need to share the same space as the IKE_SVC_* defs, without conflicts.
+ * So they're defined relative to the end of that range.
+ */
+#define IKEADM_HELP_GENERAL IKE_SVC_MAX + 1
+#define IKEADM_HELP_GET IKE_SVC_MAX + 2
+#define IKEADM_HELP_SET IKE_SVC_MAX + 3
+#define IKEADM_HELP_ADD IKE_SVC_MAX + 4
+#define IKEADM_HELP_DEL IKE_SVC_MAX + 5
+#define IKEADM_HELP_DUMP IKE_SVC_MAX + 6
+#define IKEADM_HELP_FLUSH IKE_SVC_MAX + 7
+#define IKEADM_HELP_READ IKE_SVC_MAX + 8
+#define IKEADM_HELP_WRITE IKE_SVC_MAX + 9
+#define IKEADM_HELP_HELP IKE_SVC_MAX + 10
+#define IKEADM_EXIT IKE_SVC_MAX + 11
+
+static void
+command_complete(int s)
+{
+ if (interactive) {
+ longjmp(env, 1);
+ } else {
+ exit(s);
+ }
+}
+
+static void
+usage()
+{
+ if (!interactive) {
+ (void) fprintf(stderr, gettext("Usage:\t"
+ "ikeadm [ -hnp ] cmd obj [cmd-specific options]\n"));
+ (void) fprintf(stderr, gettext(" \tikeadm help\n"));
+ }
+
+ command_complete(1);
+}
+
+static void
+print_help()
+{
+ (void) printf(gettext("Valid commands and objects:\n"));
+ (void) printf("\tget debug|priv|stats|p1|rule|preshared [%s]\n",
+ gettext("identifier"));
+ (void) printf("\tset priv %s\n", gettext("level"));
+ (void) printf("\tset debug %s [%s]\n",
+ gettext("level"), gettext("filename"));
+ (void) printf("\tadd rule|preshared {%s}|%s\n",
+ gettext("definition"), gettext("filename"));
+ (void) printf("\tdel p1|rule|preshared %s\n", gettext("identifier"));
+ (void) printf("\tdump p1|rule|preshared\n");
+ (void) printf("\tflush p1\n");
+ (void) printf("\tread rule|preshared [%s]\n", gettext("filename"));
+ (void) printf("\twrite rule|preshared %s\n", gettext("filename"));
+ (void) printf(
+ "\thelp [get|set|add|del|dump|flush|read|write|help]\n");
+ (void) printf("\texit %s\n", gettext("exit the program"));
+ (void) printf("\tquit %s\n", gettext("exit the program"));
+ (void) printf("\n");
+
+ command_complete(0);
+}
+
+static void
+print_get_help()
+{
+ (void) printf(
+ gettext("This command gets information from in.iked.\n\n"));
+ (void) printf(gettext("Objects that may be retrieved include:\n"));
+ (void) printf("\tdebug\t\t");
+ (void) printf(gettext("the current debug level\n"));
+ (void) printf("\tpriv\t\t");
+ (void) printf(gettext("the current privilege level\n"));
+ (void) printf("\tstats\t\t");
+ (void) printf(gettext("current usage statistics\n"));
+ (void) printf("\tp1\t\t");
+ (void) printf(gettext("a phase 1 SA, identified by\n"));
+ (void) printf(gettext("\t\t\t local_ip remote_ip OR\n"));
+ (void) printf(gettext("\t\t\t init_cookie resp_cookie\n"));
+ (void) printf("\trule\t\t");
+ (void) printf(gettext("a phase 1 rule, identified by its label\n"));
+ (void) printf("\tpreshared\t");
+ (void) printf(gettext("a preshared key, identified by\n"));
+ (void) printf(gettext("\t\t\t local_ip remote_ip OR\n"));
+ (void) printf(gettext("\t\t\t local_id remote_id\n"));
+ (void) printf("\n");
+
+ command_complete(0);
+}
+
+static void
+print_set_help()
+{
+ (void) printf(gettext("This command sets values in in.iked.\n\n"));
+ (void) printf(gettext("Objects that may be set include:\n"));
+ (void) printf("\tdebug\t\t");
+ (void) printf(gettext("change the debug level\n"));
+ (void) printf("\tpriv\t\t");
+ (void) printf(
+ gettext("change the privilege level (may only be lowered)\n"));
+ (void) printf("\n");
+
+ command_complete(0);
+}
+
+static void
+print_add_help()
+{
+ (void) printf(
+ gettext("This command adds items to in.iked's tables.\n\n"));
+ (void) printf(gettext("Objects that may be set include:\n"));
+ (void) printf("\trule\t\t");
+ (void) printf(gettext("a phase 1 policy rule\n"));
+ (void) printf("\tpreshared\t");
+ (void) printf(gettext("a preshared key\n"));
+ (void) printf(
+ gettext("\nObjects may be entered on the command-line, as a\n"));
+ (void) printf(
+ gettext("series of keywords and tokens contained in curly\n"));
+ (void) printf(
+ gettext("braces ('{', '}'); or the name of a file containing\n"));
+ (void) printf(gettext("the object definition may be provided.\n\n"));
+ (void) printf(
+ gettext("For security purposes, preshared keys may only be\n"));
+ (void) printf(
+ gettext("entered on the command-line if ikeadm is running in\n"));
+ (void) printf(gettext("interactive mode.\n"));
+ (void) printf("\n");
+
+ command_complete(0);
+}
+
+static void
+print_del_help()
+{
+ (void) printf(
+ gettext("This command deletes an item from in.iked's tables.\n\n"));
+ (void) printf(gettext("Objects that may be deleted include:\n"));
+ (void) printf("\tp1\t\t");
+ (void) printf(gettext("a phase 1 SA, identified by\n"));
+ (void) printf(gettext("\t\t\t local_ip remote_ip OR\n"));
+ (void) printf(gettext("\t\t\t init_cookie resp_cookie\n"));
+ (void) printf("\trule\t\t");
+ (void) printf(gettext("a phase 1 rule, identified by its label\n"));
+ (void) printf("\tpreshared\t");
+ (void) printf(gettext("a preshared key, identified by\n"));
+ (void) printf(gettext("\t\t\t local_ip remote_ip OR\n"));
+ (void) printf(gettext("\t\t\t local_id remote_id\n"));
+ (void) printf("\n");
+
+ command_complete(0);
+}
+
+static void
+print_dump_help()
+{
+ (void) printf(
+ gettext("This command dumps one of in.iked's tables.\n\n"));
+ (void) printf(gettext("Tables that may be dumped include:\n"));
+ (void) printf("\tp1\t\t");
+ (void) printf(gettext("all phase 1 SAs\n"));
+ (void) printf("\trule\t\t");
+ (void) printf(gettext("all phase 1 rules\n"));
+ (void) printf("\tpreshared\t");
+ (void) printf(gettext("all preshared keys\n"));
+ (void) printf("\n");
+
+ command_complete(0);
+}
+
+static void
+print_flush_help()
+{
+ (void) printf(
+ gettext("This command clears one of in.iked's tables.\n\n"));
+ (void) printf(gettext("Tables that may be flushed include:\n"));
+ (void) printf("\tp1\t\t");
+ (void) printf(gettext("all phase 1 SAs\n"));
+ (void) printf("\n");
+
+ command_complete(0);
+}
+
+static void
+print_read_help()
+{
+ (void) printf(
+ gettext("This command reads a new configuration file into\n"));
+ (void) printf(
+ gettext("in.iked, discarding the old configuration info.\n\n"));
+ (void) printf(gettext("Sets of data that may be read include:\n"));
+ (void) printf("\trule\t\t");
+ (void) printf(gettext("all phase 1 rules\n"));
+ (void) printf("\tpreshared\t");
+ (void) printf(gettext("all preshared keys\n\n"));
+ (void) printf(
+ gettext("A filename may be provided to specify a source file\n"));
+ (void) printf(gettext("other than the default.\n"));
+ (void) printf("\n");
+
+ command_complete(0);
+}
+
+static void
+print_write_help()
+{
+ (void) printf(
+ gettext("This command writes in.iked's current configuration\n"));
+ (void) printf(gettext("out to a config file.\n\n"));
+ (void) printf(gettext("Sets of data that may be written include:\n"));
+ (void) printf("\trule\t\t");
+ (void) printf(gettext("all phase 1 rules\n"));
+ (void) printf("\tpreshared\t");
+ (void) printf(gettext("all preshared keys\n\n"));
+ (void) printf(
+ gettext("A filename must be provided to specify the file to\n"));
+ (void) printf(gettext("which the information should be written.\n"));
+ (void) printf("\n");
+
+ command_complete(0);
+}
+
+static void
+print_help_help()
+{
+ (void) printf(
+ gettext("This command provides information about commands.\n\n"));
+ (void) printf(
+ gettext("The 'help' command alone provides a list of valid\n"));
+ (void) printf(
+ gettext("commands, along with the valid objects for each.\n"));
+ (void) printf(
+ gettext("'help' followed by a valid command name provides\n"));
+ (void) printf(gettext("further information about that command.\n"));
+ (void) printf("\n");
+
+ command_complete(0);
+}
+
+/*PRINTFLIKE1*/
+static void
+message(char *fmt, ...)
+{
+ va_list ap;
+ char msgbuf[BUFSIZ];
+
+ va_start(ap, fmt);
+ (void) vsnprintf(msgbuf, BUFSIZ, fmt, ap);
+ (void) fprintf(stderr, gettext("ikeadm: %s\n"), msgbuf);
+ va_end(ap);
+}
+
+static int
+open_door(void)
+{
+ if (doorfd >= 0)
+ (void) close(doorfd);
+ doorfd = open(DOORNM, O_RDWR);
+ return (doorfd);
+}
+
+static ike_service_t *
+ikedoor_call(char *reqp, int size, door_desc_t *descp, int ndesc)
+{
+ door_arg_t arg;
+ int retries = 0;
+
+ arg.data_ptr = reqp;
+ arg.data_size = size;
+ arg.desc_ptr = descp;
+ arg.desc_num = ndesc;
+ arg.rbuf = (char *)NULL;
+ arg.rsize = 0;
+
+retry:
+ if (door_call(doorfd, &arg) < 0) {
+ if ((errno == EBADF) && ((++retries < 2) &&
+ (open_door() >= 0)))
+ goto retry;
+ Bail("door_call failed");
+ }
+
+ if ((ndesc > 0) && (descp->d_attributes & DOOR_RELEASE) &&
+ ((errno == EBADF) || (errno == EFAULT))) {
+ /* callers assume passed fds will be closed no matter what */
+ (void) close(descp->d_data.d_desc.d_descriptor);
+ }
+
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ return ((ike_service_t *)arg.rbuf);
+}
+
+/*
+ * Parsing functions
+ */
+
+/* stolen from ipseckey.c, with a second tier added */
+static int
+parsecmd(char *cmdstr, char *objstr)
+{
+#define MAXOBJS 10
+ static struct objtbl {
+ char *obj;
+ int token;
+ };
+ static struct cmdtbl {
+ char *cmd;
+ int null_obj_token;
+ struct objtbl objt[MAXOBJS];
+ } table[] = {
+ {"get", IKE_SVC_ERROR, {
+ {"debug", IKE_SVC_GET_DBG},
+ {"priv", IKE_SVC_GET_PRIV},
+ {"stats", IKE_SVC_GET_STATS},
+ {"p1", IKE_SVC_GET_P1},
+ {"rule", IKE_SVC_GET_RULE},
+ {"preshared", IKE_SVC_GET_PS},
+ {NULL, IKE_SVC_ERROR},
+ }
+ },
+ {"set", IKE_SVC_ERROR, {
+ {"debug", IKE_SVC_SET_DBG},
+ {"priv", IKE_SVC_SET_PRIV},
+ {NULL, IKE_SVC_ERROR},
+ }
+ },
+ {"add", IKE_SVC_ERROR, {
+ {"rule", IKE_SVC_NEW_RULE},
+ {"preshared", IKE_SVC_NEW_PS},
+ {NULL, IKE_SVC_ERROR},
+ }
+ },
+ {"del", IKE_SVC_ERROR, {
+ {"p1", IKE_SVC_DEL_P1},
+ {"rule", IKE_SVC_DEL_RULE},
+ {"preshared", IKE_SVC_DEL_PS},
+ {NULL, IKE_SVC_ERROR},
+ }
+ },
+ {"dump", IKE_SVC_ERROR, {
+ {"p1", IKE_SVC_DUMP_P1S},
+ {"rule", IKE_SVC_DUMP_RULES},
+ {"preshared", IKE_SVC_DUMP_PS},
+ {NULL, IKE_SVC_ERROR},
+ }
+ },
+ {"flush", IKE_SVC_ERROR, {
+ {"p1", IKE_SVC_FLUSH_P1S},
+ {NULL, IKE_SVC_ERROR},
+ }
+ },
+ {"read", IKE_SVC_ERROR, {
+ {"rule", IKE_SVC_READ_RULES},
+ {"preshared", IKE_SVC_READ_PS},
+ {NULL, IKE_SVC_ERROR},
+ }
+ },
+ {"write", IKE_SVC_ERROR, {
+ {"rule", IKE_SVC_WRITE_RULES},
+ {"preshared", IKE_SVC_WRITE_PS},
+ {NULL, IKE_SVC_ERROR},
+ }
+ },
+ {"help", IKEADM_HELP_GENERAL, {
+ {"get", IKEADM_HELP_GET},
+ {"set", IKEADM_HELP_SET},
+ {"add", IKEADM_HELP_ADD},
+ {"del", IKEADM_HELP_DEL},
+ {"dump", IKEADM_HELP_DUMP},
+ {"flush", IKEADM_HELP_FLUSH},
+ {"read", IKEADM_HELP_READ},
+ {"write", IKEADM_HELP_WRITE},
+ {"help", IKEADM_HELP_HELP},
+ {NULL, IKE_SVC_ERROR},
+ }
+ },
+ {"exit", IKEADM_EXIT, {
+ {NULL, IKE_SVC_ERROR},
+ }
+ },
+ {"quit", IKEADM_EXIT, {
+ {NULL, IKE_SVC_ERROR},
+ }
+ },
+ {"dbg", IKE_SVC_ERROR, {
+ {"rbdump", IKE_SVC_DBG_RBDUMP},
+ {NULL, IKE_SVC_ERROR},
+ }
+ },
+ {NULL, IKE_SVC_ERROR, {
+ {NULL, IKE_SVC_ERROR},
+ }
+ },
+ };
+ struct cmdtbl *ct = table;
+ struct objtbl *ot;
+
+ if (cmdstr == NULL) {
+ return (IKE_SVC_ERROR);
+ }
+
+ while (ct->cmd != NULL && strcmp(ct->cmd, cmdstr) != 0)
+ ct++;
+ ot = ct->objt;
+
+ if (ct->cmd == NULL) {
+ message(gettext("Unrecognized command '%s'"), cmdstr);
+ return (ot->token);
+ }
+
+ if (objstr == NULL) {
+ return (ct->null_obj_token);
+ }
+
+ while (ot->obj != NULL && strcmp(ot->obj, objstr) != 0)
+ ot++;
+
+ if (ot->obj == NULL)
+ message(gettext("Unrecognized object '%s'"), objstr);
+
+ return (ot->token);
+}
+
+/*
+ * Parsing functions:
+ * Parse command-line identification info. All return -1 on failure,
+ * or the number of cmd-line args "consumed" on success (though argc
+ * and argv params are not actually modified).
+ */
+
+static int
+parse_label(int argc, char **argv, char *label)
+{
+ if ((argc < 1) || (argv == NULL))
+ return (-1);
+
+ if (strlcpy(label, argv[0], MAX_LABEL_LEN) >= MAX_LABEL_LEN)
+ return (-1);
+
+ return (1);
+}
+
+/*
+ * Parse an address off the command line. In the hpp param, either
+ * return a hostent pointer (caller frees) or a pointer to a dummy_he_t
+ * (must also be freed by the caller; both cases are handled by the
+ * macro FREE_HE). The new getipnodebyname() call does the Right Thing
+ * (TM), even with raw addresses (colon-separated IPv6 or dotted decimal
+ * IPv4).
+ * (mostly stolen from ipseckey.c, though some tweaks were made
+ * to better serve our purposes here.)
+ */
+
+typedef struct {
+ struct hostent he;
+ char *addtl[2];
+} dummy_he_t;
+
+static int
+parse_addr(int argc, char **argv, struct hostent **hpp)
+{
+ int hp_errno;
+ struct hostent *hp = NULL;
+ dummy_he_t *dhp;
+ char *addr1;
+
+ if ((argc < 1) || (argv == NULL) || (argv[0] == NULL))
+ return (-1);
+
+ if (!nflag) {
+ /*
+ * Try name->address first. Assume AF_INET6, and
+ * get IPV4s, plus IPv6s iff IPv6 is configured.
+ */
+ hp = getipnodebyname(argv[0], AF_INET6, AI_DEFAULT | AI_ALL,
+ &hp_errno);
+ } else {
+ /*
+ * Try a normal address conversion only. malloc a
+ * dummy_he_t to construct a fake hostent. Caller
+ * will know to free this one using free_he().
+ */
+ dhp = (dummy_he_t *)malloc(sizeof (dummy_he_t));
+ addr1 = (char *)malloc(sizeof (struct in6_addr));
+ if (inet_pton(AF_INET6, argv[0], addr1) == 1) {
+ dhp->he.h_addr_list = dhp->addtl;
+ dhp->addtl[0] = addr1;
+ dhp->addtl[1] = NULL;
+ hp = &dhp->he;
+ dhp->he.h_addrtype = AF_INET6;
+ dhp->he.h_length = sizeof (struct in6_addr);
+ } else if (inet_pton(AF_INET, argv[0], addr1) == 1) {
+ dhp->he.h_addr_list = dhp->addtl;
+ dhp->addtl[0] = addr1;
+ dhp->addtl[1] = NULL;
+ hp = &dhp->he;
+ dhp->he.h_addrtype = AF_INET;
+ dhp->he.h_length = sizeof (struct in_addr);
+ } else {
+ hp = NULL;
+ }
+ }
+
+ *hpp = hp;
+
+ if (hp == NULL) {
+ message(gettext("Unknown address %s."), argv[0]);
+ return (-1);
+ }
+
+ return (1);
+}
+
+/*
+ * Free a dummy_he_t structure that was malloc'd in parse_addr().
+ * Unfortunately, callers of parse_addr don't want to know about
+ * dummy_he_t structs, so all they have is a pointer to the struct
+ * hostent; so that's what's passed in. To manage this, we make
+ * the assumption that the struct hostent is the first field in
+ * the dummy_he_t, and therefore a pointer to it is a pointer to
+ * the dummy_he_t.
+ */
+static void
+free_he(struct hostent *hep)
+{
+ dummy_he_t *p = (dummy_he_t *)hep;
+
+ assert(p != NULL);
+
+ if (p->addtl[0])
+ free(p->addtl[0]);
+ if (p->addtl[1])
+ free(p->addtl[1]);
+
+ free(p);
+}
+
+#define FREE_HE(x) \
+ if (nflag) \
+ free_he(x); \
+ else \
+ freehostent(x)
+
+static void
+headdr2sa(char *hea, struct sockaddr_storage *sa, int len)
+{
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+
+ if (len == sizeof (struct in6_addr)) {
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)hea)) {
+ sin = (struct sockaddr_in *)sa;
+ (void) memset(sin, 0, sizeof (*sin));
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ IN6_V4MAPPED_TO_INADDR((struct in6_addr *)hea,
+ &sin->sin_addr);
+ sin->sin_family = AF_INET;
+ } else {
+ sin6 = (struct sockaddr_in6 *)sa;
+ (void) memset(sin6, 0, sizeof (*sin6));
+ (void) memcpy(&sin6->sin6_addr, hea,
+ sizeof (struct in6_addr));
+ sin6->sin6_family = AF_INET6;
+ }
+ } else {
+ sin = (struct sockaddr_in *)sa;
+ (void) memset(sin, 0, sizeof (*sin));
+ (void) memcpy(&sin->sin_addr, hea, sizeof (struct in_addr));
+ sin->sin_family = AF_INET;
+ }
+}
+
+/*
+ * The possible ident-type keywords that might be used on the command
+ * line. This is a superset of the ones supported by ipseckey, those
+ * in the ike config file, and those in ike.preshared.
+ */
+static keywdtab_t idtypes[] = {
+ /* ip, ipv4, and ipv6 are valid for preshared keys... */
+ {SADB_IDENTTYPE_RESERVED, "ip"},
+ {SADB_IDENTTYPE_RESERVED, "ipv4"},
+ {SADB_IDENTTYPE_RESERVED, "ipv6"},
+ {SADB_IDENTTYPE_PREFIX, "prefix"},
+ {SADB_IDENTTYPE_PREFIX, "ipv4-prefix"},
+ {SADB_IDENTTYPE_PREFIX, "ipv6-prefix"},
+ {SADB_IDENTTYPE_PREFIX, "subnet"},
+ {SADB_IDENTTYPE_PREFIX, "subnetv4"},
+ {SADB_IDENTTYPE_PREFIX, "subnetv6"},
+ {SADB_IDENTTYPE_FQDN, "fqdn"},
+ {SADB_IDENTTYPE_FQDN, "dns"},
+ {SADB_IDENTTYPE_FQDN, "domain"},
+ {SADB_IDENTTYPE_FQDN, "domainname"},
+ {SADB_IDENTTYPE_USER_FQDN, "user_fqdn"},
+ {SADB_IDENTTYPE_USER_FQDN, "mbox"},
+ {SADB_IDENTTYPE_USER_FQDN, "mailbox"},
+ {SADB_X_IDENTTYPE_DN, "dn"},
+ {SADB_X_IDENTTYPE_DN, "asn1dn"},
+ {SADB_X_IDENTTYPE_GN, "gn"},
+ {SADB_X_IDENTTYPE_GN, "asn1gn"},
+ {SADB_X_IDENTTYPE_ADDR_RANGE, "ipv4-range"},
+ {SADB_X_IDENTTYPE_ADDR_RANGE, "ipv6-range"},
+ {SADB_X_IDENTTYPE_ADDR_RANGE, "rangev4"},
+ {SADB_X_IDENTTYPE_ADDR_RANGE, "rangev6"},
+ {SADB_X_IDENTTYPE_KEY_ID, "keyid"},
+ {NULL, 0}
+};
+
+static int
+parse_idtype(char *type, uint16_t *idnum)
+{
+ keywdtab_t *idp;
+
+ if (type == NULL)
+ return (-1);
+
+ for (idp = idtypes; idp->kw_str != NULL; idp++) {
+ if (strcasecmp(idp->kw_str, type) == 0) {
+ if (idnum != NULL)
+ *idnum = idp->kw_tag;
+ return (1);
+ }
+ }
+
+ return (-1);
+}
+
+/*
+ * The sadb_ident_t is malloc'd, since its length varies;
+ * so the caller must free() it when done with the data.
+ */
+static int
+parse_ident(int argc, char **argv, sadb_ident_t **idpp)
+{
+ int alloclen, consumed;
+ sadb_ident_t *idp;
+
+ if ((argc < 2) || (argv == NULL) || (argv[0] == NULL) ||
+ (argv[1] == NULL))
+ return (-1);
+
+ alloclen = sizeof (sadb_ident_t) + IKEDOORROUNDUP(strlen(argv[1]) + 1);
+ *idpp = idp = (sadb_ident_t *)malloc(alloclen);
+ if (idp == NULL)
+ Bail("parsing identity");
+
+ if ((consumed = parse_idtype(argv[0], &idp->sadb_ident_type)) < 0) {
+ message(gettext("unknown identity type %s."), argv[0]);
+ return (-1);
+ }
+
+ idp->sadb_ident_len = SADB_8TO64(alloclen);
+ idp->sadb_ident_reserved = 0;
+ idp->sadb_ident_id = 0;
+
+ /* now copy in identity param */
+ (void) strcpy((char *)(idp + 1), argv[1]);
+
+ return (++consumed);
+}
+
+static int
+parse_cky(int argc, char **argv, uint64_t *ckyp)
+{
+ u_longlong_t arg;
+
+ if ((argc < 1) || (argv[0] == NULL))
+ return (-1);
+
+ errno = 0;
+ arg = strtoull(argv[0], NULL, 0);
+ if (errno != 0) {
+ message(gettext("failed to parse cookie %s."), argv[0]);
+ return (-1);
+ }
+
+ *ckyp = (uint64_t)arg;
+
+ return (1);
+}
+
+static int
+parse_addr_pr(int argc, char **argv, struct hostent **h1pp,
+ struct hostent **h2pp)
+{
+ int rtn, consumed = 0;
+
+ if ((rtn = parse_addr(argc, argv, h1pp)) < 0) {
+ return (-1);
+ }
+ consumed = rtn;
+ argc -= rtn;
+ argv += rtn;
+
+ if ((rtn = parse_addr(argc, argv, h2pp)) < 0) {
+ FREE_HE(*h1pp);
+ return (-1);
+ }
+ consumed += rtn;
+
+ return (consumed);
+}
+
+/*
+ * The sadb_ident_ts are malloc'd, since their length varies;
+ * so the caller must free() them when done with the data.
+ */
+static int
+parse_ident_pr(int argc, char **argv, sadb_ident_t **id1pp,
+ sadb_ident_t **id2pp)
+{
+ int rtn, consumed = 0;
+
+ if ((rtn = parse_ident(argc, argv, id1pp)) < 0) {
+ return (-1);
+ }
+ consumed = rtn;
+ argc -= rtn;
+ argv += rtn;
+
+ (*id1pp)->sadb_ident_exttype = SADB_EXT_IDENTITY_SRC;
+
+ if ((rtn = parse_ident(argc, argv, id2pp)) < 0) {
+ free(*id1pp);
+ return (-1);
+ }
+ consumed += rtn;
+
+ (*id2pp)->sadb_ident_exttype = SADB_EXT_IDENTITY_DST;
+
+ return (consumed);
+}
+
+static int
+parse_cky_pr(int argc, char **argv, ike_cky_pr_t *cpr)
+{
+ int rtn, consumed = 0;
+
+ if ((rtn = parse_cky(argc, argv, &cpr->cky_i)) < 0) {
+ return (-1);
+ }
+ consumed = rtn;
+ argc -= rtn;
+ argv += rtn;
+
+ if ((rtn = parse_cky(argc, argv, &cpr->cky_r)) < 0) {
+ return (-1);
+ }
+ consumed += rtn;
+
+ return (consumed);
+}
+
+/*
+ * Preshared key field types...used for parsing preshared keys that
+ * have been entered on the command line. The code to parse preshared
+ * keys (parse_ps, parse_key, parse_psfldid, parse_ikmtype, ...) is
+ * mostly duplicated from in.iked's readps.c.
+ */
+#define PSFLD_LOCID 1
+#define PSFLD_LOCIDTYPE 2
+#define PSFLD_REMID 3
+#define PSFLD_REMIDTYPE 4
+#define PSFLD_MODE 5
+#define PSFLD_KEY 6
+
+static keywdtab_t psfldtypes[] = {
+ {PSFLD_LOCID, "localid"},
+ {PSFLD_LOCIDTYPE, "localidtype"},
+ {PSFLD_REMID, "remoteid"},
+ {PSFLD_REMIDTYPE, "remoteidtype"},
+ {PSFLD_MODE, "ike_mode"},
+ {PSFLD_KEY, "key"},
+ {NULL, 0}
+};
+
+static int
+parse_psfldid(char *type, uint16_t *idnum)
+{
+ keywdtab_t *pfp;
+
+ if (type == NULL)
+ return (-1);
+
+ for (pfp = psfldtypes; pfp->kw_str != NULL; pfp++) {
+ if (strcasecmp(pfp->kw_str, type) == 0) {
+ if (idnum != NULL)
+ *idnum = pfp->kw_tag;
+ return (1);
+ }
+ }
+
+ return (-1);
+}
+
+static keywdtab_t ikemodes[] = {
+ {IKE_XCHG_IDENTITY_PROTECT, "main"},
+ {IKE_XCHG_AGGRESSIVE, "aggressive"},
+ {IKE_XCHG_IP_AND_AGGR, "both"},
+ {NULL, 0}
+};
+
+static int
+parse_ikmtype(char *mode, uint16_t *modenum)
+{
+ keywdtab_t *ikmp;
+
+ if (mode == NULL)
+ return (-1);
+
+ for (ikmp = ikemodes; ikmp->kw_str != NULL; ikmp++) {
+ if (strcasecmp(ikmp->kw_str, mode) == 0) {
+ if (modenum != NULL)
+ *modenum = ikmp->kw_tag;
+ return (1);
+ }
+ }
+
+ return (-1);
+}
+
+#define hd2num(hd) (((hd) >= '0' && (hd) <= '9') ? ((hd) - '0') : \
+ (((hd) >= 'a' && (hd) <= 'f') ? ((hd) - 'a' + 10) : ((hd) - 'A' + 10)))
+
+static uint8_t *
+parse_key(char *input, uint_t *keybuflen, uint_t *lbits)
+{
+ uint8_t *keyp, *keybufp;
+ uint_t i, hexlen = 0, bits, alloclen;
+
+ for (i = 0; input[i] != '\0' && input[i] != '/'; i++)
+ hexlen++;
+
+ if (input[i] == '\0') {
+ bits = 0;
+ } else {
+ /* Have /nn. */
+ input[i] = '\0';
+ if (sscanf((input + i + 1), "%u", &bits) != 1)
+ return (NULL);
+
+ /* hexlen is in nibbles */
+ if (((bits + 3) >> 2) > hexlen)
+ return (NULL);
+
+ /*
+ * Adjust hexlen down if user gave us too small of a bit
+ * count.
+ */
+ if ((hexlen << 2) > bits + 3) {
+ hexlen = (bits + 3) >> 2;
+ input[hexlen] = '\0';
+ }
+ }
+
+ /*
+ * Allocate. Remember, hexlen is in nibbles.
+ */
+
+ alloclen = (hexlen/2 + (hexlen & 0x1));
+ keyp = malloc(alloclen);
+
+ if (keyp == NULL)
+ return (NULL);
+
+ keybufp = keyp;
+ *keybuflen = alloclen;
+ if (bits == 0)
+ *lbits = (hexlen + (hexlen & 0x1)) << 2;
+ else
+ *lbits = bits;
+
+ /*
+ * Read in nibbles. Read in odd-numbered as shifted high.
+ * (e.g. 123 becomes 0x1230).
+ */
+ for (i = 0; input[i] != '\0'; i += 2) {
+ boolean_t second = (input[i + 1] != '\0');
+
+ if (!isxdigit(input[i]) ||
+ (!isxdigit(input[i + 1]) && second)) {
+ free(keyp);
+ return (NULL);
+ }
+ *keyp = (hd2num(input[i]) << 4);
+ if (second)
+ *keyp |= hd2num(input[i + 1]);
+ else
+ break; /* out of for loop. */
+ keyp++;
+ }
+
+ /* zero the remaining bits if we're a non-octet amount. */
+ if (bits & 0x7)
+ *((input[i] == '\0') ? keyp - 1 : keyp) &=
+ 0xff << (8 - (bits & 0x7));
+ return (keybufp);
+}
+
+/*
+ * the ike_ps_t struct (plus trailing data) will be allocated here,
+ * so it will need to be freed by the caller.
+ */
+static int
+parse_ps(int argc, char **argv, ike_ps_t **presharedpp, int *len)
+{
+ uint_t c = 0, locidlen, remidlen, keylen, keybits;
+ uint_t a_locidtotal = 0, a_remidtotal = 0;
+ char *locid, *remid;
+ uint8_t *keyp = NULL;
+ uint16_t fldid, locidtype, remidtype, mtype;
+ struct hostent *loche = NULL, *remhe = NULL;
+ ike_ps_t *psp = NULL;
+ sadb_ident_t *sidp;
+
+ if ((argv[c] == NULL) || (argv[c][0] != '{'))
+ return (-1);
+ if (argv[c][1] != 0) {
+ /* no space between '{' and first token */
+ argv[c]++;
+ } else {
+ c++;
+ }
+
+ while ((c < argc) && (argv[c] != NULL) && (argv[c][0] != '}')) {
+ if (parse_psfldid(argv[c++], &fldid) < 0)
+ goto bail;
+ switch (fldid) {
+ case PSFLD_LOCID:
+ locid = argv[c++];
+ locidlen = strlen(locid) + 1;
+ break;
+ case PSFLD_LOCIDTYPE:
+ if (parse_idtype(argv[c++], &locidtype) < 0)
+ goto bail;
+ break;
+ case PSFLD_REMID:
+ remid = argv[c++];
+ remidlen = strlen(remid) + 1;
+ break;
+ case PSFLD_REMIDTYPE:
+ if (parse_idtype(argv[c++], &remidtype) < 0)
+ goto bail;
+ break;
+ case PSFLD_MODE:
+ if (parse_ikmtype(argv[c++], &mtype) < 0)
+ goto bail;
+ break;
+ case PSFLD_KEY:
+ keyp = parse_key(argv[c++], &keylen, &keybits);
+ if (keyp == NULL)
+ goto bail;
+ break;
+ }
+ }
+ /*
+ * make sure we got all the required fields. If no idtype, assume
+ * ip addr; if that translation fails, we'll catch the error then.
+ */
+ if (locid == NULL || remid == NULL || keyp == NULL || mtype == 0)
+ goto bail;
+
+ /* figure out the size buffer we need */
+ *len = sizeof (ike_ps_t);
+ if (locidtype != SADB_IDENTTYPE_RESERVED) {
+ a_locidtotal = IKEDOORROUNDUP(sizeof (sadb_ident_t) + locidlen);
+ *len += a_locidtotal;
+ }
+ if (remidtype != SADB_IDENTTYPE_RESERVED) {
+ a_remidtotal = IKEDOORROUNDUP(sizeof (sadb_ident_t) + remidlen);
+ *len += a_remidtotal;
+ }
+ *len += keylen;
+
+ psp = malloc(*len);
+ if (psp == NULL)
+ goto bail;
+ (void) memset(psp, 0, *len);
+
+ psp->ps_ike_mode = mtype;
+
+ psp->ps_localid_off = sizeof (ike_ps_t);
+ if (locidtype == SADB_IDENTTYPE_RESERVED) {
+ /*
+ * this is an ip address, store in the sockaddr field;
+ * we won't use an sadb_ident_t.
+ */
+ psp->ps_localid_len = 0;
+ if (parse_addr(1, &locid, &loche) < 0)
+ goto bail;
+ if (loche->h_addr_list[1] != NULL) {
+ message(gettext("preshared key identifier cannot "
+ "match multiple IP addresses"));
+ goto bail;
+ }
+ headdr2sa(loche->h_addr_list[0], &psp->ps_ipaddrs.loc_addr,
+ loche->h_length);
+ FREE_HE(loche);
+ } else {
+ psp->ps_localid_len = sizeof (sadb_ident_t) + locidlen;
+ sidp = (sadb_ident_t *)((int)psp + psp->ps_localid_off);
+ sidp->sadb_ident_len = psp->ps_localid_len;
+ sidp->sadb_ident_type = locidtype;
+ (void) strcpy((char *)(sidp + 1), locid);
+ }
+
+ psp->ps_remoteid_off = psp->ps_localid_off + a_locidtotal;
+ if (remidtype == SADB_IDENTTYPE_RESERVED) {
+ /*
+ * this is an ip address, store in the sockaddr field;
+ * we won't use an sadb_ident_t.
+ */
+ psp->ps_remoteid_len = 0;
+ if (parse_addr(1, &remid, &remhe) < 0)
+ goto bail;
+ if (remhe->h_addr_list[1] != NULL) {
+ message(gettext("preshared key identifier cannot "
+ "match multiple IP addresses"));
+ goto bail;
+ }
+ headdr2sa(remhe->h_addr_list[0], &psp->ps_ipaddrs.rem_addr,
+ remhe->h_length);
+ FREE_HE(remhe);
+ } else {
+ /* make sure we have at least 16-bit alignment */
+ if (remidlen & 0x1)
+ remidlen++;
+ psp->ps_remoteid_len = sizeof (sadb_ident_t) + remidlen;
+ sidp = (sadb_ident_t *)((int)psp + psp->ps_remoteid_off);
+ sidp->sadb_ident_len = psp->ps_remoteid_len;
+ sidp->sadb_ident_type = remidtype;
+ (void) strcpy((char *)(sidp + 1), remid);
+ }
+
+ psp->ps_key_off = psp->ps_remoteid_off + a_remidtotal;
+ psp->ps_key_len = keylen;
+ psp->ps_key_bits = keybits;
+ (void) memcpy((uint8_t *)((int)psp + psp->ps_key_off), keyp, keylen);
+
+ *presharedpp = psp;
+
+ return (c);
+
+bail:
+ if (loche != NULL)
+ FREE_HE(loche);
+ if (remhe != NULL)
+ FREE_HE(remhe);
+ if (keyp != NULL)
+ free(keyp);
+ if (psp != NULL)
+ free(psp);
+
+ *presharedpp = NULL;
+
+ return (-1);
+}
+
+/* stolen from libdhcputil (dhcp_inittab.c) */
+static uint64_t
+ike_ntohll(uint64_t nll)
+{
+#ifdef _LITTLE_ENDIAN
+ return ((uint64_t)ntohl(nll & 0xffffffff) << 32 | ntohl(nll >> 32));
+#else
+ return (nll);
+#endif
+}
+
+/*
+ * Printing functions
+ *
+ * A potential point of confusion here is that the ikeadm-specific string-
+ * producing functions do not match the ipsec_util.c versions in style: the
+ * ikeadm-specific functions return a string (and are named foostr), while
+ * the ipsec_util.c functions actually print the string to the file named
+ * in the second arg to the function (and are named dump_foo).
+ *
+ * The reason for this is that in the context of the ikeadm output, it
+ * seemed like the localization of the text would be more straightforward
+ * (and could more easily accomodate non-english grammar!) if more complete
+ * phrases were being translated, rather than a bit of a phrase followed by
+ * a call to dump_foo() followed by more of the phrase.
+ */
+
+static char *
+errstr(int err)
+{
+ static char rtn[MAXLINESIZE];
+
+ switch (err) {
+ case IKE_ERR_NO_OBJ:
+ return (gettext("No data returned"));
+ case IKE_ERR_NO_DESC:
+ return (gettext("No destination provided"));
+ case IKE_ERR_ID_INVALID:
+ return (gettext("Id info invalid"));
+ case IKE_ERR_LOC_INVALID:
+ return (gettext("Destination invalid"));
+ case IKE_ERR_CMD_INVALID:
+ return (gettext("Command invalid"));
+ case IKE_ERR_DATA_INVALID:
+ return (gettext("Supplied data invalid"));
+ case IKE_ERR_CMD_NOTSUP:
+ return (gettext("Unknown command"));
+ case IKE_ERR_REQ_INVALID:
+ return (gettext("Request invalid"));
+ case IKE_ERR_NO_PRIV:
+ return (gettext("Not allowed at current privilege level"));
+ case IKE_ERR_SYS_ERR:
+ return (gettext("System error"));
+ default:
+ (void) snprintf(rtn, MAXLINESIZE,
+ gettext("<unknown error %d>"), err);
+ return (rtn);
+ }
+}
+
+static char *
+dbgstr(int bit)
+{
+ static char rtn[MAXLINESIZE];
+
+ switch (bit) {
+ case D_CERT:
+ return (gettext("Certificate management"));
+ case D_KEY:
+ return (gettext("Key management"));
+ case D_OP:
+ return (gettext("Operational"));
+ case D_P1:
+ return (gettext("Phase 1 SA creation"));
+ case D_P2:
+ return (gettext("Phase 2 SA creation"));
+ case D_PFKEY:
+ return (gettext("PF_KEY interface"));
+ case D_POL:
+ return (gettext("Policy management"));
+ case D_PROP:
+ return (gettext("Proposal construction"));
+ case D_DOOR:
+ return (gettext("Door interface"));
+ case D_CONFIG:
+ return (gettext("Config file processing"));
+ default:
+ (void) snprintf(rtn, MAXLINESIZE,
+ gettext("<unknown flag 0x%x>"), bit);
+ return (rtn);
+ }
+}
+
+static char *
+privstr(int priv)
+{
+ static char rtn[MAXLINESIZE];
+
+ switch (priv) {
+ case IKE_PRIV_MINIMUM:
+ return (gettext("base privileges"));
+ case IKE_PRIV_MODKEYS:
+ return (gettext("access to preshared key information"));
+ case IKE_PRIV_KEYMAT:
+ return (gettext("access to keying material"));
+ default:
+ (void) snprintf(rtn, MAXLINESIZE,
+ gettext("<unknown level %d>"), priv);
+ return (rtn);
+ }
+}
+
+static char *
+xchgstr(int xchg)
+{
+ static char rtn[MAXLINESIZE];
+
+ switch (xchg) {
+ case IKE_XCHG_NONE:
+ return (gettext("<unspecified>"));
+ case IKE_XCHG_BASE:
+ return (gettext("base"));
+ case IKE_XCHG_IDENTITY_PROTECT:
+ return (gettext("main mode (identity protect)"));
+ case IKE_XCHG_AUTH_ONLY:
+ return (gettext("authentication only"));
+ case IKE_XCHG_AGGRESSIVE:
+ return (gettext("aggressive mode"));
+ case IKE_XCHG_IP_AND_AGGR:
+ return (gettext("main and aggressive mode"));
+ case IKE_XCHG_ANY:
+ return (gettext("any mode"));
+ default:
+ (void) snprintf(rtn, MAXLINESIZE,
+ gettext("<unknown %d>"), xchg);
+ return (rtn);
+ }
+}
+
+static char *
+statestr(int state)
+{
+ static char rtn[MAXLINESIZE];
+
+ switch (state) {
+ case IKE_SA_STATE_INIT:
+ return (gettext("INITIALIZING"));
+ case IKE_SA_STATE_SENT_SA:
+ return (gettext("SENT FIRST MSG (SA)"));
+ case IKE_SA_STATE_SENT_KE:
+ return (gettext("SENT SECOND MSG (KE)"));
+ case IKE_SA_STATE_SENT_LAST:
+ return (gettext("SENT FINAL MSG"));
+ case IKE_SA_STATE_DONE:
+ return (gettext("ACTIVE"));
+ case IKE_SA_STATE_DELETED:
+ return (gettext("DELETED"));
+ case IKE_SA_STATE_INVALID:
+ return (gettext("<invalid>"));
+ default:
+ (void) snprintf(rtn, MAXLINESIZE,
+ gettext("<unknown %d>"), state);
+ return (rtn);
+ }
+}
+
+static char *
+authmethstr(int meth)
+{
+ static char rtn[MAXLINESIZE];
+
+ switch (meth) {
+ case IKE_AUTH_METH_PRE_SHARED_KEY:
+ return (gettext("pre-shared key"));
+ case IKE_AUTH_METH_DSS_SIG:
+ return (gettext("DSS signatures"));
+ case IKE_AUTH_METH_RSA_SIG:
+ return (gettext("RSA signatures"));
+ case IKE_AUTH_METH_RSA_ENCR:
+ return (gettext("RSA Encryption"));
+ case IKE_AUTH_METH_RSA_ENCR_REVISED:
+ return (gettext("Revised RSA Encryption"));
+ default:
+ (void) snprintf(rtn, MAXLINESIZE,
+ gettext("<unknown %d>"), meth);
+ return (rtn);
+ }
+}
+
+static char *
+prfstr(int prf)
+{
+ static char rtn[MAXLINESIZE];
+
+ switch (prf) {
+ case IKE_PRF_NONE:
+ return (gettext("<unknown>"));
+ case IKE_PRF_HMAC_MD5:
+ return ("HMAC MD5");
+ case IKE_PRF_HMAC_SHA1:
+ return ("HMAC SHA1");
+ default:
+ (void) snprintf(rtn, MAXLINESIZE,
+ gettext("<unknown %d>"), prf);
+ return (rtn);
+ }
+}
+
+static char *
+dhstr(int grp)
+{
+ static char rtn[MAXLINESIZE];
+
+ switch (grp) {
+ case 0:
+ return (gettext("<unknown>"));
+ case IKE_GRP_DESC_MODP_768:
+ return (gettext("768-bit MODP"));
+ case IKE_GRP_DESC_MODP_1024:
+ return (gettext("1024-bit MODP"));
+ case IKE_GRP_DESC_EC2N_155:
+ return (gettext("EC2N group on GP[2^155]"));
+ case IKE_GRP_DESC_EC2N_185:
+ return (gettext("EC2N group on GP[2^185]"));
+ case IKE_GRP_DESC_MODP_1536:
+ return (gettext("1536-bit MODP"));
+ default:
+ (void) snprintf(rtn, MAXLINESIZE, gettext("<unknown %d>"), grp);
+ return (rtn);
+ }
+}
+
+static void
+print_hdr(char *prefix, ike_p1_hdr_t *hdrp)
+{
+ (void) printf(
+ gettext("%s Cookies: Initiator 0x%llx Responder 0x%llx\n"),
+ prefix, ike_ntohll(hdrp->p1hdr_cookies.cky_i),
+ ike_ntohll(hdrp->p1hdr_cookies.cky_r));
+ (void) printf(gettext("%s The local host is the %s.\n"), prefix,
+ hdrp->p1hdr_isinit ? gettext("initiator") : gettext("responder"));
+ (void) printf(gettext("%s ISAKMP version %d.%d; %s exchange\n"), prefix,
+ hdrp->p1hdr_major, hdrp->p1hdr_minor, xchgstr(hdrp->p1hdr_xchg));
+ (void) printf(gettext("%s Current state is %s"), prefix,
+ statestr(hdrp->p1hdr_state));
+ (void) printf("\n");
+}
+
+static void
+print_lt_limits(char *prefix, ike_p1_xform_t *xfp)
+{
+ (void) printf(gettext("%s Lifetime limits:\n"), prefix);
+ (void) printf(gettext("%s %u seconds; %u kbytes protected; "),
+ prefix, xfp->p1xf_max_secs, xfp->p1xf_max_kbytes);
+ (void) printf(gettext("%u keymat provided.\n"), xfp->p1xf_max_keyuses);
+}
+
+#define LT_USAGE_LEN 16 /* 1 uint64 + 2 uint32s */
+static void
+print_lt_usage(char *prefix, ike_p1_stats_t *sp)
+{
+ time_t scratch;
+ char tbuf[TBUF_SIZE];
+
+ (void) printf(gettext("%s Current usage:\n"), prefix);
+ scratch = (time_t)sp->p1stat_start;
+ if (strftime(tbuf, TBUF_SIZE, NULL, localtime(&scratch)) == 0)
+ (void) strlcpy(tbuf, gettext("<time conversion failed>"),
+ TBUF_SIZE);
+ (void) printf(gettext("%s SA was created at %s\n"), prefix, tbuf);
+ (void) printf(gettext("%s %u kbytes protected; %u keymat provided.\n"),
+ prefix, sp->p1stat_kbytes, sp->p1stat_keyuses);
+}
+
+static void
+print_xform(char *prefix, ike_p1_xform_t *xfp, boolean_t print_lifetimes)
+{
+ (void) printf(gettext("%s Authentication method: %s"), prefix,
+ authmethstr(xfp->p1xf_auth_meth));
+ (void) printf(gettext("\n%s Encryption alg: "), prefix);
+ (void) dump_ealg(xfp->p1xf_encr_alg, stdout);
+ (void) printf(gettext("; Authentication alg: "));
+ (void) dump_aalg(xfp->p1xf_auth_alg, stdout);
+ (void) printf(gettext("\n%s PRF: %s"), prefix, prfstr(xfp->p1xf_prf));
+ (void) printf(gettext("; Oakley Group: %s\n"),
+ dhstr(xfp->p1xf_dh_group));
+ if (xfp->p1xf_pfs == 0) {
+ (void) printf(gettext("%s Phase 2 PFS is not used\n"), prefix);
+ } else {
+ (void) printf(gettext(
+ "%s Phase 2 PFS is required (Oakley Group: %s)\n"),
+ prefix, dhstr(xfp->p1xf_pfs));
+ }
+
+ if (print_lifetimes)
+ print_lt_limits(prefix, xfp);
+}
+
+static void
+print_lifetime(char *prefix, ike_p1_xform_t *xfp, ike_p1_stats_t *sp,
+ int statlen)
+{
+ time_t current, remain, exp;
+ char tbuf[TBUF_SIZE];
+
+ current = time(NULL);
+
+ print_lt_limits(prefix, xfp);
+
+ /*
+ * make sure the stats struct we've been passed is as big
+ * as we expect it to be. The usage stats are at the end,
+ * so anything less than the size we expect won't work.
+ */
+ if (statlen >= sizeof (ike_p1_stats_t)) {
+ print_lt_usage(prefix, sp);
+ } else {
+ return;
+ }
+
+ (void) printf(gettext("%s Expiration info:\n"), prefix);
+
+ if (xfp->p1xf_max_kbytes != 0)
+ (void) printf(gettext("%s %u more bytes can be protected.\n"),
+ prefix, xfp->p1xf_max_kbytes - sp->p1stat_kbytes);
+
+ if (xfp->p1xf_max_keyuses != 0)
+ (void) printf(gettext("%s Keying material can be provided "
+ "%u more times.\n"), prefix,
+ xfp->p1xf_max_keyuses - sp->p1stat_keyuses);
+
+ if (xfp->p1xf_max_secs != 0) {
+ exp = (time_t)sp->p1stat_start + (time_t)xfp->p1xf_max_secs;
+ remain = exp - current;
+ if (strftime(tbuf, TBUF_SIZE, NULL, localtime(&exp)) == 0)
+ (void) strlcpy(tbuf,
+ gettext("<time conversion failed>"), TBUF_SIZE);
+ (void) printf(gettext("%s SA expires in %lu seconds, at %s\n"),
+ prefix, remain, tbuf);
+ }
+}
+
+/* used to verify structure lengths... */
+#define COUNTER_32BIT 4
+#define COUNTER_PAIR 8
+
+static void
+print_p1stats(char *prefix, ike_p1_stats_t *sp, int statlen,
+ boolean_t print_lifetimes)
+{
+ if (statlen < COUNTER_PAIR)
+ return;
+ (void) printf(gettext("%s %u Quick Mode SAs created; "), prefix,
+ sp->p1stat_new_qm_sas);
+ (void) printf(gettext("%u Quick Mode SAs deleted\n"),
+ sp->p1stat_del_qm_sas);
+ statlen -= COUNTER_PAIR;
+
+ if ((print_lifetimes) && (statlen >= LT_USAGE_LEN))
+ print_lt_usage(prefix, sp);
+}
+
+static void
+print_errs(char *prefix, ike_p1_errors_t *errp, int errlen)
+{
+ /*
+ * Don't try to break this one up; it's either all or nothing!
+ */
+ if (errlen < sizeof (ike_p1_errors_t))
+ return;
+
+ (void) printf(gettext("%s %u RX errors: "), prefix,
+ errp->p1err_decrypt + errp->p1err_hash + errp->p1err_otherrx);
+ (void) printf(gettext("%u decryption, %u hash, %u other\n"),
+ errp->p1err_decrypt, errp->p1err_hash, errp->p1err_otherrx);
+ (void) printf(gettext("%s %u TX errors\n"), prefix, errp->p1err_tx);
+}
+
+static void
+print_addr_range(char *prefix, ike_addr_pr_t *pr)
+{
+ boolean_t range = B_TRUE;
+ struct sockaddr_storage *beg, *end;
+ struct sockaddr_in *bsin, *esin;
+ struct sockaddr_in6 *bsin6, *esin6;
+
+ beg = &pr->beg_iprange;
+ end = &pr->end_iprange;
+
+ if (beg->ss_family != end->ss_family) {
+ (void) printf(gettext("%s invalid address range\n"), prefix);
+ return;
+ }
+
+ switch (beg->ss_family) {
+ case AF_INET:
+ bsin = (struct sockaddr_in *)beg;
+ esin = (struct sockaddr_in *)end;
+ if ((uint32_t)bsin->sin_addr.s_addr ==
+ (uint32_t)esin->sin_addr.s_addr)
+ range = B_FALSE;
+ break;
+ case AF_INET6:
+ bsin6 = (struct sockaddr_in6 *)beg;
+ esin6 = (struct sockaddr_in6 *)end;
+ if (IN6_ARE_ADDR_EQUAL(&bsin6->sin6_addr, &esin6->sin6_addr))
+ range = B_FALSE;
+ break;
+ default:
+ (void) printf(gettext("%s invalid address range\n"), prefix);
+ return;
+ }
+
+ (void) printf("%s ", prefix);
+ (void) dump_sockaddr((struct sockaddr *)beg, B_TRUE, stdout);
+ if (range) {
+ (void) printf(" - ");
+ (void) dump_sockaddr((struct sockaddr *)end, B_TRUE, stdout);
+ }
+ (void) printf("\n");
+
+}
+
+/*
+ * used to tell printing function if info should be identified
+ * as belonging to initiator, responder, or neither
+ */
+#define IS_INITIATOR 1
+#define IS_RESPONDER 2
+#define DONT_PRINT_INIT 3
+
+static void
+print_addr(char *prefix, struct sockaddr_storage *sa, int init_instr)
+{
+ (void) printf(gettext("%s Address"), prefix);
+
+ if (init_instr != DONT_PRINT_INIT)
+ (void) printf(" (%s):\n", (init_instr == IS_INITIATOR) ?
+ gettext("Initiator") : gettext("Responder"));
+ else
+ (void) printf(":\n");
+
+ (void) printf("%s ", prefix);
+ (void) dump_sockaddr((struct sockaddr *)sa, B_FALSE, stdout);
+}
+
+static void
+print_id(char *prefix, sadb_ident_t *idp, int init_instr)
+{
+ boolean_t canprint;
+
+ switch (init_instr) {
+ case IS_INITIATOR:
+ (void) printf(gettext("%s Initiator identity, "), prefix);
+ break;
+ case IS_RESPONDER:
+ (void) printf(gettext("%s Responder identity, "), prefix);
+ break;
+ case DONT_PRINT_INIT:
+ (void) printf(gettext("%s Identity, "), prefix);
+ break;
+ default:
+ (void) printf(gettext("<invalid identity>\n"));
+ return;
+ }
+ (void) printf(gettext("uid=%d, type "), idp->sadb_ident_id);
+ canprint = dump_sadb_idtype(idp->sadb_ident_type, stdout, NULL);
+ if (canprint)
+ (void) printf("\n%s %s\n", prefix, (char *)(idp + 1));
+ else
+ (void) printf(gettext("\n%s <cannot print>\n"), prefix);
+}
+
+static void
+print_idspec(char *prefix, char *idp, int icnt, int ecnt)
+{
+ int i;
+
+ (void) printf(gettext("%s Identity descriptors:\n"), prefix);
+
+ for (i = 0; i < icnt; i++) {
+ if (i == 0)
+ (void) printf(gettext("%s Includes:\n"), prefix);
+ (void) printf("%s %s\n", prefix, idp);
+ idp += strlen(idp) + 1;
+ }
+
+ for (i = 0; i < ecnt; i++) {
+ if (i == 0)
+ (void) printf(gettext("%s Excludes:\n"), prefix);
+ (void) printf("%s %s\n", prefix, idp);
+ idp += strlen(idp) + 1;
+ }
+}
+
+static void
+print_keys(char *prefix, ike_p1_key_t *keyp, int size)
+{
+ uint32_t *curp;
+ ike_p1_key_t *p;
+ int ssize;
+
+ curp = (uint32_t *)keyp;
+
+ ssize = sizeof (ike_p1_key_t);
+
+ while ((intptr_t)curp - (intptr_t)keyp < size) {
+ size_t p1klen, len;
+
+ p = (ike_p1_key_t *)curp;
+ p1klen = p->p1key_len;
+ len = p1klen - ssize;
+
+ p1klen = roundup(p1klen, sizeof (ike_p1_key_t));
+ if (p1klen < ssize) {
+ (void) printf(gettext("Short key\n"));
+ break;
+ }
+
+ switch (p->p1key_type) {
+ case IKE_KEY_PRESHARED:
+ (void) printf(gettext("%s Pre-shared key (%d bytes): "),
+ prefix, len);
+ (void) dump_key((uint8_t *)(p + 1), SADB_8TO1(len),
+ stdout);
+ break;
+ case IKE_KEY_SKEYID:
+ (void) printf(gettext("%s SKEYID (%d bytes): "),
+ prefix, len);
+ (void) dump_key((uint8_t *)(p + 1), SADB_8TO1(len),
+ stdout);
+ break;
+ case IKE_KEY_SKEYID_D:
+ (void) printf(gettext("%s SKEYID_d (%d bytes): "),
+ prefix, len);
+ (void) dump_key((uint8_t *)(p + 1), SADB_8TO1(len),
+ stdout);
+ break;
+ case IKE_KEY_SKEYID_A:
+ (void) printf(gettext("%s SKEYID_a (%d bytes): "),
+ prefix, len);
+ (void) dump_key((uint8_t *)(p + 1), SADB_8TO1(len),
+ stdout);
+ break;
+ case IKE_KEY_SKEYID_E:
+ (void) printf(gettext("%s SKEYID_e (%d bytes): "),
+ prefix, len);
+ (void) dump_key((uint8_t *)(p + 1), SADB_8TO1(len),
+ stdout);
+ break;
+ case IKE_KEY_ENCR:
+ (void) printf(gettext("%s Encryption key (%d bytes): "),
+ prefix, len);
+ (void) dump_key((uint8_t *)(p + 1), SADB_8TO1(len),
+ stdout);
+ break;
+ case IKE_KEY_IV:
+ (void) printf(
+ gettext("%s Initialization vector (%d bytes): "),
+ prefix, len);
+ (void) dump_key((uint8_t *)(p + 1), SADB_8TO1(len),
+ stdout);
+ break;
+ default:
+ (void) printf(gettext("%s Unidentified key info %p %d"),
+ prefix, p, p1klen);
+ }
+ (void) printf("\n");
+ assert(IS_P2ALIGNED(p1klen, 8));
+ curp += (p1klen >> 2);
+ }
+}
+
+static void
+print_p1(ike_p1_sa_t *p1)
+{
+ ike_p1_stats_t *sp;
+ ike_p1_errors_t *ep;
+ ike_p1_key_t *kp;
+ sadb_ident_t *lidp, *ridp;
+ int lstat, rstat;
+
+ (void) printf("\n");
+ print_hdr("IKESA:", &p1->p1sa_hdr);
+ print_xform("XFORM:", &p1->p1sa_xform, B_FALSE);
+
+ if (p1->p1sa_hdr.p1hdr_isinit) {
+ lstat = IS_INITIATOR;
+ rstat = IS_RESPONDER;
+ } else {
+ lstat = IS_RESPONDER;
+ rstat = IS_INITIATOR;
+ }
+ print_addr("LOCIP:", &p1->p1sa_ipaddrs.loc_addr, lstat);
+ print_addr("REMIP:", &p1->p1sa_ipaddrs.rem_addr, rstat);
+
+ /*
+ * the stat len might be 0; but still make the call
+ * to print_lifetime() to pick up the xform info
+ */
+ sp = (ike_p1_stats_t *)((int)(p1) + p1->p1sa_stat_off);
+ print_lifetime("LIFTM:", &p1->p1sa_xform, sp, p1->p1sa_stat_len);
+
+ if (p1->p1sa_stat_len > 0) {
+ print_p1stats("STATS:", sp, p1->p1sa_stat_len, B_FALSE);
+ }
+
+ if (p1->p1sa_error_len > 0) {
+ ep = (ike_p1_errors_t *)((int)(p1) + p1->p1sa_error_off);
+ print_errs("ERRS: ", ep, p1->p1sa_error_len);
+ }
+
+ if (p1->p1sa_localid_len > 0) {
+ lidp = (sadb_ident_t *)((int)(p1) + p1->p1sa_localid_off);
+ print_id("LOCID:", lidp, lstat);
+ }
+
+ if (p1->p1sa_remoteid_len > 0) {
+ ridp = (sadb_ident_t *)((int)(p1) + p1->p1sa_remoteid_off);
+ print_id("REMID:", ridp, rstat);
+ }
+
+ if (p1->p1sa_key_len > 0) {
+ kp = (ike_p1_key_t *)((int)(p1) + p1->p1sa_key_off);
+ print_keys("KEY: ", kp, p1->p1sa_key_len);
+ }
+}
+
+static void
+print_ps(ike_ps_t *ps)
+{
+ sadb_ident_t *lidp, *ridp;
+ uint8_t *keyp;
+
+ (void) printf("\n");
+
+ (void) printf(gettext("PSKEY: For %s exchanges\n"),
+ xchgstr(ps->ps_ike_mode));
+
+ if (ps->ps_key_len > 0) {
+ keyp = (uint8_t *)((int)(ps) + ps->ps_key_off);
+ (void) printf(gettext("PSKEY: Pre-shared key (%d bytes): "),
+ ps->ps_key_len);
+ (void) dump_key(keyp, ps->ps_key_bits, stdout);
+ (void) printf("\n");
+ }
+
+ /*
+ * We get *either* and address or an ident, never both. So if
+ * the ident is there, don't try printing an address.
+ */
+ if (ps->ps_localid_len > 0) {
+ lidp = (sadb_ident_t *)
+ ((int)(ps) + ps->ps_localid_off);
+ print_id("LOCID:", lidp, DONT_PRINT_INIT);
+ } else {
+ print_addr("LOCIP:", &ps->ps_ipaddrs.loc_addr, DONT_PRINT_INIT);
+ }
+
+ if (ps->ps_remoteid_len > 0) {
+ ridp = (sadb_ident_t *)
+ ((int)(ps) + ps->ps_remoteid_off);
+ print_id("REMID:", ridp, DONT_PRINT_INIT);
+ } else {
+ print_addr("REMIP:", &ps->ps_ipaddrs.rem_addr, DONT_PRINT_INIT);
+ }
+}
+
+#define PREFIXLEN 16
+
+static void
+print_rule(ike_rule_t *rp)
+{
+ char prefix[PREFIXLEN];
+ int i;
+ ike_p1_xform_t *xfp;
+ ike_addr_pr_t *lipp, *ripp;
+ char *lidp, *ridp;
+
+ (void) printf("\n");
+ (void) printf(gettext("GLOBL: Label '%s', key manager cookie %u\n"),
+ rp->rule_label, rp->rule_kmcookie);
+ (void) printf(gettext("GLOBL: local_idtype="));
+ (void) dump_sadb_idtype(rp->rule_local_idtype, stdout, NULL);
+ (void) printf(gettext(", ike_mode=%s\n"), xchgstr(rp->rule_ike_mode));
+ (void) printf(gettext("GLOBL: p1_nonce_len=%u, p2_nonce_len=%u\n"),
+ rp->rule_p1_nonce_len, rp->rule_p2_nonce_len);
+ (void) printf(
+ gettext("GLOBL: p2_pfs=%s (group %u), p2_lifetime=%u seconds\n"),
+ (rp->rule_p2_pfs) ? gettext("true") : gettext("false"),
+ rp->rule_p2_pfs, rp->rule_p2_lifetime);
+
+ if (rp->rule_locip_cnt > 0) {
+ (void) printf(gettext("LOCIP: IP address range(s):\n"));
+ lipp = (ike_addr_pr_t *)((int)rp + rp->rule_locip_off);
+ for (i = 0; i < rp->rule_locip_cnt; i++, lipp++) {
+ print_addr_range("LOCIP:", lipp);
+ }
+ }
+
+ if (rp->rule_remip_cnt > 0) {
+ (void) printf(gettext("REMIP: IP address range(s):\n"));
+ ripp = (ike_addr_pr_t *)((int)rp + rp->rule_remip_off);
+ for (i = 0; i < rp->rule_remip_cnt; i++, ripp++) {
+ print_addr_range("REMIP:", ripp);
+ }
+ }
+
+ if (rp->rule_locid_inclcnt + rp->rule_locid_exclcnt > 0) {
+ lidp = (char *)((int)rp + rp->rule_locid_off);
+ print_idspec("LOCID:", lidp, rp->rule_locid_inclcnt,
+ rp->rule_locid_exclcnt);
+ }
+
+ if (rp->rule_remid_inclcnt + rp->rule_remid_exclcnt > 0) {
+ ridp = (char *)((int)rp + rp->rule_remid_off);
+ print_idspec("REMID:", ridp, rp->rule_remid_inclcnt,
+ rp->rule_remid_exclcnt);
+ }
+
+ if (rp->rule_xform_cnt > 0) {
+ (void) printf(gettext("XFRMS: Available Transforms:\n"));
+ xfp = (ike_p1_xform_t *)((int)rp + rp->rule_xform_off);
+ for (i = 0; i < rp->rule_xform_cnt; i++, xfp++) {
+ (void) snprintf(prefix, PREFIXLEN, "XF %2u:", i);
+ print_xform(prefix, xfp, B_TRUE);
+ }
+ }
+}
+
+#undef PREFIXLEN
+
+#define PRSACNTS(init, resp) \
+ (void) printf(gettext("initiator: %10u responder: %10u\n"), \
+ (init), (resp))
+
+static void
+print_stats(ike_stats_t *sp, int len)
+{
+ /*
+ * before printing each line, make sure the structure we were
+ * given is big enough to include the fields needed.
+ */
+ if (len < COUNTER_PAIR)
+ return;
+ (void) printf(gettext("Phase 1 SA counts:\n"));
+ (void) printf(gettext("Current: "));
+ PRSACNTS(sp->st_init_p1_current, sp->st_resp_p1_current);
+ len -= COUNTER_PAIR;
+
+ if (len < COUNTER_PAIR)
+ return;
+ (void) printf(gettext("Total: "));
+ PRSACNTS(sp->st_init_p1_total, sp->st_resp_p1_total);
+ len -= COUNTER_PAIR;
+
+ if (len < COUNTER_PAIR)
+ return;
+ (void) printf(gettext("Attempted: "));
+ PRSACNTS(sp->st_init_p1_attempts, sp->st_resp_p1_attempts);
+ len -= COUNTER_PAIR;
+
+ if (len < (COUNTER_PAIR + COUNTER_32BIT))
+ return;
+ (void) printf(gettext("Failed: "));
+ PRSACNTS(sp->st_init_p1_noresp + sp->st_init_p1_respfail,
+ sp->st_resp_p1_fail);
+ (void) printf(
+ gettext(" initiator fails include %u time-out(s)\n"),
+ sp->st_init_p1_noresp);
+
+ if (len < PATH_MAX)
+ return;
+ if (*(sp->st_pkcs11_libname) != '\0')
+ (void) printf(gettext("PKCS#11 library linked in from %s\n"),
+ sp->st_pkcs11_libname);
+}
+
+static void
+print_categories(int level)
+{
+ int mask;
+
+ if (level == 0) {
+ (void) printf(gettext("No debug categories enabled.\n"));
+ return;
+ }
+
+ (void) printf(gettext("Debug categories enabled:"));
+ for (mask = 1; mask <= D_HIGHBIT; mask <<= 1) {
+ if (level & mask)
+ (void) printf("\n\t%s", dbgstr(mask));
+ }
+ (void) printf("\n");
+}
+
+/*PRINTFLIKE2*/
+static void
+ikeadm_err_exit(ike_err_t *err, char *fmt, ...)
+{
+ va_list ap;
+ char bailbuf[BUFSIZ];
+
+ va_start(ap, fmt);
+ (void) vsnprintf(bailbuf, BUFSIZ, fmt, ap);
+ va_end(ap);
+ if ((err != NULL) && (err->ike_err == IKE_ERR_SYS_ERR)) {
+ bail_msg("%s: %s", bailbuf, (err->ike_err_unix == 0) ?
+ gettext("<unknown error>") : strerror(err->ike_err_unix));
+ } else {
+ bail_msg("%s: %s", bailbuf, (err == NULL) ?
+ gettext("<unknown error>") : errstr(err->ike_err));
+ }
+}
+
+/*PRINTFLIKE2*/
+static void
+ikeadm_err_msg(ike_err_t *err, char *fmt, ...)
+{
+ va_list ap;
+ char mbuf[BUFSIZ];
+
+ va_start(ap, fmt);
+ (void) vsnprintf(mbuf, BUFSIZ, fmt, ap);
+ va_end(ap);
+ if ((err != NULL) && (err->ike_err == IKE_ERR_SYS_ERR)) {
+ message("%s: %s", mbuf, (err->ike_err_unix == 0) ?
+ gettext("<unknown error>") : strerror(err->ike_err_unix));
+ } else {
+ message("%s: %s", mbuf, (err == NULL) ?
+ gettext("<unknown error>") : errstr(err->ike_err));
+ }
+}
+
+
+/*
+ * Command functions
+ */
+
+/*
+ * Exploit the fact that ike_dbg_t and ike_priv_t have identical
+ * formats in the following two functions.
+ */
+static void
+do_getvar(int cmd)
+{
+ ike_service_t req, *rtn;
+ ike_dbg_t *dreq;
+ char *varname;
+
+ switch (cmd) {
+ case IKE_SVC_GET_DBG:
+ varname = gettext("debug");
+ break;
+ case IKE_SVC_GET_PRIV:
+ varname = gettext("privilege");
+ break;
+ default:
+ bail_msg(gettext("unrecognized get command (%d)"), cmd);
+ }
+
+ dreq = &req.svc_dbg;
+ dreq->cmd = cmd;
+ dreq->dbg_level = 0;
+
+ rtn = ikedoor_call((char *)&req, sizeof (ike_dbg_t), NULL, 0);
+
+ if ((rtn == NULL) || (rtn->svc_err.cmd == IKE_SVC_ERROR)) {
+ ikeadm_err_exit(&rtn->svc_err,
+ gettext("error getting %s level"), varname);
+ }
+ dreq = &rtn->svc_dbg;
+ (void) printf(gettext("Current %s level is 0x%x"),
+ varname, dreq->dbg_level);
+
+ if (cmd == IKE_SVC_GET_DBG) {
+ (void) printf("\n");
+ print_categories(dreq->dbg_level);
+ } else {
+ (void) printf(gettext(", %s enabled\n"),
+ privstr(dreq->dbg_level));
+ }
+}
+
+static void
+do_setvar(int cmd, int argc, char **argv)
+{
+ ike_service_t req, *rtn;
+ ike_dbg_t *dreq;
+ door_desc_t *descp = NULL, desc;
+ int fd, ndesc = 0;
+ uint32_t reqlevel;
+ char *varname;
+
+ if (argc < 1)
+ Bail("unspecified level");
+ reqlevel = strtoul(argv[0], NULL, 0);
+
+ switch (cmd) {
+ case IKE_SVC_SET_DBG:
+ if (argc > 2)
+ Bail("Too many arguments to \"set debug\"");
+ varname = gettext("debug");
+ if (reqlevel == 0) {
+ /* check for a string... */
+ reqlevel = parsedbgopts(argv[0]);
+ }
+ if (reqlevel == D_INVALID)
+ bail_msg(gettext("Bad debug flag: %s"), argv[0]);
+ break;
+ case IKE_SVC_SET_PRIV:
+ if (argc > 1)
+ Bail("Too many arguments to \"set priv\"");
+
+ varname = gettext("privilege");
+ if (reqlevel == 0) {
+ /* check for a string... */
+ reqlevel = privstr2num(argv[0]);
+ }
+ if (reqlevel > IKE_PRIV_MAXIMUM)
+ bail_msg(gettext("Bad privilege flag: %s"), argv[0]);
+ break;
+ default:
+ bail_msg(gettext("unrecognized set command (%d)"), cmd);
+ }
+
+ dreq = &req.svc_dbg;
+ dreq->cmd = cmd;
+ dreq->dbg_level = reqlevel;
+
+ if ((argc == 2) && (cmd == IKE_SVC_SET_DBG)) {
+ fd = open(argv[1], O_RDWR | O_CREAT | O_APPEND,
+ S_IRUSR | S_IWUSR);
+ if (fd < 0)
+ Bail("open debug file");
+ desc.d_data.d_desc.d_descriptor = fd;
+ desc.d_attributes = DOOR_DESCRIPTOR;
+ descp = &desc;
+ ndesc = 1;
+ }
+
+ rtn = ikedoor_call((char *)&req, sizeof (ike_dbg_t), descp, ndesc);
+
+ if ((rtn == NULL) || (rtn->svc_err.cmd == IKE_SVC_ERROR)) {
+ ikeadm_err_exit(&rtn->svc_err,
+ gettext("error setting %s level"), varname);
+ }
+ dreq = &rtn->svc_dbg;
+ (void) printf(
+ gettext("Successfully changed %s level from 0x%x to 0x%x\n"),
+ varname, dreq->dbg_level, reqlevel);
+
+ if (cmd == IKE_SVC_SET_DBG) {
+ print_categories(reqlevel);
+ } else {
+ (void) printf(gettext("New privilege level 0x%x enables %s\n"),
+ reqlevel, privstr(reqlevel));
+ }
+}
+
+static void
+do_getstats(int cmd)
+{
+ ike_service_t *rtn;
+ ike_statreq_t sreq, *sreqp;
+ ike_stats_t *sp;
+
+ sreq.cmd = cmd;
+
+ rtn = ikedoor_call((char *)&sreq, sizeof (ike_statreq_t), NULL, 0);
+ if ((rtn == NULL) || (rtn->svc_err.cmd == IKE_SVC_ERROR)) {
+ ikeadm_err_exit(&rtn->svc_err, gettext("error getting stats"));
+ }
+
+ sreqp = &rtn->svc_stats;
+ sp = (ike_stats_t *)(sreqp + 1);
+ print_stats(sp, sreqp->stat_len);
+}
+
+static void
+do_dump(int cmd)
+{
+ char *name;
+ ike_service_t req, *rtn;
+ ike_dump_t *dreq, *dump;
+
+ switch (cmd) {
+ case IKE_SVC_DUMP_P1S:
+ name = gettext("phase 1 SA info");
+ break;
+ case IKE_SVC_DUMP_RULES:
+ name = gettext("policy rules");
+ break;
+ case IKE_SVC_DUMP_PS:
+ name = gettext("preshared keys");
+ break;
+ default:
+ bail_msg(gettext("unrecognized dump command (%d)"), cmd);
+ }
+
+ dreq = &req.svc_dump;
+ dreq->cmd = cmd;
+ dreq->dump_len = 0;
+ dreq->dump_next = 0;
+ do {
+ rtn = ikedoor_call((char *)&req, sizeof (ike_dump_t),
+ NULL, 0);
+ if ((rtn == NULL) || (rtn->svc_err.cmd == IKE_SVC_ERROR)) {
+ if (rtn && (rtn->svc_err.ike_err == IKE_ERR_NO_OBJ)) {
+ /* no entries to print */
+ break;
+ }
+ ikeadm_err_exit(&rtn->svc_err,
+ gettext("error getting %s"), name);
+ }
+ dump = &rtn->svc_dump;
+
+ switch (cmd) {
+ case IKE_SVC_DUMP_P1S:
+ print_p1((ike_p1_sa_t *)(dump + 1));
+ break;
+ case IKE_SVC_DUMP_RULES:
+ print_rule((ike_rule_t *)(dump + 1));
+ break;
+ case IKE_SVC_DUMP_PS:
+ print_ps((ike_ps_t *)(dump + 1));
+ break;
+ }
+
+ dreq->dump_next = dump->dump_next;
+
+ (void) munmap((char *)rtn, dump->dump_len);
+
+ } while (dreq->dump_next);
+
+ (void) printf(gettext("\nCompleted dump of %s\n"), name);
+}
+
+static void
+do_getdel_doorcall(int cmd, int idlen, int idtype, char *idp, char *name)
+{
+ int totallen;
+ char *p;
+ ike_service_t *reqp, *rtnp;
+ ike_get_t *getp;
+ boolean_t getcmd;
+
+ getcmd = ((cmd == IKE_SVC_GET_P1) || (cmd == IKE_SVC_GET_RULE) ||
+ (cmd == IKE_SVC_GET_PS));
+
+ /*
+ * WARNING: to avoid being redundant, this code takes advantage
+ * of the fact that the ike_get_t and ike_del_t structures are
+ * identical (only the field names differ, their function and
+ * size are the same). If for some reason those structures
+ * change, this code will need to be re-written to accomodate
+ * that difference.
+ */
+ totallen = sizeof (ike_get_t) + idlen;
+ if ((reqp = (ike_service_t *)malloc(totallen)) == NULL)
+ Bail("malloc(id)");
+
+ getp = &reqp->svc_get;
+ getp->cmd = cmd;
+ getp->get_len = totallen;
+ getp->get_idtype = idtype;
+ p = (char *)(getp + 1);
+
+ (void) memcpy(p, idp, idlen);
+
+ rtnp = ikedoor_call((char *)reqp, totallen, NULL, 0);
+ if ((rtnp == NULL) || (rtnp->svc_err.cmd == IKE_SVC_ERROR)) {
+ if (rtnp && (rtnp->svc_err.ike_err == IKE_ERR_NO_OBJ)) {
+ message(gettext("Could not find requested %s."), name);
+ } else {
+ ikeadm_err_msg(&rtnp->svc_err, gettext("error %s %s"),
+ (getcmd) ? gettext("getting") : gettext("deleting"),
+ name);
+ }
+ free(reqp);
+ return;
+ }
+ getp = &rtnp->svc_get;
+
+ if (getcmd) {
+ switch (cmd) {
+ case IKE_SVC_GET_P1:
+ print_p1((ike_p1_sa_t *)(getp + 1));
+ break;
+ case IKE_SVC_GET_PS:
+ print_ps((ike_ps_t *)(getp + 1));
+ break;
+ case IKE_SVC_GET_RULE:
+ print_rule((ike_rule_t *)(getp + 1));
+ break;
+ }
+ } else {
+ message(gettext("Successfully deleted selected %s."), name);
+ }
+
+ (void) munmap((char *)rtnp, getp->get_len);
+ free(reqp);
+}
+
+static void
+do_getdel(int cmd, int argc, char **argv)
+{
+ int idlen, idtype = 0, i, j;
+ int bytelen1, bytelen2;
+ char *name, *idp, *p, *p1, *p2;
+ ike_addr_pr_t apr;
+ ike_cky_pr_t cpr;
+ sadb_ident_t *sid1p, *sid2p;
+ struct hostent *he1p, *he2p;
+ char label[MAX_LABEL_LEN];
+
+ if ((argc < 1) || (argv[0] == NULL)) {
+ Bail("not enough identification info");
+ }
+
+ switch (cmd) {
+ case IKE_SVC_GET_P1:
+ case IKE_SVC_DEL_P1:
+ name = gettext("phase 1 SA");
+ /*
+ * The first token must either be an address (or hostname)
+ * or a cookie. We require cookies to be entered as hex
+ * numbers, beginning with 0x; so if our token starts with
+ * that, it's a cookie.
+ */
+ if (strncmp(argv[0], "0x", 2) == 0) {
+ if (parse_cky_pr(argc, argv, &cpr) >= 0) {
+ idtype = IKE_ID_CKY_PAIR;
+ idlen = sizeof (ike_cky_pr_t);
+ idp = (char *)&cpr;
+ }
+ } else {
+ if (parse_addr_pr(argc, argv, &he1p, &he2p) >= 0) {
+ idtype = IKE_ID_ADDR_PAIR;
+ idlen = sizeof (ike_addr_pr_t);
+ }
+ }
+ break;
+
+ case IKE_SVC_GET_RULE:
+ case IKE_SVC_DEL_RULE:
+ name = gettext("policy rule");
+ if (parse_label(argc, argv, label) >= 0) {
+ idtype = IKE_ID_LABEL;
+ idlen = MAX_LABEL_LEN;
+ idp = label;
+ }
+ break;
+
+ case IKE_SVC_GET_PS:
+ case IKE_SVC_DEL_PS:
+ name = gettext("preshared key");
+ /*
+ * The first token must either be an address or an ident
+ * type. Check for an ident type to determine which it is.
+ */
+ if (parse_idtype(argv[0], NULL) >= 0) {
+ if (parse_ident_pr(argc, argv, &sid1p, &sid2p) >= 0) {
+ idtype = IKE_ID_IDENT_PAIR;
+ idlen = SADB_64TO8(sid1p->sadb_ident_len) +
+ SADB_64TO8(sid2p->sadb_ident_len);
+ }
+ } else {
+ if (parse_addr_pr(argc, argv, &he1p, &he2p) >= 0) {
+ idtype = IKE_ID_ADDR_PAIR;
+ idlen = sizeof (ike_addr_pr_t);
+ }
+ }
+ break;
+
+ default:
+ bail_msg(gettext("unrecognized get/del command (%d)"), cmd);
+ }
+
+ switch (idtype) {
+ case IKE_ID_ADDR_PAIR:
+ /*
+ * we might have exploding addrs here; do every possible
+ * combination.
+ */
+ i = 0;
+ j = 0;
+ while ((p1 = he1p->h_addr_list[i++]) != NULL) {
+ headdr2sa(p1, &apr.loc_addr, he1p->h_length);
+
+ while ((p2 = he2p->h_addr_list[j++]) != NULL) {
+ headdr2sa(p2, &apr.rem_addr, he2p->h_length);
+ do_getdel_doorcall(cmd, idlen, idtype,
+ (char *)&apr, name);
+ }
+ }
+ FREE_HE(he1p);
+ FREE_HE(he2p);
+ break;
+
+ case IKE_ID_IDENT_PAIR:
+ bytelen1 = SADB_64TO8(sid1p->sadb_ident_len);
+ bytelen2 = SADB_64TO8(sid2p->sadb_ident_len);
+ if (idlen != bytelen1 + bytelen2)
+ Bail("ident syntax error");
+ idp = p = (char *)malloc(idlen);
+ if (p == NULL)
+ Bail("malloc(id)");
+ (void) memcpy(p, (char *)sid1p, bytelen1);
+ p += bytelen1;
+ (void) memcpy(p, (char *)sid2p, bytelen2);
+ do_getdel_doorcall(cmd, idlen, idtype, idp, name);
+ free(idp);
+ free(sid1p);
+ free(sid2p);
+ break;
+
+ case IKE_ID_CKY_PAIR:
+ case IKE_ID_LABEL:
+ do_getdel_doorcall(cmd, idlen, idtype, idp, name);
+ break;
+
+ case 0:
+ default:
+ bail_msg(gettext("invalid %s identification\n"), name);
+ }
+}
+
+/*
+ * Copy source into target, inserting an escape character ('\') before
+ * any quotes that appear. Return true on success, false on failure.
+ */
+static boolean_t
+escapequotes(char *target, char *source, int tlen)
+{
+ int s, t, len = strlen(source) + 1;
+
+ if (tlen < len)
+ return (B_FALSE);
+
+ for (s = 0, t = 0; s < len && t < tlen; s++) {
+ if (source[s] == '\"')
+ target[t++] = '\\';
+ target[t++] = source[s];
+ }
+
+ if ((t == tlen) && (s < len))
+ return (B_FALSE);
+
+ return (B_TRUE);
+}
+
+/*
+ * Return true if the arg following the given keyword should
+ * be in quotes (i.e. is a string), false if not.
+ */
+static boolean_t
+quotedfield(char *keywd)
+{
+ if ((strncmp(keywd, "label", strlen("label") + 1) == 0) ||
+ (strncmp(keywd, "local_id", strlen("local_id") + 1) == 0) ||
+ (strncmp(keywd, "remote_id", strlen("remote_id") + 1) == 0))
+ return (B_TRUE);
+
+ return (B_FALSE);
+}
+
+static void
+do_new(int cmd, int argc, char **argv)
+{
+ ike_service_t *rtn;
+ ike_new_t new, *newp = NULL;
+ door_desc_t desc, *descp = NULL;
+ int i, fd, ndesc = 0, buflen;
+ char *name, tmpfilepath[32];
+ FILE *tmpfile;
+
+ switch (cmd) {
+ case IKE_SVC_NEW_PS:
+ name = gettext("preshared key");
+ break;
+ case IKE_SVC_NEW_RULE:
+ name = gettext("policy rule");
+ break;
+ default:
+ bail_msg(gettext("unrecognized new command (%d)"), cmd);
+ }
+
+ if (argc == 1) {
+ /* We've been given a file to read from */
+ fd = open(argv[0], O_RDONLY);
+ if (fd < 0)
+ Bail("open source file");
+
+ desc.d_data.d_desc.d_descriptor = fd;
+ desc.d_attributes = DOOR_DESCRIPTOR | DOOR_RELEASE;
+ descp = &desc;
+ ndesc = 1;
+
+ new.cmd = cmd;
+ new.new_len = 0;
+ newp = &new;
+ buflen = sizeof (ike_new_t);
+
+ } else if ((argc > 1) && (cmd == IKE_SVC_NEW_PS)) {
+ /*
+ * This is an alternative to using the tmpfile method
+ * for preshared keys. It means we're duplicating the
+ * parsing effort that happens in readps.c; but it
+ * does avoid having the key sitting in a file.
+ */
+ ike_ps_t *psp;
+ int pslen;
+
+ /*
+ * must be in interactive mode; don't want keys in
+ * the process args.
+ */
+ if (!interactive)
+ Bail("Must be in interactive mode to add key info.");
+ if (parse_ps(argc, argv, &psp, &pslen) < 0) {
+ errno = 0;
+ Bail("invalid preshared key definition");
+ }
+ newp = malloc(sizeof (ike_new_t) + pslen);
+ if (newp == NULL)
+ Bail("alloc pskey");
+ newp->cmd = cmd;
+ newp->new_len = sizeof (ike_new_t) + pslen;
+ (void) memcpy((char *)(newp + 1), psp, pslen);
+ buflen = newp->new_len;
+ /* parse_ps allocated the ike_ps_t buffer; free it now */
+ free(psp);
+
+ } else if ((argc > 1) && (cmd == IKE_SVC_NEW_RULE)) {
+ /*
+ * We've been given the item in argv. However, parsing
+ * rules can get more than a little messy, and in.iked
+ * already has a great parser for this stuff! So don't
+ * fool around with trying to do the parsing here. Just
+ * write it out to a tempfile, and send the fd to in.iked.
+ *
+ * We could conceivably do this for preshared keys,
+ * rather than duplicating the parsing effort; but that
+ * would mean the key would be written out to a file,
+ * which isn't such a good idea.
+ */
+ boolean_t doquotes = B_FALSE;
+ int rtn;
+
+ if ((argv[0][0] != '{') ||
+ (argv[argc - 1][strlen(argv[argc - 1]) - 1] != '}'))
+ bail_msg(gettext("improperly formatted %s"), name);
+
+ /* attempt to use a fairly unpredictable file name... */
+ (void) sprintf(tmpfilepath, "/var/run/%x", (int)gethrtime());
+ fd = open(tmpfilepath, O_RDWR | O_CREAT | O_EXCL,
+ S_IRUSR | S_IWUSR);
+ if (fd < 0)
+ Bail("cannot open tmpfile");
+
+ /* and make it inaccessible asap */
+ if (unlink(tmpfilepath) < 0) {
+ (void) close(fd);
+ Bail("tmpfile error");
+ }
+
+ tmpfile = fdopen(fd, "w");
+ if (tmpfile == NULL) {
+ (void) close(fd);
+ Bail("cannot write to tmpfile");
+ }
+
+ for (i = 0; i < argc; i++) {
+ /*
+ * We have to do some gyrations with our string here,
+ * to properly handle quotes. There are two issues:
+ * - some of the fields of a rule may have embedded
+ * whitespace, and thus must be quoted on the cmd
+ * line. The shell removes the quotes, and gives
+ * us a single argv string; but we need to put the
+ * quotes back in when we write the string out to
+ * file. The doquotes boolean is set when we
+ * process a keyword which will be followed by a
+ * string value (so the NEXT argv element will be
+ * quoted).
+ * - there might be a quote character in a field,
+ * that was escaped on the cmdline. The shell
+ * removes the escape char, and leaves the quote
+ * in the string it gives us. We need to put the
+ * escape char back in before writing to file.
+ */
+ char field[MAXLINESIZE];
+ if (!escapequotes(field, argv[i], MAXLINESIZE))
+ Bail("write to tmpfile failed (arg too big)");
+ if (doquotes) {
+ rtn = fprintf(tmpfile, "\"%s\"\n", field);
+ doquotes = B_FALSE;
+ } else {
+ rtn = fprintf(tmpfile, "%s\n", field);
+ }
+ if (rtn < 0)
+ Bail("write to tmpfile failed");
+ /*
+ * check if this is a keyword identifying
+ * a field that needs to be quoted.
+ */
+ doquotes = quotedfield(argv[i]);
+ }
+ if (fflush(tmpfile) == EOF)
+ Bail("write to tmpfile failed");
+ /* rewind so that the daemon will get the beginning */
+ rewind(tmpfile);
+
+ desc.d_data.d_desc.d_descriptor = fd;
+ desc.d_attributes = DOOR_DESCRIPTOR | DOOR_RELEASE;
+ descp = &desc;
+ ndesc = 1;
+
+ new.cmd = cmd;
+ new.new_len = 0;
+ newp = &new;
+ buflen = sizeof (ike_new_t);
+
+ } else {
+ /* not enough information! */
+ bail_msg(gettext("missing %s description or file name"), name);
+ }
+
+ rtn = ikedoor_call((char *)newp, buflen, descp, ndesc);
+
+ if ((rtn == NULL) || (rtn->svc_err.cmd == IKE_SVC_ERROR)) {
+ ikeadm_err_msg(&rtn->svc_err,
+ gettext("error creating new %s"), name);
+ } else {
+ message(gettext("Successfully created new %s."), name);
+ }
+}
+
+static void
+do_flush(int cmd)
+{
+ ike_service_t *rtnp;
+ ike_flush_t flush;
+
+ if (cmd != IKE_SVC_FLUSH_P1S) {
+ bail_msg(gettext("unrecognized flush command (%d)."), cmd);
+ }
+
+ flush.cmd = cmd;
+
+ rtnp = ikedoor_call((char *)&flush, sizeof (ike_flush_t), NULL, 0);
+ if ((rtnp == NULL) || (rtnp->svc_err.cmd == IKE_SVC_ERROR)) {
+ ikeadm_err_exit(&rtnp->svc_err, gettext("error doing flush"));
+ }
+ message(gettext("Successfully flushed P1 SAs."));
+}
+
+static void
+do_rw(int cmd, int argc, char **argv)
+{
+ ike_service_t *rtnp;
+ ike_rw_t rw;
+ door_desc_t desc, *descp = NULL;
+ int oflag, omode, fd, ndesc = 0;
+ char *op, *obj = NULL;
+ boolean_t writing = B_FALSE;
+
+ switch (cmd) {
+ case IKE_SVC_READ_PS:
+ obj = gettext("preshared key");
+ /* FALLTHRU */
+ case IKE_SVC_READ_RULES:
+ if (obj == NULL)
+ obj = gettext("policy rule");
+ op = gettext("read");
+ oflag = O_RDONLY;
+ omode = 0;
+ break;
+
+ case IKE_SVC_WRITE_PS:
+ obj = gettext("preshared key");
+ /* FALLTHRU */
+ case IKE_SVC_WRITE_RULES:
+ if (obj == NULL)
+ obj = gettext("policy rule");
+ op = gettext("write");
+ oflag = O_RDWR | O_CREAT | O_EXCL;
+ omode = S_IRUSR | S_IWUSR;
+
+ /* for write commands, dest location must be specified */
+ if (argc < 1) {
+ bail_msg(gettext("destination location required "
+ "to write %ss"), obj);
+ }
+ writing = B_TRUE;
+ break;
+
+ default:
+ bail_msg(gettext("unrecognized read/write command (%d)."), cmd);
+ }
+
+ rw.cmd = cmd;
+
+ if (argc >= 1) {
+ rw.rw_loc = IKE_RW_LOC_USER_SPEC;
+ fd = open(argv[0], oflag, omode);
+ if (fd < 0)
+ Bail("open user-specified file");
+
+ desc.d_data.d_desc.d_descriptor = fd;
+ desc.d_attributes = DOOR_DESCRIPTOR | DOOR_RELEASE;
+ descp = &desc;
+ ndesc = 1;
+ } else {
+ rw.rw_loc = IKE_RW_LOC_DEFAULT;
+ }
+
+ rtnp = ikedoor_call((char *)&rw, sizeof (ike_rw_t), descp, ndesc);
+ if ((rtnp == NULL) || (rtnp->svc_err.cmd == IKE_SVC_ERROR)) {
+ /*
+ * Need to remove the target file in the
+ * case of a failed write command.
+ */
+ if (writing) {
+ /*
+ * argv[0] must be valid if we're writing; we
+ * exit before setting this boolean if not.
+ */
+ (void) unlink(argv[0]);
+ (void) close(fd);
+
+ if ((rtnp != NULL) &&
+ (rtnp->svc_err.ike_err == IKE_ERR_NO_OBJ)) {
+ message(gettext("No %s information to write."),
+ obj);
+ return;
+ }
+ }
+ ikeadm_err_exit(&rtnp->svc_err, gettext("error doing %s"), op);
+ }
+ message(gettext("Completed %s of %s configuration information."),
+ op, obj);
+}
+
+static void
+do_rbdump()
+{
+ ike_cmd_t req;
+ ike_service_t *rtnp;
+
+ req.cmd = IKE_SVC_DBG_RBDUMP;
+
+ rtnp = ikedoor_call((char *)&req, sizeof (ike_cmd_t), NULL, 0);
+ if ((rtnp == NULL) || (rtnp->svc_err.cmd == IKE_SVC_ERROR)) {
+ ikeadm_err_exit(&rtnp->svc_err, gettext("error doing flush"));
+ }
+ message(gettext("Successfully dumped rulebase; check iked dbg"));
+}
+
+#define REQ_ARG_CNT 1
+
+static void
+parseit(int argc, char **argv)
+{
+ int cmd, cmd_obj_args = 1;
+ char *cmdstr, *objstr;
+
+ if (interactive) {
+ if (argc == 0)
+ return;
+ }
+
+ if (argc < REQ_ARG_CNT) {
+ usage();
+ }
+
+ cmdstr = argv[0];
+ if (argc > REQ_ARG_CNT) {
+ cmd_obj_args++;
+ objstr = argv[1];
+ } else {
+ objstr = NULL;
+ }
+ cmd = parsecmd(cmdstr, objstr);
+
+ /* skip over args specifying command/object */
+ argc -= cmd_obj_args;
+ argv += cmd_obj_args;
+
+ switch (cmd) {
+ case IKE_SVC_GET_DBG:
+ case IKE_SVC_GET_PRIV:
+ do_getvar(cmd);
+ break;
+ case IKE_SVC_GET_STATS:
+ do_getstats(cmd);
+ break;
+ case IKE_SVC_SET_DBG:
+ case IKE_SVC_SET_PRIV:
+ do_setvar(cmd, argc, argv);
+ break;
+ case IKE_SVC_DUMP_P1S:
+ case IKE_SVC_DUMP_RULES:
+ case IKE_SVC_DUMP_PS:
+ do_dump(cmd);
+ break;
+ case IKE_SVC_GET_P1:
+ case IKE_SVC_GET_RULE:
+ case IKE_SVC_GET_PS:
+ case IKE_SVC_DEL_P1:
+ case IKE_SVC_DEL_RULE:
+ case IKE_SVC_DEL_PS:
+ do_getdel(cmd, argc, argv);
+ break;
+ case IKE_SVC_NEW_RULE:
+ case IKE_SVC_NEW_PS:
+ do_new(cmd, argc, argv);
+ break;
+ case IKE_SVC_FLUSH_P1S:
+ do_flush(cmd);
+ break;
+ case IKE_SVC_READ_RULES:
+ case IKE_SVC_READ_PS:
+ case IKE_SVC_WRITE_RULES:
+ case IKE_SVC_WRITE_PS:
+ do_rw(cmd, argc, argv);
+ break;
+ case IKEADM_HELP_GENERAL:
+ print_help();
+ break;
+ case IKEADM_HELP_GET:
+ print_get_help();
+ break;
+ case IKEADM_HELP_SET:
+ print_set_help();
+ break;
+ case IKEADM_HELP_ADD:
+ print_add_help();
+ break;
+ case IKEADM_HELP_DEL:
+ print_del_help();
+ break;
+ case IKEADM_HELP_DUMP:
+ print_dump_help();
+ break;
+ case IKEADM_HELP_FLUSH:
+ print_flush_help();
+ break;
+ case IKEADM_HELP_READ:
+ print_read_help();
+ break;
+ case IKEADM_HELP_WRITE:
+ print_write_help();
+ break;
+ case IKEADM_HELP_HELP:
+ print_help_help();
+ break;
+ case IKEADM_EXIT:
+ if (interactive)
+ exit(0);
+ break;
+ case IKE_SVC_DBG_RBDUMP:
+ do_rbdump();
+ break;
+ case IKE_SVC_ERROR:
+ usage();
+ default:
+ exit(0);
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ char ch;
+
+ (void) setlocale(LC_ALL, "");
+#if !defined(TEXT_DOMAIN)
+#define TEXT_DOMAIN "SYS_TEST"
+#endif
+ (void) textdomain(TEXT_DOMAIN);
+
+ while ((ch = getopt(argc, argv, "hpn")) != EOF) {
+ switch (ch) {
+ case 'h':
+ print_help();
+ return (0);
+ case 'p':
+ pflag = B_TRUE;
+ break;
+ case 'n':
+ nflag = B_TRUE;
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (open_door() < 0)
+ Bail("failed to open door");
+
+ if (*argv == NULL) {
+ /* no cmd-line args, do interactive mode */
+ do_interactive(stdin, "ikeadm> ", parseit);
+ }
+
+ parseit(argc, argv);
+
+ return (0);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/ikecert.sh b/usr/src/cmd/cmd-inet/usr.sbin/ikecert.sh
new file mode 100644
index 0000000000..0da3df551d
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/ikecert.sh
@@ -0,0 +1,60 @@
+#!/usr/bin/pfsh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2001, 2003 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# ikecert is a wrapper to three programs in /usr/lib/inet which do the heavy
+# lifting for IKE certificate database management (i.e. the files in
+# /etc/inet/ike/publickeys, /etc/inet/secret/ike.privatekeys, and
+# /etc/inet/ike/crls).
+#
+
+case "$1" in
+'certdb')
+ # Okay! We're good to go.
+ ;;
+'certrldb')
+ # Okay! We're good to go.
+ ;;
+'certlocal')
+ # Okay! We're good to go.
+ ;;
+'tokens')
+ # Execute the special options of certlocal.
+ exec /usr/lib/inet/certlocal -L
+ ;;
+
+*)
+ echo "Usage: ikecert { certdb | certrldb | certlocal | tokens }" \
+ "<options> "
+ echo " Use '-h' after one of the cert-commands for more details."
+ exit 1
+ ;;
+esac
+
+exec /usr/lib/inet/"$@"
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.comsat.c b/usr/src/cmd/cmd-inet/usr.sbin/in.comsat.c
new file mode 100644
index 0000000000..1ea82b80e4
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.comsat.c
@@ -0,0 +1,461 @@
+/*
+ * Copyright 1998 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T
+ * All Rights Reserved.
+ */
+
+/*
+ * Copyright (c) 1980 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted provided
+ * that: (1) source distributions retain this entire copyright notice and
+ * comment, and (2) distributions including binaries display the following
+ * acknowledgement: ``This product includes software developed by the
+ * University of California, Berkeley and its contributors'' in the
+ * documentation or other materials provided with the distribution and in
+ * all advertising materials mentioning features or use of this software.
+ * Neither the name of the University nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/file.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <string.h>
+
+#include <netinet/in.h>
+
+#include <stdio.h>
+#include <sys/ttold.h>
+#include <utmpx.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/param.h> /* for MAXHOSTNAMELEN */
+#include <netdb.h>
+#include <syslog.h>
+#include <sys/ioctl.h>
+#include <pwd.h>
+
+/*
+ * comsat
+ */
+
+
+#ifndef UTMPX_FILE
+#define UTMPX_FILE "/etc/utmpx"
+#endif /* UTMPX_FILE */
+
+int debug = 0;
+#define dsyslog if (debug) syslog
+
+struct sockaddr_in sin = { AF_INET };
+extern errno;
+
+char hostname[MAXHOSTNAMELEN];
+struct utmpx *utmp = NULL;
+int nutmp;
+int uf;
+unsigned utmpmtime = 0; /* last modification time for utmp */
+unsigned utmpsize = 0; /* last malloced size for utmp */
+int onalrm();
+time_t lastmsgtime;
+
+#ifndef SYSV
+int reapchildren();
+#else
+
+#define rindex strrchr
+#define index strchr
+#define signal(s, f) sigset((s), (f))
+
+#ifndef sigmask
+#define sigmask(m) (1 << ((m)-1))
+#endif
+
+#define set2mask(setp) ((setp)->__sigbits[0])
+#define mask2set(mask, setp) \
+ ((mask) == -1 ? sigfillset(setp) : (((setp)->__sigbits[0]) = (mask)))
+
+static sigsetmask(mask)
+int mask;
+{
+ sigset_t oset;
+ sigset_t nset;
+
+ (void) sigprocmask(0, (sigset_t *)0, &nset);
+ mask2set(mask, &nset);
+ (void) sigprocmask(SIG_SETMASK, &nset, &oset);
+ return (set2mask(&oset));
+}
+
+static sigblock(mask)
+int mask;
+{
+ sigset_t oset;
+ sigset_t nset;
+
+ (void) sigprocmask(0, (sigset_t *)0, &nset);
+ mask2set(mask, &nset);
+ (void) sigprocmask(SIG_BLOCK, &nset, &oset);
+ return (set2mask(&oset));
+}
+
+#endif /* SYSV */
+
+
+#define MAXIDLE 120
+#define NAMLEN (sizeof (uts[0].ut_name) + 1)
+
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ register int cc;
+ char buf[BUFSIZ];
+ char msgbuf[100];
+ struct sockaddr_in from;
+ socklen_t fromlen;
+ int c;
+ extern int optind;
+ extern int getopt();
+ extern char *optarg;
+
+ openlog("comsat", 0, LOG_DAEMON);
+
+ while ((c = getopt(argc, argv, "d")) != -1) {
+ switch ((char)c) {
+ case'd':
+ debug++;
+ break;
+ default:
+ syslog(LOG_ERR, "invalid argument %s", argv[optind]);
+ exit(1);
+ }
+ }
+
+ /* verify proper invocation */
+ fromlen = (socklen_t)sizeof (from);
+ if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0) {
+ fprintf(stderr, "%s: ", argv[0]);
+ perror("getsockname");
+ _exit(1);
+ }
+
+#ifdef SYSV
+ chdir("/var/mail");
+#else
+ chdir("/var/spool/mail");
+#endif /* SYSV */
+ if ((uf = open(UTMPX_FILE, 0)) < 0) {
+ syslog(LOG_ERR, "%s: %m", UTMPX_FILE);
+ (void) recv(0, msgbuf, sizeof (msgbuf) - 1, 0);
+ exit(1);
+ }
+ (void) time(&lastmsgtime);
+ (void) gethostname(hostname, sizeof (hostname));
+ onalrm();
+ (void) signal(SIGALRM, (void (*)())onalrm);
+ (void) signal(SIGTTOU, SIG_IGN);
+#ifndef SYSV
+ (void) signal(SIGCHLD, reapchildren);
+#else
+ (void) signal(SIGCHLD, SIG_IGN); /* no zombies */
+#endif /* SYSV */
+ for (;;) {
+ cc = recv(0, msgbuf, sizeof (msgbuf) - 1, 0);
+ if (cc <= 0) {
+ if (errno != EINTR)
+ sleep(1);
+ errno = 0;
+ continue;
+ }
+ if (nutmp == 0) /* no users (yet) */
+ continue;
+ sigblock(sigmask(SIGALRM));
+ msgbuf[cc] = 0;
+ (void) time(&lastmsgtime);
+ mailfor(msgbuf);
+ sigsetmask(0);
+ }
+}
+
+#ifndef SYSV
+reapchildren()
+{
+
+ while (wait3((struct wait *)0, WNOHANG, (struct rusage *)0) > 0)
+ ;
+}
+#endif /* SYSV */
+
+onalrm()
+{
+ struct stat statbf;
+ time_t now;
+
+ (void) time(&now);
+ if ((ulong_t)now - (ulong_t)lastmsgtime >= MAXIDLE)
+ exit(0);
+ dsyslog(LOG_DEBUG, "alarm\n");
+ alarm(15);
+ fstat(uf, &statbf);
+ if (statbf.st_mtime > utmpmtime) {
+ dsyslog(LOG_DEBUG, " changed\n");
+ utmpmtime = statbf.st_mtime;
+ if (statbf.st_size > utmpsize) {
+ utmpsize = statbf.st_size + 10 * sizeof (struct utmpx);
+ if (utmp)
+ utmp = (struct utmpx *)realloc(utmp, utmpsize);
+ else
+ utmp = (struct utmpx *)malloc(utmpsize);
+ if (! utmp) {
+ dsyslog(LOG_DEBUG, "malloc failed\n");
+ exit(1);
+ }
+ }
+ lseek(uf, 0, 0);
+ nutmp = read(uf, utmp, statbf.st_size)/sizeof (struct utmpx);
+ } else
+ dsyslog(LOG_DEBUG, " ok\n");
+}
+
+mailfor(name)
+char *name;
+{
+ struct utmpx *utp = &utmp[nutmp];
+ register char *cp;
+ char *rindex();
+ int offset;
+
+ /*
+ * Don't bother doing anything if nobody is
+ * logged into the system.
+ */
+ if (utmp == NULL || nutmp == 0)
+ return;
+ dsyslog(LOG_DEBUG, "mailfor %s\n", name);
+ cp = name;
+ while (*cp && *cp != '@')
+ cp++;
+ if (*cp == 0) {
+ dsyslog(LOG_DEBUG, "bad format\n");
+ return;
+ }
+ *cp = 0;
+ offset = atoi(cp+1);
+ while (--utp >= utmp)
+ if ((utp->ut_type == USER_PROCESS) &&
+ (!strncmp(utp->ut_name, name, sizeof (utmp[0].ut_name))))
+ notify(utp, offset);
+}
+
+char *cr;
+
+notify(utp, offset)
+struct utmpx *utp;
+{
+ FILE *tp;
+ struct sgttyb gttybuf;
+ char tty[sizeof (utmp[0].ut_line) + 5];
+ char name[sizeof (utmp[0].ut_name) + 1];
+ struct stat stb, stl;
+ time_t timep[2];
+ struct passwd *pwd;
+ int fd, mbox;
+
+
+ strcpy(tty, "/dev/");
+ strncat(tty, utp->ut_line, sizeof (utp->ut_line));
+ dsyslog(LOG_DEBUG, "notify %s on %s\n", utp->ut_name, tty);
+ if (stat(tty, &stb) == -1) {
+ dsyslog(LOG_DEBUG, "can't stat tty\n");
+ return;
+ }
+ if ((stb.st_mode & 0100) == 0) {
+ dsyslog(LOG_DEBUG, "wrong mode\n");
+ return;
+ }
+ if (fork())
+ return;
+ signal(SIGALRM, SIG_DFL);
+ alarm(30);
+
+ strncpy(name, utp->ut_name, sizeof (utp->ut_name));
+ name[sizeof (name) - 1] = '\0';
+
+ /*
+ * Do all operations that check protections as the user who
+ * will be getting the biff.
+ */
+ if ((pwd = getpwnam(name)) == (struct passwd *)-1) {
+ dsyslog(LOG_DEBUG, "getpwnam failed\n");
+ exit(1);
+ }
+ if (setuid(pwd->pw_uid) == -1) {
+ dsyslog(LOG_DEBUG, "setuid failed\n");
+ exit(1);
+ }
+
+ /*
+ * We need to make sure that the tty listed in the utmp
+ * file really is a tty device so that a corrupted utmp
+ * file doesn't cause us to over-write a real file.
+ */
+ if ((fd = open(tty, O_RDWR)) == -1) {
+ dsyslog(LOG_DEBUG, "can't open tty");
+ exit(1);
+ }
+ if (isatty(fd) == 0) {
+ dsyslog(LOG_DEBUG, "line listed in utmp file is not a tty\n");
+ exit(1);
+ }
+
+ /*
+ * For the case where the user getting the biff is root,
+ * we need to make sure that the tty we will be sending
+ * the biff to is also owned by root.
+ *
+ * Check after open, to prevent race on open.
+ */
+
+ if (fstat(fd, &stb) != 0 || stb.st_uid != pwd->pw_uid) {
+ dsyslog(LOG_DEBUG,
+ "tty is not owned by user getting the biff\n");
+ exit(1);
+ }
+
+ /*
+ * Prevent race by doing fdopen on fd, not fopen
+ * Fopen opens w/ O_CREAT, which is dangerous too
+ */
+ if ((tp = fdopen(fd, "w")) == 0) {
+ dsyslog(LOG_DEBUG, "fdopen failed\n");
+ exit(-1);
+ }
+
+ if (ioctl(fd, TIOCGETP, &gttybuf) == -1) {
+ dsyslog(LOG_DEBUG, "ioctl TIOCGETP failed\n");
+ exit(1);
+ }
+ cr = (gttybuf.sg_flags&CRMOD) && !(gttybuf.sg_flags&RAW) ? "" : "\r";
+ fprintf(tp, "%s\n\007New mail for %s@%.*s\007 has arrived:%s\n",
+ cr, name, sizeof (hostname), hostname, cr);
+ fprintf(tp, "----%s\n", cr);
+
+ if ((mbox = open(name, O_RDONLY)) == -1) {
+ dsyslog(LOG_DEBUG, "can't open mailbox for %s", name);
+ exit(1);
+ }
+ /*
+ * In case of a worldwritable mail spool directory, we must take
+ * care we don't open and read from the wrong file.
+ */
+ if (fstat(mbox, &stb) == -1 || lstat(name, &stl) == -1) {
+ dsyslog(LOG_DEBUG, "stat() failed on mail file\n");
+ exit(1);
+ }
+
+ /*
+ * Here we make sure that the file wasn't a hardlink or softlink
+ * while we opened it and that it wasn't changed afterwards
+ */
+ if (!S_ISREG(stl.st_mode) ||
+ stl.st_dev != stb.st_dev ||
+ stl.st_ino != stb.st_ino ||
+ stl.st_uid != pwd->pw_uid ||
+ stb.st_nlink != 1) {
+ dsyslog(LOG_DEBUG, "mail spool file must be plain file\n");
+ exit(1);
+ }
+
+ timep[0] = stb.st_atime;
+ timep[1] = stb.st_mtime;
+ jkfprintf(tp, name, mbox, offset);
+ utime(name, timep);
+ exit(0);
+}
+
+jkfprintf(tp, name, mbox, offset)
+register FILE *tp;
+char *name;
+int mbox;
+{
+ register FILE *fi;
+ register int linecnt, charcnt;
+ char line[BUFSIZ];
+ int inheader;
+
+ dsyslog(LOG_DEBUG, "HERE %s's mail starting at %d\n",
+ name, offset);
+ if ((fi = fdopen(mbox, "r")) == NULL) {
+ dsyslog(LOG_DEBUG, "Cant read the mail\n");
+ return;
+ }
+
+ fseek(fi, offset, L_SET);
+
+ /*
+ * Print the first 7 lines or 560 characters of the new mail
+ * (whichever comes first). Skip header crap other than
+ * From, Subject, To, and Date.
+ */
+ linecnt = 7;
+ charcnt = 560;
+ inheader = 1;
+
+
+ while (fgets(line, sizeof (line), fi) != NULL) {
+ register char *cp;
+ char *index();
+ int cnt;
+ int i;
+
+ if (linecnt <= 0 || charcnt <= 0) {
+ fprintf(tp, "...more...%s\n", cr);
+ return;
+ }
+ if (strncmp(line, "From ", 5) == 0)
+ continue;
+ if (inheader && (line[0] == ' ' || line[0] == '\t'))
+ continue;
+ cp = index(line, ':');
+ if (cp == 0 || (index(line, ' ') && index(line, ' ') < cp))
+ inheader = 0;
+ else
+ cnt = cp - line;
+ if (inheader &&
+ strncmp(line, "Date", cnt) &&
+ strncmp(line, "From", cnt) &&
+ strncmp(line, "Subject", cnt) &&
+ strncmp(line, "To", cnt))
+ continue;
+ cp = index(line, '\n');
+ if (cp)
+ *cp = '\0';
+
+ for (i = strlen(line); i-- > 0; )
+ if (!isprint(line[i]))
+ line[i] = ' ';
+
+
+ fprintf(tp, "%s%s\n", line, cr);
+ linecnt--, charcnt -= strlen(line);
+ }
+ fprintf(tp, "----%s\n", cr);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.fingerd.c b/usr/src/cmd/cmd-inet/usr.sbin/in.fingerd.c
new file mode 100644
index 0000000000..4b628e7a5c
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.fingerd.c
@@ -0,0 +1,147 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1989 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * Portions of this source code were derived from Berkeley 4.3 BSD
+ * under license from the Regents of the University of California.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Finger server.
+ */
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#define MAXARGS 10
+
+main(argc, argv)
+ char *argv[];
+{
+ register char *sp;
+ char line[512];
+ struct sockaddr_storage sin;
+ pid_t pid, w;
+ int i, p[2], status;
+ FILE *fp;
+ char *av[MAXARGS + 1];
+
+ i = sizeof (sin);
+ if (getpeername(0, (struct sockaddr *)&sin, &i) < 0)
+ fatal(argv[0], "getpeername");
+ line[0] = '\0';
+ if (fgets(line, sizeof (line), stdin) == NULL)
+ exit(1);
+ sp = line;
+ av[0] = "finger";
+ i = 1;
+
+ /* skip past leading white space */
+ while (isspace(*sp))
+ sp++;
+
+ /*
+ * The finger protocol says a "/W" switch means verbose output.
+ * We explicitly set either the "long" or "short" output flags
+ * to the finger program so that we don't have to know what what
+ * the "finger" program's default is.
+ */
+ if (*sp == '/' && (sp[1] == 'W' || sp[1] == 'w')) {
+ sp += 2;
+ av[i++] = "-l";
+ } else {
+ av[i++] = "-s";
+ }
+
+ /* look for username arguments */
+ while (i < MAXARGS) {
+
+ /* skip over leading white space */
+ while (isspace(*sp))
+ sp++;
+
+ /* check for end of "command line" */
+ if (*sp == '\0')
+ break;
+
+ /* pick up another name argument */
+ av[i++] = sp;
+ while ((*sp != '\0') && !isspace(*sp))
+ sp++;
+
+ /* check again for end of "command line" */
+ if (*sp == '\0')
+ break;
+ else
+ *sp++ = '\0';
+ }
+
+ av[i] = (char *)0;
+ if (pipe(p) < 0)
+ fatal(argv[0], "pipe");
+
+ if ((pid = fork()) == 0) {
+ close(p[0]);
+ if (p[1] != 1) {
+ dup2(p[1], 1);
+ close(p[1]);
+ }
+ execv("/usr/bin/finger", av);
+ printf("No local finger program found\n");
+ fflush(stdout);
+ _exit(1);
+ }
+ if (pid == (pid_t)-1)
+ fatal(argv[0], "fork");
+ close(p[1]);
+ if ((fp = fdopen(p[0], "r")) == NULL)
+ fatal(argv[0], "fdopen");
+ while ((i = getc(fp)) != EOF) {
+ if (i == '\n')
+ putchar('\r');
+ putchar(i);
+ }
+ fclose(fp);
+ while ((w = wait(&status)) != pid && w != (pid_t)-1)
+ ;
+ return (0);
+}
+
+fatal(prog, s)
+ char *prog, *s;
+{
+
+ fprintf(stderr, "%s: ", prog);
+ perror(s);
+ exit(1);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/COPYRIGHT.c b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/COPYRIGHT.c
new file mode 100644
index 0000000000..694914f1a4
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/COPYRIGHT.c
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2001-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/****************************************************************************
+
+ Copyright (c) 1999,2000 WU-FTPD Development Group.
+ All rights reserved.
+
+ Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994
+ The Regents of the University of California.
+ Portions Copyright (c) 1993, 1994 Washington University in Saint Louis.
+ Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc.
+ Portions Copyright (c) 1989 Massachusetts Institute of Technology.
+ Portions Copyright (c) 1998 Sendmail, Inc.
+ Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P. Allman.
+ Portions Copyright (c) 1997 by Stan Barber.
+ Portions Copyright (c) 1997 by Kent Landfield.
+ Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ Free Software Foundation, Inc.
+
+ Use and distribution of this software and its source code are governed
+ by the terms and conditions of the WU-FTPD Software License ("LICENSE").
+
+ If you did not receive a copy of the license, it may be obtained online
+ at http://www.wu-ftpd.org/license.html.
+
+ $Id: COPYRIGHT.c,v 1.7 2000/07/01 18:46:31 wuftpd Exp $
+
+****************************************************************************/
+
+#include <stdio.h>
+
+void print_copyright(void);
+extern char version[];
+
+char *Copyright = "\n\
+ Copyright (c) 1999,2000 WU-FTPD Development Group.\n\
+ All rights reserved.\n\
+\n\
+ Copyright 2001-2003 Sun Microsystems, Inc. All rights reserved.\n\
+ Use is subject to license terms.\n\
+\n\
+ Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994\n\
+ The Regents of the University of California.\n\
+ Portions Copyright (c) 1993, 1994 Washington University in Saint Louis.\n\
+ Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc.\n\
+ Portions Copyright (c) 1989 Massachusetts Institute of Technology.\n\
+ Portions Copyright (c) 1998 Sendmail, Inc.\n\
+ Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P. Allman.\n\
+ Portions Copyright (c) 1997 by Stan Barber.\n\
+ Portions Copyright (c) 1997 by Kent Landfield.\n\
+ Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997\n\
+ Free Software Foundation, Inc. \n\
+\n\
+ Use and distribution of this software and its source code are governed \n\
+ by the terms and conditions of the WU-FTPD Software License (\"LICENSE\").\n\
+\n\
+ If you did not receive a copy of the license, it may be obtained online\n\
+ at http://www.wu-ftpd.org/license.html.\n";
+
+void print_copyright(void)
+{
+ printf("%s\n", Copyright);
+ printf("%s\n", version);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/Makefile b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/Makefile
new file mode 100644
index 0000000000..099577dde1
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/Makefile
@@ -0,0 +1,116 @@
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../../../Makefile.cmd
+
+PROG= in.ftpd ftpcount ftpshut ftprestart privatepw
+MANIFEST= ftp.xml
+SCRIPTS= ftpaddhost ftpconfig
+COMMON_OBJS= COPYRIGHT.o vers.o
+FTPD_OBJS= $(COMMON_OBJS) ftpd.o ftpcmd.o glob.o logwtmp.o popen.o \
+ access.o extensions.o realpath.o acl.o private.o \
+ authenticate.o conversions.o rdservers.o paths.o hostacc.o \
+ routevector.o restrict.o domain.o wu_fnmatch.o timeout.o \
+ getpwnam.o strsep.o strcasestr.o inet.o xferlog.o gssutil.o \
+ privs.o
+FTPCOUNT_OBJS= $(COMMON_OBJS) ftpcount.o rdservers.o inet.o
+FTPSHUT_OBJS= $(COMMON_OBJS) ftpshut.o rdservers.o inet.o
+FTPREST_OBJS= $(COMMON_OBJS) ftprestart.o rdservers.o inet.o
+CKCONFIG_OBJS= $(COMMON_OBJS) ckconfig.o rdservers.o inet.o
+PRIVATE_OBJS= $(COMMON_OBJS) privatepw.o
+OBJS= $(FTPD_OBJS) ftpcount.o ftpshut.o ftprestart.o ckconfig.o \
+ privatepw.o
+SRCS= $(OBJS:%.o=%.c)
+CONFIGFILES= ftpaccess ftpconversions ftpgroups ftphosts ftpservers ftpusers
+ETCFTPDDIR= $(ROOTETC)/ftpd
+ETCFTPDFILES= $(CONFIGFILES:%=$(ETCFTPDDIR)/%)
+$(ETCFTPDFILES):= FILEMODE= 0644
+$(ETCFTPDFILES):= GROUP= sys
+ROOTUSRSBINSCRIPTS= $(SCRIPTS:%=$(ROOTUSRSBIN)/%)
+ROOTFTPCOUNT= $(ROOTUSRSBIN)/ftpcount
+ROOTFTPWHO= $(ROOTUSRSBIN)/ftpwho
+
+ROOTMANIFESTDIR= $(ROOTSVCNETWORK)
+$(ROOTMANIFEST) := FILEMODE= 444
+
+# I18n
+POFILE= in.ftpd_all.po
+POFILES= $(SCRIPTS:%=%.po)
+
+# When building for Solaris 8 add to CPPFLAGS:
+# -Ddn_skipname=__dn_skipname -DSOLARIS_NO_AUDIT_FTPD_LOGOUT
+CLOBBERFILES += ckconfig ftpwho $(SCRIPTS)
+CPPFLAGS += -DBSD_COMP -D_FILE_OFFSET_BITS=64 -DINET6 -DSOLARIS_BSM_AUDIT \
+ -DSOLARIS_ETC_FTPUSERS -DSENDFILE -DCLOSEFROM -DUSE_GSS \
+ -DSOLARIS_GSS_USEROK -DSOLARIS_PRIVS
+LDLIBS += -lsocket -lnsl -lpam -lbsm -lsendfile -lgss
+YFLAGS += -d
+
+# tcov output relies on atexit(3C) registered functions being called, so stop
+# _exit() from being used. Passing -l to yacc stops it generating #line
+# directives which don't work with tcov.
+tcov := CFLAGS += -xa
+tcov := CPPFLAGS += -D_exit=exit
+tcov := YFLAGS += -l
+
+.KEEP_STATE:
+
+all tcov: $(PROG) ckconfig ftpwho $(CONFIGFILES) $(SCRIPTS)
+
+in.ftpd: $(FTPD_OBJS)
+ $(LINK.c) $(FTPD_OBJS) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+ftpcount: $(FTPCOUNT_OBJS)
+ $(LINK.c) $(FTPCOUNT_OBJS) -o $@ -lsocket -lnsl
+ $(POST_PROCESS)
+
+ftpwho: ftpcount
+ $(RM) $@
+ $(LN) ftpcount $@
+
+ftpshut: $(FTPSHUT_OBJS)
+ $(LINK.c) $(FTPSHUT_OBJS) -o $@ -lsocket -lnsl
+ $(POST_PROCESS)
+
+ftprestart: $(FTPREST_OBJS)
+ $(LINK.c) $(FTPREST_OBJS) -o $@ -lsocket -lnsl
+ $(POST_PROCESS)
+
+ckconfig: $(CKCONFIG_OBJS)
+ $(LINK.c) $(CKCONFIG_OBJS) -o $@ -lsocket -lnsl
+ $(POST_PROCESS)
+
+privatepw: $(PRIVATE_OBJS)
+ $(LINK.c) $(PRIVATE_OBJS) -o $@
+ $(POST_PROCESS)
+
+# This causes y.tab.c to be renamed to ftpcmd.c, needed by tcov.
+ftpcmd.c: ftpcmd.y
+
+$(ETCFTPDDIR)/%: %
+ $(INS.file)
+
+$(POFILE): $(POFILES)
+ $(RM) $@
+ cat $(POFILES) >$@
+
+$(ROOTFTPWHO): $(ROOTFTPCOUNT)
+ $(RM) $@
+ $(LN) $(ROOTFTPCOUNT) $@
+
+install: all $(ROOTUSRSBINPROG) $(ROOTFTPWHO) $(ROOTUSRSBINSCRIPTS) \
+ $(ETCFTPDFILES) $(ROOTMANIFEST)
+
+check: $(CHKMANIFEST)
+
+clean:
+ $(RM) $(OBJS) *.d *.tcov y.tab.h y.tab.c ftpcmd.c
+
+lint: lint_SRCS
+
+include ../../../Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/access.c b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/access.c
new file mode 100644
index 0000000000..f8cf5f795f
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/access.c
@@ -0,0 +1,1531 @@
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/****************************************************************************
+
+ Copyright (c) 1999,2000 WU-FTPD Development Group.
+ All rights reserved.
+
+ Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994
+ The Regents of the University of California.
+ Portions Copyright (c) 1993, 1994 Washington University in Saint Louis.
+ Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc.
+ Portions Copyright (c) 1989 Massachusetts Institute of Technology.
+ Portions Copyright (c) 1998 Sendmail, Inc.
+ Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P. Allman.
+ Portions Copyright (c) 1997 by Stan Barber.
+ Portions Copyright (c) 1997 by Kent Landfield.
+ Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ Free Software Foundation, Inc.
+
+ Use and distribution of this software and its source code are governed
+ by the terms and conditions of the WU-FTPD Software License ("LICENSE").
+
+ If you did not receive a copy of the license, it may be obtained online
+ at http://www.wu-ftpd.org/license.html.
+
+ $Id: access.c,v 1.30 2000/07/01 18:17:38 wuftpd Exp $
+
+****************************************************************************/
+#include "config.h"
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+
+#ifdef HAVE_SYS_SYSLOG_H
+#include <sys/syslog.h>
+#endif
+#if defined(HAVE_SYSLOG_H) || (!defined(AUTOCONF) && !defined(HAVE_SYS_SYSLOG_H))
+#include <syslog.h>
+#endif
+
+#ifdef TIME_WITH_SYS_TIME
+#include <time.h>
+#include <sys/time.h>
+#elif defined(HAVE_SYS_TIME_H)
+#include <sys/time.h>
+#else
+#include <time.h>
+#endif
+
+#include <ctype.h>
+#include <pwd.h>
+#include <grp.h>
+#include <limits.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/param.h>
+
+#ifdef HAVE_PATHS_H
+#include <paths.h>
+#endif
+
+#include "pathnames.h"
+#include "extensions.h"
+#include "proto.h"
+
+#if defined(HAVE_FCNTL_H)
+#include <fcntl.h>
+#endif
+
+#ifdef OTHER_PASSWD
+#include "getpwnam.h"
+extern char _path_passwd[];
+#ifdef SHADOW_PASSWORD
+extern char _path_shadow[];
+#endif
+#endif
+
+#if defined(USE_PAM) && defined(OTHER_PASSWD)
+extern int use_pam;
+#endif
+
+extern char remotehost[], remoteaddr[], *remoteident, *aclbuf;
+extern int nameserved, anonymous, guest, TCPwindowsize, use_accessfile;
+extern mode_t defumask;
+char Shutdown[MAXPATHLEN];
+int keepalive = 0;
+#define MAXLINE 80
+static char incline[MAXLINE];
+static int pidfd = -1;
+extern int Bypass_PID_Files;
+
+#ifndef HELP_CRACKERS
+extern char DelayedMessageFile[];
+#endif
+
+#include "wu_fnmatch.h"
+
+#define ACL_COUNT 0
+#define ACL_JOIN 1
+#define ACL_REMOVE 2
+
+/*************************************************************************/
+/* FUNCTION : parse_time */
+/* PURPOSE : Check a single valid-time-string against the current time */
+/* and return whether or not a match occurs. */
+/* ARGUMENTS : a pointer to the time-string */
+/*************************************************************************/
+
+int parsetime(char *whattime)
+{
+ static char *days[] =
+ {"Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Wk"};
+ time_t clock;
+ struct tm *curtime;
+ int wday, start, stop, ltime, validday, loop, match;
+
+ (void) time(&clock);
+ curtime = localtime(&clock);
+ wday = curtime->tm_wday;
+ validday = 0;
+ match = 1;
+
+ while (match && isalpha(*whattime) && isupper(*whattime)) {
+ match = 0;
+ for (loop = 0; loop < 8; loop++) {
+ if (strncmp(days[loop], whattime, 2) == 0) {
+ whattime += 2;
+ match = 1;
+ if ((wday == loop) || ((loop == 7) && wday && (wday < 6))) {
+ validday = 1;
+ }
+ }
+ }
+ }
+
+ if (!validday) {
+ if (strncmp(whattime, "Any", 3) == 0) {
+ validday = 1;
+ whattime += 3;
+ }
+ else
+ return (0);
+ }
+
+ if (sscanf(whattime, "%d-%d", &start, &stop) == 2) {
+ ltime = curtime->tm_min + 100 * curtime->tm_hour;
+ if ((start < stop) && ((ltime >= start) && ltime < stop))
+ return (1);
+ if ((start > stop) && ((ltime >= start) || ltime < stop))
+ return (1);
+ }
+ else
+ return (1);
+
+ return (0);
+}
+
+/*************************************************************************/
+/* FUNCTION : validtime */
+/* PURPOSE : Break apart a set of valid time-strings and pass them to */
+/* parse_time, returning whether or not ANY matches occurred */
+/* ARGUMENTS : a pointer to the time-string */
+/*************************************************************************/
+
+int validtime(char *ptr)
+{
+ char *nextptr;
+ int good;
+
+ while (1) {
+ nextptr = strchr(ptr, '|');
+ if (strchr(ptr, '|') == NULL)
+ return (parsetime(ptr));
+ *nextptr = '\0';
+ good = parsetime(ptr);
+ /* gotta restore the | or things get skipped! */
+ *nextptr++ = '|';
+ if (good)
+ return (1);
+ ptr = nextptr;
+ }
+}
+
+#ifdef INET6
+/*************************************************************************/
+/* FUNCTION : ipv6str */
+/* PURPOSE : Convert an IPv6 address string with optional /CIDR suffix */
+/* into an IPv6 address and a CIDR, which are returned in */
+/* the arguments pointed to by in6p and cidrp. */
+/* ARGUMENTS : The IPv6 address string and pointers to in6_addr and CIDR */
+/* RETURNS : 1 if addr is an IPv6 address string, 0 if not */
+/*************************************************************************/
+
+static int ipv6str(char *addr, struct in6_addr *in6p, int *cidrp)
+{
+ int cidr = 128; /* IPv6 addresses are 128-bits long */
+ char *ptr;
+
+ if ((ptr = strstr(addr, "/")))
+ *ptr = '\0';
+
+ if (inet_pton(AF_INET6, addr, in6p) != 1) {
+ if (ptr)
+ *ptr = '/';
+ return 0;
+ }
+
+ if (ptr) {
+ *ptr++ = '/';
+ cidr = atoi(ptr);
+ if (cidr < 0)
+ cidr = 0;
+ else if (cidr > 128)
+ cidr = 128;
+ }
+ *cidrp = cidr;
+ return 1;
+}
+#endif
+
+/*************************************************************************/
+/* FUNCTION : hostmatch */
+/* PURPOSE : Match remote hostname or address against a glob string */
+/* ARGUMENTS : The string to match, remote address, remote hostname */
+/* RETURNS : 0 if no match, 1 if a match occurs */
+/*************************************************************************/
+
+int hostmatch(char *addr, char *remoteaddr, char *remotehost)
+{
+ FILE *incfile;
+ char *ptr, junk, s[4][4];
+ int found = 1;
+ int not_found = 0;
+ int match = 0;
+ int i, a[4], m[4], r[4], cidr;
+#ifdef INET6
+ struct in6_addr addr_in6;
+#endif
+
+ if (addr == NULL)
+ return (0);
+
+ if (*addr == '!') {
+ found = 0;
+ not_found = 1;
+ addr++;
+ }
+
+ if (sscanf(addr, "%d.%d.%d.%d/%d", a, a + 1, a + 2, a + 3, &cidr) == 5) {
+ m[0] = 0;
+ m[1] = 0;
+ m[2] = 0;
+ m[3] = 0;
+ if (cidr < 0)
+ cidr = 0;
+ else if (cidr > 32)
+ cidr = 32;
+ for (i = 0; cidr > 8; i++) {
+ m[i] = 255;
+ cidr -= 8;
+ }
+ switch (cidr) {
+ case 8:
+ m[i] += 1;
+ case 7:
+ m[i] += 2;
+ case 6:
+ m[i] += 4;
+ case 5:
+ m[i] += 8;
+ case 4:
+ m[i] += 16;
+ case 3:
+ m[i] += 32;
+ case 2:
+ m[i] += 64;
+ case 1:
+ m[i] += 128;
+ }
+ /* make sure remoteaddr is an IPv4 address */
+ if (sscanf(remoteaddr, "%d.%d.%d.%d", r, r + 1, r + 2, r + 3) != 4)
+ return not_found;
+ for (i = 0; i < 4; i++)
+ if ((a[i] & m[i]) != (r[i] & m[i]))
+ return not_found;
+ return found;
+ }
+ else if (sscanf(addr, "%d.%d.%d.%d:%d.%d.%d.%d", a, a + 1, a + 2, a + 3, m, m + 1, m + 2, m + 3) == 8) {
+ /* make sure remoteaddr is an IPv4 address */
+ if (sscanf(remoteaddr, "%d.%d.%d.%d", r, r + 1, r + 2, r + 3) != 4)
+ return not_found;
+ for (i = 0; i < 4; i++)
+ if ((a[i] & m[i]) != (r[i] & m[i]))
+ return not_found;
+ return found;
+ }
+ else if (sscanf(addr, "%3[0-9*].%3[0-9*].%3[0-9*].%3[0-9*]%c",
+ s[0], s[1], s[2], s[3], &junk) == 4 &&
+ (!strcmp(s[0],"*") || !strchr(s[0],'*')) &&
+ (!strcmp(s[1],"*") || !strchr(s[1],'*')) &&
+ (!strcmp(s[2],"*") || !strchr(s[2],'*')) &&
+ (!strcmp(s[3],"*") || !strchr(s[3],'*')) ) {
+ /* make sure remoteaddr is an IPv4 address */
+ if (sscanf(remoteaddr, "%d.%d.%d.%d", r, r + 1, r + 2, r + 3) != 4)
+ return not_found;
+ for (i = 0; i < 4; i++)
+ if ( (strcmp(s[i],"*")) && (atoi(s[i]) != r[i]) )
+ return not_found;
+ return found;
+ }
+#ifdef INET6
+ else if (ipv6str(addr, &addr_in6, &cidr)) {
+ struct in6_addr rem_in6;
+ uint32_t addr32[4], rem32[4];
+ int bitstozero;
+
+ if (inet_pton6(remoteaddr, &rem_in6) != 1)
+ return not_found;
+
+ memcpy(addr32, addr_in6.s6_addr, sizeof(addr32));
+ memcpy(rem32, rem_in6.s6_addr, sizeof(rem32));
+
+ /* IPv6 addresses are 128-bits long */
+ bitstozero = 128 - cidr;
+
+ /* zero bits starting with the least significant */
+ for (i = 3; (bitstozero > 0) && (i >= 0); i--, bitstozero -= 32) {
+ if (bitstozero >= 32)
+ addr32[i] = rem32[i] = 0;
+ else {
+ addr32[i] = (ntohl(addr32[i]) >> bitstozero) << bitstozero;
+ rem32[i] = (ntohl(rem32[i]) >> bitstozero) << bitstozero;
+ }
+ }
+ if (memcmp(addr32, rem32, sizeof(addr32)))
+ return not_found;
+ return found;
+ }
+#endif
+ else if (*addr == '/') {
+ /*
+ * read addrglobs from named path using similar format as addrglobs
+ * in access file
+ */
+ if ((incfile = fopen(addr, "r")) == NULL) {
+ if (errno != ENOENT)
+ syslog(LOG_ERR,
+ "cannot open addrglob file %s: %m", addr);
+ return (0);
+ }
+
+ while (!match && (fgets(incline, MAXLINE, incfile) != NULL)) {
+ ptr = strtok(incline, " \t\n");
+ if (ptr && hostmatch(ptr, remoteaddr, remotehost))
+ match = 1;
+ while (!match && ((ptr = strtok(NULL, " \t\n")) != NULL)) {
+ if (ptr && hostmatch(ptr, remoteaddr, remotehost))
+ match = 1;
+ }
+ }
+ fclose(incfile);
+ return (match ? found : not_found);
+ }
+ else { /* match a hostname or hostname glob */
+ match = (!wu_fnmatch(addr, remotehost, FNM_CASEFOLD)) ||
+ (!wu_fnmatch(addr, remoteaddr, 0));
+ return (match ? found : not_found);
+ }
+}
+
+/*************************************************************************/
+/* FUNCTION : acl_guestgroup */
+/* PURPOSE : If the real user is a member of any of the listed groups, */
+/* return 1. Otherwise return 0. */
+/* ARGUMENTS : pw, a pointer to the passwd struct for the user */
+/*************************************************************************/
+
+int acl_guestgroup(struct passwd *pw)
+{
+ /*
+ * guestuser <name> [<name> ...]
+ *
+ * If name begins with '%' treat as numeric.
+ * Numeric names may be ranges.
+ * %<uid> A single numeric UID
+ * %<uid>+ All UIDs greater or equal to UID
+ * %<uid>- All UIDs greater or equal to UID
+ * %-<uid> All UIDs less or equal to UID
+ * %<uid>-<uid> All UIDs between the two (inclusive)
+ * * All UIDs
+ */
+ if (uid_match("guestuser", pw->pw_uid))
+ return (1);
+
+ /*
+ * guestgroup <group> [<group> ...]
+ *
+ * If group begins with '%' treat as numeric.
+ * Numeric groups may be ranges.
+ * %<gid> A single GID
+ * %<gid>+ All GIDs greater or equal to GID
+ * %<gid>- All GIDs greater or equal to GID
+ * %-<gid> All GIDs less or equal to GID
+ * %<gid>-<gid> All GIDs between the two (inclusive)
+ * * All GIDs
+ */
+ if (gid_match("guestgroup", pw->pw_gid, pw->pw_name))
+ return (1);
+
+ return (0);
+}
+
+int acl_realgroup(struct passwd *pw)
+{
+ /*
+ * realuser <name> [<name> ...]
+ *
+ * If name begins with '%' treat as numeric.
+ * Numeric names may be ranges.
+ * %<uid> A single numeric UID
+ * %<uid>+ All UIDs greater or equal to UID
+ * %<uid>- All UIDs greater or equal to UID
+ * %-<uid> All UIDs less or equal to UID
+ * %<uid>-<uid> All UIDs between the two (inclusive)
+ * * All UIDs
+ */
+ if (uid_match("realuser", pw->pw_uid))
+ return (1);
+
+ /*
+ * realgroup <group> [<group> ...]
+ *
+ * If group begins with '%' treat as numeric.
+ * Numeric groups may be ranges.
+ * %<gid> A single GID
+ * %<gid>+ All GIDs greater or equal to GID
+ * %<gid>- All GIDs greater or equal to GID
+ * %-<gid> All GIDs less or equal to GID
+ * %<gid>-<gid> All GIDs between the two (inclusive)
+ * * All GIDs
+ */
+ if (gid_match("realgroup", pw->pw_gid, pw->pw_name))
+ return (1);
+
+ return (0);
+}
+
+/*************************************************************************/
+/* FUNCTION : acl_autogroup */
+/* PURPOSE : If the guest user is a member of any of the classes in */
+/* the autogroup comment, cause a setegid() to the specified */
+/* group. */
+/* ARGUMENTS : pw, a pointer to the passwd struct for the user */
+/*************************************************************************/
+
+void acl_autogroup(struct passwd *pw)
+{
+ char class[BUFSIZ];
+
+ struct aclmember *entry = NULL;
+ struct group *grp;
+ int which;
+
+ (void) acl_getclass(class);
+
+ /* autogroup <group> <class> [<class> ...] */
+ while (getaclentry("autogroup", &entry)) {
+ if (!ARG0 || !ARG1)
+ continue;
+ for (which = 1; (which < MAXARGS) && ARG[which]; which++) {
+ if (!strcasecmp(ARG[which], class)) {
+ if (ARG0[0] == '%')
+ pw->pw_gid = atoi(ARG0 + 1);
+ else {
+ if ((grp = getgrnam(ARG0)))
+ pw->pw_gid = grp->gr_gid;
+ else
+ syslog(LOG_ERR, "autogroup: set group %s not found", ARG0);
+ endgrent();
+ }
+ return;
+ }
+ }
+ }
+}
+
+/*************************************************************************/
+/* FUNCTION : acl_setfunctions */
+/* PURPOSE : Scan the ACL buffer and determine what logging to perform */
+/* for this user, and whether or not user is allowed to use */
+/* the automatic TAR and COMPRESS functions. Also, set the */
+/* current process priority of this copy of the ftpd server */
+/* to a `nice' value value if this user is a member of a */
+/* group which the ftpaccess file says should be nice'd. */
+/* ARGUMENTS : none */
+/*************************************************************************/
+
+void acl_setfunctions(void)
+{
+ char class[BUFSIZ];
+
+ extern int log_incoming_xfers, log_outbound_xfers, mangleopts, log_commands,
+ log_security, syslogmsg, lgi_failure_threshold;
+
+ struct aclmember *entry = NULL;
+
+ int l_compress, l_tar, inbound = 0, outbound = 0, which, set;
+
+ log_security = 0;
+
+ /* Initialize to the logging value specified on the command line, can't
+ just use the current value as it may have been set by a previous call. */
+ log_incoming_xfers = (log_incoming_xfers & 2) ? 3 : 0;
+ log_outbound_xfers = (log_outbound_xfers & 2) ? 3 : 0;
+ log_commands = (log_commands & 2) ? 3 : 0;
+
+ memset((void *) &class[0], 0, sizeof(class));
+
+ (void) acl_getclass(class);
+
+ entry = (struct aclmember *) NULL;
+ if (getaclentry("loginfails", &entry) && ARG0 != NULL) {
+ lgi_failure_threshold = atoi(ARG0);
+ }
+#ifndef NO_PRIVATE
+ entry = (struct aclmember *) NULL;
+ if (getaclentry("private", &entry) && ARG0 != NULL)
+ if (!strcasecmp(ARG0, "yes"))
+ priv_setup(_path_private);
+#endif /* !NO_PRIVATE */
+
+ entry = (struct aclmember *) NULL;
+ set = 0;
+ while (!set && getaclentry("compress", &entry)) {
+ if (!ARG0)
+ continue;
+ l_compress = 0;
+ if (!strcasecmp(ARG0, "yes"))
+ l_compress = 1;
+ for (which = 1; (which < MAXARGS) && ARG[which]; which++) {
+ if (!wu_fnmatch(ARG[which], class, FNM_CASEFOLD)) {
+ mangleopts |= l_compress * (O_COMPRESS | O_UNCOMPRESS);
+ set = 1;
+ }
+ }
+ }
+
+ entry = (struct aclmember *) NULL;
+ set = 0;
+ while (!set && getaclentry("tar", &entry)) {
+ if (!ARG0)
+ continue;
+ l_tar = 0;
+ if (!strcasecmp(ARG0, "yes"))
+ l_tar = 1;
+ for (which = 1; (which < MAXARGS) && ARG[which]; which++) {
+ if (!wu_fnmatch(ARG[which], class, FNM_CASEFOLD)) {
+ mangleopts |= l_tar * O_TAR;
+ set = 1;
+ }
+ }
+ }
+
+ /* plan on expanding command syntax to include classes for each of these */
+
+ entry = (struct aclmember *) NULL;
+ while (getaclentry("log", &entry)) {
+ if (!ARG0)
+ continue;
+ if (!strcasecmp(ARG0, "commands")) {
+ if (!ARG1)
+ continue;
+ if (anonymous && strcasestr(ARG1, "anonymous"))
+ log_commands |= 1;
+ if (guest && strcasestr(ARG1, "guest"))
+ log_commands |= 1;
+ if (!guest && !anonymous && strcasestr(ARG1, "real"))
+ log_commands |= 1;
+ }
+ if (!strcasecmp(ARG0, "transfers")) {
+ if (!ARG1 || !ARG2)
+ continue;
+ set = 0;
+ if (strcasestr(ARG1, "anonymous") && anonymous)
+ set = 1;
+ if (strcasestr(ARG1, "guest") && guest)
+ set = 1;
+ if (strcasestr(ARG1, "real") && !guest && !anonymous)
+ set = 1;
+ if (strcasestr(ARG2, "inbound"))
+ inbound = 1;
+ if (strcasestr(ARG2, "outbound"))
+ outbound = 1;
+ if (set)
+ log_incoming_xfers |= inbound;
+ if (set)
+ log_outbound_xfers |= outbound;
+ }
+ if (!strcasecmp(ARG0, "security")) {
+ if (!ARG1)
+ continue;
+ if (strcasestr(ARG1, "anonymous") && anonymous)
+ log_security = 1;
+ if (strcasestr(ARG1, "guest") && guest)
+ log_security = 1;
+ if (strcasestr(ARG1, "real") && !guest && !anonymous)
+ log_security = 1;
+ }
+ if (!strcasecmp(ARG0, "syslog"))
+ syslogmsg = 1;
+ if (!strcasecmp(ARG0, "xferlog"))
+ syslogmsg = 0;
+ if (!strcasecmp(ARG0, "syslog+xferlog")
+ || !strcasecmp(ARG0, "xferlog+syslog"))
+ syslogmsg = 2;
+ }
+}
+
+/*************************************************************************/
+/* FUNCTION : acl_getclass */
+/* PURPOSE : Scan the ACL buffer and determine what class user is in */
+/* ARGUMENTS : pointer to buffer to class name, pointer to ACL buffer */
+/*************************************************************************/
+
+int acl_getclass(char *classbuf)
+{
+ int which;
+ struct aclmember *entry = NULL;
+
+ while (getaclentry("class", &entry)) {
+ if (ARG0)
+ strlcpy(classbuf, ARG0, BUFSIZ);
+
+ for (which = 2; (which < MAXARGS) && ARG[which]; which++) {
+ if (anonymous && strcasestr(ARG1, "anonymous") &&
+ hostmatch(ARG[which], remoteaddr, remotehost))
+ return (1);
+
+ if (guest && strcasestr(ARG1, "guest") && hostmatch(ARG[which], remoteaddr, remotehost))
+ return (1);
+
+ if (!guest && !anonymous && strcasestr(ARG1, "real") &&
+ hostmatch(ARG[which], remoteaddr, remotehost))
+ return (1);
+ }
+ }
+
+ *classbuf = (char) NULL;
+ return (0);
+
+}
+
+/*************************************************************************/
+/* FUNCTION : acl_getlimit */
+/* PURPOSE : Scan the ACL buffer and determine what limit applies to */
+/* the user */
+/* ARGUMENTS : pointer class name, pointer to ACL buffer */
+/*************************************************************************/
+
+int acl_getlimit(char *class, char *msgpathbuf)
+{
+ int limit;
+ struct aclmember *entry = NULL;
+
+ if (msgpathbuf)
+ *msgpathbuf = '\0';
+
+ /* limit <class> <n> <times> [<message_file>] */
+ while (getaclentry("limit", &entry)) {
+ if (!ARG0 || !ARG1 || !ARG2)
+ continue;
+ if (!strcasecmp(class, ARG0)) {
+ limit = atoi(ARG1);
+ if (validtime(ARG2)) {
+ if (ARG3 && msgpathbuf)
+ strcpy(msgpathbuf, ARG3);
+ return (limit);
+ }
+ }
+ }
+ return (-1);
+}
+
+/*************************************************************************/
+/* FUNCTION : acl_getnice */
+/* PURPOSE : Scan the ACL buffer and determine what nice value applies */
+/* to the user */
+/* ARGUMENTS : pointer class name */
+/*************************************************************************/
+
+int acl_getnice(char *class)
+{
+ int nice_delta_for_class_found = 0;
+ int nice_delta = 0;
+ int default_nice_delta = 0;
+
+ struct aclmember *entry = NULL;
+
+ /* nice <nice_delta> [<class>] */
+ while (getaclentry("nice", &entry)) {
+ if (!ARG0)
+ continue;
+ if (!ARG1)
+ default_nice_delta = atoi(ARG0);
+ else if (!strcasecmp(class, ARG1)) {
+ nice_delta_for_class_found = 1;
+ nice_delta = atoi(ARG0);
+ }
+ }
+ if (!nice_delta_for_class_found)
+ nice_delta = default_nice_delta;
+ return nice_delta;
+}
+
+
+/*************************************************************************/
+/* FUNCTION : acl_getdefumask */
+/* PURPOSE : Scan the ACL buffer to determine what umask value applies */
+/* to the user */
+/* ARGUMENTS : pointer to class name */
+/*************************************************************************/
+
+void acl_getdefumask(char *class)
+{
+ struct aclmember *entry = NULL;
+ char *ptr;
+ unsigned int val;
+
+ /* defumask <umask> [<class>] */
+ while (getaclentry("defumask", &entry)) {
+ if (!ARG0)
+ continue;
+ if (!ARG1 || !strcasecmp(class, ARG1)) {
+ ptr = ARG0;
+ val = 0;
+ while (*ptr && *ptr >= '0' && *ptr <= '7')
+ val = val * 8 + *ptr++ - '0';
+ if (!*ptr && val <= 0777) {
+ defumask = val;
+ if (ARG1)
+ break;
+ }
+ else
+ syslog(LOG_WARNING, "bad umask in %s ignored: defumask %s",
+ _path_ftpaccess, ARG0);
+ }
+ }
+ umask(defumask);
+}
+
+/*************************************************************************/
+/* FUNCTION : acl_tcpwindow */
+/* PURPOSE : Scan the ACL buffer and determine what TCP window size to */
+/* use based upon the class */
+/* ARGUMENTS : pointer to class name */
+/*************************************************************************/
+
+void acl_tcpwindow(char *class)
+{
+ struct aclmember *entry = NULL;
+
+ /* tcpwindow <size> [<class>] */
+ while (getaclentry("tcpwindow", &entry)) {
+ if (!ARG0)
+ continue;
+ if (!ARG1)
+ TCPwindowsize = strtoul(ARG0, NULL, 0);
+ else if (!strcasecmp(class, ARG1)) {
+ TCPwindowsize = strtoul(ARG0, NULL, 0);
+ break;
+ }
+ }
+}
+
+/*************************************************************************/
+/* FUNCTION : acl_bufsize */
+/* PURPOSE : Scan the ACL buffer and determine the send and receive */
+/* buffer sizes to use */
+/* ARGUMENTS : None */
+/*************************************************************************/
+
+static void acl_bufsize()
+{
+ struct aclmember *entry;
+ extern size_t sendbufsz, recvbufsz;
+
+ /* sendbuf <size> [<typelist>] */
+ entry = (struct aclmember *) NULL;
+ sendbufsz = 0;
+ while (getaclentry("sendbuf", &entry)) {
+ if (!ARG0)
+ continue;
+ if (!ARG1)
+ sendbufsz = strtoul(ARG0, NULL, 0);
+ else if (type_match(ARG1)) {
+ sendbufsz = strtoul(ARG0, NULL, 0);
+ break;
+ }
+ }
+
+ /* recvbuf <size> [<typelist>] */
+ entry = (struct aclmember *) NULL;
+ recvbufsz = 0;
+ while (getaclentry("recvbuf", &entry)) {
+ if (!ARG0)
+ continue;
+ if (!ARG1)
+ recvbufsz = strtoul(ARG0, NULL, 0);
+ else if (type_match(ARG1)) {
+ recvbufsz = strtoul(ARG0, NULL, 0);
+ break;
+ }
+ }
+}
+
+#ifdef TRANSFER_COUNT
+#ifdef TRANSFER_LIMIT
+
+/*************************************************************************/
+/* FUNCTION : acl_filelimit */
+/* PURPOSE : Scan the ACL buffer and determine what file limit to use */
+/* based upon the class */
+/* ARGUMENTS : pointer to class name */
+/*************************************************************************/
+
+void acl_filelimit(char *class)
+{
+ struct aclmember *entry = NULL;
+ int raw_in = 0;
+ int raw_out = 0;
+ int raw_total = 0;
+ int data_in = 0;
+ int data_out = 0;
+ int data_total = 0;
+ extern int file_limit_raw_in;
+ extern int file_limit_raw_out;
+ extern int file_limit_raw_total;
+ extern int file_limit_data_in;
+ extern int file_limit_data_out;
+ extern int file_limit_data_total;
+
+ /* file-limit [<raw>] <in|out|total> <count> [<class>] */
+ while (getaclentry("file-limit", &entry)) {
+ if (!ARG0 || !ARG1)
+ continue;
+ if (!strcasecmp(ARG0, "raw")) {
+ if (!ARG2)
+ continue;
+ if (!strcasecmp(ARG1, "in")) {
+ if (!ARG3) {
+ if (!raw_in)
+ file_limit_raw_in = atoi(ARG2);
+ }
+ else if (!strcasecmp(class, ARG3)) {
+ raw_in = 1;
+ file_limit_raw_in = atoi(ARG2);
+ }
+ }
+ else if (!strcasecmp(ARG1, "out")) {
+ if (!ARG3) {
+ if (!raw_out)
+ file_limit_raw_out = atoi(ARG2);
+ }
+ else if (!strcasecmp(class, ARG3)) {
+ raw_out = 1;
+ file_limit_raw_out = atoi(ARG2);
+ }
+ }
+ else if (!strcasecmp(ARG1, "total")) {
+ if (!ARG3) {
+ if (!raw_total)
+ file_limit_raw_total = atoi(ARG2);
+ }
+ else if (!strcasecmp(class, ARG3)) {
+ raw_total = 1;
+ file_limit_raw_total = atoi(ARG2);
+ }
+ }
+ }
+ else if (!strcasecmp(ARG0, "in")) {
+ if (!ARG2) {
+ if (!data_in)
+ file_limit_data_in = atoi(ARG1);
+ }
+ else if (!strcasecmp(class, ARG2)) {
+ data_in = 1;
+ file_limit_data_in = atoi(ARG1);
+ }
+ }
+ else if (!strcasecmp(ARG0, "out")) {
+ if (!ARG2) {
+ if (!data_out)
+ file_limit_data_out = atoi(ARG1);
+ }
+ else if (!strcasecmp(class, ARG2)) {
+ data_out = 1;
+ file_limit_data_out = atoi(ARG1);
+ }
+ }
+ else if (!strcasecmp(ARG0, "total")) {
+ if (!ARG2) {
+ if (!data_total)
+ file_limit_data_total = atoi(ARG1);
+ }
+ else if (!strcasecmp(class, ARG2)) {
+ data_total = 1;
+ file_limit_data_total = atoi(ARG1);
+ }
+ }
+ }
+}
+
+/*************************************************************************/
+/* FUNCTION : acl_datalimit */
+/* PURPOSE : Scan the ACL buffer and determine what data limit to use */
+/* based upon the class */
+/* ARGUMENTS : pointer to class name */
+/*************************************************************************/
+
+void acl_datalimit(char *class)
+{
+ struct aclmember *entry = NULL;
+ int raw_in = 0;
+ int raw_out = 0;
+ int raw_total = 0;
+ int data_in = 0;
+ int data_out = 0;
+ int data_total = 0;
+ extern off_t data_limit_raw_in;
+ extern off_t data_limit_raw_out;
+ extern off_t data_limit_raw_total;
+ extern off_t data_limit_data_in;
+ extern off_t data_limit_data_out;
+ extern off_t data_limit_data_total;
+
+ /* data-limit [<raw>] <in|out|total> <count> [<class>] */
+ while (getaclentry("data-limit", &entry)) {
+ if (!ARG0 || !ARG1)
+ continue;
+ if (!strcasecmp(ARG0, "raw")) {
+ if (!ARG2)
+ continue;
+ if (!strcasecmp(ARG1, "in")) {
+ if (!ARG3) {
+ if (!raw_in)
+ data_limit_raw_in = atoi(ARG2);
+ }
+ else if (!strcasecmp(class, ARG3)) {
+ raw_in = 1;
+ data_limit_raw_in = atoi(ARG2);
+ }
+ }
+ else if (!strcasecmp(ARG1, "out")) {
+ if (!ARG3) {
+ if (!raw_out)
+ data_limit_raw_out = atoi(ARG2);
+ }
+ else if (!strcasecmp(class, ARG3)) {
+ raw_out = 1;
+ data_limit_raw_out = atoi(ARG2);
+ }
+ }
+ else if (!strcasecmp(ARG1, "total")) {
+ if (!ARG3) {
+ if (!raw_total)
+ data_limit_raw_total = atoi(ARG2);
+ }
+ else if (!strcasecmp(class, ARG3)) {
+ raw_total = 1;
+ data_limit_raw_total = atoi(ARG2);
+ }
+ }
+ }
+ else if (!strcasecmp(ARG0, "in")) {
+ if (!ARG2) {
+ if (!data_in)
+ data_limit_data_in = atoi(ARG1);
+ }
+ else if (!strcasecmp(class, ARG2)) {
+ data_in = 1;
+ data_limit_data_in = atoi(ARG1);
+ }
+ }
+ else if (!strcasecmp(ARG0, "out")) {
+ if (!ARG2) {
+ if (!data_out)
+ data_limit_data_out = atoi(ARG1);
+ }
+ else if (!strcasecmp(class, ARG2)) {
+ data_out = 1;
+ data_limit_data_out = atoi(ARG1);
+ }
+ }
+ else if (!strcasecmp(ARG0, "total")) {
+ if (!ARG2) {
+ if (!data_total)
+ data_limit_data_total = atoi(ARG1);
+ }
+ else if (!strcasecmp(class, ARG2)) {
+ data_total = 1;
+ data_limit_data_total = atoi(ARG1);
+ }
+ }
+ }
+}
+
+
+#ifdef RATIO
+
+/*************************************************************************/
+/* FUNCTION : acl_downloadrate */
+/* PURPOSE : Scan the ACL buffer and determine what data limit to use */
+/* based upon the class */
+/* ARGUMENTS : pointer to class name */
+/*************************************************************************/
+
+void acl_downloadrate(char *class)
+{
+ struct aclmember *entry = NULL;
+ extern int upload_download_rate;
+ int which;
+
+ /* ul-dl-rate <rate> [<class> ...] */
+ while (getaclentry("ul-dl-rate", &entry)) {
+ if (!ARG0 )
+ continue;
+
+ if (!ARG1) {
+ upload_download_rate = atol(ARG0);
+ }
+ else {
+ for (which = 1; (which < MAXARGS) && ARG[which]; which++) {
+ if (!strcasecmp(ARG[which], class))
+ upload_download_rate = atol(ARG0);
+ }
+ }
+
+ }
+}
+#endif /* RATIO */
+
+#endif
+#endif
+
+/*************************************************************************/
+/* FUNCTION : acl_deny */
+/* PURPOSE : Scan the ACL buffer and determine if access is denied. */
+/* ARGUMENTS : Pointer to buffer into which the path of the message file */
+/* is copied. */
+/*************************************************************************/
+
+int acl_deny(char *msgpathbuf)
+{
+ struct aclmember *entry = NULL;
+
+ if (msgpathbuf)
+ *msgpathbuf = (char) NULL;
+
+ /* deny <addrglob> [<message_file>] */
+ while (getaclentry("deny", &entry)) {
+ if (!ARG0)
+ continue;
+ if (strcasecmp(ARG0, "!nameserved") == 0) {
+ if (!nameserved) {
+ if (ARG1)
+ strcpy(msgpathbuf, entry->arg[1]);
+ return (1);
+ }
+ }
+ else if (hostmatch(ARG0, remoteaddr, remotehost)) {
+ if (ARG1)
+ strcpy(msgpathbuf, entry->arg[1]);
+ return (1);
+ }
+ }
+ return (0);
+}
+
+/*************************************************************************/
+/* FUNCTION : lock_fd */
+/* PURPOSE : Lock a file. */
+/* ARGUMENTS : File descriptor of file to lock. */
+/*************************************************************************/
+
+static void lock_fd(int fd)
+{
+#if !defined(HAVE_FLOCK)
+ struct flock arg;
+#endif /* !defined(HAVE_FLOCK) */
+
+#if defined(HAVE_FLOCK)
+ while (flock(fd, LOCK_EX)) {
+# if !defined(NO_PID_SLEEP_MSGS)
+ syslog(LOG_ERR, "sleeping: flock of pid file failed: %m");
+# endif /* !defined(NO_PID_SLEEP_MSGS) */
+#else /* !(defined(HAVE_FLOCK)) */
+ arg.l_type = F_WRLCK;
+ arg.l_whence = arg.l_start = arg.l_len = 0;
+ while (-1 == fcntl(fd, F_SETLK, &arg)) {
+# if !defined(NO_PID_SLEEP_MSGS)
+ syslog(LOG_ERR, "sleeping: fcntl lock of pid file failed: %m");
+# endif /* !defined(NO_PID_SLEEP_MSGS) */
+#endif /* !(defined(HAVE_FLOCK)) */
+ sleep(1);
+ }
+}
+
+/*************************************************************************/
+/* FUNCTION : unlock_fd */
+/* PURPOSE : Unlock a file locked by lock_fd. */
+/* ARGUMENTS : File descriptor of file to unlock. */
+/*************************************************************************/
+
+static void unlock_fd(int fd)
+{
+#if !defined(HAVE_FLOCK)
+ struct flock arg;
+#endif /* !defined(HAVE_FLOCK) */
+
+#if defined(HAVE_FLOCK)
+ flock(fd, LOCK_UN);
+#else /* !(defined(HAVE_FLOCK)) */
+ arg.l_type = F_UNLCK;
+ arg.l_whence = arg.l_start = arg.l_len = 0;
+ fcntl(fd, F_SETLK, &arg);
+#endif /* !(defined(HAVE_FLOCK)) */
+}
+
+/*************************************************************************/
+/* FUNCTION : limit_op */
+/* PURPOSE : Carry out the specified limit operation, returning the */
+/* number of users in the class or -1 on failure. */
+/* ARGUMENTS : Operation (ACL_COUNT/ACL_JOIN/ACL_REMOVE), user limit */
+/*************************************************************************/
+
+static int limit_op(int operation, int limit)
+{
+ int i, j, n, count;
+ int bit_changed, toomany, write_all_header;
+ off_t offset;
+ pid_t pid, procid;
+ time_t now;
+ struct pidfile_header hdr;
+ unsigned char bits, buf[BUFSIZ];
+
+ if (pidfd < 0)
+ return (-1);
+
+ if (lseek(pidfd, (off_t)0, SEEK_SET) != 0)
+ return (-1);
+
+ if (operation == ACL_COUNT) {
+ lock_fd(pidfd);
+ n = read(pidfd, (void *)&hdr.count, sizeof(hdr.count));
+ unlock_fd(pidfd);
+ if (n != sizeof(hdr.count))
+ return (-1);
+ return (hdr.count);
+ }
+
+ toomany = 0;
+ write_all_header = 0;
+ lock_fd(pidfd);
+ if (read(pidfd, (void *)&hdr, sizeof(hdr)) != sizeof(hdr)) {
+ hdr.count = 0;
+ hdr.last_checked = 0;
+ }
+ now = time(NULL);
+
+ /* check bitmap accuracy and re-calculate the count every 15 minutes */
+ if ((now >= (hdr.last_checked + (15 * 60))) || (now < hdr.last_checked)) {
+ count = 0;
+ procid = 0;
+ bit_changed = 0;
+ while ((n = read(pidfd, (void *)buf, sizeof(buf))) > 0) {
+ for (i = 0; i < n; i++) {
+ if (buf[i] == 0) {
+ procid += CHAR_BIT;
+ }
+ else {
+ bits = 1;
+ for (j = 0; j < CHAR_BIT; j++) {
+ if ((buf[i] & bits) != 0) {
+ if (kill(procid, 0) == 0) {
+ count++;
+ }
+ else {
+ bit_changed = 1;
+ buf[i] &= ~bits;
+ }
+ }
+ bits <<= 1;
+ procid++;
+ }
+ }
+ }
+ if (bit_changed) {
+ lseek(pidfd, (off_t)-n, SEEK_CUR);
+ write(pidfd, (void *)buf, n);
+ bit_changed = 0;
+ }
+ }
+ if (hdr.count != count) {
+ syslog(LOG_INFO, "pid file header count (%d) corrected to %d",
+ hdr.count, count);
+ hdr.count = count;
+ }
+ hdr.last_checked = time(NULL);
+ write_all_header = 1;
+ }
+
+ /* limit set to -1 when no limit defined */
+ if ((operation == ACL_JOIN) && (limit != -1) && (hdr.count >= limit)) {
+ /* return if no need to update the header */
+ if (write_all_header == 0) {
+ unlock_fd(pidfd);
+ return (-1);
+ }
+ toomany = 1;
+ }
+ else {
+ /* update the count */
+ if (operation == ACL_JOIN)
+ hdr.count++;
+ else if (hdr.count > 0) /* ACL_REMOVE */
+ hdr.count--;
+ }
+
+ /* update the header */
+ lseek(pidfd, (off_t)0, SEEK_SET);
+ if (write_all_header)
+ write(pidfd, (void *)&hdr, sizeof(hdr));
+ else
+ write(pidfd, (void *)&hdr.count, sizeof(hdr.count));
+
+ /* return if no need to update the bitmap */
+ if (toomany) {
+ unlock_fd(pidfd);
+ return (-1);
+ }
+
+ /* update the bitmap entry for the process */
+ pid = getpid();
+ offset = (off_t)(sizeof(hdr) + (pid/CHAR_BIT));
+ lseek(pidfd, offset, SEEK_SET);
+ if (read(pidfd, (void *)&bits, sizeof(bits)) != sizeof(bits))
+ bits = 0;
+ if (operation == ACL_JOIN)
+ bits |= (1 << (pid%CHAR_BIT));
+ else /* ACL_REMOVE */
+ bits &= ~(1 << (pid%CHAR_BIT));
+ lseek(pidfd, offset, SEEK_SET);
+ write(pidfd, (void *)&bits, sizeof(bits));
+ unlock_fd(pidfd);
+ return (hdr.count);
+}
+
+/*************************************************************************/
+/* FUNCTION : open_pidfile */
+/* PURPOSE : Return a file descriptor of an opened PID file. */
+/* ARGUMENTS : Users class. */
+/*************************************************************************/
+
+static int open_pidfile(char *class)
+{
+ int fd;
+ mode_t oldmask;
+ char pidfile[MAXPATHLEN];
+
+ snprintf(pidfile, sizeof(pidfile), _PATH_PIDNAMES, class);
+ oldmask = umask(0);
+ fd = open(pidfile, O_RDWR | O_CREAT, 0644);
+ (void) umask(oldmask);
+ if (fd < 0)
+ syslog(LOG_ERR, "cannot open pid file %s: %m", pidfile);
+ return (fd);
+}
+
+/*************************************************************************/
+/* FUNCTION : acl_countusers */
+/* PURPOSE : Return the number of users in the specified class. */
+/* ARGUMENTS : The name of the class to count. */
+/*************************************************************************/
+
+int acl_countusers(char *class)
+{
+ int count = 0, opidfd = pidfd;
+
+ if (Bypass_PID_Files)
+ return (0);
+
+ if (pidfd < 0) {
+ if ((pidfd = open_pidfile(class)) < 0)
+ return (-1);
+ }
+
+ count = limit_op(ACL_COUNT, 0);
+
+ /*
+ * acl_countusers may be called from msg_massage before the correct class
+ * is known, so close the pid file if we opened it.
+ */
+ if (opidfd < 0) {
+ close(pidfd);
+ pidfd = -1;
+ }
+ return (count);
+}
+
+/*************************************************************************/
+/* FUNCTION : acl_join */
+/* PURPOSE : Add the current process to the list of processes in the */
+/* specified class. */
+/* ARGUMENTS : The name of the class to join, user limit for the class. */
+/* RETURNS : 0 on success, -1 on failure */
+/*************************************************************************/
+
+int acl_join(char *class, int limit)
+{
+ if (Bypass_PID_Files)
+ return (0);
+
+ if (pidfd < 0) {
+ if ((pidfd = open_pidfile(class)) < 0)
+ return (-1);
+ }
+
+ if (limit_op(ACL_JOIN, limit) < 0) {
+ /* no need to leave the pid file open as we were not added to it */
+ close(pidfd);
+ pidfd = -1;
+ return (-1);
+ }
+ /* pidfd left open so can be updated after a chroot */
+ return (0);
+}
+
+/*************************************************************************/
+/* FUNCTION : acl_remove */
+/* PURPOSE : Remove the current process from the list of processes in */
+/* our class. */
+/* ARGUMENTS : None. */
+/*************************************************************************/
+
+void acl_remove(void)
+{
+ if (pidfd < 0)
+ return;
+ (void) limit_op(ACL_REMOVE, 0);
+ close(pidfd);
+ pidfd = -1;
+}
+
+/*************************************************************************/
+/* FUNCTION : pr_mesg */
+/* PURPOSE : Display a message to the user */
+/* ARGUMENTS : message code, name of file to display */
+/*************************************************************************/
+
+void pr_mesg(int msgcode, char *msgfile)
+{
+ FILE *infile;
+ char inbuf[BUFSIZ], outbuf[BUFSIZ], *cr;
+
+ if (msgfile && (int) strlen(msgfile) > 0) {
+ infile = fopen(msgfile, "r");
+ if (infile) {
+ while (fgets(inbuf, sizeof(inbuf), infile) != NULL) {
+ if ((cr = strchr(inbuf, '\n')) != NULL)
+ *cr = '\0';
+ msg_massage(inbuf, outbuf, sizeof(outbuf));
+ lreply(msgcode, "%s", outbuf);
+ }
+ fclose(infile);
+ }
+ }
+}
+
+/*************************************************************************/
+/* FUNCTION : access_init */
+/* PURPOSE : Read and parse the access lists to set things up */
+/* ARGUMENTS : none */
+/*************************************************************************/
+
+void access_init(void)
+{
+ struct aclmember *entry;
+ static struct stat sbuf_last;
+ struct stat sbuf_cur;
+
+ if (!use_accessfile)
+ return;
+
+ if (stat(_path_ftpaccess, &sbuf_cur) != 0) {
+ syslog(LOG_ERR, "cannot stat access file %s: %s", _path_ftpaccess,
+ strerror(errno));
+ return;
+ }
+ /* only reload the ftpaccess file if its changed */
+ if ((sbuf_last.st_mtime == sbuf_cur.st_mtime) &&
+ (sbuf_last.st_ino == sbuf_cur.st_ino) &&
+ (sbuf_last.st_dev == sbuf_cur.st_dev))
+ return;
+
+ sbuf_last = sbuf_cur;
+
+#ifdef OTHER_PASSWD
+ strcpy(_path_passwd, "/etc/passwd");
+#ifdef SHADOW_PASSWORD
+ strcpy(_path_shadow, "/etc/shadow");
+#endif
+#endif
+#if defined(USE_PAM) && defined(OTHER_PASSWD)
+ use_pam = 1;
+#endif
+ Shutdown[0] = '\0';
+ keepalive = 0;
+
+ if (!readacl(_path_ftpaccess))
+ return;
+ (void) parseacl();
+
+ entry = (struct aclmember *) NULL;
+ if (getaclentry("shutdown", &entry) && ARG0 != NULL)
+ (void) strncpy(Shutdown, ARG0, sizeof(Shutdown));
+#ifdef OTHER_PASSWD
+ entry = (struct aclmember *) NULL;
+ while (getaclentry("passwd", &entry) && ARG0 != NULL) {
+ strcpy(_path_passwd, ARG0);
+#ifdef USE_PAM
+ use_pam = 0;
+#endif
+ }
+#ifdef SHADOW_PASSWORD
+ entry = (struct aclmember *) NULL;
+ while (getaclentry("shadow", &entry) && ARG0 != NULL) {
+ strcpy(_path_shadow, ARG0);
+#ifdef USE_PAM
+ use_pam = 0;
+#endif
+ }
+#endif
+#endif
+ entry = (struct aclmember *) NULL;
+ if (getaclentry("keepalive", &entry) && ARG0 != NULL)
+ if (!strcasecmp(ARG0, "yes"))
+ keepalive = 1;
+}
+
+/*************************************************************************/
+/* FUNCTION : access_ok */
+/* PURPOSE : Check the anonymous FTP access lists to see if this */
+/* access is permitted. */
+/* ARGUMENTS : reply code to use */
+/*************************************************************************/
+
+int access_ok(int msgcode)
+{
+ char class[BUFSIZ], msgfile[MAXPATHLEN];
+ int limit;
+ int nice_delta;
+
+ if (!use_accessfile)
+ return (1);
+
+ if (aclbuf == NULL) {
+ syslog(LOG_NOTICE,
+ "ACCESS DENIED (error reading access file) TO %s",
+ remoteident);
+ return (0);
+ }
+ if (acl_deny(msgfile)) {
+#ifndef HELP_CRACKERS
+ memcpy(DelayedMessageFile, msgfile, sizeof(msgfile));
+#else
+ pr_mesg(msgcode, msgfile);
+#endif
+ syslog(LOG_NOTICE, "ACCESS DENIED (deny command) TO %s",
+ remoteident);
+ return (0);
+ }
+ /* if user is not in any class, deny access */
+ if (!acl_getclass(class)) {
+ syslog(LOG_NOTICE, "ACCESS DENIED (not in any class) TO %s",
+ remoteident);
+ return (0);
+ }
+
+ limit = acl_getlimit(class, msgfile);
+ if (acl_join(class, limit) < 0) {
+#ifdef LOG_TOOMANY
+ syslog(LOG_NOTICE, "ACCESS DENIED (user limit %d; class %s) TO %s",
+ limit, class, remoteident);
+#endif
+#ifndef HELP_CRACKERS
+ memcpy(DelayedMessageFile, msgfile, sizeof(msgfile));
+#else
+ pr_mesg(msgcode, msgfile);
+#endif
+ return (-1);
+ }
+
+ if ((nice_delta = acl_getnice(class))) {
+ if (nice_delta < 0)
+ syslog(LOG_NOTICE, "Process nice value adjusted by %d", nice_delta);
+ nice(nice_delta);
+ }
+ acl_getdefumask(class);
+ acl_tcpwindow(class);
+#ifdef TRANSFER_COUNT
+#ifdef TRANSFER_LIMIT
+ acl_filelimit(class);
+ acl_datalimit(class);
+#ifdef RATIO
+ acl_downloadrate(class);
+#endif
+#endif
+#endif
+ acl_bufsize();
+ get_xferlog_format();
+ return (1);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/acl.c b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/acl.c
new file mode 100644
index 0000000000..5df3f3db47
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/acl.c
@@ -0,0 +1,195 @@
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/****************************************************************************
+
+ Copyright (c) 1999,2000 WU-FTPD Development Group.
+ All rights reserved.
+
+ Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994
+ The Regents of the University of California.
+ Portions Copyright (c) 1993, 1994 Washington University in Saint Louis.
+ Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc.
+ Portions Copyright (c) 1989 Massachusetts Institute of Technology.
+ Portions Copyright (c) 1998 Sendmail, Inc.
+ Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P. Allman.
+ Portions Copyright (c) 1997 by Stan Barber.
+ Portions Copyright (c) 1997 by Kent Landfield.
+ Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ Free Software Foundation, Inc.
+
+ Use and distribution of this software and its source code are governed
+ by the terms and conditions of the WU-FTPD Software License ("LICENSE").
+
+ If you did not receive a copy of the license, it may be obtained online
+ at http://www.wu-ftpd.org/license.html.
+
+ $Id: acl.c,v 1.9 2000/07/01 18:17:38 wuftpd Exp $
+
+****************************************************************************/
+#include "config.h"
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#ifdef HAVE_SYS_SYSLOG_H
+#include <sys/syslog.h>
+#endif
+#if defined(HAVE_SYSLOG_H) || (!defined(AUTOCONF) && !defined(HAVE_SYS_SYSLOG_H))
+#include <syslog.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+
+#ifdef HAVE_PATHS_H
+#include <paths.h>
+#endif
+#include "pathnames.h"
+#include "extensions.h"
+#include "proto.h"
+
+char *aclbuf = NULL;
+static struct aclmember *aclmembers;
+
+/*************************************************************************/
+/* FUNCTION : getaclentry */
+/* PURPOSE : Retrieve a named entry from the ACL */
+/* ARGUMENTS : pointer to the keyword and a handle to the acl members */
+/* RETURNS : pointer to the acl member containing the keyword or NULL */
+/*************************************************************************/
+
+struct aclmember *getaclentry(char *keyword, struct aclmember **next)
+{
+ do {
+ if (!*next)
+ *next = aclmembers;
+ else
+ *next = (*next)->next;
+ } while (*next && strcasecmp((*next)->keyword, keyword));
+
+ return (*next);
+}
+
+/*************************************************************************/
+/* FUNCTION : parseacl */
+/* PURPOSE : Parse the acl buffer into its components */
+/* ARGUMENTS : A pointer to the acl file */
+/* RETURNS : nothing */
+/*************************************************************************/
+
+void parseacl(void)
+{
+ char *ptr, *aclptr = aclbuf, *line;
+ int cnt;
+ struct aclmember *member, *acltail;
+
+ if (!aclbuf || !(*aclbuf))
+ return;
+
+ aclmembers = (struct aclmember *) NULL;
+ acltail = (struct aclmember *) NULL;
+
+ while (*aclptr != '\0') {
+ line = aclptr;
+ while (*aclptr && *aclptr != '\n')
+ aclptr++;
+ *aclptr++ = (char) NULL;
+
+ /* deal with comments */
+ if ((ptr = strchr(line, '#')) != NULL)
+ /* allowed escaped '#' chars for path-filter (DiB) */
+ if ((ptr > aclbuf) && (*(ptr - 1) != '\\'))
+ *ptr = '\0';
+
+ ptr = strtok(line, " \t");
+ if (ptr) {
+ member = (struct aclmember *) calloc(1, sizeof(struct aclmember));
+
+ if (member == NULL) {
+ syslog(LOG_ERR, "calloc error parsing acl");
+ exit(1);
+ }
+ (void) strncpy(member->keyword, ptr, MAXKWLEN);
+ member->keyword[MAXKWLEN - 1] = '\0';
+ cnt = 0;
+ while ((ptr = strtok(NULL, " \t")) != NULL) {
+ if (cnt >= MAXARGS) {
+ syslog(LOG_ERR,
+ "Too many args (>%d) in ftpaccess: %s %s %s %s %s ...",
+ MAXARGS - 1, member->keyword, member->arg[0],
+ member->arg[1], member->arg[2], member->arg[3]);
+ break;
+ }
+ member->arg[cnt++] = ptr;
+ }
+ if (acltail)
+ acltail->next = member;
+ acltail = member;
+ if (!aclmembers)
+ aclmembers = member;
+ }
+ }
+}
+
+/*************************************************************************/
+/* FUNCTION : readacl */
+/* PURPOSE : Read the acl into memory */
+/* ARGUMENTS : The pathname of the acl */
+/* RETURNS : 0 if error, 1 if no error */
+/*************************************************************************/
+
+int readacl(char *aclpath)
+{
+ FILE *aclfile;
+ struct stat finfo;
+ struct aclmember *member;
+ extern int use_accessfile;
+
+ if (!use_accessfile)
+ return (0);
+
+ while (aclmembers) {
+ member = aclmembers->next;
+ free(aclmembers);
+ aclmembers = member;
+ }
+
+ if (aclbuf) {
+ free(aclbuf);
+ aclbuf = NULL;
+ }
+
+ if ((aclfile = fopen(aclpath, "r")) == NULL) {
+ syslog(LOG_ERR, "cannot open access file %s: %s", aclpath,
+ strerror(errno));
+ return (0);
+ }
+ if (fstat(fileno(aclfile), &finfo) != 0) {
+ syslog(LOG_ERR, "cannot fstat access file %s: %s", aclpath,
+ strerror(errno));
+ (void) fclose(aclfile);
+ return (0);
+ }
+ if (finfo.st_size == 0) {
+ aclbuf = (char *) calloc(1, 1);
+ }
+ else {
+ if (!(aclbuf = (char *) malloc((size_t) finfo.st_size + 1))) {
+ syslog(LOG_ERR, "could not malloc aclbuf (%d bytes)", (size_t) finfo.st_size + 1);
+ (void) fclose(aclfile);
+ return (0);
+ }
+ if (!fread(aclbuf, (size_t) finfo.st_size, 1, aclfile)) {
+ syslog(LOG_ERR, "error reading acl file %s: %s", aclpath,
+ strerror(errno));
+ free(aclbuf);
+ aclbuf = NULL;
+ (void) fclose(aclfile);
+ return (0);
+ }
+ *(aclbuf + finfo.st_size) = '\0';
+ }
+ (void) fclose(aclfile);
+ return (1);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/authenticate.c b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/authenticate.c
new file mode 100644
index 0000000000..8297eeeb9d
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/authenticate.c
@@ -0,0 +1,78 @@
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/****************************************************************************
+
+ Copyright (c) 1999,2000 WU-FTPD Development Group.
+ All rights reserved.
+
+ Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994
+ The Regents of the University of California.
+ Portions Copyright (c) 1993, 1994 Washington University in Saint Louis.
+ Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc.
+ Portions Copyright (c) 1989 Massachusetts Institute of Technology.
+ Portions Copyright (c) 1998 Sendmail, Inc.
+ Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P. Allman.
+ Portions Copyright (c) 1997 by Stan Barber.
+ Portions Copyright (c) 1997 by Kent Landfield.
+ Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ Free Software Foundation, Inc.
+
+ Use and distribution of this software and its source code are governed
+ by the terms and conditions of the WU-FTPD Software License ("LICENSE").
+
+ If you did not receive a copy of the license, it may be obtained online
+ at http://www.wu-ftpd.org/license.html.
+
+ $Id: authenticate.c,v 1.9 2000/07/01 18:17:38 wuftpd Exp $
+
+****************************************************************************/
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+#include "authuser.h"
+#include "authenticate.h"
+#include "proto.h"
+
+#define AUTHNAMESIZE 100
+
+char authuser[AUTHNAMESIZE];
+int authenticated;
+
+extern int disable_rfc931;
+extern unsigned int timeout_rfc931;
+
+/*
+ * Ideally more authentication schemes would be called from here, with the
+ * strongest called first. One possible double-check would be to verify that
+ * the results of all authentication calls (returning identical data!) are
+ * checked against each other.
+ */
+int wu_authenticate(void)
+{
+ char *user;
+#if USE_A_RFC931
+ unsigned long in;
+ unsigned short local, remote;
+#endif /* USE_A_RFC931 */
+
+ authenticated = 0; /* this is a bitmask, one bit per method */
+
+ user = "*";
+
+#if USE_A_RFC931
+ if (disable_rfc931 || (timeout_rfc931 == 0))
+ user = "*";
+ else if (auth_fd(0, &in, &local, &remote) == -1)
+ user = "?"; /* getpeername/getsockname failure */
+ else {
+ if (!(user = auth_tcpuser(in, local, remote)))
+ user = "*"; /* remote host doesn't support RFC 931 */
+ else
+ authenticated |= A_RFC931;
+ }
+#endif /* USE_A_RFC931 */
+
+ strncpy(authuser, user, sizeof(authuser));
+ authuser[AUTHNAMESIZE - 1] = '\0';
+ return (0);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/authenticate.h b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/authenticate.h
new file mode 100644
index 0000000000..2dab5b10c0
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/authenticate.h
@@ -0,0 +1,40 @@
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/****************************************************************************
+ Copyright (c) 1999,2000 WU-FTPD Development Group.
+ All rights reserved.
+
+ Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994
+ The Regents of the University of California.
+ Portions Copyright (c) 1993, 1994 Washington University in Saint Louis.
+ Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc.
+ Portions Copyright (c) 1989 Massachusetts Institute of Technology.
+ Portions Copyright (c) 1998 Sendmail, Inc.
+ Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P. Allman.
+ Portions Copyright (c) 1997 by Stan Barber.
+ Portions Copyright (c) 1997 by Kent Landfield.
+ Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ Free Software Foundation, Inc.
+
+ Use and distribution of this software and its source code are governed
+ by the terms and conditions of the WU-FTPD Software License ("LICENSE").
+
+ If you did not receive a copy of the license, it may be obtained online
+ at http://www.wu-ftpd.org/license.html.
+
+ $Id: authenticate.h,v 1.8 2000/07/01 18:17:38 wuftpd Exp $
+
+****************************************************************************/
+/*
+ * When of the supported authentication methods the ftp server will attempt
+ * to use. Define as 1 to enable, 0 to disable.
+ */
+
+#ifdef USE_RFC931
+#define USE_A_RFC931 1 /* Use RFC931-style authentication */
+#else
+#define USE_A_RFC931 0 /* No RFC931-style authentication */
+#endif
+
+/* Bitmasks used to identify authentication methods that returned a result */
+#define A_RFC931 1 << 0; /* RFC931 */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/authuser.h b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/authuser.h
new file mode 100644
index 0000000000..d702f8100a
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/authuser.h
@@ -0,0 +1,39 @@
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/****************************************************************************
+
+ Copyright (c) 1999,2000 WU-FTPD Development Group.
+ All rights reserved.
+
+ Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994
+ The Regents of the University of California.
+ Portions Copyright (c) 1993, 1994 Washington University in Saint Louis.
+ Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc.
+ Portions Copyright (c) 1989 Massachusetts Institute of Technology.
+ Portions Copyright (c) 1998 Sendmail, Inc.
+ Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P. Allman.
+ Portions Copyright (c) 1997 by Stan Barber.
+ Portions Copyright (c) 1997 by Kent Landfield.
+ Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ Free Software Foundation, Inc.
+
+ Use and distribution of this software and its source code are governed
+ by the terms and conditions of the WU-FTPD Software License ("LICENSE").
+
+ If you did not receive a copy of the license, it may be obtained online
+ at http://www.wu-ftpd.org/license.html.
+
+ $Id: authuser.h,v 1.4 2000/07/01 18:36:28 wuftpd Exp $
+
+****************************************************************************/
+#ifndef AUTHUSER_H
+#define AUTHUSER_H
+
+extern unsigned short auth_tcpport;
+
+extern char *auth_xline(register char *user, register int fd, register long unsigned int *in);
+
+extern int auth_fd(register int fd, register long unsigned int *in, register short unsigned int *local, register short unsigned int *remote);
+
+extern char *auth_tcpuser(register long unsigned int in, register short unsigned int local, register short unsigned int remote);
+#endif
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ckconfig.c b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ckconfig.c
new file mode 100644
index 0000000000..208442e5f3
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ckconfig.c
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/****************************************************************************
+
+ Copyright (c) 1999,2000 WU-FTPD Development Group.
+ All rights reserved.
+
+ Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994
+ The Regents of the University of California.
+ Portions Copyright (c) 1993, 1994 Washington University in Saint Louis.
+ Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc.
+ Portions Copyright (c) 1989 Massachusetts Institute of Technology.
+ Portions Copyright (c) 1998 Sendmail, Inc.
+ Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P. Allman.
+ Portions Copyright (c) 1997 by Stan Barber.
+ Portions Copyright (c) 1997 by Kent Landfield.
+ Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ Free Software Foundation, Inc.
+
+ Use and distribution of this software and its source code are governed
+ by the terms and conditions of the WU-FTPD Software License ("LICENSE").
+
+ If you did not receive a copy of the license, it may be obtained online
+ at http://www.wu-ftpd.org/license.html.
+
+ $Id: ckconfig.c,v 1.10 2000/07/01 18:17:38 wuftpd Exp $
+
+****************************************************************************/
+#include "config.h"
+#ifndef HOST_ACCESS
+#define HOST_ACCESS 1
+#endif
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include "pathnames.h"
+#if defined(VIRTUAL) && defined(INET6)
+#include <netinet/in.h>
+#endif
+
+/* Prototypes */
+#ifdef VIRTUAL
+extern int read_servers_line(FILE *, char *, size_t, char *, size_t);
+#endif
+void print_copyright(void);
+
+int main(int argc, char **argv)
+{
+ struct stat sbuf;
+ char *sp;
+ char buf[1024];
+ int c;
+
+#ifdef VIRTUAL
+ FILE *svrfp;
+ char accesspath[MAXPATHLEN];
+#ifdef INET6
+ char hostaddress[INET6_ADDRSTRLEN];
+#else
+ char hostaddress[32];
+#endif
+#endif
+
+ if (argc > 1) {
+ while ((c = getopt(argc, argv, "V")) != EOF) {
+ switch (c) {
+ case 'V':
+ print_copyright();
+ exit(0);
+ default:
+ fprintf(stderr, "usage: %s [-V]\n", argv[0]);
+ exit(1);
+ }
+ }
+ }
+
+ /* _PATH_FTPUSERS */
+ fprintf(stdout, "Checking _PATH_FTPUSERS :: %s\n", _PATH_FTPUSERS);
+ if ((stat(_PATH_FTPUSERS, &sbuf)) < 0)
+ printf("I can't find it... look in doc/examples for an example.\n");
+ else
+ printf("ok.\n");
+
+#ifdef VIRTUAL
+
+ /* _PATH_FTPSERVERS */
+ fprintf(stdout, "\nChecking _PATH_FTPSERVERS :: %s\n", _PATH_FTPSERVERS);
+ if ((stat(_PATH_FTPSERVERS, &sbuf)) < 0)
+ printf("I can't find it... look in doc/examples for an example.\n");
+ else {
+ printf("ok.\n");
+ /* Need to check the access files specified in the ftpservers file. */
+ if ((svrfp = fopen(_PATH_FTPSERVERS, "r")) == NULL)
+ printf("I can't open it! check permissions and run ckconfig again.\n");
+ else {
+ while (read_servers_line(svrfp, hostaddress, sizeof(hostaddress),
+ accesspath, sizeof(accesspath)) == 1) {
+ fprintf(stderr, "\nChecking accessfile for %s :: %s\n", hostaddress, accesspath);
+ /*
+ ** check to see that a valid directory value was
+ ** supplied and not something such as "INTERNAL"
+ **
+ ** It is valid to have a string such as "INTERNAL" in the
+ ** ftpservers entry. This is not an error. Silently ignore it.
+ */
+ if (stat(accesspath, &sbuf) == 0) {
+ if ((sbuf.st_mode & S_IFMT) == S_IFDIR)
+ printf("ok.\n");
+ else {
+ printf("Check servers file and make sure only directories are listed...\n");
+ printf("look in doc/examples for an example.\n");
+ }
+ }
+ else
+ printf("Internal ftpaccess usage specified... ok.\n");
+ }
+ fclose(svrfp);
+ }
+ }
+#endif
+
+ /* _PATH_FTPACCESS */
+ fprintf(stdout, "\nChecking _PATH_FTPACCESS :: %s\n", _PATH_FTPACCESS);
+ if ((stat(_PATH_FTPACCESS, &sbuf)) < 0)
+ printf("I can't find it... look in doc/examples for an example.\n");
+ else
+ printf("ok.\n");
+
+ /* _PATH_PIDNAMES */
+ fprintf(stdout, "\nChecking _PATH_PIDNAMES :: %s\n", _PATH_PIDNAMES);
+ (void) strlcpy(buf, _PATH_PIDNAMES, sizeof(buf));
+ sp = (char *) strrchr(buf, '/');
+ *sp = '\0';
+ if ((stat(buf, &sbuf)) < 0) {
+ printf("I can't find it...\n");
+ printf("You need to make this directory [%s] in order for\n", buf);
+ printf("the limit and user count functions to work.\n");
+ }
+ else
+ printf("ok.\n");
+
+ /* _PATH_CVT */
+ fprintf(stdout, "\nChecking _PATH_CVT :: %s\n", _PATH_CVT);
+ if ((stat(_PATH_CVT, &sbuf)) < 0)
+ printf("I can't find it... look in doc/examples for an example.\n");
+ else
+ printf("ok.\n");
+
+ /* _PATH_XFERLOG */
+ fprintf(stdout, "\nChecking _PATH_XFERLOG :: %s\n", _PATH_XFERLOG);
+ if ((stat(_PATH_XFERLOG, &sbuf)) < 0) {
+ printf("I can't find it... \n");
+ printf("Don't worry, it will be created automatically by the\n");
+ printf("server if you do transfer logging.\n");
+ }
+ else
+ printf("ok.\n");
+
+ /* _PATH_PRIVATE */
+ fprintf(stdout, "\nChecking _PATH_PRIVATE :: %s\n", _PATH_PRIVATE);
+ if ((stat(_PATH_PRIVATE, &sbuf)) < 0) {
+ printf("I can't find it... look in doc/examples for an example.\n");
+ printf("You only need this if you want SITE GROUP and SITE GPASS\n");
+ printf("functionality. If you do, you will need to edit the example.\n");
+ }
+ else
+ printf("ok.\n");
+
+ /* _PATH_FTPHOSTS */
+ fprintf(stdout, "\nChecking _PATH_FTPHOSTS :: %s\n", _PATH_FTPHOSTS);
+ if ((stat(_PATH_FTPHOSTS, &sbuf)) < 0) {
+ printf("I can't find it... look in doc/examples for an example.\n");
+ printf("You only need this if you are using the HOST ACCESS features\n");
+ printf("of the server.\n");
+ }
+ else
+ printf("ok.\n");
+ return (0);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/config.h b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/config.h
new file mode 100644
index 0000000000..7aa7c1af20
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/config.h
@@ -0,0 +1,268 @@
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/* src/config.h. Generated automatically by configure. */
+/****************************************************************************
+
+ Copyright (c) 1999,2000 WU-FTPD Development Group.
+ All rights reserved.
+
+ Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994
+ The Regents of the University of California.
+ Portions Copyright (c) 1993, 1994 Washington University in Saint Louis.
+ Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc.
+ Portions Copyright (c) 1989 Massachusetts Institute of Technology.
+ Portions Copyright (c) 1998 Sendmail, Inc.
+ Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P. Allman.
+ Portions Copyright (c) 1997 by Stan Barber.
+ Portions Copyright (c) 1997 by Kent Landfield.
+ Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ Free Software Foundation, Inc.
+
+ Use and distribution of this software and its source code are governed
+ by the terms and conditions of the WU-FTPD Software License ("LICENSE").
+
+ If you did not receive a copy of the license, it may be obtained online
+ at http://www.wu-ftpd.org/license.html.
+
+ $Id: config.h.in,v 1.31 2000/07/01 18:04:21 wuftpd Exp $
+
+****************************************************************************/
+
+#define SOLARIS_2
+#define SVR4
+#define HAVE_STATVFS
+#define NO_UTMP
+#define HAVE_FGETPWENT
+#define HAVE_MKSTEMP
+#define HAVE_SYS_SENDFILE_H
+
+/*
+ * Configuration file for autoconf - will be modified by configure
+ */
+
+#define HAVE_FCNTL_H 1
+#define HAVE_DIRENT_H 1
+#define HAVE_REGEX_H 1
+#define TIME_WITH_SYS_TIME 1
+/* #undef HAVE_SYS_TIME_H */
+/* #undef HAVE_TIME_H */
+/* #undef HAVE_MNTENT_H */
+#define HAVE_SYS_MNTENT_H 1
+#define HAVE_SYS_MNTTAB_H 1
+/* #undef HAVE_NDIR_H */
+#define HAVE_STRING_H 1
+/* #undef HAVE_SYS_DIR_H */
+/* #undef HAVE_SYS_NDIR_H */
+/* #undef HAVE_SYS_QUOTA_H */
+#define HAVE_SYS_FS_UFS_QUOTA_H 1
+/* #undef HAVE_UFS_QUOTA_H */
+/* #undef HAVE_JFS_QUOTA_H */
+/* #undef HAVE_UFS_UFS_QUOTA_H */
+/* #undef HAVE_LINUX_QUOTA_H */
+#define HAVE_STDLIB_H 1
+#define HAVE_UNISTD_H 1
+#define HAVE_SYS_TYPES_H 1
+#define HAVE_GLOB_H 1
+#define HAVE_GRP_H 1
+#define HAVE_SHADOW_H 1
+/* #undef HAVE_VMSDIR_H */
+
+/* #undef QUOTA_INODE */
+#define QUOTA_DEVICE
+#define QSORT_IS_VOID 1
+
+#define HAVE_SIGPROCMASK 1
+#define HAVE_VSNPRINTF 1
+/* #undef HAVE_DIRFD */
+/* #undef HAVE_FLOCK */
+#define HAVE_FTW 1
+#define HAVE_GETCWD 1
+#define HAVE_GETDTABLESIZE 1
+#define HAVE_GETRLIMIT 1
+/* #undef HAVE_PSTAT */
+#define HAVE_LSTAT 1
+#define HAVE_VPRINTF 1
+#define HAVE_SNPRINTF 1
+#define HAVE_REGEX 1
+#define HAVE_REGEXEC 1
+#define HAVE_SETSID 1
+#define HAVE_MEMMOVE 1
+#define HAVE_STRTOUL 1
+/* #undef HAVE_SIGLIST */
+#define FACILITY LOG_DAEMON
+
+#define HAVE_LIMITS_H 1
+#define HAVE_VALUES_H 1
+/* #undef HAVE_BSD_BSD_H */
+#define HAVE_SYS_PARAM_H 1
+/* #undef NEED_LIMITS_H */
+/* #undef NEED_VALUES_H */
+/* #undef NEED_BSD_BSD_H */
+#define NEED_SYS_PARAM_H 1
+#if defined(HAVE_SYS_PARAM_H) && defined(NEED_SYS_PARAM_H)
+#include <sys/param.h>
+#endif
+#if defined(HAVE_VALUES_H) && defined(NEED_VALUES_H)
+#include <values.h>
+#endif
+#if defined(HAVE_LIMITS_H) && defined(NEED_LIMITS_H)
+#include <limits.h>
+#endif
+#if defined(HAVE_BSD_BSD_H) && defined(NEED_BSD_BSD_H)
+#include <bsd/bsd.h>
+#endif
+/* #undef NBBY */
+
+#define SIGNAL_TYPE void
+#define HAVE_SETUID 1
+#define HAVE_SETEUID 1
+/* #undef HAVE_SETREUID */
+/* #undef HAVE_SETRESUID */
+#define HAVE_SETEGID 1
+/* #undef HAVE_SETREGID */
+/* #undef HAVE_SETRESGID */
+#define HAVE_ST_BLKSIZE 1
+#define HAVE_SYSCONF 1
+#define HAVE_SYS_SYSTEMINFO_H 1
+/* #undef HAVE_PATHS_H */
+#define HAVE_SYSLOG_H 1
+#define HAVE_SYS_SYSLOG_H 1
+#define HAVE_FCHDIR 1
+/* #undef HAVE_QUOTACTL */
+#define HAS_OLDSTYLE_GETMNTENT
+/* #undef HAS_PW_EXPIRE */
+#define SHADOW_PASSWORD 1
+#define AUTOCONF 1
+#if _FILE_OFFSET_BITS == 64
+#define L_FORMAT "lld"
+#else
+#define L_FORMAT "ld"
+#endif
+#define T_FORMAT "ld"
+#define PW_UID_FORMAT "ld"
+#define GR_GID_FORMAT "ld"
+
+/* #undef HAVE_UT_UT_HOST */
+#define HAVE_UT_UT_EXIT_E_TERMINATION 1
+
+/* Here instead of everywhere: */
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+/* Newer systems will have seteuid/setegid */
+/* Older systems may have the BSD setreuid/setregid */
+/* HP/UX has setresuid/setresgid */
+/* Some SCO systems appearently have none of this.
+ so if HAVE_SETUID is not defined we'll presume it's
+ all needed since we're compiling support/sco.c */
+
+#ifdef HAVE_SETUID
+
+#ifndef HAVE_SETEUID
+#ifdef HAVE_SETREUID
+#define seteuid(euid) setreuid(-1,(euid))
+#else
+#ifdef HAVE_SETRESUID
+#define seteuid(euid) setresuid(-1,(euid),-1)
+#else
+#error No seteuid() functions.
+#endif
+#endif
+#endif
+
+#ifndef HAVE_SETEGID
+#ifdef HAVE_SETREGID
+#define setegid(egid) setregid(-1,(egid))
+#else
+#ifdef HAVE_SETRESGID
+#define setegid(egid) setresgid(-1,(egid),-1)
+#else
+#error No setegid() functions.
+#endif
+#endif
+#endif
+
+#endif /* HAVE_SETUID */
+
+#ifndef HAVE_FCHDIR
+#define HAS_NO_FCHDIR 1
+#endif
+#ifndef HAVE_QUOTACTL
+#define HAS_NO_QUOTACTL
+#endif
+#ifdef HAVE_SYS_SYSTEMINFO_H
+#define HAVE_SYSINFO 1
+#endif
+#ifndef HAVE_SETSID
+#define NO_SETSID 1
+#endif
+
+#ifndef HAVE_MEMMOVE
+#define memmove(a,b,c) bcopy(b,a,c)
+#endif
+#ifndef HAVE_STRTOUL
+#define strtoul(a,b,c) (unsigned long)strtol(a,b,c)
+#endif
+
+#ifndef RAND_MAX
+#define RAND_MAX 2147483647
+#endif
+
+#define USE_PAM 1
+
+/*
+ * Socket macros which help with socket structure manipulation in a mixed
+ * IPv4 / IPv6 environment.
+ */
+#ifdef INET6
+#define HAVE_SIN6_SCOPE_ID
+#ifdef HAVE__SS_FAMILY
+#define ss_family __ss_family
+#endif
+#define SOCKSTORAGE sockaddr_storage
+#define SOCK_FAMILY(ss) ((ss).ss_family)
+#define SOCK_PORT(ss) ((ss).ss_family == AF_INET6 ? \
+ ((struct sockaddr_in6 *)&(ss))->sin6_port : \
+ ((struct sockaddr_in *)&(ss))->sin_port)
+#define SOCK_LEN(ss) ((ss).ss_family == AF_INET6 ? \
+ sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in))
+#define SOCK_ADDR(ss) ((ss).ss_family == AF_INET6 ? \
+ (void *)&((struct sockaddr_in6 *)&(ss))->sin6_addr : \
+ (void *)&((struct sockaddr_in *)&(ss))->sin_addr)
+#define SET_SOCK_FAMILY(ss, family) (SOCK_FAMILY(ss) = (family))
+#define SET_SOCK_PORT(ss, port) \
+ ((ss).ss_family == AF_INET6 ? \
+ (((struct sockaddr_in6 *)&(ss))->sin6_port = (port)) : \
+ (((struct sockaddr_in *)&(ss))->sin_port = (port)))
+#define SET_SOCK_ADDR4(ss, addr) ((void)(sock_set_inaddr(&(ss), (addr))))
+#define SET_SOCK_ADDR_ANY(ss) \
+ ((void)((ss).ss_family == AF_INET6 ? \
+ (void)(((struct sockaddr_in6 *)&(ss))->sin6_addr = \
+ in6addr_any) : \
+ (void)(((struct sockaddr_in *)&(ss))->sin_addr.s_addr = \
+ htonl(INADDR_ANY))))
+#define SET_SOCK_SCOPE(dst, src) sock_set_scope(&(dst), &(src))
+#else
+#define SOCKSTORAGE sockaddr_in
+#define SOCK_FAMILY(sin) ((sin).sin_family)
+#define SOCK_PORT(sin) ((sin).sin_port)
+#define SOCK_LEN(sin) (sizeof(sin))
+#define SOCK_ADDR(sin) ((void *)&(sin).sin_addr)
+#define SET_SOCK_FAMILY(sin, family) (SOCK_FAMILY(sin) = (family))
+#define SET_SOCK_PORT(sin, port) ((sin).sin_port = (port))
+#define SET_SOCK_ADDR4(sin, addr) ((sin).sin_addr = (addr))
+#define SET_SOCK_ADDR_ANY(sin) ((sin).sin_addr.s_addr = htonl(INADDR_ANY))
+#endif /* INET6 */
+
+#define delay_signaling()
+#define enable_signaling()
+
+#include "wu_config.h"
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/conversions.c b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/conversions.c
new file mode 100644
index 0000000000..3feabde024
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/conversions.c
@@ -0,0 +1,198 @@
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/****************************************************************************
+
+ Copyright (c) 1999,2000 WU-FTPD Development Group.
+ All rights reserved.
+
+ Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994
+ The Regents of the University of California.
+ Portions Copyright (c) 1993, 1994 Washington University in Saint Louis.
+ Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc.
+ Portions Copyright (c) 1989 Massachusetts Institute of Technology.
+ Portions Copyright (c) 1998 Sendmail, Inc.
+ Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P. Allman.
+ Portions Copyright (c) 1997 by Stan Barber.
+ Portions Copyright (c) 1997 by Kent Landfield.
+ Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ Free Software Foundation, Inc.
+
+ Use and distribution of this software and its source code are governed
+ by the terms and conditions of the WU-FTPD Software License ("LICENSE").
+
+ If you did not receive a copy of the license, it may be obtained online
+ at http://www.wu-ftpd.org/license.html.
+
+ $Id: conversions.c,v 1.10 2000/07/01 18:17:38 wuftpd Exp $
+
+****************************************************************************/
+#include "config.h"
+
+#include <stdio.h>
+#include <errno.h>
+#ifdef HAVE_SYS_SYSLOG_H
+#include <sys/syslog.h>
+#endif
+#if defined(HAVE_SYSLOG_H) || (!defined(AUTOCONF) && !defined(HAVE_SYS_SYSLOG_H))
+#include <syslog.h>
+#endif
+
+extern char *strsep(char **, const char *);
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "conversions.h"
+#include "extensions.h"
+#include "pathnames.h"
+#include "proto.h"
+
+/*************************************************************************/
+/* FUNCTION : readconv */
+/* PURPOSE : Read the conversions into memory */
+/* ARGUMENTS : The pathname of the conversion file */
+/* RETURNS : 0 if error, 1 if no error */
+/*************************************************************************/
+
+char *convbuf = NULL;
+struct convert *cvtptr;
+
+struct str2int {
+ char *string;
+ int value;
+};
+
+struct str2int c_list[] =
+{
+ {"T_REG", T_REG},
+ {"T_ASCII", T_ASCII},
+ {"T_DIR", T_DIR},
+ {"O_COMPRESS", O_COMPRESS},
+ {"O_UNCOMPRESS", O_UNCOMPRESS},
+ {"O_TAR", O_TAR},
+ {NULL, 0},
+};
+
+static int conv(char *str)
+{
+ int rc = 0;
+ int counter;
+
+ /* check for presence of ALL items in string... */
+
+ if (str)
+ for (counter = 0; c_list[counter].string; ++counter)
+ if (strstr(str, c_list[counter].string))
+ rc = rc | c_list[counter].value;
+ return (rc);
+}
+
+static int readconv(char *convpath)
+{
+ FILE *convfile;
+ struct stat finfo;
+
+ if ((convfile = fopen(convpath, "r")) == NULL) {
+ if (errno != ENOENT)
+ syslog(LOG_ERR, "cannot open conversion file %s: %s",
+ convpath, strerror(errno));
+ return (0);
+ }
+ if (fstat(fileno(convfile), &finfo) != 0) {
+ syslog(LOG_ERR, "cannot fstat conversion file %s: %s", convpath,
+ strerror(errno));
+ (void) fclose(convfile);
+ return (0);
+ }
+ if (finfo.st_size == 0) {
+ convbuf = (char *) calloc(1, 1);
+ }
+ else {
+ if (!(convbuf = (char *) malloc((size_t) finfo.st_size + 1))) {
+ syslog(LOG_ERR, "could not malloc convbuf (%d bytes)", (size_t) finfo.st_size + 1);
+ (void) fclose(convfile);
+ return (0);
+ }
+ if (!fread(convbuf, (size_t) finfo.st_size, 1, convfile)) {
+ syslog(LOG_ERR, "error reading conv file %s: %s", convpath,
+ strerror(errno));
+ convbuf = NULL;
+ (void) fclose(convfile);
+ return (0);
+ }
+ *(convbuf + finfo.st_size) = '\0';
+ }
+ (void) fclose(convfile);
+ return (1);
+}
+
+static void parseconv(void)
+{
+ char *ptr;
+ char *convptr = convbuf, *line;
+ char *argv[8], *p, *val;
+ struct convert *cptr, *cvttail = (struct convert *) NULL;
+ int n;
+
+ if (!convbuf || !(*convbuf))
+ return;
+
+ /* read through convbuf, stripping comments. */
+ while (*convptr != '\0') {
+ line = convptr;
+ while (*convptr && *convptr != '\n')
+ convptr++;
+ *convptr++ = '\0';
+
+ /* deal with comments */
+ if ((ptr = strchr(line, '#')) != NULL)
+ *ptr = '\0';
+
+ if (*line == '\0')
+ continue;
+
+ /* parse the lines... */
+ for (n = 0, p = line; n < 8 && p != NULL; n++) {
+ val = (char *) strsep(&p, ":\n");
+ argv[n] = val;
+ if ((argv[n][0] == ' ') || (argv[n][0] == '\0'))
+ argv[n] = NULL;
+ }
+ /* check their were 8 fields, if not skip the line... */
+ if (n != 8 || p != NULL)
+ continue;
+
+ /* make sure the required elements are present */
+ if ((!argv[0] && !argv[1] && !argv[2] && !argv[3]) || !argv[4] || !argv[7])
+ continue;
+
+ /* add element to end of list */
+ cptr = (struct convert *) calloc(1, sizeof(struct convert));
+
+ if (cptr == NULL) {
+ syslog(LOG_ERR, "calloc error parsing ftpconversions");
+ exit(0);
+ }
+ if (cvttail)
+ cvttail->next = cptr;
+ cvttail = cptr;
+ if (!cvtptr)
+ cvtptr = cptr;
+
+ cptr->stripprefix = (char *) argv[0];
+ cptr->stripfix = (char *) argv[1];
+ cptr->prefix = (char *) argv[2];
+ cptr->postfix = (char *) argv[3];
+ cptr->external_cmd = (char *) argv[4];
+ cptr->types = conv((char *) argv[5]);
+ cptr->options = conv((char *) argv[6]);
+ cptr->name = (char *) argv[7];
+ }
+}
+
+void conv_init(void)
+{
+ if ((readconv(_path_cvt)) <= 0)
+ return;
+ parseconv();
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/conversions.h b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/conversions.h
new file mode 100644
index 0000000000..0fb690b074
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/conversions.h
@@ -0,0 +1,45 @@
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/****************************************************************************
+ Copyright (c) 1999,2000 WU-FTPD Development Group.
+ All rights reserved.
+
+ Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994
+ The Regents of the University of California.
+ Portions Copyright (c) 1993, 1994 Washington University in Saint Louis.
+ Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc.
+ Portions Copyright (c) 1989 Massachusetts Institute of Technology.
+ Portions Copyright (c) 1998 Sendmail, Inc.
+ Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P. Allman.
+ Portions Copyright (c) 1997 by Stan Barber.
+ Portions Copyright (c) 1997 by Kent Landfield.
+ Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ Free Software Foundation, Inc.
+
+ Use and distribution of this software and its source code are governed
+ by the terms and conditions of the WU-FTPD Software License ("LICENSE").
+
+ If you did not receive a copy of the license, it may be obtained online
+ at http://www.wu-ftpd.org/license.html.
+
+ $Id: conversions.h,v 1.5 2000/07/01 18:17:38 wuftpd Exp $
+
+****************************************************************************/
+
+#define T_REG 1 /* regular files OK */
+#define T_DIR 2 /* directories OK */
+#define T_ASCII 4 /* ASCII transfers OK */
+
+struct convert {
+ struct convert *next;
+ char *stripprefix; /* prefix to strip from real file */
+ char *stripfix; /* postfix to strip from real file */
+ char *prefix; /* prefix to add to real file */
+ char *postfix; /* postfix to add to real file */
+ char *external_cmd; /* command to do conversion */
+ int types; /* types: {file,directory} OK to convert */
+ int options; /* for logging: which conversion(s) used */
+ char *name; /* description of conversion */
+};
+
+extern struct convert *cvtptr;
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/domain.c b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/domain.c
new file mode 100644
index 0000000000..02dd23ac6c
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/domain.c
@@ -0,0 +1,324 @@
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/****************************************************************************
+
+ Copyright (c) 1999,2000 WU-FTPD Development Group.
+ All rights reserved.
+
+ Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994
+ The Regents of the University of California.
+ Portions Copyright (c) 1993, 1994 Washington University in Saint Louis.
+ Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc.
+ Portions Copyright (c) 1989 Massachusetts Institute of Technology.
+ Portions Copyright (c) 1998 Sendmail, Inc.
+ Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P. Allman.
+ Portions Copyright (c) 1997 by Stan Barber.
+ Portions Copyright (c) 1997 by Kent Landfield.
+ Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ Free Software Foundation, Inc.
+
+ Use and distribution of this software and its source code are governed
+ by the terms and conditions of the WU-FTPD Software License ("LICENSE").
+
+ If you did not receive a copy of the license, it may be obtained online
+ at http://www.wu-ftpd.org/license.html.
+
+ $Id: domain.c,v 1.11 2000/07/01 18:17:38 wuftpd Exp $
+
+****************************************************************************/
+/*
+ * domain.c - Name and address lookup and checking functions
+ *
+ * INITIAL AUTHOR - * Nikos Mouat <nikm@cyberflunk.com>
+ */
+
+#include "config.h"
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_SYS_SYSLOG_H
+#include <sys/syslog.h>
+#endif
+#if defined(HAVE_SYSLOG_H) || (!defined(AUTOCONF) && !defined(HAVE_SYS_SYSLOG_H))
+#include <syslog.h>
+#endif
+#include <netdb.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include "extensions.h"
+#include "proto.h"
+
+/* these should go in a new ftpd.h perhaps? config.h doesn't seem appropriate */
+/* and there does not appear to be a global include file */
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE !TRUE
+#endif
+
+/****************************************************************************
+ * check_name_for_ip()
+ * This routine checks if the IP address in remote_socket is a valid IP
+ * address for name.
+ ***************************************************************************/
+static int check_name_for_ip(char *name, struct SOCKSTORAGE *remote_socket)
+{
+#ifdef INET6
+ int family;
+ size_t sockaddrlen, addrlen;
+ char *raddr, *addr;
+ struct addrinfo hints, *result, *ai;
+
+ family = SOCK_FAMILY(*remote_socket);
+ raddr = SOCK_ADDR(*remote_socket);
+ if ((family == AF_INET6) &&
+ IN6_IS_ADDR_V4MAPPED((struct in6_addr *)raddr)) {
+ family = AF_INET;
+ /* move to the IPv4 part of an IPv4-mapped IPv6 address */
+ raddr += 12;
+ }
+
+ if (family == AF_INET6) {
+ sockaddrlen = sizeof(struct sockaddr_in6);
+ addrlen = sizeof(struct in6_addr);
+ }
+ else {
+ sockaddrlen = sizeof(struct sockaddr_in);
+ addrlen = sizeof(struct in_addr);
+ }
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = family;
+
+ if (getaddrinfo(name, NULL, &hints, &result) == 0) {
+ for (ai = result; ai != NULL; ai = ai->ai_next) {
+ if ((family == ai->ai_family) && (sockaddrlen == ai->ai_addrlen)) {
+ if (family == AF_INET6)
+ addr = (void *)&((struct sockaddr_in6 *)(ai->ai_addr))->sin6_addr;
+ else
+ addr = (void *)&((struct sockaddr_in *)(ai->ai_addr))->sin_addr;
+ if (memcmp(addr, raddr, addrlen) == 0) {
+ freeaddrinfo(result);
+ return TRUE;
+ }
+ }
+ }
+ freeaddrinfo(result);
+ }
+#else
+ char **addrl;
+ struct hostent *hp;
+
+ if ((hp = gethostbyname(name)) != NULL) {
+ for (addrl = hp->h_addr_list; addrl != NULL; addrl++) {
+ if (memcmp(&remote_socket->sin_addr, *addrl,
+ sizeof(struct in_addr)) == 0)
+ return TRUE;
+ }
+ }
+#endif /* INET6 */
+
+ /* no matching IP's */
+ return FALSE;
+}
+
+/****************************************************************************
+ * lookup()
+ * This routine returns the result of the lookup specified by dnsarg,
+ * which is either "refuse_no_reverse" or "refuse_mismatch", using the
+ * remote host's IP address.
+ ***************************************************************************/
+static int lookup(char *dnsarg)
+{
+ static int rhost_matches = FALSE;
+ static int rhost_matches_set = FALSE;
+ extern struct SOCKSTORAGE his_addr;
+ extern int rhlookup, nameserved;
+ extern char remotehost[];
+
+ /* skip lookups when not looking up the remote host's name */
+ if (!rhlookup)
+ return FALSE;
+
+ if (strcasecmp(dnsarg, "refuse_no_reverse") == 0)
+ return nameserved;
+
+ /* refuse_mismatch */
+ if (!rhost_matches_set) {
+ if (nameserved) {
+ /*
+ * We have the hostname based on the real IP address. Lookup
+ * the hostname to make sure the real IP address is listed as
+ * a valid address for the hostname.
+ */
+ rhost_matches = check_name_for_ip(remotehost, &his_addr);
+ }
+ else
+ rhost_matches = TRUE; /* no reverse, nothing to match */
+ rhost_matches_set = TRUE;
+ }
+ return rhost_matches;
+}
+
+/****************************************************************************
+ * dns_check()
+ * This routine returns FALSE if the operation specified by dnsarg is
+ * FALSE and "override" wasn't specified, otherwise it returns TRUE.
+ ***************************************************************************/
+static int dns_check(char *dnsarg)
+{
+ struct aclmember *entry = NULL;
+ int rc = TRUE;
+
+ /* check the config to see if we care */
+ /* dns refuse_mismatch|refuse_no_reverse <filename> [override] */
+ while (getaclentry("dns", &entry)) {
+ if (!ARG0 || !ARG1)
+ continue;
+ if (!strcasecmp(ARG0, dnsarg)) {
+ FILE *msg_file;
+ char linebuf[MAXPATHLEN];
+ char outbuf[MAXPATHLEN];
+ int code = 530;
+ char *crptr;
+
+ /* lookups can be slow, so only call now result is needed */
+ if (!lookup(dnsarg)) {
+ /* ok, so we need to kick out this user */
+
+ /* check to see if admin wants to override */
+ if (ARG2 && (!strcasecmp(ARG2, "override"))) {
+ /* Administrative override - but display warning anyway */
+ code = 220;
+ }
+
+ msg_file = fopen(ARG1, "r");
+ if (msg_file != NULL) {
+ while (fgets(linebuf, sizeof(linebuf), msg_file)) {
+ if ((crptr = strchr(linebuf, '\n')) != NULL)
+ *crptr = '\0';
+ msg_massage(linebuf, outbuf, sizeof(outbuf));
+ lreply(code, "%s", outbuf);
+ }
+ fclose(msg_file);
+#ifndef NO_SUCKING_NEWLINES
+ lreply(code, "");
+#endif
+ if (code == 530) {
+ reply(code, "");
+ rc = FALSE;
+ }
+ else {
+ lreply(code, "Administrative Override. Permission granted.");
+ lreply(code, "");
+ }
+ }
+ }
+ }
+ }
+ return rc;
+}
+
+/****************************************************************************
+ * check_rhost_reverse()
+ * This routine returns FALSE if the remote host's IP address has no
+ * associated name and access should be refused, otherwise it returns TRUE.
+ ***************************************************************************/
+int check_rhost_reverse(void)
+{
+ return dns_check("refuse_no_reverse");
+}
+
+/****************************************************************************
+ * check_rhost_matches()
+ * This routine returns FALSE if the remote host's IP address isn't listed
+ * as a valid IP address for the remote hostname and access should be
+ * refused, otherwise it returns TRUE.
+ ***************************************************************************/
+int check_rhost_matches(void)
+{
+ return dns_check("refuse_mismatch");
+}
+
+/****************************************************************************
+ * rhostlookup()
+ * This routine returns TRUE if the remote host's name of a connection
+ * from remoteaddr should be looked up, otherwise it returns FALSE.
+ ***************************************************************************/
+int rhostlookup(char *remoteaddr)
+{
+ int found, lookup, set, which;
+ struct aclmember *entry = NULL;
+
+ /* default is to lookup the remote host's name */
+ lookup = TRUE;
+ found = FALSE;
+
+ /* rhostlookup yes|no [<addrglob> ...] */
+ while (!found && getaclentry("rhostlookup", &entry)) {
+ if (!ARG0)
+ continue;
+ if (strcasecmp(ARG0, "yes") == 0)
+ set = TRUE;
+ else if (strcasecmp(ARG0, "no") == 0)
+ set = FALSE;
+ else
+ continue;
+
+ if (!ARG1)
+ lookup = set;
+ else {
+ for (which = 1; (which < MAXARGS) && ARG[which]; which++) {
+ if (hostmatch(ARG[which], remoteaddr, NULL)) {
+ lookup = set;
+ found = TRUE;
+ break;
+ }
+ }
+ }
+ }
+ return lookup;
+}
+
+/****************************************************************************
+ * set_res_options()
+ * set resolver options by setting the RES_OPTIONS environment variable.
+ * Note: name and address lookups are no longer done using DNS directly,
+ * so setting resolver options may have no effect.
+ ***************************************************************************/
+void set_res_options(void)
+{
+ int which;
+ struct aclmember *entry = NULL;
+ static char envbuf[BUFSIZ];
+
+ envbuf[0] = '\0';
+
+ /* dns resolveroptions [options] */
+ while (getaclentry("dns", &entry)) {
+ if (!ARG0 || !ARG1)
+ continue;
+ /* there are other DNS options, we only care about 'resolveroptions' */
+ if (strcasecmp(ARG0, "resolveroptions") == 0) {
+ for (which = 1; (which < MAXARGS) && ARG[which]; which++) {
+ if (envbuf[0] == '\0')
+ (void) strlcpy(envbuf, "RES_OPTIONS=", sizeof(envbuf));
+ else
+ (void) strlcat(envbuf, " ", sizeof(envbuf));
+ (void) strlcat(envbuf, ARG[which], sizeof(envbuf));
+ }
+ }
+ }
+ if (envbuf[0] != '\0') {
+ if (putenv(envbuf) != 0)
+ syslog(LOG_WARNING, "putenv(\"%s\") failed", envbuf);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/extensions.c b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/extensions.c
new file mode 100644
index 0000000000..965bab8854
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/extensions.c
@@ -0,0 +1,2368 @@
+/*
+ * Copyright 2002-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/****************************************************************************
+
+ Copyright (c) 1999,2000 WU-FTPD Development Group.
+ All rights reserved.
+
+ Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994
+ The Regents of the University of California.
+ Portions Copyright (c) 1993, 1994 Washington University in Saint Louis.
+ Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc.
+ Portions Copyright (c) 1989 Massachusetts Institute of Technology.
+ Portions Copyright (c) 1998 Sendmail, Inc.
+ Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P. Allman.
+ Portions Copyright (c) 1997 by Stan Barber.
+ Portions Copyright (c) 1997 by Kent Landfield.
+ Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ Free Software Foundation, Inc.
+
+ Use and distribution of this software and its source code are governed
+ by the terms and conditions of the WU-FTPD Software License ("LICENSE").
+
+ If you did not receive a copy of the license, it may be obtained online
+ at http://www.wu-ftpd.org/license.html.
+
+ $Id: extensions.c,v 1.48 2000/07/01 18:17:38 wuftpd Exp $
+
+****************************************************************************/
+#include "config.h"
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+
+#ifdef HAVE_SYS_SYSLOG_H
+#include <sys/syslog.h>
+#endif
+#if defined(HAVE_SYSLOG_H) || (!defined(AUTOCONF) && !defined(HAVE_SYS_SYSLOG_H))
+#include <syslog.h>
+#endif
+
+#ifdef TIME_WITH_SYS_TIME
+#include <time.h>
+#include <sys/time.h>
+#else
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#else
+#include <time.h>
+#endif
+#endif
+#include <pwd.h>
+#include <setjmp.h>
+#include <grp.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/param.h>
+
+#ifdef HAVE_SYS_FS_UFS_QUOTA_H
+#include <sys/fs/ufs_quota.h>
+#elif defined(HAVE_UFS_UFS_QUOTA_H)
+#include <ufs/ufs/quota.h>
+#elif defined(HAVE_UFS_QUOTA_H)
+#include <ufs/quota.h>
+#elif defined(HAVE_SYS_MNTENT_H)
+#include <sys/mntent.h>
+#elif defined(HAVE_SYS_MNTTAB_H)
+#include <sys/mnttab.h>
+#endif
+
+#if defined(HAVE_STATVFS)
+#include <sys/statvfs.h>
+#elif defined(HAVE_SYS_VFS)
+#include <sys/vfs.h>
+#elif defined(HAVE_SYS_MOUNT)
+#include <sys/mount.h>
+#endif
+
+#include <arpa/ftp.h>
+
+#ifdef HAVE_PATHS_H
+#include <paths.h>
+#endif
+#include "pathnames.h"
+#include "extensions.h"
+#include "wu_fnmatch.h"
+#include "proto.h"
+
+#if defined(HAVE_FTW)
+#include <ftw.h>
+#else
+#include "support/ftw.h"
+#endif
+
+#ifdef QUOTA
+struct dqblk quota;
+char *time_quota(long curstate, long softlimit, long timelimit, char *timeleft);
+#endif
+
+#ifdef HAVE_REGEX_H
+#include <regex.h>
+#endif
+
+#if defined(HAVE_REGEX) && defined(SVR4) && ! (defined(NO_LIBGEN))
+#include <libgen.h>
+#endif
+
+extern int type, transflag, ftwflag, authenticated, autospout_free, data,
+ pdata, anonymous, guest;
+extern char chroot_path[], guestpw[];
+
+#ifdef TRANSFER_COUNT
+extern off_t data_count_in;
+extern off_t data_count_out;
+#ifdef TRANSFER_LIMIT
+extern off_t data_limit_raw_in;
+extern off_t data_limit_raw_out;
+extern off_t data_limit_raw_total;
+extern off_t data_limit_data_in;
+extern off_t data_limit_data_out;
+extern off_t data_limit_data_total;
+#ifdef RATIO /* 1998/08/06 K.Wakui */
+#define TRUNC_KB(n) ((n)/1024+(((n)%1024)?1:0))
+extern time_t login_time;
+extern time_t limit_time;
+extern off_t total_free_dl;
+extern int upload_download_rate;
+#endif /* RATIO */
+#endif
+#endif
+
+#ifdef OTHER_PASSWD
+#include "getpwnam.h"
+extern char _path_passwd[];
+#endif
+
+#ifdef LOG_FAILED
+extern char the_user[];
+#endif
+
+extern char *globerr, remotehost[];
+#ifdef THROUGHPUT
+extern char remoteaddr[];
+#endif
+
+#ifndef HAVE_REGEX
+char *re_comp(const char *regex);
+int re_exec(const char *p1);
+#endif
+
+char shuttime[30], denytime[30], disctime[30];
+
+FILE *dout;
+
+time_t newer_time;
+
+int show_fullinfo;
+
+/* This always was a bug, because neither st_size nor time_t were required to
+ be compatible with int, but needs fixing properly for C9X. */
+
+/* Some systems use one format, some another. This takes care of the garbage */
+/* Do the system specific stuff only if we aren't autoconfed */
+#if !defined(L_FORMAT)
+#if (defined(BSD) && (BSD >= 199103)) && !defined(LONGOFF_T)
+#define L_FORMAT "qd"
+#else
+#define L_FORMAT "d"
+#endif
+#endif
+#if !defined(T_FORMAT)
+#define T_FORMAT "d"
+#endif
+#if !defined(PW_UID_FORMAT)
+#define PW_UID_FORMAT "d"
+#endif
+#if !defined(GR_GID_FORMAT)
+#define GR_GID_FORMAT "d"
+#endif
+
+int snprintf(char *str, size_t count, const char *fmt,...);
+
+#ifdef SITE_NEWER
+int check_newer(const char *path, const struct stat *st, int flag)
+{
+ if (st->st_mtime > newer_time) {
+ if (show_fullinfo != 0) {
+ if (flag == FTW_F || flag == FTW_D) {
+ fprintf(dout, "%s %" L_FORMAT " %" T_FORMAT " %s\n",
+ flag == FTW_F ? "F" : "D",
+ st->st_size, st->st_mtime, path);
+ }
+ }
+ else if (flag == FTW_F)
+ fprintf(dout, "%s\n", path);
+ }
+
+ /* When an ABOR has been received (which sets ftwflag > 1) return a
+ * non-zero value which causes ftw to stop tree traversal and return.
+ */
+
+ return (ftwflag > 1 ? 1 : 0);
+}
+#endif
+
+#if defined(HAVE_STATVFS)
+long getSize(char *s)
+{
+ struct statvfs buf;
+
+ if (statvfs(s, &buf) != 0)
+ return (0);
+
+ return (buf.f_bavail * buf.f_frsize / 1024);
+}
+#elif defined(HAVE_SYS_VFS) || defined (HAVE_SYS_MOUNT)
+long getSize(char *s)
+{
+ struct statfs buf;
+
+ if (statfs(s, &buf) != 0)
+ return (0);
+
+ return (buf.f_bavail * buf.f_bsize / 1024);
+}
+#endif
+
+/*************************************************************************/
+/* FUNCTION : msg_massage */
+/* PURPOSE : Scan a message line for magic cookies, replacing them as */
+/* needed. */
+/* ARGUMENTS : pointer input and output buffers */
+/*************************************************************************/
+
+void msg_massage(const char *inbuf, char *outbuf, size_t outlen)
+{
+ const char *inptr = inbuf;
+ char *outptr = outbuf;
+#ifdef QUOTA
+ char timeleft[80];
+#endif
+ char buffer[MAXPATHLEN];
+ time_t curtime;
+ int limit;
+#ifndef LOG_FAILED
+ extern struct passwd *pw;
+#endif
+ struct aclmember *entry;
+
+#ifdef VIRTUAL
+ extern int virtual_mode;
+ extern int virtual_ftpaccess;
+ extern char virtual_email[];
+#endif
+ extern char hostname[];
+ extern char authuser[];
+
+ (void) acl_getclass(buffer);
+ limit = acl_getlimit(buffer, NULL);
+
+ while ((outlen > 1) && (*inptr != '\0')) {
+ if (*inptr != '%') {
+ *outptr++ = *inptr;
+ outlen -= 1;
+ }
+ else {
+ entry = NULL;
+ switch (*++inptr) {
+ case 'E':
+#ifdef VIRTUAL
+ if (virtual_mode && !virtual_ftpaccess && virtual_email[0] != '\0')
+ snprintf(outptr, outlen, "%s", virtual_email);
+ else
+#endif
+ if ((getaclentry("email", &entry)) && ARG0)
+ snprintf(outptr, outlen, "%s", ARG0);
+ else
+ *outptr = '\0';
+ break;
+
+ case 'N':
+ snprintf(outptr, outlen, "%d", acl_countusers(buffer));
+ break;
+
+ case 'M':
+ if (limit == -1)
+ strncpy(outptr, "unlimited", outlen);
+ else
+ snprintf(outptr, outlen, "%d", limit);
+ break;
+
+ case 'T':
+ (void) time(&curtime);
+ strncpy(outptr, ctime(&curtime), outlen);
+ if (outlen > 24)
+ *(outptr + 24) = '\0';
+ break;
+
+ case 'F':
+#if defined(HAVE_STATVFS) || defined(HAVE_SYS_VFS) || defined(HAVE_SYS_MOUNT)
+ snprintf(outptr, outlen, "%lu", (long) getSize("."));
+#else
+ *outptr = '\0';
+#endif
+ break;
+
+ case 'C':
+#ifdef HAVE_GETCWD
+ (void) getcwd(outptr, outlen);
+#else
+#error wu-ftpd on this platform has security deficiencies!!!
+ (void) getwd(outptr);
+#endif
+ break;
+
+ case 'R':
+ strncpy(outptr, remotehost, outlen);
+ break;
+
+ case 'L':
+ strncpy(outptr, hostname, outlen);
+ break;
+
+ case 'U':
+ if (xferdone && anonymous)
+ strncpy(outptr, guestpw, outlen);
+ else
+#ifdef LOG_FAILED
+ strncpy(outptr, the_user, outlen);
+#else /* LOG_FAILED */
+ strncpy(outptr,
+ (pw == NULL) ? "[unknown]" : pw->pw_name, outlen);
+#endif /* LOG_FAILED */
+ break;
+
+ case 's':
+ strncpy(outptr, shuttime, outlen);
+ if (outlen > 24)
+ *(outptr + 24) = '\0';
+ break;
+
+ case 'd':
+ strncpy(outptr, disctime, outlen);
+ if (outlen > 24)
+ *(outptr + 24) = '\0';
+ break;
+
+ case 'r':
+ strncpy(outptr, denytime, outlen);
+ if (outlen > 24)
+ *(outptr + 24) = '\0';
+ break;
+
+/* KH : cookie %u for RFC931 name */
+ case 'u':
+ if (authenticated)
+ strncpy(outptr, authuser, outlen);
+ else {
+ if (xferdone)
+ snprintf(outptr, outlen, "%c", '*');
+ else
+ strncpy(outptr, "[unknown]", outlen);
+ }
+ break;
+
+#ifdef QUOTA
+ case 'B':
+#ifdef QUOTA_BLOCKS /* 1024-blocks instead of 512-blocks */
+ snprintf(outptr, outlen, "%ld", quota.dqb_bhardlimit % 2 ?
+ (long) (quota.dqb_bhardlimit / 2 + 1) : (long) (quota.dqb_bhardlimit / 2));
+#else
+ snprintf(outptr, outlen, "%ld", (long) quota.dqb_bhardlimit);
+#endif
+ break;
+
+ case 'b':
+#ifdef QUOTA_BLOCKS /* 1024-blocks instead of 512-blocks */
+ snprintf(outptr, outlen, "%ld", quota.dqb_bsoftlimit % 2 ?
+ (long) (quota.dqb_bsoftlimit / 2 + 1) : (long) (quota.dqb_bsoftlimit / 2));
+#else
+ snprintf(outptr, outlen, "%ld", (long) quota.dqb_bsoftlimit);
+#endif
+ break;
+
+ case 'Q':
+#ifdef QUOTA_BLOCKS /* 1024-blocks instead of 512-blocks */
+ snprintf(outptr, outlen, "%ld", quota.dqb_curblocks % 2 ?
+ (long) (quota.dqb_curblocks / 2 + 1) : (long) (quota.dqb_curblocks / 2));
+#else
+ snprintf(outptr, outlen, "%ld", quota.dqb_curblocks);
+#endif
+ break;
+
+ case 'I':
+#if defined(QUOTA_INODE)
+ snprintf(outptr, outlen, "%d", quota.dqb_ihardlimit);
+#else
+ snprintf(outptr, outlen, "%ld", (long) quota.dqb_fhardlimit);
+#endif
+ break;
+
+ case 'i':
+#if defined(QUOTA_INODE)
+ snprintf(outptr, outlen, "%d", quota.dqb_isoftlimit);
+#else
+ snprintf(outptr, outlen, "%ld", (long) quota.dqb_fsoftlimit);
+#endif
+ break;
+
+ case 'q':
+#if defined(QUOTA_INODE)
+ snprintf(outptr, outlen, "%d", quota.dqb_curinodes);
+#else
+ snprintf(outptr, outlen, "%ld", (long) quota.dqb_curfiles);
+#endif
+ break;
+
+ case 'H':
+ time_quota(quota.dqb_curblocks, quota.dqb_bsoftlimit,
+#if defined(QUOTA_INODE)
+ quota.dqb_btime, timeleft);
+#else
+ quota.dqb_btimelimit, timeleft);
+#endif
+ strncpy(outptr, timeleft, outlen);
+ break;
+
+ case 'h':
+#if defined(QUOTA_INODE)
+ time_quota(quota.dqb_curinodes, quota.dqb_isoftlimit,
+ quota.dqb_itime, timeleft);
+#else
+ time_quota(quota.dqb_curfiles, quota.dqb_fsoftlimit,
+ quota.dqb_ftimelimit, timeleft);
+#endif
+ strncpy(outptr, timeleft, outlen);
+ break;
+#endif /* QUOTA */
+
+ case '%':
+ *outptr++ = '%';
+ outlen -= 1;
+ *outptr = '\0';
+ break;
+
+#ifdef TRANSFER_COUNT
+#ifdef TRANSFER_LIMIT
+#ifdef RATIO
+ case 'x':
+ switch (*++inptr) {
+ case 'u': /* upload bytes */
+ sprintf(outptr,"%" L_FORMAT, TRUNC_KB(data_count_in) );
+ break;
+ case 'd': /* download bytes */
+ sprintf(outptr,"%" L_FORMAT, TRUNC_KB(data_count_out) );
+ break;
+ case 'R': /* rate 1:n */
+ if( upload_download_rate > 0 ) {
+ sprintf(outptr,"%d", upload_download_rate );
+ }
+ else {
+ strcpy(outptr,"free");
+ }
+ break;
+ case 'c': /* credit bytes */
+ if( upload_download_rate > 0 ) {
+ off_t credit=( data_count_in * upload_download_rate) - (data_count_out - total_free_dl);
+ sprintf(outptr,"%" L_FORMAT, TRUNC_KB(credit) );
+ }
+ else {
+ strcpy(outptr,"unlimited");
+ }
+ break;
+ case 'T': /* time limit (minutes) */
+ if( limit_time > 0 ) {
+ sprintf(outptr,"%d", limit_time );
+ }
+ else {
+ strcpy(outptr,"unlimited");
+ }
+ break;
+ case 'E': /* elapsed time from loggedin (minutes) */
+ sprintf(outptr,"%d", (time(NULL)-login_time)/60 );
+ break;
+ case 'L': /* times left until force logout (minutes) */
+ if( limit_time > 0 ) {
+ sprintf(outptr,"%d", limit_time-(time(NULL)-login_time)/60 );
+ }
+ else {
+ strcpy(outptr,"unlimited");
+ }
+ break;
+ case 'U': /* upload limit */
+ if( data_limit_raw_in > 0 ) {
+ sprintf(outptr,"%" L_FORMAT, TRUNC_KB(data_limit_raw_in));
+ }
+ else if( data_limit_data_in > 0 ) {
+ sprintf(outptr,"%" L_FORMAT, TRUNC_KB(data_limit_data_in));
+ }
+ else if( data_limit_raw_total > 0 ) {
+ sprintf(outptr,"%" L_FORMAT, TRUNC_KB(data_limit_raw_total));
+ }
+ else if( data_limit_data_total > 0 ) {
+ sprintf(outptr,"%" L_FORMAT, TRUNC_KB(data_limit_data_total));
+ }
+ else {
+ strcpy(outptr, "unlimited");
+ }
+ break;
+ case 'D': /* download limit */
+ if( data_limit_raw_out > 0 ) {
+ sprintf(outptr,"%" L_FORMAT, TRUNC_KB(data_limit_raw_out));
+ }
+ else if( data_limit_data_out > 0 ) {
+ sprintf(outptr,"%" L_FORMAT, TRUNC_KB(data_limit_data_out));
+ }
+ else if( data_limit_raw_total > 0 ) {
+ sprintf(outptr,"%" L_FORMAT, TRUNC_KB(data_limit_raw_total));
+ }
+ else if( data_limit_data_total > 0 ) {
+ sprintf(outptr,"%" L_FORMAT, TRUNC_KB(data_limit_data_total));
+ }
+ else {
+ strcpy(outptr, "unlimited");
+ }
+ break;
+ default:
+ strcpy(outptr,"%??");
+ break;
+ }
+ break;
+#endif /* RATIO */
+#endif
+#endif
+ /* File transfer logging (xferlog) */
+ case 'X':
+ if (xferdone) { /* only if a transfer has just occurred */
+ switch (*++inptr) {
+ case 't':
+ snprintf(outptr, outlen, "%d", xfervalues.transfer_time);
+ break;
+ case 's':
+ snprintf(outptr, outlen, "%" L_FORMAT, xfervalues.filesize);
+ break;
+ case 'n':
+ snprintf(outptr, outlen, "%" L_FORMAT, xfervalues.transfer_bytes);
+ break;
+ case 'P': /* absolute pathname */
+ /* FALLTHROUGH */
+ case 'p': /* chroot-relative pathname */
+ {
+ char namebuf[MAXPATHLEN];
+ int loop;
+
+ if (*inptr == 'P')
+ wu_realpath(xfervalues.filename, namebuf, chroot_path);
+ else
+ fb_realpath(xfervalues.filename, namebuf);
+ for (loop = 0; namebuf[loop]; loop++) {
+ if (isspace(namebuf[loop]) || iscntrl(namebuf[loop]))
+ namebuf[loop] = '_';
+ }
+ snprintf(outptr, outlen, "%s", namebuf);
+ break;
+ }
+ case 'y':
+ snprintf(outptr, outlen, "%c", xfervalues.transfer_type);
+ break;
+ case 'f':
+ snprintf(outptr, outlen, "%s", xfervalues.special_action);
+ break;
+ case 'd':
+ snprintf(outptr, outlen, "%c", xfervalues.transfer_direction);
+ break;
+ case 'm':
+ snprintf(outptr, outlen, "%c", xfervalues.access_mode);
+ break;
+ case 'a':
+ snprintf(outptr, outlen, "%d", xfervalues.auth);
+ break;
+ case 'r':
+ snprintf(outptr, outlen, "%" L_FORMAT, xfervalues.restart_offset);
+ break;
+ case 'c':
+ snprintf(outptr, outlen, "%c", xfervalues.completion);
+ break;
+ default:
+ snprintf(outptr, outlen, "%%X%c", *inptr);
+ break;
+ }
+ }
+ else
+ snprintf(outptr, outlen, "%%%c", *inptr);
+ break;
+
+ default:
+ *outptr++ = '%';
+ outlen -= 1;
+ if (outlen > 1) {
+ *outptr++ = *inptr;
+ outlen -= 1;
+ }
+ *outptr = '\0';
+ break;
+ }
+ outptr[outlen - 1] = '\0';
+ while (*outptr) {
+ outptr++;
+ outlen -= 1;
+ }
+ }
+ inptr++;
+ }
+ if (outlen > 0)
+ *outptr = '\0';
+}
+
+/*************************************************************************/
+/* FUNCTION : cwd_beenhere */
+/* PURPOSE : Return 1 if the user has already visited this directory */
+/* via C_WD. */
+/* ARGUMENTS : a power-of-two directory function code (README, MESSAGE) */
+/*************************************************************************/
+
+int cwd_beenhere(int dircode)
+{
+ struct dirlist {
+ struct dirlist *next;
+ int dircode;
+ char dirname[1];
+ };
+
+ static struct dirlist *head = NULL;
+ struct dirlist *curptr;
+ char cwd[MAXPATHLEN];
+
+ (void) fb_realpath(".", cwd);
+
+ for (curptr = head; curptr != NULL; curptr = curptr->next)
+ if (strcmp(curptr->dirname, cwd) == 0) {
+ if (!(curptr->dircode & dircode)) {
+ curptr->dircode |= dircode;
+ return (0);
+ }
+ return (1);
+ }
+ curptr = (struct dirlist *) malloc(strlen(cwd) + 1 + sizeof(struct dirlist));
+
+ if (curptr != NULL) {
+ curptr->next = head;
+ head = curptr;
+ curptr->dircode = dircode;
+ strcpy(curptr->dirname, cwd);
+ }
+ return (0);
+}
+
+/*************************************************************************/
+/* FUNCTION : show_banner */
+/* PURPOSE : Display a banner on the user's terminal before login */
+/* ARGUMENTS : reply code to use */
+/*************************************************************************/
+
+void show_banner(int msgcode)
+{
+ char *crptr, linebuf[1024], outbuf[1024];
+ struct aclmember *entry = NULL;
+ FILE *infile;
+
+#ifdef VIRTUAL
+ extern int virtual_mode;
+ extern int virtual_ftpaccess;
+ extern char virtual_banner[];
+
+ if (virtual_mode && !virtual_ftpaccess) {
+ infile = fopen(virtual_banner, "r");
+ if (infile) {
+ while (fgets(linebuf, sizeof(linebuf), infile) != NULL) {
+ if ((crptr = strchr(linebuf, '\n')) != NULL)
+ *crptr = '\0';
+ msg_massage(linebuf, outbuf, sizeof(outbuf));
+ lreply(msgcode, "%s", outbuf);
+ }
+ fclose(infile);
+#ifndef NO_SUCKING_NEWLINES
+ lreply(msgcode, "");
+#endif
+ }
+ }
+ else {
+#endif
+ /* banner <path> */
+ while (getaclentry("banner", &entry)) {
+ if (!ARG0)
+ continue;
+ infile = fopen(ARG0, "r");
+ if (infile) {
+ while (fgets(linebuf, sizeof(linebuf), infile) != NULL) {
+ if ((crptr = strchr(linebuf, '\n')) != NULL)
+ *crptr = '\0';
+ msg_massage(linebuf, outbuf, sizeof(outbuf));
+ lreply(msgcode, "%s", outbuf);
+ }
+ fclose(infile);
+#ifndef NO_SUCKING_NEWLINES
+ lreply(msgcode, "");
+#endif
+ }
+ }
+#ifdef VIRTUAL
+ }
+#endif
+}
+/*************************************************************************/
+/* FUNCTION : show_message */
+/* PURPOSE : Display a message on the user's terminal if the current */
+/* conditions are right */
+/* ARGUMENTS : reply code to use, LOG_IN|CMD */
+/*************************************************************************/
+
+void show_message(int msgcode, int mode)
+{
+ char *crptr, linebuf[1024], outbuf[1024], class[MAXPATHLEN], cwd[MAXPATHLEN];
+ int show, which;
+ struct aclmember *entry = NULL;
+ FILE *infile;
+
+ if (mode == C_WD && cwd_beenhere(1) != 0)
+ return;
+
+#ifdef HAVE_GETCWD
+ (void) getcwd(cwd, MAXPATHLEN - 1);
+#else
+ (void) getwd(cwd);
+#endif
+ (void) acl_getclass(class);
+
+ /* message <path> [<when> [<class>]] */
+ while (getaclentry("message", &entry)) {
+ if (!ARG0)
+ continue;
+ show = 0;
+
+ if (mode == LOG_IN && (!ARG1 || !strcasecmp(ARG1, "login")))
+ if (!ARG2)
+ show++;
+ else {
+ for (which = 2; (which < MAXARGS) && ARG[which]; which++)
+ if (strcasecmp(class, ARG[which]) == 0)
+ show++;
+ }
+ if (mode == C_WD && ARG1 && !strncasecmp(ARG1, "cwd=", 4) &&
+ (!strcmp((ARG1) + 4, cwd) || *(ARG1 + 4) == '*' ||
+ !wu_fnmatch((ARG1) + 4, cwd, FNM_PATHNAME)))
+ if (!ARG2)
+ show++;
+ else {
+ for (which = 2; (which < MAXARGS) && ARG[which]; which++)
+ if (strcasecmp(class, ARG[which]) == 0)
+ show++;
+ }
+ if (show && (int) strlen(ARG0) > 0) {
+ infile = fopen(ARG0, "r");
+ if (infile) {
+ while (fgets(linebuf, sizeof(linebuf), infile) != NULL) {
+ if ((crptr = strchr(linebuf, '\n')) != NULL)
+ *crptr = '\0';
+ msg_massage(linebuf, outbuf, sizeof(outbuf));
+ lreply(msgcode, "%s", outbuf);
+ }
+ fclose(infile);
+#ifndef NO_SUCKING_NEWLINES
+ lreply(msgcode, "");
+#endif
+ }
+ }
+ }
+}
+
+/*************************************************************************/
+/* FUNCTION : show_readme */
+/* PURPOSE : Display a message about a README file to the user if the */
+/* current conditions are right */
+/* ARGUMENTS : pointer to ACL buffer, reply code, LOG_IN|C_WD */
+/*************************************************************************/
+
+void show_readme(int code, int mode)
+{
+ char **filelist, **sfilelist, class[MAXPATHLEN], cwd[MAXPATHLEN];
+ int show, which, days;
+ time_t clock;
+
+ struct stat buf;
+ struct tm *tp;
+ struct aclmember *entry = NULL;
+
+ if (cwd_beenhere(2) != 0)
+ return;
+
+#ifdef HAVE_GETCWD
+ (void) getcwd(cwd, MAXPATHLEN - 1);
+#else
+ (void) getwd(cwd);
+#endif
+ (void) acl_getclass(class);
+
+ /* readme <path> {<when>} */
+ while (getaclentry("readme", &entry)) {
+ if (!ARG0)
+ continue;
+ show = 0;
+
+ if (mode == LOG_IN && (!ARG1 || !strcasecmp(ARG1, "login")))
+ if (!ARG2)
+ show++;
+ else {
+ for (which = 2; (which < MAXARGS) && ARG[which]; which++)
+ if (strcasecmp(class, ARG[which]) == 0)
+ show++;
+ }
+ if (mode == C_WD && ARG1 && !strncasecmp(ARG1, "cwd=", 4)
+ && (!strcmp((ARG1) + 4, cwd) || *(ARG1 + 4) == '*' ||
+ !wu_fnmatch((ARG1) + 4, cwd, FNM_PATHNAME)))
+ if (!ARG2)
+ show++;
+ else {
+ for (which = 2; (which < MAXARGS) && ARG[which]; which++)
+ if (strcasecmp(class, ARG[which]) == 0)
+ show++;
+ }
+ if (show) {
+ globerr = NULL;
+ filelist = ftpglob(ARG0);
+ sfilelist = filelist; /* save to free later */
+ if (!globerr) {
+ while (filelist && *filelist) {
+ errno = 0;
+ if (!stat(*filelist, &buf) &&
+ (buf.st_mode & S_IFMT) == S_IFREG) {
+ lreply(code, "Please read the file %s", *filelist);
+ (void) time(&clock);
+ tp = localtime(&clock);
+ days = 365 * tp->tm_year + tp->tm_yday;
+ tp = localtime((time_t *) & buf.st_mtime);
+ days -= 365 * tp->tm_year + tp->tm_yday;
+/*
+ if (days == 0) {
+ lreply(code, " it was last modified on %.24s - Today",
+ ctime((time_t *)&buf.st_mtime));
+ } else {
+ */
+ lreply(code,
+ " it was last modified on %.24s - %d day%s ago",
+ ctime((time_t *) & buf.st_mtime), days, days == 1 ? "" : "s");
+/*
+ }
+ */
+ }
+ filelist++;
+ }
+ }
+ if (sfilelist) {
+ blkfree(sfilelist);
+ free((char *) sfilelist);
+ }
+ }
+ }
+}
+
+/*************************************************************************/
+/* FUNCTION : deny_badxfertype */
+/* PURPOSE : If user is in ASCII transfer mode and tries to retrieve a */
+/* binary file, abort transfer and display appropriate error */
+/* ARGUMENTS : message code to use for denial, path of file to check for */
+/* binary contents or NULL to assume binary file */
+/*************************************************************************/
+
+int deny_badasciixfer(int msgcode, char *filepath)
+{
+
+ if (type == TYPE_A && !*filepath) {
+ reply(msgcode, "This is a BINARY file, using ASCII mode to transfer will corrupt it.");
+ return (1);
+ }
+ /* The hooks are here to prevent transfers of actual binary files, not
+ * just TAR or COMPRESS mode files... */
+ return (0);
+}
+
+/*************************************************************************/
+/* FUNCTION : is_shutdown */
+/* PURPOSE : Check to see if the server is shutting down, if it is */
+/* arrange for the shutdown message to be sent in the next */
+/* reply to the user */
+/* ARGUMENTS : whether to arrange for a shutdown message to be sent, new */
+/* or existing connection */
+/* RETURNS : 1 if shutting down, 0 if not */
+/*************************************************************************/
+
+int is_shutdown(int quiet, int new)
+{
+ static struct tm tmbuf;
+ static struct stat s_last;
+ static time_t last = 0, shut, deny, disc;
+ static int valid;
+ static char text[2048];
+ struct stat s_cur;
+
+ extern char *autospout, Shutdown[];
+
+ FILE *fp;
+
+ int deny_off, disc_off;
+
+ time_t curtime = time(NULL);
+
+ char buf[1024], linebuf[1024];
+
+ if (Shutdown[0] == '\0' || stat(Shutdown, &s_cur))
+ return (0);
+
+ if (s_last.st_mtime != s_cur.st_mtime) {
+ valid = 0;
+
+ fp = fopen(Shutdown, "r");
+ if (fp == NULL)
+ return (0);
+ s_last = s_cur;
+ fgets(buf, sizeof(buf), fp);
+ if (sscanf(buf, "%d %d %d %d %d %ld %ld", &tmbuf.tm_year, &tmbuf.tm_mon,
+ &tmbuf.tm_mday, &tmbuf.tm_hour, &tmbuf.tm_min, &deny, &disc) != 7) {
+ (void) fclose(fp);
+ return (0);
+ }
+ valid = 1;
+ deny_off = 3600 * (deny / 100) + 60 * (deny % 100);
+ disc_off = 3600 * (disc / 100) + 60 * (disc % 100);
+
+ tmbuf.tm_year -= 1900;
+ tmbuf.tm_isdst = -1;
+ shut = mktime(&tmbuf);
+ strcpy(shuttime, ctime(&shut));
+
+ disc = shut - disc_off;
+ strcpy(disctime, ctime(&disc));
+
+ deny = shut - deny_off;
+ strcpy(denytime, ctime(&deny));
+
+ text[0] = '\0';
+
+ while (fgets(buf, sizeof(buf), fp) != NULL) {
+ msg_massage(buf, linebuf, sizeof(linebuf));
+ if ((strlen(text) + strlen(linebuf)) < sizeof(text))
+ strcat(text, linebuf);
+ }
+
+ (void) fclose(fp);
+ }
+ if (!valid)
+ return (0);
+
+ /* if last == 0, then is_shutdown() only called with quiet == 1 so far */
+ if (last == 0 && !quiet) {
+ autospout = text; /* warn them for the first time */
+ autospout_free = 0;
+ last = curtime;
+ }
+ /* if a new connection and past deny time, tell caller to drop 'em */
+ if (new && curtime > deny)
+ return (1);
+
+ /* if past disconnect time, tell caller to drop 'em */
+ if (curtime > disc)
+ return (1);
+
+ /* if less than 60 seconds to disconnection, warn 'em continuously */
+ if (curtime > (disc - 60) && !quiet) {
+ autospout = text;
+ autospout_free = 0;
+ last = curtime;
+ }
+ /* if less than 15 minutes to disconnection, warn 'em every 5 mins */
+ if (curtime > (disc - 60 * 15)) {
+ if ((curtime - last) > (60 * 5) && !quiet) {
+ autospout = text;
+ autospout_free = 0;
+ last = curtime;
+ }
+ }
+ /* if less than 24 hours to disconnection, warn 'em every 30 mins */
+ if (curtime < (disc - 24 * 60 * 60) && !quiet) {
+ if ((curtime - last) > (60 * 30)) {
+ autospout = text;
+ autospout_free = 0;
+ last = curtime;
+ }
+ }
+ /* if more than 24 hours to disconnection, warn 'em every 60 mins */
+ if (curtime > (disc - 24 * 60 * 60) && !quiet) {
+ if ((curtime - last) >= (24 * 60 * 60)) {
+ autospout = text;
+ autospout_free = 0;
+ last = curtime;
+ }
+ }
+ return (0);
+}
+
+#ifdef SITE_NEWER
+void newer(char *date, char *path, int showlots)
+{
+ struct tm tm;
+
+ if (sscanf(date, "%04d%02d%02d%02d%02d%02d",
+ &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
+ &tm.tm_hour, &tm.tm_min, &tm.tm_sec) == 6) {
+
+ tm.tm_year -= 1900;
+ tm.tm_mon--;
+ tm.tm_isdst = -1;
+ newer_time = mktime(&tm);
+ dout = dataconn("file list", (off_t) - 1, "w");
+
+ if (dout != NULL) {
+ /* As ftw allocates storage it needs a chance to cleanup, setting
+ * ftwflag prevents myoob from calling longjmp, incrementing
+ * ftwflag instead which causes check_newer to return non-zero
+ * which makes ftw return. */
+ ftwflag = 1;
+ transflag++;
+ show_fullinfo = showlots;
+#if defined(HAVE_FTW)
+ ftw(path, check_newer, -1);
+#else
+ treewalk(path, check_newer, -1, NULL);
+#endif
+
+ /* don't send a reply if myoob has already replied */
+ if (ftwflag == 1) {
+ if (ferror(dout) != 0)
+ perror_reply(550, "Data connection");
+ else
+ reply(226, "Transfer complete.");
+ }
+
+ (void) fclose(dout);
+ data = -1;
+ pdata = -1;
+ transflag = 0;
+ ftwflag = 0;
+ }
+ }
+ else
+ reply(501, "Bad DATE format");
+}
+#endif
+
+int type_match(char *typelist)
+{
+ char *start, *p;
+ int len;
+
+ if (typelist == NULL)
+ return (0);
+
+ for (p = start = typelist; *start != '\0'; start = p) {
+ while (*p != '\0' && *p != ',')
+ p++;
+ len = p - start;
+ if (*p != '\0')
+ p++;
+ if (len == 9 && anonymous && strncasecmp(start, "anonymous", 9) == 0)
+ return (1);
+ if (len == 5 && guest && strncasecmp(start, "guest", 5) == 0)
+ return (1);
+ if (len == 4 && !guest && !anonymous &&
+ strncasecmp(start, "real", 4) == 0)
+ return (1);
+
+ if (len > 6 && strncasecmp(start, "class=", 6) == 0) {
+ char class[1024];
+
+ if ((acl_getclass(class) == 1) && (strlen(class) == len - 6) &&
+ (strncasecmp(start + 6, class, len - 6) == 0))
+ return (1);
+ }
+ }
+ return (0);
+}
+
+int path_compare(char *p1, char *p2)
+{
+ if ((strcmp(p1, "*") == 0) || (wu_fnmatch(p1, p2, FNM_PATHNAME) == 0)) /* 0 means they matched */
+ return (strlen(p1));
+ else
+ return (-2);
+}
+
+void expand_id(void)
+{
+ char class[1024];
+ struct aclmember *entry = NULL;
+ (void) acl_getclass(class);
+ while (getaclentry("upload", &entry)) {
+ char *q;
+ int i = 0;
+ int options = 1;
+ int classfound = 0;
+ int classmatched = 0;
+ while (options
+ && (i < MAXARGS)
+ && ((q = entry->arg[i]) != (char *) NULL)
+ && (q[0] != '\0')) {
+ if (strcasecmp(q, "absolute") == 0)
+ i++;
+ else if (strcasecmp(q, "relative") == 0)
+ i++;
+ else if (strncasecmp(q, "class=", 6) == 0) {
+ i++;
+ classfound = 1;
+ if (strcasecmp(q + 6, class) == 0)
+ classmatched = 1;
+ }
+ else if (strcmp(q, "-") == 0) {
+ i++;
+ options = 0;
+ }
+ else
+ options = 0;
+ }
+ if (!classfound || classmatched) {
+ char buf[BUFSIZ];
+ /*
+ * UID
+ */
+ if (((i + 3) < MAXARGS)
+ && ((q = entry->arg[i + 3]) != (char *) NULL)
+ && (q[0] != '\0')
+ && (strcmp(q, "*") != 0)) {
+ if (q[0] == '%')
+ sprintf(buf, "%s", q + 1);
+ else {
+ struct passwd *pwent = getpwnam(q);
+ if (pwent)
+ sprintf(buf, "%" PW_UID_FORMAT, pwent->pw_uid);
+ else
+ sprintf(buf, "%d", 0);
+ }
+ entry->arg[i + 3] = (char *) malloc(strlen(buf) + 1);
+ if (entry->arg[i + 3] == NULL) {
+ syslog(LOG_ERR, "calloc error in expand_id");
+ dologout(1);
+ }
+ strcpy(entry->arg[i + 3], buf);
+ }
+ /*
+ * GID
+ */
+ if (((i + 4) < MAXARGS)
+ && ((q = entry->arg[i + 4]) != (char *) NULL)
+ && (q[0] != '\0')
+ && (strcmp(q, "*") != 0)) {
+ if (q[0] == '%')
+ sprintf(buf, "%s", q + 1);
+ else {
+ struct group *grent = getgrnam(q);
+ if (grent)
+ sprintf(buf, "%" GR_GID_FORMAT, grent->gr_gid);
+ else
+ sprintf(buf, "%d", 0);
+ endgrent();
+ }
+ entry->arg[i + 4] = (char *) malloc(strlen(buf) + 1);
+ if (entry->arg[i + 4] == NULL) {
+ syslog(LOG_ERR, "calloc error in expand_id");
+ dologout(1);
+ }
+ strcpy(entry->arg[i + 4], buf);
+ }
+ }
+ }
+}
+
+int fn_check(char *name)
+{
+ /* check to see if this is a valid file name... path-filter <type>
+ * <message_file> <allowed_charset> <disallowed> */
+
+ struct aclmember *entry = NULL;
+ int j;
+ char *path;
+#if ! defined(HAVE_REGEXEC)
+ char *sp;
+#endif
+
+#ifdef M_UNIX
+#ifdef HAVE_REGEX
+ char *regp;
+#endif
+#endif
+
+#ifdef HAVE_REGEXEC
+ regex_t regexbuf;
+ regmatch_t regmatchbuf;
+ int rval;
+#endif
+
+#ifdef LINUX
+ re_syntax_options = RE_SYNTAX_POSIX_EXTENDED;
+#endif
+
+ while (getaclentry("path-filter", &entry) && ARG0 != NULL) {
+ if (type_match(ARG0) && ARG1 && ARG2) {
+
+ /*
+ * check *only* the basename
+ */
+
+ if ((path = strrchr(name, '/')))
+ ++path;
+ else
+ path = name;
+
+ /* is it in the allowed character set? */
+#if defined(HAVE_REGEXEC)
+ if (regcomp(&regexbuf, ARG2, REG_EXTENDED) != 0) {
+ reply(550, "HAVE_REGEX error");
+#elif defined(HAVE_REGEX)
+ if ((sp = regcmp(ARG2, (char *) 0)) == NULL) {
+ reply(550, "HAVE_REGEX error");
+#else
+ if ((sp = re_comp(ARG2)) != 0) {
+ perror_reply(550, sp);
+#endif
+ return (0);
+ }
+#if defined(HAVE_REGEXEC)
+ rval = regexec(&regexbuf, path, 1, &regmatchbuf, 0);
+ regfree(&regexbuf);
+ if (rval != 0) {
+#elif defined(HAVE_REGEX)
+#ifdef M_UNIX
+ regp = regex(sp, path);
+ free(sp);
+ if (regp == NULL) {
+#else
+ if ((regex(sp, path)) == NULL) {
+#endif
+#else
+ if ((re_exec(path)) != 1) {
+#endif
+ pr_mesg(550, ARG1);
+ reply(550, "%s: Permission denied on server. (Filename (accept))", name);
+ return (0);
+ }
+ /* is it in any of the disallowed regexps */
+
+ for (j = 3; j < MAXARGS; ++j) {
+ /* ARGj == entry->arg[j] */
+ if (entry->arg[j]) {
+#if defined(HAVE_REGEXEC)
+ if (regcomp(&regexbuf, entry->arg[j], REG_EXTENDED) != 0) {
+ reply(550, "HAVE_REGEX error");
+#elif defined(HAVE_REGEX)
+ if ((sp = regcmp(entry->arg[j], (char *) 0)) == NULL) {
+ reply(550, "HAVE_REGEX error");
+#else
+ if ((sp = re_comp(entry->arg[j])) != 0) {
+ perror_reply(550, sp);
+#endif
+ return (0);
+ }
+#if defined(HAVE_REGEXEC)
+ rval = regexec(&regexbuf, path, 1, &regmatchbuf, 0);
+ regfree(&regexbuf);
+ if (rval == 0) {
+#elif defined(HAVE_REGEX)
+#ifdef M_UNIX
+ regp = regex(sp, path);
+ free(sp);
+ if (regp != NULL) {
+#else
+ if ((regex(sp, path)) != NULL) {
+#endif
+#else
+ if ((re_exec(path)) == 1) {
+#endif
+ pr_mesg(550, ARG1);
+ reply(550, "%s: Permission denied on server. (Filename (deny))", name);
+ return (0);
+ }
+ }
+ }
+ }
+ }
+ return (1);
+}
+
+int dir_check(char *name, uid_t * uid, gid_t * gid, int *d_mode, int *valid)
+{
+ struct aclmember *entry = NULL;
+ int match_value = -1;
+ char *ap2 = NULL;
+ char *ap3 = NULL;
+ char *ap4 = NULL;
+ char *ap5 = NULL;
+ char *ap6 = NULL;
+ char *ap7 = NULL;
+ char cwdir[MAXPATHLEN];
+ char *pwdir;
+ char abspwdir[MAXPATHLEN];
+ char relpwdir[MAXPATHLEN];
+ char path[MAXPATHLEN];
+ char *sp;
+ struct stat stbuf;
+ int stat_result = -1;
+ char class[1024];
+ extern char *home;
+
+ (void) acl_getclass(class);
+
+ *valid = 0;
+ /* what's our current directory? */
+
+ /* XXX We could use dynamic RAM to store this path, but I'd rather just bail
+ out with an error. The rest of wu is so crufy that a long path might
+ just blow up later */
+
+ if ((strlen(name) + 1) > sizeof(path)) {
+ perror_reply(550, "Path too long");
+ return (-1);
+ }
+
+ strcpy(path, name);
+ sp = strrchr(path, '/');
+ if (sp)
+ *sp = '\0';
+ else
+ strcpy(path, ".");
+
+ if ((fb_realpath(path, cwdir)) == NULL) {
+ perror_reply(550, "Could not determine cwdir");
+ return (-1);
+ }
+
+ if ((fb_realpath(home, relpwdir)) == NULL) {
+ perror_reply(550, "Could not determine pwdir");
+ return (-1);
+ }
+
+ if ((wu_realpath(home, abspwdir, chroot_path)) == NULL) {
+ perror_reply(550, "Could not determine pwdir");
+ return (-1);
+ }
+
+ while (getaclentry("upload", &entry)) {
+ char *q;
+ int i = 0;
+ int options = 1;
+ int classfound = 0;
+ int classmatched = 0;
+ pwdir = abspwdir;
+ while (options
+ && (i < MAXARGS)
+ && ((q = entry->arg[i]) != (char *) NULL)
+ && (q[0] != '\0')) {
+ if (strcasecmp(q, "absolute") == 0) {
+ i++;
+ pwdir = abspwdir;
+ }
+ else if (strcasecmp(q, "relative") == 0) {
+ i++;
+ pwdir = relpwdir;
+ }
+ else if (strncasecmp(q, "class=", 6) == 0) {
+ i++;
+ classfound = 1;
+ if (strcasecmp(q + 6, class) == 0)
+ classmatched = 1;
+ }
+ else if (strcmp(q, "-") == 0) {
+ i++;
+ options = 0;
+ }
+ else
+ options = 0;
+ }
+ if (!classfound || classmatched) {
+ int j;
+ if (((i + 1) < MAXARGS)
+ && ((q = entry->arg[i]) != (char *) NULL)
+ && (q[0] != '\0')
+ && (0 < path_compare(q, pwdir))
+ && ((j = path_compare(entry->arg[i + 1], cwdir)) >= match_value)) {
+ match_value = j;
+
+ ap2 = NULL;
+ if (((i + 2) < MAXARGS)
+ && ((q = entry->arg[i + 2]) != (char *) NULL)
+ && (q[0] != '\0'))
+ ap2 = q;
+
+ ap3 = NULL;
+ if (((i + 3) < MAXARGS)
+ && ((q = entry->arg[i + 3]) != (char *) NULL)
+ && (q[0] != '\0'))
+ ap3 = q;
+
+ ap4 = NULL;
+ if (((i + 4) < MAXARGS)
+ && ((q = entry->arg[i + 4]) != (char *) NULL)
+ && (q[0] != '\0'))
+ ap4 = q;
+
+ ap5 = NULL;
+ if (((i + 5) < MAXARGS)
+ && ((q = entry->arg[i + 5]) != (char *) NULL)
+ && (q[0] != '\0'))
+ ap5 = q;
+
+ ap6 = NULL;
+ if (((i + 6) < MAXARGS)
+ && ((q = entry->arg[i + 6]) != (char *) NULL)
+ && (q[0] != '\0'))
+ ap6 = q;
+
+ ap7 = NULL;
+ if (((i + 7) < MAXARGS)
+ && ((q = entry->arg[i + 7]) != (char *) NULL)
+ && (q[0] != '\0'))
+ ap7 = q;
+ }
+ }
+ }
+
+ if (anonymous && (match_value < 0)) {
+ reply(550, "%s: Permission denied on server. (Upload dirs)", name);
+ return (0);
+ }
+ if ((ap2 && !strcasecmp(ap2, "no"))
+ || (ap3 && !strcasecmp(ap3, "nodirs"))
+ || (ap6 && !strcasecmp(ap6, "nodirs"))) {
+ reply(550, "%s: Permission denied on server. (Upload dirs)", name);
+ return (0);
+ }
+ if ((ap3 && *ap3 == '*') || (ap4 && *ap4 == '*'))
+ stat_result = stat(path, &stbuf);
+ if (ap3) {
+ if ((ap3[0] != '*') || (ap3[1] != '\0'))
+ *uid = atoi(ap3); /* the uid */
+ else if (stat_result == 0)
+ *uid = stbuf.st_uid;
+ }
+ if (ap4) {
+ if ((ap4[0] != '*') || (ap4[1] != '\0'))
+ *gid = atoi(ap4); /* the gid */
+ else if (stat_result == 0)
+ *gid = stbuf.st_gid;
+ }
+ if (ap7) {
+ sscanf(ap7, "%o", d_mode);
+ *valid = 1;
+ }
+ else if (ap5) {
+ sscanf(ap5, "%o", d_mode);
+ if (*d_mode & 0600)
+ *d_mode |= 0100;
+ if (*d_mode & 0060)
+ *d_mode |= 0010;
+ if (*d_mode & 0006)
+ *d_mode |= 0001;
+ *valid = 1;
+ }
+ return (1);
+}
+
+int upl_check(char *name, uid_t * uid, gid_t * gid, int *f_mode, int *valid)
+{
+ int match_value = -1;
+ char cwdir[MAXPATHLEN];
+ char *pwdir;
+ char abspwdir[MAXPATHLEN];
+ char relpwdir[MAXPATHLEN];
+ char path[MAXPATHLEN];
+ char *sp;
+ struct stat stbuf;
+ int stat_result = -1;
+ char *ap2 = NULL;
+ char *ap3 = NULL;
+ char *ap4 = NULL;
+ char *ap5 = NULL;
+ struct aclmember *entry = NULL;
+ char class[1024];
+ extern char *home;
+
+ *valid = 0;
+ (void) acl_getclass(class);
+
+ /* what's our current directory? */
+
+ /* XXX We could use dynamic RAM to store this path, but I'd rather just bail
+ out with an error. The rest of wu is so crufy that a long path might
+ just blow up later */
+
+ if ((strlen(name) + 1) > sizeof(path)) {
+ perror_reply(553, "Path too long");
+ return (-1);
+ }
+
+ strcpy(path, name);
+ sp = strrchr(path, '/');
+ if (sp)
+ *sp = '\0';
+ else
+ strcpy(path, ".");
+
+ if ((fb_realpath(path, cwdir)) == NULL) {
+ perror_reply(553, "Could not determine cwdir");
+ return (-1);
+ }
+
+ if ((wu_realpath(home, abspwdir, chroot_path)) == NULL) {
+ perror_reply(553, "Could not determine pwdir");
+ return (-1);
+ }
+
+ if ((fb_realpath(home, relpwdir)) == NULL) {
+ perror_reply(553, "Could not determine pwdir");
+ return (-1);
+ }
+
+ /*
+ * we are doing a "best match"... ..so we keep track of what "match
+ * value" we have received so far...
+ */
+ while (getaclentry("upload", &entry)) {
+ char *q;
+ int i = 0;
+ int options = 1;
+ int classfound = 0;
+ int classmatched = 0;
+ pwdir = abspwdir;
+ while (options
+ && (i < MAXARGS)
+ && ((q = entry->arg[i]) != (char *) NULL)
+ && (q[0] != '\0')) {
+ if (strcasecmp(q, "absolute") == 0) {
+ i++;
+ pwdir = abspwdir;
+ }
+ else if (strcasecmp(q, "relative") == 0) {
+ i++;
+ pwdir = relpwdir;
+ }
+ else if (strncasecmp(q, "class=", 6) == 0) {
+ i++;
+ classfound = 1;
+ if (strcasecmp(q + 6, class) == 0)
+ classmatched = 1;
+ }
+ else if (strcmp(q, "-") == 0) {
+ i++;
+ options = 0;
+ }
+ else
+ options = 0;
+ }
+ if (!classfound || classmatched) {
+ int j;
+ if (((i + 1) < MAXARGS)
+ && ((q = entry->arg[i]) != (char *) NULL)
+ && (q[0] != '\0')
+ && (0 < path_compare(q, pwdir))
+ && ((j = path_compare(entry->arg[i + 1], cwdir)) >= match_value)) {
+ match_value = j;
+
+ ap2 = NULL;
+ if (((i + 2) < MAXARGS)
+ && ((q = entry->arg[i + 2]) != (char *) NULL)
+ && (q[0] != '\0'))
+ ap2 = q;
+
+ ap3 = NULL;
+ if (((i + 3) < MAXARGS)
+ && ((q = entry->arg[i + 3]) != (char *) NULL)
+ && (q[0] != '\0'))
+ ap3 = q;
+
+ ap4 = NULL;
+ if (((i + 4) < MAXARGS)
+ && ((q = entry->arg[i + 4]) != (char *) NULL)
+ && (q[0] != '\0'))
+ ap4 = q;
+
+ ap5 = NULL;
+ if (((i + 5) < MAXARGS)
+ && ((q = entry->arg[i + 5]) != (char *) NULL)
+ && (q[0] != '\0'))
+ ap5 = q;
+ }
+ }
+ }
+
+ if (ap3
+ && ((!strcasecmp("dirs", ap3))
+ || (!strcasecmp("nodirs", ap3))))
+ ap3 = NULL;
+
+ /*
+ * if we did get matches ... else don't do any of this stuff
+ */
+ if (match_value >= 0) {
+ if (!strcasecmp(ap2, "yes")) {
+ if ((ap3 && *ap3 == '*') || (ap4 && *ap4 == '*'))
+ stat_result = stat(path, &stbuf);
+ if (ap3) {
+ if ((ap3[0] != '*') || (ap3[1] != '\0'))
+ *uid = atoi(ap3); /* the uid */
+ else if (stat_result == 0)
+ *uid = stbuf.st_uid;
+ }
+ if (ap4) {
+ if ((ap4[0] != '*') || (ap4[1] != '\0'))
+ *gid = atoi(ap4); /* the gid */
+ else if (stat_result == 0)
+ *gid = stbuf.st_gid;
+ *valid = 1;
+ }
+ if (ap5)
+ sscanf(ap5, "%o", f_mode); /* the mode */
+ }
+ else {
+ reply(553, "%s: Permission denied on server. (Upload)", name);
+ return (-1);
+ }
+ }
+ else {
+ /*
+ * upload defaults to "permitted"
+ */
+ /* Not if anonymous */
+ if (anonymous) {
+ reply(553, "%s: Permission denied on server. (Upload)", name);
+ return (-1);
+ }
+ return (1);
+ }
+
+ return (match_value);
+}
+
+int del_check(char *name)
+{
+ int pdelete = (anonymous ? 0 : 1);
+ struct aclmember *entry = NULL;
+
+ while (getaclentry("delete", &entry) && ARG0 && ARG1 != NULL) {
+ if (type_match(ARG1))
+ if (anonymous) {
+ if (*ARG0 == 'y')
+ pdelete = 1;
+ }
+ else if (*ARG0 == 'n')
+ pdelete = 0;
+ }
+
+/* H* fix: no deletion, period. You put a file here, I get to look at it. */
+#ifdef PARANOID
+ pdelete = 0;
+#endif
+
+ if (!pdelete) {
+ reply(553, "%s: Permission denied on server. (Delete)", name);
+ return (0);
+ }
+ else {
+ return (1);
+ }
+}
+
+/* The following is from the Debian add-ons. */
+
+#define lbasename(x) (strrchr(x,'/')?1+strrchr(x,'/'):x)
+
+int regexmatch(char *name, char *rgexp)
+{
+
+#ifdef M_UNIX
+#ifdef HAVE_REGEX
+ char *regp;
+#endif
+#endif
+
+#ifdef HAVE_REGEXEC
+ regex_t regexbuf;
+ regmatch_t regmatchbuf;
+ int rval;
+#else
+ char *sp;
+#endif
+
+#if defined(HAVE_REGEXEC)
+ if (regcomp(&regexbuf, rgexp, REG_EXTENDED) != 0) {
+ reply(553, "HAVE_REGEX error");
+#elif defined(HAVE_REGEX)
+ if ((sp = regcmp(rgexp, (char *) 0)) == NULL) {
+ reply(553, "HAVE_REGEX error");
+#else
+ if ((sp = re_comp(rgexp)) != 0) {
+ perror_reply(553, sp);
+#endif
+ return (0);
+ }
+
+#if defined(HAVE_REGEXEC)
+ rval = regexec(&regexbuf, name, 1, &regmatchbuf, 0);
+ regfree(&regexbuf);
+ if (rval != 0) {
+#elif defined(HAVE_REGEX)
+#ifdef M_UNIX
+ regp = regex(sp, name);
+ free(sp);
+ if (regp == NULL) {
+#else
+ if ((regex(sp, name)) == NULL) {
+#endif
+#else
+ if ((re_exec(name)) != 1) {
+#endif
+ return (0);
+ }
+ return (1);
+}
+
+static int allow_retrieve(char *name)
+{
+ char realname[MAXPATHLEN + 1];
+ char localname[MAXPATHLEN + 1];
+ char *whichname;
+ int i;
+ struct aclmember *entry = NULL;
+ char *p, *q;
+ int options;
+ int classfound;
+ int classmatched;
+ char class[1024];
+
+ (void) acl_getclass(class);
+ if ((name == (char *) NULL)
+ || (*name == '\0'))
+ return 0;
+ fb_realpath(name, localname);
+ wu_realpath(name, realname, chroot_path);
+ while (getaclentry("allow-retrieve", &entry)) {
+ whichname = realname;
+ i = 0;
+ options = 1;
+ classfound = 0;
+ classmatched = 0;
+ while (options
+ && (i < MAXARGS)
+ && ((q = entry->arg[i]) != (char *) NULL)
+ && (q[0] != '\0')) {
+ if (strcasecmp(q, "absolute") == 0) {
+ i++;
+ whichname = realname;
+ }
+ else if (strcasecmp(q, "relative") == 0) {
+ i++;
+ whichname = localname;
+ }
+ else if (strncasecmp(q, "class=", 6) == 0) {
+ i++;
+ classfound = 1;
+ if (strcasecmp(q + 6, class) == 0)
+ classmatched = 1;
+ }
+ else if (strcmp(q, "-") == 0) {
+ i++;
+ options = 0;
+ }
+ else
+ options = 0;
+ }
+ if (!classfound || classmatched) {
+ for (; (i < MAXARGS) && ((q = entry->arg[i]) != (char *) NULL) && (q[0] != '\0'); i++) {
+ p = (q[0] == '/') ? whichname : lbasename(whichname);
+ if (!wu_fnmatch(q, p, FNM_PATHNAME | FNM_LEADING_DIR)) {
+ return 1;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+int checknoretrieve(char *name)
+{
+ char realname[MAXPATHLEN + 1];
+ char localname[MAXPATHLEN + 1];
+ char *whichname;
+ int i;
+ struct aclmember *entry = NULL;
+ char *p, *q;
+ int options;
+ int classfound;
+ int classmatched;
+ char class[1024];
+
+ extern struct passwd *pw;
+ extern char *remoteident;
+
+ (void) acl_getclass(class);
+ if ((name == (char *) NULL)
+ || (*name == '\0'))
+ return 0;
+ fb_realpath(name, localname);
+ wu_realpath(name, realname, chroot_path);
+ while (getaclentry("noretrieve", &entry)) {
+ whichname = realname;
+ i = 0;
+ options = 1;
+ classfound = 0;
+ classmatched = 0;
+ while (options
+ && (i < MAXARGS)
+ && ((q = entry->arg[i]) != (char *) NULL)
+ && (q[0] != '\0')) {
+ if (strcasecmp(q, "absolute") == 0) {
+ i++;
+ whichname = realname;
+ }
+ else if (strcasecmp(q, "relative") == 0) {
+ i++;
+ whichname = localname;
+ }
+ else if (strncasecmp(q, "class=", 6) == 0) {
+ i++;
+ classfound = 1;
+ if (strcasecmp(q + 6, class) == 0)
+ classmatched = 1;
+ }
+ else if (strcmp(q, "-") == 0) {
+ i++;
+ options = 0;
+ }
+ else
+ options = 0;
+ }
+ if (!classfound || classmatched) {
+ for (; (i < MAXARGS) && ((q = entry->arg[i]) != (char *) NULL) && (q[0] != '\0'); i++) {
+ p = (q[0] == '/') ? whichname : lbasename(whichname);
+ if (!wu_fnmatch(q, p, FNM_PATHNAME | FNM_LEADING_DIR)) {
+ if (!allow_retrieve(name)) {
+ reply(550, "%s is marked unretrievable", localname);
+ return 1;
+ }
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+#ifdef QUOTA
+
+#ifndef MNTMAXSTR
+#define MNTMAXSTR 2048 /* And hope it's enough */
+#endif
+
+#ifdef QUOTA_DEVICE
+
+int path_to_device(char *pathname, char *result)
+{
+ FILE *fp;
+#ifdef HAS_OLDSTYLE_GETMNTENT
+ struct mnttab static_mp;
+ struct mnttab *mp = &static_mp;
+#else
+ struct mntent *mp;
+#endif
+ struct mount_ent {
+ char mnt_fsname[MNTMAXSTR], mnt_dir[MNTMAXSTR];
+ struct mount_ent *next;
+ } mountent;
+ struct mount_ent *current, *start, *new;
+ char path[1024], mnt_dir[1024], *pos;
+ int flag = 1;
+
+ start = current = NULL;
+#ifdef HAS_OLDSTYLE_GETMNTENT
+ fp = fopen(MNTTAB, "r");
+#else
+ fp = setmntent(MNTTAB, "r");
+#endif
+ if (fp == NULL)
+ return 0;
+#ifdef HAS_OLDSTYLE_GETMNTENT
+ while (getmntent(fp, &static_mp) == 0)
+#else
+ while (mp = getmntent(fp))
+#endif
+ {
+ if (!(new = (struct mount_ent *) malloc(sizeof(mountent)))) {
+ perror("malloc");
+ flag = 0;
+ break;
+ }
+
+ if (!start)
+ start = current = new;
+ else
+ current = current->next = new;
+
+#ifdef HAS_OLDSTYLE_GETMNTENT
+ strncpy(current->mnt_fsname, mp->mnt_special, strlen(mp->mnt_special) + 1);
+ strncpy(current->mnt_dir, mp->mnt_mountp, strlen(mp->mnt_mountp) + 1);
+#else
+ strncpy(current->mnt_fsname, mp->mnt_fsname, strlen(mp->mnt_fsname) + 1);
+ strncpy(current->mnt_dir, mp->mnt_dir, strlen(mp->mnt_dir) + 1);
+#endif
+ }
+#ifdef HAS_OLDSTYLE_GETMNTENT
+ fclose(fp);
+#else
+ endmntent(fp);
+#endif
+ current->next = NULL;
+
+ wu_realpath(pathname, path, chroot_path);
+
+ while (*path && flag) {
+ current = start;
+ while (current && flag) {
+ if (strcmp(current->mnt_dir, "swap")) {
+ wu_realpath(current->mnt_dir, mnt_dir, chroot_path);
+ if (!strcmp(mnt_dir, path)) {
+ flag = 0;
+ /* no support for remote quota yet */
+ if (!strchr(current->mnt_fsname, ':'))
+ strcpy(result, current->mnt_fsname);
+ }
+ }
+ current = current->next;
+ }
+ if (!((pos = strrchr(path, '/')) - path) && strlen(path) > 1)
+ strcpy(path, "/");
+ else
+ path[pos - path] = '\0';
+ }
+ while (current) {
+ new = current->next;
+ free(current);
+ current = new;
+ }
+ return 1;
+}
+#endif
+
+void get_quota(char *fs, int uid)
+{
+ char mnt_fsname[MNTMAXSTR];
+#ifdef HAS_NO_QUOTACTL
+ int dirfd;
+ struct quotctl qp;
+#endif
+
+ /*
+ * Getting file system quota information can take a noticeable amount
+ * of time, so only get quota information for specified users.
+ * quota-info <uid-range> [<uid-range> ...]
+ */
+ if (!uid_match("quota-info", uid))
+ return;
+
+#ifdef HAS_NO_QUOTACTL
+ if (path_to_device(fs, mnt_fsname)) {
+ dirfd = open(fs, O_RDONLY);
+ qp.op = Q_GETQUOTA;
+ qp.uid = uid;
+ qp.addr = (char *) &quota;
+ ioctl(dirfd, Q_QUOTACTL, &qp);
+ close(dirfd);
+ }
+#else
+#ifdef QUOTA_DEVICE
+
+ if (path_to_device(fs, mnt_fsname))
+#ifdef QCMD
+ quotactl(QCMD(Q_GETQUOTA, USRQUOTA), mnt_fsname, uid, (char *) &quota);
+#else
+ quotactl(Q_GETQUOTA, mnt_fsname, uid, (char *) &quota);
+#endif
+#else
+ quotactl(fs, QCMD(Q_GETQUOTA, USRQUOTA), uid, (char *) &quota);
+#endif
+#endif /* HAS_NO_QUOTACTL */
+}
+
+char *time_quota(long curstate, long softlimit, long timelimit, char *timeleft)
+{
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+ if (softlimit && curstate >= softlimit) {
+ if (timelimit == 0) {
+ strcpy(timeleft, "NOT STARTED");
+ }
+ else if (timelimit > tv.tv_sec) {
+ fmttime(timeleft, timelimit - tv.tv_sec);
+ }
+ else {
+ strcpy(timeleft, "EXPIRED");
+ }
+ }
+ else {
+ timeleft[0] = '\0';
+ }
+ return (timeleft);
+}
+
+void fmttime(char *buf, register long time)
+{
+ int i;
+ static struct {
+ int c_secs; /* conversion units in secs */
+ char *c_str; /* unit string */
+ } cunits[] = {
+ {
+ 60 *60 * 24 * 28, "months"
+ } ,
+ {
+ 60 *60 * 24 * 7, "weeks"
+ } ,
+ {
+ 60 *60 * 24, "days"
+ } ,
+ {
+ 60 *60, "hours"
+ } ,
+ {
+ 60, "mins"
+ } ,
+ {
+ 1, "secs"
+ }
+ };
+
+ if (time <= 0) {
+ strcpy(buf, "EXPIRED");
+ return;
+ }
+ for (i = 0; i < sizeof(cunits) / sizeof(cunits[0]); i++) {
+ if (time >= cunits[i].c_secs)
+ break;
+ }
+ sprintf(buf, "%.1f %s", (double) time / cunits[i].c_secs, cunits[i].c_str);
+}
+
+#endif
+
+#ifdef THROUGHPUT
+
+int file_compare(char *patterns, char *file)
+{
+ char buf[MAXPATHLEN+1];
+ char *cp;
+ char *cp2;
+ int i;
+ int matches = 0;
+
+ strncpy(buf, patterns, sizeof(buf) - 1);
+ buf[sizeof(buf) - 2] = '\0';
+ i = strlen(buf);
+ buf[i++] = ',';
+ buf[i++] = '\0';
+
+ cp = buf;
+ while ((cp2 = strchr(cp, ',')) != NULL) {
+ *cp2++ = '\0';
+ if (wu_fnmatch(cp, file, FNM_PATHNAME) == 0) {
+ matches = 1;
+ break;
+ }
+ cp = cp2;
+ }
+ return matches;
+}
+
+int remote_compare(char *patterns)
+{
+ char buf[MAXPATHLEN+1];
+ char *cp;
+ char *cp2;
+ int i;
+ int matches = 0;
+
+ strncpy(buf, patterns, sizeof(buf) - 1);
+ buf[sizeof(buf) - 2] = '\0';
+ i = strlen(buf);
+ buf[i++] = ',';
+ buf[i++] = '\0';
+
+ cp = buf;
+ while ((cp2 = strchr(cp, ',')) != NULL) {
+ *cp2++ = '\0';
+ if (hostmatch(cp, remoteaddr, remotehost)) {
+ matches = 1;
+ break;
+ }
+ cp = cp2;
+ }
+ return matches;
+}
+
+void throughput_calc(char *name, int *bps, double *bpsmult)
+{
+ int match_value = -1;
+ char cwdir[MAXPATHLEN];
+ char pwdir[MAXPATHLEN];
+ char path[MAXPATHLEN];
+ char file[MAXPATHLEN];
+ char *ap3 = NULL, *ap4 = NULL;
+ struct aclmember *entry = NULL;
+ extern char *home;
+ char *sp;
+ int i;
+
+ /* default is maximum throughput */
+ *bps = -1;
+ *bpsmult = 1.0;
+
+ /* XXX We could use dynamic RAM to store this path, but I'd rather just bail
+ out with an error. The rest of wu is so crufy that a long path might
+ just blow up later */
+
+ if ((strlen(name) + 1) > sizeof(path)) {
+ return;
+ }
+
+ /* what's our current directory? */
+ strcpy(path, name);
+ if ((sp = strrchr(path, '/')))
+ *sp = '\0';
+ else
+ strcpy(path, ".");
+ if ((sp = strrchr(name, '/')))
+ strcpy(file, sp + 1);
+ else
+ strcpy(file, name);
+ if ((fb_realpath(path, cwdir)) == NULL) {
+ return;
+ }
+
+ wu_realpath(home, pwdir, chroot_path);
+
+ /* find best matching entry */
+ while (getaclentry("throughput", &entry) && ARG0 && ARG1 && ARG2 && ARG3 && ARG4 && ARG5 != NULL) {
+ if ((0 < path_compare(ARG0, pwdir))
+ && ((i = path_compare(ARG1, cwdir)) >= match_value)
+ ) {
+ if (file_compare(ARG2, file)) {
+ if (remote_compare(ARG5)) {
+ match_value = i;
+ ap3 = ARG3;
+ ap4 = ARG4;
+ }
+ }
+ }
+ }
+
+ /* if we did get matches */
+ if (match_value >= 0) {
+ if (strcasecmp(ap3, "oo") == 0)
+ *bps = -1;
+ else
+ *bps = atoi(ap3);
+ if (strcmp(ap4, "-") == 0)
+ *bpsmult = 1.0;
+ else
+ *bpsmult = atof(ap4);
+ }
+ return;
+}
+
+void throughput_adjust(char *name)
+{
+ int match_value = -1;
+ char pwdir[MAXPATHLEN];
+ char cwdir[MAXPATHLEN];
+ char path[MAXPATHLEN];
+ char file[MAXPATHLEN];
+ char buf[MAXPATHLEN];
+ char *ap3 = NULL, *ap4 = NULL;
+ char **pap;
+ struct aclmember *entry = NULL;
+ extern char *home;
+ char *sp;
+ int i;
+
+ /* XXX We could use dynamic RAM to store this path, but I'd rather just bail
+ out with an error. The rest of wu is so crufy that a long path might
+ just blow up later */
+
+ if ((strlen(name) + 1) > sizeof(path)) {
+ return;
+ }
+
+ /* what's our current directory? */
+ strcpy(path, name);
+ if ((sp = strrchr(path, '/')))
+ *sp = '\0';
+ else
+ strcpy(path, ".");
+ if ((sp = strrchr(name, '/')))
+ strcpy(file, sp + 1);
+ else
+ strcpy(file, name);
+ if ((fb_realpath(path, cwdir)) == NULL) {
+ return;
+ }
+
+ wu_realpath(home, pwdir, chroot_path);
+
+ /* find best matching entry */
+ while (getaclentry("throughput", &entry) && ARG0 && ARG1 && ARG2 && ARG3 && ARG4 && ARG5 != NULL) {
+ if ((0 < path_compare(ARG0, pwdir))
+ && ((i = path_compare(ARG1, cwdir)) >= match_value)
+ ) {
+ if (file_compare(ARG2, file)) {
+ if (remote_compare(ARG5)) {
+ match_value = i;
+ ap3 = ARG3;
+ pap = ARG;
+ ap4 = ARG4;
+ }
+ }
+ }
+ }
+
+ /* if we did get matches */
+ if (match_value >= 0) {
+ if (strcasecmp(ap3, "oo") != 0) {
+ if (strcmp(ap4, "-") != 0) {
+ sprintf(buf, "%.0f", atoi(ap3) * atof(ap4));
+ pap[3] = (char *) malloc(strlen(buf) + 1);
+ if (pap[3] == NULL) {
+ syslog(LOG_ERR, "malloc error in throughput_adjust");
+ dologout(1);
+ }
+ /* Use ARG6 to keep track of malloced memory */
+ if (pap[6])
+ free(pap[6]);
+ pap[6] = pap[3];
+ strcpy(pap[3], buf);
+ }
+ }
+ }
+ return;
+}
+
+#endif
+
+#ifdef SOLARIS_2
+static int CheckMethod = 1;
+#else
+static int CheckMethod = 0;
+#endif
+
+void SetCheckMethod(const char *method)
+{
+ if ((strcasecmp(method, "md5") == 0)
+ || (strcasecmp(method, "rfc1321") == 0))
+ CheckMethod = 0;
+ else if ((strcasecmp(method, "crc") == 0)
+ || (strcasecmp(method, "posix") == 0))
+ CheckMethod = 1;
+ else {
+ reply(500, "Unrecognized checksum method");
+ return;
+ }
+ switch (CheckMethod) {
+ default:
+ reply(200, "Checksum method is now: MD5 (RFC1321)");
+ break;
+ case 1:
+ reply(200, "Checksum method is now: CRC (POSIX)");
+ break;
+ }
+}
+
+void ShowCheckMethod(void)
+{
+ switch (CheckMethod) {
+ default:
+ reply(200, "Current checksum method: MD5 (RFC1321)");
+ break;
+ case 1:
+ reply(200, "Current checksum method: CRC (POSIX)");
+ break;
+ }
+}
+
+void CheckSum(char *pathname)
+{
+ char *cmd;
+ char buf[MAXPATHLEN];
+ FILE *cmdf;
+ struct stat st;
+
+ if (stat(pathname, &st) == 0) {
+ if ((st.st_mode & S_IFMT) != S_IFREG) {
+ reply(500, "%s: not a plain file.", pathname);
+ return;
+ }
+ }
+ else {
+ perror_reply(550, pathname);
+ return;
+ }
+
+ switch (CheckMethod) {
+ default:
+ cmd = "/bin/md5sum";
+ break;
+ case 1:
+ cmd = "/bin/cksum";
+ break;
+ }
+
+ if (strlen(cmd) + 1 + strlen(pathname) + 1 > sizeof(buf)) {
+ reply(500, "Pathname too long");
+ return;
+ }
+ sprintf(buf, "%s %s", cmd, pathname);
+
+ cmdf = ftpd_popen(buf, "r", 0);
+ if (!cmdf) {
+ perror_reply(550, cmd);
+ }
+ else {
+ if (fgets(buf, sizeof buf, cmdf)) {
+ char *crptr = strchr(buf, '\n');
+ if (crptr != NULL)
+ *crptr = '\0';
+ reply(200, "%s", buf);
+ }
+ ftpd_pclose(cmdf);
+ }
+}
+
+void CheckSumLastFile(void)
+{
+ extern char LastFileTransferred[];
+
+ if (LastFileTransferred[0] == '\0')
+ reply(500, "Nothing transferred yet");
+ else
+ CheckSum(LastFileTransferred);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/extensions.h b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/extensions.h
new file mode 100644
index 0000000000..4e05f47552
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/extensions.h
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/****************************************************************************
+ Copyright (c) 1999,2000 WU-FTPD Development Group.
+ All rights reserved.
+
+ Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994
+ The Regents of the University of California.
+ Portions Copyright (c) 1993, 1994 Washington University in Saint Louis.
+ Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc.
+ Portions Copyright (c) 1989 Massachusetts Institute of Technology.
+ Portions Copyright (c) 1998 Sendmail, Inc.
+ Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P. Allman.
+ Portions Copyright (c) 1997 by Stan Barber.
+ Portions Copyright (c) 1997 by Kent Landfield.
+ Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ Free Software Foundation, Inc.
+
+ Use and distribution of this software and its source code are governed
+ by the terms and conditions of the WU-FTPD Software License ("LICENSE").
+
+ If you did not receive a copy of the license, it may be obtained online
+ at http://www.wu-ftpd.org/license.html.
+
+ $Id: extensions.h,v 1.12 2000/07/01 18:17:39 wuftpd Exp $
+
+****************************************************************************/
+#define LOG_IN 0
+#define C_WD 1
+#define BANNER 2
+
+#ifndef ALIGN
+#define ALIGN(x) ((x) + (sizeof(long) - (x) % sizeof(long)))
+#endif
+
+#define O_COMPRESS (1 << 0) /* file was compressed */
+#define O_UNCOMPRESS (1 << 1) /* file was uncompressed */
+#define O_TAR (1 << 2) /* file was tar'ed */
+
+#define MAXARGS 50
+#define MAXKWLEN 20
+
+struct aclmember {
+ struct aclmember *next;
+ char keyword[MAXKWLEN];
+ char *arg[MAXARGS];
+};
+
+#define ARG0 entry->arg[0]
+#define ARG1 entry->arg[1]
+#define ARG2 entry->arg[2]
+#define ARG3 entry->arg[3]
+#define ARG4 entry->arg[4]
+#define ARG5 entry->arg[5]
+#define ARG6 entry->arg[6]
+#define ARG7 entry->arg[7]
+#define ARG8 entry->arg[8]
+#define ARG9 entry->arg[9]
+#define ARG entry->arg
+
+/* Header at start of PID file */
+struct pidfile_header {
+ int count;
+ time_t last_checked;
+};
+
+/* File transfer logging (xferlog) */
+#include <sys/param.h>
+
+#define MAXXFERSTRLEN (MAXPATHLEN + 1024)
+#define MAXSPACTCHARS 4
+
+struct xferstat {
+ char *filename;
+ char access_mode;
+ char completion;
+ char transfer_direction;
+ char transfer_type;
+ char special_action[MAXSPACTCHARS];
+ int auth;
+ int transfer_time;
+ off_t filesize;
+ off_t restart_offset;
+ off_t transfer_bytes;
+};
+extern int xferdone;
+extern char xferlog_format[];
+extern struct xferstat xfervalues;
+
+/* Type values for the various passive modes supported by the server */
+#define TYPE_PASV 0
+#ifdef INET6
+#define TYPE_EPSV 1
+#define TYPE_LPSV 2
+#endif
+
+#ifdef QUOTA
+#ifdef TIME_WITH_SYS_TIME
+#include <time.h>
+#include <sys/time.h>
+#else
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#else
+#include <time.h>
+#endif
+#endif
+
+#ifdef IRIX
+#define QUOTA_BLOCKS
+#define QUOTA_DEVICE
+#include <mntent.h>
+#include <sys/quota.h>
+#endif
+
+#ifdef SOLARIS_2
+#define QUOTA_BLOCKS
+#define QUOTA_DEVICE
+#define HAS_OLDSTYLE_GETMNTENT
+#define HAS_NO_QUOTACTL
+#include <sys/mntent.h>
+#include <sys/mnttab.h>
+#include <sys/fs/ufs_quota.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#endif
+
+#ifdef SUNOS
+#define QUOTA_BLOCKS
+#define QUOTA_DEVICE
+#include <mntent.h>
+#include <ufs/quota.h>
+#endif
+
+#ifdef AIX
+#include <jfs/quota.h>
+#endif
+
+#ifdef DIGITAL
+#include <ufs/quota.h>
+#endif
+
+#ifdef BSDI
+#include <ufs/ufs/quota.h>
+#endif
+
+#ifdef LINUX
+#define QUOTA_DEVICE
+#include <mntent.h>
+#include <asm/types.h>
+#ifdef HAVE_SYS_QUOTA_H
+#include <sys/quota.h>
+#else
+#include <linux/quota.h>
+#endif
+#endif
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_SYS_QUOTA_H /* This is defined only in the autoconf'ed build */
+#include <sys/quota.h>
+#endif
+#ifdef HAVE_MNTENT_H
+#include <mntent.h>
+#endif
+
+#endif /* QUOTA */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftp.xml b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftp.xml
new file mode 100644
index 0000000000..6fe406e81d
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftp.xml
@@ -0,0 +1,75 @@
+<?xml version='1.0'?>
+<!DOCTYPE service_bundle SYSTEM '/usr/share/lib/xml/dtd/service_bundle.dtd.1'>
+
+<!--
+ Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ ident "%Z%%M% %I% %E% SMI"
+
+ NOTE: This service manifest is not editable; its contents will
+ be overwritten by package or patch operations, including
+ operating system upgrade. Make customizations in a different
+ file.
+
+ Service manifest for in.ftpd
+-->
+
+<service_bundle type='manifest' name='SUNWftpr:ftp'>
+
+<service
+ name='network/ftp'
+ type='service'
+ version='1'>
+
+ <create_default_instance enabled='false' />
+
+ <restarter>
+ <service_fmri value='svc:/network/inetd:default' />
+ </restarter>
+
+ <exec_method
+ type='method'
+ name='inetd_start'
+ exec='/usr/sbin/in.ftpd -a'
+ timeout_seconds='0'>
+ <method_context>
+ <method_credential user='root' group='root' />
+ </method_context>
+ </exec_method>
+
+ <exec_method
+ type='method'
+ name='inetd_disable'
+ exec=':kill'
+ timeout_seconds='0'>
+ </exec_method>
+
+ <property_group name='inetd' type='framework'>
+ <stability value='Evolving' />
+ <propval name='endpoint_type' type='astring' value='stream' />
+ <propval name='name' type='astring' value='ftp' />
+ <propval name='proto' type='astring' value='tcp6' />
+ <propval name='wait' type='boolean' value='false' />
+ <propval name='isrpc' type='boolean' value='false' />
+ </property_group>
+
+ <stability value='Unstable' />
+
+ <template>
+ <common_name>
+ <loctext xml:lang='C'>
+ FTP server
+ </loctext>
+ </common_name>
+ <documentation>
+ <manpage title='in.ftpd' section='1M'
+ manpath='/usr/share/man' />
+ <manpage title='ftpd' section='1M'
+ manpath='/usr/share/man' />
+ </documentation>
+ </template>
+
+</service>
+
+</service_bundle>
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpaccess b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpaccess
new file mode 100644
index 0000000000..ba932b06c9
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpaccess
@@ -0,0 +1,58 @@
+# ident "%Z%%M% %I% %E% SMI"
+#
+# FTP server configuration file, see ftpaccess(4).
+#
+
+class realusers real *
+class guestusers guest *
+class anonusers anonymous *
+
+loginfails 3
+passwd-check trivial warn
+private no
+shutdown /etc/ftpd/shutdown.msg
+# email user@hostname
+# guestuser username
+# rhostlookup no
+
+keepalive yes
+recvbuf 65536 real,guest,anonymous
+sendbuf 65536 real,guest,anonymous
+# flush-wait no anonymous
+# passive ports 0.0.0.0/0 32768 65535
+# timeout data 600
+# timeout idle 300
+
+banner /etc/ftpd/banner.msg
+greeting brief
+message /etc/ftpd/welcome.msg login
+message .message cwd=*
+readme README* login
+readme README* cwd=*
+# quota-info *
+
+chmod no anonymous
+delete no anonymous
+overwrite no anonymous
+rename no anonymous
+umask no anonymous
+
+compress yes realusers guestusers anonusers
+tar yes realusers guestusers anonusers
+
+path-filter guest,anonymous /etc/ftpd/filename.msg ^[[:alnum:]._-]*$ ^[.-]
+
+noretrieve relative class=anonusers /
+allow-retrieve relative class=anonusers /pub
+
+upload class=anonusers * * no nodirs
+# upload class=anonusers * /incoming yes ftpadm ftpadm 0440 nodirs
+
+# log commands real,guest,anonymous
+# log security real,guest,anonymous
+# log transfers real,guest,anonymous inbound,outbound
+# xferlog format %T %Xt %R %Xn %XP %Xy %Xf %Xd %Xm %U ftp %Xa %u %Xc %Xs %Xr
+
+# limit-time anonymous 30
+# limit anonusers 10 Wk0730-1800 /etc/ftpd/toomany.msg
+# limit anonusers 50 SaSu|Any1800-0730 /etc/ftpd/toomany.msg
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpaddhost.sh b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpaddhost.sh
new file mode 100644
index 0000000000..1fa4778114
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpaddhost.sh
@@ -0,0 +1,319 @@
+#!/usr/bin/ksh
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 1997-2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+#
+# This script sets up a virtual FTP host.
+#
+# Usage:
+# ftpaddhost -c|-l [-b] [ -x xferlog ] hostname root_dir
+#
+# ftpaddhost configures virtual host hostname under directory root_dir.
+# An IP address can be used for hostname.
+#
+# The -c (complete) option configures complete virtual hosting, which allows
+# each virtual host to have its own version of the ftpaccess, ftpconversions,
+# ftpgroups, ftphosts and ftpusers files. The master version of each of these
+# configuration files is copied from the /etc/ftpd directory and placed in
+# the /etc/ftpd/virtual-ftpd/hostname directory. If the /etc/ftpusers file
+# exists it is appended to the virtual ftpusers file. If a virtual host lacks
+# its own version of a configuration file, the master version is used.
+#
+# The -l (limited) option configures limited virtual hosting, which only
+# allows a small number of parameters to be configured differently for a
+# virtual host (see the virtual keyword on the ftpaccess(4) manual page).
+#
+# When the -b (banner) option is supplied, ftpaddhost creates a banner for
+# the virtual host, useful to see that the virtual host is working.
+#
+# When the -x xferlog option is supplied, ftpaddhost creates a logfile entry
+# which causes the transfer logs for the virtual host to be written to the
+# specified file.
+#
+# Exit codes: 0 - success
+# 1 - usage
+# 2 - command failure
+#
+
+usage()
+{
+ fmt=`gettext "Usage: %s -c|-l [-b] [ -x xferlog ] hostname root_dir"`
+ printf "$fmt\n" "$cmd" >&2
+ exit 1
+}
+
+verify_root()
+{
+ # Verify caller has a real user ID of 0.
+ set `id`
+ if [ "$1" != "uid=0(root)" ]
+ then
+ fmt=`gettext "%s: Error: Only root can run %s"`
+ printf "$fmt\n" "$cmd" "$cmd" >&2
+ exit 1
+ fi
+}
+
+# Make directory $1 with mode $2 and ownership $3.
+make_dir()
+{
+ if [ ! -d "$1" ]
+ then
+ mkdir "$1" || exit 2
+ fi
+ chmod "$2" "$1"
+ chown "$3" "$1"
+}
+
+setup_complete_vhost()
+{
+ fmt=`gettext "Setting up complete virtual host %s"`
+ printf "$fmt\n" "$hostname"
+ make_dir /etc/ftpd/virtual-ftpd 755 root:sys
+ make_dir "/etc/ftpd/virtual-ftpd/$hostname" 755 root:sys
+
+ fmt=`gettext "Configuration directory is %s"`
+ printf "$fmt\n" "/etc/ftpd/virtual-ftpd/$hostname"
+
+ # Update the virtual host configuration file.
+ vhconfig=/etc/ftpd/ftpservers
+
+ fmt=`gettext "Updating virtual hosting configuration file %s"`
+ printf "$fmt\n" $vhconfig
+ if [ -f $vhconfig ]
+ then
+ # Remove any existing entries for the virtual host.
+ sed "/^[ ]*$hostname[ ]/d" $vhconfig >$vhconfig.tmp.$$
+ mv -f $vhconfig.tmp.$$ $vhconfig || exit 2
+ fi
+
+ echo "$hostname /etc/ftpd/virtual-ftpd/$hostname" >>$vhconfig
+ chmod 644 $vhconfig
+ chown root:sys $vhconfig
+
+ # Make copies of the master configuration files.
+ for file in ftpconversions ftpgroups ftphosts ftpusers
+ do
+ target="/etc/ftpd/virtual-ftpd/$hostname/$file"
+ rm -f "$target"
+ if [ -f /etc/ftpd/$file ]
+ then
+ cp /etc/ftpd/$file "$target" || exit 2
+ chmod 644 "$target"
+ chown root:sys "$target"
+ fi
+ done
+
+ # Append /etc/ftpusers to the virtual hosts ftpusers.
+ if [ -f /etc/ftpusers ]
+ then
+ target="/etc/ftpd/virtual-ftpd/$hostname/ftpusers"
+ cat /etc/ftpusers >>"$target"
+ chmod 644 "$target"
+ chown root:sys "$target"
+ fi
+
+ vhftpaccess="/etc/ftpd/virtual-ftpd/$hostname/ftpaccess"
+ rm -f "$vhftpaccess"
+
+ # Remove any existing root or logfile entries.
+ sed "/^[ ]*root[ ]/d
+ /^[ ]*logfile[ ]/d" $ftpaccess >"$vhftpaccess"
+
+ # Add the virtual host root.
+ echo "root $vhroot" >>"$vhftpaccess"
+
+ # Add a banner to show the virtual host configuration worked.
+ if [ -n "$banner" ]
+ then
+ # Add a banner entry if there isn't already one.
+ grep "^[ ]*banner[ ]" "$vhftpaccess" >/dev/null 2>&1
+ if [ $? -eq 0 ]
+ then
+ fmt=`gettext "Existing banner entry not changed in %s"`
+ printf "$fmt\n" "$vhftpaccess"
+ else
+ bannerf="/etc/ftpd/virtual-ftpd/$hostname/cbanner.msg"
+ if [ -f "$bannerf" ]
+ then
+ fmt=`gettext "Using existing banner file %s"`
+ printf "$fmt\n" "$bannerf"
+ else
+ fmt=`gettext "Creating banner file %s"`
+ printf "$fmt\n" "$bannerf"
+ fmt=`gettext "Complete virtual host %%L test banner"`
+ printf "$fmt\n" >"$bannerf"
+ chmod 644 "$bannerf"
+ chown root:sys "$bannerf"
+ fi
+ echo "banner $bannerf" >>"$vhftpaccess"
+ fi
+ fi
+
+ # Add the transfer logfile.
+ if [ -n "$logfile" ]
+ then
+ echo "logfile $logfile" >>"$vhftpaccess"
+ fi
+
+ chmod 644 "$vhftpaccess"
+ chown root:sys "$vhftpaccess"
+}
+
+setup_limited_vhost()
+{
+ # Check complete virtual hosting is not configured for the host.
+ grep "^[ ]*$hostname[ ]" /etc/ftpd/ftpservers >/dev/null 2>&1
+ if [ $? -eq 0 ]
+ then
+ fmt=`gettext "%s: Error: Complete virtual hosting already configured for %s"`
+ printf "$fmt\n" "$cmd" "$hostname" >&2
+ exit 1
+ fi
+
+ fmt=`gettext "Setting up limited virtual host %s"`
+ printf "$fmt\n" "$hostname"
+
+ # Update the ftpaccess file.
+ fmt=`gettext "Updating FTP server configuration file %s"`
+ printf "$fmt\n" $ftpaccess
+
+ # Remove any existing entries for the virtual host.
+ sed "/^[ ]*virtual[ ][ ]*$hostname[ ]/d" $ftpaccess >$ftpaccess.tmp.$$
+ mv -f $ftpaccess.tmp.$$ $ftpaccess || exit 2
+
+ # Add a limited virtual hosting entry for the virtual host.
+ echo "virtual $hostname root $vhroot" >>$ftpaccess
+
+ # Add a banner to show the virtual host configuration worked.
+ if [ -n "$banner" ]
+ then
+ bannerf="/etc/ftpd/virtual-ftpd/$hostname/lbanner.msg"
+ if [ -f "$bannerf" ]
+ then
+ fmt=`gettext "Using existing banner file %s"`
+ printf "$fmt\n" "$bannerf"
+ else
+ fmt=`gettext "Creating banner file %s"`
+ printf "$fmt\n" "$bannerf"
+ make_dir /etc/ftpd/virtual-ftpd 755 root:sys
+ make_dir "/etc/ftpd/virtual-ftpd/$hostname" 755 root:sys
+ fmt=`gettext "Limited virtual host %%L test banner"`
+ printf "$fmt\n" >"$bannerf"
+ chmod 644 "$bannerf"
+ chown root:sys "$bannerf"
+ fi
+ echo "virtual $hostname banner $bannerf" >>$ftpaccess
+ fi
+
+ # Add the transfer logfile.
+ if [ -n "$logfile" ]
+ then
+ echo "virtual $hostname logfile $logfile" >>$ftpaccess
+ fi
+
+ chmod 644 $ftpaccess
+ chown root:sys $ftpaccess
+}
+
+# Execution starts here.
+
+IFS="
+"
+SHELL=/usr/bin/ksh
+PATH=/usr/bin
+TEXTDOMAIN=SUNW_OST_OSCMD
+export SHELL PATH IFS TEXTDOMAIN
+
+cmd=`basename "$0"`
+
+verify_root
+
+while getopts bclx: arg
+do
+ case $arg in
+ b) banner=1;;
+ c) complete=1;;
+ l) limited=1;;
+ x) logfile="$OPTARG";;
+ \?) usage;;
+ esac
+done
+shift `expr $OPTIND - 1`
+
+# Check arguments.
+[ -z "$complete" -a -z "$limited" ] && usage
+[ -n "$complete" -a -n "$limited" ] && usage
+
+[ $# -ne 2 ] && usage
+hostname="$1"
+vhroot="$2"
+
+[ -z "$hostname" -o -z "$vhroot" ] && usage
+
+echo "$hostname" | grep / >/dev/null 2>&1
+if [ $? -eq 0 ]
+then
+ fmt=`gettext "%s: Error: hostname must not contain a /"`
+ printf "$fmt\n" "$cmd" >&2
+ usage
+fi
+
+echo "$vhroot" | grep "^/" >/dev/null 2>&1
+if [ $? -ne 0 ]
+then
+ fmt=`gettext "%s: Error: root_dir must be an absolute pathname"`
+ printf "$fmt\n" "$cmd" >&2
+ usage
+fi
+
+if [ -n "$logfile" ]
+then
+ echo "$logfile" | grep "^/" >/dev/null 2>&1
+ if [ $? -ne 0 ]
+ then
+ fmt=`gettext "%s: Error: xferlog must be an absolute pathname"`
+ printf "$fmt\n" "$cmd" >&2
+ usage
+ fi
+fi
+
+ftpaccess=/etc/ftpd/ftpaccess
+if [ ! -f $ftpaccess ]
+then
+ fmt=`gettext "%s: Error: FTP server configuration file %s missing"`
+ printf "$fmt\n" "$cmd" $ftpaccess >&2
+ exit 2
+fi
+
+grep "^ftp:" /etc/passwd >/dev/null 2>&1
+if [ $? -ne 0 ]
+then
+ fmt=`gettext "Warning: Must create ftp user account before virtual hosts will work"`
+ printf "$fmt\n"
+fi
+
+# Ignore certain signals.
+trap '' 1 2 3 15
+
+umask 022
+
+if [ -n "$complete" ]
+then
+ setup_complete_vhost
+else
+ setup_limited_vhost
+fi
+
+/usr/sbin/ftpconfig -d "$vhroot" >/dev/null
+if [ $? -ne 0 ]
+then
+ fmt=`gettext "%s: Error: ftpconfig -d %s failed"`
+ printf "$fmt\n" "$cmd" "$vhroot" >&2
+ exit 2
+fi
+
+exit 0
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpcmd.y b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpcmd.y
new file mode 100644
index 0000000000..5d6462c3cb
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpcmd.y
@@ -0,0 +1,2505 @@
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/****************************************************************************
+ Copyright (c) 1999,2000,2001 WU-FTPD Development Group.
+ All rights reserved.
+
+ Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994
+ The Regents of the University of California.
+ Portions Copyright (c) 1993, 1994 Washington University in Saint Louis.
+ Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc.
+ Portions Copyright (c) 1989 Massachusetts Institute of Technology.
+ Portions Copyright (c) 1998 Sendmail, Inc.
+ Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P. Allman.
+ Portions Copyright (c) 1997 by Stan Barber.
+ Portions Copyright (c) 1997 by Kent Landfield.
+ Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ Free Software Foundation, Inc.
+
+ Use and distribution of this software and its source code are governed
+ by the terms and conditions of the WU-FTPD Software License ("LICENSE").
+
+ If you did not receive a copy of the license, it may be obtained online
+ at http://www.wu-ftpd.org/license.html.
+
+ $Id: ftpcmd.y,v 1.27.2.2 2001/11/29 17:01:38 wuftpd Exp $
+
+****************************************************************************/
+/*
+ * Grammar for FTP commands.
+ * See RFC 959.
+ */
+
+%{
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "config.h"
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/ftp.h>
+#include <stdio.h>
+#include <signal.h>
+#include <errno.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <setjmp.h>
+#ifdef HAVE_SYS_SYSLOG_H
+#include <sys/syslog.h>
+#endif
+#if defined(HAVE_SYSLOG_H) || (!defined(AUTOCONF) && !defined(HAVE_SYS_SYSLOG_H))
+#include <syslog.h>
+#endif
+#include <time.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include "extensions.h"
+#include "pathnames.h"
+#include "proto.h"
+
+#if defined(USE_TLS) || defined(USE_GSS)
+static int pbsz_command_issued = 0;
+char *cur_auth_type = NULL;
+extern char *protnames[];
+#endif /* defined(USE_TLS) || defined(USE_GSS) */
+
+#if defined(USE_GSS)
+#include "gssutil.h"
+
+extern gss_info_t gss_info;
+#endif /* defined(USE_GSS) */
+
+extern int dolreplies;
+#ifndef INTERNAL_LS
+extern char ls_long[];
+extern char ls_short[];
+#endif
+extern struct SOCKSTORAGE data_dest;
+extern struct SOCKSTORAGE his_addr;
+extern int logged_in;
+extern struct passwd *pw;
+extern int anonymous;
+extern int logging;
+extern int log_commands;
+extern int log_security;
+extern int type;
+extern int form;
+extern int debug;
+extern unsigned int timeout_idle;
+extern unsigned int timeout_maxidle;
+extern int pdata;
+extern char hostname[], remotehost[], *remoteident;
+extern char remoteaddr[];
+extern char chroot_path[];
+extern char guestpw[], authuser[]; /* added. _H */
+extern char proctitle[];
+extern char *globerr;
+extern int usedefault;
+extern int transflag;
+extern char tmpline[];
+extern int data;
+extern int errno;
+extern char *home;
+
+off_t restart_point;
+int yyerrorcalled;
+
+extern char *strunames[];
+extern char *typenames[];
+extern char *modenames[];
+extern char *formnames[];
+extern int restricted_user; /* global flag indicating if user is restricted to home directory */
+
+#ifdef TRANSFER_COUNT
+extern off_t data_count_total;
+extern off_t byte_count_total;
+extern off_t byte_count_in;
+extern int file_count_total;
+extern int xfer_count_total;
+#endif
+
+extern int retrieve_is_data;
+
+#ifdef VIRTUAL
+extern int virtual_mode;
+extern int virtual_ftpaccess;
+extern char virtual_email[];
+#endif
+
+#ifdef IGNORE_NOOP
+static int alarm_running = 0;
+#endif
+
+static unsigned short cliport = 0;
+static struct in_addr cliaddr;
+static int cmd_type;
+static int cmd_form;
+static int cmd_bytesz;
+char cbuf[16 * BUFSIZ];
+char *fromname;
+
+#ifndef L_FORMAT /* Autoconf detects this... */
+#if (defined(BSD) && (BSD >= 199103)) && !defined(LONGOFF_T)
+#define L_FORMAT "qd"
+#else
+#ifdef _AIX42
+#define L_FORMAT "lld"
+#else
+#ifdef SOLARIS_2
+#define L_FORMAT "ld"
+#else
+#define L_FORMAT "d"
+#endif
+#endif
+#endif
+#endif
+
+#ifdef INET6
+extern int epsv_all;
+int lport_error;
+#endif
+
+/* Debian linux bison fix: moved this up, added forward decls */
+
+struct tab {
+ char *name;
+ short token;
+ short state;
+ short implemented; /* 1 if command is implemented */
+ char *help;
+};
+
+extern struct tab cmdtab[];
+extern struct tab sitetab[];
+
+static void toolong(int);
+void help(struct tab *ctab, char *s);
+struct tab *lookup(register struct tab *p, char *cmd);
+int yylex(void);
+
+static char *nullstr = "(null)";
+#define CHECKNULL(p) ((p) ? (p) : nullstr)
+
+extern int pasv_allowed(const char *remoteaddr);
+extern int port_allowed(const char *remoteaddr);
+%}
+
+%token
+ A B C E F I
+ L N P R S T
+
+ SP CRLF COMMA STRING NUMBER
+
+ USER PASS ACCT REIN QUIT PORT
+ PASV TYPE STRU MODE RETR STOR
+ APPE MLFL MAIL MSND MSOM MSAM
+ MRSQ MRCP ALLO REST RNFR RNTO
+ ABOR DELE CWD LIST NLST SITE
+ STAT HELP NOOP MKD RMD PWD
+ CDUP STOU SMNT SYST SIZE MDTM
+ EPRT EPSV LPRT LPSV
+ PROT PBSZ AUTH ADAT CCC
+
+ UMASK IDLE CHMOD GROUP GPASS NEWER
+ MINFO INDEX EXEC ALIAS CDPATH GROUPS
+ CHECKMETHOD CHECKSUM
+
+ LEXERR
+
+%union {
+ char *String;
+ int Number;
+}
+
+%type <String> STRING password pathname pathstring username method
+%type <Number> NUMBER byte_size check_login form_code
+%type <Number> struct_code mode_code octal_number
+%type <Number> prot_code
+
+%start cmd_list
+
+%%
+
+cmd_list: /* empty */
+ | cmd_list cmd
+ = {
+ if (fromname) {
+ free(fromname);
+ fromname = NULL;
+ }
+ restart_point = 0;
+ }
+ | cmd_list rcmd
+ ;
+
+cmd: USER SP username CRLF
+ = {
+ user($3);
+ if (log_commands)
+ syslog(LOG_INFO, "USER %s", $3);
+ free($3);
+ }
+ | PASS SP password CRLF
+ = {
+ if (log_commands)
+ if (anonymous)
+ syslog(LOG_INFO, "PASS %s", $3);
+ else
+ syslog(LOG_INFO, "PASS password");
+
+ pass($3);
+ free($3);
+ }
+ | PORT check_login SP host_port CRLF
+ = {
+ if (log_commands)
+ syslog(LOG_INFO, "PORT");
+/* H* port fix, part B: admonish the twit.
+ Also require login before PORT works */
+ if ($2) {
+#ifndef DISABLE_PORT
+#ifdef INET6
+ if (epsv_all) {
+ reply(501, "PORT not allowed after EPSV ALL");
+ goto prt_done;
+ }
+#endif
+ if (((sock_cmp_inaddr(&his_addr, cliaddr) == 0)
+ || port_allowed(inet_ntoa(cliaddr)))
+ && (ntohs(cliport) >= IPPORT_RESERVED)) {
+ usedefault = 0;
+ if (pdata >= 0) {
+ (void) close(pdata);
+ pdata = -1;
+ }
+ SET_SOCK_FAMILY(data_dest, SOCK_FAMILY(his_addr));
+ SET_SOCK_PORT(data_dest, cliport);
+ SET_SOCK_ADDR4(data_dest, cliaddr);
+ reply(200, "PORT command successful.");
+ }
+ else {
+#endif /* DISABLE_PORT */
+ reply(502, "Illegal PORT Command");
+prt_done:
+ usedefault = 1;
+ syslog(LOG_WARNING, "refused PORT %s,%d from %s",
+ inet_ntoa(cliaddr), ntohs(cliport), remoteident);
+#ifndef DISABLE_PORT
+ }
+#endif
+ }
+ }
+ | EPRT check_login SP STRING CRLF
+ = {
+#ifdef INET6
+ if (log_commands)
+ syslog(LOG_INFO, "EPRT");
+ if ($2 && $4 != NULL) {
+#ifndef DISABLE_PORT
+ char d, fmt[32], addr[INET6_ADDRSTRLEN + 1];
+ int proto;
+ unsigned short port;
+
+ if (epsv_all) {
+ reply(501, "EPRT not allowed after EPSV ALL");
+ goto eprt_done;
+ }
+ d = *((char *)$4);
+ if ((d < 33) || (d > 126)) {
+ reply(501, "Bad delimiter '%c' (%d).", d, d);
+ goto eprt_done;
+ }
+ if (d == '%')
+ (void) snprintf(fmt, sizeof(fmt),
+ "%%%1$c%%d%%%1$c%%%2$d[^%%%1$c]%%%1$c%%hu%%%1$c",
+ d, INET6_ADDRSTRLEN);
+ else
+ (void) snprintf(fmt, sizeof(fmt),
+ "%1$c%%d%1$c%%%2$d[^%1$c]%1$c%%hu%1$c",
+ d, INET6_ADDRSTRLEN);
+
+ if (sscanf((const char *)$4, fmt, &proto, addr, &port) != 3) {
+ reply(501, "EPRT bad format.");
+ goto eprt_done;
+ }
+ port = htons(port);
+
+ switch (proto) {
+ case 1:
+ SET_SOCK_FAMILY(data_dest, AF_INET);
+ break;
+ case 2:
+ memset(&data_dest, 0, sizeof(struct sockaddr_in6));
+ SET_SOCK_FAMILY(data_dest, AF_INET6);
+ break;
+ default:
+ reply(522, "Network protocol not supported, use (1,2)");
+ goto eprt_done;
+ }
+ if (inet_pton(SOCK_FAMILY(data_dest), addr, SOCK_ADDR(data_dest))
+ != 1) {
+ reply(501, "Bad address %s.", addr);
+ goto eprt_done;
+ }
+
+ if (((sock_cmp_addr(&his_addr, &data_dest) == 0)
+ || port_allowed(inet_stop(&data_dest)))
+ && (ntohs(port) >= IPPORT_RESERVED)) {
+ usedefault = 0;
+ if (pdata >= 0) {
+ (void) close(pdata);
+ pdata = -1;
+ }
+ SET_SOCK_PORT(data_dest, port);
+ SET_SOCK_SCOPE(data_dest, his_addr);
+ reply(200, "EPRT command successful.");
+ }
+ else {
+#endif /* DISABLE_PORT */
+ reply(502, "Illegal EPRT Command");
+eprt_done:
+ usedefault = 1;
+ syslog(LOG_WARNING, "refused EPRT %s from %s",
+ $4, remoteident);
+#ifndef DISABLE_PORT
+ }
+#endif
+ }
+ if ($4 != NULL)
+ free($4);
+#endif /* INET6 */
+ }
+ | LPRT check_login SP host_lport CRLF
+ = {
+#ifdef INET6
+ if (log_commands)
+ syslog(LOG_INFO, "LPRT");
+ if ($2) {
+#ifndef DISABLE_PORT
+ if (lport_error)
+ goto lprt_done;
+ if (((sock_cmp_addr(&his_addr, &data_dest) == 0)
+ || port_allowed(inet_stop(&data_dest)))
+ && (SOCK_PORT(data_dest) >= IPPORT_RESERVED)) {
+ usedefault = 0;
+ if (pdata >= 0) {
+ (void) close(pdata);
+ pdata = -1;
+ }
+ SET_SOCK_SCOPE(data_dest, his_addr);
+ reply(200, "LPRT command successful.");
+ }
+ else {
+#endif /* DISABLE_PORT */
+ reply(502, "Illegal LPRT Command");
+lprt_done:
+ usedefault = 1;
+ syslog(LOG_WARNING, "refused LPRT from %s", remoteident);
+#ifndef DISABLE_PORT
+ }
+#endif
+ }
+#endif /* INET6 */
+ }
+ | PASV check_login CRLF
+ = {
+/* Require login for PASV, too. This actually fixes a bug -- telnet to an
+ unfixed wu-ftpd and type PASV first off, and it crashes! */
+ if (log_commands)
+ syslog(LOG_INFO, "PASV");
+ if ($2)
+#if (defined (DISABLE_PORT) || !defined (DISABLE_PASV))
+#ifdef INET6
+ if (epsv_all)
+ reply(501, "PASV not allowed after EPSV ALL");
+ else
+#endif
+ passive(TYPE_PASV, 0);
+#else
+ reply(502, "Illegal PASV Command");
+#endif
+ }
+ | EPSV check_login CRLF
+ = {
+#ifdef INET6
+ if (log_commands)
+ syslog(LOG_INFO, "EPSV");
+ if ($2)
+#if (defined (DISABLE_PORT) || !defined (DISABLE_PASV))
+ passive(TYPE_EPSV, 0);
+#else
+ reply(502, "Illegal EPSV Command");
+#endif
+#endif /* INET6 */
+ }
+ | EPSV check_login SP STRING CRLF
+ = {
+#ifdef INET6
+ if (log_commands)
+ syslog(LOG_INFO, "EPSV");
+ if ($2 && $4 != NULL)
+#if (defined (DISABLE_PORT) || !defined (DISABLE_PASV))
+ if (strcasecmp((const char *)$4, "ALL") == 0) {
+ epsv_all = 1;
+ reply(200, "EPSV ALL command successful.");
+ }
+ else {
+ int af;
+ char *endp;
+
+ af = strtoul((char *)$4, &endp, 0);
+ if (*endp)
+ reply(501, "'EPSV %s':" "command not understood.", $4);
+ else {
+ /* Not allowed to specify address family 0 */
+ if (af == 0)
+ af = -1;
+ passive(TYPE_EPSV, af);
+ }
+ }
+#else
+ reply(502, "Illegal EPSV Command");
+#endif
+ if ($4 != NULL)
+ free($4);
+#endif /* INET6 */
+ }
+ | LPSV check_login CRLF
+ = {
+#ifdef INET6
+ if (log_commands)
+ syslog(LOG_INFO, "LPSV");
+ if ($2)
+#if (defined (DISABLE_PORT) || !defined (DISABLE_PASV))
+ if (epsv_all)
+ reply(501, "LPSV not allowed after EPSV ALL");
+ else
+ passive(TYPE_LPSV, 0);
+#else
+ reply(502, "Illegal LPSV Command");
+#endif
+#endif /* INET6 */
+ }
+ | TYPE check_login SP type_code CRLF
+ = {
+ if (log_commands)
+ syslog(LOG_INFO, "TYPE %s", typenames[cmd_type]);
+ if ($2)
+ switch (cmd_type) {
+
+ case TYPE_A:
+ if (cmd_form == FORM_N) {
+ reply(200, "Type set to A.");
+ type = cmd_type;
+ form = cmd_form;
+ }
+ else
+ reply(504, "Form must be N.");
+ break;
+
+ case TYPE_E:
+ reply(504, "Type E not implemented.");
+ break;
+
+ case TYPE_I:
+ reply(200, "Type set to I.");
+ type = cmd_type;
+ break;
+
+ case TYPE_L:
+#if NBBY == 8
+ if (cmd_bytesz == 8) {
+ reply(200,
+ "Type set to L (byte size 8).");
+ type = cmd_type;
+ }
+ else
+ reply(504, "Byte size must be 8.");
+#else /* NBBY == 8 */
+#error UNIMPLEMENTED for NBBY != 8
+#endif /* NBBY == 8 */
+ }
+ }
+ | STRU check_login SP struct_code CRLF
+ = {
+ if (log_commands)
+ syslog(LOG_INFO, "STRU %s", strunames[$4]);
+ if ($2)
+ switch ($4) {
+
+ case STRU_F:
+ reply(200, "STRU F ok.");
+ break;
+
+ default:
+ reply(504, "Unimplemented STRU type.");
+ }
+ }
+ | MODE check_login SP mode_code CRLF
+ = {
+ if (log_commands)
+ syslog(LOG_INFO, "MODE %s", modenames[$4]);
+ if ($2)
+ switch ($4) {
+
+ case MODE_S:
+ reply(200, "MODE S ok.");
+ break;
+
+ default:
+ reply(502, "Unimplemented MODE type.");
+ }
+ }
+ | ALLO check_login SP NUMBER CRLF
+ = {
+ if (log_commands)
+ syslog(LOG_INFO, "ALLO %d", $4);
+ if ($2)
+ reply(202, "ALLO command ignored.");
+ }
+ | ALLO check_login SP NUMBER SP R SP NUMBER CRLF
+ = {
+ if (log_commands)
+ syslog(LOG_INFO, "ALLO %d R %d", $4, $8);
+ if ($2)
+ reply(202, "ALLO command ignored.");
+ }
+ | RETR check_login SP pathname CRLF
+ = {
+ if (log_commands)
+ syslog(LOG_INFO, "RETR %s", CHECKNULL($4));
+ if ($2 && $4 != NULL && !restrict_check($4)) {
+ retrieve_is_data = 1;
+ retrieve((char *) NULL, $4);
+ }
+ if ($4 != NULL)
+ free($4);
+ }
+ | STOR check_login SP pathname CRLF
+ = {
+ if (log_commands)
+ syslog(LOG_INFO, "STOR %s", CHECKNULL($4));
+ if ($2 && $4 != NULL && !restrict_check($4))
+ store($4, "w", 0);
+ if ($4 != NULL)
+ free($4);
+ }
+ | APPE check_login SP pathname CRLF
+ = {
+ if (log_commands)
+ syslog(LOG_INFO, "APPE %s", CHECKNULL($4));
+ if ($2 && $4 != NULL && !restrict_check($4))
+ store($4, "a", 0);
+ if ($4 != NULL)
+ free($4);
+ }
+ | NLST check_login CRLF
+ = {
+ if (log_commands)
+ syslog(LOG_INFO, "NLST");
+ if ($2 && !restrict_check("."))
+ send_file_list("");
+ }
+ | NLST check_login SP STRING CRLF
+ = {
+ if (log_commands)
+ syslog(LOG_INFO, "NLST %s", $4);
+ if ($2 && $4 && !restrict_check($4))
+ send_file_list($4);
+ if ($4 != NULL)
+ free($4);
+ }
+ | LIST check_login CRLF
+ = {
+ if (log_commands)
+ syslog(LOG_INFO, "LIST");
+ if ($2 && !restrict_check(".")) {
+ retrieve_is_data = 0;
+#ifndef INTERNAL_LS
+ if (anonymous && dolreplies)
+ retrieve(ls_long, "");
+ else
+ retrieve(ls_short, "");
+#else
+ ls(NULL, 0);
+#endif
+ }
+ }
+ | LIST check_login SP pathname CRLF
+ = {
+ if (log_commands)
+ syslog(LOG_INFO, "LIST %s", CHECKNULL($4));
+ if ($2 && $4 != NULL && !restrict_list_check($4)) {
+ retrieve_is_data = 0;
+#ifndef INTERNAL_LS
+ if (anonymous && dolreplies)
+ retrieve(ls_long, $4);
+ else
+ retrieve(ls_short, $4);
+#else
+ ls($4, 0);
+#endif
+ }
+ if ($4 != NULL)
+ free($4);
+ }
+ | STAT check_login SP pathname CRLF
+ = {
+ if (log_commands)
+ syslog(LOG_INFO, "STAT %s", CHECKNULL($4));
+ if ($2 && $4 != NULL && !restrict_check($4))
+ statfilecmd($4);
+ if ($4 != NULL)
+ free($4);
+ }
+ | STAT check_login CRLF
+ = {
+ if (log_commands)
+ syslog(LOG_INFO, "STAT");
+ if ($2)
+ statcmd();
+ }
+ | DELE check_login SP pathname CRLF
+ = {
+ if (log_commands)
+ syslog(LOG_INFO, "DELE %s", CHECKNULL($4));
+ if ($2 && $4 != NULL && !restrict_check($4))
+ delete($4);
+ if ($4 != NULL)
+ free($4);
+ }
+ | RNTO check_login SP pathname CRLF
+ = {
+ if (log_commands)
+ syslog(LOG_INFO, "RNTO %s", CHECKNULL($4));
+ if ($2 && $4 && !restrict_check($4)) {
+ if (fromname) {
+ renamecmd(fromname, $4);
+ free(fromname);
+ fromname = NULL;
+ }
+ else {
+ reply(503, "Bad sequence of commands.");
+ }
+ }
+ if ($4)
+ free($4);
+ }
+ | ABOR check_login CRLF
+ = {
+ if (log_commands)
+ syslog(LOG_INFO, "ABOR");
+ if ($2)
+ reply(225, "ABOR command successful.");
+ }
+ | CWD check_login CRLF
+ = {
+ if (log_commands)
+ syslog(LOG_INFO, "CWD");
+ if ($2 && !restrict_check(home))
+ cwd(home);
+ }
+ | CWD check_login SP pathname CRLF
+ = {
+ if (log_commands)
+ syslog(LOG_INFO, "CWD %s", CHECKNULL($4));
+ if ($2 && $4 != NULL && !restrict_check($4))
+ cwd($4);
+ if ($4 != NULL)
+ free($4);
+ }
+ | HELP check_login CRLF
+ = {
+ if (log_commands)
+ syslog(LOG_INFO, "HELP");
+ if ($2)
+ help(cmdtab, (char *) NULL);
+ }
+ | HELP check_login SP STRING CRLF
+ = {
+ register char *cp = (char *) $4;
+
+ if (log_commands)
+ syslog(LOG_INFO, "HELP %s", $4);
+ if ($2)
+ if (strncasecmp(cp, "SITE", 4) == 0) {
+ cp = (char *) $4 + 4;
+ if (*cp == ' ')
+ cp++;
+ if (*cp)
+ help(sitetab, cp);
+ else
+ help(sitetab, (char *) NULL);
+ }
+ else
+ help(cmdtab, $4);
+ if ($4 != NULL)
+ free($4);
+ }
+ | NOOP check_login CRLF
+ = {
+ if (log_commands)
+ syslog(LOG_INFO, "NOOP");
+ if ($2)
+ reply(200, "NOOP command successful.");
+ }
+ | MKD check_login SP pathname CRLF
+ = {
+ if (log_commands)
+ syslog(LOG_INFO, "MKD %s", CHECKNULL($4));
+ if ($2 && $4 != NULL && !restrict_check($4))
+ makedir($4);
+ if ($4 != NULL)
+ free($4);
+ }
+ | RMD check_login SP pathname CRLF
+ = {
+ if (log_commands)
+ syslog(LOG_INFO, "RMD %s", CHECKNULL($4));
+ if ($2 && $4 != NULL && !restrict_check($4))
+ removedir($4);
+ if ($4 != NULL)
+ free($4);
+ }
+ | PWD check_login CRLF
+ = {
+ if (log_commands)
+ syslog(LOG_INFO, "PWD");
+ if ($2)
+ pwd();
+ }
+ | CDUP check_login CRLF
+ = {
+ if (log_commands)
+ syslog(LOG_INFO, "CDUP");
+ if ($2)
+ if (!test_restriction(".."))
+ cwd("..");
+ else
+ ack("CWD");
+ }
+
+ | SITE check_login SP HELP CRLF
+ = {
+ if (log_commands)
+ syslog(LOG_INFO, "SITE HELP");
+ if ($2)
+ help(sitetab, (char *) NULL);
+ }
+ | SITE check_login SP HELP SP STRING CRLF
+ = {
+ if (log_commands)
+ syslog(LOG_INFO, "SITE HELP %s", $6);
+ if ($2)
+ help(sitetab, $6);
+ if ($6 != NULL)
+ free($6);
+ }
+ | SITE check_login SP UMASK CRLF
+ = {
+ mode_t oldmask;
+
+ if (log_commands)
+ syslog(LOG_INFO, "SITE UMASK");
+ if ($2) {
+ oldmask = umask(0);
+ (void) umask(oldmask);
+ reply(200, "Current UMASK is %03o", oldmask);
+ }
+ }
+ | SITE check_login SP UMASK SP octal_number CRLF
+ = {
+ mode_t oldmask;
+ struct aclmember *entry = NULL;
+ int ok = 1;
+
+ if (log_commands)
+ syslog(LOG_INFO, "SITE UMASK %03o", $6);
+ if ($2) {
+ /* check for umask permission */
+ while (getaclentry("umask", &entry) && ARG0 && ARG1 != NULL) {
+ if (type_match(ARG1))
+ if (*ARG0 == 'n')
+ ok = 0;
+ }
+ if (ok && !restricted_user) {
+ if (($6 < 0) || ($6 > 0777)) {
+ reply(501, "Bad UMASK value");
+ }
+ else {
+ oldmask = umask((mode_t) $6);
+ reply(200, "UMASK set to %03o (was %03o)", $6, oldmask);
+ }
+ }
+ else
+ reply(553, "Permission denied on server. (umask)");
+ }
+ }
+ | SITE check_login SP CHMOD SP octal_number SP pathname CRLF
+ = {
+ struct aclmember *entry = NULL;
+ int ok = (anonymous ? 0 : 1);
+
+ if (log_commands)
+ syslog(LOG_INFO, "SITE CHMOD %03o %s", $6, CHECKNULL($8));
+ if ($2 && $8) {
+ /* check for chmod permission */
+ while (getaclentry("chmod", &entry) && ARG0 && ARG1 != NULL) {
+ if (type_match(ARG1))
+ if (anonymous) {
+ if (*ARG0 == 'y')
+ ok = 1;
+ }
+ else if (*ARG0 == 'n')
+ ok = 0;
+ }
+ if (ok) {
+#ifdef UNRESTRICTED_CHMOD
+ if (chmod($8, (mode_t) $6) < 0)
+#else
+ if (($6 < 0) || ($6 > 0777))
+ reply(501,
+ "CHMOD: Mode value must be between 0 and 0777");
+ else if (chmod($8, (mode_t) $6) < 0)
+#endif
+ perror_reply(550, $8);
+ else {
+ char path[MAXPATHLEN];
+
+ wu_realpath($8, path, chroot_path);
+
+ if (log_security)
+ if (anonymous) {
+ syslog(LOG_NOTICE, "%s of %s changed permissions for %s", guestpw, remoteident, path);
+ }
+ else {
+ syslog(LOG_NOTICE, "%s of %s changed permissions for %s", pw->pw_name,
+ remoteident, path);
+ }
+ reply(200, "CHMOD command successful.");
+ }
+ }
+ else
+ reply(553, "Permission denied on server. (chmod)");
+ }
+ if ($8 != NULL)
+ free($8);
+ }
+ | SITE check_login SP IDLE CRLF
+ = {
+ if (log_commands)
+ syslog(LOG_INFO, "SITE IDLE");
+ if ($2)
+ reply(200,
+ "Current IDLE time limit is %d seconds; max %d",
+ timeout_idle, timeout_maxidle);
+ }
+ | SITE check_login SP IDLE SP NUMBER CRLF
+ = {
+ if (log_commands)
+ syslog(LOG_INFO, "SITE IDLE %d", $6);
+ if ($2)
+ if ($6 < 30 || $6 > timeout_maxidle) {
+ reply(501,
+ "Maximum IDLE time must be between 30 and %d seconds",
+ timeout_maxidle);
+ }
+ else {
+ timeout_idle = $6;
+ reply(200, "Maximum IDLE time set to %d seconds", timeout_idle);
+ }
+ }
+ | SITE check_login SP GROUP SP username CRLF
+ = {
+#ifndef NO_PRIVATE
+ if (log_commands)
+ syslog(LOG_INFO, "SITE GROUP %s", $6);
+ if (!restricted_user && $2 && $6)
+ priv_group($6);
+ free($6);
+#endif /* !NO_PRIVATE */
+ }
+ | SITE check_login SP GPASS SP password CRLF
+ = {
+#ifndef NO_PRIVATE
+ if (log_commands)
+ syslog(LOG_INFO, "SITE GPASS password");
+ if (!restricted_user && $2 && $6)
+ priv_gpass($6);
+ free($6);
+#endif /* !NO_PRIVATE */
+ }
+ | SITE check_login SP GPASS CRLF
+ = {
+#ifndef NO_PRIVATE
+ if (log_commands)
+ syslog(LOG_INFO, "SITE GPASS");
+ if (!restricted_user && $2)
+ priv_gpass(NULL);
+#endif /* !NO_PRIVATE */
+ }
+ | SITE check_login SP NEWER SP STRING CRLF
+ = {
+ if (log_commands)
+ syslog(LOG_INFO, "SITE NEWER %s", $6);
+#ifdef SITE_NEWER
+ if ($2 && $6 && !restrict_check("."))
+ newer($6, ".", 0);
+#else
+ reply(502, "Command no longer honored by this server");
+#endif
+ free($6);
+ }
+ | SITE check_login SP NEWER SP STRING SP pathname CRLF
+ = {
+ if (log_commands)
+ syslog(LOG_INFO, "SITE NEWER %s %s", $6,
+ CHECKNULL($8));
+#ifdef SITE_NEWER
+ if ($2 && $6 && $8 && !restrict_check($8))
+ newer($6, $8, 0);
+#else
+ reply(502, "Command no longer honored by this server");
+#endif
+ free($6);
+ if ($8)
+ free($8);
+ }
+ | SITE check_login SP MINFO SP STRING CRLF
+ = {
+ if (log_commands)
+ syslog(LOG_INFO, "SITE MINFO %s", $6);
+#ifdef SITE_NEWER
+ if ($2 && $6 && !restrict_check("."))
+ newer($6, ".", 1);
+#else
+ reply(502, "Command no longer honored by this server");
+#endif
+ free($6);
+ }
+ | SITE check_login SP MINFO SP STRING SP pathname CRLF
+ = {
+ if (log_commands)
+ syslog(LOG_INFO, "SITE MINFO %s %s", $6,
+ CHECKNULL($8));
+#ifdef SITE_NEWER
+ if ($2 && $6 && $8 && !restrict_check($8))
+ newer($6, $8, 1);
+#else
+ reply(502, "Command no longer honored by this server");
+#endif
+ free($6);
+ if ($8)
+ free($8);
+ }
+ | SITE check_login SP INDEX SP STRING CRLF
+ = {
+ /* this is just for backward compatibility since we
+ * thought of INDEX before we thought of EXEC
+ */
+ if (!restricted_user && $2 != 0 && $6 != NULL) {
+ char buf[MAXPATHLEN];
+ if (strlen($6) + 7 <= sizeof(buf)) {
+ (void) snprintf(buf, sizeof(buf), "index %s", (char *) $6);
+ (void) site_exec(buf);
+ }
+ }
+ if ($6 != NULL)
+ free($6);
+ }
+ | SITE check_login SP EXEC SP STRING CRLF
+ = {
+ if (!restricted_user && $2 != 0 && $6 != NULL) {
+ (void) site_exec((char *) $6);
+ }
+ if ($6 != NULL)
+ free($6);
+ }
+
+ | STOU check_login SP pathname CRLF
+ = {
+ if (log_commands)
+ syslog(LOG_INFO, "STOU %s", CHECKNULL($4));
+ if ($2 && $4 && !restrict_check($4))
+ store($4, "w", 1);
+ if ($4 != NULL)
+ free($4);
+ }
+ | SYST check_login CRLF
+ = {
+ if (log_commands)
+ syslog(LOG_INFO, "SYST");
+ if ($2)
+#ifdef BSD
+ reply(215, "UNIX Type: L%d Version: BSD-%d", NBBY, BSD);
+#elif defined(SOLARIS_2)
+ reply(215, "UNIX Type: L%d Version: SUNOS", NBBY);
+#elif defined(unix) || defined(__unix__)
+ reply(215, "UNIX Type: L%d", NBBY);
+#else
+ reply(215, "UNKNOWN Type: L%d", NBBY);
+#endif /* BSD */
+ }
+
+ /*
+ * SIZE is not in RFC959, but Postel has blessed it and
+ * it will be in the updated RFC.
+ *
+ * Return size of file in a format suitable for
+ * using with RESTART (we just count bytes).
+ */
+ | SIZE check_login SP pathname CRLF
+ = {
+ if (log_commands)
+ syslog(LOG_INFO, "SIZE %s", CHECKNULL($4));
+ if ($2 && $4 && !restrict_check($4)) {
+ sizecmd($4);
+ }
+ if ($4 != NULL)
+ free($4);
+ }
+
+ /*
+ * MDTM is not in RFC959, but Postel has blessed it and
+ * it will be in the updated RFC.
+ *
+ * Return modification time of file as an ISO 3307
+ * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
+ * where xxx is the fractional second (of any precision,
+ * not necessarily 3 digits)
+ */
+ | MDTM check_login SP pathname CRLF
+ = {
+ if (log_commands)
+ syslog(LOG_INFO, "MDTM %s", CHECKNULL($4));
+ if ($2 && $4 && !restrict_check($4)) {
+ struct stat stbuf;
+
+ if (stat($4, &stbuf) < 0)
+ perror_reply(550, $4);
+ else if ((stbuf.st_mode & S_IFMT) != S_IFREG) {
+ reply(550, "%s: not a plain file.",
+ $4);
+ }
+ else {
+ register struct tm *t;
+ t = gmtime(&stbuf.st_mtime);
+ reply(213,
+ "%04d%02d%02d%02d%02d%02d",
+ t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
+ t->tm_hour, t->tm_min, t->tm_sec);
+ }
+ }
+ if ($4 != NULL)
+ free($4);
+ }
+ | QUIT CRLF
+ = {
+ if (log_commands)
+ syslog(LOG_INFO, "QUIT");
+#ifdef TRANSFER_COUNT
+ if (logged_in) {
+ lreply(221, "You have transferred %" L_FORMAT " bytes in %d files.", data_count_total, file_count_total);
+ lreply(221, "Total traffic for this session was %" L_FORMAT " bytes in %d transfers.", byte_count_total, xfer_count_total);
+ lreply(221, "Thank you for using the FTP service on %s.", hostname);
+ }
+#endif /* TRANSFER_COUNT */
+ reply(221, "Goodbye.");
+ dologout(0);
+ }
+ | error CRLF
+ = {
+ yyerrok;
+ }
+ ;
+
+rcmd: RNFR check_login SP pathname CRLF
+ = {
+
+ if (log_commands)
+ syslog(LOG_INFO, "RNFR %s", CHECKNULL($4));
+ if ($2)
+ restart_point = 0;
+ if (fromname) {
+ free(fromname);
+ fromname = NULL;
+ }
+ if ($2 && $4 && !restrict_check($4)) {
+ fromname = renamefrom($4);
+ }
+ if (fromname == NULL && $4)
+ free($4);
+ }
+ | REST check_login SP STRING CRLF
+ = {
+ if (log_commands)
+ syslog(LOG_INFO, "REST %s", CHECKNULL($4));
+ if ($2 && $4 != NULL) {
+ char *endp;
+
+ if (fromname) {
+ free(fromname);
+ fromname = NULL;
+ }
+ errno = 0;
+#if _FILE_OFFSET_BITS == 64
+ restart_point = strtoll($4, &endp, 10);
+#else
+ restart_point = strtol($4, &endp, 10);
+#endif
+ if ((errno == 0) && (restart_point >= 0) && (*endp == '\0')) {
+ reply(350, "Restarting at %" L_FORMAT
+ ". Send STORE or RETRIEVE to initiate transfer.",
+ restart_point);
+ }
+ else {
+ restart_point = 0;
+ reply(501, "Bad value for REST: %s", $4);
+ }
+ }
+ if ($4 != NULL)
+ free($4);
+ }
+
+ | SITE check_login SP ALIAS CRLF
+ = {
+ if (log_commands)
+ syslog(LOG_INFO, "SITE ALIAS");
+ if ($2)
+ alias((char *) NULL);
+ }
+ | SITE check_login SP ALIAS SP STRING CRLF
+ = {
+ if (log_commands)
+ syslog(LOG_INFO, "SITE ALIAS %s", $6);
+ if ($2)
+ alias($6);
+ if ($6 != NULL)
+ free($6);
+ }
+ | SITE check_login SP GROUPS CRLF
+ = {
+ if (log_commands)
+ syslog(LOG_INFO, "SITE GROUPS");
+ if ($2)
+ print_groups();
+ }
+ | SITE check_login SP CDPATH CRLF
+ = {
+ if (log_commands)
+ syslog(LOG_INFO, "SITE CDPATH");
+ if ($2)
+ cdpath();
+ }
+ | SITE check_login SP CHECKMETHOD SP method CRLF
+ = {
+ if (log_commands)
+ syslog(LOG_INFO, "SITE CHECKMETHOD %s", CHECKNULL($6));
+ if (($2) && ($6 != NULL))
+ SetCheckMethod($6);
+ if ($6 != NULL)
+ free($6);
+ }
+ | SITE check_login SP CHECKMETHOD CRLF
+ = {
+ if (log_commands)
+ syslog(LOG_INFO, "SITE CHECKMETHOD");
+ if ($2)
+ ShowCheckMethod();
+ }
+ | SITE check_login SP CHECKSUM SP pathname CRLF
+ = {
+ if (log_commands)
+ syslog(LOG_INFO, "SITE CHECKSUM %s", CHECKNULL($6));
+ if (($2) && ($6 != NULL) && (!restrict_check($6)))
+ CheckSum($6);
+ if ($6 != NULL)
+ free($6);
+ }
+ | SITE check_login SP CHECKSUM CRLF
+ = {
+ if (log_commands)
+ syslog(LOG_INFO, "SITE CHECKSUM");
+ if ($2)
+ CheckSumLastFile();
+ }
+ | PBSZ SP STRING CRLF
+ = {
+#if defined(USE_TLS) || defined(USE_GSS)
+ if (log_commands)
+ syslog(LOG_INFO, "PBSZ %s", $3);
+ {
+ int sz = 0;
+#if defined(USE_GSS)
+ sz = gss_setpbsz((char *)$3);
+#else
+ reply(200, "PBSZ=%d", sz);
+#endif /* defined(USE_GSS) */
+ pbsz_command_issued = 1;
+ }
+#endif /* defined(USE_TLS) || defined(USE_GSS) */
+ if ($3 != NULL)
+ free((char *)$3);
+ }
+ | AUTH SP STRING CRLF
+ = {
+#if defined(USE_TLS) || defined(USE_GSS)
+ register char *cp = (char *) $3;
+ if (log_commands)
+ syslog(LOG_INFO, "AUTH %s", $3);
+ /* convert to UPPER case as per RFC 2228 */
+ while (*cp) {
+ *cp = toupper(*cp);
+ cp++;
+ }
+#if defined(USE_GSS)
+ if (!strcmp((char *) $3, "GSSAPI")) {
+ if (cur_auth_type != NULL) {
+ reply(534, "Authentication type already set to %s",
+ cur_auth_type);
+ syslog(LOG_ERR, "Rejecting duplicate AUTH command");
+ } else {
+ cur_auth_type = strdup((char *)$3);
+ reply(334, "Using AUTH type %s; ADAT must follow",
+ cur_auth_type);
+ }
+ } else
+#endif /* defined(USE_GSS) */
+ {
+ /*
+ * Previous auth_type did not work, clear the string.
+ */
+ if (cur_auth_type != NULL) {
+ free(cur_auth_type);
+ cur_auth_type = NULL;
+ }
+ reply(504,"AUTH %s not supported.", $3);
+ }
+#endif /* !(defined(USE_TLS)) && !defined(USE_GSS) */
+ if ($3 != NULL)
+ free((char *)$3);
+ }
+ | PROT SP prot_code CRLF
+ = {
+#if defined(USE_TLS) || defined(USE_GSS)
+ if (log_commands)
+ syslog(LOG_INFO, "PROT %s", protnames[$3]);
+ {
+ if (!pbsz_command_issued) {
+ reply(503, "PROT command not valid before PBSZ.");
+ } else {
+ switch ($3) {
+ case PROT_P:
+ reply(200, "PROT P ok.");
+#if defined(USE_GSS)
+ gss_info.data_prot = PROT_P;
+#endif /* defined(USE_GSS) */
+ break;
+ case PROT_C:
+ reply(200, "PROT C ok.");
+#if defined(USE_GSS)
+ gss_info.data_prot = PROT_C;
+#endif /* defined(USE_GSS) */
+ break;
+ case PROT_E:
+ reply(536, "PROT E unsupported");
+ break;
+ case PROT_S:
+#if defined(USE_GSS)
+ reply(200, "PROT S ok.");
+ gss_info.data_prot = PROT_S;
+#endif /* defined(USE_GSS) */
+ break;
+ default:
+ reply(504, "Invalid PROT type.");
+ }
+#if defined(USE_GSS)
+ gss_adjust_buflen();
+#endif /* defined(USE_GSS) */
+ }
+ }
+#endif /* !(defined(USE_TLS) && !defined(USE_GSS)) */
+ }
+ | ADAT SP STRING CRLF
+ = {
+#if defined(USE_GSS)
+ if (log_commands)
+ syslog(LOG_INFO, "ADAT %s", $3);
+ if (cur_auth_type == NULL || strcmp(cur_auth_type, "GSSAPI")) {
+ reply(503, "Must identify AUTH GSSAPI before sending ADAT");
+ } else
+ (void) gss_adat((char *)$3);
+#endif
+ if ($3 != NULL)
+ free((char *)$3);
+ }
+ | CCC CRLF
+ = {
+#if defined(USE_GSS)
+ if (log_commands)
+ syslog(LOG_INFO, "CCC");
+ ccc();
+#endif /* defined(USE_GSS) */
+ }
+ ;
+
+username: STRING
+ ;
+
+password: /* empty */
+ = {
+ $$ = (char *) malloc(1);
+ $$[0] = '\0';
+ }
+ | STRING
+ ;
+
+byte_size: NUMBER
+ ;
+
+host_port: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER
+ = {
+ register char *a, *p;
+
+ a = (char *) &cliaddr;
+ a[0] = $1;
+ a[1] = $3;
+ a[2] = $5;
+ a[3] = $7;
+ p = (char *) &cliport;
+ p[0] = $9;
+ p[1] = $11;
+ }
+ ;
+
+host_lport: NUMBER COMMA NUMBER COMMA
+ NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
+ NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
+ NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
+ NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
+ NUMBER COMMA NUMBER COMMA NUMBER
+ = {
+#ifdef INET6
+ char *a, *p;
+ struct sockaddr_in6 *data_dest_sin6;
+
+ lport_error = 0;
+ if (epsv_all) {
+ reply(501, "LPRT not allowed after EPSV ALL");
+ lport_error = 1;
+ goto lport_done6;
+ }
+ if ($1 != 6) {
+ reply(521, "Supported address families are (4, 6)");
+ lport_error = 1;
+ goto lport_done6;
+ }
+ if (($3 != 16) || ($37 != 2)) {
+ reply(501, "Bad length.");
+ lport_error = 1;
+ goto lport_done6;
+ }
+ memset(&data_dest, 0, sizeof(struct sockaddr_in6));
+ data_dest_sin6 = (struct sockaddr_in6 *) &data_dest;
+ data_dest_sin6->sin6_family = AF_INET6;
+ a = (char *)&data_dest_sin6->sin6_addr;
+ a[0] = $5; a[1] = $7; a[2] = $9; a[3] = $11;
+ a[4] = $13; a[5] = $15; a[6] = $17; a[7] = $19;
+ a[8] = $21; a[9] = $23; a[10] = $25; a[11] = $27;
+ a[12] = $29; a[13] = $31; a[14] = $33; a[15] = $35;
+ p = (char *)&data_dest_sin6->sin6_port;
+ p[0] = $39; p[1] = $41;
+lport_done6:;
+#endif /* INET6 */
+ }
+ | NUMBER COMMA NUMBER COMMA
+ NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
+ NUMBER COMMA NUMBER COMMA NUMBER
+ = {
+#ifdef INET6
+ char *a, *p;
+ struct sockaddr_in *data_dest_sin;
+
+ lport_error = 0;
+ if (epsv_all) {
+ reply(501, "LPRT not allowed after EPSV ALL");
+ lport_error = 1;
+ goto lport_done4;
+ }
+ if ($1 != 4) {
+ reply(521, "Supported address families are (4, 6)");
+ lport_error = 1;
+ goto lport_done4;
+ }
+ if (($3 != 4) || ($13 != 2)) {
+ reply(501, "Bad length.");
+ lport_error = 1;
+ goto lport_done4;
+ }
+ data_dest_sin = (struct sockaddr_in *) &data_dest;
+ data_dest_sin->sin_family = AF_INET;
+ a = (char *)&data_dest_sin->sin_addr;
+ a[0] = $5; a[1] = $7; a[2] = $9; a[3] = $11;
+ p = (char *)&data_dest_sin->sin_port;
+ p[0] = $15; p[1] = $17;
+lport_done4:;
+#endif /* INET6 */
+ }
+ ;
+
+form_code: N
+ = {
+ $$ = FORM_N;
+ }
+ | T
+ = {
+ $$ = FORM_T;
+ }
+ | C
+ = {
+ $$ = FORM_C;
+ }
+ ;
+
+type_code: A
+ = {
+ cmd_type = TYPE_A;
+ cmd_form = FORM_N;
+ }
+ | A SP form_code
+ = {
+ cmd_type = TYPE_A;
+ cmd_form = $3;
+ }
+ | E
+ = {
+ cmd_type = TYPE_E;
+ cmd_form = FORM_N;
+ }
+ | E SP form_code
+ = {
+ cmd_type = TYPE_E;
+ cmd_form = $3;
+ }
+ | I
+ = {
+ cmd_type = TYPE_I;
+ }
+ | L
+ = {
+ cmd_type = TYPE_L;
+ cmd_bytesz = NBBY;
+ }
+ | L SP byte_size
+ = {
+ cmd_type = TYPE_L;
+ cmd_bytesz = $3;
+ }
+ /* this is for a bug in the BBN ftp */
+ | L byte_size
+ = {
+ cmd_type = TYPE_L;
+ cmd_bytesz = $2;
+ }
+ ;
+
+prot_code: C
+ = {
+#if defined(USE_GSS)
+ $$ = PROT_C;
+#endif
+ }
+ | P
+ = {
+#if defined(USE_GSS)
+ $$ = PROT_P;
+#endif
+ }
+ | S
+ = {
+#if defined(USE_GSS)
+ $$ = PROT_S;
+#endif
+ }
+ | E
+ = {
+#if defined(USE_GSS)
+ $$ = PROT_E;
+#endif
+ }
+ ;
+
+struct_code: F
+ = {
+ $$ = STRU_F;
+ }
+ | R
+ = {
+ $$ = STRU_R;
+ }
+ | P
+ = {
+ $$ = STRU_P;
+ }
+ ;
+
+mode_code: S
+ = {
+ $$ = MODE_S;
+ }
+ | B
+ = {
+ $$ = MODE_B;
+ }
+ | C
+ = {
+ $$ = MODE_C;
+ }
+ ;
+
+pathname: pathstring
+ = {
+ /*
+ * Problem: this production is used for all pathname
+ * processing, but only gives a 550 error reply.
+ * This is a valid reply in some cases but not in others.
+ */
+ if (restricted_user && logged_in && $1 && strncmp($1, "/", 1) == 0) {
+ /*
+ * This remaps the root so it is appearently at the user's home
+ * rather than the real root/chroot.
+ */
+ size_t len = strlen($1) + 2;
+ char **globlist;
+ char *t = calloc(len, sizeof(char));
+ if (t == NULL) {
+ errno = EAGAIN;
+ perror_reply(550, $1);
+ $$ = NULL;
+ }
+ else {
+ t[0] = '~';
+ t[1] = '\0';
+ if (strncmp($1, "/../", 4) == 0)
+ (void) strlcat(t, $1 + 3, len);
+ else if (strcmp($1, "/..") != 0)
+ (void) strlcat(t, $1, len);
+ globlist = ftpglob(t);
+ if (globerr) {
+ reply(550, "%s", globerr);
+ $$ = NULL;
+ if (globlist) {
+ blkfree(globlist);
+ free((char *) globlist);
+ }
+ }
+ else if (globlist && *globlist) {
+ $$ = *globlist;
+ blkfree(&globlist[1]);
+ free((char *) globlist);
+ }
+ else {
+ if (globlist) {
+ blkfree(globlist);
+ free((char *) globlist);
+ }
+ errno = ENOENT;
+ perror_reply(550, $1);
+ $$ = NULL;
+ }
+ free(t);
+ }
+ free($1);
+ }
+ else if (logged_in && $1 && strncmp($1, "~", 1) == 0) {
+ char **globlist;
+
+ globlist = ftpglob($1);
+ if (globerr) {
+ reply(550, "%s", globerr);
+ $$ = NULL;
+ if (globlist) {
+ blkfree(globlist);
+ free((char *) globlist);
+ }
+ }
+ else if (globlist && *globlist) {
+ $$ = *globlist;
+ blkfree(&globlist[1]);
+ free((char *) globlist);
+ }
+ else {
+ if (globlist) {
+ blkfree(globlist);
+ free((char *) globlist);
+ }
+ errno = ENOENT;
+ perror_reply(550, $1);
+ $$ = NULL;
+ }
+ free($1);
+ }
+ else
+ $$ = $1;
+ }
+ ;
+
+pathstring: STRING
+ ;
+
+method: STRING
+ ;
+
+octal_number: NUMBER
+ = {
+ register int ret, dec, multby, digit;
+
+ /*
+ * Convert a number that was read as decimal number
+ * to what it would be if it had been read as octal.
+ */
+ dec = $1;
+ multby = 1;
+ ret = 0;
+ while (dec) {
+ digit = dec % 10;
+ if (digit > 7) {
+ ret = -1;
+ break;
+ }
+ ret += digit * multby;
+ multby *= 8;
+ dec /= 10;
+ }
+ $$ = ret;
+ }
+ ;
+
+check_login: /* empty */
+ = {
+ if (logged_in)
+ $$ = 1;
+ else {
+ if (log_commands)
+ syslog(LOG_INFO, "cmd failure - not logged in");
+ reply(530, "Please login with USER and PASS.");
+ $$ = 0;
+ yyerrorcalled = 1;
+ }
+ }
+ ;
+
+%%
+
+extern jmp_buf errcatch;
+
+#define CMD 0 /* beginning of command */
+#define ARGS 1 /* expect miscellaneous arguments */
+#define STR1 2 /* expect SP followed by STRING */
+#define STR2 3 /* expect STRING */
+#define OSTR 4 /* optional SP then STRING */
+#define ZSTR1 5 /* SP then optional STRING */
+#define ZSTR2 6 /* optional STRING after SP */
+#define SITECMD 7 /* SITE command */
+#define NSTR 8 /* Number followed by a string */
+#define STR3 9 /* expect STRING followed by optional SP then STRING */
+
+struct tab cmdtab[] =
+{ /* In order defined in RFC 765 */
+ {"USER", USER, STR1, 1, "<sp> username"},
+ {"PASS", PASS, ZSTR1, 1, "<sp> password"},
+ {"ACCT", ACCT, STR1, 0, "(specify account)"},
+ {"SMNT", SMNT, ARGS, 0, "(structure mount)"},
+ {"REIN", REIN, ARGS, 0, "(reinitialize server state)"},
+ {"QUIT", QUIT, ARGS, 1, "(terminate service)",},
+ {"PORT", PORT, ARGS, 1, "<sp> h1, h2, h3, h4, p1, p2"},
+ {"PASV", PASV, ARGS, 1, "(set server in passive mode)"},
+#ifdef INET6
+ {"EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|"},
+ {"EPSV", EPSV, OSTR, 1, "[<sp> af|ALL]"},
+ {"LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, ..., pal, p1, p2, ..."},
+ {"LPSV", LPSV, ARGS, 1, "(set server in long passive mode)"},
+#endif
+ {"TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]"},
+ {"STRU", STRU, ARGS, 1, "(specify file structure)"},
+ {"MODE", MODE, ARGS, 1, "(specify transfer mode)"},
+ {"RETR", RETR, STR1, 1, "<sp> file-name"},
+ {"STOR", STOR, STR1, 1, "<sp> file-name"},
+ {"APPE", APPE, STR1, 1, "<sp> file-name"},
+ {"MLFL", MLFL, OSTR, 0, "(mail file)"},
+ {"MAIL", MAIL, OSTR, 0, "(mail to user)"},
+ {"MSND", MSND, OSTR, 0, "(mail send to terminal)"},
+ {"MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)"},
+ {"MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)"},
+ {"MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)"},
+ {"MRCP", MRCP, STR1, 0, "(mail recipient)"},
+ {"ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)"},
+ {"REST", REST, STR1, 1, "(restart command)"},
+ {"RNFR", RNFR, STR1, 1, "<sp> file-name"},
+ {"RNTO", RNTO, STR1, 1, "<sp> file-name"},
+ {"ABOR", ABOR, ARGS, 1, "(abort operation)"},
+ {"DELE", DELE, STR1, 1, "<sp> file-name"},
+ {"CWD", CWD, OSTR, 1, "[ <sp> directory-name ]"},
+ {"XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]"},
+ {"LIST", LIST, OSTR, 1, "[ <sp> path-name ]"},
+ {"NLST", NLST, OSTR, 1, "[ <sp> path-name ]"},
+ {"SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]"},
+ {"SYST", SYST, ARGS, 1, "(get type of operating system)"},
+ {"STAT", STAT, OSTR, 1, "[ <sp> path-name ]"},
+ {"HELP", HELP, OSTR, 1, "[ <sp> <string> ]"},
+ {"NOOP", NOOP, ARGS, 1, ""},
+ {"MKD", MKD, STR1, 1, "<sp> path-name"},
+ {"XMKD", MKD, STR1, 1, "<sp> path-name"},
+ {"RMD", RMD, STR1, 1, "<sp> path-name"},
+ {"XRMD", RMD, STR1, 1, "<sp> path-name"},
+ {"PWD", PWD, ARGS, 1, "(return current directory)"},
+ {"XPWD", PWD, ARGS, 1, "(return current directory)"},
+ {"CDUP", CDUP, ARGS, 1, "(change to parent directory)"},
+ {"XCUP", CDUP, ARGS, 1, "(change to parent directory)"},
+ {"STOU", STOU, STR1, 1, "<sp> file-name"},
+ {"SIZE", SIZE, OSTR, 1, "<sp> path-name"},
+ {"MDTM", MDTM, OSTR, 1, "<sp> path-name"},
+#if defined(USE_TLS) || defined(USE_GSS)
+ {"PROT", PROT, ARGS, 1, "<sp> protection-level"},
+ {"PBSZ", PBSZ, STR1, 1, "<sp> protection-buffer-size"},
+ {"AUTH", AUTH, STR1, 1, "<sp> authentication-mechanism"},
+ {"ADAT", ADAT, STR1, 1, "<sp> authentication-data"},
+#if defined(USE_GSS)
+ {"CCC", CCC, ARGS, 1, "(clear command channel)"},
+#endif
+#endif /* defined(USE_TLS) || defined(USE_GSS) */
+ {NULL, 0, 0, 0, 0}
+};
+
+struct tab sitetab[] =
+{
+ {"UMASK", UMASK, ARGS, 1, "[ <sp> umask ]"},
+ {"IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]"},
+ {"CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name"},
+ {"HELP", HELP, OSTR, 1, "[ <sp> <string> ]"},
+ {"GROUP", GROUP, STR1, 1, "<sp> access-group"},
+ {"GPASS", GPASS, OSTR, 1, "<sp> access-password"},
+ {"NEWER", NEWER, STR3, 1, "<sp> YYYYMMDDHHMMSS [ <sp> path-name ]"},
+ {"MINFO", MINFO, STR3, 1, "<sp> YYYYMMDDHHMMSS [ <sp> path-name ]"},
+ {"INDEX", INDEX, STR1, 1, "<sp> pattern"},
+ {"EXEC", EXEC, STR1, 1, "<sp> command [ <sp> arguments ]"},
+ {"ALIAS", ALIAS, OSTR, 1, "[ <sp> alias ] "},
+ {"CDPATH", CDPATH, OSTR, 1, "[ <sp> ] "},
+ {"GROUPS", GROUPS, OSTR, 1, "[ <sp> ] "},
+ {"CHECKMETHOD", CHECKMETHOD, OSTR, 1, "[ <sp> crc|md5 ]"},
+ {"CHECKSUM", CHECKSUM, OSTR, 1, "[ <sp> file-name ]"},
+ {NULL, 0, 0, 0, 0}
+};
+
+struct tab *lookup(register struct tab *p, char *cmd)
+{
+ for (; p->name != NULL; p++)
+ if (strcmp(cmd, p->name) == 0)
+ return (p);
+ return (0);
+}
+
+#include <arpa/telnet.h>
+
+/*
+ * getline - a hacked up version of fgets to ignore TELNET escape codes.
+ */
+char *wu_getline(char *s, int n, register FILE *iop)
+{
+ register int c;
+ register char *cs;
+ char *passtxt = "PASS password\r\n";
+
+ cs = s;
+/* tmpline may contain saved command from urgent mode interruption */
+ for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
+ *cs++ = tmpline[c];
+ if (tmpline[c] == '\n') {
+ *cs++ = '\0';
+ if (debug) {
+ if (strncasecmp(passtxt, s, 5) == 0)
+ syslog(LOG_DEBUG, "command: %s", passtxt);
+ else
+ syslog(LOG_DEBUG, "command: %s", s);
+ }
+ tmpline[0] = '\0';
+ return (s);
+ }
+ if (c == 0)
+ tmpline[0] = '\0';
+ }
+ retry:
+ while ((c = getc(iop)) != EOF) {
+#ifdef TRANSFER_COUNT
+ byte_count_total++;
+ byte_count_in++;
+#endif
+ c &= 0377;
+ if (c == IAC) {
+ if ((c = getc(iop)) != EOF) {
+#ifdef TRANSFER_COUNT
+ byte_count_total++;
+ byte_count_in++;
+#endif
+ c &= 0377;
+ switch (c) {
+ case WILL:
+ case WONT:
+ c = getc(iop);
+#ifdef TRANSFER_COUNT
+ byte_count_total++;
+ byte_count_in++;
+#endif
+ printf("%c%c%c", IAC, DONT, 0377 & c);
+ (void) fflush(stdout);
+ continue;
+ case DO:
+ case DONT:
+ c = getc(iop);
+#ifdef TRANSFER_COUNT
+ byte_count_total++;
+ byte_count_in++;
+#endif
+ printf("%c%c%c", IAC, WONT, 0377 & c);
+ (void) fflush(stdout);
+ continue;
+ case IAC:
+ break;
+ default:
+ continue; /* ignore command */
+ }
+ }
+ }
+ *cs++ = c;
+ if (--n <= 0 || c == '\n')
+ break;
+ }
+
+ if (c == EOF && cs == s) {
+ if (ferror(iop) && (errno == EINTR))
+ goto retry;
+ return (NULL);
+ }
+
+ *cs++ = '\0';
+
+#if defined(USE_GSS)
+ if (IS_GSSAUTH(cur_auth_type) &&
+ (gss_info.authstate & GSS_ADAT_DONE) &&
+ gss_info.context != GSS_C_NO_CONTEXT) {
+ s = sec_decode_command(s);
+ } else if (IS_GSSAUTH(cur_auth_type) &&
+ (!strncmp(s, "ENC", 3) || !strncmp(s, "MIC", 3) ||
+ !strncmp(s, "CONF", 4)) &&
+ !(gss_info.authstate & GSS_ADAT_DONE)) {
+ if (debug)
+ syslog(LOG_DEBUG, "command: %s", s);
+ reply(503, "Must perform authentication before sending protected commands");
+ *s = '\0';
+ return(s);
+ }
+#endif /* USE_GSS */
+ if (debug) {
+ if (strncasecmp(passtxt, s, 5) == 0)
+ syslog(LOG_DEBUG, "command: %s", passtxt);
+ else
+ syslog(LOG_DEBUG, "command: %s", s);
+ }
+ return (s);
+}
+
+static void toolong(int a) /* signal that caused this function to be called */
+{
+ time_t now;
+
+ reply(421,
+ "Timeout (%d seconds): closing control connection.", timeout_idle);
+ (void) time(&now);
+ if (logging) {
+ syslog(LOG_INFO,
+ "User %s timed out after %d seconds at %.24s",
+ (pw ? pw->pw_name : "unknown"), timeout_idle, ctime(&now));
+ }
+ dologout(1);
+}
+
+int yylex(void)
+{
+ static int cpos, state;
+ register char *cp, *cp2;
+ register struct tab *p;
+ int n;
+ time_t now;
+ char c = '\0';
+ extern time_t limit_time;
+ extern time_t login_time;
+
+ for (;;) {
+ switch (state) {
+
+ case CMD:
+ yyerrorcalled = 0;
+
+ setproctitle("%s: IDLE", proctitle);
+
+ if (is_shutdown(!logged_in, 0) != 0) {
+ reply(221, "Server shutting down. Goodbye.");
+ dologout(0);
+ }
+
+ time(&now);
+ if ((limit_time > 0) && (((now - login_time) / 60) >= limit_time)) {
+ reply(221, "Time limit reached. Goodbye.");
+ dologout(0);
+ }
+
+#ifdef IGNORE_NOOP
+ if (!alarm_running) {
+ (void) signal(SIGALRM, toolong);
+ (void) alarm((unsigned) timeout_idle);
+ alarm_running = 1;
+ }
+#else
+ (void) signal(SIGALRM, toolong);
+ (void) alarm((unsigned) timeout_idle);
+#endif
+ if (wu_getline(cbuf, sizeof(cbuf) - 1, stdin) == NULL) {
+ (void) alarm(0);
+ reply(221, "You could at least say goodbye.");
+ dologout(0);
+ }
+#ifndef IGNORE_NOOP
+ (void) alarm(0);
+#endif
+ if ((cp = strchr(cbuf, '\r'))) {
+ *cp++ = '\n';
+ *cp = '\0';
+ }
+ if ((cp = strpbrk(cbuf, " \n")))
+ cpos = cp - cbuf;
+ if (cpos == 0)
+ cpos = 4;
+ c = cbuf[cpos];
+ cbuf[cpos] = '\0';
+ upper(cbuf);
+#ifdef IGNORE_NOOP
+ if (strncasecmp(cbuf, "NOOP", 4) != 0) {
+ (void) alarm(0);
+ alarm_running = 0;
+ }
+#endif
+ p = lookup(cmdtab, cbuf);
+ cbuf[cpos] = c;
+ if (strncasecmp(cbuf, "PASS", 4) != 0 &&
+ strncasecmp(cbuf, "SITE GPASS", 10) != 0) {
+ if ((cp = strchr(cbuf, '\n')))
+ *cp = '\0';
+ setproctitle("%s: %s", proctitle, cbuf);
+ if (cp)
+ *cp = '\n';
+ }
+ if (p != 0) {
+ if (p->implemented == 0) {
+ nack(p->name);
+ longjmp(errcatch, 0);
+ /* NOTREACHED */
+ }
+ state = p->state;
+ yylval.String = p->name;
+ return (p->token);
+ }
+ break;
+
+ case SITECMD:
+ if (cbuf[cpos] == ' ') {
+ cpos++;
+ return (SP);
+ }
+ cp = &cbuf[cpos];
+ if ((cp2 = strpbrk(cp, " \n")))
+ cpos = cp2 - cbuf;
+ c = cbuf[cpos];
+ cbuf[cpos] = '\0';
+ upper(cp);
+ p = lookup(sitetab, cp);
+ cbuf[cpos] = c;
+ if (p != 0) {
+#ifndef PARANOID /* what GOOD is SITE *, anyways?! _H */
+ if (p->implemented == 0) {
+#else
+ if (1) {
+ syslog(LOG_WARNING, "refused SITE %s %s from %s of %s",
+ p->name, &cbuf[cpos],
+ anonymous ? guestpw : authuser, remoteident);
+#endif /* PARANOID */
+ state = CMD;
+ nack(p->name);
+ longjmp(errcatch, 0);
+ /* NOTREACHED */
+ }
+ state = p->state;
+ yylval.String = p->name;
+ return (p->token);
+ }
+ state = CMD;
+ break;
+
+ case OSTR:
+ if (cbuf[cpos] == '\n') {
+ state = CMD;
+ return (CRLF);
+ }
+ /* FALLTHROUGH */
+
+ case STR1:
+ case ZSTR1:
+ dostr1:
+ if (cbuf[cpos] == ' ') {
+ cpos++;
+ if (state == OSTR)
+ state = STR2;
+ else
+ ++state;
+ return (SP);
+ }
+ break;
+
+ case ZSTR2:
+ if (cbuf[cpos] == '\n') {
+ state = CMD;
+ return (CRLF);
+ }
+ /* FALLTHROUGH */
+
+ case STR2:
+ cp = &cbuf[cpos];
+ n = strlen(cp);
+ cpos += n - 1;
+ /*
+ * Make sure the string is nonempty and \n terminated.
+ */
+ if (n > 1 && cbuf[cpos] == '\n') {
+ cbuf[cpos] = '\0';
+ yylval.String = copy(cp);
+ cbuf[cpos] = '\n';
+ state = ARGS;
+ return (STRING);
+ }
+ break;
+
+ case NSTR:
+ if (cbuf[cpos] == ' ') {
+ cpos++;
+ return (SP);
+ }
+ if (isdigit(cbuf[cpos])) {
+ cp = &cbuf[cpos];
+ while (isdigit(cbuf[++cpos]));
+ c = cbuf[cpos];
+ cbuf[cpos] = '\0';
+ yylval.Number = atoi(cp);
+ cbuf[cpos] = c;
+ state = STR1;
+ return (NUMBER);
+ }
+ state = STR1;
+ goto dostr1;
+
+ case STR3:
+ if (cbuf[cpos] == ' ') {
+ cpos++;
+ return (SP);
+ }
+
+ cp = &cbuf[cpos];
+ cp2 = strpbrk(cp, " \n");
+ if (cp2 != NULL) {
+ c = *cp2;
+ *cp2 = '\0';
+ }
+ n = strlen(cp);
+ cpos += n;
+ /*
+ * Make sure the string is nonempty and SP terminated.
+ */
+ if ((cp2 - cp) > 1) {
+ yylval.String = copy(cp);
+ cbuf[cpos] = c;
+ state = OSTR;
+ return (STRING);
+ }
+ break;
+
+ case ARGS:
+ if (isdigit(cbuf[cpos])) {
+ cp = &cbuf[cpos];
+ while (isdigit(cbuf[++cpos]));
+ c = cbuf[cpos];
+ cbuf[cpos] = '\0';
+ yylval.Number = atoi(cp);
+ cbuf[cpos] = c;
+ return (NUMBER);
+ }
+ switch (cbuf[cpos++]) {
+
+ case '\n':
+ state = CMD;
+ return (CRLF);
+
+ case ' ':
+ return (SP);
+
+ case ',':
+ return (COMMA);
+
+ case 'A':
+ case 'a':
+ return (A);
+
+ case 'B':
+ case 'b':
+ return (B);
+
+ case 'C':
+ case 'c':
+ return (C);
+
+ case 'E':
+ case 'e':
+ return (E);
+
+ case 'F':
+ case 'f':
+ return (F);
+
+ case 'I':
+ case 'i':
+ return (I);
+
+ case 'L':
+ case 'l':
+ return (L);
+
+ case 'N':
+ case 'n':
+ return (N);
+
+ case 'P':
+ case 'p':
+ return (P);
+
+ case 'R':
+ case 'r':
+ return (R);
+
+ case 'S':
+ case 's':
+ return (S);
+
+ case 'T':
+ case 't':
+ return (T);
+
+ }
+ break;
+
+ default:
+ fatal("Unknown state in scanner.");
+ }
+ if (yyerrorcalled == 0) {
+ if ((cp = strchr(cbuf, '\n')) != NULL)
+ *cp = '\0';
+ if (logged_in)
+ reply(500, "'%s': command not understood.", cbuf);
+ else
+ reply(530, "Please login with USER and PASS.");
+ }
+ state = CMD;
+ longjmp(errcatch, 0);
+ }
+}
+
+void upper(char *s)
+{
+ while (*s != '\0') {
+ if (islower(*s))
+ *s = toupper(*s);
+ s++;
+ }
+}
+
+char *copy(char *s)
+{
+ char *p;
+
+ p = strdup(s);
+ if (p == NULL)
+ fatal("Ran out of memory.");
+ return (p);
+}
+
+void help(struct tab *ctab, char *s)
+{
+ struct aclmember *entry = NULL;
+ struct tab *c;
+ size_t width, NCMDS;
+ char *type;
+
+ if (ctab == sitetab)
+ type = "SITE ";
+ else
+ type = "";
+ width = 0, NCMDS = 0;
+ for (c = ctab; c->name != NULL; c++) {
+ size_t len = strlen(c->name);
+
+ if (len > width)
+ width = len;
+ NCMDS++;
+ }
+ width = (width + 8) & ~7;
+ if (s == 0) {
+ register size_t i, j, w;
+ size_t columns, lines;
+
+ lreply(214, "The following %scommands are recognized %s.",
+ type, "(* =>'s unimplemented)");
+ columns = 76 / width;
+ if (columns == 0)
+ columns = 1;
+ lines = (NCMDS + columns - 1) / columns;
+ for (i = 0; i < lines; i++) {
+ char line[BUFSIZ], *ptr = line;
+ ptr += strlcpy(line, " ", sizeof(line));
+ for (j = 0; j < columns; j++) {
+ c = ctab + j * lines + i;
+ (void) snprintf(ptr, line + sizeof(line) - ptr, "%s%c",
+ c->name, c->implemented ? ' ' : '*');
+ w = strlen(c->name) + 1;
+ ptr += w;
+ if (c + lines >= &ctab[NCMDS])
+ break;
+ while (w < width) {
+ *(ptr++) = ' ';
+ w++;
+ }
+ }
+ *ptr = '\0';
+ lreply(0, "%s", line);
+ }
+ (void) fflush(stdout);
+#ifdef VIRTUAL
+ if (virtual_mode && !virtual_ftpaccess && virtual_email[0] != '\0')
+ reply(214, "Direct comments to %s.", virtual_email);
+ else
+#endif
+ if ((getaclentry("email", &entry)) && ARG0)
+ reply(214, "Direct comments to %s.", ARG0);
+ else
+ reply(214, "Direct comments to ftp-bugs@%s.", hostname);
+ return;
+ }
+ upper(s);
+ c = lookup(ctab, s);
+ if (c == (struct tab *) NULL) {
+ reply(502, "Unknown command %s.", s);
+ return;
+ }
+ if (c->implemented)
+ reply(214, "Syntax: %s%s %s", type, c->name, c->help);
+ else
+ reply(214, "%s%-*s\t%s; unimplemented.", type, width,
+ c->name, c->help);
+}
+
+void sizecmd(char *filename)
+{
+ switch (type) {
+ case TYPE_L:
+ case TYPE_I:{
+ struct stat stbuf;
+ if (stat(filename, &stbuf) < 0 ||
+ (stbuf.st_mode & S_IFMT) != S_IFREG)
+ reply(550, "%s: not a plain file.", filename);
+ else
+ reply(213, "%" L_FORMAT, stbuf.st_size);
+ break;
+ }
+ case TYPE_A:{
+ FILE *fin;
+ register int c;
+ register off_t count;
+ struct stat stbuf;
+ fin = fopen(filename, "r");
+ if (fin == NULL) {
+ perror_reply(550, filename);
+ return;
+ }
+ if (fstat(fileno(fin), &stbuf) < 0 ||
+ (stbuf.st_mode & S_IFMT) != S_IFREG) {
+ reply(550, "%s: not a plain file.", filename);
+ (void) fclose(fin);
+ return;
+ }
+
+ count = 0;
+ while ((c = getc(fin)) != EOF) {
+ if (c == '\n') /* will get expanded to \r\n */
+ count++;
+ count++;
+ }
+ (void) fclose(fin);
+
+ reply(213, "%" L_FORMAT, count);
+ break;
+ }
+ default:
+ reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]);
+ }
+}
+
+void site_exec(char *cmd)
+{
+#ifdef PARANOID
+ syslog(LOG_CRIT, "REFUSED SITE_EXEC (slipped through!!): %s", cmd);
+#else
+ char buf[MAXPATHLEN];
+ char *sp = (char *) strchr(cmd, ' '), *slash, *t;
+ FILE *cmdf;
+
+
+ /* sanitize the command-string */
+
+ if (sp == 0) {
+ while ((slash = strchr(cmd, '/')) != 0)
+ cmd = slash + 1;
+ }
+ else {
+ while (sp && (slash = (char *) strchr(cmd, '/'))
+ && (slash < sp))
+ cmd = slash + 1;
+ }
+
+ for (t = cmd; *t && !isspace(*t); t++) {
+ if (isupper(*t)) {
+ *t = tolower(*t);
+ }
+ }
+
+ /* build the command */
+ if (strlen(_PATH_EXECPATH) + strlen(cmd) + 2 > sizeof(buf))
+ return;
+ (void) snprintf(buf, sizeof(buf), "%s/%s", _PATH_EXECPATH, cmd);
+
+ cmdf = ftpd_popen(buf, "r", 0);
+ if (!cmdf) {
+ perror_reply(550, cmd);
+ if (log_commands)
+ syslog(LOG_INFO, "SITE EXEC (FAIL: %m): %s", cmd);
+ }
+ else {
+ int lines = 0;
+ int maxlines = 0;
+ struct aclmember *entry = NULL;
+ char class[BUFSIZ];
+ int maxfound = 0;
+ int defmaxlines = 20;
+ int which;
+
+ (void) acl_getclass(class);
+ while ((getaclentry("site-exec-max-lines", &entry)) && ARG0) {
+ if (ARG1)
+ for (which = 1; (which < MAXARGS) && ARG[which]; which++) {
+ if (!strcasecmp(ARG[which], class)) {
+ maxlines = atoi(ARG0);
+ maxfound = 1;
+ }
+ if (!strcmp(ARG[which], "*"))
+ defmaxlines = atoi(ARG0);
+ }
+ else
+ defmaxlines = atoi(ARG0);
+ }
+ if (!maxfound)
+ maxlines = defmaxlines;
+ lreply(200, "%s", cmd);
+ while (fgets(buf, sizeof buf, cmdf)) {
+ size_t len = strlen(buf);
+
+ if (len > 0 && buf[len - 1] == '\n')
+ buf[--len] = '\0';
+ lreply(200, "%s", buf);
+ if (maxlines <= 0)
+ ++lines;
+ else if (++lines >= maxlines) {
+ lreply(200, "*** Truncated ***");
+ break;
+ }
+ }
+ reply(200, " (end of '%s')", cmd);
+ if (log_commands)
+ syslog(LOG_INFO, "SITE EXEC (lines: %d): %s", lines, cmd);
+ ftpd_pclose(cmdf);
+ }
+#endif /* PARANOID */
+}
+
+void alias(char *s)
+{
+ struct aclmember *entry = NULL;
+
+ if (s != (char *) NULL) {
+ while (getaclentry("alias", &entry) && ARG0 && ARG1 != NULL)
+ if (!strcmp(ARG0, s)) {
+ reply(214, "%s is an alias for %s.", ARG0, ARG1);
+ return;
+ }
+ reply(502, "Unknown alias %s.", s);
+ return;
+ }
+
+ lreply(214, "The following aliases are available.");
+
+ while (getaclentry("alias", &entry) && ARG0 && ARG1 != NULL)
+ lreply(0, " %-8s %s", ARG0, ARG1);
+ (void) fflush(stdout);
+
+ reply(214, "");
+}
+
+void cdpath(void)
+{
+ struct aclmember *entry = NULL;
+
+ lreply(214, "The cdpath is:");
+ while (getaclentry("cdpath", &entry) && ARG0 != NULL)
+ lreply(0, " %s", ARG0);
+ (void) fflush(stdout);
+ reply(214, "");
+}
+
+void print_groups(void)
+{
+ gid_t groups[NGROUPS_MAX];
+ int ngroups = 0;
+
+ if ((ngroups = getgroups(NGROUPS_MAX, groups)) < 0) {
+ return;
+ }
+
+ lreply(214, "Group membership is:");
+ ngroups--;
+
+ for (; ngroups >= 0; ngroups--)
+ lreply(214, " %d", groups[ngroups]);
+
+ (void) fflush(stdout);
+ reply(214, "");
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpconfig.sh b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpconfig.sh
new file mode 100644
index 0000000000..010030ca7b
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpconfig.sh
@@ -0,0 +1,376 @@
+#!/usr/bin/ksh
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#
+# This script sets up anonymous FTP on the current host.
+#
+# Usage:
+# ftpconfig [ftpdir]
+# ftpconfig -d ftpdir
+#
+# ftpconfig without any arguments updates the files required by anonymous FTP
+# in an existing ftp users home directory.
+#
+# If ftpdir (which must be an absolute pathname) is supplied, ftpconfig
+# creates an ftp user account with a home directory of ftpdir, or updates
+# an existing ftp user account to have a home directory of ftpdir.
+#
+# If ftpdir already exists, the files it contains which are required by
+# anonymous FTP are updated, otherwise ftpdir is created containing the files
+# required by anonymous FTP.
+#
+# The -d (directory only) option just creates a new or updates an existing
+# ftpdir without creating or updating the ftp user account. This is useful
+# when creating guest FTP user accounts.
+#
+# Exit codes: 0 - success
+# 1 - usage
+# 2 - command failure
+#
+
+usage()
+{
+ fmt=`gettext "Usage: %s [ftpdir]\n %s -d ftpdir"`
+ printf "$fmt\n" "$cmd" "$cmd" >&2
+ exit 1
+}
+
+verify_root()
+{
+ # Verify caller has a real user ID of 0.
+ set `id`
+ if [ "$1" != "uid=0(root)" ]
+ then
+ fmt=`gettext "%s: Error: Only root can run %s"`
+ printf "$fmt\n" "$cmd" "$cmd" >&2
+ exit 1
+ fi
+}
+
+# Make directory $1 under $home_dir with mode $2 and ownership $3.
+make_dir()
+{
+ # Make a special case of creating $home_dir itself
+ if [ -z "$1" ]
+ then
+ dir="$home_dir"
+ else
+ dir="$home_dir/$1"
+ fi
+ if [ ! -d "$dir" ]
+ then
+ mkdir "$dir" || exit 2
+ fi
+ chmod "$2" "$dir"
+ chown "$3" "$dir"
+}
+
+# Copy file $1 to under $home_dir with mode $2 and ownership $3.
+copy_file()
+{
+ if [ -f "$1" ]
+ then
+ file="$home_dir$1"
+ rm -f "$file"
+ cp "$1" "$file" || exit 2
+ chmod "$2" "$file"
+ chown "$3" "$file"
+ fi
+}
+
+add_user()
+{
+ pwent=`grep "^$username:" /etc/passwd`
+ if [ -z "$pwent" ]
+ then
+ # No existing ftp account.
+ if [ -z "$home_dir" ]
+ then
+ fmt=`gettext "%s: Error: No directory specified and no existing ftp account to update"`
+ printf "$fmt\n" "$cmd" >&2
+ exit 1
+ fi
+
+ # Create a new ftp account.
+ comment="Anonymous FTP"
+ fmt=`gettext "Creating user %s"`
+ printf "$fmt\n" "$username"
+ /usr/sbin/useradd -c "$comment" -d "$home_dir" -s "$login_shell" -g other "$username" || exit 2
+ else
+ # ftp account already exists.
+ if [ -z "$home_dir" ]
+ then
+ home_dir=`echo "$pwent" | cut -d: -f6`
+ if [ -z "$home_dir" ]
+ then
+ fmt=`gettext "%s: Error: Existing ftp account has no home directory"`
+ printf "$fmt\n" "$cmd" >&2
+ exit 2
+ fi
+ else
+ # Update an existing ftp account.
+ old_dir=`echo "$pwent" | cut -d: -f6`
+ if [ "$old_dir" != "$home_dir" ]
+ then
+ fmt=`gettext "Updating user %s"`
+ printf "$fmt\n" "$username"
+ /usr/sbin/usermod -d "$home_dir" "$username" || exit 2
+ fi
+ fi
+ fi
+}
+
+list_pam_session()
+{
+ # Produce a list of the PAM session management modules.
+ if [ -f /etc/pam.conf ]
+ then
+ awk '($1 == "ftp" || $1 == "other") && $2 == "session" {
+ if ($4 !~ /^\//) printf "/usr/lib/security/"
+ print $4
+ }' </etc/pam.conf | sed 's/$ISA\///'
+ fi
+}
+
+list_conv_cmds()
+{
+ # Produce a list of the commands specified in the conversions file.
+ if [ -f /etc/ftpd/ftpconversions ]
+ then
+ sed 's/#.*$//' /etc/ftpd/ftpconversions | cut -d: -f5 |
+ awk '$1 !~ /^$/ { print $1 }' | sort -u
+ fi
+}
+
+list_dyn_libs()
+{
+ # Produce a list of the required dynamic libraries.
+ for file in $* /usr/bin/ls `list_conv_cmds`
+ do
+ ldd "$file" 2>/dev/null | cut -d'>' -f2
+ done | sort -u
+}
+
+create_home_dir()
+{
+ if [ "$home_dir" = "/" -o "$home_dir" = "/usr" ]
+ then
+ fmt=`gettext "%s: Error: Installing FTP in %s is not permitted"`
+ printf "$fmt\n" "$cmd" "$home_dir" >&2
+ exit 1
+ fi
+
+ if [ ! -d "$home_dir" ]
+ then
+ if [ -e "$home_dir" ]
+ then
+ fmt=`gettext "%s: Error: %s already exists but is not a directory"`
+ printf "$fmt\n" "$cmd" "$home_dir" >&2
+ exit 2
+ else
+ fmt=`gettext "Creating directory %s"`
+ printf "$fmt\n" "$home_dir"
+ make_dir "" 755 root:sys
+ fi
+ fi
+}
+
+install_slash_etc()
+{
+ # Preserve an existing etc directory.
+ make_dir etc 111 root:sys
+ make_dir etc/ftpd 111 root:sys
+
+ # Create a stripped down password file.
+ rm -f "$home_dir/etc/passwd"
+ awk -F: '$1 ~ /^root$|^bin$|^sys$|^ftpadm$|^ftp$/ { print $1":x:"$3":"$4":::" }' </etc/passwd >"$home_dir/etc/passwd"
+ chmod 444 "$home_dir/etc/passwd"
+ chown root:sys "$home_dir/etc/passwd"
+
+ # Create a stripped down group file.
+ rm -f "$home_dir/etc/group"
+ awk -F: '$1 ~ /^root$|^other$|^bin$|^sys$|^ftpadm$/ { print $1"::"$3":" }' </etc/group >"$home_dir/etc/group"
+ chmod 444 "$home_dir/etc/group"
+ chown root:sys "$home_dir/etc/group"
+
+ # Copy in /etc/default/init, needed for timezone.
+ if [ -f /etc/default/init ]
+ then
+ make_dir etc/default 111 root:sys
+ copy_file /etc/default/init 444 root:sys
+ fi
+
+ # Copy in files used for hostname resolution
+ copy_file /etc/hosts 444 root:sys
+ copy_file /etc/resolv.conf 444 root:sys
+ make_dir etc/inet 111 root:sys
+ copy_file /etc/inet/ipnodes 444 root:sys
+}
+
+install_slash_usr()
+{
+ # Preserve an existing usr directory.
+ make_dir usr 111 root:sys
+ make_dir usr/bin 111 root:bin
+
+ if [ -h "$home_dir/bin" ]
+ then
+ rm -f "$home_dir/bin"
+ fi
+ if [ ! -e "$home_dir/bin" ]
+ then
+ ln -s ./usr/bin "$home_dir/bin" || exit 2
+ chown -h root:bin "$home_dir/bin"
+ fi
+
+ # Copy required dynamic libraries and PAM session management modules.
+ libs="/lib/nss_files.so.1 /lib/nss_dns.so.1 /lib/libresolv.so.2"
+ for lib in /lib/ld.so.1 $libs `list_dyn_libs $libs` `list_pam_session`
+ do
+ if [ -f "$lib" ]
+ then
+ dir=`dirname "$home_dir$lib"`
+ if [ ! -d "$dir" ]
+ then
+ mkdir -p "$dir" || exit 2
+ fi
+ copy_file "$lib" 555 root:bin
+ fi
+ done
+
+ # Copy required commands.
+ for prog in /usr/bin/ls `list_conv_cmds`
+ do
+ if [ -f "$prog" ]
+ then
+ dir=`dirname "$home_dir$prog"`
+ if [ ! -d "$dir" ]
+ then
+ mkdir -p "$dir" || exit 2
+ fi
+ copy_file "$prog" 111 root:bin
+ fi
+ done
+
+ # Copy timezone files.
+ if [ -d /usr/share/lib/zoneinfo ]
+ then
+ rm -rf "$home_dir/usr/share/lib/zoneinfo"
+ find /usr/share/lib/zoneinfo | cpio -pduL "$home_dir" >/dev/null 2>&1
+ (cd "$home_dir/usr/share/lib"; find zoneinfo -type f |
+ xargs chmod 444)
+ rm -rf "$home_dir/usr/share/lib/zoneinfo/src"
+ fi
+
+ for dir in usr lib platform
+ do
+ if [ -d "$home_dir/$dir" ]
+ then
+ (cd "$home_dir"; find $dir -type d | xargs chmod 111)
+ (cd "$home_dir"; find $dir -type d | xargs chown root:bin)
+ [ $dir != "lib" ] && chown root:sys "$home_dir/$dir"
+ fi
+ done
+}
+
+install_slash_dev()
+{
+ # Preserve an existing dev directory.
+ make_dir dev 111 root:sys
+
+ # Copy devices.
+ for devname in conslog null udp udp6 zero
+ do
+ rm -f "$home_dir/dev/$devname"
+ done
+ cpio -pduL "$home_dir" >/dev/null 2>&1 <<-EOF
+ /dev/conslog
+ /dev/null
+ /dev/udp
+ /dev/udp6
+ /dev/zero
+ EOF
+ if [ $? -ne 0 ]
+ then
+ fmt=`gettext "%s: Error: Creation of devices in %s failed"`
+ printf "$fmt\n" "$cmd" "$home_dir/dev" >&2
+ exit 2
+ fi
+}
+
+install_slash_pub()
+{
+ # Preserve an existing pub directory.
+ make_dir pub 755 root:sys
+}
+
+update_home_dir()
+{
+ fmt=`gettext "Updating directory %s"`
+ printf "$fmt\n" "$home_dir"
+ install_slash_dev
+ install_slash_etc
+ install_slash_usr
+ install_slash_pub
+}
+
+# Execution starts here.
+
+IFS="
+"
+SHELL=/usr/bin/ksh
+PATH=/usr/bin
+TEXTDOMAIN=SUNW_OST_OSCMD
+export SHELL PATH IFS TEXTDOMAIN
+
+cmd=`basename "$0"`
+username=ftp
+login_shell=/bin/true
+
+verify_root
+
+while getopts d arg
+do
+ case $arg in
+ d) directory_only=1;;
+ \?) usage;;
+ esac
+done
+shift `expr $OPTIND - 1`
+
+# Check arguments.
+[ $# -gt 1 ] && usage
+
+home_dir="$1"
+if [ -n "$directory_only" -a -z "$home_dir" ]
+then
+ fmt=`gettext "%s: Error: ftpdir required with -d option"`
+ printf "$fmt\n" "$cmd" >&2
+ usage
+fi
+
+if [ -n "$home_dir" ]
+then
+ echo "$home_dir" | grep "^/" >/dev/null 2>&1
+ if [ $? -ne 0 ]
+ then
+ fmt=`gettext "%s: Error: ftpdir must be an absolute pathname"`
+ printf "$fmt\n" "$cmd" >&2
+ usage
+ fi
+fi
+
+# Ignore certain signals.
+trap '' 1 2 3 15
+
+umask 022
+[ -z "$directory_only" ] && add_user
+
+create_home_dir
+update_home_dir
+
+exit 0
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpconversions b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpconversions
new file mode 100644
index 0000000000..aaecee8f5e
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpconversions
@@ -0,0 +1,14 @@
+# ident "%Z%%M% %I% %E% SMI"
+#
+# FTP server conversions database, see ftpconversions(4).
+#
+ :.Z: : :/usr/bin/compress -cd -- %s:T_REG|T_ASCII:O_UNCOMPRESS:UNCOMPRESS
+ :.gz: : :/usr/bin/gzip -cd -- %s:T_REG|T_ASCII:O_UNCOMPRESS:GUNZIP
+ :.bz2: : :/usr/bin/bzip2 -cd -- %s:T_REG|T_ASCII:O_UNCOMPRESS:BUNZIP2
+ : : :.Z:/usr/bin/compress -c -- %s:T_REG:O_COMPRESS:COMPRESS
+ : : :.gz:/usr/bin/gzip -c -- %s:T_REG:O_COMPRESS:GZIP
+ : : :.bz2:/usr/bin/bzip2 -c -- %s:T_REG:O_COMPRESS:BZIP2
+ : : :.tar:/usr/bin/tar -cf - %s:T_REG|T_DIR:O_TAR:TAR
+ : : :.tar.Z:/usr/sfw/bin/gtar --use-compress-program=/usr/bin/compress -cf - -- %s:T_REG|T_DIR:O_COMPRESS|O_TAR:TAR+COMPRESS
+ : : :.tar.gz:/usr/sfw/bin/gtar --use-compress-program=/usr/bin/gzip -cf - -- %s:T_REG|T_DIR:O_COMPRESS|O_TAR:TAR+GZIP
+ : : :.tar.bz2:/usr/sfw/bin/gtar --use-compress-program=/usr/bin/bzip2 -cf - -- %s:T_REG|T_DIR:O_COMPRESS|O_TAR:TAR+BZIP2
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpcount.c b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpcount.c
new file mode 100644
index 0000000000..129d3f6090
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpcount.c
@@ -0,0 +1,577 @@
+/*
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/****************************************************************************
+
+ Copyright (c) 1999,2000 WU-FTPD Development Group.
+ All rights reserved.
+
+ Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994
+ The Regents of the University of California.
+ Portions Copyright (c) 1993, 1994 Washington University in Saint Louis.
+ Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc.
+ Portions Copyright (c) 1989 Massachusetts Institute of Technology.
+ Portions Copyright (c) 1998 Sendmail, Inc.
+ Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P. Allman.
+ Portions Copyright (c) 1997 by Stan Barber.
+ Portions Copyright (c) 1997 by Kent Landfield.
+ Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ Free Software Foundation, Inc.
+
+ Use and distribution of this software and its source code are governed
+ by the terms and conditions of the WU-FTPD Software License ("LICENSE").
+
+ If you did not receive a copy of the license, it may be obtained online
+ at http://www.wu-ftpd.org/license.html.
+
+ $Id: ftpcount.c,v 1.22 2000/07/01 18:17:39 wuftpd Exp $
+
+****************************************************************************/
+#include "config.h"
+
+#ifdef TSOL
+#include <tsol/priv.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#ifdef HAVE_SYS_SYSLOG_H
+#include <sys/syslog.h>
+#endif
+#if defined(HAVE_SYSLOG_H) || (!defined(AUTOCONF) && !defined(HAVE_SYS_SYSLOG_H))
+#include <syslog.h>
+#endif
+#include <signal.h>
+#include <time.h>
+#include <ctype.h>
+#include <limits.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/param.h>
+
+#ifdef HAVE_PATHS_H
+#include <paths.h>
+#endif
+
+#if defined(VIRTUAL) && defined(INET6)
+#include <netinet/in.h>
+#endif
+
+#include "pathnames.h"
+#include "extensions.h"
+
+#if defined(HAVE_FCNTL_H)
+#include <fcntl.h>
+#endif
+
+#ifdef VIRTUAL
+#define ARGS "Vv"
+#else
+#define ARGS "V"
+#endif
+
+struct c_list {
+ char *class;
+ struct c_list *next;
+};
+
+#ifdef VIRTUAL
+extern int read_servers_line(FILE *, char *, size_t, char *, size_t);
+#endif
+
+void print_copyright(void);
+
+char *progname;
+
+/*************************************************************************/
+/* FUNCTION : parse_time */
+/* PURPOSE : Check a single valid-time-string against the current time */
+/* and return whether or not a match occurs. */
+/* ARGUMENTS : a pointer to the time-string */
+/*************************************************************************/
+
+static int parsetime(char *whattime)
+{
+ static char *days[] =
+ {"Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Wk"};
+ time_t clock;
+ struct tm *curtime;
+ int wday, start, stop, ltime, validday, loop, match;
+
+ (void) time(&clock);
+ curtime = localtime(&clock);
+ wday = curtime->tm_wday;
+ validday = 0;
+ match = 1;
+
+ while (match && isalpha(*whattime) && isupper(*whattime)) {
+ match = 0;
+ for (loop = 0; loop < 8; loop++) {
+ if (strncmp(days[loop], whattime, 2) == 0) {
+ whattime += 2;
+ match = 1;
+ if ((wday == loop) || ((loop == 7) && wday && (wday < 6)))
+ validday = 1;
+ }
+ }
+ }
+
+ if (!validday) {
+ if (strncmp(whattime, "Any", 3) == 0) {
+ validday = 1;
+ whattime += 3;
+ }
+ else
+ return (0);
+ }
+
+ if (sscanf(whattime, "%d-%d", &start, &stop) == 2) {
+ ltime = curtime->tm_min + 100 * curtime->tm_hour;
+ if ((start < stop) && ((ltime >= start) && ltime < stop))
+ return (1);
+ if ((start > stop) && ((ltime >= start) || ltime < stop))
+ return (1);
+ }
+ else
+ return (1);
+
+ return (0);
+}
+
+/*************************************************************************/
+/* FUNCTION : validtime */
+/* PURPOSE : Break apart a set of valid time-strings and pass them to */
+/* parse_time, returning whether or not ANY matches occurred */
+/* ARGUMENTS : a pointer to the time-string */
+/*************************************************************************/
+
+static int validtime(char *ptr)
+{
+ char *nextptr;
+ int good;
+
+ while (1) {
+ nextptr = strchr(ptr, '|');
+ if (strchr(ptr, '|') == NULL)
+ return (parsetime(ptr));
+ *nextptr = '\0';
+ good = parsetime(ptr);
+ *nextptr++ = '|'; /* gotta restore the | or things get skipped! */
+ if (good)
+ return (1);
+ ptr = nextptr;
+ }
+}
+
+static int acl_getlimit(char *aclbuf, char *class)
+{
+ char *crptr, *ptr, linebuf[1024];
+ int limit;
+
+ while (*aclbuf != '\0') {
+ if (strncasecmp(aclbuf, "limit", 5) == 0) {
+ for (crptr = aclbuf; *crptr++ != '\n';);
+ *--crptr = '\0';
+ (void) strlcpy(linebuf, aclbuf, sizeof(linebuf));
+ *crptr = '\n';
+ (void) strtok(linebuf, " \t"); /* returns "limit" */
+ if ((ptr = strtok(NULL, " \t")) && (strcmp(class, ptr) == 0)) {
+ if ((ptr = strtok(NULL, " \t"))) {
+ limit = atoi(ptr); /* returns limit <n> */
+ if ((ptr = strtok(NULL, " \t")) && validtime(ptr))
+ return (limit);
+ }
+ }
+ }
+ while (*aclbuf && *aclbuf++ != '\n');
+ }
+
+ return (-1);
+}
+
+/*************************************************************************/
+/* FUNCTION : lock_fd */
+/* PURPOSE : Lock a file. */
+/* ARGUMENTS : File descriptor of file to lock. */
+/*************************************************************************/
+
+static void lock_fd(int fd)
+{
+#ifndef HAVE_FLOCK
+ struct flock arg;
+#endif
+
+#ifdef HAVE_FLOCK
+ while (flock(fd, LOCK_SH)) {
+#ifndef NO_PID_SLEEP_MSGS
+ syslog(LOG_ERR, "sleeping: flock of pid file failed: %m");
+#endif
+#else
+ arg.l_type = F_RDLCK;
+ arg.l_whence = arg.l_start = arg.l_len = 0;
+#ifdef TSOL
+ if (set_effective_priv(PRIV_ON, 1, PRIV_FILE_LOCK) != 0) {
+ syslog(LOG_ERR, "Cannot add PRIV_FILE_LOCK to eff. priv. set");
+ }
+#endif
+ while (-1 == fcntl(fd, F_SETLK, &arg)) {
+#ifndef NO_PID_SLEEP_MSGS
+ syslog(LOG_ERR, "sleeping: fcntl lock of pid file failed: %m");
+#endif
+#endif /* HAVE_FLOCK */
+ sleep(1);
+ }
+#ifndef HAVE_FLOCK
+#ifdef TSOL
+ if (set_effective_priv(PRIV_OFF, 1, PRIV_FILE_LOCK) != 0) {
+ syslog(LOG_ERR, "Cannot remove PRIV_FILE_LOCK from eff. priv. set");
+ }
+#endif
+#endif /* HAVE_FLOCK */
+}
+
+/*************************************************************************/
+/* FUNCTION : unlock_fd */
+/* PURPOSE : Unlock a file locked by lock_fd. */
+/* ARGUMENTS : File descriptor of file to unlock. */
+/*************************************************************************/
+
+static void unlock_fd(int fd)
+{
+#ifndef HAVE_FLOCK
+ struct flock arg;
+#endif
+
+#ifdef HAVE_FLOCK
+ flock(fd, LOCK_UN);
+#else
+ arg.l_type = F_UNLCK;
+ arg.l_whence = arg.l_start = arg.l_len = 0;
+#ifdef TSOL
+ if (set_effective_priv(PRIV_ON, 1, PRIV_FILE_LOCK) != 0) {
+ syslog(LOG_ERR, "Cannot add PRIV_FILE_LOCK to eff. priv. set");
+ }
+#endif
+ fcntl(fd, F_SETLK, &arg);
+#ifdef TSOL
+ if (set_effective_priv(PRIV_OFF, 1, PRIV_FILE_LOCK) != 0) {
+ syslog(LOG_ERR, "Cannot remove PRIV_FILE_LOCK from eff. priv. set");
+ }
+#endif
+#endif /* HAVE_FLOCK */
+}
+
+static int acl_countusers(char *class)
+{
+ int i, j, n, count, pidfd;
+ pid_t procid;
+ char pidfile[MAXPATHLEN];
+ char line[1024];
+ FILE *ZeFile;
+ struct pidfile_header hdr;
+ struct stat pinfo;
+ unsigned char bits, *buf;
+
+ snprintf(pidfile, sizeof(pidfile), _PATH_PIDNAMES, class);
+ pidfd = open(pidfile, O_RDONLY);
+ if (pidfd == -1) {
+ return (0);
+ }
+
+ lock_fd(pidfd);
+ if (read(pidfd, (void *)&hdr, sizeof(hdr)) != sizeof(hdr)) {
+ unlock_fd(pidfd);
+ close(pidfd);
+ return (0);
+ }
+ if (strcmp(progname, "ftpcount") == 0) {
+ unlock_fd(pidfd);
+ close(pidfd);
+ return (hdr.count);
+ }
+
+ /*
+ * Printing the process information can take a long time, and while we
+ * hold the lock no users can join or leave this class. To minimize the
+ * problem, read the whole PID file into memory then release the lock.
+ */
+ if (fstat(pidfd, &pinfo) != 0) {
+ unlock_fd(pidfd);
+ close(pidfd);
+ return (0);
+ }
+ if ((buf = malloc((size_t)pinfo.st_size)) == NULL) {
+ unlock_fd(pidfd);
+ close(pidfd);
+ return (0);
+ }
+ n = read(pidfd, buf, (size_t)pinfo.st_size);
+ unlock_fd(pidfd);
+ close(pidfd);
+ count = 0;
+ procid = 0;
+ for (i = 0; i < n; i++) {
+ if (buf[i] == 0) {
+ procid += CHAR_BIT;
+ }
+ else {
+ bits = 1;
+ for (j = 0; j < CHAR_BIT; j++) {
+ if (((buf[i] & bits) != 0) &&
+ ((kill(procid, 0) == 0) || (errno == EPERM))) {
+#if defined(SVR4)
+#ifdef AIX
+ snprintf(line, sizeof(line), "/bin/ps %d", procid);
+#elif defined(sun)
+ snprintf(line, sizeof(line), "/usr/ucb/ps auxww %ld", procid);
+#else
+#if defined (LINUX_BUT_NOT_REDHAT_6_0)
+ snprintf(line, sizeof(line), "/bin/ps axwww %d", procid);
+#else
+ snprintf(line, sizeof(line), "/bin/ps -f -p %d", procid);
+#endif
+#endif
+#elif defined(M_UNIX)
+ snprintf(line, sizeof(line), "/bin/ps -f -p %d", procid);
+#else
+ snprintf(line, sizeof(line), "/bin/ps %d", procid);
+#endif
+ ZeFile = popen(line, "r");
+ fgets(line, sizeof(line), ZeFile);
+ line[0] = '\0';
+ fgets(line, sizeof(line), ZeFile);
+ if (line[0] != '\0') {
+ size_t i;
+ for (i = strlen(line); (i > 0) && ((line[i - 1] == ' ') || (line[i - 1] == '\n')); --i)
+ line[i - 1] = '\0';
+ printf("%s\n", line);
+ count++;
+ }
+ pclose(ZeFile);
+ }
+ bits <<= 1;
+ procid++;
+ }
+ }
+ }
+ free(buf);
+ return (count);
+}
+
+static void new_list(struct c_list **list)
+{
+ struct c_list *cp, *tcp;
+
+ if (*list == NULL) {
+ *list = (struct c_list *) malloc(sizeof(struct c_list));
+ if (*list == NULL) {
+ perror("malloc error in new_list");
+ exit(1);
+ }
+ }
+ else {
+ cp = (*list)->next;
+ while (cp) {
+ if (cp->class)
+ free(cp->class);
+ tcp = cp;
+ cp = cp->next;
+ free(tcp);
+ }
+ }
+ (*list)->next = NULL;
+}
+
+static int add_list(char *class, struct c_list **list)
+{
+ struct c_list *cp;
+
+ for (cp = (*list)->next; cp; cp = cp->next) {
+ if (!strcmp(cp->class, class))
+ return (-1);
+ }
+
+ cp = (struct c_list *) malloc(sizeof(struct c_list));
+ if (cp == NULL) {
+ perror("malloc error in add_list");
+ exit(1);
+ }
+
+ cp->class = strdup(class);
+ if (cp->class == NULL) {
+ perror("malloc error in add_list");
+ exit(1);
+ }
+ cp->next = (*list)->next;
+ (*list)->next = cp;
+ return (1);
+}
+
+static int display_info(char *ftpaccess, char *address)
+{
+ FILE *accessfile;
+ char class[80], linebuf[1024], *aclbuf, *myaclbuf, *crptr;
+ int limit;
+ struct stat finfo;
+ static struct c_list *list = NULL;
+
+ if ((accessfile = fopen(ftpaccess, "r")) == NULL) {
+ if (errno != ENOENT)
+ fprintf(stderr, "%s: could not open access file %s: %s\n",
+ progname, ftpaccess, strerror(errno));
+ return (1);
+ }
+ if (fstat(fileno(accessfile), &finfo) != 0) {
+ fprintf(stderr, "%s: could not fstat() access file %s: %s\n",
+ progname, ftpaccess, strerror(errno));
+ fclose(accessfile);
+ return (1);
+ }
+
+ if (finfo.st_size == 0) {
+ printf("%s: no service classes defined, no usage count kept\n", progname);
+ fclose(accessfile);
+ return (0);
+ }
+ else {
+ if (!(aclbuf = (char *) malloc((size_t) finfo.st_size + 1))) {
+ fprintf(stderr, "%s: could not malloc aclbuf: %s\n",
+ progname, strerror(errno));
+ fclose(accessfile);
+ return (1);
+ }
+ fread(aclbuf, (size_t) finfo.st_size, 1, accessfile);
+ fclose(accessfile);
+ *(aclbuf + (size_t) finfo.st_size) = '\0';
+ }
+
+ (void) new_list(&list);
+ myaclbuf = aclbuf;
+ while (*myaclbuf != '\0') {
+ if (strncasecmp(myaclbuf, "class", 5) == 0) {
+ for (crptr = myaclbuf; *crptr++ != '\n';);
+ *--crptr = '\0';
+ (void) strlcpy(linebuf, myaclbuf, sizeof(linebuf));
+ *crptr = '\n';
+ (void) strtok(linebuf, " \t"); /* returns "class" */
+ /* returns class name */
+ (void) strlcpy(class, strtok(NULL, " \t"), sizeof(class));
+ if ((add_list(class, &list)) < 0) {
+ /* we have a class with multiple "class..." lines so, only
+ * display one count... */
+ ;
+ }
+ else {
+ limit = acl_getlimit(myaclbuf, class);
+#ifdef VIRTUAL
+ if (address != NULL)
+ printf("%s ", address);
+#endif
+ if (strcmp(progname, "ftpcount")) {
+ printf("Service class %s: \n", class);
+ printf(" - %3d users ", acl_countusers(class));
+ }
+ else {
+ printf("Service class %-20.20s - %3d users ",
+ class, acl_countusers(class));
+ }
+ if (limit == -1)
+ printf("(no maximum)\n");
+ else
+ printf("(%3d maximum)\n", limit);
+ }
+ }
+ while (*myaclbuf && *myaclbuf++ != '\n');
+ }
+ free(aclbuf);
+ return (0);
+}
+
+int main(int argc, char **argv)
+{
+ int c, exitval;
+ int virtual = 0;
+#ifdef VIRTUAL
+ FILE *svrfp;
+ char *sp;
+ struct stat st;
+ char configdir[MAXPATHLEN];
+ char accesspath[MAXPATHLEN];
+#ifdef INET6
+ char hostaddress[INET6_ADDRSTRLEN];
+#else
+ char hostaddress[32];
+#endif
+#endif
+
+#ifdef TSOL
+/* Before anything, clear the effective privilege set */
+
+ if (set_effective_priv(PRIV_SET, 0) != 0) {
+ syslog(LOG_ERR, "ftp[count|who] cannot clear effective privileges!");
+ exit(1);
+ }
+#endif
+
+ if ((progname = strrchr(argv[0], '/')))
+ ++progname;
+ else
+ progname = argv[0];
+
+ if (argc > 1) {
+ while ((c = getopt(argc, argv, ARGS)) != EOF) {
+ switch (c) {
+ case 'V':
+ print_copyright();
+ exit(0);
+#ifdef VIRTUAL
+ case 'v':
+ virtual = 1;
+ break;
+#endif
+ default:
+ fprintf(stderr, "usage: %s [-" ARGS "]\n", progname);
+ exit(1);
+ }
+ }
+ }
+
+ exitval = 0;
+ if ((virtual == 0) && (display_info(_PATH_FTPACCESS, NULL) != 0))
+ exitval = 1;
+
+#ifdef VIRTUAL
+ /*
+ * Deal with the ftpaccess files at the virtual domain directory locations
+ * specified in the ftpservers file.
+ */
+ if (virtual && ((svrfp = fopen(_PATH_FTPSERVERS, "r")) != NULL)) {
+ while (read_servers_line(svrfp, hostaddress, sizeof(hostaddress),
+ configdir, sizeof(configdir)) == 1) {
+ /* get rid of any trailing slash */
+ sp = configdir + (strlen(configdir) - 1);
+ if (*sp == '/')
+ *sp = '\0';
+
+ /* check to see that a valid directory value was supplied */
+ if ((stat(configdir, &st) == 0) &&
+ ((st.st_mode & S_IFMT) == S_IFDIR)) {
+ snprintf(accesspath, sizeof(accesspath), "%s/ftpaccess",
+ configdir);
+ if (display_info(accesspath, hostaddress) != 0)
+ exitval = 1;
+ }
+ }
+ fclose(svrfp);
+ }
+#endif
+ return (exitval);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpd.c b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpd.c
new file mode 100644
index 0000000000..944bf1269a
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpd.c
@@ -0,0 +1,7965 @@
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/****************************************************************************
+
+ Copyright (c) 1999,2000,2001 WU-FTPD Development Group.
+ All rights reserved.
+
+ Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994
+ The Regents of the University of California.
+ Portions Copyright (c) 1993, 1994 Washington University in Saint Louis.
+ Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc.
+ Portions Copyright (c) 1989 Massachusetts Institute of Technology.
+ Portions Copyright (c) 1998 Sendmail, Inc.
+ Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P. Allman.
+ Portions Copyright (c) 1997 by Stan Barber.
+ Portions Copyright (c) 1997 by Kent Landfield.
+ Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ Free Software Foundation, Inc.
+
+ Use and distribution of this software and its source code are governed
+ by the terms and conditions of the WU-FTPD Software License ("LICENSE").
+
+ If you did not receive a copy of the license, it may be obtained online
+ at http://www.wu-ftpd.org/license.html.
+
+ $Id: ftpd.c,v 1.111 2000/07/01 18:17:39 wuftpd Exp $
+
+****************************************************************************/
+/* FTP server. */
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/file.h>
+#include <sys/wait.h>
+
+#ifdef AIX
+#include <sys/id.h>
+#include <sys/priv.h>
+#include <netinet/if_ether.h>
+#include <net/if_dl.h>
+#endif
+
+#ifdef AUX
+#include <compat.h>
+#endif
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+
+#define FTP_NAMES
+#include <arpa/ftp.h>
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <pwd.h>
+#include <grp.h>
+#include <setjmp.h>
+#include <errno.h>
+#include <string.h>
+#ifdef INTERNAL_LS
+#ifdef HAVE_GLOB_H
+#include <glob.h>
+#else
+#include <wuftpd_glob.h>
+#endif
+#endif
+#ifdef HAVE_GRP_H
+#include <grp.h>
+#endif
+#include <sys/stat.h>
+
+#define VA_LOCAL_DECL va_list ap;
+#define VA_START(f) va_start(ap, f)
+#define VA_END va_end(ap)
+
+#include "proto.h"
+
+#ifdef HAVE_UFS_QUOTA_H
+#include <ufs/quota.h>
+#endif
+#ifdef HAVE_SYS_FS_UFS_QUOTA_H
+#include <sys/fs/ufs_quota.h>
+#endif
+
+#ifdef HAVE_SYS_SYSLOG_H
+#include <sys/syslog.h>
+#endif
+#if defined(HAVE_SYSLOG_H) || (!defined(AUTOCONF) && !defined(HAVE_SYS_SYSLOG_H))
+#include <syslog.h>
+#endif
+#ifdef TIME_WITH_SYS_TIME
+#include <time.h>
+#include <sys/time.h>
+#else
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#else
+#include <time.h>
+#endif
+#endif
+
+#ifdef HAVE_SYS_SENDFILE_H
+#include <sys/sendfile.h>
+#endif
+
+#include "conversions.h"
+#include "extensions.h"
+
+#ifdef SHADOW_PASSWORD
+#include <shadow.h>
+#endif
+
+#include "pathnames.h"
+
+#ifdef M_UNIX
+#include <arpa/nameser.h>
+#include <resolv.h>
+#endif
+
+#if defined(HAVE_FCNTL_H)
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_SYSINFO
+#include <sys/systeminfo.h>
+#endif
+
+#ifdef KERBEROS
+#include <sys/types.h>
+#include <auth.h>
+#include <krb.h>
+#endif
+
+#ifdef ULTRIX_AUTH
+#include <auth.h>
+#include <sys/svcinfo.h>
+#endif
+
+#ifndef HAVE_LSTAT
+#define lstat stat
+#endif
+
+#ifdef AFS_AUTH
+#include <afs/stds.h>
+#include <afs/kautils.h>
+#endif
+
+#ifdef DCE_AUTH
+#include <dce/rpc.h>
+#include <dce/sec_login.h>
+#include <dce/dce_error.h>
+#endif
+
+
+#ifdef HAVE_DIRENT_H
+#include <dirent.h>
+#else
+#include <sys/dir.h>
+#endif
+
+#if defined(USE_LONGJMP)
+#define wu_longjmp(x, y) longjmp((x), (y))
+#define wu_setjmp(x) setjmp(x)
+#ifndef JMP_BUF
+#define JMP_BUF jmp_buf
+#endif
+#else
+#define wu_longjmp(x, y) siglongjmp((x), (y))
+#define wu_setjmp(x) sigsetjmp((x), 1)
+#ifndef JMP_BUF
+#define JMP_BUF sigjmp_buf
+#endif
+#endif
+
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 64 /* may be too big */
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE !TRUE
+#endif
+
+#ifdef MAIL_ADMIN
+#define MAILSERVERS 10
+#define INCMAILS 10
+int mailservers = 0;
+char *mailserver[MAILSERVERS];
+int incmails = 0;
+char *incmail[INCMAILS];
+char *mailfrom;
+char *email(char *full_address);
+FILE *SockOpen(char *host, int clientPort);
+char *SockGets(FILE *sockfp, char *buf, int len);
+int SockWrite(char *buf, int size, int nels, FILE *sockfp);
+int SockPrintf(FILE *sockfp, char *format,...);
+int SockPuts(FILE *sockfp, char *buf);
+int Reply(FILE *sockfp);
+int Send(FILE *sockfp, char *format,...);
+#endif /* MAIL_ADMIN */
+
+#if defined(_SCO_DS) && !defined(SIGURG)
+#define SIGURG SIGUSR1
+#endif
+
+/* File containing login names NOT to be used on this machine. Commonly used
+ * to disallow uucp. */
+extern int errno;
+
+extern char *ctime(const time_t *);
+#ifndef NO_CRYPT_PROTO
+extern char *crypt(const char *, const char *);
+#endif
+
+extern char version[];
+extern char *home; /* pointer to home directory for glob */
+extern char cbuf[];
+extern off_t restart_point;
+extern int yyerrorcalled;
+
+struct SOCKSTORAGE ctrl_addr;
+struct SOCKSTORAGE data_source;
+struct SOCKSTORAGE data_dest;
+struct SOCKSTORAGE his_addr;
+struct SOCKSTORAGE pasv_addr;
+struct SOCKSTORAGE vect_addr;
+int route_vectored = 0;
+int passive_port_min = 1024;
+int passive_port_max = 65535;
+int restricted_user = 0;
+unsigned short data_port = 0;
+
+#ifdef INET6
+int ctrl_v4mapped = 0;
+int epsv_all = 0;
+int listen_v4 = 0; /* when set, listen on IPv4 socket in standalone mode */
+#endif
+
+#ifdef VIRTUAL
+char virtual_root[MAXPATHLEN];
+char virtual_banner[MAXPATHLEN];
+char virtual_email[MAXPATHLEN];
+
+char virtual_hostname[MAXHOSTNAMELEN];
+char virtual_address[MAXHOSTNAMELEN];
+
+extern int virtual_mode;
+extern int virtual_ftpaccess;
+#endif
+
+#ifdef QUOTA
+extern struct dqblk quota;
+#endif
+
+#if defined(USE_GSS)
+#include "gssutil.h"
+
+extern gss_info_t gss_info;
+
+int allow_ccc = 0;
+int ccc_ok = 0;
+extern char *cur_auth_type;
+#endif /* USE_GSS */
+
+int data;
+jmp_buf errcatch;
+JMP_BUF urgcatch;
+int logged_in = 0;
+struct passwd *pw;
+char chroot_path[MAXPATHLEN];
+int debug = 0;
+int disable_rfc931 = 0;
+extern unsigned int timeout_idle;
+extern unsigned int timeout_maxidle;
+extern unsigned int timeout_data;
+extern unsigned int timeout_accept;
+extern unsigned int timeout_connect;
+
+/* previously defaulted to 1, and -l or -L set them to 1, so that there was
+ no way to turn them *off*! Changed so that the manpage reflects common
+ sense. -L is way noisy; -l we'll change to be "just right". _H */
+int logging = 0;
+int log_commands = 0;
+int log_security = 0;
+int syslogmsg = 0;
+static int wtmp_logging = 1;
+
+#ifdef SECUREOSF
+#define SecureWare /* Does this mean it works for all SecureWare? */
+#endif
+
+#ifdef HPUX_10_TRUSTED
+#include <hpsecurity.h>
+#endif
+
+#if defined(SecureWare) || defined(HPUX_10_TRUSTED)
+#include <prot.h>
+#endif
+
+int anonymous = 1;
+int guest;
+int type;
+int form;
+int stru; /* avoid C keyword */
+int mode;
+int usedefault = 1; /* for data transfers */
+int pdata = -1; /* for passive mode */
+int transflag;
+int ftwflag;
+off_t file_size;
+off_t byte_count;
+int TCPwindowsize = 0; /* 0 = use system default */
+size_t sendbufsz; /* buffer size to use when sending data */
+size_t recvbufsz; /* buffer size to use when receiving data */
+
+#ifdef TRANSFER_COUNT
+off_t data_count_total = 0; /* total number of data bytes */
+off_t data_count_in = 0;
+off_t data_count_out = 0;
+off_t byte_count_total = 0; /* total number of general traffic */
+off_t byte_count_in = 0;
+off_t byte_count_out = 0;
+int file_count_total = 0; /* total number of data files */
+int file_count_in = 0;
+int file_count_out = 0;
+int xfer_count_total = 0; /* total number of transfers */
+int xfer_count_in = 0;
+int xfer_count_out = 0;
+#ifdef TRANSFER_LIMIT
+int file_limit_raw_in = 0;
+int file_limit_raw_out = 0;
+int file_limit_raw_total = 0;
+int file_limit_data_in = 0;
+int file_limit_data_out = 0;
+int file_limit_data_total = 0;
+off_t data_limit_raw_in = 0;
+off_t data_limit_raw_out = 0;
+off_t data_limit_raw_total = 0;
+off_t data_limit_data_in = 0;
+off_t data_limit_data_out = 0;
+off_t data_limit_data_total = 0;
+#ifdef RATIO /* 1998/08/04 K.Wakui */
+#define TRUNC_KB(n) ((n)/1024+(((n)%1024)?1:0))
+off_t total_free_dl = 0;
+int upload_download_rate = 0;
+int freefile;
+int is_downloadfree( char * );
+#endif /* RATIO */
+#endif
+#endif
+
+int retrieve_is_data = 1; /* !0=data, 0=general traffic -- for 'ls' */
+char LastFileTransferred[MAXPATHLEN] = "";
+
+static char *RootDirectory = NULL;
+
+#if !defined(CMASK) || CMASK == 0
+#undef CMASK
+#define CMASK 022
+#endif
+mode_t defumask = CMASK; /* default umask value */
+#ifdef ALTERNATE_CD
+char defhome[] = "/";
+#endif
+char tmpline[7];
+char hostname[MAXHOSTNAMELEN];
+char remotehost[MAXHOSTNAMELEN];
+char remoteaddr[MAXHOSTNAMELEN];
+char *remoteident = "[nowhere yet]";
+int rhlookup = TRUE; /* when TRUE lookup the remote hosts name */
+
+/* log failures 27-apr-93 ehk/bm */
+#define MAXUSERNAMELEN 256
+char the_user[MAXUSERNAMELEN];
+
+/* Access control and logging passwords */
+/* OFF by default. _H */
+int use_accessfile = 0;
+char guestpw[MAXHOSTNAMELEN];
+char privatepw[MAXHOSTNAMELEN];
+int nameserved = 0;
+extern char authuser[];
+extern int authenticated;
+extern int keepalive;
+
+/* File transfer logging (xferlog) */
+int xferlog = 0;
+int log_outbound_xfers = 0;
+int log_incoming_xfers = 0;
+char logfile[MAXPATHLEN];
+
+/* Allow use of lreply(); this is here since some older FTP clients don't
+ * support continuation messages. In violation of the RFCs... */
+int dolreplies = 1;
+
+/* Spontaneous reply text. To be sent along with next reply to user */
+char *autospout = NULL;
+int autospout_free = 0;
+
+/* allowed on-the-fly file manipulations (compress, tar) */
+int mangleopts = 0;
+
+/* number of login failures before attempts are logged and FTP *EXITS* */
+int lgi_failure_threshold = 5;
+
+/* Timeout intervals for retrying connections to hosts that don't accept PORT
+ * cmds. This is a kludge, but given the problems with TCP... */
+#define SWAITMAX 90 /* wait at most 90 seconds */
+#define SWAITINT 5 /* interval between retries */
+
+int swaitmax = SWAITMAX;
+int swaitint = SWAITINT;
+
+SIGNAL_TYPE lostconn(int sig);
+SIGNAL_TYPE randomsig(int sig);
+SIGNAL_TYPE myoob(int sig);
+FILE *getdatasock(char *mode);
+FILE *dataconn(char *name, off_t size, char *mode);
+void setproctitle(const char *fmt,...);
+void initsetproctitle(int, char **, char **);
+void reply(int, char *fmt,...);
+void lreply(int, char *fmt,...);
+
+#ifndef HAVE_VSNPRINTF
+extern int vsnprintf(char *, size_t, const char *, va_list);
+#endif
+
+#ifndef HAVE_SNPRINTF
+extern int snprintf(char *, size_t, const char *,...);
+#endif
+
+#ifdef NEED_SIGFIX
+extern sigset_t block_sigmask; /* defined in sigfix.c */
+#endif
+
+char proctitle[BUFSIZ]; /* initial part of title */
+
+#if defined(SKEY) && defined(OPIE)
+#error YOU SHOULD NOT HAVE BOTH SKEY AND OPIE DEFINED!!!!!
+#endif
+
+#ifdef SKEY
+#include <skey.h>
+int pwok = 0;
+#endif
+
+#ifdef OPIE
+#include <opie.h>
+int pwok = 0;
+int af_pwok = 0;
+struct opie opiestate;
+#endif
+
+#ifdef KERBEROS
+void init_krb();
+void end_krb();
+char krb_ticket_name[100];
+#endif /* KERBEROS */
+
+#ifdef ULTRIX_AUTH
+int ultrix_check_pass(char *passwd, char *xpasswd);
+#endif
+
+#ifdef USE_PAM
+#if defined(ULTRIX_AUTH) || defined(SECUREOSF) || defined(KERBEROS) || defined(SKEY) || defined (OPIE) || defined (BSD_AUTH)
+#error No other auth methods are allowed with PAM.
+#endif
+#include <security/pam_appl.h>
+static int pam_check_pass(char *user, char *passwd);
+pam_handle_t *pamh;
+#endif
+
+#ifndef INTERNAL_LS
+/* ls program commands and options for lreplies on and off */
+char ls_long[BUFSIZ * 2];
+char ls_short[BUFSIZ * 2];
+char ls_plain[BUFSIZ * 2];
+#endif
+
+#define FTPD_OPTS ":4aAdiIlLoP:qQr:t:T:u:vVwWxX"
+#if defined(DAEMON)
+# define DAEMON_OPTS "p:sS"
+#else /* !(defined(DAEMON)) */
+# define DAEMON_OPTS
+#endif /* !(defined(DAEMON)) */
+#if defined(USE_GSS)
+# define GSS_OPTS "CK"
+#else /* !(defined(USE_GSS)) */
+# define GSS_OPTS
+#endif /* !(defined(USE_GSS)) */
+
+/* Some systems use one format, some another. This takes care of the garbage */
+#ifndef L_FORMAT /* Autoconf detects this... */
+#if (defined(BSD) && (BSD >= 199103)) && !defined(LONGOFF_T)
+#define L_FORMAT "qd"
+#else
+#ifdef _AIX42
+#define L_FORMAT "lld"
+#else
+#ifdef SOLARIS_2
+#define L_FORMAT "ld"
+#else
+#define L_FORMAT "d"
+#endif
+#endif
+#endif
+#endif
+
+#ifdef DAEMON
+int be_daemon = 0; /* Run standalone? */
+int daemon_port = 0;
+static void do_daemon(void);
+#endif
+int Bypass_PID_Files = 0;
+
+#ifdef OTHER_PASSWD
+#include "getpwnam.h"
+char _path_passwd[MAXPATHLEN];
+#ifdef SHADOW_PASSWORD
+char _path_shadow[MAXPATHLEN];
+#endif
+#endif
+#if defined(USE_PAM) && defined(OTHER_PASSWD)
+int use_pam = 1;
+#else
+int use_pam = 0;
+#endif
+
+void print_copyright(void);
+char *mapping_getcwd(char *path, size_t size);
+
+void dolog(struct SOCKSTORAGE *);
+
+#ifdef THROUGHPUT
+extern void throughput_calc(char *, int *, double *);
+extern void throughput_adjust(char *);
+#endif
+
+time_t login_time;
+time_t limit_time = 0;
+
+int regexmatch(char *name, char *rgexp);
+
+int pasv_allowed(char *remoteaddr);
+int port_allowed(char *remoteaddr);
+
+#if sparc && !__svr4__
+int fclose(FILE *);
+#endif
+
+static SIGNAL_TYPE alarm_signal(int sig)
+{
+}
+
+static FILE *draconian_FILE = NULL;
+
+static SIGNAL_TYPE draconian_alarm_signal(int sig)
+{
+ if (draconian_FILE != NULL) {
+ fclose(draconian_FILE);
+ draconian_FILE = NULL;
+ }
+ (void) signal(SIGALRM, draconian_alarm_signal);
+}
+
+static void socket_flush_wait(FILE *file)
+{
+ static int flushwait = TRUE;
+ static int first_time = TRUE;
+ char c;
+ int set;
+ int fd = fileno(file);
+ int serrno = errno;
+ struct aclmember *entry;
+
+ if (first_time) {
+ entry = NULL;
+ /* flush-wait yes|no [typelist] */
+ while (getaclentry("flush-wait", &entry)) {
+ if (!ARG0)
+ continue;
+ if (strcasecmp(ARG0, "yes") == 0)
+ set = TRUE;
+ else if (strcasecmp(ARG0, "no") == 0)
+ set = FALSE;
+ else
+ continue;
+
+ if (!ARG1)
+ flushwait = set;
+ else if (type_match(ARG1)) {
+ flushwait = set;
+ break;
+ }
+ }
+ first_time = FALSE;
+ }
+ if (flushwait) {
+ if (draconian_FILE != NULL)
+ shutdown(fd, 1);
+ if (draconian_FILE != NULL)
+ read(fd, &c, 1);
+ }
+ errno = serrno;
+/*
+ * GAL - the read() here should be checked to ensure it returned 0 (indicating
+ * EOF) or -1 (an error occurred). Anything else (real data) is a protocol
+ * error.
+ */
+}
+
+static int IPClassOfService(const char *type)
+{
+ int ipcos = -1, value;
+ char *endp;
+ struct aclmember *entry = NULL;
+
+ /* ipcos control|data <value> [<typelist>] */
+ while (getaclentry("ipcos", &entry)) {
+ if (ARG0 && ARG1) {
+ if (strcasecmp(type, ARG0) == 0) {
+ if (!ARG2) {
+ errno = 0;
+ value = (int) strtol(ARG1, &endp, 0);
+ if ((errno == 0) && (value >= 0) && (*endp == '\0'))
+ ipcos = value;
+ }
+ else if (type_match(ARG2)) {
+ errno = 0;
+ value = (int) strtol(ARG1, &endp, 0);
+ if ((errno == 0) && (value >= 0) && (*endp == '\0')) {
+ ipcos = value;
+ break;
+ }
+ }
+ }
+ }
+ }
+ return ipcos;
+}
+
+int main(int argc, char **argv, char **envp)
+{
+#if defined(UNIXWARE) || defined(AIX)
+ size_t addrlen;
+#else
+ int addrlen;
+#endif
+ int on = 1;
+ int cos;
+ int c;
+#ifndef INTERNAL_LS
+ int which;
+#endif
+ extern int optopt;
+ extern char *optarg;
+ char *hp;
+ struct aclmember *entry;
+#ifdef VIRTUAL
+#if defined(UNIXWARE) || defined(AIX)
+ size_t virtual_len;
+#else
+ int virtual_len;
+#endif
+ struct SOCKSTORAGE virtual_addr;
+#endif
+ struct servent *serv;
+
+#ifdef AUX
+ setcompat(COMPAT_POSIX | COMPAT_BSDSETUGID);
+#endif
+
+ closelog();
+#ifdef FACILITY
+ openlog("ftpd", LOG_PID | LOG_NDELAY, FACILITY);
+#else
+ openlog("ftpd", LOG_PID);
+#endif
+
+#ifdef SecureWare
+ setluid(1); /* make sure there is a valid luid */
+ set_auth_parameters(argc, argv);
+ setreuid(0, 0);
+#endif
+#if defined(M_UNIX) && !defined(_M_UNIX)
+ res_init(); /* bug in old (1.1.1) resolver */
+ _res.retrans = 20; /* because of fake syslog in 3.2.2 */
+ setlogmask(LOG_UPTO(LOG_INFO));
+#endif
+
+ while ((c = getopt(argc, argv, FTPD_OPTS DAEMON_OPTS GSS_OPTS)) != -1) {
+ switch (c) {
+
+ case '4':
+#ifdef INET6
+ listen_v4 = 1;
+#endif
+ break;
+
+ case 'a':
+ use_accessfile = 1;
+ break;
+
+ case 'A':
+ use_accessfile = 0;
+ break;
+
+ case 'v':
+ debug = 1;
+ break;
+
+ case 'd':
+ debug = 1;
+ break;
+
+#if defined(USE_GSS)
+ case 'C':
+ gss_info.want_creds = 1;
+ break;
+
+ case 'K':
+ gss_info.must_gss_auth = 1;
+ break;
+#endif /* USE_GSS */
+
+ case 'l':
+ logging = 1;
+ break;
+
+ case 'L':
+ log_commands = 3;
+ break;
+
+ case 'i':
+ log_incoming_xfers = 3;
+ break;
+
+ case 'I':
+ disable_rfc931 = 1;
+ break;
+
+ case 'o':
+ log_outbound_xfers = 3;
+ break;
+
+ case 'q':
+ Bypass_PID_Files = 0;
+ break;
+
+ case 'Q':
+ Bypass_PID_Files = 1;
+ break;
+
+ case 'r':
+ if ((optarg != NULL) && (optarg[0] != '\0')) {
+ RootDirectory = malloc(strlen(optarg) + 1);
+ if (RootDirectory != NULL)
+ strcpy(RootDirectory, optarg);
+ }
+ break;
+
+ case 'P':
+ data_port = htons(atoi(optarg));
+ break;
+
+#ifdef DAEMON
+ case 'p':
+ daemon_port = atoi(optarg);
+ break;
+
+ case 's':
+ be_daemon = 1;
+ break;
+
+ case 'S':
+ be_daemon = 2;
+ break;
+#endif /* DAEMON */
+
+ case 't':
+ timeout_idle = atoi(optarg);
+ if (timeout_maxidle < timeout_idle)
+ timeout_maxidle = timeout_idle;
+ break;
+
+ case 'T':
+ timeout_maxidle = atoi(optarg);
+ if (timeout_idle > timeout_maxidle)
+ timeout_idle = timeout_maxidle;
+ break;
+
+ case 'u':
+ {
+ unsigned int val = 0;
+
+ while (*optarg && *optarg >= '0' && *optarg <= '7')
+ val = val * 8 + *optarg++ - '0';
+ if (*optarg || val > 0777)
+ syslog(LOG_ERR, "bad value for -u");
+ else
+ defumask = val;
+ break;
+ }
+
+ case 'V':
+ print_copyright();
+ exit(0);
+ /* NOTREACHED */
+ case 'w':
+ wtmp_logging = 1;
+ break;
+
+ case 'W':
+ wtmp_logging = 0;
+ break;
+
+ case 'x':
+ syslogmsg = 2;
+ break;
+
+ case 'X':
+ syslogmsg = 1;
+ break;
+
+ case ':':
+ syslog(LOG_ERR, "option -%c requires an argument", optopt);
+ break;
+
+ default:
+ syslog(LOG_ERR, "unknown option -%c ignored", optopt);
+ break;
+ }
+ }
+ initsetproctitle(argc, argv, envp);
+ (void) freopen(_PATH_DEVNULL, "w", stderr);
+
+ /* Checking for random signals ... */
+#ifdef NEED_SIGFIX
+ sigemptyset(&block_sigmask);
+#endif
+#ifndef SIG_DEBUG
+#ifdef SIGHUP
+ (void) signal(SIGHUP, randomsig);
+#ifdef NEED_SIGFIX
+ sigaddset(&block_sigmask, SIGHUP);
+#endif
+#endif
+#ifdef SIGINT
+ (void) signal(SIGINT, randomsig);
+#ifdef NEED_SIGFIX
+ sigaddset(&block_sigmask, SIGINT);
+#endif
+#endif
+#ifdef SIGQUIT
+ (void) signal(SIGQUIT, randomsig);
+#ifdef NEED_SIGFIX
+ sigaddset(&block_sigmask, SIGQUIT);
+#endif
+#endif
+#ifdef SIGILL
+ (void) signal(SIGILL, randomsig);
+#ifdef NEED_SIGFIX
+ sigaddset(&block_sigmask, SIGILL);
+#endif
+#endif
+#ifdef SIGTRAP
+ (void) signal(SIGTRAP, randomsig);
+#ifdef NEED_SIGFIX
+ sigaddset(&block_sigmask, SIGTRAP);
+#endif
+#endif
+#ifdef SIGIOT
+ (void) signal(SIGIOT, randomsig);
+#ifdef NEED_SIGFIX
+ sigaddset(&block_sigmask, SIGIOT);
+#endif
+#endif
+#ifdef SIGEMT
+ (void) signal(SIGEMT, randomsig);
+#ifdef NEED_SIGFIX
+ sigaddset(&block_sigmask, SIGEMT);
+#endif
+#endif
+#ifdef SIGFPE
+ (void) signal(SIGFPE, randomsig);
+#ifdef NEED_SIGFIX
+ sigaddset(&block_sigmask, SIGFPE);
+#endif
+#endif
+#ifdef SIGKILL
+ (void) signal(SIGKILL, randomsig);
+#ifdef NEED_SIGFIX
+ sigaddset(&block_sigmask, SIGKILL);
+#endif
+#endif
+#ifdef SIGBUS
+ (void) signal(SIGBUS, randomsig);
+#ifdef NEED_SIGFIX
+ sigaddset(&block_sigmask, SIGBUS);
+#endif
+#endif
+#ifdef SIGSEGV
+ (void) signal(SIGSEGV, randomsig);
+#ifdef NEED_SIGFIX
+ sigaddset(&block_sigmask, SIGSEGV);
+#endif
+#endif
+#ifdef SIGSYS
+ (void) signal(SIGSYS, randomsig);
+#ifdef NEED_SIGFIX
+ sigaddset(&block_sigmask, SIGSYS);
+#endif
+#endif
+#ifdef SIGALRM
+ (void) signal(SIGALRM, randomsig);
+#ifdef NEED_SIGFIX
+ sigaddset(&block_sigmask, SIGALRM);
+#endif
+#endif
+#ifdef SIGSTOP
+ (void) signal(SIGSTOP, randomsig);
+#ifdef NEED_SIGFIX
+ sigaddset(&block_sigmask, SIGSTOP);
+#endif
+#endif
+#ifdef SIGTSTP
+ (void) signal(SIGTSTP, randomsig);
+#ifdef NEED_SIGFIX
+ sigaddset(&block_sigmask, SIGTSTP);
+#endif
+#endif
+#ifdef SIGTTIN
+ (void) signal(SIGTTIN, randomsig);
+#ifdef NEED_SIGFIX
+ sigaddset(&block_sigmask, SIGTTIN);
+#endif
+#endif
+#ifdef SIGTTOU
+ (void) signal(SIGTTOU, randomsig);
+#ifdef NEED_SIGFIX
+ sigaddset(&block_sigmask, SIGTTOU);
+#endif
+#endif
+#ifdef SIGIO
+ (void) signal(SIGIO, randomsig);
+#ifdef NEED_SIGFIX
+ sigaddset(&block_sigmask, SIGIO);
+#endif
+#endif
+#ifdef SIGXCPU
+ (void) signal(SIGXCPU, randomsig);
+#ifdef NEED_SIGFIX
+ sigaddset(&block_sigmask, SIGXCPU);
+#endif
+#endif
+#ifdef SIGXFSZ
+ (void) signal(SIGXFSZ, randomsig);
+#ifdef NEED_SIGFIX
+ sigaddset(&block_sigmask, SIGXFSZ);
+#endif
+#endif
+#ifdef SIGWINCH
+ (void) signal(SIGWINCH, randomsig);
+#ifdef NEED_SIGFIX
+ sigaddset(&block_sigmask, SIGWINCH);
+#endif
+#endif
+#ifdef SIGVTALRM
+ (void) signal(SIGVTALRM, randomsig);
+#ifdef NEED_SIGFIX
+ sigaddset(&block_sigmask, SIGVTALRM);
+#endif
+#endif
+#ifdef SIGPROF
+ (void) signal(SIGPROF, randomsig);
+#ifdef NEED_SIGFIX
+ sigaddset(&block_sigmask, SIGPROF);
+#endif
+#endif
+#ifdef SIGUSR1
+ (void) signal(SIGUSR1, randomsig);
+#ifdef NEED_SIGFIX
+ sigaddset(&block_sigmask, SIGUSR1);
+#endif
+#endif
+#ifdef SIGUSR2
+ (void) signal(SIGUSR2, randomsig);
+#ifdef NEED_SIGFIX
+ sigaddset(&block_sigmask, SIGUSR2);
+#endif
+#endif
+
+#ifdef SIGPIPE
+ (void) signal(SIGPIPE, lostconn);
+#ifdef NEED_SIGFIX
+ sigaddset(&block_sigmask, SIGPIPE);
+#endif
+#endif
+#ifdef SIGCHLD
+ (void) signal(SIGCHLD, SIG_IGN);
+#ifdef NEED_SIGFIX
+ sigaddset(&block_sigmask, SIGCHLD);
+#endif
+#endif
+
+#ifdef SIGURG
+ if (signal(SIGURG, myoob) == SIG_ERR)
+ syslog(LOG_ERR, "signal: %m");
+#ifdef NEED_SIGFIX
+ sigaddset(&block_sigmask, SIGURG);
+#endif
+#endif
+#endif /* SIG_DEBUG */
+
+#ifdef VIRTUAL
+ virtual_root[0] = '\0';
+ virtual_banner[0] = '\0';
+#endif
+
+ setup_paths();
+
+#ifdef OTHER_PASSWD
+ strcpy(_path_passwd, "/etc/passwd");
+#ifdef SHADOW_PASSWORD
+ strcpy(_path_shadow, "/etc/shadow");
+#endif
+#endif
+
+ access_init();
+
+#ifdef DAEMON
+ if (be_daemon != 0)
+ do_daemon();
+ else {
+#endif
+ addrlen = sizeof(his_addr);
+ if (getpeername(0, (struct sockaddr *) &his_addr, &addrlen) < 0) {
+ syslog(LOG_ERR, "getpeername: %m");
+#ifndef DEBUG
+ exit(1);
+#endif
+ }
+#ifdef DAEMON
+ }
+#endif
+ addrlen = sizeof(ctrl_addr);
+ if (getsockname(0, (struct sockaddr *) &ctrl_addr, &addrlen) < 0) {
+ syslog(LOG_ERR, "getsockname: %m");
+#ifndef DEBUG
+ exit(1);
+#endif
+ }
+ /* Sanity check */
+ if ((SOCK_FAMILY(ctrl_addr) != AF_INET)
+#ifdef INET6
+ && (SOCK_FAMILY(ctrl_addr) != AF_INET6)
+#endif
+ ) {
+ syslog(LOG_ERR, "control connection address family (%d) not supported.",
+ SOCK_FAMILY(ctrl_addr));
+#ifndef DEBUG
+ exit(1);
+#endif
+ }
+#ifdef SOLARIS_BSM_AUDIT
+ /* Set audit characteristics */
+ if (audit_settid(0)) {
+ syslog(LOG_ERR, "audit failure");
+ exit(1);
+ }
+#endif
+#ifdef INET6
+ /* IP_TOS is an IPv4 socket option */
+ if (SOCK_FAMILY(ctrl_addr) == AF_INET)
+#endif
+ if ((cos = IPClassOfService("control")) >= 0) {
+ if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *) &cos, sizeof(int)) < 0)
+ syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
+ }
+
+#ifdef TCP_NODELAY
+ /*
+ * Disable Nagle on the control channel so that we don't have to wait
+ * for peer's ACK before issuing our next reply.
+ */
+ if (setsockopt(0, IPPROTO_TCP, TCP_NODELAY, &on, sizeof (on)) < 0)
+ syslog(LOG_WARNING, "control setsockopt TCP_NODELAY: %m");
+#endif
+
+ if (keepalive)
+ if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, (char *) &on, sizeof(on)) < 0)
+ syslog(LOG_ERR, "setsockopt SO_KEEPALIVE %m");
+
+ /* Try to handle urgent data inline */
+#ifdef SO_OOBINLINE
+ if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *) &on, sizeof(int)) < 0)
+ syslog(LOG_ERR, "setsockopt (SO_OOBINLINE): %m");
+#endif
+
+#ifdef F_SETOWN
+ if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1)
+ syslog(LOG_ERR, "fcntl F_SETOWN: %m");
+#elif defined(SIOCSPGRP)
+ {
+ int pid;
+ pid = getpid();
+ if (ioctl(fileno(stdin), SIOCSPGRP, &pid) == -1)
+ syslog(LOG_ERR, "ioctl SIOCSPGRP: %m");
+ }
+#endif
+
+#ifdef INET6
+ if ((SOCK_FAMILY(ctrl_addr) == AF_INET6) &&
+ IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)&(ctrl_addr))->sin6_addr))
+ ctrl_v4mapped = 1;
+#endif
+
+ if (data_port == 0) {
+ serv = getservbyname("ftp-data", "tcp");
+ if (serv != NULL)
+ data_port = serv->s_port;
+ else
+ data_port = htons(ntohs(SOCK_PORT(ctrl_addr)) - 1);
+ }
+
+ if (RootDirectory != NULL) {
+ if ((chroot(RootDirectory) < 0)
+ || (chdir("/") < 0)) {
+ syslog(LOG_ERR, "Cannot chroot to initial directory, aborting.");
+ exit(1);
+ }
+ }
+
+ load_timeouts();
+
+ /* set resolver options */
+ set_res_options();
+
+ dolog(&his_addr);
+ /* Set up default state */
+ data = -1;
+ type = TYPE_A;
+ form = FORM_N;
+ stru = STRU_F;
+ mode = MODE_S;
+ tmpline[0] = '\0';
+ yyerrorcalled = 0;
+
+ entry = (struct aclmember *) NULL;
+ if ((getaclentry("hostname", &entry)) && ARG0) {
+ (void) strncpy(hostname, ARG0, sizeof(hostname));
+ hostname[sizeof(hostname) - 1] = '\0';
+ }
+ else {
+#ifdef HAVE_SYSINFO
+ sysinfo(SI_HOSTNAME, hostname, sizeof(hostname));
+#else
+ (void) gethostname(hostname, sizeof(hostname));
+#endif
+/* set the FQDN here */
+ hp = wu_gethostbyname(hostname);
+ if (hp) {
+ (void) strncpy(hostname, hp, sizeof(hostname));
+ hostname[sizeof(hostname) - 1] = '\0';
+ }
+ }
+ route_vectored = routevector();
+ conv_init();
+
+#ifdef MAIL_ADMIN
+ incmails = 0;
+ mailfrom = NULL;
+#endif /* MAIL_ADMIN */
+#ifdef VIRTUAL
+ /*
+ ** If virtual_mode is set at this point then an alternate ftpaccess
+ ** is in use. Otherwise we need to check the Master ftpaccess file
+ ** to see if the site is only using the "virtual" directives to
+ ** specify virtual site directives.
+ **
+ ** In this manner an admin can put a virtual site in the ftpservers
+ ** file if they need expanded configuration support or can use the
+ ** minimal root/banner/logfile if they do not need any more than that.
+ */
+
+ if (virtual_mode) {
+ /* Get the root of the virtual server directory */
+ entry = (struct aclmember *) NULL;
+ if (getaclentry("root", &entry)) {
+ if (ARG0)
+ strcpy(virtual_root, ARG0);
+ }
+
+ /* Get the logfile to use */
+ entry = (struct aclmember *) NULL;
+ if (getaclentry("logfile", &entry)) {
+ if (ARG0)
+ strcpy(logfile, ARG0);
+ }
+ }
+ else {
+ virtual_hostname[0] = '\0';
+ virtual_address[0] = '\0';
+ virtual_len = sizeof(virtual_addr);
+ if (getsockname(0, (struct sockaddr *) &virtual_addr, &virtual_len) == 0) {
+ strcpy(virtual_address, inet_stop(&virtual_addr));
+ wu_gethostbyaddr(&virtual_addr, virtual_hostname, sizeof(virtual_hostname));
+ entry = (struct aclmember *) NULL;
+ while (getaclentry("virtual", &entry)) {
+ if (!ARG0 || !ARG1 || !ARG2)
+ continue;
+ if (hostmatch(ARG0, virtual_address, virtual_hostname)) {
+ if (!strcasecmp(ARG1, "root")) {
+ if (debug)
+ syslog(LOG_DEBUG, "VirtualFTP Connect to: %s [%s]",
+ virtual_hostname, virtual_address);
+ virtual_mode = 1;
+ strncpy(virtual_root, ARG2, sizeof(virtual_root));
+ virtual_root[sizeof(virtual_root) - 1] = '\0';
+ /* reset hostname to this virtual name */
+ (void) strcpy(hostname, virtual_hostname);
+ virtual_email[0] = '\0';
+ }
+ if (!strcasecmp(ARG1, "banner")) {
+ strncpy(virtual_banner, ARG2, sizeof(virtual_banner));
+ virtual_banner[sizeof(virtual_banner) - 1] = '\0';
+ }
+ if (!strcasecmp(ARG1, "logfile")) {
+ strncpy(logfile, ARG2, sizeof(logfile));
+ logfile[sizeof(logfile) - 1] = '\0';
+ }
+ if (!strcasecmp(ARG1, "hostname")) {
+ strncpy(hostname, ARG2, sizeof(hostname));
+ hostname[sizeof(hostname) - 1] = '\0';
+ }
+ if (!strcasecmp(ARG1, "email")) {
+ strncpy(virtual_email, ARG2, sizeof(virtual_email));
+ virtual_email[sizeof(virtual_email) - 1] = '\0';
+ }
+#ifdef OTHER_PASSWD
+ if (!strcasecmp(ARG1, "passwd")) {
+ strncpy(_path_passwd, ARG2, sizeof(_path_passwd));
+ _path_passwd[sizeof(_path_passwd) - 1] = '\0';
+#ifdef USE_PAM
+ use_pam = 0;
+#endif
+ }
+#ifdef SHADOW_PASSWORD
+ if (!strcasecmp(ARG1, "shadow")) {
+ strncpy(_path_shadow, ARG2, sizeof(_path_shadow));
+ _path_shadow[sizeof(_path_shadow) - 1] = '\0';
+#ifdef USE_PAM
+ use_pam = 0;
+#endif
+ }
+#endif
+#endif
+#ifdef MAIL_ADMIN
+ if (mailfrom == NULL)
+ if (!strcasecmp(ARG1, "mailfrom")) {
+ mailfrom = strdup(ARG2);
+ }
+ if (!strcasecmp(ARG1, "incmail")) {
+ if (incmails < INCMAILS)
+ incmail[incmails++] = strdup(ARG2);
+ }
+#endif
+ }
+ }
+ if (!virtual_mode) {
+ entry = (struct aclmember *) NULL;
+ while (getaclentry("defaultserver", &entry)) {
+ if (!ARG0 || !ARG1)
+ continue;
+#ifdef MAIL_ADMIN
+ if (mailfrom == NULL)
+ if (!strcasecmp(ARG0, "mailfrom")) {
+ mailfrom = strdup(ARG1);
+ }
+ if (!strcasecmp(ARG0, "incmail")) {
+ if (incmails < INCMAILS)
+ incmail[incmails++] = strdup(ARG1);
+ }
+#endif
+ }
+ }
+ }
+ }
+
+#ifdef VIRTUAL_DEBUG
+ lreply(220, "_path_ftpaccess == %s", _path_ftpaccess);
+ lreply(220, "_path_ftpusers == %s", _path_ftpusers);
+ lreply(220, "_path_ftphosts == %s", _path_ftphosts);
+ lreply(220, "_path_private == %s", _path_private);
+ lreply(220, "_path_cvt == %s", _path_cvt);
+ if (virtual_mode) {
+ if (virtual_ftpaccess)
+ lreply(220, "VIRTUAL Mode: Using %s specific %s access file",
+ hostname, _path_ftpaccess);
+ else
+ lreply(220, "VIRTUAL Mode: Using Master access file %s",
+ _path_ftpaccess);
+
+ lreply(220, "virtual_root == %s", virtual_root);
+ if (!virtual_ftpaccess)
+ lreply(220, "virtual_banner == %s", virtual_banner);
+ }
+ lreply(220, "logfile == %s", logfile);
+#endif
+#endif
+
+ if (is_shutdown(1, 1) != 0) {
+ syslog(LOG_INFO, "connection refused (server shut down) from %s",
+ remoteident);
+ reply(500, "%s FTP server shut down -- please try again later.",
+ hostname);
+ exit(0);
+ }
+
+#ifdef OPIE
+ af_pwok = opieaccessfile(remotehost);
+#endif
+
+ /* check permitted access based on name and address lookup of remote host */
+ if (!check_rhost_reverse()) {
+ exit(0);
+ }
+ if (!check_rhost_matches()) {
+ exit(0);
+ }
+
+ show_banner(220);
+
+#ifndef INTERNAL_LS
+ entry = (struct aclmember *) NULL;
+ if (getaclentry("lslong", &entry) && ARG0 && (int) strlen(ARG0) > 0) {
+ strcpy(ls_long, ARG0);
+ for (which = 1; (which < MAXARGS) && ARG[which]; which++) {
+ strcat(ls_long, " ");
+ strcat(ls_long, ARG[which]);
+ }
+ }
+ else {
+#if defined(SVR4) || defined(ISC)
+#if defined(AIX) || defined(SOLARIS_2)
+ strcpy(ls_long, "/bin/ls -lA");
+#else
+ strcpy(ls_long, "/bin/ls -la");
+#endif
+#else
+ strcpy(ls_long, "/bin/ls -lgA");
+#endif
+ }
+ strcat(ls_long, " %s");
+
+ entry = (struct aclmember *) NULL;
+ if (getaclentry("lsshort", &entry) && ARG0 && (int) strlen(ARG0) > 0) {
+ strcpy(ls_short, ARG0);
+ for (which = 1; (which < MAXARGS) && ARG[which]; which++) {
+ strcat(ls_short, " ");
+ strcat(ls_short, ARG[which]);
+ }
+ }
+ else {
+#if defined(SVR4) || defined(ISC)
+#if defined(AIX) || defined(SOLARIS_2)
+ strcpy(ls_short, "/bin/ls -lA");
+#else
+ strcpy(ls_short, "/bin/ls -la");
+
+#endif
+#else
+ strcpy(ls_short, "/bin/ls -lgA");
+#endif
+ }
+ strcat(ls_short, " %s");
+
+ entry = (struct aclmember *) NULL;
+ if (getaclentry("lsplain", &entry) && ARG0 && (int) strlen(ARG0) > 0) {
+ strcpy(ls_plain, ARG0);
+ for (which = 1; (which < MAXARGS) && ARG[which]; which++) {
+ strcat(ls_plain, " ");
+ strcat(ls_plain, ARG[which]);
+ }
+ }
+ else
+ strcpy(ls_plain, "/bin/ls");
+ strcat(ls_plain, " %s");
+#endif /* ! INTERNAL_LS */
+#ifdef MAIL_ADMIN
+ mailservers = 0;
+ entry = (struct aclmember *) NULL;
+ while (getaclentry("mailserver", &entry) && ARG0 && mailservers < MAILSERVERS)
+ mailserver[mailservers++] = strdup(ARG0);
+ if (mailservers == 0)
+ mailserver[mailservers++] = strdup("localhost");
+ if (incmails == 0) {
+ entry = (struct aclmember *) NULL;
+ while (getaclentry("incmail", &entry) && ARG0 && incmails < INCMAILS)
+ incmail[incmails++] = strdup(ARG0);
+ }
+ if (mailfrom == NULL) {
+ entry = (struct aclmember *) NULL;
+ if (getaclentry("mailfrom", &entry) && ARG0)
+ mailfrom = strdup(ARG0);
+ else
+ mailfrom = strdup("wu-ftpd");
+ }
+#endif /* MAIL_ADMIN */
+ {
+#define OUTPUT_LEN (BUFSIZ * 2)
+ int version_option = 0;
+ char output_text[OUTPUT_LEN + 1];
+ int which;
+
+ entry = NULL;
+ if (getaclentry("greeting", &entry) && ARG0) {
+ if (!strcasecmp(ARG0, "full"))
+ version_option = 0;
+ else if (!strcasecmp(ARG0, "text") && ARG1)
+ version_option = 3;
+ else if (!strcasecmp(ARG0, "terse"))
+ version_option = 2;
+ else if (!strcasecmp(ARG0, "brief"))
+ version_option = 1;
+ }
+ switch (version_option) {
+ default:
+ reply(220, "%s FTP server (%s) ready.", hostname, version);
+ break;
+ case 1:
+ reply(220, "%s FTP server ready.", hostname);
+ break;
+ case 2:
+ reply(220, "FTP server ready.");
+ break;
+ case 3:
+ output_text[0] = '\0';
+ for (which = 1; (which < MAXARGS) && ARG[which]; which++) {
+ if (which > 1)
+ (void) strlcat(output_text, " ", sizeof(output_text));
+ (void) strlcat(output_text, ARG[which], sizeof(output_text));
+ }
+ reply(220, "%s", output_text);
+ break;
+ }
+ }
+ (void) setjmp(errcatch);
+
+ for (;;)
+ (void) yyparse();
+ /* NOTREACHED */
+}
+
+
+SIGNAL_TYPE randomsig(int sig)
+{
+#ifdef HAVE_SIGLIST
+ syslog(LOG_ERR, "exiting on signal %d: %s", sig, sys_siglist[sig]);
+#else
+ syslog(LOG_ERR, "exiting on signal %d", sig);
+#endif
+ chdir("/");
+ signal(SIGIOT, SIG_DFL);
+ signal(SIGILL, SIG_DFL);
+ exit(1);
+ /* dologout(-1); *//* NOTREACHED */
+}
+
+SIGNAL_TYPE lostconn(int sig)
+{
+#ifdef VERBOSE_ERROR_LOGING
+ syslog(LOG_INFO, "lost connection to %s", remoteident);
+#else
+ if (debug)
+ syslog(LOG_DEBUG, "lost connection to %s", remoteident);
+#endif
+ dologout(-1);
+}
+
+static char ttyline[20];
+
+#ifdef MAPPING_CHDIR
+/* Keep track of the path the user has chdir'd into and respond with
+ * that to pwd commands. This is to avoid having the absolue disk
+ * path returned, which I want to avoid.
+ */
+char mapped_path[MAXPATHLEN] = "/";
+
+#if !defined(HAVE_GETCWD)
+char *mapping_getwd(char *path)
+{
+ strcpy(path, mapped_path);
+ return path;
+}
+#endif /* !defined(HAVE_GETCWD) */
+
+char *mapping_getcwd(char *path, size_t size)
+{
+ (void) strlcpy(path, mapped_path, size);
+ return path;
+}
+
+/* Make these globals rather than local to mapping_chdir to avoid stack overflow */
+char pathspace[MAXPATHLEN];
+char old_mapped_path[MAXPATHLEN];
+
+void do_elem(char *dir)
+{
+ /* . */
+ if (dir[0] == '.' && dir[1] == '\0') {
+ /* ignore it */
+ return;
+ }
+
+ /* .. */
+ if (dir[0] == '.' && dir[1] == '.' && dir[2] == '\0') {
+ char *last;
+ /* lop the last directory off the path */
+ if ((last = strrchr(mapped_path, '/'))) {
+ /* If start of pathname leave the / */
+ if (last == mapped_path)
+ last++;
+ *last = '\0';
+ }
+ return;
+ }
+
+ /* append the dir part with a leading / unless at root */
+ if (!(mapped_path[0] == '/' && mapped_path[1] == '\0'))
+ (void) strlcat(mapped_path, "/", sizeof(mapped_path));
+ (void) strlcat(mapped_path, dir, sizeof(mapped_path));
+}
+
+int mapping_chdir(char *orig_path)
+{
+ int ret;
+ char *sl, *path;
+
+ (void) strlcpy(old_mapped_path, mapped_path, sizeof(old_mapped_path));
+ (void) strlcpy(pathspace, orig_path, sizeof(pathspace));
+ path = pathspace;
+
+ /* / at start of path, set the start of the mapped_path to / */
+ if (path[0] == '/') {
+ mapped_path[0] = '/';
+ mapped_path[1] = '\0';
+ path++;
+ }
+
+ while ((sl = strchr(path, '/'))) {
+ char *dir;
+ dir = path;
+ *sl = '\0';
+ path = sl + 1;
+ if (*dir)
+ do_elem(dir);
+ if (*path == '\0')
+ break;
+ }
+ if (*path)
+ do_elem(path);
+
+ if ((ret = chdir(mapped_path)) < 0) {
+ (void) strlcpy(mapped_path, old_mapped_path, sizeof(mapped_path));
+ }
+
+ return ret;
+}
+/* From now on use the mapping version */
+
+#define chdir(d) mapping_chdir(d)
+#define getwd(d) mapping_getwd(d)
+#define getcwd(d,u) mapping_getcwd((d),(u))
+
+#endif /* MAPPING_CHDIR */
+
+/* Helper function for sgetpwnam(). */
+char *sgetsave(char *s)
+{
+ char *new;
+
+ new = (char *) malloc(strlen(s) + 1);
+
+ if (new == NULL) {
+ perror_reply(421, "Local resource failure: malloc");
+ dologout(1);
+ /* NOTREACHED */
+ }
+ (void) strcpy(new, s);
+ return (new);
+}
+
+/* Save the result of a getpwnam. Used for USER command, since the data
+ * returned must not be clobbered by any other command (e.g., globbing). */
+struct passwd *sgetpwnam(char *name)
+{
+ static struct passwd save;
+ register struct passwd *p;
+#ifdef M_UNIX
+ struct passwd *ret = (struct passwd *) NULL;
+#endif
+ char *sgetsave(char *s);
+#ifdef KERBEROS
+ register struct authorization *q;
+#endif /* KERBEROS */
+
+#if defined(SecureWare) || defined(HPUX_10_TRUSTED)
+ struct pr_passwd *pr;
+#endif
+
+#ifdef KERBEROS
+ init_krb();
+ q = getauthuid(p->pw_uid);
+ end_krb();
+#endif /* KERBEROS */
+
+#ifdef M_UNIX
+#if defined(SecureWare) || defined(HPUX_10_TRUSTED)
+ if ((pr = getprpwnam(name)) == NULL)
+ goto DONE;
+#endif /* SecureWare || HPUX_10_TRUSTED */
+#ifdef OTHER_PASSWD
+ if ((p = bero_getpwnam(name, _path_passwd)) == NULL)
+#else
+ if ((p = getpwnam(name)) == NULL)
+#endif
+ goto DONE;
+#else /* M_UNIX */
+#if defined(SecureWare) || defined(HPUX_10_TRUSTED)
+ if ((pr = getprpwnam(name)) == NULL)
+ return ((struct passwd *) pr);
+#endif /* SecureWare || HPUX_10_TRUSTED */
+#ifdef OTHER_PASSWD
+ if ((p = bero_getpwnam(name, _path_passwd)) == NULL)
+#else
+ if ((p = getpwnam(name)) == NULL)
+#endif
+ return (p);
+#endif /* M_UNIX */
+
+ if (save.pw_name)
+ free(save.pw_name);
+ if (save.pw_gecos)
+ free(save.pw_gecos);
+ if (save.pw_dir)
+ free(save.pw_dir);
+ if (save.pw_shell)
+ free(save.pw_shell);
+ if (save.pw_passwd)
+ free(save.pw_passwd);
+
+ save = *p;
+
+ save.pw_name = sgetsave(p->pw_name);
+
+#ifdef KERBEROS
+ save.pw_passwd = sgetsave(q->a_password);
+#elif defined(SecureWare) || defined(HPUX_10_TRUSTED)
+ if (pr->uflg.fg_encrypt && pr->ufld.fd_encrypt && *pr->ufld.fd_encrypt)
+ save.pw_passwd = sgetsave(pr->ufld.fd_encrypt);
+ else
+ save.pw_passwd = sgetsave("");
+#else
+ save.pw_passwd = sgetsave(p->pw_passwd);
+#endif
+#ifdef SHADOW_PASSWORD
+ if (p && (p->pw_passwd==NULL || strlen(p->pw_passwd)<8)) {
+ struct spwd *spw;
+#ifdef OTHER_PASSWD
+ if ((spw = bero_getspnam(p->pw_name, _path_shadow)) != NULL) {
+#else
+ setspent();
+ if ((spw = getspnam(p->pw_name)) != NULL) {
+#endif
+ int expired = 0;
+ /*XXX Does this work on all Shadow Password Implementations? */
+ /* it is supposed to work on Solaris 2.x */
+ time_t now;
+ long today;
+
+ now = time((time_t *) 0);
+ today = now / (60 * 60 * 24);
+
+ if ((spw->sp_expire > 0) && (spw->sp_expire < today))
+ expired++;
+ if ((spw->sp_max > 0) && (spw->sp_lstchg > 0) &&
+ (spw->sp_lstchg + spw->sp_max < today))
+ expired++;
+ free(save.pw_passwd);
+ save.pw_passwd = sgetsave(expired ? "" : spw->sp_pwdp);
+ }
+/* Don't overwrite the password if the shadow read fails, getpwnam() is NIS
+ aware but getspnam() is not. */
+/* Shadow passwords are optional on Linux. --marekm */
+#if !defined(LINUX) && !defined(UNIXWARE)
+ else {
+ free(save.pw_passwd);
+ save.pw_passwd = sgetsave("");
+ }
+#endif
+/* marekm's fix for linux proc file system shadow passwd exposure problem */
+#ifndef OTHER_PASSWD
+ endspent();
+#endif
+ }
+#endif
+ save.pw_gecos = sgetsave(p->pw_gecos);
+ save.pw_dir = sgetsave(p->pw_dir);
+ save.pw_shell = sgetsave(p->pw_shell);
+#ifdef M_UNIX
+ ret = &save;
+ DONE:
+ endpwent();
+#endif
+#if defined(SecureWare) || defined(HPUX_10_TRUSTED)
+ endprpwent();
+#endif
+#ifdef M_UNIX
+ return (ret);
+#else
+ return (&save);
+#endif
+}
+#if defined(SKEY) && !defined(__NetBSD__)
+/*
+ * From Wietse Venema, Eindhoven University of Technology.
+ */
+/* skey_challenge - additional password prompt stuff */
+
+char *skey_challenge(char *name, struct passwd *pwd, int pwok)
+{
+ static char buf[128];
+ char sbuf[40];
+ struct skey skey;
+
+ /* Display s/key challenge where appropriate. */
+
+ if (pwd == NULL || skeychallenge(&skey, pwd->pw_name, sbuf))
+ sprintf(buf, "Password required for %s.", name);
+ else
+ sprintf(buf, "%s %s for %s.", sbuf,
+ pwok ? "allowed" : "required", name);
+ return (buf);
+}
+#endif
+
+int login_attempts; /* number of failed login attempts */
+int askpasswd; /* had user command, ask for passwd */
+#ifndef HELP_CRACKERS
+int DenyLoginAfterPassword;
+char DelayedMessageFile[MAXPATHLEN];
+extern void pr_mesg(int msgcode, char *msgfile);
+#endif
+
+#if defined(VIRTUAL) && defined(CLOSED_VIRTUAL_SERVER)
+static int defaultserver_allow(const char *username)
+{
+ struct aclmember *entry = NULL;
+ int which;
+
+ while (getaclentry("defaultserver", &entry))
+ if (ARG0 && !strcasecmp(ARG0, "allow"))
+ for (which = 1; (which < MAXARGS) && ARG[which]; which++)
+ if (!strcasecmp(username, ARG[which]) || !strcmp("*", ARG[which]))
+ return (1);
+ return (0);
+}
+
+static int defaultserver_deny(const char *username)
+{
+ struct aclmember *entry = NULL;
+ int which;
+
+ while (getaclentry("defaultserver", &entry))
+ if (ARG0 && !strcasecmp(ARG0, "deny"))
+ for (which = 1; (which < MAXARGS) && ARG[which]; which++)
+ if (!strcasecmp(username, ARG[which]) || !strcmp("*", ARG[which]))
+ return (1);
+ return (0);
+}
+
+static int defaultserver_private(void)
+{
+ struct aclmember *entry = NULL;
+
+ while (getaclentry("defaultserver", &entry))
+ if (ARG0 && !strcasecmp(ARG0, "private"))
+ return (1);
+ return (0);
+}
+#endif
+
+/* USER command. Sets global passwd pointer pw if named account exists and is
+ * acceptable; sets askpasswd if a PASS command is expected. If logged in
+ * previously, need to reset state. If name is "ftp" or "anonymous", the
+ * name is not in the ftpusers file, and ftp account exists, set anonymous and
+ * pw, then just return. If account doesn't exist, ask for passwd anyway.
+ * Otherwise, check user requesting login privileges. Disallow anyone who
+ * does not have a standard shell as returned by getusershell(). Disallow
+ * anyone mentioned in the ftpusers file to allow people such as root and
+ * uucp to be avoided. */
+
+/*
+ char *getusershell();
+ */
+void user(char *name)
+{
+ char *cp;
+ char *shell;
+#ifdef BSD_AUTH
+ char *auth;
+#endif
+#if defined(USE_GSS)
+ int gss_need_passwd = 1;
+#endif
+
+/* H* fix: if we're logged in at all, we can't log in again. */
+ if (logged_in) {
+#ifdef VERBOSE_ERROR_LOGING
+ syslog(LOG_NOTICE, "FTP LOGIN REFUSED (already logged in as %s) FROM %s, %s",
+ pw->pw_name, remoteident, name);
+#endif
+ reply(530, "Already logged in.");
+ return;
+ }
+#ifndef HELP_CRACKERS
+ askpasswd = 1;
+ DenyLoginAfterPassword = 0;
+ DelayedMessageFile[0] = '\0';
+#endif
+#ifdef BSD_AUTH
+ if ((auth = strchr(name, ':')))
+ *auth++ = 0;
+#endif
+
+#ifdef HOST_ACCESS /* 19-Mar-93 BM */
+ if (!rhost_ok(name, remotehost, remoteaddr)) {
+#ifndef HELP_CRACKERS
+ DenyLoginAfterPassword = 1;
+ syslog(LOG_NOTICE, "FTP LOGIN REFUSED (name in %s) FROM %s, %s",
+ _path_ftphosts, remoteident, name);
+#else
+ reply(530, "User %s access denied.", name);
+ syslog(LOG_NOTICE,
+ "FTP LOGIN REFUSED (name in %s) FROM %s, %s",
+ _path_ftphosts, remoteident, name);
+ return;
+#endif
+ }
+#endif
+
+ strncpy(the_user, name, MAXUSERNAMELEN - 1);
+
+ anonymous = 0;
+ guest = 0;
+
+ if (!strcasecmp(name, "ftp") || !strcasecmp(name, "anonymous")) {
+ struct aclmember *entry = NULL;
+ int machineok = 1;
+ char guestservername[MAXHOSTNAMELEN];
+ guestservername[0] = '\0';
+
+#ifdef NO_ANONYMOUS_ACCESS
+ reply(530, "Anonymous FTP access denied.");
+ syslog(LOG_NOTICE, "FTP LOGIN REFUSED (anonymous ftp not supported) FROM %s, %s",
+ remoteident, name);
+ return;
+#else
+#if defined(VIRTUAL) && defined(CLOSED_VIRTUAL_SERVER)
+ if (!virtual_mode && defaultserver_private()) {
+#ifndef HELP_CRACKERS
+ DenyLoginAfterPassword = 1;
+ syslog(LOG_NOTICE, "FTP LOGIN REFUSED (anonymous ftp denied on default server) FROM %s, %s",
+ remoteident, name);
+#else
+ reply(530, "User %s access denied.", name);
+ syslog(LOG_NOTICE,
+ "FTP LOGIN REFUSED (anonymous ftp denied on default server) FROM %s, %s",
+ remoteident, name);
+ return;
+#endif
+ }
+#endif
+ if (checkuser("ftp") || checkuser("anonymous")) {
+#ifndef HELP_CRACKERS
+ DenyLoginAfterPassword = 1;
+ syslog(LOG_NOTICE, "FTP LOGIN REFUSED (ftp in %s) FROM %s, %s",
+ _path_ftpusers, remoteident, name);
+#else
+ reply(530, "User %s access denied.", name);
+ syslog(LOG_NOTICE,
+ "FTP LOGIN REFUSED (ftp in %s) FROM %s, %s",
+ _path_ftpusers, remoteident, name);
+ return;
+#endif
+
+ /*
+ ** Algorithm used:
+ ** - if no "guestserver" directive is present,
+ ** anonymous access is allowed, for backward compatibility.
+ ** - if a "guestserver" directive is present,
+ ** anonymous access is restricted to the machines listed,
+ ** usually the machine whose CNAME on the current domain
+ ** is "ftp"...
+ **
+ ** the format of the "guestserver" line is
+ ** guestserver [<machine1> [<machineN>]]
+ ** that is, "guestserver" will forbid anonymous access on all machines
+ ** while "guestserver ftp inf" will allow anonymous access on
+ ** the two machines whose CNAMES are "ftp.enst.fr" and "inf.enst.fr".
+ **
+ ** if anonymous access is denied on the current machine,
+ ** the user will be asked to use the first machine listed (if any)
+ ** on the "guestserver" line instead:
+ ** 530- Guest login not allowed on this machine,
+ ** connect to ftp.enst.fr instead.
+ **
+ ** -- <Nicolas.Pioch@enst.fr>
+ */
+ }
+ else if (getaclentry("guestserver", &entry)) {
+ char *tmphost;
+
+ /*
+ ** if a "guestserver" line is present,
+ ** default is not to allow guest logins
+ */
+ machineok = 0;
+
+ if (hostname[0]
+ && ((tmphost = wu_gethostbyname(hostname)))) {
+
+ /*
+ ** hostname is the only first part of the FQDN
+ ** this may or may not correspond to the h_name value
+ ** (machines with more than one IP#, CNAMEs...)
+ ** -> need to fix that, calling gethostbyname on hostname
+ **
+ ** WARNING!
+ ** for SunOS 4.x, you need to have a working resolver in the libc
+ ** for CNAMES to work properly.
+ ** If you don't, add "-lresolv" to the libraries before compiling!
+ */
+ char dns_localhost[MAXHOSTNAMELEN];
+ int machinecount;
+
+ strncpy(dns_localhost, tmphost, sizeof(dns_localhost));
+ dns_localhost[sizeof(dns_localhost) - 1] = '\0';
+
+ for (machinecount = 0;
+ (machinecount < MAXARGS) && entry->arg[machinecount];
+ machinecount++) {
+
+ if ((tmphost = wu_gethostbyname(entry->arg[machinecount]))) {
+ /*
+ ** remember the name of the first machine for redirection
+ */
+
+ if (!machinecount) {
+ strncpy(guestservername, entry->arg[machinecount],
+ sizeof(guestservername));
+ guestservername[sizeof(guestservername) - 1] = '\0';
+ }
+
+ if (!strcasecmp(tmphost, dns_localhost)) {
+ machineok++;
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (!machineok) {
+ if (guestservername[0])
+ reply(530,
+ "Guest login not allowed on this machine, connect to %s instead.",
+ guestservername);
+ else
+ reply(530,
+ "Guest login not allowed on this machine.");
+ syslog(LOG_NOTICE,
+ "FTP LOGIN REFUSED (localhost not in guestservers) FROM %s, %s",
+ remoteident, name);
+ /* End of the big patch -- Nap */
+
+ dologout(0);
+ }
+ else if ((pw = sgetpwnam("ftp")) != NULL) {
+ anonymous = 1; /* for the access_ok call */
+ if (access_ok(530) < 1) {
+#ifndef HELP_CRACKERS
+ DenyLoginAfterPassword = 1;
+ syslog(LOG_NOTICE, "FTP LOGIN REFUSED (access denied) FROM %s, %s",
+ remoteident, name);
+ reply(331, "Guest login ok, send your complete e-mail address as password.");
+#else
+ reply(530, "User %s access denied.", name);
+ syslog(LOG_NOTICE,
+ "FTP LOGIN REFUSED (access denied) FROM %s, %s",
+ remoteident, name);
+ dologout(0);
+#endif
+ }
+ else {
+ askpasswd = 1;
+/* H* fix: obey use_accessfile a little better. This way, things set on the
+ command line [like xferlog stuff] don't get stupidly overridden.
+ XXX: all these checks maybe should be in acl.c and access.c */
+ if (use_accessfile)
+ acl_setfunctions();
+ reply(331, "Guest login ok, send your complete e-mail address as password.");
+ }
+ }
+ else {
+#ifndef HELP_CRACKERS
+ DenyLoginAfterPassword = 1;
+ reply(331, "Guest login ok, send your complete e-mail address as password.");
+ syslog(LOG_NOTICE, "FTP LOGIN REFUSED (ftp not in /etc/passwd) FROM %s, %s",
+ remoteident, name);
+#else
+ reply(530, "User %s unknown.", name);
+ syslog(LOG_NOTICE,
+ "FTP LOGIN REFUSED (ftp not in /etc/passwd) FROM %s, %s",
+ remoteident, name);
+#endif
+#ifdef SOLARIS_BSM_AUDIT
+ audit_ftpd_no_anon();
+#endif
+ }
+ return;
+#endif
+ }
+#ifdef ANON_ONLY
+/* H* fix: define the above to completely DISABLE logins by real users,
+ despite ftpusers, shells, or any of that rot. You can always hang your
+ "real" server off some other port, and access-control it. */
+
+ else { /* "ftp" or "anon" -- MARK your conditionals, okay?! */
+#ifndef HELP_CRACKERS
+ DenyLoginAfterPassword = 1;
+ syslog(LOG_NOTICE, "FTP LOGIN REFUSED (not anonymous) FROM %s, %s",
+ remoteident, name);
+ reply(331, "Password required for %s.", name);
+#else
+ reply(530, "User %s unknown.", name);
+ syslog(LOG_NOTICE,
+ "FTP LOGIN REFUSED (not anonymous) FROM %s, %s",
+ remoteident, name);
+#endif
+ return;
+ }
+/* fall here if username okay in any case */
+#endif /* ANON_ONLY */
+
+#if defined(VIRTUAL) && defined(CLOSED_VIRTUAL_SERVER)
+ if (!virtual_mode && defaultserver_deny(name) && !defaultserver_allow(name)) {
+#ifndef HELP_CRACKERS
+ DenyLoginAfterPassword = 1;
+ syslog(LOG_NOTICE, "FTP LOGIN REFUSED (ftp denied on default server) FROM %s, %s",
+ remoteident, name);
+#else
+ reply(530, "User %s access denied.", name);
+ syslog(LOG_NOTICE,
+ "FTP LOGIN REFUSED (ftp denied on default server) FROM %s, %s",
+ remoteident, name);
+ return;
+#endif
+ }
+#endif
+
+#if defined(USE_GSS)
+ if (gss_info.must_gss_auth &&
+ (!IS_GSSAUTH(cur_auth_type) ||
+ !(gss_info.authstate & GSS_ADAT_DONE))) {
+ reply(530, "Must perform authentication before identifying USER.");
+ return;
+ }
+#endif /* USE_GSS */
+
+ if ((pw = sgetpwnam(name)) != NULL) {
+ if ((denieduid(pw->pw_uid) && !alloweduid(pw->pw_uid))
+ || (deniedgid(pw->pw_gid) && !allowedgid(pw->pw_gid))) {
+#ifndef HELP_CRACKERS
+ DenyLoginAfterPassword = 1;
+ syslog(LOG_NOTICE, "FTP LOGIN REFUSED (username in denied-uid) FROM %s, %s",
+ remoteident, name);
+ reply(331, "Password required for %s.", name);
+#else
+ reply(530, "User %s access denied.", name);
+ syslog(LOG_NOTICE,
+ "FTP LOGIN REFUSED (username in denied-uid) FROM %s, %s",
+ remoteident, name);
+#endif
+ return;
+ }
+#if defined(USE_GSS)
+ if (IS_GSSAUTH(cur_auth_type) &&
+ (gss_info.authstate & GSS_ADAT_DONE)) {
+ char buf[BUFSIZ];
+
+ if (gss_user(pw))
+ gss_info.authstate |= GSS_USER_DONE;
+
+ if (gss_info.must_gss_auth &&
+ !GSSUSERAUTH_OK(gss_info)) {
+ reply(530, "User %s access denied", name);
+ if (logging)
+ syslog(LOG_NOTICE, "FTP GSSAPI LOGIN REFUSED FROM %s, %s",
+ remoteident, name);
+ pw = NULL;
+ return;
+ }
+ /*
+ * If GSSAPI user auth failed, or it succeeded but creds were
+ * not forwarded as required, prompt for password.
+ */
+ gss_need_passwd = !GSSUSERAUTH_OK(gss_info) ||
+ (GSSUSERAUTH_OK(gss_info) &&
+ (gss_info.want_creds && !gss_info.have_creds));
+ if (gss_need_passwd) {
+ snprintf(buf, sizeof(buf),
+ "GSSAPI user %s is authorized as %s password required",
+ gss_info.display_name, name);
+ reply(331, "%s", buf);
+ askpasswd = 1;
+ syslog(LOG_DEBUG, "%s", buf);
+ return;
+ }
+ }
+#endif /* defined(USE_GSS) */
+
+#if !defined(USE_PAM) || (defined(USE_PAM) && defined(OTHER_PASSWD)) || defined(SOLARIS_2) /* PAM should be doing these checks, not ftpd */
+#if defined(USE_PAM) && !defined(SOLARIS_2)
+ if(!use_pam) {
+#endif
+ if ((shell = pw->pw_shell) == NULL || *shell == 0)
+ shell = _PATH_BSHELL;
+ while ((cp = getusershell()) != NULL)
+ if (strcmp(cp, shell) == 0)
+ break;
+ endusershell();
+ if (cp == NULL || checkuser(name)) {
+#ifndef HELP_CRACKERS
+ DenyLoginAfterPassword = 1;
+ if (cp == NULL)
+ syslog(LOG_NOTICE, "FTP LOGIN REFUSED (shell not in /etc/shells) FROM %s, %s", remoteident, name);
+ else
+ syslog(LOG_NOTICE, "FTP LOGIN REFUSED (username in %s) FROM %s, %s", _path_ftpusers, remoteident, name);
+ reply(331, "Password required for %s.", name);
+#else
+ reply(530, "User %s access denied.", name);
+ if (cp == NULL)
+ syslog(LOG_NOTICE, "FTP LOGIN REFUSED (shell not in /etc/shells) FROM %s, %s", remoteident, name);
+ else
+ syslog(LOG_NOTICE, "FTP LOGIN REFUSED (username in %s) FROM %s, %s", _path_ftpusers, remoteident, name);
+#endif /* HELP_CRACKERS */
+ pw = (struct passwd *) NULL;
+ return;
+ }
+#if defined(USE_PAM) && !defined(SOLARIS_2)
+ } /* if(!use_pam) */
+#endif
+#endif /* !USE_PAM || (USE_PAM && OTHER_PASSWD) || SOLARIS_2 */
+ /* if user is a member of any of the guestgroups, cause a chroot() */
+ /* after they log in successfully */
+ if (use_accessfile) { /* see above. _H */
+ guest = acl_guestgroup(pw);
+ if (guest && acl_realgroup(pw))
+ guest = 0;
+ }
+ }
+ if (access_ok(530) < 1) {
+#ifndef HELP_CRACKERS
+ DenyLoginAfterPassword = 1;
+ syslog(LOG_NOTICE, "FTP LOGIN REFUSED (access denied) FROM %s, %s",
+ remoteident, name);
+ reply(331, "Password required for %s.", name);
+#else
+ reply(530, "User %s access denied.", name);
+ syslog(LOG_NOTICE, "FTP LOGIN REFUSED (access denied) FROM %s, %s",
+ remoteident, name);
+#endif
+ return;
+ }
+ else if (use_accessfile) /* see above. _H */
+ acl_setfunctions();
+
+#ifdef BSD_AUTH
+ if ((cp = start_auth(auth, name, pw)) != NULL) {
+ char *s;
+
+ for (;;) {
+ s = strsep(&cp, "\n");
+ if (cp == NULL || *cp == '\0')
+ break;
+ lreply(331, "%s", s);
+ }
+ reply(331, "%s", s);
+ }
+ else {
+#endif /* BSD_AUTH */
+#ifdef SKEY
+#ifndef __NetBSD__
+#ifdef SKEY_NAME
+ /* this is the old way, but freebsd uses it */
+ pwok = skeyaccess(name, NULL, remotehost, remoteaddr);
+#else
+ /* this is the new way */
+ pwok = skeyaccess(pw, NULL, remotehost, remoteaddr);
+#endif /* SKEY_NAME */
+ reply(331, "%s", skey_challenge(name, pw, pwok));
+#else
+ if (skey_haskey(name) == 0) {
+ char *myskey;
+
+ myskey = skey_keyinfo(name);
+ reply(331, "Password [%s] required for %s.",
+ myskey ? myskey : "error getting challenge", name);
+ }
+ else
+ reply(331, "Password required for %s.", name);
+#endif /* __NetBSD__ */
+#else
+#ifdef OPIE
+ {
+ char prompt[OPIE_CHALLENGE_MAX + 1];
+ opiechallenge(&opiestate, name, prompt);
+
+ if (askpasswd == -1) {
+ syslog(LOG_WARNING, "Invalid FTP user name %s attempted from %s", name, remotehost);
+ pwok = 0;
+ }
+ else
+ pwok = af_pwok && opiealways(pw->pw_dir);
+ reply(331, "Response to %s %s for %s.",
+ prompt, pwok ? "requested" : "required", name);
+ }
+#else /* !SKEY */
+
+#if defined(USE_GSS)
+ if (GSSUSERAUTH_OK(gss_info) && !gss_need_passwd) {
+ /*
+ * We got this far, we are allowing the GSSAPI authentication
+ * to succeed without further passwd prompting. Jump
+ * to "pass" processing.
+ */
+ askpasswd = 0;
+ logged_in = 1;
+ pass("");
+ return;
+ }
+#endif /* defined(USE_GSS) */
+ reply(331, "Password required for %s.", name);
+#endif /* OPIE */
+#endif /* SKEY */
+#ifdef BSD_AUTH
+ }
+#endif /* BSD_AUTH */
+
+ askpasswd = 1;
+ /* Delay before reading passwd after first failed attempt to slow down
+ * passwd-guessing programs. */
+ if (login_attempts) {
+ enable_signaling(); /* we can allow signals once again: kinch */
+ sleep((unsigned) login_attempts);
+ }
+ return;
+}
+
+/* Check if a user is in the ftpusers file */
+int checkuser(char *name)
+{
+ register FILE *fd;
+ register char *p;
+ char line[BUFSIZ];
+
+#ifdef SOLARIS_ETC_FTPUSERS
+ static int etc_ftpusers = 0;
+
+ if (etc_ftpusers) {
+ strcpy(_path_ftpusers, _PATH_FTPUSERS);
+ etc_ftpusers = 0;
+ }
+retry:
+#endif
+ if ((fd = fopen(_path_ftpusers, "r")) != NULL) {
+ while (fgets(line, sizeof(line), fd) != NULL)
+ if ((p = strchr(line, '\n')) != NULL) {
+ *p = '\0';
+ if (line[0] == '#')
+ continue;
+ if (strcasecmp(line, name) == 0) {
+ (void) fclose(fd);
+#ifdef SOLARIS_BSM_AUDIT
+ audit_ftpd_excluded(name);
+#endif
+#ifdef SOLARIS_ETC_FTPUSERS
+ if (etc_ftpusers)
+ syslog(LOG_NOTICE, "%s is deprecated, use %s instead", _path_ftpusers, _PATH_FTPUSERS);
+#endif
+ return (1);
+ }
+ }
+ (void) fclose(fd);
+ }
+#ifdef SOLARIS_ETC_FTPUSERS
+ if (!etc_ftpusers && (strcmp(_path_ftpusers, _PATH_FTPUSERS) == 0)) {
+ strcpy(_path_ftpusers, "/etc/ftpusers");
+ etc_ftpusers = 1;
+ goto retry;
+ }
+#endif
+ return (0);
+}
+
+int uid_match(char *keyword, uid_t uid)
+{
+ struct aclmember *entry = NULL;
+ int which;
+ char *ptr;
+ struct passwd *pw;
+
+ /*
+ * keyword <uid-range> [<uid-range> ...]
+ *
+ * uid-range may be a username or begin with '%' and be treated as numeric:
+ * %<uid> A single numeric UID
+ * %<uid>+ All UIDs greater or equal to UID
+ * %<uid>- All UIDs greater or equal to UID
+ * %-<uid> All UIDs less or equal to UID
+ * %<uid>-<uid> All UIDs between the two (inclusive)
+ * * All UIDs
+ */
+ while (getaclentry(keyword, &entry)) {
+ for (which = 0; (which < MAXARGS) && ARG[which]; which++) {
+ if (!strcmp(ARG[which], "*"))
+ return (1);
+ if (ARG[which][0] == '%') {
+ if ((ptr = strchr(ARG[which] + 1, '-')) == NULL) {
+ if ((ptr = strchr(ARG[which] + 1, '+')) == NULL) {
+ if (uid == strtoul(ARG[which] + 1, NULL, 0))
+ return (1);
+ }
+ else {
+ *ptr++ = '\0';
+ if ((ARG[which][1] == '\0')
+ || (uid >= strtoul(ARG[which] + 1, NULL, 0))) {
+ *--ptr = '+';
+ return (1);
+ }
+ *--ptr = '+';
+ }
+ }
+ else {
+ *ptr++ = '\0';
+ if (((ARG[which][1] == '\0')
+ || (uid >= strtoul(ARG[which] + 1, NULL, 0)))
+ && ((*ptr == '\0')
+ || (uid <= strtoul(ptr, NULL, 0)))) {
+ *--ptr = '-';
+ return (1);
+ }
+ *--ptr = '-';
+ }
+ }
+ else {
+#ifdef OTHER_PASSWD
+ pw = bero_getpwnam(ARG[which], _path_passwd);
+#else
+ pw = getpwnam(ARG[which]);
+#endif
+ if (pw && (uid == pw->pw_uid))
+ return (1);
+ }
+ }
+ }
+ return (0);
+}
+
+int gid_match(char *keyword, gid_t gid, char *username)
+{
+ struct aclmember *entry = NULL;
+ int which;
+ char *ptr;
+ struct group *grp;
+ char **member;
+
+ /*
+ * keyword <gid-range> [<gid-range> ...]
+ *
+ * gid-range may be a groupname or begin with '%' and be treated as numeric:
+ * %<gid> A single GID
+ * %<gid>+ All GIDs greater or equal to GID
+ * %<gid>- All GIDs greater or equal to GID
+ * %-<gid> All GIDs less or equal to GID
+ * %<gid>-<gid> All GIDs between the two (inclusive)
+ * * All GIDs
+ */
+ while (getaclentry(keyword, &entry)) {
+ for (which = 0; (which < MAXARGS) && ARG[which]; which++) {
+ if (!strcmp(ARG[which], "*"))
+ return (1);
+ if (ARG[which][0] == '%') {
+ if ((ptr = strchr(ARG[which] + 1, '-')) == NULL) {
+ if ((ptr = strchr(ARG[which] + 1, '+')) == NULL) {
+ if (gid == strtoul(ARG[which] + 1, NULL, 0))
+ return (1);
+ }
+ else {
+ *ptr++ = '\0';
+ if ((ARG[which][1] == '\0')
+ || (gid >= strtoul(ARG[which] + 1, NULL, 0))) {
+ *--ptr = '+';
+ return (1);
+ }
+ *--ptr = '+';
+ }
+ }
+ else {
+ *ptr++ = '\0';
+ if (((ARG[which][1] == '\0')
+ || (gid >= strtoul(ARG[which] + 1, NULL, 0)))
+ && ((*ptr == '\0')
+ || (gid <= strtoul(ptr, NULL, 0)))) {
+ *--ptr = '-';
+ return (1);
+ }
+ *--ptr = '-';
+ }
+ }
+ else {
+ if ((grp = getgrnam(ARG[which]))) {
+ if (gid == grp->gr_gid)
+ return (1);
+ if (username) {
+ for (member = grp->gr_mem; *member; member++)
+ if (!strcasecmp(*member, username))
+ return (1);
+ }
+ }
+ }
+ }
+ }
+ return (0);
+}
+
+int denieduid(uid_t uid)
+{
+ return uid_match("deny-uid", uid);
+}
+
+int alloweduid(uid_t uid)
+{
+ return uid_match("allow-uid", uid);
+}
+
+int deniedgid(gid_t gid)
+{
+ return gid_match("deny-gid", gid, NULL);
+}
+
+int allowedgid(gid_t gid)
+{
+ return gid_match("allow-gid", gid, NULL);
+}
+
+/* Terminate login as previous user, if any, resetting state; used when USER
+ * command is given or login fails. */
+
+void end_login(void)
+{
+ delay_signaling(); /* we can't allow any signals while euid==0: kinch */
+ (void) seteuid((uid_t) 0);
+ if (logged_in) {
+ if (wtmp_logging)
+ wu_logwtmp(ttyline, pw->pw_name, remotehost, 0);
+#ifdef USE_PAM
+ if (!anonymous && pamh) {
+ (void) pam_close_session(pamh, 0);
+ (void) pam_end(pamh, PAM_SUCCESS);
+ pamh = (pam_handle_t *)0;
+ }
+#endif
+ }
+ pw = NULL;
+#ifdef AFS_AUTH
+ ktc_ForgetAllTokens();
+#endif
+ logged_in = 0;
+ anonymous = 0;
+ guest = 0;
+}
+
+int validate_eaddr(char *eaddr)
+{
+ int i, host, state;
+
+ for (i = host = state = 0; eaddr[i] != '\0'; i++) {
+ switch (eaddr[i]) {
+ case '.':
+ if (!host)
+ return 0;
+ if (state == 2)
+ state = 3;
+ host = 0;
+ break;
+ case '@':
+ if (!host || state > 1 || !strncasecmp("ftp", eaddr + i - host, host))
+ return 0;
+ state = 2;
+ host = 0;
+ break;
+ case '!':
+ case '%':
+ if (!host || state > 1)
+ return 0;
+ state = 1;
+ host = 0;
+ break;
+ case '-':
+ break;
+ default:
+ host++;
+ }
+ }
+ if (((state == 3) && host > 1) || ((state == 1) && host > 1))
+ return 1;
+ else
+ return 0;
+}
+
+
+#if defined(VIRTUAL) && defined(CLOSED_VIRTUAL_SERVER)
+static int AllowVirtualUser(const char *username)
+{
+ struct aclmember *entry = NULL;
+ int which;
+
+ while (getaclentry("virtual", &entry))
+ if (ARG0 && hostmatch(ARG0, virtual_address, virtual_hostname)
+ && ARG1 && !strcasecmp(ARG1, "allow"))
+ for (which = 2; (which < MAXARGS) && ARG[which]; which++)
+ if (!strcasecmp(username, ARG[which]) || !strcmp("*", ARG[which]))
+ return (1);
+ return (0);
+}
+
+static int DenyVirtualUser(const char *username)
+{
+ struct aclmember *entry = NULL;
+ int which;
+
+ while (getaclentry("virtual", &entry))
+ if (ARG0 && hostmatch(ARG0, virtual_address, virtual_hostname)
+ && ARG1 && !strcasecmp(ARG1, "deny"))
+ for (which = 2; (which < MAXARGS) && ARG[which]; which++)
+ if (!strcasecmp(username, ARG[which]) || !strcmp("*", ARG[which]))
+ return (1);
+ return (0);
+}
+
+static int DenyVirtualAnonymous(void)
+{
+ struct aclmember *entry = NULL;
+
+ while (getaclentry("virtual", &entry))
+ if (ARG0 && hostmatch(ARG0, virtual_address, virtual_hostname)
+ && ARG1 && !strcasecmp(ARG1, "private"))
+ return (1);
+ return (0);
+}
+#endif
+
+void pass(char *passwd)
+{
+
+#if !defined(USE_PAM) || (defined(USE_PAM) && defined(OTHER_PASSWD))
+ char *xpasswd, *salt;
+#endif
+
+ int passwarn = 0;
+ int rval = 1;
+ int success_code = 230;
+ int cos;
+
+#ifdef SECUREOSF
+ struct pr_passwd *pr;
+ int crypt_alg = 0;
+#endif
+
+#ifdef BSD_AUTH
+ extern int ext_auth;
+ extern char *check_auth();
+#endif
+
+#ifdef ULTRIX_AUTH
+ int numfails;
+#endif /* ULTRIX_AUTH */
+
+#ifdef HAS_PW_EXPIRE
+ int set_expired = FALSE;
+#endif
+
+#ifdef AFS_AUTH
+ char *reason;
+#endif /* AFS_AUTH */
+
+#ifdef DCE_AUTH
+ sec_passwd_rec_t pwr;
+ sec_login_handle_t lhdl;
+ boolean32 rstpwd;
+ sec_login_auth_src_t asrc;
+ error_status_t status;
+#endif /* DCE_AUTH */
+
+#if defined(USE_GSS)
+ /*
+ * LOGIC:
+ * If [ the user presented GSSAPI creds and was authorized ]
+ * jump down past the password validation code.
+ */
+ if (GSSUSERAUTH_OK(gss_info) && logged_in) {
+ /*
+ * We could reply(202, "PASS command superfluous.") here, but
+ * allow this for compat with some clients.
+ */
+ success_code = 232;
+ goto pwd_validation_done;
+ }
+#endif /* defined(USE_GSS) */
+
+ if (logged_in || askpasswd == 0) {
+#ifdef VERBOSE_ERROR_LOGING
+ syslog(LOG_NOTICE, "FTP LOGIN REFUSED (PASS before USER) FROM %s",
+ remoteident);
+#endif
+ reply(503, "Login with USER first.");
+ return;
+ }
+ askpasswd = 0;
+
+ /* Disable lreply() if the first character of the password is '-' since
+ * some hosts don't understand continuation messages and hang... */
+
+ if (*passwd == '-')
+ dolreplies = 0;
+ else
+ dolreplies = 1;
+/* ******** REGULAR/GUEST USER PASSWORD PROCESSING ********** */
+ if (!anonymous) { /* "ftp" is only account allowed no password */
+#ifndef HELP_CRACKERS
+ if (DenyLoginAfterPassword) {
+ pr_mesg(530, DelayedMessageFile);
+ reply(530, "Login incorrect.");
+#ifdef SOLARIS_BSM_AUDIT
+ audit_ftpd_failure(the_user);
+#endif
+ acl_remove();
+ pw = NULL;
+ if (++login_attempts >= lgi_failure_threshold) {
+ syslog(LOG_NOTICE, "repeated login failures from %s",
+ remoteident);
+ exit(0);
+ }
+ return;
+ }
+#endif
+ if (*passwd == '-')
+ passwd++;
+#ifdef USE_PAM
+#ifdef OTHER_PASSWD
+ if (use_pam
+#if defined(USE_GSS)
+ && !GSSUSERAUTH_OK(gss_info)
+#endif
+ ) {
+#endif
+ /* PAM authentication
+ * If PAM authenticates a user we know nothing about on the local
+ * system, use the generic guest account credentials. We should make
+ * this somehow a configurable item somewhere; later more on that.
+ *
+ * For now assume the guest (not anonymous) identity, so the site
+ * admins can still differentiate between the truw anonymous user and
+ * a little bit more special ones. Otherwise he wouldn't go the extra
+ * mile to have a different user database, right?
+ * --gaftonc */
+ if (pam_check_pass(the_user, passwd)) {
+ rval = 0;
+ if (pw == NULL) {
+ /* assume guest account identity */
+ pw = sgetpwnam("ftp");
+ anonymous = 0;
+ guest = 1;
+ /* even go as far as... */
+ if (pw != NULL && pw->pw_name != NULL) {
+ free(pw->pw_name);
+ pw->pw_name = sgetsave(the_user);
+ }
+ }
+ }
+#ifdef OTHER_PASSWD
+ } else {
+#endif
+#endif /* USE_PAM */
+#if !defined(USE_PAM) || (defined(USE_PAM) && defined(OTHER_PASSWD))
+#ifdef BSD_AUTH
+ if (ext_auth) {
+ if ((salt = check_auth(the_user, passwd))) {
+ reply(530, "%s", salt);
+#ifdef LOG_FAILED /* 27-Apr-93 EHK/BM */
+ /*
+ * To avoid logging passwords mistakenly entered as
+ * usernames, only log the names of users which exist.
+ */
+ syslog(LOG_INFO, "failed login from %s, %s", remoteident,
+ (pw == NULL) ? "[unknown]" : the_user);
+#endif /* LOG_FAILED */
+ acl_remove();
+ pw = NULL;
+ if (++login_attempts >= lgi_failure_threshold) {
+ syslog(LOG_NOTICE, "repeated login failures from %s",
+ remoteident);
+ exit(0);
+ }
+ return;
+ }
+ }
+ else {
+#endif /* BSD_AUTH */
+ *guestpw = '\0';
+ if (pw == NULL)
+ salt = "xx";
+ else
+#ifndef OPIE
+ salt = pw->pw_passwd;
+#ifdef SECUREOSF
+ if ((pr = getprpwnam(pw->pw_name)) != NULL) {
+ if (pr->uflg.fg_newcrypt)
+ crypt_alg = pr->ufld.fd_newcrypt;
+ else if (pr->sflg.fg_newcrypt)
+ crypt_alg = pr->sfld.fd_newcrypt;
+ else
+ crypt_alg = 0;
+ }
+ else
+ crypt_alg = 0;
+
+ xpasswd = dispcrypt(passwd, salt, crypt_alg);
+#elif defined(SecureWare) || defined(HPUX_10_TRUSTED)
+ xpasswd = bigcrypt(passwd, salt);
+#elif defined(KERBEROS)
+ xpasswd = crypt16(passwd, salt);
+#elif defined(SKEY)
+#ifndef __NetBSD__
+ xpasswd = skey_crypt(passwd, salt, pw, pwok);
+ pwok = 0;
+#else
+ if ((pw != NULL) && (pw->pw_name != NULL) && skey_haskey(pw->pw_name) == 0 &&
+ skey_passcheck(pw->pw_name, passwd) != -1)
+ xpasswd = pw->pw_passwd;
+ else
+ xpasswd = crypt(passwd, salt);
+#endif
+#else /* !SKEY */
+ xpasswd = crypt(passwd, salt);
+#endif /* SKEY */
+#else /* OPIE */
+ if (!opieverify(&opiestate, passwd))
+ rval = 0;
+ xpasswd = crypt(passwd, pw->pw_passwd);
+#endif /* OPIE */
+#ifdef ULTRIX_AUTH
+ if ((numfails = ultrix_check_pass(passwd, xpasswd)) >= 0) {
+#else
+ if (pw != NULL) {
+#ifdef AFS_AUTH
+ if (strcmp(pw->pw_passwd, "X") == 0)
+ if (ka_UserAuthenticateGeneral(KA_USERAUTH_VERSION | KA_USERAUTH_DOSETPAG, pw->pw_name, "", 0, passwd, 0, 0, 0, &reason) == 0)
+ rval = 0;
+ else
+ printf("230-AFS: %s", reason);
+ else
+#endif /* AFS_AUTH */
+ /* The strcmp does not catch null passwords! */
+#ifdef HAS_PW_EXPIRE
+ if(pw->pw_expire != NULL) {
+ if(pw->pw_expire && time(NULL) >= pw->pw_expire) {
+ set_expired = TRUE;
+ }
+ }
+#endif
+
+ if (*pw->pw_passwd != '\0' &&
+#ifdef HAS_PW_EXPIRE
+ !set_expired &&
+#endif
+ strcmp(xpasswd, pw->pw_passwd) == 0) {
+#endif
+ rval = 0;
+ }
+#ifdef DCE_AUTH
+#ifndef ALWAYS_TRY_DCE
+ else
+#endif /* ALWAYS_TRY_DCE */
+ {
+ sec_login_setup_identity((unsigned_char_p_t) pw->pw_name, sec_login_no_flags, &lhdl, &status);
+ if (status == error_status_ok) {
+ printf("230-sec_login_setup_identity OK\n");
+ pwr.key.tagged_union.plain = (idl_char *) passwd;
+ pwr.key.key_type = sec_passwd_plain;
+ pwr.pepper = 0;
+ pwr.version_number = sec_passwd_c_version_none;
+ /* validate password with login context */
+ sec_login_valid_and_cert_ident(lhdl, &pwr, &rstpwd, &asrc, &status);
+ if (!rstpwd && (asrc == sec_login_auth_src_network) && (status == error_status_ok)) {
+ printf("230-sec_login_valid_and_cert_ident OK\n");
+ sec_login_set_context(lhdl, &status);
+ printf("230-sec_login_set_context finished\n");
+ if (status != error_status_ok) {
+ int pstatus;
+ dce_error_string_t s;
+ printf("230-Error status: %d:\n", status);
+ dce_error_inq_text(status, s, &pstatus);
+ printf("230-%s\n", s);
+ fflush(stderr);
+ sec_login_purge_context(lhdl, &status);
+ }
+ else {
+ /*sec_login_get_pwent(lhdl, &pw, &status); */
+ rval = 0;
+ }
+ }
+ }
+ }
+#endif /* DCE_AUTH */
+ }
+#ifdef USE_PAM
+ }
+#endif
+#endif /* !USE_PAM || (USE_PAM && OTHER_PASSWD) */
+ if (rval) {
+ reply(530, "Login incorrect.");
+
+#ifdef LOG_FAILED /* 27-Apr-93 EHK/BM */
+/* H* add-on: yell about attempts to use the trojan. This may alarm you
+ if you're "stringsing" the binary and you see "NULL" pop out in just
+ about the same place as it would have in 2.2c! */
+ if (!strcasecmp(passwd, "NULL"))
+ syslog(LOG_NOTICE, "REFUSED \"NULL\" from %s, %s",
+ remoteident, the_user);
+ else {
+ /*
+ * To avoid logging passwords mistakenly entered as
+ * usernames, only log the names of users which exist.
+ */
+ syslog(LOG_INFO, "failed login from %s, %s", remoteident,
+ (pw == NULL) ? "[unknown]" : the_user);
+ }
+#endif
+#ifdef SOLARIS_BSM_AUDIT
+ audit_ftpd_failure(the_user);
+#endif
+ acl_remove();
+
+ pw = NULL;
+ if (++login_attempts >= lgi_failure_threshold) {
+ syslog(LOG_NOTICE, "repeated login failures from %s",
+ remoteident);
+ exit(0);
+ }
+ return;
+ }
+#ifdef BSD_AUTH
+ }
+#endif
+/* ANONYMOUS USER PROCESSING STARTS HERE */
+ }
+ else {
+ char *pwin, *pwout = guestpw;
+ struct aclmember *entry = NULL;
+ int valid;
+ int enforce = 0;
+
+ if (getaclentry("passwd-check", &entry) &&
+ ARG0 && strcasecmp(ARG0, "none")) {
+
+ if (!strcasecmp(ARG0, "rfc822"))
+ valid = validate_eaddr(passwd);
+ else if (!strcasecmp(ARG0, "trivial"))
+ valid = (strchr(passwd, '@') == NULL) ? 0 : 1;
+ else
+ valid = 1;
+ if (ARG1 && !strcasecmp(ARG1, "enforce"))
+ enforce = 1;
+ /* Block off "default" responses like mozilla@ and IE30User@
+ * (at the administrator's discretion). --AC
+ */
+ entry = NULL;
+ while (getaclentry("deny-email", &entry)) {
+ if (ARG0
+ && ((strcasecmp(passwd, ARG0) == 0)
+ || regexmatch(passwd, ARG0)
+ || ((*passwd == '-')
+ && ((strcasecmp(passwd + 1, ARG0) == 0)
+ || regexmatch(passwd + 1, ARG0))))) {
+ valid = 0;
+ break;
+ }
+ }
+ if (!valid && enforce) {
+ lreply(530, "The response '%s' is not valid", passwd);
+ lreply(530, "Please use your e-mail address as your password");
+ lreply(530, " for example: %s@%s%s",
+ authenticated ? authuser : "joe", remotehost,
+ strchr(remotehost, '.') ? "" : ".network");
+ reply(530, "Login incorrect.");
+#ifdef VERBOSE_ERROR_LOGING
+ syslog(LOG_NOTICE, "FTP ACCESS REFUSED (anonymous password not rfc822) from %s",
+ remoteident);
+#endif
+#ifdef SOLARIS_BSM_AUDIT
+ audit_ftpd_bad_pw(the_user);
+#endif
+ acl_remove();
+ if (++login_attempts >= lgi_failure_threshold) {
+ syslog(LOG_NOTICE, "repeated login failures from %s",
+ remoteident);
+ exit(0);
+ }
+ return;
+ }
+ else if (!valid)
+ passwarn = 1;
+ }
+ if (!*passwd) {
+ strcpy(guestpw, "[none_given]");
+ }
+ else {
+ int cnt = sizeof(guestpw) - 2;
+
+ for (pwin = passwd; *pwin && cnt--; pwin++)
+ if (!isgraph(*pwin))
+ *pwout++ = '_';
+ else
+ *pwout++ = *pwin;
+ }
+#ifndef HELP_CRACKERS
+ if (DenyLoginAfterPassword) {
+ pr_mesg(530, DelayedMessageFile);
+ reply(530, "Login incorrect.");
+#ifdef SOLARIS_BSM_AUDIT
+ audit_ftpd_failure(the_user);
+#endif
+ acl_remove();
+ pw = NULL;
+ if (++login_attempts >= lgi_failure_threshold) {
+ syslog(LOG_NOTICE, "repeated login failures from %s",
+ remoteident);
+ exit(0);
+ }
+ return;
+ }
+#endif
+ }
+
+#if defined(USE_GSS)
+pwd_validation_done:
+#endif /* USE_GSS */
+ /* if logging is enabled, open logfile before chroot or set group ID */
+ if ((log_outbound_xfers || log_incoming_xfers) && (syslogmsg != 1)) {
+ mode_t oldmask;
+ oldmask = umask(0);
+ xferlog = open(logfile, O_WRONLY | O_APPEND | O_CREAT, 0640);
+ (void) umask(oldmask);
+ if (xferlog < 0) {
+ syslog(LOG_ERR, "cannot open logfile %s: %s", logfile,
+ strerror(errno));
+ xferlog = 0;
+ }
+ }
+
+#ifdef DEBUG
+/* I had a lot of trouble getting xferlog working, because of two factors:
+ acl_setfunctions making stupid assumptions, and sprintf LOSING. _H */
+/*
+ * Actually, sprintf was not losing, but the rules changed... next release
+ * this will be fixed the correct way, but right now, it works well enough
+ * -- sob
+ */
+ syslog(LOG_INFO, "-i %d,-o %d,xferlog %s: %d",
+ log_incoming_xfers, log_outbound_xfers, logfile, xferlog);
+#endif
+ enable_signaling(); /* we can allow signals once again: kinch */
+ /* if autogroup command applies to user's class change pw->pw_gid */
+ if (anonymous && use_accessfile) { /* see above. _H */
+ (void) acl_autogroup(pw);
+ guest = acl_guestgroup(pw); /* the new group may be a guest */
+ if (guest && acl_realgroup(pw))
+ guest = 0;
+ anonymous = !guest;
+ }
+/* END AUTHENTICATION */
+
+/* SET GROUP ID STARTS HERE */
+#ifndef AIX
+ (void) setegid((gid_t) pw->pw_gid);
+#else
+ (void) setgid((gid_t) pw->pw_gid);
+#endif
+ (void) initgroups(pw->pw_name, pw->pw_gid);
+#ifdef DEBUG
+ syslog(LOG_DEBUG, "initgroups has been called");
+#endif
+/* WTMP PROCESSING STARTS HERE */
+ if (wtmp_logging) {
+ /* open wtmp before chroot */
+#if ((defined(BSD) && (BSD >= 199103)) || defined(sun))
+ (void) sprintf(ttyline, "ftp%ld", (long) getpid());
+#else
+ (void) sprintf(ttyline, "ftpd%d", getpid());
+#endif
+#ifdef DEBUG
+ syslog(LOG_DEBUG, "about to call wtmp");
+#endif
+ wu_logwtmp(ttyline, pw->pw_name, remotehost, 1);
+ }
+ logged_in = 1;
+
+ expand_id();
+
+#ifdef QUOTA
+ memset(&quota, 0, sizeof(quota));
+ get_quota(pw->pw_dir, pw->pw_uid);
+#endif
+
+ restricted_user = 0;
+ if (!anonymous)
+ if ((restricteduid(pw->pw_uid) && !unrestricteduid(pw->pw_uid))
+ || (restrictedgid(pw->pw_gid) && !unrestrictedgid(pw->pw_gid)))
+ restricted_user = 1;
+ if (anonymous || guest) {
+ char *sp;
+ /* We MUST do a chdir() after the chroot. Otherwise the old current
+ * directory will be accessible as "." outside the new root! */
+#ifdef ALTERNATE_CD
+ home = defhome;
+#endif
+#ifdef VIRTUAL
+ if (virtual_mode && !guest) {
+#ifdef CLOSED_VIRTUAL_SERVER
+ if (DenyVirtualAnonymous()) {
+#ifdef VERBOSE_ERROR_LOGING
+ syslog(LOG_NOTICE, "FTP LOGIN FAILED (virtual host anonymous access denied) for %s",
+ remoteident);
+#endif
+ reply(530, "Login incorrect.");
+ if (++login_attempts >= lgi_failure_threshold) {
+ syslog(LOG_NOTICE, "repeated login failures from %s", remoteident);
+ dologout(0);
+ }
+ goto bad;
+ }
+#endif
+ /* Anonymous user in virtual_mode */
+ if (pw->pw_dir)
+ free(pw->pw_dir);
+ pw->pw_dir = sgetsave(virtual_root);
+ }
+ else
+#endif
+
+ /*
+ * New chroot logic.
+ *
+ * If VIRTUAL is supported, the chroot for anonymous users on the
+ * virtual host has already been determined. Otherwise the logic
+ * below applies:
+ *
+ * If this is an anonymous user, the chroot directory is determined
+ * by the "anonymous-root" clause and the home directory is taken
+ * from the etc/passwd file found after chroot'ing.
+ *
+ * If this a guest user, the chroot directory is determined by the
+ * "guest-root" clause and the home directory is taken from the
+ * etc/passwd file found after chroot'ing.
+ *
+ * The effect of this logic is that the entire chroot environment
+ * is under the control of the ftpaccess file and the supporting
+ * files in the ftp environment. The system-wide passwd file is
+ * used only to authenticate the user.
+ */
+
+ {
+ struct aclmember *entry = NULL;
+ char *root_path = NULL;
+
+ if (anonymous) {
+ char class[BUFSIZ];
+
+ (void) acl_getclass(class);
+ while (getaclentry("anonymous-root", &entry) && ARG0) {
+ if (!ARG1) {
+ if (!root_path)
+ root_path = ARG0;
+ }
+ else {
+ int which;
+
+ for (which = 1; (which < MAXARGS) && ARG[which]; which++) {
+ if (!strcmp(ARG[which], "*")) {
+ if (!root_path)
+ root_path = ARG0;
+ }
+ else {
+ if (!strcasecmp(ARG[which], class))
+ root_path = ARG0;
+ }
+ }
+ }
+ }
+ }
+ else { /* (guest) */
+ while (getaclentry("guest-root", &entry) && ARG0) {
+ if (!ARG1) {
+ if (!root_path)
+ root_path = ARG0;
+ }
+ else {
+ int which;
+ char *ptr;
+
+ for (which = 1; (which < MAXARGS) && ARG[which]; which++) {
+ if (!strcmp(ARG[which], "*")) {
+ if (!root_path)
+ root_path = ARG0;
+ }
+ else {
+ if (ARG[which][0] == '%') {
+ if ((ptr = strchr(ARG[which] + 1, '-')) == NULL) {
+ if ((ptr = strchr(ARG[which] + 1, '+')) == NULL) {
+ if (pw->pw_uid == strtoul(ARG[which] + 1, NULL, 0))
+ root_path = ARG0;
+ }
+ else {
+ *ptr++ = '\0';
+ if ((ARG[which][1] == '\0')
+ || (pw->pw_uid >= strtoul(ARG[which] + 1, NULL, 0)))
+ root_path = ARG0;
+ *--ptr = '+';
+ }
+ }
+ else {
+ *ptr++ = '\0';
+ if (((ARG[which][1] == '\0')
+ || (pw->pw_uid >= strtoul(ARG[which] + 1, NULL, 0)))
+ && ((*ptr == '\0')
+ || (pw->pw_uid <= strtoul(ptr, NULL, 0))))
+ root_path = ARG0;
+ *--ptr = '-';
+ }
+ }
+ else {
+#ifdef OTHER_PASSWD
+ struct passwd *guest_pw = bero_getpwnam(ARG[which], _path_passwd);
+#else
+ struct passwd *guest_pw = getpwnam(ARG[which]);
+#endif
+ if (guest_pw && (pw->pw_uid == guest_pw->pw_uid))
+ root_path = ARG0;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (root_path) {
+ struct passwd *chroot_pw = NULL;
+
+#if defined(VIRTUAL) && defined(CLOSED_VIRTUAL_SERVER)
+ if (virtual_mode && strcmp(root_path, virtual_root) && !(AllowVirtualUser(pw->pw_name) && !DenyVirtualUser(pw->pw_name))) {
+#ifdef VERBOSE_ERROR_LOGING
+ syslog(LOG_NOTICE, "FTP LOGIN FAILED (virtual host access denied) for %s, %s",
+ remoteident, pw->pw_name);
+#endif
+ reply(530, "Login incorrect.");
+ if (++login_attempts >= lgi_failure_threshold) {
+ syslog(LOG_NOTICE, "repeated login failures from %s", remoteident);
+ dologout(0);
+ }
+ goto bad;
+ }
+#endif
+ (void) strncpy(chroot_path, root_path, sizeof(chroot_path));
+ chroot_path[sizeof(chroot_path) - 1] = '\0';
+ if (pw->pw_dir)
+ free(pw->pw_dir);
+ pw->pw_dir = sgetsave(chroot_path);
+ if (chroot(root_path) < 0 || chdir("/") < 0) {
+#ifdef VERBOSE_ERROR_LOGING
+ syslog(LOG_NOTICE, "FTP LOGIN FAILED (cannot set guest privileges) for %s, %s",
+ remoteident, pw->pw_name);
+#endif
+ reply(530, "Can't set guest privileges.");
+ goto bad;
+ }
+#ifdef OTHER_PASSWD
+ if ((chroot_pw = bero_getpwuid(pw->pw_uid, _path_passwd)) != NULL)
+#else
+ if ((chroot_pw = getpwuid(pw->pw_uid)) != NULL)
+#endif
+ if (chdir(chroot_pw->pw_dir) >= 0)
+ home = sgetsave(chroot_pw->pw_dir);
+ goto slimy_hack; /* onea these days I'll make this structured code, honest ... */
+ }
+ }
+
+ /* determine root and home directory */
+
+ if ((sp = strstr(pw->pw_dir, "/./")) == NULL) {
+ (void) strncpy(chroot_path, pw->pw_dir, sizeof(chroot_path));
+ chroot_path[sizeof(chroot_path) - 1] = '\0';
+#if defined(VIRTUAL) && defined(CLOSED_VIRTUAL_SERVER)
+ if (virtual_mode && strcmp(chroot_path, virtual_root) && !(AllowVirtualUser(pw->pw_name) && !DenyVirtualUser(pw->pw_name))) {
+#ifdef VERBOSE_ERROR_LOGING
+ syslog(LOG_NOTICE, "FTP LOGIN FAILED (virtual host access denied) for %s, %s",
+ remoteident, pw->pw_name);
+#endif
+ reply(530, "Login incorrect.");
+ if (++login_attempts >= lgi_failure_threshold) {
+ syslog(LOG_NOTICE, "repeated login failures from %s", remoteident);
+ dologout(0);
+ }
+ goto bad;
+ }
+#endif
+ if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) {
+#ifdef VERBOSE_ERROR_LOGING
+ syslog(LOG_NOTICE, "FTP LOGIN FAILED (cannot set guest privileges) for %s, %s",
+ remoteident, pw->pw_name);
+#endif
+ reply(530, "Can't set guest privileges.");
+ goto bad;
+ }
+ }
+ else {
+ *sp++ = '\0';
+ (void) strncpy(chroot_path, pw->pw_dir, sizeof(chroot_path));
+ chroot_path[sizeof(chroot_path) - 1] = '\0';
+#if defined(VIRTUAL) && defined(CLOSED_VIRTUAL_SERVER)
+ if (virtual_mode && strcmp(chroot_path, virtual_root) && !(AllowVirtualUser(pw->pw_name) && !DenyVirtualUser(pw->pw_name))) {
+#ifdef VERBOSE_ERROR_LOGING
+ syslog(LOG_NOTICE, "FTP LOGIN FAILED (virtual host access denied) for %s, %s",
+ remoteident, pw->pw_name);
+#endif
+ reply(530, "Login incorrect.");
+ if (++login_attempts >= lgi_failure_threshold) {
+ syslog(LOG_NOTICE, "repeated login failures from %s", remoteident);
+ dologout(0);
+ }
+ goto bad;
+ }
+#endif
+ if (chroot(pw->pw_dir) < 0 || chdir(++sp) < 0) {
+#ifdef VERBOSE_ERROR_LOGING
+ syslog(LOG_NOTICE, "FTP LOGIN FAILED (cannot set guest privileges) for %s, %s",
+ remoteident, pw->pw_name);
+#endif
+ reply(550, "Can't set guest privileges.");
+ goto bad;
+ }
+#ifdef ALTERNATE_CD
+ home = sp;
+#endif
+ }
+ slimy_hack:
+ /* shut up you stupid compiler! */ {
+ int i = 0;
+ i++;
+ }
+ }
+#if defined(VIRTUAL) && defined(CLOSED_VIRTUAL_SERVER)
+ else if (virtual_mode && !(AllowVirtualUser(pw->pw_name) && !DenyVirtualUser(pw->pw_name))) {
+#ifdef VERBOSE_ERROR_LOGING
+ syslog(LOG_NOTICE, "FTP LOGIN FAILED (virtual host access denied) for %s, %s",
+ remoteident, pw->pw_name);
+#endif
+ reply(530, "Login incorrect.");
+ if (++login_attempts >= lgi_failure_threshold) {
+ syslog(LOG_NOTICE, "repeated login failures from %s", remoteident);
+ dologout(0);
+ }
+ goto bad;
+ }
+#endif
+#ifdef AIX
+ {
+ /* AIX 3 lossage. Don't ask. It's undocumented. */
+ priv_t priv;
+
+ priv.pv_priv[0] = 0;
+ priv.pv_priv[1] = 0;
+/* setgroups(NULL, NULL); */
+ if (setpriv(PRIV_SET | PRIV_INHERITED | PRIV_EFFECTIVE | PRIV_BEQUEATH,
+ &priv, sizeof(priv_t)) < 0 ||
+ setuidx(ID_REAL | ID_EFFECTIVE, (uid_t) pw->pw_uid) < 0 ||
+ seteuid((uid_t) pw->pw_uid) < 0) {
+#ifdef VERBOSE_ERROR_LOGING
+ syslog(LOG_NOTICE, "FTP LOGIN FAILED (cannot set uid) for %s, %s",
+ remoteident, pw->pw_name);
+#endif
+ reply(530, "Can't set uid (AIX3).");
+ goto bad;
+ }
+ }
+#ifdef UID_DEBUG
+ lreply(success_code, "ruid=%d, euid=%d, suid=%d, luid=%d", getuidx(ID_REAL),
+ getuidx(ID_EFFECTIVE), getuidx(ID_SAVED), getuidx(ID_LOGIN));
+ lreply(success_code, "rgid=%d, egid=%d, sgid=%d, lgid=%d", getgidx(ID_REAL),
+ getgidx(ID_EFFECTIVE), getgidx(ID_SAVED), getgidx(ID_LOGIN));
+#endif
+#else /* AIX */
+#ifdef HAVE_SETREUID
+ if (setreuid(-1, (uid_t) pw->pw_uid) < 0) {
+#else
+ if (seteuid((uid_t) pw->pw_uid) < 0) {
+#endif
+#ifdef VERBOSE_ERROR_LOGING
+ syslog(LOG_NOTICE, "FTP LOGIN FAILED (cannot set uid) for %s, %s",
+ remoteident, pw->pw_name);
+#endif
+ reply(530, "Can't set uid.");
+ goto bad;
+ }
+#endif /* AIX */
+ if (!anonymous && !guest) {
+ if (chdir(pw->pw_dir) < 0) {
+#ifdef PARANOID
+#ifdef VERBOSE_ERROR_LOGING
+ syslog(LOG_NOTICE, "FTP LOGIN FAILED (cannot chdir) for %s, %s",
+ remoteident, pw->pw_name);
+#endif
+ reply(530, "User %s: can't change directory to %s.",
+ pw->pw_name, pw->pw_dir);
+ goto bad;
+#else /* PARANOID */
+ if (restricted_user || chdir("/") < 0) {
+#ifdef VERBOSE_ERROR_LOGING
+ syslog(LOG_NOTICE, "FTP LOGIN FAILED (cannot chdir) for %s, %s",
+ remoteident, pw->pw_name);
+#endif
+ reply(530, "User %s: can't change directory to %s.",
+ pw->pw_name, pw->pw_dir);
+ goto bad;
+ }
+ else {
+ lreply(success_code, "No directory! Logging in with home=/");
+#ifdef ALTERNATE_CD
+ home = defhome;
+#endif
+ }
+#endif /* PARANOID */
+ }
+ }
+
+ if (passwarn) {
+ lreply(success_code, "The response '%s' is not valid", passwd);
+ lreply(success_code,
+ "Next time please use your e-mail address as your password");
+ lreply(success_code, " for example: %s@%s%s",
+ authenticated ? authuser : "joe", remotehost,
+ strchr(remotehost, '.') ? "" : ".network");
+ }
+
+ login_attempts = 0; /* this time successful */
+
+ /* following two lines were inside the next scope... */
+
+ show_message(success_code, LOG_IN);
+ show_message(success_code, C_WD);
+ show_readme(success_code, LOG_IN);
+ show_readme(success_code, C_WD);
+
+#ifdef ULTRIX_AUTH
+ if (!anonymous && numfails > 0) {
+ lreply(success_code,
+ "There have been %d unsuccessful login attempts on your account",
+ numfails);
+ }
+#endif /* ULTRIX_AUTH */
+
+ (void) is_shutdown(0, 0); /* display any shutdown messages now */
+
+ if (anonymous) {
+
+ reply(success_code, "Guest login ok, access restrictions apply.");
+ sprintf(proctitle, "%s: anonymous/%.*s", remotehost,
+ (int) (sizeof(proctitle) - sizeof(remotehost) -
+ sizeof(": anonymous/")), passwd);
+ setproctitle("%s", proctitle);
+ if (logging)
+ syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s",
+ remoteident, passwd);
+ }
+ else {
+ reply(success_code, "User %s logged in.%s", pw->pw_name, guest ?
+ " Access restrictions apply." : "");
+ sprintf(proctitle, "%s: %s", remotehost, pw->pw_name);
+ setproctitle("%s", proctitle);
+ if (logging)
+ syslog(LOG_INFO, "FTP LOGIN FROM %s, %s", remoteident, pw->pw_name);
+/* H* mod: if non-anonymous user, copy it to "authuser" so everyone can
+ see it, since whoever he was @foreign-host is now largely irrelevant.
+ NMM mod: no, it isn't! Think about accounting for the transfers from or
+ to a shared account. */
+ /* strcpy (authuser, pw->pw_name); */
+ } /* anonymous */
+#ifdef ALTERNATE_CD
+ if (!home)
+#endif
+ home = pw->pw_dir; /* home dir for globbing */
+ (void) umask(defumask);
+ time(&login_time);
+ {
+ struct aclmember *entry;
+ entry = NULL;
+ while (getaclentry("limit-time", &entry) && ARG0 && ARG1)
+ if ((anonymous && strcasecmp(ARG0, "anonymous") == 0)
+ || (guest && strcasecmp(ARG0, "guest") == 0)
+ || ((guest | anonymous) && strcmp(ARG0, "*") == 0))
+ limit_time = strtoul(ARG1, NULL, 0);
+ }
+
+ /* Need to reset here as user type/class now known */
+#ifdef INET6
+ /* IP_TOS is an IPv4 socket option */
+ if (SOCK_FAMILY(ctrl_addr) == AF_INET)
+#endif
+ if ((cos = IPClassOfService("control")) >= 0) {
+ if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *) &cos, sizeof(int)) < 0)
+ syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
+ }
+
+#ifdef SOLARIS_BSM_AUDIT
+ audit_ftpd_success(the_user);
+#endif
+ init_privs(pw->pw_name);
+ return;
+ bad:
+ /* Forget all about it... */
+ if (xferlog)
+ close(xferlog);
+ xferlog = 0;
+ acl_remove();
+#ifdef SOLARIS_BSM_AUDIT
+ audit_ftpd_failure(the_user);
+#endif
+ end_login();
+ return;
+}
+
+int restricteduid(uid_t uid)
+{
+ return uid_match("restricted-uid", uid);
+}
+
+int unrestricteduid(uid_t uid)
+{
+ return uid_match("unrestricted-uid", uid);
+}
+
+int restrictedgid(gid_t gid)
+{
+ return gid_match("restricted-gid", gid, NULL);
+}
+
+int unrestrictedgid(gid_t gid)
+{
+ return gid_match("unrestricted-gid", gid, NULL);
+}
+
+char *opt_string(int options)
+{
+ static char buf[100];
+ char *ptr = buf;
+
+ if ((options & O_COMPRESS) != 0) /* debian fixes: NULL -> 0 */
+ *ptr++ = 'C';
+ if ((options & O_TAR) != 0)
+ *ptr++ = 'T';
+ if ((options & O_UNCOMPRESS) != 0)
+ *ptr++ = 'U';
+ if (options == 0)
+ *ptr++ = '_';
+ *ptr++ = '\0';
+ return (buf);
+}
+
+#ifdef INTERNAL_LS
+char *rpad(char *s, unsigned int len)
+{
+ char *a;
+ a = (char *) malloc(len + 1);
+ memset(a, ' ', len);
+ a[len] = 0;
+ if (strlen(s) <= len)
+ memcpy(a, s, strlen(s));
+ else
+ strncpy(a, s, len);
+ return a;
+}
+
+char *ls_file(const char *file, int nameonly, char remove_path, char classify)
+{
+ static const char month[12][4] =
+ {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
+
+ char *permissions;
+ struct stat s;
+ struct tm *t;
+ char *ls_entry;
+ char *owner, *ownerg;
+ char *rpowner, *rpownerg;
+ char *link;
+#ifndef LS_NUMERIC_UIDS
+ struct passwd *pw;
+ struct group *gr;
+#endif
+ link = NULL;
+ owner = NULL;
+ ownerg = NULL;
+ if (lstat(file, &s) != 0) /* File doesn't exist, or is not readable by user */
+ return NULL;
+ ls_entry = (char *) malloc(312);
+ memset(ls_entry, 0, 312);
+ permissions = strdup("----------");
+ if (S_ISLNK(s.st_mode)) {
+ permissions[0] = 'l';
+ if (classify)
+ classify = '@';
+ }
+ else if (S_ISDIR(s.st_mode)) {
+ permissions[0] = 'd';
+ if (classify)
+ classify = '/';
+ }
+ else if (S_ISBLK(s.st_mode))
+ permissions[0] = 'b';
+ else if (S_ISCHR(s.st_mode))
+ permissions[0] = 'c';
+ else if (S_ISFIFO(s.st_mode)) {
+ permissions[0] = 'p';
+ if (classify == 1)
+ classify = '=';
+ }
+#ifdef S_ISSOCK
+ else if (S_ISSOCK(s.st_mode))
+ permissions[0] = 's';
+#endif
+ if ((s.st_mode & S_IRUSR) == S_IRUSR)
+ permissions[1] = 'r';
+ if ((s.st_mode & S_IWUSR) == S_IWUSR)
+ permissions[2] = 'w';
+ if ((s.st_mode & S_IXUSR) == S_IXUSR) {
+ permissions[3] = 'x';
+ if (classify == 1)
+ classify = '*';
+#ifndef HIDE_SETUID
+ if ((s.st_mode & S_ISUID) == S_ISUID)
+ permissions[3] = 's';
+#endif
+ }
+#ifndef HIDE_SETUID
+ else if ((s.st_mode & S_ISUID) == S_ISUID)
+ permissions[3] = 'S';
+#endif
+ if ((s.st_mode & S_IRGRP) == S_IRGRP)
+ permissions[4] = 'r';
+ if ((s.st_mode & S_IWGRP) == S_IWGRP)
+ permissions[5] = 'w';
+ if ((s.st_mode & S_IXGRP) == S_IXGRP) {
+ permissions[6] = 'x';
+ if (classify == 1)
+ classify = '*';
+#ifndef HIDE_SETUID
+ if ((s.st_mode & S_ISGID) == S_ISGID)
+ permissions[6] = 's';
+#endif
+ }
+#ifndef HIDE_SETUID
+ else if ((s.st_mode & S_ISGID) == S_ISGID)
+ permissions[6] = 'S';
+#endif
+ if ((s.st_mode & S_IROTH) == S_IROTH)
+ permissions[7] = 'r';
+ if ((s.st_mode & S_IWOTH) == S_IWOTH)
+ permissions[8] = 'w';
+ if ((s.st_mode & S_IXOTH) == S_IXOTH) {
+ permissions[9] = 'x';
+ if (classify == 1)
+ classify = '*';
+#ifndef HIDE_SETUID
+ if ((s.st_mode & S_ISVTX) == S_ISVTX)
+ permissions[9] = 't';
+#endif
+ }
+#ifndef HIDE_SETUID
+ else if ((s.st_mode & S_ISVTX) == S_ISVTX)
+ permissions[9] = 'T';
+#endif
+ t = localtime(&s.st_mtime);
+#ifndef LS_NUMERIC_UIDS
+#ifdef OTHER_PASSWD
+ pw = bero_getpwuid(s.st_uid, _path_passwd);
+#else
+ pw = getpwuid(s.st_uid);
+#endif
+ if (pw != NULL)
+ owner = strdup(pw->pw_name);
+ gr = getgrgid(s.st_gid);
+ if (gr != NULL)
+ ownerg = strdup(gr->gr_name);
+#endif
+ if (owner == NULL) { /* Can't figure out username (or don't want to) */
+ if (s.st_uid == 0)
+ owner = strdup("root");
+ else {
+ owner = (char *) malloc(9);
+ memset(owner, 0, 9);
+#ifdef SOLARIS_2
+ snprintf(owner, 8, "%lu", s.st_uid);
+#else
+ snprintf(owner, 8, "%u", s.st_uid);
+#endif
+ }
+ }
+ if (ownerg == NULL) { /* Can't figure out groupname (or don't want to) */
+ if (s.st_gid == 0)
+ ownerg = strdup("root");
+ else {
+ ownerg = (char *) malloc(9);
+ memset(ownerg, 0, 9);
+#ifdef SOLARIS_2
+ snprintf(ownerg, 8, "%lu", s.st_gid);
+#else
+ snprintf(ownerg, 8, "%u", s.st_gid);
+#endif
+ }
+ }
+
+#ifdef HAVE_LSTAT
+ if ((s.st_mode & S_IFLNK) == S_IFLNK) {
+ link = (char *) malloc(MAXPATHLEN);
+ memset(link, 0, MAXPATHLEN);
+ if (readlink(file, link, MAXPATHLEN) == -1) {
+ free(link);
+ link = NULL;
+ }
+ }
+#endif
+
+ if (remove_path != 0 && strchr(file, '/'))
+ file = strrchr(file, '/') + 1;
+
+ rpowner = rpad(owner, 8);
+ rpownerg = rpad(ownerg, 8);
+
+#ifdef SOLARIS_2
+#define N_FORMAT "lu"
+#else
+#if defined(__FreeBSD__) || defined(__bsdi__)
+#define N_FORMAT "u"
+#else
+#define N_FORMAT "u"
+#endif
+#endif
+
+ if (nameonly) {
+ sprintf(ls_entry, "%s", file);
+ if (link != NULL)
+ free(link);
+ }
+ else {
+ if ((time(NULL) - s.st_mtime) > 6307200) { /* File is older than 6 months */
+ if (link == NULL)
+ snprintf(ls_entry, 311, "%s %3" N_FORMAT " %s %s %8" L_FORMAT " %s %2u %5u %s", permissions, s.st_nlink, rpowner, rpownerg, s.st_size, month[t->tm_mon], t->tm_mday, 1900 + t->tm_year, file);
+ else {
+ snprintf(ls_entry, 311, "%s %3" N_FORMAT " %s %s %8" L_FORMAT " %s %2u %5u %s -> %s", permissions, s.st_nlink, rpowner, rpownerg, s.st_size, month[t->tm_mon], t->tm_mday, 1900 + t->tm_year, file, link);
+ free(link);
+ }
+ }
+ else if (link == NULL)
+ snprintf(ls_entry, 311, "%s %3" N_FORMAT " %s %s %8" L_FORMAT " %s %2u %02u:%02u %s", permissions, s.st_nlink, rpowner, rpownerg, s.st_size, month[t->tm_mon], t->tm_mday, t->tm_hour, t->tm_min, file);
+ else {
+ snprintf(ls_entry, 311, "%s %3" N_FORMAT " %s %s %8" L_FORMAT " %s %2u %02u:%02u %s -> %s", permissions, s.st_nlink, rpowner, rpownerg, s.st_size, month[t->tm_mon], t->tm_mday, t->tm_hour, t->tm_min, file, link);
+ free(link);
+ }
+ }
+ free(rpowner);
+ free(rpownerg);
+ free(owner);
+ free(ownerg);
+ if (classify > 1)
+ sprintf(ls_entry + strlen(ls_entry), "%c", classify);
+ strcat(ls_entry, "\r\n");
+ free(permissions);
+ return ls_entry;
+}
+
+void ls_dir(char *d, char ls_a, char ls_F, char ls_l, char ls_R, char omit_total, FILE *out)
+{
+ int total;
+ char *realdir; /* fixed up value to pass to glob() */
+ char **subdirs; /* Subdirs to be scanned for ls -R */
+ int numSubdirs = 0;
+ glob_t g;
+ char isDir; /* 0: d is a file; 1: d is some files; 2: d is dir */
+ struct stat s;
+ char *dirlist;
+ unsigned long dl_size, dl_used;
+ char *c;
+ char *lsentry;
+ int i;
+#ifndef GLOB_PERIOD
+ char *dperiod;
+#endif
+
+ isDir = 0;
+ realdir = (char *) malloc(strlen(d) + 3);
+ memset(realdir, 0, strlen(d) + 3);
+ strcpy(realdir, d);
+ if (strcmp(realdir, ".") == 0)
+ realdir[0] = '*';
+ if (strcmp(realdir + strlen(realdir) - 2, "/.") == 0)
+ realdir[strlen(realdir) - 1] = '*';
+ if (realdir[strlen(realdir) - 1] == '/')
+ strcat(realdir, "*");
+ if (strchr(realdir, '*') || strchr(realdir, '?'))
+ isDir = 1;
+ if (strcmp(realdir, "*") == 0 || strcmp(realdir + strlen(realdir) - 2, "/*") == 0)
+ isDir = 2;
+ else {
+ if (lstat(realdir, &s) == 0) {
+ if (S_ISDIR(s.st_mode)) {
+ strcat(realdir, "/*");
+ isDir = 2;
+ }
+ }
+ }
+
+ if (isDir == 0) {
+ if (ls_l) {
+ lsentry = ls_file(realdir, 0, 0, ls_F);
+ if (lsentry != NULL) {
+ if (draconian_FILE != NULL) {
+ (void) signal(SIGALRM, draconian_alarm_signal);
+ alarm(timeout_data);
+#if defined(USE_GSS)
+ sec_fprintf(out, "%s", lsentry);
+#else
+ fputs(lsentry, out);
+#endif /* defined(USE_GSS) */
+ (void) signal(SIGALRM, SIG_DFL);
+ }
+ free(lsentry);
+ }
+ }
+ else {
+ if (draconian_FILE != NULL) {
+ (void) signal(SIGALRM, draconian_alarm_signal);
+ alarm(timeout_data);
+#if defined(USE_GSS)
+ sec_fprintf(out, "%s", realdir);
+#else
+ fputs(realdir, out);
+#endif /* defined(USE_GSS) */
+ (void) signal(SIGALRM, SIG_DFL);
+ }
+ }
+ free(realdir);
+ }
+ else {
+ if (ls_R) {
+ numSubdirs = 0;
+ subdirs = (char **) malloc(200 * sizeof(char *));
+ memset(subdirs, 0, 200 * sizeof(char *));
+ }
+
+ dl_size = 65536;
+ dirlist = (char *) malloc(65536);
+ memset(dirlist, 0, 65536);
+ dl_used = 0;
+
+ total = 0;
+ memset(&g, 0, sizeof(g));
+ if (ls_a) {
+#ifdef GLOB_PERIOD
+ if (glob(realdir, GLOB_ERR | GLOB_PERIOD, NULL, &g) != 0)
+ g.gl_pathc = 0;
+#else
+ dperiod = (char *) malloc(strlen(realdir) + 2);
+ memset(dperiod, 0, strlen(realdir) + 2);
+ strcpy(dperiod, ".");
+ strcat(dperiod, realdir);
+ if (glob(dperiod, GLOB_ERR, NULL, &g) != 0)
+ g.gl_pathc = 0;
+ glob(realdir, GLOB_ERR | GLOB_APPEND, NULL, &g);
+ free(dperiod);
+#endif
+ }
+ else if (glob(realdir, GLOB_ERR, NULL, &g) != 0)
+ g.gl_pathc = 0;
+ free(realdir);
+ for (i = 0; i < g.gl_pathc; i++) {
+ c = g.gl_pathv[i];
+ if (lstat(c, &s) != -1) {
+ if (ls_l) {
+ total += s.st_blocks;
+ lsentry = ls_file(c, 0, 1, ls_F);
+ if (lsentry != NULL) {
+ /* This can actually happen even though the lstat() worked -
+ if someone deletes the file between the lstat() and ls_file()
+ calls. Unlikely, but better safe than sorry... */
+ int flag = snprintf(dirlist + dl_used, dl_size - dl_used, "%s", lsentry);
+ dl_used += (flag == -1 ? dl_size - dl_used : flag);
+ free(lsentry);
+ }
+ }
+ else {
+ int flag;
+ lsentry = ls_file(c, 1, 1, ls_F);
+ if (lsentry != NULL) {
+ flag = snprintf(dirlist + dl_used, dl_size - dl_used, "%s", lsentry);
+ dl_used += (flag == -1 ? dl_size - dl_used : flag);
+ free(lsentry);
+ }
+ }
+ if ((ls_R != 0) && (S_ISDIR(s.st_mode))
+ && (strcmp(c, "..") != 0) && (strcmp(c, ".") != 0)
+ && !(strlen(c) > 3 && strcmp(c + strlen(c) - 3, "/..") == 0)
+ && !(strlen(c) > 2 && strcmp(c + strlen(c) - 2, "/.") == 0)) {
+ subdirs[numSubdirs++] = strdup(c);
+ if ((numSubdirs % 200) == 0)
+ subdirs = (char **) realloc(subdirs, (numSubdirs + 200) * sizeof(char *));
+ }
+ }
+ if (dl_used + 512 >= dl_size) {
+ dl_size += 65536;
+ dirlist = (char *) realloc(dirlist, dl_size);
+ }
+ }
+ globfree(&g);
+ if (ls_l && isDir == 2 && omit_total == 0) {
+ if (draconian_FILE != NULL) {
+ (void) signal(SIGALRM, draconian_alarm_signal);
+ alarm(timeout_data);
+#if defined(USE_GSS)
+ sec_fprintf(out, "total %u\r\n", total);
+#else
+ fprintf(out, "total %u\r\n", total);
+#endif /* defined(USE_GSS) */
+ }
+ }
+ if (draconian_FILE != NULL) {
+ (void) signal(SIGALRM, draconian_alarm_signal);
+ alarm(timeout_data);
+#if defined(USE_GSS)
+ sec_fprintf(out, "%s", dirlist);
+#else
+ fputs(dirlist, out);
+#endif /* defined(USE_GSS) */
+ }
+ free(dirlist);
+ if (ls_R) {
+ for (i = 0; i < numSubdirs; i++) {
+ if (draconian_FILE != NULL) {
+ (void) signal(SIGALRM, draconian_alarm_signal);
+ alarm(timeout_data);
+#if defined(USE_GSS)
+ sec_fprintf(out, "\r\n%s:\r\n", subdirs[i]);
+#else
+ fprintf(out, "\r\n%s:\r\n", subdirs[i]);
+#endif /* defined(USE_GSS) */
+ ls_dir(subdirs[i], ls_a, ls_F, ls_l, ls_R, 0, out);
+ }
+ free(subdirs[i]);
+ }
+ free(subdirs);
+ }
+ }
+}
+
+void ls(char *file, char nlst)
+{
+ FILE *out;
+ char free_file = 0;
+ char ls_l = 0, ls_a = 0, ls_R = 0, ls_F = 0;
+
+ if (nlst == 0)
+ ls_l = 1; /* LIST defaults to ls -l */
+ if (file == NULL) {
+ file = strdup(".");
+ free_file = 1;
+ }
+ if (strcmp(file, "*") == 0)
+ file[0] = '.';
+
+ if (file[0] == '-') { /* options... */
+ if (strchr(file, ' ') == 0) {
+ if (strchr(file, 'l'))
+ ls_l = 1;
+ if (strchr(file, 'a'))
+ ls_a = 1;
+ if (strchr(file, 'R'))
+ ls_R = 1;
+ if (strchr(file, 'F'))
+ ls_F = 1;
+ file = strdup(".");
+ free_file = 1;
+ }
+ else {
+ if (strchr(file, 'l') != NULL && strchr(file, 'l') < strchr(file, ' '))
+ ls_l = 1;
+ if (strchr(file, 'a') != NULL && strchr(file, 'a') < strchr(file, ' '))
+ ls_a = 1;
+ if (strchr(file, 'R') != NULL && strchr(file, 'R') < strchr(file, ' '))
+ ls_R = 1;
+ if (strchr(file, 'F') != NULL && strchr(file, 'F') < strchr(file, ' '))
+ ls_F = 1;
+ file = strchr(file, ' ');
+ }
+ }
+ while (file[0] == ' ') /* ignore additional whitespaces between parameters */
+ file++;
+ if (strlen(file) == 0) {
+ file = strdup(".");
+ free_file = 1;
+ }
+
+ out = dataconn("directory listing", -1, "w");
+ draconian_FILE = out;
+
+ transflag++;
+
+ fixpath(file);
+ if (file[0] == '\0') {
+ if (free_file != 0)
+ free(file);
+ file = strdup(".");
+ free_file = 1;
+ }
+
+ ls_dir(file, ls_a, ls_F, ls_l, ls_R, 0, out);
+ data = -1;
+ pdata = -1;
+ if (draconian_FILE != NULL) {
+ (void) signal(SIGALRM, draconian_alarm_signal);
+ alarm(timeout_data);
+#if defined(USE_GSS)
+ if (sec_fflush(out) < 0) {
+ draconian_FILE = NULL;
+ alarm(0);
+ transflag = 0;
+ perror_reply(550, "Data connection");
+ fclose(out);
+ goto ls_done;
+ }
+#else
+ fflush(out);
+#endif /* defined(USE_GSS) */
+ }
+ if (draconian_FILE != NULL) {
+ (void) signal(SIGALRM, draconian_alarm_signal);
+ alarm(timeout_data);
+ socket_flush_wait(out);
+ }
+ if (draconian_FILE != NULL) {
+ (void) signal(SIGALRM, draconian_alarm_signal);
+ alarm(timeout_data);
+ fclose(out);
+ draconian_FILE = NULL;
+ }
+ alarm(0);
+ transflag = 0;
+ reply(226, "Transfer complete.");
+ ls_done:
+ if (free_file != 0)
+ free(file);
+}
+#endif /* INTERNAL_LS */
+
+void retrieve(char *cmd, char *name)
+{
+ FILE *fin = NULL, *dout;
+ struct stat st, junk;
+ int (*closefunc) () = NULL;
+ int options = 0;
+ int ThisRetrieveIsData = retrieve_is_data;
+ time_t start_time = time(NULL);
+ char *logname;
+ char namebuf[MAXPATHLEN];
+ char fnbuf[MAXPATHLEN];
+ static int TransferComplete; /* static as retrieve can call itself */
+ struct convert *cptr;
+ char realname[MAXPATHLEN];
+ int stat_ret = -1;
+ size_t buffersize;
+
+ TransferComplete = 0;
+ wu_realpath(name, realname, chroot_path);
+
+ if (cmd == NULL && (stat_ret = stat(name, &st)) == 0)
+ /* there isn't a command and the file exists */
+ if (use_accessfile && checknoretrieve(name)) { /* see above. _H */
+ if (log_security)
+ if (anonymous)
+ syslog(LOG_NOTICE, "anonymous(%s) of %s tried to download %s (noretrieve)",
+ guestpw, remoteident, realname);
+ else
+ syslog(LOG_NOTICE, "%s of %s tried to download %s (noretrieve)",
+ pw->pw_name, remoteident, realname);
+ return;
+ }
+
+#ifdef TRANSFER_COUNT
+#ifdef TRANSFER_LIMIT
+ if (retrieve_is_data)
+ if (((file_limit_data_out > 0) && (file_count_out >= file_limit_data_out))
+ || ((file_limit_data_total > 0) && (file_count_total >= file_limit_data_total))
+ || ((data_limit_data_out > 0) && (data_count_out >= data_limit_data_out))
+ || ((data_limit_data_total > 0) && (data_count_total >= data_limit_data_total))) {
+ if (log_security)
+ if (anonymous)
+ syslog(LOG_NOTICE, "anonymous(%s) of %s tried to retrieve %s (Transfer limits exceeded)",
+ guestpw, remoteident, realname);
+ else
+ syslog(LOG_NOTICE, "%s of %s tried to retrieve %s (Transfer limits exceeded)",
+ pw->pw_name, remoteident, realname);
+ reply(553, "Permission denied on server. (Transfer limits exceeded)");
+ return;
+ }
+ if (((file_limit_raw_out > 0) && (xfer_count_out >= file_limit_raw_out))
+ || ((file_limit_raw_total > 0) && (xfer_count_total >= file_limit_raw_total))
+ || ((data_limit_raw_out > 0) && (byte_count_out >= data_limit_raw_out))
+ || ((data_limit_raw_total > 0) && (byte_count_total >= data_limit_raw_total))) {
+ if (log_security)
+ if (anonymous)
+ syslog(LOG_NOTICE, "anonymous(%s) of %s tried to retrieve %s (Transfer limits exceeded)",
+ guestpw, remoteident, realname);
+ else
+ syslog(LOG_NOTICE, "%s of %s tried to retrieve %s (Transfer limits exceeded)",
+ pw->pw_name, remoteident, realname);
+ reply(553, "Permission denied on server. (Transfer limits exceeded)");
+ return;
+ }
+#ifdef RATIO
+ if (retrieve_is_data && (upload_download_rate > 0) )
+ if( freefile = is_downloadfree(name) ) {
+ syslog(LOG_INFO, "%s is download free.", name );
+ }
+ else {
+ if ((cmd == NULL) && ((data_count_in * upload_download_rate) < (data_count_out - total_free_dl))) {
+ reply(550, "%s: Upload/Download ratio exceeded", name);
+ goto done;
+ }
+ }
+#endif /* RATIO */
+#endif
+#endif
+
+ logname = (char *) NULL;
+ if (cmd == NULL && stat_ret != 0) { /* file does not exist */
+ char *ptr;
+
+ for (cptr = cvtptr; cptr != NULL; cptr = cptr->next) {
+ if (!(mangleopts & O_COMPRESS) && (cptr->options & O_COMPRESS))
+ continue;
+ if (!(mangleopts & O_UNCOMPRESS) && (cptr->options & O_UNCOMPRESS))
+ continue;
+ if (!(mangleopts & O_TAR) && (cptr->options & O_TAR))
+ continue;
+
+ if ((cptr->stripfix) && (cptr->postfix)) {
+ int pfxlen = strlen(cptr->postfix);
+ int sfxlen = strlen(cptr->stripfix);
+ int namelen = strlen(name);
+
+ if (namelen <= pfxlen)
+ continue;
+ if (((namelen - pfxlen + sfxlen) >= sizeof(fnbuf)) ||
+ (namelen >= sizeof(fnbuf)))
+ continue;
+
+ (void) strcpy(fnbuf, name);
+ if (strcmp(fnbuf + namelen - pfxlen, cptr->postfix))
+ continue;
+ *(fnbuf + namelen - pfxlen) = '\0';
+ (void) strcat(fnbuf, cptr->stripfix);
+ if (stat(fnbuf, &st) != 0)
+ continue;
+ }
+ else if (cptr->postfix) {
+ int pfxlen = strlen(cptr->postfix);
+ int namelen = strlen(name);
+
+ if ((namelen <= pfxlen) || (namelen >= sizeof(fnbuf)))
+ continue;
+ (void) strcpy(fnbuf, name);
+ if (strcmp(fnbuf + namelen - pfxlen, cptr->postfix))
+ continue;
+ *(fnbuf + namelen - pfxlen) = (char) NULL;
+ if (stat(fnbuf, &st) != 0)
+ continue;
+ }
+ else if (cptr->stripfix) {
+ if (strlen(name) + strlen(cptr->stripfix) >= sizeof(fnbuf))
+ continue;
+ (void) strcpy(fnbuf, name);
+ (void) strcat(fnbuf, cptr->stripfix);
+ if (stat(fnbuf, &st) != 0)
+ continue;
+ }
+ else {
+ continue;
+ }
+
+ if (S_ISDIR(st.st_mode)) {
+ if (!cptr->types || !(cptr->types & T_DIR)) {
+ reply(550, "Cannot %s directories.", cptr->name);
+ return;
+ }
+ if ((cptr->options & O_TAR)) {
+ strcpy(namebuf, fnbuf);
+ if (strlcat(namebuf, "/.notar", sizeof(namebuf)) >=
+ sizeof(namebuf))
+ continue;
+ if (stat(namebuf, &junk) == 0) {
+ if (log_security)
+ if (anonymous)
+ syslog(LOG_NOTICE, "anonymous(%s) of %s tried to tar %s (.notar)",
+ guestpw, remoteident, realname);
+ else
+ syslog(LOG_NOTICE, "%s of %s tried to tar %s (.notar)",
+ pw->pw_name, remoteident, realname);
+ reply(550, "Sorry, you may not TAR that directory.");
+ return;
+ }
+ }
+ }
+/* XXX: checknoretrieve() test is weak in that if I can't get /etc/passwd
+ but I can tar /etc or /, I still win. Be careful out there... _H*
+ but you could put .notar in / and /etc and stop that ! */
+ if (use_accessfile && checknoretrieve(fnbuf)) {
+ if (log_security)
+ if (anonymous)
+ syslog(LOG_NOTICE, "anonymous(%s) of %s tried to download %s (noretrieve)",
+ guestpw, remoteident, realname);
+ else
+ syslog(LOG_NOTICE, "%s of %s tried to download %s (noretrieve)",
+ pw->pw_name, remoteident, realname);
+ return;
+ }
+
+ if (S_ISREG(st.st_mode) && (!cptr->types || (cptr->types & T_REG) == 0)) {
+ reply(550, "Cannot %s plain files.", cptr->name);
+ return;
+ }
+ if (S_ISREG(st.st_mode) != 0 && S_ISDIR(st.st_mode) != 0) {
+ reply(550, "Cannot %s special files.", cptr->name);
+ return;
+ }
+ if ((!cptr->types || !(cptr->types & T_ASCII)) && deny_badasciixfer(550, ""))
+ return;
+
+ logname = &fnbuf[0];
+ options |= cptr->options;
+
+ strcpy(namebuf, cptr->external_cmd);
+ if ((ptr = strchr(namebuf, ' ')) != NULL)
+ *ptr = '\0';
+ if (stat(namebuf, &junk) != 0) {
+ syslog(LOG_ERR, "external command %s not found", namebuf);
+ reply(550,
+ "Local error: conversion program not found. Cannot %s file.",
+ cptr->name);
+ return;
+ }
+ (void) retrieve(cptr->external_cmd, logname);
+
+ goto logresults; /* transfer of converted file completed */
+ }
+ }
+
+ if (cmd == NULL) { /* no command */
+ fin = fopen(name, "r"), closefunc = fclose;
+ st.st_size = 0;
+ }
+ else { /* run command */
+ static char line[BUFSIZ];
+
+ (void) snprintf(line, sizeof(line), cmd, name), name = line;
+ fin = ftpd_popen(line, "r", 1), closefunc = ftpd_pclose;
+ st.st_size = -1;
+#ifdef HAVE_ST_BLKSIZE
+ st.st_blksize = BUFSIZ;
+#endif
+ }
+ if (fin == NULL) {
+ if (errno != 0)
+ perror_reply(550, name);
+ if ((errno == EACCES) || (errno == EPERM))
+ if (log_security)
+ if (anonymous)
+ syslog(LOG_NOTICE, "anonymous(%s) of %s tried to download %s (file permissions)",
+ guestpw, remoteident, realname);
+ else
+ syslog(LOG_NOTICE, "%s of %s tried to download %s (file permissions)",
+ pw->pw_name, remoteident, realname);
+ return;
+ }
+ if (cmd == NULL &&
+ (fstat(fileno(fin), &st) < 0 || (st.st_mode & S_IFMT) != S_IFREG)) {
+ reply(550, "%s: not a plain file.", name);
+ goto done;
+ }
+ if (restart_point) {
+ if (type == TYPE_A) {
+ register int c;
+ off_t i;
+
+ i = 0;
+ while (i++ < restart_point) {
+ if ((c = getc(fin)) == EOF) {
+ perror_reply(550, name);
+ goto done;
+ }
+ if (c == '\n')
+ i++;
+ }
+ }
+ else if (lseek(fileno(fin), restart_point, SEEK_SET) < 0) {
+ perror_reply(550, name);
+ goto done;
+ }
+ }
+
+ dout = dataconn(name, st.st_size, "w");
+ if (dout == NULL)
+ goto done;
+
+ if (sendbufsz > 0) {
+ buffersize = sendbufsz;
+ }
+ else {
+#ifdef BUFFER_SIZE
+ buffersize = BUFFER_SIZE;
+#elif HAVE_ST_BLKSIZE
+ buffersize = st.st_blksize * 2;
+#else
+ buffersize = BUFSIZ * 16;
+#endif
+ }
+
+#ifdef THROUGHPUT
+ TransferComplete = send_data(name, fin, dout, buffersize);
+#else
+ TransferComplete = send_data(fin, dout, buffersize);
+#endif
+#ifdef SIGPIPE
+ (void) signal(SIGPIPE, SIG_IGN);
+#endif
+ (void) fclose(dout);
+#ifdef SIGPIPE
+ (void) signal(SIGPIPE, lostconn);
+#endif
+
+ logresults:
+ if (ThisRetrieveIsData)
+ fb_realpath((logname != NULL) ? logname : name, LastFileTransferred);
+
+ if (log_outbound_xfers && (xferlog || syslogmsg) && (cmd == NULL)) {
+ char msg[MAXXFERSTRLEN]; /* see extensions.h */
+ int xfertime = time(NULL) - start_time;
+ size_t msglen;
+
+ if (!xfertime)
+ xfertime++;
+
+ /* Gather transfer statistics */
+ xfervalues.filename = (logname != NULL) ? logname : name;
+ xfervalues.filesize = st.st_size;
+ xfervalues.transfer_bytes = byte_count;
+ xfervalues.transfer_direction = 'o';
+ xfervalues.transfer_type = (type == TYPE_A) ? 'a' : 'b';
+ xfervalues.transfer_time = xfertime;
+ xfervalues.restart_offset = restart_point;
+ strlcpy(xfervalues.special_action, opt_string(options), MAXSPACTCHARS);
+ xfervalues.access_mode = anonymous ? 'a' : (guest ? 'g' : 'r');
+ xfervalues.auth = authenticated;
+ xfervalues.completion = TransferComplete ? 'c' : 'i';
+
+ xferdone = 1;
+ msg_massage(xferlog_format, msg, sizeof(msg));
+ xferdone = 0;
+
+ /* Ensure msg always ends with '\n' */
+ msglen = strlen(msg);
+ if (msglen == sizeof(msg) - 1) {
+ msg[sizeof(msg) - 2] = '\n';
+ msg[sizeof(msg) - 1] = '\0';
+ }
+ else {
+ msg[msglen] = '\n';
+ msg[msglen + 1] = '\0';
+ }
+
+ if (syslogmsg != 1)
+ write(xferlog, msg, strlen(msg));
+ if (syslogmsg != 0) {
+ char *msgp = msg;
+ /*
+ * To preserve the behavior when the xferlog format was fixed, skip
+ * over the time string if the message starts with the local time.
+ */
+ if (strncmp(xferlog_format, "%T ", 3) == 0)
+ msgp += 25;
+ syslog(LOG_INFO, "xferlog (send): %s", msgp);
+ }
+ }
+ data = -1;
+ pdata = -1;
+ done:
+ if (closefunc)
+ (*closefunc) (fin);
+}
+
+void store(char *name, char *mode, int unique)
+{
+ FILE *fout, *din;
+ struct stat st;
+ int TransferIncomplete = 1;
+ char *gunique(char *local);
+ time_t start_time = time(NULL);
+
+ struct aclmember *entry = NULL;
+
+ int fdout;
+ char realname[MAXPATHLEN];
+
+#ifdef OVERWRITE
+ int overwrite = 1;
+ int exists = 0;
+
+#endif /* OVERWRITE */
+
+ int open_flags = 0;
+
+#ifdef UPLOAD
+ mode_t oldmask;
+ uid_t uid;
+ gid_t gid;
+ uid_t oldid;
+ int f_mode = -1, match_value = -1;
+ int valid = 0;
+ int ret, serrno;
+ open_flags = (O_RDWR | O_CREAT |
+ ((mode != NULL && *mode == 'a') ? O_APPEND : O_TRUNC));
+#endif /* UPLOAD */
+
+ wu_realpath(name, realname, chroot_path);
+
+#ifdef TRANSFER_COUNT
+#ifdef TRANSFER_LIMIT
+ if (((file_limit_data_in > 0) && (file_count_in >= file_limit_data_in))
+ || ((file_limit_data_total > 0) && (file_count_total >= file_limit_data_total))
+ || ((data_limit_data_in > 0) && (data_count_in >= data_limit_data_in))
+ || ((data_limit_data_total > 0) && (data_count_total >= data_limit_data_total))) {
+ if (log_security)
+ if (anonymous)
+ syslog(LOG_NOTICE, "anonymous(%s) of %s tried to upload %s (Transfer limits exceeded)",
+ guestpw, remoteident, realname);
+ else
+ syslog(LOG_NOTICE, "%s of %s tried to upload %s (Transfer limits exceeded)",
+ pw->pw_name, remoteident, realname);
+ reply(553, "Permission denied on server. (Transfer limits exceeded)");
+ return;
+ }
+ if (((file_limit_raw_in > 0) && (xfer_count_in >= file_limit_raw_in))
+ || ((file_limit_raw_total > 0) && (xfer_count_total >= file_limit_raw_total))
+ || ((data_limit_raw_in > 0) && (byte_count_in >= data_limit_raw_in))
+ || ((data_limit_raw_total > 0) && (byte_count_total >= data_limit_raw_total))) {
+ if (log_security)
+ if (anonymous)
+ syslog(LOG_NOTICE, "anonymous(%s) of %s tried to upload %s (Transfer limits exceeded)",
+ guestpw, remoteident, realname);
+ else
+ syslog(LOG_NOTICE, "%s of %s tried to upload %s (Transfer limits exceeded)",
+ pw->pw_name, remoteident, realname);
+ reply(553, "Permission denied on server. (Transfer limits exceeded)");
+ return;
+ }
+#endif
+#endif
+
+ if (unique && stat(name, &st) == 0 &&
+ (name = gunique(name)) == NULL)
+ return;
+
+ /*
+ * check the filename, is it legal?
+ */
+ if ((fn_check(name)) <= 0) {
+ if (log_security)
+ if (anonymous)
+ syslog(LOG_NOTICE, "anonymous(%s) of %s tried to upload \"%s\" (path-filter)",
+ guestpw, remoteident, realname);
+ else
+ syslog(LOG_NOTICE, "%s of %s tried to upload \"%s\" (path-filter)",
+ pw->pw_name, remoteident, realname);
+ return;
+ }
+
+#ifdef OVERWRITE
+ /* if overwrite permission denied and file exists... then deny the user
+ * permission to write the file. */
+ while (getaclentry("overwrite", &entry) && ARG0 && ARG1 != NULL) {
+ if (type_match(ARG1))
+ if (strcasecmp(ARG0, "yes") != 0) {
+ overwrite = 0;
+ open_flags |= O_EXCL;
+ }
+ }
+
+#ifdef PARANOID
+ overwrite = 0;
+#endif
+ if (!stat(name, &st))
+ exists = 1;
+
+ if (!overwrite && exists) {
+ if (log_security)
+ if (anonymous)
+ syslog(LOG_NOTICE, "anonymous(%s) of %s tried to overwrite %s",
+ guestpw, remoteident, realname);
+ else
+ syslog(LOG_NOTICE, "%s of %s tried to overwrite %s",
+ pw->pw_name, remoteident, realname);
+ reply(553, "%s: Permission denied on server. (Overwrite)", name);
+ return;
+ }
+#endif /* OVERWRITE */
+
+#ifdef UPLOAD
+ if ((match_value = upl_check(name, &uid, &gid, &f_mode, &valid)) < 0) {
+ if (log_security)
+ if (anonymous)
+ syslog(LOG_NOTICE, "anonymous(%s) of %s tried to upload %s (upload denied)",
+ guestpw, remoteident, realname);
+ else
+ syslog(LOG_NOTICE, "%s of %s tried to upload %s (upload denied)",
+ pw->pw_name, remoteident, realname);
+ return;
+ }
+
+ /* do not truncate the file if we are restarting */
+ if (restart_point)
+ open_flags &= ~O_TRUNC;
+
+ /* if the user has an explicit new file mode, than open the file using
+ * that mode. We must take care to not let the umask affect the file
+ * mode.
+ *
+ * else open the file and let the default umask determine the file mode. */
+ if (f_mode >= 0) {
+ oldmask = umask(0000);
+ fdout = open(name, open_flags, f_mode);
+ umask(oldmask);
+ }
+ else
+ fdout = open(name, open_flags, 0666);
+
+ if (fdout < 0) {
+ if (log_security)
+ if (anonymous)
+ syslog(LOG_NOTICE, "anonymous(%s) of %s tried to upload %s (permissions)",
+ guestpw, remoteident, realname);
+ else
+ syslog(LOG_NOTICE, "%s of %s tried to upload %s (permissions)",
+ pw->pw_name, remoteident, realname);
+ perror_reply(553, name);
+ return;
+ }
+ /* if we have a uid and gid, then use them. */
+
+#ifdef OVERWRITE
+ if (!exists)
+#endif
+ if (valid > 0) {
+ oldid = geteuid();
+ if (uid != 0)
+ (void) seteuid((uid_t) uid);
+ if ((uid == 0) || ((fchown(fdout, uid, gid)) < 0)) {
+ chown_priv_on(0);
+ ret = fchown(fdout, uid, gid);
+ serrno = errno;
+ chown_priv_off(oldid);
+ if (ret < 0) {
+ errno = serrno;
+ perror_reply(550, "fchown");
+ return;
+ }
+ }
+ else
+ (void) seteuid(oldid);
+ }
+#endif /* UPLOAD */
+
+ if (restart_point && (open_flags & O_APPEND) == 0)
+ mode = "r+";
+
+#ifdef UPLOAD
+ fout = fdopen(fdout, mode);
+#else
+ fout = fopen(name, mode);
+#endif /* UPLOAD */
+
+ if (fout == NULL) {
+ if (log_security)
+ if (anonymous)
+ syslog(LOG_NOTICE, "anonymous(%s) of %s tried to upload %s (permissions)",
+ guestpw, remoteident, realname);
+ else
+ syslog(LOG_NOTICE, "%s of %s tried to upload %s (permissions)",
+ pw->pw_name, remoteident, realname);
+ perror_reply(553, name);
+ return;
+ }
+ if (restart_point && (open_flags & O_APPEND) == 0) {
+ if (type == TYPE_A) {
+ register int c;
+ off_t i;
+
+ i = 0;
+ while (i++ < restart_point) {
+ if ((c = getc(fout)) == EOF) {
+ perror_reply(550, name);
+ goto done;
+ }
+ if (c == '\n')
+ i++;
+ }
+ /* We must do this seek to "current" position because we are
+ * changing from reading to writing. */
+#if _FILE_OFFSET_BITS == 64
+ if (fseeko(fout, 0L, SEEK_CUR) < 0) {
+#else
+ if (fseek(fout, 0L, SEEK_CUR) < 0) {
+#endif
+ perror_reply(550, name);
+ goto done;
+ }
+ }
+ else if (lseek(fileno(fout), restart_point, SEEK_SET) < 0) {
+ perror_reply(550, name);
+ goto done;
+ }
+ }
+ din = dataconn(name, (off_t) - 1, "r");
+ if (din == NULL)
+ goto done;
+ TransferIncomplete = receive_data(din, fout);
+
+ if (fstat(fileno(fout), &st) != 0) {
+ /* shouldn't fail, but just in case */
+ st.st_size = -1;
+ }
+ (void) fclose(din);
+ if (TransferIncomplete == 0) {
+ if (unique)
+ reply(226, "Transfer complete (unique file name:%s).", name);
+ else
+ reply(226, "Transfer complete.");
+ }
+
+ fb_realpath(name, LastFileTransferred);
+
+#ifdef MAIL_ADMIN
+ if (anonymous && incmails > 0) {
+ FILE *sck = NULL;
+
+ unsigned char temp = 0, temp2 = 0;
+ char pathname[MAXPATHLEN];
+ while ((temp < mailservers) && (sck == NULL))
+ sck = SockOpen(mailserver[temp++], 25);
+ if (sck == NULL) {
+ syslog(LOG_ERR, "Can't connect to a mailserver.");
+ goto mailfail;
+ }
+ if (Reply(sck) != 220) {
+ syslog(LOG_ERR, "Mailserver failed to initiate contact.");
+ goto mailfail;
+ }
+ if (Send(sck, "HELO localhost\r\n") != 250) {
+ syslog(LOG_ERR, "Mailserver doesn't understand HELO.");
+ goto mailfail;
+ }
+ if (Send(sck, "MAIL FROM: <%s>\r\n", email(mailfrom)) != 250) {
+ syslog(LOG_ERR, "Mailserver didn't accept MAIL FROM.");
+ goto mailfail;
+ }
+ for (temp = 0; temp < incmails; temp++) {
+ if (Send(sck, "RCPT TO: <%s>\r\n", email(incmail[temp])) == 250)
+ temp2++;
+ }
+ if (temp2 == 0) {
+ syslog(LOG_ERR, "Mailserver didn't accept any RCPT TO.");
+ goto mailfail;
+ }
+ if (Send(sck, "DATA\r\n") != 354) {
+ syslog(LOG_ERR, "Mailserver didn't accept DATA.");
+ goto mailfail;
+ }
+ SockPrintf(sck, "From: wu-ftpd <%s>\r\n", mailfrom);
+ SockPrintf(sck, "Subject: New file uploaded: %s\r\n\r\n", name);
+ fb_realpath(name, pathname);
+ SockPrintf(sck, "%s uploaded %s from %s.\r\nFile size is %" L_FORMAT ".\r\nPlease move the file where it belongs.\r\n", guestpw, pathname, remotehost, st.st_size);
+ if (Send(sck, ".\r\n") != 250)
+ syslog(LOG_ERR, "Message rejected by mailserver.");
+ if (Send(sck, "QUIT\r\n") != 221)
+ syslog(LOG_ERR, "Mailserver didn't accept QUIT.");
+ mailfail:
+ if (sck != NULL)
+ fclose(sck);
+ }
+#endif /* MAIL_ADMIN */
+
+ if (log_incoming_xfers && (xferlog || syslogmsg)) {
+ char msg[MAXXFERSTRLEN]; /* see extensions.h */
+ int xfertime = time(NULL) - start_time;
+ size_t msglen;
+
+ if (!xfertime)
+ xfertime++;
+
+ /* Gather transfer statistics */
+ xfervalues.filename = name;
+ xfervalues.filesize = st.st_size;
+ xfervalues.transfer_bytes = byte_count;
+ xfervalues.transfer_direction = 'i';
+ xfervalues.transfer_type = (type == TYPE_A) ? 'a' : 'b';
+ xfervalues.transfer_time = xfertime;
+ xfervalues.restart_offset = restart_point;
+ strlcpy(xfervalues.special_action, opt_string(0), MAXSPACTCHARS);
+ xfervalues.access_mode = anonymous ? 'a' : (guest ? 'g' : 'r');
+ xfervalues.auth = authenticated;
+ xfervalues.completion = TransferIncomplete ? 'i' : 'c';
+
+ xferdone = 1;
+ msg_massage(xferlog_format, msg, sizeof(msg));
+ xferdone = 0;
+
+ /* Ensure msg always ends with '\n' */
+ msglen = strlen(msg);
+ if (msglen == sizeof(msg) - 1) {
+ msg[sizeof(msg) - 2] = '\n';
+ msg[sizeof(msg) - 1] = '\0';
+ }
+ else {
+ msg[msglen] = '\n';
+ msg[msglen + 1] = '\0';
+ }
+
+ if (syslogmsg != 1)
+ write(xferlog, msg, strlen(msg));
+ if (syslogmsg != 0) {
+ char *msgp = msg;
+ /*
+ * To preserve the behavior when the xferlog format was fixed, skip
+ * over the time string if the message starts with the local time.
+ */
+ if (strncmp(xferlog_format, "%T ", 3) == 0)
+ msgp += 25;
+ syslog(LOG_INFO, "xferlog (recv): %s", msgp);
+ }
+ }
+ data = -1;
+ pdata = -1;
+ done:
+ (void) fclose(fout);
+}
+
+FILE *getdatasock(char *mode)
+{
+ int s, on = 1, tries;
+
+ if (data >= 0)
+ return (fdopen(data, mode));
+ port_priv_on(0);
+ s = socket(SOCK_FAMILY(data_dest), SOCK_STREAM, 0);
+ if (s < 0)
+ goto bad;
+ if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
+ (char *) &on, sizeof(on)) < 0)
+ goto bad;
+ if (keepalive)
+ (void) setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (char *) &on, sizeof(on));
+ if (TCPwindowsize)
+ (void) setsockopt(s, SOL_SOCKET, (*mode == 'w' ? SO_SNDBUF : SO_RCVBUF),
+ (char *) &TCPwindowsize, sizeof(TCPwindowsize));
+ /* anchor socket to avoid multi-homing problems */
+#ifdef INET6
+ if (SOCK_FAMILY(data_dest) == SOCK_FAMILY(ctrl_addr))
+ data_source = ctrl_addr;
+ else if ((SOCK_FAMILY(data_dest) == AF_INET) && ctrl_v4mapped) {
+ struct sockaddr_in6 *ctrl_sin6 = (struct sockaddr_in6 *)&ctrl_addr;
+ struct sockaddr_in *data_sin = (struct sockaddr_in *)&data_source;
+
+ SET_SOCK_FAMILY(data_source, AF_INET);
+ memcpy(&data_sin->sin_addr, &ctrl_sin6->sin6_addr.s6_addr[12],
+ sizeof(struct in_addr));
+ }
+ else {
+ memset(&data_source, 0, sizeof(struct sockaddr_in6));
+ SET_SOCK_FAMILY(data_source, SOCK_FAMILY(data_dest));
+ SET_SOCK_ADDR_ANY(data_source);
+ }
+#else
+ data_source = ctrl_addr;
+#endif
+ SET_SOCK_PORT(data_source, data_port);
+
+#if defined(VIRTUAL) && defined(CANT_BIND) /* can't bind to virtual address */
+ SET_SOCK_ADDR_ANY(data_source);
+#endif
+ for (tries = 1;; tries++) {
+ if (bind(s, (struct sockaddr *) &data_source, SOCK_LEN(data_source)) >= 0)
+ break;
+ if (errno != EADDRINUSE || tries > 10)
+ goto bad;
+ sleep(tries);
+ }
+#if defined(M_UNIX) && !defined(_M_UNIX) /* bug in old TCP/IP release */
+ {
+ struct linger li;
+ li.l_onoff = 1;
+ li.l_linger = 900;
+ if (setsockopt(s, SOL_SOCKET, SO_LINGER,
+ (char *) &li, sizeof(struct linger)) < 0) {
+ syslog(LOG_WARNING, "setsockopt (SO_LINGER): %m");
+ goto bad;
+ }
+ }
+#endif
+ port_priv_off((uid_t) pw->pw_uid);
+
+#ifdef INET6
+ /* IP_TOS is an IPv4 socket option */
+ if (SOCK_FAMILY(data_source) == AF_INET)
+#endif
+ if ((on = IPClassOfService("data")) >= 0) {
+ if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *) &on, sizeof(int)) < 0)
+ syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
+ }
+#ifdef TCP_NOPUSH
+ /*
+ * Turn off push flag to keep sender TCP from sending short packets
+ * at the boundaries of each write(). Should probably do a SO_SNDBUF
+ * to set the send buffer size as well, but that may not be desirable
+ * in heavy-load situations.
+ */
+ on = 1;
+ if (setsockopt(s, IPPROTO_TCP, TCP_NOPUSH, (char *) &on, sizeof on) < 0)
+ syslog(LOG_WARNING, "setsockopt (TCP_NOPUSH): %m");
+#endif
+
+ return (fdopen(s, mode));
+ bad:
+ on = errno; /* hold errno for return */
+ port_priv_off((uid_t) pw->pw_uid);
+ if (s != -1)
+ (void) close(s);
+ errno = on;
+ return (NULL);
+}
+
+FILE *dataconn(char *name, off_t size, char *mode)
+{
+ char sizebuf[32];
+ FILE *file;
+ int retry = 0;
+ int on = 1;
+ int cval, serrno;
+ int cos;
+#ifdef THROUGHPUT
+ int bps;
+ double bpsmult;
+#endif
+
+ file_size = size;
+ byte_count = 0;
+ if (size != (off_t) - 1)
+ (void) sprintf(sizebuf, " (%" L_FORMAT " bytes)", size);
+ else
+ (void) strcpy(sizebuf, "");
+ if (pdata >= 0) {
+ struct SOCKSTORAGE from;
+ char dataaddr[MAXHOSTNAMELEN];
+#if defined(UNIXWARE) || defined(AIX)
+ size_t fromlen = sizeof(from);
+#else
+ int fromlen = sizeof(from);
+#endif
+ int s;
+#ifdef FD_ZERO
+ int rv;
+#endif
+
+ if (keepalive)
+ (void) setsockopt(pdata, SOL_SOCKET, SO_KEEPALIVE, (char *) &on, sizeof(on));
+ if (TCPwindowsize)
+ (void) setsockopt(pdata, SOL_SOCKET, (*mode == 'w' ? SO_SNDBUF : SO_RCVBUF),
+ (char *) &TCPwindowsize, sizeof(TCPwindowsize));
+#ifdef FD_ZERO
+ do {
+ struct timeval timeout;
+ fd_set set;
+
+ FD_ZERO(&set);
+ FD_SET(pdata, &set);
+
+ timeout.tv_usec = 0;
+ timeout.tv_sec = timeout_accept;
+#ifdef HPUX_SELECT
+ rv = select(pdata + 1, (int *) &set, NULL, NULL, &timeout);
+#else
+ rv = select(pdata + 1, &set, (fd_set *) 0, (fd_set *) 0,
+ (struct timeval *) &timeout);
+#endif
+ } while ((rv == -1) && (errno == EINTR));
+ if ((rv != -1) && (rv != 0))
+ s = accept(pdata, (struct sockaddr *) &from, &fromlen);
+ else
+ s = -1;
+#else /* FD_ZERO */
+ (void) signal(SIGALRM, alarm_signal);
+ alarm(timeout_accept);
+ s = accept(pdata, (struct sockaddr *) &from, &fromlen);
+ alarm(0);
+#endif
+ if (s == -1) {
+ reply(425, "Can't open data connection.");
+ (void) close(pdata);
+ pdata = -1;
+ return (NULL);
+ }
+ (void) close(pdata);
+ pdata = s;
+#ifdef INET6
+ /* IP_TOS is an IPv4 socket option */
+ if (SOCK_FAMILY(from) == AF_INET)
+#endif
+ if ((cos = IPClassOfService("data")) >= 0)
+ (void) setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&cos, sizeof(int));
+ (void) strncpy(dataaddr, inet_stop(&from), sizeof(dataaddr));
+ if (!pasv_allowed(dataaddr))
+ if (strcasecmp(dataaddr, remoteaddr) != 0) {
+ /*
+ * This will log when data connection comes from an address different
+ * than the control connection.
+ */
+#ifdef FIGHT_PASV_PORT_RACE
+ syslog(LOG_ERR, "%s of %s: data connect from %s for %s%s",
+ anonymous ? guestpw : pw->pw_name, remoteident,
+ dataaddr, name, sizebuf);
+ reply(425, "Possible PASV port theft, cannot open data connection.");
+ (void) close(pdata);
+ pdata = -1;
+ return (NULL);
+#else
+ syslog(LOG_NOTICE, "%s of %s: data connect from %s for %s%s",
+ anonymous ? guestpw : pw->pw_name, remoteident,
+ dataaddr, name, sizebuf);
+#endif
+ }
+#ifdef THROUGHPUT
+ throughput_calc(name, &bps, &bpsmult);
+ if (bps != -1) {
+ lreply(150, "Opening %s mode data connection for %s%s.",
+ type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
+ reply(150, "Restricting network throughput to %d bytes/s.", bps);
+ }
+ else
+#endif
+ reply(150, "Opening %s mode data connection for %s%s.",
+ type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
+ return (fdopen(pdata, mode));
+ }
+ if (data >= 0) {
+ reply(125, "Using existing data connection for %s%s.",
+ name, sizebuf);
+ usedefault = 1;
+ return (fdopen(data, mode));
+ }
+ if (usedefault)
+ data_dest = his_addr;
+ if (SOCK_PORT(data_dest) == 0) {
+ reply(500, "Can't build data connection: no PORT specified");
+ return (NULL);
+ }
+ usedefault = 1;
+ do {
+ file = getdatasock(mode);
+ if (file == NULL) {
+ reply(425, "Can't create data socket (%s,%d): %s.",
+ inet_stop(&data_source), ntohs(SOCK_PORT(data_source)),
+ strerror(errno));
+ return (NULL);
+ }
+ data = fileno(file);
+ (void) signal(SIGALRM, alarm_signal);
+ alarm(timeout_connect);
+ cval = connect(data, (struct sockaddr *) &data_dest,
+ SOCK_LEN(data_dest));
+ serrno = errno;
+ alarm(0);
+ if (cval == -1) {
+ /*
+ * When connect fails, the state of the socket is unspecified so
+ * it should be closed and a new socket created for each connection
+ * attempt. This also prevents denial of service problems when
+ * running on operating systems that only allow one non-connected
+ * socket bound to the same local address.
+ */
+ (void) fclose(file);
+ data = -1;
+ errno = serrno;
+ if ((errno == EADDRINUSE || errno == EINTR) && retry < swaitmax) {
+ sleep((unsigned) swaitint);
+ retry += swaitint;
+ }
+ else {
+ perror_reply(425, "Can't build data connection");
+ return (NULL);
+ }
+ }
+ } while (cval == -1);
+ if (keepalive)
+ (void) setsockopt(data, SOL_SOCKET, SO_KEEPALIVE, (char *) &on, sizeof(on));
+ if (TCPwindowsize)
+ (void) setsockopt(data, SOL_SOCKET, (*mode == 'w' ? SO_SNDBUF : SO_RCVBUF),
+ (char *) &TCPwindowsize, sizeof(TCPwindowsize));
+#ifdef THROUGHPUT
+ throughput_calc(name, &bps, &bpsmult);
+ if (bps != -1) {
+ lreply(150, "Opening %s mode data connection for %s%s.",
+ type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
+ reply(150, "Restricting network throughput to %d bytes/s.", bps);
+ }
+ else
+#endif
+ reply(150, "Opening %s mode data connection for %s%s.",
+ type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
+ return (file);
+}
+
+/* Tranfer the contents of "instr" to "outstr" peer using the appropriate
+ * encapsulation of the data subject to Mode, Structure, and Type.
+ *
+ * NB: Form isn't handled. */
+
+int
+#ifdef THROUGHPUT
+ send_data(char *name, FILE *instr, FILE *outstr, size_t blksize)
+#else
+ send_data(FILE *instr, FILE *outstr, size_t blksize)
+#endif
+{
+ register int c, cnt = 0;
+ static char *buf;
+ int netfd, filefd;
+#ifdef THROUGHPUT
+ int bps;
+ double bpsmult;
+ time_t t1, t2;
+#endif
+#ifdef SENDFILE
+ int use_sf = 0;
+ size_t xferred;
+ struct stat st;
+ struct sendfilevec sfv;
+#endif
+
+ buf = NULL;
+ if (wu_setjmp(urgcatch)) {
+ draconian_FILE = NULL;
+ alarm(0);
+ transflag = 0;
+#ifdef SIGPIPE
+ (void) signal(SIGPIPE, lostconn);
+#endif
+ if (buf)
+ (void) free(buf);
+ retrieve_is_data = 1;
+ return (0);
+ }
+ transflag++;
+
+#ifdef THROUGHPUT
+ throughput_calc(name, &bps, &bpsmult);
+#endif
+
+ switch (type) {
+
+ case TYPE_A:
+#ifdef SIGPIPE
+ /*
+ * Ignore SIGPIPE while sending data, necessary so lostconn() isn't
+ * called if we write to the data connection after the client has
+ * closed it.
+ */
+ (void) signal(SIGPIPE, SIG_IGN);
+#endif
+ draconian_FILE = outstr;
+ (void) signal(SIGALRM, draconian_alarm_signal);
+ alarm(timeout_data);
+#ifdef THROUGHPUT
+ if (bps != -1)
+ t1 = time(NULL);
+#endif
+ while ((draconian_FILE != NULL) && ((c = getc(instr)) != EOF)) {
+ if (++byte_count % 4096 == 0)
+ alarm(timeout_data);
+ if (c == '\n') {
+ if (ferror(outstr))
+ goto data_err;
+ if (++byte_count % 4096 == 0)
+ alarm(timeout_data);
+#if defined(USE_GSS)
+ if (sec_putc('\r', outstr) != '\r')
+ goto data_err;
+#else
+ (void) putc('\r', outstr);
+#endif
+#ifdef TRANSFER_COUNT
+ if (retrieve_is_data) {
+ data_count_total++;
+ data_count_out++;
+ }
+ byte_count_total++;
+ byte_count_out++;
+#endif
+ }
+#if defined(USE_GSS)
+ if (sec_putc(c, outstr) != c)
+ goto data_err;
+#else
+ (void) putc(c, outstr);
+#endif
+
+#ifdef TRANSFER_COUNT
+ if (retrieve_is_data) {
+ data_count_total++;
+ data_count_out++;
+ }
+ byte_count_total++;
+ byte_count_out++;
+#endif
+#ifdef THROUGHPUT
+ if (bps > 0 && (byte_count % bps) == 0) {
+ t2 = time(NULL);
+ if (t2 == t1)
+ sleep(1);
+ t1 = time(NULL);
+ }
+#endif
+ }
+#ifdef THROUGHPUT
+ if (bps != -1)
+ throughput_adjust(name);
+#endif
+ if (draconian_FILE != NULL) {
+ alarm(timeout_data);
+#if defined(USE_GSS)
+ if (sec_fflush(outstr) < 0)
+ goto data_err;
+#else
+ fflush(outstr);
+#endif /* defined(USE_GSS) */
+ }
+ if (draconian_FILE != NULL) {
+ alarm(timeout_data);
+ socket_flush_wait(outstr);
+ }
+ transflag = 0;
+ if (ferror(instr))
+ goto file_err;
+ if ((draconian_FILE == NULL) || ferror(outstr))
+ goto data_err;
+ draconian_FILE = NULL;
+ alarm(0);
+#ifdef SIGPIPE
+ (void) signal(SIGPIPE, lostconn);
+#endif
+ reply(226, "Transfer complete.");
+#ifdef TRANSFER_COUNT
+ if (retrieve_is_data) {
+ file_count_total++;
+ file_count_out++;
+ }
+ xfer_count_total++;
+ xfer_count_out++;
+#endif
+ retrieve_is_data = 1;
+ return (1);
+
+ case TYPE_I:
+ case TYPE_L:
+#ifdef THROUGHPUT
+ if (bps != -1)
+ blksize = bps;
+#endif
+ netfd = fileno(outstr);
+ filefd = fileno(instr);
+#ifdef SENDFILE
+ /* check the input file is a regular file */
+ if ((fstat(filefd, &st) == 0) && ((st.st_mode & S_IFMT) == S_IFREG)) {
+#if defined(USE_GSS)
+ if (gss_info.data_prot == PROT_C || !IS_GSSAUTH(cur_auth_type) ||
+ !(gss_info.authstate & GSS_ADAT_DONE))
+#endif /* defined(USE_GSS) */
+ {
+ use_sf = 1;
+ /*
+ * Use a private sfv_flag SFV_NOWAIT to tell sendfilev(),
+ * when zero-copy is enabled, not to wait for all data to be
+ * ACKed before returning. This is important for throughput
+ * performance when sendfilev() is called to send small piece
+ * at a time.
+ */
+ sfv.sfv_flag = SFV_NOWAIT;
+ sfv.sfv_fd = filefd;
+ sfv.sfv_off = restart_point;
+ sfv.sfv_len = blksize;
+ }
+ }
+ if (use_sf == 0)
+#endif
+ if ((buf = (char *) malloc(blksize)) == NULL) {
+ transflag = 0;
+ perror_reply(451, "Local resource failure: malloc");
+ retrieve_is_data = 1;
+ return (0);
+ }
+#ifdef SIGPIPE
+ /*
+ * Ignore SIGPIPE while sending data, necessary so lostconn() isn't
+ * called if we write to the data connection after the client has
+ * closed it.
+ */
+ (void) signal(SIGPIPE, SIG_IGN);
+#endif
+ draconian_FILE = outstr;
+ (void) signal(SIGALRM, draconian_alarm_signal);
+ alarm(timeout_data);
+#ifdef THROUGHPUT
+ if (bps != -1)
+ t1 = time(NULL);
+#endif
+ while ((draconian_FILE != NULL) && (
+#ifdef SENDFILE
+ (use_sf && (cnt = sendfilev(netfd, &sfv, 1, &xferred)) > 0)
+ || (!use_sf &&
+#endif
+ ((cnt = read(filefd, buf, blksize)) > 0 &&
+#if defined(USE_GSS)
+ sec_write(netfd, buf, cnt) == cnt)
+#else
+ write(netfd, buf, cnt) == cnt)
+#endif /* defined(USE_GSS) */
+#ifdef SENDFILE
+ )
+#endif
+ )) {
+ alarm(timeout_data);
+#ifdef SENDFILE
+ sfv.sfv_off += cnt;
+#endif
+ byte_count += cnt;
+#ifdef TRANSFER_COUNT
+ if (retrieve_is_data) {
+#ifdef RATIO
+ if( freefile ) {
+ total_free_dl += cnt;
+ }
+#endif /* RATIO */
+ data_count_total += cnt;
+ data_count_out += cnt;
+ }
+ byte_count_total += cnt;
+ byte_count_out += cnt;
+#endif
+#ifdef THROUGHPUT
+ if (bps != -1) {
+ t2 = time(NULL);
+ if (t2 == t1)
+ sleep(1);
+ t1 = time(NULL);
+ }
+#endif /* THROUGHPUT */
+ }
+#ifdef THROUGHPUT
+ if (bps != -1)
+ throughput_adjust(name);
+#endif
+#if defined(USE_GSS)
+ if (sec_fflush(outstr) < 0)
+ goto data_err;
+#endif
+ transflag = 0;
+ if (buf)
+ (void) free(buf);
+ if (draconian_FILE != NULL) {
+ alarm(timeout_data);
+ socket_flush_wait(outstr);
+ }
+ if (cnt != 0) {
+#ifdef SENDFILE
+ if (use_sf && cnt < 0 && errno == EPIPE)
+ goto data_err;
+#endif
+ if (cnt < 0)
+ goto file_err;
+ goto data_err;
+ }
+ if (draconian_FILE == NULL)
+ goto data_err;
+ draconian_FILE = NULL;
+ alarm(0);
+#ifdef SIGPIPE
+ (void) signal(SIGPIPE, lostconn);
+#endif
+ reply(226, "Transfer complete.");
+#ifdef TRANSFER_COUNT
+ if (retrieve_is_data) {
+ file_count_total++;
+ file_count_out++;
+ }
+ xfer_count_total++;
+ xfer_count_out++;
+#endif
+ retrieve_is_data = 1;
+ return (1);
+ default:
+ transflag = 0;
+ reply(550, "Unimplemented TYPE %d in send_data", type);
+ retrieve_is_data = 1;
+ return (0);
+ }
+
+ data_err:
+ draconian_FILE = NULL;
+ alarm(0);
+ transflag = 0;
+#ifdef SIGPIPE
+ (void) signal(SIGPIPE, lostconn);
+#endif
+ perror_reply(426, "Data connection");
+ retrieve_is_data = 1;
+ return (0);
+
+ file_err:
+ draconian_FILE = NULL;
+ alarm(0);
+ transflag = 0;
+#ifdef SIGPIPE
+ (void) signal(SIGPIPE, lostconn);
+#endif
+ perror_reply(551, "Error on input file");
+ retrieve_is_data = 1;
+ return (0);
+}
+
+/* Transfer data from peer to "outstr" using the appropriate encapulation of
+ * the data subject to Mode, Structure, and Type.
+ *
+ * N.B.: Form isn't handled. */
+
+int receive_data(FILE *instr, FILE *outstr)
+{
+ register int c;
+ int rcnt = 0, n = 0, bare_lfs = 0;
+ static char *buf;
+ int netfd, filefd, wcnt;
+#ifdef BUFFER_SIZE
+ size_t buffer_size = BUFFER_SIZE;
+#else
+ size_t buffer_size = BUFSIZ * 16;
+#endif
+
+ buf = NULL;
+ if (wu_setjmp(urgcatch)) {
+ alarm(0);
+ transflag = 0;
+ if (buf)
+ (void) free(buf);
+ return (-1);
+ }
+ transflag++;
+ switch (type) {
+
+ case TYPE_I:
+ case TYPE_L:
+#if defined(USE_GSS)
+ if (GSSUSERAUTH_OK(gss_info))
+ buffer_size = gss_getinbufsz();
+ else
+#endif
+ if (recvbufsz > 0)
+ buffer_size = recvbufsz;
+ if ((buf = (char *) malloc(buffer_size)) == NULL) {
+ transflag = 0;
+ perror_reply(451, "Local resource failure: malloc");
+ return (-1);
+ }
+ netfd = fileno(instr);
+ filefd = fileno(outstr);
+ draconian_FILE = instr;
+ (void) signal(SIGALRM, draconian_alarm_signal);
+ alarm(timeout_data);
+
+ while ((draconian_FILE != NULL) &&
+#if defined(USE_GSS)
+ (rcnt = sec_read(netfd, buf, buffer_size)) > 0) {
+#else
+ (rcnt = read(netfd, buf, buffer_size)) > 0) {
+#endif
+ for (wcnt = 0; wcnt < rcnt; wcnt += n) {
+ if ((n = write(filefd, &buf[wcnt], rcnt - wcnt)) == -1)
+ break;
+ }
+ byte_count += wcnt;
+#ifdef TRANSFER_COUNT
+ data_count_total += wcnt;
+ data_count_in += wcnt;
+ byte_count_total += wcnt;
+ byte_count_in += wcnt;
+#endif
+ if (n == -1)
+ break;
+ alarm(timeout_data);
+ }
+ transflag = 0;
+ (void) free(buf);
+ if ((rcnt == -1) || (draconian_FILE == NULL))
+ goto data_err;
+ if (n == -1)
+ goto file_err;
+ draconian_FILE = NULL;
+ alarm(0);
+#ifdef TRANSFER_COUNT
+ file_count_total++;
+ file_count_in++;
+ xfer_count_total++;
+ xfer_count_in++;
+#endif
+ return (0);
+
+ case TYPE_E:
+ reply(553, "TYPE E not implemented.");
+ transflag = 0;
+ return (-1);
+
+ case TYPE_A:
+ draconian_FILE = instr;
+ (void) signal(SIGALRM, draconian_alarm_signal);
+ alarm(timeout_data);
+ while ((draconian_FILE != NULL) &&
+#if defined(USE_GSS)
+ ((c = sec_getc(instr)) != EOF)
+#else
+ ((c = getc(instr)) != EOF)
+#endif
+ ) {
+ if (++byte_count % 4096 == 0)
+ alarm(timeout_data);
+ if (c == '\n')
+ bare_lfs++;
+ while (c == '\r') {
+ if (ferror(outstr))
+ goto file_err;
+ alarm(timeout_data);
+ if (draconian_FILE != NULL) {
+#if defined(USE_GSS)
+ if ((c = sec_getc(instr)) != '\n')
+#else
+ if ((c = getc(instr)) != '\n')
+#endif
+ (void) putc('\r', outstr);
+#ifdef TRANSFER_COUNT
+ data_count_total++;
+ data_count_in++;
+ byte_count_total++;
+ byte_count_in++;
+#endif
+ if (c == EOF) /* null byte fix, noid@cyborg.larc.nasa.gov */
+ goto contin2;
+ if (++byte_count % 4096 == 0)
+ alarm(timeout_data);
+ }
+ }
+ (void) putc(c, outstr);
+#ifdef TRANSFER_COUNT
+ data_count_total++;
+ data_count_in++;
+ byte_count_total++;
+ byte_count_in++;
+#endif
+ contin2:;
+ }
+ fflush(outstr);
+ if ((draconian_FILE == NULL) || ferror(instr))
+ goto data_err;
+ if (ferror(outstr))
+ goto file_err;
+ transflag = 0;
+ draconian_FILE = NULL;
+ alarm(0);
+ if (bare_lfs) {
+ lreply(226, "WARNING! %d bare linefeeds received in ASCII mode", bare_lfs);
+ lreply(0, " File may not have transferred correctly.");
+ }
+#ifdef TRANSFER_COUNT
+ file_count_total++;
+ file_count_in++;
+ xfer_count_total++;
+ xfer_count_in++;
+#endif
+ return (0);
+ default:
+ reply(550, "Unimplemented TYPE %d in receive_data", type);
+ transflag = 0;
+ return (-1);
+ }
+
+ data_err:
+ draconian_FILE = NULL;
+ alarm(0);
+ transflag = 0;
+ perror_reply(426, "Data Connection");
+ return (-1);
+
+ file_err:
+ draconian_FILE = NULL;
+ alarm(0);
+ transflag = 0;
+ perror_reply(452, "Error writing file");
+ return (-1);
+}
+
+void statfilecmd(char *filename)
+{
+#ifndef INTERNAL_LS
+ char line[BUFSIZ * 2], *ptr;
+ FILE *fin;
+ int c;
+#endif /* ! INTERNAL_LS */
+
+ fixpath(filename);
+ if (filename[0] == '\0')
+ filename = ".";
+#ifndef INTERNAL_LS
+ if (anonymous && dolreplies)
+ (void) snprintf(line, sizeof(line), ls_long, filename);
+ else
+ (void) snprintf(line, sizeof(line), ls_short, filename);
+ fin = ftpd_popen(line, "r", 0);
+#endif /* ! INTERNAL_LS */
+ lreply(213, "status of %s:", filename);
+#ifndef INTERNAL_LS
+ /*
+ while ((c = getc(fin)) != EOF) {
+ if (c == '\n') {
+ if (ferror(stdout)) {
+ perror_reply(421, "control connection");
+ (void) ftpd_pclose(fin);
+ dologout(1);
+ / * NOTREACHED * /
+ }
+ if (ferror(fin)) {
+ perror_reply(551, filename);
+ (void) ftpd_pclose(fin);
+ return;
+ }
+ (void) putc('\r', stdout);
+ }
+ (void) putc(c, stdout);
+ }
+ */
+ while (fgets(line, sizeof(line), fin) != NULL) {
+ if ((ptr = strchr(line, '\n'))) /* clip out unnecessary newline */
+ *ptr = '\0';
+ lreply(0, "%s", line);
+ }
+ (void) ftpd_pclose(fin);
+#else /* INTERNAL_LS */
+ ls_dir(filename, 1, 0, 1, 0, 1, stdout);
+#endif /* INTERNAL_LS */
+ reply(213, "End of Status");
+}
+
+void statcmd(void)
+{
+ struct SOCKSTORAGE *sin;
+ u_char *a, *p;
+ unsigned short port;
+#ifdef INET6
+ int isv4 = 0;
+#endif
+
+ lreply(211, "%s FTP server status:", hostname);
+ lreply(0, " %s", version);
+ if (nameserved)
+ lreply(0, " Connected to %s (%s)", remotehost, remoteaddr);
+ else
+ lreply(0, " Connected to %s", remotehost);
+
+ if (logged_in) {
+ if (anonymous)
+ lreply(0, " Logged in anonymously");
+ else
+ lreply(0, " Logged in as %s", pw->pw_name);
+ }
+ else if (askpasswd)
+ lreply(0, " Waiting for password");
+ else
+ lreply(0, " Waiting for user name");
+
+ if (type == TYPE_L)
+#ifdef NBBY
+ lreply(0, " TYPE: %s %d; STRUcture: %s; transfer MODE: %s",
+ typenames[type], NBBY, strunames[stru], modenames[mode]);
+#else
+ lreply(0, " TYPE: %s %d; STRUcture: %s; transfer MODE: %s",
+ typenames[type], bytesize, strunames[stru], modenames[mode]);
+#endif /* NBBY */
+ else
+ lreply(0, " TYPE: %s%s%s; STRUcture: %s; transfer MODE: %s",
+ typenames[type], (type == TYPE_A || type == TYPE_E) ?
+ ", FORM: " : "", (type == TYPE_A || type == TYPE_E) ?
+ formnames[form] : "", strunames[stru], modenames[mode]);
+ if (data != -1)
+ lreply(0, " Data connection open");
+ else if (pdata != -1 || usedefault == 0) {
+ if (usedefault == 0) {
+ sin = &data_dest;
+ port = SOCK_PORT(data_dest);
+ }
+ else {
+ port = SOCK_PORT(pasv_addr);
+ if (route_vectored)
+ sin = &vect_addr;
+ else
+ sin = &pasv_addr;
+ }
+ a = (u_char *) SOCK_ADDR(*sin);
+ p = (u_char *) &port;
+#define UC(b) (((int) b) & 0xff)
+#ifdef INET6
+ if (SOCK_FAMILY(*sin) == AF_INET)
+ isv4 = 1;
+ else if (IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)sin)->sin6_addr))
+ {
+ isv4 = 1;
+ a += 12; /* move to the IPv4 part of an IPv4-mapped IPv6 address */
+ }
+ if (epsv_all)
+ lreply(0, " EPSV only mode (EPSV ALL)");
+ if (isv4 && !epsv_all)
+#endif
+ lreply(0, " %s (%d,%d,%d,%d,%d,%d)",
+ usedefault == 0 ? "PORT" : "PASV",
+ UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
+#ifdef INET6
+ lreply(0, " %s (|%d|%s|%d|)", usedefault == 0 ? "EPRT" : "EPSV",
+ isv4 ? 1 : 2, inet_stop(sin), ntohs(port));
+ if (!epsv_all)
+ if (isv4)
+ lreply(0, " %s (4,4,%d,%d,%d,%d,2,%d,%d)",
+ usedefault == 0 ? "LPRT" : "LPSV",
+ UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
+ UC(p[0]), UC(p[1]));
+ else
+ lreply(0, " %s (6,16,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,"
+ "%d,%d,%d,%d,2,%d,%d)",
+ usedefault == 0 ? "LPRT" : "LPSV",
+ UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
+ UC(a[4]), UC(a[5]), UC(a[6]), UC(a[7]),
+ UC(a[8]), UC(a[9]), UC(a[10]), UC(a[11]),
+ UC(a[12]), UC(a[13]), UC(a[14]), UC(a[15]),
+ UC(p[0]), UC(p[1]));
+#endif
+#undef UC
+ }
+ else
+ lreply(0, " No data connection");
+#ifdef TRANSFER_COUNT
+ lreply(0, " %" L_FORMAT " data bytes received in %d files", data_count_in, file_count_in);
+ lreply(0, " %" L_FORMAT " data bytes transmitted in %d files", data_count_out, file_count_out);
+ lreply(0, " %" L_FORMAT " data bytes total in %d files", data_count_total, file_count_total);
+ lreply(0, " %" L_FORMAT " traffic bytes received in %d transfers", byte_count_in, xfer_count_in);
+ lreply(0, " %" L_FORMAT " traffic bytes transmitted in %d transfers", byte_count_out, xfer_count_out);
+ lreply(0, " %" L_FORMAT " traffic bytes total in %d transfers", byte_count_total, xfer_count_total);
+#endif
+ reply(211, "End of status");
+}
+
+void fatal(char *s)
+{
+ reply(451, "Error in server: %s\n", s);
+ reply(221, "Closing connection due to server error.");
+ dologout(0);
+ /* NOTREACHED */
+}
+
+#define USE_REPLY_NOTFMT (1<<1) /* fmt is not a printf fmt (KLUDGE) */
+#define USE_REPLY_LONG (1<<2) /* this is a long reply; use a - */
+
+void vreply(long flags, int n, char *fmt, va_list ap)
+{
+ char buf[BUFSIZ * 16];
+
+ flags &= USE_REPLY_NOTFMT | USE_REPLY_LONG;
+
+ if (n) /* if numeric is 0, don't output one; use n==0 in place of printf's */
+ sprintf(buf, "%03d%c", n, flags & USE_REPLY_LONG ? '-' : ' ');
+
+ /* This is somewhat of a kludge for autospout. I personally think that
+ * autospout should be done differently, but that's not my department. -Kev
+ */
+ if (flags & USE_REPLY_NOTFMT)
+ snprintf(buf + (n ? 4 : 0), n ? sizeof(buf) - 4 : sizeof(buf), "%s", fmt);
+ else
+ vsnprintf(buf + (n ? 4 : 0), n ? sizeof(buf) - 4 : sizeof(buf), fmt, ap);
+
+#if defined(USE_GSS)
+ if (IS_GSSAUTH(cur_auth_type) &&
+ (gss_info.authstate & GSS_ADAT_DONE) &&
+ gss_info.ctrl_prot != PROT_C) {
+ if (buf[strlen(buf)-1] != '\n')
+ strlcat(buf, "\r\n", sizeof(buf));
+ (void) sec_reply(buf, sizeof(buf), n);
+ }
+#endif
+
+ if (debug) /* debugging output :) */
+ syslog(LOG_DEBUG, "<--- %s", buf);
+
+ /* Yes, you want the debugging output before the client output; wrapping
+ * stuff goes here, you see, and you want to log the cleartext and send
+ * the wrapped text to the client.
+ */
+
+ printf("%s\r\n", buf); /* and send it to the client */
+#ifdef TRANSFER_COUNT
+ byte_count_total += strlen(buf);
+ byte_count_out += strlen(buf);
+#endif
+ /*
+ * We dont need to worry about "sec_fflush" here since "sec_reply"
+ * already wrapped the reply if necessary.
+ */
+ fflush(stdout);
+}
+
+void reply(int n, char *fmt,...)
+{
+ VA_LOCAL_DECL
+
+ if (autospout != NULL) { /* deal with the autospout stuff... */
+ char *p, *ptr = autospout;
+
+ while (*ptr) {
+ if ((p = strchr(ptr, '\n')) != NULL) /* step through line by line */
+ *p = '\0';
+
+ /* send a line...(note that this overrides dolreplies!) */
+ vreply(USE_REPLY_LONG | USE_REPLY_NOTFMT, n, ptr, ap);
+
+ if (p)
+ ptr = p + 1; /* set to the next line... (\0 is handled in the while) */
+ else
+ break; /* oh, we're done; drop out of the loop */
+ }
+
+ if (autospout_free) { /* free autospout if necessary */
+ (void) free(autospout);
+ autospout_free = 0;
+ }
+ autospout = 0; /* clear the autospout */
+ }
+
+ VA_START(fmt);
+
+ /* send the reply */
+ vreply(0L, n, fmt, ap);
+
+ VA_END;
+}
+
+void lreply(int n, char *fmt,...)
+{
+ VA_LOCAL_DECL
+
+ if (!dolreplies) /* prohibited from doing long replies? */
+ return;
+
+ VA_START(fmt);
+
+ /* send the reply */
+ vreply(USE_REPLY_LONG, n, fmt, ap);
+
+ VA_END;
+}
+
+void ack(char *s)
+{
+ reply(250, "%s command successful.", s);
+}
+
+void nack(char *s)
+{
+ reply(502, "%s command not implemented.", s);
+}
+
+void yyerror(char *s)
+{
+ char *cp;
+ if (s == NULL || yyerrorcalled != 0)
+ return;
+ if ((cp = strchr(cbuf, '\n')) != NULL)
+ *cp = '\0';
+ reply(500, "'%s': command not understood.", cbuf);
+ yyerrorcalled = 1;
+ return;
+}
+
+void delete(char *name)
+{
+ struct stat st;
+ char realname[MAXPATHLEN];
+
+ /*
+ * delete permission?
+ */
+
+ wu_realpath(name, realname, chroot_path);
+
+ if ((del_check(name)) == 0) {
+ if (log_security)
+ if (anonymous)
+ syslog(LOG_NOTICE, "anonymous(%s) of %s tried to delete %s",
+ guestpw, remoteident, realname);
+ else
+ syslog(LOG_NOTICE, "%s of %s tried to delete %s",
+ pw->pw_name, remoteident, realname);
+ return;
+ }
+
+ if (lstat(name, &st) < 0) {
+ perror_reply(550, name);
+ return;
+ }
+ if ((st.st_mode & S_IFMT) == S_IFDIR) {
+ uid_t uid;
+ gid_t gid;
+ int d_mode;
+ int valid;
+
+ /*
+ * check the directory, can we rmdir here?
+ */
+ if ((dir_check(name, &uid, &gid, &d_mode, &valid)) <= 0) {
+ if (log_security)
+ if (anonymous)
+ syslog(LOG_NOTICE, "anonymous(%s) of %s tried to delete directory %s",
+ guestpw, remoteident, realname);
+ else
+ syslog(LOG_NOTICE, "%s of %s tried to delete directory %s",
+ pw->pw_name, remoteident, realname);
+ return;
+ }
+
+ if (rmdir(name) < 0) {
+ if (log_security)
+ if (anonymous)
+ syslog(LOG_NOTICE, "anonymous(%s) of %s tried to delete directory %s (permissions)",
+ guestpw, remoteident, realname);
+ else
+ syslog(LOG_NOTICE, "%s of %s tried to delete directory %s (permissions)",
+ pw->pw_name, remoteident, realname);
+ perror_reply(550, name);
+ return;
+ }
+ goto done;
+ }
+ if (unlink(name) < 0) {
+ if (log_security)
+ if (anonymous)
+ syslog(LOG_NOTICE, "anonymous(%s) of %s tried to delete %s (permissions)",
+ guestpw, remoteident, realname);
+ else
+ syslog(LOG_NOTICE, "%s of %s tried to delete %s (permissions)",
+ pw->pw_name, remoteident, realname);
+ perror_reply(550, name);
+ return;
+ }
+ done:
+ {
+ char path[MAXPATHLEN];
+
+ wu_realpath(name, path, chroot_path);
+
+ if (log_security)
+ if ((st.st_mode & S_IFMT) == S_IFDIR)
+ if (anonymous) {
+ syslog(LOG_NOTICE, "%s of %s deleted directory %s", guestpw, remoteident, path);
+ }
+ else {
+ syslog(LOG_NOTICE, "%s of %s deleted directory %s", pw->pw_name,
+ remoteident, path);
+ }
+ else if (anonymous) {
+ syslog(LOG_NOTICE, "%s of %s deleted %s", guestpw,
+ remoteident, path);
+ }
+ else {
+ syslog(LOG_NOTICE, "%s of %s deleted %s", pw->pw_name,
+ remoteident, path);
+ }
+ }
+
+ ack("DELE");
+}
+
+void cwd(char *path)
+{
+ struct aclmember *entry = NULL;
+ char cdpath[MAXPATHLEN];
+
+ if (chdir(path) < 0) {
+ /* alias checking */
+ while (getaclentry("alias", &entry) && ARG0 && ARG1 != NULL) {
+ if (!strcasecmp(ARG0, path)) {
+ if (chdir(ARG1) < 0)
+ perror_reply(550, path);
+ else {
+ show_message(250, C_WD);
+ show_readme(250, C_WD);
+ ack("CWD");
+ }
+ return;
+ }
+ }
+ /* check for "cdpath" directories. */
+ entry = (struct aclmember *) NULL;
+ while (getaclentry("cdpath", &entry) && ARG0 != NULL) {
+ snprintf(cdpath, sizeof cdpath, "%s/%s", ARG0, path);
+ if (chdir(cdpath) >= 0) {
+ show_message(250, C_WD);
+ show_readme(250, C_WD);
+ ack("CWD");
+ return;
+ }
+ }
+ perror_reply(550, path);
+ }
+ else {
+ show_message(250, C_WD);
+ show_readme(250, C_WD);
+ ack("CWD");
+ }
+}
+
+void makedir(char *name)
+{
+ uid_t uid;
+ gid_t gid;
+ int d_mode;
+ mode_t oldumask;
+ int valid;
+ int ret, serrno;
+ uid_t oldid;
+ char path[MAXPATHLEN + 1]; /* for realpath() later - cky */
+ char realname[MAXPATHLEN];
+
+ wu_realpath(name, realname, chroot_path);
+ /*
+ * check the directory, can we mkdir here?
+ */
+ if ((dir_check(name, &uid, &gid, &d_mode, &valid)) <= 0) {
+ if (log_security)
+ if (anonymous)
+ syslog(LOG_NOTICE, "anonymous(%s) of %s tried to create directory %s",
+ guestpw, remoteident, realname);
+ else
+ syslog(LOG_NOTICE, "%s of %s tried to create directory %s",
+ pw->pw_name, remoteident, realname);
+ return;
+ }
+
+ /*
+ * check the filename, is it legal?
+ */
+ if ((fn_check(name)) <= 0) {
+ if (log_security)
+ if (anonymous)
+ syslog(LOG_NOTICE, "anonymous(%s) of %s tried to create directory %s (path-filter)",
+ guestpw, remoteident, realname);
+ else
+ syslog(LOG_NOTICE, "%s of %s tried to create directory %s (path-filter)",
+ pw->pw_name, remoteident, realname);
+ return;
+ }
+
+ oldumask = umask(0000);
+ if (valid <= 0) {
+ d_mode = 0777;
+ umask(oldumask);
+ }
+
+ if (mkdir(name, d_mode) < 0) {
+ if (errno == EEXIST) {
+ if (log_security)
+ if (anonymous)
+ syslog(LOG_NOTICE, "anonymous(%s) of %s tried to create directory %s (exists)",
+ guestpw, remoteident, realname);
+ else
+ syslog(LOG_NOTICE, "%s of %s tried to create directory %s (exists)",
+ pw->pw_name, remoteident, realname);
+ fb_realpath(name, path);
+ reply(521, "\"%s\" directory exists", path);
+ }
+ else {
+ if (log_security)
+ if (anonymous)
+ syslog(LOG_NOTICE, "anonymous(%s) of %s tried to create directory %s (permissions)",
+ guestpw, remoteident, realname);
+ else
+ syslog(LOG_NOTICE, "%s of %s tried to create directory %s (permissions)",
+ pw->pw_name, remoteident, realname);
+ perror_reply(550, name);
+ }
+ umask(oldumask);
+ return;
+ }
+ umask(oldumask);
+ if (valid > 0) {
+ oldid = geteuid();
+ if (uid != 0)
+ (void) seteuid((uid_t) uid);
+ if ((uid == 0) || ((chown(name, uid, gid)) < 0)) {
+ chown_priv_on(0);
+ ret = chown(name, uid, gid);
+ serrno = errno;
+ chown_priv_off(oldid);
+ if (ret < 0) {
+ errno = serrno;
+ perror_reply(550, "chown");
+ return;
+ }
+ }
+ else
+ (void) seteuid(oldid);
+ }
+ wu_realpath(name, path, chroot_path);
+ if (log_security)
+ if (anonymous) {
+ syslog(LOG_NOTICE, "%s of %s created directory %s", guestpw, remoteident, path);
+ }
+ else {
+ syslog(LOG_NOTICE, "%s of %s created directory %s", pw->pw_name,
+ remoteident, path);
+ }
+ fb_realpath(name, path);
+ /* According to RFC 959:
+ * The 257 reply to the MKD command must always contain the
+ * absolute pathname of the created directory.
+ * This is implemented here using similar code to the PWD command.
+ * XXX - still need to do `quote-doubling'.
+ */
+ reply(257, "\"%s\" new directory created.", path);
+}
+
+void removedir(char *name)
+{
+ uid_t uid;
+ gid_t gid;
+ int d_mode;
+ int valid;
+ char realname[MAXPATHLEN];
+
+ wu_realpath(name, realname, chroot_path);
+
+ /*
+ * delete permission?
+ */
+
+ if ((del_check(name)) == 0)
+ return;
+ /*
+ * check the directory, can we rmdir here?
+ */
+ if ((dir_check(name, &uid, &gid, &d_mode, &valid)) <= 0) {
+ if (log_security)
+ if (anonymous)
+ syslog(LOG_NOTICE, "anonymous(%s) of %s tried to remove directory %s",
+ guestpw, remoteident, realname);
+ else
+ syslog(LOG_NOTICE, "%s of %s tried to remove directory %s",
+ pw->pw_name, remoteident, realname);
+ return;
+ }
+
+ if (rmdir(name) < 0) {
+ if (errno == EBUSY)
+ perror_reply(450, name);
+ else {
+ if (log_security)
+ if (anonymous)
+ syslog(LOG_NOTICE, "anonymous(%s) of %s tried to remove directory %s (permissions)",
+ guestpw, remoteident, realname);
+ else
+ syslog(LOG_NOTICE, "%s of %s tried to remove directory %s (permissions)",
+ pw->pw_name, remoteident, realname);
+ perror_reply(550, name);
+ }
+ }
+ else {
+ char path[MAXPATHLEN];
+
+ wu_realpath(name, path, chroot_path);
+
+ if (log_security)
+ if (anonymous) {
+ syslog(LOG_NOTICE, "%s of %s deleted directory %s", guestpw, remoteident, path);
+ }
+ else {
+ syslog(LOG_NOTICE, "%s of %s deleted directory %s", pw->pw_name,
+ remoteident, path);
+ }
+ ack("RMD");
+ }
+}
+
+void pwd(void)
+{
+ char path[MAXPATHLEN + 1];
+ char rhome[MAXPATHLEN + 1];
+ char *rpath = path; /* Path to return to client */
+ int pathlen;
+#ifndef MAPPING_CHDIR
+#ifdef HAVE_GETCWD
+ extern char *getcwd();
+#else
+ extern char *getwd(char *);
+#endif
+#endif /* MAPPING_CHDIR */
+
+#ifdef HAVE_GETCWD
+ if (getcwd(path, MAXPATHLEN) == (char *) NULL)
+#else
+ if (getwd(path) == (char *) NULL)
+#endif
+/* Dink! If you couldn't get the path and the buffer is now likely to
+ be undefined, why are you trying to PRINT it?! _H*
+ reply(550, "%s.", path); */
+ {
+ fb_realpath(".", path); /* realpath_on_steroids can deal */
+ }
+ /* relative to home directory if restricted_user */
+ if (restricted_user) {
+ fb_realpath(home, rhome);
+ pathlen = strlen(rhome);
+ if (pathlen && rhome[pathlen - 1] == '/')
+ pathlen--;
+ rpath = rpath + pathlen;
+ if (!*rpath)
+ strcpy(rpath, "/");
+ }
+ reply(257, "\"%s\" is current directory.", rpath);
+}
+
+char *renamefrom(char *name)
+{
+ struct stat st;
+
+ if (lstat(name, &st) < 0) {
+ perror_reply(550, name);
+ return ((char *) 0);
+ }
+ reply(350, "File exists, ready for destination name");
+ return (name);
+}
+
+void renamecmd(char *from, char *to)
+{
+ int allowed = (anonymous ? 0 : 1);
+ char realfrom[MAXPATHLEN];
+ char realto[MAXPATHLEN];
+ struct aclmember *entry = NULL;
+#ifdef PARANOID
+ struct stat st;
+#endif
+ wu_realpath(from, realfrom, chroot_path);
+ wu_realpath(to, realto, chroot_path);
+ /*
+ * check the filename, is it legal?
+ */
+ if ((fn_check(to)) == 0) {
+ if (log_security)
+ if (anonymous)
+ syslog(LOG_NOTICE, "anonymous(%s) of %s tried to rename %s to \"%s\" (path-filter)",
+ guestpw, remoteident, realfrom, realto);
+ else
+ syslog(LOG_NOTICE, "%s of %s tried to rename %s to \"%s\" (path-filter)",
+ pw->pw_name, remoteident, realfrom, realto);
+ return;
+ }
+
+ /*
+ * if rename permission denied and file exists... then deny the user
+ * permission to rename the file.
+ */
+ while (getaclentry("rename", &entry) && ARG0 && ARG1 != NULL) {
+ if (type_match(ARG1))
+ if (anonymous) {
+ if (*ARG0 == 'y')
+ allowed = 1;
+ }
+ else if (*ARG0 == 'n')
+ allowed = 0;
+ }
+ if (!allowed) {
+ if (log_security)
+ if (anonymous)
+ syslog(LOG_NOTICE, "anonymous(%s) of %s tried to rename %s to %s",
+ guestpw, remoteident, realfrom, realto);
+ else
+ syslog(LOG_NOTICE, "%s of %s tried to rename %s to %s",
+ pw->pw_name, remoteident, realfrom, realto);
+ reply(553, "%s: Permission denied on server. (rename)", from);
+ return;
+ }
+
+
+#ifdef PARANOID
+/* Almost forgot about this. Don't allow renaming TO existing files --
+ otherwise someone can rename "trivial" to "warez", and "warez" is gone!
+ XXX: This part really should do the same "overwrite" check as store(). */
+ if (!stat(to, &st)) {
+ if (log_security)
+ if (anonymous)
+ syslog(LOG_NOTICE, "anonymous(%s) of %s tried to rename %s to %s",
+ guestpw, remoteident, realfrom, realto);
+ else
+ syslog(LOG_NOTICE, "%s of %s tried to rename %s to %s",
+ pw->pw_name, remoteident, realfrom, realto);
+ reply(550, "%s: Permission denied on server. (rename)", to);
+ return;
+ }
+#endif
+ if (rename(from, to) < 0) {
+ if (log_security)
+ if (anonymous)
+ syslog(LOG_NOTICE, "anonymous(%s) of %s tried to rename %s to %s",
+ guestpw, remoteident, realfrom, realto);
+ else
+ syslog(LOG_NOTICE, "%s of %s tried to rename %s to %s",
+ pw->pw_name, remoteident, realfrom, realto);
+ perror_reply(550, "rename");
+ }
+ else {
+ char frompath[MAXPATHLEN];
+ char topath[MAXPATHLEN];
+
+ wu_realpath(from, frompath, chroot_path);
+ wu_realpath(to, topath, chroot_path);
+
+ if (log_security)
+ if (anonymous) {
+ syslog(LOG_NOTICE, "%s of %s renamed %s to %s", guestpw, remoteident, frompath, topath);
+ }
+ else {
+ syslog(LOG_NOTICE, "%s of %s renamed %s to %s", pw->pw_name,
+ remoteident, frompath, topath);
+ }
+ ack("RNTO");
+ }
+}
+
+void dolog(struct SOCKSTORAGE *sin)
+{
+ char *blah;
+ int rval;
+
+ blah = inet_stop(sin);
+ (void) strncpy(remoteaddr, blah, sizeof(remoteaddr));
+ nameserved = 0;
+ (void) strncpy(remotehost, remoteaddr, sizeof(remotehost));
+
+ rhlookup = rhostlookup(remoteaddr);
+ if (rhlookup) {
+ if (!strcasecmp(remoteaddr, "0.0.0.0")) {
+ nameserved = 1;
+ strncpy(remotehost, "localhost", sizeof(remotehost));
+ }
+ else {
+#ifdef DNS_TRYAGAIN
+ int num_dns_tries = 0;
+ /*
+ * 27-Apr-93 EHK/BM
+ * far away connections might take some time to get their IP address
+ * resolved. That's why we try again -- maybe our DNS cache has the
+ * PTR-RR now. This code is sloppy. Far better is to check what the
+ * resolver returned so that in case of error, there's no need to
+ * try again.
+ */
+ dns_again:
+#endif /* DNS_TRYAGAIN */
+
+ rval = wu_gethostbyaddr(sin, remotehost, sizeof(remotehost));
+
+#ifdef DNS_TRYAGAIN
+ if (!rval && ++num_dns_tries <= 1) {
+ sleep(3);
+ goto dns_again; /* try DNS lookup once more */
+ }
+#endif /* DNS_TRYAGAIN */
+
+ if (rval)
+ nameserved = 1;
+ }
+ }
+
+ remotehost[sizeof(remotehost) - 1] = '\0';
+ sprintf(proctitle, "%s: connected", remotehost);
+ setproctitle("%s", proctitle);
+
+ wu_authenticate();
+/* Create a composite source identification string, to improve the logging
+ * when RFC 931 is being used. */
+ {
+ int n = 20 + strlen(remotehost) + strlen(remoteaddr) +
+ (authenticated ? strlen(authuser + 5) : 0);
+ if ((remoteident = malloc(n)) == NULL) {
+ syslog(LOG_ERR, "malloc: %m");
+#ifndef DEBUG
+ exit(1);
+#endif
+ }
+ else if (authenticated)
+ sprintf(remoteident, "%s @ %s [%s]",
+ authuser, remotehost, remoteaddr);
+ else
+ sprintf(remoteident, "%s [%s]", remotehost, remoteaddr);
+ }
+#ifdef DAEMON
+ if (be_daemon && logging)
+ syslog(LOG_INFO, "connection from %s", remoteident);
+#else
+#if 0 /* this is redundant unless the caller doesn't do *anything*, and
+ tcpd will pick it up and deal with it better anyways. _H */
+ if (logging)
+ syslog(LOG_INFO, "connection from %s", remoteident);
+#endif
+#endif
+}
+
+/* Record logout in wtmp file and exit with supplied status. */
+
+void dologout(int status)
+{
+ /*
+ * Prevent reception of SIGURG from resulting in a resumption
+ * back to the main program loop.
+ */
+ transflag = 0;
+
+ /*
+ * Cancel any pending alarm request, reception of SIGALRM would cause
+ * dologout() to be called again from the SIGALRM handler toolong().
+ */
+ (void) alarm(0);
+
+ if (logged_in) {
+ delay_signaling(); /* we can't allow any signals while euid==0: kinch */
+#if defined(SOLARIS_BSM_AUDIT) && !defined(SOLARIS_NO_AUDIT_FTPD_LOGOUT)
+ audit_ftpd_logout();
+#endif
+ (void) seteuid((uid_t) 0);
+ if (wtmp_logging)
+ wu_logwtmp(ttyline, pw->pw_name, remotehost, 0);
+#ifdef USE_PAM
+ if (!anonymous && pamh) {
+ (void) pam_close_session(pamh, 0);
+ (void) pam_end(pamh, PAM_SUCCESS);
+ pamh = (pam_handle_t *)0;
+ }
+#endif
+ }
+ if (logging)
+ syslog(LOG_INFO, "FTP session closed");
+ if (xferlog)
+ close(xferlog);
+ acl_remove();
+ if (data >= 0)
+ close(data);
+ if (pdata >= 0)
+ close(pdata);
+#ifdef AFS_AUTH
+ ktc_ForgetAllTokens();
+#endif
+ /* beware of flushing buffers after a SIGPIPE */
+ _exit(status);
+}
+
+SIGNAL_TYPE myoob(int sig)
+{
+ char *cp;
+#ifdef SIGPIPE
+ void (*pipe_handler)();
+#endif
+
+ /* only process if transfer occurring */
+ if (!transflag) {
+#ifdef SIGURG
+ (void) signal(SIGURG, myoob);
+#endif
+ return;
+ }
+#ifdef SIGPIPE
+ pipe_handler = signal(SIGPIPE, lostconn);
+#endif
+ cp = tmpline;
+ if (wu_getline(cp, sizeof(tmpline) - 1, stdin) == NULL) {
+ reply(221, "You could at least say goodbye.");
+ dologout(0);
+ }
+ upper(cp);
+ if (strcasecmp(cp, "ABOR\r\n") == 0) {
+ tmpline[0] = '\0';
+ reply(426, "Transfer aborted. Data connection closed.");
+ reply(226, "Abort successful");
+#ifdef SIGPIPE
+ (void) signal(SIGPIPE, pipe_handler);
+#endif
+#ifdef SIGURG
+ (void) signal(SIGURG, myoob);
+#endif
+ if (ftwflag > 0) {
+ ftwflag++;
+ return;
+ }
+ wu_longjmp(urgcatch, 1);
+ }
+ if (strcasecmp(cp, "STAT\r\n") == 0) {
+ tmpline[0] = '\0';
+ if (file_size != (off_t) - 1)
+ reply(213, "Status: %" L_FORMAT " of %" L_FORMAT " bytes transferred",
+ byte_count, file_size);
+ else
+ reply(213, "Status: %" L_FORMAT " bytes transferred", byte_count);
+ }
+#ifdef SIGPIPE
+ (void) signal(SIGPIPE, pipe_handler);
+#endif
+#ifdef SIGURG
+ (void) signal(SIGURG, myoob);
+#endif
+}
+
+/* Note: a response of 425 is not mentioned as a possible response to the
+ * PASV command in RFC959. However, it has been blessed as a legitimate
+ * response by Jon Postel in a telephone conversation with Rick Adams on 25
+ * Jan 89. */
+
+void passive(int passive_mode, int proto)
+{
+ /* First prime number after 2^n where 4 <= n <= 16 */
+ static int primes[] = {17,37,67,131,257,521,1031,2053,4099,8209,16411,32771,65537,0};
+ static int prime = 0;
+ static int range;
+#if defined(UNIXWARE) || defined(AIX)
+ size_t len;
+#else
+ int len;
+#endif
+ int bind_error, serrno;
+ int on = 1;
+ int i, j, inc, val;
+ unsigned short port;
+ register char *p, *a;
+ struct SOCKSTORAGE *reply_addr;
+ struct timeval tv;
+#ifdef INET6
+ int isv4 = 0;
+#endif
+
+/* H* fix: if we already *have* a passive socket, close it first. Prevents
+ a whole variety of entertaining clogging attacks. */
+ if (pdata >= 0) {
+ close(pdata);
+ pdata = -1;
+ }
+ if (!logged_in) {
+ reply(530, "Login with USER first.");
+ return;
+ }
+#ifdef INET6
+ switch (proto) {
+ case 0:
+ if ((passive_mode == TYPE_PASV) && (SOCK_FAMILY(ctrl_addr) == AF_INET6)
+ && !ctrl_v4mapped) {
+ reply(501, "Network protocol mismatch");
+ return;
+ }
+ else
+ pasv_addr = ctrl_addr;
+ break;
+ case 1:
+ if (SOCK_FAMILY(ctrl_addr) == AF_INET)
+ pasv_addr = ctrl_addr;
+ else if (ctrl_v4mapped) {
+ struct sockaddr_in6 *ctrl_sin6 = (struct sockaddr_in6 *)&ctrl_addr;
+ struct sockaddr_in *pasv_sin = (struct sockaddr_in *)&pasv_addr;
+
+ SET_SOCK_FAMILY(pasv_addr, AF_INET);
+ memcpy(&pasv_sin->sin_addr, &ctrl_sin6->sin6_addr.s6_addr[12],
+ sizeof(struct in_addr));
+ }
+ else {
+ reply(522, "Network protocol mismatch, use (2)");
+ return;
+ }
+ break;
+ case 2:
+ if ((SOCK_FAMILY(ctrl_addr) == AF_INET6) && !ctrl_v4mapped)
+ pasv_addr = ctrl_addr;
+ else {
+ reply(522, "Network protocol mismatch, use (1)");
+ return;
+ }
+ break;
+ default:
+ reply(522, "Network protocol not supported, use (1,2)");
+ return;
+ }
+#else
+ pasv_addr = ctrl_addr;
+#endif
+
+ if (passive_port_min == 0 && passive_port_max == 0) {
+ /* let the kernel allocate the port */
+ SET_SOCK_PORT(pasv_addr, 0);
+ }
+ else if (prime == 0) {
+ range = passive_port_max - passive_port_min + 1;
+
+ /* find the first prime greater than the range in the primes list */
+ for (i = 0; primes[i] != 0 && range >= primes[i]; i++)
+ ;
+ /* shouldn't happen, but check just in case */
+ if (primes[i] == 0) {
+ syslog(LOG_ERR, "passive ports range too large %d-%d", passive_port_min, passive_port_max);
+ /* let the kernel allocate the port */
+ SET_SOCK_PORT(pasv_addr, 0);
+ }
+ else
+ prime = primes[i];
+ }
+ len = SOCK_LEN(pasv_addr);
+
+ port_priv_on(0); /* necessary as port can be < 1024 */
+ pdata = socket(SOCK_FAMILY(pasv_addr), SOCK_STREAM, 0);
+ if (pdata < 0) {
+ serrno = errno;
+ port_priv_off((uid_t) pw->pw_uid);
+ errno = serrno;
+ perror_reply(425, "Can't open passive connection");
+ return;
+ }
+ if (keepalive)
+ (void) setsockopt(pdata, SOL_SOCKET, SO_KEEPALIVE, (char *) &on, sizeof(on));
+ if (TCPwindowsize) {
+ (void) setsockopt(pdata, SOL_SOCKET, SO_SNDBUF, (char *) &TCPwindowsize, sizeof(TCPwindowsize));
+ (void) setsockopt(pdata, SOL_SOCKET, SO_RCVBUF, (char *) &TCPwindowsize, sizeof(TCPwindowsize));
+ }
+
+ bind_error = -1;
+ errno = EADDRINUSE;
+
+ /* try each port in the specified range a maximum of 3 times */
+ for (i = 0; i < 3 && bind_error != 0 && errno == EADDRINUSE; i++) {
+ if (i > 0)
+ sleep(i);
+ if (SOCK_PORT(pasv_addr) == 0)
+ bind_error = bind(pdata, (struct sockaddr *) &pasv_addr, len);
+ else {
+ gettimeofday(&tv, NULL);
+ srand(tv.tv_usec + tv.tv_sec);
+ inc = 1 + (int) ((1.0 * (prime - 1) * rand()) / (RAND_MAX + 1.0));
+ val = (int) ((1.0 * range * rand()) / (RAND_MAX + 1.0));
+ /*
+ * Using the modulus operator with a prime number allows us to
+ * try each port in the range once.
+ */
+ for (j = 0; j < range && bind_error != 0 && errno == EADDRINUSE; j++) {
+ while ((val = ((val + inc) % prime)) >= range)
+ ;
+ SET_SOCK_PORT(pasv_addr, htons(val + passive_port_min));
+ bind_error = bind(pdata, (struct sockaddr *) &pasv_addr, len);
+ }
+ }
+ }
+ serrno = errno;
+ port_priv_off((uid_t) pw->pw_uid);
+ if (bind_error != 0) {
+ errno = serrno;
+ goto pasv_error;
+ }
+
+ /* if the kernel allocated the port, find out which one */
+ if ((SOCK_PORT(pasv_addr) == 0) &&
+ (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0))
+ goto pasv_error;
+
+ if (listen(pdata, 1) < 0)
+ goto pasv_error;
+ usedefault = 1;
+ if (route_vectored)
+ reply_addr = &vect_addr;
+ else
+ reply_addr = &pasv_addr;
+ a = (char *) SOCK_ADDR(*reply_addr);
+ port = SOCK_PORT(pasv_addr);
+ p = (char *) &port;
+
+#define UC(b) (((int) b) & 0xff)
+
+ if (debug) {
+ char *s = calloc(128 + strlen(remoteident), sizeof(char));
+ if (s) {
+ int i = ntohs(port);
+ sprintf(s, "PASV port %i assigned to %s", i, remoteident);
+ syslog(LOG_DEBUG, "%s", s);
+ free(s);
+ }
+ }
+#ifdef INET6
+ if (SOCK_FAMILY(*reply_addr) == AF_INET)
+ isv4 = 1;
+ else if (IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)reply_addr)->sin6_addr)) {
+ isv4 = 1;
+ a += 12; /* move to the IPv4 part of an IPv4-mapped IPv6 address */
+ }
+ switch (passive_mode) {
+ case TYPE_PASV:
+ reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)",
+ UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
+ return;
+ case TYPE_EPSV:
+ reply(229, "Entering Extended Passive Mode (|||%d|)", ntohs(port));
+ return;
+ case TYPE_LPSV:
+ if (isv4) {
+ reply(228, "Entering Long Passive Mode "
+ "(%d,%d,%d,%d,%d,%d,%d,%d,%d)",
+ 4, 4, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
+ 2, UC(p[0]), UC(p[1]));
+ }
+ else {
+ reply(228, "Entering Long Passive Mode "
+ "(%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,"
+ "%d,%d,%d,%d,%d)", 6, 16,
+ UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
+ UC(a[4]), UC(a[5]), UC(a[6]), UC(a[7]),
+ UC(a[8]), UC(a[9]), UC(a[10]), UC(a[11]),
+ UC(a[12]), UC(a[13]), UC(a[14]), UC(a[15]),
+ 2, UC(p[0]), UC(p[1]));
+ }
+ return;
+ }
+#else
+ reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),
+ UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
+ return;
+#endif /* INET6 */
+
+ pasv_error:
+ perror_reply(425, "Can't open passive connection");
+ (void) close(pdata);
+ pdata = -1;
+ if (debug) {
+ char *s = calloc(128 + strlen(remoteident), sizeof(char));
+ if (s) {
+ sprintf(s, "PASV port assignment assigned for %s", remoteident);
+ syslog(LOG_DEBUG, "%s", s);
+ free(s);
+ }
+ }
+ return;
+}
+
+/*
+ * Generate unique name for file with basename "local". The file named
+ * "local" is already known to exist. Generates failure reply on error.
+ */
+char *gunique(char *local)
+{
+ static char new[MAXPATHLEN];
+ struct stat st;
+ char *cp = strrchr(local, '/');
+ int count = 0;
+
+ if (cp)
+ *cp = '\0';
+ if (stat(cp ? local : ".", &st) < 0) {
+ perror_reply(553, cp ? local : ".");
+ return ((char *) 0);
+ }
+ if (cp)
+ *cp = '/';
+ (void) strncpy(new, local, (sizeof new) - 3);
+ new[sizeof(new) - 3] = '\0';
+ cp = new + strlen(new);
+ *cp++ = '.';
+ for (count = 1; count < 100; count++) {
+ if (count == 10) {
+ cp -= 2;
+ *cp++ = '.';
+ }
+ (void) sprintf(cp, "%d", count);
+ if (stat(new, &st) < 0)
+ return (new);
+ }
+ reply(452, "Unique file name cannot be created.");
+ return ((char *) 0);
+}
+
+/* Format and send reply containing system error number. */
+
+void perror_reply(int code, char *string)
+{
+ /*
+ * If restricted user and string starts with home dir path, strip it off
+ * and return only the relative path.
+ */
+ if (restricted_user && (home != NULL) && (home[0] != '\0')) {
+ size_t len = strlen (home);
+ if (strncmp (home, string, len) == 0) {
+ if (string[len - 1] == '/')
+ string += len - 1;
+ else if (string[len] == '/')
+ string += len;
+ else if (string[len] == '\0')
+ string = "/";
+ }
+ }
+ reply(code, "%s: %s.", string, strerror(errno));
+}
+
+static char *onefile[] =
+{"", 0};
+
+extern char **ftpglob(register char *v);
+extern char *globerr;
+
+void send_file_list(char *whichfiles)
+{
+ /* static so not clobbered by longjmp(), volatile would also work */
+ static FILE *dout;
+ static DIR *dirp;
+ static char **sdirlist;
+ static char *wildcard = NULL;
+
+ struct stat st;
+
+ register char **dirlist, *dirname;
+ int simple = 0;
+ int statret;
+ /* This is ANSI/ISO C .. strpbrk should be in <string.h> which we've
+ ** already included so we don't need the following line. 'sides, it
+ ** breaks the GNU EGCS C compiler
+ ** extern char *strpbrk(const char *, const char *);
+ */
+
+#ifdef TRANSFER_COUNT
+#ifdef TRANSFER_LIMIT
+ if (((file_limit_raw_out > 0) && (xfer_count_out >= file_limit_raw_out))
+ || ((file_limit_raw_total > 0) && (xfer_count_total >= file_limit_raw_total))
+ || ((data_limit_raw_out > 0) && (byte_count_out >= data_limit_raw_out))
+ || ((data_limit_raw_total > 0) && (byte_count_total >= data_limit_raw_total))) {
+ if (log_security)
+ if (anonymous)
+ syslog(LOG_NOTICE, "anonymous(%s) of %s tried to list files (Transfer limits exceeded)",
+ guestpw, remoteident);
+ else
+ syslog(LOG_NOTICE, "%s of %s tried to list files (Transfer limits exceeded)",
+ pw->pw_name, remoteident);
+ reply(553, "Permission denied on server. (Transfer limits exceeded)");
+ return;
+ }
+#endif
+#endif
+
+ draconian_FILE = NULL;
+ dout = NULL;
+ dirp = NULL;
+ sdirlist = NULL;
+ wildcard = NULL;
+ if (strpbrk(whichfiles, "~{[*?") == NULL) {
+ if (whichfiles[0] == '\0') {
+ wildcard = strdup("*");
+ if (wildcard == NULL) {
+ reply(550, "Memory allocation error");
+ goto globfree;
+ }
+ whichfiles = wildcard;
+ }
+ else {
+ if (statret=stat(whichfiles, &st) < 0)
+ statret=lstat(whichfiles, &st); /* Check if it's a dangling symlink */
+ if (statret >= 0) {
+ if ((st.st_mode & S_IFMT) == S_IFDIR) {
+ wildcard = malloc(strlen(whichfiles) + 3);
+ if (wildcard == NULL) {
+ reply(550, "Memory allocation error");
+ goto globfree;
+ }
+ strcpy(wildcard, whichfiles);
+ strcat(wildcard, "/*");
+ whichfiles = wildcard;
+ }
+ }
+ }
+ }
+ if (strpbrk(whichfiles, "~{[*?") != NULL) {
+ globerr = NULL;
+ dirlist = ftpglob(whichfiles);
+ sdirlist = dirlist; /* save to free later */
+ if (globerr != NULL) {
+ reply(550, "%s", globerr);
+ goto globfree;
+ }
+ else if (dirlist == NULL) {
+ errno = ENOENT;
+ perror_reply(550, whichfiles);
+ goto globfree;
+ }
+ }
+ else {
+ onefile[0] = whichfiles;
+ dirlist = onefile;
+ simple = 1;
+ }
+
+ if (wu_setjmp(urgcatch)) {
+ transflag = 0;
+ if (dout != NULL)
+ (void) fclose(dout);
+ if (dirp != NULL)
+ (void) closedir(dirp);
+ data = -1;
+ pdata = -1;
+ goto globfree;
+ }
+ while ((dirname = *dirlist++) != NULL) {
+ statret=stat(dirname, &st);
+ if (statret < 0)
+ statret=lstat(dirname, &st); /* Could be a dangling symlink */
+ if (statret < 0) {
+ /* If user typed "ls -l", etc, and the client used NLST, do what
+ * the user meant. */
+ if (dirname[0] == '-' && *dirlist == NULL && transflag == 0) {
+ retrieve_is_data = 0;
+#ifndef INTERNAL_LS
+ retrieve(ls_plain, dirname);
+#else
+ ls(dirname, 1);
+#endif
+ retrieve_is_data = 1;
+ goto globfree;
+ }
+ perror_reply(550, dirname);
+ if (dout != NULL) {
+ (void) fclose(dout);
+ transflag = 0;
+ data = -1;
+ pdata = -1;
+ }
+ goto globfree;
+ }
+#ifndef NLST_SHOWS_DIRS
+ if ((st.st_mode & S_IFMT) != S_IFDIR)
+#endif
+ {
+ if (dout == NULL) {
+ dout = dataconn("file list", (off_t) - 1, "w");
+ if (dout == NULL)
+ goto globfree;
+ transflag++;
+ draconian_FILE = dout;
+ }
+ if (draconian_FILE != NULL) {
+ (void) signal(SIGALRM, draconian_alarm_signal);
+ alarm(timeout_data);
+#if defined(USE_GSS)
+ (void) sec_fprintf(dout, "%s%s\n", dirname,
+ type == TYPE_A ? "\r" : "");
+#else
+ fprintf(dout, "%s%s\n", dirname,
+ type == TYPE_A ? "\r" : "");
+#endif /* USE_GSS */
+ }
+ byte_count += strlen(dirname) + 1;
+#ifdef TRANSFER_COUNT
+ byte_count_total += strlen(dirname) + 1;
+ byte_count_out += strlen(dirname) + 1;
+ if (type == TYPE_A) {
+ byte_count_total++;
+ byte_count_out++;
+ }
+#endif
+ }
+ }
+
+ if (dout != NULL) {
+ if (draconian_FILE != NULL) {
+ (void) signal(SIGALRM, draconian_alarm_signal);
+ alarm(timeout_data);
+#if defined(USE_GSS)
+ if (sec_fflush(dout) < 0) {
+ alarm(0);
+ perror_reply(550, "Data connection");
+ goto sfl_cleanup; /* send file list cleanup */
+ }
+#else
+ fflush(dout);
+#endif /* USE_GSS */
+ }
+ if (draconian_FILE != NULL) {
+ (void) signal(SIGALRM, draconian_alarm_signal);
+ alarm(timeout_data);
+ socket_flush_wait(dout);
+ }
+ }
+ if (dout == NULL)
+ reply(550, "No files found.");
+ else if ((draconian_FILE == NULL) || ferror(dout) != 0) {
+ alarm(0);
+ perror_reply(550, "Data connection");
+ }
+ else {
+#ifdef TRANSFER_COUNT
+ xfer_count_total++;
+ xfer_count_out++;
+#endif
+ alarm(0);
+ reply(226, "Transfer complete.");
+ }
+ sfl_cleanup:
+ transflag = 0;
+ if ((dout != NULL) && (draconian_FILE != NULL))
+ (void) fclose(dout);
+ data = -1;
+ pdata = -1;
+ globfree:
+ if (wildcard != NULL) {
+ free(wildcard);
+ wildcard = NULL;
+ }
+ if (sdirlist) {
+ blkfree(sdirlist);
+ free((char *) sdirlist);
+ }
+}
+
+/*
+ ** SETPROCTITLE -- set process title for ps
+ **
+ ** Parameters:
+ ** fmt -- a printf style format string.
+ ** a, b, c -- possible parameters to fmt.
+ **
+ ** Returns:
+ ** none.
+ **
+ ** Side Effects:
+ ** Clobbers argv of our main procedure so ps(1) will
+ ** display the title.
+ */
+
+#define SPT_NONE 0 /* don't use it at all */
+#define SPT_REUSEARGV 1 /* cover argv with title information */
+#define SPT_BUILTIN 2 /* use libc builtin */
+#define SPT_PSTAT 3 /* use pstat(PSTAT_SETCMD, ...) */
+#define SPT_PSSTRINGS 4 /* use PS_STRINGS->... */
+#define SPT_SYSMIPS 5 /* use sysmips() supported by NEWS-OS 6 */
+#define SPT_SCO 6 /* write kernel u. area */
+#define SPT_CHANGEARGV 7 /* write our own strings into argv[] */
+#define MAXLINE 2048 /* max line length for setproctitle */
+#define SPACELEFT(buf, ptr) (sizeof buf - ((ptr) - buf))
+
+#ifndef SPT_TYPE
+#define SPT_TYPE SPT_REUSEARGV
+#endif
+
+#if SPT_TYPE != SPT_NONE && SPT_TYPE != SPT_BUILTIN
+
+#if SPT_TYPE == SPT_PSTAT
+#include <sys/pstat.h>
+#endif
+#if SPT_TYPE == SPT_PSSTRINGS
+#include <machine/vmparam.h>
+#include <sys/exec.h>
+#ifndef PS_STRINGS /* hmmmm.... apparently not available after all */
+#undef SPT_TYPE
+#define SPT_TYPE SPT_REUSEARGV
+#else
+#ifndef NKPDE /* FreeBSD 2.0 */
+#define NKPDE 63
+typedef unsigned int *pt_entry_t;
+#endif
+#endif
+#endif
+
+#if SPT_TYPE == SPT_PSSTRINGS || SPT_TYPE == SPT_CHANGEARGV
+#define SETPROC_STATIC static
+#else
+#define SETPROC_STATIC
+#endif
+
+#if SPT_TYPE == SPT_SYSMIPS
+#include <sys/sysmips.h>
+#include <sys/sysnews.h>
+#endif
+
+#if SPT_TYPE == SPT_SCO
+#ifdef UNIXWARE
+#include <sys/exec.h>
+#include <sys/ksym.h>
+#include <sys/proc.h>
+#include <sys/user.h>
+#else /* UNIXWARE */
+#include <sys/immu.h>
+#include <sys/dir.h>
+#include <sys/user.h>
+#include <sys/fs/s5param.h>
+#endif /* UNIXWARE */
+#if PSARGSZ > MAXLINE
+#define SPT_BUFSIZE PSARGSZ
+#endif
+#ifndef _PATH_KMEM
+#define _PATH_KMEM "/dev/kmem"
+#endif /* _PATH_KMEM */
+#endif /* SPT_SCO */
+
+#ifndef SPT_PADCHAR
+#define SPT_PADCHAR ' '
+#endif
+
+#ifndef SPT_BUFSIZE
+#define SPT_BUFSIZE MAXLINE
+#endif
+
+#endif /* SPT_TYPE != SPT_NONE && SPT_TYPE != SPT_BUILTIN */
+
+#if SPT_TYPE == SPT_REUSEARGV || SPT_TYPE == SPT_CHANGEARGV
+char **Argv = NULL; /* pointer to argument vector */
+#endif
+
+#if SPT_TYPE == SPT_REUSEARGV
+char *LastArgv = NULL; /* end of argv */
+#endif
+
+/*
+ ** Pointers for setproctitle.
+ ** This allows "ps" listings to give more useful information.
+ */
+void initsetproctitle(argc, argv, envp)
+ int argc;
+ char **argv;
+ char **envp;
+{
+#if SPT_TYPE == SPT_REUSEARGV
+ register int i, envpsize = 0;
+ char **newenviron;
+ extern char **environ;
+
+ /*
+ ** Save start and extent of argv for setproctitle.
+ */
+
+ LastArgv = argv[argc - 1] + strlen(argv[argc - 1]);
+ if (envp != NULL) {
+ /*
+ ** Move the environment so setproctitle can use the space at
+ ** the top of memory.
+ */
+ for (i = 0; envp[i] != NULL; i++)
+ envpsize += strlen(envp[i]) + 1;
+ newenviron = (char **) malloc(sizeof(char *) * (i + 1));
+ if (newenviron) {
+ int err = 0;
+ for (i = 0; envp[i] != NULL; i++) {
+ if ((newenviron[i] = strdup(envp[i])) == NULL) {
+ err = 1;
+ break;
+ }
+ }
+ if (err) {
+ for (i = 0; newenviron[i] != NULL; i++)
+ free(newenviron[i]);
+ free(newenviron);
+ i = 0;
+ }
+ else {
+ newenviron[i] = NULL;
+ environ = newenviron;
+ }
+ }
+ else {
+ i = 0;
+ }
+
+ /*
+ ** Find the last environment variable within wu-ftpd's
+ ** process memory area.
+ */
+ while (i > 0 && (envp[i - 1] < argv[0] ||
+ envp[i - 1] > (argv[argc - 1] + strlen(argv[argc - 1]) +
+ 1 + envpsize)))
+ i--;
+
+ if (i > 0)
+ LastArgv = envp[i - 1] + strlen(envp[i - 1]);
+ }
+#endif /* SPT_TYPE == SPT_REUSEARGV */
+
+#if SPT_TYPE == SPT_REUSEARGV || SPT_TYPE == SPT_CHANGEARGV
+ Argv = argv;
+#endif
+}
+
+
+#if SPT_TYPE != SPT_BUILTIN
+
+/*VARARGS1 */
+void setproctitle(const char *fmt,...)
+{
+#if SPT_TYPE != SPT_NONE
+ register char *p;
+ register int i;
+ SETPROC_STATIC char buf[SPT_BUFSIZE];
+ VA_LOCAL_DECL
+#if SPT_TYPE == SPT_PSTAT
+ union pstun pst;
+#endif
+#if SPT_TYPE == SPT_SCO
+ static off_t seek_off;
+ static int kmemfd = -1;
+ static int kmempid = -1;
+#ifdef UNIXWARE
+ off_t offset;
+ void *ptr;
+ struct mioc_rksym rks;
+#endif /* UNIXWARE */
+#endif /* SPT_SCO */
+
+ p = buf;
+
+ /* print ftpd: heading for grep */
+ (void) strcpy(p, "ftpd: ");
+ p += strlen(p);
+
+ /* print the argument string */
+ VA_START(fmt);
+ (void) vsnprintf(p, SPACELEFT(buf, p), fmt, ap);
+ VA_END;
+
+ i = strlen(buf);
+
+#if SPT_TYPE == SPT_PSTAT
+ pst.pst_command = buf;
+ pstat(PSTAT_SETCMD, pst, i, 0, 0);
+#endif
+#if SPT_TYPE == SPT_PSSTRINGS
+ PS_STRINGS->ps_nargvstr = 1;
+ PS_STRINGS->ps_argvstr = buf;
+#endif
+#if SPT_TYPE == SPT_SYSMIPS
+ sysmips(SONY_SYSNEWS, NEWS_SETPSARGS, buf);
+#endif
+#if SPT_TYPE == SPT_SCO
+ if (kmemfd < 0 || kmempid != getpid()) {
+ if (kmemfd >= 0)
+ close(kmemfd);
+ if ((kmemfd = open(_PATH_KMEM, O_RDWR, 0)) < 0)
+ return;
+ (void) fcntl(kmemfd, F_SETFD, 1);
+ kmempid = getpid();
+#ifdef UNIXWARE
+ seek_off = 0;
+ rks.mirk_symname = "upointer";
+ rks.mirk_buf = &ptr;
+ rks.mirk_buflen = sizeof(ptr);
+ if (ioctl(kmemfd, MIOC_READKSYM, &rks) < 0)
+ return;
+ offset = (off_t) ptr + (off_t) & ((struct user *) 0)->u_procp;
+ if (lseek(kmemfd, offset, SEEK_SET) != offset)
+ return;
+ if (read(kmemfd, &ptr, sizeof(ptr)) != sizeof(ptr))
+ return;
+ offset = (off_t) ptr + (off_t) & ((struct proc *) 0)->p_execinfo;
+ if (lseek(kmemfd, offset, SEEK_SET) != offset)
+ return;
+ if (read(kmemfd, &ptr, sizeof(ptr)) != sizeof(ptr))
+ return;
+ seek_off = (off_t) ptr + (off_t) ((struct execinfo *) 0)->ei_psargs;
+#else /* UNIXWARE */
+ seek_off = UVUBLK + (off_t) & ((struct user *) 0)->u_psargs;
+#endif /* UNIXWARE */
+ }
+#ifdef UNIXWARE
+ if (seek_off == 0)
+ return;
+#endif /* UNIXWARE */
+ buf[PSARGSZ - 1] = '\0';
+ if (lseek(kmemfd, (off_t) seek_off, SEEK_SET) == seek_off)
+ (void) write(kmemfd, buf, PSARGSZ);
+#endif /* SPT_SCO */
+#if SPT_TYPE == SPT_REUSEARGV
+ if (i > LastArgv - Argv[0] - 2) {
+ i = LastArgv - Argv[0] - 2;
+ buf[i] = '\0';
+ }
+ (void) strcpy(Argv[0], buf);
+ p = &Argv[0][i];
+ while (p < LastArgv)
+ *p++ = SPT_PADCHAR;
+ Argv[1] = NULL;
+#endif
+#if SPT_TYPE == SPT_CHANGEARGV
+ Argv[0] = buf;
+ Argv[1] = 0;
+#endif
+#endif /* SPT_TYPE != SPT_NONE */
+}
+
+#endif /* SPT_TYPE != SPT_BUILTIN */
+
+#ifdef KERBEROS
+/* thanks to gshapiro@wpi.wpi.edu for the following kerberosities */
+
+void init_krb()
+{
+ char hostname[100];
+
+#ifdef HAVE_SYSINFO
+ if (sysinfo(SI_HOSTNAME, hostname, sizeof(hostname)) < 0) {
+ perror("sysinfo");
+#else
+ if (gethostname(hostname, sizeof(hostname)) < 0) {
+ perror("gethostname");
+#endif
+ exit(1);
+ }
+ if (strchr(hostname, '.'))
+ *(strchr(hostname, '.')) = 0;
+
+ sprintf(krb_ticket_name, "/var/dss/kerberos/tkt/tkt.%d", getpid());
+ krb_set_tkt_string(krb_ticket_name);
+
+ config_auth();
+
+ if (krb_svc_init("hesiod", hostname, (char *) NULL, 0, (char *) NULL,
+ (char *) NULL) != KSUCCESS) {
+ fprintf(stderr, "Couldn't initialize Kerberos\n");
+ exit(1);
+ }
+}
+
+void end_krb()
+{
+ unlink(krb_ticket_name);
+}
+
+#endif /* KERBEROS */
+
+#ifdef ULTRIX_AUTH
+static int ultrix_check_pass(char *passwd, char *xpasswd)
+{
+ struct svcinfo *svp;
+ int auth_status;
+
+ if ((svp = getsvc()) == (struct svcinfo *) NULL) {
+ syslog(LOG_WARNING, "getsvc() failed in ultrix_check_pass");
+ return -1;
+ }
+ if (pw == (struct passwd *) NULL) {
+ return -1;
+ }
+ if (((svp->svcauth.seclevel == SEC_UPGRADE) &&
+ (!strcmp(pw->pw_passwd, "*")))
+ || (svp->svcauth.seclevel == SEC_ENHANCED)) {
+ if ((auth_status = authenticate_user(pw, passwd, "/dev/ttypXX")) >= 0) {
+ /* Indicate successful validation */
+ return auth_status;
+ }
+ if (auth_status < 0 && errno == EPERM) {
+ /* Log some information about the failed login attempt. */
+ switch (abs(auth_status)) {
+ case A_EBADPASS:
+ break;
+ case A_ESOFTEXP:
+ syslog(LOG_NOTICE, "password will expire soon for user %s",
+ pw->pw_name);
+ break;
+ case A_EHARDEXP:
+ syslog(LOG_NOTICE, "password has expired for user %s",
+ pw->pw_name);
+ break;
+ case A_ENOLOGIN:
+ syslog(LOG_NOTICE, "user %s attempted login to disabled acct",
+ pw->pw_name);
+ break;
+ }
+ }
+ }
+ else {
+ if ((*pw->pw_passwd != '\0') && (!strcmp(xpasswd, pw->pw_passwd))) {
+ /* passwd in /etc/passwd isn't empty && encrypted passwd matches */
+ return 0;
+ }
+ }
+ return -1;
+}
+#endif /* ULTRIX_AUTH */
+
+#ifdef USE_PAM
+/* This is rather an abuse of PAM, but the FTP protocol doesn't allow much
+ * flexibility here. :-(
+ */
+
+/* Static variables used to communicate between the conversation function
+ * and the server_login function
+ */
+static char *PAM_password;
+
+/* PAM conversation function
+ * Here we assume (for now, at least) that echo on means login name, and
+ * echo off means password.
+ */
+#ifdef SOLARIS_2
+/* Workaround bug 4430970/4413889 which causes a compiler warning, necessary
+ * as usr/src/Makefile.master now includes "-errwarn=%all".
+ */
+static int PAM_conv(int num_msg, struct pam_message **msg, struct pam_response **resp, void *appdata_ptr)
+#else
+static int PAM_conv(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr)
+#endif
+{
+ int replies = 0;
+ struct pam_response *reply = NULL;
+
+#define COPY_STRING(s) (s) ? strdup(s) : NULL
+
+ reply = malloc(sizeof(struct pam_response) * num_msg);
+ if (!reply)
+ return PAM_CONV_ERR;
+
+ for (replies = 0; replies < num_msg; replies++) {
+ switch (msg[replies]->msg_style) {
+ case PAM_PROMPT_ECHO_ON:
+ return PAM_CONV_ERR;
+ break;
+ case PAM_PROMPT_ECHO_OFF:
+ reply[replies].resp_retcode = PAM_SUCCESS;
+ reply[replies].resp = COPY_STRING(PAM_password);
+ /* PAM frees resp */
+ break;
+ case PAM_TEXT_INFO:
+ /* ignore it... */
+ reply[replies].resp_retcode = PAM_SUCCESS;
+ reply[replies].resp = NULL;
+ break;
+ case PAM_ERROR_MSG:
+ /* ignore it... */
+ reply[replies].resp_retcode = PAM_SUCCESS;
+ reply[replies].resp = NULL;
+ break;
+ default:
+ /* Must be an error of some sort... */
+ return PAM_CONV_ERR;
+ }
+ }
+ *resp = reply;
+ return PAM_SUCCESS;
+}
+static struct pam_conv PAM_conversation =
+{
+ &PAM_conv,
+ NULL
+};
+
+static int pam_check_pass(char *user, char *passwd)
+{
+ char tty[20];
+ int pam_session = 0;
+
+ /* Now use PAM to do authentication and session logging. Bail out if
+ * there are any errors. Since this is a limited protocol, and an even
+ * more limited function within a server speaking this protocol, we
+ * can't be as verbose as would otherwise make sense.
+ */
+ PAM_password = passwd;
+ pamh = (pam_handle_t *)0;
+ if (pam_start("ftp", user, &PAM_conversation, &pamh) != PAM_SUCCESS)
+ return 0;
+
+#if ((defined(BSD) && (BSD >= 199103)) || defined(sun))
+ (void) sprintf(tty, "/dev/ftp%ld", (long) getpid());
+#else
+ (void) sprintf(tty, "/dev/ftpd%d", getpid());
+#endif
+
+ if (pam_set_item(pamh, PAM_TTY, tty) != PAM_SUCCESS)
+ goto pam_fail;
+ if (pam_set_item(pamh, PAM_RHOST, remotehost) != PAM_SUCCESS)
+ goto pam_fail;
+ if (pam_authenticate(pamh, PAM_DISALLOW_NULL_AUTHTOK) != PAM_SUCCESS) {
+#ifdef SOLARIS_BSM_AUDIT
+ audit_ftpd_bad_pw(user);
+#endif
+ goto pam_fail;
+ }
+ if (pam_acct_mgmt(pamh, 0) != PAM_SUCCESS) {
+#ifdef SOLARIS_BSM_AUDIT
+ audit_ftpd_bad_pw(user);
+#endif
+ goto pam_fail;
+ }
+ if (pam_open_session(pamh, 0) != PAM_SUCCESS)
+ goto pam_fail;
+ pam_session = 1;
+#ifdef PAM_ESTABLISH_CRED
+ if (pam_setcred(pamh, PAM_ESTABLISH_CRED) != PAM_SUCCESS)
+ goto pam_fail;
+#else
+ if (pam_setcred(pamh, PAM_CRED_ESTABLISH) != PAM_SUCCESS)
+ goto pam_fail;
+#endif
+ /* If this point is reached, the user has been authenticated. */
+ return 1;
+
+pam_fail:
+ if (pam_session)
+ (void) pam_close_session(pamh, 0);
+ (void) pam_end(pamh, 0);
+ pamh = (pam_handle_t *)0;
+ return 0;
+}
+#endif
+
+#ifdef DAEMON
+
+#ifdef INET6
+static struct in6_addr acl_DaemonAddress6(void)
+{
+ struct in6_addr rv = in6addr_any;
+ struct aclmember *entry = NULL;
+
+ if (getaclentry("daemonaddress", &entry) && ARG0) {
+ if (inet_pton6(ARG0, &rv) != 1)
+ rv = in6addr_any;
+ }
+ return rv;
+}
+#endif /* INET6 */
+static unsigned long int acl_DaemonAddress(void)
+{
+ unsigned long int rv = INADDR_ANY;
+ struct aclmember *entry = NULL;
+
+ if (getaclentry("daemonaddress", &entry) && ARG0) {
+ rv = inet_addr(ARG0);
+ if (rv == -1)
+ rv = INADDR_ANY;
+ }
+ return rv;
+}
+
+/* I am running as a standalone daemon (not under inetd) */
+static void do_daemon(void)
+{
+ struct SOCKSTORAGE server;
+ struct servent *serv;
+ int pgrp;
+ int lsock;
+ int one = 1;
+ FILE *pidfile;
+ int i;
+#if defined(UNIXWARE) || defined(AIX)
+ size_t addrlen;
+#else
+ int addrlen;
+#endif
+
+ /* Some of this is "borrowed" from inn - lots of it isn't */
+
+ if (be_daemon == 2) {
+ /* Fork - so I'm not the owner of the process group any more */
+ i = fork();
+ if (i < 0) {
+ syslog(LOG_ERR, "cant fork %m");
+ exit(1);
+ }
+ /* No need for the parent any more */
+ if (i > 0)
+ exit(0);
+
+#ifdef NO_SETSID
+ pgrp = setpgrp(0, getpid());
+#else
+ pgrp = setsid();
+#endif
+ if (pgrp < 0) {
+ syslog(LOG_ERR, "cannot daemonise: %m");
+ exit(1);
+ }
+ }
+
+ if (!Bypass_PID_Files)
+ if ((pidfile = fopen(_PATH_FTPD_PID, "w"))) {
+ fprintf(pidfile, "%ld\n", (long) getpid());
+ fclose(pidfile);
+ }
+ else {
+ syslog(LOG_ERR, "Cannot write pidfile: %m");
+ }
+
+ /* Close off all file descriptors and reopen syslog */
+ if (be_daemon == 2) {
+ closelog();
+ closefds(0);
+ (void) open(_PATH_DEVNULL, O_RDWR);
+ (void) dup2(0, 1);
+ /* junk stderr */
+ (void) freopen(_PATH_DEVNULL, "w", stderr);
+
+#ifdef FACILITY
+ openlog("ftpd", LOG_PID | LOG_NDELAY, FACILITY);
+#else
+ openlog("ftpd", LOG_PID);
+#endif
+ }
+
+ if (RootDirectory != NULL) {
+ if ((chroot(RootDirectory) < 0)
+ || (chdir("/") < 0)) {
+ syslog(LOG_ERR, "Cannot chroot to initial directory, aborting.");
+ exit(1);
+ }
+ free(RootDirectory);
+ RootDirectory = NULL;
+ }
+
+ if (!use_accessfile)
+ syslog(LOG_WARNING, "FTP server started without ftpaccess file");
+
+ syslog(LOG_INFO, "FTP server (%s) ready.", version);
+
+ /* Create a socket to listen on */
+#ifdef INET6
+ if (listen_v4 == 0)
+ lsock = socket(AF_INET6, SOCK_STREAM, 0);
+ else
+#endif
+ lsock = socket(AF_INET, SOCK_STREAM, 0);
+ if (lsock < 0) {
+ syslog(LOG_ERR, "Cannot create socket to listen on: %m");
+ exit(1);
+ }
+ if (setsockopt(lsock, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof(one)) < 0) {
+ syslog(LOG_ERR, "Cannot set SO_REUSEADDR option: %m");
+ exit(1);
+ }
+ if (keepalive)
+ (void) setsockopt(lsock, SOL_SOCKET, SO_KEEPALIVE, (char *) &one, sizeof(one));
+
+#ifdef INET6
+ if (listen_v4 == 0) {
+ struct sockaddr_in6 *server_sin6 = (struct sockaddr_in6 *)&server;
+
+ memset(&server, 0, sizeof(struct sockaddr_in6));
+ server_sin6->sin6_family = AF_INET6;
+ server_sin6->sin6_addr = acl_DaemonAddress6();
+ }
+ else {
+ struct sockaddr_in *server_sin = (struct sockaddr_in *)&server;
+
+ server_sin->sin_family = AF_INET;
+ server_sin->sin_addr.s_addr = acl_DaemonAddress();
+ }
+#else
+ server.sin_family = AF_INET;
+ server.sin_addr.s_addr = acl_DaemonAddress();
+#endif
+ if (daemon_port == 0) {
+ if (!(serv = getservbyname("ftp", "tcp"))) {
+ syslog(LOG_ERR, "Cannot find service ftp: %m");
+ exit(1);
+ }
+ SET_SOCK_PORT(server, serv->s_port);
+ daemon_port = ntohs(serv->s_port);
+ }
+ else
+ SET_SOCK_PORT(server, htons(daemon_port));
+
+ if (bind(lsock, (struct sockaddr *) &server, SOCK_LEN(server)) < 0) {
+ syslog(LOG_ERR, "Cannot bind socket: %m");
+ exit(1);
+ }
+
+ listen(lsock, MAX_BACKLOG);
+
+ sprintf(proctitle, "accepting connections on port %i", daemon_port);
+ setproctitle("%s", proctitle);
+
+ while (1) {
+ int pid;
+ int msgsock;
+
+ addrlen = sizeof(his_addr);
+ msgsock = accept(lsock, (struct sockaddr *) &his_addr, &addrlen);
+ if (msgsock < 0) {
+ int severity = LOG_ERR;
+
+ if (errno == EINTR || errno == ECONNABORTED)
+ severity = LOG_INFO;
+ syslog(severity, "Accept failed: %m");
+ sleep(1);
+ continue;
+ }
+
+ /* Fork off a handler */
+ pid = fork();
+ if (pid < 0) {
+ syslog(LOG_ERR, "failed to fork: %m");
+ close(msgsock);
+ sleep(1);
+ continue;
+ }
+ if (pid == 0) {
+ /* I am that forked off child */
+ /* Only parent needs lsock */
+ close(lsock);
+ closelog();
+ /* Make sure that stdin/stdout are the new socket */
+ dup2(msgsock, 0);
+ dup2(msgsock, 1);
+ if (msgsock != 0 && msgsock != 1)
+ close(msgsock);
+#ifdef FACILITY
+ openlog("ftpd", LOG_PID | LOG_NDELAY, FACILITY);
+#else
+ openlog("ftpd", LOG_PID);
+#endif
+ setup_paths();
+ access_init();
+ return;
+ }
+
+ /* I am the parent */
+ close(msgsock);
+
+ /* Quick check to see if any of the forked off children have
+ * terminated. */
+ while ((pid = waitpid((pid_t) -1, (int *) 0, WNOHANG)) > 0) {
+ /* A child has finished */
+ }
+
+ access_init();
+ }
+}
+
+#endif /* DAEMON */
+
+#ifdef RATIO
+int is_downloadfree(char *fname)
+{
+ char rpath[MAXPATHLEN];
+ char class[BUFSIZ];
+ char *cp;
+ int which;
+ struct aclmember *entry = NULL;
+
+ if( wu_realpath(fname,rpath,chroot_path) == NULL )
+ return 0;
+
+ (void) acl_getclass(class);
+
+ if (debug)
+ syslog(LOG_DEBUG, "class: %s, fname: %s, rpath: %s", class, fname, rpath);
+
+ while( getaclentry("dl-free-dir",&entry) ) {
+ if( ARG0 == NULL )
+ continue;
+ if( strncmp(rpath,ARG0,strlen(ARG0)) == 0 ) {
+ if( ARG1 == NULL )
+ return 1;
+ else for(which = 1; (which < MAXARGS) && ARG[which]; which++) {
+ if( strcmp(class,ARG[which]) == 0 )
+ return 1;
+ }
+ }
+ }
+ while( getaclentry("dl-free",&entry) ) {
+ if( ARG0 == NULL )
+ continue;
+ if( *(ARG0) != '/' ) { /* compare basename */
+ if( (cp = strrchr(rpath,'/')) == NULL ) {
+ cp = rpath;
+ }
+ else {
+ ++cp;
+ }
+ if( strcmp(cp,ARG0) == 0 ) {
+ if( ARG1 == NULL )
+ return 1;
+ else for(which = 1; (which < MAXARGS) && ARG[which]; which++) {
+ if( strcmp(class,ARG[which]) == 0 )
+ return 1;
+ }
+ }
+ }
+ else { /* compare real path */
+ if( strcmp(rpath,ARG0) == 0 ) {
+ if( ARG1 == NULL )
+ return 1;
+ else for(which = 1; (which < MAXARGS) && ARG[which] ; which++) {
+ if( strcmp(class,ARG[which]) == 0 )
+ return 1;
+ }
+ }
+ }
+ }
+ return 0;
+}
+#endif /* RATIO */
+
+int pasv_allowed(char *remoteaddr)
+{
+ char class[MAXPATHLEN];
+ int which;
+ struct aclmember *entry = NULL;
+ (void) acl_getclass(class);
+ while (getaclentry("pasv-allow", &entry)) {
+ if ((ARG0 != NULL) && (strcasecmp(class, ARG0) == 0))
+ for (which = 1; (which < MAXARGS) && (ARG[which] != NULL); which++) {
+ if (hostmatch(ARG[which], remoteaddr, NULL))
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int port_allowed(char *remoteaddr)
+{
+ char class[MAXPATHLEN];
+ int which;
+ struct aclmember *entry = NULL;
+ (void) acl_getclass(class);
+ while (getaclentry("port-allow", &entry)) {
+ if ((ARG0 != NULL) && (strcasecmp(class, ARG0) == 0))
+ for (which = 1; (which < MAXARGS) && (ARG[which] != NULL); which++) {
+ if (hostmatch(ARG[which], remoteaddr, NULL))
+ return 1;
+ }
+ }
+ return 0;
+}
+
+#ifdef MAIL_ADMIN
+char *email(char *full_address)
+{
+ /* Get the plain address part from an e-mail address
+ (i.e. remove realname) */
+
+ static char *email_buf = NULL;
+ char *addr, *ptr;
+
+ if (email_buf != NULL)
+ free(email_buf);
+
+ email_buf = (char *) malloc(strlen(full_address) + 1);
+ addr = email_buf;
+ memset(addr, 0, strlen(full_address) + 1);
+ strcpy(addr, full_address);
+
+ /* Realname <user@host> type address */
+ if ((ptr = (char *) strchr(addr, '<')) != NULL) {
+ addr = ++ptr;
+ if ((ptr = (char *) strchr(addr, '>')) != NULL)
+ *ptr = '\0';
+ }
+
+ /* user@host (Realname) type address */
+ if (((char *) strchr(addr, ' ')) != NULL)
+ addr[strchr(addr, ' ') - addr] = '\0';
+
+ return addr;
+}
+
+FILE *SockOpen(char *host, int clientPort)
+{
+ int sock;
+ unsigned long inaddr;
+ struct sockaddr_in ad;
+ FILE *fp;
+#ifdef INET6
+ struct sockaddr_in6 ad6;
+ struct addrinfo hints, *result, *res;
+ int af = AF_INET;
+#else
+ struct hostent *hp;
+#endif
+
+ memset(&ad, 0, sizeof(ad));
+ ad.sin_family = AF_INET;
+
+#ifdef INET6
+ memset(&ad6, 0, sizeof(ad6));
+ ad6.sin6_family = AF_INET6;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_CANONNAME;
+ hints.ai_family = PF_UNSPEC;
+
+ if (getaddrinfo(host, NULL, &hints, &result) != 0)
+ return (FILE *) NULL;
+
+ for (res = result; res; res = res->ai_next) {
+ af = res->ai_family;
+ if (af == AF_INET)
+ memcpy(&ad.sin_addr, &((struct sockaddr_in *)res->ai_addr)->sin_addr, sizeof(struct in_addr));
+ else if (af == AF_INET6)
+ memcpy(&ad6.sin6_addr, &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, sizeof(struct in6_addr));
+ else
+ continue;
+
+ if (af == AF_INET6) {
+ ad6.sin6_port = htons(clientPort);
+ sock = socket(AF_INET6, SOCK_STREAM, 0);
+ if (sock < 0)
+ continue;
+ if (connect(sock, (struct sockaddr *) &ad6, sizeof(ad6)) != -1)
+ break;
+ close(sock);
+ }
+ else {
+ ad.sin_port = htons(clientPort);
+ sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (sock < 0)
+ continue;
+ if (connect(sock, (struct sockaddr *) &ad, sizeof(ad)) != -1)
+ break;
+ close(sock);
+ }
+ }
+ freeaddrinfo(result);
+ if (!res)
+ return (FILE *) NULL;
+#else
+ inaddr = inet_addr(host);
+ if (inaddr != (unsigned long) -1)
+ memcpy(&ad.sin_addr, &inaddr, sizeof(inaddr));
+ else {
+ hp = gethostbyname(host);
+ if (hp == NULL)
+ return (FILE *) NULL;
+ memcpy(&ad.sin_addr, hp->h_addr, hp->h_length);
+ }
+ ad.sin_port = htons(clientPort);
+ sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (sock < 0)
+ return (FILE *) NULL;
+ if (connect(sock, (struct sockaddr *) &ad, sizeof(ad)) < 0) {
+ close(sock);
+ return (FILE *) NULL;
+ }
+#endif /* INET6 */
+
+ fp = fdopen(sock, "r+");
+ setvbuf(fp, NULL, _IOLBF, 2048);
+ return (fp);
+}
+
+int SockPrintf(FILE *sockfp, char *format,...)
+{
+ va_list ap;
+ char buf[16384];
+
+ va_start(ap, format);
+ vsnprintf(buf, sizeof(buf), format, ap);
+ buf[sizeof(buf) - 1] = '\0';
+ va_end(ap);
+ return SockWrite(buf, 1, strlen(buf), sockfp);
+}
+
+int SockWrite(char *buf, int size, int len, FILE *sockfp)
+{
+ return (fwrite(buf, size, len, sockfp));
+}
+
+char *SockGets(FILE *sockfp, char *buf, int len)
+{
+ return (fgets(buf, len, sockfp));
+}
+
+int SockPuts(FILE *sockfp, char *buf)
+{
+ int rc;
+
+ if ((rc = SockWrite(buf, 1, strlen(buf), sockfp)))
+ return rc;
+ return SockWrite("\r\n", 1, 2, sockfp);
+}
+
+int Reply(FILE *sockfp)
+{
+ char *reply, *rec, *separator;
+ int ret = 0;
+
+ if ((reply = (char *) malloc(BUFSIZ)) == NULL)
+ return ret;
+ memset(reply, 0, 1024);
+ do {
+ rec = SockGets(sockfp, reply, BUFSIZ);
+ if (rec != NULL) {
+ ret = strtol(reply, &separator, 10);
+ }
+ else
+ ret = 250;
+ } while ((rec != NULL) && (separator[0] != ' '));
+ free(reply);
+ fflush(sockfp); /* Solaris bug: need to clear buf before fwrite() */
+ return ret;
+}
+
+int Send(FILE *sockfp, char *format,...)
+{
+ va_list ap;
+ char buf[16384];
+
+ va_start(ap, format);
+ vsnprintf(buf, sizeof(buf), format, ap);
+ buf[sizeof(buf) - 1] = '\0';
+ va_end(ap);
+ SockWrite(buf, 1, strlen(buf), sockfp);
+ return Reply(sockfp);
+}
+#endif /* MAIL_ADMIN */
+
+
+/*
+ * fixpath
+ *
+ * In principal, this is similar to realpath() or the mapping chdir function.
+ * It removes unnecessary path components. We do this to put a stop to
+ * attempts to cause a memory starvation DoS.
+ *
+ */
+
+void fixpath(char *path)
+{
+ int abs = 0;
+ char *in;
+ char *out;
+
+ if (*path == '/') {
+ abs = 1;
+ path++;
+ }
+ else if (*path == '~') {
+ do
+ path++;
+ while ((*path != '\0') && (*path != '/'));
+ if (*path == '/')
+ path++;
+ }
+ in = path;
+ out = path;
+ while (*in != '\0') {
+ if (*in == '/')
+ in++;
+ else if ((in[0] == '.') && ((in[1] == '/') || (in[1] == '\0'))) {
+ in++;
+ if (*in == '/')
+ in++;
+ }
+ else if ((in[0] == '.') && (in[1] == '.') && ((in[2] == '/') || (in[2] == '\0'))) {
+ if (out == path) {
+ if (abs) {
+ in++;
+ in++;
+ if (*in == '/')
+ in++;
+ }
+ else {
+ *out++ = *in++;
+ *out++ = *in++;
+ if (*in == '/')
+ *out++ = *in++;
+ path = out;
+ }
+ }
+ else {
+ out--;
+ while ((out != path) && (*--out != '/'));
+ in++;
+ in++;
+ if (*in == '/')
+ in++;
+ }
+ }
+ else {
+ do
+ *out++ = *in++;
+ while ((*in != '\0') && (*in != '/'));
+ if (*in == '/')
+ *out++ = *in++;
+ }
+ }
+ *out = '\0';
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpgroups b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpgroups
new file mode 100644
index 0000000000..a025a4694f
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpgroups
@@ -0,0 +1,4 @@
+# ident "%Z%%M% %I% %E% SMI"
+#
+# FTP server enhanced group access file, see ftpgroups(4).
+#
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftphosts b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftphosts
new file mode 100644
index 0000000000..249e56bd19
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftphosts
@@ -0,0 +1,4 @@
+# ident "%Z%%M% %I% %E% SMI"
+#
+# FTP server individual user host access file, see ftphosts(4).
+#
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftprestart.c b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftprestart.c
new file mode 100644
index 0000000000..ea31b1bc3d
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftprestart.c
@@ -0,0 +1,343 @@
+/*
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/****************************************************************************
+
+ Copyright (c) 1999,2000 WU-FTPD Development Group.
+ All rights reserved.
+
+ Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994
+ The Regents of the University of California.
+ Portions Copyright (c) 1993, 1994 Washington University in Saint Louis.
+ Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc.
+ Portions Copyright (c) 1989 Massachusetts Institute of Technology.
+ Portions Copyright (c) 1998 Sendmail, Inc.
+ Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P. Allman.
+ Portions Copyright (c) 1997 by Stan Barber.
+ Portions Copyright (c) 1997 by Kent Landfield.
+ Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ Free Software Foundation, Inc.
+
+ Use and distribution of this software and its source code are governed
+ by the terms and conditions of the WU-FTPD Software License ("LICENSE").
+
+ If you did not receive a copy of the license, it may be obtained online
+ at http://www.wu-ftpd.org/license.html.
+
+ $Id: ftprestart.c,v 1.7 2000/07/01 18:17:39 wuftpd Exp $
+
+****************************************************************************/
+/* ftprestart
+ **
+ ** removes the ftpd shutdown files.
+ **
+ ** In the previous versions of the wu-ftpd server it was recommended to
+ ** create a link in order for shutdown to work properly for real and
+ ** anonymous user, e.g. If you use ftpshut, it will create a message
+ ** file at the location specified in the ftpaccess shutdown directive.
+ ** ln -s /etc/shutmsg ~ftp/etc/shutmsg
+ **
+ ** When ftp service is to be restarted after an ftpshut, the shutdown
+ ** message files must be removed. This program reads the ftpaccess
+ ** file and finds the location of the system shutdown file. It
+ ** then proceeds to construct a path to the anonymous ftp area with
+ ** information found in the "ftp" account. If virtual ftp servers
+ ** are enabled, the shutdown message files within those directories
+ ** are also removed.
+ **
+ ** Initial Author: Kent Landfield
+ */
+#include "config.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <pwd.h>
+#if defined(VIRTUAL) && defined(INET6)
+#include <netinet/in.h>
+#endif
+
+#include "pathnames.h"
+
+#define MAXVIRTUALS 512
+
+char *progname;
+char *msgfiles[MAXVIRTUALS];
+int numfiles = 0;
+
+#ifdef VIRTUAL
+extern int read_servers_line(FILE *, char *, size_t, char *, size_t);
+#endif
+
+void print_copyright(void);
+
+static int newfile(char *fpath)
+{
+ int i;
+ int fnd;
+
+ /*
+ ** Check to see if the message file path has already been
+ ** seen. If so then there is no need to create it again.
+ */
+
+ fnd = 0;
+ for (i = 0; i < numfiles; i++) {
+ if (strcmp(msgfiles[i], fpath) == 0) {
+ fnd = 1;
+ break;
+ }
+ }
+ if (!fnd) {
+ msgfiles[numfiles++] = strdup(fpath);
+ return (1);
+ }
+ return (0);
+}
+
+static int remove_shutdown_file(char *path)
+{
+ struct stat stbuf;
+ int rc = 1; /* guilty until proven innocent */
+
+ fprintf(stderr, "%s: %s ", progname, path);
+
+ if (stat(path, &stbuf) == 0) {
+ if ((rc = unlink(path)) == 0)
+ fprintf(stderr, "removed.\n");
+ else
+ perror(path);
+ }
+ else
+ fprintf(stderr, "does not exist.\n");
+
+ return (rc);
+}
+
+int main(int argc, char **argv)
+{
+ int c;
+
+ char *p;
+ char *cp = NULL;
+ char linebuf[BUFSIZ];
+ char shutmsg[256];
+ char anonpath[MAXPATHLEN];
+ FILE *accessfile;
+ struct passwd *pw;
+
+#if defined(VIRTUAL)
+ FILE *svrfp;
+ char *sp;
+#ifdef INET6
+ char hostaddress[INET6_ADDRSTRLEN];
+#else
+ char hostaddress[32];
+#endif
+ char root[MAXPATHLEN];
+ char configdir[MAXPATHLEN];
+ char accesspath[MAXPATHLEN];
+ char altmsgpath[MAXPATHLEN];
+ struct stat finfo;
+#endif
+
+ if ((progname = strrchr(argv[0], '/')))
+ ++progname;
+ else
+ progname = argv[0];
+
+ if (argc > 1) {
+ while ((c = getopt(argc, argv, "V")) != EOF) {
+ switch (c) {
+ case 'V':
+ print_copyright();
+ exit(0);
+ default:
+ fprintf(stderr, "usage: %s [-V]\n", progname);
+ exit(1);
+ }
+ }
+ }
+
+ if ((accessfile = fopen(_PATH_FTPACCESS, "r")) == NULL) {
+ if (errno != ENOENT)
+ fprintf(stderr, "%s: could not open access file %s: %s\n",
+ progname, _PATH_FTPACCESS, strerror(errno));
+ exit(1);
+ }
+
+ /*
+ ** Search the access file for the 'shutdown' directive.
+ */
+
+ while (fgets(linebuf, BUFSIZ, accessfile) != NULL) {
+ if (strncasecmp(linebuf, "shutdown", 8) == 0) {
+ (void) strtok(linebuf, " \t");
+ (void) strlcpy(shutmsg, strtok(NULL, " \t"), sizeof(shutmsg));
+ cp = shutmsg;
+ if ((p = strchr(cp, '\n')) != NULL)
+ *p = '\0';
+ }
+ }
+
+ if (cp == NULL) {
+ fprintf(stderr, "%s: no shutdown path defined in ftpaccess file %s.\n",
+ progname, _PATH_FTPACCESS);
+ exit(1);
+ }
+
+ msgfiles[numfiles++] = shutmsg;
+
+ /*
+ ** Get the location of the anonymous ftp area and check
+ ** to see if there is a file shutdown file there as well.
+ ** If so, remove it.
+ */
+ if ((pw = getpwnam("ftp")) != NULL) {
+ (void) snprintf(anonpath, sizeof(anonpath), "%s%s", pw->pw_dir,
+ shutmsg);
+ if (newfile(anonpath))
+ (void) remove_shutdown_file(anonpath);
+ }
+
+#ifdef VIRTUAL
+ /*
+ ** Search the access file for virtual ftp servers.
+ ** If found, check if there are links/shutdown
+ ** message files files in the virtual server areas.
+ ** If so, remove them.
+ */
+
+ rewind(accessfile);
+
+ while (fgets(linebuf, sizeof(linebuf) - 1, accessfile) != NULL) {
+ if (strncasecmp(linebuf, "virtual", 7) == 0) {
+ if ((p = strstr(linebuf, "root")) != NULL) {
+ p += 4;
+
+ if ((cp = strchr(linebuf, '\n')) != NULL)
+ *cp = '\0';
+
+ /* skip to the path */
+
+ while (*p && isspace(*p))
+ p++;
+ cp = p;
+ while (*p && isalnum(*p))
+ p++;
+
+ (void) snprintf(altmsgpath, sizeof(altmsgpath), "%s%s", cp,
+ shutmsg);
+ if (newfile(altmsgpath))
+ (void) remove_shutdown_file(altmsgpath);
+ }
+ }
+ }
+
+
+ /*
+ ** Need to deal with the access files at the virtual domain directory
+ ** locations specified in the ftpservers file.
+ */
+
+ if ((svrfp = fopen(_PATH_FTPSERVERS, "r")) != NULL) {
+ while (read_servers_line(svrfp, hostaddress, sizeof(hostaddress),
+ configdir, sizeof(configdir)) == 1) {
+ /* get rid of any trailing slash */
+ sp = configdir + (strlen(configdir) - 1);
+ if (*sp == '/')
+ *sp = '\0';
+
+ /*
+ ** check to see that a valid directory value was
+ ** supplied and not something such as "INTERNAL"
+ **
+ ** It is valid to have a string such as "INTERNAL" in the
+ ** ftpservers entry. This is not an error. Silently ignore it.
+ */
+
+ if ((stat(configdir, &finfo) < 0) ||
+ ((finfo.st_mode & S_IFMT) != S_IFDIR))
+ continue;
+
+ (void) snprintf(accesspath, sizeof(accesspath), "%s/ftpaccess",
+ configdir);
+
+ (void) fclose(accessfile);
+
+ if ((accessfile = fopen(accesspath, "r")) == NULL) {
+ if (errno != ENOENT) {
+ fprintf(stderr, "%s: could not open access file %s: %s\n",
+ progname, accesspath, strerror(errno));
+ continue;
+ }
+ }
+
+ /* need to find the root path */
+
+ while (fgets(linebuf, sizeof(linebuf) - 1, accessfile) != NULL) {
+ if ((sp = strstr(linebuf, "root")) != NULL) {
+ if ((cp = strchr(sp, '\n')) != NULL)
+ *cp = '\0'; /* strip newline */
+ sp += 4; /* skip past "root" keyword */
+
+ while (*sp && isspace(*sp)) /* skip whitespace to path */
+ sp++;
+ cp = sp;
+ while (*sp && !isspace(*sp))
+ sp++;
+ *sp = '\0'; /* truncate blanks, comments etc. */
+ (void) strlcpy(root, cp, sizeof(root));
+ break;
+ }
+ }
+
+ rewind(accessfile);
+
+ /* need to find the shutdown message file path */
+
+ while (fgets(linebuf, sizeof(linebuf) - 1, accessfile) != NULL) {
+ if ((sp = strstr(linebuf, "shutdown")) != NULL) {
+ if ((cp = strchr(sp, '\n')) != NULL)
+ *cp = '\0'; /* strip newline */
+ sp += 8; /* skip past "root" keyword */
+
+ while (*sp && isspace(*sp)) /* skip whitespace to path */
+ sp++;
+ cp = sp;
+ while (*sp && !isspace(*sp))
+ sp++;
+ *sp = '\0'; /* truncate blanks, comments etc. */
+ break;
+ }
+ }
+
+ /*
+ ** check to make sure the admin hasn't specified
+ ** a complete path in the 'shutdown' directive.
+ */
+ if ((sp = strstr(cp, root)) == NULL)
+ (void) snprintf(altmsgpath, sizeof(altmsgpath), "%s%s", root,
+ cp);
+
+ if (newfile(altmsgpath))
+ (void) remove_shutdown_file(altmsgpath);
+ }
+ fclose(svrfp);
+ }
+#endif /* VIRTUAL */
+
+ fclose(accessfile);
+
+ /*
+ ** Time to remove the system wide shutdown file.
+ */
+ return (remove_shutdown_file(shutmsg));
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpservers b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpservers
new file mode 100644
index 0000000000..ec67385b99
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpservers
@@ -0,0 +1,4 @@
+# ident "%Z%%M% %I% %E% SMI"
+#
+# FTP server virtual hosting configuration file, see ftpservers(4).
+#
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpshut.c b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpshut.c
new file mode 100644
index 0000000000..51b4ac8e81
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpshut.c
@@ -0,0 +1,511 @@
+/*
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/****************************************************************************
+
+ Copyright (c) 1999,2000 WU-FTPD Development Group.
+ All rights reserved.
+
+ Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994
+ The Regents of the University of California.
+ Portions Copyright (c) 1993, 1994 Washington University in Saint Louis.
+ Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc.
+ Portions Copyright (c) 1989 Massachusetts Institute of Technology.
+ Portions Copyright (c) 1998 Sendmail, Inc.
+ Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P. Allman.
+ Portions Copyright (c) 1997 by Stan Barber.
+ Portions Copyright (c) 1997 by Kent Landfield.
+ Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ Free Software Foundation, Inc.
+
+ Use and distribution of this software and its source code are governed
+ by the terms and conditions of the WU-FTPD Software License ("LICENSE").
+
+ If you did not receive a copy of the license, it may be obtained online
+ at http://www.wu-ftpd.org/license.html.
+
+ $Id: ftpshut.c,v 1.12 2000/07/01 18:17:39 wuftpd Exp $
+
+****************************************************************************/
+/* ftpshut
+ * =======
+ * creates the ftpd shutdown file.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#ifdef TIME_WITH_SYS_TIME
+#include <time.h>
+#include <sys/time.h>
+#else
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#else
+#include <time.h>
+#endif
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/param.h>
+#if defined(VIRTUAL) && defined(INET6)
+#include <netinet/in.h>
+#endif
+
+#include "pathnames.h"
+
+#define WIDTH 70
+
+int verbose = 0;
+int denyoffset = 10; /* default deny time */
+int discoffset = 5; /* default disc time */
+char *message = "System shutdown at %s"; /* default message */
+
+struct tm *tp;
+
+#define MAXVIRTUALS 512
+
+char *progname;
+char *msgfiles[MAXVIRTUALS];
+int numfiles = 0;
+
+#ifdef VIRTUAL
+extern int read_servers_line(FILE *, char *, size_t, char *, size_t);
+#endif
+void print_copyright(void);
+
+static int newfile(char *fpath)
+{
+ int i;
+ int fnd;
+
+ /*
+ ** Check to see if the message file path has already been
+ ** seen. If so then there is no need to create it again.
+ */
+
+ fnd = 0;
+ for (i = 0; i < numfiles; i++) {
+ if (strcmp(msgfiles[i], fpath) == 0) {
+ fnd = 1;
+ break;
+ }
+ }
+ if (!fnd) {
+ msgfiles[numfiles++] = strdup(fpath);
+ return (1);
+ }
+ return (0);
+}
+
+static int shutdown_msgfile(char *filename, char *buffer)
+{
+ FILE *fp;
+ mode_t oldmask;
+
+ oldmask = umask(022);
+ fp = fopen(filename, "w");
+ (void) umask(oldmask);
+ if (fp == NULL) {
+ fprintf(stderr, "%s: could not open shutdown file %s: %s\n",
+ progname, filename, strerror(errno));
+ return (1);
+ }
+
+ fprintf(fp, "%.4d %.2d %.2d %.2d %.2d %.4d %.4d\n",
+ (tp->tm_year) + 1900,
+ tp->tm_mon,
+ tp->tm_mday,
+ tp->tm_hour,
+ tp->tm_min,
+ denyoffset,
+ discoffset);
+ fprintf(fp, "%s\n", buffer);
+ fclose(fp);
+ if (verbose)
+ printf("%s: %s created\n", progname, filename);
+ return (0);
+}
+
+static void massage(char *buf)
+{
+ char *sp = NULL;
+ char *ptr;
+ int i = 0;
+ int j = 0;
+
+ ptr = buf;
+
+ while (*ptr++ != '\0') {
+ ++i;
+
+ /* if we have a space, keep track of where and at what "count" */
+
+ if (*ptr == ' ') {
+ sp = ptr;
+ j = i;
+ }
+ /* magic cookies... */
+
+ if (*ptr == '%') {
+ ++ptr;
+ switch (*ptr) {
+ case 'r':
+ case 's':
+ case 'd':
+ case 'T':
+ i = i + 24;
+ break;
+ case '\n':
+ i = 0;
+ break;
+ case 'C':
+ case 'R':
+ case 'L':
+ case 'U':
+ i = i + 10;
+ break;
+ case 'M':
+ case 'N':
+ i = i + 3;
+ break;
+ case '\0':
+ return;
+ /* break; */
+ default:
+ i = i + 1;
+ break;
+ }
+ }
+ /* break up the long lines... */
+
+ if ((i >= WIDTH) && (sp != NULL)) {
+ *sp = '\n';
+ sp = NULL;
+ i = i - j;
+ }
+ }
+}
+
+static void usage(int exitval)
+{
+ fprintf(stderr,
+ "Usage: %s [-d min] [-l min] now [\"message\"]\n", progname);
+ fprintf(stderr,
+ " %s [-d min] [-l min] +dd [\"message\"]\n", progname);
+ fprintf(stderr,
+ " %s [-d min] [-l min] HHMM [\"message\"]\n", progname);
+ exit(exitval);
+}
+
+int main(int argc, char **argv)
+{
+ time_t c_time = 0;
+
+ char buf[BUFSIZ];
+
+ int c;
+ extern int optind;
+ extern char *optarg;
+
+ FILE *accessfile;
+ char *aclbuf, *myaclbuf, *crptr;
+ char *sp = NULL;
+ char linebuf[1024];
+ char shutmsg[BUFSIZ];
+ char anonpath[MAXPATHLEN];
+ struct stat finfo;
+ struct passwd *pwent;
+
+#ifdef VIRTUAL
+ char *cp = NULL;
+ FILE *svrfp;
+#ifdef INET6
+ char hostaddress[INET6_ADDRSTRLEN];
+#else
+ char hostaddress[32];
+#endif
+ char root[MAXPATHLEN];
+ char accesspath[MAXPATHLEN];
+ char configdir[MAXPATHLEN];
+ char altmsgpath[MAXPATHLEN];
+#endif
+
+ if ((progname = strrchr(argv[0], '/')))
+ ++progname;
+ else
+ progname = argv[0];
+
+ while ((c = getopt(argc, argv, "vVl:d:")) != EOF) {
+ switch (c) {
+ case 'v':
+ verbose++;
+ break;
+ case 'l':
+ denyoffset = atoi(optarg);
+ break;
+ case 'd':
+ discoffset = atoi(optarg);
+ break;
+ case 'V':
+ print_copyright();
+ exit(0);
+ default:
+ usage(-1);
+ }
+ }
+
+ if ((accessfile = fopen(_PATH_FTPACCESS, "r")) == NULL) {
+ if (errno != ENOENT)
+ fprintf(stderr, "%s: could not open access file %s: %s\n",
+ progname, _PATH_FTPACCESS, strerror(errno));
+ exit(1);
+ }
+ if (fstat(fileno(accessfile), &finfo) != 0) {
+ fprintf(stderr, "%s: could not fstat() access file %s: %s\n",
+ progname, _PATH_FTPACCESS, strerror(errno));
+ exit(1);
+ }
+ if (finfo.st_size == 0) {
+ fprintf(stderr, "%s: no shutdown path defined in ftpaccess file %s.\n",
+ progname, _PATH_FTPACCESS);
+ exit(1);
+ }
+ else {
+ if (!(aclbuf = (char *) malloc(finfo.st_size + 1))) {
+ fprintf(stderr, "%s: could not malloc aclbuf: %s\n",
+ progname, strerror(errno));
+ exit(1);
+ }
+ fread(aclbuf, finfo.st_size, 1, accessfile);
+ *(aclbuf + finfo.st_size) = '\0';
+ }
+
+ myaclbuf = aclbuf;
+ while (*myaclbuf != '\0') {
+ if (strncasecmp(myaclbuf, "shutdown", 8) == 0) {
+ for (crptr = myaclbuf; *crptr++ != '\n';);
+ *--crptr = '\0';
+ (void) strlcpy(linebuf, myaclbuf, sizeof(linebuf));
+ *crptr = '\n';
+ (void) strtok(linebuf, " \t"); /* returns "shutdown" */
+ sp = strtok(NULL, " \t"); /* returns shutdown path */
+ /* save for future use */
+ (void) strlcpy(shutmsg, sp, sizeof(shutmsg));
+ }
+ while (*myaclbuf && *myaclbuf++ != '\n');
+ }
+
+ /* three cases
+ * -- now
+ * -- +ddd
+ * -- HHMM
+ */
+
+ c = -1;
+
+ if (optind < argc) {
+ if (!strcasecmp(argv[optind], "now")) {
+ c_time = time(0);
+ tp = localtime(&c_time);
+ }
+ else if ((*(argv[optind])) == '+') {
+ c_time = time(0);
+ c_time += 60 * atoi(++(argv[optind]));
+ tp = localtime(&c_time);
+ }
+ else if ((c = atoi(argv[optind])) >= 0) {
+ c_time = time(0);
+ tp = localtime(&c_time);
+ tp->tm_hour = c / 100;
+ tp->tm_min = c % 100;
+
+ if ((tp->tm_hour > 23) || (tp->tm_min > 59)) {
+ fprintf(stderr, "%s: illegal time format.\n", progname);
+ exit(1);
+ }
+ }
+ }
+ if (c_time <= 0) {
+ usage(1);
+ }
+
+ if (sp == NULL) {
+ fprintf(stderr, "%s: no shutdown path defined in ftpaccess file %s.\n",
+ progname, _PATH_FTPACCESS);
+ exit(1);
+ }
+
+ /* do we have a shutdown message? */
+ if (++optind < argc)
+ (void) strlcpy(buf, argv[optind++], sizeof(buf));
+ else
+ (void) strlcpy(buf, message, sizeof(buf));
+
+ massage(buf);
+
+ /*
+ ** Create the system shutdown message file at the location
+ ** specified in the ftpaccess 'shutdown' directive. This
+ ** is for support of real system users.
+ */
+ c = shutdown_msgfile(shutmsg, buf);
+ msgfiles[numfiles++] = shutmsg;
+
+ /*
+ ** Determine if the site supports anonymous ftp and if so, create
+ ** the shutdown message file in the anonymous ftp area as well
+ ** so that shutdown works appropriately for both real and guest
+ ** accounts. Save in msgfiles array for later comparison.
+ */
+
+ if ((pwent = getpwnam("ftp")) != NULL) {
+ (void) snprintf(anonpath, sizeof(anonpath), "%s%s", pwent->pw_dir,
+ shutmsg);
+ if (newfile(anonpath))
+ c += shutdown_msgfile(anonpath, buf);
+ }
+
+#ifdef VIRTUAL
+ /*
+ ** Search the Master access file for virtual ftp servers.
+ ** If found, construct a path to the shutdown message file
+ ** under the virtual server's root. Don't duplicate what
+ ** is specified in the "ftp" account directory information.
+ */
+
+ rewind(accessfile);
+
+ while (fgets(linebuf, sizeof(linebuf) - 1, accessfile) != NULL) {
+ if (strncasecmp(linebuf, "virtual", 7) == 0) {
+
+ if ((sp = strstr(linebuf, "root")) != NULL) {
+ if ((cp = strchr(sp, '\n')) != NULL)
+ *cp = '\0'; /* strip newline */
+
+ sp += 4; /* skip past "root" keyword */
+
+ while (*sp && isspace(*sp)) /* skip whitespace to root path */
+ sp++;
+ cp = sp;
+ while (*sp && !isspace(*sp))
+ sp++;
+ *sp = '\0'; /* truncate blanks, comments etc. */
+
+ (void) snprintf(altmsgpath, sizeof(altmsgpath), "%s%s", cp,
+ shutmsg);
+
+ if (newfile(altmsgpath))
+ c += shutdown_msgfile(altmsgpath, buf);
+ }
+ }
+ }
+
+ /*
+ ** Need to deal with the access files at the virtual domain directory
+ ** locations specified in the ftpservers file.
+ */
+
+ if ((svrfp = fopen(_PATH_FTPSERVERS, "r")) != NULL) {
+ while (read_servers_line(svrfp, hostaddress, sizeof(hostaddress),
+ configdir, sizeof(configdir)) == 1) {
+ /* get rid of any trailing slash */
+ sp = configdir + (strlen(configdir) - 1);
+ if (*sp == '/')
+ *sp = '\0';
+
+ /*
+ ** check to see that a valid directory value was
+ ** supplied and not something such as "INTERNAL"
+ **
+ ** It is valid to have a string such as "INTERNAL" in the
+ ** ftpservers entry. This is not an error. Silently ignore it.
+ */
+
+ if ((stat(configdir, &finfo) < 0) ||
+ ((finfo.st_mode & S_IFMT) != S_IFDIR))
+ continue;
+
+ (void) snprintf(accesspath, sizeof(accesspath), "%s/ftpaccess",
+ configdir);
+
+ (void) fclose(accessfile);
+
+ if ((accessfile = fopen(accesspath, "r")) == NULL) {
+ if (errno != ENOENT) {
+ fprintf(stderr, "%s: could not open access file %s: %s\n",
+ progname, accesspath, strerror(errno));
+ continue;
+ }
+ }
+
+ /* need to find the root path */
+
+ while (fgets(linebuf, sizeof(linebuf) - 1, accessfile) != NULL) {
+ if ((sp = strstr(linebuf, "root")) != NULL) {
+ if ((cp = strchr(sp, '\n')) != NULL)
+ *cp = '\0'; /* strip newline */
+ sp += 4; /* skip past "root" keyword */
+
+ while (*sp && isspace(*sp)) /* skip whitespace to path */
+ sp++;
+ cp = sp;
+ while (*sp && !isspace(*sp))
+ sp++;
+ *sp = '\0'; /* truncate blanks, comments etc. */
+ (void) strlcpy(root, cp, sizeof(root));
+ break;
+ }
+ }
+ /* need to find the shutdown message file path */
+
+ rewind(accessfile);
+
+ while (fgets(linebuf, sizeof(linebuf) - 1, accessfile) != NULL) {
+ if ((sp = strstr(linebuf, "shutdown")) != NULL) {
+ if ((cp = strchr(sp, '\n')) != NULL)
+ *cp = '\0'; /* strip newline */
+ sp += 8; /* skip past "root" keyword */
+
+ while (*sp && isspace(*sp)) /* skip whitespace to path */
+ sp++;
+ cp = sp;
+ while (*sp && !isspace(*sp))
+ sp++;
+ *sp = '\0'; /* truncate blanks, comments etc. */
+ break;
+ }
+ }
+
+ /*
+ ** check to make sure the admin hasn't specified
+ ** a complete path in the 'shutdown' directive.
+ */
+ if ((sp = strstr(cp, root)) == NULL)
+ (void) snprintf(altmsgpath, sizeof(altmsgpath), "%s%s", root,
+ cp);
+
+ /*
+ ** Check to see if the message file has been created elsewhere.
+ */
+ if (newfile(altmsgpath))
+ c += shutdown_msgfile(altmsgpath, buf);
+ }
+ fclose(svrfp);
+ }
+#endif /* VIRTUAL */
+
+ fclose(accessfile);
+ free(aclbuf);
+ exit(c > 0 ? 1 : 0);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpusers b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpusers
new file mode 100644
index 0000000000..cf50a88824
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpusers
@@ -0,0 +1,19 @@
+# ident "%Z%%M% %I% %E% SMI"
+#
+# List of users denied access to the FTP server, see ftpusers(4).
+#
+root
+daemon
+bin
+sys
+adm
+lp
+uucp
+nuucp
+smmsp
+listen
+gdm
+webservd
+nobody
+noaccess
+nobody4
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/getpwnam.c b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/getpwnam.c
new file mode 100644
index 0000000000..c3a2682dab
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/getpwnam.c
@@ -0,0 +1,158 @@
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Replacement for getpwnam - we need it to handle files other than
+ * /etc/passwd so we can permit different passwd files for each different
+ * host
+ * (c) 1998-2000 by Bernhard Rosenkränzer <bero@redhat.com>
+ * 19980930 Initial version
+ * 20000211 Various fixes
+ */
+
+#include "config.h"
+#include <pwd.h>
+#include <sys/types.h>
+#include <stdio.h>
+#ifdef SHADOW_PASSWORD
+# ifdef HAVE_SHADOW_H
+# include <shadow.h>
+# endif
+#endif
+
+#ifndef HAVE_FGETPWENT /* Some systems (*BSD) don't have fgetpwent... */
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#else
+#include <string.h>
+#endif
+struct passwd *fgetpwent(FILE *stream)
+{
+ char *entry=(char *) malloc(1024);
+ struct passwd *p=(struct passwd *) malloc(sizeof(struct passwd));
+ char *tmp,*tmp2;
+
+ if(!fgets(entry,1024,stream)) {
+ free(entry);
+ free(p);
+ return NULL;
+ }
+ tmp=strdup(entry);
+ if(strchr(tmp,':')) {
+ *strchr(tmp,':')=0;
+ p->pw_name=tmp;
+ } else {
+ free(tmp); free(entry); free(p); return NULL;
+ }
+ tmp2=strchr(entry,':')+1;
+ tmp=strdup(tmp2);
+ if(strchr(tmp,':')) {
+ *strchr(tmp,':')=0;
+ p->pw_passwd=tmp;
+ } else {
+ free(tmp); free(entry); free(p->pw_name); free(p); return NULL;
+ }
+ tmp2=strchr(tmp2,':')+1;
+ tmp=strdup(tmp2);
+ if(strchr(tmp,':')) {
+ *strchr(tmp,':')=0;
+ p->pw_uid=(uid_t) atoi(tmp);
+ } else {
+ free(tmp); free(entry); free(p->pw_passwd); free(p->pw_name); free(p); return NULL;
+ }
+ free(tmp);
+ tmp2=strchr(tmp2,':')+1;
+ tmp=strdup(tmp2);
+ if(strchr(tmp,':')) {
+ *strchr(tmp,':')=0;
+ p->pw_gid=(gid_t) atoi(tmp);
+ } else {
+ free(tmp); free(entry); free(p->pw_passwd); free(p->pw_name); free(p); return NULL;
+ }
+ free(tmp);
+ tmp2=strchr(tmp2,':')+1;
+ tmp=strdup(tmp2);
+ if(strchr(tmp,':')) {
+ *strchr(tmp,':')=0;
+ p->pw_gecos=tmp;
+ } else {
+ free(tmp); free(entry); free(p->pw_passwd); free(p->pw_name); free(p); return NULL;
+ }
+ tmp2=strchr(tmp2,':')+1;
+ tmp=strdup(tmp2);
+ if(strchr(tmp,':')) {
+ *strchr(tmp,':')=0;
+ p->pw_dir=tmp;
+ } else {
+ free(tmp); free(entry); free(p->pw_gecos); free(p->pw_passwd); free(p->pw_name); free(p); return NULL;
+ }
+ tmp2=strchr(tmp2,':')+1;
+ if(strchr(tmp2,':')) {
+ free(entry); free(p->pw_dir); free(p->pw_gecos); free(p->pw_passwd); free(p->pw_name); free(p); return NULL;
+ }
+ while(strlen(tmp2) && isspace(tmp2[strlen(tmp2)-1]))
+ tmp2[strlen(tmp2)-1]=0;
+ p->pw_shell=strdup(tmp2);
+ free(entry);
+ return p;
+}
+#endif
+
+
+struct passwd *bero_getpwnam(const char * name, const char * file)
+{
+ FILE *f;
+ struct passwd *p;
+ struct passwd *r;
+
+ if (!strcmp(file,"/etc/passwd"))
+ return (getpwnam(name));
+ f=fopen(file,"r");
+ if(f==NULL)
+ return NULL;
+ p=NULL;
+ r=NULL;
+ while((r==NULL) && (p=fgetpwent(f)))
+ if(!strcasecmp(p->pw_name,name))
+ r=p;
+ fclose(f);
+ return r;
+}
+
+struct passwd *bero_getpwuid(uid_t uid, const char * file)
+{
+ FILE *f;
+ struct passwd *p;
+ struct passwd *r;
+
+ if (!strcmp(file,"/etc/passwd"))
+ return getpwuid(uid);
+ f=fopen(file,"r");
+ if(f==NULL)
+ return NULL;
+ p=NULL;
+ r=NULL;
+ while((r==NULL) && (p=fgetpwent(f)))
+ if(p->pw_uid==uid)
+ r=p;
+ fclose(f);
+ return r;
+}
+
+#ifdef SHADOW_PASSWORD
+struct spwd *bero_getspnam(const char * name, const char * file)
+{
+ FILE *f;
+ struct spwd *s;
+ struct spwd *r;
+ f=fopen(file,"r");
+ if(f==NULL)
+ return NULL;
+ s=NULL;
+ r=NULL;
+ while((r==NULL) && (s=fgetspent(f)))
+ if(!strcasecmp(s->sp_namp,name))
+ r=s;
+ fclose(f);
+ return r;
+}
+#endif
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/getpwnam.h b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/getpwnam.h
new file mode 100644
index 0000000000..e3b29d3546
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/getpwnam.h
@@ -0,0 +1,25 @@
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Replacement for getpwnam - we need it to handle files other than
+ * /etc/passwd so we can permit different passwd files for each different
+ * host
+ * (c) 1998-2000 by Bernhard Rosenkränzer <bero@redhat.com>
+ * 19980930 Initial version
+ * 20000211 Various fixes
+ */
+
+#include <pwd.h>
+#include <sys/types.h>
+#include <stdio.h>
+#ifdef SHADOW_PASSWORD
+# ifdef HAVE_SHADOW_H
+# include <shadow.h>
+# endif
+#endif
+
+struct passwd *bero_getpwnam(const char * name, const char * file);
+struct passwd *bero_getpwuid(uid_t uid, const char * file);
+#ifdef SHADOW_PASSWORD
+struct spwd *bero_getspnam(const char * name, const char * file);
+#endif
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/glob.c b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/glob.c
new file mode 100644
index 0000000000..c27dec277d
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/glob.c
@@ -0,0 +1,665 @@
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/****************************************************************************
+ Copyright (c) 1999,2000,2001 WU-FTPD Development Group.
+ All rights reserved.
+
+ Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994
+ The Regents of the University of California.
+ Portions Copyright (c) 1993, 1994 Washington University in Saint Louis.
+ Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc.
+ Portions Copyright (c) 1989 Massachusetts Institute of Technology.
+ Portions Copyright (c) 1998 Sendmail, Inc.
+ Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P. Allman.
+ Portions Copyright (c) 1997 by Stan Barber.
+ Portions Copyright (c) 1997 by Kent Landfield.
+ Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ Free Software Foundation, Inc.
+
+ Use and distribution of this software and its source code are governed
+ by the terms and conditions of the WU-FTPD Software License ("LICENSE").
+
+ If you did not receive a copy of the license, it may be obtained online
+ at http://www.wu-ftpd.org/license.html.
+
+ $Id: glob.c,v 1.14.2.2 2001/11/29 17:01:38 wuftpd Exp $
+
+****************************************************************************/
+/*
+ * C-shell glob for random programs.
+ */
+
+#include "config.h"
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#ifdef HAVE_DIRENT_H
+#include <dirent.h>
+#else
+#include <sys/dir.h>
+#endif
+
+#include <pwd.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "proto.h"
+
+#define QUOTE 0200
+#define TRIM 0177
+#define eq(a,b) (strcmp(a, b)==0)
+#define GAVSIZ (1024 * 8)
+#define isdir(d) ((d.st_mode & S_IFMT) == S_IFDIR)
+
+static char **gargv; /* Pointer to the (stack) arglist */
+static char **agargv;
+static size_t agargv_size;
+static int gargc; /* Number args in gargv */
+static size_t gnleft;
+static short gflag;
+static int tglob(register char);
+
+/* Prototypes */
+
+static char *strend(register char *);
+static void addpath(char);
+static void ginit(char **);
+static void collect(register char *);
+static void acollect(register char *);
+static void sort(void);
+static void expand(char *);
+static void matchdir(char *);
+static int execbrc(char *, char *);
+static int match(char *, char *);
+static int amatch(char *, char *);
+static void Gcat(register char *, register char *);
+static void rscan(register char **, int (*f) (register char));
+static int tglob(register char c);
+static int gethdir(char *);
+
+int letter(register char);
+int digit(register char);
+int any(register int, register char *);
+int blklen(register char **);
+char **blkcpy(char **, register char **);
+
+char *globerr;
+char *home;
+extern int errno;
+
+static int globcnt;
+
+char *globchars = "`{[*?";
+
+static char *gpath, *gpathp, *lastgpathp;
+static int globbed;
+static char *entp;
+static char **sortbas;
+
+#ifdef OTHER_PASSWD
+#include "getpwnam.h"
+extern char _path_passwd[];
+#endif
+
+char **ftpglob(register char *v)
+{
+ char agpath[BUFSIZ];
+ char *vv[2];
+
+ if (agargv == NULL) {
+ agargv = (char **) malloc(GAVSIZ * sizeof (char *));
+ if (agargv == NULL) {
+ fatal("Out of memory");
+ }
+ agargv_size = GAVSIZ;
+ }
+ fixpath(v);
+ if (v[0] == '\0')
+ v = "*";
+ else if ((strlen(v) > 1) && (v[strlen(v) - 1] == '/'))
+ v[strlen(v) - 1] = '\0';
+
+ vv[0] = v;
+ vv[1] = NULL;
+ globerr = NULL;
+ gflag = 0;
+ rscan(vv, tglob);
+ if (gflag == 0) {
+ vv[0] = strspl(v, "");
+ return (copyblk(vv));
+ }
+
+ globerr = NULL;
+ gpath = agpath;
+ gpathp = gpath;
+ *gpathp = 0;
+ lastgpathp = &gpath[sizeof agpath - 2];
+ ginit(agargv);
+ globcnt = 0;
+ collect(v);
+ if (globcnt == 0 && (gflag & 1)) {
+ blkfree(gargv), gargv = 0;
+ return (0);
+ }
+ else
+ return (gargv = copyblk(gargv));
+}
+
+static void ginit(char **agargv)
+{
+
+ agargv[0] = 0;
+ gargv = agargv;
+ sortbas = agargv;
+ gargc = 0;
+ gnleft = NCARGS - 4;
+}
+
+static void collect(register char *as)
+{
+ if (eq(as, "{") || eq(as, "{}")) {
+ Gcat(as, "");
+ sort();
+ }
+ else
+ acollect(as);
+}
+
+static void acollect(register char *as)
+{
+ register int ogargc = gargc;
+
+ gpathp = gpath;
+ *gpathp = 0;
+ globbed = 0;
+ expand(as);
+ if (gargc != ogargc)
+ sort();
+}
+
+static int
+argcmp(const void *p1, const void *p2)
+{
+ char *s1 = *(char **) p1;
+ char *s2 = *(char **) p2;
+
+ return (strcmp(s1, s2));
+}
+
+static void sort(void)
+{
+ char **Gvp = &gargv[gargc];
+
+ if (!globerr)
+ qsort(sortbas, Gvp - sortbas, sizeof (*sortbas), argcmp);
+ sortbas = Gvp;
+}
+
+static void expand(char *as)
+{
+ register char *cs;
+ register char *sgpathp, *oldcs;
+ struct stat stb;
+
+ if (globerr)
+ return;
+ sgpathp = gpathp;
+ cs = as;
+ if (*cs == '~' && gpathp == gpath) {
+ addpath('~');
+ for (cs++; letter(*cs) || digit(*cs) || *cs == '-';)
+ addpath(*cs++);
+ if (!*cs || *cs == '/') {
+ if (gpathp != gpath + 1) {
+ *gpathp = 0;
+ if (gethdir(gpath + 1))
+ globerr = "Unknown user name after ~";
+ /* memmove used as strings overlap */
+ (void) memmove(gpath, gpath + 1, strlen(gpath + 1) + 1);
+ }
+ else
+ (void) strlcpy(gpath, home, BUFSIZ);
+ gpathp = strend(gpath);
+ }
+ }
+ while (!any(*cs, globchars)) {
+ if (*cs == 0) {
+ if (!globbed)
+ Gcat(gpath, "");
+ else if (stat(gpath, &stb) >= 0) {
+ Gcat(gpath, "");
+ globcnt++;
+ }
+ goto endit;
+ }
+ addpath(*cs++);
+ }
+ oldcs = cs;
+ while (cs > as && *cs != '/')
+ cs--, gpathp--;
+ if (*cs == '/')
+ cs++, gpathp++;
+ *gpathp = 0;
+ if (*oldcs == '{') {
+ (void) execbrc(cs, ((char *) 0));
+ return;
+ }
+ matchdir(cs);
+ endit:
+ gpathp = sgpathp;
+ *gpathp = 0;
+}
+
+static void matchdir(char *pattern)
+{
+ struct stat stb;
+
+#ifdef HAVE_DIRENT_H
+ register struct dirent *dp;
+#else
+ register struct direct *dp;
+#endif
+
+ DIR *dirp;
+
+ dirp = opendir(*gpath == '\0' ? "." : gpath);
+ if (dirp == NULL) {
+ if (globbed)
+ return;
+ goto patherr2;
+ }
+#ifdef HAVE_DIRFD
+ if (fstat(dirfd(dirp), &stb) < 0)
+#else /* HAVE_DIRFD */
+ if (fstat(dirp->dd_fd, &stb) < 0)
+#endif /* HAVE_DIRFD */
+ goto patherr1;
+ if (!isdir(stb)) {
+ errno = ENOTDIR;
+ goto patherr1;
+ }
+ while (!globerr && ((dp = readdir(dirp)) != NULL)) {
+ if (dp->d_ino == 0)
+ continue;
+ if (match(dp->d_name, pattern)) {
+ Gcat(gpath, dp->d_name);
+ globcnt++;
+ }
+ }
+ closedir(dirp);
+ return;
+
+ patherr1:
+ closedir(dirp);
+ patherr2:
+ globerr = "Bad directory components";
+}
+
+static int execbrc(char *p, char *s)
+{
+ char restbuf[BUFSIZ + 2];
+ char *restbufend = &restbuf[sizeof(restbuf)];
+ register char *pe, *pm, *pl;
+ int brclev = 0;
+ char *lm, savec, *sgpathp;
+
+ for (lm = restbuf; *p != '{'; *lm++ = *p++) {
+ if (lm >= restbufend)
+ return (0);
+ }
+ for (pe = ++p; *pe; pe++) {
+ switch (*pe) {
+
+ case '{':
+ brclev++;
+ continue;
+
+ case '}':
+ if (brclev == 0)
+ goto pend;
+ brclev--;
+ continue;
+
+ case '[':
+ for (pe++; *pe && *pe != ']'; pe++)
+ continue;
+ if (!*pe) {
+ globerr = "Missing ]";
+ return (0);
+ }
+ continue;
+ }
+ }
+ pend:
+ if (brclev || !*pe) {
+ globerr = "Missing }";
+ return (0);
+ }
+ for (pl = pm = p; pm <= pe; pm++) {
+ switch (*pm & (QUOTE | TRIM)) {
+
+ case '{':
+ brclev++;
+ continue;
+
+ case '}':
+ if (brclev) {
+ brclev--;
+ continue;
+ }
+ goto doit;
+
+ case ',' | QUOTE:
+ case ',':
+ if (brclev)
+ continue;
+ doit:
+ savec = *pm;
+ *pm = 0;
+ if (lm + strlen(pl) + strlen(pe + 1) >= restbufend)
+ return (0);
+ (void) strlcpy(lm, pl, restbufend - lm);
+ (void) strlcat(restbuf, pe + 1, sizeof(restbuf));
+ *pm = savec;
+ if (s == 0) {
+ sgpathp = gpathp;
+ expand(restbuf);
+ gpathp = sgpathp;
+ *gpathp = 0;
+ }
+ else if (amatch(s, restbuf))
+ return (1);
+ sort();
+ pl = pm + 1;
+ continue;
+
+ case '[':
+ for (pm++; *pm && *pm != ']'; pm++)
+ continue;
+ if (!*pm) {
+ globerr = "Missing ]";
+ return (0);
+ }
+ continue;
+ }
+ }
+ return (0);
+}
+
+static int match(char *s, char *p)
+{
+ register int c;
+ register char *sentp;
+ char sglobbed = globbed;
+
+ if (*s == '.' && *p != '.')
+ return (0);
+ sentp = entp;
+ entp = s;
+ c = amatch(s, p);
+ entp = sentp;
+ globbed = sglobbed;
+ return (c);
+}
+
+static int amatch(char *s, char *p)
+{
+ register int scc;
+ int ok, lc;
+ char *sgpathp;
+ struct stat stb;
+ int c, cc;
+
+ globbed = 1;
+ for (;;) {
+ scc = *s++ & TRIM;
+ switch (c = *p++) {
+
+ case '{':
+ return (execbrc(p - 1, s - 1));
+
+ case '[':
+ ok = 0;
+ lc = 077777;
+ while ((cc = *p++)) {
+ if (cc == ']') {
+ if (ok)
+ break;
+ return (0);
+ }
+ if (cc == '-') {
+ if (lc <= scc && scc <= *p++)
+ ok++;
+ }
+ else if (scc == (lc = cc))
+ ok++;
+ }
+ if (cc == 0) {
+ globerr = "Missing ]";
+ return (0);
+ }
+ continue;
+
+ case '*':
+ if (!*p)
+ return (1);
+ if (*p == '/') {
+ p++;
+ goto slash;
+ } else if (*p == '*') {
+ s--;
+ continue;
+ }
+ s--;
+ do {
+ if (amatch(s, p))
+ return (1);
+ } while (*s++);
+ return (0);
+
+ case 0:
+ return (scc == 0);
+
+ default:
+ if (c != scc)
+ return (0);
+ continue;
+
+ case '?':
+ if (scc == 0)
+ return (0);
+ continue;
+
+ case '/':
+ if (scc)
+ return (0);
+ slash:
+ s = entp;
+ sgpathp = gpathp;
+ while (*s)
+ addpath(*s++);
+ addpath('/');
+ if (stat(gpath, &stb) == 0 && isdir(stb))
+ if (*p == 0) {
+ Gcat(gpath, "");
+ globcnt++;
+ }
+ else
+ expand(p);
+ gpathp = sgpathp;
+ *gpathp = 0;
+ return (0);
+ }
+ }
+}
+
+static void Gcat(register char *s1, register char *s2)
+{
+ register size_t len = strlen(s1) + strlen(s2) + 1;
+
+ if (globerr)
+ return;
+
+ if ((len + sizeof (char *)) >= gnleft) {
+ globerr = "Arguments too long";
+ return;
+ }
+ if (len > MAXPATHLEN) {
+ globerr = "Pathname too long";
+ return;
+ }
+ if (gargc >= agargv_size - 1) {
+ char **tmp;
+
+ tmp = (char **)realloc(agargv,
+ (agargv_size + GAVSIZ) * sizeof (char *));
+ if (tmp == NULL) {
+ fatal("Out of memory");
+ } else {
+ agargv = tmp;
+ agargv_size += GAVSIZ;
+ }
+ gargv = agargv;
+ sortbas = agargv;
+ }
+ gargc++;
+ gnleft -= len + sizeof (char *);
+ gargv[gargc] = 0;
+ gargv[gargc - 1] = strspl(s1, s2);
+}
+
+static void addpath(char c)
+{
+
+ if (gpathp >= lastgpathp)
+ globerr = "Pathname too long";
+ else {
+ *gpathp++ = c;
+ *gpathp = 0;
+ }
+}
+
+static void rscan(register char **t, int (*f) (register char))
+{
+ register char *p, c;
+
+ while ((p = *t++)) {
+ if (*p == '~')
+ gflag |= 2;
+ else if (eq(p, "{") || eq(p, "{}"))
+ continue;
+ while ((c = *p++))
+ (*f) (c);
+ }
+}
+static int tglob(register char c)
+{
+ if (any(c, globchars))
+ gflag |= c == '{' ? 2 : 1;
+ return (c);
+}
+
+int letter(register char c)
+{
+ return (((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z'))
+ || (c == '_'));
+}
+
+int digit(register char c)
+{
+ return (c >= '0' && c <= '9');
+}
+
+int any(register int c, register char *s)
+{
+ while (*s)
+ if (*s++ == c)
+ return (1);
+ return (0);
+}
+
+int blklen(register char **av)
+{
+ register int i = 0;
+
+ while (*av++)
+ i++;
+ return (i);
+}
+
+char **blkcpy(char **oav, register char **bv)
+{
+ register char **av = oav;
+
+ while ((*av++ = *bv++))
+ continue;
+ return (oav);
+}
+
+void blkfree(char **av0)
+{
+ register char **av = av0;
+
+ if (av) {
+ while (*av)
+ free(*av++);
+ }
+}
+
+char *strspl(register char *cp, register char *dp)
+{
+ int bufsize = strlen(cp) + strlen(dp) + 1;
+ char *ep = malloc(bufsize);
+
+ if (ep == NULL)
+ fatal("Out of memory");
+ (void) strlcpy(ep, cp, bufsize);
+ (void) strlcat(ep, dp, bufsize);
+ return (ep);
+}
+
+char **copyblk(register char **v)
+{
+ register char **nv = (char **) malloc((unsigned) ((blklen(v) + 1) *
+ sizeof(char **)));
+ if (nv == (char **) 0)
+ fatal("Out of memory");
+
+ return (blkcpy(nv, v));
+}
+
+static char *strend(register char *cp)
+{
+ while (*cp)
+ cp++;
+ return (cp);
+}
+/*
+ * Extract a home directory from the password file
+ * The argument points to a buffer where the name of the
+ * user whose home directory is sought is currently.
+ * We write the home directory of the user back there.
+ */
+static int gethdir(char *home)
+{
+#ifdef OTHER_PASSWD
+ register struct passwd *pp = bero_getpwnam(home, _path_passwd);
+#else
+ register struct passwd *pp = getpwnam(home);
+#endif
+ register char *root = NULL;
+ if (!pp || home + strlen(pp->pw_dir) >= lastgpathp)
+ return (1);
+ root = strstr(pp->pw_dir, "/./");
+ (void) strlcpy(home, root ? (root + 2) : pp->pw_dir, lastgpathp - home);
+
+ return (0);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/gssutil.c b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/gssutil.c
new file mode 100644
index 0000000000..0ed5b16a21
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/gssutil.c
@@ -0,0 +1,1299 @@
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * gssutil.c
+ *
+ * Utility routines for providing security related services to
+ * the FTP server. This code uses the GSSAPI (RFC 2743, 2744)
+ * to provide a generic security layer to the application. The
+ * security mechanism providing the actual security functions
+ * is abstracted from the application itself. In the case of the FTP
+ * server, the security mechanism is based on what the client chooses
+ * to use when it makes the secure connection. If the client's
+ * choice of GSS mechanism is not supported by the FTP server, the
+ * connection may be rejected or fall back to standard Unix/PAM
+ * authentication.
+ *
+ * This code is primarily intended to work with clients who choose
+ * the Kerberos V5 GSSAPI mechanism as their security service.
+ */
+
+#include "config.h"
+
+#if defined(USE_GSS)
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <pwd.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+
+#include <errno.h>
+#include <sys/param.h>
+#include <netdb.h>
+#ifdef HAVE_SYS_SYSLOG_H
+#include <sys/syslog.h>
+#endif
+
+/* CSTYLED */
+#if defined(HAVE_SYSLOG_H) || (!defined(AUTOCONF) && !defined(HAVE_SYS_SYSLOG_H))
+#include <syslog.h>
+#endif
+
+#ifdef HAVE_SYSINFO
+#include <sys/systeminfo.h>
+#endif
+
+#include <arpa/ftp.h>
+
+#include "gssutil.h"
+#include "proto.h"
+
+static char *gss_services[] = { "ftp", "host", 0 };
+
+gss_info_t gss_info = {
+ /* context */ GSS_C_NO_CONTEXT,
+ /* mechoid */ GSS_C_NULL_OID,
+ /* client */ NULL,
+ /* display_name */ NULL,
+ /* data_prot */ PROT_C,
+ /* ctrl_prot */ PROT_C,
+ /* authstate */ GSS_AUTH_NONE,
+ /* want_creds */ 0,
+ /* have_creds */ 0,
+ /* must_auth */ 0
+};
+
+
+extern char *cur_auth_type;
+extern struct SOCKSTORAGE his_addr;
+extern struct SOCKSTORAGE ctrl_addr;
+extern int debug;
+
+static char *radixN =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static char pad = '=';
+
+#define DEF_GSSBUF_SIZE 2028
+#define DECODELEN(l) (((3 * (l)) / 4) + 4)
+#define ENCODELEN(l) (((4 * (l)) / 3) + 4)
+
+typedef struct {
+ char *buf;
+ size_t alloc_len;
+ size_t len; /* max length of buffer */
+ size_t idx; /* offset to beginning of read/write data */
+ size_t clen; /* length of the remaining, decrypted data from client */
+}bufrec;
+
+static bufrec obr = {NULL, 0, 0, 0, 0};
+static bufrec ibr = {NULL, 0, 0, 0, 0};
+
+static int looping_write(int fd, const char *buf, size_t len);
+static int looping_read(int fd, char *buf, size_t len);
+static int radix_encode(unsigned char *inbuf, unsigned char *outbuf,
+ size_t len, int *outlen, int decode);
+static char *radix_error(int e);
+static void reply_gss_error(int code, OM_uint32 maj_stat,
+ OM_uint32 min_stat, gss_OID mechoid, char *s);
+static void cleanup_bufrec(bufrec *brec);
+static int alloc_bufrec(bufrec *brec, size_t newsz);
+static int sec_putbuf(int fd, unsigned char *buf, int len);
+static int sec_getbytes(int fd, char *buf, int nbytes);
+
+/*
+ * Provide a routine so that ftpd can know the max amount to read
+ */
+size_t
+gss_getinbufsz(void) {
+ return (ibr.len);
+}
+
+/*
+ * gss_adjust_buflen
+ *
+ * Called when the protection method changes so we can adjust the
+ * "useable" length of our output buffer accordingly.
+ */
+void
+gss_adjust_buflen()
+{
+ OM_uint32 maj_stat, min_stat, mlen;
+
+ /*
+ * If we switched to CLEAR protection, we can use the entire buffer
+ */
+ if (gss_info.data_prot == PROT_C) {
+ obr.len = obr.alloc_len;
+ return;
+ }
+
+ /*
+ * Otherwise, determine the maximum size that will allow for
+ * the GSSAPI overhead to fit into the buffer size.
+ */
+ maj_stat = gss_wrap_size_limit(&min_stat, gss_info.context,
+ (gss_info.data_prot == PROT_P),
+ GSS_C_QOP_DEFAULT,
+ (OM_uint32)obr.alloc_len, &mlen);
+ if (maj_stat != GSS_S_COMPLETE) {
+ reply_gss_error(535, maj_stat, min_stat,
+ gss_info.mechoid,
+ "GSSAPI fudge determination");
+ return;
+ }
+ obr.len = mlen;
+
+ if (debug)
+ syslog(LOG_DEBUG, "GSSAPI alloc_len = %d len = %d",
+ obr.alloc_len, obr.len);
+}
+
+static int
+looping_write(int fd, const char *buf, size_t len)
+{
+ int cc;
+ register size_t wrlen = len;
+
+ do {
+ cc = write(fd, buf, wrlen);
+ if (cc < 0) {
+ if (errno == EINTR)
+ continue;
+ return (cc);
+ } else {
+ buf += cc;
+ wrlen -= cc;
+ }
+ } while (wrlen > 0);
+
+ return (len);
+}
+
+static int
+looping_read(int fd, char *buf, size_t len)
+{
+ int cc;
+ size_t len2 = 0;
+
+ do {
+ cc = read(fd, buf, len);
+ if (cc < 0) {
+ if (errno == EINTR)
+ continue;
+ return (cc); /* errno is already set */
+ } else if (cc == 0) {
+ return (len2);
+ } else {
+ buf += cc;
+ len2 += cc;
+ len -= cc;
+ }
+ } while (len > 0);
+ return (len2);
+}
+
+static int
+radix_encode(unsigned char *inbuf, unsigned char *outbuf,
+ size_t buflen, int *outlen, int decode)
+{
+ register int i, j, D;
+ char *p;
+ unsigned char c;
+
+ if (decode) {
+ for (i = 0, j = 0; (j < buflen) &&
+ inbuf[i] && inbuf[i] != pad; i++) {
+ if ((p = strchr(radixN, inbuf[i])) == NULL)
+ return (1);
+ D = p - radixN;
+ switch (i&3) {
+ case 0:
+ outbuf[j] = D <<2;
+ break;
+ case 1:
+ outbuf[j++] |= D >>4;
+ outbuf[j] = (D&15)<<4;
+ break;
+ case 2:
+ outbuf[j++] |= D >>2;
+ outbuf[j] = (D&3)<<6;
+ break;
+ case 3:
+ outbuf[j++] |= D;
+ }
+ }
+ if (j == buflen && (inbuf[i] && inbuf[i] != pad)) {
+ /* Oops, we ran out of space in the output buffer */
+ return (4);
+ }
+ switch (i&3) {
+ case 1:
+ return (3);
+ case 2: if (D&15)
+ return (3);
+ if (strcmp((char *)&inbuf[i], "=="))
+ return (2);
+ break;
+ case 3: if (D&3)
+ return (3);
+ if (strcmp((char *)&inbuf[i], "="))
+ return (2);
+ }
+ *outlen = j;
+ } else {
+ for (i = 0, j = 0; i < *outlen && j < buflen; i++)
+ switch (i%3) {
+ case 0:
+ outbuf[j++] = radixN[inbuf[i]>>2];
+ c = (inbuf[i]&3)<<4;
+ break;
+ case 1:
+ outbuf[j++] = radixN[c|inbuf[i]>>4];
+ c = (inbuf[i]&15)<<2;
+ break;
+ case 2:
+ outbuf[j++] = radixN[c|inbuf[i]>>6];
+ outbuf[j++] = radixN[inbuf[i]&63];
+ c = 0;
+ }
+ if (j == buflen && i < *outlen) {
+ /* output buffer is not big enough */
+ return (4);
+ }
+
+ if (i%3) outbuf[j++] = radixN[c];
+ switch (i%3) {
+ case 1: outbuf[j++] = pad;
+ case 2: outbuf[j++] = pad;
+ }
+ outbuf[*outlen = j] = '\0';
+ }
+ return (0);
+}
+
+static char *
+radix_error(int e)
+{
+ switch (e) {
+ case 0: return ("Success");
+ case 1: return ("Bad character in encoding");
+ case 2: return ("Encoding not properly padded");
+ case 3: return ("Decoded # of bits not a multiple of 8");
+ case 4: return ("Buffer size error");
+ default: return ("Unknown error");
+ }
+}
+
+static void
+reply_gss_error(int code, OM_uint32 maj_stat,
+ OM_uint32 min_stat, gss_OID mechoid, char *s)
+{
+ /* a lot of work just to report the error */
+ OM_uint32 gmaj_stat, gmin_stat;
+ gss_buffer_desc msg;
+ int msg_ctx;
+ msg_ctx = 0;
+
+ gmaj_stat = gss_display_status(&gmin_stat, maj_stat,
+ GSS_C_GSS_CODE,
+ mechoid,
+ (OM_uint32 *)&msg_ctx, &msg);
+ if (gmaj_stat == GSS_S_COMPLETE) {
+ lreply(code, "GSSAPI error major: %s",
+ (char *)msg.value);
+ (void) gss_release_buffer(&gmin_stat, &msg);
+ }
+
+ gmaj_stat = gss_display_status(&gmin_stat, min_stat,
+ GSS_C_MECH_CODE,
+ mechoid,
+ (OM_uint32 *)&msg_ctx, &msg);
+ if (gmaj_stat == GSS_S_COMPLETE) {
+ lreply(code, "GSSAPI error minor: %s", (char *)msg.value);
+ (void) gss_release_buffer(&gmin_stat, &msg);
+ }
+
+ reply(code, "GSSAPI error: %s", s);
+}
+
+
+static void
+log_status(char *msg,
+ OM_uint32 status_code,
+ int status_type)
+{
+ OM_uint32 message_context;
+ gss_buffer_desc status_string;
+ OM_uint32 maj_status;
+ OM_uint32 min_status;
+
+ /* From RFC2744: */
+ message_context = 0;
+
+ do {
+ maj_status = gss_display_status(
+ &min_status,
+ status_code,
+ status_type,
+ GSS_C_NO_OID,
+ &message_context,
+ &status_string);
+
+ if (maj_status == GSS_S_COMPLETE) {
+ syslog(LOG_ERR,
+ "GSSAPI Error %s: %.*s\n",
+ msg ? msg : "<null>",
+ (int)status_string.length,
+ (char *)status_string.value);
+
+ (void) gss_release_buffer(&min_status,
+ &status_string);
+ } else {
+ syslog(LOG_ERR,
+ "log_status internal error: gss_display_status failed");
+ return;
+ }
+ } while (message_context != 0);
+
+}
+
+static void
+log_gss_error(char *msg,
+ OM_uint32 maj_stat,
+ OM_uint32 min_stat)
+{
+ log_status(msg, maj_stat, GSS_C_GSS_CODE);
+ log_status(msg, min_stat, GSS_C_MECH_CODE);
+}
+
+
+static void
+log_gss_info(int priority,
+ char *luser,
+ char *remprinc,
+ gss_OID mechoid,
+ char *s)
+{
+ const char *mechStr = __gss_oid_to_mech(mechoid);
+
+ syslog(priority,
+ "%s: local user=`%s', remote princ=`%s', mech=%s",
+ s ? s : "<null>",
+ luser ? luser : "<null>",
+ remprinc ? remprinc : "<unknown>",
+ mechStr ? mechStr : "<unknown>");
+}
+
+/*
+ * gss_user
+ *
+ * Handle USER command after AUTH GSSAPI
+ *
+ * Check if the remote user can login to the local system w/out a passwd.
+ * Use the Solaris (private) interface (__gss_userok) if possible, else do
+ * a basic GSS-API compare.
+ *
+ * return 0 == BAD
+ * 1 == OK
+ */
+int
+gss_user(struct passwd *user_pw)
+{
+ int retval = 0;
+ OM_uint32 status, minor;
+
+#ifdef SOLARIS_GSS_USEROK
+
+ int user_ok = 0;
+
+ if (debug)
+ log_gss_info(LOG_DEBUG,
+ user_pw->pw_name, gss_info.display_name,
+ gss_info.mechoid,
+ "gss_user: start (gss_userok)");
+
+ /* gss_auth_rules(5) */
+ status = __gss_userok(&minor, gss_info.client,
+ user_pw->pw_name, &user_ok);
+ if (status == GSS_S_COMPLETE) {
+ if (user_ok) {
+ retval = 1; /* remote user is a-ok */
+ }
+ }
+
+#else /* SOLARIS_GSS_USEROK */
+
+ gss_name_t imported_name;
+ gss_name_t canon_name;
+ gss_buffer_desc gss_user;
+ OM_uint32 tmpMinor;
+ int match = 0;
+
+ if (debug)
+ log_gss_info(LOG_DEBUG,
+ user_pw->pw_name, gss_info.display_name,
+ gss_info.mechoid, "gss_user: start");
+
+ gss_user.value = user_pw->pw_name;
+ gss_user.length = strlen(gss_user.value);
+
+ status = gss_import_name(&minor,
+ &gss_user,
+ GSS_C_NT_USER_NAME,
+ &imported_name);
+ if (status != GSS_S_COMPLETE) {
+ goto out;
+ }
+
+ status = gss_canonicalize_name(&minor,
+ imported_name,
+ gss_info.mechoid,
+ &canon_name);
+ if (status != GSS_S_COMPLETE) {
+ (void) gss_release_name(&tmpMinor, &imported_name);
+ goto out;
+ }
+
+ status = gss_compare_name(&minor,
+ canon_name,
+ gss_info.client,
+ &match);
+ (void) gss_release_name(&tmpMinor, &canon_name);
+ (void) gss_release_name(&tmpMinor, &imported_name);
+ if (status == GSS_S_COMPLETE) {
+ if (match) {
+ retval = 1; /* remote user is a-ok */
+ }
+ }
+
+out:
+
+#endif /* SOLARIS_GSS_USEROK */
+
+ if (status != GSS_S_COMPLETE) {
+ log_gss_info(LOG_ERR, user_pw->pw_name,
+ gss_info.display_name, gss_info.mechoid,
+ "gss_user failed");
+ log_gss_error("gss_user failed", status, minor);
+ }
+
+ if (debug)
+ syslog(LOG_DEBUG, "gss_user: end: retval=%d", retval);
+
+ return (retval);
+}
+
+
+/*
+ * gss_adat
+ *
+ * Handle ADAT(Authentication Data) command data.
+ */
+int
+gss_adat(char *adatstr)
+{
+ int kerror, length;
+ int replied = 0;
+ int ret_flags;
+ gss_buffer_desc tok, out_tok;
+ gss_cred_id_t deleg_creds = NULL;
+ OM_uint32 accept_maj, accept_min;
+ OM_uint32 stat_maj, stat_min;
+ uchar_t *gout_buf;
+ size_t outlen;
+
+ length = strlen(adatstr);
+ outlen = DECODELEN(length);
+
+ gout_buf = (uchar_t *)malloc(outlen);
+ if (gout_buf == NULL) {
+ reply(501, "Couldn't decode ADAT, not enough memory");
+ syslog(LOG_ERR, "Couldn't decode ADAT, not enough memory");
+ return (0);
+ }
+
+ if ((kerror = radix_encode((unsigned char *)adatstr,
+ (unsigned char *)gout_buf,
+ outlen, &length, 1))) {
+ reply(501, "Couldn't decode ADAT(%s)",
+ radix_error(kerror));
+ syslog(LOG_ERR, "Couldn't decode ADAT(%s)",
+ radix_error(kerror));
+ return (0);
+ }
+ tok.value = gout_buf;
+ tok.length = length;
+
+ gss_info.context = GSS_C_NO_CONTEXT;
+
+ /*
+ * Call accept_sec_context w/GSS_C_NO_CREDENTIAL to request
+ * default cred and to not limit the service name to one name
+ * but rather accept what the clnt requests if service
+ * princ/keys are available.
+ */
+ if (debug)
+ syslog(LOG_DEBUG,
+ "gss_adat: accept_sec_context will try default cred");
+
+ out_tok.value = NULL;
+ out_tok.length = 0;
+
+ accept_maj = gss_accept_sec_context(&accept_min,
+ &gss_info.context,
+ GSS_C_NO_CREDENTIAL,
+ &tok, /* ADAT data */
+ GSS_C_NO_CHANNEL_BINDINGS,
+ &gss_info.client,
+ &gss_info.mechoid,
+ &out_tok, /* output_token */
+ (unsigned int *)&ret_flags,
+ NULL, /* ignore time_rec */
+ NULL); /* delegated creds */
+
+
+ if (debug) {
+ if (accept_maj == GSS_S_COMPLETE)
+ syslog(LOG_DEBUG,
+ "gss_adat: accept_maj = GSS_S_COMPLETE");
+ else if (accept_maj == GSS_S_CONTINUE_NEEDED)
+ syslog(LOG_DEBUG,
+ "gss_adat: accept_maj = GSS_S_CONTINUE_NEEDED");
+ }
+ free(gout_buf);
+
+ if (accept_maj != GSS_S_COMPLETE &&
+ accept_maj != GSS_S_CONTINUE_NEEDED) {
+ reply_gss_error(535, accept_maj, accept_min,
+ GSS_C_NO_OID, "accepting context");
+ syslog(LOG_ERR, "failed accepting context");
+ if ((ret_flags & GSS_C_DELEG_FLAG) &&
+ deleg_creds != NULL)
+ (void) gss_release_cred(&stat_min,
+ &deleg_creds);
+
+ (void) gss_release_buffer(&stat_min, &out_tok);
+ return (0);
+ }
+
+ if (debug)
+ syslog(LOG_DEBUG, "gss_adat: out_tok.length=%d",
+ out_tok.length);
+ if (out_tok.length) {
+ size_t buflen = ENCODELEN(out_tok.length);
+ uchar_t *gbuf = (uchar_t *)malloc(buflen);
+ if (gbuf == NULL) {
+ reply(535, "Couldn't encode ADAT reply, "
+ "not enough memory.");
+ syslog(LOG_ERR, "Couldn't encode ADAT reply, "
+ "not enough memory.");
+ (void) gss_release_buffer(&stat_min, &out_tok);
+ return (0);
+ }
+ if ((kerror = radix_encode(out_tok.value,
+ (unsigned char *)gbuf,
+ buflen, (int *)&out_tok.length,
+ 0))) {
+ reply(535, "Couldn't encode ADAT reply(%s)",
+ radix_error(kerror));
+ syslog(LOG_ERR, "couldn't encode ADAT reply");
+ if ((ret_flags & GSS_C_DELEG_FLAG) &&
+ deleg_creds != NULL)
+ (void) gss_release_cred(&stat_min,
+ &deleg_creds);
+
+ (void) gss_release_buffer(&stat_min, &out_tok);
+ free(gbuf);
+ return (0);
+ }
+
+ if (accept_maj == GSS_S_COMPLETE) {
+ reply(235, "ADAT=%s", gbuf);
+ replied = 1;
+ } else {
+ /*
+ * If the server accepts the security data, and
+ * requires additional data, it should respond
+ * with reply code 335.
+ */
+ reply(335, "ADAT=%s", gbuf);
+ }
+ free(gbuf);
+ (void) gss_release_buffer(&stat_min, &out_tok);
+ }
+ if (accept_maj == GSS_S_COMPLETE) {
+ gss_buffer_desc namebuf;
+ gss_OID out_oid;
+
+ /* GSSAPI authentication succeeded */
+ gss_info.authstate = GSS_ADAT_DONE;
+ (void) alloc_bufrec(&obr, DEF_GSSBUF_SIZE);
+ (void) alloc_bufrec(&ibr, DEF_GSSBUF_SIZE);
+ /*
+ * RFC 2228 - "..., once a security data exchange completes
+ * successfully, if the security mechanism supports
+ * integrity, then integrity(via the MIC or ENC command,
+ * and 631 or 632 reply) must be used, ..."
+ */
+ gss_info.ctrl_prot = PROT_S;
+
+ stat_maj = gss_display_name(&stat_min, gss_info.client,
+ &namebuf, &out_oid);
+ if (stat_maj != GSS_S_COMPLETE) {
+ /*
+ * RFC 2228 -
+ * "If the server rejects the security data(if
+ * a checksum fails, for instance), it should
+ * respond with reply code 535."
+ */
+ reply_gss_error(535, stat_maj, stat_min,
+ gss_info.mechoid,
+ "extracting GSSAPI identity name");
+ syslog(LOG_ERR, "gssapi error extracting identity");
+ if ((ret_flags & GSS_C_DELEG_FLAG) &&
+ deleg_creds != NULL)
+ (void) gss_release_cred(&stat_min,
+ &deleg_creds);
+ return (0);
+ }
+ gss_info.display_name = (char *)namebuf.value;
+
+ if (ret_flags & GSS_C_DELEG_FLAG) {
+ gss_info.have_creds = 1;
+ if (deleg_creds != NULL)
+ (void) gss_release_cred(&stat_min,
+ &deleg_creds);
+ }
+
+ /*
+ * If the server accepts the security data, but does
+ * not require any additional data(i.e., the security
+ * data exchange has completed successfully), it must
+ * respond with reply code 235.
+ */
+ if (!replied) {
+ if ((ret_flags & GSS_C_DELEG_FLAG) &&
+ !gss_info.have_creds)
+ reply(235,
+ "GSSAPI Authentication succeeded, but "
+ "could not accept forwarded credentials");
+ else
+ reply(235, "GSSAPI Authentication succeeded");
+ }
+ return (1);
+ } else if (accept_maj == GSS_S_CONTINUE_NEEDED) {
+ /*
+ * If the server accepts the security data, and
+ * requires additional data, it should respond with
+ * reply code 335.
+ */
+ reply(335, "more data needed");
+ if ((ret_flags & GSS_C_DELEG_FLAG) &&
+ deleg_creds != NULL)
+ (void) gss_release_cred(&stat_min, &deleg_creds);
+ }
+
+ return (0);
+}
+
+/*
+ * cleanup_bufrec
+ *
+ * cleanup the secure buffers
+ */
+static void
+cleanup_bufrec(bufrec *brec)
+{
+ if (brec->buf)
+ free(brec->buf);
+ brec->len = 0;
+ brec->clen = 0;
+ brec->idx = 0;
+}
+
+static int
+alloc_bufrec(bufrec *brec, size_t newsz)
+{
+ /*
+ * Try to allocate a buffer, if it fails,
+ * divide by 2 and try again.
+ */
+ cleanup_bufrec(brec);
+
+ while (newsz > 0 && !(brec->buf = malloc(newsz))) {
+ syslog(LOG_ERR,
+ "malloc bufrec(%d bytes) failed, trying %d",
+ newsz >>= 1);
+ }
+
+ if (brec->buf == NULL)
+ return (-1);
+
+ brec->alloc_len = newsz;
+ brec->len = newsz;
+ brec->clen = 0;
+ brec->idx = 0;
+ return (0);
+}
+
+/*
+ * Handle PBSZ command data, return value to caller.
+ * RFC 2228 says this is a 32 bit int, so limit max value here.
+ */
+unsigned int
+gss_setpbsz(char *pbszstr)
+{
+ unsigned int newsz = 0;
+ char *endp;
+#define MAX_PBSZ 4294967295
+
+ errno = 0;
+ newsz = (unsigned int)strtol(pbszstr, &endp, 10);
+ if (errno != 0 || newsz > MAX_PBSZ || *endp != '\0') {
+ reply(501, "Bad value for PBSZ: %s", pbszstr);
+ return (0);
+ }
+
+ if (newsz > ibr.len) {
+ if (alloc_bufrec(&obr, newsz) == -1) {
+ perror_reply(421, "Local resource failure: malloc");
+ dologout(1);
+ }
+ if (alloc_bufrec(&ibr, newsz) == -1) {
+ perror_reply(421, "Local resource failure: malloc");
+ dologout(1);
+ }
+ }
+ reply(200, "PBSZ =%lu", ibr.len);
+
+ return (ibr.len);
+}
+
+/*
+ * sec_putbuf
+ *
+ * Wrap the plaintext 'buf' data using gss_wrap and send
+ * it out.
+ *
+ * returns:
+ * bytes written (success)
+ * -1 on error(errno set)
+ * -2 on security error
+ */
+static int
+sec_putbuf(int fd, unsigned char *buf, int len)
+{
+ unsigned long net_len;
+ int ret = 0;
+ gss_buffer_desc in_buf, out_buf;
+ OM_uint32 maj_stat, min_stat;
+ int conf_state;
+
+ in_buf.value = buf;
+ in_buf.length = len;
+ maj_stat = gss_wrap(&min_stat, gss_info.context,
+ (gss_info.data_prot == PROT_P),
+ GSS_C_QOP_DEFAULT,
+ &in_buf, &conf_state,
+ &out_buf);
+
+ if (maj_stat != GSS_S_COMPLETE) {
+ reply_gss_error(535, maj_stat, min_stat,
+ gss_info.mechoid,
+ gss_info.data_prot == PROT_P ?
+ "GSSAPI wrap failed":
+ "GSSAPI sign failed");
+ return (-2);
+ }
+
+ net_len = (unsigned long)htonl((unsigned long) out_buf.length);
+
+ if ((ret = looping_write(fd, (const char *)&net_len, 4)) != 4) {
+ syslog(LOG_ERR, "Error writing net_len(%d): %m", net_len);
+ ret = -1;
+ goto putbuf_done;
+ }
+
+ if ((ret = looping_write(fd, out_buf.value, out_buf.length)) !=
+ out_buf.length) {
+ syslog(LOG_ERR, "Error writing %d bytes: %m", out_buf.length);
+ ret = -1;
+ goto putbuf_done;
+ }
+putbuf_done:
+
+ gss_release_buffer(&min_stat, &out_buf);
+ return (ret);
+}
+
+/*
+ * sec_write
+ *
+ * If GSSAPI security is established, encode the output
+ * and write it to the client. Else, just write it directly.
+ */
+int
+sec_write(int fd, char *buf, int len)
+{
+ int nbytes = 0;
+ if (gss_info.data_prot == PROT_C ||
+ !IS_GSSAUTH(cur_auth_type) ||
+ !(gss_info.authstate & GSS_ADAT_DONE))
+ nbytes = write(fd, buf, len);
+ else {
+ /*
+ * Fill up the buffer before actually encrypting
+ * and writing it out.
+ */
+ while ((obr.idx < obr.len) && (len > 0)) {
+ int n, ret;
+
+ /* how many bytes can we fit into the buffer? */
+ n = (len < (obr.len - obr.idx) ? len :
+ obr.len - obr.idx);
+ memcpy(obr.buf + obr.idx, buf, n);
+
+ obr.idx += n;
+
+ if (obr.idx >= obr.len) {
+ ret = sec_putbuf(fd, (unsigned char *)obr.buf,
+ obr.idx);
+ obr.idx = 0;
+ if (ret < 0)
+ return (ret);
+ }
+ len -= n;
+ nbytes += n;
+ }
+ }
+
+ return (nbytes);
+}
+
+/*
+ * CCC
+ *
+ * Clear Command Channel.
+ *
+ * We will understand this command but not allow it in a secure
+ * connection. It is very dangerous to allow someone to degrade
+ * the security of the command channel. See RFC2228 for more info.
+ */
+void
+ccc(void)
+{
+ /*
+ * Once we have negotiated security successfully,
+ * do not allow the control channel to be downgraded.
+ * It should be at least SAFE if not PRIVATE.
+ */
+ if (IS_GSSAUTH(cur_auth_type) &&
+ (gss_info.authstate & GSS_ADAT_DONE) == GSS_ADAT_DONE)
+ reply(534, "Control channel may not be downgraded");
+ else {
+ gss_info.ctrl_prot = PROT_C;
+ reply(200, "CCC ok");
+ }
+}
+
+int
+sec_putc(int c, FILE *stream)
+{
+ int ret = 0;
+ /*
+ * If we are NOT protecting the data
+ * OR not using the GSSAPI authentication
+ * OR GSSAPI data is not yet completed, send
+ * plaintext.
+ */
+ if (gss_info.data_prot == PROT_C ||
+ !IS_GSSAUTH(cur_auth_type) ||
+ !(gss_info.authstate & GSS_ADAT_DONE))
+ return (putc(c, stream));
+
+ /*
+ * Add the latest byte to the current buffer
+ */
+ if (obr.idx < obr.len) {
+ obr.buf[obr.idx++] = (unsigned char)(c & 0xff);
+ }
+
+ if (obr.idx == obr.len) {
+ ret = sec_putbuf(fileno(stream), (uchar_t *)obr.buf, obr.idx);
+ if (ret >= 0)
+ ret = 0;
+ obr.idx = 0;
+ }
+
+ return ((ret == 0 ? c : ret));
+}
+
+int
+sec_fprintf(FILE *stream, char *fmt, ...)
+{
+ int ret;
+ va_list ap;
+ va_start(ap, fmt);
+
+ if (gss_info.data_prot == PROT_C ||
+ !IS_GSSAUTH(cur_auth_type) ||
+ !(gss_info.authstate & GSS_ADAT_DONE)) {
+ ret = vfprintf(stream, fmt, ap);
+ } else {
+ (void) vsnprintf(obr.buf, obr.len, fmt, ap);
+ ret = sec_putbuf(fileno(stream), (unsigned char *)obr.buf,
+ strlen(obr.buf));
+ }
+ va_end(ap);
+ return (ret);
+}
+
+/*
+ * sec_fflush
+ *
+ * If GSSAPI protection is configured, write out whatever remains
+ * in the output buffer using the secure routines, otherwise
+ * just flush the stream.
+ */
+int
+sec_fflush(FILE *stream)
+{
+ int ret = 0;
+ if (gss_info.data_prot == PROT_C ||
+ !IS_GSSAUTH(cur_auth_type) ||
+ !(gss_info.authstate & GSS_ADAT_DONE)) {
+ fflush(stream);
+ return (0);
+ }
+ if (obr.idx > 0) {
+ ret = sec_putbuf(fileno(stream),
+ (unsigned char *)obr.buf, obr.idx);
+ obr.idx = 0;
+ }
+
+ if (ret >= 0)
+ ret = sec_putbuf(fileno(stream), (unsigned char *)"", 0);
+ /*
+ * putbuf returns number of bytes or a negative value,
+ * but fflush must return 0 or -1, so adjust the return
+ * value so that a positive value is interpreted as success.
+ */
+ return (ret >= 0 ? 0 : ret);
+}
+
+/*
+ * sec_getbytes
+ *
+ * Read and decrypt from the secure data channel.
+ *
+ * Return:
+ * > 0 == number of bytes available in gssbuf
+ * EOF == End of file.
+ * -2 == GSS error.
+ *
+ */
+static int
+sec_getbytes(int fd, char *buf, int nbytes)
+{
+ /*
+ * Only read from the network if our current buffer
+ * is all used up.
+ */
+ if (ibr.idx >= ibr.clen) {
+ int kerror;
+ int conf_state;
+ unsigned int length;
+ gss_buffer_desc xmit_buf, msg_buf;
+ OM_uint32 maj_stat, min_stat;
+
+ if ((kerror = looping_read(fd, (char *)&length, 4)) != 4) {
+ reply(535, "Couldn't read PROT buffer length: %d/%s",
+ kerror,
+ (kerror == -1) ? strerror(errno) : "premature EOF");
+ return (-2);
+ }
+
+ if ((length = (unsigned int)ntohl(length)) > ibr.len) {
+ reply(535, "Length(%d) > PBSZ(%d)", length, ibr.len);
+ return (-2);
+ }
+
+ if (length > 0) {
+ if ((kerror = looping_read(fd, ibr.buf, length)) !=
+ length) {
+ reply(535, "Couldn't read %u byte PROT buf: %s",
+ length, (kerror == -1) ?
+ strerror(errno) : "premature EOF");
+ return (-2);
+ }
+
+ xmit_buf.value = (char *)ibr.buf;
+ xmit_buf.length = length;
+
+ conf_state = (gss_info.data_prot == PROT_P);
+
+ /* decrypt/verify the message */
+ maj_stat = gss_unwrap(&min_stat, gss_info.context,
+ &xmit_buf, &msg_buf, &conf_state, NULL);
+ if (maj_stat != GSS_S_COMPLETE) {
+ reply_gss_error(535, maj_stat, min_stat,
+ gss_info.mechoid,
+ (gss_info.data_prot == PROT_P)?
+ "failed unwrapping ENC message":
+ "failed unwrapping MIC message");
+ return (-2);
+ }
+
+ memcpy(ibr.buf, msg_buf.value, msg_buf.length);
+ ibr.clen = msg_buf.length;
+ ibr.idx = 0;
+
+ gss_release_buffer(&min_stat, &msg_buf);
+ } else {
+ ibr.idx = 0;
+ ibr.clen = 0;
+ return (EOF);
+ }
+ }
+
+ /*
+ * If there are 'nbytes' of plain text available, use them, else
+ * get whats available.
+ */
+ nbytes = (nbytes < (ibr.clen - ibr.idx) ? nbytes : ibr.clen - ibr.idx);
+
+ memcpy(buf, ibr.buf + ibr.idx, nbytes);
+ ibr.idx += nbytes;
+
+ return ((nbytes == 0 ? EOF : nbytes));
+}
+
+/*
+ * Get a buffer of 'maxlen' bytes from the client.
+ * If we are using GSSAPI protection, use the secure
+ * input buffer.
+ */
+int
+sec_read(int fd, char *buf, int maxlen)
+{
+ int nbytes = 0;
+
+ if (gss_info.data_prot != PROT_C &&
+ IS_GSSAUTH(cur_auth_type) &&
+ (gss_info.authstate & GSS_ADAT_DONE)) {
+ /* Get as much data as possible */
+ nbytes = sec_getbytes(fd, buf, maxlen);
+ if (nbytes == EOF)
+ nbytes = 0;
+ } else {
+ nbytes = read(fd, buf, maxlen);
+ }
+ return (nbytes);
+}
+
+/*
+ * sec_getc
+ *
+ * Get a single character from the secure network buffer.
+ */
+int
+sec_getc(FILE *stream)
+{
+ int nbytes;
+ unsigned char c;
+
+ if (gss_info.data_prot != PROT_C &&
+ IS_GSSAUTH(cur_auth_type) &&
+ (gss_info.authstate & GSS_ADAT_DONE)) {
+ nbytes = sec_getbytes(fileno(stream), (char *)&c, 1);
+ if (nbytes > 0)
+ nbytes = (int)c;
+ return (nbytes);
+ } else
+ return (getc(stream));
+}
+
+/*
+ * sec_reply
+ *
+ * Securely encode a reply destined for the ftp client
+ * depending on the GSSAPI settings.
+ */
+int
+sec_reply(char *buf, int bufsiz, int n)
+{
+ char *out = NULL, *in = NULL;
+ size_t inlen;
+ gss_buffer_desc in_buf, out_buf;
+ OM_uint32 maj_stat, min_stat;
+ int conf_state, length, kerror;
+ int ret = 0;
+
+ if (debug)
+ syslog(LOG_DEBUG, "encoding %s", buf);
+
+ in_buf.value = buf;
+ in_buf.length = strlen(buf) + 1;
+ maj_stat = gss_wrap(&min_stat, gss_info.context,
+ gss_info.ctrl_prot == PROT_P,
+ GSS_C_QOP_DEFAULT,
+ &in_buf, &conf_state,
+ &out_buf);
+ if (maj_stat != GSS_S_COMPLETE) {
+ syslog(LOG_ERR, "gss_wrap %s did not complete",
+ (gss_info.ctrl_prot == PROT_P) ? "ENC": "MIC");
+ ret = -2;
+ gss_release_buffer(&min_stat, &out_buf);
+ goto end;
+ } else if ((gss_info.ctrl_prot == PROT_P) && !conf_state) {
+ syslog(LOG_ERR, "gss_wrap did not encrypt message");
+ ret = -2;
+ gss_release_buffer(&min_stat, &out_buf);
+ goto end;
+ } else {
+ out = (char *)malloc(out_buf.length);
+ if (out == NULL) {
+ syslog(LOG_ERR, "Memory error allocating buffer");
+ ret = -2;
+ gss_release_buffer(&min_stat, &out_buf);
+ goto end;
+ }
+ memcpy(out, out_buf.value, out_buf.length);
+ length = out_buf.length;
+ gss_release_buffer(&min_stat, &out_buf);
+ ret = 0;
+ }
+ /*
+ * Base64 encode the reply. encrypted "out" becomes
+ * encoded "in" buffer.
+ * Stick it all back in 'buf' for final output.
+ */
+ inlen = ENCODELEN(length);
+ in = (char *)malloc(inlen);
+ if (in == NULL) {
+ syslog(LOG_ERR, "Memory error allocating buffer");
+ ret = -2;
+ goto end;
+ }
+ if ((kerror = radix_encode((unsigned char *)out,
+ (unsigned char *)in, inlen,
+ &length, 0))) {
+ syslog(LOG_ERR, "Couldn't encode reply(%s)",
+ radix_error(kerror));
+ strncpy(buf, in, bufsiz-1);
+ buf[bufsiz - 1] = '\0';
+ } else {
+ snprintf(buf, bufsiz, "%s%c%s",
+ gss_info.ctrl_prot == PROT_P ? "632" : "631",
+ n ? ' ' : '-', in);
+ }
+end:
+ if (in) free(in);
+ if (out) free(out);
+
+ return (ret);
+}
+
+/*
+ * sec_decode_command
+ *
+ * If a command is received which is encoded(ENC, MIC, or CONF),
+ * decode it here using GSSAPI.
+ */
+char *
+sec_decode_command(char *cmd)
+{
+ char *out = NULL, *cp;
+ int len, mic, outlen;
+ gss_buffer_desc xmit_buf, msg_buf;
+ OM_uint32 maj_stat, min_stat;
+ int conf_state;
+ int kerror;
+ char *cs;
+ char *s = cmd;
+
+ if ((cs = strpbrk(s, " \r\n")))
+ *cs++ = '\0';
+ upper(s);
+
+ if ((mic = strcmp(s, "ENC")) != 0 && strcmp(s, "MIC") &&
+ strcmp(s, "CONF")) {
+ reply(533, "All commands must be protected.");
+ syslog(LOG_ERR, "Unprotected command received %s", s);
+ *s = '\0';
+ return (s);
+ }
+
+ if ((cp = strpbrk(cs, " \r\n")))
+ *cp = '\0';
+
+ outlen = DECODELEN(strlen(cs));
+
+ out = (char *)malloc(outlen);
+ if (out == NULL) {
+ reply(501, "Cannot decode response - not enough memory");
+ syslog(LOG_ERR, "Cannot decode response - not enough memory");
+ *s = '\0';
+ return (s);
+ }
+ len = strlen(cs);
+ if ((kerror = radix_encode((unsigned char *)cs,
+ (unsigned char *)out,
+ outlen, &len, 1))) {
+ reply(501, "Can't base 64 decode argument to %s command(%s)",
+ mic ? "MIC" : "ENC", radix_error(kerror));
+ *s = '\0';
+ free(out);
+ return (s);
+ }
+
+ if (debug)
+ syslog(LOG_DEBUG, "getline got %d from %s <%s >\n",
+ len, cs, mic ? "MIC" : "ENC");
+
+ xmit_buf.value = out;
+ xmit_buf.length = len;
+
+ /* decrypt the message */
+ conf_state = !mic;
+ maj_stat = gss_unwrap(&min_stat, gss_info.context, &xmit_buf,
+ &msg_buf, &conf_state, NULL);
+ if (maj_stat == GSS_S_CONTINUE_NEEDED) {
+ if (debug) syslog(LOG_DEBUG, "%s-unwrap continued",
+ mic ? "MIC" : "ENC");
+ reply(535, "%s-unwrap continued, oops", mic ? "MIC" : "ENC");
+ *s = 0;
+ free(out);
+ return (s);
+ }
+
+ free(out);
+ if (maj_stat != GSS_S_COMPLETE) {
+ reply_gss_error(535, maj_stat, min_stat,
+ gss_info.mechoid,
+ mic ? "failed unwrapping MIC message":
+ "failed unwrapping ENC message");
+ *s = 0;
+ return (s);
+ }
+
+ memcpy(s, msg_buf.value, msg_buf.length);
+ strcpy(s + msg_buf.length-(s[msg_buf.length-1] ? 0 : 1), "\r\n");
+ gss_release_buffer(&min_stat, &msg_buf);
+
+ return (s);
+}
+
+#endif /* defined(USE_GSS) */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/gssutil.h b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/gssutil.h
new file mode 100644
index 0000000000..84ee9298a1
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/gssutil.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _GSSUTIL_H
+#define _GSSUTIL_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <gssapi/gssapi.h>
+#ifdef SOLARIS_2
+#include <gssapi/gssapi_ext.h>
+#else
+#include <gssapi/gssapi_generic.h>
+#endif
+
+#ifndef SOLARIS_2
+#define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name
+#endif
+
+#ifndef g_OID_equal
+#define g_OID_equal(o1, o2) \
+ (((o1)->length == (o2)->length) && \
+ (memcmp((o1)->elements, (o2)->elements, (int)(o1)->length) == 0))
+#endif /* g_OID_equal */
+
+#define GSS_AUTH_NONE 0x00
+#define GSS_ADAT_DONE 0x01
+#define GSS_USER_DONE 0x02
+#define GSS_PWD_DONE 0x04
+
+typedef struct gss_inforec {
+ gss_ctx_id_t context;
+ gss_OID mechoid;
+ gss_name_t client;
+ char *display_name;
+ unsigned char data_prot;
+ unsigned char ctrl_prot;
+ unsigned char authstate;
+ unsigned char want_creds;
+ unsigned char have_creds;
+ unsigned char must_gss_auth;
+} gss_info_t;
+
+#define GSSUSERAUTH_OK(x) (((x).authstate & (GSS_ADAT_DONE|GSS_USER_DONE)) \
+== (GSS_ADAT_DONE|GSS_USER_DONE))
+
+#define IS_GSSAUTH(s) ((s) != NULL && (strcmp((s), "GSSAPI") == 0))
+
+int gss_user(struct passwd *);
+int gss_adat(char *adatstr);
+unsigned int gss_setpbsz(char *pbszstr);
+int sec_write(int fd, char *buf, int len);
+void ccc(void);
+int sec_putc(int c, FILE *stream);
+int sec_getc(FILE *stream);
+int sec_fprintf(FILE *stream, char *fmt, ...);
+int sec_fflush(FILE *stream);
+int sec_read(int fd, char *buf, int maxlen);
+int sec_reply(char *buf, int bufsiz, int n);
+char *sec_decode_command(char *cmd);
+size_t gss_getinbufsz(void);
+void gss_adjust_buflen(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _GSSUTIL_H */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/hostacc.c b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/hostacc.c
new file mode 100644
index 0000000000..9ba9a39f3a
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/hostacc.c
@@ -0,0 +1,359 @@
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/****************************************************************************
+ Copyright (c) 1999,2000 WU-FTPD Development Group.
+ All rights reserved.
+
+ Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994
+ The Regents of the University of California.
+ Portions Copyright (c) 1993, 1994 Washington University in Saint Louis.
+ Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc.
+ Portions Copyright (c) 1989 Massachusetts Institute of Technology.
+ Portions Copyright (c) 1998 Sendmail, Inc.
+ Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P. Allman.
+ Portions Copyright (c) 1997 by Stan Barber.
+ Portions Copyright (c) 1997 by Kent Landfield.
+ Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ Free Software Foundation, Inc.
+
+ Use and distribution of this software and its source code are governed
+ by the terms and conditions of the WU-FTPD Software License ("LICENSE").
+
+ If you did not receive a copy of the license, it may be obtained online
+ at http://www.wu-ftpd.org/license.html.
+
+ $Id: hostacc.c,v 1.8 2000/07/01 18:17:39 wuftpd Exp $
+
+****************************************************************************/
+/*
+ * hostacc.c - Implementation of host access for the
+ * experimental FTP daemon developed at
+ * Washington University.
+ *
+ * INITIAL AUTHOR - Bart Muijzer <bartm@cv.ruu.nl>
+ *
+ * HISTORY
+ * 930316 BM Created
+ * 930317 BM Converted to local naming convention;
+ * added rhost_ok(), cleanup code in enghacc()
+ * 930318 BM Ported to BSD; fixed memory leaks
+ * 930322 BM Changed algorithm: not in configfile = allow
+ * in configfile and match = allow|deny
+ * in configfile and no match = deny
+ */
+#include "config.h"
+
+#ifdef HOST_ACCESS
+
+#include "proto.h"
+#include "hostacc.h"
+
+static char linbuf[MAXLEN]; /* Buffer to hold one line of config-file */
+static char unibuf[MAXLEN]; /* Buffer to hold unified line */
+static hacc_t *ha_arr; /* Array with host access information */
+
+static FILE *ptFp; /* FILE * into host access config file */
+static int iHaInd = 0; /* Index in ha_arr */
+static int iHaSize; /* Will hold actual #elems in ha_arr */
+static int iFirstTim = 1; /* Used by gethacc() to see if index in */
+ /* ha_arr needs to be reset */
+
+/* ------------------------------------------------------------------------ *\
+ * FUNCTION : rhost_ok *
+ * PURPOSE : Check if a host is allowed to make a connection *
+ * ARGUMENTS : Remote user name, remote host name, remote host address *
+ * RETURNS : 1 if host is granted access, 0 if not *
+ \* ------------------------------------------------------------------------ */
+
+int rhost_ok(char *pcRuser, char *pcRhost, char *pcRaddr)
+{
+ hacc_t *ptHtmp;
+ char *pcHost;
+ char *ha_login;
+ int iInd, iLineMatch = 0, iUserSeen = 0;
+
+ switch (sethacc()) {
+ case 1:
+ /* no hostaccess file; disable mechanism */
+ return (1);
+ /* break; */
+ case -1:
+ syslog(LOG_INFO, "rhost_ok: sethacc failed");
+ endhacc();
+ return (0);
+ /* break; */
+ default:
+ break;
+ }
+
+ /* user names "ftp" and "anonymous" are equivalent */
+ if (!strcasecmp(pcRuser, "anonymous"))
+ pcRuser = "ftp";
+
+ while (((ptHtmp = gethacc()) != (hacc_t *) NULL) && !iLineMatch) {
+ if (strcasecmp(ptHtmp->ha_login, "anonymous"))
+ ha_login = ptHtmp->ha_login;
+ else
+ ha_login = "ftp";
+
+ if ((strcasecmp(pcRuser, ha_login)) && strcmp(ha_login, "*"))
+ /* wrong user, check rest of file */
+ continue;
+
+ /*
+ * We have seen a line regarding the current user.
+ * Remember this.
+ */
+ iUserSeen = 1;
+
+ for (iInd = 0, pcHost = ptHtmp->ha_hosts[0];
+ ((iInd < MAXHST) && (pcHost != NULL) && !iLineMatch);
+ pcHost = ptHtmp->ha_hosts[++iInd]) {
+ iLineMatch = hostmatch(pcHost, pcRaddr, pcRhost);
+ if (iLineMatch) {
+ iLineMatch = (ptHtmp->ha_type == ALLOW) ? 1 : 0;
+ goto match;
+ }
+ }
+ }
+
+ match:
+ /*
+ * At this point, iUserSeen == 1 if we've seen lines regarding
+ * the current user, and 0 otherwise. If we reached the end of
+ * the config file without a match we allow. Else, we allow or
+ * deny according to the rule found.
+ */
+
+ if (endhacc()) {
+ syslog(LOG_INFO, "rhost_ok: endhacc failed");
+ return (0);
+ }
+
+ if (iUserSeen)
+ return (ptHtmp == NULL) ? 0 : iLineMatch;
+ else
+ /* Nothing at all about user in configfile, allow */
+ return (1);
+}
+
+/* ------------------------------------------------------------------------ *\
+ * FUNCTION : sethacc *
+ * PURPOSE : Initialize data structures for host access *
+ * ARGUMENTS : None *
+ * RETURNS : -1 on failure, 1 if host access file doesn't exist, *
+ * 0 otherwise *
+ \* ------------------------------------------------------------------------ */
+
+static int sethacc(void)
+{
+ int iHaHind = 0; /* Index in list of hosts */
+ char *pcBegin, *pcEnd, *pcColon;
+ char *pcTmp1, *pcTmp2;
+ int iHaMalloc = 0; /* how many elem malloced */
+
+ iHaInd = 0;
+ iFirstTim = 1;
+ /* Open config file */
+ if ((ptFp = fopen(_path_ftphosts, "r")) == NULL) {
+ if (errno == ENOENT)
+ return (1);
+ else {
+ fatalmsg("Can't open host access file");
+ iHaSize = iHaInd;
+ return (-1);
+ }
+ }
+ ha_arr = (hacc_t *) malloc((iHaMalloc = 10) * sizeof(hacc_t));
+ if (ha_arr == NULL) {
+ syslog(LOG_ERR, "malloc error in sethacc");
+ exit(0);
+ }
+
+ while (fgets(linbuf, MAXLEN, ptFp) != NULL) {
+ iHaHind = 0;
+
+ /* Find first non-whitespace character */
+ for (pcBegin = linbuf;
+ ((*pcBegin == '\t') || (*pcBegin == ' '));
+ pcBegin++);
+
+ /* Get rid of comments */
+ if ((pcEnd = strchr(linbuf, '#')) != NULL)
+ *pcEnd = '\0';
+
+
+ /* Skip empty lines */
+ if ((pcBegin == pcEnd) || (*pcBegin == '\n'))
+ continue;
+
+ /* Substitute all whitespace by a single ":" so we can
+ * easily break on words later on. The easiest way is
+ * to copy the result into a temporary buffer (called
+ * the "unified buffer" because it will store a line in
+ * the same format, regardless of the format the original
+ * line was in).
+ * The result will look like: "allow:name:host:host:host"
+ */
+ for (pcTmp1 = pcBegin, pcTmp2 = unibuf; *pcTmp1; pcTmp1++) {
+ if (*pcTmp1 != '\t' && *pcTmp1 != ' ' && *pcTmp1 != '\n')
+ *pcTmp2++ = *pcTmp1;
+ else
+ /* whitespace */
+ if (*(pcTmp2 - 1) == ':')
+ continue;
+ else
+ *pcTmp2++ = ':';
+ }
+
+ /* Throw away trailing whitespace, now indicated by
+ * the last character of the unified buffer being a
+ * colon. Remember where the news string ends.
+ */
+ pcEnd = (*(pcTmp2 - 1) == ':') ? (pcTmp2 - 1) : pcTmp2;
+ *pcEnd = '\0'; /* Terminate new string */
+
+ /*
+ * Check if we need to expand the array with
+ * host access information
+ */
+ if (iHaInd >= iHaMalloc) {
+ ha_arr = (hacc_t *) realloc(ha_arr, (iHaMalloc += 10) * sizeof(hacc_t));
+ if (!ha_arr) {
+ fatalmsg("Failed to realloc host access array");
+ iHaSize = iHaInd;
+ return (-1);
+ }
+ }
+
+ /* Store what's left of the line into the
+ * hacc_t structure. First the access type,
+ * then the loginname, and finally a list of
+ * hosts to which all this applies.
+ */
+ pcBegin = unibuf;
+ if (!strncmp(pcBegin, "deny", 4)) {
+ ha_arr[iHaInd].ha_type = DENY;
+ pcBegin += 5;
+ }
+ else if (!strncmp(pcBegin, "allow", 5)) {
+ ha_arr[iHaInd].ha_type = ALLOW;
+ pcBegin += 6;
+ }
+ else {
+ fatalmsg("Format error in host access file");
+ iHaSize = iHaInd;
+ return (-1);
+ }
+
+ if ((pcColon = strchr(pcBegin, ':')) != NULL)
+ ha_arr[iHaInd].ha_login =
+ strnsav(pcBegin, (pcColon - pcBegin));
+ else {
+ fatalmsg("Format error in host access file");
+ iHaSize = iHaInd;
+ return (-1);
+ }
+
+ pcBegin = pcColon + 1;
+ while ((pcColon = strchr(pcBegin, ':')) != NULL) {
+ ha_arr[iHaInd].ha_hosts[iHaHind++] =
+ strnsav(pcBegin, (pcColon - pcBegin));
+ pcBegin = pcColon + 1;
+ if (iHaHind >= MAXHST) {
+ fatalmsg("Line too long");
+ iHaSize = iHaInd;
+ return (-1);
+ }
+ }
+ ha_arr[iHaInd].ha_hosts[iHaHind++] =
+ strnsav(pcBegin, (pcEnd - pcBegin));
+ ha_arr[iHaInd].ha_hosts[iHaHind] = NULL;
+ iHaInd++;
+ }
+ iHaSize = iHaInd; /* Record current size of ha_arr */
+ return ((feof(ptFp)) ? 0 : -1);
+}
+
+/* ------------------------------------------------------------------------ *\
+ * FUNCTION : gethacc *
+ * PURPOSE : return pointer to the next host_access structure *
+ * ARGUMENTS : None *
+ * RETURNS : NULL on failure, pointervalue otherwise *
+ \* ------------------------------------------------------------------------ */
+
+static hacc_t *gethacc(void)
+{
+ static int iHaInd;
+ static hacc_t ptTmp;
+
+ if (iFirstTim) {
+ iFirstTim = 0;
+ iHaInd = 0;
+ }
+ if (iHaInd >= iHaSize)
+ return ((hacc_t *) NULL);
+ else {
+ memmove(&ptTmp, &(ha_arr[iHaInd]), sizeof(hacc_t));
+ iHaInd++;
+ return (&ptTmp);
+ }
+}
+
+/* ------------------------------------------------------------------------ *\
+ * FUNCTION : endhacc *
+ * PURPOSE : Free allocated data structures for host access *
+ * ARGUMENTS : None *
+ * RETURNS : -1 on failure, 0 otherwise *
+ \* ------------------------------------------------------------------------ */
+
+static int endhacc(void)
+{
+ int iInd;
+ hacc_t *ptHtmp;
+
+ if (ha_arr == (hacc_t *) NULL)
+ return (0);
+
+ for (ptHtmp = ha_arr;
+ ptHtmp < ha_arr + iHaSize && ptHtmp->ha_type;
+ ptHtmp++) {
+ ptHtmp->ha_type = 0;
+ if (ptHtmp->ha_login) {
+ free(ptHtmp->ha_login);
+ ptHtmp->ha_login = NULL;
+ }
+ for (iInd = 0;
+ iInd < MAXHST && ptHtmp->ha_hosts[iInd];
+ iInd++) {
+ free(ptHtmp->ha_hosts[iInd]);
+ ptHtmp->ha_hosts[iInd] = NULL;
+ }
+ }
+ free(ha_arr);
+ ha_arr = NULL;
+
+ if (ptFp && fclose(ptFp))
+ return (-1);
+ return (0);
+}
+
+/* ------------------------------------------------------------------------ */
+
+static void fatalmsg(char *pcMsg)
+{
+ syslog(LOG_INFO, "host_access: %s", pcMsg);
+}
+
+static char *strnsav(char *pcStr, int iLen)
+{
+ char *pcBuf;
+
+ if ((pcBuf = (char *) malloc(iLen + 1)) == NULL)
+ return (NULL);
+ strncpy(pcBuf, pcStr, iLen);
+ pcBuf[iLen] = '\0';
+ return (pcBuf);
+}
+
+#endif /* HOST_ACCESS */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/hostacc.h b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/hostacc.h
new file mode 100644
index 0000000000..4e191d1347
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/hostacc.h
@@ -0,0 +1,82 @@
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/****************************************************************************
+ Copyright (c) 1999,2000 WU-FTPD Development Group.
+ All rights reserved.
+
+ Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994
+ The Regents of the University of California.
+ Portions Copyright (c) 1993, 1994 Washington University in Saint Louis.
+ Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc.
+ Portions Copyright (c) 1989 Massachusetts Institute of Technology.
+ Portions Copyright (c) 1998 Sendmail, Inc.
+ Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P. Allman.
+ Portions Copyright (c) 1997 by Stan Barber.
+ Portions Copyright (c) 1997 by Kent Landfield.
+ Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ Free Software Foundation, Inc.
+
+ Use and distribution of this software and its source code are governed
+ by the terms and conditions of the WU-FTPD Software License ("LICENSE").
+
+ If you did not receive a copy of the license, it may be obtained online
+ at http://www.wu-ftpd.org/license.html.
+
+ $Id: hostacc.h,v 1.9 2000/07/01 18:17:39 wuftpd Exp $
+
+****************************************************************************/
+/*
+ * hostacc.h - Header file used in the implementation of
+ * host access for the WU-FTPD FTP daemon
+ *
+ * INITIAL AUTHOR - Bart Muijzer <bartm@cv.ruu.nl>
+ */
+
+#ifdef HOST_ACCESS
+
+#include <stdio.h>
+#ifdef HAVE_SYS_SYSLOG_H
+#include <sys/syslog.h>
+#endif
+#if defined(HAVE_SYSLOG_H) || (!defined(AUTOCONF) && !defined(HAVE_SYS_SYSLOG_H))
+#include <syslog.h>
+#endif
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "pathnames.h" /* From the ftpd sources */
+
+/*
+ * Host Access types, as stored in the ha_type field,
+ * and some other constants. All of this is tunable as
+ * long as you don't depend on the values.
+ */
+
+#define ALLOW 1
+#define DENY 2
+
+#define MAXLEN 1024 /* Maximum length of one line in config file */
+#define MAXHST 12 /* Max. number of hosts allowed on one line */
+
+/* ------------------------------------------------------------------------- */
+
+/*
+ * Structure holding all host-access information
+ */
+
+typedef struct {
+ short ha_type; /* ALLOW | DENY */
+ char *ha_login; /* Loginname to investigate */
+ char *ha_hosts[MAXHST]; /* Array of hostnames */
+} hacc_t;
+
+/* ------------------------------------------------------------------------ */
+
+static int sethacc(void);
+static int endhacc(void);
+static hacc_t *gethacc(void);
+static void fatalmsg(char *pcMsg);
+static char *strnsav(char *pcStr, int iLen);
+
+#endif /* HOST_ACCESS */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/inet.c b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/inet.c
new file mode 100644
index 0000000000..0b8407cce4
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/inet.c
@@ -0,0 +1,233 @@
+/*
+ * Copyright 2001-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Utility functions which help with address and hostname manipulation in a
+ * mixed IPv4 / IPv6 environment.
+ */
+
+#include "config.h"
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <string.h>
+#include "proto.h"
+
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 64
+#endif
+
+/* Converts a hostname into an IP address in presentation form */
+char *inet_htop(const char *hostname)
+{
+#ifdef INET6
+ static char abuf[INET6_ADDRSTRLEN];
+ struct addrinfo hints, *result;
+ char *str = NULL;
+ void *addr = NULL;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_CANONNAME;
+ hints.ai_family = PF_UNSPEC;
+
+ if (getaddrinfo(hostname, NULL, &hints, &result) == 0) {
+ if (result->ai_family == AF_INET)
+ addr = ((void *)&((struct sockaddr_in *)result->ai_addr)->sin_addr);
+ else if (result->ai_family == AF_INET6)
+ addr = ((void *)&((struct sockaddr_in6 *)result->ai_addr)->sin6_addr);
+ if (addr)
+ str = (char *)inet_ntop_native(result->ai_family, addr, abuf,
+ sizeof(abuf));
+ freeaddrinfo(result);
+ return str;
+ }
+#else
+ struct hostent *hp;
+ struct in_addr in;
+
+ if ((hp = gethostbyname(hostname)) != NULL) {
+ memcpy(&in, hp->h_addr, sizeof(in));
+ return inet_ntoa(in);
+ }
+#endif
+ return NULL;
+}
+
+/*
+ * Converts a socket structures IP address into presentation form.
+ * Note: returns a pointer to a buffer which is overwritten on each call.
+ */
+char *inet_stop(struct SOCKSTORAGE *ss)
+{
+#ifdef INET6
+ static char abuf[INET6_ADDRSTRLEN];
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ss;
+
+ if (ss->ss_family == AF_INET6)
+ return (char *)inet_ntop_native(AF_INET6, &sin6->sin6_addr, abuf, sizeof (abuf));
+#endif
+ return inet_ntoa(((struct sockaddr_in *)ss)->sin_addr);
+}
+
+char *wu_gethostbyname(const char *hostname)
+{
+#ifdef INET6
+ static char hostbuf[MAXHOSTNAMELEN];
+ struct addrinfo hints, *result;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_CANONNAME;
+ hints.ai_family = PF_UNSPEC;
+
+ if (getaddrinfo(hostname, NULL, &hints, &result) == 0) {
+ strncpy(hostbuf, result->ai_canonname, sizeof(hostbuf));
+ hostbuf[sizeof(hostbuf) - 1] = '\0';
+ freeaddrinfo(result);
+ return hostbuf;
+ }
+#else
+ struct hostent *hp = gethostbyname(hostname);
+
+ if (hp)
+ return hp->h_name;
+#endif
+ return NULL;
+}
+
+int wu_gethostbyaddr(struct SOCKSTORAGE *ss, char *hostname, int hostlen)
+{
+#ifdef INET6
+ char hostbuf[NI_MAXHOST];
+#else
+ struct hostent *hp;
+#endif
+
+ if ((ss == NULL) || (hostname == NULL) || (hostlen < 1))
+ return 0;
+
+#ifdef INET6
+ if (getnameinfo((struct sockaddr *)ss, SOCK_LEN(*ss), hostbuf,
+ sizeof(hostbuf), NULL, 0, NI_NAMEREQD) == 0) {
+ strncpy(hostname, hostbuf, hostlen);
+ hostname[hostlen - 1] = '\0';
+ return 1;
+ }
+#else
+ hp = gethostbyaddr((char *)&ss->sin_addr, sizeof(struct in_addr), AF_INET);
+ if (hp) {
+ strncpy(hostname, hp->h_name, hostlen);
+ hostname[hostlen - 1] = '\0';
+ return 1;
+ }
+#endif
+ return 0;
+}
+
+/* Compares a socket structures IP address with addr, returning 0 on a match */
+int sock_cmp_inaddr(struct SOCKSTORAGE *ss, struct in_addr addr) {
+#ifdef INET6
+ if (ss->ss_family == AF_INET6) {
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ss;
+
+ if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
+ u_char *a = (u_char *)&sin6->sin6_addr;
+
+ /* compare the IPv4 part of an IPv4-mapped IPv6 address */
+ return memcmp(&addr, a + sizeof(struct in6_addr) - sizeof(struct in_addr), sizeof(struct in_addr));
+ }
+ return 1;
+ }
+#endif
+ return ((struct sockaddr_in *)ss)->sin_addr.s_addr != addr.s_addr;
+}
+
+#ifdef INET6
+/* Sets a socket structures IP address to addr */
+void sock_set_inaddr(struct SOCKSTORAGE *ss, struct in_addr addr) {
+ if (ss->ss_family == AF_INET6) {
+ struct in6_addr *in6;
+
+ in6 = &((struct sockaddr_in6 *)ss)->sin6_addr;
+ memset(&in6->s6_addr[0], 0, 10);
+ memset(&in6->s6_addr[10], 0xff, 2);
+ memcpy(&in6->s6_addr[12], &addr, sizeof(struct in_addr));
+ return;
+ }
+ ((struct sockaddr_in *)ss)->sin_addr = addr;
+}
+
+/* Compares two socket structure IP addresses, returning 0 if they match */
+int sock_cmp_addr(struct SOCKSTORAGE *ss1, struct SOCKSTORAGE *ss2) {
+ if (ss1->ss_family == AF_INET6) {
+ if (ss2->ss_family == AF_INET6)
+ return memcmp(&((struct sockaddr_in6 *)ss1)->sin6_addr,
+ &((struct sockaddr_in6 *)ss2)->sin6_addr,
+ sizeof(struct in6_addr));
+ return sock_cmp_inaddr(ss1, ((struct sockaddr_in *)ss2)->sin_addr);
+ }
+ return sock_cmp_inaddr(ss2, ((struct sockaddr_in *)ss1)->sin_addr);
+}
+
+void sock_set_scope(struct SOCKSTORAGE *dst, struct SOCKSTORAGE *src) {
+#ifdef HAVE_SIN6_SCOPE_ID
+ struct sockaddr_in6 *src_in6 = (struct sockaddr_in6 *)src;
+ struct sockaddr_in6 *dst_in6 = (struct sockaddr_in6 *)dst;
+
+ if (dst->ss_family == AF_INET6) {
+ if ((src->ss_family == AF_INET6) &&
+ (memcmp(&src_in6->sin6_addr, &dst_in6->sin6_addr,
+ sizeof(struct in6_addr)) == 0))
+ dst_in6->sin6_scope_id = src_in6->sin6_scope_id;
+ else
+ dst_in6->sin6_scope_id = 0;
+ }
+#endif
+}
+
+/*
+ * Similar to inet_pton(), str can be an IPv4 or IPv6 address, but an IPv6
+ * address is returned in addr.
+ */
+int inet_pton6(char *str, struct in6_addr *addr)
+{
+ struct in_addr v4addr;
+
+ /* Try v6 first */
+ if (inet_pton(AF_INET6, str, addr) != 1) {
+ /* If that fails, try v4 and map it */
+ if (inet_pton(AF_INET, str, &v4addr) == 1) {
+ memset(&addr->s6_addr[0], 0, 10);
+ memset(&addr->s6_addr[10], 0xff, 2);
+ memcpy(&addr->s6_addr[12], &v4addr, sizeof(struct in_addr));
+ }
+ else
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * Similar to inet_ntop(), except when addr is an IPv4-mapped IPv6 address
+ * returns a printable IPv4 address (not an IPv4-mapped IPv6 address).
+ */
+const char *inet_ntop_native(int af, const void *addr, char *dst, size_t size)
+{
+ const char *result;
+
+ if (af == AF_INET6) {
+ if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)addr))
+ result = inet_ntop(AF_INET, (char *)addr + sizeof(struct in6_addr)
+ - sizeof(struct in_addr), dst, size);
+ else
+ result = inet_ntop(AF_INET6, addr, dst, size);
+ }
+ else
+ result = inet_ntop(af, addr, dst, size);
+ return result;
+}
+#endif /* INET6 */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/logwtmp.c b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/logwtmp.c
new file mode 100644
index 0000000000..ec17f3ee68
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/logwtmp.c
@@ -0,0 +1,198 @@
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/****************************************************************************
+ Copyright (c) 1999,2000 WU-FTPD Development Group.
+ All rights reserved.
+
+ Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994
+ The Regents of the University of California.
+ Portions Copyright (c) 1993, 1994 Washington University in Saint Louis.
+ Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc.
+ Portions Copyright (c) 1989 Massachusetts Institute of Technology.
+ Portions Copyright (c) 1998 Sendmail, Inc.
+ Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P. Allman.
+ Portions Copyright (c) 1997 by Stan Barber.
+ Portions Copyright (c) 1997 by Kent Landfield.
+ Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ Free Software Foundation, Inc.
+
+ Use and distribution of this software and its source code are governed
+ by the terms and conditions of the WU-FTPD Software License ("LICENSE").
+
+ If you did not receive a copy of the license, it may be obtained online
+ at http://www.wu-ftpd.org/license.html.
+
+ $Id: logwtmp.c,v 1.16 2000/07/01 18:17:39 wuftpd Exp $
+
+****************************************************************************/
+#include "config.h"
+
+#include <sys/types.h>
+#ifdef TIME_WITH_SYS_TIME
+#include <time.h>
+#include <sys/time.h>
+#else
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#else
+#include <time.h>
+#endif
+#endif
+#include <sys/stat.h>
+#if defined(HAVE_FCNTL_H)
+#include <fcntl.h>
+#endif
+#include <utmp.h>
+#ifdef SVR4
+#ifndef NO_UTMPX
+#include <utmpx.h>
+#ifndef _SCO_DS
+#include <sac.h>
+#endif
+#endif
+#endif
+#ifdef BSD
+#include <strings.h>
+#else
+#include <string.h>
+#endif
+#ifdef HAVE_SYS_SYSLOG_H
+#include <sys/syslog.h>
+#endif
+#if defined(HAVE_SYSLOG_H) || (!defined(AUTOCONF) && !defined(HAVE_SYS_SYSLOG_H))
+#include <syslog.h>
+#endif
+#ifdef __FreeBSD__
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#endif
+
+#include "pathnames.h"
+#include "proto.h"
+
+#ifndef NO_UTMP
+static int fd = -1;
+#endif
+#if defined(SVR4) && !defined(NO_UTMPX)
+static int fdx = -1;
+#endif
+
+/* Modified version of logwtmp that holds wtmp file open after first call,
+ * for use with ftp (which may chroot after login, but before logout). */
+
+void wu_logwtmp(char *line, char *name, char *host, int login)
+{
+ struct stat buf;
+#ifndef NO_UTMP
+ struct utmp ut;
+#endif
+
+#if defined(SVR4) && !defined(NO_UTMPX)
+ /*
+ * Date: Tue, 09 Mar 1999 14:59:42 -0600
+ * From: Chad Price <cprice@molbio.unmc.edu>
+ * To: wu-ftpd@wugate.wustl.edu
+ * Subject: Re: Problem w/ Solaris /var/adm/wtmpx and /usr/bin/last(1)
+ *
+ * I've been running Sol 2.4 since it came out, and the 'last' command
+ * has never worked correctly, for ftpd or logins either one. wtmpx
+ * often fails to close out sessions when the user logs out. As a
+ * result, I only use last to see who logged in, not who/when the
+ * logout occurred.
+ *
+ * When I first installed it, it was even worse, and they immediately
+ * told me to patch the system. This fixed it to semi-compus mentis,
+ * but not to working order. So I guess my conclusion is: ignore the
+ * wtmpx / last log stuff on Solaris 2.4 (and other releases of Solaris
+ * too from what I see in the comments), it's broken and always has
+ * been. I do of course stand ready to be corrected (in this case,
+ * pointed to a patch which really does fix it.)
+ *
+ */
+ struct utmpx utx;
+
+ if (fdx < 0 && (fdx = open(WTMPX_FILE, O_WRONLY | O_APPEND, 0)) < 0) {
+ syslog(LOG_ERR, "wtmpx %s %m", WTMPX_FILE);
+ return;
+ }
+
+ if (fstat(fdx, &buf) == 0) {
+ memset((void *) &utx, '\0', sizeof(utx));
+ (void) strncpy(utx.ut_user, name, sizeof(utx.ut_user));
+ (void) strncpy(utx.ut_host, host, sizeof(utx.ut_host));
+ (void) strncpy(utx.ut_id, "ftp", sizeof(utx.ut_id));
+ (void) strncpy(utx.ut_line, line, sizeof(utx.ut_line));
+ utx.ut_syslen = strlen(utx.ut_host) + 1;
+ utx.ut_pid = getpid();
+ (void) time(&utx.ut_tv.tv_sec);
+ if (login /* name && *name */ ) {
+ utx.ut_type = USER_PROCESS;
+ }
+ else {
+ utx.ut_type = DEAD_PROCESS;
+ }
+ utx.ut_exit.e_termination = 0;
+ utx.ut_exit.e_exit = 0;
+ if (write(fdx, (char *) &utx, sizeof(struct utmpx)) !=
+ sizeof(struct utmpx))
+ (void) ftruncate(fdx, buf.st_size);
+ }
+#endif /* defined(SVR4) && !defined(NO_UTMPX) */
+
+#ifndef NO_UTMP
+#ifdef __FreeBSD__
+ if (strlen(host) > UT_HOSTSIZE) {
+ if ((host = inet_htop(host)) == NULL)
+ host = "invalid hostname";
+ }
+#endif
+
+ if (fd < 0 && (fd = open(_PATH_WTMP, O_WRONLY | O_APPEND, 0)) < 0) {
+ syslog(LOG_ERR, "wtmp %s %m", _PATH_WTMP);
+ return;
+ }
+ if (fstat(fd, &buf) == 0) {
+#ifdef UTMAXTYPE
+ memset((void *) &ut, 0, sizeof(ut));
+#ifdef LINUX
+ (void) strncpy(ut.ut_id, "", sizeof(ut.ut_id));
+#else
+ (void) strncpy(ut.ut_id, "ftp", sizeof(ut.ut_id));
+#endif
+ (void) strncpy(ut.ut_line, line, sizeof(ut.ut_line));
+ ut.ut_pid = getpid();
+ if (login /* name && *name */ ) {
+ (void) strncpy(ut.ut_user, name, sizeof(ut.ut_user));
+ ut.ut_type = USER_PROCESS;
+ }
+ else
+ ut.ut_type = DEAD_PROCESS;
+#if defined(HAVE_UT_UT_EXIT_E_TERMINATION) || (!defined(AUTOCONF) && !defined(LINUX))
+ ut.ut_exit.e_termination = 0;
+ ut.ut_exit.e_exit = 0;
+#endif
+#else
+ (void) strncpy(ut.ut_line, line, sizeof(ut.ut_line));
+ if (login) {
+ (void) strncpy(ut.ut_name, name, sizeof(ut.ut_name));
+ }
+ else {
+ (void) strncpy(ut.ut_name, "", sizeof(ut.ut_name));
+ }
+#endif /* UTMAXTYPE */
+#ifdef HAVE_UT_UT_HOST /* does have host in utmp */
+ if (login) {
+ (void) strncpy(ut.ut_host, host, sizeof(ut.ut_host));
+ }
+ else {
+ (void) strncpy(ut.ut_host, "", sizeof(ut.ut_host));
+ }
+#endif
+ (void) time(&ut.ut_time);
+ if (write(fd, (char *) &ut, sizeof(struct utmp)) !=
+ sizeof(struct utmp))
+ (void) ftruncate(fd, buf.st_size);
+ }
+#endif /* NO_UTMP */
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/pathnames.h b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/pathnames.h
new file mode 100644
index 0000000000..344e26906f
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/pathnames.h
@@ -0,0 +1,152 @@
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/****************************************************************************
+ Copyright (c) 1999,2000 WU-FTPD Development Group.
+ All rights reserved.
+
+ Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994
+ The Regents of the University of California.
+ Portions Copyright (c) 1993, 1994 Washington University in Saint Louis.
+ Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc.
+ Portions Copyright (c) 1989 Massachusetts Institute of Technology.
+ Portions Copyright (c) 1998 Sendmail, Inc.
+ Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P. Allman.
+ Portions Copyright (c) 1997 by Stan Barber.
+ Portions Copyright (c) 1997 by Kent Landfield.
+ Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ Free Software Foundation, Inc.
+
+ Use and distribution of this software and its source code are governed
+ by the terms and conditions of the WU-FTPD Software License ("LICENSE").
+
+ If you did not receive a copy of the license, it may be obtained online
+ at http://www.wu-ftpd.org/license.html.
+
+ $Id: pathnames.h.in,v 1.5 2000/07/01 18:04:21 wuftpd Exp $
+
+****************************************************************************/
+
+#ifdef HAVE_PATHS_H
+#include <paths.h>
+#endif
+
+#ifdef SOLARIS_2
+#define UTMP_DIR "/var/adm"
+#define WTMP_DIR "/var/adm"
+#define LASTLOG_DIR "/var/adm"
+#else
+#define UTMP_DIR "/etc"
+#define WTMP_DIR "/usr/adm"
+#define LASTLOG_DIR "/usr/adm"
+#endif
+
+#define _PATH_EXECPATH "/bin/ftp-exec"
+
+#ifdef VIRTUAL
+/*
+ ** Virtual hosting requires to support many different types of customer.
+ ** needs. There must be complete support for the various ftpd system files
+ ** and their functionality.
+ **
+ ** Supported on an individual virtual host basis:
+ ** ----------------------------------------------
+ ** _PATH_FTPACCESS
+ ** _PATH_FTPUSERS
+ ** _PATH_PRIVATE
+ ** _PATH_FTPHOSTS
+ ** _PATH_CVT
+ **
+ ** Set in a site's ftpaccess file
+ ** _PATH_XFERLOG
+ **
+ ** Supported on a site-wide basis:
+ ** --------------------------------
+ ** _PATH_FTPSERVERS
+ ** _PATH_EXECPATH
+ ** _PATH_PIDNAMES
+ ** _PATH_UTMP
+ ** _PATH_WTMP
+ ** _PATH_LASTLOG
+ ** _PATH_BSHELL
+ ** _PATH_DEVNULL
+ **
+ ** Following are possibly overridden by VIRTUAL Hosting Configuation
+ ** Edit accordingly.
+ */
+#endif
+
+#undef _PATH_FTPUSERS
+#undef _PATH_FTPACCESS
+#undef _PATH_CVT
+#undef _PATH_PRIVATE
+
+#define _PATH_FTPUSERS "/etc/ftpd/ftpusers"
+#define _PATH_FTPACCESS "/etc/ftpd/ftpaccess"
+#define _PATH_CVT "/etc/ftpd/ftpconversions"
+#define _PATH_PRIVATE "/etc/ftpd/ftpgroups"
+
+#ifdef VIRTUAL
+#undef _PATH_FTPSERVERS
+#define _PATH_FTPSERVERS "/etc/ftpd/ftpservers"
+#endif
+
+#ifdef HOST_ACCESS
+#undef _PATH_FTPHOSTS
+#define _PATH_FTPHOSTS "/etc/ftpd/ftphosts"
+#endif
+
+/* _PATH_FTPD_PIDFILE is only used if DAEMON is defined */
+
+#define _PATH_PIDNAMES "/var/run/ftp.pids-%s"
+#define _PATH_FTPD_PID "/var/run/ftpd.pid"
+#define _PATH_XFERLOG "/var/log/xferlog"
+
+#ifndef _PATH_UTMP
+#ifdef UTMP_FILE
+#define _PATH_UTMP UTMP_FILE
+#endif
+#endif
+
+#ifndef _PATH_WTMP
+#ifdef WTMP_FILE
+#define _PATH_WTMP WTMP_FILE
+#endif
+#endif
+
+#if defined(sun) && defined(SOLARIS_2)
+#ifndef _PATH_UTMP
+#define _PATH_UTMP UTMP_DIR"/utmp"
+#endif
+#ifndef _PATH_WTMP
+#define _PATH_WTMP WTMP_DIR"/wtmp"
+#endif
+#ifndef _PATH_LASTLOG
+#define _PATH_LASTLOG LASTLOG_DIR"/lastlog"
+#endif
+#else
+#ifndef _PATH_UTMP
+#define _PATH_UTMP "/etc/utmp"
+#endif
+#ifndef _PATH_WTMP
+#define _PATH_WTMP "/usr/adm/wtmp"
+#endif
+#ifndef _PATH_LASTLOG
+#define _PATH_LASTLOG "/usr/adm/lastlog"
+#endif
+#endif
+
+#ifndef _PATH_BSHELL
+#define _PATH_BSHELL "/bin/sh"
+#endif
+
+#ifndef _PATH_DEVNULL
+#define _PATH_DEVNULL "/dev/null"
+#endif
+
+#ifndef _PATHS_DEFINED_
+extern char _path_ftpaccess[];
+extern char _path_ftpusers[];
+extern char _path_ftphosts[];
+extern char _path_private[];
+extern char _path_cvt[];
+#endif
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/paths.c b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/paths.c
new file mode 100644
index 0000000000..3ea65f0d7d
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/paths.c
@@ -0,0 +1,250 @@
+/*
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/****************************************************************************
+ Copyright (c) 1999,2000 WU-FTPD Development Group.
+ All rights reserved.
+
+ Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994
+ The Regents of the University of California.
+ Portions Copyright (c) 1993, 1994 Washington University in Saint Louis.
+ Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc.
+ Portions Copyright (c) 1989 Massachusetts Institute of Technology.
+ Portions Copyright (c) 1998 Sendmail, Inc.
+ Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P. Allman.
+ Portions Copyright (c) 1997 by Stan Barber.
+ Portions Copyright (c) 1997 by Kent Landfield.
+ Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ Free Software Foundation, Inc.
+
+ Use and distribution of this software and its source code are governed
+ by the terms and conditions of the WU-FTPD Software License ("LICENSE").
+
+ If you did not receive a copy of the license, it may be obtained online
+ at http://www.wu-ftpd.org/license.html.
+
+ $Id: paths.c,v 1.7 2000/07/01 18:17:39 wuftpd Exp $
+
+****************************************************************************/
+/*
+ * paths.c - setting up the correct pathing to support files/directories
+ *
+ * INITAL AUTHOR - Kent Landfield <kent@landfield.com>
+ */
+#include "config.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <syslog.h>
+#include <sys/param.h>
+
+#include "pathnames.h"
+#include "proto.h"
+
+#ifdef VIRTUAL
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+int virtual_mode = 0;
+int virtual_ftpaccess = 0;
+
+extern int debug;
+extern char virtual_hostname[];
+extern char virtual_address[];
+
+#endif
+
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 64
+#endif
+
+/*
+ ** Pathing storage
+ */
+
+#define _PATHS_DEFINED_ 1
+char _path_ftpaccess[MAXPATHLEN];
+char _path_ftpusers[MAXPATHLEN];
+char _path_ftphosts[MAXPATHLEN];
+char _path_private[MAXPATHLEN];
+char _path_cvt[MAXPATHLEN];
+
+extern char logfile[];
+extern char hostname[];
+
+void setup_paths(void);
+
+/*
+ ** Virtual hosting has to support many different types of needs. There
+ ** must be complete support for the various ftpd system files and their
+ ** functionality.
+ **
+ ** Full support on a virtual host basis:
+ ** -------------------------------------
+ ** _PATH_FTPACCESS
+ ** _PATH_FTPUSERS
+ ** _PATH_PRIVATE
+ ** _PATH_FTPHOSTS
+ ** _PATH_CVT
+ **
+ ** Set in a site's ftpaccess file
+ ** _PATH_XFERLOG
+ **
+ ** Supported on a site basis:
+ ** --------------------------
+ ** _PATH_FTPSERVERS
+ ** _PATH_EXECPATH
+ ** _PATH_PIDNAMES
+ ** _PATH_UTMP
+ ** _PATH_WTMP
+ ** _PATH_LASTLOG
+ ** _PATH_BSHELL
+ ** _PATH_DEVNULL
+ */
+
+/* ------------------------------------------------------------------------ */
+/* FUNCTION : setup_paths */
+/* PURPOSE : Determine appropriate paths to various configuration files. */
+/* ARGUMENTS : None */
+/* RETURNS : None */
+/* ------------------------------------------------------------------------ */
+
+void setup_paths(void)
+{
+#ifdef VIRTUAL
+ char *sp;
+ char configdir[MAXPATHLEN];
+ char filepath[MAXPATHLEN];
+#ifdef INET6
+ char hostaddress[INET6_ADDRSTRLEN];
+#else
+ char hostaddress[32];
+#endif
+ FILE *svrfp;
+ struct stat st;
+#if defined(UNIXWARE) || defined(AIX)
+ size_t virtual_len;
+#else
+ int virtual_len;
+#endif
+ struct SOCKSTORAGE virtual_addr;
+#endif
+
+ (void) strlcpy(_path_ftpaccess, _PATH_FTPACCESS, sizeof(_path_ftpaccess));
+ (void) strlcpy(_path_ftpusers, _PATH_FTPUSERS, sizeof(_path_ftpusers));
+ (void) strlcpy(_path_private, _PATH_PRIVATE, sizeof(_path_private));
+ (void) strlcpy(_path_cvt, _PATH_CVT, sizeof(_path_cvt));
+ (void) strlcpy(logfile, _PATH_XFERLOG, MAXPATHLEN);
+ (void) strlcpy(_path_ftphosts, _PATH_FTPHOSTS, sizeof(_path_ftphosts));
+
+#ifdef VIRTUAL
+ /*
+ ** Open PATH_FTPSERVERS config file. If the file does not
+ ** exist then revert to using the standard _PATH_* path defines.
+ */
+
+ if ((svrfp = fopen(_PATH_FTPSERVERS, "r")) != NULL) {
+ /*
+ ** OK. The ftpservers file exists and is open.
+ **
+ ** Format of the file is:
+ ** ipaddr/hostname directory-containing-configuration-files
+ **
+ ** 208.196.145.10 /etc/ftpd/ftpaccess.somedomain/
+ ** 208.196.145.200 /etc/ftpd/ftpaccess.someotherdomain/
+ ** some.domain INTERNAL
+ **
+ ** Parse the file and try to match the IP address to one found
+ ** in the file. If a match is found then return the path to
+ ** the specified directory that contains the configuration files
+ ** for that specific domain. If a match is not found, or an invalid
+ ** directory path is encountered like above, return standard paths.
+ **
+ ** As usual, comments and blanklines are ignored.
+ */
+
+ /* get our address */
+
+ virtual_len = sizeof(virtual_addr);
+ if (getsockname(0, (struct sockaddr *) &virtual_addr, &virtual_len) == 0) {
+ while (read_servers_line(svrfp, hostaddress, sizeof(hostaddress),
+ configdir, sizeof(configdir)) == 1) {
+ if (!strcmp(hostaddress, inet_stop(&virtual_addr))) {
+ if (debug)
+ syslog(LOG_DEBUG, "VirtualFTP Connect to: %s", hostaddress);
+ (void) strlcpy(virtual_address, hostaddress,
+ MAXHOSTNAMELEN);
+ if (hostname != NULL) {
+ /* reset hostname to this virtual name */
+ wu_gethostbyaddr(&virtual_addr, hostname, MAXHOSTNAMELEN);
+ (void) strlcpy(virtual_hostname, hostname,
+ MAXHOSTNAMELEN);
+ }
+
+ /* get rid of trailing slash */
+ sp = configdir + (strlen(configdir) - 1);
+ if (*sp == '/')
+ *sp = '\0';
+
+ /*
+ ** check to see that a valid directory value was
+ ** supplied and not something such as "INTERNAL"
+ */
+
+ if ((stat(configdir, &st) == 0) &&
+ ((st.st_mode & S_IFMT) == S_IFDIR)) {
+
+ (void) snprintf(filepath, sizeof(filepath),
+ "%s/ftpaccess", configdir);
+ if (access(filepath, R_OK) == 0) {
+ (void) strlcpy(_path_ftpaccess, filepath,
+ sizeof(_path_ftpaccess));
+ virtual_mode = 1;
+ virtual_ftpaccess = 1;
+ }
+
+ (void) snprintf(filepath, sizeof(filepath),
+ "%s/ftpusers", configdir);
+ if (access(filepath, R_OK) == 0)
+ (void) strlcpy(_path_ftpusers, filepath,
+ sizeof(_path_ftpusers));
+
+ (void) snprintf(filepath, sizeof(filepath),
+ "%s/ftpgroups", configdir);
+ if (access(filepath, R_OK) == 0)
+ (void) strlcpy(_path_private, filepath,
+ sizeof(_path_private));
+
+ (void) snprintf(filepath, sizeof(filepath),
+ "%s/ftphosts", configdir);
+ if (access(filepath, R_OK) == 0)
+ (void) strlcpy(_path_ftphosts, filepath,
+ sizeof(_path_ftphosts));
+
+ (void) snprintf(filepath, sizeof(filepath),
+ "%s/ftpconversions", configdir);
+ if (access(filepath, R_OK) == 0)
+ (void) strlcpy(_path_cvt, filepath,
+ sizeof(_path_cvt));
+ }
+ (void) fclose(svrfp);
+ return;
+ }
+ }
+ }
+ (void) fclose(svrfp);
+ }
+#endif /* VIRTUAL */
+
+ return;
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/popen.c b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/popen.c
new file mode 100644
index 0000000000..7848dc7c85
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/popen.c
@@ -0,0 +1,328 @@
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/****************************************************************************
+ Copyright (c) 1999,2000 WU-FTPD Development Group.
+ All rights reserved.
+
+ Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994
+ The Regents of the University of California.
+ Portions Copyright (c) 1993, 1994 Washington University in Saint Louis.
+ Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc.
+ Portions Copyright (c) 1989 Massachusetts Institute of Technology.
+ Portions Copyright (c) 1998 Sendmail, Inc.
+ Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P. Allman.
+ Portions Copyright (c) 1997 by Stan Barber.
+ Portions Copyright (c) 1997 by Kent Landfield.
+ Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ Free Software Foundation, Inc.
+
+ Use and distribution of this software and its source code are governed
+ by the terms and conditions of the WU-FTPD Software License ("LICENSE").
+
+ If you did not receive a copy of the license, it may be obtained online
+ at http://www.wu-ftpd.org/license.html.
+
+ $Id: popen.c,v 1.16 2000/07/01 18:17:39 wuftpd Exp $
+
+****************************************************************************/
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#if defined(HAVE_FCNTL_H)
+#include <fcntl.h>
+#endif
+#include "pathnames.h"
+#include "proto.h"
+
+/*
+ * Special version of popen which avoids call to shell. This insures noone
+ * may create a pipe to a hidden program as a side effect of a list or dir
+ * command.
+ */
+static int popen_fd = -1;
+static pid_t popen_pid = -1;
+/*
+ * The globbed argv could end up being huge, so we must dynamically allocate
+ * it. Allocate it in chunks of GARGV_INC pointers.
+ */
+#define GARGV_INC 100
+#define ARGV_INC 5
+
+static char **argv;
+static char **gargv;
+static int argv_size;
+static int gargv_size;
+
+FILE *ftpd_popen(char *program, char *type, int closestderr)
+{
+ register char *cp;
+ FILE *iop = NULL;
+ int argc, gargc, pdes[2], i, devnullfd;
+ char **pop, *vv[2];
+ extern char *globerr;
+
+ /*
+ * ftpd never needs more than one pipe open at a time, so only one PID is
+ * stored (in popen_pid). Protect against multiple pipes in case this
+ * changes.
+ */
+ if (popen_fd != -1)
+ return (NULL);
+
+ if ((*type != 'r' && *type != 'w') || type[1])
+ return (NULL);
+
+ if (gargv == NULL) {
+ gargv = (char **)malloc(GARGV_INC * sizeof (char *));
+ if (gargv == NULL) {
+ return (NULL);
+ }
+ gargv_size = GARGV_INC;
+ }
+
+ if (argv == NULL) {
+ argv = (char **)malloc(ARGV_INC * sizeof (char *));
+ if (argv == NULL) {
+ return (NULL);
+ }
+ argv_size = ARGV_INC;
+ }
+
+ if (pipe(pdes) < 0)
+ return (NULL);
+
+ /* empty the array */
+ (void) memset((void *) argv, 0, argv_size * sizeof(char *));
+ /* break up string into pieces */
+ for (argc = 0, cp = program; ;cp = NULL) {
+ if (!(argv[argc++] = strtok(cp, " \t\n"))) {
+ break;
+ }
+ if (argc >= argv_size) {
+ char **tmp;
+
+ tmp = (char **)realloc(argv,
+ (argv_size + ARGV_INC) * sizeof (char *));
+ if (tmp == NULL) {
+ (void) close(pdes[0]);
+ (void) close(pdes[1]);
+ return (NULL);
+ } else {
+ argv = tmp;
+ argv_size += ARGV_INC;
+ }
+ }
+ }
+
+ /* glob each piece */
+ gargv[0] = argv[0];
+ for (gargc = argc = 1; argv[argc]; argc++) {
+ if (!(pop = ftpglob(argv[argc])) || globerr != NULL) { /* globbing failed */
+ if (pop) {
+ blkfree(pop);
+ free((char *) pop);
+ }
+ vv[0] = strspl(argv[argc], "");
+ vv[1] = NULL;
+ pop = copyblk(vv);
+ }
+ argv[argc] = (char *) pop; /* save to free later */
+ while (*pop) {
+ gargv[gargc++] = *pop++;
+ if (gargc >= gargv_size) {
+ char **tmp;
+
+ tmp = (char **)realloc(gargv,
+ (gargv_size + GARGV_INC) * sizeof (char *));
+ if (tmp == NULL) {
+ (void) close(pdes[0]);
+ (void) close(pdes[1]);
+ goto pfree;
+ } else {
+ gargv = tmp;
+ gargv_size += GARGV_INC;
+ }
+ }
+ }
+ }
+ gargv[gargc] = NULL;
+
+#ifdef SIGCHLD
+ (void) signal(SIGCHLD, SIG_DFL);
+#endif
+ switch (popen_pid = vfork()) {
+ case -1: /* error */
+ (void) close(pdes[0]);
+ (void) close(pdes[1]);
+ goto pfree;
+ /* NOTREACHED */
+ case 0: /* child */
+ if (*type == 'r') {
+ if (pdes[1] != 1) {
+ dup2(pdes[1], 1);
+ if (closestderr) {
+ (void) close(2);
+ /* stderr output is written to fd 2, so make sure it isn't
+ * available to be assigned to another file */
+ if ((devnullfd = open(_PATH_DEVNULL, O_RDWR)) != -1) {
+ if (devnullfd != 2) {
+ dup2(devnullfd, 2);
+ (void) close(devnullfd);
+ }
+ }
+ }
+ else
+ dup2(pdes[1], 2); /* stderr, too! */
+ (void) close(pdes[1]);
+ }
+ (void) close(pdes[0]);
+ }
+ else {
+ if (pdes[0] != 0) {
+ dup2(pdes[0], 0);
+ (void) close(pdes[0]);
+ }
+ (void) close(pdes[1]);
+ }
+ closefds(3);
+ /* begin CERT suggested fixes */
+ close(0);
+ i = geteuid();
+ delay_signaling(); /* we can't allow any signals while euid==0: kinch */
+ seteuid(0);
+ setgid(getegid());
+ setuid(i);
+ enable_signaling(); /* we can allow signals once again: kinch */
+ /* end CERT suggested fixes */
+ execv(gargv[0], gargv);
+ perror(gargv[0]);
+ _exit(1);
+ }
+ /* parent; assume fdopen can't fail... */
+ if (*type == 'r') {
+ iop = fdopen(pdes[0], type);
+ (void) close(pdes[1]);
+ }
+ else {
+ iop = fdopen(pdes[1], type);
+ (void) close(pdes[0]);
+ }
+ popen_fd = fileno(iop);
+
+ pfree:for (argc = 1; argv[argc]; argc++) {
+ blkfree((char **) argv[argc]);
+ free((char *) argv[argc]);
+ }
+ return (iop);
+}
+
+int ftpd_pclose(FILE *iop)
+{
+ pid_t pid;
+#if defined(HAVE_SIGPROCMASK) || (defined(SVR4) && !defined(AUTOCONF))
+ sigset_t sig, omask;
+ int stat_loc;
+ sigemptyset(&sig);
+ sigaddset(&sig, SIGINT);
+ sigaddset(&sig, SIGQUIT);
+ sigaddset(&sig, SIGHUP);
+#elif defined (_OSF_SOURCE)
+ int omask;
+ int status;
+#else
+ int omask;
+ union wait stat_loc;
+#endif
+
+ /* pclose returns -1 if stream is not associated with a `popened'
+ * command, or, if already `pclosed'. */
+ if ((popen_fd == -1) || (popen_fd != fileno(iop)))
+ return (-1);
+ (void) fclose(iop);
+#if defined(HAVE_SIGPROCMASK) || (!defined(AUTOCONF) && defined(SVR4))
+ sigprocmask(SIG_BLOCK, &sig, &omask);
+#else
+ omask = sigblock(sigmask(SIGINT) | sigmask(SIGQUIT) | sigmask(SIGHUP));
+#endif
+
+#if (!defined(HAVE_SIGPROCMASK) || (!defined(SVR4) && !defined(AUTOCONF))) && defined (_OSF_SOURCE)
+ while ((pid = wait(&status)) != popen_pid && pid != -1);
+#elif ! defined(NeXT)
+ while ((pid = wait((int *) &stat_loc)) != popen_pid && pid != -1);
+#else
+ while ((pid = wait(&stat_loc)) != popen_pid && pid != -1);
+#endif
+ popen_pid = -1;
+ popen_fd = -1;
+#ifdef SIGCHLD
+ (void) signal(SIGCHLD, SIG_IGN);
+#endif
+#if defined(HAVE_SIGPROCMASK) || (defined(SVR4) && !defined(AUTOCONF))
+ sigprocmask(SIG_SETMASK, &omask, (sigset_t *) NULL);
+ return (pid == -1 ? -1 : WEXITSTATUS(stat_loc));
+#else
+ (void) sigsetmask(omask);
+#ifdef _OSF_SOURCE
+ return (pid == -1 ? -1 : status);
+#elif defined(LINUX)
+ return (pid == -1 ? -1 : WEXITSTATUS(stat_loc));
+#else
+ return (pid == -1 ? -1 : stat_loc.w_status);
+#endif
+#endif
+}
+
+#ifdef CLOSEFROM
+void closefds(int startfd)
+{
+ closefrom(startfd);
+}
+#else
+
+#ifdef HAVE_GETRLIMIT
+#include <sys/resource.h>
+#endif
+
+void closefds(int startfd)
+{
+ int i, fds;
+#ifdef HAVE_GETRLIMIT
+ struct rlimit rlp;
+#endif
+
+#ifdef OPEN_MAX
+ fds = OPEN_MAX;
+#else
+ fds = 31;
+#endif
+
+#ifdef HAVE_GETRLIMIT
+ if ((getrlimit(RLIMIT_NOFILE, &rlp) == 0) &&
+ (rlp.rlim_cur != RLIM_INFINITY)) {
+ fds = rlp.rlim_cur;
+ }
+#else
+#ifdef HAVE_GETDTABLESIZE
+ if ((i = getdtablesize()) > 0)
+ fds = i;
+#else
+#ifdef HAVE_SYSCONF
+ fds = sysconf(_SC_OPEN_MAX);
+#endif /* HAVE_SYSCONF */
+#endif /* HAVE_GETDTABLESIZE */
+#endif /* HAVE_GETRLIMIT */
+
+ for (i = startfd; i < fds; i++)
+ close(i);
+}
+#endif /* CLOSEFROM */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/private.c b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/private.c
new file mode 100644
index 0000000000..58a782537b
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/private.c
@@ -0,0 +1,335 @@
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/****************************************************************************
+ Copyright (c) 1999,2000 WU-FTPD Development Group.
+ All rights reserved.
+
+ Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994
+ The Regents of the University of California.
+ Portions Copyright (c) 1993, 1994 Washington University in Saint Louis.
+ Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc.
+ Portions Copyright (c) 1989 Massachusetts Institute of Technology.
+ Portions Copyright (c) 1998 Sendmail, Inc.
+ Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P. Allman.
+ Portions Copyright (c) 1997 by Stan Barber.
+ Portions Copyright (c) 1997 by Kent Landfield.
+ Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ Free Software Foundation, Inc.
+
+ Use and distribution of this software and its source code are governed
+ by the terms and conditions of the WU-FTPD Software License ("LICENSE").
+
+ If you did not receive a copy of the license, it may be obtained online
+ at http://www.wu-ftpd.org/license.html.
+
+ $Id: private.c,v 1.12 2000/07/01 18:17:39 wuftpd Exp $
+
+****************************************************************************/
+#include "config.h"
+
+#ifndef NO_PRIVATE
+
+#include <stdio.h>
+#include <errno.h>
+
+extern char *strsep(char **, const char *);
+
+#include <string.h>
+#ifdef HAVE_SYS_SYSLOG_H
+#include <sys/syslog.h>
+#endif
+#if defined(HAVE_SYSLOG_H) || (!defined(AUTOCONF) && !defined(HAVE_SYS_SYSLOG_H))
+#include <syslog.h>
+#endif
+#include <grp.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+
+#ifdef HAVE_PATHS_H
+#include <paths.h>
+#endif
+#include "pathnames.h"
+#include "extensions.h"
+#include "proto.h"
+
+#ifdef SECUREOSF
+#define SecureWare /* Does this mean it works for all SecureWare? */
+#endif
+
+#ifdef HPUX_10_TRUSTED
+#include <hpsecurity.h>
+#endif
+
+#if defined(SecureWare) || defined(HPUX_10_TRUSTED)
+#include <prot.h>
+#endif
+
+#ifndef NO_CRYPT_PROTO
+extern char *crypt(const char *, const char *);
+#endif
+
+static int group_attempts, group_given;
+static char *groupname, *passbuf;
+
+struct acgrp {
+ char *gname; /* access group name */
+ char *gpass; /* access group password */
+ gid_t gr_gid; /* group to setegid() to */
+ struct acgrp *next;
+};
+
+static struct acgrp *privptr, *privtail;
+
+extern int lgi_failure_threshold;
+extern char remoteident[];
+
+static void add_acgrp(char *gname, char *gpass, gid_t gid)
+{
+ struct acgrp *aptr;
+
+ aptr = (struct acgrp *) calloc(1, sizeof(struct acgrp));
+ if (aptr == NULL) {
+ syslog(LOG_ERR, "calloc error in add_acgrp");
+ dologout(1);
+ }
+
+ /* add element to end of list */
+ if (privtail)
+ privtail->next = aptr;
+ privtail = aptr;
+ if (!privptr)
+ privptr = aptr;
+
+ aptr->gname = strdup(gname);
+ if (aptr->gname == NULL) {
+ syslog(LOG_ERR, "malloc error in add_acgrp");
+ dologout(1);
+ }
+ if (gpass == NULL)
+ aptr->gpass = strdup("");
+ else
+ aptr->gpass = strdup(gpass);
+ if (aptr->gpass == NULL) {
+ syslog(LOG_ERR, "malloc error in add_acgrp");
+ dologout(1);
+ }
+ aptr->gr_gid = gid;
+}
+
+static void parsepriv(void)
+{
+ char *ptr;
+ char *acptr = passbuf, *line;
+ char *argv[3], *p, *val;
+ struct group *gr;
+ int n;
+
+ if (!passbuf || !(*passbuf))
+ return;
+
+ /* read through passbuf, stripping comments. */
+ while (*acptr != '\0') {
+ line = acptr;
+ while (*acptr && *acptr != '\n')
+ acptr++;
+ *acptr++ = '\0';
+
+ /* deal with comments */
+ if ((ptr = strchr(line, '#')) != NULL)
+ *ptr = '\0';
+
+ if (*line == '\0')
+ continue;
+
+ /* parse the lines... */
+ for (n = 0, p = line; n < 3 && p != NULL; n++) {
+ val = (char *) strsep(&p, ":\n");
+ argv[n] = val;
+ if ((argv[n][0] == ' ') || (argv[n][0] == '\0'))
+ argv[n] = NULL;
+ }
+ /* check their were 3 fields, if not skip the line... */
+ if (n != 3 || p != NULL)
+ continue;
+
+ if (argv[0] && argv[2]) {
+ if (argv[2][0] == '%') {
+ gid_t gid = atoi(argv[2] + 1);
+ if ((gr = getgrgid(gid)) != NULL)
+ add_acgrp(argv[0], argv[1], gid);
+ }
+ else {
+ if ((gr = getgrnam((char *) argv[2])) != NULL)
+ add_acgrp(argv[0], argv[1], gr->gr_gid);
+ }
+ endgrent();
+ }
+ }
+}
+
+/*************************************************************************/
+/* FUNCTION : priv_setup */
+/* PURPOSE : Set things up to use the private access password file. */
+/* ARGUMENTS : path, the path to the private access password file */
+/*************************************************************************/
+
+void priv_setup(char *path)
+{
+ FILE *prvfile;
+ struct stat finfo;
+ struct acgrp *aptr;
+
+ while (privptr) {
+ aptr = privptr->next;
+ free(privptr->gname);
+ free(privptr->gpass);
+ free(privptr);
+ privptr = aptr;
+ }
+ privtail = NULL;
+
+ if (passbuf) {
+ free(passbuf);
+ passbuf = NULL;
+ }
+
+ if ((prvfile = fopen(path, "r")) == NULL) {
+ if (errno != ENOENT)
+ syslog(LOG_ERR, "cannot open private access file %s: %s",
+ path, strerror(errno));
+ return;
+ }
+ if (fstat(fileno(prvfile), &finfo) != 0) {
+ syslog(LOG_ERR, "cannot fstat private access file %s: %s", path,
+ strerror(errno));
+ (void) fclose(prvfile);
+ return;
+ }
+ if (finfo.st_size == 0) {
+ passbuf = (char *) calloc(1, 1);
+ }
+ else {
+ if (!(passbuf = (char *) malloc((size_t) finfo.st_size + 1))) {
+ (void) syslog(LOG_ERR, "could not malloc passbuf (%d bytes)",
+ (size_t) finfo.st_size + 1);
+ (void) fclose(prvfile);
+ return;
+ }
+ if (!fread(passbuf, (size_t) finfo.st_size, 1, prvfile)) {
+ (void) syslog(LOG_ERR, "error reading private access file %s: %s",
+ path, strerror(errno));
+ (void) fclose(prvfile);
+ return;
+ }
+ *(passbuf + finfo.st_size) = '\0';
+ }
+ (void) fclose(prvfile);
+ (void) parsepriv();
+}
+
+/*************************************************************************/
+/* FUNCTION : priv_getent */
+/* PURPOSE : Retrieve an entry from the in-memory copy of the group */
+/* access file. */
+/* ARGUMENTS : pointer to group name */
+/*************************************************************************/
+
+static struct acgrp *priv_getent(char *group)
+{
+ struct acgrp *ptr;
+
+ for (ptr = privptr; ptr; ptr = ptr->next)
+ if (!strcasecmp(group, ptr->gname))
+ return (ptr);
+
+ return (NULL);
+}
+
+/*************************************************************************/
+/* FUNCTION : priv_group */
+/* PURPOSE : */
+/* ARGUMENTS : */
+/*************************************************************************/
+
+void priv_group(char *group)
+{
+ if (groupname)
+ free(groupname);
+
+ groupname = strdup(group);
+ if (groupname == NULL) {
+ reply(421, "Local resource failure: malloc");
+ syslog(LOG_ERR, "malloc error in priv_group");
+ dologout(1);
+ }
+ group_given = 1;
+ reply(200, "Request for access to group %s accepted.", group);
+}
+
+/*************************************************************************/
+/* FUNCTION : priv_gpass */
+/* PURPOSE : validate the group access request, and if OK place user */
+/* in the proper group. */
+/* ARGUMENTS : group access password */
+/*************************************************************************/
+
+void priv_gpass(char *gpass)
+{
+ char *xgpass = NULL;
+ struct acgrp *grp;
+ uid_t uid;
+
+ if (group_given == 0) {
+ reply(503, "Give group name with SITE GROUP first.");
+ return;
+ }
+ /* OK, now they're getting a chance to specify a password. Make them
+ * give the group name again if they fail... */
+ group_given = 0;
+
+ grp = priv_getent(groupname);
+ if (passbuf && gpass && *gpass != '\0' && grp && *grp->gpass != '\0')
+#if defined(SecureWare) || defined(HPUX_10_TRUSTED)
+ xgpass = bigcrypt(gpass, grp->gpass);
+#else
+ xgpass = crypt(gpass, grp->gpass);
+#endif
+
+ if (!(((gpass != NULL)
+ && (*gpass != '\0')
+ && (grp != NULL)
+ && (*grp->gpass != '\0')
+ && (strcmp(xgpass, grp->gpass) == 0))
+ || (((gpass == NULL)
+ || (*gpass == '\0'))
+ && (grp != NULL)
+ && (*grp->gpass == '\0'))
+ )) {
+ reply(530, "Group access request incorrect.");
+ grp = NULL;
+ if (++group_attempts >= lgi_failure_threshold) {
+ syslog(LOG_NOTICE,
+ "repeated group access failures from %s, group %s",
+ remoteident, groupname);
+ dologout(0);
+ }
+ sleep(group_attempts); /* slow down password crackers */
+ return;
+ }
+
+ uid = geteuid();
+ setid_priv_on(0);
+ setegid(grp->gr_gid);
+ setid_priv_off(uid);
+
+ reply(200, "Group access enabled.");
+ group_attempts = 0;
+}
+#endif /* !NO_PRIVATE */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/privatepw.c b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/privatepw.c
new file mode 100644
index 0000000000..cd574ace35
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/privatepw.c
@@ -0,0 +1,392 @@
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/****************************************************************************
+
+ Copyright (c) 1999,2000 WU-FTPD Development Group.
+ All rights reserved.
+
+ Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994
+ The Regents of the University of California. Portions Copyright (c)
+ 1993, 1994 Washington University in Saint Louis. Portions Copyright
+ (c) 1996, 1998 Berkeley Software Design, Inc. Portions Copyright (c)
+ 1998 Sendmail, Inc. Portions Copyright (c) 1983, 1995, 1996, 1997 Eric
+ P. Allman. Portions Copyright (c) 1989 Massachusetts Institute of
+ Technology. Portions Copyright (c) 1997 by Stan Barber. Portions
+ Copyright (C) 1991, 1992, 1993, 1994, 1995, 1996, 1997 Free Software
+ Foundation, Inc. Portions Copyright (c) 1997 by Kent Landfield.
+
+ Use and distribution of this software and its source code are governed
+ by the terms and conditions of the WU-FTPD Software License ("LICENSE").
+
+ $Id: privatepw.c,v 1.10 2000/07/01 18:43:59 wuftpd Exp $
+
+****************************************************************************/
+/*
+ Subsystem: WU-FTPD FTP Server
+ Purpose: Change WU-FTPD Guest Passwords
+ File Name: privatepw.c
+
+ usage: privatepw [-c] [-f passwordfile] [-g group] accessgroup
+ privatepw [-d] [-f passwordfile] accessgroup
+ privatepw [-l] [-f passwordfile]
+ -c: creates a new file.
+ -d: deletes specified accessgroup.
+ -l: list contents of ftpgroups file.
+ -f ftpgroups: updates the specified file.
+ -g group: set real group to the specified group.
+
+ This software was initially written by Kent Landfield (kent@landfield.com)
+ */
+
+#include <sys/types.h>
+#include <sys/signal.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <grp.h>
+#include <unistd.h>
+#include "config.h"
+#include "pathnames.h"
+
+#define BUFLEN 256
+#define GROUPLEN 8
+
+char *tmp;
+char line[BUFLEN];
+FILE *fp;
+int verbose = 0;
+
+static unsigned char itoa64[] = /* 0 ... 63 => ascii - 64 */
+"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+
+void print_copyright(void);
+
+static void usage(void)
+{
+ fprintf(stderr, "usage: privatepw [-c] [-f ftpgroups] [-g group] accessgroup\n");
+ fprintf(stderr, " privatepw [-d] [-f ftpgroups] accessgroup\n");
+ fprintf(stderr, " privatepw [-l] [-f ftpgroups]\n");
+ fprintf(stderr, "\t\t-c: creates a new file.\n");
+ fprintf(stderr, "\t\t-d: deletes specified accessgroup.\n");
+ fprintf(stderr, "\t\t-l: list contents of ftpgroups file.\n");
+ fprintf(stderr, "\t\t-f ftpgroups: updates the specified file.\n");
+ fprintf(stderr, "\t\t-g group: set real group to the specified group.\n");
+ exit(1);
+}
+
+static void to64(register char *s, register long v, register int n)
+{
+ while (--n >= 0) {
+ *s++ = itoa64[v & 0x3f];
+ v >>= 6;
+ }
+}
+
+static void terminate(void)
+{
+ if (tmp)
+ unlink(tmp);
+ exit(1);
+}
+
+static void catchintr(void)
+{
+ fprintf(stderr, "Interrupted.\n");
+ terminate();
+}
+
+static char *savit(char *s)
+{
+ char *d;
+
+ if ((d = (char *) malloc(strlen(s) + 1)) == NULL) {
+ fprintf(stderr, "Whoa... Malloc failed.\n");
+ terminate();
+ }
+ strcpy(d, s);
+ return (d);
+}
+
+static int confirmed(char *accessgroup)
+{
+ register int ch;
+
+ printf("Delete %s: Are your sure ? (y/n) ", accessgroup);
+ ch = getc(stdin);
+ if (ch == 'y')
+ return (1);
+ return (0);
+}
+
+static char *getgroup(char *msg)
+{
+ register int ch;
+ register char *p;
+ static char buf[GROUPLEN + 1];
+
+ fputs(msg, stderr);
+ rewind(stderr); /* implied flush */
+ for (p = buf; (ch = getc(stdin)) != EOF && ch != '\n';)
+ if (p < buf + GROUPLEN)
+ *p++ = ch;
+ *p = '\0';
+
+ if (getgrnam(buf) == NULL) {
+ fprintf(stderr, "Invalid group \'%s\' specified\n", buf);
+ terminate();
+ }
+ return (buf);
+}
+
+static void addrecord(char *accessgroup, char *sysgroup, char *msg, FILE *f)
+{
+ char *pw, *cpw, salt[3];
+#ifndef NO_CRYPT_PROTO
+ extern char *crypt(const char *, const char *);
+#endif
+ char *getpass(const char *prompt);
+
+ printf("%s %s\n", msg, accessgroup);
+
+ if (sysgroup[0] == '\0')
+ strcpy(sysgroup, getgroup("Real System Group to use: "));
+
+ pw = savit((char *) getpass("New password: "));
+ if (strcmp(pw, (char *) getpass("Re-type new password: "))) {
+ fprintf(stderr, "They don't match, sorry.\n");
+ if (tmp)
+ unlink(tmp);
+ exit(1);
+ }
+
+ srand((int) time((time_t *) NULL));
+ to64(&salt[0], rand(), 2);
+ cpw = crypt(pw, salt);
+ free(pw);
+ fprintf(f, "%s:%s:%s\n", accessgroup, cpw, sysgroup);
+}
+
+static void list_privatefile(char *privatefile)
+{
+ if (verbose)
+ fprintf(stderr, "Private File: %s file.\n", privatefile);
+
+ if ((fp = fopen(privatefile, "r")) == NULL) {
+ fprintf(stderr, "Could not open %s file.\n", privatefile);
+ exit(1);
+ }
+
+ printf("\nWU-FTPD Private file: %s\n", privatefile);
+ printf("accessgroup : password : system group\n");
+ printf("-------\n");
+
+ while (fgets(line, BUFLEN, fp) != NULL)
+ fputs(line, stdout);
+ printf("-------\n");
+}
+
+int main(int argc, char **argv)
+{
+ extern void (*signal(int sig, void (*disp) (int))) (int);
+ extern int getopt(int argc, char *const *argv, const char *optstring);
+ extern char *optarg;
+ extern int optind;
+ extern int opterr;
+
+ struct stat stbuf;
+
+ char realgroup[BUFLEN];
+ char *passwdpath;
+ char *cp;
+
+ char accessgroup[BUFLEN];
+ char w[BUFLEN];
+ char command[BUFLEN];
+
+ int create;
+ int delete;
+ int list;
+ int found;
+ int lineno;
+ int c;
+
+ FILE *tfp;
+
+#ifdef HAVE_MKSTEMP
+ char tmpname[BUFLEN];
+ int tfd;
+#endif
+
+ opterr = 0;
+ create = 0;
+ delete = 0;
+ list = 0;
+
+ tmp = NULL;
+ realgroup[0] = '\0';
+
+ passwdpath = _PATH_PRIVATE;
+
+ if (argc == 1)
+ usage();
+
+ while ((c = getopt(argc, argv, "Vvcdf:g:l")) != EOF) {
+ switch (c) {
+ case 'd':
+ delete++;
+ break;
+ case 'c':
+ create++;
+ break;
+ case 'f':
+ passwdpath = optarg;
+ break;
+ case 'g':
+ strcpy(realgroup, optarg);
+ if (getgrnam(realgroup) == NULL) {
+ fprintf(stderr, "Invalid group \'%s\' specified\n", realgroup);
+ return (1);
+ }
+ break;
+ case 'l':
+ list++;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case 'V':
+ print_copyright();
+ return (0);
+ /* NOTREACHED */
+ default:
+ usage();
+ }
+ }
+
+ if (list) {
+ list_privatefile(passwdpath);
+ return (0);
+ }
+
+ if (optind >= argc) {
+ fprintf(stderr, "Need to specify an accessgroup name.\n");
+ usage();
+ }
+
+ signal(SIGINT, (void (*)()) catchintr);
+
+ strcpy(accessgroup, argv[optind]);
+
+ if (create) {
+ if (stat(passwdpath, &stbuf) == 0) {
+ fprintf(stderr, "%s exists, cannot create it.\n", passwdpath);
+ fprintf(stderr, "Remove -c option or use the -f option to specify another.\n");
+ return (1);
+ }
+
+ if ((tfp = fopen(passwdpath, "w")) == NULL) {
+ fprintf(stderr, "Could not open \"%s\" for writing.\n", passwdpath);
+ perror("fopen");
+ return (1);
+ }
+
+ tmp = passwdpath;
+
+ printf("Creating WU-FTPD Private file: %s\n", passwdpath);
+ addrecord(accessgroup, realgroup, "Adding accessgroup", tfp);
+
+ fclose(tfp);
+ return (0);
+ }
+
+#ifdef HAVE_MKSTEMP
+ strcpy (tmpname, "/tmp/privatepwXXXXXX");
+ tmp = tmpname;
+ if ((tfd = mkstemp(tmp)) < 0) {
+ fprintf(stderr, "Could not open temp file.\n");
+ return (1);
+ }
+
+ if ((tfp = fdopen(tfd, "w")) == NULL) {
+ unlink(tmp);
+ fprintf(stderr, "Could not open temp file.\n");
+ return (1);
+ }
+#else
+ tmp = tmpnam(NULL);
+
+ if ((tfp = fopen(tmp, "w")) == NULL) {
+ fprintf(stderr, "Could not open temp file.\n");
+ return (1);
+ }
+#endif
+
+ if ((fp = fopen(passwdpath, "r")) == NULL) {
+ fprintf(stderr, "Could not open %s file.\n", passwdpath);
+ fprintf(stderr, "Use -c option to create new one.\n");
+ return (1);
+ }
+
+ lineno = 0;
+ found = 0;
+
+ while (fgets(line, BUFLEN, fp) != NULL) {
+ lineno++;
+
+ if (found || (line[0] == '#') || (!line[0])) {
+ fputs(line, tfp);
+ continue;
+ }
+
+ strcpy(w, line);
+
+ if ((cp = strchr(w, ':')) == NULL) {
+ fprintf(stderr, "%s: line %d: invalid record format.\n", passwdpath, lineno);
+ continue;
+ }
+ *cp++ = '\0';
+
+ if ((cp = strchr(cp, ':')) == NULL) {
+ fprintf(stderr, "%s: line %d: invalid record format.\n", passwdpath, lineno);
+ continue;
+ }
+ *cp++ = '\0';
+
+ if (strcmp(accessgroup, w)) {
+ fputs(line, tfp);
+ continue;
+ }
+ else {
+ if (delete) {
+ if (!confirmed(accessgroup))
+ terminate();
+ }
+ else {
+ if (realgroup[0] == '\0') {
+ strcpy(realgroup, cp);
+ if ((cp = strchr(realgroup, '\n')) != NULL)
+ *cp = '\0';
+ }
+ addrecord(accessgroup, realgroup, "Updating accessgroup", tfp);
+ }
+ found = 1;
+ }
+ }
+
+ if (!found && !delete)
+ addrecord(accessgroup, realgroup, "Adding accessgroup", tfp);
+ else if (!found && delete) {
+ fprintf(stderr, "%s not found in %s.\n", accessgroup, passwdpath);
+ terminate();
+ }
+
+ fclose(fp);
+ fclose(tfp);
+
+ sprintf(command, "cp %s %s", tmp, passwdpath);
+ system(command);
+ unlink(tmp);
+ return (0);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/privs.c b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/privs.c
new file mode 100644
index 0000000000..400a9ffec0
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/privs.c
@@ -0,0 +1,264 @@
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Least privilege support functions.
+ */
+
+#include "config.h"
+
+#ifdef SOLARIS_PRIVS
+#include <priv.h>
+#ifdef HAVE_SYS_SYSLOG_H
+#include <sys/syslog.h>
+#endif
+#if defined(HAVE_SYSLOG_H) || (!defined(AUTOCONF) && !defined(HAVE_SYS_SYSLOG_H))
+#include <syslog.h>
+#endif
+#endif /* SOLARIS_PRIVS */
+
+#include "proto.h"
+
+#ifdef SOLARIS_PRIVS
+/*
+ * Before becoming privilege aware in init_privs(), no explicit privilege
+ * manipulation using priv_on()/priv_off() is necessary as seteuid(0) sets
+ * the effective privilege set to the limit set. Thus these are all
+ * initialized to TRUE.
+ */
+static boolean_t got_setid_priv = B_TRUE;
+static boolean_t got_privaddr_priv = B_TRUE;
+static boolean_t got_read_priv = B_TRUE;
+static boolean_t got_search_priv = B_TRUE;
+static boolean_t got_chown_priv = B_TRUE;
+#endif /* SOLARIS_PRIVS */
+
+#ifdef SOLARIS_PRIVS
+#ifdef PRIVS_DEBUG
+static void print_privs(priv_ptype_t which, const char *str)
+{
+ priv_set_t *privset;
+ char *privstr;
+
+ if ((privset = priv_allocset()) == NULL)
+ return;
+
+ (void) getppriv(which, privset);
+ privstr = priv_set_to_str(privset, ',', PRIV_STR_SHORT);
+ syslog(LOG_DEBUG, "%s: %s", str, privstr);
+ free(privstr);
+ priv_freeset(privset);
+}
+#endif /* PRIVS_DEBUG */
+
+static void priv_on(const char *priv, boolean_t already_have)
+{
+ /* no need to add the privilege if already have it */
+ if (already_have)
+ return;
+
+ if (priv_set(PRIV_ON, PRIV_EFFECTIVE, priv, NULL) == -1)
+ syslog(LOG_ERR, "priv_set: error adding privilege %s: %m", priv);
+}
+
+static void priv_off(const char *priv, boolean_t already_had)
+{
+ /* don't remove the privilege if already had it */
+ if (already_had)
+ return;
+
+ if (priv_set(PRIV_OFF, PRIV_EFFECTIVE, priv, NULL) == -1)
+ syslog(LOG_ERR, "priv_set: error removing privilege %s: %m", priv);
+}
+#endif /* SOLARIS_PRIVS */
+
+/*
+ * init_privs() is called after a user has logged in to drop from the
+ * permitted privilege set those privileges which are no longer required.
+ */
+/*ARGSUSED*/
+void init_privs(const char *username)
+{
+#ifdef SOLARIS_PRIVS
+ uid_t euid = geteuid();
+ priv_set_t *privset;
+
+ /*
+ * The FTP server runs with "basic" inheritable privileges, which are
+ * reset in pam_setcred() for non anonymous users. The seteuid() call in
+ * pass() sets the effective privileges to the inheritable privileges.
+ */
+ if ((privset = priv_allocset()) == NULL) {
+ syslog(LOG_ERR, "priv_allocset failed: %m");
+ dologout(1);
+ }
+ if (getppriv(PRIV_EFFECTIVE, privset) == -1) {
+ syslog(LOG_ERR, "getppriv(effective) failed: %m");
+ dologout(1);
+ }
+
+ /*
+ * Set the permitted privilege set to the effective privileges plus
+ * those required after init_privs() is called. Keep note of which
+ * effective privileges we already had so we don't turn them off.
+ */
+ if (!priv_ismember(privset, PRIV_PROC_SETID)) {
+ got_setid_priv = B_FALSE;
+ (void) priv_addset(privset, PRIV_PROC_SETID);
+ }
+ if (!priv_ismember(privset, PRIV_NET_PRIVADDR)) {
+ got_privaddr_priv = B_FALSE;
+ (void) priv_addset(privset, PRIV_NET_PRIVADDR);
+ }
+ if (!priv_ismember(privset, PRIV_FILE_DAC_READ)) {
+ got_read_priv = B_FALSE;
+ (void) priv_addset(privset, PRIV_FILE_DAC_READ);
+ }
+ if (!priv_ismember(privset, PRIV_FILE_DAC_SEARCH)) {
+ got_search_priv = B_FALSE;
+ (void) priv_addset(privset, PRIV_FILE_DAC_SEARCH);
+ }
+ if (!priv_ismember(privset, PRIV_FILE_CHOWN)) {
+ got_chown_priv = B_FALSE;
+ (void) priv_addset(privset, PRIV_FILE_CHOWN);
+ }
+#if defined(SOLARIS_BSM_AUDIT) && !defined(SOLARIS_NO_AUDIT_FTPD_LOGOUT)
+ /* needed for audit_ftpd_logout() */
+ (void) priv_addset(privset, PRIV_PROC_AUDIT);
+#endif
+ if (setppriv(PRIV_SET, PRIV_PERMITTED, privset) == -1) {
+ syslog(LOG_ERR,
+ "unable to set privileges for %s: setppriv(permitted): %m",
+ username);
+ dologout(1);
+ }
+ /*
+ * setppriv() has made us privilege aware, so the effective privileges
+ * are no longer modified by user ID changes.
+ */
+
+ priv_freeset(privset);
+
+ /* set the real, effective and saved group ID's */
+ setid_priv_on(0);
+ if (setgid(getegid()) != 0) {
+ syslog(LOG_ERR, "setgid(%d) failed: %m", getegid());
+ setid_priv_off(euid);
+ dologout(1);
+ }
+ /*
+ * Set the real and effective user ID's, leaving the saved user ID set
+ * to 0 so seteuid(0) succeeds.
+ */
+ (void) seteuid(0);
+ if (setreuid(euid, -1) != 0) {
+ syslog(LOG_ERR, "setreuid(%d, -1) failed: %m", euid);
+ setid_priv_off(euid);
+ dologout(1);
+ }
+ setid_priv_off(euid);
+ if (seteuid(euid) != 0) {
+ syslog(LOG_ERR, "seteuid(%d) failed: %m", euid);
+ dologout(1);
+ }
+
+#ifdef PRIVS_DEBUG
+ print_privs(PRIV_EFFECTIVE, "effective privilege set");
+ print_privs(PRIV_PERMITTED, "permitted privilege set");
+ print_privs(PRIV_INHERITABLE, "inheritable privilege set");
+ print_privs(PRIV_LIMIT, "limit privilege set");
+#endif /* PRIVS_DEBUG */
+#endif /* SOLARIS_PRIVS */
+}
+
+/* allow a process to bind to a privileged port */
+/*ARGSUSED*/
+void port_priv_on(uid_t uid)
+{
+ delay_signaling();
+#ifdef SOLARIS_PRIVS
+ priv_on(PRIV_NET_PRIVADDR, got_privaddr_priv);
+#else
+ (void) seteuid(uid);
+#endif
+}
+
+/*ARGSUSED*/
+void port_priv_off(uid_t uid)
+{
+#ifdef SOLARIS_PRIVS
+ priv_off(PRIV_NET_PRIVADDR, got_privaddr_priv);
+#else
+ (void) seteuid(uid);
+#endif
+ enable_signaling();
+}
+
+/* allow a process to read any file or directory and to search any directory */
+void access_priv_on(uid_t uid)
+{
+ delay_signaling();
+#ifdef SOLARIS_PRIVS
+ priv_on(PRIV_FILE_DAC_READ, got_read_priv);
+ priv_on(PRIV_FILE_DAC_SEARCH, got_search_priv);
+#endif
+ /* necessary on Solaris for access over NFS */
+ (void) seteuid(uid);
+}
+
+void access_priv_off(uid_t uid)
+{
+#ifdef SOLARIS_PRIVS
+ priv_off(PRIV_FILE_DAC_READ, got_read_priv);
+ priv_off(PRIV_FILE_DAC_SEARCH, got_search_priv);
+#endif
+ (void) seteuid(uid);
+ enable_signaling();
+}
+
+/* allow a process to set its user IDs and group IDs */
+/*ARGSUSED*/
+void setid_priv_on(uid_t uid)
+{
+ delay_signaling();
+#ifdef SOLARIS_PRIVS
+ priv_on(PRIV_PROC_SETID, got_setid_priv);
+#else
+ (void) seteuid(uid);
+#endif
+}
+
+/*ARGSUSED*/
+void setid_priv_off(uid_t uid)
+{
+#ifdef SOLARIS_PRIVS
+ priv_off(PRIV_PROC_SETID, got_setid_priv);
+#else
+ (void) seteuid(uid);
+#endif
+ enable_signaling();
+}
+
+/* allow a process to change the ownership of files and directories */
+void chown_priv_on(uid_t uid)
+{
+ delay_signaling();
+#ifdef SOLARIS_PRIVS
+ priv_on(PRIV_FILE_CHOWN, got_chown_priv);
+#endif
+ /* necessary on Solaris for chown over NFS */
+ (void) seteuid(uid);
+}
+
+void chown_priv_off(uid_t uid)
+{
+#ifdef SOLARIS_PRIVS
+ priv_off(PRIV_FILE_CHOWN, got_chown_priv);
+#endif
+ (void) seteuid(uid);
+ enable_signaling();
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/proto.h b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/proto.h
new file mode 100644
index 0000000000..1e264ad313
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/proto.h
@@ -0,0 +1,346 @@
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/****************************************************************************
+ Copyright (c) 1999,2000 WU-FTPD Development Group.
+ All rights reserved.
+
+ Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994
+ The Regents of the University of California.
+ Portions Copyright (c) 1993, 1994 Washington University in Saint Louis.
+ Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc.
+ Portions Copyright (c) 1989 Massachusetts Institute of Technology.
+ Portions Copyright (c) 1998 Sendmail, Inc.
+ Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P. Allman.
+ Portions Copyright (c) 1997 by Stan Barber.
+ Portions Copyright (c) 1997 by Kent Landfield.
+ Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ Free Software Foundation, Inc.
+
+ Use and distribution of this software and its source code are governed
+ by the terms and conditions of the WU-FTPD Software License ("LICENSE").
+
+ If you did not receive a copy of the license, it may be obtained online
+ at http://www.wu-ftpd.org/license.html.
+
+ $Id: proto.h,v 1.10 2000/07/01 18:17:39 wuftpd Exp $
+
+****************************************************************************/
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <pwd.h>
+#include <grp.h>
+#include <setjmp.h>
+#include <netdb.h>
+#include <errno.h>
+#include <string.h>
+#include <stdarg.h>
+#include <netinet/in.h>
+
+/*
+ ** access.c
+ */
+int parsetime(char *);
+int validtime(char *);
+int hostmatch(char *, char *, char *);
+int acl_guestgroup(struct passwd *);
+int acl_realgroup(struct passwd *);
+void acl_autogroup(struct passwd *);
+void acl_setfunctions(void);
+int acl_getclass(char *);
+int acl_getlimit(char *, char *);
+int acl_getnice(char *);
+void acl_getdefumask(char *);
+void acl_tcpwindow(char *);
+void acl_filelimit(char *);
+void acl_datalimit(char *);
+int acl_deny(char *);
+int acl_countusers(char *);
+int acl_join(char *, int);
+void acl_remove(void);
+void pr_mesg(int, char *);
+void access_init(void);
+int access_ok(int);
+
+/*
+ ** acl.c
+ */
+struct aclmember *getaclentry(char *, struct aclmember **);
+void parseacl(void);
+int readacl(char *);
+
+/*
+ ** auth.c
+ */
+#ifdef BSD_AUTH
+char *start_auth(char *, char *, struct passwd *);
+#endif
+char *check_auth(char *, char *);
+
+/*
+ ** authenticate.c
+ */
+int wu_authenticate(void);
+
+/*
+ ** conversions.c
+ */
+void conv_init(void);
+
+/*
+ ** domain.c
+ */
+int check_rhost_reverse(void);
+int check_rhost_matches(void);
+int rhostlookup(char *);
+void set_res_options(void);
+
+/*
+ ** extensions.c
+ */
+#ifdef SITE_NEWER
+int check_newer(const char *, const struct stat *, int);
+void newer(char *date, char *path, int showlots);
+#endif
+long getSize(char *);
+void msg_massage(const char *, char *, size_t);
+int cwd_beenhere(int);
+void show_banner(int);
+void show_message(int, int);
+void show_readme(int, int);
+int deny_badasciixfer(int, char *);
+int is_shutdown(int, int);
+int type_match(char *typelist);
+int path_compare(char *p1, char *p2);
+void expand_id(void);
+int fn_check(char *name);
+int dir_check(char *name, uid_t * uid, gid_t * gid, int *d_mode, int *valid);
+int upl_check(char *name, uid_t * uid, gid_t * gid, int *f_mode, int *valid);
+int del_check(char *name);
+int regexmatch(char *name, char *rgexp);
+int checknoretrieve(char *name);
+int path_to_device(char *pathname, char *result);
+void get_quota(char *fs, int uid);
+char *time_quota(long curstate, long softlimit, long timelimit, char *timeleft);
+void fmttime(char *buf, register long time);
+int file_compare(char *patterns, char *file);
+int remote_compare(char *patterns);
+void throughput_calc(char *name, int *bps, double *bpsmult);
+void throughput_adjust(char *name);
+void SetCheckMethod(const char *method);
+void ShowCheckMethod(void);
+void CheckSum(char *pathname);
+void CheckSumLastFile(void);
+
+/*
+ ** ftpcmd.c
+ */
+char *wu_getline(char *s, int n, register FILE *iop);
+int yyparse(void);
+void upper(char *s);
+char *copy(char *s);
+void sizecmd(char *filename);
+void site_exec(char *cmd);
+void alias(char *s);
+void cdpath(void);
+void print_groups(void);
+
+/*
+ ** ftpd.c
+ */
+
+SIGNAL_TYPE randomsig(int sig);
+SIGNAL_TYPE lostconn(int sig);
+char *mapping_getwd(char *path);
+void do_elem(char *dir);
+int mapping_chdir(char *orig_path);
+char *sgetsave(char *s);
+struct passwd *sgetpwnam(char *name);
+char *skey_challenge(char *name, struct passwd *pwd, int pwok);
+void user(char *name);
+int checkuser(char *name);
+int uid_match(char *keyword, uid_t uid);
+int gid_match(char *keyword, gid_t gid, char *username);
+int denieduid(uid_t uid);
+int alloweduid(uid_t uid);
+int deniedgid(gid_t gid);
+int allowedgid(gid_t gid);
+void end_login(void);
+int validate_eaddr(char *eaddr);
+void pass(char *passwd);
+int restricteduid(uid_t uid);
+int unrestricteduid(uid_t uid);
+int restrictedgid(gid_t gid);
+int unrestrictedgid(gid_t gid);
+char *opt_string(int options);
+void retrieve(char *cmd, char *name);
+void store(char *name, char *mode, int unique);
+FILE *getdatasock(char *mode);
+FILE *dataconn(char *name, off_t size, char *mode);
+#ifdef THROUGHPUT
+int send_data(char *name, FILE *instr, FILE *outstr, size_t blksize);
+#else
+int send_data(FILE *instr, FILE *outstr, size_t blksize);
+#endif
+int receive_data(FILE *instr, FILE *outstr);
+void statfilecmd(char *filename);
+void statcmd(void);
+void fatal(char *s);
+void vreply(long flags, int n, char *fmt, va_list ap);
+void reply(int, char *fmt,...);
+void lreply(int, char *fmt,...);
+void ack(char *s);
+void nack(char *s);
+void yyerror(char *s);
+void delete(char *name);
+void cwd(char *path);
+void makedir(char *name);
+void removedir(char *name);
+void pwd(void);
+char *renamefrom(char *name);
+void renamecmd(char *from, char *to);
+void dologout(int status);
+SIGNAL_TYPE myoob(int sig);
+void passive(int passive_mode, int proto);
+char *gunique(char *local);
+void perror_reply(int code, char *string);
+void send_file_list(char *whichfiles);
+void initsetproctitle(int argc, char **argv, char **envp);
+void setproctitle(const char *fmt,...);
+void init_krb(void);
+void end_krb(void);
+
+#ifdef INTERNAL_LS
+char *rpad(char *s, unsigned int len);
+char *ls_file(const char *file, int nameonly, char remove_path, char classify);
+void ls_dir(char *d, char ls_a, char ls_F, char ls_l, char ls_R, char omit_total, FILE *out);
+void ls(char *file, char nlst);
+#endif
+
+void fixpath(char *path);
+
+/*
+ ** glob.c
+ */
+void blkfree(char **);
+char **ftpglob(register char *);
+char *strspl(register char *, register char *);
+char **copyblk(register char **);
+
+/*
+ ** hostacc.c
+ */
+int rhost_ok(char *pcRuser, char *pcRhost, char *pcRaddr);
+
+/*
+ ** loadavg.c
+ */
+/*
+ ** logwtmp.c
+ */
+void wu_logwtmp(char *line, char *name, char *host, int login);
+
+/*
+ ** paths.c
+ */
+void setup_paths(void);
+
+/*
+ ** popen.c
+ */
+FILE *ftpd_popen(char *program, char *type, int closestderr);
+int ftpd_pclose(FILE *iop);
+void closefds(int startfd);
+
+/*
+ ** private.c
+ */
+#ifndef NO_PRIVATE
+void priv_setup(char *path);
+void priv_group(char *group);
+void priv_gpass(char *gpass);
+#endif
+
+/*
+ ** rdservers.c
+ */
+#ifdef VIRTUAL
+int read_servers_line(FILE *, char *, size_t, char *, size_t);
+#endif
+
+/*
+ ** realpath.c
+ */
+char *fb_realpath(const char *path, char *resolved);
+char *wu_realpath(const char *path, char *resolved_path, char *chroot_path);
+
+/*
+ ** restrict.c
+ */
+int restrict_check(char *name);
+int test_restriction(char *name);
+int restrict_list_check(char *name);
+
+/*
+ ** routevector.c
+ */
+int routevector(void);
+
+/*
+ ** timeout.c
+ */
+void load_timeouts(void);
+
+/*
+ ** inet.c
+ */
+char *inet_htop(const char *hostname);
+char *inet_stop(struct SOCKSTORAGE *ss);
+char *wu_gethostbyname(const char *hostname);
+int wu_gethostbyaddr(struct SOCKSTORAGE *ss, char *hostname, int hostlen);
+int sock_cmp_inaddr(struct SOCKSTORAGE *ss, struct in_addr addr);
+#ifdef INET6
+void sock_set_inaddr(struct SOCKSTORAGE *ss, struct in_addr addr);
+int sock_cmp_addr(struct SOCKSTORAGE *ss1, struct SOCKSTORAGE *ss2);
+void sock_set_scope(struct SOCKSTORAGE *dst, struct SOCKSTORAGE *src);
+int inet_pton6(char *str, struct in6_addr *addr);
+const char *inet_ntop_native(int af, const void *addr, char *dst, size_t size);
+#endif
+
+/*
+ ** xferlog.c
+ */
+void get_xferlog_format(void);
+
+/*
+ ** privs.c
+ */
+void init_privs(const char *);
+void port_priv_on(uid_t);
+void port_priv_off(uid_t);
+void access_priv_on(uid_t);
+void access_priv_off(uid_t);
+void setid_priv_on(uid_t);
+void setid_priv_off(uid_t);
+void chown_priv_on(uid_t);
+void chown_priv_off(uid_t);
+
+/*
+ ** support/getusershell.c
+ */
+char *getusershell(void);
+void endusershell(void);
+
+/*
+ ** support/strcasestr.c
+ */
+char *strcasestr(register char *s, register char *find);
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/rdservers.c b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/rdservers.c
new file mode 100644
index 0000000000..f5be3caeda
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/rdservers.c
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/****************************************************************************
+ Copyright (c) 1999,2000 WU-FTPD Development Group.
+ All rights reserved.
+
+ Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994
+ The Regents of the University of California.
+ Portions Copyright (c) 1993, 1994 Washington University in Saint Louis.
+ Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc.
+ Portions Copyright (c) 1989 Massachusetts Institute of Technology.
+ Portions Copyright (c) 1998 Sendmail, Inc.
+ Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P. Allman.
+ Portions Copyright (c) 1997 by Stan Barber.
+ Portions Copyright (c) 1997 by Kent Landfield.
+ Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ Free Software Foundation, Inc.
+
+ Use and distribution of this software and its source code are governed
+ by the terms and conditions of the WU-FTPD Software License ("LICENSE").
+
+ If you did not receive a copy of the license, it may be obtained online
+ at http://www.wu-ftpd.org/license.html.
+
+ $Id: rdservers.c,v 1.4 2000/07/01 18:17:39 wuftpd Exp $
+
+****************************************************************************/
+/*
+ * rdservers - read ftpservers file
+ *
+ * INITIAL AUTHOR - Kent Landfield <kent@landfield.com>
+ */
+
+#include "config.h"
+
+#ifdef VIRTUAL
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include "proto.h"
+
+int read_servers_line(FILE *svrfp, char *hostaddress, size_t hsize,
+ char *accesspath, size_t asize)
+{
+ static char buffer[BUFSIZ];
+
+ char *hcp, *acp;
+ char *bcp, *ecp;
+ char *ap;
+
+ while (fgets(buffer, BUFSIZ, svrfp) != NULL) {
+
+ /* Find first non-whitespace character */
+ for (bcp = buffer; ((*bcp == '\t') || (*bcp == ' ')); bcp++);
+
+ /* Get rid of comments */
+ if ((ecp = strchr(buffer, '#')) != NULL)
+ *ecp = '\0';
+
+ /* Skip empty lines */
+ if ((bcp == ecp) || (*bcp == '\n'))
+ continue;
+
+ /* separate parts */
+
+ hcp = bcp;
+ for (acp = hcp;
+ (*acp && !isspace(*acp)); acp++);
+
+ /* better have something in access path or skip the line */
+ if (!*acp)
+ continue;
+
+ *acp++ = '\0';
+
+ while (*acp && isspace(*acp))
+ acp++;
+
+ /* again better have something in access path or skip the line */
+ if (!*acp)
+ continue;
+
+ ecp = acp;
+
+ while (*ecp && (!isspace(*ecp)) && *ecp != '\n')
+ ++ecp;
+
+ *ecp = '\0';
+
+ if ((ap = inet_htop(hcp)) != NULL)
+ (void) strlcpy(hostaddress, ap, hsize);
+ else
+ (void) strlcpy(hostaddress, hcp, hsize);
+
+ (void) strlcpy(accesspath, acp, asize);
+
+ return (1);
+ }
+ return (0);
+}
+#endif
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/realpath.c b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/realpath.c
new file mode 100644
index 0000000000..b961f2e474
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/realpath.c
@@ -0,0 +1,361 @@
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/****************************************************************************
+ Copyright (c) 1999,2000 WU-FTPD Development Group.
+ All rights reserved.
+
+ Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994
+ The Regents of the University of California.
+ Portions Copyright (c) 1993, 1994 Washington University in Saint Louis.
+ Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc.
+ Portions Copyright (c) 1989 Massachusetts Institute of Technology.
+ Portions Copyright (c) 1998 Sendmail, Inc.
+ Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P. Allman.
+ Portions Copyright (c) 1997 by Stan Barber.
+ Portions Copyright (c) 1997 by Kent Landfield.
+ Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ Free Software Foundation, Inc.
+
+ Use and distribution of this software and its source code are governed
+ by the terms and conditions of the WU-FTPD Software License ("LICENSE").
+
+ If you did not receive a copy of the license, it may be obtained online
+ at http://www.wu-ftpd.org/license.html.
+
+ $Id: realpath.c,v 1.11 2000/07/01 18:17:39 wuftpd Exp $
+
+****************************************************************************/
+/* Originally taken from FreeBSD 3.0's libc; adapted to handle chroot
+ * directories in BeroFTPD by Bernhard Rosenkraenzer
+ * <bero@beroftpd.unix.eu.org>
+ *
+ * Added super-user permissions so we can determine the real pathname even
+ * if the user cannot access the file. <lundberg+wuftpd@vr.net>
+ */
+#include "config.h"
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#if defined(HAVE_FCNTL_H)
+#include <fcntl.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "proto.h"
+
+#ifndef MAXSYMLINKS /* Workaround for Linux libc 4.x/5.x */
+#define MAXSYMLINKS 5
+#endif
+
+#ifndef HAVE_LSTAT
+#define lstat stat
+#endif
+
+char *wu_realpath(const char *path, char resolved_path[MAXPATHLEN], char *chroot_path)
+{
+ char *ptr;
+ char q[MAXPATHLEN];
+
+ fb_realpath(path, q);
+
+ if (chroot_path == NULL)
+ strcpy(resolved_path, q);
+ else {
+ strcpy(resolved_path, chroot_path);
+ if (q[0] != '/') {
+ if (strlen(resolved_path) + strlen(q) < MAXPATHLEN)
+ strcat(resolved_path, q);
+ else /* Avoid buffer overruns... */
+ return NULL;
+ }
+ else if (q[1] != '\0') {
+ for (ptr = q; *ptr != '\0'; ptr++);
+ if (ptr == resolved_path || *--ptr != '/') {
+ if (strlen(resolved_path) + strlen(q) < MAXPATHLEN)
+ strcat(resolved_path, q);
+ else /* Avoid buffer overruns... */
+ return NULL;
+ }
+ else {
+ if (strlen(resolved_path) + strlen(q) - 1 < MAXPATHLEN)
+ strcat(resolved_path, &q[1]);
+ else /* Avoid buffer overruns... */
+ return NULL;
+ }
+ }
+ }
+ return resolved_path;
+}
+
+/*
+ * char *fb_realpath(const char *path, char resolved_path[MAXPATHLEN]);
+ *
+ * Find the real name of path, by removing all ".", ".." and symlink
+ * components. Returns (resolved) on success, or (NULL) on failure,
+ * in which case the path which caused trouble is left in (resolved).
+ */
+char *fb_realpath(const char *path, char *resolved)
+{
+ struct stat sb;
+ int fd, n, rootd, serrno;
+ char *p, *q, wbuf[MAXPATHLEN];
+ int symlinks = 0;
+ int resultcode;
+#ifdef HAS_NO_FCHDIR
+/* AIX Has no fchdir() so we hope the getcwd() call doesn't overrun the buffer! */
+ char cwd[MAXPATHLEN + 1];
+ char *pcwd;
+#endif
+
+ /* Save the starting point. */
+ errno = 0;
+#ifdef HAS_NO_FCHDIR
+#ifdef HAVE_GETCWD
+ pcwd = getcwd(cwd, sizeof(cwd));
+#else
+ pcwd = getwd(cwd);
+#endif
+#else
+ fd = open(".", O_RDONLY);
+#endif
+ if (EACCES == errno) {
+ uid_t userid = geteuid();
+ access_priv_on(0);
+#ifdef HAS_NO_FCHDIR
+#ifdef HAVE_GETCWD
+ pcwd = getcwd(cwd, sizeof(cwd));
+#else
+ pcwd = getwd(cwd);
+#endif
+#else
+ fd = open(".", O_RDONLY);
+#endif
+ access_priv_off(userid);
+ }
+#ifdef HAS_NO_FCHDIR
+ if (pcwd == NULL)
+#else
+ if (fd < 0)
+#endif
+ {
+ (void) strcpy(resolved, ".");
+ return (NULL);
+ }
+
+ /*
+ * Find the dirname and basename from the path to be resolved.
+ * Change directory to the dirname component.
+ * lstat the basename part.
+ * if it is a symlink, read in the value and loop.
+ * if it is a directory, then change to that directory.
+ * get the current directory name and append the basename.
+ */
+ (void) strncpy(resolved, path, MAXPATHLEN - 1);
+ resolved[MAXPATHLEN - 1] = '\0';
+ loop:
+ q = strrchr(resolved, '/');
+ if (q != NULL) {
+ p = q + 1;
+ if (q == resolved)
+ q = "/";
+ else {
+ do {
+ --q;
+ } while (q > resolved && *q == '/');
+ q[1] = '\0';
+ q = resolved;
+ }
+ errno = 0;
+ resultcode = chdir(q);
+ if (EACCES == errno) {
+ uid_t userid = geteuid();
+ access_priv_on(0);
+ errno = 0;
+ resultcode = chdir(q);
+ access_priv_off(userid);
+ }
+ if (resultcode < 0)
+ goto err1;
+ }
+ else
+ p = resolved;
+
+ /* Deal with the last component. */
+ if (*p != '\0') {
+ errno = 0;
+ resultcode = lstat(p, &sb);
+ if (EACCES == errno) {
+ uid_t userid = geteuid();
+ access_priv_on(0);
+ errno = 0;
+ resultcode = lstat(p, &sb);
+ access_priv_off(userid);
+ }
+ if (resultcode == 0) {
+#ifdef HAVE_LSTAT
+ if (S_ISLNK(sb.st_mode)) {
+ if (++symlinks > MAXSYMLINKS) {
+ errno = ELOOP;
+ goto err1;
+ }
+ errno = 0;
+ {
+ size_t len = strlen(p);
+ char *tmp = calloc(len + 1, sizeof(char));
+ if (tmp == 0) {
+ serrno = errno;
+ goto err1;
+ }
+ strcpy(tmp, p);
+ p = tmp;
+ }
+ n = readlink(p, resolved, MAXPATHLEN);
+ if (EACCES == errno) {
+ uid_t userid = geteuid();
+ access_priv_on(0);
+ errno = 0;
+ n = readlink(p, resolved, MAXPATHLEN);
+ access_priv_off(userid);
+ }
+ if (n < 0) {
+ free(p);
+ goto err1;
+ }
+ free(p);
+ /* n should be less than MAXPATHLEN, but check to be safe */
+ if (n >= MAXPATHLEN)
+ n = MAXPATHLEN - 1;
+ resolved[n] = '\0';
+ goto loop;
+ }
+#endif /* HAVE_LSTAT */
+ if (S_ISDIR(sb.st_mode)) {
+ errno = 0;
+ resultcode = chdir(p);
+ if (EACCES == errno) {
+ uid_t userid = geteuid();
+ access_priv_on(0);
+ errno = 0;
+ resultcode = chdir(p);
+ access_priv_off(userid);
+ }
+ if (resultcode < 0)
+ goto err1;
+ p = "";
+ }
+ }
+ }
+
+ /*
+ * Save the last component name and get the full pathname of
+ * the current directory.
+ */
+ (void) strcpy(wbuf, p);
+ errno = 0;
+#ifdef HAVE_GETCWD
+ resultcode = getcwd(resolved, MAXPATHLEN) == NULL ? 0 : 1;
+#else
+ resultcode = getwd(resolved) == NULL ? 0 : 1;
+ if (resolved[MAXPATHLEN - 1] != '\0') {
+ resultcode = 0;
+ errno = ERANGE;
+ }
+#endif
+ if (EACCES == errno) {
+ uid_t userid = geteuid();
+ access_priv_on(0);
+ errno = 0;
+#ifdef HAVE_GETCWD
+ resultcode = getcwd(resolved, MAXPATHLEN) == NULL ? 0 : 1;
+#else
+ resultcode = getwd(resolved) == NULL ? 0 : 1;
+ if (resolved[MAXPATHLEN - 1] != '\0') {
+ resultcode = 0;
+ errno = ERANGE;
+ }
+#endif
+ access_priv_off(userid);
+ }
+ if (resultcode == 0)
+ goto err1;
+
+ /*
+ * Join the two strings together, ensuring that the right thing
+ * happens if the last component is empty, or the dirname is root.
+ */
+ if (resolved[0] == '/' && resolved[1] == '\0')
+ rootd = 1;
+ else
+ rootd = 0;
+
+ if (*wbuf) {
+ if (strlen(resolved) + strlen(wbuf) + !rootd + 1 > MAXPATHLEN) {
+ errno = ENAMETOOLONG;
+ goto err1;
+ }
+ if (rootd == 0)
+ (void) strcat(resolved, "/");
+ (void) strcat(resolved, wbuf);
+ }
+
+ /* Go back to where we came from. */
+ errno = 0;
+#ifdef HAS_NO_FCHDIR
+ resultcode = chdir(cwd);
+#else
+ resultcode = fchdir(fd);
+#endif
+ if (EACCES == errno) {
+ uid_t userid = geteuid();
+ access_priv_on(0);
+ errno = 0;
+#ifdef HAS_NO_FCHDIR
+ resultcode = chdir(cwd);
+#else
+ resultcode = fchdir(fd);
+#endif
+ access_priv_off(userid);
+ }
+ if (resultcode < 0) {
+ serrno = errno;
+ goto err2;
+ }
+
+#ifndef HAS_NO_FCHDIR
+ /* It's okay if the close fails, what's an fd more or less? */
+ (void) close(fd);
+#endif
+ return (resolved);
+
+ err1:serrno = errno;
+#ifdef HAS_NO_FCHDIR
+ (void) chdir(cwd);
+#else
+ (void) fchdir(fd);
+#endif
+ if (EACCES == errno) {
+ uid_t userid = geteuid();
+ access_priv_on(0);
+#ifdef HAS_NO_FCHDIR
+ (void) chdir(cwd);
+#else
+ (void) fchdir(fd);
+#endif
+ access_priv_off(userid);
+ }
+#ifdef HAS_NO_FCHDIR
+ err2:errno = serrno;
+#else
+ err2:(void) close(fd);
+ errno = serrno;
+#endif
+ return (NULL);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/restrict.c b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/restrict.c
new file mode 100644
index 0000000000..7142ef8e9d
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/restrict.c
@@ -0,0 +1,191 @@
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/****************************************************************************
+ Copyright (c) 1999,2000 WU-FTPD Development Group.
+ All rights reserved.
+
+ Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994
+ The Regents of the University of California.
+ Portions Copyright (c) 1993, 1994 Washington University in Saint Louis.
+ Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc.
+ Portions Copyright (c) 1989 Massachusetts Institute of Technology.
+ Portions Copyright (c) 1998 Sendmail, Inc.
+ Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P. Allman.
+ Portions Copyright (c) 1997 by Stan Barber.
+ Portions Copyright (c) 1997 by Kent Landfield.
+ Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ Free Software Foundation, Inc.
+
+ Use and distribution of this software and its source code are governed
+ by the terms and conditions of the WU-FTPD Software License ("LICENSE").
+
+ If you did not receive a copy of the license, it may be obtained online
+ at http://www.wu-ftpd.org/license.html.
+
+ $Id: restrict.c,v 1.14 2000/07/01 18:17:39 wuftpd Exp $
+
+****************************************************************************/
+/*
+ * Contributed by Glenn Nielsen <glenn@more.net>
+ * Mon, 18 Jan 1999 20:04:07 -0600
+ */
+#include "config.h"
+
+#include <sys/param.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include "proto.h"
+
+#ifdef HAVE_GETCWD
+extern char *getcwd(char *, size_t);
+#else
+extern char *getwd(char *);
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#define FALSE 0
+#endif
+
+extern char *home;
+extern int restricted_user;
+
+/*
+ * name is the function parameter
+ * home is a global string containing the user's home directory
+ *
+ * rhome is the resolved home directory
+ * rname is the resolved requested filename
+ * curwd is the current working directory
+ * path is name, possibly prepended by the current working directory
+ */
+
+int restrict_check(char *name)
+{
+ if (!test_restriction(name))
+ return 0;
+ reply(550, "Permission denied on server. You are restricted to your account.");
+ return 1;
+}
+
+int test_restriction(char *name)
+{
+ char rhome[MAXPATHLEN + 1], rname[MAXPATHLEN + 1], path[MAXPATHLEN + 1];
+
+ /* we're not in restrict mode so all access is OK */
+ if (restricted_user == FALSE)
+ return 0;
+
+ /* get resolved equivalent of user's home directory */
+ fb_realpath(home, rhome);
+
+ path[0] = '\0';
+
+ /* a relative path is specified, so resolve it w.r.t. current working directory */
+ if ((name)[0] != '/') {
+
+ char curwd[MAXPATHLEN + 1];
+
+ /* determine current working directory */
+#ifdef HAVE_GETCWD
+ if (getcwd(curwd, MAXPATHLEN) == (char *) NULL) {
+#else
+ if (getwd(curwd) == (char *) NULL) {
+#endif
+ return 1;
+ } /* if */
+
+ strcpy(path, curwd);
+ strcat(path, "/");
+
+ } /* if */
+
+ if ((strlen(path) + strlen(name) + 2) > sizeof(path)) {
+ return 1;
+ }
+
+ strcat(path, name);
+ fb_realpath(path, rname);
+ strcat(rname, "/");
+
+ if (strncmp(rhome, rname, strlen(rhome))) {
+ return 1;
+ } /* if */
+
+ return 0;
+} /* restrict_check */
+
+int restrict_list_check(char *name)
+{
+ char *beg, *copy, *end;
+ int flag;
+
+ beg = name;
+
+ while (*beg != '\0') {
+
+ flag = 0;
+ end = beg;
+ while (*end && !isspace(*end))
+ ++end;
+ if (!*end)
+ flag = 1;
+ if (!flag)
+ *end = '\0';
+ copy = strdup(beg);
+ if (!flag)
+ *end = ' ';
+
+ if (!copy) {
+ reply(550, "Permission denied on server. Out of memory.");
+ return 1;
+
+ } /* if */
+
+ if (restrict_check(copy)) {
+ free(copy);
+ return 1;
+ }
+ free(copy);
+ beg = end;
+ if (!flag)
+ ++beg;
+
+ } /* while */
+
+ return 0;
+
+} /* restrict_list_check */
+
+/*
+ * $Log: restrict.c,v $
+ * Revision 1.14 2000/07/01 18:17:39 wuftpd
+ *
+ * Updated copyright statement for the WU-FTPD Development Group.
+ *
+ * Revision 1.13 1999/10/08 03:42:12 wuftpd
+ * Fixed a bug in restrict_check which could allow access outside the users home
+ *
+ * Revision 1.12 1999/09/05 02:31:50 wuftpd
+ * Add virtual and defaultserver support for email notification
+ *
+ * Revision 1.11 1999/09/02 19:35:48 wuftpd
+ * CDUP was leaking information about restrictions.
+ *
+ * Revision 1.10 1999/09/02 14:04:29 wuftpd
+ * Cleaning up. Indented and removed some STDC checks
+ *
+ * Revision 1.9 1999/08/24 23:41:39 wuftpd
+ * wu-ftpd-2.4.x RCS Ids removed and new Ids added for wu-ftpd.org usage.
+ * WU-FTPD Development Group copyright headers added.
+ * Original Copyright headers moved into the COPYRIGHT file.
+ * COPYPRIGHT.c added to build for ftpshut and ftpd.
+ *
+ * Revision 1.2 1996/02/20 04:54:04 root
+ * added #define to make gcc use HAVE_GETCWD
+ *
+ * Revision 1.1 1996/02/20 03:52:48 root
+ * Initial revision
+ *
+ */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/routevector.c b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/routevector.c
new file mode 100644
index 0000000000..c0c7fc38f2
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/routevector.c
@@ -0,0 +1,292 @@
+/*
+ * Copyright 2001-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/****************************************************************************
+ Copyright (c) 1999,2000 WU-FTPD Development Group.
+ All rights reserved.
+
+ Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994
+ The Regents of the University of California.
+ Portions Copyright (c) 1993, 1994 Washington University in Saint Louis.
+ Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc.
+ Portions Copyright (c) 1989 Massachusetts Institute of Technology.
+ Portions Copyright (c) 1998 Sendmail, Inc.
+ Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P. Allman.
+ Portions Copyright (c) 1997 by Stan Barber.
+ Portions Copyright (c) 1997 by Kent Landfield.
+ Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ Free Software Foundation, Inc.
+
+ Use and distribution of this software and its source code are governed
+ by the terms and conditions of the WU-FTPD Software License ("LICENSE").
+
+ If you did not receive a copy of the license, it may be obtained online
+ at http://www.wu-ftpd.org/license.html.
+
+ $Id: routevector.c,v 1.13 2000/07/01 18:17:39 wuftpd Exp $
+
+****************************************************************************/
+/*
+ * Parse the entire ftpaccess file looking for:
+ *
+ * passive address <externalip> <address/CIDR>
+ * passive ports <address/CIDR> <min> <max>
+ *
+ * vect_addr, passive_port_min and passive_port_max store the external IP
+ * address, min and max ports found whose associated address is the most
+ * specific match of the address the client connected from.
+ *
+ * The optional CIDR denotes the number of significant bits in the address,
+ * the higher the CIDR the more specific the address. If no CIDR is specified,
+ * the whole address is significant.
+ *
+ * When a passive data connection is requested the server listens on a port
+ * randomly selected between passive_port_min and passive_port_max
+ * (inclusive), if vect_addr is set its address is reported (if not the
+ * local address of the control connection is reported). Note this does not
+ * change the address the server actually listens on, only the address
+ * reported to the client.
+ *
+ * For example if the ftpaccess file includes:
+ * passive address 194.80.17.14 0.0.0.0/0
+ * passive address 10.0.1.15 10.0.0.0/8
+ *
+ * Clients connecting from the class-A network 10 will be told the passive
+ * connection is listening on IP address 10.0.1.15, while clients connecting
+ * from all other addresses will be told the connection is listening on
+ * 194.80.17.14 (a CIDR of /0 matches all addresses of the same address
+ * family, if IPv6 support is enabled then IPv4 and IPv6 addresses are
+ * supported).
+ */
+
+#include "config.h"
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+#ifdef HAVE_SYS_SYSLOG_H
+#include <sys/syslog.h>
+#endif
+#if defined(HAVE_SYSLOG_H) || (!defined(AUTOCONF) && !defined(HAVE_SYS_SYSLOG_H))
+#include <syslog.h>
+#endif
+#include "extensions.h"
+#include "proto.h"
+
+extern struct SOCKSTORAGE his_addr;
+extern struct SOCKSTORAGE vect_addr; /* best matching external IP address */
+extern int passive_port_min;
+extern int passive_port_max;
+
+/* significance of the external IP address and port entries */
+static int vect_sig = -1;
+static int port_sig = -1;
+
+#ifdef INET6
+static int his_addr_family = AF_INET;
+static int his_v4mapped = 0;
+#endif
+
+/*
+ * Compares the address the client connected from (in his_addr) with the
+ * supplied address, with the specified number of bits being significant
+ * in the comparison. Returns 0 if the addresses match, non-zero otherwise.
+ */
+static int addr_cmp(void *addr, int sig)
+{
+ uint32_t addr32[4], rem32[4];
+ int bitstozero, i, start = 0, len = sizeof(uint32_t);
+ char *ptr;
+
+#ifdef INET6
+ if (his_addr_family == AF_INET) {
+ if (his_v4mapped) {
+ ptr = (char *)&((struct sockaddr_in6 *)&his_addr)->sin6_addr;
+ /* move to the IPv4 part of an IPv4-mapped IPv6 address */
+ ptr += 12;
+ }
+ else
+#endif
+ ptr = (char *)&((struct sockaddr_in *)&his_addr)->sin_addr;
+
+ /* IPv4 addresses are 32-bits long */
+ bitstozero = 32 - sig;
+ memcpy(addr32, addr, sizeof(uint32_t));
+ memcpy(rem32, ptr, sizeof(uint32_t));
+#ifdef INET6
+ }
+ else {
+ /* IPv6 addresses are 128-bits long */
+ bitstozero = 128 - sig;
+ start = 3;
+ len = sizeof(addr32);
+ memcpy(addr32, addr, sizeof(addr32));
+ memcpy(rem32, &((struct sockaddr_in6 *)&his_addr)->sin6_addr, sizeof(rem32));
+ }
+#endif
+
+ /* zero bits starting with the least significant */
+ for (i = start; (bitstozero > 0) && (i >= 0); i--, bitstozero -= 32) {
+ if (bitstozero >= 32)
+ addr32[i] = rem32[i] = 0;
+ else {
+ addr32[i] = (ntohl(addr32[i]) >> bitstozero) << bitstozero;
+ rem32[i] = (ntohl(rem32[i]) >> bitstozero) << bitstozero;
+ }
+ }
+
+ /* compare the IP addresses */
+ return memcmp(addr32, rem32, len);
+}
+
+/*
+ * Matches a supplied IP address string against the address the client
+ * connected from (in his_addr). Returns 1 and updates sig if the addresses
+ * match and there hasn't already been a more specific match, zero otherwise.
+ */
+static int better_match(char *addrstr, int *sig)
+{
+ int addr_sig, max_sig = 32;
+ char *ptr;
+ void *addr;
+#ifdef INET6
+ int rval;
+ struct in6_addr in6;
+#else
+ struct in_addr in;
+#endif
+
+ /* look for the optional significance (/CIDR) */
+ if ((ptr = strstr(addrstr, "/")))
+ *ptr = '\0';
+
+#ifdef INET6
+ if (his_addr_family == AF_INET6)
+ max_sig = 128;
+#endif
+
+ if (ptr) {
+ addr_sig = atoi(++ptr);
+ if (addr_sig < 0)
+ addr_sig = 0;
+ else if (addr_sig > max_sig)
+ addr_sig = max_sig;
+ }
+ else
+ addr_sig = max_sig;
+
+ /* return if we already have a more specific match */
+ if (addr_sig < *sig) {
+ if (ptr)
+ *--ptr = '/';
+ return 0;
+ }
+
+#ifdef INET6
+ rval = inet_pton6(addrstr, &in6);
+ if (ptr)
+ *--ptr = '/';
+ if (rval != 1)
+ return 0;
+
+ if (his_addr_family == AF_INET) {
+ /* convert IPv4-mapped IPv6 addresses to IPv4 addresses */
+ if (IN6_IS_ADDR_V4MAPPED(&in6))
+ addr = &in6.s6_addr[12];
+ else
+ return 0;
+ }
+ else
+ addr = &in6.s6_addr;
+#else
+ in.s_addr = inet_addr(addrstr);
+ if (ptr)
+ *--ptr = '/';
+ if ((int)in.s_addr == -1)
+ return 0;
+ addr = &in.s_addr;
+#endif
+
+ if (addr_cmp(addr, addr_sig) == 0) {
+ *sig = addr_sig;
+ return 1;
+ }
+ return 0;
+}
+
+static void update_address(char *externalip, char *addrstr)
+{
+ struct SOCKSTORAGE ext_addr;
+#ifndef INET6
+ struct in_addr in;
+#endif
+
+ /* validate the external IP address string */
+#ifdef INET6
+ SET_SOCK_FAMILY(ext_addr, AF_INET6);
+ if (inet_pton6(externalip, SOCK_ADDR(ext_addr)) != 1)
+ return;
+ if ((his_addr_family == AF_INET) &&
+ !IN6_IS_ADDR_V4MAPPED((struct in6_addr *)SOCK_ADDR(ext_addr)))
+ return;
+#else
+ if ((int)(in.s_addr = inet_addr(externalip)) == -1)
+ return;
+ SET_SOCK_FAMILY(ext_addr, AF_INET);
+ SET_SOCK_ADDR4(ext_addr, in);
+#endif
+
+ if (better_match(addrstr, &vect_sig))
+ vect_addr = ext_addr;
+}
+
+static void update_ports(char *addrstr, char *minport, char *maxport)
+{
+ int min, max;
+
+ min = atoi(minport);
+ max = atoi(maxport);
+
+ /* validate the ports supplied */
+ if ((min > max) || (min < 0) || (max > 65535) || (min == 0 && max != 0)) {
+ syslog(LOG_WARNING, "ftpaccess passive ports entry invalid: %s %s %s", addrstr, minport, maxport);
+ return;
+ }
+
+ if (better_match(addrstr, &port_sig)) {
+ passive_port_min = min;
+ passive_port_max = max;
+ }
+}
+
+int routevector(void)
+{
+ struct aclmember *entry = NULL;
+
+#ifdef INET6
+ if (SOCK_FAMILY(his_addr) == AF_INET6) {
+ if (IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)&(his_addr))->sin6_addr))
+ his_v4mapped = 1;
+ else
+ his_addr_family = AF_INET6;
+ }
+#endif
+
+ while (getaclentry("passive", &entry)) {
+ if (!strcasecmp(ARG0, "address")) {
+ if (!ARG1 || !ARG2)
+ continue;
+ update_address(ARG1, ARG2);
+ }
+ if (!strcasecmp(ARG0, "ports")) {
+ if (!ARG1 || !ARG2 || !ARG3)
+ continue;
+ update_ports(ARG1, ARG2, ARG3);
+ }
+ }
+ return vect_sig != -1;
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/strcasestr.c b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/strcasestr.c
new file mode 100644
index 0000000000..ec80947aa7
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/strcasestr.c
@@ -0,0 +1,49 @@
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/****************************************************************************
+
+ Copyright (c) 1999,2000 WU-FTPD Development Group.
+ All rights reserved.
+
+ Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994
+ The Regents of the University of California.
+ Portions Copyright (c) 1993, 1994 Washington University in Saint Louis.
+ Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc.
+ Portions Copyright (c) 1989 Massachusetts Institute of Technology.
+ Portions Copyright (c) 1998 Sendmail, Inc.
+ Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P. Allman.
+ Portions Copyright (c) 1997 by Stan Barber.
+ Portions Copyright (c) 1997 by Kent Landfield.
+ Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ Free Software Foundation, Inc.
+
+ Use and distribution of this software and its source code are governed
+ by the terms and conditions of the WU-FTPD Software License ("LICENSE").
+
+ If you did not receive a copy of the license, it may be obtained online
+ at http://www.wu-ftpd.org/license.html.
+
+ $Id: strcasestr.c,v 1.5 2000/07/01 18:36:29 wuftpd Exp $
+
+****************************************************************************/
+#include <string.h>
+/*
+ * Find the first occurrence of find in s.
+ */
+char *strcasestr(register char *s, register char *find)
+{
+ register char c, sc;
+ register size_t len;
+
+ if ((c = *find++) != 0) {
+ len = strlen(find);
+ do {
+ do {
+ if ((sc = *s++) == 0)
+ return (NULL);
+ } while (sc != c);
+ } while (strncasecmp(s, find, len) != 0);
+ s--;
+ }
+ return ((char *) s);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/strsep.c b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/strsep.c
new file mode 100644
index 0000000000..98657c4dd3
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/strsep.c
@@ -0,0 +1,67 @@
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/****************************************************************************
+
+ Copyright (c) 1999,2000 WU-FTPD Development Group.
+ All rights reserved.
+
+ Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994
+ The Regents of the University of California.
+ Portions Copyright (c) 1993, 1994 Washington University in Saint Louis.
+ Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc.
+ Portions Copyright (c) 1989 Massachusetts Institute of Technology.
+ Portions Copyright (c) 1998 Sendmail, Inc.
+ Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P. Allman.
+ Portions Copyright (c) 1997 by Stan Barber.
+ Portions Copyright (c) 1997 by Kent Landfield.
+ Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ Free Software Foundation, Inc.
+
+ Use and distribution of this software and its source code are governed
+ by the terms and conditions of the WU-FTPD Software License ("LICENSE").
+
+ If you did not receive a copy of the license, it may be obtained online
+ at http://www.wu-ftpd.org/license.html.
+
+ $Id: strsep.c,v 1.6 2000/07/01 18:36:29 wuftpd Exp $
+
+****************************************************************************/
+#include <string.h>
+#include <stdio.h>
+
+/*
+ * Get next token from string *stringp, where tokens are nonempty
+ * strings separated by characters from delim.
+ *
+ * Writes NULLs into the string at *stringp to end tokens.
+ * delim need not remain constant from call to call.
+ * On return, *stringp points past the last NUL written (if there might
+ * be further tokens), or is NULL (if there are definitely no more tokens).
+ *
+ * If *stringp is NULL, strsep returns NULL.
+ */
+char *strsep(register char **stringp, register const char *delim)
+{
+ register char *s;
+ register const char *spanp;
+ register int c, sc;
+ char *tok;
+
+ if ((s = *stringp) == NULL)
+ return (NULL);
+ for (tok = s;;) {
+ c = *s++;
+ spanp = delim;
+ do {
+ if ((sc = *spanp++) == c) {
+ if (c == 0)
+ s = NULL;
+ else
+ s[-1] = 0;
+ *stringp = s;
+ return (tok);
+ }
+ } while (sc != 0);
+ }
+ /* NOTREACHED */
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/timeout.c b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/timeout.c
new file mode 100644
index 0000000000..6bf043c638
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/timeout.c
@@ -0,0 +1,72 @@
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/****************************************************************************
+ Copyright (c) 1999,2000 WU-FTPD Development Group.
+ All rights reserved.
+
+ Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994
+ The Regents of the University of California.
+ Portions Copyright (c) 1993, 1994 Washington University in Saint Louis.
+ Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc.
+ Portions Copyright (c) 1989 Massachusetts Institute of Technology.
+ Portions Copyright (c) 1998 Sendmail, Inc.
+ Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P. Allman.
+ Portions Copyright (c) 1997 by Stan Barber.
+ Portions Copyright (c) 1997 by Kent Landfield.
+ Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ Free Software Foundation, Inc.
+
+ Use and distribution of this software and its source code are governed
+ by the terms and conditions of the WU-FTPD Software License ("LICENSE").
+
+ If you did not receive a copy of the license, it may be obtained online
+ at http://www.wu-ftpd.org/license.html.
+
+ $Id: timeout.c,v 1.5 2000/07/01 18:17:39 wuftpd Exp $
+
+****************************************************************************/
+#include "config.h"
+#include "proto.h"
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "extensions.h"
+
+unsigned int timeout_idle = 900; /* Command idle: 15 minutes */
+unsigned int timeout_maxidle = 7200; /* Command idle (MAX): 2 hours */
+unsigned int timeout_data = 1200; /* Data idle: 20 minutes */
+unsigned int timeout_rfc931 = 10; /* RFC931 session, total: 10 seconds */
+unsigned int timeout_accept = 120; /* Accepting data connection: 2 minutes */
+unsigned int timeout_connect = 120; /* Establishing data connection: 2 minutes */
+
+void load_timeouts(void)
+{
+ struct aclmember *entry = NULL;
+ while (getaclentry("timeout", &entry)) {
+ if ((ARG0 != NULL) && (ARG1 != NULL)) {
+ unsigned long value = strtoul(ARG1, NULL, 0);
+ if (strcasecmp(ARG0, "rfc931") == 0)
+ timeout_rfc931 = value;
+ else if (value > 0)
+ if (strcasecmp(ARG0, "idle") == 0) {
+ timeout_idle = value;
+ if (timeout_maxidle < timeout_idle)
+ timeout_maxidle = timeout_idle;
+ }
+ else if (strcasecmp(ARG0, "maxidle") == 0) {
+ timeout_maxidle = value;
+ if (timeout_idle > timeout_maxidle)
+ timeout_idle = timeout_maxidle;
+ }
+ else if (strcasecmp(ARG0, "data") == 0)
+ timeout_data = value;
+ else if (strcasecmp(ARG0, "accept") == 0)
+ timeout_accept = value;
+ else if (strcasecmp(ARG0, "connect") == 0)
+ timeout_connect = value;
+ }
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/vers.c b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/vers.c
new file mode 100644
index 0000000000..a76c60eac7
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/vers.c
@@ -0,0 +1,3 @@
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+char version[] = "Version wu-2.6.2+Sun";
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/wu_config.h b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/wu_config.h
new file mode 100644
index 0000000000..fe71afb310
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/wu_config.h
@@ -0,0 +1,338 @@
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/* config.h. Generated automatically by configure. */
+/****************************************************************************
+
+ Copyright (c) 1999,2000 WU-FTPD Development Group.
+ All rights reserved.
+
+ Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994
+ The Regents of the University of California.
+ Portions Copyright (c) 1993, 1994 Washington University in Saint Louis.
+ Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc.
+ Portions Copyright (c) 1989 Massachusetts Institute of Technology.
+ Portions Copyright (c) 1998 Sendmail, Inc.
+ Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P. Allman.
+ Portions Copyright (c) 1997 by Stan Barber.
+ Portions Copyright (c) 1997 by Kent Landfield.
+ Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ Free Software Foundation, Inc.
+
+ Use and distribution of this software and its source code are governed
+ by the terms and conditions of the WU-FTPD Software License ("LICENSE").
+
+ If you did not receive a copy of the license, it may be obtained online
+ at http://www.wu-ftpd.org/license.html.
+
+ $Id: config.h.in,v 1.15 2000/07/01 17:42:15 wuftpd Exp $
+
+****************************************************************************/
+
+/*
+ * Top level config file... These values will be adjusted by autoconf.
+ * $Id: config.h.in,v 1.15 2000/07/01 17:42:15 wuftpd Exp $
+ */
+
+/*
+ * allow "upload" keyword in ftpaccess
+ */
+
+#define UPLOAD 1
+
+/*
+ * allow "overwrite" keyword in ftpaccess.
+ */
+
+#define OVERWRITE 1
+
+/*
+ * allow "allow/deny" for individual users.
+ */
+
+#define HOST_ACCESS 1
+
+/*
+ * log failed login attempts
+ */
+
+#define LOG_FAILED 1
+
+/*
+ * log login attempts that fail because of class connection
+ * limits. Busy servers may want to prevent this logging
+ * since it can fill up the log file and put a high load on
+ * syslog.
+ */
+#define LOG_TOOMANY 1
+
+/*
+ * allow use of private file. (for site group and site gpass)
+ * NO_PRIVATE
+ * Define this if you don't want to use the private authentication databases.
+ */
+
+/* #undef NO_PRIVATE */
+
+/*
+ * Try once more on failed DNS lookups (to allow far away connections
+ * which might resolve slowly)
+ */
+
+/* #undef DNS_TRYAGAIN */
+
+/*
+ * ANON_ONLY
+ * Permit only anonymous logins... disables all other type
+ * See FIXES-2.4-HOBBIT for more information on this option.
+ */
+
+/* #undef ANON_ONLY */
+
+/*
+ * PARANOID
+ * Disable "questionable" functions
+ * See FIXES-2.4-HOBBIT for more information on this option.
+ */
+
+/* #undef PARANOID */
+
+/*
+ * SKEY
+ * Add SKEY support -- REQUIRES SKEY libraries
+ * See FIXES-2.4-HOBBIT for more information on this option.
+ */
+
+/* #undef SKEY */
+
+/*
+ * OPIE
+ * One-time Passwords In Everything (OPIE)
+ * Add OPIE support -- REQUIRES OPIE libraries
+ */
+
+#if !defined (LINUX) /* Linux autodetects OPIE */
+/* #undef OPIE */
+#endif
+
+/*
+ * ALTERNATE_CD
+ * Causes "cd ~" to return the chroot-relative directory instead of the
+ * real directory.
+ */
+#define ALTERNATE_CD 1
+
+/*
+ * UNRESTRICTED_CHMOD
+ * If defined, any valid value for the mode will be accepted.
+ * Otherwise, only values between 0 and 777 are accepted.
+ */
+/* #undef UNRESTRICTED_CHMOD */
+
+/*
+ * USE_RFC931
+ * Define this if you want to use RFC 931 'authentication' - this improves
+ * the logging at the cost of a possible slight delay in connection.
+ */
+/* #undef USE_RFC931 */
+
+/*
+ * BUFFER_SIZE
+ * You can specify the buffer size for binary transfers; the defaults
+ * are often far too small for efficiency.
+ */
+/* #undef BUFFER_SIZE */
+
+/*
+ * If you want to specify the syslog facility, you should modify CFLAGS in
+ * the appropriate src/makefile/Makefile.*.
+ */
+
+/* If you want to set the paths where the configuration files, pids and logs
+ * are stored, you should inspect src/pathnames.h and modify the appropriate
+ * src/config/config.*.
+ */
+
+/*
+ * RATIO
+ * Support for Upload/Download ratios (may download x bytes for uploading 1 byte)
+ */
+/* #undef RATIO */
+
+/*
+ * OTHER_PASSWD
+ * Support for using alternative passwd/shadow files
+ */
+#define OTHER_PASSWD 1
+
+/*
+ * DAEMON
+ * If ftpd called with -D then run as a standalone daemon listing on the
+ * ftp port. This can speed up ftpd response as all ftpd then needs to
+ * do is fork off a copy to handle an incoming request. Under inetd
+ * a new copy has to be opened and exec'd.
+ */
+#define DAEMON 1
+
+/*
+ * MAX_BACKLOG
+ * Only used in DAEMON mode.
+ * This is second parameter to listen. It defines the number of incoming
+ * processes to allow to backlog, prior to being accept() processing them,
+ * before rejecting.
+ */
+#define MAX_BACKLOG 100
+
+/*
+ * MAPPING_CHDIR
+ * Keep track of the path the user has chdir'd into and respond with
+ * that to pwd commands. This is to avoid having the absolue disk
+ * path returned. This helps avoid returning dirs like '.1/fred'
+ * when lots of disks make up the ftp area.
+ */
+
+#define MAPPING_CHDIR 1
+
+/*
+ * THROUGHPUT
+ * Keep track of total throughput for the user and limit if required.
+ */
+
+#define THROUGHPUT 1
+
+/*
+ * TRANSFER_COUNT
+ * Keep track of total bytes for statistics.
+ */
+
+#define TRANSFER_COUNT 1
+
+/*
+ * TRANSFER_LIMIT
+ * Limit file and bytes transferred in a session.
+ */
+
+#define TRANSFER_LIMIT 1
+
+/*
+ * NO_SUCKING_NEWLINES
+ * Don't suppress some extra blank lines on messages and banners.
+ */
+
+/* #undef NO_SUCKING_NEWLINES */
+
+/*
+ * HELP_CRACKERS
+ * Define this to help crackers break into your system by letting them
+ * figure out which user names exist to guess passwords on.
+ */
+
+/* #undef HELP_CRACKERS */
+
+/*
+ * VERBOSE_ERROR_LOGING
+ * Log all problems with USER and PASS as well as all rejected commands
+ * and denied uploads/downloads.
+ */
+
+#define VERBOSE_ERROR_LOGING 1
+
+/*
+ * IGNORE_NOOP
+ * Undefine this to let NOOP reset the idle timeout.
+ */
+
+#define IGNORE_NOOP 1
+
+/*
+ * CLOSED_VIRTUAL_SERVER
+ * Undefine this to allow real and non-owner guests to log in on a virutal server's address.
+ */
+#define CLOSED_VIRTUAL_SERVER 1
+
+/*
+ * Some people don't like PASV and want to disable it. Whatever.
+ * PORT can be abused to attack other hosts. Let's give the option to
+ * disable one or the other. We'll ignore DISABLE_PASV if you defined
+ * DISABLE_PORT (hey, you gotta have at least one!).
+ */
+/* #undef DISABLE_PORT */
+/* #undef DISABLE_PASV */
+
+/*
+ * Define this to suppress messages about PID locks causing the daemon to
+ * sleep. This should only be needed at busy sites.
+ */
+#define NO_PID_SLEEP_MSGS 1
+
+/*
+ * Define this to require the remove end of a PASV connection to have the
+ * same IP as the control connection. This limits, but does not eliminate,
+ * the risk of PASV port race stealing the connection. It also is non-RFC
+ * compliant, so it may cause problems for some client sites.
+ */
+#define FIGHT_PASV_PORT_RACE 1
+
+/*
+ * Define this to completely disable anonymous FTP access.
+ */
+/* #undef NO_ANONYMOUS_ACCESS */
+
+/*
+ * Define this to have an ls command compiled into the daemon. That way you
+ * don't need to put statically linked ls's into every chroot directory.
+ */
+/* #undef INTERNAL_LS */
+
+/*
+ * Define this if you want the internal ls to display UIDs/GIDs rather than
+ * user/group names. This is faster, but doesn't look as nice.
+ */
+/* #undef LS_NUMERIC_UIDS */
+
+/*
+ * Define this if you want to hide setuid bits in the internal ls
+ * this might be a good idea for security.
+ */
+#define HIDE_SETUID 1
+
+/*
+ * Define this if you want to support virtual servers
+ */
+#define VIRTUAL 1
+
+/*
+ * Define this if you want to be able to receive mail on anonymous
+ * uploads
+ */
+#define MAIL_ADMIN 1
+
+/*
+ * Config files in /etc by default
+ */
+#define USE_ETC 1
+
+/*
+ * Define this to support quota mechanisms...
+ */
+#define QUOTA 1
+
+/*
+ * The intention of SITE NEWER was to enable mirrors to quickly determine which
+ * files have changed since the last run. Since most mirror packages wish to
+ * work with all daemons (not just wu-ftpd), and since SITE NEWER is a wu-ftpd
+ * only feature, they don't use the feature. Therefore there seems little
+ * reason to continue to support it.
+ *
+ * Define this to support SITE NEWER and SITE MINFO.
+ */
+/* #undef SITE_NEWER */
+
+/*
+ * Define this to revert the NLST command to showing directories.
+ *
+ * This will cause mget to have errors when it attempts to RETR the
+ * directory name (which is not a RETRievable object) but will revert
+ * the NLST command enough to quell complains from Solaris command-
+ * line FTP client users.
+ */
+#define NLST_SHOWS_DIRS 1
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/wu_fnmatch.c b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/wu_fnmatch.c
new file mode 100644
index 0000000000..32461fd338
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/wu_fnmatch.c
@@ -0,0 +1,206 @@
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/****************************************************************************
+ Copyright (c) 1999,2000 WU-FTPD Development Group.
+ All rights reserved.
+
+ Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994
+ The Regents of the University of California.
+ Portions Copyright (c) 1993, 1994 Washington University in Saint Louis.
+ Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc.
+ Portions Copyright (c) 1989 Massachusetts Institute of Technology.
+ Portions Copyright (c) 1998 Sendmail, Inc.
+ Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P. Allman.
+ Portions Copyright (c) 1997 by Stan Barber.
+ Portions Copyright (c) 1997 by Kent Landfield.
+ Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ Free Software Foundation, Inc.
+
+ Use and distribution of this software and its source code are governed
+ by the terms and conditions of the WU-FTPD Software License ("LICENSE").
+
+ If you did not receive a copy of the license, it may be obtained online
+ at http://www.wu-ftpd.org/license.html.
+
+ $Id: wu_fnmatch.c,v 1.7 2000/10/25 20:18:13 wuftpd Exp $
+
+****************************************************************************/
+/*
+ * Function fnmatch() as specified in POSIX 1003.2-1992, section B.6.
+ * Compares a filename or pathname to a pattern.
+ */
+
+#include <ctype.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+
+typedef int boolean;
+#define FALSE 0
+#define TRUE 1
+
+#include "wu_fnmatch.h"
+
+#define EOS '\0'
+
+static const char *rangematch(const char *pattern, const char *string, int flags)
+{
+/*
+ * A bracket expression starting with an unquoted circumflex character
+ * produces unspecified results (IEEE 1003.2-1992, 3.13.2). This
+ * implementation treats it like '!', for consistency with the regular
+ * expression syntax. J.T. Conklin (conklin@ngai.kaleida.com)
+ */
+ char test = *string;
+ boolean negate = ((*pattern == '!') || (*pattern == '^'));
+ boolean ok = FALSE;
+ if (negate)
+ ++pattern;
+ if (flags & FNM_CASEFOLD)
+ test = tolower((unsigned char) test);
+ while (*pattern != ']') {
+ char c = *pattern++;
+ if ((c == '\\') && !(flags & FNM_NOESCAPE))
+ c = *pattern++;
+ if (c == EOS)
+ return (NULL);
+ if (flags & FNM_CASEFOLD)
+ c = tolower((unsigned char) c);
+ if (*pattern == '-') {
+ char c2 = pattern[1];
+ if ((c2 != EOS)
+ && (c2 != ']')) {
+ pattern += 2;
+ if ((c2 == '\\') && !(flags & FNM_NOESCAPE))
+ c2 = *pattern++;
+ if (c2 == EOS)
+ return (NULL);
+ if (flags & FNM_CASEFOLD)
+ c2 = tolower((unsigned char) c2);
+ /* this is a hack */
+ if ((c <= test) && (test <= c2))
+ ok = TRUE;
+ }
+ else if (c == test)
+ ok = TRUE;
+ }
+ else if (c == test)
+ ok = TRUE;
+ }
+ return ((ok == negate) ? NULL : pattern+1);
+}
+
+int wu_fnmatch(const char *pattern, const char *string, int flags)
+{
+ const char *stringstart = string;
+ if ((pattern == NULL) || (string == NULL))
+ return FNM_NOMATCH;
+ while (TRUE) {
+ char test;
+ char c = *pattern++;
+ switch (c) {
+ case EOS:
+#ifdef FNM_LEADING_DIR
+ if ((flags & FNM_LEADING_DIR)
+ && (*string == '/'))
+ return (0);
+ /*
+ * WU-FTPD extension/correction.
+ *
+ * If the pattern ended with a '/', and we're doing
+ * FNM_PATHNAME matching, consider it a match if the
+ * previous string character was a '/' and the current
+ * is not a '/'.
+ */
+ if ((flags & FNM_LEADING_DIR)
+ && (string != stringstart)
+ && (flags & FNM_PATHNAME)
+ && (*(string - 1) == '/'))
+ return (0);
+#endif
+ return ((*string == EOS) ? 0 : FNM_NOMATCH);
+ case '?':
+ if (*string == EOS)
+ return (FNM_NOMATCH);
+ if ((*string == '/')
+ && (flags & FNM_PATHNAME))
+ return (FNM_NOMATCH);
+ if ((*string == '.')
+ && (flags & FNM_PERIOD)
+ && ((string == stringstart)
+ || ((flags & FNM_PATHNAME)
+ && (*(string - 1) == '/'))))
+ return (FNM_NOMATCH);
+ ++string;
+ break;
+ case '*':
+ c = *pattern;
+ while (c == '*')
+ c = *++pattern;
+ if ((*string == '.')
+ && (flags & FNM_PERIOD)
+ && ((string == stringstart)
+ || ((flags & FNM_PATHNAME)
+ && (*(string - 1) == '/'))))
+ return (FNM_NOMATCH);
+ /* Optimize for pattern with * at end or before /. */
+ if (c == EOS)
+ if (flags & FNM_PATHNAME) {
+#ifdef FNM_LEADING_DIR
+ if (flags & FNM_LEADING_DIR)
+ return (0);
+#endif
+ return ((strchr(string, '/') == NULL) ? 0 : FNM_NOMATCH);
+ }
+ else
+ return (0);
+ else if ((c == '/')
+ && (flags & FNM_PATHNAME)) {
+ string = strchr(string, '/');
+ if (string == NULL)
+ return (FNM_NOMATCH);
+ break;
+ }
+ /* General case, use recursion. */
+ for (test = *string; test != EOS; test = *++string) {
+ if (!wu_fnmatch(pattern, string, (flags & ~FNM_PERIOD)))
+ return (0);
+ if ((test == '/')
+ && (flags & FNM_PATHNAME))
+ break;
+ }
+ return (FNM_NOMATCH);
+ case '[':
+ if (*string == EOS)
+ return (FNM_NOMATCH);
+ if ((*string == '/')
+ && (flags & FNM_PATHNAME))
+ return (FNM_NOMATCH);
+ pattern = rangematch(pattern, string, flags);
+ if (pattern == NULL)
+ return (FNM_NOMATCH);
+ ++string;
+ break;
+ case '\\':
+ if (!(flags & FNM_NOESCAPE)) {
+ c = *pattern++;
+ if (c == EOS) {
+ c = '\\';
+ --pattern;
+ }
+ }
+ /* FALLTHROUGH */
+ default:
+ if (c == *string);
+#ifdef FNM_CASEFOLD
+ else if ((flags & FNM_CASEFOLD)
+ && (tolower((unsigned char) c) == tolower((unsigned char) *string)));
+#endif
+ else
+ return (FNM_NOMATCH);
+ string++;
+ break;
+ }
+ }
+/* NOTREACHED */
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/wu_fnmatch.h b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/wu_fnmatch.h
new file mode 100644
index 0000000000..f96c839058
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/wu_fnmatch.h
@@ -0,0 +1,39 @@
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/****************************************************************************
+ Copyright (c) 1999,2000 WU-FTPD Development Group.
+ All rights reserved.
+
+ Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994
+ The Regents of the University of California.
+ Portions Copyright (c) 1993, 1994 Washington University in Saint Louis.
+ Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc.
+ Portions Copyright (c) 1989 Massachusetts Institute of Technology.
+ Portions Copyright (c) 1998 Sendmail, Inc.
+ Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P. Allman.
+ Portions Copyright (c) 1997 by Stan Barber.
+ Portions Copyright (c) 1997 by Kent Landfield.
+ Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ Free Software Foundation, Inc.
+
+ Use and distribution of this software and its source code are governed
+ by the terms and conditions of the WU-FTPD Software License ("LICENSE").
+
+ If you did not receive a copy of the license, it may be obtained online
+ at http://www.wu-ftpd.org/license.html.
+
+ $Id: wu_fnmatch.h,v 1.5 2000/07/01 18:17:39 wuftpd Exp $
+
+****************************************************************************/
+#ifndef __WU_FNMATCH_H
+#define __WU_FNMATCH_H 1
+
+extern int wu_fnmatch(const char *pattern, const char *string, int flags);
+#define FNM_NOMATCH 1
+#define FNM_PATHNAME 0x01
+#define FNM_NOESCAPE 0x02
+#define FNM_PERIOD 0x04
+#define FNM_LEADING_DIR 0x08
+#define FNM_CASEFOLD 0x10
+
+#endif
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/xferlog.c b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/xferlog.c
new file mode 100644
index 0000000000..0123ce64d3
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/xferlog.c
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "config.h"
+#include <string.h>
+#include "extensions.h"
+#include "proto.h"
+
+#define DEFXFERFORMAT "%T %Xt %R %Xn %XP %Xy %Xf %Xd %Xm %U ftp %Xa %u %Xc"
+
+int xferdone = 0;
+struct xferstat xfervalues;
+char xferlog_format[MAXXFERSTRLEN] = DEFXFERFORMAT;
+
+/*************************************************************************/
+/* FUNCTION : get_xferlog_format */
+/* PURPOSE : Read the xferlog format string from ftpaccess into */
+/* xferlog_format if it exists otherwise load default string */
+/* ARGUMENTS : none */
+/*************************************************************************/
+
+void get_xferlog_format(void)
+{
+ int which;
+ struct aclmember *entry = (struct aclmember *)NULL;
+
+ /* xferlog format <formatstring> */
+ xferlog_format[0] = '\0';
+ while (getaclentry("xferlog", &entry)) {
+ if (ARG0 && (strcasecmp(ARG0, "format") == 0)) {
+ for (which = 1; (which < MAXARGS) && ARG[which]; which++) {
+ if (which > 1) {
+ if (strlcat(xferlog_format, " ",
+ sizeof(xferlog_format)) >= sizeof(xferlog_format))
+ break;
+ }
+ if (strlcat(xferlog_format, ARG[which],
+ sizeof(xferlog_format)) >= sizeof(xferlog_format))
+ break;
+ }
+ break;
+ }
+ }
+
+ /* default xferlog format */
+ if (xferlog_format[0] == '\0')
+ (void) strlcpy(xferlog_format, DEFXFERFORMAT, sizeof(xferlog_format));
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.rarpd.c b/usr/src/cmd/cmd-inet/usr.sbin/in.rarpd.c
new file mode 100644
index 0000000000..2e756c342e
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.rarpd.c
@@ -0,0 +1,1409 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * Portions of this source code were derived from Berkeley 4.3 BSD
+ * under license from the Regents of the University of California.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * rarpd.c Reverse-ARP server.
+ * Refer to RFC 903 "A Reverse Address Resolution Protocol".
+ */
+
+#define _REENTRANT
+
+#include <thread.h>
+#include <synch.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/resource.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <syslog.h>
+#include <signal.h>
+#include <netdb.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <netinet/if_ether.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stropts.h>
+#include <sys/dlpi.h>
+#include <libinetutil.h>
+#include <net/if_types.h>
+#include <net/if_dl.h>
+
+#define BOOTDIR "/tftpboot" /* boot files directory */
+#define DEVDIR "/dev" /* devices directory */
+#define DEVIP "/dev/ip" /* path to ip driver */
+#define DEVARP "/dev/arp" /* path to arp driver */
+
+#define BUFSIZE 2048 /* max receive frame length */
+#define MAXPATHL 128 /* max path length */
+#define MAXHOSTL 128 /* max host name length */
+#define MAXIFS 256
+
+/*
+ * Logical network devices
+ */
+struct ifdev {
+ char ldevice[IFNAMSIZ];
+ int lunit;
+ ipaddr_t ipaddr; /* network order */
+ ipaddr_t if_netmask; /* host order */
+ ipaddr_t if_ipaddr; /* host order */
+ ipaddr_t if_netnum; /* host order, with subnet */
+ struct ifdev *next;
+};
+
+/*
+ * Physical network device
+ */
+struct rarpdev {
+ char device[IFNAMSIZ];
+ int unit;
+ int fd;
+ uchar_t *lladdr; /* mac address of interface */
+ int ifaddrlen; /* mac address length */
+ int ifsaplen; /* indicates dlsap format */
+ int ifrarplen; /* size of rarp data packet */
+ struct ifdev *ifdev; /* private interface info */
+ struct rarpdev *next; /* list of managed devices */
+};
+
+struct rarpreply {
+ struct rarpdev *rdev; /* which device reply for */
+ struct timeval tv; /* send RARP reply by when */
+ uchar_t *lldest; /* target mac to send reply */
+ uchar_t *arprep; /* [R]ARP response */
+ struct rarpreply *next;
+};
+
+static struct rarpreply *delay_list;
+static sema_t delay_sema;
+static mutex_t delay_mutex;
+static mutex_t debug_mutex;
+
+static struct rarpdev *rarpdev_head;
+
+/*
+ * Globals initialized before multi-threading
+ */
+static char *cmdname; /* command name from argv[0] */
+static int dflag = 0; /* enable diagnostics */
+static int aflag = 0; /* start rarpd on all interfaces */
+static char *alarmmsg; /* alarm() error message */
+static long pc_name_max; /* pathconf maximum path name */
+
+static void getintf(void);
+static struct rarpdev *find_device(ifspec_t *);
+static void init_rarpdev(struct rarpdev *);
+static void do_rarp(void *);
+static void rarp_request(struct rarpdev *, struct arphdr *,
+ uchar_t *);
+static void add_arp(struct rarpdev *, uchar_t *, uchar_t *);
+static void arp_request(struct rarpdev *, struct arphdr *, uchar_t *);
+static int rarp_open(struct rarpdev *, ushort_t);
+static void do_delay_write(void *);
+static void delay_write(struct rarpdev *, struct rarpreply *);
+static int rarp_write(int, struct rarpreply *);
+static int mightboot(ipaddr_t);
+static void get_ifdata(char *, int, ipaddr_t *, ipaddr_t *);
+static int get_ipaddr(struct rarpdev *, uchar_t *, uchar_t *, ipaddr_t *);
+static void sigalarm(int);
+static int strioctl(int, int, int, int, char *);
+static void usage();
+static void syserr(char *);
+static void error(char *, ...);
+static void debug(char *, ...);
+
+extern int optind;
+extern char *optarg;
+
+int
+main(int argc, char *argv[])
+{
+ int c;
+ struct rlimit rl;
+ struct rarpdev *rdev;
+ int i;
+
+ cmdname = argv[0];
+
+ while ((c = getopt(argc, argv, "ad")) != -1) {
+ switch (c) {
+ case 'a':
+ aflag = 1;
+ break;
+
+ case 'd':
+ dflag = 1;
+ break;
+
+ default:
+ usage();
+ }
+ }
+
+ if ((!aflag && (argc - optind) != 2) ||
+ (aflag && (argc - optind) != 0)) {
+ usage();
+ /* NOTREACHED */
+ }
+
+ if (!dflag) {
+ /*
+ * Background
+ */
+ switch (fork()) {
+ case -1: /* error */
+ syserr("fork");
+ /*NOTREACHED*/
+
+ case 0: /* child */
+ break;
+
+ default: /* parent */
+ return (0);
+ }
+ for (i = 0; i < 3; i++) {
+ (void) close(i);
+ }
+ (void) open("/", O_RDONLY, 0);
+ (void) dup2(0, 1);
+ (void) dup2(0, 2);
+ /*
+ * Detach terminal
+ */
+ if (setsid() < 0)
+ syserr("setsid");
+ }
+
+ rl.rlim_cur = RLIM_INFINITY;
+ rl.rlim_max = RLIM_INFINITY;
+ if (setrlimit(RLIMIT_NOFILE, &rl) == -1)
+ syserr("setrlimit");
+
+ /*
+ * Look up the maximum name length of the BOOTDIR, it may not
+ * exist so use /, if that fails use a reasonable sized buffer.
+ */
+ if ((pc_name_max = pathconf(BOOTDIR, _PC_NAME_MAX)) == -1) {
+ if ((pc_name_max = pathconf("/", _PC_NAME_MAX)) == -1) {
+ pc_name_max = 255;
+ }
+ }
+
+ (void) openlog(cmdname, LOG_PID, LOG_DAEMON);
+
+ if (aflag) {
+ /*
+ * Get each interface name and load rarpdev list
+ */
+ getintf();
+ } else {
+ ifspec_t ifsp;
+ struct ifdev *ifdev;
+ char buf[IFNAMSIZ + 1];
+
+ /*
+ * Load specified device as only element of the list
+ */
+ rarpdev_head = (struct rarpdev *)calloc(1,
+ sizeof (struct rarpdev));
+ if (rarpdev_head == NULL) {
+ error("out of memory");
+ }
+ (void) strncpy(buf, argv[optind], IFNAMSIZ);
+ (void) strncat(buf, argv[optind + 1], IFNAMSIZ - strlen(buf));
+
+ if ((ifdev = calloc(1, sizeof (struct ifdev))) == NULL) {
+ error("out of memory");
+ }
+
+ if (!ifparse_ifspec(buf, &ifsp) || ifsp.ifsp_modcnt != 0) {
+ error("invalid interface specification");
+ }
+
+ if (ifsp.ifsp_lunvalid) {
+ (void) snprintf(ifdev->ldevice,
+ sizeof (ifdev->ldevice), "%s%d:",
+ ifsp.ifsp_devnm, ifsp.ifsp_ppa);
+ ifdev->lunit = ifsp.ifsp_lun;
+ } else
+ ifdev->lunit = -1; /* no logical unit */
+ (void) strlcpy(rarpdev_head->device, ifsp.ifsp_devnm,
+ sizeof (rarpdev_head->device));
+ rarpdev_head->unit = ifsp.ifsp_ppa;
+
+ ifdev->next = rarpdev_head->ifdev;
+ rarpdev_head->ifdev = ifdev;
+ }
+
+ /*
+ * Initialize each rarpdev
+ */
+ for (rdev = rarpdev_head; rdev != NULL; rdev = rdev->next) {
+ init_rarpdev(rdev);
+ }
+
+ (void) sema_init(&delay_sema, 0, USYNC_THREAD, NULL);
+ (void) mutex_init(&delay_mutex, USYNC_THREAD, NULL);
+ (void) mutex_init(&debug_mutex, USYNC_THREAD, NULL);
+
+ /*
+ * Start delayed processing thread
+ */
+ (void) thr_create(NULL, NULL, (void *(*)(void *))do_delay_write, NULL,
+ THR_NEW_LWP, NULL);
+
+ /*
+ * Start RARP processing for each device
+ */
+ for (rdev = rarpdev_head; rdev != NULL; rdev = rdev->next) {
+ if (rdev->fd != -1) {
+ (void) thr_create(NULL, NULL,
+ (void *(*)(void *))do_rarp, (void *)rdev,
+ THR_NEW_LWP, NULL);
+ }
+ }
+
+ /*
+ * Exit main() thread
+ */
+ thr_exit(NULL);
+
+ return (0);
+}
+
+static void
+getintf(void)
+{
+ int fd;
+ int numifs;
+ unsigned bufsize;
+ struct ifreq *reqbuf;
+ struct ifconf ifconf;
+ struct ifreq *ifr;
+ struct rarpdev *rdev;
+ struct ifdev *ifdev;
+
+ /*
+ * Open the IP provider.
+ */
+ if ((fd = open(DEVIP, 0)) < 0)
+ syserr(DEVIP);
+
+ /*
+ * Ask IP for the list of configured interfaces.
+ */
+ if (ioctl(fd, SIOCGIFNUM, (char *)&numifs) < 0) {
+ numifs = MAXIFS;
+ }
+ bufsize = numifs * sizeof (struct ifreq);
+ reqbuf = (struct ifreq *)malloc(bufsize);
+ if (reqbuf == NULL) {
+ error("out of memory");
+ }
+
+ ifconf.ifc_len = bufsize;
+ ifconf.ifc_buf = (caddr_t)reqbuf;
+ if (ioctl(fd, SIOCGIFCONF, (char *)&ifconf) < 0)
+ syserr("SIOCGIFCONF");
+
+ /*
+ * Initialize a rarpdev for each interface
+ */
+ for (ifr = ifconf.ifc_req; ifconf.ifc_len > 0;
+ ifr++, ifconf.ifc_len -= sizeof (struct ifreq)) {
+ ifspec_t ifsp;
+
+ if (ioctl(fd, SIOCGIFFLAGS, (char *)ifr) < 0) {
+ syserr("ioctl SIOCGIFFLAGS");
+ exit(1);
+ }
+ if ((ifr->ifr_flags & IFF_LOOPBACK) ||
+ !(ifr->ifr_flags & IFF_UP) ||
+ !(ifr->ifr_flags & IFF_BROADCAST) ||
+ (ifr->ifr_flags & IFF_NOARP) ||
+ (ifr->ifr_flags & IFF_POINTOPOINT))
+ continue;
+
+ if (!ifparse_ifspec(ifr->ifr_name, &ifsp))
+ error("ifparse_ifspec failed");
+
+ /*
+ * Look for an existing device for logical interfaces
+ */
+ if ((rdev = find_device(&ifsp)) == NULL) {
+ rdev = calloc(1, sizeof (struct rarpdev));
+ if (rdev == NULL)
+ error("out of memory");
+
+ (void) strlcpy(rdev->device, ifsp.ifsp_devnm,
+ sizeof (rdev->device));
+ rdev->unit = ifsp.ifsp_ppa;
+
+ rdev->next = rarpdev_head;
+ rarpdev_head = rdev;
+ }
+
+ if ((ifdev = calloc(1, sizeof (struct ifdev))) == NULL)
+ error("out of memory");
+
+ if (ifsp.ifsp_lunvalid) {
+ (void) snprintf(ifdev->ldevice,
+ sizeof (ifdev->ldevice), "%s%d:",
+ ifsp.ifsp_devnm, ifsp.ifsp_ppa);
+ ifdev->lunit = ifsp.ifsp_lun;
+ } else
+ ifdev->lunit = -1; /* no logical unit */
+
+ ifdev->next = rdev->ifdev;
+ rdev->ifdev = ifdev;
+ }
+ (void) free((char *)reqbuf);
+}
+
+static struct rarpdev *
+find_device(ifspec_t *specp)
+{
+ struct rarpdev *rdev;
+
+ for (rdev = rarpdev_head; rdev != NULL; rdev = rdev->next) {
+ if (specp->ifsp_ppa == rdev->unit &&
+ strcmp(specp->ifsp_devnm, rdev->device) == 0)
+ return (rdev);
+ }
+ return (NULL);
+}
+
+static void
+init_rarpdev(struct rarpdev *rdev)
+{
+ char *dev;
+ int unit;
+ struct ifdev *ifdev;
+
+ /*
+ * Open datalink provider and get our mac address.
+ */
+ rdev->fd = rarp_open(rdev, ETHERTYPE_REVARP);
+
+ /*
+ * rarp_open may fail on certain types of interfaces
+ */
+ if (rdev->fd < 0) {
+ rdev->fd = -1;
+ return;
+ }
+
+ /*
+ * Get the IP address and netmask from directory service for
+ * each logical interface.
+ */
+ for (ifdev = rdev->ifdev; ifdev != NULL; ifdev = ifdev->next) {
+ /*
+ * If lunit == -1 then this is the primary interface name
+ */
+ if (ifdev->lunit == -1) {
+ dev = rdev->device;
+ unit = rdev->unit;
+ } else {
+ dev = ifdev->ldevice;
+ unit = ifdev->lunit;
+ }
+ get_ifdata(dev, unit, &ifdev->if_ipaddr, &ifdev->if_netmask);
+
+ /*
+ * Use IP address of the interface.
+ */
+ ifdev->if_netnum = ifdev->if_ipaddr & ifdev->if_netmask;
+ ifdev->ipaddr = (ipaddr_t)htonl(ifdev->if_ipaddr);
+ }
+}
+
+static void
+do_rarp(void *buf)
+{
+ struct rarpdev *rdev = (struct rarpdev *)buf;
+ struct strbuf ctl;
+ char ctlbuf[BUFSIZE];
+ struct strbuf data;
+ char databuf[BUFSIZE];
+ char *cause;
+ struct arphdr *ans;
+ uchar_t *shost;
+ int flags, ret;
+ union DL_primitives *dlp;
+ uchar_t *laddrp;
+ char *str = NULL;
+
+ /*
+ * Sanity check; if we hit this limit, ctlbuf/databuf needs
+ * to be malloc'ed.
+ */
+ if ((sizeof (ctlbuf) < (DL_UNITDATA_IND_SIZE + rdev->ifaddrlen)) ||
+ (sizeof (databuf) < rdev->ifrarplen))
+ error("unsupported media");
+
+ if (((shost = (uchar_t *)malloc(rdev->ifaddrlen)) == NULL) ||
+ ((ans = (struct arphdr *)malloc(rdev->ifrarplen)) == NULL))
+ syserr("malloc");
+
+ if (dflag) {
+ str = _link_ntoa(rdev->lladdr, str, rdev->ifaddrlen, IFT_OTHER);
+ if (str != NULL) {
+ debug("starting rarp service on device %s%d address %s",
+ rdev->device, rdev->unit, str);
+ free(str);
+ }
+ }
+
+ /*
+ * read RARP packets and respond to them.
+ */
+ for (;;) {
+ ctl.len = 0;
+ ctl.maxlen = BUFSIZE;
+ ctl.buf = ctlbuf;
+ data.len = 0;
+ data.maxlen = BUFSIZE;
+ data.buf = databuf;
+ flags = 0;
+
+ if ((ret = getmsg(rdev->fd, &ctl, &data, &flags)) < 0)
+ syserr("getmsg");
+
+ /*
+ * Validate DL_UNITDATA_IND.
+ */
+ /* LINTED pointer */
+ dlp = (union DL_primitives *)ctlbuf;
+
+ (void) memcpy(ans, databuf, rdev->ifrarplen);
+
+ cause = NULL;
+ if (ctl.len == 0)
+ cause = "missing control part of message";
+ else if (ctl.len < 0)
+ cause = "short control part of message";
+ else if (dlp->dl_primitive != DL_UNITDATA_IND)
+ cause = "not unitdata_ind";
+ else if (ret & MORECTL)
+ cause = "MORECTL flag";
+ else if (ret & MOREDATA)
+ cause = "MOREDATA flag";
+ else if (ctl.len < DL_UNITDATA_IND_SIZE)
+ cause = "short unitdata_ind";
+ else if (data.len < rdev->ifrarplen)
+ cause = "short arp";
+ else if (ans->ar_hrd != htons(ARPHRD_ETHER))
+ cause = "hrd";
+ else if (ans->ar_pro != htons(ETHERTYPE_IP))
+ cause = "pro";
+ else if (ans->ar_hln != rdev->ifaddrlen)
+ cause = "hln";
+ else if (ans->ar_pln != sizeof (ipaddr_t))
+ cause = "pln";
+ if (cause) {
+ if (dflag)
+ debug("receive check failed: cause: %s",
+ cause);
+ continue;
+ }
+
+ /*
+ * Good request.
+ * Pick out the mac source address of this RARP request.
+ */
+ laddrp = (uchar_t *)ctlbuf +
+ dlp->unitdata_ind.dl_src_addr_offset;
+ (void) memcpy(shost, laddrp, ans->ar_hln);
+
+ /*
+ * Handle the request.
+ */
+ switch (ntohs(ans->ar_op)) {
+ case REVARP_REQUEST:
+ rarp_request(rdev, ans, shost);
+ break;
+
+ case ARPOP_REQUEST:
+ arp_request(rdev, ans, shost);
+ break;
+
+ case REVARP_REPLY:
+ if (dflag)
+ debug("REVARP_REPLY ignored");
+ break;
+
+ case ARPOP_REPLY:
+ if (dflag)
+ debug("ARPOP_REPLY ignored");
+ break;
+
+ default:
+ if (dflag)
+ debug("unknown opcode 0x%x", ans->ar_op);
+ break;
+ }
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * Reverse address determination and allocation code.
+ */
+static void
+rarp_request(struct rarpdev *rdev, struct arphdr *rp, uchar_t *shost)
+{
+ ipaddr_t tpa, spa;
+ struct rarpreply *rrp;
+ uchar_t *shap, *thap, *spap, *tpap;
+ char *str = NULL;
+
+ shap = (uchar_t *)rp + sizeof (struct arphdr);
+ spap = shap + rp->ar_hln;
+ thap = spap + rp->ar_pln;
+ tpap = thap + rp->ar_hln;
+
+ if (dflag) {
+ str = _link_ntoa(thap, str, rdev->ifaddrlen, IFT_OTHER);
+ if (str != NULL) {
+ debug("RARP_REQUEST for %s", str);
+ free(str);
+ }
+ }
+
+ /*
+ * third party lookups are rare and wonderful
+ */
+ if ((memcmp(shap, thap, rdev->ifaddrlen) != 0) ||
+ (memcmp(shap, shost, rdev->ifaddrlen) != 0)) {
+ if (dflag)
+ debug("weird (3rd party lookup)");
+ }
+
+ /*
+ * fill in given parts of reply packet
+ */
+ (void) memcpy(shap, rdev->lladdr, rdev->ifaddrlen);
+
+ /*
+ * If a good address is stored in our lookup tables, return it
+ * immediately or after a delay. Store it our kernel's ARP cache.
+ */
+ if (get_ipaddr(rdev, thap, tpap, &spa))
+ return;
+ (void) memcpy(spap, &spa, sizeof (spa));
+
+ add_arp(rdev, tpap, thap);
+
+ rp->ar_op = htons(REVARP_REPLY);
+
+ if (dflag) {
+ struct in_addr addr;
+
+ (void) memcpy(&addr, tpap, sizeof (ipaddr_t));
+ debug("good lookup, maps to %s", inet_ntoa(addr));
+ }
+
+ rrp = (struct rarpreply *)calloc(1, sizeof (struct rarpreply) +
+ rdev->ifaddrlen + rdev->ifrarplen);
+ if (rrp == NULL)
+ error("out of memory");
+ rrp->lldest = (uchar_t *)rrp + sizeof (struct rarpreply);
+ rrp->arprep = rrp->lldest + rdev->ifaddrlen;
+
+ /*
+ * Create rarpreply structure.
+ */
+ (void) gettimeofday(&rrp->tv, NULL);
+ rrp->tv.tv_sec += 3; /* delay */
+ rrp->rdev = rdev;
+ (void) memcpy(rrp->lldest, shost, rdev->ifaddrlen);
+ (void) memcpy(rrp->arprep, rp, rdev->ifrarplen);
+
+ /*
+ * If this is diskless and we're not its bootserver, let the
+ * bootserver reply first by delaying a while.
+ */
+ (void) memcpy(&tpa, tpap, sizeof (ipaddr_t));
+ if (mightboot(ntohl(tpa))) {
+ if (rarp_write(rdev->fd, rrp) < 0)
+ syslog(LOG_ERR, "Bad rarp_write: %m");
+ if (dflag)
+ debug("immediate reply sent");
+ (void) free(rrp);
+ } else {
+ delay_write(rdev, rrp);
+ }
+}
+
+/*
+ * Download an ARP entry into our kernel.
+ */
+static void
+add_arp(struct rarpdev *rdev, uchar_t *ip, uchar_t *laddr)
+{
+ struct xarpreq ar;
+ struct sockaddr_in *sin;
+ int fd;
+
+ /*
+ * Common part of query or set
+ */
+ (void) memset(&ar, 0, sizeof (ar));
+ ar.xarp_pa.ss_family = AF_INET;
+ sin = (struct sockaddr_in *)&ar.xarp_pa;
+ (void) memcpy(&sin->sin_addr, ip, sizeof (ipaddr_t));
+
+ /*
+ * Open the IP provider.
+ */
+ if ((fd = open(DEVARP, 0)) < 0)
+ syserr(DEVARP);
+
+ /*
+ * Set the entry
+ */
+ (void) memcpy(LLADDR(&ar.xarp_ha), laddr, rdev->ifaddrlen);
+ ar.xarp_ha.sdl_alen = rdev->ifaddrlen;
+ ar.xarp_ha.sdl_family = AF_LINK;
+ (void) strioctl(fd, SIOCDXARP, -1, sizeof (struct xarpreq),
+ (char *)&ar);
+ if (strioctl(fd, SIOCSXARP, -1, sizeof (struct xarpreq),
+ (char *)&ar) < 0)
+ syserr("SIOCSXARP");
+
+ (void) close(fd);
+}
+
+/*
+ * The RARP spec says we must be able to process ARP requests,
+ * even through the packet type is RARP. Let's hope this feature
+ * is not heavily used.
+ */
+static void
+arp_request(struct rarpdev *rdev, struct arphdr *rp, uchar_t *shost)
+{
+ struct rarpreply *rrp;
+ struct ifdev *ifdev;
+ uchar_t *shap, *thap, *spap, *tpap;
+ int ret;
+
+ shap = (uchar_t *)rp + sizeof (struct arphdr);
+ spap = shap + rp->ar_hln;
+ thap = spap + rp->ar_pln;
+ tpap = thap + rp->ar_hln;
+
+ if (dflag)
+ debug("ARPOP_REQUEST");
+
+ for (ifdev = rdev->ifdev; ifdev != NULL; ifdev = ifdev->next) {
+ if (memcmp(&ifdev->ipaddr, tpap, sizeof (ipaddr_t)) == 0)
+ break;
+ }
+ if (ifdev == NULL)
+ return;
+
+ rp->ar_op = ARPOP_REPLY;
+ (void) memcpy(shap, rdev->lladdr, rdev->ifaddrlen);
+ (void) memcpy(spap, &ifdev->ipaddr, sizeof (ipaddr_t));
+ (void) memcpy(thap, rdev->lladdr, rdev->ifaddrlen);
+
+ add_arp(rdev, tpap, thap);
+
+ /*
+ * Create rarp reply structure.
+ */
+ rrp = (struct rarpreply *)calloc(1, sizeof (struct rarpreply) +
+ rdev->ifaddrlen + rdev->ifrarplen);
+ if (rrp == NULL)
+ error("out of memory");
+ rrp->lldest = (uchar_t *)rrp + sizeof (struct rarpreply);
+ rrp->arprep = rrp->lldest + rdev->ifaddrlen;
+ rrp->rdev = rdev;
+
+ (void) memcpy(rrp->lldest, shost, rdev->ifaddrlen);
+ (void) memcpy(rrp->arprep, rp, rdev->ifrarplen);
+
+ ret = rarp_write(rdev->fd, rrp);
+ free(rrp);
+ if (ret < 0)
+ error("rarp_write error");
+}
+
+/*
+ * OPEN the datalink provider device, ATTACH to the unit,
+ * and BIND to the revarp type.
+ * Return the resulting descriptor.
+ *
+ * MT-UNSAFE
+ */
+static int
+rarp_open(struct rarpdev *rarpdev, ushort_t type)
+{
+ register int fd;
+ char path[MAXPATHL];
+ union DL_primitives *dlp;
+ char buf[BUFSIZE];
+ struct strbuf ctl;
+ int flags;
+ uchar_t *eap;
+ char *device = rarpdev->device;
+ int unit = rarpdev->unit;
+ char *str = NULL;
+
+ /*
+ * Prefix the device name with "/dev/" if it doesn't
+ * start with a "/" .
+ */
+ if (*device == '/')
+ (void) snprintf(path, sizeof (path), "%s", device);
+ else
+ (void) snprintf(path, sizeof (path), "%s/%s", DEVDIR, device);
+
+ /*
+ * Open the datalink provider.
+ */
+ if ((fd = open(path, O_RDWR)) < 0)
+ syserr(path);
+
+ /*
+ * Issue DL_INFO_REQ and check DL_INFO_ACK for sanity.
+ */
+ /* LINTED pointer */
+ dlp = (union DL_primitives *)buf;
+ dlp->info_req.dl_primitive = DL_INFO_REQ;
+
+ ctl.buf = (char *)dlp;
+ ctl.len = DL_INFO_REQ_SIZE;
+
+ if (putmsg(fd, &ctl, NULL, 0) < 0)
+ syserr("putmsg");
+
+ (void) signal(SIGALRM, sigalarm);
+
+ alarmmsg = "DL_INFO_REQ failed: timeout waiting for DL_INFO_ACK";
+ (void) alarm(10);
+
+ ctl.buf = (char *)dlp;
+ ctl.len = 0;
+ ctl.maxlen = BUFSIZE;
+ flags = 0;
+ if (getmsg(fd, &ctl, NULL, &flags) < 0)
+ syserr("getmsg");
+
+ (void) alarm(0);
+ (void) signal(SIGALRM, SIG_DFL);
+
+ /*
+ * Validate DL_INFO_ACK reply.
+ */
+ if (ctl.len < sizeof (ulong_t))
+ error("DL_INFO_REQ failed: short reply to DL_INFO_REQ");
+
+ if (dlp->dl_primitive != DL_INFO_ACK)
+ error("DL_INFO_REQ failed: dl_primitive 0x%lx received",
+ dlp->dl_primitive);
+
+ if (ctl.len < DL_INFO_ACK_SIZE)
+ error("DL_INFO_REQ failed: short info_ack: %d bytes",
+ ctl.len);
+
+ if (dlp->info_ack.dl_version != DL_VERSION_2)
+ error("DL_INFO_ACK: incompatible version: %lu",
+ dlp->info_ack.dl_version);
+
+ if (dlp->info_ack.dl_sap_length != -2) {
+ if (dflag)
+ debug(
+"%s%d DL_INFO_ACK: incompatible dl_sap_length: %ld",
+ device, unit, dlp->info_ack.dl_sap_length);
+ (void) close(fd);
+ return (-1);
+ }
+
+ if ((dlp->info_ack.dl_service_mode & DL_CLDLS) == 0) {
+ if (dflag)
+ debug(
+"%s%d DL_INFO_ACK: incompatible dl_service_mode: 0x%lx",
+ device, unit, dlp->info_ack.dl_service_mode);
+ (void) close(fd);
+ return (-1);
+ }
+
+ rarpdev->ifsaplen = dlp->info_ack.dl_sap_length;
+ rarpdev->ifaddrlen = dlp->info_ack.dl_addr_length -
+ abs(rarpdev->ifsaplen);
+ rarpdev->ifrarplen = sizeof (struct arphdr) +
+ (2 * sizeof (ipaddr_t)) + (2 * rarpdev->ifaddrlen);
+
+ /*
+ * Issue DL_ATTACH_REQ.
+ */
+ /* LINTED pointer */
+ dlp = (union DL_primitives *)buf;
+ dlp->attach_req.dl_primitive = DL_ATTACH_REQ;
+ dlp->attach_req.dl_ppa = unit;
+
+ ctl.buf = (char *)dlp;
+ ctl.len = DL_ATTACH_REQ_SIZE;
+
+ if (putmsg(fd, &ctl, NULL, 0) < 0)
+ syserr("putmsg");
+
+ (void) signal(SIGALRM, sigalarm);
+ alarmmsg = "DL_ATTACH_REQ failed: timeout waiting for DL_OK_ACK";
+
+ (void) alarm(10);
+
+ ctl.buf = (char *)dlp;
+ ctl.len = 0;
+ ctl.maxlen = BUFSIZE;
+ flags = 0;
+ if (getmsg(fd, &ctl, NULL, &flags) < 0)
+ syserr("getmsg");
+
+ (void) alarm(0);
+ (void) signal(SIGALRM, SIG_DFL);
+
+ /*
+ * Validate DL_OK_ACK reply.
+ */
+ if (ctl.len < sizeof (ulong_t))
+ error("DL_ATTACH_REQ failed: short reply to attach request");
+
+ if (dlp->dl_primitive == DL_ERROR_ACK)
+ error("DL_ATTACH_REQ failed: dl_errno %lu unix_errno %lu",
+ dlp->error_ack.dl_errno, dlp->error_ack.dl_unix_errno);
+
+ if (dlp->dl_primitive != DL_OK_ACK)
+ error("DL_ATTACH_REQ failed: dl_primitive 0x%lx received",
+ dlp->dl_primitive);
+
+ if (ctl.len < DL_OK_ACK_SIZE)
+ error("attach failed: short ok_ack: %d bytes",
+ ctl.len);
+
+ /*
+ * Issue DL_BIND_REQ.
+ */
+ /* LINTED pointer */
+ dlp = (union DL_primitives *)buf;
+ dlp->bind_req.dl_primitive = DL_BIND_REQ;
+ dlp->bind_req.dl_sap = type;
+ dlp->bind_req.dl_max_conind = 0;
+ dlp->bind_req.dl_service_mode = DL_CLDLS;
+ dlp->bind_req.dl_conn_mgmt = 0;
+ dlp->bind_req.dl_xidtest_flg = 0;
+
+ ctl.buf = (char *)dlp;
+ ctl.len = DL_BIND_REQ_SIZE;
+
+ if (putmsg(fd, &ctl, NULL, 0) < 0)
+ syserr("putmsg");
+
+ (void) signal(SIGALRM, sigalarm);
+
+ alarmmsg = "DL_BIND_REQ failed: timeout waiting for DL_BIND_ACK";
+ (void) alarm(10);
+
+ ctl.buf = (char *)dlp;
+ ctl.len = 0;
+ ctl.maxlen = BUFSIZE;
+ flags = 0;
+ if (getmsg(fd, &ctl, NULL, &flags) < 0)
+ syserr("getmsg");
+
+ (void) alarm(0);
+ (void) signal(SIGALRM, SIG_DFL);
+
+ /*
+ * Validate DL_BIND_ACK reply.
+ */
+ if (ctl.len < sizeof (ulong_t))
+ error("DL_BIND_REQ failed: short reply");
+
+ if (dlp->dl_primitive == DL_ERROR_ACK)
+ error("DL_BIND_REQ failed: dl_errno %lu unix_errno %lu",
+ dlp->error_ack.dl_errno, dlp->error_ack.dl_unix_errno);
+
+ if (dlp->dl_primitive != DL_BIND_ACK)
+ error("DL_BIND_REQ failed: dl_primitive 0x%lx received",
+ dlp->dl_primitive);
+
+ if (ctl.len < DL_BIND_ACK_SIZE)
+ error(
+"DL_BIND_REQ failed: short bind acknowledgement received");
+
+ if (dlp->bind_ack.dl_sap != type)
+ error(
+"DL_BIND_REQ failed: returned dl_sap %lu != requested sap %d",
+ dlp->bind_ack.dl_sap, type);
+
+ /*
+ * Issue DL_PHYS_ADDR_REQ to get our local mac address.
+ */
+ /* LINTED pointer */
+ dlp = (union DL_primitives *)buf;
+ dlp->physaddr_req.dl_primitive = DL_PHYS_ADDR_REQ;
+ dlp->physaddr_req.dl_addr_type = DL_CURR_PHYS_ADDR;
+
+ ctl.buf = (char *)dlp;
+ ctl.len = DL_PHYS_ADDR_REQ_SIZE;
+
+ if (putmsg(fd, &ctl, NULL, 0) < 0)
+ syserr("putmsg");
+
+ (void) signal(SIGALRM, sigalarm);
+
+ alarmmsg =
+ "DL_PHYS_ADDR_REQ failed: timeout waiting for DL_PHYS_ADDR_ACK";
+ (void) alarm(10);
+
+ ctl.buf = (char *)dlp;
+ ctl.len = 0;
+ ctl.maxlen = BUFSIZE;
+ flags = 0;
+ if (getmsg(fd, &ctl, NULL, &flags) < 0)
+ syserr("getmsg");
+
+ (void) alarm(0);
+ (void) signal(SIGALRM, SIG_DFL);
+
+ /*
+ * Validate DL_PHYS_ADDR_ACK reply.
+ */
+ if (ctl.len < sizeof (ulong_t))
+ error("DL_PHYS_ADDR_REQ failed: short reply");
+
+ if (dlp->dl_primitive == DL_ERROR_ACK)
+ error("DL_PHYS_ADDR_REQ failed: dl_errno %lu unix_errno %lu",
+ dlp->error_ack.dl_errno, dlp->error_ack.dl_unix_errno);
+
+ if (dlp->dl_primitive != DL_PHYS_ADDR_ACK)
+ error("DL_PHYS_ADDR_REQ failed: dl_primitive 0x%lx received",
+ dlp->dl_primitive);
+
+ if (ctl.len < DL_PHYS_ADDR_ACK_SIZE)
+ error("DL_PHYS_ADDR_REQ failed: short ack received");
+
+ if (dlp->physaddr_ack.dl_addr_length != rarpdev->ifaddrlen) {
+ if (dflag)
+ debug(
+"%s%d DL_PHYS_ADDR_ACK failed: incompatible dl_addr_length: %lu",
+ device, unit, dlp->physaddr_ack.dl_addr_length);
+ (void) close(fd);
+ return (-1);
+ }
+
+ /*
+ * Save our mac address.
+ */
+ if ((rarpdev->lladdr = (uchar_t *)malloc(rarpdev->ifaddrlen)) == NULL) {
+ if (dflag)
+ debug(" %s%d malloc failed: %d bytes", device,
+ unit, rarpdev->ifaddrlen);
+ (void) close(fd);
+ return (-1);
+ }
+
+ eap = (uchar_t *)dlp + dlp->physaddr_ack.dl_addr_offset;
+ (void) memcpy(rarpdev->lladdr, eap, dlp->physaddr_ack.dl_addr_length);
+
+ if (dflag) {
+ str = _link_ntoa(rarpdev->lladdr, str, rarpdev->ifaddrlen,
+ IFT_OTHER);
+ if (str != NULL) {
+ debug("device %s%d lladdress %s", device, unit, str);
+ free(str);
+ }
+ }
+
+ return (fd);
+}
+
+/* ARGSUSED */
+static void
+do_delay_write(void *buf)
+{
+ struct timeval tv;
+ struct rarpreply *rrp;
+ int err;
+
+ for (;;) {
+ if ((err = sema_wait(&delay_sema)) != 0) {
+ if (err == EINTR)
+ continue;
+ error("do_delay_write: sema_wait failed");
+ }
+
+ (void) mutex_lock(&delay_mutex);
+ rrp = delay_list;
+ delay_list = delay_list->next;
+ (void) mutex_unlock(&delay_mutex);
+
+ (void) gettimeofday(&tv, NULL);
+ if (tv.tv_sec < rrp->tv.tv_sec)
+ (void) sleep(rrp->tv.tv_sec - tv.tv_sec);
+
+ if (rarp_write(rrp->rdev->fd, rrp) < 0)
+ error("rarp_write error");
+
+ (void) free(rrp);
+ }
+ /* NOTREACHED */
+}
+
+/* ARGSUSED */
+static void
+delay_write(struct rarpdev *rdev, struct rarpreply *rrp)
+{
+ struct rarpreply *trp;
+
+ (void) mutex_lock(&delay_mutex);
+ if (delay_list == NULL) {
+ delay_list = rrp;
+ } else {
+ trp = delay_list;
+ while (trp->next != NULL)
+ trp = trp->next;
+ trp->next = rrp;
+ }
+ (void) mutex_unlock(&delay_mutex);
+
+ (void) sema_post(&delay_sema);
+}
+
+static int
+rarp_write(int fd, struct rarpreply *rrp)
+{
+ struct strbuf ctl, data;
+ union DL_primitives *dlp;
+ char ctlbuf[BUFSIZE];
+ ushort_t etype = ETHERTYPE_REVARP;
+ int ifaddrlen = rrp->rdev->ifaddrlen;
+
+ /*
+ * Construct DL_UNITDATA_REQ.
+ */
+ /* LINTED pointer */
+ dlp = (union DL_primitives *)ctlbuf;
+ ctl.len = DL_UNITDATA_REQ_SIZE + ifaddrlen + abs(rrp->rdev->ifsaplen);
+ ctl.buf = ctlbuf;
+ data.len = rrp->rdev->ifrarplen;
+ data.buf = (char *)rrp->arprep;
+ if (ctl.len > sizeof (ctlbuf))
+ return (-1);
+
+ dlp->unitdata_req.dl_primitive = DL_UNITDATA_REQ;
+ dlp->unitdata_req.dl_dest_addr_length = ifaddrlen +
+ abs(rrp->rdev->ifsaplen);
+ dlp->unitdata_req.dl_dest_addr_offset = DL_UNITDATA_REQ_SIZE;
+ dlp->unitdata_req.dl_priority.dl_min = 0;
+ dlp->unitdata_req.dl_priority.dl_max = 0;
+ (void) memcpy(ctlbuf + DL_UNITDATA_REQ_SIZE, rrp->lldest, ifaddrlen);
+ (void) memcpy(ctlbuf + DL_UNITDATA_REQ_SIZE + ifaddrlen, &etype,
+ sizeof (etype));
+
+ /*
+ * Send DL_UNITDATA_REQ.
+ */
+ return (putmsg(fd, &ctl, &data, 0));
+}
+
+/*
+ * See if we have a TFTP boot file for this guy. Filenames in TFTP
+ * boot requests are of the form <ipaddr> for Sun-3's and of the form
+ * <ipaddr>.<arch> for all other architectures. Since we don't know
+ * the client's architecture, either format will do.
+ */
+static int
+mightboot(ipaddr_t ipa)
+{
+ char path[MAXPATHL];
+ DIR *dirp;
+ struct dirent *dp;
+ struct dirent *dentry;
+
+ (void) snprintf(path, sizeof (path), "%s/%08X", BOOTDIR, ipa);
+
+ /*
+ * Try a quick access() first.
+ */
+ if (access(path, 0) == 0)
+ return (1);
+
+ /*
+ * Not there, do it the slow way by
+ * reading through the directory.
+ */
+ (void) sprintf(path, "%08X", ipa);
+
+ if (!(dirp = opendir(BOOTDIR)))
+ return (0);
+
+ dentry = (struct dirent *)malloc(sizeof (struct dirent) +
+ pc_name_max + 1);
+ if (dentry == NULL) {
+ error("out of memory");
+ }
+#ifdef _POSIX_PTHREAD_SEMANTICS
+ while ((readdir_r(dirp, dentry, &dp)) != 0) {
+ if (dp == NULL)
+ break;
+#else
+ while ((dp = readdir_r(dirp, dentry)) != NULL) {
+#endif
+ if (strncmp(dp->d_name, path, 8) != 0)
+ continue;
+ if ((strlen(dp->d_name) != 8) && (dp->d_name[8] != '.'))
+ continue;
+ break;
+ }
+
+ (void) closedir(dirp);
+ (void) free(dentry);
+
+ return (dp? 1: 0);
+}
+
+/*
+ * Get our IP address and local netmask.
+ */
+static void
+get_ifdata(char *dev, int unit, ipaddr_t *ipp, ipaddr_t *maskp)
+{
+ int fd;
+ struct ifreq ifr;
+ struct sockaddr_in *sin;
+
+ /* LINTED pointer */
+ sin = (struct sockaddr_in *)&ifr.ifr_addr;
+
+ /*
+ * Open the IP provider.
+ */
+ if ((fd = open(DEVIP, 0)) < 0)
+ syserr(DEVIP);
+
+ /*
+ * Ask IP for our IP address.
+ */
+ (void) snprintf(ifr.ifr_name, sizeof (ifr.ifr_name), "%s%d", dev, unit);
+ if (strioctl(fd, SIOCGIFADDR, -1, sizeof (struct ifreq),
+ (char *)&ifr) < 0)
+ syserr("SIOCGIFADDR");
+ *ipp = (ipaddr_t)ntohl(sin->sin_addr.s_addr);
+
+ if (dflag)
+ debug("device %s%d address %s",
+ dev, unit, inet_ntoa(sin->sin_addr));
+
+ /*
+ * Ask IP for our netmask.
+ */
+ if (strioctl(fd, SIOCGIFNETMASK, -1, sizeof (struct ifreq),
+ (char *)&ifr) < 0)
+ syserr("SIOCGIFNETMASK");
+ *maskp = (ipaddr_t)ntohl(sin->sin_addr.s_addr);
+
+ if (dflag)
+ debug("device %s%d subnet mask %s",
+ dev, unit, inet_ntoa(sin->sin_addr));
+
+ /*
+ * Thankyou ip.
+ */
+ (void) close(fd);
+}
+
+/*
+ * Translate mac address to IP address.
+ * Return 0 on success, nonzero on failure.
+ */
+static int
+get_ipaddr(struct rarpdev *rdev, uchar_t *laddr, uchar_t *ipp, ipaddr_t *ipaddr)
+{
+ char host[MAXHOSTL];
+ char hbuffer[BUFSIZE];
+ struct hostent *hp, res;
+ int herror;
+ struct in_addr addr;
+ char **p;
+ struct ifdev *ifdev;
+
+ if (rdev->ifaddrlen != ETHERADDRL) {
+ if (dflag)
+ debug("%s %s", " can not map non 6 byte hardware ",
+ "address to IP address");
+ return (1);
+ }
+
+ /*
+ * Translate mac address to hostname
+ * and IP address.
+ */
+ if (ether_ntohost(host, (struct ether_addr *)laddr) != 0 ||
+ !(hp = gethostbyname_r(host, &res, hbuffer, sizeof (hbuffer),
+ &herror)) ||
+ hp->h_addrtype != AF_INET || hp->h_length != sizeof (ipaddr_t)) {
+ if (dflag)
+ debug("could not map hardware address to IP address");
+ return (1);
+ }
+
+ /*
+ * Find the IP address on the right net.
+ */
+ for (p = hp->h_addr_list; *p; p++) {
+ (void) memcpy(&addr, *p, sizeof (ipaddr_t));
+ for (ifdev = rdev->ifdev; ifdev != NULL; ifdev = ifdev->next) {
+ if (dflag) {
+ struct in_addr daddr;
+ ipaddr_t netnum;
+
+ netnum = htonl(ifdev->if_netnum);
+ (void) memcpy(&daddr, &netnum,
+ sizeof (ipaddr_t));
+ if (ifdev->lunit == -1)
+ debug(
+"trying physical netnum %s mask %x",
+ inet_ntoa(daddr),
+ ifdev->if_netmask);
+ else
+ debug(
+"trying logical %d netnum %s mask %x",
+ ifdev->lunit,
+ inet_ntoa(daddr),
+ ifdev->if_netmask);
+ }
+ if ((ntohl(addr.s_addr) & ifdev->if_netmask) ==
+ ifdev->if_netnum) {
+ /*
+ * Return the correct IP address.
+ */
+ (void) memcpy(ipp, &addr, sizeof (ipaddr_t));
+
+ /*
+ * Return the interface's ipaddr
+ */
+ (void) memcpy(ipaddr, &ifdev->ipaddr,
+ sizeof (ipaddr_t));
+
+ return (0);
+ }
+ }
+ }
+
+ if (dflag)
+ debug("got host entry but no IP address on this net");
+ return (1);
+}
+
+/*ARGSUSED*/
+void
+sigalarm(int i)
+{
+ error(alarmmsg);
+}
+
+static int
+strioctl(int fd, int cmd, int timout, int len, char *dp)
+{
+ struct strioctl si;
+
+ si.ic_cmd = cmd;
+ si.ic_timout = timout;
+ si.ic_len = len;
+ si.ic_dp = dp;
+ return (ioctl(fd, I_STR, &si));
+}
+
+static void
+usage()
+{
+ error("Usage: %s [ -ad ] device unit", cmdname);
+}
+
+static void
+syserr(s)
+char *s;
+{
+ char buf[256];
+ int status = 1;
+
+ (void) snprintf(buf, sizeof (buf), "%s: %s", s, strerror(errno));
+ (void) fprintf(stderr, "%s: %s\n", cmdname, buf);
+ syslog(LOG_ERR, "%s", buf);
+ thr_exit(&status);
+}
+
+/*PRINTFLIKE1*/
+static void
+error(char *fmt, ...)
+{
+ char buf[256];
+ va_list ap;
+ int status = 1;
+
+ va_start(ap, fmt);
+ (void) vsprintf(buf, fmt, ap);
+ va_end(ap);
+ (void) fprintf(stderr, "%s: %s\n", cmdname, buf);
+ syslog(LOG_ERR, buf);
+ thr_exit(&status);
+}
+
+/*PRINTFLIKE1*/
+static void
+debug(char *fmt, ...)
+{
+ va_list ap;
+
+ (void) mutex_lock(&debug_mutex);
+ va_start(ap, fmt);
+ (void) fprintf(stderr, "%s:[%u] ", cmdname, thr_self());
+ (void) vfprintf(stderr, fmt, ap);
+ (void) fprintf(stderr, "\n");
+ va_end(ap);
+ (void) mutex_unlock(&debug_mutex);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.rdisc.c b/usr/src/cmd/cmd-inet/usr.sbin/in.rdisc.c
new file mode 100644
index 0000000000..b97fc53e56
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.rdisc.c
@@ -0,0 +1,2279 @@
+/*
+ * Copyright 1991-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 1987 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <errno.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/file.h>
+
+#include <sys/ioctl.h>
+#include <net/if.h>
+
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+
+#include <fcntl.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <assert.h>
+
+#ifdef lint
+#define ALIGN(ptr) (ptr ? 0 : 0)
+#else
+#define ALIGN(ptr) (ptr)
+#endif
+
+#ifdef SYSV
+#define signal(s, f) sigset(s, (void (*)(int))f)
+#define random() rand()
+#endif
+
+#define ALL_HOSTS_ADDRESS "224.0.0.1"
+#define ALL_ROUTERS_ADDRESS "224.0.0.2"
+
+#define MAXIFS 256
+
+/* For router advertisement */
+struct icmp_ra {
+ uchar_t icmp_type; /* type of message, see below */
+ uchar_t icmp_code; /* type sub code */
+ ushort_t icmp_cksum; /* ones complement cksum of struct */
+ uchar_t icmp_num_addrs;
+ uchar_t icmp_wpa; /* Words per address */
+ short icmp_lifetime;
+};
+
+struct icmp_ra_addr {
+ ulong_t addr;
+ ulong_t preference;
+};
+
+/* Router constants */
+#define MAX_INITIAL_ADVERT_INTERVAL 16
+#define MAX_INITIAL_ADVERTISEMENTS 3
+#define MAX_RESPONSE_DELAY 2 /* Not used */
+
+/* Host constants */
+#define MAX_SOLICITATIONS 3
+#define SOLICITATION_INTERVAL 3
+#define MAX_SOLICITATION_DELAY 1 /* Not used */
+
+#define IGNORE_PREFERENCE 0x80000000 /* Maximum negative */
+
+#define MAX_ADV_INT 600
+
+
+/*
+ * A doubly linked list of all physical interfaces that each contain a
+ * doubly linked list of logical interfaces aka IP addresses.
+ */
+struct phyint {
+ char pi_name[IFNAMSIZ]; /* Used to identify it */
+ int pi_state; /* See below */
+ struct logint *pi_logical_first;
+ struct logint *pi_logical_last;
+ struct phyint *pi_next;
+ struct phyint *pi_prev;
+};
+
+struct logint {
+ char li_name[IFNAMSIZ]; /* Used to identify it */
+ int li_state; /* See below */
+ struct in_addr li_address; /* Used to identify the interface */
+ struct in_addr li_localaddr; /* Actual address of the interface */
+ int li_preference;
+ int li_index; /* interface index (SIOCGLIFINDEX) */
+ uint64_t li_flags;
+ struct in_addr li_bcastaddr;
+ struct in_addr li_remoteaddr;
+ struct in_addr li_netmask;
+ struct logint *li_next; /* Next logical for this physical */
+ struct logint *li_prev; /* Prev logical for this physical */
+ struct phyint *li_physical; /* Back pointer */
+};
+
+struct phyint *phyint;
+int num_usable_interfaces; /* Num used for sending/receiving */
+
+/*
+ * State bits
+ */
+#define ST_MARKED 0x01 /* To determine removed interfaces */
+#define ST_JOINED 0x02 /* Joined multicast group */
+#define ST_DELETED 0x04 /* Interface should be ignored */
+
+/* Function prototypes */
+static void solicitor(struct sockaddr_in *sin);
+static void advertise(struct sockaddr_in *sin);
+
+static void age_table(int time);
+static void flush_unreachable_routers(void);
+static void record_router(struct in_addr router, long preference, int ttl);
+
+static void add_route(struct in_addr addr);
+static void del_route(struct in_addr addr);
+static void rtioctl(struct in_addr addr, int op);
+
+static int support_multicast(void);
+static int sendbcast(int s, char *packet, int packetlen);
+static int sendbcastif(int s, char *packet, int packetlen,
+ struct logint *li);
+static int sendmcast(int s, char *packet, int packetlen,
+ struct sockaddr_in *sin);
+static int sendmcastif(int s, char *packet, int packetlen,
+ struct sockaddr_in *sin, struct logint *li);
+
+static int ismulticast(struct sockaddr_in *sin);
+static int isbroadcast(struct sockaddr_in *sin);
+int in_cksum(ushort_t *addr, int len);
+static struct logint *find_directly_connected_logint(struct in_addr in,
+ struct phyint *pi);
+static void force_preference(int preference);
+
+static void timer(void);
+static void finish(void);
+static void report(void);
+static void report_interfaces(void);
+static void report_routes(void);
+static void reinitifs(void);
+
+static struct phyint *find_phyint(char *name);
+static struct phyint *add_phyint(char *name);
+static void free_phyint(struct phyint *pi);
+static struct logint *find_logint(struct phyint *pi, char *name);
+static struct logint *add_logint(struct phyint *pi, char *name);
+static void free_logint(struct logint *li);
+
+static void deleted_phyint(struct phyint *pi, int s,
+ struct sockaddr_in *joinaddr);
+static void added_logint(struct logint *li, int s,
+ struct sockaddr_in *joinaddr);
+static void deleted_logint(struct logint *li, struct logint *newli, int s,
+ struct sockaddr_in *joinaddr);
+
+static int initifs(int s, struct sockaddr_in *joinaddr, int preference);
+static boolean_t getconfig(int sock, uint64_t if_flags, struct sockaddr *addr,
+ struct ifreq *ifr, struct logint *li);
+
+static void pr_pack(char *buf, int cc, struct sockaddr_in *from);
+char *pr_name(struct in_addr addr);
+char *pr_type(int t);
+
+static void initlog(void);
+void logerr(), logtrace(), logdebug(), logperror();
+
+/* Local variables */
+
+#define MAXPACKET 4096 /* max packet size */
+uchar_t packet[MAXPACKET];
+
+char usage[] =
+"Usage: rdisc [-s] [-v] [-f] [-a] [send_address] [receive_address]\n"
+" rdisc -r [-v] [-p <preference>] [-T <secs>] \n"
+" [send_address] [receive_address]\n";
+
+
+int s; /* Socket file descriptor */
+struct sockaddr_in whereto; /* Address to send to */
+struct sockaddr_in g_joinaddr; /* Address to receive on */
+char *sendaddress, *recvaddress; /* For logging purposes only */
+
+/* Common variables */
+int verbose = 0;
+int debug = 0;
+int trace = 0;
+int start_solicit = 0; /* -s parameter set */
+int solicit = 0; /* Are we currently sending solicitations? */
+int responder;
+int ntransmitted = 0;
+int nreceived = 0;
+int forever = 0; /* Never give up on host. If 0 defer fork until */
+ /* first response. */
+
+/* Router variables */
+int max_adv_int = MAX_ADV_INT;
+int min_adv_int;
+int lifetime;
+int initial_advert_interval = MAX_INITIAL_ADVERT_INTERVAL;
+int initial_advertisements = MAX_INITIAL_ADVERTISEMENTS;
+ulong_t g_preference = 0; /* Setable with -p option */
+
+/* Host variables */
+int max_solicitations = MAX_SOLICITATIONS;
+unsigned int solicitation_interval = SOLICITATION_INTERVAL;
+int best_preference = 1; /* Set to record only the router(s) with the */
+ /* best preference in the kernel. Not set */
+ /* puts all routes in the kernel. */
+
+
+static void
+prusage()
+{
+ (void) fprintf(stderr, usage);
+ exit(1);
+}
+
+static int sock = -1;
+
+static void
+do_fork()
+{
+ int t;
+
+ if (trace)
+ return;
+
+ if (fork())
+ exit(0);
+ for (t = 0; t < 20; t++)
+ if (t != s)
+ (void) close(t);
+ sock = -1;
+ (void) open("/", 0);
+ (void) dup2(0, 1);
+ (void) dup2(0, 2);
+#ifndef SYSV
+ t = open("/dev/tty", 2);
+ if (t >= 0) {
+ (void) ioctl(t, TIOCNOTTY, (char *)0);
+ (void) close(t);
+ }
+#else
+ (void) setpgrp();
+#endif
+ initlog();
+}
+
+/*
+ * M A I N
+ */
+int
+main(int argc, char *argv[])
+{
+#ifndef SYSV
+ struct sigvec sv;
+#endif
+ struct sockaddr_in from;
+ char **av = argv;
+ struct sockaddr_in *to = &whereto;
+ ulong_t val;
+
+ min_adv_int = (max_adv_int * 3 / 4);
+ lifetime = (3*max_adv_int);
+
+ argc--, av++;
+ while (argc > 0 && *av[0] == '-') {
+ while (*++av[0])
+ switch (*av[0]) {
+ case 'd':
+ debug = 1;
+ break;
+ case 't':
+ trace = 1;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case 's':
+ start_solicit = solicit = 1;
+ break;
+ case 'r':
+ responder = 1;
+ break;
+ case 'a':
+ best_preference = 0;
+ break;
+ case 'b':
+ best_preference = 1;
+ break;
+ case 'f':
+ forever = 1;
+ break;
+ case 'T':
+ argc--, av++;
+ if (argc != 0) {
+ val = strtol(av[0], (char **)NULL, 0);
+ if (val < 4 || val > 1800) {
+ (void) fprintf(stderr,
+ "Bad Max Advertisement Interval\n");
+ exit(1);
+ }
+ max_adv_int = val;
+ min_adv_int = (max_adv_int * 3 / 4);
+ lifetime = (3*max_adv_int);
+ } else {
+ prusage();
+ /* NOTREACHED */
+ }
+ goto next;
+ case 'p':
+ argc--, av++;
+ if (argc != 0) {
+ val = strtoul(av[0], (char **)NULL, 0);
+ g_preference = val;
+ } else {
+ prusage();
+ /* NOTREACHED */
+ }
+ goto next;
+ default:
+ prusage();
+ /* NOTREACHED */
+ }
+ next:
+ argc--, av++;
+ }
+ if (argc < 1) {
+ if (support_multicast()) {
+ if (responder)
+ sendaddress = ALL_HOSTS_ADDRESS;
+ else
+ sendaddress = ALL_ROUTERS_ADDRESS;
+ } else
+ sendaddress = "255.255.255.255";
+ } else {
+ sendaddress = av[0];
+ argc--;
+ }
+ if (argc < 1) {
+ if (support_multicast()) {
+ if (responder)
+ recvaddress = ALL_ROUTERS_ADDRESS;
+ else
+ recvaddress = ALL_HOSTS_ADDRESS;
+ } else
+ recvaddress = "255.255.255.255";
+ } else {
+ recvaddress = av[0];
+ argc--;
+ }
+ if (argc != 0) {
+ (void) fprintf(stderr, "Extra paramaters\n");
+ prusage();
+ /* NOTREACHED */
+ }
+
+ if (solicit && responder) {
+ prusage();
+ /* NOTREACHED */
+ }
+
+ if (!(solicit && !forever)) {
+ do_fork();
+ }
+
+ bzero((char *)&whereto, sizeof (struct sockaddr_in));
+ to->sin_family = AF_INET;
+ to->sin_addr.s_addr = inet_addr(sendaddress);
+ if (to->sin_addr.s_addr == (unsigned long)-1) {
+ logerr("in.rdisc: bad address %s\n", sendaddress);
+ exit(1);
+ }
+
+ bzero((char *)&g_joinaddr, sizeof (struct sockaddr_in));
+ g_joinaddr.sin_family = AF_INET;
+ g_joinaddr.sin_addr.s_addr = inet_addr(recvaddress);
+ if (g_joinaddr.sin_addr.s_addr == (unsigned long)-1) {
+ logerr("in.rdisc: bad address %s\n", recvaddress);
+ exit(1);
+ }
+
+ if (responder) {
+#ifdef SYSV
+ srand((int)gethostid());
+#else
+ srandom((int)gethostid());
+#endif
+ }
+
+ if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) {
+ logperror("socket");
+ exit(5);
+ }
+
+#ifdef SYSV
+ setvbuf(stdout, NULL, _IOLBF, 0);
+#else
+ setlinebuf(stdout);
+#endif
+
+ (void) signal(SIGINT, finish);
+ (void) signal(SIGTERM, finish);
+ (void) signal(SIGHUP, reinitifs);
+ (void) signal(SIGUSR1, report);
+
+ if (initifs(s, &g_joinaddr, g_preference) < 0) {
+ logerr("Failed initializing interfaces\n");
+ exit(2);
+ }
+
+ /*
+ * If there are no usable interfaces and we are soliciting
+ * waiting for to return an exit code (i.e. forever isn't set)
+ * give up immediately.
+ */
+ if (num_usable_interfaces == 0 && solicit && !forever) {
+ logerr("in.rdisc: No interfaces up\n");
+ exit(5);
+ }
+
+#ifdef SYSV
+ (void) signal(SIGALRM, timer);
+#else
+ /*
+ * Make sure that this signal actually interrupts (rather than
+ * restarts) the recvfrom call below.
+ */
+ sv.sv_handler = timer;
+ sv.sv_mask = 0;
+ sv.sv_flags = SV_INTERRUPT;
+ (void) sigvec(SIGALRM, &sv, (struct sigvec *)NULL);
+#endif
+ timer(); /* start things going */
+
+ for (;;) {
+ int len = sizeof (packet);
+ socklen_t fromlen = (socklen_t)sizeof (from);
+ int cc;
+ sigset_t newmask, oldmask;
+
+ if ((cc = recvfrom(s, (char *)packet, len, 0,
+ (struct sockaddr *)&from,
+ &fromlen)) < 0) {
+ if (errno == EINTR)
+ continue;
+ logperror("recvfrom");
+ continue;
+ }
+ /* Block all signals while processing */
+ (void) sigfillset(&newmask);
+ (void) sigprocmask(SIG_SETMASK, &newmask, &oldmask);
+ pr_pack((char *)packet, cc, &from);
+ (void) sigprocmask(SIG_SETMASK, &oldmask, NULL);
+ }
+ /* NOTREACHED */
+}
+
+static void
+report(void)
+{
+ report_interfaces();
+ report_routes();
+}
+
+#define TIMER_INTERVAL 6
+#define GETIFCONF_TIMER 30
+
+static left_until_advertise;
+
+/* Called every TIMER_INTERVAL */
+static void
+timer(void)
+{
+ static time;
+ static left_until_getifconf;
+ static left_until_solicit;
+
+ time += TIMER_INTERVAL;
+
+ left_until_getifconf -= TIMER_INTERVAL;
+ left_until_advertise -= TIMER_INTERVAL;
+ left_until_solicit -= TIMER_INTERVAL;
+
+ if (left_until_getifconf < 0) {
+ (void) initifs(s, &g_joinaddr, g_preference);
+ left_until_getifconf = GETIFCONF_TIMER;
+ }
+ if (responder && left_until_advertise <= 0) {
+ ntransmitted++;
+ advertise(&whereto);
+ if (ntransmitted < initial_advertisements)
+ left_until_advertise = initial_advert_interval;
+ else
+ left_until_advertise = min_adv_int +
+ ((max_adv_int - min_adv_int) *
+ (random() % 1000)/1000);
+ } else if (solicit && left_until_solicit <= 0) {
+ if (ntransmitted < max_solicitations) {
+ ntransmitted++;
+ solicitor(&whereto);
+ left_until_solicit = solicitation_interval;
+ } else {
+ solicit = 0;
+ if (!forever && nreceived == 0)
+ exit(5);
+ }
+ }
+ age_table(TIMER_INTERVAL);
+ (void) alarm(TIMER_INTERVAL);
+}
+
+/*
+ * S O L I C I T O R
+ *
+ * Compose and transmit an ICMP ROUTER SOLICITATION REQUEST packet.
+ * The IP packet will be added on by the kernel.
+ */
+static void
+solicitor(struct sockaddr_in *sin)
+{
+ static uchar_t outpack[MAXPACKET];
+ register struct icmp *icp = (struct icmp *)ALIGN(outpack);
+ int packetlen, i;
+
+ if (verbose) {
+ logtrace("Sending solicitation to %s\n",
+ pr_name(sin->sin_addr));
+ }
+ icp->icmp_type = ICMP_ROUTERSOLICIT;
+ icp->icmp_code = 0;
+ icp->icmp_cksum = 0;
+ icp->icmp_void = 0; /* Reserved */
+ packetlen = 8;
+
+ /* Compute ICMP checksum here */
+ icp->icmp_cksum = in_cksum((ushort_t *)icp, packetlen);
+
+ if (isbroadcast(sin))
+ i = sendbcast(s, (char *)outpack, packetlen);
+ else if (ismulticast(sin))
+ i = sendmcast(s, (char *)outpack, packetlen, sin);
+ else {
+ struct logint *li;
+
+ li = find_directly_connected_logint(sin->sin_addr, NULL);
+ if (li != NULL && (li->li_flags & IFF_NORTEXCH)) {
+ if (verbose) {
+ logtrace("Suppressing sending %s on %s "
+ "(no route exchange on interface)\n",
+ pr_type((int)icp->icmp_type), li->li_name);
+ }
+ return;
+ } else {
+ i = sendto(s, (char *)outpack, packetlen, 0,
+ (struct sockaddr *)sin, sizeof (struct sockaddr));
+ }
+ }
+
+ if (i < 0 || i != packetlen) {
+ if (i < 0) {
+ logperror("sendto");
+ }
+ logerr("wrote %s %d chars, ret=%d\n",
+ sendaddress, packetlen, i);
+ }
+}
+
+/*
+ * A D V E R T I S E
+ *
+ * Compose and transmit an ICMP ROUTER ADVERTISEMENT packet.
+ * The IP packet will be added on by the kernel.
+ */
+static void
+advertise(struct sockaddr_in *sin)
+{
+ struct phyint *pi;
+ struct logint *li, *li_tmp;
+ static uchar_t outpack[MAXPACKET];
+ register struct icmp_ra *rap = (struct icmp_ra *)ALIGN(outpack);
+ struct icmp_ra_addr *ap;
+ int packetlen, cc;
+
+ if (verbose) {
+ logtrace("Sending advertisement to %s\n",
+ pr_name(sin->sin_addr));
+ }
+
+ for (pi = phyint; pi != NULL; pi = pi->pi_next) {
+ rap->icmp_type = ICMP_ROUTERADVERT;
+ rap->icmp_code = 0;
+ rap->icmp_cksum = 0;
+ rap->icmp_num_addrs = 0;
+ rap->icmp_wpa = 2;
+ rap->icmp_lifetime = htons(lifetime);
+ packetlen = ICMP_MINLEN;
+
+ for (li = pi->pi_logical_first; li != NULL; li = li->li_next) {
+ if (li->li_state & ST_DELETED)
+ continue;
+
+ /*
+ * XXX Just truncate the list of addresses.
+ * Should probably send multiple packets.
+ */
+ if (packetlen + rap->icmp_wpa * 4 > sizeof (outpack)) {
+ if (debug)
+ logdebug("full packet: %d addresses\n",
+ rap->icmp_num_addrs);
+ break;
+ }
+ ap = (struct icmp_ra_addr *)ALIGN(outpack + packetlen);
+ ap->addr = li->li_localaddr.s_addr;
+ ap->preference = htonl(li->li_preference);
+ packetlen += rap->icmp_wpa * 4;
+ rap->icmp_num_addrs++;
+ }
+
+ if (rap->icmp_num_addrs == 0)
+ continue;
+
+ /* Compute ICMP checksum here */
+ rap->icmp_cksum = in_cksum((ushort_t *)rap, packetlen);
+
+ if (isbroadcast(sin))
+ cc = sendbcastif(s, (char *)outpack, packetlen,
+ pi->pi_logical_first);
+ else if (ismulticast(sin))
+ cc = sendmcastif(s, (char *)outpack, packetlen, sin,
+ pi->pi_logical_first);
+ else {
+ /*
+ * Verify that the physical interface matches the
+ * destination address.
+ */
+ li_tmp = find_directly_connected_logint(sin->sin_addr,
+ pi);
+ if (li_tmp == NULL)
+ continue;
+ if (li_tmp->li_flags & IFF_NORTEXCH) {
+ if (verbose) {
+ logtrace("Suppressing sending %s on %s "
+ "(no route exchange on "
+ "interface)\n",
+ pr_type((int)rap->icmp_type),
+ li_tmp->li_name);
+ }
+ continue;
+ }
+ if (debug) {
+ logdebug("Unicast to %s ",
+ pr_name(sin->sin_addr));
+ logdebug("on interface %s\n", pi->pi_name);
+ }
+ cc = sendto(s, (char *)outpack, packetlen, 0,
+ (struct sockaddr *)sin, sizeof (struct sockaddr));
+ }
+ if (cc < 0 || cc != packetlen) {
+ if (cc < 0) {
+ logperror("sendto");
+ } else {
+ logerr("wrote %s %d chars, ret=%d\n",
+ sendaddress, packetlen, cc);
+ }
+ }
+ }
+}
+
+/*
+ * P R _ T Y P E
+ *
+ * Convert an ICMP "type" field to a printable string.
+ */
+char *
+pr_type(int t)
+{
+ static char *ttab[] = {
+ "Echo Reply",
+ "ICMP 1",
+ "ICMP 2",
+ "Dest Unreachable",
+ "Source Quench",
+ "Redirect",
+ "ICMP 6",
+ "ICMP 7",
+ "Echo",
+ "Router Advertise",
+ "Router Solicitation",
+ "Time Exceeded",
+ "Parameter Problem",
+ "Timestamp",
+ "Timestamp Reply",
+ "Info Request",
+ "Info Reply",
+ "Netmask Request",
+ "Netmask Reply"
+ };
+
+ if (t < 0 || t > 16)
+ return ("OUT-OF-RANGE");
+
+ return (ttab[t]);
+}
+
+/*
+ * P R _ N A M E
+ *
+ * Return a string name for the given IP address.
+ */
+char *
+pr_name(struct in_addr addr)
+{
+ struct hostent *phe;
+ static char buf[256];
+
+ phe = gethostbyaddr((char *)&addr.s_addr, 4, AF_INET);
+ if (phe == NULL)
+ return (inet_ntoa(addr));
+ (void) sprintf(buf, "%s (%s)", phe->h_name, inet_ntoa(addr));
+ return (buf);
+}
+
+/*
+ * P R _ P A C K
+ *
+ * Print out the packet, if it came from us. This logic is necessary
+ * because ALL readers of the ICMP socket get a copy of ALL ICMP packets
+ * which arrive ('tis only fair). This permits multiple copies of this
+ * program to be run without having intermingled output (or statistics!).
+ */
+static void
+pr_pack(char *buf, int cc, struct sockaddr_in *from)
+{
+ struct ip *ip;
+ register struct icmp *icp;
+ register int i;
+ int hlen;
+ struct logint *li;
+
+ ip = (struct ip *)ALIGN(buf);
+ hlen = ip->ip_hl << 2;
+ if (cc < hlen + ICMP_MINLEN) {
+ if (verbose)
+ logtrace("packet too short (%d bytes) from %s\n", cc,
+ pr_name(from->sin_addr));
+ return;
+ }
+
+ cc -= hlen;
+ icp = (struct icmp *)ALIGN(buf + hlen);
+
+ /*
+ * Let's check if IFF_NORTEXCH flag is set on the interface which
+ * recevied this packet.
+ * TODO: this code can be re-written using one socket per interface
+ * to determine which interface the packet is recevied.
+ */
+ li = find_directly_connected_logint(ip->ip_src, NULL);
+ if (li != NULL && (li->li_flags & IFF_NORTEXCH)) {
+ if (verbose) {
+ logtrace("Ignoring received %s on %s "
+ "(no route exchange on interface)",
+ pr_type((int)icp->icmp_type), li->li_name);
+ }
+ return;
+ }
+
+ if (ip->ip_p == 0) {
+ /*
+ * Assume that we are running on a pre-4.3BSD system
+ * such as SunOS before 4.0
+ */
+ icp = (struct icmp *)ALIGN(buf);
+ }
+ switch (icp->icmp_type) {
+ case ICMP_ROUTERADVERT: {
+ struct icmp_ra *rap = (struct icmp_ra *)ALIGN(icp);
+ struct icmp_ra_addr *ap;
+
+ if (responder)
+ break;
+
+ /* TBD verify that the link is multicast or broadcast */
+ /* XXX Find out the link it came in over? */
+#ifdef notdef
+ if (debug) {
+ logdebug("ROUTER_ADVERTISEMENT: \n");
+ pr_hex(buf+hlen, cc);
+ }
+#endif /* notdef */
+ if (in_cksum((ushort_t *)ALIGN(buf+hlen), cc)) {
+ if (verbose)
+ logtrace("ICMP %s from %s: Bad checksum\n",
+ pr_type((int)rap->icmp_type),
+ pr_name(from->sin_addr));
+ return;
+ }
+ if (rap->icmp_code != 0) {
+ if (verbose)
+ logtrace("ICMP %s from %s: Code = %d\n",
+ pr_type((int)rap->icmp_type),
+ pr_name(from->sin_addr),
+ rap->icmp_code);
+ return;
+ }
+ if (rap->icmp_num_addrs < 1) {
+ if (verbose)
+ logtrace("ICMP %s from %s: No addresses\n",
+ pr_type((int)rap->icmp_type),
+ pr_name(from->sin_addr));
+ return;
+ }
+ if (rap->icmp_wpa < 2) {
+ if (verbose)
+ logtrace("ICMP %s from %s: Words/addr = %d\n",
+ pr_type((int)rap->icmp_type),
+ pr_name(from->sin_addr),
+ rap->icmp_wpa);
+ return;
+ }
+ if ((unsigned)cc <
+ ICMP_MINLEN + rap->icmp_num_addrs * rap->icmp_wpa * 4) {
+ if (verbose)
+ logtrace("ICMP %s from %s: Too short %d, %d\n",
+ pr_type((int)rap->icmp_type),
+ pr_name(from->sin_addr),
+ cc,
+ ICMP_MINLEN +
+ rap->icmp_num_addrs *
+ rap->icmp_wpa * 4);
+ return;
+ }
+ rap->icmp_lifetime = ntohs(rap->icmp_lifetime);
+ if ((rap->icmp_lifetime < 4 && rap->icmp_lifetime != 0) ||
+ rap->icmp_lifetime > 9000) {
+ if (verbose)
+ logtrace("ICMP %s from %s: Invalid lifetime %d\n",
+ pr_type((int)rap->icmp_type),
+ pr_name(from->sin_addr),
+ rap->icmp_lifetime);
+ return;
+ }
+ if (verbose)
+ logtrace("ICMP %s from %s, lifetime %d\n",
+ pr_type((int)rap->icmp_type),
+ pr_name(from->sin_addr),
+ rap->icmp_lifetime);
+
+ /*
+ * Check that at least one router address is a neighbor
+ * on the arriving link.
+ */
+ for (i = 0; (unsigned)i < rap->icmp_num_addrs; i++) {
+ struct in_addr ina;
+ ap = (struct icmp_ra_addr *)
+ ALIGN(buf + hlen + ICMP_MINLEN +
+ i * rap->icmp_wpa * 4);
+ ap->preference = ntohl(ap->preference);
+ ina.s_addr = ap->addr;
+ if (verbose)
+ logtrace("\taddress %s, preference 0x%x\n",
+ pr_name(ina),
+ ap->preference);
+ if (!responder) {
+ if (find_directly_connected_logint(ina, NULL) !=
+ NULL) {
+ record_router(ina,
+ (long)ap->preference,
+ rap->icmp_lifetime);
+ }
+ }
+ }
+ nreceived++;
+ if (!forever) {
+ (void) alarm(0);
+ do_fork();
+ forever = 1;
+ (void) alarm(TIMER_INTERVAL);
+ }
+ break;
+ }
+
+ case ICMP_ROUTERSOLICIT: {
+ struct sockaddr_in sin;
+
+ if (!responder)
+ break;
+
+ /* TBD verify that the link is multicast or broadcast */
+ /* XXX Find out the link it came in over? */
+#ifdef notdef
+ if (debug) {
+ logdebug("ROUTER_SOLICITATION: \n");
+ pr_hex(buf+hlen, cc);
+ }
+#endif /* notdef */
+ if (in_cksum((ushort_t *)ALIGN(buf+hlen), cc)) {
+ if (verbose)
+ logtrace("ICMP %s from %s: Bad checksum\n",
+ pr_type((int)icp->icmp_type),
+ pr_name(from->sin_addr));
+ return;
+ }
+ if (icp->icmp_code != 0) {
+ if (verbose)
+ logtrace("ICMP %s from %s: Code = %d\n",
+ pr_type((int)icp->icmp_type),
+ pr_name(from->sin_addr),
+ icp->icmp_code);
+ return;
+ }
+
+ if (cc < ICMP_MINLEN) {
+ if (verbose)
+ logtrace("ICMP %s from %s: Too short %d, %d\n",
+ pr_type((int)icp->icmp_type),
+ pr_name(from->sin_addr),
+ cc,
+ ICMP_MINLEN);
+ return;
+ }
+
+ if (verbose)
+ logtrace("ICMP %s from %s\n",
+ pr_type((int)icp->icmp_type),
+ pr_name(from->sin_addr));
+
+ if (!responder)
+ break;
+
+ /*
+ * Check that ip_src is either a neighbor
+ * on the arriving link or 0.
+ */
+ sin.sin_family = AF_INET;
+ if (ip->ip_src.s_addr == 0) {
+ /*
+ * If it was sent to the broadcast address we respond
+ * to the broadcast address.
+ */
+ if (IN_CLASSD(ntohl(ip->ip_dst.s_addr))) {
+ sin.sin_addr.s_addr =
+ htonl(INADDR_ALLHOSTS_GROUP);
+ } else
+ sin.sin_addr.s_addr = htonl(INADDR_BROADCAST);
+ /* Restart the timer when we broadcast */
+ left_until_advertise = min_adv_int +
+ ((max_adv_int - min_adv_int)
+ * (random() % 1000)/1000);
+ } else {
+ if (li == NULL) {
+ if (verbose)
+ logtrace("ICMP %s from %s: %s\n",
+ pr_type((int)icp->icmp_type),
+ pr_name(from->sin_addr),
+ "source not directly connected");
+ break;
+ }
+ sin.sin_addr.s_addr = ip->ip_src.s_addr;
+ }
+ nreceived++;
+ ntransmitted++;
+ advertise(&sin);
+ break;
+ }
+ }
+}
+
+
+/*
+ * I N _ C K S U M
+ *
+ * Checksum routine for Internet Protocol family headers (C Version)
+ *
+ */
+int
+in_cksum(ushort_t *addr, int len)
+{
+ register int nleft = len;
+ register ushort_t *w = addr;
+ register ushort_t answer;
+ ushort_t odd_byte = 0;
+ register int sum = 0;
+
+ /*
+ * Our algorithm is simple, using a 32 bit accumulator (sum),
+ * we add sequential 16 bit words to it, and at the end, fold
+ * back all the carry bits from the top 16 bits into the lower
+ * 16 bits.
+ */
+ while (nleft > 1) {
+ sum += *w++;
+ nleft -= 2;
+ }
+
+ /* mop up an odd byte, if necessary */
+ if (nleft == 1) {
+ *(uchar_t *)(&odd_byte) = *(uchar_t *)w;
+ sum += odd_byte;
+ }
+
+ /*
+ * add back carry outs from top 16 bits to low 16 bits
+ */
+ sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
+ sum += (sum >> 16); /* add carry */
+ answer = ~sum; /* truncate to 16 bits */
+ return (answer);
+}
+
+/*
+ * F I N I S H
+ *
+ * Print out statistics, and give up.
+ * Heavily buffered stdio is used here, so that all the statistics
+ * will be written with 1 sys-write call. This is nice when more
+ * than one copy of the program is running on a terminal; it prevents
+ * the statistics output from becoming intermingled.
+ */
+static void
+finish(void)
+{
+ if (responder) {
+ /*
+ * Send out a packet with a preference so that all
+ * hosts will know that we are dead.
+ */
+ logerr("terminated\n");
+ force_preference(IGNORE_PREFERENCE);
+ ntransmitted++;
+ advertise(&whereto);
+ }
+ if (verbose) {
+ logtrace("\n----%s rdisc Statistics----\n", sendaddress);
+ logtrace("%d packets transmitted, ", ntransmitted);
+ logtrace("%d packets received, ", nreceived);
+ logtrace("\n");
+ }
+ (void) fflush(stdout);
+ exit(0);
+}
+
+#include <ctype.h>
+
+#ifdef notdef
+int
+pr_hex(unsigned char *data, int len)
+{
+ FILE *out;
+
+ out = stdout;
+
+ while (len) {
+ register int i;
+ char charstring[17];
+
+ (void) strcpy(charstring, " "); /* 16 spaces */
+ for (i = 0; i < 16; i++) {
+ /*
+ * output the bytes one at a time,
+ * not going past "len" bytes
+ */
+ if (len) {
+ char ch = *data & 0x7f; /* strip parity */
+ if (!isprint((uchar_t)ch))
+ ch = ' '; /* ensure printable */
+ charstring[i] = ch;
+ (void) fprintf(out, "%02x ", *data++);
+ len--;
+ } else
+ (void) fprintf(out, " ");
+ if (i == 7)
+ (void) fprintf(out, " ");
+ }
+
+ (void) fprintf(out, " *%s*\n", charstring);
+ }
+}
+#endif /* notdef */
+
+static int
+isbroadcast(struct sockaddr_in *sin)
+{
+ return (sin->sin_addr.s_addr == htonl(INADDR_BROADCAST));
+}
+
+static int
+ismulticast(struct sockaddr_in *sin)
+{
+ return (IN_CLASSD(ntohl(sin->sin_addr.s_addr)));
+}
+
+/* From libc/rpc/pmap_rmt.c */
+
+
+/* Only send once per physical interface */
+static int
+sendbcast(int s, char *packet, int packetlen)
+{
+ struct phyint *pi;
+ struct logint *li;
+ boolean_t bcast;
+ int cc;
+
+ for (pi = phyint; pi != NULL; pi = pi->pi_next) {
+ bcast = B_FALSE;
+ for (li = pi->pi_logical_first; li != NULL; li = li->li_next) {
+ if (li->li_state & ST_DELETED)
+ continue;
+
+ if (li->li_flags & IFF_BROADCAST) {
+ bcast = B_TRUE;
+ break;
+ }
+ }
+ if (!bcast)
+ continue;
+ cc = sendbcastif(s, packet, packetlen, li);
+ if (cc != packetlen) {
+ return (cc);
+ }
+ }
+ return (packetlen);
+}
+
+static int
+sendbcastif(int s, char *packet, int packetlen, struct logint *li)
+{
+ int cc;
+ struct sockaddr_in baddr;
+ struct icmp *icp = (struct icmp *)ALIGN(packet);
+
+ baddr.sin_family = AF_INET;
+
+ if ((li->li_flags & IFF_BROADCAST) == 0) {
+ if (verbose) {
+ logtrace("Suppressing sending %s on %s "
+ "(interface is not broadcast capable)\n",
+ pr_type((int)icp->icmp_type), li->li_name);
+ }
+ return (packetlen);
+ }
+ if (li->li_flags & IFF_NORTEXCH) {
+ if (verbose) {
+ logtrace("Suppressing sending %s on %s "
+ "(no route exchange on interface)\n",
+ pr_type((int)icp->icmp_type), li->li_name);
+ }
+ return (packetlen);
+ }
+
+ baddr.sin_addr = li->li_bcastaddr;
+ if (debug)
+ logdebug("Broadcast to %s\n",
+ pr_name(baddr.sin_addr));
+ cc = sendto(s, packet, packetlen, 0,
+ (struct sockaddr *)&baddr, sizeof (struct sockaddr));
+ if (cc != packetlen) {
+ logperror("sendbcast: sendto");
+ logerr("Cannot send broadcast packet to %s\n",
+ pr_name(baddr.sin_addr));
+ }
+ return (cc);
+}
+
+static int
+sendmcast(int s, char *packet, int packetlen, struct sockaddr_in *sin)
+{
+ struct phyint *pi;
+ struct logint *li;
+ boolean_t mcast;
+ int cc;
+
+ for (pi = phyint; pi != NULL; pi = pi->pi_next) {
+ mcast = B_FALSE;
+ for (li = pi->pi_logical_first; li != NULL; li = li->li_next) {
+ if (li->li_state & ST_DELETED)
+ continue;
+
+ if (li->li_flags & IFF_MULTICAST) {
+ mcast = B_TRUE;
+ break;
+ }
+ }
+ if (!mcast)
+ continue;
+ cc = sendmcastif(s, packet, packetlen, sin, li);
+ if (cc != packetlen) {
+ return (cc);
+ }
+ }
+ return (packetlen);
+}
+
+static int
+sendmcastif(int s, char *packet, int packetlen, struct sockaddr_in *sin,
+ struct logint *li)
+{
+ int cc;
+ struct sockaddr_in ifaddr;
+ struct icmp *icp = (struct icmp *)ALIGN(packet);
+
+ ifaddr.sin_family = AF_INET;
+
+ if ((li->li_flags & IFF_MULTICAST) == 0) {
+ if (verbose) {
+ logtrace("Suppressing sending %s on %s "
+ "(interface is not multicast capable)\n",
+ pr_type((int)icp->icmp_type), li->li_name);
+ }
+ return (packetlen);
+ }
+ if (li->li_flags & IFF_NORTEXCH) {
+ if (verbose) {
+ logtrace("Suppressing sending %s on %s "
+ "(no route exchange on interface)\n",
+ pr_type((int)icp->icmp_type), li->li_name);
+ }
+ return (packetlen);
+ }
+
+ ifaddr.sin_addr = li->li_address;
+ if (debug)
+ logdebug("Multicast to interface %s\n",
+ pr_name(ifaddr.sin_addr));
+ if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF,
+ (char *)&ifaddr.sin_addr,
+ sizeof (ifaddr.sin_addr)) < 0) {
+ logperror("setsockopt (IP_MULTICAST_IF)");
+ logerr("Cannot send multicast packet over interface %s\n",
+ pr_name(ifaddr.sin_addr));
+ return (-1);
+ }
+ cc = sendto(s, packet, packetlen, 0,
+ (struct sockaddr *)sin, sizeof (struct sockaddr));
+ if (cc != packetlen) {
+ logperror("sendmcast: sendto");
+ logerr("Cannot send multicast packet over interface %s\n",
+ pr_name(ifaddr.sin_addr));
+ }
+ return (cc);
+}
+
+static void
+reinitifs(void)
+{
+ (void) initifs(s, &g_joinaddr, g_preference);
+}
+
+static void
+force_preference(int preference)
+{
+ struct phyint *pi;
+ struct logint *li;
+
+ for (pi = phyint; pi != NULL; pi = pi->pi_next) {
+ for (li = pi->pi_logical_first; li != NULL; li = li->li_next) {
+ if (li->li_state & ST_DELETED)
+ continue;
+
+ li->li_preference = preference;
+ }
+ }
+}
+
+/*
+ * Returns -1 on failure.
+ */
+static int
+initifs(int s, struct sockaddr_in *joinaddr, int preference)
+{
+ struct ifconf ifc;
+ struct ifreq ifreq, *ifr;
+ struct lifreq lifreq;
+ int n;
+ char *buf;
+ int numifs;
+ unsigned bufsize;
+ struct phyint *pi;
+ struct logint *li;
+ int num_deletions;
+ char phyintname[IFNAMSIZ];
+ char *cp;
+ int old_num_usable_interfaces = num_usable_interfaces;
+
+ /*
+ * Mark all interfaces so that we can determine which ones
+ * have gone away.
+ */
+ for (pi = phyint; pi != NULL; pi = pi->pi_next) {
+ pi->pi_state |= ST_MARKED;
+ for (li = pi->pi_logical_first; li != NULL; li = li->li_next) {
+ li->li_state |= ST_MARKED;
+ }
+ }
+
+ if (sock < 0) {
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock < 0) {
+ logperror("initifs: socket");
+ return (-1);
+ }
+ }
+#ifdef SIOCGIFNUM
+ if (ioctl(sock, SIOCGIFNUM, (char *)&numifs) < 0) {
+ logperror("initifs: SIOCGIFNUM");
+ return (-1);
+ }
+#else
+ numifs = MAXIFS;
+#endif
+ bufsize = numifs * sizeof (struct ifreq);
+ buf = (char *)malloc(bufsize);
+ if (buf == NULL) {
+ logerr("out of memory\n");
+ (void) close(sock);
+ sock = -1;
+ return (-1);
+ }
+ ifc.ifc_len = bufsize;
+ ifc.ifc_buf = buf;
+ if (ioctl(sock, SIOCGIFCONF, (char *)&ifc) < 0) {
+ logperror("initifs: ioctl (get interface configuration)");
+ (void) close(sock);
+ sock = -1;
+ (void) free(buf);
+ return (-1);
+ }
+ ifr = ifc.ifc_req;
+ for (n = ifc.ifc_len/sizeof (struct ifreq); n > 0; n--, ifr++) {
+ ifreq = *ifr;
+ /*
+ * We need to use new interface ioctls to get 64-bit flags.
+ */
+ (void) strncpy(lifreq.lifr_name, ifr->ifr_name,
+ sizeof (ifr->ifr_name));
+ if (ioctl(sock, SIOCGLIFFLAGS, (char *)&lifreq) < 0) {
+ logperror("initifs: ioctl (get interface flags)");
+ continue;
+ }
+ if (ifr->ifr_addr.sa_family != AF_INET)
+ continue;
+ if ((lifreq.lifr_flags & IFF_UP) == 0)
+ continue;
+ if (lifreq.lifr_flags & IFF_LOOPBACK)
+ continue;
+ if ((lifreq.lifr_flags & (IFF_MULTICAST | IFF_BROADCAST)) == 0)
+ continue;
+
+ /* Create the physical name by truncating at the ':' */
+ strncpy(phyintname, ifreq.ifr_name, sizeof (phyintname));
+ if ((cp = strchr(phyintname, ':')) != NULL)
+ *cp = '\0';
+
+ pi = find_phyint(phyintname);
+ if (pi == NULL) {
+ pi = add_phyint(phyintname);
+ if (pi == NULL) {
+ logerr("out of memory\n");
+ (void) close(sock);
+ sock = -1;
+ (void) free(buf);
+ return (-1);
+ }
+ }
+ pi->pi_state &= ~ST_MARKED;
+
+ li = find_logint(pi, ifreq.ifr_name);
+ if (li != NULL) {
+ /*
+ * Detect significant changes.
+ * We treat netmask changes as insignificant but all
+ * other changes cause a delete plus add of the
+ * logical interface.
+ * Note: if the flags and localaddr are unchanged
+ * then nothing but the netmask and the broadcast
+ * address could have changed since the other addresses
+ * are derived from the flags and the localaddr.
+ */
+ struct logint newli;
+
+ if (!getconfig(sock, lifreq.lifr_flags, &ifr->ifr_addr,
+ &ifreq, &newli)) {
+ free_logint(li);
+ continue;
+ }
+
+ if (newli.li_flags != li->li_flags ||
+ newli.li_localaddr.s_addr !=
+ li->li_localaddr.s_addr || newli.li_index !=
+ li->li_index) {
+ /* Treat as an interface deletion + addition */
+ li->li_state |= ST_DELETED;
+ deleted_logint(li, &newli, s, joinaddr);
+ free_logint(li);
+ li = NULL; /* li recreated below */
+ } else {
+ /*
+ * No significant changes.
+ * Just update the netmask, and broadcast.
+ */
+ li->li_netmask = newli.li_netmask;
+ li->li_bcastaddr = newli.li_bcastaddr;
+ }
+ }
+ if (li == NULL) {
+ li = add_logint(pi, ifreq.ifr_name);
+ if (li == NULL) {
+ logerr("out of memory\n");
+ (void) close(sock);
+ sock = -1;
+ (void) free(buf);
+ return (-1);
+ }
+
+ /* init li */
+ if (!getconfig(sock, lifreq.lifr_flags, &ifr->ifr_addr,
+ &ifreq, li)) {
+ free_logint(li);
+ continue;
+ }
+ li->li_preference = preference;
+ added_logint(li, s, joinaddr);
+ }
+ li->li_state &= ~ST_MARKED;
+ }
+ (void) free(buf);
+
+ /*
+ * Determine which interfaces have gone away.
+ * The deletion is done in three phases:
+ * 1. Mark ST_DELETED
+ * 2. Inform using the deleted_* function.
+ * 3. Unlink and free the actual memory.
+ * Note that for #3 the physical interface must be deleted after
+ * the logical ones.
+ * Also count the number of physical interfaces.
+ */
+ num_usable_interfaces = 0;
+ num_deletions = 0;
+ for (pi = phyint; pi != NULL; pi = pi->pi_next) {
+ if (pi->pi_state & ST_MARKED) {
+ num_deletions++;
+ pi->pi_state |= ST_DELETED;
+ }
+ for (li = pi->pi_logical_first; li != NULL; li = li->li_next) {
+ if (li->li_state & ST_MARKED) {
+ num_deletions++;
+ li->li_state |= ST_DELETED;
+ }
+ }
+ if (!(pi->pi_state & ST_DELETED))
+ num_usable_interfaces++;
+ }
+ if (num_deletions != 0) {
+ struct phyint *nextpi;
+ struct logint *nextli;
+
+ for (pi = phyint; pi != NULL; pi = pi->pi_next) {
+ if (pi->pi_state & ST_DELETED) {
+ /*
+ * By deleting the physical interface pi, all of
+ * the corresponding logical interfaces will
+ * also be deleted so there is no need to delete
+ * them individually.
+ */
+ deleted_phyint(pi, s, joinaddr);
+ } else {
+ for (li = pi->pi_logical_first; li != NULL;
+ li = li->li_next) {
+ if (li->li_state & ST_DELETED) {
+ deleted_logint(li, NULL, s,
+ joinaddr);
+ }
+ }
+ }
+ }
+ /* Do the actual linked list update + free */
+ for (pi = phyint; pi != NULL; pi = nextpi) {
+ nextpi = pi->pi_next;
+ for (li = pi->pi_logical_first; li != NULL;
+ li = nextli) {
+ nextli = li->li_next;
+ if (li->li_state & ST_DELETED)
+ free_logint(li);
+ }
+ if (pi->pi_state & ST_DELETED)
+ free_phyint(pi);
+ }
+ }
+ /*
+ * When the set of available interfaces goes from zero to
+ * non-zero we restart solicitations if '-s' was specified.
+ */
+ if (old_num_usable_interfaces == 0 && num_usable_interfaces > 0 &&
+ start_solicit && !solicit) {
+ if (debug)
+ logdebug("switching to solicitations: num if %d\n",
+ num_usable_interfaces);
+ solicit = start_solicit;
+ ntransmitted = 0;
+ ntransmitted++;
+ solicitor(&whereto);
+ }
+ return (0);
+}
+
+static boolean_t
+getconfig(int sock, uint64_t if_flags, struct sockaddr *addr,
+ struct ifreq *ifr, struct logint *li)
+{
+ struct ifreq ifreq;
+ struct sockaddr_in *sin;
+ struct lifreq lifreq;
+
+ ifreq = *ifr; /* Copy name etc */
+
+ li->li_flags = if_flags;
+ sin = (struct sockaddr_in *)ALIGN(addr);
+ li->li_localaddr = sin->sin_addr;
+
+ (void) strlcpy(lifreq.lifr_name, ifr->ifr_name,
+ sizeof (lifreq.lifr_name));
+ if (ioctl(sock, SIOCGLIFINDEX, &lifreq) < 0) {
+ logperror("initifs: ioctl (get if index)");
+ /* Continue with 0; a safe value never used for interfaces */
+ li->li_index = 0;
+ } else {
+ li->li_index = lifreq.lifr_index;
+ }
+
+ if (if_flags & IFF_POINTOPOINT) {
+ li->li_netmask.s_addr = (unsigned long)0xffffffff;
+ if (ioctl(sock, SIOCGIFDSTADDR, (char *)&ifreq) < 0) {
+ logperror("initifs: ioctl (get dest addr)");
+ return (B_FALSE);
+ }
+ /* A pt-pt link is identified by the remote address */
+ sin = (struct sockaddr_in *)ALIGN(&ifreq.ifr_addr);
+ li->li_address = sin->sin_addr;
+ li->li_remoteaddr = sin->sin_addr;
+ /* Simulate broadcast for pt-pt */
+ li->li_bcastaddr = sin->sin_addr;
+ li->li_flags |= IFF_BROADCAST;
+ } else {
+ /*
+ * Non pt-pt links are identified by the local
+ * address
+ */
+ li->li_address = li->li_localaddr;
+ li->li_remoteaddr = li->li_address;
+ if (ioctl(sock, SIOCGIFNETMASK, (char *)&ifreq) < 0) {
+ logperror("initifs: ioctl (get netmask)");
+ return (B_FALSE);
+ }
+ sin = (struct sockaddr_in *)ALIGN(&ifreq.ifr_addr);
+ li->li_netmask = sin->sin_addr;
+ if (if_flags & IFF_BROADCAST) {
+ if (ioctl(sock, SIOCGIFBRDADDR, (char *)&ifreq) < 0) {
+ logperror(
+ "initifs: ioctl (get broadcast address)");
+ return (B_FALSE);
+ }
+ sin = (struct sockaddr_in *)ALIGN(&ifreq.ifr_addr);
+ li->li_bcastaddr = sin->sin_addr;
+ }
+ }
+ return (B_TRUE);
+}
+
+
+static int
+support_multicast(void)
+{
+ int sock;
+ uchar_t ttl = 1;
+
+ sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (sock < 0) {
+ logperror("support_multicast: socket");
+ return (0);
+ }
+
+ if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL,
+ (char *)&ttl, sizeof (ttl)) < 0) {
+ (void) close(sock);
+ return (0);
+ }
+ (void) close(sock);
+ return (1);
+}
+
+/*
+ * For a given destination address, find the logical interface to use.
+ * If opi is NULL check all interfaces. Otherwise just match against
+ * the specified physical interface.
+ * Return logical interface if there's a match, NULL otherwise.
+ */
+static struct logint *
+find_directly_connected_logint(struct in_addr in, struct phyint *opi)
+{
+ struct phyint *pi;
+ struct logint *li;
+
+ if (opi == NULL)
+ pi = phyint;
+ else
+ pi = opi;
+
+ for (; pi != NULL; pi = pi->pi_next) {
+ for (li = pi->pi_logical_first; li != NULL; li = li->li_next) {
+ if (li->li_state & ST_DELETED)
+ continue;
+
+ /* Check that the subnetwork numbers match */
+ if ((in.s_addr & li->li_netmask.s_addr) ==
+ (li->li_remoteaddr.s_addr &
+ li->li_netmask.s_addr))
+ return (li);
+ }
+ if (opi != NULL)
+ break;
+ }
+ return (NULL);
+}
+
+/*
+ * INTERFACES - physical and logical identified by name
+ */
+
+
+static void
+report_interfaces(void)
+{
+ struct phyint *pi;
+ struct logint *li;
+
+ logdebug("\nInterfaces:\n\n");
+ for (pi = phyint; pi != NULL; pi = pi->pi_next) {
+ logdebug("Phyint %s state 0x%x\n",
+ pi->pi_name, pi->pi_state);
+ for (li = pi->pi_logical_first; li != NULL; li = li->li_next) {
+ logdebug("IF %s state 0x%x, flags 0x%x, addr %s\n",
+ li->li_name, li->li_state, li->li_flags,
+ pr_name(li->li_address));
+ logdebug("\tlocal %s pref 0x%x ",
+ pr_name(li->li_localaddr), li->li_preference);
+ logdebug("bcast %s\n",
+ pr_name(li->li_bcastaddr));
+ logdebug("\tremote %s ",
+ pr_name(li->li_remoteaddr));
+ logdebug("netmask %s\n",
+ pr_name(li->li_netmask));
+ }
+ }
+}
+
+static struct phyint *
+find_phyint(char *name)
+{
+ struct phyint *pi;
+
+ for (pi = phyint; pi != NULL; pi = pi->pi_next) {
+ if (strcmp(pi->pi_name, name) == 0)
+ return (pi);
+ }
+ return (NULL);
+}
+
+/* Assumes that the entry does not exist - caller must use find_* */
+static struct phyint *
+add_phyint(char *name)
+{
+ struct phyint *pi;
+
+ pi = malloc(sizeof (*pi));
+ if (pi == NULL)
+ return (NULL);
+ bzero((char *)pi, sizeof (*pi));
+
+ strncpy(pi->pi_name, name, sizeof (pi->pi_name));
+ /* Link into list */
+ pi->pi_next = phyint;
+ pi->pi_prev = NULL;
+ if (phyint != NULL)
+ phyint->pi_prev = pi;
+ phyint = pi;
+ return (pi);
+}
+
+static void
+free_phyint(struct phyint *pi)
+{
+ assert(pi->pi_logical_first == NULL);
+ assert(pi->pi_logical_last == NULL);
+
+ if (pi->pi_prev == NULL) {
+ /* Delete first */
+ assert(phyint == pi);
+ phyint = pi->pi_next;
+ } else {
+ assert(pi->pi_prev->pi_next == pi);
+ pi->pi_prev->pi_next = pi->pi_next;
+ }
+ if (pi->pi_next != NULL) {
+ assert(pi->pi_next->pi_prev == pi);
+ pi->pi_next->pi_prev = pi->pi_prev;
+ }
+ free(pi);
+}
+
+static struct logint *
+find_logint(struct phyint *pi, char *name)
+{
+ struct logint *li;
+
+ for (li = pi->pi_logical_first; li != NULL; li = li->li_next) {
+ if (strcmp(li->li_name, name) == 0)
+ return (li);
+ }
+ return (NULL);
+}
+
+/*
+ * Assumes that the entry does not exist - caller must use find_*
+ * Tail insertion.
+ */
+static struct logint *
+add_logint(struct phyint *pi, char *name)
+{
+ struct logint *li;
+
+ li = malloc(sizeof (*li));
+ if (li == NULL)
+ return (NULL);
+ bzero((char *)li, sizeof (*li));
+
+ strncpy(li->li_name, name, sizeof (li->li_name));
+ /* Link into list */
+ li->li_prev = pi->pi_logical_last;
+ if (pi->pi_logical_last == NULL) {
+ /* First one */
+ assert(pi->pi_logical_first == NULL);
+ pi->pi_logical_first = li;
+ } else {
+ pi->pi_logical_last->li_next = li;
+ }
+ li->li_next = NULL;
+ li->li_physical = pi;
+ pi->pi_logical_last = li;
+ return (li);
+
+}
+
+static void
+free_logint(struct logint *li)
+{
+ struct phyint *pi;
+
+ pi = li->li_physical;
+ if (li->li_prev == NULL) {
+ /* Delete first */
+ assert(pi->pi_logical_first == li);
+ pi->pi_logical_first = li->li_next;
+ } else {
+ assert(li->li_prev->li_next == li);
+ li->li_prev->li_next = li->li_next;
+ }
+ if (li->li_next == NULL) {
+ /* Delete last */
+ assert(pi->pi_logical_last == li);
+ pi->pi_logical_last = li->li_prev;
+ } else {
+ assert(li->li_next->li_prev == li);
+ li->li_next->li_prev = li->li_prev;
+ }
+ free(li);
+}
+
+
+/* Tell all the logical interfaces that they are going away */
+static void
+deleted_phyint(struct phyint *pi, int s,
+ struct sockaddr_in *joinaddr)
+{
+ struct logint *li;
+
+ if (debug)
+ logdebug("Deleting physical interface %s\n", pi->pi_name);
+
+ for (li = pi->pi_logical_first; li != NULL; li = li->li_next) {
+ li->li_state |= ST_DELETED;
+ }
+ for (li = pi->pi_logical_first; li != NULL; li = li->li_next) {
+ deleted_logint(li, NULL, s, joinaddr);
+ }
+}
+
+/*
+ * Join the multicast address if no other logical interface has done
+ * so for this physical interface.
+ */
+static void
+added_logint(struct logint *li, int s,
+ struct sockaddr_in *joinaddr)
+{
+ if (debug)
+ logdebug("Adding logical interface %s\n", li->li_name);
+
+ if ((!(li->li_physical->pi_state & ST_JOINED)) &&
+ (!isbroadcast(joinaddr))) {
+ struct ip_mreq mreq;
+
+ mreq.imr_multiaddr = joinaddr->sin_addr;
+ mreq.imr_interface = li->li_address;
+
+ if (debug)
+ logdebug("Joining MC on interface %s\n", li->li_name);
+
+ if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ (char *)&mreq, sizeof (mreq)) < 0) {
+ logperror("setsockopt (IP_ADD_MEMBERSHIP)");
+ } else {
+ li->li_physical->pi_state |= ST_JOINED;
+ li->li_state |= ST_JOINED;
+ }
+ }
+}
+
+/*
+ * Leave the multicast address if this logical interface joined it.
+ * Look for a replacement logical interface for the same physical interface.
+ * Remove any routes which are no longer reachable.
+ *
+ * If newli is non-NULL, then it is likely that the address of a logical
+ * interface has changed. In this case, the membership should be dropped using
+ * the new address of the interface in question.
+ *
+ * XXX When a physical interface is being deleted by deleted_phyint(), this
+ * routine will be called for each logical interface associated with the
+ * physical one. This should be made more efficient as there is no point in
+ * searching for an alternate logical interface to add group membership to as
+ * they all are marked ST_DELETED.
+ */
+static void
+deleted_logint(struct logint *li, struct logint *newli, int s,
+ struct sockaddr_in *joinaddr)
+{
+ struct phyint *pi;
+ struct logint *oli;
+
+ if (debug)
+ logdebug("Deleting logical interface %s\n", li->li_name);
+
+ assert(li->li_state & ST_DELETED);
+
+ if (li->li_state & ST_JOINED) {
+ struct ip_mreq mreq;
+
+ pi = li->li_physical;
+ assert(pi->pi_state & ST_JOINED);
+ assert(!isbroadcast(joinaddr));
+
+ mreq.imr_multiaddr = joinaddr->sin_addr;
+ if (newli != NULL)
+ mreq.imr_interface = newli->li_address;
+ else
+ mreq.imr_interface = li->li_address;
+
+ if (debug)
+ logdebug("Leaving MC on interface %s\n", li->li_name);
+
+ if (setsockopt(s, IPPROTO_IP, IP_DROP_MEMBERSHIP,
+ (char *)&mreq, sizeof (mreq)) < 0) {
+ /*
+ * EADDRNOTAVAIL will be returned if the interface has
+ * been unplumbed or if the interface no longer has
+ * IFF_MULTICAST set. The former is the common case
+ * while the latter is rare so don't log the error
+ * unless some other error was returned or if debug is
+ * set.
+ */
+ if (errno != EADDRNOTAVAIL) {
+ logperror("setsockopt (IP_DROP_MEMBERSHIP)");
+ } else if (debug) {
+ logdebug("%s: %s\n",
+ "setsockopt (IP_DROP_MEMBERSHIP)",
+ strerror(errno));
+ }
+ }
+ li->li_physical->pi_state &= ~ST_JOINED;
+ li->li_state &= ~ST_JOINED;
+
+ /* Is there another interface that can join? */
+ for (oli = pi->pi_logical_first; oli != NULL;
+ oli = oli->li_next) {
+ if (oli->li_state & ST_DELETED)
+ continue;
+
+ mreq.imr_multiaddr = joinaddr->sin_addr;
+ mreq.imr_interface = oli->li_address;
+
+ if (debug)
+ logdebug("Joining MC on interface %s\n",
+ oli->li_name);
+
+ if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ (char *)&mreq, sizeof (mreq)) < 0) {
+ logperror("setsockopt (IP_ADD_MEMBERSHIP)");
+ } else {
+ pi->pi_state |= ST_JOINED;
+ oli->li_state |= ST_JOINED;
+ break;
+ }
+ }
+ }
+
+ flush_unreachable_routers();
+}
+
+
+
+/*
+ * TABLES
+ */
+struct table {
+ struct in_addr router;
+ int preference;
+ int remaining_time;
+ int in_kernel;
+ struct table *next;
+};
+
+struct table *table;
+
+static void
+report_routes(void)
+{
+ struct table *tp;
+
+ logdebug("\nRoutes:\n\n");
+ tp = table;
+ while (tp) {
+ logdebug("Router %s, pref 0x%x, time %d, %s kernel\n",
+ pr_name(tp->router), tp->preference,
+ tp->remaining_time,
+ (tp->in_kernel ? "in" : "not in"));
+ tp = tp->next;
+ }
+}
+
+static struct table *
+find_router(struct in_addr addr)
+{
+ struct table *tp;
+
+ tp = table;
+ while (tp) {
+ if (tp->router.s_addr == addr.s_addr)
+ return (tp);
+ tp = tp->next;
+ }
+ return (NULL);
+}
+
+static int
+max_preference(void)
+{
+ struct table *tp;
+ int max = (int)IGNORE_PREFERENCE;
+
+ tp = table;
+ while (tp) {
+ if (tp->preference > max)
+ max = tp->preference;
+ tp = tp->next;
+ }
+ return (max);
+}
+
+
+/* Note: this might leave the kernel with no default route for a short time. */
+static void
+age_table(int time)
+{
+ struct table **tpp, *tp;
+ int recalculate_max = 0;
+ int max = max_preference();
+
+ tpp = &table;
+ while (*tpp != NULL) {
+ tp = *tpp;
+ tp->remaining_time -= time;
+ if (tp->remaining_time <= 0) {
+ *tpp = tp->next;
+ if (debug) {
+ logdebug("Timed out router %s\n",
+ pr_name(tp->router));
+ }
+ if (tp->in_kernel)
+ del_route(tp->router);
+ if (best_preference &&
+ tp->preference == max)
+ recalculate_max++;
+ free((char *)tp);
+ } else {
+ tpp = &tp->next;
+ }
+ }
+ if (recalculate_max) {
+ int max = max_preference();
+
+ if (max != IGNORE_PREFERENCE) {
+ tp = table;
+ while (tp) {
+ if (tp->preference == max && !tp->in_kernel) {
+ add_route(tp->router);
+ tp->in_kernel++;
+ }
+ tp = tp->next;
+ }
+ }
+ }
+}
+
+/*
+ * Remove any routes which are no longer directly connected.
+ */
+static void
+flush_unreachable_routers(void)
+{
+ struct table **tpp, *tp;
+ int recalculate_max = 0;
+ int max = max_preference();
+
+ tpp = &table;
+ while (*tpp != NULL) {
+ tp = *tpp;
+ if (find_directly_connected_logint(tp->router, NULL) == NULL) {
+ *tpp = tp->next;
+ if (debug) {
+ logdebug("Unreachable router %s\n",
+ pr_name(tp->router));
+ }
+ if (tp->in_kernel)
+ del_route(tp->router);
+ if (best_preference &&
+ tp->preference == max)
+ recalculate_max++;
+ free((char *)tp);
+ } else {
+ tpp = &tp->next;
+ }
+ }
+ if (recalculate_max) {
+ int max = max_preference();
+
+ if (max != IGNORE_PREFERENCE) {
+ tp = table;
+ while (tp) {
+ if (tp->preference == max && !tp->in_kernel) {
+ add_route(tp->router);
+ tp->in_kernel++;
+ }
+ tp = tp->next;
+ }
+ }
+ }
+}
+
+static void
+record_router(struct in_addr router, long preference, int ttl)
+{
+ struct table *tp;
+ int old_max = max_preference();
+ int changed_up = 0; /* max preference could have increased */
+ int changed_down = 0; /* max preference could have decreased */
+
+ if (debug)
+ logdebug("Recording %s, preference 0x%x\n",
+ pr_name(router),
+ preference);
+ tp = find_router(router);
+ if (tp) {
+ if (tp->preference > preference &&
+ tp->preference == old_max)
+ changed_down++;
+ else if (preference > tp->preference)
+ changed_up++;
+ tp->preference = preference;
+ tp->remaining_time = ttl;
+ } else {
+ if (preference > old_max)
+ changed_up++;
+ tp = (struct table *)ALIGN(malloc(sizeof (struct table)));
+ if (tp == NULL) {
+ logerr("Out of memory\n");
+ return;
+ }
+ tp->router = router;
+ tp->preference = preference;
+ tp->remaining_time = ttl;
+ tp->in_kernel = 0;
+ tp->next = table;
+ table = tp;
+ }
+ if (!tp->in_kernel &&
+ (!best_preference || tp->preference == max_preference()) &&
+ tp->preference != IGNORE_PREFERENCE) {
+ add_route(tp->router);
+ tp->in_kernel++;
+ }
+ if (tp->preference == IGNORE_PREFERENCE && tp->in_kernel) {
+ del_route(tp->router);
+ tp->in_kernel = 0;
+ }
+ if (best_preference && changed_down) {
+ /* Check if we should add routes */
+ int new_max = max_preference();
+ if (new_max != IGNORE_PREFERENCE) {
+ tp = table;
+ while (tp) {
+ if (tp->preference == new_max &&
+ !tp->in_kernel) {
+ add_route(tp->router);
+ tp->in_kernel++;
+ }
+ tp = tp->next;
+ }
+ }
+ }
+ if (best_preference && (changed_up || changed_down)) {
+ /* Check if we should remove routes already in the kernel */
+ int new_max = max_preference();
+ tp = table;
+ while (tp) {
+ if (tp->preference < new_max && tp->in_kernel) {
+ del_route(tp->router);
+ tp->in_kernel = 0;
+ }
+ tp = tp->next;
+ }
+ }
+}
+
+
+#include <net/route.h>
+
+static void
+add_route(struct in_addr addr)
+{
+ if (debug)
+ logdebug("Add default route to %s\n", pr_name(addr));
+ rtioctl(addr, SIOCADDRT);
+}
+
+static void
+del_route(struct in_addr addr)
+{
+ if (debug)
+ logdebug("Delete default route to %s\n", pr_name(addr));
+ rtioctl(addr, SIOCDELRT);
+}
+
+static void
+rtioctl(struct in_addr addr, int op)
+{
+ int sock;
+ struct rtentry rt;
+ struct sockaddr_in *sin;
+ bzero((char *)&rt, sizeof (struct rtentry));
+ rt.rt_dst.sa_family = AF_INET;
+ rt.rt_gateway.sa_family = AF_INET;
+ sin = (struct sockaddr_in *)ALIGN(&rt.rt_gateway);
+ sin->sin_addr = addr;
+ rt.rt_flags = RTF_UP | RTF_GATEWAY;
+
+ sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (sock < 0) {
+ logperror("rtioctl: socket");
+ return;
+ }
+ if (ioctl(sock, op, (char *)&rt) < 0) {
+ if (!(op == SIOCADDRT && errno == EEXIST))
+ logperror("ioctl (add/delete route)");
+ }
+ (void) close(sock);
+}
+
+
+
+/*
+ * LOGGER
+ */
+
+#include <syslog.h>
+
+static logging = 0;
+
+static void
+initlog(void)
+{
+ logging++;
+ openlog("in.rdisc", LOG_PID | LOG_CONS, LOG_DAEMON);
+}
+
+/* VARARGS1 */
+void
+logerr(fmt, a, b, c, d, e, f, g, h)
+ char *fmt;
+{
+ if (logging)
+ syslog(LOG_ERR, fmt, a, b, c, d, e, f, g, h);
+ else
+ (void) fprintf(stderr, fmt, a, b, c, d, e, f, g, h);
+}
+
+/* VARARGS1 */
+void
+logtrace(fmt, a, b, c, d, e, f, g, h)
+ char *fmt;
+{
+ if (logging)
+ syslog(LOG_INFO, fmt, a, b, c, d, e, f, g, h);
+ else
+ (void) fprintf(stdout, fmt, a, b, c, d, e, f, g, h);
+}
+
+/* VARARGS1 */
+void
+logdebug(fmt, a, b, c, d, e, f, g, h)
+ char *fmt;
+{
+ if (logging)
+ syslog(LOG_DEBUG, fmt, a, b, c, d, e, f, g, h);
+ else
+ (void) fprintf(stdout, fmt, a, b, c, d, e, f, g, h);
+}
+
+void
+logperror(str)
+ char *str;
+{
+ if (logging)
+ syslog(LOG_ERR, "%s: %s\n", str, strerror(errno));
+ else
+ (void) fprintf(stderr, "%s: %s\n", str, strerror(errno));
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.rexecd.c b/usr/src/cmd/cmd-inet/usr.sbin/in.rexecd.c
new file mode 100644
index 0000000000..95704bacc9
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.rexecd.c
@@ -0,0 +1,558 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983-1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * Portions of this source code were derived from Berkeley 4.3 BSD
+ * under license from the Regents of the University of California.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/filio.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <pwd.h>
+#include <grp.h>
+#include <signal.h>
+#include <netdb.h>
+#include <syslog.h>
+#include <security/pam_appl.h>
+
+#ifdef SYSV
+#include <shadow.h>
+#endif /* SYSV */
+
+#ifndef NCARGS
+#define NCARGS 5120
+#endif /* NCARGS */
+
+#ifdef SYSV
+#define rindex strrchr
+#define killpg(a, b) kill(-(a), (b))
+#else
+char *sprintf();
+#endif /* SYSV */
+
+#define MAXFD(A, B) ((A) > (B) ? (A) : (B))
+
+static void error(char *fmt, ...);
+static void doit(int f, struct sockaddr_storage *fromp);
+static void getstr(char *buf, int cnt, char *err);
+
+static int legalenvvar(char *s);
+
+/* Function decls. for functions not in any header file. (Grrrr.) */
+extern int audit_rexecd_setup(void);
+extern int audit_rexecd_success(char *, char *, char *);
+extern int audit_rexecd_fail(char *, char *, char *, char *);
+extern int audit_settid(int); /* set termnal ID */
+
+/* PAM conversation function */
+static int rexec_conv(int, struct pam_message **,
+ struct pam_response **, void *);
+
+static pam_handle_t *pamh; /* authentication handle */
+static struct pam_conv conv = {
+ rexec_conv,
+ NULL
+ };
+
+/*
+ * remote execute server:
+ * username\0
+ * password\0
+ * command\0
+ * data
+ *
+ * in.rexecd has been modified to run as the user invoking it. Hence there is no
+ * need to limit any privileges.
+ */
+/*ARGSUSED*/
+void
+main(int argc, char **argv)
+{
+ struct sockaddr_storage from;
+ socklen_t fromlen;
+
+ openlog("rexec", LOG_PID | LOG_ODELAY, LOG_DAEMON);
+ (void) audit_rexecd_setup(); /* BSM */
+ fromlen = (socklen_t)sizeof (from);
+ if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) {
+ (void) fprintf(stderr, "%s: ", argv[0]);
+ perror("getpeername");
+ exit(1);
+ }
+
+ if (audit_settid(0) != 0) {
+ perror("settid");
+ exit(1);
+ }
+
+ doit(0, &from);
+ /* NOTREACHED */
+}
+
+static char username[20] = "USER=";
+static char homedir[64] = "HOME=";
+static char shell[64] = "SHELL=";
+
+static char *envinit[] =
+#ifdef SYSV
+ {homedir, shell, (char *)0, username,
+ (char *)0, (char *)0, (char *)0, (char *)0,
+ (char *)0, (char *)0, (char *)0, (char *)0,
+ (char *)0, (char *)0, (char *)0, (char *)0,
+ (char *)0, (char *)0, (char *)0, (char *)0,
+ (char *)0};
+#define ENVINIT_PATH 2 /* position of PATH in envinit[] */
+#define PAM_ENV_ELIM 16 /* max PAM environment variables */
+
+/*
+ * See PSARC opinion 1992/025
+ */
+static char userpath[] = "PATH=/usr/bin:";
+static char rootpath[] = "PATH=/usr/sbin:/usr/bin";
+#else
+ {homedir, shell, "PATH=:/usr/ucb:/bin:/usr/bin", username, 0};
+#endif /* SYSV */
+
+static struct sockaddr_storage asin;
+static char pass[16];
+
+static void
+doit(int f, struct sockaddr_storage *fromp)
+{
+ char cmdbuf[NCARGS+1], *cp;
+ char user[16];
+ char hostname [MAXHOSTNAMELEN + 1];
+ struct passwd *pwd;
+ int s;
+ ushort_t port;
+ pid_t pid;
+ int pv[2], cc;
+ fd_set readfrom, ready;
+ char buf[BUFSIZ], sig;
+ int one = 1;
+ int idx = 0, end_env = 0;
+ char **pam_env;
+ int status = PAM_AUTH_ERR;
+ char abuf[INET6_ADDRSTRLEN];
+ struct in_addr v4dst;
+ socklen_t fromplen;
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+
+ (void) signal(SIGINT, SIG_DFL);
+ (void) signal(SIGQUIT, SIG_DFL);
+ (void) signal(SIGTERM, SIG_DFL);
+#ifdef DEBUG
+ {
+ int t = open("/dev/tty", 2);
+ if (t >= 0) {
+#ifdef SYSV
+ (void) setsid();
+#else
+ (void) ioctl(t, TIOCNOTTY, (char *)0);
+#endif /* SYSV */
+ (void) close(t);
+ }
+ }
+#endif
+ if (fromp->ss_family == AF_INET) {
+ sin = (struct sockaddr_in *)fromp;
+ fromplen = sizeof (struct sockaddr_in);
+ asin.ss_family = AF_INET; /* used for bind */
+ } else if (fromp->ss_family == AF_INET6) {
+ sin6 = (struct sockaddr_in6 *)fromp;
+ fromplen = sizeof (struct sockaddr_in6);
+ asin.ss_family = AF_INET6; /* used for bind */
+ } else {
+ syslog(LOG_ERR, "unknown address family %d\n",
+ fromp->ss_family);
+ exit(1);
+ }
+ /*
+ * store common info. for audit record
+ */
+
+ if (getnameinfo((const struct sockaddr *) fromp, fromplen, hostname,
+ sizeof (hostname), NULL, 0, 0) != 0) {
+ if (fromp->ss_family == AF_INET6) {
+ if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
+ struct in_addr ipv4_addr;
+
+ IN6_V4MAPPED_TO_INADDR(&sin6->sin6_addr,
+ &ipv4_addr);
+ inet_ntop(AF_INET, &ipv4_addr, abuf,
+ sizeof (abuf));
+ } else {
+ inet_ntop(AF_INET6, &sin6->sin6_addr,
+ abuf, sizeof (abuf));
+ }
+ } else if (fromp->ss_family == AF_INET) {
+ inet_ntop(AF_INET, &sin->sin_addr,
+ abuf, sizeof (abuf));
+ }
+ (void) strncpy(hostname, abuf, sizeof (hostname));
+ }
+ (void) dup2(f, 0);
+ (void) dup2(f, 1);
+ (void) dup2(f, 2);
+ (void) alarm(60);
+ port = 0;
+ for (;;) {
+ char c;
+ if (read(f, &c, 1) != 1)
+ exit(1);
+ if (c == 0)
+ break;
+ port = port * 10 + c - '0';
+ }
+ (void) alarm(0);
+ if (port != 0) {
+ s = socket(fromp->ss_family, SOCK_STREAM, 0);
+ if (s < 0)
+ exit(1);
+ if (bind(s, (struct sockaddr *)&asin, fromplen) < 0)
+ exit(1);
+ (void) alarm(60);
+ if (fromp->ss_family == AF_INET) {
+ sin->sin_port = htons((ushort_t)port);
+ } else if (fromp->ss_family == AF_INET6) {
+ sin6->sin6_port = htons((ushort_t)port);
+ }
+ if (connect(s, (struct sockaddr *)fromp, fromplen) < 0)
+ exit(1);
+ (void) alarm(0);
+ }
+ getstr(user, sizeof (user), "username");
+ getstr(pass, sizeof (pass), "password");
+ getstr(cmdbuf, sizeof (cmdbuf), "command");
+
+ setpwent();
+ pwd = getpwnam(user);
+ if (pwd == NULL) {
+ (void) audit_rexecd_fail("Login incorrect", hostname, user,
+ cmdbuf); /* BSM */
+ error("Login incorrect.\n");
+ exit(1);
+ }
+ endpwent();
+
+ if (pam_start("rexec", user, &conv, &pamh) != PAM_SUCCESS) {
+ exit(1);
+ }
+ if (pam_set_item(pamh, PAM_RHOST, hostname) != PAM_SUCCESS) {
+ exit(1);
+ }
+
+ if ((status = pam_authenticate(pamh, 0)) != PAM_SUCCESS) {
+ switch (status) {
+ case PAM_USER_UNKNOWN:
+ (void) audit_rexecd_fail("Login incorrect", hostname,
+ user, cmdbuf); /* BSM */
+ error("Login incorrect.\n");
+ break;
+ default:
+ (void) audit_rexecd_fail("Password incorrect", hostname,
+ user, cmdbuf); /* BSM */
+ error("Password incorrect.\n");
+ }
+ pam_end(pamh, status);
+ exit(1);
+ }
+ if ((status = pam_acct_mgmt(pamh, 0)) != PAM_SUCCESS) {
+ (void) audit_rexecd_fail("Account or Password Expired",
+ hostname, user, cmdbuf);
+ switch (status) {
+ case PAM_NEW_AUTHTOK_REQD:
+ error("Password Expired.\n");
+ break;
+ case PAM_PERM_DENIED:
+ error("Account Expired.\n");
+ break;
+ case PAM_AUTHTOK_EXPIRED:
+ error("Password Expired.\n");
+ break;
+ default:
+ error("Login incorrect.\n");
+ break;
+ }
+ pam_end(pamh, status);
+ exit(1);
+ }
+
+ (void) write(2, "\0", 1);
+
+ if (setgid((gid_t)pwd->pw_gid) < 0) {
+ (void) audit_rexecd_fail("Can't setgid", hostname,
+ user, cmdbuf); /* BSM */
+ error("setgid");
+ pam_end(pamh, PAM_ABORT);
+ exit(1);
+ }
+ (void) initgroups(pwd->pw_name, pwd->pw_gid);
+
+ if ((status = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) {
+ (void) audit_rexecd_fail("Unable to establish credentials",
+ hostname, user, cmdbuf); /* BSM */
+ error("Unable to establish credentials.\n");
+ pam_end(pamh, PAM_SUCCESS);
+ }
+
+ (void) audit_rexecd_success(hostname, user, cmdbuf); /* BSM */
+
+ if (setuid((uid_t)pwd->pw_uid) < 0) {
+ (void) audit_rexecd_fail("Can't setuid", hostname,
+ user, cmdbuf); /* BSM */
+ error("setuid");
+ pam_end(pamh, PAM_ABORT);
+ exit(1);
+ }
+
+
+ if (port) {
+ (void) pipe(pv);
+ pid = fork();
+ if (pid == (pid_t)-1) {
+ error("Try again.\n");
+ pam_end(pamh, PAM_ABORT);
+ exit(1);
+ }
+ if (pid) {
+ /*
+ * since the daemon is running as the user no need
+ * to prune privileges.
+ */
+ (void) close(0); (void) close(1); (void) close(2);
+ (void) close(f); (void) close(pv[1]);
+ FD_ZERO(&readfrom);
+ FD_SET(s, &readfrom);
+ FD_SET(pv[0], &readfrom);
+ (void) ioctl(pv[0], FIONBIO, (char *)&one);
+ /* should set s nbio! */
+ do {
+ ready = readfrom;
+ if (select(MAXFD(s, pv[0])+1, &ready, NULL,
+ NULL, NULL) < 0) {
+ perror("select:");
+ exit(1);
+ }
+ if (FD_ISSET(s, &ready)) {
+ if (read(s, &sig, 1) <= 0)
+ FD_CLR(s, &readfrom);
+ else
+ (void) killpg(pid, sig);
+ }
+ if (FD_ISSET(pv[0], &ready)) {
+ cc = read(pv[0], buf, sizeof (buf));
+ if (cc <= 0) {
+ (void) shutdown(s, 1+1);
+ FD_CLR(pv[0], &readfrom);
+ } else
+ (void) write(s, buf, cc);
+ }
+ } while (FD_ISSET(s, &readfrom) ||
+ FD_ISSET(pv[0], &readfrom));
+ exit(0);
+ }
+ /* setpgrp(0, getpid()); */
+ (void) setsid(); /* Should be the same as above. */
+ (void) close(s); (void)close(pv[0]);
+ (void) dup2(pv[1], 2);
+ }
+
+ if (*pwd->pw_shell == '\0')
+ pwd->pw_shell = "/bin/sh";
+ if (f > 2)
+ (void) close(f);
+ /* Change directory only after becoming the appropriate user. */
+ if (chdir(pwd->pw_dir) < 0) {
+ error("No remote directory.\n");
+ pam_end(pamh, PAM_ABORT);
+ exit(1);
+ }
+#ifdef SYSV
+ if (pwd->pw_uid)
+ envinit[ENVINIT_PATH] = userpath;
+ else
+ envinit[ENVINIT_PATH] = rootpath;
+#endif /* SYSV */
+ (void) strncat(homedir, pwd->pw_dir, sizeof (homedir) - 6);
+ (void) strncat(shell, pwd->pw_shell, sizeof (shell) - 7);
+ (void) strncat(username, pwd->pw_name, sizeof (username) - 6);
+
+ /*
+ * add PAM environment variables set by modules
+ * -- only allowed 16 (PAM_ENV_ELIM)
+ * -- check to see if the environment variable is legal
+ */
+ for (end_env = 0; envinit[end_env] != 0; end_env++)
+ ;
+ if ((pam_env = pam_getenvlist(pamh)) != 0) {
+ while (pam_env[idx] != 0) {
+ if (idx < PAM_ENV_ELIM &&
+ legalenvvar(pam_env[idx])) {
+ envinit[end_env + idx] = pam_env[idx];
+ }
+ idx++;
+ }
+ }
+
+ pam_end(pamh, PAM_SUCCESS);
+
+ cp = rindex(pwd->pw_shell, '/');
+ if (cp)
+ cp++;
+ else
+ cp = pwd->pw_shell;
+ (void) execle(pwd->pw_shell, cp, "-c", cmdbuf, (char *)0, envinit);
+ perror(pwd->pw_shell);
+ exit(1);
+}
+
+static void
+getstr(char *buf, int cnt, char *err)
+{
+ char c;
+
+ do {
+ if (read(0, &c, 1) != 1)
+ exit(1);
+ *buf++ = c;
+ if (--cnt == 0) {
+ error("%s too long\n", err);
+ exit(1);
+ }
+ } while (c != 0);
+}
+
+static void
+error(char *fmt, ...)
+{
+ va_list ap;
+ char buf[BUFSIZ];
+
+ buf[0] = 1;
+ va_start(ap, fmt);
+ (void) vsprintf(buf+1, fmt, ap);
+ va_end(ap);
+ (void) write(2, buf, strlen(buf));
+}
+
+static char *illegal[] = {
+ "SHELL=",
+ "HOME=",
+ "LOGNAME=",
+#ifndef NO_MAIL
+ "MAIL=",
+#endif
+ "CDPATH=",
+ "IFS=",
+ "PATH=",
+ "USER=",
+ 0
+};
+
+/*
+ * legalenvvar - can PAM insert this environmental variable?
+ */
+
+static int
+legalenvvar(char *s)
+{
+ register char **p;
+
+ for (p = illegal; *p; p++)
+ if (strncmp(s, *p, strlen(*p)) == 0)
+ return (0);
+
+ if (s[0] == 'L' && s[1] == 'D' && s[2] == '_')
+ return (0);
+
+ return (1);
+}
+
+/*
+ * rexec_conv - This is the conv (conversation) function called from
+ * a PAM authentication module to print error messages
+ * or garner information from the user.
+ */
+
+/* ARGSUSED3 */
+static int
+rexec_conv(int num_msg, struct pam_message **msg,
+ struct pam_response **response, void *appdata_ptr)
+{
+ struct pam_message *m;
+ struct pam_response *r;
+ int i;
+
+ if (num_msg <= 0)
+ return (PAM_CONV_ERR);
+
+ *response = calloc(num_msg, sizeof (struct pam_response));
+ if (*response == NULL)
+ return (PAM_BUF_ERR);
+
+ m = *msg;
+ r = *response;
+
+ if (m->msg_style == PAM_PROMPT_ECHO_OFF) {
+ if (pass[0] != '\0') {
+ r->resp = strdup(pass);
+ if (r->resp == NULL) {
+ /* free responses */
+ r = *response;
+ for (i = 0; i < num_msg; i++, r++) {
+ if (r->resp)
+ free(r->resp);
+ }
+ free(*response);
+ *response = NULL;
+ return (PAM_BUF_ERR);
+ }
+ }
+ }
+
+ return (PAM_SUCCESS);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.rlogind.c b/usr/src/cmd/cmd-inet/usr.sbin/in.rlogind.c
new file mode 100644
index 0000000000..a01c2094f5
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.rlogind.c
@@ -0,0 +1,1631 @@
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright(c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * Copyright (c) 1983 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * remote login server:
+ * remuser\0
+ * locuser\0
+ * terminal info\0
+ * data
+ */
+
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+
+#include <netinet/in.h>
+
+#include <errno.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <netdb.h>
+#include <syslog.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <alloca.h>
+#include <stropts.h>
+#include <sac.h> /* for SC_WILDC */
+#include <utmpx.h>
+#include <sys/filio.h>
+#include <sys/logindmux.h>
+#include <sys/rlioctl.h>
+#include <sys/termios.h>
+#include <sys/tihdr.h>
+#include <arpa/inet.h>
+#include <security/pam_appl.h>
+#include <strings.h>
+#include <com_err.h>
+#include <k5-int.h>
+#include <kcmd.h>
+#include <krb5_repository.h>
+#include <sys/cryptmod.h>
+#include <bsm/adt.h>
+
+#define KRB5_RECVAUTH_V5 5
+#define UT_NAMESIZE sizeof (((struct utmpx *)0)->ut_name)
+
+static char lusername[UT_NAMESIZE+1];
+static char rusername[UT_NAMESIZE+1];
+static char *krusername = NULL;
+static char term[64];
+
+static krb5_ccache ccache = NULL;
+static krb5_keyblock *session_key = NULL;
+static int chksum_flag = 0;
+static int use_auth = 0;
+static enum kcmd_proto kcmd_protocol;
+#ifdef ALLOW_KCMD_V2
+static krb5_data encr_iv = { NULL, 0 };
+static krb5_data decr_iv = { NULL, 0 };
+#endif /* ALLOW_KCMD_V2 */
+
+#define CHKSUM_REQUIRED 0x01
+#define CHKSUM_IGNORED 0x02
+#define VALID_CHKSUM(x) ((x) == 0 || (x) == CHKSUM_REQUIRED ||\
+ (x) == CHKSUM_IGNORED)
+
+#define PWD_IF_FAIL 0x01
+#define PWD_REQUIRED 0x02
+
+#define AUTH_NONE 0x00
+
+#define ARGSTR "k5exEXciM:s:S:D:"
+#define DEFAULT_TOS 16
+
+#define KRB5_PROG_NAME "krlogin"
+
+#define SECURE_MSG "This rlogin session is using encryption " \
+ "for all data transmissions.\r\n"
+
+#define KRB_V5_SENDAUTH_VERS "KRB5_SENDAUTH_V1.0"
+#define KRB5_RECVAUTH_V5 5
+
+static krb5_error_code krb5_compat_recvauth(krb5_context context,
+ krb5_auth_context *auth_context,
+ krb5_pointer fdp,
+ krb5_principal server,
+ krb5_int32 flags,
+ krb5_keytab keytab,
+ krb5_ticket **ticket,
+ krb5_int32 *auth_sys,
+ krb5_data *version);
+
+static void do_krb_login(int, char *, char *, krb5_context, int, krb5_keytab);
+static int configure_stream(int, krb5_keyblock *, int, krb5_data *, uint_t);
+
+extern krb5_error_code krb5_read_message(krb5_context, krb5_pointer,
+ krb5_data *);
+extern krb5_error_code krb5_net_read(krb5_context, int, char *, int);
+
+#define LOGIN_PROGRAM "/bin/login"
+
+#define DEFAULT_PROG_NAME "rlogin"
+
+static const char *pam_prog_name = DEFAULT_PROG_NAME;
+static void rmut(void);
+static void doit(int, struct sockaddr_storage *, krb5_context, int,
+ krb5_keytab);
+static void protocol(int, int, int);
+
+static int readstream(int, char *, int);
+static void fatal(int, const char *);
+static void fatalperror(int, const char *);
+static int send_oob(int fd, void *ptr, size_t count);
+static int removemod(int f, char *modname);
+
+static int
+issock(int fd)
+{
+ struct stat stats;
+
+ if (fstat(fd, &stats) == -1)
+ return (0);
+ return (S_ISSOCK(stats.st_mode));
+}
+
+/*
+ * audit_rlogin_settid stores the terminal id while it is still
+ * available. Subsequent calls to adt_load_hostname() return
+ * the id which is stored here.
+ */
+static int
+audit_rlogin_settid(int fd) {
+ adt_session_data_t *ah;
+ adt_termid_t *termid;
+ int rc;
+
+ if ((rc = adt_start_session(&ah, NULL, 0)) == 0) {
+ if ((rc = adt_load_termid(fd, &termid)) == 0) {
+ if ((rc = adt_set_user(ah, ADT_NO_AUDIT,
+ ADT_NO_AUDIT, 0, ADT_NO_AUDIT,
+ termid, ADT_SETTID)) == 0)
+ (void) adt_set_proc(ah);
+ free(termid);
+ }
+ (void) adt_end_session(ah);
+ }
+ return (rc);
+}
+
+
+/* ARGSUSED */
+void
+main(int argc, char *argv[])
+{
+ int on = 1;
+ socklen_t fromlen;
+ struct sockaddr_storage from;
+ int fd = -1;
+
+ extern char *optarg;
+ char c;
+ int tos = -1;
+ krb5_context krb_context;
+ krb5_keytab keytab = NULL;
+ krb5_error_code status;
+ char *realm = NULL;
+ char *keytab_file = NULL;
+ int encr_flag = 0;
+ struct sockaddr_storage ouraddr;
+ socklen_t ourlen;
+#ifdef DEBUG
+ int debug_port = 0;
+#endif /* DEBUG */
+ openlog("rlogind", LOG_PID | LOG_ODELAY, LOG_DAEMON);
+
+ while ((c = getopt(argc, argv, ARGSTR)) != -1) {
+ switch (c) {
+ case 'k':
+ case '5':
+ use_auth = KRB5_RECVAUTH_V5;
+ break;
+ case 'e':
+ case 'E':
+ case 'x':
+ case 'X':
+ encr_flag = 1;
+ break;
+ case 'M':
+ realm = (char *)strdup(optarg);
+ break;
+ case 'S':
+ keytab_file = (char *)strdup(optarg);
+ break;
+ case 'c':
+ chksum_flag |= CHKSUM_REQUIRED;
+ break;
+ case 'i':
+ chksum_flag |= CHKSUM_IGNORED;
+ break;
+ case 's':
+ if (optarg == NULL || (tos = atoi(optarg)) < 0 ||
+ tos > 255) {
+ syslog(LOG_ERR, "%s: illegal tos value: "
+ "%s\n", argv[0], optarg);
+ } else {
+ if (tos < 0)
+ tos = DEFAULT_TOS;
+ }
+ break;
+#ifdef DEBUG
+ case 'D':
+ debug_port = atoi(optarg);
+ break;
+#endif /* DEBUG */
+ default:
+ syslog(LOG_ERR, "Unrecognized command line option "
+ "(%s), exiting", argv[optind]);
+ exit(EXIT_FAILURE);
+ }
+ }
+ if (use_auth == KRB5_RECVAUTH_V5) {
+ status = krb5_init_context(&krb_context);
+ if (status) {
+ syslog(LOG_ERR, "Error initializing krb5: %s",
+ error_message(status));
+ exit(EXIT_FAILURE);
+ }
+ if (realm != NULL)
+ krb5_set_default_realm(krb_context, realm);
+ if (keytab_file != NULL) {
+ if ((status = krb5_kt_resolve(krb_context,
+ keytab_file,
+ &keytab))) {
+ com_err(argv[0],
+ status,
+ "while resolving srvtab file %s",
+ keytab_file);
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+
+#ifdef DEBUG
+ if (debug_port) {
+ int s;
+ struct sockaddr_in sin;
+
+ if ((s = socket(AF_INET, SOCK_STREAM, PF_UNSPEC)) < 0) {
+ fatalperror(STDERR_FILENO, "Error in socket");
+ }
+
+ (void) memset((char *)&sin, 0, sizeof (sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(debug_port);
+ sin.sin_addr.s_addr = INADDR_ANY;
+
+ (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&on, sizeof (on));
+
+ if ((bind(s, (struct sockaddr *)&sin, sizeof (sin))) < 0) {
+ fatalperror(STDERR_FILENO, "bind error");
+ }
+
+ if ((listen(s, 5)) < 0) {
+ fatalperror(STDERR_FILENO, "listen error");
+ }
+
+ fromlen = sizeof (from);
+ if ((fd = accept(s, (struct sockaddr *)&from, &fromlen)) < 0) {
+ fatalperror(STDERR_FILENO, "accept error");
+ }
+
+ (void) close(s);
+ } else
+#endif /* DEBUG */
+ {
+ if (!issock(STDIN_FILENO))
+ fatal(STDIN_FILENO,
+ "stdin is not a socket file descriptor");
+ fd = STDIN_FILENO;
+ }
+
+ fromlen = sizeof (from);
+ if (getpeername(fd, (struct sockaddr *)&from, &fromlen) < 0)
+ fatalperror(STDERR_FILENO, "getpeername");
+
+ if (audit_rlogin_settid(fd)) /* set terminal ID */
+ fatalperror(STDERR_FILENO, "audit");
+
+ if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *)&on,
+ sizeof (on)) < 0)
+ syslog(LOG_WARNING, "setsockopt(SO_KEEPALIVE): %m");
+
+ if (!VALID_CHKSUM(chksum_flag)) {
+ syslog(LOG_ERR, "Configuration error: mutually exclusive "
+ "options specified (-c and -i)");
+ fatal(fd, "Checksums are required and ignored (-c and -i);"
+ "these options are mutually exclusive - check "
+ "the documentation.");
+ }
+ ourlen = sizeof (ouraddr);
+ if (getsockname(fd, (struct sockaddr *)&ouraddr, &ourlen) == -1) {
+ syslog(LOG_ERR, "getsockname error: %m");
+ exit(EXIT_FAILURE);
+ }
+
+ if (tos != -1 &&
+ ouraddr.ss_family != AF_INET6 &&
+ setsockopt(fd, IPPROTO_IP, IP_TOS, (char *)&tos,
+ sizeof (tos)) < 0 &&
+ errno != ENOPROTOOPT) {
+ syslog(LOG_ERR, "setsockopt(IP_TOS %d): %m", tos);
+ }
+ doit(fd, &from, krb_context, encr_flag, keytab);
+ /*NOTREACHED*/
+}
+
+static void cleanup(int);
+static int nsize = 0; /* bytes read prior to pushing rlmod */
+static char *rlbuf; /* buffer where nbytes are read to */
+static char *line;
+
+static struct winsize win = { 0, 0, 0, 0 };
+static pid_t pid;
+static char hostname[MAXHOSTNAMELEN + 1];
+
+static void
+getstr(int f, char *buf, int cnt, char *err)
+{
+ char c;
+ do {
+ if (read(f, &c, 1) != 1 || (--cnt < 0)) {
+ syslog(LOG_ERR, "Error reading \'%s\' field", err);
+ exit(EXIT_FAILURE);
+ }
+ *buf++ = c;
+ } while (c != '\0');
+}
+
+static krb5_error_code
+recvauth(int f,
+ krb5_context krb_context,
+ unsigned int *valid_checksum,
+ krb5_ticket **ticket,
+ int *auth_type,
+ krb5_principal *client,
+ int encr_flag,
+ krb5_keytab keytab)
+{
+ krb5_error_code status = 0;
+ krb5_auth_context auth_context = NULL;
+ krb5_rcache rcache;
+ krb5_authenticator *authenticator;
+ krb5_data inbuf;
+ krb5_data auth_version;
+
+ *valid_checksum = 0;
+
+ if ((status = krb5_auth_con_init(krb_context, &auth_context)))
+ return (status);
+
+ /* Only need remote address for rd_cred() to verify client */
+ if ((status = krb5_auth_con_genaddrs(krb_context, auth_context, f,
+ KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR)))
+ return (status);
+
+ status = krb5_auth_con_getrcache(krb_context, auth_context, &rcache);
+ if (status)
+ return (status);
+
+ if (!rcache) {
+ krb5_principal server;
+
+ status = krb5_sname_to_principal(krb_context, 0, 0,
+ KRB5_NT_SRV_HST, &server);
+ if (status)
+ return (status);
+
+ status = krb5_get_server_rcache(krb_context,
+ krb5_princ_component(krb_context, server, 0),
+ &rcache);
+ krb5_free_principal(krb_context, server);
+ if (status)
+ return (status);
+
+ status = krb5_auth_con_setrcache(krb_context, auth_context,
+ rcache);
+ if (status)
+ return (status);
+ }
+ if ((status = krb5_compat_recvauth(krb_context,
+ &auth_context,
+ &f,
+ NULL, /* Specify daemon principal */
+ 0, /* no flags */
+ keytab, /* NULL to use v5srvtab */
+ ticket, /* return ticket */
+ auth_type, /* authentication system */
+ &auth_version))) {
+ if (*auth_type == KRB5_RECVAUTH_V5) {
+ /*
+ * clean up before exiting
+ */
+ getstr(f, rusername, sizeof (rusername), "remuser");
+ getstr(f, lusername, sizeof (lusername), "locuser");
+ getstr(f, term, sizeof (term), "Terminal type");
+ }
+ return (status);
+ }
+
+ getstr(f, lusername, sizeof (lusername), "locuser");
+ getstr(f, term, sizeof (term), "Terminal type");
+
+ kcmd_protocol = KCMD_UNKNOWN_PROTOCOL;
+ if (auth_version.length != 9 || auth_version.data == NULL) {
+ syslog(LOG_ERR, "Bad application protocol version length in "
+ "KRB5 exchange, exiting");
+ fatal(f, "Bad application version length, exiting.");
+ }
+ /*
+ * Determine which Kerberos CMD protocol was used.
+ */
+ if (strncmp(auth_version.data, "KCMDV0.1", 9) == 0) {
+ kcmd_protocol = KCMD_OLD_PROTOCOL;
+ } else if (strncmp(auth_version.data, "KCMDV0.2", 9) == 0) {
+ kcmd_protocol = KCMD_NEW_PROTOCOL;
+ } else {
+ syslog(LOG_ERR, "Unrecognized KCMD protocol (%s), exiting",
+ (char *)auth_version.data);
+ fatal(f, "Unrecognized KCMD protocol, exiting");
+ }
+
+ if ((*auth_type == KRB5_RECVAUTH_V5) && chksum_flag &&
+ kcmd_protocol == KCMD_OLD_PROTOCOL) {
+ if ((status = krb5_auth_con_getauthenticator(krb_context,
+ auth_context,
+ &authenticator)))
+ return (status);
+ if (authenticator->checksum) {
+ struct sockaddr_storage adr;
+ int adr_length = sizeof (adr);
+ int buflen;
+ krb5_data input;
+ krb5_keyblock key;
+ char *chksumbuf;
+
+ /*
+ * Define the lenght of the chksum buffer.
+ * chksum string = "[portnum]:termstr:username"
+ * The extra 32 is to hold a integer string for
+ * the portnumber.
+ */
+ buflen = strlen(term) + strlen(lusername) + 32;
+ chksumbuf = (char *)malloc(buflen);
+ if (chksumbuf == 0) {
+ krb5_free_authenticator(krb_context,
+ authenticator);
+ fatal(f, "Out of memory error");
+ }
+
+ if (getsockname(f, (struct sockaddr *)&adr,
+ &adr_length) != 0) {
+ krb5_free_authenticator(krb_context,
+ authenticator);
+ fatal(f, "getsockname error");
+ }
+
+ (void) snprintf(chksumbuf, buflen,
+ "%u:%s%s",
+ ntohs(SOCK_PORT(adr)),
+ term, lusername);
+
+ input.data = chksumbuf;
+ input.length = strlen(chksumbuf);
+ key.contents = (*ticket)->enc_part2->session->contents;
+ key.length = (*ticket)->enc_part2->session->length;
+ status = krb5_c_verify_checksum(krb_context,
+ &key, 0,
+ &input,
+ authenticator->checksum,
+ valid_checksum);
+
+ if (status == 0 && *valid_checksum == 0)
+ status = KRB5KRB_AP_ERR_BAD_INTEGRITY;
+
+ if (chksumbuf)
+ krb5_xfree(chksumbuf);
+ if (status) {
+ krb5_free_authenticator(krb_context,
+ authenticator);
+ return (status);
+ }
+ }
+ krb5_free_authenticator(krb_context, authenticator);
+ }
+
+ if ((status = krb5_copy_principal(krb_context,
+ (*ticket)->enc_part2->client,
+ client)))
+ return (status);
+
+ /* Get the Unix username of the remote user */
+ getstr(f, rusername, sizeof (rusername), "remuser");
+
+ /* Get the Kerberos principal name string of the remote user */
+ if ((status = krb5_unparse_name(krb_context, *client, &krusername)))
+ return (status);
+
+#ifdef DEBUG
+ syslog(LOG_DEBUG | LOG_AUTH, "rlogind: got krb5 credentials for %s",
+ (krusername != NULL ? krusername : "<unknown>"));
+#endif
+
+ if (encr_flag) {
+ status = krb5_auth_con_getremotesubkey(krb_context,
+ auth_context,
+ &session_key);
+ if (status) {
+ syslog(LOG_ERR, "Error getting KRB5 session "
+ "subkey, exiting");
+ fatal(f, "Error getting KRB5 session subkey, exiting");
+ }
+ /*
+ * The "new" protocol requires that a subkey be sent.
+ */
+ if (session_key == NULL &&
+ kcmd_protocol == KCMD_NEW_PROTOCOL) {
+ syslog(LOG_ERR, "No KRB5 session subkey sent, exiting");
+ fatal(f, "No KRB5 session subkey sent, exiting");
+ }
+ /*
+ * The "old" protocol does not permit an authenticator subkey.
+ * The key is taken from the ticket instead (see below).
+ */
+ if (session_key != NULL &&
+ kcmd_protocol == KCMD_OLD_PROTOCOL) {
+ syslog(LOG_ERR, "KRB5 session subkey not permitted "
+ "with old KCMD protocol, exiting");
+
+ fatal(f, "KRB5 session subkey not permitted "
+ "with old KCMD protocol, exiting");
+ }
+ /*
+ * If no key at this point, use the session key from
+ * the ticket.
+ */
+ if (session_key == NULL) {
+ /*
+ * Save the session key so we can configure the crypto
+ * module later.
+ */
+ status = krb5_copy_keyblock(krb_context,
+ (*ticket)->enc_part2->session,
+ &session_key);
+ if (status) {
+ syslog(LOG_ERR, "krb5_copy_keyblock failed");
+ fatal(f, "krb5_copy_keyblock failed");
+ }
+ }
+ /*
+ * If session key still cannot be found, we must
+ * exit because encryption is required here
+ * when encr_flag (-x) is set.
+ */
+ if (session_key == NULL) {
+ syslog(LOG_ERR, "Could not find an encryption key,"
+ "exiting");
+ fatal(f, "Encryption required but key not found, "
+ "exiting");
+ }
+ }
+ /*
+ * Use krb5_read_message to read the principal stuff.
+ */
+ if ((status = krb5_read_message(krb_context, (krb5_pointer)&f,
+ &inbuf)))
+ fatal(f, "Error reading krb5 message");
+
+ if ((inbuf.length) && /* Forwarding being done, read creds */
+ (status = rd_and_store_for_creds(krb_context, auth_context,
+ &inbuf, *ticket, lusername,
+ &ccache))) {
+ if (rcache)
+ (void) krb5_rc_close(krb_context, rcache);
+ fatal(f, "Can't get forwarded credentials");
+ }
+ if (rcache)
+ (void) krb5_rc_close(krb_context, rcache);
+
+ return (status);
+}
+
+static void
+do_krb_login(int f, char *host_addr, char *hostname,
+ krb5_context krb_context, int encr_flag,
+ krb5_keytab keytab)
+{
+ krb5_error_code status;
+ uint_t valid_checksum;
+ krb5_ticket *ticket = NULL;
+ int auth_sys = 0;
+ int auth_sent = 0;
+ krb5_principal client = NULL;
+
+ if (getuid())
+ fatal(f, "Error authorizing KRB5 connection, "
+ "server lacks privilege");
+
+ status = recvauth(f, krb_context, &valid_checksum, &ticket,
+ &auth_sys, &client, encr_flag, keytab);
+ if (status) {
+ if (ticket)
+ krb5_free_ticket(krb_context, ticket);
+ if (status != 255)
+ syslog(LOG_ERR,
+ "Authentication failed from %s(%s): %s\n",
+ host_addr, hostname, error_message(status));
+ fatal(f, "Kerberos authentication failed, exiting");
+ }
+
+ if (auth_sys != KRB5_RECVAUTH_V5) {
+ fatal(f, "This server only supports Kerberos V5");
+ } else {
+ /*
+ * Authenticated OK, now check authorization.
+ */
+ if (client && krb5_kuserok(krb_context, client, lusername))
+ auth_sent = KRB5_RECVAUTH_V5;
+ }
+
+ if (auth_sent == KRB5_RECVAUTH_V5 &&
+ kcmd_protocol == KCMD_OLD_PROTOCOL &&
+ chksum_flag == CHKSUM_REQUIRED && !valid_checksum) {
+ syslog(LOG_ERR, "Client did not supply required checksum, "
+ "connection rejected.");
+ fatal(f, "Client did not supply required checksum, "
+ "connection rejected.");
+ }
+
+ if (auth_sys != auth_sent) {
+ char *msg_fail = NULL;
+ int msgsize = 0;
+
+ if (ticket)
+ krb5_free_ticket(krb_context, ticket);
+
+ if (krusername != NULL) {
+ /*
+ * msgsize must be enough to hold
+ * krusername, lusername and a brief
+ * message describing the failure.
+ */
+ msgsize = strlen(krusername) +
+ strlen(lusername) + 80;
+ msg_fail = (char *)malloc(msgsize);
+ }
+ if (msg_fail == NULL) {
+ syslog(LOG_ERR, "User is not authorized to login to "
+ "specified account");
+
+ fatal(f, "User is not authorized to login to "
+ "specified account");
+ }
+ if (auth_sent != 0)
+ (void) snprintf(msg_fail, msgsize,
+ "Access denied because of improper "
+ "KRB5 credentials");
+ else
+ (void) snprintf(msg_fail, msgsize,
+ "User %s is not authorized to login "
+ "to account %s",
+ krusername, lusername);
+ syslog(LOG_ERR, "%s", msg_fail);
+ fatal(f, msg_fail);
+ }
+}
+
+/*
+ * stop_stream
+ *
+ * Utility routine to send a CRYPTIOCSTOP ioctl to the
+ * crypto module(cryptmod).
+ */
+static void
+stop_stream(int fd, int dir)
+{
+ struct strioctl crioc;
+ uint32_t stopdir = dir;
+
+ crioc.ic_cmd = CRYPTIOCSTOP;
+ crioc.ic_timout = -1;
+ crioc.ic_len = sizeof (stopdir);
+ crioc.ic_dp = (char *)&stopdir;
+
+ if (ioctl(fd, I_STR, &crioc))
+ syslog(LOG_ERR, "Error sending CRYPTIOCSTOP ioctl: %m");
+}
+
+/*
+ * start_stream
+ *
+ * Utility routine to send a CRYPTIOCSTART ioctl to the
+ * crypto module(cryptmod). This routine may contain optional
+ * payload data that the cryptmod will interpret as bytes that
+ * need to be decrypted and sent back up to the application
+ * via the data stream.
+ */
+static void
+start_stream(int fd, int dir)
+{
+ struct strioctl crioc;
+ uint32_t iocval;
+ size_t datalen = 0;
+ char *data = NULL;
+
+ if (dir == CRYPT_DECRYPT) {
+ iocval = CRYPTIOCSTARTDEC;
+
+ /* Look for data not yet processed */
+ if (ioctl(fd, I_NREAD, &datalen) < 0) {
+ syslog(LOG_ERR, "I_NREAD returned error %m");
+ datalen = 0;
+ } else {
+ if (datalen > 0) {
+ data = malloc(datalen);
+ if (data != NULL) {
+ int nbytes = read(fd, data, datalen);
+ datalen = nbytes;
+ } else {
+ syslog(LOG_ERR,
+ "malloc error (%d bytes)",
+ datalen);
+ datalen = 0;
+ }
+ } else {
+ datalen = 0;
+ }
+ }
+ } else {
+ iocval = CRYPTIOCSTARTENC;
+ }
+
+ crioc.ic_cmd = iocval;
+ crioc.ic_timout = -1;
+ crioc.ic_len = datalen;
+ crioc.ic_dp = data;
+
+ if (ioctl(fd, I_STR, &crioc))
+ syslog(LOG_ERR, "Error sending CRYPTIOCSTART ioctl: %m");
+
+ if (data != NULL)
+ free(data);
+}
+
+static int
+configure_stream(int fd, krb5_keyblock *skey, int dir, krb5_data *ivec,
+ uint_t iv_usage)
+{
+ struct cr_info_t setup_info;
+ struct strioctl crioc;
+ int retval = 0;
+
+ switch (skey->enctype) {
+ case ENCTYPE_DES_CBC_CRC:
+ setup_info.crypto_method = CRYPT_METHOD_DES_CBC_CRC;
+ break;
+ case ENCTYPE_DES_CBC_MD5:
+ setup_info.crypto_method = CRYPT_METHOD_DES_CBC_MD5;
+ break;
+ case ENCTYPE_DES_CBC_RAW:
+ setup_info.crypto_method = CRYPT_METHOD_DES_CBC_NULL;
+ break;
+ case ENCTYPE_DES3_CBC_SHA1:
+ setup_info.crypto_method = CRYPT_METHOD_DES3_CBC_SHA1;
+ break;
+ case ENCTYPE_ARCFOUR_HMAC:
+ setup_info.crypto_method = CRYPT_METHOD_ARCFOUR_HMAC_MD5;
+ break;
+ case ENCTYPE_ARCFOUR_HMAC_EXP:
+ setup_info.crypto_method = CRYPT_METHOD_ARCFOUR_HMAC_MD5_EXP;
+ break;
+ case ENCTYPE_AES128_CTS_HMAC_SHA1_96:
+ setup_info.crypto_method = CRYPT_METHOD_AES128;
+ break;
+ case ENCTYPE_AES256_CTS_HMAC_SHA1_96:
+ setup_info.crypto_method = CRYPT_METHOD_AES256;
+ break;
+ default:
+ syslog(LOG_ERR, "Enctype in kerberos session key "
+ "is not supported by crypto module(%d)",
+ skey->enctype);
+ return (-1);
+ }
+ if (ivec == NULL || ivec->length == 0) {
+ (void) memset(&setup_info.ivec, 0, sizeof (setup_info.ivec));
+
+ if (skey->enctype != ENCTYPE_ARCFOUR_HMAC &&
+ skey->enctype != ENCTYPE_ARCFOUR_HMAC_EXP)
+ /* Kerberos IVs are 8 bytes long for DES keys */
+ setup_info.iveclen = KRB5_MIT_DES_KEYSIZE;
+ else
+ setup_info.iveclen = 0;
+ } else {
+ (void) memcpy(&setup_info.ivec, ivec->data, ivec->length);
+ setup_info.iveclen = ivec->length;
+ }
+
+ setup_info.ivec_usage = iv_usage;
+ (void) memcpy(&setup_info.key, skey->contents, skey->length);
+
+ setup_info.keylen = skey->length;
+ setup_info.direction_mask = dir;
+ /*
+ * R* commands get special handling by crypto module -
+ * 4 byte length field is used before each encrypted block
+ * of data.
+ */
+ setup_info.option_mask = (kcmd_protocol == KCMD_OLD_PROTOCOL ?
+ CRYPTOPT_RCMD_MODE_V1 :
+ CRYPTOPT_RCMD_MODE_V2);
+
+ crioc.ic_cmd = CRYPTIOCSETUP;
+ crioc.ic_timout = -1;
+ crioc.ic_len = sizeof (setup_info);
+ crioc.ic_dp = (char *)&setup_info;
+
+ if (ioctl(fd, I_STR, &crioc)) {
+ syslog(LOG_ERR, "Error sending CRYPTIOCSETUP ioctl: %m");
+ retval = -1;
+ }
+ return (retval);
+}
+
+static krb5_error_code
+krb5_compat_recvauth(krb5_context context,
+ krb5_auth_context *auth_context,
+ krb5_pointer fdp, /* IN */
+ krb5_principal server, /* IN */
+ krb5_int32 flags, /* IN */
+ krb5_keytab keytab, /* IN */
+ krb5_ticket **ticket, /* OUT */
+ krb5_int32 *auth_sys, /* OUT */
+ krb5_data *version) /* OUT */
+{
+ krb5_int32 vlen;
+ char *buf;
+ int len, length;
+ krb5_int32 retval;
+ int fd = *((int *)fdp);
+
+ if ((retval = krb5_net_read(context, fd, (char *)&vlen, 4)) != 4)
+ return ((retval < 0) ? errno : ECONNABORTED);
+
+ /*
+ * Assume that we're talking to a V5 recvauth; read in the
+ * the version string, and make sure it matches.
+ */
+ len = (int)ntohl(vlen);
+
+ if (len < 0 || len > 255)
+ return (KRB5_SENDAUTH_BADAUTHVERS);
+
+ buf = malloc(len);
+ if (buf == NULL)
+ return (ENOMEM);
+
+ length = krb5_net_read(context, fd, buf, len);
+ if (len != length) {
+ krb5_xfree(buf);
+ return ((len < 0) ? errno : ECONNABORTED);
+ }
+
+ if (strcmp(buf, KRB_V5_SENDAUTH_VERS) != 0) {
+ krb5_xfree(buf);
+ return (KRB5_SENDAUTH_BADAUTHVERS);
+ }
+ krb5_xfree(buf);
+
+ *auth_sys = KRB5_RECVAUTH_V5;
+
+ retval = krb5_recvauth_version(context, auth_context, fdp,
+ server, flags | KRB5_RECVAUTH_SKIP_VERSION,
+ keytab, ticket, version);
+
+ return (retval);
+}
+
+
+static void
+doit(int f,
+ struct sockaddr_storage *fromp,
+ krb5_context krb_context,
+ int encr_flag,
+ krb5_keytab keytab)
+{
+ int p, t, on = 1;
+ char c;
+ char abuf[INET6_ADDRSTRLEN];
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+ int fromplen;
+ in_port_t port;
+ struct termios tp;
+ boolean_t bad_port;
+ boolean_t no_name;
+ char rhost_addra[INET6_ADDRSTRLEN];
+
+ if (!(rlbuf = malloc(BUFSIZ))) {
+ syslog(LOG_ERR, "rlbuf malloc failed\n");
+ exit(EXIT_FAILURE);
+ }
+ (void) alarm(60);
+ if (read(f, &c, 1) != 1 || c != 0) {
+ syslog(LOG_ERR, "failed to receive protocol zero byte\n");
+ exit(EXIT_FAILURE);
+ }
+ (void) alarm(0);
+ if (fromp->ss_family == AF_INET) {
+ sin = (struct sockaddr_in *)fromp;
+ port = sin->sin_port = ntohs((ushort_t)sin->sin_port);
+ fromplen = sizeof (struct sockaddr_in);
+
+ if (!inet_ntop(AF_INET, &sin->sin_addr,
+ rhost_addra, sizeof (rhost_addra)))
+ goto badconversion;
+ } else if (fromp->ss_family == AF_INET6) {
+ sin6 = (struct sockaddr_in6 *)fromp;
+ port = sin6->sin6_port = ntohs((ushort_t)sin6->sin6_port);
+ fromplen = sizeof (struct sockaddr_in6);
+
+ if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
+ struct in_addr ipv4_addr;
+
+ IN6_V4MAPPED_TO_INADDR(&sin6->sin6_addr,
+ &ipv4_addr);
+ if (!inet_ntop(AF_INET, &ipv4_addr, rhost_addra,
+ sizeof (rhost_addra)))
+ goto badconversion;
+ } else {
+ if (!inet_ntop(AF_INET6, &sin6->sin6_addr,
+ rhost_addra, sizeof (rhost_addra)))
+ goto badconversion;
+ }
+ } else {
+ syslog(LOG_ERR, "unknown address family %d\n",
+ fromp->ss_family);
+ fatal(f, "Permission denied");
+ }
+
+ /*
+ * Allow connections only from the "ephemeral" reserved
+ * ports(ports 512 - 1023) by checking the remote port
+ * because other utilities(e.g. in.ftpd) can be used to
+ * allow a unprivileged user to originate a connection
+ * from a privileged port and provide untrustworthy
+ * authentication.
+ */
+ bad_port = (use_auth != KRB5_RECVAUTH_V5 &&
+ (port >= (in_port_t)IPPORT_RESERVED) ||
+ (port < (in_port_t)(IPPORT_RESERVED/2)));
+ no_name = getnameinfo((const struct sockaddr *) fromp,
+ fromplen, hostname, sizeof (hostname),
+ NULL, 0, 0) != 0;
+
+ if (no_name || bad_port) {
+ (void) strlcpy(abuf, rhost_addra, sizeof (abuf));
+ /* If no host name, use IP address for name later on. */
+ if (no_name)
+ (void) strlcpy(hostname, abuf, sizeof (hostname));
+ }
+
+ if (bad_port) {
+ if (no_name)
+ syslog(LOG_NOTICE,
+ "connection from %s - bad port\n",
+ abuf);
+ else
+ syslog(LOG_NOTICE,
+ "connection from %s(%s) - bad port\n",
+ hostname, abuf);
+ fatal(f, "Permission denied");
+ }
+
+ if (use_auth == KRB5_RECVAUTH_V5) {
+ do_krb_login(f, rhost_addra, hostname,
+ krb_context, encr_flag, keytab);
+ if (krusername != NULL && strlen(krusername)) {
+ /*
+ * Kerberos Authentication succeeded,
+ * so set the proper program name to use
+ * with pam (important during 'cleanup'
+ * routine later).
+ */
+ pam_prog_name = KRB5_PROG_NAME;
+ }
+ }
+
+ if (write(f, "", 1) != 1) {
+ syslog(LOG_NOTICE,
+ "send of the zero byte(to %s) failed:"
+ " cannot start data transfer mode\n",
+ (no_name ? abuf : hostname));
+ exit(EXIT_FAILURE);
+ }
+ if ((p = open("/dev/ptmx", O_RDWR)) == -1)
+ fatalperror(f, "cannot open /dev/ptmx");
+ if (grantpt(p) == -1)
+ fatal(f, "could not grant slave pty");
+ if (unlockpt(p) == -1)
+ fatal(f, "could not unlock slave pty");
+ if ((line = ptsname(p)) == NULL)
+ fatal(f, "could not enable slave pty");
+ if ((t = open(line, O_RDWR)) == -1)
+ fatal(f, "could not open slave pty");
+ if (ioctl(t, I_PUSH, "ptem") == -1)
+ fatalperror(f, "ioctl I_PUSH ptem");
+ if (ioctl(t, I_PUSH, "ldterm") == -1)
+ fatalperror(f, "ioctl I_PUSH ldterm");
+ if (ioctl(t, I_PUSH, "ttcompat") == -1)
+ fatalperror(f, "ioctl I_PUSH ttcompat");
+ /*
+ * POP the sockmod and push the rlmod module.
+ *
+ * Note that sockmod has to be removed since readstream assumes
+ * a "raw" TPI endpoint(e.g. it uses getmsg).
+ */
+ if (removemod(f, "sockmod") < 0)
+ fatalperror(f, "couldn't remove sockmod");
+
+ if (encr_flag) {
+ if (ioctl(f, I_PUSH, "cryptmod") < 0)
+ fatalperror(f, "ioctl I_PUSH rlmod");
+
+ }
+
+ if (ioctl(f, I_PUSH, "rlmod") < 0)
+ fatalperror(f, "ioctl I_PUSH rlmod");
+
+ if (encr_flag) {
+ /*
+ * Make sure rlmod will pass unrecognized IOCTLs to cryptmod
+ */
+ uchar_t passthru = 1;
+ struct strioctl rlmodctl;
+
+ rlmodctl.ic_cmd = CRYPTPASSTHRU;
+ rlmodctl.ic_timout = -1;
+ rlmodctl.ic_len = sizeof (uchar_t);
+ rlmodctl.ic_dp = (char *)&passthru;
+
+ if (ioctl(f, I_STR, &rlmodctl) < 0)
+ fatal(f, "ioctl CRYPTPASSTHRU failed\n");
+ }
+
+ /*
+ * readstream will do a getmsg till it receives
+ * M_PROTO type T_DATA_REQ from rloginmodopen()
+ * indicating all data on the stream prior to pushing rlmod has
+ * been drained at the stream head.
+ */
+ if ((nsize = readstream(f, rlbuf, BUFSIZ)) < 0)
+ fatalperror(f, "readstream failed");
+ /*
+ * Make sure the pty doesn't modify the strings passed
+ * to login as part of the "rlogin protocol." The login
+ * program should set these flags to apropriate values
+ * after it has read the strings.
+ */
+ if (ioctl(t, TCGETS, &tp) == -1)
+ fatalperror(f, "ioctl TCGETS");
+ tp.c_lflag &= ~(ECHO|ICANON);
+ tp.c_oflag &= ~(XTABS|OCRNL);
+ tp.c_iflag &= ~(IGNPAR|ICRNL);
+ if (ioctl(t, TCSETS, &tp) == -1)
+ fatalperror(f, "ioctl TCSETS");
+
+ /*
+ * System V ptys allow the TIOC{SG}WINSZ ioctl to be
+ * issued on the master side of the pty. Luckily, that's
+ * the only tty ioctl we need to do do, so we can close the
+ * slave side in the parent process after the fork.
+ */
+ (void) ioctl(p, TIOCSWINSZ, &win);
+
+ pid = fork();
+ if (pid < 0)
+ fatalperror(f, "fork");
+ if (pid == 0) {
+ int tt;
+ struct utmpx ut;
+
+ /* System V login expects a utmp entry to already be there */
+ (void) memset(&ut, 0, sizeof (ut));
+ (void) strncpy(ut.ut_user, ".rlogin", sizeof (ut.ut_user));
+ (void) strncpy(ut.ut_line, line, sizeof (ut.ut_line));
+ ut.ut_pid = getpid();
+ ut.ut_id[0] = 'r';
+ ut.ut_id[1] = (char)SC_WILDC;
+ ut.ut_id[2] = (char)SC_WILDC;
+ ut.ut_id[3] = (char)SC_WILDC;
+ ut.ut_type = LOGIN_PROCESS;
+ ut.ut_exit.e_termination = 0;
+ ut.ut_exit.e_exit = 0;
+ (void) time(&ut.ut_tv.tv_sec);
+ if (makeutx(&ut) == NULL)
+ syslog(LOG_INFO, "in.rlogind:\tmakeutx failed");
+
+ /* controlling tty */
+ if (setsid() == -1)
+ fatalperror(f, "setsid");
+ if ((tt = open(line, O_RDWR)) == -1)
+ fatalperror(f, "could not re-open slave pty");
+
+ if (close(p) == -1)
+ fatalperror(f, "error closing pty master");
+ if (close(t) == -1)
+ fatalperror(f, "error closing pty slave"
+ " opened before session established");
+ /*
+ * If this fails we may or may not be able to output an
+ * error message.
+ */
+ if (close(f) == -1)
+ fatalperror(f, "error closing deamon stdout");
+ if (dup2(tt, STDIN_FILENO) == -1 ||
+ dup2(tt, STDOUT_FILENO) == -1 ||
+ dup2(tt, STDERR_FILENO) == -1)
+ exit(EXIT_FAILURE); /* Disaster! No stderr! */
+
+ (void) close(tt);
+
+ if (use_auth == KRB5_RECVAUTH_V5 &&
+ krusername != NULL && strlen(krusername)) {
+ (void) execl(LOGIN_PROGRAM, "login",
+ "-d", line,
+ "-r", hostname,
+ "-u", krusername, /* KRB5 principal name */
+ "-s", pam_prog_name,
+ "-t", term, /* Remote Terminal */
+ "-U", rusername, /* Remote User */
+ "-R", KRB5_REPOSITORY_NAME,
+ lusername, /* local user */
+ NULL);
+ } else {
+ (void) execl(LOGIN_PROGRAM, "login",
+ "-d", line,
+ "-r", hostname,
+ NULL);
+ }
+
+ fatalperror(STDERR_FILENO, "/bin/login");
+ /*NOTREACHED*/
+ }
+ (void) close(t);
+ (void) ioctl(f, FIONBIO, &on);
+ (void) ioctl(p, FIONBIO, &on);
+
+ /*
+ * Must ignore SIGTTOU, otherwise we'll stop
+ * when we try and set slave pty's window shape
+ * (our controlling tty is the master pty).
+ * Likewise, we don't want any of the tty-generated
+ * signals from chars passing through.
+ */
+ (void) sigset(SIGTSTP, SIG_IGN);
+ (void) sigset(SIGINT, SIG_IGN);
+ (void) sigset(SIGQUIT, SIG_IGN);
+ (void) sigset(SIGTTOU, SIG_IGN);
+ (void) sigset(SIGTTIN, SIG_IGN);
+ (void) sigset(SIGCHLD, cleanup);
+ (void) setpgrp();
+
+ if (encr_flag) {
+ krb5_data ivec, *ivptr;
+ uint_t ivec_usage;
+ stop_stream(f, CRYPT_ENCRYPT|CRYPT_DECRYPT);
+
+ /*
+ * Configure the STREAMS crypto module. For now,
+ * don't use any IV parameter. KCMDV0.2 support
+ * will require the use of Initialization Vectors
+ * for both encrypt and decrypt modes.
+ */
+ if (kcmd_protocol == KCMD_OLD_PROTOCOL) {
+ if (session_key->enctype == ENCTYPE_DES_CBC_CRC) {
+ /*
+ * This is gross but necessary for MIT compat.
+ */
+ ivec.length = session_key->length;
+ ivec.data = (char *)session_key->contents;
+ ivec_usage = IVEC_REUSE;
+ ivptr = &ivec;
+ } else {
+ ivptr = NULL; /* defaults to all 0's */
+ ivec_usage = IVEC_NEVER;
+ }
+ /*
+ * configure both sides of stream together
+ * since they share the same IV.
+ * This is what makes the OLD KCMD protocol
+ * less secure than the newer one - Bad ivecs.
+ */
+ if (configure_stream(f, session_key,
+ CRYPT_ENCRYPT|CRYPT_DECRYPT,
+ ivptr, ivec_usage) != 0)
+ fatal(f, "Cannot initialize encryption -"
+ " exiting.\n");
+ } else {
+ size_t blocksize;
+ if (session_key->enctype == ENCTYPE_ARCFOUR_HMAC ||
+ session_key->enctype == ENCTYPE_ARCFOUR_HMAC_EXP) {
+ if (configure_stream(f, session_key,
+ CRYPT_ENCRYPT|CRYPT_DECRYPT,
+ NULL, IVEC_NEVER) != 0)
+ fatal(f,
+ "Cannot initialize encryption -"
+ " exiting.\n");
+ goto startcrypto;
+ }
+ if (krb5_c_block_size(krb_context,
+ session_key->enctype,
+ &blocksize)) {
+ syslog(LOG_ERR, "Cannot determine blocksize "
+ "for encryption type %d",
+ session_key->enctype);
+ fatal(f, "Cannot determine blocksize "
+ "for encryption - exiting.\n");
+ }
+ ivec.data = (char *)malloc(blocksize);
+ ivec.length = blocksize;
+ if (ivec.data == NULL)
+ fatal(f, "memory error - exiting\n");
+ /*
+ * Following MIT convention -
+ * encrypt IV = 0x01 x blocksize
+ * decrypt IV = 0x00 x blocksize
+ * ivec_usage = IVEC_ONETIME
+ *
+ * configure_stream separately for encrypt and
+ * decrypt because there are 2 different IVs.
+ *
+ * AES uses 0's for IV.
+ */
+ if (session_key->enctype ==
+ ENCTYPE_AES128_CTS_HMAC_SHA1_96 ||
+ session_key->enctype ==
+ ENCTYPE_AES256_CTS_HMAC_SHA1_96)
+ (void) memset(ivec.data, 0x00, blocksize);
+ else
+ (void) memset(ivec.data, 0x01, blocksize);
+ if (configure_stream(f, session_key, CRYPT_ENCRYPT,
+ &ivec, IVEC_ONETIME) != 0)
+ fatal(f, "Cannot initialize encryption -"
+ " exiting.\n");
+ (void) memset(ivec.data, 0x00, blocksize);
+ if (configure_stream(f, session_key, CRYPT_DECRYPT,
+ &ivec, IVEC_ONETIME) != 0)
+ fatal(f, "Cannot initialize encryption -"
+ " exiting.\n");
+
+ (void) free(ivec.data);
+ }
+startcrypto:
+ start_stream(f, CRYPT_ENCRYPT);
+ start_stream(f, CRYPT_DECRYPT);
+ }
+ protocol(f, p, encr_flag);
+ cleanup(0);
+ /*NOTREACHED*/
+
+badconversion:
+ fatalperror(f, "address conversion");
+ /*NOTREACHED*/
+}
+
+/*
+ * rlogin "protocol" machine.
+ */
+static void
+protocol(int f, int p, int encr_flag)
+{
+ struct stat buf;
+ struct protocol_arg rloginp;
+ struct strioctl rloginmod;
+ int ptmfd; /* fd of logindmux coneected to ptmx */
+ int netfd; /* fd of logindmux connected to netf */
+ static uchar_t oobdata[] = {TIOCPKT_WINDOW};
+
+ /* indicate new rlogin */
+ if (send_oob(f, oobdata, 1) < 0)
+ fatalperror(f, "send_oob");
+ /*
+ * We cannot send the SECURE_MSG until after the
+ * client has been signaled with the oobdata (above).
+ */
+ if (encr_flag) {
+ if (write(f, SECURE_MSG, strlen(SECURE_MSG)) < 0)
+ fatalperror(f, "Error writing SECURE message");
+ }
+
+ /*
+ * Open logindmux driver and link netf and ptmx
+ * underneath logindmux.
+ */
+ if ((ptmfd = open("/dev/logindmux", O_RDWR)) == -1)
+ fatalperror(f, "open /dev/logindmux");
+
+ if ((netfd = open("/dev/logindmux", O_RDWR)) == -1)
+ fatalperror(f, "open /dev/logindmux");
+
+ if (ioctl(ptmfd, I_LINK, p) < 0)
+ fatal(f, "ioctl I_LINK of /dev/ptmx failed\n");
+
+ if (ioctl(netfd, I_LINK, f) < 0)
+ fatal(f, "ioctl I_LINK of tcp connection failed\n");
+
+ /*
+ * Figure out the device number of the ptm's mux fd, and pass that
+ * to the net's mux.
+ */
+ if (fstat(ptmfd, &buf) < 0)
+ fatalperror(f, "cannot determine device number"
+ " of pty side of /dev/logindmux");
+ rloginp.dev = buf.st_rdev;
+ rloginp.flag = 0;
+
+ rloginmod.ic_cmd = LOGDMX_IOC_QEXCHANGE;
+ rloginmod.ic_timout = -1;
+ rloginmod.ic_len = sizeof (struct protocol_arg);
+ rloginmod.ic_dp = (char *)&rloginp;
+
+ if (ioctl(netfd, I_STR, &rloginmod) < 0)
+ fatal(netfd, "ioctl LOGDMX_IOC_QEXCHANGE of netfd failed\n");
+
+ /*
+ * Figure out the device number of the net's mux fd, and pass that
+ * to the ptm's mux.
+ */
+ if (fstat(netfd, &buf))
+ fatalperror(f, "cannot determine device number"
+ " of network side of /dev/logindmux");
+ rloginp.dev = buf.st_rdev;
+ rloginp.flag = 1;
+
+ rloginmod.ic_cmd = LOGDMX_IOC_QEXCHANGE;
+ rloginmod.ic_timout = -1;
+ rloginmod.ic_len = sizeof (struct protocol_arg);
+ rloginmod.ic_dp = (char *)&rloginp;
+
+ if (ioctl(ptmfd, I_STR, &rloginmod) < 0)
+ fatal(netfd, "ioctl LOGDMXZ_IOC_QEXCHANGE of ptmfd failed\n");
+ /*
+ * Send an ioctl type RL_IOC_ENABLE to reenable the
+ * message queue and reinsert the data read from streamhead
+ * at the time of pushing rloginmod module.
+ * We need to send this ioctl even if no data was read earlier
+ * since we need to reenable the message queue of rloginmod module.
+ */
+ rloginmod.ic_cmd = RL_IOC_ENABLE;
+ rloginmod.ic_timout = -1;
+ if (nsize) {
+ rloginmod.ic_len = nsize;
+ rloginmod.ic_dp = rlbuf;
+ } else {
+ rloginmod.ic_len = 0;
+ rloginmod.ic_dp = NULL;
+ }
+
+ if (ioctl(netfd, I_STR, &rloginmod) < 0)
+ fatal(netfd, "ioctl RL_IOC_ENABLE of netfd failed\n");
+
+ /*
+ * User level daemon now pauses till the shell exits.
+ */
+ (void) pause();
+}
+
+/* This is a signal handler, hence the dummy argument */
+/*ARGSUSED*/
+static void
+cleanup(int dummy)
+{
+ rmut();
+ exit(EXIT_FAILURE);
+ /*NOTREACHED*/
+}
+
+/*
+ * TPI style replacement for socket send() primitive, so we don't require
+ * sockmod to be on the stream.
+ */
+static int
+send_oob(int fd, void *ptr, size_t count)
+{
+ struct T_exdata_req exd_req;
+ struct strbuf hdr, dat;
+ int ret;
+
+ exd_req.PRIM_type = T_EXDATA_REQ;
+ exd_req.MORE_flag = 0;
+
+ hdr.buf = (char *)&exd_req;
+ hdr.len = sizeof (exd_req);
+
+ dat.buf = ptr;
+ dat.len = count;
+
+ ret = putmsg(fd, &hdr, &dat, 0);
+ if (ret == 0)
+ ret = count;
+ return (ret);
+}
+
+static void
+fatal(int fd, const char *msg)
+{
+ char *bufp;
+ size_t len = strlen(msg) + 16; /* enough for our wrapper */
+
+ bufp = alloca(len);
+ /* ASCII 001 is the error indicator */
+ len = snprintf(bufp, len, "\01rlogind: %s.\r\n", msg);
+ (void) write(fd, bufp, len);
+ exit(EXIT_FAILURE);
+ /*NOTREACHED*/
+}
+
+/*PRINTFLIKE2*/
+static void
+fatalperror(int fd, const char *msg)
+{
+ char *bufp;
+ const char *errstr;
+ int save_errno = errno;
+ size_t len = strlen(msg);
+
+ if ((errstr = strerror(save_errno))) {
+ len += strlen(errstr) + 3; /* 3 for ": " and \0 below */
+ bufp = alloca(len);
+ (void) snprintf(bufp, len, "%s: %s", msg, errstr);
+ } else {
+ const char fmt[] = "%s: Error %d";
+
+ /* -4 for %s & %d. "*8/3" is bytes->decimal, pessimistically */
+ len += sizeof (fmt) -4 + (sizeof (save_errno) *8 /3);
+ bufp = alloca(len);
+ (void) snprintf(bufp, len, fmt, msg, save_errno);
+ }
+ fatal(fd, bufp);
+ /*NOTREACHED*/
+}
+
+static void
+rmut(void)
+{
+ pam_handle_t *pamh;
+ struct utmpx *up;
+ char user[sizeof (up->ut_user) + 1];
+ char ttyn[sizeof (up->ut_line) + 1];
+ char rhost[sizeof (up->ut_host) + 1];
+
+ /* while cleaning up dont allow disruption */
+ (void) sigset(SIGCHLD, SIG_IGN);
+
+ setutxent();
+ while (up = getutxent()) {
+ if (up->ut_pid == pid) {
+ if (up->ut_type == DEAD_PROCESS)
+ break; /* Cleaned up elsewhere. */
+
+ /*
+ * call pam_close_session if login changed
+ * the utmpx user entry from type LOGIN_PROCESS
+ * to type USER_PROCESS, which happens
+ * after pam_open_session is called.
+ */
+ if (up->ut_type == USER_PROCESS) {
+ (void) strlcpy(user, up->ut_user,
+ sizeof (user));
+ (void) strlcpy(ttyn, up->ut_line,
+ sizeof (ttyn));
+ (void) strlcpy(rhost, up->ut_host,
+ sizeof (rhost));
+
+ /*
+ * Use the same pam_prog_name that
+ * 'login' used.
+ */
+ if ((pam_start(pam_prog_name, user, NULL,
+ &pamh))
+ == PAM_SUCCESS) {
+ (void) pam_set_item(pamh, PAM_TTY,
+ ttyn);
+ (void) pam_set_item(pamh, PAM_RHOST,
+ rhost);
+ (void) pam_close_session(pamh, 0);
+ (void) pam_end(pamh, PAM_SUCCESS);
+ }
+ }
+
+ up->ut_type = DEAD_PROCESS;
+ up->ut_exit.e_termination = WTERMSIG(0);
+ up->ut_exit.e_exit = WEXITSTATUS(0);
+ (void) time(&up->ut_tv.tv_sec);
+
+ if (modutx(up) == NULL) {
+ /*
+ * Since modutx failed we'll
+ * write out the new entry
+ * ourselves.
+ */
+ (void) pututxline(up);
+ updwtmpx("wtmpx", up);
+ }
+ break;
+ }
+ }
+
+ endutxent();
+
+ (void) sigset(SIGCHLD, cleanup);
+}
+
+static int
+readstream(int fd, char *buf, int size)
+{
+ struct strbuf ctlbuf, datbuf;
+ union T_primitives tpi;
+ int nbytes = 0;
+ int ret = 0;
+ int flags = 0;
+ int bufsize = size;
+ int nread;
+
+ (void) memset(&ctlbuf, 0, sizeof (ctlbuf));
+ (void) memset(&datbuf, 0, sizeof (datbuf));
+
+ ctlbuf.buf = (char *)&tpi;
+ ctlbuf.maxlen = sizeof (tpi);
+ datbuf.buf = buf;
+ datbuf.maxlen = size;
+
+ for (;;) {
+ if (ioctl(fd, I_NREAD, &nread) < 0) {
+ syslog(LOG_ERR, "I_NREAD returned error %m");
+ return (-1);
+ }
+ if (nread + nbytes > bufsize) {
+ buf = (char *)realloc(buf, (unsigned)(bufsize + nread));
+ if (buf == NULL) {
+ syslog(LOG_WARNING,
+ "buffer allocation failed\n");
+ return (-1);
+ }
+ bufsize += nread;
+ rlbuf = buf;
+ datbuf.buf = buf + nbytes;
+ }
+ datbuf.maxlen = bufsize - nbytes;
+ ret = getmsg(fd, &ctlbuf, &datbuf, &flags);
+ if (ret < 0) {
+ syslog(LOG_ERR, "getmsg failed error %m");
+ return (-1);
+ }
+ if ((ctlbuf.len == 0) && (datbuf.len == 0)) {
+ /*
+ * getmsg() returned no data - this indicates
+ * that the connection is closing down.
+ */
+ cleanup(0);
+ }
+ if (ctlbuf.len <= 0) {
+ nbytes += datbuf.len;
+ datbuf.buf += datbuf.len;
+ continue;
+ }
+ if (tpi.type == T_DATA_REQ) {
+ return (nbytes);
+ }
+ if ((tpi.type == T_ORDREL_IND) || (tpi.type == T_DISCON_IND))
+ cleanup(0);
+ }
+}
+
+/*
+ * Verify that the named module is at the top of the stream
+ * and then pop it off.
+ */
+static int
+removemod(int f, char *modname)
+{
+ char topmodname[BUFSIZ];
+
+ if (ioctl(f, I_LOOK, topmodname) < 0)
+ return (-1);
+ if (strcmp(modname, topmodname) != 0) {
+ errno = ENXIO;
+ return (-1);
+ }
+ if (ioctl(f, I_POP, 0) < 0)
+ return (-1);
+ return (0);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.routed/Makefile b/usr/src/cmd/cmd-inet/usr.sbin/in.routed/Makefile
new file mode 100644
index 0000000000..8d1cf19e06
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.routed/Makefile
@@ -0,0 +1,82 @@
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+ROUTEDPROG= in.routed
+ROUTEDOBJS= common.o if.o input.o main.o output.o parms.o radix.o \
+ rdisc.o table.o trace.o
+ROUTEDSRCS= $(ROUTEDOBJS:.o=.c)
+RTQUERYPROG= rtquery
+RTQUERYOBJS= common.o rtquery.o
+RTQUERYSRCS= $(RTQUERYOBJS:.o=.c)
+POFILEOBJS= $(ROUTEDOBJS) $(RTQUERYOBJS)
+
+PROG= $(ROUTEDPROG) $(RTQUERYPROG)
+SRCS= $(ROUTEDSRCS) $(RTQUERYSRCS)
+
+include ../../../Makefile.cmd
+
+#
+# in.routed uses ancillary data features available through
+# the Open Group's Networking Services standard. The following
+# pre-processor definitions enable these features.
+#
+_D_XOPEN_EXTN = -D_XOPEN_SOURCE=500 -D__EXTENSIONS__
+
+# Turning on __EXTENSIONS__ breaks lint, and we need __EXTENSIONS__.
+# This is really a lint problem, so around the breakage.
+LINTFLAGS += -erroff=E_FUNC_DECL_VAR_ARG2 -erroff=E_INCONS_VAL_TYPE_DECL2 \
+ -erroff=E_INCONS_ARG_DECL2 -erroff=E_INCONS_ARG_USED2
+
+CPPFLAGS += $(_D_XOPEN_EXTN)
+CFLAGS += $(CCVERBOSE)
+LDLIBS += -lxnet -lmd5 -lsocket
+CLEAN_FILES += $(ROUTEDOBJS) $(RTQUERYOBJS)
+CLOBBERFILES += $(ROUTEDPROG) $(RTQUERYPROG)
+#
+# Message catalog
+#
+POFILE= in.routed.po
+POFILES= $(POFILEOBJS:.o=.po)
+#
+$(ROUTEDPROG):= LDLIBS += -lkstat
+lint := LDLIBS += -lkstat
+
+# This needs to be done because of SPARC/x86 differences. On x86,
+# double has required alignment of only 4 bytes, but on SPARC it's 8
+# bytes. This means that sockaddr_in can be casted to
+# sockaddr_storage without complaint on x86, but requires a
+# suppression directive on SPARC.
+LINTFLAGS += -erroff=E_SUPPRESSION_DIRECTIVE_UNUSED
+
+.KEEP_STATE:
+
+.PARALLEL: $(ROUTEDPROG) $(RTQUERYOBJS)
+
+all: $(PROG)
+
+$(POFILE): $(POFILES)
+ $(RM) $@
+ cat $(POFILES) > $@
+
+$(ROUTEDPROG): $(ROUTEDOBJS)
+ $(LINK.c) -o $@ $(ROUTEDOBJS) $(LDLIBS)
+ $(POST_PROCESS)
+
+$(RTQUERYPROG): $(RTQUERYOBJS)
+ $(LINK.c) -o $@ $(RTQUERYOBJS) $(LDLIBS) -lresolv
+ $(POST_PROCESS)
+
+lint:
+ $(LINT.c) $(ROUTEDSRCS) $(LDLIBS)
+ $(LINT.c) $(RTQUERYSRCS) $(LDLIBS)
+
+install: all $(ROOTUSRSBINPROG) $(MANTARGET)
+
+clean:
+ $(RM) $(CLEAN_FILES)
+
+include ../../../Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.routed/common.c b/usr/src/cmd/cmd-inet/usr.sbin/in.routed/common.c
new file mode 100644
index 0000000000..705eee56ce
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.routed/common.c
@@ -0,0 +1,237 @@
+/*
+ * Copyright 2001-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Common (shared) routines used by in.routed daemon and the
+ * the rtquery utility program
+ */
+
+#include "defs.h"
+#include <ctype.h>
+
+/* Return the classical netmask for an IP address. */
+in_addr_t /* host byte order */
+std_mask(in_addr_t addr) /* network byte order */
+{
+ addr = ntohl(addr);
+
+ if (addr == 0) /* default route has mask 0 */
+ return (0);
+ if (IN_CLASSA(addr))
+ return (IN_CLASSA_NET);
+ if (IN_CLASSB(addr))
+ return (IN_CLASSB_NET);
+ return (IN_CLASSC_NET);
+}
+
+/*
+ * Get a network number as a name or a number, with an optional "/xx"
+ * netmask.
+ */
+boolean_t /* 0=bad */
+getnet(const char *name,
+ in_addr_t *netp, /* network in host byte order */
+ in_addr_t *maskp) /* masks are always in host order */
+{
+ int i;
+ struct netent *np;
+ in_addr_t mask; /* in host byte order */
+ struct in_addr in; /* a network and so host byte order */
+ char hname[MAXHOSTNAMELEN+1];
+ char *mname, *p;
+
+
+ /*
+ * The "name" argument of this function can be one of
+ * the follwoing:
+ * a) network name/mask
+ * b) network name
+ * c) network number/mask
+ * d) network number
+ * e) host IP address/mask
+ * f) host IP address
+ * g) "default"
+ *
+ * Detect and separate "1.2.3.4/24"
+ */
+ if (NULL != (mname = strrchr(name, '/'))) {
+ i = (int)(mname - name);
+ if (i > (int)sizeof (hname)-1) /* name too long */
+ return (_B_FALSE);
+ (void) memmove(hname, name, i);
+ hname[i] = '\0';
+ mname++;
+ name = hname;
+ }
+
+ if ((in.s_addr = inet_network(name)) == (in_addr_t)-1) {
+ if (mname == NULL && strcasecmp(name, "default") == 0)
+ in.s_addr = ntohl(RIP_DEFAULT);
+ else if ((np = getnetbyname(name)) != NULL)
+ in.s_addr = np->n_net;
+ else
+ return (_B_FALSE);
+ }
+ /* Left-align the host-byte-order result from above. */
+ if (0 == (in.s_addr & 0xff000000))
+ in.s_addr <<= 8;
+ if (0 == (in.s_addr & 0xff000000))
+ in.s_addr <<= 8;
+ if (0 == (in.s_addr & 0xff000000))
+ in.s_addr <<= 8;
+
+ if (mname == NULL) {
+ mask = std_mask(htonl(in.s_addr));
+ if ((~mask & in.s_addr) != 0)
+ mask = HOST_MASK;
+ } else {
+ mask = (uint32_t)strtoul(mname, &p, 0);
+ if (*p != '\0' || mask > 32 || mname == p)
+ return (_B_FALSE);
+ if (mask != 0)
+ mask = HOST_MASK << (32-mask);
+ }
+
+ /* must have mask of 0 with default */
+ if (mask != 0 && in.s_addr == RIP_DEFAULT)
+ return (_B_FALSE);
+ /* no host bits allowed in a network number */
+ if ((~mask & in.s_addr) != 0)
+ return (_B_FALSE);
+ /* require non-zero network number */
+ if ((mask & in.s_addr) == 0 && in.s_addr != RIP_DEFAULT)
+ return (_B_FALSE);
+ if ((in.s_addr >> 24) == 0 && in.s_addr != RIP_DEFAULT)
+ return (_B_FALSE);
+ if ((in.s_addr >> 24) == 0xff)
+ return (_B_FALSE);
+
+ *netp = in.s_addr;
+ *maskp = mask;
+ return (_B_TRUE);
+}
+
+/*
+ * Convert string to printable characters
+ */
+char *
+qstring(const uchar_t *srcp, int len)
+{
+ /*
+ * Authentication schemes for RIPv2 uses the space of an
+ * 20-octet route entry.
+ */
+ static char buf[8*20+1];
+ char *prcp, *tmp_ptr;
+ uchar_t c;
+ const uchar_t *s2;
+
+ s2 = srcp + len;
+ while (s2 > srcp && *--s2 == '\0')
+ len--;
+ for (prcp = buf; len != 0 && prcp < &buf[sizeof (buf)-1]; len--) {
+ c = *srcp++;
+ if (isprint(c) && c != '\\') {
+ *prcp++ = c;
+ continue;
+ }
+
+ *prcp++ = '\\';
+ tmp_ptr = strchr("\\\\\nn\rr\tt\bb\aa\ff", c);
+ if (tmp_ptr != NULL)
+ *prcp++ = tmp_ptr[1];
+ else
+ prcp += snprintf(prcp,
+ (sizeof (buf) - (strlen(buf)+1)), "%o", c);
+ }
+ *prcp = '\0';
+ return (buf);
+}
+
+/* like strtok(), but honoring backslash and not changing the source string */
+int /* 0=ok, -1=bad */
+parse_quote(char **linep, /* look here */
+ const char *delims, /* for these delimiters */
+ char *delimp, /* 0 or put found delimiter here */
+ char *buf, /* copy token to here */
+ int lim) /* at most this many bytes */
+{
+ char c = '\0', *pc;
+ const char *p;
+
+
+ pc = *linep;
+ if (*pc == '\0')
+ return (-1);
+
+ while (lim != 0) {
+ c = *pc++;
+ if (c == '\0')
+ break;
+
+ if (c == '\\' && *pc != '\0') {
+ c = *pc++;
+ switch (c) {
+ case 'n':
+ c = '\n';
+ break;
+ case 'r':
+ c = '\r';
+ break;
+ case 't':
+ c = '\t';
+ break;
+ case 'b':
+ c = '\b';
+ }
+ if (c >= '0' && c <= '7') {
+ c -= '0';
+ if (*pc >= '0' && *pc <= '7') {
+ c = (c<<3)+(*pc++ - '0');
+ if (*pc >= '0' && *pc <= '7')
+ c = (c<<3)+(*pc++ - '0');
+ }
+ }
+
+ } else {
+ for (p = delims; *p != '\0'; ++p) {
+ if (*p == c || isspace(c) && *p == ' ')
+ goto exit;
+ }
+ }
+
+ *buf++ = c;
+ --lim;
+ }
+exit:
+ if (lim == 0)
+ return (-1);
+
+ *buf = '\0'; /* terminate copy of token */
+ if (delimp != NULL)
+ *delimp = c; /* return delimiter */
+ *linep = pc-1; /* say where we ended */
+ return (0);
+}
+
+/*
+ * Find the option buffer in the msg corresponding to cmsg_type.
+ */
+void *
+find_ancillary(struct msghdr *msg, int cmsg_type)
+{
+ struct cmsghdr *cmsg;
+
+ for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL;
+ cmsg = CMSG_NXTHDR(msg, cmsg)) {
+ if (cmsg->cmsg_level == IPPROTO_IP &&
+ cmsg->cmsg_type == cmsg_type) {
+ return (CMSG_DATA(cmsg));
+ }
+ }
+ return (NULL);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.routed/defs.h b/usr/src/cmd/cmd-inet/usr.sbin/in.routed/defs.h
new file mode 100644
index 0000000000..0022ac4c15
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.routed/defs.h
@@ -0,0 +1,805 @@
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 1983, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgment:
+ * 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.
+ *
+ * @(#)defs.h 8.1 (Berkeley) 6/5/93
+ *
+ * $FreeBSD: src/sbin/routed/defs.h,v 1.14 2000/08/11 08:24:38 sheldonh Exp $
+ */
+
+#ifndef _DEFS_H
+#define _DEFS_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Definitions for RIPv2 routing process.
+ *
+ * This code is based on the 4.4BSD `routed` daemon, with extensions to
+ * support:
+ * RIPv2, including variable length subnet masks.
+ * Router Discovery
+ * aggregate routes in the kernel tables.
+ * aggregate advertised routes.
+ * maintain spare routes for faster selection of another gateway
+ * when the current gateway dies.
+ * timers on routes with second granularity so that selection
+ * of a new route does not wait 30-60 seconds.
+ * tolerance of static routes.
+ * tell the kernel hop counts.
+ * use of per-interface ip_forwarding state.
+ *
+ * The vestigial support for other protocols has been removed. There
+ * is no likelihood that IETF RIPv1 or RIPv2 will ever be used with
+ * other protocols. The result is far smaller, faster, cleaner, and
+ * perhaps understandable.
+ *
+ * The accumulation of special flags and kludges added over the many
+ * years have been simplified and integrated.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <stdarg.h>
+#include <syslog.h>
+#include <time.h>
+#include <md5.h>
+#include <libintl.h>
+#include <locale.h>
+#include "radix.h"
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/sysmacros.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <net/if_dl.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#define RIPVERSION RIPv2
+#include <protocols/routed.h>
+
+
+#define DAY (24*60*60)
+#define NEVER DAY /* a long time */
+#define EPOCH NEVER /* bias time by this to avoid <0 */
+
+/*
+ * Scan the kernel regularly to see if any interfaces have appeared or been
+ * turned off.
+ */
+#define CHECK_BAD_INTERVAL 5 /* when an interface is known bad */
+#define CHECK_ACT_INTERVAL 30 /* when advertising */
+#define CHECK_QUIET_INTERVAL 300 /* when not */
+
+/*
+ * Limit the seconds in the timeval structure "s" to "l" seconds, but only
+ * if l is less than the current seconds in s. This is used to shorten
+ * certain timers to ensure that scheduled events occur sooner than
+ * originally scheduled.
+ */
+#define LIM_SEC(s, l) ((s).tv_sec = MIN((s).tv_sec, (l)))
+
+/*
+ * Metric used for fake default routes. It ought to be 15, but when
+ * processing advertised routes, previous versions of `routed` added
+ * to the received metric and discarded the route if the total was 16
+ * or larger.
+ */
+#define FAKE_METRIC (HOPCNT_INFINITY-2)
+
+
+/* Router Discovery parameters */
+#define MAX_MAXADVERTISEINTERVAL 1800
+#define MIN_MAXADVERTISEINTERVAL 4
+#define DEF_MAXADVERTISEINTERVAL 600
+#define DEF_PREFERENCELEVEL 0
+#define MIN_PREFERENCELEVEL 0x80000000
+
+#define MAX_INITIAL_ADVERT_INTERVAL 16
+#define MAX_INITIAL_ADVERTS 3
+#define MAX_RESPONSE_DELAY 2
+
+#define MAX_SOLICITATION_DELAY 1
+#define SOLICITATION_INTERVAL 3
+#define MAX_SOLICITATIONS 3
+
+/*
+ * convert between signed, balanced around zero,
+ * and unsigned zero-based preferences
+ */
+#define SIGN_PREF(p) ((p) ^ MIN_PREFERENCELEVEL)
+#define UNSIGN_PREF(p) SIGN_PREF(p)
+
+/*
+ * Bloated packet size for systems that simply add authentication to
+ * full-sized packets
+ */
+#define OVER_MAXPACKETSIZE (MAXPACKETSIZE+sizeof (struct netinfo)*2)
+/* typical packet buffers */
+union pkt_buf {
+ uint8_t packet[OVER_MAXPACKETSIZE*2];
+ struct rip rip;
+};
+
+/*
+ * IF_NAME_LEN is the maximum size of interface names represented within
+ * in.routed. Regular Solaris interfaces have names of at most LIFNAMESIZ
+ * characters, but in.routed has remote interfaces represented internally
+ * as "remote(<gatewayname>)", where <gatewayname> is a hostname or IP
+ * address. IF_NAME_LEN needs to be large enough to also hold such
+ * interface names as well.
+ */
+#define IF_NAME_LEN (MAXHOSTNAMELEN + sizeof ("remote()") + 1)
+
+/*
+ * No more routes than this, to protect ourself in case something goes
+ * whacko and starts broadcasting zillions of bogus routes.
+ */
+#define MAX_ROUTES (128*1024)
+
+enum origin {
+ RO_NONE, /* empty slot */
+ RO_RIP, /* learnt from RIP */
+ RO_RDISC, /* learnt from RDISC */
+ RO_STATIC, /* learnt from kernel */
+ RO_LOOPBCK, /* loopback route */
+ RO_PTOPT, /* point-to-point route */
+ RO_NET_SYN, /* fake net route for subnet */
+ RO_IF, /* interface route */
+ RO_FILE /* from /etc/gateways */
+};
+
+/*
+ * Main, daemon routing table structure
+ */
+struct rt_spare {
+ struct interface *rts_ifp;
+ uint32_t rts_gate; /* forward packets here */
+ uint32_t rts_router; /* on this router's authority */
+ uint8_t rts_metric;
+ enum origin rts_origin;
+ uint16_t rts_tag;
+ time_t rts_time; /* timer to junk stale routes */
+ uint32_t rts_de_ag; /* de-aggregation level */
+ uint16_t rts_flags;
+};
+
+#define RTS_EXTERNAL 0x0001 /* handled by other routing protocol e.g. EGP */
+#define SPARE_INC 2
+#define EMPTY_RT_SPARE { NULL, 0, 0, HOPCNT_INFINITY, RO_NONE, 0, 0, 0, 0 }
+
+struct rt_entry {
+ struct radix_node rt_nodes[2]; /* radix tree glue */
+ struct sockaddr_in rt_dst_sock;
+ time_t rt_poison_time; /* advertised metric */
+ in_addr_t rt_mask;
+ uint32_t rt_seqno; /* when last changed */
+ uint16_t rt_state;
+#define RS_IF 0x0001 /* for network interface */
+#define RS_NET_INT 0x0002 /* authority route */
+#define RS_NET_SYN 0x0004 /* fake net route for subnet */
+#define RS_NO_NET_SYN (RS_LOCAL | RS_IF)
+#define RS_SUBNET 0x0008 /* subnet route from any source */
+#define RS_LOCAL 0x0010 /* loopback for pt-to-pt */
+#define RS_MHOME 0x0020 /* from -m */
+#define RS_STATIC 0x0040 /* from the kernel */
+#define RS_NOPROPAGATE 0x0080 /* route which must not be propagated */
+#define RS_BADIF 0x0100 /* route through dead ifp */
+
+ uint8_t rt_poison_metric; /* to notice maximum recently */
+ uint_t rt_num_spares;
+ struct rt_spare *rt_spares;
+};
+#define rt_dst rt_dst_sock.sin_addr.s_addr
+#define rt_ifp rt_spares[0].rts_ifp
+#define rt_gate rt_spares[0].rts_gate
+#define rt_router rt_spares[0].rts_router
+#define rt_metric rt_spares[0].rts_metric
+#define rt_tag rt_spares[0].rts_tag
+#define rt_time rt_spares[0].rts_time
+#define rt_de_ag rt_spares[0].rts_de_ag
+
+#define HOST_MASK 0xffffffffU
+#define RT_ISHOST(rt) ((rt)->rt_mask == HOST_MASK)
+
+/*
+ * Determine if a route should be aged. Age all routes that are:
+ * Not from -g, -m, nor static routes from the kernel
+ * not unbroken interface routes but not broken interfaces
+ * not learnt from RDISC or from /etc/gateways
+ * nor non-passive, remote interfaces that are not aliases
+ * (i.e. remote & metric=0)
+ */
+#define AGE_RT(rt_state, rts_origin, ifp) \
+ ((!((rt_state) & (RS_MHOME | RS_STATIC | RS_NET_SYN)) && \
+ (rts_origin != RO_RDISC) && \
+ (rts_origin != RO_FILE)) && \
+ (!((rt_state) & RS_IF) || \
+ (ifp) == NULL || \
+ (((ifp)->int_state & IS_REMOTE) && \
+ !((ifp)->int_state & IS_PASSIVE))))
+
+/*
+ * true if A is better than B
+ * Better if
+ * - A is not a poisoned route
+ * - and A is not stale
+ * - and either:
+ * - A has a shorter path
+ * - or the router is speaking for itself
+ * - or B has the same metric and isn't stale
+ * - or A is a host route advertised by a system for itself
+ */
+#define BETTER_LINK(rt, A, B) ((A)->rts_metric < HOPCNT_INFINITY && \
+ now_stale <= (A)->rts_time && \
+ ((A)->rts_metric < (B)->rts_metric || \
+ ((A)->rts_gate == (A)->rts_router && \
+ (B)->rts_gate != (B)->rts_router) || \
+ ((A)->rts_metric == (B)->rts_metric && \
+ now_stale > (B)->rts_time) || \
+ (RT_ISHOST(rt) && \
+ (rt)->rt_dst == (A)->rts_router && \
+ (A)->rts_metric == (B)->rts_metric)))
+
+struct hlinkage {
+ void *hl_next;
+ void **hl_prev;
+};
+
+/*
+ * A "physical_interface" represents the actual hardware. It is also
+ * a container for a list of the interfaces that have the same ifIndex
+ * number. This will consist of zero or one "main" interface plus
+ * zero or more IS_ALIAS interfaces.
+ */
+struct physical_interface {
+ struct hlinkage phyi_link;
+ uint32_t phyi_index;
+ struct interface *phyi_interface;
+ struct phyi_data {
+ uint32_t ipackets; /* previous network stats */
+ uint32_t ierrors;
+ uint32_t opackets;
+ uint32_t oerrors;
+ time_t ts; /* timestamp on network stats */
+ } phyi_data;
+ char phyi_name[IF_NAME_LEN+1];
+};
+
+/*
+ * An "interface" is similar to a kernel ifnet structure, except it also
+ * handles "logical" or "IS_REMOTE" interfaces (remote gateways).
+ */
+struct interface {
+ /*
+ * We keep interfaces in a variety of data structures to
+ * optimize for different types of searches.
+ */
+ struct hlinkage int_link;
+#define int_next int_link.hl_next
+ struct hlinkage int_ahash; /* by address */
+ struct hlinkage int_bhash; /* by broadcast address */
+ struct hlinkage int_nhash; /* by name */
+ struct hlinkage int_ilist; /* ifIndex list */
+ struct physical_interface *int_phys; /* backpointer */
+ char int_name[IF_NAME_LEN+1];
+ in_addr_t int_addr; /* address on this host (net order) */
+ in_addr_t int_brdaddr; /* broadcast address (n) */
+ in_addr_t int_dstaddr; /* other end of pt-to-pt link (n) */
+ in_addr_t int_net; /* working network # (host order) */
+ in_addr_t int_mask; /* working net mask (host order) */
+ in_addr_t int_ripv1_mask; /* for inferring a mask (n) */
+ in_addr_t int_std_addr; /* class A/B/C address (n) */
+ in_addr_t int_std_net; /* class A/B/C network (h) */
+ in_addr_t int_std_mask; /* class A/B/C netmask (h) */
+ in_addr_t int_ripout_addr; /* RIP pkts sent to this addr */
+ int int_if_flags; /* some bits copied from kernel */
+ uint32_t int_state;
+ time_t int_act_time; /* last thought healthy (IS_REMOTE) */
+ time_t int_query_time; /* last query (IS_REMOTE) */
+ uint32_t int_transitions; /* times gone up-down */
+ uint8_t int_metric;
+ uint8_t int_d_metric; /* for faked default route */
+#define MAX_AUTH_KEYS 5
+ struct auth { /* authentication info */
+ time_t start, end;
+ uint16_t type;
+ /*
+ * Although the following key is just an array of bytes,
+ * in.routed is currently limited to ascii characters
+ * because of its configuration syntax and parsing.
+ */
+ uint8_t key[RIP_AUTH_PW_LEN +1];
+ uint8_t keyid;
+ uint8_t warnedflag;
+ } int_auth[MAX_AUTH_KEYS];
+ /* router discovery parameters */
+ int int_rdisc_pref; /* signed preference to advertise */
+ uint32_t int_rdisc_int; /* MaxAdvertiseInterval */
+ uint32_t int_rdisc_cnt;
+ struct timeval int_rdisc_timer;
+};
+
+/* bits in int_state */
+#define IS_ALIAS 0x00000001 /* interface alias */
+#define IS_SUBNET 0x00000002 /* interface on subnetted network */
+#define IS_REMOTE 0x00000004 /* interface is not on this machine */
+#define IS_PASSIVE 0x00000008 /* remote and does not do RIP */
+#define IS_EXTERNAL 0x00000010 /* handled by EGP or something */
+#define IS_CHECKED 0x00000020 /* still exists */
+#define IS_ALL_HOSTS 0x00000040 /* in INADDR_ALLHOSTS_GROUP */
+#define IS_ALL_ROUTERS 0x00000080 /* in INADDR_ALLROUTERS_GROUP */
+#define IS_DISTRUST 0x00000100 /* ignore untrusted routers */
+#define IS_REDIRECT_OK 0x00000200 /* accept ICMP redirects */
+#define IS_BROKE 0x00000400 /* seems to be broken */
+#define IS_SICK 0x00000800 /* seems to be broken */
+#define IS_DUP 0x00001000 /* duplicates another interface */
+#define IS_NEED_NET_SYN 0x00002000 /* need RS_NET_SYN route */
+#define IS_NO_AG 0x00004000 /* do not aggregate subnets */
+#define IS_NO_SUPER_AG 0x00008000 /* do not aggregate networks */
+#define IS_NO_RIPV1_IN 0x00010000 /* no RIPv1 input at all */
+#define IS_NO_RIPV2_IN 0x00020000 /* no RIPv2 input at all */
+#define IS_NO_RIP_IN (IS_NO_RIPV1_IN | IS_NO_RIPV2_IN)
+#define IS_RIP_IN_OFF(s) (((s) & IS_NO_RIP_IN) == IS_NO_RIP_IN)
+#define IS_NO_RIPV1_OUT 0x00040000 /* no RIPv1 output at all */
+#define IS_NO_RIPV2_OUT 0x00080000 /* no RIPv2 output at all */
+#define IS_NO_RIP_OUT (IS_NO_RIPV1_OUT | IS_NO_RIPV2_OUT)
+#define IS_NO_RIP (IS_NO_RIP_OUT | IS_NO_RIP_IN)
+#define IS_RIP_OUT_OFF(s) (((s) & IS_NO_RIP_OUT) == IS_NO_RIP_OUT)
+#define IS_RIP_OFF(s) (((s) & IS_NO_RIP) == IS_NO_RIP)
+#define IS_NO_RIP_MCAST 0x00100000 /* broadcast RIPv2 */
+#define IS_NO_ADV_IN 0x00200000 /* do not listen to advertisements */
+#define IS_NO_SOL_OUT 0x00400000 /* send no solicitations */
+#define IS_SOL_OUT 0x00800000 /* send solicitations */
+#define GROUP_IS_SOL_OUT (IS_SOL_OUT | IS_NO_SOL_OUT)
+#define IS_NO_ADV_OUT 0x01000000 /* do not advertise rdisc */
+#define IS_ADV_OUT 0x02000000 /* advertise rdisc */
+#define GROUP_IS_ADV_OUT (IS_NO_ADV_OUT | IS_ADV_OUT)
+#define IS_BCAST_RDISC 0x04000000 /* broadcast instead of multicast */
+#define IS_NO_RDISC (IS_NO_ADV_IN | IS_NO_SOL_OUT | IS_NO_ADV_OUT)
+#define IS_PM_RDISC 0x08000000 /* poor-man's router discovery */
+#define IS_NO_HOST 0x10000000 /* disallow host routes */
+#define IS_SUPPRESS_RDISC 0x20000000 /* don't send rdisc advs */
+#define IS_FLUSH_RDISC 0x40000000 /* flush client rdisc caches */
+
+/*
+ * passive interfaces are added through gwkludge
+ */
+#define IS_PASSIVE_IFP(ifp) \
+ (((ifp)->int_state & (IS_REMOTE|IS_PASSIVE|IS_EXTERNAL|IS_ALIAS)) == \
+ (IS_REMOTE|IS_PASSIVE))
+
+/*
+ * Is an IP interface up? Because of the way IPMP uses deprecated
+ * interfaces, we need to check more than the IFF_UP and IFF_RUNNING
+ * interface flags here. Basically, we do not want to use IFF_DEPRECATED
+ * interfaces unless they are also IFF_STANDBY and not IFF_INACTIVE.
+ */
+#define IFF_GOOD (IFF_UP|IFF_RUNNING)
+#define IS_IFF_UP(f) \
+ ((((f) & (IFF_GOOD|IFF_DEPRECATED)) == IFF_GOOD) || \
+ (((f) & (IFF_GOOD|IFF_INACTIVE|IFF_STANDBY)) == \
+ (IFF_GOOD|IFF_STANDBY)))
+
+/*
+ * This defines interfaces that we should not use for advertising or
+ * soliciting routes by way of RIP and rdisc. Interfaces marked this
+ * way do not count for purposes of determining how many interfaces
+ * this router has.
+ */
+#define IS_IFF_QUIET(f) ((f) & (IFF_LOOPBACK|IFF_NORTEXCH|IFF_NOXMIT))
+
+/*
+ * This defines interfaces that we can use for advertising routes by way of
+ * RIP and rdisc.
+ */
+#define IS_IFF_ROUTING(f) \
+ (((f) & IFF_ROUTER) && !((f) & (IFF_NORTEXCH|IFF_NOXMIT)))
+
+/* Information for aggregating routes */
+#define NUM_AG_SLOTS 32
+struct ag_info {
+ struct ag_info *ag_fine; /* slot with finer netmask */
+ struct ag_info *ag_cors; /* more coarse netmask */
+ in_addr_t ag_dst_h; /* destination in host byte order */
+ in_addr_t ag_mask;
+ in_addr_t ag_gate;
+ struct interface *ag_ifp;
+ in_addr_t ag_nhop;
+ uint8_t ag_metric; /* metric to be advertised */
+ uint8_t ag_pref; /* aggregate based on this */
+ uint32_t ag_seqno;
+ uint16_t ag_tag;
+ uint16_t ag_state;
+#define AGS_SUPPRESS 0x001 /* combine with coarser mask */
+#define AGS_AGGREGATE 0x002 /* synthesize combined routes */
+#define AGS_REDUN0 0x004 /* redundant, finer routes output */
+#define AGS_REDUN1 0x008
+#define AG_IS_REDUN(state) (((state) & (AGS_REDUN0 | AGS_REDUN1)) \
+ == (AGS_REDUN0 | AGS_REDUN1))
+#define AGS_GATEWAY 0x010 /* tell kernel RTF_GATEWAY */
+#define AGS_IF 0x020 /* for an interface */
+#define AGS_RIPV2 0x040 /* send only as RIPv2 */
+#define AGS_FINE_GATE 0x080 /* ignore differing ag_gate when */
+ /* this has the finer netmask */
+#define AGS_CORS_GATE 0x100 /* ignore differing gate when this */
+ /* has the coarser netmasks */
+#define AGS_SPLIT_HZ 0x200 /* suppress for split horizon */
+#define AGS_PASSIVE 0x400 /* passive "remote" interface route */
+#define AGS_FILE 0x800 /* from /etc/gateways */
+
+ /* some bits are set if they are set on either route */
+#define AGS_AGGREGATE_EITHER (AGS_RIPV2 | AGS_GATEWAY | \
+ AGS_SUPPRESS | AGS_CORS_GATE)
+};
+
+struct khash {
+ struct khash *k_next;
+ in_addr_t k_dst;
+ in_addr_t k_mask;
+ in_addr_t k_gate;
+ struct interface *k_ifp;
+ short k_metric;
+ ushort_t k_state; /* KS_* */
+ time_t k_keep;
+ time_t k_redirect_time; /* when redirected route 1st seen */
+};
+
+/* bit flags for k_state; shared between table.c and trace.c */
+#define KS_NEW 0x0001
+#define KS_DELETE 0x0002 /* need to delete the route */
+#define KS_ADD 0x0004 /* add to the kernel */
+#define KS_CHANGE 0x0008 /* tell kernel to change the route */
+#define KS_DEL_ADD 0x0010 /* delete & add to change the kernel */
+#define KS_STATIC 0x0020 /* Static flag in kernel */
+#define KS_GATEWAY 0x0040 /* G flag in kernel */
+#define KS_DYNAMIC 0x0080 /* result of redirect */
+#define KS_DELETED 0x0100 /* already deleted from kernel */
+#define KS_PRIVATE 0x0200 /* Private flag in kernel */
+#define KS_CHECK 0x0400
+#define KS_IF 0x0800 /* interface route */
+#define KS_PASSIVE 0x1000 /* passive remote interface route */
+#define KS_DEPRE_IF 0x2000 /* IPMP deprecated interface route */
+#define KS_FILE 0x4000 /* from /etc/gateways */
+
+/* default router structure */
+struct dr { /* accumulated advertisements */
+ struct interface *dr_ifp;
+ in_addr_t dr_gate; /* gateway */
+ time_t dr_ts; /* when received */
+ time_t dr_life; /* lifetime in host byte order */
+ uint32_t dr_recv_pref; /* received but biased preference */
+ uint32_t dr_pref; /* preference adjusted by metric */
+ uint32_t dr_flags;
+#define DR_CHANGED 1 /* received new info for known dr */
+};
+
+/* parameters for interfaces */
+struct parm {
+ struct parm *parm_next;
+ in_addr_t parm_net;
+ in_addr_t parm_mask;
+ in_addr_t parm_ripout_addr;
+ uint32_t parm_int_state;
+ int32_t parm_rdisc_pref; /* signed IRDP preference */
+ uint32_t parm_rdisc_int; /* IRDP advertising interval */
+ struct auth parm_auth[MAX_AUTH_KEYS];
+ char parm_name[IF_NAME_LEN+1];
+ uint8_t parm_d_metric;
+};
+
+/* authority for internal networks */
+extern struct intnet {
+ struct intnet *intnet_next;
+ in_addr_t intnet_addr; /* network byte order */
+ in_addr_t intnet_mask;
+ int8_t intnet_metric;
+} *intnets;
+
+/*
+ * Defined RIPv1 netmasks. These come from ripv1_mask entries in
+ * /etc/gateways of the form:
+ *
+ * ripv1_mask=<net>/<match>,<mask>
+ *
+ * The intended use of these structures is to give RIPv1 destinations which
+ * are in <net>/<match> a netmask of <mask>, where <mask> > <match>.
+ */
+extern struct r1net {
+ struct r1net *r1net_next;
+ in_addr_t r1net_net; /* host order */
+ in_addr_t r1net_match;
+ in_addr_t r1net_mask;
+} *r1nets;
+
+/* trusted routers */
+extern struct tgate {
+ struct tgate *tgate_next;
+ in_addr_t tgate_addr;
+#define MAX_TGATE_NETS 32
+ struct tgate_net {
+ in_addr_t net; /* host order */
+ in_addr_t mask;
+ } tgate_nets[MAX_TGATE_NETS];
+} *tgates;
+
+enum output_type {OUT_QUERY, OUT_UNICAST, OUT_BROADCAST, OUT_MULTICAST,
+ NO_OUT_MULTICAST, NO_OUT_RIPV2};
+
+/* common output buffers */
+extern struct ws_buf {
+ struct rip *buf;
+ struct netinfo *n;
+ struct netinfo *base;
+ struct netinfo *lim;
+ enum output_type type;
+} v12buf;
+
+extern int stopint; /* !=0 to stop in.routed */
+
+extern int rip_sock; /* RIP socket */
+extern struct interface *rip_sock_interface; /* current output interface */
+extern int rt_sock; /* routing socket */
+extern int rdisc_sock; /* router-discovery raw socket */
+
+extern boolean_t rip_enabled; /* is rip on? */
+extern boolean_t supplier; /* process should supply updates */
+extern boolean_t supplier_set; /* -s or -q requested */
+extern boolean_t save_space; /* -S option 1=treat all RIP speakers */
+extern boolean_t ridhosts; /* 1=reduce host routes */
+extern boolean_t mhome; /* 1=want multi-homed host route */
+extern boolean_t advertise_mhome; /* 1=must continue advertising it */
+extern boolean_t auth_ok; /* 1=ignore auth if we do not care */
+extern boolean_t no_install; /* 1=don't install in kernel */
+
+extern struct timeval clk; /* system clock's idea of time */
+extern struct timeval epoch; /* system clock when started */
+extern struct timeval now; /* current idea of time */
+extern time_t now_stale;
+extern time_t now_expire;
+extern time_t now_garbage;
+
+extern struct timeval age_timer; /* next check of old routes */
+extern struct timeval no_flash; /* inhibit flash update until then */
+extern struct timeval rdisc_timer; /* next advert. or solicitation */
+extern boolean_t rdisc_ok; /* using solicited route */
+
+extern struct timeval ifscan_timer; /* time to check interfaces */
+
+extern in_addr_t loopaddr; /* our address on loopback */
+extern uint_t tot_interfaces; /* # of remote and local interfaces */
+extern uint_t rip_interfaces; /* # of interfaces doing RIP */
+extern uint_t ripout_interfaces; /* # of interfaces advertising RIP */
+extern uint_t fwd_interfaces; /* # of interfaces ip_forwarding=1 */
+extern struct interface *ifnet; /* all interfaces */
+extern size_t hash_table_sizes[]; /* list of primes for hash tables */
+extern boolean_t have_ripv1_out; /* have a RIPv1 interface */
+extern boolean_t need_flash; /* flash update needed */
+extern struct timeval need_kern; /* need to update kernel table */
+extern uint32_t update_seqno; /* a route has changed */
+extern struct interface dummy_ifp; /* wildcard interface */
+
+extern int tracelevel, new_tracelevel;
+#define MAX_TRACELEVEL 5
+#define TRACERTS (tracelevel >= 5) /* log routing socket contents */
+#define TRACEKERNEL (tracelevel >= 4) /* log kernel changes */
+#define TRACECONTENTS (tracelevel >= 3) /* display packet contents */
+#define TRACEPACKETS (tracelevel >= 2) /* note packets */
+#define TRACEACTIONS (tracelevel != 0)
+extern FILE *ftrace; /* output trace file */
+extern char inittracename[MAXPATHLEN+1];
+
+extern struct radix_node_head *rhead;
+
+extern void fix_sock(int, const char *);
+extern void fix_select(void);
+extern void rip_off(void);
+extern void rip_on(struct interface *);
+
+extern void bufinit(void);
+extern int output(enum output_type, struct sockaddr_in *,
+ struct interface *, struct rip *, int);
+extern void clr_ws_buf(struct ws_buf *, struct auth *);
+extern void rip_query(void);
+extern void rip_bcast(int);
+extern void supply(struct sockaddr_in *, struct interface *,
+ enum output_type, int, int, boolean_t);
+
+extern void msglog(const char *, ...);
+extern void writelog(int, const char *, ...);
+struct msg_limit {
+ time_t reuse;
+ struct msg_sub {
+ in_addr_t addr;
+ time_t until;
+#define MSG_SUBJECT_N 8
+ } subs[MSG_SUBJECT_N];
+};
+extern void msglim(struct msg_limit *, in_addr_t, const char *, ...);
+#define LOGERR(msg) msglog(msg ": %s", rip_strerror(errno))
+extern void logbad(boolean_t, const char *, ...);
+#define BADERR(dump, msg) logbad(dump, msg ": %s", rip_strerror(errno))
+#ifdef DEBUG
+#define DBGERR(dump, msg) BADERR(dump, msg)
+#else
+#define DBGERR(dump, msg) LOGERR(msg)
+#endif
+extern char *naddr_ntoa(in_addr_t);
+extern const char *saddr_ntoa(struct sockaddr_storage *);
+extern const char *rip_strerror(int errnum);
+extern char *if_bit_string(uint_t, boolean_t);
+
+extern void *rtmalloc(size_t, const char *);
+extern void timevaladd(struct timeval *, struct timeval *);
+extern void intvl_random(struct timeval *, ulong_t, ulong_t);
+extern boolean_t getnet(const char *, in_addr_t *, in_addr_t *);
+extern int gethost(char *, in_addr_t *);
+extern void gwkludge(void);
+extern const char *parse_parms(char *, boolean_t);
+extern const char *insert_parm(struct parm *);
+extern void get_parms(struct interface *);
+
+extern void lastlog(void);
+extern void trace_close(int);
+extern void set_tracefile(const char *, const char *, int);
+extern void tracelevel_msg(const char *, int);
+extern void trace_off(const char *, ...);
+extern void set_tracelevel(void);
+extern void trace_flush(void);
+extern void trace_misc(const char *, ...);
+extern void trace_act(const char *, ...);
+extern void trace_pkt(const char *, ...);
+extern void trace_add_del(const char *, struct rt_entry *);
+extern void trace_change(struct rt_entry *, uint16_t, struct rt_spare *,
+ const char *);
+extern void trace_if(const char *, struct interface *);
+extern void trace_khash(const struct khash *);
+extern void trace_dr(const struct dr *);
+extern void trace_upslot(struct rt_entry *, struct rt_spare *,
+ struct rt_spare *);
+extern void trace_rip(const char *, const char *, struct sockaddr_in *,
+ struct interface *, struct rip *, int);
+extern char *addrname(in_addr_t, in_addr_t, int);
+extern char *rtname(in_addr_t, in_addr_t, in_addr_t);
+
+extern void rdisc_age(in_addr_t);
+extern void set_rdisc_mg(struct interface *, int);
+extern void set_supplier(void);
+extern void if_bad_rdisc(struct interface *);
+extern void if_rewire_rdisc(struct interface *, struct interface *);
+extern void if_ok_rdisc(struct interface *);
+extern int read_rip(void);
+extern void input_route(in_addr_t, in_addr_t, struct rt_spare *,
+ struct netinfo *, uint16_t);
+extern void read_rt(void);
+extern void read_d(void);
+extern void rdisc_adv(boolean_t);
+extern void rdisc_sol(void);
+extern struct interface *receiving_interface(struct msghdr *, boolean_t);
+extern void *find_ancillary(struct msghdr *, int);
+extern boolean_t should_supply(struct interface *);
+extern void rdisc_dump(void);
+extern void rdisc_suppress(struct interface *);
+extern void rdisc_restore(struct interface *);
+
+extern void age_peer_info(void);
+
+extern void sigtrace_more(int);
+extern void sigtrace_less(int);
+extern void sigtrace_dump(int);
+
+extern void sync_kern(void);
+extern void age(in_addr_t);
+extern void kern_dump(void);
+extern void kern_flush_ifp(struct interface *);
+extern void kern_rewire_ifp(struct interface *, struct interface *);
+
+extern void ag_flush(in_addr_t, in_addr_t, void (*)(struct ag_info *));
+extern void ag_check(in_addr_t, in_addr_t, in_addr_t, struct interface *,
+ in_addr_t, uint8_t, uint8_t, uint32_t, uint16_t, uint16_t,
+ void (*)(struct ag_info *));
+extern void del_static(in_addr_t, in_addr_t, in_addr_t,
+ struct interface *, int);
+extern void del_redirects(in_addr_t, time_t);
+extern struct rt_entry *rtget(in_addr_t, in_addr_t);
+extern struct rt_entry *rtfind(in_addr_t);
+extern void rtinit(void);
+extern void rtadd(in_addr_t, in_addr_t, uint16_t, struct rt_spare *);
+extern void rtchange(struct rt_entry *, uint16_t, struct rt_spare *,
+ char *);
+extern void rtdelete(struct rt_entry *);
+extern void rts_delete(struct rt_entry *, struct rt_spare *);
+extern void rtbad_sub(struct rt_entry *, struct interface *);
+extern void rtswitch(struct rt_entry *, struct rt_spare *);
+
+#define S_ADDR(x) (((struct sockaddr_in *)(x))->sin_addr.s_addr)
+#define INFO_DST(I) ((I)->rti_info[RTAX_DST])
+#define INFO_GATE(I) ((I)->rti_info[RTAX_GATEWAY])
+#define INFO_MASK(I) ((I)->rti_info[RTAX_NETMASK])
+#define INFO_AUTHOR(I) ((I)->rti_info[RTAX_AUTHOR])
+
+struct rewire_data {
+ struct interface *if_old;
+ struct interface *if_new;
+ int metric_delta;
+};
+
+extern char *qstring(const uchar_t *, int);
+extern in_addr_t std_mask(in_addr_t);
+extern int parse_quote(char **, const char *, char *, char *, int);
+extern in_addr_t ripv1_mask_net(in_addr_t, const struct interface *);
+extern in_addr_t ripv1_mask_host(in_addr_t, const struct interface *);
+#define on_net(a, net, mask) (((ntohl(a) ^ (net)) & (mask)) == 0)
+extern boolean_t check_dst(in_addr_t);
+extern boolean_t remote_address_ok(struct interface *, in_addr_t);
+extern struct interface *check_dup(const char *, in_addr_t, in_addr_t,
+ in_addr_t, int, boolean_t);
+extern boolean_t check_remote(struct interface *);
+extern void iftbl_alloc(void);
+extern void ifscan(void);
+extern int walk_bad(struct radix_node *, void *);
+extern int walk_rewire(struct radix_node *, void *);
+extern void if_ok(struct interface *, const char *, boolean_t);
+extern void if_sick(struct interface *, boolean_t);
+extern void if_link(struct interface *, uint32_t);
+extern struct interface *ifwithaddr(in_addr_t, boolean_t, boolean_t);
+extern struct interface *ifwithindex(ulong_t, boolean_t);
+extern struct interface *ifwithname(const char *);
+extern struct interface *findremoteif(in_addr_t);
+extern struct interface *findifaddr(in_addr_t);
+extern struct interface *iflookup(in_addr_t);
+extern struct auth *find_auth(struct interface *);
+extern void end_md5_auth(struct ws_buf *, struct auth *);
+extern void rip_mcast_on(struct interface *);
+extern void rip_mcast_off(struct interface *);
+extern void trace_dump();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _DEFS_H */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.routed/if.c b/usr/src/cmd/cmd-inet/usr.sbin/in.routed/if.c
new file mode 100644
index 0000000000..0349b2ba4c
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.routed/if.c
@@ -0,0 +1,1937 @@
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgment:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sbin/routed/if.c,v 1.8 2000/08/11 08:24:38 sheldonh Exp $
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "defs.h"
+#include "pathnames.h"
+#include <sys/sockio.h>
+#include <inet/ip.h>
+#include <kstat.h>
+#include <stropts.h>
+#include <fcntl.h>
+#include <stddef.h>
+#include <assert.h>
+
+/* linked list of all interfaces */
+struct interface *ifnet;
+
+/*
+ * Acceptable sizes (in number of interfaces) for the interface hash
+ * tables. These must all be prime. The interface hash tables all
+ * start with a size of hash_table_sizes[0], and increase as needed.
+ */
+size_t hash_table_sizes[] = { 67, 131, 257, 521, 1031, 2053, 4099, 0 };
+
+struct htbl {
+ void **htbl_ptrs;
+ uint_t (*htbl_hash)(const void *, size_t);
+ size_t htbl_link_off; /* offset of the linkage structure */
+ size_t htbl_key_off; /* offset of the key value (rehash) */
+ size_t htbl_size; /* size of the hash */
+ uint_t htbl_size_index;
+ uint_t htbl_ifcount; /* count of entries */
+ boolean_t htbl_grow; /* growth allowed */
+};
+
+/* Get first element -- for iteration */
+#define HFIRST(htbl, arg) \
+ ((htbl)->htbl_ptrs[(htbl)->htbl_hash((arg), 0) % (htbl)->htbl_size])
+
+/* Add an element to a hash */
+#define HADD(htbl, strp) \
+ hash_link((htbl), (htbl)->htbl_hash((strp), (htbl)->htbl_key_off), \
+ (strp))
+
+uint_t tot_interfaces; /* # of remote and local interfaces */
+uint_t rip_interfaces; /* # of interfaces doing RIP */
+uint_t ripout_interfaces; /* # of interfaces advertising RIP */
+uint_t fwd_interfaces; /* # of interfaces ip_forwarding=1 */
+static boolean_t foundloopback; /* valid flag for loopaddr */
+in_addr_t loopaddr; /* our address on loopback */
+static struct rt_spare loop_rts;
+
+struct timeval ifscan_timer;
+static struct timeval last_ifscan;
+#define IF_RESCAN_DELAY() \
+ (last_ifscan.tv_sec == now.tv_sec && \
+ last_ifscan.tv_usec == now.tv_usec && \
+ timercmp(&ifscan_timer, &now, > /* */))
+
+boolean_t have_ripv1_out; /* have a RIPv1 interface */
+static boolean_t have_ripv1_in;
+
+static void if_bad(struct interface *, boolean_t);
+static boolean_t addrouteforif(struct interface *);
+static int get_if_kstats(struct interface *, struct phyi_data *);
+static uint_t ahash(const void *, uint_t);
+static uint_t ihash(const void *, uint_t);
+static uint_t nhash(const void *, uint_t);
+static void htbl_grow(struct htbl *);
+
+/*
+ * Table of all interfaces, hashed by interface address. For remote
+ * interfaces, the gateway address is used.
+ */
+static struct htbl ahash_tbl = {
+ NULL, ahash, offsetof(struct interface, int_ahash),
+ offsetof(struct interface, int_addr),
+ 0, 0, 0, _B_TRUE };
+/*
+ * Table of broadcast capable interfaces, hashed by interface broadcast
+ * address.
+ */
+static struct htbl bhash_tbl = {
+ NULL, ahash, offsetof(struct interface, int_bhash),
+ offsetof(struct interface, int_brdaddr),
+ 0, 0, 0, _B_TRUE };
+/*
+ * Table of physical_interface structures (lists of interfaces by ifIndex),
+ * hashed by interface index.
+ */
+static struct htbl ihash_tbl = {
+ NULL, ihash, offsetof(struct physical_interface, phyi_link),
+ offsetof(struct physical_interface, phyi_index),
+ 0, 0, 0, _B_TRUE };
+/*
+ * Table of all interfaces, hashed by interface name.
+ */
+static struct htbl nhash_tbl = {
+ NULL, nhash, offsetof(struct interface, int_nhash),
+ offsetof(struct interface, int_name),
+ 0, 0, 0, _B_TRUE };
+
+static struct physical_interface dummy_phyi;
+struct interface dummy_ifp;
+
+/* Hash based on an IP address. */
+static uint_t
+ahash(const void *arg, size_t voffs)
+{
+ /* LINTED */
+ return ((uint_t)*(const in_addr_t *)((const char *)arg + voffs));
+}
+
+static uint_t
+ihash(const void *arg, size_t voffs)
+{
+ /* LINTED */
+ return ((uint_t)*(const uint32_t *)((const char *)arg + voffs));
+}
+
+static uint_t
+nhash(const void *arg, size_t voffs)
+{
+ const char *cp = (const char *)arg + voffs;
+ uint_t i;
+
+ for (i = 0; *cp != '\0'; cp++) {
+ i = ((i<<1) & 0x7fffffff) | ((i>>30) & 0x00000003);
+ i ^= *cp;
+ }
+ return (i);
+}
+
+/*
+ * Add an element to the head of the list.
+ */
+static void
+link_in(void **head, void *strp, size_t loffs)
+{
+ struct hlinkage *hlp;
+
+ /* LINTED: alignment known to be good. */
+ hlp = (struct hlinkage *)((char *)strp + loffs);
+ hlp->hl_prev = head;
+ if ((hlp->hl_next = *head) != NULL) {
+ /* LINTED */
+ ((struct hlinkage *)((char *)*head + loffs))->hl_prev =
+ &hlp->hl_next;
+ }
+ *head = strp;
+}
+
+/* Remove from a list */
+static void
+link_out(void *strp, size_t loffs)
+{
+ struct hlinkage *hlp;
+
+ /* LINTED: alignment known to be good. */
+ hlp = (struct hlinkage *)((char *)strp + loffs);
+ if ((*hlp->hl_prev = hlp->hl_next) != NULL) {
+ /* LINTED */
+ ((struct hlinkage *)((char *)hlp->hl_next + loffs))->hl_prev =
+ hlp->hl_prev;
+ }
+}
+
+/* Add to a hash */
+static void
+hash_link(struct htbl *htbl, uint_t hval, void *strp)
+{
+ void **hep;
+
+ if (htbl->htbl_grow && htbl->htbl_ifcount >= htbl->htbl_size * 5)
+ htbl_grow(htbl);
+
+ hep = &htbl->htbl_ptrs[hval % htbl->htbl_size];
+ link_in(hep, strp, htbl->htbl_link_off);
+ htbl->htbl_ifcount++;
+}
+
+/* Remove from a hash */
+static void
+hash_unlink(struct htbl *htbl, void *strp)
+{
+ link_out(strp, htbl->htbl_link_off);
+ htbl->htbl_ifcount--;
+}
+
+static void
+dummy_ifp_init(void)
+{
+ dummy_phyi.phyi_interface = &dummy_ifp;
+ dummy_ifp.int_phys = &dummy_phyi;
+ (void) strcpy(dummy_phyi.phyi_name, "wildcard");
+ (void) strcpy(dummy_ifp.int_name, "wildcard");
+ dummy_ifp.int_dstaddr = dummy_ifp.int_addr = INADDR_NONE;
+ dummy_ifp.int_mask = IP_HOST_MASK;
+ dummy_ifp.int_metric = HOPCNT_INFINITY;
+ dummy_ifp.int_state = (IS_BROKE|IS_PASSIVE|IS_NO_RIP|IS_NO_RDISC);
+ dummy_ifp.int_std_mask = std_mask(dummy_ifp.int_addr);
+ dummy_ifp.int_std_net = dummy_ifp.int_net & dummy_ifp.int_std_mask;
+ dummy_ifp.int_std_addr = htonl(dummy_ifp.int_std_net);
+}
+
+/* allocate the interface hash tables */
+void
+iftbl_alloc(void)
+{
+ size_t initial_size = hash_table_sizes[0];
+
+ errno = 0;
+ ahash_tbl.htbl_ptrs = calloc(initial_size, sizeof (void *));
+ bhash_tbl.htbl_ptrs = calloc(initial_size, sizeof (void *));
+ ihash_tbl.htbl_ptrs = calloc(initial_size, sizeof (void *));
+ nhash_tbl.htbl_ptrs = calloc(initial_size, sizeof (void *));
+
+ if (errno != 0)
+ BADERR(_B_FALSE, "Unable to allocate interface tables");
+
+ ahash_tbl.htbl_size = initial_size;
+ bhash_tbl.htbl_size = initial_size;
+ ihash_tbl.htbl_size = initial_size;
+ nhash_tbl.htbl_size = initial_size;
+
+ dummy_ifp_init();
+}
+
+
+static void
+htbl_grow(struct htbl *htbl)
+{
+ void *strp;
+ void **new_ptrs, **saved_old_ptrs, **old_ptrs;
+ size_t new_size, old_size;
+ static uint_t failed_count;
+
+ if ((new_size = hash_table_sizes[htbl->htbl_size_index + 1]) == 0)
+ return;
+
+ if ((new_ptrs = calloc(new_size, sizeof (void *))) == NULL) {
+ /*
+ * This is not fatal since we already have a
+ * functional, yet crowded, interface table.
+ */
+ if (++failed_count % 100 == 1)
+ msglog("%sunable to grow interface hash table: %s",
+ failed_count > 1 ? "Still " : "",
+ rip_strerror(errno));
+ return;
+ }
+
+ failed_count = 0;
+
+ saved_old_ptrs = old_ptrs = htbl->htbl_ptrs;
+ old_size = htbl->htbl_size;
+ htbl->htbl_ptrs = new_ptrs;
+ htbl->htbl_size = new_size;
+ htbl->htbl_size_index++;
+ htbl->htbl_ifcount = 0;
+
+ /*
+ * Go through the list of structures, and re-link each into
+ * this new table.
+ */
+ htbl->htbl_grow = _B_FALSE;
+ while (old_size-- > 0) {
+ strp = *old_ptrs++;
+ HADD(htbl, strp);
+ }
+
+ htbl->htbl_grow = _B_TRUE;
+ free(saved_old_ptrs);
+}
+
+/* Link a new interface into the lists and hash tables. */
+void
+if_link(struct interface *ifp, uint32_t ifindex)
+{
+ struct physical_interface *phyi;
+
+ link_in((void **)&ifnet, ifp, offsetof(struct interface, int_link));
+
+ HADD(&ahash_tbl, ifp);
+ HADD(&nhash_tbl, ifp);
+
+ if (ifp->int_if_flags & IFF_BROADCAST)
+ HADD(&bhash_tbl, ifp);
+
+ if (ifindex != 0) {
+ for (phyi = HFIRST(&ihash_tbl, &ifindex);
+ phyi != NULL; phyi = phyi->phyi_link.hl_next) {
+ if (phyi->phyi_index == ifindex)
+ break;
+ }
+ if (phyi == NULL) {
+ size_t size;
+
+ phyi = rtmalloc(sizeof (*phyi), "physical_interface");
+ (void) memset(phyi, 0, sizeof (*phyi));
+ phyi->phyi_index = ifindex;
+ /* LINTED */
+ assert(IF_NAME_LEN >= IF_NAMESIZE);
+
+ size = strcspn(ifp->int_name, ":");
+ (void) strncpy(phyi->phyi_name, ifp->int_name,
+ size);
+ phyi->phyi_name[size] = '\0';
+ HADD(&ihash_tbl, phyi);
+ }
+ link_in((void **)&phyi->phyi_interface, ifp,
+ offsetof(struct interface, int_ilist));
+ ifp->int_phys = phyi;
+ }
+}
+
+/* Find the interface with an address */
+struct interface *
+ifwithaddr(in_addr_t addr,
+ boolean_t bcast, /* notice IFF_BROADCAST address */
+ boolean_t remote) /* include IS_REMOTE interfaces */
+{
+ struct interface *ifp, *possible = NULL;
+ uint32_t remote_state;
+
+ remote_state = (!remote ? IS_REMOTE : 0);
+
+ for (ifp = HFIRST(&ahash_tbl, &addr); ifp != NULL;
+ ifp = ifp->int_ahash.hl_next) {
+ if (ifp->int_addr != addr)
+ continue;
+ if (ifp->int_state & remote_state)
+ continue;
+ if (!(ifp->int_state & (IS_BROKE | IS_PASSIVE)))
+ return (ifp);
+ possible = ifp;
+ }
+
+ if (possible != NULL || !bcast)
+ return (possible);
+
+ for (ifp = HFIRST(&bhash_tbl, &addr); ifp != NULL;
+ ifp = ifp->int_bhash.hl_next) {
+ if (ifp->int_brdaddr != addr)
+ continue;
+ if (ifp->int_state & remote_state)
+ continue;
+ if (!(ifp->int_state & (IS_BROKE | IS_PASSIVE)))
+ return (ifp);
+ possible = ifp;
+ }
+
+ return (possible);
+}
+
+
+/* find the interface with the specified name ("hme0" for example) */
+struct interface *
+ifwithname(const char *name)
+{
+ struct interface *ifp;
+
+ for (;;) {
+ for (ifp = HFIRST(&nhash_tbl, name); ifp != NULL;
+ ifp = ifp->int_nhash.hl_next) {
+ if (strcmp(ifp->int_name, name) == 0)
+ return (ifp);
+ }
+
+ /*
+ * If there is no known interface, maybe there is a
+ * new interface. So just once look for new interfaces.
+ */
+ if (IF_RESCAN_DELAY())
+ return (NULL);
+ ifscan();
+ }
+}
+
+struct interface *
+findremoteif(in_addr_t addr)
+{
+ struct interface *ifp;
+
+ for (ifp = HFIRST(&ahash_tbl, &addr); ifp != NULL;
+ ifp = ifp->int_ahash.hl_next) {
+ if ((ifp->int_state & IS_REMOTE) && ifp->int_addr == addr)
+ return (ifp);
+ }
+
+ return (NULL);
+}
+
+struct interface *
+findifaddr(in_addr_t addr)
+{
+ struct interface *ifp;
+
+ for (ifp = HFIRST(&ahash_tbl, &addr); ifp != NULL;
+ ifp = ifp->int_ahash.hl_next) {
+ if (ifp->int_addr == addr)
+ return (ifp);
+ }
+
+ return (NULL);
+}
+
+/*
+ * Return the first interface with the given index.
+ */
+struct interface *
+ifwithindex(ulong_t index,
+ boolean_t rescan_ok)
+{
+ struct physical_interface *phyi;
+
+ for (;;) {
+ for (phyi = HFIRST(&ihash_tbl, &index); phyi != NULL;
+ phyi = phyi->phyi_link.hl_next) {
+ if (phyi->phyi_index == index)
+ return (phyi->phyi_interface);
+ }
+
+ /*
+ * If there is no known interface, maybe there is a
+ * new interface. So just once look for new interfaces.
+ */
+ if (!rescan_ok || IF_RESCAN_DELAY())
+ return (NULL);
+ rescan_ok = _B_FALSE;
+ ifscan();
+ }
+}
+
+
+/*
+ * Find an interface which should be receiving packets sent from the
+ * given address. Used as a last ditch effort for figuring out which
+ * interface a packet came in on. Also used for finding out which
+ * interface points towards the gateway of static routes learned from
+ * the kernel.
+ */
+struct interface *
+iflookup(in_addr_t addr)
+{
+ struct interface *ifp, *maybe;
+
+ maybe = NULL;
+ for (;;) {
+ for (ifp = ifnet; ifp != NULL; ifp = ifp->int_next) {
+ /*
+ * Don't return a duplicate interface since
+ * it is unusable for output.
+ */
+ if (ifp->int_state & IS_DUP)
+ continue;
+
+ if (ifp->int_if_flags & IFF_POINTOPOINT) {
+ /* finished with a match */
+ if (ifp->int_dstaddr == addr)
+ return (ifp);
+ } else {
+ /* finished with an exact match */
+ if (ifp->int_addr == addr) {
+ if (IS_PASSIVE_IFP(ifp))
+ trace_misc("iflookup "
+ "returning passive intf %s",
+ ifp->int_name);
+ return (ifp);
+ }
+
+ /* Look for the longest approximate match. */
+ if (on_net(addr, ifp->int_net, ifp->int_mask) &&
+ (maybe == NULL ||
+ ifp->int_mask > maybe->int_mask))
+ maybe = ifp;
+ }
+ }
+
+ /*
+ * If there is no known interface, maybe there is a
+ * new interface. So just once look for new interfaces.
+ */
+ if (maybe == NULL && !IF_RESCAN_DELAY())
+ ifscan();
+ else
+ break;
+ }
+
+ if (maybe != NULL && IS_PASSIVE_IFP(maybe)) {
+ trace_misc("iflookup returning passive intf %s",
+ maybe->int_name);
+ }
+ return (maybe);
+}
+
+/*
+ * Find the netmask that would be inferred by RIPv1 listeners
+ * on the given interface for a given network.
+ * If no interface is specified, look for the best fitting interface.
+ */
+in_addr_t
+ripv1_mask_net(in_addr_t addr, /* in network byte order */
+ const struct interface *ifp) /* as seen on this interface */
+{
+ const struct r1net *r1p;
+ in_addr_t mask = 0;
+
+ if (addr == 0) /* default always has 0 mask */
+ return (mask);
+
+ if (ifp != NULL && ifp->int_ripv1_mask != HOST_MASK) {
+ /*
+ * If the target network is that of the associated interface
+ * on which it arrived, then use the netmask of the interface.
+ */
+ if (on_net(addr, ifp->int_net, ifp->int_std_mask))
+ mask = ifp->int_ripv1_mask;
+
+ } else {
+ /*
+ * Examine all interfaces, and if it the target seems
+ * to have the same network number of an interface, use the
+ * netmask of that interface. If there is more than one
+ * such interface, prefer the interface with the longest
+ * match.
+ */
+ for (ifp = ifnet; ifp != NULL; ifp = ifp->int_next) {
+ if (on_net(addr, ifp->int_std_net, ifp->int_std_mask) &&
+ ifp->int_ripv1_mask > mask &&
+ ifp->int_ripv1_mask != HOST_MASK)
+ mask = ifp->int_ripv1_mask;
+ }
+
+ }
+
+ if (mask == 0) {
+ /*
+ * Check to see if the user has supplied an applicable
+ * netmask as a ripv1_mask option in /etc/gateways.
+ */
+ for (r1p = r1nets; r1p != NULL; r1p = r1p->r1net_next) {
+ /*
+ * If the address is is on a matching network
+ * and we haven't already found a longer match,
+ * use the matching netmask.
+ */
+ if (on_net(addr, r1p->r1net_net, r1p->r1net_match) &&
+ r1p->r1net_mask > mask)
+ mask = r1p->r1net_mask;
+ }
+
+ /* Otherwise, make the classic A/B/C guess. */
+ if (mask == 0)
+ mask = std_mask(addr);
+ }
+
+ return (mask);
+}
+
+
+in_addr_t
+ripv1_mask_host(in_addr_t addr, /* in network byte order */
+ const struct interface *ifp) /* as seen on this interface */
+{
+ in_addr_t mask = ripv1_mask_net(addr, ifp);
+
+
+ /*
+ * If the computed netmask does not mask all of the set bits
+ * in the address, then assume it is a host address
+ */
+ if ((ntohl(addr) & ~mask) != 0)
+ mask = HOST_MASK;
+ return (mask);
+}
+
+
+/* See if a IP address looks reasonable as a destination */
+boolean_t /* _B_FALSE=bad _B_TRUE=good */
+check_dst(in_addr_t addr)
+{
+ addr = ntohl(addr);
+
+ if (IN_CLASSA(addr)) {
+ if (addr == 0)
+ return (_B_TRUE); /* default */
+
+ addr >>= IN_CLASSA_NSHIFT;
+ return (addr != 0 && addr != IN_LOOPBACKNET);
+ }
+
+ return (IN_CLASSB(addr) || IN_CLASSC(addr));
+}
+
+/*
+ * Find an existing interface which has the given parameters, but don't
+ * return the interface with name "name" if "name" is specified.
+ */
+struct interface *
+check_dup(const char *name, /* Don't return this interface */
+ in_addr_t addr, /* IP address, so network byte order */
+ in_addr_t dstaddr, /* ditto */
+ in_addr_t mask, /* mask, so host byte order */
+ int if_flags, /* set IFF_POINTOPOINT to ignore local int_addr */
+ boolean_t allowdups) /* set true to include duplicates */
+{
+ struct interface *best_ifp = NULL;
+ struct interface *ifp;
+ in_addr_t dstaddr_h = ntohl(dstaddr);
+ int best_pref = 0;
+ int pref;
+
+ for (ifp = ifnet; ifp != NULL; ifp = ifp->int_next) {
+ /* This interface, not a duplicate. */
+ if (name != NULL && strcmp(name, ifp->int_name) == 0)
+ continue;
+
+ /*
+ * Find an interface which isn't already a duplicate to
+ * avoid cyclical duplication. (i.e. qfe0:1 is a duplicate
+ * of qfe0, and qfe0 is a duplicate of qfe0:1. That would
+ * be bad)
+ */
+ if (!allowdups && (ifp->int_state & IS_DUP))
+ continue;
+
+ if (ifp->int_mask != mask)
+ continue;
+
+ if (!IS_IFF_UP(ifp->int_if_flags))
+ continue;
+
+ /*
+ * The local address can only be shared with a point-to-point
+ * link.
+ */
+ if ((ifp->int_addr == addr &&
+ ((if_flags|ifp->int_if_flags) & IFF_POINTOPOINT) == 0) ||
+ on_net(ifp->int_dstaddr, dstaddr_h, mask)) {
+ pref = 0;
+ if (!(ifp->int_state & IS_ALIAS))
+ pref++;
+ if (!IS_RIP_OUT_OFF(ifp->int_state))
+ pref += 2;
+ if (IS_IFF_ROUTING(ifp->int_if_flags))
+ pref += 4;
+ if (pref > best_pref) {
+ best_pref = pref;
+ best_ifp = ifp;
+ }
+ }
+ }
+ return (best_ifp);
+}
+
+
+/*
+ * See that a remote gateway is reachable.
+ * Note that the answer can change as real interfaces come and go.
+ */
+boolean_t /* _B_FALSE=bad _B_TRUE=good */
+check_remote(struct interface *ifp)
+{
+ struct rt_entry *rt;
+
+ /* do not worry about other kinds */
+ if (!(ifp->int_state & IS_REMOTE))
+ return (_B_TRUE);
+
+ rt = rtfind(ifp->int_addr);
+ if (rt != NULL &&
+ rt->rt_ifp != NULL &&
+ on_net(ifp->int_addr,
+ rt->rt_ifp->int_net, rt->rt_ifp->int_mask))
+ return (_B_TRUE);
+
+ /*
+ * the gateway cannot be reached directly from one of our
+ * interfaces
+ */
+ if (!(ifp->int_state & IS_BROKE)) {
+ msglog("unreachable gateway %s in "PATH_GATEWAYS,
+ naddr_ntoa(ifp->int_addr));
+ if_bad(ifp, _B_FALSE);
+ }
+ return (_B_FALSE);
+}
+
+/* Delete an interface. */
+static void
+ifdel(struct interface *ifp)
+{
+ struct rewire_data wire;
+ boolean_t resurrected;
+ struct physical_interface *phyi;
+
+ trace_if("Del", ifp);
+
+ ifp->int_state |= IS_BROKE;
+
+ /* unlink the interface */
+ link_out(ifp, offsetof(struct interface, int_link));
+ hash_unlink(&ahash_tbl, ifp);
+ hash_unlink(&nhash_tbl, ifp);
+ if (ifp->int_if_flags & IFF_BROADCAST)
+ hash_unlink(&bhash_tbl, ifp);
+
+ /* Remove from list of interfaces with this ifIndex */
+ if ((phyi = ifp->int_phys) != NULL) {
+ link_out(ifp, offsetof(struct interface, int_ilist));
+ if (phyi->phyi_interface == NULL) {
+ hash_unlink(&ihash_tbl, phyi);
+ free(phyi);
+ }
+ }
+
+ /*
+ * If this is a lead interface, then check first for
+ * duplicates of this interface with an eye towards promoting
+ * one of them.
+ */
+ resurrected = _B_FALSE;
+ if (!(ifp->int_state & IS_DUP) &&
+ (wire.if_new = check_dup(ifp->int_name, ifp->int_addr,
+ ifp->int_dstaddr, ifp->int_mask, ifp->int_if_flags,
+ _B_TRUE)) != NULL &&
+ !IS_IFF_QUIET(wire.if_new->int_if_flags)) {
+
+ trace_act("promoting duplicate %s in place of %s",
+ wire.if_new->int_name, ifp->int_name);
+
+ /* Rewire routes with the replacement interface */
+ wire.if_old = ifp;
+ wire.metric_delta = wire.if_new->int_metric - ifp->int_metric;
+ (void) rn_walktree(rhead, walk_rewire, &wire);
+ kern_rewire_ifp(wire.if_old, wire.if_new);
+ if_rewire_rdisc(wire.if_old, wire.if_new);
+
+ /* Mark the replacement as being no longer a duplicate */
+ wire.if_new->int_state &= ~IS_DUP;
+ tot_interfaces++;
+ if (!IS_RIP_OFF(wire.if_new->int_state))
+ rip_interfaces++;
+ if (!IS_RIP_OUT_OFF(wire.if_new->int_state))
+ ripout_interfaces++;
+ if (IS_IFF_ROUTING(wire.if_new->int_if_flags))
+ fwd_interfaces++;
+
+ set_rdisc_mg(wire.if_new, 1);
+ rip_mcast_on(wire.if_new);
+
+ /* We came out ok; no need to clobber routes over this. */
+ resurrected = _B_TRUE;
+ }
+
+ rip_mcast_off(ifp);
+ if (rip_sock_interface == ifp)
+ rip_sock_interface = NULL;
+
+ set_rdisc_mg(ifp, 0);
+
+ /*
+ * Note that duplicates are not counted in the total number of
+ * interfaces.
+ */
+ if (!(ifp->int_state & IS_DUP) && !IS_IFF_QUIET(ifp->int_if_flags)) {
+ tot_interfaces--;
+ if (!IS_RIP_OFF(ifp->int_state))
+ rip_interfaces--;
+ if (!IS_RIP_OUT_OFF(ifp->int_state))
+ ripout_interfaces--;
+ if (IS_IFF_ROUTING(ifp->int_if_flags))
+ fwd_interfaces--;
+ }
+
+ if (!resurrected) {
+ /*
+ * Zap all routes associated with this interface.
+ * Assume routes just using gateways beyond this interface
+ * will timeout naturally, and have probably already died.
+ */
+ (void) rn_walktree(rhead, walk_bad, ifp);
+ kern_flush_ifp(ifp);
+
+ if_bad_rdisc(ifp);
+ }
+
+ free(ifp);
+}
+
+
+/* Mark an interface ill. */
+void
+if_sick(struct interface *ifp, boolean_t recurse)
+{
+ struct interface *ifp1;
+
+ if (0 == (ifp->int_state & (IS_SICK | IS_BROKE))) {
+ ifp->int_state |= IS_SICK;
+ ifp->int_act_time = NEVER;
+ trace_if("Chg", ifp);
+
+ LIM_SEC(ifscan_timer, now.tv_sec+CHECK_BAD_INTERVAL);
+ if (recurse && ifp->int_phys != NULL) {
+ /* If an interface is sick, so are its aliases. */
+ for (ifp1 = ifp->int_phys->phyi_interface;
+ ifp1 != NULL; ifp1 = ifp1->int_ilist.hl_next) {
+ if (ifp1 != ifp)
+ if_sick(ifp1, _B_FALSE);
+ }
+ }
+ }
+}
+
+
+/* Mark an interface dead. */
+static void
+if_bad(struct interface *ifp, boolean_t recurse)
+{
+ struct interface *ifp1;
+ struct rewire_data wire;
+
+ if (ifp->int_state & IS_BROKE)
+ return;
+
+ LIM_SEC(ifscan_timer, now.tv_sec+CHECK_BAD_INTERVAL);
+
+ ifp->int_state |= (IS_BROKE | IS_SICK);
+ ifp->int_act_time = NEVER;
+ ifp->int_query_time = NEVER;
+ /* Note: don't reset the stats timestamp here */
+
+ trace_if("Chg", ifp);
+
+ if (recurse && ifp->int_phys != NULL) {
+ /* If an interface is bad, so are its aliases. */
+ for (ifp1 = ifp->int_phys->phyi_interface;
+ ifp1 != NULL; ifp1 = ifp1->int_ilist.hl_next) {
+ if (ifp1 != ifp)
+ if_bad(ifp1, _B_FALSE);
+ }
+ }
+
+ /* If we can find a replacement, then pick it up. */
+ if (!(ifp->int_state & IS_DUP) &&
+ (wire.if_new = check_dup(ifp->int_name, ifp->int_addr,
+ ifp->int_dstaddr, ifp->int_mask, ifp->int_if_flags,
+ _B_TRUE)) != NULL &&
+ !IS_IFF_QUIET(wire.if_new->int_if_flags)) {
+ trace_act("promoting duplicate %s in place of %s",
+ wire.if_new->int_name, ifp->int_name);
+ wire.if_old = ifp;
+ wire.metric_delta = wire.if_new->int_metric - ifp->int_metric;
+ (void) rn_walktree(rhead, walk_rewire, &wire);
+ if_rewire_rdisc(wire.if_old, wire.if_new);
+
+ /* The broken guy becomes the duplicate */
+ wire.if_new->int_state &= ~IS_DUP;
+ set_rdisc_mg(ifp, 0);
+ rip_mcast_off(ifp);
+ ifp->int_state |= IS_DUP;
+
+ /* join the mcast groups for the replacement */
+ set_rdisc_mg(wire.if_new, 1);
+ rip_mcast_on(wire.if_new);
+
+ if (rip_sock_interface == ifp)
+ rip_sock_interface = NULL;
+ } else {
+ (void) rn_walktree(rhead, walk_bad, ifp);
+ if_bad_rdisc(ifp);
+ }
+}
+
+
+/* Mark an interface alive */
+void
+if_ok(struct interface *ifp, const char *type, boolean_t recurse)
+{
+ struct interface *ifp1;
+ boolean_t wasbroken = _B_FALSE;
+
+ if (ifp->int_state & IS_BROKE) {
+ writelog(LOG_WARNING, "%sinterface %s to %s restored",
+ type, ifp->int_name, naddr_ntoa(ifp->int_dstaddr));
+ ifp->int_state &= ~(IS_BROKE | IS_SICK);
+ wasbroken = _B_TRUE;
+ } else if (ifp->int_state & IS_SICK) {
+ trace_act("%sinterface %s to %s working better",
+ type, ifp->int_name, naddr_ntoa(ifp->int_dstaddr));
+ ifp->int_state &= ~IS_SICK;
+ }
+
+ if (recurse && ifp->int_phys != NULL && IS_IFF_UP(ifp->int_if_flags)) {
+ ifp->int_phys->phyi_data.ts = 0;
+
+ /* Also mark all aliases of this interface as ok */
+ for (ifp1 = ifp->int_phys->phyi_interface;
+ ifp1 != NULL; ifp1 = ifp1->int_ilist.hl_next) {
+ if (ifp1 != ifp)
+ if_ok(ifp1, type, _B_FALSE);
+ }
+ }
+
+ if (wasbroken) {
+ if (!(ifp->int_state & IS_DUP))
+ if_ok_rdisc(ifp);
+
+ if (ifp->int_state & IS_REMOTE)
+ (void) addrouteforif(ifp);
+ }
+}
+
+boolean_t
+remote_address_ok(struct interface *ifp, in_addr_t addr)
+{
+ if (ifp->int_if_flags & IFF_POINTOPOINT) {
+ if (addr == ifp->int_dstaddr)
+ return (_B_TRUE);
+ } else if (on_net(addr, ifp->int_net, ifp->int_mask)) {
+ return (_B_TRUE);
+ }
+ return (_B_FALSE);
+}
+
+/*
+ * Find the network interfaces which have configured themselves.
+ * This must be done regularly, if only for extra addresses
+ * that come and go on interfaces.
+ */
+void
+ifscan(void)
+{
+ uint_t complaints = 0;
+ static uint_t prev_complaints = 0;
+#define COMP_BADADDR 0x001
+#define COMP_NODST 0x002
+#define COMP_NOBADDR 0x004
+#define COMP_NOMASK 0x008
+#define COMP_BAD_METRIC 0x010
+#define COMP_NETMASK 0x020
+#define COMP_NO_INDEX 0x040
+#define COMP_BAD_FLAGS 0x080
+#define COMP_NO_KSTATS 0x100
+#define COMP_IPFORWARD 0x200
+
+ struct interface ifs, *ifp, *ifp1;
+ struct rt_entry *rt;
+ size_t needed;
+ static size_t lastneeded = 0;
+ char *buf;
+ static char *lastbuf = NULL;
+ int32_t in, ierr, out, oerr;
+ struct intnet *intnetp;
+ int sock;
+ struct lifnum lifn;
+ struct lifconf lifc;
+ struct lifreq *lifrp, *lifrp_lim;
+ struct sockaddr_in *sinp;
+ in_addr_t haddr;
+ static in_addr_t myaddr = 0;
+ uint32_t ifindex;
+ struct phyi_data newstats;
+ struct physical_interface *phyi;
+
+ last_ifscan = now;
+ ifscan_timer.tv_sec = now.tv_sec +
+ (supplier || tot_interfaces != 1 ?
+ CHECK_ACT_INTERVAL : CHECK_QUIET_INTERVAL);
+
+ /* mark all interfaces so we can get rid of those that disappear */
+ for (ifp = ifnet; ifp != NULL; ifp = ifp->int_next)
+ ifp->int_state &= ~IS_CHECKED;
+
+ /* Fetch the size of the current interface list */
+ if ((sock = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
+ BADERR(_B_TRUE, "ifscan: socket(SOCK_DGRAM)");
+ lifn.lifn_family = AF_INET; /* Only count IPv4 interfaces */
+ /*
+ * Include IFF_NOXMIT interfaces. Such interfaces are exluded
+ * from protocol operations, but their inclusion in the
+ * internal table enables us to know when packets arrive on
+ * such interfaces.
+ */
+ lifn.lifn_flags = LIFC_NOXMIT;
+calculate_lifc_len:
+ if (ioctl(sock, SIOCGLIFNUM, &lifn) == -1) {
+ BADERR(_B_TRUE, "ifscan: ioctl(SIOCGLIFNUM)");
+ }
+
+ /*
+ * When calculating the buffer size needed, add a small number
+ * of interfaces to those we counted. We do this to capture
+ * the interface status of potential interfaces which may have
+ * been plumbed between the SIOCGLIFNUM and the SIOCGLIFCONF.
+ * Try to reuse the buffer we already have to avoid heap
+ * thrash.
+ */
+ needed = (lifn.lifn_count + 4) * sizeof (struct lifreq);
+ if (needed > lastneeded || needed < lastneeded/2) {
+ if (lastbuf != NULL)
+ free(lastbuf);
+ if ((buf = malloc(needed)) == NULL) {
+ lastbuf = NULL;
+ msglog("ifscan: malloc: %s", rip_strerror(errno));
+ return;
+ }
+ } else {
+ buf = lastbuf;
+ }
+ lastbuf = buf;
+ lastneeded = needed;
+
+ /* Get the list */
+ lifc.lifc_family = AF_INET; /* We only need IPv4 interfaces */
+ lifc.lifc_flags = LIFC_NOXMIT;
+ lifc.lifc_len = needed;
+ lifc.lifc_buf = buf;
+ if (ioctl(sock, SIOCGLIFCONF, &lifc) == -1) {
+ /*
+ * IP returns EINVAL if the lifc_len we passed in is
+ * too small. If that's the case, we need to go back
+ * and recalculate it.
+ */
+ if (errno == EINVAL)
+ goto calculate_lifc_len;
+ BADERR(_B_TRUE, "ifscan: ioctl(SIOCGLIFCONF)");
+ }
+
+ /*
+ * If the returned lifc_len is within one lifreq of the
+ * requested ammount, we may have used a buffer which
+ * was too small to hold all of the interfaces. In that
+ * case go back and recalculate needed.
+ */
+ if (lifc.lifc_len >= needed - sizeof (struct lifreq))
+ goto calculate_lifc_len;
+
+ lifrp = lifc.lifc_req;
+ lifrp_lim = lifrp + lifc.lifc_len / sizeof (*lifrp);
+ for (; lifrp < lifrp_lim; lifrp++) {
+
+ (void) memset(&ifs, 0, sizeof (ifs));
+
+ (void) strlcpy(ifs.int_name, lifrp->lifr_name,
+ sizeof (ifs.int_name));
+
+ /* SIOCGLIFCONF fills in the lifr_addr of each lifreq */
+ ifs.int_addr = ((struct sockaddr_in *)&lifrp->lifr_addr)->
+ sin_addr.s_addr;
+
+ if (ioctl(sock, SIOCGLIFFLAGS, lifrp) == -1) {
+ if (!(prev_complaints & COMP_BAD_FLAGS))
+ writelog(LOG_NOTICE,
+ "unable to get interface flags for %s: %s",
+ ifs.int_name, rip_strerror(errno));
+ complaints |= COMP_BAD_FLAGS;
+ ifs.int_if_flags = 0;
+ } else {
+ ifs.int_if_flags = lifrp->lifr_flags;
+ }
+
+ if (IN_EXPERIMENTAL(ntohl(ifs.int_addr)) ||
+ (ntohl(ifs.int_addr) & IN_CLASSA_NET) == 0) {
+ if (IS_IFF_UP(ifs.int_if_flags)) {
+ if (!(prev_complaints & COMP_BADADDR))
+ writelog(LOG_NOTICE,
+ "%s has a bad address %s",
+ ifs.int_name,
+ naddr_ntoa(ifs.int_addr));
+ complaints |= COMP_BADADDR;
+ }
+ continue;
+ }
+
+ /* Get the interface index. */
+ if (ioctl(sock, SIOCGLIFINDEX, lifrp) == -1) {
+ ifindex = 0;
+ ifs.int_if_flags &= ~IFF_UP;
+ if (!(prev_complaints & COMP_NO_INDEX))
+ writelog(LOG_NOTICE, "%s has no ifIndex: %s",
+ ifs.int_name, rip_strerror(errno));
+ complaints |= COMP_NO_INDEX;
+ } else {
+ ifindex = lifrp->lifr_index;
+ }
+
+ /*
+ * Get the destination address for point-to-point
+ * interfaces.
+ */
+ if (ifs.int_if_flags & IFF_POINTOPOINT) {
+ sinp = (struct sockaddr_in *)&lifrp->lifr_dstaddr;
+ if (ioctl(sock, SIOCGLIFDSTADDR, lifrp) == -1) {
+ if (IS_IFF_UP(ifs.int_if_flags)) {
+ if (!(prev_complaints & COMP_NODST))
+ writelog(LOG_NOTICE,
+ "%s has no destination "
+ "address : %s",
+ ifs.int_name,
+ rip_strerror(errno));
+ complaints |= COMP_NODST;
+ }
+ continue;
+ }
+ ifs.int_net = ntohl(sinp->sin_addr.s_addr);
+ if (IN_EXPERIMENTAL(ifs.int_net) ||
+ (ifs.int_net != 0 &&
+ (ifs.int_net & IN_CLASSA_NET) == 0)) {
+ if (IS_IFF_UP(ifs.int_if_flags)) {
+ if (!(prev_complaints & COMP_NODST))
+ writelog(LOG_NOTICE,
+ "%s has a bad "
+ "destination address %s",
+ ifs.int_name,
+ naddr_ntoa(ifs.int_net));
+ complaints |= COMP_NODST;
+ }
+ continue;
+ }
+ ifs.int_dstaddr = sinp->sin_addr.s_addr;
+ }
+
+ /* Get the subnet mask */
+ sinp = (struct sockaddr_in *)&lifrp->lifr_addr;
+ if (ioctl(sock, SIOCGLIFNETMASK, lifrp) == -1) {
+ if (IS_IFF_UP(ifs.int_if_flags)) {
+ if (!(prev_complaints & COMP_NOMASK))
+ writelog(LOG_NOTICE,
+ "%s has no netmask: %s",
+ ifs.int_name, rip_strerror(errno));
+ complaints |= COMP_NOMASK;
+ }
+ continue;
+ }
+ if (sinp->sin_addr.s_addr == INADDR_ANY) {
+ if (!(ifs.int_if_flags &
+ (IFF_POINTOPOINT|IFF_LOOPBACK))) {
+ if (IS_IFF_UP(ifs.int_if_flags)) {
+ if (!(prev_complaints & COMP_NOMASK))
+ writelog(LOG_NOTICE,
+ "%s has all-zero netmask",
+ ifs.int_name);
+ complaints |= COMP_NOMASK;
+ }
+ continue;
+ }
+ ifs.int_mask = IP_HOST_MASK;
+ } else {
+ ifs.int_mask = ntohl(sinp->sin_addr.s_addr);
+ }
+
+ /*
+ * Get the broadcast address on broadcast capable
+ * interfaces.
+ */
+ if (ifs.int_if_flags & IFF_BROADCAST) {
+ if (ioctl(sock, SIOCGLIFBRDADDR, lifrp) == -1) {
+ if (IS_IFF_UP(ifs.int_if_flags)) {
+ if (!(prev_complaints & COMP_NOBADDR))
+ writelog(LOG_NOTICE,
+ "%s has no broadcast "
+ "address: %s",
+ ifs.int_name,
+ rip_strerror(errno));
+ complaints |= COMP_NOBADDR;
+ }
+ continue;
+ }
+ haddr = ntohl(sinp->sin_addr.s_addr);
+ if (IN_EXPERIMENTAL(haddr) ||
+ (haddr & IN_CLASSA_NET) == 0) {
+ if (IS_IFF_UP(ifs.int_if_flags)) {
+ if (!(prev_complaints & COMP_NOBADDR))
+ writelog(LOG_NOTICE,
+ "%s has a bad broadcast "
+ "address %s",
+ ifs.int_name,
+ naddr_ntoa(haddr));
+ complaints |= COMP_NOBADDR;
+ }
+ continue;
+ }
+ }
+ ifs.int_brdaddr = sinp->sin_addr.s_addr;
+
+ /* Get interface metric, if possible. */
+ if (ioctl(sock, SIOCGLIFMETRIC, lifrp) == -1) {
+ if (IS_IFF_UP(ifs.int_if_flags)) {
+ if (!(prev_complaints & COMP_BAD_METRIC))
+ writelog(LOG_NOTICE,
+ "%s has no metric: %s",
+ ifs.int_name, rip_strerror(errno));
+ complaints |= COMP_BAD_METRIC;
+ }
+ } else {
+ ifs.int_metric = lifrp->lifr_metric;
+ if (ifs.int_metric > HOPCNT_INFINITY) {
+ if (IS_IFF_UP(ifs.int_if_flags)) {
+ if (!(prev_complaints &
+ COMP_BAD_METRIC))
+ writelog(LOG_NOTICE,
+ "%s has a metric of %d, "
+ "defaulting to %d",
+ ifs.int_name,
+ ifs.int_metric,
+ HOPCNT_INFINITY);
+ complaints |= COMP_BAD_METRIC;
+ }
+ ifs.int_metric = HOPCNT_INFINITY;
+ }
+ }
+
+ ifs.int_state |= IS_CHECKED;
+ ifs.int_query_time = NEVER;
+
+ /*
+ * If this is an alias, then mark it appropriately.
+ * Do not output RIP or Router-Discovery packets via
+ * aliases.
+ */
+ if (strchr(ifs.int_name, ':') != NULL)
+ ifs.int_state |= IS_ALIAS;
+
+ if (ifs.int_if_flags & IFF_LOOPBACK) {
+ ifs.int_state |= IS_PASSIVE | IS_NO_RIP | IS_NO_RDISC;
+ ifs.int_dstaddr = ifs.int_addr;
+ ifs.int_mask = HOST_MASK;
+ ifs.int_ripv1_mask = HOST_MASK;
+ ifs.int_std_mask = std_mask(ifs.int_dstaddr);
+ ifs.int_net = ntohl(ifs.int_dstaddr);
+ if (!foundloopback) {
+ foundloopback = _B_TRUE;
+ loopaddr = ifs.int_addr;
+ loop_rts.rts_gate = loopaddr;
+ loop_rts.rts_router = loopaddr;
+ }
+
+ } else if (ifs.int_if_flags & IFF_POINTOPOINT) {
+ ifs.int_ripv1_mask = ifs.int_mask;
+ ifs.int_mask = HOST_MASK;
+ ifs.int_std_mask = std_mask(ifs.int_dstaddr);
+
+ } else {
+ ifs.int_dstaddr = ifs.int_addr;
+ ifs.int_ripv1_mask = ifs.int_mask;
+ ifs.int_std_mask = std_mask(ifs.int_addr);
+ ifs.int_net = ntohl(ifs.int_addr) & ifs.int_mask;
+ if (ifs.int_mask != ifs.int_std_mask)
+ ifs.int_state |= IS_SUBNET;
+ }
+ ifs.int_std_net = ifs.int_net & ifs.int_std_mask;
+ ifs.int_std_addr = htonl(ifs.int_std_net);
+
+ /*
+ * If this interface duplicates another, mark it
+ * appropriately so that we don't generate duplicate
+ * packets.
+ */
+ ifp = check_dup(ifs.int_name, ifs.int_addr, ifs.int_dstaddr,
+ ifs.int_mask, ifs.int_if_flags, _B_FALSE);
+ if (ifp != NULL) {
+ trace_misc("%s (%s%s%s) is a duplicate of %s (%s%s%s)",
+ ifs.int_name,
+ addrname(ifs.int_addr, ifs.int_mask, 1),
+ ((ifs.int_if_flags & IFF_POINTOPOINT) ?
+ "-->" : ""),
+ ((ifs.int_if_flags & IFF_POINTOPOINT) ?
+ naddr_ntoa(ifs.int_dstaddr) : ""),
+ ifp->int_name,
+ addrname(ifp->int_addr, ifp->int_mask, 1),
+ ((ifp->int_if_flags & IFF_POINTOPOINT) ?
+ "-->" : ""),
+ ((ifp->int_if_flags & IFF_POINTOPOINT) ?
+ naddr_ntoa(ifp->int_dstaddr) : ""));
+ ifs.int_state |= IS_DUP;
+ } else {
+ ifs.int_state &= ~IS_DUP;
+ }
+
+ /*
+ * See if this is a familiar interface.
+ * If so, stop worrying about it if it is the same.
+ * Start it over if it now is to somewhere else, as happens
+ * frequently with PPP and SLIP, or if its forwarding
+ * status has changed.
+ */
+ ifp = ifwithname(ifs.int_name);
+ if (ifp != NULL) {
+ ifp->int_state |= IS_CHECKED;
+ ifp->int_state = (ifp->int_state & ~IS_DUP) |
+ (ifs.int_state & IS_DUP);
+
+ if ((ifp->int_phys == NULL && ifindex != 0) ||
+ (ifp->int_phys != NULL &&
+ ifp->int_phys->phyi_index != ifindex) ||
+ 0 != ((ifp->int_if_flags ^ ifs.int_if_flags)
+ & (IFF_BROADCAST | IFF_LOOPBACK |
+ IFF_POINTOPOINT | IFF_MULTICAST |
+ IFF_ROUTER | IFF_NORTEXCH | IFF_NOXMIT)) ||
+ ifp->int_addr != ifs.int_addr ||
+ ifp->int_brdaddr != ifs.int_brdaddr ||
+ ifp->int_dstaddr != ifs.int_dstaddr ||
+ ifp->int_mask != ifs.int_mask ||
+ ifp->int_metric != ifs.int_metric) {
+ /*
+ * Forget old information about
+ * a changed interface.
+ */
+ trace_act("interface %s has changed",
+ ifp->int_name);
+ ifdel(ifp);
+ ifp = NULL;
+ }
+ }
+
+ if (ifp != NULL) {
+ /* note interfaces that have been turned off */
+ if (!IS_IFF_UP(ifs.int_if_flags)) {
+ if (IS_IFF_UP(ifp->int_if_flags)) {
+ writelog(LOG_WARNING,
+ "interface %s to %s turned off",
+ ifp->int_name,
+ naddr_ntoa(ifp->int_dstaddr));
+ if_bad(ifp, _B_FALSE);
+ ifp->int_if_flags &= ~IFF_UP;
+ } else if (ifp->int_phys != NULL &&
+ now.tv_sec > (ifp->int_phys->phyi_data.ts +
+ CHECK_BAD_INTERVAL)) {
+ trace_act("interface %s has been off"
+ " %ld seconds; forget it",
+ ifp->int_name,
+ now.tv_sec -
+ ifp->int_phys->phyi_data.ts);
+ ifdel(ifp);
+ }
+ continue;
+ }
+ /* or that were off and are now ok */
+ if (!IS_IFF_UP(ifp->int_if_flags)) {
+ ifp->int_if_flags |= IFF_UP;
+ if_ok(ifp, "", _B_FALSE);
+ }
+
+ /*
+ * If it has been long enough,
+ * see if the interface is broken.
+ */
+ if ((phyi = ifp->int_phys) == NULL ||
+ now.tv_sec < phyi->phyi_data.ts +
+ CHECK_BAD_INTERVAL)
+ continue;
+
+ (void) memset(&newstats, 0, sizeof (newstats));
+ if (get_if_kstats(ifp, &newstats) == -1) {
+ if (!(prev_complaints & COMP_NO_KSTATS))
+ writelog(LOG_WARNING,
+ "unable to obtain kstats for %s",
+ phyi->phyi_name);
+ complaints |= COMP_NO_KSTATS;
+ }
+
+ /*
+ * If the interface just awoke, restart the counters.
+ */
+ if (phyi->phyi_data.ts == 0) {
+ phyi->phyi_data = newstats;
+ continue;
+ }
+
+ in = newstats.ipackets - phyi->phyi_data.ipackets;
+ ierr = newstats.ierrors - phyi->phyi_data.ierrors;
+ out = newstats.opackets - phyi->phyi_data.opackets;
+ oerr = newstats.oerrors - phyi->phyi_data.oerrors;
+ phyi->phyi_data = newstats;
+
+ /*
+ * Withhold judgment when the short error counters
+ * wrap, the interface is reset, or if there are
+ * no kstats.
+ */
+ if (ierr < 0 || in < 0 || oerr < 0 || out < 0 ||
+ newstats.ts == 0) {
+ LIM_SEC(ifscan_timer,
+ now.tv_sec + CHECK_BAD_INTERVAL);
+ continue;
+ }
+
+ /* Withhold judgement when there is no traffic */
+ if (in == 0 && out == 0 && ierr == 0 && oerr == 0)
+ continue;
+
+ /*
+ * It is bad if at least 25% of input or output on
+ * an interface results in errors. Require
+ * presistent problems before marking it dead.
+ */
+ if ((ierr > 0 && ierr >= in/4) ||
+ (oerr > 0 && oerr >= out/4)) {
+ if (!(ifp->int_state & IS_SICK)) {
+ trace_act("interface %s to %s"
+ " sick: in=%d ierr=%d"
+ " out=%d oerr=%d",
+ ifp->int_name,
+ naddr_ntoa(ifp->int_dstaddr),
+ in, ierr, out, oerr);
+ if_sick(ifp, _B_TRUE);
+ continue;
+ }
+ if (!(ifp->int_state & IS_BROKE)) {
+ writelog(LOG_WARNING,
+ "interface %s to %s broken:"
+ " in=%d ierr=%d out=%d oerr=%d",
+ ifp->int_name,
+ naddr_ntoa(ifp->int_dstaddr),
+ in, ierr, out, oerr);
+ if_bad(ifp, _B_TRUE);
+ }
+ continue;
+ }
+
+ /* otherwise, it is active and healthy */
+ ifp->int_act_time = now.tv_sec;
+ if_ok(ifp, "", _B_TRUE);
+ continue;
+ }
+
+ /*
+ * This is a new interface.
+ * If it is dead, forget it.
+ */
+ if (!IS_IFF_UP(ifs.int_if_flags))
+ continue;
+
+ if (0 == (ifs.int_if_flags & (IFF_POINTOPOINT |
+ IFF_BROADCAST | IFF_LOOPBACK)) &&
+ !(ifs.int_state & IS_PASSIVE)) {
+ if (!(prev_complaints & COMP_BAD_FLAGS))
+ trace_act("%s is neither broadcast, "
+ "point-to-point, nor loopback",
+ ifs.int_name);
+ complaints |= COMP_BAD_FLAGS;
+ if (!(ifs.int_if_flags & IFF_MULTICAST))
+ ifs.int_state |= IS_NO_RDISC;
+ }
+
+
+ /*
+ * It is new and ok. Add it to the list of interfaces
+ */
+ ifp = rtmalloc(sizeof (*ifp), "ifscan ifp");
+ (void) memcpy(ifp, &ifs, sizeof (*ifp));
+ get_parms(ifp);
+ if_link(ifp, ifindex);
+ trace_if("Add", ifp);
+
+ if (ifp->int_phys != NULL &&
+ get_if_kstats(ifp, &ifp->int_phys->phyi_data) == -1) {
+ if (!(prev_complaints & COMP_NO_KSTATS))
+ writelog(LOG_NOTICE,
+ "unable to obtain kstats for %s",
+ ifp->int_phys->phyi_name);
+ complaints |= COMP_NO_KSTATS;
+ }
+
+ /* Detect interfaces that have conflicting netmasks. */
+ if (!(ifp->int_if_flags & (IFF_POINTOPOINT|IFF_LOOPBACK))) {
+ for (ifp1 = ifnet; ifp1 != NULL;
+ ifp1 = ifp1->int_next) {
+ if (ifp1->int_mask == ifp->int_mask)
+ continue;
+
+ /*
+ * we don't care about point-to-point
+ * or loopback aliases
+ */
+ if (ifp1->int_if_flags &
+ (IFF_POINTOPOINT|IFF_LOOPBACK)) {
+ continue;
+ }
+
+ /* ignore aliases on the same network */
+ if (ifp->int_phys == ifp1->int_phys)
+ continue;
+
+ if (on_net(ifp->int_addr,
+ ifp1->int_net, ifp1->int_mask) ||
+ on_net(ifp1->int_addr,
+ ifp->int_net, ifp->int_mask)) {
+ writelog(LOG_INFO,
+ "possible netmask problem"
+ " between %s:%s and %s:%s",
+ ifp->int_name,
+ addrname(htonl(ifp->int_net),
+ ifp->int_mask, 1),
+ ifp1->int_name,
+ addrname(htonl(ifp1->int_net),
+ ifp1->int_mask, 1));
+ complaints |= COMP_NETMASK;
+ }
+ }
+ }
+
+ if (!(ifp->int_state & IS_DUP) &&
+ !IS_IFF_QUIET(ifp->int_if_flags)) {
+ /* Count the # of directly connected networks. */
+ tot_interfaces++;
+ if (!IS_RIP_OFF(ifp->int_state))
+ rip_interfaces++;
+ if (!IS_RIP_OUT_OFF(ifp->int_state))
+ ripout_interfaces++;
+ if (IS_IFF_ROUTING(ifp->int_if_flags))
+ fwd_interfaces++;
+
+ if_ok_rdisc(ifp);
+ rip_on(ifp);
+ }
+ }
+
+ (void) close(sock);
+
+ /*
+ * If we are multi-homed and have at least two interfaces that
+ * are able to forward, then output RIP by default.
+ */
+ if (!supplier_set)
+ set_supplier();
+
+ /*
+ * If we are multi-homed, optionally advertise a route to
+ * our main address.
+ */
+ if (advertise_mhome || (tot_interfaces > 1 && mhome)) {
+ /* lookup myaddr if we haven't done so already */
+ if (myaddr == 0) {
+ char myname[MAXHOSTNAMELEN+1];
+
+ /*
+ * If we are unable to resolve our hostname, don't
+ * bother trying again.
+ */
+ if (gethostname(myname, MAXHOSTNAMELEN) == -1) {
+ msglog("gethostname: %s", rip_strerror(errno));
+ advertise_mhome = _B_FALSE;
+ mhome = _B_FALSE;
+ } else if (gethost(myname, &myaddr) == 0) {
+ writelog(LOG_WARNING,
+ "unable to resolve local hostname %s",
+ myname);
+ advertise_mhome = _B_FALSE;
+ mhome = _B_FALSE;
+ }
+ }
+ if (myaddr != 0 &&
+ (ifp = ifwithaddr(myaddr, _B_FALSE, _B_FALSE)) != NULL &&
+ foundloopback) {
+ advertise_mhome = _B_TRUE;
+ rt = rtget(myaddr, HOST_MASK);
+ if (rt != NULL) {
+ if (rt->rt_ifp != ifp ||
+ rt->rt_router != loopaddr) {
+ rtdelete(rt);
+ rt = NULL;
+ } else {
+ loop_rts.rts_ifp = ifp;
+ loop_rts.rts_metric = 0;
+ loop_rts.rts_time = rt->rt_time;
+ loop_rts.rts_origin = RO_LOOPBCK;
+ rtchange(rt, rt->rt_state | RS_MHOME,
+ &loop_rts, NULL);
+ }
+ }
+ if (rt == NULL) {
+ loop_rts.rts_ifp = ifp;
+ loop_rts.rts_metric = 0;
+ loop_rts.rts_origin = RO_LOOPBCK;
+ rtadd(myaddr, HOST_MASK, RS_MHOME, &loop_rts);
+ }
+ }
+ }
+
+ for (ifp = ifnet; ifp != NULL; ifp = ifp1) {
+ ifp1 = ifp->int_next; /* because we may delete it */
+
+ /* Forget any interfaces that have disappeared. */
+ if (!(ifp->int_state & (IS_CHECKED | IS_REMOTE))) {
+ trace_act("interface %s has disappeared",
+ ifp->int_name);
+ ifdel(ifp);
+ continue;
+ }
+
+ if ((ifp->int_state & IS_BROKE) &&
+ !(ifp->int_state & IS_PASSIVE))
+ LIM_SEC(ifscan_timer, now.tv_sec+CHECK_BAD_INTERVAL);
+
+ /*
+ * If we ever have a RIPv1 interface, assume we always will.
+ * It might come back if it ever goes away.
+ */
+ if (!(ifp->int_state & (IS_NO_RIPV1_OUT | IS_DUP)) &&
+ should_supply(ifp))
+ have_ripv1_out = _B_TRUE;
+ if (!(ifp->int_state & IS_NO_RIPV1_IN))
+ have_ripv1_in = _B_TRUE;
+ }
+
+ for (ifp = ifnet; ifp != NULL; ifp = ifp->int_next) {
+ /*
+ * Ensure there is always a network route for interfaces,
+ * after any dead interfaces have been deleted, which
+ * might affect routes for point-to-point links.
+ */
+ if (addrouteforif(ifp) == 0)
+ continue;
+
+ /*
+ * Add routes to the local end of point-to-point interfaces
+ * using loopback.
+ */
+ if ((ifp->int_if_flags & IFF_POINTOPOINT) &&
+ !(ifp->int_state & IS_REMOTE) && foundloopback) {
+ /*
+ * Delete any routes to the network address through
+ * foreign routers. Remove even static routes.
+ */
+ del_static(ifp->int_addr, HOST_MASK, 0, ifp, 0);
+ rt = rtget(ifp->int_addr, HOST_MASK);
+ if (rt != NULL && rt->rt_router != loopaddr) {
+ rtdelete(rt);
+ rt = NULL;
+ }
+ if (rt != NULL) {
+ if (!(rt->rt_state & RS_LOCAL) ||
+ rt->rt_metric > ifp->int_metric) {
+ ifp1 = ifp;
+ } else {
+ ifp1 = rt->rt_ifp;
+ }
+ loop_rts.rts_ifp = ifp1;
+ loop_rts.rts_metric = 0;
+ loop_rts.rts_time = rt->rt_time;
+ loop_rts.rts_origin = RO_LOOPBCK;
+ rtchange(rt, ((rt->rt_state & ~RS_NET_SYN) |
+ (RS_IF|RS_LOCAL)), &loop_rts, 0);
+ } else {
+ loop_rts.rts_ifp = ifp;
+ loop_rts.rts_metric = 0;
+ loop_rts.rts_origin = RO_LOOPBCK;
+ rtadd(ifp->int_addr, HOST_MASK,
+ (RS_IF | RS_LOCAL), &loop_rts);
+ }
+ }
+ }
+
+ /* add the authority routes */
+ for (intnetp = intnets; intnetp != NULL;
+ intnetp = intnetp->intnet_next) {
+ rt = rtget(intnetp->intnet_addr, intnetp->intnet_mask);
+ if (rt != NULL &&
+ !(rt->rt_state & RS_NO_NET_SYN) &&
+ !(rt->rt_state & RS_NET_INT)) {
+ rtdelete(rt);
+ rt = NULL;
+ }
+ if (rt == NULL) {
+ loop_rts.rts_ifp = NULL;
+ loop_rts.rts_metric = intnetp->intnet_metric-1;
+ loop_rts.rts_origin = RO_LOOPBCK;
+ rtadd(intnetp->intnet_addr, intnetp->intnet_mask,
+ RS_NET_SYN | RS_NET_INT, &loop_rts);
+ }
+ }
+
+ prev_complaints = complaints;
+}
+
+
+static void
+check_net_syn(struct interface *ifp)
+{
+ struct rt_entry *rt;
+ struct rt_spare new;
+
+ /*
+ * Turn on the need to automatically synthesize a network route
+ * for this interface only if we are running RIPv1 on some other
+ * interface that is on a different class-A,B,or C network.
+ */
+ if (have_ripv1_out || have_ripv1_in) {
+ ifp->int_state |= IS_NEED_NET_SYN;
+ rt = rtget(ifp->int_std_addr, ifp->int_std_mask);
+ if (rt != NULL &&
+ 0 == (rt->rt_state & RS_NO_NET_SYN) &&
+ (!(rt->rt_state & RS_NET_SYN) ||
+ rt->rt_metric > ifp->int_metric)) {
+ rtdelete(rt);
+ rt = NULL;
+ }
+ if (rt == NULL) {
+ (void) memset(&new, 0, sizeof (new));
+ new.rts_ifp = ifp;
+ new.rts_gate = ifp->int_addr;
+ new.rts_router = ifp->int_addr;
+ new.rts_metric = ifp->int_metric;
+ new.rts_origin = RO_NET_SYN;
+ rtadd(ifp->int_std_addr, ifp->int_std_mask,
+ RS_NET_SYN, &new);
+ }
+
+ } else {
+ ifp->int_state &= ~IS_NEED_NET_SYN;
+
+ rt = rtget(ifp->int_std_addr, ifp->int_std_mask);
+ if (rt != NULL &&
+ (rt->rt_state & RS_NET_SYN) &&
+ rt->rt_ifp == ifp)
+ rtbad_sub(rt, NULL);
+ }
+}
+
+
+/*
+ * Add route for interface if not currently installed.
+ * Create route to other end if a point-to-point link,
+ * otherwise a route to this (sub)network.
+ */
+static boolean_t /* _B_FALSE=bad interface */
+addrouteforif(struct interface *ifp)
+{
+ struct rt_entry *rt;
+ struct rt_spare new;
+ in_addr_t dst;
+ uint16_t rt_newstate = RS_IF;
+
+
+ /* skip sick interfaces */
+ if (ifp->int_state & IS_BROKE)
+ return (_B_FALSE);
+
+ /*
+ * don't install routes for duplicate interfaces, or
+ * unnumbered point-to-point interfaces.
+ */
+ if ((ifp->int_state & IS_DUP) ||
+ ((ifp->int_if_flags & IFF_POINTOPOINT) && ifp->int_dstaddr == 0))
+ return (_B_TRUE);
+
+ /*
+ * If the interface on a subnet, then install a RIPv1 route to
+ * the network as well (unless it is sick).
+ */
+ if (ifp->int_state & IS_SUBNET)
+ check_net_syn(ifp);
+
+ dst = (0 != (ifp->int_if_flags & (IFF_POINTOPOINT | IFF_LOOPBACK)) ?
+ ifp->int_dstaddr : htonl(ifp->int_net));
+
+ (void) memset(&new, 0, sizeof (new));
+ new.rts_ifp = ifp;
+ new.rts_router = ifp->int_addr;
+ new.rts_gate = ifp->int_addr;
+ new.rts_metric = ifp->int_metric;
+ new.rts_time = now.tv_sec;
+ if (ifp->int_if_flags & IFF_POINTOPOINT)
+ new.rts_origin = RO_PTOPT;
+ else if (ifp->int_if_flags & IFF_LOOPBACK)
+ new.rts_origin = RO_LOOPBCK;
+ else
+ new.rts_origin = RO_IF;
+
+ /*
+ * If we are going to send packets to the gateway,
+ * it must be reachable using our physical interfaces
+ */
+ if ((ifp->int_state & IS_REMOTE) &&
+ !(ifp->int_state & IS_EXTERNAL) &&
+ !check_remote(ifp))
+ return (_B_FALSE);
+
+ /*
+ * We are finished if the correct main interface route exists.
+ * The right route must be for the right interface, not synthesized
+ * from a subnet, be a "gateway" or not as appropriate, and so forth.
+ */
+ del_static(dst, ifp->int_mask, 0, ifp, 0);
+ rt = rtget(dst, ifp->int_mask);
+ if (!IS_IFF_ROUTING(ifp->int_if_flags))
+ rt_newstate |= RS_NOPROPAGATE;
+ if (rt != NULL) {
+ if ((rt->rt_ifp != ifp || rt->rt_router != ifp->int_addr) &&
+ (rt->rt_ifp == NULL ||
+ (rt->rt_ifp->int_state & IS_BROKE))) {
+ rtdelete(rt);
+ rt = NULL;
+ } else {
+ rtchange(rt, ((rt->rt_state | rt_newstate) &
+ ~(RS_NET_SYN | RS_LOCAL)), &new, 0);
+ }
+ }
+ if (rt == NULL) {
+ if (ifp->int_transitions++ > 0)
+ trace_act("re-installing interface %s;"
+ " went up %d times",
+ ifp->int_name, ifp->int_transitions);
+
+ rtadd(dst, ifp->int_mask, rt_newstate, &new);
+ }
+
+ return (_B_TRUE);
+}
+
+/*
+ * Obtains the named kstat, and places its value in *value. It
+ * returns 0 for success, -1 for failure.
+ */
+static int
+kstat_named_value(kstat_t *ksp, char *name, uint32_t *value)
+{
+ kstat_named_t *knp;
+
+ if (ksp == NULL)
+ return (-1);
+
+ if ((knp = kstat_data_lookup(ksp, name)) == NULL) {
+ return (-1);
+ } else if (knp->data_type != KSTAT_DATA_UINT32) {
+ return (-1);
+ } else {
+ *value = knp->value.ui32;
+ return (0);
+ }
+}
+
+static int
+get_if_kstats(struct interface *ifp, struct phyi_data *newdata)
+{
+ struct physical_interface *phyi = ifp->int_phys;
+ kstat_ctl_t *kc;
+ kstat_t *ksp;
+
+ /* We did this recently; don't do it again. */
+ if (phyi->phyi_data.ts == now.tv_sec) {
+ if (newdata != &phyi->phyi_data)
+ *newdata = phyi->phyi_data;
+ return (0);
+ }
+
+ if ((kc = kstat_open()) == NULL)
+ return (-1);
+
+ if ((ksp = kstat_lookup(kc, NULL, -1, phyi->phyi_name)) == NULL) {
+ (void) kstat_close(kc);
+ return (-1);
+ }
+
+ if (kstat_read(kc, ksp, NULL) == -1) {
+ (void) kstat_close(kc);
+ return (-1);
+ }
+
+ if ((kstat_named_value(ksp, "ipackets", &newdata->ipackets) == -1) ||
+ (kstat_named_value(ksp, "opackets", &newdata->opackets) == -1)) {
+ newdata->ts = 0;
+ (void) kstat_close(kc);
+ return (-1);
+ }
+
+ /* The loopback interface does not keep track of errors */
+ if (!(ifp->int_if_flags & IFF_LOOPBACK)) {
+ if ((kstat_named_value(ksp, "ierrors",
+ &newdata->ierrors) == -1) ||
+ (kstat_named_value(ksp, "oerrors",
+ &newdata->oerrors) == -1)) {
+ newdata->ts = 0;
+ (void) kstat_close(kc);
+ return (-1);
+ }
+ }
+
+ newdata->ts = now.tv_sec;
+ (void) kstat_close(kc);
+ return (0);
+}
+
+/*
+ * Returns true if we should supply routes to other systems. If the
+ * user has forced us to be a supplier (by the command line) or if we
+ * have more than one forwarding interface and this is one of the
+ * forwarding interfaces, then behave as a RIP supplier (supply rdisc
+ * advertisements and RIP responses).
+ */
+boolean_t
+should_supply(struct interface *ifp)
+{
+ if (ifp != NULL && !IS_IFF_ROUTING(ifp->int_if_flags))
+ return (_B_FALSE);
+ return ((supplier_set && supplier) ||
+ (!supplier_set && fwd_interfaces > 1));
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.routed/input.c b/usr/src/cmd/cmd-inet/usr.sbin/in.routed/input.c
new file mode 100644
index 0000000000..6d1f95a468
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.routed/input.c
@@ -0,0 +1,1515 @@
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 1983, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgment:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sbin/routed/input.c,v 1.9 2001/06/06 20:52:30 phk Exp $
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "defs.h"
+#include <md5.h>
+
+/*
+ * The size of the control buffer passed to recvmsg() used to receive
+ * ancillary data.
+ */
+#define CONTROL_BUFSIZE 1024
+
+static void input(struct sockaddr_in *, struct interface *, struct rip *, int);
+static boolean_t ck_passwd(struct interface *, struct rip *, uint8_t *,
+ in_addr_t, struct msg_limit *);
+
+
+/*
+ * Find the interface which received the given message.
+ */
+struct interface *
+receiving_interface(struct msghdr *msg, boolean_t findremote)
+{
+ struct interface *ifp, *ifp1, *ifp2;
+ struct sockaddr_in *from;
+ void *opt;
+ uint_t ifindex;
+
+ from = (struct sockaddr_in *)msg->msg_name;
+
+ /* First see if this packet came from a remote gateway. */
+ if (findremote && ((ifp = findremoteif(from->sin_addr.s_addr)) != NULL))
+ return (ifp);
+
+ /*
+ * It did not come from a remote gateway. Determine which
+ * physical interface this packet was received on by
+ * processing the message's ancillary data to find the
+ * IP_RECVIF option we requested.
+ */
+ if ((opt = find_ancillary(msg, IP_RECVIF)) == NULL) {
+ msglog("unable to retrieve IP_RECVIF");
+ } else {
+ ifindex = *(uint_t *)opt;
+ if ((ifp = ifwithindex(ifindex, _B_TRUE)) != NULL) {
+ /* Find the best match of the aliases */
+ ifp2 = NULL;
+ for (ifp1 = ifp; ifp1 != NULL;
+ ifp1 = ifp1->int_ilist.hl_next) {
+ if (ifp1->int_addr == from->sin_addr.s_addr)
+ return (ifp1);
+ if ((ifp2 == NULL ||
+ (ifp2->int_state & IS_ALIAS)) &&
+ on_net(from->sin_addr.s_addr, ifp1->int_net,
+ ifp1->int_mask))
+ ifp2 = ifp1;
+ }
+ if (ifp2 != NULL)
+ ifp = ifp2;
+ return (ifp);
+ }
+ }
+
+ /*
+ * As a last resort (for some reason, ip didn't give us the
+ * IP_RECVIF index we requested), try to deduce the receiving
+ * interface based on the source address of the packet.
+ */
+ return (iflookup(from->sin_addr.s_addr));
+}
+
+/*
+ * Process RIP input on rip_sock. Returns 0 for success, -1 for failure.
+ */
+int
+read_rip()
+{
+ struct sockaddr_in from;
+ struct interface *ifp;
+ int cc;
+ union pkt_buf inbuf;
+ struct msghdr msg;
+ struct iovec iov;
+ uint8_t ancillary_data[CONTROL_BUFSIZE];
+
+ iov.iov_base = &inbuf;
+ iov.iov_len = sizeof (inbuf);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_name = &from;
+ msg.msg_control = &ancillary_data;
+
+ for (;;) {
+ msg.msg_namelen = sizeof (from);
+ msg.msg_controllen = sizeof (ancillary_data);
+ cc = recvmsg(rip_sock, &msg, 0);
+ if (cc == 0)
+ return (-1);
+ if (cc < 0) {
+ if (errno == EWOULDBLOCK || errno == EINTR)
+ return (0);
+ LOGERR("recvmsg(rip_sock)");
+ return (-1);
+ }
+
+ /*
+ * ifp is the interface via which the packet arrived.
+ */
+ ifp = receiving_interface(&msg, _B_TRUE);
+
+ input(&from, ifp, &inbuf.rip, cc);
+ }
+}
+
+
+/* Process a RIP packet */
+static void
+input(struct sockaddr_in *from, /* received from this IP address */
+ struct interface *ifp, /* interface of incoming socket */
+ struct rip *rip,
+ int cc)
+{
+#define FROM_NADDR from->sin_addr.s_addr
+ static struct msg_limit use_auth, bad_len, bad_mask;
+ static struct msg_limit unk_router, bad_router, bad_nhop;
+
+ struct rt_entry *rt;
+ struct rt_spare new;
+ struct netinfo *n, *lim;
+ struct interface *ifp1;
+ in_addr_t gate, mask, v1_mask, dst, ddst_h = 0;
+ struct auth *ap;
+ struct tgate *tg = NULL;
+ struct tgate_net *tn;
+ int i, j;
+ boolean_t poll_answer = _B_FALSE; /* Set to _B_TRUE if RIPCMD_POLL */
+ uint16_t rt_state = 0; /* Extra route state to pass to input_route() */
+ uint8_t metric;
+
+ (void) memset(&new, 0, sizeof (new));
+ /* Notice when we hear from a remote gateway */
+ if (ifp != NULL && (ifp->int_state & IS_REMOTE))
+ ifp->int_act_time = now.tv_sec;
+
+ trace_rip("Recv", "from", from, ifp, rip, cc);
+
+ if (ifp != NULL && (ifp->int_if_flags & IFF_NORTEXCH)) {
+ trace_misc("discard RIP packet received over %s (IFF_NORTEXCH)",
+ ifp->int_name);
+ return;
+ }
+
+ gate = ntohl(FROM_NADDR);
+ if (IN_EXPERIMENTAL(gate) || (gate >> IN_CLASSA_NSHIFT) == 0) {
+ msglim(&bad_router, FROM_NADDR, "source address %s unusable",
+ naddr_ntoa(FROM_NADDR));
+ return;
+ }
+
+ if (rip->rip_vers == 0) {
+ msglim(&bad_router, FROM_NADDR,
+ "RIP version 0, cmd %d, packet received from %s",
+ rip->rip_cmd, naddr_ntoa(FROM_NADDR));
+ return;
+ }
+
+ if (rip->rip_vers > RIPv2) {
+ msglim(&bad_router, FROM_NADDR,
+ "Treating RIP version %d packet received from %s as "
+ "version %d", rip->rip_vers, naddr_ntoa(FROM_NADDR),
+ RIPv2);
+ rip->rip_vers = RIPv2;
+ }
+
+ if (cc > (int)OVER_MAXPACKETSIZE) {
+ msglim(&bad_router, FROM_NADDR,
+ "packet at least %d bytes too long received from %s",
+ cc-MAXPACKETSIZE, naddr_ntoa(FROM_NADDR));
+ }
+
+ n = rip->rip_nets;
+ lim = n + (cc - 4) / sizeof (struct netinfo);
+
+ /*
+ * Notice authentication.
+ * As required by section 5.2 of RFC 2453, discard authenticated
+ * RIPv2 messages, but only if configured for that silliness.
+ *
+ * RIPv2 authentication is lame. Why authenticate queries?
+ * Why should a RIPv2 implementation with authentication disabled
+ * not be able to listen to RIPv2 packets with authentication, while
+ * RIPv1 systems will listen? Crazy!
+ */
+ if (!auth_ok && rip->rip_vers == RIPv2 && n < lim &&
+ n->n_family == RIP_AF_AUTH) {
+ msglim(&use_auth, FROM_NADDR,
+ "RIPv2 message with authentication from %s discarded",
+ naddr_ntoa(FROM_NADDR));
+ return;
+ }
+
+ switch (rip->rip_cmd) {
+ case RIPCMD_POLL:
+ /*
+ * Similar to RIPCMD_REQUEST, this command is used to
+ * request either a full-table or a set of entries. Both
+ * silent processes and routers can respond to this
+ * command.
+ */
+ poll_answer = _B_TRUE;
+ /* FALLTHRU */
+ case RIPCMD_REQUEST:
+ /* Are we talking to ourself or a remote gateway? */
+ ifp1 = ifwithaddr(FROM_NADDR, _B_FALSE, _B_TRUE);
+ if (ifp1 != NULL) {
+ if (ifp1->int_state & IS_REMOTE) {
+ /* remote gateway */
+ ifp = ifp1;
+ if (check_remote(ifp)) {
+ ifp->int_act_time = now.tv_sec;
+ if_ok(ifp, "remote ", _B_FALSE);
+ }
+ } else if (from->sin_port == htons(RIP_PORT)) {
+ trace_pkt(" discard our own RIP request");
+ return;
+ }
+ }
+
+ /* did the request come from a router? */
+ if (!poll_answer && (from->sin_port == htons(RIP_PORT))) {
+ /*
+ * yes, ignore the request if RIP is off so that
+ * the router does not depend on us.
+ */
+ if (ripout_interfaces == 0 ||
+ (ifp != NULL && (IS_RIP_OUT_OFF(ifp->int_state) ||
+ !IS_IFF_ROUTING(ifp->int_if_flags)))) {
+ trace_pkt(" discard request while RIP off");
+ return;
+ }
+ }
+
+ /*
+ * According to RFC 2453 section 5.2, we should ignore
+ * unauthenticated queries when authentication is
+ * configured. That is too silly to bother with. Sheesh!
+ * Are forwarding tables supposed to be secret even though
+ * a bad guy can infer them with test traffic? RIP is
+ * still the most common router-discovery protocol, so
+ * hosts need to send queries that will be answered. What
+ * about `rtquery`? Maybe on firewalls you'd care, but not
+ * enough to give up the diagnostic facilities of remote
+ * probing.
+ */
+
+ if (n >= lim) {
+ msglim(&bad_len, FROM_NADDR, "empty request from %s",
+ naddr_ntoa(FROM_NADDR));
+ return;
+ }
+ if (cc%sizeof (*n) != sizeof (struct rip)%sizeof (*n)) {
+ msglim(&bad_len, FROM_NADDR,
+ "request of bad length (%d) from %s",
+ cc, naddr_ntoa(FROM_NADDR));
+ }
+
+ if (rip->rip_vers == RIPv2 && (ifp == NULL ||
+ (ifp->int_state & IS_NO_RIPV1_OUT))) {
+ v12buf.buf->rip_vers = RIPv2;
+ /*
+ * If we have a secret but it is a cleartext secret,
+ * do not disclose our secret unless the other guy
+ * already knows it.
+ */
+ ap = find_auth(ifp);
+ if (ap != NULL &&
+ (ulong_t)ap->end < (ulong_t)clk.tv_sec) {
+ /*
+ * Don't authenticate incoming packets
+ * using an expired key.
+ */
+ msglim(&use_auth, FROM_NADDR,
+ "%s attempting to authenticate using "
+ "an expired password.",
+ naddr_ntoa(FROM_NADDR));
+ ap = NULL;
+ }
+ if (ap != NULL && ap->type == RIP_AUTH_PW &&
+ (n->n_family != RIP_AF_AUTH ||
+ !ck_passwd(ifp, rip, (uint8_t *)lim, FROM_NADDR,
+ &use_auth)))
+ ap = NULL;
+ } else {
+ v12buf.buf->rip_vers = RIPv1;
+ ap = NULL;
+ }
+ clr_ws_buf(&v12buf, ap);
+
+ do {
+ n->n_metric = ntohl(n->n_metric);
+
+ /*
+ * A single entry with family RIP_AF_UNSPEC and
+ * metric HOPCNT_INFINITY means "all routes".
+ * We respond to routers only if we are acting
+ * as a supplier, or to anyone other than a router
+ * (i.e. a query).
+ */
+ if (n->n_family == RIP_AF_UNSPEC &&
+ n->n_metric == HOPCNT_INFINITY) {
+ /*
+ * Answer a full-table query from a utility
+ * program with all we know.
+ */
+ if (poll_answer ||
+ (from->sin_port != htons(RIP_PORT))) {
+ supply(from, ifp, OUT_QUERY, 0,
+ rip->rip_vers, ap != NULL);
+ return;
+ }
+
+ /*
+ * A router is trying to prime its tables.
+ * Filter the answer in the same way
+ * broadcasts are filtered.
+ *
+ * Only answer a router if we are a supplier
+ * to keep an unwary host that is just starting
+ * from picking us as a router.
+ */
+ if (ifp == NULL) {
+ trace_pkt("ignore distant router");
+ return;
+ }
+ if (IS_RIP_OFF(ifp->int_state) ||
+ !should_supply(ifp)) {
+ trace_pkt("ignore; not supplying");
+ return;
+ }
+
+ /*
+ * Do not answer a RIPv1 router if
+ * we are sending RIPv2. But do offer
+ * poor man's router discovery.
+ */
+ if ((ifp->int_state & IS_NO_RIPV1_OUT) &&
+ rip->rip_vers == RIPv1) {
+ if (!(ifp->int_state & IS_PM_RDISC)) {
+ trace_pkt("ignore; sending RIPv2");
+ return;
+ }
+
+ v12buf.n->n_family = RIP_AF_INET;
+ v12buf.n->n_dst = RIP_DEFAULT;
+ metric = ifp->int_d_metric;
+ if (NULL !=
+ (rt = rtget(RIP_DEFAULT, 0)))
+ metric = MIN(metric,
+ (rt->rt_metric + 1));
+ v12buf.n->n_metric = htonl(metric);
+ v12buf.n++;
+ break;
+ }
+
+ /*
+ * Respond with RIPv1 instead of RIPv2 if
+ * that is what we are broadcasting on the
+ * interface to keep the remote router from
+ * getting the wrong initial idea of the
+ * routes we send.
+ */
+ supply(from, ifp, OUT_UNICAST, 0,
+ (ifp->int_state & IS_NO_RIPV1_OUT)
+ ? RIPv2 : RIPv1,
+ ap != NULL);
+ return;
+ }
+
+ /* Ignore authentication */
+ if (n->n_family == RIP_AF_AUTH)
+ continue;
+
+ if (n->n_family != RIP_AF_INET) {
+ msglim(&bad_router, FROM_NADDR,
+ "request from %s for unsupported"
+ " (af %d) %s",
+ naddr_ntoa(FROM_NADDR),
+ ntohs(n->n_family),
+ naddr_ntoa(n->n_dst));
+ return;
+ }
+
+ /* We are being asked about a specific destination. */
+ v12buf.n->n_dst = dst = n->n_dst;
+ v12buf.n->n_family = RIP_AF_INET;
+ if (!check_dst(dst)) {
+ msglim(&bad_router, FROM_NADDR,
+ "bad queried destination %s from %s",
+ naddr_ntoa(dst),
+ naddr_ntoa(FROM_NADDR));
+ v12buf.n->n_metric = HOPCNT_INFINITY;
+ goto rte_done;
+ }
+
+ /* decide what mask was intended */
+ if (rip->rip_vers == RIPv1 ||
+ 0 == (mask = ntohl(n->n_mask)) ||
+ 0 != (ntohl(dst) & ~mask))
+ mask = ripv1_mask_host(dst, ifp);
+
+ /*
+ * Try to find the answer. If we don't have an
+ * explicit route for the destination, use the best
+ * route to the destination.
+ */
+ rt = rtget(dst, mask);
+ if (rt == NULL && dst != RIP_DEFAULT)
+ rt = rtfind(n->n_dst);
+
+ if (v12buf.buf->rip_vers != RIPv1)
+ v12buf.n->n_mask = htonl(mask);
+ if (rt == NULL) {
+ /* we do not have the answer */
+ v12buf.n->n_metric = HOPCNT_INFINITY;
+ goto rte_done;
+ }
+
+ /*
+ * we have the answer, so compute the right metric
+ * and next hop.
+ */
+ v12buf.n->n_metric = rt->rt_metric + 1;
+ if (v12buf.n->n_metric > HOPCNT_INFINITY)
+ v12buf.n->n_metric = HOPCNT_INFINITY;
+ if (v12buf.buf->rip_vers != RIPv1) {
+ v12buf.n->n_tag = rt->rt_tag;
+ if (ifp != NULL &&
+ on_net(rt->rt_gate, ifp->int_net,
+ ifp->int_mask) &&
+ rt->rt_gate != ifp->int_addr)
+ v12buf.n->n_nhop = rt->rt_gate;
+ }
+rte_done:
+ v12buf.n->n_metric = htonl(v12buf.n->n_metric);
+
+ /*
+ * Stop paying attention if we fill the output buffer.
+ */
+ if (++v12buf.n >= v12buf.lim)
+ break;
+ } while (++n < lim);
+
+ /*
+ * If our response is authenticated with md5, complete the
+ * md5 computation.
+ */
+ if (ap != NULL && ap->type == RIP_AUTH_MD5)
+ end_md5_auth(&v12buf, ap);
+
+ /*
+ * Diagnostic programs make specific requests
+ * from ports other than 520. Log other types
+ * of specific requests as suspicious.
+ */
+ if (!poll_answer && (from->sin_port == htons(RIP_PORT))) {
+ writelog(LOG_WARNING,
+ "Received suspicious request from %s port %d",
+ naddr_ntoa(FROM_NADDR), RIP_PORT);
+ }
+ if (poll_answer || (from->sin_port != htons(RIP_PORT))) {
+ /* query */
+ (void) output(OUT_QUERY, from, ifp, v12buf.buf,
+ ((char *)v12buf.n - (char *)v12buf.buf));
+ } else {
+ (void) output(OUT_UNICAST, from, ifp,
+ v12buf.buf, ((char *)v12buf.n -
+ (char *)v12buf.buf));
+ }
+ return;
+
+ case RIPCMD_TRACEON:
+ case RIPCMD_TRACEOFF:
+ /*
+ * Notice that trace messages are turned off for all possible
+ * abuse if PATH_TRACE is undefined in pathnames.h.
+ * Notice also that because of the way the trace file is
+ * handled in trace.c, no abuse is plausible even if
+ * PATH_TRACE is defined.
+ *
+ * First verify message came from a privileged port.
+ */
+ if (ntohs(from->sin_port) > IPPORT_RESERVED) {
+ trace_pkt("trace command from untrusted port %d on %s",
+ ntohs(from->sin_port), naddr_ntoa(FROM_NADDR));
+ return;
+ }
+ if (ifp == NULL || !remote_address_ok(ifp, FROM_NADDR)) {
+ /*
+ * Use a message here to warn about strange
+ * messages from remote systems.
+ */
+ msglim(&bad_router, FROM_NADDR,
+ "trace command from non-local host %s",
+ naddr_ntoa(FROM_NADDR));
+ return;
+ }
+ if (ifp->int_state & IS_DISTRUST) {
+ tg = tgates;
+ while (tg->tgate_addr != FROM_NADDR) {
+ tg = tg->tgate_next;
+ if (tg == NULL) {
+ trace_pkt("trace command from "
+ "untrusted host %s",
+ naddr_ntoa(FROM_NADDR));
+ return;
+ }
+ }
+ }
+ if (ifp->int_auth[0].type != RIP_AUTH_NONE) {
+ /*
+ * Technically, it would be fairly easy to add
+ * standard authentication to the existing
+ * trace commands -- just bracket the payload
+ * with the authentication information.
+ * However, the tracing message behavior
+ * itself is marginal enough that we don't
+ * actually care. Just discard if
+ * authentication is needed.
+ */
+ trace_pkt("trace command unauthenticated from %s",
+ naddr_ntoa(FROM_NADDR));
+ return;
+ }
+ if (rip->rip_cmd == RIPCMD_TRACEON) {
+ rip->rip_tracefile[cc-4] = '\0';
+ set_tracefile(rip->rip_tracefile,
+ "trace command: %s\n", 0);
+ } else {
+ trace_off("tracing turned off by %s",
+ naddr_ntoa(FROM_NADDR));
+ }
+ return;
+
+ case RIPCMD_RESPONSE:
+ if (ifp != NULL && (ifp->int_if_flags & IFF_NOXMIT)) {
+ trace_misc("discard RIP response received over %s "
+ "(IFF_NOXMIT)", ifp->int_name);
+ return;
+ }
+
+ if (cc%sizeof (*n) != sizeof (struct rip)%sizeof (*n)) {
+ msglim(&bad_len, FROM_NADDR,
+ "response of bad length (%d) from %s",
+ cc, naddr_ntoa(FROM_NADDR));
+ }
+
+ if ((ntohl(FROM_NADDR) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) {
+ msglim(&bad_router, FROM_NADDR,
+ "discard RIP response from bad source address %s",
+ naddr_ntoa(FROM_NADDR));
+ return;
+ }
+
+ /* verify message came from a router */
+ if (from->sin_port != htons(RIP_PORT)) {
+ msglim(&bad_router, FROM_NADDR,
+ " discard RIP response from unknown port"
+ " %d on host %s", ntohs(from->sin_port),
+ naddr_ntoa(FROM_NADDR));
+ return;
+ }
+
+ if (!rip_enabled) {
+ trace_pkt(" discard response while RIP off");
+ return;
+ }
+
+ /* Are we talking to ourself or a remote gateway? */
+ ifp1 = ifwithaddr(FROM_NADDR, _B_FALSE, _B_TRUE);
+ if (ifp1 != NULL) {
+ if (ifp1->int_state & IS_REMOTE) {
+ /* remote gateway */
+ ifp = ifp1;
+ if (check_remote(ifp)) {
+ ifp->int_act_time = now.tv_sec;
+ if_ok(ifp, "remote ", _B_FALSE);
+ }
+ } else {
+ trace_pkt(" discard our own RIP response");
+ return;
+ }
+ } else {
+ /*
+ * If it's not a remote gateway, then the
+ * remote address *must* be directly
+ * connected. Make sure that it is.
+ */
+ if (ifp != NULL &&
+ !remote_address_ok(ifp, FROM_NADDR)) {
+ msglim(&bad_router, FROM_NADDR,
+ "discard RIP response; source %s not on "
+ "interface %s", naddr_ntoa(FROM_NADDR),
+ ifp->int_name);
+ return;
+ }
+ }
+
+ /*
+ * Accept routing packets from routers directly connected
+ * via broadcast or point-to-point networks, and from
+ * those listed in /etc/gateways.
+ */
+ if (ifp == NULL) {
+ msglim(&unk_router, FROM_NADDR,
+ " discard response from %s"
+ " via unexpected interface",
+ naddr_ntoa(FROM_NADDR));
+ return;
+ }
+
+ if (IS_RIP_IN_OFF(ifp->int_state)) {
+ trace_pkt(" discard RIPv%d response"
+ " via disabled interface %s",
+ rip->rip_vers, ifp->int_name);
+ return;
+ }
+
+ if (n >= lim) {
+ msglim(&bad_len, FROM_NADDR, "empty response from %s",
+ naddr_ntoa(FROM_NADDR));
+ return;
+ }
+
+ if (((ifp->int_state & IS_NO_RIPV1_IN) &&
+ rip->rip_vers == RIPv1) ||
+ ((ifp->int_state & IS_NO_RIPV2_IN) &&
+ rip->rip_vers != RIPv1)) {
+ trace_pkt(" discard RIPv%d response",
+ rip->rip_vers);
+ return;
+ }
+
+ /*
+ * Continue to listen to routes via broken interfaces
+ * which might be declared IS_BROKE because of
+ * device-driver idiosyncracies, but might otherwise
+ * be perfectly healthy.
+ */
+ if (ifp->int_state & IS_BROKE) {
+ trace_pkt("response via broken interface %s",
+ ifp->int_name);
+ }
+
+ /*
+ * If the interface cares, ignore bad routers.
+ * Trace but do not log this problem, because where it
+ * happens, it happens frequently.
+ */
+ if (ifp->int_state & IS_DISTRUST) {
+ tg = tgates;
+ while (tg->tgate_addr != FROM_NADDR) {
+ tg = tg->tgate_next;
+ if (tg == NULL) {
+ trace_pkt(" discard RIP response"
+ " from untrusted router %s",
+ naddr_ntoa(FROM_NADDR));
+ return;
+ }
+ }
+ }
+
+ /*
+ * Authenticate the packet if we have a secret.
+ * If we do not have any secrets, ignore the error in
+ * RFC 1723 and accept it regardless.
+ */
+ if (ifp->int_auth[0].type != RIP_AUTH_NONE &&
+ rip->rip_vers != RIPv1 &&
+ !ck_passwd(ifp, rip, (uint8_t *)lim, FROM_NADDR, &use_auth))
+ return;
+
+ /*
+ * Do this only if we're supplying routes to *nobody*.
+ */
+ if (!should_supply(NULL) && save_space) {
+ /*
+ * "-S" option. Instead of entering all routes,
+ * only enter a default route for the sender of
+ * this RESPONSE message
+ */
+
+ /* Should we trust this route from this router? */
+ if (tg != NULL && tg->tgate_nets->mask != 0) {
+ trace_pkt(" ignored unauthorized %s",
+ addrname(RIP_DEFAULT, 0, 0));
+ break;
+ }
+
+ new.rts_gate = FROM_NADDR;
+ new.rts_router = FROM_NADDR;
+ new.rts_metric = HOPCNT_INFINITY-1;
+ new.rts_tag = n->n_tag;
+ new.rts_time = now.tv_sec;
+ new.rts_ifp = ifp;
+ new.rts_de_ag = 0;
+ new.rts_origin = RO_RIP;
+ /*
+ * Add the newly generated default route, but don't
+ * propagate the madness. Treat it the same way as
+ * default routes learned from Router Discovery.
+ */
+ input_route(RIP_DEFAULT, 0, &new, n, RS_NOPROPAGATE);
+ return;
+ }
+
+ if (!IS_IFF_ROUTING(ifp->int_if_flags)) {
+ /*
+ * We don't want to propagate routes which would
+ * result in a black-hole.
+ */
+ rt_state = RS_NOPROPAGATE;
+ }
+
+ do {
+ if (n->n_family == RIP_AF_AUTH)
+ continue;
+
+ n->n_metric = ntohl(n->n_metric);
+ dst = n->n_dst;
+ if (n->n_family != RIP_AF_INET &&
+ (n->n_family != RIP_AF_UNSPEC ||
+ dst != RIP_DEFAULT)) {
+ msglim(&bad_router, FROM_NADDR,
+ "route from %s to unsupported"
+ " address family=%d destination=%s",
+ naddr_ntoa(FROM_NADDR), n->n_family,
+ naddr_ntoa(dst));
+ continue;
+ }
+ if (!check_dst(dst)) {
+ msglim(&bad_router, FROM_NADDR,
+ "bad destination %s from %s",
+ naddr_ntoa(dst),
+ naddr_ntoa(FROM_NADDR));
+ continue;
+ }
+ if (n->n_metric == 0 || n->n_metric > HOPCNT_INFINITY) {
+ msglim(&bad_router, FROM_NADDR,
+ "bad metric %d from %s"
+ " for destination %s",
+ n->n_metric, naddr_ntoa(FROM_NADDR),
+ naddr_ntoa(dst));
+ continue;
+ }
+
+ /*
+ * Notice the next-hop.
+ */
+ gate = FROM_NADDR;
+ if (n->n_nhop != 0) {
+ if (rip->rip_vers == RIPv1) {
+ n->n_nhop = 0;
+ } else {
+ /* Use it only if it is valid. */
+ if (on_net(n->n_nhop,
+ ifp->int_net, ifp->int_mask) &&
+ check_dst(n->n_nhop)) {
+ gate = n->n_nhop;
+ } else {
+ msglim(&bad_nhop,
+ FROM_NADDR,
+ "router %s to %s"
+ " has bad next hop %s",
+ naddr_ntoa(FROM_NADDR),
+ naddr_ntoa(dst),
+ naddr_ntoa(n->n_nhop));
+ n->n_nhop = 0;
+ }
+ }
+ }
+
+ if (rip->rip_vers == RIPv1 ||
+ 0 == (mask = ntohl(n->n_mask))) {
+ mask = ripv1_mask_host(dst, ifp);
+ } else if ((ntohl(dst) & ~mask) != 0) {
+ msglim(&bad_mask, FROM_NADDR,
+ "router %s sent bad netmask %s with %s",
+ naddr_ntoa(FROM_NADDR),
+ naddr_ntoa(htonl(mask)),
+ naddr_ntoa(dst));
+ continue;
+ }
+
+ if (mask == HOST_MASK &&
+ (ifp->int_state & IS_NO_HOST)) {
+ trace_pkt(" ignored host route %s",
+ addrname(dst, mask, 0));
+ continue;
+ }
+
+ if (rip->rip_vers == RIPv1)
+ n->n_tag = 0;
+
+ /*
+ * Adjust metric according to incoming interface cost.
+ * We intentionally don't drop incoming routes with
+ * metric 15 on the floor even though they will
+ * not be advertised to other routers. We can use
+ * such routes locally, resulting in a network with
+ * a maximum width of 15 hops rather than 14.
+ */
+ n->n_metric += ifp->int_metric;
+ if (n->n_metric > HOPCNT_INFINITY)
+ n->n_metric = HOPCNT_INFINITY;
+
+ /*
+ * Should we trust this route from this router?
+ */
+ if (tg != NULL && (tn = tg->tgate_nets)->mask != 0) {
+ for (i = 0; i < MAX_TGATE_NETS; i++, tn++) {
+ if (on_net(dst, tn->net, tn->mask) &&
+ tn->mask <= mask)
+ break;
+ }
+ if (i >= MAX_TGATE_NETS || tn->mask == 0) {
+ trace_pkt(" ignored unauthorized %s",
+ addrname(dst, mask, 0));
+ continue;
+ }
+ }
+
+ /*
+ * Recognize and ignore a default route we faked
+ * which is being sent back to us by a machine with
+ * broken split-horizon. Be a little more paranoid
+ * than that, and reject default routes with the
+ * same metric we advertised.
+ */
+ if (ifp->int_d_metric != 0 && dst == RIP_DEFAULT &&
+ n->n_metric >= ifp->int_d_metric)
+ continue;
+
+ /*
+ * We can receive aggregated RIPv2 routes that must
+ * be broken down before they are transmitted by
+ * RIPv1 via an interface on a subnet. We might
+ * also receive the same routes aggregated via
+ * other RIPv2 interfaces. This could cause
+ * duplicate routes to be sent on the RIPv1
+ * interfaces. "Longest matching variable length
+ * netmasks" lets RIPv2 listeners understand, but
+ * breaking down the aggregated routes for RIPv1
+ * listeners can produce duplicate routes.
+ *
+ * Breaking down aggregated routes here bloats the
+ * daemon table, but does not hurt the kernel
+ * table, since routes are always aggregated for
+ * the kernel.
+ *
+ * Notice that this does not break down network
+ * routes corresponding to subnets. This is part of
+ * the defense against RS_NET_SYN.
+ */
+ if (have_ripv1_out &&
+ (((rt = rtget(dst, mask)) == NULL ||
+ !(rt->rt_state & RS_NET_SYN))) &&
+ (v1_mask = ripv1_mask_net(dst, 0)) > mask) {
+ /* Get least significant set bit */
+ ddst_h = v1_mask & -v1_mask;
+ i = (v1_mask & ~mask)/ddst_h;
+ /*
+ * If you're going to make 512 or more
+ * routes, then that's just too many. The
+ * reason here is that breaking an old
+ * class B into /24 allocations is common
+ * enough that allowing for the creation of
+ * at least 256 deaggregated routes is
+ * good. The next power of 2 is 512.
+ */
+ if (i >= 511) {
+ /*
+ * Punt if we would have to
+ * generate an unreasonable number
+ * of routes.
+ */
+ if (TRACECONTENTS)
+ trace_misc("accept %s-->%s as 1"
+ " instead of %d routes",
+ addrname(dst, mask, 0),
+ naddr_ntoa(FROM_NADDR),
+ i + 1);
+ i = 0;
+ } else {
+ mask = v1_mask;
+ }
+ } else {
+ i = 0;
+ }
+
+ new.rts_gate = gate;
+ new.rts_router = FROM_NADDR;
+ new.rts_metric = n->n_metric;
+ new.rts_tag = n->n_tag;
+ new.rts_time = now.tv_sec;
+ new.rts_ifp = ifp;
+ new.rts_de_ag = i;
+ new.rts_origin = RO_RIP;
+ j = 0;
+ for (;;) {
+ input_route(dst, mask, &new, n, rt_state);
+ if (++j > i)
+ break;
+ dst = htonl(ntohl(dst) + ddst_h);
+ }
+ } while (++n < lim);
+ return;
+ case RIPCMD_POLLENTRY:
+ /*
+ * With this command one can request a single entry.
+ * Both silent processes and routers can respond to this
+ * command
+ */
+
+ if (n >= lim) {
+ msglim(&bad_len, FROM_NADDR, "empty request from %s",
+ naddr_ntoa(FROM_NADDR));
+ return;
+ }
+ if (cc%sizeof (*n) != sizeof (struct rip)%sizeof (*n)) {
+ msglim(&bad_len, FROM_NADDR,
+ "request of bad length (%d) from %s",
+ cc, naddr_ntoa(FROM_NADDR));
+ }
+
+ if (rip->rip_vers == RIPv2 && (ifp == NULL ||
+ (ifp->int_state & IS_NO_RIPV1_OUT))) {
+ v12buf.buf->rip_vers = RIPv2;
+ } else {
+ v12buf.buf->rip_vers = RIPv1;
+ }
+ /* Dont bother with md5 authentication with POLLENTRY */
+ ap = NULL;
+ clr_ws_buf(&v12buf, ap);
+
+ n->n_metric = ntohl(n->n_metric);
+
+ if (n->n_family != RIP_AF_INET) {
+ msglim(&bad_router, FROM_NADDR,
+ "POLLENTRY request from %s for unsupported"
+ " (af %d) %s",
+ naddr_ntoa(FROM_NADDR),
+ ntohs(n->n_family),
+ naddr_ntoa(n->n_dst));
+ return;
+ }
+
+ /* We are being asked about a specific destination. */
+ v12buf.n->n_dst = dst = n->n_dst;
+ v12buf.n->n_family = RIP_AF_INET;
+ if (!check_dst(dst)) {
+ msglim(&bad_router, FROM_NADDR,
+ "bad queried destination %s from %s",
+ naddr_ntoa(dst),
+ naddr_ntoa(FROM_NADDR));
+ v12buf.n->n_metric = HOPCNT_INFINITY;
+ goto pollentry_done;
+ }
+
+ /* decide what mask was intended */
+ if (rip->rip_vers == RIPv1 ||
+ 0 == (mask = ntohl(n->n_mask)) ||
+ 0 != (ntohl(dst) & ~mask))
+ mask = ripv1_mask_host(dst, ifp);
+
+ /* try to find the answer */
+ rt = rtget(dst, mask);
+ if (rt == NULL && dst != RIP_DEFAULT)
+ rt = rtfind(n->n_dst);
+
+ if (v12buf.buf->rip_vers != RIPv1)
+ v12buf.n->n_mask = htonl(mask);
+ if (rt == NULL) {
+ /* we do not have the answer */
+ v12buf.n->n_metric = HOPCNT_INFINITY;
+ goto pollentry_done;
+ }
+
+
+ /*
+ * we have the answer, so compute the right metric and next
+ * hop.
+ */
+ v12buf.n->n_metric = rt->rt_metric + 1;
+ if (v12buf.n->n_metric > HOPCNT_INFINITY)
+ v12buf.n->n_metric = HOPCNT_INFINITY;
+ if (v12buf.buf->rip_vers != RIPv1) {
+ v12buf.n->n_tag = rt->rt_tag;
+ if (ifp != NULL &&
+ on_net(rt->rt_gate, ifp->int_net, ifp->int_mask) &&
+ rt->rt_gate != ifp->int_addr)
+ v12buf.n->n_nhop = rt->rt_gate;
+ }
+pollentry_done:
+ v12buf.n->n_metric = htonl(v12buf.n->n_metric);
+
+ /*
+ * Send the answer about specific routes.
+ */
+ (void) output(OUT_QUERY, from, ifp, v12buf.buf,
+ ((char *)v12buf.n - (char *)v12buf.buf));
+ break;
+ }
+#undef FROM_NADDR
+}
+
+
+/*
+ * Process a single input route.
+ */
+void
+input_route(in_addr_t dst, /* network order */
+ in_addr_t mask,
+ struct rt_spare *new,
+ struct netinfo *n,
+ uint16_t rt_state)
+{
+ int i;
+ struct rt_entry *rt;
+ struct rt_spare *rts, *rts0;
+ struct interface *ifp1;
+ struct rt_spare *ptr;
+ size_t ptrsize;
+
+ /*
+ * See if we can already get there by a working interface. Ignore
+ * if so.
+ */
+ ifp1 = ifwithaddr(dst, _B_TRUE, _B_FALSE);
+ if (ifp1 != NULL && (ifp1->int_state & IS_PASSIVE))
+ return;
+
+ /*
+ * Look for the route in our table.
+ */
+ rt = rtget(dst, mask);
+
+ /* Consider adding the route if we do not already have it. */
+ if (rt == NULL) {
+ /* Ignore unknown routes being poisoned. */
+ if (new->rts_metric == HOPCNT_INFINITY)
+ return;
+
+ /* Ignore the route if it points to us */
+ if (n != NULL && n->n_nhop != 0 &&
+ NULL != ifwithaddr(n->n_nhop, _B_TRUE, _B_FALSE))
+ return;
+
+ /*
+ * If something has not gone crazy and tried to fill
+ * our memory, accept the new route.
+ */
+ rtadd(dst, mask, rt_state, new);
+ return;
+ }
+
+ /*
+ * We already know about the route. Consider this update.
+ *
+ * If (rt->rt_state & RS_NET_SYN), then this route
+ * is the same as a network route we have inferred
+ * for subnets we know, in order to tell RIPv1 routers
+ * about the subnets.
+ *
+ * It is impossible to tell if the route is coming
+ * from a distant RIPv2 router with the standard
+ * netmask because that router knows about the entire
+ * network, or if it is a round-about echo of a
+ * synthetic, RIPv1 network route of our own.
+ * The worst is that both kinds of routes might be
+ * received, and the bad one might have the smaller
+ * metric. Partly solve this problem by never
+ * aggregating into such a route. Also keep it
+ * around as long as the interface exists.
+ */
+
+ rts0 = rt->rt_spares;
+ trace_misc("rt 0x%lx num_spares %d", rt, rt->rt_num_spares);
+ for (rts = rts0, i = rt->rt_num_spares; i != 0; i--, rts++) {
+ if (rts->rts_router == new->rts_router)
+ break;
+ /*
+ * Note the worst slot to reuse,
+ * other than the current slot.
+ */
+ if (BETTER_LINK(rt, rts0, rts))
+ rts0 = rts;
+ }
+ if (i != 0) {
+ /*
+ * Found a route from the router already in the table.
+ */
+
+ /*
+ * If the new route is a route broken down from an
+ * aggregated route, and if the previous route is either
+ * not a broken down route or was broken down from a finer
+ * netmask, and if the previous route is current,
+ * then forget this one.
+ */
+ if (new->rts_de_ag > rts->rts_de_ag &&
+ now_stale <= rts->rts_time)
+ return;
+
+ /*
+ * Keep poisoned routes around only long enough to pass
+ * the poison on. Use a new timestamp for good routes.
+ */
+ if (rts->rts_metric == HOPCNT_INFINITY &&
+ new->rts_metric == HOPCNT_INFINITY)
+ new->rts_time = rts->rts_time;
+
+ /*
+ * If this is an update for the router we currently prefer,
+ * then note it.
+ */
+ if (i == rt->rt_num_spares) {
+ rtchange(rt, rt->rt_state | rt_state, new, 0);
+ /*
+ * If the route got worse, check for something better.
+ */
+ if (new->rts_metric != rts->rts_metric)
+ rtswitch(rt, 0);
+ return;
+ }
+
+ /*
+ * This is an update for a spare route.
+ * Finished if the route is unchanged.
+ */
+ if (rts->rts_gate == new->rts_gate &&
+ rts->rts_metric == new->rts_metric &&
+ rts->rts_tag == new->rts_tag) {
+ if ((rt->rt_dst == RIP_DEFAULT) &&
+ (rts->rts_ifp != new->rts_ifp))
+ trace_misc("input_route update for spare");
+ trace_upslot(rt, rts, new);
+ *rts = *new;
+ return;
+ }
+
+ /*
+ * Forget it if it has gone bad.
+ */
+ if (new->rts_metric == HOPCNT_INFINITY) {
+ rts_delete(rt, rts);
+ return;
+ }
+
+ } else {
+ /*
+ * The update is for a route we know about,
+ * but not from a familiar router.
+ *
+ * Ignore the route if it points to us.
+ */
+ if (n != NULL && n->n_nhop != 0 &&
+ NULL != ifwithaddr(n->n_nhop, _B_TRUE, _B_FALSE))
+ return;
+
+ /* the loop above set rts0=worst spare */
+ if (rts0->rts_metric < HOPCNT_INFINITY) {
+ ptrsize = (rt->rt_num_spares + SPARE_INC) *
+ sizeof (struct rt_spare);
+ ptr = realloc(rt->rt_spares, ptrsize);
+ if (ptr != NULL) {
+
+ rt->rt_spares = ptr;
+ rts0 = &rt->rt_spares[rt->rt_num_spares];
+ (void) memset(rts0, 0,
+ SPARE_INC * sizeof (struct rt_spare));
+ rt->rt_num_spares += SPARE_INC;
+ for (rts = rts0, i = SPARE_INC;
+ i != 0; i--, rts++)
+ rts->rts_metric = HOPCNT_INFINITY;
+ }
+ }
+ rts = rts0;
+
+ /*
+ * Save the route as a spare only if it has
+ * a better metric than our worst spare.
+ * This also ignores poisoned routes (those
+ * received with metric HOPCNT_INFINITY).
+ */
+ if (new->rts_metric >= rts->rts_metric)
+ return;
+ }
+ trace_upslot(rt, rts, new);
+ *rts = *new;
+
+ /* try to switch to a better route */
+ rtswitch(rt, rts);
+}
+
+/*
+ * Recorded information about peer's MD5 sequence numbers. This is
+ * used to validate that received sequence numbers are in
+ * non-decreasing order as per the RFC.
+ */
+struct peer_hash {
+ struct peer_hash *ph_next;
+ in_addr_t ph_addr;
+ time_t ph_heard;
+ uint32_t ph_seqno;
+};
+
+static struct peer_hash **peer_hashes;
+static int ph_index;
+static int ph_num_peers;
+
+/*
+ * Get a peer_hash structure from the hash of known peers. Create a
+ * new one if not found. Returns NULL on unrecoverable allocation
+ * failure.
+ */
+static struct peer_hash *
+get_peer_info(in_addr_t from)
+{
+ struct peer_hash *php;
+ struct peer_hash *pnhp;
+ struct peer_hash **ph_pp;
+ struct peer_hash **ph2_pp;
+ struct peer_hash **ph3_pp;
+ int i;
+ static uint_t failed_count;
+
+ if (peer_hashes == NULL) {
+ peer_hashes = calloc(hash_table_sizes[0],
+ sizeof (peer_hashes[0]));
+ if (peer_hashes == NULL) {
+ if (++failed_count % 100 == 1)
+ msglog("no memory for peer hash");
+ return (NULL);
+ }
+ }
+ /* Search for peer in existing hash table */
+ ph_pp = peer_hashes + (from % hash_table_sizes[ph_index]);
+ for (php = ph_pp[0]; php != NULL; php = php->ph_next) {
+ if (php->ph_addr == from)
+ return (php);
+ }
+ /*
+ * Not found; we need to add this peer to the table. If there
+ * are already too many peers, then try to expand the table
+ * first. It's not a big deal if we can't expand the table
+ * right now due to memory constraints. We'll try again
+ * later.
+ */
+ if (ph_num_peers >= hash_table_sizes[ph_index] * 5 &&
+ hash_table_sizes[ph_index + 1] != 0 &&
+ (ph_pp = calloc(hash_table_sizes[ph_index + 1],
+ sizeof (peer_hashes[0]))) != NULL) {
+ ph2_pp = peer_hashes;
+ for (i = hash_table_sizes[ph_index] - 1; i >= 0; i--) {
+ for (php = ph2_pp[i]; php != NULL; php = pnhp) {
+ pnhp = php->ph_next;
+ ph3_pp = ph_pp + (php->ph_addr %
+ hash_table_sizes[ph_index + 1]);
+ php->ph_next = ph3_pp[0];
+ ph3_pp[0] = php;
+ }
+ }
+ ph_index++;
+ free(peer_hashes);
+ peer_hashes = ph_pp;
+ ph_pp += from % hash_table_sizes[ph_index];
+ }
+ php = calloc(sizeof (*php), 1);
+ if (php == NULL) {
+ if (++failed_count % 100 == 1)
+ msglog("no memory for peer hash entry");
+ } else {
+ php->ph_addr = from;
+ php->ph_heard = now.tv_sec;
+ php->ph_next = ph_pp[0];
+ ph_pp[0] = php;
+ ph_num_peers++;
+ }
+ return (php);
+}
+
+/*
+ * Age out entries in the peer table. This is called every time we do
+ * a normal 30 second broadcast.
+ */
+void
+age_peer_info(void)
+{
+ struct peer_hash *php;
+ struct peer_hash *next_ph;
+ struct peer_hash *prev_ph;
+ struct peer_hash **ph_pp;
+ int i;
+
+ /*
+ * Scan through the list and remove peers that should not
+ * still have valid authenticated entries in the routing
+ * table.
+ */
+ if ((ph_pp = peer_hashes) == NULL || ph_num_peers == 0)
+ return;
+ for (i = hash_table_sizes[ph_index] - 1; i >= 0; i--) {
+ prev_ph = NULL;
+ for (php = ph_pp[i]; php != NULL; php = next_ph) {
+ next_ph = php->ph_next;
+ if (php->ph_heard <= now_expire) {
+ if (prev_ph == NULL)
+ ph_pp[i] = next_ph;
+ else
+ prev_ph->ph_next = next_ph;
+ free(php);
+ if (--ph_num_peers == 0)
+ return;
+ } else {
+ prev_ph = php;
+ }
+ }
+ }
+}
+
+static boolean_t /* _B_FALSE if bad, _B_TRUE if good */
+ck_passwd(struct interface *aifp,
+ struct rip *rip,
+ uint8_t *lim,
+ in_addr_t from,
+ struct msg_limit *use_authp)
+{
+#define NA (rip->rip_auths)
+ struct netauth *na2;
+ struct auth *ap;
+ MD5_CTX md5_ctx;
+ uchar_t hash[RIP_AUTH_PW_LEN];
+ int i, len;
+ struct peer_hash *php;
+ uint32_t seqno;
+
+ if ((uint8_t *)NA >= lim || NA->a_family != RIP_AF_AUTH) {
+ msglim(use_authp, from, "missing auth data from %s",
+ naddr_ntoa(from));
+ return (_B_FALSE);
+ }
+
+ /*
+ * Validate sequence number on RIPv2 responses using keyed MD5
+ * authentication per RFC 2082 section 3.2.2. Note that if we
+ * can't locate the peer information (due to transient
+ * allocation problems), then we don't do the test. Also note
+ * that we assume that all sequence numbers 0x80000000 or more
+ * away are "less than."
+ *
+ * We intentionally violate RFC 2082 with respect to one case:
+ * restablishing contact. The RFC says that you should
+ * continue to ignore old sequence numbers in this case but
+ * make a special allowance for 0. This is extremely foolish.
+ * The problem is that if the router has crashed, it's
+ * entirely possible that either we'll miss sequence zero (or
+ * that it might not even send it!) or that the peer doesn't
+ * remember what it last used for a sequence number. In
+ * either case, we'll create a failure state that persists
+ * until the sequence number happens to advance past the last
+ * one we saw. This is bad because it means that we may have
+ * to wait until the router has been up for at least as long
+ * as it was last time before we even pay attention to it.
+ * Meanwhile, other routers may listen to it if they hadn't
+ * seen it before (i.e., if they crashed in the meantime).
+ * This means -- perversely -- that stable systems that stay
+ * "up" for a long time pay a penalty for doing so.
+ */
+ if (rip->rip_cmd == RIPCMD_RESPONSE && NA->a_type == RIP_AUTH_MD5 &&
+ (php = get_peer_info(from)) != NULL) {
+ /*
+ * If the entry that we find has been updated
+ * recently enough that the routes are known
+ * to still be good, but the sequence number
+ * looks bad, then discard the packet.
+ */
+ seqno = ntohl(NA->au.a_md5.md5_seqno);
+ if (php->ph_heard > now_expire && php->ph_seqno != 0 &&
+ (seqno == 0 || ((seqno - php->ph_seqno) & 0x80000000ul))) {
+ msglim(use_authp, from,
+ "discarding sequence %x (older than %x)",
+ (unsigned)seqno, (unsigned)php->ph_seqno);
+ return (_B_FALSE);
+ }
+ php->ph_heard = now.tv_sec;
+ php->ph_seqno = seqno;
+ }
+
+ /*
+ * accept any current (+/- 24 hours) password
+ */
+ for (ap = aifp->int_auth, i = 0; i < MAX_AUTH_KEYS; i++, ap++) {
+ if (ap->type != NA->a_type ||
+ (ulong_t)ap->start > (ulong_t)clk.tv_sec+DAY ||
+ (ulong_t)ap->end+DAY < (ulong_t)clk.tv_sec)
+ continue;
+
+ if (NA->a_type == RIP_AUTH_PW) {
+ if (0 == memcmp(NA->au.au_pw, ap->key, RIP_AUTH_PW_LEN))
+ return (_B_TRUE);
+
+ } else {
+ /*
+ * accept MD5 secret with the right key ID
+ */
+ if (NA->au.a_md5.md5_keyid != ap->keyid)
+ continue;
+
+ len = ntohs(NA->au.a_md5.md5_pkt_len);
+ if ((len - sizeof (*rip)) % sizeof (*NA) != 0 ||
+ len > (lim - (uint8_t *)rip - sizeof (*NA))) {
+ msglim(use_authp, from,
+ "wrong MD5 RIPv2 packet length of %d"
+ " instead of %d from %s",
+ len, lim - (uint8_t *)rip - sizeof (*NA),
+ naddr_ntoa(from));
+ return (_B_FALSE);
+ }
+ na2 = (struct netauth *)(rip->rip_nets +
+ (len - 4) / sizeof (struct netinfo));
+
+ /*
+ * Given a good hash value, these are not security
+ * problems so be generous and accept the routes,
+ * after complaining.
+ */
+ if (TRACEPACKETS) {
+ if (NA->au.a_md5.md5_auth_len !=
+ RIP_AUTH_MD5_LEN)
+ msglim(use_authp, from,
+ "unknown MD5 RIPv2 auth len %#x"
+ " instead of %#x from %s",
+ NA->au.a_md5.md5_auth_len,
+ RIP_AUTH_MD5_LEN,
+ naddr_ntoa(from));
+ if (na2->a_family != RIP_AF_AUTH)
+ msglim(use_authp, from,
+ "unknown MD5 RIPv2 family %#x"
+ " instead of %#x from %s",
+ na2->a_family, RIP_AF_AUTH,
+ naddr_ntoa(from));
+ if (na2->a_type != RIP_AUTH_TRAILER)
+ msglim(use_authp, from,
+ "MD5 RIPv2 hash has %#x"
+ " instead of %#x from %s",
+ ntohs(na2->a_type),
+ ntohs(RIP_AUTH_TRAILER),
+ naddr_ntoa(from));
+ }
+
+ MD5Init(&md5_ctx);
+ /*
+ * len+4 to include auth trailer's family/type in
+ * MD5 sum
+ */
+ MD5Update(&md5_ctx, (uchar_t *)rip, len + 4);
+ MD5Update(&md5_ctx, ap->key, RIP_AUTH_MD5_LEN);
+ MD5Final(hash, &md5_ctx);
+ if (0 == memcmp(hash, na2->au.au_pw, sizeof (hash)))
+ return (_B_TRUE);
+ }
+ }
+
+ msglim(use_authp, from, "bad auth data from %s",
+ naddr_ntoa(from));
+ return (_B_FALSE);
+#undef NA
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.routed/main.c b/usr/src/cmd/cmd-inet/usr.sbin/in.routed/main.c
new file mode 100644
index 0000000000..1e89421e55
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.routed/main.c
@@ -0,0 +1,1091 @@
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 1983, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgment:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sbin/routed/main.c,v 1.14 2000/08/11 08:24:38 sheldonh Exp $
+ * char copyright[] = "@(#) Copyright (c) 1983, 1988, 1993\n"
+ * " The Regents of the University of California. All rights reserved.\n";
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "defs.h"
+#include "pathnames.h"
+#include <signal.h>
+#include <fcntl.h>
+#include <sys/file.h>
+#include <userdefs.h>
+#include <sys/stat.h>
+
+#define IN_ROUTED_VERSION "2.22"
+
+int stopint;
+boolean_t supplier; /* supply or broadcast updates */
+boolean_t supplier_set;
+/* -S option. _B_TRUE=treat all RIP speakers as default routers. */
+boolean_t save_space = _B_FALSE;
+
+static boolean_t default_gateway; /* _B_TRUE=advertise default */
+static boolean_t background = _B_TRUE;
+boolean_t ridhosts; /* _B_TRUE=reduce host routes */
+boolean_t mhome; /* _B_TRUE=want multi-homed host route */
+boolean_t advertise_mhome; /* _B_TRUE=must continue advertising it */
+boolean_t auth_ok = _B_TRUE; /* _B_TRUE=ignore auth if we don't care */
+boolean_t no_install; /* _B_TRUE=don't install in kernel */
+
+struct timeval epoch; /* when started */
+struct timeval clk;
+static struct timeval prev_clk;
+static int usec_fudge;
+struct timeval now; /* current idea of time */
+/* If a route's rts_time is <= to now_stale, the route is stale. */
+time_t now_stale;
+/* If a route's rts_time is <= to now_expire, the route is expired */
+time_t now_expire;
+/* If a route's rts_time is <= to now_garbage, the route needs to be deleted */
+time_t now_garbage;
+
+static struct timeval next_bcast; /* next general broadcast */
+struct timeval no_flash = { /* inhibit flash update */
+ EPOCH+SUPPLY_INTERVAL, 0
+};
+
+/* When now reaches this time, it's time to call sync_kern() */
+static struct timeval sync_kern_timer;
+
+static fd_set fdbits;
+static int sock_max;
+int rip_sock = -1; /* RIP socket */
+boolean_t rip_enabled;
+static boolean_t openlog_done;
+
+/*
+ * The interface to which rip_sock is currently pointing for
+ * output.
+ */
+struct interface *rip_sock_interface;
+
+int rt_sock; /* routing socket */
+
+
+static int open_rip_sock();
+static void timevalsub(struct timeval *, struct timeval *, struct timeval *);
+static void sigalrm(int);
+static void sigterm(int);
+
+static int
+daemon(boolean_t nochdir, boolean_t noclose)
+{
+ int retv;
+
+ if ((retv = fork()) == -1)
+ return (-1);
+ if (retv != 0)
+ _exit(EXIT_SUCCESS);
+ if (setsid() == -1)
+ return (-1);
+ if ((retv = fork()) == -1)
+ return (-1);
+ if (retv != 0)
+ _exit(EXIT_SUCCESS);
+ if (!nochdir && chdir("/") == -1)
+ return (-1);
+ if (!noclose) {
+ (void) close(0);
+ (void) close(1);
+ (void) close(2);
+ if ((retv = open("/dev/null", O_RDWR)) != -1) {
+ (void) dup2(retv, 1);
+ (void) dup2(retv, 2);
+ }
+ }
+ return (0);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int n, off;
+ char *p, *q;
+ const char *cp;
+ struct timeval select_timeout, result;
+ fd_set ibits;
+ in_addr_t p_net, p_mask;
+ struct parm parm;
+ char *tracename = NULL;
+ boolean_t vflag = _B_FALSE;
+ boolean_t version = _B_FALSE;
+ int sigerr = 0;
+ FILE *pidfp;
+ mode_t pidmode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); /* 0644 */
+
+ (void) setlocale(LC_ALL, "");
+
+#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
+#define TEXT_DOMAIN "SYS_TEXT"
+#endif /* ! TEXT_DOMAIN */
+
+ (void) textdomain(TEXT_DOMAIN);
+
+ /*
+ * Some shells are badly broken and send SIGHUP to backgrounded
+ * processes.
+ */
+ if (signal(SIGHUP, SIG_IGN) == SIG_ERR)
+ sigerr = errno;
+
+ ftrace = stdout;
+
+ if (gettimeofday(&clk, 0) == -1) {
+ logbad(_B_FALSE, "gettimeofday: %s", rip_strerror(errno));
+ }
+ prev_clk = clk;
+ epoch = clk;
+ epoch.tv_sec -= EPOCH;
+ now.tv_sec = EPOCH;
+ now_stale = EPOCH - STALE_TIME;
+ now_expire = EPOCH - EXPIRE_TIME;
+ now_garbage = EPOCH - GARBAGE_TIME;
+ select_timeout.tv_sec = 0;
+
+ while ((n = getopt(argc, argv, "sSqdghmpAztVvnT:F:P:")) != -1) {
+ switch (n) {
+ case 'A':
+ /*
+ * Ignore authentication if we do not care.
+ * Crazy as it is, that is what RFC 2453 requires.
+ */
+ auth_ok = _B_FALSE;
+ break;
+
+ case 't':
+ if (new_tracelevel < 2)
+ new_tracelevel = 2;
+ background = _B_FALSE;
+ break;
+
+ case 'd': /* put in.routed in foreground */
+ background = _B_FALSE;
+ break;
+
+ case 'F': /* minimal routes for SLIP */
+ n = FAKE_METRIC;
+ p = strchr(optarg, ',');
+ if (p != NULL) {
+ n = (int)strtoul(p+1, &q, 0);
+ if (*q == '\0' && p+1 != q &&
+ n <= HOPCNT_INFINITY-1 && n >= 1)
+ *p = '\0';
+ }
+ if (!getnet(optarg, &p_net, &p_mask)) {
+ if (p != NULL)
+ *p = ',';
+ msglog(gettext("bad network; \"-F %s\""),
+ optarg);
+ break;
+ }
+ (void) memset(&parm, 0, sizeof (parm));
+ parm.parm_net = p_net;
+ parm.parm_mask = p_mask;
+ parm.parm_d_metric = n;
+ cp = insert_parm(&parm);
+ if (cp != NULL)
+ msglog(gettext("bad -F: %s"), cp);
+ break;
+
+ case 'g':
+ (void) memset(&parm, 0, sizeof (parm));
+ parm.parm_d_metric = 1;
+ cp = insert_parm(&parm);
+ if (cp != NULL)
+ msglog(gettext("bad -g: %s"), cp);
+ else
+ default_gateway = _B_TRUE;
+ break;
+
+ case 'h': /* suppress extra host routes */
+ ridhosts = _B_TRUE;
+ break;
+
+ case 'm': /* advertise host route */
+ mhome = _B_TRUE; /* on multi-homed hosts */
+ break;
+
+ case 'n': /* No-install mode */
+ no_install = _B_TRUE;
+ break;
+
+ case 'P':
+ /* handle arbitrary parameters. */
+ q = strdup(optarg);
+ if (q == NULL)
+ logbad(_B_FALSE, "strdup: %s",
+ rip_strerror(errno));
+ cp = parse_parms(q, _B_FALSE);
+ if (cp != NULL)
+ msglog(gettext("%1$s in \"-P %2$s\""), cp,
+ optarg);
+ free(q);
+ break;
+
+ case 'q':
+ supplier = _B_FALSE;
+ supplier_set = _B_TRUE;
+ break;
+
+ case 's':
+ supplier = _B_TRUE;
+ supplier_set = _B_TRUE;
+ break;
+
+ case 'S': /* save-space option */
+ save_space = _B_TRUE;
+ break;
+
+ case 'T':
+ tracename = optarg;
+ break;
+
+ case 'V':
+ /* display version */
+ version = _B_TRUE;
+ msglog(gettext("version " IN_ROUTED_VERSION));
+ break;
+
+ case 'v':
+ /* display route changes to supplied logfile */
+ new_tracelevel = 1;
+ vflag = _B_TRUE;
+ break;
+
+ case 'z': /* increase debug-level */
+ new_tracelevel++;
+ break;
+
+ default:
+ goto usage;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (tracename == NULL && argc >= 1) {
+ tracename = *argv++;
+ argc--;
+ }
+ if (tracename != NULL && tracename[0] == '\0')
+ goto usage;
+ if (vflag && tracename == NULL)
+ goto usage;
+ if (argc != 0) {
+usage:
+ (void) fprintf(stderr,
+ gettext("usage: in.routed [-AdghmnqsStVvz] "
+ "[-T <tracefile>]\n"));
+ (void) fprintf(stderr,
+ gettext("\t[-F <net>[/<mask>][,<metric>]] [-P <parms>]\n"));
+ logbad(_B_FALSE, gettext("excess arguments"));
+ }
+ if (geteuid() != 0) {
+ /*
+ * Regular users are allowed to run in.routed for the
+ * sole purpose of obtaining the version number. In
+ * that case, exit(EXIT_SUCCESS) without complaining.
+ */
+ if (version)
+ exit(EXIT_SUCCESS);
+ logbad(_B_FALSE, gettext("requires UID 0"));
+ }
+
+ if (default_gateway) {
+ if (supplier_set && !supplier) {
+ msglog(gettext("-g and -q are incompatible"));
+ } else {
+ supplier = _B_TRUE;
+ supplier_set = _B_TRUE;
+ }
+ }
+
+ if (signal(SIGALRM, sigalrm) == SIG_ERR)
+ sigerr = errno;
+ /* SIGHUP fatal during debugging */
+ if (!background)
+ if (signal(SIGHUP, sigterm) == SIG_ERR)
+ sigerr = errno;
+ if (signal(SIGTERM, sigterm) == SIG_ERR)
+ sigerr = errno;
+ if (signal(SIGINT, sigterm) == SIG_ERR)
+ sigerr = errno;
+ if (signal(SIGUSR1, sigtrace_more) == SIG_ERR)
+ sigerr = errno;
+ if (signal(SIGUSR2, sigtrace_less) == SIG_ERR)
+ sigerr = errno;
+ if (signal(SIGHUP, sigtrace_dump) == SIG_ERR)
+ sigerr = errno;
+
+ if (sigerr)
+ msglog("signal: %s", rip_strerror(sigerr));
+
+ /* get into the background */
+ if (background && daemon(_B_FALSE, _B_FALSE) < 0)
+ BADERR(_B_FALSE, "daemon()");
+
+ /* Store our process id, blow away any existing file if it exists. */
+ if ((pidfp = fopen(PATH_PID, "w")) == NULL) {
+ (void) fprintf(stderr,
+ gettext("in.routed: unable to open " PATH_PID ": %s\n"),
+ strerror(errno));
+ } else {
+ (void) fprintf(pidfp, "%ld\n", getpid());
+ (void) fclose(pidfp);
+ (void) chmod(PATH_PID, pidmode);
+ }
+
+ srandom((int)(clk.tv_sec ^ clk.tv_usec ^ getpid()));
+
+ /* allocate the interface tables */
+ iftbl_alloc();
+
+ /* prepare socket connected to the kernel. */
+ rt_sock = socket(PF_ROUTE, SOCK_RAW, AF_INET);
+ if (rt_sock < 0)
+ BADERR(_B_TRUE, "rt_sock = socket()");
+ if (fcntl(rt_sock, F_SETFL, O_NONBLOCK) == -1)
+ logbad(_B_TRUE, "fcntl(rt_sock) O_NONBLOCK: %s",
+ rip_strerror(errno));
+ off = 0;
+ if (setsockopt(rt_sock, SOL_SOCKET, SO_USELOOPBACK,
+ &off, sizeof (off)) < 0)
+ LOGERR("setsockopt(SO_USELOOPBACK,0)");
+
+ fix_select();
+
+
+ if (tracename != NULL) {
+ (void) strlcpy(inittracename, tracename,
+ sizeof (inittracename));
+ set_tracefile(inittracename, "%s", -1);
+ } else {
+ tracelevel_msg("%s", -1); /* turn on tracing to stdio */
+ }
+
+ bufinit();
+
+ /* initialize radix tree */
+ rtinit();
+
+ /*
+ * Pick a random part of the second for our output to minimize
+ * collisions.
+ *
+ * Start broadcasting after hearing from other routers, and
+ * at a random time so a bunch of systems do not get synchronized
+ * after a power failure.
+ *
+ * Since now is the number of seconds since epoch (this is initially
+ * EPOCH seconds), these times are really relative to now.
+ */
+ intvl_random(&next_bcast, EPOCH+MIN_WAITTIME, EPOCH+SUPPLY_INTERVAL);
+ age_timer.tv_usec = next_bcast.tv_usec;
+ age_timer.tv_sec = EPOCH+MIN_WAITTIME;
+ rdisc_timer = next_bcast;
+ ifscan_timer.tv_usec = next_bcast.tv_usec;
+
+ /*
+ * Open the global rip socket. From now on, this socket can be
+ * assumed to be open. It will remain open until in.routed
+ * exits.
+ */
+ rip_sock = open_rip_sock();
+
+ /*
+ * Collect an initial view of the world by checking the interface
+ * configuration and the kludge file.
+ *
+ * gwkludge() could call addroutefordefault(), resulting in a call to
+ * iflookup, and thus ifscan() to find the physical interfaces.
+ * ifscan() will attempt to use the rip_sock in order to join
+ * mcast groups, so gwkludge *must* be called after opening
+ * the rip_sock.
+ */
+ gwkludge();
+
+ ifscan();
+
+ /* Ask for routes */
+ rip_query();
+ rdisc_sol();
+
+ /* Now turn off stdio if not tracing */
+ if (new_tracelevel == 0)
+ trace_close(background);
+
+ /* Loop until a fatal error occurs, listening and broadcasting. */
+ for (;;) {
+ prev_clk = clk;
+ if (gettimeofday(&clk, 0) == -1) {
+ logbad(_B_FALSE, "gettimeofday: %s",
+ rip_strerror(errno));
+ }
+ if (prev_clk.tv_sec == clk.tv_sec &&
+ prev_clk.tv_usec == clk.tv_usec+usec_fudge) {
+ /*
+ * Much of `in.routed` depends on time always advancing.
+ * On systems that do not guarantee that gettimeofday()
+ * produces unique timestamps even if called within
+ * a single tick, use trickery like that in classic
+ * BSD kernels.
+ */
+ clk.tv_usec += ++usec_fudge;
+
+ } else {
+ time_t dt;
+
+ usec_fudge = 0;
+
+ timevalsub(&result, &clk, &prev_clk);
+ if (result.tv_sec < 0 || result.tv_sec >
+ select_timeout.tv_sec + 5) {
+ /*
+ * Deal with time changes before other
+ * housekeeping to keep everything straight.
+ */
+ dt = result.tv_sec;
+ if (dt > 0)
+ dt -= select_timeout.tv_sec;
+ trace_act("time changed by %d sec", (int)dt);
+ epoch.tv_sec += dt;
+ }
+ }
+ timevalsub(&now, &clk, &epoch);
+ now_stale = now.tv_sec - STALE_TIME;
+ now_expire = now.tv_sec - EXPIRE_TIME;
+ now_garbage = now.tv_sec - GARBAGE_TIME;
+
+ /* deal with signals that should affect tracing */
+ set_tracelevel();
+
+ if (stopint != 0) {
+ trace_off("exiting with signal %d", stopint);
+ break;
+ }
+
+ /* look for new or dead interfaces */
+ timevalsub(&select_timeout, &ifscan_timer, &now);
+ if (select_timeout.tv_sec <= 0) {
+ select_timeout.tv_sec = 0;
+ ifscan();
+ rip_query();
+ continue;
+ }
+
+ /*
+ * Check the kernel table occassionally for mysteriously
+ * evaporated routes
+ */
+ timevalsub(&result, &sync_kern_timer, &now);
+ if (result.tv_sec <= 0) {
+ sync_kern();
+ sync_kern_timer.tv_sec = (now.tv_sec
+ + CHECK_QUIET_INTERVAL);
+ continue;
+ }
+ if (timercmp(&result, &select_timeout, < /* */))
+ select_timeout = result;
+
+ /* If it is time, then broadcast our routes. */
+ if (should_supply(NULL) || advertise_mhome) {
+ timevalsub(&result, &next_bcast, &now);
+ if (result.tv_sec <= 0) {
+ /*
+ * Synchronize the aging and broadcast
+ * timers to minimize awakenings
+ */
+ age(0);
+ age_peer_info();
+
+ rip_bcast(0);
+
+ /*
+ * It is desirable to send routing updates
+ * regularly. So schedule the next update
+ * 30 seconds after the previous one was
+ * scheduled, instead of 30 seconds after
+ * the previous update was finished.
+ * Even if we just started after discovering
+ * a 2nd interface or were otherwise delayed,
+ * pick a 30-second aniversary of the
+ * original broadcast time.
+ */
+ n = 1 + (0-result.tv_sec)/SUPPLY_INTERVAL;
+ next_bcast.tv_sec += n*SUPPLY_INTERVAL;
+
+ continue;
+ }
+
+ if (timercmp(&result, &select_timeout, < /* */))
+ select_timeout = result;
+ }
+
+ /*
+ * If we need a flash update, either do it now or
+ * set the delay to end when it is time.
+ *
+ * If we are within MIN_WAITTIME seconds of a full update,
+ * do not bother.
+ */
+ if (need_flash && should_supply(NULL) &&
+ no_flash.tv_sec+MIN_WAITTIME < next_bcast.tv_sec) {
+ /* accurate to the millisecond */
+ if (!timercmp(&no_flash, &now, > /* */))
+ rip_bcast(1);
+ timevalsub(&result, &no_flash, &now);
+ if (timercmp(&result, &select_timeout, < /* */))
+ select_timeout = result;
+ }
+
+ /* trigger the main aging timer. */
+ timevalsub(&result, &age_timer, &now);
+ if (result.tv_sec <= 0) {
+ age(0);
+ continue;
+ }
+ if (timercmp(&result, &select_timeout, < /* */))
+ select_timeout = result;
+
+ /* update the kernel routing table */
+ timevalsub(&result, &need_kern, &now);
+ if (result.tv_sec <= 0) {
+ age(0);
+ continue;
+ }
+ if (timercmp(&result, &select_timeout, < /* */))
+ select_timeout = result;
+
+ /*
+ * take care of router discovery. We compare timeval
+ * structures here to have millisecond granularity.
+ */
+ if (!timercmp(&rdisc_timer, &now, > /* */)) {
+ rdisc_age(0);
+ continue;
+ }
+ timevalsub(&result, &rdisc_timer, &now);
+ if (timercmp(&result, &select_timeout, < /* */))
+ select_timeout = result;
+
+ /*
+ * Well-known bit of select(3c) silliness inherited
+ * from BSD: anything over 100 million seconds is
+ * considered an "error." Reset that to zero.
+ */
+ if (select_timeout.tv_sec > 100000000)
+ select_timeout.tv_sec = 0;
+
+ /* wait for input or a timer to expire. */
+ trace_flush();
+ ibits = fdbits;
+ n = select(sock_max, &ibits, 0, 0, &select_timeout);
+ if (n <= 0) {
+ if (n < 0 && errno != EINTR && errno != EAGAIN)
+ BADERR(_B_TRUE, "select");
+ continue;
+ }
+
+ if (FD_ISSET(rt_sock, &ibits)) {
+ read_rt();
+ n--;
+ }
+ if (rdisc_sock >= 0 && FD_ISSET(rdisc_sock, &ibits)) {
+ read_d();
+ n--;
+ }
+ if (rip_sock >= 0 && FD_ISSET(rip_sock, &ibits)) {
+ if (read_rip() == -1) {
+ rip_enabled = _B_FALSE;
+ trace_off("main rip socket failed");
+ (void) close(rip_sock);
+ rip_sock = -1;
+ fix_select();
+ break;
+ }
+ n--;
+ }
+ }
+ rip_bcast(0);
+ rdisc_adv(_B_FALSE);
+ (void) unlink(PATH_PID);
+ return (stopint | 128);
+}
+
+
+static void
+sigalrm(int sig)
+{
+ /*
+ * Historically, SIGALRM would cause the daemon to check for
+ * new and broken interfaces.
+ */
+ ifscan_timer.tv_sec = now.tv_sec;
+ trace_act("SIGALRM");
+ if (signal(sig, sigalrm) == SIG_ERR)
+ msglog("signal: %s", rip_strerror(errno));
+}
+
+
+/* watch for fatal signals */
+static void
+sigterm(int sig)
+{
+ stopint = sig;
+ if (signal(sig, SIG_DFL) == SIG_ERR) /* catch it only once */
+ msglog("signal: %s", rip_strerror(errno));
+}
+
+
+void
+fix_select(void)
+{
+ (void) FD_ZERO(&fdbits);
+ sock_max = 0;
+
+ FD_SET(rt_sock, &fdbits);
+ if (sock_max <= rt_sock)
+ sock_max = rt_sock+1;
+ if (rip_sock >= 0) {
+ FD_SET(rip_sock, &fdbits);
+ if (sock_max <= rip_sock)
+ sock_max = rip_sock+1;
+ }
+ if (rdisc_sock >= 0) {
+ FD_SET(rdisc_sock, &fdbits);
+ if (sock_max <= rdisc_sock)
+ sock_max = rdisc_sock+1;
+ }
+}
+
+
+void
+fix_sock(int sock,
+ const char *name)
+{
+ int on;
+#define MIN_SOCKBUF (4*1024)
+ static int rbuf;
+
+ if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1)
+ logbad(_B_TRUE, "fcntl(%s) O_NONBLOCK: %s", name,
+ rip_strerror(errno));
+ on = 1;
+ if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &on, sizeof (on)) < 0)
+ msglog("setsockopt(%s,SO_BROADCAST): %s",
+ name, rip_strerror(errno));
+
+ if (rbuf >= MIN_SOCKBUF) {
+ if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF,
+ &rbuf, sizeof (rbuf)) < 0)
+ msglog("setsockopt(%s,SO_RCVBUF=%d): %s",
+ name, rbuf, rip_strerror(errno));
+ } else {
+ for (rbuf = 60*1024; ; rbuf -= 4096) {
+ if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF,
+ &rbuf, sizeof (rbuf)) == 0) {
+ trace_act("RCVBUF=%d", rbuf);
+ break;
+ }
+ if (rbuf < MIN_SOCKBUF) {
+ msglog("setsockopt(%s,SO_RCVBUF = %d): %s",
+ name, rbuf, rip_strerror(errno));
+ break;
+ }
+ }
+ }
+}
+
+
+/*
+ * Open and return the global rip socket. It is guaranteed to return
+ * a good file descriptor.
+ */
+static int
+open_rip_sock()
+{
+ struct sockaddr_in sin;
+ unsigned char ttl;
+ int s;
+ int on = 1;
+
+
+ if ((s = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
+ BADERR(_B_TRUE, "rip_sock = socket()");
+
+ (void) memset(&sin, 0, sizeof (sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(RIP_PORT);
+ sin.sin_addr.s_addr = INADDR_ANY;
+ if (bind(s, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
+ BADERR(_B_FALSE, "bind(rip_sock)");
+ }
+ fix_sock(s, "rip_sock");
+
+ ttl = 1;
+ if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL,
+ &ttl, sizeof (ttl)) < 0)
+ DBGERR(_B_TRUE, "rip_sock setsockopt(IP_MULTICAST_TTL)");
+
+ if (setsockopt(s, IPPROTO_IP, IP_RECVIF, &on, sizeof (on)))
+ BADERR(_B_FALSE, "setsockopt(IP_RECVIF)");
+
+ return (s);
+}
+
+
+/*
+ * Disable RIP. Note that we don't close the global rip socket since
+ * it is used even when RIP is disabled to receive and answer certain
+ * queries.
+ */
+void
+rip_off(void)
+{
+ struct ip_mreq m;
+ struct interface *ifp;
+ char addrstr[INET_ADDRSTRLEN];
+
+ if (rip_enabled && !mhome) {
+ trace_act("turn off RIP");
+
+ /*
+ * Unsubscribe from the 224.0.0.9 RIP multicast
+ * group address
+ */
+ for (ifp = ifnet; ifp != NULL; ifp = ifp->int_next) {
+ if ((ifp->int_if_flags & IFF_MULTICAST) &&
+ !IS_IFF_QUIET(ifp->int_if_flags) &&
+ !IS_RIP_IN_OFF(ifp->int_state) &&
+ !(ifp->int_state & IS_DUP)) {
+ m.imr_multiaddr.s_addr =
+ htonl(INADDR_RIP_GROUP);
+ m.imr_interface.s_addr =
+ (ifp->int_if_flags & IFF_POINTOPOINT) ?
+ ifp->int_dstaddr : ifp->int_addr;
+ (void) strlcpy(addrstr,
+ inet_ntoa(m.imr_multiaddr),
+ sizeof (addrstr));
+ if (setsockopt(rip_sock, IPPROTO_IP,
+ IP_DROP_MEMBERSHIP, &m,
+ sizeof (m)) < 0 &&
+ errno != EADDRNOTAVAIL && errno != ENOENT)
+ writelog(LOG_WARNING,
+ "%s: setsockopt(IP_DROP_MEMBERSHIP "
+ "%s, %s): %s", ifp->int_name,
+ addrstr, inet_ntoa(m.imr_interface),
+ rip_strerror(errno));
+ }
+ }
+ rip_enabled = _B_FALSE;
+
+ age(0);
+ }
+}
+
+
+/* turn on RIP multicast input via an interface */
+void
+rip_mcast_on(struct interface *ifp)
+{
+ struct ip_mreq m;
+
+ if (!IS_RIP_IN_OFF(ifp->int_state) &&
+ (ifp->int_if_flags & IFF_MULTICAST) &&
+ !IS_IFF_QUIET(ifp->int_if_flags) &&
+ !(ifp->int_state & IS_DUP)) {
+ m.imr_multiaddr.s_addr = htonl(INADDR_RIP_GROUP);
+ m.imr_interface.s_addr = (ifp->int_if_flags & IFF_POINTOPOINT) ?
+ ifp->int_dstaddr : ifp->int_addr;
+ if ((setsockopt(rip_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ &m, sizeof (m)) < 0) && !(ifp->int_state & IS_BROKE))
+ writelog(LOG_WARNING,
+ "Could not join 224.0.0.9 on interface %s: %s",
+ ifp->int_name, rip_strerror(errno));
+ }
+}
+
+/* turn off RIP multicast input via an interface */
+void
+rip_mcast_off(struct interface *ifp)
+{
+ struct ip_mreq m;
+
+ if ((ifp->int_if_flags & IFF_MULTICAST) &&
+ !IS_IFF_QUIET(ifp->int_if_flags) && rip_enabled) {
+ m.imr_multiaddr.s_addr = htonl(INADDR_RIP_GROUP);
+ m.imr_interface.s_addr = (ifp->int_if_flags & IFF_POINTOPOINT) ?
+ ifp->int_dstaddr : ifp->int_addr;
+ if ((setsockopt(rip_sock, IPPROTO_IP, IP_DROP_MEMBERSHIP,
+ &m, sizeof (m)) < 0) && errno != EADDRNOTAVAIL &&
+ errno != ENOENT)
+ writelog(LOG_WARNING,
+ "setsockopt(IP_DROP_MEMBERSHIP RIP) for %s: %s",
+ ifp->int_name, rip_strerror(errno));
+ }
+}
+
+/* enable RIP */
+void
+rip_on(struct interface *ifp)
+{
+ /*
+ * If RIP is already enabled, only start receiving
+ * multicasts for this interface.
+ */
+ if (rip_enabled) {
+ if (ifp != NULL)
+ rip_mcast_on(ifp);
+ return;
+ }
+
+ /*
+ * If RIP is disabled and it makes sense to enable it, then enable
+ * it on all of the interfaces. It makes sense if either router
+ * discovery is off, or if router discovery is on and at most one
+ * interface is doing RIP.
+ */
+ if (rip_interfaces > 0 && (!rdisc_ok || rip_interfaces > 1)) {
+ trace_act("turn on RIP");
+
+ rip_enabled = _B_TRUE;
+ rip_sock_interface = NULL;
+
+ /* Do not advertise anything until we have heard something */
+ if (next_bcast.tv_sec < now.tv_sec+MIN_WAITTIME)
+ next_bcast.tv_sec = now.tv_sec+MIN_WAITTIME;
+
+ for (ifp = ifnet; ifp != NULL; ifp = ifp->int_next) {
+ ifp->int_query_time = NEVER;
+ rip_mcast_on(ifp);
+ }
+ ifscan_timer.tv_sec = now.tv_sec;
+ }
+
+ fix_select();
+}
+
+
+/* die if malloc(3) fails */
+void *
+rtmalloc(size_t size,
+ const char *msg)
+{
+ void *p = malloc(size);
+ if (p == NULL)
+ logbad(_B_TRUE, "malloc(%lu) failed in %s: %s", (ulong_t)size,
+ msg, rip_strerror(errno));
+ return (p);
+}
+
+
+/* get a random instant in an interval */
+void
+intvl_random(struct timeval *tp, /* put value here */
+ ulong_t lo, /* value is after this second */
+ ulong_t hi) /* and before this */
+{
+ tp->tv_sec = (time_t)(hi == lo ? lo : (lo + random() % ((hi - lo))));
+ tp->tv_usec = random() % 1000000;
+}
+
+
+void
+timevaladd(struct timeval *t1,
+ struct timeval *t2)
+{
+
+ t1->tv_sec += t2->tv_sec;
+ if ((t1->tv_usec += t2->tv_usec) >= 1000000) {
+ t1->tv_sec++;
+ t1->tv_usec -= 1000000;
+ }
+}
+
+
+/* t1 = t2 - t3 */
+static void
+timevalsub(struct timeval *t1,
+ struct timeval *t2,
+ struct timeval *t3)
+{
+ t1->tv_sec = t2->tv_sec - t3->tv_sec;
+ if ((t1->tv_usec = t2->tv_usec - t3->tv_usec) < 0) {
+ t1->tv_sec--;
+ t1->tv_usec += 1000000;
+ }
+}
+
+static void
+do_openlog(void)
+{
+ openlog_done = _B_TRUE;
+ openlog("in.routed", LOG_PID | LOG_ODELAY, LOG_DAEMON);
+}
+
+/* put a LOG_ERR message into the system log */
+void
+msglog(const char *p, ...)
+{
+ va_list args;
+
+ trace_flush();
+
+ if (!openlog_done)
+ do_openlog();
+ va_start(args, p);
+ vsyslog(LOG_ERR, p, args);
+
+ if (ftrace != 0) {
+ if (ftrace == stdout)
+ (void) fputs("in.routed: ", ftrace);
+ (void) vfprintf(ftrace, p, args);
+ (void) fputc('\n', ftrace);
+ }
+}
+
+
+/*
+ * Put a message about a bad system into the system log if
+ * we have not complained about it recently.
+ *
+ * It is desirable to complain about all bad systems, but not too often.
+ * In the worst case, it is not practical to keep track of all bad systems.
+ * For example, there can be many systems with the wrong password.
+ */
+void
+msglim(struct msg_limit *lim, in_addr_t addr, const char *p, ...)
+{
+ va_list args;
+ int i;
+ struct msg_sub *ms1, *ms;
+ const char *p1;
+
+ va_start(args, p);
+
+ /*
+ * look for the oldest slot in the table
+ * or the slot for the bad router.
+ */
+ ms = ms1 = lim->subs;
+ for (i = MSG_SUBJECT_N; ; i--, ms1++) {
+ if (i == 0) {
+ /* Reuse a slot at most once every 10 minutes. */
+ if (lim->reuse > now.tv_sec) {
+ ms = NULL;
+ } else {
+ lim->reuse = now.tv_sec + 10*60;
+ }
+ break;
+ }
+ if (ms->addr == addr) {
+ /*
+ * Repeat a complaint about a given system at
+ * most once an hour.
+ */
+ if (ms->until > now.tv_sec)
+ ms = NULL;
+ break;
+ }
+ if (ms->until < ms1->until)
+ ms = ms1;
+ }
+ if (ms != NULL) {
+ ms->addr = addr;
+ ms->until = now.tv_sec + 60*60; /* 60 minutes */
+
+ if (!openlog_done)
+ do_openlog();
+ trace_flush();
+ for (p1 = p; *p1 == ' '; p1++)
+ continue;
+ vsyslog(LOG_ERR, p1, args);
+ }
+
+ /* always display the message if tracing */
+ if (ftrace != 0) {
+ (void) vfprintf(ftrace, p, args);
+ (void) fputc('\n', ftrace);
+ }
+}
+
+
+void
+logbad(boolean_t dump, const char *p, ...)
+{
+ va_list args;
+
+ trace_flush();
+
+ if (!openlog_done)
+ do_openlog();
+ va_start(args, p);
+ vsyslog(LOG_ERR, p, args);
+
+ (void) fputs(gettext("in.routed: "), stderr);
+ (void) vfprintf(stderr, p, args);
+ (void) fputs(gettext("; giving up\n"), stderr);
+ (void) fflush(stderr);
+
+ if (dump)
+ abort();
+ exit(EXIT_FAILURE);
+}
+
+/* put a message into the system log */
+void
+writelog(int level, const char *p, ...)
+{
+ va_list args;
+
+ trace_flush();
+
+ if (!openlog_done)
+ do_openlog();
+ va_start(args, p);
+ vsyslog(level, p, args);
+
+ if (ftrace != 0) {
+ if (ftrace == stdout)
+ (void) fputs("in.routed: ", ftrace);
+ (void) vfprintf(ftrace, p, args);
+ (void) fputc('\n', ftrace);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.routed/output.c b/usr/src/cmd/cmd-inet/usr.sbin/in.routed/output.c
new file mode 100644
index 0000000000..ae212db11b
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.routed/output.c
@@ -0,0 +1,1019 @@
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 1983, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgment:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sbin/routed/output.c,v 1.7 2000/08/11 08:24:38 sheldonh Exp $
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "defs.h"
+#include <md5.h>
+
+uint_t update_seqno;
+
+
+/*
+ * walk the tree of routes with this for output
+ */
+static struct {
+ struct sockaddr_in to;
+ in_addr_t to_mask;
+ in_addr_t to_net;
+ in_addr_t to_std_mask;
+ in_addr_t to_std_net;
+ struct interface *ifp; /* usually output interface */
+ struct auth *a;
+ uint8_t metric; /* adjust metrics by interface */
+ uint32_t npackets;
+ uint32_t gen_limit;
+#define WS_GEN_LIMIT_MAX 1024
+ uint16_t state;
+#define WS_ST_FLASH 0x001 /* send only changed routes */
+#define WS_ST_RIP2_ALL 0x002 /* send full featured RIPv2 */
+#define WS_ST_AG 0x004 /* ok to aggregate subnets */
+#define WS_ST_SUPER_AG 0x008 /* ok to aggregate networks */
+#define WS_ST_QUERY 0x010 /* responding to a query */
+#define WS_ST_TO_ON_NET 0x020 /* sending onto one of our nets */
+#define WS_ST_DEFAULT 0x040 /* faking a default */
+} ws;
+
+/* A buffer for what can be heard by both RIPv1 and RIPv2 listeners */
+struct ws_buf v12buf;
+static union pkt_buf ripv12_buf;
+
+/* Another for only RIPv2 listeners */
+static struct ws_buf v2buf;
+static union pkt_buf rip_v2_buf;
+
+
+
+void
+bufinit(void)
+{
+ ripv12_buf.rip.rip_cmd = RIPCMD_RESPONSE;
+ v12buf.buf = &ripv12_buf.rip;
+ v12buf.base = &v12buf.buf->rip_nets[0];
+
+ rip_v2_buf.rip.rip_cmd = RIPCMD_RESPONSE;
+ rip_v2_buf.rip.rip_vers = RIPv2;
+ v2buf.buf = &rip_v2_buf.rip;
+ v2buf.base = &v2buf.buf->rip_nets[0];
+}
+
+
+/*
+ * Send the contents of the global buffer via the non-multicast socket
+ */
+int /* <0 on failure */
+output(enum output_type type,
+ struct sockaddr_in *dst, /* send to here */
+ struct interface *ifp,
+ struct rip *buf,
+ int size) /* this many bytes */
+{
+ struct sockaddr_in sin;
+ int flags;
+ const char *msg;
+ int res;
+ int ifindex;
+ struct in_addr addr;
+ static int rip_sock_ifindex;
+
+ sin = *dst;
+ if (sin.sin_port == 0)
+ sin.sin_port = htons(RIP_PORT);
+
+ flags = 0;
+
+ if (ifp == NULL && type == OUT_MULTICAST) {
+ msglog("Cannot send RIP message to %s",
+ inet_ntoa(sin.sin_addr));
+ return (-1);
+ }
+
+ switch (type) {
+ case OUT_QUERY:
+ msg = "Answer Query";
+ break;
+ case OUT_UNICAST:
+ msg = "Send";
+ flags = MSG_DONTROUTE;
+ break;
+ case OUT_BROADCAST:
+ msg = "Send bcast";
+ break;
+ case OUT_MULTICAST:
+ msg = "Send mcast";
+ break;
+
+ case NO_OUT_MULTICAST:
+ case NO_OUT_RIPV2:
+ default:
+#ifdef DEBUG
+ abort();
+#endif
+ return (-1);
+ }
+
+ /*
+ * Note that we intentionally reset IP_XMIT_IF to zero if
+ * we're doing multicast. The kernel ignores IP_MULTICAST_IF
+ * if IP_XMIT_IF is set, and we can't deal with alias source
+ * addresses without it.
+ */
+ ifindex = (type != OUT_MULTICAST && type != OUT_QUERY &&
+ ifp != NULL && ifp->int_phys != NULL) ?
+ ifp->int_phys->phyi_index : 0;
+ if (rip_sock_ifindex != ifindex) {
+ if (setsockopt(rip_sock, IPPROTO_IP, IP_XMIT_IF, &ifindex,
+ sizeof (ifindex)) == -1) {
+ LOGERR("setsockopt(rip_sock, IP_XMIT_IF)");
+ return (-1);
+ }
+ rip_sock_ifindex = ifindex;
+ }
+
+ if (rip_sock_interface != ifp) {
+ /*
+ * For multicast, we have to choose the source
+ * address. This is either the local address
+ * (non-point-to-point) or the remote address.
+ */
+ if (ifp != NULL) {
+ addr.s_addr = (ifp->int_if_flags & IFF_POINTOPOINT) ?
+ ifp->int_dstaddr : ifp->int_addr;
+ if (type == OUT_MULTICAST &&
+ setsockopt(rip_sock, IPPROTO_IP,
+ IP_MULTICAST_IF, &addr, sizeof (addr)) == -1) {
+ LOGERR("setsockopt(rip_sock, IP_MULTICAST_IF)");
+ return (-1);
+ }
+ }
+ rip_sock_interface = ifp;
+ }
+
+ trace_rip(msg, "to", &sin, ifp, buf, size);
+
+ res = sendto(rip_sock, buf, size, flags,
+ (struct sockaddr *)&sin, sizeof (sin));
+ if (res < 0 &&
+ (ifp == NULL || !(ifp->int_state & IS_BROKE))) {
+ writelog(LOG_WARNING, "%s sendto(%s%s%s.%d): %s", msg,
+ ifp != NULL ? ifp->int_name : "",
+ ifp != NULL ? ", " : "",
+ inet_ntoa(sin.sin_addr),
+ ntohs(sin.sin_port),
+ rip_strerror(errno));
+ }
+
+ return (res);
+}
+
+
+/*
+ * Find the first key for a packet to send.
+ * Try for a key that is eligible and has not expired, but settle for
+ * the last key if they have all expired.
+ * If no key is ready yet, give up.
+ */
+struct auth *
+find_auth(struct interface *ifp)
+{
+ struct auth *ap, *res = NULL;
+ int i;
+
+
+ if (ifp == NULL)
+ return (NULL);
+
+ if ((ap = ifp->int_auth) == NULL)
+ return (NULL);
+
+ for (i = 0; i < MAX_AUTH_KEYS; i++, ap++) {
+ /* stop looking after the last key */
+ if (ap->type == RIP_AUTH_NONE)
+ break;
+
+ /* ignore keys that are not ready yet */
+ if ((ulong_t)ap->start > (ulong_t)clk.tv_sec)
+ continue;
+
+ if ((ulong_t)ap->end < (ulong_t)clk.tv_sec) {
+ /* note best expired password as a fall-back */
+ if (res == NULL ||
+ (((ulong_t)ap->end > (ulong_t)res->end)) &&
+ ((ulong_t)res->end < (ulong_t)clk.tv_sec))
+ res = ap;
+ continue;
+ }
+
+ /* note key with the best future */
+ if (res == NULL || (ulong_t)res->end < (ulong_t)ap->end)
+ res = ap;
+ }
+ return (res);
+}
+
+
+void
+clr_ws_buf(struct ws_buf *wb, struct auth *ap)
+{
+ struct netauth *na;
+
+ wb->lim = wb->base + NETS_LEN;
+ wb->n = wb->base;
+ (void) memset(wb->n, 0, NETS_LEN*sizeof (*wb->n));
+
+ /*
+ * (start to) install authentication if appropriate
+ */
+ if (ap == NULL)
+ return;
+
+ na = (struct netauth *)wb->n;
+ if (ap->type == RIP_AUTH_PW) {
+ na->a_family = RIP_AF_AUTH;
+ na->a_type = RIP_AUTH_PW;
+ (void) memcpy(na->au.au_pw, ap->key, sizeof (na->au.au_pw));
+ wb->n++;
+
+ } else if (ap->type == RIP_AUTH_MD5) {
+ na->a_family = RIP_AF_AUTH;
+ na->a_type = RIP_AUTH_MD5;
+ na->au.a_md5.md5_keyid = ap->keyid;
+ na->au.a_md5.md5_auth_len = RIP_AUTH_MD5_LEN;
+ na->au.a_md5.md5_seqno = htonl(clk.tv_sec);
+ wb->n++;
+ wb->lim--; /* make room for trailer */
+ }
+}
+
+
+void
+end_md5_auth(struct ws_buf *wb, struct auth *ap)
+{
+ struct netauth *na, *na2;
+ MD5_CTX md5_ctx;
+ int len;
+
+ na = (struct netauth *)wb->base;
+ na2 = (struct netauth *)wb->n;
+ len = (char *)na2-(char *)wb->buf;
+ na2->a_family = RIP_AF_AUTH;
+ na2->a_type = RIP_AUTH_TRAILER;
+ na->au.a_md5.md5_pkt_len = htons(len);
+ MD5Init(&md5_ctx);
+ /* len+4 to include auth trailer's family/type in MD5 sum */
+ MD5Update(&md5_ctx, (uchar_t *)wb->buf, len + 4);
+ MD5Update(&md5_ctx, ap->key, RIP_AUTH_MD5_LEN);
+ MD5Final(na2->au.au_pw, &md5_ctx);
+ wb->n++;
+}
+
+
+/*
+ * Send the buffer
+ */
+static void
+supply_write(struct ws_buf *wb)
+{
+ /*
+ * Output multicast only if legal.
+ * If we would multicast and it would be illegal, then discard the
+ * packet.
+ */
+ switch (wb->type) {
+ case NO_OUT_MULTICAST:
+ trace_pkt("skip multicast to %s because impossible",
+ naddr_ntoa(ws.to.sin_addr.s_addr));
+ break;
+ case NO_OUT_RIPV2:
+ break;
+ default:
+ if (ws.a != NULL && ws.a->type == RIP_AUTH_MD5)
+ end_md5_auth(wb, ws.a);
+ if (output(wb->type, &ws.to, ws.ifp, wb->buf,
+ ((char *)wb->n - (char *)wb->buf)) < 0 && ws.ifp != NULL)
+ if_sick(ws.ifp, _B_FALSE);
+ ws.npackets++;
+ break;
+ }
+
+ clr_ws_buf(wb, ws.a);
+}
+
+
+/*
+ * Put an entry into the packet
+ */
+static void
+supply_out(struct ag_info *ag)
+{
+ uint32_t dstcount;
+ in_addr_t mask, v1_mask, dst_h, ddst_h = 0;
+ struct ws_buf *wb;
+
+
+ /*
+ * Skip this route if doing a flash update and it and the routes
+ * it aggregates have not changed recently.
+ */
+ if (ag->ag_seqno < update_seqno && (ws.state & WS_ST_FLASH))
+ return;
+
+ dst_h = ag->ag_dst_h;
+ mask = ag->ag_mask;
+ v1_mask = ripv1_mask_host(htonl(dst_h),
+ (ws.state & WS_ST_TO_ON_NET) ? ws.ifp : NULL);
+ dstcount = 0;
+
+ /*
+ * If we are sending RIPv2 packets that cannot (or must not) be
+ * heard by RIPv1 listeners, do not worry about sub- or supernets.
+ * Subnets (from other networks) can only be sent via multicast.
+ * A pair of subnet routes might have been promoted so that they
+ * are legal to send by RIPv1.
+ * If RIPv1 is off, use the multicast buffer.
+ */
+ if ((ws.state & WS_ST_RIP2_ALL) ||
+ ((ag->ag_state & AGS_RIPV2) && v1_mask != mask)) {
+ /* use the RIPv2-only buffer */
+ wb = &v2buf;
+
+ } else {
+ /*
+ * use the RIPv1-or-RIPv2 buffer
+ */
+ wb = &v12buf;
+
+ /*
+ * Convert supernet route into corresponding set of network
+ * routes for RIPv1, but leave non-contiguous netmasks
+ * to ag_check().
+ */
+ if (v1_mask > mask &&
+ mask + (mask & -mask) == 0) {
+ ddst_h = v1_mask & -v1_mask;
+ dstcount = (v1_mask & ~mask)/ddst_h;
+
+ if (dstcount > ws.gen_limit) {
+ /*
+ * Punt if we would have to generate an
+ * unreasonable number of routes.
+ */
+ if (TRACECONTENTS)
+ trace_misc("sending %s-->%s as 1"
+ " instead of %d routes",
+ addrname(htonl(dst_h), mask, 1),
+ naddr_ntoa(ws.to.sin_addr.s_addr),
+ dstcount + 1);
+ dstcount = 0;
+
+ } else {
+ mask = v1_mask;
+ ws.gen_limit -= dstcount;
+ }
+ }
+ }
+
+ do {
+ wb->n->n_family = RIP_AF_INET;
+ wb->n->n_dst = htonl(dst_h);
+ /*
+ * If the route is from router-discovery or we are
+ * shutting down, or this is a broken/sick interface,
+ * admit only a bad metric.
+ */
+ wb->n->n_metric = ((stopint || ag->ag_metric < 1 ||
+ (ag->ag_ifp && (ag->ag_ifp->int_state &
+ (IS_BROKE|IS_SICK)))) ? HOPCNT_INFINITY : ag->ag_metric);
+ wb->n->n_metric = htonl(wb->n->n_metric);
+ /*
+ * Any non-zero bits in the supposedly unused RIPv1 fields
+ * cause the old `routed` to ignore the route.
+ * That means the mask and so forth cannot be sent
+ * in the hybrid RIPv1/RIPv2 mode.
+ */
+ if (ws.state & WS_ST_RIP2_ALL) {
+ if (ag->ag_nhop != 0 &&
+ ((ws.state & WS_ST_QUERY) ||
+ (ag->ag_nhop != ws.ifp->int_addr &&
+ on_net(ag->ag_nhop, ws.ifp->int_net,
+ ws.ifp->int_mask)) &&
+ ifwithaddr(ag->ag_nhop, _B_FALSE, _B_FALSE) ==
+ NULL))
+ wb->n->n_nhop = ag->ag_nhop;
+ wb->n->n_mask = htonl(mask);
+ wb->n->n_tag = ag->ag_tag;
+ }
+ dst_h += ddst_h;
+
+ if (++wb->n >= wb->lim)
+ supply_write(wb);
+ } while (dstcount-- > 0);
+}
+
+
+/*
+ * Supply one route from the table
+ */
+/* ARGSUSED */
+static int
+walk_supply(struct radix_node *rn, void *argp)
+{
+#define RT ((struct rt_entry *)rn)
+ ushort_t ags;
+ uint8_t metric, pref;
+ in_addr_t dst, nhop;
+ struct rt_spare *rts;
+ uint_t sparecount;
+
+
+ /*
+ * Do not advertise external remote interfaces or passive interfaces.
+ */
+ if ((RT->rt_state & RS_IF) && RT->rt_ifp != NULL &&
+ (RT->rt_ifp->int_state & IS_PASSIVE) &&
+ !(RT->rt_state & RS_MHOME))
+ return (0);
+ /*
+ * Do not advertise routes learnt from /etc/gateways.
+ */
+ if (RT->rt_spares[0].rts_origin == RO_FILE)
+ return (0);
+
+ /*
+ * Do not advertise routes which would lead to forwarding on a
+ * non-forwarding interface.
+ */
+ if (RT->rt_state & RS_NOPROPAGATE)
+ return (0);
+
+ /*
+ * If being quiet about our ability to forward, then
+ * do not say anything unless responding to a query,
+ * except about our main interface.
+ */
+ if (!should_supply(NULL) && !(ws.state & WS_ST_QUERY) &&
+ !(RT->rt_state & RS_MHOME))
+ return (0);
+
+ dst = RT->rt_dst;
+
+ /*
+ * do not collide with the fake default route
+ */
+ if (dst == RIP_DEFAULT && (ws.state & WS_ST_DEFAULT))
+ return (0);
+
+ if (RT->rt_state & RS_NET_SYN) {
+ if (RT->rt_state & RS_NET_INT) {
+ /*
+ * Do not send manual synthetic network routes
+ * into the subnet.
+ */
+ if (on_net(ws.to.sin_addr.s_addr,
+ ntohl(dst), RT->rt_mask))
+ return (0);
+
+ } else {
+ /*
+ * Do not send automatic synthetic network routes
+ * if they are not needed because no RIPv1 listeners
+ * can hear them.
+ */
+ if (ws.state & WS_ST_RIP2_ALL)
+ return (0);
+
+ /*
+ * Do not send automatic synthetic network routes to
+ * the real subnet.
+ */
+ if (on_net(ws.to.sin_addr.s_addr,
+ ntohl(dst), RT->rt_mask))
+ return (0);
+ }
+ nhop = 0;
+
+ } else {
+ /*
+ * Advertise the next hop if this is not a route for one
+ * of our interfaces and the next hop is on the same
+ * network as the target.
+ * The final determination is made by supply_out().
+ */
+ if (!(RT->rt_state & RS_IF) && !(RT->rt_state & RS_MHOME) &&
+ RT->rt_gate != loopaddr)
+ nhop = RT->rt_gate;
+ else
+ nhop = 0;
+ }
+
+ metric = RT->rt_metric;
+ ags = 0;
+
+ if (!RT_ISHOST(RT)) {
+ /*
+ * Always suppress network routes into other, existing
+ * network routes
+ */
+ ags |= AGS_SUPPRESS;
+
+ /*
+ * Generate supernets if allowed.
+ * If we can be heard by RIPv1 systems, we will
+ * later convert back to ordinary nets.
+ * This unifies dealing with received supernets.
+ */
+ if ((ws.state & WS_ST_AG) && ((RT->rt_state & RS_SUBNET) ||
+ (ws.state & WS_ST_SUPER_AG)))
+ ags |= AGS_AGGREGATE;
+ } else if (!(RT->rt_state & RS_MHOME)) {
+ /*
+ * We should always suppress (into existing network routes)
+ * the host routes for the local end of our point-to-point
+ * links.
+ * If we are suppressing host routes in general, then do so.
+ * Avoid advertising host routes onto their own network,
+ * where they should be handled by proxy-ARP.
+ */
+ if ((RT->rt_state & RS_LOCAL) || ridhosts ||
+ on_net(dst, ws.to_net, ws.to_mask))
+ ags |= AGS_SUPPRESS;
+
+ /*
+ * Aggregate stray host routes into network routes if allowed.
+ * We cannot aggregate host routes into small network routes
+ * without confusing RIPv1 listeners into thinking the
+ * network routes are host routes.
+ */
+ if ((ws.state & WS_ST_AG) && (ws.state & WS_ST_RIP2_ALL))
+ ags |= AGS_AGGREGATE;
+ }
+
+ /*
+ * Do not send RIPv1 advertisements of subnets to other
+ * networks. If possible, multicast them by RIPv2.
+ */
+ if ((RT->rt_state & RS_SUBNET) && !(ws.state & WS_ST_RIP2_ALL) &&
+ !on_net(dst, ws.to_std_net, ws.to_std_mask))
+ ags |= AGS_RIPV2 | AGS_AGGREGATE;
+
+
+ /*
+ * Do not send a route back to where it came from, except in
+ * response to a query. This is "split-horizon". That means not
+ * advertising back to the same network and so via the same interface.
+ *
+ * We want to suppress routes that might have been fragmented
+ * from this route by a RIPv1 router and sent back to us, and so we
+ * cannot forget this route here. Let the split-horizon route
+ * suppress the fragmented routes and then itself be forgotten.
+ *
+ * Include the routes for both ends of point-to-point interfaces
+ * among those suppressed by split-horizon, since the other side
+ * should knows them as well as we do.
+ *
+ * Notice spare routes with the same metric that we are about to
+ * advertise, to split the horizon on redundant, inactive paths.
+ */
+ if (ws.ifp != NULL && !(ws.state & WS_ST_QUERY) &&
+ (ws.state & WS_ST_TO_ON_NET) && (!(RT->rt_state & RS_IF) ||
+ (ws.ifp->int_if_flags & IFF_POINTOPOINT))) {
+ for (rts = RT->rt_spares, sparecount = 0;
+ sparecount < RT->rt_num_spares; sparecount++, rts++) {
+ if (rts->rts_metric > metric || rts->rts_ifp != ws.ifp)
+ continue;
+
+ /*
+ * If we do not mark the route with AGS_SPLIT_HZ here,
+ * it will be poisoned-reverse, or advertised back
+ * toward its source with an infinite metric.
+ * If we have recently advertised the route with a
+ * better metric than we now have, then we should
+ * poison-reverse the route before suppressing it for
+ * split-horizon.
+ *
+ * In almost all cases, if there is no spare for the
+ * route then it is either old and dead or a brand
+ * new route. If it is brand new, there is no need
+ * for poison-reverse. If it is old and dead, it
+ * is already poisoned.
+ */
+ if (RT->rt_poison_time < now_expire ||
+ RT->rt_poison_metric >= metric ||
+ RT->rt_spares[1].rts_gate == 0) {
+ ags |= AGS_SPLIT_HZ;
+ ags &= ~AGS_SUPPRESS;
+ }
+ metric = HOPCNT_INFINITY;
+ break;
+ }
+ }
+
+ /*
+ * Keep track of the best metric with which the
+ * route has been advertised recently.
+ */
+ if (RT->rt_poison_metric >= metric ||
+ RT->rt_poison_time < now_expire) {
+ RT->rt_poison_time = now.tv_sec;
+ RT->rt_poison_metric = metric;
+ }
+
+ /*
+ * Adjust the outgoing metric by the cost of the link.
+ * Avoid aggregation when a route is counting to infinity.
+ */
+ pref = RT->rt_poison_metric + ws.metric;
+ metric += ws.metric;
+
+ /*
+ * If this is a static route pointing to the same interface
+ * upon which we are sending out the RIP RESPONSE
+ * adjust the preference so that we don't aggregate into this
+ * route. Note that the maximum possible hop count on a route
+ * per RFC 2453 is 16 (HOPCNT_INFINITY)
+ */
+ if ((RT->rt_state & RS_STATIC) && (ws.ifp == RT->rt_ifp))
+ pref = (HOPCNT_INFINITY+1);
+
+ /*
+ * Do not advertise stable routes that will be ignored,
+ * unless we are answering a query.
+ * If the route recently was advertised with a metric that
+ * would have been less than infinity through this interface,
+ * we need to continue to advertise it in order to poison it.
+ */
+ if (metric >= HOPCNT_INFINITY) {
+ if (!(ws.state & WS_ST_QUERY) && (pref >= HOPCNT_INFINITY ||
+ RT->rt_poison_time < now_garbage))
+ return (0);
+
+ metric = HOPCNT_INFINITY;
+ }
+
+ /*
+ * supply this route out on the wire- we only care about dest/mask
+ * and so can ignore all rt_spares[i] with i > 0
+ */
+ ag_check(dst, RT->rt_mask, 0, RT->rt_ifp, nhop, metric, pref,
+ RT->rt_seqno, RT->rt_tag, ags, supply_out);
+ return (0);
+#undef RT
+}
+
+
+/*
+ * Supply dst with the contents of the routing tables.
+ * If this won't fit in one packet, chop it up into several.
+ */
+void
+supply(struct sockaddr_in *dst,
+ struct interface *ifp, /* output interface */
+ enum output_type type,
+ int flash, /* 1=flash update */
+ int vers, /* RIP version */
+ boolean_t passwd_ok) /* OK to include cleartext password */
+{
+ struct rt_entry *rt;
+ uint8_t def_metric;
+
+
+ ws.state = 0;
+ ws.gen_limit = WS_GEN_LIMIT_MAX;
+
+ ws.to = *dst;
+ ws.to_std_mask = std_mask(ws.to.sin_addr.s_addr);
+ ws.to_std_net = ntohl(ws.to.sin_addr.s_addr) & ws.to_std_mask;
+
+ if (ifp != NULL) {
+ ws.to_mask = ifp->int_mask;
+ ws.to_net = ifp->int_net;
+ if (on_net(ws.to.sin_addr.s_addr, ws.to_net, ws.to_mask) ||
+ type == OUT_MULTICAST)
+ ws.state |= WS_ST_TO_ON_NET;
+
+ } else {
+ ws.to_mask = ripv1_mask_net(ws.to.sin_addr.s_addr, NULL);
+ ws.to_net = ntohl(ws.to.sin_addr.s_addr) & ws.to_mask;
+ rt = rtfind(dst->sin_addr.s_addr);
+ if (rt != NULL)
+ ifp = rt->rt_ifp;
+ else
+ return;
+ }
+
+ ws.npackets = 0;
+ if (flash)
+ ws.state |= WS_ST_FLASH;
+
+ ws.ifp = ifp;
+
+ /*
+ * Routes in the table were already adjusted by their respective
+ * destination interface costs (which are zero by default) on
+ * input. The following is the value by which each route's metric
+ * will be bumped up on output.
+ */
+ ws.metric = 1;
+
+ ripv12_buf.rip.rip_vers = vers;
+
+ switch (type) {
+ case OUT_MULTICAST:
+ if (ifp->int_if_flags & IFF_MULTICAST)
+ v2buf.type = OUT_MULTICAST;
+ else
+ v2buf.type = NO_OUT_MULTICAST;
+ v12buf.type = OUT_BROADCAST;
+ break;
+
+ case OUT_QUERY:
+ ws.state |= WS_ST_QUERY;
+ /* FALLTHROUGH */
+ case OUT_BROADCAST:
+ case OUT_UNICAST:
+ v2buf.type = (vers == RIPv2) ? type : NO_OUT_RIPV2;
+ v12buf.type = type;
+ break;
+
+ case NO_OUT_MULTICAST:
+ case NO_OUT_RIPV2:
+ return; /* no output */
+ }
+
+ if (vers == RIPv2) {
+ /* full RIPv2 only if cannot be heard by RIPv1 listeners */
+ if (type != OUT_BROADCAST)
+ ws.state |= WS_ST_RIP2_ALL;
+ if ((ws.state & WS_ST_QUERY) || !(ws.state & WS_ST_TO_ON_NET)) {
+ ws.state |= (WS_ST_AG | WS_ST_SUPER_AG);
+ } else if (ifp == NULL || !(ifp->int_state & IS_NO_AG)) {
+ ws.state |= WS_ST_AG;
+ if (type != OUT_BROADCAST && (ifp == NULL ||
+ !(ifp->int_state & IS_NO_SUPER_AG)))
+ ws.state |= WS_ST_SUPER_AG;
+ }
+
+ /* See if this packet needs authenticating */
+ ws.a = find_auth(ifp);
+ if (!passwd_ok && ws.a != NULL && ws.a->type == RIP_AUTH_PW)
+ ws.a = NULL;
+ if (ws.a != NULL && (ulong_t)ws.a->end < (ulong_t)clk.tv_sec &&
+ !ws.a->warnedflag) {
+ /*
+ * If the best key is an expired one, we may as
+ * well use it. Log this event.
+ */
+ writelog(LOG_WARNING,
+ "Using expired auth while transmitting to %s",
+ naddr_ntoa(ws.to.sin_addr.s_addr));
+ ws.a->warnedflag = 1;
+ }
+ } else {
+ ws.a = NULL;
+ }
+
+ clr_ws_buf(&v12buf, ws.a);
+ clr_ws_buf(&v2buf, ws.a);
+
+ /*
+ * Fake a default route if asked and if there is not already
+ * a better, real default route.
+ */
+ if (should_supply(NULL) && (def_metric = ifp->int_d_metric) != 0) {
+ if (NULL == (rt = rtget(RIP_DEFAULT, 0)) ||
+ rt->rt_metric+ws.metric >= def_metric) {
+ ws.state |= WS_ST_DEFAULT;
+ ag_check(0, 0, 0, NULL, 0, def_metric, def_metric,
+ 0, 0, 0, supply_out);
+ } else {
+ def_metric = rt->rt_metric+ws.metric;
+ }
+
+ /*
+ * If both RIPv2 and the poor-man's router discovery
+ * kludge are on, arrange to advertise an extra
+ * default route via RIPv1.
+ */
+ if ((ws.state & WS_ST_RIP2_ALL) &&
+ (ifp->int_state & IS_PM_RDISC)) {
+ ripv12_buf.rip.rip_vers = RIPv1;
+ v12buf.n->n_family = RIP_AF_INET;
+ v12buf.n->n_dst = htonl(RIP_DEFAULT);
+ v12buf.n->n_metric = htonl(def_metric);
+ v12buf.n++;
+ }
+ }
+
+ (void) rn_walktree(rhead, walk_supply, NULL);
+ ag_flush(0, 0, supply_out);
+
+ /*
+ * Flush the packet buffers, provided they are not empty and
+ * do not contain only the password.
+ */
+ if (v12buf.n != v12buf.base &&
+ (v12buf.n > v12buf.base+1 ||
+ v12buf.base->n_family != RIP_AF_AUTH))
+ supply_write(&v12buf);
+ if (v2buf.n != v2buf.base && (v2buf.n > v2buf.base+1 ||
+ v2buf.base->n_family != RIP_AF_AUTH))
+ supply_write(&v2buf);
+
+ /*
+ * If we sent nothing and this is an answer to a query, send
+ * an empty buffer.
+ */
+ if (ws.npackets == 0 && (ws.state & WS_ST_QUERY)) {
+ supply_write(&v2buf);
+ if (ws.npackets == 0)
+ supply_write(&v12buf);
+ }
+}
+
+
+/*
+ * send all of the routing table or just do a flash update
+ */
+void
+rip_bcast(int flash)
+{
+ static struct sockaddr_in dst = {AF_INET};
+ struct interface *ifp;
+ enum output_type type;
+ int vers;
+ struct timeval rtime;
+
+
+ need_flash = _B_FALSE;
+ intvl_random(&rtime, MIN_WAITTIME, MAX_WAITTIME);
+ no_flash = rtime;
+ timevaladd(&no_flash, &now);
+
+ if (!rip_enabled)
+ return;
+
+ trace_act("send %s and inhibit dynamic updates for %.3f sec",
+ flash ? "dynamic update" : "all routes",
+ rtime.tv_sec + ((double)rtime.tv_usec)/1000000.0);
+
+ for (ifp = ifnet; ifp != NULL; ifp = ifp->int_next) {
+ /*
+ * Skip interfaces not doing RIP or for which IP
+ * forwarding isn't turned on. Skip duplicate
+ * interfaces, we don't want to generate duplicate
+ * packets. Do try broken interfaces to see if they
+ * have healed.
+ */
+ if (IS_RIP_OUT_OFF(ifp->int_state) ||
+ (ifp->int_state & IS_DUP) ||
+ !IS_IFF_ROUTING(ifp->int_if_flags))
+ continue;
+
+ /* skip turned off interfaces */
+ if (!IS_IFF_UP(ifp->int_if_flags))
+ continue;
+
+ /* skip interfaces we shouldn't use */
+ if (IS_IFF_QUIET(ifp->int_if_flags))
+ continue;
+
+ vers = (ifp->int_state & IS_NO_RIPV1_OUT) ? RIPv2 : RIPv1;
+ dst.sin_addr.s_addr = ifp->int_ripout_addr;
+
+ /*
+ * Ignore the interface if it's not broadcast,
+ * point-to-point, or remote. It must be non-broadcast
+ * multiaccess, and therefore unsupported.
+ */
+ if (!(ifp->int_if_flags & (IFF_BROADCAST | IFF_POINTOPOINT)) &&
+ !(ifp->int_state & IS_REMOTE))
+ continue;
+
+ type = (ifp->int_if_flags & IFF_BROADCAST) ?
+ OUT_BROADCAST : OUT_UNICAST;
+ if (vers == RIPv2 && (ifp->int_if_flags & IFF_MULTICAST) &&
+ !(ifp->int_state & IS_NO_RIP_MCAST))
+ type = OUT_MULTICAST;
+
+ supply(&dst, ifp, type, flash, vers, _B_TRUE);
+ }
+
+ update_seqno++; /* all routes are up to date */
+}
+
+
+/*
+ * Ask for routes
+ * Do it only once to an interface, and not even after the interface
+ * was broken and recovered.
+ */
+void
+rip_query(void)
+{
+ static struct sockaddr_in dst = {AF_INET};
+ struct interface *ifp;
+ struct rip buf;
+ enum output_type type;
+
+
+ if (!rip_enabled)
+ return;
+
+ (void) memset(&buf, 0, sizeof (buf));
+
+ for (ifp = ifnet; ifp; ifp = ifp->int_next) {
+ /*
+ * Skip interfaces those already queried. Do not ask
+ * via interfaces through which we don't accept input.
+ * Do not ask via interfaces that cannot send RIP
+ * packets. Don't send queries on duplicate
+ * interfaces, that would generate duplicate packets
+ * on link. Do try broken interfaces to see if they
+ * have healed.
+ */
+ if (IS_RIP_IN_OFF(ifp->int_state) ||
+ (ifp->int_state & IS_DUP) ||
+ ifp->int_query_time != NEVER)
+ continue;
+
+ /* skip turned off interfaces */
+ if (!IS_IFF_UP(ifp->int_if_flags))
+ continue;
+
+ /* skip interfaces we shouldn't use */
+ if (IS_IFF_QUIET(ifp->int_if_flags))
+ continue;
+
+ /*
+ * Ignore the interface if it's not broadcast,
+ * point-to-point, or remote. It must be non-broadcast
+ * multiaccess, and therefore unsupported.
+ */
+ if (!(ifp->int_if_flags & (IFF_BROADCAST | IFF_POINTOPOINT)) &&
+ !(ifp->int_state & IS_REMOTE))
+ continue;
+
+ buf.rip_cmd = RIPCMD_REQUEST;
+ buf.rip_nets[0].n_family = RIP_AF_UNSPEC;
+ buf.rip_nets[0].n_metric = htonl(HOPCNT_INFINITY);
+
+ /*
+ * Send a RIPv1 query only if allowed and if we will
+ * listen to RIPv1 routers.
+ */
+ if ((ifp->int_state & IS_NO_RIPV1_OUT) ||
+ (ifp->int_state & IS_NO_RIPV1_IN)) {
+ buf.rip_vers = RIPv2;
+ } else {
+ buf.rip_vers = RIPv1;
+ }
+
+ dst.sin_addr.s_addr = ifp->int_ripout_addr;
+
+ type = (ifp->int_if_flags & IFF_BROADCAST) ?
+ OUT_BROADCAST : OUT_UNICAST;
+ if (buf.rip_vers == RIPv2 &&
+ (ifp->int_if_flags & IFF_MULTICAST) &&
+ !(ifp->int_state & IS_NO_RIP_MCAST))
+ type = OUT_MULTICAST;
+
+ ifp->int_query_time = now.tv_sec+SUPPLY_INTERVAL;
+ if (output(type, &dst, ifp, &buf, sizeof (buf)) < 0)
+ if_sick(ifp, _B_FALSE);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.routed/parms.c b/usr/src/cmd/cmd-inet/usr.sbin/in.routed/parms.c
new file mode 100644
index 0000000000..86eb3adb83
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.routed/parms.c
@@ -0,0 +1,1046 @@
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgment:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sbin/routed/parms.c,v 1.9 2000/08/11 08:24:38 sheldonh Exp $
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "defs.h"
+#include "pathnames.h"
+#include <sys/stat.h>
+#include <arpa/inet.h>
+#include <ctype.h>
+
+#define PARMS_MAXLINELEN 500
+static struct parm *parms;
+struct intnet *intnets;
+struct r1net *r1nets;
+struct tgate *tgates;
+
+static void addroutefordefault(in_addr_t, in_addr_t, in_addr_t,
+ uint32_t, uint16_t);
+
+/* use configured parameters */
+void
+get_parms(struct interface *ifp)
+{
+ static boolean_t warned_auth_in, warned_auth_out;
+ struct parm *parmp;
+ int i, num_passwds = 0;
+
+ if (ifp == NULL)
+ return;
+
+ /* get all relevant parameters */
+ for (parmp = parms; parmp != NULL; parmp = parmp->parm_next) {
+ if (parmp->parm_name[0] == '\0' ||
+ strcmp(ifp->int_name, parmp->parm_name) == 0 ||
+ (parmp->parm_name[0] == '\n' &&
+ on_net(ifp->int_addr,
+ parmp->parm_net, parmp->parm_mask))) {
+
+ /*
+ * This group of parameters is relevant,
+ * so get its settings
+ */
+ ifp->int_state |= parmp->parm_int_state;
+ for (i = 0; i < MAX_AUTH_KEYS; i++) {
+ if (parmp->parm_auth[i].type == RIP_AUTH_NONE ||
+ num_passwds >= MAX_AUTH_KEYS)
+ break;
+ ifp->int_auth[num_passwds++] =
+ parmp->parm_auth[i];
+ }
+ if (parmp->parm_rdisc_pref != 0)
+ ifp->int_rdisc_pref = parmp->parm_rdisc_pref;
+ if (parmp->parm_rdisc_int != 0)
+ ifp->int_rdisc_int = parmp->parm_rdisc_int;
+ if (parmp->parm_d_metric != 0)
+ ifp->int_d_metric = parmp->parm_d_metric;
+ if (parmp->parm_ripout_addr != 0)
+ ifp->int_ripout_addr = parmp->parm_ripout_addr;
+ }
+ }
+
+ /*
+ * Set general defaults.
+ *
+ * Default poor-man's router discovery to a metric that will
+ * be heard by old versions of `routed`. They ignored received
+ * routes with metric 15.
+ */
+ if ((ifp->int_state & IS_PM_RDISC) && ifp->int_d_metric == 0)
+ ifp->int_d_metric = FAKE_METRIC;
+
+ if (ifp->int_rdisc_int == 0)
+ ifp->int_rdisc_int = DEF_MAXADVERTISEINTERVAL;
+
+ if (!(ifp->int_if_flags & IFF_MULTICAST) &&
+ !(ifp->int_state & IS_REMOTE))
+ ifp->int_state |= IS_BCAST_RDISC;
+
+ if (ifp->int_if_flags & IFF_POINTOPOINT) {
+ ifp->int_state |= IS_BCAST_RDISC;
+ /*
+ * By default, point-to-point links should be passive
+ * about router-discovery for the sake of demand-dialing.
+ */
+ if (!(ifp->int_state & GROUP_IS_SOL_OUT))
+ ifp->int_state |= IS_NO_SOL_OUT;
+ if (!(ifp->int_state & GROUP_IS_ADV_OUT))
+ ifp->int_state |= IS_NO_ADV_OUT;
+ }
+
+ if (0 != (ifp->int_state & (IS_PASSIVE | IS_REMOTE)))
+ ifp->int_state |= IS_NO_RDISC;
+ if (ifp->int_state & IS_PASSIVE)
+ ifp->int_state |= IS_NO_RIP;
+
+ if (!IS_RIP_IN_OFF(ifp->int_state) &&
+ ifp->int_auth[0].type != RIP_AUTH_NONE &&
+ !(ifp->int_state & IS_NO_RIPV1_IN) && !warned_auth_in) {
+ writelog(LOG_WARNING, "RIPv1 input via %s"
+ " will be accepted without authentication",
+ ifp->int_name);
+ warned_auth_in = _B_TRUE;
+ }
+ if (!IS_RIP_OUT_OFF(ifp->int_state) &&
+ ifp->int_auth[0].type != RIP_AUTH_NONE &&
+ !(ifp->int_state & IS_NO_RIPV1_OUT)) {
+ if (!warned_auth_out) {
+ writelog(LOG_WARNING, "RIPv1 output via %s"
+ " will be sent without authentication",
+ ifp->int_name);
+ warned_auth_out = _B_TRUE;
+ }
+ }
+
+ /*
+ * If not overriden by the rip_neighbor option, set the
+ * default address to which RIP packets will be sent on
+ * this interface.
+ */
+ if (ifp->int_ripout_addr == 0) {
+ if (ifp->int_state & IS_REMOTE) {
+ /*
+ * By definition we always send RIP packets to
+ * the address assigned to a remote interface.
+ */
+ ifp->int_ripout_addr = ifp->int_addr;
+ } else if ((ifp->int_state & IS_NO_RIPV1_OUT) &&
+ (ifp->int_if_flags & IFF_MULTICAST) &&
+ !(ifp->int_state & IS_NO_RIP_MCAST)) {
+ /*
+ * If the interface is being used for RIPv2
+ * and it supports multicast, and if the user
+ * has not explicitely turned off multicast
+ * RIP output, send to the all RIP routers
+ * multicast address.
+ */
+ ifp->int_ripout_addr = htonl(INADDR_RIP_GROUP);
+ } else if (ifp->int_if_flags & IFF_POINTOPOINT) {
+ /*
+ * For point-to-point interfaces which don't
+ * fall into the two categories above, just
+ * send to the destination address of the
+ * interface.
+ */
+ ifp->int_ripout_addr = ifp->int_dstaddr;
+ } else {
+ /* Otherwise, use the broadcast address. */
+ ifp->int_ripout_addr = ifp->int_brdaddr;
+ }
+ }
+}
+
+
+/*
+ * Read a list of gateways from /etc/gateways and add them to our tables.
+ *
+ * This file contains a list of "remote" gateways. That is usually
+ * a gateway which we cannot immediately determine if it is present or
+ * not as we can do for those provided by directly connected hardware.
+ *
+ * If a gateway is marked "passive" in the file, then we assume it
+ * does not understand RIP and assume it is always present. Those
+ * not marked passive are treated as if they were directly connected
+ * and assumed to be broken if they do not send us advertisements.
+ * All remote interfaces are added to our list, and those not marked
+ * passive are sent routing updates.
+ *
+ * A passive interface can also be local, hardware interface exempt
+ * from RIP.
+ */
+void
+gwkludge(void)
+{
+#define STR2(x) #x
+#define STR(x) STR2(x)
+
+#define NETHOST_LEN 4
+#define DNAME_LEN MAXHOSTNAMELEN
+#define GNAME_LEN MAXHOSTNAMELEN
+#define QUAL_LEN 8
+
+ FILE *fp;
+ char *p, *lptr;
+ const char *cp;
+ char lbuf[PARMS_MAXLINELEN], net_host[NETHOST_LEN + 1];
+ char dname[MAXHOSTNAMELEN + 1];
+ char gname[MAXHOSTNAMELEN + 1], qual[QUAL_LEN +1];
+ struct interface *ifp;
+ uint32_t dst, netmask, gate;
+ int n;
+ uint32_t lnum;
+ struct stat sb;
+ uint32_t state, metric;
+ boolean_t default_dst;
+
+
+ fp = fopen(PATH_GATEWAYS, "r");
+ if (fp == NULL)
+ return;
+
+ if (0 > fstat(fileno(fp), &sb)) {
+ msglog("fstat() failed: %s for "PATH_GATEWAYS,
+ rip_strerror(errno));
+ (void) fclose(fp);
+ return;
+ }
+
+ for (lnum = 1; ; lnum++) {
+ if (NULL == fgets(lbuf, sizeof (lbuf), fp))
+ break;
+
+ /* Eliminate the /n character at the end of the lbuf */
+ if (strlen(lbuf) > 0)
+ lbuf[strlen(lbuf) - 1] = '\0';
+
+ /* Move lptr to the first non-space character */
+ for (lptr = lbuf; isspace(*lptr); lptr++)
+ ;
+
+ if (*lptr == '#' || *lptr == '\0')
+ continue;
+
+ /* Move p to the end of the line */
+ p = lptr + strlen(lptr) - 1;
+
+ /* Skip all trailing spaces except escaped space */
+ while (p > lptr && (isspace(*p) && *(p-1) != '\\'))
+ p--;
+
+ /* truncate the line to remove trailing spaces */
+ *++p = '\0';
+
+ /* notice newfangled parameter lines */
+ if (strncasecmp("net", lptr, 3) != 0 &&
+ strncasecmp("host", lptr, 4) != 0) {
+ cp = parse_parms(lptr, (sb.st_uid == 0 &&
+ !(sb.st_mode&(S_IRWXG|S_IRWXO))));
+ if (cp != 0)
+ msglog("%s in line %u of "PATH_GATEWAYS,
+ cp, lnum);
+ continue;
+ }
+
+ /*
+ * Processes lines of the follwoing format:
+ * net|host <name>[/mask] gateway <Gname> metric <value>
+ * passive|active|extern
+ */
+ qual[0] = '\0';
+ n = sscanf(lptr, "%"STR(NETHOST_LEN)"s %"STR(DNAME_LEN)
+ "[^ \t] gateway %"STR(GNAME_LEN)"[^ / \t] metric %u %"
+ STR(QUAL_LEN)"s\n", net_host, dname, gname, &metric, qual);
+ if (n != 4 && n != 5) {
+ msglog("bad "PATH_GATEWAYS" entry \"%s\"; %d values",
+ lptr, n);
+ continue;
+ }
+ if (metric >= HOPCNT_INFINITY) {
+ msglog("bad metric in "PATH_GATEWAYS" entry \"%s\"",
+ lptr);
+ continue;
+ }
+ default_dst = _B_FALSE;
+ if (strcasecmp(net_host, "host") == 0) {
+ if (!gethost(dname, &dst)) {
+ msglog("bad host \"%s\" in "PATH_GATEWAYS
+ " entry \"%s\"", dname, lptr);
+ continue;
+ }
+ netmask = HOST_MASK;
+ } else if (strcasecmp(net_host, "net") == 0) {
+ if (!getnet(dname, &dst, &netmask)) {
+ msglog("bad net \"%s\" in "PATH_GATEWAYS
+ " entry \"%s\"", dname, lptr);
+ continue;
+ }
+ default_dst = (dst == RIP_DEFAULT);
+ dst = htonl(dst); /* make network # into IP address */
+ } else {
+ msglog("bad \"%s\" in "PATH_GATEWAYS
+ " entry \"%s\"", net_host, lptr);
+ continue;
+ }
+
+ if (!gethost(gname, &gate)) {
+ msglog("bad gateway \"%s\" in "PATH_GATEWAYS
+ " entry \"%s\"", gname, lptr);
+ continue;
+ }
+
+ if (strcasecmp(qual, "passive") == 0) {
+ /*
+ * Passive entries are not placed in our tables,
+ * only the kernel's, so we don't copy all of the
+ * external routing information within a net.
+ * Internal machines should use the default
+ * route to a suitable gateway (like us).
+ */
+ state = IS_REMOTE | IS_PASSIVE;
+ if (metric == 0)
+ metric = 1;
+
+ } else if (strcasecmp(qual, "external") == 0) {
+ /*
+ * External entries are handled by other means
+ * such as EGP, and are placed only in the daemon
+ * tables to prevent overriding them with something
+ * else.
+ */
+ (void) strlcpy(qual, "external", sizeof (qual));
+ state = IS_REMOTE | IS_PASSIVE | IS_EXTERNAL;
+ if (metric == 0)
+ metric = 1;
+
+ } else if (strcasecmp(qual, "active") == 0 ||
+ qual[0] == '\0') {
+
+ if (default_dst) {
+ msglog("bad net \"%s\" in "PATH_GATEWAYS
+ " entry \"%s\"-- cannot be default",
+ dname, lptr);
+ continue;
+ }
+
+ if (metric != 0) {
+ /*
+ * Entries that are neither "passive" nor
+ * "external" are "remote" and must behave
+ * like physical interfaces. If they are not
+ * heard from regularly, they are deleted.
+ */
+ state = IS_REMOTE;
+ } else {
+ /*
+ * "remote" entries with a metric of 0
+ * are aliases for our own interfaces
+ */
+ state = IS_REMOTE | IS_PASSIVE | IS_ALIAS;
+ }
+
+ } else {
+ msglog("bad "PATH_GATEWAYS" entry \"%s\";"
+ " unknown type %s", lptr, qual);
+ continue;
+ }
+
+ if (0 != (state & (IS_PASSIVE | IS_REMOTE)))
+ state |= IS_NO_RDISC;
+ if (state & IS_PASSIVE)
+ state |= IS_NO_RIP;
+
+
+ if (default_dst) {
+ addroutefordefault(dst, gate, netmask, metric,
+ ((state & IS_EXTERNAL)? RTS_EXTERNAL : 0));
+ continue;
+ }
+
+ ifp = check_dup(NULL, gate, dst, netmask, 0, _B_FALSE);
+ if (ifp != NULL) {
+ msglog("duplicate "PATH_GATEWAYS" entry \"%s\"", lptr);
+ continue;
+ }
+
+ ifp = rtmalloc(sizeof (*ifp), "gwkludge()");
+ (void) memset(ifp, 0, sizeof (*ifp));
+
+ ifp->int_state = state;
+ if (netmask == HOST_MASK)
+ ifp->int_if_flags = IFF_POINTOPOINT | IFF_UP;
+ else
+ ifp->int_if_flags = IFF_UP;
+ ifp->int_act_time = NEVER;
+ ifp->int_addr = gate;
+ ifp->int_dstaddr = dst;
+ ifp->int_mask = netmask;
+ ifp->int_ripv1_mask = netmask;
+ ifp->int_std_mask = std_mask(gate);
+ ifp->int_net = ntohl(dst);
+ ifp->int_std_net = ifp->int_net & ifp->int_std_mask;
+ ifp->int_std_addr = htonl(ifp->int_std_net);
+ ifp->int_metric = metric;
+ if (!(state & IS_EXTERNAL) &&
+ ifp->int_mask != ifp->int_std_mask)
+ ifp->int_state |= IS_SUBNET;
+ (void) snprintf(ifp->int_name, sizeof (ifp->int_name),
+ "remote(%s)", gname);
+
+ if_link(ifp, 0);
+ }
+
+ (void) fclose(fp);
+
+ /*
+ * After all of the parameter lines have been read,
+ * apply them to any remote interfaces.
+ */
+ for (ifp = ifnet; NULL != ifp; ifp = ifp->int_next) {
+ get_parms(ifp);
+
+ tot_interfaces++;
+ if (!IS_RIP_OFF(ifp->int_state))
+ rip_interfaces++;
+ if (!IS_RIP_OUT_OFF(ifp->int_state))
+ ripout_interfaces++;
+
+ trace_if("Add", ifp);
+ }
+
+}
+
+/* Parse password timestamp */
+static char *
+parse_ts(time_t *tp,
+ char **valp,
+ char *val0,
+ char *delimp,
+ char *buf,
+ uint_t bufsize)
+{
+ struct tm tm;
+
+ if (0 > parse_quote(valp, "| ,", delimp, buf, bufsize) ||
+ buf[bufsize-1] != '\0' || buf[bufsize-2] != '\0') {
+ (void) snprintf(buf, bufsize, "bad timestamp %.25s", val0);
+ return (buf);
+ }
+ (void) strlcat(buf, "\n", bufsize);
+ (void) memset(&tm, 0, sizeof (tm));
+ if (5 != sscanf(buf, "%u/%u/%u@%u:%u\n",
+ (unsigned *)&tm.tm_year, (unsigned *)&tm.tm_mon,
+ (unsigned *)&tm.tm_mday, (unsigned *)&tm.tm_hour,
+ (unsigned *)&tm.tm_min) ||
+ tm.tm_mon < 1 || tm.tm_mon > 12 ||
+ tm.tm_mday < 1 || tm.tm_mday > 31) {
+ (void) snprintf(buf, bufsize, "bad timestamp %.25s", val0);
+ return (buf);
+ }
+ tm.tm_mon--;
+ /* assume small years are in the 3rd millenium */
+ if (tm.tm_year <= 37)
+ tm.tm_year += 100;
+
+ if (tm.tm_year >= 1900)
+ tm.tm_year -= 1900;
+
+ if ((*tp = mktime(&tm)) == -1) {
+ (void) snprintf(buf, bufsize, "bad timestamp %.25s", val0);
+ return (buf);
+ }
+
+ return (NULL);
+}
+
+
+/*
+ * Get a password, key ID, and expiration date in the format
+ * passwd|keyID|year/mon/day@hour:min|year/mon/day@hour:min
+ * returns NULL or error message
+ */
+static const char *
+get_passwd(char *tgt,
+ char *val,
+ struct parm *parmp,
+ uint16_t type,
+ boolean_t safe) /* 1=from secure file */
+{
+ static char buf[80];
+ char *val0, *p, delim;
+ struct auth k, *ap, *ap2;
+ int i;
+ ulong_t l;
+
+
+ if (!safe)
+ return ("ignore unsafe password");
+
+ for (ap = parmp->parm_auth, i = 0; ap->type != RIP_AUTH_NONE;
+ i++, ap++) {
+ if (i >= MAX_AUTH_KEYS)
+ return ("too many passwords");
+ }
+
+ (void) memset(&k, 0, sizeof (k));
+ k.type = type;
+ k.end = -1-DAY;
+
+ val0 = val;
+ if (0 > parse_quote(&val, "| ,", &delim,
+ (char *)k.key, sizeof (k.key)))
+ return (tgt);
+
+ if (delim != '|') {
+ if (type == RIP_AUTH_MD5)
+ return ("missing Keyid");
+ } else {
+ val0 = ++val;
+ buf[sizeof (buf)-1] = '\0';
+ if (0 > parse_quote(&val, "| ,", &delim, buf,
+ sizeof (buf)) ||
+ buf[sizeof (buf) - 1] != '\0' ||
+ (l = strtoul(buf, &p, 0)) > 255 ||
+ p == buf || *p != '\0') {
+ (void) snprintf(buf, sizeof (buf),
+ "bad KeyID \"%.20s\"", val0);
+ return (buf);
+ }
+ for (ap2 = parmp->parm_auth; ap2 < ap; ap2++) {
+ if (ap2->keyid == l) {
+ (void) snprintf(buf, sizeof (buf),
+ "duplicate KeyID \"%.20s\"",
+ val0);
+ return (buf);
+ }
+ }
+ k.keyid = (int)l;
+
+ if (delim == '|') {
+ val0 = ++val;
+ if (NULL != (p = parse_ts(&k.start, &val, val0, &delim,
+ buf, sizeof (buf))))
+ return (p);
+ if (delim != '|')
+ return ("missing second timestamp");
+ val0 = ++val;
+ if (NULL != (p = parse_ts(&k.end, &val, val0, &delim,
+ buf, sizeof (buf))))
+ return (p);
+ if ((ulong_t)k.start > (ulong_t)k.end) {
+ (void) snprintf(buf, sizeof (buf),
+ "out of order timestamp %.30s", val0);
+ return (buf);
+ }
+ }
+ }
+ if (delim != '\0')
+ return (tgt);
+
+ (void) memmove(ap, &k, sizeof (*ap));
+ return (NULL);
+}
+
+
+static const char *
+bad_str(const char *estr)
+{
+ static char buf[100+8];
+
+ (void) snprintf(buf, sizeof (buf), "bad \"%.100s\"", estr);
+ return (buf);
+}
+
+
+/*
+ * Parse a set of parameters for an interface.
+ * returns NULL or error message
+ */
+const char *
+parse_parms(char *line,
+ boolean_t safe) /* 1=from secure file */
+{
+#define PARS(str) (strcasecmp(tgt, str) == 0)
+#define PARSEQ(str) (strncasecmp(tgt, str"=", sizeof (str)) == 0)
+/*
+ * This macro checks for conflicting configurations options
+ * For eg one can set either the IS_NO_SOL_OUT flag bit or the IS_SOL_OUT flag
+ * bit, but not both.
+ */
+#define CKF(g, b) {if (0 != (parm.parm_int_state & ((g) & ~(b)))) break; \
+ parm.parm_int_state |= (b); }
+ struct parm parm;
+ struct intnet *intnetp;
+ struct r1net *r1netp;
+ struct tgate *tg;
+ uint32_t addr, mask;
+ char delim, *val0 = 0, *tgt, *val, *p;
+ const char *msg;
+ char buf[PARMS_MAXLINELEN], buf2[PARMS_MAXLINELEN];
+ int i;
+
+
+ /* "subnet=x.y.z.u/mask[,metric]" must be alone on the line */
+ if (strncasecmp(line, "subnet=", sizeof ("subnet=") - 1) == 0 &&
+ *(val = &line[sizeof ("subnet=") -1 ]) != '\0') {
+ if (0 > parse_quote(&val, ",", &delim, buf, sizeof (buf)))
+ return (bad_str(line));
+ intnetp = rtmalloc(sizeof (*intnetp),
+ "parse_parms subnet");
+ intnetp->intnet_metric = 1;
+ if (delim == ',') {
+ intnetp->intnet_metric = (int)strtol(val+1, &p, 0);
+ if (*p != '\0' || intnetp->intnet_metric <= 0 ||
+ val+1 == p ||
+ intnetp->intnet_metric >= HOPCNT_INFINITY) {
+ free(intnetp);
+ return (bad_str(line));
+ }
+ }
+ if (!getnet(buf, &intnetp->intnet_addr,
+ &intnetp->intnet_mask) ||
+ intnetp->intnet_mask == HOST_MASK ||
+ intnetp->intnet_addr == RIP_DEFAULT) {
+ free(intnetp);
+ return (bad_str(line));
+ }
+ intnetp->intnet_addr = htonl(intnetp->intnet_addr);
+ intnetp->intnet_next = intnets;
+ intnets = intnetp;
+ return (NULL);
+ }
+
+ /*
+ * "ripv1_mask=x.y.z.u/mask1,mask2" must be alone on the line.
+ * This requires that x.y.z.u/mask1 be considered a subnet of
+ * x.y.z.u/mask2, as if x.y.z.u/mask2 were a class-full network.
+ */
+ if (!strncasecmp(line, "ripv1_mask=", sizeof ("ripv1_mask=") - 1) &&
+ *(val = &line[sizeof ("ripv1_mask=")-1]) != '\0') {
+ if (0 > parse_quote(&val, ",", &delim, buf, sizeof (buf)) ||
+ delim == '\0')
+ return (bad_str(line));
+ if ((i = (int)strtol(val+1, &p, 0)) <= 0 || i > 32 ||
+ *p != '\0')
+ return (bad_str(line));
+ r1netp = rtmalloc(sizeof (*r1netp), "parse_parms ripv1_mask");
+ r1netp->r1net_mask = HOST_MASK << (32-i);
+ if (!getnet(buf, &r1netp->r1net_net, &r1netp->r1net_match) ||
+ r1netp->r1net_net == RIP_DEFAULT ||
+ r1netp->r1net_mask > r1netp->r1net_match) {
+ free(r1netp);
+ return (bad_str(line));
+ }
+ r1netp->r1net_next = r1nets;
+ r1nets = r1netp;
+ return (NULL);
+ }
+
+ (void) memset(&parm, 0, sizeof (parm));
+ /*
+ * Support of the following for Solaris backward compatibility
+ * norip <ifname>
+ * noripin <ifname>
+ * noripout <ifname>
+ */
+ if (strncasecmp("norip", line, 5) == 0) {
+ char cmd[64], ifname[64];
+ int n;
+
+ n = sscanf(line, "%63s %63s\n", cmd, ifname);
+ if (n != 2) {
+ /* Not enough parameters */
+ return (bad_str(line));
+ }
+
+ /*
+ * Get the interface name and turn on the appropriate
+ * interface flags
+ */
+ (void) strlcpy(parm.parm_name, ifname, sizeof (parm.parm_name));
+ if (strcasecmp("norip", cmd) == 0) {
+ parm.parm_int_state |= IS_NO_RIP;
+ } else if (strcasecmp("noripin", cmd) == 0) {
+ parm.parm_int_state |= IS_NO_RIP_IN;
+ } else if (strcasecmp("noripout", cmd) == 0) {
+ parm.parm_int_state |= IS_NO_RIP_OUT;
+ } else {
+ /* Bad command */
+ return (bad_str(line));
+ }
+ /*
+ * Look for duplication, and if new,
+ * link to the rest of the parm entries.
+ */
+ return (insert_parm(&parm));
+ }
+
+ for (;;) {
+ tgt = line + strspn(line, " ,\n\r");
+ if (*tgt == '\0' || *tgt == '#')
+ break;
+ line = tgt+strcspn(tgt, "= #,\n\r");
+ delim = *line;
+ if (delim == '=') {
+ val0 = ++line;
+ if (0 > parse_quote(&line, " #,", &delim,
+ buf, sizeof (buf)))
+ return (bad_str(tgt));
+ }
+ if (delim != '\0') {
+ for (;;) {
+ *line = '\0';
+ if (delim == '#')
+ break;
+ ++line;
+ if (!isspace(delim) ||
+ ((delim = *line), !isspace(delim)))
+ break;
+ }
+ }
+
+ if (PARSEQ("if")) {
+ if (parm.parm_name[0] != '\0' ||
+ strlen(buf) > IF_NAME_LEN)
+ return (bad_str(tgt));
+ (void) strlcpy(parm.parm_name, buf,
+ sizeof (parm.parm_name));
+
+ } else if (PARSEQ("addr")) {
+ /*
+ * This is a bad idea, because the address based
+ * sets of parameters cannot be checked for
+ * consistency with the interface name parameters.
+ * The parm_net stuff is needed to allow several
+ * -F settings.
+ */
+ if (!getnet(val0, &addr, &mask) ||
+ parm.parm_name[0] != '\0')
+ return (bad_str(tgt));
+ parm.parm_net = addr;
+ parm.parm_mask = mask;
+ parm.parm_name[0] = '\n';
+
+ } else if (PARSEQ("passwd")) {
+ /*
+ * since cleartext passwords are so weak allow
+ * them anywhere
+ */
+ msg = get_passwd(tgt, val0, &parm, RIP_AUTH_PW, 1);
+ if (msg) {
+ *val0 = '\0';
+ return (bad_str(msg));
+ }
+
+ } else if (PARSEQ("md5_passwd")) {
+ msg = get_passwd(tgt, val0, &parm, RIP_AUTH_MD5, safe);
+ if (msg) {
+ *val0 = '\0';
+ return (bad_str(msg));
+ }
+
+ } else if (PARS("no_ag")) {
+ parm.parm_int_state |= (IS_NO_AG | IS_NO_SUPER_AG);
+
+ } else if (PARS("no_host")) {
+ parm.parm_int_state |= IS_NO_HOST;
+
+ } else if (PARS("no_super_ag")) {
+ parm.parm_int_state |= IS_NO_SUPER_AG;
+
+ } else if (PARS("no_ripv1_in")) {
+ parm.parm_int_state |= IS_NO_RIPV1_IN;
+
+ } else if (PARS("no_ripv2_in")) {
+ parm.parm_int_state |= IS_NO_RIPV2_IN;
+
+ } else if (PARS("ripv2_out")) {
+ if (parm.parm_int_state & IS_NO_RIPV2_OUT)
+ return (bad_str(tgt));
+ parm.parm_int_state |= IS_NO_RIPV1_OUT;
+
+ } else if (PARS("ripv2")) {
+ if ((parm.parm_int_state & IS_NO_RIPV2_OUT) ||
+ (parm.parm_int_state & IS_NO_RIPV2_IN))
+ return (bad_str(tgt));
+ parm.parm_int_state |= (IS_NO_RIPV1_IN
+ | IS_NO_RIPV1_OUT);
+
+ } else if (PARS("no_rip")) {
+ CKF(IS_PM_RDISC, IS_NO_RIP);
+
+ } else if (PARS("no_rip_mcast")) {
+ parm.parm_int_state |= IS_NO_RIP_MCAST;
+
+ } else if (PARS("no_rdisc")) {
+ CKF((GROUP_IS_SOL_OUT|GROUP_IS_ADV_OUT), IS_NO_RDISC);
+
+ } else if (PARS("no_solicit")) {
+ CKF(GROUP_IS_SOL_OUT, IS_NO_SOL_OUT);
+
+ } else if (PARS("send_solicit")) {
+ CKF(GROUP_IS_SOL_OUT, IS_SOL_OUT);
+
+ } else if (PARS("no_rdisc_adv")) {
+ CKF(GROUP_IS_ADV_OUT, IS_NO_ADV_OUT);
+
+ } else if (PARS("rdisc_adv")) {
+ CKF(GROUP_IS_ADV_OUT, IS_ADV_OUT);
+
+ } else if (PARS("bcast_rdisc")) {
+ parm.parm_int_state |= IS_BCAST_RDISC;
+
+ } else if (PARS("passive")) {
+ CKF((GROUP_IS_SOL_OUT|GROUP_IS_ADV_OUT), IS_NO_RDISC);
+ parm.parm_int_state |= IS_NO_RIP | IS_PASSIVE;
+
+ } else if (PARSEQ("rdisc_pref")) {
+ if (parm.parm_rdisc_pref != 0 ||
+ (parm.parm_rdisc_pref = (int)strtol(buf, &p, 0),
+ *p != '\0') || (buf == p))
+ return (bad_str(tgt));
+
+ } else if (PARS("pm_rdisc")) {
+ if (IS_RIP_OUT_OFF(parm.parm_int_state))
+ return (bad_str(tgt));
+ parm.parm_int_state |= IS_PM_RDISC;
+
+ } else if (PARSEQ("rdisc_interval")) {
+ if (parm.parm_rdisc_int != 0 ||
+ (parm.parm_rdisc_int = (int)strtoul(buf, &p, 0),
+ *p != '\0') || (buf == p) ||
+ parm.parm_rdisc_int < MIN_MAXADVERTISEINTERVAL ||
+ parm.parm_rdisc_int > MAX_MAXADVERTISEINTERVAL)
+ return (bad_str(tgt));
+
+ } else if (PARSEQ("fake_default")) {
+ if (parm.parm_d_metric != 0 ||
+ IS_RIP_OUT_OFF(parm.parm_int_state) ||
+ (parm.parm_d_metric = (int)strtoul(buf, &p, 0),
+ *p != '\0') || (buf == p) ||
+ parm.parm_d_metric > HOPCNT_INFINITY-1)
+ return (bad_str(tgt));
+
+ } else if (PARSEQ("trust_gateway")) {
+ /* look for trust_gateway=x.y.z|net/mask|...) */
+ p = buf;
+ if (0 > parse_quote(&p, "|", &delim, buf2,
+ sizeof (buf2)) || !gethost(buf2, &addr))
+ return (bad_str(tgt));
+ tg = rtmalloc(sizeof (*tg),
+ "parse_parms trust_gateway");
+ (void) memset(tg, 0, sizeof (*tg));
+ tg->tgate_addr = addr;
+ i = 0;
+ /* The default is to trust all routes. */
+ while (delim == '|') {
+ p++;
+ if (i >= MAX_TGATE_NETS ||
+ 0 > parse_quote(&p, "|", &delim, buf2,
+ sizeof (buf2)) ||
+ !getnet(buf2, &tg->tgate_nets[i].net,
+ &tg->tgate_nets[i].mask) ||
+ tg->tgate_nets[i].net == RIP_DEFAULT ||
+ tg->tgate_nets[i].mask == 0) {
+ free(tg);
+ return (bad_str(tgt));
+ }
+ i++;
+ }
+ tg->tgate_next = tgates;
+ tgates = tg;
+ parm.parm_int_state |= IS_DISTRUST;
+
+ } else if (PARS("redirect_ok")) {
+ parm.parm_int_state |= IS_REDIRECT_OK;
+
+ } else if (PARSEQ("rip_neighbor")) {
+ if (parm.parm_name[0] == '\0' ||
+ gethost(buf, &parm.parm_ripout_addr) != 1)
+ return (bad_str(tgt));
+
+ } else {
+ return (bad_str(tgt)); /* error */
+ }
+ }
+
+ return (insert_parm(&parm));
+#undef PARS
+#undef PARSEQ
+#undef CKF
+}
+
+
+/*
+ * Insert parameter specifications into the parms list. Returns NULL if
+ * successful, or an error message otherwise.
+ */
+const char *
+insert_parm(struct parm *new)
+{
+ struct parm *parmp, **parmpp;
+ int i, num_passwds;
+
+ /* set implicit values */
+ if (new->parm_int_state & (IS_NO_ADV_IN|IS_NO_SOL_OUT))
+ new->parm_int_state |= IS_NO_ADV_IN|IS_NO_SOL_OUT;
+
+ for (i = num_passwds = 0; i < MAX_AUTH_KEYS; i++) {
+ if (new->parm_auth[i].type != RIP_AUTH_NONE)
+ num_passwds++;
+ }
+
+ /* compare with existing sets of parameters */
+ for (parmpp = &parms; (parmp = *parmpp) != 0;
+ parmpp = &parmp->parm_next) {
+ if (strcmp(new->parm_name, parmp->parm_name) != 0)
+ continue;
+ if (!on_net(htonl(parmp->parm_net), new->parm_net,
+ new->parm_mask) &&
+ !on_net(htonl(new->parm_net), parmp->parm_net,
+ parmp->parm_mask))
+ continue;
+
+ for (i = 0; i < MAX_AUTH_KEYS; i++) {
+ if (parmp->parm_auth[i].type != RIP_AUTH_NONE)
+ num_passwds++;
+ }
+ if (num_passwds > MAX_AUTH_KEYS)
+ return ("too many conflicting passwords");
+
+ if ((0 != (new->parm_int_state & GROUP_IS_SOL_OUT) &&
+ 0 != (parmp->parm_int_state & GROUP_IS_SOL_OUT) &&
+ 0 != ((new->parm_int_state ^ parmp->parm_int_state) &&
+ GROUP_IS_SOL_OUT)) ||
+ (0 != (new->parm_int_state & GROUP_IS_ADV_OUT) &&
+ 0 != (parmp->parm_int_state & GROUP_IS_ADV_OUT) &&
+ 0 != ((new->parm_int_state ^ parmp->parm_int_state) &&
+ GROUP_IS_ADV_OUT)) ||
+ (new->parm_rdisc_pref != 0 &&
+ parmp->parm_rdisc_pref != 0 &&
+ new->parm_rdisc_pref != parmp->parm_rdisc_pref) ||
+ (new->parm_rdisc_int != 0 &&
+ parmp->parm_rdisc_int != 0 &&
+ new->parm_rdisc_int != parmp->parm_rdisc_int)) {
+ return ("conflicting, duplicate router discovery"
+ " parameters");
+
+ }
+
+ if (new->parm_d_metric != 0 && parmp->parm_d_metric != 0 &&
+ new->parm_d_metric != parmp->parm_d_metric) {
+ return ("conflicting, duplicate poor man's router"
+ " discovery or fake default metric");
+ }
+ }
+
+ /*
+ * link new entry on the list so that when the entries are scanned,
+ * they affect the result in the order the operator specified.
+ */
+ parmp = rtmalloc(sizeof (*parmp), "insert_parm");
+ (void) memcpy(parmp, new, sizeof (*parmp));
+ *parmpp = parmp;
+
+ return (NULL);
+}
+
+int /* 0=bad */
+gethost(char *name, in_addr_t *addrp)
+{
+ struct hostent *hp;
+ struct in_addr in;
+
+
+ /*
+ * Try for a number first. This avoids hitting the name
+ * server which might be sick because routing is.
+ */
+ if ((in.s_addr = inet_addr(name)) != (in_addr_t)-1) {
+ /*
+ * get a good number, but check that it makes some
+ * sense.
+ */
+ if ((ntohl(in.s_addr) >> 24) == 0 ||
+ (ntohl(in.s_addr) >> 24) == 0xff)
+ return (0);
+ *addrp = in.s_addr;
+ return (1);
+ }
+
+ hp = gethostbyname(name);
+ if (hp != NULL) {
+ (void) memcpy(addrp, hp->h_addr, sizeof (*addrp));
+ return (1);
+ }
+
+ return (0);
+}
+
+
+static void
+addroutefordefault(in_addr_t dst, in_addr_t gate, in_addr_t mask,
+ uint32_t metric, uint16_t rts_flags)
+{
+ struct rt_spare new;
+ struct interface *ifp;
+ uint16_t rt_newstate = RS_STATIC;
+
+
+ ifp = iflookup(gate);
+ if (ifp == NULL) {
+ msglog("unreachable gateway %s in "PATH_GATEWAYS,
+ naddr_ntoa(gate));
+ return;
+ }
+
+ /* Get the ifp of the physical interface */
+ ifp = ifwithname(ifp->int_phys->phyi_name);
+ trace_misc("addroutefordefault: found interface %s", ifp->int_name);
+
+ (void) memset(&new, 0, sizeof (new));
+ new.rts_ifp = ifp;
+ new.rts_router = gate;
+ new.rts_gate = gate;
+ new.rts_metric = metric;
+ new.rts_time = now.tv_sec;
+ new.rts_flags = rts_flags;
+ new.rts_origin = RO_FILE;
+
+ input_route(dst, mask, &new, NULL, rt_newstate);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.routed/pathnames.h b/usr/src/cmd/cmd-inet/usr.sbin/in.routed/pathnames.h
new file mode 100644
index 0000000000..103577d7e3
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.routed/pathnames.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2001-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 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 acknowledgment:
+ * 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.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/5/93
+ *
+ * $FreeBSD:src/sbin/routed/pathnames.h,v 1.6 2000/08/11 08:24:38 sheldonh Exp $
+ */
+
+#ifndef _PATHNAMES_H
+#define _PATHNAMES_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * All remotely requested trace files must either start with this prefix
+ * or be the same as the tracefile specified when the daemon was started.
+ * If this is a directory, routed will create log files in it. That
+ * might be a security problem.
+ *
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define PATH_GATEWAYS "/etc/gateways"
+#define PATH_TRACE "/var/log/in.routed.trace"
+#define PATH_PID "/var/run/in.routed.pid"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PATHNAMES_H */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.routed/radix.c b/usr/src/cmd/cmd-inet/usr.sbin/in.routed/radix.c
new file mode 100644
index 0000000000..c57327a464
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.routed/radix.c
@@ -0,0 +1,999 @@
+/*
+ * Copyright 2001-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 1988, 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 acknowledgment:
+ * 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.
+ *
+ * @(#)radix.c 8.4 (Berkeley) 11/2/94
+ *
+ * $FreeBSD: src/sbin/routed/radix.c,v 1.6 2000/08/11 08:24:38 sheldonh Exp $
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Routines to build and maintain radix trees for routing lookups.
+ */
+
+#include "defs.h"
+
+static const size_t max_keylen = sizeof (struct sockaddr_in);
+static struct radix_mask *rn_mkfreelist;
+static struct radix_node_head *mask_rnhead;
+static uint8_t *rn_zeros, *rn_ones, *addmask_key;
+
+#define rn_masktop (mask_rnhead->rnh_treetop)
+
+static boolean_t rn_satisfies_leaf(uint8_t *, struct radix_node *, int);
+
+static boolean_t rn_refines(void *, void *);
+
+static struct radix_node
+ *rn_addmask(void *, uint_t, uint_t),
+ *rn_addroute(void *, void *, struct radix_node_head *,
+ struct radix_node [2]),
+ *rn_delete(void *, void *, struct radix_node_head *),
+ *rn_insert(void *, struct radix_node_head *, boolean_t *,
+ struct radix_node [2]),
+ *rn_match(void *, struct radix_node_head *),
+ *rn_newpair(void *, uint_t, struct radix_node[2]),
+ *rn_search(void *, struct radix_node *),
+ *rn_search_m(void *, struct radix_node *, void *);
+
+static struct radix_node *rn_lookup(void *, void *, struct radix_node_head *);
+
+#ifdef DEBUG
+#define DBGMSG(x) msglog x
+#else
+#define DBGMSG(x) (void) 0
+#endif
+
+/*
+ * The data structure for the keys is a radix tree with one way
+ * branching removed. The index rn_b at an internal node n represents a bit
+ * position to be tested. The tree is arranged so that all descendants
+ * of a node n have keys whose bits all agree up to position rn_b - 1.
+ * (We say the index of n is rn_b.)
+ *
+ * There is at least one descendant which has a one bit at position rn_b,
+ * and at least one with a zero there.
+ *
+ * A route is determined by a pair of key and mask. We require that the
+ * bit-wise logical and of the key and mask to be the key.
+ * We define the index of a route to associated with the mask to be
+ * the first bit number in the mask where 0 occurs (with bit number 0
+ * representing the highest order bit).
+ *
+ * We say a mask is normal if every bit is 0, past the index of the mask.
+ * If a node n has a descendant (k, m) with index(m) == index(n) == rn_b,
+ * and m is a normal mask, then the route applies to every descendant of n.
+ * If the index(m) < rn_b, this implies the trailing last few bits of k
+ * before bit b are all 0, (and hence consequently true of every descendant
+ * of n), so the route applies to all descendants of the node as well.
+ *
+ * Similar logic shows that a non-normal mask m such that
+ * index(m) <= index(n) could potentially apply to many children of n.
+ * Thus, for each non-host route, we attach its mask to a list at an internal
+ * node as high in the tree as we can go.
+ *
+ * The present version of the code makes use of normal routes in short-
+ * circuiting an explict mask and compare operation when testing whether
+ * a key satisfies a normal route, and also in remembering the unique leaf
+ * that governs a subtree.
+ */
+
+static struct radix_node *
+rn_search(void *v_arg, struct radix_node *head)
+{
+ struct radix_node *x;
+ uint8_t *v;
+
+ for (x = head, v = v_arg; x->rn_b >= 0; ) {
+ if (x->rn_bmask & v[x->rn_off])
+ x = x->rn_r;
+ else
+ x = x->rn_l;
+ }
+ return (x);
+}
+
+static struct radix_node *
+rn_search_m(void *v_arg, struct radix_node *head, void *m_arg)
+{
+ struct radix_node *x;
+ uint8_t *v = v_arg, *m = m_arg;
+
+ for (x = head; x->rn_b >= 0; ) {
+ if (x->rn_bmask & m[x->rn_off] & v[x->rn_off])
+ x = x->rn_r;
+ else
+ x = x->rn_l;
+ }
+ return (x);
+}
+
+/*
+ * Returns true if there are no bits set in n_arg that are zero in
+ * m_arg and the masks aren't equal. In other words, it returns true
+ * when m_arg is a finer-granularity netmask -- it represents a subset
+ * of the destinations implied by n_arg.
+ */
+static boolean_t
+rn_refines(void* m_arg, void *n_arg)
+{
+ uint8_t *m = m_arg, *n = n_arg;
+ uint8_t *lim;
+ boolean_t masks_are_equal = _B_TRUE;
+
+ lim = n + sizeof (struct sockaddr);
+
+ while (n < lim) {
+ if (*n & ~(*m))
+ return (_B_FALSE);
+ if (*n++ != *m++)
+ masks_are_equal = _B_FALSE;
+ }
+ return (!masks_are_equal);
+}
+
+static struct radix_node *
+rn_lookup(void *v_arg, void *m_arg, struct radix_node_head *head)
+{
+ struct radix_node *x;
+ uint8_t *netmask = NULL;
+
+ if (m_arg) {
+ if ((x = rn_addmask(m_arg, 1, head->rnh_treetop->rn_off)) ==
+ NULL) {
+ DBGMSG(("rn_lookup: failed to add mask"));
+ return (NULL);
+ }
+ netmask = x->rn_key;
+ }
+ x = rn_match(v_arg, head);
+ if (x && netmask) {
+ while (x && x->rn_mask != netmask)
+ x = x->rn_dupedkey;
+ }
+ return (x);
+}
+
+/*
+ * Returns true if address 'trial' has no bits differing from the
+ * leaf's key when compared under the leaf's mask. In other words,
+ * returns true when 'trial' matches leaf.
+ */
+static boolean_t
+rn_satisfies_leaf(uint8_t *trial,
+ struct radix_node *leaf,
+ int skip)
+{
+ uint8_t *cp = trial, *cp2 = leaf->rn_key, *cp3 = leaf->rn_mask;
+ uint8_t *cplim;
+ size_t length;
+
+ length = sizeof (struct sockaddr);
+
+ if (cp3 == NULL)
+ cp3 = rn_ones;
+ cplim = cp + length;
+ cp3 += skip;
+ cp2 += skip;
+ for (cp += skip; cp < cplim; cp++, cp2++, cp3++)
+ if ((*cp ^ *cp2) & *cp3)
+ return (_B_FALSE);
+ return (_B_TRUE);
+}
+
+static struct radix_node *
+rn_match(void *v_arg, struct radix_node_head *head)
+{
+ uint8_t *v = v_arg;
+ struct radix_node *t = head->rnh_treetop, *x;
+ uint8_t *cp = v, *cp2;
+ uint8_t *cplim;
+ struct radix_node *saved_t, *top = t;
+ uint_t off = t->rn_off, vlen, matched_off;
+ int test, b, rn_b;
+
+ vlen = sizeof (struct sockaddr);
+
+ /*
+ * Open code rn_search(v, top) to avoid overhead of extra
+ * subroutine call.
+ */
+ for (; t->rn_b >= 0; ) {
+ if (t->rn_bmask & cp[t->rn_off])
+ t = t->rn_r;
+ else
+ t = t->rn_l;
+ }
+
+ cp += off;
+ cp2 = t->rn_key + off;
+ cplim = v + vlen;
+ for (; cp < cplim; cp++, cp2++)
+ if (*cp != *cp2)
+ goto found_difference_with_key;
+ /*
+ * This extra grot is in case we are explicitly asked
+ * to look up the default. Ugh!
+ * Or 255.255.255.255
+ *
+ * In this case, we have a complete match of the key. Unless
+ * the node is one of the roots, we are finished.
+ * If it is the zeros root, then take what we have, prefering
+ * any real data.
+ * If it is the ones root, then pretend the target key was followed
+ * by a byte of zeros.
+ */
+ if (!(t->rn_flags & RNF_ROOT))
+ return (t); /* not a root */
+ if (t->rn_dupedkey) {
+ t = t->rn_dupedkey;
+ return (t); /* have some real data */
+ }
+ if (*(cp-1) == 0)
+ return (t); /* not the ones root */
+ b = 0; /* fake a zero after 255.255.255.255 */
+ goto calculated_differing_bit;
+found_difference_with_key:
+ test = (*cp ^ *cp2) & 0xff; /* find first bit that differs */
+ for (b = 7; (test >>= 1) > 0; )
+ b--;
+calculated_differing_bit:
+ matched_off = cp - v;
+ b += matched_off << 3;
+ rn_b = -1 - b;
+ /*
+ * If there is a host route in a duped-key chain, it will be first.
+ */
+ if ((saved_t = t)->rn_mask == NULL)
+ t = t->rn_dupedkey;
+ for (; t; t = t->rn_dupedkey) {
+ /*
+ * Even if we don't match exactly as a host,
+ * we may match if the leaf we wound up at is
+ * a route to a net.
+ */
+ if (t->rn_flags & RNF_NORMAL) {
+ if (rn_b <= t->rn_b)
+ return (t);
+ } else if (rn_satisfies_leaf(v, t, matched_off)) {
+ return (t);
+ }
+ }
+ t = saved_t;
+ /* start searching up the tree */
+ do {
+ struct radix_mask *m;
+ t = t->rn_p;
+ if ((m = t->rn_mklist) != NULL) {
+ /*
+ * If non-contiguous masks ever become important
+ * we can restore the masking and open coding of
+ * the search and satisfaction test and put the
+ * calculation of "off" back before the "do".
+ */
+ do {
+ if (m->rm_flags & RNF_NORMAL) {
+ if (rn_b <= m->rm_b)
+ return (m->rm_leaf);
+ } else {
+ off = MIN(t->rn_off, matched_off);
+ x = rn_search_m(v, t, m->rm_mask);
+ while (x != NULL &&
+ x->rn_mask != m->rm_mask)
+ x = x->rn_dupedkey;
+ if (x != NULL &&
+ rn_satisfies_leaf(v, x, off))
+ return (x);
+ }
+ } while ((m = m->rm_mklist) != NULL);
+ }
+ } while (t != top);
+ return (NULL);
+}
+
+#ifdef RN_DEBUG
+int rn_nodenum;
+struct radix_node *rn_clist;
+int rn_saveinfo;
+boolean_t rn_debug = 1;
+#endif
+
+static struct radix_node *
+rn_newpair(void *v, uint_t b, struct radix_node nodes[2])
+{
+ struct radix_node *tt = nodes, *t = tt + 1;
+
+ t->rn_b = b;
+ t->rn_bmask = 0x80 >> (b & 7);
+ t->rn_l = tt;
+ t->rn_off = b >> 3;
+ tt->rn_b = -1;
+ tt->rn_key = v;
+ tt->rn_p = t;
+ tt->rn_flags = t->rn_flags = RNF_ACTIVE;
+#ifdef RN_DEBUG
+ tt->rn_info = rn_nodenum++;
+ t->rn_info = rn_nodenum++;
+ tt->rn_twin = t;
+ tt->rn_ybro = rn_clist;
+ rn_clist = tt;
+#endif
+ return (t);
+}
+
+static struct radix_node *
+rn_insert(void* v_arg, struct radix_node_head *head, boolean_t *dupentry,
+ struct radix_node nodes[2])
+{
+ uint8_t *v = v_arg;
+ struct radix_node *top = head->rnh_treetop;
+ uint_t head_off = top->rn_off, vlen;
+ struct radix_node *t = rn_search(v_arg, top);
+ uint8_t *cp = v + head_off, b;
+ struct radix_node *tt;
+
+ vlen = sizeof (struct sockaddr);
+
+ /*
+ * Find first bit at which v and t->rn_key differ
+ */
+ {
+ uint8_t *cp2 = t->rn_key + head_off;
+ uint8_t cmp_res;
+ uint8_t *cplim = v + vlen;
+
+ while (cp < cplim)
+ if (*cp2++ != *cp++)
+ goto found_differing_byte;
+ /* handle adding 255.255.255.255 */
+ if (!(t->rn_flags & RNF_ROOT) || *(cp2-1) == 0) {
+ *dupentry = _B_TRUE;
+ return (t);
+ }
+found_differing_byte:
+ *dupentry = _B_FALSE;
+ cmp_res = cp[-1] ^ cp2[-1];
+ for (b = (cp - v) << 3; cmp_res != 0; b--)
+ cmp_res >>= 1;
+ }
+ {
+ struct radix_node *p, *x = top;
+ cp = v;
+ do {
+ p = x;
+ if (cp[x->rn_off] & x->rn_bmask)
+ x = x->rn_r;
+ else
+ x = x->rn_l;
+ } while (b > (unsigned)x->rn_b);
+#ifdef RN_DEBUG
+ if (rn_debug) {
+ msglog("rn_insert: Going In:");
+ traverse(p);
+ }
+#endif
+ t = rn_newpair(v_arg, b, nodes);
+ tt = t->rn_l;
+ if (!(cp[p->rn_off] & p->rn_bmask))
+ p->rn_l = t;
+ else
+ p->rn_r = t;
+ x->rn_p = t; /* frees x, p as temp vars below */
+ t->rn_p = p;
+ if (!(cp[t->rn_off] & t->rn_bmask)) {
+ t->rn_r = x;
+ } else {
+ t->rn_r = tt;
+ t->rn_l = x;
+ }
+#ifdef RN_DEBUG
+ if (rn_debug) {
+ msglog("rn_insert: Coming Out:");
+ traverse(p);
+ }
+#endif
+ }
+ return (tt);
+}
+
+static struct radix_node *
+rn_addmask(void *n_arg, uint_t search, uint_t skip)
+{
+ uint8_t *netmask = n_arg;
+ struct radix_node *x;
+ uint8_t *cp, *cplim;
+ int b = 0, mlen, j, m0;
+ boolean_t maskduplicated;
+ struct radix_node *saved_x;
+ static int last_zeroed = 0;
+
+ mlen = sizeof (struct sockaddr);
+ if (skip == 0)
+ skip = 1;
+ if (mlen <= skip)
+ return (mask_rnhead->rnh_nodes);
+ if (skip > 1)
+ (void) memmove(addmask_key + 1, rn_ones + 1, skip - 1);
+ if ((m0 = mlen) > skip)
+ (void) memmove(addmask_key + skip, netmask + skip, mlen - skip);
+ /*
+ * Trim trailing zeroes.
+ */
+ for (cp = addmask_key + mlen; (cp > addmask_key) && cp[-1] == 0; )
+ cp--;
+ mlen = cp - addmask_key;
+ if (mlen <= skip) {
+ if (m0 >= last_zeroed)
+ last_zeroed = mlen;
+ return (mask_rnhead->rnh_nodes);
+ }
+ if (m0 < last_zeroed)
+ (void) memset(addmask_key + m0, 0, last_zeroed - m0);
+ *addmask_key = last_zeroed = mlen;
+ x = rn_search(addmask_key, rn_masktop);
+ if (memcmp(addmask_key, x->rn_key, mlen) != 0)
+ x = NULL;
+ if (x != NULL || search != 0)
+ return (x);
+ x = rtmalloc(max_keylen + 2*sizeof (*x), "rn_addmask");
+ saved_x = x;
+ (void) memset(x, 0, max_keylen + 2 * sizeof (*x));
+ netmask = cp = (uint8_t *)(x + 2);
+ (void) memmove(cp, addmask_key, mlen);
+ x = rn_insert(cp, mask_rnhead, &maskduplicated, x);
+ if (maskduplicated) {
+#ifdef DEBUG
+ logbad(1, "rn_addmask: mask impossibly already in tree");
+#else
+ msglog("rn_addmask: mask impossibly already in tree");
+#endif
+ free(saved_x);
+ return (x);
+ }
+ /*
+ * Calculate index of mask, and check for normalcy.
+ */
+ cplim = netmask + mlen;
+ x->rn_flags |= RNF_NORMAL;
+ for (cp = netmask + skip; (cp < cplim) && *cp == 0xff; )
+ cp++;
+ if (cp != cplim) {
+ for (j = 0x80; (j & *cp) != 0; j >>= 1)
+ b++;
+ if (*cp != (0xFF & ~(0xFF >> b)) || cp != (cplim - 1))
+ x->rn_flags &= ~RNF_NORMAL;
+ }
+ b += (cp - netmask) << 3;
+ x->rn_b = -1 - b;
+ return (x);
+}
+
+static boolean_t /* Note: arbitrary ordering for non-contiguous masks */
+rn_lexobetter(void *m_arg, void *n_arg)
+{
+ uint8_t *mp = m_arg, *np = n_arg, *lim;
+
+ lim = mp + sizeof (struct sockaddr);
+ while (mp < lim)
+ if (*mp++ > *np++)
+ return (_B_TRUE);
+ return (_B_FALSE);
+}
+
+static struct radix_mask *
+rn_new_radix_mask(struct radix_node *tt,
+ struct radix_mask *next)
+{
+ struct radix_mask *m;
+
+ MKGet(m);
+ if (m == NULL) {
+#ifdef DEBUG
+ logbad(1, "Mask for route not entered");
+#else
+ msglog("Mask for route not entered");
+#endif
+ return (NULL);
+ }
+ (void) memset(m, 0, sizeof (*m));
+ m->rm_b = tt->rn_b;
+ m->rm_flags = tt->rn_flags;
+ if (tt->rn_flags & RNF_NORMAL)
+ m->rm_leaf = tt;
+ else
+ m->rm_mask = tt->rn_mask;
+ m->rm_mklist = next;
+ tt->rn_mklist = m;
+ return (m);
+}
+
+static struct radix_node *
+rn_addroute(void *v_arg, void *n_arg, struct radix_node_head *head,
+ struct radix_node treenodes[2])
+{
+ uint8_t *v = v_arg, *netmask = n_arg;
+ struct radix_node *t, *x = 0, *tt;
+ struct radix_node *saved_tt, *top = head->rnh_treetop;
+ short b = 0, b_leaf = 0;
+ boolean_t keyduplicated;
+ uint8_t *mmask;
+ struct radix_mask *m, **mp;
+
+ /*
+ * In dealing with non-contiguous masks, there may be
+ * many different routes which have the same mask.
+ * We will find it useful to have a unique pointer to
+ * the mask to speed avoiding duplicate references at
+ * nodes and possibly save time in calculating indices.
+ */
+ if (netmask) {
+ if ((x = rn_addmask(netmask, 0, top->rn_off)) == NULL) {
+ DBGMSG(("rn_addroute: addmask failed"));
+ return (NULL);
+ }
+ b_leaf = x->rn_b;
+ b = -1 - x->rn_b;
+ netmask = x->rn_key;
+ }
+ /*
+ * Deal with duplicated keys: attach node to previous instance
+ */
+ saved_tt = tt = rn_insert(v, head, &keyduplicated, treenodes);
+ if (keyduplicated) {
+ for (t = tt; tt; t = tt, tt = tt->rn_dupedkey) {
+ if (tt->rn_mask == netmask) {
+ DBGMSG(("rn_addroute: duplicated route and "
+ "mask"));
+ return (NULL);
+ }
+ if (netmask == NULL ||
+ (tt->rn_mask &&
+ ((b_leaf < tt->rn_b) ||
+ rn_refines(netmask, tt->rn_mask) ||
+ rn_lexobetter(netmask, tt->rn_mask))))
+ break;
+ }
+ /*
+ * If the mask is not duplicated, we wouldn't
+ * find it among possible duplicate key entries
+ * anyway, so the above test doesn't hurt.
+ *
+ * We sort the masks for a duplicated key the same way as
+ * in a masklist -- most specific to least specific.
+ * This may require the unfortunate nuisance of relocating
+ * the head of the list.
+ */
+ if (tt == saved_tt) {
+ struct radix_node *xx = x;
+ /* link in at head of list */
+ (tt = treenodes)->rn_dupedkey = t;
+ tt->rn_flags = t->rn_flags;
+ tt->rn_p = x = t->rn_p;
+ if (x->rn_l == t)
+ x->rn_l = tt;
+ else
+ x->rn_r = tt;
+ saved_tt = tt;
+ x = xx;
+ } else {
+ (tt = treenodes)->rn_dupedkey = t->rn_dupedkey;
+ t->rn_dupedkey = tt;
+ }
+#ifdef RN_DEBUG
+ t = tt + 1;
+ tt->rn_info = rn_nodenum++;
+ t->rn_info = rn_nodenum++;
+ tt->rn_twin = t;
+ tt->rn_ybro = rn_clist;
+ rn_clist = tt;
+#endif
+ tt->rn_key = v;
+ tt->rn_b = -1;
+ tt->rn_flags = RNF_ACTIVE;
+ }
+ /*
+ * Put mask in tree.
+ */
+ if (netmask) {
+ tt->rn_mask = netmask;
+ tt->rn_b = x->rn_b;
+ tt->rn_flags |= x->rn_flags & RNF_NORMAL;
+ }
+ t = saved_tt->rn_p;
+ if (keyduplicated)
+ goto key_already_in_tree;
+ b_leaf = -1 - t->rn_b;
+ if (t->rn_r == saved_tt)
+ x = t->rn_l;
+ else
+ x = t->rn_r;
+ /* Promote general routes from below */
+ if (x->rn_b < 0) {
+ for (mp = &t->rn_mklist; x; x = x->rn_dupedkey)
+ if (x->rn_mask != NULL && (x->rn_b >= b_leaf) &&
+ x->rn_mklist == NULL) {
+ if ((*mp = m = rn_new_radix_mask(x, 0)) != NULL)
+ mp = &m->rm_mklist;
+ }
+ } else if (x->rn_mklist) {
+ /*
+ * Skip over masks whose index is > that of new node
+ */
+ for (mp = &x->rn_mklist; (m = *mp) != NULL; mp = &m->rm_mklist)
+ if (m->rm_b >= b_leaf)
+ break;
+ t->rn_mklist = m;
+ *mp = 0;
+ }
+key_already_in_tree:
+ /* Add new route to highest possible ancestor's list */
+ if ((netmask == NULL) || (b > t->rn_b)) {
+ return (tt); /* can't lift at all */
+ }
+ b_leaf = tt->rn_b;
+ do {
+ x = t;
+ t = t->rn_p;
+ } while (b <= t->rn_b && x != top);
+ /*
+ * Search through routes associated with node to
+ * insert new route according to index.
+ * Need same criteria as when sorting dupedkeys to avoid
+ * double loop on deletion.
+ */
+ for (mp = &x->rn_mklist; (m = *mp) != NULL; mp = &m->rm_mklist) {
+ if (m->rm_b < b_leaf)
+ continue;
+ if (m->rm_b > b_leaf)
+ break;
+ if (m->rm_flags & RNF_NORMAL) {
+ mmask = m->rm_leaf->rn_mask;
+ if (tt->rn_flags & RNF_NORMAL) {
+#ifdef DEBUG
+ logbad(1, "Non-unique normal route, mask "
+ "not entered");
+#else
+ msglog("Non-unique normal route, mask "
+ "not entered");
+#endif
+ return (tt);
+ }
+ } else
+ mmask = m->rm_mask;
+ if (mmask == netmask) {
+ m->rm_refs++;
+ tt->rn_mklist = m;
+ return (tt);
+ }
+ if (rn_refines(netmask, mmask) || rn_lexobetter(netmask, mmask))
+ break;
+ }
+ *mp = rn_new_radix_mask(tt, *mp);
+ return (tt);
+}
+
+static struct radix_node *
+rn_delete(void *v_arg, void *netmask_arg, struct radix_node_head *head)
+{
+ struct radix_node *t, *p, *x, *tt;
+ struct radix_mask *m, *saved_m, **mp;
+ struct radix_node *dupedkey, *saved_tt, *top;
+ uint8_t *v, *netmask;
+ int b;
+ uint_t head_off, vlen;
+
+ v = v_arg;
+ netmask = netmask_arg;
+ x = head->rnh_treetop;
+ tt = rn_search(v, x);
+ head_off = x->rn_off;
+ vlen = sizeof (struct sockaddr);
+ saved_tt = tt;
+ top = x;
+ if (tt == NULL ||
+ memcmp(v + head_off, tt->rn_key + head_off, vlen - head_off) != 0) {
+ DBGMSG(("rn_delete: unable to locate route to delete"));
+ return (NULL);
+ }
+ /*
+ * Delete our route from mask lists.
+ */
+ if (netmask) {
+ if ((x = rn_addmask(netmask, 1, head_off)) == NULL) {
+ DBGMSG(("rn_delete: cannot add mask"));
+ return (NULL);
+ }
+ netmask = x->rn_key;
+ while (tt->rn_mask != netmask)
+ if ((tt = tt->rn_dupedkey) == NULL) {
+ DBGMSG(("rn_delete: cannot locate mask"));
+ return (NULL);
+ }
+ }
+ if (tt->rn_mask == NULL || (saved_m = m = tt->rn_mklist) == NULL)
+ goto annotation_removed;
+ if (tt->rn_flags & RNF_NORMAL) {
+ if (m->rm_leaf != tt || m->rm_refs > 0) {
+#ifdef DEBUG
+ logbad(1, "rn_delete: inconsistent annotation");
+#else
+ msglog("rn_delete: inconsistent annotation");
+#endif
+ return (NULL); /* dangling ref could cause disaster */
+ }
+ } else {
+ if (m->rm_mask != tt->rn_mask) {
+#ifdef DEBUG
+ logbad(1, "rn_delete: inconsistent annotation");
+#else
+ msglog("rn_delete: inconsistent annotation");
+#endif
+ goto annotation_removed;
+ }
+ if (--m->rm_refs >= 0)
+ goto annotation_removed;
+ }
+ b = -1 - tt->rn_b;
+ t = saved_tt->rn_p;
+ if (b > t->rn_b)
+ goto annotation_removed; /* Wasn't lifted at all */
+ do {
+ x = t;
+ t = t->rn_p;
+ } while (b <= t->rn_b && x != top);
+ for (mp = &x->rn_mklist; (m = *mp) != NULL; mp = &m->rm_mklist)
+ if (m == saved_m) {
+ *mp = m->rm_mklist;
+ MKFree(m);
+ break;
+ }
+ if (m == NULL) {
+#ifdef DEBUG
+ logbad(1, "rn_delete: couldn't find our annotation");
+#else
+ msglog("rn_delete: couldn't find our annotation");
+#endif
+ if (tt->rn_flags & RNF_NORMAL)
+ return (NULL); /* Dangling ref to us */
+ }
+annotation_removed:
+ /*
+ * Eliminate us from tree
+ */
+ if (tt->rn_flags & RNF_ROOT) {
+ DBGMSG(("rn_delete: cannot delete root"));
+ return (NULL);
+ }
+#ifdef RN_DEBUG
+ /* Get us out of the creation list */
+ for (t = rn_clist; t && t->rn_ybro != tt; t = t->rn_ybro) {}
+ if (t != NULL)
+ t->rn_ybro = tt->rn_ybro;
+#endif
+ t = tt->rn_p;
+ if ((dupedkey = saved_tt->rn_dupedkey) != NULL) {
+ if (tt == saved_tt) {
+ x = dupedkey;
+ x->rn_p = t;
+ if (t->rn_l == tt)
+ t->rn_l = x;
+ else
+ t->rn_r = x;
+ } else {
+ for (x = p = saved_tt; p && p->rn_dupedkey != tt; )
+ p = p->rn_dupedkey;
+ if (p != NULL) {
+ p->rn_dupedkey = tt->rn_dupedkey;
+ } else {
+#ifdef DEBUG
+ logbad(1, "rn_delete: couldn't find us");
+#else
+ msglog("rn_delete: couldn't find us");
+#endif
+ }
+ }
+ t = tt + 1;
+ if (t->rn_flags & RNF_ACTIVE) {
+#ifndef RN_DEBUG
+ *++x = *t;
+ p = t->rn_p;
+#else
+ b = t->rn_info;
+ *++x = *t;
+ t->rn_info = b;
+ p = t->rn_p;
+#endif
+ if (p->rn_l == t)
+ p->rn_l = x;
+ else
+ p->rn_r = x;
+ x->rn_l->rn_p = x;
+ x->rn_r->rn_p = x;
+ }
+ goto out;
+ }
+ if (t->rn_l == tt)
+ x = t->rn_r;
+ else
+ x = t->rn_l;
+ p = t->rn_p;
+ if (p->rn_r == t)
+ p->rn_r = x;
+ else
+ p->rn_l = x;
+ x->rn_p = p;
+ /*
+ * Demote routes attached to us.
+ */
+ if (t->rn_mklist) {
+ if (x->rn_b >= 0) {
+ for (mp = &x->rn_mklist; (m = *mp) != NULL; )
+ mp = &m->rm_mklist;
+ *mp = t->rn_mklist;
+ } else {
+ /*
+ * If there are any key,mask pairs in a sibling
+ * duped-key chain, some subset will appear sorted
+ * in the same order attached to our mklist
+ */
+ for (m = t->rn_mklist; m && x; x = x->rn_dupedkey)
+ if (m == x->rn_mklist) {
+ struct radix_mask *mm = m->rm_mklist;
+ x->rn_mklist = 0;
+ if (--(m->rm_refs) < 0)
+ MKFree(m);
+ m = mm;
+ }
+ if (m != NULL) {
+#ifdef DEBUG
+ logbad(1, "rn_delete: Orphaned Mask %p at %p\n",
+ m, x);
+#else
+ msglog("rn_delete: Orphaned Mask %p at %p\n", m,
+ x);
+#endif
+ }
+ }
+ }
+ /*
+ * We may be holding an active internal node in the tree.
+ */
+ x = tt + 1;
+ if (t != x) {
+#ifndef RN_DEBUG
+ *t = *x;
+#else
+ b = t->rn_info;
+ *t = *x;
+ t->rn_info = b;
+#endif
+ t->rn_l->rn_p = t;
+ t->rn_r->rn_p = t;
+ p = x->rn_p;
+ if (p->rn_l == x)
+ p->rn_l = t;
+ else
+ p->rn_r = t;
+ }
+out:
+ tt->rn_flags &= ~RNF_ACTIVE;
+ tt[1].rn_flags &= ~RNF_ACTIVE;
+ return (tt);
+}
+
+int
+rn_walktree(struct radix_node_head *h,
+ int (*f)(struct radix_node *, void *),
+ void *w)
+{
+ int error;
+ struct radix_node *base, *next;
+ struct radix_node *rn = h->rnh_treetop;
+ /*
+ * This gets complicated because we may delete the node
+ * while applying the function f to it, so we need to calculate
+ * the successor node in advance.
+ */
+ /* First time through node, go left */
+ while (rn->rn_b >= 0)
+ rn = rn->rn_l;
+ do {
+ base = rn;
+ /* If at right child go back up, otherwise, go right */
+ while (rn->rn_p->rn_r == rn && !(rn->rn_flags & RNF_ROOT))
+ rn = rn->rn_p;
+ /* Find the next *leaf* since next node might vanish, too */
+ for (rn = rn->rn_p->rn_r; rn->rn_b >= 0; )
+ rn = rn->rn_l;
+ next = rn;
+ /* Process leaves */
+ while ((rn = base) != NULL) {
+ base = rn->rn_dupedkey;
+ if (!(rn->rn_flags & RNF_ROOT) && (error = (*f)(rn, w)))
+ return (error);
+ }
+ rn = next;
+ } while (!(rn->rn_flags & RNF_ROOT));
+ return (0);
+}
+
+int
+rn_inithead(void **head, uint_t off)
+{
+ struct radix_node_head *rnh;
+ struct radix_node *t, *tt, *ttt;
+ if (*head)
+ return (1);
+ rnh = rtmalloc(sizeof (*rnh), "rn_inithead");
+ (void) memset(rnh, 0, sizeof (*rnh));
+ *head = rnh;
+ t = rn_newpair(rn_zeros, off, rnh->rnh_nodes);
+ ttt = rnh->rnh_nodes + 2;
+ t->rn_r = ttt;
+ t->rn_p = t;
+ tt = t->rn_l;
+ tt->rn_flags = t->rn_flags = RNF_ROOT | RNF_ACTIVE;
+ tt->rn_b = -1 - off;
+ *ttt = *tt;
+ ttt->rn_key = rn_ones;
+ rnh->rnh_addaddr = rn_addroute;
+ rnh->rnh_deladdr = rn_delete;
+ rnh->rnh_matchaddr = rn_match;
+ rnh->rnh_lookup = rn_lookup;
+ rnh->rnh_walktree = rn_walktree;
+ rnh->rnh_treetop = t;
+ return (1);
+}
+
+void
+rn_init(void)
+{
+ uint8_t *cp, *cplim;
+
+ if (max_keylen == 0) {
+ logbad(1, "radix functions require max_keylen be set");
+ return;
+ }
+ rn_zeros = rtmalloc(3 * max_keylen, "rn_init");
+ (void) memset(rn_zeros, 0, 3 * max_keylen);
+ rn_ones = cp = rn_zeros + max_keylen;
+ addmask_key = cplim = rn_ones + max_keylen;
+ while (cp < cplim)
+ *cp++ = 0xFF;
+ if (rn_inithead((void **)&mask_rnhead, 0) == 0) {
+ logbad(0, "rn_init: could not initialize radix tree");
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.routed/radix.h b/usr/src/cmd/cmd-inet/usr.sbin/in.routed/radix.h
new file mode 100644
index 0000000000..93dc2c244d
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.routed/radix.h
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2001-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 1988, 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 acknowledgment:
+ * 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.
+ *
+ * @(#)radix.h 8.2 (Berkeley) 10/31/94
+ *
+ * $FreeBSD: src/sbin/routed/radix.h,v 1.5 2000/08/11 08:24:38 sheldonh Exp $
+ */
+
+#ifndef ROUTED_RADIX_H
+#define ROUTED_RADIX_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*
+ * Radix search tree node layout.
+ */
+
+struct radix_node {
+ struct radix_mask *rn_mklist; /* list of masks contained in subtree */
+ struct radix_node *rn_p; /* parent */
+ int16_t rn_b; /* bit offset; -1-index(netmask) */
+ uint8_t rn_bmask; /* node: mask for bit test */
+ uint8_t rn_flags; /* enumerated next */
+#define RNF_NORMAL 1 /* leaf contains normal route */
+#define RNF_ROOT 2 /* leaf is root leaf for tree */
+#define RNF_ACTIVE 4 /* This node is alive (for rtfree) */
+ union {
+ struct { /* leaf only data: */
+ uint8_t *rn_Key; /* object of search */
+ uint8_t *rn_Mask; /* netmask, if present */
+ struct radix_node *rn_Dupedkey;
+ } rn_leaf;
+ struct { /* node only data: */
+ int rn_Off; /* where to start compare */
+ struct radix_node *rn_L; /* progeny */
+ struct radix_node *rn_R; /* progeny */
+ } rn_node;
+ } rn_u;
+#ifdef RN_DEBUG
+ int rn_info;
+ struct radix_node *rn_twin;
+ struct radix_node *rn_ybro;
+#endif
+};
+
+#define rn_dupedkey rn_u.rn_leaf.rn_Dupedkey
+#define rn_key rn_u.rn_leaf.rn_Key
+#define rn_mask rn_u.rn_leaf.rn_Mask
+#define rn_off rn_u.rn_node.rn_Off
+#define rn_l rn_u.rn_node.rn_L
+#define rn_r rn_u.rn_node.rn_R
+
+/*
+ * Annotations to tree concerning potential routes applying to subtrees.
+ */
+
+struct radix_mask {
+ short rm_b; /* bit offset; -1-index(netmask) */
+ uint8_t rm_unused; /* cf. rn_bmask */
+ uint8_t rm_flags; /* cf. rn_flags */
+ struct radix_mask *rm_mklist; /* more masks to try */
+ union {
+ uint8_t *rmu_mask; /* the mask */
+ struct radix_node *rmu_leaf; /* for normal routes */
+ } rm_rmu;
+ int rm_refs; /* # of references to this struct */
+};
+
+#define rm_mask rm_rmu.rmu_mask
+#define rm_leaf rm_rmu.rmu_leaf /* extra field would make 32 bytes */
+
+#define MKGet(m) {\
+ if (rn_mkfreelist != NULL) {\
+ m = rn_mkfreelist; \
+ rn_mkfreelist = (m)->rm_mklist; \
+ } else \
+ m = (struct radix_mask *)rtmalloc(sizeof (*(m)), "MKGet"); }
+
+#define MKFree(m) { (m)->rm_mklist = rn_mkfreelist; rn_mkfreelist = (m); }
+
+struct radix_node_head {
+ struct radix_node *rnh_treetop;
+ int rnh_addrsize; /* permit, but not require fixed keys */
+ int rnh_pktsize; /* permit, but not require fixed keys */
+ struct radix_node *(*rnh_addaddr) /* add based on sockaddr */
+ (void *v, void *mask, struct radix_node_head *head,
+ struct radix_node nodes[]);
+ struct radix_node *(*rnh_addpkt) /* add based on packet hdr */
+ (void *v, void *mask, struct radix_node_head *head,
+ struct radix_node nodes[]);
+ struct radix_node *(*rnh_deladdr) /* remove based on sockaddr */
+ (void *v, void *mask, struct radix_node_head *head);
+ struct radix_node *(*rnh_delpkt) /* remove based on packet hdr */
+ (void *v, void *mask, struct radix_node_head *head);
+ struct radix_node *(*rnh_matchaddr) /* locate based on sockaddr */
+ (void *v, struct radix_node_head *head);
+ struct radix_node *(*rnh_lookup) /* locate based on sockaddr */
+ (void *v, void *mask, struct radix_node_head *head);
+ struct radix_node *(*rnh_matchpkt) /* locate based on packet hdr */
+ (void *v, struct radix_node_head *head);
+ /* traverse tree */
+ int (*rnh_walktree)(struct radix_node_head *head,
+ int (*f)(struct radix_node *, void *),
+ void *w);
+ struct radix_node rnh_nodes[3]; /* empty tree for common case */
+};
+
+
+void rn_init(void);
+int rn_inithead(void **, uint_t);
+int rn_walktree(struct radix_node_head *,
+ int (*)(struct radix_node *, void *),
+ void *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ROUTED_RADIX_H */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.routed/rdisc.c b/usr/src/cmd/cmd-inet/usr.sbin/in.routed/rdisc.c
new file mode 100644
index 0000000000..d8c0622d92
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.routed/rdisc.c
@@ -0,0 +1,1397 @@
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 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 acknowledgment:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sbin/routed/rdisc.c,v 1.8 2000/08/11 08:24:38 sheldonh Exp $
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "defs.h"
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+
+/*
+ * The size of the control buffer passed to recvmsg() used to receive
+ * ancillary data.
+ */
+#define CONTROL_BUFSIZE 1024
+
+/* router advertisement ICMP packet */
+struct icmp_ad {
+ uint8_t icmp_type; /* type of message */
+ uint8_t icmp_code; /* type sub code */
+ uint16_t icmp_cksum; /* ones complement cksum of struct */
+ uint8_t icmp_ad_num; /* # of following router addresses */
+ uint8_t icmp_ad_asize; /* 2--words in each advertisement */
+ uint16_t icmp_ad_life; /* seconds of validity */
+ struct icmp_ad_info {
+ in_addr_t icmp_ad_addr;
+ uint32_t icmp_ad_pref;
+ } icmp_ad_info[1];
+};
+
+/* router solicitation ICMP packet */
+struct icmp_so {
+ uint8_t icmp_type; /* type of message */
+ uint8_t icmp_code; /* type sub code */
+ uint16_t icmp_cksum; /* ones complement cksum of struct */
+ uint32_t icmp_so_rsvd;
+};
+
+union ad_u {
+ struct icmp icmp;
+ struct icmp_ad ad;
+ struct icmp_so so;
+};
+
+
+int rdisc_sock = -1; /* router-discovery raw socket */
+static struct interface *rdisc_sock_interface; /* current rdisc interface */
+
+struct timeval rdisc_timer;
+boolean_t rdisc_ok; /* using solicited route */
+
+#define MAX_ADS 16
+int max_ads; /* at least one per interface */
+/* accumulated advertisements */
+static struct dr *cur_drp, *drs;
+
+/*
+ * adjust unsigned preference by interface metric,
+ * without driving it to infinity
+ */
+#define PREF(p, ifp) ((p) <= (uint32_t)(ifp)->int_metric ? ((p) != 0 ? 1 : 0) \
+ : (p) - ((ifp)->int_metric))
+
+static void rdisc_sort(void);
+
+typedef enum { unicast, bcast, mcast } dstaddr_t;
+
+/* dump an ICMP Router Discovery Advertisement Message */
+static void
+trace_rdisc(const char *act,
+ uint32_t from,
+ uint32_t to,
+ struct interface *ifp,
+ union ad_u *p,
+ uint_t len)
+{
+ int i;
+ n_long *wp, *lim;
+
+
+ if (!TRACEPACKETS || ftrace == 0)
+ return;
+
+ lastlog();
+
+ if (p->icmp.icmp_type == ICMP_ROUTERADVERT) {
+ (void) fprintf(ftrace, "%s Router Ad"
+ " from %s to %s via %s life=%d\n",
+ act, naddr_ntoa(from), naddr_ntoa(to),
+ ifp ? ifp->int_name : "?",
+ ntohs(p->ad.icmp_ad_life));
+ if (!TRACECONTENTS)
+ return;
+
+ wp = &p->ad.icmp_ad_info[0].icmp_ad_addr;
+ lim = &wp[(len - sizeof (p->ad)) / sizeof (*wp)];
+ for (i = 0; i < p->ad.icmp_ad_num && wp <= lim; i++) {
+ (void) fprintf(ftrace, "\t%s preference=%ld",
+ naddr_ntoa(wp[0]), (long)ntohl(wp[1]));
+ wp += p->ad.icmp_ad_asize;
+ }
+ (void) fputc('\n', ftrace);
+
+ } else {
+ trace_act("%s Router Solic. from %s to %s via %s rsvd=%#x",
+ act, naddr_ntoa(from), naddr_ntoa(to),
+ ifp ? ifp->int_name : "?",
+ ntohl(p->so.icmp_so_rsvd));
+ }
+}
+
+/*
+ * Prepare Router Discovery socket.
+ */
+static void
+get_rdisc_sock(void)
+{
+ int on = 1;
+ unsigned char ttl = 1;
+
+ if (rdisc_sock < 0) {
+ max_ads = MAX_ADS;
+ drs = rtmalloc(max_ads * sizeof (struct dr), "get_rdisc_sock");
+ (void) memset(drs, 0, max_ads * sizeof (struct dr));
+ rdisc_sock = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP);
+ if (rdisc_sock < 0)
+ BADERR(_B_TRUE, "rdisc_sock = socket()");
+ fix_sock(rdisc_sock, "rdisc_sock");
+
+ if (setsockopt(rdisc_sock, IPPROTO_IP, IP_RECVIF, &on,
+ sizeof (on)))
+ BADERR(_B_FALSE, "setsockopt(IP_RECVIF)");
+
+ if (setsockopt(rdisc_sock, IPPROTO_IP, IP_MULTICAST_TTL,
+ &ttl, sizeof (ttl)) < 0)
+ DBGERR(_B_TRUE,
+ "rdisc_sock setsockopt(IP_MULTICAST_TTL)");
+
+ fix_select();
+ }
+}
+
+
+/*
+ * Pick multicast group for router-discovery socket
+ */
+void
+set_rdisc_mg(struct interface *ifp,
+ int on) /* 0=turn it off */
+{
+ struct ip_mreq m;
+ boolean_t dosupply;
+
+ if (rdisc_sock < 0) {
+ /*
+ * Create the raw socket so that we can hear at least
+ * broadcast router discovery packets.
+ */
+ if ((ifp->int_state & IS_NO_RDISC) == IS_NO_RDISC ||
+ !on)
+ return;
+ get_rdisc_sock();
+ }
+
+ if (!(ifp->int_if_flags & IFF_MULTICAST)) {
+ /* Can't multicast, so no groups could have been joined. */
+ ifp->int_state &= ~(IS_ALL_HOSTS | IS_ALL_ROUTERS);
+ return;
+ }
+
+ dosupply = should_supply(ifp);
+
+ (void) memset(&m, 0, sizeof (m));
+ m.imr_interface.s_addr = ((ifp->int_if_flags & IFF_POINTOPOINT) &&
+ (ifp->int_dstaddr != 0) ? ifp->int_dstaddr : ifp->int_addr);
+ if (dosupply || (ifp->int_state & IS_NO_ADV_IN) || !on) {
+ /* stop listening to advertisements */
+ if (ifp->int_state & IS_ALL_HOSTS) {
+ m.imr_multiaddr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
+ if (setsockopt(rdisc_sock, IPPROTO_IP,
+ IP_DROP_MEMBERSHIP, &m, sizeof (m)) < 0 &&
+ errno != EADDRNOTAVAIL && errno != ENOENT)
+ LOGERR("IP_DROP_MEMBERSHIP ALLHOSTS");
+ ifp->int_state &= ~IS_ALL_HOSTS;
+ }
+
+ } else if (!(ifp->int_state & IS_ALL_HOSTS)) {
+ /* start listening to advertisements */
+ m.imr_multiaddr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
+ if (setsockopt(rdisc_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ &m, sizeof (m)) < 0) {
+ LOGERR("IP_ADD_MEMBERSHIP ALLHOSTS");
+ } else {
+ ifp->int_state |= IS_ALL_HOSTS;
+ }
+ }
+
+ if (!dosupply || (ifp->int_state & IS_NO_ADV_OUT) ||
+ !IS_IFF_ROUTING(ifp->int_if_flags) || !on) {
+ /* stop listening to solicitations */
+ if (ifp->int_state & IS_ALL_ROUTERS) {
+ m.imr_multiaddr.s_addr = htonl(INADDR_ALLRTRS_GROUP);
+ if (setsockopt(rdisc_sock, IPPROTO_IP,
+ IP_DROP_MEMBERSHIP, &m, sizeof (m)) < 0 &&
+ errno != EADDRNOTAVAIL && errno != ENOENT)
+ LOGERR("IP_DROP_MEMBERSHIP ALLROUTERS");
+ ifp->int_state &= ~IS_ALL_ROUTERS;
+ }
+
+ } else if (!(ifp->int_state & IS_ALL_ROUTERS)) {
+ /* start hearing solicitations */
+ m.imr_multiaddr.s_addr = htonl(INADDR_ALLRTRS_GROUP);
+ if (setsockopt(rdisc_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ &m, sizeof (m)) < 0) {
+ LOGERR("IP_ADD_MEMBERSHIP ALLROUTERS");
+ } else {
+ ifp->int_state |= IS_ALL_ROUTERS;
+ }
+ }
+}
+
+
+/*
+ * start or stop supplying routes to other systems.
+ */
+void
+set_supplier(void)
+{
+ struct interface *ifp;
+ struct dr *drp;
+ static boolean_t supplystate = _B_FALSE;
+
+ if (supplystate == (fwd_interfaces > 1))
+ return;
+ supplystate = fwd_interfaces > 1;
+
+ trace_act("%d forwarding interfaces present; becoming %ssupplier",
+ fwd_interfaces, supplystate ? "" : "non-");
+
+ if (supplystate) {
+ /* Forget discovered routes. */
+ for (drp = drs; drp < &drs[max_ads]; drp++) {
+ drp->dr_recv_pref = DEF_PREFERENCELEVEL;
+ drp->dr_life = 0;
+ }
+ rdisc_age(0);
+
+ /*
+ * Do not start advertising until we have heard some
+ * RIP routes.
+ */
+ LIM_SEC(rdisc_timer, now.tv_sec+MIN_WAITTIME);
+
+ /* get rid of any redirects */
+ del_redirects(0, 0);
+ } else {
+ /*
+ * Flush out all those advertisements we had sent by sending
+ * one with lifetime=0.
+ */
+ rdisc_adv(_B_TRUE);
+ }
+
+ /*
+ * Switch router discovery multicast groups from soliciting
+ * to advertising or back.
+ */
+ for (ifp = ifnet; ifp; ifp = ifp->int_next) {
+ if (ifp->int_state & IS_BROKE)
+ continue;
+ ifp->int_rdisc_cnt = 0;
+ ifp->int_rdisc_timer.tv_usec = rdisc_timer.tv_usec;
+ ifp->int_rdisc_timer.tv_sec = now.tv_sec+MIN_WAITTIME;
+ set_rdisc_mg(ifp, 1);
+ }
+}
+
+
+/*
+ * Age discovered routes and find the best one
+ */
+void
+rdisc_age(in_addr_t bad_gate)
+{
+ time_t sec;
+ struct dr *drp;
+ struct rt_spare new;
+ struct rt_entry *rt;
+
+ /*
+ * If we are being told about a bad router,
+ * then age the discovered default route, and if there is
+ * no alternative, solicit a replacement.
+ */
+ if (bad_gate != 0) {
+ /*
+ * Look for the bad discovered default route.
+ * Age it and note its interface.
+ */
+ for (drp = drs; drp < &drs[max_ads]; drp++) {
+ if (drp->dr_ts == 0)
+ continue;
+
+ /*
+ * When we find the bad router, age the route
+ * to at most SUPPLY_INTERVAL.
+ * This is contrary to RFC 1256, but defends against
+ * black holes.
+ */
+ if (drp->dr_gate == bad_gate) {
+ sec = (now.tv_sec - drp->dr_life +
+ SUPPLY_INTERVAL);
+ if (drp->dr_ts > sec) {
+ trace_act("age 0.0.0.0 --> %s via %s",
+ naddr_ntoa(drp->dr_gate),
+ drp->dr_ifp->int_name);
+ drp->dr_ts = sec;
+ }
+ break;
+ }
+ }
+ } else if (should_supply(NULL)) {
+ /*
+ * If switching from client to server, get rid of old
+ * default routes.
+ */
+ if (cur_drp != NULL) {
+ rt = rtget(RIP_DEFAULT, 0);
+ /*
+ * If there is a current default router, and the
+ * there is no rt_spare entry, create one
+ * for cur_drp to prevent segmentation fault
+ * at rdisc_sort.
+ */
+ if (rt == NULL) {
+ (void) memset(&new, 0, sizeof (new));
+ new.rts_ifp = cur_drp->dr_ifp;
+ new.rts_gate = cur_drp->dr_gate;
+ new.rts_router = cur_drp->dr_gate;
+ new.rts_metric = HOPCNT_INFINITY-1;
+ new.rts_time = now.tv_sec;
+ new.rts_origin = RO_RDISC;
+ rtadd(RIP_DEFAULT, 0, RS_NOPROPAGATE, &new);
+ }
+
+ rdisc_sort();
+ }
+ rdisc_adv(_B_FALSE);
+ }
+
+ rdisc_sol();
+ if (cur_drp != NULL) {
+ rt = rtget(RIP_DEFAULT, 0);
+ if (rt == NULL) {
+ (void) memset(&new, 0, sizeof (new));
+ new.rts_ifp = cur_drp->dr_ifp;
+ new.rts_gate = cur_drp->dr_gate;
+ new.rts_router = cur_drp->dr_gate;
+ new.rts_metric = HOPCNT_INFINITY-1;
+ new.rts_time = now.tv_sec;
+ new.rts_origin = RO_RDISC;
+ rtadd(RIP_DEFAULT, 0, RS_NOPROPAGATE, &new);
+ }
+ }
+ rdisc_sort();
+
+ /*
+ * Delete old redirected routes to keep the kernel table small,
+ * and to prevent black holes. Check that the kernel table
+ * matches the daemon table (i.e. has the default route).
+ * But only if RIP is not running and we are not dealing with
+ * a bad gateway, since otherwise age() will be called.
+ */
+ if (rip_sock < 0 && bad_gate == 0)
+ age(0);
+}
+
+
+/*
+ * Zap all routes discovered via an interface that has gone bad
+ * This should only be called when !(ifp->int_state & IS_DUP)
+ * This is called by if_del and if_bad, and the interface pointer
+ * might not be valid after this.
+ */
+void
+if_bad_rdisc(struct interface *ifp)
+{
+ struct dr *drp;
+
+ for (drp = drs; drp < &drs[max_ads]; drp++) {
+ if (drp->dr_ifp != ifp)
+ continue;
+ (void) memset(drp, 0, sizeof (*drp));
+ }
+
+ /* make a note to re-solicit, turn RIP on or off, etc. */
+ rdisc_timer.tv_sec = 0;
+}
+
+/*
+ * Rewire all routes discovered via an interface that has gone bad
+ * This is only called by if_del.
+ */
+void
+if_rewire_rdisc(struct interface *oldifp, struct interface *newifp)
+{
+ struct dr *drp;
+
+ for (drp = drs; drp < &drs[max_ads]; drp++) {
+ if (drp->dr_ifp != oldifp)
+ continue;
+ drp->dr_ifp = newifp;
+ drp->dr_pref += (newifp->int_metric - oldifp->int_metric);
+ drp->dr_flags |= DR_CHANGED;
+ }
+
+ /* make a note to re-solicit, turn RIP on or off, etc. */
+ rdisc_timer.tv_sec = 0;
+}
+
+/*
+ * Mark an interface ok for router discovering.
+ * This is called by if_ok and ifinit.
+ */
+void
+if_ok_rdisc(struct interface *ifp)
+{
+ set_rdisc_mg(ifp, 1);
+
+ ifp->int_rdisc_cnt = 0;
+ ifp->int_rdisc_timer.tv_sec = now.tv_sec +
+ ((ifp->int_state & IS_NO_ADV_OUT) ?
+ MAX_SOLICITATION_DELAY : MIN_WAITTIME);
+ if (timercmp(&rdisc_timer, &ifp->int_rdisc_timer, > /* cstyle */))
+ rdisc_timer = ifp->int_rdisc_timer;
+}
+
+/*
+ * Get rid of a dead discovered router
+ */
+static void
+del_rdisc(struct dr *drp)
+{
+ struct interface *ifp;
+ uint32_t gate;
+ int i;
+ struct rt_entry *rt;
+ struct rt_spare *rts = NULL;
+
+ del_redirects(gate = drp->dr_gate, 0);
+ drp->dr_ts = 0;
+ drp->dr_life = 0;
+
+ rt = rtget(RIP_DEFAULT, 0);
+ if (rt == NULL) {
+ trace_act("could not find default route in table");
+ } else {
+ for (i = 0; i < rt->rt_num_spares; i++) {
+ if ((rt->rt_spares[i].rts_gate == drp->dr_gate) &&
+ (rt->rt_spares[i].rts_origin == RO_RDISC)) {
+ rts = &rt->rt_spares[i];
+ break;
+ }
+ }
+ if (rts != NULL)
+ rts_delete(rt, rts);
+ else
+ trace_act("could not find default route "
+ "through %s in table", naddr_ntoa(drp->dr_gate));
+ }
+
+ /* Count the other discovered routers on the interface. */
+ i = 0;
+ ifp = drp->dr_ifp;
+ for (drp = drs; drp < &drs[max_ads]; drp++) {
+ if (drp->dr_ts != 0 && drp->dr_ifp == ifp)
+ i++;
+ }
+
+ /*
+ * If that was the last good discovered router on the interface,
+ * then solicit a new one.
+ * This is contrary to RFC 1256, but defends against black holes.
+ */
+ if (i != 0) {
+ trace_act("discovered router %s via %s"
+ " is bad--have %d remaining",
+ naddr_ntoa(gate), ifp->int_name, i);
+ } else if (ifp->int_rdisc_cnt >= MAX_SOLICITATIONS) {
+ trace_act("last discovered router %s via %s"
+ " is bad--re-solicit",
+ naddr_ntoa(gate), ifp->int_name);
+ ifp->int_rdisc_cnt = 0;
+ ifp->int_rdisc_timer.tv_sec = 0;
+ rdisc_sol();
+ } else {
+ trace_act("last discovered router %s via %s"
+ " is bad--wait to solicit",
+ naddr_ntoa(gate), ifp->int_name);
+ }
+}
+
+
+/* Find the best discovered route, and discard stale routers. */
+static void
+rdisc_sort(void)
+{
+ struct dr *drp, *new_drp;
+ struct rt_entry *rt;
+ struct rt_spare new, *rts;
+ struct interface *ifp;
+ uint_t new_st = 0;
+ uint32_t new_pref = DEF_PREFERENCELEVEL;
+ int first_rdisc_slot = 0;
+ int j;
+ boolean_t spares_avail;
+ void *ptr;
+ size_t ptrsize;
+
+ rt = rtget(RIP_DEFAULT, 0);
+
+ /*
+ * If all the rt_spare entries are taken up with with default routes
+ * learnt from RIP (ie rts_origin = RO_RIP), bail out.
+ * NOTE:
+ * We *always* prefer default routes learned via RIP
+ * (ie RO_RIP) over those learnt via RDISC (ie RO_RDISC).
+ * The rdisc machinery should not modify, replace or
+ * remove any existing default routes with RO_RIP set.
+ */
+ if (rt != NULL) {
+ spares_avail = _B_FALSE;
+ for (j = 0; j < rt->rt_num_spares; j++) {
+ rts = &rt->rt_spares[j];
+ if (rts->rts_gate == 0 || rts->rts_origin != RO_RIP) {
+ spares_avail = _B_TRUE;
+ break;
+ }
+ }
+ if (!spares_avail) {
+ ptrsize = (rt->rt_num_spares + SPARE_INC) *
+ sizeof (struct rt_spare);
+ ptr = realloc(rt->rt_spares, ptrsize);
+ if (ptr != NULL) {
+ struct rt_spare *tmprts;
+
+ rt->rt_spares = ptr;
+ rts = &rt->rt_spares[rt->rt_num_spares];
+ (void) memset(rts, 0,
+ (SPARE_INC * sizeof (struct rt_spare)));
+ rt->rt_num_spares += SPARE_INC;
+ for (tmprts = rts, j = SPARE_INC;
+ j != 0; j--, tmprts++)
+ tmprts->rts_metric = HOPCNT_INFINITY;
+ spares_avail = _B_TRUE;
+ } else {
+ return;
+ }
+ }
+ }
+ /* Find the best RDISC advertiser */
+ rt = NULL;
+ new_drp = NULL;
+ for (drp = drs; drp < &drs[max_ads]; drp++) {
+ if (drp->dr_ts == 0)
+ continue;
+ ifp = drp->dr_ifp;
+
+ /* Get rid of expired discovered routers. */
+ if (drp->dr_ts + drp->dr_life <= now.tv_sec) {
+ del_rdisc(drp);
+ continue;
+ }
+
+ LIM_SEC(rdisc_timer, drp->dr_ts+drp->dr_life);
+
+ /*
+ * Update preference with possibly changed interface
+ * metric.
+ */
+ drp->dr_pref = PREF(drp->dr_recv_pref, ifp);
+
+ /*
+ * Prefer the current route to prevent thrashing.
+ * Prefer shorter lifetimes to speed the detection of
+ * bad routers.
+ * Avoid sick interfaces.
+ */
+ if (new_drp == NULL ||
+ (!((new_st ^ drp->dr_ifp->int_state) & IS_SICK) &&
+ (new_pref < drp->dr_pref ||
+ (new_pref == drp->dr_pref && (drp == cur_drp ||
+ (new_drp != cur_drp &&
+ new_drp->dr_life > drp->dr_life))))) ||
+ ((new_st & IS_SICK) &&
+ !(drp->dr_ifp->int_state & IS_SICK))) {
+ new_drp = drp;
+ new_st = drp->dr_ifp->int_state;
+ new_pref = drp->dr_pref;
+ }
+ }
+
+ /*
+ * switch to a better RDISC advertiser
+ */
+ if ((new_drp != cur_drp) || (rt == NULL)) {
+ rt = rtget(RIP_DEFAULT, 0);
+
+ /*
+ * Purge the table of all the default routes that were
+ * learnt via RDISC, while keeping an eye the first available
+ * slot for the spare entry of new_drp
+ */
+ if (rt != NULL) {
+ int i;
+ for (i = 0; i < rt->rt_num_spares; i++) {
+ rts = &rt->rt_spares[i];
+ if (rts->rts_gate == 0 && first_rdisc_slot == 0)
+ first_rdisc_slot = i;
+ if (rts->rts_origin == RO_RDISC) {
+ rts_delete(rt, rts);
+ if (first_rdisc_slot == 0) {
+ first_rdisc_slot = i;
+ }
+ }
+ }
+ }
+
+ /* Stop using RDISC routes if they are all bad */
+ if (new_drp == NULL) {
+ trace_act("turn off Router Discovery client");
+ rdisc_ok = _B_FALSE;
+
+ } else {
+ if (cur_drp == NULL) {
+ trace_act("turn on Router Discovery client"
+ " using %s via %s",
+ naddr_ntoa(new_drp->dr_gate),
+ new_drp->dr_ifp->int_name);
+ rdisc_ok = _B_TRUE;
+ }
+
+ /* Prepare a spare entry for the new_drp */
+ (void) memset(&new, 0, sizeof (new));
+ new.rts_ifp = new_drp->dr_ifp;
+ new.rts_gate = new_drp->dr_gate;
+ new.rts_router = new_drp->dr_gate;
+ new.rts_metric = HOPCNT_INFINITY-1;
+ new.rts_time = now.tv_sec;
+ new.rts_origin = RO_RDISC;
+ /*
+ * If there is no existing default route, add it
+ * to rts_spare[0].
+ */
+ if (rt == NULL) {
+ rtadd(RIP_DEFAULT, 0, RS_NOPROPAGATE, &new);
+ } else {
+
+ /*
+ * Add the spare entry for the new_drp in
+ * the first available slot
+ */
+ trace_act("Switching to "
+ "default router with better "
+ "preference %s via %s ",
+ naddr_ntoa(new_drp->dr_gate),
+ new_drp->dr_ifp->int_name);
+ rt->rt_spares[first_rdisc_slot] = new;
+ rt = NULL; /* redo rt_spares */
+ }
+ }
+
+ /*
+ * Get ready to redo the entire table. The table should
+ * only include :
+ * a. empty rt_spare slots
+ * b. default routes learnt via RIP
+ * c. default route for the latest best RDISC advertiser
+ * d. default routes of other RDISC advertisers whose
+ * dr_pref == best RDISC advertiser->dr_pref
+ */
+ cur_drp = new_drp;
+ }
+
+ /* Redo the entire spare table (without touching RO_RIP entries) */
+ if (rdisc_ok && rt == NULL) {
+ int i;
+ /*
+ * We've either just turned on router discovery,
+ * or switched to a router with better preference.
+ * Find all other default routers whose
+ * pref == cur_drp->dr_pref and add them as spares
+ */
+
+ rt = rtget(RIP_DEFAULT, 0);
+
+ for (drp = drs; drp < &drs[max_ads]; drp++) {
+ boolean_t dr_done = _B_FALSE;
+ int slot = -1;
+
+ if (drp->dr_ts == 0)
+ continue;
+
+ if (drp->dr_pref != cur_drp->dr_pref &&
+ ((drp->dr_flags & DR_CHANGED) == 0))
+ continue;
+
+ /*
+ * Either pref matches cur_drp->dr_pref,
+ * or something has changed in this drp.
+ * In the former case, we may need to add
+ * this to rt_spares. In the latter case,
+ * if the pref has changed, need to take it
+ * out of rt_spares and the kernel.
+ *
+ * First, find an empty slot in rt_spares
+ * in case we have to add this drp to kernel.
+ * Also check if it is already there.
+ */
+ for (i = 0; i < rt->rt_num_spares; i++) {
+ if (rt->rt_spares[i].rts_gate == 0) {
+ if (slot < 0)
+ slot = i;
+ continue;
+ }
+ if ((rt->rt_spares[i].rts_gate ==
+ drp->dr_gate) &&
+ (rt->rt_spares[i].rts_origin ==
+ RO_RDISC)) {
+ /*
+ * a spare entry for this RDISC
+ * advertiser already exists. We need
+ * to check if this entry still belongs
+ * in the table
+ */
+ dr_done = _B_TRUE;
+ break;
+ }
+ }
+
+ drp->dr_flags &= ~DR_CHANGED;
+
+ if (drp->dr_pref != cur_drp->dr_pref) {
+ if (dr_done) {
+ /*
+ * The rt_spare of this RDISC advertiser
+ * needs to be removed as it no longer
+ * belongs in the table because its
+ * dr_pref is different than the latest
+ * RDISC advertiser's->dr_pref
+ */
+ rts_delete(rt, &rt->rt_spares[i]);
+ }
+ continue;
+ }
+
+ if (slot < 0) {
+ ptrsize = (rt->rt_num_spares + SPARE_INC) *
+ sizeof (struct rt_spare);
+ ptr = realloc(rt->rt_spares, ptrsize);
+ if (ptr != NULL) {
+ struct rt_spare *tmprts;
+
+ rt->rt_spares = ptr;
+ slot = rt->rt_num_spares;
+ rts = &rt->rt_spares[rt->rt_num_spares];
+ (void) memset(rts, 0, (SPARE_INC *
+ sizeof (struct rt_spare)));
+ rt->rt_num_spares += SPARE_INC;
+ for (tmprts = rts, i = SPARE_INC;
+ i != 0; i--, tmprts++)
+ tmprts->rts_metric =
+ HOPCNT_INFINITY;
+ }
+ }
+
+ if (slot >= 0 && (dr_done != _B_TRUE)) {
+ (void) memset(&new, 0, sizeof (new));
+ new.rts_ifp = drp->dr_ifp;
+ new.rts_gate = drp->dr_gate;
+ new.rts_router = drp->dr_gate;
+ new.rts_metric = HOPCNT_INFINITY-1;
+ new.rts_time = now.tv_sec;
+ new.rts_origin = RO_RDISC;
+ rt->rt_spares[slot] = new;
+ trace_act("spare default %s via %s",
+ naddr_ntoa(drp->dr_gate),
+ drp->dr_ifp->int_name);
+ }
+ }
+ }
+
+ /* turn RIP on or off */
+ if (!rdisc_ok || rip_interfaces > 1) {
+ rip_on(0);
+ } else {
+ rip_off();
+ }
+}
+
+
+/* Handle a single address in an advertisement */
+static void
+parse_ad(uint32_t from,
+ in_addr_t gate,
+ uint32_t pref, /* signed and in network order */
+ ushort_t life, /* in host byte order */
+ struct interface *ifp)
+{
+ static struct msg_limit bad_gate;
+ struct dr *drp, *new_drp;
+ void *ptr;
+ size_t ptrsize;
+
+ if (gate == RIP_DEFAULT || !check_dst(gate)) {
+ msglim(&bad_gate, from, "router %s advertising bad gateway %s",
+ naddr_ntoa(from), naddr_ntoa(gate));
+ return;
+ }
+
+ /*
+ * ignore pointers to ourself and routes via unreachable networks
+ */
+ if (ifwithaddr(gate, _B_TRUE, _B_FALSE) != 0) {
+ trace_pkt(" discard Router Discovery Ad pointing at us");
+ return;
+ }
+ if (!on_net(gate, ifp->int_net, ifp->int_mask)) {
+ trace_pkt(" discard Router Discovery Ad"
+ " toward unreachable net");
+ return;
+ }
+ /*
+ * Convert preference to an unsigned value
+ * and later bias it by the metric of the interface.
+ */
+ pref = UNSIGN_PREF(ntohl(pref));
+
+ if (pref == DEF_PREFERENCELEVEL || life < MIN_MAXADVERTISEINTERVAL) {
+ pref = DEF_PREFERENCELEVEL;
+ life = 0;
+ }
+
+ for (new_drp = NULL, drp = drs; drp < &drs[max_ads]; drp++) {
+ /* accept new info for a familiar entry */
+ if ((drp->dr_gate == gate) && (drp->dr_ifp == ifp)) {
+ new_drp = drp;
+ drp->dr_flags |= DR_CHANGED;
+ break;
+ }
+
+ if (life == 0)
+ continue; /* do not worry about dead ads */
+
+ if (drp->dr_ts == 0) {
+ new_drp = drp; /* use unused entry */
+
+ } else if (new_drp == NULL) {
+ /* look for an entry worse than the new one to reuse. */
+ if ((!(ifp->int_state & IS_SICK) &&
+ (drp->dr_ifp->int_state & IS_SICK)) ||
+ (pref > drp->dr_pref &&
+ !((ifp->int_state ^ drp->dr_ifp->int_state) &
+ IS_SICK)))
+ new_drp = drp;
+
+ } else if (new_drp->dr_ts != 0) {
+ /* look for the least valuable entry to reuse */
+ if ((!(new_drp->dr_ifp->int_state & IS_SICK) &&
+ (drp->dr_ifp->int_state & IS_SICK)) ||
+ (new_drp->dr_pref > drp->dr_pref &&
+ !((new_drp->dr_ifp->int_state ^
+ drp->dr_ifp->int_state) & IS_SICK)))
+ new_drp = drp;
+ }
+ }
+
+ /* if all of the current entries are better, add more drs[] */
+ if (new_drp == NULL) {
+ ptrsize = (max_ads + MAX_ADS) * sizeof (struct dr);
+ ptr = realloc(drs, ptrsize);
+ if (ptr == NULL)
+ return;
+ drs = ptr;
+ (void) memset(&drs[max_ads], 0, MAX_ADS * sizeof (struct dr));
+ new_drp = &drs[max_ads];
+ max_ads += MAX_ADS;
+ }
+
+ /*
+ * Pointer copy is safe here because if_del
+ * calls if_bad_rdisc first, so a non-NULL df_ifp
+ * is always a valid pointer.
+ */
+ new_drp->dr_ifp = ifp;
+ new_drp->dr_gate = gate;
+ new_drp->dr_ts = now.tv_sec;
+ new_drp->dr_life = life;
+ new_drp->dr_recv_pref = pref;
+ /* bias functional preference by metric of the interface */
+ new_drp->dr_pref = PREF(pref, ifp);
+
+ /* after hearing a good advertisement, stop asking */
+ if (!(ifp->int_state & IS_SICK))
+ ifp->int_rdisc_cnt = MAX_SOLICITATIONS;
+}
+
+
+/*
+ * Compute the IP checksum. This assumes the packet is less than 32K long.
+ */
+static uint16_t
+in_cksum(uint16_t *p, uint_t len)
+{
+ uint32_t sum = 0;
+ int nwords = len >> 1;
+
+ while (nwords-- != 0)
+ sum += *p++;
+
+ if (len & 1)
+ sum += *(uchar_t *)p;
+
+ /* end-around-carry */
+ sum = (sum >> 16) + (sum & 0xffff);
+ sum += (sum >> 16);
+ return (~sum);
+}
+
+
+/* Send a router discovery advertisement or solicitation ICMP packet. */
+static void
+send_rdisc(union ad_u *p,
+ uint_t p_size,
+ struct interface *ifp,
+ in_addr_t dst, /* 0 or unicast destination */
+ dstaddr_t type)
+{
+ struct sockaddr_in sin;
+ int flags = 0;
+ const char *msg;
+ int ifindex;
+ struct in_addr addr;
+
+ /*
+ * Don't send Rdisc packets on duplicate interfaces, we
+ * don't want to generate duplicate packets.
+ */
+ if (ifp->int_state & IS_DUP)
+ return;
+
+ (void) memset(&sin, 0, sizeof (sin));
+ sin.sin_addr.s_addr = dst;
+ sin.sin_family = AF_INET;
+
+ switch (type) {
+ case unicast: /* unicast */
+ default:
+ flags = MSG_DONTROUTE;
+ msg = "Send";
+ break;
+
+ case bcast: /* broadcast */
+ if (ifp->int_if_flags & IFF_POINTOPOINT) {
+ msg = "Send pt-to-pt";
+ if (ifp->int_dstaddr == 0)
+ sin.sin_addr.s_addr = htonl(INADDR_BROADCAST);
+ else
+ sin.sin_addr.s_addr = ifp->int_dstaddr;
+ } else {
+ msg = "Send broadcast";
+ sin.sin_addr.s_addr = ifp->int_brdaddr;
+ }
+ break;
+
+ case mcast: /* multicast */
+ msg = "Send multicast";
+ break;
+ }
+
+ if (rdisc_sock < 0)
+ get_rdisc_sock();
+
+ if (rdisc_sock_interface != ifp) {
+ /* select the right interface. */
+ ifindex = (type != mcast && ifp->int_phys != NULL) ?
+ ifp->int_phys->phyi_index : 0;
+ if (setsockopt(rdisc_sock, IPPROTO_IP, IP_XMIT_IF, &ifindex,
+ sizeof (ifindex)) == -1) {
+ LOGERR("setsockopt(rdisc_sock, IP_XMIT_IF)");
+ return;
+ }
+ /*
+ * For multicast, we have to choose the source
+ * address. This is either the local address
+ * (non-point-to-point) or the remote address.
+ */
+ addr.s_addr = (ifp->int_if_flags & IFF_POINTOPOINT) ?
+ ifp->int_dstaddr : ifp->int_addr;
+ if (type == mcast &&
+ setsockopt(rdisc_sock, IPPROTO_IP, IP_MULTICAST_IF, &addr,
+ sizeof (addr)) == -1) {
+ LOGERR("setsockopt(rdisc_sock, IP_MULTICAST_IF)");
+ return;
+ }
+ rdisc_sock_interface = ifp;
+ }
+
+ trace_rdisc(msg, ifp->int_addr, sin.sin_addr.s_addr, ifp, p, p_size);
+
+ if (0 > sendto(rdisc_sock, p, p_size, flags,
+ (struct sockaddr *)&sin, sizeof (sin))) {
+ if (!(ifp->int_state & IS_BROKE))
+ writelog(LOG_WARNING, "sendto(%s%s%s): %s",
+ ifp->int_name, ", ",
+ inet_ntoa(sin.sin_addr),
+ rip_strerror(errno));
+ if (ifp != NULL)
+ if_sick(ifp, _B_FALSE);
+ }
+}
+
+
+/* Send an advertisement */
+static void
+send_adv(struct interface *ifp,
+ in_addr_t dst,
+ dstaddr_t type)
+{
+ union ad_u u;
+
+ if ((ifp->int_state & (IS_SUPPRESS_RDISC|IS_FLUSH_RDISC)) ==
+ IS_SUPPRESS_RDISC)
+ return;
+
+ (void) memset(&u, 0, sizeof (u.ad));
+
+ u.ad.icmp_type = ICMP_ROUTERADVERT;
+ u.ad.icmp_code = ICMP_ROUTERADVERT_COMMON;
+ u.ad.icmp_ad_num = 1;
+ u.ad.icmp_ad_asize = sizeof (u.ad.icmp_ad_info[0])/4;
+
+ u.ad.icmp_ad_life = (stopint || !should_supply(ifp) ||
+ (ifp->int_state & IS_SUPPRESS_RDISC)) ? 0 :
+ htons(ifp->int_rdisc_int*3);
+
+ /* Send the configured preference as a network byte order value */
+ u.ad.icmp_ad_info[0].icmp_ad_pref = htonl(ifp->int_rdisc_pref);
+
+ u.ad.icmp_ad_info[0].icmp_ad_addr = ifp->int_addr;
+
+ u.ad.icmp_cksum = in_cksum((uint16_t *)&u.ad, sizeof (u.ad));
+
+ send_rdisc(&u, sizeof (u.ad), ifp, dst, type);
+
+ if (ifp->int_state & IS_SUPPRESS_RDISC)
+ ifp->int_state &= ~IS_FLUSH_RDISC;
+}
+
+
+/* Advertise as a default router by way of router discovery. */
+void
+rdisc_adv(boolean_t forceadv)
+{
+ struct interface *ifp;
+
+ if (!forceadv && !should_supply(NULL))
+ return;
+
+ rdisc_timer.tv_sec = now.tv_sec + NEVER;
+
+ for (ifp = ifnet; ifp; ifp = ifp->int_next) {
+ if ((ifp->int_state & (IS_NO_ADV_OUT | IS_BROKE)) ||
+ (!forceadv && !IS_IFF_ROUTING(ifp->int_if_flags)))
+ continue;
+
+ /* skip interfaces we shouldn't use */
+ if (IS_IFF_QUIET(ifp->int_if_flags))
+ continue;
+
+ if (!timercmp(&ifp->int_rdisc_timer, &now, > /* cstyle */) ||
+ stopint != 0 || forceadv) {
+ send_adv(ifp, htonl(INADDR_ALLHOSTS_GROUP),
+ (ifp->int_state & IS_BCAST_RDISC) ? 1 : 2);
+ ifp->int_rdisc_cnt++;
+
+ intvl_random(&ifp->int_rdisc_timer,
+ (ifp->int_rdisc_int*3)/4, ifp->int_rdisc_int);
+ if (ifp->int_rdisc_cnt < MAX_INITIAL_ADVERTS &&
+ (ifp->int_rdisc_timer.tv_sec >
+ MAX_INITIAL_ADVERT_INTERVAL)) {
+ ifp->int_rdisc_timer.tv_sec =
+ MAX_INITIAL_ADVERT_INTERVAL;
+ }
+ timevaladd(&ifp->int_rdisc_timer, &now);
+ }
+ if (timercmp(&rdisc_timer, &ifp->int_rdisc_timer,
+ > /* cstyle */))
+ rdisc_timer = ifp->int_rdisc_timer;
+ }
+}
+
+
+/* Solicit for Router Discovery */
+void
+rdisc_sol(void)
+{
+ struct interface *ifp;
+ union ad_u u;
+
+ if (should_supply(NULL))
+ return;
+
+ rdisc_timer.tv_sec = now.tv_sec + NEVER;
+
+ for (ifp = ifnet; ifp; ifp = ifp->int_next) {
+ if (0 != (ifp->int_state & (IS_NO_SOL_OUT | IS_BROKE)) ||
+ ifp->int_rdisc_cnt >= MAX_SOLICITATIONS)
+ continue;
+
+ /* skip interfaces we shouldn't use */
+ if (IS_IFF_QUIET(ifp->int_if_flags))
+ continue;
+
+ if (!timercmp(&ifp->int_rdisc_timer, &now, > /* cstyle */)) {
+ (void) memset(&u, 0, sizeof (u.so));
+ u.so.icmp_type = ICMP_ROUTERSOLICIT;
+ u.so.icmp_cksum = in_cksum((uint16_t *)&u.so,
+ sizeof (u.so));
+ send_rdisc(&u, sizeof (u.so), ifp,
+ htonl(INADDR_ALLRTRS_GROUP),
+ ((ifp->int_state&IS_BCAST_RDISC) ? bcast : mcast));
+
+ if (++ifp->int_rdisc_cnt >= MAX_SOLICITATIONS)
+ continue;
+
+ ifp->int_rdisc_timer.tv_sec = SOLICITATION_INTERVAL;
+ ifp->int_rdisc_timer.tv_usec = 0;
+ timevaladd(&ifp->int_rdisc_timer, &now);
+ }
+
+ if (timercmp(&rdisc_timer, &ifp->int_rdisc_timer,
+ > /* cstyle */))
+ rdisc_timer = ifp->int_rdisc_timer;
+ }
+}
+
+
+/*
+ * check the IP header of a possible Router Discovery ICMP packet
+ * Returns 0 if bad
+ */
+static struct interface *
+ck_icmp(const char *act,
+ in_addr_t from,
+ struct interface *ifp,
+ in_addr_t to,
+ union ad_u *p,
+ uint_t len)
+{
+ const char *type;
+
+
+ if (p->icmp.icmp_type == ICMP_ROUTERADVERT) {
+ type = "advertisement";
+ if (p->icmp.icmp_code == ICMP_ROUTERADVERT_NOCOMMON)
+ return (NULL); /* Mobile IP */
+ } else if (p->icmp.icmp_type == ICMP_ROUTERSOLICIT) {
+ type = "solicitation";
+ } else {
+ return (NULL);
+ }
+
+ if (p->icmp.icmp_code != ICMP_ROUTERADVERT_COMMON) {
+ trace_pkt("unrecognized ICMP Router %s code=%d from %s to %s",
+ type, p->icmp.icmp_code, naddr_ntoa(from), naddr_ntoa(to));
+ return (NULL);
+ }
+
+ trace_rdisc(act, from, to, ifp, p, len);
+
+ if (ifp == NULL)
+ trace_pkt("unknown interface for router-discovery %s from %s "
+ "to %s", type, naddr_ntoa(from), naddr_ntoa(to));
+
+ return (ifp);
+}
+
+
+/* Read packets from the router discovery socket */
+void
+read_d(void)
+{
+#define PKTLEN 512
+ static struct msg_limit bad_asize, bad_len;
+ struct sockaddr_in from;
+ int n, cc, hlen;
+ struct {
+ union {
+ struct ip ip;
+ uint16_t s[PKTLEN/sizeof (uint16_t)];
+ uint8_t b[PKTLEN/sizeof (uint8_t)];
+ } pkt;
+ } buf;
+ union ad_u *p;
+ n_long *wp;
+ struct interface *ifp;
+ boolean_t needsort = _B_FALSE;
+ struct msghdr msg;
+ struct iovec iov;
+ uint8_t ancillary_data[CONTROL_BUFSIZE];
+
+ iov.iov_base = &buf;
+ iov.iov_len = sizeof (buf);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_name = &from;
+ msg.msg_control = &ancillary_data;
+
+ for (;;) {
+ msg.msg_namelen = sizeof (from);
+ msg.msg_controllen = sizeof (ancillary_data);
+ cc = recvmsg(rdisc_sock, &msg, 0);
+ if (cc <= 0) {
+ if (cc < 0 && errno != EWOULDBLOCK)
+ LOGERR("recvmsg(rdisc_sock)");
+ break;
+ }
+
+ hlen = buf.pkt.ip.ip_hl << 2;
+ if (cc < hlen + ICMP_MINLEN)
+ continue;
+ /* LINTED [alignment will be lw aligned] */
+ p = (union ad_u *)&buf.pkt.b[hlen];
+ cc -= hlen;
+
+ /*
+ * If we could tell the interface on which a packet from
+ * address 0 arrived, we could deal with such solicitations.
+ */
+ ifp = receiving_interface(&msg, _B_FALSE);
+ ifp = ck_icmp("Recv", from.sin_addr.s_addr, ifp,
+ buf.pkt.ip.ip_dst.s_addr, p, cc);
+ if (ifp == NULL)
+ continue;
+
+ if (IS_IFF_QUIET(ifp->int_if_flags)) {
+ trace_misc("discard RDISC packet received over %s, %X",
+ ifp->int_name, ifp->int_if_flags);
+ continue;
+ }
+
+ if (from.sin_addr.s_addr != 0 &&
+ ifwithaddr(from.sin_addr.s_addr, _B_FALSE, _B_FALSE)) {
+ trace_pkt(" "
+ "discard our own Router Discovery message");
+ continue;
+ }
+
+ /* The remote address *must* be directly connected. */
+ if (!remote_address_ok(ifp, from.sin_addr.s_addr)) {
+ trace_misc("discard rdisc message; source %s not on "
+ "interface %s", naddr_ntoa(from.sin_addr.s_addr),
+ ifp->int_name);
+ continue;
+ }
+
+ switch (p->icmp.icmp_type) {
+ case ICMP_ROUTERADVERT:
+ if (ifp->int_state & IS_NO_ADV_IN)
+ continue;
+
+ if (p->ad.icmp_ad_asize*2*sizeof (wp[0]) <
+ sizeof (p->ad.icmp_ad_info[0])) {
+ msglim(&bad_asize, from.sin_addr.s_addr,
+ "intolerable rdisc address size=%d",
+ p->ad.icmp_ad_asize);
+ continue;
+ }
+ if (p->ad.icmp_ad_num == 0) {
+ trace_pkt(" empty?");
+ continue;
+ }
+ if (cc < (sizeof (p->ad) -
+ sizeof (p->ad.icmp_ad_info) +
+ (p->ad.icmp_ad_num *
+ sizeof (p->ad.icmp_ad_info[0])))) {
+ msglim(&bad_len, from.sin_addr.s_addr,
+ "rdisc length %d does not match ad_num"
+ " %d", cc, p->ad.icmp_ad_num);
+ continue;
+ }
+
+ needsort = _B_TRUE;
+ wp = &p->ad.icmp_ad_info[0].icmp_ad_addr;
+ for (n = 0; n < p->ad.icmp_ad_num; n++) {
+ parse_ad(from.sin_addr.s_addr,
+ wp[0], wp[1],
+ ntohs(p->ad.icmp_ad_life), ifp);
+ wp += p->ad.icmp_ad_asize;
+ }
+ break;
+
+
+ case ICMP_ROUTERSOLICIT:
+ if (!should_supply(ifp))
+ continue;
+ if ((ifp->int_state & IS_NO_ADV_OUT) ||
+ !IS_IFF_ROUTING(ifp->int_if_flags))
+ continue;
+ if (stopint != 0)
+ continue;
+
+ /*
+ * We should handle messages from address 0,
+ * but cannot due to kernel limitations.
+ */
+
+ /* Respond with a point-to-point advertisement */
+ send_adv(ifp, from.sin_addr.s_addr, 0);
+ break;
+ }
+ }
+
+ if (needsort)
+ rdisc_sort();
+}
+
+void
+rdisc_dump(void)
+{
+ struct dr *drp;
+
+ for (drp = drs; drp < &drs[max_ads]; drp++)
+ if (drp->dr_ts != 0)
+ trace_dr(drp);
+}
+
+void
+rdisc_suppress(struct interface *ifp)
+{
+ if (ifp->int_state & IS_ADV_OUT) {
+ msglog("%s \"rdisc_adv\" specified, will not "
+ "suppress rdisc adv", ifp->int_name);
+ } else {
+ if (ifp->int_state & IS_SUPPRESS_RDISC)
+ return;
+ ifp->int_state |= (IS_SUPPRESS_RDISC|IS_FLUSH_RDISC);
+ trace_misc("suppress rdisc adv on %s", ifp->int_name);
+ rdisc_timer.tv_sec = 0;
+ }
+}
+
+void
+rdisc_restore(struct interface *ifp)
+{
+ if ((ifp->int_state & IS_SUPPRESS_RDISC) == 0)
+ return;
+ ifp->int_state &= ~(IS_SUPPRESS_RDISC|IS_FLUSH_RDISC);
+ trace_misc("restoring rdisc adv on %s", ifp->int_name);
+ rdisc_timer.tv_sec = 0;
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.routed/rtquery.c b/usr/src/cmd/cmd-inet/usr.sbin/in.routed/rtquery.c
new file mode 100644
index 0000000000..914fdd6baa
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.routed/rtquery.c
@@ -0,0 +1,784 @@
+/*
+ * Copyright 2001-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * 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 acknowledgment:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sbin/routed/rtquery/rtquery.c,v 1.14 2000/08/11 08:24:39
+ * sheldonh Exp $
+ * char copyright[] = "@(#) Copyright (c) 1982, 1986, 1993\n"
+ * "The Regents of the University of California. All rights reserved.\n";
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "defs.h"
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#define RIPVERSION RIPv2
+#include <protocols/routed.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <md5.h>
+#include <libintl.h>
+#include <locale.h>
+#include <net/if.h>
+#include <netinet/udp.h>
+
+#ident "$Revision: 1.12 $"
+
+#define WTIME 15 /* Time to wait for all responses */
+#define STIME (250*1000) /* usec to wait for another response */
+
+/*
+ * The size of the control buffer passed to recvmsg() used to receive
+ * ancillary data.
+ */
+#define CONTROL_BUFSIZE 1024
+
+static const char *pgmname;
+
+static union {
+ struct rip rip;
+ char packet[MAXPACKETSIZE+MAXPATHLEN];
+} omsg_buf;
+#define OMSG omsg_buf.rip
+static int omsg_len = sizeof (struct rip);
+
+static union {
+ struct rip rip;
+ char packet[MAXPACKETSIZE+1024];
+} imsg_buf;
+#define IMSG imsg_buf.rip
+
+static int wtime = WTIME;
+static int auth_type = RIP_AUTH_NONE;
+char passwd[RIP_AUTH_PW_LEN+1];
+static ulong_t keyid;
+static boolean_t ripv2 = _B_TRUE; /* use RIP version 2 */
+static boolean_t trace, not_trace; /* send trace command or not */
+static boolean_t nflag; /* numbers, no names */
+static boolean_t pflag; /* play the `gated` game */
+static boolean_t rflag; /* 1=ask about a particular route */
+
+static struct timeval sent; /* when query sent */
+
+static char *default_argv[] = {"localhost", 0};
+
+static void rip_input(struct sockaddr_in *, int, uint_t);
+static int out(const char *, int);
+static void trace_loop(char *argv[], int);
+static void query_loop(char *argv[], int, int);
+static uint_t incoming_interface(struct msghdr *);
+static void usage(void);
+
+
+int
+main(int argc, char *argv[])
+{
+#define MAX_RCVBUF 127*1024
+#define MIN_RCVBUF 4*1024
+
+ int ch, bsize, soc;
+ char *p, *tmp_ptr, *options, *value, delim;
+ const char *result;
+ in_addr_t netaddr, netmask;
+ int on;
+
+ (void) setlocale(LC_ALL, "");
+#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
+#define TEXT_DOMAIN "SYS_TEXT"
+#endif /* ! TEXT_DOMAIN */
+
+ (void) textdomain(TEXT_DOMAIN);
+
+ OMSG.rip_nets[0].n_dst = RIP_DEFAULT;
+ OMSG.rip_nets[0].n_family = RIP_AF_UNSPEC;
+ OMSG.rip_nets[0].n_metric = htonl(HOPCNT_INFINITY);
+
+ if ((pgmname = argv[0]) == NULL)
+ pgmname = "rtquery";
+ while ((ch = getopt(argc, argv, "np1w:r:t:a:")) != -1)
+ switch (ch) {
+ case 'n':
+ not_trace = _B_TRUE;
+ nflag = _B_TRUE;
+ break;
+
+ case 'p':
+ not_trace = _B_TRUE;
+ pflag = _B_TRUE;
+ break;
+
+ case '1':
+ ripv2 = _B_FALSE;
+ break;
+
+ case 'w':
+ not_trace = _B_TRUE;
+ wtime = (int)strtoul(optarg, &p, 0);
+ if (*p != '\0' || wtime <= 0 || p == optarg)
+ usage();
+ break;
+
+ case 'r':
+ not_trace = _B_TRUE;
+ if (rflag)
+ usage();
+ rflag = getnet(optarg, &netaddr, &netmask);
+ if (rflag) {
+ OMSG.rip_nets[0].n_dst = htonl(netaddr);
+ OMSG.rip_nets[0].n_family = RIP_AF_INET;
+ OMSG.rip_nets[0].n_mask = htonl(netmask);
+ } else {
+ struct hostent *hp = gethostbyname(optarg);
+ if (hp == NULL) {
+ (void) fprintf(stderr, "%s: %s: %s\n",
+ pgmname, optarg,
+ hstrerror(h_errno));
+ exit(EXIT_FAILURE);
+ }
+ (void) memcpy(&OMSG.rip_nets[0].n_dst,
+ hp->h_addr,
+ sizeof (OMSG.rip_nets[0].n_dst));
+ OMSG.rip_nets[0].n_family = RIP_AF_INET;
+ OMSG.rip_nets[0].n_mask = INADDR_BROADCAST;
+ rflag = _B_TRUE;
+ }
+ break;
+
+ case 't':
+ trace = _B_TRUE;
+ options = optarg;
+ while (*options != '\0') {
+ /* messy complications to make -W -Wall happy */
+ static char on_str[] = "on";
+ static char more_str[] = "more";
+ static char off_str[] = "off";
+ static char dump_str[] = "dump";
+ static char *traceopts[] = {
+#define TRACE_ON 0
+ on_str,
+#define TRACE_MORE 1
+ more_str,
+#define TRACE_OFF 2
+ off_str,
+#define TRACE_DUMP 3
+ dump_str,
+ 0
+ };
+ result = "";
+ switch (getsubopt(&options, traceopts,
+ &value)) {
+ case TRACE_ON:
+ OMSG.rip_cmd = RIPCMD_TRACEON;
+ if (value == NULL ||
+ strlen(value) > MAXPATHLEN)
+ usage();
+ result = value;
+ break;
+ case TRACE_MORE:
+ if (value != NULL)
+ usage();
+ OMSG.rip_cmd = RIPCMD_TRACEON;
+ break;
+ case TRACE_OFF:
+ if (value != NULL)
+ usage();
+ OMSG.rip_cmd = RIPCMD_TRACEOFF;
+ break;
+ case TRACE_DUMP:
+ if (value != NULL)
+ usage();
+ OMSG.rip_cmd = RIPCMD_TRACEON;
+ result = "dump/../table";
+ break;
+ default:
+ usage();
+ }
+ (void) strlcpy((char *)OMSG.rip_tracefile,
+ result, MAXPATHLEN);
+ omsg_len += strlen(result) -
+ sizeof (OMSG.ripun);
+ }
+ break;
+
+ case 'a':
+ not_trace = _B_TRUE;
+ p = strchr(optarg, '=');
+ if (p == NULL)
+ usage();
+ *p++ = '\0';
+ if (0 == strcasecmp("passwd", optarg))
+ auth_type = RIP_AUTH_PW;
+ else if (0 == strcasecmp("md5_passwd", optarg))
+ auth_type = RIP_AUTH_MD5;
+ else
+ usage();
+ if (0 > parse_quote(&p, "|", &delim,
+ passwd, sizeof (passwd)))
+ usage();
+ if (auth_type == RIP_AUTH_MD5 &&
+ delim == '|') {
+ tmp_ptr = p+1;
+ keyid = strtoul(p+1, &p, 0);
+ if (keyid > 255 || *p != '\0' ||
+ p == tmp_ptr)
+ usage();
+ } else if (delim != '\0') {
+ usage();
+ }
+ break;
+
+ default:
+ usage();
+ }
+ argv += optind;
+ argc -= optind;
+ if (not_trace && trace)
+ usage();
+ if (argc == 0) {
+ argc = 1;
+ argv = default_argv;
+ }
+
+ soc = socket(PF_INET, SOCK_DGRAM, 0);
+ if (soc < 0) {
+ perror("rtquery: socket");
+ exit(EXIT_FAILURE);
+ }
+
+ on = 1;
+ if (setsockopt(soc, IPPROTO_IP, IP_RECVIF, &on, sizeof (on)))
+ perror("rtquery: setsockopt IP_RECVIF");
+
+ /* be prepared to receive a lot of routes */
+ for (bsize = MAX_RCVBUF; ; bsize -= 1024) {
+ if (setsockopt(soc, SOL_SOCKET, SO_RCVBUF,
+ &bsize, sizeof (bsize)) == 0)
+ break;
+ if (bsize <= MIN_RCVBUF) {
+ perror("rtquery: setsockopt SO_RCVBUF");
+ break;
+ }
+ }
+
+ if (trace)
+ trace_loop(argv, soc);
+ else
+ query_loop(argv, argc, soc);
+ /* NOTREACHED */
+ return (0);
+}
+
+
+static void
+usage(void)
+{
+ (void) fprintf(stderr,
+ gettext("usage: %s [-np1] [-r tgt_rt] [-w wtime]"
+ " [-a type=passwd] [host1 ...]\n"),
+ pgmname);
+ (void) fprintf(stderr,
+ gettext("\t%s -t {on=filename|more|off|dump} [host1 ...]\n"),
+ pgmname);
+ exit(EXIT_FAILURE);
+}
+
+
+/* Tell the target hosts about tracing */
+static void
+trace_loop(char *argv[], int soc)
+{
+ struct sockaddr_in myaddr;
+ int res;
+ int optval = 1;
+
+ if (ripv2) {
+ OMSG.rip_vers = RIPv2;
+ } else {
+ OMSG.rip_vers = RIPv1;
+ }
+
+ (void) memset(&myaddr, 0, sizeof (myaddr));
+ myaddr.sin_family = AF_INET;
+ if (setsockopt(soc, IPPROTO_UDP, UDP_ANONPRIVBIND,
+ &optval, sizeof (optval)) < 0) {
+ perror("rtquery: setsockopt UDP_ANONPRIVBIND");
+ exit(EXIT_FAILURE);
+ }
+
+ if (bind(soc, (struct sockaddr *)&myaddr, sizeof (myaddr)) < 0) {
+ perror("rtquery: bind");
+ exit(EXIT_FAILURE);
+ }
+
+ res = EXIT_FAILURE;
+ while (*argv != NULL) {
+ if (out(*argv++, soc) == 0)
+ res = EXIT_SUCCESS;
+ }
+ exit(res);
+}
+
+
+/* Query all of the listed hosts */
+static void
+query_loop(char *argv[], int argc, int soc)
+{
+#define NA0 (OMSG.rip_auths[0])
+#define NA2 (OMSG.rip_auths[2])
+ struct seen {
+ struct seen *next;
+ struct in_addr addr;
+ } *seen, *sp;
+ int answered = 0;
+ int cc;
+ fd_set bits;
+ struct timeval now, delay;
+ struct sockaddr_in from;
+ MD5_CTX md5_ctx;
+ struct msghdr msg;
+ uint_t ifindex;
+ struct iovec iov;
+ uint8_t ancillary_data[CONTROL_BUFSIZE];
+
+
+ OMSG.rip_cmd = (pflag) ? RIPCMD_POLL : RIPCMD_REQUEST;
+ if (ripv2) {
+ OMSG.rip_vers = RIPv2;
+ if (auth_type == RIP_AUTH_PW) {
+ OMSG.rip_nets[1] = OMSG.rip_nets[0];
+ NA0.a_family = RIP_AF_AUTH;
+ NA0.a_type = RIP_AUTH_PW;
+ (void) memcpy(NA0.au.au_pw, passwd, RIP_AUTH_PW_LEN);
+ omsg_len += sizeof (OMSG.rip_nets[0]);
+
+ } else if (auth_type == RIP_AUTH_MD5) {
+ OMSG.rip_nets[1] = OMSG.rip_nets[0];
+ NA0.a_family = RIP_AF_AUTH;
+ NA0.a_type = RIP_AUTH_MD5;
+ NA0.au.a_md5.md5_keyid = (int8_t)keyid;
+ NA0.au.a_md5.md5_auth_len = RIP_AUTH_MD5_LEN;
+ NA0.au.a_md5.md5_seqno = 0;
+ cc = (char *)&NA2-(char *)&OMSG;
+ NA0.au.a_md5.md5_pkt_len = htons(cc);
+ NA2.a_family = RIP_AF_AUTH;
+ NA2.a_type = RIP_AUTH_TRAILER;
+ MD5Init(&md5_ctx);
+ MD5Update(&md5_ctx, (uchar_t *)&OMSG, cc+4);
+ MD5Update(&md5_ctx,
+ (uchar_t *)passwd, RIP_AUTH_MD5_LEN);
+ MD5Final(NA2.au.au_pw, &md5_ctx);
+ omsg_len += 2*sizeof (OMSG.rip_nets[0]);
+ }
+
+ } else {
+ OMSG.rip_vers = RIPv1;
+ OMSG.rip_nets[0].n_mask = 0;
+ }
+
+ /* ask the first (valid) host */
+ seen = NULL;
+ while (0 > out(*argv++, soc)) {
+ if (*argv == NULL)
+ exit(EXIT_FAILURE);
+ answered++;
+ }
+
+ iov.iov_base = &imsg_buf;
+ iov.iov_len = sizeof (imsg_buf);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_name = &from;
+ msg.msg_control = &ancillary_data;
+
+ (void) FD_ZERO(&bits);
+ FD_SET(soc, &bits);
+ for (;;) {
+ delay.tv_sec = 0;
+ delay.tv_usec = STIME;
+ cc = select(soc+1, &bits, 0, 0, &delay);
+ if (cc > 0) {
+ msg.msg_namelen = sizeof (from);
+ msg.msg_controllen = sizeof (ancillary_data);
+ cc = recvmsg(soc, &msg, 0);
+ if (cc < 0) {
+ perror("rtquery: recvmsg");
+ exit(EXIT_FAILURE);
+ }
+
+ /* avoid looping on high traffic */
+ if (answered > argc + 200)
+ break;
+
+ /*
+ * count the distinct responding hosts.
+ * You cannot match responding hosts with
+ * addresses to which queries were transmitted,
+ * because a router might respond with a
+ * different source address.
+ */
+ for (sp = seen; sp != NULL; sp = sp->next) {
+ if (sp->addr.s_addr == from.sin_addr.s_addr)
+ break;
+ }
+ if (sp == NULL) {
+ sp = malloc(sizeof (*sp));
+ if (sp != NULL) {
+ sp->addr = from.sin_addr;
+ sp->next = seen;
+ seen = sp;
+ } else {
+ perror("rtquery: malloc");
+ }
+ answered++;
+ }
+
+ ifindex = incoming_interface(&msg);
+ rip_input(&from, cc, ifindex);
+ continue;
+ }
+
+ if (cc < 0) {
+ if (errno == EINTR)
+ continue;
+ perror("rtquery: select");
+ exit(EXIT_FAILURE);
+ }
+
+ /*
+ * After a pause in responses, probe another host.
+ * This reduces the intermingling of answers.
+ */
+ while (*argv != NULL && 0 > out(*argv++, soc))
+ answered++;
+
+ /*
+ * continue until no more packets arrive
+ * or we have heard from all hosts
+ */
+ if (answered >= argc)
+ break;
+
+ /* or until we have waited a long time */
+ if (gettimeofday(&now, 0) < 0) {
+ perror("rtquery: gettimeofday");
+ exit(EXIT_FAILURE);
+ }
+ if (sent.tv_sec + wtime <= now.tv_sec)
+ break;
+ }
+
+ /* fail if there was no answer */
+ exit(answered >= argc ? EXIT_SUCCESS : EXIT_FAILURE);
+}
+
+
+/* Send to one host */
+static int
+out(const char *host, int soc)
+{
+
+ struct addrinfo hints, *res;
+ int ret;
+
+ if (gettimeofday(&sent, 0) < 0) {
+ perror("rtquery: gettimeofday");
+ return (-1);
+ }
+
+ (void) memset(&hints, 0, sizeof (hints));
+ hints.ai_family = PF_INET;
+ hints.ai_socktype = SOCK_DGRAM;
+ if ((ret = getaddrinfo(host, "route", &hints, &res)) != 0) {
+ (void) fprintf(stderr, "%s: getaddrinfo: %s: %s\n", pgmname,
+ host, gai_strerror(ret));
+ return (-1);
+ }
+
+ if (sendto(soc, &omsg_buf, omsg_len, 0, res->ai_addr,
+ res->ai_addrlen) < 0) {
+ perror("rtquery: sendto");
+ return (-1);
+ }
+
+ freeaddrinfo(res);
+ return (0);
+}
+
+/*
+ * Handle an incoming RIP packet.
+ */
+static void
+rip_input(struct sockaddr_in *from, int size, uint_t ifindex)
+{
+ struct netinfo *n, *lim;
+ struct in_addr in;
+ const char *name;
+ char net_buf[80];
+ uchar_t hash[RIP_AUTH_MD5_LEN];
+ MD5_CTX md5_ctx;
+ uchar_t md5_authed = 0;
+ in_addr_t mask, dmask;
+ struct in_addr tmp_addr;
+ char *sp;
+ char ifname[IF_NAMESIZE+1];
+ int i;
+ struct hostent *hp;
+ struct netent *np;
+ struct netauth *na;
+ char srcaddr[MAXHOSTNAMELEN + sizeof (" (123.123.123.123)") + 1];
+ char ifstring[IF_NAMESIZE + 3*sizeof (ifindex) + sizeof (" ()") + 1];
+
+ if (!nflag && (hp = gethostbyaddr((char *)&from->sin_addr,
+ sizeof (struct in_addr), AF_INET)) != NULL) {
+ (void) snprintf(srcaddr, sizeof (srcaddr), "%s (%s)",
+ hp->h_name, inet_ntoa(from->sin_addr));
+ } else {
+ /* safe; cannot overflow destination */
+ (void) strcpy(srcaddr, inet_ntoa(from->sin_addr));
+ }
+ if (ifindex == 0) {
+ (void) printf("%s:", srcaddr);
+ } else {
+ if (if_indextoname(ifindex, ifname) != NULL)
+ (void) snprintf(ifstring, sizeof (ifstring), "%s (%d)",
+ ifname, ifindex);
+ else
+ (void) snprintf(ifstring, sizeof (ifstring), "%d",
+ ifindex);
+ (void) printf(gettext("%1$s received on interface %2$s:"),
+ srcaddr, ifstring);
+ }
+
+ if (IMSG.rip_cmd != RIPCMD_RESPONSE) {
+ (void) printf(gettext("\n unexpected response type %d\n"),
+ IMSG.rip_cmd);
+ return;
+ }
+ (void) printf(gettext(" RIPv%1$d%2$s %3$d bytes\n"), IMSG.rip_vers,
+ (IMSG.rip_vers != RIPv1 && IMSG.rip_vers != RIPv2) ? " ?" : "",
+ size);
+ if (size > MAXPACKETSIZE) {
+ if (size > sizeof (imsg_buf) - sizeof (*n)) {
+ (void) printf(
+ gettext(" at least %d bytes too long\n"),
+ size-MAXPACKETSIZE);
+ size = sizeof (imsg_buf) - sizeof (*n);
+ } else {
+ (void) printf(gettext(" %d bytes too long\n"),
+ size-MAXPACKETSIZE);
+ }
+ } else if (size%sizeof (*n) != sizeof (struct rip)%sizeof (*n)) {
+ (void) printf(gettext(" response of bad length=%d\n"), size);
+ }
+
+ n = IMSG.rip_nets;
+ lim = n + (size - 4) / sizeof (struct netinfo);
+ for (; n < lim; n++) {
+ name = "";
+ if (n->n_family == RIP_AF_INET) {
+ in.s_addr = n->n_dst;
+ (void) strlcpy(net_buf, inet_ntoa(in),
+ sizeof (net_buf));
+
+ tmp_addr.s_addr = (n->n_mask);
+ mask = ntohl(n->n_mask);
+ dmask = mask & -mask;
+ if (mask != 0) {
+ sp = &net_buf[strlen(net_buf)];
+ if (IMSG.rip_vers == RIPv1) {
+ (void) snprintf(sp,
+ (sizeof (net_buf) -
+ strlen(net_buf)),
+ gettext(" mask=%s ? "),
+ inet_ntoa(tmp_addr));
+ mask = 0;
+ } else if (mask + dmask == 0) {
+ i = ffs(mask) - 1;
+ (void) snprintf(sp,
+ (sizeof (net_buf) -
+ strlen(net_buf)), "/%d", 32-i);
+ } else {
+ (void) snprintf(sp,
+ (sizeof (net_buf) -
+ strlen(net_buf)),
+ gettext(" (mask %s)"),
+ inet_ntoa(tmp_addr));
+ }
+ }
+
+ if (!nflag) {
+ if (mask == 0) {
+ mask = std_mask(in.s_addr);
+ if ((ntohl(in.s_addr) & ~mask) != 0)
+ mask = 0;
+ }
+ /*
+ * Without a netmask, do not worry about
+ * whether the destination is a host or a
+ * network. Try both and use the first name
+ * we get.
+ *
+ * If we have a netmask we can make a
+ * good guess.
+ */
+ if ((in.s_addr & ~mask) == 0) {
+ np = getnetbyaddr((long)in.s_addr,
+ AF_INET);
+ if (np != NULL)
+ name = np->n_name;
+ else if (in.s_addr == 0)
+ name = "default";
+ }
+ if (name[0] == '\0' &&
+ ((in.s_addr & ~mask) != 0 ||
+ mask == 0xffffffff)) {
+ hp = gethostbyaddr((char *)&in,
+ sizeof (in), AF_INET);
+ if (hp != NULL)
+ name = hp->h_name;
+ }
+ }
+
+ } else if (n->n_family == RIP_AF_AUTH) {
+ na = (struct netauth *)n;
+ if (na->a_type == RIP_AUTH_PW &&
+ n == IMSG.rip_nets) {
+ (void) printf(
+ gettext(" Password Authentication:"
+ " \"%s\"\n"),
+ qstring(na->au.au_pw,
+ RIP_AUTH_PW_LEN));
+ continue;
+ }
+
+ if (na->a_type == RIP_AUTH_MD5 &&
+ n == IMSG.rip_nets) {
+ (void) printf(gettext(" MD5 Auth"
+ " len=%1$d KeyID=%2$d"
+ " auth_len=%3$d"
+ " seqno=%4$#x"
+ " rsvd=%5$#x,%6$#x\n"),
+ ntohs(na->au.a_md5.md5_pkt_len),
+ na->au.a_md5.md5_keyid,
+ na->au.a_md5.md5_auth_len,
+ (int)ntohl(na->au.a_md5.md5_seqno),
+ na->au.a_md5.rsvd[0],
+ na->au.a_md5.rsvd[1]);
+ md5_authed = 1;
+ continue;
+ }
+ (void) printf(gettext(" Authentication type %d: "),
+ ntohs(na->a_type));
+ for (i = 0; i < sizeof (na->au.au_pw); i++)
+ (void) printf("%02x ",
+ na->au.au_pw[i]);
+ (void) putchar('\n');
+ if (md5_authed && n+1 > lim &&
+ na->a_type == RIP_AUTH_TRAILER) {
+ MD5Init(&md5_ctx);
+ MD5Update(&md5_ctx, (uchar_t *)&IMSG,
+ (char *)na-(char *)&IMSG);
+ MD5Update(&md5_ctx,
+ (uchar_t *)passwd, RIP_AUTH_MD5_LEN);
+ MD5Final(hash, &md5_ctx);
+ (void) printf(gettext(" %s hash\n"),
+ memcmp(hash, na->au.au_pw, sizeof (hash)) ?
+ gettext("WRONG") : gettext("correct"));
+ } else if (md5_authed && n+1 > lim &&
+ na->a_type != RIP_AUTH_TRAILER) {
+ (void) printf(gettext("Error -"
+ "authentication entry missing hash\n"));
+ }
+ continue;
+
+ } else {
+ tmp_addr.s_addr = n->n_dst;
+ (void) snprintf(net_buf, sizeof (net_buf),
+ gettext("(address family %1$u) %2$s"),
+ ntohs(n->n_family), inet_ntoa(tmp_addr));
+ }
+
+ (void) printf(gettext(" %1$-18s metric %2$2lu %3$-10s"),
+ net_buf, ntohl(n->n_metric), name);
+
+ if (n->n_nhop != 0) {
+ in.s_addr = n->n_nhop;
+ if (nflag)
+ hp = NULL;
+ else
+ hp = gethostbyaddr((char *)&in, sizeof (in),
+ AF_INET);
+ (void) printf(gettext(" nhop=%1$-15s%2$s"),
+ (hp != NULL) ? hp->h_name : inet_ntoa(in),
+ (IMSG.rip_vers == RIPv1) ? " ?" : "");
+ }
+ if (n->n_tag != 0)
+ (void) printf(gettext(" tag=%1$#x%2$s"), n->n_tag,
+ (IMSG.rip_vers == RIPv1) ? " ?" : "");
+ (void) putchar('\n');
+ }
+}
+
+/*
+ * Find the interface which received the given message.
+ */
+static uint_t
+incoming_interface(struct msghdr *msg)
+{
+ void *opt;
+ uint_t ifindex = 0;
+
+ /*
+ * Determine which physical interface this packet was received on by
+ * processing the message's ancillary data to find the
+ * IP_RECVIF option we requested.
+ */
+ if ((opt = find_ancillary(msg, IP_RECVIF)) == NULL)
+ (void) fprintf(stderr,
+ gettext("%s: unable to retrieve input interface\n"),
+ pgmname);
+ else
+ ifindex = *(uint_t *)opt;
+ return (ifindex);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.routed/table.c b/usr/src/cmd/cmd-inet/usr.sbin/in.routed/table.c
new file mode 100644
index 0000000000..1ad9e14e25
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.routed/table.c
@@ -0,0 +1,2795 @@
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 1983, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgment:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sbin/routed/table.c,v 1.15 2000/08/11 08:24:38 sheldonh Exp $
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "defs.h"
+#include <fcntl.h>
+#include <stropts.h>
+#include <sys/tihdr.h>
+#include <inet/mib2.h>
+#include <inet/ip.h>
+
+/* This structure is used to store a disassembled routing socket message. */
+struct rt_addrinfo {
+ int rti_addrs;
+ struct sockaddr_storage *rti_info[RTAX_MAX];
+};
+
+static struct rt_spare *rts_better(struct rt_entry *);
+static struct rt_spare rts_empty = EMPTY_RT_SPARE;
+static void set_need_flash(void);
+static void rtbad(struct rt_entry *, struct interface *);
+static int rt_xaddrs(struct rt_addrinfo *, struct sockaddr_storage *,
+ char *, int);
+static struct interface *gwkludge_iflookup(in_addr_t, in_addr_t, in_addr_t);
+
+struct radix_node_head *rhead; /* root of the radix tree */
+
+/* Flash update needed. _B_TRUE to suppress the 1st. */
+boolean_t need_flash = _B_TRUE;
+
+struct timeval age_timer; /* next check of old routes */
+struct timeval need_kern = { /* need to update kernel table */
+ EPOCH+MIN_WAITTIME-1, 0
+};
+
+static uint32_t total_routes;
+
+#define ROUNDUP_LONG(a) \
+ ((a) > 0 ? (1 + (((a) - 1) | (sizeof (long) - 1))) : sizeof (long))
+
+/*
+ * It is desirable to "aggregate" routes, to combine differing routes of
+ * the same metric and next hop into a common route with a smaller netmask
+ * or to suppress redundant routes, routes that add no information to
+ * routes with smaller netmasks.
+ *
+ * A route is redundant if and only if any and all routes with smaller
+ * but matching netmasks and nets are the same. Since routes are
+ * kept sorted in the radix tree, redundant routes always come second.
+ *
+ * There are two kinds of aggregations. First, two routes of the same bit
+ * mask and differing only in the least significant bit of the network
+ * number can be combined into a single route with a coarser mask.
+ *
+ * Second, a route can be suppressed in favor of another route with a more
+ * coarse mask provided no incompatible routes with intermediate masks
+ * are present. The second kind of aggregation involves suppressing routes.
+ * A route must not be suppressed if an incompatible route exists with
+ * an intermediate mask, since the suppressed route would be covered
+ * by the intermediate.
+ *
+ * This code relies on the radix tree walk encountering routes
+ * sorted first by address, with the smallest address first.
+ */
+
+static struct ag_info ag_slots[NUM_AG_SLOTS], *ag_avail, *ag_corsest,
+ *ag_finest;
+
+#ifdef DEBUG_AG
+#define CHECK_AG() do { int acnt = 0; struct ag_info *cag; \
+ for (cag = ag_avail; cag != NULL; cag = cag->ag_fine) \
+ acnt++; \
+ for (cag = ag_corsest; cag != NULL; cag = cag->ag_fine) \
+ acnt++; \
+ if (acnt != NUM_AG_SLOTS) \
+ abort(); \
+} while (_B_FALSE)
+#else
+#define CHECK_AG() (void)0
+#endif
+
+
+/*
+ * Output the contents of an aggregation table slot.
+ * This function must always be immediately followed with the deletion
+ * of the target slot.
+ */
+static void
+ag_out(struct ag_info *ag, void (*out)(struct ag_info *))
+{
+ struct ag_info *ag_cors;
+ uint32_t bit;
+
+
+ /* Forget it if this route should not be output for split-horizon. */
+ if (ag->ag_state & AGS_SPLIT_HZ)
+ return;
+
+ /*
+ * If we output both the even and odd twins, then the immediate parent,
+ * if it is present, is redundant, unless the parent manages to
+ * aggregate into something coarser.
+ * On successive calls, this code detects the even and odd twins,
+ * and marks the parent.
+ *
+ * Note that the order in which the radix tree code emits routes
+ * ensures that the twins are seen before the parent is emitted.
+ */
+ ag_cors = ag->ag_cors;
+ if (ag_cors != NULL &&
+ ag_cors->ag_mask == (ag->ag_mask << 1) &&
+ ag_cors->ag_dst_h == (ag->ag_dst_h & ag_cors->ag_mask)) {
+ ag_cors->ag_state |= ((ag_cors->ag_dst_h == ag->ag_dst_h) ?
+ AGS_REDUN0 : AGS_REDUN1);
+ }
+
+ /*
+ * Skip it if this route is itself redundant.
+ *
+ * It is ok to change the contents of the slot here, since it is
+ * always deleted next.
+ */
+ if (ag->ag_state & AGS_REDUN0) {
+ if (ag->ag_state & AGS_REDUN1)
+ return; /* quit if fully redundant */
+ /* make it finer if it is half-redundant */
+ bit = (-ag->ag_mask) >> 1;
+ ag->ag_dst_h |= bit;
+ ag->ag_mask |= bit;
+
+ } else if (ag->ag_state & AGS_REDUN1) {
+ /* make it finer if it is half-redundant */
+ bit = (-ag->ag_mask) >> 1;
+ ag->ag_mask |= bit;
+ }
+ out(ag);
+}
+
+
+static void
+ag_del(struct ag_info *ag)
+{
+ CHECK_AG();
+
+ if (ag->ag_cors == NULL)
+ ag_corsest = ag->ag_fine;
+ else
+ ag->ag_cors->ag_fine = ag->ag_fine;
+
+ if (ag->ag_fine == NULL)
+ ag_finest = ag->ag_cors;
+ else
+ ag->ag_fine->ag_cors = ag->ag_cors;
+
+ ag->ag_fine = ag_avail;
+ ag_avail = ag;
+
+ CHECK_AG();
+}
+
+
+/* Look for a route that can suppress the given route. */
+static struct ag_info *
+ag_find_suppressor(struct ag_info *ag)
+{
+ struct ag_info *ag_cors;
+ in_addr_t dst_h = ag->ag_dst_h;
+
+ for (ag_cors = ag->ag_cors; ag_cors != NULL;
+ ag_cors = ag_cors->ag_cors) {
+
+ if ((dst_h & ag_cors->ag_mask) == ag_cors->ag_dst_h) {
+ /*
+ * We found a route with a coarser mask that covers
+ * the given target. It can suppress the target
+ * only if it has a good enough metric and it
+ * either has the same (gateway, ifp), or if its state
+ * includes AGS_CORS_GATE or the target's state
+ * includes AGS_FINE_GATE.
+ */
+ if (ag_cors->ag_pref <= ag->ag_pref &&
+ (((ag->ag_nhop == ag_cors->ag_nhop) &&
+ (ag->ag_ifp == ag_cors->ag_ifp)) ||
+ ag_cors->ag_state & AGS_CORS_GATE ||
+ ag->ag_state & AGS_FINE_GATE)) {
+ return (ag_cors);
+ }
+ }
+ }
+
+ return (NULL);
+}
+
+
+/*
+ * Flush routes waiting for aggregation.
+ * This must not suppress a route unless it is known that among all routes
+ * with coarser masks that match it, the one with the longest mask is
+ * appropriate. This is ensured by scanning the routes in lexical order,
+ * and with the most restrictive mask first among routes to the same
+ * destination.
+ */
+void
+ag_flush(in_addr_t lim_dst_h, /* flush routes to here */
+ in_addr_t lim_mask, /* matching this mask */
+ void (*out)(struct ag_info *))
+{
+ struct ag_info *ag, *ag_cors, *ag_supr;
+ in_addr_t dst_h;
+
+
+ for (ag = ag_finest; ag != NULL && ag->ag_mask >= lim_mask;
+ ag = ag_cors) {
+ /* Get the next route now, before we delete ag. */
+ ag_cors = ag->ag_cors;
+
+ /* Work on only the specified routes. */
+ dst_h = ag->ag_dst_h;
+ if ((dst_h & lim_mask) != lim_dst_h)
+ continue;
+
+ /*
+ * Don't try to suppress the route if its state doesn't
+ * include AGS_SUPPRESS.
+ */
+ if (!(ag->ag_state & AGS_SUPPRESS)) {
+ ag_out(ag, out);
+ ag_del(ag);
+ continue;
+ }
+
+ ag_supr = ag_find_suppressor(ag);
+ if (ag_supr == NULL) {
+ /*
+ * We didn't find a route which suppresses the
+ * target, so the target can go out.
+ */
+ ag_out(ag, out);
+ } else {
+ /*
+ * We found a route which suppresses the target, so
+ * don't output the target.
+ */
+ if (TRACEACTIONS) {
+ trace_misc("aggregated away %s",
+ rtname(htonl(ag->ag_dst_h), ag->ag_mask,
+ ag->ag_nhop));
+ trace_misc("on coarser route %s",
+ rtname(htonl(ag_supr->ag_dst_h),
+ ag_supr->ag_mask, ag_supr->ag_nhop));
+ }
+ /*
+ * If the suppressed target was redundant, then
+ * mark the suppressor as redundant.
+ */
+ if (AG_IS_REDUN(ag->ag_state) &&
+ ag_supr->ag_mask == (ag->ag_mask<<1)) {
+ if (ag_supr->ag_dst_h == dst_h)
+ ag_supr->ag_state |= AGS_REDUN0;
+ else
+ ag_supr->ag_state |= AGS_REDUN1;
+ }
+ if (ag->ag_tag != ag_supr->ag_tag)
+ ag_supr->ag_tag = 0;
+ if (ag->ag_nhop != ag_supr->ag_nhop)
+ ag_supr->ag_nhop = 0;
+ }
+
+ /* The route has either been output or suppressed */
+ ag_del(ag);
+ }
+
+ CHECK_AG();
+}
+
+
+/* Try to aggregate a route with previous routes. */
+void
+ag_check(in_addr_t dst,
+ in_addr_t mask,
+ in_addr_t gate,
+ struct interface *ifp,
+ in_addr_t nhop,
+ uint8_t metric,
+ uint8_t pref,
+ uint32_t seqno,
+ uint16_t tag,
+ uint16_t state,
+ void (*out)(struct ag_info *)) /* output using this */
+{
+ struct ag_info *ag, *nag, *ag_cors;
+ in_addr_t xaddr;
+ int tmp;
+ struct interface *xifp;
+
+ dst = ntohl(dst);
+
+ /*
+ * Don't bother trying to aggregate routes with non-contiguous
+ * subnet masks.
+ *
+ * (X & -X) contains a single bit if and only if X is a power of 2.
+ * (X + (X & -X)) == 0 if and only if X is a power of 2.
+ */
+ if ((mask & -mask) + mask != 0) {
+ struct ag_info nc_ag;
+
+ nc_ag.ag_dst_h = dst;
+ nc_ag.ag_mask = mask;
+ nc_ag.ag_gate = gate;
+ nc_ag.ag_ifp = ifp;
+ nc_ag.ag_nhop = nhop;
+ nc_ag.ag_metric = metric;
+ nc_ag.ag_pref = pref;
+ nc_ag.ag_tag = tag;
+ nc_ag.ag_state = state;
+ nc_ag.ag_seqno = seqno;
+ out(&nc_ag);
+ return;
+ }
+
+ /* Search for the right slot in the aggregation table. */
+ ag_cors = NULL;
+ ag = ag_corsest;
+ while (ag != NULL) {
+ if (ag->ag_mask >= mask)
+ break;
+
+ /*
+ * Suppress old routes (i.e. combine with compatible routes
+ * with coarser masks) as we look for the right slot in the
+ * aggregation table for the new route.
+ * A route to an address less than the current destination
+ * will not be affected by the current route or any route
+ * seen hereafter. That means it is safe to suppress it.
+ * This check keeps poor routes (e.g. with large hop counts)
+ * from preventing suppression of finer routes.
+ */
+ if (ag_cors != NULL && ag->ag_dst_h < dst &&
+ (ag->ag_state & AGS_SUPPRESS) &&
+ ag_cors->ag_pref <= ag->ag_pref &&
+ (ag->ag_dst_h & ag_cors->ag_mask) == ag_cors->ag_dst_h &&
+ ((ag_cors->ag_nhop == ag->ag_nhop &&
+ (ag_cors->ag_ifp == ag->ag_ifp))||
+ (ag->ag_state & AGS_FINE_GATE) ||
+ (ag_cors->ag_state & AGS_CORS_GATE))) {
+ /*
+ * If the suppressed target was redundant,
+ * then mark the suppressor redundant.
+ */
+ if (AG_IS_REDUN(ag->ag_state) &&
+ ag_cors->ag_mask == (ag->ag_mask << 1)) {
+ if (ag_cors->ag_dst_h == dst)
+ ag_cors->ag_state |= AGS_REDUN0;
+ else
+ ag_cors->ag_state |= AGS_REDUN1;
+ }
+ if (ag->ag_tag != ag_cors->ag_tag)
+ ag_cors->ag_tag = 0;
+ if (ag->ag_nhop != ag_cors->ag_nhop)
+ ag_cors->ag_nhop = 0;
+ ag_del(ag);
+ CHECK_AG();
+ } else {
+ ag_cors = ag;
+ }
+ ag = ag_cors->ag_fine;
+ }
+
+ /*
+ * If we find the even/odd twin of the new route, and if the
+ * masks and so forth are equal, we can aggregate them.
+ * We can probably promote one of the pair.
+ *
+ * Since the routes are encountered in lexical order,
+ * the new route must be odd. However, the second or later
+ * times around this loop, it could be the even twin promoted
+ * from the even/odd pair of twins of the finer route.
+ */
+ while (ag != NULL && ag->ag_mask == mask &&
+ ((ag->ag_dst_h ^ dst) & (mask<<1)) == 0) {
+
+ /*
+ * Here we know the target route and the route in the current
+ * slot have the same netmasks and differ by at most the
+ * last bit. They are either for the same destination, or
+ * for an even/odd pair of destinations.
+ */
+ if (ag->ag_dst_h == dst) {
+ if (ag->ag_nhop == nhop && ag->ag_ifp == ifp) {
+ /*
+ * We have two routes to the same destination,
+ * with the same nexthop and interface.
+ * Routes are encountered in lexical order,
+ * so a route is never promoted until the
+ * parent route is already present. So we
+ * know that the new route is a promoted (or
+ * aggregated) pair and the route already in
+ * the slot is the explicit route.
+ *
+ * Prefer the best route if their metrics
+ * differ, or the aggregated one if not,
+ * following a sort of longest-match rule.
+ */
+ if (pref <= ag->ag_pref) {
+ ag->ag_gate = gate;
+ ag->ag_ifp = ifp;
+ ag->ag_nhop = nhop;
+ ag->ag_tag = tag;
+ ag->ag_metric = metric;
+ ag->ag_pref = pref;
+ if (seqno > ag->ag_seqno)
+ ag->ag_seqno = seqno;
+ tmp = ag->ag_state;
+ ag->ag_state = state;
+ state = tmp;
+ }
+
+ /*
+ * Some bits are set if they are set on
+ * either route, except when the route is
+ * for an interface.
+ */
+ if (!(ag->ag_state & AGS_IF))
+ ag->ag_state |=
+ (state & (AGS_AGGREGATE_EITHER |
+ AGS_REDUN0 | AGS_REDUN1));
+
+ return;
+ } else {
+ /*
+ * multiple routes to same dest/mask with
+ * differing gate nexthop/or ifp. Flush
+ * both out.
+ */
+ break;
+ }
+ }
+
+ /*
+ * If one of the routes can be promoted and the other can
+ * be suppressed, it may be possible to combine them or
+ * worthwhile to promote one.
+ *
+ * Any route that can be promoted is always
+ * marked to be eligible to be suppressed.
+ */
+ if (!((state & AGS_AGGREGATE) &&
+ (ag->ag_state & AGS_SUPPRESS)) &&
+ !((ag->ag_state & AGS_AGGREGATE) && (state & AGS_SUPPRESS)))
+ break;
+
+ /*
+ * A pair of even/odd twin routes can be combined
+ * if either is redundant, or if they are via the
+ * same gateway and have the same metric.
+ */
+ if (AG_IS_REDUN(ag->ag_state) || AG_IS_REDUN(state) ||
+ (ag->ag_nhop == nhop && ag->ag_ifp == ifp &&
+ ag->ag_pref == pref &&
+ (state & ag->ag_state & AGS_AGGREGATE) != 0)) {
+
+ /*
+ * We have both the even and odd pairs.
+ * Since the routes are encountered in order,
+ * the route in the slot must be the even twin.
+ *
+ * Combine and promote (aggregate) the pair of routes.
+ */
+ if (seqno < ag->ag_seqno)
+ seqno = ag->ag_seqno;
+ if (!AG_IS_REDUN(state))
+ state &= ~AGS_REDUN1;
+ if (AG_IS_REDUN(ag->ag_state))
+ state |= AGS_REDUN0;
+ else
+ state &= ~AGS_REDUN0;
+ state |= (ag->ag_state & AGS_AGGREGATE_EITHER);
+ if (ag->ag_tag != tag)
+ tag = 0;
+ if (ag->ag_nhop != nhop)
+ nhop = 0;
+
+ /*
+ * Get rid of the even twin that was already
+ * in the slot.
+ */
+ ag_del(ag);
+
+ } else if (ag->ag_pref >= pref &&
+ (ag->ag_state & AGS_AGGREGATE)) {
+ /*
+ * If we cannot combine the pair, maybe the route
+ * with the worse metric can be promoted.
+ *
+ * Promote the old, even twin, by giving its slot
+ * in the table to the new, odd twin.
+ */
+ ag->ag_dst_h = dst;
+
+ xaddr = ag->ag_gate;
+ ag->ag_gate = gate;
+ gate = xaddr;
+
+ xifp = ag->ag_ifp;
+ ag->ag_ifp = ifp;
+ ifp = xifp;
+
+ xaddr = ag->ag_nhop;
+ ag->ag_nhop = nhop;
+ nhop = xaddr;
+
+ tmp = ag->ag_tag;
+ ag->ag_tag = tag;
+ tag = tmp;
+
+ /*
+ * The promoted route is even-redundant only if the
+ * even twin was fully redundant. It is not
+ * odd-redundant because the odd-twin will still be
+ * in the table.
+ */
+ tmp = ag->ag_state;
+ if (!AG_IS_REDUN(tmp))
+ tmp &= ~AGS_REDUN0;
+ tmp &= ~AGS_REDUN1;
+ ag->ag_state = state;
+ state = tmp;
+
+ tmp = ag->ag_metric;
+ ag->ag_metric = metric;
+ metric = tmp;
+
+ tmp = ag->ag_pref;
+ ag->ag_pref = pref;
+ pref = tmp;
+
+ /* take the newest sequence number */
+ if (seqno <= ag->ag_seqno)
+ seqno = ag->ag_seqno;
+ else
+ ag->ag_seqno = seqno;
+
+ } else {
+ if (!(state & AGS_AGGREGATE))
+ break; /* cannot promote either twin */
+
+ /*
+ * Promote the new, odd twin by shaving its
+ * mask and address.
+ * The promoted route is odd-redundant only if the
+ * odd twin was fully redundant. It is not
+ * even-redundant because the even twin is still in
+ * the table.
+ */
+ if (!AG_IS_REDUN(state))
+ state &= ~AGS_REDUN1;
+ state &= ~AGS_REDUN0;
+ if (seqno < ag->ag_seqno)
+ seqno = ag->ag_seqno;
+ else
+ ag->ag_seqno = seqno;
+ }
+
+ mask <<= 1;
+ dst &= mask;
+
+ if (ag_cors == NULL) {
+ ag = ag_corsest;
+ break;
+ }
+ ag = ag_cors;
+ ag_cors = ag->ag_cors;
+ }
+
+ /*
+ * When we can no longer promote and combine routes,
+ * flush the old route in the target slot. Also flush
+ * any finer routes that we know will never be aggregated by
+ * the new route.
+ *
+ * In case we moved toward coarser masks,
+ * get back where we belong
+ */
+ if (ag != NULL && ag->ag_mask < mask) {
+ ag_cors = ag;
+ ag = ag->ag_fine;
+ }
+
+ /* Empty the target slot */
+ if (ag != NULL && ag->ag_mask == mask) {
+ ag_flush(ag->ag_dst_h, ag->ag_mask, out);
+ ag = (ag_cors == NULL) ? ag_corsest : ag_cors->ag_fine;
+ }
+
+#ifdef DEBUG_AG
+ if (ag == NULL && ag_cors != ag_finest)
+ abort();
+ if (ag_cors == NULL && ag != ag_corsest)
+ abort();
+ if (ag != NULL && ag->ag_cors != ag_cors)
+ abort();
+ if (ag_cors != NULL && ag_cors->ag_fine != ag)
+ abort();
+ CHECK_AG();
+#endif
+
+ /* Save the new route on the end of the table. */
+ nag = ag_avail;
+ ag_avail = nag->ag_fine;
+
+ nag->ag_dst_h = dst;
+ nag->ag_mask = mask;
+ nag->ag_ifp = ifp;
+ nag->ag_gate = gate;
+ nag->ag_nhop = nhop;
+ nag->ag_metric = metric;
+ nag->ag_pref = pref;
+ nag->ag_tag = tag;
+ nag->ag_state = state;
+ nag->ag_seqno = seqno;
+
+ nag->ag_fine = ag;
+ if (ag != NULL)
+ ag->ag_cors = nag;
+ else
+ ag_finest = nag;
+ nag->ag_cors = ag_cors;
+ if (ag_cors == NULL)
+ ag_corsest = nag;
+ else
+ ag_cors->ag_fine = nag;
+ CHECK_AG();
+}
+
+
+static const char *
+rtm_type_name(uchar_t type)
+{
+ static const char *rtm_types[] = {
+ "RTM_ADD",
+ "RTM_DELETE",
+ "RTM_CHANGE",
+ "RTM_GET",
+ "RTM_LOSING",
+ "RTM_REDIRECT",
+ "RTM_MISS",
+ "RTM_LOCK",
+ "RTM_OLDADD",
+ "RTM_OLDDEL",
+ "RTM_RESOLVE",
+ "RTM_NEWADDR",
+ "RTM_DELADDR",
+ "RTM_IFINFO",
+ "RTM_NEWMADDR",
+ "RTM_DELMADDR"
+ };
+#define NEW_RTM_PAT "RTM type %#x"
+ static char name0[sizeof (NEW_RTM_PAT) + 2];
+
+ if (type > sizeof (rtm_types) / sizeof (rtm_types[0]) || type == 0) {
+ (void) snprintf(name0, sizeof (name0), NEW_RTM_PAT, type);
+ return (name0);
+ } else {
+ return (rtm_types[type-1]);
+ }
+#undef NEW_RTM_PAT
+}
+
+
+static void
+dump_rt_msg(const char *act, struct rt_msghdr *rtm, int mlen)
+{
+ const char *mtype;
+ uchar_t *cp;
+ int i, j;
+ char buffer[16*3 + 1], *ibs;
+ struct ifa_msghdr *ifam;
+ struct if_msghdr *ifm;
+
+ switch (rtm->rtm_type) {
+ case RTM_NEWADDR:
+ case RTM_DELADDR:
+ mtype = "ifam";
+ break;
+ case RTM_IFINFO:
+ mtype = "ifm";
+ break;
+ default:
+ mtype = "rtm";
+ break;
+ }
+ trace_misc("%s %s %d bytes", act, mtype, mlen);
+ if (mlen > rtm->rtm_msglen) {
+ trace_misc("%s: extra %d bytes ignored", mtype,
+ mlen - rtm->rtm_msglen);
+ mlen = rtm->rtm_msglen;
+ } else if (mlen < rtm->rtm_msglen) {
+ trace_misc("%s: truncated by %d bytes", mtype,
+ rtm->rtm_msglen - mlen);
+ }
+ switch (rtm->rtm_type) {
+ case RTM_NEWADDR:
+ case RTM_DELADDR:
+ ifam = (struct ifa_msghdr *)rtm;
+ trace_misc("ifam: msglen %d version %d type %d addrs %X",
+ ifam->ifam_msglen, ifam->ifam_version, ifam->ifam_type,
+ ifam->ifam_addrs);
+ trace_misc("ifam: flags %X index %d metric %d",
+ ifam->ifam_flags, ifam->ifam_index, ifam->ifam_metric);
+ cp = (uchar_t *)(ifam + 1);
+ break;
+ case RTM_IFINFO:
+ ifm = (struct if_msghdr *)rtm;
+ trace_misc("ifm: msglen %d version %d type %d addrs %X",
+ ifm->ifm_msglen, ifm->ifm_version, ifm->ifm_type,
+ ifm->ifm_addrs);
+ ibs = if_bit_string(ifm->ifm_flags, _B_TRUE);
+ if (ibs == NULL) {
+ trace_misc("ifm: flags %#x index %d", ifm->ifm_flags,
+ ifm->ifm_index);
+ } else {
+ trace_misc("ifm: flags %s index %d", ibs,
+ ifm->ifm_index);
+ free(ibs);
+ }
+ cp = (uchar_t *)(ifm + 1);
+ break;
+ default:
+ trace_misc("rtm: msglen %d version %d type %d index %d",
+ rtm->rtm_msglen, rtm->rtm_version, rtm->rtm_type,
+ rtm->rtm_index);
+ trace_misc("rtm: flags %X addrs %X pid %d seq %d",
+ rtm->rtm_flags, rtm->rtm_addrs, rtm->rtm_pid, rtm->rtm_seq);
+ trace_misc("rtm: errno %d use %d inits %X", rtm->rtm_errno,
+ rtm->rtm_use, rtm->rtm_inits);
+ cp = (uchar_t *)(rtm + 1);
+ break;
+ }
+ i = mlen - (cp - (uint8_t *)rtm);
+ while (i > 0) {
+ buffer[0] = '\0';
+ ibs = buffer;
+ for (j = 0; j < 16 && i > 0; j++, i--)
+ ibs += sprintf(ibs, " %02X", *cp++);
+ trace_misc("addr%s", buffer);
+ }
+}
+
+/*
+ * Tell the kernel to add, delete or change a route
+ * Pass k_state from khash in for diagnostic info.
+ */
+static void
+rtioctl(int action, /* RTM_DELETE, etc */
+ in_addr_t dst,
+ in_addr_t gate,
+ in_addr_t mask,
+ struct interface *ifp,
+ uint8_t metric,
+ int flags)
+{
+ static int rt_sock_seqno = 0;
+ struct {
+ struct rt_msghdr w_rtm;
+ struct sockaddr_in w_dst;
+ struct sockaddr_in w_gate;
+ uint8_t w_space[512];
+ } w;
+ struct sockaddr_in w_mask;
+ struct sockaddr_dl w_ifp;
+ uint8_t *cp;
+ long cc;
+#define PAT " %-10s %s metric=%d flags=%#x"
+#define ARGS rtm_type_name(action), rtname(dst, mask, gate), metric, flags
+
+again:
+ (void) memset(&w, 0, sizeof (w));
+ (void) memset(&w_mask, 0, sizeof (w_mask));
+ (void) memset(&w_ifp, 0, sizeof (w_ifp));
+ cp = w.w_space;
+ w.w_rtm.rtm_msglen = sizeof (struct rt_msghdr) +
+ 2 * ROUNDUP_LONG(sizeof (struct sockaddr_in));
+ w.w_rtm.rtm_version = RTM_VERSION;
+ w.w_rtm.rtm_type = action;
+ w.w_rtm.rtm_flags = flags;
+ w.w_rtm.rtm_seq = ++rt_sock_seqno;
+ w.w_rtm.rtm_addrs = RTA_DST|RTA_GATEWAY;
+ if (metric != 0 || action == RTM_CHANGE) {
+ w.w_rtm.rtm_rmx.rmx_hopcount = metric;
+ w.w_rtm.rtm_inits |= RTV_HOPCOUNT;
+ }
+ w.w_dst.sin_family = AF_INET;
+ w.w_dst.sin_addr.s_addr = dst;
+ w.w_gate.sin_family = AF_INET;
+ w.w_gate.sin_addr.s_addr = gate;
+ if (mask == HOST_MASK) {
+ w.w_rtm.rtm_flags |= RTF_HOST;
+ } else {
+ w.w_rtm.rtm_addrs |= RTA_NETMASK;
+ w_mask.sin_family = AF_INET;
+ w_mask.sin_addr.s_addr = htonl(mask);
+ (void) memmove(cp, &w_mask, sizeof (w_mask));
+ cp += ROUNDUP_LONG(sizeof (struct sockaddr_in));
+ w.w_rtm.rtm_msglen += ROUNDUP_LONG(sizeof (struct sockaddr_in));
+ }
+ if (ifp == NULL)
+ ifp = iflookup(gate);
+
+ if ((ifp == NULL) || (ifp->int_phys == NULL)) {
+ trace_misc("no ifp for" PAT, ARGS);
+ } else {
+ if (ifp->int_phys->phyi_index > UINT16_MAX) {
+ trace_misc("ifindex %d is too big for sdl_index",
+ ifp->int_phys->phyi_index);
+ } else {
+ w_ifp.sdl_family = AF_LINK;
+ w.w_rtm.rtm_addrs |= RTA_IFP;
+ w_ifp.sdl_index = ifp->int_phys->phyi_index;
+ (void) memmove(cp, &w_ifp, sizeof (w_ifp));
+ w.w_rtm.rtm_msglen +=
+ ROUNDUP_LONG(sizeof (struct sockaddr_dl));
+ }
+ }
+
+
+ if (!no_install) {
+ if (TRACERTS)
+ dump_rt_msg("write", &w.w_rtm, w.w_rtm.rtm_msglen);
+ cc = write(rt_sock, &w, w.w_rtm.rtm_msglen);
+ if (cc < 0) {
+ if (errno == ESRCH && (action == RTM_CHANGE ||
+ action == RTM_DELETE)) {
+ trace_act("route disappeared before" PAT, ARGS);
+ if (action == RTM_CHANGE) {
+ action = RTM_ADD;
+ goto again;
+ }
+ return;
+ }
+ writelog(LOG_WARNING, "write(rt_sock)" PAT ": %s ",
+ ARGS, rip_strerror(errno));
+ return;
+ } else if (cc != w.w_rtm.rtm_msglen) {
+ msglog("write(rt_sock) wrote %ld instead of %d for" PAT,
+ cc, w.w_rtm.rtm_msglen, ARGS);
+ return;
+ }
+ }
+ if (TRACEKERNEL)
+ trace_misc("write kernel" PAT, ARGS);
+#undef PAT
+#undef ARGS
+}
+
+
+/* Hash table containing our image of the kernel forwarding table. */
+#define KHASH_SIZE 71 /* should be prime */
+#define KHASH(a, m) khash_bins[((a) ^ (m)) % KHASH_SIZE]
+static struct khash *khash_bins[KHASH_SIZE];
+
+#define K_KEEP_LIM 30 /* k_keep */
+
+static struct khash *
+kern_find(in_addr_t dst, in_addr_t mask, in_addr_t gate,
+ struct interface *ifp, struct khash ***ppk)
+{
+ struct khash *k, **pk;
+
+ for (pk = &KHASH(dst, mask); (k = *pk) != NULL; pk = &k->k_next) {
+ if (k->k_dst == dst && k->k_mask == mask &&
+ (gate == 0 || k->k_gate == gate) &&
+ (ifp == NULL || k->k_ifp == ifp)) {
+ break;
+ }
+ }
+ if (ppk != NULL)
+ *ppk = pk;
+ return (k);
+}
+
+
+/*
+ * Find out if there is an alternate route to a given destination
+ * off of a given interface.
+ */
+static struct khash *
+kern_alternate(in_addr_t dst, in_addr_t mask, in_addr_t gate,
+ struct interface *ifp, struct khash ***ppk)
+{
+ struct khash *k, **pk;
+
+ for (pk = &KHASH(dst, mask); (k = *pk) != NULL; pk = &k->k_next) {
+ if (k->k_dst == dst && k->k_mask == mask &&
+ (k->k_gate != gate) &&
+ (k->k_ifp == ifp)) {
+ break;
+ }
+ }
+ if (ppk != NULL)
+ *ppk = pk;
+ return (k);
+}
+
+static struct khash *
+kern_add(in_addr_t dst, uint32_t mask, in_addr_t gate, struct interface *ifp)
+{
+ struct khash *k, **pk;
+
+ k = kern_find(dst, mask, gate, ifp, &pk);
+ if (k != NULL)
+ return (k);
+
+ k = rtmalloc(sizeof (*k), "kern_add");
+
+ (void) memset(k, 0, sizeof (*k));
+ k->k_dst = dst;
+ k->k_mask = mask;
+ k->k_state = KS_NEW;
+ k->k_keep = now.tv_sec;
+ k->k_gate = gate;
+ k->k_ifp = ifp;
+ *pk = k;
+
+ return (k);
+}
+
+/* delete all khash entries that are wired through the interface ifp */
+void
+kern_flush_ifp(struct interface *ifp)
+{
+ struct khash *k, *kprev, *knext;
+ int i;
+
+ for (i = 0; i < KHASH_SIZE; i++) {
+ kprev = NULL;
+ for (k = khash_bins[i]; k != NULL; k = knext) {
+ knext = k->k_next;
+ if (k->k_ifp == ifp) {
+ if (kprev != NULL)
+ kprev->k_next = k->k_next;
+ else
+ khash_bins[i] = k->k_next;
+ free(k);
+ continue;
+ }
+ kprev = k;
+ }
+ }
+}
+
+/*
+ * rewire khash entries that currently go through oldifp to
+ * go through newifp.
+ */
+void
+kern_rewire_ifp(struct interface *oldifp, struct interface *newifp)
+{
+ struct khash *k;
+ int i;
+
+ for (i = 0; i < KHASH_SIZE; i++) {
+ for (k = khash_bins[i]; k; k = k->k_next) {
+ if (k->k_ifp == oldifp) {
+ k->k_ifp = newifp;
+ trace_misc("kern_rewire_ifp k 0x%lx "
+ "from %s to %s", k, oldifp->int_name,
+ newifp->int_name);
+ }
+ }
+ }
+}
+
+
+/*
+ * Check that a static route it is still in the daemon table, and not
+ * deleted by interfaces coming and going. This is also the routine
+ * responsible for adding new static routes to the daemon table.
+ */
+static void
+kern_check_static(struct khash *k, struct interface *ifp)
+{
+ struct rt_entry *rt;
+ struct rt_spare new;
+ uint16_t rt_state = RS_STATIC;
+
+ (void) memset(&new, 0, sizeof (new));
+ new.rts_ifp = ifp;
+ new.rts_gate = k->k_gate;
+ new.rts_router = (ifp != NULL) ? ifp->int_addr : loopaddr;
+ new.rts_metric = k->k_metric;
+ new.rts_time = now.tv_sec;
+ new.rts_origin = RO_STATIC;
+
+ rt = rtget(k->k_dst, k->k_mask);
+ if ((ifp != NULL && !IS_IFF_ROUTING(ifp->int_if_flags)) ||
+ (k->k_state & KS_PRIVATE))
+ rt_state |= RS_NOPROPAGATE;
+
+ if (rt != NULL) {
+ if ((rt->rt_state & RS_STATIC) == 0) {
+ /*
+ * We are already tracking this dest/mask
+ * via RIP/RDISC. Ignore the static route,
+ * because we don't currently have a good
+ * way to compare metrics on static routes
+ * with rip metrics, and therefore cannot
+ * mix and match the two.
+ */
+ return;
+ }
+ rt_state |= rt->rt_state;
+ if (rt->rt_state != rt_state)
+ rtchange(rt, rt_state, &new, 0);
+ } else {
+ rtadd(k->k_dst, k->k_mask, rt_state, &new);
+ }
+}
+
+
+/* operate on a kernel entry */
+static void
+kern_ioctl(struct khash *k,
+ int action, /* RTM_DELETE, etc */
+ int flags)
+{
+ if (((k->k_state & (KS_IF|KS_PASSIVE)) == KS_IF) ||
+ (k->k_state & KS_DEPRE_IF)) {
+ /*
+ * Prevent execution of RTM_DELETE, RTM_ADD or
+ * RTM_CHANGE of interface routes
+ */
+ trace_act("Blocking execution of %s %s --> %s ",
+ rtm_type_name(action),
+ addrname(k->k_dst, k->k_mask, 0), naddr_ntoa(k->k_gate));
+ return;
+ }
+
+ switch (action) {
+ case RTM_DELETE:
+ k->k_state &= ~KS_DYNAMIC;
+ if (k->k_state & KS_DELETED)
+ return;
+ k->k_state |= KS_DELETED;
+ break;
+ case RTM_ADD:
+ k->k_state &= ~KS_DELETED;
+ break;
+ case RTM_CHANGE:
+ if (k->k_state & KS_DELETED) {
+ action = RTM_ADD;
+ k->k_state &= ~KS_DELETED;
+ }
+ break;
+ }
+
+ rtioctl(action, k->k_dst, k->k_gate, k->k_mask, k->k_ifp,
+ k->k_metric, flags);
+}
+
+
+/* add a route the kernel told us */
+static void
+rtm_add(struct rt_msghdr *rtm,
+ struct rt_addrinfo *info,
+ time_t keep,
+ boolean_t interf_route,
+ struct interface *ifptr)
+{
+ struct khash *k;
+ struct interface *ifp = ifptr;
+ in_addr_t mask, gate = 0;
+ static struct msg_limit msg_no_ifp;
+
+ if (rtm->rtm_flags & RTF_HOST) {
+ mask = HOST_MASK;
+ } else if (INFO_MASK(info) != 0) {
+ mask = ntohl(S_ADDR(INFO_MASK(info)));
+ } else {
+ writelog(LOG_WARNING,
+ "ignore %s without mask", rtm_type_name(rtm->rtm_type));
+ return;
+ }
+
+ /*
+ * Find the interface toward the gateway.
+ */
+ if (INFO_GATE(info) != NULL)
+ gate = S_ADDR(INFO_GATE(info));
+
+ if (ifp == NULL) {
+ if (INFO_GATE(info) != NULL)
+ ifp = iflookup(gate);
+ if (ifp == NULL)
+ msglim(&msg_no_ifp, gate,
+ "route %s --> %s nexthop is not directly connected",
+ addrname(S_ADDR(INFO_DST(info)), mask, 0),
+ naddr_ntoa(gate));
+ }
+
+ k = kern_add(S_ADDR(INFO_DST(info)), mask, gate, ifp);
+
+ if (k->k_state & KS_NEW)
+ k->k_keep = now.tv_sec+keep;
+ if (INFO_GATE(info) == 0) {
+ trace_act("note %s without gateway",
+ rtm_type_name(rtm->rtm_type));
+ k->k_metric = HOPCNT_INFINITY;
+ } else if (INFO_GATE(info)->ss_family != AF_INET) {
+ trace_act("note %s with gateway AF=%d",
+ rtm_type_name(rtm->rtm_type),
+ INFO_GATE(info)->ss_family);
+ k->k_metric = HOPCNT_INFINITY;
+ } else {
+ k->k_gate = S_ADDR(INFO_GATE(info));
+ k->k_metric = rtm->rtm_rmx.rmx_hopcount;
+ if (k->k_metric < 0)
+ k->k_metric = 0;
+ else if (k->k_metric > HOPCNT_INFINITY-1)
+ k->k_metric = HOPCNT_INFINITY-1;
+ }
+
+ if ((k->k_state & KS_NEW) && interf_route) {
+ if (k->k_gate != 0 && findifaddr(k->k_gate) == NULL)
+ k->k_state |= KS_DEPRE_IF;
+ else
+ k->k_state |= KS_IF;
+ }
+
+ k->k_state &= ~(KS_NEW | KS_DELETE | KS_ADD | KS_CHANGE | KS_DEL_ADD |
+ KS_STATIC | KS_GATEWAY | KS_DELETED | KS_PRIVATE | KS_CHECK);
+ if (rtm->rtm_flags & RTF_GATEWAY)
+ k->k_state |= KS_GATEWAY;
+ if (rtm->rtm_flags & RTF_STATIC)
+ k->k_state |= KS_STATIC;
+ if (rtm->rtm_flags & RTF_PRIVATE)
+ k->k_state |= KS_PRIVATE;
+
+
+ if (rtm->rtm_flags & (RTF_DYNAMIC | RTF_MODIFIED)) {
+ if (INFO_AUTHOR(info) != 0 &&
+ INFO_AUTHOR(info)->ss_family == AF_INET)
+ ifp = iflookup(S_ADDR(INFO_AUTHOR(info)));
+ else
+ ifp = NULL;
+ if (should_supply(ifp) && (ifp == NULL ||
+ !(ifp->int_state & IS_REDIRECT_OK))) {
+ /*
+ * Routers are not supposed to listen to redirects,
+ * so delete it if it came via an unknown interface
+ * or the interface does not have special permission.
+ */
+ k->k_state &= ~KS_DYNAMIC;
+ k->k_state |= KS_DELETE;
+ LIM_SEC(need_kern, 0);
+ trace_act("mark for deletion redirected %s --> %s"
+ " via %s",
+ addrname(k->k_dst, k->k_mask, 0),
+ naddr_ntoa(k->k_gate),
+ ifp ? ifp->int_name : "unknown interface");
+ } else {
+ k->k_state |= KS_DYNAMIC;
+ k->k_redirect_time = now.tv_sec;
+ trace_act("accept redirected %s --> %s via %s",
+ addrname(k->k_dst, k->k_mask, 0),
+ naddr_ntoa(k->k_gate),
+ ifp ? ifp->int_name : "unknown interface");
+ }
+ return;
+ }
+
+ /*
+ * If it is not a static route, quit until the next comparison
+ * between the kernel and daemon tables, when it will be deleted.
+ */
+ if (!(k->k_state & KS_STATIC)) {
+ if (!(k->k_state & (KS_IF|KS_DEPRE_IF|KS_FILE)))
+ k->k_state |= KS_DELETE;
+ LIM_SEC(need_kern, k->k_keep);
+ return;
+ }
+
+ /*
+ * Put static routes with real metrics into the daemon table so
+ * they can be advertised.
+ */
+
+ kern_check_static(k, ifp);
+}
+
+
+/* deal with packet loss */
+static void
+rtm_lose(struct rt_msghdr *rtm, struct rt_addrinfo *info)
+{
+ if (INFO_GATE(info) == NULL || INFO_GATE(info)->ss_family != AF_INET) {
+ trace_act("ignore %s without gateway",
+ rtm_type_name(rtm->rtm_type));
+ age(0);
+ return;
+ }
+
+ if (rdisc_ok)
+ rdisc_age(S_ADDR(INFO_GATE(info)));
+ age(S_ADDR(INFO_GATE(info)));
+}
+
+
+/*
+ * Make the gateway slot of an info structure point to something
+ * useful. If it is not already useful, but it specifies an interface,
+ * then fill in the sockaddr_in provided and point it there.
+ */
+static int
+get_info_gate(struct sockaddr_storage **ssp, struct sockaddr_in *sin)
+{
+ struct sockaddr_dl *sdl = (struct sockaddr_dl *)*ssp;
+ struct interface *ifp;
+
+ if (sdl == NULL)
+ return (0);
+ if ((sdl)->sdl_family == AF_INET)
+ return (1);
+ if ((sdl)->sdl_family != AF_LINK)
+ return (0);
+
+ ifp = ifwithindex(sdl->sdl_index, _B_TRUE);
+ if (ifp == NULL)
+ return (0);
+
+ sin->sin_addr.s_addr = ifp->int_addr;
+ sin->sin_family = AF_INET;
+ /* LINTED */
+ *ssp = (struct sockaddr_storage *)sin;
+
+ return (1);
+}
+
+
+/*
+ * Clean the kernel table by copying it to the daemon image.
+ * Eventually the daemon will delete any extra routes.
+ */
+void
+sync_kern(void)
+{
+ int i;
+ struct khash *k;
+ struct {
+ struct T_optmgmt_req req;
+ struct opthdr hdr;
+ } req;
+ union {
+ struct T_optmgmt_ack ack;
+ unsigned char space[64];
+ } ack;
+ struct opthdr *rh;
+ struct strbuf cbuf, dbuf;
+ int ipfd, nroutes, flags, r;
+ mib2_ipRouteEntry_t routes[8];
+ mib2_ipRouteEntry_t *rp;
+ struct rt_msghdr rtm;
+ struct rt_addrinfo info;
+ struct sockaddr_in sin_dst;
+ struct sockaddr_in sin_gate;
+ struct sockaddr_in sin_mask;
+ struct sockaddr_in sin_author;
+ struct interface *ifp;
+ char ifname[LIFNAMSIZ + 1];
+
+ for (i = 0; i < KHASH_SIZE; i++) {
+ for (k = khash_bins[i]; k != NULL; k = k->k_next) {
+ if (!(k->k_state & (KS_IF|KS_DEPRE_IF)))
+ k->k_state |= KS_CHECK;
+ }
+ }
+
+ ipfd = open(IP_DEV_NAME, O_RDWR);
+ if (ipfd == -1) {
+ msglog("open " IP_DEV_NAME ": %s", rip_strerror(errno));
+ goto hash_clean;
+ }
+
+ req.req.PRIM_type = T_OPTMGMT_REQ;
+ req.req.OPT_offset = (caddr_t)&req.hdr - (caddr_t)&req;
+ req.req.OPT_length = sizeof (req.hdr);
+ req.req.MGMT_flags = T_CURRENT;
+
+ req.hdr.level = MIB2_IP;
+ req.hdr.name = 0;
+ req.hdr.len = 0;
+
+ cbuf.buf = (caddr_t)&req;
+ cbuf.len = sizeof (req);
+
+ if (putmsg(ipfd, &cbuf, NULL, 0) == -1) {
+ msglog("T_OPTMGMT_REQ putmsg: %s", rip_strerror(errno));
+ goto hash_clean;
+ }
+
+ for (;;) {
+ cbuf.buf = (caddr_t)&ack;
+ cbuf.maxlen = sizeof (ack);
+ dbuf.buf = (caddr_t)routes;
+ dbuf.maxlen = sizeof (routes);
+ flags = 0;
+ r = getmsg(ipfd, &cbuf, &dbuf, &flags);
+ if (r == -1) {
+ msglog("T_OPTMGMT_REQ getmsg: %s", rip_strerror(errno));
+ goto hash_clean;
+ }
+
+ if (cbuf.len < sizeof (struct T_optmgmt_ack) ||
+ ack.ack.PRIM_type != T_OPTMGMT_ACK ||
+ ack.ack.MGMT_flags != T_SUCCESS ||
+ ack.ack.OPT_length < sizeof (struct opthdr)) {
+ msglog("bad T_OPTMGMT response; len=%d prim=%d "
+ "flags=%d optlen=%d", cbuf.len, ack.ack.PRIM_type,
+ ack.ack.MGMT_flags, ack.ack.OPT_length);
+ goto hash_clean;
+ }
+ /* LINTED */
+ rh = (struct opthdr *)((caddr_t)&ack + ack.ack.OPT_offset);
+ if (rh->level == 0 && rh->name == 0) {
+ break;
+ }
+ if (rh->level != MIB2_IP || rh->name != MIB2_IP_21) {
+ while (r == MOREDATA) {
+ r = getmsg(ipfd, NULL, &dbuf, &flags);
+ }
+ continue;
+ }
+ break;
+ }
+
+ (void) memset(&rtm, 0, sizeof (rtm));
+ (void) memset(&info, 0, sizeof (info));
+ (void) memset(&sin_dst, 0, sizeof (sin_dst));
+ (void) memset(&sin_gate, 0, sizeof (sin_gate));
+ (void) memset(&sin_mask, 0, sizeof (sin_mask));
+ (void) memset(&sin_author, 0, sizeof (sin_author));
+ sin_dst.sin_family = AF_INET;
+ /* LINTED */
+ info.rti_info[RTAX_DST] = (struct sockaddr_storage *)&sin_dst;
+ sin_gate.sin_family = AF_INET;
+ /* LINTED */
+ info.rti_info[RTAX_GATEWAY] = (struct sockaddr_storage *)&sin_gate;
+ sin_mask.sin_family = AF_INET;
+ /* LINTED */
+ info.rti_info[RTAX_NETMASK] = (struct sockaddr_storage *)&sin_mask;
+ sin_dst.sin_family = AF_INET;
+ /* LINTED */
+ info.rti_info[RTAX_AUTHOR] = (struct sockaddr_storage *)&sin_author;
+
+ for (;;) {
+ nroutes = dbuf.len / sizeof (mib2_ipRouteEntry_t);
+ for (rp = routes; nroutes > 0; ++rp, nroutes--) {
+
+ /*
+ * Ignore IRE cache, broadcast, and local address
+ * entries; they're not subject to routing socket
+ * control.
+ */
+ if (rp->ipRouteInfo.re_ire_type &
+ (IRE_BROADCAST | IRE_CACHE | IRE_LOCAL))
+ continue;
+
+ /* ignore multicast addresses */
+ if (IN_MULTICAST(ntohl(rp->ipRouteDest)))
+ continue;
+
+
+#ifdef DEBUG_KERNEL_ROUTE_READ
+ (void) fprintf(stderr, "route type %d, ire type %08X, "
+ "flags %08X: %s", rp->ipRouteType,
+ rp->ipRouteInfo.re_ire_type,
+ rp->ipRouteInfo.re_flags,
+ naddr_ntoa(rp->ipRouteDest));
+ (void) fprintf(stderr, " %s",
+ naddr_ntoa(rp->ipRouteMask));
+ (void) fprintf(stderr, " %s\n",
+ naddr_ntoa(rp->ipRouteNextHop));
+#endif
+
+ /* Fake up the needed entries */
+ rtm.rtm_flags = rp->ipRouteInfo.re_flags;
+ rtm.rtm_type = RTM_GET;
+ rtm.rtm_rmx.rmx_hopcount = rp->ipRouteMetric1;
+
+ (void) memset(ifname, 0, sizeof (ifname));
+ if (rp->ipRouteIfIndex.o_length <
+ sizeof (rp->ipRouteIfIndex.o_bytes))
+ rp->ipRouteIfIndex.o_bytes[
+ rp->ipRouteIfIndex.o_length] = '\0';
+ (void) strncpy(ifname,
+ rp->ipRouteIfIndex.o_bytes,
+ sizeof (ifname));
+
+ /*
+ * First try to match up on gwkludge entries
+ * before trying to match ifp by name.
+ */
+ if ((ifp = gwkludge_iflookup(rp->ipRouteDest,
+ rp->ipRouteNextHop, rp->ipRouteMask)) == NULL)
+ ifp = ifwithname(ifname);
+
+ info.rti_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
+ if (rp->ipRouteInfo.re_ire_type & IRE_HOST_REDIRECT)
+ info.rti_addrs |= RTA_AUTHOR;
+ sin_dst.sin_addr.s_addr = rp->ipRouteDest;
+ sin_gate.sin_addr.s_addr = rp->ipRouteNextHop;
+ sin_mask.sin_addr.s_addr = rp->ipRouteMask;
+ sin_author.sin_addr.s_addr =
+ rp->ipRouteInfo.re_src_addr;
+
+ /*
+ * Note static routes and interface routes, and also
+ * preload the image of the kernel table so that
+ * we can later clean it, as well as avoid making
+ * unneeded changes. Keep the old kernel routes for a
+ * few seconds to allow a RIP or router-discovery
+ * response to be heard.
+ */
+ rtm_add(&rtm, &info, MAX_WAITTIME,
+ ((rp->ipRouteInfo.re_ire_type &
+ (IRE_INTERFACE|IRE_LOOPBACK)) != 0), ifp);
+ }
+ if (r == 0) {
+ break;
+ }
+ r = getmsg(ipfd, NULL, &dbuf, &flags);
+ }
+
+hash_clean:
+ if (ipfd != -1)
+ (void) close(ipfd);
+ for (i = 0; i < KHASH_SIZE; i++) {
+ for (k = khash_bins[i]; k != NULL; k = k->k_next) {
+
+ /*
+ * KS_DELETED routes have been removed from the
+ * kernel, but we keep them around for reasons
+ * stated in del_static(), so we skip the check
+ * for KS_DELETED routes here.
+ */
+ if ((k->k_state & (KS_CHECK|KS_DELETED)) == KS_CHECK) {
+
+ if (!(k->k_state & KS_DYNAMIC))
+ writelog(LOG_WARNING,
+ "%s --> %s disappeared from kernel",
+ addrname(k->k_dst, k->k_mask, 0),
+ naddr_ntoa(k->k_gate));
+ del_static(k->k_dst, k->k_mask, k->k_gate,
+ k->k_ifp, 1);
+
+ }
+ }
+ }
+}
+
+
+/* Listen to announcements from the kernel */
+void
+read_rt(void)
+{
+ long cc;
+ struct interface *ifp;
+ struct sockaddr_in gate_sin;
+ in_addr_t mask, gate;
+ union {
+ struct {
+ struct rt_msghdr rtm;
+ struct sockaddr_storage addrs[RTA_NUMBITS];
+ } r;
+ struct if_msghdr ifm;
+ } m;
+ char str[100], *strp;
+ struct rt_addrinfo info;
+
+
+ for (;;) {
+ cc = read(rt_sock, &m, sizeof (m));
+ if (cc <= 0) {
+ if (cc < 0 && errno != EWOULDBLOCK)
+ LOGERR("read(rt_sock)");
+ return;
+ }
+
+ if (TRACERTS)
+ dump_rt_msg("read", &m.r.rtm, cc);
+
+ if (cc < m.r.rtm.rtm_msglen) {
+ msglog("routing message truncated (%d < %d)",
+ cc, m.r.rtm.rtm_msglen);
+ }
+
+ if (m.r.rtm.rtm_version != RTM_VERSION) {
+ msglog("bogus routing message version %d",
+ m.r.rtm.rtm_version);
+ continue;
+ }
+
+ ifp = NULL;
+
+ if (m.r.rtm.rtm_type == RTM_IFINFO ||
+ m.r.rtm.rtm_type == RTM_NEWADDR ||
+ m.r.rtm.rtm_type == RTM_DELADDR) {
+ strp = if_bit_string(m.ifm.ifm_flags, _B_TRUE);
+ if (strp == NULL) {
+ strp = str;
+ (void) sprintf(str, "%#x", m.ifm.ifm_flags);
+ }
+ ifp = ifwithindex(m.ifm.ifm_index,
+ m.r.rtm.rtm_type != RTM_DELADDR);
+ if (ifp == NULL) {
+ char ifname[LIFNAMSIZ], *ifnamep;
+
+ ifnamep = if_indextoname(m.ifm.ifm_index,
+ ifname);
+ if (ifnamep == NULL) {
+ trace_act("note %s with flags %s"
+ " for unknown interface index #%d",
+ rtm_type_name(m.r.rtm.rtm_type),
+ strp, m.ifm.ifm_index);
+ } else {
+ trace_act("note %s with flags %s"
+ " for unknown interface %s",
+ rtm_type_name(m.r.rtm.rtm_type),
+ strp, ifnamep);
+ }
+ } else {
+ trace_act("note %s with flags %s for %s",
+ rtm_type_name(m.r.rtm.rtm_type),
+ strp, ifp->int_name);
+ }
+ if (strp != str)
+ free(strp);
+
+ /*
+ * After being informed of a change to an interface,
+ * check them all now if the check would otherwise
+ * be a long time from now, if the interface is
+ * not known, or if the interface has been turned
+ * off or on.
+ */
+ if (ifscan_timer.tv_sec-now.tv_sec >=
+ CHECK_BAD_INTERVAL || ifp == NULL ||
+ ((ifp->int_if_flags ^ m.ifm.ifm_flags) &
+ IFF_UP) != 0)
+ ifscan_timer.tv_sec = now.tv_sec;
+ continue;
+ } else {
+ if (m.r.rtm.rtm_index != 0)
+ ifp = ifwithindex(m.r.rtm.rtm_index, 1);
+ }
+
+ (void) strlcpy(str, rtm_type_name(m.r.rtm.rtm_type),
+ sizeof (str));
+ strp = &str[strlen(str)];
+ if (m.r.rtm.rtm_type <= RTM_CHANGE)
+ strp += snprintf(strp, sizeof (str) - (strp - str),
+ " from pid %d", (int)m.r.rtm.rtm_pid);
+
+ /* LINTED */
+ (void) rt_xaddrs(&info, (struct sockaddr_storage *)(&m.r.rtm +
+ 1), (char *)&m + cc, m.r.rtm.rtm_addrs);
+
+ if (INFO_DST(&info) == 0) {
+ trace_act("ignore %s without dst", str);
+ continue;
+ }
+
+ if (INFO_DST(&info)->ss_family != AF_INET) {
+ trace_act("ignore %s for AF %d", str,
+ INFO_DST(&info)->ss_family);
+ continue;
+ }
+
+ mask = ((INFO_MASK(&info) != 0) ?
+ ntohl(S_ADDR(INFO_MASK(&info))) :
+ (m.r.rtm.rtm_flags & RTF_HOST) ?
+ HOST_MASK : std_mask(S_ADDR(INFO_DST(&info))));
+
+ strp += snprintf(strp, sizeof (str) - (strp - str), ": %s",
+ addrname(S_ADDR(INFO_DST(&info)), mask, 0));
+
+ if (IN_MULTICAST(ntohl(S_ADDR(INFO_DST(&info))))) {
+ trace_act("ignore multicast %s", str);
+ continue;
+ }
+
+ if (m.r.rtm.rtm_flags & RTF_LLINFO) {
+ trace_act("ignore ARP %s", str);
+ continue;
+ }
+
+ if (get_info_gate(&INFO_GATE(&info), &gate_sin)) {
+ gate = S_ADDR(INFO_GATE(&info));
+ strp += snprintf(strp, sizeof (str) - (strp - str),
+ " --> %s", naddr_ntoa(gate));
+ } else {
+ gate = 0;
+ }
+
+ if (INFO_AUTHOR(&info) != 0)
+ strp += snprintf(strp, sizeof (str) - (strp - str),
+ " by authority of %s",
+ saddr_ntoa(INFO_AUTHOR(&info)));
+
+ switch (m.r.rtm.rtm_type) {
+ case RTM_ADD:
+ case RTM_CHANGE:
+ case RTM_REDIRECT:
+ if (m.r.rtm.rtm_errno != 0) {
+ trace_act("ignore %s with \"%s\" error",
+ str, rip_strerror(m.r.rtm.rtm_errno));
+ } else {
+ trace_act("%s", str);
+ rtm_add(&m.r.rtm, &info, 0,
+ !(m.r.rtm.rtm_flags & RTF_GATEWAY) &&
+ m.r.rtm.rtm_type != RTM_REDIRECT, ifp);
+
+ }
+ break;
+
+ case RTM_DELETE:
+ if (m.r.rtm.rtm_errno != 0 &&
+ m.r.rtm.rtm_errno != ESRCH) {
+ trace_act("ignore %s with \"%s\" error",
+ str, rip_strerror(m.r.rtm.rtm_errno));
+ } else {
+ trace_act("%s", str);
+ del_static(S_ADDR(INFO_DST(&info)), mask,
+ gate, ifp, 1);
+ }
+ break;
+
+ case RTM_LOSING:
+ trace_act("%s", str);
+ rtm_lose(&m.r.rtm, &info);
+ break;
+
+ default:
+ trace_act("ignore %s", str);
+ break;
+ }
+ }
+}
+
+
+/*
+ * Disassemble a routing message. The result is an array of pointers
+ * to sockaddr_storage structures stored in the info argument.
+ *
+ * ss is a pointer to the beginning of the data following the
+ * rt_msghdr contained in the routing socket message, which consists
+ * of a string of concatenated sockaddr structure of different types.
+ */
+static int
+rt_xaddrs(struct rt_addrinfo *info,
+ struct sockaddr_storage *ss,
+ char *lim,
+ int addrs)
+{
+ int retv = 0;
+ int i;
+ int abit;
+ int complaints;
+ static int prev_complaints;
+
+#define XBAD_AF 0x1
+#define XBAD_SHORT 0x2
+#define XBAD_LONG 0x4
+
+ (void) memset(info, 0, sizeof (*info));
+ info->rti_addrs = addrs;
+ complaints = 0;
+ for (i = 0, abit = 1; i < RTAX_MAX && (char *)ss < lim;
+ i++, abit <<= 1) {
+ if ((addrs & abit) == 0)
+ continue;
+ info->rti_info[i] = ss;
+ /* Horrible interface here */
+ switch (ss->ss_family) {
+ case AF_UNIX:
+ /* LINTED */
+ ss = (struct sockaddr_storage *)(
+ (struct sockaddr_un *)ss + 1);
+ break;
+ case AF_INET:
+ /* LINTED */
+ ss = (struct sockaddr_storage *)(
+ (struct sockaddr_in *)ss + 1);
+ break;
+ case AF_LINK:
+ /* LINTED */
+ ss = (struct sockaddr_storage *)(
+ (struct sockaddr_dl *)ss + 1);
+ break;
+ case AF_INET6:
+ /* LINTED */
+ ss = (struct sockaddr_storage *)(
+ (struct sockaddr_in6 *)ss + 1);
+ break;
+ default:
+ if (!(prev_complaints & XBAD_AF))
+ writelog(LOG_WARNING,
+ "unknown address family %d "
+ "encountered", ss->ss_family);
+ if (complaints & XBAD_AF)
+ goto xaddr_done;
+ /* LINTED */
+ ss = (struct sockaddr_storage *)(
+ (struct sockaddr *)ss + 1);
+ complaints |= XBAD_AF;
+ info->rti_addrs &= abit - 1;
+ addrs = info->rti_addrs;
+ retv = -1;
+ break;
+ }
+ if ((char *)ss > lim) {
+ if (!(prev_complaints & XBAD_SHORT))
+ msglog("sockaddr %d too short by %d "
+ "bytes", i + 1, (char *)ss - lim);
+ complaints |= XBAD_SHORT;
+ info->rti_info[i] = NULL;
+ info->rti_addrs &= abit - 1;
+ retv = -1;
+ goto xaddr_done;
+ }
+ }
+ if ((char *)ss != lim) {
+ if (!(prev_complaints & XBAD_LONG))
+ msglog("%d bytes of routing message left over",
+ lim - (char *)ss);
+ complaints |= XBAD_LONG;
+ retv = -1;
+ }
+xaddr_done:
+ prev_complaints = complaints;
+ return (retv);
+}
+
+
+/* after aggregating, note routes that belong in the kernel */
+static void
+kern_out(struct ag_info *ag)
+{
+ struct khash *k;
+
+ /*
+ * Do not install bad routes if they are not already present.
+ * This includes routes that had RS_NET_SYN for interfaces that
+ * recently died.
+ */
+ if (ag->ag_metric == HOPCNT_INFINITY) {
+ k = kern_find(htonl(ag->ag_dst_h), ag->ag_mask,
+ ag->ag_nhop, ag->ag_ifp, NULL);
+ if (k == NULL)
+ return;
+ } else {
+ k = kern_add(htonl(ag->ag_dst_h), ag->ag_mask, ag->ag_nhop,
+ ag->ag_ifp);
+ }
+
+ if (k->k_state & KS_NEW) {
+ /* will need to add new entry to the kernel table */
+ k->k_state = KS_ADD;
+ if (ag->ag_state & AGS_GATEWAY)
+ k->k_state |= KS_GATEWAY;
+ if (ag->ag_state & AGS_IF)
+ k->k_state |= KS_IF;
+ if (ag->ag_state & AGS_PASSIVE)
+ k->k_state |= KS_PASSIVE;
+ if (ag->ag_state & AGS_FILE)
+ k->k_state |= KS_FILE;
+ k->k_gate = ag->ag_nhop;
+ k->k_ifp = ag->ag_ifp;
+ k->k_metric = ag->ag_metric;
+ return;
+ }
+
+ if ((k->k_state & (KS_STATIC|KS_DEPRE_IF)) ||
+ ((k->k_state & (KS_IF|KS_PASSIVE)) == KS_IF)) {
+ return;
+ }
+
+ /* modify existing kernel entry if necessary */
+ if (k->k_gate == ag->ag_nhop && k->k_ifp == ag->ag_ifp &&
+ k->k_metric != ag->ag_metric) {
+ /*
+ * Must delete bad interface routes etc.
+ * to change them.
+ */
+ if (k->k_metric == HOPCNT_INFINITY)
+ k->k_state |= KS_DEL_ADD;
+ k->k_gate = ag->ag_nhop;
+ k->k_metric = ag->ag_metric;
+ k->k_state |= KS_CHANGE;
+ }
+
+ /*
+ * If the daemon thinks the route should exist, forget
+ * about any redirections.
+ * If the daemon thinks the route should exist, eventually
+ * override manual intervention by the operator.
+ */
+ if ((k->k_state & (KS_DYNAMIC | KS_DELETED)) != 0) {
+ k->k_state &= ~KS_DYNAMIC;
+ k->k_state |= (KS_ADD | KS_DEL_ADD);
+ }
+
+ if ((k->k_state & KS_GATEWAY) && !(ag->ag_state & AGS_GATEWAY)) {
+ k->k_state &= ~KS_GATEWAY;
+ k->k_state |= (KS_ADD | KS_DEL_ADD);
+ } else if (!(k->k_state & KS_GATEWAY) && (ag->ag_state & AGS_GATEWAY)) {
+ k->k_state |= KS_GATEWAY;
+ k->k_state |= (KS_ADD | KS_DEL_ADD);
+ }
+
+ /*
+ * Deleting-and-adding is necessary to change aspects of a route.
+ * Just delete instead of deleting and then adding a bad route.
+ * Otherwise, we want to keep the route in the kernel.
+ */
+ if (k->k_metric == HOPCNT_INFINITY && (k->k_state & KS_DEL_ADD))
+ k->k_state |= KS_DELETE;
+ else
+ k->k_state &= ~KS_DELETE;
+#undef RT
+}
+
+/*
+ * Update our image of the kernel forwarding table using the given
+ * route from our internal routing table.
+ */
+
+/*ARGSUSED1*/
+static int
+walk_kern(struct radix_node *rn, void *argp)
+{
+#define RT ((struct rt_entry *)rn)
+ uint8_t metric, pref;
+ uint_t ags = 0;
+ int i;
+ struct rt_spare *rts;
+
+ /* Do not install synthetic routes */
+ if (RT->rt_state & RS_NET_SYN)
+ return (0);
+
+ /*
+ * Do not install static routes here. Only
+ * read_rt->rtm_add->kern_add should install those
+ */
+ if ((RT->rt_state & RS_STATIC) &&
+ (RT->rt_spares[0].rts_origin != RO_FILE))
+ return (0);
+
+ /* Do not clobber kernel if this is a route for a dead interface */
+ if (RT->rt_state & RS_BADIF)
+ return (0);
+
+ if (!(RT->rt_state & RS_IF)) {
+ /* This is an ordinary route, not for an interface. */
+
+ /*
+ * aggregate, ordinary good routes without regard to
+ * their metric
+ */
+ pref = 1;
+ ags |= (AGS_GATEWAY | AGS_SUPPRESS | AGS_AGGREGATE);
+
+ /*
+ * Do not install host routes directly to hosts, to avoid
+ * interfering with ARP entries in the kernel table.
+ */
+ if (RT_ISHOST(RT) && ntohl(RT->rt_dst) == RT->rt_gate)
+ return (0);
+
+ } else {
+ /*
+ * This is an interface route.
+ * Do not install routes for "external" remote interfaces.
+ */
+ if (RT->rt_ifp != NULL && (RT->rt_ifp->int_state & IS_EXTERNAL))
+ return (0);
+
+ /* Interfaces should override received routes. */
+ pref = 0;
+ ags |= (AGS_IF | AGS_CORS_GATE);
+ if (RT->rt_ifp != NULL &&
+ !(RT->rt_ifp->int_if_flags & IFF_LOOPBACK) &&
+ (RT->rt_ifp->int_state & (IS_PASSIVE|IS_ALIAS)) ==
+ IS_PASSIVE) {
+ ags |= AGS_PASSIVE;
+ }
+
+ /*
+ * If it is not an interface, or an alias for an interface,
+ * it must be a "gateway."
+ *
+ * If it is a "remote" interface, it is also a "gateway" to
+ * the kernel if is not a alias.
+ */
+ if (RT->rt_ifp == NULL || (RT->rt_ifp->int_state & IS_REMOTE))
+ ags |= (AGS_GATEWAY | AGS_SUPPRESS | AGS_AGGREGATE);
+ }
+
+ metric = RT->rt_metric;
+ if (metric == HOPCNT_INFINITY) {
+ /* If the route is dead, try hard to aggregate. */
+ pref = HOPCNT_INFINITY;
+ ags |= (AGS_FINE_GATE | AGS_SUPPRESS);
+ ags &= ~(AGS_IF | AGS_CORS_GATE);
+ }
+
+ /*
+ * dump all routes that have the same metric as rt_spares[0]
+ * into the kern_table, to be added to the kernel.
+ */
+ for (i = 0; i < RT->rt_num_spares; i++) {
+ rts = &RT->rt_spares[i];
+
+ /* Do not install external routes */
+ if (rts->rts_flags & RTS_EXTERNAL)
+ continue;
+
+ if (rts->rts_metric == metric) {
+ ag_check(RT->rt_dst, RT->rt_mask,
+ rts->rts_router, rts->rts_ifp, rts->rts_gate,
+ metric, pref, 0, 0,
+ (rts->rts_origin & RO_FILE) ? (ags|AGS_FILE) : ags,
+ kern_out);
+ }
+ }
+ return (0);
+#undef RT
+}
+
+
+/* Update the kernel table to match the daemon table. */
+static void
+fix_kern(void)
+{
+ int i;
+ struct khash *k, *pk, *knext;
+
+
+ need_kern = age_timer;
+
+ /* Walk daemon table, updating the copy of the kernel table. */
+ (void) rn_walktree(rhead, walk_kern, NULL);
+ ag_flush(0, 0, kern_out);
+
+ for (i = 0; i < KHASH_SIZE; i++) {
+ pk = NULL;
+ for (k = khash_bins[i]; k != NULL; k = knext) {
+ knext = k->k_next;
+
+ /* Do not touch local interface routes */
+ if ((k->k_state & KS_DEPRE_IF) ||
+ (k->k_state & (KS_IF|KS_PASSIVE)) == KS_IF) {
+ pk = k;
+ continue;
+ }
+
+ /* Do not touch static routes */
+ if (k->k_state & KS_STATIC) {
+ kern_check_static(k, 0);
+ pk = k;
+ continue;
+ }
+
+ /* check hold on routes deleted by the operator */
+ if (k->k_keep > now.tv_sec) {
+ /* ensure we check when the hold is over */
+ LIM_SEC(need_kern, k->k_keep);
+ pk = k;
+ continue;
+ }
+
+ if ((k->k_state & KS_DELETE) &&
+ !(k->k_state & KS_DYNAMIC)) {
+ if ((k->k_dst == RIP_DEFAULT) &&
+ (k->k_ifp != NULL) &&
+ (kern_alternate(RIP_DEFAULT,
+ k->k_mask, k->k_gate, k->k_ifp,
+ NULL) == NULL))
+ rdisc_restore(k->k_ifp);
+ kern_ioctl(k, RTM_DELETE, 0);
+ if (pk != NULL)
+ pk->k_next = knext;
+ else
+ khash_bins[i] = knext;
+ free(k);
+ continue;
+ }
+
+ if (k->k_state & KS_DEL_ADD)
+ kern_ioctl(k, RTM_DELETE, 0);
+
+ if (k->k_state & KS_ADD) {
+ if ((k->k_dst == RIP_DEFAULT) &&
+ (k->k_ifp != NULL))
+ rdisc_suppress(k->k_ifp);
+ kern_ioctl(k, RTM_ADD,
+ ((0 != (k->k_state & (KS_GATEWAY |
+ KS_DYNAMIC))) ? RTF_GATEWAY : 0));
+ } else if (k->k_state & KS_CHANGE) {
+ /*
+ * Should be using RTM_CHANGE here, but
+ * since RTM_CHANGE is currently
+ * not multipath-aware, and assumes
+ * that RTF_GATEWAY implies the gateway
+ * of the route for dst has to be
+ * changed, we play safe, and do a del + add.
+ */
+ kern_ioctl(k, RTM_DELETE, 0);
+ kern_ioctl(k, RTM_ADD,
+ ((0 != (k->k_state & (KS_GATEWAY |
+ KS_DYNAMIC))) ? RTF_GATEWAY : 0));
+ }
+ k->k_state &= ~(KS_ADD|KS_CHANGE|KS_DEL_ADD);
+
+ /*
+ * Mark this route to be deleted in the next cycle.
+ * This deletes routes that disappear from the
+ * daemon table, since the normal aging code
+ * will clear the bit for routes that have not
+ * disappeared from the daemon table.
+ */
+ k->k_state |= KS_DELETE;
+ pk = k;
+ }
+ }
+}
+
+
+/* Delete a static route in the image of the kernel table. */
+void
+del_static(in_addr_t dst, in_addr_t mask, in_addr_t gate,
+ struct interface *ifp, int gone)
+{
+ struct khash *k;
+ struct rt_entry *rt;
+
+ /*
+ * Just mark it in the table to be deleted next time the kernel
+ * table is updated.
+ * If it has already been deleted, mark it as such, and set its
+ * keep-timer so that it will not be deleted again for a while.
+ * This lets the operator delete a route added by the daemon
+ * and add a replacement.
+ */
+ k = kern_find(dst, mask, gate, ifp, NULL);
+ if (k != NULL && (gate == 0 || k->k_gate == gate)) {
+ k->k_state &= ~(KS_STATIC | KS_DYNAMIC | KS_CHECK);
+ k->k_state |= KS_DELETE;
+ if (gone) {
+ k->k_state |= KS_DELETED;
+ k->k_keep = now.tv_sec + K_KEEP_LIM;
+ }
+ }
+
+ rt = rtget(dst, mask);
+ if (rt != NULL && (rt->rt_state & RS_STATIC))
+ rtbad(rt, NULL);
+}
+
+
+/*
+ * Delete all routes generated from ICMP Redirects that use a given gateway,
+ * as well as old redirected routes.
+ */
+void
+del_redirects(in_addr_t bad_gate, time_t old)
+{
+ int i;
+ struct khash *k;
+ boolean_t dosupply = should_supply(NULL);
+
+ for (i = 0; i < KHASH_SIZE; i++) {
+ for (k = khash_bins[i]; k != NULL; k = k->k_next) {
+ if (!(k->k_state & KS_DYNAMIC) ||
+ (k->k_state & (KS_STATIC|KS_IF|KS_DEPRE_IF)))
+ continue;
+
+ if (k->k_gate != bad_gate && k->k_redirect_time > old &&
+ !dosupply)
+ continue;
+
+ k->k_state |= KS_DELETE;
+ k->k_state &= ~KS_DYNAMIC;
+ need_kern.tv_sec = now.tv_sec;
+ trace_act("mark redirected %s --> %s for deletion",
+ addrname(k->k_dst, k->k_mask, 0),
+ naddr_ntoa(k->k_gate));
+ }
+ }
+}
+
+/* Start the daemon tables. */
+void
+rtinit(void)
+{
+ int i;
+ struct ag_info *ag;
+
+ /* Initialize the radix trees */
+ rn_init();
+ (void) rn_inithead((void**)&rhead, 32);
+
+ /* mark all of the slots in the table free */
+ ag_avail = ag_slots;
+ for (ag = ag_slots, i = 1; i < NUM_AG_SLOTS; i++) {
+ ag->ag_fine = ag+1;
+ ag++;
+ }
+}
+
+
+static struct sockaddr_in dst_sock = {AF_INET};
+static struct sockaddr_in mask_sock = {AF_INET};
+
+
+static void
+set_need_flash(void)
+{
+ if (!need_flash) {
+ need_flash = _B_TRUE;
+ /*
+ * Do not send the flash update immediately. Wait a little
+ * while to hear from other routers.
+ */
+ no_flash.tv_sec = now.tv_sec + MIN_WAITTIME;
+ }
+}
+
+
+/* Get a particular routing table entry */
+struct rt_entry *
+rtget(in_addr_t dst, in_addr_t mask)
+{
+ struct rt_entry *rt;
+
+ dst_sock.sin_addr.s_addr = dst;
+ mask_sock.sin_addr.s_addr = htonl(mask);
+ rt = (struct rt_entry *)rhead->rnh_lookup(&dst_sock, &mask_sock, rhead);
+ if (rt == NULL || rt->rt_dst != dst || rt->rt_mask != mask)
+ return (NULL);
+
+ return (rt);
+}
+
+
+/* Find a route to dst as the kernel would. */
+struct rt_entry *
+rtfind(in_addr_t dst)
+{
+ dst_sock.sin_addr.s_addr = dst;
+ return ((struct rt_entry *)rhead->rnh_matchaddr(&dst_sock, rhead));
+}
+
+
+/* add a route to the table */
+void
+rtadd(in_addr_t dst,
+ in_addr_t mask,
+ uint16_t state, /* rt_state for the entry */
+ struct rt_spare *new)
+{
+ struct rt_entry *rt;
+ in_addr_t smask;
+ int i;
+ struct rt_spare *rts;
+
+ /* This is the only function that increments total_routes. */
+ if (total_routes == MAX_ROUTES) {
+ msglog("have maximum (%d) routes", total_routes);
+ return;
+ }
+
+ rt = rtmalloc(sizeof (*rt), "rtadd");
+ (void) memset(rt, 0, sizeof (*rt));
+ rt->rt_spares = rtmalloc(SPARE_INC * sizeof (struct rt_spare),
+ "rtadd");
+ rt->rt_num_spares = SPARE_INC;
+ (void) memset(rt->rt_spares, 0, SPARE_INC * sizeof (struct rt_spare));
+ for (rts = rt->rt_spares, i = rt->rt_num_spares; i != 0; i--, rts++)
+ rts->rts_metric = HOPCNT_INFINITY;
+
+ rt->rt_nodes->rn_key = (uint8_t *)&rt->rt_dst_sock;
+ rt->rt_dst = dst;
+ rt->rt_dst_sock.sin_family = AF_INET;
+ if (mask != HOST_MASK) {
+ smask = std_mask(dst);
+ if ((smask & ~mask) == 0 && mask > smask)
+ state |= RS_SUBNET;
+ }
+ mask_sock.sin_addr.s_addr = htonl(mask);
+ rt->rt_mask = mask;
+ rt->rt_spares[0] = *new;
+ rt->rt_state = state;
+ rt->rt_time = now.tv_sec;
+ rt->rt_poison_metric = HOPCNT_INFINITY;
+ rt->rt_seqno = update_seqno;
+
+ if (TRACEACTIONS)
+ trace_add_del("Add", rt);
+
+ need_kern.tv_sec = now.tv_sec;
+ set_need_flash();
+
+ if (NULL == rhead->rnh_addaddr(&rt->rt_dst_sock, &mask_sock, rhead,
+ rt->rt_nodes)) {
+ msglog("rnh_addaddr() failed for %s mask=%s",
+ naddr_ntoa(dst), naddr_ntoa(htonl(mask)));
+ free(rt);
+ }
+
+ total_routes++;
+}
+
+
+/* notice a changed route */
+void
+rtchange(struct rt_entry *rt,
+ uint16_t state, /* new state bits */
+ struct rt_spare *new,
+ char *label)
+{
+ if (rt->rt_metric != new->rts_metric) {
+ /*
+ * Fix the kernel immediately if it seems the route
+ * has gone bad, since there may be a working route that
+ * aggregates this route.
+ */
+ if (new->rts_metric == HOPCNT_INFINITY) {
+ need_kern.tv_sec = now.tv_sec;
+ if (new->rts_time >= now.tv_sec - EXPIRE_TIME)
+ new->rts_time = now.tv_sec - EXPIRE_TIME;
+ }
+ rt->rt_seqno = update_seqno;
+ set_need_flash();
+ }
+
+ if (rt->rt_gate != new->rts_gate) {
+ need_kern.tv_sec = now.tv_sec;
+ rt->rt_seqno = update_seqno;
+ set_need_flash();
+ }
+
+ state |= (rt->rt_state & RS_SUBNET);
+
+ /* Keep various things from deciding ageless routes are stale. */
+ if (!AGE_RT(state, rt->rt_spares[0].rts_origin, new->rts_ifp))
+ new->rts_time = now.tv_sec;
+
+ if (TRACEACTIONS)
+ trace_change(rt, state, new,
+ label ? label : "Chg ");
+
+ rt->rt_state = state;
+ /*
+ * If the interface state of the new primary route is good,
+ * turn off RS_BADIF flag
+ */
+ if ((rt->rt_state & RS_BADIF) &&
+ IS_IFF_UP(new->rts_ifp->int_if_flags) &&
+ !(new->rts_ifp->int_state & (IS_BROKE | IS_SICK)))
+ rt->rt_state &= ~(RS_BADIF);
+
+ rt->rt_spares[0] = *new;
+}
+
+
+/* check for a better route among the spares */
+static struct rt_spare *
+rts_better(struct rt_entry *rt)
+{
+ struct rt_spare *rts, *rts1;
+ int i;
+
+ /* find the best alternative among the spares */
+ rts = rt->rt_spares+1;
+ for (i = rt->rt_num_spares, rts1 = rts+1; i > 2; i--, rts1++) {
+ if (BETTER_LINK(rt, rts1, rts))
+ rts = rts1;
+ }
+
+ return (rts);
+}
+
+
+/* switch to a backup route */
+void
+rtswitch(struct rt_entry *rt,
+ struct rt_spare *rts)
+{
+ struct rt_spare swap;
+ char label[10];
+
+ /* Do not change permanent routes */
+ if (0 != (rt->rt_state & (RS_MHOME | RS_STATIC |
+ RS_NET_SYN | RS_IF)))
+ return;
+
+ /* find the best alternative among the spares */
+ if (rts == NULL)
+ rts = rts_better(rt);
+
+ /* Do not bother if it is not worthwhile. */
+ if (!BETTER_LINK(rt, rts, rt->rt_spares))
+ return;
+
+ swap = rt->rt_spares[0];
+ (void) snprintf(label, sizeof (label), "Use #%d",
+ (int)(rts - rt->rt_spares));
+ rtchange(rt, rt->rt_state & ~(RS_NET_SYN), rts, label);
+
+ if (swap.rts_metric == HOPCNT_INFINITY) {
+ *rts = rts_empty;
+ } else {
+ *rts = swap;
+ }
+
+}
+
+
+void
+rtdelete(struct rt_entry *rt)
+{
+ struct rt_entry *deleted_rt;
+ struct rt_spare *rts;
+ int i;
+ in_addr_t gate = rt->rt_gate; /* for debugging */
+
+ if (TRACEACTIONS)
+ trace_add_del("Del", rt);
+
+ for (i = 0; i < rt->rt_num_spares; i++) {
+ rts = &rt->rt_spares[i];
+ rts_delete(rt, rts);
+ }
+
+ dst_sock.sin_addr.s_addr = rt->rt_dst;
+ mask_sock.sin_addr.s_addr = htonl(rt->rt_mask);
+ if (rt != (deleted_rt =
+ ((struct rt_entry *)rhead->rnh_deladdr(&dst_sock, &mask_sock,
+ rhead)))) {
+ msglog("rnh_deladdr(%s) failed; found rt 0x%lx",
+ rtname(rt->rt_dst, rt->rt_mask, gate), deleted_rt);
+ if (deleted_rt != NULL)
+ free(deleted_rt);
+ }
+ total_routes--;
+ free(rt);
+
+ if (dst_sock.sin_addr.s_addr == RIP_DEFAULT) {
+ /*
+ * we just deleted the default route. Trigger rdisc_sort
+ * so that we can recover from any rdisc information that
+ * is valid
+ */
+ rdisc_timer.tv_sec = 0;
+ }
+}
+
+void
+rts_delete(struct rt_entry *rt, struct rt_spare *rts)
+{
+ struct khash *k;
+
+ trace_upslot(rt, rts, &rts_empty);
+ k = kern_find(rt->rt_dst, rt->rt_mask,
+ rts->rts_gate, rts->rts_ifp, NULL);
+ if (k != NULL &&
+ !(k->k_state & KS_DEPRE_IF) &&
+ ((k->k_state & (KS_IF|KS_PASSIVE)) != KS_IF)) {
+ k->k_state |= KS_DELETE;
+ need_kern.tv_sec = now.tv_sec;
+ }
+
+ *rts = rts_empty;
+}
+
+/*
+ * Get rid of a bad route, and try to switch to a replacement.
+ * If the route has gone bad because of a bad interface,
+ * the information about the dead interface is available in badifp
+ * for the purpose of sanity checks, if_flags checks etc.
+ */
+static void
+rtbad(struct rt_entry *rt, struct interface *badifp)
+{
+ struct rt_spare new;
+ uint16_t rt_state;
+
+
+ if (badifp == NULL || (rt->rt_spares[0].rts_ifp == badifp)) {
+ /* Poison the route */
+ new = rt->rt_spares[0];
+ new.rts_metric = HOPCNT_INFINITY;
+ rt_state = rt->rt_state & ~(RS_IF | RS_LOCAL | RS_STATIC);
+ }
+
+ if (badifp != NULL) {
+ /*
+ * Dont mark the rtentry bad unless the ifp for the primary
+ * route is the bad ifp
+ */
+ if (rt->rt_spares[0].rts_ifp != badifp)
+ return;
+ /*
+ * badifp has just gone bad. We want to keep this
+ * rt_entry around so that we tell our rip-neighbors
+ * about the bad route, but we can't do anything
+ * to the kernel itself, so mark it as RS_BADIF
+ */
+ trace_misc("rtbad:Setting RS_BADIF (%s)", badifp->int_name);
+ rt_state |= RS_BADIF;
+ new.rts_ifp = &dummy_ifp;
+ }
+ rtchange(rt, rt_state, &new, 0);
+ rtswitch(rt, 0);
+}
+
+
+/*
+ * Junk a RS_NET_SYN or RS_LOCAL route,
+ * unless it is needed by another interface.
+ */
+void
+rtbad_sub(struct rt_entry *rt, struct interface *badifp)
+{
+ struct interface *ifp, *ifp1;
+ struct intnet *intnetp;
+ uint_t state;
+
+
+ ifp1 = NULL;
+ state = 0;
+
+ if (rt->rt_state & RS_LOCAL) {
+ /*
+ * Is this the route through loopback for the interface?
+ * If so, see if it is used by any other interfaces, such
+ * as a point-to-point interface with the same local address.
+ */
+ for (ifp = ifnet; ifp != NULL; ifp = ifp->int_next) {
+ /* Retain it if another interface needs it. */
+ if (ifp->int_addr == rt->rt_ifp->int_addr) {
+ state |= RS_LOCAL;
+ ifp1 = ifp;
+ break;
+ }
+ }
+
+ }
+
+ if (!(state & RS_LOCAL)) {
+ /*
+ * Retain RIPv1 logical network route if there is another
+ * interface that justifies it.
+ */
+ if (rt->rt_state & RS_NET_SYN) {
+ for (ifp = ifnet; ifp != NULL; ifp = ifp->int_next) {
+ if ((ifp->int_state & IS_NEED_NET_SYN) &&
+ rt->rt_mask == ifp->int_std_mask &&
+ rt->rt_dst == ifp->int_std_addr) {
+ state |= RS_NET_SYN;
+ ifp1 = ifp;
+ break;
+ }
+ }
+ }
+
+ /* or if there is an authority route that needs it. */
+ for (intnetp = intnets; intnetp != NULL;
+ intnetp = intnetp->intnet_next) {
+ if (intnetp->intnet_addr == rt->rt_dst &&
+ intnetp->intnet_mask == rt->rt_mask) {
+ state |= (RS_NET_SYN | RS_NET_INT);
+ break;
+ }
+ }
+ }
+
+ if (ifp1 != NULL || (state & RS_NET_SYN)) {
+ struct rt_spare new = rt->rt_spares[0];
+ new.rts_ifp = ifp1;
+ rtchange(rt, ((rt->rt_state & ~(RS_NET_SYN|RS_LOCAL)) | state),
+ &new, 0);
+ } else {
+ rtbad(rt, badifp);
+ }
+}
+
+/*
+ * Called while walking the table looking for sick interfaces
+ * or after a time change.
+ */
+int
+walk_bad(struct radix_node *rn,
+ void *argp)
+{
+#define RT ((struct rt_entry *)rn)
+ struct rt_spare *rts;
+ int i, j = -1;
+
+ /* fix any spare routes through the interface */
+ for (i = 1; i < RT->rt_num_spares; i++) {
+ rts = &((struct rt_entry *)rn)->rt_spares[i];
+
+ if (rts->rts_metric < HOPCNT_INFINITY &&
+ (rts->rts_ifp == NULL ||
+ (rts->rts_ifp->int_state & IS_BROKE)))
+ rts_delete(RT, rts);
+ else {
+ if (rts->rts_origin != RO_NONE)
+ j = i;
+ }
+ }
+
+ /*
+ * Deal with the main route
+ * finished if it has been handled before or if its interface is ok
+ */
+ if (RT->rt_ifp == NULL || !(RT->rt_ifp->int_state & IS_BROKE))
+ return (0);
+
+ /* Bad routes for other than interfaces are easy. */
+ if (!(RT->rt_state & (RS_IF | RS_NET_SYN | RS_LOCAL))) {
+ if (j > 0)
+ rtswitch(RT, NULL);
+ else
+ rtbad(RT, (struct interface *)argp);
+ return (0);
+ }
+
+ rtbad_sub(RT, (struct interface *)argp);
+ return (0);
+#undef RT
+}
+
+/*
+ * Called while walking the table to replace a duplicate interface
+ * with a backup.
+ */
+int
+walk_rewire(struct radix_node *rn, void *argp)
+{
+ struct rt_entry *RT = (struct rt_entry *)rn;
+ struct rewire_data *wire = (struct rewire_data *)argp;
+ struct rt_spare *rts;
+ int i;
+
+ /* fix any spare routes through the interface */
+ rts = RT->rt_spares;
+ for (i = RT->rt_num_spares; i > 0; i--, rts++) {
+ if (rts->rts_ifp == wire->if_old) {
+ rts->rts_ifp = wire->if_new;
+ if ((RT->rt_dst == RIP_DEFAULT) &&
+ (wire->if_old->int_state & IS_SUPPRESS_RDISC))
+ rdisc_suppress(rts->rts_ifp);
+ if ((rts->rts_metric += wire->metric_delta) >
+ HOPCNT_INFINITY)
+ rts->rts_metric = HOPCNT_INFINITY;
+
+ /*
+ * If the main route is getting a worse metric,
+ * then it may be time to switch to a backup.
+ */
+ if (i == RT->rt_num_spares && wire->metric_delta > 0) {
+ rtswitch(RT, NULL);
+ }
+ }
+ }
+
+ return (0);
+}
+
+/* Check the age of an individual route. */
+static int
+walk_age(struct radix_node *rn, void *argp)
+{
+#define RT ((struct rt_entry *)rn)
+ struct interface *ifp;
+ struct rt_spare *rts;
+ int i;
+ in_addr_t age_bad_gate = *(in_addr_t *)argp;
+
+
+ /*
+ * age all of the spare routes, including the primary route
+ * currently in use
+ */
+ rts = RT->rt_spares;
+ for (i = RT->rt_num_spares; i != 0; i--, rts++) {
+
+ ifp = rts->rts_ifp;
+ if (i == RT->rt_num_spares) {
+ if (!AGE_RT(RT->rt_state, rts->rts_origin, ifp)) {
+ /*
+ * Keep various things from deciding ageless
+ * routes are stale
+ */
+ rts->rts_time = now.tv_sec;
+ continue;
+ }
+
+ /* forget RIP routes after RIP has been turned off. */
+ if (rip_sock < 0) {
+ rts->rts_time = now_stale + 1;
+ }
+ }
+
+ /* age failing routes */
+ if (age_bad_gate == rts->rts_gate &&
+ rts->rts_time >= now_stale) {
+ rts->rts_time -= SUPPLY_INTERVAL;
+ }
+
+ /* trash the spare routes when they go bad */
+ if (rts->rts_origin == RO_RIP &&
+ ((rip_sock < 0) ||
+ (rts->rts_metric < HOPCNT_INFINITY &&
+ now_garbage > rts->rts_time)) &&
+ i != RT->rt_num_spares) {
+ rts_delete(RT, rts);
+ }
+ }
+
+
+ /* finished if the active route is still fresh */
+ if (now_stale <= RT->rt_time)
+ return (0);
+
+ /* try to switch to an alternative */
+ rtswitch(RT, NULL);
+
+ /* Delete a dead route after it has been publically mourned. */
+ if (now_garbage > RT->rt_time) {
+ rtdelete(RT);
+ return (0);
+ }
+
+ /* Start poisoning a bad route before deleting it. */
+ if (now.tv_sec - RT->rt_time > EXPIRE_TIME) {
+ struct rt_spare new = RT->rt_spares[0];
+
+ new.rts_metric = HOPCNT_INFINITY;
+ rtchange(RT, RT->rt_state, &new, 0);
+ }
+ return (0);
+}
+
+
+/* Watch for dead routes and interfaces. */
+void
+age(in_addr_t bad_gate)
+{
+ struct interface *ifp;
+ int need_query = 0;
+
+ /*
+ * If not listening to RIP, there is no need to age the routes in
+ * the table.
+ */
+ age_timer.tv_sec = (now.tv_sec
+ + ((rip_sock < 0) ? NEVER : SUPPLY_INTERVAL));
+
+ /*
+ * Check for dead IS_REMOTE interfaces by timing their
+ * transmissions.
+ */
+ for (ifp = ifnet; ifp; ifp = ifp->int_next) {
+ if (!(ifp->int_state & IS_REMOTE))
+ continue;
+
+ /* ignore unreachable remote interfaces */
+ if (!check_remote(ifp))
+ continue;
+
+ /* Restore remote interface that has become reachable */
+ if (ifp->int_state & IS_BROKE)
+ if_ok(ifp, "remote ", _B_FALSE);
+
+ if (ifp->int_act_time != NEVER &&
+ now.tv_sec - ifp->int_act_time > EXPIRE_TIME) {
+ writelog(LOG_NOTICE,
+ "remote interface %s to %s timed out after"
+ " %ld:%ld",
+ ifp->int_name,
+ naddr_ntoa(ifp->int_dstaddr),
+ (now.tv_sec - ifp->int_act_time)/60,
+ (now.tv_sec - ifp->int_act_time)%60);
+ if_sick(ifp, _B_FALSE);
+ }
+
+ /*
+ * If we have not heard from the other router
+ * recently, ask it.
+ */
+ if (now.tv_sec >= ifp->int_query_time) {
+ ifp->int_query_time = NEVER;
+ need_query = 1;
+ }
+ }
+
+ /* Age routes. */
+ (void) rn_walktree(rhead, walk_age, &bad_gate);
+
+ /*
+ * delete old redirected routes to keep the kernel table small
+ * and prevent blackholes
+ */
+ del_redirects(bad_gate, now.tv_sec-STALE_TIME);
+
+ /* Update the kernel routing table. */
+ fix_kern();
+
+ /* poke reticent remote gateways */
+ if (need_query)
+ rip_query();
+}
+
+void
+kern_dump(void)
+{
+ int i;
+ struct khash *k;
+
+ for (i = 0; i < KHASH_SIZE; i++) {
+ for (k = khash_bins[i]; k != NULL; k = k->k_next)
+ trace_khash(k);
+ }
+}
+
+
+static struct interface *
+gwkludge_iflookup(in_addr_t dstaddr, in_addr_t addr, in_addr_t mask)
+{
+ uint32_t int_state;
+ struct interface *ifp;
+
+ for (ifp = ifnet; ifp != NULL; ifp = ifp->int_next) {
+ int_state = ifp->int_state;
+
+ if (!(int_state & IS_REMOTE))
+ continue;
+
+ if (ifp->int_dstaddr == dstaddr && ifp->int_addr == addr &&
+ ifp->int_mask == mask)
+ return (ifp);
+ }
+ return (NULL);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.routed/trace.c b/usr/src/cmd/cmd-inet/usr.sbin/in.routed/trace.c
new file mode 100644
index 0000000000..1572decfa7
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.routed/trace.c
@@ -0,0 +1,1252 @@
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 1983, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgment:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sbin/routed/trace.c,v 1.6 2000/08/11 08:24:38 sheldonh Exp $
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "defs.h"
+#include "pathnames.h"
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/signal.h>
+#include <strings.h>
+#include <fcntl.h>
+#include <protocols/routed.h>
+
+#define NRECORDS 50 /* size of circular trace buffer */
+
+int tracelevel, new_tracelevel;
+FILE *ftrace = stdout; /* output trace file */
+static const char *sigtrace_pat = "%s";
+static char savetracename[MAXPATHLEN+1];
+static char *ripcmds[RIPCMD_MAX] =
+ {"#0", "REQUEST", "RESPONSE", "TRACEON", "TRACEOFF", "POLL",
+ "POLLENTRY"};
+char inittracename[MAXPATHLEN+1];
+static boolean_t file_trace; /* 1=tracing to file, not stdout */
+
+static void tmsg(const char *, ...);
+
+const char *
+rip_strerror(int err)
+{
+ const char *cp = strerror(err);
+ static char msgbuf[64];
+
+ if (cp == NULL) {
+ if (err == 0) {
+ cp = "success";
+ } else {
+ (void) snprintf(msgbuf, sizeof (msgbuf),
+ "unknown error %d", err);
+ cp = msgbuf;
+ }
+ }
+ return (cp);
+}
+
+/* convert IP address to a string, but not into a single buffer */
+char *
+naddr_ntoa(in_addr_t a)
+{
+#define NUM_BUFS 4
+ static int bufno;
+ static struct {
+ char str[INET_ADDRSTRLEN]; /* xxx.xxx.xxx.xxx\0 */
+ } bufs[NUM_BUFS];
+ char *s;
+ struct in_addr addr;
+
+ addr.s_addr = a;
+ s = strcpy(bufs[bufno].str, inet_ntoa(addr));
+ bufno = (bufno+1) % NUM_BUFS;
+ return (s);
+#undef NUM_BUFS
+}
+
+
+const char *
+saddr_ntoa(struct sockaddr_storage *ss)
+{
+ return (ss == NULL) ? "?" : naddr_ntoa(S_ADDR(ss));
+}
+
+
+static char *
+ts(time_t secs)
+{
+ static char s[20];
+
+ secs += epoch.tv_sec;
+ (void) strftime(s, sizeof (s), "%T", localtime(&secs));
+ return (s);
+}
+
+static char *
+ts_full(struct timeval *tv)
+{
+ static char s[32];
+ time_t secs;
+ int len;
+
+ secs = tv->tv_sec + epoch.tv_sec;
+ (void) strftime(s, sizeof (s), "%Y/%m/%d %T", localtime(&secs));
+ len = strlen(s);
+ (void) snprintf(s + len, sizeof (s) - len, ".%06ld", tv->tv_usec);
+ return (s);
+}
+
+/*
+ * On each event, display a time stamp.
+ * This assumes that 'now' is update once for each event, and
+ * that at least now.tv_usec changes.
+ */
+static struct timeval lastlog_time;
+
+void
+lastlog(void)
+{
+ if (lastlog_time.tv_sec != now.tv_sec ||
+ lastlog_time.tv_usec != now.tv_usec) {
+ (void) fprintf(ftrace, "-- %s --\n", ts_full(&now));
+ lastlog_time = now;
+ }
+}
+
+
+static void
+tmsg(const char *p, ...)
+{
+ va_list args;
+
+ if (ftrace != NULL) {
+ lastlog();
+ va_start(args, p);
+ (void) vfprintf(ftrace, p, args);
+ (void) fputc('\n', ftrace);
+ (void) fflush(ftrace);
+ (void) va_end(args);
+ }
+}
+
+
+void
+trace_close(int zap_stdio)
+{
+ int fd;
+
+
+ (void) fflush(stdout);
+ (void) fflush(stderr);
+
+ if (ftrace != NULL && zap_stdio) {
+ if (ftrace != stdout)
+ (void) fclose(ftrace);
+ ftrace = NULL;
+ fd = open("/dev/null", O_RDWR);
+ if (isatty(STDIN_FILENO))
+ (void) dup2(fd, STDIN_FILENO);
+ if (isatty(STDOUT_FILENO))
+ (void) dup2(fd, STDOUT_FILENO);
+ if (isatty(STDERR_FILENO))
+ (void) dup2(fd, STDERR_FILENO);
+ (void) close(fd);
+ }
+ lastlog_time.tv_sec = 0;
+}
+
+
+void
+trace_flush(void)
+{
+ if (ftrace != NULL) {
+ (void) fflush(ftrace);
+ if (ferror(ftrace))
+ trace_off("tracing off: %s",
+ rip_strerror(ferror(ftrace)));
+ }
+}
+
+
+void
+trace_off(const char *p, ...)
+{
+ va_list args;
+
+
+ if (ftrace != NULL) {
+ lastlog();
+ va_start(args, p);
+ (void) vfprintf(ftrace, p, args);
+ (void) fputc('\n', ftrace);
+ (void) va_end(args);
+ }
+ trace_close(file_trace);
+
+ new_tracelevel = tracelevel = 0;
+}
+
+
+/* log a change in tracing */
+void
+tracelevel_msg(const char *pat,
+ int dump) /* -1=no dump, 0=default, 1=force */
+{
+ static const char *off_msgs[MAX_TRACELEVEL] = {
+ "Tracing actions stopped",
+ "Tracing packets stopped",
+ "Tracing packet contents stopped",
+ "Tracing kernel changes stopped",
+ "Tracing routing socket messages stopped",
+ };
+ static const char *on_msgs[MAX_TRACELEVEL] = {
+ "Tracing actions started",
+ "Tracing packets started",
+ "Tracing packet contents started",
+ "Tracing kernel changes started",
+ "Tracing routing socket messages started",
+ };
+ uint_t old_tracelevel = tracelevel;
+
+
+ if (new_tracelevel < 0)
+ new_tracelevel = 0;
+ else if (new_tracelevel > MAX_TRACELEVEL)
+ new_tracelevel = MAX_TRACELEVEL;
+
+ if (new_tracelevel < tracelevel) {
+ if (new_tracelevel <= 0) {
+ trace_off(pat, off_msgs[0]);
+ } else {
+ do {
+ tmsg(pat, off_msgs[tracelevel]);
+ } while (--tracelevel != new_tracelevel);
+ }
+
+ } else if (new_tracelevel > tracelevel) {
+ do {
+ tmsg(pat, on_msgs[tracelevel++]);
+ } while (tracelevel != new_tracelevel);
+ }
+
+ if (dump > 0 ||
+ (dump == 0 && old_tracelevel == 0 && tracelevel != 0))
+ trace_dump();
+}
+
+void
+set_tracefile(const char *filename,
+ const char *pat,
+ int dump) /* -1=no dump, 0=default, 1=force */
+{
+ struct stat stbuf;
+ struct stat stbuf2;
+ FILE *n_ftrace;
+ const char *fn;
+ int nfd;
+ boolean_t allow_create;
+
+ /*
+ * main() calls this routine with "dump == -1". All others
+ * call it with 0, so we take dump == -1 to mean "can create
+ * the file."
+ */
+ allow_create = (dump == -1);
+
+ /*
+ * Allow a null filename to increase the level if the trace file
+ * is already open or if coming from a trusted source, such as
+ * a signal or the command line.
+ */
+ if (filename == NULL || filename[0] == '\0') {
+ filename = NULL;
+ if (ftrace == NULL) {
+ if (inittracename[0] == '\0') {
+ msglog("missing trace file name");
+ return;
+ }
+ fn = inittracename;
+ } else {
+ goto set_tracelevel;
+ }
+
+ } else if (strcmp(filename, "dump/../table") == 0) {
+ trace_dump();
+ return;
+
+ } else {
+ /*
+ * Allow the file specified with "-T file" to be reopened,
+ * but require all other names specified over the net to
+ * match the official path. The path can specify a directory
+ * in which the file is to be created.
+ */
+
+ if (strcmp(filename, inittracename) != 0) {
+ if (strncmp(filename, PATH_TRACE,
+ sizeof (PATH_TRACE)-1) != 0 ||
+ (strstr(filename, "../") != NULL)) {
+ msglog("wrong trace file \"%s\"", filename);
+ return;
+ }
+ if (stat(PATH_TRACE, &stbuf) == -1) {
+ fn = PATH_TRACE;
+ goto missing_file;
+ }
+ if (filename[sizeof (PATH_TRACE) - 1] != '\0' &&
+ (filename[sizeof (PATH_TRACE) - 1] != '/' ||
+ !S_ISDIR(stbuf.st_mode))) {
+ goto bad_file_type;
+ }
+ if (S_ISDIR(stbuf.st_mode))
+ allow_create = _B_TRUE;
+ }
+
+ fn = filename;
+ }
+ /* fn cannot be null here */
+
+ /* If the new tracefile exists, it must be a regular file. */
+ if (lstat(fn, &stbuf) == -1) {
+ if (!allow_create)
+ goto missing_file;
+ nfd = open(fn, O_CREAT|O_EXCL|O_WRONLY, 0644);
+ if (nfd != -1 && fstat(nfd, &stbuf) == -1) {
+ (void) close(nfd);
+ goto missing_file;
+ }
+ } else if (S_ISREG(stbuf.st_mode)) {
+ nfd = open(fn, O_APPEND|O_WRONLY, 0644);
+ } else {
+ goto bad_file_type;
+ }
+
+ if (nfd == -1 || (n_ftrace = fdopen(nfd, "a")) == NULL) {
+ msglog("failed to open trace file \"%s\" %s", fn,
+ rip_strerror(errno));
+ if (fn == inittracename)
+ inittracename[0] = '\0';
+ if (nfd != -1)
+ (void) close(nfd);
+ return;
+ }
+
+ if (fstat(nfd, &stbuf2) == -1 || !S_ISREG(stbuf2.st_mode) ||
+ stbuf2.st_dev != stbuf.st_dev || stbuf2.st_ino != stbuf.st_ino) {
+ msglog("trace file \"%s\" moved", fn);
+ (void) fclose(n_ftrace);
+ return;
+ }
+
+ tmsg("switch to trace file %s", fn);
+ trace_close(file_trace = _B_TRUE);
+ (void) dup2(nfd, STDOUT_FILENO);
+ (void) dup2(nfd, STDERR_FILENO);
+
+ if (fn != savetracename)
+ (void) strlcpy(savetracename, fn, sizeof (savetracename) - 1);
+ ftrace = n_ftrace;
+
+set_tracelevel:
+ if (new_tracelevel == 0 || filename == NULL)
+ new_tracelevel++;
+ tracelevel_msg(pat, dump != 0 ? dump : (filename != NULL));
+ return;
+
+missing_file:
+ msglog("trace \"%s\" missing", fn);
+ return;
+
+bad_file_type:
+ msglog("wrong type (%#x) of trace file \"%s\"", stbuf.st_mode, fn);
+}
+
+
+/* ARGSUSED */
+void
+sigtrace_more(int s)
+{
+ new_tracelevel++;
+ sigtrace_pat = "SIGUSR1: %s";
+ if (signal(s, sigtrace_more) == SIG_ERR)
+ msglog("signal: %s", rip_strerror(errno));
+}
+
+
+/* ARGSUSED */
+void
+sigtrace_less(int s)
+{
+ new_tracelevel--;
+ sigtrace_pat = "SIGUSR2: %s";
+ if (signal(s, sigtrace_less) == SIG_ERR)
+ msglog("signal: %s", rip_strerror(errno));
+}
+
+/* ARGSUSED */
+void
+sigtrace_dump(int s)
+{
+ trace_dump();
+ if (signal(s, sigtrace_dump) == SIG_ERR)
+ msglog("signal: %s", rip_strerror(errno));
+}
+
+/* Set tracing after a signal. */
+void
+set_tracelevel(void)
+{
+ if (new_tracelevel == tracelevel)
+ return;
+
+ /*
+ * If tracing entirely off, and there was no tracefile specified
+ * on the command line, then leave it off.
+ */
+ if (new_tracelevel > tracelevel && ftrace == NULL) {
+ if (savetracename[0] != '\0') {
+ set_tracefile(savetracename, sigtrace_pat, 0);
+ } else if (inittracename[0] != '\0') {
+ set_tracefile(inittracename, sigtrace_pat, 0);
+ } else {
+ new_tracelevel = 0;
+ return;
+ }
+ } else {
+ tracelevel_msg(sigtrace_pat, 0);
+ }
+}
+
+
+/* display an address */
+char *
+addrname(in_addr_t addr, /* in network byte order */
+ in_addr_t mask,
+ int force) /* 0=show mask if nonstandard, */
+{ /* 1=always show mask, 2=never */
+#define NUM_BUFS 4
+ static int bufno;
+ static struct {
+ /*
+ * this array can hold either of the following strings terminated
+ * by a null character:
+ * "xxx.xxx.xxx.xxx/xx"
+ * "xxx.xxx.xxx.xxx (mask xxx.xxx.xxx.xxx)"
+ *
+ */
+ char str[2*INET_ADDRSTRLEN + sizeof (" (mask )")];
+ } bufs[NUM_BUFS];
+ char *s, *sp;
+ in_addr_t dmask;
+ int i, len;
+ struct in_addr tmp_addr;
+
+ tmp_addr.s_addr = addr;
+ len = strlcpy(bufs[bufno].str, inet_ntoa(tmp_addr),
+ sizeof (bufs[bufno].str));
+ s = bufs[bufno].str;
+ bufno = (bufno+1) % NUM_BUFS;
+
+ if (force == 1 || (force == 0 && mask != std_mask(addr))) {
+ sp = &s[strlen(s)];
+
+ dmask = mask & -mask;
+ if (mask + dmask == 0) {
+ i = ffs(mask);
+ (void) snprintf(sp,
+ (sizeof (bufs[bufno].str) - len), "/%d",
+ (NBBY * sizeof (in_addr_t) + 1) - i);
+
+ } else {
+ (void) snprintf(sp,
+ (sizeof (bufs[bufno].str) - len), " (mask %s)",
+ naddr_ntoa(htonl(mask)));
+ }
+ }
+
+ return (s);
+#undef NUM_BUFS
+}
+
+
+/* display a bit-field */
+struct or_bits {
+ uint8_t origin;
+ const char *origin_name;
+};
+
+static struct or_bits origin_bits[] = {
+ { RO_RIP, "RIP" },
+ { RO_RDISC, "RDISC" },
+ { RO_STATIC, "STATIC" },
+ { RO_LOOPBCK, "LOOPBCK" },
+ { RO_PTOPT, "PTOPT" },
+ { RO_NET_SYN, "NET_SYN" },
+ { RO_IF, "IF" },
+ { RO_FILE, "FILE" },
+ { RO_NONE, " " },
+ { 0, NULL}
+};
+
+/* display a bit-field */
+struct bits {
+ uint_t bits_mask;
+ uint_t bits_clear;
+ const char *bits_name;
+};
+
+static struct bits if_bits[] = {
+ { IFF_BROADCAST, 0, "BROADCAST" },
+ { IFF_DEBUG, 0, "DEBUG" },
+ { IFF_LOOPBACK, 0, "LOOPBACK" },
+ { IFF_POINTOPOINT, 0, "POINTOPOINT" },
+ { IFF_NOTRAILERS, 0, "NOTRAILERS" },
+ { IFF_RUNNING, 0, "RUNNING" },
+ { IFF_NOARP, 0, "NOARP" },
+ { IFF_PROMISC, 0, "PROMISC" },
+ { IFF_ALLMULTI, 0, "ALLMULTI" },
+ { IFF_INTELLIGENT, 0, "INTELLIGENT" },
+ { IFF_MULTICAST, 0, "MULTICAST" },
+ { IFF_MULTI_BCAST, 0, "MULTI_BCAST" },
+ { IFF_UNNUMBERED, 0, "UNNUMBERED" },
+ { IFF_DHCPRUNNING, 0, "DHCP" },
+ { IFF_PRIVATE, 0, "PRIVATE" },
+ { IFF_NOXMIT, 0, "NOXMIT" },
+ { IFF_NOLOCAL, 0, "NOLOCAL" },
+ { IFF_DEPRECATED, 0, "DEPRECATED" },
+ { IFF_ADDRCONF, 0, "ADDRCONF" },
+ { IFF_ROUTER, 0, "ROUTER" },
+ { IFF_NONUD, 0, "NONUD" },
+ { IFF_ANYCAST, 0, "ANYCAST" },
+ { IFF_NORTEXCH, 0, "NORTEXCH" },
+ { IFF_IPV4, 0, "IPv4" },
+ { IFF_IPV6, 0, "IPv6" },
+ { IFF_MIPRUNNING, 0, "MIP" },
+ { IFF_NOFAILOVER, 0, "NOFAILOVER" },
+ { IFF_FAILED, 0, "FAILED" },
+ { IFF_STANDBY, 0, "STANDBY" },
+ { IFF_INACTIVE, 0, "INACTIVE" },
+ { IFF_OFFLINE, 0, "OFFLINE" },
+ { IFF_XRESOLV, 0, "XRESOLV" },
+ { IFF_COS_ENABLED, 0, "CoS" },
+ { IFF_PREFERRED, 0, "PREFERRED" },
+ { IFF_TEMPORARY, 0, "TEMPORARY" },
+ { IFF_FIXEDMTU, 0, "FIXEDMTU" },
+ { IFF_VIRTUAL, 0, "VIRTUAL"},
+ { 0, 0, NULL}
+};
+
+static struct bits is_bits[] = {
+ { IS_ALIAS, 0, "ALIAS" },
+ { IS_SUBNET, 0, "" },
+ { IS_REMOTE, (IS_NO_RDISC |
+ IS_BCAST_RDISC), "REMOTE" },
+ { IS_PASSIVE, (IS_NO_RDISC |
+ IS_NO_RIP |
+ IS_NO_SUPER_AG |
+ IS_PM_RDISC |
+ IS_NO_AG), "PASSIVE" },
+ { IS_EXTERNAL, 0, "EXTERNAL" },
+ { IS_CHECKED, 0, "" },
+ { IS_ALL_HOSTS, 0, "" },
+ { IS_ALL_ROUTERS, 0, "" },
+ { IS_DISTRUST, 0, "DISTRUST" },
+ { IS_BROKE, IS_SICK, "BROKEN" },
+ { IS_SICK, 0, "SICK" },
+ { IS_DUP, 0, "DUPLICATE" },
+ { IS_REDIRECT_OK, 0, "REDIRECT_OK" },
+ { IS_NEED_NET_SYN, 0, "" },
+ { IS_NO_AG, IS_NO_SUPER_AG, "NO_AG" },
+ { IS_NO_SUPER_AG, 0, "NO_SUPER_AG" },
+ { (IS_NO_RIPV1_IN |
+ IS_NO_RIPV2_IN |
+ IS_NO_RIPV1_OUT |
+ IS_NO_RIPV2_OUT), 0, "NO_RIP" },
+ { (IS_NO_RIPV1_IN |
+ IS_NO_RIPV1_OUT), 0, "RIPV2" },
+ { IS_NO_RIPV1_IN, 0, "NO_RIPV1_IN" },
+ { IS_NO_RIPV2_IN, 0, "NO_RIPV2_IN" },
+ { IS_NO_RIPV1_OUT, 0, "NO_RIPV1_OUT" },
+ { IS_NO_RIPV2_OUT, 0, "NO_RIPV2_OUT" },
+ { IS_NO_RIP_MCAST, 0, "NO_RIP_MCAST" },
+ { (IS_NO_ADV_IN |
+ IS_NO_SOL_OUT |
+ IS_NO_ADV_OUT), IS_BCAST_RDISC, "NO_RDISC" },
+ { IS_NO_SOL_OUT, 0, "NO_SOLICIT" },
+ { IS_SOL_OUT, 0, "SEND_SOLICIT" },
+ { IS_NO_ADV_OUT, IS_BCAST_RDISC, "NO_RDISC_ADV" },
+ { IS_ADV_OUT, 0, "RDISC_ADV" },
+ { IS_BCAST_RDISC, 0, "BCAST_RDISC" },
+ { IS_PM_RDISC, 0, "" },
+ { IS_NO_HOST, 0, "NO_HOST" },
+ { IS_SUPPRESS_RDISC, 0, "SUPPRESS_RDISC" },
+ { IS_FLUSH_RDISC, 0, "FLUSH_RDISC" },
+ { 0, 0, NULL}
+};
+
+static struct bits rs_bits[] = {
+ { RS_IF, 0, "IF" },
+ { RS_NET_INT, RS_NET_SYN, "NET_INT" },
+ { RS_NET_SYN, 0, "NET_SYN" },
+ { RS_SUBNET, 0, "" },
+ { RS_LOCAL, 0, "LOCAL" },
+ { RS_MHOME, 0, "MHOME" },
+ { RS_STATIC, 0, "STATIC" },
+ { RS_NOPROPAGATE, 0, "NOPROP" },
+ { RS_BADIF, 0, "BADIF" },
+ { 0, 0, NULL}
+};
+
+static struct bits ks_bits[] = {
+ { KS_NEW, 0, "NEW" },
+ { KS_DELETE, 0, "DELETE" },
+ { KS_ADD, 0, "ADD" },
+ { KS_CHANGE, 0, "CHANGE" },
+ { KS_DEL_ADD, 0, "DEL_ADD" },
+ { KS_STATIC, 0, "STATIC" },
+ { KS_GATEWAY, 0, "GATEWAY" },
+ { KS_DYNAMIC, 0, "DYNAMIC" },
+ { KS_DELETED, 0, "DELETED" },
+ { KS_PRIVATE, 0, "PRIVATE" },
+ { KS_CHECK, 0, "CHECK" },
+ { KS_IF, 0, "IF" },
+ { KS_PASSIVE, 0, "PASSIVE" },
+ { KS_DEPRE_IF, 0, "DEPRE_IF" },
+ { KS_FILE, 0, "FILE" },
+ { 0, 0, NULL}
+};
+
+static void
+trace_bits(const struct bits *tbl,
+ uint_t field,
+ boolean_t force)
+{
+ uint_t b;
+ char c;
+
+ if (force) {
+ (void) putc('<', ftrace);
+ c = '\0';
+ } else {
+ c = '<';
+ }
+
+ while (field != 0 &&
+ (b = tbl->bits_mask) != 0) {
+ if ((b & field) == b) {
+ if (tbl->bits_name[0] != '\0') {
+ if (c != '\0')
+ (void) putc(c, ftrace);
+ (void) fprintf(ftrace, "%s", tbl->bits_name);
+ c = '|';
+ }
+ field &= ~(b | tbl->bits_clear);
+ }
+ tbl++;
+ }
+ if (field != 0) {
+ if (c != '\0')
+ (void) putc(c, ftrace);
+ (void) fprintf(ftrace, "%#x", field);
+ c = '|';
+ }
+
+ if (c != '<' || force)
+ (void) fputs("> ", ftrace);
+}
+
+static char *
+trace_string(const struct bits *tbl, uint_t field, boolean_t force)
+{
+ const struct bits *tbp;
+ char *sbuf, *cp, chr;
+ size_t slen;
+
+ /* minimum default string */
+ slen = sizeof ("<0x12345678>");
+ for (tbp = tbl; tbp->bits_mask != 0; tbp++)
+ if (tbp->bits_name[0] != '\0')
+ slen += strlen(tbp->bits_name) + 1;
+ if ((sbuf = malloc(slen)) == NULL)
+ return (NULL);
+ cp = sbuf;
+
+ if (force) {
+ *cp++ = '<';
+ chr = '\0';
+ } else {
+ chr = '<';
+ }
+
+ while (field != 0 && tbl->bits_mask != 0) {
+ if ((tbl->bits_mask & field) == tbl->bits_mask) {
+ if (tbl->bits_name[0] != '\0') {
+ if (chr != '\0')
+ *cp++ = chr;
+ (void) strcpy(cp, tbl->bits_name);
+ cp += strlen(tbl->bits_name);
+ chr = '|';
+ }
+ field &= ~(tbl->bits_mask | tbl->bits_clear);
+ }
+ tbl++;
+ }
+ if (field != 0) {
+ if (chr != '\0')
+ *cp++ = chr;
+ cp += sprintf(cp, "%#x", field);
+ chr = '|';
+ }
+
+ if (chr != '<' || force)
+ *cp++ = '>';
+ *cp = '\0';
+ return (sbuf);
+}
+
+char *
+if_bit_string(uint_t field, boolean_t force)
+{
+ return (trace_string(if_bits, field, force));
+}
+
+char *
+rtname(in_addr_t dst,
+ in_addr_t mask,
+ in_addr_t gate)
+{
+ static char buf[sizeof ("xxx.xxx.xxx.xxx/xx-->xxx.xxx.xxx.xxx")];
+ int i;
+
+ (void) snprintf(buf, sizeof (buf), "%-16s-->", addrname(dst, mask, 0));
+ i = strlen(buf);
+ (void) snprintf(&buf[i], (sizeof (buf) -i), "%-*s", 15+24-MAX(24, i),
+ naddr_ntoa(gate));
+ return (buf);
+}
+
+
+static void
+print_rts(struct rt_spare *rts,
+ int force_metric, /* -1=suppress, 0=default */
+ int force_ifp, /* -1=suppress, 0=default */
+ int force_router, /* -1=suppress, 0=default, 1=display */
+ int force_tag, /* -1=suppress, 0=default, 1=display */
+ int force_time) /* 0=suppress, 1=display */
+{
+ int i;
+
+ if (force_metric >= 0)
+ (void) fprintf(ftrace, "metric=%-2d ", rts->rts_metric);
+ if (force_ifp >= 0)
+ (void) fprintf(ftrace, "%s ", (rts->rts_ifp == 0 ?
+ "if?" : rts->rts_ifp->int_name));
+ if (force_router > 0 ||
+ (force_router == 0 && rts->rts_router != rts->rts_gate))
+ (void) fprintf(ftrace, "router=%s ",
+ naddr_ntoa(rts->rts_router));
+ if (force_time > 0)
+ (void) fprintf(ftrace, "%s ", ts(rts->rts_time));
+ if (force_tag > 0 ||
+ (force_tag == 0 && rts->rts_tag != 0))
+ (void) fprintf(ftrace, "tag=%#x ", ntohs(rts->rts_tag));
+ if (rts->rts_de_ag != 0) {
+ for (i = 1; (uint_t)(1 << i) <= rts->rts_de_ag; i++)
+ continue;
+ (void) fprintf(ftrace, "de_ag=%d ", i);
+ }
+ (void) fprintf(ftrace, "flags 0x%x ", rts->rts_flags);
+
+}
+
+
+static void
+print_rtsorigin(const struct or_bits *tbl, uint8_t route_origin)
+{
+
+ uint8_t tblentry;
+ while ((tblentry = tbl->origin) != 0) {
+ if (tblentry == route_origin) {
+ (void) fprintf(ftrace, "origin=%s ", tbl->origin_name);
+ }
+ tbl++;
+ }
+}
+
+
+void
+trace_if(const char *act, struct interface *ifp)
+{
+ if (!TRACEACTIONS || ftrace == NULL)
+ return;
+
+ lastlog();
+ (void) fprintf(ftrace, "%-3s interface %-4s #%-3d ", act,
+ ifp->int_name,
+ ifp->int_phys != NULL ? ifp->int_phys->phyi_index : 0);
+ (void) fprintf(ftrace, "%-15s-->%-15s",
+ naddr_ntoa(ifp->int_addr),
+ addrname(((ifp->int_if_flags & IFF_POINTOPOINT) ?
+ ifp->int_dstaddr : htonl(ifp->int_net)),
+ ifp->int_mask, 1));
+ if (ifp->int_metric != 0)
+ (void) fprintf(ftrace, " metric=%d", ifp->int_metric);
+ if (!IS_RIP_OUT_OFF(ifp->int_state) &&
+ ifp->int_d_metric != 0)
+ (void) fprintf(ftrace, " fake_default=%d", ifp->int_d_metric);
+ (void) fputs("\n ", ftrace);
+ trace_bits(if_bits, ifp->int_if_flags, _B_FALSE);
+ trace_bits(is_bits, ifp->int_state, _B_FALSE);
+ (void) fputc('\n', ftrace);
+}
+
+void
+trace_khash(const struct khash *krt)
+{
+ if (ftrace == NULL)
+ return;
+
+ lastlog();
+ (void) fprintf(ftrace, " %-15s-->%-15s metric=%d ",
+ addrname(krt->k_dst, krt->k_mask, 0),
+ naddr_ntoa(krt->k_gate), krt->k_metric);
+ if (krt->k_ifp != NULL)
+ (void) fprintf(ftrace, "ifp %s ", krt->k_ifp->int_name);
+ else
+ (void) fprintf(ftrace, "ifp NULL ");
+ (void) fprintf(ftrace, "%s ", ts(krt->k_keep));
+ (void) fprintf(ftrace, "%s ", ts(krt->k_redirect_time));
+ trace_bits(ks_bits, krt->k_state, _B_TRUE);
+ (void) fputc('\n', ftrace);
+}
+
+void
+trace_dr(const struct dr *drp)
+{
+ if (ftrace == NULL)
+ return;
+
+ lastlog();
+ (void) fprintf(ftrace, " %-4s %-15s %s ",
+ drp->dr_ifp != NULL ? drp->dr_ifp->int_name : "?",
+ naddr_ntoa(drp->dr_gate), ts(drp->dr_ts));
+ (void) fprintf(ftrace, "%s %d %u\n", ts(drp->dr_life),
+ SIGN_PREF(drp->dr_recv_pref), drp->dr_pref);
+}
+
+void
+trace_upslot(struct rt_entry *rt,
+ struct rt_spare *rts,
+ struct rt_spare *new)
+{
+ if (!TRACEACTIONS || ftrace == NULL)
+ return;
+
+ if (rts->rts_gate == new->rts_gate &&
+ rts->rts_router == new->rts_router &&
+ rts->rts_metric == new->rts_metric &&
+ rts->rts_tag == new->rts_tag &&
+ rts->rts_de_ag == new->rts_de_ag)
+ return;
+
+ lastlog();
+ if (new->rts_gate == 0) {
+ (void) fprintf(ftrace, "Del #%d %-35s ",
+ (int)(rts - rt->rt_spares),
+ rtname(rt->rt_dst, rt->rt_mask, rts->rts_gate));
+ print_rts(rts, 0, 0, 0, 0,
+ (rts != rt->rt_spares ||
+ AGE_RT(rt->rt_state, rts->rts_origin, new->rts_ifp)));
+
+ } else if (rts->rts_gate != RIP_DEFAULT) {
+ (void) fprintf(ftrace, "Chg #%d %-35s ",
+ (int)(rts - rt->rt_spares),
+ rtname(rt->rt_dst, rt->rt_mask, rts->rts_gate));
+ print_rts(rts, 0, 0,
+ rts->rts_gate != new->rts_gate,
+ rts->rts_tag != new->rts_tag,
+ rts != rt->rt_spares || AGE_RT(rt->rt_state,
+ rts->rts_origin, rt->rt_ifp));
+
+ (void) fprintf(ftrace, "\n %19s%-16s ", "",
+ (new->rts_gate != rts->rts_gate ?
+ naddr_ntoa(new->rts_gate) : ""));
+ print_rts(new,
+ ((new->rts_metric == rts->rts_metric) ? -1 : 0),
+ ((new->rts_ifp == rts->rts_ifp) ? -1 : 0),
+ 0,
+ rts->rts_tag != new->rts_tag,
+ (new->rts_time != rts->rts_time &&
+ (rts != rt->rt_spares ||
+ AGE_RT(rt->rt_state, new->rts_origin, new->rts_ifp))));
+
+ } else {
+ (void) fprintf(ftrace, "Add #%d %-35s ",
+ (int)(rts - rt->rt_spares),
+ rtname(rt->rt_dst, rt->rt_mask, new->rts_gate));
+ print_rts(new, 0, 0, 0, 0,
+ (rts != rt->rt_spares ||
+ AGE_RT(rt->rt_state, new->rts_origin, new->rts_ifp)));
+ }
+ (void) fputc('\n', ftrace);
+}
+
+
+/* miscellaneous message checked by the caller */
+void
+trace_misc(const char *p, ...)
+{
+ va_list args;
+
+ if (ftrace == NULL)
+ return;
+
+ lastlog();
+ va_start(args, p);
+ (void) vfprintf(ftrace, p, args);
+ (void) fputc('\n', ftrace);
+ (void) va_end(args);
+}
+
+
+/* display a message if tracing actions */
+void
+trace_act(const char *p, ...)
+{
+ va_list args;
+
+ if (!TRACEACTIONS || ftrace == NULL)
+ return;
+
+ lastlog();
+ va_start(args, p);
+ (void) vfprintf(ftrace, p, args);
+ (void) fputc('\n', ftrace);
+ (void) va_end(args);
+}
+
+
+/* display a message if tracing packets */
+void
+trace_pkt(const char *p, ...)
+{
+ va_list args;
+
+ if (!TRACEPACKETS || ftrace == NULL)
+ return;
+
+ lastlog();
+ va_start(args, p);
+ (void) vfprintf(ftrace, p, args);
+ (void) fputc('\n', ftrace);
+ (void) va_end(args);
+}
+
+
+void
+trace_change(struct rt_entry *rt,
+ uint16_t state,
+ struct rt_spare *new,
+ const char *label)
+{
+ if (ftrace == NULL)
+ return;
+
+ if (rt->rt_metric == new->rts_metric &&
+ rt->rt_gate == new->rts_gate &&
+ rt->rt_router == new->rts_router &&
+ rt->rt_state == state &&
+ rt->rt_tag == new->rts_tag &&
+ rt->rt_de_ag == new->rts_de_ag)
+ return;
+
+ lastlog();
+ (void) fprintf(ftrace, "%s %-35s ",
+ label,
+ rtname(rt->rt_dst, rt->rt_mask, rt->rt_gate));
+ print_rts(rt->rt_spares,
+ 0, 0, 0, 0, AGE_RT(rt->rt_state, rt->rt_spares->rts_origin,
+ rt->rt_ifp));
+ print_rtsorigin(origin_bits, rt->rt_spares->rts_origin);
+ trace_bits(rs_bits, rt->rt_state, rt->rt_state != state);
+
+ (void) fprintf(ftrace, "\n%*s %19s%-16s ",
+ strlen(label), "", "",
+ (rt->rt_gate != new->rts_gate ?
+ naddr_ntoa(new->rts_gate) : ""));
+ print_rts(new,
+ ((new->rts_metric == rt->rt_metric) ? -1 : 0),
+ ((new->rts_ifp == rt->rt_ifp) ? -1 : 0),
+ 0,
+ rt->rt_tag != new->rts_tag,
+ (rt->rt_time != new->rts_time &&
+ AGE_RT(rt->rt_state, new->rts_origin, new->rts_ifp)));
+ if (rt->rt_state != state) {
+ print_rtsorigin(origin_bits, new->rts_origin);
+ trace_bits(rs_bits, state, _B_TRUE);
+ }
+ (void) fputc('\n', ftrace);
+}
+
+
+void
+trace_add_del(const char *action, struct rt_entry *rt)
+{
+ if (ftrace == NULL)
+ return;
+
+ lastlog();
+ (void) fprintf(ftrace, "%s %-35s ",
+ action,
+ rtname(rt->rt_dst, rt->rt_mask, rt->rt_gate));
+ print_rts(rt->rt_spares, 0, 0, 0, 0, AGE_RT(rt->rt_state,
+ rt->rt_spares->rts_origin, rt->rt_ifp));
+ print_rtsorigin(origin_bits, rt->rt_spares->rts_origin);
+ trace_bits(rs_bits, rt->rt_state, _B_FALSE);
+ (void) fputc('\n', ftrace);
+}
+
+
+/* ARGSUSED */
+static int
+walk_trace(struct radix_node *rn,
+ void *w)
+{
+#define RT ((struct rt_entry *)rn)
+ struct rt_spare *rts;
+ int i;
+
+ (void) fprintf(ftrace, " %-35s ",
+ rtname(RT->rt_dst, RT->rt_mask, RT->rt_gate));
+ print_rts(&RT->rt_spares[0], 0, 0, 0, 0,
+ AGE_RT(RT->rt_state, RT->rt_spares[0].rts_origin, RT->rt_ifp));
+ print_rtsorigin(origin_bits, RT->rt_spares[0].rts_origin);
+ trace_bits(rs_bits, RT->rt_state, _B_FALSE);
+ if (RT->rt_poison_time >= now_garbage &&
+ RT->rt_poison_metric < RT->rt_metric)
+ (void) fprintf(ftrace, "pm=%d@%s",
+ RT->rt_poison_metric, ts(RT->rt_poison_time));
+
+ rts = &RT->rt_spares[1];
+ for (i = 1; i < RT->rt_num_spares; i++, rts++) {
+ if (rts->rts_gate != RIP_DEFAULT) {
+ (void) fprintf(ftrace, "\n #%d%15s%-16s ",
+ i, "", naddr_ntoa(rts->rts_gate));
+ print_rts(rts, 0, 0, 0, 0, 1);
+ print_rtsorigin(origin_bits, rts->rts_origin);
+ }
+ }
+ (void) fputc('\n', ftrace);
+
+ return (0);
+}
+
+
+void
+trace_dump(void)
+{
+ struct interface *ifp;
+
+ if (ftrace == NULL)
+ return;
+ lastlog();
+
+ /*
+ * Warning: the rtquery.trace.* family of STC tests depend on
+ * the log file format here. If you need to change this next
+ * message, make sure that you change the TRACE_DUMP variable
+ * as well.
+ */
+ (void) fputs("current daemon state:\n", ftrace);
+ for (ifp = ifnet; ifp != NULL; ifp = ifp->int_next)
+ trace_if("", ifp);
+ (void) fputs("Routes:\n", ftrace);
+ (void) rn_walktree(rhead, walk_trace, NULL);
+ (void) fputs("Kernel routes:\n", ftrace);
+ kern_dump();
+ (void) fputs("Discovered routers:\n", ftrace);
+ rdisc_dump();
+}
+
+
+void
+trace_rip(const char *dir1, const char *dir2,
+ struct sockaddr_in *who,
+ struct interface *ifp,
+ struct rip *msg,
+ int size) /* total size of message */
+{
+ struct netinfo *n, *lim;
+#define NA ((struct netauth *)n)
+ int i, seen_route;
+ struct in_addr tmp_mask;
+
+ if (!TRACEPACKETS || ftrace == NULL)
+ return;
+
+ lastlog();
+ if (msg->rip_cmd >= RIPCMD_MAX || msg->rip_vers == 0) {
+ (void) fprintf(ftrace, "%s bad RIPv%d cmd=%d %s"
+ " %s.%d size=%d\n",
+ dir1, msg->rip_vers, msg->rip_cmd, dir2,
+ naddr_ntoa(who->sin_addr.s_addr),
+ ntohs(who->sin_port),
+ size);
+ return;
+ }
+
+ (void) fprintf(ftrace, "%s RIPv%d %s %s %s.%d%s%s\n",
+ dir1, msg->rip_vers, ripcmds[msg->rip_cmd], dir2,
+ naddr_ntoa(who->sin_addr.s_addr), ntohs(who->sin_port),
+ ifp ? " via " : "", ifp ? ifp->int_name : "");
+ if (!TRACECONTENTS)
+ return;
+
+ seen_route = 0;
+ switch (msg->rip_cmd) {
+ case RIPCMD_REQUEST:
+ case RIPCMD_RESPONSE:
+
+ n = msg->rip_nets;
+ tmp_mask.s_addr = n->n_mask;
+ lim = n + (size - 4) / sizeof (struct netinfo);
+ for (; n < lim; n++) {
+ if (!seen_route &&
+ n->n_family == RIP_AF_UNSPEC &&
+ ntohl(n->n_metric) == HOPCNT_INFINITY &&
+ msg->rip_cmd == RIPCMD_REQUEST &&
+ (n+1 == lim ||
+ (n+2 == lim &&
+ (n+1)->n_family == RIP_AF_AUTH))) {
+ (void) fputs("\tQUERY ", ftrace);
+ if (n->n_dst != 0)
+ (void) fprintf(ftrace, "%s ",
+ naddr_ntoa(n->n_dst));
+ if (n->n_mask != 0)
+ (void) fprintf(ftrace, "mask=%s ",
+ inet_ntoa(tmp_mask));
+ if (n->n_nhop != 0)
+ (void) fprintf(ftrace, "nhop=%s ",
+ naddr_ntoa(n->n_nhop));
+ if (n->n_tag != 0)
+ (void) fprintf(ftrace, "tag=%#x ",
+ ntohs(n->n_tag));
+ (void) fputc('\n', ftrace);
+ continue;
+ }
+
+ if (n->n_family == RIP_AF_AUTH) {
+ if (NA->a_type == RIP_AUTH_PW &&
+ n == msg->rip_nets) {
+ (void) fprintf(ftrace, "\tPassword"
+ " Authentication:"
+ " \"%s\"\n",
+ qstring(NA->au.au_pw,
+ RIP_AUTH_PW_LEN));
+ continue;
+ }
+
+ if (NA->a_type == RIP_AUTH_MD5 &&
+ n == msg->rip_nets) {
+ (void) fprintf(ftrace,
+ "\tMD5 Auth"
+ " pkt_len=%d KeyID=%u"
+ " auth_len=%d"
+ " seqno=%#lx"
+ " rsvd=%#x,%#x\n",
+ ntohs(NA->au.a_md5.md5_pkt_len),
+ NA->au.a_md5.md5_keyid,
+ NA->au.a_md5.md5_auth_len,
+ (unsigned long)ntohl(NA->au.a_md5.
+ md5_seqno),
+ ntohs(NA->au.a_md5.rsvd[0]),
+ ntohs(NA->au.a_md5.rsvd[1]));
+ continue;
+ }
+ (void) fprintf(ftrace,
+ "\tAuthentication type %d: ",
+ ntohs(NA->a_type));
+ for (i = 0; i < (int)sizeof (NA->au.au_pw);
+ i++)
+ (void) fprintf(ftrace, "%02x ",
+ NA->au.au_pw[i]);
+ (void) fputc('\n', ftrace);
+ continue;
+ }
+
+ seen_route = 1;
+ if (n->n_family != RIP_AF_INET) {
+ (void) fprintf(ftrace,
+ "\t(af %d) %-18s mask=%s ",
+ ntohs(n->n_family),
+ naddr_ntoa(n->n_dst),
+ inet_ntoa(tmp_mask));
+ } else if (msg->rip_vers == RIPv1) {
+ (void) fprintf(ftrace, "\t%-18s ",
+ addrname(n->n_dst,
+ ntohl(n->n_mask),
+ n->n_mask == 0 ? 2 : 1));
+ } else {
+ (void) fprintf(ftrace, "\t%-18s ",
+ addrname(n->n_dst,
+ ntohl(n->n_mask),
+ n->n_mask == 0 ? 2 : 0));
+ }
+ (void) fprintf(ftrace, "metric=%-2lu ",
+ (unsigned long)ntohl(n->n_metric));
+ if (n->n_nhop != 0)
+ (void) fprintf(ftrace, " nhop=%s ",
+ naddr_ntoa(n->n_nhop));
+ if (n->n_tag != 0)
+ (void) fprintf(ftrace, "tag=%#x",
+ ntohs(n->n_tag));
+ (void) fputc('\n', ftrace);
+ }
+ if (size != (char *)n - (char *)msg)
+ (void) fprintf(ftrace, "truncated record, len %d\n",
+ size);
+ break;
+
+ case RIPCMD_TRACEON:
+ (void) fprintf(ftrace, "\tfile=\"%.*s\"\n", size-4,
+ msg->rip_tracefile);
+ break;
+
+ case RIPCMD_TRACEOFF:
+ break;
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.rshd.c b/usr/src/cmd/cmd-inet/usr.sbin/in.rshd.c
new file mode 100644
index 0000000000..d5ffbcffc5
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.rshd.c
@@ -0,0 +1,1657 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983-1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * Portions of this source code were derived from Berkeley 4.3 BSD
+ * under license from the Regents of the University of California.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#define _FILE_OFFSET_BITS 64
+
+/*
+ * remote shell server:
+ * remuser\0
+ * locuser\0
+ * command\0
+ * data
+ */
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/telioctl.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/select.h>
+
+#include <netinet/in.h>
+
+#include <arpa/inet.h>
+
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <pwd.h>
+#include <grp.h>
+#include <signal.h>
+#include <netdb.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <locale.h>
+
+#include <sys/resource.h>
+#include <sys/filio.h>
+#include <shadow.h>
+#include <stdlib.h>
+
+#include <security/pam_appl.h>
+
+#include <k5-int.h>
+#include <krb5_repository.h>
+#include <com_err.h>
+#include <kcmd.h>
+
+#ifndef NCARGS
+#define NCARGS 5120
+#endif /* !NCARGS */
+
+static void error(char *, ...);
+static void doit(int, struct sockaddr_storage *, char **);
+static void getstr(int, char *, int, char *);
+
+static int legalenvvar(char *);
+static void add_to_envinit(char *);
+static int locale_envmatch(char *, char *);
+
+/* Function decls. for functions not in any header file. (Grrrr.) */
+extern int audit_rshd_setup(void);
+extern int audit_rshd_success(char *, char *, char *, char *);
+extern int audit_rshd_fail(char *, char *, char *, char *, char *);
+extern int audit_settid(int);
+
+static int do_encrypt = 0;
+static pam_handle_t *pamh;
+
+/*
+ * This is the shell/kshell daemon. The very basic protocol for checking
+ * authentication and authorization is:
+ * 1) Check authentication.
+ * 2) Check authorization via the access-control files:
+ * ~/.k5login (using krb5_kuserok) and/or
+ * Execute command if configured authoriztion checks pass, else deny
+ * permission.
+ *
+ * The configuration is done either by command-line arguments passed by inetd,
+ * or by the name of the daemon. If command-line arguments are present, they
+ * take priority. The options are:
+ * -k allow kerberos authentication (krb5 only; krb4 support is not provided)
+ * -5 same as `-k', mainly for compatability with MIT
+ * -e allow encrypted session
+ * -c demand authenticator checksum
+ * -i ignore authenticator checksum
+ * -U Refuse connections that cannot be mapped to a name via `gethostbyname'
+ * -s <tos> Set the IP TOS option
+ * -S <keytab> Set the keytab file to use
+ * -M <realm> Set the Kerberos realm to use
+ */
+
+#define ARGSTR "ek5ciUD:M:S:L:?:"
+#define RSHD_BUFSIZ (50 * 1024)
+
+static krb5_context bsd_context;
+static krb5_keytab keytab = NULL;
+static krb5_ccache ccache = NULL;
+static krb5_keyblock *sessionkey = NULL;
+
+static int require_encrypt = 0;
+static int resolve_hostname = 0;
+static int krb5auth_flag = 0; /* Flag set, when KERBEROS is enabled */
+static enum kcmd_proto kcmd_protocol;
+
+#ifdef DEBUG
+static int debug_port = 0;
+#endif /* DEBUG */
+
+/*
+ * There are two authentication related masks:
+ * auth_ok and auth_sent.
+ * The auth_ok mask is the or'ing of authentication
+ * systems any one of which can be used.
+ * The auth_sent mask is the or'ing of one or more authentication/authorization
+ * systems that succeeded. If the and'ing
+ * of these two masks is true, then authorization is successful.
+ */
+
+#define AUTH_KRB5 (0x2)
+static int auth_ok = 0;
+static int auth_sent = 0;
+static int checksum_required = 0;
+static int checksum_ignored = 0;
+
+/*
+ * Leave room for 4 environment variables to be passed.
+ * The "-L env_var" option has been added primarily to
+ * maintain compatability with MIT.
+ */
+#define MAXENV 4
+static char *save_env[MAXENV];
+static int num_env = 0;
+
+static void usage(void);
+static krb5_error_code recvauth(int, int *);
+
+/*ARGSUSED*/
+void
+main(int argc, char **argv, char **renvp)
+{
+ struct linger linger;
+ int on = 1, fromlen;
+ struct sockaddr_storage from;
+ int fd = 0;
+
+ extern int opterr, optind;
+ extern char *optarg;
+ int ch;
+ int tos = -1;
+ krb5_error_code status;
+
+ openlog("rsh", LOG_PID | LOG_ODELAY, LOG_DAEMON);
+ (void) audit_rshd_setup(); /* BSM */
+ fromlen = sizeof (from);
+
+ (void) setlocale(LC_ALL, "");
+
+ /*
+ * Analyze parameters.
+ */
+ opterr = 0;
+ while ((ch = getopt(argc, argv, ARGSTR)) != EOF)
+ switch (ch) {
+ case '5':
+ case 'k':
+ auth_ok |= AUTH_KRB5;
+ krb5auth_flag++;
+ break;
+
+ case 'c':
+ checksum_required = 1;
+ krb5auth_flag++;
+ break;
+ case 'i':
+ checksum_ignored = 1;
+ krb5auth_flag++;
+ break;
+
+ case 'e':
+ require_encrypt = 1;
+ krb5auth_flag++;
+ break;
+#ifdef DEBUG
+ case 'D':
+ debug_port = atoi(optarg);
+ break;
+#endif /* DEBUG */
+ case 'U':
+ resolve_hostname = 1;
+ break;
+
+ case 'M':
+ krb5_set_default_realm(bsd_context, optarg);
+ krb5auth_flag++;
+ break;
+
+ case 'S':
+ if ((status = krb5_kt_resolve(bsd_context, optarg,
+ &keytab))) {
+ com_err("rsh", status,
+ gettext("while resolving "
+ "srvtab file %s"), optarg);
+ exit(2);
+ }
+ krb5auth_flag++;
+ break;
+
+ case 's':
+ if (optarg == NULL || ((tos = atoi(optarg)) < 0) ||
+ (tos > 255)) {
+ syslog(LOG_ERR, "rshd: illegal tos value: "
+ "%s\n", optarg);
+ }
+ break;
+
+ case 'L':
+ if (num_env < MAXENV) {
+ save_env[num_env] = strdup(optarg);
+ if (!save_env[num_env++]) {
+ com_err("rsh", ENOMEM,
+ gettext("in saving env"));
+ exit(2);
+ }
+ } else {
+ (void) fprintf(stderr, gettext("rshd: Only %d"
+ " -L arguments allowed\n"),
+ MAXENV);
+ exit(2);
+ }
+ break;
+
+ case '?':
+ default:
+ usage();
+ exit(1);
+ break;
+ }
+
+ if (optind == 0) {
+ usage();
+ exit(1);
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (krb5auth_flag > 0) {
+ status = krb5_init_context(&bsd_context);
+ if (status) {
+ syslog(LOG_ERR, "Error initializing krb5: %s",
+ error_message(status));
+ exit(1);
+ }
+ }
+
+ if (!checksum_required && !checksum_ignored)
+ checksum_ignored = 1;
+
+ if (checksum_required && checksum_ignored) {
+ syslog(LOG_CRIT, gettext("Checksums are required and ignored."
+ "These options are mutually exclusive"
+ "--check the documentation."));
+ error("Configuration error: mutually exclusive "
+ "options specified.\n");
+ exit(1);
+ }
+
+#ifdef DEBUG
+ if (debug_port) {
+ int s;
+ struct sockaddr_in sin;
+
+ if ((s = socket(AF_INET, SOCK_STREAM, PF_UNSPEC)) < 0) {
+ fprintf(stderr, gettext("Error in socket: %s\n"),
+ strerror(errno));
+ exit(2);
+ }
+ (void) memset((char *)&sin, 0, sizeof (sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(debug_port);
+ sin.sin_addr.s_addr = INADDR_ANY;
+
+ (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&on, sizeof (on));
+
+ if ((bind(s, (struct sockaddr *)&sin, sizeof (sin))) < 0) {
+ (void) fprintf(stderr, gettext("Error in bind: %s\n"),
+ strerror(errno));
+ exit(2);
+ }
+ if ((listen(s, 5)) < 0) {
+ (void) fprintf(stderr, gettext("Error in listen: %s\n"),
+ strerror(errno));
+ exit(2);
+ }
+ if ((fd = accept(s, (struct sockaddr *)&from,
+ &fromlen)) < 0) {
+ (void) fprintf(stderr, gettext("Error in accept: %s\n"),
+ strerror(errno));
+ exit(2);
+ }
+ (void) close(s);
+ }
+ else
+#endif /* DEBUG */
+ {
+ if (getpeername(STDIN_FILENO, (struct sockaddr *)&from,
+ (socklen_t *)&fromlen) < 0) {
+ (void) fprintf(stderr, "rshd: ");
+ perror("getpeername");
+ _exit(1);
+ }
+ fd = STDIN_FILENO;
+ }
+
+ if (audit_settid(fd) != 0) {
+ perror("settid");
+ exit(1);
+ }
+
+ if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *)&on,
+ sizeof (on)) < 0)
+ syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
+ linger.l_onoff = 1;
+ linger.l_linger = 60; /* XXX */
+ if (setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *)&linger,
+ sizeof (linger)) < 0)
+ syslog(LOG_WARNING, "setsockopt (SO_LINGER): %m");
+
+ if ((tos != -1) && (setsockopt(fd, IPPROTO_IP, IP_TOS, (char *)&tos,
+ sizeof (tos)) < 0) &&
+ (errno != ENOPROTOOPT)) {
+ syslog(LOG_ERR, "setsockopt (IP_TOS %d): %m");
+ }
+
+ doit(dup(fd), &from, renvp);
+ /* NOTREACHED */
+}
+
+/*
+ * locale environments to be passed to shells.
+ */
+static char *localeenv[] = {
+ "LANG",
+ "LC_CTYPE", "LC_NUMERIC", "LC_TIME", "LC_COLLATE",
+ "LC_MONETARY", "LC_MESSAGES", "LC_ALL", NULL};
+
+/*
+ * The following is for the environment variable list
+ * used in the call to execle(). envinit is declared here,
+ * but populated after the call to getpwnam().
+ */
+static char *homedir; /* "HOME=" */
+static char *shell; /* "SHELL=" */
+static char *username; /* "USER=" */
+static char *tz; /* "TZ=" */
+
+static char homestr[] = "HOME=";
+static char shellstr[] = "SHELL=";
+static char userstr[] = "USER=";
+static char tzstr[] = "TZ=";
+
+static char **envinit;
+#define PAM_ENV_ELIM 16 /* allow 16 PAM environment variables */
+#define USERNAME_LEN 16 /* maximum number of characters in user name */
+
+/*
+ * See PSARC opinion 1992/025
+ */
+static char userpath[] = "PATH=/usr/bin:";
+static char rootpath[] = "PATH=/usr/sbin:/usr/bin";
+
+static char cmdbuf[NCARGS+1];
+static char hostname [MAXHOSTNAMELEN + 1];
+static char locuser[USERNAME_LEN + 1];
+static char remuser[USERNAME_LEN + 1];
+
+#define KRB5_RECVAUTH_V5 5
+#define SIZEOF_INADDR sizeof (struct in_addr)
+
+#define MAX_REPOSITORY_LEN 255
+static char repository[MAX_REPOSITORY_LEN];
+
+static char *kremuser;
+static krb5_principal client = NULL;
+
+static char remote_addr[64];
+static char local_addr[64];
+
+static void
+doit(int f, struct sockaddr_storage *fromp, char **renvp)
+{
+ char *cp;
+
+ struct passwd *pwd;
+ char *path;
+ char *tzenv;
+ struct spwd *shpwd;
+ struct stat statb;
+ char **lenvp;
+
+ krb5_error_code status;
+ int valid_checksum;
+ int cnt;
+ int sin_len;
+ struct sockaddr_in localaddr;
+
+ int s;
+ in_port_t port;
+ pid_t pid;
+ int pv[2], pw[2], px[2], cc;
+ char buf[RSHD_BUFSIZ];
+ char sig;
+ int one = 1;
+ int v = 0;
+ int err = 0;
+ int idx = 0;
+ char **pam_env;
+ char abuf[INET6_ADDRSTRLEN];
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+ int fromplen;
+ int homedir_len, shell_len, username_len, tz_len;
+ int no_name;
+ int bad_port;
+ int netf = 0;
+
+ (void) signal(SIGINT, SIG_DFL);
+ (void) signal(SIGQUIT, SIG_DFL);
+ (void) signal(SIGTERM, SIG_DFL);
+ (void) signal(SIGXCPU, SIG_DFL);
+ (void) signal(SIGXFSZ, SIG_DFL);
+ (void) sigset(SIGCHLD, SIG_IGN);
+ (void) signal(SIGPIPE, SIG_DFL);
+ (void) signal(SIGHUP, SIG_DFL);
+
+#ifdef DEBUG
+ { int t = open("/dev/tty", 2);
+ if (t >= 0) {
+ (void) setsid();
+ (void) close(t);
+ }
+ }
+#endif
+ if (fromp->ss_family == AF_INET) {
+ sin = (struct sockaddr_in *)fromp;
+ port = ntohs((ushort_t)sin->sin_port);
+ fromplen = sizeof (struct sockaddr_in);
+ } else if (fromp->ss_family == AF_INET6) {
+ sin6 = (struct sockaddr_in6 *)fromp;
+ port = ntohs((ushort_t)sin6->sin6_port);
+ fromplen = sizeof (struct sockaddr_in6);
+ } else {
+ syslog(LOG_ERR, "wrong address family\n");
+ exit(1);
+ }
+
+ sin_len = sizeof (struct sockaddr_in);
+ if (getsockname(f, (struct sockaddr *)&localaddr,
+ &sin_len) < 0) {
+ perror("getsockname");
+ exit(1);
+ }
+
+ netf = f;
+
+ bad_port = (port >= IPPORT_RESERVED ||
+ port < (uint_t)(IPPORT_RESERVED/2));
+
+ no_name = (getnameinfo((const struct sockaddr *) fromp, fromplen,
+ hostname, sizeof (hostname), NULL, 0, 0) != 0);
+
+ /* Get the name of the client side host to use later */
+ if (no_name == 1 || bad_port == 1) {
+ if (no_name != 0) {
+ /*
+ * If the '-U' option was given on the cmd line,
+ * we must be able to lookup the hostname
+ */
+ if (resolve_hostname) {
+ syslog(LOG_ERR, "rshd: Couldn't resolve your "
+ "address into a host name.\r\n Please "
+ "contact your net administrator");
+ exit(1);
+ } else {
+ /*
+ * If there is no host name available and the
+ * -U option hasnt been used on the cmd line,
+ * use the IP address to identify the
+ * host in the pam call below.
+ */
+ (void) strncpy(hostname, abuf,
+ sizeof (hostname));
+ }
+ }
+
+ if (fromp->ss_family == AF_INET6) {
+ if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
+ struct in_addr ipv4_addr;
+
+ IN6_V4MAPPED_TO_INADDR(&sin6->sin6_addr,
+ &ipv4_addr);
+ (void) inet_ntop(AF_INET, &ipv4_addr, abuf,
+ sizeof (abuf));
+ } else {
+ (void) inet_ntop(AF_INET6, &sin6->sin6_addr,
+ abuf, sizeof (abuf));
+ }
+ } else if (fromp->ss_family == AF_INET) {
+ (void) inet_ntop(AF_INET, &sin->sin_addr,
+ abuf, sizeof (abuf));
+ }
+ }
+
+ if (!krb5auth_flag && bad_port) {
+ if (no_name)
+ syslog(LOG_NOTICE, "connection from %s - "
+ "bad port\n", abuf);
+ else
+ syslog(LOG_NOTICE, "connection from %s (%s) - "
+ "bad port\n", hostname, abuf);
+ exit(1);
+ }
+
+ (void) alarm(60);
+ port = 0;
+ for (;;) {
+ char c;
+ if ((cc = read(f, &c, 1)) != 1) {
+ if (cc < 0)
+ syslog(LOG_NOTICE, "read: %m");
+ (void) shutdown(f, 1+1);
+ exit(1);
+ }
+ if (c == 0)
+ break;
+ port = port * 10 + c - '0';
+ }
+ (void) alarm(0);
+ if (port != 0) {
+ int lport = 0;
+ struct sockaddr_storage ctl_addr;
+ int addrlen;
+
+ (void) memset(&ctl_addr, 0, sizeof (ctl_addr));
+ addrlen = sizeof (ctl_addr);
+ if (getsockname(f, (struct sockaddr *)&ctl_addr,
+ &addrlen) < 0) {
+ syslog(LOG_ERR, "getsockname: %m");
+ exit(1);
+ }
+get_port:
+ /*
+ * 0 means that rresvport_addr() will bind to a port in
+ * the anonymous priviledged port range.
+ */
+ if (krb5auth_flag) {
+ /*
+ * Kerberos does not support IPv6 yet.
+ */
+ lport = IPPORT_RESERVED - 1;
+ }
+ s = rresvport_addr(&lport, &ctl_addr);
+
+ if (s < 0) {
+ syslog(LOG_ERR, "can't get stderr port: %m");
+ exit(1);
+ }
+ if (!krb5auth_flag && (port >= IPPORT_RESERVED)) {
+ syslog(LOG_ERR, "2nd port not reserved\n");
+ exit(1);
+ }
+ if (fromp->ss_family == AF_INET) {
+ sin->sin_port = htons((ushort_t)port);
+ } else if (fromp->ss_family == AF_INET6) {
+ sin6->sin6_port = htons((ushort_t)port);
+ }
+ if (connect(s, (struct sockaddr *)fromp, fromplen) < 0) {
+ if (errno == EADDRINUSE) {
+ (void) close(s);
+ goto get_port;
+ }
+ syslog(LOG_INFO, "connect second port: %m");
+ exit(1);
+ }
+ }
+ (void) dup2(f, 0);
+ (void) dup2(f, 1);
+ (void) dup2(f, 2);
+
+#ifdef DEBUG
+ syslog(LOG_NOTICE, "rshd: Client hostname = %s", hostname);
+ if (debug_port)
+ syslog(LOG_NOTICE, "rshd: Debug port is %d", debug_port);
+ if (krb5auth_flag > 0)
+ syslog(LOG_NOTICE, "rshd: Kerberos mode is ON");
+ else
+ syslog(LOG_NOTICE, "rshd: Kerberos mode is OFF");
+#endif /* DEBUG */
+
+ if (krb5auth_flag > 0) {
+ if ((status = recvauth(f, &valid_checksum))) {
+ syslog(LOG_ERR, gettext("Kerberos Authentication "
+ "failed \n"));
+ error("Authentication failed: %s\n",
+ error_message(status));
+ (void) audit_rshd_fail("Kerberos Authentication "
+ "failed", hostname, remuser, locuser, cmdbuf);
+ exit(1);
+ }
+
+ if (checksum_required && !valid_checksum &&
+ kcmd_protocol == KCMD_OLD_PROTOCOL) {
+ syslog(LOG_WARNING, "Client did not supply required"
+ " checksum--connection rejected.");
+ error("Client did not supply required"
+ "checksum--connection rejected.\n");
+ (void) audit_rshd_fail("Client did not supply required"
+ " checksum--connection rejected.", hostname,
+ remuser, locuser, cmdbuf); /* BSM */
+ goto signout;
+ }
+
+ /*
+ * Authentication has succeeded, we now need
+ * to check authorization.
+ *
+ * krb5_kuserok returns 1 if OK.
+ */
+ if (client && krb5_kuserok(bsd_context, client, locuser)) {
+ auth_sent |= AUTH_KRB5;
+ } else {
+ syslog(LOG_ERR, "Principal %s (%s@%s) for local user "
+ "%s failed krb5_kuserok.\n",
+ kremuser, remuser, hostname, locuser);
+ }
+ } else {
+ getstr(netf, remuser, sizeof (remuser), "remuser");
+ getstr(netf, locuser, sizeof (locuser), "locuser");
+ getstr(netf, cmdbuf, sizeof (cmdbuf), "command");
+ }
+
+#ifdef DEBUG
+ syslog(LOG_NOTICE, "rshd: locuser = %s, remuser = %s, cmdbuf = %s",
+ locuser, remuser, cmdbuf);
+#endif /* DEBUG */
+
+ /*
+ * Note that there is no rsh conv functions at present.
+ */
+ if (krb5auth_flag > 0) {
+ if ((err = pam_start("krsh", locuser, NULL, &pamh))
+ != PAM_SUCCESS) {
+ syslog(LOG_ERR, "pam_start() failed: %s\n",
+ pam_strerror(0, err));
+ exit(1);
+ }
+ }
+ else
+ {
+ if ((err = pam_start("rsh", locuser, NULL, &pamh))
+ != PAM_SUCCESS) {
+ syslog(LOG_ERR, "pam_start() failed: %s\n",
+ pam_strerror(0, err));
+ exit(1);
+ }
+ }
+ if ((err = pam_set_item(pamh, PAM_RHOST, hostname)) != PAM_SUCCESS) {
+ syslog(LOG_ERR, "pam_set_item() failed: %s\n",
+ pam_strerror(pamh, err));
+ exit(1);
+ }
+ if ((err = pam_set_item(pamh, PAM_RUSER, remuser)) != PAM_SUCCESS) {
+ syslog(LOG_ERR, "pam_set_item() failed: %s\n",
+ pam_strerror(pamh, err));
+ exit(1);
+ }
+
+ pwd = getpwnam(locuser);
+ shpwd = getspnam(locuser);
+ if ((pwd == NULL) || (shpwd == NULL)) {
+ if (krb5auth_flag > 0)
+ syslog(LOG_ERR, "Principal %s (%s@%s) for local user "
+ "%s has no account.\n", kremuser, remuser,
+ hostname, locuser);
+ error("permission denied.\n");
+ (void) audit_rshd_fail("Login incorrect", hostname,
+ remuser, locuser, cmdbuf); /* BSM */
+ exit(1);
+ }
+
+ if (krb5auth_flag > 0) {
+ (void) snprintf(repository, sizeof (repository),
+ KRB5_REPOSITORY_NAME);
+ /*
+ * We currently only support special handling of the
+ * KRB5 PAM repository
+ */
+ if (strlen(locuser) != 0) {
+ krb5_repository_data_t krb5_data;
+ pam_repository_t pam_rep_data;
+
+ krb5_data.principal = locuser;
+ krb5_data.flags = SUNW_PAM_KRB5_ALREADY_AUTHENTICATED;
+
+ pam_rep_data.type = repository;
+ pam_rep_data.scope = (void *)&krb5_data;
+ pam_rep_data.scope_len = sizeof (krb5_data);
+
+ (void) pam_set_item(pamh, PAM_REPOSITORY,
+ (void *)&pam_rep_data);
+ }
+ }
+
+ /*
+ * maintain 2.1 and 4.* and BSD semantics with anonymous rshd
+ */
+ if (shpwd->sp_pwdp != 0 && *shpwd->sp_pwdp != '\0' &&
+ (v = pam_authenticate(pamh, 0)) != PAM_SUCCESS) {
+ error("permission denied\n");
+ (void) audit_rshd_fail("Permission denied", hostname,
+ remuser, locuser, cmdbuf); /* BSM */
+ (void) pam_end(pamh, v);
+ exit(1);
+ }
+
+ if (krb5auth_flag > 0) {
+ if (require_encrypt && (!do_encrypt)) {
+ error("You must use encryption.\n");
+ (void) audit_rshd_fail("You must use encryption.",
+ hostname, remuser, locuser, cmdbuf); /* BSM */
+ goto signout;
+ }
+
+ if (!(auth_ok & auth_sent)) {
+ if (auth_sent) {
+ error("Another authentication mechanism "
+ "must be used to access this host.\n");
+ (void) audit_rshd_fail("Another authentication"
+ " mechanism must be used to access"
+ " this host.\n", hostname, remuser,
+ locuser, cmdbuf); /* BSM */
+ goto signout;
+ } else {
+ error("Permission denied.\n");
+ (void) audit_rshd_fail("Permission denied.",
+ hostname, remuser, locuser, cmdbuf);
+ /* BSM */
+ goto signout;
+ }
+ }
+
+
+ if (pwd->pw_uid && !access("/etc/nologin", F_OK)) {
+ error("Logins currently disabled.\n");
+ (void) audit_rshd_fail("Logins currently disabled.",
+ hostname, remuser, locuser, cmdbuf);
+ goto signout;
+ }
+
+ /* Log access to account */
+ if (pwd && (pwd->pw_uid == 0)) {
+ syslog(LOG_NOTICE, "Executing %s for user %s (%s@%s)"
+ " as ROOT", cmdbuf,
+ kremuser, remuser, hostname);
+ }
+ }
+
+ if ((v = pam_acct_mgmt(pamh, 0)) != PAM_SUCCESS) {
+ switch (v) {
+ case PAM_NEW_AUTHTOK_REQD:
+ error("password expired\n");
+ (void) audit_rshd_fail("Password expired", hostname,
+ remuser, locuser, cmdbuf); /* BSM */
+ break;
+ case PAM_PERM_DENIED:
+ error("account expired\n");
+ (void) audit_rshd_fail("Account expired", hostname,
+ remuser, locuser, cmdbuf); /* BSM */
+ break;
+ case PAM_AUTHTOK_EXPIRED:
+ error("password expired\n");
+ (void) audit_rshd_fail("Password expired", hostname,
+ remuser, locuser, cmdbuf); /* BSM */
+ break;
+ default:
+ error("login incorrect\n");
+ (void) audit_rshd_fail("Permission denied", hostname,
+ remuser, locuser, cmdbuf); /* BSM */
+ break;
+ }
+ (void) pam_end(pamh, PAM_ABORT);
+ exit(1);
+ }
+
+ if (chdir(pwd->pw_dir) < 0) {
+ (void) chdir("/");
+#ifdef notdef
+ error("No remote directory.\n");
+
+ exit(1);
+#endif
+ }
+
+ /*
+ * XXX There is no session management currently being done
+ */
+
+ (void) write(STDERR_FILENO, "\0", 1);
+ if (port || do_encrypt) {
+ if ((pipe(pv) < 0)) {
+ error("Can't make pipe.\n");
+ (void) pam_end(pamh, PAM_ABORT);
+ exit(1);
+ }
+ if (do_encrypt) {
+ if (pipe(pw) < 0) {
+ error("Can't make pipe 2.\n");
+ (void) pam_end(pamh, PAM_ABORT);
+ exit(1);
+ }
+ if (pipe(px) < 0) {
+ error("Can't make pipe 3.\n");
+ (void) pam_end(pamh, PAM_ABORT);
+ exit(1);
+ }
+ }
+ pid = fork();
+ if (pid == (pid_t)-1) {
+ error("Fork (to start shell) failed on server. "
+ "Please try again later.\n");
+ (void) pam_end(pamh, PAM_ABORT);
+ exit(1);
+ }
+ if (pid) {
+ fd_set ready;
+ fd_set readfrom;
+
+ (void) close(STDIN_FILENO);
+ (void) close(STDOUT_FILENO);
+ (void) close(STDERR_FILENO);
+ (void) close(pv[1]);
+ if (do_encrypt) {
+ (void) close(pw[1]);
+ (void) close(px[0]);
+ } else {
+ (void) close(f);
+ }
+
+ (void) FD_ZERO(&readfrom);
+
+ FD_SET(pv[0], &readfrom);
+ if (do_encrypt) {
+ FD_SET(pw[0], &readfrom);
+ FD_SET(f, &readfrom);
+ }
+ if (port)
+ FD_SET(s, &readfrom);
+
+ /* read f (net), write to px[1] (child stdin) */
+ /* read pw[0] (child stdout), write to f (net) */
+ /* read s (alt. channel), signal child */
+ /* read pv[0] (child stderr), write to s */
+ if (ioctl(pv[0], FIONBIO, (char *)&one) == -1)
+ syslog(LOG_INFO, "ioctl FIONBIO: %m");
+ if (do_encrypt &&
+ ioctl(pw[0], FIONBIO, (char *)&one) == -1)
+ syslog(LOG_INFO, "ioctl FIONBIO: %m");
+ do {
+ ready = readfrom;
+ if (select(FD_SETSIZE, &ready, NULL,
+ NULL, NULL) < 0) {
+ if (errno == EINTR) {
+ continue;
+ } else {
+ break;
+ }
+ }
+ /*
+ * Read from child stderr, write to net
+ */
+ if (port && FD_ISSET(pv[0], &ready)) {
+ errno = 0;
+ cc = read(pv[0], buf, sizeof (buf));
+ if (cc <= 0) {
+ (void) shutdown(s, 2);
+ FD_CLR(pv[0], &readfrom);
+ } else {
+ (void) deswrite(s, buf, cc, 1);
+ }
+ }
+ /*
+ * Read from alternate channel, signal child
+ */
+ if (port && FD_ISSET(s, &ready)) {
+ if ((int)desread(s, &sig, 1, 1) <= 0)
+ FD_CLR(s, &readfrom);
+ else
+ (void) killpg(pid, sig);
+ }
+ /*
+ * Read from child stdout, write to net
+ */
+ if (do_encrypt && FD_ISSET(pw[0], &ready)) {
+ errno = 0;
+ cc = read(pw[0], buf, sizeof (buf));
+ if (cc <= 0) {
+ (void) shutdown(f, 2);
+ FD_CLR(pw[0], &readfrom);
+ } else {
+ (void) deswrite(f, buf, cc, 0);
+ }
+ }
+ /*
+ * Read from the net, write to child stdin
+ */
+ if (do_encrypt && FD_ISSET(f, &ready)) {
+ errno = 0;
+ cc = desread(f, buf, sizeof (buf), 0);
+ if (cc <= 0) {
+ (void) close(px[1]);
+ FD_CLR(f, &readfrom);
+ } else {
+ int wcc;
+ wcc = write(px[1], buf, cc);
+ if (wcc == -1) {
+ /*
+ * pipe closed,
+ * don't read any
+ * more
+ *
+ * might check for
+ * EPIPE
+ */
+ (void) close(px[1]);
+ FD_CLR(f, &readfrom);
+ } else if (wcc != cc) {
+ /* CSTYLED */
+ syslog(LOG_INFO, gettext("only wrote %d/%d to child"),
+ wcc, cc);
+ }
+ }
+ }
+ } while ((port && FD_ISSET(s, &readfrom)) ||
+ (port && FD_ISSET(pv[0], &readfrom)) ||
+ (do_encrypt && FD_ISSET(f, &readfrom)) ||
+ (do_encrypt && FD_ISSET(pw[0], &readfrom)));
+#ifdef DEBUG
+ syslog(LOG_INFO, "Shell process completed.");
+#endif /* DEBUG */
+ if (ccache)
+ (void) pam_close_session(pamh, 0);
+ (void) pam_end(pamh, PAM_SUCCESS);
+
+ exit(0);
+ } /* End of Parent block */
+
+ (void) setsid(); /* Should be the same as above. */
+ (void) close(pv[0]);
+ (void) dup2(pv[1], 2);
+ (void) close(pv[1]);
+ if (port)
+ (void) close(s);
+ if (do_encrypt) {
+ (void) close(f);
+ (void) close(pw[0]);
+ (void) close(px[1]);
+
+ (void) dup2(px[0], 0);
+ (void) dup2(pw[1], 1);
+
+ (void) close(px[0]);
+ (void) close(pw[1]);
+ }
+ }
+
+ if (*pwd->pw_shell == '\0')
+ pwd->pw_shell = "/bin/sh";
+ if (!do_encrypt)
+ (void) close(f);
+ /*
+ * write audit record before making uid switch
+ */
+ (void) audit_rshd_success(hostname, remuser, locuser, cmdbuf); /* BSM */
+
+ /* set the real (and effective) GID */
+ if (setgid(pwd->pw_gid) == -1) {
+ error("Invalid gid.\n");
+ (void) pam_end(pamh, PAM_ABORT);
+ exit(1);
+ }
+
+ /*
+ * Initialize the supplementary group access list.
+ */
+ if (strlen(locuser) == 0) {
+ error("No local user.\n");
+ (void) pam_end(pamh, PAM_ABORT);
+ exit(1);
+ }
+ if (initgroups(locuser, pwd->pw_gid) == -1) {
+ error("Initgroup failed.\n");
+ (void) pam_end(pamh, PAM_ABORT);
+ exit(1);
+ }
+
+ if ((v = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) {
+ error("Insufficient credentials.\n");
+ (void) pam_end(pamh, v);
+ exit(1);
+ }
+
+ /* set the real (and effective) UID */
+ if (setuid(pwd->pw_uid) == -1) {
+ error("Invalid uid.\n");
+ (void) pam_end(pamh, PAM_ABORT);
+ exit(1);
+ }
+
+ /* Change directory only after becoming the appropriate user. */
+ if (chdir(pwd->pw_dir) < 0) {
+ (void) chdir("/");
+ if (krb5auth_flag > 0) {
+ syslog(LOG_ERR, "Principal %s (%s@%s) for local user"
+ " %s has no home directory.",
+ kremuser, remuser, hostname, locuser);
+ error("No remote directory.\n");
+ goto signout;
+ }
+#ifdef notdef
+ error("No remote directory.\n");
+ exit(1);
+#endif
+ }
+
+ path = (pwd->pw_uid == 0) ? rootpath : userpath;
+
+ /*
+ * Space for the following environment variables are dynamically
+ * allocated because their lengths are not known before calling
+ * getpwnam().
+ */
+ homedir_len = strlen(pwd->pw_dir) + strlen(homestr) + 1;
+ shell_len = strlen(pwd->pw_shell) + strlen(shellstr) + 1;
+ username_len = strlen(pwd->pw_name) + strlen(userstr) + 1;
+ homedir = (char *)malloc(homedir_len);
+ shell = (char *)malloc(shell_len);
+ username = (char *)malloc(username_len);
+ if (homedir == NULL || shell == NULL || username == NULL) {
+ perror("malloc");
+ exit(1);
+ }
+ (void) snprintf(homedir, homedir_len, "%s%s", homestr, pwd->pw_dir);
+ (void) snprintf(shell, shell_len, "%s%s", shellstr, pwd->pw_shell);
+ (void) snprintf(username, username_len, "%s%s", userstr, pwd->pw_name);
+
+ /* Pass timezone to executed command. */
+ if (tzenv = getenv("TZ")) {
+ tz_len = strlen(tzenv) + strlen(tzstr) + 1;
+ tz = malloc(tz_len);
+ if (tz != NULL)
+ (void) snprintf(tz, tz_len, "%s%s", tzstr, tzenv);
+ }
+
+ add_to_envinit(homedir);
+ add_to_envinit(shell);
+ add_to_envinit(path);
+ add_to_envinit(username);
+ add_to_envinit(tz);
+
+ if (krb5auth_flag > 0) {
+ int length;
+ char *buffer;
+
+ /*
+ * If we have KRB5CCNAME set, then copy into the child's
+ * environment. This can't really have a fixed position
+ * because `tz' may or may not be set.
+ */
+ if (getenv("KRB5CCNAME")) {
+ length = (int)strlen(getenv("KRB5CCNAME")) +
+ (int)strlen("KRB5CCNAME=") + 1;
+ buffer = (char *)malloc(length);
+
+ if (buffer) {
+ (void) snprintf(buffer, length, "KRB5CCNAME=%s",
+ getenv("KRB5CCNAME"));
+ add_to_envinit(buffer);
+ }
+ } {
+ /* These two are covered by ADDRPAD */
+ length = strlen(inet_ntoa(localaddr.sin_addr)) + 1 +
+ strlen("KRB5LOCALADDR=");
+ (void) snprintf(local_addr, length, "KRB5LOCALADDR=%s",
+ inet_ntoa(localaddr.sin_addr));
+ add_to_envinit(local_addr);
+
+ length = strlen(inet_ntoa(sin->sin_addr)) + 1 +
+ strlen("KRB5REMOTEADDR=");
+ (void) snprintf(remote_addr, length,
+ "KRB5REMOTEADDR=%s", inet_ntoa(sin->sin_addr));
+ add_to_envinit(remote_addr);
+ }
+
+ /*
+ * If we do anything else, make sure there is
+ * space in the array.
+ */
+ for (cnt = 0; cnt < num_env; cnt++) {
+ char *buf;
+
+ if (getenv(save_env[cnt])) {
+ length = (int)strlen(getenv(save_env[cnt])) +
+ (int)strlen(save_env[cnt]) + 2;
+
+ buf = (char *)malloc(length);
+ if (buf) {
+ (void) snprintf(buf, length, "%s=%s",
+ save_env[cnt],
+ getenv(save_env[cnt]));
+ add_to_envinit(buf);
+ }
+ }
+ }
+
+ }
+
+ /*
+ * add PAM environment variables set by modules
+ * -- only allowed 16 (PAM_ENV_ELIM)
+ * -- check to see if the environment variable is legal
+ */
+ if ((pam_env = pam_getenvlist(pamh)) != 0) {
+ while (pam_env[idx] != 0) {
+ if (idx < PAM_ENV_ELIM &&
+ legalenvvar(pam_env[idx])) {
+ add_to_envinit(pam_env[idx]);
+ }
+ idx++;
+ }
+ }
+
+ (void) pam_end(pamh, PAM_SUCCESS);
+
+ /*
+ * Pick up locale environment variables, if any.
+ */
+ lenvp = renvp;
+ while (*lenvp != NULL) {
+ int index;
+
+ for (index = 0; localeenv[index] != NULL; index++)
+ /*
+ * locale_envmatch() returns 1 if
+ * *lenvp is localenev[index] and valid.
+ */
+ if (locale_envmatch(localeenv[index], *lenvp)) {
+ add_to_envinit(*lenvp);
+ break;
+ }
+
+ lenvp++;
+ }
+
+ cp = strrchr(pwd->pw_shell, '/');
+ if (cp != NULL)
+ cp++;
+ else
+ cp = pwd->pw_shell;
+ /*
+ * rdist has been moved to /usr/bin, so /usr/ucb/rdist might not
+ * be present on a system. So if it doesn't exist we fall back
+ * and try for it in /usr/bin. We take care to match the space
+ * after the name because the only purpose of this is to protect
+ * the internal call from old rdist's, not humans who type
+ * "rsh foo /usr/ucb/rdist".
+ */
+#define RDIST_PROG_NAME "/usr/ucb/rdist -Server"
+ if (strncmp(cmdbuf, RDIST_PROG_NAME, strlen(RDIST_PROG_NAME)) == 0) {
+ if (stat("/usr/ucb/rdist", &statb) != 0) {
+ (void) strncpy(cmdbuf + 5, "bin", 3);
+ }
+ }
+
+#ifdef DEBUG
+ syslog(LOG_NOTICE, "rshd: cmdbuf = %s", cmdbuf);
+ if (do_encrypt)
+ syslog(LOG_NOTICE, "rshd: cmd to be exec'ed = %s",
+ ((char *)cmdbuf + 3));
+#endif /* DEBUG */
+
+ if (do_encrypt && (strncmp(cmdbuf, "-x ", 3) == 0)) {
+ (void) execle(pwd->pw_shell, cp, "-c", (char *)cmdbuf + 3,
+ NULL, envinit);
+ } else {
+ (void) execle(pwd->pw_shell, cp, "-c", cmdbuf, NULL,
+ envinit);
+ }
+
+ perror(pwd->pw_shell);
+ exit(1);
+
+signout:
+ if (ccache)
+ (void) pam_close_session(pamh, 0);
+ ccache = NULL;
+ (void) pam_end(pamh, PAM_ABORT);
+ exit(1);
+}
+
+static void
+getstr(fd, buf, cnt, err)
+ int fd;
+ char *buf;
+ int cnt;
+ char *err;
+{
+ char c;
+
+ do {
+ if (read(fd, &c, 1) != 1)
+ exit(1);
+ if (cnt-- == 0) {
+ error("%s too long\n", err);
+ exit(1);
+ }
+ *buf++ = c;
+ } while (c != 0);
+}
+
+/*PRINTFLIKE1*/
+static void
+error(char *fmt, ...)
+{
+ va_list ap;
+ char buf[RSHD_BUFSIZ];
+
+ buf[0] = 1;
+ va_start(ap, fmt);
+ (void) vsnprintf(&buf[1], sizeof (buf) - 1, fmt, ap);
+ va_end(ap);
+ (void) write(STDERR_FILENO, buf, strlen(buf));
+}
+
+static char *illegal[] = {
+ "SHELL=",
+ "HOME=",
+ "LOGNAME=",
+#ifndef NO_MAIL
+ "MAIL=",
+#endif
+ "CDPATH=",
+ "IFS=",
+ "PATH=",
+ "USER=",
+ "TZ=",
+ 0
+};
+
+/*
+ * legalenvvar - can PAM modules insert this environmental variable?
+ */
+
+static int
+legalenvvar(char *s)
+{
+ register char **p;
+
+ for (p = illegal; *p; p++)
+ if (strncmp(s, *p, strlen(*p)) == 0)
+ return (0);
+
+ if (s[0] == 'L' && s[1] == 'D' && s[2] == '_')
+ return (0);
+
+ return (1);
+}
+
+/*
+ * Add a string to the environment of the new process.
+ */
+
+static void
+add_to_envinit(char *string)
+{
+ /*
+ * Reserve space for 2 * 8 = 16 environment entries initially which
+ * should be enough to avoid reallocation of "envinit" in most cases.
+ */
+ static int size = 8;
+ static int index = 0;
+
+ if (string == NULL)
+ return;
+
+ if ((envinit == NULL) || (index == size)) {
+ size *= 2;
+ envinit = realloc(envinit, (size + 1) * sizeof (char *));
+ if (envinit == NULL) {
+ perror("malloc");
+ exit(1);
+ }
+ }
+
+ envinit[index++] = string;
+ envinit[index] = NULL;
+}
+
+/*
+ * Check if lenv and penv matches or not.
+ */
+static int
+locale_envmatch(char *lenv, char *penv)
+{
+ while ((*lenv == *penv) && (*lenv != '\0') && (*penv != '=')) {
+ lenv++;
+ penv++;
+ }
+
+ /*
+ * '/' is eliminated for security reason.
+ */
+ return ((*lenv == '\0' && *penv == '=' && *(penv + 1) != '/'));
+}
+
+#ifndef KRB_SENDAUTH_VLEN
+#define KRB_SENDAUTH_VLEN 8 /* length for version strings */
+#endif
+
+/* MUST be KRB_SENDAUTH_VLEN chars */
+#define KRB_SENDAUTH_VERS "AUTHV0.1"
+#define SIZEOF_INADDR sizeof (struct in_addr)
+
+static krb5_error_code
+recvauth(int netf, int *valid_checksum)
+{
+ krb5_auth_context auth_context = NULL;
+ krb5_error_code status;
+ struct sockaddr_in laddr;
+ int len;
+ krb5_data inbuf;
+ krb5_authenticator *authenticator;
+ krb5_ticket *ticket;
+ krb5_rcache rcache;
+ krb5_data version;
+ krb5_encrypt_block eblock; /* eblock for encrypt/decrypt */
+ krb5_data desinbuf;
+ krb5_data desoutbuf;
+ char des_inbuf[2 * RSHD_BUFSIZ];
+ /* needs to be > largest read size */
+ char des_outbuf[2 * RSHD_BUFSIZ + 4];
+ /* needs to be > largest write size */
+
+ *valid_checksum = 0;
+ len = sizeof (laddr);
+
+ if (getsockname(netf, (struct sockaddr *)&laddr, &len)) {
+ exit(1);
+ }
+
+ if (status = krb5_auth_con_init(bsd_context, &auth_context))
+ return (status);
+
+ if (status = krb5_auth_con_genaddrs(bsd_context, auth_context, netf,
+ KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR))
+ return (status);
+
+ status = krb5_auth_con_getrcache(bsd_context, auth_context, &rcache);
+ if (status)
+ return (status);
+
+ if (!rcache) {
+ krb5_principal server;
+
+ status = krb5_sname_to_principal(bsd_context, 0, 0,
+ KRB5_NT_SRV_HST, &server);
+ if (status)
+ return (status);
+
+ status = krb5_get_server_rcache(bsd_context,
+ krb5_princ_component(bsd_context, server, 0),
+ &rcache);
+ krb5_free_principal(bsd_context, server);
+ if (status)
+ return (status);
+
+ status = krb5_auth_con_setrcache(bsd_context, auth_context,
+ rcache);
+ if (status)
+ return (status);
+ }
+
+ status = krb5_recvauth_version(bsd_context, &auth_context, &netf,
+ NULL, /* Specify daemon principal */
+ 0, /* no flags */
+ keytab, /* normally NULL to use v5srvtab */
+ &ticket, /* return ticket */
+ &version); /* application version string */
+
+
+ if (status) {
+ getstr(netf, locuser, sizeof (locuser), "locuser");
+ getstr(netf, cmdbuf, sizeof (cmdbuf), "command");
+ getstr(netf, remuser, sizeof (locuser), "remuser");
+ return (status);
+ }
+ getstr(netf, locuser, sizeof (locuser), "locuser");
+ getstr(netf, cmdbuf, sizeof (cmdbuf), "command");
+
+ /* Must be V5 */
+
+ kcmd_protocol = KCMD_UNKNOWN_PROTOCOL;
+ if (version.length != 9 || version.data == NULL) {
+ syslog(LOG_ERR, "bad application version length");
+ error(gettext("bad application version length\n"));
+ exit(1);
+ }
+ if (strncmp(version.data, "KCMDV0.1", 9) == 0) {
+ kcmd_protocol = KCMD_OLD_PROTOCOL;
+ } else if (strncmp(version.data, "KCMDV0.2", 9) == 0) {
+ kcmd_protocol = KCMD_NEW_PROTOCOL;
+ } else {
+ syslog(LOG_ERR, "Unrecognized KCMD protocol (%s)",
+ (char *)version.data);
+ error(gettext("Unrecognized KCMD protocol (%s)"),
+ (char *)version.data);
+ exit(1);
+ }
+ getstr(netf, remuser, sizeof (locuser), "remuser");
+
+ if ((status = krb5_unparse_name(bsd_context, ticket->enc_part2->client,
+ &kremuser)))
+ return (status);
+
+ if ((status = krb5_copy_principal(bsd_context,
+ ticket->enc_part2->client, &client)))
+ return (status);
+
+
+ if (checksum_required && (kcmd_protocol == KCMD_OLD_PROTOCOL)) {
+ if ((status = krb5_auth_con_getauthenticator(bsd_context,
+ auth_context, &authenticator)))
+ return (status);
+
+ if (authenticator->checksum && checksum_required) {
+ struct sockaddr_in adr;
+ int adr_length = sizeof (adr);
+ int chksumsize = strlen(cmdbuf) + strlen(locuser) + 32;
+ krb5_data input;
+ krb5_keyblock key;
+
+ char *chksumbuf = (char *)malloc(chksumsize);
+
+ if (chksumbuf == 0)
+ goto error_cleanup;
+ if (getsockname(netf, (struct sockaddr *)&adr,
+ &adr_length) != 0)
+ goto error_cleanup;
+
+ (void) snprintf(chksumbuf, chksumsize, "%u:",
+ ntohs(adr.sin_port));
+ if (strlcat(chksumbuf, cmdbuf,
+ chksumsize) >= chksumsize) {
+ syslog(LOG_ERR, "cmd buffer too long.");
+ free(chksumbuf);
+ return (-1);
+ }
+ if (strlcat(chksumbuf, locuser,
+ chksumsize) >= chksumsize) {
+ syslog(LOG_ERR, "locuser too long.");
+ free(chksumbuf);
+ return (-1);
+ }
+
+ input.data = chksumbuf;
+ input.length = strlen(chksumbuf);
+ key.magic = ticket->enc_part2->session->magic;
+ key.enctype = ticket->enc_part2->session->enctype;
+ key.contents = ticket->enc_part2->session->contents;
+ key.length = ticket->enc_part2->session->length;
+
+ status = krb5_c_verify_checksum(bsd_context,
+ &key, 0, &input, authenticator->checksum,
+ (unsigned int *)valid_checksum);
+
+ if (status == 0 && *valid_checksum == 0)
+ status = KRB5KRB_AP_ERR_BAD_INTEGRITY;
+error_cleanup:
+ if (chksumbuf)
+ krb5_xfree(chksumbuf);
+ if (status) {
+ krb5_free_authenticator(bsd_context,
+ authenticator);
+ return (status);
+ }
+ }
+ krb5_free_authenticator(bsd_context, authenticator);
+ }
+
+
+ if ((strncmp(cmdbuf, "-x ", 3) == 0)) {
+ if (krb5_privacy_allowed()) {
+ do_encrypt = 1;
+ } else {
+ syslog(LOG_ERR, "rshd: Encryption not supported");
+ error("rshd: Encryption not supported. \n");
+ exit(2);
+ }
+
+ status = krb5_auth_con_getremotesubkey(bsd_context,
+ auth_context,
+ &sessionkey);
+ if (status) {
+ syslog(LOG_ERR, "Error getting KRB5 session subkey");
+ error(gettext("Error getting KRB5 session subkey"));
+ exit(1);
+ }
+ /*
+ * The "new" protocol requires that a subkey be sent.
+ */
+ if (sessionkey == NULL && kcmd_protocol == KCMD_NEW_PROTOCOL) {
+ syslog(LOG_ERR, "No KRB5 session subkey sent");
+ error(gettext("No KRB5 session subkey sent"));
+ exit(1);
+ }
+ /*
+ * The "old" protocol does not permit an authenticator subkey.
+ * The key is taken from the ticket instead (see below).
+ */
+ if (sessionkey != NULL && kcmd_protocol == KCMD_OLD_PROTOCOL) {
+ syslog(LOG_ERR, "KRB5 session subkey not permitted "
+ "with old KCMD protocol");
+ error(gettext("KRB5 session subkey not permitted "
+ "with old KCMD protocol"));
+ exit(1);
+ }
+ /*
+ * If no key at this point, use the session key from
+ * the ticket.
+ */
+ if (sessionkey == NULL) {
+ /*
+ * Save the session key so we can configure the crypto
+ * module later.
+ */
+ status = krb5_copy_keyblock(bsd_context,
+ ticket->enc_part2->session,
+ &sessionkey);
+ if (status) {
+ syslog(LOG_ERR, "krb5_copy_keyblock failed");
+ error(gettext("krb5_copy_keyblock failed"));
+ exit(1);
+ }
+ }
+ /*
+ * If session key still cannot be found, we must
+ * exit because encryption is required here
+ * when encr_flag (-x) is set.
+ */
+ if (sessionkey == NULL) {
+ syslog(LOG_ERR, "Could not find an encryption key");
+ error(gettext("Could not find an encryption key"));
+ exit(1);
+ }
+
+ /*
+ * Initialize parameters/buffers for desread & deswrite here.
+ */
+ desinbuf.data = des_inbuf;
+ desoutbuf.data = des_outbuf;
+ desinbuf.length = sizeof (des_inbuf);
+ desoutbuf.length = sizeof (des_outbuf);
+
+ eblock.crypto_entry = sessionkey->enctype;
+ eblock.key = (krb5_keyblock *)sessionkey;
+
+ init_encrypt(do_encrypt, bsd_context, kcmd_protocol,
+ &desinbuf, &desoutbuf, SERVER, &eblock);
+ }
+
+ ticket->enc_part2->session = 0;
+
+ if ((status = krb5_read_message(bsd_context, (krb5_pointer) & netf,
+ &inbuf))) {
+ error(gettext("Error reading message: %s\n"),
+ error_message(status));
+ exit(1);
+ }
+
+ if (inbuf.length) {
+ /* Forwarding being done, read creds */
+ if ((status = rd_and_store_for_creds(bsd_context,
+ auth_context, &inbuf, ticket, locuser,
+ &ccache))) {
+ error("Can't get forwarded credentials: %s\n",
+ error_message(status));
+ exit(1);
+ }
+
+ }
+ krb5_free_ticket(bsd_context, ticket);
+ return (0);
+}
+
+static void
+usage(void)
+{
+ (void) fprintf(stderr, gettext("%s: rshd [-k5eciU] "
+ "[-P path] [-M realm] [-s tos] "
+#ifdef DEBUG
+ "[-D port] "
+#endif /* DEBUG */
+ "[-S keytab]"), gettext("usage"));
+
+ syslog(LOG_ERR, "%s: rshd [-k5eciU] [-P path] [-M realm] [-s tos] "
+#ifdef DEBUG
+ "[-D port] "
+#endif /* DEBUG */
+ "[-S keytab]", gettext("usage"));
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.rwhod.c b/usr/src/cmd/cmd-inet/usr.sbin/in.rwhod.c
new file mode 100644
index 0000000000..4641131900
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.rwhod.c
@@ -0,0 +1,777 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * Portions of this source code were derived from Berkeley 4.3 BSD
+ * under license from the Regents of the University of California.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/file.h>
+#include <sys/loadavg.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+
+#include <stdio.h>
+#include <signal.h>
+#include <errno.h>
+#include <utmpx.h>
+#include <ctype.h>
+#include <netdb.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/isa_defs.h> /* for ENDIAN defines */
+#include <arpa/inet.h>
+#include <protocols/rwhod.h>
+
+#include <strings.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+/*
+ * This version of Berkeley's rwhod has been modified to use IP multicast
+ * datagrams, under control of a new command-line option:
+ *
+ * rwhod -m causes rwhod to use IP multicast (instead of
+ * broadcast or unicast) on all interfaces that have
+ * the IFF_MULTICAST flag set in their "ifnet" structs
+ * (excluding the loopback interface). The multicast
+ * reports are sent with a time-to-live of 1, to prevent
+ * forwarding beyond the directly-connected subnet(s).
+ *
+ * rwhod -m <ttl> causes rwhod to send IP multicast datagrams with a
+ * time-to-live of <ttl>, via a SINGLE interface rather
+ * than all interfaces. <ttl> must be between 0 and
+ * MAX_MULTICAST_SCOPE, defined below. Note that "-m 1"
+ * is different than "-m", in that "-m 1" specifies
+ * transmission on one interface only.
+ *
+ * When "-m" is used without a <ttl> argument, the program accepts multicast
+ * rwhod reports from all multicast-capable interfaces. If a <ttl> argument
+ * is given, it accepts multicast reports from only one interface, the one
+ * on which reports are sent (which may be controlled via the host's routing
+ * table). Regardless of the "-m" option, the program accepts broadcast or
+ * unicast reports from all interfaces. Thus, this program will hear the
+ * reports of old, non-multicasting rwhods, but, if multicasting is used,
+ * those old rwhods won't hear the reports generated by this program.
+ *
+ * -- Steve Deering, Stanford University, February 1989
+ */
+
+#define NO_MULTICAST 0 /* multicast modes */
+#define PER_INTERFACE_MULTICAST 1
+#define SCOPED_MULTICAST 2
+
+#define MAX_MULTICAST_SCOPE 32 /* "site-wide", by convention */
+
+#define INADDR_WHOD_GROUP (ulong_t)0xe0000103 /* 224.0.1.3 */
+ /* (belongs in protocols/rwhod.h) */
+
+static int multicast_mode = NO_MULTICAST;
+static int multicast_scope;
+static struct sockaddr_in multicast_addr = { AF_INET };
+
+
+/*
+ * Alarm interval. Don't forget to change the down time check in ruptime
+ * if this is changed.
+ */
+#define AL_INTERVAL (3 * 60)
+
+static struct sockaddr_in sin = { AF_INET };
+
+static char myname[MAXHOSTNAMELEN];
+
+/*
+ * We communicate with each neighbor in
+ * a list constructed at the time we're
+ * started up. Neighbors are currently
+ * directly connected via a hardware interface.
+ */
+struct neighbor {
+ struct neighbor *n_next;
+ char *n_name; /* interface name */
+ char *n_addr; /* who to send to */
+ int n_addrlen; /* size of address */
+ ulong_t n_subnet; /* AF_INET subnet */
+ uint_t n_flags; /* should forward?, interface flags */
+};
+
+static struct neighbor *neighbors;
+static struct whod mywd;
+static struct servent *sp;
+static int s;
+
+#define WHDRSIZE (sizeof (mywd) - sizeof (mywd.wd_we))
+#define RWHODIR "/var/spool/rwho"
+
+static void onalrm(void);
+static void getkmem(void);
+static boolean_t configure(int);
+static int verify(const struct whod *);
+
+void
+main(int argc, char *argv[])
+{
+ struct sockaddr_in from;
+ struct stat st;
+ char path[64];
+ struct hostent *hp;
+ int on = 1;
+ char *cp;
+ struct stat sb;
+
+ if (getuid()) {
+ (void) fprintf(stderr, "in.rwhod: not super user\n");
+ exit(1);
+ }
+ sp = getservbyname("who", "udp");
+ if (sp == NULL) {
+ (void) fprintf(stderr, "in.rwhod: udp/who: unknown service\n");
+ exit(1);
+ }
+ argv++;
+ argc--;
+ while (argc > 0 && *argv[0] == '-') {
+ if (strcmp(*argv, "-m") == 0) {
+ if (argc > 1 && isdigit(*(argv + 1)[0])) {
+ argv++;
+ argc--;
+ multicast_mode = SCOPED_MULTICAST;
+ multicast_scope = atoi(*argv);
+ if (multicast_scope > MAX_MULTICAST_SCOPE) {
+ (void) fprintf(stderr,
+ "in.rwhod: "
+ "ttl must not exceed %u\n",
+ MAX_MULTICAST_SCOPE);
+ exit(1);
+ }
+ } else {
+ multicast_mode = PER_INTERFACE_MULTICAST;
+ }
+ } else {
+ goto usage;
+ }
+ argv++;
+ argc--;
+ }
+ if (argc > 0)
+ goto usage;
+ if (chdir(RWHODIR) < 0) {
+ perror(RWHODIR);
+ exit(1);
+ }
+#ifndef DEBUG
+ if (fork())
+ exit(0);
+ /* CSTYLED */
+ {
+ (void) close(0);
+ (void) close(1);
+ (void) close(2);
+ (void) open("/", 0);
+ (void) dup2(0, 1);
+ (void) dup2(0, 2);
+ (void) setsid();
+ }
+#endif
+ (void) sigset(SIGHUP, (void (*)())getkmem);
+ openlog("in.rwhod", LOG_PID, LOG_DAEMON);
+ /*
+ * Establish host name as returned by system.
+ */
+ if (gethostname(myname, sizeof (myname) - 1) < 0) {
+ syslog(LOG_ERR, "main: gethostname: %m");
+ exit(1);
+ }
+ if ((cp = index(myname, '.')) != NULL)
+ *cp = '\0';
+ (void) strlcpy(mywd.wd_hostname, myname, sizeof (mywd.wd_hostname));
+
+ if (stat(UTMPX_FILE, &sb) < 0) {
+ syslog(LOG_ERR, "main: stat: %s: %m", UTMPX_FILE);
+ exit(1);
+ }
+ getkmem();
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ syslog(LOG_ERR, "main: socket: %m");
+ exit(1);
+ }
+ if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof (on)) < 0) {
+ syslog(LOG_ERR, "main: setsockopt SO_BROADCAST: %m");
+ exit(1);
+ }
+ hp = gethostbyname(myname);
+ if (hp == NULL) {
+ syslog(LOG_ERR, "main: %s: don't know my own name\n", myname);
+ exit(1);
+ }
+ sin.sin_family = hp->h_addrtype;
+ sin.sin_port = sp->s_port;
+ if (bind(s, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
+ syslog(LOG_ERR, "main: bind: %m");
+ exit(1);
+ }
+ if (!configure(s))
+ exit(1);
+ (void) sigset(SIGALRM, (void (*)())onalrm);
+ onalrm();
+ for (;;) {
+ struct whod wd;
+ int cc, whod;
+ socklen_t len = sizeof (from);
+
+ cc = recvfrom(s, &wd, sizeof (struct whod), 0,
+ (struct sockaddr *)&from, &len);
+ if (cc <= 0) {
+ if (cc < 0 && errno != EINTR)
+ syslog(LOG_WARNING, "main: recvfrom: %m");
+ continue;
+ }
+ if (from.sin_port != sp->s_port) {
+ syslog(LOG_WARNING, "main: %d: bad from port",
+ ntohs(from.sin_port));
+ continue;
+ }
+#ifdef notdef
+ if (gethostbyname(wd.wd_hostname) == 0) {
+ syslog(LOG_WARNING, "main: %s: unknown host",
+ wd.wd_hostname);
+ continue;
+ }
+#endif
+ if (wd.wd_vers != WHODVERSION)
+ continue;
+ if (wd.wd_type != WHODTYPE_STATUS)
+ continue;
+ if (!verify(&wd)) {
+ syslog(LOG_WARNING, "main: malformed host name from %x",
+ from.sin_addr.s_addr);
+ continue;
+ }
+ (void) sprintf(path, "whod.%s", wd.wd_hostname);
+ /*
+ * Rather than truncating and growing the file each time,
+ * use ftruncate if size is less than previous size.
+ */
+ whod = open(path, O_WRONLY | O_CREAT, 0644);
+ if (whod < 0) {
+ syslog(LOG_WARNING, "main: open: %s: %m", path);
+ continue;
+ }
+#if defined(_LITTLE_ENDIAN)
+ /* CSTYLED */
+ {
+ int i, n = (cc - WHDRSIZE)/sizeof (struct whoent);
+ struct whoent *we;
+
+ /* undo header byte swapping before writing to file */
+ wd.wd_sendtime = ntohl(wd.wd_sendtime);
+ for (i = 0; i < 3; i++)
+ wd.wd_loadav[i] = ntohl(wd.wd_loadav[i]);
+ wd.wd_boottime = ntohl(wd.wd_boottime);
+ we = wd.wd_we;
+ for (i = 0; i < n; i++) {
+ we->we_idle = ntohl(we->we_idle);
+ we->we_utmp.out_time =
+ ntohl(we->we_utmp.out_time);
+ we++;
+ }
+ }
+#endif
+ (void) time((time_t *)&wd.wd_recvtime);
+ (void) write(whod, &wd, cc);
+ if (fstat(whod, &st) < 0 || st.st_size > cc)
+ (void) ftruncate(whod, cc);
+ (void) close(whod);
+ }
+ /* NOTREACHED */
+usage:
+ (void) fprintf(stderr, "usage: in.rwhod [ -m [ ttl ] ]\n");
+ exit(1);
+}
+
+/*
+ * Check out host name for unprintables
+ * and other funnies before allowing a file
+ * to be created. Sorry, but blanks aren't allowed.
+ */
+static int
+verify(const struct whod *wd)
+{
+ int size = 0;
+ const char *name = wd->wd_hostname;
+
+ /*
+ * We shouldn't assume the name is NUL terminated, so bound the
+ * checks at the size of the whod structures wd_hostname field.
+ */
+ while ((size < sizeof (wd->wd_hostname)) &&
+ (*name != '\0')) {
+ if (*name == '/' || !isascii(*name) ||
+ !(isalnum(*name) || ispunct(*name)))
+ return (0);
+ name++, size++;
+ }
+ /*
+ * Fail the verification if NULL name or it wasn't NUL terminated.
+ */
+ return ((size > 0) && (size < sizeof (wd->wd_hostname)));
+}
+
+static int utmpxtime;
+static int utmpxent;
+static int alarmcount;
+struct utmpx *utmpx;
+
+static void
+onalrm(void)
+{
+ int i;
+ struct stat stb;
+ int utmpxsize = 0;
+ int entries;
+ struct utmpx *utp;
+ struct utmpx *utmpxbegin;
+ struct whoent *we = mywd.wd_we, *wlast;
+ int cc, cnt;
+ double avenrun[3];
+
+ time_t now = time(0);
+ struct neighbor *np;
+
+ if (alarmcount % 10 == 0)
+ getkmem();
+ alarmcount++;
+ (void) stat(UTMPX_FILE, &stb);
+ entries = stb.st_size / sizeof (struct futmpx);
+ if ((stb.st_mtime != utmpxtime) || (entries > utmpxent)) {
+ utmpxtime = stb.st_mtime;
+ if (entries > utmpxent) {
+ utmpxent = entries;
+ utmpxsize = utmpxent * sizeof (struct utmpx);
+ utmpx = realloc(utmpx, utmpxsize);
+ if (utmpx == NULL) {
+ syslog(LOG_ERR, "onalrm: realloc: %m");
+ utmpxsize = 0;
+ goto done;
+ }
+ }
+ utmpxbegin = utmpx;
+ setutxent();
+ cnt = 0;
+ while (cnt++ < utmpxent && (utp = getutxent()) != NULL)
+ (void) memcpy(utmpxbegin++, utp, sizeof (struct utmpx));
+ endutxent();
+ wlast = &mywd.wd_we[1024 / sizeof (struct whoent) - 1];
+ for (i = 0; i < utmpxent; i++) {
+ if (utmpx[i].ut_name[0] &&
+ utmpx[i].ut_type == USER_PROCESS) {
+ /*
+ * XXX - utmpx name and line lengths should
+ * be here
+ */
+ bcopy(utmpx[i].ut_line, we->we_utmp.out_line,
+ sizeof (we->we_utmp.out_line));
+ bcopy(utmpx[i].ut_name, we->we_utmp.out_name,
+ sizeof (we->we_utmp.out_name));
+ we->we_utmp.out_time =
+ htonl(utmpx[i].ut_xtime);
+ if (we >= wlast)
+ break;
+ we++;
+ }
+ }
+ utmpxent = we - mywd.wd_we;
+ }
+
+ /*
+ * The test on utmpxent looks silly---after all, if no one is
+ * logged on, why worry about efficiency?---but is useful on
+ * (e.g.) compute servers.
+ */
+ if (utmpxent > 0 && chdir("/dev") == -1) {
+ syslog(LOG_ERR, "onalrm: chdir /dev: %m");
+ exit(1);
+ }
+ we = mywd.wd_we;
+ for (i = 0; i < utmpxent; i++) {
+ if (stat(we->we_utmp.out_line, &stb) >= 0)
+ we->we_idle = htonl(now - stb.st_atime);
+ we++;
+ }
+ if (getloadavg(avenrun, 3) == -1) {
+ syslog(LOG_ERR, "onalrm: getloadavg: %m");
+ exit(1);
+ }
+
+ for (i = 0; i < 3; i++)
+ mywd.wd_loadav[i] = htonl((ulong_t)(avenrun[i] * 100));
+ cc = (char *)we - (char *)&mywd;
+ mywd.wd_sendtime = htonl(time(0));
+ mywd.wd_vers = WHODVERSION;
+ mywd.wd_type = WHODTYPE_STATUS;
+ if (multicast_mode == SCOPED_MULTICAST) {
+ (void) sendto(s, &mywd, cc, 0,
+ (struct sockaddr *)&multicast_addr,
+ sizeof (multicast_addr));
+ } else for (np = neighbors; np != NULL; np = np->n_next) {
+ if (multicast_mode == PER_INTERFACE_MULTICAST &&
+ np->n_flags & IFF_MULTICAST) {
+ /*
+ * Select the outgoing interface for the multicast.
+ */
+ if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF,
+ &(((struct sockaddr_in *)np->n_addr)->sin_addr),
+ sizeof (struct in_addr)) < 0) {
+ syslog(LOG_ERR,
+ "onalrm: setsockopt IP_MULTICAST_IF: %m");
+ exit(1);
+ }
+ (void) sendto(s, &mywd, cc, 0,
+ (struct sockaddr *)&multicast_addr,
+ sizeof (multicast_addr));
+ } else {
+ (void) sendto(s, &mywd, cc, 0,
+ (struct sockaddr *)np->n_addr, np->n_addrlen);
+ }
+ }
+ if (utmpxent > 0 && chdir(RWHODIR) == -1) {
+ syslog(LOG_ERR, "onalrm: chdir %s: %m", RWHODIR);
+ exit(1);
+ }
+done:
+ (void) alarm(AL_INTERVAL);
+}
+
+static void
+getkmem(void)
+{
+ struct utmpx *utmpx, utmpx_id;
+
+ utmpx_id.ut_type = BOOT_TIME;
+ if ((utmpx = getutxid(&utmpx_id)) != NULL)
+ mywd.wd_boottime = utmpx->ut_xtime;
+ endutxent();
+ mywd.wd_boottime = htonl(mywd.wd_boottime);
+}
+
+/*
+ * Figure out device configuration and select
+ * networks which deserve status information.
+ */
+static boolean_t
+configure(int s)
+{
+ char *buf;
+ struct ifconf ifc;
+ struct ifreq ifreq, *ifr;
+ struct sockaddr_in *sin;
+ struct neighbor *np;
+ struct neighbor *np2;
+ int n;
+ int numifs;
+ unsigned bufsize;
+
+ if (multicast_mode == SCOPED_MULTICAST) {
+ struct ip_mreq mreq;
+ unsigned char ttl;
+
+ mreq.imr_multiaddr.s_addr = htonl(INADDR_WHOD_GROUP);
+ mreq.imr_interface.s_addr = htonl(INADDR_ANY);
+ if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq,
+ sizeof (mreq)) < 0) {
+ syslog(LOG_ERR,
+ "configure: setsockopt IP_ADD_MEMBERSHIP: %m");
+ return (B_FALSE);
+ }
+ ttl = multicast_scope;
+ if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, &ttl,
+ sizeof (ttl)) < 0) {
+ syslog(LOG_ERR,
+ "configure: setsockopt IP_MULTICAST_TTL: %m");
+ return (B_FALSE);
+ }
+ multicast_addr.sin_addr.s_addr = htonl(INADDR_WHOD_GROUP);
+ multicast_addr.sin_port = sp->s_port;
+ return (B_TRUE);
+ }
+
+ if (ioctl(s, SIOCGIFNUM, (char *)&numifs) < 0) {
+ syslog(LOG_ERR, "configure: ioctl SIOCGIFNUM: %m");
+ return (B_FALSE);
+ }
+ bufsize = numifs * sizeof (struct ifreq);
+ buf = malloc(bufsize);
+ if (buf == NULL) {
+ syslog(LOG_ERR, "configure: malloc: %m");
+ return (B_FALSE);
+ }
+ ifc.ifc_len = bufsize;
+ ifc.ifc_buf = buf;
+ if (ioctl(s, SIOCGIFCONF, (char *)&ifc) < 0) {
+ syslog(LOG_ERR,
+ "configure: ioctl (get interface configuration): %m");
+ (void) free(buf);
+ return (B_FALSE);
+ }
+ ifr = ifc.ifc_req;
+ for (n = ifc.ifc_len / sizeof (struct ifreq); n > 0; n--, ifr++) {
+ /* Skip all logical interfaces */
+ if (index(ifr->ifr_name, ':') != NULL)
+ continue;
+
+ for (np = neighbors; np != NULL; np = np->n_next) {
+ if (np->n_name &&
+ strcmp(ifr->ifr_name, np->n_name) == 0)
+ break;
+ }
+ if (np != NULL)
+ continue;
+ ifreq = *ifr;
+ np = (struct neighbor *)malloc(sizeof (*np));
+ if (np == NULL)
+ continue;
+ np->n_name = malloc(strlen(ifr->ifr_name) + 1);
+ if (np->n_name == NULL) {
+ free(np);
+ continue;
+ }
+ (void) strcpy(np->n_name, ifr->ifr_name);
+ np->n_addrlen = sizeof (ifr->ifr_addr);
+ np->n_addr = malloc(np->n_addrlen);
+ if (np->n_addr == NULL) {
+ free(np->n_name);
+ free(np);
+ continue;
+ }
+ bcopy(&ifr->ifr_addr, np->n_addr, np->n_addrlen);
+ if (ioctl(s, SIOCGIFFLAGS, (char *)&ifreq) < 0) {
+ syslog(LOG_ERR,
+ "configure: ioctl (get interface flags): %m");
+ free(np->n_addr);
+ free(np->n_name);
+ free(np);
+ continue;
+ }
+ np->n_flags = ifreq.ifr_flags;
+ if (((struct sockaddr_in *)np->n_addr)->sin_family == AF_INET &&
+ ioctl(s, SIOCGIFNETMASK, (char *)&ifreq) >= 0) {
+ sin = (struct sockaddr_in *)np->n_addr;
+
+ np->n_subnet = sin->sin_addr.s_addr &
+ ((struct sockaddr_in *)&ifreq.ifr_addr)->
+ sin_addr.s_addr;
+ }
+ if (multicast_mode == PER_INTERFACE_MULTICAST &&
+ (np->n_flags & IFF_UP) &&
+ (np->n_flags & IFF_MULTICAST) &&
+ !(np->n_flags & IFF_LOOPBACK)) {
+ struct ip_mreq mreq;
+
+ /*
+ * Skip interfaces that have matching subnets i.e.
+ * (addr & netmask) are identical.
+ * Such interfaces are connected to the same
+ * physical wire.
+ */
+ for (np2 = neighbors; np2 != NULL; np2 = np2->n_next) {
+
+ if (!(np->n_flags & IFF_POINTOPOINT) &&
+ !(np2->n_flags & IFF_POINTOPOINT) &&
+ (np->n_subnet == np2->n_subnet)) {
+ free(np->n_addr);
+ free(np->n_name);
+ free(np);
+ break;
+ }
+ }
+ if (np2 != NULL)
+ continue;
+
+ mreq.imr_multiaddr.s_addr = htonl(INADDR_WHOD_GROUP);
+ mreq.imr_interface.s_addr =
+ ((struct sockaddr_in *)np->n_addr)->sin_addr.s_addr;
+ if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq,
+ sizeof (mreq)) < 0) {
+ syslog(LOG_ERR,
+ "configure: "
+ "setsockopt IP_ADD_MEMBERSHIP: %m");
+ free(np->n_addr);
+ free(np->n_name);
+ free(np);
+ continue;
+ }
+ multicast_addr.sin_addr.s_addr =
+ htonl(INADDR_WHOD_GROUP);
+ multicast_addr.sin_port = sp->s_port;
+ np->n_next = neighbors;
+ neighbors = np;
+ continue;
+ }
+ if ((np->n_flags & IFF_UP) == 0 ||
+ (np->n_flags & (IFF_BROADCAST|IFF_POINTOPOINT)) == 0) {
+ free(np->n_addr);
+ free(np->n_name);
+ free(np);
+ continue;
+ }
+ if (np->n_flags & IFF_POINTOPOINT) {
+ if (ioctl(s, SIOCGIFDSTADDR, (char *)&ifreq) < 0) {
+ syslog(LOG_ERR,
+ "configure: ioctl (get dstaddr): %m");
+ free(np->n_addr);
+ free(np->n_name);
+ free(np);
+ continue;
+ }
+ /* we assume addresses are all the same size */
+ bcopy(&ifreq.ifr_dstaddr, np->n_addr, np->n_addrlen);
+ }
+ if (np->n_flags & IFF_BROADCAST) {
+ if (ioctl(s, SIOCGIFBRDADDR, (char *)&ifreq) < 0) {
+ syslog(LOG_ERR,
+ "configure: ioctl (get broadaddr): %m");
+ free(np->n_addr);
+ free(np->n_name);
+ free(np);
+ continue;
+ }
+ /* we assume addresses are all the same size */
+ bcopy(&ifreq.ifr_broadaddr, np->n_addr, np->n_addrlen);
+ }
+ /* gag, wish we could get rid of Internet dependencies */
+ sin = (struct sockaddr_in *)np->n_addr;
+ sin->sin_port = sp->s_port;
+
+ /*
+ * Avoid adding duplicate broadcast and pt-pt destinations
+ * to the list.
+ */
+ for (np2 = neighbors; np2 != NULL; np2 = np2->n_next) {
+ struct sockaddr_in *sin2;
+
+ sin2 = (struct sockaddr_in *)np2->n_addr;
+ if (sin2->sin_addr.s_addr == sin->sin_addr.s_addr) {
+ free(np->n_addr);
+ free(np->n_name);
+ free(np);
+ break;
+ }
+ }
+ if (np2 != NULL)
+ continue;
+
+ np->n_next = neighbors;
+ neighbors = np;
+ }
+ (void) free(buf);
+ return (B_TRUE);
+}
+
+#ifdef DEBUG
+static char *interval(uint_t, char *);
+
+/* ARGSUSED */
+static ssize_t
+sendto(int s, const void *buf, size_t cc, int flags, const struct sockaddr *to,
+ socklen_t tolen)
+{
+ struct whod *w = (struct whod *)buf;
+ struct whoent *we;
+ struct sockaddr_in *sin = (struct sockaddr_in *)to;
+ int nsz;
+
+ (void) printf("sendto %x.%d\n", ntohl(sin->sin_addr.s_addr),
+ ntohs(sin->sin_port));
+ (void) printf("hostname %s %s\n", w->wd_hostname,
+ interval(ntohl(w->wd_sendtime) - ntohl(w->wd_boottime), " up"));
+ (void) printf("load %4.2f, %4.2f, %4.2f\n",
+ ntohl(w->wd_loadav[0]) / 100.0, ntohl(w->wd_loadav[1]) / 100.0,
+ ntohl(w->wd_loadav[2]) / 100.0);
+ cc -= WHDRSIZE;
+ for (we = w->wd_we, cc /= sizeof (struct whoent); cc > 0; cc--, we++) {
+ time_t t = ntohl(we->we_utmp.out_time);
+
+ nsz = sizeof (we->we_utmp.out_name);
+ (void) printf("%-*.*s %s:%s %.12s",
+ nsz,
+ nsz,
+ we->we_utmp.out_name,
+ w->wd_hostname,
+ we->we_utmp.out_line,
+ ctime(&t)+4);
+ we->we_idle = ntohl(we->we_idle) / 60;
+ if (we->we_idle) {
+ if (we->we_idle >= 100*60)
+ we->we_idle = 100*60 - 1;
+ if (we->we_idle >= 60)
+ (void) printf(" %2d", we->we_idle / 60);
+ else
+ (void) printf(" ");
+ (void) printf(":%02d", we->we_idle % 60);
+ }
+ (void) printf("\n");
+ }
+ return (0);
+}
+
+static char *
+interval(uint_t time, char *updown)
+{
+ static char resbuf[32];
+ int days, hours, minutes;
+
+ if (time > 3*30*24*60*60) {
+ (void) sprintf(resbuf, " %s ??:??", updown);
+ return (resbuf);
+ }
+ minutes = (time + 59) / 60; /* round to minutes */
+ hours = minutes / 60;
+ minutes %= 60;
+ days = hours / 24;
+ hours %= 24;
+ if (days > 0) {
+ (void) sprintf(resbuf, "%s %2d+%02d:%02d",
+ updown, days, hours, minutes);
+ } else {
+ (void) sprintf(resbuf, "%s %2d:%02d",
+ updown, hours, minutes);
+ }
+ return (resbuf);
+}
+#endif
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.talkd/Makefile b/usr/src/cmd/cmd-inet/usr.sbin/in.talkd/Makefile
new file mode 100644
index 0000000000..2cd82d1018
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.talkd/Makefile
@@ -0,0 +1,61 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+PROG= in.talkd
+MANIFEST= talk.xml
+OBJS= in.talkd.o announce.o process.o table.o print.o
+SRCS= $(OBJS:%.o=%.c)
+
+include ../../../Makefile.cmd
+
+ROOTMANIFESTDIR= $(ROOTSVCNETWORK)
+$(ROOTMANIFEST) := FILEMODE= 444
+
+CFLAGS += $(CCVERBOSE)
+LDLIBS += -lsocket -lnsl
+
+.KEEP_STATE:
+
+.PARALLEL: $(OBJS)
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+ $(LINK.c) $(OBJS) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+install: all $(ROOTUSRSBINPROG) $(ROOTMANIFEST)
+
+check: $(CHKMANIFEST)
+
+clean:
+ $(RM) $(OBJS)
+
+lint: lint_SRCS
+
+include ../../../Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.talkd/announce.c b/usr/src/cmd/cmd-inet/usr.sbin/in.talkd/announce.c
new file mode 100644
index 0000000000..8e71483fa4
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.talkd/announce.c
@@ -0,0 +1,292 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T
+ * All Rights Reserved.
+ */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California.
+ * All Rights Reserved.
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/fcntl.h>
+#include <sys/time.h>
+#include <time.h>
+#include <stdio.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <string.h>
+#include "talkd_impl.h"
+
+static int nofork = 0; /* to be set from the debugger */
+
+static int announce_proc(CTL_MSG *request, char *remote_machine);
+static void print_mesg(FILE *tf, CTL_MSG *request, char *remote_machine);
+
+/*
+ * Because the tty driver insists on attaching a terminal-less
+ * process to any terminal that it writes on, we must fork a child
+ * to protect ourselves.
+ */
+
+int
+announce(CTL_MSG *request, char *remote_machine)
+{
+ pid_t pid, val;
+ int status;
+
+ if (nofork) {
+ return (announce_proc(request, remote_machine));
+ }
+
+ if (pid = fork()) {
+
+ /* we are the parent, so wait for the child */
+ if (pid == (pid_t)-1) {
+ /* the fork failed */
+ return (FAILED);
+ }
+
+ do {
+ val = wait(&status);
+ if (val == (pid_t)-1) {
+ if (errno == EINTR) {
+ continue;
+ } else {
+ /* shouldn't happen */
+ print_error("wait");
+ return (FAILED);
+ }
+ }
+ } while (val != pid);
+
+ if ((status & 0377) > 0) {
+ /* we were killed by some signal */
+ return (FAILED);
+ }
+
+ /* Get the second byte, this is the exit/return code */
+ return ((status>>8)&0377);
+ } else {
+ /* we are the child, go and do it */
+ _exit(announce_proc(request, remote_machine));
+ }
+ /* NOTREACHED */
+}
+
+
+/*
+ * See if the user is accepting messages. If so, announce that
+ * a talk is requested.
+ */
+static int
+announce_proc(CTL_MSG *request, char *remote_machine)
+{
+#define TTY_BUFSZ 32
+ char full_tty[TTY_BUFSZ];
+ FILE *tf;
+ struct stat stbuf;
+ int fd;
+ struct passwd *p;
+
+ (void) snprintf(full_tty, TTY_BUFSZ, "/dev/%s", request->r_tty);
+ p = getpwnam(request->r_name);
+
+ if (p == 0 || access(full_tty, 0) != 0) {
+ return (FAILED);
+ }
+
+ /* fopen uses O_CREAT|O_TRUNC, we don't want that */
+ if ((fd = open(full_tty, O_WRONLY|O_NONBLOCK)) == -1) {
+ return (PERMISSION_DENIED);
+ }
+ /* must be tty */
+ if (!isatty(fd)) {
+ (void) close(fd);
+ return (PERMISSION_DENIED);
+ }
+
+ /*
+ * open gratuitously attaches the talkd to any tty it opens, so
+ * disconnect us from the tty before we catch a signal
+ */
+ (void) setsid();
+
+ if (fstat(fd, &stbuf) < 0 || stbuf.st_uid != p->pw_uid) {
+ (void) close(fd);
+ return (PERMISSION_DENIED);
+ }
+
+ if ((stbuf.st_mode&020) == 0) {
+ (void) close(fd);
+ return (PERMISSION_DENIED);
+ }
+
+ if ((tf = fdopen(fd, "w")) == NULL) {
+ (void) close(fd);
+ return (PERMISSION_DENIED);
+ }
+
+ print_mesg(tf, request, remote_machine);
+ (void) fclose(tf);
+ return (SUCCESS);
+}
+
+#define max(a, b) ((a) > (b) ? (a) : (b))
+#define N_LINES 5
+#define N_CHARS 300
+
+/*
+ * Build a block of characters containing the message.
+ * It is sent blank filled and in a single block to
+ * try to keep the message in one piece if the recipient
+ * is in vi at the time.
+ */
+static void
+print_mesg(FILE *tf, CTL_MSG *request, char *remote_machine)
+{
+ struct timeval clock;
+ struct tm *localclock;
+ char line_buf[N_LINES][N_CHARS];
+ int sizes[N_LINES];
+ char *bptr, *lptr;
+ int i, j, max_size;
+
+ /*
+ * [3 wakeup chars + (lines * max chars/line) +
+ * (lines * strlen("\r\n")) + 1(NUL)].
+ */
+ char big_buf[3 + (N_LINES * (N_CHARS - 1)) + (N_LINES * 2) + 1];
+ /*
+ * ( (length of (request->l_name) - 1(NUL)) *
+ * (strlen("M-") + 1('^') + 1(printable char)) ) + 1(NUL).
+ */
+ char l_username[((NAME_SIZE - 1) * 4) + 1];
+ int len, k;
+
+ i = 0;
+ max_size = 0;
+
+ (void) gettimeofday(&clock, NULL);
+ localclock = localtime(&clock.tv_sec);
+
+ (void) sprintf(line_buf[i], " ");
+
+ sizes[i] = strlen(line_buf[i]);
+ max_size = max(max_size, sizes[i]);
+ i++;
+
+ (void) snprintf(line_buf[i], N_CHARS,
+ "Message from Talk_Daemon@%s at %d:%02d ...", hostname,
+ localclock->tm_hour, localclock->tm_min);
+
+ sizes[i] = strlen(line_buf[i]);
+ max_size = max(max_size, sizes[i]);
+ i++;
+
+ len = (strlen(request->l_name) > NAME_SIZE - 1) ? (NAME_SIZE - 1) :
+ strlen(request->l_name);
+ for (j = 0, k = 0; j < len; j++) {
+ if (!isprint((unsigned char)request->l_name[j])) {
+ char c;
+ if (!isascii((unsigned char)request->l_name[j])) {
+ l_username[k++] = 'M';
+ l_username[k++] = '-';
+ c = toascii(request->l_name[j]);
+ }
+ if (iscntrl((unsigned char)request->l_name[j])) {
+ l_username[k++] = '^';
+ /* add decimal 64 to the control character */
+ c = request->l_name[j] + 0100;
+ }
+ l_username[k++] = c;
+ } else {
+ l_username[k++] = request->l_name[j];
+ }
+ }
+ l_username[k] = '\0';
+
+ (void) snprintf(line_buf[i], N_CHARS,
+ "talk: connection requested by %s@%s.", l_username, remote_machine);
+
+ sizes[i] = strlen(line_buf[i]);
+ max_size = max(max_size, sizes[i]);
+ i++;
+
+ (void) snprintf(line_buf[i], N_CHARS, "talk: respond with: talk %s@%s",
+ l_username, remote_machine);
+
+ sizes[i] = strlen(line_buf[i]);
+ max_size = max(max_size, sizes[i]);
+ i++;
+
+ (void) sprintf(line_buf[i], " ");
+
+ sizes[i] = strlen(line_buf[i]);
+ max_size = max(max_size, sizes[i]);
+ i++;
+
+ bptr = big_buf;
+ *(bptr++) = '\a'; /* send something to wake them up */
+ *(bptr++) = '\r'; /* add a \r in case of raw mode */
+ *(bptr++) = '\n';
+ for (i = 0; i < N_LINES; i++) {
+ /* copy the line into the big buffer */
+ lptr = line_buf[i];
+ while (*lptr != '\0') {
+ *(bptr++) = *(lptr++);
+ }
+
+ /* pad out the rest of the lines with blanks */
+ for (j = sizes[i]; j < max_size; j++) {
+ *(bptr++) = ' ';
+ }
+
+ *(bptr++) = '\r'; /* add a \r in case of raw mode */
+ *(bptr++) = '\n';
+ }
+ *bptr = '\0';
+
+ (void) fputs(big_buf, tf);
+ (void) fflush(tf);
+ (void) setsid();
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.talkd/ctl.h b/usr/src/cmd/cmd-inet/usr.sbin/in.talkd/ctl.h
new file mode 100644
index 0000000000..c10adb105f
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.talkd/ctl.h
@@ -0,0 +1,118 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T
+ * All Rights Reserved.
+ */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California.
+ * All Rights Reserved.
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#ifndef _CTL_H
+#define _CTL_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * ctl.h describes the structure that talk and talkd pass back
+ * and forth.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+#define NAME_SIZE 9
+#define TTY_SIZE 16
+#define HOST_NAME_LENGTH 256
+
+/*
+ * Maximum time an invitation is saved by the talk daemons.
+ */
+#define MAX_LIFE 60
+/*
+ * Time to wait before refreshing invitation should be 10's of seconds less
+ * than MAX_LIFE.
+ */
+#define RING_WAIT 30
+
+/*
+ * The values for type.
+ */
+#define LEAVE_INVITE 0
+#define LOOK_UP 1
+#define DELETE 2
+#define ANNOUNCE 3
+
+/*
+ * The values for answer.
+ */
+#define SUCCESS 0
+#define NOT_HERE 1
+#define FAILED 2
+#define MACHINE_UNKNOWN 3
+#define PERMISSION_DENIED 4
+#define UNKNOWN_REQUEST 5
+
+typedef struct ctl_response CTL_RESPONSE;
+
+struct ctl_response {
+ char type;
+ char answer;
+ int id_num;
+ struct sockaddr_in addr;
+};
+
+typedef struct ctl_msg CTL_MSG;
+
+struct ctl_msg {
+ char type;
+ char l_name[NAME_SIZE];
+ char r_name[NAME_SIZE];
+ int id_num;
+ pid_t pid;
+ char r_tty[TTY_SIZE];
+ struct sockaddr_in addr;
+ struct sockaddr_in ctl_addr;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _CTL_H */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.talkd/in.talkd.c b/usr/src/cmd/cmd-inet/usr.sbin/in.talkd/in.talkd.c
new file mode 100644
index 0000000000..069fbcfe8d
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.talkd/in.talkd.c
@@ -0,0 +1,184 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T
+ * All Rights Reserved.
+ */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California.
+ * All Rights Reserved.
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Invoked by the Internet daemon to handle talk requests
+ * Processes talk requests until MAX_LIFE seconds go by with
+ * no action, then dies.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/systeminfo.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include "talkd_impl.h"
+
+static CTL_MSG request;
+static CTL_RESPONSE response;
+
+char hostname[HOST_NAME_LENGTH];
+int debug = 0;
+
+static CTL_MSG swapmsg(CTL_MSG req);
+
+int
+main()
+{
+ struct sockaddr_in from;
+ socklen_t from_size = (socklen_t)sizeof (from);
+ int cc;
+ int name_length = sizeof (hostname);
+ fd_set rfds;
+ struct timeval tv;
+
+ (void) sysinfo(SI_HOSTNAME, hostname, name_length);
+
+ for (;;) {
+ tv.tv_sec = MAX_LIFE;
+ tv.tv_usec = 0;
+ FD_ZERO(&rfds);
+ FD_SET(0, &rfds);
+ if (select(1, &rfds, 0, 0, &tv) <= 0)
+ return (0);
+ cc = recvfrom(0, (char *)&request, sizeof (request), 0,
+ (struct sockaddr *)&from, &from_size);
+
+ if (cc != sizeof (request)) {
+ if (cc < 0 && errno != EINTR) {
+ print_error("receive");
+ }
+ } else {
+
+ if (debug) {
+ (void) printf("Request received : \n");
+ (void) print_request(&request);
+ }
+
+ request = swapmsg(request);
+ process_request(&request, &response);
+
+ if (debug) {
+ (void) printf("Response sent : \n");
+ print_response(&response);
+ }
+
+ /*
+ * Can block here, is this what I want?
+ */
+ cc = sendto(0, (char *)&response, sizeof (response), 0,
+ (struct sockaddr *)&request.ctl_addr,
+ (socklen_t)sizeof (request.ctl_addr));
+
+ if (cc != sizeof (response)) {
+ print_error("Send");
+ }
+ }
+ }
+}
+
+void
+print_error(char *string)
+{
+ FILE *cons;
+ char *err_dev = "/dev/console";
+ char *sys;
+ pid_t val, pid;
+
+ if (debug)
+ err_dev = "/dev/tty";
+
+ if ((sys = strerror(errno)) == (char *)NULL)
+ sys = "Unknown error";
+
+ /* don't ever open tty's directly, let a child do it */
+ if ((pid = fork()) == 0) {
+ cons = fopen(err_dev, "a");
+ if (cons != NULL) {
+ (void) fprintf(cons, "Talkd : %s : %s(%d)\n\r", string,
+ sys, errno);
+ (void) fclose(cons);
+ }
+ exit(0);
+ } else {
+ /* wait for the child process to return */
+ do {
+ val = wait(0);
+ if (val == (pid_t)-1) {
+ if (errno == EINTR) {
+ continue;
+ } else if (errno == ECHILD) {
+ break;
+ }
+ }
+ } while (val != pid);
+ }
+}
+
+#define swapshort(a) (((a << 8) | ((unsigned short) a >> 8)) & 0xffff)
+#define swaplong(a) ((swapshort(a) << 16) | (swapshort(((unsigned)a >> 16))))
+
+/*
+ * Heuristic to detect if need to swap bytes.
+ */
+
+static CTL_MSG
+swapmsg(CTL_MSG req)
+{
+ CTL_MSG swapreq;
+
+ if (req.ctl_addr.sin_family == swapshort(AF_INET)) {
+ swapreq = req;
+ swapreq.id_num = swaplong(req.id_num);
+ swapreq.pid = swaplong(req.pid);
+ swapreq.addr.sin_family = swapshort(req.addr.sin_family);
+ swapreq.ctl_addr.sin_family =
+ swapshort(req.ctl_addr.sin_family);
+ return (swapreq);
+ } else {
+ return (req);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.talkd/print.c b/usr/src/cmd/cmd-inet/usr.sbin/in.talkd/print.c
new file mode 100644
index 0000000000..a872d25596
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.talkd/print.c
@@ -0,0 +1,64 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T
+ * All Rights Reserved.
+ */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California.
+ * All Rights Reserved.
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/* debug print routines */
+
+#include <stdio.h>
+#include "talkd_impl.h"
+
+void
+print_request(CTL_MSG *request)
+{
+ (void) printf("type is %d, l_user %s, r_user %s, r_tty %s\n",
+ request->type, request->l_name, request->r_name,
+ request->r_tty);
+ (void) printf(" id = %d\n", request->id_num);
+ (void) fflush(stdout);
+}
+
+void
+print_response(CTL_RESPONSE *response)
+{
+ (void) printf("type is %d, answer is %d, id = %d\n\n", response->type,
+ response->answer, response->id_num);
+ (void) fflush(stdout);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.talkd/process.c b/usr/src/cmd/cmd-inet/usr.sbin/in.talkd/process.c
new file mode 100644
index 0000000000..b922dec0c6
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.talkd/process.c
@@ -0,0 +1,227 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T
+ * All Rights Reserved.
+ */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California.
+ * All Rights Reserved.
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+
+/*
+ * process.c handles the requests, which can be of three types:
+ *
+ * ANNOUNCE - announce to a user that a talk is wanted
+ *
+ * LEAVE_INVITE - insert the request into the table
+ *
+ * LOOK_UP - look up to see if a request is waiting in
+ * in the table for the local user
+ *
+ * DELETE - delete invitation
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <string.h>
+#include <utmpx.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "talkd_impl.h"
+
+static void do_announce(CTL_MSG *request, CTL_RESPONSE *response);
+static int find_user(char *name, char *tty);
+
+void
+process_request(CTL_MSG *request, CTL_RESPONSE *response)
+{
+ CTL_MSG *ptr;
+
+ response->type = request->type;
+ response->id_num = 0;
+
+ /*
+ * Check if any of the strings within the request structure aren't
+ * NUL terminated, and if so don't bother processing the request
+ * further.
+ */
+ if ((memchr(request->l_name, '\0', sizeof (request->l_name)) == NULL) ||
+ (memchr(request->r_name, '\0', sizeof (request->r_name)) == NULL) ||
+ (memchr(request->r_tty, '\0', sizeof (request->r_tty)) == NULL)) {
+ response->answer = FAILED;
+ openlog("talk", 0, LOG_AUTH);
+ syslog(LOG_CRIT, "malformed talk request\n");
+ closelog();
+ return;
+ }
+
+ switch (request->type) {
+
+ case ANNOUNCE :
+
+ do_announce(request, response);
+ break;
+
+ case LEAVE_INVITE :
+
+ ptr = find_request(request);
+ if (ptr != NULL) {
+ response->id_num = ptr->id_num;
+ response->answer = SUCCESS;
+ } else {
+ insert_table(request, response);
+ }
+ break;
+
+ case LOOK_UP :
+
+ ptr = find_match(request);
+ if (ptr != NULL) {
+ response->id_num = ptr->id_num;
+ response->addr = ptr->addr;
+ response->answer = SUCCESS;
+ } else {
+ response->answer = NOT_HERE;
+ }
+ break;
+
+ case DELETE :
+
+ response->answer = delete_invite(request->id_num);
+ break;
+
+ default :
+
+ response->answer = UNKNOWN_REQUEST;
+ break;
+ }
+}
+
+static void
+do_announce(CTL_MSG *request, CTL_RESPONSE *response)
+{
+ struct hostent *hp;
+ CTL_MSG *ptr;
+ int result;
+
+ /*
+ * See if the user is logged.
+ */
+ result = find_user(request->r_name, request->r_tty);
+ if (result != SUCCESS) {
+ response->answer = result;
+ return;
+ }
+
+ hp = gethostbyaddr((const char *)&request->ctl_addr.sin_addr,
+ sizeof (struct in_addr), AF_INET);
+ if (hp == NULL) {
+ response->answer = MACHINE_UNKNOWN;
+ return;
+ }
+
+ ptr = find_request(request);
+ if (ptr == NULL) {
+ insert_table(request, response);
+ response->answer = announce(request, hp->h_name);
+ } else if (request->id_num > ptr->id_num) {
+ /*
+ * This is an explicit re-announce, so update the id_num
+ * field to avoid duplicates and re-announce the talk.
+ */
+ ptr->id_num = response->id_num = new_id();
+ response->answer = announce(request, hp->h_name);
+ } else {
+ /* a duplicated request, so ignore it */
+ response->id_num = ptr->id_num;
+ response->answer = SUCCESS;
+ }
+}
+
+/*
+ * Search utmp for the local user.
+ */
+
+static int
+find_user(char *name, char *tty)
+{
+ struct utmpx *ubuf;
+ int tfd;
+ char dev[MAXPATHLEN];
+
+ setutxent(); /* reset the utmpx file */
+
+ while (ubuf = getutxent()) {
+ if (ubuf->ut_type == USER_PROCESS &&
+ strncmp(ubuf->ut_user, name, sizeof (ubuf->ut_user)) == 0) {
+ /*
+ * Check if this entry is really a tty.
+ */
+ (void) snprintf(dev, sizeof (dev), "/dev/%.*s",
+ sizeof (ubuf->ut_line), ubuf->ut_line);
+ if ((tfd = open(dev, O_WRONLY|O_NOCTTY)) == -1) {
+ continue;
+ }
+ if (!isatty(tfd)) {
+ (void) close(tfd);
+ openlog("talk", 0, LOG_AUTH);
+ syslog(LOG_CRIT, "%.*s in utmp is not a tty\n",
+ sizeof (ubuf->ut_line), ubuf->ut_line);
+ closelog();
+ continue;
+ }
+ (void) close(tfd);
+ if (*tty == '\0') {
+ /*
+ * No particular tty was requested.
+ */
+ (void) strlcpy(tty, ubuf->ut_line, TTY_SIZE);
+ endutxent(); /* close the utmpx file */
+ return (SUCCESS);
+ } else if (strcmp(ubuf->ut_line, tty) == 0) {
+ endutxent(); /* close the utmpx file */
+ return (SUCCESS);
+ }
+ }
+ }
+
+ endutxent(); /* close the utmpx file */
+ return (NOT_HERE);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.talkd/table.c b/usr/src/cmd/cmd-inet/usr.sbin/in.talkd/table.c
new file mode 100644
index 0000000000..1e4c86adfc
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.talkd/table.c
@@ -0,0 +1,285 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T
+ * All Rights Reserved.
+ */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California.
+ * All Rights Reserved.
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+
+/*
+ * Routines to handle insertion, deletion, etc on the table
+ * of requests kept by the daemon. Nothing fancy here, linear
+ * search on a double-linked list. A time is kept with each
+ * entry so that overly old invitations can be eliminated.
+ *
+ * Consider this a mis-guided attempt at modularity
+ */
+
+#include <sys/time.h>
+#include <string.h>
+#include <stdio.h>
+#include <malloc.h>
+#include "talkd_impl.h"
+
+#define MAX_ID 16000 /* << 2^15 so I don't have sign troubles */
+
+typedef struct table_entry TABLE_ENTRY;
+
+struct table_entry {
+ CTL_MSG request;
+ long time;
+ TABLE_ENTRY *next;
+ TABLE_ENTRY *last;
+};
+
+static struct timeval tp;
+static TABLE_ENTRY *table = NULL;
+
+static void delete(TABLE_ENTRY *ptr);
+
+/*
+ * Look in the table for an invitation that matches the current
+ * request looking for an invitation.
+ */
+
+CTL_MSG *
+find_match(CTL_MSG *request)
+{
+ TABLE_ENTRY *ptr;
+ TABLE_ENTRY *prevp;
+ long current_time;
+
+ (void) gettimeofday(&tp, NULL);
+ current_time = tp.tv_sec;
+
+ ptr = table;
+
+ if (debug) {
+ (void) printf("Entering Look-Up with : \n");
+ print_request(request);
+ }
+
+ while (ptr != NULL) {
+
+ if ((ptr->time - current_time) > MAX_LIFE) {
+ /* the entry is too old */
+ if (debug) {
+ (void) printf("Deleting expired entry : \n");
+ print_request(&ptr->request);
+ }
+ prevp = ptr;
+ ptr = ptr->next;
+ delete(prevp);
+ continue;
+ }
+
+ if (debug)
+ print_request(&ptr->request);
+
+ if (strcmp(request->l_name, ptr->request.r_name) == 0 &&
+ strcmp(request->r_name, ptr->request.l_name) == 0 &&
+ ptr->request.type == LEAVE_INVITE) {
+ return (&ptr->request);
+ }
+
+ ptr = ptr->next;
+ }
+
+ return (NULL);
+}
+
+/*
+ * Look for an identical request, as opposed to a complimentary
+ * one as find_match does.
+ */
+
+CTL_MSG *
+find_request(CTL_MSG *request)
+{
+ TABLE_ENTRY *ptr;
+ TABLE_ENTRY *prevp;
+ long current_time;
+
+ (void) gettimeofday(&tp, NULL);
+ current_time = tp.tv_sec;
+
+ /*
+ * See if this is a repeated message, and check for
+ * out of date entries in the table while we are it.
+ */
+
+ ptr = table;
+
+ if (debug) {
+ (void) printf("Entering find_request with : \n");
+ print_request(request);
+ }
+
+ while (ptr != NULL) {
+
+ if ((ptr->time - current_time) > MAX_LIFE) {
+ /* the entry is too old */
+ if (debug) {
+ (void) printf("Deleting expired entry : \n");
+ print_request(&ptr->request);
+ }
+ prevp = ptr;
+ ptr = ptr->next;
+ delete(prevp);
+ continue;
+ }
+
+ if (debug)
+ print_request(&ptr->request);
+
+ if (strcmp(request->r_name, ptr->request.r_name) == 0 &&
+ strcmp(request->l_name, ptr->request.l_name) == 0 &&
+ request->type == ptr->request.type &&
+ request->pid == ptr->request.pid) {
+
+ /* update the time if we 'touch' it */
+ ptr->time = current_time;
+ return (&ptr->request);
+ }
+
+ ptr = ptr->next;
+ }
+
+ return (NULL);
+}
+
+void
+insert_table(CTL_MSG *request, CTL_RESPONSE *response)
+{
+ TABLE_ENTRY *ptr;
+ long current_time;
+
+ (void) gettimeofday(&tp, NULL);
+ current_time = tp.tv_sec;
+
+ response->id_num = request->id_num = new_id();
+
+ /*
+ * Insert a new entry into the top of the list.
+ */
+ ptr = (TABLE_ENTRY *) malloc(sizeof (TABLE_ENTRY));
+
+ if (ptr == NULL) {
+ print_error("malloc in insert_table");
+ }
+
+ ptr->time = current_time;
+ ptr->request = *request;
+
+ ptr->next = table;
+ if (ptr->next != NULL) {
+ ptr->next->last = ptr;
+ }
+ ptr->last = NULL;
+ table = ptr;
+}
+
+/*
+ * Generate a unique non-zero sequence number.
+ */
+
+int
+new_id(void)
+{
+ static int current_id = 0;
+
+ current_id = (current_id + 1) % MAX_ID;
+
+ /* 0 is reserved, helps to pick up bugs */
+ if (current_id == 0)
+ current_id = 1;
+
+ return (current_id);
+}
+
+/*
+ * Delete the invitation with id 'id_num'.
+ */
+
+int
+delete_invite(int id_num)
+{
+ TABLE_ENTRY *ptr;
+
+ ptr = table;
+
+ if (debug)
+ (void) printf("Entering delete_invite with %d\n", id_num);
+
+ while (ptr != NULL && ptr->request.id_num != id_num) {
+ if (debug)
+ print_request(&ptr->request);
+ ptr = ptr->next;
+ }
+
+ if (ptr != NULL) {
+ delete(ptr);
+ return (SUCCESS);
+ }
+
+ return (NOT_HERE);
+}
+
+/*
+ * Classic delete from a double-linked list.
+ */
+
+static void
+delete(TABLE_ENTRY *ptr)
+{
+ if (debug) {
+ (void) printf("Deleting : ");
+ print_request(&ptr->request);
+ }
+ if (table == ptr) {
+ table = ptr->next;
+ } else if (ptr->last != NULL) {
+ ptr->last->next = ptr->next;
+ }
+
+ if (ptr->next != NULL) {
+ ptr->next->last = ptr->last;
+ }
+
+ free(ptr);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.talkd/talk.xml b/usr/src/cmd/cmd-inet/usr.sbin/in.talkd/talk.xml
new file mode 100644
index 0000000000..bddc7cbea1
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.talkd/talk.xml
@@ -0,0 +1,103 @@
+<?xml version="1.0"?>
+<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
+<!--
+ Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ ident "%Z%%M% %I% %E% SMI"
+
+ NOTE: This service manifest is not editable; its contents will
+ be overwritten by package or patch operations, including
+ operating system upgrade. Make customizations in a different
+ file.
+
+ Service manifest for the in.talkd service.
+-->
+
+<service_bundle type='manifest' name='SUNWrcmdr:talk'>
+
+<service
+ name='network/talk'
+ type='service'
+ version='1'>
+
+ <create_default_instance enabled='false' />
+
+ <restarter>
+ <service_fmri value='svc:/network/inetd:default' />
+ </restarter>
+
+ <exec_method
+ type='method'
+ name='inetd_start'
+ exec='/usr/sbin/in.talkd'
+ timeout_seconds='0'>
+ <method_context>
+ <method_credential user='root' group='root' />
+ </method_context>
+ </exec_method>
+
+ <exec_method
+ type='method'
+ name='inetd_offline'
+ exec=':kill_process'
+ timeout_seconds='0'>
+ </exec_method>
+
+ <exec_method
+ type='method'
+ name='inetd_disable'
+ exec=':kill'
+ timeout_seconds='0'>
+ </exec_method>
+
+ <property_group name='inetd' type='framework'>
+ <stability value='Evolving' />
+ <propval name='name' type='astring' value='talk' />
+ <propval name='endpoint_type' type='astring' value='dgram' />
+ <propval name='proto' type='astring' value='udp' />
+ <propval name='wait' type='boolean' value='true' />
+ <propval name='isrpc' type='boolean' value='false' />
+ </property_group>
+
+ <stability value='Unstable' />
+
+ <template>
+ <common_name>
+ <loctext xml:lang='C'>talk</loctext>
+ </common_name>
+ <description>
+ <loctext xml:lang='C'>
+ in.talkd is a service used by the talk(1) program.
+ </loctext>
+ </description>
+ <documentation>
+ <manpage title='in.talkd' section='1M'
+ manpath='/usr/share/man' />
+ <manpage title='talkd' section='1M'
+ manpath='/usr/share/man' />
+ </documentation>
+ </template>
+</service>
+
+</service_bundle>
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.talkd/talkd_impl.h b/usr/src/cmd/cmd-inet/usr.sbin/in.talkd/talkd_impl.h
new file mode 100644
index 0000000000..2bfd307084
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.talkd/talkd_impl.h
@@ -0,0 +1,61 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _TALKD_IMPL_H
+#define _TALKD_IMPL_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * This file contains the header, function and global variable definitions
+ * used throughout the in.talkd source.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "ctl.h"
+
+extern int debug;
+extern char hostname[];
+
+extern void print_error(char *string);
+extern void print_response(CTL_RESPONSE *response);
+extern void print_request(CTL_MSG *request);
+extern void process_request(CTL_MSG *request, CTL_RESPONSE *response);
+extern CTL_MSG *find_request(CTL_MSG *request);
+extern CTL_MSG *find_match(CTL_MSG *request);
+extern void insert_table(CTL_MSG *request, CTL_RESPONSE *response);
+extern int delete_invite(int id_num);
+extern int announce(CTL_MSG *request, char *remote_machine);
+extern int new_id(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TALKD_IMPL_H */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.telnetd.c b/usr/src/cmd/cmd-inet/usr.sbin/in.telnetd.c
new file mode 100644
index 0000000000..44ffc79b93
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.telnetd.c
@@ -0,0 +1,4903 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T
+ * All Rights Reserved.
+ */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California.
+ * All Rights Reserved.
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Telnet server.
+ */
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/filio.h>
+#include <sys/time.h>
+#include <sys/stropts.h>
+#include <sys/stream.h>
+#include <sys/tihdr.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+
+#include <netinet/in.h>
+
+#define AUTHWHO_STR
+#define AUTHTYPE_NAMES
+#define AUTHHOW_NAMES
+#define AUTHRSP_NAMES
+#define ENCRYPT_NAMES
+
+#include <arpa/telnet.h>
+#include <arpa/inet.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <signal.h>
+#include <errno.h>
+#include <netdb.h>
+#include <syslog.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <sac.h> /* for SC_WILDC */
+#include <utmpx.h>
+#include <sys/ttold.h>
+#include <malloc.h>
+#include <string.h>
+#include <security/pam_appl.h>
+#include <sys/tihdr.h>
+#include <sys/logindmux.h>
+#include <sys/telioctl.h>
+#include <deflt.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stropts.h>
+#include <termios.h>
+
+#include <com_err.h>
+#include <krb5.h>
+#include <krb5_repository.h>
+#include <des/des.h>
+#include <rpc/des_crypt.h>
+#include <sys/cryptmod.h>
+#include <bsm/adt.h>
+
+#define TELNETD_OPTS "Ss:a:dEXUhR:M:"
+#ifdef DEBUG
+#define DEBUG_OPTS "p:e"
+#else
+#define DEBUG_OPTS ""
+#endif /* DEBUG */
+
+#define OPT_NO 0 /* won't do this option */
+#define OPT_YES 1 /* will do this option */
+#define OPT_YES_BUT_ALWAYS_LOOK 2
+#define OPT_NO_BUT_ALWAYS_LOOK 3
+
+#define MAXOPTLEN 256
+#define MAXUSERNAMELEN 256
+
+static char remopts[MAXOPTLEN];
+static char myopts[MAXOPTLEN];
+static uchar_t doopt[] = { (uchar_t)IAC, (uchar_t)DO, '%', 'c', 0 };
+static uchar_t dont[] = { (uchar_t)IAC, (uchar_t)DONT, '%', 'c', 0 };
+static uchar_t will[] = { (uchar_t)IAC, (uchar_t)WILL, '%', 'c', 0 };
+static uchar_t wont[] = { (uchar_t)IAC, (uchar_t)WONT, '%', 'c', 0 };
+/*
+ * I/O data buffers, pointers, and counters.
+ */
+static char ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf;
+
+static char *netibuf, *netip;
+static int netibufsize;
+
+#define NIACCUM(c) { *netip++ = c; \
+ ncc++; \
+ }
+
+static char netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf;
+static char *neturg = 0; /* one past last bye of urgent data */
+/* the remote system seems to NOT be an old 4.2 */
+static int not42 = 1;
+static char defaultfile[] = "/etc/default/telnetd";
+static char bannervar[] = "BANNER=";
+
+static char BANNER1[] = "\r\n\r\n";
+static char BANNER2[] = "\r\n\r\0\r\n\r\0";
+
+/*
+ * buffer for sub-options - enlarged to 4096 to handle credentials
+ * from AUTH options
+ */
+static char subbuffer[4096], *subpointer = subbuffer, *subend = subbuffer;
+#define SB_CLEAR() subpointer = subbuffer;
+#define SB_TERM() { subend = subpointer; SB_CLEAR(); }
+#define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof (subbuffer))) { \
+ *subpointer++ = (c); \
+ }
+#define SB_GET() ((*subpointer++)&0xff)
+#define SB_EOF() (subpointer >= subend)
+#define SB_LEN() (subend - subpointer)
+
+#define MAXCCACHENAMELEN 36
+#define MAXERRSTRLEN 1024
+#define MAXPRINCLEN 256
+
+static boolean_t auth_debug = 0;
+static boolean_t negotiate_auth_krb5 = 1;
+static boolean_t auth_negotiated = 0;
+static int auth_status = 0;
+static int auth_level = 0;
+static char *AuthenticatingUser = NULL;
+static char *krb5_name = NULL;
+
+static krb5_address rsaddr = { 0, 0, 0, NULL };
+static krb5_address rsport = { 0, 0, 0, NULL };
+
+static krb5_context telnet_context = 0;
+static krb5_auth_context auth_context = 0;
+
+/* telnetd gets session key from here */
+static krb5_ticket *ticket = NULL;
+static krb5_keyblock *session_key = NULL;
+static char *telnet_srvtab = NULL;
+
+typedef struct {
+ uchar_t AuthName;
+ uchar_t AuthHow;
+ char *AuthString;
+} AuthInfo;
+
+static AuthInfo auth_list[] = {
+ {AUTHTYPE_KERBEROS_V5, AUTH_WHO_CLIENT | AUTH_HOW_MUTUAL |
+ AUTH_ENCRYPT_ON, "KRB5 MUTUAL CRYPTO"},
+ {AUTHTYPE_KERBEROS_V5, AUTH_WHO_CLIENT | AUTH_HOW_MUTUAL,
+ "KRB5 MUTUAL" },
+ {AUTHTYPE_KERBEROS_V5, AUTH_WHO_CLIENT | AUTH_HOW_ONE_WAY,
+ "KRB5 1-WAY" },
+ {0, 0, "NONE"}
+};
+
+static AuthInfo NoAuth = {0, 0, NULL};
+
+static AuthInfo *authenticated = NULL;
+
+#define PREAMBLE_SIZE 5 /* for auth_reply_str allocation */
+#define POSTAMBLE_SIZE 5
+#define STR_DATA_LEN(len) ((len) * 2 + PREAMBLE_SIZE + POSTAMBLE_SIZE)
+
+static void auth_name(uchar_t *, int);
+static void auth_is(uchar_t *, int);
+
+#define NO_ENCRYPTION 0x00
+#define SEND_ENCRYPTED 0x01
+#define RECV_ENCRYPTED 0x02
+#define ENCRYPT_BOTH_WAYS (SEND_ENCRYPTED | RECV_ENCRYPTED)
+
+static telnet_enc_data_t encr_data;
+static boolean_t negotiate_encrypt = B_TRUE;
+static boolean_t sent_encrypt_support = B_FALSE;
+static boolean_t sent_will_encrypt = B_FALSE;
+static boolean_t sent_do_encrypt = B_FALSE;
+static boolean_t enc_debug = 0;
+
+static void encrypt_session_key(Session_Key *key, cipher_info_t *cinfo);
+static int encrypt_send_encrypt_is();
+
+extern void mit_des_fixup_key_parity(Block);
+extern int krb5_setenv(const char *, const char *, int);
+/* need to know what FD to use to talk to the crypto module */
+static int cryptmod_fd = -1;
+
+#define LOGIN_PROGRAM "/bin/login"
+
+/*
+ * State for recv fsm
+ */
+#define TS_DATA 0 /* base state */
+#define TS_IAC 1 /* look for double IAC's */
+#define TS_CR 2 /* CR-LF ->'s CR */
+#define TS_SB 3 /* throw away begin's... */
+#define TS_SE 4 /* ...end's (suboption negotiation) */
+#define TS_WILL 5 /* will option negotiation */
+#define TS_WONT 6 /* wont " */
+#define TS_DO 7 /* do " */
+#define TS_DONT 8 /* dont " */
+
+static int ncc;
+static int master; /* master side of pty */
+static int pty; /* side of pty that gets ioctls */
+static int net;
+static int inter;
+extern char **environ;
+static char *line;
+static int SYNCHing = 0; /* we are in TELNET SYNCH mode */
+static int state = TS_DATA;
+
+static int env_ovar = -1; /* XXX.sparker */
+static int env_ovalue = -1; /* XXX.sparker */
+static char pam_svc_name[64];
+static boolean_t telmod_init_done = B_FALSE;
+
+static void doit(int, struct sockaddr_storage *);
+static void willoption(int);
+static void wontoption(int);
+static void dooption(int);
+static void dontoption(int);
+static void fatal(int, char *);
+static void fatalperror(int, char *, int);
+static void mode(int, int);
+static void interrupt(void);
+static void drainstream(int);
+static int readstream(int, char *, int);
+static int send_oob(int fd, char *ptr, int count);
+static int local_setenv(const char *name, const char *value, int rewrite);
+static void local_unsetenv(const char *name);
+static void suboption(void);
+static int removemod(int f, char *modname);
+static void willoption(int option);
+static void wontoption(int option);
+static void dooption(int option);
+static void dontoption(int option);
+static void write_data(const char *, ...);
+static void write_data_len(const char *, int);
+static void rmut(void);
+static void cleanup(int);
+static void telnet(int, int);
+static void telrcv(void);
+static void sendbrk(void);
+static void ptyflush(void);
+static void netclear(void);
+static void netflush(void);
+static void showbanner(void);
+static void map_banner(char *);
+static void defbanner(void);
+static void ttloop(void);
+
+/*
+ * The env_list linked list is used to store the environment variables
+ * until the final exec of login. A malevolent client might try to
+ * send an environment variable intended to affect the telnet daemon's
+ * execution. Right now the BANNER expansion is the only instance.
+ * Note that it is okay to pass the environment variables to login
+ * because login protects itself against environment variables mischief.
+ */
+
+struct envlist {
+ struct envlist *next;
+ char *name;
+ char *value;
+ int delete;
+};
+
+static struct envlist *envlist_head = NULL;
+
+/*
+ * The following are some clocks used to decide how to interpret
+ * the relationship between various variables.
+ */
+
+static struct {
+ int
+ system, /* what the current time is */
+ echotoggle, /* last time user entered echo character */
+ modenegotiated, /* last time operating mode negotiated */
+ didnetreceive, /* last time we read data from network */
+ ttypeopt, /* ttype will/won't received */
+ ttypesubopt, /* ttype subopt is received */
+ getterminal, /* time started to get terminal information */
+ xdisplocopt, /* xdisploc will/wont received */
+ xdisplocsubopt, /* xdisploc suboption received */
+ nawsopt, /* window size will/wont received */
+ nawssubopt, /* window size received */
+ environopt, /* environment option will/wont received */
+ oenvironopt, /* "old" environ option will/wont received */
+ environsubopt, /* environment option suboption received */
+ oenvironsubopt, /* "old environ option suboption received */
+ gotDM; /* when did we last see a data mark */
+
+ int getauth;
+ int authopt; /* Authentication option negotiated */
+ int authdone;
+
+ int getencr;
+ int encropt;
+ int encr_support;
+} clocks;
+
+static int init_neg_done = 0;
+static boolean_t resolve_hostname = 0;
+static boolean_t show_hostinfo = 1;
+
+#define settimer(x) (clocks.x = ++clocks.system)
+#define sequenceIs(x, y) (clocks.x < clocks.y)
+
+static void send_will(int);
+static void send_wont(int);
+static void send_do(int);
+static char *__findenv(const char *name, int *offset);
+
+/* ARGSUSED */
+static void
+auth_finished(AuthInfo *ap, int result)
+{
+ if ((authenticated = ap) == NULL) {
+ authenticated = &NoAuth;
+ if (myopts[TELOPT_ENCRYPT] == OPT_YES)
+ send_wont(TELOPT_ENCRYPT);
+ myopts[TELOPT_ENCRYPT] = remopts[TELOPT_ENCRYPT] = OPT_NO;
+ encr_data.encrypt.autoflag = 0;
+ } else if (result != AUTH_REJECT &&
+ myopts[TELOPT_ENCRYPT] == OPT_YES &&
+ remopts[TELOPT_ENCRYPT] == OPT_YES) {
+
+ /*
+ * Authentication successful, so we have a session key, and
+ * we're willing to do ENCRYPT, so send our ENCRYPT SUPPORT.
+ *
+ * Can't have sent ENCRYPT SUPPORT yet! And if we're sending it
+ * now it's really only because we did the DO ENCRYPT/WILL
+ * ENCRYPT dance before authentication, which is ok, but not too
+ * bright since we have to do the DONT ENCRYPT/WONT ENCRYPT
+ * dance if authentication fails, though clients typically just
+ * don't care.
+ */
+ write_data("%c%c%c%c%c%c%c",
+ (uchar_t)IAC,
+ (uchar_t)SB,
+ (uchar_t)TELOPT_ENCRYPT,
+ (uchar_t)ENCRYPT_SUPPORT,
+ (uchar_t)TELOPT_ENCTYPE_DES_CFB64,
+ (uchar_t)IAC,
+ (uchar_t)SE);
+
+ netflush();
+
+ sent_encrypt_support = B_TRUE;
+
+ if (enc_debug)
+ (void) fprintf(stderr,
+ "SENT ENCRYPT SUPPORT\n");
+
+ (void) encrypt_send_encrypt_is();
+ }
+
+ auth_status = result;
+
+ settimer(authdone);
+}
+
+static void
+reply_to_client(AuthInfo *ap, int type, void *data, int len)
+{
+ uchar_t reply[BUFSIZ];
+ uchar_t *p = reply;
+ uchar_t *cd = (uchar_t *)data;
+
+ if (len == -1 && data != NULL)
+ len = strlen((char *)data);
+ else if (len > (sizeof (reply) - 9)) {
+ syslog(LOG_ERR,
+ "krb5 auth reply length too large (%d)", len);
+ if (auth_debug)
+ (void) fprintf(stderr,
+ "krb5 auth reply length too large (%d)\n",
+ len);
+ return;
+ } else if (data == NULL)
+ len = 0;
+
+ *p++ = IAC;
+ *p++ = SB;
+ *p++ = TELOPT_AUTHENTICATION;
+ *p++ = AUTHTYPE_KERBEROS_V5;
+ *p++ = ap->AuthName;
+ *p++ = ap->AuthHow; /* MUTUAL, ONE-WAY, etc */
+ *p++ = type; /* RESPONSE or ACCEPT */
+ while (len-- > 0) {
+ if ((*p++ = *cd++) == IAC)
+ *p++ = IAC;
+ }
+ *p++ = IAC;
+ *p++ = SE;
+
+ /* queue the data to be sent */
+ write_data_len((const char *)reply, p-reply);
+
+#if defined(AUTHTYPE_NAMES) && defined(AUTHWHO_STR) &&\
+defined(AUTHHOW_NAMES) && defined(AUTHRSP_NAMES)
+ if (auth_debug) {
+ (void) fprintf(stderr, "SENT TELOPT_AUTHENTICATION REPLY "
+ "%s %s|%s %s\n",
+ AUTHTYPE_NAME(ap->AuthName),
+ AUTHWHO_NAME(ap->AuthHow & AUTH_WHO_MASK),
+ AUTHHOW_NAME(ap->AuthHow & AUTH_HOW_MASK),
+ AUTHRSP_NAME(type));
+ }
+#endif /* AUTHTYPE_NAMES && AUTHWHO_NAMES && AUTHHOW_NAMES && AUTHRSP_NAMES */
+
+ netflush();
+}
+
+/* Decode, decrypt and store the forwarded creds in the local ccache. */
+static krb5_error_code
+rd_and_store_forwarded_creds(krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_data *inbuf, krb5_ticket *ticket,
+ char *username)
+{
+ krb5_creds **creds;
+ krb5_error_code retval;
+ char ccname[MAXCCACHENAMELEN];
+ krb5_ccache ccache = NULL;
+
+ if (retval = krb5_rd_cred(context, auth_context, inbuf, &creds, NULL))
+ return (retval);
+
+ (void) sprintf(ccname, "FILE:/tmp/krb5cc_p%ld", getpid());
+ (void) krb5_setenv("KRB5CCNAME", ccname, 1);
+
+ if ((retval = krb5_cc_default(context, &ccache)))
+ goto cleanup;
+
+ if ((retval = krb5_cc_initialize(context, ccache,
+ ticket->enc_part2->client)) != 0)
+ goto cleanup;
+
+ if ((retval = krb5_cc_store_cred(context, ccache, *creds)) != 0)
+ goto cleanup;
+
+ if ((retval = krb5_cc_close(context, ccache)) != 0)
+ goto cleanup;
+
+ if (username != NULL) {
+ /*
+ * This verifies that the user is valid on the local system,
+ * maps the username from KerberosV5 to unix,
+ * and moves the KRB5CCNAME file to the correct place
+ * /tmp/krb5cc_[uid] with correct ownership (0600 uid gid).
+ *
+ * NOTE: the user must be in the gsscred table in order to map
+ * from KRB5 to Unix.
+ */
+ (void) krb5_kuserok(context, ticket->enc_part2->client,
+ username);
+ }
+ if (auth_debug)
+ (void) fprintf(stderr,
+ "Successfully stored forwarded creds\n");
+
+cleanup:
+ krb5_free_creds(context, *creds);
+ return (retval);
+}
+
+static void
+kerberos5_is(AuthInfo *ap, uchar_t *data, int cnt)
+{
+ krb5_error_code err = 0;
+ krb5_principal server;
+ krb5_keyblock *newkey = NULL;
+ krb5_keytab keytabid = 0;
+ krb5_data outbuf;
+ krb5_data inbuf;
+ krb5_authenticator *authenticator;
+ char errbuf[MAXERRSTRLEN];
+ char *name;
+ krb5_data auth;
+
+ Session_Key skey;
+
+ if (cnt-- < 1)
+ return;
+ switch (*data++) {
+ case KRB_AUTH:
+ auth.data = (char *)data;
+ auth.length = cnt;
+
+ if (auth_context == NULL) {
+ err = krb5_auth_con_init(telnet_context, &auth_context);
+ if (err)
+ syslog(LOG_ERR,
+ "Error getting krb5 auth "
+ "context: %s", error_message(err));
+ }
+ if (!err) {
+ krb5_rcache rcache;
+
+ err = krb5_auth_con_getrcache(telnet_context,
+ auth_context,
+ &rcache);
+ if (!err && !rcache) {
+ err = krb5_sname_to_principal(telnet_context,
+ 0, 0,
+ KRB5_NT_SRV_HST,
+ &server);
+ if (!err) {
+ err = krb5_get_server_rcache(
+ telnet_context,
+ krb5_princ_component(
+ telnet_context,
+ server, 0),
+ &rcache);
+
+ krb5_free_principal(telnet_context,
+ server);
+ }
+ }
+ if (err)
+ syslog(LOG_ERR,
+ "Error allocating krb5 replay cache: %s",
+ error_message(err));
+ else {
+ err = krb5_auth_con_setrcache(telnet_context,
+ auth_context,
+ rcache);
+ if (err)
+ syslog(LOG_ERR,
+ "Error creating krb5 "
+ "replay cache: %s",
+ error_message(err));
+ }
+ }
+ if (!err && telnet_srvtab != NULL)
+ err = krb5_kt_resolve(telnet_context,
+ telnet_srvtab, &keytabid);
+ if (!err)
+ err = krb5_rd_req(telnet_context, &auth_context, &auth,
+ NULL, keytabid, NULL, &ticket);
+ if (err) {
+ (void) snprintf(errbuf, sizeof (errbuf),
+ "Error reading krb5 auth information:"
+ " %s", error_message(err));
+ goto errout;
+ }
+
+ /*
+ * Verify that the correct principal was used
+ */
+ if (krb5_princ_component(telnet_context,
+ ticket->server, 0)->length < MAXPRINCLEN) {
+ char princ[MAXPRINCLEN];
+ (void) strncpy(princ,
+ krb5_princ_component(telnet_context,
+ ticket->server, 0)->data,
+ krb5_princ_component(telnet_context,
+ ticket->server, 0)->length);
+ princ[krb5_princ_component(telnet_context,
+ ticket->server, 0)->length] = '\0';
+ if (strcmp("host", princ)) {
+ if (strlen(princ) < sizeof (errbuf) - 39) {
+ (void) snprintf(errbuf, sizeof (errbuf),
+ "incorrect service "
+ "name: \"%s\" != "
+ "\"host\"",
+ princ);
+ } else {
+ (void) strncpy(errbuf,
+ "incorrect service "
+ "name: principal != "
+ "\"host\"",
+ sizeof (errbuf));
+ }
+ goto errout;
+ }
+ } else {
+ (void) strlcpy(errbuf, "service name too long",
+ sizeof (errbuf));
+ goto errout;
+ }
+
+ err = krb5_auth_con_getauthenticator(telnet_context,
+ auth_context,
+ &authenticator);
+ if (err) {
+ (void) snprintf(errbuf, sizeof (errbuf),
+ "Failed to get authenticator: %s",
+ error_message(err));
+ goto errout;
+ }
+ if ((ap->AuthHow & AUTH_ENCRYPT_MASK) == AUTH_ENCRYPT_ON &&
+ !authenticator->checksum) {
+ (void) strlcpy(errbuf,
+ "authenticator is missing checksum",
+ sizeof (errbuf));
+ goto errout;
+ }
+ if (authenticator->checksum) {
+ char type_check[2];
+ krb5_checksum *cksum = authenticator->checksum;
+ krb5_keyblock *key;
+ krb5_data input;
+ krb5_boolean valid;
+
+ type_check[0] = ap->AuthName;
+ type_check[1] = ap->AuthHow;
+
+ err = krb5_auth_con_getkey(telnet_context,
+ auth_context, &key);
+ if (err) {
+ (void) snprintf(errbuf, sizeof (errbuf),
+ "Failed to get key from "
+ "authenticator: %s",
+ error_message(err));
+ goto errout;
+ }
+
+ input.data = type_check;
+ input.length = 2;
+ err = krb5_c_verify_checksum(telnet_context,
+ key, 0,
+ &input,
+ cksum,
+ &valid);
+ if (!err && !valid)
+ err = KRB5KRB_AP_ERR_BAD_INTEGRITY;
+
+ if (err) {
+ (void) snprintf(errbuf, sizeof (errbuf),
+ "Kerberos checksum "
+ "verification failed: "
+ "%s",
+ error_message(err));
+ goto errout;
+ }
+ krb5_free_keyblock(telnet_context, key);
+ }
+
+ krb5_free_authenticator(telnet_context, authenticator);
+ if ((ap->AuthHow & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) {
+ /* do ap_rep stuff here */
+ if ((err = krb5_mk_rep(telnet_context, auth_context,
+ &outbuf))) {
+ (void) snprintf(errbuf, sizeof (errbuf),
+ "Failed to make "
+ "Kerberos auth reply: "
+ "%s",
+ error_message(err));
+ goto errout;
+ }
+ reply_to_client(ap, KRB_RESPONSE, outbuf.data,
+ outbuf.length);
+ }
+ if (krb5_unparse_name(telnet_context,
+ ticket->enc_part2->client,
+ &name))
+ name = 0;
+ reply_to_client(ap, KRB_ACCEPT, name, name ? -1 : 0);
+ if (auth_debug) {
+ syslog(LOG_NOTICE,
+ "\tKerberos5 identifies user as ``%s''\r\n",
+ name ? name : "");
+ }
+ if (name != NULL) {
+ krb5_name = (char *)strdup(name);
+ }
+ auth_finished(ap, AUTH_USER);
+
+ if (name != NULL)
+ free(name);
+ krb5_auth_con_getremotesubkey(telnet_context, auth_context,
+ &newkey);
+ if (session_key != NULL) {
+ krb5_free_keyblock(telnet_context, session_key);
+ session_key = 0;
+ }
+ if (newkey != NULL) {
+ krb5_copy_keyblock(telnet_context,
+ newkey, &session_key);
+ krb5_free_keyblock(telnet_context, newkey);
+ } else {
+ krb5_copy_keyblock(telnet_context,
+ ticket->enc_part2->session,
+ &session_key);
+ }
+
+ /*
+ * Initialize encryption stuff. Currently, we are only
+ * supporting 8 byte keys and blocks. Check for this later.
+ */
+ skey.type = SK_DES;
+ skey.length = DES_BLOCKSIZE;
+ skey.data = session_key->contents;
+ encrypt_session_key(&skey, &encr_data.encrypt);
+ encrypt_session_key(&skey, &encr_data.decrypt);
+ break;
+ case KRB_FORWARD:
+ inbuf.length = cnt;
+ inbuf.data = (char *)data;
+ if (auth_debug)
+ (void) fprintf(stderr,
+ "RCVD KRB_FORWARD data (%d bytes)\n", cnt);
+
+ if (auth_context != NULL) {
+ krb5_rcache rcache;
+
+ err = krb5_auth_con_getrcache(telnet_context,
+ auth_context, &rcache);
+ if (!err && !rcache) {
+ err = krb5_sname_to_principal(telnet_context,
+ 0, 0, KRB5_NT_SRV_HST, &server);
+ if (!err) {
+ err = krb5_get_server_rcache(
+ telnet_context,
+ krb5_princ_component(
+ telnet_context,
+ server, 0),
+ &rcache);
+ krb5_free_principal(telnet_context,
+ server);
+ }
+ }
+ if (err) {
+ syslog(LOG_ERR,
+ "Error allocating krb5 replay cache: %s",
+ error_message(err));
+ } else {
+ err = krb5_auth_con_setrcache(telnet_context,
+ auth_context, rcache);
+ if (err)
+ syslog(LOG_ERR,
+ "Error creating krb5 replay cache:"
+ " %s",
+ error_message(err));
+ }
+ }
+ /*
+ * Use the 'rsaddr' and 'rsport' (remote service addr/port)
+ * from the original connection. This data is used to
+ * verify the forwarded credentials.
+ */
+ if (!(err = krb5_auth_con_setaddrs(telnet_context, auth_context,
+ NULL, &rsaddr)))
+ err = krb5_auth_con_setports(telnet_context,
+ auth_context, NULL, &rsport);
+
+ if (err == 0)
+ /*
+ * If all is well, store the forwarded creds in
+ * the users local credential cache.
+ */
+ err = rd_and_store_forwarded_creds(telnet_context,
+ auth_context, &inbuf,
+ ticket,
+ AuthenticatingUser);
+ if (err) {
+ (void) snprintf(errbuf, sizeof (errbuf),
+ "Read forwarded creds failed: %s",
+ error_message(err));
+ syslog(LOG_ERR, "%s", errbuf);
+
+ reply_to_client(ap, KRB_FORWARD_REJECT, errbuf, -1);
+ if (auth_debug)
+ (void) fprintf(stderr,
+ "\tCould not read "
+ "forwarded credentials\r\n");
+ } else
+ reply_to_client(ap, KRB_FORWARD_ACCEPT, (void *) 0, 0);
+
+ if (rsaddr.contents != NULL)
+ free(rsaddr.contents);
+
+ if (rsport.contents != NULL)
+ free(rsport.contents);
+
+ if (auth_debug)
+ (void) fprintf(stderr, "\tForwarded "
+ "credentials obtained\r\n");
+ break;
+ default:
+ if (auth_debug)
+ (void) fprintf(stderr,
+ "\tUnknown Kerberos option %d\r\n",
+ data[-1]);
+ reply_to_client(ap, KRB_REJECT, (void *) 0, 0);
+ break;
+ }
+ return;
+
+errout:
+ reply_to_client(ap, KRB_REJECT, errbuf, -1);
+
+ if (auth_debug)
+ (void) fprintf(stderr, "\tKerberos V5 error: %s\r\n", errbuf);
+
+ syslog(LOG_ERR, "%s", errbuf);
+
+ if (auth_context != NULL) {
+ krb5_auth_con_free(telnet_context, auth_context);
+ auth_context = 0;
+ }
+}
+
+static int
+krb5_init()
+{
+ int code = 0;
+
+ if (telnet_context == NULL) {
+ code = krb5_init_context(&telnet_context);
+ if (code != 0 && auth_debug)
+ syslog(LOG_NOTICE,
+ "Cannot initialize Kerberos V5: %s",
+ error_message(code));
+ }
+
+ return (code);
+}
+
+static void
+auth_name(uchar_t *data, int cnt)
+{
+ char namebuf[MAXPRINCLEN];
+
+ if (cnt < 1) {
+ if (auth_debug)
+ (void) fprintf(stderr,
+ "\t(auth_name) Empty NAME in auth "
+ "reply\n");
+ return;
+ }
+ if (cnt > sizeof (namebuf)-1) {
+ if (auth_debug)
+ (void) fprintf(stderr,
+ "\t(auth_name) NAME exceeds %d bytes\n",
+ sizeof (namebuf)-1);
+ return;
+ }
+ (void) memcpy((void *)namebuf, (void *)data, cnt);
+ namebuf[cnt] = 0;
+ if (auth_debug)
+ (void) fprintf(stderr, "\t(auth_name) name [%s]\n", namebuf);
+ AuthenticatingUser = (char *)strdup(namebuf);
+}
+
+static void
+auth_is(uchar_t *data, int cnt)
+{
+ AuthInfo *aptr = auth_list;
+
+ if (cnt < 2)
+ return;
+
+ /*
+ * We failed to negoiate secure authentication
+ */
+ if (data[0] == AUTHTYPE_NULL) {
+ auth_finished(0, AUTH_REJECT);
+ return;
+ }
+
+ while (aptr->AuthName != NULL &&
+ (aptr->AuthName != data[0] || aptr->AuthHow != data[1]))
+ aptr++;
+
+ if (aptr != NULL) {
+ if (auth_debug)
+ (void) fprintf(stderr, "\t(auth_is) auth type is %s "
+ "(%d bytes)\n", aptr->AuthString, cnt);
+
+ if (aptr->AuthName == AUTHTYPE_KERBEROS_V5)
+ kerberos5_is(aptr, data+2, cnt-2);
+ }
+}
+
+static int
+krb5_user_status(char *name, int namelen, int level)
+{
+ int retval = AUTH_USER;
+
+ if (auth_debug)
+ (void) fprintf(stderr, "\t(krb5_user_status) level = %d "
+ "auth_level = %d user = %s\n",
+ level, auth_level,
+ (AuthenticatingUser != NULL ? AuthenticatingUser : ""));
+
+ if (level < AUTH_USER)
+ return (level);
+
+ if (AuthenticatingUser != NULL &&
+ (retval = krb5_kuserok(telnet_context, ticket->enc_part2->client,
+ AuthenticatingUser))) {
+ (void) strncpy(name, AuthenticatingUser, namelen);
+ return (AUTH_VALID);
+ } else {
+ if (!retval)
+ syslog(LOG_ERR,
+ "Krb5 principal lacks permission to "
+ "access local account for %s",
+ AuthenticatingUser);
+ return (AUTH_USER);
+ }
+}
+
+/*
+ * Wrapper around /dev/urandom
+ */
+static int
+getrandom(char *buf, int buflen)
+{
+ static int devrandom = -1;
+
+ if (devrandom == -1 &&
+ (devrandom = open("/dev/urandom", O_RDONLY)) == -1) {
+ fatalperror(net, "Unable to open /dev/urandom: ",
+ errno);
+ return (-1);
+ }
+
+ if (read(devrandom, buf, buflen) == -1) {
+ fatalperror(net, "Unable to read from /dev/urandom: ",
+ errno);
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * encrypt_init
+ *
+ * Initialize the encryption data structures
+ */
+static void
+encrypt_init()
+{
+ (void) memset(&encr_data.encrypt, 0, sizeof (cipher_info_t));
+ (void) memset(&encr_data.decrypt, 0, sizeof (cipher_info_t));
+
+ encr_data.encrypt.state = ENCR_STATE_NOT_READY;
+ encr_data.decrypt.state = ENCR_STATE_NOT_READY;
+}
+
+/*
+ * encrypt_send_request_start
+ *
+ * Request that the remote side automatically start sending
+ * encrypted output
+ */
+static void
+encrypt_send_request_start()
+{
+ uchar_t buf[6+TELNET_MAXKEYIDLEN], *p;
+
+ p = buf;
+
+ *p++ = IAC;
+ *p++ = SB;
+ *p++ = TELOPT_ENCRYPT;
+ *p++ = ENCRYPT_REQSTART;
+ /*
+ * We are telling the remote side which
+ * decrypt key we will use so that it may
+ * encrypt in the same key.
+ */
+ (void) memcpy(p, encr_data.decrypt.keyid, encr_data.decrypt.keyidlen);
+ p += encr_data.decrypt.keyidlen;
+
+ *p++ = IAC;
+ *p++ = SE;
+
+ write_data_len((const char *)buf, p-buf);
+ netflush();
+ if (enc_debug)
+ (void) fprintf(stderr,
+ "SENT TELOPT_ENCRYPT ENCRYPT_REQSTART\n");
+}
+
+/*
+ * encrypt_is
+ *
+ * When we receive the TELOPT_ENCRYPT ENCRYPT_IS ...
+ * message, the client is telling us that it will be sending
+ * encrypted data using the indicated cipher.
+ * We must initialize the read (decrypt) side of our connection
+ */
+static void
+encrypt_is(uchar_t *data, int cnt)
+{
+ register int type;
+ register int iv_status = CFB64_IV_OK;
+ register int lstate = 0;
+
+ uchar_t sbbuf[] = {
+ (uchar_t)IAC,
+ (uchar_t)SB,
+ (uchar_t)TELOPT_ENCRYPT,
+ (uchar_t)ENCRYPT_REPLY,
+ (uchar_t)0, /* placeholder: sbbuf[4] */
+ (uchar_t)CFB64_IV_OK, /* placeholder: sbbuf[5] */
+ (uchar_t)IAC,
+ (uchar_t)SE,
+ };
+
+ if (--cnt < 0)
+ return;
+
+ type = sbbuf[4] = *data++;
+
+ /*
+ * Steps to take:
+ * 1. Create the proper stream Initialization vector
+ * - copy the correct 'seed' to IV and output blocks
+ * - set the correct key schedule
+ * 2. Generate reply for the other side:
+ * IAC SB TELOPT_ENCRYPT ENCRYPT_REPLY type CFB64_IV_OK
+ * [ data ... ] IAC SE
+ * 3. Tell crypto module: method, direction, IV
+ */
+ switch (type) {
+ case TELOPT_ENCTYPE_DES_CFB64:
+ encr_data.decrypt.type = type;
+
+ lstate = encr_data.decrypt.state;
+ if (enc_debug)
+ (void) fprintf(stderr,
+ "\t(encrypt_is) initial state = %d\n",
+ lstate);
+ /*
+ * Before we extract the IV bytes, make sure we got
+ * enough data.
+ */
+ if (cnt < sizeof (Block)) {
+ iv_status = CFB64_IV_BAD;
+ if (enc_debug)
+ (void) fprintf(stderr,
+ "\t(encrypt_is) Not enough "
+ "IV bytes\n");
+ lstate = ENCR_STATE_NOT_READY;
+ } else {
+ data++; /* skip over the CFB64_IV byte */
+ (void) memcpy(encr_data.decrypt.ivec, data,
+ sizeof (Block));
+ lstate = ENCR_STATE_IN_PROGRESS;
+ }
+ break;
+ case TELOPT_ENCTYPE_NULL:
+ encr_data.decrypt.type = type;
+ lstate &= ~ENCR_STATE_NO_RECV_IV;
+ lstate &= ~ENCR_STATE_NO_SEND_IV;
+ if (enc_debug)
+ (void) fprintf(stderr,
+ "\t(encrypt_is) We accept NULL encr\n");
+ break;
+ default:
+ iv_status = CFB64_IV_BAD;
+ encr_data.decrypt.type = NULL;
+ if (enc_debug)
+ (void) fprintf(stderr,
+ "\t(encrypt_is) Can't find type (%d) "
+ "for initial negotiation\r\n",
+ type);
+ lstate = ENCR_STATE_NOT_READY;
+ break;
+ }
+
+ sbbuf[5] = (uchar_t)iv_status; /* either CFB64_IV_OK or BAD */
+
+ if (iv_status == CFB64_IV_OK) {
+ /*
+ * send IV to crypto module and indicate it is for
+ * decrypt only
+ */
+ lstate &= ~ENCR_STATE_NO_RECV_IV; /* we received an OK IV */
+ lstate &= ~ENCR_STATE_NO_SEND_IV; /* we dont send an IV */
+ } else {
+ /* tell crypto module to disable crypto on "read" stream */
+ lstate = ENCR_STATE_NOT_READY;
+ }
+
+ write_data_len((const char *)sbbuf, sizeof (sbbuf));
+ netflush();
+#ifdef ENCRYPT_NAMES
+ if (enc_debug)
+ (void) fprintf(stderr,
+ "SENT TELOPT_ENCRYPT ENCRYPT_REPLY %s %s\n",
+ ENCTYPE_NAME(type),
+ (iv_status == CFB64_IV_OK ? "CFB64_IV_OK" :
+ "CFB64_IV_BAD"));
+#endif /* ENCRYPT_NAMES */
+ /* Update the state of the decryption negotiation */
+ encr_data.decrypt.state = lstate;
+
+ if (lstate == ENCR_STATE_NOT_READY)
+ encr_data.decrypt.autoflag = 0;
+ else {
+ if (lstate == ENCR_STATE_OK && encr_data.decrypt.autoflag)
+ encrypt_send_request_start();
+ }
+ if (enc_debug)
+ (void) fprintf(stderr,
+ "\t(encrypt_is) final DECRYPT state = %d\n",
+ encr_data.decrypt.state);
+}
+
+/*
+ * encrypt_send_encrypt_is
+ *
+ * Tell the client what encryption we will use
+ * and what our IV will be.
+ */
+static int
+encrypt_send_encrypt_is()
+{
+ register int lstate;
+ krb5_error_code kret;
+ uchar_t sbbuf[MAXOPTLEN], *p;
+ int i;
+
+ lstate = encr_data.encrypt.state;
+
+ if (encr_data.encrypt.type == ENCTYPE_NULL) {
+ /*
+ * Haven't received ENCRYPT SUPPORT yet or we couldn't agree
+ * on a cipher.
+ */
+ return (lstate);
+ }
+
+ /*
+ * - Create a random DES key
+ *
+ * - DES ECB encrypt
+ * encrypt the IV using itself as the key.
+ *
+ * - Send response
+ * IAC SB TELOPT_ENCRYPT ENCRYPT_IS CFB64 FB64_IV [ feed block ]
+ * IAC SE
+ *
+ */
+ if (lstate == ENCR_STATE_NOT_READY)
+ lstate = ENCR_STATE_IN_PROGRESS;
+ else if ((lstate & ENCR_STATE_NO_SEND_IV) == 0) {
+ if (enc_debug)
+ (void) fprintf(stderr,
+ "\t(encrypt_send_is) IV already sent,"
+ " state = %d\n", lstate);
+ return (lstate);
+ }
+
+ if (!VALIDKEY(encr_data.encrypt.krbdes_key)) {
+ /*
+ * Invalid key, set flag so we try again later
+ * when we get a good one
+ */
+ encr_data.encrypt.need_start = 1;
+ if (enc_debug)
+ (void) fprintf(stderr,
+ "\t(encrypt_send_is) No Key, cannot "
+ "start encryption yet\n");
+ return (lstate);
+ }
+ if (enc_debug)
+ (void) fprintf(stderr,
+ "\t(encrypt_send_is) Creating new feed\n");
+
+ /*
+ * Create a random feed and send it over.
+ *
+ * Use the /dev/[u]random interface to generate
+ * our encryption IV.
+ */
+ kret = getrandom((char *)encr_data.encrypt.ivec, sizeof (Block));
+
+ if (kret) {
+ if (enc_debug)
+ (void) fprintf(stderr,
+ "\t(encrypt_send_is) error from "
+ "getrandom: %d\n", kret);
+ syslog(LOG_ERR, "Failed to create encryption key (err %d)\n");
+ encr_data.encrypt.type = ENCTYPE_NULL;
+ } else {
+ mit_des_fixup_key_parity(encr_data.encrypt.ivec);
+ }
+
+ p = sbbuf;
+ *p++ = IAC;
+ *p++ = SB;
+ *p++ = TELOPT_ENCRYPT;
+ *p++ = ENCRYPT_IS;
+ *p++ = encr_data.encrypt.type;
+ *p++ = CFB64_IV;
+
+ /*
+ * Copy the IV bytes individually so that when a
+ * 255 (telnet IAC) is used, it can be "escaped" by
+ * adding it twice (telnet RFC 854).
+ */
+ for (i = 0; i < sizeof (Block); i++)
+ if ((*p++ = encr_data.encrypt.ivec[i]) == IAC)
+ *p++ = IAC;
+
+ *p++ = IAC;
+ *p++ = SE;
+ write_data_len((const char *)sbbuf, (size_t)(p-sbbuf));
+ netflush();
+
+ if (!kret) {
+ lstate &= ~ENCR_STATE_NO_SEND_IV; /* we sent our IV */
+ lstate &= ~ENCR_STATE_NO_SEND_IV; /* dont need decrypt IV */
+ }
+ encr_data.encrypt.state = lstate;
+
+ if (enc_debug) {
+ int i;
+ (void) fprintf(stderr,
+ "SENT TELOPT_ENCRYPT ENCRYPT_IS %d CFB64_IV ",
+ encr_data.encrypt.type);
+ for (i = 0; i < (p-sbbuf); i++)
+ (void) fprintf(stderr, "%d ", (int)sbbuf[i]);
+ (void) fprintf(stderr, "\n");
+ }
+
+ return (lstate);
+}
+
+/*
+ * stop_stream
+ *
+ * Utility routine to send a CRIOCSTOP ioctl to the
+ * crypto module (cryptmod).
+ */
+static void
+stop_stream(int fd, int dir)
+{
+ struct strioctl crioc;
+ uint32_t stopdir = dir;
+
+ crioc.ic_cmd = CRYPTIOCSTOP;
+ crioc.ic_timout = -1;
+ crioc.ic_len = sizeof (stopdir);
+ crioc.ic_dp = (char *)&stopdir;
+
+ if (ioctl(fd, I_STR, &crioc)) {
+ syslog(LOG_ERR, "Error sending CRYPTIOCSTOP ioctl: %m");
+ }
+}
+
+/*
+ * start_stream
+ *
+ * Utility routine to send a CRYPTIOCSTART ioctl to the
+ * crypto module (cryptmod). This routine may contain optional
+ * payload data that the cryptmod will interpret as bytes that
+ * need to be decrypted and sent back up to the application
+ * via the data stream.
+ */
+static void
+start_stream(int fd, int dir, int datalen, char *data)
+{
+ struct strioctl crioc;
+
+ crioc.ic_cmd = (dir == CRYPT_ENCRYPT ? CRYPTIOCSTARTENC :
+ CRYPTIOCSTARTDEC);
+ crioc.ic_timout = -1;
+ crioc.ic_len = datalen;
+ crioc.ic_dp = data;
+
+ if (ioctl(fd, I_STR, &crioc)) {
+ syslog(LOG_ERR, "Error sending CRYPTIOCSTART ioctl: %m");
+ }
+}
+
+/*
+ * encrypt_start_output
+ *
+ * Tell the other side to start encrypting its data
+ */
+static void
+encrypt_start_output()
+{
+ int lstate;
+ uchar_t *p;
+ uchar_t sbbuf[MAXOPTLEN];
+ struct strioctl crioc;
+ struct cr_info_t cki;
+
+ /*
+ * Initialize crypto and send the ENCRYPT_IS msg
+ */
+ lstate = encrypt_send_encrypt_is();
+
+ if (lstate != ENCR_STATE_OK) {
+ if (enc_debug)
+ (void) fprintf(stderr,
+ "\t(encrypt_start_output) ENCRYPT state "
+ "= %d\n", lstate);
+ return;
+ }
+
+ p = sbbuf;
+
+ *p++ = IAC;
+ *p++ = SB;
+ *p++ = TELOPT_ENCRYPT;
+ *p++ = ENCRYPT_START;
+
+ (void) memcpy(p, encr_data.encrypt.keyid, encr_data.encrypt.keyidlen);
+ p += encr_data.encrypt.keyidlen;
+
+ *p++ = IAC;
+ *p++ = SE;
+
+ /* Flush this data out before we start encrypting */
+ write_data_len((const char *)sbbuf, (int)(p-sbbuf));
+ netflush();
+
+ if (enc_debug)
+ (void) fprintf(stderr, "SENT TELOPT_ENCRYPT ENCRYPT_START %d "
+ "(lstate = %d) data waiting = %d\n",
+ (int)encr_data.encrypt.keyid[0],
+ lstate, nfrontp-nbackp);
+
+ encr_data.encrypt.state = lstate;
+
+ /*
+ * tell crypto module what key to use for encrypting
+ * Note that the ENCRYPT has not yet been enabled, but we
+ * need to first set the crypto key to use.
+ */
+ cki.direction_mask = CRYPT_ENCRYPT;
+
+ if (encr_data.encrypt.type == TELOPT_ENCTYPE_DES_CFB64) {
+ cki.crypto_method = CRYPT_METHOD_DES_CFB;
+ } else {
+ if (enc_debug)
+ (void) fprintf(stderr,
+ "\t(encrypt_start_output) - unknown "
+ "crypto_method %d\n",
+ encr_data.encrypt.type);
+ syslog(LOG_ERR, "unrecognized crypto encrypt method: %d",
+ encr_data.encrypt.type);
+
+ return;
+ }
+
+ /*
+ * If we previously configured this crypto method, we dont want to
+ * overwrite the key or ivec information already given to the crypto
+ * module as it will cause the cipher data between the client and server
+ * to become out of synch and impossible to decipher.
+ */
+ if (encr_data.encrypt.setup == cki.crypto_method) {
+ cki.keylen = 0;
+ cki.iveclen = 0;
+ } else {
+ cki.keylen = DES_BLOCKSIZE;
+ (void) memcpy(cki.key, (void *)encr_data.encrypt.krbdes_key,
+ DES_BLOCKSIZE);
+
+ cki.iveclen = DES_BLOCKSIZE;
+ (void) memcpy(cki.ivec, (void *)encr_data.encrypt.ivec,
+ DES_BLOCKSIZE);
+
+ cki.ivec_usage = IVEC_ONETIME;
+ }
+
+ cki.option_mask = 0;
+
+ /* Stop encrypt side prior to setup so we dont lose data */
+ stop_stream(cryptmod_fd, CRYPT_ENCRYPT);
+
+ crioc.ic_cmd = CRYPTIOCSETUP;
+ crioc.ic_timout = -1;
+ crioc.ic_len = sizeof (struct cr_info_t);
+ crioc.ic_dp = (char *)&cki;
+
+ if (ioctl(cryptmod_fd, I_STR, &crioc)) {
+ perror("ioctl(CRYPTIOCSETUP) [encrypt_start_output] error");
+ } else {
+ /* Setup completed OK */
+ encr_data.encrypt.setup = cki.crypto_method;
+ }
+
+ /*
+ * We do not check for "stuck" data when setting up the
+ * outbound "encrypt" channel. Any data queued prior to
+ * this IOCTL will get processed correctly without our help.
+ */
+ start_stream(cryptmod_fd, CRYPT_ENCRYPT, 0, NULL);
+
+ /*
+ * tell crypto module to start encrypting
+ */
+ if (enc_debug)
+ (void) fprintf(stderr,
+ "\t(encrypt_start_output) Encrypting output\n");
+}
+
+/*
+ * encrypt_request_start
+ *
+ * The client requests that we start encryption immediately after
+ * successful negotiation
+ */
+static void
+encrypt_request_start(void)
+{
+ if (encr_data.encrypt.type == ENCTYPE_NULL) {
+ encr_data.encrypt.autoflag = 1;
+ if (enc_debug)
+ (void) fprintf(stderr, "\t(encrypt_request_start) "
+ "autoencrypt = ON\n");
+ } else {
+ encrypt_start_output();
+ }
+}
+
+/*
+ * encrypt_end
+ *
+ * ENCRYPT END received, stop decrypting the read stream
+ */
+static void
+encrypt_end(int direction)
+{
+ struct cr_info_t cki;
+ struct strioctl crioc;
+ uint32_t stopdir;
+
+ stopdir = (direction == TELNET_DIR_DECRYPT ? CRYPT_DECRYPT :
+ CRYPT_ENCRYPT);
+
+ stop_stream(cryptmod_fd, stopdir);
+
+ /*
+ * Call this function when we wish to disable crypto in
+ * either direction (ENCRYPT or DECRYPT)
+ */
+ cki.direction_mask = (direction == TELNET_DIR_DECRYPT ? CRYPT_DECRYPT :
+ CRYPT_ENCRYPT);
+ cki.crypto_method = CRYPT_METHOD_NONE;
+ cki.option_mask = 0;
+
+ cki.keylen = 0;
+ cki.iveclen = 0;
+
+ crioc.ic_cmd = CRYPTIOCSETUP;
+ crioc.ic_timout = -1;
+ crioc.ic_len = sizeof (cki);
+ crioc.ic_dp = (char *)&cki;
+
+ if (ioctl(cryptmod_fd, I_STR, &crioc)) {
+ perror("ioctl(CRYPTIOCSETUP) [encrypt_end] error");
+ }
+
+ start_stream(cryptmod_fd, stopdir, 0, NULL);
+}
+
+/*
+ * encrypt_request_end
+ *
+ * When we receive a REQEND from the client, it means
+ * that we are supposed to stop encrypting
+ */
+static void
+encrypt_request_end()
+{
+ /*
+ * Tell the other side we are done encrypting
+ */
+
+ write_data("%c%c%c%c%c%c",
+ (uchar_t)IAC,
+ (uchar_t)SB,
+ (uchar_t)TELOPT_ENCRYPT,
+ (uchar_t)ENCRYPT_END,
+ (uchar_t)IAC,
+ (uchar_t)SE);
+ netflush();
+ if (enc_debug)
+ (void) fprintf(stderr, "SENT TELOPT_ENCRYPT ENCRYPT_END\n");
+
+ /*
+ * Turn off encryption of the write stream
+ */
+ encrypt_end(TELNET_DIR_ENCRYPT);
+}
+
+/*
+ * encrypt_send_request_end
+ *
+ * We stop encrypting the write stream and tell the other side about it.
+ */
+static void
+encrypt_send_request_end()
+{
+ write_data("%c%c%c%c%c%c",
+ (uchar_t)IAC,
+ (uchar_t)SB,
+ (uchar_t)TELOPT_ENCRYPT,
+ (uchar_t)ENCRYPT_REQEND,
+ (uchar_t)IAC,
+ (uchar_t)SE);
+ netflush();
+ if (enc_debug)
+ (void) fprintf(stderr, "SENT TELOPT_ENCRYPT ENCRYPT_REQEND\n");
+}
+
+/*
+ * encrypt_start
+ *
+ * The client is going to start sending encrypted data
+ * using the previously negotiated cipher (see what we set
+ * when we did the REPLY in encrypt_is).
+ */
+static void
+encrypt_start(void)
+{
+ struct cr_info_t cki;
+ struct strioctl crioc;
+ int bytes = 0;
+ char *dataptr = NULL;
+
+ if (encr_data.decrypt.type == ENCTYPE_NULL) {
+ if (enc_debug)
+ (void) fprintf(stderr,
+ "\t(encrypt_start) No DECRYPT method "
+ "defined yet\n");
+ encrypt_send_request_end();
+ return;
+ }
+
+ cki.direction_mask = CRYPT_DECRYPT;
+
+ if (encr_data.decrypt.type == TELOPT_ENCTYPE_DES_CFB64) {
+ cki.crypto_method = CRYPT_METHOD_DES_CFB;
+ } else {
+ if (enc_debug)
+ (void) fprintf(stderr,
+ "\t(encrypt_start) - unknown "
+ "crypto_method %d\n", encr_data.decrypt.type);
+
+ syslog(LOG_ERR, "unrecognized crypto decrypt method: %d",
+ encr_data.decrypt.type);
+
+ return;
+ }
+
+ /*
+ * Don't overwrite previously configured key and ivec info
+ */
+ if (encr_data.decrypt.setup != cki.crypto_method) {
+ (void) memcpy(cki.key, (void *)encr_data.decrypt.krbdes_key,
+ DES_BLOCKSIZE);
+ (void) memcpy(cki.ivec, (void *)encr_data.decrypt.ivec,
+ DES_BLOCKSIZE);
+
+ cki.keylen = DES_BLOCKSIZE;
+ cki.iveclen = DES_BLOCKSIZE;
+ cki.ivec_usage = IVEC_ONETIME;
+ } else {
+ cki.keylen = 0;
+ cki.iveclen = 0;
+ }
+ cki.option_mask = 0;
+
+ stop_stream(cryptmod_fd, CRYPT_DECRYPT);
+
+ crioc.ic_cmd = CRYPTIOCSETUP;
+ crioc.ic_timout = -1;
+ crioc.ic_len = sizeof (struct cr_info_t);
+ crioc.ic_dp = (char *)&cki;
+
+ if (ioctl(cryptmod_fd, I_STR, &crioc)) {
+ syslog(LOG_ERR, "ioctl(CRYPTIOCSETUP) [encrypt_start] "
+ "error: %m");
+ } else {
+ encr_data.decrypt.setup = cki.crypto_method;
+ }
+ if (enc_debug)
+ (void) fprintf(stderr,
+ "\t(encrypt_start) called CRYPTIOCSETUP for "
+ "decrypt side\n");
+
+ /*
+ * Read any data stuck between the cryptmod and the application
+ * so we can pass it back down to be properly decrypted after
+ * this operation finishes.
+ */
+ if (ioctl(cryptmod_fd, I_NREAD, &bytes) < 0) {
+ syslog(LOG_ERR, "I_NREAD returned error %m");
+ bytes = 0;
+ }
+
+ /*
+ * Any data which was read AFTER the ENCRYPT START message
+ * must be sent back down to be decrypted properly.
+ *
+ * 'ncc' is the number of bytes that have been read but
+ * not yet processed by the telnet state machine.
+ *
+ * 'bytes' is the number of bytes waiting to be read from
+ * the stream.
+ *
+ * If either one is a positive value, then those bytes
+ * must be pulled up and sent back down to be decrypted.
+ */
+ if (ncc || bytes) {
+ drainstream(bytes);
+ if (enc_debug)
+ (void) fprintf(stderr,
+ "\t(encrypt_start) after drainstream, "
+ "ncc=%d bytes = %d\n", ncc, bytes);
+ bytes += ncc;
+ dataptr = netip;
+ }
+
+ start_stream(cryptmod_fd, CRYPT_DECRYPT, bytes, dataptr);
+
+ /*
+ * The bytes putback into the stream are no longer
+ * available to be read by the server, so adjust the
+ * counter accordingly.
+ */
+ ncc = 0;
+ netip = netibuf;
+ (void) memset(netip, 0, netibufsize);
+
+#ifdef ENCRYPT_NAMES
+ if (enc_debug) {
+ (void) fprintf(stderr,
+ "\t(encrypt_start) Start DECRYPT using %s\n",
+ ENCTYPE_NAME(encr_data.decrypt.type));
+ }
+#endif /* ENCRYPT_NAMES */
+}
+
+/*
+ * encrypt_support
+ *
+ * Called when we recieve the TELOPT_ENCRYPT SUPPORT [ encr type list ]
+ * message from a client.
+ *
+ * Choose an agreeable method (DES_CFB64) and
+ * respond with TELOPT_ENCRYPT ENCRYPT_IS [ desired crypto method ]
+ *
+ * from: RFC 2946
+ */
+static void
+encrypt_support(char *data, int cnt)
+{
+ int lstate = ENCR_STATE_NOT_READY;
+ int type, use_type = 0;
+
+ while (cnt-- > 0 && use_type == 0) {
+ type = *data++;
+#ifdef ENCRYPT_NAMES
+ if (enc_debug)
+ (void) fprintf(stderr,
+ "RCVD ENCRYPT SUPPORT %s\n",
+ ENCTYPE_NAME(type));
+#endif /* ENCRYPT_NAMES */
+ /*
+ * Prefer CFB64
+ */
+ if (type == TELOPT_ENCTYPE_DES_CFB64) {
+ use_type = type;
+ }
+ }
+ encr_data.encrypt.type = use_type;
+
+ if (use_type != TELOPT_ENCTYPE_NULL &&
+ authenticated != NULL && authenticated != &NoAuth &&
+ auth_status != AUTH_REJECT) {
+
+ /* Authenticated -> have session key -> send ENCRYPT IS */
+ lstate = encrypt_send_encrypt_is();
+ if (lstate == ENCR_STATE_OK)
+ encrypt_start_output();
+ } else if (use_type == TELOPT_ENCTYPE_NULL) {
+ if (enc_debug)
+ (void) fprintf(stderr,
+ "\t(encrypt_support) Cannot agree "
+ "on crypto algorithm, output encryption "
+ "disabled.\n");
+
+ /*
+ * Cannot agree on crypto algorithm
+ * RFC 2946 sez:
+ * send "IAC SB ENCRYPT IS NULL IAC SE"
+ * optionally, also send IAC WONT ENCRYPT
+ */
+ write_data("%c%c%c%c%c%c%c",
+ (uchar_t)IAC,
+ (uchar_t)SB,
+ (uchar_t)TELOPT_ENCRYPT,
+ (uchar_t)ENCRYPT_IS,
+ (uchar_t)TELOPT_ENCTYPE_NULL,
+ (uchar_t)IAC,
+ (uchar_t)SE);
+ send_wont(TELOPT_ENCRYPT);
+ netflush();
+ if (enc_debug)
+ (void) fprintf(stderr,
+ "SENT TELOPT_ENCRYPT ENCRYPT_IS "
+ "[NULL]\n");
+
+ remopts[TELOPT_ENCRYPT] = OPT_NO;
+ }
+ settimer(encr_support);
+}
+
+/*
+ * encrypt_send_keyid
+ *
+ * Sent the key id we will use to the client
+ */
+static void
+encrypt_send_keyid(int dir, uchar_t *keyid, int keylen, boolean_t saveit)
+{
+ uchar_t sbbuf[128], *p;
+
+ p = sbbuf;
+
+ *p++ = IAC;
+ *p++ = SB;
+ *p++ = TELOPT_ENCRYPT;
+ *p++ = (dir == TELNET_DIR_ENCRYPT ? ENCRYPT_ENC_KEYID :
+ ENCRYPT_DEC_KEYID);
+ if (saveit) {
+ if (enc_debug)
+ (void) fprintf(stderr,
+ "\t(send_keyid) store %d byte %s keyid\n",
+ keylen,
+ (dir == TELNET_DIR_ENCRYPT ? "ENCRYPT" :
+ "DECRYPT"));
+
+ if (dir == TELNET_DIR_ENCRYPT) {
+ (void) memcpy(encr_data.encrypt.keyid, keyid, keylen);
+ encr_data.encrypt.keyidlen = keylen;
+ } else {
+ (void) memcpy(encr_data.decrypt.keyid, keyid, keylen);
+ encr_data.decrypt.keyidlen = keylen;
+ }
+ }
+ (void) memcpy(p, keyid, keylen);
+ p += keylen;
+
+ *p++ = IAC;
+ *p++ = SE;
+ write_data_len((const char *)sbbuf, (size_t)(p-sbbuf));
+ netflush();
+
+ if (enc_debug)
+ (void) fprintf(stderr, "SENT TELOPT_ENCRYPT %s %d\n",
+ (dir == TELNET_DIR_ENCRYPT ? "ENC_KEYID" :
+ "DEC_KEYID"), keyid[0]);
+}
+
+/*
+ * encrypt_reply
+ *
+ * When we receive the TELOPT_ENCRYPT REPLY [crtype] CFB64_IV_OK IAC SE
+ * message, process it accordingly.
+ * If the vector is acceptable, tell client we are encrypting and
+ * enable encryption on our write stream.
+ *
+ * Negotiate the KEYID next..
+ * RFC 2946, 2952
+ */
+static void
+encrypt_reply(char *data, int len)
+{
+ uchar_t type = (uchar_t)(*data++);
+ uchar_t result = (uchar_t)(*data);
+ int lstate;
+
+#ifdef ENCRYPT_NAMES
+ if (enc_debug)
+ (void) fprintf(stderr,
+ "\t(encrypt_reply) ENCRYPT REPLY %s %s [len=%d]\n",
+ ENCRYPT_NAME(type),
+ (result == CFB64_IV_OK ? "CFB64_IV_OK" :
+ "CFB64_IV_BAD"), len);
+#endif /* ENCRYPT_NAMES */
+
+ lstate = encr_data.encrypt.state;
+ if (enc_debug)
+ (void) fprintf(stderr,
+ "\t(encrypt_reply) initial ENCRYPT state = %d\n",
+ lstate);
+ switch (result) {
+ case CFB64_IV_OK:
+ if (lstate == ENCR_STATE_NOT_READY)
+ lstate = ENCR_STATE_IN_PROGRESS;
+ lstate &= ~ENCR_STATE_NO_RECV_IV; /* we got the IV */
+ lstate &= ~ENCR_STATE_NO_SEND_IV; /* we dont need to send IV */
+
+ /*
+ * The correct response here is to send the encryption key id
+ * RFC 2752.
+ *
+ * Send keyid 0 to indicate that we will just use default
+ * keys.
+ */
+ encrypt_send_keyid(TELNET_DIR_ENCRYPT, (uchar_t *)"\0", 1, 1);
+
+ break;
+ case CFB64_IV_BAD:
+ /*
+ * Clear the ivec
+ */
+ (void) memset(encr_data.encrypt.ivec, 0, sizeof (Block));
+ lstate = ENCR_STATE_NOT_READY;
+ break;
+ default:
+ if (enc_debug)
+ (void) fprintf(stderr,
+ "\t(encrypt_reply) Got unknown IV value in "
+ "REPLY message\n");
+ lstate = ENCR_STATE_NOT_READY;
+ break;
+ }
+
+ encr_data.encrypt.state = lstate;
+ if (lstate == ENCR_STATE_NOT_READY) {
+ encr_data.encrypt.autoflag = 0;
+ encr_data.encrypt.type = ENCTYPE_NULL;
+ if (enc_debug)
+ (void) fprintf(stderr,
+ "\t(encrypt_reply) encrypt.autoflag = "
+ "OFF\n");
+ } else {
+ encr_data.encrypt.type = type;
+ if ((lstate == ENCR_STATE_OK) && encr_data.encrypt.autoflag)
+ encrypt_start_output();
+ }
+
+ if (enc_debug)
+ (void) fprintf(stderr,
+ "\t(encrypt_reply) ENCRYPT final state = %d\n",
+ lstate);
+}
+
+static void
+encrypt_set_keyid_state(uchar_t *keyid, int *keyidlen, int dir)
+{
+ int lstate;
+
+ lstate = (dir == TELNET_DIR_ENCRYPT ? encr_data.encrypt.state :
+ encr_data.decrypt.state);
+
+ if (enc_debug)
+ (void) fprintf(stderr,
+ "\t(set_keyid_state) %s initial state = %d\n",
+ (dir == TELNET_DIR_ENCRYPT ? "ENCRYPT" :
+ "DECRYPT"), lstate);
+
+ /*
+ * Currently, we only support using the default keyid,
+ * so it should be an error if the len > 1 or the keyid != 0.
+ */
+ if (*keyidlen != 1 || (*keyid != '\0')) {
+ if (enc_debug)
+ (void) fprintf(stderr,
+ "\t(set_keyid_state) unexpected keyid: "
+ "len=%d value=%d\n", *keyidlen, *keyid);
+ *keyidlen = 0;
+ syslog(LOG_ERR, "rcvd unexpected keyid %d - only keyid of 0 "
+ "is supported", *keyid);
+ } else {
+ /*
+ * We move to the "IN_PROGRESS" state.
+ */
+ if (lstate == ENCR_STATE_NOT_READY)
+ lstate = ENCR_STATE_IN_PROGRESS;
+ /*
+ * Clear the NO_KEYID bit because we now have a valid keyid
+ */
+ lstate &= ~ENCR_STATE_NO_KEYID;
+ }
+
+ if (enc_debug)
+ (void) fprintf(stderr,
+ "\t(set_keyid_state) %s final state = %d\n",
+ (dir == TELNET_DIR_ENCRYPT ? "ENCRYPT" :
+ "DECRYPT"), lstate);
+
+ if (dir == TELNET_DIR_ENCRYPT)
+ encr_data.encrypt.state = lstate;
+ else
+ encr_data.decrypt.state = lstate;
+}
+
+/*
+ * encrypt_keyid
+ *
+ * Set the keyid value in the key_info structure.
+ * if necessary send a response to the sender
+ */
+static void
+encrypt_keyid(uchar_t *newkeyid, int *keyidlen, uchar_t *keyid,
+ int len, int dir)
+{
+ if (len > TELNET_MAXNUMKEYS) {
+ if (enc_debug)
+ (void) fprintf(stderr,
+ "\t(keyid) keylen too big (%d)\n", len);
+ return;
+ }
+
+ if (enc_debug) {
+ (void) fprintf(stderr, "\t(keyid) set KEYID for %s len = %d\n",
+ (dir == TELNET_DIR_ENCRYPT ? "ENCRYPT" :
+ "DECRYPT"), len);
+ }
+
+ if (len == 0) {
+ if (*keyidlen == 0) {
+ if (enc_debug)
+ (void) fprintf(stderr,
+ "\t(keyid) Got 0 length keyid - "
+ "failure\n");
+ return;
+ }
+ *keyidlen = 0;
+ encrypt_set_keyid_state(newkeyid, keyidlen, dir);
+
+ } else if (len != *keyidlen || memcmp(keyid, newkeyid, len)) {
+ if (enc_debug)
+ (void) fprintf(stderr,
+ "\t(keyid) Setting new key (%d bytes)\n",
+ len);
+
+ *keyidlen = len;
+ (void) memcpy(newkeyid, keyid, len);
+
+ encrypt_set_keyid_state(newkeyid, keyidlen, dir);
+ } else {
+ encrypt_set_keyid_state(newkeyid, keyidlen, dir);
+
+ if (enc_debug)
+ (void) fprintf(stderr,
+ "\t(keyid) %s Key already in place,"
+ "state = %d autoflag=%d\n",
+ (dir == TELNET_DIR_ENCRYPT ? "ENCRYPT" : "DECRYPT"),
+ (dir == TELNET_DIR_ENCRYPT ? encr_data.encrypt.state:
+ encr_data.decrypt.state),
+ (dir == TELNET_DIR_ENCRYPT ?
+ encr_data.encrypt.autoflag:
+ encr_data.decrypt.autoflag));
+
+ /* key already in place */
+ if ((encr_data.encrypt.state == ENCR_STATE_OK) &&
+ dir == TELNET_DIR_ENCRYPT && encr_data.encrypt.autoflag) {
+ encrypt_start_output();
+ }
+ return;
+ }
+
+ if (enc_debug)
+ (void) fprintf(stderr, "\t(keyid) %s final state = %d\n",
+ (dir == TELNET_DIR_ENCRYPT ? "ENCRYPT" :
+ "DECRYPT"),
+ (dir == TELNET_DIR_ENCRYPT ?
+ encr_data.encrypt.state :
+ encr_data.decrypt.state));
+
+ encrypt_send_keyid(dir, newkeyid, *keyidlen, 0);
+}
+
+/*
+ * encrypt_enc_keyid
+ *
+ * We received the ENC_KEYID message from a client indicating that
+ * the client wishes to verify that the indicated keyid maps to a
+ * valid key.
+ */
+static void
+encrypt_enc_keyid(char *data, int cnt)
+{
+ /*
+ * Verify the decrypt keyid is valid
+ */
+ encrypt_keyid(encr_data.decrypt.keyid, &encr_data.decrypt.keyidlen,
+ (uchar_t *)data, cnt, TELNET_DIR_DECRYPT);
+}
+
+/*
+ * encrypt_dec_keyid
+ *
+ * We received the DEC_KEYID message from a client indicating that
+ * the client wants to verify that the indicated keyid maps to a valid key.
+ */
+static void
+encrypt_dec_keyid(char *data, int cnt)
+{
+ encrypt_keyid(encr_data.encrypt.keyid, &encr_data.encrypt.keyidlen,
+ (uchar_t *)data, cnt, TELNET_DIR_ENCRYPT);
+}
+
+/*
+ * encrypt_session_key
+ *
+ * Store the session key in the encryption data record
+ */
+static void
+encrypt_session_key(Session_Key *key, cipher_info_t *cinfo)
+{
+ if (key == NULL || key->type != SK_DES) {
+ if (enc_debug)
+ (void) fprintf(stderr,
+ "\t(session_key) Cannot set krb5 "
+ "session key (unknown type = %d)\n",
+ key ? key->type : -1);
+ }
+ if (enc_debug)
+ (void) fprintf(stderr,
+ "\t(session_key) Settting session key "
+ "for server\n");
+
+ /* store the key in the cipher info data struct */
+ (void) memcpy(cinfo->krbdes_key, (void *)key->data, sizeof (Block));
+
+ /*
+ * Now look to see if we still need to send the key and start
+ * encrypting.
+ *
+ * If so, go ahead an call it now that we have the key.
+ */
+ if (cinfo->need_start) {
+ if (encrypt_send_encrypt_is() == ENCR_STATE_OK) {
+ cinfo->need_start = 0;
+ }
+ }
+}
+
+/*
+ * new_env
+ *
+ * Used to add an environment variable and value to the
+ * linked list structure.
+ */
+static int
+new_env(const char *name, const char *value)
+{
+ struct envlist *env;
+
+ env = malloc(sizeof (struct envlist));
+ if (env == NULL)
+ return (1);
+ if ((env->name = strdup(name)) == NULL) {
+ free(env);
+ return (1);
+ }
+ if ((env->value = strdup(value)) == NULL) {
+ free(env->name);
+ free(env);
+ return (1);
+ }
+ env->delete = 0;
+ env->next = envlist_head;
+ envlist_head = env;
+ return (0);
+}
+
+/*
+ * del_env
+ *
+ * Used to delete an environment variable from the linked list
+ * structure. We just set a flag because we will delete the list
+ * anyway before we exec login.
+ */
+static int
+del_env(const char *name)
+{
+ struct envlist *env;
+
+ for (env = envlist_head; env; env = env->next) {
+ if (strcmp(env->name, name) == 0) {
+ env->delete = 1;
+ break;
+ }
+ }
+ return (0);
+}
+
+static int
+issock(int fd)
+{
+ struct stat stats;
+
+ if (fstat(fd, &stats) == -1)
+ return (0);
+ return (S_ISSOCK(stats.st_mode));
+}
+
+/*
+ * audit_telnet_settid stores the terminal id while it is still
+ * available. Subsequent calls to adt_load_hostname() return
+ * the id which is stored here.
+ */
+static int
+audit_telnet_settid(int sock) {
+ adt_session_data_t *ah;
+ adt_termid_t *termid;
+ int rc;
+
+ if ((rc = adt_start_session(&ah, NULL, 0)) == 0) {
+ if ((rc = adt_load_termid(sock, &termid)) == 0) {
+ if ((rc = adt_set_user(ah, ADT_NO_AUDIT,
+ ADT_NO_AUDIT, 0, ADT_NO_AUDIT,
+ termid, ADT_SETTID)) == 0)
+ (void) adt_set_proc(ah);
+ free(termid);
+ }
+ (void) adt_end_session(ah);
+ }
+ return (rc);
+}
+
+/* ARGSUSED */
+int
+main(int argc, char *argv[])
+{
+ struct sockaddr_storage from;
+ int on = 1;
+ socklen_t fromlen;
+ int issocket;
+#if defined(DEBUG)
+ ushort_t porttouse = 0;
+ boolean_t standalone = 0;
+#endif /* defined(DEBUG) */
+ extern char *optarg;
+ char c;
+ int tos = -1;
+
+ while ((c = getopt(argc, argv, TELNETD_OPTS DEBUG_OPTS)) != -1) {
+ switch (c) {
+#if defined(DEBUG)
+ case 'p':
+ /*
+ * note: alternative port number only used in
+ * standalone mode.
+ */
+ porttouse = atoi(optarg);
+ standalone = 1;
+ break;
+ case 'e':
+ enc_debug = 1;
+ break;
+#endif /* DEBUG */
+ case 'a':
+ if (strcasecmp(optarg, "none") == 0) {
+ auth_level = 0;
+ } else if (strcasecmp(optarg, "user") == 0) {
+ auth_level = AUTH_USER;
+ } else if (strcasecmp(optarg, "valid") == 0) {
+ auth_level = AUTH_VALID;
+ } else if (strcasecmp(optarg, "off") == 0) {
+ auth_level = -1;
+ negotiate_auth_krb5 = 0;
+ } else if (strcasecmp(optarg, "debug") == 0) {
+ auth_debug = 1;
+ } else {
+ syslog(LOG_ERR,
+ "unknown authentication level specified "
+ "with \'-a\' option (%s)", optarg);
+ auth_level = AUTH_USER;
+ }
+ break;
+ case 'X':
+ /* disable authentication negotiation */
+ negotiate_auth_krb5 = 0;
+ break;
+ case 'R':
+ case 'M':
+ if (optarg != NULL) {
+ int ret = krb5_init();
+ if (ret) {
+ syslog(LOG_ERR,
+ "Unable to use Kerberos V5 as "
+ "requested, exiting");
+ exit(1);
+ }
+ krb5_set_default_realm(telnet_context, optarg);
+ syslog(LOG_NOTICE,
+ "using %s as default KRB5 realm", optarg);
+ }
+ break;
+ case 'S':
+ telnet_srvtab = (char *)strdup(optarg);
+ break;
+ case 'E': /* disable automatic encryption */
+ negotiate_encrypt = B_FALSE;
+ break;
+ case 'U':
+ resolve_hostname = 1;
+ break;
+ case 's':
+ if (optarg == NULL || (tos = atoi(optarg)) < 0 ||
+ tos > 255) {
+ syslog(LOG_ERR, "telnetd: illegal tos value: "
+ "%s\n", optarg);
+ } else {
+ if (tos < 0)
+ tos = 020;
+ }
+ break;
+ case 'h':
+ show_hostinfo = 0;
+ break;
+ default:
+ syslog(LOG_ERR, "telnetd: illegal cmd line option %c",
+ c);
+ break;
+ }
+ }
+
+ netibufsize = BUFSIZ;
+ if (!(netibuf = (char *)malloc(netibufsize)))
+ syslog(LOG_ERR, "netibuf malloc failed\n");
+ (void) memset(netibuf, 0, netibufsize);
+ netip = netibuf;
+
+#if defined(DEBUG)
+ if (standalone) {
+ int s, ns, foo;
+ struct servent *sp;
+ static struct sockaddr_in6 sin6 = { AF_INET6 };
+ int option = 1;
+
+ if (porttouse) {
+ sin6.sin6_port = htons(porttouse);
+ } else {
+ sp = getservbyname("telnet", "tcp");
+ if (sp == 0) {
+ (void) fprintf(stderr,
+ "telnetd: tcp/telnet: "
+ "unknown service\n");
+ exit(EXIT_FAILURE);
+ }
+ sin6.sin6_port = sp->s_port;
+ }
+
+ s = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
+ if (s < 0) {
+ perror("telnetd: socket");
+ exit(EXIT_FAILURE);
+ }
+ if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&option,
+ sizeof (option)) == -1)
+ perror("setsockopt SO_REUSEADDR");
+ if (bind(s, (struct sockaddr *)&sin6, sizeof (sin6)) < 0) {
+ perror("bind");
+ exit(EXIT_FAILURE);
+ }
+ if (listen(s, 32) < 0) {
+ perror("listen");
+ exit(EXIT_FAILURE);
+ }
+
+ /* automatically reap all child processes */
+ (void) signal(SIGCHLD, SIG_IGN);
+
+ for (;;) {
+ pid_t pid;
+
+ foo = sizeof (sin6);
+ ns = accept(s, (struct sockaddr *)&sin6, &foo);
+ if (ns < 0) {
+ perror("accept");
+ exit(EXIT_FAILURE);
+ }
+ pid = fork();
+ if (pid == -1) {
+ perror("fork");
+ exit(EXIT_FAILURE);
+ }
+ if (pid == 0) {
+ (void) dup2(ns, 0);
+ (void) close(s);
+ (void) signal(SIGCHLD, SIG_DFL);
+ break;
+ }
+ (void) close(ns);
+ }
+ }
+#endif /* defined(DEBUG) */
+
+ openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON);
+
+ issocket = issock(0);
+ if (!issocket)
+ fatal(0, "stdin is not a socket file descriptor");
+
+ fromlen = (socklen_t)sizeof (from);
+ (void) memset((char *)&from, 0, sizeof (from));
+ if (getpeername(0, (struct sockaddr *)&from, &fromlen)
+ < 0) {
+ (void) fprintf(stderr, "%s: ", argv[0]);
+ perror("getpeername");
+ _exit(EXIT_FAILURE);
+ }
+
+ if (audit_telnet_settid(0)) { /* set terminal ID */
+ (void) fprintf(stderr, "%s: ", argv[0]);
+ perror("audit");
+ exit(EXIT_FAILURE);
+ }
+
+ if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, (const char *)&on,
+ sizeof (on)) < 0) {
+ syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
+ }
+
+ /*
+ * Set the TOS value
+ */
+ if (tos != -1 &&
+ setsockopt(0, IPPROTO_IP, IP_TOS,
+ (char *)&tos, sizeof (tos)) < 0 &&
+ errno != ENOPROTOOPT) {
+ syslog(LOG_ERR, "setsockopt (IP_TOS %d): %m", tos);
+ }
+
+ if (setsockopt(net, SOL_SOCKET, SO_OOBINLINE, (char *)&on,
+ sizeof (on)) < 0) {
+ syslog(LOG_WARNING, "setsockopt (SO_OOBINLINE): %m");
+ }
+
+ /* set the default PAM service name */
+ (void) strcpy(pam_svc_name, "telnet");
+
+ doit(0, &from);
+ /* NOTREACHED */
+ return (EXIT_SUCCESS);
+}
+
+static char *terminaltype = 0;
+
+/*
+ * ttloop
+ *
+ * A small subroutine to flush the network output buffer, get some data
+ * from the network, and pass it through the telnet state machine. We
+ * also flush the pty input buffer (by dropping its data) if it becomes
+ * too full.
+ */
+static void
+ttloop(void)
+{
+ if (nfrontp-nbackp) {
+ netflush();
+ }
+read_again:
+ ncc = read(net, netibuf, netibufsize);
+ if (ncc < 0) {
+ if (errno == EINTR)
+ goto read_again;
+ syslog(LOG_INFO, "ttloop: read: %m");
+ exit(EXIT_FAILURE);
+ } else if (ncc == 0) {
+ syslog(LOG_INFO, "ttloop: peer closed connection\n");
+ exit(EXIT_FAILURE);
+ }
+
+ netip = netibuf;
+ telrcv(); /* state machine */
+ if (ncc > 0) {
+ pfrontp = pbackp = ptyobuf;
+ telrcv();
+ }
+}
+
+static void
+send_do(int option)
+{
+ write_data("%c%c%c", (uchar_t)IAC, (uchar_t)DO, (uchar_t)option);
+}
+
+static void
+send_will(int option)
+{
+ write_data("%c%c%c", (uchar_t)IAC, (uchar_t)WILL, (uchar_t)option);
+}
+
+static void
+send_wont(int option)
+{
+ write_data("%c%c%c", (uchar_t)IAC, (uchar_t)WONT, (uchar_t)option);
+}
+
+
+/*
+ * getauthtype
+ *
+ * Negotiate automatic authentication, is possible.
+ */
+static int
+getauthtype(char *username, int *len)
+{
+ int init_status = -1;
+
+ init_status = krb5_init();
+
+ if (auth_level == -1 || init_status != 0) {
+ remopts[TELOPT_AUTHENTICATION] = OPT_NO;
+ myopts[TELOPT_AUTHENTICATION] = OPT_NO;
+ negotiate_auth_krb5 = B_FALSE;
+ negotiate_encrypt = B_FALSE;
+ return (AUTH_REJECT);
+ }
+
+ if (init_status == 0 && auth_level != -1) {
+ if (negotiate_auth_krb5) {
+ /*
+ * Negotiate Authentication FIRST
+ */
+ send_do(TELOPT_AUTHENTICATION);
+ remopts[TELOPT_AUTHENTICATION] =
+ OPT_YES_BUT_ALWAYS_LOOK;
+ }
+ while (sequenceIs(authopt, getauth))
+ ttloop();
+
+ if (remopts[TELOPT_AUTHENTICATION] == OPT_YES) {
+ /*
+ * Request KRB5 Mutual authentication and if that fails,
+ * KRB5 1-way client authentication
+ */
+ uchar_t sbbuf[MAXOPTLEN], *p;
+ p = sbbuf;
+ *p++ = (uchar_t)IAC;
+ *p++ = (uchar_t)SB;
+ *p++ = (uchar_t)TELOPT_AUTHENTICATION;
+ *p++ = (uchar_t)TELQUAL_SEND;
+ if (negotiate_auth_krb5) {
+ *p++ = (uchar_t)AUTHTYPE_KERBEROS_V5;
+ *p++ = (uchar_t)(AUTH_WHO_CLIENT |
+ AUTH_HOW_MUTUAL |
+ AUTH_ENCRYPT_ON);
+ *p++ = (uchar_t)AUTHTYPE_KERBEROS_V5;
+ *p++ = (uchar_t)(AUTH_WHO_CLIENT |
+ AUTH_HOW_MUTUAL);
+ *p++ = (uchar_t)AUTHTYPE_KERBEROS_V5;
+ *p++ = (uchar_t)(AUTH_WHO_CLIENT|
+ AUTH_HOW_ONE_WAY);
+ } else {
+ *p++ = (uchar_t)AUTHTYPE_NULL;
+ }
+ *p++ = (uchar_t)IAC;
+ *p++ = (uchar_t)SE;
+
+ write_data_len((const char *)sbbuf,
+ (size_t)(p - sbbuf));
+ netflush();
+ if (auth_debug)
+ (void) fprintf(stderr,
+ "SENT TELOPT_AUTHENTICATION "
+ "[data]\n");
+
+ /* auth_wait returns the authentication level */
+ /* status = auth_wait(username, len); */
+ while (sequenceIs(authdone, getauth))
+ ttloop();
+ /*
+ * Now check to see if the user is valid or not
+ */
+ if (authenticated == NULL || authenticated == &NoAuth)
+ auth_status = AUTH_REJECT;
+ else {
+ /*
+ * We cant be VALID until the user status is
+ * checked.
+ */
+ if (auth_status == AUTH_VALID)
+ auth_status = AUTH_USER;
+
+ if (authenticated->AuthName ==
+ AUTHTYPE_KERBEROS_V5)
+ auth_status = krb5_user_status(
+ username, *len, auth_status);
+ }
+ }
+ }
+ return (auth_status);
+}
+
+static void
+getencrtype(void)
+{
+ if (krb5_privacy_allowed() && negotiate_encrypt) {
+ if (myopts[TELOPT_ENCRYPT] != OPT_YES) {
+ if (!sent_will_encrypt) {
+ send_will(TELOPT_ENCRYPT);
+ sent_will_encrypt = B_TRUE;
+ }
+ if (enc_debug)
+ (void) fprintf(stderr, "SENT WILL ENCRYPT\n");
+ }
+ if (remopts[TELOPT_ENCRYPT] != OPT_YES) {
+ if (!sent_do_encrypt) {
+ send_do(TELOPT_ENCRYPT);
+ sent_do_encrypt = B_TRUE;
+ }
+ if (enc_debug)
+ (void) fprintf(stderr, "SENT DO ENCRYPT\n");
+ }
+ myopts[TELOPT_ENCRYPT] = OPT_YES;
+
+ while (sequenceIs(encropt, getencr))
+ ttloop();
+
+ if (auth_status != AUTH_REJECT &&
+ remopts[TELOPT_ENCRYPT] == OPT_YES &&
+ myopts[TELOPT_ENCRYPT] == OPT_YES) {
+
+ if (sent_encrypt_support == B_FALSE) {
+ write_data("%c%c%c%c%c%c%c",
+ (uchar_t)IAC,
+ (uchar_t)SB,
+ (uchar_t)TELOPT_ENCRYPT,
+ (uchar_t)ENCRYPT_SUPPORT,
+ (uchar_t)TELOPT_ENCTYPE_DES_CFB64,
+ (uchar_t)IAC,
+ (uchar_t)SE);
+
+ netflush();
+ }
+ /*
+ * Now wait for a response to these messages before
+ * continuing...
+ * Look for TELOPT_ENCRYPT suboptions
+ */
+ while (sequenceIs(encr_support, getencr))
+ ttloop();
+ }
+ } else {
+ /* Dont need responses to these, so dont wait for them */
+ settimer(encropt);
+ remopts[TELOPT_ENCRYPT] = OPT_NO;
+ myopts[TELOPT_ENCRYPT] = OPT_NO;
+ }
+
+}
+
+/*
+ * getterminaltype
+ *
+ * Ask the other end to send along its terminal type.
+ * Output is the variable terminaltype filled in.
+ */
+static void
+getterminaltype(void)
+{
+ /*
+ * The remote side may have already sent this info, so
+ * dont ask for these options if the other side already
+ * sent the information.
+ */
+ if (sequenceIs(ttypeopt, getterminal)) {
+ send_do(TELOPT_TTYPE);
+ remopts[TELOPT_TTYPE] = OPT_YES_BUT_ALWAYS_LOOK;
+ }
+
+ if (sequenceIs(nawsopt, getterminal)) {
+ send_do(TELOPT_NAWS);
+ remopts[TELOPT_NAWS] = OPT_YES_BUT_ALWAYS_LOOK;
+ }
+
+ if (sequenceIs(xdisplocopt, getterminal)) {
+ send_do(TELOPT_XDISPLOC);
+ remopts[TELOPT_XDISPLOC] = OPT_YES_BUT_ALWAYS_LOOK;
+ }
+
+ if (sequenceIs(environopt, getterminal)) {
+ send_do(TELOPT_NEW_ENVIRON);
+ remopts[TELOPT_NEW_ENVIRON] = OPT_YES_BUT_ALWAYS_LOOK;
+ }
+
+ if (sequenceIs(oenvironopt, getterminal)) {
+ send_do(TELOPT_OLD_ENVIRON);
+ remopts[TELOPT_OLD_ENVIRON] = OPT_YES_BUT_ALWAYS_LOOK;
+ }
+
+ /* make sure encryption is started here */
+ while (auth_status != AUTH_REJECT &&
+ authenticated != &NoAuth && authenticated != NULL &&
+ remopts[TELOPT_ENCRYPT] == OPT_YES &&
+ encr_data.encrypt.autoflag &&
+ encr_data.encrypt.state != ENCR_STATE_OK) {
+ if (enc_debug)
+ (void) fprintf(stderr, "getterminaltype() forcing encrypt\n");
+ ttloop();
+ }
+
+ if (enc_debug) {
+ (void) fprintf(stderr, "getterminaltype() encryption %sstarted\n",
+ encr_data.encrypt.state == ENCR_STATE_OK ? "" : "not ");
+ }
+
+ while (sequenceIs(ttypeopt, getterminal) ||
+ sequenceIs(nawsopt, getterminal) ||
+ sequenceIs(xdisplocopt, getterminal) ||
+ sequenceIs(environopt, getterminal) ||
+ sequenceIs(oenvironopt, getterminal)) {
+ ttloop();
+ }
+
+
+ if (remopts[TELOPT_TTYPE] == OPT_YES) {
+ static uchar_t sbbuf[] = { (uchar_t)IAC, (uchar_t)SB,
+ (uchar_t)TELOPT_TTYPE, (uchar_t)TELQUAL_SEND,
+ (uchar_t)IAC, (uchar_t)SE };
+
+ write_data_len((const char *)sbbuf, sizeof (sbbuf));
+ }
+ if (remopts[TELOPT_XDISPLOC] == OPT_YES) {
+ static uchar_t sbbuf[] = { (uchar_t)IAC, (uchar_t)SB,
+ (uchar_t)TELOPT_XDISPLOC, (uchar_t)TELQUAL_SEND,
+ (uchar_t)IAC, (uchar_t)SE };
+
+ write_data_len((const char *)sbbuf, sizeof (sbbuf));
+ }
+ if (remopts[TELOPT_NEW_ENVIRON] == OPT_YES) {
+ static uchar_t sbbuf[] = { (uchar_t)IAC, (uchar_t)SB,
+ (uchar_t)TELOPT_NEW_ENVIRON, (uchar_t)TELQUAL_SEND,
+ (uchar_t)IAC, (uchar_t)SE };
+
+ write_data_len((const char *)sbbuf, sizeof (sbbuf));
+ }
+ if (remopts[TELOPT_OLD_ENVIRON] == OPT_YES) {
+ static uchar_t sbbuf[] = { (uchar_t)IAC, (uchar_t)SB,
+ (uchar_t)TELOPT_OLD_ENVIRON, (uchar_t)TELQUAL_SEND,
+ (uchar_t)IAC, (uchar_t)SE };
+
+ write_data_len((const char *)sbbuf, sizeof (sbbuf));
+ }
+
+ if (remopts[TELOPT_TTYPE] == OPT_YES) {
+ while (sequenceIs(ttypesubopt, getterminal)) {
+ ttloop();
+ }
+ }
+ if (remopts[TELOPT_XDISPLOC] == OPT_YES) {
+ while (sequenceIs(xdisplocsubopt, getterminal)) {
+ ttloop();
+ }
+ }
+ if (remopts[TELOPT_NEW_ENVIRON] == OPT_YES) {
+ while (sequenceIs(environsubopt, getterminal)) {
+ ttloop();
+ }
+ }
+ if (remopts[TELOPT_OLD_ENVIRON] == OPT_YES) {
+ while (sequenceIs(oenvironsubopt, getterminal)) {
+ ttloop();
+ }
+ }
+ init_neg_done = 1;
+}
+
+pid_t pid;
+
+/*
+ * Get a pty, scan input lines.
+ */
+static void
+doit(int f, struct sockaddr_storage *who)
+{
+ char *host;
+ char host_name[MAXHOSTNAMELEN];
+ int p, t, tt;
+ struct sgttyb b;
+ int ptmfd; /* fd of logindmux connected to pty */
+ int netfd; /* fd of logindmux connected to netf */
+ struct stat buf;
+ struct protocol_arg telnetp;
+ struct strioctl telnetmod;
+ struct envlist *env, *next;
+ int nsize = 0;
+ char abuf[INET6_ADDRSTRLEN];
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+ socklen_t wholen;
+ char username[MAXUSERNAMELEN];
+ int len;
+ uchar_t passthru;
+ char *slavename;
+
+ if ((p = open("/dev/ptmx", O_RDWR | O_NOCTTY)) == -1) {
+ fatalperror(f, "open /dev/ptmx", errno);
+ }
+ if (grantpt(p) == -1)
+ fatal(f, "could not grant slave pty");
+ if (unlockpt(p) == -1)
+ fatal(f, "could not unlock slave pty");
+ if ((slavename = ptsname(p)) == NULL)
+ fatal(f, "could not enable slave pty");
+ (void) dup2(f, 0);
+ if ((t = open(slavename, O_RDWR | O_NOCTTY)) == -1)
+ fatal(f, "could not open slave pty");
+ if (ioctl(t, I_PUSH, "ptem") == -1)
+ fatalperror(f, "ioctl I_PUSH ptem", errno);
+ if (ioctl(t, I_PUSH, "ldterm") == -1)
+ fatalperror(f, "ioctl I_PUSH ldterm", errno);
+ if (ioctl(t, I_PUSH, "ttcompat") == -1)
+ fatalperror(f, "ioctl I_PUSH ttcompat", errno);
+
+ line = slavename;
+
+ pty = t;
+
+ if (ioctl(t, TIOCGETP, &b) == -1)
+ syslog(LOG_INFO, "ioctl TIOCGETP pty t: %m\n");
+ b.sg_flags = O_CRMOD|O_XTABS|O_ANYP;
+ /* XXX - ispeed and ospeed must be non-zero */
+ b.sg_ispeed = B38400;
+ b.sg_ospeed = B38400;
+ if (ioctl(t, TIOCSETN, &b) == -1)
+ syslog(LOG_INFO, "ioctl TIOCSETN pty t: %m\n");
+ if (ioctl(pty, TIOCGETP, &b) == -1)
+ syslog(LOG_INFO, "ioctl TIOCGETP pty pty: %m\n");
+ b.sg_flags &= ~O_ECHO;
+ if (ioctl(pty, TIOCSETN, &b) == -1)
+ syslog(LOG_INFO, "ioctl TIOCSETN pty pty: %m\n");
+
+ if (who->ss_family == AF_INET) {
+ char *addrbuf = NULL;
+ char *portbuf = NULL;
+
+ sin = (struct sockaddr_in *)who;
+ wholen = sizeof (struct sockaddr_in);
+
+ addrbuf = (char *)malloc(wholen);
+ if (addrbuf == NULL)
+ fatal(f, "Cannot alloc memory for address info\n");
+ portbuf = (char *)malloc(sizeof (sin->sin_port));
+ if (portbuf == NULL) {
+ free(addrbuf);
+ fatal(f, "Cannot alloc memory for port info\n");
+ }
+
+ (void) memcpy(addrbuf, (const void *)&sin->sin_addr, wholen);
+ (void) memcpy(portbuf, (const void *)&sin->sin_port,
+ sizeof (sin->sin_port));
+
+ if (rsaddr.contents != NULL)
+ free(rsaddr.contents);
+
+ rsaddr.contents = (krb5_octet *)addrbuf;
+ rsaddr.length = wholen;
+ rsaddr.addrtype = ADDRTYPE_INET;
+
+ if (rsport.contents != NULL)
+ free(rsport.contents);
+
+ rsport.contents = (krb5_octet *)portbuf;
+ rsport.length = sizeof (sin->sin_port);
+ rsport.addrtype = ADDRTYPE_IPPORT;
+ } else if (who->ss_family == AF_INET6) {
+ struct in_addr ipv4_addr;
+ char *addrbuf = NULL;
+ char *portbuf = NULL;
+
+ sin6 = (struct sockaddr_in6 *)who;
+ wholen = sizeof (struct sockaddr_in6);
+
+ IN6_V4MAPPED_TO_INADDR(&sin6->sin6_addr,
+ &ipv4_addr);
+
+ addrbuf = (char *)malloc(wholen);
+ if (addrbuf == NULL)
+ fatal(f, "Cannot alloc memory for address info\n");
+
+ portbuf = (char *)malloc(sizeof (sin6->sin6_port));
+ if (portbuf == NULL) {
+ free(addrbuf);
+ fatal(f, "Cannot alloc memory for port info\n");
+ }
+
+ (void) memcpy((void *) addrbuf,
+ (const void *)&ipv4_addr,
+ wholen);
+ /*
+ * If we already used rsaddr.contents, free the previous
+ * buffer.
+ */
+ if (rsaddr.contents != NULL)
+ free(rsaddr.contents);
+
+ rsaddr.contents = (krb5_octet *)addrbuf;
+ rsaddr.length = sizeof (ipv4_addr);
+ rsaddr.addrtype = ADDRTYPE_INET;
+
+ (void) memcpy((void *) portbuf, (const void *)&sin6->sin6_port,
+ sizeof (sin6->sin6_port));
+
+ if (rsport.contents != NULL)
+ free(rsport.contents);
+
+ rsport.contents = (krb5_octet *)portbuf;
+ rsport.length = sizeof (sin6->sin6_port);
+ rsport.addrtype = ADDRTYPE_IPPORT;
+ } else {
+ syslog(LOG_ERR, "unknown address family %d\n",
+ who->ss_family);
+ fatal(f, "getpeername: unknown address family\n");
+ }
+
+ if (getnameinfo((const struct sockaddr *) who, wholen, host_name,
+ sizeof (host_name), NULL, 0, 0) == 0) {
+ host = host_name;
+ } else {
+ /*
+ * If the '-U' option was given on the cmd line, we must
+ * be able to lookup the hostname
+ */
+ if (resolve_hostname) {
+ fatal(f, "Couldn't resolve your address into a "
+ "host name.\r\nPlease contact your net "
+ "administrator");
+ }
+
+ if (who->ss_family == AF_INET6) {
+ if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
+ struct in_addr ipv4_addr;
+
+ IN6_V4MAPPED_TO_INADDR(&sin6->sin6_addr,
+ &ipv4_addr);
+ host = (char *)inet_ntop(AF_INET,
+ &ipv4_addr, abuf, sizeof (abuf));
+ } else {
+ host = (char *)inet_ntop(AF_INET6,
+ &sin6->sin6_addr, abuf,
+ sizeof (abuf));
+ }
+ } else if (who->ss_family == AF_INET) {
+ host = (char *)inet_ntop(AF_INET,
+ &sin->sin_addr, abuf, sizeof (abuf));
+ }
+ }
+ /*
+ * Note that sockmod has to be removed since readstream assumes
+ * a "raw" TPI endpoint (e.g. it uses getmsg).
+ */
+ if (removemod(f, "sockmod") < 0)
+ fatalperror(f, "couldn't remove sockmod", errno);
+
+ encrypt_init();
+
+ /*
+ * Push the crypto module on the stream before 'telmod' so it
+ * can encrypt/decrypt without interfering with telmod functionality
+ * We must push it now because many of the crypto options negotiated
+ * initially must be saved in the crypto module (via IOCTL calls).
+ */
+ if (ioctl(f, I_PUSH, "cryptmod") < 0)
+ fatalperror(f, "ioctl I_PUSH cryptmod", errno);
+
+ cryptmod_fd = f;
+ /*
+ * gotta set the encryption clock now because it is often negotiated
+ * immediately by the client, and if we wait till after we negotiate
+ * auth, it will be out of whack with when the WILL/WONT ENCRYPT
+ * option is received.
+ */
+ settimer(getencr);
+
+ /*
+ * get terminal type.
+ */
+ username[0] = '\0';
+ len = sizeof (username);
+
+ settimer(getterminal);
+ settimer(getauth);
+ /*
+ * Exchange TELOPT_AUTHENTICATE options per RFC 2941/2942
+ */
+ auth_status = getauthtype(username, &len);
+ /*
+ * Exchange TELOPT_ENCRYPT options per RFC 2946
+ */
+ getencrtype();
+ getterminaltype();
+
+ if (ioctl(f, I_PUSH, "telmod") < 0)
+ fatalperror(f, "ioctl I_PUSH telmod", errno);
+
+ /*
+ * Make sure telmod will pass unrecognized IOCTLs to cryptmod
+ */
+ passthru = 1;
+
+ telnetmod.ic_cmd = CRYPTPASSTHRU;
+ telnetmod.ic_timout = -1;
+ telnetmod.ic_len = sizeof (uchar_t);
+ telnetmod.ic_dp = (char *)&passthru;
+
+ if (ioctl(f, I_STR, &telnetmod) < 0)
+ fatal(f, "ioctl CRPASSTHRU failed\n");
+
+ if (!ncc)
+ netip = netibuf;
+
+ /*
+ * readstream will do a getmsg till it receives M_PROTO type
+ * T_DATA_REQ from telnetmodopen(). This signals that all data
+ * in-flight before telmod was pushed has been received at the
+ * stream head.
+ */
+ while ((nsize = readstream(f, netibuf, ncc + netip - netibuf)) > 0) {
+ ncc += nsize;
+ }
+
+ if (nsize < 0) {
+ fatalperror(f, "readstream failed\n", errno);
+ }
+
+ /*
+ * open logindmux drivers and link them with network and ptm
+ * file descriptors.
+ */
+ if ((ptmfd = open("/dev/logindmux", O_RDWR)) == -1) {
+ fatalperror(f, "open /dev/logindmux", errno);
+ }
+ if ((netfd = open("/dev/logindmux", O_RDWR)) == -1) {
+ fatalperror(f, "open /dev/logindmux", errno);
+ }
+
+ if (ioctl(ptmfd, I_LINK, p) < 0)
+ fatal(f, "ioctl I_LINK of /dev/ptmx failed\n");
+ if (ioctl(netfd, I_LINK, f) < 0)
+ fatal(f, "ioctl I_LINK of tcp connection failed\n");
+
+ /*
+ * Figure out the device number of ptm's mux fd, and pass that
+ * to the net's mux.
+ */
+ if (fstat(ptmfd, &buf) < 0) {
+ fatalperror(f, "fstat ptmfd failed", errno);
+ }
+ telnetp.dev = buf.st_rdev;
+ telnetp.flag = 0;
+
+ telnetmod.ic_cmd = LOGDMX_IOC_QEXCHANGE;
+ telnetmod.ic_timout = -1;
+ telnetmod.ic_len = sizeof (struct protocol_arg);
+ telnetmod.ic_dp = (char *)&telnetp;
+
+ if (ioctl(netfd, I_STR, &telnetmod) < 0)
+ fatal(netfd, "ioctl LOGDMX_IOC_QEXCHANGE of netfd failed\n");
+
+ /*
+ * Figure out the device number of the net's mux fd, and pass that
+ * to the ptm's mux.
+ */
+ if (fstat(netfd, &buf) < 0) {
+ fatalperror(f, "fstat netfd failed", errno);
+ }
+ telnetp.dev = buf.st_rdev;
+ telnetp.flag = 1;
+
+ telnetmod.ic_cmd = LOGDMX_IOC_QEXCHANGE;
+ telnetmod.ic_timout = -1;
+ telnetmod.ic_len = sizeof (struct protocol_arg);
+ telnetmod.ic_dp = (char *)&telnetp;
+
+ if (ioctl(ptmfd, I_STR, &telnetmod) < 0)
+ fatal(netfd, "ioctl LOGDMX_IOC_QEXCHANGE of ptmfd failed\n");
+
+ net = netfd;
+ master = ptmfd;
+ cryptmod_fd = netfd;
+
+ /*
+ * Show banner that getty never gave, but
+ * only if the user did not automatically authenticate.
+ */
+ if (getenv("USER") == '\0' && auth_status < AUTH_USER)
+ showbanner();
+
+ /*
+ * If the user automatically authenticated with Kerberos
+ * we must set the service name that PAM will use. We
+ * need to do it BEFORE the child fork so that 'cleanup'
+ * in the parent can call the PAM cleanup stuff with the
+ * same PAM service that /bin/login will use to authenticate
+ * this session.
+ */
+ if (auth_level >= 0 && auth_status >= AUTH_USER &&
+ (AuthenticatingUser != NULL) && strlen(AuthenticatingUser)) {
+ (void) strcpy(pam_svc_name, "ktelnet");
+ }
+ /*
+ * Request to do suppress go ahead.
+ *
+ * Send this before sending the TELOPT_ECHO stuff below because
+ * some clients (MIT KRB5 telnet) have quirky 'kludge mode' support
+ * that has them turn off local echo mode if SGA is not received first.
+ * This also has the odd side-effect of causing the client to enable
+ * encryption and then immediately disable it during the ECHO option
+ * negotiations. Its just better to to SGA first now that we support
+ * encryption.
+ */
+ if (!myopts[TELOPT_SGA]) {
+ dooption(TELOPT_SGA);
+ }
+
+ /*
+ * Pretend we got a DO ECHO from the client if we have not
+ * yet negotiated the ECHO.
+ */
+ if (!myopts[TELOPT_ECHO]) {
+ dooption(TELOPT_ECHO);
+ }
+
+ /*
+ * Is the client side a 4.2 (NOT 4.3) system? We need to know this
+ * because 4.2 clients are unable to deal with TCP urgent data.
+ *
+ * To find out, we send out a "DO ECHO". If the remote system
+ * answers "WILL ECHO" it is probably a 4.2 client, and we note
+ * that fact ("WILL ECHO" ==> that the client will echo what
+ * WE, the server, sends it; it does NOT mean that the client will
+ * echo the terminal input).
+ */
+ send_do(TELOPT_ECHO);
+ remopts[TELOPT_ECHO] = OPT_YES_BUT_ALWAYS_LOOK;
+
+ if ((pid = fork()) < 0)
+ fatalperror(netfd, "fork", errno);
+ if (pid)
+ telnet(net, master);
+ /*
+ * The child process needs to be the session leader
+ * and have the pty as its controlling tty. Thus we need
+ * to re-open the slave side of the pty no without
+ * the O_NOCTTY flag that we have been careful to
+ * use up to this point.
+ */
+ (void) setsid();
+
+ tt = open(line, O_RDWR);
+ if (tt < 0)
+ fatalperror(netfd, line, errno);
+ (void) close(netfd);
+ (void) close(ptmfd);
+ (void) close(f);
+ (void) close(p);
+ (void) close(t);
+ if (tt != 0)
+ (void) dup2(tt, 0);
+ if (tt != 1)
+ (void) dup2(tt, 1);
+ if (tt != 2)
+ (void) dup2(tt, 2);
+ if (tt > 2)
+ (void) close(tt);
+
+ if (terminaltype)
+ (void) local_setenv("TERM", terminaltype+5, 1);
+ /*
+ * -h : pass on name of host.
+ * WARNING: -h is accepted by login if and only if
+ * getuid() == 0.
+ * -p : don't clobber the environment (so terminal type stays set).
+ */
+ {
+ /* System V login expects a utmp entry to already be there */
+ struct utmpx ut;
+ (void) memset((char *)&ut, 0, sizeof (ut));
+ (void) strncpy(ut.ut_user, ".telnet", sizeof (ut.ut_user));
+ (void) strncpy(ut.ut_line, line, sizeof (ut.ut_line));
+ ut.ut_pid = getpid();
+ ut.ut_id[0] = 't';
+ ut.ut_id[1] = (char)SC_WILDC;
+ ut.ut_id[2] = (char)SC_WILDC;
+ ut.ut_id[3] = (char)SC_WILDC;
+ ut.ut_type = LOGIN_PROCESS;
+ ut.ut_exit.e_termination = 0;
+ ut.ut_exit.e_exit = 0;
+ (void) time(&ut.ut_tv.tv_sec);
+ if (makeutx(&ut) == NULL)
+ syslog(LOG_INFO, "in.telnetd:\tmakeutx failed");
+ }
+
+ /*
+ * Load in the cached environment variables and either
+ * set/unset them in the environment.
+ */
+ for (next = envlist_head; next; ) {
+ env = next;
+ if (env->delete)
+ (void) local_unsetenv(env->name);
+ else
+ (void) local_setenv(env->name, env->value, 1);
+ free(env->name);
+ free(env->value);
+ next = env->next;
+ free(env);
+ }
+
+ if (!username || !username[0])
+ auth_status = AUTH_REJECT; /* we dont know who this is */
+
+ /* If the current auth status is less than the required level, exit */
+ if (auth_status < auth_level) {
+ fatal(net, "Authentication failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /*
+ * If AUTH_VALID (proper authentication REQUIRED and we have
+ * a krb5_name), exec '/bin/login', make sure it uses the
+ * correct PAM service name (pam_svc_name). If possible,
+ * make sure the krb5 authenticated user's name (krb5_name)
+ * is in the PAM REPOSITORY for krb5.
+ */
+ if (auth_level >= 0 &&
+ (auth_status == AUTH_VALID || auth_status == AUTH_USER) &&
+ ((krb5_name != NULL) && strlen(krb5_name)) &&
+ ((AuthenticatingUser != NULL) && strlen(AuthenticatingUser))) {
+ (void) execl(LOGIN_PROGRAM, "login",
+ "-p",
+ "-d", slavename,
+ "-h", host,
+ "-u", krb5_name,
+ "-s", pam_svc_name,
+ "-R", KRB5_REPOSITORY_NAME,
+ AuthenticatingUser, 0);
+ } else if (auth_level >= 0 &&
+ auth_status >= AUTH_USER &&
+ (((AuthenticatingUser != NULL) && strlen(AuthenticatingUser)) ||
+ getenv("USER"))) {
+ /*
+ * If we only know the name but not the principal,
+ * login will have to authenticate further.
+ */
+ (void) execl(LOGIN_PROGRAM, "login",
+ "-p",
+ "-d", slavename,
+ "-h", host,
+ "-s", pam_svc_name,
+ (AuthenticatingUser != NULL ? AuthenticatingUser :
+ getenv("USER")),
+ 0);
+
+ } else /* default, no auth. info available, login does it all */ {
+ (void) execl(LOGIN_PROGRAM, "login",
+ "-p", "-h", host, "-d", slavename,
+ getenv("USER"), 0);
+ }
+
+ fatalperror(netfd, LOGIN_PROGRAM, errno);
+ /*NOTREACHED*/
+}
+
+static void
+fatal(int f, char *msg)
+{
+ char buf[BUFSIZ];
+
+ (void) snprintf(buf, sizeof (buf), "telnetd: %s.\r\n", msg);
+ (void) write(f, buf, strlen(buf));
+ exit(EXIT_FAILURE);
+ /*NOTREACHED*/
+}
+
+static void
+fatalperror(int f, char *msg, int errnum)
+{
+ char buf[BUFSIZ];
+
+ (void) snprintf(buf, sizeof (buf),
+ "%s: %s\r\n", msg, strerror(errnum));
+ fatal(f, buf);
+ /*NOTREACHED*/
+}
+
+/*
+ * Main loop. Select from pty and network, and
+ * hand data to telnet receiver finite state machine
+ * when it receives telnet protocol. Regular data
+ * flow between pty and network takes place through
+ * inkernel telnet streams module (telmod).
+ */
+static void
+telnet(int net, int master)
+{
+ int on = 1;
+ char mode;
+ struct strioctl telnetmod;
+ int nsize = 0;
+ char binary_in = 0;
+ char binary_out = 0;
+
+ if (ioctl(net, FIONBIO, &on) == -1)
+ syslog(LOG_INFO, "ioctl FIONBIO net: %m\n");
+ if (ioctl(master, FIONBIO, &on) == -1)
+ syslog(LOG_INFO, "ioctl FIONBIO pty p: %m\n");
+ (void) signal(SIGTSTP, SIG_IGN);
+ (void) signal(SIGCHLD, (void (*)())cleanup);
+ (void) setpgrp();
+
+ /*
+ * Call telrcv() once to pick up anything received during
+ * terminal type negotiation.
+ */
+ telrcv();
+
+ netflush();
+ ptyflush();
+
+ for (;;) {
+ fd_set ibits, obits, xbits;
+ int c;
+
+ if (ncc < 0)
+ break;
+
+ FD_ZERO(&ibits);
+ FD_ZERO(&obits);
+ FD_ZERO(&xbits);
+
+ /*
+ * If we couldn't flush all our output to the network,
+ * keep checking for when we can.
+ */
+ if (nfrontp - nbackp)
+ FD_SET(net, &obits);
+ /*
+ * Never look for input if there's still
+ * stuff in the corresponding output buffer
+ */
+ if (pfrontp - pbackp) {
+ FD_SET(master, &obits);
+ } else {
+ FD_SET(net, &ibits);
+ }
+ if (!SYNCHing) {
+ FD_SET(net, &xbits);
+ }
+
+#define max(x, y) (((x) < (y)) ? (y) : (x))
+
+ /*
+ * make an ioctl to telnet module (net side) to send
+ * binary mode of telnet daemon. binary_in and
+ * binary_out are 0 if not in binary mode.
+ */
+ if (binary_in != myopts[TELOPT_BINARY] ||
+ binary_out != remopts[TELOPT_BINARY]) {
+
+ mode = 0;
+ if (myopts[TELOPT_BINARY] != OPT_NO)
+ mode |= TEL_BINARY_IN;
+
+ if (remopts[TELOPT_BINARY] != OPT_NO)
+ mode |= TEL_BINARY_OUT;
+
+ telnetmod.ic_cmd = TEL_IOC_MODE;
+ telnetmod.ic_timout = -1;
+ telnetmod.ic_len = 1;
+ telnetmod.ic_dp = &mode;
+
+ syslog(LOG_DEBUG, "TEL_IOC_MODE binary has changed\n");
+
+ if (ioctl(net, I_STR, &telnetmod) < 0)
+ fatal(net, "ioctl TEL_IOC_MODE failed\n");
+ binary_in = myopts[TELOPT_BINARY];
+ binary_out = remopts[TELOPT_BINARY];
+ }
+ if (state == TS_DATA) {
+ if ((nfrontp == nbackp) &&
+ (pfrontp == pbackp)) {
+ if (ioctl(net, I_NREAD, &nsize) < 0)
+ fatalperror(net,
+ "ioctl I_NREAD failed\n", errno);
+ if (nsize)
+ drainstream(nsize);
+
+ /*
+ * make an ioctl to reinsert remaining data at
+ * streamhead. After this, ioctl reenables the
+ * telnet lower put queue. This queue was
+ * noenabled by telnet module after sending
+ * protocol/urgent data to telnetd.
+ */
+
+ telnetmod.ic_cmd = TEL_IOC_ENABLE;
+ telnetmod.ic_timout = -1;
+ if (ncc || nsize) {
+ telnetmod.ic_len = ncc + nsize;
+ telnetmod.ic_dp = netip;
+ } else {
+ telnetmod.ic_len = 0;
+ telnetmod.ic_dp = NULL;
+ }
+ if (ioctl(net, I_STR, &telnetmod) < 0)
+ fatal(net, "ioctl TEL_IOC_ENABLE \
+ failed\n");
+
+ telmod_init_done = B_TRUE;
+
+ netip = netibuf;
+ (void) memset(netibuf, 0, netibufsize);
+
+ ncc = 0;
+ }
+ } else {
+ /*
+ * state not changed to TS_DATA and hence, more to read
+ * send ioctl to get one more message block.
+ */
+ telnetmod.ic_cmd = TEL_IOC_GETBLK;
+ telnetmod.ic_timout = -1;
+ telnetmod.ic_len = 0;
+ telnetmod.ic_dp = NULL;
+
+ if (ioctl(net, I_STR, &telnetmod) < 0)
+ fatal(net, "ioctl TEL_IOC_GETBLK failed\n");
+ }
+
+ if ((c = select(max(net, master) + 1, &ibits, &obits, &xbits,
+ (struct timeval *)0)) < 1) {
+ if (c == -1) {
+ if (errno == EINTR) {
+ continue;
+ }
+ }
+ (void) sleep(5);
+ continue;
+ }
+
+ /*
+ * Any urgent data?
+ */
+ if (FD_ISSET(net, &xbits)) {
+ SYNCHing = 1;
+ }
+
+ /*
+ * Something to read from the network...
+ */
+ if (FD_ISSET(net, &ibits)) {
+ ncc = read(net, netibuf, netibufsize);
+ if (ncc < 0 && errno == EWOULDBLOCK)
+ ncc = 0;
+ else {
+ if (ncc <= 0) {
+ break;
+ }
+ netip = netibuf;
+ }
+ }
+
+ if (FD_ISSET(net, &obits) && (nfrontp - nbackp) > 0)
+ netflush();
+ if (ncc > 0)
+ telrcv();
+ if (FD_ISSET(master, &obits) && (pfrontp - pbackp) > 0)
+ ptyflush();
+ }
+ cleanup(0);
+}
+
+static void
+telrcv(void)
+{
+ int c;
+
+ while (ncc > 0) {
+ if ((&ptyobuf[BUFSIZ] - pfrontp) < 2)
+ return;
+ c = *netip & 0377;
+ /*
+ * Once we hit data, we want to transition back to
+ * in-kernel processing. However, this code is shared
+ * by getterminaltype()/ttloop() which run before the
+ * in-kernel plumbing is available. So if we are still
+ * processing the initial option negotiation, even TS_DATA
+ * must be processed here.
+ */
+ if (c != IAC && state == TS_DATA && init_neg_done) {
+ break;
+ }
+ netip++;
+ ncc--;
+ switch (state) {
+
+ case TS_CR:
+ state = TS_DATA;
+ /* Strip off \n or \0 after a \r */
+ if ((c == 0) || (c == '\n')) {
+ break;
+ }
+ /* FALLTHRU */
+
+ case TS_DATA:
+ if (c == IAC) {
+ state = TS_IAC;
+ break;
+ }
+ if (inter > 0)
+ break;
+ /*
+ * We map \r\n ==> \r, since
+ * We now map \r\n ==> \r for pragmatic reasons.
+ * Many client implementations send \r\n when
+ * the user hits the CarriageReturn key.
+ *
+ * We USED to map \r\n ==> \n, since \r\n says
+ * that we want to be in column 1 of the next
+ * line.
+ */
+ if (c == '\r' && (myopts[TELOPT_BINARY] == OPT_NO)) {
+ state = TS_CR;
+ }
+ *pfrontp++ = c;
+ break;
+
+ case TS_IAC:
+ switch (c) {
+
+ /*
+ * Send the process on the pty side an
+ * interrupt. Do this with a NULL or
+ * interrupt char; depending on the tty mode.
+ */
+ case IP:
+ interrupt();
+ break;
+
+ case BREAK:
+ sendbrk();
+ break;
+
+ /*
+ * Are You There?
+ */
+ case AYT:
+ write_data_len("\r\n[Yes]\r\n", 9);
+ break;
+
+ /*
+ * Abort Output
+ */
+ case AO: {
+ struct ltchars tmpltc;
+
+ ptyflush(); /* half-hearted */
+ if (ioctl(pty, TIOCGLTC, &tmpltc) == -1)
+ syslog(LOG_INFO,
+ "ioctl TIOCGLTC: %m\n");
+ if (tmpltc.t_flushc != '\377') {
+ *pfrontp++ = tmpltc.t_flushc;
+ }
+ netclear(); /* clear buffer back */
+ write_data("%c%c", (uchar_t)IAC,
+ (uchar_t)DM);
+
+ neturg = nfrontp-1; /* off by one XXX */
+ netflush();
+ netflush(); /* XXX.sparker */
+ break;
+ }
+
+ /*
+ * Erase Character and
+ * Erase Line
+ */
+ case EC:
+ case EL: {
+ struct sgttyb b;
+ char ch;
+
+ ptyflush(); /* half-hearted */
+ if (ioctl(pty, TIOCGETP, &b) == -1)
+ syslog(LOG_INFO,
+ "ioctl TIOCGETP: %m\n");
+ ch = (c == EC) ?
+ b.sg_erase : b.sg_kill;
+ if (ch != '\377') {
+ *pfrontp++ = ch;
+ }
+ break;
+ }
+
+ /*
+ * Check for urgent data...
+ */
+ case DM:
+ break;
+
+ /*
+ * Begin option subnegotiation...
+ */
+ case SB:
+ state = TS_SB;
+ SB_CLEAR();
+ continue;
+
+ case WILL:
+ state = TS_WILL;
+ continue;
+
+ case WONT:
+ state = TS_WONT;
+ continue;
+
+ case DO:
+ state = TS_DO;
+ continue;
+
+ case DONT:
+ state = TS_DONT;
+ continue;
+
+ case IAC:
+ *pfrontp++ = c;
+ break;
+ }
+ state = TS_DATA;
+ break;
+ case TS_SB:
+ if (c == IAC) {
+ state = TS_SE;
+ } else {
+ SB_ACCUM(c);
+ }
+ break;
+ case TS_SE:
+ if (c != SE) {
+ if (c != IAC) {
+ SB_ACCUM((uchar_t)IAC);
+ }
+ SB_ACCUM(c);
+ state = TS_SB;
+
+ } else {
+ SB_TERM();
+ suboption(); /* handle sub-option */
+ state = TS_DATA;
+ }
+ break;
+
+ case TS_WILL:
+ if (remopts[c] != OPT_YES)
+ willoption(c);
+ state = TS_DATA;
+ continue;
+
+ case TS_WONT:
+ if (remopts[c] != OPT_NO)
+ wontoption(c);
+ state = TS_DATA;
+ continue;
+
+ case TS_DO:
+ if (myopts[c] != OPT_YES)
+ dooption(c);
+ state = TS_DATA;
+ continue;
+
+ case TS_DONT:
+ if (myopts[c] != OPT_NO) {
+ dontoption(c);
+ }
+ state = TS_DATA;
+ continue;
+
+ default:
+ syslog(LOG_ERR, "telnetd: panic state=%d\n", state);
+ (void) printf("telnetd: panic state=%d\n", state);
+ exit(EXIT_FAILURE);
+ }
+ }
+}
+
+static void
+willoption(int option)
+{
+ uchar_t *fmt;
+ boolean_t send_reply = B_TRUE;
+
+ switch (option) {
+ case TELOPT_BINARY:
+ mode(O_RAW, 0);
+ fmt = doopt;
+ break;
+
+ case TELOPT_ECHO:
+ not42 = 0; /* looks like a 4.2 system */
+ /*
+ * Now, in a 4.2 system, to break them out of ECHOing
+ * (to the terminal) mode, we need to send a "WILL ECHO".
+ * Kludge upon kludge!
+ */
+ if (myopts[TELOPT_ECHO] == OPT_YES) {
+ dooption(TELOPT_ECHO);
+ }
+ fmt = dont;
+ break;
+ case TELOPT_TTYPE:
+ settimer(ttypeopt);
+ goto common;
+
+ case TELOPT_NAWS:
+ settimer(nawsopt);
+ goto common;
+
+ case TELOPT_XDISPLOC:
+ settimer(xdisplocopt);
+ goto common;
+
+ case TELOPT_NEW_ENVIRON:
+ settimer(environopt);
+ goto common;
+
+ case TELOPT_AUTHENTICATION:
+ settimer(authopt);
+ if (remopts[option] == OPT_NO ||
+ negotiate_auth_krb5 == 0)
+ fmt = dont;
+ else
+ fmt = doopt;
+ break;
+
+ case TELOPT_OLD_ENVIRON:
+ settimer(oenvironopt);
+ goto common;
+common:
+ if (remopts[option] == OPT_YES_BUT_ALWAYS_LOOK) {
+ remopts[option] = OPT_YES;
+ return;
+ }
+ /*FALLTHRU*/
+ case TELOPT_SGA:
+ fmt = doopt;
+ break;
+
+ case TELOPT_TM:
+ fmt = dont;
+ break;
+
+ case TELOPT_ENCRYPT:
+ settimer(encropt); /* got response to do/dont */
+ if (enc_debug)
+ (void) fprintf(stderr,
+ "RCVD IAC WILL TELOPT_ENCRYPT\n");
+ if (krb5_privacy_allowed()) {
+ fmt = doopt;
+ if (sent_do_encrypt)
+ send_reply = B_FALSE;
+ else
+ sent_do_encrypt = B_TRUE;
+ } else {
+ fmt = dont;
+ }
+ break;
+
+ default:
+ fmt = dont;
+ break;
+ }
+ if (fmt == doopt) {
+ remopts[option] = OPT_YES;
+ } else {
+ remopts[option] = OPT_NO;
+ }
+ if (send_reply) {
+ write_data((const char *)fmt, option);
+ netflush();
+ }
+}
+
+static void
+wontoption(int option)
+{
+ uchar_t *fmt;
+ int send_reply = 1;
+
+ switch (option) {
+ case TELOPT_ECHO:
+ not42 = 1; /* doesn't seem to be a 4.2 system */
+ break;
+
+ case TELOPT_BINARY:
+ mode(0, O_RAW);
+ break;
+
+ case TELOPT_TTYPE:
+ settimer(ttypeopt);
+ break;
+
+ case TELOPT_NAWS:
+ settimer(nawsopt);
+ break;
+
+ case TELOPT_XDISPLOC:
+ settimer(xdisplocopt);
+ break;
+
+ case TELOPT_NEW_ENVIRON:
+ settimer(environopt);
+ break;
+
+ case TELOPT_OLD_ENVIRON:
+ settimer(oenvironopt);
+ break;
+
+ case TELOPT_AUTHENTICATION:
+ settimer(authopt);
+ auth_finished(0, AUTH_REJECT);
+ if (auth_debug)
+ (void) fprintf(stderr,
+ "RCVD WONT TELOPT_AUTHENTICATE\n");
+
+ remopts[option] = OPT_NO;
+ send_reply = 0;
+ break;
+
+ case TELOPT_ENCRYPT:
+ if (enc_debug)
+ (void) fprintf(stderr,
+ "RCVD IAC WONT TELOPT_ENCRYPT\n");
+ settimer(encropt); /* got response to will/wont */
+ /*
+ * Remote side cannot send encryption. No reply necessary
+ * Treat this as if "IAC SB ENCRYPT END IAC SE" were
+ * received (RFC 2946) and disable crypto.
+ */
+ encrypt_end(TELNET_DIR_DECRYPT);
+ send_reply = 0;
+ break;
+ }
+
+ fmt = dont;
+ remopts[option] = OPT_NO;
+ if (send_reply) {
+ write_data((const char *)fmt, option);
+ }
+}
+
+/*
+ * We received an "IAC DO ..." message from the client, change our state
+ * to OPT_YES.
+ */
+static void
+dooption(int option)
+{
+ uchar_t *fmt;
+ boolean_t send_reply = B_TRUE;
+
+ switch (option) {
+
+ case TELOPT_TM:
+ fmt = wont;
+ break;
+
+ case TELOPT_ECHO:
+ mode(O_ECHO|O_CRMOD, 0);
+ fmt = will;
+ break;
+
+ case TELOPT_BINARY:
+ mode(O_RAW, 0);
+ fmt = will;
+ break;
+
+ case TELOPT_SGA:
+ fmt = will;
+ break;
+
+ case TELOPT_LOGOUT:
+ /*
+ * Options don't get much easier. Acknowledge the option,
+ * and then clean up and exit.
+ */
+ write_data((const char *)will, option);
+ netflush();
+ cleanup(0);
+ /*NOTREACHED*/
+
+ case TELOPT_ENCRYPT:
+ if (enc_debug)
+ (void) fprintf(stderr, "RCVD DO TELOPT_ENCRYPT\n");
+ settimer(encropt);
+ /*
+ * We received a "DO". This indicates that the other side
+ * wants us to encrypt our data (pending negotiatoin).
+ * reply with "IAC WILL ENCRYPT" if we are able to send
+ * encrypted data.
+ */
+ if (krb5_privacy_allowed() && negotiate_encrypt) {
+ fmt = will;
+ if (sent_will_encrypt)
+ send_reply = B_FALSE;
+ else
+ sent_will_encrypt = B_TRUE;
+ /* return if we already sent "WILL ENCRYPT" */
+ if (myopts[option] == OPT_YES)
+ return;
+ } else {
+ fmt = wont;
+ }
+ break;
+
+ case TELOPT_AUTHENTICATION:
+ if (auth_debug) {
+ (void) fprintf(stderr,
+ "RCVD DO TELOPT_AUTHENTICATION\n");
+ }
+ /*
+ * RFC 2941 - only the server can send
+ * "DO TELOPT_AUTHENTICATION".
+ * if a server receives this, it must respond with WONT...
+ */
+ fmt = wont;
+ break;
+
+ default:
+ fmt = wont;
+ break;
+ }
+ if (fmt == will) {
+ myopts[option] = OPT_YES;
+ } else {
+ myopts[option] = OPT_NO;
+ }
+ if (send_reply) {
+ write_data((const char *)fmt, option);
+ netflush();
+ }
+}
+
+/*
+ * We received an "IAC DONT ..." message from client.
+ * Client does not agree with the option so act accordingly.
+ */
+static void
+dontoption(int option)
+{
+ int send_reply = 1;
+ switch (option) {
+ case TELOPT_ECHO:
+ /*
+ * we should stop echoing, since the client side will be doing
+ * it, but keep mapping CR since CR-LF will be mapped to it.
+ */
+ mode(0, O_ECHO);
+ break;
+
+ case TELOPT_ENCRYPT:
+ if (enc_debug)
+ (void) fprintf(stderr, "RCVD IAC DONT ENCRYPT\n");
+ settimer(encropt);
+ /*
+ * Remote side cannot receive any encrypted data,
+ * so dont send any. No reply necessary.
+ */
+ send_reply = 0;
+ break;
+
+ default:
+ break;
+ }
+
+ myopts[option] = OPT_NO;
+
+ if (send_reply) {
+ write_data((const char *)wont, option);
+ }
+}
+
+/*
+ * suboption()
+ *
+ * Look at the sub-option buffer, and try to be helpful to the other
+ * side.
+ *
+ */
+static void
+suboption(void)
+{
+ int subchar;
+
+ switch (subchar = SB_GET()) {
+ case TELOPT_TTYPE: { /* Yaaaay! */
+ static char terminalname[5+41] = "TERM=";
+
+ settimer(ttypesubopt);
+
+ if (SB_GET() != TELQUAL_IS) {
+ return; /* ??? XXX but, this is the most robust */
+ }
+
+ terminaltype = terminalname+strlen(terminalname);
+
+ while (terminaltype < (terminalname + sizeof (terminalname) -
+ 1) && !SB_EOF()) {
+ int c;
+
+ c = SB_GET();
+ if (isupper(c)) {
+ c = tolower(c);
+ }
+ *terminaltype++ = c; /* accumulate name */
+ }
+ *terminaltype = 0;
+ terminaltype = terminalname;
+ break;
+ }
+
+ case TELOPT_NAWS: {
+ struct winsize ws;
+
+ if (SB_EOF()) {
+ return;
+ }
+ ws.ws_col = SB_GET() << 8;
+ if (SB_EOF()) {
+ return;
+ }
+ ws.ws_col |= SB_GET();
+ if (SB_EOF()) {
+ return;
+ }
+ ws.ws_row = SB_GET() << 8;
+ if (SB_EOF()) {
+ return;
+ }
+ ws.ws_row |= SB_GET();
+ ws.ws_xpixel = 0; ws.ws_ypixel = 0;
+ (void) ioctl(pty, TIOCSWINSZ, &ws);
+ settimer(nawsopt);
+ break;
+ }
+
+ case TELOPT_XDISPLOC: {
+ if (SB_EOF() || SB_GET() != TELQUAL_IS) {
+ return;
+ }
+ settimer(xdisplocsubopt);
+ subpointer[SB_LEN()] = '\0';
+ if ((new_env("DISPLAY", subpointer)) == 1)
+ perror("malloc");
+ break;
+ }
+
+ case TELOPT_NEW_ENVIRON:
+ case TELOPT_OLD_ENVIRON: {
+ int c;
+ char *cp, *varp, *valp;
+
+ if (SB_EOF())
+ return;
+ c = SB_GET();
+ if (c == TELQUAL_IS) {
+ if (subchar == TELOPT_OLD_ENVIRON)
+ settimer(oenvironsubopt);
+ else
+ settimer(environsubopt);
+ } else if (c != TELQUAL_INFO) {
+ return;
+ }
+
+ if (subchar == TELOPT_NEW_ENVIRON) {
+ while (!SB_EOF()) {
+ c = SB_GET();
+ if ((c == NEW_ENV_VAR) || (c == ENV_USERVAR))
+ break;
+ }
+ } else
+ {
+ while (!SB_EOF()) {
+ c = SB_GET();
+ if ((c == env_ovar) || (c == ENV_USERVAR))
+ break;
+ }
+ }
+
+ if (SB_EOF())
+ return;
+
+ cp = varp = (char *)subpointer;
+ valp = 0;
+
+ while (!SB_EOF()) {
+ c = SB_GET();
+ if (subchar == TELOPT_OLD_ENVIRON) {
+ if (c == env_ovar)
+ c = NEW_ENV_VAR;
+ else if (c == env_ovalue)
+ c = NEW_ENV_VALUE;
+ }
+ switch (c) {
+
+ case NEW_ENV_VALUE:
+ *cp = '\0';
+ cp = valp = (char *)subpointer;
+ break;
+
+ case NEW_ENV_VAR:
+ case ENV_USERVAR:
+ *cp = '\0';
+ if (valp) {
+ if ((new_env(varp, valp)) == 1) {
+ perror("malloc");
+ }
+ } else {
+ (void) del_env(varp);
+ }
+ cp = varp = (char *)subpointer;
+ valp = 0;
+ break;
+
+ case ENV_ESC:
+ if (SB_EOF())
+ break;
+ c = SB_GET();
+ /* FALL THROUGH */
+ default:
+ *cp++ = c;
+ break;
+ }
+ }
+ *cp = '\0';
+ if (valp) {
+ if ((new_env(varp, valp)) == 1) {
+ perror("malloc");
+ }
+ } else {
+ (void) del_env(varp);
+ }
+ break;
+ } /* end of case TELOPT_NEW_ENVIRON */
+
+ case TELOPT_AUTHENTICATION:
+ if (SB_EOF())
+ break;
+ switch (SB_GET()) {
+ case TELQUAL_SEND:
+ case TELQUAL_REPLY:
+ /*
+ * These are sent server only and cannot be sent by the
+ * client.
+ */
+ break;
+ case TELQUAL_IS:
+ if (auth_debug)
+ (void) fprintf(stderr,
+ "RCVD AUTHENTICATION IS "
+ "(%d bytes)\n",
+ SB_LEN());
+ if (!auth_negotiated)
+ auth_is((uchar_t *)subpointer, SB_LEN());
+ break;
+ case TELQUAL_NAME:
+ if (auth_debug)
+ (void) fprintf(stderr,
+ "RCVD AUTHENTICATION NAME "
+ "(%d bytes)\n",
+ SB_LEN());
+ if (!auth_negotiated)
+ auth_name((uchar_t *)subpointer, SB_LEN());
+ break;
+ }
+ break;
+
+ case TELOPT_ENCRYPT: {
+ int c;
+ if (SB_EOF())
+ break;
+ c = SB_GET();
+#ifdef ENCRYPT_NAMES
+ if (enc_debug)
+ (void) fprintf(stderr, "RCVD ENCRYPT %s\n",
+ ENCRYPT_NAME(c));
+#endif /* ENCRYPT_NAMES */
+ switch (c) {
+ case ENCRYPT_SUPPORT:
+ encrypt_support(subpointer, SB_LEN());
+ break;
+ case ENCRYPT_IS:
+ encrypt_is((uchar_t *)subpointer, SB_LEN());
+ break;
+ case ENCRYPT_REPLY:
+ (void) encrypt_reply(subpointer, SB_LEN());
+ break;
+ case ENCRYPT_START:
+ encrypt_start();
+ break;
+ case ENCRYPT_END:
+ encrypt_end(TELNET_DIR_DECRYPT);
+ break;
+ case ENCRYPT_REQSTART:
+ encrypt_request_start();
+ break;
+ case ENCRYPT_REQEND:
+ /*
+ * We can always send an REQEND so that we cannot
+ * get stuck encrypting. We should only get this
+ * if we have been able to get in the correct mode
+ * anyhow.
+ */
+ encrypt_request_end();
+ break;
+ case ENCRYPT_ENC_KEYID:
+ encrypt_enc_keyid(subpointer, SB_LEN());
+ break;
+ case ENCRYPT_DEC_KEYID:
+ encrypt_dec_keyid(subpointer, SB_LEN());
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void
+mode(int on, int off)
+{
+ struct termios tios;
+
+ ptyflush();
+ if (tcgetattr(pty, &tios) < 0)
+ syslog(LOG_INFO, "tcgetattr: %m\n");
+
+ if (on & O_RAW) {
+ tios.c_cflag |= CS8;
+ tios.c_iflag &= ~IUCLC;
+ tios.c_lflag &= ~(XCASE|IEXTEN);
+ }
+ if (off & O_RAW) {
+ if ((tios.c_cflag & PARENB) != 0)
+ tios.c_cflag &= ~CS8;
+ tios.c_lflag |= IEXTEN;
+ }
+
+ if (on & O_ECHO)
+ tios.c_lflag |= ECHO;
+ if (off & O_ECHO)
+ tios.c_lflag &= ~ECHO;
+
+ if (on & O_CRMOD) {
+ tios.c_iflag |= ICRNL;
+ tios.c_oflag |= ONLCR;
+ }
+ /*
+ * Because "O_CRMOD" will never be set in "off" we don't have to
+ * handle this case here.
+ */
+
+ if (tcsetattr(pty, TCSANOW, &tios) < 0)
+ syslog(LOG_INFO, "tcsetattr: %m\n");
+}
+
+/*
+ * Send interrupt to process on other side of pty.
+ * If it is in raw mode, just write NULL;
+ * otherwise, write intr char.
+ */
+static void
+interrupt(void)
+{
+ struct sgttyb b;
+ struct tchars tchars;
+
+ ptyflush(); /* half-hearted */
+ if (ioctl(pty, TIOCGETP, &b) == -1)
+ syslog(LOG_INFO, "ioctl TIOCGETP: %m\n");
+ if (b.sg_flags & O_RAW) {
+ *pfrontp++ = '\0';
+ return;
+ }
+ *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ?
+ '\177' : tchars.t_intrc;
+}
+
+/*
+ * Send quit to process on other side of pty.
+ * If it is in raw mode, just write NULL;
+ * otherwise, write quit char.
+ */
+static void
+sendbrk(void)
+{
+ struct sgttyb b;
+ struct tchars tchars;
+
+ ptyflush(); /* half-hearted */
+ (void) ioctl(pty, TIOCGETP, &b);
+ if (b.sg_flags & O_RAW) {
+ *pfrontp++ = '\0';
+ return;
+ }
+ *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ?
+ '\034' : tchars.t_quitc;
+}
+
+static void
+ptyflush(void)
+{
+ int n;
+
+ if ((n = pfrontp - pbackp) > 0)
+ n = write(master, pbackp, n);
+ if (n < 0)
+ return;
+ pbackp += n;
+ if (pbackp == pfrontp)
+ pbackp = pfrontp = ptyobuf;
+}
+
+/*
+ * nextitem()
+ *
+ * Return the address of the next "item" in the TELNET data
+ * stream. This will be the address of the next character if
+ * the current address is a user data character, or it will
+ * be the address of the character following the TELNET command
+ * if the current address is a TELNET IAC ("I Am a Command")
+ * character.
+ */
+
+static char *
+nextitem(char *current)
+{
+ if ((*current&0xff) != IAC) {
+ return (current+1);
+ }
+ switch (*(current+1)&0xff) {
+ case DO:
+ case DONT:
+ case WILL:
+ case WONT:
+ return (current+3);
+ case SB: /* loop forever looking for the SE */
+ {
+ char *look = current+2;
+
+ for (;;) {
+ if ((*look++&0xff) == IAC) {
+ if ((*look++&0xff) == SE) {
+ return (look);
+ }
+ }
+ }
+ }
+ default:
+ return (current+2);
+ }
+}
+
+
+/*
+ * netclear()
+ *
+ * We are about to do a TELNET SYNCH operation. Clear
+ * the path to the network.
+ *
+ * Things are a bit tricky since we may have sent the first
+ * byte or so of a previous TELNET command into the network.
+ * So, we have to scan the network buffer from the beginning
+ * until we are up to where we want to be.
+ *
+ * A side effect of what we do, just to keep things
+ * simple, is to clear the urgent data pointer. The principal
+ * caller should be setting the urgent data pointer AFTER calling
+ * us in any case.
+ */
+static void
+netclear(void)
+{
+ char *thisitem, *next;
+ char *good;
+#define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \
+ ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
+
+ thisitem = netobuf;
+
+ while ((next = nextitem(thisitem)) <= nbackp) {
+ thisitem = next;
+ }
+
+ /* Now, thisitem is first before/at boundary. */
+
+ good = netobuf; /* where the good bytes go */
+
+ while (nfrontp > thisitem) {
+ if (wewant(thisitem)) {
+ int length;
+
+ next = thisitem;
+ do {
+ next = nextitem(next);
+ } while (wewant(next) && (nfrontp > next));
+ length = next-thisitem;
+ (void) memmove(good, thisitem, length);
+ good += length;
+ thisitem = next;
+ } else {
+ thisitem = nextitem(thisitem);
+ }
+ }
+
+ nbackp = netobuf;
+ nfrontp = good; /* next byte to be sent */
+ neturg = 0;
+}
+
+
+/*
+ * netflush
+ * Send as much data as possible to the network,
+ * handling requests for urgent data.
+ */
+static void
+netflush(void)
+{
+ int n;
+
+ if ((n = nfrontp - nbackp) > 0) {
+ /*
+ * if no urgent data, or if the other side appears to be an
+ * old 4.2 client (and thus unable to survive TCP urgent data),
+ * write the entire buffer in non-OOB mode.
+ */
+ if ((neturg == 0) || (not42 == 0)) {
+ n = write(net, nbackp, n); /* normal write */
+ } else {
+ n = neturg - nbackp;
+ /*
+ * In 4.2 (and 4.3) systems, there is some question
+ * about what byte in a sendOOB operation is the "OOB"
+ * data. To make ourselves compatible, we only send ONE
+ * byte out of band, the one WE THINK should be OOB
+ * (though we really have more the TCP philosophy of
+ * urgent data rather than the Unix philosophy of OOB
+ * data).
+ */
+ if (n > 1) {
+ /* send URGENT all by itself */
+ n = write(net, nbackp, n-1);
+ } else {
+ /* URGENT data */
+ n = send_oob(net, nbackp, n);
+ }
+ }
+ }
+ if (n < 0) {
+ if (errno == EWOULDBLOCK)
+ return;
+ /* should blow this guy away... */
+ return;
+ }
+
+ nbackp += n;
+
+ if (nbackp >= neturg) {
+ neturg = 0;
+ }
+ if (nbackp == nfrontp) {
+ nbackp = nfrontp = netobuf;
+ }
+}
+
+/* ARGSUSED */
+static void
+cleanup(int signum)
+{
+ /*
+ * If the TEL_IOC_ENABLE ioctl hasn't completed, then we need to
+ * handle closing differently. We close "net" first and then
+ * "master" in that order. We do close(net) first because
+ * we have no other way to disconnect forwarding between the network
+ * and master. So by issuing the close()'s we ensure that no further
+ * data rises from TCP. A more complex fix would be adding proper
+ * support for throwing a "stop" switch for forwarding data between
+ * logindmux peers. It's possible to block in the close of the tty
+ * while the network still receives data and the telmod module is
+ * TEL_STOPPED. A denial-of-service attack generates this case,
+ * see 4102102.
+ */
+
+ if (!telmod_init_done) {
+ (void) close(net);
+ (void) close(master);
+ }
+ rmut();
+
+ exit(EXIT_FAILURE);
+}
+
+static void
+rmut(void)
+{
+ pam_handle_t *pamh;
+ struct utmpx *up;
+ char user[sizeof (up->ut_user) + 1];
+ char ttyn[sizeof (up->ut_line) + 1];
+ char rhost[sizeof (up->ut_host) + 1];
+
+ /* while cleaning up don't allow disruption */
+ (void) signal(SIGCHLD, SIG_IGN);
+
+ setutxent();
+ while (up = getutxent()) {
+ if (up->ut_pid == pid) {
+ if (up->ut_type == DEAD_PROCESS) {
+ /*
+ * Cleaned up elsewhere.
+ */
+ break;
+ }
+
+ /*
+ * call pam_close_session if login changed
+ * the utmpx user entry from type LOGIN_PROCESS
+ * to type USER_PROCESS, which happens
+ * after pam_open_session is called.
+ */
+ if (up->ut_type == USER_PROCESS) {
+ (void) strlcpy(user, up->ut_user,
+ sizeof (user));
+ (void) strlcpy(ttyn, up->ut_line,
+ sizeof (ttyn));
+ (void) strlcpy(rhost, up->ut_host,
+ sizeof (rhost));
+ if ((pam_start("telnet", user, NULL, &pamh)) ==
+ PAM_SUCCESS) {
+ (void) pam_set_item(pamh, PAM_TTY,
+ ttyn);
+ (void) pam_set_item(pamh, PAM_RHOST,
+ rhost);
+ (void) pam_close_session(pamh, 0);
+ (void) pam_end(pamh, PAM_SUCCESS);
+ }
+ }
+
+ up->ut_type = DEAD_PROCESS;
+ up->ut_exit.e_termination = WTERMSIG(0);
+ up->ut_exit.e_exit = WEXITSTATUS(0);
+ (void) time(&up->ut_tv.tv_sec);
+
+ if (modutx(up) == NULL) {
+ /*
+ * Since modutx failed we'll
+ * write out the new entry
+ * ourselves.
+ */
+ (void) pututxline(up);
+ updwtmpx("wtmpx", up);
+ }
+ break;
+ }
+ }
+
+ endutxent();
+
+ (void) signal(SIGCHLD, (void (*)())cleanup);
+}
+
+static int
+readstream(int fd, char *buf, int offset)
+{
+ struct strbuf ctlbuf, datbuf;
+ union T_primitives tpi;
+ int ret = 0;
+ int flags = 0;
+ int bytes_avail, count;
+
+ (void) memset((char *)&ctlbuf, 0, sizeof (ctlbuf));
+ (void) memset((char *)&datbuf, 0, sizeof (datbuf));
+
+ ctlbuf.buf = (char *)&tpi;
+ ctlbuf.maxlen = sizeof (tpi);
+
+ if (ioctl(fd, I_NREAD, &bytes_avail) < 0) {
+ syslog(LOG_ERR, "I_NREAD returned error %m");
+ return (-1);
+ }
+ if (bytes_avail > netibufsize - offset) {
+ count = netip - netibuf;
+ netibuf = (char *)realloc(netibuf,
+ (unsigned)netibufsize + bytes_avail);
+ if (netibuf == NULL) {
+ fatal(net, "netibuf realloc failed\n");
+ }
+ netibufsize += bytes_avail;
+ netip = netibuf + count;
+ buf = netibuf;
+ }
+ datbuf.buf = buf + offset;
+ datbuf.maxlen = netibufsize;
+ ret = getmsg(fd, &ctlbuf, &datbuf, &flags);
+ if (ret < 0) {
+ syslog(LOG_ERR, "getmsg returned -1, errno %d\n",
+ errno);
+ return (-1);
+ }
+ if (ctlbuf.len <= 0) {
+ return (datbuf.len);
+ }
+
+ if (tpi.type == T_DATA_REQ) {
+ return (0);
+ }
+
+ if ((tpi.type == T_ORDREL_IND) || (tpi.type == T_DISCON_IND))
+ cleanup(0);
+ fatal(fd, "no data or protocol element recognized");
+ /*NOTREACHED*/
+}
+
+static void
+drainstream(int size)
+{
+ int nbytes;
+ int tsize;
+
+ tsize = netip - netibuf;
+
+ if ((tsize + ncc + size) > netibufsize) {
+ if (!(netibuf = (char *)realloc(netibuf,
+ (unsigned)tsize + ncc + size)))
+ fatalperror(net, "netibuf realloc failed\n", errno);
+ netibufsize = tsize + ncc + size;
+
+ netip = netibuf + tsize;
+ }
+
+ if ((nbytes = read(net, (char *)netip + ncc, size)) != size)
+ syslog(LOG_ERR, "read %d bytes\n", nbytes);
+}
+
+/*
+ * TPI style replacement for socket send() primitive, so we don't require
+ * sockmod to be on the stream.
+ */
+static int
+send_oob(int fd, char *ptr, int count)
+{
+ struct T_exdata_req exd_req;
+ struct strbuf hdr, dat;
+ int ret;
+
+ exd_req.PRIM_type = T_EXDATA_REQ;
+ exd_req.MORE_flag = 0;
+
+ hdr.buf = (char *)&exd_req;
+ hdr.len = sizeof (exd_req);
+
+ dat.buf = ptr;
+ dat.len = count;
+
+ ret = putmsg(fd, &hdr, &dat, 0);
+ if (ret == 0) {
+ ret = count;
+ }
+ return (ret);
+}
+
+
+/*
+ * local_setenv --
+ * Set the value of the environmental variable "name" to be
+ * "value". If rewrite is set, replace any current value.
+ */
+static int
+local_setenv(const char *name, const char *value, int rewrite)
+{
+ static int alloced; /* if allocated space before */
+ char *c;
+ int l_value, offset;
+
+ /*
+ * Do not allow environment variables which begin with LD_ to be
+ * inserted into the environment. While normally the dynamic linker
+ * protects the login program, that is based on the assumption hostile
+ * invocation of login are from non-root users. However, since telnetd
+ * runs as root, this cannot be utilized. So instead we simply
+ * prevent LD_* from being inserted into the environment.
+ * This also applies to other environment variables that
+ * are to be ignored in setugid apps.
+ * Note that at this point name can contain '='!
+ * Also, do not allow TTYPROMPT to be passed along here.
+ */
+ if (strncmp(name, "LD_", 3) == 0 ||
+ strncmp(name, "NLSPATH", 7) == 0 ||
+ (strncmp(name, "TTYPROMPT", 9) == 0 &&
+ (name[9] == '\0' || name[9] == '='))) {
+ return (-1);
+ }
+ if (*value == '=') /* no `=' in value */
+ ++value;
+ l_value = strlen(value);
+ if ((c = __findenv(name, &offset))) { /* find if already exists */
+ if (!rewrite)
+ return (0);
+ if ((int)strlen(c) >= l_value) { /* old larger; copy over */
+ while (*c++ = *value++);
+ return (0);
+ }
+ } else { /* create new slot */
+ int cnt;
+ char **p;
+
+ for (p = environ, cnt = 0; *p; ++p, ++cnt);
+ if (alloced) { /* just increase size */
+ environ = (char **)realloc((char *)environ,
+ (size_t)(sizeof (char *) * (cnt + 2)));
+ if (!environ)
+ return (-1);
+ } else { /* get new space */
+ alloced = 1; /* copy old entries into it */
+ p = (char **)malloc((size_t)(sizeof (char *)*
+ (cnt + 2)));
+ if (!p)
+ return (-1);
+ (void) memcpy(p, environ, cnt * sizeof (char *));
+ environ = p;
+ }
+ environ[cnt + 1] = NULL;
+ offset = cnt;
+ }
+ for (c = (char *)name; *c && *c != '='; ++c); /* no `=' in name */
+ if (!(environ[offset] = /* name + `=' + value */
+ malloc((size_t)((int)(c - name) + l_value + 2))))
+ return (-1);
+ for (c = environ[offset]; ((*c = *name++) != 0) && (*c != '='); ++c);
+ for (*c++ = '='; *c++ = *value++; );
+ return (0);
+}
+
+/*
+ * local_unsetenv(name) --
+ * Delete environmental variable "name".
+ */
+static void
+local_unsetenv(const char *name)
+{
+ char **p;
+ int offset;
+
+ while (__findenv(name, &offset)) /* if set multiple times */
+ for (p = &environ[offset]; ; ++p)
+ if ((*p = *(p + 1)) == 0)
+ break;
+}
+
+/*
+ * __findenv --
+ * Returns pointer to value associated with name, if any, else NULL.
+ * Sets offset to be the offset of the name/value combination in the
+ * environmental array, for use by local_setenv() and local_unsetenv().
+ * Explicitly removes '=' in argument name.
+ */
+static char *
+__findenv(const char *name, int *offset)
+{
+ extern char **environ;
+ int len;
+ const char *np;
+ char **p, *c;
+
+ if (name == NULL || environ == NULL)
+ return (NULL);
+ for (np = name; *np && *np != '='; ++np)
+ continue;
+ len = np - name;
+ for (p = environ; (c = *p) != NULL; ++p)
+ if (strncmp(c, name, len) == 0 && c[len] == '=') {
+ *offset = p - environ;
+ return (c + len + 1);
+ }
+ return (NULL);
+}
+
+static void
+showbanner(void)
+{
+ char *cp;
+ char evalbuf[BUFSIZ];
+
+ if (defopen(defaultfile) == 0) {
+ int flags;
+
+ /* ignore case */
+ flags = defcntl(DC_GETFLAGS, 0);
+ TURNOFF(flags, DC_CASE);
+ defcntl(DC_SETFLAGS, flags);
+ if (cp = defread(bannervar)) {
+ FILE *fp;
+
+ if (strlen(cp) + strlen("eval echo '") + strlen("'\n")
+ + 1 < sizeof (evalbuf)) {
+ (void) strlcpy(evalbuf, "eval echo '",
+ sizeof (evalbuf));
+ (void) strlcat(evalbuf, cp, sizeof (evalbuf));
+ (void) strlcat(evalbuf, "'\n",
+ sizeof (evalbuf));
+
+ if (fp = popen(evalbuf, "r")) {
+ char buf[BUFSIZ];
+ size_t size;
+
+ /*
+ * Pipe I/O atomicity guarantees we
+ * need only one read.
+ */
+ if ((size = fread(buf, 1,
+ sizeof (buf) - 1,
+ fp)) != 0) {
+ char *p;
+ buf[size] = '\0';
+ p = strrchr(buf, '\n');
+ if (p != NULL)
+ *p = '\0';
+ if (strlen(buf)) {
+ map_banner(buf);
+ netflush();
+ }
+ }
+ (void) pclose(fp);
+ /* close default file */
+ (void) defopen(NULL);
+ return;
+ }
+ }
+ }
+ (void) defopen(NULL); /* close default file */
+ }
+
+ defbanner();
+ netflush();
+}
+
+static void
+map_banner(char *p)
+{
+ char *q;
+
+ /*
+ * Map the banner: "\n" -> "\r\n" and "\r" -> "\r\0"
+ */
+ for (q = nfrontp; p && *p && q < nfrontp + sizeof (netobuf) - 1; )
+ if (*p == '\n') {
+ *q++ = '\r';
+ *q++ = '\n';
+ p++;
+ } else if (*p == '\r') {
+ *q++ = '\r';
+ *q++ = '\0';
+ p++;
+ } else
+ *q++ = *p++;
+
+ nfrontp += q - netobuf;
+}
+
+/*
+ * Show banner that getty never gave. By default, this is `uname -sr`.
+ *
+ * The banner includes some null's (for TELNET CR disambiguation),
+ * so we have to be somewhat complicated.
+ */
+static void
+defbanner(void)
+{
+ struct utsname u;
+
+ /*
+ * Dont show this if the '-h' option was present
+ */
+ if (!show_hostinfo)
+ return;
+
+ if (uname(&u) == -1)
+ return;
+
+ write_data_len((const char *) BANNER1, sizeof (BANNER1) - 1);
+ write_data_len(u.sysname, strlen(u.sysname));
+ write_data_len(" ", 1);
+ write_data_len(u.release, strlen(u.release));
+ write_data_len((const char *)BANNER2, sizeof (BANNER2) - 1);
+}
+
+/*
+ * Verify that the named module is at the top of the stream
+ * and then pop it off.
+ */
+static int
+removemod(int f, char *modname)
+{
+ char topmodname[BUFSIZ];
+
+ if (ioctl(f, I_LOOK, topmodname) < 0)
+ return (-1);
+ if (strcmp(modname, topmodname) != 0) {
+ errno = ENXIO;
+ return (-1);
+ }
+ if (ioctl(f, I_POP, 0) < 0)
+ return (-1);
+ return (0);
+}
+
+static void
+write_data(const char *format, ...)
+{
+ va_list args;
+ int len;
+ char argp[BUFSIZ];
+
+ va_start(args, format);
+
+ if ((len = vsnprintf(argp, sizeof (argp), format, args)) == -1)
+ return;
+
+ write_data_len(argp, len);
+ va_end(args);
+}
+
+static void
+write_data_len(const char *buf, int len)
+{
+ int remaining, copied;
+
+ remaining = BUFSIZ - (nfrontp - netobuf);
+ while (len > 0) {
+ /*
+ * If there's not enough space in netobuf then
+ * try to make some.
+ */
+ if ((len > BUFSIZ ? BUFSIZ : len) > remaining) {
+ netflush();
+ remaining = BUFSIZ - (nfrontp - netobuf);
+ }
+ /* Copy as much as we can */
+ copied = remaining > len ? len : remaining;
+ (void) memmove(nfrontp, buf, copied);
+ nfrontp += copied;
+ len -= copied;
+ remaining -= copied;
+ buf += copied;
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.tftpd.c b/usr/src/cmd/cmd-inet/usr.sbin/in.tftpd.c
new file mode 100644
index 0000000000..47060343e7
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.tftpd.c
@@ -0,0 +1,1376 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T
+ * All Rights Reserved.
+ */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California.
+ * All Rights Reserved.
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Trivial file transfer protocol server. A top level process runs in
+ * an infinite loop fielding new TFTP requests. A child process,
+ * communicating via a pipe with the top level process, sends delayed
+ * NAKs for those that we can't handle. A new child process is created
+ * to service each request that we can handle. The top level process
+ * exits after a period of time during which no new requests are
+ * received.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <netinet/in.h>
+
+#include <arpa/inet.h>
+#include <dirent.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <ctype.h>
+#include <netdb.h>
+#include <setjmp.h>
+#include <syslog.h>
+#include <sys/param.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <string.h>
+#include <priv_utils.h>
+#include "tftpcommon.h"
+
+#define TIMEOUT 5
+#define DELAY_SECS 3
+#define DALLYSECS 60
+
+#define SYSLOG_MSG(message) \
+ (syslog((((errno == ENETUNREACH) || (errno == EHOSTUNREACH) || \
+ (errno == ECONNREFUSED)) ? LOG_WARNING : LOG_ERR), message))
+
+static int rexmtval = TIMEOUT;
+static int maxtimeout = 5*TIMEOUT;
+static int securetftp;
+static int debug;
+static int disable_pnp;
+static int standalone;
+static uid_t uid_nobody = UID_NOBODY;
+static uid_t gid_nobody = GID_NOBODY;
+static int reqsock = -1;
+ /* file descriptor of request socket */
+static socklen_t fromlen;
+static socklen_t fromplen;
+static struct sockaddr_storage client;
+static struct sockaddr_in6 *sin6_ptr;
+static struct sockaddr_in *sin_ptr;
+static struct sockaddr_in6 *from6_ptr;
+static struct sockaddr_in *from_ptr;
+static int addrfmly;
+static int peer;
+static off_t tsize;
+static tftpbuf ackbuf;
+static struct sockaddr_storage from;
+static boolean_t tsize_set;
+static pid_t child;
+ /* pid of child handling delayed replys */
+static int delay_fd [2];
+ /* pipe for communicating with child */
+static FILE *file;
+static char *filename;
+
+static union {
+ struct tftphdr hdr;
+ char data[SEGSIZE + 4];
+} buf;
+
+static union {
+ struct tftphdr hdr;
+ char data[SEGSIZE];
+} oackbuf;
+
+struct delay_info {
+ long timestamp; /* time request received */
+ int ecode; /* error code to return */
+ struct sockaddr_storage from; /* address of client */
+};
+
+int blocksize = SEGSIZE; /* Number of data bytes in a DATA packet */
+
+/*
+ * Default directory for unqualified names
+ * Used by TFTP boot procedures
+ */
+static char *homedir = "/tftpboot";
+
+struct formats {
+ char *f_mode;
+ int (*f_validate)(int);
+ void (*f_send)(struct formats *, int);
+ void (*f_recv)(struct formats *, int);
+ int f_convert;
+};
+
+static void delayed_responder(void);
+static void tftp(struct tftphdr *, int);
+static int validate_filename(int);
+static void tftpd_sendfile(struct formats *, int);
+static void tftpd_recvfile(struct formats *, int);
+static void nak(int);
+static char *blksize_handler(int, char *, int *);
+static char *timeout_handler(int, char *, int *);
+static char *tsize_handler(int, char *, int *);
+
+static struct formats formats[] = {
+ { "netascii", validate_filename, tftpd_sendfile, tftpd_recvfile, 1 },
+ { "octet", validate_filename, tftpd_sendfile, tftpd_recvfile, 0 },
+ { NULL }
+};
+
+struct options {
+ char *opt_name;
+ char *(*opt_handler)(int, char *, int *);
+};
+
+static struct options options[] = {
+ { "blksize", blksize_handler },
+ { "timeout", timeout_handler },
+ { "tsize", tsize_handler },
+ { NULL }
+};
+
+static char optbuf[MAX_OPTVAL_LEN];
+static int timeout;
+static sigjmp_buf timeoutbuf;
+
+int
+main(int argc, char **argv)
+{
+ struct tftphdr *tp;
+ int n;
+ int c;
+ struct passwd *pwd; /* for "nobody" entry */
+ struct in_addr ipv4addr;
+ char abuf[INET6_ADDRSTRLEN];
+ socklen_t addrlen;
+
+ openlog("tftpd", LOG_PID, LOG_DAEMON);
+
+ pwd = getpwnam("nobody");
+ if (pwd != NULL) {
+ uid_nobody = pwd->pw_uid;
+ gid_nobody = pwd->pw_gid;
+ }
+
+ (void) __init_daemon_priv(
+ PU_LIMITPRIVS,
+ uid_nobody, gid_nobody,
+ PRIV_PROC_FORK, PRIV_PROC_CHROOT, NULL);
+
+ /*
+ * Limit set is still "all." Trim it down to just what we need:
+ * fork and chroot.
+ */
+ (void) priv_set(PRIV_SET,
+ PRIV_ALLSETS, PRIV_PROC_FORK, PRIV_PROC_CHROOT, NULL);
+ (void) priv_set(PRIV_SET, PRIV_EFFECTIVE, NULL);
+ (void) priv_set(PRIV_SET, PRIV_INHERITABLE, NULL);
+
+ while ((c = getopt(argc, argv, "dspS")) != EOF)
+ switch (c) {
+ case 'd': /* enable debug */
+ debug++;
+ continue;
+ case 's': /* secure daemon */
+ securetftp = 1;
+ continue;
+ case 'p': /* disable name pnp mapping */
+ disable_pnp = 1;
+ continue;
+ case 'S':
+ standalone = 1;
+ continue;
+ case '?':
+ default:
+usage:
+ (void) fprintf(stderr,
+ "usage: %s [-spd] [home-directory]\n", argv[0]);
+ for (; optind < argc; optind++)
+ syslog(LOG_ERR, "bad argument %s",
+ argv[optind]);
+ exit(1);
+ }
+
+ if (optind < argc)
+ if (optind == argc - 1 && *argv [optind] == '/')
+ homedir = argv [optind];
+ else
+ goto usage;
+
+ if (pipe(delay_fd) < 0) {
+ syslog(LOG_ERR, "pipe (main): %m");
+ exit(1);
+ }
+
+ (void) sigset(SIGCHLD, SIG_IGN); /* no zombies please */
+
+ if (standalone) {
+ socklen_t clientlen;
+
+ sin6_ptr = (struct sockaddr_in6 *)&client;
+ clientlen = sizeof (struct sockaddr_in6);
+ reqsock = socket(AF_INET6, SOCK_DGRAM, 0);
+ if (reqsock == -1) {
+ perror("socket");
+ exit(1);
+ }
+ (void) memset(&client, 0, clientlen);
+ sin6_ptr->sin6_family = AF_INET6;
+ sin6_ptr->sin6_port = htons(IPPORT_TFTP);
+ if (bind(reqsock, (struct sockaddr *)&client,
+ clientlen) == -1) {
+ perror("bind");
+ exit(1);
+ }
+ if (debug)
+ (void) puts("running in standalone mode...");
+ } else {
+ /* request socket passed on fd 0 by inetd */
+ reqsock = 0;
+ }
+ if (debug) {
+ int on = 1;
+
+ (void) setsockopt(reqsock, SOL_SOCKET, SO_DEBUG,
+ (char *)&on, sizeof (on));
+ }
+
+ (void) chdir(homedir);
+
+ (void) priv_set(PRIV_SET, PRIV_EFFECTIVE, PRIV_PROC_FORK, NULL);
+ if ((child = fork()) < 0) {
+ syslog(LOG_ERR, "fork (main): %m");
+ exit(1);
+ }
+ (void) priv_set(PRIV_SET, PRIV_EFFECTIVE, NULL);
+
+ if (child == 0) {
+ (void) priv_set(PRIV_SET, PRIV_ALLSETS, NULL);
+ delayed_responder();
+ } /* child */
+
+ /* close read side of pipe */
+ (void) close(delay_fd[0]);
+
+
+ /*
+ * Top level handling of incomming tftp requests. Read a request
+ * and pass it off to be handled. If request is valid, handling
+ * forks off and parent returns to this loop. If no new requests
+ * are received for DALLYSECS, exit and return to inetd.
+ */
+
+ for (;;) {
+ fd_set readfds;
+ struct timeval dally;
+
+ FD_ZERO(&readfds);
+ FD_SET(reqsock, &readfds);
+ dally.tv_sec = DALLYSECS;
+ dally.tv_usec = 0;
+
+ n = select(reqsock + 1, &readfds, NULL, NULL, &dally);
+ if (n < 0) {
+ if (errno == EINTR)
+ continue;
+ syslog(LOG_ERR, "select: %m");
+ (void) kill(child, SIGKILL);
+ exit(1);
+ }
+ if (n == 0) {
+ /* Select timed out. Its time to die. */
+ if (standalone)
+ continue;
+ else {
+ (void) kill(child, SIGKILL);
+ exit(0);
+ }
+ }
+ addrlen = sizeof (from);
+ if (getsockname(reqsock, (struct sockaddr *)&from,
+ &addrlen) < 0) {
+ syslog(LOG_ERR, "getsockname: %m");
+ exit(1);
+ }
+
+ switch (from.ss_family) {
+ case AF_INET:
+ fromlen = (socklen_t)sizeof (struct sockaddr_in);
+ break;
+ case AF_INET6:
+ fromlen = (socklen_t)sizeof (struct sockaddr_in6);
+ break;
+ default:
+ syslog(LOG_ERR,
+ "Unknown address Family on peer connection %d",
+ from.ss_family);
+ exit(1);
+ }
+
+ n = recvfrom(reqsock, &buf, sizeof (buf), 0,
+ (struct sockaddr *)&from, &fromlen);
+ if (n < 0) {
+ if (errno == EINTR)
+ continue;
+ if (standalone)
+ perror("recvfrom");
+ else
+ syslog(LOG_ERR, "recvfrom: %m");
+ (void) kill(child, SIGKILL);
+ exit(1);
+ }
+
+ (void) alarm(0);
+
+ switch (from.ss_family) {
+ case AF_INET:
+ addrfmly = AF_INET;
+ fromplen = sizeof (struct sockaddr_in);
+ sin_ptr = (struct sockaddr_in *)&client;
+ (void) memset(&client, 0, fromplen);
+ sin_ptr->sin_family = AF_INET;
+ break;
+ case AF_INET6:
+ addrfmly = AF_INET6;
+ fromplen = sizeof (struct sockaddr_in6);
+ sin6_ptr = (struct sockaddr_in6 *)&client;
+ (void) memset(&client, 0, fromplen);
+ sin6_ptr->sin6_family = AF_INET6;
+ break;
+ default:
+ syslog(LOG_ERR,
+ "Unknown address Family on peer connection");
+ exit(1);
+ }
+ peer = socket(addrfmly, SOCK_DGRAM, 0);
+ if (peer < 0) {
+ if (standalone)
+ perror("socket (main)");
+ else
+ syslog(LOG_ERR, "socket (main): %m");
+ (void) kill(child, SIGKILL);
+ exit(1);
+ }
+ if (debug) {
+ int on = 1;
+
+ (void) setsockopt(peer, SOL_SOCKET, SO_DEBUG,
+ (char *)&on, sizeof (on));
+ }
+
+ if (bind(peer, (struct sockaddr *)&client, fromplen) < 0) {
+ if (standalone)
+ perror("bind (main)");
+ else
+ syslog(LOG_ERR, "bind (main): %m");
+ (void) kill(child, SIGKILL);
+ exit(1);
+ }
+ if (standalone && debug) {
+ sin6_ptr = (struct sockaddr_in6 *)&client;
+ from6_ptr = (struct sockaddr_in6 *)&from;
+ if (IN6_IS_ADDR_V4MAPPED(&from6_ptr->sin6_addr)) {
+ IN6_V4MAPPED_TO_INADDR(&from6_ptr->sin6_addr,
+ &ipv4addr);
+ (void) inet_ntop(AF_INET, &ipv4addr, abuf,
+ sizeof (abuf));
+ } else {
+ (void) inet_ntop(AF_INET6,
+ &from6_ptr->sin6_addr, abuf,
+ sizeof (abuf));
+ }
+ /* get local port */
+ if (getsockname(peer, (struct sockaddr *)&client,
+ &fromplen) < 0)
+ perror("getsockname (main)");
+ (void) fprintf(stderr,
+ "request from %s port %d; local port %d\n",
+ abuf, from6_ptr->sin6_port, sin6_ptr->sin6_port);
+ }
+ tp = &buf.hdr;
+ tp->th_opcode = ntohs((ushort_t)tp->th_opcode);
+ if (tp->th_opcode == RRQ || tp->th_opcode == WRQ)
+ tftp(tp, n);
+
+ (void) close(peer);
+ (void) fclose(file);
+ }
+
+ /*NOTREACHED*/
+ return (0);
+}
+
+static void
+delayed_responder(void)
+{
+ struct delay_info dinfo;
+ long now;
+
+ /* we don't use the descriptors passed in to the parent */
+ (void) close(0);
+ (void) close(1);
+ if (standalone)
+ (void) close(reqsock);
+
+ /* close write side of pipe */
+ (void) close(delay_fd[1]);
+
+ for (;;) {
+ int n;
+
+ if ((n = read(delay_fd[0], &dinfo,
+ sizeof (dinfo))) != sizeof (dinfo)) {
+ if (n < 0) {
+ if (errno == EINTR)
+ continue;
+ if (standalone)
+ perror("read from pipe "
+ "(delayed responder)");
+ else
+ syslog(LOG_ERR, "read from pipe: %m");
+ }
+ exit(1);
+ }
+ switch (dinfo.from.ss_family) {
+ case AF_INET:
+ addrfmly = AF_INET;
+ fromplen = sizeof (struct sockaddr_in);
+ sin_ptr = (struct sockaddr_in *)&client;
+ (void) memset(&client, 0, fromplen);
+ sin_ptr->sin_family = AF_INET;
+ break;
+ case AF_INET6:
+ addrfmly = AF_INET6;
+ fromplen = sizeof (struct sockaddr_in6);
+ sin6_ptr = (struct sockaddr_in6 *)&client;
+ (void) memset(&client, 0, fromplen);
+ sin6_ptr->sin6_family = AF_INET6;
+ break;
+ }
+ peer = socket(addrfmly, SOCK_DGRAM, 0);
+ if (peer == -1) {
+ if (standalone)
+ perror("socket (delayed responder)");
+ else
+ syslog(LOG_ERR, "socket (delay): %m");
+ exit(1);
+ }
+ if (debug) {
+ int on = 1;
+
+ (void) setsockopt(peer, SOL_SOCKET, SO_DEBUG,
+ (char *)&on, sizeof (on));
+ }
+
+ if (bind(peer, (struct sockaddr *)&client, fromplen) < 0) {
+ if (standalone)
+ perror("bind (delayed responder)");
+ else
+ syslog(LOG_ERR, "bind (delay): %m");
+ exit(1);
+ }
+ if (client.ss_family == AF_INET) {
+ from_ptr = (struct sockaddr_in *)&dinfo.from;
+ from_ptr->sin_family = AF_INET;
+ } else {
+ from6_ptr = (struct sockaddr_in6 *)&dinfo.from;
+ from6_ptr->sin6_family = AF_INET6;
+ }
+ /*
+ * Since a request hasn't been received from the client
+ * before the delayed responder process is forked, the
+ * from variable is uninitialized. So set it to contain
+ * the client address.
+ */
+ from = dinfo.from;
+
+ /*
+ * only sleep if DELAY_SECS has not elapsed since
+ * original request was received. Ensure that `now'
+ * is not earlier than `dinfo.timestamp'
+ */
+ now = time(0);
+ if ((uint_t)(now - dinfo.timestamp) < DELAY_SECS)
+ (void) sleep(DELAY_SECS - (now - dinfo.timestamp));
+ nak(dinfo.ecode);
+ (void) close(peer);
+ } /* for */
+
+ /* NOTREACHED */
+}
+
+/*
+ * Handle the Blocksize option.
+ * Return the blksize option value string to include in the OACK reply.
+ */
+/*ARGSUSED*/
+static char *
+blksize_handler(int opcode, char *optval, int *errcode)
+{
+ char *endp;
+ int value;
+
+ *errcode = -1;
+ errno = 0;
+ value = (int)strtol(optval, &endp, 10);
+ if (errno != 0 || value < MIN_BLKSIZE || *endp != '\0')
+ return (NULL);
+ /*
+ * As the blksize value in the OACK reply can be less than the value
+ * requested, to support broken clients if the value requested is larger
+ * than allowed in the RFC, reply with the maximum value permitted.
+ */
+ if (value > MAX_BLKSIZE)
+ value = MAX_BLKSIZE;
+
+ blocksize = value;
+ (void) snprintf(optbuf, sizeof (optbuf), "%d", blocksize);
+ return (optbuf);
+}
+
+/*
+ * Handle the Timeout Interval option.
+ * Return the timeout option value string to include in the OACK reply.
+ */
+/*ARGSUSED*/
+static char *
+timeout_handler(int opcode, char *optval, int *errcode)
+{
+ char *endp;
+ int value;
+
+ *errcode = -1;
+ errno = 0;
+ value = (int)strtol(optval, &endp, 10);
+ if (errno != 0 || *endp != '\0')
+ return (NULL);
+ /*
+ * The timeout value in the OACK reply must match the value specified
+ * by the client, so if an invalid timeout is requested don't include
+ * the timeout option in the OACK reply.
+ */
+ if (value < MIN_TIMEOUT || value > MAX_TIMEOUT)
+ return (NULL);
+
+ rexmtval = value;
+ maxtimeout = 5 * rexmtval;
+ (void) snprintf(optbuf, sizeof (optbuf), "%d", rexmtval);
+ return (optbuf);
+}
+
+/*
+ * Handle the Transfer Size option.
+ * Return the tsize option value string to include in the OACK reply.
+ */
+static char *
+tsize_handler(int opcode, char *optval, int *errcode)
+{
+ char *endp;
+ longlong_t value;
+
+ *errcode = -1;
+ errno = 0;
+ value = strtoll(optval, &endp, 10);
+ if (errno != 0 || value < 0 || *endp != '\0')
+ return (NULL);
+
+ if (opcode == RRQ) {
+ if (tsize_set == B_FALSE)
+ return (NULL);
+ /*
+ * The tsize value should be 0 for a read request, but to
+ * support broken clients we don't check that it is.
+ */
+ } else {
+#if _FILE_OFFSET_BITS == 32
+ if (value > MAXOFF_T) {
+ *errcode = ENOSPACE;
+ return (NULL);
+ }
+#endif
+ tsize = value;
+ tsize_set = B_TRUE;
+ }
+ (void) snprintf(optbuf, sizeof (optbuf), OFF_T_FMT, tsize);
+ return (optbuf);
+}
+
+/*
+ * Process any options included by the client in the request packet.
+ * Return the size of the OACK reply packet built or 0 for no OACK reply.
+ */
+static int
+process_options(int opcode, char *opts, char *endopts)
+{
+ char *cp, *optname, *optval, *ostr, *oackend;
+ struct tftphdr *oackp;
+ int i, errcode;
+
+ /*
+ * To continue to interoperate with broken TFTP clients, ignore
+ * null padding appended to requests which don't include options.
+ */
+ cp = opts;
+ while ((cp < endopts) && (*cp == '\0'))
+ cp++;
+ if (cp == endopts)
+ return (0);
+
+ /*
+ * Construct an Option ACKnowledgement packet if any requested option
+ * is recognized.
+ */
+ oackp = &oackbuf.hdr;
+ oackend = oackbuf.data + sizeof (oackbuf.data);
+ oackp->th_opcode = htons((ushort_t)OACK);
+ cp = (char *)&oackp->th_stuff;
+ while (opts < endopts) {
+ optname = opts;
+ if ((optval = next_field(optname, endopts)) == NULL) {
+ nak(EOPTNEG);
+ exit(1);
+ }
+ if ((opts = next_field(optval, endopts)) == NULL) {
+ nak(EOPTNEG);
+ exit(1);
+ }
+ for (i = 0; options[i].opt_name != NULL; i++) {
+ if (strcasecmp(optname, options[i].opt_name) == 0)
+ break;
+ }
+ if (options[i].opt_name != NULL) {
+ ostr = options[i].opt_handler(opcode, optval, &errcode);
+ if (ostr != NULL) {
+ cp += strlcpy(cp, options[i].opt_name,
+ oackend - cp) + 1;
+ if (cp <= oackend)
+ cp += strlcpy(cp, ostr, oackend - cp)
+ + 1;
+
+ if (cp > oackend) {
+ nak(EOPTNEG);
+ exit(1);
+ }
+ } else if (errcode >= 0) {
+ nak(errcode);
+ exit(1);
+ }
+ }
+ }
+ if (cp != (char *)&oackp->th_stuff)
+ return (cp - oackbuf.data);
+ return (0);
+}
+
+/*
+ * Handle access errors caused by client requests.
+ */
+
+static void
+delay_exit(int ecode)
+{
+ struct delay_info dinfo;
+
+ /*
+ * The most likely cause of an error here is that
+ * someone has broadcast an RRQ packet because s/he's
+ * trying to boot and doesn't know who the server is.
+ * Rather then sending an ERROR packet immediately, we
+ * wait a while so that the real server has a better chance
+ * of getting through (in case client has lousy Ethernet
+ * interface). We write to a child that handles delayed
+ * ERROR packets to avoid delaying service to new
+ * requests. Of course, we would rather just not answer
+ * RRQ packets that are broadcasted, but there's no way
+ * for a user process to determine this.
+ */
+
+ dinfo.timestamp = time(0);
+
+ /*
+ * If running in secure mode, we map all errors to EACCESS
+ * so that the client gets no information about which files
+ * or directories exist.
+ */
+ if (securetftp)
+ dinfo.ecode = EACCESS;
+ else
+ dinfo.ecode = ecode;
+
+ dinfo.from = from;
+ if (write(delay_fd[1], &dinfo, sizeof (dinfo)) !=
+ sizeof (dinfo)) {
+ syslog(LOG_ERR, "delayed write failed.");
+ (void) kill(child, SIGKILL);
+ exit(1);
+ }
+ exit(0);
+}
+
+/*
+ * Handle initial connection protocol.
+ */
+static void
+tftp(struct tftphdr *tp, int size)
+{
+ char *cp;
+ int readmode, ecode;
+ struct formats *pf;
+ char *mode;
+ int fd;
+ static boolean_t firsttime = B_TRUE;
+ int oacklen;
+ struct stat statb;
+
+ readmode = (tp->th_opcode == RRQ);
+ filename = (char *)&tp->th_stuff;
+ mode = next_field(filename, &buf.data[size]);
+ cp = (mode != NULL) ? next_field(mode, &buf.data[size]) : NULL;
+ if (cp == NULL) {
+ nak(EBADOP);
+ exit(1);
+ }
+ if (debug && standalone) {
+ (void) fprintf(stderr, "%s for %s %s ",
+ readmode ? "RRQ" : "WRQ", filename, mode);
+ print_options(stderr, cp, size + buf.data - cp);
+ (void) putc('\n', stderr);
+ }
+ for (pf = formats; pf->f_mode != NULL; pf++)
+ if (strcasecmp(pf->f_mode, mode) == 0)
+ break;
+ if (pf->f_mode == NULL) {
+ nak(EBADOP);
+ exit(1);
+ }
+
+ /*
+ * XXX fork a new process to handle this request before
+ * chroot(), otherwise the parent won't be able to create a
+ * new socket as that requires library access to system files
+ * and devices.
+ */
+ (void) priv_set(PRIV_SET, PRIV_EFFECTIVE, PRIV_PROC_FORK, NULL);
+ switch (fork()) {
+ case -1:
+ syslog(LOG_ERR, "fork (tftp): %m");
+ (void) priv_set(PRIV_SET, PRIV_EFFECTIVE, NULL);
+ return;
+ case 0:
+ (void) priv_set(PRIV_SET, PRIV_EFFECTIVE, NULL);
+ break;
+ default:
+ (void) priv_set(PRIV_SET, PRIV_EFFECTIVE, NULL);
+ return;
+ }
+
+ /*
+ * Try to see if we can access the file. The access can still
+ * fail later if we are running in secure mode because of
+ * the chroot() call. We only want to execute the chroot() once.
+ */
+ if (securetftp && firsttime) {
+ (void) priv_set(
+ PRIV_SET, PRIV_EFFECTIVE, PRIV_PROC_CHROOT, NULL);
+ if (chroot(homedir) == -1) {
+ syslog(LOG_ERR,
+ "tftpd: cannot chroot to directory %s: %m\n",
+ homedir);
+ delay_exit(EACCESS);
+ }
+ else
+ {
+ firsttime = B_FALSE;
+ }
+ (void) priv_set(PRIV_SET, PRIV_EFFECTIVE, NULL);
+ (void) chdir("/"); /* cd to new root */
+ }
+ (void) priv_set(PRIV_SET, PRIV_ALLSETS, NULL);
+
+ ecode = (*pf->f_validate)(tp->th_opcode);
+ if (ecode != 0)
+ delay_exit(ecode);
+
+ /* we don't use the descriptors passed in to the parent */
+ (void) close(STDIN_FILENO);
+ (void) close(STDOUT_FILENO);
+
+ /*
+ * Try to open file as low-priv setuid/setgid. Note that
+ * a chroot() has already been done.
+ */
+ fd = open(filename,
+ (readmode ? O_RDONLY : (O_WRONLY|O_TRUNC)) | O_NONBLOCK);
+ if ((fd < 0) || (fstat(fd, &statb) < 0))
+ delay_exit((errno == ENOENT) ? ENOTFOUND : EACCESS);
+
+ if (((statb.st_mode & ((readmode) ? S_IROTH : S_IWOTH)) == 0) ||
+ ((statb.st_mode & S_IFMT) != S_IFREG))
+ delay_exit(EACCESS);
+
+ file = fdopen(fd, readmode ? "r" : "w");
+ if (file == NULL)
+ delay_exit(errno + 100);
+
+ /* Don't know the size of transfers which involve conversion */
+ tsize_set = (readmode && (pf->f_convert == 0));
+ if (tsize_set)
+ tsize = statb.st_size;
+
+ /* Deal with any options sent by the client */
+ oacklen = process_options(tp->th_opcode, cp, buf.data + size);
+
+ if (tp->th_opcode == WRQ)
+ (*pf->f_recv)(pf, oacklen);
+ else
+ (*pf->f_send)(pf, oacklen);
+
+ exit(0);
+}
+
+/*
+ * Maybe map filename into another one.
+ *
+ * For PNP, we get TFTP boot requests for filenames like
+ * <Unknown Hex IP Addr>.<Architecture Name>. We must
+ * map these to 'pnp.<Architecture Name>'. Note that
+ * uppercase is mapped to lowercase in the architecture names.
+ *
+ * For names <Hex IP Addr> there are two cases. First,
+ * it may be a buggy prom that omits the architecture code.
+ * So first check if <Hex IP Addr>.<arch> is on the filesystem.
+ * Second, this is how most Sun3s work; assume <arch> is sun3.
+ */
+
+static char *
+pnp_check(char *origname)
+{
+ static char buf [MAXNAMLEN + 1];
+ char *arch, *s, *bufend;
+ in_addr_t ipaddr;
+ int len = (origname ? strlen(origname) : 0);
+ DIR *dir;
+ struct dirent *dp;
+
+ if (securetftp || disable_pnp || len < 8 || len > 14)
+ return (NULL);
+
+ /*
+ * XXX see if this cable allows pnp; if not, return NULL
+ * Requires YP support for determining this!
+ */
+
+ ipaddr = htonl(strtol(origname, &arch, 16));
+ if ((arch == NULL) || (len > 8 && *arch != '.'))
+ return (NULL);
+ if (len == 8)
+ arch = "SUN3";
+ else
+ arch++;
+
+ /*
+ * Allow <Hex IP Addr>* filename request to to be
+ * satisfied by <Hex IP Addr><Any Suffix> rather
+ * than enforcing this to be Sun3 systems. Also serves
+ * to make case of suffix a don't-care.
+ */
+ if ((dir = opendir(homedir)) == NULL)
+ return (NULL);
+ while ((dp = readdir(dir)) != NULL) {
+ if (strncmp(origname, dp->d_name, 8) == 0) {
+ (void) strlcpy(buf, dp->d_name, sizeof (buf));
+ (void) closedir(dir);
+ return (buf);
+ }
+ }
+ (void) closedir(dir);
+
+ /*
+ * XXX maybe call YP master for most current data iff
+ * pnp is enabled.
+ */
+
+ /*
+ * only do mapping PNP boot file name for machines that
+ * are not in the hosts database.
+ */
+ if (gethostbyaddr((char *)&ipaddr, sizeof (ipaddr), AF_INET) != NULL)
+ return (NULL);
+
+ s = buf + strlcpy(buf, "pnp.", sizeof (buf));
+ bufend = &buf[sizeof (buf) - 1];
+ while ((*arch != '\0') && (s < bufend))
+ *s++ = tolower (*arch++);
+ *s = '\0';
+ return (buf);
+}
+
+
+/*
+ * Try to validate filename. If the filename doesn't exist try PNP mapping.
+ */
+static int
+validate_filename(int mode)
+{
+ struct stat stbuf;
+ char *origfile;
+
+ if (stat(filename, &stbuf) < 0) {
+ if (errno != ENOENT)
+ return (EACCESS);
+ if (mode == WRQ)
+ return (ENOTFOUND);
+
+ /* try to map requested filename into a pnp filename */
+ origfile = filename;
+ filename = pnp_check(origfile);
+ if (filename == NULL)
+ return (ENOTFOUND);
+
+ if (stat(filename, &stbuf) < 0)
+ return (errno == ENOENT ? ENOTFOUND : EACCESS);
+ syslog(LOG_NOTICE, "%s -> %s\n", origfile, filename);
+ }
+
+ return (0);
+}
+
+/* ARGSUSED */
+static void
+timer(int signum)
+{
+ timeout += rexmtval;
+ if (timeout >= maxtimeout)
+ exit(1);
+ siglongjmp(timeoutbuf, 1);
+}
+
+/*
+ * Send the requested file.
+ */
+static void
+tftpd_sendfile(struct formats *pf, int oacklen)
+{
+ struct tftphdr *dp;
+ volatile int block = 1;
+ int size, n, serrno;
+
+ if (oacklen != 0) {
+ (void) sigset(SIGALRM, timer);
+ timeout = 0;
+ (void) sigsetjmp(timeoutbuf, 1);
+ if (debug && standalone) {
+ (void) fputs("Sending OACK ", stderr);
+ print_options(stderr, (char *)&oackbuf.hdr.th_stuff,
+ oacklen - 2);
+ (void) putc('\n', stderr);
+ }
+ if (sendto(peer, &oackbuf, oacklen, 0,
+ (struct sockaddr *)&from, fromplen) != oacklen) {
+ if (debug && standalone) {
+ serrno = errno;
+ perror("sendto (oack)");
+ errno = serrno;
+ }
+ SYSLOG_MSG("sendto (oack): %m");
+ goto abort;
+ }
+ (void) alarm(rexmtval); /* read the ack */
+ for (;;) {
+ (void) sigrelse(SIGALRM);
+ n = recv(peer, &ackbuf, sizeof (ackbuf), 0);
+ (void) sighold(SIGALRM);
+ if (n < 0) {
+ if (errno == EINTR)
+ continue;
+ serrno = errno;
+ SYSLOG_MSG("recv (ack): %m");
+ if (debug && standalone) {
+ errno = serrno;
+ perror("recv (ack)");
+ }
+ goto abort;
+ }
+ ackbuf.tb_hdr.th_opcode =
+ ntohs((ushort_t)ackbuf.tb_hdr.th_opcode);
+ ackbuf.tb_hdr.th_block =
+ ntohs((ushort_t)ackbuf.tb_hdr.th_block);
+
+ if (ackbuf.tb_hdr.th_opcode == ERROR) {
+ if (debug && standalone) {
+ (void) fprintf(stderr,
+ "received ERROR %d",
+ ackbuf.tb_hdr.th_code);
+ if (n > 4)
+ (void) fprintf(stderr,
+ " %.*s", n - 4,
+ ackbuf.tb_hdr.th_msg);
+ (void) putc('\n', stderr);
+ }
+ goto abort;
+ }
+
+ if (ackbuf.tb_hdr.th_opcode == ACK) {
+ if (debug && standalone)
+ (void) fprintf(stderr,
+ "received ACK for block %d\n",
+ ackbuf.tb_hdr.th_block);
+ if (ackbuf.tb_hdr.th_block == 0)
+ break;
+ /*
+ * Don't resend the OACK, avoids getting stuck
+ * in an OACK/ACK loop if the client keeps
+ * replying with a bad ACK. Client will either
+ * send a good ACK or timeout sending bad ones.
+ */
+ }
+ }
+ cancel_alarm();
+ }
+ dp = r_init();
+ do {
+ (void) sigset(SIGALRM, timer);
+ size = readit(file, &dp, pf->f_convert);
+ if (size < 0) {
+ nak(errno + 100);
+ goto abort;
+ }
+ dp->th_opcode = htons((ushort_t)DATA);
+ dp->th_block = htons((ushort_t)block);
+ timeout = 0;
+ (void) sigsetjmp(timeoutbuf, 1);
+ if (debug && standalone)
+ (void) fprintf(stderr, "Sending DATA block %d\n",
+ block);
+ if (sendto(peer, dp, size + 4, 0,
+ (struct sockaddr *)&from, fromplen) != size + 4) {
+ if (debug && standalone) {
+ serrno = errno;
+ perror("sendto (data)");
+ errno = serrno;
+ }
+ SYSLOG_MSG("sendto (data): %m");
+ goto abort;
+ }
+ read_ahead(file, pf->f_convert);
+ (void) alarm(rexmtval); /* read the ack */
+ for (;;) {
+ (void) sigrelse(SIGALRM);
+ n = recv(peer, &ackbuf, sizeof (ackbuf), 0);
+ (void) sighold(SIGALRM);
+ if (n < 0) {
+ if (errno == EINTR)
+ continue;
+ serrno = errno;
+ SYSLOG_MSG("recv (ack): %m");
+ if (debug && standalone) {
+ errno = serrno;
+ perror("recv (ack)");
+ }
+ goto abort;
+ }
+ ackbuf.tb_hdr.th_opcode =
+ ntohs((ushort_t)ackbuf.tb_hdr.th_opcode);
+ ackbuf.tb_hdr.th_block =
+ ntohs((ushort_t)ackbuf.tb_hdr.th_block);
+
+ if (ackbuf.tb_hdr.th_opcode == ERROR) {
+ if (debug && standalone) {
+ (void) fprintf(stderr,
+ "received ERROR %d",
+ ackbuf.tb_hdr.th_code);
+ if (n > 4)
+ (void) fprintf(stderr,
+ " %.*s", n - 4,
+ ackbuf.tb_hdr.th_msg);
+ (void) putc('\n', stderr);
+ }
+ goto abort;
+ }
+
+ if (ackbuf.tb_hdr.th_opcode == ACK) {
+ if (debug && standalone)
+ (void) fprintf(stderr,
+ "received ACK for block %d\n",
+ ackbuf.tb_hdr.th_block);
+ if (ackbuf.tb_hdr.th_block == block) {
+ break;
+ }
+ /*
+ * Never resend the current DATA packet on
+ * receipt of a duplicate ACK, doing so would
+ * cause the "Sorcerer's Apprentice Syndrome".
+ */
+ }
+ }
+ cancel_alarm();
+ block++;
+ } while (size == blocksize);
+
+abort:
+ cancel_alarm();
+ (void) fclose(file);
+}
+
+/* ARGSUSED */
+static void
+justquit(int signum)
+{
+ exit(0);
+}
+
+/*
+ * Receive a file.
+ */
+static void
+tftpd_recvfile(struct formats *pf, int oacklen)
+{
+ struct tftphdr *dp;
+ struct tftphdr *ap; /* ack buffer */
+ int block = 0, n, size, acklen, serrno;
+
+ dp = w_init();
+ ap = &ackbuf.tb_hdr;
+ do {
+ (void) sigset(SIGALRM, timer);
+ timeout = 0;
+ if (oacklen == 0) {
+ ap->th_opcode = htons((ushort_t)ACK);
+ ap->th_block = htons((ushort_t)block);
+ acklen = 4;
+ } else {
+ /* copy OACK packet to the ack buffer ready to send */
+ (void) memcpy(&ackbuf, &oackbuf, oacklen);
+ acklen = oacklen;
+ oacklen = 0;
+ }
+ block++;
+ (void) sigsetjmp(timeoutbuf, 1);
+send_ack:
+ if (debug && standalone) {
+ if (ap->th_opcode == htons((ushort_t)ACK)) {
+ (void) fprintf(stderr,
+ "Sending ACK for block %d\n", block - 1);
+ } else {
+ (void) fprintf(stderr, "Sending OACK ");
+ print_options(stderr, (char *)&ap->th_stuff,
+ acklen - 2);
+ (void) putc('\n', stderr);
+ }
+ }
+ if (sendto(peer, &ackbuf, acklen, 0, (struct sockaddr *)&from,
+ fromplen) != acklen) {
+ if (ap->th_opcode == htons((ushort_t)ACK)) {
+ if (debug && standalone) {
+ serrno = errno;
+ perror("sendto (ack)");
+ errno = serrno;
+ }
+ syslog(LOG_ERR, "sendto (ack): %m\n");
+ } else {
+ if (debug && standalone) {
+ serrno = errno;
+ perror("sendto (oack)");
+ errno = serrno;
+ }
+ syslog(LOG_ERR, "sendto (oack): %m\n");
+ }
+ goto abort;
+ }
+ if (write_behind(file, pf->f_convert) < 0) {
+ nak(errno + 100);
+ goto abort;
+ }
+ (void) alarm(rexmtval);
+ for (;;) {
+ (void) sigrelse(SIGALRM);
+ n = recv(peer, dp, blocksize + 4, 0);
+ (void) sighold(SIGALRM);
+ if (n < 0) { /* really? */
+ if (errno == EINTR)
+ continue;
+ syslog(LOG_ERR, "recv (data): %m");
+ goto abort;
+ }
+ dp->th_opcode = ntohs((ushort_t)dp->th_opcode);
+ dp->th_block = ntohs((ushort_t)dp->th_block);
+ if (dp->th_opcode == ERROR) {
+ cancel_alarm();
+ if (debug && standalone) {
+ (void) fprintf(stderr,
+ "received ERROR %d", dp->th_code);
+ if (n > 4)
+ (void) fprintf(stderr,
+ " %.*s", n - 4, dp->th_msg);
+ (void) putc('\n', stderr);
+ }
+ return;
+ }
+ if (dp->th_opcode == DATA) {
+ if (debug && standalone)
+ (void) fprintf(stderr,
+ "Received DATA block %d\n",
+ dp->th_block);
+ if (dp->th_block == block) {
+ break; /* normal */
+ }
+ /* Re-synchronize with the other side */
+ if (synchnet(peer) < 0) {
+ nak(errno + 100);
+ goto abort;
+ }
+ if (dp->th_block == (block-1))
+ goto send_ack; /* rexmit */
+ }
+ }
+ cancel_alarm();
+ /* size = write(file, dp->th_data, n - 4); */
+ size = writeit(file, &dp, n - 4, pf->f_convert);
+ if (size != (n - 4)) {
+ nak((size < 0) ? (errno + 100) : ENOSPACE);
+ goto abort;
+ }
+ } while (size == blocksize);
+ if (write_behind(file, pf->f_convert) < 0) {
+ nak(errno + 100);
+ goto abort;
+ }
+ n = fclose(file); /* close data file */
+ file = NULL;
+ if (n == EOF) {
+ nak(errno + 100);
+ goto abort;
+ }
+
+ ap->th_opcode = htons((ushort_t)ACK); /* send the "final" ack */
+ ap->th_block = htons((ushort_t)(block));
+ if (debug && standalone)
+ (void) fprintf(stderr, "Sending ACK for block %d\n", block);
+ if (sendto(peer, &ackbuf, 4, 0, (struct sockaddr *)&from,
+ fromplen) == -1) {
+ if (debug && standalone)
+ perror("sendto (ack)");
+ }
+ (void) sigset(SIGALRM, justquit); /* just quit on timeout */
+ (void) alarm(rexmtval);
+ /* normally times out and quits */
+ n = recv(peer, dp, blocksize + 4, 0);
+ (void) alarm(0);
+ dp->th_opcode = ntohs((ushort_t)dp->th_opcode);
+ dp->th_block = ntohs((ushort_t)dp->th_block);
+ if (n >= 4 && /* if read some data */
+ dp->th_opcode == DATA && /* and got a data block */
+ block == dp->th_block) { /* then my last ack was lost */
+ if (debug && standalone) {
+ (void) fprintf(stderr, "Sending ACK for block %d\n",
+ block);
+ }
+ /* resend final ack */
+ if (sendto(peer, &ackbuf, 4, 0, (struct sockaddr *)&from,
+ fromplen) == -1) {
+ if (debug && standalone)
+ perror("sendto (last ack)");
+ }
+ }
+
+abort:
+ cancel_alarm();
+ if (file != NULL)
+ (void) fclose(file);
+}
+
+/*
+ * Send a nak packet (error message).
+ * Error code passed in is one of the
+ * standard TFTP codes, or a UNIX errno
+ * offset by 100.
+ * Handles connected as well as unconnected peer.
+ */
+static void
+nak(int error)
+{
+ struct tftphdr *tp;
+ int length;
+ struct errmsg *pe;
+ int ret;
+
+ tp = &buf.hdr;
+ tp->th_opcode = htons((ushort_t)ERROR);
+ tp->th_code = htons((ushort_t)error);
+ for (pe = errmsgs; pe->e_code >= 0; pe++)
+ if (pe->e_code == error)
+ break;
+ if (pe->e_code < 0) {
+ pe->e_msg = strerror(error - 100);
+ tp->th_code = EUNDEF; /* set 'undef' errorcode */
+ }
+ (void) strlcpy(tp->th_msg, (pe->e_msg != NULL) ? pe->e_msg : "UNKNOWN",
+ sizeof (buf) - sizeof (struct tftphdr));
+ length = strlen(tp->th_msg);
+ length += sizeof (struct tftphdr);
+ if (debug && standalone)
+ (void) fprintf(stderr, "Sending NAK: %s\n", tp->th_msg);
+
+ ret = sendto(peer, &buf, length, 0, (struct sockaddr *)&from,
+ fromplen);
+ if (ret == -1 && errno == EISCONN) {
+ /* Try without an address */
+ ret = send(peer, &buf, length, 0);
+ }
+ if (ret == -1) {
+ if (standalone)
+ perror("sendto (nak)");
+ else
+ syslog(LOG_ERR, "tftpd: nak: %m\n");
+ } else if (ret != length) {
+ if (standalone)
+ perror("sendto (nak) lost data");
+ else
+ syslog(LOG_ERR, "tftpd: nak: %d lost\n", length - ret);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.tnamed.c b/usr/src/cmd/cmd-inet/usr.sbin/in.tnamed.c
new file mode 100644
index 0000000000..d21dcf63f7
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.tnamed.c
@@ -0,0 +1,232 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ * Copyright 1998 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T
+ * All Rights Reserved.
+ */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California.
+ * All Rights Reserved.
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * This program implements a UDP basic name server as specified in IEN116
+ * The extended name server functionality is NOT implemented here (yet).
+ * This is generally used in conjunction with MIT's PC/IP software.
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+#ifdef SYSV
+#define bzero(s, n) memset((s), 0, (n))
+#define bcopy(a, b, c) memcpy((b), (a), (c))
+#endif /* SYSV */
+
+/*
+ * These command codes come from IEN116
+ */
+#define NAMECODE 1
+#define ADDRESSCODE 2
+#define ERRORCODE 3
+/*
+ * These error codes are used to qualify ERRORCODE
+ */
+#define UNDEFINEDERROR 0
+#define NOTFOUNDERROR 1
+#define SYNTAXERROR 2
+#define BUFLEN 2000
+static int handler();
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int s;
+ struct sockaddr_in client;
+ int length;
+ socklen_t clientlength;
+ register struct hostent *hp;
+ char hostname[BUFLEN];
+ char iobuf[BUFLEN];
+ register char *buffer = iobuf;
+ register int replylength;
+ int request;
+ struct in_addr x;
+
+ if (argc > 1) {
+ /* the daemon is run by hand and never exits */
+ struct servent temp;
+ register struct servent *sp;
+ register struct protoent *pp;
+ struct sockaddr_in server;
+
+ if ((sp = getservbyname("name", "udp")) == NULL) {
+ fprintf(stderr, "in.tnamed: UDP name server not in ");
+ fprintf(stderr, "/etc/services\n");
+ sp = &temp;
+ sp->s_port = htons(42);
+ }
+ if ((pp = getprotobyname("udp")) == NULL) {
+ fprintf(stderr, "in.tnamed: UDP protocol not in ");
+ fprintf(stderr, "/etc/protocols\n");
+ exit(1);
+ }
+ if ((s = socket(AF_INET, SOCK_DGRAM, pp->p_proto)) < 0) {
+ perror("in.tnamed: socket error");
+ exit(1);
+ }
+ bzero((char *)&server, sizeof (server));
+ server.sin_family = AF_INET;
+ server.sin_port = sp->s_port;
+ if (bind(s, (struct sockaddr *)&server, sizeof (server)) != 0) {
+ perror("in.tnamed: bind error");
+ exit(1);
+ }
+ fprintf(stderr, "in.tnamed: UDP name server running\n");
+ } else {
+ /* daemon forked by inetd and is short lived */
+ struct itimerval value, ovalue;
+
+ signal(SIGALRM, (void (*)())handler);
+ value.it_value.tv_sec = 5 * 60;
+ value.it_value.tv_usec = value.it_interval.tv_usec = 0;
+ setitimer(ITIMER_REAL, &value, &ovalue);
+ s = 0; /* by inetd conventions */
+ }
+
+ for (;;) {
+
+ clientlength = (socklen_t)sizeof (client);
+ length = recvfrom(s, buffer, BUFLEN, 0,
+ (struct sockaddr *)&client, &clientlength);
+ if (length < 0) {
+ perror("in.tnamed: recvfrom error.Try in.tnamed -v ?");
+ continue;
+ }
+
+ request = buffer[0];
+ length = buffer[1];
+ replylength = length + 2; /* reply is appended to request */
+ if (length < sizeof (hostname)) {
+ strncpy(hostname, &buffer[2], length);
+ hostname[length] = 0;
+ } else {
+ hostname[0] = 0;
+ }
+
+ if (request != NAMECODE) {
+ fprintf(stderr, "in.tnamed: bad request from %s\n",
+ inet_ntoa(client.sin_addr));
+ buffer[replylength++] = ERRORCODE;
+ buffer[replylength++] = 3; /* no error msg yet */
+ buffer[replylength++] = SYNTAXERROR;
+ fprintf(stderr,
+ "in.tnamed: request (%d) not NAMECODE\n", request);
+ sleep(5); /* pause before saying something negative */
+ goto sendreply;
+ }
+
+ if (hostname[0] == '!') {
+ /*
+ * !host!net name format is not implemented yet,
+ * only host alone.
+ */
+ fprintf(stderr, "in.tnamed: %s ",
+ inet_ntoa(client.sin_addr));
+ fprintf(stderr, "using !net!host format name ");
+ fprintf(stderr, "request\n");
+
+ buffer[replylength++] = ERRORCODE;
+ buffer[replylength++] = 0; /* no error msg yet */
+ buffer[replylength++] = UNDEFINEDERROR;
+ fprintf(stderr,
+ "in.tnamed: format (%s) not supported\n", hostname);
+ sleep(5); /* pause before saying something negative */
+ goto sendreply;
+ }
+
+ if ((hp = gethostbyname(hostname)) == NULL) {
+ buffer[replylength++] = ERRORCODE;
+ buffer[replylength++] = 0; /* no error msg yet */
+ buffer[replylength++] = NOTFOUNDERROR;
+ fprintf(stderr, "in.tnamed: name (%s) not found\n",
+ hostname);
+ sleep(5); /* pause before saying something negative */
+ goto sendreply;
+ }
+
+ if (hp->h_addrtype != AF_INET) {
+ buffer[replylength++] = ERRORCODE;
+ buffer[replylength++] = 0; /* no error msg yet */
+ buffer[replylength++] = UNDEFINEDERROR;
+ fprintf(stderr,
+ "in.tnamed: address type (%d) not AF_INET\n",
+ hp->h_addrtype);
+ sleep(5); /* pause before saying something negative */
+ goto sendreply;
+ }
+
+ fprintf(stderr, "in.tnamed: %s asked for address of %s",
+ inet_ntoa(client.sin_addr), hostname);
+ bcopy(hp->h_addr, (char *)&x, sizeof (x));
+ printf(" - it's %s\n", inet_ntoa(x));
+
+ buffer[replylength++] = ADDRESSCODE;
+ buffer[replylength++] = hp->h_length;
+ bcopy(hp->h_addr, &buffer[replylength], hp->h_length);
+ replylength += hp->h_length;
+
+ sendreply:
+ if (sendto(s, buffer, replylength, 0,
+ (struct sockaddr *)&client, clientlength)
+ != replylength) {
+ perror("in.tnamed: sendto error");
+ continue;
+ }
+ }
+}
+
+static int
+handler()
+{
+
+ exit(0);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/inc.flg b/usr/src/cmd/cmd-inet/usr.sbin/inc.flg
new file mode 100644
index 0000000000..5f0fa874b4
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/inc.flg
@@ -0,0 +1,33 @@
+#!/bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+find_files "s.*" usr/src/uts/common/gssapi/mechs/krb5/include
+find_files "s.*" usr/src/lib/gss_mechs/mech_krb5/include
+find_files "s.*.h" usr/src/lib/pam_modules/krb5
+find_files "s.*" usr/src/cmd/cmd-inet/usr.bin/tftp
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/inetadm/Makefile b/usr/src/cmd/cmd-inet/usr.sbin/inetadm/Makefile
new file mode 100644
index 0000000000..e7bb52c886
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/inetadm/Makefile
@@ -0,0 +1,51 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+PROG = inetadm
+SRCS = inetadm.c
+LNTS = $(SRCS:%.c=%.ln)
+OBJS = $(SRCS:%.c=%.o)
+
+include ../../../Makefile.cmd
+
+XGETFLAGS = -a
+LDLIBS += -lscf -luutil -linetsvc
+
+lint := LINTFLAGS = -ux
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+install: all $(ROOTUSRSBINPROG)
+
+clean:
+ $(RM) $(OBJS)
+
+lint: lint_SRCS
+
+include ../../../Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/inetadm/inetadm.c b/usr/src/cmd/cmd-inet/usr.sbin/inetadm/inetadm.c
new file mode 100644
index 0000000000..47d45ecb95
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/inetadm/inetadm.c
@@ -0,0 +1,1095 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * inetadm - administer services controlled by inetd and print out inetd
+ * service related information.
+ */
+
+#include <locale.h>
+#include <libintl.h>
+#include <libscf.h>
+#include <libscf_priv.h>
+#include <libuutil.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <inetsvc.h>
+#include <errno.h>
+
+#ifndef TEXT_DOMAIN
+#define TEXT_DOMAIN "SUNW_OST_OSCMD"
+#endif /* TEXT_DOMAIN */
+
+/* Strings for output to the user, and checking user's input */
+
+#define INETADM_TRUE_STR "TRUE"
+#define INETADM_FALSE_STR "FALSE"
+#define INETADM_ENABLED_STR "enabled"
+#define INETADM_DISABLED_STR "disabled"
+#define INETADM_DEFAULT_STR "default"
+
+/* String for checking if an instance is under inetd's control. */
+
+#define INETADM_INETD_STR "network/inetd"
+
+/*
+ * Used to hold a list of scf_value_t's whilst performing a transaction
+ * to write a proto list back.
+ */
+typedef struct scf_val_el {
+ scf_value_t *val;
+ uu_list_node_t link;
+} scf_val_el_t;
+
+/*
+ * Structure used to encapsulate argc and argv so they can be passed using the
+ * single data argument supplied by scf_walk_fmri() for the consumption of
+ * modify_inst_props_cb().
+ */
+typedef struct arglist {
+ int argc;
+ char **argv;
+} arglist_t;
+
+static scf_handle_t *h;
+
+static void
+scfdie()
+{
+ uu_die(gettext("Unexpected libscf error: %s. Exiting.\n"),
+ scf_strerror(scf_error()));
+}
+
+static void
+usage(boolean_t detailed)
+{
+
+ uu_warn(gettext(
+ "Usage:\n"
+ " inetadm\n"
+ " inetadm -?\n"
+ " inetadm -p\n"
+ " inetadm -l {FMRI | pattern}...\n"
+ " inetadm -e {FMRI | pattern}...\n"
+ " inetadm -d {FMRI | pattern}...\n"
+ " inetadm -m {FMRI | pattern}... {name=value}...\n"
+ " inetadm -M {name=value}...\n"));
+
+ if (!detailed)
+ exit(UU_EXIT_USAGE);
+
+ (void) fprintf(stdout, gettext(
+ "\n"
+ "Without any options inetadm lists all inetd managed services.\n"
+ "\n"
+ "Options:\n"
+ " -? Print help.\n"
+ " -p List all default inetd property values.\n"
+ " -l List all inetd property values for the inet "
+ "service(s).\n"
+ " -e Enable the inet service(s).\n"
+ " -d Disable the inet service(s).\n"
+ " -m Modify the inet service(s) inetd property values.\n"
+ " -M Modify default inetd property values.\n"));
+}
+
+/*
+ * Add the proto list contained in array 'plist' to entry 'entry', storing
+ * aside the scf_value_t's created and added to the entry in a list that the
+ * pointer referenced by sv_list is made to point at.
+ */
+static void
+add_proto_list(scf_transaction_entry_t *entry, scf_handle_t *hdl,
+ char **plist, uu_list_t **sv_list)
+{
+ scf_val_el_t *sv_el;
+ int i;
+
+ static uu_list_pool_t *sv_pool = NULL;
+
+ if ((sv_pool == NULL) &&
+ ((sv_pool = uu_list_pool_create("sv_pool",
+ sizeof (scf_val_el_t), offsetof(scf_val_el_t, link), NULL,
+ UU_LIST_POOL_DEBUG)) == NULL))
+ uu_die(gettext("Error: %s.\n"), uu_strerror(uu_error()));
+
+ if ((*sv_list = uu_list_create(sv_pool, NULL, 0)) == NULL)
+ uu_die(gettext("Error: %s.\n"), uu_strerror(uu_error()));
+
+ for (i = 0; plist[i] != NULL; i++) {
+ if ((sv_el = malloc(sizeof (scf_val_el_t))) == NULL)
+ uu_die(gettext("Error:"));
+
+ if (((sv_el->val = scf_value_create(hdl)) == NULL) ||
+ (scf_value_set_astring(sv_el->val, plist[i]) != 0) ||
+ (scf_entry_add_value(entry, sv_el->val) != 0))
+ scfdie();
+
+ uu_list_node_init(sv_el, &sv_el->link, sv_pool);
+ (void) uu_list_insert_after(*sv_list, NULL, sv_el);
+ }
+}
+
+/*
+ * A counterpart to add_proto_list(), this function removes and frees the
+ * scf_value_t's it added to entry 'entry'.
+ */
+static void
+remove_proto_list(scf_transaction_entry_t *entry, uu_list_t *sv_list)
+{
+ scf_val_el_t *sv_el;
+ void *cookie = NULL;
+
+ scf_entry_reset(entry);
+
+ while ((sv_el = uu_list_teardown(sv_list, &cookie)) != NULL) {
+ scf_value_destroy(sv_el->val);
+ free(sv_el);
+ }
+
+ uu_list_destroy(sv_list);
+}
+
+/*
+ * modify_prop takes an instance, property group, property name, type, and
+ * value, and modifies the specified property in the repository to the
+ * submitted value.
+ */
+
+static void
+modify_prop(const scf_instance_t *inst, const char *pg, const char *prop,
+ scf_type_t type, void *value)
+{
+ scf_transaction_t *tx;
+ scf_transaction_entry_t *ent;
+ scf_propertygroup_t *gpg;
+ scf_property_t *eprop;
+ scf_value_t *v;
+ int ret, create = 0;
+ char *fmri;
+ ssize_t max_fmri_len;
+
+ if ((gpg = scf_pg_create(h)) == NULL ||
+ (eprop = scf_property_create(h)) == NULL ||
+ (v = scf_value_create(h)) == NULL)
+ scfdie();
+
+ /* Get the property group or create it if it is missing. */
+ if (scf_instance_get_pg(inst, pg, gpg) == -1) {
+ if (scf_error() != SCF_ERROR_NOT_FOUND)
+ scfdie();
+
+ max_fmri_len = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH);
+ if ((fmri = malloc(max_fmri_len + 1)) == NULL)
+ uu_die(gettext("Error: Out of memory.\n"));
+
+ if (scf_instance_to_fmri(inst, fmri, max_fmri_len + 1) < 0)
+ scfdie();
+
+ syslog(LOG_NOTICE, "inetadm: Property group \"%s\" missing "
+ "from \"%s\", attempting to add it.\n", pg, fmri);
+ free(fmri);
+
+ if (scf_instance_add_pg(inst, pg, SCF_GROUP_FRAMEWORK, 0,
+ gpg) == -1) {
+ switch (scf_error()) {
+ case SCF_ERROR_EXISTS:
+ break;
+ case SCF_ERROR_PERMISSION_DENIED:
+ uu_die(gettext("Error: Permission denied.\n"));
+ default:
+ scfdie();
+ }
+ }
+ }
+
+ if (scf_pg_get_property(gpg, prop, eprop) == -1) {
+ if (scf_error() != SCF_ERROR_NOT_FOUND)
+ scfdie();
+
+ create = 1;
+ }
+
+ if ((tx = scf_transaction_create(h)) == NULL ||
+ (ent = scf_entry_create(h)) == NULL)
+ scfdie();
+
+ do {
+ uu_list_t *sv_list;
+
+ if (scf_transaction_start(tx, gpg) == -1) {
+ if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
+ scfdie();
+
+ uu_die(gettext("Error: Permission denied.\n"));
+ }
+
+ /* Modify the property or create it if it is missing */
+ if (create)
+ ret = scf_transaction_property_new(tx, ent, prop, type);
+ else
+ ret = scf_transaction_property_change_type(tx, ent,
+ prop, type);
+ if (ret == -1)
+ scfdie();
+
+ switch (type) {
+ case SCF_TYPE_BOOLEAN:
+ scf_value_set_boolean(v, *(uint8_t *)value);
+ break;
+ case SCF_TYPE_INTEGER:
+ scf_value_set_integer(v, *(int64_t *)value);
+ break;
+ case SCF_TYPE_ASTRING:
+ if (strcmp(prop, PR_PROTO_NAME) == 0) {
+ add_proto_list(ent, h, (char **)value,
+ &sv_list);
+ } else if (scf_value_set_astring(v, value) == -1) {
+ scfdie();
+ }
+ break;
+ default:
+ uu_die(gettext("Error: Internal inetadm error"));
+ }
+
+ if ((strcmp(prop, PR_PROTO_NAME) != 0) &&
+ (scf_entry_add_value(ent, v) == -1))
+ scfdie();
+
+ ret = scf_transaction_commit(tx);
+ if (ret == -1) {
+ if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
+ scfdie();
+
+ uu_die(gettext("Error: Permission denied.\n"));
+ }
+
+ scf_transaction_reset(tx);
+
+ if (ret == 0) {
+ if (scf_pg_update(gpg) == -1)
+ scfdie();
+ }
+
+ if (strcmp(prop, PR_PROTO_NAME) == 0)
+ remove_proto_list(ent, sv_list);
+
+ } while (ret == 0);
+
+ scf_value_destroy(v);
+ scf_entry_destroy(ent);
+ scf_transaction_destroy(tx);
+ scf_property_destroy(eprop);
+ scf_pg_destroy(gpg);
+}
+
+/*
+ * delete_prop takes an instance, property group name and property, and
+ * deletes the specified property from the repository.
+ */
+
+static void
+delete_prop(const scf_instance_t *inst, const char *pg, const char *prop)
+{
+ scf_transaction_t *tx;
+ scf_transaction_entry_t *ent;
+ scf_propertygroup_t *gpg;
+ scf_property_t *eprop;
+ int ret;
+
+ if ((gpg = scf_pg_create(h)) == NULL ||
+ (eprop = scf_property_create(h)) == NULL ||
+ (tx = scf_transaction_create(h)) == NULL ||
+ (ent = scf_entry_create(h)) == NULL)
+ scfdie();
+
+ if (scf_instance_get_pg(inst, pg, gpg) != SCF_SUCCESS) {
+ if (scf_error() != SCF_ERROR_NOT_FOUND)
+ scfdie();
+
+ uu_die(gettext("Error: \"%s\" property group missing.\n"), pg);
+ }
+
+ do {
+ if (scf_transaction_start(tx, gpg) != SCF_SUCCESS) {
+ if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
+ scfdie();
+
+ uu_die(gettext("Error: Permission denied.\n"));
+ }
+
+ if (scf_transaction_property_delete(tx, ent,
+ prop) != SCF_SUCCESS) {
+ if (scf_error() != SCF_ERROR_NOT_FOUND)
+ scfdie();
+
+ uu_die(
+ gettext("Error: \"%s\" property does not exist.\n"),
+ prop);
+ }
+
+ ret = scf_transaction_commit(tx);
+ if (ret < 0) {
+ if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
+ scfdie();
+
+ uu_die(gettext("Error: Permission denied.\n"));
+ }
+ if (ret == 0) {
+ scf_transaction_reset(tx);
+ if (scf_pg_update(gpg) == -1)
+ scfdie();
+ }
+ } while (ret == 0);
+
+ (void) scf_entry_destroy(ent);
+ scf_transaction_destroy(tx);
+ scf_property_destroy(eprop);
+ scf_pg_destroy(gpg);
+}
+
+/*
+ * commit_props evaluates an entire property list that has been created
+ * based on command line options, and either deletes or modifies properties
+ * as requested.
+ */
+
+static void
+commit_props(const scf_instance_t *inst, inetd_prop_t *mod, boolean_t defaults)
+{
+ int i;
+ uint8_t new_bool;
+ size_t numprops;
+
+ (void) get_prop_table(&numprops);
+
+ for (i = 0; i < numprops; i++) {
+ switch (mod[i].ip_error) {
+ case IVE_UNSET:
+ break;
+ case IVE_INVALID:
+ delete_prop(inst, mod[i].ip_pg, mod[i].ip_name);
+ break;
+ case IVE_VALID:
+ switch (mod[i].ip_type) {
+ case SCF_TYPE_ASTRING:
+ modify_prop(inst,
+ defaults ? PG_NAME_SERVICE_DEFAULTS :
+ mod[i].ip_pg, mod[i].ip_name,
+ SCF_TYPE_ASTRING,
+ (i == PT_PROTO_INDEX) ?
+ (void *)mod[i].ip_value.iv_proto_list :
+ (void *)mod[i].ip_value.iv_astring);
+ break;
+ case SCF_TYPE_INTEGER:
+ modify_prop(inst,
+ defaults ? PG_NAME_SERVICE_DEFAULTS :
+ mod[i].ip_pg, mod[i].ip_name,
+ SCF_TYPE_INTEGER, &mod[i].ip_value.iv_int);
+ break;
+ case SCF_TYPE_BOOLEAN:
+ new_bool = (mod[i].ip_value.iv_boolean) ? 1 : 0;
+
+ modify_prop(inst,
+ defaults ? PG_NAME_SERVICE_DEFAULTS :
+ mod[i].ip_pg, mod[i].ip_name,
+ SCF_TYPE_BOOLEAN, &new_bool);
+ break;
+ }
+ }
+ }
+}
+
+/*
+ * list_callback is the callback function to be handed to simple_walk_instances
+ * in list_services. It is called once on every instance on a machine. If
+ * that instance is controlled by inetd, it prints enabled/disabled, state,
+ * and instance FMRI.
+ */
+
+/*ARGSUSED*/
+static int
+list_callback(scf_handle_t *hin, scf_instance_t *inst, void *buf)
+{
+ ssize_t max_name_length;
+ char *inst_name;
+ scf_simple_prop_t *prop = NULL, *prop2 = NULL;
+ const uint8_t *enabled;
+ const char *state, *restart_str;
+
+ max_name_length = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH);
+ if ((inst_name = malloc(max_name_length + 1)) == NULL)
+ uu_die(gettext("Error: Out of memory.\n"));
+
+ /*
+ * Get the FMRI of the instance, and check if its delegated restarter
+ * is inetd.
+ */
+
+ if (scf_instance_to_fmri(inst, inst_name, max_name_length + 1) < 0)
+ return (SCF_FAILED);
+
+ if ((prop = scf_simple_prop_get(hin, inst_name, SCF_PG_GENERAL,
+ SCF_PROPERTY_RESTARTER)) == NULL)
+ goto out;
+
+ if ((restart_str = scf_simple_prop_next_ustring(prop)) == NULL)
+ goto out;
+
+ if (strstr(restart_str, INETADM_INETD_STR) == NULL)
+ goto out;
+
+ /* Free restarter prop so it can be reused below */
+ scf_simple_prop_free(prop);
+
+ /*
+ * We know that this instance is managed by inetd.
+ * Now get the enabled and state properties.
+ */
+
+ if (((prop = scf_simple_prop_get(hin, inst_name, SCF_PG_GENERAL,
+ SCF_PROPERTY_ENABLED)) == NULL) ||
+ ((enabled = scf_simple_prop_next_boolean(prop)) == NULL)) {
+ (void) uu_warn(gettext("Error: Instance %s is missing enabled "
+ "property.\n"), inst_name);
+ goto out;
+ }
+
+ if (((prop2 = scf_simple_prop_get(hin, inst_name, SCF_PG_RESTARTER,
+ SCF_PROPERTY_STATE)) == NULL) ||
+ ((state = scf_simple_prop_next_astring(prop2)) == NULL)) {
+ (void) uu_warn(gettext("Error: Instance %s is missing state "
+ "property.\n"), inst_name);
+ goto out;
+ }
+
+ /* Print enabled/disabled, state, and FMRI for the instance. */
+
+ if (*enabled)
+ (void) printf("%-10s%-15s%s\n", INETADM_ENABLED_STR, state,
+ inst_name);
+ else
+ (void) printf("%-10s%-15s%s\n", INETADM_DISABLED_STR, state,
+ inst_name);
+
+out:
+ free(inst_name);
+ scf_simple_prop_free(prop);
+ scf_simple_prop_free(prop2);
+ return (SCF_SUCCESS);
+}
+
+/*
+ * list_services calls list_callback on every instance on the machine.
+ */
+
+static void
+list_services()
+{
+ (void) printf("%-10s%-15s%s\n", "ENABLED", "STATE", "FMRI");
+
+ if (scf_simple_walk_instances(SCF_STATE_ALL, NULL, list_callback) ==
+ SCF_FAILED)
+ scfdie();
+}
+
+static void
+print_prop_val(int index, inetd_prop_t *prop)
+{
+ switch (prop->ip_type) {
+ case SCF_TYPE_ASTRING:
+ if (index == PT_PROTO_INDEX) {
+ int j = 0;
+ char **cpp = prop->ip_value.iv_proto_list;
+
+ /*
+ * Print proto string array as comma separated list.
+ */
+
+ (void) printf("\"%s", cpp[j]);
+ while (cpp[++j] != NULL)
+ (void) printf(",%s", cpp[j]);
+ (void) printf("\"\n");
+ } else {
+ (void) printf("\"%s\"\n",
+ prop->ip_value.iv_astring);
+ }
+ break;
+ case SCF_TYPE_INTEGER:
+ (void) printf("%lld\n", prop->ip_value.iv_int);
+ break;
+ case SCF_TYPE_BOOLEAN:
+ if (prop->ip_value.iv_boolean)
+ (void) printf("%s\n", INETADM_TRUE_STR);
+ else
+ (void) printf("%s\n", INETADM_FALSE_STR);
+ break;
+ }
+}
+
+/*
+ * list_props_cb is a callback function for scf_walk_fmri that lists all
+ * relevant inetd properties for an instance managed by inetd.
+ */
+
+/* ARGSUSED0 */
+static int
+list_props_cb(void *data, scf_walkinfo_t *wip)
+{
+ int i;
+ const char *instname = wip->fmri;
+ scf_simple_prop_t *prop;
+ inetd_prop_t *proplist;
+ const char *restart_str;
+ boolean_t is_rpc = B_FALSE;
+ size_t numprops;
+ scf_handle_t *h;
+ scf_error_t err;
+
+ if (((h = scf_handle_create(SCF_VERSION)) == NULL) ||
+ (scf_handle_bind(h) == -1))
+ scfdie();
+
+ /*
+ * Get the property that holds the name of this instance's
+ * restarter, and make sure that it is inetd.
+ */
+ if ((prop = scf_simple_prop_get(h, instname, SCF_PG_GENERAL,
+ SCF_PROPERTY_RESTARTER)) == NULL) {
+ if (scf_error() == SCF_ERROR_NOT_FOUND)
+ uu_die(gettext("Error: Specified service instance "
+ "\"%s\" has no restarter property. inetd is not "
+ "the delegated restarter of this instance.\n"),
+ instname);
+ if (scf_error() == SCF_ERROR_INVALID_ARGUMENT)
+ uu_die(gettext("Error: \"%s\" is not a valid service "
+ "instance.\n"), instname);
+
+ scfdie();
+ }
+
+ if (((restart_str = scf_simple_prop_next_ustring(prop)) == NULL) ||
+ (strstr(restart_str, INETADM_INETD_STR) == NULL)) {
+ uu_die(gettext("Error: inetd is not the delegated restarter of "
+ "specified service instance \"%s\".\n"), instname);
+ }
+
+ scf_simple_prop_free(prop);
+
+ /*
+ * This instance is controlled by inetd, so now we display all
+ * of its properties. First the mandatory properties, and then
+ * the properties that have default values, substituting the
+ * default values inherited from inetd as necessary (this is done
+ * for us by read_instance_props()).
+ */
+
+ if ((proplist = read_instance_props(h, instname, &numprops, &err)) ==
+ NULL) {
+ uu_die(gettext("Unexpected libscf error: %s. Exiting.\n"),
+ scf_strerror(err));
+ }
+ scf_handle_destroy(h);
+
+ (void) printf("%-9s%s\n", "SCOPE", "NAME=VALUE");
+
+ for (i = 0; i < numprops; i++) {
+ /* Skip rpc version properties if it's not an RPC service */
+ if ((strcmp(PR_RPC_LW_VER_NAME, proplist[i].ip_name) == 0) ||
+ (strcmp(PR_RPC_HI_VER_NAME, proplist[i].ip_name) == 0))
+ if (!is_rpc)
+ continue;
+
+ /* If it's not an unset property, print it out. */
+ if (proplist[i].ip_error != IVE_UNSET) {
+ if (strcmp(PR_ISRPC_NAME, proplist[i].ip_name) == 0)
+ is_rpc = proplist[i].ip_value.iv_boolean;
+
+ (void) printf("%-9s%s=",
+ proplist[i].from_inetd ? INETADM_DEFAULT_STR : "",
+ proplist[i].ip_name);
+ print_prop_val(i, &proplist[i]);
+ continue;
+ }
+
+ /* arg0 is non-default, but also doesn't have to be set. */
+
+ if (i == PT_ARG0_INDEX)
+ continue;
+
+ /* all other properties should have values. */
+ if (proplist[i].ip_default) {
+ (void) uu_warn(gettext("Error: Property %s is missing "
+ "and has no defined default value.\n"),
+ proplist[i].ip_name);
+ } else {
+ (void) uu_warn(gettext("Error: Required property %s is "
+ "missing.\n"), proplist[i].ip_name);
+ }
+ }
+
+ free_instance_props(proplist);
+ return (0);
+}
+
+/*
+ * set_svc_enable_cb is a callback function for scf_walk_fmri that sets the
+ * enabled property in the repository for an instance based on the value given
+ * by 'data'.
+ */
+
+static int
+set_svc_enable_cb(void *data, scf_walkinfo_t *wip)
+{
+ uint8_t desired = *(uint8_t *)data;
+ const char *instname = wip->fmri;
+
+ if (desired) {
+ if (smf_enable_instance(instname, 0) == 0)
+ return (0);
+ } else {
+ if (smf_disable_instance(instname, 0) == 0)
+ return (0);
+ }
+
+ switch (scf_error()) {
+ case SCF_ERROR_INVALID_ARGUMENT:
+ uu_die(gettext("Error: \"%s\" is not a valid service "
+ "instance.\n"), instname);
+ break;
+ case SCF_ERROR_NOT_FOUND:
+ uu_die(gettext("Error: Service instance \"%s\" not found.\n"),
+ instname);
+ break;
+ default:
+ scfdie();
+ }
+
+ return (0);
+}
+
+/*
+ * list_defaults lists all the default property values being provided by
+ * inetd.
+ */
+
+static void
+list_defaults()
+{
+ scf_handle_t *h;
+ scf_error_t err;
+ int i;
+ inetd_prop_t *proptable;
+ size_t numprops;
+
+ if (((h = scf_handle_create(SCF_VERSION)) == NULL) ||
+ (scf_handle_bind(h) == -1))
+ scfdie();
+
+ if ((proptable = read_default_props(h, &numprops, &err)) == NULL) {
+ uu_die(gettext("Unexpected libscf error: %s. Exiting.\n"),
+ scf_strerror(err));
+ }
+
+ (void) printf("NAME=VALUE\n");
+
+ for (i = 0; i < numprops; i++) {
+ if (!proptable[i].ip_default)
+ continue;
+
+ if (proptable[i].ip_error == IVE_UNSET) {
+ (void) uu_warn(gettext("Error: Default property %s "
+ "missing.\n"), proptable[i].ip_name);
+ continue;
+ }
+
+ (void) printf("%s=", proptable[i].ip_name);
+ print_prop_val(i, &proptable[i]);
+ }
+
+ free_instance_props(proptable);
+}
+
+/*
+ * modify_inst_props_cb is a callback function for scf_walk_fmri that modifies
+ * the properties that are given as name=value pairs on the command line
+ * to the requested value.
+ */
+
+static int
+modify_inst_props_cb(void *data, scf_walkinfo_t *wip)
+{
+ int i, j;
+ char *value;
+ const char *fmri = wip->fmri;
+ scf_instance_t *inst = wip->inst;
+ inetd_prop_t *mod, *prop_table;
+ size_t numprops;
+ ssize_t max_val;
+ int64_t new_int;
+ int argc = ((arglist_t *)data)->argc;
+ char **argv = ((arglist_t *)data)->argv;
+
+ if ((max_val = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH)) < 0)
+ scfdie();
+
+ prop_table = get_prop_table(&numprops);
+
+ if ((mod = malloc(numprops * sizeof (inetd_prop_t))) == NULL)
+ uu_die(gettext("Error: Out of memory.\n"));
+
+ (void) memcpy(mod, prop_table, numprops * sizeof (inetd_prop_t));
+
+ /*
+ * For each property to be changed, look up the property name in the
+ * property table. Change each property in the mod array, and then
+ * write the entire thing back.
+ */
+ for (i = 0; i < argc; i++) {
+ /* Separate argument into name and value pair */
+ if ((value = strchr(argv[i], '=')) == NULL)
+ uu_die(gettext("Error: Malformed name=value pair "
+ "\"%s\"\n"), argv[i]);
+
+ *value = '\0';
+ value++;
+
+ /* Find property name in array of properties */
+ for (j = 0; j < numprops; j++) {
+ if (strcmp(mod[j].ip_name, argv[i]) == 0)
+ break;
+ }
+
+ if (j >= numprops)
+ uu_die(gettext("Error: \"%s\" is not a valid "
+ "property.\n"), argv[i]);
+
+ if (*value == '\0') {
+ if ((mod[j].ip_default) || (j == PT_ARG0_INDEX)) {
+ /* mark property for deletion */
+ mod[j].ip_error = IVE_INVALID;
+
+ /* return the '=' taken out above */
+ *(--value) = '=';
+
+ continue;
+ } else {
+ uu_die(gettext("\"%s\" is a mandatory "
+ "property and can not be deleted.\n"),
+ argv[i]);
+ }
+ }
+
+ switch (mod[j].ip_type) {
+ case SCF_TYPE_INTEGER:
+ if (uu_strtoint(value, &new_int, sizeof (new_int), NULL,
+ NULL, NULL) == -1)
+ uu_die(gettext("Error: \"%s\" is not a valid "
+ "integer value.\n"), value);
+
+ mod[j].ip_value.iv_int = new_int;
+ break;
+ case SCF_TYPE_ASTRING:
+ if (j == PT_PROTO_INDEX) {
+ if ((mod[j].ip_value.iv_proto_list =
+ get_protos(value)) == NULL) {
+ if (errno == ENOMEM) {
+ uu_die(gettext(
+ "Error: Out of memory.\n"));
+ } else if (errno == E2BIG) {
+ uu_die(gettext(
+ "Error: String value in "
+ "%s property longer than "
+ "%l characters.\n"),
+ PR_PROTO_NAME, max_val);
+ } else {
+ uu_die(gettext(
+ "Error: No values "
+ "specified for %s "
+ "property.\n"),
+ PR_PROTO_NAME);
+ }
+ }
+ } else if (strlen(value) >= max_val) {
+ uu_die(gettext("Error: String value is longer "
+ "than %l characters.\n"), max_val);
+ } else if ((mod[j].ip_value.iv_astring = strdup(value))
+ == NULL) {
+ uu_die(gettext("Error: Out of memory.\n"));
+ }
+ break;
+ case SCF_TYPE_BOOLEAN:
+ if (strcasecmp(value, INETADM_TRUE_STR) == 0)
+ mod[j].ip_value.iv_boolean = B_TRUE;
+ else if (strcasecmp(value, INETADM_FALSE_STR) == 0)
+ mod[j].ip_value.iv_boolean = B_FALSE;
+ else
+ uu_die(gettext("Error: \"%s\" is not a valid "
+ "boolean value. (TRUE or FALSE)\n"), value);
+ }
+ /* mark property for modification */
+ mod[j].ip_error = IVE_VALID;
+
+ /* return the '=' taken out above */
+ *(--value) = '=';
+ }
+
+ commit_props(inst, mod, B_FALSE);
+ free(mod);
+ if (smf_refresh_instance(fmri) != 0)
+ uu_die(gettext("Error: Unable to refresh instance %s.\n"),
+ fmri);
+
+ return (0);
+}
+
+/*
+ * modify_defaults takes n name=value pairs for inetd default property values,
+ * parses them, and then modifies the values in the repository.
+ */
+
+static void
+modify_defaults(int argc, char *argv[])
+{
+ int i, j;
+ char *value;
+ scf_instance_t *inst;
+ inetd_prop_t *mod, *prop_table;
+ size_t numprops;
+ ssize_t max_val;
+ int64_t new_int;
+
+ if ((inst = scf_instance_create(h)) == NULL)
+ scfdie();
+
+ if (scf_handle_decode_fmri(h, INETD_INSTANCE_FMRI, NULL, NULL,
+ inst, NULL, NULL, SCF_DECODE_FMRI_EXACT) == -1) {
+ if (scf_error() == SCF_ERROR_NOT_FOUND) {
+ uu_die(gettext("inetd instance missing in repository."
+ "\n"));
+ } else {
+ scfdie();
+ }
+ }
+
+ if ((max_val = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH)) < 0)
+ scfdie();
+
+ prop_table = get_prop_table(&numprops);
+
+ if ((mod = malloc(numprops * sizeof (inetd_prop_t))) == NULL)
+ uu_die(gettext("Error: Out of memory.\n"));
+
+ (void) memcpy(mod, prop_table, numprops * sizeof (inetd_prop_t));
+
+ for (i = 0; i < argc; i++) {
+ /* Separate argument into name and value pair */
+ if ((value = strchr(argv[i], '=')) == NULL)
+ uu_die(gettext("Error: Malformed name=value pair \"%s"
+ "\"\n"), argv[i]);
+
+ *value = '\0';
+ value++;
+
+ /* Find property name in array of defaults */
+ for (j = 0; j < numprops; j++) {
+ if (!mod[j].ip_default)
+ continue;
+ if (strcmp(mod[j].ip_name, argv[i]) == 0)
+ break;
+ }
+
+ if (j >= numprops)
+ uu_die(gettext("Error: \"%s\" is not a default inetd "
+ "property.\n"), argv[i]);
+
+ if (*value == '\0')
+ uu_die(gettext("Cannot accept NULL values for default "
+ "properties.\n"));
+
+ switch (mod[j].ip_type) {
+ case SCF_TYPE_INTEGER:
+ if (uu_strtoint(value, &new_int, sizeof (new_int), NULL,
+ NULL, NULL) == -1)
+ uu_die(gettext("Error: \"%s\" is not a valid "
+ "integer value.\n"), value);
+
+ mod[j].ip_value.iv_int = new_int;
+ break;
+ case SCF_TYPE_ASTRING:
+ if (strlen(value) >= max_val)
+ uu_die(gettext("Error: String value is longer "
+ "than %l characters.\n"), max_val);
+ if ((mod[j].ip_value.iv_astring = strdup(value))
+ == NULL)
+ uu_die(gettext("Error: Out of memory.\n"));
+ break;
+ case SCF_TYPE_BOOLEAN:
+ if (strcasecmp(value, INETADM_TRUE_STR) == 0)
+ mod[j].ip_value.iv_boolean = B_TRUE;
+ else if (strcasecmp(value, INETADM_FALSE_STR) == 0)
+ mod[j].ip_value.iv_boolean = B_FALSE;
+ else
+ uu_die(gettext("Error: \"%s\" is not a valid "
+ "boolean value. (TRUE or FALSE)\n"), value);
+ }
+ /* mark property for modification */
+ mod[j].ip_error = IVE_VALID;
+ }
+
+ commit_props(inst, mod, B_TRUE);
+ free(mod);
+ scf_instance_destroy(inst);
+ if (refresh_inetd() != 0)
+ uu_warn(gettext("Warning: Unable to refresh inetd.\n"));
+}
+
+int
+main(int argc, char *argv[])
+{
+ int opt;
+ uint_t lflag, eflag, dflag, pflag, mflag, Mflag;
+ uint8_t enable;
+ scf_error_t serr;
+ int exit_status = 0;
+
+ (void) setlocale(LC_ALL, "");
+ (void) textdomain(TEXT_DOMAIN);
+
+ if ((h = scf_handle_create(SCF_VERSION)) == NULL)
+ scfdie();
+
+ if (scf_handle_bind(h) == -1)
+ uu_die(gettext("Error: Couldn't bind to svc.configd.\n"));
+
+ if (argc == 1) {
+ list_services();
+ goto out;
+ }
+
+ lflag = eflag = dflag = pflag = mflag = Mflag = 0;
+ while ((opt = getopt(argc, argv, "ledpMm?")) != -1) {
+ switch (opt) {
+ case 'l':
+ lflag = 1;
+ break;
+ case 'e':
+ eflag = 1;
+ break;
+ case 'd':
+ dflag = 1;
+ break;
+ case 'p':
+ pflag = 1;
+ break;
+ case 'M':
+ Mflag = 1;
+ break;
+ case 'm':
+ mflag = 1;
+ break;
+ case '?':
+ if (optopt == '?') {
+ usage(B_TRUE);
+ goto out;
+ } else {
+ usage(B_FALSE);
+ }
+ default:
+ usage(B_FALSE);
+ }
+ }
+
+ /*
+ * All options are mutually exclusive, and we must have an option
+ * if we reached here.
+ */
+ if (lflag + eflag + dflag + pflag + mflag + Mflag != 1)
+ usage(B_FALSE);
+
+ argv += optind;
+ argc -= optind;
+ if ((pflag == 0) && (argc == 0))
+ usage(B_FALSE);
+
+ serr = 0;
+ if (lflag) {
+ serr = scf_walk_fmri(h, argc, argv, 0, list_props_cb, NULL,
+ &exit_status, uu_warn);
+ } else if (dflag) {
+ enable = 0;
+ serr = scf_walk_fmri(h, argc, argv, 0, set_svc_enable_cb,
+ &enable, &exit_status, uu_warn);
+ } else if (eflag) {
+ enable = 1;
+ serr = scf_walk_fmri(h, argc, argv, 0, set_svc_enable_cb,
+ &enable, &exit_status, uu_warn);
+ } else if (mflag) {
+ arglist_t args;
+ char **cpp = argv;
+ uint_t fmri_args = 0;
+
+ /* count number of fmri arguments */
+ while ((fmri_args < argc) && (strchr(*cpp, '=') == NULL)) {
+ fmri_args++;
+ cpp++;
+ }
+
+ /* if no x=y args or no fmri, show usage */
+ if ((fmri_args == argc) || (fmri_args == 0))
+ usage(B_FALSE);
+
+ /* setup args for modify_inst_props_cb */
+ args.argc = argc - fmri_args;
+ args.argv = argv + fmri_args;
+
+ serr = scf_walk_fmri(h, fmri_args, argv, 0,
+ modify_inst_props_cb, &args, &exit_status, uu_warn);
+ } else if (Mflag) {
+ modify_defaults(argc, argv);
+ } else if (pflag) {
+ /* ensure there's no trailing garbage */
+ if (argc != 0)
+ usage(B_FALSE);
+ list_defaults();
+ }
+ if (serr != 0) {
+ uu_warn(gettext("failed to iterate over instances: %s"),
+ scf_strerror(serr));
+ exit(UU_EXIT_FATAL);
+ }
+
+out:
+ (void) scf_handle_unbind(h);
+ scf_handle_destroy(h);
+
+ return (exit_status);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/inetconv/Makefile b/usr/src/cmd/cmd-inet/usr.sbin/inetconv/Makefile
new file mode 100644
index 0000000000..4de2d22645
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/inetconv/Makefile
@@ -0,0 +1,45 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+PROG = inetconv
+
+include ../../../Makefile.cmd
+
+LDLIBS += -lscf -linetsvc
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+install: all $(ROOTUSRSBINPROG)
+
+clean:
+
+lint: lint_PROG
+
+include ../../../Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/inetconv/inetconv.c b/usr/src/cmd/cmd-inet/usr.sbin/inetconv/inetconv.c
new file mode 100644
index 0000000000..fcc10d0a44
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/inetconv/inetconv.c
@@ -0,0 +1,1527 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * inetconv - convert inetd.conf entries into smf(5) service manifests,
+ * import them into smf(5) repository
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <grp.h>
+#include <errno.h>
+#include <limits.h>
+#include <locale.h>
+#include <libintl.h>
+#include <libscf.h>
+#include <inetsvc.h>
+#include <rpc/nettype.h>
+
+/* exit codes */
+#define EXIT_SUCCESS 0 /* succeeded */
+#define EXIT_USAGE 1 /* bad options */
+#define EXIT_ERROR_CONV 2 /* error(s) coverting inetd.conf entries */
+#define EXIT_ERROR_IMP 3 /* error(s) importing manifests */
+#define EXIT_ERROR_SYS 4 /* system error */
+#define EXIT_ERROR_ENBL 5 /* error(s) enabling services */
+
+#ifndef TEXT_DOMAIN
+#define TEXT_DOMAIN "SUNW_OST_OSCMD"
+#endif
+
+#define MAIN_CONFIG "/etc/inet/inetd.conf"
+#define ALT_CONFIG "/etc/inetd.conf"
+
+#define MANIFEST_DIR "/var/svc/manifest/network"
+#define MANIFEST_RPC_DIR MANIFEST_DIR ## "/rpc"
+#define SVCCFG_PATH "/usr/sbin/svccfg"
+
+/* maximum allowed length of an inetd.conf format line */
+#define MAX_SRC_LINELEN 32768
+
+/* Version of inetconv, used as a marker in services we generate */
+#define INETCONV_VERSION 1
+
+struct inetconfent {
+ /* fields as read from inetd.conf format line */
+ char *service;
+ char *endpoint;
+ char *protocol;
+ char *wait_status;
+ char *username;
+ char *server_program;
+ char *server_args;
+ /* information derived from above fields */
+ boolean_t wait;
+ boolean_t isrpc;
+ int rpc_low_version;
+ int rpc_high_version;
+ char *rpc_prog;
+ char *groupname;
+ char *exec;
+ char *arg0;
+};
+
+struct fileinfo {
+ FILE *fp;
+ char *filename;
+ int lineno;
+ int failcnt;
+};
+
+static char *progname;
+
+static boolean_t import = B_TRUE;
+
+/* start of manifest XML template strings */
+static const char xml_header[] =
+"<?xml version='1.0'?>\n"
+"<!DOCTYPE service_bundle SYSTEM "
+"'/usr/share/lib/xml/dtd/service_bundle.dtd.1'>\n";
+
+static const char xml_comment[] =
+"<!--\n"
+" Service manifest for the %s service.\n"
+"\n"
+" Generated by inetconv(1M) from inetd.conf(4).\n"
+"-->\n\n";
+
+static const char xml_service_bundle[] =
+"<service_bundle type='manifest' name='inetconv:%s'>\n\n";
+
+static const char xml_service_name[] =
+"<service\n"
+" name='network/%s'\n"
+" type='service'\n"
+" version='1'>\n\n";
+
+static const char xml_instance[] =
+" <create_default_instance enabled='true'/>\n\n";
+
+static const char xml_restarter[] =
+" <restarter>\n"
+" <service_fmri value='%s' />\n"
+" </restarter>\n\n";
+
+static const char xml_exec_method_start[] =
+" <!--\n"
+" Set a timeout of 0 to signify to inetd that we don't want to\n"
+" timeout this service, since the forked process is the one that\n"
+" does the service's work. This is the case for most/all legacy\n"
+" inetd services; for services written to take advantage of SMF\n"
+" capabilities, the start method should fork off a process to\n"
+" handle the request and return a success code.\n"
+" -->\n"
+" <exec_method\n"
+" type='method'\n"
+" name='%s'\n"
+" %s='%s'\n"
+" timeout_seconds='0'>\n"
+" <method_context>\n"
+" <method_credential %s='%s' group='%s' />\n"
+" </method_context>\n";
+
+static const char xml_arg0[] =
+" <propval name='%s' type='astring'\n"
+" value='%s' />\n";
+
+static const char xml_exec_method_end[] =
+" </exec_method>\n\n";
+
+static const char xml_exec_method_disable[] =
+" <!--\n"
+" Use inetd's built-in kill support to disable services.\n"
+" -->\n"
+" <exec_method\n"
+" type='method'\n"
+" name='%s'\n"
+" %s=':kill'\n"
+" timeout_seconds='0'>\n";
+
+static const char xml_exec_method_offline[] =
+" <!--\n"
+" Use inetd's built-in process kill support to offline wait type\n"
+" services.\n"
+" -->\n"
+" <exec_method\n"
+" type='method'\n"
+" name='%s'\n"
+" %s=':kill_process'\n"
+" timeout_seconds='0'>\n";
+
+static const char xml_inetconv_group_start[] =
+" <!--\n"
+" This property group is used to record information about\n"
+" how this manifest was created. It is an implementation\n"
+" detail which should not be modified or deleted.\n"
+" -->\n"
+" <property_group name='%s' type='framework'>\n"
+" <propval name='%s' type='boolean' value='%s' />\n"
+" <propval name='%s' type='integer' value='%d' />\n"
+" <propval name='%s' type='astring' value=\n"
+"'%s %s %s %s %s %s%s%s'\n"
+" />\n";
+
+static const char xml_property_group_start[] =
+" <property_group name='%s' type='framework'>\n"
+" <propval name='%s' type='astring' value='%s' />\n"
+" <propval name='%s' type='astring' value='%s' />\n"
+" <propval name='%s' type='astring' value='%s' />\n"
+" <propval name='%s' type='boolean' value='%s' />\n"
+" <propval name='%s' type='boolean' value='%s' />\n";
+
+static const char xml_property_group_rpc[] =
+" <propval name='%s' type='integer' value='%d' />\n"
+" <propval name='%s' type='integer' value='%d' />"
+"\n";
+
+static const char xml_property_group_end[] =
+" </property_group>\n\n";
+
+static const char xml_stability[] =
+" <stability value='External' />\n\n";
+
+static const char xml_template[] =
+" <template>\n"
+" <common_name>\n"
+" <loctext xml:lang='C'>\n"
+"%s\n"
+" </loctext>\n"
+" </common_name>\n"
+" </template>\n";
+
+static const char xml_footer[] =
+"</service>\n"
+"\n"
+"</service_bundle>\n";
+/* end of manifest XML template strings */
+
+static void *
+safe_malloc(size_t size)
+{
+ void *cp;
+
+ if ((cp = malloc(size)) == NULL) {
+ (void) fprintf(stderr, gettext("%s: malloc failed: %s\n"),
+ progname, strerror(errno));
+ exit(EXIT_ERROR_SYS);
+ }
+ return (cp);
+}
+
+static char *
+safe_strdup(char *s)
+{
+ char *cp;
+
+ if ((cp = strdup(s)) == NULL) {
+ (void) fprintf(stderr, gettext("%s: strdup failed: %s\n"),
+ progname, strerror(errno));
+ exit(EXIT_ERROR_SYS);
+ }
+ return (cp);
+}
+
+static char *
+propertyname(char *name, char *prefix)
+{
+ static char *buf;
+ size_t len;
+ int c;
+ char *cp;
+
+ /* free any memory allocated by a previous call */
+ free(buf);
+
+ len = strlen(name) + strlen(prefix) + 1;
+ buf = safe_malloc(len);
+ buf[0] = '\0';
+
+ /*
+ * Property names must match the regular expression:
+ * ([A-Za-z][_A-Za-z0-9.-]*,)?[A-Za-z][_A-Za-z0-9-]*
+ */
+
+ /*
+ * Make sure the first character is alphabetic, if not insert prefix.
+ * Can't use isalpha() here as its locale dependent but the property
+ * name regular expression isn't.
+ */
+ c = name[0];
+ if ((c < 'A' || c > 'Z') && (c < 'a' || c > 'z')) {
+ (void) strlcat(buf, prefix, len);
+ }
+ (void) strlcat(buf, name, len);
+
+ /* convert any dissallowed characters into '_' */
+ for (cp = buf; *cp != '\0'; cp++) {
+ if ((*cp < 'A' || *cp > 'Z') && (*cp < 'a' || *cp > 'z') &&
+ (*cp < '0' || *cp > '9') && (*cp != '.') && (*cp != '-'))
+ *cp = '_';
+ }
+ return (buf);
+}
+
+static char *
+servicename(struct inetconfent *iconf)
+{
+ static char *buf;
+ size_t len;
+ char *proto;
+
+ /* free any memory allocated by a previous call */
+ free(buf);
+
+ len = strlen(iconf->service) + strlen(iconf->protocol) +
+ sizeof ("rpc-/visible");
+ buf = safe_malloc(len);
+
+ /*
+ * Combine the service and protocol fields to produce a unique
+ * manifest service name. The syntax of a service name is:
+ * prop(/prop)*
+ */
+ (void) strlcpy(buf, propertyname(iconf->service,
+ iconf->isrpc ? "rpc-": "s-"), len);
+ (void) strlcat(buf, "/", len);
+
+ proto = iconf->protocol;
+ if (iconf->isrpc && (strcmp(iconf->protocol, "rpc/*") == 0))
+ proto = "rpc/visible";
+ (void) strlcat(buf, propertyname(proto, "p-"), len);
+ return (buf);
+}
+
+static boolean_t
+is_v6only(char *protocol)
+{
+ /* returns true if protocol is an IPv6 only protocol */
+ if ((strcmp(protocol, SOCKET_PROTO_TCP6_ONLY) == 0) ||
+ (strcmp(protocol, SOCKET_PROTO_UDP6_ONLY) == 0))
+ return (B_TRUE);
+ return (B_FALSE);
+}
+
+static char *
+invalid_props(inetd_prop_t *p)
+{
+ static char
+ buf[sizeof (" service-name endpoint-type protocol wait-status")];
+
+ buf[0] = '\0';
+ if ((p[PT_SVC_NAME_INDEX].ip_error == IVE_INVALID) ||
+ (p[PT_SVC_NAME_INDEX].ip_error == IVE_UNSET) ||
+ (p[PT_RPC_LW_VER_INDEX].ip_error == IVE_INVALID) ||
+ (p[PT_RPC_HI_VER_INDEX].ip_error == IVE_INVALID))
+ (void) strlcat(buf, " service-name", sizeof (buf));
+ if ((p[PT_SOCK_TYPE_INDEX].ip_error == IVE_INVALID) ||
+ (p[PT_SOCK_TYPE_INDEX].ip_error == IVE_UNSET))
+ (void) strlcat(buf, " endpoint-type", sizeof (buf));
+ if ((p[PT_PROTO_INDEX].ip_error == IVE_INVALID) ||
+ (p[PT_PROTO_INDEX].ip_error == IVE_UNSET) ||
+ (p[PT_ISRPC_INDEX].ip_error == IVE_INVALID))
+ (void) strlcat(buf, " protocol", sizeof (buf));
+ if (p[PT_ISWAIT_INDEX].ip_error == IVE_INVALID)
+ (void) strlcat(buf, " wait-status", sizeof (buf));
+ return (buf);
+}
+
+/*
+ * wrapper around put_prop_value() that errors and exits on malloc failures,
+ * returns -1 on other failures, else returns 0.
+ */
+static int
+my_put_prop_value(inetd_prop_t *props, char *pname, void *value)
+{
+ if (put_prop_value(props, pname, value) == 0)
+ return (0);
+
+ if (errno == ENOMEM) {
+ (void) fprintf(stderr, gettext("%s: malloc failed: %s\n"),
+ progname, strerror(errno));
+ exit(EXIT_ERROR_SYS);
+ }
+ return (-1);
+}
+
+static boolean_t
+valid_basic_properties(struct inetconfent *iconf, struct fileinfo *finfo)
+{
+ size_t prop_size;
+ inetd_prop_t *prop, *inetd_properties;
+ boolean_t valid = B_TRUE;
+ char *proto = iconf->protocol;
+ char *svc_name = iconf->service;
+
+ inetd_properties = get_prop_table(&prop_size);
+ prop = safe_malloc(prop_size * sizeof (inetd_prop_t));
+ (void) memcpy(prop, inetd_properties,
+ prop_size * sizeof (inetd_prop_t));
+
+ (void) put_prop_value(prop, PR_ISRPC_NAME, &iconf->isrpc);
+ (void) put_prop_value(prop, PR_ISWAIT_NAME, &iconf->wait);
+ if (iconf->isrpc) {
+ (void) put_prop_value(prop, PR_RPC_LW_VER_NAME,
+ &iconf->rpc_low_version);
+ (void) put_prop_value(prop, PR_RPC_HI_VER_NAME,
+ &iconf->rpc_high_version);
+ svc_name = iconf->rpc_prog;
+ proto += 4; /* skip 'rpc/' */
+ }
+
+ if ((my_put_prop_value(prop, PR_SOCK_TYPE_NAME, iconf->endpoint)
+ != 0) ||
+ (my_put_prop_value(prop, PR_SVC_NAME_NAME, svc_name) != 0) ||
+ (my_put_prop_value(prop, PR_PROTO_NAME, proto) != 0))
+ valid = B_FALSE;
+
+ if (!valid_props(prop, NULL, NULL, NULL, NULL) || !valid) {
+ valid = B_FALSE;
+ (void) fprintf(stderr, gettext("%s: Error %s line %d "
+ "invalid or inconsistent fields:%s\n"), progname,
+ finfo->filename, finfo->lineno,
+ invalid_props(prop));
+ }
+
+ free_instance_props(prop);
+ return (valid);
+}
+
+static boolean_t
+valid_inetconfent(struct inetconfent *iconf, struct fileinfo *finfo)
+{
+ boolean_t valid = B_TRUE;
+ size_t len;
+ char *cp, *endp;
+ struct passwd *pwd;
+ struct group *grp;
+ struct stat statb;
+ char *proto = iconf->protocol;
+
+ iconf->isrpc = B_FALSE;
+ if (strncmp(iconf->protocol, "rpc/", 4) == 0) {
+ iconf->isrpc = B_TRUE;
+ iconf->rpc_prog = safe_strdup(iconf->service);
+
+ /* set RPC version numbers */
+ iconf->rpc_low_version = 1;
+ iconf->rpc_high_version = 1;
+ if ((cp = strrchr(iconf->rpc_prog, '/')) != NULL) {
+ *cp = '\0';
+ if (*++cp != '\0') {
+ errno = 0;
+ iconf->rpc_low_version = strtol(cp, &endp, 10);
+ if (errno != 0)
+ goto vererr;
+ cp = endp;
+ if (*cp == '-') {
+ if (*++cp == '\0')
+ goto vererr;
+ errno = 0;
+ iconf->rpc_high_version = strtol(cp,
+ &endp, 10);
+ if ((errno != 0) || (*endp != '\0'))
+ goto vererr;
+ } else if (*cp == '\0') {
+ iconf->rpc_high_version =
+ iconf->rpc_low_version;
+ } else {
+vererr:
+ (void) fprintf(stderr, gettext(
+ "%s: Error %s line %d invalid RPC "
+ "version in service: %s\n"),
+ progname, finfo->filename,
+ finfo->lineno, iconf->service);
+ valid = B_FALSE;
+ }
+ }
+ }
+ proto += 4; /* skip 'rpc/' */
+ }
+ /* tcp6only and udp6only are not valid in inetd.conf */
+ if (is_v6only(proto)) {
+ (void) fprintf(stderr, gettext("%s: Error %s line %d "
+ "invalid protocol: %s\n"), progname,
+ finfo->filename, finfo->lineno, proto);
+ valid = B_FALSE;
+ }
+
+ if (strcmp(iconf->wait_status, "wait") == 0) {
+ iconf->wait = B_TRUE;
+ } else if (strcmp(iconf->wait_status, "nowait") == 0) {
+ iconf->wait = B_FALSE;
+ } else {
+ (void) fprintf(stderr,
+ gettext("%s: Error %s line %d invalid wait-status: %s\n"),
+ progname, finfo->filename, finfo->lineno,
+ iconf->wait_status);
+ valid = B_FALSE;
+ }
+
+ /* look up the username to set the groupname */
+ if ((pwd = getpwnam(iconf->username)) == NULL) {
+ (void) fprintf(stderr,
+ gettext("%s: Error %s line %d unknown user: %s\n"),
+ progname, finfo->filename, finfo->lineno,
+ iconf->username);
+ valid = B_FALSE;
+ } else {
+ if ((grp = getgrgid(pwd->pw_gid)) != NULL) {
+ iconf->groupname = safe_strdup(grp->gr_name);
+ } else {
+ /* use the group ID if no groupname */
+ char s[1];
+
+ len = snprintf(s, 1, "%d", pwd->pw_gid) + 1;
+ iconf->groupname = safe_malloc(len);
+ (void) snprintf(iconf->groupname, len, "%d",
+ pwd->pw_gid);
+ }
+ }
+
+ /* check for internal services */
+ if (strcmp(iconf->server_program, "internal") == 0) {
+ valid = B_FALSE;
+ if ((strcmp(iconf->service, "echo") == 0) ||
+ (strcmp(iconf->service, "discard") == 0) ||
+ (strcmp(iconf->service, "time") == 0) ||
+ (strcmp(iconf->service, "daytime") == 0) ||
+ (strcmp(iconf->service, "chargen") == 0)) {
+ (void) fprintf(stderr, gettext(
+ "%s: Error %s line %d the SUNWcnsr and SUNWcnsu"
+ " packages contain the internal services\n"),
+ progname, finfo->filename, finfo->lineno);
+ } else {
+ (void) fprintf(stderr, gettext("%s: Error %s line %d "
+ "unknown internal service: %s\n"), progname,
+ finfo->filename, finfo->lineno, iconf->service);
+ }
+ } else if ((stat(iconf->server_program, &statb) == -1) &&
+ (errno == ENOENT)) {
+ (void) fprintf(stderr, gettext(
+ "%s: Error %s line %d server-program not found: %s\n"),
+ progname, finfo->filename, finfo->lineno,
+ iconf->server_program);
+ valid = B_FALSE;
+ }
+
+ return (valid && valid_basic_properties(iconf, finfo));
+}
+
+static void
+free_inetconfent(struct inetconfent *iconf)
+{
+ if (iconf == NULL)
+ return;
+
+ free(iconf->service);
+ free(iconf->endpoint);
+ free(iconf->protocol);
+ free(iconf->wait_status);
+ free(iconf->username);
+ free(iconf->server_program);
+ free(iconf->server_args);
+ free(iconf->rpc_prog);
+ free(iconf->groupname);
+ free(iconf->exec);
+ free(iconf->arg0);
+
+ free(iconf);
+}
+
+static struct inetconfent *
+line_to_inetconfent(char *line)
+{
+ char *cp;
+ struct inetconfent *iconf;
+
+ iconf = safe_malloc(sizeof (struct inetconfent));
+ (void) memset(iconf, 0, sizeof (struct inetconfent));
+
+ if ((cp = strtok(line, " \t\n")) == NULL)
+ goto fail;
+ iconf->service = safe_strdup(cp);
+
+ if ((cp = strtok(NULL, " \t\n")) == NULL)
+ goto fail;
+ iconf->endpoint = safe_strdup(cp);
+
+ if ((cp = strtok(NULL, " \t\n")) == NULL)
+ goto fail;
+ iconf->protocol = safe_strdup(cp);
+
+ if ((cp = strtok(NULL, " \t\n")) == NULL)
+ goto fail;
+ iconf->wait_status = safe_strdup(cp);
+
+ if ((cp = strtok(NULL, " \t\n")) == NULL)
+ goto fail;
+ iconf->username = safe_strdup(cp);
+
+ if ((cp = strtok(NULL, " \t\n")) == NULL)
+ goto fail;
+ iconf->server_program = safe_strdup(cp);
+
+ /* last field is optional */
+ if ((cp = strtok(NULL, "\n")) != NULL)
+ iconf->server_args = safe_strdup(cp);
+
+ /* Combine args and server name to construct exec and args fields */
+ if (iconf->server_args == NULL) {
+ iconf->exec = safe_strdup(iconf->server_program);
+ } else {
+ int len;
+ char *args, *endp;
+
+ len = strlen(iconf->server_program) +
+ strlen(iconf->server_args) + 1;
+ iconf->exec = safe_malloc(len);
+ (void) strlcpy(iconf->exec, iconf->server_program, len);
+
+ args = safe_strdup(iconf->server_args);
+ if ((cp = strtok(args, " \t")) != NULL) {
+ if ((endp = strrchr(iconf->exec, '/')) == NULL)
+ endp = iconf->exec;
+ else
+ endp++;
+ /* only set arg0 property value if needed */
+ if (strcmp(endp, cp) != 0)
+ iconf->arg0 = safe_strdup(cp);
+ while ((cp = strtok(NULL, " \t")) != NULL) {
+ (void) strlcat(iconf->exec, " ", len);
+ (void) strlcat(iconf->exec, cp, len);
+ }
+ }
+ free(args);
+ }
+
+ return (iconf);
+fail:
+ free_inetconfent(iconf);
+ return (NULL);
+}
+
+static void
+skipline(FILE *fp)
+{
+ int c;
+
+ /* skip remainder of a line */
+ while (((c = getc(fp)) != EOF) && (c != '\n'))
+ ;
+}
+
+static struct inetconfent *
+fgetinetconfent(struct fileinfo *finfo, boolean_t validate)
+{
+ char *cp;
+ struct inetconfent *iconf;
+ char line[MAX_SRC_LINELEN];
+
+ while (fgets(line, sizeof (line), finfo->fp) != NULL) {
+ finfo->lineno++;
+
+ /* skip empty or commented out lines */
+ if (*line == '\n')
+ continue;
+ if (*line == '#') {
+ if (line[strlen(line) - 1] != '\n')
+ skipline(finfo->fp);
+ continue;
+ }
+ /* check for lines which are too long */
+ if (line[strlen(line) - 1] != '\n') {
+ (void) fprintf(stderr,
+ gettext("%s: Error %s line %d too long, skipped\n"),
+ progname, finfo->filename, finfo->lineno);
+ skipline(finfo->fp);
+ finfo->failcnt++;
+ continue;
+ }
+ /* remove in line comments and newline character */
+ if ((cp = strchr(line, '#')) == NULL)
+ cp = strchr(line, '\n');
+ if (cp)
+ *cp = '\0';
+
+ if ((iconf = line_to_inetconfent(line)) == NULL) {
+ (void) fprintf(stderr, gettext(
+ "%s: Error %s line %d too few fields, skipped\n"),
+ progname, finfo->filename, finfo->lineno);
+ finfo->failcnt++;
+ continue;
+ }
+
+ if (!validate || valid_inetconfent(iconf, finfo))
+ return (iconf);
+
+ finfo->failcnt++;
+ free_inetconfent(iconf);
+ }
+ return (NULL);
+}
+
+static char *
+boolstr(boolean_t val)
+{
+ if (val)
+ return ("true");
+ return ("false");
+}
+
+static int
+print_manifest(FILE *f, char *filename, struct inetconfent *iconf)
+{
+ if (fprintf(f, xml_header) < 0)
+ goto print_err;
+
+ if (fprintf(f, xml_comment,
+ iconf->isrpc ? iconf->rpc_prog : iconf->service) < 0)
+ goto print_err;
+
+ if (fprintf(f, xml_service_bundle, iconf->service) < 0)
+ goto print_err;
+ if (fprintf(f, xml_service_name, servicename(iconf)) < 0)
+ goto print_err;
+ if (fprintf(f, xml_instance) < 0)
+ goto print_err;
+ if (fprintf(f, xml_restarter, INETD_INSTANCE_FMRI) < 0)
+ goto print_err;
+
+ if (fprintf(f, xml_exec_method_start, START_METHOD_NAME, PR_EXEC_NAME,
+ iconf->exec, PR_USER_NAME, iconf->username, iconf->groupname) < 0)
+ goto print_err;
+ if (iconf->arg0 != NULL) {
+ if (fprintf(f, xml_arg0, PR_ARG0_NAME, iconf->arg0) < 0)
+ goto print_err;
+ }
+ if (fprintf(f, xml_exec_method_end) < 0)
+ goto print_err;
+
+ if (fprintf(f, xml_exec_method_disable, DISABLE_METHOD_NAME,
+ PR_EXEC_NAME) < 0)
+ goto print_err;
+ if (fprintf(f, xml_exec_method_end) < 0)
+ goto print_err;
+
+ if (iconf->wait) {
+ if (fprintf(f, xml_exec_method_offline, OFFLINE_METHOD_NAME,
+ PR_EXEC_NAME) < 0)
+ goto print_err;
+ if (fprintf(f, xml_exec_method_end) < 0)
+ goto print_err;
+ }
+
+ if (fprintf(f, xml_inetconv_group_start, PG_NAME_INETCONV,
+ PR_AUTO_CONVERTED_NAME, boolstr(B_TRUE),
+ PR_VERSION_NAME, INETCONV_VERSION,
+ PR_SOURCE_LINE_NAME, iconf->service,
+ iconf->endpoint, iconf->protocol, iconf->wait_status,
+ iconf->username, iconf->server_program,
+ iconf->server_args == NULL ? "" : " ",
+ iconf->server_args == NULL ? "" : iconf->server_args) < 0)
+ goto print_err;
+ if (fprintf(f, xml_property_group_end) < 0)
+ goto print_err;
+
+ if (fprintf(f, xml_property_group_start, PG_NAME_SERVICE_CONFIG,
+ PR_SVC_NAME_NAME, iconf->isrpc ? iconf->rpc_prog : iconf->service,
+ PR_SOCK_TYPE_NAME, iconf->endpoint,
+ PR_PROTO_NAME, iconf->isrpc ? iconf->protocol + 4 :
+ iconf->protocol,
+ PR_ISWAIT_NAME, boolstr(iconf->wait),
+ PR_ISRPC_NAME, boolstr(iconf->isrpc)) < 0)
+ goto print_err;
+ if (iconf->isrpc) {
+ if (fprintf(f, xml_property_group_rpc,
+ PR_RPC_LW_VER_NAME, iconf->rpc_low_version,
+ PR_RPC_HI_VER_NAME, iconf->rpc_high_version) < 0)
+ goto print_err;
+ }
+ if (fprintf(f, xml_property_group_end) < 0)
+ goto print_err;
+
+ if (fprintf(f, xml_stability) < 0)
+ goto print_err;
+ if (fprintf(f, xml_template,
+ iconf->isrpc ? iconf->rpc_prog : iconf->service) < 0)
+ goto print_err;
+ if (fprintf(f, xml_footer) < 0)
+ goto print_err;
+
+ (void) printf("%s -> %s\n", iconf->service, filename);
+ return (0);
+
+print_err:
+ (void) fprintf(stderr, gettext("%s: Error writing manifest %s: %s\n"),
+ progname, filename, strerror(errno));
+ return (-1);
+}
+
+static struct fileinfo *
+open_srcfile(char *filename)
+{
+ struct fileinfo *finfo = NULL;
+ FILE *fp;
+
+ if (filename != NULL) {
+ if ((fp = fopen(filename, "r")) == NULL) {
+ (void) fprintf(stderr,
+ gettext("%s: Error opening %s: %s\n"),
+ progname, filename, strerror(errno));
+ }
+ } else {
+ /*
+ * If no source file specified, do the same as inetd and first
+ * try /etc/inet/inetd.conf, followed by /etc/inetd.conf.
+ */
+ filename = MAIN_CONFIG;
+ if ((fp = fopen(filename, "r")) == NULL) {
+ (void) fprintf(stderr,
+ gettext("%s: Error opening %s: %s\n"),
+ progname, filename, strerror(errno));
+ filename = ALT_CONFIG;
+ if ((fp = fopen(filename, "r")) == NULL) {
+ (void) fprintf(stderr, gettext(
+ "%s: Error opening %s: %s\n"), progname,
+ filename, strerror(errno));
+ }
+ }
+ }
+ if (fp != NULL) {
+ finfo = safe_malloc(sizeof (struct fileinfo));
+ finfo->fp = fp;
+ finfo->filename = filename;
+ finfo->lineno = 0;
+ finfo->failcnt = 0;
+ (void) fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
+ }
+ return (finfo);
+}
+
+/*
+ * Opens manifest output file. Returns 0 on success, -1 if the file
+ * exists, -2 on other errors.
+ */
+static int
+open_dstfile(
+ char *destdir,
+ boolean_t overwrite,
+ struct inetconfent *iconf,
+ struct fileinfo **finfo)
+{
+ int fd;
+ size_t len;
+ char *dstfile, *cp, *proto;
+ FILE *fp;
+
+ /* if no destdir specified, use appropriate default */
+ if (destdir == NULL) {
+ if (iconf->isrpc)
+ destdir = MANIFEST_RPC_DIR;
+ else
+ destdir = MANIFEST_DIR;
+ }
+
+ len = strlen(destdir) + strlen(iconf->service) +
+ strlen(iconf->protocol) + sizeof ("/-visible.xml");
+ dstfile = safe_malloc(len);
+
+ (void) strlcpy(dstfile, destdir, len);
+ if (dstfile[strlen(dstfile) - 1] != '/')
+ (void) strlcat(dstfile, "/", len);
+ cp = dstfile + strlen(dstfile);
+
+ (void) strlcat(dstfile, iconf->service, len);
+ (void) strlcat(dstfile, "-", len);
+
+ proto = iconf->protocol;
+ if (iconf->isrpc && (strcmp(iconf->protocol, "rpc/*") == 0))
+ proto = "rpc/visible";
+
+ (void) strlcat(dstfile, proto, len);
+ (void) strlcat(dstfile, ".xml", len);
+
+ /* convert any '/' chars in service or protocol to '_' chars */
+ while ((cp = strchr(cp, '/')) != NULL)
+ *cp = '_';
+
+ fd = open(dstfile, O_WRONLY|O_CREAT|(overwrite ? O_TRUNC : O_EXCL),
+ 0644);
+ if (fd == -1) {
+ if (!overwrite && (errno == EEXIST)) {
+ (void) fprintf(stderr,
+ gettext("%s: Notice: Service manifest for "
+ "%s already generated as %s, skipped\n"),
+ progname, iconf->service, dstfile);
+ free(dstfile);
+ return (-1);
+ } else {
+ (void) fprintf(stderr,
+ gettext("%s: Error opening %s: %s\n"),
+ progname, dstfile, strerror(errno));
+ free(dstfile);
+ return (-2);
+ }
+ }
+ /* Clear errno to catch the "no stdio streams" case */
+ errno = 0;
+ if ((fp = fdopen(fd, "w")) == NULL) {
+ char *s = strerror(errno);
+ if (errno == 0)
+ s = gettext("No stdio streams available");
+ (void) fprintf(stderr, gettext("%s: Error fdopen failed: %s\n"),
+ progname, s);
+ (void) close(fd);
+ free(dstfile);
+ return (-2);
+ }
+ *finfo = safe_malloc(sizeof (struct fileinfo));
+ (*finfo)->fp = fp;
+ (*finfo)->filename = dstfile;
+ (*finfo)->lineno = 0;
+ (*finfo)->failcnt = 0;
+ return (0);
+}
+
+static int
+import_manifest(char *filename)
+{
+ int status;
+ pid_t pid, wpid;
+ char *cp;
+
+ if ((cp = strrchr(filename, '/')) == NULL)
+ cp = filename;
+ else
+ cp++;
+ (void) printf(gettext("Importing %s ..."), cp);
+
+ if ((pid = fork()) == -1) {
+ (void) fprintf(stderr,
+ gettext("\n%s: fork failed, %s not imported: %s\n"),
+ progname, filename, strerror(errno));
+ exit(EXIT_ERROR_SYS);
+ }
+ if (pid == 0) {
+ /* child */
+ (void) fclose(stdin);
+ (void) setenv("SVCCFG_CHECKHASH", "1", 1);
+ (void) execl(SVCCFG_PATH, "svccfg", "import", filename, NULL);
+ (void) fprintf(stderr, gettext("\n%s: exec of %s failed: %s"),
+ progname, SVCCFG_PATH, strerror(errno));
+ _exit(EXIT_ERROR_SYS);
+ }
+ /* parent */
+ if ((wpid = waitpid(pid, &status, 0)) != pid) {
+ (void) fprintf(stderr, gettext(
+ "\n%s: unexpected wait (%d) from import of %s: %s\n"),
+ progname, wpid, filename, strerror(errno));
+ return (-1);
+ }
+ if (WIFEXITED(status) && (WEXITSTATUS(status) != 0)) {
+ (void) fprintf(stderr,
+ gettext("\n%s: import failure (%d) for %s\n"),
+ progname, WEXITSTATUS(status), filename);
+ return (-1);
+ }
+ (void) printf(gettext("Done\n"));
+ return (0);
+}
+
+static int
+inetd_config_path(char **path)
+{
+ int fd;
+ char *arg1, *configfile, *configstr;
+ scf_simple_prop_t *sp;
+ char cpath[PATH_MAX];
+
+ if ((sp = scf_simple_prop_get(NULL, INETD_INSTANCE_FMRI, "start",
+ SCF_PROPERTY_EXEC)) == NULL)
+ return (-1);
+ if ((configstr = scf_simple_prop_next_astring(sp)) == NULL) {
+ scf_simple_prop_free(sp);
+ return (-1);
+ }
+ configstr = safe_strdup(configstr);
+ scf_simple_prop_free(sp);
+
+ /*
+ * Look for the optional configuration file, the syntax is:
+ * /usr/lib/inet/inetd [config-file] start|stop|refresh|disable|%m
+ */
+ if (strtok(configstr, " \t") == NULL) {
+ free(configstr);
+ return (-1);
+ }
+ if ((arg1 = strtok(NULL, " \t")) == NULL) {
+ free(configstr);
+ return (-1);
+ }
+ if (strtok(NULL, " \t") == NULL) {
+ /*
+ * No configuration file specified, do the same as inetd and
+ * first try /etc/inet/inetd.conf, followed by /etc/inetd.conf.
+ */
+ configfile = MAIN_CONFIG;
+ if ((fd = open(configfile, O_RDONLY)) >= 0)
+ (void) close(fd);
+ else
+ configfile = ALT_CONFIG;
+
+ } else {
+ /* make sure there are no more arguments */
+ if (strtok(NULL, " \t") != NULL) {
+ free(configstr);
+ return (-1);
+ }
+ configfile = arg1;
+ }
+
+ /* configuration file must be an absolute pathname */
+ if (*configfile != '/') {
+ free(configstr);
+ return (-1);
+ }
+
+ if (realpath(configfile, cpath) == NULL)
+ (void) strlcpy(cpath, configfile, sizeof (cpath));
+
+ free(configstr);
+ *path = safe_strdup(cpath);
+ return (0);
+}
+
+static int
+update_hash(char *srcfile)
+{
+ scf_error_t rval;
+ char *inetd_cpath, *hashstr;
+ char cpath[PATH_MAX];
+
+ /* determine the config file inetd is using */
+ if (inetd_config_path(&inetd_cpath) == -1) {
+ (void) fprintf(stderr,
+ gettext("%s: Error reading from repository\n"), progname);
+ return (-1);
+ }
+
+ /* resolve inetconv input filename */
+ if (realpath(srcfile, cpath) == NULL)
+ (void) strlcpy(cpath, srcfile, sizeof (cpath));
+
+ /* if inetconv and inetd are using the same config file, update hash */
+ if (strcmp(cpath, inetd_cpath) != 0) {
+ free(inetd_cpath);
+ return (0);
+ }
+ free(inetd_cpath);
+
+ /* generic error message as use of hash is not exposed to the user */
+ if (calculate_hash(cpath, &hashstr) != 0) {
+ (void) fprintf(stderr,
+ gettext("%s: Error unable to update repository\n"),
+ progname);
+ return (-1);
+ }
+ /* generic error message as use of hash is not exposed to the user */
+ if ((rval = store_inetd_hash(hashstr)) != SCF_ERROR_NONE) {
+ (void) fprintf(stderr,
+ gettext("%s: Error updating repository: %s\n"),
+ progname, scf_strerror(rval));
+ free(hashstr);
+ return (-1);
+ }
+ free(hashstr);
+ return (0);
+}
+
+static void
+property_error(const char *fmri, const char *prop)
+{
+ (void) fprintf(stderr,
+ gettext("Error: Instance %1$s is missing property '%2$s'.\n"),
+ fmri, prop);
+}
+
+/*
+ * modify_sprop takes a handle, an instance, a property group, a property,
+ * and an astring value, and modifies the instance (or service's) specified
+ * property in the repository to the submitted value.
+ *
+ * returns -1 on error, 1 on successful transaction completion.
+ */
+
+static int
+modify_sprop(scf_handle_t *h, const scf_instance_t *inst,
+ const char *pg, const char *prop, const char *value)
+{
+ scf_transaction_t *tx = NULL;
+ scf_transaction_entry_t *ent = NULL;
+ scf_propertygroup_t *gpg = NULL;
+ scf_property_t *eprop = NULL;
+ scf_value_t *v = NULL;
+ scf_service_t *svc = NULL;
+ int ret = 0, create = 0;
+
+ if ((gpg = scf_pg_create(h)) == NULL)
+ return (-1);
+
+ /* Get the property group */
+ if (scf_instance_get_pg(inst, pg, gpg) == -1) {
+ /* Not a property of the instance, try the service instead */
+ if ((svc = scf_service_create(h)) == NULL) {
+ ret = -1;
+ goto out;
+ }
+ if ((scf_instance_get_parent(inst, svc) == -1) ||
+ (scf_service_get_pg(svc, pg, gpg) == -1)) {
+ ret = -1;
+ goto out;
+ }
+ }
+
+ if ((eprop = scf_property_create(h)) == NULL) {
+ ret = -1;
+ goto out;
+ }
+
+ if (scf_pg_get_property(gpg, prop, eprop) == -1) {
+ if (scf_error() != SCF_ERROR_NOT_FOUND) {
+ ret = -1;
+ goto out;
+ }
+
+ create = 1;
+ }
+
+ if ((tx = scf_transaction_create(h)) == NULL ||
+ (ent = scf_entry_create(h)) == NULL) {
+ ret = -1;
+ goto out;
+ }
+
+ do {
+ if (scf_transaction_start(tx, gpg) == -1) {
+ ret = -1;
+ goto out;
+ }
+
+ /* Modify the property */
+ if (create)
+ ret = scf_transaction_property_new(tx, ent, prop,
+ SCF_TYPE_ASTRING);
+ else
+ ret = scf_transaction_property_change_type(tx, ent,
+ prop, SCF_TYPE_ASTRING);
+
+ if (ret == -1)
+ goto out;
+
+ if ((v = scf_value_create(h)) == NULL) {
+ ret = -1;
+ goto out;
+ }
+
+ if (scf_value_set_astring(v, value) == -1) {
+ ret = -1;
+ goto out;
+ }
+
+ if (scf_entry_add_value(ent, v) == -1) {
+ ret = -1;
+ goto out;
+ }
+
+ ret = scf_transaction_commit(tx);
+
+ if (ret == 0) {
+ /* Property group was stale, retry */
+ if (scf_pg_update(gpg) == -1) {
+ ret = -1;
+ goto out;
+ }
+ scf_transaction_reset(tx);
+ }
+
+ } while (ret == 0);
+out:
+ scf_value_destroy(v);
+ scf_entry_destroy(ent);
+ scf_transaction_destroy(tx);
+ scf_property_destroy(eprop);
+ scf_service_destroy(svc);
+ scf_pg_destroy(gpg);
+
+ return (ret);
+}
+
+/*
+ * list_callback is the callback function to be handed to simple_walk_instances
+ * in main. It is called once on every instance on a machine. If that
+ * instance is controlled by inetd, we test whether it's the same
+ * service that we're looking at from the inetd.conf file, and enable it if
+ * they are the same.
+ */
+
+/*ARGSUSED*/
+static int
+list_callback(scf_handle_t *h, scf_instance_t *inst, void *buf)
+{
+ ssize_t max_name_length;
+ char *svc_name;
+ scf_simple_prop_t *prop = NULL;
+ scf_simple_prop_t *sockprop = NULL;
+ scf_simple_prop_t *rpcprop = NULL;
+ scf_simple_prop_t *progprop = NULL;
+ const char *name, *endpoint, *restart_str, *prog;
+ struct inetconfent *iconf = (struct inetconfent *)buf;
+ uint8_t *isrpc;
+
+ max_name_length = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH);
+ if ((svc_name = malloc(max_name_length + 1)) == NULL) {
+ (void) fprintf(stderr, gettext("Error: Out of memory.\n"));
+ return (SCF_FAILED);
+ }
+
+ /*
+ * Get the FMRI of the instance, and check if its delegated restarter
+ * is inetd. A missing or empty restarter property implies that
+ * svc.startd is the restarter.
+ */
+
+ if (scf_instance_to_fmri(inst, svc_name, max_name_length) < 0) {
+ (void) fprintf(stderr,
+ gettext("Error: Unable to obtain FMRI for service %1$s."),
+ svc_name);
+ free(svc_name);
+ return (SCF_FAILED);
+ }
+
+ if ((prop = scf_simple_prop_get(h, svc_name, SCF_PG_GENERAL,
+ SCF_PROPERTY_RESTARTER)) == NULL)
+ goto out;
+
+ if ((restart_str = scf_simple_prop_next_ustring(prop)) == NULL)
+ goto out;
+
+ if (strcmp(restart_str, INETD_INSTANCE_FMRI) != 0)
+ goto out;
+
+ /* Free restarter prop so it can be reused below */
+ scf_simple_prop_free(prop);
+
+ /*
+ * We know that this instance is managed by inetd.
+ * Now get the properties needed to decide if it matches this
+ * line in the old config file.
+ */
+
+ if (((prop = scf_simple_prop_get(h, svc_name, PG_NAME_SERVICE_CONFIG,
+ PR_SVC_NAME_NAME)) == NULL) ||
+ ((name = scf_simple_prop_next_astring(prop)) == NULL)) {
+ property_error(svc_name, PR_SVC_NAME_NAME);
+ goto out;
+ }
+
+ if (((sockprop = scf_simple_prop_get(h, svc_name,
+ PG_NAME_SERVICE_CONFIG, PR_SOCK_TYPE_NAME)) == NULL) ||
+ ((endpoint = scf_simple_prop_next_astring(sockprop)) == NULL)) {
+ property_error(svc_name, PR_SOCK_TYPE_NAME);
+ goto out;
+ }
+
+ if (((rpcprop = scf_simple_prop_get(h, svc_name,
+ PG_NAME_SERVICE_CONFIG, PR_ISRPC_NAME)) == NULL) ||
+ ((isrpc = scf_simple_prop_next_boolean(rpcprop)) == NULL)) {
+ property_error(svc_name, PR_ISRPC_NAME);
+ goto out;
+ }
+
+ if (((progprop = scf_simple_prop_get(h, svc_name, START_METHOD_NAME,
+ PR_EXEC_NAME)) == NULL) ||
+ ((prog = scf_simple_prop_next_astring(progprop)) == NULL)) {
+ property_error(svc_name, PR_EXEC_NAME);
+ }
+
+
+ /* If it's RPC, we truncate off the version portion for comparison */
+ if (*isrpc) {
+ char *cp;
+
+ cp = strchr(iconf->service, '/');
+ if (cp != NULL)
+ *cp = '\0';
+ }
+
+ /*
+ * If name of this service and endpoint are equal to values from
+ * iconf fields, and they're either both RPC or both non-RPC,
+ * then we have a match; update the exec and arg0 properties if
+ * necessary, then enable it.
+ * We don't return an error if either operation fails so that we
+ * continue to try all the other services.
+ */
+ if (strcmp(name, iconf->service) == 0 &&
+ strcmp(endpoint, iconf->endpoint) == 0 &&
+ *isrpc == (strncmp(iconf->protocol, "rpc/", 4) == 0)) {
+ /* Can't update exec on internal services */
+ if ((strcmp(iconf->server_program, "internal") != 0) &&
+ (strcmp(iconf->exec, prog) != 0)) {
+ /* User had edited the command */
+ if (!import) {
+ /* Dry run only */
+ (void) printf(
+ gettext("Would update %s to %s %s"),
+ svc_name, PR_EXEC_NAME, iconf->exec);
+ if (iconf->arg0 != NULL) {
+ (void) printf(
+ gettext(" with %s of %s\n"),
+ PR_ARG0_NAME, iconf->arg0);
+ } else {
+ (void) printf("\n");
+ }
+ } else {
+ /* Update instance's exec property */
+ if (modify_sprop(h, inst, START_METHOD_NAME,
+ PR_EXEC_NAME, iconf->exec) != 1)
+ (void) fprintf(stderr,
+ gettext("Error: Unable to update "
+ "%s property of %s, %s\n"),
+ PR_EXEC_NAME, svc_name,
+ scf_strerror(scf_error()));
+ else
+ (void) printf("%s will %s %s\n",
+ svc_name, PR_EXEC_NAME,
+ iconf->exec);
+
+ /* Update arg0 prop, if needed */
+ if (iconf->arg0 != NULL) {
+ if (modify_sprop(h, inst,
+ START_METHOD_NAME, PR_ARG0_NAME,
+ iconf->arg0) != 1) {
+ (void) fprintf(stderr,
+ gettext("Error: Unable to "
+ "update %s property of "
+ "%s, %s\n"), PR_ARG0_NAME,
+ svc_name,
+ scf_strerror(scf_error()));
+ } else {
+ (void) printf("%s will have an "
+ "%s of %s\n", svc_name,
+ PR_ARG0_NAME, iconf->arg0);
+ }
+ }
+ }
+ }
+
+ if (!import) {
+ /* Dry-run only */
+ (void) printf("Would enable %s\n", svc_name);
+ } else {
+ if (smf_enable_instance(svc_name, 0) != 0)
+ (void) fprintf(stderr,
+ gettext("Error: Failed to enable %s\n"),
+ svc_name);
+ else
+ (void) printf("%s enabled\n", svc_name);
+ }
+ }
+
+out:
+ free(svc_name);
+ scf_simple_prop_free(prop);
+ scf_simple_prop_free(sockprop);
+ scf_simple_prop_free(rpcprop);
+ scf_simple_prop_free(progprop);
+ return (SCF_SUCCESS);
+}
+
+static void
+usage(void)
+{
+ (void) fprintf(stderr, gettext(
+ "Usage: %s [-fn] [-i srcfile] [-o destdir]\n"
+ " %1$s -e [-n] [-i srcfile]\n"
+ "-? Display this usage message\n"
+ "-e Enable smf services which are enabled in the input\n"
+ " file\n"
+ "-f Force overwrite of existing manifests\n"
+ "-n Do not import converted manifests,\n"
+ " or only display services which would be enabled\n"
+ "-i srcfile Alternate input file\n"
+ "-o destdir Alternate output directory for manifests\n"),
+ progname);
+ exit(EXIT_USAGE);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int c, rval, convert_err, import_err = 0, enable_err = 0;
+ boolean_t overwrite = B_FALSE;
+ boolean_t enable = B_FALSE;
+ char *srcfile = NULL;
+ char *destdir = NULL;
+ struct fileinfo *srcfinfo, *dstfinfo;
+ struct inetconfent *iconf;
+
+ setbuf(stdout, NULL);
+ (void) setlocale(LC_ALL, "");
+ (void) textdomain(TEXT_DOMAIN);
+
+ if ((progname = strrchr(argv[0], '/')) == NULL)
+ progname = argv[0];
+ else
+ progname++;
+
+ while ((c = getopt(argc, argv, "?efni:o:")) != -1) {
+ switch (c) {
+ case 'e':
+ /* enable services based on existing file config */
+ enable = B_TRUE;
+ break;
+
+ case 'f':
+ /* overwrite existing manifests */
+ overwrite = B_TRUE;
+ break;
+ case 'n':
+ /* don't import manifests, or dry-run enable */
+ import = B_FALSE;
+ break;
+ case 'i':
+ /* alternate input file */
+ if (srcfile != NULL) {
+ (void) fprintf(stderr,
+ gettext("%s: Error only one -%c allowed\n"),
+ progname, optopt);
+ usage();
+ }
+ srcfile = optarg;
+ break;
+ case 'o':
+ /* alternate output directory */
+ if (destdir != NULL) {
+ (void) fprintf(stderr,
+ gettext("%s: Error only one -%c allowed\n"),
+ progname, optopt);
+ usage();
+ }
+ destdir = optarg;
+ break;
+ case '?': /*FALLTHROUGH*/
+ default:
+ usage();
+ break;
+ }
+ }
+
+ /*
+ * Display usage if extraneous args supplied or enable specified in
+ * combination with overwrite or destdir
+ */
+ if ((optind != argc) || (enable && (overwrite || destdir != NULL)))
+ usage();
+
+ if ((srcfinfo = open_srcfile(srcfile)) == NULL)
+ return (EXIT_ERROR_CONV);
+
+ while ((iconf = fgetinetconfent(srcfinfo, !enable)) != NULL) {
+ /*
+ * If we're enabling, then just walk all the services for each
+ * line and enable those which match.
+ */
+ if (enable) {
+ rval = scf_simple_walk_instances(SCF_STATE_ALL, iconf,
+ list_callback);
+ free_inetconfent(iconf);
+ if (rval == SCF_FAILED) {
+ /* Only print msg if framework error */
+ if (scf_error() != SCF_ERROR_CALLBACK_FAILED)
+ (void) fprintf(stderr, gettext(
+ "Error walking instances: %s.\n"),
+ scf_strerror(scf_error()));
+ enable_err++;
+ break;
+ }
+ continue;
+ }
+
+ /* Remainder of loop used for conversion & import */
+ if ((rval = open_dstfile(destdir, overwrite, iconf, &dstfinfo))
+ < 0) {
+ /*
+ * Only increment error counter if the failure was
+ * other than the file already existing.
+ */
+ if (rval == -2)
+ srcfinfo->failcnt++;
+ free_inetconfent(iconf);
+ continue;
+ }
+ rval = print_manifest(dstfinfo->fp, dstfinfo->filename, iconf);
+ (void) fclose(dstfinfo->fp);
+ if (rval == 0) {
+ if (import &&
+ (import_manifest(dstfinfo->filename) != 0))
+ import_err++;
+ } else {
+ (void) unlink(dstfinfo->filename);
+ srcfinfo->failcnt++;
+ }
+ free(dstfinfo->filename);
+ free(dstfinfo);
+ free_inetconfent(iconf);
+ }
+ (void) fclose(srcfinfo->fp);
+ convert_err = srcfinfo->failcnt;
+
+ /* Update hash only if not in enable mode, and only if importing */
+ if (!enable && import && (update_hash(srcfinfo->filename) != 0))
+ import_err++;
+
+ free(srcfinfo);
+
+ if (enable_err != 0)
+ return (EXIT_ERROR_ENBL);
+ if (import_err != 0)
+ return (EXIT_ERROR_IMP);
+ if (convert_err != 0)
+ return (EXIT_ERROR_CONV);
+ return (EXIT_SUCCESS);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/ipaddrsel.c b/usr/src/cmd/cmd-inet/usr.sbin/ipaddrsel.c
new file mode 100644
index 0000000000..9c8468c375
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/ipaddrsel.c
@@ -0,0 +1,510 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <libintl.h>
+#include <locale.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <stropts.h>
+#include <sys/conf.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <inet/ip.h>
+#include <inet/ip6_asp.h>
+
+/*
+ * The size of the table we initially use to retrieve the kernel's policy
+ * table. If this value is too small, we use the value returned from the
+ * SIOCGIP6ADDRPOLICY ioctl.
+ */
+#define KERN_POLICY_SIZE 32
+#define IPV6DAS_MAXLINELEN 1024
+#define IPV6DAS_MAXENTRIES 512
+
+typedef enum {
+ IPV6DAS_PRINTPOLICY,
+ IPV6DAS_SETPOLICY,
+ IPV6DAS_SETDEFAULT
+} ipv6das_cmd_t;
+
+static char *myname; /* Copied from argv[0] */
+
+static int parseconf(const char *, ip6_asp_t **);
+static int setpolicy(int, ip6_asp_t *, int);
+static int printpolicy(int);
+static int ip_mask_to_plen_v6(const in6_addr_t *);
+static in6_addr_t *ip_plen_to_mask_v6(int, in6_addr_t *);
+static int strioctl(int, int, void *, int);
+static void usage(void);
+
+int
+main(int argc, char **argv)
+{
+ int opt, status, sock, count;
+ char *conf_filename;
+ ipv6das_cmd_t ipv6das_cmd = IPV6DAS_PRINTPOLICY;
+ ip6_asp_t *policy_table;
+
+ myname = *argv;
+
+ (void) setlocale(LC_ALL, "");
+
+#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
+#define TEXT_DOMAIN "SYS_TEST"
+#endif
+
+ (void) textdomain(TEXT_DOMAIN);
+
+ while ((opt = getopt(argc, argv, "df:")) != EOF)
+ switch (opt) {
+ case 'd':
+ ipv6das_cmd = IPV6DAS_SETDEFAULT;
+ break;
+ case 'f':
+ conf_filename = optarg;
+ ipv6das_cmd = IPV6DAS_SETPOLICY;
+ break;
+ default:
+ usage();
+ return (EXIT_FAILURE);
+ }
+ if (argc > optind) {
+ /* shouldn't be any extra args */
+ usage();
+ return (EXIT_FAILURE);
+ }
+
+ /* Open a socket that we can use to send ioctls down to IP. */
+ if ((sock = socket(PF_INET6, SOCK_DGRAM, 0)) == -1) {
+ perror("socket");
+ return (EXIT_FAILURE);
+ }
+
+ switch (ipv6das_cmd) {
+ case IPV6DAS_SETPOLICY:
+ if ((count = parseconf(conf_filename, &policy_table)) <= 0)
+ return (EXIT_FAILURE);
+ status = setpolicy(sock, policy_table, count);
+ free(policy_table);
+ break;
+ case IPV6DAS_SETDEFAULT:
+ status = setpolicy(sock, NULL, 0);
+ break;
+ case IPV6DAS_PRINTPOLICY:
+ default:
+ status = printpolicy(sock);
+ break;
+ }
+
+ (void) close(sock);
+ return (status);
+}
+
+/*
+ * parseconf(filename, new_policy)
+ *
+ * Parses the file identified by filename, filling in new_policy
+ * with the address selection policy table specified in filename.
+ * Returns -1 on failure, or the number of table entries found
+ * on success.
+ */
+static int
+parseconf(const char *filename, ip6_asp_t **new_policy)
+{
+ FILE *fp;
+ char line[IPV6DAS_MAXLINELEN];
+ char *cp, *end;
+ char *prefixstr;
+ uint_t lineno = 0, entryindex = 0;
+ int plen, precedence;
+ char *label;
+ size_t labellen;
+ int retval;
+ ip6_asp_t tmp_policy[IPV6DAS_MAXENTRIES];
+ boolean_t have_default = B_FALSE;
+ in6_addr_t prefix, mask;
+ boolean_t comment_found = B_FALSE, end_of_line = B_FALSE;
+
+ if ((fp = fopen(filename, "r")) == NULL) {
+ perror(filename);
+ return (-1);
+ }
+
+ while (fgets(line, sizeof (line), fp) != NULL) {
+ if (entryindex == IPV6DAS_MAXENTRIES) {
+ (void) fprintf(stderr,
+ gettext("%s: too many entries\n"), filename);
+ retval = -1;
+ goto end_parse;
+ }
+
+ lineno++;
+ cp = line;
+
+ /* Skip leading whitespace */
+ while (isspace(*cp))
+ cp++;
+
+ /* Is this a comment or blank line? */
+ if (*cp == '#' || *cp == '\0')
+ continue;
+
+ /*
+ * Anything else must be of the form:
+ * <IPv6-addr>/<plen> <precedence> <label>
+ */
+ prefixstr = cp;
+ if ((cp = strchr(cp, '/')) == NULL) {
+ (void) fprintf(stderr,
+ gettext("%s: invalid prefix on line %d: %s\n"),
+ filename, lineno, prefixstr);
+ continue;
+ }
+ *cp = '\0';
+ if (inet_pton(AF_INET6, prefixstr, &prefix) != 1) {
+ (void) fprintf(stderr,
+ gettext("%s: invalid prefix on line %d: %s\n"),
+ filename, lineno, prefixstr);
+ continue;
+ }
+ cp++;
+
+ errno = 0;
+ plen = strtol(cp, &end, 10);
+ if (cp == end || errno != 0) {
+ (void) fprintf(stderr,
+ gettext("%s: invalid prefix length on line %d\n"),
+ filename, lineno);
+ continue;
+ }
+ if (ip_plen_to_mask_v6(plen, &mask) == NULL) {
+ (void) fprintf(stderr,
+ gettext("%s: invalid prefix length on line %d:"
+ " %d\n"), filename, lineno, plen);
+ continue;
+ }
+ cp = end;
+
+ errno = 0;
+ precedence = strtol(cp, &end, 10);
+ if (cp == end || precedence < 0 || errno != 0) {
+ (void) fprintf(stderr,
+ gettext("%s: invalid precedence on line %d\n"),
+ filename, lineno);
+ continue;
+ }
+ cp = end;
+
+ while (isspace(*cp))
+ cp++;
+ label = cp;
+ /*
+ * NULL terminate the label string. The label string is
+ * composed of non-blank characters, and can optionally be
+ * followed by a comment.
+ */
+ while (*cp != '\0' && !isspace(*cp) && *cp != '#')
+ cp++;
+ if (*cp == '#')
+ comment_found = B_TRUE;
+ else if (*cp == '\0' || *cp == '\n')
+ end_of_line = B_TRUE;
+ *cp = '\0';
+
+ labellen = cp - label;
+ if (labellen == 0) {
+ (void) fprintf(stderr,
+ gettext("%s: missing label on line %d\n"),
+ filename, lineno);
+ continue;
+ }
+ if (labellen >= IP6_ASP_MAXLABELSIZE) {
+ (void) fprintf(stderr,
+ gettext("%s: label too long on line %d, labels "
+ "have a %d character limit.\n"), filename, lineno,
+ IP6_ASP_MAXLABELSIZE - 1);
+ continue;
+ }
+
+ tmp_policy[entryindex].ip6_asp_prefix = prefix;
+ tmp_policy[entryindex].ip6_asp_mask = mask;
+ tmp_policy[entryindex].ip6_asp_precedence = precedence;
+ /*
+ * We're specifically using strncpy() to copy the label
+ * to take advantage of the fact that strncpy will add
+ * NULL characters to the target string up to the given
+ * length, so don't change the call to strncpy() with
+ * out also taking into account this requirement. The
+ * labels are stored in the kernel in that way in order
+ * to make comparisons more efficient: all 16 bytes of
+ * the labels are compared to each other; random bytes
+ * after the NULL terminator would yield incorrect
+ * comparisons.
+ */
+ (void) strncpy(tmp_policy[entryindex].ip6_asp_label, label,
+ IP6_ASP_MAXLABELSIZE);
+
+ /*
+ * Anything else on the line should be a comment; print
+ * a warning if that's not the case.
+ */
+ if (!comment_found && !end_of_line) {
+ cp++;
+ while (*cp != '\0' && isspace(*cp) && *cp != '#')
+ cp++;
+ if (*cp != '\0' && *cp != '#') {
+ (void) fprintf(stderr,
+ gettext("%s: characters following label "
+ "on line %d will be ignored\n"),
+ filename, lineno);
+ }
+ }
+
+ if (IN6_IS_ADDR_UNSPECIFIED(&prefix) && plen == 0)
+ have_default = B_TRUE;
+
+ comment_found = B_FALSE;
+ end_of_line = B_FALSE;
+ entryindex++;
+ }
+
+ if (!have_default) {
+ (void) fprintf(stderr,
+ gettext("%s: config doesn't contain a default entry.\n"),
+ filename);
+ retval = -1;
+ goto end_parse;
+ }
+
+ /* Allocate the caller's array. */
+ if ((*new_policy = malloc(entryindex * sizeof (ip6_asp_t))) == NULL) {
+ perror("malloc");
+ retval = -1;
+ goto end_parse;
+ }
+
+ (void) memcpy(*new_policy, tmp_policy, entryindex * sizeof (ip6_asp_t));
+ retval = entryindex;
+
+end_parse:
+ (void) fclose(fp);
+ return (retval);
+}
+
+/*
+ * setpolicy(sock, new_policy, count)
+ *
+ * Sends an SIOCSIP6ADDRPOLICY ioctl to the kernel to set the address
+ * selection policy table pointed to by new_policy. count should be
+ * the number of entries in the table; sock should be an open INET6
+ * socket. Returns EXIT_FAILURE or EXIT_SUCCESS.
+ */
+static int
+setpolicy(int sock, ip6_asp_t *new_policy, int count)
+{
+ if (strioctl(sock, SIOCSIP6ADDRPOLICY, new_policy,
+ count * sizeof (ip6_asp_t)) < 0) {
+ perror("SIOCSIP6ADDRPOLICY");
+ return (EXIT_FAILURE);
+ }
+ return (EXIT_SUCCESS);
+}
+
+/*
+ * printpolicy(sock)
+ *
+ * Queries the kernel for the current address selection policy using
+ * the open socket sock, and prints the result. Returns EXIT_FAILURE
+ * if the table cannot be obtained, or EXIT_SUCCESS if the table is
+ * obtained and printed successfully.
+ */
+static int
+printpolicy(int sock)
+{
+ ip6_asp_t policy[KERN_POLICY_SIZE];
+ ip6_asp_t *policy_ptr = policy;
+ int count, policy_index;
+ char prefixstr[INET6_ADDRSTRLEN + sizeof ("/128")];
+
+ if ((count = strioctl(sock, SIOCGIP6ADDRPOLICY, policy_ptr,
+ KERN_POLICY_SIZE * sizeof (ip6_asp_t))) < 0) {
+ perror("SIOCGIP6ADDRPOLICY");
+ return (EXIT_FAILURE);
+ }
+ if (count > KERN_POLICY_SIZE) {
+ policy_ptr = malloc(count * sizeof (ip6_asp_t));
+ if (policy_ptr == NULL) {
+ perror("malloc");
+ return (EXIT_FAILURE);
+ }
+ if ((count = strioctl(sock, SIOCGIP6ADDRPOLICY, policy_ptr,
+ count * sizeof (ip6_asp_t))) < 0) {
+ perror("SIOCGIP6ADDRPOLICY");
+ return (EXIT_FAILURE);
+ }
+ }
+
+ if (count == 0) {
+ /*
+ * There should always at least be a default entry in the
+ * policy table, so the minimum acceptable value of
+ * policy_count is 1.
+ */
+ (void) fprintf(stderr, gettext("%s: ERROR: "
+ "IPv6 address selection policy is empty.\n"), myname);
+ return (EXIT_FAILURE);
+ }
+
+ /*
+ * The format printed here must also be parsable by parseconf(),
+ * since we expect users to be able to redirect this output to
+ * a usable configuration file if need be.
+ */
+ (void) printf("# Prefix Precedence Label\n");
+ for (policy_index = 0; policy_index < count; policy_index++) {
+ (void) snprintf(prefixstr, sizeof (prefixstr), "%s/%d",
+ inet_ntop(AF_INET6,
+ &policy_ptr[policy_index].ip6_asp_prefix, prefixstr,
+ sizeof (prefixstr)),
+ ip_mask_to_plen_v6(&policy_ptr[policy_index].ip6_asp_mask));
+ (void) printf("%-25s %10d %s\n", prefixstr,
+ policy_ptr[policy_index].ip6_asp_precedence,
+ policy_ptr[policy_index].ip6_asp_label);
+ }
+
+ if (policy_ptr != policy)
+ free(policy_ptr);
+ return (EXIT_SUCCESS);
+}
+
+/*
+ * ip_mask_to_plen_v6(v6mask)
+ *
+ * This function takes a mask and returns number of bits set in the
+ * mask (the represented prefix length). Assumes a contigious mask.
+ */
+int
+ip_mask_to_plen_v6(const in6_addr_t *v6mask)
+{
+ uint8_t bits;
+ uint32_t mask;
+ int i;
+
+ if (v6mask->_S6_un._S6_u32[3] == 0xffffffff) /* check for all ones */
+ return (IPV6_ABITS);
+
+ /* Find number of words with 32 ones */
+ bits = 0;
+ for (i = 0; i < 4; i++) {
+ if (v6mask->_S6_un._S6_u32[i] == 0xffffffff) {
+ bits += 32;
+ continue;
+ }
+ break;
+ }
+
+ /*
+ * Find number of bits in the last word by searching
+ * for the first one from the right
+ */
+ mask = ntohl(v6mask->_S6_un._S6_u32[i]);
+ if (mask == 0)
+ return (bits);
+
+ return (bits + 32 - (ffs(mask) - 1));
+}
+
+/*
+ * ip_plen_to_mask_v6(plen, bitmask)
+ *
+ * Convert a prefix length to the mask for that prefix.
+ * Returns the argument bitmask.
+ */
+in6_addr_t *
+ip_plen_to_mask_v6(int plen, in6_addr_t *bitmask)
+{
+ uint32_t *ptr;
+
+ if (plen > IPV6_ABITS || plen < 0)
+ return (NULL);
+
+ (void) memset(bitmask, 0, sizeof (in6_addr_t));
+ if (plen == 0)
+ return (bitmask);
+
+ ptr = (uint32_t *)bitmask;
+ while (plen > 32) {
+ *ptr++ = 0xffffffffU;
+ plen -= 32;
+ }
+ *ptr = htonl(0xffffffffU << (32 - plen));
+ return (bitmask);
+}
+
+/*
+ * strioctl(fd, cmd, ptr, ilen)
+ *
+ * Passes an I_STR ioctl to fd. The ioctl type is specified by cmd, and
+ * any date to be sent down is specified by a pointer to the buffer (ptr)
+ * and the buffer size (ilen). Returns the return value from the ioctl()
+ * call.
+ */
+static int
+strioctl(int fd, int cmd, void *ptr, int ilen)
+{
+ struct strioctl str;
+ int retv;
+
+ str.ic_cmd = cmd;
+ str.ic_timout = 0;
+ str.ic_len = ilen;
+ str.ic_dp = ptr;
+
+ while ((retv = ioctl(fd, I_STR, &str)) == -1) {
+ if (errno != EINTR)
+ break;
+ }
+ return (retv);
+}
+
+static void
+usage(void)
+{
+ (void) fprintf(stderr, gettext(
+ "Usage: %s\n"
+ " %s -f <filename>\n"
+ " %s -d\n"), myname, myname, myname);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/ipqosconf/Makefile b/usr/src/cmd/cmd-inet/usr.sbin/ipqosconf/Makefile
new file mode 100644
index 0000000000..d348b7aeaf
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/ipqosconf/Makefile
@@ -0,0 +1,89 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+#ident "%Z%%M% %I% %E% SMI"
+
+#
+# cmd/cmd-inet/usr.sbin/ipqosconf/Makefile
+
+PROG= ipqosconf
+OBJS= ipqosconf.o
+SRCS= $(OBJS:%.o=%.c)
+HDRS= ipqosconf.h
+
+MODS= dlcosmk \
+ flowacct \
+ ipgpc \
+ dscpmk \
+ tokenmt \
+ tswtclmt
+
+TYPES= $(MODS:%=%.types)
+
+ROOTLIBIPQOSCONF= $(ROOTLIB)/ipqosconf
+ROOTLIBIPQOSCONFTYPES = $(TYPES:%=$(ROOTLIBIPQOSCONF)/%)
+
+include ../../../Makefile.cmd
+include ../../Makefile.cmd-inet
+
+$(ROOTUSRSBIN)/$(PROG) := FILEMODE= 0755
+$(ROOTUSRSBIN)/$(PROG) := OWNER= root
+$(ROOTUSRSBIN)/$(PROG) := GROUP= bin
+
+$(ROOTLIBIPQOSCONFTYPES) := FILEMODE= 0444
+$(ROOTLIBIPQOSCONFTYPES) := OWNER= root
+$(ROOTLIBIPQOSCONFTYPES) := GROUP= bin
+
+#pull in prototype for strtok_r
+CPPFLAGS += -D__EXTENSIONS__
+
+LDLIBS += -lsocket -lnsl -lnvpair -lipp
+
+.KEEP_STATE:
+
+.INIT: $(HDRS)
+
+all: $(PROG)
+
+$(PROG): $(OBJS) $(HDRS)
+ $(LINK.c) -o $@ $(OBJS) $(LDLIBS)
+ $(POST_PROCESS)
+
+install: all $(ROOTUSRSBINPROG) $(ROOTLIBIPQOSCONF) .WAIT \
+ $(ROOTLIBIPQOSCONFTYPES)
+
+$(ROOTLIBIPQOSCONF):
+ $(INS.dir)
+
+$(ROOTLIBIPQOSCONF)/%: %
+ $(INS.file)
+
+clean:
+ $(RM) $(OBJS)
+
+lint: lint_SRCS
+
+include ../../../Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/ipqosconf/dlcosmk.types b/usr/src/cmd/cmd-inet/usr.sbin/ipqosconf/dlcosmk.types
new file mode 100644
index 0000000000..d345502d35
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/ipqosconf/dlcosmk.types
@@ -0,0 +1,34 @@
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+fmt_version 1.0
+mod_version 1.0
+
+params next_action action
+params cos mindex bband,dlpri
+map bband uint32 0,0,0,96,128,160,192,224
+map dlpri uint32 0,0,0,3,4,5,6,7
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/ipqosconf/dscpmk.types b/usr/src/cmd/cmd-inet/usr.sbin/ipqosconf/dscpmk.types
new file mode 100644
index 0000000000..426699167c
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/ipqosconf/dscpmk.types
@@ -0,0 +1,33 @@
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+fmt_version 1.0
+mod_version 1.0
+
+params next_action action
+params dscp_map int_array 64,0-63
+params dscp_detailed_stats boolean
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/ipqosconf/flowacct.types b/usr/src/cmd/cmd-inet/usr.sbin/ipqosconf/flowacct.types
new file mode 100644
index 0000000000..5dbedaa558
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/ipqosconf/flowacct.types
@@ -0,0 +1,34 @@
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+fmt_version 1.0
+mod_version 1.0
+
+params next_action action
+params timeout uint32
+params timer uint32
+params max_limit uint32
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/ipqosconf/ipgpc.types b/usr/src/cmd/cmd-inet/usr.sbin/ipqosconf/ipgpc.types
new file mode 100644
index 0000000000..bb15199492
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/ipqosconf/ipgpc.types
@@ -0,0 +1,48 @@
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+fmt_version 1.0
+mod_version 1.0
+
+#PERM_CLASS default
+
+filter name string
+filter if_groupname string
+filter user user
+filter projid int32
+filter if_name ifname
+filter direction enum { LOCAL_IN=0x01, LOCAL_OUT=0x02, FWD_IN=0x04, FWD_OUT=0x08 }
+filter protocol protocol
+filter dsfield uint8
+filter dsfield_mask uint8
+filter saddr address
+filter daddr address
+filter sport port
+filter dport port
+filter priority uint32
+filter precedence uint32
+filter ip_version enum { V4=0x01, V6=0x02 }
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/ipqosconf/ipqosconf.c b/usr/src/cmd/cmd-inet/usr.sbin/ipqosconf/ipqosconf.c
new file mode 100644
index 0000000000..e85bc168a4
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/ipqosconf/ipqosconf.c
@@ -0,0 +1,10041 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/* enable debug output and some debug asserts */
+#undef _IPQOS_CONF_DEBUG
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <libintl.h>
+#include <signal.h>
+#include <strings.h>
+#include <sys/nvpair.h>
+#include <stdio.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <ctype.h>
+#include <sys/socket.h>
+#include <limits.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <libipp.h>
+#include <ipp/ipp_config.h>
+#include <ipp/ipgpc/ipgpc.h>
+#include <ipp/ipp.h>
+#ifdef _IPQOS_CONF_DEBUG
+#include <assert.h>
+#endif
+#include <sys/sockio.h>
+#include <syslog.h>
+#include <stdarg.h>
+#include <libintl.h>
+#include <locale.h>
+#include <pwd.h>
+#include "ipqosconf.h"
+
+#if defined(_IPQOS_CONF_DEBUG)
+
+/* debug level */
+static int ipqosconf_dbg_flgs =
+/*
+ */
+RBK |
+MHME |
+KRET |
+DIFF |
+APPLY |
+L2 |
+L1 |
+L0 |
+0;
+
+
+
+#define IPQOSCDBG0(lvl, x)\
+ if (lvl & ipqosconf_dbg_flgs)\
+ (void) fprintf(stderr, x)
+
+#define IPQOSCDBG1(lvl, x, y)\
+ if (lvl & ipqosconf_dbg_flgs)\
+ (void) fprintf(stderr, x, y)
+
+#define IPQOSCDBG2(lvl, x, y, z)\
+ if (lvl & ipqosconf_dbg_flgs)\
+ (void) fprintf(stderr, x, y, z)
+
+#define IPQOSCDBG3(lvl, x, y, z, a)\
+ if (lvl & ipqosconf_dbg_flgs)\
+ (void) fprintf(stderr, x, y, z, a)
+
+#define IPQOSCDBG4(lvl, x, y, z, a, b)\
+ if (lvl & ipqosconf_dbg_flgs)\
+ (void) fprintf(stderr, x, y, z, a, b)
+
+#define IPQOSCDBG5(lvl, x, y, z, a, b, c)\
+ if (lvl & ipqosconf_dbg_flgs)\
+ (void) fprintf(stderr, x, y, z, a, b, c)
+
+#else /* defined(_IPQOS_CONF_DEBUG) && !defined(lint) */
+
+#define IPQOSCDBG0(lvl, x)
+#define IPQOSCDBG1(lvl, x, y)
+#define IPQOSCDBG2(lvl, x, y, z)
+#define IPQOSCDBG3(lvl, x, y, z, a)
+#define IPQOSCDBG4(lvl, x, y, z, a, b)
+#define IPQOSCDBG5(lvl, x, y, z, a, b, c)
+
+#endif /* defined(_IPQOS_CONF_DEBUG) */
+
+
+
+/* function prototypes */
+
+static int modify_params(char *, nvlist_t **, int, boolean_t);
+static int add_class(char *, char *, int, boolean_t, char *);
+static int modify_class(char *, char *, int, boolean_t, char *,
+ enum ipp_flags);
+static int remove_class(char *, char *, int, enum ipp_flags);
+static int add_filter(char *, ipqos_conf_filter_t *, int);
+static int modify_filter(char *, ipqos_conf_filter_t *, int);
+static int remove_filter(char *, char *, int, int);
+static boolean_t arrays_equal(int *, int *, uint32_t);
+static int diffclass(ipqos_conf_class_t *, ipqos_conf_class_t *);
+static int diffparams(ipqos_conf_params_t *, ipqos_conf_params_t *, char *);
+static int difffilter(ipqos_conf_filter_t *, ipqos_conf_filter_t *, char *);
+static int add_filters(ipqos_conf_filter_t *, char *, int, boolean_t);
+static int add_classes(ipqos_conf_class_t *, char *, int, boolean_t);
+static int modify_items(ipqos_conf_action_t *);
+static int add_items(ipqos_conf_action_t *, boolean_t);
+static int add_item(ipqos_conf_action_t *, boolean_t);
+static int remove_items(ipqos_conf_action_t *, boolean_t);
+static int remove_item(ipqos_conf_action_t *, boolean_t);
+static int undo_modifys(ipqos_conf_action_t *, ipqos_conf_action_t *);
+static int applydiff(ipqos_conf_action_t *, ipqos_conf_action_t *);
+static int rollback(ipqos_conf_action_t *, ipqos_conf_action_t *);
+static int rollback_recover(ipqos_conf_action_t *);
+static ipqos_conf_class_t *classexist(char *, ipqos_conf_class_t *);
+static ipqos_conf_filter_t *filterexist(char *, int, ipqos_conf_filter_t *);
+static ipqos_conf_action_t *actionexist(char *, ipqos_conf_action_t *);
+static int diffnvlists(nvlist_t *, nvlist_t *, char *, int *, place_t);
+static int diffaction(ipqos_conf_action_t *, ipqos_conf_action_t *);
+static int diffconf(ipqos_conf_action_t *, ipqos_conf_action_t *);
+static int readllong(char *, long long *, char **);
+static int readuint8(char *, uint8_t *, char **);
+static int readuint16(char *, uint16_t *, char **);
+static int readint16(char *, int16_t *, char **);
+static int readint32(char *, int *, char **);
+static int readuint32(char *, uint32_t *, char **);
+static int readbool(char *, boolean_t *);
+static void setmask(int, in6_addr_t *, int);
+static int readtoken(FILE *, char **);
+static nvpair_t *find_nvpair(nvlist_t *, char *);
+static char *prepend_module_name(char *, char *);
+static int readnvpair(FILE *, FILE *, nvlist_t **, nvpair_t **,
+ ipqos_nvtype_t *, place_t, char *);
+static int add_aref(ipqos_conf_act_ref_t **, char *, char *);
+static int readparams(FILE *, FILE *, char *, ipqos_conf_params_t *);
+static int readclass(FILE *, char *, ipqos_conf_class_t **, char **, int);
+static int readfilter(FILE *, FILE *, char *, ipqos_conf_filter_t **, char **,
+ int);
+static FILE *validmod(char *, int *);
+static int readaction(FILE *, ipqos_conf_action_t **);
+static int actions_unique(ipqos_conf_action_t *, char **);
+static int validconf(ipqos_conf_action_t *, int);
+static int readconf(FILE *, ipqos_conf_action_t **);
+static int flush(boolean_t *);
+static int atomic_flush(boolean_t);
+static int flushconf();
+static int writeconf(ipqos_conf_action_t *, char *);
+static int commitconf();
+static int applyconf(char *ifile);
+static int block_all_signals();
+static int restore_all_signals();
+static int unlock(int fd);
+static int lock();
+static int viewconf(int);
+static void usage();
+static int valid_name(char *);
+static int in_cycle(ipqos_conf_action_t *);
+static int readtype(FILE *, char *, char *, ipqos_nvtype_t *, str_val_nd_t **,
+ char *, boolean_t, place_t *);
+static int read_int_array_info(char *, str_val_nd_t **, uint32_t *, int *,
+ int *, char *);
+static str_val_nd_t *read_enum_nvs(char *, char *);
+static int add_str_val_entry(str_val_nd_t **, char *, uint32_t);
+static void free_str_val_entrys(str_val_nd_t *);
+static void get_str_val_value_range(str_val_nd_t *, int *, int *);
+static int read_enum_value(FILE *, char *, str_val_nd_t *, uint32_t *);
+static int read_mapped_values(FILE *, nvlist_t **, char *, char *,
+ int);
+static int read_int_array(FILE *, char *, int **, uint32_t, int, int,
+ str_val_nd_t *);
+static int str_val_list_lookup(str_val_nd_t *, char *, uint32_t *);
+static int parse_kparams(char *, ipqos_conf_params_t *, nvlist_t *);
+static int parse_kclass(ipqos_conf_class_t *, nvlist_t *);
+static int parse_kfilter(ipqos_conf_filter_t *, nvlist_t *);
+static int parse_kaction(nvlist_t *, ipqos_actinfo_prm_t *);
+static int readkconf(ipqos_conf_action_t **);
+static void print_int_array(FILE *, int *, uint32_t, int, int, str_val_nd_t *,
+ int);
+static void printrange(FILE *fp, uint32_t, uint32_t);
+static void printenum(FILE *, uint32_t, str_val_nd_t *);
+static void printproto(FILE *, uint8_t);
+static void printport(FILE *, uint16_t);
+static int printnvlist(FILE *, char *, nvlist_t *, int, ipqos_conf_filter_t *,
+ int, place_t);
+static int virtual_action(char *);
+static void free_arefs(ipqos_conf_act_ref_t *);
+static void print_action_nm(FILE *, char *);
+static int add_orig_ipqosconf(nvlist_t *);
+static char *get_originator_nm(uint32_t);
+static void mark_classes_filters_new(ipqos_conf_action_t *);
+static void mark_classes_filters_del(ipqos_conf_action_t *);
+static void mark_config_new(ipqos_conf_action_t *);
+static int printifname(FILE *, int);
+static int readifindex(char *, int *);
+static void cleanup_string_table(char **, int);
+static int domultihome(ipqos_conf_filter_t *, ipqos_conf_filter_t **,
+ boolean_t);
+static int dup_filter(ipqos_conf_filter_t *, ipqos_conf_filter_t **, int, int,
+ void *, void *, int);
+static void free_actions(ipqos_conf_action_t *);
+static ipqos_conf_filter_t *alloc_filter();
+static void free_filter(ipqos_conf_filter_t *);
+static int read_curl_begin(FILE *);
+static ipqos_conf_class_t *alloc_class(void);
+static int diffclasses(ipqos_conf_action_t *old, ipqos_conf_action_t *new);
+static int difffilters(ipqos_conf_action_t *old, ipqos_conf_action_t *new);
+static int dup_class(ipqos_conf_class_t *src, ipqos_conf_class_t **dst);
+static int add_action(ipqos_conf_action_t *act);
+static int masktocidr(int af, in6_addr_t *mask);
+static int read_perm_items(int, FILE *, char *, char ***, int *);
+static int in_string_table(char *stable[], int size, char *string);
+static void list_end(ipqos_list_el_t **listp, ipqos_list_el_t ***lendpp);
+static void add_to_list(ipqos_list_el_t **listp, ipqos_list_el_t *el);
+static int read_cfile_ver(FILE *, char *);
+static char *quote_ws_string(const char *);
+static int read_tfile_ver(FILE *, char *, char *);
+static int ver_str_to_int(char *);
+static void printuser(FILE *fp, uid_t uid);
+static int readuser(char *str, uid_t *uid);
+
+/*
+ * macros to call list functions with the more complex list element type
+ * cast to the skeletal type iqpos_list_el_t.
+ */
+#define LIST_END(list, end)\
+ list_end((ipqos_list_el_t **)list, (ipqos_list_el_t ***)end)
+#define ADD_TO_LIST(list, el)\
+ add_to_list((ipqos_list_el_t **)list, (ipqos_list_el_t *)el)
+
+/*
+ * Macros to produce a quoted string containing the value of a
+ * preprocessor macro. For example, if SIZE is defined to be 256,
+ * VAL2STR(SIZE) is "256". This is used to construct format
+ * strings for scanf-family functions below.
+ */
+#define QUOTE(x) #x
+#define VAL2STR(x) QUOTE(x)
+
+
+/* globals */
+
+/* table of supported parameter types and enum value */
+static str_val_t nv_types[] = {
+{"uint8", IPQOS_DATA_TYPE_UINT8},
+{"int16", IPQOS_DATA_TYPE_INT16},
+{"uint16", IPQOS_DATA_TYPE_UINT16},
+{"int32", IPQOS_DATA_TYPE_INT32},
+{"uint32", IPQOS_DATA_TYPE_UINT32},
+{"boolean", IPQOS_DATA_TYPE_BOOLEAN},
+{"string", IPQOS_DATA_TYPE_STRING},
+{"action", IPQOS_DATA_TYPE_ACTION},
+{"address", IPQOS_DATA_TYPE_ADDRESS},
+{"port", IPQOS_DATA_TYPE_PORT},
+{"protocol", IPQOS_DATA_TYPE_PROTO},
+{"enum", IPQOS_DATA_TYPE_ENUM},
+{"ifname", IPQOS_DATA_TYPE_IFNAME},
+{"mindex", IPQOS_DATA_TYPE_M_INDEX},
+{"int_array", IPQOS_DATA_TYPE_INT_ARRAY},
+{"user", IPQOS_DATA_TYPE_USER},
+{"", 0}
+};
+
+/* table of name to id mappings for originator field */
+
+static str_val_t originators[] = {
+{IPP_CONFIG_NAME_PERMANENT, IPP_CONFIG_PERMANENT},
+{IPP_CONFIG_NAME_IPQOSCONF, IPP_CONFIG_IPQOSCONF},
+{IPP_CONFIG_NAME_FTPCL, IPP_CONFIG_FTPCL},
+{"", -1}
+};
+
+/* current parse line */
+static int lineno;
+
+/* verbose output flag */
+static int verbose;
+
+/* use syslog for msg reporting flag */
+static int use_syslog;
+
+#ifdef _IPQOS_CONF_DEBUG
+/*
+ * flag used to indicate that a rollback should be carried out regardless.
+ * Only settable during debug.
+ */
+static int force_rback = 0;
+#endif /* _IPQOS_CONF_DEBUG */
+
+/*
+ * delivers messages to either syslog or stderr, dependant upon the
+ * the state of the flags use_syslog and verbose. The type
+ * of the msg as given in msg_type is indicated in the output msg.
+ *
+ * valid message types are:
+ * o MT_ERROR (standard error message)
+ * o MT_ENOSTR (error message with system error string appended)
+ * o MT_WARNING (warning message)
+ * o MT_LOG (logging message)
+ *
+ * Log messages only go to syslog. Warning messages only go to stderr
+ * and only when the verbose flag is set. All other messages go by default
+ * to the console; to syslog if syslog flag set, and to both if both
+ * syslog and verbose are set.
+ *
+ */
+/*PRINTFLIKE2*/
+static void
+ipqos_msg(enum msg_type msgt, char *format, ...)
+{
+ va_list ap;
+ char str_buf[IPQOS_MSG_BUF_SZ];
+ char fmt_buf[IPQOS_MSG_BUF_SZ];
+ char *cp;
+
+ IPQOSCDBG0(L1, "In ipqos_msg:\n");
+
+ va_start(ap, format);
+
+ /*
+ * send msgs to syslog if use_syslog set (except warning msgs),
+ * or a log msg.
+ */
+ if ((use_syslog && (msgt != MT_WARNING)) || msgt == MT_LOG) {
+
+ /* fill in format string */
+ (void) vsnprintf(str_buf, IPQOS_MSG_BUF_SZ, format, ap);
+
+ /*
+ * print message to syslog with appropriate severity
+ */
+ if (msgt == MT_ERROR) {
+ syslog(LOG_ERR, str_buf);
+ } else if (msgt == MT_LOG) {
+ syslog(LOG_INFO, str_buf);
+ /*
+ * for errno message type suffix with %m for syslog to
+ * interpret.
+ */
+ } else if (msgt == MT_ENOSTR) {
+ /*
+ * remove any newline in message parameter.
+ * syslog will reapply a newline for us later.
+ */
+ if ((cp = strchr(str_buf, '\n')) != NULL)
+ *cp = '\0';
+ (void) strlcat(str_buf, ": %m", IPQOS_MSG_BUF_SZ);
+ syslog(LOG_ERR, str_buf);
+ }
+ }
+
+ /*
+ * send msgs to stderr if use_syslog not set (except log msgs), or
+ * if verbose set.
+ */
+ if ((!use_syslog && (msgt != MT_LOG)) || (verbose)) {
+
+ /*
+ * prefix message with appropriate severity string
+ */
+ if (msgt == MT_ERROR) {
+ (void) strlcpy(fmt_buf, gettext("Error: "),
+ IPQOS_MSG_BUF_SZ);
+ } else if (msgt == MT_WARNING) {
+ if (!verbose) { /* don't show warn msg if !verbose */
+ va_end(ap);
+ return;
+ }
+ (void) strlcpy(fmt_buf, gettext("Warning: "),
+ IPQOS_MSG_BUF_SZ);
+ } else if (msgt == MT_ENOSTR) {
+ (void) strlcpy(fmt_buf, gettext("Error: "),
+ IPQOS_MSG_BUF_SZ);
+ } else if (msgt == MT_LOG) {
+ (void) strlcpy(fmt_buf, gettext("Notice: "),
+ IPQOS_MSG_BUF_SZ);
+ }
+ (void) strlcat(fmt_buf, format, IPQOS_MSG_BUF_SZ);
+
+ /*
+ * for errno message type suffix message with errno string
+ */
+ if (msgt == MT_ENOSTR) {
+ /*
+ * get rid of any newline in passed message.
+ * we'll apply another later.
+ */
+ if ((cp = strchr(fmt_buf, '\n')) != NULL)
+ *cp = '\0';
+ (void) strlcat(fmt_buf, ": ", IPQOS_MSG_BUF_SZ);
+ (void) strlcat(fmt_buf, strerror(errno),
+ IPQOS_MSG_BUF_SZ);
+ }
+
+ /*
+ * append a newline to message if not one already.
+ */
+ if ((cp = strchr(fmt_buf, '\n')) == NULL)
+ (void) strlcat(fmt_buf, "\n", IPQOS_MSG_BUF_SZ);
+
+ (void) vfprintf(stderr, fmt_buf, ap);
+ }
+
+ va_end(ap);
+}
+
+/* **************** kernel filter/class/params manipulation fns *********** */
+
+
+/*
+ * modify the kernel parameters of the action action_nm using the nvlist
+ * parameter nvl and setting the stats according to stats_enable.
+ * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
+ */
+
+static int
+modify_params(
+char *action_name,
+nvlist_t **nvl,
+int module_version,
+boolean_t stats_enable)
+{
+
+ int res;
+ int created = 0;
+
+ IPQOSCDBG1(APPLY, "In modify_params: action: %s\n", action_name);
+
+ /* create nvlist if NULL */
+ if (*nvl == NULL) {
+ created++;
+ res = nvlist_alloc(nvl, NV_UNIQUE_NAME, 0);
+ if (res != 0) {
+ ipqos_msg(MT_ENOSTR, "nvlist_alloc");
+ return (IPQOS_CONF_ERR);
+ }
+ }
+
+ /* add params modify config type */
+ res = nvlist_add_byte(*nvl, IPP_CONFIG_TYPE, IPP_SET);
+ if (res != 0) {
+ ipqos_msg(MT_ENOSTR, "nvlist_add_byte");
+ goto fail;
+ }
+
+ /*
+ * add module version
+ */
+ if (nvlist_add_uint32(*nvl, IPP_MODULE_VERSION,
+ (uint32_t)module_version) != 0) {
+ ipqos_msg(MT_ENOSTR, "nvlist_add_uint32");
+ goto fail;
+ }
+
+ /* add stats_enable */
+ res = nvlist_add_uint32(*nvl, IPP_ACTION_STATS_ENABLE,
+ (uint32_t)stats_enable);
+ if (res != 0) {
+ ipqos_msg(MT_ENOSTR, "nvlist_add_uint32");
+ goto fail;
+ }
+
+ /* add ipqosconf as originator */
+ res = add_orig_ipqosconf(*nvl);
+ if (res != IPQOS_CONF_SUCCESS) {
+ goto fail;
+ }
+
+ /* call lib to do modify */
+ res = ipp_action_modify(action_name, nvl, 0);
+ if (res != 0) {
+
+ /* invalid parameters */
+
+ if (errno == EINVAL) {
+ ipqos_msg(MT_ERROR,
+ gettext("Invalid parameters for action %s.\n"),
+ action_name);
+
+
+ } else if (errno == ENOENT) {
+ ipqos_msg(MT_ERROR,
+ gettext("Mandatory parameter missing for "
+ "action %s.\n"), action_name);
+
+
+ } else { /* unexpected error */
+ ipqos_msg(MT_ERROR, gettext("Failed to modify action "
+ "%s parameters: %s.\n"), action_name,
+ strerror(errno));
+ }
+
+ goto fail;
+ }
+
+ return (IPQOS_CONF_SUCCESS);
+fail:
+ if (created && *nvl != NULL) {
+ nvlist_free(*nvl);
+ *nvl = NULL;
+ }
+ return (IPQOS_CONF_ERR);
+}
+
+/*
+ * add a class to the kernel action action_name called class_name with
+ * stats set according to stats_enable and the first action set to
+ * first_action.
+ * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
+ */
+static int
+add_class(
+char *action_name,
+char *class_name,
+int module_version,
+boolean_t stats_enable,
+char *first_action)
+{
+
+ nvlist_t *nvl;
+
+ IPQOSCDBG4(APPLY, "add_class: action: %s, class: %s, "
+ "first_action: %s, stats: %s\n", action_name, class_name,
+ first_action, (stats_enable == B_TRUE ? "true" : "false"));
+
+
+ /* create nvlist */
+ if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {
+ ipqos_msg(MT_ENOSTR, "nvlist_alloc");
+ return (IPQOS_CONF_ERR);
+ }
+
+ /* add 'add class' config type */
+ if (nvlist_add_byte(nvl, IPP_CONFIG_TYPE, CLASSIFIER_ADD_CLASS) != 0) {
+ ipqos_msg(MT_ENOSTR, "nvlist_add_byte");
+ goto fail;
+ }
+
+ /*
+ * add module version
+ */
+ if (nvlist_add_uint32(nvl, IPP_MODULE_VERSION,
+ (uint32_t)module_version) != 0) {
+ ipqos_msg(MT_ENOSTR, "nvlist_add_uint32");
+ goto fail;
+ }
+
+ /* add class name */
+ if (nvlist_add_string(nvl, CLASSIFIER_CLASS_NAME, class_name) != 0) {
+ ipqos_msg(MT_ENOSTR, "nvlist_add_string");
+ goto fail;
+ }
+
+ /* add next action */
+ if (nvlist_add_string(nvl, CLASSIFIER_NEXT_ACTION, first_action) != 0) {
+ ipqos_msg(MT_ENOSTR, "nvlist_add_string");
+ goto fail;
+ }
+
+ /* add stats_enable */
+ if (nvlist_add_uint32(nvl, CLASSIFIER_CLASS_STATS_ENABLE,
+ (uint32_t)stats_enable) != 0) {
+ ipqos_msg(MT_ENOSTR, "nvlist_add_uint32");
+ goto fail;
+ }
+
+ /* add ipqosconf as originator */
+ if (add_orig_ipqosconf(nvl) != IPQOS_CONF_SUCCESS) {
+ goto fail;
+ }
+
+ /* call lib to do modify */
+ if (ipp_action_modify(action_name, &nvl, 0) != 0) {
+
+ /* ipgpc max classes */
+
+ if (errno == ENOSPC &&
+ strcmp(action_name, IPGPC_CLASSIFY) == 0) {
+ ipqos_msg(MT_ERROR,
+ gettext("Max number of classes reached in %s.\n"),
+ IPGPC_NAME);
+
+ /* other errors */
+
+ } else {
+ ipqos_msg(MT_ERROR,
+ gettext("Failed to create class %s in action "
+ "%s: %s.\n"), class_name, action_name,
+ strerror(errno));
+ }
+
+ goto fail;
+ }
+
+ return (IPQOS_CONF_SUCCESS);
+fail:
+ if (nvl != NULL)
+ nvlist_free(nvl);
+ return (IPQOS_CONF_ERR);
+}
+
+
+/*
+ * modify the class in the kernel action action_name called class_name with
+ * stats set according to stats_enable and the first action set to
+ * first_action.
+ * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
+ */
+static int
+modify_class(
+char *action_name,
+char *class_name,
+int module_version,
+boolean_t stats_enable,
+char *first_action,
+enum ipp_flags flags)
+{
+
+ nvlist_t *nvl;
+
+ IPQOSCDBG5(APPLY, "modify_class: action: %s, class: %s, first: %s, "
+ "stats: %s, flags: %x\n", action_name, class_name, first_action,
+ stats_enable == B_TRUE ? "true" : "false", flags);
+
+
+ /* create nvlist */
+ if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {
+ ipqos_msg(MT_ENOSTR, "nvlist_alloc");
+ return (IPQOS_CONF_ERR);
+ }
+
+ /* add 'modify class' config type */
+ if (nvlist_add_byte(nvl, IPP_CONFIG_TYPE, CLASSIFIER_MODIFY_CLASS) !=
+ 0) {
+ ipqos_msg(MT_ENOSTR, "nvlist_add_byte");
+ goto fail;
+ }
+
+ /*
+ * add module version
+ */
+ if (nvlist_add_uint32(nvl, IPP_MODULE_VERSION,
+ (uint32_t)module_version) != 0) {
+ ipqos_msg(MT_ENOSTR, "nvlist_add_uint32");
+ goto fail;
+ }
+
+ /* add class name */
+ if (nvlist_add_string(nvl, CLASSIFIER_CLASS_NAME, class_name) != 0) {
+ ipqos_msg(MT_ENOSTR, "nvlist_add_string");
+ goto fail;
+ }
+
+ /* add next action */
+ if (nvlist_add_string(nvl, CLASSIFIER_NEXT_ACTION, first_action) != 0) {
+ ipqos_msg(MT_ENOSTR, "nvlist_add_string");
+ goto fail;
+ }
+
+ /* add stats enable */
+ if (nvlist_add_uint32(nvl, CLASSIFIER_CLASS_STATS_ENABLE,
+ (uint32_t)stats_enable) != 0) {
+ ipqos_msg(MT_ENOSTR, "nvlist_add_uint32");
+ goto fail;
+ }
+
+ /* add originator ipqosconf */
+ if (add_orig_ipqosconf(nvl) != IPQOS_CONF_SUCCESS) {
+ goto fail;
+ }
+
+ /* call lib to do modify */
+ if (ipp_action_modify(action_name, &nvl, flags) != 0) {
+
+ /* generic error message */
+
+ ipqos_msg(MT_ERROR,
+ gettext("Modifying class %s in action %s failed: %s.\n"),
+ class_name, action_name, strerror(errno));
+
+ goto fail;
+ }
+
+ return (IPQOS_CONF_SUCCESS);
+fail:
+ if (nvl != NULL)
+ nvlist_free(nvl);
+ return (IPQOS_CONF_ERR);
+}
+
+/*
+ * removes the class class_name from the kernel action action_name. The
+ * flags argument can currently be set to IPP_ACTION_DESTROY which will
+ * result in the action this class references being destroyed.
+ * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
+ */
+static int
+remove_class(
+char *action_name,
+char *class_name,
+int module_version,
+enum ipp_flags flags)
+{
+
+ nvlist_t *nvl;
+
+ IPQOSCDBG3(APPLY, "remove_class: action: %s, class: %s, "
+ "flags: %x\n", action_name, class_name, flags);
+
+ /* allocate nvlist */
+ if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {
+ ipqos_msg(MT_ENOSTR, "nvlist_alloc");
+ return (IPQOS_CONF_ERR);
+ }
+
+ /* add 'remove class' config type */
+ if (nvlist_add_byte(nvl, IPP_CONFIG_TYPE, CLASSIFIER_REMOVE_CLASS) !=
+ 0) {
+ ipqos_msg(MT_ENOSTR, "nvlist_add_byte");
+ goto fail;
+ }
+
+ /*
+ * add module version
+ */
+ if (nvlist_add_uint32(nvl, IPP_MODULE_VERSION,
+ (uint32_t)module_version) != 0) {
+ ipqos_msg(MT_ENOSTR, "nvlist_add_uint32");
+ goto fail;
+ }
+
+ /* add class name */
+ if (nvlist_add_string(nvl, CLASSIFIER_CLASS_NAME, class_name) != 0) {
+ ipqos_msg(MT_ENOSTR, "nvlist_add_string");
+ goto fail;
+ }
+
+ if (ipp_action_modify(action_name, &nvl, flags) != 0) {
+
+ /* generic error message */
+
+ ipqos_msg(MT_ERROR,
+ gettext("Removing class %s in action %s failed: %s.\n"),
+ class_name, action_name, strerror(errno));
+
+ goto fail;
+ }
+
+ return (IPQOS_CONF_SUCCESS);
+fail:
+ if (nvl != NULL)
+ nvlist_free(nvl);
+ return (IPQOS_CONF_ERR);
+}
+
+/*
+ * add the filter flt to the kernel action named action_name.
+ * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
+ */
+static int
+add_filter(
+char *action_name,
+ipqos_conf_filter_t *flt,
+int module_version)
+{
+
+ nvlist_t *nvl = flt->nvlist;
+ char ipvsbuf[IPQOS_INT_STR_LEN];
+
+ IPQOSCDBG4(APPLY, "add_filter: action: %s, filter: %s, "
+ "instance: %d, class: %s\n", action_name, flt->name,
+ flt->instance, flt->class_name);
+
+
+ /* add 'add filter' config type to filter nvlist */
+ if (nvlist_add_byte(nvl, IPP_CONFIG_TYPE, CLASSIFIER_ADD_FILTER) != 0) {
+ ipqos_msg(MT_ENOSTR, "nvlist_add_byte");
+ return (IPQOS_CONF_ERR);
+ }
+
+ /*
+ * add module version
+ */
+ if (nvlist_add_uint32(nvl, IPP_MODULE_VERSION,
+ (uint32_t)module_version) != 0) {
+ ipqos_msg(MT_ENOSTR, "nvlist_add_uint32");
+ return (IPQOS_CONF_ERR);
+ }
+
+ /* add filter name to nvlist */
+ if (nvlist_add_string(nvl, CLASSIFIER_FILTER_NAME, flt->name) != 0) {
+ ipqos_msg(MT_ENOSTR, "nvlist_add_string");
+ return (IPQOS_CONF_ERR);
+ }
+
+ /* add class name to nvlist */
+ if (nvlist_add_string(nvl, CLASSIFIER_CLASS_NAME, flt->class_name) !=
+ 0) {
+ ipqos_msg(MT_ENOSTR, "nvlist_add_string");
+ return (IPQOS_CONF_ERR);
+ }
+
+ /* add ipqosconf as originator to nvlist */
+ if (add_orig_ipqosconf(nvl) != IPQOS_CONF_SUCCESS) {
+ return (IPQOS_CONF_ERR);
+ }
+
+ /* add ipgpc specific nv entrys */
+ if (strcmp(action_name, IPGPC_CLASSIFY) == 0) {
+
+ /* add src and dst nodes to nvlist if present */
+
+ if (flt->src_nd_name != NULL &&
+ nvlist_add_string(nvl, IPGPC_SADDR_HOSTNAME,
+ flt->src_nd_name) != 0) {
+ ipqos_msg(MT_ENOSTR, "nvlist_add_string");
+ return (IPQOS_CONF_ERR);
+ }
+ if (flt->dst_nd_name != NULL &&
+ nvlist_add_string(nvl, IPGPC_DADDR_HOSTNAME,
+ flt->dst_nd_name) != 0) {
+ ipqos_msg(MT_ENOSTR, "nvlist_add_string");
+ return (IPQOS_CONF_ERR);
+ }
+
+ /*
+ * add ip_version to private list element if present.
+ * NOTE: this value is of only real use to ipqosconf so
+ * it is placed in this opaque private field.
+ */
+ if (flt->ip_versions != 0) {
+ (void) sprintf(ipvsbuf, "%d", flt->ip_versions);
+ if (nvlist_add_string(nvl, IPGPC_FILTER_PRIVATE,
+ ipvsbuf) != 0) {
+ ipqos_msg(MT_ENOSTR, "nvlist_add_string");
+ return (IPQOS_CONF_ERR);
+ }
+ }
+
+ /* add filter instance if present */
+
+ if (nvlist_add_int32(nvl, IPGPC_FILTER_INSTANCE,
+ flt->instance) != 0) {
+ ipqos_msg(MT_ENOSTR, "nvlist_add_int32");
+ return (IPQOS_CONF_ERR);
+ }
+ }
+
+ if (ipp_action_modify(action_name, &flt->nvlist, 0) != 0) {
+
+ /* invalid parameters */
+
+ if (errno == EINVAL) {
+ ipqos_msg(MT_ERROR,
+ gettext("Invalid/missing parameters for filter "
+ "%s in action %s.\n"), flt->name, action_name);
+
+ /* max ipgpc filters/classes */
+
+ } else if (errno == ENOSPC &&
+ strcmp(action_name, IPGPC_CLASSIFY) == 0) {
+ ipqos_msg(MT_ERROR, gettext("Max number of filters "
+ "reached in action %s.\n"), IPGPC_NAME);
+
+ /* anything other errnos */
+ } else {
+ ipqos_msg(MT_ERROR,
+ gettext("Failed to create filter %s in action "
+ "%s: %s.\n"), flt->name, action_name,
+ strerror(errno));
+ }
+
+ return (IPQOS_CONF_ERR);
+ }
+
+ return (IPQOS_CONF_SUCCESS);
+}
+
+
+/*
+ * modify the filter flt in the kernel action named action_name.
+ * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
+ */
+static int
+modify_filter(
+char *action_name,
+ipqos_conf_filter_t *flt,
+int module_version)
+{
+
+ nvlist_t *nvl = flt->nvlist;
+ char ipvsbuf[IPQOS_INT_STR_LEN];
+
+ IPQOSCDBG4(APPLY, "modify_filter: action: %s, filter: %s, "
+ "instance: %d, class: %s\n", action_name, flt->name,
+ flt->instance, flt->class_name);
+
+/* show src address and dst address if present */
+#ifdef _IPQOS_CONF_DEBUG
+ if (ipqosconf_dbg_flgs & APPLY) {
+ uint_t tmp;
+ in6_addr_t *add;
+ char st[100];
+
+ if (nvlist_lookup_uint32_array(nvl, IPGPC_SADDR,
+ (uint32_t **)&add, &tmp) == 0) {
+ (void) fprintf(stderr, "saddr: %s\n",
+ inet_ntop(AF_INET6, add, st, 100));
+ }
+
+ if (nvlist_lookup_uint32_array(nvl, IPGPC_DADDR,
+ (uint32_t **)&add, &tmp) == 0) {
+ (void) fprintf(stderr, "daddr: %s\n",
+ inet_ntop(AF_INET6, add, st, 100));
+ }
+ }
+#endif /* _IPQOS_CONF_DEBUG */
+
+ /* add 'modify filter' config type to filters nvlist */
+ if (nvlist_add_byte(nvl, IPP_CONFIG_TYPE,
+ CLASSIFIER_MODIFY_FILTER) != 0) {
+ ipqos_msg(MT_ENOSTR, "nvlist_add_byte");
+ return (IPQOS_CONF_ERR);
+ }
+
+ /*
+ * add module version
+ */
+ if (nvlist_add_uint32(nvl, IPP_MODULE_VERSION,
+ (uint32_t)module_version) != 0) {
+ ipqos_msg(MT_ENOSTR, "nvlist_add_uint32");
+ return (IPQOS_CONF_ERR);
+ }
+
+ /* add filter name to nvlist */
+ if (nvlist_add_string(nvl, CLASSIFIER_FILTER_NAME, flt->name) != 0) {
+ ipqos_msg(MT_ENOSTR, "nvlist_add_string");
+ return (IPQOS_CONF_ERR);
+ }
+
+ /* add class name to nvlist */
+ if (nvlist_add_string(nvl, CLASSIFIER_CLASS_NAME, flt->class_name) !=
+ 0) {
+ ipqos_msg(MT_ENOSTR, "nvlist_add_string");
+ return (IPQOS_CONF_ERR);
+ }
+
+ /* add originator ipqosconf to nvlist */
+ if (add_orig_ipqosconf(nvl) != IPQOS_CONF_SUCCESS) {
+ return (IPQOS_CONF_ERR);
+ }
+
+ /* add ipgpc specific nvpairs */
+ if (strcmp(action_name, IPGPC_CLASSIFY) == 0) {
+
+ /* add src and dst nodes to nvlist if present */
+
+ if (flt->src_nd_name &&
+ nvlist_add_string(nvl, IPGPC_SADDR_HOSTNAME,
+ flt->src_nd_name) != 0) {
+ ipqos_msg(MT_ENOSTR, "nvlist_add_string");
+ return (IPQOS_CONF_ERR);
+ }
+ if (flt->dst_nd_name &&
+ nvlist_add_string(nvl, IPGPC_DADDR_HOSTNAME,
+ flt->dst_nd_name) != 0) {
+ ipqos_msg(MT_ENOSTR, "nvlist_add_string");
+ return (IPQOS_CONF_ERR);
+ }
+
+ /*
+ * add ip_version to private list element if present.
+ * NOTE: this value is of only real use to ipqosconf so
+ * it is placed in this opaque private field.
+ */
+ if (flt->ip_versions != 0) {
+ (void) sprintf(ipvsbuf, "%d", flt->ip_versions);
+ if (nvlist_add_string(nvl, IPGPC_FILTER_PRIVATE,
+ ipvsbuf) != 0) {
+ ipqos_msg(MT_ENOSTR, "nvlist_add_string");
+ return (IPQOS_CONF_ERR);
+ }
+ }
+
+ /* add filter instance if present */
+
+ if (nvlist_add_int32(nvl, IPGPC_FILTER_INSTANCE,
+ flt->instance) != 0) {
+ ipqos_msg(MT_ENOSTR, "nvlist_add_int32");
+ return (IPQOS_CONF_ERR);
+ }
+ }
+
+ if (ipp_action_modify(action_name, &flt->nvlist, 0) != 0) {
+
+ /* invalid parameters */
+
+ if (errno == EINVAL) {
+ ipqos_msg(MT_ERROR, gettext("Missing/Invalid "
+ "parameter for filter %s in action %s.\n"),
+ flt->name, action_name);
+
+ /* any other errnos */
+
+ } else {
+ ipqos_msg(MT_ERROR,
+ gettext("Failed to modify filter %s in action %s: "
+ "%s.\n"), flt->name, action_name, strerror(errno));
+ }
+
+ return (IPQOS_CONF_ERR);
+ }
+
+ return (IPQOS_CONF_SUCCESS);
+}
+
+/*
+ * remove the filter named filter_name instance number instance from the
+ * kernel action action_name.
+ * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
+ */
+static int
+remove_filter(
+char *action_name,
+char *filter_name,
+int instance,
+int module_version)
+{
+
+ nvlist_t *nvl;
+
+ IPQOSCDBG2(APPLY, "remove_filter: action: %s, filter: %s\n",
+ action_name, filter_name);
+
+ /* create nvlist */
+ if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {
+ ipqos_msg(MT_ENOSTR, "nvlist_alloc");
+ return (IPQOS_CONF_ERR);
+ }
+
+ /* add 'remove filter' config type to list */
+ if (nvlist_add_byte(nvl, IPP_CONFIG_TYPE, CLASSIFIER_REMOVE_FILTER)
+ != 0) {
+ ipqos_msg(MT_ENOSTR, "nvlist_add_byte");
+ return (IPQOS_CONF_ERR);
+ }
+
+ /*
+ * add module version
+ */
+ if (nvlist_add_uint32(nvl, IPP_MODULE_VERSION,
+ (uint32_t)module_version) != 0) {
+ ipqos_msg(MT_ENOSTR, "nvlist_add_uint32");
+ return (IPQOS_CONF_ERR);
+ }
+
+ /* add filter name to list */
+ if (nvlist_add_string(nvl, CLASSIFIER_FILTER_NAME, filter_name) != 0) {
+ ipqos_msg(MT_ENOSTR, "nvlist_add_string");
+ return (IPQOS_CONF_ERR);
+ }
+
+ /* add instance number if part of multi-instance filter */
+ if (instance != -1 && nvlist_add_int32(nvl, IPGPC_FILTER_INSTANCE,
+ instance) != 0) {
+ ipqos_msg(MT_ENOSTR, "nvlist_add_int32");
+ return (IPQOS_CONF_ERR);
+ }
+
+ /* call into lib to remove */
+ if (ipp_action_modify(action_name, &nvl, 0) != 0) {
+
+ /* generic error message */
+
+ ipqos_msg(MT_ERROR,
+ gettext("Removing filter %s in action %s failed: %s.\n"),
+ filter_name, action_name, strerror(errno));
+
+ return (IPQOS_CONF_ERR);
+ }
+
+ return (IPQOS_CONF_SUCCESS);
+}
+
+/* ******************************************************************* */
+
+
+/*
+ * add originator nvpair set to ipqosconf to nvl.
+ * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
+ */
+static int
+add_orig_ipqosconf(nvlist_t *nvl)
+{
+
+ if (nvlist_add_uint32(nvl, IPP_CONFIG_ORIGINATOR,
+ IPP_CONFIG_IPQOSCONF) != 0) {
+ ipqos_msg(MT_ENOSTR, "nvlist_add_uint32: originator:");
+ return (IPQOS_CONF_ERR);
+ }
+
+ return (IPQOS_CONF_SUCCESS);
+}
+
+/* ************************* differencing functions ************************ */
+
+
+/*
+ * compares the contents of arrays array1 and array2, both of size size, and
+ * returns B_TRUE or B_FALSE if they're equal or not respectively.
+ * RETURNS: B_TRUE if equal, else B_FALSE.
+ */
+static boolean_t
+arrays_equal(
+int array1[],
+int array2[],
+uint32_t size)
+{
+ int x;
+
+ for (x = 0; x < size; x++) {
+ if (array1[x] != array2[x])
+ return (B_FALSE);
+ }
+ return (B_TRUE);
+}
+
+/*
+ * difference class old against class new. It marks the new class as
+ * modified if it is different.
+ * RETURNS: IPQOS_CONF_SUCCESS.
+ */
+static int
+diffclass(
+ipqos_conf_class_t *old,
+ipqos_conf_class_t *new)
+{
+
+ IPQOSCDBG0(L0, "In diffclass:\n");
+
+ /* two different spec'd actions */
+ if (strcmp(old->alist->name, new->alist->name) != 0) {
+ IPQOSCDBG1(DIFF, "marking class %s as modified\n", new->name);
+
+ new->modified = B_TRUE;
+ return (IPQOS_CONF_SUCCESS);
+ }
+
+ /* different stats values */
+ if (old->stats_enable != new->stats_enable) {
+ IPQOSCDBG1(DIFF, "marking class %s as modified\n", new->name);
+
+ new->modified = B_TRUE;
+ return (IPQOS_CONF_SUCCESS);
+ }
+
+ return (IPQOS_CONF_SUCCESS);
+}
+
+/*
+ * difference params set old against params set new of module module_name. It
+ * marks the new params as modified if different.
+ * RETURNS: if error IPQOS_CONF_ERR, else IPQOS_CONF_SUCCESS.
+ */
+static int
+diffparams(
+ipqos_conf_params_t *old,
+ipqos_conf_params_t *new,
+char *module_name)
+{
+
+ int diff;
+ int res;
+
+ IPQOSCDBG0(L0, "In diffparams\n");
+
+ /* diff stats */
+ if (old->stats_enable != new->stats_enable) {
+
+ new->modified = B_TRUE;
+ return (IPQOS_CONF_SUCCESS);
+ }
+
+ /* diff module specific params */
+ res = diffnvlists(old->nvlist, new->nvlist, module_name, &diff,
+ PL_PARAMS);
+ if (res != IPQOS_CONF_SUCCESS) {
+ return (res);
+ }
+ if (diff) {
+
+ new->modified = B_TRUE;
+ }
+
+ return (IPQOS_CONF_SUCCESS);
+}
+
+/*
+ * differences filter old against filter new of module module_name. It marks
+ * filter new as different if so.
+ * RETURNS: if error IPQOS_CONF_ERR, else IPQOS_CONF_SUCCESS.
+ */
+static int
+difffilter(
+ipqos_conf_filter_t *old,
+ipqos_conf_filter_t *new,
+char *module_name)
+{
+
+ int res;
+ int diff;
+
+ IPQOSCDBG0(L0, "In difffilter\n");
+
+ /* compare class name */
+
+ if (strcmp(old->class_name, new->class_name) != 0) {
+ IPQOSCDBG1(DIFF, "Marking filter %s as modified\n", new->name);
+
+ new->modified = B_TRUE;
+ return (IPQOS_CONF_SUCCESS);
+ }
+
+ /* compare module specific params */
+
+ res = diffnvlists(old->nvlist, new->nvlist, module_name, &diff,
+ PL_FILTER);
+ if (res != IPQOS_CONF_SUCCESS) {
+ return (res);
+ }
+
+ if (diff) {
+ IPQOSCDBG1(DIFF, "Marking filter %s as modified\n", new->name);
+ new->modified = B_TRUE;
+ }
+
+ return (IPQOS_CONF_SUCCESS);
+}
+
+
+/*
+ * mark all the filters and classes in parameter action either
+ * for deletion (if they are ipqosconf originated) or for modification.
+ */
+static void
+mark_classes_filters_del(ipqos_conf_action_t *action)
+{
+
+ ipqos_conf_filter_t *flt;
+ ipqos_conf_class_t *cls;
+
+ IPQOSCDBG1(L1, "In mark_classes_filters_del: action: %s\n",
+ action->name);
+
+ /* mark all non-permanent filters for del and permanent to modify */
+ for (flt = action->filters; flt; flt = flt->next) {
+ if (flt->originator == IPP_CONFIG_PERMANENT) {
+ IPQOSCDBG1(DIFF, "Marking prm filter %s as modified.\n",
+ flt->name);
+
+ flt->modified = B_TRUE;
+ } else {
+ IPQOSCDBG1(DIFF, "Marking filter %s as del.\n",
+ flt->name);
+
+ flt->todel = B_TRUE;
+ }
+ }
+
+ /* mark all non-permanent classes for del and permanent to modify */
+ for (cls = action->classes; cls; cls = cls->next) {
+ if (cls->originator == IPP_CONFIG_PERMANENT) {
+ IPQOSCDBG1(DIFF, "Marking prm class %s as modified.\n",
+ cls->name);
+
+ cls->modified = B_TRUE;
+ } else {
+ IPQOSCDBG1(DIFF, "Marking class %s as del.\n",
+ cls->name);
+
+ cls->todel = B_TRUE;
+ }
+ }
+}
+
+/*
+ * mark all classes and filters either new (non-permanent) or modified.
+ */
+static void
+mark_classes_filters_new(ipqos_conf_action_t *action)
+{
+
+ ipqos_conf_filter_t *flt;
+ ipqos_conf_class_t *cls;
+
+ IPQOSCDBG1(L1, "In mark_classes_filters_new: action: %s\n",
+ action->name);
+
+ /* mark all permanent filters as modified and all others new */
+
+ for (flt = action->filters; flt; flt = flt->next) {
+ if (flt->originator == IPP_CONFIG_PERMANENT) {
+ IPQOSCDBG1(DIFF, "Marking prm filter %s as modified.\n",
+ flt->name);
+
+ flt->modified = B_TRUE;
+ action->modified = B_TRUE;
+ } else {
+ IPQOSCDBG1(DIFF, "Marking filter %s as new.\n",
+ flt->name);
+
+ flt->new = B_TRUE;
+ }
+ }
+
+ /* mark all permanent classes as modified and all others new */
+ for (cls = action->classes; cls; cls = cls->next) {
+ if (cls->originator == IPP_CONFIG_PERMANENT) {
+ IPQOSCDBG1(DIFF, "Marking prm class %s as modified.\n",
+ cls->name);
+
+ cls->modified = B_TRUE;
+ action->modified = B_TRUE;
+ } else {
+ IPQOSCDBG1(DIFF, "Marking class %s as new.\n",
+ cls->name);
+
+ cls->new = B_TRUE;
+ }
+ }
+}
+
+/*
+ * Marks all the actions and their constituent elements in conf
+ * as new.
+ */
+static void
+mark_config_new(
+ipqos_conf_action_t *conf)
+{
+ while (conf != NULL) {
+ IPQOSCDBG1(DIFF, "Marking action %s as new\n", conf->name);
+ mark_classes_filters_new(conf);
+ conf->new = B_TRUE;
+ conf->visited = 0;
+ conf = conf->next;
+ }
+}
+
+/*
+ * differences the configuration in new against old marking the actions
+ * and their contents appropriately.
+ * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
+ */
+static int
+diffconf(
+ipqos_conf_action_t *old,
+ipqos_conf_action_t *new)
+{
+
+ int res;
+ ipqos_conf_action_t *act;
+ ipqos_conf_action_t *tmp;
+
+ IPQOSCDBG0((L0 | DIFF), "In diffconf\n");
+
+ /* check the new actions against the old */
+
+ for (act = new; act; act = act->next) {
+
+ /* if action not in old mark it and it's contents as new */
+
+ if ((tmp = actionexist(act->name, old)) == NULL) {
+ IPQOSCDBG1(DIFF, "marking act %s as new\n", act->name);
+
+ act->new = B_TRUE;
+ mark_classes_filters_new(act);
+ continue;
+ }
+
+ /* if action in old diff old against new */
+
+ res = diffaction(tmp, act);
+ if (res != IPQOS_CONF_SUCCESS) {
+ return (res);
+ }
+ }
+
+ /*
+ * mark actions, and their contents, in old but not new that were
+ * created by us for del.
+ */
+
+ for (act = old; act; act = act->next) {
+ if (act->params->originator == IPP_CONFIG_IPQOSCONF &&
+ actionexist(act->name, new) == NULL) {
+ IPQOSCDBG1(DIFF, "marking act %s for del\n", act->name);
+
+ act->todel = B_TRUE;
+ mark_classes_filters_del(act);
+ }
+ }
+
+ return (IPQOS_CONF_SUCCESS);
+}
+
+/*
+ * differences action old against action new, comparing its classes, filters
+ * and parameters. If it is different the new action is marked as modified
+ * and it's different sub-objects are also marked approriately.
+ * RETURNS: IPQOS_CONF_ERR if error, else IPQOS_CONF_SUCCESS.
+ */
+static int
+diffaction(
+ipqos_conf_action_t *old,
+ipqos_conf_action_t *new)
+{
+
+ int res;
+
+ IPQOSCDBG0(L0, "In diffaction\n");
+
+ /* compare and mark classes */
+ res = diffclasses(old, new);
+ if (res != IPQOS_CONF_SUCCESS) {
+ return (res);
+ }
+
+ /* compare and mark filters */
+ res = difffilters(old, new);
+ if (res != IPQOS_CONF_SUCCESS) {
+ return (res);
+ }
+
+ /* compare and mark parameters */
+ res = diffparams(old->params, new->params, old->module);
+ if (res != IPQOS_CONF_SUCCESS) {
+ return (res);
+ }
+
+ /* mark action as modified if params are */
+ if (new->params->modified == B_TRUE) {
+ IPQOSCDBG1(DIFF, "Marking params for action %s modified\n",
+ new->name);
+
+ new->modified = B_TRUE;
+ }
+
+ return (IPQOS_CONF_SUCCESS);
+}
+
+/*
+ * differences the set of classes in new against those in old, marking any
+ * that are new/modified, approriately in the new class, and any removed
+ * in the old class appropriately. Also marks the action which has had an
+ * object within marked, as modified.
+ * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
+ */
+
+static int
+diffclasses(
+ipqos_conf_action_t *old,
+ipqos_conf_action_t *new)
+{
+
+
+ ipqos_conf_class_t *cls;
+ ipqos_conf_class_t *tmpc;
+ ipqos_conf_class_t *ncls;
+ int res;
+
+
+ /* loop through old classes checking for classes not present in new */
+
+ for (cls = old->classes; cls; cls = cls->next) {
+
+ if (classexist(cls->name, new->classes) == NULL) {
+
+ /* if we created original class mark for deletion */
+
+ if (cls->originator == IPP_CONFIG_IPQOSCONF) {
+ IPQOSCDBG1(DIFF, "marking class %s for del\n",
+ cls->name);
+
+ cls->todel = B_TRUE;
+
+ /* mark old action */
+ old->modified = B_TRUE;
+
+ /*
+ * if permanent class and next action created by us
+ * copy it, set it's next action to continue and
+ * add it to new action. This will cause the class
+ * to be marked as and modified. This returns the class
+ * to an assumed default state and prevents the
+ * case where the class is pointing at an action
+ * we want to remove and therefore couldn't without
+ * this forced modify.
+ */
+ } else if (cls->originator == IPP_CONFIG_PERMANENT &&
+ cls->alist->action && /* not virtual action */
+ cls->alist->action->params->originator ==
+ IPP_CONFIG_IPQOSCONF) {
+
+ /* copy class */
+
+ res = dup_class(cls, &ncls);
+ if (res != IPQOS_CONF_SUCCESS) {
+ return (IPQOS_CONF_ERR);
+ }
+
+ /* set next action to continue */
+
+ (void) strcpy(ncls->alist->name,
+ IPP_ANAME_CONT);
+
+ /* add to news classes to be diffed below */
+ ADD_TO_LIST(&new->classes, ncls);
+ }
+ }
+ }
+
+ /* loop through new classes checking for new / modified classes */
+
+ for (cls = new->classes; cls; cls = cls->next) {
+
+ /* new ipqosconf class */
+
+ if ((tmpc = classexist(cls->name, old->classes)) == NULL ||
+ (tmpc->originator != IPP_CONFIG_IPQOSCONF &&
+ tmpc->originator != IPP_CONFIG_PERMANENT)) {
+ IPQOSCDBG1(DIFF, "marking class %s new\n",
+ cls->name);
+
+ cls->new = B_TRUE;
+
+ new->modified = B_TRUE; /* mark new action */
+ continue;
+
+ /* existing ipqosconf/perm class */
+ } else {
+ res = diffclass(tmpc, cls);
+ if (res != IPQOS_CONF_SUCCESS) {
+ return (res);
+ }
+
+ if (cls->modified == B_TRUE) {
+ new->modified = B_TRUE;
+ }
+ }
+ }
+
+ return (IPQOS_CONF_SUCCESS);
+}
+
+/*
+ * differences the set of filters in new against those in old, marking any
+ * that are new/modified, approriately in the new filter/s, and any removed
+ * in the old filter appropriately. Also marks the action which has had an
+ * object within marked, as modified.
+ * RETURNS: IPQOS_CONF_SUCCESS (we return an int for symmetry with diffclasses
+ * and difffparams).
+ */
+static int
+difffilters(
+ipqos_conf_action_t *old,
+ipqos_conf_action_t *new)
+{
+
+ ipqos_conf_filter_t *flt;
+ ipqos_conf_filter_t *tmpf;
+ int maxi;
+ int newi;
+ int res;
+
+ /* check for new/modified filters */
+
+ for (flt = new->filters; flt; flt = flt->next) {
+
+ /* new ipqosconf filter */
+
+ if ((tmpf = filterexist(flt->name, -1, old->filters)) == NULL) {
+
+ /* mark all instances of this filter as new */
+ for (;;) {
+ IPQOSCDBG1(DIFF, "Marking filter %s as "
+ "new\n", flt->name);
+
+ flt->new = B_TRUE;
+
+
+ if (flt->next == NULL ||
+ strcmp(flt->next->name, flt->name) != 0) {
+ break;
+ }
+ flt = flt->next;
+ }
+ new->modified = B_TRUE; /* mark new action */
+
+ /* ipqosconf/permanent filter existed */
+ } else {
+ /*
+ * if ip node name force filter refresh - ie. mark
+ * all old filter instances as todel and all new new.
+ */
+ if (tmpf->src_nd_name || tmpf->dst_nd_name ||
+ flt->src_nd_name || flt->dst_nd_name) {
+
+ /* init max previous filter instance */
+ maxi = tmpf->instance;
+
+ /* mark old instances for deletion */
+ do {
+ IPQOSCDBG2(DIFF, "Marking filter "
+ "%s, instance %d for del\n",
+ tmpf->name, tmpf->instance);
+
+ tmpf->todel = B_TRUE;
+
+ /*
+ * check and update previous instance
+ * max.
+ */
+ if (tmpf->instance > maxi) {
+ maxi = tmpf->instance;
+ }
+
+ tmpf = tmpf->next;
+ } while (tmpf != NULL &&
+ strcmp(tmpf->name, flt->name) == 0);
+
+ /*
+ * use the max previous instance + 1 for
+ * the start of the new instance numbers.
+ */
+ newi = (uint32_t)++maxi % INT_MAX;
+
+ /*
+ * mark new instances for addition and
+ * give new instance number.
+ */
+ for (;;) {
+ IPQOSCDBG2(DIFF, "Marking filter "
+ "%s, instance %d as new\n",
+ flt->name, newi);
+
+ flt->new = B_TRUE;
+ flt->instance = newi++;
+ if (flt->next == NULL ||
+ strcmp(flt->next->name,
+ flt->name) != 0) {
+ break;
+ }
+ flt = flt->next;
+ }
+ new->modified = B_TRUE; /* mark new action */
+
+ /* mark old action */
+ old->modified = B_TRUE;
+
+ /* non-node name filter */
+ } else {
+ /* compare and mark as modified if diff */
+
+ res = difffilter(tmpf, flt, new->module);
+ if (res != IPQOS_CONF_SUCCESS) {
+ return (res);
+ }
+ if (flt->modified == B_TRUE) {
+ /* mark action if diff */
+ new->modified = B_TRUE;
+ }
+ }
+ }
+ }
+
+ /*
+ * Check for deleted ipqosconf created filters and mark
+ * any found for deletion.
+ * For non-ipqosconf generated filters, including permanent
+ * ones (none of these exist at the moment) we just leave
+ * the filter unmarked.
+ */
+ for (flt = old->filters; flt; flt = flt->next) {
+
+ if (flt->originator == IPP_CONFIG_IPQOSCONF &&
+ filterexist(flt->name, -1, new->filters) == NULL) {
+
+ /* mark all old instances for deletions */
+ for (;;) {
+ IPQOSCDBG2(DIFF, "marking flt %s, inst %d "
+ "for del\n", flt->name, flt->instance);
+
+ flt->todel = B_TRUE;
+ old->modified = B_TRUE; /* mark old action */
+
+ if (flt->next == NULL ||
+ strcmp(flt->next->name, flt->name) != 0) {
+ break;
+ }
+ flt = flt->next;
+ }
+ }
+ }
+
+ return (IPQOS_CONF_SUCCESS);
+}
+
+
+/*
+ * differences the elements of nvlists old and new using the types file
+ * for module name to interpret the element types. It sets pdiff to either
+ * 0 or 1 if they are the same or different respectively.
+ * RETURNS: IPQOS_CONF_ERR if any errors, else IPQOS_CONF_SUCCESS.
+ */
+static int
+diffnvlists(
+nvlist_t *old,
+nvlist_t *new,
+char *module_name,
+int *pdiff,
+place_t place)
+{
+
+ int first_pass = 1;
+ nvlist_t *tmp;
+ int res;
+ nvpair_t *nvp;
+ FILE *tfp;
+ str_val_nd_t *enum_nvs;
+ char dfltst[IPQOS_VALST_MAXLEN+1] = "";
+ char *lo;
+ ipqos_nvtype_t type;
+ char *nme;
+ int diff;
+ int openerr;
+
+
+ IPQOSCDBG0(L0, "In diffnvlists\n");
+
+ /* open stream to types file */
+
+ tfp = validmod(module_name, &openerr);
+ if (tfp == NULL) {
+ if (openerr) {
+ ipqos_msg(MT_ENOSTR, "fopen");
+ }
+ return (IPQOS_CONF_ERR);
+ }
+start:
+ /*
+ * loop through each of the elements of the new list comparing
+ * it with the old one if present. If the old one isn't present
+ * then it is compared with the default value for that type (if
+ * set). Any time the values are determined to be different
+ * or the default value is to be used but isn't present the diff
+ * param is set to 1 and we return.
+ *
+ * If the loop runs its course then the new and old nvlists are
+ * reversed and the loop is entered for a second time.
+ */
+ nvp = nvlist_next_nvpair(new, NULL);
+ while (nvp != NULL) {
+
+ /* get name */
+ nme = nvpair_name(nvp);
+
+ /*
+ * get type.
+ */
+ place = PL_ANY;
+ res = readtype(tfp, module_name, SHORT_NAME(nme), &type,
+ &enum_nvs, dfltst, B_TRUE, &place);
+ if (res != IPQOS_CONF_SUCCESS) {
+ return (res);
+ }
+
+ /* init diff to 1 */
+ diff = 1;
+
+ switch (type) {
+
+ /* interface name */
+ case IPQOS_DATA_TYPE_IFINDEX: {
+ uint32_t ifidx;
+ uint32_t oifidx;
+
+ /* get new value */
+ (void) nvpair_value_uint32(nvp, &ifidx);
+
+ /* compare against old if present */
+
+ res = nvlist_lookup_uint32(old, nme, &oifidx);
+ if (res == 0) {
+ /* diff values */
+ diff = (ifidx != oifidx);
+
+ /* not in old so see if new value is default */
+
+ } else {
+ diff = (ifidx != 0);
+ }
+ break;
+ }
+ /* protocol */
+ case IPQOS_DATA_TYPE_PROTO: {
+ uchar_t proto;
+ uchar_t oproto;
+
+ (void) nvpair_value_byte(nvp, &proto);
+
+ res = nvlist_lookup_byte(old, nme, &oproto);
+ if (res == 0) {
+ diff = (proto != oproto);
+ } else {
+ diff = (proto != 0);
+ }
+ break;
+ }
+ /* port */
+ case IPQOS_DATA_TYPE_PORT: {
+ uint16_t port;
+ uint16_t oport;
+
+ (void) nvpair_value_uint16(nvp, &port);
+ res = nvlist_lookup_uint16(old, nme, &oport);
+ if (res == 0) {
+ diff = (port != oport);
+ } else {
+ diff = (port != 0);
+ }
+ break;
+ }
+ /* action name / string */
+ case IPQOS_DATA_TYPE_ACTION:
+ case IPQOS_DATA_TYPE_STRING: {
+ char *str;
+ char *ostr;
+
+ (void) nvpair_value_string(nvp, &str);
+ res = nvlist_lookup_string(old, nme, &ostr);
+ if (res == 0) {
+ diff = strcmp(str, ostr);
+ } else if (*dfltst) {
+ diff = strcmp(str, dfltst);
+ }
+ break;
+ }
+ /* address mask / address */
+ case IPQOS_DATA_TYPE_ADDRESS_MASK:
+ case IPQOS_DATA_TYPE_ADDRESS: {
+ in6_addr_t *in6;
+ in6_addr_t *oin6;
+ uint_t x;
+
+ /*
+ * all addresses are stored as v6 addresses, so
+ * a uint32_t[4] array is used.
+ */
+
+ /* lookup new value */
+
+ (void) nvpair_value_uint32_array(nvp,
+ (uint32_t **)&in6, &x);
+
+ /* see if there's an old value and diff it */
+
+ res = nvlist_lookup_uint32_array(old, nme,
+ (uint32_t **)&oin6, &x);
+ if (res == 0) {
+ /* diff each of the 16 v6 address bytes */
+
+ for (x = 0; x < 16; x++) {
+ if (in6->s6_addr[x] !=
+ oin6->s6_addr[x]) {
+ diff++;
+ break;
+ }
+ }
+ }
+ break;
+ }
+ /* boolean */
+ case IPQOS_DATA_TYPE_BOOLEAN: {
+ boolean_t bl;
+ boolean_t obl;
+
+ (void) nvpair_value_uint32(nvp, (uint32_t *)&bl);
+
+ /* see if there's an old value and diff it */
+ res = nvlist_lookup_uint32(old, nme, (uint32_t *)&obl);
+ if (res == 0) {
+ diff = (bl != obl);
+
+ /* compare against default if present */
+ } else if (*dfltst) {
+ res = readbool(dfltst, &obl);
+ if (res == IPQOS_CONF_SUCCESS) {
+ diff = (bl != obl);
+ }
+ }
+ break;
+ }
+ /* uint 8 */
+ case IPQOS_DATA_TYPE_UINT8: {
+ uint8_t u8;
+ uint8_t ou8;
+
+ (void) nvpair_value_byte(nvp, (uchar_t *)&u8);
+ res = nvlist_lookup_byte(old, nme, (uchar_t *)&ou8);
+ if (res == 0) {
+ diff = (u8 != ou8);
+ } else if (*dfltst) {
+ res = readuint8(dfltst, &ou8, &lo);
+ if (res == IPQOS_CONF_SUCCESS) {
+ diff = (u8 != ou8);
+ }
+ }
+ break;
+ }
+ /* int 16 */
+ case IPQOS_DATA_TYPE_INT16: {
+ int16_t i16;
+ int16_t oi16;
+
+ (void) nvpair_value_int16(nvp, &i16);
+ res = nvlist_lookup_int16(old, nme, &oi16);
+ if (res == 0) {
+ diff = (i16 != oi16);
+ } else if (*dfltst) {
+ res = readint16(dfltst, &oi16, &lo);
+ if (res == IPQOS_CONF_SUCCESS) {
+ diff = (i16 != oi16);
+ }
+ }
+ break;
+ }
+ /* uint16 */
+ case IPQOS_DATA_TYPE_UINT16: {
+ uint16_t ui16;
+ uint16_t oui16;
+
+ (void) nvpair_value_uint16(nvp, &ui16);
+ res = nvlist_lookup_uint16(old, nme, &oui16);
+ if (res == 0) {
+ diff = (ui16 != oui16);
+ } else if (*dfltst) {
+ res = readuint16(dfltst, &oui16, &lo);
+ if (res == IPQOS_CONF_SUCCESS) {
+ diff = (ui16 != oui16);
+ }
+ }
+ break;
+ }
+ /*
+ * int32 and user.
+ * Since user uids are stored in an int32 nvpair we can use
+ * the same comparison code.
+ */
+ case IPQOS_DATA_TYPE_USER:
+ case IPQOS_DATA_TYPE_INT32: {
+ int32_t i32;
+ int32_t oi32;
+
+ (void) nvpair_value_int32(nvp, &i32);
+ res = nvlist_lookup_int32(old, nme, &oi32);
+ if (res == 0) {
+ diff = (i32 != oi32);
+ } else if (*dfltst) {
+ res = readint32(dfltst, &oi32, &lo);
+ if (res == IPQOS_CONF_SUCCESS) {
+ diff = (i32 != oi32);
+ }
+ }
+ break;
+ }
+ /* uint32 */
+ case IPQOS_DATA_TYPE_UINT32: {
+ uint32_t ui32;
+ uint32_t oui32;
+
+ (void) nvpair_value_uint32(nvp, &ui32);
+ res = nvlist_lookup_uint32(old, nme, &oui32);
+ if (res == 0) {
+ diff = (ui32 != oui32);
+ } else if (*dfltst) {
+ res = readuint32(dfltst, &oui32, &lo);
+ if (res == IPQOS_CONF_SUCCESS) {
+ diff = (ui32 != oui32);
+ }
+ }
+ break;
+ }
+ /* enumeration */
+ case IPQOS_DATA_TYPE_ENUM: {
+ uint32_t eval;
+ uint32_t oeval;
+
+ (void) nvpair_value_uint32(nvp, &eval);
+ res = nvlist_lookup_uint32(old, nme, &oeval);
+ if (res == 0) {
+ diff = (eval != oeval);
+ } else if (*dfltst) {
+ res = readuint32(dfltst, &oeval, &lo);
+ if (res == IPQOS_CONF_SUCCESS) {
+ diff = (eval != oeval);
+ }
+ }
+ break;
+ }
+ case IPQOS_DATA_TYPE_M_INDEX: {
+ uint8_t idx, oidx;
+
+ (void) nvpair_value_byte(nvp, &idx);
+ res = nvlist_lookup_byte(old, nme, &oidx);
+ if (res == 0)
+ diff = (idx != oidx);
+ break;
+ }
+ case IPQOS_DATA_TYPE_INT_ARRAY: {
+ int *oarr, *arr;
+ uint32_t osize, size;
+
+ (void) nvpair_value_int32_array(nvp, &arr, &size);
+ res = nvlist_lookup_int32_array(old, nme, &oarr,
+ &osize);
+ if (res == 0)
+ diff = (arrays_equal(arr, oarr, size) ==
+ B_FALSE);
+ break;
+ }
+#ifdef _IPQOS_CONF_DEBUG
+ default: {
+ /* shouldn't get here as all types should be covered */
+ assert(1);
+ }
+#endif
+ } /* switch */
+ if (diff != 0) {
+ IPQOSCDBG1(DIFF, "parameter %s different\n", nme);
+ *pdiff = 1;
+ (void) fclose(tfp);
+ return (IPQOS_CONF_SUCCESS);
+ }
+
+
+ nvp = nvlist_next_nvpair(new, nvp);
+
+ }
+
+ /* now compare all the stuff in the second list with the first */
+ if (first_pass) {
+ tmp = old;
+ old = new;
+ new = tmp;
+ first_pass = 0;
+ goto start;
+ }
+
+ (void) fclose(tfp);
+
+ *pdiff = 0;
+ return (IPQOS_CONF_SUCCESS);
+}
+
+
+
+/* ************************** difference application *********************** */
+
+
+
+/*
+ * causes all items marked as requiring change in actions and old_actions
+ * to have the change applied.
+ * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
+ */
+static int
+applydiff(
+ipqos_conf_action_t *actions,
+ipqos_conf_action_t *old_actions)
+{
+
+ int res;
+
+ IPQOSCDBG0(L1, "In applydiff:\n");
+
+
+ /* add each item marked as new */
+
+ res = add_items(actions, B_FALSE);
+ if (res != IPQOS_CONF_SUCCESS) {
+ return (res);
+ }
+
+ /* modify items marked for modification */
+
+ res = modify_items(actions);
+ if (res != IPQOS_CONF_SUCCESS) {
+ return (res);
+ }
+
+ /* delete items marked for deletion */
+
+ res = remove_items(old_actions, B_FALSE);
+ if (res != IPQOS_CONF_SUCCESS) {
+ return (res);
+ }
+
+ return (IPQOS_CONF_SUCCESS);
+}
+
+static int
+add_items(
+ipqos_conf_action_t *actions,
+boolean_t rem_undo)
+{
+
+ int res;
+ ipqos_conf_action_t *act;
+
+ IPQOSCDBG1(L1, "In add_items, rem_undo: %u\n", rem_undo);
+
+ /*
+ * we need to create ipgpc action before any others as some actions
+ * such as ftpcl which make calls to it depend on it being there on
+ * their creation.
+ */
+ act = actionexist(IPGPC_CLASSIFY, actions);
+ if (act &&
+ (rem_undo == B_FALSE && act->new == B_TRUE ||
+ rem_undo == B_TRUE && act->deleted == B_TRUE)) {
+
+ res = add_action(act);
+ if (res != IPQOS_CONF_SUCCESS) {
+ return (res);
+ }
+ }
+
+ /*
+ * loop though action list and add any actions marked as
+ * new/modified action and apply any additions there, then return.
+ */
+
+ for (act = actions; act; act = act->next) {
+ res = add_item(act, rem_undo);
+ if (res != IPQOS_CONF_SUCCESS) {
+ return (IPQOS_CONF_ERR);
+ }
+ }
+
+ return (IPQOS_CONF_SUCCESS);
+}
+
+
+/*
+ *
+ * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
+ */
+static int
+add_item(
+ipqos_conf_action_t *actions,
+boolean_t rem_undo)
+{
+
+ ipqos_conf_action_t *act = actions;
+ int res;
+ ipqos_conf_class_t *cls;
+ ipqos_conf_act_ref_t *pact;
+
+ IPQOSCDBG2(L1, "In add_item: action: %s, rem_undo: %u\n",
+ actions->name, rem_undo);
+
+ /* if already visited return immediately */
+
+ if (act->visited == ADD_VISITED) {
+ IPQOSCDBG0(L1, "Early exit due to visited\n");
+ return (IPQOS_CONF_SUCCESS);
+ }
+ act->visited = ADD_VISITED;
+
+
+ /* recurse to last action in tree */
+
+ for (cls = act->classes; cls; cls = cls->next) {
+
+ /* if not virtual action */
+
+ if (cls->alist->action) {
+ res = add_item(cls->alist->action, rem_undo);
+ if (res != IPQOS_CONF_SUCCESS) {
+ return (res);
+ }
+ }
+ }
+
+ for (pact = act->params->actions; pact; pact = pact->next) {
+
+ /* if not virtual */
+
+ if (pact->action) {
+ res = add_item(pact->action, rem_undo);
+ if (res != IPQOS_CONF_SUCCESS) {
+ return (res);
+ }
+ }
+ }
+
+
+ /* if action marked as new and not ipgpc, create */
+
+ if (((rem_undo == B_FALSE && act->new == B_TRUE) ||
+ (rem_undo == B_TRUE && act->deleted == B_TRUE)) &&
+ strcmp(act->name, IPGPC_CLASSIFY) != 0) {
+ res = add_action(act);
+ if (res != IPQOS_CONF_SUCCESS) {
+ return (res);
+ }
+ }
+
+ /* add any classes and filters marked as new */
+
+ if (add_classes(act->classes, act->name, act->module_version,
+ rem_undo) != IPQOS_CONF_SUCCESS ||
+ add_filters(act->filters, act->name, act->module_version,
+ rem_undo) != IPQOS_CONF_SUCCESS) {
+ return (IPQOS_CONF_ERR);
+ }
+
+ return (IPQOS_CONF_SUCCESS);
+}
+
+
+/*
+ * Uses the contents of acts params nvlist and adds an originator
+ * element set to ipqosconf and the stats parameter. This list
+ * is then used as the parameter to a call to ipp_action_create to create
+ * this action in the kernel.
+ * RETURNS: IPQOS_CONF_ERR on err, else IPQOS_CONF_SUCCESS.
+ */
+static int
+add_action(ipqos_conf_action_t *act)
+{
+
+ int res;
+ nvlist_t **nvl;
+
+ IPQOSCDBG2(APPLY, "add_action: action: %s, module: %s\n", act->name,
+ act->module);
+
+ nvl = &act->params->nvlist;
+
+ /* alloc params nvlist if not already one */
+
+ if (*nvl == NULL) {
+ res = nvlist_alloc(nvl, NV_UNIQUE_NAME, 0);
+ if (res != 0) {
+ ipqos_msg(MT_ENOSTR, "nvlist_alloc");
+ return (IPQOS_CONF_ERR);
+ }
+ }
+
+ /*
+ * add module version
+ */
+ if (nvlist_add_uint32(*nvl, IPP_MODULE_VERSION,
+ (uint32_t)act->module_version) != 0) {
+ ipqos_msg(MT_ENOSTR, "nvlist_add_uint32");
+ return (IPQOS_CONF_ERR);
+ }
+
+ /* add action stats */
+
+ if (nvlist_add_uint32(*nvl, IPP_ACTION_STATS_ENABLE,
+ (uint32_t)act->params->stats_enable) != 0) {
+ ipqos_msg(MT_ENOSTR, "nvlist_add_uint32: action stats");
+ return (IPQOS_CONF_ERR);
+ }
+
+ /* add ipqosconf originator id */
+
+ if (add_orig_ipqosconf(*nvl) != IPQOS_CONF_SUCCESS) {
+ return (IPQOS_CONF_ERR);
+ }
+
+ /* call into lib to create action */
+
+ res = ipp_action_create(act->module, act->name, nvl, 0);
+ if (res != 0) {
+ IPQOSCDBG2(APPLY, "Create action %s, module %s failed\n",
+ act->name, act->module);
+
+ /* invalid params */
+
+ if (errno == EINVAL) {
+ ipqos_msg(MT_ERROR,
+ gettext("Invalid Parameters for action %s.\n"),
+ act->name);
+
+ } else if (errno == ENOENT) {
+ ipqos_msg(MT_ERROR,
+ gettext("Missing required parameter for action "
+ "%s.\n"), act->name);
+
+ } else { /* unexpected error */
+ ipqos_msg(MT_ERROR, gettext("Failed to create action "
+ "%s: %s.\n"), act->name, strerror(errno));
+ }
+
+ return (IPQOS_CONF_ERR);
+ }
+
+ /* mark action as created */
+ act->cr_mod = B_TRUE;
+
+ return (IPQOS_CONF_SUCCESS);
+}
+
+/*
+ * for each of the filters in parameter filters if rem_undo is false and
+ * the filter is marked as new or if rem_undo is true and the filter is
+ * marked as deleted then add the filter to the kernel action named by action
+ * and if successful mark as created.
+ * RETURNS: IPQOS_CONF_ERR on errors, else IPQOS_CONF_SUCCESS.
+ */
+static int
+add_filters(
+ipqos_conf_filter_t *filters,
+char *action,
+int module_version,
+boolean_t rem_undo)
+{
+
+ ipqos_conf_filter_t *flt;
+
+ IPQOSCDBG0(L1, "In add_filters\n");
+
+ /* loop through filters in filters param */
+ for (flt = filters; flt; flt = flt->next) {
+ /*
+ * skip filter if in normal mode and not new filter or
+ * if doing rollback and filter wasn't previously deleted.
+ */
+ if ((rem_undo == B_FALSE && flt->new == B_FALSE) ||
+ (rem_undo == B_TRUE && flt->deleted == B_FALSE)) {
+ continue;
+ }
+
+ /* add filter to action */
+ if (add_filter(action, flt, module_version) !=
+ IPQOS_CONF_SUCCESS) {
+ return (IPQOS_CONF_ERR);
+ }
+
+ /* mark as created */
+ flt->cr_mod = B_TRUE;
+ }
+
+ return (IPQOS_CONF_SUCCESS);
+}
+
+/*
+ * for each of the classes in parameter classes if rem_undo is false and
+ * the class is marked as new or if rem_undo is true and the class is
+ * marked as deleted then add the class to the kernel action named by action
+ * and if successful mark as created.
+ * RETURNS: IPQOS_CONF_ERR on errors, else IPQOS_CONF_SUCCESS.
+ */
+int
+add_classes(
+ipqos_conf_class_t *classes,
+char *action,
+int module_version,
+boolean_t rem_undo) {
+
+ int res;
+ ipqos_conf_class_t *cls;
+
+ IPQOSCDBG0(L1, "In add_classes\n");
+
+ /* for each class */
+ for (cls = classes; cls; cls = cls->next) {
+ /*
+ * skip class if in normal mode and not new class or
+ * if doing rollback and class wasn't deleted.
+ */
+ if ((rem_undo == B_FALSE && cls->new == B_FALSE) ||
+ (rem_undo == B_TRUE && cls->deleted == B_FALSE)) {
+ continue;
+ }
+
+ /* add class to action */
+ res = add_class(action, cls->name, module_version,
+ cls->stats_enable, cls->alist->name);
+ if (res != IPQOS_CONF_SUCCESS) {
+ return (IPQOS_CONF_ERR);
+ }
+
+ /* mark class as created */
+ cls->cr_mod = B_TRUE;
+ }
+
+ return (IPQOS_CONF_SUCCESS);
+}
+
+/*
+ * For each of the actions in actions remove the action if marked as
+ * such or remove any objects within marked as such.
+ * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
+ */
+static int
+remove_items(
+ipqos_conf_action_t *actions,
+boolean_t add_undo)
+{
+
+ int res;
+ ipqos_conf_action_t *act;
+
+ IPQOSCDBG1(L0, "In remove_items, add_undo: %u\n", add_undo);
+
+ /*
+ * loop through actions removing any actions, or action contents
+ * that are marked as such.
+ */
+ for (act = actions; act; act = act->next) {
+ res = remove_item(act, add_undo);
+ if (res != IPQOS_CONF_SUCCESS) {
+ return (res);
+ }
+ }
+
+ return (IPQOS_CONF_SUCCESS);
+}
+
+/*
+ * Deletes this action if marked for deletion or any of it's contents marked
+ * for deletion. If the action is marked for deletion any actions referencing
+ * this action are destroyed first if marked or have their contents destroyed
+ * if marked. This is recursive.
+ * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
+ */
+static int
+remove_item(
+ipqos_conf_action_t *act,
+boolean_t add_undo)
+{
+
+ ipqos_conf_class_t *cls;
+ ipqos_conf_filter_t *flt;
+ ipqos_conf_act_ref_t *dep;
+ int res;
+
+ IPQOSCDBG3(L1, "In remove_item: action: %s, add_undo: %u, mod: %u\n",
+ act->name, add_undo, act->modified);
+
+
+ /* return immmediately if previously visited in remove phase */
+
+ if (act->visited == REM_VISITED) {
+ IPQOSCDBG0(L1, "Exit due to REM_VISITED set\n");
+ return (IPQOS_CONF_SUCCESS);
+ }
+ act->visited = REM_VISITED;
+
+
+ /* if this action is to be deleted */
+
+ if (add_undo == B_FALSE && act->todel == B_TRUE ||
+ add_undo == B_TRUE && act->new == B_TRUE &&
+ act->cr_mod == B_TRUE) {
+
+ /* modify parent actions first */
+
+ for (dep = act->dependencies; dep; dep = dep->next) {
+ res = remove_item(dep->action, add_undo);
+ if (res != IPQOS_CONF_SUCCESS) {
+ return (res);
+ }
+ }
+
+ /* delete this action */
+
+ IPQOSCDBG1(APPLY, "deleting action %s\n", act->name);
+ res = ipp_action_destroy(act->name, 0);
+ if (res != 0) {
+ IPQOSCDBG1(APPLY, "failed to destroy action %s\n",
+ act->name);
+ return (IPQOS_CONF_ERR);
+ }
+
+ /* flag as deleted */
+
+ act->deleted = B_TRUE;
+
+ /* if modified action */
+
+ } else if (act->modified == B_TRUE) {
+
+ /* loop through removing any filters marked for del */
+
+ for (flt = act->filters; flt; flt = flt->next) {
+ if ((add_undo == B_FALSE && flt->todel == B_TRUE) ||
+ (add_undo == B_TRUE && flt->new == B_TRUE &&
+ flt->cr_mod == B_TRUE)) {
+
+ /* do deletion */
+
+ res = remove_filter(act->name, flt->name,
+ flt->instance, act->module_version);
+ if (res != IPQOS_CONF_SUCCESS) {
+ IPQOSCDBG2(APPLY, "failed to destroy "
+ "filter %s, inst: %d\n", flt->name,
+ flt->instance);
+
+ return (IPQOS_CONF_ERR);
+ }
+
+ /* flag deleted */
+
+ flt->deleted = B_TRUE;
+ }
+ }
+
+ /* remove any classes marked for del */
+
+ for (cls = act->classes; cls; cls = cls->next) {
+ if ((add_undo == B_FALSE && cls->todel == B_TRUE) ||
+ (add_undo == B_TRUE && cls->new == B_TRUE &&
+ cls->cr_mod == B_TRUE)) {
+
+ /* do deletion */
+
+ res = remove_class(act->name, cls->name,
+ act->module_version, 0);
+ if (res != IPQOS_CONF_SUCCESS) {
+ IPQOSCDBG1(APPLY, "failed to destroy "
+ "class %s\n", cls->name);
+
+ return (IPQOS_CONF_ERR);
+ }
+
+ /* flag deleted */
+
+ cls->deleted = B_TRUE;
+ }
+ }
+
+ /* mark action as having been modified */
+
+ act->cr_mod = B_TRUE;
+ }
+
+ return (IPQOS_CONF_SUCCESS);
+}
+
+/*
+ * for each of the actions in parameter actions apply any objects marked as
+ * modified as a modification to the kernel action represented.
+ * RETURNS: IPQOS_CONF_ERR on err, else IPQOS_CONF_SUCCESS.
+ */
+static int
+modify_items(ipqos_conf_action_t *actions)
+{
+
+ ipqos_conf_action_t *act;
+ int res;
+ ipqos_conf_filter_t *flt;
+ ipqos_conf_class_t *cls;
+
+
+ IPQOSCDBG0(L1, "In modify_items\n");
+
+ /* loop through actions in parameter actions */
+
+ for (act = actions; act; act = act->next) {
+
+ /* skip unchanged actions */
+
+ if (act->modified == B_FALSE) {
+ continue;
+ }
+
+ /* apply any parameter mods */
+
+ if (act->params->modified) {
+ res = modify_params(act->name,
+ &act->params->nvlist,
+ act->module_version, act->params->stats_enable);
+ if (res != IPQOS_CONF_SUCCESS) {
+ return (IPQOS_CONF_ERR);
+ }
+
+ act->params->cr_mod = B_TRUE;
+ }
+
+ /* apply any class mods */
+
+ for (cls = act->classes; cls; cls = cls->next) {
+ if (cls->modified) {
+ res = modify_class(act->name, cls->name,
+ act->module_version, cls->stats_enable,
+ cls->alist->name, 0);
+ if (res != IPQOS_CONF_SUCCESS) {
+ return (IPQOS_CONF_ERR);
+ }
+
+ /* mark modification done */
+ cls->cr_mod = B_TRUE;
+ }
+ }
+
+ /* apply any filter mods */
+
+ for (flt = act->filters; flt; flt = flt->next) {
+ if (flt->modified) {
+ res = modify_filter(act->name, flt,
+ act->module_version);
+ if (res != 0) {
+ return (IPQOS_CONF_ERR);
+ }
+
+ /* mark modification done */
+ flt->cr_mod = B_TRUE;
+ }
+ }
+
+ /* mark action modified */
+
+ act->cr_mod = B_TRUE;
+ }
+
+ return (IPQOS_CONF_SUCCESS);
+}
+
+/*
+ * For each of the objects of each of the actions in nactions that are
+ * marked as having been modified the object modification is done in
+ * reverse using the same named object from oactions.
+ * RETURNS: IPQOS_CONF_ERR on error, IPQOS_CONF_SUCCESS otherwise.
+ */
+static int
+undo_modifys(
+ipqos_conf_action_t *oactions,
+ipqos_conf_action_t *nactions)
+{
+
+ ipqos_conf_filter_t *flt;
+ ipqos_conf_class_t *cls;
+ ipqos_conf_action_t *act;
+ ipqos_conf_action_t *oldact;
+ ipqos_conf_filter_t *oldflt;
+ ipqos_conf_class_t *oldcls;
+ int res;
+
+ IPQOSCDBG0(L1, "In undo_modifys:\n");
+
+ /* loop throught new actions */
+
+ for (act = nactions; act; act = act->next) {
+ oldact = actionexist(act->name, oactions);
+
+ /*
+ * if the action was new then it will be removed and
+ * any permamanent items that were marked for modify
+ * will dissappear, so ignore action.
+ */
+ if (oldact == NULL) {
+ continue;
+ }
+
+ /* if parameters were modified switch them back */
+
+ if (act->params->modified == B_TRUE &&
+ act->params->cr_mod == B_TRUE) {
+ res = modify_params(act->name,
+ &oldact->params->nvlist,
+ act->module_version, act->params->stats_enable);
+ if (res != IPQOS_CONF_SUCCESS) {
+ return (res);
+ }
+ }
+
+ /* for each filter in action if filter modified switch back */
+
+ for (flt = act->filters; flt; flt = flt->next) {
+ if (flt->modified == B_TRUE &&
+ flt->cr_mod == B_TRUE) {
+ oldflt = filterexist(flt->name, -1,
+ oldact->filters);
+ res = modify_filter(act->name, oldflt,
+ act->module_version);
+ if (res != IPQOS_CONF_SUCCESS) {
+ return (res);
+ }
+ }
+ }
+
+ /* for each class in action if class modified switch back */
+
+ for (cls = act->classes; cls; cls = cls->next) {
+ if (cls->modified == B_TRUE &&
+ cls->cr_mod == B_TRUE) {
+ oldcls = classexist(cls->name, oldact->classes);
+ if (oldcls->alist) {
+ res = modify_class(act->name,
+ cls->name, act->module_version,
+ oldcls->stats_enable,
+ oldcls->alist->name, 0);
+ }
+ if (res != IPQOS_CONF_SUCCESS) {
+ return (res);
+ }
+ }
+ }
+ }
+
+ /*
+ * Go through the old actions modifying perm filters and classes
+ * whose action was deleted.
+ *
+ */
+ for (act = oactions; act != NULL; act = act->next) {
+
+ if (act->deleted == B_FALSE) {
+ continue;
+ }
+
+ for (flt = act->filters; flt != NULL; flt = flt->next) {
+ if (flt->originator == IPP_CONFIG_PERMANENT) {
+ res = modify_filter(act->name, flt,
+ act->module_version);
+ if (res != IPQOS_CONF_SUCCESS) {
+ return (res);
+ }
+ }
+ }
+
+ for (cls = act->classes; cls != NULL; cls = cls->next) {
+ if (cls->originator == IPP_CONFIG_PERMANENT) {
+ res = modify_class(act->name, cls->name,
+ act->module_version, cls->stats_enable,
+ cls->alist->name, 0);
+ if (res != IPQOS_CONF_SUCCESS) {
+ return (res);
+ }
+ }
+
+ }
+ }
+
+ return (IPQOS_CONF_SUCCESS);
+}
+
+
+/*
+ * causes all changes marked as being done in actions and old_actions
+ * to be undone.
+ * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
+ */
+static int
+rollback(
+ipqos_conf_action_t *actions,
+ipqos_conf_action_t *old_actions)
+{
+
+ int res;
+
+ IPQOSCDBG0(RBK, "In rollback:\n");
+
+ /* re-add items that were deleted */
+
+ res = add_items(old_actions, B_TRUE);
+ if (res != IPQOS_CONF_SUCCESS) {
+ return (res);
+ }
+
+ /* change modified items back how they were */
+
+ res = undo_modifys(old_actions, actions);
+ if (res != IPQOS_CONF_SUCCESS) {
+ return (res);
+ }
+
+ /* remove new items that were added */
+
+ res = remove_items(actions, B_TRUE);
+ if (res != IPQOS_CONF_SUCCESS) {
+ return (res);
+ }
+
+ return (IPQOS_CONF_SUCCESS);
+}
+
+/* ******************************* print config **************************** */
+
+/*
+ * Prints the username of the user with uid 'uid' to 'fp' if the uid belongs
+ * to a known user on the system, otherwise just print 'uid'.
+ */
+static void
+printuser(
+FILE *fp,
+uid_t uid)
+{
+ struct passwd *pwd;
+
+ IPQOSCDBG0(L0, "In printuser\n");
+
+ pwd = getpwuid(uid);
+ if (pwd != NULL) {
+ (void) fprintf(fp, "%s\n", pwd->pw_name);
+ } else {
+ (void) fprintf(fp, "%u\n", (int)uid);
+ }
+}
+
+/*
+ * print either a single value of start to fp (if start equals end), else
+ * print start'-'end if start is the smaller of the two values, otherwise
+ * print end'-'start.
+ */
+static void
+printrange(
+FILE *fp,
+uint32_t start,
+uint32_t end)
+{
+ uint32_t tmp;
+
+ if (start > end) {
+ tmp = start;
+ start = end;
+ end = tmp;
+ }
+
+ (void) fprintf(fp, "%u", start);
+ if (end != start)
+ (void) fprintf(fp, "-%u", end);
+}
+
+/*
+ * print the contents of the array arr to fp in the form:
+ * {0-6:1;7-12:2;13:3.....} or {0-6:GREEN;7-12:YELLOW:...}
+ * dependant upon whether this is an integer or enumerated array resectively
+ * (if enum_nvs isn't set to NULL this is assumed to be an enumerated array);
+ * where 0-6 is the range of indexes with value 1 (or GREEN), 7-12 the range
+ * with value 2 (or YELLOW), and so forth. size is the array size and llimit
+ * and ulimit are the lower and upper limits of the array values printed
+ * respectively. For enumerated arrays enum_nvs carries the list of name
+ * and value pairs and ulimit and llimit parameters are ignored and instead
+ * determined from the enum_nvs list.
+ */
+static void
+print_int_array(
+FILE *fp,
+int arr[],
+uint32_t size,
+int llimit,
+int ulimit,
+str_val_nd_t *enum_nvs,
+int tab_inserts)
+{
+ int x, y;
+ uint32_t first, last;
+ boolean_t first_entry; /* first 'ranges:value' to be printed ? */
+ boolean_t first_range; /* first range for a value to be printed ? */
+ boolean_t found_range; /* did we find a range for this value ? */
+
+ IPQOSCDBG4(L0, "In print_int_array: size: %u, llimit: %u, ulimit: %u, "
+ "enum_nvs: %x \n", size, llimit, ulimit, enum_nvs);
+
+ /*
+ * if an enumeration retrieve value range.
+ */
+ if (enum_nvs != NULL)
+ get_str_val_value_range(enum_nvs, &llimit, &ulimit);
+
+ /*
+ * print opening curl.
+ */
+ (void) fprintf(fp, "%c\n", CURL_BEGIN);
+ PRINT_TABS(fp, tab_inserts + 1);
+
+ first_entry = B_TRUE;
+ /*
+ * for each value in range.
+ */
+ for (x = llimit; x <= ulimit; x++) {
+ found_range = B_FALSE;
+ first_range = B_TRUE;
+ y = 0;
+ /*
+ * scan array and print ranges of indexes with value x.
+ */
+ while (y < size) {
+ /*
+ * get first occurence of value for this range.
+ */
+ while ((arr[y] != x) && (y < size))
+ y++;
+ if (y == size) {
+ break;
+ } else {
+ found_range = B_TRUE;
+ }
+ first = y;
+
+ /*
+ * get last occurence of value for this range.
+ */
+ while ((arr[y] == x) && (y < size))
+ y++;
+ last = y - 1;
+
+ /*
+ * print entry delimiter (semi-colon)? It must be
+ * the first range for this value and this mustn't
+ * be the first 'ranges:value' entry.
+ */
+ if (!first_entry && first_range) {
+ (void) fprintf(fp, ";\n");
+ PRINT_TABS(fp, tab_inserts + 1);
+ } else {
+ first_entry = B_FALSE;
+ }
+
+ /*
+ * print comma (range delimeter) only if there was
+ * a previous range for this value.
+ */
+ if (!first_range) {
+ (void) fprintf(fp, ",");
+ } else {
+ first_range = B_FALSE;
+ }
+
+ /*
+ * print range.
+ */
+ printrange(fp, first, last);
+ }
+ /*
+ * only print a colon and value if we found a range with
+ * this value.
+ */
+ if (found_range) {
+ (void) fprintf(fp, ":");
+
+ /*
+ * print numeric/symbolic value.
+ */
+ if (enum_nvs) {
+ printenum(fp, x, enum_nvs);
+ } else {
+ (void) fprintf(fp, "%d", x);
+ }
+ }
+ }
+
+ /*
+ * print closing curl.
+ */
+ (void) fprintf(fp, "\n");
+ PRINT_TABS(fp, tab_inserts);
+ (void) fprintf(fp, "%c\n", CURL_END);
+}
+
+/* print the protocol name for proto, or if unknown protocol number proto. */
+static void
+printproto(
+FILE *fp,
+uint8_t proto)
+{
+
+ struct protoent *pent;
+
+ pent = getprotobynumber(proto);
+ if (pent != NULL) {
+ (void) fprintf(fp, "%s\n", pent->p_name);
+ } else {
+ (void) fprintf(fp, "%u\n", proto);
+ }
+}
+
+/*
+ * prints the name associated with interface with index ifindex to fp.
+ * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
+ */
+static int
+printifname(
+FILE *fp,
+int ifindex)
+{
+
+ int s;
+ struct lifconf lc;
+ struct lifnum ln;
+ struct lifreq *lr;
+ char *buf;
+ int len;
+ char *cp;
+ int ret;
+ int x;
+ int idx;
+
+ /* open socket */
+
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
+ ipqos_msg(MT_ENOSTR, gettext("opening AF_INET socket"));
+ return (IPQOS_CONF_ERR);
+ }
+
+ /* get number of lifreq structs that need to be alloc'd for */
+
+ ln.lifn_family = AF_UNSPEC;
+ ln.lifn_flags = 0;
+ ret = ioctl(s, SIOCGLIFNUM, &ln);
+ if (ret < 0) {
+ ipqos_msg(MT_ENOSTR, "SIOCLIFNUM ioctl");
+ (void) close(s);
+ return (IPQOS_CONF_ERR);
+ }
+
+ /* allocate buffer for SIOGLIFCONF ioctl */
+
+ len = ln.lifn_count * sizeof (struct lifreq);
+ buf = malloc(len);
+ if (buf == NULL) {
+ ipqos_msg(MT_ENOSTR, "malloc");
+ (void) close(s);
+ return (IPQOS_CONF_ERR);
+ }
+
+ /* setup lifconf params for ioctl */
+
+ lc.lifc_family = AF_UNSPEC;
+ lc.lifc_flags = 0;
+ lc.lifc_len = len;
+ lc.lifc_buf = buf;
+
+ /* do SIOCGLIFCONF ioctl */
+
+ ret = ioctl(s, SIOCGLIFCONF, &lc);
+ if (ret < 0) {
+ ipqos_msg(MT_ENOSTR, "SIGLIFCONF");
+ (void) close(s);
+ free(buf);
+ return (IPQOS_CONF_ERR);
+ }
+ (void) close(s);
+
+ /*
+ * for each interface name given in the returned lifreq list get
+ * it's index and compare with ifindex param. Break if equal.
+ */
+ for (x = ln.lifn_count, lr = lc.lifc_req; x > 0; x--, lr++) {
+ ret = readifindex(lr->lifr_name, &idx);
+ if (ret != IPQOS_CONF_SUCCESS) {
+ free(buf);
+ return (IPQOS_CONF_ERR);
+ }
+ if (idx == ifindex) {
+ break;
+ }
+ }
+ free(buf);
+
+ if (x == 0) {
+ IPQOSCDBG1(L1, "Failed to find if index %u in returned "
+ "if list.\n", ifindex);
+ return (IPQOS_CONF_ERR);
+ }
+ /* truncate any logical suffix */
+
+ if ((cp = strchr(lr->lifr_name, '@')) != NULL) {
+ *cp = NULL;
+ }
+
+ /* print interface name */
+ (void) fprintf(fp, "%s\n", lr->lifr_name);
+
+ return (IPQOS_CONF_SUCCESS);
+}
+
+/*
+ * print to fp the enumeration clause evaluating to the value val using the
+ * names/values given in enum_nvs.
+ */
+static void
+printenum(
+FILE *fp,
+uint32_t val,
+str_val_nd_t *enum_nvs)
+{
+
+ boolean_t isfirstval = B_TRUE;
+ str_val_nd_t *name_val = enum_nvs;
+
+ /* for each value in enum_nvs if same bit set in val print name */
+
+ while (name_val) {
+ if ((name_val->sv.value & val) == name_val->sv.value) {
+ if (isfirstval == B_TRUE) {
+ (void) fprintf(fp, "%s", name_val->sv.string);
+ isfirstval = B_FALSE;
+ } else {
+ (void) fprintf(fp, ", %s", name_val->sv.string);
+ }
+ }
+ name_val = name_val->next;
+ }
+}
+
+
+/* prints the service name of port, or if unknown the number to fp. */
+static void
+printport(
+FILE *fp,
+uint16_t port)
+{
+
+ struct servent *sent;
+
+ sent = getservbyport(port, NULL);
+ if (sent != NULL) {
+ (void) fprintf(fp, "%s\n", sent->s_name);
+ } else {
+ (void) fprintf(fp, "%u\n", ntohs(port));
+ }
+}
+
+/*
+ * prints tp fp the name and value of all user specifiable parameters in the
+ * nvlist.
+ * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
+ */
+static int
+printnvlist(
+FILE *fp,
+char *module,
+nvlist_t *nvl,
+int printall, /* are we want ip addresses printing if node name */
+ipqos_conf_filter_t *flt, /* used to determine if node name set */
+int tab_inserts,
+place_t place)
+{
+ FILE *tfp;
+ nvpair_t *nvp;
+ char *name;
+ ipqos_nvtype_t type;
+ str_val_nd_t *enum_nvs;
+ int ret;
+ char dfltst[IPQOS_VALST_MAXLEN+1];
+ char *param;
+ int openerr;
+ int res;
+
+ IPQOSCDBG0(L1, "In printnvlist\n");
+
+
+ /* open stream to types file */
+
+ tfp = validmod(module, &openerr);
+ if (tfp == NULL) {
+ if (openerr) {
+ ipqos_msg(MT_ENOSTR, "fopen");
+ }
+ return (IPQOS_CONF_ERR);
+ }
+
+
+ /* go through list getting param name and type and printing it */
+
+ nvp = nvlist_next_nvpair(nvl, NULL);
+ while (nvp) {
+
+ /* get nvpair name */
+ name = nvpair_name(nvp);
+ IPQOSCDBG1(L0, "processing element %s.\n", name);
+
+ /* skip ipgpc params that are not explicitly user settable */
+
+ if (strcmp(name, IPGPC_FILTER_TYPE) == 0 ||
+ strcmp(name, IPGPC_SADDR_MASK) == 0 ||
+ strcmp(name, IPGPC_DADDR_MASK) == 0 ||
+ strcmp(name, IPGPC_SPORT_MASK) == 0 ||
+ strcmp(name, IPGPC_DPORT_MASK) == 0) {
+ nvp = nvlist_next_nvpair(nvl, nvp);
+ continue;
+ }
+
+ param = SHORT_NAME(name);
+
+ /*
+ * get parameter type from types file.
+ */
+ place = PL_ANY;
+ ret = readtype(tfp, module, param, &type, &enum_nvs, dfltst,
+ B_TRUE, &place);
+ if (ret != IPQOS_CONF_SUCCESS) {
+ return (ret);
+ }
+
+ /*
+ * for map entries we don't print the map value, only
+ * the index value it was derived from.
+ */
+ if (place == PL_MAP) {
+ nvp = nvlist_next_nvpair(nvl, nvp);
+ continue;
+ }
+
+ /*
+ * the ifindex is converted to the name and printed out
+ * so print the parameter name as ifname.
+ */
+ if (strcmp(name, IPGPC_IF_INDEX) == 0) {
+ PRINT_TABS(fp, tab_inserts);
+ (void) fprintf(fp, "%s ", IPQOS_IFNAME_STR);
+ /*
+ * we may not print the address due to us instead printing
+ * the node name in printfilter, therefore we leave the
+ * printing of the parameter in the addresses switch case code.
+ */
+ } else if ((strcmp(name, IPGPC_SADDR) != 0 &&
+ strcmp(name, IPGPC_DADDR) != 0)) {
+ PRINT_TABS(fp, tab_inserts);
+ (void) fprintf(fp, "%s ", param);
+ }
+
+ switch (type) {
+ case IPQOS_DATA_TYPE_IFINDEX: {
+ uint32_t ifidx;
+
+ (void) nvpair_value_uint32(nvp, &ifidx);
+ (void) printifname(fp, ifidx);
+ break;
+ }
+ case IPQOS_DATA_TYPE_BOOLEAN: {
+ boolean_t bl;
+
+ (void) nvpair_value_uint32(nvp,
+ (uint32_t *)&bl);
+ (void) fprintf(fp, "%s\n",
+ bl == B_TRUE ? "true" : "false");
+ break;
+ }
+ case IPQOS_DATA_TYPE_ACTION: {
+ char *strval;
+
+ (void) nvpair_value_string(nvp, &strval);
+ print_action_nm(fp, strval);
+ break;
+ }
+ case IPQOS_DATA_TYPE_STRING: {
+ char *strval;
+
+ (void) nvpair_value_string(nvp, &strval);
+ (void) fprintf(fp, "%s\n",
+ quote_ws_string(strval));
+ break;
+ }
+ case IPQOS_DATA_TYPE_ADDRESS: {
+ uint_t tmp;
+ in6_addr_t *addr;
+ char addrstr[INET6_ADDRSTRLEN];
+ uchar_t ftype;
+ int af;
+ in6_addr_t *mask;
+
+ /*
+ * skip addresses that have node names for
+ * non printall listings.
+ */
+ if (printall == 0 &&
+ (strcmp(nvpair_name(nvp), IPGPC_SADDR) ==
+ 0 && flt->src_nd_name ||
+ strcmp(nvpair_name(nvp), IPGPC_DADDR) ==
+ 0 && flt->dst_nd_name)) {
+ break;
+ }
+
+ /* we skipped this above */
+
+ PRINT_TABS(fp, tab_inserts);
+ (void) fprintf(fp, "%s ", param);
+
+ (void) nvpair_value_uint32_array(nvp,
+ (uint32_t **)&addr, &tmp);
+
+ /* get filter type */
+
+ (void) nvlist_lookup_byte(nvl,
+ IPGPC_FILTER_TYPE, &ftype);
+ if (ftype == IPGPC_V4_FLTR) {
+ af = AF_INET;
+ addr = (in6_addr_t *)
+ &V4_PART_OF_V6((*addr));
+ } else {
+ af = AF_INET6;
+ }
+ /* get mask */
+
+ if (strcmp(nvpair_name(nvp), IPGPC_SADDR) ==
+ 0) {
+ ret = nvlist_lookup_uint32_array(nvl,
+ IPGPC_SADDR_MASK,
+ (uint32_t **)&mask, &tmp);
+ } else {
+ ret = nvlist_lookup_uint32_array(nvl,
+ IPGPC_DADDR_MASK,
+ (uint32_t **)&mask, &tmp);
+ }
+
+ /* print address/mask to fp */
+
+ (void) fprintf(fp, "%s/%u\n",
+ inet_ntop(af, addr, addrstr,
+ INET6_ADDRSTRLEN), masktocidr(af, mask));
+ break;
+ }
+ case IPQOS_DATA_TYPE_ENUM: {
+ uint32_t val;
+
+ (void) nvpair_value_uint32(nvp, &val);
+
+ /*
+ * print list of tokens resulting in val
+ */
+ (void) fprintf(fp, "{ ");
+ printenum(fp, val, enum_nvs);
+ (void) fprintf(fp, " }\n");
+ break;
+ }
+ case IPQOS_DATA_TYPE_PORT: {
+ uint16_t port;
+
+ (void) nvpair_value_uint16(nvp, &port);
+ printport(fp, port);
+ break;
+ }
+ case IPQOS_DATA_TYPE_PROTO: {
+ uint8_t proto;
+
+ (void) nvpair_value_byte(nvp, &proto);
+ printproto(fp, proto);
+ break;
+ }
+ case IPQOS_DATA_TYPE_M_INDEX:
+ case IPQOS_DATA_TYPE_UINT8: {
+ uchar_t u8;
+
+ (void) nvpair_value_byte(nvp, &u8);
+ (void) fprintf(fp, "%u\n", u8);
+ break;
+ }
+ case IPQOS_DATA_TYPE_UINT16: {
+ uint16_t u16;
+
+ (void) nvpair_value_uint16(nvp, &u16);
+ (void) fprintf(fp, "%u\n", u16);
+ break;
+ }
+ case IPQOS_DATA_TYPE_INT16: {
+ int16_t i16;
+
+ (void) nvpair_value_int16(nvp, &i16);
+ (void) fprintf(fp, "%d\n", i16);
+ break;
+ }
+ case IPQOS_DATA_TYPE_UINT32: {
+ uint32_t u32;
+
+ (void) nvpair_value_uint32(nvp, &u32);
+ (void) fprintf(fp, "%u\n", u32);
+ break;
+ }
+ case IPQOS_DATA_TYPE_INT32: {
+ int i32;
+
+ (void) nvpair_value_int32(nvp, &i32);
+ (void) fprintf(fp, "%d\n", i32);
+ break;
+ }
+ case IPQOS_DATA_TYPE_INT_ARRAY: {
+ str_val_nd_t *arr_enum_nvs = NULL;
+ uint32_t size;
+ int llimit, ulimit;
+ int *arr;
+
+ (void) nvpair_value_int32_array(nvp, &arr,
+ &size);
+
+ /*
+ * read array info from types file.
+ */
+ res = read_int_array_info(dfltst,
+ &arr_enum_nvs, &size, &llimit, &ulimit,
+ module);
+
+ /*
+ * print array with numbers, or symbols
+ * if enumerated.
+ */
+ if (res == IPQOS_CONF_SUCCESS) {
+ print_int_array(fp, arr, size,
+ llimit, ulimit, arr_enum_nvs,
+ tab_inserts);
+ if (arr_enum_nvs != NULL) {
+ free_str_val_entrys(
+ arr_enum_nvs);
+ }
+ }
+ break;
+ }
+ case IPQOS_DATA_TYPE_USER: {
+ uid_t uid;
+
+ (void) nvpair_value_int32(nvp, (int *)&uid);
+ printuser(fp, uid);
+ break;
+ }
+#ifdef _IPQOS_CONF_DEBUG
+ default: {
+ /*
+ * we should have catered for all used data
+ * types that readtype returns.
+ */
+ assert(1);
+ }
+#endif
+ }
+
+ nvp = nvlist_next_nvpair(nvl, nvp);
+ }
+
+ (void) fclose(tfp);
+ return (IPQOS_CONF_SUCCESS);
+}
+
+/*
+ * print a parameter clause for the parmeters given in params to fp.
+ * If printall is set, then the originator of the parameter object is printed.
+ * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
+ */
+static int
+printparams(
+FILE *fp,
+char *module,
+ipqos_conf_params_t *params,
+int printall,
+int tab_inserts)
+{
+
+ int res;
+
+ /* print opening clause */
+
+ PRINT_TABS(fp, tab_inserts);
+ (void) fprintf(fp, IPQOS_CONF_PARAMS_STR " {\n");
+
+ /* print originator name if printall flag set */
+
+ if (printall) {
+ PRINT_TABS(fp, tab_inserts + 1);
+ (void) fprintf(stdout, "Originator %s\n",
+ quote_ws_string(get_originator_nm(params->originator)));
+ }
+
+ /* print global stats */
+
+ PRINT_TABS(fp, tab_inserts + 1);
+ (void) fprintf(fp, IPQOS_CONF_GLOBAL_STATS_STR " %s\n",
+ params->stats_enable == B_TRUE ? "true" : "false");
+
+ /* print module specific parameters */
+ res = printnvlist(fp, module, params->nvlist, printall, NULL,
+ tab_inserts + 1, PL_PARAMS);
+ if (res != IPQOS_CONF_SUCCESS) {
+ return (res);
+ }
+
+ PRINT_TABS(fp, tab_inserts);
+ (void) fprintf(fp, "}\n");
+
+ return (IPQOS_CONF_SUCCESS);
+}
+
+/*
+ * print the interpreted name of the action_nm parameter if it is a special
+ * action, else action_nm verbatim to fp parameter.
+ */
+static void
+print_action_nm(FILE *fp, char *action_nm)
+{
+
+ if (strcmp(action_nm, IPP_ANAME_CONT) == 0) {
+ (void) fprintf(fp, IPQOS_CONF_CONT_STR "\n");
+ } else if (strcmp(action_nm, IPP_ANAME_DEFER) == 0) {
+ (void) fprintf(fp, IPQOS_CONF_DEFER_STR "\n");
+ } else if (strcmp(action_nm, IPP_ANAME_DROP) == 0) {
+ (void) fprintf(fp, IPQOS_CONF_DROP_STR "\n");
+ } else {
+ (void) fprintf(fp, "%s\n", quote_ws_string(action_nm));
+ }
+}
+
+/*
+ * print a class clause for class to fp. If printall is set the originator
+ * is printed.
+ */
+static void
+printclass(
+FILE *fp,
+ipqos_conf_class_t *class,
+int printall,
+int tab_inserts)
+{
+
+ /* print opening clause */
+
+ PRINT_TABS(fp, tab_inserts);
+ (void) fprintf(fp, IPQOS_CONF_CLASS_STR " {\n");
+
+
+ /* if printall flag print originator name */
+
+ if (printall) {
+ PRINT_TABS(fp, tab_inserts + 1);
+ (void) fprintf(stdout, "Originator %s\n",
+ get_originator_nm(class->originator));
+ }
+
+ /* print name, next action and stats enable */
+
+ PRINT_TABS(fp, tab_inserts + 1);
+ (void) fprintf(fp, IPQOS_CONF_NAME_STR " %s\n",
+ quote_ws_string(class->name));
+ PRINT_TABS(fp, tab_inserts + 1);
+ (void) fprintf(fp, IPQOS_CONF_NEXT_ACTION_STR " ");
+ print_action_nm(fp, class->alist->name);
+ PRINT_TABS(fp, tab_inserts + 1);
+ (void) fprintf(fp, IPQOS_CONF_STATS_ENABLE_STR " %s\n",
+ class->stats_enable == B_TRUE ? "true" : "false");
+
+ PRINT_TABS(fp, tab_inserts);
+ (void) fprintf(fp, "}\n");
+}
+
+/*
+ * Returns a ptr to the originator name associated with origid. If unknown
+ * id returns ptr to "unknown".
+ * RETURNS: ptr to originator name, or if id not known "unknown".
+ */
+static char *
+get_originator_nm(uint32_t origid)
+{
+
+ int x;
+
+ /* scan originators table for origid */
+
+ for (x = 0; originators[x].value != -1 &&
+ originators[x].value != origid; x++) {}
+
+ /* if we've reached end of array due to unknown type return "unknown" */
+
+ if (originators[x].value == -1) {
+ return ("unknown");
+ }
+
+ return (originators[x].string);
+}
+
+/*
+ * print a filter clause for filter pointed to by filter out to fp. If printall
+ * is set then the originator is printed, for filters with node names instance
+ * numbers are printed, and the filter pointer isn't advanced to point at the
+ * last instance of the printed filter.
+ * RETURNS: IPQOS_CONF_ERR on errors, else IPQOS_CONF_SUCCESS.
+ */
+static int
+printfilter(
+FILE *fp,
+char *module,
+ipqos_conf_filter_t **filter,
+int printall,
+int tab_inserts)
+{
+
+ int res;
+
+ /* print opening clause */
+
+ PRINT_TABS(fp, tab_inserts);
+ (void) fprintf(fp, IPQOS_CONF_FILTER_STR " {\n");
+
+ /* print originator if printall flag set */
+
+ if (printall) {
+ PRINT_TABS(fp, tab_inserts + 1);
+ (void) fprintf(stdout, "Originator %s\n",
+ quote_ws_string(get_originator_nm((*filter)->originator)));
+ }
+
+ /* print name and class */
+
+ PRINT_TABS(fp, tab_inserts + 1);
+ (void) fprintf(fp, IPQOS_CONF_NAME_STR " %s\n",
+ quote_ws_string((*filter)->name));
+ PRINT_TABS(fp, tab_inserts + 1);
+ (void) fprintf(fp, IPQOS_CONF_CLASS_STR " %s\n",
+ quote_ws_string((*filter)->class_name));
+
+ /* print the instance if printall and potential mhomed addresses */
+
+ if (printall && ((*filter)->src_nd_name || (*filter)->dst_nd_name)) {
+ PRINT_TABS(fp, tab_inserts + 1);
+ (void) fprintf(fp, "Instance %u\n", (*filter)->instance);
+ }
+
+ /* print node names if any */
+
+ if ((*filter)->src_nd_name) {
+ PRINT_TABS(fp, tab_inserts + 1);
+ (void) fprintf(fp, "%s %s\n", strchr(IPGPC_SADDR, '.') + 1,
+ (*filter)->src_nd_name);
+ }
+ if ((*filter)->dst_nd_name) {
+ PRINT_TABS(fp, tab_inserts + 1);
+ (void) fprintf(fp, "%s %s\n", strchr(IPGPC_DADDR, '.') + 1,
+ (*filter)->dst_nd_name);
+ }
+
+ /* print ip_version enumeration if set */
+
+ if ((*filter)->ip_versions != 0) {
+ PRINT_TABS(fp, tab_inserts + 1);
+ (void) fprintf(fp, IPQOS_CONF_IP_VERSION_STR " {");
+ if (VERSION_IS_V4(*filter)) {
+ (void) fprintf(fp, " V4");
+ }
+ if (VERSION_IS_V6(*filter)) {
+ (void) fprintf(fp, " V6");
+ }
+ (void) fprintf(fp, " }\n");
+ }
+
+ /* print other module specific parameters parameters */
+
+ res = printnvlist(fp, module, (*filter)->nvlist, printall, *filter,
+ tab_inserts + 1, PL_FILTER);
+ if (res != IPQOS_CONF_SUCCESS) {
+ return (res);
+ }
+
+ PRINT_TABS(fp, tab_inserts);
+ (void) fprintf(fp, "}\n");
+
+ /*
+ * if not printall advance filter parameter to last instance of this
+ * filter.
+ */
+
+ if (!printall) {
+ for (;;) {
+ if ((*filter)->next == NULL ||
+ strcmp((*filter)->name, (*filter)->next->name) !=
+ 0) {
+ break;
+ }
+ *filter = (*filter)->next;
+ }
+ }
+
+ return (IPQOS_CONF_SUCCESS);
+}
+
+/*
+ * Returns a pointer to str if no whitespace is present, else it returns
+ * a pointer to a string with the contents of str enclose in double quotes.
+ * This returned strings contents may change in subsequent calls so a copy
+ * should be made of it if the caller wishes to retain it.
+ */
+static char *
+quote_ws_string(const char *str)
+{
+ static char *buf = NULL;
+ const char *cp; /* we don't modify the contents of str so const */
+
+ IPQOSCDBG0(L0, "In quote_ws_string\n");
+
+ /*
+ * Just return str if no whitespace.
+ */
+ for (cp = str; (*cp != '\0') && !isspace(*cp); cp++)
+ ;
+ if (*cp == '\0')
+ return ((char *)str);
+
+ if (buf == NULL) {
+ /*
+ * if first run just allocate buffer of
+ * strlen(str) + 2 quote characters + NULL terminator.
+ */
+ buf = malloc(strlen(str) + 3);
+ } else if ((strlen(str) + 2) > strlen(buf)) {
+ /*
+ * Not first run, so check if we have a big enough buffer
+ * and if not reallocate the buffer to a sufficient size.
+ */
+ buf = realloc(buf, strlen(str) + 3);
+ }
+ if (buf == NULL)
+ return ("");
+
+ /*
+ * copy string into buffer with quotes.
+ */
+ (void) strcpy(buf, "\"");
+ (void) strcat(buf, str);
+ (void) strcat(buf, "\"");
+
+ return (buf);
+}
+
+/*
+ * print an action clause for action to fp. If the printall flag is set
+ * then all filters and classes (regardless of their originator) and
+ * their originators are displayed.
+ * RETURNS: IPQOS_CONF_ERR on errors, else IPQOS_CONF_SUCCESS.
+ */
+static int
+printaction(
+FILE *fp,
+ipqos_conf_action_t *action,
+int printall,
+int tab_inserts)
+{
+
+ ipqos_conf_filter_t *flt;
+ ipqos_conf_class_t *cls;
+ int res;
+
+ /* print opening clause, module and name */
+
+ PRINT_TABS(fp, tab_inserts);
+ (void) fprintf(fp, IPQOS_CONF_ACTION_STR " {\n");
+ PRINT_TABS(fp, tab_inserts + 1);
+ (void) fprintf(fp, IPQOS_CONF_MODULE_STR " %s\n",
+ quote_ws_string(action->module));
+ PRINT_TABS(fp, tab_inserts + 1);
+ (void) fprintf(fp, "name %s\n", quote_ws_string(action->name));
+
+ /* print params clause */
+
+ (void) fprintf(fp, "\n");
+ res = printparams(fp, action->module, action->params, printall,
+ tab_inserts + 1);
+ if (res != IPQOS_CONF_SUCCESS) {
+ return (res);
+ }
+
+ /*
+ * print classes clause for each class if printall is set, else
+ * just ipqosconf created or permanent classes.
+ */
+ for (cls = action->classes; cls != NULL; cls = cls->next) {
+ if (printall ||
+ cls->originator == IPP_CONFIG_IPQOSCONF ||
+ cls->originator == IPP_CONFIG_PERMANENT) {
+ (void) fprintf(fp, "\n");
+ printclass(fp, cls, printall, tab_inserts + 1);
+ }
+ }
+
+ /*
+ * print filter clause for each filter if printall is set, else
+ * just ipqosconf created or permanent filters.
+ */
+ for (flt = action->filters; flt != NULL; flt = flt->next) {
+ if (printall ||
+ flt->originator == IPP_CONFIG_IPQOSCONF ||
+ flt->originator == IPP_CONFIG_PERMANENT) {
+ (void) fprintf(fp, "\n");
+ res = printfilter(fp, action->module, &flt, printall,
+ tab_inserts + 1);
+ if (res != IPQOS_CONF_SUCCESS) {
+ return (res);
+ }
+ }
+ }
+
+ PRINT_TABS(fp, tab_inserts);
+ (void) fprintf(fp, "}\n");
+
+ return (IPQOS_CONF_SUCCESS);
+}
+
+
+
+/* *************************************************************** */
+
+
+static void
+list_end(
+ipqos_list_el_t **listp,
+ipqos_list_el_t ***lendpp)
+{
+ *lendpp = listp;
+ while (**lendpp != NULL) {
+ *lendpp = &(**lendpp)->next;
+ }
+}
+
+static void
+add_to_list(
+ipqos_list_el_t **listp,
+ipqos_list_el_t *el)
+{
+ el->next = *listp;
+ *listp = el;
+}
+
+/*
+ * given mask calculates the number of bits it spans. The mask must be
+ * continuous.
+ * RETURNS: number of bits spanned.
+ */
+static int
+masktocidr(
+int af,
+in6_addr_t *mask)
+{
+ int zeros = 0;
+ int byte;
+ int cidr;
+
+ /*
+ * loop through from lowest byte to highest byte counting the
+ * number of zero bits till hitting a one bit.
+ */
+ for (byte = 15; byte >= 0; byte--) {
+ /*
+ * zero byte, so add 8 to zeros.
+ */
+ if (mask->s6_addr[byte] == 0) {
+ zeros += 8;
+ /*
+ * non-zero byte, add zero count to zeros.
+ */
+ } else {
+ zeros += (ffs((int)mask->s6_addr[byte]) - 1);
+ break;
+ }
+ }
+ /*
+ * translate zero bits to 32 or 128 bit mask based on af.
+ */
+ if (af == AF_INET) {
+ cidr = 32 - zeros;
+ } else {
+ cidr = 128 - zeros;
+ }
+
+ return (cidr);
+}
+
+/*
+ * Sets the first prefix_len bits in the v4 or v6 address (based upon af)
+ * contained in the v6 address referenced by addr to 1.
+ */
+static void
+setmask(int prefix_len, in6_addr_t *addr, int af)
+{
+
+ int i;
+ int shift;
+ int maskstartbit = 128 - prefix_len;
+ int end_u32;
+
+ IPQOSCDBG2(L1, "In setmask, prefix_len: %u, af: %s\n", prefix_len,
+ af == AF_INET ? "AF_INET" : "AF_INET6");
+
+ /* zero addr */
+ bzero(addr, sizeof (in6_addr_t));
+
+
+ /* set which 32bits in *addr are relevant to this af */
+
+ if (af == AF_INET) {
+ end_u32 = 3;
+ maskstartbit = 32 - prefix_len;
+ /* AF_INET6 */
+ } else {
+ end_u32 = 0;
+ }
+ /*
+ * go through each of the 32bit quantities in 128 bit in6_addr_t
+ * and set appropriate bits according to prefix_len.
+ */
+ for (i = 3; i >= end_u32; i--) {
+
+ /* does the prefix apply to this 32bits? */
+
+ if (maskstartbit < ((4 - i) * 32)) {
+
+ /* is this 32bits fully masked? */
+
+ if (maskstartbit <= ((3 - i) * 32)) {
+ shift = 0;
+ } else {
+ shift = maskstartbit % 32;
+ }
+ addr->_S6_un._S6_u32[i] = (uint32_t)~0;
+ addr->_S6_un._S6_u32[i] =
+ addr->_S6_un._S6_u32[i] >> shift;
+ addr->_S6_un._S6_u32[i] =
+ addr->_S6_un._S6_u32[i] << shift;
+ }
+
+ /* translate to NBO */
+ addr->_S6_un._S6_u32[i] = htonl(addr->_S6_un._S6_u32[i]);
+ }
+}
+
+/*
+ * search nvlist for an element with the name specified and return a ptr
+ * to it if found.
+ * RETURNS: pointer to nvpair named name if found, else NULL.
+ */
+static nvpair_t *
+find_nvpair(nvlist_t *nvl, char *name)
+{
+
+ nvpair_t *nvp;
+ nvpair_t *match = NULL;
+ char *nvp_name;
+
+ IPQOSCDBG0(L1, "In find_nvpair\n");
+
+ nvp = nvlist_next_nvpair(nvl, NULL);
+ while (nvp) {
+ nvp_name = nvpair_name(nvp);
+ if (strcmp(name, nvp_name) == 0) {
+ match = nvp;
+ }
+ nvp = nvlist_next_nvpair(nvl, nvp);
+ }
+
+ return (match);
+}
+
+/*
+ * returns a string containing module_name '.' name.
+ * RETURNS: IPQOS_CONF_ERR if error, else IPQOS_CONF_SUCCESS.
+ */
+static char *
+prepend_module_name(
+char *name,
+char *module)
+{
+
+ char *ret;
+
+ IPQOSCDBG0(L2, "In prepend_module_name\n");
+
+ ret = malloc(strlen(module) + strlen(".") + strlen(name) + 1);
+ if (ret == NULL) {
+ ipqos_msg(MT_ENOSTR, "malloc");
+ return (NULL);
+ }
+
+ (void) strcpy(ret, module);
+ (void) strcat(ret, ".");
+ (void) strcat(ret, name);
+
+ return (ret);
+}
+
+#if 0
+
+/*
+ * check if element with matching s1 and s2 string is in table table.
+ * RETURNS: 1 if found else 0.
+ */
+static int
+in_str_str_table(
+str_str_t *table,
+char *s1,
+char *s2)
+{
+
+ str_str_t *ss = table;
+
+ /* loop through table till matched or end */
+
+ while (ss->s1[0] != '\0' &&
+ (strcmp(ss->s1, s1) != 0 || strcmp(ss->s2, s2) != 0)) {
+ ss++;
+ }
+
+ if (ss->s1[0] != '\0') {
+ return (1);
+ }
+
+ return (0);
+}
+#endif /* 0 */
+
+/*
+ * check whether name is a valid action/class/filter name.
+ * RETURNS: IPQOS_CONF_ERR if invalid name else IPQOS_CONF_SUCCESS.
+ */
+static int
+valid_name(char *name)
+{
+
+ IPQOSCDBG1(L1, "In valid_name: name: %s\n", name);
+
+ /* first char can't be '!' */
+ if (name[0] == '!') {
+ ipqos_msg(MT_ERROR, gettext("Name not allowed to start with "
+ "'!', line %u.\n"), lineno);
+ return (IPQOS_CONF_ERR);
+ }
+
+ /* can't exceed IPQOS_CONF_NAME_LEN size */
+ if (strlen(name) >= IPQOS_CONF_NAME_LEN) {
+ ipqos_msg(MT_ERROR, gettext("Name exceeds maximum name length "
+ "line %u.\n"), lineno);
+ return (IPQOS_CONF_ERR);
+ }
+
+ return (IPQOS_CONF_SUCCESS);
+}
+
+/* ********************* string value manip fns ************************** */
+
+
+/*
+ * searches through the str_val_nd_t list of string value pairs finding
+ * the minimum and maximum values for value and places them in the
+ * integers pointed at by min and max.
+ */
+static void
+get_str_val_value_range(
+str_val_nd_t *svnp,
+int *min,
+int *max)
+{
+ if (svnp != NULL) {
+ *min = *max = svnp->sv.value;
+ svnp = svnp->next;
+ }
+ while (svnp != NULL) {
+ if (svnp->sv.value > *max)
+ *max = svnp->sv.value;
+ if (svnp->sv.value < *min)
+ *min = svnp->sv.value;
+ svnp = svnp->next;
+ }
+}
+
+/*
+ * add an entry with string string and value val to sv_entrys.
+ * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
+ */
+static int
+add_str_val_entry(
+str_val_nd_t **sv_entrys,
+char *string,
+uint32_t val)
+{
+
+ str_val_nd_t *sv_entry;
+
+ IPQOSCDBG2(L1, "In add_str_val_entry: string: %s, val: %u\n", string,
+ val);
+
+ /* alloc new node */
+
+ sv_entry = malloc(sizeof (str_val_nd_t));
+ if (sv_entry == NULL) {
+ return (IPQOS_CONF_ERR);
+ }
+
+ /* populate node */
+
+ sv_entry->sv.string = malloc(strlen(string) + 1);
+ if (sv_entry->sv.string == NULL) {
+ free(sv_entry);
+ ipqos_msg(MT_ENOSTR, "malloc");
+ return (IPQOS_CONF_ERR);
+ } else {
+ (void) strcpy(sv_entry->sv.string, string);
+ }
+ sv_entry->sv.value = val;
+
+ /* place at start of sv_entrys list */
+
+ sv_entry->next = *sv_entrys;
+ *sv_entrys = sv_entry;
+
+ return (IPQOS_CONF_SUCCESS);
+}
+
+
+/* frees all the elements of sv_entrys. */
+static void
+free_str_val_entrys(
+str_val_nd_t *sv_entrys)
+{
+
+ str_val_nd_t *sve = sv_entrys;
+ str_val_nd_t *tmp;
+
+ IPQOSCDBG0(L1, "In free_str_val_entrys\n");
+
+ while (sve) {
+ free(sve->sv.string);
+ tmp = sve->next;
+ free(sve);
+ sve = tmp;
+ }
+}
+
+/*
+ * finds the value associated with string and assigns it to value ref'd by
+ * val.
+ * RETURNS: IPQOS_CONF_ERR if string not found, else IPQOS_CONF_SUCCESS.
+ */
+static int
+str_val_list_lookup(
+str_val_nd_t *svs,
+char *string,
+uint32_t *val)
+{
+
+ str_val_nd_t *sv = svs;
+
+ IPQOSCDBG1(L1, "In str_val_list_lookup: %s\n", string);
+
+ /* loop through list and exit when found or list end */
+
+ while (sv != NULL) {
+ if (strcmp(sv->sv.string, string) == 0) {
+ break;
+ }
+ sv = sv->next;
+ }
+
+ /* ret error if not found */
+
+ if (sv == NULL) {
+ return (IPQOS_CONF_ERR);
+ }
+
+ *val = sv->sv.value;
+
+ IPQOSCDBG1(L1, "svll: Value returned is %u\n", *val);
+ return (IPQOS_CONF_SUCCESS);
+}
+
+
+/* ************************ conf file read fns ***************************** */
+
+/*
+ * Reads a uid or username from string 'str' and assigns either the uid
+ * or associated uid respectively to storage pointed at by 'uid'. The
+ * function determines whether to read a uid by checking whether the first
+ * character of 'str' is numeric, in which case it reads a uid; otherwise it
+ * assumes a username.
+ * RETURNS: IPQOS_CONF_ERR if a NULL string pointer is passed, the read uid
+ * doesn't have an entry on the system, or the read username doesn't have an
+ * entry on the system.
+ */
+static int
+readuser(
+char *str,
+uid_t *uid)
+{
+ struct passwd *pwd;
+ char *lo;
+
+ IPQOSCDBG1(L0, "In readuser, str: %s\n", str);
+
+ if (str == NULL)
+ return (IPQOS_CONF_ERR);
+ /*
+ * Check if this appears to be a uid, and if so check that a
+ * corresponding user exists.
+ */
+ if (isdigit((int)str[0])) {
+ /*
+ * Read a 32bit integer and check in doing so that
+ * we have consumed the whole string.
+ */
+ if (readint32(str, (int *)uid, &lo) != IPQOS_CONF_SUCCESS ||
+ *lo != '\0')
+ return (IPQOS_CONF_ERR);
+ if (getpwuid(*uid) == NULL)
+ return (IPQOS_CONF_ERR);
+
+ } else { /* This must be a username, so lookup the uid. */
+ pwd = getpwnam(str);
+ if (pwd == NULL) {
+ return (IPQOS_CONF_ERR);
+ } else {
+ *uid = pwd->pw_uid;
+ }
+ }
+ return (IPQOS_CONF_SUCCESS);
+}
+
+/*
+ * Reads a range from range_st, either of form 'a-b' or simply 'a'.
+ * In the former case lower and upper have their values set to a
+ * and b respectively; in the later lower and upper have both
+ * their values set to a.
+ * RETURNS: IPQOS_CONF_ERR if there's a parse error, else IPQOS_CONF_SUCCESS.
+ */
+static int
+readrange(
+char *range_st,
+int *lower,
+int *upper)
+{
+ char *cp;
+ char *end, *end2;
+
+ IPQOSCDBG1(L0, "In readrange: string: %s\n", range_st);
+
+ /*
+ * get range boundarys.
+ */
+ cp = strchr(range_st, '-');
+
+ if (cp != NULL) { /* we have a range */
+ *cp++ = '\0';
+ *lower = (int)strtol(range_st, &end, 10);
+ *upper = (int)strtol(cp, &end2, 10);
+ SKIPWS(end);
+ SKIPWS(end2);
+ if ((range_st == end) || (*end != NULL) ||
+ (cp == end) || (*end2 != NULL)) {
+ IPQOSCDBG0(L0, "Failed reading a-b\n");
+ return (IPQOS_CONF_ERR);
+ }
+
+ } else { /* single value */
+
+ *lower = *upper = (int)strtol(range_st, &end, 10);
+ SKIPWS(end);
+ if ((range_st == end) || (*end != NULL)) {
+ IPQOSCDBG0(L0, "Failed reading a\n");
+ return (IPQOS_CONF_ERR);
+ }
+ }
+
+ return (IPQOS_CONF_SUCCESS);
+}
+
+/*
+ * Reads the values of an integer array from fp whose format is:
+ * '{'RANGE[,RANGE[..]]:VALUE[;RANGE:VALUE[..]]'}', creates an array of size
+ * arr_size, applies the values to it and points arrp at this array.
+ * RANGE is one set of array indexes over which this value is to
+ * be applied, and VALUE either an integer within the range
+ * llimit - ulimit, or if enum_nvs isn't NULL, an enumeration value
+ * found in the list enum_nvs. Those values which aren't explicity set
+ * will be set to -1.
+ *
+ * RETURNS: IPQOS_CONF_ERR on resource or parse error, else IPQOS_CONF_SUCCESS.
+ */
+static int
+read_int_array(
+FILE *fp,
+char *first_token,
+int **arrp,
+uint32_t arr_size,
+int llimit,
+int ulimit,
+str_val_nd_t *enum_nvs)
+{
+
+ char buf[5 * IPQOS_CONF_LINEBUF_SZ];
+ char *token;
+ char *range;
+ char *ranges;
+ char *svalue;
+ int value;
+ int res;
+ char *entry;
+ char *tmp;
+ char *end;
+ int lower, upper;
+ int x;
+ uint32_t startln;
+
+ IPQOSCDBG4(L0, "In read_int_array: size: %u, lower: %u, upper: %u, "
+ "first_token: %s\n", arr_size, llimit, ulimit, first_token);
+
+ /*
+ * read beginning curl.
+ */
+ if (first_token[0] != CURL_BEGIN) {
+ ipqos_msg(MT_ERROR, gettext("\'{\' missing at line "
+ "%u.\n"), lineno);
+ return (IPQOS_CONF_ERR);
+ }
+
+ /*
+ * allocate and initialise array for holding read values.
+ */
+ *arrp = malloc(arr_size * sizeof (int));
+ if (*arrp == NULL) {
+ ipqos_msg(MT_ENOSTR, "malloc");
+ return (IPQOS_CONF_ERR);
+ }
+ (void) memset(*arrp, -1, arr_size * sizeof (int));
+
+ /*
+ * read whole array declaration string into buffer.
+ * this is because readtoken doesn't interpret our
+ * delimeter values specially and may return them
+ * within another string.
+ */
+ startln = lineno; /* store starting lineno for error reports */
+ buf[0] = '\0';
+ res = readtoken(fp, &token);
+ while ((res != IPQOS_CONF_CURL_END) && (res != IPQOS_CONF_ERR) &&
+ (res != IPQOS_CONF_EOF)) {
+ (void) strlcat(buf, token, sizeof (buf));
+ free(token);
+ res = readtoken(fp, &token);
+ }
+ if (res != IPQOS_CONF_CURL_END) {
+ goto array_err;
+ }
+ IPQOSCDBG1(L0, "array declaration buffer contains: %s\n", buf);
+
+ /*
+ * loop reading "ranges ':' value;" till end of buffer.
+ */
+ entry = strtok(buf, ";");
+ while (entry != NULL) {
+ svalue = strchr(entry, ':');
+ if (svalue == NULL) { /* missing value string */
+ IPQOSCDBG0(L0, "Missing value string\n");
+ goto array_err;
+ }
+ *svalue++ = '\0';
+ ranges = entry;
+
+ /*
+ * get value of number or enumerated symbol.
+ */
+ if (enum_nvs) {
+ /*
+ * get rid of surrounding whitespace so as not to
+ * confuse read_enum_value.
+ */
+ SKIPWS(svalue);
+ tmp = svalue;
+ while (*tmp != '\0') {
+ if (isspace(*tmp)) {
+ *tmp = '\0';
+ break;
+ } else {
+ tmp++;
+ }
+ }
+
+ /*
+ * read enumeration value.
+ */
+ res = read_enum_value(NULL, svalue, enum_nvs,
+ (uint32_t *)&value);
+ if (res != IPQOS_CONF_SUCCESS)
+ goto array_err;
+ } else {
+ value = (int)strtol(svalue, &end, 10);
+ SKIPWS(end);
+ if ((svalue == end) || (*end != NULL)) {
+ IPQOSCDBG0(L0, "Invalid value\n");
+ goto array_err;
+ }
+ IPQOSCDBG1(L0, "value: %u\n", value);
+
+ /*
+ * check value within valid range.
+ */
+ if ((value < llimit) || (value > ulimit)) {
+ IPQOSCDBG0(L0, "value out of range\n");
+ goto array_err;
+ }
+ }
+
+ /*
+ * loop reading ranges for this value.
+ */
+ range = strtok_r(ranges, ",", &tmp);
+ while (range != NULL) {
+ res = readrange(range, &lower, &upper);
+ if (res != IPQOS_CONF_SUCCESS)
+ goto array_err;
+ IPQOSCDBG2(L0, "range: %u - %u\n", lower, upper);
+
+
+ if (upper < lower) {
+ uint32_t u = lower;
+ lower = upper;
+ upper = u;
+ }
+
+ /*
+ * check range valid for array size.
+ */
+ if ((lower < 0) || (upper > arr_size)) {
+ IPQOSCDBG0(L0, "Range out of array "
+ "dimensions\n");
+ goto array_err;
+ }
+
+ /*
+ * add this value to array indexes within range.
+ */
+ for (x = lower; x <= upper; x++)
+ (*arrp)[x] = value;
+
+ /*
+ * get next range.
+ */
+ range = strtok_r(NULL, ",", &tmp);
+ }
+
+ entry = strtok(NULL, ";");
+ }
+
+ return (IPQOS_CONF_SUCCESS);
+
+array_err:
+ ipqos_msg(MT_ERROR,
+ gettext("Array declaration line %u is invalid.\n"), startln);
+ free(*arrp);
+ return (IPQOS_CONF_ERR);
+}
+
+static int
+readllong(char *str, long long *llp, char **lo)
+{
+
+ *llp = strtoll(str, lo, 0);
+ if (*lo == str) {
+ return (IPQOS_CONF_ERR);
+ }
+ return (IPQOS_CONF_SUCCESS);
+}
+
+static int
+readuint8(char *str, uint8_t *ui8, char **lo)
+{
+
+ long long tmp;
+
+ if (readllong(str, &tmp, lo) != 0) {
+ return (IPQOS_CONF_ERR);
+ }
+ if (tmp > UCHAR_MAX || tmp < 0) {
+ return (IPQOS_CONF_ERR);
+ }
+ *ui8 = (uint8_t)tmp;
+ return (IPQOS_CONF_SUCCESS);
+}
+
+static int
+readuint16(char *str, uint16_t *ui16, char **lo)
+{
+ long long tmp;
+
+ if (readllong(str, &tmp, lo) != IPQOS_CONF_SUCCESS) {
+ return (IPQOS_CONF_ERR);
+ }
+ if (tmp > USHRT_MAX || tmp < 0) {
+ return (IPQOS_CONF_ERR);
+ }
+ *ui16 = (uint16_t)tmp;
+ return (IPQOS_CONF_SUCCESS);
+}
+
+static int
+readint16(char *str, int16_t *i16, char **lo)
+{
+ long long tmp;
+
+ if (readllong(str, &tmp, lo) != 0) {
+ return (IPQOS_CONF_ERR);
+ }
+ if (tmp > SHRT_MAX || tmp < SHRT_MIN) {
+ return (IPQOS_CONF_ERR);
+ }
+ *i16 = (int16_t)tmp;
+ return (IPQOS_CONF_SUCCESS);
+}
+
+static int
+readint32(char *str, int *i32, char **lo)
+{
+ long long tmp;
+
+ if (readllong(str, &tmp, lo) != IPQOS_CONF_SUCCESS) {
+ return (IPQOS_CONF_ERR);
+ }
+ if (tmp > INT_MAX || tmp < INT_MIN) {
+ return (IPQOS_CONF_ERR);
+ }
+ *i32 = tmp;
+ return (IPQOS_CONF_SUCCESS);
+}
+
+static int
+readuint32(char *str, uint32_t *ui32, char **lo)
+{
+ long long tmp;
+
+ if (readllong(str, &tmp, lo) != IPQOS_CONF_SUCCESS) {
+ return (IPQOS_CONF_ERR);
+ }
+ if (tmp > UINT_MAX || tmp < 0) {
+ return (IPQOS_CONF_ERR);
+ }
+ *ui32 = (uint32_t)tmp;
+ return (IPQOS_CONF_SUCCESS);
+}
+
+/*
+ * retrieves the index associated with the interface named ifname and assigns
+ * it to the int pointed to by ifindex.
+ * RETURNS: IPQOS_CONF_ERR on errors, else IPQOS_CONF_SUCCESS.
+ */
+static int
+readifindex(
+char *ifname,
+int *ifindex)
+{
+
+ int s;
+ struct lifreq lifrq;
+
+
+ /* open socket */
+
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
+ ipqos_msg(MT_ENOSTR, gettext("opening AF_INET socket"));
+ return (IPQOS_CONF_ERR);
+ }
+
+ /* copy ifname into lifreq */
+
+ (void) strlcpy(lifrq.lifr_name, ifname, LIFNAMSIZ);
+
+ /* do SIOGLIFINDEX ioctl */
+
+ if (ioctl(s, SIOCGLIFINDEX, (caddr_t)&lifrq) == -1) {
+ (void) close(s);
+ return (IPQOS_CONF_ERR);
+ }
+
+ /* Warn if a virtual interface is specified */
+ if ((ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifrq) != -1) &&
+ (lifrq.lifr_flags & IFF_VIRTUAL)) {
+ ipqos_msg(MT_WARNING, gettext("Invalid interface"));
+ }
+ (void) close(s);
+ *ifindex = lifrq.lifr_index;
+ return (IPQOS_CONF_SUCCESS);
+}
+
+/*
+ * Case insensitively compares the string in str with IPQOS_CONF_TRUE_STR
+ * and IPQOS_CONF_FALSE_STR and sets boolean pointed to by bool accordingly.
+ * RETURNS: if failure to match either IPQOS_CONF_ERR, else IPQOS_CONF_SUCCESS.
+ */
+static int
+readbool(char *str, boolean_t *bool)
+{
+
+ if (strcasecmp(str, IPQOS_CONF_TRUE_STR) == 0) {
+ *bool = B_TRUE;
+ } else if (strcasecmp(str, IPQOS_CONF_FALSE_STR) == 0) {
+ *bool = B_FALSE;
+ } else {
+ return (IPQOS_CONF_ERR);
+ }
+
+ return (IPQOS_CONF_SUCCESS);
+}
+
+/*
+ * reads a protocol name/number from proto_str and assigns the number
+ * to the uint8 ref'd by proto.
+ * RETURNS: If not a valid name or protocol number IPQOS_CONF_ERR, else
+ * IPQOS_CONF_SUCCESS.
+ */
+static int
+readproto(char *proto_str, uint8_t *proto)
+{
+
+ struct protoent *pent;
+ char *lo;
+ int res;
+
+ IPQOSCDBG1(L1, "In readproto: string: %s\n", proto_str);
+
+ /* try name lookup */
+
+ pent = getprotobyname(proto_str);
+ if (pent) {
+ *proto = pent->p_proto;
+
+ /* check valid protocol number */
+ } else {
+ res = readuint8(proto_str, proto, &lo);
+ if (res != IPQOS_CONF_SUCCESS || proto == 0) {
+ return (IPQOS_CONF_ERR);
+ }
+ }
+
+ return (IPQOS_CONF_SUCCESS);
+}
+
+/*
+ * reads either a port service, or a port number from port_str and assigns
+ * the associated port number to short ref'd by port.
+ * RETURNS: If invalid name and number IPQOS_CONF_ERR, else IPQOS_CONF_SUCCESS.
+ */
+static int
+readport(char *port_str, uint16_t *port)
+{
+
+ struct servent *sent;
+ char *tmp;
+
+ IPQOSCDBG1(L1, "In readport: string: %s\n", port_str);
+
+ /* try service name lookup */
+ sent = getservbyname(port_str, NULL);
+
+ /* failed name lookup so read port number */
+ if (sent == NULL) {
+ if (readuint16(port_str, port, &tmp) != IPQOS_CONF_SUCCESS ||
+ *port == 0) {
+ return (IPQOS_CONF_ERR);
+ }
+ *port = htons(*port);
+ } else {
+ *port = sent->s_port;
+ }
+
+ return (IPQOS_CONF_SUCCESS);
+}
+
+
+/*
+ * Reads a curly brace, a string enclosed in double quotes, or a whitespace/
+ * curly brace delimited string. If a double quote enclosed string the
+ * closing quotes need to be on the same line.
+ * RETURNS:
+ * on reading a CURL_BEGIN token it returns IPQOS_CONF_CURL_BEGIN,
+ * on reading a CURL_END token it returns IPQOS_CONF_CURL_END,
+ * on reading another valid token it returns IPQOS_CONF_SUCCESS.
+ * for each of these token is set to point at the read string.
+ * at EOF it returns IPQOS_CONF_EOF and if errors it returns IPQOS_CONF_ERR.
+ */
+static int
+readtoken(
+FILE *fp,
+char **token)
+{
+
+ char *st, *tmp;
+ int len;
+ int quoted = 0;
+ char *cmnt;
+ char *bpos;
+ int rembuf;
+
+ static char *lo;
+ static char *buf = NULL;
+ static int bufsize;
+
+ /* if first call initialize line buf to default size */
+
+ if (buf == NULL) {
+ bufsize = IPQOS_CONF_LINEBUF_SZ;
+ buf = malloc(bufsize);
+ if (buf == NULL) {
+ ipqos_msg(MT_ENOSTR, "malloc");
+ return (IPQOS_CONF_ERR);
+ }
+ }
+
+ /* set buffer postition and size to use whole buffer */
+
+ bpos = buf;
+ rembuf = bufsize;
+
+
+ /*
+ * loop reading lines until we've read a line with a non-whitespace
+ * char.
+ */
+
+ do {
+ /* if no leftover from previous invocation */
+
+ if (lo == NULL) {
+
+ /*
+ * loop reading into buffer doubling if necessary until
+ * we have either read a complete line or reached the
+ * end of file.
+ */
+ for (;;) {
+ st = fgets(bpos, rembuf, fp);
+
+ if (st == NULL) {
+
+ /* if read error */
+ if (ferror(fp)) {
+ free(buf);
+ buf = NULL;
+ ipqos_msg(MT_ENOSTR,
+ "fgets");
+ return (IPQOS_CONF_ERR);
+
+ /* end of file */
+ } else {
+ free(buf);
+ buf = NULL;
+ *token = NULL;
+ return (IPQOS_CONF_EOF);
+ }
+ } else {
+ /* if read a newline */
+
+ if (buf[strlen(buf) - 1] == '\n') {
+ lineno++;
+ break;
+
+ /* if read the last line */
+
+ } else if (feof(fp)) {
+ break;
+
+ /*
+ * not read a full line so buffer size
+ * is too small, double it and retry.
+ */
+ } else {
+ bufsize *= 2;
+ tmp = realloc(buf, bufsize);
+ if (tmp == NULL) {
+ ipqos_msg(MT_ENOSTR,
+ "realloc");
+ free(buf);
+ return (IPQOS_CONF_ERR);
+ } else {
+ buf = tmp;
+ }
+
+ /*
+ * make parameters to fgets read
+ * into centre of doubled buffer
+ * so we retain what we've
+ * already read.
+ */
+ bpos = &buf[(bufsize / 2) - 1];
+ rembuf = (bufsize / 2) + 1;
+ }
+ }
+ }
+
+ st = buf;
+
+ /* previous leftover, assign to st */
+
+ } else {
+ st = lo;
+ lo = NULL;
+ }
+
+ /* truncate at comment */
+
+ cmnt = strchr(st, '#');
+ if (cmnt) {
+ *cmnt = '\0';
+ }
+
+ /* Skip any whitespace */
+
+ while (isspace(*st) && st != '\0') {
+ st++;
+ }
+
+ } while (*st == '\0');
+
+
+ /* find end of token */
+
+ tmp = st;
+
+ /* if curl advance 1 char */
+
+ if (*tmp == CURL_BEGIN || *tmp == CURL_END) {
+ tmp++;
+
+
+ /* if dbl quote read until matching quote */
+
+ } else if (*tmp == '"') {
+ quoted++;
+ tmp = ++st;
+
+ while (*tmp != '"' && *tmp != '\n' && *tmp != '\0') {
+ tmp++;
+ }
+ if (*tmp != '"') {
+ ipqos_msg(MT_ERROR, gettext("Quoted string exceeds "
+ "line, line %u.\n"), lineno);
+ free(buf);
+ return (IPQOS_CONF_ERR);
+ }
+
+ /* normal token */
+ } else {
+ /* find first whitespace, curl, newline or string end */
+
+ while (!isspace(*tmp) && *tmp != CURL_BEGIN &&
+ *tmp != CURL_END && *tmp != '\n' && *tmp != '\0') {
+ tmp++;
+ }
+ }
+
+ /* copy token to return */
+ len = tmp - st;
+ *token = malloc(len + 1);
+ if (!*token) {
+ free(buf);
+ ipqos_msg(MT_ENOSTR, "malloc");
+ return (IPQOS_CONF_ERR);
+ }
+ bcopy(st, *token, len);
+ (*token)[len] = '\0';
+
+ /* if just read quoted string remove quote from remaining string */
+
+ if (quoted) {
+ tmp++;
+ }
+
+ /* if not end of string, store rest for latter parsing */
+
+ if (*tmp != '\0' && *tmp != '\n') {
+ lo = tmp;
+ }
+
+ /* for curl_end and curl_begin return special ret codes */
+
+ if ((*token)[1] == '\0') {
+ if (**token == CURL_BEGIN) {
+ return (IPQOS_CONF_CURL_BEGIN);
+ } else if (**token == CURL_END) {
+ return (IPQOS_CONF_CURL_END);
+ }
+ }
+
+ return (IPQOS_CONF_SUCCESS);
+}
+
+/*
+ * Reads an enumeration bitmask definition from line. The format is:
+ * { NAME=VAL, NAME2=VAL2 }. The resulting names and values are returned.
+ * RETURNS: NULL on error, else ptr to name/values.
+ */
+static str_val_nd_t *
+read_enum_nvs(char *line, char *module_name)
+{
+
+ str_val_nd_t *enum_vals = NULL;
+ char *cp;
+ char *start;
+ char *name = NULL;
+ int len;
+ uint32_t val;
+ int ret;
+ int readc;
+
+ IPQOSCDBG1(L1, "In read_enum_nvs, line: %s\n", line);
+
+ /* read opening brace */
+
+ cp = strchr(line, CURL_BEGIN);
+ if (cp == NULL) {
+ IPQOSCDBG0(L1, "missing curl begin\n");
+ goto fail;
+ } else {
+ start = cp + 1;
+ }
+
+ /*
+ * loop reading 'name = value' entrys seperated by comma until
+ * reach closing brace.
+ */
+
+ for (;;) {
+ SKIPWS(start);
+ if (*start == '\0') {
+ IPQOSCDBG0(L1, "missing closing bracket\n");
+ goto fail;
+ }
+
+ /*
+ * read name - read until whitespace, '=', closing curl,
+ * or string end.
+ */
+
+ for (cp = start;
+ !isspace(*cp) && *cp != '=' && *cp != CURL_END &&
+ *cp != '\0'; cp++) {}
+
+ if (*cp == '\0') {
+ IPQOSCDBG0(L1, "Unexpected line end in enum def'n\n");
+ goto fail;
+
+ /* finished definition, exit loop */
+ } else if (*cp == CURL_END) {
+ break;
+ }
+
+ /* store name */
+
+ len = cp - start;
+ name = malloc(len + 1);
+ if (name == NULL) {
+ ipqos_msg(MT_ENOSTR, "malloc");
+ goto fail;
+ }
+ bcopy(start, name, len);
+ name[len] = NULL;
+ IPQOSCDBG1(L0, "Stored name: %s\n", name);
+
+ /* read assignment */
+
+ start = strchr(cp, '=');
+ if (start == NULL) {
+ IPQOSCDBG0(L1, "Missing = in enum def'n\n");
+ goto fail;
+ }
+
+ /* read value */
+
+ ret = sscanf(++start, "%x%n", &val, &readc);
+ if (ret != 1) {
+ IPQOSCDBG1(L1, "sscanf of value failed, string: %s\n",
+ cp);
+ goto fail;
+ }
+
+ /* add name value to set */
+
+ ret = add_str_val_entry(&enum_vals, name, val);
+ if (ret != IPQOS_CONF_SUCCESS) {
+ IPQOSCDBG0(L1, "Failed to add str_val entry\n");
+ goto fail;
+ }
+ free(name);
+ name = NULL;
+
+ /* try reading comma */
+ cp = strchr(start, ',');
+
+ if (cp != NULL) {
+ start = cp + 1;
+
+ /* no comma, advance to char past value last read */
+ } else {
+ start += readc;
+ }
+ }
+
+ return (enum_vals);
+fail:
+ free_str_val_entrys(enum_vals);
+ if (name != NULL)
+ free(name);
+
+ /* if a parse error */
+
+ if (errno == 0) {
+ ipqos_msg(MT_ERROR, gettext("Types file for module %s is "
+ "corrupt.\n"), module_name);
+ }
+
+ return (NULL);
+}
+
+/*
+ * Given mapped_list with is a comma seperated list of map names, and value,
+ * which is used to index into these maps, the function creates x new entries
+ * in nvpp, where x is the number of map names specified. Each of these
+ * entries has the value from the map in the position indexed by value and
+ * with name module.${MAP_NAME}. The maps are contained in the modules config
+ * file and have the form:
+ * map map1 uint32 1,23,32,45,3
+ * As you can see the map values are uint32, and along with uint8 are the
+ * only supported types at the moment.
+ *
+ * RETURNS: IPQOS_CONF_ERR if one of the maps specified in mapped_list
+ * doesn't exist, if value is not a valid map position for a map, or if
+ * there's a resource failure. otherwise IPQOS_CONF_SUCCESS is returned.
+ */
+static int
+read_mapped_values(
+FILE *tfp,
+nvlist_t **nvlp,
+char *module,
+char *mapped_list,
+int value)
+{
+ char *map_name, *lastparam, *tmpname;
+ int res;
+ ipqos_nvtype_t type;
+ char dfltst[IPQOS_VALST_MAXLEN+1] = "";
+ str_val_nd_t *enum_nvs;
+ place_t place;
+
+ IPQOSCDBG0(L1, "In read_mapped_values\n");
+
+ map_name = (char *)strtok_r(mapped_list, ",", &lastparam);
+ while (map_name != NULL) {
+ char *tokval, *lastval;
+ int index = 0;
+
+ /*
+ * get map info from types file.
+ */
+ place = PL_MAP;
+ res = readtype(tfp, module, map_name, &type, &enum_nvs,
+ dfltst, B_FALSE, &place);
+ if (res != IPQOS_CONF_SUCCESS) {
+ return (IPQOS_CONF_ERR);
+ }
+
+ /*
+ * Just keep browsing the list till we get to the element
+ * with the index from the value parameter or the end.
+ */
+ tokval = (char *)strtok_r(dfltst, ",", &lastval);
+ for (;;) {
+ if (tokval == NULL) {
+ ipqos_msg(MT_ERROR,
+ gettext("Invalid value, %u, line %u.\n"),
+ value, lineno);
+ return (IPQOS_CONF_ERR);
+ }
+ if (index++ == value) {
+ break;
+ }
+ tokval = (char *)strtok_r(NULL, ",", &lastval);
+ }
+
+
+ /*
+ * create fully qualified parameter name for map value.
+ */
+ tmpname = prepend_module_name(map_name, module);
+ if (tmpname == NULL) {
+ return (IPQOS_CONF_ERR);
+ }
+
+ /*
+ * add map value with fqn to parameter nvlist.
+ */
+ IPQOSCDBG2(L0, "Adding map %s, value %u to nvlist\n",
+ tmpname, atoi(tokval));
+ switch (type) {
+ case IPQOS_DATA_TYPE_UINT8: {
+ res = nvlist_add_byte(*nvlp, tmpname,
+ (uint8_t)atoi(tokval));
+ if (res != 0) {
+ free(tmpname);
+ ipqos_msg(MT_ENOSTR,
+ "nvlist_add_uint8");
+ return (IPQOS_CONF_ERR);
+ }
+ break;
+ }
+ case IPQOS_DATA_TYPE_UINT32: {
+ res = nvlist_add_uint32(*nvlp, tmpname,
+ (uint32_t)atoi(tokval));
+ if (res != 0) {
+ free(tmpname);
+ ipqos_msg(MT_ENOSTR,
+ "nvlist_add_uint32");
+ return (IPQOS_CONF_ERR);
+ }
+ break;
+ }
+ default: {
+ ipqos_msg(MT_ERROR,
+ gettext("Types file for module %s is "
+ "corrupt.\n"), module);
+ IPQOSCDBG1(L0, "Unsupported map type for "
+ "parameter %s given in types file.\n",
+ map_name);
+ return (IPQOS_CONF_ERR);
+ }
+ }
+ free(tmpname);
+
+ map_name = (char *)strtok_r(NULL, ",", &lastparam);
+ }
+
+ return (IPQOS_CONF_SUCCESS);
+}
+
+/*
+ * Parses the string info_str into it's components. Its format is:
+ * SIZE','[ENUM_DEF | RANGE], where SIZE is the size of the array,
+ * ENUM_DEF is the definition of the enumeration for this array,
+ * and RANGE is the set of values this array can accept. In
+ * the event this array has an enumeration definition enum_nvs is
+ * set to point at a str_val_nd_t structure which stores the names
+ * and values associated with this enumeration. Otherwise, if this
+ * is not an enumerated array, lower and upper are set to the lower
+ * and upper values of RANGE.
+ * RETURNS: IPQOS_CONF_ERR due to unexpected parse errors, else
+ * IPQOS_CONF_SUCCESS.
+ */
+static int
+read_int_array_info(
+char *info_str,
+str_val_nd_t **enum_nvs,
+uint32_t *size,
+int *lower,
+int *upper,
+char *module)
+{
+ int res;
+ char *end;
+ char *token;
+ char *tmp;
+
+ IPQOSCDBG1(L0, "In read_array_info: info_str: %s\n",
+ (info_str != NULL) ? info_str : "NULL");
+
+ if (info_str == NULL) {
+ IPQOSCDBG0(L0, "Null info string\n");
+ goto fail;
+ }
+
+ /*
+ * read size.
+ */
+ token = strtok(info_str, ",");
+ *size = (uint32_t)strtol(token, &end, 10);
+ SKIPWS(end);
+ if ((end == token) || (*end != NULL)) {
+ IPQOSCDBG0(L0, "Invalid size\n");
+ goto fail;
+ }
+ IPQOSCDBG1(L0, "read size: %u\n", *size);
+
+ /*
+ * check we have another string.
+ */
+ token = strtok(NULL, "\n");
+ if (token == NULL) {
+ IPQOSCDBG0(L0, "Missing range/enum def\n");
+ goto fail;
+ }
+ IPQOSCDBG1(L0, "range/enum def: %s\n", token);
+
+ /*
+ * check if enumeration set or integer set and read enumeration
+ * definition or integer range respectively.
+ */
+ tmp = strchr(token, CURL_BEGIN);
+ if (tmp == NULL) { /* a numeric range */
+ res = readrange(token, lower, upper);
+ if (res != IPQOS_CONF_SUCCESS) {
+ IPQOSCDBG0(L0, "Failed reading range\n");
+ goto fail;
+ }
+ } else { /* an enumeration */
+ *enum_nvs = read_enum_nvs(token, module);
+ if (*enum_nvs == NULL) {
+ IPQOSCDBG0(L0, "Failed reading enum def\n");
+ goto fail;
+ }
+ }
+
+ return (IPQOS_CONF_SUCCESS);
+fail:
+ ipqos_msg(MT_ERROR,
+ gettext("Types file for module %s is corrupt.\n"), module);
+ return (IPQOS_CONF_ERR);
+}
+
+/*
+ * reads the value of an enumeration parameter from first_token and fp.
+ * first_token is the first token of the value.
+ * The format expected is NAME | { NAME1 [, NAME2 ] [, NAME3 ] }.
+ * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
+ */
+static int
+read_enum_value(
+FILE *fp,
+char *first_token,
+str_val_nd_t *enum_vals,
+uint32_t *val)
+{
+
+ uint32_t u32;
+ int ret;
+ char *tk;
+ char *lo = NULL;
+ char *cm;
+ int name_expected = 0;
+
+ IPQOSCDBG0(L1, "In read_enum_value\n");
+
+ /* init param val */
+ *val = 0;
+
+ /* first token not curl_begin, so lookup its value */
+
+ if (*first_token != CURL_BEGIN) {
+ ret = str_val_list_lookup(enum_vals, first_token, val);
+ if (ret != IPQOS_CONF_SUCCESS) {
+ ipqos_msg(MT_ERROR,
+ gettext("Unrecognized value, %s, line %u.\n"),
+ first_token, lineno);
+ return (ret);
+ }
+
+ /* curl_begin, so read values till curl_end, dicing at ',' */
+ } else {
+
+ name_expected++;
+
+ for (;;) {
+
+ /*
+ * no leftover from pervious iteration so read new
+ * token. This leftover happens because readtoken
+ * doesn't interpret comma's as special characters
+ * and thus could return 'val1,val2' as one token.
+ * If this happens the val1 will be used in the
+ * current iteration and what follows saved in lo
+ * for processing by successive iterations.
+ */
+
+ if (lo == NULL) {
+ ret = readtoken(fp, &tk);
+ if (ret == IPQOS_CONF_ERR) {
+ return (ret);
+ } else if (ret == IPQOS_CONF_EOF) {
+ ipqos_msg(MT_ERROR,
+ gettext("Unexpected EOF.\n"));
+ return (IPQOS_CONF_ERR);
+
+ }
+ } else { /* previous leftover, so use it */
+
+ IPQOSCDBG1(L1, "Using leftover %s.\n", lo);
+ tk = lo;
+ lo = NULL;
+ }
+
+ if (name_expected) {
+ if (ret == IPQOS_CONF_CURL_END ||
+ tk[0] == ',') {
+ ipqos_msg(MT_ERROR,
+ gettext("Malformed value list "
+ "line %u.\n"), lineno);
+ free(tk);
+ return (IPQOS_CONF_ERR);
+ }
+
+ /*
+ * check if this token contains a ',' and
+ * if so store it and what follows for next
+ * iteration.
+ */
+ cm = strchr(tk, ',');
+ if (cm != NULL) {
+ lo = malloc(strlen(cm) + 1);
+ if (lo == NULL) {
+ ipqos_msg(MT_ENOSTR, "malloc");
+ free(tk);
+ return (IPQOS_CONF_ERR);
+ }
+
+ (void) strcpy(lo, cm);
+ *cm = '\0';
+ }
+
+
+ /* get name value and add to total val */
+
+ ret = str_val_list_lookup(enum_vals, tk, &u32);
+ if (ret != IPQOS_CONF_SUCCESS) {
+ ipqos_msg(MT_ERROR,
+ gettext("Unrecognized value, %s, "
+ "line %u.\n"), tk, lineno);
+ free(tk);
+ return (IPQOS_CONF_ERR);
+ }
+
+ *val = *val | u32;
+ name_expected--;
+
+ /* comma or curl end accepted */
+ } else {
+
+ /* we've reached curl_end so break */
+
+ if (ret == IPQOS_CONF_CURL_END) {
+ free(tk);
+ break;
+
+ /* not curl end and not comma */
+
+ } else if (tk[0] != ',') {
+ ipqos_msg(MT_ERROR,
+ gettext("Malformed value list "
+ "line %u.\n"), lineno);
+ free(tk);
+ return (IPQOS_CONF_ERR);
+ }
+
+ /*
+ * store anything after the comma for next
+ * iteration.
+ */
+ if (tk[1] != '\0') {
+ lo = malloc(strlen(&tk[1]) + 1);
+ if (lo == NULL) {
+ ipqos_msg(MT_ENOSTR, "malloc");
+ free(tk);
+ return (IPQOS_CONF_ERR);
+ }
+ (void) strcpy(lo, &tk[1]);
+ }
+
+ name_expected++;
+ }
+
+ free(tk);
+ }
+ }
+
+ IPQOSCDBG1(L1, "value returned is: %u\n", *val);
+
+ return (IPQOS_CONF_SUCCESS);
+}
+
+/*
+ * read the set of permanent classes/filter from the types file ref'd by tfp
+ * and store them in a string table pointed to by perm_items,
+ * with *nitems getting set to number of items read. perm_filters is set
+ * to 1 if we're searching for permanent filters, else 0 for classes.
+ * RETURNS: IPQOS_CONF_ERR if any errors, else IPQOS_CONF_SUCCESS.
+ */
+static int
+read_perm_items(
+int perm_filters,
+FILE *tfp,
+char *module_name,
+char ***perm_items,
+int *nitems)
+{
+
+ char lbuf[IPQOS_CONF_TYPE_LINE_LEN];
+ int cnt = 0;
+ char name[IPQOS_CONF_NAME_LEN+1];
+ char foo[IPQOS_CONF_NAME_LEN+1];
+ int res;
+ char **items = NULL;
+ char **tmp;
+ char *marker;
+
+ IPQOSCDBG0(L1, "In read_perm_items\n");
+
+
+ /* seek to start of types file */
+
+ if (fseek(tfp, 0, SEEK_SET) != 0) {
+ ipqos_msg(MT_ENOSTR, "fseek");
+ return (IPQOS_CONF_ERR);
+ }
+
+ /* select which marker were looking for */
+
+ if (perm_filters) {
+ marker = IPQOS_CONF_PERM_FILTER_MK;
+ } else {
+ marker = IPQOS_CONF_PERM_CLASS_MK;
+ }
+
+ /* scan file line by line till end */
+
+ while (fgets(lbuf, IPQOS_CONF_TYPE_LINE_LEN, tfp) != NULL) {
+
+ /*
+ * if the line is marked as containing a default item name
+ * read the name, extend the items string array
+ * and store the string off the array.
+ */
+ if (strncmp(lbuf, marker, strlen(marker)) == 0) {
+
+ res = sscanf(lbuf,
+ "%" VAL2STR(IPQOS_CONF_NAME_LEN) "s"
+ "%" VAL2STR(IPQOS_CONF_NAME_LEN) "s",
+ foo, name);
+ if (res < 2) {
+ ipqos_msg(MT_ERROR,
+ gettext("Types file for module %s is "
+ "corrupt.\n"), module_name);
+ IPQOSCDBG1(L0, "Missing name with a %s.\n",
+ marker);
+ goto fail;
+ }
+
+ /* extend items array to accomodate new item */
+
+ tmp = realloc(items, (cnt + 1) * sizeof (char *));
+ if (tmp == NULL) {
+ ipqos_msg(MT_ENOSTR, "realloc");
+ goto fail;
+ } else {
+ items = tmp;
+ }
+
+ /* copy and store item name */
+
+ items[cnt] = malloc(strlen(name) + 1);
+ if (items[cnt] == NULL) {
+ ipqos_msg(MT_ENOSTR, "malloc");
+ goto fail;
+ }
+
+ (void) strcpy(items[cnt], name);
+ cnt++;
+
+
+ IPQOSCDBG1(L1, "stored %s in perm items array\n",
+ name);
+ }
+ }
+
+ *perm_items = items;
+ *nitems = cnt;
+
+ return (IPQOS_CONF_SUCCESS);
+fail:
+ for (cnt--; cnt >= 0; cnt--)
+ free(items[cnt]);
+ free(items);
+ return (IPQOS_CONF_ERR);
+}
+
+/*
+ * Searches types file ref'd by tfp for the parameter named name
+ * with the place corresponding with place parameter. The format
+ * of the lines in the file are:
+ * PLACE NAME TYPE [ ENUM_DEF ] [ DEFAULT_STR ]
+ * The ENUM_DEF is an enumeration definition and is only present
+ * for parameters of type enum. DEFAULT_STR is a default value for
+ * this parameter. If present type is set to the appropriate type
+ * enumeration and dfltst filled with DEFAULT_STR if one was set.
+ * Also if the type is enum enum_nvps is made to point at a
+ * set of name value pairs representing ENUM_DEF.
+ *
+ * RETURNS: If any resource errors occur, or a matching parameter
+ * isn't found IPQOS_CONF_ERR is returned, else IPQOS_CONF_SUCCESS.
+ */
+static int
+readtype(
+FILE *tfp,
+char *module_name,
+char *name,
+ipqos_nvtype_t *type,
+str_val_nd_t **enum_nvps,
+char *dfltst,
+boolean_t allow_ipgpc_priv,
+place_t *place)
+{
+
+ int ac;
+ char lbuf[IPQOS_CONF_TYPE_LINE_LEN];
+ char param[IPQOS_CONF_PNAME_LEN+1];
+ char typest[IPQOS_CONF_TYPE_LEN+1];
+ char place_st[IPQOS_CONF_TYPE_LEN+1];
+ char *cp;
+ int x;
+ char *ipgpc_nm;
+ int found = 0;
+
+ IPQOSCDBG1(L1, "In readtype: param: %s\n", name);
+
+
+ /*
+ * if allow_ipgpc_priv is true then we allow ipgpc parameters that are
+ * private between ipqosconf and ipgpc. eg. address masks, port masks.
+ */
+ if (allow_ipgpc_priv && strcmp(module_name, IPGPC_NAME) == 0) {
+ ipgpc_nm = prepend_module_name(name, IPGPC_NAME);
+ if (ipgpc_nm == NULL) {
+ return (IPQOS_CONF_ERR);
+ }
+
+ if (strcmp(ipgpc_nm, IPGPC_SADDR_MASK) == 0 ||
+ strcmp(ipgpc_nm, IPGPC_DADDR_MASK) == 0) {
+ *type = IPQOS_DATA_TYPE_ADDRESS_MASK;
+ return (IPQOS_CONF_SUCCESS);
+ } else if (strcmp(ipgpc_nm, IPGPC_SPORT_MASK) == 0 ||
+ strcmp(ipgpc_nm, IPGPC_DPORT_MASK) == 0) {
+ *type = IPQOS_DATA_TYPE_UINT16;
+ return (IPQOS_CONF_SUCCESS);
+ } else if (strcmp(ipgpc_nm, IPGPC_FILTER_TYPE) == 0) {
+ *type = IPQOS_DATA_TYPE_UINT32;
+ return (IPQOS_CONF_SUCCESS);
+ } else if (strcmp(ipgpc_nm, IPGPC_IF_INDEX) == 0) {
+ *type = IPQOS_DATA_TYPE_IFINDEX;
+ return (IPQOS_CONF_SUCCESS);
+ }
+
+ free(ipgpc_nm);
+ }
+
+ /*
+ * read upto and including module version line.
+ */
+ if (read_tfile_ver(tfp, IPQOS_MOD_STR, module_name) == -1)
+ return (IPQOS_CONF_ERR);
+
+
+ /*
+ * loop reading lines of the types file until named parameter
+ * found or EOF.
+ */
+ while (fgets(lbuf, IPQOS_CONF_TYPE_LINE_LEN, tfp) != NULL) {
+
+ /*
+ * check whether blank or commented line; if so skip
+ */
+ for (cp = lbuf; isspace(*cp) && *cp != '\0'; cp++) {}
+ if (*cp == '\0' || *cp == '#') {
+ continue;
+ }
+
+ dfltst[0] = '\0';
+
+ /*
+ * read place, param, type and if present default str
+ * from line.
+ */
+ ac = sscanf(lbuf,
+ "%" VAL2STR(IPQOS_CONF_TYPE_LEN) "s "
+ "%" VAL2STR(IPQOS_CONF_PNAME_LEN) "s "
+ "%" VAL2STR(IPQOS_CONF_TYPE_LEN) "s "
+ "%" VAL2STR(IPQOS_VALST_MAXLEN) "s",
+ place_st, param, typest, dfltst);
+ if (ac < 3) {
+ ipqos_msg(MT_ERROR,
+ gettext("Types file for module %s is corrupt.\n"),
+ module_name);
+ IPQOSCDBG0(L0, "sscanf failed to read 3 strings.\n");
+ return (IPQOS_CONF_ERR);
+ }
+
+ /*
+ * if the place and name match no need to look any further.
+ */
+ if ((*place == PL_ANY) ||
+ ((*place == PL_PARAMS) &&
+ strcmp(place_st, IPQOS_PLACE_PRM_STR) == 0) ||
+ ((*place == PL_FILTER) &&
+ strcmp(place_st, IPQOS_PLACE_FILTER_STR) == 0) ||
+ ((*place == PL_MAP) &&
+ strcmp(place_st, IPQOS_PLACE_MAP_STR) == 0)) {
+ if (strcmp(param, name) == 0) {
+ found++;
+ break;
+ }
+ }
+ }
+ if (found == 0) {
+ ipqos_msg(MT_ERROR,
+ gettext("Invalid parameter, %s, line %u.\n"), name,
+ lineno);
+ return (IPQOS_CONF_ERR);
+ }
+
+ /*
+ * set the place parameter to the actual place when the PL_ANY flag
+ * was set.
+ */
+ if (*place == PL_ANY) {
+ if (strcmp(place_st, IPQOS_PLACE_PRM_STR) == 0) {
+ *place = PL_PARAMS;
+ } else if (strcmp(place_st, IPQOS_PLACE_FILTER_STR) == 0) {
+ *place = PL_FILTER;
+ } else if (strcmp(place_st, IPQOS_PLACE_MAP_STR) == 0) {
+ *place = PL_MAP;
+ }
+ }
+
+ /*
+ * get type enumeration
+ */
+ for (x = 0; nv_types[x].string[0]; x++) {
+ if (strcmp(nv_types[x].string, typest) == 0) {
+ break;
+ }
+ }
+ /*
+ * check that we have a type corresponding with the one the types
+ * file specifies.
+ */
+ if (nv_types[x].string[0] == '\0') {
+ ipqos_msg(MT_ERROR,
+ gettext("Types file for module %s is corrupt.\n"),
+ module_name);
+ return (IPQOS_CONF_ERR);
+ }
+ *type = nv_types[x].value;
+
+ /*
+ * if enumeration type get set of name/vals and any default value
+ */
+ if (*type == IPQOS_DATA_TYPE_ENUM) {
+ *enum_nvps = read_enum_nvs(lbuf, module_name);
+ if (*enum_nvps == NULL) {
+ return (IPQOS_CONF_ERR);
+ }
+
+ dfltst[0] = '\0';
+ cp = strchr(lbuf, CURL_END);
+ (void) sscanf(++cp,
+ "%" VAL2STR(IPQOS_VALST_MAXLEN) "s", dfltst);
+ }
+
+
+ IPQOSCDBG2(L1, "read type: %s default: %s\n", nv_types[x].string,
+ *dfltst ? dfltst : "None");
+ return (IPQOS_CONF_SUCCESS);
+}
+
+
+/*
+ * Reads a name and a value from file ref'd by cfp into list indirectly
+ * ref'd by nvlp; If this list is NULL it will be created to accomodate
+ * the name/value. The name must be either a special token for
+ * for the place, or be present in the module types file ref'd by tfp.
+ * *type is set to the enumeration of the type of the parameter and
+ * nvp to point at the element with the nvlp ref'd list.
+ * RETURNS: IPQOS_CONF_CURL_END if read CURL_END as name,
+ * IPQOS_CONF_ERR on errors, else IPQOS_CONF_SUCCESS.
+ */
+static int
+readnvpair(
+FILE *cfp,
+FILE *tfp,
+nvlist_t **nvlp,
+nvpair_t **nvp,
+ipqos_nvtype_t *type,
+place_t place,
+char *module_name)
+{
+
+ char *name = NULL;
+ char *valst = NULL;
+ int res;
+ char *tmp;
+ str_val_nd_t *enum_nvs = NULL;
+ char dfltst[IPQOS_VALST_MAXLEN+1];
+
+ IPQOSCDBG0(L1, "in readnvpair\n");
+
+ /*
+ * read nvpair name
+ */
+ res = readtoken(cfp, &name);
+
+ /*
+ * if reached eof, curl end or error encountered return to caller
+ */
+ if (res == IPQOS_CONF_EOF) {
+ ipqos_msg(MT_ERROR, gettext("Unexpected EOF.\n"));
+ return (IPQOS_CONF_ERR);
+ } else if (res == IPQOS_CONF_ERR) {
+ return (res);
+ } else if (res == IPQOS_CONF_CURL_END) {
+ free(name);
+ return (res);
+ }
+
+ /*
+ * read nvpair value
+ */
+ res = readtoken(cfp, &valst);
+
+ /*
+ * check we've read a valid value
+ */
+ if (res != IPQOS_CONF_SUCCESS && res != IPQOS_CONF_CURL_BEGIN) {
+ if (res == IPQOS_CONF_EOF) {
+ ipqos_msg(MT_ERROR, gettext("Unexpected EOF.\n"));
+ } else if (res == IPQOS_CONF_CURL_END) {
+ ipqos_msg(MT_ERROR,
+ gettext("Missing parameter value line %u.\n"),
+ lineno);
+ free(valst);
+ } /* we do nothing special for IPQOS_CONF_ERR */
+ free(name);
+ return (IPQOS_CONF_ERR);
+ }
+
+ /*
+ * check for generic parameters.
+ */
+
+ if ((place == PL_CLASS) &&
+ strcmp(name, IPQOS_CONF_NEXT_ACTION_STR) == 0) {
+ *type = IPQOS_DATA_TYPE_ACTION;
+
+ } else if (place == PL_PARAMS &&
+ strcmp(name, IPQOS_CONF_GLOBAL_STATS_STR) == 0 ||
+ place == PL_CLASS &&
+ strcmp(name, IPQOS_CONF_STATS_ENABLE_STR) == 0) {
+ *type = IPQOS_DATA_TYPE_BOOLEAN;
+
+ } else if (tfp == NULL ||
+ ((place != PL_PARAMS) && strcmp(name, IPQOS_CONF_NAME_STR) == 0) ||
+ (place == PL_FILTER) && (strcmp(name, IPQOS_CONF_CLASS_STR) ==
+ 0) ||
+ (place == PL_ACTION) && (strcmp(name, IPQOS_CONF_MODULE_STR) ==
+ 0)) {
+ *type = IPQOS_DATA_TYPE_STRING;
+
+ } else { /* if not generic parameter */
+ /*
+ * get type from types file
+ */
+ if (readtype(tfp, module_name, name, type, &enum_nvs, dfltst,
+ B_FALSE, &place) != IPQOS_CONF_SUCCESS) {
+ free(name);
+ free(valst);
+ return (IPQOS_CONF_ERR);
+ }
+
+ /*
+ * get full module prefix parameter name
+ */
+ tmp = name;
+ if ((name = prepend_module_name(name, module_name)) == NULL) {
+ name = tmp;
+ goto fail;
+ }
+ free(tmp);
+ }
+
+ IPQOSCDBG3(L1, "NVP, name: %s, str_value: %s, type: %s\n", name,
+ valst, nv_types[*type].string);
+
+
+ /*
+ * create nvlist if not present already
+ */
+ if (*nvlp == NULL) {
+ res = nvlist_alloc(nvlp, NV_UNIQUE_NAME, 0);
+ if (res != 0) {
+ ipqos_msg(MT_ENOSTR, "nvlist_alloc");
+ free(name);
+ free(valst);
+ return (IPQOS_CONF_ERR);
+ }
+ }
+
+ /*
+ * check we haven't already read this parameter
+ */
+ if (find_nvpair(*nvlp, name)) {
+ ipqos_msg(MT_ERROR, gettext("Duplicate parameter line %u.\n"),
+ lineno);
+ goto fail;
+ }
+
+ /*
+ * convert value string to appropriate type and add to nvlist
+ */
+
+ switch (*type) {
+ case IPQOS_DATA_TYPE_IFNAME: {
+ uint32_t ifidx;
+
+ res = readifindex(valst, (int *)&ifidx);
+ if (res == IPQOS_CONF_SUCCESS) {
+ res = nvlist_add_uint32(*nvlp, IPGPC_IF_INDEX,
+ ifidx);
+ if (res != 0) {
+ ipqos_msg(MT_ENOSTR,
+ "nvlist_add_uint32");
+ goto fail;
+ }
+ (void) nvlist_remove_all(*nvlp, name);
+ /*
+ * change name to point at the name of the
+ * new ifindex nvlist entry as name is used
+ * later in the function.
+ */
+ free(name);
+ name = malloc(strlen(IPGPC_IF_INDEX) + 1);
+ if (name == NULL) {
+ ipqos_msg(MT_ENOSTR, "malloc");
+ goto fail;
+ }
+ (void) strcpy(name, IPGPC_IF_INDEX);
+ }
+ break;
+ }
+ case IPQOS_DATA_TYPE_PROTO: {
+ uint8_t proto;
+
+ res = readproto(valst, &proto);
+ if (res == IPQOS_CONF_SUCCESS) {
+ res = nvlist_add_byte(*nvlp, name, proto);
+ if (res != 0) {
+ ipqos_msg(MT_ENOSTR, "nvlist_add_byte");
+ goto fail;
+ }
+ }
+ break;
+ }
+ case IPQOS_DATA_TYPE_PORT: {
+ uint16_t port;
+
+ res = readport(valst, &port);
+ if (res == IPQOS_CONF_SUCCESS) {
+
+ /* add port */
+
+ res = nvlist_add_uint16(*nvlp, name, port);
+ if (res != 0) {
+ ipqos_msg(MT_ENOSTR,
+ "nvlist_add_uint16");
+ goto fail;
+ }
+
+ /* add appropriate all ones port mask */
+
+ if (strcmp(name, IPGPC_DPORT) == 0) {
+ res = nvlist_add_uint16(*nvlp,
+ IPGPC_DPORT_MASK, ~0);
+
+ } else if (strcmp(name, IPGPC_SPORT) == 0) {
+ res = nvlist_add_uint16(*nvlp,
+ IPGPC_SPORT_MASK, ~0);
+ }
+ if (res != 0) {
+ ipqos_msg(MT_ENOSTR,
+ "nvlist_add_uint16");
+ goto fail;
+ }
+ }
+ break;
+ }
+ case IPQOS_DATA_TYPE_ADDRESS:
+ case IPQOS_DATA_TYPE_ACTION:
+ case IPQOS_DATA_TYPE_STRING:
+ res = nvlist_add_string(*nvlp, name, valst);
+ if (res != 0) {
+ ipqos_msg(MT_ENOSTR, "nvlist_add_string");
+ goto fail;
+ }
+ break;
+ case IPQOS_DATA_TYPE_BOOLEAN: {
+ boolean_t b;
+
+ res = readbool(valst, &b);
+ if (res == IPQOS_CONF_SUCCESS) {
+ res = nvlist_add_uint32(*nvlp, name,
+ (uint32_t)b);
+ if (res != 0) {
+ ipqos_msg(MT_ENOSTR,
+ "nvlist_add_uint32");
+ goto fail;
+ }
+ }
+ break;
+ }
+ case IPQOS_DATA_TYPE_UINT8: {
+ uint8_t u8;
+
+ res = readuint8(valst, &u8, &tmp);
+ if (res == IPQOS_CONF_SUCCESS) {
+ res = nvlist_add_byte(*nvlp, name, u8);
+ if (res != 0) {
+ ipqos_msg(MT_ENOSTR, "nvlist_add_byte");
+ goto fail;
+ }
+ }
+ break;
+ }
+ case IPQOS_DATA_TYPE_INT16: {
+ int16_t i16;
+
+ res = readint16(valst, &i16, &tmp);
+ if (res == IPQOS_CONF_SUCCESS) {
+ res = nvlist_add_int16(*nvlp, name, i16);
+ if (res != 0) {
+ ipqos_msg(MT_ENOSTR,
+ "nvlist_add_int16");
+ goto fail;
+ }
+ }
+ break;
+ }
+ case IPQOS_DATA_TYPE_UINT16: {
+ uint16_t u16;
+
+ res = readuint16(valst, &u16, &tmp);
+ if (res == IPQOS_CONF_SUCCESS) {
+ res = nvlist_add_uint16(*nvlp, name, u16);
+ if (res != 0) {
+ ipqos_msg(MT_ENOSTR,
+ "nvlist_add_int16");
+ goto fail;
+ }
+ }
+ break;
+ }
+ case IPQOS_DATA_TYPE_INT32: {
+ int i32;
+
+ res = readint32(valst, &i32, &tmp);
+ if (res == IPQOS_CONF_SUCCESS) {
+ res = nvlist_add_int32(*nvlp, name, i32);
+ if (res != 0) {
+ ipqos_msg(MT_ENOSTR,
+ "nvlist_add_int32");
+ goto fail;
+ }
+ }
+ break;
+ }
+ case IPQOS_DATA_TYPE_UINT32: {
+ uint32_t u32;
+
+ res = readuint32(valst, &u32, &tmp);
+ if (res == IPQOS_CONF_SUCCESS) {
+ res = nvlist_add_uint32(*nvlp, name, u32);
+ if (res != 0) {
+ ipqos_msg(MT_ENOSTR,
+ "nvlist_add_uint32");
+ goto fail;
+ }
+ }
+ break;
+ }
+ case IPQOS_DATA_TYPE_ENUM: {
+ uint32_t val;
+
+ res = read_enum_value(cfp, valst, enum_nvs, &val);
+ if (res == IPQOS_CONF_SUCCESS) {
+ res = nvlist_add_uint32(*nvlp, name, val);
+ if (res != 0) {
+ ipqos_msg(MT_ENOSTR,
+ "nvlist_add_uint32");
+ goto fail;
+ }
+ } else {
+ goto fail;
+ }
+ break;
+ }
+ /*
+ * For now the dfltst contains a comma separated list of the
+ * type we need this parameter to be mapped to.
+ * read_mapped_values will fill in all the mapped parameters
+ * and their values in the nvlist.
+ */
+ case IPQOS_DATA_TYPE_M_INDEX: {
+ uint8_t u8;
+
+ res = readuint8(valst, &u8, &tmp);
+ if (res == IPQOS_CONF_SUCCESS) {
+ res = nvlist_add_byte(*nvlp, name, u8);
+ if (res != 0) {
+ ipqos_msg(MT_ENOSTR,
+ "nvlist_add_uint8");
+ goto fail;
+ }
+ } else {
+ *type = IPQOS_DATA_TYPE_UINT8;
+ break;
+ }
+ res = read_mapped_values(tfp, nvlp, module_name,
+ dfltst, u8);
+ if (res != IPQOS_CONF_SUCCESS) {
+ goto fail;
+ }
+ break;
+ }
+ case IPQOS_DATA_TYPE_INT_ARRAY: {
+ str_val_nd_t *arr_enum_nvs = NULL;
+ uint32_t size;
+ int llimit = 0, ulimit = 0;
+ int *arr;
+
+ /*
+ * read array info from types file.
+ */
+ res = read_int_array_info(dfltst, &arr_enum_nvs, &size,
+ &llimit, &ulimit, module_name);
+ if (res != IPQOS_CONF_SUCCESS) {
+ goto fail;
+ }
+
+ /*
+ * read array contents from config file and construct
+ * array with them.
+ */
+ res = read_int_array(cfp, valst, &arr, size, llimit,
+ ulimit, arr_enum_nvs);
+ if (res != IPQOS_CONF_SUCCESS) {
+ goto fail;
+ }
+
+ /*
+ * add array to nvlist.
+ */
+ res = nvlist_add_int32_array(*nvlp, name, arr, size);
+ if (res != 0) {
+ ipqos_msg(MT_ENOSTR, "nvlist_add_int32");
+ goto fail;
+ }
+
+ /*
+ * free uneeded resources.
+ */
+ free(arr);
+ if (arr_enum_nvs)
+ free_str_val_entrys(arr_enum_nvs);
+
+ break;
+ }
+ case IPQOS_DATA_TYPE_USER: {
+ uid_t uid;
+
+ res = readuser(valst, &uid);
+ if (res == IPQOS_CONF_SUCCESS) {
+ res = nvlist_add_int32(*nvlp, name, (int)uid);
+ if (res != 0) {
+ ipqos_msg(MT_ENOSTR,
+ "nvlist_add_int32");
+ goto fail;
+ }
+ }
+ break;
+ }
+#ifdef _IPQOS_CONF_DEBUG
+ default: {
+ /*
+ * we shouldn't have a type that doesn't have a switch
+ * entry.
+ */
+ assert(1);
+ }
+#endif
+ }
+ if (res != 0) {
+ ipqos_msg(MT_ERROR, gettext("Invalid %s, line %u.\n"),
+ nv_types[*type].string, lineno);
+ goto fail;
+ }
+
+ /* set the nvp parameter to point at the newly added nvlist entry */
+
+ *nvp = find_nvpair(*nvlp, name);
+
+ free(name);
+ free(valst);
+ if (enum_nvs)
+ free_str_val_entrys(enum_nvs);
+ return (IPQOS_CONF_SUCCESS);
+fail:
+ if (name != NULL)
+ free(name);
+ if (valst != NULL)
+ free(valst);
+ if (enum_nvs != NULL)
+ free_str_val_entrys(enum_nvs);
+ return (IPQOS_CONF_ERR);
+}
+
+/*
+ * read a parameter clause from cfp into *params.
+ * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
+ */
+static int
+readparams(
+FILE *cfp,
+FILE *tfp,
+char *module_name,
+ipqos_conf_params_t *params)
+{
+
+ int res;
+ nvpair_t *nvp;
+ ipqos_nvtype_t type;
+ boolean_t bl;
+ char *nm;
+ char *action;
+ char tmp[IPQOS_CONF_PNAME_LEN];
+ int read_stats = 0;
+
+ IPQOSCDBG0(L0, "in readparams\n");
+
+ /* read beginning curl */
+
+ res = read_curl_begin(cfp);
+ if (res != IPQOS_CONF_SUCCESS) {
+ return (res);
+ }
+
+ /*
+ * loop reading nvpairs, adding to params nvlist until encounter
+ * CURL_END.
+ */
+ for (;;) {
+ /* read nvpair */
+
+ res = readnvpair(cfp, tfp, &params->nvlist,
+ &nvp, &type, PL_PARAMS, module_name);
+ if (res == IPQOS_CONF_ERR) {
+ goto fail;
+
+ /* we have finished reading params */
+
+ } else if (res == IPQOS_CONF_CURL_END) {
+ break;
+ }
+
+ /*
+ * read global stats - place into params struct and remove
+ * from nvlist.
+ */
+ if (strcmp(nvpair_name(nvp), IPQOS_CONF_GLOBAL_STATS_STR) ==
+ 0) {
+ /* check we haven't read stats before */
+
+ if (read_stats) {
+ ipqos_msg(MT_ERROR,
+ gettext("Duplicate parameter line %u.\n"),
+ lineno);
+ goto fail;
+ }
+ read_stats++;
+
+ (void) nvpair_value_uint32(nvp, (uint32_t *)&bl);
+ params->stats_enable = bl;
+ (void) nvlist_remove_all(params->nvlist,
+ IPQOS_CONF_GLOBAL_STATS_STR);
+
+
+ /*
+ * read action type parameter - add it to list of action refs.
+ * also, if it's one of continue or drop virtual actions
+ * change the action name to their special ipp names in
+ * the action ref list and the nvlist.
+ */
+ } else if (type == IPQOS_DATA_TYPE_ACTION) {
+
+ /* get name and value from nvlist */
+
+ nm = nvpair_name(nvp);
+ (void) nvpair_value_string(nvp, &action);
+
+ /* if virtual action names change to ipp name */
+
+ if ((strcmp(action, IPQOS_CONF_CONT_STR) == 0) ||
+ strcmp(action, IPQOS_CONF_DROP_STR) == 0) {
+ /*
+ * we copy nm to a seperate buffer as nv_pair
+ * name above gave us a ptr to internal
+ * memory which causes strange behaviour
+ * when we re-value that nvlist element.
+ */
+ (void) strlcpy(tmp, nm, sizeof (tmp));
+ nm = tmp;
+
+
+ /* modify nvlist entry and change action */
+
+ if (strcmp(action, IPQOS_CONF_CONT_STR) == 0) {
+ action = IPP_ANAME_CONT;
+ res = nvlist_add_string(params->nvlist,
+ nm, action);
+ } else {
+ action = IPP_ANAME_DROP;
+ res = nvlist_add_string(params->nvlist,
+ nm, action);
+ }
+ if (res != 0) {
+ ipqos_msg(MT_ENOSTR,
+ "nvlist_add_string");
+ goto fail;
+ }
+ }
+
+ /* add action reference to params */
+
+ res = add_aref(&params->actions, nm, action);
+ }
+ }
+
+ return (IPQOS_CONF_SUCCESS);
+fail:
+
+ if (params->nvlist) {
+ nvlist_free(params->nvlist);
+ params->nvlist = NULL;
+ }
+ if (params->actions) {
+ free_arefs(params->actions);
+ params->actions = NULL;
+ }
+ return (IPQOS_CONF_ERR);
+}
+
+/* ************************* class manip fns ****************************** */
+
+
+
+/*
+ * make dst point at a dupicate class struct with duplicate elements to src.
+ * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
+ */
+static int
+dup_class(
+ipqos_conf_class_t *src,
+ipqos_conf_class_t **dst)
+{
+
+ ipqos_conf_class_t *cls;
+ int res;
+
+ IPQOSCDBG1(DIFF, "In dup_class: class: %s\n", src->name);
+ cls = alloc_class();
+ if (cls == NULL) {
+ return (IPQOS_CONF_ERR);
+ }
+
+ /* struct copy */
+ *cls = *src;
+
+ /* we're not interested in the nvlist for a class */
+ cls->nvlist = NULL;
+
+
+ /* copy first action reference */
+ cls->alist = NULL;
+ res = add_aref(&cls->alist, src->alist->field, src->alist->name);
+ if (res != IPQOS_CONF_SUCCESS) {
+ free(cls);
+ return (res);
+ }
+
+ *dst = cls;
+
+ return (IPQOS_CONF_SUCCESS);
+}
+
+/*
+ * create a zero'd class struct and return a ptr to it.
+ * RETURNS: ptr to struct on success, NULL otherwise.
+ */
+static ipqos_conf_class_t *
+alloc_class()
+{
+
+ ipqos_conf_class_t *class;
+
+ class = malloc(sizeof (ipqos_conf_class_t));
+ if (class) {
+ bzero(class, sizeof (ipqos_conf_class_t));
+ } else {
+ ipqos_msg(MT_ENOSTR, "malloc");
+ }
+
+ return (class);
+}
+
+/* frees up all memory occupied by a filter struct and its contents. */
+static void
+free_class(ipqos_conf_class_t *cls)
+{
+
+ if (cls == NULL)
+ return;
+
+ /* free its nvlist if present */
+
+ if (cls->nvlist)
+ nvlist_free(cls->nvlist);
+
+ /* free its action refs if present */
+
+ if (cls->alist)
+ free_arefs(cls->alist);
+
+ /* finally free class itself */
+ free(cls);
+}
+
+/*
+ * Checks whether there is a class called class_nm in classes list.
+ * RETURNS: ptr to first matched class, else if not matched NULL.
+ */
+static ipqos_conf_class_t *
+classexist(
+char *class_nm,
+ipqos_conf_class_t *classes)
+{
+
+ ipqos_conf_class_t *cls;
+
+ IPQOSCDBG1(L1, "In classexist: name: %s\n", class_nm);
+
+ for (cls = classes; cls; cls = cls->next) {
+ if (strcmp(class_nm, cls->name) == 0) {
+ break;
+ }
+ }
+
+ return (cls);
+}
+
+
+
+/* ************************** filter manip fns **************************** */
+
+
+
+/*
+ * Checks whether there is a filter called filter_nm with instance number
+ * instance in filters list created by us or permanent. Instance value -1
+ * is a wildcard.
+ * RETURNS: ptr to first matched filter, else if not matched NULL.
+ */
+static ipqos_conf_filter_t *
+filterexist(
+char *filter_nm,
+int instance,
+ipqos_conf_filter_t *filters)
+{
+
+ IPQOSCDBG2(L1, "In filterexist: name :%s, inst: %d\n", filter_nm,
+ instance);
+
+ while (filters) {
+ if (strcmp(filters->name, filter_nm) == 0 &&
+ (instance == -1 || filters->instance == instance) &&
+ (filters->originator == IPP_CONFIG_IPQOSCONF ||
+ filters->originator == IPP_CONFIG_PERMANENT)) {
+ break;
+ }
+ filters = filters->next;
+ }
+ return (filters);
+}
+
+/*
+ * allocate and zero a filter structure.
+ * RETURNS: NULL on error, else ptr to filter struct.
+ */
+static ipqos_conf_filter_t *
+alloc_filter()
+{
+
+ ipqos_conf_filter_t *flt;
+
+ flt = malloc(sizeof (ipqos_conf_filter_t));
+ if (flt) {
+ bzero(flt, sizeof (ipqos_conf_filter_t));
+ flt->instance = -1;
+ } else {
+ ipqos_msg(MT_ENOSTR, "malloc");
+ }
+
+ return (flt);
+}
+
+/* free flt and all it's contents. */
+
+static void
+free_filter(ipqos_conf_filter_t *flt)
+{
+
+ IPQOSCDBG2(L1, "In free_filter: filter: %s, inst: %d\n", flt->name,
+ flt->instance);
+
+ if (flt == NULL)
+ return;
+
+ if (flt->src_nd_name)
+ free(flt->src_nd_name);
+ if (flt->dst_nd_name)
+ free(flt->dst_nd_name);
+ if (flt->nvlist) {
+ nvlist_free(flt->nvlist);
+ }
+ free(flt);
+}
+
+/*
+ * makes a copy of ofilter and its contents and points nfilter at it. It
+ * also adds an instance number to the filter and if either saddr or
+ * daddr are non-null that address to the filters nvlist along with
+ * an all 1s address mask and the af.
+ * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
+ */
+static int
+dup_filter(
+ipqos_conf_filter_t *ofilter,
+ipqos_conf_filter_t **nfilter,
+int af,
+int inv6, /* if saddr or daddr set and v4 filter are they in v6 addr */
+void *saddr,
+void *daddr,
+int inst)
+{
+
+ ipqos_conf_filter_t *nf;
+ int res;
+ in6_addr_t v6addr;
+ in6_addr_t all_1s_v6;
+
+ IPQOSCDBG4(MHME, "In dup_filter: name: %s, af: %u, inv6: %u, ins: %d\n",
+ ofilter->name, af, inv6, inst);
+
+/* show src address and dst address if present */
+#ifdef _IPQOS_CONF_DEBUG
+ if (ipqosconf_dbg_flgs & MHME) {
+ char st[100];
+
+ if (saddr) {
+ (void) fprintf(stderr, "saddr: %s\n",
+ inet_ntop(inv6 ? AF_INET6 : AF_INET, saddr, st,
+ 100));
+ }
+
+ if (daddr) {
+ (void) fprintf(stderr, "daddr: %s\n",
+ inet_ntop(inv6 ? AF_INET6 : AF_INET, daddr, st,
+ 100));
+ }
+ }
+#endif /* _IPQOS_CONF_DEBUG */
+
+ /* init local v6 address to 0 */
+ (void) bzero(&v6addr, sizeof (in6_addr_t));
+
+ /* create an all 1s address for use as mask */
+ (void) memset(&all_1s_v6, ~0, sizeof (in6_addr_t));
+
+ /* create a new filter */
+
+ nf = alloc_filter();
+ if (nf == NULL) {
+ return (IPQOS_CONF_ERR);
+ }
+
+ /* struct copy old filter to new */
+ *nf = *ofilter;
+
+ /* copy src filters nvlist if there is one to copy */
+
+ if (ofilter->nvlist) {
+ res = nvlist_dup(ofilter->nvlist, &nf->nvlist, 0);
+ if (res != 0) {
+ ipqos_msg(MT_ENOSTR, "nvlist_dup");
+ goto fail;
+ }
+ }
+
+ /* copy src and dst node names if present */
+
+ if (ofilter->src_nd_name) {
+ nf->src_nd_name = malloc(strlen(ofilter->src_nd_name) + 1);
+ if (nf->src_nd_name == NULL) {
+ ipqos_msg(MT_ENOSTR, "malloc");
+ goto fail;
+ }
+ (void) strcpy(nf->src_nd_name, ofilter->src_nd_name);
+ }
+ if (ofilter->dst_nd_name) {
+ nf->dst_nd_name = malloc(strlen(ofilter->dst_nd_name) + 1);
+ if (nf->dst_nd_name == NULL) {
+ ipqos_msg(MT_ENOSTR, "malloc");
+ goto fail;
+ }
+ (void) strcpy(nf->dst_nd_name, ofilter->dst_nd_name);
+ }
+
+ /* add filter addresses type */
+
+ res = nvlist_add_byte(nf->nvlist, IPGPC_FILTER_TYPE,
+ af == AF_INET ? IPGPC_V4_FLTR : IPGPC_V6_FLTR);
+ if (res != 0) {
+ ipqos_msg(MT_ENOSTR, "nvlist_add_byte");
+ goto fail;
+ }
+ IPQOSCDBG1(MHME, "adding address type %s in dup filter\n",
+ af == AF_INET ? "AF_INET" : "AF_INET6");
+
+ /* add saddr if present */
+
+ if (saddr) {
+ if (af == AF_INET && !inv6) {
+ V4_PART_OF_V6(v6addr) = *(uint32_t *)saddr;
+ saddr = &v6addr;
+ }
+
+ /* add address and all 1's mask */
+
+ if (nvlist_add_uint32_array(nf->nvlist, IPGPC_SADDR,
+ (uint32_t *)saddr, 4) != 0 ||
+ nvlist_add_uint32_array(nf->nvlist, IPGPC_SADDR_MASK,
+ (uint32_t *)&all_1s_v6, 4) != 0) {
+ ipqos_msg(MT_ENOSTR, "nvlist_add_uint32_array");
+ goto fail;
+ }
+
+ }
+
+ /* add daddr if present */
+
+ if (daddr) {
+ if (af == AF_INET && !inv6) {
+ V4_PART_OF_V6(v6addr) = *(uint32_t *)daddr;
+ daddr = &v6addr;
+ }
+
+ /* add address and all 1's mask */
+
+ if (nvlist_add_uint32_array(nf->nvlist, IPGPC_DADDR,
+ (uint32_t *)daddr, 4) != 0 ||
+ nvlist_add_uint32_array(nf->nvlist, IPGPC_DADDR_MASK,
+ (uint32_t *)&all_1s_v6, 4) != 0) {
+ ipqos_msg(MT_ENOSTR, "nvlist_add_uint32_array");
+ goto fail;
+ }
+ }
+
+ /* add filter instance */
+
+ nf->instance = inst;
+
+ *nfilter = nf;
+ return (IPQOS_CONF_SUCCESS);
+fail:
+ free_filter(nf);
+ return (IPQOS_CONF_ERR);
+}
+
+
+
+/* ************************* action manip fns ********************** */
+
+
+
+/*
+ * create and zero action structure and a params structure hung off of it.
+ * RETURNS: ptr to allocated action on success, else NULL.
+ */
+static ipqos_conf_action_t *
+alloc_action()
+{
+
+ ipqos_conf_action_t *action;
+
+ action = (ipqos_conf_action_t *)malloc(sizeof (ipqos_conf_action_t));
+ if (action == NULL) {
+ ipqos_msg(MT_ENOSTR, "malloc");
+ return (action);
+ }
+ bzero(action, sizeof (ipqos_conf_action_t));
+
+ action->params = (ipqos_conf_params_t *)
+ malloc(sizeof (ipqos_conf_params_t));
+ if (action->params == NULL) {
+ free(action);
+ return (NULL);
+ }
+ bzero(action->params, sizeof (ipqos_conf_params_t));
+ action->params->stats_enable = B_FALSE;
+
+ return (action);
+}
+
+/*
+ * free all the memory used in all the actions in actions list.
+ */
+static void
+free_actions(
+ipqos_conf_action_t *actions)
+{
+
+ ipqos_conf_action_t *act = actions;
+ ipqos_conf_action_t *next;
+ ipqos_conf_filter_t *flt, *nf;
+ ipqos_conf_class_t *cls, *nc;
+
+ while (act != NULL) {
+ /* free parameters */
+
+ if (act->params != NULL) {
+ free_arefs(act->params->actions);
+ if (act->params->nvlist != NULL) {
+ nvlist_free(act->params->nvlist);
+ }
+ free(act->params);
+ }
+
+ /* free action nvlist */
+
+ if (act->nvlist != NULL)
+ free(act->nvlist);
+
+ /* free filters */
+
+ flt = act->filters;
+ while (flt != NULL) {
+ nf = flt->next;
+ free_filter(flt);
+ flt = nf;
+ }
+
+ /* free classes */
+
+ cls = act->classes;
+ while (cls != NULL) {
+ nc = cls->next;
+ free_class(cls);
+ cls = nc;
+ }
+
+ /* free permanent classes table */
+ cleanup_string_table(act->perm_classes, act->num_perm_classes);
+
+ /* free filters to retry */
+
+ flt = act->retry_filters;
+ while (flt != NULL) {
+ nf = flt->next;
+ free_filter(flt);
+ flt = nf;
+ }
+
+ /* free dependency pointers */
+ free_arefs(act->dependencies);
+
+ next = act->next;
+ free(act);
+ act = next;
+ }
+}
+
+/*
+ * Checks whether there is an action called action_name in actions list.
+ * RETURNS: ptr to first matched action, else if not matched NULL.
+ *
+ */
+static ipqos_conf_action_t *
+actionexist(
+char *action_name,
+ipqos_conf_action_t *actions)
+{
+
+ IPQOSCDBG1(L1, "In actionexist: name: %s\n", action_name);
+
+ while (actions) {
+ if (strcmp(action_name, actions->name) == 0) {
+ break;
+ }
+ actions = actions->next;
+ }
+
+ return (actions);
+}
+
+/* **************************** act ref manip fns ******************** */
+
+
+/*
+ * add an action reference element with parameter field and action
+ * action_name to arefs.
+ * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
+ */
+static int
+add_aref(
+ipqos_conf_act_ref_t **arefs,
+char *field,
+char *action_name)
+{
+
+ ipqos_conf_act_ref_t *aref;
+
+ IPQOSCDBG1(L1, "add_aref: action: %s.\n", action_name);
+
+ /* allocate zero'd aref */
+
+ aref = malloc(sizeof (ipqos_conf_act_ref_t));
+ if (aref == NULL) {
+ ipqos_msg(MT_ENOSTR, "malloc");
+ return (IPQOS_CONF_ERR);
+ }
+ (void) bzero(aref, sizeof (ipqos_conf_act_ref_t));
+
+ /* copy parameter name if present */
+
+ if (field)
+ (void) strlcpy(aref->field, field, IPQOS_CONF_PNAME_LEN);
+
+ /* copy action name */
+ (void) strlcpy(aref->name, action_name, IPQOS_CONF_NAME_LEN);
+
+ /* place at head of list */
+
+ aref->next = *arefs;
+ *arefs = aref;
+
+ return (IPQOS_CONF_SUCCESS);
+}
+
+/*
+ * free all the memory used by the action references in arefs.
+ */
+static void
+free_arefs(
+ipqos_conf_act_ref_t *arefs)
+{
+
+ ipqos_conf_act_ref_t *aref = arefs;
+ ipqos_conf_act_ref_t *next;
+
+ while (aref) {
+ if (aref->nvlist)
+ nvlist_free(aref->nvlist);
+ next = aref->next;
+ free(aref);
+ aref = next;
+ }
+}
+
+
+
+/* *************************************************************** */
+
+
+
+/*
+ * checks whether aname is a valid action name.
+ * RETURNS: IPQOS_CONF_ERR if invalid, else IPQOS_CONF_SUCCESS.
+ */
+static int
+valid_aname(char *aname)
+{
+
+ /*
+ * dissallow the use of the name of a virtual action, either
+ * the ipqosconf name, or the longer ipp names.
+ */
+ if (strcmp(aname, IPQOS_CONF_CONT_STR) == 0 ||
+ strcmp(aname, IPQOS_CONF_DEFER_STR) == 0 ||
+ strcmp(aname, IPQOS_CONF_DROP_STR) == 0 ||
+ virtual_action(aname)) {
+ ipqos_msg(MT_ERROR, gettext("Invalid action name line %u.\n"),
+ lineno);
+ return (IPQOS_CONF_ERR);
+ }
+
+ return (IPQOS_CONF_SUCCESS);
+}
+
+/*
+ * Opens a stream to the types file for module module_name (assuming
+ * that the file path is TYPES_FILE_DIR/module_name.types). if
+ * a file open failure occurs, *openerr is set to 1.
+ * RETURNS: NULL on error, else stream ptr to module types file.
+ */
+static FILE *
+validmod(
+char *module_name,
+int *openerr)
+{
+
+ FILE *fp;
+ char *path;
+
+ IPQOSCDBG1(L1, "In validmod: module_name: %s\n", module_name);
+
+ *openerr = 0;
+
+ /* create modules type file path */
+
+ path = malloc(strlen(TYPES_FILE_DIR) + strlen(module_name) +
+ strlen(".types") + 1);
+ if (path == NULL) {
+ ipqos_msg(MT_ENOSTR, "malloc");
+ return (NULL);
+ }
+ (void) strcpy(path, TYPES_FILE_DIR);
+ (void) strcat(path, module_name);
+ (void) strcat(path, ".types");
+
+
+ IPQOSCDBG1(L1, "opening file %s\n", path);
+
+ /* open stream to types file */
+
+ fp = fopen(path, "r");
+ if (fp == NULL) {
+ (*openerr)++;
+ }
+
+ free(path);
+ return (fp);
+}
+
+
+/*
+ * read a class clause from cfp into a class struct and point class at this.
+ * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
+ */
+static int
+readclass(
+FILE *cfp,
+char *module_name,
+ipqos_conf_class_t **class,
+char **perm_classes,
+int num_perm_classes)
+{
+
+ int nm, act;
+ int res;
+ nvpair_t *nvp;
+ ipqos_nvtype_t type;
+ char *name;
+ char *action;
+ int stats;
+
+ IPQOSCDBG0(L0, "in readclass\n");
+
+ /* create and zero class struct */
+
+ *class = alloc_class();
+ if (!*class) {
+ return (IPQOS_CONF_ERR);
+ }
+ (*class)->originator = IPP_CONFIG_IPQOSCONF;
+
+ /* get starting line for error reporting */
+ (*class)->lineno = lineno;
+
+ /* read curl_begin */
+
+ res = read_curl_begin(cfp);
+ if (res != IPQOS_CONF_SUCCESS) {
+ goto fail;
+ }
+
+ /* loop reading parameters till read curl_end */
+
+ stats = nm = act = 0;
+ for (;;) {
+ /* read nvpair */
+ res = readnvpair(cfp, NULL, &(*class)->nvlist,
+ &nvp, &type, PL_CLASS, module_name);
+ if (res == IPQOS_CONF_ERR) {
+ goto fail;
+
+ /* reached end of class clause */
+ } else if (res == IPQOS_CONF_CURL_END) {
+ break;
+ }
+
+ /*
+ * catch name and action nv pairs and stats if present
+ * and place values in class structure.
+ */
+
+ /* name */
+
+ if (nm == 0 &&
+ strcmp(nvpair_name(nvp), IPQOS_CONF_NAME_STR) == 0) {
+
+ (void) nvpair_value_string(nvp, &name);
+
+ if (valid_name(name) != IPQOS_CONF_SUCCESS) {
+ goto fail;
+ }
+ (void) strcpy((*class)->name, name);
+ nm++;
+
+ /* next action */
+
+ } else if (act == 0 &&
+ strcmp(nvpair_name(nvp), IPQOS_CONF_NEXT_ACTION_STR) == 0) {
+
+ (void) nvpair_value_string(nvp, &action);
+
+ /*
+ * if next action string continue string set action to
+ * IPP_ANAME_CONT, else if drop string IPP_ANAME_DROP
+ */
+ if (strcmp(action, IPQOS_CONF_CONT_STR) == 0) {
+ action = IPP_ANAME_CONT;
+ } else if (strcmp(action, IPQOS_CONF_DROP_STR) == 0) {
+ action = IPP_ANAME_DROP;
+ }
+
+ /* add an action reference to action list */
+
+ res = add_aref(&(*class)->alist,
+ IPQOS_CONF_NEXT_ACTION_STR, action);
+ if (res != IPQOS_CONF_SUCCESS) {
+ goto fail;
+ }
+ act++;
+
+ /* class stats enable */
+
+ } else if (stats == 0 &&
+ strcmp(nvpair_name(nvp), IPQOS_CONF_STATS_ENABLE_STR) ==
+ 0) {
+ boolean_t bl;
+
+ (void) nvpair_value_uint32(nvp, (uint32_t *)&bl);
+ (*class)->stats_enable = bl;
+
+ stats++;
+
+ /* no other / duplicate parameters allowed */
+
+ } else {
+ ipqos_msg(MT_ERROR,
+ gettext("Unexpected parameter line %u.\n"), lineno);
+ goto fail;
+ }
+ }
+ if (nm == 0 || act == 0) {
+ ipqos_msg(MT_ERROR,
+ gettext("Missing class name/next action before line %u.\n"),
+ lineno);
+ goto fail;
+ }
+
+ /* change class originator field to permanent if permanent class */
+
+ if (in_string_table(perm_classes, num_perm_classes, (*class)->name)) {
+ IPQOSCDBG1(L0, "Setting class %s as permanent.\n", (*class)->name);
+ (*class)->originator = IPP_CONFIG_PERMANENT;
+ }
+
+ return (IPQOS_CONF_SUCCESS);
+fail:
+ if (*class)
+ free_class(*class);
+ return (IPQOS_CONF_ERR);
+}
+
+/*
+ * This function assumes either src_nd_name or dst_node_nm are set in filter.
+ *
+ * Creates one of more copies of filter according to the ip versions
+ * requested (or assumed) and the resolution of the src and dst address
+ * node names if spec'd. If both node names are spec'd then a filter is
+ * created for each pair of addresses (one from each node name) that is
+ * compatible with the chosen address family, otherwise a filter copy is
+ * created for just each address of the single node name that is
+ * compatible.
+ * If filter->ip_versions has been set that is used to determine the
+ * af's we will create filters for, else if a numeric address was
+ * added the family of that will be used, otherwise we fall back
+ * to both v4 and v6 addresses.
+ *
+ * Any name lookup failures that occur are checked to see whether the failure
+ * was a soft or hard failure and the nlerr field of filter set accordingly
+ * before the error is returned.
+ *
+ * RETURNS: IPQOS_CONF_ERR on any error, else IPQOS_CONF_SUCCESS.
+ */
+
+static int
+domultihome(
+ipqos_conf_filter_t *filter,
+ipqos_conf_filter_t **flist,
+boolean_t last_retry)
+{
+
+ uint32_t ftype;
+ int v4 = 1, v6 = 1; /* default lookup family is v4 and v6 */
+ int saf, daf;
+ struct hostent *shp = NULL;
+ struct hostent *dhp = NULL;
+ in6_addr_t daddr, saddr;
+ int idx = 0;
+ ipqos_conf_filter_t *nfilter;
+ int res;
+ int ernum;
+ int in32b = 0;
+ char **sp, **dp;
+
+ IPQOSCDBG3(MHME, "In domultihome: filter: %s, src_node: %s, "
+ "dst_node: %s\n", filter->name,
+ (filter->src_nd_name ? filter->src_nd_name : "NULL"),
+ (filter->dst_nd_name ? filter->dst_nd_name : "NULL"));
+
+ /* check if we've read an ip_version request to get the versions */
+
+ if (filter->ip_versions != 0) {
+ v4 = VERSION_IS_V4(filter);
+ v6 = VERSION_IS_V6(filter);
+
+ /* otherwise check if we've read a numeric address and get versions */
+
+ } else if (nvlist_lookup_uint32(filter->nvlist, IPGPC_FILTER_TYPE,
+ &ftype) == 0) {
+ if (ftype == IPGPC_V4_FLTR) {
+ v6--;
+ } else {
+ v4--;
+ }
+ }
+
+ /* read saddrs if src node name */
+
+ if (filter->src_nd_name) {
+
+ /* v4 only address */
+
+ if (v4 && !v6) {
+ in32b++;
+ shp = getipnodebyname(filter->src_nd_name, AF_INET,
+ AI_ADDRCONFIG, &ernum);
+
+ /* v6 only */
+
+ } else if (v6 && !v4) {
+ shp = getipnodebyname(filter->src_nd_name, AF_INET6,
+ AI_DEFAULT, &ernum);
+
+ /* v4 and v6 */
+
+ } else if (v6 && v4) {
+ shp = getipnodebyname(filter->src_nd_name, AF_INET6,
+ AI_DEFAULT|AI_ALL, &ernum);
+ }
+
+#ifdef TESTING_RETRY
+if (!last_retry) {
+ filter->nlerr = IPQOS_LOOKUP_RETRY;
+ goto fail;
+}
+#endif
+
+ /*
+ * if lookup error determine whether it was a soft or hard
+ * failure and mark as such in filter.
+ */
+ if (shp == NULL) {
+ if (ernum != TRY_AGAIN) {
+ ipqos_msg(MT_ERROR, gettext("Failed to "
+ "resolve src host name for filter at "
+ "line %u, ignoring filter.\n"),
+ filter->lineno);
+ filter->nlerr = IPQOS_LOOKUP_FAIL;
+ } else {
+ if (last_retry) {
+ ipqos_msg(MT_ERROR, gettext("Failed "
+ "to resolve src host name for "
+ "filter at line %u, ignoring "
+ "filter.\n"), filter->lineno);
+ }
+ filter->nlerr = IPQOS_LOOKUP_RETRY;
+ }
+ goto fail;
+ }
+ }
+
+ /* read daddrs if dst node name */
+ if (filter->dst_nd_name) {
+
+ /* v4 only address */
+
+ if (v4 && !v6) {
+ in32b++;
+ dhp = getipnodebyname(filter->dst_nd_name, AF_INET,
+ AI_ADDRCONFIG, &ernum);
+
+ /* v6 only */
+
+ } else if (v6 && !v4) {
+ dhp = getipnodebyname(filter->dst_nd_name, AF_INET6,
+ AI_DEFAULT, &ernum);
+
+ /* v6 and v4 addresses */
+
+ } else {
+ dhp = getipnodebyname(filter->dst_nd_name, AF_INET6,
+ AI_DEFAULT|AI_ALL, &ernum);
+ }
+
+ if (dhp == NULL) {
+ if (ernum != TRY_AGAIN) {
+ ipqos_msg(MT_ERROR, gettext("Failed to "
+ "resolve dst host name for filter at "
+ "line %u, ignoring filter.\n"),
+ filter->lineno);
+ filter->nlerr = IPQOS_LOOKUP_FAIL;
+ } else {
+ if (last_retry) {
+ ipqos_msg(MT_ERROR, gettext("Failed "
+ "to resolve dst host name for "
+ "filter at line %u, ignoring "
+ "filter.\n"), filter->lineno);
+ }
+ filter->nlerr = IPQOS_LOOKUP_RETRY;
+ }
+ goto fail;
+ }
+ }
+
+ /*
+ * if src and dst node name, create set of filters; one for each
+ * src and dst address of matching types.
+ */
+ if (filter->src_nd_name && filter->dst_nd_name) {
+
+ for (sp = shp->h_addr_list; *sp != NULL; sp++) {
+ (void) bcopy(*sp, &saddr, shp->h_length);
+
+ /* get saddr family */
+
+ if (in32b || IN6_IS_ADDR_V4MAPPED(&saddr)) {
+ saf = AF_INET;
+ } else {
+ saf = AF_INET6;
+ }
+
+ for (dp = dhp->h_addr_list; *dp != NULL; dp++) {
+ (void) bcopy(*dp, &daddr, dhp->h_length);
+
+ /* get daddr family */
+
+ if (in32b || IN6_IS_ADDR_V4MAPPED(&daddr)) {
+ daf = AF_INET;
+ } else {
+ daf = AF_INET6;
+ }
+
+ /*
+ * if saddr and daddr same af duplicate
+ * filter adding addresses and new instance
+ * number and add to flist filter list.
+ */
+
+ if (daf == saf) {
+
+ res = dup_filter(filter, &nfilter, saf,
+ !in32b, &saddr, &daddr, ++idx);
+ if (res != IPQOS_CONF_SUCCESS) {
+ goto fail;
+ }
+ ADD_TO_LIST(flist, nfilter);
+ }
+ }
+ }
+
+ /* if src name only create set of filters, one for each node address */
+
+ } else if (filter->src_nd_name) {
+
+ for (sp = shp->h_addr_list; *sp != NULL; sp++) {
+ (void) bcopy(*sp, &saddr, shp->h_length);
+
+ /* get af */
+
+ if (in32b || IN6_IS_ADDR_V4MAPPED(&saddr)) {
+ saf = AF_INET;
+ } else {
+ saf = AF_INET6;
+ }
+
+
+ /*
+ * dup filter adding saddr and new instance num and
+ * add to flist filter list.
+ */
+ res = dup_filter(filter, &nfilter, saf, !in32b, &saddr,
+ NULL, ++idx);
+ if (res != IPQOS_CONF_SUCCESS) {
+ goto fail;
+ }
+
+ ADD_TO_LIST(flist, nfilter);
+
+ }
+
+ /* if dname only create set of filters, one for each node address */
+
+ } else {
+ for (dp = dhp->h_addr_list; *dp != NULL; dp++) {
+ (void) bcopy(*dp, &daddr, dhp->h_length);
+
+ /* get af */
+
+ if (in32b || IN6_IS_ADDR_V4MAPPED(&daddr)) {
+ daf = AF_INET;
+ } else {
+ daf = AF_INET6;
+ }
+
+ /*
+ * dup filter adding daddr and new instance num and
+ * add to flist filter list.
+ */
+ res = dup_filter(filter, &nfilter, daf, !in32b, NULL,
+ &daddr, ++idx);
+ if (res != IPQOS_CONF_SUCCESS) {
+ goto fail;
+ }
+
+ ADD_TO_LIST(flist, nfilter);
+ }
+ }
+
+ if (shp)
+ freehostent(shp);
+ if (dhp)
+ freehostent(dhp);
+ return (IPQOS_CONF_SUCCESS);
+fail:
+ /*
+ * should really clean up any filters that we have created,
+ * however, free_actions called from readaction will cleam them up.
+ */
+ if (shp)
+ freehostent(shp);
+ if (dhp)
+ freehostent(dhp);
+ return (IPQOS_CONF_ERR);
+}
+
+
+/*
+ * read a filter clause from cfp into a filter struct and point filter
+ * at this.
+ * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
+ */
+static int
+readfilter(
+FILE *cfp,
+FILE *tfp,
+char *module_name,
+ipqos_conf_filter_t **filter,
+char **perm_filters,
+int num_perm_filters)
+{
+
+ int res;
+ int nm, cls, ipv;
+ in6_addr_t mask;
+ char *addr_str;
+ char *sl = NULL;
+ in6_addr_t addr;
+ int sa;
+ struct hostent *hp;
+ int err_num;
+ int v4 = 0, v6 = 0;
+ uchar_t mlen;
+ char *tmp;
+ nvpair_t *nvp;
+ ipqos_nvtype_t type;
+ char *name;
+ char *class;
+ uchar_t b;
+ in6_addr_t v6addr;
+
+ IPQOSCDBG0(L0, "in readfilter\n");
+
+
+ /* create and zero filter struct */
+
+ *filter = alloc_filter();
+ if (*filter == NULL) {
+ return (IPQOS_CONF_ERR);
+ }
+ (*filter)->originator = IPP_CONFIG_IPQOSCONF;
+
+ /* get starting line for error reporting */
+ (*filter)->lineno = lineno;
+
+ /* read beginning curl */
+
+ res = read_curl_begin(cfp);
+ if (res != IPQOS_CONF_SUCCESS) {
+ goto fail;
+ }
+
+
+ /*
+ * loop reading nvpairs onto nvlist until encounter CURL_END
+ */
+ ipv = nm = cls = 0;
+ for (;;) {
+ /* read nvpair */
+
+ res = readnvpair(cfp, tfp, &(*filter)->nvlist,
+ &nvp, &type, PL_FILTER, module_name);
+ if (res == IPQOS_CONF_ERR) {
+ goto fail;
+
+ /* reached the end of filter definition */
+
+ } else if (res == IPQOS_CONF_CURL_END) {
+ break;
+ }
+
+ /*
+ * catch name and class and place value into filter
+ * structure.
+ */
+
+ /* read filter name */
+
+ if (strcmp(nvpair_name(nvp), IPQOS_CONF_NAME_STR) == 0) {
+ if (nm != 0) {
+ ipqos_msg(MT_ERROR,
+ gettext("Duplicate parameter line %u.\n"),
+ lineno);
+ goto fail;
+ }
+
+ (void) nvpair_value_string(nvp, &name);
+ if (valid_name(name) != IPQOS_CONF_SUCCESS) {
+ goto fail;
+ }
+
+ (void) strcpy((*filter)->name, name);
+ (void) nvlist_remove_all((*filter)->nvlist,
+ IPQOS_CONF_NAME_STR);
+ nm++;
+
+ /* read class name */
+
+ } else if (strcmp(nvpair_name(nvp), IPQOS_CONF_CLASS_STR) ==
+ 0) {
+ if (cls != 0) {
+ ipqos_msg(MT_ERROR,
+ gettext("Duplicate parameter line %u.\n"),
+ lineno);
+ goto fail;
+ }
+
+ if (nvpair_value_string(nvp, &class) != 0) {
+ ipqos_msg(MT_ENOSTR, "nvpair_value_string");
+ break;
+ }
+ if (valid_name(class) != IPQOS_CONF_SUCCESS) {
+ goto fail;
+ }
+ (void) strcpy((*filter)->class_name, class);
+ (void) nvlist_remove_all((*filter)->nvlist,
+ IPQOS_CONF_CLASS_STR);
+ cls++;
+
+ /*
+ * if a src or dst ip node name/address. For those that
+ * are determined to be addresses we convert them from
+ * strings here and add to the filter nvlist; for node names
+ * we add the name to the filter struct for readaction to
+ * process.
+ */
+ } else if (strcmp(nvpair_name(nvp), IPGPC_SADDR) == 0 ||
+ strcmp(nvpair_name(nvp), IPGPC_DADDR) == 0) {
+
+ sa = 0;
+
+ if (strcmp(nvpair_name(nvp), IPGPC_SADDR) == 0) {
+ sa++;
+ }
+
+ (void) nvpair_value_string(nvp, &addr_str);
+
+ /*
+ * get the address mask if present.
+ * make a copy so that the nvlist element that
+ * it is part of doesn't dissapear and causes probs.
+ */
+ sl = strchr(addr_str, '/');
+ if (sl) {
+ *sl = '\0';
+ tmp = malloc(strlen(++sl) + 1);
+ if (tmp == NULL) {
+ ipqos_msg(MT_ENOSTR, "malloc");
+ goto fail;
+ }
+ (void) strcpy(tmp, sl);
+ sl = tmp;
+ }
+
+
+ /* if a numeric address */
+
+ if (inet_pton(AF_INET, addr_str, &addr) == 1 ||
+ inet_pton(AF_INET6, addr_str, &addr) == 1) {
+
+ /* get address */
+
+ hp = getipnodebyname(addr_str, AF_INET6,
+ AI_DEFAULT, &err_num);
+ if (hp == NULL) {
+ ipqos_msg(MT_ENOSTR,
+ "getipnodebyname");
+ goto fail;
+ }
+
+ (void) bcopy(hp->h_addr_list[0], &v6addr,
+ hp->h_length);
+ freehostent(hp);
+
+ /* determine address type */
+
+ v4 = IN6_IS_ADDR_V4MAPPED(&v6addr);
+ if (!v4) {
+ v6++;
+ }
+
+ /*
+ * check any previous addresses have same
+ * version.
+ */
+ if (nvlist_lookup_byte((*filter)->nvlist,
+ IPGPC_FILTER_TYPE, &b) == 0) {
+ if (v4 && b != IPGPC_V4_FLTR ||
+ v6 && b != IPGPC_V6_FLTR) {
+ ipqos_msg(MT_ERROR,
+ gettext("Incompatible "
+ "address version line "
+ "%u.\n"), lineno);
+ goto fail;
+ }
+ }
+
+ /*
+ * check that if ip_version spec'd it
+ * corresponds.
+ */
+ if ((*filter)->ip_versions != 0) {
+ if (v4 && !VERSION_IS_V4(*filter) ||
+ v6 && !VERSION_IS_V6(*filter)) {
+ ipqos_msg(MT_ERROR,
+ gettext("Incompatible "
+ "address version line %u"
+ ".\n"), lineno);
+ goto fail;
+ }
+ }
+
+ /* add the address type */
+
+ res = nvlist_add_byte(
+ (*filter)->nvlist, IPGPC_FILTER_TYPE,
+ v4 ? IPGPC_V4_FLTR : IPGPC_V6_FLTR);
+ if (res != 0) {
+ ipqos_msg(MT_ENOSTR,
+ "nvlist_add_byte");
+ goto fail;
+ }
+
+ /* add address to list */
+
+ res = nvlist_add_uint32_array((*filter)->nvlist,
+ sa ? IPGPC_SADDR : IPGPC_DADDR,
+ (uint32_t *)&v6addr, 4);
+ if (res != 0) {
+ ipqos_msg(MT_ENOSTR,
+ "nvlist_add_uint32_array");
+ goto fail;
+ }
+
+
+ /*
+ * add mask entry in list.
+ */
+
+ if (sl) { /* have CIDR mask */
+ char *lo;
+ res = readuint8(sl, &mlen, &lo);
+ if (res != IPQOS_CONF_SUCCESS ||
+ v4 && mlen > 32 ||
+ !v4 && mlen > 128 ||
+ mlen == 0) {
+ ipqos_msg(MT_ERROR,
+ gettext("Invalid CIDR "
+ "mask line %u.\n"), lineno);
+ goto fail;
+ }
+ setmask(mlen, &mask,
+ v4 ? AF_INET : AF_INET6);
+ free(sl);
+ } else {
+ /* no CIDR mask spec'd - use all 1s */
+
+ (void) memset(&mask, ~0,
+ sizeof (in6_addr_t));
+ }
+ res = nvlist_add_uint32_array((*filter)->nvlist,
+ sa ? IPGPC_SADDR_MASK : IPGPC_DADDR_MASK,
+ (uint32_t *)&mask, 4);
+ if (res != 0) {
+ ipqos_msg(MT_ENOSTR,
+ "nvlist_add_uint32_arr");
+ goto fail;
+ }
+
+ /* inet_pton returns fail - we assume a node name */
+
+ } else {
+ /*
+ * doesn't make sense to have a mask
+ * with a node name.
+ */
+ if (sl) {
+ ipqos_msg(MT_ERROR,
+ gettext("Address masks aren't "
+ "allowed for host names line "
+ "%u.\n"), lineno);
+ goto fail;
+ }
+
+ /*
+ * store node name in filter struct for
+ * later resolution.
+ */
+ if (sa) {
+ (*filter)->src_nd_name =
+ malloc(strlen(addr_str) + 1);
+ (void) strcpy((*filter)->src_nd_name,
+ addr_str);
+ } else {
+ (*filter)->dst_nd_name =
+ malloc(strlen(addr_str) + 1);
+ (void) strcpy((*filter)->dst_nd_name,
+ addr_str);
+ }
+ }
+
+ /* ip_version enumeration */
+
+ } else if (strcmp(nvpair_name(nvp), IPQOS_CONF_IP_VERSION) ==
+ 0) {
+ /* check we haven't read ip_version before */
+ if (ipv) {
+ ipqos_msg(MT_ERROR,
+ gettext("Duplicate parameter line %u.\n"),
+ lineno);
+ goto fail;
+ }
+ ipv++;
+
+ /* get bitmask value */
+
+ (void) nvpair_value_uint32(nvp,
+ &(*filter)->ip_versions);
+
+ /*
+ * check that if either ip address is spec'd it
+ * corresponds.
+ */
+ if (v4 && !VERSION_IS_V4(*filter) ||
+ v6 && !VERSION_IS_V6(*filter)) {
+ ipqos_msg(MT_ERROR, gettext("Incompatible "
+ "address version line %u.\n"), lineno);
+ goto fail;
+ }
+
+ /* remove ip_version from nvlist */
+
+ (void) nvlist_remove_all((*filter)->nvlist,
+ IPQOS_CONF_IP_VERSION);
+ }
+ }
+ if (nm == 0 || cls == 0) {
+ ipqos_msg(MT_ERROR, gettext("Missing filter/class name "
+ "before line %u.\n"), lineno);
+ goto fail;
+ }
+
+ if (in_string_table(perm_filters, num_perm_filters, (*filter)->name)) {
+ IPQOSCDBG1(L0, "Setting filter %s as permanent.\n",
+ (*filter)->name);
+
+ (*filter)->originator = IPP_CONFIG_PERMANENT;
+ }
+
+ return (IPQOS_CONF_SUCCESS);
+fail:
+ if (*filter)
+ free_filter(*filter);
+ if (hp)
+ freehostent(hp);
+ if (sl)
+ free(sl);
+
+ return (IPQOS_CONF_ERR);
+}
+
+/*
+ * reads the curl begin token from cfp stream.
+ * RETURNS: IPQOS_CONF_ERR if not read successfully, else IPQOS_CONF_SUCCES.
+ */
+static int
+read_curl_begin(FILE *cfp)
+{
+
+ int res;
+ char *st;
+
+ res = readtoken(cfp, &st);
+
+ if (res != IPQOS_CONF_CURL_BEGIN) {
+ if (res == IPQOS_CONF_EOF) {
+ ipqos_msg(MT_ERROR, gettext("Unexpected EOF.\n"));
+
+ /* if CURL_END or something else */
+ } else if (res != IPQOS_CONF_ERR) {
+ free(st);
+ ipqos_msg(MT_ERROR, gettext("\'{\' missing at line "
+ "%u.\n"), lineno);
+ }
+ return (IPQOS_CONF_ERR);
+ }
+
+ free(st);
+ return (IPQOS_CONF_SUCCESS);
+}
+
+/*
+ * This function parses the parameter string version into a version of the
+ * form "%u.%u" (as a sscanf format string). It then encodes this into an
+ * int and returns this encoding.
+ * RETURNS: -1 if an invalid string, else the integer encoding.
+ */
+static int
+ver_str_to_int(
+char *version)
+{
+ uint32_t major, minor;
+ int ver;
+
+ if (sscanf(version, "%u.%u", &major, &minor) != 2) {
+ IPQOSCDBG0(L0, "Failed to process version number string\n");
+ return (-1);
+ }
+
+ ver = (int)((major * 10000) + minor);
+ return (ver);
+}
+
+/*
+ * This function scans through the stream fp line by line looking for
+ * a line beginning with version_tag and returns a integer encoding of
+ * the version following it.
+ *
+ * RETURNS: If the version definition isn't found or the version is not
+ * a valid version (%u.%u) then -1 is returned, else an integer encoding
+ * of the read version.
+ */
+static int
+read_tfile_ver(
+FILE *fp,
+char *version_tag,
+char *module_name)
+{
+ char lbuf[IPQOS_CONF_LINEBUF_SZ];
+ char buf[IPQOS_CONF_LINEBUF_SZ+1];
+ char buf2[IPQOS_CONF_LINEBUF_SZ+1];
+ int found = 0;
+ int version;
+
+ /*
+ * reset to file start
+ */
+ if (fseek(fp, 0, SEEK_SET) != 0) {
+ ipqos_msg(MT_ENOSTR, "fseek");
+ return (-1);
+ }
+
+ /*
+ * loop reading lines till found the one beginning with version_tag.
+ */
+ while (fgets(lbuf, IPQOS_CONF_LINEBUF_SZ, fp) != NULL) {
+ if ((sscanf(lbuf,
+ "%" VAL2STR(IPQOS_CONF_LINEBUF_SZ) "s"
+ "%" VAL2STR(IPQOS_CONF_LINEBUF_SZ) "s",
+ buf, buf2) == 2) &&
+ (strcmp(buf, version_tag) == 0)) {
+ found++;
+ break;
+ }
+ }
+ if (found == 0) {
+ ipqos_msg(MT_ERROR, gettext("Types file for module %s is "
+ "corrupt.\n"), module_name);
+ IPQOSCDBG1(L1, "Couldn't find %s in types file\n",
+ version_tag);
+ return (-1);
+ }
+
+ /*
+ * convert version string into int.
+ */
+ if ((version = ver_str_to_int(buf2)) == -1) {
+ ipqos_msg(MT_ERROR, gettext("Types file for module %s is "
+ "corrupt.\n"), module_name);
+ return (-1);
+ }
+
+ return (version);
+}
+
+/*
+ * read action clause and params/classes/filters clauses within and
+ * store in and hang off an action structure, and point action at it.
+ * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
+ */
+static int
+readaction(
+FILE *cfp,
+ipqos_conf_action_t **action)
+{
+
+ char *st;
+ FILE *tfp = NULL;
+ int nm, md;
+ int readprms = 0;
+ int res;
+ char *strval;
+ char *name;
+ nvpair_t *nvp;
+ ipqos_nvtype_t type;
+ ipqos_conf_filter_t *filter;
+ ipqos_conf_class_t *class;
+ int oe;
+ char **perm_filters;
+ int num_perm_filters;
+ int tf_fmt_ver;
+
+ IPQOSCDBG0(L0, "in readaction\n");
+
+ res = readtoken(cfp, &st);
+ if (res == IPQOS_CONF_ERR || res == IPQOS_CONF_EOF) {
+ return (res);
+ } else if (strcmp(st, IPQOS_CONF_ACTION_STR) != 0) {
+ ipqos_msg(MT_ERROR, gettext("Missing %s token line "
+ "%u.\n"), IPQOS_CONF_ACTION_STR, lineno);
+ free(st);
+ return (IPQOS_CONF_ERR);
+ }
+ free(st);
+
+ /* create action structure */
+
+ *action = alloc_action();
+ if (*action == NULL) {
+ return (IPQOS_CONF_ERR);
+ }
+ (*action)->params->originator = IPP_CONFIG_IPQOSCONF;
+
+
+ /* get starting line for error reporting */
+ (*action)->lineno = lineno;
+
+ /* read beginning curl */
+
+ res = read_curl_begin(cfp);
+ if (res != IPQOS_CONF_SUCCESS) {
+ goto fail;
+ }
+
+ /* loop till read both action name and module */
+
+ nm = md = 0;
+ do {
+ /* read nvpair */
+
+ res = readnvpair(cfp, NULL, &(*action)->nvlist, &nvp, &type,
+ PL_ACTION, NULL);
+ if (res == IPQOS_CONF_ERR) {
+ goto fail;
+
+ /* read curl_end */
+
+ } else if (res == IPQOS_CONF_CURL_END) {
+ if (nm == 0 || md == 0) {
+ ipqos_msg(MT_ERROR,
+ gettext("Missing action name/ module "
+ "before line %u.\n"), lineno);
+ goto fail;
+ }
+ }
+
+
+ /* store name and module in action structure */
+
+ name = nvpair_name(nvp);
+
+ /* read action name */
+
+ if (nm == 0 && strcmp(name, IPQOS_CONF_NAME_STR) == 0) {
+
+ (void) nvpair_value_string(nvp, &strval);
+
+ /* check name is valid */
+
+ if (valid_name(strval) != IPQOS_CONF_SUCCESS ||
+ valid_aname(strval) != IPQOS_CONF_SUCCESS) {
+ goto fail;
+ }
+
+ /* store and remove from list */
+
+ (void) strcpy((*action)->name, strval);
+ /* remove name from nvlist */
+ (void) nvlist_remove_all((*action)->nvlist,
+ IPQOS_CONF_NAME_STR);
+
+ nm++;
+
+ /* read module name */
+
+ } else if (md == 0 &&
+ strcmp(name, IPQOS_CONF_MODULE_STR) == 0) {
+ /*
+ * check that module has a type file and get
+ * open stream to it.
+ */
+ (void) nvpair_value_string(nvp, &strval);
+ if ((tfp = validmod(strval, &oe)) == NULL) {
+ if (oe) {
+ if (errno == ENOENT) {
+ ipqos_msg(MT_ERROR,
+ gettext("Invalid "
+ "module name line %u.\n"),
+ lineno);
+ } else {
+ ipqos_msg(MT_ENOSTR, "fopen");
+ }
+ }
+ goto fail;
+ }
+
+ /*
+ * move module name to action struct
+ */
+ (void) strlcpy((*action)->module, strval,
+ IPQOS_CONF_NAME_LEN);
+ (void) nvlist_remove_all((*action)->nvlist,
+ IPQOS_CONF_MODULE_STR);
+ md++;
+
+ /* duplicate/other parameter */
+
+ } else {
+ ipqos_msg(MT_ERROR,
+ gettext("Unexpected parameter line %u.\n"),
+ lineno);
+ goto fail;
+ }
+
+ } while (nm == 0 || md == 0);
+
+ /*
+ * check that if the ipgpc action it is named correctly
+ */
+ if ((strcmp((*action)->module, IPGPC_NAME) == 0) &&
+ (strcmp((*action)->name, IPGPC_CLASSIFY) != 0)) {
+ ipqos_msg(MT_ERROR,
+ gettext("%s action has incorrect name line %u.\n"),
+ IPGPC_NAME, (*action)->lineno);
+ goto fail;
+ }
+
+ /* get list of permanent classes */
+
+ res = read_perm_items(0, tfp, (*action)->module,
+ &(*action)->perm_classes, &(*action)->num_perm_classes);
+ if (res != IPQOS_CONF_SUCCESS) {
+ goto fail;
+ }
+
+ /* get list of permanent filters */
+
+ res = read_perm_items(1, tfp, (*action)->module,
+ &perm_filters, &num_perm_filters);
+ if (res != IPQOS_CONF_SUCCESS) {
+ goto fail;
+ }
+
+ /*
+ * get types file format version and check its supported.
+ */
+ if ((tf_fmt_ver = read_tfile_ver(tfp, IPQOS_FMT_STR,
+ (*action)->module)) == -1)
+ goto fail;
+ if (IPP_MAJOR_MODULE_VER(tf_fmt_ver) > 1 ||
+ IPP_MINOR_MODULE_VER(tf_fmt_ver) > 0) {
+ ipqos_msg(MT_ERROR, gettext("Types file for module %s is "
+ "incompatible.\n"), (*action)->module);
+ IPQOSCDBG0(L1, "Unsupported fmt major/minor version\n");
+ goto fail;
+ }
+
+ /*
+ * get module version
+ */
+ if (((*action)->module_version = read_tfile_ver(tfp, IPQOS_MOD_STR,
+ (*action)->module)) == -1)
+ goto fail;
+
+ /* read filter/class/params blocks until CURL_END */
+
+ for (;;) {
+ /* read token */
+ res = readtoken(cfp, &st);
+
+ if (res == IPQOS_CONF_ERR) {
+ goto fail;
+ } else if (res == IPQOS_CONF_EOF) {
+ ipqos_msg(MT_ERROR, gettext("Unexpected EOF.\n"));
+ goto fail;
+
+ /* read CURL_END - end of action definition */
+
+ } else if (res == IPQOS_CONF_CURL_END) {
+ free(st);
+ break;
+ }
+
+
+ /*
+ * read in either a filter/class or parameter block.
+ */
+
+ /* read filter */
+
+ if (strcmp(st, IPQOS_CONF_FILTER_STR) == 0) {
+ free(st);
+
+ res = readfilter(cfp, tfp, (*action)->module, &filter,
+ perm_filters, num_perm_filters);
+ if (res != IPQOS_CONF_SUCCESS) {
+ goto fail;
+ }
+
+ /*
+ * if we read a host name for either src or dst addr
+ * resolve the hostnames and create the appropriate
+ * number of filters.
+ */
+
+ if (filter->src_nd_name || filter->dst_nd_name) {
+
+ res = domultihome(filter, &(*action)->filters,
+ B_FALSE);
+ /*
+ * if a lookup fails and the filters
+ * marked as retry we add it to a list
+ * for another attempt later, otherwise
+ * it is thrown away.
+ */
+ if (res != IPQOS_CONF_SUCCESS) {
+
+ /* if not name lookup problem */
+
+ if (filter->nlerr == 0) {
+ free_filter(filter);
+ goto fail;
+
+ /* name lookup problem */
+
+ /*
+ * if intermitent lookup failure
+ * add to list of filters to
+ * retry later.
+ */
+ } else if (filter->nlerr ==
+ IPQOS_LOOKUP_RETRY) {
+ filter->nlerr = 0;
+ ADD_TO_LIST(
+ &(*action)->retry_filters,
+ filter);
+ /*
+ * for non-existing names
+ * ignore the filter.
+ */
+ } else {
+ free_filter(filter);
+ }
+
+ /* creation of new filters successful */
+
+ } else {
+ free_filter(filter);
+ }
+
+ /* non-node name filter */
+
+ } else {
+ ADD_TO_LIST(&(*action)->filters, filter);
+ }
+
+ /* read class */
+
+ } else if (strcmp(st, IPQOS_CONF_CLASS_STR) == 0) {
+ free(st);
+ res = readclass(cfp, (*action)->module, &class,
+ (*action)->perm_classes,
+ (*action)->num_perm_classes);
+ if (res != IPQOS_CONF_SUCCESS) {
+ goto fail;
+ }
+
+ ADD_TO_LIST(&(*action)->classes, class);
+
+ /* read params */
+
+ } else if (strcmp(st, IPQOS_CONF_PARAMS_STR) == 0) {
+ free(st);
+ if (readprms) {
+ ipqos_msg(MT_ERROR,
+ gettext("Second parameter clause not "
+ "supported line %u.\n"), lineno);
+ goto fail;
+ }
+ res = readparams(cfp, tfp, (*action)->module,
+ (*action)->params);
+ if (res != IPQOS_CONF_SUCCESS) {
+ goto fail;
+ }
+ readprms++;
+
+ /* something unexpected */
+ } else {
+ free(st);
+ ipqos_msg(MT_ERROR,
+ gettext("Params/filter/class clause expected "
+ "line %u.\n"), lineno);
+ goto fail;
+ }
+ }
+
+ (void) fclose(tfp);
+ return (IPQOS_CONF_SUCCESS);
+
+fail:
+ if (tfp)
+ (void) fclose(tfp);
+ if (*action) {
+ free_actions(*action);
+ *action = NULL;
+ }
+ return (IPQOS_CONF_ERR);
+}
+
+/*
+ * check that each of the actions in actions is uniquely named. If one isn't
+ * set *name to point at the name of the duplicate action.
+ * RETURNS: IPQOS_CONF_ERR if a non-unique action, else IPQOS_CONF_SUCCESS.
+ */
+static int
+actions_unique(ipqos_conf_action_t *actions, char **name)
+{
+
+ IPQOSCDBG0(L1, "In actions_unique.\n");
+
+ while (actions) {
+ if (actionexist(actions->name, actions->next)) {
+ *name = actions->name;
+ return (IPQOS_CONF_ERR);
+ }
+ actions = actions->next;
+ }
+
+ return (IPQOS_CONF_SUCCESS);
+}
+
+/*
+ * checks whether the action parameter is involved in an action cycle.
+ * RETURNS: 1 if involved in a cycle, 0 otherwise.
+ */
+static int
+in_cycle(
+ipqos_conf_action_t *action)
+{
+
+ ipqos_conf_act_ref_t *aref;
+ ipqos_conf_class_t *c;
+
+ IPQOSCDBG1(L0, "in_cycle: visiting action %s\n", action->name);
+
+
+ /* have we visited this action before? */
+
+ if (action->visited == INCYCLE_VISITED) {
+ action->visited = 0;
+ return (1);
+ }
+ action->visited = INCYCLE_VISITED;
+
+ /*
+ * recurse down the child actions of this action through the
+ * classes next action and parameter actions.
+ */
+
+ for (aref = action->params->actions; aref != NULL; aref = aref->next) {
+
+ /* skip virtual actions - they can't be in a cycle */
+
+ if (virtual_action(aref->name)) {
+ continue;
+ }
+
+ if (in_cycle(aref->action)) {
+ action->visited = 0;
+ return (1);
+ }
+ }
+
+ for (c = action->classes; c != NULL; c = c->next) {
+ aref = c->alist;
+
+ if (virtual_action(aref->name)) {
+ continue;
+ }
+
+ if (in_cycle(aref->action)) {
+ action->visited = 0;
+ return (1);
+ }
+ }
+
+ IPQOSCDBG0(L0, "in_cycle: return\n");
+ action->visited = 0;
+ return (0);
+}
+
+/*
+ * checks that the configuration in actions is a valid whole, that
+ * all actions are unique, all filters and classes are unique within
+ * their action, that classes referenced by filters exist and actions
+ * referenced by classes and params exist. Also checks that there are no root
+ * actions but ipgpc and that no actions are involved in cycles. As
+ * a consequence of checking that the actions exist two way pointers
+ * are created between the dependee and dependant actions.
+ *
+ * In the case the the userconf flag is zero only this link creation is
+ * set as we trust the kernel to return a valid configuration.
+ *
+ * RETURNS: IPQOS_CONF_ERR if config isn't valid, else IPQOS_CONF_SUCCESS.
+ *
+ */
+
+static int
+validconf(
+ipqos_conf_action_t *actions,
+int userconf) /* are we checking a conf file ? */
+{
+ char *name;
+ ipqos_conf_action_t *act;
+ int res;
+ ipqos_conf_action_t *dact;
+ ipqos_conf_filter_t *flt;
+ ipqos_conf_class_t *cls;
+ ipqos_conf_params_t *params;
+ ipqos_conf_act_ref_t *aref;
+
+ IPQOSCDBG0(L0, "In validconf\n");
+
+ /* check actions are unique */
+
+ if (userconf && actions_unique(actions, &name) != IPQOS_CONF_SUCCESS) {
+ ipqos_msg(MT_ERROR, gettext("Duplicate named action %s.\n"),
+ name);
+ return (IPQOS_CONF_ERR);
+ }
+
+ for (act = actions; act; act = act->next) {
+
+ /*
+ * check filters (for user land configs only).
+ * check they are unique in this action and their class exists.
+ */
+ if (userconf) {
+ for (flt = act->filters; flt; flt = flt->next) {
+
+ /* check unique name */
+
+ if (filterexist(flt->name, flt->instance,
+ flt->next)) {
+ ipqos_msg(MT_ERROR,
+ gettext("Duplicate named filter "
+ "%s in action %s.\n"), flt->name,
+ act->name);
+ return (IPQOS_CONF_ERR);
+ }
+
+ /*
+ * check existence of class and error if
+ * class doesn't exist and not a perm class
+ */
+
+ if (!classexist(flt->class_name,
+ act->classes)) {
+ if (!in_string_table(act->perm_classes,
+ act->num_perm_classes,
+ flt->class_name)) {
+ ipqos_msg(MT_ERROR,
+ gettext("Undefined "
+ "class in filter %s, "
+ "action %s.\n"), flt->name,
+ act->name);
+ return (IPQOS_CONF_ERR);
+ }
+ }
+ }
+ }
+
+ /* check classes */
+
+ for (cls = act->classes; cls; cls = cls->next) {
+
+ /* check if class name unique (userland only) */
+
+ if (userconf && classexist(cls->name, cls->next)) {
+ ipqos_msg(MT_ERROR,
+ gettext("Duplicate named class %s in "
+ "action %s.\n"), cls->name, act->name);
+ return (IPQOS_CONF_ERR);
+ }
+
+ /*
+ * virtual actions always exist so don't check for next
+ * action.
+ */
+ if (virtual_action(cls->alist->name)) {
+ continue;
+ }
+
+ /*
+ * check existance of next action and create link to
+ * it.
+ */
+ if ((cls->alist->action =
+ actionexist(cls->alist->name, actions)) == NULL) {
+ ipqos_msg(MT_ERROR,
+ gettext("Undefined action in class %s, "
+ "action %s.\n"), cls->name, act->name);
+ return (IPQOS_CONF_ERR);
+ }
+
+ /* create backwards link - used for deletions */
+
+ dact = cls->alist->action;
+ res = add_aref(&dact->dependencies, NULL, act->name);
+ if (res != IPQOS_CONF_SUCCESS) {
+ return (IPQOS_CONF_ERR);
+ }
+ dact->dependencies->action = act;
+ }
+
+
+ /* check actions exist for action type parameters */
+
+ params = act->params;
+ for (aref = params->actions; aref; aref = aref->next) {
+
+ /* skip virtuals */
+
+ if (virtual_action(aref->name)) {
+ continue;
+ }
+
+ /*
+ * check existance of action in this ref
+ * and if present create a ptr to it.
+ */
+ aref->action = actionexist(aref->name, actions);
+ if (aref->action == NULL) {
+ ipqos_msg(MT_ERROR,
+ gettext("Undefined action in parameter "
+ "%s, action %s.\n"),
+ SHORT_NAME(aref->field), act->name);
+ return (IPQOS_CONF_ERR);
+ }
+
+ /* create backwards link */
+
+ dact = aref->action;
+ res = add_aref(&dact->dependencies, NULL,
+ act->name);
+ if (res != IPQOS_CONF_SUCCESS) {
+ return (IPQOS_CONF_ERR);
+ }
+ dact->dependencies->action = act;
+ }
+ }
+
+ /* for kernel retrieved configs we don't do the following checks. */
+ if (!userconf) {
+ return (IPQOS_CONF_SUCCESS);
+ }
+
+ /* check for cycles in config and orphaned actions other than ipgpc */
+
+ for (act = actions; act; act = act->next) {
+
+ /* check if involved in cycle */
+
+ if (in_cycle(act)) {
+ ipqos_msg(MT_ERROR,
+ gettext("Action %s involved in cycle.\n"),
+ act->name);
+ return (IPQOS_CONF_ERR);
+ }
+
+ /* check that this action has a parent (except ipgpc) */
+
+ if (act->dependencies == NULL &&
+ strcmp(act->name, IPGPC_CLASSIFY) != 0) {
+ ipqos_msg(MT_ERROR, gettext("Action %s isn't "
+ "referenced by any other actions.\n"), act->name);
+ return (IPQOS_CONF_ERR);
+ }
+ }
+
+ return (IPQOS_CONF_SUCCESS);
+}
+
+/*
+ * Read the version from the config file with stream cfp with
+ * the tag version_tag. The tag-value pair should be the first tokens
+ * encountered.
+ *
+ * RETURNS: -1 if a missing or invalid version or a read error,
+ * else an integer encoding of the version.
+ */
+static int
+read_cfile_ver(
+FILE *cfp,
+char *version_tag)
+{
+ char *sp = NULL;
+ int res;
+ int version;
+
+ IPQOSCDBG0(L1, "In read_cfile_ver:\n");
+
+ /*
+ * read version tag string.
+ */
+ res = readtoken(cfp, &sp);
+ if (res != IPQOS_CONF_SUCCESS) {
+ goto fail;
+ } else if (strcasecmp(sp, version_tag) != 0) {
+ goto fail;
+ }
+ free(sp);
+ sp = NULL;
+
+ /*
+ * read version number string.
+ */
+ res = readtoken(cfp, &sp);
+ if (res != IPQOS_CONF_SUCCESS) {
+ goto fail;
+ }
+
+ /*
+ * encode version into int.
+ */
+ if ((version = ver_str_to_int(sp)) == -1) {
+ goto fail;
+ }
+ free(sp);
+
+ return (version);
+fail:
+ ipqos_msg(MT_ERROR,
+ gettext("Missing/Invalid config file %s.\n"), version_tag);
+ if (sp != NULL)
+ free(sp);
+ return (-1);
+}
+
+/*
+ * read the set of actions definitions from the stream cfp and store
+ * them in a list pointed to by conf.
+ * RETURNS: IPQOS_CONF_ERR if any errors, else IPQOS_CONF_SUCCESS.
+ */
+static int
+readconf(
+FILE *cfp,
+ipqos_conf_action_t **conf)
+{
+
+ int res;
+ ipqos_conf_action_t *action;
+ boolean_t ipgpc_action = B_FALSE;
+ int fmt_ver;
+
+ IPQOSCDBG0(L0, "In readconf\n");
+
+ *conf = NULL;
+
+ /*
+ * get config file format version.
+ */
+ fmt_ver = read_cfile_ver(cfp, IPQOS_FMT_VERSION_STR);
+ if (fmt_ver == -1) {
+ return (IPQOS_CONF_ERR);
+ } else {
+ /*
+ * check version is valid
+ */
+ if ((IPP_MAJOR_MODULE_VER(fmt_ver) > 1) ||
+ (IPP_MINOR_MODULE_VER(fmt_ver) > 0)) {
+ ipqos_msg(MT_ERROR, gettext("Unsupported config file "
+ "format version.\n"));
+ return (IPQOS_CONF_ERR);
+ }
+ }
+
+ /* loop reading actions adding to conf till EOF */
+
+ for (;;) {
+ action = NULL;
+
+ /* readaction */
+
+ res = readaction(cfp, &action);
+ if (res == IPQOS_CONF_ERR) {
+ goto fail;
+ }
+
+ /* reached eof, finish */
+
+ if (res == IPQOS_CONF_EOF) {
+ break;
+ }
+
+ ADD_TO_LIST(conf, action);
+
+ /* check if we just read an ipgpc action */
+
+ if (strcmp(action->name, IPGPC_CLASSIFY) == 0)
+ ipgpc_action = B_TRUE;
+ }
+
+ /* check that there is one or more actions and that one is ipgpc */
+
+ if (ipgpc_action == B_FALSE) {
+ ipqos_msg(MT_ERROR, gettext("No %s action defined.\n"),
+ IPGPC_NAME);
+ goto fail;
+ }
+
+ return (IPQOS_CONF_SUCCESS);
+fail:
+ free_actions(*conf);
+ *conf = NULL;
+ return (IPQOS_CONF_ERR);
+}
+
+/* ************************ kernel config retrieval ************************ */
+
+
+/*
+ * read the current configuration from the kernel and make *conf a ptr to it.
+ * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
+ */
+static int
+readkconf(ipqos_conf_action_t **conf)
+{
+
+ int res;
+ char **modnames = NULL;
+ int nmods;
+ char **actnames = NULL;
+ int nacts;
+ int x, y;
+ FILE *tfp;
+ int openerr;
+ ipqos_actinfo_prm_t ai_prm;
+
+
+ IPQOSCDBG0(L0, "In readkconf\n");
+
+ /* initialise conf to NULL */
+ *conf = NULL;
+
+ /* get list of modules currently loaded */
+
+ res = ipp_list_mods(&modnames, &nmods);
+ if (res != 0) {
+ ipqos_msg(MT_ENOSTR, "ipp_list_mods");
+ return (IPQOS_CONF_ERR);
+ }
+
+ /*
+ * iterate through all loaded modules retrieving their list of actions
+ * and then retrieving the configuration of each of these
+ * and attatching it to conf.
+ */
+ for (x = 0; x < nmods; x++) {
+
+ /* skip actions of modules that we can't open types file of */
+
+ if ((tfp = validmod(modnames[x], &openerr)) == NULL) {
+
+ /* mem error */
+
+ if (!openerr) {
+ goto fail;
+
+ /*
+ * fopen fail - if we failed because the file didn't
+ * exist we assume this is an unknown module and
+ * ignore this module, otherwise error.
+ */
+ } else {
+ if (errno == ENOENT) {
+ continue;
+ } else {
+ ipqos_msg(MT_ENOSTR, "fopen");
+ goto fail;
+ }
+ }
+ }
+ (void) fclose(tfp);
+
+ /* get action list for this module */
+
+ res = ipp_mod_list_actions(modnames[x], &actnames, &nacts);
+ if (res != 0) {
+ ipqos_msg(MT_ENOSTR, "ipp_mod_list_actions");
+ goto fail;
+ }
+
+ /* read config of each action of this module */
+
+ for (y = 0; y < nacts; y++) {
+ ai_prm.action = alloc_action();
+ if (ai_prm.action == NULL) {
+ goto fail;
+ }
+
+ /* copy action name into action struct */
+
+ (void) strlcpy(ai_prm.action->name, actnames[y],
+ IPQOS_CONF_NAME_LEN);
+
+ /* copy module name into action struct */
+
+ (void) strlcpy(ai_prm.action->module, modnames[x],
+ IPQOS_CONF_NAME_LEN);
+
+ /* get action info */
+
+ res = ipp_action_info(actnames[y],
+ (int (*)(nvlist_t *, void *))parse_kaction,
+ (void *)&ai_prm, 0);
+ if (res != 0) {
+ /* was this an ipp error */
+ if (ai_prm.intl_ret == IPQOS_CONF_SUCCESS) {
+ ipqos_msg(MT_ENOSTR,
+ "ipp_action_info");
+ }
+ goto fail;
+ }
+
+ ADD_TO_LIST(conf, ai_prm.action);
+ }
+
+ cleanup_string_table(actnames, nacts);
+ }
+
+ cleanup_string_table(modnames, nmods);
+ return (IPQOS_CONF_SUCCESS);
+fail:
+ free_actions(*conf);
+ *conf = NULL;
+ cleanup_string_table(modnames, nmods);
+ cleanup_string_table(actnames, nacts);
+ return (IPQOS_CONF_ERR);
+}
+
+/*
+ * This is passed as a parameter to ipp_action_info() in readkaction and
+ * is called back one for each configuration element within the action
+ * specified. This results in filters and classes being created and chained
+ * off of action, and action having its params set.
+ * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCESS.
+ */
+static int
+parse_kaction(
+nvlist_t *nvl,
+ipqos_actinfo_prm_t *ai_prm)
+{
+
+ int ret;
+ uint8_t cfgtype;
+ ipqos_conf_filter_t *filter = NULL;
+ ipqos_conf_class_t *class = NULL;
+ ipqos_conf_action_t *action = ai_prm->action;
+
+
+ IPQOSCDBG1(KRET, "In parse_kaction: action_name: %s\n", action->name);
+
+ /* get config type */
+
+ (void) nvlist_lookup_byte(nvl, IPP_CONFIG_TYPE, &cfgtype);
+ (void) nvlist_remove_all(nvl, IPP_CONFIG_TYPE);
+
+ switch (cfgtype) {
+ case CLASSIFIER_ADD_FILTER: {
+ /*
+ * parse the passed filter nvlist
+ * and add result to action's filter list.
+ */
+ filter = alloc_filter();
+ if (filter == NULL) {
+ ai_prm->intl_ret = IPQOS_CONF_ERR;
+ return (IPQOS_CONF_ERR);
+ }
+
+ ret = parse_kfilter(filter, nvl);
+ if (ret != IPQOS_CONF_SUCCESS) {
+ free_filter(filter);
+ ai_prm->intl_ret = IPQOS_CONF_ERR;
+ return (ret);
+ }
+
+ ADD_TO_LIST(&action->filters, filter);
+ break;
+ }
+ case CLASSIFIER_ADD_CLASS:
+ case CLASSIFIER_MODIFY_CLASS: {
+ /*
+ * parse the passed class nvlist
+ * and add result to action's class list.
+ */
+ class = alloc_class();
+ if (class == NULL) {
+ ai_prm->intl_ret = IPQOS_CONF_ERR;
+ return (IPQOS_CONF_ERR);
+ }
+
+ ret = parse_kclass(class, nvl);
+ if (ret != IPQOS_CONF_SUCCESS) {
+ free_class(class);
+ ai_prm->intl_ret = IPQOS_CONF_ERR;
+ return (ret);
+ }
+
+ ADD_TO_LIST(&action->classes, class);
+ break;
+ }
+ case IPP_SET: {
+ /*
+ * we don't alloc a params struct as it is created
+ * as part of an action.
+ */
+
+ /* parse the passed params nvlist */
+
+ ret = parse_kparams(action->module, action->params,
+ nvl);
+ if (ret != IPQOS_CONF_SUCCESS) {
+ ai_prm->intl_ret = IPQOS_CONF_ERR;
+ return (ret);
+ }
+ }
+ }
+
+ ai_prm->intl_ret = IPQOS_CONF_SUCCESS;
+ return (IPQOS_CONF_SUCCESS);
+}
+
+/*
+ * parses a params nvlist returned from the kernel.
+ * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
+ */
+int
+parse_kparams(
+char *module,
+ipqos_conf_params_t *params,
+nvlist_t *nvl) {
+
+ int ret;
+ ipqos_nvtype_t type;
+ str_val_nd_t *tmp;
+ char *act;
+ uint32_t u32;
+ nvpair_t *nvp;
+ FILE *tfp;
+ char dfltst[IPQOS_VALST_MAXLEN];
+ char *param;
+ nvlist_t *nvlcp;
+ int openerr;
+ place_t place;
+
+ IPQOSCDBG0(KRET, "In parse_kparams:\n");
+
+ /* get stream to module types file */
+
+ tfp = validmod(module, &openerr);
+ if (tfp == NULL) {
+ if (openerr) {
+ ipqos_msg(MT_ENOSTR, "fopen");
+ }
+ return (IPQOS_CONF_ERR);
+ }
+
+ /* make copy of passed in nvlist as it is freed by the caller */
+
+ ret = nvlist_dup(nvl, &nvlcp, 0);
+ if (ret != 0) {
+ return (IPQOS_CONF_ERR);
+ }
+
+ /*
+ * get config originator and remove from nvlist. If no owner we
+ * assume ownership.
+ */
+ ret = nvlist_lookup_uint32(nvlcp, IPP_CONFIG_ORIGINATOR, &u32);
+ if (ret == 0) {
+ params->originator = u32;
+ (void) nvlist_remove_all(nvlcp, IPP_CONFIG_ORIGINATOR);
+ } else {
+ params->originator = IPP_CONFIG_IPQOSCONF;
+ }
+
+ /* get action stats and remove from nvlist */
+
+ ret = nvlist_lookup_uint32(nvlcp, IPP_ACTION_STATS_ENABLE, &u32);
+ if (ret == 0) {
+ params->stats_enable = *(boolean_t *)&u32;
+ (void) nvlist_remove_all(nvlcp, IPP_ACTION_STATS_ENABLE);
+ }
+
+ /*
+ * loop throught nvlist elements and for those that are actions create
+ * action ref entrys for them.
+ */
+ nvp = nvlist_next_nvpair(nvlcp, NULL);
+ while (nvp != NULL) {
+ param = SHORT_NAME(nvpair_name(nvp));
+ place = PL_ANY;
+ ret = readtype(tfp, module, param, &type, &tmp, dfltst,
+ B_FALSE, &place);
+ if (ret != IPQOS_CONF_SUCCESS) {
+ goto fail;
+ }
+
+ if ((place == PL_PARAMS) && /* avoid map entries */
+ (type == IPQOS_DATA_TYPE_ACTION)) {
+ (void) nvpair_value_string(nvp, &act);
+ ret = add_aref(&params->actions, nvpair_name(nvp), act);
+ if (ret != IPQOS_CONF_SUCCESS) {
+ goto fail;
+ }
+ }
+
+ nvp = nvlist_next_nvpair(nvlcp, nvp);
+ }
+
+ /* assign copied nvlist to params struct */
+
+ params->nvlist = nvlcp;
+
+ (void) fclose(tfp);
+ return (IPQOS_CONF_SUCCESS);
+fail:
+ (void) fclose(tfp);
+ free_arefs(params->actions);
+ params->actions = NULL;
+ nvlist_free(nvlcp);
+ return (IPQOS_CONF_ERR);
+}
+
+/*
+ * parses a classes nvlist returned from the kernel.
+ * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
+ */
+static int
+parse_kclass(
+ipqos_conf_class_t *class,
+nvlist_t *nvl)
+{
+
+ int ret;
+ uint32_t u32;
+ char *str;
+
+ IPQOSCDBG0(KRET, "In parse_kclass:\n");
+
+ /* lookup object originator */
+
+ ret = nvlist_lookup_uint32(nvl, IPP_CONFIG_ORIGINATOR, &u32);
+ if (ret == 0) {
+ class->originator = u32;
+ } else {
+ class->originator = IPP_CONFIG_IPQOSCONF;
+ }
+
+ /* lookup name */
+
+ (void) nvlist_lookup_string(nvl, CLASSIFIER_CLASS_NAME, &str);
+ (void) strlcpy(class->name, str, IPQOS_CONF_NAME_LEN);
+ IPQOSCDBG1(KRET, "reading class %s\n", class->name);
+
+ /* lookup next action */
+
+ (void) nvlist_lookup_string(nvl, CLASSIFIER_NEXT_ACTION, &str);
+ ret = add_aref(&class->alist, NULL, str);
+ if (ret != IPQOS_CONF_SUCCESS) {
+ return (IPQOS_CONF_ERR);
+ }
+
+ /* lookup stats enable */
+
+ ret = nvlist_lookup_uint32(nvl, CLASSIFIER_CLASS_STATS_ENABLE, &u32);
+ if (ret == 0) {
+ class->stats_enable = *(boolean_t *)&u32;
+ }
+
+ return (IPQOS_CONF_SUCCESS);
+}
+
+/*
+ * parses a filters nvlist returned from the kernel.
+ * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
+ */
+static int
+parse_kfilter(
+ipqos_conf_filter_t *filter,
+nvlist_t *nvl)
+{
+
+ int ret;
+ char *str;
+ uint32_t u32;
+ nvlist_t *nvlcp;
+ char *end;
+
+ IPQOSCDBG0(KRET, "In parse_kfilter:\n");
+
+ /* make copy of passed in nvlist as it is freed by the caller */
+
+ ret = nvlist_dup(nvl, &nvlcp, 0);
+ if (ret != 0) {
+ return (IPQOS_CONF_ERR);
+ }
+
+ /* lookup originator */
+
+ ret = nvlist_lookup_uint32(nvlcp, IPP_CONFIG_ORIGINATOR, &u32);
+ if (ret == 0) {
+ filter->originator = u32;
+ (void) nvlist_remove_all(nvlcp, IPP_CONFIG_ORIGINATOR);
+ } else {
+ filter->originator = IPP_CONFIG_IPQOSCONF;
+ }
+
+ /* lookup filter name */
+
+ (void) nvlist_lookup_string(nvlcp, CLASSIFIER_FILTER_NAME, &str);
+ (void) strlcpy(filter->name, str, IPQOS_CONF_NAME_LEN);
+ (void) nvlist_remove_all(nvlcp, CLASSIFIER_FILTER_NAME);
+
+ /* lookup class name */
+
+ (void) nvlist_lookup_string(nvlcp, CLASSIFIER_CLASS_NAME, &str);
+ (void) strlcpy(filter->class_name, str, IPQOS_CONF_NAME_LEN);
+ (void) nvlist_remove_all(nvlcp, CLASSIFIER_CLASS_NAME);
+
+ /* lookup src and dst host names if present */
+
+ if (nvlist_lookup_string(nvlcp, IPGPC_SADDR_HOSTNAME, &str) == 0) {
+ filter->src_nd_name = malloc(strlen(str) + 1);
+ if (filter->src_nd_name) {
+ (void) strcpy(filter->src_nd_name, str);
+ (void) nvlist_remove_all(nvlcp, IPGPC_SADDR_HOSTNAME);
+ } else {
+ ipqos_msg(MT_ENOSTR, "malloc");
+ nvlist_free(nvlcp);
+ return (IPQOS_CONF_ERR);
+ }
+ }
+ if (nvlist_lookup_string(nvlcp, IPGPC_DADDR_HOSTNAME, &str) == 0) {
+ filter->dst_nd_name = malloc(strlen(str) + 1);
+ if (filter->dst_nd_name) {
+ (void) strcpy(filter->dst_nd_name, str);
+ (void) nvlist_remove_all(nvlcp, IPGPC_DADDR_HOSTNAME);
+ } else {
+ ipqos_msg(MT_ENOSTR, "malloc");
+ nvlist_free(nvlcp);
+ return (IPQOS_CONF_ERR);
+ }
+ }
+
+ /* lookup ip_version if present */
+
+ if (nvlist_lookup_string(nvlcp, IPGPC_FILTER_PRIVATE, &str) == 0) {
+ filter->ip_versions = (uint32_t)strtol(str, &end, 0);
+ if (end != str) {
+ (void) nvlist_remove_all(nvlcp, IPGPC_FILTER_PRIVATE);
+ } else {
+ ipqos_msg(MT_ERROR,
+ gettext("Corrupted ip_version returned from "
+ "kernel.\n"));
+ nvlist_free(nvlcp);
+ return (IPQOS_CONF_ERR);
+ }
+ }
+
+ /* lookup filter instance if present */
+
+ ret = nvlist_lookup_int32(nvlcp, IPGPC_FILTER_INSTANCE,
+ &filter->instance);
+ if (ret != 0) {
+ filter->instance = -1;
+ } else {
+ (void) nvlist_remove_all(nvlcp, IPGPC_FILTER_INSTANCE);
+ }
+
+ /* attach new trimmed nvlist to filter */
+ filter->nvlist = nvlcp;
+
+ return (IPQOS_CONF_SUCCESS);
+}
+
+
+/*
+ * determines whether action_name is a virtual action name.
+ * RETURNS: if virtual action 1, else 0.
+ */
+static int
+virtual_action(char *action_name)
+{
+
+ if (strcmp(action_name, IPP_ANAME_CONT) == 0 ||
+ strcmp(action_name, IPP_ANAME_DEFER) == 0 ||
+ strcmp(action_name, IPP_ANAME_DROP) == 0) {
+ return (1);
+ }
+
+ return (0);
+}
+
+/*
+ * remove all the actions within the kernel. If there is a failure
+ * modified is set to represent whether the attempt to flush modified
+ * the configuration in any way.
+ * RETURNS: IPQOS_CONF_ERR if the ipp_* functions return any errors,
+ * else IPQOS_CONF_SUCCESS.
+ */
+static int
+flush(
+boolean_t *modified)
+{
+
+ int res;
+ char **modnames = NULL;
+ int nmods;
+ char **actnames = NULL;
+ int nacts;
+ int x, y;
+
+ IPQOSCDBG0(L0, "In flush\n");
+
+ *modified = B_FALSE;
+
+ /*
+ * get list of modules currently loaded.
+ */
+ res = ipp_list_mods(&modnames, &nmods);
+ if (res != 0) {
+ ipqos_msg(MT_ENOSTR, "ipp_list_mods");
+ return (IPQOS_CONF_ERR);
+ }
+
+ /*
+ * iterate through all the modules listing their actions and
+ * deleting all of them.
+ */
+ for (x = 0; x < nmods; x++) {
+ IPQOSCDBG1(APPLY, "Getting actions of module %s.\n",
+ modnames[x]);
+ res = ipp_mod_list_actions(modnames[x], &actnames, &nacts);
+ if (res != 0) {
+ ipqos_msg(MT_ENOSTR, "ipp_mod_list_actions");
+ cleanup_string_table(modnames, nmods);
+ return (IPQOS_CONF_ERR);
+ }
+
+ for (y = 0; y < nacts; y++) {
+ IPQOSCDBG1(APPLY, "deleting action %s\n", actnames[y]);
+ res = ipp_action_destroy(actnames[y], IPP_DESTROY_REF);
+ /*
+ * if fails for reason other than action doesn't
+ * exist or action has dependency.
+ */
+ if (res != 0 && errno != ENOENT && errno != EBUSY) {
+ ipqos_msg(MT_ENOSTR, "ipp_action_destroy");
+ cleanup_string_table(modnames, nmods);
+ cleanup_string_table(actnames, nacts);
+ return (IPQOS_CONF_ERR);
+ }
+
+ if (res == 0)
+ *modified = B_TRUE;
+ }
+ cleanup_string_table(actnames, nacts);
+ }
+ cleanup_string_table(modnames, nmods);
+
+ return (IPQOS_CONF_SUCCESS);
+}
+
+/*
+ * Trys to flush the configuration. If it fails and nothing has been modified
+ * and force_flush is false just return an error, otherwise persist trying to
+ * completion.
+ * RETURNS: IPQOS_CONF_ERR if flush attempt failed without modifying anything
+ * and force_flush was set to false, otherwise IPQOS_CONF_SUCCESS.
+ */
+static int
+atomic_flush(
+boolean_t force_flush)
+{
+ int x = 0;
+ int res;
+ boolean_t modified = B_FALSE;
+
+ /*
+ * attempt first flush of config.
+ */
+ res = flush(&modified);
+ if ((force_flush == B_FALSE) && (res != IPQOS_CONF_SUCCESS) &&
+ (modified == B_FALSE)) {
+ return (IPQOS_CONF_ERR);
+ } else if (res == IPQOS_CONF_SUCCESS) {
+ return (IPQOS_CONF_SUCCESS);
+ }
+
+ /*
+ * failed flush that modified config, or force flush set; loop till
+ * successful flush.
+ */
+ while (res != IPQOS_CONF_SUCCESS) {
+ if (x == 5) { /* 10 secs since start/last message. */
+ ipqos_msg(MT_ERROR,
+ gettext("Retrying configuration flush.\n"));
+ x = 0;
+ }
+ (void) sleep(2);
+ x++;
+ res = flush(&modified);
+ }
+
+ return (IPQOS_CONF_SUCCESS);
+}
+
+/*
+ * Performs a flush of the configuration within a signal blocking region
+ * so that there's minimal chance of it being killed and the flush only
+ * partially completing.
+ * RETURNS: IPQOS_CONF_SUCCESS (for symmetry with the other main functions).
+ */
+static int
+flushconf()
+{
+ int res;
+
+ /*
+ * make sure that flush is as atomic as possible.
+ */
+ if ((res = block_all_signals()) == -1)
+ return (IPQOS_CONF_ERR);
+
+ res = atomic_flush(B_FALSE);
+
+ /*
+ * restore signals.
+ */
+ (void) restore_all_signals();
+
+ if (res == IPQOS_CONF_SUCCESS) {
+ ipqos_msg(MT_LOG, gettext("Configuration flushed.\n"));
+ } else {
+ ipqos_msg(MT_ENOSTR, "atomic_flush");
+ }
+
+ return (res);
+}
+
+static int
+in_string_table(char *stable[], int size, char *string)
+{
+
+ IPQOSCDBG1(L1, "In in_string_table: search string %s\n", string);
+
+ for (--size; size >= 0; size--) {
+ if (strcmp(stable[size], string) == 0) {
+ IPQOSCDBG1(L1, "Found %s in string table\n", string);
+ return (1);
+ }
+ }
+
+ return (0);
+}
+
+/* free the memory occupied by the string table ctable and its contents. */
+static void
+cleanup_string_table(char *ctable[], int size)
+{
+
+ int x;
+
+ if (ctable) {
+ for (x = 0; x < size; x++) {
+ free(ctable[x]);
+ }
+ free(ctable);
+ }
+}
+
+#if 0
+
+/*
+ * makes a copy of a string table and returns a ptr to it.
+ * RETURNS: NULL on error or if size was 0, else ptr to copied table.
+ */
+static char **
+copy_string_table(char *stable1[], int size)
+{
+
+ char **st = NULL;
+ int pos;
+
+ /* create char ptr array */
+
+ st = malloc(size * sizeof (char *));
+ if (st == NULL) {
+ ipqos_msg(MT_ENOSTR, "malloc");
+ return (st);
+ }
+
+ /* create copy of each string from stable1 in array */
+
+ for (pos = size - 1; pos >= 0; pos--) {
+ st[pos] = malloc(strlen(stable1[pos] + 1));
+ if (st[pos] == NULL) {
+ for (pos++; pos < size; pos++)
+ free(st[pos]);
+ free(st);
+ ipqos_msg(MT_ENOSTR, "malloc");
+ return (NULL);
+ }
+
+ (void) strcpy(st[pos], stable1[pos]);
+ }
+
+ return (st);
+}
+#endif /* 0 */
+
+/*
+ * retry lookups on filters that soft failed a previous lookup and
+ * were put on the retry list.
+ * RETURNS: IPQOS_CONF_ERR on any errors, else IPQOS_CONF_SUCCESS.
+ */
+static int
+retry_name_lookups(
+ipqos_conf_action_t *actions)
+{
+
+ ipqos_conf_action_t *act;
+ ipqos_conf_filter_t **new_filters;
+ ipqos_conf_filter_t *flt;
+
+ IPQOSCDBG0(APPLY, "In retry_name_lookups:\n");
+
+ for (act = actions; act != NULL; act = act->next) {
+
+ /* store start of new resolved filters */
+ LIST_END(&act->filters, &new_filters);
+
+ /*
+ * do name resolution on retry list adding resolved filters
+ * to end of actions filters.
+ */
+ for (flt = act->retry_filters; flt != NULL; flt = flt->next) {
+
+ if (domultihome(flt, new_filters, B_TRUE) !=
+ IPQOS_CONF_SUCCESS) {
+
+ /* if resource failure */
+
+ if (flt->nlerr == 0) {
+ return (IPQOS_CONF_ERR);
+ }
+ }
+ }
+
+ /* add the newly resolved filters to the kernel action */
+
+ for (flt = *new_filters; flt != NULL; flt = flt->next) {
+ if (add_filter(act->name, flt, act->module_version) !=
+ IPQOS_CONF_SUCCESS) {
+ return (IPQOS_CONF_ERR);
+ }
+ }
+ }
+
+ return (IPQOS_CONF_SUCCESS);
+}
+
+/*
+ * write the configuration in conf to the file given in dstpath. This
+ * is done by writing first to a temporary file and then renaming that
+ * file to dstpath. This assures an atomic write.
+ * RETURNS: IPQOS_CONF_ERR on any errors, else IPQOS_CONF_SUCCESS.
+ */
+static int
+writeconf(
+ipqos_conf_action_t *conf,
+char *dstpath)
+{
+
+ FILE *tmpfp;
+ char *tmppath;
+ char *pathend;
+ ipqos_conf_action_t *act;
+ int res;
+
+ IPQOSCDBG0(L0, "in writeconf\n");
+
+ /* construct tmp file path so we can use rename() */
+
+ pathend = strrchr(dstpath, '/');
+
+ /* dstpath in current dir */
+
+ if (pathend == NULL) {
+ tmppath = malloc(strlen("ipqosconf.tmp") + 1);
+ if (tmppath == NULL) {
+ ipqos_msg(MT_ENOSTR, "malloc");
+ return (IPQOS_CONF_ERR);
+ }
+ (void) strcpy(tmppath, "ipqosconf.tmp");
+
+ /* dstpath in root dir */
+
+ } else if (pathend == dstpath) {
+ tmppath = malloc(strlen("/ipqosconf.tmp") + 1);
+ if (tmppath == NULL) {
+ ipqos_msg(MT_ENOSTR, "malloc");
+ return (IPQOS_CONF_ERR);
+ }
+ (void) strcpy(tmppath, "/ipqosconf.tmp");
+
+ /* not pwd or root */
+
+ } else {
+ *pathend = NULL;
+ tmppath = malloc(strlen(dstpath) + strlen("/ipqosconf.tmp") +
+ 1);
+ if (tmppath == NULL) {
+ ipqos_msg(MT_ENOSTR, "malloc");
+ return (IPQOS_CONF_ERR);
+ }
+ (void) strcpy(tmppath, dstpath);
+ (void) strcat(tmppath, "/ipqosconf.tmp");
+ *pathend = '/';
+ }
+
+
+ /* open tmp file */
+
+ tmpfp = fopen(tmppath, "w");
+ if (tmpfp == NULL) {
+ ipqos_msg(MT_ENOSTR, "fopen");
+ free(tmppath);
+ return (IPQOS_CONF_ERR);
+ }
+
+ /* write out format version */
+
+ (void) fprintf(tmpfp, "%s %d.%d\n\n", IPQOS_FMT_VERSION_STR,
+ IPQOS_CUR_FMT_MAJOR_VER, IPQOS_CUR_FMT_MINOR_VER);
+
+ /*
+ * loop through actions in list writing ipqosconf originated
+ * ones out to the tmp file.
+ */
+ for (act = conf; act != NULL; act = act->next) {
+ if (act->params->originator == IPP_CONFIG_IPQOSCONF) {
+ res = printaction(tmpfp, act, 0, 0);
+ if (res != IPQOS_CONF_SUCCESS) {
+ free(tmppath);
+ (void) fclose(tmpfp);
+ return (res);
+ }
+ }
+ }
+ (void) fclose(tmpfp);
+
+ /* rename tmp file to dst file */
+
+ if (rename(tmppath, dstpath) != 0) {
+ ipqos_msg(MT_ENOSTR, "rename");
+ free(tmppath);
+ return (IPQOS_CONF_ERR);
+ }
+ free(tmppath);
+
+ return (IPQOS_CONF_SUCCESS);
+}
+
+/*
+ * read the configuration back from the kernel and then write each of the
+ * actions read to IPQOS_CONF_INIT_PATH.
+ * RETURNS: IPQOS_CONF_ERR if error, else IPQOS_CONF_SUCCESS.
+ */
+static int
+commitconf()
+{
+
+ int ret;
+ ipqos_conf_action_t *conf;
+
+ IPQOSCDBG0(L0, "In commitconf\n");
+
+ /* read the configuration from the kernel */
+
+ ret = readkconf(&conf);
+ if (ret != IPQOS_CONF_SUCCESS) {
+ return (IPQOS_CONF_ERR);
+ }
+
+ /* dissallow a null config to be stored (we can't read one in) */
+
+ if (conf == NULL) {
+ ipqos_msg(MT_ERROR,
+ gettext("Can't commit a null configuration.\n"));
+ return (IPQOS_CONF_ERR);
+ }
+
+ /* make sure if we create file that perms are 644 */
+
+ (void) umask(S_IXUSR | S_IWGRP | S_IXGRP | S_IWOTH | S_IXOTH);
+
+ /* write the configuration to the init file */
+
+ ret = writeconf(conf, IPQOS_CONF_INIT_PATH);
+ if (ret != IPQOS_CONF_SUCCESS) {
+ return (IPQOS_CONF_ERR);
+ }
+
+ ipqos_msg(MT_LOG,
+ gettext("Current configuration saved to init file.\n"));
+
+ return (IPQOS_CONF_SUCCESS);
+}
+
+/*
+ * Called in the event of a failed rollback. It first flushes the
+ * current configuration, then attempts to apply the oconf (the old
+ * one), and if that fails flushes again.
+ *
+ * RETURNS: IPQOS_CONF_ERR if the application of old config fails,
+ * else IPQOS_CONF_SUCCESS.
+ */
+static int
+rollback_recover(
+ipqos_conf_action_t *oconf)
+{
+ int res;
+
+ IPQOSCDBG0(RBK, "In rollback_recover\n");
+
+ /*
+ * flush configuration.
+ */
+ (void) atomic_flush(B_TRUE);
+
+ /*
+ * mark all elements of old config for application.
+ */
+ mark_config_new(oconf);
+
+ /*
+ * attempt to apply old config.
+ */
+ res = applydiff(oconf, NULL);
+ /*
+ * if failed force flush of config.
+ */
+ if (res != IPQOS_CONF_SUCCESS) {
+ (void) atomic_flush(B_TRUE);
+ return (IPQOS_CONF_ERR);
+ }
+
+ return (IPQOS_CONF_SUCCESS);
+}
+
+/*
+ * read and apply the configuration contained if file ifile to the kernel.
+ * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
+ */
+static int
+applyconf(char *ifile)
+{
+
+ FILE *ifp;
+ ipqos_conf_action_t *conf = NULL;
+ ipqos_conf_action_t *oconf = NULL;
+ ipqos_conf_action_t *act, *oact;
+ int res;
+
+ IPQOSCDBG0(L0, "In applyconf:\n");
+
+
+ /* if filename '-' read from stdin */
+
+ if (strcmp(ifile, "-") == 0) {
+ ifp = stdin;
+ } else {
+ ifp = fopen(ifile, "r");
+ if (ifp == NULL) {
+ ipqos_msg(MT_ERROR,
+ gettext("Opening file %s for read: %s.\n"),
+ ifile, strerror(errno));
+ return (IPQOS_CONF_ERR);
+ }
+ }
+
+ /* read in new configuration */
+
+ res = readconf(ifp, &conf);
+ if (res != IPQOS_CONF_SUCCESS) {
+ goto fail;
+ }
+
+ /* check configuration is valid */
+
+ res = validconf(conf, 1);
+ if (res != IPQOS_CONF_SUCCESS) {
+ goto fail;
+ }
+
+ /* read in kernel configuration */
+
+ res = readkconf(&oconf);
+ if (res != IPQOS_CONF_SUCCESS) {
+ goto fail;
+ }
+
+ /*
+ * check there are no same named actions in both config file and the
+ * the kernel that are for a different module. The application
+ * system can't handle these as we would try to add the new
+ * action before we deleted the old one and because actions
+ * in the kernel are indexed solely on their name (their module
+ * isn't included) the kernel would return an error. We want
+ * to avoid this error and the resulting rollback.
+ */
+ for (act = conf; act != NULL; act = act->next) {
+ for (oact = oconf; oact != NULL; oact = oact->next) {
+ /* found action */
+ if (strcmp(act->name, oact->name) == 0) {
+ /* different module */
+ if (strcmp(act->module, oact->module) != 0) {
+ ipqos_msg(MT_ERROR,
+ gettext("Action at line %u has "
+ "same name as currently "
+ "installed action, but is for a "
+ "different module.\n"),
+ act->lineno);
+ goto fail;
+ /* same module - stop search */
+ } else {
+ break;
+ }
+ }
+ }
+ }
+
+
+ /* create links between actions for use with deletions etc.. */
+
+ res = validconf(oconf, 0);
+ if (res != IPQOS_CONF_SUCCESS) {
+ goto fail;
+ }
+
+ /* diff conf file against kernel */
+
+ res = diffconf(oconf, conf);
+ if (res != IPQOS_CONF_SUCCESS) {
+ goto fail;
+ }
+
+ /* make kernel mods as atomic as possible */
+
+ if ((res = block_all_signals()) == -1) {
+ res = IPQOS_CONF_ERR;
+ goto fail;
+ }
+
+ /* apply difference to kernel */
+
+ res = applydiff(conf, oconf);
+#ifdef _IPQOS_CONF_DEBUG
+ if (force_rback || res != IPQOS_CONF_SUCCESS) {
+#else
+ if (res != IPQOS_CONF_SUCCESS) {
+#endif /* _IPQOS_CONF_DEBUG */
+
+ res = rollback(conf, oconf);
+ if (res != IPQOS_CONF_SUCCESS) {
+ res = rollback_recover(oconf);
+ if (res != IPQOS_CONF_SUCCESS) {
+ /* system left flushed */
+ ipqos_msg(MT_ERROR,
+ gettext("Failed to rollback from failed "
+ "configuration, configuration flushed.\n"));
+ res = IPQOS_CONF_RECOVER_ERR;
+ } else { /* old config re-applied */
+ ipqos_msg(MT_ERROR,
+ gettext("Configuration failed, system "
+ "state unchanged.\n"));
+ res = IPQOS_CONF_ERR;
+ }
+ } else {
+ ipqos_msg(MT_ERROR,
+ gettext("Configuration failed, system "
+ "state unchanged.\n"));
+ res = IPQOS_CONF_ERR;
+ }
+ goto fail;
+ }
+
+ /* retry any soft name lookup failures */
+
+ res = retry_name_lookups(conf);
+ if (res != IPQOS_CONF_SUCCESS) {
+ res = rollback(conf, oconf);
+ if (res != IPQOS_CONF_SUCCESS) {
+ res = rollback_recover(oconf);
+ if (res != IPQOS_CONF_SUCCESS) {
+ /* system left flushed */
+ ipqos_msg(MT_ERROR,
+ gettext("Failed to rollback from failed "
+ "configuration, configuration flushed.\n"));
+ res = IPQOS_CONF_RECOVER_ERR;
+ } else { /* old config re-applied */
+ ipqos_msg(MT_ERROR,
+ gettext("Configuration failed, system "
+ "state unchanged.\n"));
+ res = IPQOS_CONF_ERR;
+ }
+ } else {
+ ipqos_msg(MT_ERROR,
+ gettext("Configuration failed, system "
+ "state unchanged.\n"));
+ res = IPQOS_CONF_ERR;
+ }
+ goto fail;
+
+ }
+
+ ipqos_msg(MT_LOG, gettext("IPQoS configuration applied.\n"));
+
+ /* re-enable signals */
+ (void) restore_all_signals();
+
+ (void) fclose(ifp);
+ free_actions(conf);
+ free_actions(oconf);
+ return (IPQOS_CONF_SUCCESS);
+fail:
+ (void) fclose(ifp);
+ (void) restore_all_signals();
+ if (conf)
+ free_actions(conf);
+ if (oconf)
+ free_actions(oconf);
+ if (res == IPQOS_CONF_RECOVER_ERR)
+ ipqos_msg(MT_LOG, gettext("Configuration flushed.\n"));
+ return (res);
+}
+
+static sigset_t set, oset;
+
+static int
+block_all_signals()
+{
+ if (sigfillset(&set) == -1) {
+ ipqos_msg(MT_ENOSTR, "sigfillset");
+ return (-1);
+ }
+ if (sigprocmask(SIG_SETMASK, &set, &oset) == -1) {
+ ipqos_msg(MT_ENOSTR, "sigprocmask");
+ return (-1);
+ }
+ return (0);
+}
+
+static int
+restore_all_signals()
+{
+ if (sigprocmask(SIG_SETMASK, &oset, NULL) == -1) {
+ ipqos_msg(MT_ENOSTR, "sigprocmask");
+ return (-1);
+ }
+ return (0);
+}
+
+static int
+unlock(int fd)
+{
+ if (lockf(fd, F_ULOCK, 0) == -1) {
+ ipqos_msg(MT_ENOSTR, "lockf");
+ return (-1);
+ }
+ return (0);
+}
+
+static int
+lock()
+{
+ int fd;
+ struct stat sbuf1;
+ struct stat sbuf2;
+
+ /*
+ * Open the file with O_CREAT|O_EXCL. If it exists already, it
+ * will fail. If it already exists, check whether it looks like
+ * the one we created.
+ */
+ (void) umask(0077);
+ if ((fd = open(IPQOS_CONF_LOCK_FILE, O_EXCL|O_CREAT|O_RDWR,
+ S_IRUSR|S_IWUSR)) == -1) {
+ if (errno != EEXIST) {
+ /* Some other problem. */
+ ipqos_msg(MT_ENOSTR,
+ gettext("Cannot open lock file %s"),
+ IPQOS_CONF_LOCK_FILE);
+ return (-1);
+ }
+
+ /*
+ * open() returned an EEXIST error. We don't fail yet
+ * as it could be a residual from a previous
+ * execution. However, we need to clear errno here.
+ * If we don't and print_cmd_buf() is later invoked
+ * as the result of a parsing error, it
+ * will assume that the current error is EEXIST and
+ * that a corresponding error message has already been
+ * printed, which results in an incomplete error
+ * message. If errno is zero, print_cmd_buf() will
+ * assume that it is called as a result of a
+ * parsing error and will print the appropriate
+ * error message.
+ */
+ errno = 0;
+
+ /*
+ * File exists. make sure it is OK. We need to lstat()
+ * as fstat() stats the file pointed to by the symbolic
+ * link.
+ */
+ if (lstat(IPQOS_CONF_LOCK_FILE, &sbuf1) == -1) {
+ ipqos_msg(MT_ENOSTR,
+ gettext("Cannot lstat lock file %s\n"),
+ IPQOS_CONF_LOCK_FILE);
+ return (-1);
+ }
+ /*
+ * Check whether it is a regular file and not a symbolic
+ * link. Its link count should be 1. The owner should be
+ * root and the file should be empty.
+ */
+ if (((sbuf1.st_mode & (S_IFREG|S_IFLNK)) != S_IFREG) ||
+ sbuf1.st_nlink != 1 ||
+ sbuf1.st_uid != 0 ||
+ sbuf1.st_size != 0) {
+ ipqos_msg(MT_ERROR, gettext("Bad lock file %s.\n"),
+ IPQOS_CONF_LOCK_FILE);
+ return (-1);
+ }
+ if ((fd = open(IPQOS_CONF_LOCK_FILE, O_CREAT|O_RDWR,
+ S_IRUSR|S_IWUSR)) == -1) {
+ ipqos_msg(MT_ENOSTR,
+ gettext("Cannot open lock file %s"),
+ IPQOS_CONF_LOCK_FILE);
+ return (-1);
+ }
+
+ /* Check whether we opened the file that we lstat()ed. */
+ if (fstat(fd, &sbuf2) == -1) {
+ ipqos_msg(MT_ENOSTR,
+ gettext("Cannot fstat lock file %s\n"),
+ IPQOS_CONF_LOCK_FILE);
+ return (-1);
+ }
+ if (sbuf1.st_dev != sbuf2.st_dev ||
+ sbuf1.st_ino != sbuf2.st_ino) {
+ /* File changed after we did the lstat() above */
+ ipqos_msg(MT_ERROR, gettext("Bad lock file %s.\n"),
+ IPQOS_CONF_LOCK_FILE);
+ return (-1);
+ }
+ }
+ if (lockf(fd, F_LOCK, 0) == -1) {
+ ipqos_msg(MT_ENOSTR, "lockf");
+ return (-1);
+ }
+ return (fd);
+}
+
+/*
+ * print the current kernel configuration out to stdout. If viewall
+ * is set this causes more verbose configuration listing including
+ * showing objects we didn't create, each instance of a mhome filter,
+ * etc.. see printaction().
+ * RETURNS: IPQOS_CONF_ERR on error, else IPQOS_CONF_SUCCES.
+ */
+
+static int
+viewconf(int viewall)
+{
+
+ ipqos_conf_action_t *conf = NULL;
+ ipqos_conf_action_t *act;
+ int ret;
+
+ IPQOSCDBG0(L0, "In viewconf\n");
+
+ /* get kernel configuration */
+
+ ret = readkconf(&conf);
+ if (ret != IPQOS_CONF_SUCCESS) {
+ return (IPQOS_CONF_ERR);
+ }
+
+ /* write out format version */
+
+ if (conf != NULL) {
+ (void) fprintf(stdout, "%s %d.%d\n\n", IPQOS_FMT_VERSION_STR,
+ IPQOS_CUR_FMT_MAJOR_VER, IPQOS_CUR_FMT_MINOR_VER);
+ }
+
+ /* print each of the actions in the kernel config to stdout */
+
+ for (act = conf; act != NULL; act = act->next) {
+ ret = printaction(stdout, act, viewall, 0);
+ if (ret != IPQOS_CONF_SUCCESS) {
+ free_actions(conf);
+ return (ret);
+ }
+ (void) fprintf(stdout, "\n");
+ }
+
+ free_actions(conf);
+
+ return (IPQOS_CONF_SUCCESS);
+}
+
+
+/*
+ * debug function that reads the config file and prints it out after
+ * interpreting to stdout.
+ */
+#ifdef _IPQOS_CONF_DEBUG
+static int
+viewcfile(char *cfile)
+{
+
+ ipqos_conf_action_t *conf;
+ ipqos_conf_action_t *act;
+ int res;
+ FILE *ifp;
+ int viewall = 1;
+
+ IPQOSCDBG0(L0, "In viewcfile\n");
+ ifp = fopen(cfile, "r");
+ if (ifp == NULL) {
+ ipqos_msg(MT_ERROR, gettext("Opening file %s for read: %s.\n"),
+ cfile, strerror(errno));
+ return (IPQOS_CONF_ERR);
+ }
+
+ res = readconf(ifp, &conf);
+ if (res != IPQOS_CONF_SUCCESS) {
+ free(ifp);
+ return (IPQOS_CONF_ERR);
+ }
+
+ /* print each of the actions in the kernel config to stdout */
+ for (act = conf; act != NULL; act = act->next) {
+ res = printaction(stdout, act, viewall, 0);
+ if (res != IPQOS_CONF_SUCCESS) {
+ free(ifp);
+ return (res);
+ }
+
+ (void) fprintf(stdout, "\n");
+ }
+
+ (void) fprintf(stdout, "\n");
+
+
+ return (IPQOS_CONF_SUCCESS);
+}
+#endif /* _IPQOS_CONF_DEBUG */
+
+static void
+usage(void)
+{
+ (void) fprintf(stderr, gettext("usage:\n"
+ "\tipqosconf [-sv] -a file|-\n"
+ "\tipqosconf -c\n"
+ "\tipqosconf -l\n"
+ "\tipqosconf -L\n"
+ "\tipqosconf -f\n"));
+}
+
+int
+main(int argc, char *argv[])
+{
+
+ int c;
+ char *ifile = NULL;
+ int args;
+ int ret;
+ int cmd;
+ int viewall = 0;
+ int lfp;
+
+ /* init global flags */
+ use_syslog = verbose = 0;
+
+ /* init current line number */
+ lineno = 0;
+
+ /* setup internationalisation */
+
+ (void) setlocale(LC_ALL, "");
+#if !defined(TEXT_DOMAIN)
+#define TEXT_DOMAIN "SYS_TEST"
+#endif
+ (void) textdomain(TEXT_DOMAIN);
+
+ /* setup syslog parameters */
+ openlog("ipqosconf", 0, LOG_USER);
+
+ args = 0;
+
+/* enable debug options */
+
+#ifdef _IPQOS_CONF_DEBUG
+#define DBGOPTS "rz:"
+#else
+#define DBGOPTS
+#endif /* _IPQOS_CONF_DEBUG */
+
+ while ((c = getopt(argc, argv, "sca:vflL" DBGOPTS)) != EOF) {
+ switch (c) {
+#ifdef _IPQOS_CONF_DEBUG
+ case 'z':
+ cmd = -1;
+ ifile = optarg;
+ if (*ifile == '\0') {
+ usage();
+ exit(1);
+ }
+ args++;
+ break;
+ case 'r':
+ force_rback++;
+ break;
+#endif /* _IPQOS_CONF_DEBUG */
+ case 'c':
+ cmd = IPQOS_CONF_COMMIT;
+ args++;
+ break;
+ case 'a':
+ cmd = IPQOS_CONF_APPLY;
+ ifile = optarg;
+ if (*ifile == '\0') {
+ usage();
+ exit(1);
+ }
+ args++;
+ break;
+ case 'f':
+ cmd = IPQOS_CONF_FLUSH;
+ args++;
+ break;
+ case 'l':
+ cmd = IPQOS_CONF_VIEW;
+ args++;
+ break;
+ case 'L':
+ cmd = IPQOS_CONF_VIEW;
+ viewall++;
+ args++;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case 's':
+ use_syslog++;
+ break;
+ case '?':
+ usage();
+ return (1);
+ }
+ }
+
+ /*
+ * dissallow non-option args, > 1 cmd args and syslog/verbose flags set
+ * for anything but apply.
+ */
+ if (optind != argc || args > 1 ||
+ use_syslog && cmd != IPQOS_CONF_APPLY ||
+ verbose && cmd != IPQOS_CONF_APPLY) {
+ usage();
+ exit(1);
+ }
+
+ /* if no cmd option then show config */
+
+ if (args == 0) {
+ cmd = IPQOS_CONF_VIEW;
+ }
+
+ /* stop concurrent ipqosconf invocations */
+ lfp = lock();
+ if (lfp == -1) {
+ exit(1);
+ }
+
+ switch (cmd) {
+#ifdef _IPQOS_CONF_DEBUG
+ case -1:
+ ret = viewcfile(ifile);
+ break;
+#endif /* _IPQOS_CONF_DEBUG */
+ case IPQOS_CONF_APPLY:
+ ret = applyconf(ifile);
+ break;
+ case IPQOS_CONF_COMMIT:
+ ret = commitconf();
+ break;
+ case IPQOS_CONF_VIEW:
+ ret = viewconf(viewall);
+ break;
+ case IPQOS_CONF_FLUSH:
+ ret = flushconf();
+ break;
+ }
+
+ (void) unlock(lfp);
+
+ return (ret);
+
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/ipqosconf/ipqosconf.h b/usr/src/cmd/cmd-inet/usr.sbin/ipqosconf/ipqosconf.h
new file mode 100644
index 0000000000..38172d3378
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/ipqosconf/ipqosconf.h
@@ -0,0 +1,344 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _IPQOS_CONF_H
+#define _IPQOS_CONF_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/nvpair.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* debug level bits */
+#define L0 0x01
+#define L1 0x02
+#define L2 0x04
+#define DIFF 0x08
+#define KRET 0x10
+#define APPLY 0x20
+#define MHME 0x40
+#define RBK 0x80
+
+/* directory for types files */
+#define TYPES_FILE_DIR "/usr/lib/ipqosconf/"
+
+/* location of lock file */
+#define IPQOS_CONF_LOCK_FILE "/var/run/ipqosconf.lock"
+
+/* location of startup config file */
+#define IPQOS_CONF_INIT_PATH "/etc/inet/ipqosinit.conf"
+
+/* ipqosconf commands */
+
+#define IPQOS_CONF_APPLY 1
+#define IPQOS_CONF_VIEW 2
+#define IPQOS_CONF_COMMIT 3
+#define IPQOS_CONF_FLUSH 4
+
+/* print ntabs to stream fp */
+
+#define PRINT_TABS(fp, ntabs)\
+{\
+ int x;\
+ for (x = 0; x < ntabs; x++)\
+ (void) fprintf(fp, "\t");\
+}
+
+/* having to define this as ip6.h version in _KERNEL guard */
+#ifndef V4_PART_OF_V6
+#define V4_PART_OF_V6(v6) v6._S6_un._S6_u32[3]
+#endif
+
+/*
+ * given pointer cp advance it to the first non-space character.
+ */
+#define SKIPWS(cp) while (isspace(*cp) && (*cp != '\0')) cp++
+
+/* extract the v4 and v6 bits of the ip_version enumeration from the filter */
+#define VERSION_IS_V4(flt) ((flt)->ip_versions & 0x01)
+#define VERSION_IS_V6(flt) ((flt)->ip_versions & 0x02)
+
+/* retrieve short name from a module.name nvpair name */
+#define SHORT_NAME(longnme) (strchr(longnme, '.') + 1)
+
+/* latest version of cfg file supported (1.0) */
+#define IPQOS_CUR_FMT_MAJOR_VER 1
+#define IPQOS_CUR_FMT_MINOR_VER 0
+
+/* length of string buffer used for storing an integer as a string */
+#define IPQOS_INT_STR_LEN 15
+
+/* length of line buffer used to read types file */
+#define IPQOS_CONF_TYPE_LINE_LEN 1024
+
+/* length of buffer used to store name of type when reading types file */
+#define IPQOS_CONF_TYPE_LEN 24
+
+/* max length of value string in types file */
+#define IPQOS_VALST_MAXLEN 100
+
+/* initial size of line buffer used by readtoken */
+#define IPQOS_CONF_LINEBUF_SZ 150
+
+/* length of class/filter/action names */
+#define IPQOS_CONF_NAME_LEN 24
+
+/* length of module names */
+#define IPQOS_CONF_MOD_NAME_LEN 10
+
+/* IPQOS_CONF_NAME_LEN + IPQOS_CONF_MOD_NAME_LEN */
+/* must be a numeric literal for use in scanf() format string */
+#define IPQOS_CONF_PNAME_LEN 34
+
+/* length of buffer used to construct msgs for printing */
+#define IPQOS_MSG_BUF_SZ 200
+/*
+ * Define CURL here so that while you are reading
+ * the code, it does not affect "vi" in pattern
+ * matching.
+ */
+#define CURL_BEGIN '{'
+#define CURL_END '}'
+
+/* internal return codes */
+#define IPQOS_CONF_SUCCESS 0
+#define IPQOS_CONF_ERR 1
+#define IPQOS_CONF_RECOVER_ERR 2
+#define IPQOS_CONF_CURL_END 3
+#define IPQOS_CONF_CURL_BEGIN 4
+#define IPQOS_CONF_EOF 5
+#define IPQOS_CONF_NO_VER_STR 6
+
+/* special tokens in config file */
+#define IPQOS_CONF_IP_VERSION_STR "ip_version"
+#define IPQOS_CONF_NEXT_ACTION_STR "next_action"
+#define IPQOS_CONF_NAME_STR "name"
+#define IPQOS_CONF_MODULE_STR "module"
+#define IPQOS_CONF_FILTER_STR "filter"
+#define IPQOS_CONF_ACTION_STR "action"
+#define IPQOS_CONF_CLASS_STR "class"
+#define IPQOS_CONF_PARAMS_STR "params"
+#define IPQOS_CONF_NEXT_STR "next"
+#define IPQOS_CONF_STATS_ENABLE_STR "enable_stats"
+#define IPQOS_CONF_GLOBAL_STATS_STR "global_stats"
+#define IPQOS_CONF_DROP_STR "drop"
+#define IPQOS_CONF_CONT_STR "continue"
+#define IPQOS_CONF_DEFER_STR "defer"
+#define IPQOS_CONF_TRUE_STR "true"
+#define IPQOS_CONF_FALSE_STR "false"
+#define IPQOS_FMT_VERSION_STR "fmt_version"
+#define IPQOS_IFNAME_STR "if_name"
+#define IPQOS_PLACE_PRM_STR IPQOS_CONF_PARAMS_STR
+#define IPQOS_PLACE_FILTER_STR IPQOS_CONF_FILTER_STR
+#define IPQOS_PLACE_MAP_STR "map"
+
+/* special tokens in types file */
+#define IPQOS_CONF_PERM_FILTER_MK "#PERM_FILTER"
+#define IPQOS_CONF_PERM_CLASS_MK "#PERM_CLASS"
+#define IPQOS_FMT_STR "fmt_version"
+#define IPQOS_MOD_STR "mod_version"
+
+
+/* nvlist parameters */
+#define IPQOS_CONF_IP_VERSION "ipgpc.ip_version"
+
+/* name lookup errors returned from domultihome() */
+#define IPQOS_LOOKUP_RETRY 1
+#define IPQOS_LOOKUP_FAIL 2
+
+/*
+ * used in calls to ipp_action_info() to encapuslate both an action and
+ * an ipqosconf internal return code.
+ */
+typedef struct ipqos_actinfo_prm_s {
+ struct ipqos_conf_action_s *action;
+ int intl_ret;
+} ipqos_actinfo_prm_t;
+
+/*
+ * skeletal list element struct used in manipulating lists of more complex
+ * structures.
+ */
+typedef struct ipqos_list_el_s {
+ struct ipqos_list_el_s *next;
+} ipqos_list_el_t;
+
+typedef struct str_str {
+ char *s1;
+ char *s2;
+} str_str_t;
+
+typedef struct str_val {
+ char *string;
+ int value;
+} str_val_t;
+
+typedef struct str_val_nd {
+ struct str_val sv;
+ struct str_val_nd *next;
+} str_val_nd_t;
+
+/* type of msg to be printed by ipqos_msg */
+enum msg_type { MT_ERROR, MT_WARNING, MT_LOG, MT_ENOSTR };
+
+/* enum for allowable parameter types */
+
+typedef enum ipqos_nvtype_e {
+IPQOS_DATA_TYPE_UINT8,
+IPQOS_DATA_TYPE_INT16,
+IPQOS_DATA_TYPE_UINT16,
+IPQOS_DATA_TYPE_INT32,
+IPQOS_DATA_TYPE_UINT32,
+IPQOS_DATA_TYPE_BOOLEAN,
+IPQOS_DATA_TYPE_STRING,
+IPQOS_DATA_TYPE_ACTION,
+IPQOS_DATA_TYPE_ADDRESS,
+IPQOS_DATA_TYPE_PORT,
+IPQOS_DATA_TYPE_PROTO,
+IPQOS_DATA_TYPE_ENUM,
+IPQOS_DATA_TYPE_IFNAME,
+IPQOS_DATA_TYPE_M_INDEX,
+IPQOS_DATA_TYPE_INT_ARRAY,
+IPQOS_DATA_TYPE_USER,
+IPQOS_DATA_TYPE_ADDRESS_MASK,
+IPQOS_DATA_TYPE_IFINDEX
+} ipqos_nvtype_t;
+
+/*
+ * passed to readnvpair to indicate which special meanings for nv names
+ * to use.
+ */
+typedef enum place_e {
+PL_ACTION, PL_FILTER, PL_CLASS, PL_PARAMS, PL_MAP, PL_ANY} place_t;
+
+
+/* classifier filter representation */
+
+typedef struct ipqos_conf_filter_s {
+ struct ipqos_conf_filter_s *next;
+ char name[IPQOS_CONF_NAME_LEN];
+ char class_name[IPQOS_CONF_NAME_LEN];
+ nvlist_t *nvlist;
+ boolean_t new;
+ boolean_t modified;
+ boolean_t cr_mod;
+ boolean_t todel;
+ boolean_t deleted;
+ uint32_t originator;
+ char *src_nd_name;
+ char *dst_nd_name;
+ int instance;
+ uint32_t lineno;
+ uint32_t ip_versions;
+ int nlerr;
+} ipqos_conf_filter_t;
+
+
+/*
+ * action reference - used to store information and reference an action struct.
+ */
+
+typedef struct ipqos_conf_act_ref_s {
+ struct ipqos_conf_act_ref_s *next;
+ struct ipqos_conf_act_ref_s *prev;
+ char name[IPQOS_CONF_NAME_LEN];
+ char field[IPQOS_CONF_PNAME_LEN];
+ struct ipqos_conf_action_s *action;
+ nvlist_t *nvlist;
+} ipqos_conf_act_ref_t;
+
+
+/* classifier class representation */
+
+typedef struct ipqos_conf_class_s {
+ struct ipqos_conf_class_s *next;
+ char name[IPQOS_CONF_NAME_LEN];
+ nvlist_t *nvlist;
+ ipqos_conf_act_ref_t *alist;
+ boolean_t modified;
+ boolean_t new;
+ boolean_t cr_mod;
+ boolean_t todel;
+ boolean_t deleted;
+ boolean_t stats_enable;
+ uint32_t originator;
+ uint32_t lineno;
+} ipqos_conf_class_t;
+
+/* action parameters representation */
+
+typedef struct ipqos_conf_params_s {
+ struct ipqos_conf_params_s *next;
+ ipqos_conf_act_ref_t *actions;
+ nvlist_t *nvlist;
+ boolean_t modified;
+ boolean_t stats_enable;
+ uint32_t originator;
+ uint32_t lineno;
+ boolean_t cr_mod;
+} ipqos_conf_params_t;
+
+
+/* signifys which stage of configuration application has just past */
+enum visit {ADD_VISITED = 1, MOD_VISITED, REM_VISITED, INCYCLE_VISITED};
+
+/*
+ * action representation, with parameters, and lists of filters and classes
+ * if classifier action.
+ */
+typedef struct ipqos_conf_action_s {
+ struct ipqos_conf_action_s *next;
+ char name[IPQOS_CONF_NAME_LEN];
+ char module[IPQOS_CONF_NAME_LEN];
+ ipqos_conf_filter_t *filters;
+ ipqos_conf_class_t *classes;
+ ipqos_conf_params_t *params;
+ nvlist_t *nvlist;
+ boolean_t todel;
+ boolean_t deleted;
+ boolean_t new;
+ boolean_t modified;
+ boolean_t cr_mod;
+ ipqos_conf_act_ref_t *dependencies;
+ enum visit visited;
+ uint32_t lineno;
+ ipqos_conf_filter_t *retry_filters;
+ char **perm_classes;
+ int num_perm_classes;
+ int module_version;
+} ipqos_conf_action_t;
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _IPQOS_CONF_H */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/ipqosconf/tokenmt.types b/usr/src/cmd/cmd-inet/usr.sbin/ipqosconf/tokenmt.types
new file mode 100644
index 0000000000..59b7c1f114
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/ipqosconf/tokenmt.types
@@ -0,0 +1,39 @@
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#ident "%Z%%M% %I% %E% SMI"
+
+fmt_version 1.0
+mod_version 1.0
+
+params red_action_name action
+params yellow_action_name action
+params green_action_name action
+params committed_rate uint32
+params peak_rate uint32
+params committed_burst uint32
+params peak_burst uint32
+params color_aware boolean
+params color_map int_array 64,{GREEN=0x01,YELLOW=0x02,RED=0x04}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/ipqosconf/tswtclmt.types b/usr/src/cmd/cmd-inet/usr.sbin/ipqosconf/tswtclmt.types
new file mode 100644
index 0000000000..1c1f263ccd
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/ipqosconf/tswtclmt.types
@@ -0,0 +1,36 @@
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+fmt_version 1.0
+mod_version 1.0
+
+params red_action_name action
+params yellow_action_name action
+params green_action_name action
+params committed_rate uint32
+params peak_rate uint32
+params window uint32
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/ipsecalgs.c b/usr/src/cmd/cmd-inet/usr.sbin/ipsecalgs.c
new file mode 100644
index 0000000000..220b7c039e
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/ipsecalgs.c
@@ -0,0 +1,1073 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <ipsec_util.h>
+#include <netdb.h>
+#include <locale.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <unistd.h>
+#include <net/pfpolicy.h>
+#include <strings.h>
+#include <errno.h>
+#include <sys/crypto/common.h>
+
+#define SPDSOCK_DIAG_BUF_LEN 128
+
+typedef enum cmd_s {
+ CMD_NONE = 0,
+ CMD_ADD,
+ CMD_ADD_PROTO,
+ CMD_DEL,
+ CMD_DEL_PROTO,
+ CMD_EXEC_MODE,
+ CMD_LIST_KERNEL
+} cmd_t;
+
+static const char *comma = ",";
+static int adddel_flags, increment = 0, default_keylen;
+static boolean_t synch_kernel;
+static cmd_t cmd = CMD_NONE;
+static int proto_number = -1, alg_number = -1;
+static char *proto_name, *alg_names_string, *block_sizes_string;
+static char *key_sizes_string, *mech_name, *exec_mode_string;
+static ipsecalgs_exec_mode_t proto_exec_mode = LIBIPSEC_ALGS_EXEC_SYNC;
+
+/*
+ * Used by the algorithm walker callback to populate a SPD_UPDATEALGS
+ * request.
+ */
+
+#define SYNC_REQ_SIZE 2048
+
+static uint64_t sync_req_buf[SYNC_REQ_SIZE];
+static struct spd_attribute *sync_req_attr;
+static uint_t sync_req_alg_count, sync_req_proto_count;
+
+#define EMIT(ap, tag, value) { \
+ (ap)->spd_attr_tag = (tag); \
+ (ap)->spd_attr_value = (value); \
+ (ap)++; \
+ if ((char *)(ap) + sizeof (*ap) - \
+ (char *)sync_req_buf > SYNC_REQ_SIZE) \
+ bail_nomem(); \
+ }
+
+static void dump_alg(struct ipsecalgent *);
+static void algs_walker(void (*)(struct ipsecalgent *), void (*)(uint_t));
+
+static void
+usage(void)
+{
+ errx(EXIT_FAILURE, gettext("Usage:\tipsecalgs\n"
+ "\tipsecalgs -l\n"
+ "\tipsecalgs -s\n"
+ "\tipsecalgs -a [-P protocol-number | -p protocol-name]\n"
+ "\t\t-k keylen-list [-i inc]\n"
+ "\t\t[-K default-keylen] -b blocklen-list\n"
+ "\t\t-n alg-names -N alg-number -m mech-name [-f] [-s]\n"
+ "\tipsecalgs -P protocol-number -p protocol-name\n"
+ "\t\t[-e exec-mode] [-f] [-s]\n"
+ "\tipsecalgs -r -p protocol-name -n alg-name [-s]\n"
+ "\tipsecalgs -r -p protocol-name -N alg-number [-s]\n"
+ "\tipsecalgs -R -P protocol-number [-s]\n"
+ "\tipsecalgs -R -p protocol-name [-s]\n"
+ "\tipsecalgs -e exec-mode -P protocol-number [-s]\n"
+ "\tipsecalgs -e exec-mode -p protocol-number [-s]"));
+}
+
+static void
+bail_nomem(void)
+{
+ errx(EXIT_FAILURE, gettext("Out of memory."));
+}
+
+/*
+ * Return the number of key or block sizes in the specified array.
+ */
+static uint_t
+num_sizes(int *sizes)
+{
+ uint_t nsizes = 0;
+
+ while (sizes[nsizes] != 0)
+ nsizes++;
+
+ return (nsizes);
+}
+
+/*
+ * Algorithms walker callback. Adds an algorithm to the current SPD_UPDATEALGS
+ * request.
+ */
+static void
+synch_emit_alg(struct ipsecalgent *alg)
+{
+ uint_t nkey_sizes, nblock_sizes, i;
+
+ EMIT(sync_req_attr, SPD_ATTR_ALG_ID, alg->a_alg_num);
+ EMIT(sync_req_attr, SPD_ATTR_ALG_PROTO, alg->a_proto_num);
+ EMIT(sync_req_attr, SPD_ATTR_ALG_INCRBITS, alg->a_key_increment);
+
+ nkey_sizes = num_sizes(alg->a_key_sizes);
+ EMIT(sync_req_attr, SPD_ATTR_ALG_NKEYSIZES, nkey_sizes);
+ for (i = 0; i < nkey_sizes; i++)
+ EMIT(sync_req_attr, SPD_ATTR_ALG_KEYSIZE, alg->a_key_sizes[i]);
+
+ nblock_sizes = num_sizes(alg->a_block_sizes);
+ EMIT(sync_req_attr, SPD_ATTR_ALG_NBLOCKSIZES, nblock_sizes);
+ for (i = 0; i < nblock_sizes; i++) {
+ EMIT(sync_req_attr, SPD_ATTR_ALG_BLOCKSIZE,
+ alg->a_block_sizes[i]);
+ }
+
+ EMIT(sync_req_attr, SPD_ATTR_ALG_MECHNAME, CRYPTO_MAX_MECH_NAME);
+ (void) strncpy((char *)sync_req_attr, alg->a_mech_name,
+ CRYPTO_MAX_MECH_NAME);
+ sync_req_attr = (struct spd_attribute *)((uint64_t *)sync_req_attr +
+ SPD_8TO64(CRYPTO_MAX_MECH_NAME));
+
+ EMIT(sync_req_attr, SPD_ATTR_NEXT, 0);
+
+ sync_req_alg_count++;
+}
+
+/*
+ * Protocol walker callback. Add protocol related info to the current
+ * SPD_UPDATEALGS request.
+ */
+static void
+synch_emit_proto(uint_t proto_num)
+{
+ ipsecalgs_exec_mode_t exec_mode;
+ uint32_t exec_mode_spdval;
+
+ EMIT(sync_req_attr, SPD_ATTR_PROTO_ID, proto_num);
+
+ /* execution mode */
+ if (ipsecproto_get_exec_mode(proto_num, &exec_mode) != 0) {
+ errx(EXIT_FAILURE, gettext("cannot get execution mode for "
+ "proto %d"), proto_num);
+ }
+
+ switch (exec_mode) {
+ case LIBIPSEC_ALGS_EXEC_SYNC:
+ exec_mode_spdval = SPD_ALG_EXEC_MODE_SYNC;
+ break;
+ case LIBIPSEC_ALGS_EXEC_ASYNC:
+ exec_mode_spdval = SPD_ALG_EXEC_MODE_ASYNC;
+ break;
+ }
+ EMIT(sync_req_attr, SPD_ATTR_PROTO_EXEC_MODE, exec_mode_spdval);
+
+ EMIT(sync_req_attr, SPD_ATTR_NEXT, 0);
+
+ sync_req_proto_count++;
+}
+
+/*
+ * Causes the kernel to be re-synched with the contents of /etc/inet/algs
+ */
+static void
+kernel_synch(void)
+{
+ int sfd = socket(PF_POLICY, SOCK_RAW, PF_POLICY_V1);
+ int cnt, req_len;
+ struct spd_msg *msg;
+ struct spd_ext_actions *act;
+ struct spd_attribute *attr;
+
+ if (sfd < 0) {
+ err(EXIT_FAILURE, gettext("Unable to open policy socket"));
+ }
+
+ /*
+ * Initialize the SPD message header and action. Some fields
+ * are set after having walked through the algorithms (number
+ * of algorithms, sizes, etc.)
+ */
+ msg = (struct spd_msg *)sync_req_buf;
+ (void) memset(msg, 0, sizeof (*msg));
+ msg->spd_msg_version = PF_POLICY_V1;
+ msg->spd_msg_type = SPD_UPDATEALGS;
+
+ act = (struct spd_ext_actions *)(msg + 1);
+ act->spd_actions_exttype = SPD_EXT_ACTION;
+ act->spd_actions_reserved = 0;
+
+ /*
+ * Walk through the algorithms defined and populate the
+ * request buffer.
+ */
+ sync_req_alg_count = 0;
+ sync_req_proto_count = 0;
+ sync_req_attr = (struct spd_attribute *)(act + 1);
+ algs_walker(synch_emit_alg, synch_emit_proto);
+ act->spd_actions_count = sync_req_alg_count + sync_req_proto_count;
+
+ /*
+ * Replace the last SPD_ATTR_NEXT attribute by a SPD_ATTR_END.
+ */
+ attr = sync_req_attr - 1;
+ attr->spd_attr_tag = SPD_ATTR_END;
+
+ /*
+ * Now that the message is built, compute its total length and
+ * update the length fields that depend on this value.
+ */
+ req_len = (char *)sync_req_attr - (char *)sync_req_buf;
+ msg->spd_msg_len = SPD_8TO64(req_len);
+ act->spd_actions_len = SPD_8TO64(req_len - sizeof (*msg));
+
+ /* ship the update request to spdsock */
+ cnt = write(sfd, sync_req_buf, req_len);
+ if (cnt != req_len) {
+ if (cnt < 0) {
+ err(EXIT_FAILURE, gettext("algs update write failed"));
+ } else {
+ errx(EXIT_FAILURE, gettext("algs update short write"));
+ }
+ /* err/errx call exit(). */
+ }
+
+ cnt = read(sfd, sync_req_buf, req_len);
+
+ if (cnt == -1) {
+ err(EXIT_FAILURE, gettext("algs update read failed"));
+ }
+
+ if (cnt < sizeof (struct spd_msg)) {
+ errx(EXIT_FAILURE, gettext(
+ "algs update failed while reading reply (short read)"));
+ }
+
+ msg = (struct spd_msg *)sync_req_buf;
+ if (msg->spd_msg_errno != 0) {
+ errno = msg->spd_msg_errno;
+ warn(gettext("algs update failed"));
+ if (msg->spd_msg_diagnostic != 0) {
+ warnx("%s", spdsock_diag(msg->spd_msg_diagnostic));
+ }
+ exit(EXIT_FAILURE);
+ }
+
+ (void) close(sfd);
+}
+
+static void
+list_kernel_algs(void)
+{
+ int sfd = socket(PF_POLICY, SOCK_RAW, PF_POLICY_V1);
+ int cnt, retval;
+ uint64_t reply_buf[2048];
+ spd_ext_t *exts[SPD_EXT_MAX+1];
+ struct spd_msg msg;
+ struct spd_ext_actions *actp;
+ struct spd_attribute *attr, *endattr;
+ uint64_t *start, *end;
+ struct ipsecalgent alg;
+ uint_t cur_key, cur_block;
+ uint_t nkey_sizes, nblock_sizes;
+ char diag_buf[SPDSOCK_DIAG_BUF_LEN];
+
+ if (sfd < 0) {
+ err(EXIT_FAILURE, gettext("Unable to open policy socket"));
+ }
+
+ (void) memset(&msg, 0, sizeof (msg));
+ msg.spd_msg_version = PF_POLICY_V1;
+ msg.spd_msg_type = SPD_DUMPALGS;
+ msg.spd_msg_len = SPD_8TO64(sizeof (msg));
+
+ cnt = write(sfd, &msg, sizeof (msg));
+ if (cnt != sizeof (msg)) {
+ if (cnt < 0) {
+ err(EXIT_FAILURE, gettext("dump algs write failed"));
+ } else {
+ errx(EXIT_FAILURE, gettext("dump algs short write"));
+ }
+ /* err/errx call exit(). */
+ }
+
+ cnt = read(sfd, reply_buf, sizeof (reply_buf));
+
+ if (cnt == -1) {
+ err(EXIT_FAILURE, gettext("dump algs read failed"));
+ }
+
+ if (cnt < sizeof (struct spd_msg)) {
+ errx(EXIT_FAILURE, gettext(
+ "dump algs failed while reading reply (short read)"));
+ }
+
+ (void) close(sfd);
+
+ retval = spdsock_get_ext(exts, (spd_msg_t *)reply_buf, SPD_8TO64(cnt),
+ diag_buf, SPDSOCK_DIAG_BUF_LEN);
+
+ if (retval == KGE_LEN && exts[0]->spd_ext_len == 0) {
+ /*
+ * No algorithms are defined in the kernel, which caused
+ * the extension length to be zero, and spdsock_get_ext()
+ * to fail with a KGE_LEN error. This is not an error
+ * condition, so we return nicely.
+ */
+ return;
+ } else if (retval != 0) {
+ if (strlen(diag_buf) != 0)
+ warnx("%s", diag_buf);
+ errx(EXIT_FAILURE, gettext("invalid extension "
+ "in dump algs reply (%d)"), retval);
+ }
+
+ if (exts[SPD_EXT_ACTION] == NULL) {
+ errx(EXIT_FAILURE,
+ gettext("action missing in dump algs reply"));
+ }
+
+ actp = (struct spd_ext_actions *)exts[SPD_EXT_ACTION];
+ start = (uint64_t *)actp;
+ end = (start + actp->spd_actions_len);
+ endattr = (struct spd_attribute *)end;
+ attr = (struct spd_attribute *)&actp[1];
+
+ bzero(&alg, sizeof (alg));
+ nkey_sizes = nblock_sizes = 0;
+
+ (void) printf("Kernel list of algorithms:\n\n");
+
+ while (attr < endattr) {
+ switch (attr->spd_attr_tag) {
+ case SPD_ATTR_NOP:
+ case SPD_ATTR_EMPTY:
+ break;
+ case SPD_ATTR_END:
+ attr = endattr;
+ /* FALLTHRU */
+ case SPD_ATTR_NEXT:
+ /*
+ * Note that if the message received from the spdsock
+ * has a premature SPD_ATTR_END or SPD_ATTR_NEXT, this
+ * could cause the current algorithm to be only
+ * partially initialized.
+ */
+ dump_alg(&alg);
+ free(alg.a_key_sizes);
+ free(alg.a_block_sizes);
+ free(alg.a_mech_name);
+ bzero(&alg, sizeof (alg));
+ nkey_sizes = nblock_sizes = 0;
+ break;
+
+ case SPD_ATTR_ALG_ID:
+ alg.a_alg_num = attr->spd_attr_value;
+ break;
+
+ case SPD_ATTR_ALG_PROTO:
+ alg.a_proto_num = attr->spd_attr_value;
+ break;
+
+ case SPD_ATTR_ALG_INCRBITS:
+ alg.a_key_increment = attr->spd_attr_value;
+ break;
+
+ case SPD_ATTR_ALG_NKEYSIZES:
+ nkey_sizes = attr->spd_attr_value;
+ if (alg.a_key_sizes != NULL) {
+ errx(EXIT_FAILURE, gettext("duplicate number "
+ "of keys in dump algs reply"));
+ }
+ alg.a_key_sizes = calloc(nkey_sizes + 1, sizeof (int));
+ if (alg.a_key_sizes == NULL)
+ bail_nomem();
+ cur_key = 0;
+ break;
+
+ case SPD_ATTR_ALG_KEYSIZE:
+ if (cur_key >= nkey_sizes) {
+ errx(EXIT_FAILURE, gettext("too many key sizes"
+ " in dump algs reply"));
+ }
+ alg.a_key_sizes[cur_key++] = attr->spd_attr_value;
+ break;
+
+ case SPD_ATTR_ALG_NBLOCKSIZES:
+ nblock_sizes = attr->spd_attr_value;
+ if (alg.a_block_sizes != NULL) {
+ errx(EXIT_FAILURE, gettext("duplicate number "
+ "of blocks in dump algs reply"));
+ }
+ alg.a_block_sizes = calloc(nblock_sizes + 1,
+ sizeof (int));
+ if (alg.a_block_sizes == NULL)
+ bail_nomem();
+ cur_block = 0;
+ break;
+
+ case SPD_ATTR_ALG_BLOCKSIZE:
+ if (cur_block >= nblock_sizes) {
+ errx(EXIT_FAILURE, gettext("too many block "
+ "sizes in dump algs reply"));
+ }
+ alg.a_block_sizes[cur_block++] = attr->spd_attr_value;
+ break;
+
+ case SPD_ATTR_ALG_MECHNAME: {
+ char *mech_name;
+
+ if (alg.a_mech_name != NULL) {
+ errx(EXIT_FAILURE, gettext(
+ "duplicate mech name in dump algs reply"));
+ }
+
+ alg.a_mech_name = malloc(attr->spd_attr_value);
+ if (alg.a_mech_name == NULL)
+ bail_nomem();
+
+ mech_name = (char *)(attr + 1);
+ bcopy(mech_name, alg.a_mech_name, attr->spd_attr_value);
+ attr = (struct spd_attribute *)((uint64_t *)attr +
+ SPD_8TO64(attr->spd_attr_value));
+ break;
+ }
+ }
+ attr++;
+ }
+
+}
+
+
+static int *
+parse_intlist(char *args, int *num_args)
+{
+ int *rc = NULL;
+ char *holder = NULL;
+
+ while ((holder = strtok((holder == NULL) ? args : NULL, comma)) !=
+ NULL) {
+ (*num_args)++;
+ rc = realloc(rc, ((*num_args) + 1) * sizeof (int));
+ if (rc == NULL)
+ bail_nomem();
+ rc[(*num_args) - 1] = atoi(holder);
+ if (rc[(*num_args) - 1] == 0)
+ usage(); /* Malformed integer list! */
+ rc[*num_args] = 0;
+ }
+
+ return (rc);
+}
+
+static void
+new_alg(void)
+{
+ struct ipsecalgent newbie;
+ int num_names = 0, num_block_sizes = 0, num_key_sizes = 0;
+ int i, rc;
+ char *holder = NULL;
+
+ /* Parameter reality check... */
+ if (proto_number == -1) {
+ if (proto_name == NULL) {
+ warnx(gettext("Missing protocol number."));
+ usage();
+ }
+ proto_number = getipsecprotobyname(proto_name);
+ if (proto_number == -1) {
+ warnx(gettext("Unknown protocol."));
+ usage();
+ }
+ }
+ if (alg_number == -1) {
+ warnx(gettext("Missing algorithm number."));
+ usage();
+ }
+ if (key_sizes_string == NULL) {
+ warnx(gettext("Missing key size(s)."));
+ usage();
+ }
+ if (alg_names_string == NULL) {
+ warnx(gettext("Missing algorithm name(s)."));
+ usage();
+ }
+ if (block_sizes_string == NULL) {
+ warnx(gettext("Missing block/MAC lengths"));
+ usage();
+ }
+ if (mech_name == NULL) {
+ warnx(gettext("Missing mechanism name."));
+ usage();
+ }
+
+ newbie.a_proto_num = proto_number;
+ newbie.a_alg_num = alg_number;
+ newbie.a_key_increment = increment;
+ newbie.a_mech_name = mech_name;
+
+ newbie.a_names = NULL;
+ while ((holder = strtok((holder == NULL) ? alg_names_string : NULL,
+ comma)) != NULL) {
+ newbie.a_names = realloc(newbie.a_names,
+ sizeof (char *) * ((++num_names) + 1));
+ if (newbie.a_names == NULL)
+ bail_nomem();
+ newbie.a_names[num_names - 1] = holder;
+ newbie.a_names[num_names] = NULL;
+ }
+
+ /* Extract block sizes. */
+ newbie.a_block_sizes = parse_intlist(block_sizes_string,
+ &num_block_sizes);
+
+ /* Extract key sizes. */
+ if ((holder = strchr(key_sizes_string, '-')) != NULL) {
+ /* key sizes by range, key size increment required */
+ if (newbie.a_key_increment == 0) {
+ warnx(gettext("Missing key increment"));
+ usage();
+ }
+ newbie.a_key_sizes = calloc(sizeof (int),
+ LIBIPSEC_ALGS_KEY_NUM_VAL);
+ if (newbie.a_key_sizes == NULL)
+ bail_nomem();
+ *holder = '\0';
+ holder++;
+ /*
+ * At this point, holder points to high, key_sizes_string
+ * points to low.
+ */
+ newbie.a_key_sizes[LIBIPSEC_ALGS_KEY_MIN_IDX] =
+ atoi(key_sizes_string);
+ if (newbie.a_key_sizes[LIBIPSEC_ALGS_KEY_MIN_IDX] == 0) {
+ warnx(gettext("Invalid lower key size range"));
+ usage();
+ }
+ newbie.a_key_sizes[LIBIPSEC_ALGS_KEY_MAX_IDX] = atoi(holder);
+ if (newbie.a_key_sizes[LIBIPSEC_ALGS_KEY_MAX_IDX] == 0) {
+ warnx(gettext("Invalid higher key size range"));
+ usage();
+ }
+
+ /* sanity check key range consistency */
+ if (newbie.a_key_sizes[LIBIPSEC_ALGS_KEY_MIN_IDX] >=
+ newbie.a_key_sizes[LIBIPSEC_ALGS_KEY_MAX_IDX]) {
+ warnx(gettext("Invalid key size range (min >= max)"));
+ usage();
+ }
+
+ /* check key increment vs key range */
+ if (((newbie.a_key_sizes[LIBIPSEC_ALGS_KEY_MAX_IDX] -
+ newbie.a_key_sizes[LIBIPSEC_ALGS_KEY_MIN_IDX]) %
+ newbie.a_key_increment) != 0) {
+ warnx(gettext("Key size increment"
+ " not consistent with key size range"));
+ usage();
+ }
+
+ /* default key size */
+ if (default_keylen != 0) {
+ /* check specified default key size */
+ if (default_keylen <
+ newbie.a_key_sizes[LIBIPSEC_ALGS_KEY_MIN_IDX] ||
+ default_keylen >
+ newbie.a_key_sizes[LIBIPSEC_ALGS_KEY_MAX_IDX] ||
+ ((default_keylen -
+ newbie.a_key_sizes[LIBIPSEC_ALGS_KEY_MIN_IDX]) %
+ newbie.a_key_increment) != 0) {
+ warnx(gettext("Default key size not consistent"
+ " with key size range"));
+ usage();
+ }
+ newbie.a_key_sizes[LIBIPSEC_ALGS_KEY_DEF_IDX] =
+ default_keylen;
+ } else {
+ /* min key size in range if not specified */
+ newbie.a_key_sizes[LIBIPSEC_ALGS_KEY_DEF_IDX] =
+ newbie.a_key_sizes[LIBIPSEC_ALGS_KEY_MIN_IDX];
+ }
+ } else {
+ /* key sizes by enumeration */
+ if (newbie.a_key_increment != 0) {
+ warnx(gettext("Key increment must "
+ "not be specified with key sizes enumeration"));
+ usage();
+ }
+ newbie.a_key_sizes = parse_intlist(key_sizes_string,
+ &num_key_sizes);
+
+ /* default key size */
+ if (default_keylen != 0 && default_keylen !=
+ newbie.a_key_sizes[0]) {
+ /*
+ * The default key size is not at the front of the
+ * list. Swap it with the first element of the list.
+ */
+ for (i = 1; i < num_key_sizes; i++) {
+ if (newbie.a_key_sizes[i] == default_keylen)
+ break;
+ if (i >= num_key_sizes) {
+ warnx(gettext("Default key size not "
+ "in list of key sizes"));
+ usage();
+ }
+ newbie.a_key_sizes[i] = newbie.a_key_sizes[0];
+ newbie.a_key_sizes[0] = default_keylen;
+ }
+ }
+ }
+
+ /* Call things! */
+ if ((rc = addipsecalg(&newbie, adddel_flags)) != 0) {
+ errx(EXIT_FAILURE, gettext("addipsecalg() call failed: "
+ "%s"), ipsecalgs_diag(rc));
+ }
+
+ free(newbie.a_names);
+ free(newbie.a_block_sizes);
+ free(newbie.a_key_sizes);
+}
+
+static void
+new_proto(void)
+{
+ int rc;
+
+ if ((rc = addipsecproto(proto_name, proto_number, proto_exec_mode,
+ adddel_flags))
+ != 0) {
+ errx(EXIT_FAILURE, gettext(
+ "Cannot add protocol %1$d \"%2$s\": %3$s"), proto_number,
+ proto_name, ipsecalgs_diag(rc));
+ }
+}
+
+static void
+remove_alg(void)
+{
+ int rc;
+
+ if (proto_number == -1) {
+ if (proto_name == NULL) {
+ warnx(gettext("Missing protocol number."));
+ usage();
+ }
+ proto_number = getipsecprotobyname(proto_name);
+ if (proto_number == -1) {
+ errx(EXIT_FAILURE, gettext(
+ "Unknown protocol \"%s\"."), proto_name);
+ }
+ }
+
+ if (alg_number == -1) {
+ if (alg_names_string == NULL) {
+ errx(EXIT_FAILURE, gettext("Missing algorithm ID."));
+ }
+ if (strchr(alg_names_string, ',') != NULL) {
+ errx(EXIT_FAILURE, gettext(
+ "Specify a single algorithm name for removal, "
+ "not a list."));
+ }
+ if ((rc = delipsecalgbyname(alg_names_string, proto_number))
+ != 0) {
+ errx(EXIT_FAILURE, gettext(
+ "Could not remove algorithm %1$s: %2$s"),
+ alg_names_string, ipsecalgs_diag(rc));
+ }
+ } else {
+ if ((rc = delipsecalgbynum(alg_number, proto_number)) != 0) {
+ errx(EXIT_FAILURE, gettext(
+ "Could not remove algorithm %1$d: %2$s"),
+ alg_number, ipsecalgs_diag(rc));
+ }
+ }
+}
+
+static void
+remove_proto(void)
+{
+ int rc;
+
+ if (proto_number == -1) {
+ if (proto_name == NULL) {
+ warnx(gettext("Please specify protocol to remove."));
+ usage();
+ }
+ if ((rc = delipsecprotobyname(proto_name)) != 0) {
+ errx(EXIT_FAILURE, gettext(
+ "Could not remove protocol %1$s: %2$s"),
+ proto_name, ipsecalgs_diag(rc));
+ }
+ } else {
+ if ((rc = delipsecprotobynum(proto_number)) != 0) {
+ errx(EXIT_FAILURE, gettext(
+ "Could not remove protocol %1$d: %2$s"),
+ proto_number, ipsecalgs_diag(rc));
+ }
+ }
+}
+
+static void
+set_exec_mode(void)
+{
+ int rc;
+
+ if (proto_number == -1) {
+ if (proto_name == NULL) {
+ warnx(gettext(
+ "Please specify protocol name or number."));
+ usage();
+ }
+ proto_number = getipsecprotobyname(proto_name);
+ if (proto_number == -1) {
+ errx(EXIT_FAILURE, gettext("Unknown protocol %s"),
+ proto_name);
+ }
+ }
+
+ if ((rc = ipsecproto_set_exec_mode(proto_number, proto_exec_mode))
+ != 0) {
+ errx(EXIT_FAILURE, gettext("Cannot set execution mode: %s"),
+ ipsecalgs_diag(rc));
+ }
+}
+
+/*
+ * Print a description of an algorithm to standard output.
+ */
+static void
+dump_alg(struct ipsecalgent *alg)
+{
+ int *ifloater;
+ char **floater;
+
+ /* protocol number */
+ (void) printf(gettext("\tProtocol number: %d\n"), alg->a_proto_num);
+
+ /* algorithm number */
+ (void) printf(gettext("\tAlgorithm number: %d\n"), alg->a_alg_num);
+
+ /* algorithm name(s) */
+ if (alg->a_names != NULL) {
+ (void) printf(gettext("\tAlgorithm names: "));
+ floater = alg->a_names;
+ assert(floater != NULL && *floater != NULL);
+ do {
+ /* Assume at least one string. */
+ (void) printf("%s", *floater);
+ if (*(++floater) != NULL)
+ (void) putchar(',');
+ } while (*floater != NULL);
+ (void) putchar('\n');
+ }
+
+ /* mechanism name */
+ (void) printf(gettext("\tMechanism Name: %s\n"), alg->a_mech_name);
+
+ /* block/MAC sizes */
+ (void) printf(gettext("\tBlock sizes or MAC sizes: "));
+ ifloater = alg->a_block_sizes;
+ (void) list_ints(stdout, ifloater);
+ (void) putchar('\n');
+
+ /* key sizes */
+ (void) printf(gettext("\tKey sizes: "));
+ if (alg->a_key_increment != 0)
+ /* key specified by range */
+ (void) printf(gettext(
+ "%1$d-%2$d, increment %3$d, default %4$d"),
+ alg->a_key_sizes[LIBIPSEC_ALGS_KEY_MIN_IDX],
+ alg->a_key_sizes[LIBIPSEC_ALGS_KEY_MAX_IDX],
+ alg->a_key_increment,
+ alg->a_key_sizes[LIBIPSEC_ALGS_KEY_DEF_IDX]);
+ else
+ /* key specified by enumeration */
+ (void) list_ints(stdout, alg->a_key_sizes);
+ (void) putchar('\n');
+
+ (void) putchar('\n');
+}
+
+/*
+ * Print the description of a protocol.
+ */
+static void
+dump_proto(uint_t proto_id)
+{
+ char *proto_name;
+ ipsecalgs_exec_mode_t exec_mode;
+
+ /* protocol name and number */
+ proto_name = getipsecprotobynum(proto_id);
+ (void) printf(gettext("Protocol %1$d/%2$s "),
+ proto_id, proto_name != NULL ? proto_name : gettext("<unknown>"));
+
+ /* execution mode */
+ (void) printf("(%s", gettext("execution mode: "));
+
+ if (ipsecproto_get_exec_mode(proto_id, &exec_mode) != 0) {
+ (void) printf(gettext("<unknown>"));
+ } else {
+ switch (exec_mode) {
+ case LIBIPSEC_ALGS_EXEC_SYNC:
+ (void) printf("sync");
+ break;
+ case LIBIPSEC_ALGS_EXEC_ASYNC:
+ (void) printf("async");
+ break;
+ }
+ }
+
+ (void) printf(")\n\n");
+
+ free(proto_name);
+}
+
+
+/*
+ * Algorithm walker table. Call proto_action() for each protocol,
+ * and alg_action() for each algorithm.
+ */
+static void
+algs_walker(void (*alg_action)(struct ipsecalgent *),
+ void (*proto_action)(uint_t))
+{
+ int *proto_nums, proto_count, i;
+ int *alg_nums, alg_count, j;
+ struct ipsecalgent *alg;
+
+ proto_nums = getipsecprotos(&proto_count);
+ if (proto_nums == NULL) {
+ errx(EXIT_FAILURE, gettext("getipsecprotos() failed."));
+ }
+
+ for (i = 0; i < proto_count; i++) {
+
+ if (proto_action != NULL)
+ proto_action(proto_nums[i]);
+
+ alg_nums = getipsecalgs(&alg_count, proto_nums[i]);
+ if (alg_nums == NULL) {
+ free(proto_nums);
+ errx(EXIT_FAILURE, gettext("getipsecalgs() failed."));
+ }
+
+ for (j = 0; j < alg_count; j++) {
+ alg = getipsecalgbynum(alg_nums[j], proto_nums[i],
+ NULL);
+ if (alg == NULL)
+ continue;
+ if (alg_action != NULL)
+ alg_action(alg);
+ freeipsecalgent(alg);
+ }
+ free(alg_nums);
+ }
+ free(proto_nums);
+}
+
+/*
+ * Use just the libnsl/libipsecutil APIs to dump out all of the algorithms.
+ */
+static void
+show_algs(void)
+{
+ /* Yes, I'm aware that this'll produce TWO newlines. */
+ (void) puts(gettext(
+ "List of algorithms, grouped by IPsec protocol:\n"));
+
+ algs_walker(dump_alg, dump_proto);
+}
+
+static int
+try_int(char *optarg, const char *what)
+{
+ int rc = atoi(optarg);
+
+ if (rc <= 0) {
+ warnx(gettext("Invalid %s value"), what);
+ usage();
+ }
+ return (rc);
+}
+
+static void
+try_cmd(cmd_t newcmd)
+{
+ if (cmd != CMD_NONE)
+ usage();
+ cmd = newcmd;
+}
+
+int
+main(int argc, char *argv[])
+{
+ int c;
+
+ (void) setlocale(LC_ALL, "");
+#if !defined(TEXT_DOMAIN)
+#define TEXT_DOMAIN "SYS_TEST"
+#endif
+ (void) textdomain(TEXT_DOMAIN);
+
+ if (argc == 1) {
+ show_algs();
+ return (EXIT_SUCCESS);
+ }
+
+ while ((c = getopt(argc, argv,
+ "aflrRsb:p:P:i:k:K:m:n:N:e:")) != EOF) {
+ switch (c) {
+ case 'a':
+ try_cmd(CMD_ADD);
+ break;
+ case 'f':
+ /* multiple occurences of -f are harmless */
+ adddel_flags = LIBIPSEC_ALGS_ADD_FORCE;
+ break;
+ case 'l':
+ try_cmd(CMD_LIST_KERNEL);
+ break;
+ case 'r':
+ try_cmd(CMD_DEL);
+ break;
+ case 'R':
+ try_cmd(CMD_DEL_PROTO);
+ break;
+ case 's':
+ /* multiple occurences of -s are harmless */
+ synch_kernel = B_TRUE;
+ break;
+ case 'n':
+ if (alg_names_string != NULL)
+ usage();
+ alg_names_string = optarg;
+ break;
+ case 'b':
+ if (block_sizes_string != NULL)
+ usage();
+ block_sizes_string = optarg;
+ break;
+ case 'p':
+ if (proto_name != NULL)
+ usage();
+ proto_name = optarg;
+ break;
+ case 'P':
+ if (proto_number != -1)
+ usage();
+ proto_number = try_int(optarg,
+ gettext("protocol number"));
+ break;
+ case 'e':
+ if (exec_mode_string != NULL)
+ usage();
+ exec_mode_string = optarg;
+ if (_str_to_ipsec_exec_mode(exec_mode_string,
+ &proto_exec_mode) == -1) {
+ warnx(gettext("Invalid execution mode \"%s\""),
+ exec_mode_string);
+ usage();
+ }
+ break;
+ case 'i':
+ if (increment != 0)
+ usage();
+ increment = try_int(optarg,
+ gettext("key size increment"));
+ break;
+ case 'k':
+ if (key_sizes_string != NULL)
+ usage();
+ key_sizes_string = optarg;
+ break;
+ case 'K':
+ if (default_keylen != 0)
+ usage();
+ default_keylen = try_int(optarg,
+ gettext("default key size"));
+ break;
+ case 'm':
+ if (mech_name != NULL)
+ usage();
+ mech_name = optarg;
+ break;
+ case 'N':
+ if (alg_number != -1)
+ usage();
+ alg_number = try_int(optarg,
+ gettext("algorithm number"));
+ break;
+ }
+ }
+
+ /*
+ * When both protocol name (-p) and protocol number (-P) are
+ * specified, a new protocol is being defined.
+ */
+ if (proto_number != -1 && proto_name != NULL)
+ try_cmd(CMD_ADD_PROTO);
+ else if (exec_mode_string != NULL)
+ try_cmd(CMD_EXEC_MODE);
+
+ /*
+ * Process specified command.
+ */
+ switch (cmd) {
+ case CMD_ADD:
+ new_alg();
+ break;
+ case CMD_ADD_PROTO:
+ new_proto();
+ break;
+ case CMD_DEL:
+ remove_alg();
+ break;
+ case CMD_DEL_PROTO:
+ remove_proto();
+ break;
+ case CMD_EXEC_MODE:
+ set_exec_mode();
+ break;
+ case CMD_LIST_KERNEL:
+ if (synch_kernel)
+ usage();
+ list_kernel_algs();
+ break;
+ default:
+ if (!synch_kernel)
+ usage();
+ }
+
+ if (synch_kernel)
+ kernel_synch();
+
+ return (EXIT_SUCCESS);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/ipsecconf.c b/usr/src/cmd/cmd-inet/usr.sbin/ipsecconf.c
new file mode 100644
index 0000000000..ad9cc36187
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/ipsecconf.c
@@ -0,0 +1,5327 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <strings.h>
+#include <stropts.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <arpa/inet.h>
+#include <locale.h>
+#include <syslog.h>
+#include <pwd.h>
+#include <sys/param.h>
+#include <sys/sysmacros.h> /* MIN, MAX */
+#include <sys/sockio.h>
+#include <net/pfkeyv2.h>
+#include <net/pfpolicy.h>
+#include <inet/ipsec_impl.h>
+#include <signal.h>
+#include <errno.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <sys/systeminfo.h>
+#include <nss_dbdefs.h> /* NSS_BUFLEN_HOSTS */
+#include <netinet/in.h>
+#include <assert.h>
+#include <inet/ip.h>
+#include <ipsec_util.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/icmp6.h>
+
+/*
+ * Buffer length to read in pattern/properties.
+ */
+#define MAXLEN 1024
+
+/*
+ * Used by parse_one and parse/parse_action to communicate
+ * the errors. -1 is failure, which is not defined here.
+ */
+enum parse_errors {PARSE_SUCCESS, PARSE_EOF};
+
+/*
+ * For spdsock_get_ext() diagnostics.
+ */
+#define SPDSOCK_DIAG_BUF_LEN 128
+static char spdsock_diag_buf[SPDSOCK_DIAG_BUF_LEN];
+
+/*
+ * Define CURL here so that while you are reading
+ * this code, it does not affect "vi" in pattern
+ * matching.
+ */
+#define CURL_BEGIN '{'
+#define CURL_END '}'
+#define MAXARGS 20
+
+/*
+ * IPSEC_CONF_ADD should start with 1, so that when multiple commands
+ * are given, we can fail the request.
+ */
+
+static enum ipsec_cmds {IPSEC_CONF_ADD = 1, IPSEC_CONF_DEL, IPSEC_CONF_VIEW,
+ IPSEC_CONF_FLUSH, IPSEC_CONF_LIST, IPSEC_CONF_SUB};
+
+static const char policy_conf_file[] = "/var/run/ipsecpolicy.conf";
+static const char lock_file[] = "/var/run/ipsecconf.lock";
+static const char index_tag[] = "#INDEX";
+
+#define POLICY_CONF_FILE policy_conf_file
+#define LOCK_FILE lock_file
+#define INDEX_TAG index_tag
+
+/*
+ * Valid algorithm length.
+ */
+#define VALID_ALG_LEN 40
+
+/* Types of Error messages */
+typedef enum error_tpye {BAD_ERROR, DUP_ERROR} error_type_t;
+
+static int cmd;
+static char *filename;
+static char lo_buf[MAXLEN]; /* Leftover buffer */
+
+/* Error reporting stuff */
+#define CBUF_LEN 4096 /* Maximum size of the cmd */
+/*
+ * Following are used for reporting errors with arguments.
+ * We store the line numbers of each argument as we parse them,
+ * so that the error reporting is more specific. We can have only
+ * MAXARGS -1 for pattern and properties and one for action.
+ */
+#define ARG_BUF_LEN ((2 * (MAXARGS - 1)) + 1)
+static int arg_indices[ARG_BUF_LEN];
+static int argindex;
+static int linecount;
+static char cbuf[CBUF_LEN]; /* Command buffer */
+static int cbuf_offset;
+
+
+#define BYPASS_POLICY_BOOST 0x00800000
+#define ESP_POLICY_BOOST 0x00400000
+#define AH_POLICY_BOOST 0x00200000
+#define INITIAL_BASE_PRIORITY 0x000fffff
+
+/*
+ * the number used to order the
+ * rules starts at a certain base and
+ * goes down. i.e. rules earlier in
+ * the file are checked first
+ */
+static uint32_t priority = INITIAL_BASE_PRIORITY;
+
+#define AH_AUTH 0
+#define ESP_ENCR 1
+#define ESP_AUTH 2
+
+
+/*
+ * for deleting adds on error
+ */
+
+typedef struct d_list_s
+{
+ struct d_list_s *next;
+ int index;
+} d_list_t;
+
+static d_list_t *d_list = NULL;
+static d_list_t *d_tail = NULL;
+
+
+/*
+ * Used for multi-homed source/dest hosts.
+ */
+static struct hostent *shp, *dhp;
+static unsigned int splen, dplen;
+static boolean_t has_saprefix, has_daprefix;
+static uint32_t seq_cnt = 0;
+
+/* lexxed out action and related properties */
+typedef struct ap_s
+{
+ char *act;
+ char *prop[MAXARGS + 1];
+} ap_t;
+
+
+/* one lexxed out rule */
+typedef struct act_prop_s {
+ char *pattern[MAXARGS+1];
+ ap_t ap[MAXARGS + 1];
+} act_prop_t;
+
+typedef struct
+{
+ uint8_t alg_id;
+ uint32_t alg_minbits;
+ uint32_t alg_maxbits;
+} algreq_t;
+
+/* structure to hold all information for one act_prop_t */
+typedef struct ips_act_props_s {
+ struct ips_act_props_s *iap_next;
+ struct ips_conf_s *iap_head;
+
+/*
+ * IPsec action types (in SPD_ATTR_TYPE attribute)
+ * SPD_ACTTYPE_DROP 0x0001
+ * SPD_ACTTYPE_PASS 0x0002
+ * SPD_ACTTYPE_IPSEC 0x0003
+ */
+ uint16_t iap_action;
+ uint16_t iap_act_tok;
+
+/*
+ * Action ATTR flags (in SPD_ATTR_FLAGS attribute)
+ * SPD_APPLY_AH 0x0001
+ * SPD_APPLY_ESP 0x0002
+ * SPD_APPLY_SE 0x0004 * self-encapsulation *
+ * SPD_APPLY_COMP 0x0008 * compression; NYI *
+ * SPD_APPLY_UNIQUE 0x0010 * unique per-flow SA *
+ * SPD_APPLY_BYPASS 0x0020 * bypass policy *
+ */
+ uint16_t iap_attr;
+ uint16_t iap_attr_tok[5];
+
+ algreq_t iap_aauth;
+ algreq_t iap_eencr;
+ algreq_t iap_eauth;
+
+ uint32_t iap_life_soft_time;
+ uint32_t iap_life_hard_time;
+ uint32_t iap_life_soft_bytes;
+ uint32_t iap_life_hard_bytes;
+
+} ips_act_props_t;
+
+#define V4_PART_OF_V6(v6) v6._S6_un._S6_u32[3]
+
+typedef struct ips_conf_s {
+ /* selector */
+ uint16_t patt_tok[8];
+ uint8_t has_saddr;
+ uint8_t has_daddr;
+ uint8_t has_smask;
+ uint8_t has_dmask;
+ uint8_t has_type;
+ uint8_t has_code;
+ uint16_t swap;
+
+ struct in6_addr ips_src_addr_v6;
+ struct in6_addr ips_src_mask_v6;
+ struct in6_addr ips_dst_addr_v6;
+ struct in6_addr ips_dst_mask_v6;
+ uint8_t ips_src_mask_len;
+ uint8_t ips_dst_mask_len;
+ in_port_t ips_src_port_min;
+ in_port_t ips_src_port_max;
+ in_port_t ips_dst_port_min;
+ in_port_t ips_dst_port_max;
+ uint8_t ips_icmp_type;
+ uint8_t ips_icmp_type_end;
+ uint8_t ips_icmp_code;
+ uint8_t ips_icmp_code_end;
+ uint8_t ips_ulp_prot;
+ uint8_t ips_ipsec_prot;
+ uint8_t ips_isv4;
+ /*
+ * SPD_RULE_FLAG_INBOUND 0x0001
+ * SPD_RULE_FLAG_OUTBOUND 0x0002
+ */
+ uint8_t ips_dir;
+ uint64_t ips_policy_index;
+ uint32_t ips_act_cnt;
+ ips_act_props_t *ips_acts;
+} ips_conf_t;
+
+#define ips_src_addr V4_PART_OF_V6(ips_src_addr_v6)
+#define ips_dst_addr V4_PART_OF_V6(ips_dst_addr_v6)
+
+static int ipsecconf_nflag; /* Used only with -l option */
+static int ipsecconf_qflag; /* Used only with -a|-r option */
+
+typedef struct str_val {
+ const char *string;
+ int value;
+} str_val_t;
+
+typedef struct str_tval {
+ const char *string;
+ int tok_val;
+ int value;
+} str_tval_t;
+
+static int parse_int(const char *);
+static void usage(void);
+static int ipsec_conf_del(int, boolean_t);
+static int ipsec_conf_add(void);
+static int ipsec_conf_sub(void);
+static int ipsec_conf_flush(int);
+static int ipsec_conf_view(void);
+static int ipsec_conf_list(void);
+static int lock(void);
+static int unlock(int);
+static int parse_one(FILE *, act_prop_t *);
+static void reconfigure();
+static void in_prefixlentomask(unsigned int, uchar_t *);
+static unsigned int in_getprefixlen(char *);
+static int in_masktoprefix(uint8_t *, boolean_t);
+static int parse_address(int, char *);
+#ifdef DEBUG_HEAVY
+static void pfpol_msg_dump(spd_msg_t *msg, char *);
+#endif /* DEBUG_HEAVY */
+static void print_pfpol_msg(spd_msg_t *);
+static int pfp_delete_rule(uint64_t);
+static void ipsec_conf_admin(uint8_t);
+static void print_bit_range(int, int);
+static void nuke_adds();
+
+#ifdef DEBUG
+static void dump_conf(ips_conf_t *);
+#endif
+
+typedef struct
+{
+ uint32_t id;
+ uint32_t minkeybits;
+ uint32_t maxkeybits;
+ uint32_t defkeybits;
+ uint32_t incr;
+} alginfo_t;
+
+static int ipsec_nalgs[3];
+static alginfo_t known_algs[3][256];
+
+#define IPS_SRC_MASK SPD_EXT_LCLADDR + 100
+#define IPS_DST_MASK SPD_EXT_REMADDR + 100
+
+/*
+ * if inbound, src=remote, dst=local
+ * if outbound, src=local, dst=remote
+ */
+
+#define TOK_saddr 1
+#define TOK_daddr 2
+#define TOK_sport 3
+#define TOK_dport 4
+#define TOK_smask 5
+#define TOK_dmask 6
+#define TOK_ulp 7
+#define TOK_local 8
+#define TOK_lport 9
+#define TOK_remote 10
+#define TOK_rport 11
+#define TOK_dir 12
+#define TOK_type 13
+#define TOK_code 14
+
+#define IPS_SA SPD_ATTR_END
+#define IPS_DIR SPD_ATTR_EMPTY
+
+
+static str_tval_t pattern_table[] = {
+ {"saddr", TOK_saddr, SPD_EXT_LCLADDR},
+ {"src", TOK_saddr, SPD_EXT_LCLADDR},
+ {"srcaddr", TOK_saddr, SPD_EXT_LCLADDR},
+ {"daddr", TOK_daddr, SPD_EXT_REMADDR},
+ {"dst", TOK_daddr, SPD_EXT_REMADDR},
+ {"dstaddr", TOK_daddr, SPD_EXT_REMADDR},
+ {"sport", TOK_sport, SPD_EXT_LCLPORT},
+ {"dport", TOK_dport, SPD_EXT_REMPORT},
+ {"smask", TOK_smask, IPS_SRC_MASK},
+ {"dmask", TOK_dmask, IPS_DST_MASK},
+ {"ulp", TOK_ulp, SPD_EXT_PROTO},
+ {"proto", TOK_ulp, SPD_EXT_PROTO},
+ {"local", TOK_local, SPD_EXT_LCLADDR},
+ {"laddr", TOK_local, SPD_EXT_LCLADDR},
+ {"lport", TOK_lport, SPD_EXT_LCLPORT},
+ {"remote", TOK_remote, SPD_EXT_REMADDR},
+ {"raddr", TOK_remote, SPD_EXT_REMADDR},
+ {"rport", TOK_rport, SPD_EXT_REMPORT},
+ {"dir", TOK_dir, IPS_DIR},
+ {"type", TOK_type, SPD_EXT_ICMP_TYPECODE},
+ {"code", TOK_code, SPD_EXT_ICMP_TYPECODE},
+ {NULL, 0, 0},
+};
+
+#define TOK_apply 1
+#define TOK_permit 2
+#define TOK_ipsec 3
+#define TOK_bypass 4
+#define TOK_drop 5
+#define TOK_or 6
+
+static str_tval_t action_table[] = {
+ {"apply", TOK_apply, SPD_ACTTYPE_IPSEC},
+ {"permit", TOK_permit, SPD_ACTTYPE_IPSEC},
+ {"ipsec", TOK_ipsec, SPD_ACTTYPE_IPSEC},
+ {"bypass", TOK_bypass, SPD_ACTTYPE_PASS},
+ {"pass", TOK_bypass, SPD_ACTTYPE_PASS},
+ {"drop", TOK_drop, SPD_ACTTYPE_DROP},
+ {"or", TOK_or, 0},
+ {NULL, 0, 0},
+};
+
+static str_val_t property_table[] = {
+ {"auth_algs", SPD_ATTR_AH_AUTH},
+ {"encr_algs", SPD_ATTR_ESP_ENCR},
+ {"encr_auth_algs", SPD_ATTR_ESP_AUTH},
+ {"sa", IPS_SA},
+ {"dir", IPS_DIR},
+ {NULL, 0},
+};
+
+static str_val_t icmp_type_table[] = {
+ {"unreach", ICMP_UNREACH},
+ {"echo", ICMP_ECHO},
+ {"echorep", ICMP_ECHOREPLY},
+ {"squench", ICMP_SOURCEQUENCH},
+ {"redir", ICMP_REDIRECT},
+ {"timex", ICMP_TIMXCEED},
+ {"paramprob", ICMP_PARAMPROB},
+ {"timest", ICMP_TSTAMP},
+ {"timestrep", ICMP_TSTAMPREPLY},
+ {"inforeq", ICMP_IREQ},
+ {"inforep", ICMP_IREQREPLY},
+ {"maskreq", ICMP_MASKREQ},
+ {"maskrep", ICMP_MASKREPLY},
+ {"unreach6", ICMP6_DST_UNREACH},
+ {"pkttoobig6", ICMP6_PACKET_TOO_BIG},
+ {"timex6", ICMP6_TIME_EXCEEDED},
+ {"paramprob6", ICMP6_PARAM_PROB},
+ {"echo6", ICMP6_ECHO_REQUEST},
+ {"echorep6", ICMP6_ECHO_REPLY},
+ {"router-sol6", ND_ROUTER_SOLICIT},
+ {"router-ad6", ND_ROUTER_ADVERT},
+ {"neigh-sol6", ND_NEIGHBOR_SOLICIT},
+ {"neigh-ad6", ND_NEIGHBOR_ADVERT},
+ {"redir6", ND_REDIRECT},
+ {NULL, 0},
+};
+
+static str_val_t icmp_code_table[] = {
+ {"net-unr", ICMP_UNREACH_NET},
+ {"host-unr", ICMP_UNREACH_HOST},
+ {"proto-unr", ICMP_UNREACH_PROTOCOL},
+ {"port-unr", ICMP_UNREACH_PORT},
+ {"needfrag", ICMP_UNREACH_NEEDFRAG},
+ {"srcfail", ICMP_UNREACH_SRCFAIL},
+ {"net-unk", ICMP_UNREACH_NET_UNKNOWN},
+ {"host-unk", ICMP_UNREACH_HOST_UNKNOWN},
+ {"isolate", ICMP_UNREACH_ISOLATED},
+ {"net-prohib", ICMP_UNREACH_NET_PROHIB},
+ {"host-prohib", ICMP_UNREACH_HOST_PROHIB},
+ {"net-tos", ICMP_UNREACH_TOSNET},
+ {"host-tos", ICMP_UNREACH_TOSHOST},
+ {"filter-prohib", ICMP_UNREACH_FILTER_PROHIB},
+ {"host-preced", ICMP_UNREACH_HOST_PRECEDENCE},
+ {"cutoff-preced", ICMP_UNREACH_PRECEDENCE_CUTOFF},
+ {"no-route6", ICMP6_DST_UNREACH_NOROUTE},
+ {"adm-prohib6", ICMP6_DST_UNREACH_ADMIN},
+ {"addr-unr6", ICMP6_DST_UNREACH_ADDR},
+ {"port-unr6", ICMP6_DST_UNREACH_NOPORT},
+ {"hop-limex6", ICMP6_TIME_EXCEED_TRANSIT},
+ {"frag-re-timex6", ICMP6_TIME_EXCEED_REASSEMBLY},
+ {"err-head6", ICMP6_PARAMPROB_HEADER},
+ {"unrec-head6", ICMP6_PARAMPROB_NEXTHEADER},
+ {"unreq-opt6", ICMP6_PARAMPROB_OPTION},
+ {NULL, 0},
+};
+
+static sigset_t set, oset;
+
+
+static boolean_t
+add_index(int index)
+{
+ d_list_t *temp = malloc(sizeof (d_list_t));
+
+ if (temp == NULL) {
+ warn("malloc");
+ return (B_TRUE);
+ }
+
+ temp->index = index;
+ temp->next = NULL;
+
+ if (d_tail == NULL) {
+ d_list = d_tail = temp;
+ return (B_FALSE);
+ }
+
+ d_tail->next = temp;
+ d_tail = temp;
+
+ return (B_FALSE);
+}
+
+static int
+block_all_signals()
+{
+ if (sigfillset(&set) == -1) {
+ warn("sigfillset");
+ return (-1);
+ }
+ if (sigprocmask(SIG_SETMASK, &set, &oset) == -1) {
+ warn("sigprocmask");
+ return (-1);
+ }
+ return (0);
+}
+
+static int
+restore_all_signals()
+{
+ if (sigprocmask(SIG_SETMASK, &oset, NULL) == -1) {
+ warn("sigprocmask");
+ return (-1);
+ }
+ return (0);
+}
+
+/* allocate an ips_act_props_t and link it in correctly */
+static ips_act_props_t *
+alloc_iap(ips_conf_t *parent)
+{
+ ips_act_props_t *ret;
+ ips_act_props_t *next = parent->ips_acts;
+ ips_act_props_t *current = NULL;
+
+ ret = (ips_act_props_t *)calloc(sizeof (ips_act_props_t), 1);
+
+ if (ret == NULL)
+ return (NULL);
+
+ ret->iap_head = parent;
+
+ while (next != NULL) {
+ current = next;
+ next = next->iap_next;
+ }
+
+ if (current != NULL)
+ current->iap_next = ret;
+ else
+ parent->ips_acts = ret;
+
+ parent->ips_act_cnt++;
+
+ return (ret);
+}
+
+/*
+ * This function exit()s if it fails.
+ */
+static void
+fetch_algorithms()
+{
+ struct spd_msg msg;
+ struct spd_ext_actions *actp;
+ struct spd_attribute *attr, *endattr;
+ spd_ext_t *exts[SPD_EXT_MAX+1];
+ uint64_t reply_buf[256];
+ int sfd = socket(PF_POLICY, SOCK_RAW, PF_POLICY_V1);
+ int cnt, retval;
+ uint64_t *start, *end;
+ alginfo_t alg = {0, 0, 0, 0, 0};
+ uint_t algtype;
+ static boolean_t has_run = B_FALSE;
+
+ if (has_run)
+ return;
+ else
+ has_run = B_TRUE;
+
+ if (sfd < 0) {
+ err(-1, gettext("unable to open policy socket"));
+ }
+
+ (void) memset(&msg, 0, sizeof (msg));
+ msg.spd_msg_version = PF_POLICY_V1;
+ msg.spd_msg_type = SPD_ALGLIST;
+ msg.spd_msg_len = SPD_8TO64(sizeof (msg));
+
+ cnt = write(sfd, &msg, sizeof (msg));
+ if (cnt != sizeof (msg)) {
+ if (cnt < 0) {
+ err(-1, gettext("alglist failed: write"));
+ } else {
+ errx(-1, gettext("admin failed: short write"));
+ }
+ }
+
+ cnt = read(sfd, reply_buf, sizeof (reply_buf));
+
+ retval = spdsock_get_ext(exts, (spd_msg_t *)reply_buf, SPD_8TO64(cnt),
+ spdsock_diag_buf, SPDSOCK_DIAG_BUF_LEN);
+
+ if (retval == KGE_LEN && exts[0]->spd_ext_len == 0) {
+ /*
+ * No algorithms are defined in the kernel, which caused
+ * the extension length to be zero, and spdsock_get_ext()
+ * to fail with a KGE_LEN error. This is not an error
+ * condition, so we return nicely.
+ */
+ return;
+ } else if (retval != 0) {
+ if (strlen(spdsock_diag_buf) != 0)
+ warnx(spdsock_diag_buf);
+ err(1, gettext("fetch_algorithms failed"));
+ }
+
+ if (!exts[SPD_EXT_ACTION]) {
+ errx(1, gettext("fetch_algorithms: action missing?!"));
+ }
+
+ actp = (struct spd_ext_actions *)exts[SPD_EXT_ACTION];
+ start = (uint64_t *)actp;
+ end = (start + actp->spd_actions_len);
+ endattr = (struct spd_attribute *)end;
+ attr = (struct spd_attribute *)&actp[1];
+
+ algtype = 0;
+
+ while (attr < endattr) {
+ switch (attr->spd_attr_tag) {
+ case SPD_ATTR_NOP:
+ case SPD_ATTR_EMPTY:
+ break;
+ case SPD_ATTR_END:
+ attr = endattr;
+ /* FALLTHRU */
+ case SPD_ATTR_NEXT:
+ known_algs[algtype][ipsec_nalgs[algtype]] = alg;
+ ipsec_nalgs[algtype]++;
+ break;
+
+ case SPD_ATTR_ENCR_MINBITS:
+ case SPD_ATTR_AH_MINBITS:
+ case SPD_ATTR_ESPA_MINBITS:
+ alg.minkeybits = attr->spd_attr_value;
+ break;
+
+ case SPD_ATTR_ENCR_MAXBITS:
+ case SPD_ATTR_AH_MAXBITS:
+ case SPD_ATTR_ESPA_MAXBITS:
+ alg.maxkeybits = attr->spd_attr_value;
+ break;
+
+ case SPD_ATTR_ENCR_DEFBITS:
+ case SPD_ATTR_AH_DEFBITS:
+ case SPD_ATTR_ESPA_DEFBITS:
+ alg.defkeybits = attr->spd_attr_value;
+ break;
+
+ case SPD_ATTR_ENCR_INCRBITS:
+ case SPD_ATTR_AH_INCRBITS:
+ case SPD_ATTR_ESPA_INCRBITS:
+ alg.incr = attr->spd_attr_value;
+ break;
+
+ case SPD_ATTR_AH_AUTH:
+ case SPD_ATTR_ESP_AUTH:
+ case SPD_ATTR_ESP_ENCR:
+ alg.id = attr->spd_attr_value;
+ algtype = attr->spd_attr_tag - SPD_ATTR_AH_AUTH;
+ break;
+ }
+ attr++;
+ }
+
+ (void) close(sfd);
+}
+
+/* data dependant transform (act_cnt) */
+#define ATTR(ap, tag, value) \
+do { (ap)->spd_attr_tag = (tag); \
+ (ap)->spd_attr_value = (value); \
+ ap++; } while (0)
+
+static struct spd_attribute *
+emit_alg(struct spd_attribute *ap, int type, const algreq_t *ar,
+ int algattr, int minbitattr, int maxbitattr)
+{
+ int id = ar->alg_id;
+ int minbits, i;
+
+ if (id != 0) {
+ /* LINTED E_CONST_COND */
+ ATTR(ap, algattr, ar->alg_id);
+
+ minbits = ar->alg_minbits;
+ if (minbits == 0) {
+ for (i = 0; i < ipsec_nalgs[type]; i++) {
+ if (known_algs[type][i].id == id)
+ break;
+ }
+ if (i < ipsec_nalgs[type])
+ minbits = known_algs[type][i].defkeybits;
+ }
+ if (minbits != 0)
+ /* LINTED E_CONST_COND */
+ ATTR(ap, minbitattr, minbits);
+ if (ar->alg_maxbits != SPD_MAX_MAXBITS)
+ /* LINTED E_CONST_COND */
+ ATTR(ap, maxbitattr, ar->alg_maxbits);
+ }
+
+ return (ap);
+}
+
+
+
+static struct spd_attribute *
+ips_act_props_to_action(struct spd_attribute *ap, uint32_t *rule_priorityp,
+ const ips_act_props_t *act_ptr)
+{
+ uint32_t rule_priority = *rule_priorityp;
+
+ /* LINTED E_CONST_COND */
+ ATTR(ap, SPD_ATTR_EMPTY, 0);
+
+ /* type */
+ /* LINTED E_CONST_COND */
+ ATTR(ap, SPD_ATTR_TYPE, act_ptr->iap_action);
+
+ if (act_ptr->iap_action == SPD_ACTTYPE_PASS)
+ rule_priority |= BYPASS_POLICY_BOOST;
+
+ /* flags */
+ if (act_ptr->iap_attr != 0)
+ /* LINTED E_CONST_COND */
+ ATTR(ap, SPD_ATTR_FLAGS, act_ptr->iap_attr);
+
+ /* esp */
+ if (act_ptr->iap_attr & SPD_APPLY_ESP) {
+ rule_priority |= ESP_POLICY_BOOST;
+
+ /* encr */
+ ap = emit_alg(ap, ESP_ENCR, &act_ptr->iap_eencr,
+ SPD_ATTR_ESP_ENCR,
+ SPD_ATTR_ENCR_MINBITS, SPD_ATTR_ENCR_MAXBITS);
+
+ /* auth */
+ ap = emit_alg(ap, ESP_AUTH, &act_ptr->iap_eauth,
+ SPD_ATTR_ESP_AUTH,
+ SPD_ATTR_ESPA_MINBITS, SPD_ATTR_ESPA_MAXBITS);
+ }
+
+ /* ah */
+ if (act_ptr->iap_attr & SPD_APPLY_AH) {
+ rule_priority |= AH_POLICY_BOOST;
+ /* auth */
+ ap = emit_alg(ap, AH_AUTH, &act_ptr->iap_aauth,
+ SPD_ATTR_AH_AUTH,
+ SPD_ATTR_AH_MINBITS, SPD_ATTR_AH_MAXBITS);
+ }
+
+ /* lifetimes */
+ if (act_ptr->iap_life_soft_time != 0)
+ /* LINTED E_CONST_COND */
+ ATTR(ap, SPD_ATTR_LIFE_SOFT_TIME, act_ptr->iap_life_soft_time);
+ if (act_ptr->iap_life_hard_time != 0)
+ /* LINTED E_CONST_COND */
+ ATTR(ap, SPD_ATTR_LIFE_HARD_TIME, act_ptr->iap_life_hard_time);
+ if (act_ptr->iap_life_soft_bytes != 0)
+ /* LINTED E_CONST_COND */
+ ATTR(ap, SPD_ATTR_LIFE_SOFT_BYTES,
+ act_ptr->iap_life_soft_bytes);
+ if (act_ptr->iap_life_hard_bytes != 0)
+ /* LINTED E_CONST_COND */
+ ATTR(ap, SPD_ATTR_LIFE_HARD_BYTES,
+ act_ptr->iap_life_hard_bytes);
+
+ /* LINTED E_CONST_COND */
+ ATTR(ap, SPD_ATTR_NEXT, 0);
+
+ *rule_priorityp = rule_priority;
+
+ return (ap);
+}
+
+static boolean_t
+alg_rangecheck(uint_t type, uint_t algid, const algreq_t *ar)
+{
+ int i;
+ uint_t minbits = ar->alg_minbits;
+ uint_t maxbits = ar->alg_maxbits;
+
+ for (i = 0; i < ipsec_nalgs[type]; i++) {
+ if (known_algs[type][i].id == algid)
+ break;
+ }
+
+ if (i >= ipsec_nalgs[type]) {
+ /*
+ * The kernel (where we populate known_algs from) doesn't
+ * return the id's associated with NONE algorithms so we
+ * test here if this was the reason the algorithm wasn't
+ * found before wrongly failing.
+ */
+ if (((type == ESP_ENCR) && (algid == SADB_EALG_NONE)) ||
+ ((type == ESP_AUTH) && (algid == SADB_AALG_NONE)) ||
+ ((type == AH_AUTH) && (algid == SADB_AALG_NONE))) {
+ return (B_TRUE);
+ } else {
+ return (B_FALSE); /* not found */
+ }
+ }
+
+ if ((minbits == 0) && (maxbits == 0))
+ return (B_TRUE);
+
+ minbits = MAX(minbits, known_algs[type][i].minkeybits);
+ maxbits = MIN(maxbits, known_algs[type][i].maxkeybits);
+
+ /* we could also check key increments here.. */
+ return (minbits <= maxbits); /* non-null intersection */
+}
+
+/*
+ * Inspired by uts/common/inet/spd.c:ipsec_act_wildcard_expand()
+ */
+
+static struct spd_attribute *
+ips_act_wild_props_to_action(struct spd_attribute *ap,
+ uint32_t *rule_priorityp, uint16_t *act_cntp,
+ const ips_act_props_t *act_ptr)
+{
+ ips_act_props_t tact = *act_ptr;
+ boolean_t use_ah, use_esp, use_espa;
+ boolean_t wild_auth, wild_encr, wild_eauth;
+ uint_t auth_alg, auth_idx, auth_min, auth_max;
+ uint_t eauth_alg, eauth_idx, eauth_min, eauth_max;
+ uint_t encr_alg, encr_idx, encr_min, encr_max;
+
+ use_ah = !!(act_ptr->iap_attr & SPD_APPLY_AH);
+ use_esp = !!(act_ptr->iap_attr & SPD_APPLY_ESP);
+ use_espa = !!(act_ptr->iap_attr & SPD_APPLY_ESPA);
+ auth_alg = act_ptr->iap_aauth.alg_id;
+ eauth_alg = act_ptr->iap_eauth.alg_id;
+ encr_alg = act_ptr->iap_eencr.alg_id;
+
+ wild_auth = use_ah && (auth_alg == SADB_AALG_NONE);
+ wild_eauth = use_espa && (eauth_alg == SADB_AALG_NONE);
+ wild_encr = use_esp && (encr_alg == SADB_EALG_NONE);
+
+ auth_min = auth_max = auth_alg;
+ eauth_min = eauth_max = eauth_alg;
+ encr_min = encr_max = encr_alg;
+
+ /*
+ * set up for explosion.. for each dimension, expand output
+ * size by the explosion factor.
+ */
+ if (wild_auth) {
+ auth_min = 0;
+ auth_max = ipsec_nalgs[AH_AUTH] - 1;
+ }
+ if (wild_eauth) {
+ eauth_min = 0;
+ eauth_max = ipsec_nalgs[ESP_AUTH] - 1;
+ }
+ if (wild_encr) {
+ encr_min = 0;
+ encr_max = ipsec_nalgs[ESP_ENCR] - 1;
+ }
+
+#define WHICH_ALG(type, wild, idx) ((wild)?(known_algs[type][idx].id):(idx))
+
+ for (encr_idx = encr_min; encr_idx <= encr_max; encr_idx++) {
+ encr_alg = WHICH_ALG(ESP_ENCR, wild_encr, encr_idx);
+
+ if (use_esp &&
+ !alg_rangecheck(ESP_ENCR, encr_alg, &act_ptr->iap_eencr))
+ continue;
+
+ for (auth_idx = auth_min; auth_idx <= auth_max; auth_idx++) {
+ auth_alg = WHICH_ALG(AH_AUTH, wild_auth, auth_idx);
+
+ if (use_ah &&
+ !alg_rangecheck(AH_AUTH, auth_alg,
+ &act_ptr->iap_aauth))
+ continue;
+
+
+ for (eauth_idx = eauth_min; eauth_idx <= eauth_max;
+ eauth_idx++) {
+ eauth_alg = WHICH_ALG(ESP_AUTH, wild_eauth,
+ eauth_idx);
+
+ if (use_espa &&
+ !alg_rangecheck(ESP_AUTH, eauth_alg,
+ &act_ptr->iap_eauth))
+ continue;
+
+ tact.iap_eencr.alg_id = encr_alg;
+ tact.iap_eauth.alg_id = eauth_alg;
+ tact.iap_aauth.alg_id = auth_alg;
+
+ (*act_cntp)++;
+ ap = ips_act_props_to_action(ap,
+ rule_priorityp, &tact);
+ }
+ }
+ }
+
+#undef WHICH_ALG
+
+ return (ap);
+}
+
+/* huge, but not safe since no length checking is done */
+#define MAX_POL_MSG_LEN 16384
+
+
+/*
+ * hand in some ips_conf_t's, get back an
+ * iovec of pfpol messages.
+ * this function converts the internal ips_conf_t into
+ * a form that pf_pol can use.
+ * return 0 on success, 1 on failure
+ */
+static int
+ips_conf_to_pfpol_msg(int ipsec_cmd, ips_conf_t *inConf, int num_ips,
+ struct iovec *msg)
+{
+ int i;
+ ips_conf_t *conf;
+ uint64_t *scratch = NULL;
+
+ for (i = 0; i < num_ips; i++) {
+ uint16_t *msg_len;
+ uint16_t act_cnt = 0;
+ uint64_t *next = NULL;
+ spd_msg_t *spd_msg;
+ spd_address_t *spd_address;
+ struct spd_rule *spd_rule;
+ struct spd_proto *spd_proto;
+ struct spd_portrange *spd_portrange;
+ struct spd_ext_actions *spd_ext_actions;
+ struct spd_attribute *ap;
+ struct spd_typecode *spd_typecode;
+ ips_act_props_t *act_ptr;
+ uint32_t rule_priority = 0;
+
+ scratch = calloc(1, MAX_POL_MSG_LEN);
+ msg[i].iov_base = (char *)scratch;
+ if (scratch == NULL) {
+ warn(gettext("memory"));
+ return (1);
+ }
+ conf = &(inConf[i]);
+
+ spd_msg = (spd_msg_t *)scratch;
+ next = (uint64_t *)&(spd_msg[1]);
+
+ msg_len = &(spd_msg->spd_msg_len);
+
+ spd_msg->spd_msg_version = PF_POLICY_V1;
+ spd_msg->spd_msg_pid = getpid();
+ spd_msg->spd_msg_seq = ++seq_cnt;
+
+ switch (ipsec_cmd) {
+ case SPD_ADDRULE:
+ spd_msg->spd_msg_type = SPD_ADDRULE;
+ break;
+
+ default:
+ warnx("%s %d", gettext("bad command:"), ipsec_cmd);
+ spd_msg->spd_msg_type = SPD_ADDRULE;
+ break;
+ }
+
+ /*
+ * SELECTOR
+ */
+
+ spd_msg->spd_msg_spdid = SPD_STANDBY;
+
+ /* rule */
+ spd_rule = (struct spd_rule *)next;
+
+ spd_rule->spd_rule_len = SPD_8TO64(sizeof (struct spd_rule));
+ spd_rule->spd_rule_type = SPD_EXT_RULE;
+ spd_rule->spd_rule_flags = conf->ips_dir;
+
+ next = (uint64_t *)&(spd_rule[1]);
+
+ /* proto */
+ if (conf->ips_ulp_prot != 0) {
+ spd_proto = (struct spd_proto *)next;
+ spd_proto->spd_proto_len =
+ SPD_8TO64(sizeof (struct spd_proto));
+ spd_proto->spd_proto_exttype = SPD_EXT_PROTO;
+ spd_proto->spd_proto_number = conf->ips_ulp_prot;
+ next = (uint64_t *)&(spd_proto[1]);
+ }
+
+ /* icmp type/code */
+ if (conf->ips_ulp_prot == IPPROTO_ICMP ||
+ conf->ips_ulp_prot == IPPROTO_ICMPV6) {
+ if (conf->has_type) {
+ spd_typecode = (struct spd_typecode *)next;
+ spd_typecode->spd_typecode_len =
+ SPD_8TO64(sizeof (struct spd_typecode));
+ spd_typecode->spd_typecode_exttype =
+ SPD_EXT_ICMP_TYPECODE;
+ spd_typecode->spd_typecode_type =
+ conf->ips_icmp_type;
+ spd_typecode->spd_typecode_type_end =
+ conf->ips_icmp_type_end;
+ if (conf->has_code) {
+ spd_typecode->spd_typecode_code =
+ conf->ips_icmp_code;
+ spd_typecode->spd_typecode_code_end =
+ conf->ips_icmp_code_end;
+ } else {
+ spd_typecode->spd_typecode_code = 255;
+ spd_typecode->spd_typecode_code_end
+ = 255;
+ }
+ next = (uint64_t *)&(spd_typecode[1]);
+ }
+ }
+
+ /* src port */
+ if (conf->ips_src_port_min != 0 ||
+ conf->ips_src_port_max != 0) {
+ spd_portrange = (struct spd_portrange *)next;
+ spd_portrange->spd_ports_len =
+ SPD_8TO64(sizeof (struct spd_portrange));
+ spd_portrange->spd_ports_exttype =
+ (conf->swap)?SPD_EXT_REMPORT:SPD_EXT_LCLPORT;
+ spd_portrange->spd_ports_minport =
+ conf->ips_src_port_min;
+ spd_portrange->spd_ports_maxport =
+ conf->ips_src_port_max;
+ next = (uint64_t *)&(spd_portrange[1]);
+ }
+ /* dst port */
+ if (conf->ips_dst_port_min != 0 ||
+ conf->ips_dst_port_max != 0) {
+ spd_portrange = (struct spd_portrange *)next;
+ spd_portrange->spd_ports_len =
+ SPD_8TO64(sizeof (struct spd_portrange));
+ spd_portrange->spd_ports_exttype =
+ (conf->swap)?SPD_EXT_LCLPORT:SPD_EXT_REMPORT;
+ spd_portrange->spd_ports_minport =
+ conf->ips_dst_port_min;
+ spd_portrange->spd_ports_maxport =
+ conf->ips_dst_port_max;
+ next = (uint64_t *)&(spd_portrange[1]);
+ }
+
+ /* saddr */
+ if (conf->has_saddr) {
+ spd_address = (spd_address_t *)next;
+ next = (uint64_t *)(spd_address + 1);
+
+ spd_address->spd_address_exttype =
+ (conf->swap)?SPD_EXT_REMADDR:SPD_EXT_LCLADDR;
+ spd_address->spd_address_prefixlen =
+ conf->ips_src_mask_len;
+
+ if (conf->ips_isv4) {
+ spd_address->spd_address_af = AF_INET;
+ (void) memcpy(next, &(conf->ips_src_addr),
+ sizeof (ipaddr_t));
+ spd_address->spd_address_len = 2;
+ next += SPD_8TO64(sizeof (ipaddr_t) + 4);
+ if (!conf->has_smask)
+ spd_address->spd_address_prefixlen = 32;
+ } else {
+ spd_address->spd_address_af = AF_INET6;
+ (void) memcpy(next, &(conf->ips_src_addr_v6),
+ sizeof (in6_addr_t));
+ spd_address->spd_address_len = 3;
+ next += SPD_8TO64(sizeof (in6_addr_t));
+ if (!conf->has_smask)
+ spd_address->spd_address_prefixlen
+ = 128;
+ }
+ }
+
+ /* daddr */
+ if (conf->has_daddr) {
+ spd_address = (spd_address_t *)next;
+
+ next = (uint64_t *)(spd_address + 1);
+
+ spd_address->spd_address_exttype =
+ (conf->swap)?SPD_EXT_LCLADDR:SPD_EXT_REMADDR;
+ spd_address->spd_address_prefixlen =
+ conf->ips_dst_mask_len;
+
+ if (conf->ips_isv4) {
+ spd_address->spd_address_af = AF_INET;
+ (void) memcpy(next, &conf->ips_dst_addr,
+ sizeof (ipaddr_t));
+ spd_address->spd_address_len = 2;
+ /* "+ 4" below is for padding. */
+ next += SPD_8TO64(sizeof (ipaddr_t) + 4);
+ if (!conf->has_dmask)
+ spd_address->spd_address_prefixlen = 32;
+ } else {
+ spd_address->spd_address_af = AF_INET6;
+ (void) memcpy(next, &(conf->ips_dst_addr_v6),
+ sizeof (in6_addr_t));
+ spd_address->spd_address_len = 3;
+ next += SPD_8TO64(sizeof (in6_addr_t));
+ if (!conf->has_dmask)
+ spd_address->spd_address_prefixlen
+ = 128;
+ }
+ }
+
+ /* actions */
+ spd_ext_actions = (struct spd_ext_actions *)next;
+
+ spd_ext_actions->spd_actions_exttype = SPD_EXT_ACTION;
+
+ act_ptr = conf->ips_acts;
+ ap = (struct spd_attribute *)(&spd_ext_actions[1]);
+
+ rule_priority = priority--;
+
+ for (act_ptr = conf->ips_acts; act_ptr != NULL;
+ act_ptr = act_ptr->iap_next) {
+ ap = ips_act_wild_props_to_action(ap, &rule_priority,
+ &act_cnt, act_ptr);
+ }
+ ap[-1].spd_attr_tag = SPD_ATTR_END;
+
+ next = (uint64_t *)ap;
+
+ spd_rule->spd_rule_priority = rule_priority;
+
+ msg[i].iov_len = (uintptr_t)next - (uintptr_t)msg[i].iov_base;
+ *msg_len = (uint16_t)SPD_8TO64(msg[i].iov_len);
+ spd_ext_actions->spd_actions_count = act_cnt;
+ spd_ext_actions->spd_actions_len =
+ SPD_8TO64((uintptr_t)next - (uintptr_t)spd_ext_actions);
+#ifdef DEBUG_HEAVY
+ printf("pfpol msg len in uint64_t's = %d\n", *msg_len);
+ printf("pfpol test_len in bytes = %d\n", msg[i].iov_len);
+ pfpol_msg_dump((spd_msg_t *)scratch,
+ "ips_conf_to_pfpol_msg");
+#endif
+ }
+
+#undef ATTR
+ return (0);
+}
+
+static int
+get_pf_pol_socket(void)
+{
+ int s = socket(PF_POLICY, SOCK_RAW, PF_POLICY_V1);
+ if (s < 0) {
+ warn(gettext("(loading pf_policy) socket:"));
+ }
+
+ return (s);
+}
+
+
+static int
+send_pf_pol_message(int ipsec_cmd, ips_conf_t *conf)
+{
+ int retval;
+ int cnt;
+ int total_len;
+ struct iovec polmsg;
+ spd_msg_t *return_buf;
+ spd_ext_t *exts[SPD_EXT_MAX+1];
+ int fd = get_pf_pol_socket();
+
+ if (fd < 0)
+ return (1);
+
+ retval = ips_conf_to_pfpol_msg(ipsec_cmd, conf, 1, &polmsg);
+
+ if (retval) {
+ (void) close(fd);
+ return (1);
+ }
+
+ total_len = polmsg.iov_len;
+
+ cnt = writev(fd, &polmsg, 1);
+
+#ifdef DEBUG_HEAVY
+ (void) printf("cnt = %d\n", cnt);
+#endif
+ if (cnt < 0) {
+ warn(gettext("pf_pol write"));
+ } else {
+ return_buf = (spd_msg_t *)calloc(total_len, 1);
+
+ if (return_buf == NULL) {
+ warn(gettext("memory"));
+ } else {
+ cnt = read(fd, (void*)return_buf, total_len);
+#ifdef DEBUG_HEAVY
+ (void) printf("pf_pol read: cnt = %d(%d)\n", cnt,
+ total_len);
+#endif
+
+ if (cnt > 8 && return_buf->spd_msg_errno) {
+ int diag = return_buf->spd_msg_diagnostic;
+ warnx("%s: %s", gettext("spd_msg return"),
+ strerror(return_buf->spd_msg_errno));
+ if (diag != 0)
+ (void) printf("%s\n",
+ spdsock_diag(diag));
+#ifdef DEBUG_HEAVY
+ pfpol_msg_dump((spd_msg_t *)polmsg.iov_base,
+ "message in");
+ pfpol_msg_dump(return_buf,
+ "send_pf_pol_message");
+#endif
+ free(return_buf);
+ free(polmsg.iov_base);
+ (void) close(fd);
+ return (1);
+ }
+
+ retval = spdsock_get_ext(exts, return_buf,
+ return_buf->spd_msg_len, NULL, 0);
+ /* ignore retval */
+
+ if (exts[SPD_EXT_RULE]) {
+ conf->ips_policy_index =
+ ((struct spd_rule *)
+ exts[SPD_EXT_RULE])->spd_rule_index;
+
+ if (add_index(conf->ips_policy_index)) {
+ free(return_buf);
+ free(polmsg.iov_base);
+ (void) close(fd);
+ return (1);
+ }
+ }
+
+ free(return_buf);
+ }
+ }
+
+ free(polmsg.iov_base);
+ (void) close(fd);
+
+ return (0);
+
+}
+
+int
+main(int argc, char *argv[])
+{
+ int ret, flushret;
+ int c;
+ int index;
+ int lfd;
+
+ (void) setlocale(LC_ALL, "");
+#if !defined(TEXT_DOMAIN)
+#define TEXT_DOMAIN "SYS_TEST"
+#endif
+ (void) textdomain(TEXT_DOMAIN);
+
+ openlog("ipsecconf", LOG_CONS, LOG_AUTH);
+
+ /*
+ * We don't immediately check for privilege here. This is done by IP
+ * when we open /dev/ip below.
+ */
+
+ if (argc == 1) {
+ cmd = IPSEC_CONF_VIEW;
+ goto done;
+ }
+ while ((c = getopt(argc, argv, "nlfa:qd:r:")) != EOF) {
+ switch (c) {
+ case 'f':
+ /* Only one command at a time */
+ if (cmd != 0) {
+ usage();
+ exit(1);
+ }
+ cmd = IPSEC_CONF_FLUSH;
+ break;
+ case 'l':
+ /* Only one command at a time */
+ if (cmd != 0) {
+ usage();
+ exit(1);
+ }
+ cmd = IPSEC_CONF_LIST;
+ break;
+ case 'a':
+ /* Only one command at a time */
+ if (cmd != 0) {
+ usage();
+ exit(1);
+ }
+ cmd = IPSEC_CONF_ADD;
+ filename = optarg;
+ break;
+ case 'd':
+ /* Only one command at a time */
+ if (cmd != 0) {
+ usage();
+ exit(1);
+ }
+ cmd = IPSEC_CONF_DEL;
+ index = parse_int(optarg);
+ break;
+ case 'n' :
+ ipsecconf_nflag++;
+ break;
+ case 'q' :
+ ipsecconf_qflag++;
+ break;
+ case 'r' :
+ /* only one command at a time */
+ if (cmd != 0) {
+ usage();
+ exit(1);
+ }
+ cmd = IPSEC_CONF_SUB;
+ filename = optarg;
+ break;
+ default :
+ usage();
+ exit(1);
+ }
+ }
+
+done:
+ ret = 0;
+ lfd = lock();
+ if (lfd == -1) {
+ exit(1);
+ }
+
+ /*
+ * ADD, FLUSH, DELETE needs to do two operations.
+ *
+ * 1) Update/delete/empty the POLICY_CONF_FILE.
+ * 2) Make an ioctl and tell IP to update its state.
+ *
+ * We already lock()ed so that only one instance of this
+ * program runs. We also need to make sure that the above
+ * operations are atomic i.e we don't want to update the file
+ * and get interrupted before we could tell IP. To make it
+ * atomic we block all the signals and restore them.
+ */
+ switch (cmd) {
+ case IPSEC_CONF_LIST:
+ fetch_algorithms();
+ ret = ipsec_conf_list();
+ break;
+ case IPSEC_CONF_FLUSH:
+ if ((ret = block_all_signals()) == -1) {
+ break;
+ }
+ ret = ipsec_conf_flush(SPD_ACTIVE);
+ (void) restore_all_signals();
+ break;
+ case IPSEC_CONF_VIEW:
+ ret = ipsec_conf_view();
+ break;
+ case IPSEC_CONF_DEL:
+ if (index == -1) {
+ warnx(gettext("Invalid index"));
+ ret = -1;
+ break;
+ }
+ if ((ret = block_all_signals()) == -1) {
+ break;
+ }
+ ret = ipsec_conf_del(index, B_FALSE);
+ (void) restore_all_signals();
+ break;
+ case IPSEC_CONF_ADD:
+ fetch_algorithms();
+ if ((ret = block_all_signals()) == -1) {
+ break;
+ }
+ ret = ipsec_conf_add();
+ (void) restore_all_signals();
+ break;
+ case IPSEC_CONF_SUB:
+ fetch_algorithms();
+ if ((ret = block_all_signals()) == -1) {
+ break;
+ }
+ ret = ipsec_conf_sub();
+ (void) restore_all_signals();
+ break;
+ default :
+ /* If no argument is given but a "-" */
+ usage();
+ exit(1);
+ }
+
+ /* flush standby db */
+ flushret = (cmd != IPSEC_CONF_VIEW) ?
+ ipsec_conf_flush(SPD_STANDBY) : 0;
+
+ (void) unlock(lfd);
+ if (ret != 0 || flushret != 0)
+ ret = 1;
+ return (ret);
+}
+
+static int
+perm_check(void)
+{
+ if (errno != EACCES)
+ warn(gettext("Cannot open lock file %s"), LOCK_FILE);
+ else
+ warnx(gettext("You must be root to run ipsecconf."));
+ return (-1);
+}
+
+static int
+lock()
+{
+ int fd;
+ struct stat sbuf1;
+ struct stat sbuf2;
+
+ /*
+ * Open the file with O_CREAT|O_EXCL. If it exists already, it
+ * will fail. If it already exists, check whether it looks like
+ * the one we created.
+ */
+ (void) umask(0077);
+ if ((fd = open(LOCK_FILE, O_EXCL|O_CREAT|O_RDWR, S_IRUSR|S_IWUSR))
+ == -1) {
+ if (errno != EEXIST) {
+ /* Some other problem. */
+ return (perm_check());
+ }
+
+ /*
+ * open() returned an EEXIST error. We don't fail yet
+ * as it could be a residual from a previous
+ * execution. However, we need to clear errno here.
+ * If we don't and print_cmd_buf() is later invoked
+ * as the result of a parsing error, it
+ * will assume that the current error is EEXIST and
+ * that a corresponding error message has already been
+ * printed, which results in an incomplete error
+ * message. If errno is zero, print_cmd_buf() will
+ * assume that it is called as a result of a
+ * parsing error and will print the appropriate
+ * error message.
+ */
+ errno = 0;
+
+ /*
+ * File exists. make sure it is OK. We need to lstat()
+ * as fstat() stats the file pointed to by the symbolic
+ * link.
+ */
+ if (lstat(LOCK_FILE, &sbuf1) == -1) {
+ warn(gettext("Cannot lstat lock file %s"), LOCK_FILE);
+ return (-1);
+ }
+ /*
+ * Check whether it is a regular file and not a symbolic
+ * link. Its link count should be 1. The owner should be
+ * root and the file should be empty.
+ */
+ if (((sbuf1.st_mode & (S_IFREG|S_IFLNK)) != S_IFREG) ||
+ sbuf1.st_nlink != 1 ||
+ sbuf1.st_uid != 0 ||
+ sbuf1.st_size != 0) {
+ warnx(gettext("Bad lock file %s"), LOCK_FILE);
+ return (-1);
+ }
+ if ((fd = open(LOCK_FILE, O_CREAT|O_RDWR,
+ S_IRUSR|S_IWUSR)) == -1) {
+ return (perm_check());
+ }
+ /*
+ * Check whether we opened the file that we lstat()ed.
+ */
+ if (fstat(fd, &sbuf2) == -1) {
+ warn(gettext("Cannot fstat lock file %s"), LOCK_FILE);
+ return (-1);
+ }
+ if (sbuf1.st_dev != sbuf2.st_dev ||
+ sbuf1.st_ino != sbuf2.st_ino) {
+ /* File changed after we did the lstat() above */
+ warnx(gettext("Bad lock file %s"), LOCK_FILE);
+ return (-1);
+ }
+ }
+ if (lockf(fd, F_LOCK, 0) == -1) {
+ warn("lockf");
+ return (-1);
+ }
+ return (fd);
+}
+
+static int
+unlock(int fd)
+{
+ if (lockf(fd, F_ULOCK, 0) == -1) {
+ warn("lockf");
+ return (-1);
+ }
+ return (0);
+}
+
+/* send in TOK_* */
+static void
+print_pattern_string(int type)
+{
+ int j;
+
+ for (j = 0; pattern_table[j].string != NULL; j++) {
+ if (type == pattern_table[j].tok_val) {
+ (void) printf("%s ", pattern_table[j].string);
+ return;
+ }
+ }
+}
+
+static void
+print_icmp_typecode(uint8_t type, uint8_t type_end, uint8_t code,
+ uint8_t code_end)
+{
+ (void) printf("type %d", type);
+ if (type_end != type)
+ (void) printf("-%d ", type_end);
+ else
+ (void) printf(" ");
+ if (code != 255) {
+ (void) printf("code %d", code);
+ if (code_end != code)
+ (void) printf("-%d ", code_end);
+ else
+ (void) printf(" ");
+ }
+}
+
+
+static void
+print_spd_flags(uint32_t flags)
+{
+ flags &= (SPD_RULE_FLAG_INBOUND|SPD_RULE_FLAG_OUTBOUND);
+
+ if (flags == SPD_RULE_FLAG_OUTBOUND)
+ (void) printf("dir out ");
+ else if (flags == SPD_RULE_FLAG_INBOUND)
+ (void) printf("dir in ");
+ else if (flags == (SPD_RULE_FLAG_INBOUND|SPD_RULE_FLAG_OUTBOUND))
+ (void) printf("dir both ");
+}
+
+static void
+print_bit_range(int min, int max)
+{
+ if (min != 0 || (max != 0 && max != SPD_MAX_MAXBITS)) {
+ (void) printf("(");
+ if (min != 0)
+ (void) printf("%d", min);
+ if (min != 0 && max != 0 && min != max) {
+ (void) printf("..");
+ if (max != 0 && max != SPD_MAX_MAXBITS)
+ (void) printf("%d", max);
+ }
+ (void) printf(")");
+ }
+}
+
+static void
+print_alg(const char *tag, algreq_t *algreq, int proto_num)
+{
+ int min = algreq->alg_minbits;
+ int max = algreq->alg_maxbits;
+ struct ipsecalgent *alg;
+
+ /*
+ * This function won't be called with alg_id == 0, so we don't
+ * have to worry about ANY vs. NONE here.
+ */
+
+ (void) printf("%s ", tag);
+
+ alg = getipsecalgbynum(algreq->alg_id, proto_num, NULL);
+ if (alg == NULL) {
+ (void) printf("%d", algreq->alg_id);
+ } else {
+ (void) printf("%s", alg->a_names[0]);
+ freeipsecalgent(alg);
+ }
+
+ print_bit_range(min, max);
+ (void) printf(" ");
+}
+
+static void
+print_ulp(uint8_t proto)
+{
+ struct protoent *pe;
+
+ if (proto == 0)
+ return;
+
+ print_pattern_string(TOK_ulp);
+ pe = NULL;
+ if (!ipsecconf_nflag) {
+ pe = getprotobynumber(proto);
+ }
+ if (pe != NULL)
+ (void) printf("%s ", pe->p_name);
+ else
+ (void) printf("%d ", proto);
+}
+
+/* needs to do ranges */
+static void
+print_port(uint16_t in_port, int type)
+{
+ in_port_t port = ntohs(in_port);
+ struct servent *sp;
+
+ if (port == 0)
+ return;
+
+ print_pattern_string(type);
+ sp = NULL;
+ if (!ipsecconf_nflag)
+ sp = getservbyport(port, NULL);
+
+ if (sp != NULL)
+ (void) printf("%s ", sp->s_name);
+ else
+ (void) printf("%d ", port);
+}
+
+#if 0
+/*
+ * Print the mask (source or destination depending on the specified type)
+ * defined in the policy pointed to by cptr.
+ * We follow ifconfig's lead, i.e. we use the decimal dot notation for IPv4
+ * masks and the /N prefix length form for IPv6.
+ */
+static void
+print_mask(ips_conf_t *cptr, int type)
+{
+ struct in_addr addr;
+ struct in6_addr addr6;
+ struct in_addr mask;
+ char buf[INET6_ADDRSTRLEN];
+ boolean_t isv4;
+ struct in6_addr *in_addr_ptr;
+ struct in6_addr *in_mask_ptr;
+
+ if (type == IPS_SRC_MASK) {
+ in_addr_ptr = &cptr->ips_src_addr_v6;
+ in_mask_ptr = &cptr->ips_src_mask_v6;
+ } else {
+ in_addr_ptr = &cptr->ips_dst_addr_v6;
+ in_mask_ptr = &cptr->ips_dst_mask_v6;
+ }
+
+ isv4 = cptr->ips_isv4;
+
+ /*
+ * If the address is INADDR_ANY, don't print the mask.
+ */
+ if (isv4) {
+ IN6_V4MAPPED_TO_INADDR(in_addr_ptr, &addr);
+ if (addr.s_addr == INADDR_ANY)
+ return;
+ } else {
+ addr6 = *in_addr_ptr;
+ if (IN6_IS_ADDR_UNSPECIFIED(&addr6))
+ return;
+ }
+
+ if (isv4) {
+ (void) printf(" ");
+ print_pattern_string(type);
+ IN6_V4MAPPED_TO_INADDR(in_mask_ptr, &mask);
+ (void) printf("%s ",
+ inet_ntop(AF_INET, (uchar_t *)&mask.s_addr, buf,
+ INET6_ADDRSTRLEN));
+ } else {
+ (void) printf("/%d ",
+ in_masktoprefix((uint8_t *)&in_mask_ptr->s6_addr, B_FALSE));
+ }
+}
+
+/*
+ * Print the address and mask.
+ */
+static void
+print_address(ips_conf_t *cptr, int type)
+{
+ char *cp;
+ struct hostent *hp;
+ char domain[MAXHOSTNAMELEN + 1];
+ struct in_addr addr;
+ struct in6_addr addr6;
+ char abuf[INET6_ADDRSTRLEN];
+ int error_num;
+ struct in6_addr *in_addr_ptr;
+ uchar_t *addr_ptr;
+ sa_family_t af;
+ int addr_len;
+
+ if (type == SPD_EXT_LCLADDR)
+ in_addr_ptr = &cptr->ips_src_addr_v6;
+ else
+ in_addr_ptr = &cptr->ips_dst_addr_v6;
+
+ if (cptr->ips_isv4) {
+ af = AF_INET;
+ /* we don't print unspecified addresses */
+ IN6_V4MAPPED_TO_INADDR(in_addr_ptr, &addr);
+ if (addr.s_addr == INADDR_ANY)
+ return;
+ addr_ptr = (uchar_t *)&addr.s_addr;
+ addr_len = IPV4_ADDR_LEN;
+ } else {
+ af = AF_INET6;
+ addr6 = *in_addr_ptr;
+ /* we don't print unspecified addresses */
+ if (IN6_IS_ADDR_UNSPECIFIED(&addr6))
+ return;
+ addr_ptr = (uchar_t *)&addr6.s6_addr;
+ addr_len = sizeof (struct in6_addr);
+ }
+
+ print_pattern_string(type);
+
+ if (!ipsecconf_nflag) {
+ if (sysinfo(SI_HOSTNAME, domain, MAXHOSTNAMELEN) != -1 &&
+ (cp = strchr(domain, '.')) != NULL) {
+ (void) strcpy(domain, cp + 1);
+ } else {
+ domain[0] = 0;
+ }
+ hp = getipnodebyaddr(addr_ptr, addr_len, af, &error_num);
+ if (hp == NULL)
+ cp = NULL;
+ else {
+ if ((cp = strchr(hp->h_name, '.')) != 0 &&
+ strcasecmp(cp + 1, domain) == 0)
+ *cp = 0;
+ cp = hp->h_name;
+ }
+ }
+
+ if (cp) {
+ (void) printf("%s", cp);
+ } else {
+ (void) printf("%s", inet_ntop(af, addr_ptr, abuf,
+ INET6_ADDRSTRLEN));
+ }
+}
+#endif
+
+/*
+ * Print the address and mask.
+ */
+static void
+print_address2(void *input, int isv4)
+{
+ char *cp;
+ struct hostent *hp;
+ char domain[MAXHOSTNAMELEN + 1];
+ struct in_addr addr;
+ struct in6_addr addr6;
+ char abuf[INET6_ADDRSTRLEN];
+ int error_num;
+ struct in6_addr in_addr;
+ uchar_t *addr_ptr;
+ sa_family_t af;
+ int addr_len;
+
+
+ if (isv4) {
+ af = AF_INET;
+ (void) memcpy(&V4_PART_OF_V6(in_addr), input, 4);
+ /* we don't print unspecified addresses */
+ IN6_V4MAPPED_TO_INADDR(&in_addr, &addr);
+ if (addr.s_addr == INADDR_ANY)
+ return;
+ addr_ptr = (uchar_t *)&addr.s_addr;
+ addr_len = IPV4_ADDR_LEN;
+ } else {
+ (void) memcpy(&addr6, input, 16);
+ af = AF_INET6;
+ /* we don't print unspecified addresses */
+ if (IN6_IS_ADDR_UNSPECIFIED(&addr6))
+ return;
+ addr_ptr = (uchar_t *)&addr6.s6_addr;
+ addr_len = sizeof (struct in6_addr);
+ }
+
+ cp = NULL;
+ if (!ipsecconf_nflag) {
+ if (sysinfo(SI_HOSTNAME, domain, MAXHOSTNAMELEN) != -1 &&
+ (cp = strchr(domain, '.')) != NULL) {
+ (void) strcpy(domain, cp + 1);
+ } else {
+ domain[0] = 0;
+ }
+ hp = getipnodebyaddr(addr_ptr, addr_len, af, &error_num);
+ if (hp) {
+ if ((cp = strchr(hp->h_name, '.')) != 0 &&
+ strcasecmp(cp + 1, domain) == 0)
+ *cp = 0;
+ cp = hp->h_name;
+ }
+ }
+
+ if (cp) {
+ (void) printf("%s", cp);
+ } else {
+ (void) printf("%s", inet_ntop(af, addr_ptr, abuf,
+ INET6_ADDRSTRLEN));
+ }
+}
+
+/*
+ * Get the next SPD_DUMP message from the PF_POLICY socket. A single
+ * read may contain multiple messages. This function uses static buffers,
+ * and is therefore non-reentrant, so if you lift it for an MT application,
+ * be careful.
+ *
+ * Return NULL if there's an error.
+ */
+static spd_msg_t *
+ipsec_read_dump(int pfd)
+{
+ static uint64_t buf[SADB_8TO64(CBUF_LEN)];
+ static uint64_t *offset;
+ static int len; /* In uint64_t units. */
+ spd_msg_t *retval;
+
+ /* Assume offset and len are initialized to NULL and 0. */
+
+ if ((offset - len == buf) || (offset == NULL)) {
+ /* read a new block from the socket. */
+ len = read(pfd, &buf, sizeof (buf));
+ if (len == -1) {
+ warn(gettext("rule dump: bad read"));
+ return (NULL);
+ }
+ offset = buf;
+ len = SADB_8TO64(len);
+ } /* Else I still have more messages from a previous read. */
+
+ retval = (spd_msg_t *)offset;
+ offset += retval->spd_msg_len;
+ if (offset > buf + len) {
+ warnx(gettext("dump read: message corruption,"
+ " %d len exceeds %d boundary."),
+ SADB_64TO8((uintptr_t)(offset - buf)),
+ SADB_64TO8((uintptr_t)(buf + len)));
+ return (NULL);
+ }
+
+ return (retval);
+}
+
+/*
+ * returns 0 on success
+ * -1 on read error
+ * >0 on invalid returned message
+ */
+
+static int
+ipsec_conf_list(void)
+{
+ int ret;
+ int pfd;
+ struct spd_msg msg;
+ int cnt;
+ spd_msg_t *rmsg;
+ spd_ext_t *exts[SPD_EXT_MAX+1];
+
+ pfd = get_pf_pol_socket();
+
+ if (pfd == -1) {
+ warnx(gettext("Error getting list of policies from kernel"));
+ return (-1);
+ }
+
+ (void) memset(&msg, 0, sizeof (msg));
+ msg.spd_msg_version = PF_POLICY_V1;
+ msg.spd_msg_type = SPD_DUMP;
+ msg.spd_msg_len = SPD_8TO64(sizeof (msg));
+
+ cnt = write(pfd, &msg, sizeof (msg));
+
+ if (cnt < 0) {
+ warn(gettext("dump: invalid write() return"));
+ (void) close(pfd);
+ return (-1);
+ }
+
+ rmsg = ipsec_read_dump(pfd);
+
+ if (rmsg == NULL || rmsg->spd_msg_errno != 0) {
+ warnx("%s: %s", gettext("ruleset dump failed"),
+ (rmsg == NULL ?
+ gettext("read error") : strerror(rmsg->spd_msg_errno)));
+ (void) close(pfd);
+ return (-1);
+ }
+
+
+ for (;;) {
+ /* read rule */
+ rmsg = ipsec_read_dump(pfd);
+
+ if (rmsg == NULL) {
+ (void) close(pfd);
+ return (-1);
+ }
+
+ if (rmsg->spd_msg_errno != 0) {
+ warnx("%s: %s", gettext("dump read: bad message"),
+ strerror(rmsg->spd_msg_errno));
+ (void) close(pfd);
+ return (-1);
+ }
+
+ ret = spdsock_get_ext(exts, rmsg, rmsg->spd_msg_len,
+ spdsock_diag_buf, SPDSOCK_DIAG_BUF_LEN);
+ if (ret != 0) {
+ if (strlen(spdsock_diag_buf) != 0)
+ warnx(spdsock_diag_buf);
+ warnx("%s: %s", gettext("dump read: bad message"),
+ strerror(rmsg->spd_msg_errno));
+ (void) close(pfd);
+ return (ret);
+ }
+
+ /*
+ * End of dump..
+ */
+ if (exts[SPD_EXT_RULESET] != NULL)
+ break; /* and return 0. */
+
+ print_pfpol_msg(rmsg);
+ }
+
+ (void) close(pfd);
+ return (0);
+}
+
+static void
+print_iap(ips_act_props_t *iap)
+{
+
+ /* action */
+ switch (iap->iap_action) {
+ case SPD_ACTTYPE_PASS:
+ (void) printf("pass ");
+ break;
+ case SPD_ACTTYPE_DROP:
+ (void) printf("drop ");
+ break;
+ case SPD_ACTTYPE_IPSEC:
+ (void) printf("ipsec ");
+ break;
+ }
+
+ /* properties */
+ (void) printf("%c ", CURL_BEGIN);
+ if (iap->iap_action == SPD_ACTTYPE_IPSEC) {
+ if (iap->iap_attr & SPD_APPLY_AH &&
+ iap->iap_aauth.alg_id != 0)
+ print_alg("auth_algs", &iap->iap_aauth,
+ IPSEC_PROTO_AH);
+
+ if (iap->iap_attr & SPD_APPLY_ESP) {
+ print_alg("encr_algs", &iap->iap_eencr,
+ IPSEC_PROTO_ESP);
+ if (iap->iap_eauth.alg_id != 0)
+ print_alg("encr_auth_algs", &iap->iap_eauth,
+ IPSEC_PROTO_AH);
+ }
+ if (iap->iap_attr & SPD_APPLY_UNIQUE)
+ (void) printf("sa unique ");
+ else
+ (void) printf("sa shared ");
+ }
+ (void) printf("%c ", CURL_END);
+}
+
+
+static void
+print_pfpol_msg(spd_msg_t *msg)
+{
+ spd_ext_t *exts[SPD_EXT_MAX+1];
+ spd_address_t *spd_address;
+ struct spd_rule *spd_rule;
+ struct spd_proto *spd_proto;
+ struct spd_portrange *spd_portrange;
+ struct spd_ext_actions *spd_ext_actions;
+ struct spd_typecode *spd_typecode;
+ struct spd_attribute *app;
+ uint32_t rv;
+ uint16_t act_count;
+
+ rv = spdsock_get_ext(exts, msg, msg->spd_msg_len, spdsock_diag_buf,
+ SPDSOCK_DIAG_BUF_LEN);
+
+ if (rv == KGE_OK && exts[SPD_EXT_RULE] != NULL) {
+ spd_rule = (struct spd_rule *)exts[SPD_EXT_RULE];
+ (void) printf("%s %lld\n", INDEX_TAG, spd_rule->spd_rule_index);
+ } else {
+ if (strlen(spdsock_diag_buf) != 0)
+ warnx(spdsock_diag_buf);
+ warnx(gettext("print_pfpol_msg: malformed PF_POLICY message."));
+ return;
+ }
+
+ (void) printf("%c ", CURL_BEGIN);
+
+ if (exts[SPD_EXT_PROTO] != NULL) {
+ spd_proto = (struct spd_proto *)exts[SPD_EXT_PROTO];
+ print_ulp(spd_proto->spd_proto_number);
+ }
+
+ if (exts[SPD_EXT_LCLADDR] != NULL) {
+ spd_address = (spd_address_t *)exts[SPD_EXT_LCLADDR];
+
+ (void) printf("laddr ");
+ if (spd_address->spd_address_len == 2)
+ print_address2((spd_address+1), 1);
+ else
+ print_address2((spd_address+1), 0);
+
+ (void) printf("/%d ", spd_address->spd_address_prefixlen);
+ }
+
+ if (exts[SPD_EXT_LCLPORT] != NULL) {
+ spd_portrange = (struct spd_portrange *)exts[SPD_EXT_LCLPORT];
+ if (spd_portrange->spd_ports_minport != 0) {
+ print_port(spd_portrange->spd_ports_minport,
+ TOK_lport);
+ }
+ }
+
+
+ if (exts[SPD_EXT_REMADDR] != NULL) {
+ spd_address = (spd_address_t *)exts[SPD_EXT_REMADDR];
+
+ (void) printf("raddr ");
+ if (spd_address->spd_address_len == 2)
+ print_address2((spd_address+1), 1);
+ else
+ print_address2((spd_address+1), 0);
+
+ (void) printf("/%d ", spd_address->spd_address_prefixlen);
+ }
+
+ if (exts[SPD_EXT_REMPORT] != NULL) {
+ spd_portrange =
+ (struct spd_portrange *)exts[SPD_EXT_REMPORT];
+ if (spd_portrange->spd_ports_minport != 0) {
+ print_port(
+ spd_portrange->spd_ports_minport,
+ TOK_rport);
+ }
+ }
+
+ if (exts[SPD_EXT_ICMP_TYPECODE] != NULL) {
+ spd_typecode =
+ (struct spd_typecode *)exts[SPD_EXT_ICMP_TYPECODE];
+ print_icmp_typecode(spd_typecode->spd_typecode_type,
+ spd_typecode->spd_typecode_type_end,
+ spd_typecode->spd_typecode_code,
+ spd_typecode->spd_typecode_code_end);
+ }
+
+ if (exts[SPD_EXT_RULE] != NULL) {
+ spd_rule = (struct spd_rule *)exts[SPD_EXT_RULE];
+ print_spd_flags(spd_rule->spd_rule_flags);
+ }
+
+
+ (void) printf("%c ", CURL_END);
+
+ if (exts[SPD_EXT_ACTION] != NULL) {
+ ips_act_props_t iap;
+ int or_needed = 0;
+
+ (void) memset(&iap, 0, sizeof (iap));
+ spd_ext_actions =
+ (struct spd_ext_actions *)exts[SPD_EXT_ACTION];
+ app = (struct spd_attribute *)(spd_ext_actions + 1);
+
+ for (act_count = 0;
+ act_count < spd_ext_actions->spd_actions_len -1;
+ act_count++) {
+
+ switch (app->spd_attr_tag) {
+
+ case SPD_ATTR_NOP:
+ break;
+
+ case SPD_ATTR_END:
+ /* print */
+ if (or_needed) {
+ (void) printf("or ");
+ } else {
+ or_needed = 1;
+ }
+ print_iap(&iap);
+ break;
+
+ case SPD_ATTR_EMPTY:
+ /* clear */
+ (void) memset(&iap, 0, sizeof (iap));
+ break;
+
+ case SPD_ATTR_NEXT:
+ /* print */
+ if (or_needed) {
+ (void) printf("or ");
+ } else {
+ or_needed = 1;
+ }
+
+ print_iap(&iap);
+ break;
+
+ case SPD_ATTR_TYPE:
+ iap.iap_action = app->spd_attr_value;
+ break;
+
+ case SPD_ATTR_FLAGS:
+ iap.iap_attr = app->spd_attr_value;
+ break;
+
+ case SPD_ATTR_AH_AUTH:
+ iap.iap_aauth.alg_id = app->spd_attr_value;
+ break;
+
+ case SPD_ATTR_ESP_ENCR:
+ iap.iap_eencr.alg_id = app->spd_attr_value;
+ break;
+
+ case SPD_ATTR_ESP_AUTH:
+ iap.iap_eauth.alg_id = app->spd_attr_value;
+ break;
+
+ case SPD_ATTR_ENCR_MINBITS:
+ iap.iap_eencr.alg_minbits = app->spd_attr_value;
+ break;
+
+ case SPD_ATTR_ENCR_MAXBITS:
+ iap.iap_eencr.alg_maxbits = app->spd_attr_value;
+ break;
+
+ case SPD_ATTR_AH_MINBITS:
+ iap.iap_aauth.alg_minbits = app->spd_attr_value;
+ break;
+
+ case SPD_ATTR_AH_MAXBITS:
+ iap.iap_aauth.alg_maxbits = app->spd_attr_value;
+ break;
+
+ case SPD_ATTR_ESPA_MINBITS:
+ iap.iap_eauth.alg_minbits = app->spd_attr_value;
+ break;
+
+ case SPD_ATTR_ESPA_MAXBITS:
+ iap.iap_eauth.alg_maxbits = app->spd_attr_value;
+ break;
+
+ case SPD_ATTR_LIFE_SOFT_TIME:
+ case SPD_ATTR_LIFE_HARD_TIME:
+ case SPD_ATTR_LIFE_SOFT_BYTES:
+ case SPD_ATTR_LIFE_HARD_BYTES:
+ default:
+ (void) printf("\tattr %d: %X-%d\n",
+ act_count,
+ app->spd_attr_tag,
+ app->spd_attr_value);
+ break;
+ }
+ app++;
+ }
+ }
+
+ (void) printf("\n");
+}
+
+#ifdef DEBUG_HEAVY
+static void
+pfpol_msg_dump(spd_msg_t *msg, char *tag)
+{
+ spd_ext_t *exts[SPD_EXT_MAX+1];
+ uint32_t i;
+ spd_address_t *spd_address;
+ struct spd_rule *spd_rule;
+ struct spd_proto *spd_proto;
+ struct spd_portrange *spd_portrange;
+ struct spd_typecode *spd_typecode;
+ struct spd_ext_actions *spd_ext_actions;
+ struct spd_attribute *app;
+ char abuf[INET6_ADDRSTRLEN];
+ uint32_t rv;
+ uint16_t act_count;
+
+ rv = spdsock_get_ext(exts, msg, msg->spd_msg_len, NULL, 0);
+ if (rv != KGE_OK)
+ return;
+
+ (void) printf("===========%s==============\n", tag);
+ (void) printf("pfpol_msg_dump %d\n-------------------\n", rv);
+
+ (void) printf("spd_msg_version:%d\n", msg->spd_msg_version);
+ (void) printf("spd_msg_type:%d\n", msg->spd_msg_type);
+ (void) printf("spd_msg_errno:%d\n", msg->spd_msg_errno);
+ (void) printf("spd_msg_spdid:%d\n", msg->spd_msg_spdid);
+ (void) printf("spd_msg_len:%d\n", msg->spd_msg_len);
+ (void) printf("spd_msg_diagnostic:%d\n", msg->spd_msg_diagnostic);
+ (void) printf("spd_msg_seq:%d\n", msg->spd_msg_seq);
+ (void) printf("spd_msg_pid:%d\n", msg->spd_msg_pid);
+
+ for (i = 1; i <= SPD_EXT_MAX; i++) {
+ if (exts[i] == NULL) {
+ printf("skipped %d\n", i);
+ continue;
+ }
+
+ switch (i) {
+ case SPD_EXT_ICMP_TYPECODE:
+ spd_typecode = (struct spd_typecode *)exts[i];
+ (void) printf("icmp type %d-%d code %d-%d\n",
+ spd_typecode->spd_typecode_type,
+ spd_typecode->spd_typecode_type_end,
+ spd_typecode->spd_typecode_code,
+ spd_typecode->spd_typecode_code_end);
+ break;
+
+ case SPD_EXT_LCLPORT:
+ spd_portrange = (struct spd_portrange *)exts[i];
+ (void) printf("local ports %d-%d\n",
+ spd_portrange->spd_ports_minport,
+ spd_portrange->spd_ports_maxport);
+
+ break;
+
+ case SPD_EXT_REMPORT:
+ spd_portrange = (struct spd_portrange *)exts[i];
+ (void) printf("remote ports %d-%d\n",
+ spd_portrange->spd_ports_minport,
+ spd_portrange->spd_ports_maxport);
+
+ break;
+
+ case SPD_EXT_PROTO:
+ spd_proto = (struct spd_proto *)exts[i];
+ (void) printf("proto:spd_proto_exttype %d\n",
+ spd_proto->spd_proto_exttype);
+ (void) printf("proto:spd_proto_number %d\n",
+ spd_proto->spd_proto_number);
+ break;
+
+ case SPD_EXT_LCLADDR:
+ case SPD_EXT_REMADDR:
+ spd_address = (spd_address_t *)exts[i];
+ if (i == SPD_EXT_LCLADDR)
+ (void) printf("local addr ");
+ else
+ (void) printf("remote addr ");
+
+
+ (void) printf("%s\n",
+ inet_ntop(spd_address->spd_address_af,
+ (void *) (spd_address +1), abuf,
+ INET6_ADDRSTRLEN));
+
+ (void) printf("prefixlen: %d\n",
+ spd_address->spd_address_prefixlen);
+ break;
+
+ case SPD_EXT_ACTION:
+ spd_ext_actions = (struct spd_ext_actions *)exts[i];
+ (void) printf("spd_ext_action\n");
+ (void) printf("spd_actions_count %d\n",
+ spd_ext_actions->spd_actions_count);
+ app = (struct spd_attribute *)(spd_ext_actions + 1);
+
+ for (act_count = 0;
+ act_count < spd_ext_actions->spd_actions_len -1;
+ act_count++) {
+ (void) printf("\tattr %d: %X-%d\n", act_count,
+ app->spd_attr_tag, app->spd_attr_value);
+ app++;
+ }
+
+ break;
+
+ case SPD_EXT_RULE:
+ spd_rule = (struct spd_rule *)exts[i];
+ (void) printf("spd_rule_priority: 0x%x\n",
+ spd_rule->spd_rule_priority);
+ (void) printf("spd_rule_flags: %d\n",
+ spd_rule->spd_rule_flags);
+ break;
+
+ case SPD_EXT_RULESET:
+ (void) printf("spd_ext_ruleset\n");
+ break;
+ default:
+ (void) printf("default\n");
+ break;
+ }
+ }
+
+ (void) printf("-------------------\n");
+ (void) printf("=========================\n");
+}
+#endif /* DEBUG_HEAVY */
+
+static int
+ipsec_conf_view()
+{
+ char buf[MAXLEN];
+ FILE *fp;
+
+ fp = fopen(POLICY_CONF_FILE, "r");
+ if (fp == NULL) {
+ if (errno == ENOENT) {
+ /*
+ * The absence of POLICY_CONF_FILE should
+ * not cause the command to exit with a
+ * non-zero status, since this condition
+ * is valid when no policies were previously
+ * defined.
+ */
+ return (0);
+ }
+ warn(gettext("%s cannot be opened"), POLICY_CONF_FILE);
+ return (-1);
+ }
+ while (fgets(buf, MAXLEN, fp) != NULL) {
+ /* Don't print removed entries */
+ if (*buf == ';')
+ continue;
+ if (strlen(buf) != 0)
+ buf[strlen(buf) - 1] = '\0';
+ (void) puts(buf);
+ }
+ return (0);
+}
+
+/*
+ * Delete nlines from start in the POLICY_CONF_FILE.
+ */
+static int
+delete_from_file(int start, int nlines)
+{
+ FILE *fp;
+ char ibuf[MAXLEN];
+ int len;
+
+ if ((fp = fopen(POLICY_CONF_FILE, "r+b")) == NULL) {
+ warn(gettext("%s cannot be opened"), POLICY_CONF_FILE);
+ return (-1);
+ }
+
+ /*
+ * Insert a ";", read the line and discard it. Repeat
+ * this logic nlines - 1 times. For the last line there
+ * is just a newline character. We can't just insert a
+ * single ";" character instead of the newline character
+ * as it would affect the next line. Thus when we comment
+ * the last line we seek one less and insert a ";"
+ * character, which will replace the newline of the
+ * penultimate line with ; and newline of the last line
+ * will become part of the previous line.
+ */
+ do {
+ /*
+ * It is not enough to seek just once and expect the
+ * subsequent fgets below to take you to the right
+ * offset of the next line. fgets below seems to affect
+ * the offset. Thus we need to seek, replace with ";",
+ * and discard a line using fgets for every line.
+ */
+ if (fseek(fp, start, SEEK_SET) == -1) {
+ warn("fseek");
+ return (-1);
+ }
+ if (fputc(';', fp) < 0) {
+ warn("fputc");
+ return (-1);
+ }
+ /*
+ * Flush the above ";" character before we do the fgets().
+ * Without this, fgets() gets confused with offsets.
+ */
+ (void) fflush(fp);
+ len = 0;
+ while (fgets(ibuf, MAXLEN, fp) != NULL) {
+ len += strlen(ibuf);
+ if (ibuf[len - 1] == '\n') {
+ /*
+ * We have read a complete line.
+ */
+ break;
+ }
+ }
+ /*
+ * We read the line after ";" character has been inserted.
+ * Thus len does not count ";". To advance to the next line
+ * increment by 1.
+ */
+ start += (len + 1);
+ /*
+ * If nlines == 2, we will be commenting out the last
+ * line next, which has only one newline character.
+ * If we blindly replace it with ";", it will be
+ * read as part of the next line which could have
+ * a INDEX string and thus confusing ipsec_conf_view.
+ * Thus, we seek one less and replace the previous
+ * line's newline character with ";", and the
+ * last line's newline character will become part of
+ * the previous line.
+ */
+ if (nlines == 2)
+ start--;
+ } while (--nlines != 0);
+ (void) fclose(fp);
+ if (nlines != 0)
+ return (-1);
+ else
+ return (0);
+}
+
+/*
+ * Delete an entry from the file by inserting a ";" at the
+ * beginning of the lines to be removed.
+ */
+static int
+ipsec_conf_del(int policy_index, boolean_t ignore_spd)
+{
+ act_prop_t *act_props = malloc(sizeof (act_prop_t));
+ char *buf;
+ FILE *fp;
+ char ibuf[MAXLEN];
+ int ibuf_len, index_len, index;
+ int ret = 0;
+ int offset, prev_offset;
+ int nlines;
+
+ if (act_props == NULL) {
+ warn(gettext("memory"));
+ return (-1);
+ }
+
+ fp = fopen(POLICY_CONF_FILE, "r");
+ if (fp == NULL) {
+ warn(gettext("%s cannot be opened"), POLICY_CONF_FILE);
+ free(act_props);
+ return (-1);
+ }
+
+ index_len = strlen(INDEX_TAG);
+ index = 0;
+ for (offset = prev_offset = 0; fgets(ibuf, MAXLEN, fp) != NULL;
+ offset += ibuf_len) {
+ prev_offset = offset;
+ ibuf_len = strlen(ibuf);
+
+ if (strncmp(ibuf, INDEX_TAG, index_len) != 0) {
+ continue;
+ }
+
+ /*
+ * This line contains INDEX_TAG
+ */
+ buf = ibuf + index_len;
+ buf++; /* Skip the space */
+ index = parse_int(buf);
+ if (index == -1) {
+ warnx(gettext("Invalid index in the file"));
+ free(act_props);
+ return (-1);
+ }
+ if (index == policy_index) {
+ if (!ignore_spd) {
+ ret = parse_one(fp, act_props);
+ if (ret == -1) {
+ warnx(gettext("Invalid policy entry "
+ "in the file"));
+ free(act_props);
+ return (-1);
+ }
+ }
+ /*
+ * nlines is the number of lines we should comment
+ * out. linecount tells us how many lines this command
+ * spans. And we need to remove the line with INDEX
+ * and an extra line we added during ipsec_conf_add.
+ *
+ * NOTE : If somebody added a policy entry which does
+ * not have a newline, ipsec_conf_add() fills in the
+ * newline. Hence, there is always 2 extra lines
+ * to delete.
+ */
+ nlines = linecount + 2;
+ goto delete;
+ }
+ }
+
+ if (!ignore_spd)
+ ret = pfp_delete_rule(policy_index);
+
+ if (ret != 0) {
+ warnx(gettext("Deletion incomplete. Please "
+ "flush all the entries and re-configure :"));
+ reconfigure();
+ free(act_props);
+ return (ret);
+ }
+ free(act_props);
+ return (ret);
+
+delete:
+ /* Delete nlines from prev_offset */
+ (void) fclose(fp);
+ ret = delete_from_file(prev_offset, nlines);
+
+ if (ret != 0) {
+ warnx(gettext("Deletion incomplete. Please "
+ "flush all the entries and re-configure :"));
+ reconfigure();
+ free(act_props);
+ return (ret);
+ }
+
+ if (!ignore_spd)
+ ret = pfp_delete_rule(policy_index);
+
+ if (ret != 0) {
+ warnx(gettext("Deletion incomplete. Please "
+ "flush all the entries and re-configure :"));
+ reconfigure();
+ free(act_props);
+ return (ret);
+ }
+ free(act_props);
+ return (0);
+}
+
+static int
+pfp_delete_rule(uint64_t index)
+{
+ struct spd_msg *msg;
+ struct spd_rule *rule;
+ int sfd = socket(PF_POLICY, SOCK_RAW, PF_POLICY_V1);
+ int cnt;
+
+ if (sfd < 0) {
+ warn(gettext("unable to open policy socket"));
+ return (-1);
+ }
+
+ msg = (spd_msg_t *)malloc(sizeof (spd_msg_t)
+ + sizeof (struct spd_rule));
+
+ if (msg == NULL) {
+ warn("malloc");
+ return (-1);
+ }
+
+ rule = (struct spd_rule *)(msg + 1);
+
+ (void) memset(msg, 0, sizeof (spd_msg_t) + sizeof (struct spd_rule));
+ msg->spd_msg_version = PF_POLICY_V1;
+ msg->spd_msg_type = SPD_DELETERULE;
+ msg->spd_msg_len = SPD_8TO64(sizeof (spd_msg_t)
+ + sizeof (struct spd_rule));
+
+ rule->spd_rule_type = SPD_EXT_RULE;
+ rule->spd_rule_len = SPD_8TO64(sizeof (struct spd_rule));
+ rule->spd_rule_index = index;
+
+ cnt = write(sfd, msg,
+ sizeof (spd_msg_t) + sizeof (struct spd_rule));
+
+ if (cnt != sizeof (spd_msg_t) + sizeof (struct spd_rule)) {
+ if (cnt < 0) {
+ (void) close(sfd);
+ free(msg);
+ warn(gettext("Delete failed: write"));
+ return (-1);
+ } else {
+ (void) close(sfd);
+ free(msg);
+ warnx(gettext("Delete failed: short write"));
+ return (-1);
+ }
+ }
+
+ cnt = read(sfd, msg,
+ sizeof (spd_msg_t) + sizeof (struct spd_rule));
+ if (cnt != sizeof (spd_msg_t) + sizeof (struct spd_rule)) {
+ if (cnt < 0) {
+ (void) close(sfd);
+ free(msg);
+ warn(gettext("Delete failed: read"));
+ return (-1);
+ } else {
+ (void) close(sfd);
+ free(msg);
+ warnx(gettext("Delete failed while reading reply"));
+ return (-1);
+ }
+ }
+ (void) close(sfd);
+ if (msg->spd_msg_errno != 0) {
+ free(msg);
+ errno = msg->spd_msg_errno;
+ warn(gettext("Delete failed: SPD_FLUSH"));
+ return (-1);
+ }
+
+ free(msg);
+ return (0);
+}
+
+static int
+ipsec_conf_flush(int db)
+{
+ int pfd, cnt;
+ int sfd = socket(PF_POLICY, SOCK_RAW, PF_POLICY_V1);
+ struct spd_msg msg;
+
+ if (sfd < 0) {
+ warn(gettext("unable to open policy socket"));
+ return (-1);
+ }
+
+ (void) memset(&msg, 0, sizeof (msg));
+ msg.spd_msg_version = PF_POLICY_V1;
+ msg.spd_msg_type = SPD_FLUSH;
+ msg.spd_msg_len = SPD_8TO64(sizeof (msg));
+ msg.spd_msg_spdid = db;
+
+ cnt = write(sfd, &msg, sizeof (msg));
+ if (cnt != sizeof (msg)) {
+ if (cnt < 0) {
+ warn(gettext("Flush failed: write"));
+ return (-1);
+ } else {
+ warnx(gettext("Flush failed: short write"));
+ return (-1);
+ }
+ }
+
+ cnt = read(sfd, &msg, sizeof (msg));
+ if (cnt != sizeof (msg)) {
+ if (cnt < 0) {
+ warn(gettext("Flush failed: read"));
+ return (-1);
+ } else {
+ warnx(gettext("Flush failed while reading reply"));
+ return (-1);
+ }
+ }
+ (void) close(sfd);
+ if (msg.spd_msg_errno != 0) {
+ warnx("%s: %s", gettext("Flush failed: SPD_FLUSH"),
+ strerror(msg.spd_msg_errno));
+ return (-1);
+ }
+
+ /* Truncate the file */
+ if (db == SPD_ACTIVE) {
+ if ((pfd = open(POLICY_CONF_FILE, O_TRUNC|O_RDWR)) == -1) {
+ if (errno == ENOENT) {
+ /*
+ * The absence of POLICY_CONF_FILE should
+ * not cause the command to exit with a
+ * non-zero status, since this condition
+ * is valid when no policies were previously
+ * defined.
+ */
+ return (0);
+ }
+ warn(gettext("%s cannot be truncated"),
+ POLICY_CONF_FILE);
+ return (-1);
+ }
+ (void) close(pfd);
+ }
+ return (0);
+}
+
+/* function to send SPD_FLIP and SPD_CLONE messages */
+static void
+ipsec_conf_admin(uint8_t type)
+{
+ int cnt;
+ int sfd = socket(PF_POLICY, SOCK_RAW, PF_POLICY_V1);
+ struct spd_msg msg;
+
+ if (sfd < 0) {
+ err(-1, gettext("unable to open policy socket"));
+ }
+
+ (void) memset(&msg, 0, sizeof (msg));
+ msg.spd_msg_version = PF_POLICY_V1;
+ msg.spd_msg_type = type;
+ msg.spd_msg_len = SPD_8TO64(sizeof (msg));
+
+ cnt = write(sfd, &msg, sizeof (msg));
+ if (cnt != sizeof (msg)) {
+ if (cnt < 0) {
+ err(-1, gettext("admin failed: write"));
+ } else {
+ errx(-1, gettext("admin failed: short write"));
+ }
+ }
+
+ cnt = read(sfd, &msg, sizeof (msg));
+ if (cnt != sizeof (msg)) {
+ if (cnt < 0) {
+ err(-1, gettext("admin failed: read"));
+ } else {
+ errx(-1, gettext("admin failed while reading reply"));
+ }
+ }
+ (void) close(sfd);
+ if (msg.spd_msg_errno != 0) {
+ errno = msg.spd_msg_errno;
+ err(-1, gettext("admin failed"));
+ }
+}
+
+static void
+reconfigure()
+{
+ (void) fprintf(stderr, gettext(
+ "\tipsecconf -f \n "
+ "\tipsecconf -a policy_file\n"));
+}
+
+static void
+usage(void)
+{
+ (void) fprintf(stderr, gettext(
+ "Usage: ipsecconf\n"
+ "\tipsecconf -a ([-]|<filename>) [-q]\n"
+ "\tipsecconf -r ([-]|<filename>) [-q]\n"
+ "\tipsecconf -d <index>\n"
+ "\tipsecconf -l [-n]\n"
+ "\tipsecconf -f\n"));
+}
+
+/*
+ * a type consists of
+ * "type" <int>{ "-" <int>}
+ * or
+ * "type" keyword
+ *
+ * a code consists of
+ * "code" <int>{ "-" <int>}
+ * or
+ * "code" keyword
+ */
+
+
+static int
+parse_type_code(const char *str, const str_val_t *table)
+{
+ char *end1, *end2;
+ int res1 = 0, res2 = 0;
+ int i;
+
+ if (isdigit(str[0])) {
+ res1 = strtol(str, &end1, 0);
+
+ if (end1 == str) {
+ return (-1);
+ }
+
+ if (res1 > 255 || res1 < 0) {
+ return (-1);
+ }
+
+ if (*end1 == '-') {
+ end1++;
+ res2 = strtol(end1, &end2, 0);
+ if (res2 > 255 || res2 < 0) {
+ return (-1);
+ }
+ } else {
+ end2 = end1;
+ }
+
+ while (isspace(*end2))
+ end2++;
+
+ if (*end2 != '\0') {
+ return (-1);
+ }
+
+ return (res1 + (res2 << 8));
+ }
+
+ for (i = 0; table[i].string; i++) {
+ if (strcmp(str, table[i].string) == 0) {
+ return (table[i].value);
+ }
+ }
+
+ return (-1);
+}
+
+static int
+parse_int(const char *str)
+{
+ char *end;
+ int res;
+
+ res = strtol(str, &end, 0);
+ if (end == str)
+ return (-1);
+ while (isspace(*end))
+ end++;
+ if (*end != '\0')
+ return (-1);
+ return (res);
+}
+
+/*
+ * Convert a mask to a prefix length.
+ * Returns prefix length on success, 0 otherwise.
+ */
+static unsigned int
+in_getprefixlen(char *mask)
+{
+ long prefixlen;
+ char *end;
+
+ prefixlen = strtol(mask, &end, 10);
+ if (prefixlen < 0) {
+ return (0);
+ }
+ if (mask == end) {
+ return (0);
+ }
+ if (*end != '\0') {
+ return (0);
+ }
+ return ((unsigned int)prefixlen);
+}
+
+/*
+ * Convert an IPv6 mask to a prefix len. I assume all IPv6 masks are
+ * contiguous, so I stop at the first bit!
+ */
+static int
+in_masktoprefix(uint8_t *mask, boolean_t is_v4mapped)
+{
+ int rc = 0;
+ uint8_t last;
+ int limit = IPV6_ABITS;
+
+ if (is_v4mapped) {
+ mask += ((IPV6_ABITS - IP_ABITS)/8);
+ limit = IP_ABITS;
+ }
+
+ while (*mask == 0xff) {
+ rc += 8;
+ if (rc == limit)
+ return (limit);
+ mask++;
+ }
+
+ last = *mask;
+ while (last != 0) {
+ rc++;
+ last = (last << 1) & 0xff;
+ }
+
+ return (rc);
+}
+
+/*
+ * Convert a prefix length to a mask.
+ * Assumes the mask array is zero'ed by the caller.
+ */
+static void
+in_prefixlentomask(unsigned int prefixlen, uchar_t *mask)
+{
+ while (prefixlen > 0) {
+ if (prefixlen >= 8) {
+ *mask++ = 0xFF;
+ prefixlen -= 8;
+ continue;
+ }
+ *mask |= 1 << (8 - prefixlen);
+ prefixlen--;
+ }
+}
+
+
+static int
+parse_address(int type, char *addr_str)
+{
+ char *ptr;
+ unsigned int prefix_len = 0;
+ struct netent *ne = NULL;
+ struct hostent *hp = NULL;
+ int h_errno;
+ struct in_addr netaddr;
+ struct in6_addr *netaddr6;
+ struct hostent *ne_hent;
+ boolean_t has_mask = B_FALSE;
+
+ ptr = strchr(addr_str, '/');
+ if (ptr != NULL) {
+ has_mask = B_TRUE;
+ *ptr++ = NULL;
+
+ prefix_len = in_getprefixlen(ptr);
+ if (prefix_len == 0)
+ return (-1);
+ }
+
+ /*
+ * getipnodebyname() is thread safe. This allows us to hold on to the
+ * returned hostent structure, which is pointed to by the shp and
+ * dhp globals for the source and destination addresses, respectively.
+ */
+ hp = getipnodebyname(addr_str, AF_INET6, AI_DEFAULT | AI_ALL, &h_errno);
+ if (hp != NULL) {
+ /*
+ * We come here for both a hostname and
+ * any host address /network address.
+ */
+ assert(hp->h_addrtype == AF_INET6);
+ } else if ((ne = getnetbyname(addr_str)) != NULL) {
+ switch (ne->n_addrtype) {
+ case AF_INET:
+ /*
+ * Allocate a struct hostent and initialize
+ * it with the address corresponding to the
+ * network number previously returned by
+ * getnetbyname(). Freed by do_address_adds()
+ * once the policy is defined.
+ */
+ ne_hent = malloc(sizeof (struct hostent));
+ if (ne_hent == NULL) {
+ warn("malloc");
+ return (-1);
+ }
+ ne_hent->h_addr_list = malloc(2*sizeof (char *));
+ if (ne_hent->h_addr_list == NULL) {
+ warn("malloc");
+ free(ne_hent);
+ return (-1);
+ }
+ netaddr6 = malloc(sizeof (struct in6_addr));
+ if (netaddr6 == NULL) {
+ warn("malloc");
+ free(ne_hent->h_addr_list);
+ free(ne_hent);
+ return (-1);
+ }
+ ne_hent->h_addr_list[0] = (char *)netaddr6;
+ ne_hent->h_addr_list[1] = NULL;
+ netaddr = inet_makeaddr(ne->n_net, INADDR_ANY);
+ IN6_INADDR_TO_V4MAPPED(&netaddr, netaddr6);
+ hp = ne_hent;
+ break;
+ default:
+ warnx("Address type %d not supported.", ne->n_addrtype);
+ return (-1);
+ }
+ } else {
+ return (-1);
+ }
+
+ if (type == IPSEC_CONF_SRC_ADDRESS) {
+ shp = hp;
+ if (has_mask)
+ splen = prefix_len;
+ has_saprefix = has_mask;
+ } else {
+ dhp = hp;
+ if (has_mask)
+ dplen = prefix_len;
+ has_daprefix = has_mask;
+ }
+
+ return (0);
+}
+
+/*
+ * Add port-only entries. Make sure to add them in both the V6 and V4 tables!
+ */
+static int
+do_port_adds(ips_conf_t *cptr)
+{
+ int ret;
+
+ assert(IN6_IS_ADDR_UNSPECIFIED(&cptr->ips_src_addr_v6));
+ assert(IN6_IS_ADDR_UNSPECIFIED(&cptr->ips_dst_addr_v6));
+
+#ifdef DEBUG_HEAVY
+ (void) dump_conf(cptr);
+#endif
+
+ ret = send_pf_pol_message(SPD_ADDRULE, cptr);
+ if (ret != 0) {
+ warnx(
+ gettext("Could not add IPv4 policy for sport %d, dport %d"),
+ cptr->ips_src_port_min, cptr->ips_dst_port_min);
+
+ return (errno);
+ }
+
+ return (0);
+}
+
+/*
+ * Nuke a list of policy entries.
+ * rewrite this to use flipping
+ * d_list isn't freed because we will be
+ * exiting the program soon.
+ */
+static void
+nuke_adds()
+{
+ d_list_t *temp = d_list;
+ FILE *policy_fp;
+
+ policy_fp = fopen(POLICY_CONF_FILE, "a");
+ if (policy_fp == NULL) {
+ warn(gettext("%s cannot be opened"), POLICY_CONF_FILE);
+ }
+ (void) fprintf(policy_fp, "\n\n");
+ (void) fflush(policy_fp);
+
+ while (temp != NULL) {
+ (void) ipsec_conf_del(temp->index, B_TRUE);
+ temp = temp->next;
+ }
+}
+
+/*
+ * Set mask info from the specified prefix len. Fail if multihomed.
+ */
+static int
+set_mask_info(struct hostent *hp, unsigned int plen, struct in6_addr *mask_v6)
+{
+ struct in6_addr addr;
+ struct in_addr mask_v4;
+
+ if (hp->h_addr_list[1] != NULL) {
+ return (EINVAL);
+ }
+
+ if (!IN6_IS_ADDR_UNSPECIFIED(mask_v6)) {
+ return (EBUSY);
+ }
+
+ bcopy(hp->h_addr_list[0], &addr, sizeof (struct in6_addr));
+ if (IN6_IS_ADDR_V4MAPPED(&addr)) {
+ if (plen > IP_ABITS) {
+ return (ERANGE);
+ }
+ (void) memset(&mask_v4, 0, sizeof (mask_v4));
+ in_prefixlentomask(plen, (uchar_t *)&mask_v4);
+ IN6_INADDR_TO_V4MAPPED(&mask_v4, mask_v6);
+ } else {
+ if (plen > IPV6_ABITS) {
+ return (ERANGE);
+ }
+ /* mask_v6 is already zero (unspecified), see test above */
+ in_prefixlentomask(plen, (uchar_t *)mask_v6);
+ }
+ return (0);
+}
+
+/*
+ * Initialize the specified IPv6 address with all f's.
+ */
+static void
+init_addr_wildcard(struct in6_addr *addr_v6, boolean_t isv4)
+{
+ if (isv4) {
+ uint32_t addr_v4 = 0xffffffff;
+ IN6_INADDR_TO_V4MAPPED((struct in_addr *)&addr_v4, addr_v6);
+ } else {
+ (void) memset(addr_v6, 0xff, sizeof (struct in6_addr));
+ }
+}
+
+/*
+ * Called at the end to actually add policy. Handles single and multi-homed
+ * cases.
+ */
+static int
+do_address_adds(ips_conf_t *cptr)
+{
+ int i, j;
+ int ret = 0; /* For ioctl() call. */
+ int rc = 0; /* My own return code. */
+ struct in6_addr zeroes = {0, 0, 0, 0};
+ char *ptr[2];
+ struct hostent hent;
+ boolean_t isv4;
+ int add_count = 0;
+
+ /*
+ * dst_hent may not be initialized if a destination
+ * address was not given. It will be initalized with just
+ * one address if a destination address was given. In both
+ * the cases, we initialize here with ipsc_dst_addr and enter
+ * the loop below.
+ */
+ if (dhp == NULL) {
+ assert(shp != NULL);
+ hent.h_addr_list = ptr;
+ ptr[0] = (char *)&zeroes.s6_addr;
+ ptr[1] = NULL;
+ dhp = &hent;
+ } else if (shp == NULL) {
+ assert(dhp != NULL);
+ hent.h_addr_list = ptr;
+ ptr[0] = (char *)&zeroes.s6_addr;
+ ptr[1] = NULL;
+ shp = &hent;
+ }
+
+ /*
+ * Set mask info here. Bail if multihomed and there's a prefix len.
+ */
+ if (has_saprefix) {
+ rc = set_mask_info(shp, splen, &cptr->ips_src_mask_v6);
+ if (rc != 0)
+ goto bail;
+ cptr->ips_src_mask_len = splen;
+ }
+
+ if (has_daprefix) {
+ rc = set_mask_info(dhp, dplen, &cptr->ips_dst_mask_v6);
+ if (rc != 0)
+ goto bail;
+ cptr->ips_dst_mask_len = dplen;
+ }
+
+ for (i = 0; shp->h_addr_list[i] != NULL; i++) {
+ bcopy(shp->h_addr_list[i], &cptr->ips_src_addr_v6,
+ sizeof (struct in6_addr));
+ isv4 = cptr->ips_isv4 =
+ IN6_IS_ADDR_V4MAPPED(&cptr->ips_src_addr_v6);
+ if (IN6_IS_ADDR_UNSPECIFIED(&cptr->ips_src_mask_v6) &&
+ shp != &hent) {
+ init_addr_wildcard(&cptr->ips_src_mask_v6, isv4);
+ }
+
+ for (j = 0; dhp->h_addr_list[j] != NULL; j++) {
+ bcopy(dhp->h_addr_list[j], &cptr->ips_dst_addr_v6,
+ sizeof (struct in6_addr));
+ if (IN6_IS_ADDR_UNSPECIFIED(&cptr->ips_src_addr_v6)) {
+ /*
+ * Src was not specified, so update isv4 flag
+ * for this policy according to the family
+ * of the destination address.
+ */
+ isv4 = cptr->ips_isv4 =
+ IN6_IS_ADDR_V4MAPPED(
+ &cptr->ips_dst_addr_v6);
+ } else if ((dhp != &hent) && (isv4 !=
+ IN6_IS_ADDR_V4MAPPED(&cptr->ips_dst_addr_v6))) {
+ /* v6/v4 mismatch. */
+ continue;
+ }
+ if (IN6_IS_ADDR_UNSPECIFIED(&cptr->ips_dst_mask_v6) &&
+ dhp != &hent) {
+ init_addr_wildcard(&cptr->ips_dst_mask_v6,
+ isv4);
+ }
+
+ ret = send_pf_pol_message(SPD_ADDRULE, cptr);
+
+ if (ret != 0) {
+ /* For now, allow duplicate/overlap policies. */
+ if (errno != EEXIST) {
+ /*
+ * We have an error where we added
+ * some, but had errors with others.
+ * Undo the previous adds, and
+ * bail.
+ */
+ rc = errno;
+ goto bail;
+ }
+ }
+
+ add_count++;
+ bzero(&cptr->ips_dst_mask_v6,
+ sizeof (struct in6_addr));
+ }
+
+ bzero(&cptr->ips_src_mask_v6, sizeof (struct in6_addr));
+ }
+
+bail:
+ if (shp != &hent)
+ freehostent(shp);
+ shp = NULL;
+ if (dhp != &hent)
+ freehostent(dhp);
+ dhp = NULL;
+ splen = 0;
+ dplen = 0;
+
+ if ((add_count == 0) && (rc == 0)) {
+ /*
+ * No entries were added. We failed all adds
+ * because the entries already existed, or because
+ * no v4 or v6 src/dst pairs were found. Either way,
+ * we must fail here with an appropriate error
+ * to avoid a corresponding entry from being added
+ * to ipsecpolicy.conf.
+ */
+ if ((ret == -1) && (errno == EEXIST)) {
+ /* All adds failed with EEXIST */
+ rc = EEXIST;
+ } else {
+ /* No matching v4 or v6 src/dst pairs */
+ rc = ESRCH;
+ }
+ }
+
+ return (rc);
+}
+
+static int
+parse_mask(int type, char *mask_str, ips_conf_t *cptr)
+{
+ struct in_addr mask;
+ struct in6_addr *mask6;
+
+ if (type == IPSEC_CONF_SRC_MASK) {
+ mask6 = &cptr->ips_src_mask_v6;
+ } else {
+ mask6 = &cptr->ips_dst_mask_v6;
+ }
+
+ if ((strncasecmp(mask_str, "0x", 2) == 0) &&
+ (strchr(mask_str, '.') == NULL)) {
+ /* Is it in the form 0xff000000 ? */
+ char *end;
+
+ mask.s_addr = strtoul(mask_str, &end, 0);
+ if (end == mask_str) {
+ return (-1);
+ }
+ if (*end != '\0') {
+ return (-1);
+ }
+ mask.s_addr = htonl(mask.s_addr);
+ } else {
+ /*
+ * Since inet_addr() returns -1 on error, we have
+ * to convert a broadcast address ourselves.
+ */
+ if (strcmp(mask_str, "255.255.255.255") == 0) {
+ mask.s_addr = 0xffffffff;
+ } else {
+ mask.s_addr = inet_addr(mask_str);
+ if (mask.s_addr == (unsigned int)-1)
+ return (-1);
+ }
+ }
+
+ /* Should we check for non-contiguous masks ? */
+ if (mask.s_addr == 0)
+ return (-1);
+ IN6_INADDR_TO_V4MAPPED(&mask, mask6);
+
+
+ if (type == IPSEC_CONF_SRC_MASK) {
+ cptr->ips_src_mask_len = in_masktoprefix(mask6->s6_addr,
+ B_TRUE);
+ } else {
+ cptr->ips_dst_mask_len = in_masktoprefix(mask6->s6_addr,
+ B_TRUE);
+ }
+
+ return (0);
+}
+
+static int
+parse_port(int type, char *port_str, ips_conf_t *conf)
+{
+ struct servent *sent;
+ in_port_t port;
+ int ret;
+
+ sent = getservbyname(port_str, NULL);
+ if (sent == NULL) {
+ ret = parse_int(port_str);
+ if (ret < 0 || ret >= 65536) {
+ return (-1);
+ }
+ port = htons((in_port_t)ret);
+ } else {
+ port = sent->s_port;
+ }
+ if (type == IPSEC_CONF_SRC_PORT) {
+ conf->ips_src_port_min = conf->ips_src_port_max = port;
+ } else {
+ conf->ips_dst_port_min = conf->ips_dst_port_max = port;
+ }
+ return (0);
+}
+
+static int
+valid_algorithm(int proto_num, const char *str)
+{
+ const char *tmp;
+ int ret;
+ struct ipsecalgent *alg;
+
+ alg = getipsecalgbyname(str, proto_num, NULL);
+ if (alg != NULL) {
+ ret = alg->a_alg_num;
+ freeipsecalgent(alg);
+ return (ret);
+ }
+
+ /*
+ * Look whether it could be a valid number.
+ * We support numbers also so that users can
+ * load algorithms as they need it. We can't
+ * check for validity of numbers here. It will
+ * be checked when the SA is negotiated/looked up.
+ * parse_int uses strtol(str), which converts 3DES
+ * to a valid number i.e looks only at initial
+ * number part. If we come here we should expect
+ * only a decimal number.
+ */
+ tmp = str;
+ while (*tmp) {
+ if (!isdigit(*tmp))
+ return (-1);
+ tmp++;
+ }
+
+ ret = parse_int(str);
+ if (ret > 0 && ret <= 255)
+ return (ret);
+ else
+ return (-1);
+}
+
+static int
+parse_ipsec_alg(char *str, ips_act_props_t *iap, int alg_type)
+{
+ int alg_value;
+ char tstr[VALID_ALG_LEN];
+ char *lens = NULL;
+ char *l1_str;
+ int l1 = 0;
+ char *l2_str;
+ int l2 = SPD_MAX_MAXBITS;
+ algreq_t *ap;
+ uint_t a_type;
+
+ fetch_algorithms();
+
+ /*
+ * Make sure that we get a null terminated string.
+ * For a bad input, we truncate at VALID_ALG_LEN.
+ */
+ (void) strncpy(tstr, str, VALID_ALG_LEN - 1);
+ tstr[VALID_ALG_LEN - 1] = '\0';
+ lens = strtok(tstr, "()");
+ lens = strtok(NULL, "()");
+
+ if (lens != NULL) {
+ int len1 = 0;
+ int len2 = SPD_MAX_MAXBITS;
+ int len_all = strlen(lens);
+ int dot_start = (lens[0] == '.');
+ l1_str = strtok(lens, ".");
+ l2_str = strtok(NULL, ".");
+ if (l1_str != NULL) {
+ l1 = parse_int(l1_str);
+ len1 = strlen(l1_str);
+ if (len1 < 0)
+ return (1);
+ }
+ if (l2_str != NULL) {
+ l2 = parse_int(l2_str);
+ len2 = strlen(l2_str);
+ if (len2 < 0)
+ return (1);
+ }
+
+ if (len_all == len1) {
+ /* alg(n) */
+ l2 = l1;
+ } else if (dot_start) {
+ /* alg(..n) */
+ l2 = l1;
+ l1 = 0;
+ } else if ((len_all - 2) == len1) {
+ /* alg(n..) */
+ l2 = SPD_MAX_MAXBITS;
+ } /* else alg(n..m) */
+ }
+
+ if (alg_type == SPD_ATTR_AH_AUTH ||
+ alg_type == SPD_ATTR_ESP_AUTH) {
+ alg_value = valid_algorithm(IPSEC_PROTO_AH, tstr);
+ } else {
+ alg_value = valid_algorithm(IPSEC_PROTO_ESP, tstr);
+ }
+ if (alg_value == -1) {
+ return (-1);
+ }
+
+ if (alg_type == SPD_ATTR_AH_AUTH) {
+ a_type = AH_AUTH;
+ iap->iap_attr |= SPD_APPLY_AH;
+ ap = &(iap->iap_aauth);
+ } else if (alg_type == SPD_ATTR_ESP_AUTH) {
+ a_type = ESP_AUTH;
+ iap->iap_attr |= SPD_APPLY_ESP|SPD_APPLY_ESPA;
+ ap = &(iap->iap_eauth);
+ } else {
+ a_type = ESP_ENCR;
+ iap->iap_attr |= SPD_APPLY_ESP;
+ ap = &(iap->iap_eencr);
+ }
+
+ ap->alg_id = alg_value;
+ ap->alg_minbits = l1;
+ ap->alg_maxbits = l2;
+
+ if (!alg_rangecheck(a_type, alg_value, ap))
+ return (-1);
+
+ return (0);
+}
+
+static void
+error_message(error_type_t error, int type, int line)
+{
+ char *mesg;
+
+ switch (type) {
+ case IPSEC_CONF_SRC_ADDRESS:
+ mesg = gettext("Source Address");
+ break;
+ case IPSEC_CONF_DST_ADDRESS:
+ mesg = gettext("Destination Address");
+ break;
+ case IPSEC_CONF_SRC_PORT:
+ mesg = gettext("Source Port");
+ break;
+ case IPSEC_CONF_DST_PORT:
+ mesg = gettext("Destination Port");
+ break;
+ case IPSEC_CONF_SRC_MASK:
+ mesg = gettext("Source Mask");
+ break;
+ case IPSEC_CONF_DST_MASK:
+ mesg = gettext("Destination Mask");
+ break;
+ case IPSEC_CONF_ULP:
+ mesg = gettext("Upper Layer Protocol");
+ break;
+ case IPSEC_CONF_IPSEC_AALGS:
+ mesg = gettext("Authentication Algorithm");
+ break;
+ case IPSEC_CONF_IPSEC_EALGS:
+ mesg = gettext("Encryption Algorithm");
+ break;
+ case IPSEC_CONF_IPSEC_EAALGS:
+ mesg = gettext("ESP Authentication Algorithm");
+ break;
+ case IPSEC_CONF_IPSEC_SA:
+ mesg = gettext("SA");
+ break;
+ case IPSEC_CONF_IPSEC_DIR:
+ mesg = gettext("Direction");
+ break;
+ case IPSEC_CONF_ICMP_TYPE:
+ mesg = gettext("ICMP type");
+ break;
+ case IPSEC_CONF_ICMP_CODE:
+ mesg = gettext("ICMP code");
+ break;
+ default :
+ return;
+ }
+ /*
+ * If we never read a newline character, we don't want
+ * to print 0.
+ */
+ warnx(gettext("%s %s on line: %d"), (error == BAD_ERROR) ?
+ gettext("Bad") : gettext("Duplicate"), mesg,
+ (arg_indices[line] == 0) ? 1 : arg_indices[line]);
+}
+
+static int
+validate_properties(ips_act_props_t *cptr, boolean_t dir, boolean_t is_alg)
+{
+ if (cptr->iap_action == SPD_ACTTYPE_PASS ||
+ cptr->iap_action == SPD_ACTTYPE_DROP) {
+ if (!dir) {
+ warnx(gettext("dir string "
+ "not found for bypass policy"));
+ }
+
+ if (is_alg) {
+ warnx(gettext("Algorithms found for bypass policy"));
+ return (-1);
+ }
+ return (0);
+ }
+ if (!is_alg) {
+ warnx(gettext("No IPSEC algorithms given"));
+ return (-1);
+ }
+ if (cptr->iap_attr == 0) {
+ warnx(gettext("No SA attribute"));
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * This function is called only to parse a single rule's worth of
+ * action strings. This is called after parsing pattern and before
+ * parsing properties. Thus we may have something in the leftover
+ * buffer while parsing the pattern, which we need to handle here.
+ */
+static int
+parse_action(FILE *fp, char **action, char **leftover)
+{
+ char *cp;
+ char ibuf[MAXLEN];
+ char *tmp_buf;
+ char *buf;
+ boolean_t new_stuff;
+
+ if (*leftover != NULL) {
+ buf = *leftover;
+ new_stuff = B_FALSE;
+ goto scan;
+ }
+ while (fgets(ibuf, MAXLEN, fp) != NULL) {
+ new_stuff = B_TRUE;
+ if (ibuf[strlen(ibuf) - 1] == '\n')
+ linecount++;
+ buf = ibuf;
+scan:
+ /* Truncate at the beginning of a comment */
+ cp = strchr(buf, '#');
+ if (cp != NULL)
+ *cp = NULL;
+
+ /* Skip any whitespace */
+ while (*buf != NULL && isspace(*buf))
+ buf++;
+
+ /* Empty line */
+ if (*buf == NULL)
+ continue;
+
+ /*
+ * Store the command for error reporting
+ * and ipsec_conf_add().
+ */
+ if (new_stuff) {
+ /*
+ * Check for buffer overflow including the null
+ * terminating character.
+ */
+ int len = strlen(ibuf);
+ if ((cbuf_offset + len + 1) >= CBUF_LEN)
+ return (-1);
+ (void) strcpy(cbuf + cbuf_offset, ibuf);
+ cbuf_offset += len;
+ }
+ /*
+ * Start of the non-empty non-space character.
+ */
+ tmp_buf = buf++;
+
+ /* Skip until next whitespace or CURL_BEGIN */
+ while (*buf != NULL && !isspace(*buf) &&
+ *buf != CURL_BEGIN)
+ buf++;
+
+
+ if (*buf != NULL) {
+ if (*buf == CURL_BEGIN) {
+ *buf = NULL;
+ /* Allocate an extra byte for the null also */
+ if ((*action = malloc(strlen(tmp_buf) + 1)) ==
+ NULL) {
+ warn("malloc");
+ return (ENOMEM);
+ }
+ (void) strcpy(*action, tmp_buf);
+ *buf = CURL_BEGIN;
+ } else {
+ /* We have hit a space */
+ *buf++ = NULL;
+ /* Allocate an extra byte for the null also */
+ if ((*action = malloc(strlen(tmp_buf) + 1)) ==
+ NULL) {
+ warn("malloc");
+ return (ENOMEM);
+ }
+ (void) strcpy(*action, tmp_buf);
+ }
+ /*
+ * Copy the rest of the line into the
+ * leftover buffer.
+ */
+ if (*buf != NULL) {
+ (void) strcpy(lo_buf, buf);
+ *leftover = lo_buf;
+ } else {
+ *leftover = NULL;
+ }
+ } else {
+ /* Allocate an extra byte for the null also */
+ if ((*action = malloc(strlen(tmp_buf) + 1)) ==
+ NULL) {
+ warn("malloc");
+ return (ENOMEM);
+ }
+ (void) strcpy(*action, tmp_buf);
+ *leftover = NULL;
+ }
+ if (argindex >= ARG_BUF_LEN)
+ return (-1);
+ arg_indices[argindex++] = linecount;
+ return (PARSE_SUCCESS);
+ }
+ /*
+ * Return error, on an empty action field.
+ */
+ return (-1);
+}
+
+/*
+ * This is called to parse pattern or properties that is enclosed
+ * between CURL_BEGIN and CURL_END.
+ */
+static int
+parse_pattern_or_prop(FILE *fp, char *argvec[], char **leftover)
+{
+ char *cp;
+ int i = 0;
+ boolean_t curl_begin_seen = B_FALSE;
+ char ibuf[MAXLEN];
+ char *tmp_buf;
+ char *buf;
+ boolean_t new_stuff;
+
+ /*
+ * When parsing properties, leftover buffer could have the
+ * leftovers of the previous fgets().
+ */
+ if (*leftover != NULL) {
+ buf = *leftover;
+ new_stuff = B_FALSE;
+ goto scan;
+ }
+ while (fgets(ibuf, MAXLEN, fp) != NULL) {
+ new_stuff = B_TRUE;
+#ifdef DEBUG_HEAVY
+ (void) printf("%s\n", ibuf);
+#endif
+ if (ibuf[strlen(ibuf) - 1] == '\n')
+ linecount++;
+ buf = ibuf;
+scan:
+ /* Truncate at the beginning of a comment */
+ cp = strchr(buf, '#');
+ if (cp != NULL)
+ *cp = NULL;
+
+ /* Skip any whitespace */
+ while (*buf != NULL && isspace(*buf))
+ buf++;
+
+ /* Empty line */
+ if (*buf == NULL)
+ continue;
+ /*
+ * Store the command for error reporting
+ * and ipsec_conf_add().
+ */
+ if (new_stuff) {
+ /*
+ * Check for buffer overflow including the null
+ * terminating character.
+ */
+ int len = strlen(ibuf);
+ if ((cbuf_offset + len + 1) >= CBUF_LEN)
+ return (-1);
+ (void) strcpy(cbuf + cbuf_offset, ibuf);
+ cbuf_offset += len;
+ }
+ /*
+ * First non-space character should be
+ * a curly bracket.
+ */
+ if (!curl_begin_seen) {
+ if (*buf != CURL_BEGIN) {
+ /*
+ * If we never read a newline character,
+ * we don't want to print 0.
+ */
+ warnx(gettext("Bad start on line %d :"),
+ (linecount == 0) ? 1 : linecount);
+ return (-1);
+ }
+ buf++;
+ curl_begin_seen = B_TRUE;
+ }
+ /*
+ * Arguments are separated by white spaces or
+ * newlines. Scan till you see a CURL_END.
+ */
+ while (*buf != NULL) {
+ if (*buf == CURL_END) {
+ret:
+ *buf++ = NULL;
+ /*
+ * Copy the rest of the line into the
+ * leftover buffer if any.
+ */
+ if (*buf != NULL) {
+ (void) strcpy(lo_buf, buf);
+ *leftover = lo_buf;
+ } else {
+ *leftover = NULL;
+ }
+ return (PARSE_SUCCESS);
+ }
+ /*
+ * Skip any trailing whitespace until we see a
+ * non white-space character.
+ */
+ while (*buf != NULL && isspace(*buf))
+ buf++;
+
+ if (*buf == CURL_END)
+ goto ret;
+
+ /* Scan the next line as this buffer is empty */
+ if (*buf == NULL)
+ break;
+
+ if (i >= MAXARGS) {
+ warnx(
+ gettext("Number of Arguments exceeded %d"),
+ i);
+ return (-1);
+ }
+ /*
+ * Non-empty, Non-space buffer.
+ */
+ tmp_buf = buf++;
+ /*
+ * Real scan of the argument takes place here.
+ * Skip past till space or CURL_END.
+ */
+ while (*buf != NULL && !isspace(*buf) &&
+ *buf != CURL_END) {
+ buf++;
+ }
+ /*
+ * Either a space or we have hit the CURL_END or
+ * the real end.
+ */
+ if (*buf != NULL) {
+ if (*buf == CURL_END) {
+ *buf++ = NULL;
+ if ((argvec[i] = malloc(strlen(tmp_buf)
+ + 1)) == NULL) {
+ warn("malloc");
+ return (ENOMEM);
+ }
+ /*
+ * Copy the rest of the line into the
+ * leftover buffer.
+ */
+ if (*buf != NULL) {
+ (void) strcpy(lo_buf, buf);
+ *leftover = lo_buf;
+ } else {
+ *leftover = NULL;
+ }
+ if (strlen(tmp_buf) != 0) {
+ (void) strcpy(argvec[i],
+ tmp_buf);
+ if (argindex >= ARG_BUF_LEN)
+ return (-1);
+ arg_indices[argindex++] =
+ linecount;
+ }
+ return (PARSE_SUCCESS);
+ } else {
+ *buf++ = NULL;
+ }
+ }
+ /*
+ * Copy this argument and scan for the buffer more
+ * if it is non-empty. If it is empty scan for
+ * the next line.
+ */
+ if ((argvec[i] = malloc(strlen(tmp_buf) + 1)) ==
+ NULL) {
+ warn("malloc");
+ return (ENOMEM);
+ }
+ (void) strcpy(argvec[i++], tmp_buf);
+ if (argindex >= ARG_BUF_LEN)
+ return (-1);
+ arg_indices[argindex++] = linecount;
+ }
+ }
+ /*
+ * If nothing is given in the file, it is okay.
+ * If something is given in the file and it is
+ * not CURL_BEGIN, we would have returned error
+ * above. If curl_begin_seen and we are here,
+ * something is wrong.
+ */
+ if (curl_begin_seen)
+ return (-1);
+ return (PARSE_EOF); /* Nothing more in the file */
+}
+
+/*
+ * Parse one command i.e {pattern} action {properties}.
+ *
+ * {pattern} ( action {prop} | pass | drop ) (or ...)*
+ */
+static int
+parse_one(FILE *fp, act_prop_t *act_props)
+{
+ char *leftover;
+ int ret;
+ int i;
+ int ap_num = 0;
+ enum parse_state {pattern, action, prop } pstate;
+
+ has_daprefix = has_saprefix = B_FALSE;
+
+ (void) memset(act_props, 0, sizeof (act_prop_t));
+ pstate = pattern;
+
+ ret = 0;
+ leftover = NULL;
+ argindex = 0;
+ cbuf_offset = 0;
+ assert(shp == NULL && dhp == NULL);
+
+
+ for (;;) {
+ switch (pstate) {
+ case pattern:
+ {
+#ifdef DEBUG_HEAVY
+ (void) printf("pattern\n");
+#endif
+ ret = parse_pattern_or_prop(fp,
+ act_props->pattern, &leftover);
+ if (ret == PARSE_EOF) {
+ /* EOF reached */
+ return (0);
+ }
+ if (ret != 0) {
+ goto err;
+ }
+ pstate = action;
+ break;
+ }
+ case action:
+ {
+#ifdef DEBUG_HEAVY
+ (void) printf("action\n");
+#endif
+ ret = parse_action(fp,
+ &act_props->ap[ap_num].act, &leftover);
+ if (ret != 0) {
+ goto err;
+ }
+
+ /*
+ * Validate action now itself so that we don't
+ * proceed too much into the bad world.
+ */
+ for (i = 0; action_table[i].string; i++) {
+ if (strcmp(act_props->ap[ap_num].act,
+ action_table[i].string) == 0)
+ break;
+ }
+
+ if (action_table[i].tok_val == TOK_or) {
+ /* hit an or, go again */
+ break;
+ }
+
+ if (action_table[i].string == NULL) {
+ /*
+ * If we never read a newline
+ * character, we don't want
+ * to print 0.
+ */
+ warnx(gettext("(parse one)"
+ "Invalid action on line %d: %s"),
+ (linecount == 0) ? 1 : linecount,
+ act_props->ap[ap_num].act);
+ return (-1);
+ }
+
+ pstate = prop;
+ break;
+ }
+ case prop:
+ {
+#ifdef DEBUG_HEAVY
+ (void) printf("prop\n");
+#endif
+ ret = parse_pattern_or_prop(fp,
+ act_props->ap[ap_num].prop, &leftover);
+ if (ret != 0) {
+ goto err;
+ }
+
+ if (leftover != NULL) {
+ /* Accomodate spaces at the end */
+ while (*leftover != NULL) {
+ if (*leftover == 'o') {
+ leftover++;
+ if (*leftover == 'r') {
+ leftover++;
+ ap_num++;
+ if (ap_num > MAXARGS)
+ return (1);
+ pstate = action;
+ goto again;
+ }
+
+ }
+ if (!isspace(*leftover)) {
+ ret = -1;
+ goto err;
+ }
+ leftover++;
+ }
+ return (0);
+ }
+ ap_num++;
+ if (ap_num > MAXARGS)
+ return (0);
+ pstate = action; /* or */
+ break;
+ } /* case prop: */
+ } /* switch(pstate) */
+
+again:
+ if (ap_num > MAXARGS)
+ return (0);
+ } /* while(1) */
+err:
+ if (ret != 0) {
+ /*
+ * If we never read a newline character, we don't want
+ * to print 0.
+ */
+ warnx(gettext("Error before or at line %d"),
+ (linecount == 0) ? 1 : linecount);
+ }
+ return (ret);
+}
+
+/*
+ * convert an act_propts_t to an ips_conf_t
+ */
+
+static int
+form_ipsec_conf(act_prop_t *act_props, ips_conf_t *cptr)
+{
+ int i, j, k;
+ int tok_count = 0;
+ struct protoent *pent;
+ boolean_t saddr, daddr, ipsec_aalg, ipsec_ealg, ipsec_eaalg, dir;
+ boolean_t old_style, new_style;
+ struct in_addr mask;
+ int line_no;
+ int ret;
+ int ap_num = 0;
+ int type, code, type_end, code_end;
+#ifdef DEBUG_HEAVY
+ /*
+ * pattern => act_props->pattern
+ * action => act_props->ap[].act
+ * properties => act_props->ap[].prop
+ */
+ (void) printf("\npattern\n------------\n");
+ for (i = 0; act_props->pattern[i] != NULL; i++)
+ (void) printf("%s\n", act_props->pattern[i]);
+ (void) printf("apz\n----------\n");
+ for (j = 0; act_props->ap[j].act != NULL; j++) {
+
+ (void) printf("act%d->%s\n", j, act_props->ap[j].act);
+ for (i = 0; act_props->ap[j].prop[i] != NULL; i++)
+ (void) printf("%dprop%d->%s\n",
+ j, i, act_props->ap[j].prop[i]);
+ }
+ (void) printf("------------\n\n");
+#endif
+
+ (void) memset(cptr, 0, sizeof (ips_conf_t));
+ saddr = daddr = ipsec_aalg = ipsec_ealg = ipsec_eaalg = dir = B_FALSE;
+ old_style = new_style = B_FALSE;
+ /*
+ * Get the Pattern. NULL pattern is valid.
+ */
+ for (i = 0, line_no = 0; act_props->pattern[i]; i++, line_no++) {
+ for (j = 0; pattern_table[j].string; j++) {
+ if (strcmp(act_props->pattern[i],
+ pattern_table[j].string) == 0)
+ break;
+ }
+
+ if (pattern_table[j].string == NULL) {
+ /*
+ * If we never read a newline character, we don't want
+ * to print 0.
+ */
+ warnx(gettext("Invalid pattern on line %d: %s"),
+ (arg_indices[line_no] == 0) ? 1 :
+ arg_indices[line_no], act_props->pattern[i]);
+ return (-1);
+ }
+
+ cptr->patt_tok[tok_count++] = pattern_table[j].tok_val;
+
+ switch (pattern_table[j].tok_val) {
+
+ case TOK_dir:
+ i++, line_no++;
+ if (act_props->pattern[i] == NULL) {
+ error_message(BAD_ERROR,
+ IPSEC_CONF_IPSEC_DIR, line_no);
+ return (-1);
+ }
+
+ if (strncmp(act_props->pattern[i], "in", 2) == 0) {
+ cptr->ips_dir = SPD_RULE_FLAG_INBOUND;
+ } else if (strncmp(
+ act_props->pattern[i], "out", 3) == 0) {
+ cptr->ips_dir = SPD_RULE_FLAG_OUTBOUND;
+ } else if (strncmp(
+ act_props->pattern[i], "both", 4) == 0) {
+ if (old_style) {
+ error_message(BAD_ERROR,
+ IPSEC_CONF_IPSEC_DIR, line_no);
+ return (-1);
+ }
+ new_style = B_TRUE;
+ cptr->ips_dir =
+ SPD_RULE_FLAG_OUTBOUND |
+ SPD_RULE_FLAG_INBOUND;
+ } else {
+ error_message(BAD_ERROR,
+ IPSEC_CONF_IPSEC_DIR, line_no);
+ return (-1);
+ }
+ dir = B_TRUE;
+ break;
+
+ case TOK_local:
+ if (old_style) {
+ error_message(BAD_ERROR,
+ IPSEC_CONF_SRC_ADDRESS, line_no);
+ return (-1);
+ }
+ new_style = B_TRUE;
+
+ if (saddr) {
+ error_message(DUP_ERROR,
+ IPSEC_CONF_SRC_ADDRESS, line_no);
+ return (-1);
+ }
+ /*
+ * Use this to detect duplicates rather
+ * than 0 like other cases, because 0 for
+ * address means INADDR_ANY.
+ */
+ saddr = B_TRUE;
+ cptr->has_saddr = 1;
+ /*
+ * Advance to the string containing
+ * the address.
+ */
+ i++, line_no++;
+ if (act_props->pattern[i] == NULL) {
+ error_message(BAD_ERROR,
+ IPSEC_CONF_SRC_ADDRESS, line_no);
+ return (-1);
+ }
+ if (parse_address(IPSEC_CONF_SRC_ADDRESS,
+ act_props->pattern[i]) != 0) {
+ error_message(BAD_ERROR,
+ IPSEC_CONF_SRC_ADDRESS, line_no);
+ return (-1);
+ }
+ if (!cptr->has_smask)
+ cptr->has_smask = has_saprefix;
+
+ break;
+ case TOK_remote:
+ if (old_style) {
+ error_message(BAD_ERROR,
+ IPSEC_CONF_DST_ADDRESS, line_no);
+ return (-1);
+ }
+ new_style = B_TRUE;
+
+ if (daddr) {
+ error_message(DUP_ERROR,
+ IPSEC_CONF_DST_ADDRESS, line_no);
+ return (-1);
+ }
+ /*
+ * Use this to detect duplicates rather
+ * than 0 like other cases, because 0 for
+ * address means INADDR_ANY.
+ */
+ daddr = B_TRUE;
+ cptr->has_daddr = 1;
+ /*
+ * Advance to the string containing
+ * the address.
+ */
+ i++, line_no++;
+ if (act_props->pattern[i] == NULL) {
+ error_message(BAD_ERROR,
+ IPSEC_CONF_DST_ADDRESS, line_no);
+ return (-1);
+ }
+ if (parse_address(IPSEC_CONF_DST_ADDRESS,
+ act_props->pattern[i]) != 0) {
+ error_message(BAD_ERROR,
+ IPSEC_CONF_DST_ADDRESS, line_no);
+ return (-1);
+ }
+ if (!cptr->has_dmask)
+ cptr->has_dmask = has_daprefix;
+ break;
+
+ case TOK_saddr:
+ if (new_style) {
+ error_message(BAD_ERROR,
+ IPSEC_CONF_SRC_ADDRESS, line_no);
+ return (-1);
+ }
+ old_style = B_TRUE;
+
+ if (saddr) {
+ error_message(DUP_ERROR,
+ IPSEC_CONF_SRC_ADDRESS, line_no);
+ return (-1);
+ }
+ /*
+ * Use this to detect duplicates rather
+ * than 0 like other cases, because 0 for
+ * address means INADDR_ANY.
+ */
+ saddr = B_TRUE;
+ cptr->has_saddr = 1;
+ /*
+ * Advance to the string containing
+ * the address.
+ */
+ i++, line_no++;
+ if (act_props->pattern[i] == NULL) {
+ error_message(BAD_ERROR,
+ IPSEC_CONF_SRC_ADDRESS, line_no);
+ return (-1);
+ }
+
+ if (parse_address(IPSEC_CONF_SRC_ADDRESS,
+ act_props->pattern[i]) != 0) {
+ error_message(BAD_ERROR,
+ IPSEC_CONF_SRC_ADDRESS, line_no);
+ return (-1);
+ }
+ /* shp or bhp? */
+ if (!cptr->has_smask)
+ cptr->has_smask = has_saprefix;
+ break;
+
+ case TOK_daddr:
+ if (new_style) {
+ error_message(BAD_ERROR,
+ IPSEC_CONF_DST_ADDRESS, line_no);
+ return (-1);
+ }
+ old_style = B_TRUE;
+
+ if (daddr) {
+ error_message(DUP_ERROR,
+ IPSEC_CONF_DST_ADDRESS, line_no);
+ return (-1);
+ }
+ /*
+ * Use this to detect duplicates rather
+ * than 0 like other cases, because 0 for
+ * address means INADDR_ANY.
+ */
+ daddr = B_TRUE;
+ cptr->has_daddr = 1;
+ /*
+ * Advance to the string containing
+ * the address.
+ */
+ i++, line_no++;
+ if (act_props->pattern[i] == NULL) {
+ error_message(BAD_ERROR,
+ IPSEC_CONF_DST_ADDRESS, line_no);
+ return (-1);
+ }
+ if (parse_address(IPSEC_CONF_DST_ADDRESS,
+ act_props->pattern[i]) != 0) {
+ error_message(BAD_ERROR,
+ IPSEC_CONF_DST_ADDRESS, line_no);
+ return (-1);
+ }
+ if (!cptr->has_dmask)
+ cptr->has_dmask = has_daprefix;
+ break;
+
+ case TOK_sport:
+ if (new_style) {
+ error_message(BAD_ERROR,
+ IPSEC_CONF_SRC_PORT, line_no);
+ return (-1);
+ }
+ old_style = B_TRUE;
+
+ if (cptr->ips_src_port_min != 0) {
+ error_message(DUP_ERROR, IPSEC_CONF_SRC_PORT,
+ line_no);
+ return (-1);
+ }
+ i++, line_no++;
+ if (act_props->pattern[i] == NULL) {
+ error_message(BAD_ERROR, IPSEC_CONF_SRC_PORT,
+ line_no);
+ return (-1);
+ }
+ ret = parse_port(IPSEC_CONF_SRC_PORT,
+ act_props->pattern[i], cptr);
+ if (ret != 0) {
+ error_message(BAD_ERROR, IPSEC_CONF_SRC_PORT,
+ line_no);
+ return (-1);
+ }
+ break;
+ case TOK_dport:
+ if (new_style) {
+ error_message(BAD_ERROR,
+ IPSEC_CONF_DST_PORT, line_no);
+ return (-1);
+ }
+ old_style = B_TRUE;
+
+ if (cptr->ips_dst_port_min != 0) {
+ error_message(DUP_ERROR, IPSEC_CONF_DST_PORT,
+ line_no);
+ return (-1);
+ }
+ i++, line_no++;
+ if (act_props->pattern[i] == NULL) {
+ error_message(BAD_ERROR, IPSEC_CONF_DST_PORT,
+ line_no);
+ return (-1);
+ }
+ ret = parse_port(IPSEC_CONF_DST_PORT,
+ act_props->pattern[i],
+ cptr);
+ if (ret != 0) {
+ error_message(BAD_ERROR, IPSEC_CONF_DST_PORT,
+ line_no);
+ return (-1);
+ }
+ break;
+
+ case TOK_lport:
+ if (old_style) {
+ error_message(BAD_ERROR,
+ IPSEC_CONF_SRC_PORT, line_no);
+ return (-1);
+ }
+ new_style = B_TRUE;
+
+ if (cptr->ips_src_port_min != 0) {
+ error_message(DUP_ERROR, IPSEC_CONF_SRC_PORT,
+ line_no);
+ return (-1);
+ }
+ i++, line_no++;
+ if (act_props->pattern[i] == NULL) {
+ error_message(BAD_ERROR, IPSEC_CONF_SRC_PORT,
+ line_no);
+ return (-1);
+ }
+ ret = parse_port(IPSEC_CONF_SRC_PORT,
+ act_props->pattern[i],
+ cptr);
+ if (ret != 0) {
+ error_message(BAD_ERROR, IPSEC_CONF_SRC_PORT,
+ line_no);
+ return (-1);
+ }
+ break;
+
+ case TOK_rport:
+ if (old_style) {
+ error_message(BAD_ERROR,
+ IPSEC_CONF_DST_PORT, line_no);
+ return (-1);
+ }
+ new_style = B_TRUE;
+
+ if (cptr->ips_dst_port_min != 0) {
+ error_message(DUP_ERROR, IPSEC_CONF_DST_PORT,
+ line_no);
+ return (-1);
+ }
+ i++, line_no++;
+ if (act_props->pattern[i] == NULL) {
+ error_message(BAD_ERROR, IPSEC_CONF_DST_PORT,
+ line_no);
+ return (-1);
+ }
+ ret = parse_port(IPSEC_CONF_DST_PORT,
+ act_props->pattern[i],
+ cptr);
+ if (ret != 0) {
+ error_message(BAD_ERROR, IPSEC_CONF_DST_PORT,
+ line_no);
+ return (-1);
+ }
+ break;
+
+ case TOK_smask:
+ if (new_style) {
+ error_message(BAD_ERROR,
+ IPSEC_CONF_SRC_MASK, line_no);
+ return (-1);
+ }
+ old_style = B_TRUE;
+ cptr->has_smask = B_TRUE;
+
+ IN6_V4MAPPED_TO_INADDR(&cptr->ips_src_mask_v6, &mask);
+ if (mask.s_addr != 0) {
+ error_message(DUP_ERROR, IPSEC_CONF_SRC_MASK,
+ line_no);
+ return (-1);
+ }
+ i++, line_no++;
+ if (act_props->pattern[i] == NULL) {
+ error_message(BAD_ERROR, IPSEC_CONF_SRC_MASK,
+ line_no);
+ return (-1);
+ }
+ ret = parse_mask(IPSEC_CONF_SRC_MASK,
+ act_props->pattern[i],
+ cptr);
+ if (ret != 0) {
+ error_message(BAD_ERROR, IPSEC_CONF_SRC_MASK,
+ line_no);
+ return (-1);
+ }
+ break;
+ case TOK_dmask:
+ if (new_style) {
+ error_message(BAD_ERROR,
+ IPSEC_CONF_DST_MASK, line_no);
+ return (-1);
+ }
+ old_style = B_TRUE;
+ cptr->has_dmask = B_TRUE;
+
+ IN6_V4MAPPED_TO_INADDR(&cptr->ips_dst_mask_v6, &mask);
+ if (mask.s_addr != 0) {
+ error_message(DUP_ERROR, IPSEC_CONF_DST_MASK,
+ line_no);
+ return (-1);
+ }
+ i++, line_no++;
+ if (act_props->pattern[i] == NULL) {
+ error_message(BAD_ERROR, IPSEC_CONF_DST_MASK,
+ line_no);
+ return (-1);
+ }
+ ret = parse_mask(IPSEC_CONF_DST_MASK,
+ act_props->pattern[i],
+ cptr);
+ if (ret != 0) {
+ error_message(BAD_ERROR, IPSEC_CONF_DST_MASK,
+ line_no);
+ return (-1);
+ }
+ break;
+ case TOK_ulp:
+ if (cptr->ips_ulp_prot != 0) {
+ error_message(DUP_ERROR,
+ IPSEC_CONF_ULP, line_no);
+ return (-1);
+ }
+ i++, line_no++;
+ if (act_props->pattern[i] == NULL) {
+ error_message(BAD_ERROR,
+ IPSEC_CONF_ULP, line_no);
+ return (-1);
+ }
+ pent = getprotobyname(act_props->pattern[i]);
+ if (pent == NULL) {
+ int ulp;
+ ulp = parse_int(act_props->pattern[i]);
+ if (ulp == -1) {
+ error_message(BAD_ERROR,
+ IPSEC_CONF_ULP, line_no);
+ return (-1);
+ }
+ cptr->ips_ulp_prot = ulp;
+ } else {
+ cptr->ips_ulp_prot = pent->p_proto;
+ }
+ break;
+ case TOK_type:
+ if (cptr->has_type) {
+ error_message(DUP_ERROR,
+ IPSEC_CONF_ICMP_TYPE, line_no);
+ return (-1);
+ }
+
+ i++, line_no++;
+ type = parse_type_code(act_props->pattern[i],
+ icmp_type_table);
+
+ if (type > 65536 || type < 0) {
+ error_message(BAD_ERROR,
+ IPSEC_CONF_ICMP_TYPE, line_no);
+ return (-1);
+ }
+
+ type_end = type / 256;
+ type = type % 256;
+
+ if (type_end < type)
+ type_end = type;
+
+ cptr->has_type = 1;
+ cptr->ips_icmp_type = (uint8_t)type;
+ cptr->ips_icmp_type_end = (uint8_t)type_end;
+ break;
+ case TOK_code:
+ if (!cptr->has_type) {
+ error_message(BAD_ERROR,
+ IPSEC_CONF_ICMP_CODE, line_no);
+ return (-1);
+ }
+
+ if (cptr->has_code) {
+ error_message(DUP_ERROR,
+ IPSEC_CONF_ICMP_CODE, line_no);
+ return (-1);
+ }
+
+ i++, line_no++;
+
+ code = parse_type_code(act_props->pattern[i],
+ icmp_code_table);
+ if (type > 65536 || type < 0) {
+ error_message(BAD_ERROR,
+ IPSEC_CONF_ICMP_CODE, line_no);
+ return (-1);
+ }
+ code_end = code / 256;
+ code = code % 256;
+
+ if (code_end < code)
+ code_end = code;
+
+ cptr->has_code = 1;
+ cptr->ips_icmp_code = (uint8_t)code;
+ cptr->ips_icmp_code_end = (uint8_t)code_end;
+ break;
+ }
+ }
+
+ /*
+ * Get the actions.
+ */
+
+ for (ap_num = 0; act_props->ap[ap_num].act != NULL; ap_num++) {
+ ips_act_props_t *iap;
+
+ if (ap_num > 0) {
+ /* or's only with new style */
+ if (old_style) {
+ (void) printf("%s\n", gettext(
+ "or's only with new style"));
+ return (-1);
+ }
+ new_style = B_TRUE;
+ }
+
+ ipsec_aalg = ipsec_ealg = ipsec_eaalg = B_FALSE;
+ tok_count = 0;
+
+ for (k = 0; action_table[k].string; k++) {
+ if (strcmp(act_props->ap[ap_num].act,
+ action_table[k].string) == 0)
+ break;
+ }
+ /*
+ * The following thing should never happen as
+ * we have already tested for its validity in parse.
+ */
+ if (action_table[k].string == NULL) {
+ warnx(gettext("(form act)Invalid action on line "
+ "%d: %s"), (arg_indices[line_no] == 0) ? 1 :
+ arg_indices[line_no],
+ act_props->ap[ap_num].act);
+ warnx("%s", act_props->ap[ap_num].act);
+ return (-1);
+ }
+
+ /* we have a good action alloc an iap */
+ iap = alloc_iap(cptr);
+
+ iap->iap_action = action_table[k].value;
+ iap->iap_act_tok = action_table[k].tok_val;
+
+ switch (action_table[k].tok_val) {
+ case TOK_apply:
+ cptr->ips_dir = SPD_RULE_FLAG_OUTBOUND;
+ break;
+ case TOK_permit:
+ cptr->ips_dir = SPD_RULE_FLAG_INBOUND;
+ break;
+ case TOK_ipsec:
+ if (!dir)
+ cptr->ips_dir =
+ SPD_RULE_FLAG_INBOUND
+ |SPD_RULE_FLAG_OUTBOUND;
+ break;
+ case TOK_bypass:
+ /* do something? */
+ break;
+ }
+
+ line_no++;
+ /*
+ * Get the properties. NULL properties is not valid.
+ * Later checks will catch it.
+ */
+ for (i = 0; act_props->ap[ap_num].prop[i]; i++, line_no++) {
+ for (j = 0; property_table[j].string; j++) {
+ if (strcmp(act_props->ap[ap_num].prop[i],
+ property_table[j].string) == 0) {
+ break;
+ }
+ }
+ if (property_table[j].string == NULL) {
+ warnx(gettext("Invalid properties on line "
+ "%d: %s"),
+ (arg_indices[line_no] == 0) ?
+ 1 : arg_indices[line_no],
+ act_props->ap[ap_num].prop[i]);
+ return (-1);
+ }
+
+ iap->iap_attr_tok[tok_count++]
+ = property_table[j].value;
+
+ switch (property_table[j].value) {
+ case SPD_ATTR_AH_AUTH:
+ if (ipsec_aalg) {
+ error_message(DUP_ERROR,
+ IPSEC_CONF_IPSEC_AALGS, line_no);
+ return (-1);
+ }
+ i++, line_no++;
+ if (act_props->ap[ap_num].prop[i] == NULL) {
+ error_message(BAD_ERROR,
+ IPSEC_CONF_IPSEC_AALGS, line_no);
+ return (-1);
+ }
+ ret = parse_ipsec_alg(
+ act_props->ap[ap_num].prop[i],
+ iap, SPD_ATTR_AH_AUTH);
+ if (ret != 0) {
+ error_message(BAD_ERROR,
+ IPSEC_CONF_IPSEC_AALGS, line_no);
+ return (-1);
+ }
+ ipsec_aalg = B_TRUE;
+ break;
+ case SPD_ATTR_ESP_ENCR:
+ /*
+ * If this option was not given
+ * and encr_auth_algs was given,
+ * we provide null-encryption. We do the
+ * setting after we parse all the options.
+ */
+ if (ipsec_ealg) {
+ error_message(DUP_ERROR,
+ IPSEC_CONF_IPSEC_EALGS, line_no);
+ return (-1);
+ }
+ i++, line_no++;
+ if (act_props->ap[ap_num].prop[i] == NULL) {
+ error_message(BAD_ERROR,
+ IPSEC_CONF_IPSEC_EALGS, line_no);
+ return (-1);
+ }
+ ret = parse_ipsec_alg(
+ act_props->ap[ap_num].prop[i],
+ iap, SPD_ATTR_ESP_ENCR);
+ if (ret != 0) {
+ error_message(BAD_ERROR,
+ IPSEC_CONF_IPSEC_EALGS, line_no);
+ return (-1);
+ }
+ ipsec_ealg = B_TRUE;
+ break;
+ case SPD_ATTR_ESP_AUTH:
+ /*
+ * If this option was not given and encr_algs
+ * option was given, we still pass a default
+ * value in ipsc_esp_auth_algs. This is to
+ * encourage the use of authentication with
+ * ESP.
+ */
+ if (ipsec_eaalg) {
+ error_message(DUP_ERROR,
+ IPSEC_CONF_IPSEC_EAALGS, line_no);
+ return (-1);
+ }
+ i++, line_no++;
+ if (act_props->ap[ap_num].prop[i] == NULL) {
+ error_message(BAD_ERROR,
+ IPSEC_CONF_IPSEC_EAALGS, line_no);
+ return (-1);
+ }
+ ret = parse_ipsec_alg(
+ act_props->ap[ap_num].prop[i],
+ iap, SPD_ATTR_ESP_AUTH);
+ if (ret != 0) {
+ error_message(BAD_ERROR,
+ IPSEC_CONF_IPSEC_EAALGS, line_no);
+ return (-1);
+ }
+ ipsec_eaalg = B_TRUE;
+ break;
+ case IPS_SA:
+ i++, line_no++;
+ if (act_props->ap[ap_num].prop[i] == NULL) {
+ error_message(BAD_ERROR,
+ IPSEC_CONF_IPSEC_SA, line_no);
+ return (-1);
+ }
+
+ if (strcmp(act_props->ap[ap_num].prop[i],
+ "unique") == 0) {
+ iap->iap_attr |= SPD_APPLY_UNIQUE;
+ } else if (strcmp(act_props->ap[ap_num].prop[i],
+ "shared") != 0) {
+ /* "shared" is default. */
+ error_message(BAD_ERROR,
+ IPSEC_CONF_IPSEC_SA, line_no);
+ return (-1);
+ }
+
+ break;
+ case IPS_DIR:
+ if (dir) {
+ error_message(DUP_ERROR,
+ IPSEC_CONF_IPSEC_DIR, line_no);
+ return (-1);
+ }
+ if (new_style) {
+ error_message(BAD_ERROR,
+ IPSEC_CONF_IPSEC_DIR, line_no);
+ return (-1);
+ }
+ old_style = B_TRUE;
+ dir = B_TRUE;
+ i++, line_no++;
+ if (act_props->ap[ap_num].prop[i] == NULL) {
+ error_message(BAD_ERROR,
+ IPSEC_CONF_IPSEC_DIR, line_no);
+ return (-1);
+ }
+ if (strcmp(act_props->ap[ap_num].prop[i],
+ "out") == 0) {
+ cptr->ips_dir = SPD_RULE_FLAG_OUTBOUND;
+ } else if (strcmp(act_props->ap[ap_num].prop[i],
+ "in") == 0) {
+ cptr->ips_dir = SPD_RULE_FLAG_INBOUND;
+ } else {
+ error_message(BAD_ERROR,
+ IPSEC_CONF_IPSEC_DIR, line_no);
+ return (-1);
+ }
+ if ((cptr->ips_dir & SPD_RULE_FLAG_INBOUND) &&
+ iap->iap_act_tok == TOK_apply) {
+ warnx(gettext("Direction"
+ " in conflict with action"));
+ return (-1);
+ }
+ if ((cptr->ips_dir & SPD_RULE_FLAG_OUTBOUND) &&
+ iap->iap_act_tok == TOK_permit) {
+ warnx(gettext("Direction"
+ "in conflict with action"));
+ return (-1);
+ }
+
+ break;
+ }
+ }
+
+ if (!ipsec_ealg && ipsec_eaalg) {
+ /*
+ * If the user has specified the auth alg to be used
+ * with encryption and did not provide a encryption
+ * algorithm, provide null encryption.
+ */
+ iap->iap_eencr.alg_id = SADB_EALG_NULL;
+ ipsec_ealg = B_TRUE;
+ }
+
+ /* Set the level of IPSEC protection we want */
+ if (ipsec_aalg && (ipsec_ealg || ipsec_eaalg)) {
+ iap->iap_attr |= SPD_APPLY_AH|SPD_APPLY_ESP;
+ } else if (ipsec_aalg) {
+ iap->iap_attr |= SPD_APPLY_AH;
+ } else if (ipsec_ealg || ipsec_eaalg) {
+ iap->iap_attr |= SPD_APPLY_ESP;
+ }
+
+ /* convert src/dst to local/remote */
+ if (!new_style) {
+ switch (cptr->ips_acts->iap_act_tok) {
+ case TOK_apply:
+ /* outbound */
+ /* src=local, dst=remote */
+ /* this is ok. */
+ break;
+
+ case TOK_permit:
+ /* inbound */
+ /* src=remote, dst=local */
+ /* switch */
+ cptr->swap = 1;
+ break;
+ case TOK_bypass:
+ case TOK_drop:
+ /* check the direction for what to do */
+ if (cptr->ips_dir == SPD_RULE_FLAG_INBOUND)
+ cptr->swap = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ /* Validate the properties */
+ if (ret = validate_properties(iap, dir,
+ (ipsec_aalg || ipsec_ealg || ipsec_eaalg))) {
+ return (ret);
+ }
+ }
+
+ return (0);
+
+}
+
+static int
+print_cmd_buf(FILE *fp)
+{
+ *(cbuf + cbuf_offset) = '\0';
+
+ if (fp == stderr) {
+ if (errno == EEXIST) {
+ warnx(gettext("Command:\n%s"), cbuf);
+ } else {
+ warnx(gettext("Malformed command:\n%s"), cbuf);
+ }
+
+ } else {
+ if (fprintf(fp, "%s", cbuf) == -1) {
+ warn("fprintf");
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
+#ifdef DEBUG
+
+static uchar_t *
+addr_ptr(int isv4, struct in6_addr *addr6, struct in_addr *addr4)
+{
+ if (isv4) {
+ IN6_V4MAPPED_TO_INADDR(addr6, addr4);
+ return ((uchar_t *)&addr4->s_addr);
+ } else {
+ return ((uchar_t *)&addr6->s6_addr);
+ }
+}
+
+static void
+dump_algreq(const char *tag, algreq_t *alg)
+{
+ (void) printf("%s algid %d, bits %d..%d\n",
+ tag, alg->alg_id, alg->alg_minbits, alg->alg_maxbits);
+}
+
+static void
+dump_conf(ips_conf_t *conf)
+{
+ boolean_t isv4 = conf->ips_isv4;
+ struct in_addr addr;
+ char buf[INET6_ADDRSTRLEN];
+ int af;
+ ips_act_props_t *iap = conf->ips_acts;
+
+ af = isv4 ? AF_INET : AF_INET6;
+
+ (void) printf("Source Addr is %s\n",
+ inet_ntop(af, addr_ptr(isv4, &conf->ips_src_addr_v6, &addr),
+ buf, INET6_ADDRSTRLEN));
+
+ (void) printf("Dest Addr is %s\n",
+ inet_ntop(af, addr_ptr(isv4, &conf->ips_dst_addr_v6, &addr),
+ buf, INET6_ADDRSTRLEN));
+
+ (void) printf("Source Mask is %s\n",
+ inet_ntop(af, addr_ptr(isv4, &conf->ips_src_mask_v6, &addr),
+ buf, INET6_ADDRSTRLEN));
+
+ (void) printf("Dest Mask is %s\n",
+ inet_ntop(af, addr_ptr(isv4, &conf->ips_dst_mask_v6, &addr),
+ buf, INET6_ADDRSTRLEN));
+
+ (void) printf("Source port %d\n", conf->ips_src_port_min);
+ (void) printf("Dest port %d\n", conf->ips_dst_port_min);
+ (void) printf("ULP %d\n", conf->ips_ulp_prot);
+
+ (void) printf("ICMP type %d-%d code %d-%d", conf->ips_icmp_type,
+ conf->ips_icmp_type_end,
+ conf->ips_icmp_code,
+ conf->ips_icmp_code_end);
+
+ while (iap != NULL) {
+ (void) printf("------------------------------------\n");
+ (void) printf("IPSec act is %d\n", iap->iap_action);
+ (void) printf("IPSec attr is %d\n", iap->iap_attr);
+ dump_algreq("AH authentication", &iap->iap_aauth);
+ dump_algreq("ESP authentication", &iap->iap_eauth);
+ dump_algreq("ESP encryption", &iap->iap_eencr);
+ (void) printf("------------------------------------\n");
+ iap = iap->iap_next;
+ }
+
+ fflush(stdout);
+}
+#endif /* DEBUG */
+
+
+static int
+ipsec_conf_add(void)
+{
+ act_prop_t *act_props = malloc(sizeof (act_prop_t));
+ ips_conf_t conf;
+ FILE *fp, *policy_fp;
+ int ret, i, j;
+ char *warning = gettext(
+ "\tWARNING : New policy entries that are being added may\n "
+ "\taffect the existing connections. Existing connections\n"
+ "\tthat are not subjected to policy constraints, may be\n"
+ "\tsubjected to policy constraints because of the new\n"
+ "\tpolicy. This can disrupt the communication of the\n"
+ "\texisting connections.\n");
+
+ if (act_props == NULL) {
+ warn(gettext("memory"));
+ return (-1);
+ }
+
+ /* clone into standby DB */
+ ipsec_conf_admin(SPD_CLONE);
+
+ if (strcmp(filename, "-") == 0)
+ fp = stdin;
+ else
+ fp = fopen(filename, "r");
+
+ if (fp == NULL) {
+ warn(gettext("%s : Input file cannot be opened"), filename);
+ usage();
+ return (-1);
+ }
+ /*
+ * This will create the file if it does not exist.
+ * Make sure the umask is right.
+ */
+ (void) umask(0022);
+ policy_fp = fopen(POLICY_CONF_FILE, "a");
+ if (policy_fp == NULL) {
+ warn(gettext("%s cannot be opened"), POLICY_CONF_FILE);
+ return (-1);
+ }
+
+ if (!ipsecconf_qflag) {
+ (void) printf("%s", warning);
+ }
+
+ /*
+ * Pattern, action, and properties are allocated in
+ * parse_pattern_or_prop and in parse_action (called by
+ * parse_one) as we parse arguments.
+ */
+ while ((ret = parse_one(fp, act_props)) == 0) {
+
+ /*
+ * If there is no action and parse returned success,
+ * it means that there is nothing to add.
+ */
+
+ if (act_props->pattern[0] == NULL &&
+ act_props->ap[0].act == NULL)
+ break;
+
+ ret = form_ipsec_conf(act_props, &conf);
+ if (ret != 0) {
+ warnx(gettext("form_ipsec_conf error"));
+ break;
+ }
+
+ /*
+ * shp, dhp, splen, and dplen are globals set by
+ * form_ipsec_conf() while parsing the addresses.
+ */
+ if (shp == NULL && dhp == NULL) {
+ switch (do_port_adds(&conf)) {
+ case 0:
+ /* no error */
+ break;
+ case EEXIST:
+ /* duplicate entries, continue adds */
+ (void) print_cmd_buf(stderr);
+ goto next;
+ default:
+ /* other error, bail */
+ ret = -1;
+ goto bail;
+ }
+ } else {
+ ret = do_address_adds(&conf);
+ switch (ret) {
+ case 0:
+ /* no error. */
+ break;
+ case EEXIST:
+ (void) print_cmd_buf(stderr);
+ goto next;
+ case EBUSY:
+ warnx(gettext(
+ "Can't set mask and /NN prefix."));
+ ret = -1;
+ break;
+ case EINVAL:
+ warnx(gettext("Can't set /NN"
+ " prefix on multi-host name."));
+ ret = -1;
+ break;
+ case ERANGE:
+ warnx(gettext("/NN prefix is too big!"));
+ ret = -1;
+ break;
+ case ESRCH:
+ warnx(gettext("No matching IPv4 or "
+ "IPv6 saddr/daddr pairs"));
+ ret = -1;
+ break;
+ default:
+ /* Should never get here. */
+ errno = ret;
+ warn(gettext("Misc. error"));
+ ret = -1;
+ }
+ if (ret == -1)
+ goto bail;
+ }
+
+ /*
+ * The # should help re-using the ipsecpolicy.conf
+ * for input again as # will be treated as comment.
+ */
+ if (fprintf(policy_fp, "%s %lld \n", INDEX_TAG,
+ conf.ips_policy_index) == -1) {
+ warn("fprintf");
+ warnx(gettext("Addition incomplete, Please "
+ "flush all the entries and re-configure :"));
+ reconfigure();
+ ret = -1;
+ break;
+ }
+ if (print_cmd_buf(policy_fp) == -1) {
+ warnx(gettext("Addition incomplete. Please "
+ "flush all the entries and re-configure :"));
+ reconfigure();
+ ret = -1;
+ break;
+ }
+ /*
+ * We add one newline by default to separate out the
+ * entries. If the last character is not a newline, we
+ * insert a newline for free. This makes sure that all
+ * entries look consistent in the file.
+ */
+ if (*(cbuf + cbuf_offset - 1) == '\n') {
+ if (fprintf(policy_fp, "\n") == -1) {
+ warn("fprintf");
+ warnx(gettext("Addition incomplete. "
+ "Please flush all the entries and "
+ "re-configure :"));
+ reconfigure();
+ ret = -1;
+ break;
+ }
+ } else {
+ if (fprintf(policy_fp, "\n\n") == -1) {
+ warn("fprintf");
+ warnx(gettext("Addition incomplete. "
+ "Please flush all the entries and "
+ "re-configure :"));
+ reconfigure();
+ ret = -1;
+ break;
+ }
+ }
+ /*
+ * Make sure this gets to the disk before
+ * we parse the next entry.
+ */
+ (void) fflush(policy_fp);
+next:
+ for (i = 0; act_props->pattern[i] != NULL; i++)
+ free(act_props->pattern[i]);
+ for (j = 0; act_props->ap[j].act != NULL; j++) {
+ free(act_props->ap[j].act);
+ for (i = 0; act_props->ap[j].prop[i] != NULL; i++)
+ free(act_props->ap[j].prop[i]);
+ }
+ }
+bail:
+ if (ret == -1) {
+ (void) print_cmd_buf(stderr);
+ for (i = 0; act_props->pattern[i] != NULL; i++)
+ free(act_props->pattern[i]);
+ for (j = 0; act_props->ap[j].act != NULL; j++) {
+ free(act_props->ap[j].act);
+ for (i = 0; act_props->ap[j].prop[i] != NULL; i++)
+ free(act_props->ap[j].prop[i]);
+ }
+ }
+#ifdef DEBUG_HEAVY
+ (void) printf("ipsec_conf_add: ret val = %d\n", ret);
+ fflush(stdout);
+#endif
+ /* looks good, flip it in */
+ if (ret == 0)
+ ipsec_conf_admin(SPD_FLIP);
+ else
+ nuke_adds();
+
+ return (ret);
+}
+
+
+static int
+ipsec_conf_sub()
+{
+ act_prop_t *act_props = malloc(sizeof (act_prop_t));
+ FILE *remove_fp, *policy_fp;
+ char rbuf[MAXLEN], pbuf[MAXLEN], /* remove buffer, and policy buffer */
+ *warning = gettext(
+ "\tWARNING: Policy entries that are being removed may\n"
+ "\taffect the existing connections. Existing connections\n"
+ "\tthat are subjeced to policy constraints may no longer\n"
+ "\tbe subjected to policy contraints because of its\n"
+ "\tremoval. This can compromise security, and disrupt\n"
+ "\tthe communication of the existing connection.\n"
+ "\tConnections that are latched will remain unaffected\n"
+ "\tuntil they close.\n");
+ int ret = 0;
+ int index_len, pindex = 0; /* init value in case of pfile error */
+
+ if (act_props == NULL) {
+ warn(gettext("memory"));
+ return (-1);
+ }
+
+ /* clone into standby DB */
+ (void) ipsec_conf_admin(SPD_CLONE);
+
+ if (strcmp(filename, "-") == 0)
+ remove_fp = stdin;
+ else
+ remove_fp = fopen(filename, "r");
+
+ if (remove_fp == NULL) {
+ warn(gettext("%s : Input file cannot be opened"), filename);
+ usage();
+ free(act_props);
+ return (-1);
+ }
+
+ /* open policy file so we can locate the correct policy */
+ (void) umask(0022); /* in case it gets created! */
+ policy_fp = fopen(POLICY_CONF_FILE, "r+");
+ if (policy_fp == NULL) {
+ warn(gettext("%s cannot be opened"), POLICY_CONF_FILE);
+ (void) fclose(remove_fp);
+ free(act_props);
+ return (-1);
+ }
+
+ /* don't print the warning if we're in q[uiet] mode */
+ if (!ipsecconf_qflag)
+ (void) printf("%s", warning);
+
+ /* this bit is done primarily so we can read what we write */
+ index_len = strlen(INDEX_TAG);
+
+ /*
+ * We want to look for the policy in rbuf in the policy file.
+ * Go through the list of policies to remove, locating each one.
+ */
+ while (fgets(rbuf, MAXLEN, remove_fp) != NULL) {
+ char *buf;
+ int offset, prev_offset, prev_prev_offset, nlines;
+ fpos_t ipos;
+ int pbuf_len = 0;
+ char *tmp;
+ /* skip blanks here (so we don't need to do it below)! */
+ for (tmp = rbuf; (*tmp != '\0') && isspace(*tmp); tmp++);
+ if (*tmp == '\0')
+ continue;
+
+ /* skip the INDEX_TAG lines in the remove buffer */
+ if (strncasecmp(rbuf, INDEX_TAG, index_len) == 0)
+ continue;
+
+ /* skip commented lines */
+ if (*tmp == '#')
+ continue;
+
+ /*
+ * We start by presuming only good policies are in the pfile,
+ * and so only good policies from the rfile will match them.
+ * ipsec_conf_del ensures this later by calling parse_one() on
+ * pfile before it deletes the entry.
+ */
+ for (offset = prev_offset = prev_prev_offset = 0;
+ fgets(pbuf, MAXLEN, policy_fp) != NULL;
+ offset += pbuf_len) {
+ prev_offset = offset;
+ pbuf_len = strlen(pbuf);
+
+ /* skip blank lines which seperate policy entries */
+ if (pbuf[0] == '\n')
+ continue;
+
+ /* if we found an index, save it */
+ if (strncasecmp(pbuf, INDEX_TAG, index_len) == 0) {
+ buf = pbuf + index_len;
+ buf++;
+ if ((pindex = parse_int(buf)) == -1) {
+ /* bad index, we can't continue */
+ warnx(gettext(
+ "Invalid index in the file"));
+ (void) fclose(remove_fp);
+ (void) fclose(policy_fp);
+ free(act_props);
+ return (-1);
+ }
+
+ /* save this position in case it's the one */
+ if (fgetpos(policy_fp, &ipos) != 0) {
+ (void) fclose(remove_fp);
+ (void) fclose(policy_fp);
+ free(act_props);
+ return (-1);
+ }
+ }
+
+ /* Does pbuf contain the remove policy? */
+ if (strncasecmp(rbuf, pbuf, pbuf_len) == 0) {
+ /* we found the one to remove! */
+ if (pindex == 0) {
+ warnx(gettext("Didn't find a valid "
+ "index for policy"));
+ (void) fclose(remove_fp);
+ (void) fclose(policy_fp);
+ free(act_props);
+ return (-1);
+ }
+
+ /* off it - back up to the last INDEX! */
+ if (fsetpos(policy_fp, &ipos) != 0) {
+ (void) fclose(remove_fp);
+ (void) fclose(policy_fp);
+ free(act_props);
+ return (-1);
+ }
+
+ /* parse_one sets linecount = #lines to off */
+ if (parse_one(policy_fp, act_props) == -1) {
+ warnx(gettext("Invalid policy entry "
+ "in the file"));
+ (void) fclose(remove_fp);
+ (void) fclose(policy_fp);
+ free(act_props);
+ return (-1);
+ }
+
+ nlines = linecount + 2;
+ goto delete;
+ }
+ /*
+ * When we find a match, we want to pass the offset
+ * of the line that is before it - the INDEX_TAG line.
+ */
+ prev_prev_offset = prev_offset;
+ }
+ /* Didn't find a match - look at the next remove policy */
+ continue;
+
+delete:
+ (void) fclose(policy_fp);
+
+ if (delete_from_file(prev_prev_offset, nlines) != 0) {
+ warnx(gettext("delete_from_file failure. "
+ "Please flush all entries and re-configure :"));
+ reconfigure();
+ (void) fclose(remove_fp);
+ free(act_props);
+ return (-1);
+ }
+
+ if (pfp_delete_rule(pindex) != 0) {
+ warnx(gettext("Deletion incomplete. Please flush"
+ "all the entries and re-configure :"));
+ reconfigure();
+ (void) fclose(remove_fp);
+ free(act_props);
+ return (-1);
+ }
+
+ /* reset the globals */
+ linecount = 0;
+ pindex = 0;
+
+ /* reopen for next pass, automagically starting over. */
+ policy_fp = fopen(POLICY_CONF_FILE, "r");
+ if (policy_fp == NULL) {
+ warn(gettext("%s cannot be re-opened, can't continue"),
+ POLICY_CONF_FILE);
+ (void) fclose(remove_fp);
+ free(act_props);
+ return (-1);
+ }
+
+ } /* read next remove policy */
+
+ if ((ret = pfp_delete_rule(pindex)) != 0) {
+ warnx(gettext("Removal incomplete. Please flush "
+ "all the entries and re-configure :"));
+ reconfigure();
+ free(act_props);
+ return (ret);
+ }
+
+ /* nothing left to look for */
+ (void) fclose(remove_fp);
+ free(act_props);
+
+ return (0);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/ipseckey.c b/usr/src/cmd/cmd-inet/usr.sbin/ipseckey.c
new file mode 100644
index 0000000000..91ab7edd7c
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/ipseckey.c
@@ -0,0 +1,3896 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * NOTE:I'm trying to use "struct sadb_foo" instead of "sadb_foo_t"
+ * as a maximal PF_KEY portability test.
+ *
+ * Also, this is a deliberately single-threaded app, also for portability
+ * to systems without POSIX threads.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/sysmacros.h>
+#include <sys/fcntl.h>
+#include <net/pfkeyv2.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/uio.h>
+
+#include <syslog.h>
+#include <signal.h>
+#include <unistd.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <errno.h>
+#include <libintl.h>
+#include <locale.h>
+#include <fcntl.h>
+#include <strings.h>
+#include <ctype.h>
+
+#include <ipsec_util.h>
+
+static char numprint[NBUF_SIZE];
+static int keysock;
+static uint32_t seq;
+static pid_t mypid;
+static boolean_t vflag = B_FALSE; /* Verbose? */
+
+#define MAX_GET_SIZE 1024
+/* Defined as a uint64_t array for alignment purposes. */
+static uint64_t get_buffer[MAX_GET_SIZE];
+
+/* local prototypes */
+static const char *do_inet_ntop(const void *, char *, size_t);
+static void printsatime(int64_t, const char *, const char *, const char *);
+
+/*
+ * When something syntactically bad happens while reading commands,
+ * print it. For command line, exit. For reading from a file, exit, and
+ * print the offending line number. For interactive, just print the error
+ * and reset the program state with the longjmp().
+ */
+static void
+usage(void)
+{
+ if (readfile) {
+ warnx(gettext("Parse error on line %u."), lineno);
+ }
+ if (!interactive) {
+ (void) fprintf(stderr, gettext("Usage:\t"
+ "ipseckey [ -nvp ] | cmd [sa_type] [extfield value]*\n"));
+ (void) fprintf(stderr,
+ gettext("\tipseckey [ -nvp ] -f infile\n"));
+ (void) fprintf(stderr,
+ gettext("\tipseckey [ -nvp ] -s outfile\n"));
+ exit(1);
+ } else {
+ longjmp(env, 1);
+ }
+}
+
+/*
+ * Initialize a PF_KEY base message.
+ */
+static void
+msg_init(struct sadb_msg *msg, uint8_t type, uint8_t satype)
+{
+ msg->sadb_msg_version = PF_KEY_V2;
+ msg->sadb_msg_type = type;
+ msg->sadb_msg_errno = 0;
+ msg->sadb_msg_satype = satype;
+ /* For starters... */
+ msg->sadb_msg_len = SADB_8TO64(sizeof (*msg));
+ msg->sadb_msg_reserved = 0;
+ msg->sadb_msg_seq = ++seq;
+ msg->sadb_msg_pid = mypid;
+}
+
+/*
+ * parseXXX and rparseXXX commands parse input and convert them to PF_KEY
+ * field values, or do the reverse for the purposes of saving the SA tables.
+ * (See the save_XXX functions.)
+ */
+
+#define CMD_NONE 0
+#define CMD_UPDATE 2
+#define CMD_ADD 3
+#define CMD_DELETE 4
+#define CMD_GET 5
+#define CMD_FLUSH 9
+#define CMD_DUMP 10
+#define CMD_MONITOR 11
+#define CMD_PMONITOR 12
+#define CMD_QUIT 13
+#define CMD_SAVE 14
+#define CMD_HELP 15
+
+/*
+ * Parse the command.
+ */
+static int
+parsecmd(char *cmdstr)
+{
+ static struct cmdtable {
+ char *cmd;
+ int token;
+ } table[] = {
+ /*
+ * Q: Do we want to do GETSPI?
+ * A: No, it's for automated key mgmt. only. Either that,
+ * or it isn't relevant until we support non IPsec SA types.
+ */
+ {"update", CMD_UPDATE},
+ {"add", CMD_ADD},
+ {"delete", CMD_DELETE},
+ {"get", CMD_GET},
+ /*
+ * Q: And ACQUIRE and REGISTER and EXPIRE?
+ * A: not until we support non IPsec SA types.
+ */
+ {"flush", CMD_FLUSH},
+ {"dump", CMD_DUMP},
+ {"monitor", CMD_MONITOR},
+ {"passive_monitor", CMD_PMONITOR},
+ {"pmonitor", CMD_PMONITOR},
+ {"quit", CMD_QUIT},
+ {"exit", CMD_QUIT},
+ {"save", CMD_SAVE},
+ {"help", CMD_HELP},
+ {"?", CMD_HELP},
+ {NULL, CMD_NONE}
+ };
+ struct cmdtable *ct = table;
+
+ while (ct->cmd != NULL && strcmp(ct->cmd, cmdstr) != 0)
+ ct++;
+ return (ct->token);
+}
+
+/*
+ * Convert a number from a command line. I picked "u_longlong_t" for the
+ * number because we need the largest number available. Also, the strto<num>
+ * calls don't deal in units of uintNN_t.
+ */
+static u_longlong_t
+parsenum(char *num, boolean_t bail)
+{
+ u_longlong_t rc;
+ char *end = NULL;
+
+ if (num == NULL) {
+ warnx(gettext("Unexpected end of command line."));
+ usage();
+ }
+
+ errno = 0;
+ rc = strtoull(num, &end, 0);
+ if (errno != 0 || end == num || *end != '\0') {
+ if (bail) {
+ /* Errno message printed by warn(). */
+ warn(gettext("Expecting a number, but got"));
+ usage();
+ } else {
+ /*
+ * -1, while not optimal, is sufficiently out of range
+ * for most of this function's applications when
+ * we don't just bail.
+ */
+ return ((u_longlong_t)-1);
+ }
+ }
+
+ return (rc);
+}
+
+/*
+ * Parse and reverse parse a specific SA type (AH, ESP, etc.).
+ */
+static struct typetable {
+ char *type;
+ int token;
+} type_table[] = {
+ {"all", SADB_SATYPE_UNSPEC},
+ {"ah", SADB_SATYPE_AH},
+ {"esp", SADB_SATYPE_ESP},
+ /* PF_KEY NOTE: More to come if net/pfkeyv2.h gets updated. */
+ {NULL, 0} /* Token value is irrelevant for this entry. */
+};
+
+static char *
+rparsesatype(int type)
+{
+ struct typetable *tt = type_table;
+
+ while (tt->type != NULL && type != tt->token)
+ tt++;
+
+ if (tt->type == NULL) {
+ (void) snprintf(numprint, NBUF_SIZE, "%d", type);
+ } else {
+ return (tt->type);
+ }
+
+ return (numprint);
+}
+
+static int
+parsesatype(char *type)
+{
+ struct typetable *tt = type_table;
+
+ if (type == NULL)
+ return (SADB_SATYPE_UNSPEC);
+
+ while (tt->type != NULL && strcasecmp(tt->type, type) != 0)
+ tt++;
+
+ /*
+ * New SA types (including ones keysock maintains for user-land
+ * protocols) may be added, so parse a numeric value if possible.
+ */
+ if (tt->type == NULL) {
+ tt->token = (int)parsenum(type, B_FALSE);
+ if (tt->token == -1) {
+ warnx(gettext("Unknown SA type (%s)."), type);
+ usage();
+ }
+ }
+
+ return (tt->token);
+}
+
+#define NEXTEOF 0
+#define NEXTNONE 1
+#define NEXTNUM 2
+#define NEXTSTR 3
+#define NEXTNUMSTR 4
+#define NEXTADDR 5
+#define NEXTHEX 6
+#define NEXTIDENT 7
+#define NEXTADDR4 8
+#define NEXTADDR6 9
+
+#define TOK_EOF 0
+#define TOK_UNKNOWN 1
+#define TOK_SPI 2
+#define TOK_REPLAY 3
+#define TOK_STATE 4
+#define TOK_AUTHALG 5
+#define TOK_ENCRALG 6
+#define TOK_FLAGS 7
+#define TOK_SOFT_ALLOC 8
+#define TOK_SOFT_BYTES 9
+#define TOK_SOFT_ADDTIME 10
+#define TOK_SOFT_USETIME 11
+#define TOK_HARD_ALLOC 12
+#define TOK_HARD_BYTES 13
+#define TOK_HARD_ADDTIME 14
+#define TOK_HARD_USETIME 15
+#define TOK_CURRENT_ALLOC 16
+#define TOK_CURRENT_BYTES 17
+#define TOK_CURRENT_ADDTIME 18
+#define TOK_CURRENT_USETIME 19
+#define TOK_SRCADDR 20
+#define TOK_DSTADDR 21
+#define TOK_PROXYADDR 22
+#define TOK_AUTHKEY 23
+#define TOK_ENCRKEY 24
+#define TOK_SRCIDTYPE 25
+#define TOK_DSTIDTYPE 26
+#define TOK_DPD 27
+#define TOK_SENS_LEVEL 28
+#define TOK_SENS_MAP 29
+#define TOK_INTEG_LEVEL 30
+#define TOK_INTEG_MAP 31
+#define TOK_SRCADDR6 32
+#define TOK_DSTADDR6 33
+#define TOK_PROXYADDR6 34
+#define TOK_SRCPORT 35
+#define TOK_DSTPORT 36
+#define TOK_PROTO 37
+#define TOK_ENCAP 38
+#define TOK_NATLOC 39
+#define TOK_NATREM 40
+#define TOK_NATLPORT 41
+#define TOK_NATRPORT 42
+
+static struct toktable {
+ char *string;
+ int token;
+ int next;
+} tokens[] = {
+ /* "String", token value, next arg is */
+ {"spi", TOK_SPI, NEXTNUM},
+ {"replay", TOK_REPLAY, NEXTNUM},
+ {"state", TOK_STATE, NEXTNUMSTR},
+ {"auth_alg", TOK_AUTHALG, NEXTNUMSTR},
+ {"authalg", TOK_AUTHALG, NEXTNUMSTR},
+ {"encr_alg", TOK_ENCRALG, NEXTNUMSTR},
+ {"encralg", TOK_ENCRALG, NEXTNUMSTR},
+ {"flags", TOK_FLAGS, NEXTNUM},
+ {"soft_alloc", TOK_SOFT_ALLOC, NEXTNUM},
+ {"soft_bytes", TOK_SOFT_BYTES, NEXTNUM},
+ {"soft_addtime", TOK_SOFT_ADDTIME, NEXTNUM},
+ {"soft_usetime", TOK_SOFT_USETIME, NEXTNUM},
+ {"hard_alloc", TOK_HARD_ALLOC, NEXTNUM},
+ {"hard_bytes", TOK_HARD_BYTES, NEXTNUM},
+ {"hard_addtime", TOK_HARD_ADDTIME, NEXTNUM},
+ {"hard_usetime", TOK_HARD_USETIME, NEXTNUM},
+ {"current_alloc", TOK_CURRENT_ALLOC, NEXTNUM},
+ {"current_bytes", TOK_CURRENT_BYTES, NEXTNUM},
+ {"current_addtime", TOK_CURRENT_ADDTIME, NEXTNUM},
+ {"current_usetime", TOK_CURRENT_USETIME, NEXTNUM},
+
+ {"saddr", TOK_SRCADDR, NEXTADDR},
+ {"srcaddr", TOK_SRCADDR, NEXTADDR},
+ {"src", TOK_SRCADDR, NEXTADDR},
+ {"daddr", TOK_DSTADDR, NEXTADDR},
+ {"dstaddr", TOK_DSTADDR, NEXTADDR},
+ {"dst", TOK_DSTADDR, NEXTADDR},
+ {"proxyaddr", TOK_PROXYADDR, NEXTADDR},
+ {"proxy", TOK_PROXYADDR, NEXTADDR},
+
+ {"sport", TOK_SRCPORT, NEXTNUM},
+ {"dport", TOK_DSTPORT, NEXTNUM},
+ {"proto", TOK_PROTO, NEXTNUM},
+
+ {"saddr6", TOK_SRCADDR6, NEXTADDR},
+ {"srcaddr6", TOK_SRCADDR6, NEXTADDR},
+ {"src6", TOK_SRCADDR6, NEXTADDR},
+ {"daddr6", TOK_DSTADDR6, NEXTADDR},
+ {"dstaddr6", TOK_DSTADDR6, NEXTADDR},
+ {"dst6", TOK_DSTADDR6, NEXTADDR},
+ {"proxyaddr6", TOK_PROXYADDR6, NEXTADDR},
+ {"proxy6", TOK_PROXYADDR6, NEXTADDR},
+
+ {"authkey", TOK_AUTHKEY, NEXTHEX},
+ {"encrkey", TOK_ENCRKEY, NEXTHEX},
+ {"srcidtype", TOK_SRCIDTYPE, NEXTIDENT},
+ {"dstidtype", TOK_DSTIDTYPE, NEXTIDENT},
+ {"dpd", TOK_DPD, NEXTNUM},
+ {"sens_level", TOK_SENS_LEVEL, NEXTNUM},
+ {"sens_map", TOK_SENS_MAP, NEXTHEX},
+ {"integ_level", TOK_INTEG_LEVEL, NEXTNUM},
+ {"integ_map", TOK_INTEG_MAP, NEXTHEX},
+ {"nat_loc", TOK_NATLOC, NEXTADDR},
+ {"nat_rem", TOK_NATREM, NEXTADDR},
+ {"nat_lport", TOK_NATLPORT, NEXTNUM},
+ {"nat_rport", TOK_NATRPORT, NEXTNUM},
+ {"encap", TOK_ENCAP, NEXTNUMSTR},
+ {NULL, TOK_UNKNOWN, NEXTEOF}
+};
+
+/*
+ * Q: Do I need stuff for proposals, combinations, supported algorithms,
+ * or SPI ranges?
+ *
+ * A: Probably not, but you never know.
+ *
+ * Parse out extension header type values.
+ */
+static int
+parseextval(char *value, int *next)
+{
+ struct toktable *tp;
+
+ if (value == NULL)
+ return (TOK_EOF);
+
+ for (tp = tokens; tp->string != NULL; tp++)
+ if (strcmp(value, tp->string) == 0)
+ break;
+
+ /*
+ * Since the OS controls what extensions are available, we don't have
+ * to parse numeric values here.
+ */
+
+ *next = tp->next;
+ return (tp->token);
+}
+
+/*
+ * Parse possible state values.
+ */
+static uint8_t
+parsestate(char *state)
+{
+ struct states {
+ char *state;
+ uint8_t retval;
+ } states[] = {
+ {"larval", SADB_SASTATE_LARVAL},
+ {"mature", SADB_SASTATE_MATURE},
+ {"dying", SADB_SASTATE_DYING},
+ {"dead", SADB_SASTATE_DEAD},
+ {NULL, 0}
+ };
+ struct states *sp;
+
+ if (state == NULL) {
+ warnx(gettext("Unexpected end of command line."));
+ usage();
+ }
+
+ for (sp = states; sp->state != NULL; sp++) {
+ if (strcmp(sp->state, state) == 0)
+ return (sp->retval);
+ }
+ warnx(gettext("Unknown state type %s."), state);
+ usage();
+ /* NOTREACHED */
+}
+
+/*
+ * Return a string containing the name of the specified numerical algorithm
+ * identifier.
+ */
+static char *
+rparsealg(uint8_t alg, int proto_num)
+{
+ static struct ipsecalgent *holder = NULL; /* we're single-threaded */
+
+ if (holder != NULL)
+ freeipsecalgent(holder);
+
+ holder = getipsecalgbynum(alg, proto_num, NULL);
+ if (holder == NULL) {
+ (void) snprintf(numprint, NBUF_SIZE, "%d", alg);
+ return (numprint);
+ }
+
+ return (*(holder->a_names));
+}
+
+/*
+ * Return the numerical algorithm identifier corresponding to the specified
+ * algorithm name.
+ */
+static uint8_t
+parsealg(char *alg, int proto_num)
+{
+ u_longlong_t invalue;
+ struct ipsecalgent *algent;
+
+ if (alg == NULL) {
+ warnx(gettext("Unexpected end of command line."));
+ usage();
+ }
+
+ algent = getipsecalgbyname(alg, proto_num, NULL);
+ if (algent != NULL) {
+ uint8_t alg_num;
+
+ alg_num = algent->a_alg_num;
+ freeipsecalgent(algent);
+
+ return (alg_num);
+ }
+
+ /*
+ * Since algorithms can be loaded during kernel run-time, check for
+ * numeric algorithm values too. PF_KEY can catch bad ones with EINVAL.
+ */
+ invalue = parsenum(alg, B_FALSE);
+ if (invalue != (u_longlong_t)-1 &&
+ (u_longlong_t)(invalue & (u_longlong_t)0xff) == invalue)
+ return ((uint8_t)invalue);
+
+ if (proto_num == IPSEC_PROTO_ESP)
+ warnx(gettext("Unknown encryption algorithm type %s."), alg);
+ else
+ warnx(gettext("Unknown authentication algorithm type %s."),
+ alg);
+ usage();
+ /* NOTREACHED */
+}
+
+/*
+ * Parse and reverse parse out a source/destination ID type.
+ */
+static struct idtypes {
+ char *idtype;
+ uint8_t retval;
+} idtypes[] = {
+ {"prefix", SADB_IDENTTYPE_PREFIX},
+ {"fqdn", SADB_IDENTTYPE_FQDN},
+ {"domain", SADB_IDENTTYPE_FQDN},
+ {"domainname", SADB_IDENTTYPE_FQDN},
+ {"user_fqdn", SADB_IDENTTYPE_USER_FQDN},
+ {"mailbox", SADB_IDENTTYPE_USER_FQDN},
+ {"der_dn", SADB_X_IDENTTYPE_DN},
+ {"der_gn", SADB_X_IDENTTYPE_GN},
+ {NULL, 0}
+};
+
+static char *
+rparseidtype(uint16_t type)
+{
+ struct idtypes *idp;
+
+ for (idp = idtypes; idp->idtype != NULL; idp++) {
+ if (type == idp->retval)
+ return (idp->idtype);
+ }
+
+ (void) snprintf(numprint, NBUF_SIZE, "%d", type);
+ return (numprint);
+}
+
+static uint16_t
+parseidtype(char *type)
+{
+ struct idtypes *idp;
+ u_longlong_t invalue;
+
+ if (type == NULL) {
+ /* Shouldn't reach here, see callers for why. */
+ warnx(gettext("Unexpected end of command line."));
+ usage();
+ }
+
+ for (idp = idtypes; idp->idtype != NULL; idp++) {
+ if (strcasecmp(idp->idtype, type) == 0)
+ return (idp->retval);
+ }
+ /*
+ * Since identity types are almost arbitrary, check for numeric
+ * algorithm values too. PF_KEY can catch bad ones with EINVAL.
+ */
+ invalue = parsenum(type, B_FALSE);
+ if (invalue != (u_longlong_t)-1 &&
+ (u_longlong_t)(invalue & (u_longlong_t)0xffff) == invalue)
+ return ((uint16_t)invalue);
+
+
+ warnx(gettext("Unknown identity type %s."), type);
+ usage();
+ /* NOTREACHED */
+}
+
+/*
+ * Parse an address off the command line. Return length of sockaddr,
+ * and either return a hostent pointer (caller frees). The new
+ * getipnodebyname() call does the Right Thing (TM), even with
+ * raw addresses (colon-separated IPv6 or dotted decimal IPv4).
+ */
+
+static struct {
+ struct hostent he;
+ char *addtl[2];
+ } dummy;
+static union {
+ struct in6_addr ipv6;
+ struct in_addr ipv4;
+ uint64_t aligner;
+} addr1;
+
+static int
+parseaddr(char *addr, struct hostent **hpp, boolean_t v6only)
+{
+ int hp_errno;
+ struct hostent *hp = NULL;
+
+ if (addr == NULL) {
+ warnx(gettext("Unexpected end of command line."));
+ usage();
+ }
+
+ if (!nflag) {
+ /*
+ * Try name->address first. Assume AF_INET6, and
+ * get IPv4's, plus IPv6's if and only if IPv6 is configured.
+ * This means to add IPv6 SAs, you must have IPv6
+ * up-and-running. (AI_DEFAULT works here.)
+ */
+ hp = getipnodebyname(addr, AF_INET6,
+ (v6only ? AI_ADDRCONFIG : (AI_DEFAULT | AI_ALL)),
+ &hp_errno);
+ } else {
+ /*
+ * Try a normal address conversion only. Use "dummy"
+ * to construct a fake hostent. Caller will know not
+ * to free this one.
+ */
+ if (inet_pton(AF_INET6, addr, &addr1) == 1) {
+ dummy.he.h_addr_list = dummy.addtl;
+ dummy.addtl[0] = (char *)&addr1;
+ dummy.addtl[1] = NULL;
+ hp = &dummy.he;
+ dummy.he.h_addrtype = AF_INET6;
+ dummy.he.h_length = sizeof (struct in6_addr);
+ } else if (inet_pton(AF_INET, addr, &addr1) == 1) {
+ /*
+ * Remape to AF_INET6 anyway.
+ */
+ dummy.he.h_addr_list = dummy.addtl;
+ dummy.addtl[0] = (char *)&addr1;
+ dummy.addtl[1] = NULL;
+ hp = &dummy.he;
+ dummy.he.h_addrtype = AF_INET6;
+ dummy.he.h_length = sizeof (struct in6_addr);
+ /*
+ * NOTE: If macro changes to disallow in-place
+ * conversion, rewhack this.
+ */
+ IN6_INADDR_TO_V4MAPPED(&addr1.ipv4, &addr1.ipv6);
+ } else {
+ hp = NULL;
+ }
+ }
+
+ if (hp == NULL) {
+ warnx(gettext("Unknown address %s."), addr);
+ usage();
+ }
+
+ *hpp = hp;
+ /* Always return sockaddr_storage for now. */
+ return (sizeof (struct sockaddr_storage));
+}
+
+/*
+ * Parse a hex character for a key. A string will take the form:
+ * xxxxxxxxx/nn
+ * where
+ * xxxxxxxxx == a string of hex characters ([0-9][a-f][A-F])
+ * nn == an optional decimal "mask". If it is not present, it
+ * is assumed that the hex string will be rounded to the nearest
+ * byte, where odd nibbles, like 123 will become 0x0123.
+ *
+ * NOTE:Unlike the expression of IP addresses, I will not allow an
+ * excessive "mask". For example 2112/50 is very illegal.
+ * NOTE2: This key should be in canonical order. Consult your man
+ * pages per algorithm about said order.
+ */
+
+#define hd2num(hd) (((hd) >= '0' && (hd) <= '9') ? ((hd) - '0') : \
+ (((hd) >= 'a' && (hd) <= 'f') ? ((hd) - 'a' + 10) : ((hd) - 'A' + 10)))
+
+static struct sadb_key *
+parsekey(char *input)
+{
+ struct sadb_key *retval;
+ uint_t i, hexlen = 0, bits, alloclen;
+ uint8_t *key;
+
+ if (input == NULL) {
+ warnx(gettext("Unexpected end of command line."));
+ usage();
+ }
+
+ for (i = 0; input[i] != '\0' && input[i] != '/'; i++)
+ hexlen++;
+
+ if (input[i] == '\0') {
+ bits = 0;
+ } else {
+ /* Have /nn. */
+ input[i] = '\0';
+ if (sscanf((input + i + 1), "%u", &bits) != 1) {
+ warnx(gettext("%s is not a bit specifier."),
+ (input + i + 1));
+ usage();
+ }
+ /* hexlen in nibbles */
+ if (((bits + 3) >> 2) > hexlen) {
+ warnx(gettext("bit length %d is too big for %s."),
+ bits, input);
+ usage();
+ }
+ /*
+ * Adjust hexlen down if user gave us too small of a bit
+ * count.
+ */
+ if ((hexlen << 2) > bits + 3) {
+ warnx(gettext("WARNING: Lower bits will be truncated "
+ "for:\n\t%s/%d."), input, bits);
+ hexlen = (bits + 3) >> 2;
+ input[hexlen] = '\0';
+ }
+ }
+
+ /*
+ * Allocate. Remember, hexlen is in nibbles.
+ */
+
+ alloclen = sizeof (*retval) + roundup((hexlen/2 + (hexlen & 0x1)), 8);
+ retval = malloc(alloclen);
+
+ if (retval == NULL)
+ Bail("malloc(parsekey)");
+ retval->sadb_key_len = SADB_8TO64(alloclen);
+ retval->sadb_key_reserved = 0;
+ if (bits == 0)
+ retval->sadb_key_bits = (hexlen + (hexlen & 0x1)) << 2;
+ else
+ retval->sadb_key_bits = bits;
+
+ /*
+ * Read in nibbles. Read in odd-numbered as shifted high.
+ * (e.g. 123 becomes 0x1230).
+ */
+
+ key = (uint8_t *)(retval + 1);
+ for (i = 0; input[i] != '\0'; i += 2) {
+ boolean_t second = (input[i + 1] != '\0');
+
+ if (!isxdigit(input[i]) ||
+ (!isxdigit(input[i + 1]) && second)) {
+ warnx(gettext("string '%s' not a hex string."), input);
+ usage();
+ }
+ *key = (hd2num(input[i]) << 4);
+ if (second)
+ *key |= hd2num(input[i + 1]);
+ else
+ break; /* out of for loop. */
+ key++;
+ }
+
+ /* bzero the remaining bits if we're a non-octet amount. */
+ if (bits & 0x7)
+ *((input[i] == '\0') ? key - 1 : key) &=
+ 0xff << (8 - (bits & 0x7));
+
+ return (retval);
+}
+
+/*
+ * Expand the diagnostic code into a message.
+ */
+static void
+print_diagnostic(FILE *file, uint16_t diagnostic)
+{
+ /* Use two spaces so above strings can fit on the line. */
+ (void) fprintf(file, gettext(" Diagnostic code %u: %s.\n"),
+ diagnostic, keysock_diag(diagnostic));
+}
+
+/*
+ * Prints the base PF_KEY message.
+ */
+static void
+print_sadb_msg(struct sadb_msg *samsg, time_t wallclock)
+{
+ if (wallclock != 0)
+ printsatime(wallclock, gettext("%sTimestamp: %s\n"), "", NULL);
+
+ (void) printf(gettext("Base message (version %u) type "),
+ samsg->sadb_msg_version);
+ switch (samsg->sadb_msg_type) {
+ case SADB_RESERVED:
+ (void) printf(gettext("RESERVED (warning: set to 0)"));
+ break;
+ case SADB_GETSPI:
+ (void) printf("GETSPI");
+ break;
+ case SADB_UPDATE:
+ (void) printf("UPDATE");
+ break;
+ case SADB_ADD:
+ (void) printf("ADD");
+ break;
+ case SADB_DELETE:
+ (void) printf("DELETE");
+ break;
+ case SADB_GET:
+ (void) printf("GET");
+ break;
+ case SADB_ACQUIRE:
+ (void) printf("ACQUIRE");
+ break;
+ case SADB_REGISTER:
+ (void) printf("REGISTER");
+ break;
+ case SADB_EXPIRE:
+ (void) printf("EXPIRE");
+ break;
+ case SADB_FLUSH:
+ (void) printf("FLUSH");
+ break;
+ case SADB_DUMP:
+ (void) printf("DUMP");
+ break;
+ case SADB_X_PROMISC:
+ (void) printf("X_PROMISC");
+ break;
+ case SADB_X_INVERSE_ACQUIRE:
+ (void) printf("X_INVERSE_ACQUIRE");
+ break;
+ default:
+ (void) printf(gettext("Unknown (%u)"), samsg->sadb_msg_type);
+ break;
+ }
+ (void) printf(gettext(", SA type "));
+
+ switch (samsg->sadb_msg_satype) {
+ case SADB_SATYPE_UNSPEC:
+ (void) printf(gettext("<unspecified/all>"));
+ break;
+ case SADB_SATYPE_AH:
+ (void) printf("AH");
+ break;
+ case SADB_SATYPE_ESP:
+ (void) printf("ESP");
+ break;
+ case SADB_SATYPE_RSVP:
+ (void) printf("RSVP");
+ break;
+ case SADB_SATYPE_OSPFV2:
+ (void) printf("OSPFv2");
+ break;
+ case SADB_SATYPE_RIPV2:
+ (void) printf("RIPv2");
+ break;
+ case SADB_SATYPE_MIP:
+ (void) printf(gettext("Mobile IP"));
+ break;
+ default:
+ (void) printf(gettext("<unknown %u>"), samsg->sadb_msg_satype);
+ break;
+ }
+
+ (void) printf(".\n");
+
+ if (samsg->sadb_msg_errno != 0) {
+ (void) printf(gettext("Error %s from PF_KEY.\n"),
+ strerror(samsg->sadb_msg_errno));
+ print_diagnostic(stdout, samsg->sadb_x_msg_diagnostic);
+ }
+
+ (void) printf(gettext("Message length %u bytes, seq=%u, pid=%u.\n"),
+ SADB_64TO8(samsg->sadb_msg_len), samsg->sadb_msg_seq,
+ samsg->sadb_msg_pid);
+}
+
+/*
+ * Print the SA extension for PF_KEY.
+ */
+static void
+print_sa(char *prefix, struct sadb_sa *assoc)
+{
+ if (assoc->sadb_sa_len != SADB_8TO64(sizeof (*assoc))) {
+ warnx(gettext("WARNING: SA info extension length (%u) is bad."),
+ SADB_64TO8(assoc->sadb_sa_len));
+ }
+
+ (void) printf(gettext("%sSADB_ASSOC spi=0x%x, replay=%u, state="),
+ prefix, ntohl(assoc->sadb_sa_spi), assoc->sadb_sa_replay);
+ switch (assoc->sadb_sa_state) {
+ case SADB_SASTATE_LARVAL:
+ (void) printf(gettext("LARVAL"));
+ break;
+ case SADB_SASTATE_MATURE:
+ (void) printf(gettext("MATURE"));
+ break;
+ case SADB_SASTATE_DYING:
+ (void) printf(gettext("DYING"));
+ break;
+ case SADB_SASTATE_DEAD:
+ (void) printf(gettext("DEAD"));
+ break;
+ default:
+ (void) printf(gettext("<unknown %u>"), assoc->sadb_sa_state);
+ }
+
+ if (assoc->sadb_sa_auth != SADB_AALG_NONE) {
+ (void) printf(gettext("\n%sAuthentication algorithm = "),
+ prefix);
+ (void) dump_aalg(assoc->sadb_sa_auth, stdout);
+ }
+
+ if (assoc->sadb_sa_encrypt != SADB_EALG_NONE) {
+ (void) printf(gettext("\n%sEncryption algorithm = "), prefix);
+ (void) dump_ealg(assoc->sadb_sa_encrypt, stdout);
+ }
+
+ (void) printf(gettext("\n%sflags=0x%x < "), prefix,
+ assoc->sadb_sa_flags);
+ if (assoc->sadb_sa_flags & SADB_SAFLAGS_PFS)
+ (void) printf("PFS ");
+ if (assoc->sadb_sa_flags & SADB_SAFLAGS_NOREPLAY)
+ (void) printf("NOREPLAY ");
+
+ /* BEGIN Solaris-specific flags. */
+ if (assoc->sadb_sa_flags & SADB_X_SAFLAGS_USED)
+ (void) printf("X_USED ");
+ if (assoc->sadb_sa_flags & SADB_X_SAFLAGS_UNIQUE)
+ (void) printf("X_UNIQUE ");
+ if (assoc->sadb_sa_flags & SADB_X_SAFLAGS_AALG1)
+ (void) printf("X_AALG1 ");
+ if (assoc->sadb_sa_flags & SADB_X_SAFLAGS_AALG2)
+ (void) printf("X_AALG2 ");
+ if (assoc->sadb_sa_flags & SADB_X_SAFLAGS_EALG1)
+ (void) printf("X_EALG1 ");
+ if (assoc->sadb_sa_flags & SADB_X_SAFLAGS_EALG2)
+ (void) printf("X_EALG2 ");
+ if (assoc->sadb_sa_flags & SADB_X_SAFLAGS_NATT_LOC)
+ (void) printf("X_NATT_LOC ");
+ if (assoc->sadb_sa_flags & SADB_X_SAFLAGS_NATT_REM)
+ (void) printf("X_NATT_REM ");
+ /* END Solaris-specific flags. */
+
+ (void) printf(">\n");
+}
+
+static void
+printsatime(int64_t lt, const char *msg, const char *pfx, const char *pfx2)
+{
+ char tbuf[TBUF_SIZE]; /* For strftime() call. */
+ const char *tp = tbuf;
+ time_t t = lt;
+ if (t != lt) {
+ if (lt > 0)
+ t = LONG_MAX;
+ else
+ t = LONG_MIN;
+ }
+
+ if (strftime(tbuf, TBUF_SIZE, NULL, localtime(&t)) == 0)
+ tp = gettext("<time conversion failed>");
+ (void) printf(msg, pfx, tp);
+ if (vflag && (pfx2 != NULL))
+ (void) printf(gettext("%s\t(raw time value %llu)\n"), pfx2, lt);
+}
+
+/*
+ * Print the SA lifetime information. (An SADB_EXT_LIFETIME_* extension.)
+ */
+static void
+print_lifetimes(time_t wallclock, struct sadb_lifetime *current,
+ struct sadb_lifetime *hard, struct sadb_lifetime *soft)
+{
+ int64_t scratch;
+ char *soft_prefix = gettext("SLT: ");
+ char *hard_prefix = gettext("HLT: ");
+ char *current_prefix = gettext("CLT: ");
+
+ if (current != NULL &&
+ current->sadb_lifetime_len != SADB_8TO64(sizeof (*current))) {
+ warnx(gettext("WARNING: CURRENT lifetime extension length "
+ "(%u) is bad."),
+ SADB_64TO8(current->sadb_lifetime_len));
+ }
+
+ if (hard != NULL &&
+ hard->sadb_lifetime_len != SADB_8TO64(sizeof (*hard))) {
+ warnx(gettext("WARNING: HARD lifetime "
+ "extension length (%u) is bad."),
+ SADB_64TO8(hard->sadb_lifetime_len));
+ }
+
+ if (soft != NULL &&
+ soft->sadb_lifetime_len != SADB_8TO64(sizeof (*soft))) {
+ warnx(gettext("WARNING: SOFT lifetime "
+ "extension length (%u) is bad."),
+ SADB_64TO8(soft->sadb_lifetime_len));
+ }
+
+ (void) printf(" LT: Lifetime information\n");
+
+ if (current != NULL) {
+ /* Express values as current values. */
+ (void) printf(gettext(
+ "%s%llu bytes protected, %u allocations used.\n"),
+ current_prefix, current->sadb_lifetime_bytes,
+ current->sadb_lifetime_allocations);
+ printsatime(current->sadb_lifetime_addtime,
+ gettext("%sSA added at time %s\n"),
+ current_prefix, current_prefix);
+ if (current->sadb_lifetime_usetime != 0) {
+ printsatime(current->sadb_lifetime_usetime,
+ gettext("%sSA first used at time %s\n"),
+ current_prefix, current_prefix);
+ }
+ printsatime(wallclock, gettext("%sTime now is %s\n"),
+ current_prefix, current_prefix);
+ }
+
+ if (soft != NULL) {
+ (void) printf(gettext("%sSoft lifetime information: "),
+ soft_prefix);
+ (void) printf(gettext("%llu bytes of lifetime, %u "
+ "allocations.\n"), soft->sadb_lifetime_bytes,
+ soft->sadb_lifetime_allocations);
+ (void) printf(gettext("%s%llu seconds of post-add lifetime.\n"),
+ soft_prefix, soft->sadb_lifetime_addtime);
+ (void) printf(gettext("%s%llu seconds of post-use lifetime.\n"),
+ soft_prefix, soft->sadb_lifetime_usetime);
+ /* If possible, express values as time remaining. */
+ if (current != NULL) {
+ if (soft->sadb_lifetime_bytes != 0)
+ (void) printf(gettext(
+ "%s%llu more bytes can be protected.\n"),
+ soft_prefix,
+ (soft->sadb_lifetime_bytes >
+ current->sadb_lifetime_bytes) ?
+ (soft->sadb_lifetime_bytes -
+ current->sadb_lifetime_bytes) : (0));
+ if (soft->sadb_lifetime_addtime != 0 ||
+ (soft->sadb_lifetime_usetime != 0 &&
+ current->sadb_lifetime_usetime != 0)) {
+ int64_t adddelta, usedelta;
+
+ if (soft->sadb_lifetime_addtime != 0) {
+ adddelta =
+ current->sadb_lifetime_addtime +
+ soft->sadb_lifetime_addtime -
+ wallclock;
+ } else {
+ adddelta = TIME_MAX;
+ }
+
+ if (soft->sadb_lifetime_usetime != 0 &&
+ current->sadb_lifetime_usetime != 0) {
+ usedelta =
+ current->sadb_lifetime_usetime +
+ soft->sadb_lifetime_usetime -
+ wallclock;
+ } else {
+ usedelta = TIME_MAX;
+ }
+ (void) printf("%s", soft_prefix);
+ scratch = MIN(adddelta, usedelta);
+ if (scratch >= 0) {
+ (void) printf(gettext("Soft expiration "
+ "occurs in %lld seconds, "),
+ scratch);
+ } else {
+ (void) printf(gettext(
+ "Soft expiration occurred "));
+ }
+ scratch += wallclock;
+ printsatime(scratch, gettext("%sat %s.\n"), "",
+ soft_prefix);
+ }
+ }
+ }
+
+ if (hard != NULL) {
+ (void) printf(gettext("%sHard lifetime information: "),
+ hard_prefix);
+ (void) printf(gettext("%llu bytes of lifetime, "
+ "%u allocations.\n"), hard->sadb_lifetime_bytes,
+ hard->sadb_lifetime_allocations);
+ (void) printf(gettext("%s%llu seconds of post-add lifetime.\n"),
+ hard_prefix, hard->sadb_lifetime_addtime);
+ (void) printf(gettext("%s%llu seconds of post-use lifetime.\n"),
+ hard_prefix, hard->sadb_lifetime_usetime);
+ /* If possible, express values as time remaining. */
+ if (current != NULL) {
+ if (hard->sadb_lifetime_bytes != 0)
+ (void) printf(gettext(
+ "%s%llu more bytes can be protected.\n"),
+ hard_prefix,
+ (hard->sadb_lifetime_bytes >
+ current->sadb_lifetime_bytes) ?
+ (hard->sadb_lifetime_bytes -
+ current->sadb_lifetime_bytes) : (0));
+ if (hard->sadb_lifetime_addtime != 0 ||
+ (hard->sadb_lifetime_usetime != 0 &&
+ current->sadb_lifetime_usetime != 0)) {
+ int64_t adddelta, usedelta;
+
+ if (hard->sadb_lifetime_addtime != 0) {
+ adddelta =
+ current->sadb_lifetime_addtime +
+ hard->sadb_lifetime_addtime -
+ wallclock;
+ } else {
+ adddelta = TIME_MAX;
+ }
+
+ if (hard->sadb_lifetime_usetime != 0 &&
+ current->sadb_lifetime_usetime != 0) {
+ usedelta =
+ current->sadb_lifetime_usetime +
+ hard->sadb_lifetime_usetime -
+ wallclock;
+ } else {
+ usedelta = TIME_MAX;
+ }
+ (void) printf("%s", hard_prefix);
+ scratch = MIN(adddelta, usedelta);
+ if (scratch >= 0) {
+ (void) printf(gettext("Hard expiration "
+ "occurs in %lld seconds, "),
+ scratch);
+ } else {
+ (void) printf(gettext(
+ "Hard expiration occured "));
+ }
+ scratch += wallclock;
+ printsatime(scratch, gettext("%sat %s.\n"), "",
+ hard_prefix);
+ }
+ }
+ }
+}
+
+/*
+ * Print an SADB_EXT_ADDRESS_* extension.
+ */
+static void
+print_address(char *prefix, struct sadb_address *addr)
+{
+ struct protoent *pe;
+
+ (void) printf("%s", prefix);
+ switch (addr->sadb_address_exttype) {
+ case SADB_EXT_ADDRESS_SRC:
+ (void) printf(gettext("Source address "));
+ break;
+ case SADB_EXT_ADDRESS_DST:
+ (void) printf(gettext("Destination address "));
+ break;
+ case SADB_EXT_ADDRESS_PROXY:
+ (void) printf(gettext("Proxy address "));
+ break;
+ case SADB_X_EXT_ADDRESS_NATT_LOC:
+ (void) printf(gettext("NATT local address "));
+ break;
+ case SADB_X_EXT_ADDRESS_NATT_REM:
+ (void) printf(gettext("NATT remote address "));
+ break;
+ }
+
+ (void) printf(gettext("(proto=%d"), addr->sadb_address_proto);
+ if (!nflag) {
+ if (addr->sadb_address_proto == 0) {
+ (void) printf(gettext("/<unspecified>"));
+ } else if ((pe = getprotobynumber(addr->sadb_address_proto))
+ != NULL) {
+ (void) printf("/%s", pe->p_name);
+ } else {
+ (void) printf(gettext("/<unknown>"));
+ }
+ }
+ (void) printf(gettext(")\n%s"), prefix);
+ (void) dump_sockaddr((struct sockaddr *)(addr + 1), B_FALSE, stdout);
+}
+
+/*
+ * Print an SADB_EXT_KEY extension.
+ */
+static void
+print_key(char *prefix, struct sadb_key *key)
+{
+ (void) printf("%s", prefix);
+
+ switch (key->sadb_key_exttype) {
+ case SADB_EXT_KEY_AUTH:
+ (void) printf(gettext("Authentication"));
+ break;
+ case SADB_EXT_KEY_ENCRYPT:
+ (void) printf(gettext("Encryption"));
+ break;
+ }
+
+ (void) printf(gettext(" key.\n%s"), prefix);
+ (void) dump_key((uint8_t *)(key + 1), key->sadb_key_bits, stdout);
+ (void) putchar('\n');
+}
+
+/*
+ * Print an SADB_EXT_IDENTITY_* extension.
+ */
+static void
+print_ident(char *prefix, struct sadb_ident *id)
+{
+ boolean_t canprint = B_TRUE;
+
+ (void) printf("%s", prefix);
+ switch (id->sadb_ident_exttype) {
+ case SADB_EXT_IDENTITY_SRC:
+ (void) printf(gettext("Source"));
+ break;
+ case SADB_EXT_IDENTITY_DST:
+ (void) printf(gettext("Destination"));
+ break;
+ }
+
+ (void) printf(gettext(" identity, uid=%d, type "), id->sadb_ident_id);
+ canprint = dump_sadb_idtype(id->sadb_ident_type, stdout, NULL);
+ (void) printf("\n%s", prefix);
+ if (canprint)
+ (void) printf("%s\n", (char *)(id + 1));
+ else
+ (void) printf(gettext("<cannot print>\n"));
+}
+
+/*
+ * Print an SADB_SENSITIVITY extension.
+ */
+static void
+print_sens(char *prefix, struct sadb_sens *sens)
+{
+ uint64_t *bitmap = (uint64_t *)(sens + 1);
+ int i;
+
+ (void) printf(
+ gettext("%sSensitivity DPD %d, sens level=%d, integ level=%d\n"),
+ prefix, sens->sadb_sens_dpd, sens->sadb_sens_sens_level,
+ sens->sadb_sens_integ_level);
+ for (i = 0; sens->sadb_sens_sens_len-- > 0; i++, bitmap++)
+ (void) printf(
+ gettext("%s Sensitivity BM extended word %d 0x%llx\n"),
+ i, *bitmap);
+ for (i = 0; sens->sadb_sens_integ_len-- > 0; i++, bitmap++)
+ (void) printf(
+ gettext("%s Integrity BM extended word %d 0x%llx\n"),
+ i, *bitmap);
+}
+
+/*
+ * Print an SADB_EXT_PROPOSAL extension.
+ */
+static void
+print_prop(char *prefix, struct sadb_prop *prop)
+{
+ struct sadb_comb *combs;
+ int i, numcombs;
+
+ (void) printf(gettext("%sProposal, replay counter = %u.\n"), prefix,
+ prop->sadb_prop_replay);
+
+ numcombs = prop->sadb_prop_len - SADB_8TO64(sizeof (*prop));
+ numcombs /= SADB_8TO64(sizeof (*combs));
+
+ combs = (struct sadb_comb *)(prop + 1);
+
+ for (i = 0; i < numcombs; i++) {
+ (void) printf(gettext("%s Combination #%u "), prefix, i + 1);
+ if (combs[i].sadb_comb_auth != SADB_AALG_NONE) {
+ (void) printf(gettext("Authentication = "));
+ (void) dump_aalg(combs[i].sadb_comb_auth, stdout);
+ (void) printf(gettext(" minbits=%u, maxbits=%u.\n%s "),
+ combs[i].sadb_comb_auth_minbits,
+ combs[i].sadb_comb_auth_maxbits, prefix);
+ }
+
+ if (combs[i].sadb_comb_encrypt != SADB_EALG_NONE) {
+ (void) printf(gettext("Encryption = "));
+ (void) dump_ealg(combs[i].sadb_comb_encrypt, stdout);
+ (void) printf(gettext(" minbits=%u, maxbits=%u.\n%s "),
+ combs[i].sadb_comb_encrypt_minbits,
+ combs[i].sadb_comb_encrypt_maxbits, prefix);
+ }
+
+ (void) printf(gettext("HARD: "));
+ if (combs[i].sadb_comb_hard_allocations)
+ (void) printf(gettext("alloc=%u "),
+ combs[i].sadb_comb_hard_allocations);
+ if (combs[i].sadb_comb_hard_bytes)
+ (void) printf(gettext("bytes=%llu "),
+ combs[i].sadb_comb_hard_bytes);
+ if (combs[i].sadb_comb_hard_addtime)
+ (void) printf(gettext("post-add secs=%llu "),
+ combs[i].sadb_comb_hard_addtime);
+ if (combs[i].sadb_comb_hard_usetime)
+ (void) printf(gettext("post-use secs=%llu"),
+ combs[i].sadb_comb_hard_usetime);
+
+ (void) printf(gettext("\n%s SOFT: "), prefix);
+ if (combs[i].sadb_comb_soft_allocations)
+ (void) printf(gettext("alloc=%u "),
+ combs[i].sadb_comb_soft_allocations);
+ if (combs[i].sadb_comb_soft_bytes)
+ (void) printf(gettext("bytes=%llu "),
+ combs[i].sadb_comb_soft_bytes);
+ if (combs[i].sadb_comb_soft_addtime)
+ (void) printf(gettext("post-add secs=%llu "),
+ combs[i].sadb_comb_soft_addtime);
+ if (combs[i].sadb_comb_soft_usetime)
+ (void) printf(gettext("post-use secs=%llu"),
+ combs[i].sadb_comb_soft_usetime);
+ (void) putchar('\n');
+ }
+}
+
+/*
+ * Print an extended proposal (SADB_X_EXT_EPROP).
+ */
+static void
+print_eprop(char *prefix, struct sadb_prop *eprop)
+{
+ uint64_t *sofar;
+ struct sadb_x_ecomb *ecomb;
+ struct sadb_x_algdesc *algdesc;
+ int i, j;
+
+ (void) printf(gettext("%sExtended Proposal, replay counter = %u, "),
+ prefix, eprop->sadb_prop_replay);
+ (void) printf(gettext("number of combinations = %u.\n"),
+ eprop->sadb_x_prop_numecombs);
+
+ sofar = (uint64_t *)(eprop + 1);
+ ecomb = (struct sadb_x_ecomb *)sofar;
+
+ for (i = 0; i < eprop->sadb_x_prop_numecombs; ) {
+ (void) printf(gettext("%s Extended combination #%u:\n"),
+ prefix, ++i);
+
+ (void) printf(gettext("%s HARD: "), prefix);
+ (void) printf(gettext("alloc=%u, "),
+ ecomb->sadb_x_ecomb_hard_allocations);
+ (void) printf(gettext("bytes=%llu, "),
+ ecomb->sadb_x_ecomb_hard_bytes);
+ (void) printf(gettext("post-add secs=%llu, "),
+ ecomb->sadb_x_ecomb_hard_addtime);
+ (void) printf(gettext("post-use secs=%llu\n"),
+ ecomb->sadb_x_ecomb_hard_usetime);
+
+ (void) printf(gettext("%s SOFT: "), prefix);
+ (void) printf(gettext("alloc=%u, "),
+ ecomb->sadb_x_ecomb_soft_allocations);
+ (void) printf(gettext("bytes=%llu, "),
+ ecomb->sadb_x_ecomb_soft_bytes);
+ (void) printf(gettext("post-add secs=%llu, "),
+ ecomb->sadb_x_ecomb_soft_addtime);
+ (void) printf(gettext("post-use secs=%llu\n"),
+ ecomb->sadb_x_ecomb_soft_usetime);
+
+ sofar = (uint64_t *)(ecomb + 1);
+ algdesc = (struct sadb_x_algdesc *)sofar;
+
+ for (j = 0; j < ecomb->sadb_x_ecomb_numalgs; ) {
+ (void) printf(gettext("%s Alg #%u "), prefix, ++j);
+ switch (algdesc->sadb_x_algdesc_satype) {
+ case SADB_SATYPE_ESP:
+ (void) printf(gettext("for ESP "));
+ break;
+ case SADB_SATYPE_AH:
+ (void) printf(gettext("for AH "));
+ break;
+ default:
+ (void) printf(gettext("for satype=%d "),
+ algdesc->sadb_x_algdesc_satype);
+ }
+ switch (algdesc->sadb_x_algdesc_algtype) {
+ case SADB_X_ALGTYPE_CRYPT:
+ (void) printf(gettext("Encryption = "));
+ (void) dump_ealg(algdesc->sadb_x_algdesc_alg,
+ stdout);
+ break;
+ case SADB_X_ALGTYPE_AUTH:
+ (void) printf(gettext("Authentication = "));
+ (void) dump_aalg(algdesc->sadb_x_algdesc_alg,
+ stdout);
+ break;
+ default:
+ (void) printf(gettext("algtype(%d) = alg(%d)"),
+ algdesc->sadb_x_algdesc_algtype,
+ algdesc->sadb_x_algdesc_alg);
+ break;
+ }
+
+ (void) printf(gettext(" minbits=%u, maxbits=%u.\n"),
+ algdesc->sadb_x_algdesc_minbits,
+ algdesc->sadb_x_algdesc_maxbits);
+
+ sofar = (uint64_t *)(++algdesc);
+ }
+ ecomb = (struct sadb_x_ecomb *)sofar;
+ }
+}
+
+/*
+ * Print an SADB_EXT_SUPPORTED extension.
+ */
+static void
+print_supp(char *prefix, struct sadb_supported *supp)
+{
+ struct sadb_alg *algs;
+ int i, numalgs;
+
+ (void) printf(gettext("%sSupported "), prefix);
+ switch (supp->sadb_supported_exttype) {
+ case SADB_EXT_SUPPORTED_AUTH:
+ (void) printf(gettext("authentication"));
+ break;
+ case SADB_EXT_SUPPORTED_ENCRYPT:
+ (void) printf(gettext("encryption"));
+ break;
+ }
+ (void) printf(gettext(" algorithms.\n"));
+
+ algs = (struct sadb_alg *)(supp + 1);
+ numalgs = supp->sadb_supported_len - SADB_8TO64(sizeof (*supp));
+ numalgs /= SADB_8TO64(sizeof (*algs));
+ for (i = 0; i < numalgs; i++) {
+ (void) printf("%s", prefix);
+ switch (supp->sadb_supported_exttype) {
+ case SADB_EXT_SUPPORTED_AUTH:
+ (void) dump_aalg(algs[i].sadb_alg_id, stdout);
+ break;
+ case SADB_EXT_SUPPORTED_ENCRYPT:
+ (void) dump_ealg(algs[i].sadb_alg_id, stdout);
+ break;
+ }
+ (void) printf(gettext(" minbits=%u, maxbits=%u, ivlen=%u.\n"),
+ algs[i].sadb_alg_minbits, algs[i].sadb_alg_maxbits,
+ algs[i].sadb_alg_ivlen);
+ }
+}
+
+/*
+ * Print an SADB_EXT_SPIRANGE extension.
+ */
+static void
+print_spirange(char *prefix, struct sadb_spirange *range)
+{
+ (void) printf(gettext("%sSPI Range, min=0x%x, max=0x%x\n"), prefix,
+ htonl(range->sadb_spirange_min),
+ htonl(range->sadb_spirange_max));
+}
+
+/*
+ * Print an SADB_X_EXT_KM_COOKIE extension.
+ */
+
+static void
+print_kmc(char *prefix, struct sadb_x_kmc *kmc)
+{
+ char *cookie_label;
+
+ if ((cookie_label = kmc_lookup_by_cookie(kmc->sadb_x_kmc_cookie)) ==
+ NULL)
+ cookie_label = gettext("<Label not found.>");
+
+ (void) printf(gettext("%sProtocol %u, cookie=\"%s\" (%u)\n"), prefix,
+ kmc->sadb_x_kmc_proto, cookie_label, kmc->sadb_x_kmc_cookie);
+}
+
+/*
+ * Take a PF_KEY message pointed to buffer and print it. Useful for DUMP
+ * and GET.
+ */
+static void
+print_samsg(uint64_t *buffer, boolean_t want_timestamp)
+{
+ uint64_t *current;
+ struct sadb_msg *samsg = (struct sadb_msg *)buffer;
+ struct sadb_ext *ext;
+ struct sadb_lifetime *currentlt = NULL, *hardlt = NULL, *softlt = NULL;
+ int i;
+ time_t wallclock;
+
+ (void) time(&wallclock);
+
+ print_sadb_msg(samsg, want_timestamp ? wallclock : 0);
+ current = (uint64_t *)(samsg + 1);
+ while (current - buffer < samsg->sadb_msg_len) {
+ int lenbytes;
+
+ ext = (struct sadb_ext *)current;
+ lenbytes = SADB_64TO8(ext->sadb_ext_len);
+ switch (ext->sadb_ext_type) {
+ case SADB_EXT_SA:
+ print_sa(gettext("SA: "), (struct sadb_sa *)current);
+ break;
+ /*
+ * Pluck out lifetimes and print them at the end. This is
+ * to show relative lifetimes.
+ */
+ case SADB_EXT_LIFETIME_CURRENT:
+ currentlt = (struct sadb_lifetime *)current;
+ break;
+ case SADB_EXT_LIFETIME_HARD:
+ hardlt = (struct sadb_lifetime *)current;
+ break;
+ case SADB_EXT_LIFETIME_SOFT:
+ softlt = (struct sadb_lifetime *)current;
+ break;
+
+ case SADB_EXT_ADDRESS_SRC:
+ print_address(gettext("SRC: "),
+ (struct sadb_address *)current);
+ break;
+ case SADB_EXT_ADDRESS_DST:
+ print_address(gettext("DST: "),
+ (struct sadb_address *)current);
+ break;
+ case SADB_EXT_ADDRESS_PROXY:
+ print_address(gettext("PXY: "),
+ (struct sadb_address *)current);
+ break;
+ case SADB_EXT_KEY_AUTH:
+ print_key(gettext("AKY: "), (struct sadb_key *)current);
+ break;
+ case SADB_EXT_KEY_ENCRYPT:
+ print_key(gettext("EKY: "), (struct sadb_key *)current);
+ break;
+ case SADB_EXT_IDENTITY_SRC:
+ print_ident(gettext("SID: "),
+ (struct sadb_ident *)current);
+ break;
+ case SADB_EXT_IDENTITY_DST:
+ print_ident(gettext("DID: "),
+ (struct sadb_ident *)current);
+ break;
+ case SADB_EXT_SENSITIVITY:
+ print_sens(gettext("SNS: "),
+ (struct sadb_sens *)current);
+ break;
+ case SADB_EXT_PROPOSAL:
+ print_prop(gettext("PRP: "),
+ (struct sadb_prop *)current);
+ break;
+ case SADB_EXT_SUPPORTED_AUTH:
+ print_supp(gettext("SUA: "),
+ (struct sadb_supported *)current);
+ break;
+ case SADB_EXT_SUPPORTED_ENCRYPT:
+ print_supp(gettext("SUE: "),
+ (struct sadb_supported *)current);
+ break;
+ case SADB_EXT_SPIRANGE:
+ print_spirange(gettext("SPR: "),
+ (struct sadb_spirange *)current);
+ break;
+ case SADB_X_EXT_EPROP:
+ print_eprop(gettext("EPR: "),
+ (struct sadb_prop *)current);
+ break;
+ case SADB_X_EXT_KM_COOKIE:
+ print_kmc(gettext("KMC: "),
+ (struct sadb_x_kmc *)current);
+ break;
+ case SADB_X_EXT_ADDRESS_NATT_REM:
+ print_address(gettext("NRM: "),
+ (struct sadb_address *)current);
+ break;
+ case SADB_X_EXT_ADDRESS_NATT_LOC:
+ print_address(gettext("NLC: "),
+ (struct sadb_address *)current);
+ break;
+ default:
+ (void) printf(gettext(
+ "UNK: Unknown ext. %d, len %d.\n"),
+ ext->sadb_ext_type, lenbytes);
+ for (i = 0; i < ext->sadb_ext_len; i++)
+ (void) printf(gettext("UNK: 0x%llx\n"),
+ ((uint64_t *)ext)[i]);
+ break;
+ }
+ current += ext->sadb_ext_len;
+ }
+ /*
+ * Print lifetimes NOW.
+ */
+ if (currentlt != NULL || hardlt != NULL || softlt != NULL)
+ print_lifetimes(wallclock, currentlt, hardlt, softlt);
+
+ if (current - buffer != samsg->sadb_msg_len) {
+ warnx(gettext("WARNING: insufficient buffer "
+ "space or corrupt message."));
+ }
+
+ (void) fflush(stdout); /* Make sure our message is out there. */
+}
+
+/*
+ * Write a message to the PF_KEY socket. If verbose, print the message
+ * heading into the kernel.
+ */
+static int
+key_write(int fd, void *msg, size_t len)
+{
+ if (vflag) {
+ (void) printf(
+ gettext("VERBOSE ON: Message to kernel looks like:\n"));
+ (void) printf("==========================================\n");
+ print_samsg(msg, B_FALSE);
+ (void) printf("==========================================\n");
+ }
+
+ return (write(fd, msg, len));
+}
+
+/*
+ * SIGALRM handler for time_critical_enter.
+ */
+static void
+time_critical_catch(int signal)
+{
+ if (signal == SIGALRM) {
+ errx(1, gettext("Reply message from PF_KEY timed out."));
+ } else {
+ errx(1, gettext("Caught signal %d while trying to receive"
+ "PF_KEY reply message"), signal);
+ }
+ /* errx() calls exit. */
+}
+
+#define TIME_CRITICAL_TIME 10 /* In seconds */
+
+/*
+ * Enter a "time critical" section where key is waiting for a return message.
+ */
+static void
+time_critical_enter(void)
+{
+ (void) signal(SIGALRM, time_critical_catch);
+ (void) alarm(TIME_CRITICAL_TIME);
+}
+
+/*
+ * Exit the "time critical" section after getting an appropriate return
+ * message.
+ */
+static void
+time_critical_exit(void)
+{
+ (void) alarm(0);
+ (void) signal(SIGALRM, SIG_DFL);
+}
+
+/*
+ * Construct a PF_KEY FLUSH message for the SA type specified.
+ */
+static void
+doflush(int satype)
+{
+ struct sadb_msg msg;
+ int rc;
+
+ msg_init(&msg, SADB_FLUSH, (uint8_t)satype);
+ rc = key_write(keysock, &msg, sizeof (msg));
+ if (rc == -1)
+ Bail("write() to PF_KEY socket failed (in doflush)");
+
+ time_critical_enter();
+ do {
+ rc = read(keysock, &msg, sizeof (msg));
+ if (rc == -1)
+ Bail("read (in doflush)");
+ } while (msg.sadb_msg_seq != seq || msg.sadb_msg_pid != mypid);
+ time_critical_exit();
+
+ /*
+ * I should _never_ hit the following unless:
+ *
+ * 1. There is a kernel bug.
+ * 2. There is another process filling in its pid with mine, and
+ * issuing a different message that would cause a different result.
+ */
+ if (msg.sadb_msg_type != SADB_FLUSH ||
+ msg.sadb_msg_satype != (uint8_t)satype) {
+ syslog((LOG_NOTICE|LOG_AUTH),
+ gettext("doflush: Return message not of type SADB_FLUSH!"));
+ Bail("doflush: Return message not of type SADB_FLUSH!");
+ }
+
+ if (msg.sadb_msg_errno != 0) {
+ errno = msg.sadb_msg_errno;
+ if (errno == EINVAL) {
+ print_diagnostic(stderr, msg.sadb_x_msg_diagnostic);
+ warnx(gettext("Cannot flush SA type %d."), satype);
+ }
+ Bail("return message (in doflush)");
+ }
+}
+
+/*
+ * save_XXX functions are used when "saving" the SA tables to either a
+ * file or standard output. They use the dump_XXX functions where needed,
+ * but mostly they use the rparseXXX functions.
+ */
+
+/*
+ * Print save information for a lifetime extension.
+ *
+ * NOTE : It saves the lifetime in absolute terms. For example, if you
+ * had a hard_usetime of 60 seconds, you'll save it as 60 seconds, even though
+ * there may have been 59 seconds burned off the clock.
+ */
+static boolean_t
+save_lifetime(struct sadb_lifetime *lifetime, FILE *ofile)
+{
+ char *prefix;
+
+ prefix = (lifetime->sadb_lifetime_exttype == SADB_EXT_LIFETIME_SOFT) ?
+ "soft" : "hard";
+
+ if (putc('\t', ofile) == EOF)
+ return (B_FALSE);
+
+ if (lifetime->sadb_lifetime_allocations != 0 && fprintf(ofile,
+ "%s_alloc %u ", prefix, lifetime->sadb_lifetime_allocations) < 0)
+ return (B_FALSE);
+
+ if (lifetime->sadb_lifetime_bytes != 0 && fprintf(ofile,
+ "%s_bytes %llu ", prefix, lifetime->sadb_lifetime_bytes) < 0)
+ return (B_FALSE);
+
+ if (lifetime->sadb_lifetime_addtime != 0 && fprintf(ofile,
+ "%s_addtime %llu ", prefix, lifetime->sadb_lifetime_addtime) < 0)
+ return (B_FALSE);
+
+ if (lifetime->sadb_lifetime_usetime != 0 && fprintf(ofile,
+ "%s_usetime %llu ", prefix, lifetime->sadb_lifetime_usetime) < 0)
+ return (B_FALSE);
+
+ return (B_TRUE);
+}
+
+/*
+ * Print save information for an address extension.
+ */
+static boolean_t
+save_address(struct sadb_address *addr, FILE *ofile)
+{
+ char *printable_addr, buf[INET6_ADDRSTRLEN];
+ const char *prefix, *pprefix;
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)(addr + 1);
+ struct sockaddr_in *sin = (struct sockaddr_in *)sin6;
+ int af = sin->sin_family;
+
+ /*
+ * Address-family reality check.
+ */
+ if (af != AF_INET6 && af != AF_INET)
+ return (B_FALSE);
+
+ switch (addr->sadb_address_exttype) {
+ case SADB_EXT_ADDRESS_SRC:
+ prefix = "src";
+ pprefix = "sport";
+ break;
+ case SADB_EXT_ADDRESS_DST:
+ prefix = "dst";
+ pprefix = "dport";
+ break;
+ case SADB_EXT_ADDRESS_PROXY:
+ prefix = "proxy";
+ pprefix = NULL;
+ break;
+ case SADB_X_EXT_ADDRESS_NATT_LOC:
+ prefix = "nat_loc ";
+ pprefix = "nat_lport";
+ break;
+ case SADB_X_EXT_ADDRESS_NATT_REM:
+ prefix = "nat_rem ";
+ pprefix = "nat_rport";
+ break;
+ }
+
+ if (fprintf(ofile, " %s ", prefix) < 0)
+ return (B_FALSE);
+
+ /*
+ * Do not do address-to-name translation, given that we live in
+ * an age of names that explode into many addresses.
+ */
+ printable_addr = (char *)inet_ntop(af,
+ (af == AF_INET) ? (char *)&sin->sin_addr : (char *)&sin6->sin6_addr,
+ buf, sizeof (buf));
+ if (printable_addr == NULL)
+ printable_addr = "<inet_ntop() failed>";
+ if (fprintf(ofile, "%s", printable_addr) < 0)
+ return (B_FALSE);
+
+ /*
+ * The port is in the same position for struct sockaddr_in and
+ * struct sockaddr_in6. We exploit that property here.
+ */
+ if ((pprefix != NULL) && (sin->sin_port != 0))
+ (void) fprintf(ofile, " %s %d", pprefix, ntohs(sin->sin_port));
+
+ return (B_TRUE);
+}
+
+/*
+ * Print save information for a key extension. Returns whether writing
+ * to the specified output file was successful or not.
+ */
+static boolean_t
+save_key(struct sadb_key *key, FILE *ofile)
+{
+ char *prefix;
+
+ if (putc('\t', ofile) == EOF)
+ return (B_FALSE);
+
+ prefix = (key->sadb_key_exttype == SADB_EXT_KEY_AUTH) ? "auth" : "encr";
+
+ if (fprintf(ofile, "%skey ", prefix) < 0)
+ return (B_FALSE);
+
+ if (dump_key((uint8_t *)(key + 1), key->sadb_key_bits, ofile) == -1)
+ return (B_FALSE);
+
+ return (B_TRUE);
+}
+
+/*
+ * Print save information for an identity extension.
+ */
+static boolean_t
+save_ident(struct sadb_ident *ident, FILE *ofile)
+{
+ char *prefix;
+
+ if (putc('\t', ofile) == EOF)
+ return (B_FALSE);
+
+ prefix = (ident->sadb_ident_exttype == SADB_EXT_IDENTITY_SRC) ? "src" :
+ "dst";
+
+ if (fprintf(ofile, "%sidtype %s ", prefix,
+ rparseidtype(ident->sadb_ident_type)) < 0)
+ return (B_FALSE);
+
+ if (ident->sadb_ident_type == SADB_X_IDENTTYPE_DN ||
+ ident->sadb_ident_type == SADB_X_IDENTTYPE_GN) {
+ if (fprintf(ofile, gettext("<can-not-print>")) < 0)
+ return (B_FALSE);
+ } else {
+ if (fprintf(ofile, "%s", (char *)(ident + 1)) < 0)
+ return (B_FALSE);
+ }
+
+ return (B_TRUE);
+}
+
+/*
+ * "Save" a security association to an output file.
+ *
+ * NOTE the lack of calls to gettext() because I'm outputting parseable stuff.
+ * ALSO NOTE that if you change keywords (see parsecmd()), you'll have to
+ * change them here as well.
+ */
+static void
+save_assoc(uint64_t *buffer, FILE *ofile)
+{
+ int seen_proto = 0;
+ uint64_t *current;
+ struct sadb_address *addr;
+ struct sadb_msg *samsg = (struct sadb_msg *)buffer;
+ struct sadb_ext *ext;
+#define bail2(s) do { \
+ int t = errno; \
+ (void) fclose(ofile); \
+ errno = t; \
+ interactive = B_FALSE; /* Guarantees exit. */ \
+ Bail(s); \
+ } while (B_FALSE) /* How do I lint-clean this? */
+
+#define savenl() if (fputs(" \\\n", ofile) == EOF) { bail2("savenl"); }
+
+ if (fputs("# begin assoc\n", ofile) == EOF)
+ Bail("save_assoc: Opening comment of SA");
+ if (fprintf(ofile, "add %s ", rparsesatype(samsg->sadb_msg_satype)) < 0)
+ Bail("save_assoc: First line of SA");
+ /* LINTED E_CONST_COND */
+ savenl();
+
+ current = (uint64_t *)(samsg + 1);
+ while (current - buffer < samsg->sadb_msg_len) {
+ struct sadb_sa *assoc;
+
+ ext = (struct sadb_ext *)current;
+ switch (ext->sadb_ext_type) {
+ case SADB_EXT_SA:
+ assoc = (struct sadb_sa *)ext;
+ if (assoc->sadb_sa_state != SADB_SASTATE_MATURE) {
+ if (fprintf(ofile, "# WARNING: SA was dying "
+ "or dead.\n") < 0) {
+ /* LINTED E_CONST_COND */
+ bail2("save_assoc: fprintf not mature");
+ }
+ }
+ if (fprintf(ofile, " spi 0x%x ",
+ ntohl(assoc->sadb_sa_spi)) < 0)
+ /* LINTED E_CONST_COND */
+ bail2("save_assoc: fprintf spi");
+ if (fprintf(ofile, "encr_alg %s ",
+ rparsealg(assoc->sadb_sa_encrypt,
+ IPSEC_PROTO_ESP)) < 0)
+ /* LINTED E_CONST_COND */
+ bail2("save_assoc: fprintf encrypt");
+ if (fprintf(ofile, "auth_alg %s ",
+ rparsealg(assoc->sadb_sa_auth,
+ IPSEC_PROTO_AH)) < 0)
+ /* LINTED E_CONST_COND */
+ bail2("save_assoc: fprintf auth");
+ if (fprintf(ofile, "replay %d ",
+ assoc->sadb_sa_replay) < 0)
+ /* LINTED E_CONST_COND */
+ bail2("save_assoc: fprintf replay");
+ if (assoc->sadb_sa_flags & (SADB_X_SAFLAGS_NATT_LOC |
+ SADB_X_SAFLAGS_NATT_REM)) {
+ if (fprintf(ofile, "encap udp") < 0)
+ /* LINTED E_CONST_COND */
+ bail2("save_assoc: fprintf encap");
+ }
+ /* LINTED E_CONST_COND */
+ savenl();
+ break;
+ case SADB_EXT_LIFETIME_HARD:
+ case SADB_EXT_LIFETIME_SOFT:
+ if (!save_lifetime((struct sadb_lifetime *)ext, ofile))
+ /* LINTED E_CONST_COND */
+ bail2("save_lifetime");
+ /* LINTED E_CONST_COND */
+ savenl();
+ break;
+ case SADB_EXT_ADDRESS_SRC:
+ case SADB_EXT_ADDRESS_DST:
+ case SADB_EXT_ADDRESS_PROXY:
+ case SADB_X_EXT_ADDRESS_NATT_REM:
+ case SADB_X_EXT_ADDRESS_NATT_LOC:
+ addr = (struct sadb_address *)ext;
+ if (!seen_proto && addr->sadb_address_proto) {
+ (void) fprintf(ofile, " proto %d",
+ addr->sadb_address_proto);
+ /* LINTED E_CONST_COND */
+ savenl();
+ seen_proto = 1;
+ }
+ if (!save_address(addr, ofile))
+ /* LINTED E_CONST_COND */
+ bail2("save_address");
+ /* LINTED E_CONST_COND */
+ savenl();
+ break;
+ case SADB_EXT_KEY_AUTH:
+ case SADB_EXT_KEY_ENCRYPT:
+ if (!save_key((struct sadb_key *)ext, ofile))
+ /* LINTED E_CONST_COND */
+ bail2("save_address");
+ /* LINTED E_CONST_COND */
+ savenl();
+ break;
+ case SADB_EXT_IDENTITY_SRC:
+ case SADB_EXT_IDENTITY_DST:
+ if (!save_ident((struct sadb_ident *)ext, ofile))
+ /* LINTED E_CONST_COND */
+ bail2("save_address");
+ /* LINTED E_CONST_COND */
+ savenl();
+ break;
+ case SADB_EXT_SENSITIVITY:
+ default:
+ /* Skip over irrelevant extensions. */
+ break;
+ }
+ current += ext->sadb_ext_len;
+ }
+
+ if (fputs(gettext("\n# end assoc\n\n"), ofile) == EOF)
+ /* LINTED E_CONST_COND */
+ bail2("save_assoc: last fputs");
+}
+
+/*
+ * Because "save" and "dump" both use the SADB_DUMP message, fold both
+ * into the same function.
+ */
+static void
+dodump(int satype, FILE *ofile)
+{
+ struct sadb_msg *msg = (struct sadb_msg *)get_buffer;
+ int rc;
+
+ if (ofile != NULL) {
+ (void) fprintf(ofile,
+ gettext("# This key file was generated by the"));
+ (void) fprintf(ofile,
+ gettext(" ipseckey(1m) command's 'save' feature.\n\n"));
+ }
+ msg_init(msg, SADB_DUMP, (uint8_t)satype);
+ rc = key_write(keysock, msg, sizeof (*msg));
+ if (rc == -1)
+ Bail("write to PF_KEY socket failed (in dodump)");
+
+ do {
+ /*
+ * For DUMP, do only the read as a time critical section.
+ */
+ time_critical_enter();
+ rc = read(keysock, get_buffer, sizeof (get_buffer));
+ time_critical_exit();
+ if (rc == -1)
+ Bail("read (in dodump)");
+ if (msg->sadb_msg_pid == mypid &&
+ msg->sadb_msg_type == SADB_DUMP &&
+ msg->sadb_msg_seq != 0 &&
+ msg->sadb_msg_errno == 0) {
+ if (ofile == NULL) {
+ print_samsg(get_buffer, B_FALSE);
+ (void) putchar('\n');
+ } else {
+ save_assoc(get_buffer, ofile);
+ }
+ }
+ } while (msg->sadb_msg_pid != mypid ||
+ (msg->sadb_msg_errno == 0 && msg->sadb_msg_seq != 0));
+
+ if (ofile != NULL && ofile != stdout)
+ (void) fclose(ofile);
+
+ if (msg->sadb_msg_errno == 0) {
+ if (ofile == NULL)
+ (void) printf(
+ gettext("Dump succeeded for SA type %d.\n"),
+ satype);
+ } else {
+ print_diagnostic(stderr, msg->sadb_x_msg_diagnostic);
+ errno = msg->sadb_msg_errno;
+ Bail("Dump failed");
+ }
+}
+
+#define SCOPE_UNSPEC 0
+#define SCOPE_LINKLOCAL 1
+#define SCOPE_SITELOCAL 2
+#define SCOPE_GLOBAL 3
+#define SCOPE_V4COMPAT 4
+#define SCOPE_LOOPBACK 5 /* Pedantic, yes, but necessary. */
+
+static int
+ipv6_addr_scope(struct in6_addr *addr)
+{
+ /* Don't return anything regarding multicast for now... */
+
+ if (IN6_IS_ADDR_UNSPECIFIED(addr))
+ return (SCOPE_UNSPEC);
+
+ if (IN6_IS_ADDR_LINKLOCAL(addr))
+ return (SCOPE_LINKLOCAL);
+
+ if (IN6_IS_ADDR_SITELOCAL(addr))
+ return (SCOPE_SITELOCAL);
+
+ if (IN6_IS_ADDR_V4COMPAT(addr))
+ return (SCOPE_V4COMPAT);
+
+ if (IN6_IS_ADDR_LOOPBACK(addr))
+ return (SCOPE_LOOPBACK);
+
+ /* For now, return global by default. */
+ return (SCOPE_GLOBAL);
+}
+
+/*
+ * doaddresses():
+ *
+ * Used by doaddup() and dodelget() to create new SA's based on the
+ * provided source and destination addresses hostent.
+ *
+ * sadb_msg_type: expected PF_KEY reply message type
+ * sadb_msg_satype: expected PF_KEY reply satype
+ * cmd: user command
+ * srchp: hostent for the source address(es)
+ * dsthp: hostent for the destination address(es)
+ * src: points to the SADB source address extension
+ * dst: points to the SADB destination address extension
+ * unspec_src: indicates an unspecified source address.
+ * buffer: pointer to the SADB buffer to use with PF_KEY
+ * buffer_size: size of buffer
+ * spi: spi for this message (set by caller)
+ * srcport: source port if specified
+ * dstport: destination port is specified
+ * proto: IP protocol number if specified
+ * NATT note: we are going to assume a semi-sane world where NAT
+ * boxen don't explode to multiple addresses.
+ */
+static void
+doaddresses(uint8_t sadb_msg_type, uint8_t sadb_msg_satype, int cmd,
+ struct hostent *srchp, struct hostent *dsthp,
+ struct sadb_address *src, struct sadb_address *dst,
+ boolean_t unspec_src, uint64_t *buffer, int buffer_size, uint32_t spi,
+ uint16_t srcport, uint16_t dstport, uint16_t proto,
+ struct hostent *natt_lhp, struct hostent *natt_rhp,
+ struct sadb_address *natt_loc, struct sadb_address *natt_rem,
+ uint16_t natt_lport, uint16_t natt_rport)
+{
+ boolean_t single_dst;
+ struct sockaddr_in6 *sin6;
+ struct sadb_msg *msgp;
+ int i, rc;
+ char **walker; /* For the SRC and PROXY walking functions. */
+ char *first_match;
+ uint64_t savebuf[SADB_8TO64(MAX_GET_SIZE)];
+
+ /*
+ * Okay, now we have "src", "dst", and maybe "proxy" reassigned
+ * to point into the buffer to be written to PF_KEY, we can do
+ * potentially several writes based on destination address.
+ *
+ * First, fill in port numbers and protocol in extensions.
+ */
+
+ if ((proto == 0) && ((srcport != 0) || (dstport != 0))) {
+ warnx(gettext("WARNING: ports without proto is nonsensical."));
+ /*
+ * Don't worry about it, it just may make the SA not match
+ * any outbound traffic, or it perhaps could be perverted
+ * by the kernel to cover both TCP and UDP traffic on the
+ * same port (e.g. DNS).
+ */
+ }
+
+ if (src != NULL) {
+ src->sadb_address_proto = proto;
+ sin6 = (struct sockaddr_in6 *)(src + 1);
+ sin6->sin6_port = htons(srcport);
+ }
+ if (dst != NULL) {
+ dst->sadb_address_proto = proto;
+ sin6 = (struct sockaddr_in6 *)(dst + 1);
+ sin6->sin6_port = htons(dstport);
+ }
+ if (natt_loc != NULL) {
+ sin6 = (struct sockaddr_in6 *)(natt_loc + 1);
+ bzero(sin6, sizeof (*sin6));
+ bcopy(natt_lhp->h_addr_list[0], &sin6->sin6_addr,
+ sizeof (struct in6_addr));
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = htons(natt_lport);
+ }
+ if (natt_rem != NULL) {
+ sin6 = (struct sockaddr_in6 *)(natt_rem + 1);
+ bzero(sin6, sizeof (*sin6));
+ bcopy(natt_rhp->h_addr_list[0], &sin6->sin6_addr,
+ sizeof (struct in6_addr));
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = htons(natt_rport);
+ }
+
+ /*
+ * The rules for ADD, GET, and UPDATE: (NOTE: This assumes IPsec.
+ * If other consumers of PF_KEY happen, this will have to be
+ * rewhacked.):
+ *
+ * Do a message for every possible DST address.
+ *
+ * If a source or proxy address explodes, keep unspecified
+ * (and mention unspecified).
+ *
+ * If dsthp is == dummy.he, then go through the loop once.
+ * If any other hp is == dummy.he, then you don't have to apply any
+ * silly rules.
+ *
+ * DELETE is different, because you can leave either "src" or "dst"
+ * blank! You need to explode if one of them is full, and not assume
+ * that the other is set.
+ */
+
+ if (dsthp == NULL) {
+ /*
+ * No destination address specified.
+ * With extended diagnostics, we don't have to bail the
+ * non-DELETE cases here. The EINVAL diagnostics will be
+ * enough to inform the user(s) what happened.
+ */
+ i = 0;
+ do {
+ if (srchp == &dummy.he) {
+ /* Just to be sure... */
+ srchp->h_addr_list[1] = NULL;
+ } else if (srchp != NULL) {
+ /* Degenerate case, h_addr_list[0] == NULL. */
+ if (srchp->h_addr_list[i] == NULL)
+ Bail("Empty source address list");
+
+ /*
+ * Fill in the src sockaddr.
+ */
+ sin6 = (struct sockaddr_in6 *)(src + 1);
+ bzero(sin6, sizeof (*sin6));
+ bcopy(srchp->h_addr_list[i], &sin6->sin6_addr,
+ sizeof (struct in6_addr));
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = htons(srcport);
+ }
+
+ /* Save off a copy for later writing... */
+ msgp = (struct sadb_msg *)buffer;
+ bcopy(buffer, savebuf, SADB_64TO8(msgp->sadb_msg_len));
+
+ rc = key_write(keysock, buffer,
+ SADB_64TO8(msgp->sadb_msg_len));
+ if (rc == -1)
+ Bail("write() to PF_KEY socket "
+ "(in doaddresses)");
+
+ time_critical_enter();
+ do {
+ rc = read(keysock, buffer, buffer_size);
+ if (rc == -1)
+ Bail("read (in doaddresses)");
+ } while (msgp->sadb_msg_seq != seq ||
+ msgp->sadb_msg_pid != mypid);
+ time_critical_exit();
+
+ if (msgp->sadb_msg_type != sadb_msg_type ||
+ msgp->sadb_msg_satype != sadb_msg_satype) {
+ syslog((LOG_NOTICE|LOG_AUTH), gettext(
+ "doaddresses: Unexpected returned message "
+ "(%d exp %d)\n"), msgp->sadb_msg_type,
+ sadb_msg_type);
+ Bail("doaddresses: Unexpected returned "
+ "message");
+ }
+
+ errno = msgp->sadb_msg_errno;
+ if (errno != 0) {
+ if (errno == EINVAL) {
+ warnx(gettext("One of the entered "
+ "values is incorrect."));
+ print_diagnostic(stderr,
+ msgp->sadb_x_msg_diagnostic);
+ }
+ Bail("return message (in doaddresses)");
+ }
+
+ /* ...and then restore the saved buffer. */
+ msgp = (struct sadb_msg *)savebuf;
+ bcopy(savebuf, buffer, SADB_64TO8(msgp->sadb_msg_len));
+ } while (srchp != NULL && srchp->h_addr_list[++i] != NULL);
+ return;
+ }
+
+ single_dst = (dsthp == &dummy.he || dsthp->h_addr_list[1] == NULL);
+
+ for (i = 0; dsthp->h_addr_list[i] != NULL; i++) {
+ if (dsthp == &dummy.he) {
+ /* Just to be sure... */
+ dsthp->h_addr_list[1] = NULL;
+ } else {
+ /*
+ * Fill in the dst sockaddr.
+ */
+ sin6 = (struct sockaddr_in6 *)(dst + 1);
+ bzero(sin6, sizeof (*sin6));
+ bcopy(dsthp->h_addr_list[i], &sin6->sin6_addr,
+ sizeof (struct in6_addr));
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = htons(dstport);
+ }
+
+ /*
+ * Try and assign src, if there's any ambiguity.
+ */
+ if (!unspec_src && srchp != &dummy.he) {
+ if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
+ /*
+ * IPv4 address. Find an IPv4 address, then
+ * keep looking for a second one. If a second
+ * exists, print a message, and fill in the
+ * unspecified address.
+ */
+ first_match = NULL;
+
+ for (walker = srchp->h_addr_list;
+ *walker != NULL; walker++) {
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ if (IN6_IS_ADDR_V4MAPPED(
+ (struct in6_addr *)*walker)) {
+ if (first_match != NULL)
+ break;
+ else
+ first_match = *walker;
+ }
+ }
+ sin6 = (struct sockaddr_in6 *)(src + 1);
+ bzero(sin6, sizeof (*sin6));
+
+ if (first_match == NULL) {
+ /*
+ * No IPv4 hits. Is this a single
+ * dest?
+ */
+ warnx(gettext(
+ "No IPv4 source address "
+ "for name %s."), srchp->h_name);
+ if (single_dst) {
+ /* Error. */
+ usage();
+ } else {
+ /* Continue, but do I print? */
+ continue; /* for loop */
+ }
+
+ /* I should never reach here. */
+ }
+
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = htons(srcport);
+ if (*walker != NULL) {
+ /*
+ * Early loop exit. It must've been
+ * multiple hits...
+ *
+ * Issue a null-source warning?
+ */
+ warnx(gettext(
+ "Multiple IPv4 source addresses "
+ "for %s, using unspecified source "
+ "instead."), srchp->h_name);
+ } else {
+ /*
+ * If I reach here w/o hitting the
+ * previous if statements, I have a
+ * single source address for this
+ * destination.
+ */
+ bcopy(first_match, &sin6->sin6_addr,
+ sizeof (struct in6_addr));
+ }
+ } else {
+ /*
+ * IPv6 address. Find an IPv6 address.
+ * Unlike IPv4 addresses, things can get a
+ * little more sticky with scopes, etc.
+ */
+ int dst_scope, src_scope;
+
+ dst_scope = ipv6_addr_scope(&sin6->sin6_addr);
+
+ first_match = NULL;
+ for (walker = srchp->h_addr_list;
+ *walker != NULL; walker++) {
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ if (!IN6_IS_ADDR_V4MAPPED(
+ (struct in6_addr *)*walker)) {
+ /*
+ * Set first-match, etc.
+ * Take into account scopes,
+ * and other IPv6 thingies.
+ */
+ src_scope = ipv6_addr_scope(
+ /* LINTED E_BAD_PTR_CAST */
+ (struct in6_addr *)*walker);
+ if (src_scope == SCOPE_UNSPEC ||
+ src_scope == dst_scope) {
+ if (first_match !=
+ NULL)
+ break;
+ else
+ first_match =
+ *walker;
+ }
+ }
+ }
+
+ sin6 = (struct sockaddr_in6 *)(src + 1);
+ bzero(sin6, sizeof (*sin6));
+ sin6->sin6_port = htons(srcport);
+ if (first_match == NULL) {
+ /*
+ * No IPv6 hits. Is this a single
+ * dest?
+ */
+ warnx(gettext(
+ "No IPv6 source address of "
+ "matching scope for name %s."),
+ srchp->h_name);
+ if (single_dst) {
+ /* Error. */
+ usage();
+ } else {
+ /* Continue, but do I print? */
+ continue; /* for loop */
+ }
+
+ /* I should never reach here. */
+ }
+ sin6->sin6_family = AF_INET6;
+ if (*walker != NULL) {
+ /*
+ * Early loop exit. Issue a
+ * null-source warning?
+ */
+ warnx(gettext(
+ "Multiple IPv6 source addresses "
+ "for %s of the same scope, using "
+ "unspecified source instead."),
+ srchp->h_name);
+ } else {
+ /*
+ * If I reach here w/o hitting the
+ * previous if statements, I have a
+ * single source address for this
+ * destination.
+ */
+ bcopy(first_match, &sin6->sin6_addr,
+ sizeof (struct in6_addr));
+ }
+ }
+ }
+
+ /* Save off a copy for later writing... */
+ msgp = (struct sadb_msg *)buffer;
+ bcopy(buffer, savebuf, SADB_64TO8(msgp->sadb_msg_len));
+
+ rc = key_write(keysock, buffer, SADB_64TO8(msgp->sadb_msg_len));
+ if (rc == -1)
+ Bail("write() to PF_KEY socket (in doaddresses)");
+
+ /* Blank the key for paranoia's sake. */
+ bzero(buffer, buffer_size);
+ time_critical_enter();
+ do {
+ rc = read(keysock, buffer, buffer_size);
+ if (rc == -1)
+ Bail("read (in doaddresses)");
+ } while (msgp->sadb_msg_seq != seq ||
+ msgp->sadb_msg_pid != mypid);
+ time_critical_exit();
+
+ /*
+ * I should _never_ hit the following unless:
+ *
+ * 1. There is a kernel bug.
+ * 2. Another process is mistakenly using my pid in a PF_KEY
+ * message.
+ */
+ if (msgp->sadb_msg_type != sadb_msg_type ||
+ msgp->sadb_msg_satype != sadb_msg_satype) {
+ syslog((LOG_NOTICE|LOG_AUTH), gettext(
+ "doaddresses: Unexpected returned message "
+ "(%d exp %d)\n"), msgp->sadb_msg_type,
+ sadb_msg_type);
+ Bail("doaddresses: Unexpected returned message");
+ }
+
+ if (msgp->sadb_msg_errno != 0) {
+ char addrprint[INET6_ADDRSTRLEN];
+ int on_errno = 0;
+ char *on_errno_msg;
+
+ /*
+ * Print different error messages depending
+ * on the SADB message type being processed.
+ * If we get a ESRCH error for a GET/DELETE
+ * messages, we report that the SA does not
+ * exist. If we get a EEXIST error for a
+ * ADD/UPDATE message, we report that the
+ * SA already exists.
+ */
+ if (sadb_msg_type == SADB_GET ||
+ sadb_msg_type == SADB_DELETE) {
+ on_errno = ESRCH;
+ on_errno_msg = "does not exist";
+ } else if (sadb_msg_type == SADB_ADD ||
+ sadb_msg_type == SADB_UPDATE) {
+ on_errno = EEXIST;
+ on_errno_msg = "already exists";
+ }
+
+ errno = msgp->sadb_msg_errno;
+ if (errno == on_errno) {
+ warnx(gettext("Association (type = %s) "
+ "with spi 0x%x and addr\n%s %s."),
+ rparsesatype(msgp->sadb_msg_satype),
+ ntohl(spi),
+ do_inet_ntop(dsthp->h_addr_list[i],
+ addrprint, sizeof (addrprint)),
+ on_errno_msg);
+ msgp = (struct sadb_msg *)savebuf;
+ bcopy(savebuf, buffer,
+ SADB_64TO8(msgp->sadb_msg_len));
+ continue;
+ } else {
+ if (errno == EINVAL) {
+ warnx(gettext("One of the entered "
+ "values is incorrect."));
+ print_diagnostic(stderr,
+ msgp->sadb_x_msg_diagnostic);
+ }
+ Bail("return message (in doaddresses)");
+ }
+ }
+ if (cmd == CMD_GET) {
+ if (SADB_64TO8(msgp->sadb_msg_len) > MAX_GET_SIZE) {
+ warnx(gettext("WARNING: "
+ "SA information bigger than %d bytes."),
+ MAX_GET_SIZE);
+ }
+ print_samsg(buffer, B_FALSE);
+ }
+
+ /* ...and then restore the saved buffer. */
+ msgp = (struct sadb_msg *)savebuf;
+ bcopy(savebuf, buffer, SADB_64TO8(msgp->sadb_msg_len));
+ }
+
+ /* Degenerate case, h_addr_list[0] == NULL. */
+ if (i == 0)
+ Bail("Empty destination address list");
+}
+
+/*
+ * Perform an add or an update. ADD and UPDATE are similar in the extensions
+ * they need.
+ */
+static void
+doaddup(int cmd, int satype, char *argv[])
+{
+ uint64_t *buffer, *nexthdr;
+ struct sadb_msg msg;
+ struct sadb_sa *assoc = NULL;
+ struct sadb_address *src = NULL, *dst = NULL, *proxy = NULL;
+ struct sadb_address *natt_local = NULL, *natt_remote = NULL;
+ struct sadb_key *encrypt = NULL, *auth = NULL;
+ struct sadb_ident *srcid = NULL, *dstid = NULL;
+ struct sadb_lifetime *hard = NULL, *soft = NULL; /* Current? */
+ struct sockaddr_in6 *sin6;
+ /* MLS TODO: Need sensitivity eventually. */
+ int next, token, sa_len, alloclen, totallen = sizeof (msg);
+ uint32_t spi;
+ char *thiscmd;
+ boolean_t readstate = B_FALSE, unspec_src = B_FALSE, use_natt = B_FALSE;
+ struct hostent *srchp = NULL, *dsthp = NULL, *proxyhp = NULL;
+ struct hostent *natt_lhp = NULL, *natt_rhp = NULL;
+ uint16_t srcport = 0, dstport = 0, natt_lport = 0, natt_rport = 0;
+ uint8_t proto = 0;
+
+ thiscmd = (cmd == CMD_ADD) ? "add" : "update";
+
+ msg_init(&msg, ((cmd == CMD_ADD) ? SADB_ADD : SADB_UPDATE),
+ (uint8_t)satype);
+
+ /* Assume last element in argv is set to NULL. */
+ do {
+ token = parseextval(*argv, &next);
+ argv++;
+ switch (token) {
+ case TOK_EOF:
+ /* Do nothing, I'm done. */
+ break;
+ case TOK_UNKNOWN:
+ warnx(gettext("Unknown extension field %s."),
+ *(argv - 1));
+ usage(); /* Will exit program. */
+ break;
+ case TOK_SPI:
+ case TOK_REPLAY:
+ case TOK_STATE:
+ case TOK_AUTHALG:
+ case TOK_ENCRALG:
+ case TOK_ENCAP:
+ /*
+ * May want to place this chunk of code in a function.
+ *
+ * This code checks for duplicate entries on a command
+ * line.
+ */
+
+ /* Allocate the SADB_EXT_SA extension. */
+ if (assoc == NULL) {
+ assoc = malloc(sizeof (*assoc));
+ if (assoc == NULL)
+ Bail("malloc(assoc)");
+ bzero(assoc, sizeof (*assoc));
+ assoc->sadb_sa_exttype = SADB_EXT_SA;
+ assoc->sadb_sa_len =
+ SADB_8TO64(sizeof (*assoc));
+ totallen += sizeof (*assoc);
+ }
+ switch (token) {
+ case TOK_SPI:
+ /*
+ * If some cretin types in "spi 0" then he/she
+ * can type in another SPI.
+ */
+ if (assoc->sadb_sa_spi != 0) {
+ warnx(gettext("Can only specify "
+ "single SPI value."));
+ usage();
+ }
+ /* Must convert SPI to network order! */
+ assoc->sadb_sa_spi =
+ htonl((uint32_t)parsenum(*argv, B_TRUE));
+ break;
+ case TOK_REPLAY:
+ /*
+ * That same cretin can do the same with
+ * replay.
+ */
+ if (assoc->sadb_sa_replay != 0) {
+ warnx(gettext("Can only specify "
+ "single replay wsize."));
+ usage();
+ }
+ assoc->sadb_sa_replay =
+ (uint8_t)parsenum(*argv, B_TRUE);
+ if (assoc->sadb_sa_replay != 0) {
+ warnx(gettext(
+ "WARNING: Replay with manual"
+ " keying considered harmful."));
+ }
+ break;
+ case TOK_STATE:
+ /*
+ * 0 is an actual state value, LARVAL. This
+ * means that one can type in the larval state
+ * and then type in another state on the same
+ * command line.
+ */
+ if (assoc->sadb_sa_state != 0) {
+ warnx(gettext("Can only specify "
+ "single SA state."));
+ usage();
+ }
+ assoc->sadb_sa_state = parsestate(*argv);
+ readstate = B_TRUE;
+ break;
+ case TOK_AUTHALG:
+ if (assoc->sadb_sa_auth != 0) {
+ warnx(gettext("Can only specify "
+ "single auth algorithm."));
+ usage();
+ }
+ assoc->sadb_sa_auth = parsealg(*argv,
+ IPSEC_PROTO_AH);
+ break;
+ case TOK_ENCRALG:
+ if (assoc->sadb_sa_encrypt != 0) {
+ warnx(gettext("Can only specify single"
+ " encryption algorithm."));
+ usage();
+ }
+ assoc->sadb_sa_encrypt = parsealg(*argv,
+ IPSEC_PROTO_ESP);
+ break;
+ case TOK_ENCAP:
+ if (use_natt) {
+ warnx(gettext("Can only specify single"
+ " encapsulation."));
+ usage();
+ }
+ if (strncmp(*argv, "udp", 3)) {
+ warnx(gettext("Can only specify udp"
+ " encapsulation."));
+ usage();
+ }
+ use_natt = B_TRUE;
+ /* set assoc flags later */
+ break;
+ }
+ argv++;
+ break;
+ case TOK_SRCPORT:
+ if (srcport != 0) {
+ warnx(gettext("Can only specify "
+ "single source port."));
+ usage();
+ }
+ srcport = parsenum(*argv, B_TRUE);
+ argv++;
+ break;
+ case TOK_DSTPORT:
+ if (dstport != 0) {
+ warnx(gettext("Can only specify "
+ "single destination port."));
+ usage();
+ }
+ dstport = parsenum(*argv, B_TRUE);
+ argv++;
+ break;
+ case TOK_NATLPORT:
+ if (natt_lport != 0) {
+ warnx(gettext("Can only specify "
+ "single natt local port."));
+ usage();
+ }
+
+ if (natt_rport != 0) {
+ warnx(gettext("Can only specify "
+ "one of natt remote and local port."));
+ usage();
+ }
+ natt_lport = parsenum(*argv, B_TRUE);
+ argv++;
+ break;
+ case TOK_NATRPORT:
+ if (natt_rport != 0) {
+ warnx(gettext("Can only specify "
+ "single natt remote port."));
+ usage();
+ }
+
+ if (natt_lport != 0) {
+ warnx(gettext("Can only specify "
+ "one of natt remote and local port."));
+ usage();
+ }
+ natt_rport = parsenum(*argv, B_TRUE);
+ argv++;
+ break;
+
+ case TOK_PROTO:
+ if (proto != 0) {
+ warnx(gettext("Can only specify "
+ "single protocol."));
+ usage();
+ }
+ proto = parsenum(*argv, B_TRUE);
+ argv++;
+ break;
+ case TOK_SRCADDR:
+ case TOK_SRCADDR6:
+ if (src != NULL) {
+ warnx(gettext("Can only specify "
+ "single source address."));
+ usage();
+ }
+ sa_len = parseaddr(*argv, &srchp,
+ (token == TOK_SRCADDR6));
+ argv++;
+ /*
+ * Round of the sockaddr length to an 8 byte
+ * boundary to make PF_KEY happy.
+ */
+ alloclen = sizeof (*src) + roundup(sa_len, 8);
+ src = malloc(alloclen);
+ if (src == NULL)
+ Bail("malloc(src)");
+ totallen += alloclen;
+ src->sadb_address_len = SADB_8TO64(alloclen);
+ src->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
+ src->sadb_address_reserved = 0;
+ src->sadb_address_prefixlen = 0;
+ src->sadb_address_proto = 0;
+ if (srchp == &dummy.he) {
+ /*
+ * Single address with -n flag.
+ */
+ sin6 = (struct sockaddr_in6 *)(src + 1);
+ bzero(sin6, sizeof (*sin6));
+ sin6->sin6_family = AF_INET6;
+ bcopy(srchp->h_addr_list[0], &sin6->sin6_addr,
+ sizeof (struct in6_addr));
+ }
+ break;
+ case TOK_DSTADDR:
+ case TOK_DSTADDR6:
+ if (dst != NULL) {
+ warnx(gettext("Can only specify single "
+ "destination address."));
+ usage();
+ }
+ sa_len = parseaddr(*argv, &dsthp,
+ (token == TOK_DSTADDR6));
+ argv++;
+ alloclen = sizeof (*dst) + roundup(sa_len, 8);
+ dst = malloc(alloclen);
+ if (dst == NULL)
+ Bail("malloc(dst)");
+ totallen += alloclen;
+ dst->sadb_address_len = SADB_8TO64(alloclen);
+ dst->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
+ dst->sadb_address_reserved = 0;
+ dst->sadb_address_prefixlen = 0;
+ dst->sadb_address_proto = 0;
+ if (dsthp == &dummy.he) {
+ /*
+ * Single address with -n flag.
+ */
+ sin6 = (struct sockaddr_in6 *)(dst + 1);
+ bzero(sin6, sizeof (*sin6));
+ sin6->sin6_family = AF_INET6;
+ bcopy(dsthp->h_addr_list[0], &sin6->sin6_addr,
+ sizeof (struct in6_addr));
+ }
+ break;
+ case TOK_PROXYADDR:
+ case TOK_PROXYADDR6:
+ if (proxy != NULL) {
+ warnx(gettext("Can only specify single "
+ "proxy address."));
+ usage();
+ }
+ sa_len = parseaddr(*argv, &proxyhp,
+ (token == TOK_PROXYADDR6));
+ argv++;
+ alloclen = sizeof (*proxy) + roundup(sa_len, 8);
+ proxy = malloc(alloclen);
+ if (proxy == NULL)
+ Bail("malloc(proxy)");
+ totallen += alloclen;
+ proxy->sadb_address_len = SADB_8TO64(alloclen);
+ proxy->sadb_address_exttype = SADB_EXT_ADDRESS_PROXY;
+ proxy->sadb_address_reserved = 0;
+ proxy->sadb_address_prefixlen = 0;
+ proxy->sadb_address_proto = 0;
+ if (proxyhp == &dummy.he ||
+ proxyhp->h_addr_list[1] == NULL) {
+ /*
+ * Single address with -n flag or single name.
+ */
+ sin6 = (struct sockaddr_in6 *)(proxy + 1);
+ bzero(sin6, sizeof (*sin6));
+ sin6->sin6_family = AF_INET6;
+ bcopy(proxyhp->h_addr_list[0], &sin6->sin6_addr,
+ sizeof (struct in6_addr));
+ } else {
+ /*
+ * If the proxy address is vague, don't bother.
+ */
+ totallen -= alloclen;
+ free(proxy);
+ proxy = NULL;
+ warnx(gettext("Proxy address %s is vague, not"
+ " using."), proxyhp->h_name);
+ freehostent(proxyhp);
+ proxyhp = NULL;
+ }
+ break;
+ case TOK_NATLOC:
+ if (natt_local != NULL) {
+ warnx(gettext("Can only specify "
+ "single natt local address."));
+ usage();
+ }
+ sa_len = parseaddr(*argv, &natt_lhp, 0);
+ argv++;
+ /*
+ * Round of the sockaddr length to an 8 byte
+ * boundary to make PF_KEY happy.
+ */
+ alloclen = sizeof (*natt_local) + roundup(sa_len, 8);
+ natt_local = malloc(alloclen);
+ if (natt_local == NULL)
+ Bail("malloc(natt_local)");
+ totallen += alloclen;
+ natt_local->sadb_address_len = SADB_8TO64(alloclen);
+ natt_local->sadb_address_exttype =
+ SADB_X_EXT_ADDRESS_NATT_LOC;
+ natt_local->sadb_address_reserved = 0;
+ natt_local->sadb_address_prefixlen = 0;
+ natt_local->sadb_address_proto = 0;
+ if (natt_lhp == &dummy.he) {
+ /*
+ * Single address with -n flag.
+ */
+ sin6 = (struct sockaddr_in6 *)(natt_local + 1);
+ bzero(sin6, sizeof (*sin6));
+ sin6->sin6_family = AF_INET6;
+ bcopy(natt_lhp->h_addr_list[0],
+ &sin6->sin6_addr, sizeof (struct in6_addr));
+ }
+ break;
+ case TOK_NATREM:
+ if (natt_remote != NULL) {
+ warnx(gettext("Can only specify "
+ "single natt remote address."));
+ usage();
+ }
+ sa_len = parseaddr(*argv, &natt_rhp, 0);
+ argv++;
+ /*
+ * Round of the sockaddr length to an 8 byte
+ * boundary to make PF_KEY happy.
+ */
+ alloclen = sizeof (*natt_remote) + roundup(sa_len, 8);
+ natt_remote = malloc(alloclen);
+ if (natt_remote == NULL)
+ Bail("malloc(natt_remote)");
+ totallen += alloclen;
+ natt_remote->sadb_address_len = SADB_8TO64(alloclen);
+ natt_remote->sadb_address_exttype =
+ SADB_X_EXT_ADDRESS_NATT_REM;
+ natt_remote->sadb_address_reserved = 0;
+ natt_remote->sadb_address_prefixlen = 0;
+ natt_remote->sadb_address_proto = 0;
+ if (natt_rhp == &dummy.he) {
+ /*
+ * Single address with -n flag.
+ */
+ sin6 = (struct sockaddr_in6 *)(natt_remote + 1);
+ bzero(sin6, sizeof (*sin6));
+ sin6->sin6_family = AF_INET6;
+ bcopy(natt_rhp->h_addr_list[0],
+ &sin6->sin6_addr, sizeof (struct in6_addr));
+ }
+ break;
+ case TOK_ENCRKEY:
+ if (encrypt != NULL) {
+ warnx(gettext("Can only specify "
+ "single encryption key."));
+ usage();
+ }
+ encrypt = parsekey(*argv);
+ totallen += SADB_64TO8(encrypt->sadb_key_len);
+ argv++;
+ encrypt->sadb_key_exttype = SADB_EXT_KEY_ENCRYPT;
+ break;
+ case TOK_AUTHKEY:
+ if (auth != NULL) {
+ warnx(gettext("Can only specify single"
+ " authentication key."));
+ usage();
+ }
+ auth = parsekey(*argv);
+ argv++;
+ totallen += SADB_64TO8(auth->sadb_key_len);
+ auth->sadb_key_exttype = SADB_EXT_KEY_AUTH;
+ break;
+ case TOK_SRCIDTYPE:
+ if (*argv == NULL || *(argv + 1) == NULL) {
+ warnx(gettext("Unexpected end of command "
+ "line."));
+ usage();
+ }
+ if (srcid != NULL) {
+ warnx(gettext("Can only specify single"
+ " source certificate identity."));
+ usage();
+ }
+ alloclen = sizeof (*srcid) +
+ roundup(strlen(*(argv + 1)) + 1, 8);
+ srcid = malloc(alloclen);
+ if (srcid == NULL)
+ Bail("malloc(srcid)");
+ totallen += alloclen;
+ srcid->sadb_ident_type = parseidtype(*argv);
+ argv++;
+ srcid->sadb_ident_len = SADB_8TO64(alloclen);
+ srcid->sadb_ident_exttype = SADB_EXT_IDENTITY_SRC;
+ srcid->sadb_ident_reserved = 0;
+ srcid->sadb_ident_id = 0; /* Not useful here. */
+ /* Can use strcpy because I allocate my own memory. */
+ (void) strcpy((char *)(srcid + 1), *argv);
+ argv++;
+ break;
+ case TOK_DSTIDTYPE:
+ if (*argv == NULL || *(argv + 1) == NULL) {
+ warnx(gettext("Unexpected end of command"
+ " line."));
+ usage();
+ }
+ if (dstid != NULL) {
+ warnx(gettext("Can only specify single destina"
+ "tion certificate identity."));
+ usage();
+ }
+ alloclen = sizeof (*dstid) +
+ roundup(strlen(*(argv + 1)) + 1, 8);
+ dstid = malloc(alloclen);
+ if (dstid == NULL)
+ Bail("malloc(dstid)");
+ totallen += alloclen;
+ dstid->sadb_ident_type = parseidtype(*argv);
+ argv++;
+ dstid->sadb_ident_len = SADB_8TO64(alloclen);
+ dstid->sadb_ident_exttype = SADB_EXT_IDENTITY_DST;
+ dstid->sadb_ident_reserved = 0;
+ dstid->sadb_ident_id = 0; /* Not useful here. */
+ /* Can use strcpy because I allocate my own memory. */
+ (void) strcpy((char *)(dstid + 1), *argv);
+ argv++;
+ break;
+ case TOK_HARD_ALLOC:
+ case TOK_HARD_BYTES:
+ case TOK_HARD_ADDTIME:
+ case TOK_HARD_USETIME:
+ if (hard == NULL) {
+ hard = malloc(sizeof (*hard));
+ if (hard == NULL)
+ Bail("malloc(hard_lifetime)");
+ bzero(hard, sizeof (*hard));
+ hard->sadb_lifetime_exttype =
+ SADB_EXT_LIFETIME_HARD;
+ hard->sadb_lifetime_len =
+ SADB_8TO64(sizeof (*hard));
+ totallen += sizeof (*hard);
+ }
+ switch (token) {
+ case TOK_HARD_ALLOC:
+ if (hard->sadb_lifetime_allocations != 0) {
+ warnx(gettext("Can only specify single"
+ " hard allocation limit."));
+ usage();
+ }
+ hard->sadb_lifetime_allocations =
+ (uint32_t)parsenum(*argv, B_TRUE);
+ break;
+ case TOK_HARD_BYTES:
+ if (hard->sadb_lifetime_bytes != 0) {
+ warnx(gettext("Can only specify "
+ "single hard byte limit."));
+ usage();
+ }
+ hard->sadb_lifetime_bytes = parsenum(*argv,
+ B_TRUE);
+ break;
+ case TOK_HARD_ADDTIME:
+ if (hard->sadb_lifetime_addtime != 0) {
+ warnx(gettext("Can only specify "
+ "single past-add lifetime."));
+ usage();
+ }
+ hard->sadb_lifetime_addtime = parsenum(*argv,
+ B_TRUE);
+ break;
+ case TOK_HARD_USETIME:
+ if (hard->sadb_lifetime_usetime != 0) {
+ warnx(gettext("Can only specify "
+ "single past-use lifetime."));
+ usage();
+ }
+ hard->sadb_lifetime_usetime = parsenum(*argv,
+ B_TRUE);
+ break;
+ }
+ argv++;
+ break;
+ case TOK_SOFT_ALLOC:
+ case TOK_SOFT_BYTES:
+ case TOK_SOFT_ADDTIME:
+ case TOK_SOFT_USETIME:
+ if (soft == NULL) {
+ soft = malloc(sizeof (*soft));
+ if (soft == NULL)
+ Bail("malloc(soft_lifetime)");
+ bzero(soft, sizeof (*soft));
+ soft->sadb_lifetime_exttype =
+ SADB_EXT_LIFETIME_SOFT;
+ soft->sadb_lifetime_len =
+ SADB_8TO64(sizeof (*soft));
+ totallen += sizeof (*soft);
+ }
+ switch (token) {
+ case TOK_SOFT_ALLOC:
+ if (soft->sadb_lifetime_allocations != 0) {
+ warnx(gettext("Can only specify single"
+ " soft allocation limit."));
+ usage();
+ }
+ soft->sadb_lifetime_allocations =
+ (uint32_t)parsenum(*argv, B_TRUE);
+ break;
+ case TOK_SOFT_BYTES:
+ if (soft->sadb_lifetime_bytes != 0) {
+ warnx(gettext("Can only specify single"
+ " soft byte limit."));
+ usage();
+ }
+ soft->sadb_lifetime_bytes = parsenum(*argv,
+ B_TRUE);
+ break;
+ case TOK_SOFT_ADDTIME:
+ if (soft->sadb_lifetime_addtime != 0) {
+ warnx(gettext("Can only specify single"
+ " past-add lifetime."));
+ usage();
+ }
+ soft->sadb_lifetime_addtime = parsenum(*argv,
+ B_TRUE);
+ break;
+ case TOK_SOFT_USETIME:
+ if (soft->sadb_lifetime_usetime != 0) {
+ warnx(gettext("Can only specify single"
+ " past-use lifetime."));
+ usage();
+ }
+ soft->sadb_lifetime_usetime = parsenum(*argv,
+ B_TRUE);
+ break;
+ }
+ argv++;
+ break;
+ default:
+ warnx(gettext("Don't use extension %s for add/update."),
+ *(argv - 1));
+ usage();
+ break;
+ }
+ } while (token != TOK_EOF);
+
+ /*
+ * Okay, so now I have all of the potential extensions!
+ * Allocate a single contiguous buffer. Keep in mind that it'll
+ * be enough because the key itself will be yanked.
+ */
+
+ if (src == NULL && dst != NULL) {
+ /*
+ * Set explicit unspecified source address.
+ */
+ size_t lenbytes = SADB_64TO8(dst->sadb_address_len);
+
+ unspec_src = B_TRUE;
+ totallen += lenbytes;
+ src = malloc(lenbytes);
+ if (src == NULL)
+ Bail("malloc(implicit src)");
+ /* Confusing, but we're copying from DST to SRC. :) */
+ bcopy(dst, src, lenbytes);
+ src->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
+ sin6 = (struct sockaddr_in6 *)(src + 1);
+ bzero(sin6, sizeof (*sin6));
+ sin6->sin6_family = AF_INET6;
+ }
+ msg.sadb_msg_len = SADB_8TO64(totallen);
+
+ buffer = malloc(totallen);
+ nexthdr = buffer;
+ bcopy(&msg, nexthdr, sizeof (msg));
+ nexthdr += SADB_8TO64(sizeof (msg));
+ if (assoc != NULL) {
+ if (assoc->sadb_sa_spi == 0) {
+ warnx(gettext("The SPI value is missing for "
+ "the association you wish to %s."), thiscmd);
+ usage();
+ }
+ if (assoc->sadb_sa_auth == 0 && assoc->sadb_sa_encrypt == 0 &&
+ cmd == CMD_ADD) {
+ warnx(gettext("Select at least one algorithm "
+ "for this add."));
+ usage();
+ }
+
+ /* Hack to let user specify NULL ESP implicitly. */
+ if (msg.sadb_msg_satype == SADB_SATYPE_ESP &&
+ assoc->sadb_sa_encrypt == 0)
+ assoc->sadb_sa_encrypt = SADB_EALG_NULL;
+
+ /* 0 is an actual value. Print a warning if it was entered. */
+ if (assoc->sadb_sa_state == 0) {
+ if (readstate)
+ warnx(gettext(
+ "WARNING: Cannot set LARVAL SA state."));
+ assoc->sadb_sa_state = SADB_SASTATE_MATURE;
+ }
+
+ if (use_natt) {
+ if (natt_remote != NULL)
+ assoc->sadb_sa_flags |= SADB_X_SAFLAGS_NATT_REM;
+ if (natt_local != NULL)
+ assoc->sadb_sa_flags |= SADB_X_SAFLAGS_NATT_LOC;
+ }
+
+ bcopy(assoc, nexthdr, SADB_64TO8(assoc->sadb_sa_len));
+ nexthdr += assoc->sadb_sa_len;
+ /* Save the SPI for the case of an error. */
+ spi = assoc->sadb_sa_spi;
+ free(assoc);
+ } else {
+ warnx(gettext("Need SA parameters for %s."), thiscmd);
+ usage();
+ }
+
+ if (hard != NULL) {
+ bcopy(hard, nexthdr, SADB_64TO8(hard->sadb_lifetime_len));
+ nexthdr += hard->sadb_lifetime_len;
+ free(hard);
+ }
+
+ if (soft != NULL) {
+ bcopy(soft, nexthdr, SADB_64TO8(soft->sadb_lifetime_len));
+ nexthdr += soft->sadb_lifetime_len;
+ free(soft);
+ }
+
+ if (encrypt == NULL && auth == NULL && cmd == CMD_ADD) {
+ warnx(gettext("Must have at least one key for an add."));
+ usage();
+ }
+
+ if (encrypt != NULL) {
+ bcopy(encrypt, nexthdr, SADB_64TO8(encrypt->sadb_key_len));
+ nexthdr += encrypt->sadb_key_len;
+ bzero(encrypt, SADB_64TO8(encrypt->sadb_key_len));
+ free(encrypt);
+ }
+
+ if (auth != NULL) {
+ bcopy(auth, nexthdr, SADB_64TO8(auth->sadb_key_len));
+ nexthdr += auth->sadb_key_len;
+ bzero(auth, SADB_64TO8(auth->sadb_key_len));
+ free(auth);
+ }
+
+ if (srcid != NULL) {
+ bcopy(srcid, nexthdr, SADB_64TO8(srcid->sadb_ident_len));
+ nexthdr += srcid->sadb_ident_len;
+ free(srcid);
+ }
+
+ if (dstid != NULL) {
+ bcopy(dstid, nexthdr, SADB_64TO8(dstid->sadb_ident_len));
+ nexthdr += dstid->sadb_ident_len;
+ free(dstid);
+ }
+
+ if (dst != NULL) {
+ bcopy(dst, nexthdr, SADB_64TO8(dst->sadb_address_len));
+ free(dst);
+ dst = (struct sadb_address *)nexthdr;
+ nexthdr += dst->sadb_address_len;
+ } else {
+ warnx(gettext("Need destination address for %s."), thiscmd);
+ usage();
+ }
+
+ if (use_natt) {
+ if (natt_remote == NULL && natt_local == NULL) {
+ warnx(gettext(
+ "Must specify natt remote or local address "
+ "for UDP encapsulation."));
+ usage();
+ }
+
+ if (natt_lport != 0 && natt_local == NULL) {
+ warnx(gettext("If natt local port is specified, natt "
+ "local address must also be specified."));
+ usage();
+ }
+
+ if (natt_rport != 0 && natt_remote == NULL) {
+ warnx(gettext("If natt remote port is specified, natt "
+ "remote address must also be specified."));
+ usage();
+ }
+
+ if (natt_remote != NULL) {
+ free(natt_remote);
+ natt_remote = (struct sadb_address *)nexthdr;
+ nexthdr += natt_remote->sadb_address_len;
+ }
+ if (natt_local != NULL) {
+ bcopy(natt_local, nexthdr,
+ SADB_64TO8(natt_local->sadb_address_len));
+ free(natt_local);
+ natt_local = (struct sadb_address *)nexthdr;
+ nexthdr += natt_local->sadb_address_len;
+ }
+ }
+ /*
+ * PF_KEY requires a source address extension, even if the source
+ * address itself is unspecified. (See "Set explicit unspecified..."
+ * code fragment above. Destination reality check was above.)
+ */
+ bcopy(src, nexthdr, SADB_64TO8(src->sadb_address_len));
+ free(src);
+ src = (struct sadb_address *)nexthdr;
+ nexthdr += src->sadb_address_len;
+
+ if (proxy != NULL) {
+ bcopy(proxy, nexthdr, SADB_64TO8(proxy->sadb_address_len));
+ free(proxy);
+ proxy = (struct sadb_address *)nexthdr;
+ nexthdr += proxy->sadb_address_len;
+ }
+
+ doaddresses((cmd == CMD_ADD) ? SADB_ADD : SADB_UPDATE, satype, cmd,
+ srchp, dsthp, src, dst, unspec_src, buffer, totallen, spi,
+ srcport, dstport, proto, natt_lhp, natt_rhp,
+ natt_local, natt_remote, natt_lport, natt_rport);
+
+ free(buffer);
+
+ if (proxyhp != NULL && proxyhp != &dummy.he)
+ freehostent(proxyhp);
+ if (srchp != NULL && srchp != &dummy.he)
+ freehostent(srchp);
+ if (dsthp != NULL && dsthp != &dummy.he)
+ freehostent(dsthp);
+ if (natt_lhp != NULL && natt_lhp != &dummy.he)
+ freehostent(natt_lhp);
+ if (natt_rhp != NULL && natt_rhp != &dummy.he)
+ freehostent(natt_rhp);
+}
+
+/*
+ * DELETE and GET are similar, in that they only need the extensions
+ * required to _find_ an SA, and then either delete it or obtain its
+ * information.
+ */
+static void
+dodelget(int cmd, int satype, char *argv[])
+{
+ struct sadb_msg *msg = (struct sadb_msg *)get_buffer;
+ uint64_t *nextext;
+ struct sadb_sa *assoc = NULL;
+ struct sadb_address *src = NULL, *dst = NULL;
+ int next, token, sa_len;
+ char *thiscmd;
+ uint32_t spi;
+ struct hostent *srchp = NULL, *dsthp = NULL;
+ struct sockaddr_in6 *sin6;
+ boolean_t unspec_src = B_TRUE;
+ uint16_t srcport = 0, dstport = 0;
+ uint8_t proto = 0;
+
+ msg_init(msg, ((cmd == CMD_GET) ? SADB_GET : SADB_DELETE),
+ (uint8_t)satype);
+ /* Set the first extension header to right past the base message. */
+ nextext = (uint64_t *)(msg + 1);
+ bzero(nextext, sizeof (get_buffer) - sizeof (*msg));
+
+ thiscmd = (cmd == CMD_GET) ? "get" : "delete";
+
+ /* Assume last element in argv is set to NULL. */
+ do {
+ token = parseextval(*argv, &next);
+ argv++;
+ switch (token) {
+ case TOK_EOF:
+ /* Do nothing, I'm done. */
+ break;
+ case TOK_UNKNOWN:
+ warnx(gettext("Unknown extension field %s."),
+ *(argv - 1));
+ usage(); /* Will exit program. */
+ break;
+ case TOK_SPI:
+ if (assoc != NULL) {
+ warnx(gettext(
+ "Can only specify single SPI value."));
+ usage();
+ }
+ assoc = (struct sadb_sa *)nextext;
+ nextext = (uint64_t *)(assoc + 1);
+ assoc->sadb_sa_len = SADB_8TO64(sizeof (*assoc));
+ assoc->sadb_sa_exttype = SADB_EXT_SA;
+ assoc->sadb_sa_spi = htonl((uint32_t)parsenum(*argv,
+ B_TRUE));
+ spi = assoc->sadb_sa_spi;
+ argv++;
+ break;
+ case TOK_SRCPORT:
+ if (srcport != 0) {
+ warnx(gettext(
+ "Can only specify single source port."));
+ usage();
+ }
+ srcport = parsenum(*argv, B_TRUE);
+ argv++;
+ break;
+ case TOK_DSTPORT:
+ if (dstport != 0) {
+ warnx(gettext("Can only "
+ "specify single destination port."));
+ usage();
+ }
+ dstport = parsenum(*argv, B_TRUE);
+ argv++;
+ break;
+ case TOK_PROTO:
+ if (proto != 0) {
+ warnx(gettext(
+ "Can only specify single protocol."));
+ usage();
+ }
+ proto = parsenum(*argv, B_TRUE);
+ argv++;
+ break;
+ case TOK_SRCADDR:
+ case TOK_SRCADDR6:
+ if (src != NULL) {
+ warnx(gettext(
+ "Can only specify single source addr."));
+ usage();
+ }
+ sa_len = parseaddr(*argv, &srchp,
+ (token == TOK_SRCADDR6));
+ argv++;
+
+ unspec_src = B_FALSE;
+ src = (struct sadb_address *)nextext;
+ nextext = (uint64_t *)(src + 1);
+ nextext += SADB_8TO64(roundup(sa_len, 8));
+ src->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
+ src->sadb_address_len = nextext - ((uint64_t *)src);
+ if (srchp == &dummy.he) {
+ /*
+ * Single address with -n flag.
+ */
+ sin6 = (struct sockaddr_in6 *)(src + 1);
+ bzero(sin6, sizeof (*sin6));
+ sin6->sin6_family = AF_INET6;
+ bcopy(srchp->h_addr_list[0], &sin6->sin6_addr,
+ sizeof (struct in6_addr));
+ }
+ /* The rest is pre-bzeroed for us. */
+ break;
+ case TOK_DSTADDR:
+ case TOK_DSTADDR6:
+ if (dst != NULL) {
+ warnx(gettext("Can only specify single dest. "
+ "addr."));
+ usage();
+ }
+ sa_len = parseaddr(*argv, &dsthp,
+ (token == TOK_SRCADDR6));
+ argv++;
+
+ dst = (struct sadb_address *)nextext;
+ nextext = (uint64_t *)(dst + 1);
+ nextext += SADB_8TO64(roundup(sa_len, 8));
+ dst->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
+ dst->sadb_address_len = nextext - ((uint64_t *)dst);
+ if (dsthp == &dummy.he) {
+ /*
+ * Single address with -n flag.
+ */
+ sin6 = (struct sockaddr_in6 *)(dst + 1);
+ bzero(sin6, sizeof (*sin6));
+ sin6->sin6_family = AF_INET6;
+ bcopy(dsthp->h_addr_list[0], &sin6->sin6_addr,
+ sizeof (struct in6_addr));
+ }
+ /* The rest is pre-bzeroed for us. */
+ break;
+ default:
+ warnx(gettext("Don't use extension %s "
+ "for '%s' command."), *(argv - 1), thiscmd);
+ usage(); /* Will exit program. */
+ break;
+ }
+ } while (token != TOK_EOF);
+
+ /* So I have enough of the message to send it down! */
+ msg->sadb_msg_len = nextext - get_buffer;
+
+ doaddresses((cmd == CMD_GET) ? SADB_GET : SADB_DELETE, satype, cmd,
+ srchp, dsthp, src, dst, unspec_src, get_buffer,
+ sizeof (get_buffer), spi, srcport, dstport, proto,
+ NULL, NULL, NULL, NULL, 0, 0);
+
+ if (srchp != NULL && srchp != &dummy.he)
+ freehostent(srchp);
+ if (dsthp != NULL && dsthp != &dummy.he)
+ freehostent(dsthp);
+}
+
+/*
+ * "ipseckey monitor" should exit very gracefully if ^C is tapped.
+ */
+static void
+monitor_catch(int signal)
+{
+ errx(signal, gettext("Bailing on signal %d."), signal);
+}
+
+/*
+ * Loop forever, listening on PF_KEY messages.
+ */
+static void
+domonitor(boolean_t passive)
+{
+ struct sadb_msg *samsg;
+ int rc;
+
+ /* Catch ^C. */
+ (void) signal(SIGINT, monitor_catch);
+
+ samsg = (struct sadb_msg *)get_buffer;
+ if (!passive) {
+ (void) printf(gettext("Actively"));
+ msg_init(samsg, SADB_X_PROMISC, 1); /* Turn ON promisc. */
+ rc = key_write(keysock, samsg, sizeof (*samsg));
+ if (rc == -1)
+ Bail("write (SADB_X_PROMISC)");
+ } else {
+ (void) printf(gettext("Passively"));
+ }
+ (void) printf(gettext(" monitoring the PF_KEY socket.\n"));
+
+ for (; ; ) {
+ /*
+ * I assume that read() is non-blocking, and will never
+ * return 0.
+ */
+ rc = read(keysock, samsg, sizeof (get_buffer));
+ if (rc == -1)
+ Bail("read (in domonitor)");
+ (void) printf(gettext("Read %d bytes.\n"), rc);
+ /*
+ * Q: Should I use the same method of printing as GET does?
+ * A: For now, yes.
+ */
+ print_samsg(get_buffer, B_TRUE);
+ (void) putchar('\n');
+ }
+}
+
+/*
+ * Open the output file for the "save" command.
+ */
+static FILE *
+opensavefile(char *filename)
+{
+ int fd;
+ FILE *retval;
+ struct stat buf;
+
+ /*
+ * If the user specifies "-" or doesn't give a filename, then
+ * dump to stdout. Make sure to document the dangers of files
+ * that are NFS, directing your output to strange places, etc.
+ */
+ if (filename == NULL || strcmp("-", filename) == 0)
+ return (stdout);
+
+ /*
+ * open the file with the create bits set. Since I check for
+ * real UID == root in main(), I won't worry about the ownership
+ * problem.
+ */
+ fd = open(filename, O_WRONLY | O_EXCL | O_CREAT | O_TRUNC, S_IRUSR);
+ if (fd == -1) {
+ if (errno != EEXIST)
+ bail_msg("%s %s: %s", filename, gettext("open error"),
+ strerror(errno));
+ fd = open(filename, O_WRONLY | O_TRUNC, 0);
+ if (fd == -1)
+ bail_msg("%s %s: %s", filename, gettext("open error"),
+ strerror(errno));
+ if (fstat(fd, &buf) == -1) {
+ (void) close(fd);
+ bail_msg("%s fstat: %s", filename, strerror(errno));
+ }
+ if (S_ISREG(buf.st_mode) &&
+ ((buf.st_mode & S_IAMB) != S_IRUSR)) {
+ warnx(gettext("WARNING: Save file already exists with "
+ "permission %o."), buf.st_mode & S_IAMB);
+ warnx(gettext("Normal users may be able to read IPsec "
+ "keying material."));
+ }
+ }
+
+ /* Okay, we have an FD. Assign it to a stdio FILE pointer. */
+ retval = fdopen(fd, "w");
+ if (retval == NULL) {
+ (void) close(fd);
+ bail_msg("%s %s: %s", filename, gettext("fdopen error"),
+ strerror(errno));
+ }
+ return (retval);
+}
+
+/*
+ * Either mask or unmask all relevant signals.
+ */
+static void
+mask_signals(boolean_t unmask)
+{
+ sigset_t set;
+ static sigset_t oset;
+
+ if (unmask) {
+ (void) sigprocmask(SIG_SETMASK, &oset, NULL);
+ } else {
+ (void) sigfillset(&set);
+ (void) sigprocmask(SIG_SETMASK, &set, &oset);
+ }
+}
+
+/*
+ * Wrapper for inet_ntop(3SOCKET). Expects AF_INET6 address.
+ * Process the address as a AF_INET address if it is a IPv4 mapped
+ * address.
+ */
+static const char *
+do_inet_ntop(const void *addr, char *cp, size_t size)
+{
+ boolean_t isv4;
+ struct in6_addr *inaddr6 = (struct in6_addr *)addr;
+ struct in_addr inaddr;
+
+ if ((isv4 = IN6_IS_ADDR_V4MAPPED(inaddr6)) == B_TRUE) {
+ IN6_V4MAPPED_TO_INADDR(inaddr6, &inaddr);
+ }
+
+ return (inet_ntop(isv4 ? AF_INET : AF_INET6,
+ isv4 ? (void *)&inaddr : inaddr6, cp, size));
+}
+
+
+/*
+ * Assorted functions to print help text.
+ */
+#define puts_tr(s) (void) puts(gettext(s))
+
+static void
+doattrhelp()
+{
+ int i;
+
+ puts_tr("\nSA attributes:");
+
+ for (i = 0; tokens[i].string != NULL; i++) {
+ if (i%3 == 0)
+ (void) printf("\n");
+ (void) printf(" %-15.15s", tokens[i].string);
+ }
+ (void) printf("\n");
+}
+
+static void
+dohelpcmd(char *cmds)
+{
+ int cmd;
+
+ if (strcmp(cmds, "attr") == 0) {
+ doattrhelp();
+ return;
+ }
+
+ cmd = parsecmd(cmds);
+ switch (cmd) {
+ case CMD_UPDATE:
+ puts_tr("update - Update an existing SA");
+ break;
+ case CMD_ADD:
+ puts_tr("add - Add a new security association (SA)");
+ break;
+ case CMD_DELETE:
+ puts_tr("delete - Delete an SA");
+ break;
+ case CMD_GET:
+ puts_tr("get - Display an SA");
+ break;
+ case CMD_FLUSH:
+ puts_tr("flush - Delete all SAs");
+ break;
+ case CMD_DUMP:
+ puts_tr("dump - Display all SAs");
+ break;
+ case CMD_MONITOR:
+ puts_tr("monitor - Monitor all PF_KEY reply messages.");
+ break;
+ case CMD_PMONITOR:
+ puts_tr(
+"pmonitor, passive_monitor - Monitor PF_KEY messages that");
+ puts_tr(
+" reply to all PF_KEY sockets.");
+ break;
+
+ case CMD_QUIT:
+ puts_tr("quit, exit - Exit the program");
+ break;
+ case CMD_SAVE:
+ puts_tr("save - Saves all SAs to a file");
+ break;
+ case CMD_HELP:
+ puts_tr("help - Display list of commands");
+ puts_tr("help <cmd> - Display help for command");
+ puts_tr("help attr - Display possible SA attributes");
+ break;
+ default:
+ (void) printf(gettext("%s: Unknown command\n"), cmds);
+ break;
+ }
+}
+
+
+static void
+dohelp(char *cmds)
+{
+ if (cmds != NULL) {
+ dohelpcmd(cmds);
+ return;
+ }
+ puts_tr("Commands");
+ puts_tr("--------");
+ puts_tr("?, help - Display this list");
+ puts_tr("help <cmd> - Display help for command");
+ puts_tr("help attr - Display possible SA attributes");
+ puts_tr("quit, exit - Exit the program");
+ puts_tr("monitor - Monitor all PF_KEY reply messages.");
+ puts_tr("pmonitor, passive_monitor - Monitor PF_KEY messages that");
+ puts_tr(" reply to all PF_KEY sockets.");
+ puts_tr("");
+ puts_tr("The following commands are of the form:");
+ puts_tr(" <command> {SA type} {attribute value}*");
+ puts_tr("");
+ puts_tr("add (interactive only) - Add a new security association (SA)");
+ puts_tr("update (interactive only) - Update an existing SA");
+ puts_tr("delete - Delete an SA");
+ puts_tr("get - Display an SA");
+ puts_tr("flush - Delete all SAs");
+ puts_tr("dump - Display all SAs");
+ puts_tr("save - Saves all SAs to a file");
+}
+
+/*
+ * "Parse" a command line from argv.
+ */
+static void
+parseit(int argc, char *argv[])
+{
+ int cmd, satype;
+
+ if (argc == 0)
+ return;
+ cmd = parsecmd(*argv++);
+
+ switch (cmd) {
+ case CMD_HELP:
+ dohelp(*argv);
+ return;
+ case CMD_MONITOR:
+ domonitor(B_FALSE);
+ break;
+ case CMD_PMONITOR:
+ domonitor(B_TRUE);
+ break;
+ case CMD_QUIT:
+ exit(0);
+ }
+
+ satype = parsesatype(*argv);
+
+ if (satype != SADB_SATYPE_UNSPEC) {
+ argv++;
+ } else {
+ /*
+ * You must specify either "all" or a specific SA type
+ * for the "save" command.
+ */
+ if (cmd == CMD_SAVE)
+ if (*argv == NULL) {
+ warnx(gettext("Must specify a specific "
+ "SA type for save."));
+ usage();
+ } else {
+ argv++;
+ }
+ }
+
+ switch (cmd) {
+ case CMD_FLUSH:
+ doflush(satype);
+ break;
+ case CMD_ADD:
+ case CMD_UPDATE:
+ /*
+ * NOTE: Shouldn't allow ADDs or UPDATEs with keying material
+ * from the command line.
+ */
+ if (!interactive) {
+ errx(1, gettext(
+ "can't do ADD or UPDATE from the command line."));
+ }
+ if (satype == SADB_SATYPE_UNSPEC) {
+ warnx(gettext("Must specify a specific SA type."));
+ usage();
+ /* NOTREACHED */
+ }
+ /* Parse for extensions, including keying material. */
+ doaddup(cmd, satype, argv);
+ break;
+ case CMD_DELETE:
+ case CMD_GET:
+ if (satype == SADB_SATYPE_UNSPEC) {
+ warnx(gettext("Must specify a single SA type."));
+ usage();
+ /* NOTREACHED */
+ }
+ /* Parse for bare minimum to locate an SA. */
+ dodelget(cmd, satype, argv);
+ break;
+ case CMD_DUMP:
+ dodump(satype, NULL);
+ break;
+ case CMD_SAVE:
+ mask_signals(B_FALSE); /* Mask signals */
+ dodump(satype, opensavefile(argv[0]));
+ mask_signals(B_TRUE); /* Unmask signals */
+ break;
+ default:
+ warnx(gettext("Unknown command (%s)."),
+ *(argv - ((satype == SADB_SATYPE_UNSPEC) ? 1 : 2)));
+ usage();
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+ int ch;
+ FILE *infile = stdin, *savefile;
+ boolean_t dosave = B_FALSE, readfile = B_FALSE;
+
+ (void) setlocale(LC_ALL, "");
+#if !defined(TEXT_DOMAIN)
+#define TEXT_DOMAIN "SYS_TEST"
+#endif
+ (void) textdomain(TEXT_DOMAIN);
+
+ openlog("ipseckey", LOG_CONS, LOG_AUTH);
+ if (getuid() != 0) {
+ errx(1, "You must be root to run ipseckey.");
+ }
+
+ /* umask me to paranoid, I only want to create files read-only */
+ (void) umask((mode_t)00377);
+
+ while ((ch = getopt(argc, argv, "pnvf:s:")) != EOF)
+ switch (ch) {
+ case 'p':
+ pflag = B_TRUE;
+ break;
+ case 'n':
+ nflag = B_TRUE;
+ break;
+ case 'v':
+ vflag = B_TRUE;
+ break;
+ case 'f':
+ if (dosave)
+ usage();
+ infile = fopen(optarg, "r");
+ if (infile == NULL)
+ bail(optarg);
+ readfile = B_TRUE;
+ break;
+ case 's':
+ if (readfile)
+ usage();
+ dosave = B_TRUE;
+ savefile = opensavefile(optarg);
+ break;
+ default:
+ usage();
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ mypid = getpid();
+
+ keysock = socket(PF_KEY, SOCK_RAW, PF_KEY_V2);
+
+ if (keysock == -1)
+ Bail("Opening PF_KEY socket");
+
+ if (dosave) {
+ mask_signals(B_FALSE); /* Mask signals */
+ dodump(SADB_SATYPE_UNSPEC, savefile);
+ mask_signals(B_TRUE); /* Unmask signals */
+ exit(0);
+ }
+
+ if (infile != stdin || *argv == NULL) {
+ /* Go into interactive mode here. */
+ do_interactive(infile, "ipseckey> ", parseit);
+ }
+
+ parseit(argc, argv);
+
+ return (0);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/login.xml b/usr/src/cmd/cmd-inet/usr.sbin/login.xml
new file mode 100644
index 0000000000..05cd788095
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/login.xml
@@ -0,0 +1,158 @@
+<?xml version='1.0'?>
+<!DOCTYPE service_bundle SYSTEM '/usr/share/lib/xml/dtd/service_bundle.dtd.1'>
+
+<!--
+ Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ ident "%Z%%M% %I% %E% SMI"
+
+ NOTE: This service manifest is not editable; its contents will
+ be overwritten by package or patch operations, including
+ operating system upgrade. Make customizations in a different
+ file.
+
+ Service manifests for in.rlogind.
+-->
+
+<service_bundle type='manifest' name='SUNWrcmdr:rlogin'>
+
+<service
+ name='network/login'
+ type='service'
+ version='1'>
+
+ <restarter>
+ <service_fmri value='svc:/network/inetd:default' />
+ </restarter>
+
+ <property_group name='inetd' type='framework'>
+ <stability value='Evolving' />
+ <propval name='endpoint_type' type='astring' value='stream' />
+ <propval name='wait' type='boolean' value='false' />
+ <propval name='isrpc' type='boolean' value='false' />
+ </property_group>
+
+ <!--
+ RLOGIND - Good old rlogin
+ -->
+ <instance name='rlogin' enabled='false' >
+ <exec_method
+ type='method'
+ name='inetd_start'
+ exec='/usr/sbin/in.rlogind'
+ timeout_seconds='0'>
+ <method_context>
+ <method_credential user='root' group='root' />
+ </method_context>
+ </exec_method>
+
+ <exec_method
+ type='method'
+ name='inetd_disable'
+ exec=':kill'
+ timeout_seconds='0'>
+ </exec_method>
+
+ <property_group name='inetd' type='framework'>
+ <propval name='name' type='astring' value='login' />
+ <propval name='proto' type='astring' value='tcp6' />
+ </property_group>
+ </instance>
+
+ <!--
+ RLOGIND - with Kerberos authentication
+ -->
+ <instance name='klogin' enabled='false' >
+ <exec_method
+ type='method'
+ name='inetd_start'
+ exec='/usr/sbin/in.rlogind -kc'
+ timeout_seconds='0'>
+ <method_context>
+ <method_credential user='root' group='root' />
+ </method_context>
+ </exec_method>
+
+ <exec_method
+ type='method'
+ name='inetd_disable'
+ exec=':kill'
+ timeout_seconds='0'>
+ </exec_method>
+
+ <!-- Only works over IPv4 at the moment -->
+ <property_group name='inetd' type='framework'>
+ <propval name='name' type='astring' value='klogin' />
+ <propval name='proto' type='astring' value='tcp' />
+ </property_group>
+ </instance>
+
+ <!--
+
+ RLOGIND - with Kerberos authentication and encryption
+ -->
+ <instance name='eklogin' enabled='false' >
+ <exec_method
+ type='method'
+ name='inetd_start'
+ exec='/usr/sbin/in.rlogind -kcx'
+ timeout_seconds='0'>
+ <method_context>
+ <method_credential user='root' group='root' />
+ </method_context>
+ </exec_method>
+
+ <exec_method
+ type='method'
+ name='inetd_disable'
+ exec=':kill'
+ timeout_seconds='0'>
+ </exec_method>
+
+ <!-- Only works over IPv4 at the moment -->
+ <property_group name='inetd' type='framework'>
+ <propval name='name' type='astring' value='eklogin' />
+ <propval name='proto' type='astring' value='tcp' />
+ </property_group>
+ </instance>
+
+ <stability value='Unstable' />
+
+ <template>
+ <common_name>
+ <loctext xml:lang='C'>
+ remote login
+ </loctext>
+ </common_name>
+ <documentation>
+ <manpage title='in.rlogind' section='1M'
+ manpath='/usr/share/man' />
+ <manpage title='rlogind' section='1M'
+ manpath='/usr/share/man' />
+ </documentation>
+ </template>
+</service>
+
+
+</service_bundle>
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/Makefile b/usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/Makefile
new file mode 100644
index 0000000000..7b9e563f6c
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/Makefile
@@ -0,0 +1,101 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 1999,2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+# cmd/cmd-inet/usr.sbin/mipagentconfig/Makefile
+#
+
+include $(SRC)/cmd/Makefile.cmd
+include $(SRC)/cmd/cmd-inet/Makefile.cmd-inet
+
+PROG= mipagentconfig
+
+LCLOBJS=addr.o \
+ general.o \
+ gsp.o \
+ utils.o \
+ advertisements.o \
+ spi.o \
+ pool.o \
+ mipagentconfig.o
+
+COMOBJS= conflib.o
+
+HDRS= addr.h \
+ advertisements.h \
+ general.h \
+ gsp.h \
+ mipagentconfig.h \
+ pool.h \
+ spi.h \
+ utils.h
+
+
+OBJS= $(LCLOBJS) $(COMOBJS)
+
+# Currently used only for linting and style checking
+SRCS=$(LCLOBJS:%.o=%.c) $(COMOBJS:%.o=$(CMDINETCOMMONDIR)/%.c)
+
+# I18n
+POFILE=$(PROG)_prog.po
+POFILES=$(LCLOBJS:%.o=%.po)
+
+CPPFLAGS += -I$(CMDINETCOMMONDIR) -D_XOPEN_SOURCE=500 -D__EXTENSIONS__
+CLOBBERFILES += $(PROG)
+
+# Only root should be able to run mipagentconfig
+FILEMODE= 500
+
+.KEEP_STATE:
+
+.PARALLEL: $(OBJS)
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+ $(LINK.c) -o $@ $(OBJS) $(LDLIBS)
+ $(POST_PROCESS)
+
+clean:
+ $(RM) *.o
+
+check: $(HDRS:%.h=%.hcheck) $(SRCS:%.c=%.check)
+
+%.hcheck: %.h
+ $(DOT_H_CHECK)
+
+%.check: %.c
+ $(DOT_C_CHECK)
+
+install: all $(ROOTUSRSBINPROG)
+
+$(POFILE): $(POFILES)
+ $(RM) $@
+ cat $(POFILES) > $@
+
+include $(SRC)/cmd/Makefile.targ
+
+lint: lint_SRCS
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/addr.c b/usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/addr.c
new file mode 100644
index 0000000000..77c23ca9d2
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/addr.c
@@ -0,0 +1,529 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999 - 2002 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * This source file contains functions that manupilate addresses. Addresses
+ * are sections in the config file that look like:
+ *
+ * [ Address xxx.xxx.xxx.xxx ] or [ Address foo@bar.com ]
+ * Type = agent
+ * SPI = 23
+ * Pool = 7
+ *
+ * Valid for "type = agent" entries ONLY (making these valid for type=node
+ * should be fairly easy):
+ * IPSecRequest = apply <properties> : permit <properties>
+ * IPSecReply = apply <properties> : permit <properties>
+ * IPSecTunnel = apply <properties> : permit <properties>
+ * IPSecReverseTunnel = apply <properties> : permit <properties>
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libintl.h>
+#include <conflib.h>
+#include <sys/types.h>
+#include "mipagentconfig.h"
+#include "utils.h"
+#include "addr.h"
+
+/* Private Prototypes */
+static int addrTypeFunc(char *, char *, char *, int, int, char **);
+static int ipsecFunc(char *, char *, char *, int, int, char **);
+
+/*
+ * This is the function table for the Addresses. All of the addresses
+ * use general functions found in utils.c, except the Type.
+ */
+static FuncEntry addrFunctions[] = {
+ /* TAG, Section, Label, AddFunc, ChangeFunc, DeleteFunc, GetFunc */
+ { "SPI", NULL, "SPI", posIntFunc, posIntFunc, posIntFunc, posIntFunc},
+ { "Pool", NULL, "Pool", posIntFunc, posIntFunc, posIntFunc, posIntFunc},
+ { "Type", NULL, "Type", addrTypeFunc, addrTypeFunc, addrTypeFunc,
+ addrTypeFunc},
+ { "IPSecRequest", NULL, "IPSecRequest", ipsecFunc, ipsecFunc,
+ ipsecFunc, ipsecFunc},
+ { "IPSecReply", NULL, "IPSecReply", ipsecFunc, ipsecFunc, ipsecFunc,
+ ipsecFunc},
+ { "IPSecTunnel", NULL, "IPSecTunnel", ipsecFunc, ipsecFunc, ipsecFunc,
+ ipsecFunc},
+ { "IPSecReverseTunnel", NULL, "IPSecReverseTunnel", ipsecFunc,
+ ipsecFunc, ipsecFunc, ipsecFunc},
+ { NULL, NULL, NULL, NULL, NULL }
+};
+
+
+/*
+ * Function: addrFunc
+ *
+ * Arguments: char *configFile, char *Section, char *Label, int command
+ * int argc, char *argv[]
+ * Description: This function will take apart the argc/argv array, check the
+ * number of parameters, and call the appropriate function
+ * based on the command code.
+ *
+ * Returns: int (zero on success)
+ */
+int
+addrFunc(char *configFile, char *Section, char *Label, int command,
+ int argc, char *argv[])
+{
+ char DestSection[MAX_SECTION_LEN];
+ FuncEntry *funcEntry = NULL;
+ int (*function)(char *, char *, char *, int, int, char **) = NULL;
+ char *validStrings[] = {
+ "Node-Default",
+ NULL
+ };
+
+ /* Use Section and label to get rid of lint warnings */
+ Section = Label;
+
+ /* ARGV[0] should be the Address */
+ if (argc < 1) {
+ (void) fprintf(stderr,
+ gettext("Error: address identifier was not specified. "
+ "Please specify an identifier for the Address section. "
+ "Identifiers are either a valid IP address, an NAI "
+ "(e.g. bob@domain.com), or "));
+ printValidStrings(validStrings);
+ (void) fprintf(stderr, ".\n");
+ return (-1);
+ }
+
+ /* Validate Address */
+ if (!ipValid(argv[0]) && !naiValid(argv[0])) {
+ /* Ok not a valid address, check for Defaults */
+ if (checkValidStrings(validStrings, argv[0])) {
+ (void) fprintf(stderr,
+ gettext("Error: invalid identifier for "
+ "Address section. Identifier must "
+ "be a valid IP address, a valid NAI "
+ "(e.g. bob@domain.com), or "));
+ printValidStrings(validStrings);
+ (void) fprintf(stderr, ".\n");
+ return (-1);
+ }
+ }
+
+ /* Build our Section */
+ (void) sprintf(DestSection, "Address %s", argv[0]);
+
+ /* Finally, look up our functions and call them based on the dest */
+ if (argc > 1) {
+ funcEntry = getFunctions(addrFunctions, argv[1]);
+ if (!funcEntry) {
+ (void) fprintf(stderr,
+ gettext("Error: command '%s' is not valid "
+ "for %s.\n"),
+ Command2Str(command), argv[1]);
+ return (-1);
+ }
+ }
+
+ /* Now check the particular function we need. */
+ switch (command) {
+ case Add:
+ if (argc == 1) {
+ /* A raw add Warn the user */
+ (void) fprintf(stderr,
+ gettext("Warning: attributes will be created as "
+ "parameters are added.\n Example: "
+ "mipagentconfig add addr 192.168.168.1 SPI 5\n "
+ "will add the address, and add the SPI "
+ "configuration to it.\n"));
+ return (0);
+ }
+ function = funcEntry->addFunc;
+ break;
+ case Change:
+ if (argc == 1) {
+ (void) fprintf(stderr,
+ gettext("Error: cannot change the identifier of an "
+ "Address section. Delete, and make a new one.\n"));
+ return (-1);
+ }
+ function = funcEntry->changeFunc;
+ break;
+ case Delete:
+ if (argc == 1) {
+ return DeletePrivateProfileSection(DestSection,
+ configFile);
+ }
+ function = funcEntry->deleteFunc;
+ break;
+ case Get:
+ if (argc == 1) {
+ sectionDump(configFile, DestSection,
+ addrFunctions);
+ return (0);
+ }
+ function = funcEntry->getFunc;
+ break;
+ }
+
+ /* Print error if this function is not allowed (null in table) */
+ if (!function) {
+ (void) fprintf(stderr,
+ gettext("Error: <%s> is not valid for '%s' command.\n"),
+ argv[0], Command2Str(command));
+ return (-1);
+ }
+
+ /* And finally, call function */
+ return (function(configFile, DestSection, funcEntry->Label,
+ command, argc-2, &argv[2]));
+
+} /* addrFunc */
+
+/*
+ * Function: addrTypeFunc
+ *
+ * Arguments: char *configFile, char *Section, char *Label, int command
+ * int argc, char *argv[]
+ * Description: This function verifys the Type option in address sections.
+ *
+ * Returns: int
+ */
+static int
+addrTypeFunc(char *configFile, char *Section, char *Label, int command,
+ int argc, char *argv[])
+{
+ char buffer[MAX_VALUE_LEN];
+ int rc, LabelExists;
+ char *validStrings[] = {
+ "Agent",
+ "Node",
+ NULL
+ };
+
+ /* Check to see if label already exists */
+ rc = GetPrivateProfileString(Section, Label, "", buffer,
+ MAX_VALUE_LEN-1, configFile);
+ if (!rc)
+ LabelExists = TRUE;
+ else
+ LabelExists = FALSE;
+
+ switch (command) {
+ case Add:
+ /* Now, check for the parameters. */
+ if (argc != 1) {
+ (void) fprintf(stderr,
+ gettext("Error: entry type wasn't specified. "
+ "Please specify the type of entry for [%s]. "
+ "The type must be one of ("), Section);
+ printValidStrings(validStrings);
+ (void) fprintf(stderr, ").\n");
+ return (-1);
+ }
+
+ if (LabelExists) {
+ (void) fprintf(stderr,
+ gettext("Error: %s is already configured in [%s]:\n"
+ "\t%s = %s\n"),
+ Label, Section, Label, buffer);
+ return (-1);
+ }
+ if (!checkValidStrings(validStrings, argv[0])) {
+ /* Add it! */
+ rc = WritePrivateProfileString(Section, Label,
+ argv[0], configFile);
+ if (rc) {
+ (void) fprintf(stderr, "%s\n", ErrorString);
+ return (rc);
+ }
+ return (0);
+ } else {
+ (void) fprintf(stderr,
+ gettext("Error: Address type must be one of ("));
+ printValidStrings(validStrings);
+ (void) fprintf(stderr, ").\n");
+ return (-1);
+ }
+ break;
+
+
+ case Delete:
+ if (!LabelExists) {
+ (void) fprintf(stderr,
+ gettext("Error: %s is not configured in [%s].\n"),
+ Label, Section);
+ return (-1);
+ }
+ rc = DeletePrivateProfileLabel(Section, Label, configFile);
+ if (rc) {
+ (void) fprintf(stderr, "%s\n", ErrorString);
+ return (rc);
+ }
+ return (rc);
+ break;
+
+
+ case Change:
+ /* Now, check for the parameters. */
+ if (argc != 1) {
+ (void) fprintf(stderr,
+ gettext("Error: entry type wasn't specified. "
+ "Please specify the type [%s] is to be changed to."
+ " Valid types are one of ("), Section);
+ printValidStrings(validStrings);
+ (void) fprintf(stderr, ").\n");
+ return (-1);
+ }
+
+ if (!LabelExists) {
+ (void) fprintf(stderr,
+ gettext("Error: %s is not configured in [%s].\n"),
+ Label, Section);
+ return (-1);
+ }
+
+ if (!checkValidStrings(validStrings, argv[0])) {
+ /* Change it! */
+ rc = WritePrivateProfileString(Section, Label,
+ argv[0], configFile);
+ if (rc) {
+ (void) fprintf(stderr, "%s\n", ErrorString);
+ return (rc);
+ }
+ return (0);
+ } else {
+ (void) fprintf(stderr,
+ gettext("Error: type must be one of ("));
+ printValidStrings(validStrings);
+ (void) fprintf(stderr, ").\n");
+ return (-1);
+ }
+ break;
+
+ case Get:
+ if (!LabelExists) {
+ (void) fprintf(stderr,
+ gettext("Error: %s is not configured in [%s].\n"),
+ Label, Section);
+ return (-1);
+ }
+ (void) printf(gettext("[%s]\n\t%s = %s\n"),
+ Section, Label, buffer);
+ return (0);
+ break;
+
+
+
+ default:
+ (void) fprintf(stderr,
+ gettext("Error: Invalid command code!\n"));
+ return (-1);
+ } /* switch (command) */
+} /* addrTypeFunc */
+
+
+/*
+ * Function: iposecFunc()
+ *
+ * Arguments: char *configFile, char *Section, char *Label, int command
+ * int argc, char *argv[]
+ * Description: This function verifys the ipsec properties.
+ *
+ * Returns: int
+ */
+static int
+ipsecFunc(char *configFile, char *Section, char *Label, int command,
+ int argc, char *argv[])
+{
+ char buffer[MAX_VALUE_LEN], pbuf[MAX_VALUE_LEN] = "",
+ *policy, *policyP;
+ int rc, LabelExists;
+ extern char *validIPsecAction[];
+
+ /* Check to see if label already exists */
+ rc = GetPrivateProfileString(Section, Label, "", buffer,
+ MAX_VALUE_LEN-1, configFile);
+ if (!rc)
+ LabelExists = TRUE;
+ else
+ LabelExists = FALSE;
+
+ /*
+ * mipagentconfig differs from mipagent here in that the configuration
+ * is broken into argv[]'s, where as when we read this in mipagent it
+ * comes in one string. For the sake of common code, we put all the
+ * argv[]'s into one buffer
+ */
+ for (rc = 0; rc < argc; rc++) {
+ (void) strcat(pbuf, argv[rc]);
+ (void) strcat(pbuf, " ");
+ }
+
+ switch (command) {
+ case Add:
+ /* what are we adding? */
+ if (argc < 2) {
+ /*
+ * Must have at least "<action> {<property>}" = 2.
+ * Then again, "<action> {<<tag> <alg>>} = 3...
+ */
+ (void) fprintf(stderr,
+ gettext("Error: IPsec policy is incomplete. "
+ "Please specify the complete IPsec policy. "
+ "See ipsec(7p).\n"));
+ return (-1);
+ }
+
+ if (LabelExists) {
+ (void) fprintf(stderr,
+ gettext("Error: %s is already configured in [%s]:\n"
+ "\t%s = %s\n"),
+ Label, Section, Label, buffer);
+ return (-1);
+ }
+
+ /*
+ * Determine if this is a valid policy. Note: we have to do
+ * this one IPsec Policy at a time.
+ */
+ policy = strdup(pbuf);
+ policyP = policy; /* strtok() is destructive */
+
+ while ((policy = strtok(policy, IPSP_SEPARATOR)) != NULL) {
+ if (isIPsecPolicyValid(policy, NULL) != TRUE) {
+ (void) fprintf(stderr,
+ gettext("Error: policy %s is not valid "
+ "Policy may only contain <"), policy);
+ (void) printValidStrings(validIPsecAction);
+ (void) fprintf(stderr,
+ gettext("> as actions, and valid IPsec"
+ "<properties>. See ipsec(7P).\n"));
+ return (-1);
+ }
+ policy = NULL;
+ }
+
+ free(policyP);
+
+ /* Checks out, so add it */
+ rc = WritePrivateProfileString(Section, Label, pbuf,
+ configFile);
+
+ if (rc) {
+ (void) fprintf(stderr, "%s\n", ErrorString);
+ return (rc);
+ }
+
+ return (0);
+
+
+ case Delete:
+ if (!LabelExists) {
+ (void) fprintf(stderr,
+ gettext("Error: %s is not configured in [%s].\n"),
+ Label, Section);
+ return (-1);
+ }
+
+ rc = DeletePrivateProfileLabel(Section, Label, configFile);
+ if (rc)
+ (void) fprintf(stderr, "%s\n", ErrorString);
+ return (rc);
+
+
+ case Change:
+ if (argc < 2) {
+ /* must have at least "<action> {<properties>}" = 2 */
+ (void) fprintf(stderr,
+ gettext("Error: IPsec Policy incomplete. "
+ "Please specify the complete new IPsec Policy. "
+ "See ipsec(7P).\n"));
+ return (-1);
+ }
+
+ if (!LabelExists) {
+ (void) fprintf(stderr,
+ gettext("Error: %s is not configured in [%s].\n"),
+ Label, Section);
+ return (-1);
+ }
+
+ /*
+ * Is the format of this setting valid? Note: we have to do
+ * this one IPsec Policy at a time.
+ */
+ policy = strdup(pbuf);
+ policyP = policy; /* strtok() is destructive */
+
+ while ((policy = strtok(policy, IPSP_SEPARATOR)) != NULL) {
+ if (isIPsecPolicyValid(policy, NULL) != TRUE) {
+ (void) fprintf(stderr,
+ gettext("Error: %s is not a valid IPsec "
+ "policy. Policy may only contain <"),
+ policy);
+ (void) printValidStrings(validIPsecAction);
+ (void) fprintf(stderr,
+ gettext("> as actions, and valid IPsec "
+ "<properties>. See ipsec(7P).\n"));
+ return (-1);
+ }
+ policy = NULL;
+ }
+
+ free(policyP);
+
+ /* Checks out, so change it */
+ rc = WritePrivateProfileString(Section, Label, pbuf,
+ configFile);
+
+ if (rc) {
+ (void) fprintf(stderr, "%s\n", ErrorString);
+ return (rc);
+ }
+
+ return (0);
+
+
+ case Get:
+ if (!LabelExists) {
+ (void) fprintf(stderr,
+ gettext("Error: %s is not configigured in [%s].\n"),
+ Label, Section);
+ return (-1);
+ }
+ (void) printf(gettext("[%s]\n\t%s = %s\n"),
+ Section, Label, buffer);
+ return (0);
+
+
+ default:
+ (void) fprintf(stderr,
+ gettext("Error: Invalid command code!\n"));
+ return (-1);
+ } /* switch (command) */
+} /* ipsecFunc */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/addr.h b/usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/addr.h
new file mode 100644
index 0000000000..0741cc16bf
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/addr.h
@@ -0,0 +1,46 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _ADDR_H
+#define _ADDR_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Address Pool Prototypes
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int addrFunc(char *, char *, char *, int, int, char **);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ADDR_H */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/advertisements.c b/usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/advertisements.c
new file mode 100644
index 0000000000..9feb1e2abc
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/advertisements.c
@@ -0,0 +1,160 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999-2002 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Functions to manipulate advertisements
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libintl.h>
+#include <conflib.h>
+#include "mipagentconfig.h"
+#include "utils.h"
+#include "advertisements.h"
+
+
+static FuncEntry advFunctions[] = {
+ { "advLifeTime", NULL, "advLifeTime", posIntFunc, posIntFunc,
+ posIntFunc, posIntFunc},
+ { "regLifeTime", NULL, "regLifeTime", posIntFunc, posIntFunc,
+ posIntFunc, posIntFunc},
+ { "advFrequency", NULL, "advFrequency", posIntFunc, posIntFunc,
+ posIntFunc, posIntFunc},
+ { "homeAgent", NULL, "homeAgent", ynFunc, ynFunc, ynFunc, ynFunc},
+ { "foreignAgent", NULL, "foreignAgent", ynFunc, ynFunc, ynFunc,
+ ynFunc},
+ { "prefixLengthExt", NULL, "prefixLengthExt", ynFunc, ynFunc, ynFunc,
+ ynFunc},
+ { "reverseTunnel", NULL, "reverseTunnel", fhFunc, fhFunc, fhFunc,
+ fhFunc},
+ { "reverseTunnelRequired", NULL, "reverseTunnelRequired", fhFunc,
+ fhFunc, fhFunc, fhFunc},
+ { "advInitCount", NULL, "advInitCount", posIntFunc, posIntFunc,
+ posIntFunc, posIntFunc},
+ { "advLimitUnsolicited", NULL, "advLimitUnsolicited", ynFunc,
+ ynFunc, ynFunc, ynFunc},
+ { NULL, NULL, NULL, NULL, NULL }
+};
+
+/*
+ * Function: advFunc
+ *
+ * Arguments: char *configFile, char *Section, char *Label, int command,
+ * int argc, char *argv[]
+ *
+ * Description: This function implements all the manipulation functions
+ * for the advertisements sections
+ *
+ * Returns: int
+ */
+int
+advFunc(char *configFile, char *Section, char *Label, int command, int argc,
+ char *argv[])
+{
+ char DestSection[MAX_SECTION_LEN];
+ FuncEntry *funcEntry = NULL;
+ int (*function)(char *, char *, char *, int, int, char **) = NULL;
+
+ /* Get rid of lint warnings about unused parameters */
+ Section = Label;
+
+ /* ARGV[0] should be the interface */
+ if (argc < 1) {
+ (void) fprintf(stderr,
+ gettext("Error: advertisement interface not specified. "
+ "Please specify the interface name.\n"));
+ return (-1);
+ }
+
+ /* Build our Section */
+ (void) sprintf(DestSection, "Advertisements %s", argv[0]);
+
+ /* Finally, look up our functions and call them based on the dest */
+ if (argc > 1) {
+ funcEntry = getFunctions(advFunctions, argv[1]);
+ if (!funcEntry) {
+ (void) fprintf(stderr,
+ gettext("Error: %s does not support '%s'.\n"),
+ Command2Str(command), argv[1]);
+ return (-1);
+ }
+ }
+
+ /* Now check the particular function we need. */
+ switch (command) {
+ case Add:
+ if (argc == 1) {
+ /*
+ * A raw add. Add an empty section by adding
+ * a lable, then deleting it.
+ */
+ return (addEmptySection(configFile, DestSection));
+ }
+ function = funcEntry->addFunc;
+ break;
+ case Change:
+ if (argc == 1) {
+ (void) fprintf(stderr,
+ gettext("Error: cannot change the identifier of an "
+ "Address section. Delete, and make a new one.\n"));
+ return (-1);
+ }
+ function = funcEntry->changeFunc;
+ break;
+ case Delete:
+ if (argc == 1) {
+ return (DeletePrivateProfileSection(DestSection,
+ configFile));
+ }
+ function = funcEntry->deleteFunc;
+ break;
+ case Get:
+ if (argc == 1) {
+ sectionDump(configFile, DestSection,
+ advFunctions);
+ return (0);
+ }
+ function = funcEntry->getFunc;
+ break;
+ }
+
+ if (!function) {
+ (void) fprintf(stderr,
+ gettext("Error: %s does not support '%s'.\n"),
+ Command2Str(command), argv[0]);
+ return (-1);
+ }
+
+ /* And finally, call function */
+ return (function(configFile, DestSection, funcEntry->Label,
+ command, argc-2, &argv[2]));
+
+} /* advFunc */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/advertisements.h b/usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/advertisements.h
new file mode 100644
index 0000000000..af2159995a
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/advertisements.h
@@ -0,0 +1,42 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _ADVERTISEMENTS_H
+#define _ADVERTISEMENTS_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int advFunc(char *, char *, char *, int, int, char **);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ADVERTISEMENTS_H */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/general.c b/usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/general.c
new file mode 100644
index 0000000000..9c76f799fc
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/general.c
@@ -0,0 +1,188 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999 - 2002 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * general.c -- General variable modification
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libintl.h>
+#include <conflib.h>
+#include "mipagentconfig.h"
+#include "utils.h"
+#include "general.h"
+
+
+/*
+ * Function: logFunc
+ *
+ * Arguments: char *configFile, char *Section, char *Label, int command
+ * int argc, char *argv[]
+ *
+ * Description: This function implements all the manipulation functions
+ * for the logVerbosity key.
+ *
+ * Returns: int
+ */
+int
+logFunc(char *configFile, char *Section, char *Label, int command,
+ int argc, char *argv[])
+{
+ char buffer[MAX_VALUE_LEN] = {0};
+ int rc;
+ int LabelExists;
+ char *validStrings[] = {
+ "quiet",
+ "low",
+ "norm",
+ "all",
+ NULL
+ };
+
+ /* Check to see if label already exists */
+ rc = GetPrivateProfileString(Section, Label, "", buffer,
+ MAX_VALUE_LEN-1, configFile);
+ if (!rc)
+ LabelExists = TRUE;
+ else
+ LabelExists = FALSE;
+
+ switch (command) {
+ case Add:
+ /* Check to see if label already exists */
+ if (LabelExists) {
+ (void) fprintf(stderr,
+ gettext("Error: %s is already configured in [%s]:\n"
+ "\t%s = %s\n"),
+ Label, Section, Label, buffer);
+ return (-1);
+ }
+
+ /* Now, check for the parameters. */
+ if (argc != 1) {
+ (void) fprintf(stderr,
+ gettext("Error: Must specify log level (one of: "));
+ printValidStrings(validStrings);
+ (void) fprintf(stderr, ").\n");
+ return (-1);
+ }
+
+ if (!checkValidStrings(validStrings, argv[0])) {
+ /* Add it! */
+ rc = WritePrivateProfileString(Section, Label,
+ argv[0], configFile);
+ if (rc) {
+ (void) fprintf(stderr, "%s\n", ErrorString);
+ return (rc);
+ }
+ return (0);
+ } else {
+ (void) fprintf(stderr,
+ gettext("Error: [%s] configuration for %s must be "
+ "one of ("), Section, Label);
+ printValidStrings(validStrings);
+ (void) fprintf(stderr, ").\n");
+ return (-1);
+ }
+ break;
+
+
+ case Delete:
+ if (!LabelExists) {
+ (void) fprintf(stderr,
+ gettext("Error: %s is not configured in [%s].\n"),
+ Label, Section);
+ return (-1);
+ }
+ rc = DeletePrivateProfileLabel(Section, Label, configFile);
+ if (rc)
+ (void) fprintf(stderr, "Error: %s\n", ErrorString);
+
+ return (rc);
+
+
+ case Change:
+ if (!LabelExists) {
+ (void) fprintf(stderr,
+ gettext("Error: %s is not configured in [%s].\n"),
+ Label, Section);
+ return (-1);
+ }
+
+ /* Now, check for the parameters. */
+ if (argc != 1) {
+ (void) fprintf(stderr,
+ gettext("Error: log level was not specified. "
+ "Please specify the desired log level. "
+ "Valid log levels are one of ("));
+ printValidStrings(validStrings);
+ (void) fprintf(stderr, ").\n");
+ return (-1);
+ }
+
+ if (!checkValidStrings(validStrings, argv[0])) {
+ /* Change it! */
+ rc = WritePrivateProfileString(Section, Label,
+ argv[0], configFile);
+ if (rc) {
+ (void) fprintf(stderr, "%s\n", ErrorString);
+ return (rc);
+ }
+ return (0);
+ } else {
+ (void) fprintf(stderr,
+ gettext("Error: Log level %s is not supported. "
+ "Log level must be one of ("),
+ argv[0]);
+ printValidStrings(validStrings);
+ (void) fprintf(stderr, ").\n");
+ return (-1);
+ }
+ break;
+
+ case Get:
+ if (!LabelExists) {
+ (void) fprintf(stderr,
+ gettext("Error: %s is not configured in [%s].\n"),
+ Label, Section);
+ return (-1);
+ }
+ (void) printf(gettext("[%s]\n\t%s = %s\n"),
+ Section, Label, buffer);
+ return (0);
+ break;
+
+ default:
+ (void) fprintf(stderr,
+ gettext("Error: Invalid command code!\n"));
+ return (-1);
+ } /* switch (command) */
+} /* logFunc */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/general.h b/usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/general.h
new file mode 100644
index 0000000000..dd8b98e1da
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/general.h
@@ -0,0 +1,46 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _GENERAL_H
+#define _GENERAL_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * general.h -- General variable modification
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int logFunc(char *, char *, char *, int, int, char **);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _GENERAL_H */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/gsp.c b/usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/gsp.c
new file mode 100644
index 0000000000..a3b344e3f2
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/gsp.c
@@ -0,0 +1,188 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999 - 2002 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * gsp.c -- GlobalSecurityParameters variable modification
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libintl.h>
+#include <conflib.h>
+#include "mipagentconfig.h"
+#include "utils.h"
+#include "gsp.h"
+
+/*
+ * Function: keyDistributionFunc
+ *
+ * Arguments: configFile, command, arguments
+ *
+ * Description: This function sets the "keyDistribution" label of the
+ * GlobalSecurityParameters section. The only valid value
+ * at this time is "files". Eventually, we will allow some
+ * kind of external AAA to be configured here (diameter,
+ * RADIUS, SunDS, etc.)
+ *
+ * Returns: int
+ *
+ */
+int
+keyDistributionFunc(char *configFile, char *Section, char *Label, int command,
+ int argc, char *argv[])
+{
+ char buffer[MAX_VALUE_LEN] = {0};
+ int rc;
+ int LabelExists;
+ char *validStrings[] = {
+ "files",
+ NULL
+ };
+
+ /* Check to see if label already exists */
+ rc = GetPrivateProfileString(Section, Label, "", buffer,
+ MAX_VALUE_LEN-1, configFile);
+ if (!rc)
+ LabelExists = TRUE;
+ else
+ LabelExists = FALSE;
+
+ switch (command) {
+ case Add:
+ if (LabelExists) {
+ (void) fprintf(stderr,
+ gettext("Error: %s is already configured in [%s]:\n"
+ "\t%s = %s\n"),
+ Label, Section, Label, buffer);
+ return (-1);
+ }
+
+ /* Now, check for the parameters. */
+ if (argc != 1) {
+ (void) fprintf(stderr,
+ gettext("Usage: add keyDistribution <type>\n"
+ "\tWhere <type> is one of ("));
+ printValidStrings(validStrings);
+ (void) fprintf(stderr, ").\n");
+ return (-1);
+ }
+
+ if (!checkValidStrings(validStrings, argv[0])) {
+ /* Add it! */
+ rc = WritePrivateProfileString(Section, Label,
+ argv[0], configFile);
+ if (rc) {
+ (void) fprintf(stderr, "%s\n", ErrorString);
+ return (rc);
+ }
+ return (0);
+ } else {
+ (void) fprintf(stderr,
+ gettext("Error: keyDistribution must be one of ("));
+ printValidStrings(validStrings);
+ (void) fprintf(stderr, ").\n");
+ return (-1);
+ }
+ break;
+
+
+ case Delete:
+ if (!LabelExists) {
+ (void) fprintf(stderr,
+ gettext("Error: %s is not configured in [%s].\n"),
+ Label, Section);
+ return (-1);
+ }
+ rc = DeletePrivateProfileLabel(Section, Label, configFile);
+ if (rc) {
+ (void) fprintf(stderr, "%s\n", ErrorString);
+ return (rc);
+ }
+ return (rc);
+ break;
+
+
+ case Change:
+ /* Now, check for the parameters. */
+ if (argc != 1) {
+ (void) fprintf(stderr,
+ gettext("Usage: change keyDistribution <type>\n"
+ "<\tWhere <type> is one of ("));
+ printValidStrings(validStrings);
+ (void) fprintf(stderr, ").\n");
+ return (-1);
+ }
+
+ if (!LabelExists) {
+ (void) fprintf(stderr,
+ gettext("Error: %s is not configured in [%s].\n"),
+ Section, Label);
+ return (-1);
+ }
+
+ if (!checkValidStrings(validStrings, argv[0])) {
+ /* Change it! */
+ rc = WritePrivateProfileString(Section, Label,
+ argv[0], configFile);
+ if (rc) {
+ (void) fprintf(stderr, "%s\n", ErrorString);
+ return (rc);
+ }
+ return (0);
+ } else {
+ (void) fprintf(stderr,
+ gettext("Error: keyDistribution must be one of ("));
+ printValidStrings(validStrings);
+ (void) fprintf(stderr, ").\n");
+ return (-1);
+ }
+ break;
+
+ case Get:
+ rc = GetPrivateProfileString(Section, Label, "", buffer,
+ MAX_VALUE_LEN-1, configFile);
+ if (rc) {
+ (void) fprintf(stderr,
+ gettext("Error: %s is not configured in [%s].\n"),
+ Label, Section);
+ return (-1);
+ }
+ (void) printf(gettext("[%s]\n\t%s = %s\n"),
+ Section, Label, buffer);
+
+ return (0);
+ break;
+
+ default:
+ (void) fprintf(stderr,
+ gettext("Error: Invalid command code!\n"));
+ return (-1);
+ } /* switch (command) */
+} /* keyDistributionFunc */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/gsp.h b/usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/gsp.h
new file mode 100644
index 0000000000..f1ca77e481
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/gsp.h
@@ -0,0 +1,46 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _GSP_H
+#define _GSP_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * gsp.h -- GlobalSecurityParameters variable modification
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int keyDistributionFunc(char *, char *, char *, int, int, char **);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _GSP_H */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/inc.flg b/usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/inc.flg
new file mode 100644
index 0000000000..373eff0264
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/inc.flg
@@ -0,0 +1,29 @@
+#!/bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright (c) 1999 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+find_files "s.*" usr/src/cmd/cmd-inet/common
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/mipagentconfig.c b/usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/mipagentconfig.c
new file mode 100644
index 0000000000..dae83a8686
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/mipagentconfig.c
@@ -0,0 +1,201 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999-2002 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * mipagentconfig.c
+ *
+ * This program is used to manipulate configuration files for mobile ip.
+ * It is used as follows (for more info on usage, check the man page(s):
+ *
+ * foo$ mipagentconfig add address 192.168.168.10 SPI 7
+ *
+ * The parameters are: Command, destination, [values . . . ]
+ *
+ * For each command/destination, an entry is looked up in a function table, and
+ * the appropriate function is called with the rest of the command line as
+ * parameters.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libintl.h>
+#include <locale.h>
+#include "mipagentconfig.h"
+#include "utils.h"
+
+#include "general.h"
+#include "gsp.h"
+#include "advertisements.h"
+#include "spi.h"
+#include "pool.h"
+#include "addr.h"
+
+/*
+ * This is the string - to - enum table for command codes.
+ */
+CommandTable Commands[] = {
+ { "add", Add },
+ { "change", Change },
+ { "delete", Delete },
+ { "del", Delete }, /* for lazy typers */
+ { "get", Get },
+ { NULL, NULL }
+};
+
+/*
+ * Function Notes:
+ *
+ * The first parameter passed to all functions is the command. This
+ * is to allow the same function to be used for all commands, add
+ * change, delete or get.
+ *
+ * You do not have to support all commands. For any unsupported command,
+ * just use NULL.
+ */
+static FuncEntry Functions[] = {
+ /* Lable or tag, Section, SectionLabel, ADD, CHANGE, DELETE, GET */
+ /* From general.h */
+ { "logVerbosity", "General", "logVerbosity", logFunc,
+ logFunc, logFunc, logFunc },
+ { "AdvertiseNAI", "General", "AdvertiseNAI", ynFunc, ynFunc, ynFunc,
+ ynFunc },
+ /* from gsp.h */
+ { "HA-FAauth", "GlobalSecurityParameters", "HA-FAauth", ynFunc,
+ ynFunc, ynFunc, ynFunc },
+ { "MN-FAauth", "GlobalSecurityParameters", "MN-FAauth", ynFunc,
+ ynFunc, ynFunc, ynFunc },
+ { "Challenge", "GlobalSecurityParameters", "Challenge", ynFunc,
+ ynFunc, ynFunc, ynFunc },
+ { "maxClockSkew", "GlobalSecurityParameters", "maxClockSkew",
+ posIntFunc, posIntFunc, posIntFunc, posIntFunc },
+ { "keyDistribution", "GlobalSecurityParameters", "keyDistribution",
+ keyDistributionFunc, keyDistributionFunc, keyDistributionFunc,
+ keyDistributionFunc },
+ { "adv", NULL, NULL, advFunc, advFunc, advFunc, advFunc},
+ { "SPI", NULL, NULL, spiFunc, spiFunc, spiFunc, spiFunc},
+ { "Pool", NULL, NULL, poolFunc, poolFunc, poolFunc, poolFunc},
+ { "addr", NULL, NULL, addrFunc, addrFunc, addrFunc, addrFunc },
+ { NULL, NULL, NULL, NULL, NULL }
+};
+
+#define USAGE "%s: [ -f configfile ] command dest [parameters . . .] \n"
+
+#define CONFIG_FILE "/etc/inet/mipagent.conf"
+
+int
+main(int argc, char *argv[])
+{
+ extern char *optarg;
+ extern int optind;
+ int c;
+ char *configFile = CONFIG_FILE;
+ int argsLeft;
+ FuncEntry *funcEntry;
+ Command command = Add;
+ int (*function)(char *, char *, char *, int, int, char **) = NULL;
+
+ (void) setlocale(LC_ALL, "");
+#if !defined(TEXT_DOMAIN)
+#define TEXT_DOMAIN "SYS_TEST"
+#endif
+
+ (void) textdomain(TEXT_DOMAIN);
+
+ while ((c = getopt(argc, argv, "f:?h")) != EOF) {
+ switch (c) {
+ case 'f' : /* Change the config file */
+ configFile = optarg;
+ break;
+ case '?':
+ case 'h': /* print usage */
+ (void) fprintf(stderr, USAGE, argv[0]);
+ return (0);
+ }
+ } /* while . . . getopt */
+
+ argsLeft = argc-optind;
+
+ if (argsLeft < 2) {
+ (void) fprintf(stderr,
+ gettext("Error: Not enough arguments.\n"));
+ (void) fprintf(stderr, USAGE, argv[0]);
+ return (-1);
+ }
+
+ /* Now, Check the command */
+ command = Str2Command(argv[optind]);
+ if (command < 0) {
+ (void) fprintf(stderr, gettext("Error: Invalid command <%s>\n"),
+ argv[optind]);
+ (void) fprintf(stderr, USAGE, argv[0]);
+ return (-1);
+ }
+
+
+ /* Finally, look up our functions and call them based on the dest */
+ funcEntry = getFunctions(Functions, argv[optind+1]);
+
+ if (funcEntry) {
+ /* Now check the particular function we need. */
+ switch (command) {
+ case Add:
+ function = funcEntry->addFunc;
+ break;
+ case Change:
+ function = funcEntry->changeFunc;
+ break;
+ case Delete:
+ function = funcEntry->deleteFunc;
+ break;
+ case Get:
+ function = funcEntry->getFunc;
+ break;
+ }
+ } else {
+ (void) fprintf(stderr,
+ gettext("Error: <%s> is not valid for <%s>\n"),
+ argv[optind+1], argv[optind]);
+ (void) fprintf(stderr, USAGE, argv[0]);
+ return (-1);
+ }
+
+ if (!function) {
+ (void) fprintf(stderr,
+ gettext("Error: %s does not support '%s'.\n"),
+ argv[optind+1], argv[optind]);
+ (void) fprintf(stderr, USAGE, argv[0]);
+ return (-1);
+ }
+
+ /* And finally, call function */
+ return (function(configFile, funcEntry->Section, funcEntry->Label,
+ command, argsLeft-2, &argv[optind+2]));
+
+} /* main */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/mipagentconfig.h b/usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/mipagentconfig.h
new file mode 100644
index 0000000000..b0acf45ab9
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/mipagentconfig.h
@@ -0,0 +1,81 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _MIPAGENTCONFIG_H
+#define _MIPAGENTCONFIG_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * mipagentconfig.h -- Header file
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * These entries are used to build tables of actions to manipulate the config
+ * file. The tag is the entry the user would type. Section is the section
+ * in the config file that this entry will modify, and Label is the label
+ * in the section of the config file to be modified.
+ *
+ * The functions are pointers to functions to be called to add, change,
+ * delete, or get the value.
+ */
+typedef struct {
+ char *tag; /* Tag used on command line */
+ char *Section; /* Section in config file */
+ char *Label; /* Label in section in config file */
+ int (*addFunc)(char *, char *, char *, int, int, char **);
+ int (*changeFunc)(char *, char *, char *, int, int, char **);
+ int (*deleteFunc)(char *, char *, char *, int, int, char **);
+ int (*getFunc)(char *, char *, char *, int, int, char **);
+} FuncEntry;
+
+/*
+ * Commands
+ */
+typedef enum c {
+ Add = 0,
+ Change,
+ Delete,
+ Get
+} Command;
+
+/*
+ * This table is used to equate command codes with strings.
+ */
+typedef struct {
+ char *string;
+ Command command;
+} CommandTable;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MIPAGENTCONFIG_H */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/pool.c b/usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/pool.c
new file mode 100644
index 0000000000..902cf569bd
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/pool.c
@@ -0,0 +1,171 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999 - 2002 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * pool.c - Functions to manipulate address pools for dynamically allocated
+ * ip numbers.
+ *
+ * Example of good entry:
+ *
+ * [ pool 7 ]
+ * BaseAddress = 192.168.168.1
+ * Size = 10
+ *
+ * The above entry would allocate 192.168.168.1 - 192.168.168.11 to incoming
+ * NAI users.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libintl.h>
+#include <conflib.h>
+#include "mipagentconfig.h"
+#include "utils.h"
+#include "pool.h"
+
+
+static FuncEntry poolFunctions[] = {
+ { "BaseAddress", NULL, "BaseAddress", ipFunc, ipFunc, ipFunc, ipFunc },
+ { "Size", NULL, "Size", posIntFunc, posIntFunc, posIntFunc,
+ posIntFunc},
+ { NULL, NULL, NULL, NULL, NULL }
+};
+
+
+
+/*
+ * Function: poolFunc
+ *
+ * Arguments: char *configFile, char *Section, char *Label, int command,
+ * int argc, char *argv[]
+ *
+ * Description: This function implements all the manipulation functions
+ * for the Pools. All the functions used are generic ones in
+ * utils.c
+ * Returns: int
+ */
+int
+poolFunc(char *configFile, char *Section, char *Label, int command, int argc,
+ char *argv[])
+{
+ char DestSection[MAX_SECTION_LEN];
+ FuncEntry *funcEntry = NULL;
+ int (*function)(char *, char *, char *, int, int, char **) = NULL;
+ int value;
+
+ /* Get rid of lint warnings about unused parameters */
+ Section = Label;
+
+ /* ARGV[0] should be the Number */
+ if (argc < 1) {
+ (void) fprintf(stderr,
+ gettext("Error: pool number was not specified. "
+ "Please specify a Pool number.\n"));
+ return (-1);
+ }
+
+ /* Build our Section */
+ (void) sprintf(DestSection, "Pool %s", argv[0]);
+
+ /*
+ * Now, verify that the label is valid.
+ */
+ value = atoi(argv[0]);
+ if (value <= 0) {
+ (void) fprintf(stderr,
+ gettext("Error: SPI <%s> is not valid.\n"),
+ argv[0]);
+ return (-1);
+ }
+
+ /* Finally, look up our functions and call them based on the dest */
+ if (argc > 1) {
+ funcEntry = getFunctions(poolFunctions, argv[1]);
+ if (!funcEntry) {
+ (void) fprintf(stderr,
+ gettext("Error: <%s> is not valid for <%s>.\n"),
+ argv[1], Command2Str(command));
+ return (-1);
+ }
+ }
+
+ /* Now check the particular function we need. */
+ switch (command) {
+ case Add:
+ if (argc == 1) {
+ /* A raw add Warn the user */
+ (void) fprintf(stderr,
+ gettext("Warning: Pool will be created as "
+ "parameters are added.\n\tExample:"
+ "mipagentconfig add Pool 5 SPI 3\n"
+ "will add the Pool, and add the SPI to it.\n"));
+ return (0);
+ }
+ function = funcEntry->addFunc;
+ break;
+ case Change:
+ if (argc == 1) {
+ (void) fprintf(stderr,
+ gettext("Error: cannot change the identifier "
+ "of an [Address <identifier>] section. "
+ "Delete, and create a new section.\n"));
+ return (-1);
+ }
+ function = funcEntry->changeFunc;
+ break;
+ case Delete:
+ if (argc == 1) {
+ return (DeletePrivateProfileSection(DestSection,
+ configFile));
+ }
+ function = funcEntry->deleteFunc;
+ break;
+ case Get:
+ if (argc == 1) {
+ sectionDump(configFile, DestSection,
+ poolFunctions);
+ return (0);
+ }
+ function = funcEntry->getFunc;
+ break;
+ }
+
+ if (!function) {
+ (void) fprintf(stderr,
+ gettext("Error: %s does not support '%s'.\n"),
+ Command2Str(command), argv[0]);
+ return (-1);
+ }
+
+ /* And finally, call function */
+ return (function(configFile, DestSection, funcEntry->Label,
+ command, argc-2, &argv[2]));
+
+} /* poolFunc */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/pool.h b/usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/pool.h
new file mode 100644
index 0000000000..c2e4d8f607
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/pool.h
@@ -0,0 +1,42 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _POOL_H
+#define _POOL_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int poolFunc(char *, char *, char *, int, int, char **);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _POOL_H */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/spi.c b/usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/spi.c
new file mode 100644
index 0000000000..87048b0b57
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/spi.c
@@ -0,0 +1,450 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999 - 2002 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * spi.c - Functions to manipulate Security parameters.
+ *
+ * Example of good entry:
+ *
+ * [ spi 7 ]
+ * Key = 0123456789abcdef0123456789abcdef
+ * replayMethod = timestamps
+ *
+ * The above entry would allocate a SPI with the provided key and replay
+ * method.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libintl.h>
+#include <conflib.h>
+#include "mipagentconfig.h"
+#include "utils.h"
+#include "spi.h"
+
+/* Private Prototypes */
+static int spiKeyFunc(char *, char *, char *, int, int, char **);
+static int spiReplayFunc(char *, char *, char *, int, int, char **);
+
+
+static FuncEntry spiFunctions[] = {
+ { "key", NULL, "Key", spiKeyFunc, spiKeyFunc, spiKeyFunc, spiKeyFunc },
+ { "replayMethod", NULL, "replayMethod", spiReplayFunc, spiReplayFunc,
+ spiReplayFunc, spiReplayFunc},
+ { NULL, NULL, NULL, NULL, NULL }
+
+};
+
+
+/*
+ * Function: spiFunc
+ *
+ * Arguments: char *configFile, char *Section, char *Label, int command,
+ * int argc, char *argv[]
+ *
+ * Description: This function implements all the manipulation functions
+ * for the SPI sections.
+ *
+ * Returns: int
+ */
+int
+spiFunc(char *configFile, char *Section, char *Label, int command, int argc,
+ char *argv[])
+{
+ char DestSection[MAX_SECTION_LEN];
+ FuncEntry *funcEntry = NULL;
+ int (*function)(char *, char *, char *, int, int, char **) = NULL;
+ int value;
+
+ /* Get rid of lint warnings about unused parameters */
+ Section = Label;
+
+ /* ARGV[0] should be the Number */
+ if (argc < 1) {
+ (void) fprintf(stderr,
+ gettext("Error: SPI value was not specified. "
+ "Please specify the SPI number.\n"));
+ return (-1);
+ }
+
+ /*
+ * Now, verify that the label is valid.
+ */
+ value = atoi(argv[0]);
+ if (value <= 0) {
+ (void) fprintf(stderr,
+ gettext("Error: <%s> is not a valid SPI.\n"),
+ argv[0]);
+ return (-1);
+ }
+
+ /* Build our Section */
+ (void) sprintf(DestSection, "SPI %s", argv[0]);
+
+ /* Finally, look up our functions and call them based on the dest */
+ if (argc > 1) {
+ funcEntry = getFunctions(spiFunctions, argv[1]);
+ if (!funcEntry) {
+ (void) fprintf(stderr,
+ gettext("Error: <%s> is not valid "
+ "for '%s' command.\n"),
+ argv[1], Command2Str(command));
+ return (-1);
+ }
+ }
+
+ /* Now check the particular function we need. */
+ switch (command) {
+ case Add:
+ if (argc == 1) {
+ /* A raw add Warn the user */
+ (void) fprintf(stderr,
+ gettext("Warning: SPI will need to be created as "
+ "parameters are added.\n\tExample: "
+ "mipagentconfig add SPI 5 key 123456\n"
+ "will add the SPI, and add the key to it.\n"));
+ return (0);
+ }
+ function = funcEntry->addFunc;
+ break;
+ case Change:
+ if (argc == 1) {
+ (void) fprintf(stderr,
+ gettext("Error: cannot change the identifier of an "
+ "Address section. Delete, and make a new one.\n"));
+ return (-1);
+ }
+ function = funcEntry->changeFunc;
+ break;
+ case Delete:
+ if (argc == 1) {
+ return (DeletePrivateProfileSection(DestSection,
+ configFile));
+ }
+ function = funcEntry->deleteFunc;
+ break;
+ case Get:
+ if (argc == 1) {
+ sectionDump(configFile, DestSection,
+ spiFunctions);
+ return (0);
+ }
+ function = funcEntry->getFunc;
+ break;
+ }
+
+ if (!function) {
+ (void) fprintf(stderr,
+ gettext("Error: %s does not support '%s'.\n"),
+ Command2Str(command), argv[0]);
+ return (-1);
+ }
+
+ /* And finally, call function */
+ return (function(configFile, DestSection, funcEntry->Label,
+ command, argc-2, &argv[2]));
+
+} /* spiFunc */
+
+
+/*
+ * Function: spiKeyFunc
+ *
+ * Arguments: char *configFile, char *Section, char *Label, int command
+ * int argc, char *argv[]
+ *
+ * Description: This function will get and validate an spi key.
+ *
+ * Returns: int
+ */
+static int
+spiKeyFunc(char *configFile, char *Section, char *Label, int command,
+ int argc, char *argv[])
+{
+ char buffer[MAX_VALUE_LEN] = {0};
+ int rc, LabelExists;
+
+ /* Check to see if label already exists */
+ rc = GetPrivateProfileString(Section, Label, "", buffer,
+ MAX_VALUE_LEN-1, configFile);
+ if (!rc)
+ LabelExists = TRUE;
+ else
+ LabelExists = FALSE;
+
+ switch (command) {
+ case Add:
+ /* Now, check for the parameters. */
+ if (argc != 1) {
+ (void) fprintf(stderr,
+ gettext("Error: the SPI value wasn't specified. "
+ "Please specify a value.\n"));
+ return (-1);
+ }
+ if (LabelExists) {
+ (void) fprintf(stderr,
+ gettext("Error: %s is already configured in [%s]:\n"
+ "\t%s = %s\n"),
+ Label, Section, Label, buffer);
+ return (-1);
+ }
+ if (hexValid(argv[0])) {
+ /* Add it! */
+ rc = WritePrivateProfileString(Section, Label,
+ argv[0], configFile);
+ if (rc) {
+ (void) fprintf(stderr, "%s\n", ErrorString);
+ return (rc);
+ }
+ return (0);
+ } else {
+ (void) fprintf(stderr,
+ gettext("Error: value must be a valid "
+ "hexadecimal string.\n"));
+ return (-1);
+ }
+ break;
+
+
+ case Delete:
+ if (!LabelExists) {
+ (void) fprintf(stderr,
+ gettext("Error: %s is not configured in [%s].\n"),
+ Label, Section);
+ return (-1);
+ }
+ rc = DeletePrivateProfileLabel(Section, Label, configFile);
+ if (rc) {
+ (void) fprintf(stderr, "%s\n", ErrorString);
+ return (rc);
+ }
+ return (rc);
+ break;
+
+
+ case Change:
+ /* Now, check for the parameters. */
+ if (argc != 1) {
+ (void) fprintf(stderr,
+ gettext("Error: the new value wasn't specified. "
+ "Please specify a value to change %s to.\n"),
+ Label);
+ return (-1);
+ }
+
+ if (!LabelExists) {
+ (void) fprintf(stderr,
+ gettext("Error: %s is not configured in [%s].\n"),
+ Section, Label);
+ return (-1);
+ }
+
+ if (hexValid(argv[0])) {
+ /* Add it! */
+ rc = WritePrivateProfileString(Section, Label,
+ argv[0], configFile);
+ if (rc) {
+ (void) fprintf(stderr,
+ "%s\n", ErrorString);
+ return (rc);
+ }
+ return (0);
+ } else {
+ (void) fprintf(stderr,
+ gettext("Error: SPI value must be a valid "
+ "hexadecimal string.\n"));
+ return (-1);
+ }
+ break;
+
+ case Get:
+ if (!LabelExists) {
+ (void) fprintf(stderr,
+ gettext("Error: %s is not configured in [%s].\n"),
+ Label, Section);
+ return (-1);
+ }
+ (void) printf(gettext("[%s]\n\t%s = 0x%s\n"),
+ Section, Label, buffer);
+ return (0);
+ break;
+
+ default:
+ (void) fprintf(stderr,
+ gettext("Error: Invalid command code!\n"));
+ return (-1);
+ } /* switch (command) */
+
+
+} /* spiKeyFunc */
+
+/*
+ * Function: spiReplayFunc
+ *
+ * Arguments: char *configFile, char *Section, char *Label, int command
+ * int argc, char *argv[]
+ *
+ * Description: This function sets the SPI replay function to one of the
+ * allowed values.
+ *
+ * Returns: int
+ */
+static int
+spiReplayFunc(char *configFile, char *Section, char *Label, int command,
+ int argc, char *argv[])
+{
+ char buffer[MAX_VALUE_LEN] = {0};
+ int rc, LabelExists;
+ char *validStrings[] = {
+ "timestamps",
+ "none",
+ NULL
+ };
+
+ /* Check to see if label already exists */
+ rc = GetPrivateProfileString(Section, Label, "", buffer,
+ MAX_VALUE_LEN-1, configFile);
+ if (!rc)
+ LabelExists = TRUE;
+ else
+ LabelExists = FALSE;
+
+ switch (command) {
+ case Add:
+ /* Now, check for the parameters. */
+ if (argc != 1) {
+ (void) fprintf(stderr,
+ gettext("Usage: add spi <value> replayMethod <type>"
+ "\n\tWhere <type> is one of ("));
+ printValidStrings(validStrings);
+ (void) fprintf(stderr, ").\n");
+ return (-1);
+ }
+
+ if (LabelExists) {
+ (void) fprintf(stderr,
+ gettext("Error: %s is already configured in [%s]:\n"
+ "\t%s = %s\n"),
+ Label, Section, Label, buffer);
+ return (-1);
+ }
+ if (!checkValidStrings(validStrings, argv[0])) {
+ /* Add it! */
+ rc = WritePrivateProfileString(Section, Label,
+ argv[0], configFile);
+ if (rc) {
+ (void) fprintf(stderr, "%s\n", ErrorString);
+ return (rc);
+ }
+ return (0);
+ } else {
+ (void) fprintf(stderr,
+ gettext("Error: replay method must be one of ("));
+ printValidStrings(validStrings);
+ (void) fprintf(stderr, ").\n");
+ return (-1);
+ }
+ break;
+
+
+ case Delete:
+ if (!LabelExists) {
+ (void) fprintf(stderr,
+ gettext("Error: %s is not configured in [%s].\n"),
+ Label, Section);
+ return (-1);
+ }
+ rc = DeletePrivateProfileLabel(Section, Label, configFile);
+ if (rc) {
+ (void) fprintf(stderr, "%s\n", ErrorString);
+ return (rc);
+ }
+ return (rc);
+ break;
+
+
+ case Change:
+ /* Now, check for the parameters. */
+ if (argc != 1) {
+ (void) fprintf(stderr,
+ gettext("Usage: "
+ "change spi <value> replayMethod <type>\n"
+ "\tWhere <type> is one of ("));
+ printValidStrings(validStrings);
+ (void) fprintf(stderr, ").\n");
+ return (-1);
+ }
+
+ if (!LabelExists) {
+ (void) fprintf(stderr,
+ gettext("Error: %s is not configured in [%s].\n"),
+ Label, Section);
+ return (-1);
+ }
+
+ if (!checkValidStrings(validStrings, argv[0])) {
+ /* Add it! */
+ rc = WritePrivateProfileString(Section, Label,
+ argv[0], configFile);
+ if (rc) {
+ (void) fprintf(stderr,
+ "%s\n", ErrorString);
+ return (rc);
+ }
+ return (0);
+ } else {
+ (void) fprintf(stderr,
+ gettext("Error: replay method must be one of ("));
+ printValidStrings(validStrings);
+ (void) fprintf(stderr, ").\n");
+ return (-1);
+ }
+ break;
+
+ case Get:
+ if (!LabelExists) {
+ (void) fprintf(stderr,
+ gettext("Error: %s is not configured in [%s].\n"),
+ Label, Section);
+ return (-1);
+ }
+ (void) printf(gettext("[%s]\n\t%s = %s\n"),
+ Section, Label, buffer);
+ return (0);
+ break;
+
+ default:
+ (void) fprintf(stderr,
+ gettext("Error: Invalid command code!\n"));
+ return (-1);
+ } /* switch (command) */
+
+
+} /* spiReplayFunc */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/spi.h b/usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/spi.h
new file mode 100644
index 0000000000..9af402db1b
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/spi.h
@@ -0,0 +1,46 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _SPI_H
+#define _SPI_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * spi.h
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int spiFunc(char *, char *, char *, int, int, char **);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SPI_H */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/utils.c b/usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/utils.c
new file mode 100644
index 0000000000..8e6615ab19
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/utils.c
@@ -0,0 +1,810 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999 - 2002 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * utils.c -- Generic variable modification functions
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libintl.h>
+#include <conflib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <ctype.h>
+#include "utils.h"
+
+
+/*
+ * Function: ynFunc
+ *
+ * Arguments: char *configFile, char *Section, char *Label, int command
+ * int argc, char *argv[]
+ *
+ * Description: This function implements all the manipulation functions
+ * for any yes no options
+ *
+ * Returns: int
+ */
+int
+ynFunc(char *configFile, char *Section, char *Label, int command,
+ int argc, char *argv[])
+{
+ char *validStrings[] = {
+ "yes",
+ "no",
+ NULL
+ };
+ char *usageString = " (yes or no)";
+
+ return (strFunc(configFile, Section, Label, command, argc, argv,
+ validStrings, usageString));
+
+} /* ynFunc */
+
+/*
+ * Function: fhFunc
+ *
+ * Arguments: char *configFile, char *Section, char *Label, int command
+ * int argc, char *argv[]
+ *
+ * Description: This function implements all the manipulation functions
+ * for any no/neither, fa, ha, both/yes options
+ *
+ * Returns: int
+ */
+int
+fhFunc(char *configFile, char *Section, char *Label, int command,
+ int argc, char *argv[])
+{
+ char *validStrings[] = {
+ "yes",
+ "both",
+ "ha",
+ "fa",
+ "neither",
+ "no",
+ NULL
+ };
+ char *usageString = " (yes|both, ha, fa, neither|no)";
+
+ return (strFunc(configFile, Section, Label, command, argc, argv,
+ validStrings, usageString));
+
+} /* fin fhFunc() */
+
+
+/*
+ * Function: strFunc
+ *
+ * Arguments: char *configFile, char *Section, char *Label, int command,
+ * int argc, char *argv[], char *validStrings[], char *usageString
+ *
+ * Description: This function is called by wrapper functions needing to
+ * validate, and manipulate string data. It compares the
+ * user-setting to validStrings, then either returning
+ * usageString, passed in by each wrapper depending on the
+ * acceptable settings, or performing command appropriately.
+ *
+ * Note: Due to internationalization issues, this function can not print
+ * usage messages, etc, to the console - there are too many possible
+ * string combinations to consider. The wrapper functions, therefore,
+ * spit out there own usage functions on error.
+ *
+ * Return: 0 on success
+ * -1 on error
+ */
+static int
+strFunc(char *configFile, char *Section, char *Label, int command, int argc,
+ char *argv[], char *validStrings[], char *usageString)
+{
+ char buffer[MAX_VALUE_LEN] = {0};
+ int rc, LabelExists;
+
+ /* Check to see if label already exists */
+ rc = GetPrivateProfileString(Section, Label, "", buffer,
+ MAX_VALUE_LEN-1, configFile);
+ if (!rc)
+ LabelExists = TRUE;
+ else
+ LabelExists = FALSE;
+
+ switch (command) {
+ case Add:
+ /* Now, check for the parameters. */
+ if (argc != 1) {
+ (void) fprintf(stderr,
+ gettext("Error: invalid configuration for %s. "
+ "Please specify any of: %s.\n"),
+ Label, gettext(usageString));
+ return (-1);
+ }
+ /* Now, check to make sure the item isn't already here */
+ if (LabelExists) {
+ (void) fprintf(stderr,
+ gettext("Error: %s is already configured in [%s]:\n"
+ "\t%s = %s\n"),
+ Label, Section, Label, buffer);
+ return (-1);
+ }
+ if (!checkValidStrings(validStrings, argv[0])) {
+ /* Add it! */
+ rc = WritePrivateProfileString(Section, Label,
+ argv[0], configFile);
+ if (rc) {
+ (void) fprintf(stderr, "%s\n", ErrorString);
+ return (rc);
+ }
+ return (0);
+ } else {
+ (void) fprintf(stderr,
+ gettext("Error: value must be one of ("));
+ printValidStrings(validStrings);
+ (void) fprintf(stderr, ").\n");
+ return (-1);
+ }
+ break;
+
+
+ case Delete:
+ if (!LabelExists) {
+ (void) fprintf(stderr,
+ gettext("Error: %s is not configured in [%s].\n"),
+ Label, Section);
+ return (-1);
+ }
+ rc = DeletePrivateProfileLabel(Section, Label, configFile);
+ if (rc) {
+ (void) fprintf(stderr,
+ "Error: %s\n", ErrorString);
+ }
+ return (rc);
+ break;
+
+
+ case Change:
+ /* Now, check for the parameters. */
+ if (argc != 1) {
+ (void) fprintf(stderr,
+ gettext("Error: new value for %s unspecified. "
+ "Please specify any of: %s.\n"),
+ Label, gettext(usageString));
+ return (-1);
+ }
+
+ if (!LabelExists) {
+ (void) fprintf(stderr,
+ gettext("Error: %s is not configured in [%s].\n"),
+ Label, Section);
+ return (-1);
+ }
+
+ if (!checkValidStrings(validStrings, argv[0])) {
+ /* Change it! */
+ rc = WritePrivateProfileString(Section, Label,
+ argv[0], configFile);
+ if (rc) {
+ (void) fprintf(stderr, "%s\n", ErrorString);
+ return (rc);
+ }
+ return (0);
+ } else {
+ (void) fprintf(stderr,
+ gettext("Error: value must be one of ("));
+ printValidStrings(validStrings);
+ (void) fprintf(stderr, ").\n");
+ return (-1);
+ }
+ break;
+
+ case Get:
+ rc = GetPrivateProfileString(Section, Label, "",
+ buffer, MAX_VALUE_LEN-1, configFile);
+ if (rc) {
+ (void) fprintf(stderr,
+ gettext("Error: %s is not configured in [%s].\n"),
+ Label, Section);
+ return (-1);
+ }
+ (void) printf(gettext("[%s]\n\t%s = %s\n"),
+ Section, Label, buffer);
+ return (0);
+ break;
+
+ default:
+ (void) fprintf(stderr,
+ gettext("Error: Invalid command code!\n"));
+ return (-1);
+ } /* switch (command) */
+
+} /* strFunc */
+
+/*
+ * Function: posIntFunc
+ *
+ * Arguments: char *configFile, char *Section, char *Label, int command,
+ * int argc, char *argv[]
+ *
+ * Description: This function implements all the manipulation functions
+ * for any positive integer functions.
+ *
+ * Returns: int
+ */
+int
+posIntFunc(char *configFile, char *Section, char *Label, int command, int argc,
+ char *argv[])
+{
+ char buffer[MAX_VALUE_LEN] = {0};
+ int rc, LabelExists;
+ int value;
+
+ /* Check to see if label already exists */
+ rc = GetPrivateProfileString(Section, Label, "", buffer,
+ MAX_VALUE_LEN-1, configFile);
+ if (!rc)
+ LabelExists = TRUE;
+ else
+ LabelExists = FALSE;
+
+ switch (command) {
+ case Add:
+ /* Check for the parameters. */
+ if (argc != 1) {
+ (void) fprintf(stderr,
+ gettext("Error: value for %s was not specified. "
+ "Please specify value.\n"), Label);
+ return (-1);
+ }
+
+ /* Now, check to make sure it is not already here */
+ if (LabelExists) {
+ (void) fprintf(stderr,
+ gettext("Error: %s is already configured in [%s].\n"
+ "\tvalue = %s\n"),
+ Label, Section, Label, buffer);
+ return (-1);
+ }
+ value = atoi(argv[0]);
+ if (value > 0) {
+ /* Add it! */
+ rc = WritePrivateProfileInt(Section, Label,
+ value, configFile);
+ if (rc) {
+ (void) fprintf(stderr, "%s\n", ErrorString);
+ return (rc);
+ }
+ return (0);
+ } else {
+ (void) fprintf(stderr,
+ gettext("Error: value must be a positive,"
+ " non-zero integer.\n"));
+ return (-1);
+ }
+ break;
+
+
+ case Delete:
+ if (!LabelExists) {
+ (void) fprintf(stderr,
+ gettext("Error: %s is not configured in [%s].\n"),
+ Label, Section);
+ return (-1);
+ }
+ rc = DeletePrivateProfileLabel(Section, Label, configFile);
+ if (rc)
+ (void) fprintf(stderr, "Error: %s\n", ErrorString);
+ return (rc);
+ break;
+
+
+ case Change:
+ /* Now, check for the parameters. */
+ if (argc != 1) {
+ (void) fprintf(stderr,
+ gettext("Error: New value for %s was not specified."
+ " Please specify a value.\n"));
+ return (-1);
+ }
+
+ if (!LabelExists) {
+ (void) fprintf(stderr,
+ gettext("Error: %s is not configured in [%s].\n"),
+ Label, Section);
+ return (-1);
+ }
+
+ value = atoi(argv[0]);
+
+ if (value > 0) {
+ /* Change it! */
+ rc = WritePrivateProfileString(Section, Label,
+ argv[0], configFile);
+ if (rc) {
+ (void) fprintf(stderr, "%s\n", ErrorString);
+ return (rc);
+ }
+ return (0);
+ } else {
+ (void) fprintf(stderr,
+ gettext("Error: value must be a positive,"
+ " non-zero integer.\n"));
+ return (-1);
+ }
+ break;
+
+ case Get:
+ if (!LabelExists) {
+ (void) fprintf(stderr,
+ gettext("Error: %s is not configured in [%s].\n"),
+ Label, Section);
+ return (-1);
+ }
+ (void) printf(gettext("[%s]\n\t%s = %s\n"),
+ Section, Label, buffer);
+ return (0);
+ break;
+
+
+
+ default:
+ (void) fprintf(stderr,
+ gettext("Error: Invalid command code!\n"));
+ return (-1);
+ } /* switch (command) */
+} /* posIntFunc */
+
+/*
+ * Function: ipFunc
+ *
+ * Arguments: char *configFile, char *Section, char *Label, int command,
+ * int argc, char *argv[]
+ *
+ * Description: This function implements all the manipulation functions
+ * for any ip numbers or masks
+ *
+ * Returns: int
+ */
+int
+ipFunc(char *configFile, char *Section, char *Label, int command, int argc,
+ char *argv[])
+{
+ char buffer[MAX_VALUE_LEN] = {0};
+ int rc, LabelExists;
+
+ /* Check to see if label already exists */
+ rc = GetPrivateProfileString(Section, Label, "", buffer,
+ MAX_VALUE_LEN-1, configFile);
+ if (!rc)
+ LabelExists = TRUE;
+ else
+ LabelExists = FALSE;
+
+ switch (command) {
+ case Add:
+ /* Check for the parameters. */
+ if (argc != 1) {
+ (void) fprintf(stderr,
+ gettext("Error: value for %s was not specified. "
+ "Please specify a value.\n"), Label);
+ return (-1);
+ }
+
+ if (LabelExists) {
+ (void) fprintf(stderr,
+ gettext("Error: %s is already configured in [%s]:\n"
+ "\tvalue = %s\n"),
+ Label, Section, Label, buffer);
+ return (-1);
+ }
+
+
+ if (ipValid(argv[0])) {
+ /* Add it! */
+ rc = WritePrivateProfileString(Section, Label, argv[0],
+ configFile);
+ if (rc) {
+ (void) fprintf(stderr, "%s\n", ErrorString);
+ return (rc);
+ }
+ return (0);
+ } else {
+ (void) fprintf(stderr,
+ gettext("Error: %s is not a valid IP address.\n"),
+ argv[0]);
+ return (-1);
+ }
+ break;
+
+
+ case Delete:
+ if (!LabelExists) {
+ (void) fprintf(stderr,
+ gettext("Error: %s is not configured in [%s].\n"),
+ Label, Section);
+ return (-1);
+ }
+ rc = DeletePrivateProfileLabel(Section, Label, configFile);
+ if (rc)
+ (void) fprintf(stderr, "Error: %s\n", ErrorString);
+ return (rc);
+ break;
+
+
+ case Change:
+ /* Now, check for the parameters. */
+ if (argc != 1) {
+ (void) fprintf(stderr,
+ gettext("Error: new value for %s was not specified."
+ " Please specify a value.\n"), Label);
+ return (-1);
+ }
+
+ if (!LabelExists) {
+ (void) fprintf(stderr,
+ gettext("Error: %s is not configured in [%s].\n"),
+ Label, Section);
+ return (-1);
+ }
+
+ if (ipValid(argv[0])) {
+ /* Change it! */
+ rc = WritePrivateProfileString(Section, Label,
+ argv[0], configFile);
+ if (rc) {
+ (void) fprintf(stderr, "%s\n", ErrorString);
+ return (rc);
+ }
+ return (0);
+ } else {
+ (void) fprintf(stderr,
+ gettext("Error: %s is not a valid IP address.\n"),
+ argv[0]);
+ return (-1);
+ }
+ break;
+
+ case Get:
+ if (!LabelExists) {
+ (void) fprintf(stderr,
+ gettext("Error: %s is not configured in [%s].\n"),
+ Label, Section);
+ return (-1);
+ }
+ (void) printf(gettext("[%s]\n\t%s = %s\n"),
+ Section, Label, buffer);
+ return (0);
+ break;
+
+
+
+ default:
+ (void) fprintf(stderr,
+ gettext("Error: Invalid command code!\n"));
+ return (-1);
+ } /* switch (command) */
+
+} /* ipFunc */
+
+/*
+ * Function: getFunctions
+ *
+ * Arguments: FunctionArray, string
+ *
+ * Description: This routine returns the func entry structure associated
+ * with the dest named, "string"
+ *
+ * Returns: FuncEntry
+ *
+ */
+FuncEntry *
+getFunctions(FuncEntry Funcs[], char *string)
+{
+ int i;
+ for (i = 0; Funcs[i].tag; i++)
+ if (!strcasecmp(string, Funcs[i].tag)) {
+ return (&Funcs[i]);
+ }
+ return (NULL);
+} /* getFunctions */
+
+
+
+/*
+ * Function: Str2Command
+ *
+ * Arguments: string
+ *
+ * Description: This routine returns the command associated
+ * with the command string, "string"
+ *
+ * Returns: Command
+ *
+ */
+Command
+Str2Command(char *string)
+{
+ extern CommandTable Commands[];
+ int i;
+ for (i = 0; Commands[i].string; i++)
+ if (!strcasecmp(string, Commands[i].string)) {
+ return (Commands[i].command);
+ }
+ return (-1);
+} /* Str2Command */
+
+/*
+ * Function: Command2Str
+ *
+ * Arguments: command
+ *
+ * Description: This function translates a command into a string.
+ *
+ * Returns: char *
+ *
+ */
+char *
+Command2Str(Command cmd)
+{
+ extern CommandTable Commands[];
+ CommandTable *item;
+ for (item = Commands; item->string; item++)
+ if (item->command == cmd)
+ return (item->string);
+ return ("Unknown");
+} /* Command2Str */
+
+
+/*
+ * Function: sectionDump
+ *
+ * Arguments: configFile, Section, funcs
+ *
+ * Description: This funciton will dump the value of particular
+ * labels (Funcs) in the given section.
+ *
+ * Returns: void
+ *
+ */
+void
+sectionDump(char *configFile, char *Section, FuncEntry Funcs[])
+{
+ int i;
+ int rc;
+ char buffer[MAX_VALUE_LEN] = {0};
+
+ (void) printf(gettext("[%s]\n"), Section);
+ for (i = 0; Funcs[i].tag; i++) {
+ rc = GetPrivateProfileString(Section, Funcs[i].Label, "",
+ buffer, MAX_VALUE_LEN-1, configFile);
+ /* If the item was there, and it was not null, then print it */
+ if (!rc && *buffer) {
+ (void) printf(gettext("\t%s = %s\n"),
+ Funcs[i].Label, buffer);
+ }
+ }
+} /* sectionDump */
+
+/*
+ * Function: checkValidStrings
+ *
+ * Arguments: validStrings, string
+ *
+ * Description: This function returns zero if the passed in string
+ * is in validStrings.
+ *
+ * Returns: int
+ *
+ */
+int
+checkValidStrings(char *validStrings[], char *string)
+{
+ char *probe;
+ int i = 0;
+
+ for (probe = validStrings[i]; probe; probe = validStrings[++i])
+ if (!strcasecmp(probe, string))
+ return (0);
+
+ return (-1);
+} /* checkValidStrings */
+
+
+/*
+ * Function: printValidStrings
+ *
+ * Arguments: validStrings
+ *
+ * Description: This routine prints out the valid strings for
+ * display messages.
+ *
+ * Returns: void
+ *
+ */
+void
+printValidStrings(char *validStrings[])
+{
+ char *probe;
+ int i = 0;
+
+ for (probe = validStrings[i]; probe; probe = validStrings[++i])
+ (void) fprintf(stderr, "%s ", probe);
+
+} /* printValidStrings */
+
+
+/*
+ * Function: ipValid
+ *
+ * Arguments: ipString
+ *
+ * Description: This function returns true if the string is valid.
+ * TODO: Use inet_pton to validate string.
+ *
+ * Returns: int
+ *
+ */
+int
+ipValid(char *ipString)
+{
+#if 0
+ uint32_t in_addr;
+
+ in_addr = inet_pton(ipString);
+ (void) fprintf(stderr, "DEBUG: inet_pton(\"%s\") =0x%08x\n",
+ ipString, in_addr);
+ return (-1);
+#else
+
+ int a, b, c, d;
+ int rc;
+
+ rc = sscanf(ipString, "%d.%d.%d.%d", &a, &b, &c, &d);
+ /* Check the parsing */
+ if (rc != 4)
+ return (0);
+
+ /* Check the bounds of each number */
+ if ((a < 0 || a > 255) ||
+ (b < 0 || b > 255) ||
+ (c < 0 || c > 255) ||
+ (d < 0 || d > 255))
+ return (0);
+
+ /* otherwise, it's valid */
+ return (1);
+#endif
+} /* ipValid */
+
+
+/*
+ * Function: naiValid
+ *
+ * Arguments: string
+ *
+ * Description: This function returns true if the strings looks like
+ * a valid NAI.
+ * TODO: use RFC to validate NAI.
+ *
+ * Returns: int
+ *
+ */
+int
+naiValid(char *ipString)
+{
+ int atFound = FALSE;
+
+ /*
+ * Walk through the string, checking for at least one at,
+ * and no wierd characters.
+ */
+
+ for (; *ipString; ipString++) {
+ if (*ipString == '@') {
+ if (atFound) {
+ /* Error: two @ symbols! */
+ return (0);
+ } else {
+ atFound = TRUE;
+ }
+ continue; /* to keep the if from nesting */
+ }
+ if (isspace(*ipString))
+ return (0);
+ }
+
+ if (atFound)
+ return (1); /* Success! */
+ else
+ return (0);
+} /* naiValid */
+
+/*
+ * Function: hexCheck
+ *
+ * Arguments: string
+ *
+ * Description: Check the string to make sure it is a valid Hex string
+ *
+ * Returns: int
+ *
+ */
+int
+hexValid(char *string)
+{
+ if (strlen(string) % 2) {
+ (void) fprintf(stderr,
+ gettext("Error: hex string must be of even length.\n"));
+ return (0);
+ }
+
+ for (; *string; string++) {
+ if (!isxdigit(*string))
+ return (0);
+ }
+ return (1);
+} /* hexValid */
+
+
+/*
+ * Function: addEmptySection
+ *
+ * Arguments: char *configFile, char *SectionName
+ *
+ * Description: This routine will add an empty section to a config file,
+ * by creating a section with a dummy label, then deleting
+ * the label. NOTE: If the section already exists, this
+ * function is a NOP. Also, if the label "DummyLabel"
+ * already exists in the section, it will be deleted as a side
+ * effect.
+ *
+ * Returns: int
+ *
+ */
+int
+addEmptySection(char *configFile, char *Section)
+{
+ int rc;
+ char *Label = "DummyLabel";
+ char *Value = "DummyValue";
+
+ rc = WritePrivateProfileString(Section, Label, Value, configFile);
+ if (rc) {
+ (void) fprintf(stderr, "Error: %s\n", ErrorString);
+ return (1);
+ }
+ rc = DeletePrivateProfileLabel(Section, Label, configFile);
+ if (rc) {
+ (void) fprintf(stderr, "Error: %s\n", ErrorString);
+ return (1);
+ }
+ return (0);
+} /* addEmptySection */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/utils.h b/usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/utils.h
new file mode 100644
index 0000000000..fa45ab251c
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/mipagentconfig/utils.h
@@ -0,0 +1,64 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999-2000 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _UTILS_H
+#define _UTILS_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * utils.h -- Utilities, common functions
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "mipagentconfig.h"
+
+
+FuncEntry *getFunctions(FuncEntry *, char *);
+Command Str2Command(char *);
+char *Command2Str(Command);
+
+int addEmptySection(char *, char *);
+void sectionDump(char *, char *, FuncEntry *);
+int fhFunc(char *, char *, char *, int, int, char **);
+int ynFunc(char *, char *, char *, int, int, char **);
+int strFunc(char *, char *, char *, int, int, char **, char **, char *);
+int ipFunc(char *, char *, char *, int, int, char **);
+int posIntFunc(char *, char *, char *, int, int, char **);
+int ipValid(char *);
+int naiValid(char *);
+int hexValid(char *);
+int checkValidStrings(char **, char *);
+void printValidStrings(char **);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _UTILS_H */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/mipagentstat/Makefile b/usr/src/cmd/cmd-inet/usr.sbin/mipagentstat/Makefile
new file mode 100644
index 0000000000..571efa33da
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/mipagentstat/Makefile
@@ -0,0 +1,66 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+include $(SRC)/cmd/Makefile.cmd
+include $(SRC)/cmd/cmd-inet/Makefile.cmd-inet
+
+PROG= mipagentstat
+
+OBJS= main.o
+
+# For linting
+SRCS= $(OBJS:%.o=%.c)
+LINTFLAGS += -errchk=%all
+
+LDLIBS += -lnsl
+CPPFLAGS += -I$(CMDINETCOMMONDIR)
+
+# I18n
+POFILE= $(PROG).po
+POFILES= $(OBJS:%.o=%.po)
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+ $(LINK.c) -o $@ $(OBJS) $(LDLIBS)
+ $(POST_PROCESS)
+
+install: all $(ROOTUSRSBINPROG)
+
+clean:
+ $(RM) *.o
+
+$(POFILE): $(POFILES)
+ $(RM) $@
+ cat $(POFILES) > $@
+
+include $(SRC)/cmd/Makefile.targ
+
+lint: lint_SRCS
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/mipagentstat/inc.flg b/usr/src/cmd/cmd-inet/usr.sbin/mipagentstat/inc.flg
new file mode 100644
index 0000000000..373eff0264
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/mipagentstat/inc.flg
@@ -0,0 +1,29 @@
+#!/bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright (c) 1999 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+find_files "s.*" usr/src/cmd/cmd-inet/common
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/mipagentstat/main.c b/usr/src/cmd/cmd-inet/usr.sbin/mipagentstat/main.c
new file mode 100644
index 0000000000..fe39e291b2
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/mipagentstat/main.c
@@ -0,0 +1,495 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <door.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <libintl.h>
+#include <locale.h>
+#include "conflib.h"
+#include "mipagentstat_door.h"
+
+/*
+ * Main for mipagentstat. This uses the protocol defined in
+ * "mipagentstat_door.h" to act as a door client to mipagent.
+ * It displays statistics tables for home and foreign agents
+ * by enumerating the registration tables in mipagent. Each
+ * door call retrieves an single entry from the table. Doors
+ * are fast enough that performance is good for this approach,
+ * and memory usage (particularly in mipagent) is small and
+ * non-disruptive to the rest of the process. This is a priority
+ * since we expect that mipagent may eventually be required to
+ * service thousands of nodes.
+ *
+ * mipagentstat follows the following logic flow:
+ *
+ * 1. (main) Process command-line arguments
+ * 2. (main) Call enumerate_stats for each agent to stat
+ * 3. (enumerate_stats) Display table banner if there are any entries
+ * 4. (enumerate_stats)
+ * while more entries forthcoming
+ * get next entry via a door_call
+ * display the entry with display_entry
+ * 5. (display_entry) convert address to printable format; print it.
+ */
+
+/* Flag to tell display function whether or not to resolve host names */
+#define NO_NAME_RESOLUTION 0x01
+#define PEER_PROTECTION 0x02
+
+/* *80* columns for addrs/hostnames or NAIs, times, and registered flags. */
+#define MN_ADDR_COL_WIDTH 26 /* Mobile Node - NAI likely */
+#define MA_ADDR_COL_WIDTH 24 /* mipagent-peer - NAI unlikely */
+#define TIME_COL_WIDTH 9 /* lifetime, and remaining (ulongs) */
+#define FLAG_COL_WIDTH 8 /* services being given */
+#define PROT_TYPE_COL_WIDTH 8 /* enough to print protection types */
+
+/*
+ * Displays the header for the mobile node stats listing. Column widths for
+ * the fields are defined by the macros M*_ADDR_COL_WIDTH and TIME_COL_WIDTH.
+ *
+ * type IN Foreign or Home Agent
+ */
+static void display_mn_header(enum_stat_type type) {
+ (void) printf("\n%-*.*s %-*.*s %-*.*s %-*.*s %-*.*s\n",
+ MN_ADDR_COL_WIDTH, MN_ADDR_COL_WIDTH, "Mobile Node",
+ MA_ADDR_COL_WIDTH, MA_ADDR_COL_WIDTH,
+ (type == FOREIGN_AGENT ? "Home Agent" : "Foreign Agent"),
+ TIME_COL_WIDTH, TIME_COL_WIDTH, "Time (s)",
+ TIME_COL_WIDTH, TIME_COL_WIDTH, "Time (s)",
+ FLAG_COL_WIDTH, FLAG_COL_WIDTH, "Service");
+ (void) printf("%*s %-*.*s %-*.*s %-*.*s\n",
+ (MN_ADDR_COL_WIDTH + MA_ADDR_COL_WIDTH) + 1, "",
+ TIME_COL_WIDTH, TIME_COL_WIDTH, "Granted ",
+ TIME_COL_WIDTH, TIME_COL_WIDTH, "Remaining",
+ FLAG_COL_WIDTH, FLAG_COL_WIDTH, "Flags");
+ (void) printf("%-*.*s %-*.*s %-*.*s %-*.*s %-*.*s\n",
+ MN_ADDR_COL_WIDTH, MN_ADDR_COL_WIDTH,
+ "-------------------------------------------------",
+ MA_ADDR_COL_WIDTH, MA_ADDR_COL_WIDTH,
+ "-------------------------------------------------",
+ TIME_COL_WIDTH, TIME_COL_WIDTH,
+ "-------------------------------------------------",
+ TIME_COL_WIDTH, TIME_COL_WIDTH,
+ "-------------------------------------------------",
+ FLAG_COL_WIDTH, FLAG_COL_WIDTH,
+ "-------------------------------------------------");
+}
+
+/*
+ * Displays the header for the agent stats listing. Column widths for the
+ * fields are defined by the macros PEER_ADDR_COL_WIDTH, and
+ * PROT_TYPE_COL_WIDTH.
+ */
+static void display_agent_header(enum_stat_type type) {
+ (void) printf("\n%-*.*s %-*.*s\n",
+ MA_ADDR_COL_WIDTH, MA_ADDR_COL_WIDTH,
+ (type == FOREIGN_AGENT_PEER ? "Foreign" : "Home"),
+ PROT_TYPE_COL_WIDTH * 4 + 3, PROT_TYPE_COL_WIDTH * 4 + 3,
+ "..... Security Association(s) .....");
+ (void) printf("%-*.*s %-*.*s %-*.*s %-*.*s %-*.*s\n",
+ MA_ADDR_COL_WIDTH, MA_ADDR_COL_WIDTH, "Agent",
+ PROT_TYPE_COL_WIDTH, PROT_TYPE_COL_WIDTH, "Requests",
+ PROT_TYPE_COL_WIDTH, PROT_TYPE_COL_WIDTH, "Replies",
+ PROT_TYPE_COL_WIDTH, PROT_TYPE_COL_WIDTH, "FTunnel",
+ PROT_TYPE_COL_WIDTH, PROT_TYPE_COL_WIDTH, "RTunnel");
+ (void) printf("%-*.*s %-*.*s %-*.*s %-*.*s %-*.*s\n",
+ MA_ADDR_COL_WIDTH, MA_ADDR_COL_WIDTH,
+ "-------------------------------------------------",
+ PROT_TYPE_COL_WIDTH, PROT_TYPE_COL_WIDTH,
+ "-------------------------------------------------",
+ PROT_TYPE_COL_WIDTH, PROT_TYPE_COL_WIDTH,
+ "-------------------------------------------------",
+ PROT_TYPE_COL_WIDTH, PROT_TYPE_COL_WIDTH,
+ "-------------------------------------------------",
+ PROT_TYPE_COL_WIDTH, PROT_TYPE_COL_WIDTH,
+ "-------------------------------------------------");
+}
+
+
+
+/*
+ * Converts the address in src to a string. If NO_NAME_RESOLUTION is
+ * not set in flags, we try to resolve the address to a host name
+ * using getipnodebyaddr. If this fails (for any reason) or if
+ * NO_NAME_RESOLUTION is set in flags, we just convert the address
+ * to presentation format using inet_ntop.
+ *
+ * af IN Address familiy of src
+ * src IN Address to resolve or convert to presentation
+ * srclen IN Length of src buffer
+ * buf IN/OUT Buffer to hold coverted string. Should be big
+ * enough to hold either an address or a fully
+ * qualified host name string.
+ * bufsz IN Size of buf
+ * flags IN Special flags
+ *
+ * Returns A pointer to a character buffer containing the
+ * printable address or host name. Never returns
+ * NULL. The pointer may point into buf; either
+ * way, the caller must not free the result.
+ */
+static char *addr2str(int af,
+ void *src,
+ size_t srclen,
+ char *buf,
+ size_t bufsz,
+ int flags) {
+ char *answer;
+ struct hostent *he;
+ int err;
+ size_t addrlen;
+
+ /*
+ * If -n wasn't given at the command line, try to resolve
+ * the hostname into an address.
+ */
+ if ((flags & NO_NAME_RESOLUTION) == 0) {
+ /* Set the addrlen according to the AF */
+ switch (af) {
+ case AF_INET:
+ addrlen = sizeof (struct in_addr);
+ break;
+ case AF_INET6:
+ addrlen = sizeof (struct in6_addr);
+ break;
+ default:
+ addrlen = srclen;
+ break;
+ }
+
+ he = getipnodebyaddr(src, addrlen, af, &err);
+ if (he && err != 0) {
+ (void) strncpy(buf, he->h_name, bufsz);
+ freehostent(he);
+ return (buf);
+ }
+ }
+
+ /*
+ * Else we shouldn't or couldn't resolve the hostname,
+ * so just convert to presentation format.
+ */
+
+ answer = (char *)inet_ntop(af, src, buf, bufsz);
+
+ return (answer ? answer : "<bad address>");
+}
+
+/*
+ * Displays a single mobile node entry, formatting the fields
+ * according to the macros M*_ADDR_COL_WIDTH and TIME_COL_WIDTH
+ * and using addr2str to conver the addresses in stat_args into
+ * printable strings.
+ *
+ * stat_args IN An entry returned from the stat door call
+ * flags IN Passed through to addr2str
+ */
+static void display_mn_entry(DoorStatArgs stat_args, int flags) {
+ char node_str[NI_MAXHOST];
+ char agent_str[NI_MAXHOST];
+ char service_str[FLAG_COL_WIDTH];
+ struct timeval now;
+ ulong_t expires = stat_args.expires;
+
+ (void) gettimeofday(&now, NULL);
+
+ /* Calculate what to print in the Service Flags column */
+ (void) snprintf(service_str, sizeof (service_str), "%s%s%s%s%s%s%s%s",
+ (stat_args.service_flags & SERVICE_SIMULTANEOUS_BINDINGS ?
+ "S" : "."),
+ (stat_args.service_flags & SERVICE_FWD_BROADCASTS ?
+ "B" : "."),
+ (stat_args.service_flags & SERVICE_DECAPSULATION_BY_MN ?
+ "D" : "."),
+ (stat_args.service_flags & SERVICE_MIN_ENCAP ?
+ "M" : "."),
+ (stat_args.service_flags & SERVICE_GRE_ENCAP ?
+ "G" : "."),
+ (stat_args.service_flags & SERVICE_VJ_COMPRESSION ?
+ "V" : "."),
+ (stat_args.service_flags & SERVICE_REVERSE_TUNNEL ?
+ "T" : "."),
+ (stat_args.service_flags & SERVICE_BIT_UNUSED ?
+ "?" : "."));
+ /* When the last reg bit becomes defined, it *replaces* the ? entry. */
+
+ (void) printf("%-*.*s %-*.*s %-*lu %-*lu %*.*s\n",
+ /* Mobile Node */
+ MN_ADDR_COL_WIDTH, MN_ADDR_COL_WIDTH,
+ addr2str(stat_args.node_af,
+ stat_args.node,
+ sizeof (stat_args.node),
+ node_str,
+ NI_MAXHOST,
+ flags),
+ /* Agent */
+ MA_ADDR_COL_WIDTH, MA_ADDR_COL_WIDTH,
+ addr2str(stat_args.agent_af,
+ stat_args.agent,
+ sizeof (stat_args.agent),
+ agent_str,
+ NI_MAXHOST,
+ flags),
+ /* Time granted and expires */
+ TIME_COL_WIDTH, expires - stat_args.granted,
+ TIME_COL_WIDTH, (expires < now.tv_sec ?
+ 0 :
+ expires - now.tv_sec),
+ /* Flags indicating services for the mn */
+ FLAG_COL_WIDTH, FLAG_COL_WIDTH, service_str);
+}
+
+
+/*
+ * Displays a single agent-peer entry, formatting the fields
+ * according to the macros M*_ADDR_COL_WIDTH and PROT_TYPE_COL_WIDTH
+ * and using addr2str to conver the addresses in stat_args into
+ * printable strings depending on whether the '-n' flag was set.
+ *
+ * stat_args IN An entry returned from the stat door call
+ * flags IN Passed through to addr2str
+ */
+static void display_agent_entry(DoorStatArgs stat_args, int flags) {
+ char agent_str[MA_ADDR_COL_WIDTH];
+ char request_str[PROT_TYPE_COL_WIDTH] = "";
+ char reply_str[PROT_TYPE_COL_WIDTH] = "";
+ char tunnel_str[PROT_TYPE_COL_WIDTH] = "";
+ char reversetunnel_str[PROT_TYPE_COL_WIDTH] = "";
+
+ /* calculate what to print in the protection columns */
+ if (stat_args.service_flags & IPSEC_REQUEST_AH) {
+ (void) strcat(request_str, "AH ");
+ }
+ if (stat_args.service_flags & IPSEC_REQUEST_ESP) {
+ (void) strcat(request_str, "ESP");
+ }
+
+ if (stat_args.service_flags & IPSEC_REPLY_AH) {
+ (void) strcat(reply_str, "AH ");
+ }
+ if (stat_args.service_flags & IPSEC_REPLY_ESP) {
+ (void) strcat(reply_str, "ESP");
+ }
+
+ if (stat_args.service_flags & IPSEC_TUNNEL_AH) {
+ (void) strcat(tunnel_str, "AH ");
+ }
+ if (stat_args.service_flags & IPSEC_TUNNEL_ESP) {
+ (void) strcat(tunnel_str, "ESP");
+ }
+
+ if (stat_args.service_flags & IPSEC_REVERSE_TUNNEL_AH) {
+ (void) strcat(reversetunnel_str, "AH ");
+ }
+ if (stat_args.service_flags & IPSEC_REVERSE_TUNNEL_ESP) {
+ (void) strcat(reversetunnel_str, "ESP");
+ }
+
+ (void) printf("%-*.*s %-*.*s %-*.*s %-*.*s %-*.*s\n",
+ /* agent-peer */
+ MA_ADDR_COL_WIDTH, MA_ADDR_COL_WIDTH,
+ addr2str(stat_args.agent_af,
+ stat_args.agent,
+ sizeof (stat_args.agent),
+ agent_str,
+ MA_ADDR_COL_WIDTH,
+ flags),
+ /* protection info */
+ PROT_TYPE_COL_WIDTH, PROT_TYPE_COL_WIDTH, request_str,
+ PROT_TYPE_COL_WIDTH, PROT_TYPE_COL_WIDTH, reply_str,
+ PROT_TYPE_COL_WIDTH, PROT_TYPE_COL_WIDTH, tunnel_str,
+ PROT_TYPE_COL_WIDTH, PROT_TYPE_COL_WIDTH, reversetunnel_str);
+}
+
+
+/*
+ * Enumerates through mipagent's entire table of foreign or home
+ * agent entries. We use a doors IPC to communicate with mipagent.
+ * Each door call retrieves a single entry, keeping memory usage
+ * low. Memory management is simplified by using the automatic
+ * variable stat_args to allocate all needed memory. Each entry
+ * is displayed based on the flags passed in. If flags indicate
+ * the user wants to see the protection in place (-p) with our
+ * agent peers, then display_agent_entry() is called, otherwise
+ * the user wants mobile nodes, and we call display_mn_entry().
+ *
+ * type IN Foreign or Home Agent
+ * flags IN Passed through to display_*_entries()
+ */
+static void enumerate_stats(enum_stat_type type, int flags) {
+ int fd = open(MIPAGENTSTAT_DOOR, O_RDONLY);
+ door_arg_t arg;
+ DoorStatArgs stat_args;
+
+ if (fd == -1) {
+ (void) fprintf(stderr, gettext("mipagent unavailable\n"));
+ exit(1);
+ }
+
+ /* Set up door args */
+ (void) memset(&stat_args, 0, sizeof (stat_args));
+ stat_args.type = type;
+ stat_args.op = FIRST_ENT;
+
+ (void) memset(&arg, 0, sizeof (arg));
+ arg.data_ptr = (char *)&stat_args;
+ arg.data_size = sizeof (stat_args);
+ arg.rbuf = (char *)&stat_args;
+ arg.rsize = sizeof (stat_args);
+
+ /* Do the first entry. If the server is down, we find out here. */
+ if (door_call(fd, &arg) == -1) {
+ (void) fprintf(stderr, gettext("mipagent unavailable\n"));
+ exit(1);
+ }
+
+ /*
+ * Now that we know the server is up, display the banner,
+ * then display information, or at least the fact that
+ * there's nothing to display!
+ */
+ if (flags & PEER_PROTECTION)
+ /* display the mobility agent peer stat header */
+ display_agent_header(type);
+ else
+ /* display the mn-stat header */
+ display_mn_header(type);
+
+ if (arg.data_size == 0) {
+ (void) fprintf(stdout, gettext("<none>\n\n"));
+ goto done;
+ }
+
+ /* Switch to next entry mode for the rest of the enumeration */
+ stat_args.op = NEXT_ENT;
+
+ /* Enumerate */
+ while (arg.data_size != 0) {
+ if (arg.data_size < sizeof (stat_args)) {
+ (void) fprintf(stderr, gettext("bad reply from mipagent\n"));
+ break;
+ }
+
+ if (flags & PEER_PROTECTION)
+ display_agent_entry(stat_args, flags);
+ else
+ display_mn_entry(stat_args, flags);
+
+ if (door_call(fd, &arg) == -1) {
+ perror("door_call");
+ break;
+ }
+ }
+
+done:
+ (void) close(fd);
+}
+
+static void usage(char **argv) {
+ (void) printf(gettext("Usage: %s [ -fhp ]\n"), *argv);
+}
+
+/*
+ * Entry point for mipagentstat. main simply processes the command
+ * line arguments and uses them to dispatch foreign or home agent
+ * statistics enumerations. If no arguments are given, we retrieve
+ * both home and foreign agent stats.
+ */
+int
+main(int argc, char **argv) {
+ int c;
+ int type = 0;
+ int flags = 0;
+
+ (void) setlocale(LC_ALL, "");
+#if !defined(TEXT_DOMAIN)
+#define TEXT_DOMAIN "SYS_TEST"
+#endif
+ (void) textdomain(TEXT_DOMAIN);
+
+ while ((c = getopt(argc, argv, "fhnp")) != EOF) {
+ switch (c) {
+ case 'f':
+ type |= DO_FA;
+ break;
+ case 'h':
+ type |= DO_HA;
+ break;
+ case 'n':
+ /*
+ * private flag: if true, we don't try to resolve
+ * addresses into host names. This can result in
+ * a significantly faster listing, and follows the
+ * tried and true behavior of utilities like netstat.
+ */
+ flags |= NO_NAME_RESOLUTION;
+ break;
+ case 'p':
+ /*
+ * User wants to see the protection with our agent peers.
+ * This is set in type because doors doesn't see any flags.
+ */
+ flags |= PEER_PROTECTION;
+ break;
+ default:
+ usage(argv);
+ exit(1);
+ }
+ }
+
+ if ((!(type & DO_FA)) && (!(type & DO_HA)))
+ /* Neither is set, so user didn't specify, therefore do both. */
+ type |= DO_BOTH;
+
+ if (flags & PEER_PROTECTION) {
+ if (type & DO_FA)
+ /* user types -fp, wants peers of the FA = HA-peers */
+ enumerate_stats(HOME_AGENT_PEER, flags);
+
+ if (type & DO_HA)
+ /* user types -hp, wants peers of the HA = FA-peers */
+ enumerate_stats(FOREIGN_AGENT_PEER, flags);
+ } else {
+ if (type & DO_FA)
+ enumerate_stats(FOREIGN_AGENT, flags);
+
+ if (type & DO_HA)
+ enumerate_stats(HOME_AGENT, flags);
+ }
+
+ return (0);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/ndd.c b/usr/src/cmd/cmd-inet/usr.sbin/ndd.c
new file mode 100644
index 0000000000..e36b27a264
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/ndd.c
@@ -0,0 +1,299 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1998, 2000, 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 1990 Mentat Inc.
+ * ndd.c 2.1, last change 11/14/90
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <stropts.h>
+#include <inet/nd.h>
+#include <string.h>
+#include <stdlib.h>
+
+static boolean_t do_getset(int fd, int cmd, char *buf, int buf_len);
+static int get_value(char *msg, char *buf, int buf_len);
+static void name_print(char *buf);
+static void getset_interactive(int fd);
+static int open_device(void);
+static char *errmsg(int err);
+static void fatal(char *fmt, ...);
+static void printe(boolean_t print_errno, char *fmt, ...);
+
+static char gbuf[65536]; /* Need 20k for 160 IREs ... */
+static char usage_str[] = "usage: ndd -set device_name name value\n"
+ " ndd [-get] device_name name [name ...]";
+
+/* ARGSUSED */
+int
+main(int argc, char **argv)
+{
+ char *cp, *value;
+ int cmd;
+ int fd;
+
+ if (!(cp = *++argv)) {
+ while ((fd = open_device()) != -1) {
+ getset_interactive(fd);
+ (void) close(fd);
+ }
+ return (EXIT_SUCCESS);
+ }
+
+ cmd = ND_GET;
+ if (cp[0] == '-') {
+ if (strncmp(&cp[1], "set", 3) == 0)
+ cmd = ND_SET;
+ else if (strncmp(&cp[1], "get", 3) != 0)
+ fatal(usage_str);
+ if (!(cp = *++argv))
+ fatal(usage_str);
+ }
+ if ((fd = open(cp, O_RDWR)) == -1)
+ fatal("open of %s failed: %s", cp, errmsg(errno));
+
+ if (!isastream(fd))
+ fatal("%s is not a streams device", cp);
+
+ if (!(cp = *++argv)) {
+ getset_interactive(fd);
+ (void) close(fd);
+ return (EXIT_SUCCESS);
+ }
+
+ if (cmd == ND_SET) {
+ if (!(value = *++argv))
+ fatal(usage_str);
+ (void) snprintf(gbuf, sizeof (gbuf), "%s%c%s%c", cp, '\0',
+ value, '\0');
+ if (!do_getset(fd, cmd, gbuf, sizeof (gbuf)))
+ return (EXIT_FAILURE);
+ } else {
+ do {
+ (void) memset(gbuf, '\0', sizeof (gbuf));
+ (void) strlcpy(gbuf, cp, sizeof (gbuf));
+ if (!do_getset(fd, cmd, gbuf, sizeof (gbuf)))
+ return (EXIT_FAILURE);
+ if (cp = *++argv)
+ (void) putchar('\n');
+ } while (cp);
+ }
+
+ (void) close(fd);
+ return (EXIT_SUCCESS);
+}
+
+static void
+name_print(char *buf)
+{
+ char *cp, *rwtag;
+
+ for (cp = buf; cp[0]; ) {
+ for (rwtag = cp; !isspace(*rwtag); rwtag++)
+ ;
+ *rwtag++ = '\0';
+ while (isspace(*rwtag))
+ rwtag++;
+ (void) printf("%-30s%s\n", cp, rwtag);
+ for (cp = rwtag; *cp++; )
+ ;
+ }
+}
+
+/*
+ * This function is vile, but it's better here than in the kernel.
+ */
+static boolean_t
+is_obsolete(const char *param)
+{
+ if (strcmp(param, "ip_enable_group_ifs") == 0 ||
+ strcmp(param, "ifgrp_status") == 0) {
+ (void) fprintf(stderr, "The \"%s\" tunable has been superseded "
+ "by IP Multipathing.\nPlease see the IP Network "
+ "Multipathing Administration Guide for details.\n", param);
+ return (B_TRUE);
+ }
+ return (B_FALSE);
+}
+
+static boolean_t
+do_getset(int fd, int cmd, char *buf, int buf_len)
+{
+ char *cp;
+ struct strioctl stri;
+ boolean_t is_name_get;
+
+ if (is_obsolete(buf))
+ return (B_TRUE);
+
+ stri.ic_cmd = cmd;
+ stri.ic_timout = 0;
+ stri.ic_len = buf_len;
+ stri.ic_dp = buf;
+ is_name_get = stri.ic_cmd == ND_GET && buf[0] == '?' && buf[1] == '\0';
+
+ if (ioctl(fd, I_STR, &stri) == -1) {
+ if (errno == ENOENT)
+ (void) printf("name is non-existent for this module\n"
+ "for a list of valid names, use name '?'\n");
+ else
+ (void) printf("operation failed: %s\n", errmsg(errno));
+ return (B_FALSE);
+ }
+ if (is_name_get)
+ name_print(buf);
+ else if (stri.ic_cmd == ND_GET) {
+ for (cp = buf; *cp != '\0'; cp += strlen(cp) + 1)
+ (void) puts(cp);
+ }
+ (void) fflush(stdout);
+ return (B_TRUE);
+}
+
+static int
+get_value(char *msg, char *buf, int buf_len)
+{
+ int len;
+
+ (void) printf("%s", msg);
+ (void) fflush(stdout);
+
+ buf[buf_len-1] = '\0';
+ if (fgets(buf, buf_len-1, stdin) == NULL)
+ exit(EXIT_SUCCESS);
+ len = strlen(buf);
+ if (buf[len-1] == '\n')
+ buf[len - 1] = '\0';
+ else
+ len++;
+ return (len);
+}
+
+static void
+getset_interactive(int fd)
+{
+ int cmd;
+ char *cp;
+ int len, buf_len;
+ char len_buf[10];
+
+ for (;;) {
+ (void) memset(gbuf, '\0', sizeof (gbuf));
+ len = get_value("name to get/set ? ", gbuf, sizeof (gbuf));
+ if (len == 1 || (gbuf[0] == 'q' && gbuf[1] == '\0'))
+ return;
+ for (cp = gbuf; cp < &gbuf[len]; cp++) {
+ if (isspace(*cp))
+ *cp = '\0';
+ }
+ cmd = ND_GET;
+ if (gbuf[0] != '?' &&
+ get_value("value ? ", &gbuf[len], sizeof (gbuf) - len) > 1)
+ cmd = ND_SET;
+ if (cmd == ND_GET && gbuf[0] != '?' &&
+ get_value("length ? ", len_buf, sizeof (len_buf)) > 1) {
+ if (!isdigit(len_buf[0])) {
+ (void) printf("invalid length\n");
+ continue;
+ }
+ buf_len = atoi(len_buf);
+ } else
+ buf_len = sizeof (gbuf);
+ (void) do_getset(fd, cmd, gbuf, buf_len);
+ }
+}
+
+static void
+printe(boolean_t print_errno, char *fmt, ...)
+{
+ va_list ap;
+ int error = errno;
+
+ va_start(ap, fmt);
+ (void) printf("*ERROR* ");
+ (void) vprintf(fmt, ap);
+ va_end(ap);
+
+ if (print_errno)
+ (void) printf(": %s\n", errmsg(error));
+ else
+ (void) printf("\n");
+}
+
+static int
+open_device(void)
+{
+ char name[80];
+ int fd, len;
+
+ for (;;) {
+ len = get_value("module to query ? ", name, sizeof (name));
+ if (len <= 1 ||
+ (len == 2 && (name[0] == 'q' || name[0] == 'Q')))
+ return (-1);
+
+ if ((fd = open(name, O_RDWR)) == -1) {
+ printe(B_TRUE, "open of %s failed", name);
+ continue;
+ }
+
+ if (isastream(fd))
+ return (fd);
+
+ (void) close(fd);
+ printe(B_FALSE, "%s is not a streams device", name);
+ }
+}
+
+static void
+fatal(char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ (void) vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ (void) fprintf(stderr, "\n");
+
+ exit(EXIT_FAILURE);
+}
+
+static char *
+errmsg(int error)
+{
+ char *msg = strerror(error);
+
+ return (msg != NULL ? msg : "unknown error");
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/ping/Makefile b/usr/src/cmd/cmd-inet/usr.sbin/ping/Makefile
new file mode 100644
index 0000000000..47fb328f66
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/ping/Makefile
@@ -0,0 +1,90 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+PROG= ping
+PINGOBJS= ping.o ping_aux.o ping_aux6.o
+COMMONOBJS= ifaddrlist.o
+OBJS= $(PINGOBJS) $(COMMONOBJS)
+SUIDPROG= ping
+
+include ../../../Makefile.cmd
+include ../../Makefile.cmd-inet
+
+PINGSRCS= $(PINGOBJS:.o=.c)
+COMMONSRCS= $(CMDINETCOMMONDIR)/$(COMMONOBJS:.o=.c)
+SRCS= $(PINGSRCS) $(COMMONSRCS)
+HDRS= ping.h $(CMDINETCOMMONDIR)/ifaddrlist.h
+
+$(ROOTUSRSBIN)/ping := FILEMODE= 04555
+$(ROOTUSRSBIN)/ping := OWNER= root
+
+
+CPPFLAGS += -I$(CMDINETCOMMONDIR)
+
+# Ping uses the ancillary data feature which is available only through
+# UNIX 98 standards version of Socket interface. This interface is supposed to
+# be accessed by -lxnet. In addition -lsocket and -lnsl are used to
+# capture new not-yet-standard interfaces. Someday -lxnet alone should be enough
+# when IPv6 inspired new interfaces are part of standards.
+LDLIBS += -lxnet -lsocket -lnsl -lm
+
+# these #defines are required to use UNIX 98 interfaces
+_D_UNIX98_EXTN = -D_XOPEN_SOURCE=500 -D__EXTENSIONS__
+
+$(PINGOBJS) := CPPFLAGS += $(_D_UNIX98_EXTN)
+
+LINTFLAGS += $(_D_UNIX98_EXTN)
+
+# Setting the above defines to use the UNIX98 ancillary data feature
+# causes lint to output warnings about lint library declarations conflicting
+# with those in the header files. Since we need these features the best
+# course of action is to switch the types of the resulting warnings off
+# when running lint.
+LINTFLAGS += -erroff=E_INCONS_VAL_TYPE_DECL2 -erroff=E_INCONS_ARG_DECL2 \
+ -erroff=E_FUNC_DECL_VAR_ARG2
+
+.KEEP_STATE:
+.PARALLEL:
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+ $(LINK.c) -o $@ $(OBJS) $(LDLIBS)
+ $(POST_PROCESS)
+
+$(COMMONOBJS): $(COMMONSRCS)
+ $(COMPILE.c) $(COMMONSRCS)
+
+install: all $(ROOTUSRSBINPROG)
+
+clean:
+ $(RM) $(OBJS)
+
+lint: lint_SRCS
+
+include ../../../Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/ping/ping.c b/usr/src/cmd/cmd-inet/usr.sbin/ping/ping.c
new file mode 100644
index 0000000000..85cc4ed135
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/ping/ping.c
@@ -0,0 +1,2264 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T
+ * All Rights Reserved.
+ */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California.
+ * All Rights Reserved.
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <strings.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <signal.h>
+#include <limits.h>
+#include <math.h>
+
+#include <sys/time.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/stropts.h>
+#include <sys/file.h>
+#include <sys/sysmacros.h>
+
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/ip_var.h>
+#include <netinet/ip6.h>
+#include <netinet/icmp6.h>
+#include <netinet/udp.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <priv_utils.h>
+
+#include <ifaddrlist.h>
+#include "ping.h"
+
+/*
+ * This macro is used to compare 16bit, wrapping sequence numbers. Inspired by
+ * TCP's SEQ_LEQ macro.
+ */
+#define PINGSEQ_LEQ(a, b) ((int16_t)((a)-(b)) <= 0)
+
+#define MAX_WAIT 10 /* max sec. to wait for response */
+#define MAX_TRAFFIC_CLASS 255 /* max traffic class for IPv6 */
+#define MAX_FLOW_LABEL 0xFFFFF /* max flow label for IPv6 */
+#define MAX_TOS 255 /* max type-of-service for IPv4 */
+
+#define TIMEOUT 20 /* default timeout value */
+#define DEFAULT_DATALEN 56
+
+#define MULTICAST_NOLOOP 1 /* multicast options */
+#define MULTICAST_TTL 2
+#define MULTICAST_IF 4
+
+#define IF_INDEX 0 /* types of -i argument */
+#define IF_NAME 1
+#define IF_ADDR 2
+#define IF_ADDR6 3
+
+#ifdef BSD
+#define setbuf(s, b) setlinebuf((s))
+#endif /* BSD */
+
+
+/* interface identification */
+union if_id {
+ int index; /* interface index (e.g., 1, 2) */
+ char *name; /* interface name (e.g., le0, hme0) */
+ union any_in_addr addr; /* interface address (e.g., 10.123.4.5) */
+};
+
+/* stores the interface supplied by the user */
+struct if_entry {
+ char *str; /* unresolved, string input */
+ int id_type; /* type of ID (index, name, addr, addr6) */
+ union if_id id; /* ID */
+};
+
+char *progname;
+char *targethost;
+
+static int send_sock; /* send sockets */
+static int send_sock6;
+static struct sockaddr_in to; /* where to send */
+static struct sockaddr_in6 to6;
+static union any_in_addr gw_IP_list[MAX_GWS]; /* gateways */
+static union any_in_addr gw_IP_list6[MAX_GWS6];
+static int if_index = 0; /* outgoing interface index */
+boolean_t is_alive = _B_FALSE; /* is target host alive */
+struct targetaddr *current_targetaddr; /* current target IP address to probe */
+static struct targetaddr *targetaddr_list; /* list of IP addresses to probe */
+static int num_targetaddrs; /* no of target addresses to probe */
+static int num_v4 = 0; /* count of IPv4 addresses */
+static int num_v6 = 0; /* count of IPv6 addresses */
+boolean_t verbose = _B_FALSE; /* verbose output */
+boolean_t stats = _B_FALSE; /* display statistics */
+static boolean_t settos = _B_FALSE; /* set type-of-service value */
+boolean_t rr_option = _B_FALSE; /* true if using record route */
+boolean_t send_reply = _B_FALSE; /* Send an ICMP_{ECHO|TSTAMP}REPLY */
+ /* that goes to target and comes back */
+ /* to the the sender via src routing. */
+boolean_t strict = _B_FALSE; /* true if using strict source route */
+boolean_t ts_option = _B_FALSE; /* true if using timestamp option */
+boolean_t use_icmp_ts = _B_FALSE; /* Use ICMP timestamp request */
+boolean_t use_udp = _B_FALSE; /* Use UDP instead of ICMP */
+boolean_t probe_all = _B_FALSE; /* probe all the IP addresses */
+boolean_t nflag = _B_FALSE; /* do not reverse lookup addresses */
+static int family_input = AF_UNSPEC; /* address family supplied by user */
+int datalen = DEFAULT_DATALEN; /* How much data */
+int ts_flag; /* timestamp flag value */
+static int num_gw; /* number of gateways */
+static int eff_num_gw; /* effective number of gateways */
+ /* if send_reply, it's 2*num_gw+1 */
+static int num_wraps = -1; /* no of times 64K icmp_seq wrapped */
+static ushort_t dest_port = 32768 + 666; /* starting port for the UDP probes */
+static char *gw_list[MAXMAX_GWS]; /* list of gateways as user enters */
+static int interval = 1; /* interval between transmissions */
+static int options; /* socket options */
+static int moptions; /* multicast options */
+int npackets; /* number of packets to send */
+static ushort_t tos; /* type-of-service value */
+static int hoplimit = -1; /* time-to-live value */
+static int timeout = TIMEOUT; /* timeout value (sec) for probes */
+static struct if_entry out_if; /* interface argument */
+int ident; /* ID for this ping run */
+static hrtime_t t_last_probe_sent; /* the time we sent the last probe */
+
+/*
+ * This buffer stores the received packets. Currently it needs to be 32 bit
+ * aligned. In the future, we'll be using 64 bit alignment, so let's use 64 bit
+ * alignment now.
+ */
+static uint64_t in_pkt[(IP_MAXPACKET + 1)/8];
+
+/* Used to store the ancillary data that comes with the received packets */
+static uint64_t ancillary_data[(IP_MAXPACKET + 1)/8];
+
+static int ntransmitted; /* number of packet sent to single IP address */
+int nreceived; /* # of packets we got back from target host */
+int nreceived_last_target; /* received from last target IP */
+/*
+ * These are used for statistics. tmin is initialized to maximum longint value.
+ * The max value is also used for timeouts. All times are in microseconds.
+ */
+long long tmin = LLONG_MAX;
+long long tmax;
+int64_t tsum; /* sum of all times, for doing average */
+int64_t tsum2; /* sum of squared times, for std. dev. */
+
+static struct targetaddr *build_targetaddr_list(struct addrinfo *,
+ union any_in_addr *);
+extern void check_reply(struct addrinfo *, struct msghdr *, int, ushort_t);
+extern void check_reply6(struct addrinfo *, struct msghdr *, int, ushort_t);
+static struct targetaddr *create_targetaddr_item(int, union any_in_addr *,
+ union any_in_addr *);
+void find_dstaddr(ushort_t, union any_in_addr *);
+static struct ifaddrlist *find_if(struct ifaddrlist *, int);
+static void finish();
+static void get_gwaddrs(char *[], int, union any_in_addr *,
+ union any_in_addr *, int *, int *);
+static void get_hostinfo(char *, int, struct addrinfo **);
+static ushort_t in_cksum(ushort_t *, int);
+static int int_arg(char *s, char *what);
+boolean_t is_a_target(struct addrinfo *, union any_in_addr *);
+static void mirror_gws(union any_in_addr *, int);
+static void pinger(int, struct sockaddr *, struct msghdr *, int);
+char *pr_name(char *, int);
+char *pr_protocol(int);
+static void print_unknown_host_msg(const char *, const char *);
+static void recv_icmp_packet(struct addrinfo *, int, int, ushort_t, ushort_t);
+static void resolve_nodes(struct addrinfo **, union any_in_addr **);
+void schedule_sigalrm();
+static void select_all_src_addrs(union any_in_addr **, struct addrinfo *,
+ union any_in_addr *, union any_in_addr *);
+static void select_src_addr(union any_in_addr *, int, union any_in_addr *);
+void send_scheduled_probe();
+boolean_t seq_match(ushort_t, int, ushort_t);
+extern void set_ancillary_data(struct msghdr *, int, union any_in_addr *, int,
+ uint_t);
+extern void set_IPv4_options(int, union any_in_addr *, int, struct in_addr *,
+ struct in_addr *);
+static boolean_t setup_socket(int, int *, int *, int *, ushort_t *);
+void sigalrm_handler();
+void tvsub(struct timeval *, struct timeval *);
+static void usage(char *);
+
+/*
+ * main()
+ */
+void
+main(int argc, char *argv[])
+{
+ struct addrinfo *ai_dst = NULL; /* addrinfo host list */
+ union any_in_addr *src_addr_list = NULL; /* src addrs to use */
+ int recv_sock = -1; /* receive sockets */
+ int recv_sock6 = -1;
+ ushort_t udp_src_port; /* src ports for UDP probes */
+ ushort_t udp_src_port6; /* used to identify replies */
+ uint_t flowinfo = 0;
+ uint_t class = 0;
+ char tmp_buf[INET6_ADDRSTRLEN];
+ int c;
+ int i;
+
+ progname = argv[0];
+
+ /*
+ * This program needs the net_icmpaccess privileges. We'll fail
+ * on the socket call and report the error there when we have
+ * insufficient privileges.
+ */
+ (void) __init_suid_priv(PU_CLEARLIMITSET, PRIV_NET_ICMPACCESS,
+ (char *)NULL);
+
+ setbuf(stdout, (char *)0);
+
+ while ((c = getopt(argc, argv,
+ "aA:c:dF:G:g:I:i:LlnP:p:rRSsTt:UvX:x:Y0123?")) != -1) {
+ switch ((char)c) {
+ case 'A':
+ if (strcmp(optarg, "inet") == 0) {
+ family_input = AF_INET;
+ } else if (strcmp(optarg, "inet6") == 0) {
+ family_input = AF_INET6;
+ } else {
+ Fprintf(stderr,
+ "%s: unknown address family %s\n",
+ progname, optarg);
+ exit(EXIT_FAILURE);
+ }
+ break;
+
+ case 'a':
+ probe_all = _B_TRUE;
+ break;
+
+ case 'c':
+ i = int_arg(optarg, "traffic class");
+ if (i > MAX_TRAFFIC_CLASS) {
+ Fprintf(stderr, "%s: traffic class %d out of "
+ "range\n", progname, i);
+ exit(EXIT_FAILURE);
+ }
+ class = (uint_t)i;
+ break;
+
+ case 'd':
+ options |= SO_DEBUG;
+ break;
+
+ case 'F':
+ i = int_arg(optarg, "flow label");
+ if (i > MAX_FLOW_LABEL) {
+ Fprintf(stderr, "%s: flow label %d out of "
+ "range\n", progname, i);
+ exit(EXIT_FAILURE);
+ }
+ flowinfo = (uint_t)i;
+ break;
+
+ case 'I':
+ stats = _B_TRUE;
+ interval = int_arg(optarg, "interval");
+ break;
+
+ case 'i':
+ /*
+ * this can accept interface index, interface name, and
+ * address configured on the interface
+ */
+ moptions |= MULTICAST_IF;
+ out_if.str = optarg;
+
+ if (inet_pton(AF_INET6, optarg, &out_if.id.addr) > 0) {
+ out_if.id_type = IF_ADDR6;
+ } else if (inet_pton(AF_INET, optarg,
+ &out_if.id.addr) > 0) {
+ out_if.id_type = IF_ADDR;
+ } else if (strcmp(optarg, "0") == 0) {
+ out_if.id_type = IF_INDEX;
+ out_if.id.index = 0;
+ } else if ((out_if.id.index = atoi(optarg)) != 0) {
+ out_if.id_type = IF_INDEX;
+ } else {
+ out_if.id.name = optarg;
+ out_if.id_type = IF_NAME;
+ }
+ break;
+
+ case 'L':
+ moptions |= MULTICAST_NOLOOP;
+ break;
+
+ case 'l':
+ send_reply = _B_TRUE;
+ strict = _B_FALSE;
+ break;
+
+ case 'n':
+ nflag = _B_TRUE;
+ break;
+
+ case 'P':
+ settos = _B_TRUE;
+ i = int_arg(optarg, "type-of-service");
+ if (i > MAX_TOS) {
+ Fprintf(stderr, "%s: tos value %d out of "
+ "range\n", progname, i);
+ exit(EXIT_FAILURE);
+ }
+ tos = (ushort_t)i;
+ break;
+
+ case 'p':
+ i = int_arg(optarg, "port number");
+ if (i > MAX_PORT) {
+ Fprintf(stderr, "%s: port number %d out of "
+ "range\n", progname, i);
+ exit(EXIT_FAILURE);
+ }
+ dest_port = (ushort_t)i;
+ break;
+
+ case 'r':
+ options |= SO_DONTROUTE;
+ break;
+
+ case 'R':
+ rr_option = _B_TRUE;
+ break;
+
+ case 'S':
+ send_reply = _B_TRUE;
+ strict = _B_TRUE;
+ break;
+
+ case 's':
+ stats = _B_TRUE;
+ break;
+
+ case 'T':
+ ts_option = _B_TRUE;
+ break;
+
+ case 't':
+ moptions |= MULTICAST_TTL;
+ hoplimit = int_arg(optarg, "ttl");
+ if (hoplimit > MAXTTL) {
+ Fprintf(stderr, "%s: ttl %d out of range\n",
+ progname, hoplimit);
+ exit(EXIT_FAILURE);
+ }
+ break;
+
+ case 'U':
+ use_udp = _B_TRUE;
+ use_icmp_ts = _B_FALSE;
+ break;
+
+ case 'v':
+ verbose = _B_TRUE;
+ break;
+ /*
+ * 'x' and 'X' has been undocumented flags for source routing.
+ * Now we document loose source routing with the new flag 'g',
+ * which is same as in traceroute. We still keep x/X as
+ * as undocumented. 'G', which is for strict source routing is
+ * also undocumented.
+ */
+ case 'x':
+ case 'g':
+ strict = _B_FALSE;
+ if (num_gw > MAXMAX_GWS) {
+ Fprintf(stderr, "%s: too many gateways\n",
+ progname);
+ exit(EXIT_FAILURE);
+ }
+ gw_list[num_gw++] = optarg;
+ break;
+
+ case 'X':
+ case 'G':
+ strict = _B_TRUE;
+ if (num_gw > MAXMAX_GWS) {
+ Fprintf(stderr, "%s: too many gateways\n",
+ progname);
+ exit(EXIT_FAILURE);
+ }
+ gw_list[num_gw++] = optarg;
+ break;
+
+ case 'Y':
+ use_icmp_ts = _B_TRUE;
+ use_udp = _B_FALSE;
+ break;
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ ts_flag = (char)c - '0';
+ break;
+
+ case '?':
+ usage(progname);
+ exit(EXIT_FAILURE);
+ break;
+
+ default:
+ usage(progname);
+ exit(EXIT_FAILURE);
+ break;
+ }
+ }
+
+ if (optind >= argc) {
+ usage(progname);
+ exit(EXIT_FAILURE);
+ }
+
+ /*
+ * send_reply, which sends the probe packet back to itself
+ * doesn't work with UDP
+ */
+ if (use_udp)
+ send_reply = _B_FALSE;
+
+ targethost = argv[optind];
+ optind++;
+ if (optind < argc) {
+ if (stats) {
+ datalen = int_arg(argv[optind], "data size");
+ optind++;
+ if (optind < argc) {
+ npackets = int_arg(argv[optind],
+ "packet count");
+ if (npackets < 1) {
+ Fprintf(stderr, "%s: packet count %d "
+ "out of range\n", progname,
+ npackets);
+ exit(EXIT_FAILURE);
+ }
+ }
+ } else {
+ timeout = int_arg(argv[optind], "timeout");
+ }
+ }
+
+ /*
+ * Let's prepare sockaddr_in* structures, cause we might need both of
+ * them.
+ */
+ bzero((char *)&to, sizeof (struct sockaddr_in));
+ to.sin_family = AF_INET;
+
+ bzero((char *)&to6, sizeof (struct sockaddr_in6));
+ to6.sin6_family = AF_INET6;
+ to6.sin6_flowinfo = htonl((class << 20) | flowinfo);
+
+ if (stats)
+ (void) sigset(SIGINT, finish);
+
+ ident = (int)getpid() & 0xFFFF;
+
+ /* resolve the hostnames */
+ resolve_nodes(&ai_dst, &src_addr_list);
+
+ /*
+ * We should make sure datalen is reasonable.
+ * IP_MAXPACKET >= IPv4/IPv6 header length +
+ * IPv4 options/IPv6 routing header length +
+ * ICMP/ICMP6/UDP header length +
+ * datalen
+ */
+
+ if (family_input == AF_INET6 ||
+ (family_input == AF_UNSPEC && num_v6 != 0)) {
+ size_t exthdr_len = 0;
+
+ if (send_reply) {
+ exthdr_len = sizeof (struct ip6_rthdr0) +
+ 2 * num_gw * sizeof (struct in6_addr);
+ } else if (num_gw > 0) {
+ exthdr_len = sizeof (struct ip6_rthdr0) +
+ num_gw * sizeof (struct in6_addr);
+ }
+
+ /*
+ * Size of ICMP6 header and UDP header are the same. Let's
+ * use ICMP6_MINLEN.
+ */
+ if (datalen > (IP_MAXPACKET - (sizeof (struct ip6_hdr) +
+ exthdr_len + ICMP6_MINLEN))) {
+ Fprintf(stderr,
+ "%s: data size too large for IPv6 packet\n",
+ progname);
+ num_v6 = 0;
+ }
+ }
+
+ if (family_input == AF_INET ||
+ (family_input == AF_UNSPEC && num_v4 != 0)) {
+ size_t opt_len = 0;
+
+ if (send_reply) {
+ /*
+ * Includes 3 bytes code+ptr+len, the intermediate
+ * gateways, the actual and the effective target.
+ */
+ opt_len = 3 +
+ (2 * num_gw + 2) * sizeof (struct in_addr);
+ } else if (num_gw > 0) {
+ opt_len = 3 + (num_gw + 1) * sizeof (struct in_addr);
+ }
+
+ if (rr_option) {
+ opt_len = MAX_IPOPTLEN;
+ } else if (ts_option) {
+ if ((ts_flag & 0x0f) <= IPOPT_TS_TSANDADDR) {
+ opt_len = MAX_IPOPTLEN;
+ } else {
+ opt_len += IPOPT_MINOFF +
+ 2 * sizeof (struct ipt_ta);
+ /*
+ * Note: BSD/4.X is broken in their check so we
+ * have to bump up this number by at least one.
+ */
+ opt_len++;
+ }
+ }
+
+ /* Round up to 4 byte boundary */
+ if (opt_len & 0x3)
+ opt_len = (opt_len & ~0x3) + 4;
+
+ if (datalen > (IP_MAXPACKET - (sizeof (struct ip) + opt_len +
+ ICMP_MINLEN))) {
+ Fprintf(stderr,
+ "%s: data size too large for IPv4 packet\n",
+ progname);
+ num_v4 = 0;
+ }
+ }
+
+ if (num_v4 == 0 && num_v6 == 0) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* setup the sockets */
+ if (num_v6 != 0) {
+ if (!setup_socket(AF_INET6, &send_sock6, &recv_sock6,
+ &if_index, &udp_src_port6))
+ exit(EXIT_FAILURE);
+ }
+
+ if (num_v4 != 0) {
+ if (!setup_socket(AF_INET, &send_sock, &recv_sock, &if_index,
+ &udp_src_port))
+ exit(EXIT_FAILURE);
+ }
+
+ __priv_relinquish();
+
+ /*
+ * If sending back to ourself, add the mirror image of current
+ * gateways, so that the probes travel to and from the target
+ * by visiting the same gateways in reverse order.
+ */
+ if (send_reply) {
+ if (num_v6 != 0)
+ mirror_gws(gw_IP_list6, AF_INET6);
+ if (num_v4 != 0)
+ mirror_gws(gw_IP_list, AF_INET);
+
+ /* We add 1 because we put the target as the middle gateway */
+ eff_num_gw = 2 * num_gw + 1;
+
+ } else {
+ eff_num_gw = num_gw;
+ }
+
+ targetaddr_list = build_targetaddr_list(ai_dst, src_addr_list);
+ current_targetaddr = targetaddr_list;
+
+ /*
+ * Set the starting_seq_num for the first targetaddr.
+ * If we are sending ICMP Echo Requests, the sequence number is same as
+ * ICMP sequence number, and it starts from zero. If we are sending UDP
+ * packets, the sequence number is the destination UDP port number,
+ * which starts from dest_port. At each probe, this sequence number is
+ * incremented by one.
+ * We set the starting_seq_num for first targetaddr here. The
+ * following ones will be set by looking at where we left with the last
+ * targetaddr.
+ */
+ current_targetaddr->starting_seq_num = use_udp ? dest_port : 0;
+
+ if (stats) {
+ if (probe_all || !nflag) {
+ Printf("PING %s: %d data bytes\n", targethost, datalen);
+ } else {
+ if (ai_dst->ai_family == AF_INET) {
+ Printf("PING %s (%s): %d data bytes\n",
+ targethost,
+ inet_ntop(AF_INET,
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ &((struct sockaddr_in *)
+ ai_dst->ai_addr)->sin_addr,
+ tmp_buf, sizeof (tmp_buf)),
+ datalen);
+ } else {
+ Printf("PING %s (%s): %d data bytes\n",
+ targethost,
+ inet_ntop(AF_INET6,
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ &((struct sockaddr_in6 *)
+ ai_dst->ai_addr)->sin6_addr,
+ tmp_buf, sizeof (tmp_buf)),
+ datalen);
+ }
+ }
+ }
+
+ /* Let's get things going */
+ send_scheduled_probe();
+
+ /* SIGALRM is used to send the next scheduled probe */
+ (void) sigset(SIGALRM, sigalrm_handler);
+ schedule_sigalrm();
+
+ /*
+ * From now on, we'll always be listening to ICMP packets. As SIGALRM
+ * comes in, sigalrm_handler() will be invoked and send another
+ * probe.
+ */
+ recv_icmp_packet(ai_dst, recv_sock6, recv_sock, udp_src_port6,
+ udp_src_port);
+
+ exit(EXIT_SUCCESS); /* should never come here */
+}
+
+/*
+ * Build the target IP address list. Use command line options and
+ * name lookup results returned from name server to determine which addresses
+ * to probe, how many times, in which order.
+ */
+static struct targetaddr *
+build_targetaddr_list(struct addrinfo *ai_dst, union any_in_addr *src_addr_list)
+{
+ struct targetaddr *head = NULL;
+ struct targetaddr *targetaddr;
+ struct targetaddr **nextp;
+ int num_dst;
+ int i;
+ struct addrinfo *aip;
+
+ aip = ai_dst;
+ if (probe_all)
+ num_dst = num_v4 + num_v6;
+ else
+ num_dst = 1;
+ num_targetaddrs = num_dst;
+ nextp = &head;
+ for (aip = ai_dst, i = 0; aip != NULL; aip = aip->ai_next, i++) {
+ if (aip->ai_family == AF_INET && num_v4 != 0) {
+ targetaddr = create_targetaddr_item(aip->ai_family,
+ (union any_in_addr *)
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ &((struct sockaddr_in *)
+ aip->ai_addr)->sin_addr,
+ &src_addr_list[i]);
+ } else if (aip->ai_family == AF_INET6 && num_v6 != 0) {
+ targetaddr = create_targetaddr_item(aip->ai_family,
+ (union any_in_addr *)
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ &((struct sockaddr_in6 *)
+ aip->ai_addr)->sin6_addr,
+ &src_addr_list[i]);
+ } else {
+ continue;
+ }
+ *nextp = targetaddr;
+ nextp = &targetaddr->next;
+ if (num_targetaddrs == 1)
+ break;
+ }
+ if (npackets == 0 && stats)
+ *nextp = head; /* keep going indefinitely */
+
+ return (head);
+}
+
+/*
+ * Given an address family, dst and src addresses, by also looking at the
+ * options provided at the command line, this function creates a targetaddr
+ * to be linked with others, forming a global targetaddr list. Each targetaddr
+ * item contains information about probes sent to a specific IP address.
+ */
+static struct targetaddr *
+create_targetaddr_item(int family, union any_in_addr *dst_addr,
+ union any_in_addr *src_addr)
+{
+ struct targetaddr *targetaddr;
+
+ targetaddr = (struct targetaddr *)malloc(sizeof (struct targetaddr));
+ if (targetaddr == NULL) {
+ Fprintf(stderr, "%s: malloc %s\n", progname, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ targetaddr->family = family;
+ targetaddr->dst_addr = *dst_addr;
+ targetaddr->src_addr = *src_addr;
+ if (stats) {
+ /*
+ * npackets is only defined if we are in stats mode.
+ * npackets determines how many probes to send to each target
+ * IP address. npackets == 0 means send only 1 and move on to
+ * next target IP.
+ */
+ if (npackets > 0)
+ targetaddr->num_probes = npackets;
+ else
+ targetaddr->num_probes = 1;
+ } else {
+ targetaddr->num_probes = timeout;
+ }
+ targetaddr->num_sent = 0;
+ targetaddr->got_reply = _B_FALSE;
+ targetaddr->probing_done = _B_FALSE;
+ targetaddr->starting_seq_num = 0; /* actual value will be set later */
+ targetaddr->next = NULL; /* actual value will be set later */
+
+ return (targetaddr);
+}
+
+/*
+ * print "unknown host" message
+ */
+static void
+print_unknown_host_msg(const char *protocol, const char *hostname)
+{
+ Fprintf(stderr, "%s: unknown%s host %s\n", progname, protocol,
+ hostname);
+}
+
+/*
+ * Resolve hostnames for the target host and gateways. Also, determine source
+ * addresses to use for each target address.
+ */
+static void
+resolve_nodes(struct addrinfo **ai_dstp, union any_in_addr **src_addr_listp)
+{
+ struct addrinfo *ai_dst = NULL;
+ struct addrinfo *aip = NULL;
+ union any_in_addr *src_addr_list = NULL;
+ int num_resolved_gw = 0;
+ int num_resolved_gw6 = 0;
+
+ get_hostinfo(targethost, family_input, &ai_dst);
+ if (ai_dst == NULL) {
+ print_unknown_host_msg("", targethost);
+ exit(EXIT_FAILURE);
+ }
+ /* Get a count of the v4 & v6 addresses */
+ for (aip = ai_dst; aip != NULL; aip = aip->ai_next) {
+ switch (aip->ai_family) {
+ case AF_INET:
+ num_v4++;
+ break;
+ case AF_INET6:
+ num_v6++;
+ break;
+ }
+ }
+
+ if (family_input == AF_UNSPEC && !probe_all) {
+ family_input = ai_dst->ai_family;
+ }
+
+ /* resolve gateways */
+ if (num_gw > 0) {
+ get_gwaddrs(gw_list, family_input, gw_IP_list, gw_IP_list6,
+ &num_resolved_gw, &num_resolved_gw6);
+
+ /* we couldn't resolve a gateway as an IPv6 host */
+ if (num_resolved_gw6 != num_gw && num_v6 != 0 &&
+ (family_input == AF_INET6 || family_input == AF_UNSPEC)) {
+ print_unknown_host_msg(" IPv6",
+ gw_list[num_resolved_gw6]);
+ num_v6 = 0;
+ }
+
+ /* we couldn't resolve a gateway as an IPv4 host */
+ if (num_resolved_gw != num_gw && num_v4 != 0 &&
+ (family_input == AF_INET || family_input == AF_UNSPEC)) {
+ print_unknown_host_msg(" IPv4",
+ gw_list[num_resolved_gw]);
+ num_v4 = 0;
+ }
+ }
+
+ if (num_v4 == 0 && num_v6 == 0)
+ exit(EXIT_FAILURE);
+
+ select_all_src_addrs(&src_addr_list, ai_dst, gw_IP_list, gw_IP_list6);
+ *ai_dstp = ai_dst;
+ *src_addr_listp = src_addr_list;
+}
+
+/*
+ * Resolve the gateway names, splitting results into v4 and v6 lists.
+ * Gateway addresses are added to the appropriate passed-in array; the
+ * number of resolved gateways for each af is returned in resolved[6].
+ * Assumes that passed-in arrays are large enough for MAX_GWS[6] addrs
+ * and resolved[6] ptrs are non-null; ignores array and counter if the
+ * address family param makes them irrelevant.
+ */
+static void
+get_gwaddrs(char **gw_list, int family, union any_in_addr *gwIPlist,
+ union any_in_addr *gwIPlist6, int *resolved, int *resolved6)
+{
+ int i;
+ boolean_t check_v4 = _B_TRUE, check_v6 = _B_TRUE;
+ struct addrinfo *ai = NULL;
+ struct addrinfo *aip = NULL;
+
+ *resolved = *resolved6 = 0;
+ switch (family) {
+ case AF_UNSPEC:
+ break;
+ case AF_INET:
+ check_v6 = _B_FALSE;
+ break;
+ case AF_INET6:
+ check_v4 = _B_FALSE;
+ break;
+ default:
+ return;
+ }
+
+ if (check_v4 && num_gw >= MAX_GWS) {
+ check_v4 = _B_FALSE;
+ Fprintf(stderr, "%s: too many IPv4 gateways\n", progname);
+ }
+ if (check_v6 && num_gw > MAX_GWS6) {
+ check_v6 = _B_FALSE;
+ Fprintf(stderr, "%s: too many IPv6 gateways\n", progname);
+ }
+
+ for (i = 0; i < num_gw; i++) {
+ if (!check_v4 && !check_v6)
+ return;
+ get_hostinfo(gw_list[i], family, &ai);
+ if (ai == NULL)
+ return;
+ if (check_v4 && num_v4 != 0) {
+ for (aip = ai; aip != NULL; aip = aip->ai_next) {
+ if (aip->ai_family == AF_INET) {
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ bcopy(&((struct sockaddr_in *)
+ aip->ai_addr)->sin_addr,
+ &gwIPlist[i].addr,
+ aip->ai_addrlen);
+ (*resolved)++;
+ break;
+ }
+ }
+ } else if (check_v4) {
+ check_v4 = _B_FALSE;
+ }
+ if (check_v6 && num_v6 != 0) {
+ for (aip = ai; aip != NULL; aip = aip->ai_next) {
+ if (aip->ai_family == AF_INET6) {
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ bcopy(&((struct sockaddr_in6 *)
+ aip->ai_addr)->sin6_addr,
+ &gwIPlist6[i].addr6,
+ aip->ai_addrlen);
+ (*resolved6)++;
+ break;
+ }
+ }
+ } else if (check_v6) {
+ check_v6 = _B_FALSE;
+ }
+ }
+ freeaddrinfo(ai);
+}
+
+/*
+ * Given the list of gateways, extends the list with its mirror image. This is
+ * used when -l/-S is used. The middle gateway will be the target address. We'll
+ * leave it blank for now.
+ */
+static void
+mirror_gws(union any_in_addr *gwIPlist, int family)
+{
+ int effective_num_gw;
+ int i;
+
+ /* We add 1 because we put the target as the middle gateway */
+ effective_num_gw = 2 * num_gw + 1;
+
+ if ((family == AF_INET && effective_num_gw >= MAX_GWS) ||
+ (family == AF_INET6 && effective_num_gw > MAX_GWS6)) {
+ Fprintf(stderr, "%s: too many %s gateways\n",
+ progname, (family == AF_INET) ? "IPv4" : "IPv6");
+ exit(EXIT_FAILURE);
+ }
+
+ for (i = 0; i < num_gw; i++)
+ gwIPlist[num_gw + i + 1].addr6 = gwIPlist[num_gw - i - 1].addr6;
+}
+
+/*
+ * Given IP address or hostname, return addrinfo list.
+ * Assumes that addrinfo ** ptr is non-null.
+ */
+static void
+get_hostinfo(char *host, int family, struct addrinfo **aipp)
+{
+ struct addrinfo hints, *ai;
+ struct in6_addr addr6;
+ struct in_addr addr;
+ boolean_t broadcast; /* is this 255.255.255.255? */
+ char tmp_buf[INET6_ADDRSTRLEN];
+ int rc;
+
+ /* check if broadcast */
+ if (strcmp(host, "255.255.255.255") == 0)
+ broadcast = _B_TRUE;
+ else
+ broadcast = _B_FALSE;
+
+ /* check if IPv4-mapped address or broadcast */
+ if (((inet_pton(AF_INET6, host, &addr6) > 0) &&
+ IN6_IS_ADDR_V4MAPPED(&addr6)) || broadcast) {
+ if (!broadcast) {
+ /*
+ * Peel off the "mapping" stuff, leaving 32 bit IPv4
+ * address.
+ */
+ IN6_V4MAPPED_TO_INADDR(&addr6, &addr);
+
+ /* convert it back to a string */
+ (void) inet_ntop(AF_INET, (void *)&addr, tmp_buf,
+ sizeof (tmp_buf));
+ /*
+ * Now the host is an IPv4 address.
+ * Since it previously was a v4 mapped v6 address
+ * we can be sure that the size of buffer 'host'
+ * is large enough to contain the associated v4
+ * address and so we don't need to use a strn/lcpy
+ * here.
+ */
+ (void) strcpy(host, tmp_buf);
+ }
+ /*
+ * If it's a broadcast address, it cannot be an IPv6 address.
+ * Also, if it's a mapped address, we convert it into IPv4
+ * address because ping will send and receive IPv4 packets for
+ * that address. Therefore, it's a failure case to ask
+ * get_hostinfo() to treat a broadcast or a mapped address
+ * as an IPv6 address.
+ */
+ if (family == AF_INET6) {
+ return;
+ }
+ }
+
+ (void) memset(&hints, 0, sizeof (hints));
+ hints.ai_family = family;
+ hints.ai_flags = AI_ADDRCONFIG;
+ rc = getaddrinfo(host, NULL, &hints, &ai);
+ if (rc != 0) {
+ if (rc != EAI_NONAME)
+ Fprintf(stderr, "%s: getaddrinfo: %s\n", progname,
+ gai_strerror(rc));
+ return;
+ }
+ *aipp = ai;
+}
+
+/*
+ * For each IP address of the target host, determine a source address to use.
+ */
+static void
+select_all_src_addrs(union any_in_addr **src_addr_list, struct addrinfo *ai,
+ union any_in_addr *gwv4, union any_in_addr *gwv6)
+{
+ union any_in_addr *list;
+ struct addrinfo *aip;
+ int num_dst = 1;
+ int i;
+
+ if (probe_all)
+ for (aip = ai; aip->ai_next != NULL;
+ aip = aip->ai_next, num_dst++);
+
+ list = (union any_in_addr *)
+ calloc((size_t)num_dst, sizeof (union any_in_addr));
+ if (list == NULL) {
+ Fprintf(stderr, "%s: calloc: %s\n", progname, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ /*
+ * If there's a gateway, a routing header as a consequence, our kernel
+ * picks the source address based on the first hop address, rather than
+ * final destination address.
+ */
+ if (num_gw > 0) {
+ if (ai->ai_family == AF_INET)
+ select_src_addr(gwv4, ai->ai_family, &list[0]);
+ else
+ select_src_addr(gwv6, ai->ai_family, &list[0]);
+ /*
+ * Since the first gateway address is fixed, we'll use the same
+ * src address for every different final destination address
+ * we send to.
+ */
+ for (i = 1; i < num_dst; i++)
+ list[i] = list[0];
+ } else {
+ /*
+ * Although something like 'ping -l host' results in a routing
+ * header, the first gateway address is the target host's
+ * address. Therefore, as far as src address selection goes,
+ * the result is same as having no routing header.
+ */
+ for (i = 0, aip = ai; i < num_dst && aip != NULL;
+ i++, aip = aip->ai_next) {
+ if (aip->ai_family == AF_INET) {
+ if (num_v4 != 0) {
+ select_src_addr((union any_in_addr *)
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ &((struct sockaddr_in *)
+ aip->ai_addr)->sin_addr,
+ aip->ai_family,
+ &list[i]);
+ }
+ } else {
+ if (num_v6 != 0) {
+ select_src_addr((union any_in_addr *)
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ &((struct sockaddr_in6 *)
+ aip->ai_addr)->sin6_addr,
+ aip->ai_family,
+ &list[i]);
+ }
+ }
+ }
+ }
+
+ *src_addr_list = list;
+}
+
+/*
+ * For a given destination address, determine a source address to use.
+ * Returns wildcard address if it cannot determine the source address.
+ */
+static void
+select_src_addr(union any_in_addr *dst_addr, int family,
+ union any_in_addr *src_addr)
+{
+ struct sockaddr *sock;
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+ int tmp_fd;
+ size_t sock_len;
+
+ sock = (struct sockaddr *)malloc(sizeof (struct sockaddr_in6));
+ if (sock == NULL) {
+ Fprintf(stderr, "%s: malloc: %s\n", progname, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ (void) bzero(sock, sizeof (struct sockaddr_in6));
+
+ if (family == AF_INET) {
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ sin = (struct sockaddr_in *)sock;
+ sin->sin_family = AF_INET;
+ sin->sin_addr = dst_addr->addr;
+ sin->sin_port = IPPORT_ECHO; /* port shouldn't be 0 */
+ sock_len = sizeof (struct sockaddr_in);
+ } else {
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ sin6 = (struct sockaddr_in6 *)sock;
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_addr = dst_addr->addr6;
+ sin6->sin6_port = IPPORT_ECHO; /* port shouldn't be 0 */
+ sock_len = sizeof (struct sockaddr_in6);
+ }
+
+ /* open a UDP socket */
+ if ((tmp_fd = socket(family, SOCK_DGRAM, 0)) < 0) {
+ Fprintf(stderr, "%s: udp socket: %s\n", progname,
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ /* connect it */
+ if (connect(tmp_fd, sock, sock_len) < 0) {
+ /*
+ * If there's no route to the destination, this connect() call
+ * fails. We just return all-zero (wildcard) as the source
+ * address, so that user can get to see "no route to dest"
+ * message, as it'll try to send the probe packet out and will
+ * receive ICMP unreachable.
+ */
+ if (family == AF_INET)
+ src_addr->addr.s_addr = INADDR_ANY;
+ else
+ src_addr->addr6 = in6addr_any;
+ free(sock);
+ return;
+ }
+
+ /* get the local sock info */
+ if (getsockname(tmp_fd, sock, &sock_len) < 0) {
+ Fprintf(stderr, "%s: getsockname: %s\n", progname,
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ if (family == AF_INET) {
+ src_addr->addr = sin->sin_addr;
+ } else {
+ src_addr->addr6 = sin6->sin6_addr;
+ }
+
+ (void) close(tmp_fd);
+ free(sock);
+}
+
+/*
+ * Setup the socket for the given address family.
+ * Returns _B_TRUE on success, _B_FALSE on failure. Failure is the case when no
+ * interface can be found, or the specified interface (-i) is not found. On
+ * library call failures, it exit()s.
+ */
+static boolean_t
+setup_socket(int family, int *send_sockp, int *recv_sockp, int *if_index,
+ ushort_t *udp_src_port)
+{
+ int send_sock;
+ int recv_sock;
+ struct sockaddr_in6 sin6;
+ struct sockaddr_in sin;
+ struct sockaddr *sp;
+ size_t slen;
+ int on = 1;
+ uchar_t char_op;
+ int int_op;
+
+ /* now we need the net_icmpaccess privilege */
+ (void) __priv_bracket(PRIV_ON);
+
+ recv_sock = socket(family, SOCK_RAW,
+ (family == AF_INET) ? IPPROTO_ICMP : IPPROTO_ICMPV6);
+
+ if (recv_sock < 0) {
+ Fprintf(stderr, "%s: socket %s\n", progname, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ /* revert to non-privileged user after opening sockets */
+ (void) __priv_bracket(PRIV_OFF);
+
+ /*
+ * We always receive on raw icmp socket. But the sending socket can be
+ * raw icmp or udp, depending on the use of -U flag.
+ */
+ if (use_udp) {
+ send_sock = socket(family, SOCK_DGRAM, IPPROTO_UDP);
+ if (send_sock < 0) {
+ Fprintf(stderr, "%s: socket %s\n", progname,
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ /*
+ * In order to distinguish replies to our UDP probes from
+ * other pings', we need to know our source port number.
+ */
+ if (family == AF_INET) {
+ sp = (struct sockaddr *)&sin;
+ slen = sizeof (sin);
+ } else {
+ sp = (struct sockaddr *)&sin6;
+ slen = sizeof (sin6);
+ }
+ bzero(sp, slen);
+ sp->sa_family = family;
+
+ /* Let's bind() send_sock to wildcard address and port */
+ if (bind(send_sock, sp, slen) < 0) {
+ Fprintf(stderr, "%s: bind %s\n", progname,
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ /* .... and see what port kernel picked for us */
+ if (getsockname(send_sock, sp, &slen) < 0) {
+ Fprintf(stderr, "%s: getsockname %s\n", progname,
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ *udp_src_port = (family == AF_INET) ? sin.sin_port :
+ sin6.sin6_port;
+ } else {
+ send_sock = recv_sock;
+ }
+
+ int_op = 48 * 1024;
+ if (int_op < datalen)
+ int_op = datalen;
+ if (setsockopt(recv_sock, SOL_SOCKET, SO_RCVBUF, (char *)&int_op,
+ sizeof (int_op)) == -1) {
+ Fprintf(stderr, "%s: setsockopt SO_RCVBUF %s\n", progname,
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ if (setsockopt(send_sock, SOL_SOCKET, SO_SNDBUF, (char *)&int_op,
+ sizeof (int_op)) == -1) {
+ Fprintf(stderr, "%s: setsockopt SO_SNDBUF %s\n", progname,
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ if (options & SO_DEBUG) {
+ if (setsockopt(send_sock, SOL_SOCKET, SO_DEBUG, (char *)&on,
+ sizeof (on)) == -1) {
+ Fprintf(stderr, "%s: setsockopt SO_DEBUG %s\n",
+ progname, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (options & SO_DONTROUTE) {
+ if (setsockopt(send_sock, SOL_SOCKET, SO_DONTROUTE, (char *)&on,
+ sizeof (on)) == -1) {
+ Fprintf(stderr, "%s: setsockopt SO_DONTROUTE %s\n",
+ progname, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (moptions & MULTICAST_NOLOOP) {
+ if (family == AF_INET) {
+ char_op = 0; /* used to turn off option */
+
+ if (setsockopt(send_sock, IPPROTO_IP, IP_MULTICAST_LOOP,
+ (char *)&char_op, sizeof (char_op)) == -1) {
+ Fprintf(stderr, "%s: setsockopt "
+ "IP_MULTICAST_NOLOOP %s\n", progname,
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ } else {
+ int_op = 0; /* used to turn off option */
+
+ if (setsockopt(send_sock, IPPROTO_IPV6,
+ IPV6_MULTICAST_LOOP, (char *)&int_op,
+ sizeof (int_op)) == -1) {
+ Fprintf(stderr, "%s: setsockopt "
+ "IPV6_MULTICAST_NOLOOP %s\n", progname,
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+
+ if (moptions & MULTICAST_TTL) {
+ char_op = hoplimit;
+
+ if (family == AF_INET) {
+ if (setsockopt(send_sock, IPPROTO_IP, IP_MULTICAST_TTL,
+ (char *)&char_op, sizeof (char)) == -1) {
+ Fprintf(stderr, "%s: setsockopt "
+ "IP_MULTICAST_TTL %s\n", progname,
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ if (setsockopt(send_sock, IPPROTO_IP, IP_TTL,
+ (char *)&hoplimit, sizeof (hoplimit)) == -1) {
+ Fprintf(stderr, "%s: setsockopt IP_TTL %s\n",
+ progname, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ }
+ /*
+ * AF_INET6 case is handled in set_ancillary_data() function.
+ * This is because when ancillary data is used (for routing
+ * header and outgoing interface index), the hoplimit set using
+ * setsockopt() is ignored.
+ */
+ }
+
+ /* did the user specify an interface? */
+ if (moptions & MULTICAST_IF) {
+ struct ifaddrlist *al = NULL; /* interface list */
+ struct ifaddrlist *my_if;
+ char errbuf[ERRBUFSIZE];
+ int num_ifs;
+ int num_src_ifs; /* exclude down and loopback */
+ int i;
+
+ /* pull out the interface list */
+ num_ifs = ifaddrlist(&al, family, errbuf);
+ if (num_ifs == -1) {
+ Fprintf(stderr, "%s: %s\n", progname, errbuf);
+ exit(EXIT_FAILURE);
+ }
+
+ /* filter out down and loopback interfaces */
+ num_src_ifs = 0;
+ for (i = 0; i < num_ifs; i++) {
+ if (!(al[i].flags & IFF_LOOPBACK) &&
+ (al[i].flags & IFF_UP))
+ num_src_ifs++;
+ }
+
+ if (num_src_ifs == 0) {
+ Fprintf(stderr, "%s: can't find any %s interface\n",
+ progname, (family == AF_INET) ? "IPv4" : "IPv6");
+
+ return (_B_FALSE); /* failure */
+ }
+
+ /* locate the specified interface */
+ my_if = find_if(al, num_ifs);
+ if (my_if == NULL) {
+ Fprintf(stderr, "%s: %s is an invalid %s interface\n",
+ progname, out_if.str,
+ (family == AF_INET) ? "IPv4" : "IPv6");
+
+ return (_B_FALSE);
+ }
+
+ if (family == AF_INET) {
+ if (setsockopt(send_sock, IPPROTO_IP, IP_MULTICAST_IF,
+ (char *)&my_if->addr.addr,
+ sizeof (struct in_addr)) == -1) {
+ Fprintf(stderr, "%s: setsockopt "
+ "IP_MULTICAST_IF %s\n", progname,
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ } else {
+ /*
+ * the outgoing interface is set in set_ancillary_data()
+ * function
+ */
+ *if_index = my_if->index;
+ }
+
+ free(al);
+ }
+
+ if (settos && family == AF_INET) {
+ int_op = tos;
+ if (setsockopt(send_sock, IPPROTO_IP, IP_TOS, (char *)&int_op,
+ sizeof (int_op)) == -1) {
+ Fprintf(stderr, "%s: setsockopt IP_TOS %s\n",
+ progname, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ /* receiving IPv6 extension headers in verbose mode */
+ if (verbose && family == AF_INET6) {
+ if (setsockopt(recv_sock, IPPROTO_IPV6, IPV6_RECVHOPOPTS,
+ (char *)&on, sizeof (on)) == -1) {
+ Fprintf(stderr, "%s: setsockopt IPV6_RECVHOPOPTS %s\n",
+ progname, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ if (setsockopt(recv_sock, IPPROTO_IPV6, IPV6_RECVDSTOPTS,
+ (char *)&on, sizeof (on)) == -1) {
+ Fprintf(stderr, "%s: setsockopt IPV6_RECVDSTOPTS %s\n",
+ progname, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ if (setsockopt(recv_sock, IPPROTO_IPV6, IPV6_RECVRTHDR,
+ (char *)&on, sizeof (on)) == -1) {
+ Fprintf(stderr, "%s: setsockopt IPV6_RECVRTHDR %s\n",
+ progname, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ if (setsockopt(recv_sock, IPPROTO_IPV6, IPV6_RECVRTHDRDSTOPTS,
+ (char *)&on, sizeof (on)) == -1) {
+ Fprintf(stderr,
+ "%s: setsockopt IPV6_RECVRTHDRDSTOPTS %s\n",
+ progname, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ *send_sockp = send_sock;
+ *recv_sockp = recv_sock;
+
+ /* successful */
+ return (_B_TRUE);
+}
+
+/*
+ * Pull out the record containing all the info about the interface specified by
+ * `out_if'. Skips interfaces which are down or loopback.
+ */
+static struct ifaddrlist *
+find_if(struct ifaddrlist *al, int num_ifs)
+{
+ static struct ifaddrlist tmp_if;
+ boolean_t found;
+ int i;
+
+ i = 0;
+ found = _B_FALSE;
+
+ while (i < num_ifs && !found) {
+ tmp_if = al[i];
+
+ /* skip down or loopback interfaces */
+ if ((tmp_if.flags & IFF_LOOPBACK) || !(tmp_if.flags & IFF_UP)) {
+ i++;
+ continue;
+ }
+
+ /* the type of interface id is variable */
+ switch (out_if.id_type) {
+ case IF_INDEX:
+ if (out_if.id.index == tmp_if.index)
+ found = _B_TRUE;
+ break;
+
+ case IF_NAME:
+ if (strcmp(out_if.id.name, tmp_if.device) == 0)
+ found = _B_TRUE;
+ break;
+
+ case IF_ADDR:
+ if (out_if.id.addr.addr.s_addr ==
+ tmp_if.addr.addr.s_addr) {
+ found = _B_TRUE;
+ }
+ break;
+
+ case IF_ADDR6:
+ if (IN6_ARE_ADDR_EQUAL(&out_if.id.addr.addr6,
+ &tmp_if.addr.addr6)) {
+ found = _B_TRUE;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ i++;
+ }
+
+ if (found)
+ return (&tmp_if);
+ else
+ return (NULL);
+}
+
+/*
+ * Invoked by SIGALRM, sigalrm_handler() is, responsible for calling
+ * send_scheduled_probe() to send next probe.
+ */
+void
+sigalrm_handler(void)
+{
+ /*
+ * Guard againist denial-of-service attacks. Make sure ping doesn't
+ * send probes for every SIGALRM it receives. Evil hacker can generate
+ * SIGALRMs as fast as it can, but ping will ignore those which are
+ * received too soon (earlier than 0.5 sec) after it sent the last
+ * probe. We use gethrtime() instead of gettimeofday() because
+ * the latter is not linear and is prone to resetting or drifting
+ */
+ if ((gethrtime() - t_last_probe_sent) < 500000000) {
+ return;
+ }
+ send_scheduled_probe();
+ schedule_sigalrm();
+}
+
+/*
+ * Schedule next SIGALRM.
+ */
+void
+schedule_sigalrm(void)
+{
+ int waittime;
+
+ if (npackets == 0 ||
+ current_targetaddr->num_sent < current_targetaddr->num_probes) {
+ (void) alarm(interval);
+ } else {
+ if (current_targetaddr->got_reply) {
+ waittime = 2 * tmax / MICROSEC;
+ if (waittime == 0)
+ waittime = 1;
+ } else {
+ waittime = MAX_WAIT;
+ }
+ (void) alarm(waittime);
+ }
+}
+
+/*
+ * Called by sigalrm_handler(), check_reply() or check_reply6(),
+ * send_scheduled_probe() looks at the current_targetaddr and determines what
+ * should be sent next and calls pinger().
+ */
+void
+send_scheduled_probe()
+{
+ static struct msghdr msg6;
+ static boolean_t first_probe = _B_TRUE;
+ char tmp_buf[INET6_ADDRSTRLEN];
+
+ /*
+ * We are about to move to next targetaddr if it's either we sent
+ * all the probes, or somebody set the probing_done flag to
+ * _B_TRUE prompting us to move on.
+ */
+ if (current_targetaddr->num_sent == current_targetaddr->num_probes ||
+ current_targetaddr->probing_done) {
+ /*
+ * is this a dead target?
+ */
+ if (!stats && !current_targetaddr->got_reply) {
+ if (!probe_all) {
+ Printf("no answer from %s\n", targethost);
+ } else {
+ Printf("no answer from %s(%s)\n", targethost,
+ inet_ntop(current_targetaddr->family,
+ &current_targetaddr->dst_addr,
+ tmp_buf, sizeof (tmp_buf)));
+ }
+ }
+ /*
+ * Before we move onto next item, let's do some clean up.
+ */
+ current_targetaddr->got_reply = _B_FALSE;
+ current_targetaddr->probing_done = _B_FALSE;
+ /*
+ * If this is probe-all without stats mode, then we need to
+ * preserve this count. This is needed when we try to map an
+ * icmp_seq to IP address. Otherwise, clear it.
+ */
+ if (stats || !probe_all)
+ current_targetaddr->num_sent = 0;
+ nreceived_last_target = 0;
+
+ current_targetaddr = current_targetaddr->next;
+
+ /*
+ * Did we reach the end of road?
+ */
+ if (current_targetaddr == NULL) {
+ (void) alarm(0); /* cancel alarm */
+ if (stats)
+ finish();
+ if (is_alive)
+ exit(EXIT_SUCCESS);
+ else
+ exit(EXIT_FAILURE);
+ } else {
+ /*
+ * We use starting_seq_num for authenticating replies.
+ * Each time we move to a new targetaddr, which has
+ * a different target IP address, we update this field.
+ */
+ current_targetaddr->starting_seq_num =
+ use_udp ? dest_port :
+ (ntransmitted % (MAX_ICMP_SEQ + 1));
+ }
+ }
+
+ if (current_targetaddr->family == AF_INET6) {
+ if (send_reply) {
+ /* sending back to ourself */
+ to6.sin6_addr = current_targetaddr->src_addr.addr6;
+ } else {
+ to6.sin6_addr = current_targetaddr->dst_addr.addr6;
+ }
+ /*
+ * Setting the ancillary data once is enough, if we are
+ * not using source routing through target (-l/-S). In
+ * case -l/-S used, the middle gateway will be the
+ * IP address of the source, which can be different
+ * for each target IP.
+ */
+ if (first_probe ||
+ (send_reply && current_targetaddr->num_sent == 0)) {
+ if (send_reply) {
+ /* target is the middle gateway now */
+ gw_IP_list6[num_gw].addr6 =
+ current_targetaddr->dst_addr.addr6;
+ }
+ set_ancillary_data(&msg6, hoplimit, gw_IP_list6,
+ eff_num_gw, if_index);
+ first_probe = _B_FALSE;
+ }
+ pinger(send_sock6, (struct sockaddr *)&to6, &msg6, AF_INET6);
+ } else {
+ to.sin_addr = current_targetaddr->dst_addr.addr;
+ /*
+ * Set IPv4 options when sending the first probe to a target
+ * IP address. Some options change when the target address
+ * changes.
+ */
+ if (current_targetaddr->num_sent == 0) {
+ if (eff_num_gw > 0) {
+ gw_IP_list[num_gw].addr =
+ current_targetaddr->dst_addr.addr;
+ /*
+ * If send_reply, the target becomes the
+ * middle gateway, sender becomes the last
+ * gateway.
+ */
+ if (send_reply) {
+ gw_IP_list[eff_num_gw].addr =
+ current_targetaddr->src_addr.addr;
+ }
+ }
+ /*
+ * In IPv4, if source routing is used, the target
+ * address shows up as the last gateway, hence +1.
+ */
+ set_IPv4_options(send_sock, gw_IP_list,
+ (eff_num_gw > 0) ? eff_num_gw + 1 : 0,
+ &current_targetaddr->src_addr.addr, &to.sin_addr);
+ }
+ pinger(send_sock, (struct sockaddr *)&to, NULL, AF_INET);
+ }
+
+ current_targetaddr->num_sent++;
+}
+
+/*
+ * recv_icmp_packet()'s job is to listen to icmp packets and filter out
+ * those ping is interested in.
+ */
+static void
+recv_icmp_packet(struct addrinfo *ai_dst, int recv_sock6, int recv_sock,
+ushort_t udp_src_port6, ushort_t udp_src_port)
+{
+ struct msghdr in_msg;
+ struct iovec iov;
+ struct sockaddr_in6 from6;
+ fd_set fds;
+ int result;
+ int cc;
+ boolean_t always_true = _B_TRUE; /* lint doesn't like while(_B_TRUE) */
+
+ while (always_true) {
+ (void) FD_ZERO(&fds);
+ if (recv_sock6 != -1)
+ FD_SET(recv_sock6, &fds);
+ if (recv_sock != -1)
+ FD_SET(recv_sock, &fds);
+
+ result = select(MAX(recv_sock6, recv_sock) + 1, &fds,
+ (fd_set *)NULL, (fd_set *)NULL, (struct timeval *)NULL);
+ if (result == -1) {
+ if (errno == EINTR) {
+ continue;
+ } else {
+ Fprintf(stderr, "%s: select %s\n", progname,
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ } else if (result > 0) {
+ in_msg.msg_name = &from6;
+ in_msg.msg_namelen = sizeof (from6);
+ iov.iov_base = in_pkt;
+ iov.iov_len = sizeof (in_pkt);
+ in_msg.msg_iov = &iov;
+ in_msg.msg_iovlen = 1;
+ in_msg.msg_control = ancillary_data;
+ in_msg.msg_controllen = sizeof (ancillary_data);
+
+ /* Do we have an ICMP6 packet waiting? */
+ if ((recv_sock6 != -1) &&
+ (FD_ISSET(recv_sock6, &fds))) {
+ cc = recvmsg(recv_sock6, &in_msg, 0);
+ if (cc < 0) {
+ if (errno != EINTR) {
+ Fprintf(stderr,
+ "%s: recvmsg %s\n",
+ progname, strerror(errno));
+ }
+ continue;
+ } else if (cc > 0) {
+ check_reply6(ai_dst, &in_msg, cc,
+ udp_src_port6);
+ }
+ }
+ /* Do we have an ICMP packet waiting? */
+ if ((recv_sock != -1) && (FD_ISSET(recv_sock, &fds))) {
+ cc = recvmsg(recv_sock, &in_msg, 0);
+ if (cc < 0) {
+ if (errno != EINTR) {
+ Fprintf(stderr,
+ "%s: recvmsg %s\n",
+ progname, strerror(errno));
+ }
+ continue;
+ } if (cc > 0) {
+ check_reply(ai_dst, &in_msg, cc,
+ udp_src_port);
+ }
+ }
+ }
+ /*
+ * If we were probing last IP address of the target host and
+ * received a reply for each probe sent to this address,
+ * then we are done!
+ */
+ if ((npackets > 0) && (current_targetaddr->next == NULL) &&
+ (nreceived_last_target == npackets)) {
+ (void) alarm(0); /* cancel alarm */
+ finish();
+ }
+ } /* infinite loop */
+}
+
+/*
+ * Given a host (with possibly multiple IP addresses) and an IP address, this
+ * function determines if this IP address is one of the host's addresses to
+ * which we're sending probes. Used to determine if we are interested in a
+ * packet.
+ */
+boolean_t
+is_a_target(struct addrinfo *ai, union any_in_addr *addr)
+{
+ int num_addrs;
+ int i;
+ struct addrinfo *aip;
+
+ aip = ai;
+ if (probe_all)
+ num_addrs = num_v4 + num_v6;
+ else
+ num_addrs = 1;
+ for (i = 0; i < num_addrs && aip != NULL; i++) {
+ if (aip->ai_family == AF_INET6) {
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ if (IN6_ARE_ADDR_EQUAL(&((struct sockaddr_in6 *)
+ aip->ai_addr)->sin6_addr, &addr->addr6))
+ return (_B_TRUE);
+ } else {
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ if (((struct sockaddr_in *)
+ aip->ai_addr)->sin_addr.s_addr == addr->addr.s_addr)
+ return (_B_TRUE);
+ }
+ }
+
+ return (_B_FALSE);
+}
+
+/*
+ * Compose and transmit an ICMP ECHO REQUEST packet. The IP packet
+ * will be added on by the kernel. The ID field is our UNIX process ID,
+ * and the sequence number is an ascending integer. The first 8 bytes
+ * of the data portion are used to hold a UNIX "timeval" struct in network
+ * byte-order, to compute the round-trip time.
+ */
+static void
+pinger(int send_sock, struct sockaddr *whereto, struct msghdr *msg6,
+ int family)
+{
+ static uint64_t out_pkt_buf[(IP_MAXPACKET + 1) / 8];
+ uchar_t *out_pkt = (uchar_t *)&out_pkt_buf;
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ struct icmp *icp = (struct icmp *)out_pkt;
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ struct sockaddr_in6 *to6 = (struct sockaddr_in6 *)whereto;
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ struct sockaddr_in *to = (struct sockaddr_in *)whereto;
+ struct timeval *tp;
+ struct timeval t_snd;
+ uchar_t *datap;
+ struct iovec iov;
+ int start = 0;
+ int cc;
+ int i;
+
+ /* using UDP? */
+ if (use_udp) {
+ cc = datalen;
+
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ tp = (struct timeval *)out_pkt;
+ datap = &out_pkt[sizeof (struct timeval)];
+
+ /*
+ * This sets the port whether we are handling a v4 or v6
+ * sockaddr structure.
+ */
+ to->sin_port = htons(dest_port);
+
+ dest_port = (dest_port + 1) % (MAX_PORT + 1);
+ ntransmitted++;
+ } else { /* using ICMP */
+ cc = datalen + ICMP_MINLEN;
+
+ if (family == AF_INET6) {
+ icp->icmp_type = send_reply ?
+ ICMP6_ECHO_REPLY : ICMP6_ECHO_REQUEST;
+ } else if (use_icmp_ts) { /* family is AF_INET */
+ icp->icmp_type = send_reply ?
+ ICMP_TSTAMPREPLY : ICMP_TSTAMP;
+ } else {
+ icp->icmp_type = send_reply ?
+ ICMP_ECHOREPLY : ICMP_ECHO;
+ }
+
+ icp->icmp_code = 0;
+ icp->icmp_cksum = 0;
+ icp->icmp_seq = htons(ntransmitted++ % (MAX_ICMP_SEQ + 1));
+ if (icp->icmp_seq == 0)
+ num_wraps++;
+ icp->icmp_id = htons(ident); /* ID */
+
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ tp = (struct timeval *)&out_pkt[ICMP_MINLEN];
+ datap = &out_pkt[ICMP_MINLEN + sizeof (struct timeval)];
+ }
+
+ start = sizeof (struct timeval); /* skip for time */
+
+ (void) gettimeofday(&t_snd, (struct timezone *)NULL);
+
+ /* if packet is big enough to store timeval OR ... */
+ if ((datalen >= sizeof (struct timeval)) ||
+ (family == AF_INET && use_icmp_ts))
+ *tp = t_snd;
+
+ if (family == AF_INET && use_icmp_ts) {
+ start = sizeof (struct id_ts); /* skip for ICMP timestamps */
+ /* Number of milliseconds since midnight */
+ icp->icmp_otime = htonl((tp->tv_sec % (24*60*60)) * 1000 +
+ tp->tv_usec / 1000);
+ }
+
+ for (i = start; i < datalen; i++)
+ *datap++ = i;
+
+ if (family == AF_INET) {
+ if (!use_udp)
+ icp->icmp_cksum = in_cksum((ushort_t *)icp, cc);
+
+ i = sendto(send_sock, (char *)out_pkt, cc, 0, whereto,
+ sizeof (struct sockaddr_in));
+ } else {
+ /*
+ * Fill in the rest of the msghdr structure. msg_control is set
+ * in set_ancillary_data().
+ */
+ msg6->msg_name = to6;
+ msg6->msg_namelen = sizeof (struct sockaddr_in6);
+
+ iov.iov_base = out_pkt;
+ iov.iov_len = cc;
+
+ msg6->msg_iov = &iov;
+ msg6->msg_iovlen = 1;
+
+ i = sendmsg(send_sock, msg6, 0);
+ }
+
+ /* This is a more precise time (right after we send the packet) */
+ t_last_probe_sent = gethrtime();
+
+ if (i < 0 || i != cc) {
+ if (i < 0) {
+ Fprintf(stderr, "%s: sendto %s\n", progname,
+ strerror(errno));
+ if (!stats)
+ exit(EXIT_FAILURE);
+ }
+ Printf("ping: wrote %s %d chars, ret=%d\n",
+ targethost, cc, i);
+ (void) fflush(stdout);
+ }
+}
+
+/*
+ * Return a hostname for the given IP address.
+ */
+char *
+pr_name(char *addr, int family)
+{
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+ struct sockaddr *sa;
+ static struct in6_addr prev_addr = IN6ADDR_ANY_INIT;
+ char *cp;
+ char abuf[INET6_ADDRSTRLEN];
+ static char buf[NI_MAXHOST + INET6_ADDRSTRLEN + 3];
+ uint_t slen, alen, hlen;
+
+ switch (family) {
+ case AF_INET:
+ (void) memset(&sin, 0, sizeof (sin));
+ slen = sizeof (struct sockaddr_in);
+ alen = sizeof (struct in_addr);
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ sin.sin_addr = *(struct in_addr *)addr;
+ sin.sin_port = 0;
+ sa = (struct sockaddr *)&sin;
+ break;
+ case AF_INET6:
+ (void) memset(&sin6, 0, sizeof (sin6));
+ slen = sizeof (struct sockaddr_in6);
+ alen = sizeof (struct in6_addr);
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ sin6.sin6_addr = *(struct in6_addr *)addr;
+ sin6.sin6_port = 0;
+ sa = (struct sockaddr *)&sin6;
+ break;
+ default:
+ (void) snprintf(buf, sizeof (buf), "<invalid address family>");
+ return (buf);
+ }
+ sa->sa_family = family;
+
+ /* compare with the buffered (previous) lookup */
+ if (memcmp(addr, &prev_addr, alen) != 0) {
+ int flags = (nflag) ? NI_NUMERICHOST : NI_NAMEREQD;
+ if (getnameinfo(sa, slen, buf, sizeof (buf),
+ NULL, 0, flags) != 0) {
+ /* getnameinfo() failed; return just the address */
+ if (inet_ntop(family, (const void*)addr,
+ buf, sizeof (buf)) == NULL)
+ buf[0] = 0;
+ } else if (!nflag) {
+ /* append numeric address to hostname string */
+ hlen = strlen(buf);
+ cp = (char *)(buf + hlen);
+ (void) snprintf(cp, sizeof (buf) - hlen, " (%s)",
+ inet_ntop(family, (const void *)addr, abuf,
+ sizeof (abuf)));
+ }
+
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ prev_addr = *(struct in6_addr *)addr;
+ }
+ return (buf);
+}
+
+/*
+ * Return the protocol string, given its protocol number.
+ */
+char *
+pr_protocol(int prot)
+{
+ static char buf[20];
+
+ switch (prot) {
+ case IPPROTO_ICMPV6:
+ (void) strlcpy(buf, "icmp6", sizeof (buf));
+ break;
+
+ case IPPROTO_ICMP:
+ (void) strlcpy(buf, "icmp", sizeof (buf));
+ break;
+
+ case IPPROTO_TCP:
+ (void) strlcpy(buf, "tcp", sizeof (buf));
+ break;
+
+ case IPPROTO_UDP:
+ (void) strlcpy(buf, "udp", sizeof (buf));
+ break;
+
+ default:
+ (void) snprintf(buf, sizeof (buf), "prot %d", prot);
+ break;
+ }
+
+ return (buf);
+}
+
+/*
+ * Checks if value is between seq_begin and seq_begin+seq_len. Note that
+ * sequence numbers wrap around after MAX_ICMP_SEQ (== MAX_PORT).
+ */
+boolean_t
+seq_match(ushort_t seq_begin, int seq_len, ushort_t value)
+{
+ /*
+ * If seq_len is too big, like some value greater than MAX_ICMP_SEQ/2,
+ * truncate it down to MAX_ICMP_SEQ/2. We are not going to accept any
+ * reply which come 83hr later!
+ */
+ if (seq_len > MAX_ICMP_SEQ / 2) {
+ seq_begin = (seq_begin + seq_len - MAX_ICMP_SEQ / 2) %
+ (MAX_ICMP_SEQ + 1);
+ seq_len = MAX_ICMP_SEQ / 2;
+ }
+
+ if (PINGSEQ_LEQ(seq_begin, value) &&
+ PINGSEQ_LEQ(value, (seq_begin + seq_len - 1) % (MAX_ICMP_SEQ + 1)))
+ return (_B_TRUE);
+ else
+ return (_B_FALSE);
+}
+
+/*
+ * For a given icmp_seq, find which destination address we must have sent this
+ * to.
+ */
+void
+find_dstaddr(ushort_t icmpseq, union any_in_addr *ipaddr)
+{
+ struct targetaddr *target = targetaddr_list;
+ int real_seq;
+ int targetaddr_index;
+ int real_npackets;
+ int i;
+
+ ipaddr->addr6 = in6addr_any;
+
+ /*
+ * If this is probe_all and not stats, then the number of probes sent to
+ * each IP address may be different (remember, we stop sending to one IP
+ * address as soon as it replies). They are stored in target->num_sent
+ * field. Since we don't wrap around the list (!stats), they are also
+ * preserved.
+ */
+ if (probe_all && !stats) {
+ do {
+ if (seq_match(target->starting_seq_num,
+ target->num_sent, icmpseq)) {
+ ipaddr->addr6 = target->dst_addr.addr6;
+ /*
+ * We are not immediately return()ing here.
+ * Because of wrapping, we might find another
+ * match later, which is more likely to be the
+ * real one.
+ */
+ }
+ target = target->next;
+ } while (target != NULL);
+ } else {
+ /*
+ * Find the absolute (non-wrapped) seq number within the last
+ * 64K
+ */
+ if (icmpseq < (ntransmitted % (MAX_ICMP_SEQ + 1))) {
+ real_seq = num_wraps * (MAX_ICMP_SEQ + 1) + icmpseq;
+ } else {
+ real_seq = (num_wraps - 1) * (MAX_ICMP_SEQ + 1) +
+ icmpseq;
+ }
+
+ /* Make sure it's non-negative */
+ if (real_seq < 0)
+ return;
+ real_npackets = (npackets == 0) ? 1 : npackets;
+
+ /*
+ * We sent npackets many packets to each of those
+ * num_targetaddrs many IP addresses.
+ */
+ targetaddr_index =
+ (real_seq % (num_targetaddrs * real_npackets)) /
+ real_npackets;
+ for (i = 0; i < targetaddr_index; i++)
+ target = target->next;
+ ipaddr->addr6 = target->dst_addr.addr6;
+ }
+}
+
+/*
+ * Checksum routine for Internet Protocol family headers (C Version)
+ */
+static ushort_t
+in_cksum(ushort_t *addr, int len)
+{
+ int nleft = len;
+ ushort_t *w = addr;
+ ushort_t answer;
+ ushort_t odd_byte = 0;
+ int sum = 0;
+
+ /*
+ * Our algorithm is simple, using a 32 bit accumulator (sum),
+ * we add sequential 16 bit words to it, and at the end, fold
+ * back all the carry bits from the top 16 bits into the lower
+ * 16 bits.
+ */
+ while (nleft > 1) {
+ sum += *w++;
+ nleft -= 2;
+ }
+
+ /* mop up an odd byte, if necessary */
+ if (nleft == 1) {
+ *(uchar_t *)(&odd_byte) = *(uchar_t *)w;
+ sum += odd_byte;
+ }
+
+ /*
+ * add back carry outs from top 16 bits to low 16 bits
+ */
+ sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
+ sum += (sum >> 16); /* add carry */
+ answer = ~sum; /* truncate to 16 bits */
+ return (answer);
+}
+
+/*
+ * Subtract 2 timeval structs: out = out - in.
+ * Out is assumed to be >= in.
+ */
+void
+tvsub(struct timeval *out, struct timeval *in)
+{
+ if ((out->tv_usec -= in->tv_usec) < 0) {
+ out->tv_sec--;
+ out->tv_usec += 1000000;
+ }
+ out->tv_sec -= in->tv_sec;
+}
+
+/*
+ * Print out statistics, and give up.
+ * Heavily buffered STDIO is used here, so that all the statistics
+ * will be written with 1 sys-write call. This is nice when more
+ * than one copy of the program is running on a terminal; it prevents
+ * the statistics output from becoming intermingled.
+ */
+static void
+finish()
+{
+ Printf("\n----%s PING Statistics----\n", targethost);
+ Printf("%d packets transmitted, ", ntransmitted);
+ Printf("%d packets received, ", nreceived);
+ if (ntransmitted) {
+ if (nreceived <= ntransmitted) {
+ Printf("%d%% packet loss",
+ (int)(((ntransmitted-nreceived)*100) /
+ ntransmitted));
+ } else {
+ Printf("%.2f times amplification",
+ (double)nreceived / (double)ntransmitted);
+ }
+ }
+ (void) putchar('\n');
+
+ /* if packet is big enough to store timeval AND ... */
+ if ((datalen >= sizeof (struct timeval)) && (nreceived > 0)) {
+ int precision = nreceived < 10 ? 3 : nreceived < 100 ? 4 : 5;
+ double mean = (double)tsum / nreceived;
+ double smean = (double)tsum2 / nreceived;
+ double sd =
+ sqrt(((smean - mean*mean) * nreceived) / (nreceived-1));
+
+ Printf("round-trip (ms) min/avg/max/stddev = "
+ TIMEFORMAT "/" TIMEFORMAT_V "/"
+ TIMEFORMAT "/" TIMEFORMAT_V "\n",
+ (double)tmin / 1000, precision, mean / 1000,
+ (double)tmax / 1000, precision-1, sd / 1000);
+ }
+ (void) fflush(stdout);
+
+ exit(is_alive ? EXIT_SUCCESS : EXIT_FAILURE);
+}
+
+/*
+ * print the usage line
+ */
+static void
+usage(char *cmdname)
+{
+ Fprintf(stderr, "usage: %s host [timeout]\n", cmdname);
+ Fprintf(stderr,
+/* CSTYLED */
+"usage: %s -s [-l | U] [adLnRrv] [-A addr_family] [-c traffic_class]\n\t"
+"[-g gateway [-g gateway ...]] [-F flow_label] [-I interval]\n\t"
+"[-i interface] [-P tos] [-p port] [-t ttl] host [data_size] [npackets]\n",
+ cmdname);
+}
+
+/*
+ * Parse integer argument; exit with an error if it's not a number.
+ * Now it also accepts hex. values.
+ */
+static int
+int_arg(char *s, char *what)
+{
+ char *cp;
+ char *ep;
+ int num;
+
+ errno = 0;
+ if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
+ cp = s + 2;
+ num = (int)strtol(cp, &ep, 16);
+ } else {
+ num = (int)strtol(s, &ep, 10);
+ }
+
+ if (errno || *ep != '\0' || num < 0) {
+ (void) Fprintf(stderr, "%s: bad %s: %s\n",
+ progname, what, s);
+ exit(EXIT_FAILURE);
+ }
+
+ return (num);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/ping/ping.h b/usr/src/cmd/cmd-inet/usr.sbin/ping/ping.h
new file mode 100644
index 0000000000..a7a2493549
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/ping/ping.h
@@ -0,0 +1,140 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#ifndef _PING_H
+#define _PING_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MAX_PORT 65535 /* max port number for UDP probes */
+#define MAX_ICMP_SEQ 65535 /* max icmp sequence value */
+
+/*
+ * Maximum number of source route space. Please note that because of the API,
+ * we can only specify 8 gateways, the last address has to be the target
+ * address.
+ */
+#define MAX_GWS 9
+
+/*
+ * This is the max it can be. But another limiting factor is the PMTU,
+ * so in any instance, it can be less than 127.
+ */
+#define MAX_GWS6 127
+
+/* maximum of above two */
+#define MAXMAX_GWS MAX(MAX_GWS, MAX_GWS6)
+
+/* size of buffer to store the IPv4 gateway addresses */
+#define ROUTE_SIZE (IPOPT_OLEN + IPOPT_OFFSET + \
+ MAX_GWS * sizeof (struct in_addr))
+
+#define A_CNT(ARRAY) (sizeof (ARRAY) / sizeof ((ARRAY)[0]))
+
+
+#define Printf (void) printf
+#define Fprintf (void) fprintf
+
+#define TIMEFORMAT "%#.3g"
+#define TIMEFORMAT_V "%#.*g"
+
+
+/*
+ * For each target IP address we are going to probe, we store required info,
+ * such as address family, IP address of target, source IP address to use
+ * for that target address, and number of probes to send in the targetaddr
+ * structure.
+ * All target addresses are also linked to each other and used in
+ * scheduling probes. Each targetaddr structure identifies a batch of probes to
+ * send : where to send, how many to send. We capture state information, such as
+ * number of probes already sent (in this batch only), whether target replied
+ * as we probe it, whether we are done with probing this address (can happen
+ * in regular (!stats) mode when we get a reply for a probe sent in current
+ * batch), and starting sequence number which is used together with number of
+ * probes sent to determine if the incoming reply is for a probe we sent in
+ * current batch.
+ */
+struct targetaddr {
+ int family;
+ union any_in_addr dst_addr; /* dst address for the probe */
+ union any_in_addr src_addr; /* src addr to use for this dst addr */
+ int num_probes; /* num of probes to send to this dst */
+ int num_sent; /* number of probes already sent */
+ boolean_t got_reply; /* received a reply from dst while */
+ /* still probing it */
+ boolean_t probing_done; /* skip without sending all probes */
+ ushort_t starting_seq_num; /* initial icmp_seq/UDP port, used */
+ /* for authenticating replies */
+ struct targetaddr *next; /* next targetaddr item in the list */
+};
+
+struct hostinfo {
+ char *name; /* hostname */
+ int family; /* address family */
+ int num_addr; /* number of addresses */
+ union any_in_addr *addrs; /* address list */
+};
+
+struct icmptype_table {
+ int type; /* ICMP type */
+ char *message; /* corresponding string message */
+};
+
+extern struct targetaddr *current_targetaddr;
+extern int nreceived;
+extern int nreceived_last_target;
+extern int npackets;
+extern boolean_t is_alive;
+extern int datalen;
+extern boolean_t nflag;
+extern int ident;
+extern boolean_t probe_all;
+extern char *progname;
+extern boolean_t rr_option;
+extern boolean_t stats;
+extern boolean_t strict;
+extern char *targethost;
+extern long long tmax;
+extern long long tmin;
+extern int ts_flag;
+extern boolean_t ts_option;
+extern int64_t tsum;
+extern int64_t tsum2;
+extern boolean_t use_icmp_ts;
+extern boolean_t use_udp;
+extern boolean_t verbose;
+extern boolean_t send_reply;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PING_H */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/ping/ping_aux.c b/usr/src/cmd/cmd-inet/usr.sbin/ping/ping_aux.c
new file mode 100644
index 0000000000..dae7a6e1fe
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/ping/ping_aux.c
@@ -0,0 +1,1277 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * Portions of this source code were derived from Berkeley 4.3 BSD
+ * under license from the Regents of the University of California.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <signal.h>
+
+#include <sys/time.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/stropts.h>
+#include <sys/file.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/udp.h>
+#include <netdb.h>
+#include <stdlib.h>
+
+#include <ifaddrlist.h>
+#include "ping.h"
+
+
+/*
+ * IPv4 source routing option.
+ * In order to avoid padding for the alignment of IPv4 addresses, ipsr_addrs
+ * is defined as a 2-D array of uint8_t, instead of 1-D array of struct in_addr.
+ */
+struct ip_sourceroute {
+ uint8_t ipsr_code;
+ uint8_t ipsr_len;
+ uint8_t ipsr_ptr;
+ /* up to 9 IPv4 addresses */
+ uint8_t ipsr_addrs[1][sizeof (struct in_addr)];
+};
+
+void check_reply(struct addrinfo *, struct msghdr *, int, ushort_t);
+extern void find_dstaddr(ushort_t, union any_in_addr *);
+extern boolean_t is_a_target(struct addrinfo *, union any_in_addr *);
+extern char *pr_name(char *, int);
+static void pr_options(uchar_t *, int);
+extern char *pr_protocol(int);
+static void pr_rropt(uchar_t *, int, boolean_t);
+static void pr_tsopt(uchar_t *, int);
+static char *pr_type(int);
+extern void schedule_sigalrm();
+extern void send_scheduled_probe();
+extern boolean_t seq_match(ushort_t, int, ushort_t);
+extern void sigalrm_handler();
+void set_IPv4_options(int, union any_in_addr *, int, struct in_addr *,
+ struct in_addr *);
+extern void tvsub(struct timeval *, struct timeval *);
+
+/*
+ * Set IPv4 options
+ */
+void
+set_IPv4_options(int sock, union any_in_addr *gw_IP_list, int gw_count,
+ struct in_addr *src, struct in_addr *dst)
+{
+ int req_size;
+ char srr[ROUTE_SIZE + 1];
+ char *bufp;
+ int optsize = ROUTE_SIZE;
+ struct ip_sourceroute *srp;
+ struct ip_timestamp *tsp;
+ int i;
+
+ if (rr_option || ts_option || gw_count > 0) {
+ bzero(srr, sizeof (srr));
+ bufp = srr;
+
+ if (gw_count > 0) {
+ /* 3 = 1 (code) + 1 (len) + 1 (ptr) of src route opt. */
+ req_size = 3 + (sizeof (struct in_addr)) * gw_count;
+
+ if (optsize < req_size) {
+ Fprintf(stderr, "%s: too many IPv4 gateways\n",
+ progname);
+ exit(EXIT_FAILURE);
+ }
+
+ srp = (struct ip_sourceroute *)bufp;
+ srp->ipsr_code = strict ? IPOPT_SSRR : IPOPT_LSRR;
+ srp->ipsr_len = req_size;
+ srp->ipsr_ptr = IPOPT_MINOFF;
+
+ for (i = 0; i < gw_count; i++) {
+ bcopy((char *)&gw_IP_list[i].addr,
+ &srp->ipsr_addrs[i],
+ sizeof (struct in_addr));
+ }
+ optsize -= srp->ipsr_len;
+ bufp += srp->ipsr_len;
+ }
+ /* do we send a timestamp option? */
+ if (ts_option) {
+ if (optsize < IPOPT_MINOFF) {
+ Fprintf(stderr,
+ "%s: no room for timestamp option\n",
+ progname);
+ exit(EXIT_FAILURE);
+ }
+ /* LINTED */
+ tsp = (struct ip_timestamp *)bufp;
+ tsp->ipt_code = IPOPT_TS;
+ tsp->ipt_len = optsize;
+ tsp->ipt_ptr = IPOPT_MINOFF + 1;
+ tsp->ipt_flg = ts_flag & 0x0f;
+
+ if (tsp->ipt_flg > IPOPT_TS_TSANDADDR) {
+ req_size = IPOPT_MINOFF +
+ 2 * sizeof (struct ipt_ta);
+ /*
+ * Note: BSD/4.X is broken in their check so we
+ * have to bump up this number by at least one.
+ */
+ req_size++;
+
+ if (optsize < req_size) {
+ Fprintf(stderr, "%s: no room for "
+ "timestamp option\n", progname);
+ exit(EXIT_FAILURE);
+ }
+
+ bcopy((char *)dst,
+ &tsp->ipt_timestamp.ipt_ta[0].ipt_addr,
+ sizeof (struct in_addr));
+
+ bcopy((char *)src,
+ &tsp->ipt_timestamp.ipt_ta[1].ipt_addr,
+ sizeof (struct in_addr));
+ tsp->ipt_len = req_size;
+
+ }
+ optsize -= tsp->ipt_len;
+ bufp += tsp->ipt_len;
+ }
+ /* do we send a record route option? */
+ if (rr_option) {
+ if (optsize < IPOPT_MINOFF) {
+ Fprintf(stderr,
+ "%s: no room for record route option\n",
+ progname);
+ exit(EXIT_FAILURE);
+
+ }
+ /*
+ * Format of record route option is same as source
+ * route option.
+ */
+ srp = (struct ip_sourceroute *)bufp;
+ srp->ipsr_code = IPOPT_RR;
+ srp->ipsr_len = optsize;
+ srp->ipsr_ptr = IPOPT_MINOFF;
+
+ optsize -= srp->ipsr_len;
+ bufp += srp->ipsr_len;
+ }
+ optsize = bufp - srr;
+ /* Round up to 4 byte boundary */
+ if (optsize & 0x3)
+ optsize = (optsize & ~0x3) + 4;
+ if (setsockopt(sock, IPPROTO_IP, IP_OPTIONS, srr, optsize) <
+ 0) {
+ Fprintf(stderr, "%s: setsockopt IP_OPTIONS %s\n",
+ progname, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ }
+}
+
+/*
+ * Check out the packet to see if it came from us. This logic is necessary
+ * because ALL readers of the ICMP socket get a copy of ALL ICMP packets
+ * which arrive ('tis only fair). This permits multiple copies of this
+ * program to be run without having intermingled output (or statistics!).
+ */
+void
+check_reply(struct addrinfo *ai_dst, struct msghdr *msg, int cc,
+ ushort_t udp_src_port)
+{
+ struct ip *ip;
+ struct icmp *icp;
+ struct udphdr *up;
+ union any_in_addr dst_addr;
+ uchar_t *buf;
+ int32_t *intp;
+ struct sockaddr_in *from;
+ struct timeval *tp;
+ struct timeval tv;
+ int hlen, hlen1;
+ int64_t triptime;
+ boolean_t valid_reply = _B_FALSE;
+ boolean_t reply_matched_current_target; /* Is the source address of */
+ /* this reply same as where */
+ /* we're sending currently? */
+ boolean_t last_reply_from_targetaddr = _B_FALSE; /* Is this stats, */
+ /* probe all with npackets>0 */
+ /* and we received reply for */
+ /* the last probe sent to */
+ /* targetaddr */
+ int cc_left;
+ char tmp_buf[INET6_ADDRSTRLEN];
+ static char *unreach[] = {
+ "Net Unreachable",
+ "Host Unreachable",
+ "Protocol Unreachable",
+ "Port Unreachable",
+ "Fragmentation needed and DF set",
+ "Source Route Failed",
+ /* The following are from RFC1700 */
+ "Net Unknown",
+ "Host Unknown",
+ "Source Host Isolated",
+ "Dest Net Prohibited",
+ "Dest Host Prohibited",
+ "Net Unreachable for TOS",
+ "Host Unreachable for TOS",
+ "Communication Administratively Prohibited",
+ "Host Precedence Violation",
+ "Precedence Cutoff in Effect"
+ };
+ static char *redirect[] = {
+ "Net",
+ "Host",
+ "TOS Net",
+ "TOS Host"
+ };
+ static char *timexceed[] = {
+ "Time exceeded in transit",
+ "Time exceeded during reassembly"
+ };
+ boolean_t print_newline = _B_FALSE;
+ int i;
+
+ /* decompose msghdr into useful pieces */
+ buf = (uchar_t *)msg->msg_iov->iov_base;
+ from = (struct sockaddr_in *)msg->msg_name;
+
+ /* LINTED */
+ intp = (int32_t *)buf;
+
+ (void) gettimeofday(&tv, (struct timezone *)NULL);
+
+ /* LINTED */
+ ip = (struct ip *)buf;
+ hlen = ip->ip_hl << 2;
+
+ if ((cc < sizeof (struct ip)) || (cc < hlen + ICMP_MINLEN)) {
+ if (verbose) {
+ Printf("packet too short (%d bytes) from %s\n", cc,
+ pr_name((char *)&from->sin_addr, AF_INET));
+ }
+ return;
+ }
+
+ cc -= hlen;
+ /* LINTED */
+ icp = (struct icmp *)(buf + hlen);
+
+ if (ip->ip_p == 0) {
+ /*
+ * Assume that we are running on a pre-4.3BSD system
+ * such as SunOS before 4.0
+ */
+ /* LINTED */
+ icp = (struct icmp *)buf;
+ }
+ cc_left = cc - ICMP_MINLEN;
+
+ switch (icp->icmp_type) {
+ case ICMP_UNREACH:
+ ip = &icp->icmp_ip;
+ hlen1 = ip->ip_hl << 2;
+
+ /* check if we have enough of the packet to work on */
+ if ((cc_left < sizeof (struct ip)) ||
+ (cc_left < hlen1 + sizeof (struct udphdr))) {
+ if (verbose) {
+ Printf("packet too short (%d bytes) from %s\n",
+ cc, pr_name((char *)&from->sin_addr,
+ AF_INET));
+ }
+ return;
+ }
+
+ /* get the UDP packet */
+ cc_left -= hlen1 + sizeof (struct udphdr);
+ /* LINTED */
+ up = (struct udphdr *)((uchar_t *)ip + hlen1);
+
+ /* check to see if this is what we sent */
+ if (icp->icmp_code == ICMP_UNREACH_PORT &&
+ ip->ip_p == IPPROTO_UDP &&
+ udp_src_port == up->uh_sport &&
+ use_udp) {
+ valid_reply = _B_TRUE;
+ } else {
+ valid_reply = _B_FALSE;
+ }
+
+ if (valid_reply) {
+ /*
+ * For this valid reply, if we are still sending to
+ * this target IP address, we'd like to do some
+ * updates to targetaddr, so hold SIGALRMs.
+ */
+ (void) sighold(SIGALRM);
+ is_alive = _B_TRUE;
+ nreceived++;
+ reply_matched_current_target =
+ seq_match(current_targetaddr->starting_seq_num,
+ current_targetaddr->num_sent,
+ ntohs(up->uh_dport));
+ if (reply_matched_current_target) {
+ current_targetaddr->got_reply = _B_TRUE;
+ nreceived_last_target++;
+ /*
+ * Determine if stats, probe-all, and
+ * npackets != 0, and this is the reply for
+ * the last probe we sent to current target
+ * address.
+ */
+ if (stats && probe_all && npackets > 0 &&
+ ((current_targetaddr->starting_seq_num +
+ current_targetaddr->num_probes - 1) %
+ (MAX_PORT + 1) == ntohs(up->uh_dport)) &&
+ (current_targetaddr->num_probes ==
+ current_targetaddr->num_sent))
+ last_reply_from_targetaddr = _B_TRUE;
+ } else {
+ /*
+ * If it's just probe_all and we just received
+ * a reply from a target address we were
+ * probing and had timed out (now we are probing
+ * some other target address), we ignore
+ * this reply.
+ */
+ if (probe_all && !stats) {
+ valid_reply = _B_FALSE;
+ /*
+ * Only if it's verbose, we get a
+ * message regarding this reply,
+ * otherwise we are done here.
+ */
+ if (!verbose) {
+ (void) sigrelse(SIGALRM);
+ return;
+ }
+ }
+ }
+ }
+
+ /* stats mode doesn't print 'alive' messages */
+ if (valid_reply && !stats) {
+ /*
+ * if we are still sending to the same target address,
+ * then stop it, because we know it's alive.
+ */
+ if (reply_matched_current_target) {
+ (void) alarm(0); /* cancel alarm */
+ (void) sigset(SIGALRM, SIG_IGN);
+ current_targetaddr->probing_done = _B_TRUE;
+ }
+ (void) sigrelse(SIGALRM);
+
+ if (!probe_all) {
+ Printf("%s is alive\n", targethost);
+ } else {
+ (void) inet_ntop(AF_INET, (void *)&ip->ip_dst,
+ tmp_buf, sizeof (tmp_buf));
+
+ if (nflag) {
+ Printf("%s is alive\n", tmp_buf);
+ } else {
+ Printf("%s (%s) is alive\n",
+ targethost, tmp_buf);
+ }
+ }
+ if (reply_matched_current_target) {
+ /*
+ * Let's get things going again, but now
+ * ping will start sending to next target IP
+ * address.
+ */
+ send_scheduled_probe();
+ (void) sigset(SIGALRM, sigalrm_handler);
+ schedule_sigalrm();
+ }
+ return;
+ } else {
+ /*
+ * If we are not moving to next targetaddr, let's
+ * release the SIGALRM now. We don't want to stall in
+ * the middle of probing a targetaddr if the pr_name()
+ * call (see below) takes longer.
+ */
+ if (!last_reply_from_targetaddr)
+ (void) sigrelse(SIGALRM);
+ /* else, we'll release it later */
+ }
+
+ dst_addr.addr = ip->ip_dst;
+ if (valid_reply) {
+ Printf("%d bytes from %s: ", cc,
+ pr_name((char *)&from->sin_addr, AF_INET));
+ Printf("udp_port=%d. ", ntohs(up->uh_dport));
+ print_newline = _B_TRUE;
+ } else if (is_a_target(ai_dst, &dst_addr) || verbose) {
+ if (icp->icmp_code >= A_CNT(unreach)) {
+ Printf("ICMP %d Unreachable from gateway %s\n",
+ icp->icmp_code,
+ pr_name((char *)&from->sin_addr, AF_INET));
+ } else {
+ Printf("ICMP %s from gateway %s\n",
+ unreach[icp->icmp_code],
+ pr_name((char *)&from->sin_addr, AF_INET));
+ }
+ Printf(" for %s from %s", pr_protocol(ip->ip_p),
+ pr_name((char *)&ip->ip_src, AF_INET));
+ Printf(" to %s", pr_name((char *)&ip->ip_dst, AF_INET));
+ if (ip->ip_p == IPPROTO_TCP ||
+ ip->ip_p == IPPROTO_UDP) {
+ Printf(" port %d ", ntohs(up->uh_dport));
+ }
+ print_newline = _B_TRUE;
+ }
+
+ /* if we are timing and the reply has a timeval */
+ if (valid_reply && datalen >= sizeof (struct timeval) &&
+ cc_left >= sizeof (struct timeval)) {
+ /* LINTED */
+ tp = (struct timeval *)((char *)up +
+ sizeof (struct udphdr));
+ (void) tvsub(&tv, tp);
+ triptime = (int64_t)tv.tv_sec * MICROSEC + tv.tv_usec;
+ Printf("time=" TIMEFORMAT " ms", triptime/1000.0);
+ tsum += triptime;
+ tsum2 += triptime*triptime;
+ if (triptime < tmin)
+ tmin = triptime;
+ if (triptime > tmax)
+ tmax = triptime;
+ print_newline = _B_TRUE;
+ }
+ if (print_newline)
+ (void) putchar('\n');
+ /*
+ * If it's stats, probe-all, npackets > 0, and we received reply
+ * for the last probe sent to this target address, then we
+ * don't need to wait anymore, let's move on to next target
+ * address, now!
+ */
+ if (last_reply_from_targetaddr) {
+ (void) alarm(0); /* cancel alarm */
+ current_targetaddr->probing_done = _B_TRUE;
+ (void) sigrelse(SIGALRM);
+ send_scheduled_probe();
+ schedule_sigalrm();
+ }
+ break;
+
+ case ICMP_REDIRECT:
+ if (cc_left < sizeof (struct ip)) {
+ if (verbose) {
+ Printf("packet too short (%d bytes) from %s\n",
+ cc, pr_name((char *)&from->sin_addr,
+ AF_INET));
+ }
+ return;
+ }
+
+ ip = &icp->icmp_ip;
+ dst_addr.addr = ip->ip_dst;
+ if (is_a_target(ai_dst, &dst_addr) || verbose) {
+ if (icp->icmp_code >= A_CNT(redirect)) {
+ Printf("ICMP %d redirect from gateway %s\n",
+ icp->icmp_code,
+ pr_name((char *)&from->sin_addr, AF_INET));
+ } else {
+ Printf("ICMP %s redirect from gateway %s\n",
+ redirect[icp->icmp_code],
+ pr_name((char *)&from->sin_addr, AF_INET));
+ }
+ Printf(" to %s",
+ pr_name((char *)&icp->icmp_gwaddr, AF_INET));
+ Printf(" for %s\n",
+ pr_name((char *)&ip->ip_dst, AF_INET));
+ }
+ break;
+
+ case ICMP_ECHOREPLY:
+ if (ntohs(icp->icmp_id) == ident) {
+ if (!use_udp && !use_icmp_ts)
+ valid_reply = _B_TRUE;
+ else
+ valid_reply = _B_FALSE;
+ } else {
+ return;
+ }
+
+ if (valid_reply) {
+ /*
+ * For this valid reply, if we are still sending to
+ * this target IP address, we'd like to do some
+ * updates to targetaddr, so hold SIGALRMs.
+ */
+ (void) sighold(SIGALRM);
+ is_alive = _B_TRUE;
+ nreceived++;
+ reply_matched_current_target =
+ seq_match(current_targetaddr->starting_seq_num,
+ current_targetaddr->num_sent,
+ ntohs(icp->icmp_seq));
+ if (reply_matched_current_target) {
+ current_targetaddr->got_reply = _B_TRUE;
+ nreceived_last_target++;
+ /*
+ * Determine if stats, probe-all, and
+ * npackets != 0, and this is the reply for
+ * the last probe we sent to current target
+ * address.
+ */
+ if (stats && probe_all && npackets > 0 &&
+ ((current_targetaddr->starting_seq_num +
+ current_targetaddr->num_probes - 1) %
+ (MAX_ICMP_SEQ + 1) ==
+ ntohs(icp->icmp_seq)) &&
+ (current_targetaddr->num_probes ==
+ current_targetaddr->num_sent))
+ last_reply_from_targetaddr = _B_TRUE;
+ } else {
+ /*
+ * If it's just probe_all and we just received
+ * a reply from a target address we were
+ * probing and had timed out (now we are probing
+ * some other target address), we ignore
+ * this reply.
+ */
+ if (probe_all && !stats) {
+ valid_reply = _B_FALSE;
+ /*
+ * Only if it's verbose, we get a
+ * message regarding this reply,
+ * otherwise we are done here.
+ */
+ if (!verbose) {
+ (void) sigrelse(SIGALRM);
+ return;
+ }
+ }
+ }
+ }
+
+ if (!stats && valid_reply) {
+ /*
+ * if we are still sending to the same target address,
+ * then stop it, because we know it's alive.
+ */
+ if (reply_matched_current_target) {
+ (void) alarm(0); /* cancel alarm */
+ (void) sigset(SIGALRM, SIG_IGN);
+ current_targetaddr->probing_done = _B_TRUE;
+ }
+ (void) sigrelse(SIGALRM);
+
+ if (!probe_all) {
+ Printf("%s is alive\n", targethost);
+ } else {
+ /*
+ * If we are using send_reply, the real
+ * target address is not the src address of the
+ * replies. Use icmp_seq to find out where this
+ * probe was sent to.
+ */
+ if (send_reply) {
+ (void) find_dstaddr(
+ ntohs(icp->icmp_seq), &dst_addr);
+ (void) inet_ntop(AF_INET,
+ (void *)&dst_addr.addr,
+ tmp_buf, sizeof (tmp_buf));
+ } else {
+ (void) inet_ntop(AF_INET,
+ (void *)&from->sin_addr,
+ tmp_buf, sizeof (tmp_buf));
+ }
+ if (nflag) {
+ Printf("%s is alive\n", tmp_buf);
+ } else {
+ Printf("%s (%s) is alive\n",
+ targethost, tmp_buf);
+ }
+ }
+ if (reply_matched_current_target) {
+ /*
+ * Let's get things going again, but now
+ * ping will start sending to next target IP
+ * address.
+ */
+ send_scheduled_probe();
+ (void) sigset(SIGALRM, sigalrm_handler);
+ schedule_sigalrm();
+ }
+ return;
+ } else {
+ /*
+ * If we are not moving to next targetaddr, let's
+ * release the SIGALRM now. We don't want to stall in
+ * the middle of probing a targetaddr if the pr_name()
+ * call (see below) takes longer.
+ */
+ if (!last_reply_from_targetaddr)
+ (void) sigrelse(SIGALRM);
+ /* else, we'll release it later */
+ }
+ /*
+ * If we are using send_reply, the real target address is
+ * not the src address of the replies. Use icmp_seq to find out
+ * where this probe was sent to.
+ */
+ if (send_reply) {
+ (void) find_dstaddr(ntohs(icp->icmp_seq), &dst_addr);
+ Printf("%d bytes from %s: ", cc,
+ pr_name((char *)&dst_addr.addr, AF_INET));
+ } else {
+ Printf("%d bytes from %s: ", cc,
+ pr_name((char *)&from->sin_addr, AF_INET));
+ }
+ Printf("icmp_seq=%d. ", ntohs(icp->icmp_seq));
+
+ if (valid_reply && datalen >= sizeof (struct timeval) &&
+ cc_left >= sizeof (struct timeval)) {
+ /* LINTED */
+ tp = (struct timeval *)&icp->icmp_data[0];
+ (void) tvsub(&tv, tp);
+ triptime = (int64_t)tv.tv_sec * MICROSEC + tv.tv_usec;
+ Printf("time=" TIMEFORMAT " ms", triptime/1000.0);
+ tsum += triptime;
+ tsum2 += triptime*triptime;
+ if (triptime < tmin)
+ tmin = triptime;
+ if (triptime > tmax)
+ tmax = triptime;
+ }
+ (void) putchar('\n');
+
+ /*
+ * If it's stats, probe-all, npackets > 0, and we received reply
+ * for the last probe sent to this target address, then we
+ * don't need to wait anymore, let's move on to next target
+ * address, now!
+ */
+ if (last_reply_from_targetaddr) {
+ (void) alarm(0); /* cancel alarm */
+ current_targetaddr->probing_done = _B_TRUE;
+ (void) sigrelse(SIGALRM);
+ send_scheduled_probe();
+ schedule_sigalrm();
+ }
+ break;
+
+ case ICMP_SOURCEQUENCH:
+ if (cc_left < sizeof (struct ip)) {
+ if (verbose) {
+ Printf("packet too short (%d bytes) from %s\n",
+ cc, pr_name((char *)&from->sin_addr,
+ AF_INET));
+ }
+ return;
+ }
+ ip = &icp->icmp_ip;
+ hlen1 = ip->ip_hl << 2;
+ dst_addr.addr = ip->ip_dst;
+ if (is_a_target(ai_dst, &dst_addr) || verbose) {
+ Printf("ICMP Source Quench from %s\n",
+ pr_name((char *)&from->sin_addr, AF_INET));
+ Printf(" for %s from %s", pr_protocol(ip->ip_p),
+ pr_name((char *)&ip->ip_src, AF_INET));
+ Printf(" to %s", pr_name((char *)&ip->ip_dst, AF_INET));
+
+ /*
+ * if it's a UDP or TCP packet, we need at least first
+ * 4 bytes of it to see the src/dst ports
+ */
+ if ((ip->ip_p == IPPROTO_TCP ||
+ ip->ip_p == IPPROTO_UDP) &&
+ (cc_left >= hlen1 + 4)) {
+ /* LINTED */
+ up = (struct udphdr *)((uchar_t *)ip + hlen1);
+ Printf(" port %d", ntohs(up->uh_dport));
+ }
+ (void) putchar('\n');
+ }
+ break;
+
+ case ICMP_PARAMPROB:
+ if (cc_left < sizeof (struct ip)) {
+ if (verbose) {
+ Printf("packet too short (%d bytes) from %s\n",
+ cc, pr_name((char *)&from->sin_addr,
+ AF_INET));
+ }
+ return;
+ }
+ ip = &icp->icmp_ip;
+ hlen1 = ip->ip_hl << 2;
+ dst_addr.addr = ip->ip_dst;
+ if (is_a_target(ai_dst, &dst_addr) || verbose) {
+ switch (icp->icmp_code) {
+ case ICMP_PARAMPROB_OPTABSENT:
+ Printf("ICMP Missing a Required Option "
+ "parameter problem from %s\n",
+ pr_name((char *)&from->sin_addr, AF_INET));
+ Printf(" option type = %d", icp->icmp_pptr);
+ break;
+ case ICMP_PARAMPROB_BADLENGTH:
+ Printf("ICMP Bad Length parameter problem "
+ "from %s\n",
+ pr_name((char *)&from->sin_addr, AF_INET));
+ Printf(" in byte %d", icp->icmp_pptr);
+ if (icp->icmp_pptr <= hlen1) {
+ Printf(" (value 0x%x)",
+ *((char *)ip + icp->icmp_pptr));
+ }
+ break;
+ case 0:
+ default:
+ Printf("ICMP Parameter Problem from %s\n",
+ pr_name((char *)&from->sin_addr, AF_INET));
+ Printf(" in byte %d", icp->icmp_pptr);
+ if (icp->icmp_pptr <= hlen1) {
+ Printf(" (value 0x%x)",
+ *((char *)ip + icp->icmp_pptr));
+ }
+ break;
+ }
+
+ Printf(" for %s from %s", pr_protocol(ip->ip_p),
+ pr_name((char *)&ip->ip_src, AF_INET));
+ Printf(" to %s", pr_name((char *)&ip->ip_dst, AF_INET));
+
+ /*
+ * if it's a UDP or TCP packet, we need at least first
+ * 4 bytes of it to see the src/dst ports
+ */
+ if ((ip->ip_p == IPPROTO_TCP ||
+ ip->ip_p == IPPROTO_UDP) &&
+ (cc_left >= hlen1 + 4)) {
+ /* LINTED */
+ up = (struct udphdr *)((uchar_t *)ip + hlen1);
+ Printf(" port %d", ntohs(up->uh_dport));
+ }
+ (void) putchar('\n');
+ }
+ break;
+
+ case ICMP_TIMXCEED:
+ if (cc_left < sizeof (struct ip)) {
+ if (verbose) {
+ Printf("packet too short (%d bytes) from %s\n",
+ cc, pr_name((char *)&from->sin_addr,
+ AF_INET));
+ }
+ return;
+ }
+ ip = &icp->icmp_ip;
+ hlen1 = ip->ip_hl << 2;
+ dst_addr.addr = ip->ip_dst;
+ if (is_a_target(ai_dst, &dst_addr) || verbose) {
+ if (icp->icmp_code >= A_CNT(timexceed)) {
+ Printf("ICMP %d time exceeded from %s\n",
+ icp->icmp_code,
+ pr_name((char *)&from->sin_addr, AF_INET));
+ } else {
+ Printf("ICMP %s from %s\n",
+ timexceed[icp->icmp_code],
+ pr_name((char *)&from->sin_addr, AF_INET));
+ }
+ Printf(" for %s from %s", pr_protocol(ip->ip_p),
+ pr_name((char *)&ip->ip_src, AF_INET));
+ Printf(" to %s", pr_name((char *)&ip->ip_dst, AF_INET));
+ if ((ip->ip_p == IPPROTO_TCP ||
+ ip->ip_p == IPPROTO_UDP) &&
+ (cc_left >= hlen1 + 4)) {
+ /* LINTED */
+ up = (struct udphdr *)((uchar_t *)ip + hlen1);
+ Printf(" port %d", ntohs(up->uh_dport));
+ }
+ (void) putchar('\n');
+ }
+ break;
+
+ case ICMP_TSTAMPREPLY:
+ /* the packet should have enough space to store timestamps */
+ if (cc_left < sizeof (struct id_ts)) {
+ if (verbose) {
+ Printf("packet too short (%d bytes) from %s\n",
+ cc, pr_name((char *)&from->sin_addr,
+ AF_INET));
+ }
+ return;
+ }
+
+ if (ntohs(icp->icmp_id) == ident) {
+ if (use_icmp_ts)
+ valid_reply = _B_TRUE;
+ else
+ valid_reply = _B_FALSE;
+ } else {
+ return;
+ }
+
+ if (valid_reply) {
+ /*
+ * For this valid reply, if we are still sending to
+ * this target IP address, we'd like to do some
+ * updates to targetaddr, so hold SIGALRMs.
+ */
+ (void) sighold(SIGALRM);
+ is_alive = _B_TRUE;
+ nreceived++;
+ reply_matched_current_target =
+ seq_match(current_targetaddr->starting_seq_num,
+ current_targetaddr->num_sent,
+ ntohs(icp->icmp_seq));
+ if (reply_matched_current_target) {
+ current_targetaddr->got_reply = _B_TRUE;
+ nreceived_last_target++;
+ /*
+ * Determine if stats, probe-all, and
+ * npackets != 0, and this is the reply for
+ * the last probe we sent to current target
+ * address.
+ */
+ if (stats && probe_all && npackets > 0 &&
+ ((current_targetaddr->starting_seq_num +
+ current_targetaddr->num_probes - 1) %
+ (MAX_ICMP_SEQ + 1) ==
+ ntohs(icp->icmp_seq)) &&
+ (current_targetaddr->num_probes ==
+ current_targetaddr->num_sent))
+ last_reply_from_targetaddr = _B_TRUE;
+ } else {
+ /*
+ * If it's just probe_all and we just received
+ * a reply from a target address we were
+ * probing and had timed out (now we are probing
+ * some other target address), we ignore
+ * this reply.
+ */
+ if (probe_all && !stats) {
+ valid_reply = _B_FALSE;
+ /*
+ * Only if it's verbose, we get a
+ * message regarding this reply,
+ * otherwise we are done here.
+ */
+ if (!verbose) {
+ (void) sigrelse(SIGALRM);
+ return;
+ }
+ }
+ }
+ }
+
+ if (!stats && valid_reply) {
+ /*
+ * if we are still sending to the same target address,
+ * then stop it, because we know it's alive.
+ */
+ if (reply_matched_current_target) {
+ (void) alarm(0); /* cancel alarm */
+ (void) sigset(SIGALRM, SIG_IGN);
+ current_targetaddr->probing_done = _B_TRUE;
+ }
+ (void) sigrelse(SIGALRM);
+
+ if (!probe_all) {
+ Printf("%s is alive\n", targethost);
+ } else {
+ /*
+ * If we are using send_reply, the real
+ * target address is not the src address of the
+ * replies. Use icmp_seq to find out where this
+ * probe was sent to.
+ */
+ if (send_reply) {
+ (void) find_dstaddr(
+ ntohs(icp->icmp_seq), &dst_addr);
+ (void) inet_ntop(AF_INET,
+ (void *)&dst_addr.addr,
+ tmp_buf, sizeof (tmp_buf));
+ } else {
+ (void) inet_ntop(AF_INET,
+ (void *)&from->sin_addr,
+ tmp_buf, sizeof (tmp_buf));
+ }
+ if (nflag) {
+ Printf("%s is alive\n", tmp_buf);
+ } else {
+ Printf("%s (%s) is alive\n",
+ targethost, tmp_buf);
+ }
+ }
+ if (reply_matched_current_target) {
+ /*
+ * Let's get things going again, but now
+ * ping will start sending to next target IP
+ * address.
+ */
+ send_scheduled_probe();
+ (void) sigset(SIGALRM, sigalrm_handler);
+ schedule_sigalrm();
+ }
+ return;
+ } else {
+ /*
+ * If we are not moving to next targetaddr, let's
+ * release the SIGALRM now. We don't want to stall in
+ * the middle of probing a targetaddr if the pr_name()
+ * call (see below) takes longer.
+ */
+ if (!last_reply_from_targetaddr)
+ (void) sigrelse(SIGALRM);
+ /* else, we'll release it later */
+ }
+
+ /*
+ * If we are using send_reply, the real target address is
+ * not the src address of the replies. Use icmp_seq to find out
+ * where this probe was sent to.
+ */
+ if (send_reply) {
+ (void) find_dstaddr(ntohs(icp->icmp_seq), &dst_addr);
+ Printf("%d bytes from %s: ", cc,
+ pr_name((char *)&dst_addr.addr, AF_INET));
+ } else {
+ Printf("%d bytes from %s: ", cc,
+ pr_name((char *)&from->sin_addr, AF_INET));
+ }
+ Printf("icmp_seq=%d. ", ntohs(icp->icmp_seq));
+ Printf("orig = %lu, recv = %lu, xmit = %lu ",
+ (ulong_t)ntohl(icp->icmp_otime),
+ (ulong_t)ntohl(icp->icmp_rtime),
+ (ulong_t)ntohl(icp->icmp_ttime));
+
+ if (valid_reply) {
+ /*
+ * icp->icmp_otime is the time passed since midnight.
+ * Therefore we need to adjust tv value, which is
+ * the time passed since Jan 1, 1970.
+ */
+ triptime = (tv.tv_sec % (24LL * 60 * 60)) * MILLISEC +
+ (tv.tv_usec / (MICROSEC/MILLISEC));
+ triptime -= ntohl(icp->icmp_otime);
+ if (triptime < 0)
+ triptime += 24LL * 60 * 60 * MILLISEC;
+
+ Printf("time=%d. ms", (int)triptime);
+ triptime *= (MICROSEC/MILLISEC);
+ tsum += triptime;
+ tsum2 += triptime*triptime;
+ if (triptime < tmin)
+ tmin = triptime;
+ if (triptime > tmax)
+ tmax = triptime;
+ }
+ (void) putchar('\n');
+ /*
+ * If it's stats, probe-all, npackets > 0, and we received reply
+ * for the last probe sent to this target address, then we
+ * don't need to wait anymore, let's move on to next target
+ * address, now!
+ */
+ if (last_reply_from_targetaddr) {
+ (void) alarm(0); /* cancel alarm */
+ current_targetaddr->probing_done = _B_TRUE;
+ (void) sigrelse(SIGALRM);
+ send_scheduled_probe();
+ schedule_sigalrm();
+ }
+ break;
+ case ICMP_ROUTERADVERT:
+ case ICMP_ROUTERSOLICIT:
+ /* Router discovery messages */
+ return;
+
+ case ICMP_ECHO:
+ case ICMP_TSTAMP:
+ case ICMP_IREQ:
+ case ICMP_MASKREQ:
+ /* These were never passed out from the SunOS 4.X kernel. */
+ return;
+
+ case ICMP_IREQREPLY:
+ case ICMP_MASKREPLY:
+ /* Replies for information and address mask requests */
+ return;
+
+ default:
+ if (verbose) {
+ Printf("%d bytes from %s:\n", cc,
+ pr_name((char *)&from->sin_addr, AF_INET));
+ Printf("icmp_type=%d (%s) ",
+ icp->icmp_type, pr_type(icp->icmp_type));
+ Printf("icmp_code=%d\n", icp->icmp_code);
+ for (i = 0; i < 12; i++) {
+ Printf("x%2.2x: x%8.8x\n",
+ i * sizeof (int32_t), *intp++);
+ }
+ }
+ break;
+ }
+
+ buf += sizeof (struct ip);
+ hlen -= sizeof (struct ip);
+
+ /* if verbose and there exists IP options */
+ if (verbose && hlen > 0)
+ pr_options((uchar_t *)buf, hlen);
+}
+
+/*
+ * Print out the ip options.
+ */
+static void
+pr_options(uchar_t *opt, int optlength)
+{
+ int curlength;
+
+ Printf(" IP options: ");
+ while (optlength > 0) {
+ curlength = opt[1];
+ switch (*opt) {
+ case IPOPT_EOL:
+ optlength = 0;
+ break;
+
+ case IPOPT_NOP:
+ opt++;
+ optlength--;
+ continue;
+
+ case IPOPT_RR:
+ Printf(" <record route> ");
+ pr_rropt(opt, curlength, _B_TRUE);
+ break;
+
+ case IPOPT_TS:
+ Printf(" <time stamp> ");
+ pr_tsopt(opt, curlength);
+ break;
+
+ case IPOPT_SECURITY:
+ Printf(" <security>");
+ break;
+
+ case IPOPT_LSRR:
+ Printf(" <loose source route> ");
+ pr_rropt(opt, curlength, _B_FALSE);
+ break;
+
+ case IPOPT_SATID:
+ Printf(" <stream id>");
+ break;
+
+ case IPOPT_SSRR:
+ Printf(" <strict source route> ");
+ pr_rropt(opt, curlength, _B_FALSE);
+ break;
+
+ default:
+ Printf(" <option %d, len %d>", *opt, curlength);
+ break;
+ }
+ /*
+ * Following most options comes a length field
+ */
+ opt += curlength;
+ optlength -= curlength;
+ }
+ (void) putchar('\n');
+}
+
+/*
+ * Print out a recorded route option. If rrflag is _B_TRUE, it prints record
+ * route option, otherwise LSRR/SSRR.
+ */
+static void
+pr_rropt(uchar_t *opt, int length, boolean_t rrflag)
+{
+ struct ip_sourceroute *rrp;
+ int sr_index = 0;
+ struct in_addr addr;
+
+ rrp = (struct ip_sourceroute *)opt;
+
+ /* data starts at offset 3 */
+ length -= 3;
+ while (length > 0) {
+ /*
+ * Let's see if we are examining the addr pointed by ipsr_ptr
+ */
+ if ((rrp->ipsr_ptr == (sr_index + 1) * sizeof (addr)) &&
+ rrflag) {
+ Printf(" (End of record)");
+ break;
+ }
+
+ bcopy(&rrp->ipsr_addrs[sr_index], &addr, sizeof (addr));
+ Printf("%s", pr_name((char *)&addr, AF_INET));
+
+ if (rrp->ipsr_ptr == (sr_index + 1) * sizeof (addr)) {
+ Printf("(Current)");
+ }
+
+ sr_index++;
+
+ length -= sizeof (addr);
+ if (length > 0)
+ Printf(", ");
+ }
+}
+
+/*
+ * Print out a timestamp option.
+ */
+static void
+pr_tsopt(uchar_t *opt, int length)
+{
+ boolean_t address_present;
+ boolean_t rrflag; /* End at current entry? */
+ struct ip_timestamp *tsp;
+ int ts_index = 0;
+ struct in_addr addr;
+ size_t data_len;
+ int32_t time;
+
+ /* LINTED */
+ tsp = (struct ip_timestamp *)opt;
+
+ switch (tsp->ipt_flg) {
+ case IPOPT_TS_TSONLY:
+ address_present = _B_FALSE;
+ data_len = sizeof (tsp->ipt_timestamp.ipt_time[0]);
+ rrflag = _B_TRUE;
+ break;
+ case IPOPT_TS_TSANDADDR:
+ address_present = _B_TRUE;
+ data_len = sizeof (tsp->ipt_timestamp.ipt_ta[0]);
+ rrflag = _B_TRUE;
+ break;
+ case IPOPT_TS_PRESPEC:
+ case 3:
+ address_present = _B_TRUE;
+ data_len = sizeof (tsp->ipt_timestamp.ipt_ta[0]);
+ rrflag = _B_FALSE;
+ break;
+ default:
+ Printf("(Bad flag value: 0x%x)", tsp->ipt_flg);
+ return;
+ }
+ if (tsp->ipt_oflw > 0)
+ Printf("(Overflow: %d) ", tsp->ipt_oflw);
+
+ /* data starts at offset 4 */
+ length -= 4;
+
+ while (length > 0) {
+ if (length < data_len)
+ break;
+
+ /* the minimum value of ipt_ptr is 5 */
+ if ((tsp->ipt_ptr == ts_index * data_len + 5) && rrflag) {
+ Printf(" (End of record)");
+ break;
+ }
+ if (address_present) {
+ bcopy(&tsp->ipt_timestamp.ipt_ta[ts_index].ipt_addr,
+ &addr, sizeof (addr));
+ Printf("%s: ", pr_name((char *)&addr, AF_INET));
+ bcopy(&tsp->ipt_timestamp.ipt_ta[ts_index].ipt_time,
+ &time, sizeof (time));
+ } else {
+ bcopy(&tsp->ipt_timestamp.ipt_time[ts_index],
+ &time, sizeof (time));
+ }
+ Printf("%d", ntohl(time));
+
+ if (tsp->ipt_ptr == ts_index * data_len + 5)
+ Printf("(Current)");
+
+ ts_index++;
+ length -= data_len;
+ if (length > 0)
+ Printf(", ");
+ }
+}
+
+/*
+ * Convert an ICMP "type" field to a printable string.
+ */
+static char *
+pr_type(int icmp_type)
+{
+ static struct icmptype_table ttab[] = {
+ {ICMP_ECHOREPLY, "Echo Reply"},
+ {1, "ICMP 1"},
+ {2, "ICMP 2"},
+ {ICMP_UNREACH, "Dest Unreachable"},
+ {ICMP_SOURCEQUENCH, "Source Quench"},
+ {ICMP_REDIRECT, "Redirect"},
+ {6, "ICMP 6"},
+ {7, "ICMP 7"},
+ {ICMP_ECHO, "Echo"},
+ {ICMP_ROUTERADVERT, "Router Advertisement"},
+ {ICMP_ROUTERSOLICIT, "Router Solicitation"},
+ {ICMP_TIMXCEED, "Time Exceeded"},
+ {ICMP_PARAMPROB, "Parameter Problem"},
+ {ICMP_TSTAMP, "Timestamp"},
+ {ICMP_TSTAMPREPLY, "Timestamp Reply"},
+ {ICMP_IREQ, "Info Request"},
+ {ICMP_IREQREPLY, "Info Reply"},
+ {ICMP_MASKREQ, "Netmask Request"},
+ {ICMP_MASKREPLY, "Netmask Reply"}
+ };
+ int i;
+
+ for (i = 0; i < A_CNT(ttab); i++) {
+ if (ttab[i].type == icmp_type)
+ return (ttab[i].message);
+ }
+
+ return ("OUT-OF-RANGE");
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/ping/ping_aux6.c b/usr/src/cmd/cmd-inet/usr.sbin/ping/ping_aux6.c
new file mode 100644
index 0000000000..29353b4af8
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/ping/ping_aux6.c
@@ -0,0 +1,1085 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * Portions of this source code were derived from Berkeley 4.3 BSD
+ * under license from the Regents of the University of California.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <signal.h>
+
+#include <sys/time.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/stropts.h>
+#include <sys/file.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/ip6.h>
+#include <netinet/icmp6.h>
+#include <netinet/udp.h>
+#include <netdb.h>
+#include <stdlib.h>
+
+#include <ifaddrlist.h>
+#include "ping.h"
+
+void check_reply6(struct addrinfo *, struct msghdr *, int, ushort_t);
+extern void find_dstaddr(ushort_t, union any_in_addr *);
+static int IPv6_hdrlen(ip6_t *, int, uint8_t *);
+extern boolean_t is_a_target(struct addrinfo *, union any_in_addr *);
+static void pr_ext_headers(struct msghdr *);
+extern char *pr_name(char *, int);
+extern char *pr_protocol(int);
+static void pr_rthdr(unsigned char *);
+static char *pr_type6(uchar_t);
+extern void schedule_sigalrm();
+extern void send_scheduled_probe();
+extern boolean_t seq_match(ushort_t, int, ushort_t);
+void set_ancillary_data(struct msghdr *, int, union any_in_addr *, int, uint_t);
+extern void sigalrm_handler();
+extern void tvsub(struct timeval *, struct timeval *);
+
+
+/*
+ * Initialize the msghdr for specifying the hoplimit, outgoing interface and
+ * routing header.
+ */
+void
+set_ancillary_data(struct msghdr *msgp, int hoplimit,
+ union any_in_addr *gwIPlist, int gw_cnt, uint_t if_index)
+{
+ size_t hoplimit_space;
+ size_t rthdr_space;
+ size_t pktinfo_space;
+ size_t bufspace;
+ struct cmsghdr *cmsgp;
+ uchar_t *cmsg_datap;
+ static boolean_t first = _B_TRUE;
+ int i;
+
+ if (hoplimit == -1 && gw_cnt == 0 && if_index == 0)
+ return;
+
+ /*
+ * Need to figure out size of buffer needed for ancillary data
+ * containing routing header and packet info options.
+ *
+ * Portable heuristic to compute upper bound on space needed for
+ * N ancillary data options. It assumes up to _MAX_ALIGNMENT padding
+ * after both header and data as the worst possible upper bound on space
+ * consumed by padding.
+ * It also adds one extra "sizeof (struct cmsghdr)" for the last option.
+ * This is needed because we would like to use CMSG_NXTHDR() while
+ * composing the buffer. The CMSG_NXTHDR() macro is designed better for
+ * parsing than composing the buffer. It requires the pointer it returns
+ * to leave space in buffer for addressing a cmsghdr and we want to make
+ * sure it works for us while we skip beyond the last ancillary data
+ * option.
+ *
+ * bufspace[i] = sizeof(struct cmsghdr) + <pad after header> +
+ * <option[i] content length> + <pad after data>;
+ *
+ * total_bufspace = bufspace[0] + bufspace[1] + ...
+ * ... + bufspace[N-1] + sizeof (struct cmsghdr);
+ */
+
+ rthdr_space = 0;
+ pktinfo_space = 0;
+ hoplimit_space = 0;
+ bufspace = 0;
+
+ if (hoplimit != -1) {
+ hoplimit_space = sizeof (int);
+ bufspace += sizeof (struct cmsghdr) + _MAX_ALIGNMENT +
+ hoplimit_space + _MAX_ALIGNMENT;
+ }
+
+ if (gw_cnt > 0) {
+ rthdr_space = inet6_rth_space(IPV6_RTHDR_TYPE_0, gw_cnt);
+ bufspace += sizeof (struct cmsghdr) + _MAX_ALIGNMENT +
+ rthdr_space + _MAX_ALIGNMENT;
+ }
+
+ if (if_index != 0) {
+ pktinfo_space = sizeof (struct in6_pktinfo);
+ bufspace += sizeof (struct cmsghdr) + _MAX_ALIGNMENT +
+ pktinfo_space + _MAX_ALIGNMENT;
+ }
+
+ /*
+ * We need to temporarily set the msgp->msg_controllen to bufspace
+ * (we will later trim it to actual length used). This is needed because
+ * CMSG_NXTHDR() uses it to check we have not exceeded the bounds.
+ */
+ bufspace += sizeof (struct cmsghdr);
+ msgp->msg_controllen = bufspace;
+
+ /*
+ * This function is called more than once only if -l/-S used,
+ * since we need to modify the middle gateway. So, don't alloc
+ * new memory, just reuse what msg6 points to.
+ */
+ if (first) {
+ first = _B_FALSE;
+ msgp->msg_control = (struct cmsghdr *)malloc(bufspace);
+ if (msgp->msg_control == NULL) {
+ Fprintf(stderr, "%s: malloc %s\n",
+ progname, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ };
+ cmsgp = CMSG_FIRSTHDR(msgp);
+
+ /*
+ * Fill ancillary data. First hoplimit, then rthdr and pktinfo.
+ */
+
+ /* set hoplimit ancillary data if needed */
+ if (hoplimit != -1) {
+ cmsgp->cmsg_level = IPPROTO_IPV6;
+ cmsgp->cmsg_type = IPV6_HOPLIMIT;
+ cmsg_datap = CMSG_DATA(cmsgp);
+ /* LINTED */
+ *(int *)cmsg_datap = hoplimit;
+ cmsgp->cmsg_len = cmsg_datap + hoplimit_space -
+ (uchar_t *)cmsgp;
+ cmsgp = CMSG_NXTHDR(msgp, cmsgp);
+ }
+
+ /* set rthdr ancillary data if needed */
+ if (gw_cnt > 0) {
+ struct ip6_rthdr0 *rthdr0p;
+
+ cmsgp->cmsg_level = IPPROTO_IPV6;
+ cmsgp->cmsg_type = IPV6_RTHDR;
+ cmsg_datap = CMSG_DATA(cmsgp);
+
+ /*
+ * Initialize rthdr structure
+ */
+ /* LINTED */
+ rthdr0p = (struct ip6_rthdr0 *)cmsg_datap;
+ if (inet6_rth_init(rthdr0p, rthdr_space,
+ IPV6_RTHDR_TYPE_0, gw_cnt) == NULL) {
+ Fprintf(stderr, "%s: inet6_rth_init failed\n",
+ progname);
+ exit(EXIT_FAILURE);
+ }
+
+ /*
+ * Stuff in gateway addresses
+ */
+ for (i = 0; i < gw_cnt; i++) {
+ if (inet6_rth_add(rthdr0p, &gwIPlist[i].addr6) == -1) {
+ Fprintf(stderr,
+ "%s: inet6_rth_add\n", progname);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ cmsgp->cmsg_len = cmsg_datap + rthdr_space - (uchar_t *)cmsgp;
+ cmsgp = CMSG_NXTHDR(msgp, cmsgp);
+ }
+
+ /* set pktinfo ancillary data if needed */
+ if (if_index != 0) {
+ struct in6_pktinfo *pktinfop;
+
+ cmsgp->cmsg_level = IPPROTO_IPV6;
+ cmsgp->cmsg_type = IPV6_PKTINFO;
+ cmsg_datap = CMSG_DATA(cmsgp);
+
+ /* LINTED */
+ pktinfop = (struct in6_pktinfo *)cmsg_datap;
+ /*
+ * We don't know if pktinfop->ipi6_addr is aligned properly,
+ * therefore let's use bcopy, instead of assignment.
+ */
+ (void) bcopy(&in6addr_any, &pktinfop->ipi6_addr,
+ sizeof (struct in6_addr));
+
+ /*
+ * We can assume pktinfop->ipi6_ifindex is 32 bit aligned.
+ */
+ pktinfop->ipi6_ifindex = if_index;
+ cmsgp->cmsg_len = cmsg_datap + pktinfo_space - (uchar_t *)cmsgp;
+ cmsgp = CMSG_NXTHDR(msgp, cmsgp);
+ }
+
+ msgp->msg_controllen = (char *)cmsgp - (char *)msgp->msg_control;
+}
+
+/*
+ * Check out the packet to see if it came from us. This logic is necessary
+ * because ALL readers of the ICMP socket get a copy of ALL ICMP packets
+ * which arrive ('tis only fair). This permits multiple copies of this
+ * program to be run without having intermingled output (or statistics!).
+ */
+void
+check_reply6(struct addrinfo *ai_dst, struct msghdr *msg, int cc,
+ ushort_t udp_src_port)
+{
+ struct icmp6_hdr *icmp6;
+ ip6_t *ip6h;
+ nd_redirect_t *nd_rdrct;
+ struct udphdr *up;
+ union any_in_addr dst_addr;
+ uchar_t *buf;
+ int32_t *intp;
+ struct sockaddr_in6 *from6;
+ struct timeval tv;
+ struct timeval *tp;
+ int64_t triptime;
+ boolean_t valid_reply = _B_FALSE;
+ boolean_t reply_matched_current_target; /* Is the source address of */
+ /* this reply same as where */
+ /* we're sending currently? */
+ boolean_t last_reply_from_targetaddr = _B_FALSE; /* Is this stats, */
+ /* probe all with npackets>0 */
+ /* and we received reply for */
+ /* the last probe sent to */
+ /* targetaddr */
+ uint32_t ip6hdr_len;
+ uint8_t last_hdr;
+ int cc_left;
+ int i;
+ char tmp_buf[INET6_ADDRSTRLEN];
+ static char *unreach6[] = {
+ "No Route to Destination",
+ "Communication Administratively Prohibited",
+ "Not a Neighbor (obsoleted ICMPv6 code)",
+ "Address Unreachable",
+ "Port Unreachable"
+ };
+ static char *timexceed6[] = {
+ "Hop limit exceeded in transit",
+ "Fragment reassembly time exceeded"
+ };
+ static char *param_prob6[] = {
+ "Erroneous header field encountered",
+ "Unrecognized next header type encountered",
+ "Unrecognized IPv6 option encountered"
+ };
+ boolean_t print_newline = _B_FALSE;
+
+ /* decompose msghdr into useful pieces */
+ buf = (uchar_t *)msg->msg_iov->iov_base;
+ from6 = (struct sockaddr_in6 *)msg->msg_name;
+
+ /* LINTED */
+ intp = (int32_t *)buf;
+
+ /* get time now for most accurate time calculation */
+ (void) gettimeofday(&tv, (struct timezone *)NULL);
+
+ /* Ignore packets > 64k or control buffers that don't fit */
+ if (msg->msg_flags & (MSG_TRUNC|MSG_CTRUNC)) {
+ if (verbose) {
+ Printf("Truncated message: msg_flags 0x%x from %s\n",
+ msg->msg_flags,
+ pr_name((char *)&from6->sin6_addr, AF_INET6));
+ }
+ return;
+ }
+ if (cc < ICMP6_MINLEN) {
+ if (verbose) {
+ Printf("packet too short (%d bytes) from %s\n", cc,
+ pr_name((char *)&from6->sin6_addr, AF_INET6));
+ }
+ return;
+ }
+ /* LINTED */
+ icmp6 = (struct icmp6_hdr *)buf;
+ cc_left = cc - ICMP6_MINLEN;
+
+ switch (icmp6->icmp6_type) {
+ case ICMP6_DST_UNREACH:
+ /* LINTED */
+ ip6h = (ip6_t *)((char *)icmp6 + ICMP6_MINLEN);
+ if (cc_left < sizeof (ip6_t)) {
+ if (verbose) {
+ Printf("packet too short (%d bytes) from %s\n",
+ cc,
+ pr_name((char *)&from6->sin6_addr,
+ AF_INET6));
+ }
+ return;
+ }
+
+ /*
+ * Determine the total length of IPv6 header and extension
+ * headers, also the upper layer header (UDP, TCP, ICMP, etc.)
+ * following.
+ */
+ ip6hdr_len = IPv6_hdrlen(ip6h, cc_left, &last_hdr);
+
+ cc_left -= ip6hdr_len;
+
+ /* LINTED */
+ up = (struct udphdr *)((char *)ip6h + ip6hdr_len);
+ if (cc_left < sizeof (struct udphdr)) {
+ if (verbose) {
+ Printf("packet too short (%d bytes) from %s\n",
+ cc,
+ pr_name((char *)&from6->sin6_addr,
+ AF_INET6));
+ }
+ return;
+ }
+ cc_left -= sizeof (struct udphdr);
+
+ /* determine if this is *the* reply */
+ if (icmp6->icmp6_code == ICMP6_DST_UNREACH_NOPORT &&
+ last_hdr == IPPROTO_UDP &&
+ udp_src_port == up->uh_sport &&
+ use_udp) {
+ valid_reply = _B_TRUE;
+ } else {
+ valid_reply = _B_FALSE;
+ }
+
+ if (valid_reply) {
+ /*
+ * For this valid reply, if we are still sending to
+ * this target IP address, we'd like to do some
+ * updates to targetaddr, so hold SIGALRMs.
+ */
+ (void) sighold(SIGALRM);
+ is_alive = _B_TRUE;
+ nreceived++;
+ reply_matched_current_target =
+ seq_match(current_targetaddr->starting_seq_num,
+ current_targetaddr->num_sent,
+ ntohs(up->uh_dport));
+ if (reply_matched_current_target) {
+ current_targetaddr->got_reply = _B_TRUE;
+ nreceived_last_target++;
+ /*
+ * Determine if stats, probe-all, and
+ * npackets != 0, and this is the reply for
+ * the last probe we sent to current target
+ * address.
+ */
+ if (stats && probe_all && npackets > 0 &&
+ ((current_targetaddr->starting_seq_num +
+ current_targetaddr->num_probes - 1) %
+ (MAX_PORT + 1) == ntohs(up->uh_dport)) &&
+ (current_targetaddr->num_probes ==
+ current_targetaddr->num_sent))
+ last_reply_from_targetaddr = _B_TRUE;
+ } else {
+ /*
+ * If it's just probe_all and we just received
+ * a reply from a target address we were
+ * probing and had timed out (now we are probing
+ * some other target address), we ignore
+ * this reply.
+ */
+ if (probe_all && !stats) {
+ valid_reply = _B_FALSE;
+ /*
+ * Only if it's verbose, we get a
+ * message regarding this reply,
+ * otherwise we are done here.
+ */
+ if (!verbose) {
+ (void) sigrelse(SIGALRM);
+ return;
+ }
+ }
+ }
+ }
+
+ if (valid_reply && !stats) {
+ /*
+ * if we are still sending to the same target address,
+ * then stop it, because we know it's alive.
+ */
+ if (reply_matched_current_target) {
+ (void) alarm(0); /* cancel alarm */
+ (void) sigset(SIGALRM, SIG_IGN);
+ current_targetaddr->probing_done = _B_TRUE;
+ }
+ (void) sigrelse(SIGALRM);
+
+ if (!probe_all) {
+ Printf("%s is alive\n", targethost);
+ } else {
+ (void) inet_ntop(AF_INET6,
+ (void *)&ip6h->ip6_dst,
+ tmp_buf, sizeof (tmp_buf));
+ if (nflag) {
+ Printf("%s is alive\n", tmp_buf);
+ } else {
+ Printf("%s (%s) is alive\n",
+ targethost, tmp_buf);
+ }
+ }
+ if (reply_matched_current_target) {
+ /*
+ * Let's get things going again, but now
+ * ping will start sending to next target IP
+ * address.
+ */
+ send_scheduled_probe();
+ (void) sigset(SIGALRM, sigalrm_handler);
+ schedule_sigalrm();
+ }
+ return;
+ } else {
+ /*
+ * If we are not moving to next targetaddr, let's
+ * release the SIGALRM now. We don't want to stall in
+ * the middle of probing a targetaddr if the pr_name()
+ * call (see below) takes longer.
+ */
+ if (!last_reply_from_targetaddr)
+ (void) sigrelse(SIGALRM);
+ /* else, we'll release it later */
+ }
+
+ dst_addr.addr6 = ip6h->ip6_dst;
+ if (valid_reply) {
+ Printf("%d bytes from %s: ", cc,
+ pr_name((char *)&from6->sin6_addr, AF_INET6));
+ Printf("udp_port=%d. ", ntohs(up->uh_dport));
+ print_newline = _B_TRUE;
+ } else if (is_a_target(ai_dst, &dst_addr)|| verbose) {
+ if (icmp6->icmp6_code >= A_CNT(unreach6)) {
+ Printf("ICMPv6 %d Unreachable from gateway "
+ "%s\n", icmp6->icmp6_code,
+ pr_name((char *)&from6->sin6_addr,
+ AF_INET6));
+ } else {
+ Printf("ICMPv6 %s from gateway %s\n",
+ unreach6[icmp6->icmp6_code],
+ pr_name((char *)&from6->sin6_addr,
+ AF_INET6));
+ }
+ Printf(" for %s from %s", pr_protocol(last_hdr),
+ pr_name((char *)&ip6h->ip6_src, AF_INET6));
+ Printf(" to %s", pr_name((char *)&ip6h->ip6_dst,
+ AF_INET6));
+ if (last_hdr == IPPROTO_TCP || last_hdr == IPPROTO_UDP)
+ Printf(" port %d ", ntohs(up->uh_dport));
+ print_newline = _B_TRUE;
+ }
+
+ /*
+ * Update and print the stats, if it's a valid reply and
+ * contains a timestamp.
+ */
+ if (valid_reply && datalen >= sizeof (struct timeval) &&
+ cc_left >= sizeof (struct timeval)) {
+ /* LINTED */
+ tp = (struct timeval *)((char *)up +
+ sizeof (struct udphdr));
+ (void) tvsub(&tv, tp);
+ triptime = (int64_t)tv.tv_sec * MICROSEC + tv.tv_usec;
+ Printf("time=" TIMEFORMAT " ms", triptime/1000.0);
+ tsum += triptime;
+ tsum2 += triptime*triptime;
+ if (triptime < tmin)
+ tmin = triptime;
+ if (triptime > tmax)
+ tmax = triptime;
+ print_newline = _B_TRUE;
+ }
+ if (print_newline)
+ (void) putchar('\n');
+ /*
+ * If it's stats, probe-all, npackets > 0, and we received reply
+ * for the last probe sent to this target address, then we
+ * don't need to wait anymore, let's move on to next target
+ * address, now!
+ */
+ if (last_reply_from_targetaddr) {
+ (void) alarm(0); /* cancel alarm */
+ current_targetaddr->probing_done = _B_TRUE;
+ (void) sigrelse(SIGALRM);
+ send_scheduled_probe();
+ schedule_sigalrm();
+ }
+ break;
+
+ case ICMP6_PACKET_TOO_BIG:
+ /* LINTED */
+ ip6h = (ip6_t *)((char *)icmp6 + ICMP6_MINLEN);
+ if (cc_left < sizeof (ip6_t)) {
+ if (verbose) {
+ Printf("packet too short (%d bytes) from %s\n",
+ cc, pr_name((char *)&from6->sin6_addr,
+ AF_INET6));
+ }
+ return;
+ }
+ ip6hdr_len = IPv6_hdrlen(ip6h, cc_left, &last_hdr);
+
+ dst_addr.addr6 = ip6h->ip6_dst;
+ if (is_a_target(ai_dst, &dst_addr) || verbose) {
+ Printf("ICMPv6 packet too big from %s\n",
+ pr_name((char *)&from6->sin6_addr, AF_INET6));
+
+ Printf(" for %s from %s", pr_protocol(last_hdr),
+ pr_name((char *)&ip6h->ip6_src, AF_INET6));
+ Printf(" to %s", pr_name((char *)&ip6h->ip6_dst,
+ AF_INET6));
+ if ((last_hdr == IPPROTO_TCP ||
+ last_hdr == IPPROTO_UDP) &&
+ (cc_left >= (ip6hdr_len + 4))) {
+ /* LINTED */
+ up = (struct udphdr *)
+ ((char *)ip6h + ip6hdr_len);
+ Printf(" port %d ", ntohs(up->uh_dport));
+ }
+ Printf(" MTU = %d\n", ntohl(icmp6->icmp6_mtu));
+ }
+ break;
+
+ case ICMP6_TIME_EXCEEDED:
+ /* LINTED */
+ ip6h = (ip6_t *)((char *)icmp6 + ICMP6_MINLEN);
+ if (cc_left < sizeof (ip6_t)) {
+ if (verbose) {
+ Printf("packet too short (%d bytes) from %s\n",
+ cc,
+ pr_name((char *)&from6->sin6_addr,
+ AF_INET6));
+ }
+ return;
+ }
+ ip6hdr_len = IPv6_hdrlen(ip6h, cc_left, &last_hdr);
+
+ dst_addr.addr6 = ip6h->ip6_dst;
+ if (is_a_target(ai_dst, &dst_addr) || verbose) {
+ if (icmp6->icmp6_code >= A_CNT(timexceed6)) {
+ Printf("ICMPv6 %d time exceeded from %s\n",
+ icmp6->icmp6_code,
+ pr_name((char *)&from6->sin6_addr,
+ AF_INET6));
+ } else {
+ Printf("ICMPv6 %s from %s\n",
+ timexceed6[icmp6->icmp6_code],
+ pr_name((char *)&from6->sin6_addr,
+ AF_INET6));
+ }
+ Printf(" for %s from %s", pr_protocol(last_hdr),
+ pr_name((char *)&ip6h->ip6_src, AF_INET6));
+ Printf(" to %s", pr_name((char *)&ip6h->ip6_dst,
+ AF_INET6));
+ if ((last_hdr == IPPROTO_TCP ||
+ last_hdr == IPPROTO_UDP) &&
+ (cc_left >= (ip6hdr_len + 4))) {
+ /* LINTED */
+ up = (struct udphdr *)
+ ((char *)ip6h + ip6hdr_len);
+ Printf(" port %d", ntohs(up->uh_dport));
+ }
+ (void) putchar('\n');
+ }
+ break;
+
+ case ICMP6_PARAM_PROB:
+ /* LINTED */
+ ip6h = (ip6_t *)((char *)icmp6 + ICMP6_MINLEN);
+ if (cc_left < sizeof (ip6_t)) {
+ if (verbose) {
+ Printf("packet too short (%d bytes) from %s\n",
+ cc,
+ pr_name((char *)&from6->sin6_addr,
+ AF_INET6));
+ }
+ return;
+ }
+ ip6hdr_len = IPv6_hdrlen(ip6h, cc_left, &last_hdr);
+
+ dst_addr.addr6 = ip6h->ip6_dst;
+ if (is_a_target(ai_dst, &dst_addr) || verbose) {
+ if (icmp6->icmp6_code >= A_CNT(param_prob6)) {
+ Printf("ICMPv6 %d parameter problem from %s\n",
+ icmp6->icmp6_code,
+ pr_name((char *)&from6->sin6_addr,
+ AF_INET6));
+ } else {
+ Printf("ICMPv6 %s from %s\n",
+ param_prob6[icmp6->icmp6_code],
+ pr_name((char *)&from6->sin6_addr,
+ AF_INET6));
+ }
+ icmp6->icmp6_pptr = ntohl(icmp6->icmp6_pptr);
+ Printf(" in byte %d", icmp6->icmp6_pptr);
+ if (icmp6->icmp6_pptr <= ip6hdr_len) {
+ Printf(" (value 0x%x)",
+ *((uchar_t *)ip6h + icmp6->icmp6_pptr));
+ }
+ Printf(" for %s from %s", pr_protocol(last_hdr),
+ pr_name((char *)&ip6h->ip6_src, AF_INET6));
+ Printf(" to %s", pr_name((char *)&ip6h->ip6_dst,
+ AF_INET6));
+ if ((last_hdr == IPPROTO_TCP ||
+ last_hdr == IPPROTO_UDP) &&
+ (cc_left >= (ip6hdr_len + 4))) {
+ /* LINTED */
+ up = (struct udphdr *)
+ ((char *)ip6h + ip6hdr_len);
+ Printf(" port %d", ntohs(up->uh_dport));
+ }
+ (void) putchar('\n');
+ }
+ break;
+
+ case ICMP6_ECHO_REQUEST:
+ return;
+
+ case ICMP6_ECHO_REPLY:
+ if (ntohs(icmp6->icmp6_id) == ident) {
+ if (!use_udp)
+ valid_reply = _B_TRUE;
+ else
+ valid_reply = _B_FALSE;
+ } else {
+ return;
+ }
+
+ if (valid_reply) {
+ /*
+ * For this valid reply, if we are still sending to
+ * this target IP address, we'd like to do some
+ * updates to targetaddr, so hold SIGALRMs.
+ */
+ (void) sighold(SIGALRM);
+ is_alive = _B_TRUE;
+ nreceived++;
+ reply_matched_current_target =
+ seq_match(current_targetaddr->starting_seq_num,
+ current_targetaddr->num_sent,
+ ntohs(icmp6->icmp6_seq));
+ if (reply_matched_current_target) {
+ current_targetaddr->got_reply = _B_TRUE;
+ nreceived_last_target++;
+ /*
+ * Determine if stats, probe-all, and
+ * npackets != 0, and this is the reply for
+ * the last probe we sent to current target
+ * address.
+ */
+ if (stats && probe_all && npackets > 0 &&
+ ((current_targetaddr->starting_seq_num +
+ current_targetaddr->num_probes - 1) %
+ (MAX_ICMP_SEQ + 1) ==
+ ntohs(icmp6->icmp6_seq)) &&
+ (current_targetaddr->num_probes ==
+ current_targetaddr->num_sent))
+ last_reply_from_targetaddr = _B_TRUE;
+ } else {
+ /*
+ * If it's just probe_all and we just received
+ * a reply from a target address we were
+ * probing and had timed out (now we are probing
+ * some other target address), we ignore
+ * this reply.
+ */
+ if (probe_all && !stats) {
+ valid_reply = _B_FALSE;
+ /*
+ * Only if it's verbose, we get a
+ * message regarding this reply,
+ * otherwise we are done here.
+ */
+ if (!verbose) {
+ (void) sigrelse(SIGALRM);
+ return;
+ }
+ }
+ }
+ }
+
+ if (!stats && valid_reply) {
+ /*
+ * if we are still sending to the same target address,
+ * then stop it, because we know it's alive.
+ */
+ if (reply_matched_current_target) {
+ (void) alarm(0); /* cancel alarm */
+ (void) sigset(SIGALRM, SIG_IGN);
+ current_targetaddr->probing_done = _B_TRUE;
+ }
+ (void) sigrelse(SIGALRM);
+
+ if (!probe_all) {
+ Printf("%s is alive\n", targethost);
+ } else {
+ /*
+ * If we are using send_reply, the real
+ * target address is not the src address of the
+ * replies. Use icmp_seq to find out where this
+ * probe was sent to.
+ */
+ if (send_reply) {
+ (void) find_dstaddr(
+ ntohs(icmp6->icmp6_seq), &dst_addr);
+ (void) inet_ntop(AF_INET6,
+ (void *)&dst_addr.addr6,
+ tmp_buf, sizeof (tmp_buf));
+ } else {
+ (void) inet_ntop(AF_INET6,
+ (void *)&from6->sin6_addr,
+ tmp_buf, sizeof (tmp_buf));
+ }
+
+ if (nflag) {
+ Printf("%s is alive\n", tmp_buf);
+ } else {
+ Printf("%s (%s) is alive\n",
+ targethost, tmp_buf);
+ }
+ }
+ if (reply_matched_current_target) {
+ /*
+ * Let's get things going again, but now
+ * ping will start sending to next target IP
+ * address.
+ */
+ send_scheduled_probe();
+ (void) sigset(SIGALRM, sigalrm_handler);
+ schedule_sigalrm();
+ }
+ return;
+ } else {
+ /*
+ * If we are not moving to next targetaddr, let's
+ * release the SIGALRM now. We don't want to stall in
+ * the middle of probing a targetaddr if the pr_name()
+ * call (see below) takes longer.
+ */
+ if (!last_reply_from_targetaddr)
+ (void) sigrelse(SIGALRM);
+ /* else, we'll release it later */
+ }
+
+ /*
+ * If we are using send_reply, the real target address is
+ * not the src address of the replies. Use icmp_seq to find out
+ * where this probe was sent to.
+ */
+ if (send_reply) {
+ (void) find_dstaddr(ntohs(icmp6->icmp6_seq), &dst_addr);
+ Printf("%d bytes from %s: ", cc,
+ pr_name((char *)&dst_addr.addr6, AF_INET6));
+ } else {
+ Printf("%d bytes from %s: ", cc,
+ pr_name((char *)&from6->sin6_addr, AF_INET6));
+ }
+ Printf("icmp_seq=%d. ", ntohs(icmp6->icmp6_seq));
+
+ if (valid_reply && datalen >= sizeof (struct timeval) &&
+ cc_left >= sizeof (struct timeval)) {
+ /* LINTED */
+ tp = (struct timeval *)&icmp6->icmp6_data16[2];
+ (void) tvsub(&tv, tp);
+ triptime = (int64_t)tv.tv_sec * MICROSEC + tv.tv_usec;
+ Printf("time=" TIMEFORMAT " ms", triptime/1000.0);
+ tsum += triptime;
+ tsum2 += triptime*triptime;
+ if (triptime < tmin)
+ tmin = triptime;
+ if (triptime > tmax)
+ tmax = triptime;
+ }
+ (void) putchar('\n');
+ /*
+ * If it's stats, probe-all, npackets > 0, and we received reply
+ * for the last probe sent to this target address, then we
+ * don't need to wait anymore, let's move on to next target
+ * address, now!
+ */
+ if (last_reply_from_targetaddr) {
+ (void) alarm(0); /* cancel alarm */
+ current_targetaddr->probing_done = _B_TRUE;
+ (void) sigrelse(SIGALRM);
+ send_scheduled_probe();
+ schedule_sigalrm();
+ }
+ break;
+
+ case MLD_LISTENER_QUERY:
+ case MLD_LISTENER_REPORT:
+ case MLD_LISTENER_REDUCTION:
+ case ND_ROUTER_SOLICIT:
+ case ND_ROUTER_ADVERT:
+ case ND_NEIGHBOR_SOLICIT:
+ case ND_NEIGHBOR_ADVERT:
+ return;
+
+ case ND_REDIRECT:
+ nd_rdrct = (nd_redirect_t *)icmp6;
+
+ if (cc_left < sizeof (nd_redirect_t) - ICMP6_MINLEN) {
+ if (verbose) {
+ Printf("packet too short (%d bytes) from %s\n",
+ cc,
+ pr_name((char *)&from6->sin6_addr,
+ AF_INET6));
+ }
+ return;
+ }
+ dst_addr.addr6 = nd_rdrct->nd_rd_dst;
+ if (is_a_target(ai_dst, &dst_addr) || verbose) {
+ Printf("ICMPv6 redirect from gateway %s\n",
+ pr_name((char *)&from6->sin6_addr, AF_INET6));
+
+ Printf(" to %s",
+ pr_name((char *)&nd_rdrct->nd_rd_target, AF_INET6));
+ Printf(" for %s\n",
+ pr_name((char *)&nd_rdrct->nd_rd_dst, AF_INET6));
+ }
+ break;
+
+ default:
+ if (verbose) {
+ Printf("%d bytes from %s:\n", cc,
+ pr_name((char *)&from6->sin6_addr, AF_INET6));
+ Printf("icmp6_type=%d (%s) ", icmp6->icmp6_type,
+ pr_type6(icmp6->icmp6_type));
+ Printf("icmp6_code=%d\n", icmp6->icmp6_code);
+ for (i = 0; i < 12; i++) {
+ Printf("x%2.2x: x%8.8x\n",
+ i * sizeof (int32_t), *intp++);
+ }
+ }
+ break;
+ }
+
+ /*
+ * If it's verbose mode and we recv'd ancillary data, print extension
+ * headers.
+ */
+ if (verbose && msg->msg_controllen > 0)
+ pr_ext_headers(msg);
+}
+
+/*
+ * Convert an ICMP6 "type" field to a printable string.
+ */
+static char *
+pr_type6(uchar_t icmp6_type)
+{
+ static struct icmptype_table ttab6[] = {
+ {ICMP6_DST_UNREACH, "Dest Unreachable"},
+ {ICMP6_PACKET_TOO_BIG, "Packet Too Big"},
+ {ICMP6_TIME_EXCEEDED, "Time Exceeded"},
+ {ICMP6_PARAM_PROB, "Parameter Problem"},
+ {ICMP6_ECHO_REQUEST, "Echo Request"},
+ {ICMP6_ECHO_REPLY, "Echo Reply"},
+ {MLD_LISTENER_QUERY, "Multicast Listener Query"},
+ {MLD_LISTENER_REPORT, "Multicast Listener Report"},
+ {MLD_LISTENER_REDUCTION, "Multicast Listener Done"},
+ {ND_ROUTER_SOLICIT, "Router Solicitation"},
+ {ND_ROUTER_ADVERT, "Router Advertisement"},
+ {ND_NEIGHBOR_SOLICIT, "Neighbor Solicitation"},
+ {ND_NEIGHBOR_ADVERT, "Neighbor Advertisement"},
+ {ND_REDIRECT, "Redirect Message"},
+ };
+ int i;
+
+ for (i = 0; i < A_CNT(ttab6); i++) {
+ if (ttab6[i].type == icmp6_type)
+ return (ttab6[i].message);
+
+ }
+
+ return ("OUT-OF-RANGE");
+}
+
+/*
+ * Return the length of the IPv6 related headers (including extension headers).
+ * It also sets the *last_hdr_rtrn to the first upper layer protocol header
+ * following IPv6 header and extension headers. If print_flag is _B_TRUE, it
+ * prints extension headers.
+ */
+static int
+IPv6_hdrlen(ip6_t *ip6h, int pkt_len, uint8_t *last_hdr_rtrn)
+{
+ int length;
+ int exthdrlength;
+ uint8_t nexthdr;
+ uint8_t *whereptr;
+ ip6_hbh_t *hbhhdr;
+ ip6_dest_t *desthdr;
+ ip6_rthdr_t *rthdr;
+ ip6_frag_t *fraghdr;
+ uint8_t *endptr;
+
+ length = sizeof (ip6_t);
+
+ whereptr = ((uint8_t *)&ip6h[1]); /* point to next hdr */
+ endptr = ((uint8_t *)ip6h) + pkt_len;
+
+ nexthdr = ip6h->ip6_nxt;
+ *last_hdr_rtrn = IPPROTO_NONE;
+
+ if (whereptr >= endptr)
+ return (length);
+
+ while (whereptr < endptr) {
+ *last_hdr_rtrn = nexthdr;
+ switch (nexthdr) {
+ case IPPROTO_HOPOPTS:
+ hbhhdr = (ip6_hbh_t *)whereptr;
+ exthdrlength = 8 * (hbhhdr->ip6h_len + 1);
+ if ((uchar_t *)hbhhdr + exthdrlength > endptr)
+ return (length);
+ nexthdr = hbhhdr->ip6h_nxt;
+ length += exthdrlength;
+ break;
+
+ case IPPROTO_DSTOPTS:
+ desthdr = (ip6_dest_t *)whereptr;
+ exthdrlength = 8 * (desthdr->ip6d_len + 1);
+ if ((uchar_t *)desthdr + exthdrlength > endptr)
+ return (length);
+ nexthdr = desthdr->ip6d_nxt;
+ length += exthdrlength;
+ break;
+
+ case IPPROTO_ROUTING:
+ rthdr = (ip6_rthdr_t *)whereptr;
+ exthdrlength = 8 * (rthdr->ip6r_len + 1);
+ if ((uchar_t *)rthdr + exthdrlength > endptr)
+ return (length);
+ nexthdr = rthdr->ip6r_nxt;
+ length += exthdrlength;
+ break;
+
+ case IPPROTO_FRAGMENT:
+ /* LINTED */
+ fraghdr = (ip6_frag_t *)whereptr;
+ if ((uchar_t *)&fraghdr[1] > endptr)
+ return (length);
+ nexthdr = fraghdr->ip6f_nxt;
+ length += sizeof (struct ip6_frag);
+ break;
+
+ case IPPROTO_NONE:
+ default:
+ return (length);
+ }
+ whereptr = (uint8_t *)ip6h + length;
+ }
+ *last_hdr_rtrn = nexthdr;
+
+ return (length);
+}
+
+/*
+ * Print extension headers
+ */
+static void
+pr_ext_headers(struct msghdr *msg)
+{
+ struct cmsghdr *cmsg;
+
+ Printf(" IPv6 extension headers: ");
+
+ for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL;
+ cmsg = CMSG_NXTHDR(msg, cmsg)) {
+ if (cmsg->cmsg_level == IPPROTO_IPV6) {
+ switch (cmsg->cmsg_type) {
+ case IPV6_HOPOPTS:
+ Printf(" <hop-by-hop options>");
+ break;
+
+ case IPV6_DSTOPTS:
+ Printf(" <destination options (after routing"
+ "header)>");
+ break;
+
+ case IPV6_RTHDRDSTOPTS:
+ Printf(" <destination options (before routing"
+ "header)>");
+ break;
+
+ case IPV6_RTHDR:
+ pr_rthdr((uchar_t *)CMSG_DATA(cmsg));
+ break;
+
+ default:
+ Printf(" <option type %d>", cmsg->cmsg_type);
+ break;
+ }
+ }
+ }
+ (void) putchar('\n');
+}
+
+/*
+ * Print the routing header 0 information
+ */
+static void
+pr_rthdr(uchar_t *buf)
+{
+ ip6_rthdr_t *rthdr;
+ ip6_rthdr0_t *rthdr0;
+ struct in6_addr *gw_addr;
+ int i, num_addr;
+
+ rthdr = (ip6_rthdr_t *)buf;
+ Printf(" <type %d routing header, segleft %u> ",
+ rthdr->ip6r_type, rthdr->ip6r_segleft);
+
+ if (rthdr->ip6r_type == 0) {
+ /* LINTED */
+ rthdr0 = (ip6_rthdr0_t *)buf;
+ gw_addr = (struct in6_addr *)(rthdr0 + 1);
+ num_addr = rthdr0->ip6r0_len / 2;
+
+ for (i = 0; i < num_addr; i++) {
+ Printf("%s", pr_name((char *)gw_addr, AF_INET6));
+ if (i == (num_addr - rthdr0->ip6r0_segleft))
+ Printf("(Current)");
+ gw_addr++;
+ if (i != num_addr - 1)
+ Printf(", ");
+ }
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/ping/req.flg b/usr/src/cmd/cmd-inet/usr.sbin/ping/req.flg
new file mode 100644
index 0000000000..19f083c41a
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/ping/req.flg
@@ -0,0 +1,29 @@
+#!/bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+echo_file usr/src/cmd/cmd-inet/Makefile.cmd-inet
+echo_file usr/src/cmd/cmd-inet/common/Makefile
+echo_file usr/src/cmd/cmd-inet/common/ifaddrlist.c
+echo_file usr/src/cmd/cmd-inet/common/ifaddrlist.h
+
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/pntadm.sh b/usr/src/cmd/cmd-inet/usr.sbin/pntadm.sh
new file mode 100644
index 0000000000..9ca8c16d28
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/pntadm.sh
@@ -0,0 +1,30 @@
+#!/usr/bin/pfsh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+
+exec /usr/lib/inet/dhcp/svcadm/pntadm "$@"
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/rarp.xml b/usr/src/cmd/cmd-inet/usr.sbin/rarp.xml
new file mode 100644
index 0000000000..de1c254018
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/rarp.xml
@@ -0,0 +1,115 @@
+<?xml version="1.0"?>
+<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
+<!--
+ Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ ident "%Z%%M% %I% %E% SMI"
+
+ NOTE: This service manifest is not editable; its contents will
+ be overwritten by package or patch operations, including
+ operating system upgrade. Make customizations in a different
+ file.
+-->
+
+<service_bundle type='manifest' name='SUNWbsr:in.rarpd'>
+
+<service
+ name='network/rarp'
+ type='service'
+ version='1'>
+
+ <create_default_instance enabled='false' />
+
+ <single_instance/>
+
+ <dependency
+ name='services'
+ grouping='require_all'
+ restart_on='error'
+ type='path'>
+ <service_fmri value='file://localhost/etc/nsswitch.conf' />
+ </dependency>
+
+ <dependency
+ name='loopback'
+ grouping='require_any'
+ restart_on='error'
+ type='service'>
+ <service_fmri value='svc:/network/loopback' />
+ </dependency>
+
+ <dependency
+ name='network'
+ grouping='optional_all'
+ restart_on='error'
+ type='service'>
+ <service_fmri value='svc:/milestone/network' />
+ </dependency>
+
+ <dependency
+ name='syslog'
+ grouping='require_all'
+ restart_on='none'
+ type='service'>
+ <service_fmri value='svc:/system/system-log' />
+ </dependency>
+
+ <dependent
+ name='rarp_multi-user-server'
+ grouping='optional_all'
+ restart_on='none'>
+ <service_fmri
+ value='svc:/milestone/multi-user-server' />
+ </dependent>
+
+ <exec_method
+ type='method'
+ name='start'
+ exec='/usr/sbin/in.rarpd -a'
+ timeout_seconds='60' />
+
+ <exec_method
+ type='method'
+ name='stop'
+ exec=':kill'
+ timeout_seconds='60' />
+
+ <stability value='Unstable' />
+
+ <template>
+ <common_name>
+ <loctext xml:lang='C'>
+Reverse Address Resolution Protocol (RARP) server
+ </loctext>
+ </common_name>
+ <documentation>
+ <manpage title='rarp' section='7P'
+ manpath='/usr/share/man' />
+ <manpage title='in.rarpd' section='1M'
+ manpath='/usr/share/man' />
+ </documentation>
+ </template>
+</service>
+
+</service_bundle>
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/req.flg b/usr/src/cmd/cmd-inet/usr.sbin/req.flg
new file mode 100644
index 0000000000..fde232588e
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/req.flg
@@ -0,0 +1,30 @@
+#!/bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+echo_file usr/src/lib/gss_mechs/mech_krb5/Makefile.mech_krb5
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/rexec.xml b/usr/src/cmd/cmd-inet/usr.sbin/rexec.xml
new file mode 100644
index 0000000000..8eb11f4da7
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/rexec.xml
@@ -0,0 +1,102 @@
+<?xml version='1.0'?>
+<!DOCTYPE service_bundle SYSTEM '/usr/share/lib/xml/dtd/service_bundle.dtd.1'>
+
+<!--
+ Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ ident "%Z%%M% %I% %E% SMI"
+
+ NOTE: This service manifest is not editable; its contents will
+ be overwritten by package or patch operations, including
+ operating system upgrade. Make customizations in a different
+ file.
+
+ Service manifests for in.rexecd.
+-->
+
+<service_bundle type='manifest' name='SUNWrcmdr:rexec'>
+
+<service
+ name='network/rexec'
+ type='service'
+ version='1'>
+
+ <create_default_instance enabled='false' />
+
+ <restarter>
+ <service_fmri value='svc:/network/inetd:default' />
+ </restarter>
+
+ <exec_method
+ type='method'
+ name='inetd_start'
+ exec='/usr/sbin/in.rexecd'
+ timeout_seconds='0'>
+ <method_context>
+ <method_credential user='root' group='root' />
+ </method_context>
+ </exec_method>
+
+ <exec_method
+ type='method'
+ name='inetd_disable'
+ exec=':kill'
+ timeout_seconds='0'>
+ </exec_method>
+
+ <!--
+ The proto setting here of both tcp and tcp6only is required in order
+ to prevent breaking applications which assume that the socket they
+ are handed by rexec is of the AF_INET family.
+ -->
+ <property_group name='inetd' type='framework'>
+ <stability value='Evolving' />
+ <propval name='name' type='astring' value='exec' />
+ <propval name='endpoint_type' type='astring' value='stream' />
+ <propval name='wait' type='boolean' value='false' />
+ <propval name='isrpc' type='boolean' value='false' />
+ <property name='proto' type='astring' override='true'>
+ <astring_list>
+ <value_node value='tcp'/>
+ <value_node value='tcp6only'/>
+ </astring_list>
+ </property>
+ </property_group>
+
+ <stability value='Unstable' />
+
+ <template>
+ <common_name>
+ <loctext xml:lang='C'>
+ rexec
+ </loctext>
+ </common_name>
+ <documentation>
+ <manpage title='in.rexecd' section='1M'
+ manpath='/usr/share/man' />
+ </documentation>
+ </template>
+</service>
+
+</service_bundle>
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/route.c b/usr/src/cmd/cmd-inet/usr.sbin/route.c
new file mode 100644
index 0000000000..dfb5683f16
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/route.c
@@ -0,0 +1,2432 @@
+/*
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/* Copyright (c) 1990 Mentat Inc. */
+
+/*
+ *
+ * Copyright (c) 1983, 1989, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)route.c 8.6 (Berkeley) 4/28/95
+ * @(#)linkaddr.c 8.1 (Berkeley) 6/4/93
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/stream.h>
+#include <sys/tihdr.h>
+#include <sys/sysmacros.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <net/if_dl.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <inet/mib2.h>
+#include <inet/ip.h>
+
+#include <locale.h>
+
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stropts.h>
+#include <fcntl.h>
+#include <assert.h>
+
+static struct keytab {
+ char *kt_cp;
+ int kt_i;
+} keywords[] = {
+#define K_ADD 1
+ {"add", K_ADD},
+#define K_BLACKHOLE 2
+ {"blackhole", K_BLACKHOLE},
+#define K_CHANGE 3
+ {"change", K_CHANGE},
+#define K_CLONING 4
+ {"cloning", K_CLONING},
+#define K_DELETE 5
+ {"delete", K_DELETE},
+#define K_DST 6
+ {"dst", K_DST},
+#define K_EXPIRE 7
+ {"expire", K_EXPIRE},
+#define K_FLUSH 8
+ {"flush", K_FLUSH},
+#define K_GATEWAY 9
+ {"gateway", K_GATEWAY},
+#define K_GET 11
+ {"get", K_GET},
+#define K_HOPCOUNT 12
+ {"hopcount", K_HOPCOUNT},
+#define K_HOST 13
+ {"host", K_HOST},
+#define K_IFA 14
+ {"ifa", K_IFA},
+#define K_IFACE 15
+ {"iface", K_IFACE},
+#define K_IFP 16
+ {"ifp", K_IFP},
+#define K_INET 17
+ {"inet", K_INET},
+#define K_INET6 18
+ {"inet6", K_INET6},
+#define K_INTERFACE 19
+ {"interface", K_INTERFACE},
+#define K_LINK 20
+ {"link", K_LINK},
+#define K_LOCK 21
+ {"lock", K_LOCK},
+#define K_LOCKREST 22
+ {"lockrest", K_LOCKREST},
+#define K_MASK 23
+ {"mask", K_MASK},
+#define K_MONITOR 24
+ {"monitor", K_MONITOR},
+#define K_MTU 25
+ {"mtu", K_MTU},
+#define K_NET 26
+ {"net", K_NET},
+#define K_NETMASK 27
+ {"netmask", K_NETMASK},
+#define K_NOSTATIC 28
+ {"nostatic", K_NOSTATIC},
+#define K_PRIVATE 29
+ {"private", K_PRIVATE},
+#define K_PROTO1 30
+ {"proto1", K_PROTO1},
+#define K_PROTO2 31
+ {"proto2", K_PROTO2},
+#define K_RECVPIPE 32
+ {"recvpipe", K_RECVPIPE},
+#define K_REJECT 33
+ {"reject", K_REJECT},
+#define K_RTT 34
+ {"rtt", K_RTT},
+#define K_RTTVAR 35
+ {"rttvar", K_RTTVAR},
+#define K_SA 36
+ {"sa", K_SA},
+#define K_SENDPIPE 37
+ {"sendpipe", K_SENDPIPE},
+#define K_SSTHRESH 38
+ {"ssthresh", K_SSTHRESH},
+#define K_STATIC 39
+ {"static", K_STATIC},
+#define K_XRESOLVE 40
+ {"xresolve", K_XRESOLVE},
+#define K_MULTIRT 41
+ {"multirt", K_MULTIRT},
+#define K_SETSRC 42
+ {"setsrc", K_SETSRC},
+ {0, 0}
+};
+
+static union sockunion {
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+ struct sockaddr_dl sdl;
+ struct sockaddr_in6 sin6;
+} so_dst, so_gate, so_mask, so_ifa, so_ifp, so_src;
+
+typedef struct mib_item_s {
+ struct mib_item_s *next_item;
+ long group;
+ long mib_id;
+ long length;
+ intmax_t *valp;
+} mib_item_t;
+
+typedef union sockunion *sup;
+
+static void bprintf(FILE *fp, int b, char *s);
+static void delRouteEntry(mib2_ipRouteEntry_t *rp,
+ mib2_ipv6RouteEntry_t *rp6, int seqno);
+static void flushroutes(int argc, char *argv[]);
+static boolean_t getaddr(int which, char *s, struct hostent **hpp);
+static boolean_t in6_getaddr(char *s, struct sockaddr_in6 *sin6,
+ int *plenp, struct hostent **hpp);
+static boolean_t in_getaddr(char *s, struct sockaddr_in *sin,
+ int *plenp, int which, struct hostent **hpp);
+static int in_getprefixlen(char *addr, int max_plen);
+static boolean_t in_prefixlentomask(int prefixlen, int maxlen,
+ uchar_t *mask);
+static void inet_makenetandmask(in_addr_t net,
+ struct sockaddr_in *sin);
+static in_addr_t inet_makesubnetmask(in_addr_t addr, in_addr_t mask);
+static int keyword(char *cp);
+static void link_addr(const char *addr, struct sockaddr_dl *sdl);
+static char *link_ntoa(const struct sockaddr_dl *sdl);
+static mib_item_t *mibget(int sd);
+static char *netname(struct sockaddr *sa);
+static int newroute(int argc, char **argv);
+static void pmsg_addrs(char *cp, int addrs);
+static void pmsg_common(struct rt_msghdr *rtm);
+static void print_getmsg(struct rt_msghdr *rtm, int msglen);
+static void print_rtmsg(struct rt_msghdr *rtm, int msglen);
+static void quit(char *s, int err);
+static char *routename(struct sockaddr *sa);
+static void rtmonitor(int argc, char *argv[]);
+static int rtmsg(int cmd, int flags);
+static int salen(struct sockaddr *sa);
+static void set_metric(char *value, int key);
+static void sockaddr(char *addr, struct sockaddr *sa);
+static void sodump(sup su, char *which);
+static void usage(char *cp);
+
+static int pid, rtm_addrs;
+static int s;
+static boolean_t forcehost, forcenet, nflag;
+static int af = AF_INET;
+static boolean_t qflag, tflag;
+static boolean_t iflag, verbose;
+static boolean_t locking, lockrest, debugonly;
+static boolean_t fflag;
+static struct rt_metrics rt_metrics;
+static ulong_t rtm_inits;
+static int masklen;
+
+static struct {
+ struct rt_msghdr m_rtm;
+ char m_space[512];
+} m_rtmsg;
+
+/*
+ * Sizes of data structures extracted from the base mib.
+ * This allows the size of the tables entries to grow while preserving
+ * binary compatibility.
+ */
+static int ipRouteEntrySize;
+static int ipv6RouteEntrySize;
+
+#define ROUNDUP_LONG(a) \
+ ((a) > 0 ? (1 + (((a) - 1) | (sizeof (long) - 1))) : sizeof (long))
+#define ADVANCE(x, n) ((x) += ROUNDUP_LONG(salen(n)))
+#define C(x) ((x) & 0xff)
+
+/*
+ * return values from in_getprefixlen()
+ */
+#define BAD_ADDR -1 /* prefix is invalid */
+#define NO_PREFIX -2 /* no prefix was found */
+
+void
+usage(char *cp)
+{
+ if (cp != NULL)
+ (void) fprintf(stderr, gettext("route: botched keyword: %s\n"),
+ cp);
+ (void) fprintf(stderr,
+ gettext("usage: route [ -fnqv ] cmd [[ -<qualifers> ] args ]\n"));
+ exit(1);
+ /* NOTREACHED */
+}
+
+void
+quit(char *s, int sverrno)
+{
+ (void) fprintf(stderr, "route: ");
+ if (s != NULL)
+ (void) fprintf(stderr, "%s: ", s);
+ (void) fprintf(stderr, "%s\n", strerror(sverrno));
+ exit(sverrno);
+ /* NOTREACHED */
+}
+
+int
+main(int argc, char **argv)
+{
+ extern int optind;
+ int ch;
+ int key;
+
+ (void) setlocale(LC_ALL, "");
+
+#if !defined(TEXT_DOMAIN)
+#define TEXT_DOMAIN "SYS_TEST"
+#endif
+ (void) textdomain(TEXT_DOMAIN);
+
+ if (argc < 2)
+ usage((char *)NULL);
+
+ while ((ch = getopt(argc, argv, "nqdtvf")) != EOF) {
+ switch (ch) {
+ case 'n':
+ nflag = B_TRUE;
+ break;
+ case 'q':
+ qflag = B_TRUE;
+ break;
+ case 'v':
+ verbose = B_TRUE;
+ break;
+ case 't':
+ tflag = B_TRUE;
+ break;
+ case 'd':
+ debugonly = B_TRUE;
+ break;
+ case 'f':
+ fflag = B_TRUE;
+ break;
+ case '?':
+ default:
+ usage((char *)NULL);
+ /* NOTREACHED */
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ pid = getpid();
+ if (tflag)
+ s = open("/dev/null", O_WRONLY);
+ else
+ s = socket(PF_ROUTE, SOCK_RAW, 0);
+ if (s < 0)
+ quit("socket", errno);
+ if (fflag) {
+ /*
+ * Accept an address family keyword after the -f. Since the
+ * default address family is AF_INET, reassign af only for the
+ * other valid address families.
+ */
+ if (*argv != NULL) {
+ switch (key = keyword(*argv)) {
+ case K_INET:
+ case K_INET6:
+ if (key == K_INET6)
+ af = AF_INET6;
+ /* Skip over the address family parameter. */
+ argc--;
+ argv++;
+ break;
+ }
+ }
+ flushroutes(0, NULL);
+ }
+ if (*argv != NULL) {
+ switch (keyword(*argv)) {
+ case K_GET:
+ case K_CHANGE:
+ case K_ADD:
+ case K_DELETE:
+ return (newroute(argc, argv));
+
+ case K_MONITOR:
+ rtmonitor(argc, argv);
+ /* NOTREACHED */
+
+ case K_FLUSH:
+ flushroutes(argc, argv);
+ exit(0);
+ /* NOTREACHED */
+ }
+ }
+ if (!fflag)
+ usage(*argv);
+ return (0);
+}
+
+/*
+ * Purge all entries in the routing tables not
+ * associated with network interfaces.
+ */
+void
+flushroutes(int argc, char *argv[])
+{
+ int seqno;
+ int sd; /* mib stream */
+ mib_item_t *item;
+ mib2_ipRouteEntry_t *rp;
+ mib2_ipv6RouteEntry_t *rp6;
+ int oerrno;
+ int off = 0;
+ int on = 1;
+
+ if (setsockopt(s, SOL_SOCKET, SO_USELOOPBACK, (char *)&off,
+ sizeof (off)) < 0)
+ quit("setsockopt", errno);
+ if (argc > 1) {
+ argv++;
+ if (argc == 2 && **argv == '-') {
+ /*
+ * The address family (preceded by a dash) may be used
+ * to flush the routes of that particular family.
+ */
+ switch (keyword(*argv + 1)) {
+ case K_INET:
+ af = AF_INET;
+ break;
+ case K_LINK:
+ af = AF_LINK;
+ break;
+ case K_INET6:
+ af = AF_INET6;
+ break;
+ default:
+ usage(*argv);
+ /* NOTREACHED */
+ }
+ } else {
+ usage(*argv);
+ }
+ }
+ sd = open("/dev/ip", O_RDWR);
+ oerrno = errno;
+ if (sd < 0) {
+ switch (errno) {
+ case EACCES:
+ (void) fprintf(stderr,
+ gettext("route: flush: insufficient privileges\n"));
+ exit(oerrno);
+ /* NOTREACHED */
+ default:
+ quit(gettext("can't open mib stream"), oerrno);
+ /* NOTREACHED */
+ }
+ }
+ if ((item = mibget(sd)) == NULL)
+ quit("mibget", errno);
+ if (verbose) {
+ (void) printf("Examining routing table from "
+ "T_SVR4_OPTMGMT_REQ\n");
+ }
+ seqno = 0; /* ??? */
+ switch (af) {
+ case AF_INET:
+ /* Extract ipRouteEntrySize */
+ for (; item != NULL; item = item->next_item) {
+ if (item->mib_id != 0)
+ continue;
+ if (item->group == MIB2_IP) {
+ ipRouteEntrySize =
+ ((mib2_ip_t *)item->valp)->ipRouteEntrySize;
+ assert(IS_P2ALIGNED(ipRouteEntrySize,
+ sizeof (mib2_ipRouteEntry_t *)));
+ break;
+ }
+ }
+ if (ipRouteEntrySize == 0) {
+ (void) fprintf(stderr,
+ gettext("ipRouteEntrySize can't be determined.\n"));
+ exit(1);
+ }
+ for (; item != NULL; item = item->next_item) {
+ /*
+ * skip all the other trash that comes up the mib stream
+ */
+ if (item->group != MIB2_IP ||
+ item->mib_id != MIB2_IP_ROUTE)
+ continue;
+ for (rp = (mib2_ipRouteEntry_t *)item->valp;
+ (char *)rp < (char *)item->valp + item->length;
+ /* LINTED */
+ rp = (mib2_ipRouteEntry_t *)
+ ((char *)rp + ipRouteEntrySize)) {
+ delRouteEntry(rp, NULL, seqno);
+ seqno++;
+ }
+ break;
+ }
+ break;
+ case AF_INET6:
+ /* Extract ipv6RouteEntrySize */
+ for (; item != NULL; item = item->next_item) {
+ if (item->mib_id != 0)
+ continue;
+ if (item->group == MIB2_IP6) {
+ ipv6RouteEntrySize =
+ ((mib2_ipv6IfStatsEntry_t *)item->valp)->
+ ipv6RouteEntrySize;
+ assert(IS_P2ALIGNED(ipv6RouteEntrySize,
+ sizeof (mib2_ipv6RouteEntry_t *)));
+ break;
+ }
+ }
+ if (ipv6RouteEntrySize == 0) {
+ (void) fprintf(stderr, gettext(
+ "ipv6RouteEntrySize cannot be determined.\n"));
+ exit(1);
+ }
+ for (; item != NULL; item = item->next_item) {
+ /*
+ * skip all the other trash that comes up the mib stream
+ */
+ if (item->group != MIB2_IP6 ||
+ item->mib_id != MIB2_IP6_ROUTE)
+ continue;
+ for (rp6 = (mib2_ipv6RouteEntry_t *)item->valp;
+ (char *)rp6 < (char *)item->valp + item->length;
+ /* LINTED */
+ rp6 = (mib2_ipv6RouteEntry_t *)
+ ((char *)rp6 + ipv6RouteEntrySize)) {
+ delRouteEntry(NULL, rp6, seqno);
+ seqno++;
+ }
+ break;
+ }
+ break;
+ }
+
+ if (setsockopt(s, SOL_SOCKET, SO_USELOOPBACK, (char *)&on,
+ sizeof (on)) < 0)
+ quit("setsockopt", errno);
+}
+
+/*
+ * Given the contents of a mib_item_t of id type MIB2_IP_ROUTE or
+ * MIB2_IP6_ROUTE, construct and send an RTM_DELETE routing socket message in
+ * order to facilitate the flushing of RTF_GATEWAY routes.
+ */
+static void
+delRouteEntry(mib2_ipRouteEntry_t *rp, mib2_ipv6RouteEntry_t *rp6, int seqno)
+{
+ char *cp;
+ int ire_type;
+ int rlen;
+ struct rt_msghdr *rtm;
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+ int slen;
+
+ if (rp != NULL)
+ ire_type = rp->ipRouteInfo.re_ire_type;
+ else
+ ire_type = rp6->ipv6RouteInfo.re_ire_type;
+ if (ire_type != IRE_DEFAULT &&
+ ire_type != IRE_PREFIX &&
+ ire_type != IRE_HOST &&
+ ire_type != IRE_HOST_REDIRECT)
+ return;
+
+ rtm = &m_rtmsg.m_rtm;
+ (void) memset(rtm, 0, sizeof (m_rtmsg));
+ rtm->rtm_type = RTM_DELETE;
+ rtm->rtm_seq = seqno;
+ rtm->rtm_flags |= RTF_GATEWAY;
+ rtm->rtm_version = RTM_VERSION;
+ rtm->rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
+ cp = m_rtmsg.m_space;
+ if (rp != NULL) {
+ slen = sizeof (struct sockaddr_in);
+ if (rp->ipRouteMask == IP_HOST_MASK)
+ rtm->rtm_flags |= RTF_HOST;
+ (void) memset(&sin, 0, slen);
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = rp->ipRouteDest;
+ (void) memmove(cp, &sin, slen);
+ cp += slen;
+ sin.sin_addr.s_addr = rp->ipRouteNextHop;
+ (void) memmove(cp, &sin, slen);
+ cp += slen;
+ sin.sin_addr.s_addr = rp->ipRouteMask;
+ (void) memmove(cp, &sin, slen);
+ cp += slen;
+ } else {
+ slen = sizeof (struct sockaddr_in6);
+ if (rp6->ipv6RoutePfxLength == IPV6_ABITS)
+ rtm->rtm_flags |= RTF_HOST;
+ (void) memset(&sin6, 0, slen);
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_addr = rp6->ipv6RouteDest;
+ (void) memmove(cp, &sin6, slen);
+ cp += slen;
+ sin6.sin6_addr = rp6->ipv6RouteNextHop;
+ (void) memmove(cp, &sin6, slen);
+ cp += slen;
+ (void) memset(&sin6.sin6_addr, 0, sizeof (sin6.sin6_addr));
+ (void) in_prefixlentomask(rp6->ipv6RoutePfxLength, IPV6_ABITS,
+ (uchar_t *)&sin6.sin6_addr.s6_addr);
+ (void) memmove(cp, &sin6, slen);
+ cp += slen;
+ }
+ rtm->rtm_msglen = cp - (char *)&m_rtmsg;
+ if (debugonly) {
+ /*
+ * In debugonly mode, the routing socket message to delete the
+ * current entry is not actually sent. However if verbose is
+ * also set, the routing socket message that would have been
+ * is printed.
+ */
+ if (verbose)
+ print_rtmsg(rtm, rtm->rtm_msglen);
+ return;
+ }
+
+ rlen = write(s, (char *)&m_rtmsg, rtm->rtm_msglen);
+ if (rlen < (int)rtm->rtm_msglen) {
+ if (rlen < 0) {
+ (void) fprintf(stderr,
+ gettext("route: write to routing socket: %s\n"),
+ strerror(errno));
+ } else {
+ (void) fprintf(stderr, gettext("route: write to "
+ "routing socket got only %d for rlen\n"), rlen);
+ }
+ return;
+ }
+ if (qflag) {
+ /*
+ * In quiet mode, nothing is printed at all (unless the write()
+ * itself failed.
+ */
+ return;
+ }
+ if (verbose) {
+ print_rtmsg(rtm, rlen);
+ } else {
+ struct sockaddr *sa = (struct sockaddr *)(rtm + 1);
+
+ (void) printf("%-20.20s ",
+ rtm->rtm_flags & RTF_HOST ? routename(sa) :
+ netname(sa));
+ /* LINTED */
+ sa = (struct sockaddr *)(salen(sa) + (char *)sa);
+ (void) printf("%-20.20s ", routename(sa));
+ (void) printf("done\n");
+ }
+}
+
+/*
+ * Return the name of the host whose address is given.
+ */
+char *
+routename(struct sockaddr *sa)
+{
+ char *cp;
+ static char line[MAXHOSTNAMELEN + 1];
+ struct hostent *hp = NULL;
+ static char domain[MAXHOSTNAMELEN + 1];
+ static boolean_t first = B_TRUE;
+ struct in_addr in;
+ struct in6_addr in6;
+ int error_num;
+ ushort_t *s;
+ ushort_t *slim;
+
+ if (first) {
+ first = B_FALSE;
+ if (gethostname(domain, MAXHOSTNAMELEN) == 0 &&
+ (cp = strchr(domain, '.')))
+ (void) strcpy(domain, cp + 1);
+ else
+ domain[0] = 0;
+ }
+
+ if (salen(sa) == 0) {
+ (void) strcpy(line, "default");
+ return (line);
+ }
+ switch (sa->sa_family) {
+
+ case AF_INET:
+ /* LINTED */
+ in = ((struct sockaddr_in *)sa)->sin_addr;
+
+ cp = NULL;
+ if (in.s_addr == INADDR_ANY)
+ cp = "default";
+ if (cp == NULL && !nflag) {
+ hp = gethostbyaddr((char *)&in, sizeof (struct in_addr),
+ AF_INET);
+ if (hp != NULL) {
+ if (((cp = strchr(hp->h_name, '.')) != NULL) &&
+ (strcmp(cp + 1, domain) == 0))
+ *cp = 0;
+ cp = hp->h_name;
+ }
+ }
+ if (cp != NULL) {
+ (void) strncpy(line, cp, MAXHOSTNAMELEN);
+ line[MAXHOSTNAMELEN] = '\0';
+ } else {
+ in.s_addr = ntohl(in.s_addr);
+ (void) sprintf(line, "%u.%u.%u.%u", C(in.s_addr >> 24),
+ C(in.s_addr >> 16), C(in.s_addr >> 8),
+ C(in.s_addr));
+ }
+ break;
+
+ case AF_LINK:
+ return (link_ntoa((struct sockaddr_dl *)sa));
+
+ case AF_INET6:
+ /* LINTED */
+ in6 = ((struct sockaddr_in6 *)sa)->sin6_addr;
+
+ cp = NULL;
+ if (IN6_IS_ADDR_UNSPECIFIED(&in6))
+ cp = "default";
+ if (cp == NULL && !nflag) {
+ hp = getipnodebyaddr((char *)&in6,
+ sizeof (struct in6_addr), AF_INET6, &error_num);
+ if (hp != NULL) {
+ if (((cp = strchr(hp->h_name, '.')) != NULL) &&
+ (strcmp(cp + 1, domain) == 0))
+ *cp = 0;
+ cp = hp->h_name;
+ }
+ }
+ if (cp != NULL) {
+ (void) strncpy(line, cp, MAXHOSTNAMELEN);
+ line[MAXHOSTNAMELEN] = '\0';
+ } else {
+ (void) inet_ntop(AF_INET6, (void *)&in6, line,
+ INET6_ADDRSTRLEN);
+ }
+ if (hp != NULL)
+ freehostent(hp);
+
+ break;
+
+ default:
+ s = (ushort_t *)sa;
+
+ slim = s + ((salen(sa) + 1) >> 1);
+ cp = line + sprintf(line, "(%d)", sa->sa_family);
+
+ while (++s < slim) /* start with sa->sa_data */
+ cp += sprintf(cp, " %x", *s);
+ break;
+ }
+ return (line);
+}
+
+/*
+ * Return the name of the network whose address is given.
+ * The address is assumed to be that of a net or subnet, not a host.
+ */
+static char *
+netname(struct sockaddr *sa)
+{
+ char *cp = NULL;
+ static char line[MAXHOSTNAMELEN + 1];
+ struct netent *np;
+ in_addr_t net, mask;
+ int subnetshift;
+ struct in_addr in;
+ ushort_t *s;
+ ushort_t *slim;
+
+ switch (sa->sa_family) {
+
+ case AF_INET:
+ /* LINTED */
+ in = ((struct sockaddr_in *)sa)->sin_addr;
+
+ in.s_addr = ntohl(in.s_addr);
+ if (in.s_addr == INADDR_ANY) {
+ cp = "default";
+ } else if (!nflag) {
+ if (IN_CLASSA(in.s_addr)) {
+ mask = IN_CLASSA_NET;
+ subnetshift = 8;
+ } else if (IN_CLASSB(in.s_addr)) {
+ mask = IN_CLASSB_NET;
+ subnetshift = 8;
+ } else {
+ mask = IN_CLASSC_NET;
+ subnetshift = 4;
+ }
+ /*
+ * If there are more bits than the standard mask
+ * would suggest, subnets must be in use.
+ * Guess at the subnet mask, assuming reasonable
+ * width subnet fields.
+ */
+ while (in.s_addr &~ mask)
+ mask = (long)mask >> subnetshift;
+ net = in.s_addr & mask;
+ while ((mask & 1) == 0)
+ mask >>= 1, net >>= 1;
+ np = getnetbyaddr(net, AF_INET);
+ if (np != NULL)
+ cp = np->n_name;
+ }
+ if (cp != NULL) {
+ (void) strncpy(line, cp, MAXHOSTNAMELEN);
+ line[MAXHOSTNAMELEN] = '\0';
+ } else if ((in.s_addr & 0xffffff) == 0) {
+ (void) sprintf(line, "%u", C(in.s_addr >> 24));
+ } else if ((in.s_addr & 0xffff) == 0) {
+ (void) sprintf(line, "%u.%u", C(in.s_addr >> 24),
+ C(in.s_addr >> 16));
+ } else if ((in.s_addr & 0xff) == 0) {
+ (void) sprintf(line, "%u.%u.%u", C(in.s_addr >> 24),
+ C(in.s_addr >> 16), C(in.s_addr >> 8));
+ } else {
+ (void) sprintf(line, "%u.%u.%u.%u", C(in.s_addr >> 24),
+ C(in.s_addr >> 16), C(in.s_addr >> 8),
+ C(in.s_addr));
+ }
+ break;
+
+ case AF_LINK:
+ return (link_ntoa((struct sockaddr_dl *)sa));
+
+ case AF_INET6:
+ return (routename(sa));
+
+ default:
+ /* LINTED */
+ s = (ushort_t *)sa->sa_data;
+
+ slim = s + ((salen(sa) + 1) >> 1);
+ cp = line + sprintf(line, "af %d:", sa->sa_family);
+
+ while (s < slim)
+ cp += sprintf(cp, " %x", *s++);
+ break;
+ }
+ return (line);
+}
+
+void
+set_metric(char *value, int key)
+{
+ int flag = 0;
+ uint_t noval, *valp = &noval;
+
+ switch (key) {
+#define caseof(x, y, z) case (x): valp = &rt_metrics.z; flag = (y); break
+ caseof(K_MTU, RTV_MTU, rmx_mtu);
+ caseof(K_HOPCOUNT, RTV_HOPCOUNT, rmx_hopcount);
+ caseof(K_EXPIRE, RTV_EXPIRE, rmx_expire);
+ caseof(K_RECVPIPE, RTV_RPIPE, rmx_recvpipe);
+ caseof(K_SENDPIPE, RTV_SPIPE, rmx_sendpipe);
+ caseof(K_SSTHRESH, RTV_SSTHRESH, rmx_ssthresh);
+ caseof(K_RTT, RTV_RTT, rmx_rtt);
+ caseof(K_RTTVAR, RTV_RTTVAR, rmx_rttvar);
+#undef caseof
+ }
+ rtm_inits |= flag;
+ if (lockrest || locking)
+ rt_metrics.rmx_locks |= flag;
+ if (locking)
+ locking = B_FALSE;
+ *valp = atoi(value);
+}
+
+int
+newroute(int argc, char **argv)
+{
+ char *cmd, *dest = "", *gateway = "", *err;
+ boolean_t ishost = B_FALSE;
+ int ret, attempts, oerrno, flags = RTF_STATIC;
+ int key;
+ struct hostent *hp = NULL;
+ static char obuf[INET6_ADDRSTRLEN];
+
+ cmd = argv[0];
+ if (*cmd != 'g' && !tflag) {
+ /* Don't want to read back our messages */
+ (void) shutdown(s, 0);
+ }
+ while (--argc > 0) {
+ key = keyword(*(++argv));
+ if (key == K_HOST) {
+ forcehost = B_TRUE;
+ } else if (key == K_NET) {
+ forcenet = B_TRUE;
+ } else if (**(argv) == '-') {
+ switch (key = keyword(1 + *argv)) {
+ case K_LINK:
+ af = AF_LINK;
+ break;
+ case K_INET:
+ af = AF_INET;
+ break;
+ case K_SA:
+ af = PF_ROUTE;
+ break;
+ case K_INET6:
+ af = AF_INET6;
+ break;
+ case K_IFACE:
+ case K_INTERFACE:
+ iflag = B_TRUE;
+ /* FALLTHROUGH */
+ case K_NOSTATIC:
+ flags &= ~RTF_STATIC;
+ break;
+ case K_LOCK:
+ locking = B_TRUE;
+ break;
+ case K_LOCKREST:
+ lockrest = B_TRUE;
+ break;
+ case K_HOST:
+ forcehost = B_TRUE;
+ break;
+ case K_REJECT:
+ flags |= RTF_REJECT;
+ break;
+ case K_BLACKHOLE:
+ flags |= RTF_BLACKHOLE;
+ break;
+ case K_PROTO1:
+ flags |= RTF_PROTO1;
+ break;
+ case K_PROTO2:
+ flags |= RTF_PROTO2;
+ break;
+ case K_CLONING:
+ flags |= RTF_CLONING;
+ break;
+ case K_XRESOLVE:
+ flags |= RTF_XRESOLVE;
+ break;
+ case K_STATIC:
+ flags |= RTF_STATIC;
+ break;
+ case K_IFA:
+ argc--;
+ (void) getaddr(RTA_IFA, *++argv, NULL);
+ break;
+ case K_IFP:
+ argc--;
+ (void) getaddr(RTA_IFP, *++argv, NULL);
+ break;
+ case K_GATEWAY:
+ /*
+ * For the gateway parameter, retrieve the
+ * pointer to the struct hostent so that all
+ * possible addresses can be tried until one
+ * is successful.
+ */
+ argc--;
+ gateway = *++argv;
+ (void) getaddr(RTA_GATEWAY, *argv, &hp);
+ break;
+ case K_DST:
+ argc--;
+ ishost = getaddr(RTA_DST, *++argv, NULL);
+ dest = *argv;
+ break;
+ case K_NETMASK:
+ argc--;
+ (void) getaddr(RTA_NETMASK, *++argv, NULL);
+ /* FALLTHROUGH */
+ case K_NET:
+ forcenet = B_TRUE;
+ break;
+ case K_MTU:
+ case K_HOPCOUNT:
+ case K_EXPIRE:
+ case K_RECVPIPE:
+ case K_SENDPIPE:
+ case K_SSTHRESH:
+ case K_RTT:
+ case K_RTTVAR:
+ argc--;
+ set_metric(*++argv, key);
+ break;
+ case K_PRIVATE:
+ flags |= RTF_PRIVATE;
+ break;
+ case K_MULTIRT:
+ flags |= RTF_MULTIRT;
+ break;
+ case K_SETSRC:
+ argc--;
+ (void) getaddr(RTA_SRC, *++argv, NULL);
+ flags |= RTF_SETSRC;
+ break;
+ default:
+ usage(*argv + 1);
+ /* NOTREACHED */
+ }
+ } else {
+ if ((rtm_addrs & RTA_DST) == 0) {
+ dest = *argv;
+ ishost = getaddr(RTA_DST, *argv, NULL);
+ } else if ((rtm_addrs & RTA_GATEWAY) == 0) {
+ /*
+ * For the gateway parameter, retrieve the
+ * pointer to the struct hostent so that all
+ * possible addresses can be tried until one
+ * is successful.
+ */
+ gateway = *argv;
+ (void) getaddr(RTA_GATEWAY, *argv, &hp);
+ } else {
+ ulong_t metric = strtoul(*argv, &err, 10);
+
+ /*
+ * Assume that a regular number is a metric.
+ * Needed for compatibility with old route
+ * command syntax.
+ */
+ if (*argv != err && *err == '\0' &&
+ metric < 0x80000000ul) {
+ iflag = (metric == 0);
+ if (verbose) {
+ (void) printf("old usage of "
+ "trailing number, assuming "
+ "route %s\n", iflag ?
+ "to if" : "via gateway");
+ }
+ continue;
+ }
+ (void) getaddr(RTA_NETMASK, *argv, NULL);
+ }
+ }
+ }
+ if ((rtm_addrs & RTA_DST) == 0) {
+ (void) fprintf(stderr,
+ gettext("route: destination required following command\n"));
+ usage((char *)NULL);
+ } else if ((*cmd == 'a' || *cmd == 'd') &&
+ (rtm_addrs & RTA_GATEWAY) == 0) {
+ (void) fprintf(stderr,
+ gettext("route: gateway required for add or delete "
+ "command\n"));
+ usage((char *)NULL);
+ }
+
+ /*
+ * If the netmask has been specified use it to determine RTF_HOST.
+ * Otherwise rely on the "-net" and "-host" specifiers.
+ * Final fallback is whether ot not any bits were set in the address
+ * past the classful network component.
+ */
+ if (rtm_addrs & RTA_NETMASK) {
+ if ((af == AF_INET &&
+ so_mask.sin.sin_addr.s_addr == IP_HOST_MASK) ||
+ (af == AF_INET6 && masklen == IPV6_ABITS))
+ forcehost = B_TRUE;
+ else
+ forcenet = B_TRUE;
+ }
+ if (forcehost)
+ ishost = B_TRUE;
+ if (forcenet)
+ ishost = B_FALSE;
+ flags |= RTF_UP;
+ if (ishost)
+ flags |= RTF_HOST;
+ if (!iflag)
+ flags |= RTF_GATEWAY;
+ for (attempts = 1; ; attempts++) {
+ errno = 0;
+ if ((ret = rtmsg(*cmd, flags)) == 0)
+ break;
+ if (errno != ENETUNREACH && errno != ESRCH)
+ break;
+ if (*gateway != '\0' && hp != NULL &&
+ hp->h_addr_list[attempts] != NULL) {
+ switch (af) {
+ case AF_INET:
+ (void) memmove(&so_gate.sin.sin_addr,
+ hp->h_addr_list[attempts], hp->h_length);
+ continue;
+ case AF_INET6:
+ (void) memmove(&so_gate.sin6.sin6_addr,
+ hp->h_addr_list[attempts], hp->h_length);
+ continue;
+ }
+ }
+ break;
+ }
+ oerrno = errno;
+ if (*cmd != 'g') {
+ (void) printf("%s %s %s", cmd, ishost ? "host" : "net", dest);
+ if (*gateway != '\0') {
+ switch (af) {
+ case AF_INET:
+ if (nflag) {
+ (void) printf(": gateway %s",
+ inet_ntoa(so_gate.sin.sin_addr));
+ } else if (attempts > 1 && ret == 0) {
+ (void) printf(": gateway %s (%s)",
+ gateway,
+ inet_ntoa(so_gate.sin.sin_addr));
+ } else {
+ (void) printf(": gateway %s", gateway);
+ }
+ break;
+ case AF_INET6:
+ if (inet_ntop(AF_INET6,
+ (void *)&so_gate.sin6.sin6_addr, obuf,
+ INET6_ADDRSTRLEN) != NULL) {
+ if (nflag) {
+ (void) printf(": gateway %s",
+ obuf);
+ } else if (attempts > 1 && ret == 0) {
+ (void) printf(": gateway %s "
+ "(%s)",
+ gateway, obuf);
+ }
+ break;
+ }
+ /* FALLTHROUGH */
+ default:
+ (void) printf(": gateway %s", gateway);
+ break;
+ }
+ }
+ if (ret == 0)
+ (void) printf("\n");
+ }
+ if (ret != 0) {
+ if (*cmd == 'g') {
+ if (nflag) {
+ switch (af) {
+ case AF_INET:
+ (void) printf(" %s",
+ inet_ntoa(so_dst.sin.sin_addr));
+ break;
+ case AF_INET6:
+ if (inet_ntop(AF_INET6,
+ (void *)&so_dst.sin6.sin6_addr,
+ obuf, INET6_ADDRSTRLEN) != NULL) {
+ (void) printf(" %s", obuf);
+ break;
+ }
+ /* FALLTHROUGH */
+ default:
+ (void) printf("%s", dest);
+ break;
+ }
+ } else {
+ (void) printf("%s", dest);
+ }
+ }
+ switch (oerrno) {
+ case ESRCH:
+ err = "not in table";
+ break;
+ case EBUSY:
+ err = "entry in use";
+ break;
+ case ENOBUFS:
+ err = "routing table overflow";
+ break;
+ case EEXIST:
+ err = "entry exists";
+ break;
+ case EPERM:
+ err = "insufficient privileges";
+ break;
+ default:
+ err = strerror(oerrno);
+ break;
+ }
+ (void) printf(": %s\n", err);
+ }
+ /*
+ * In the case of AF_INET6, one of the getipnodebyX() functions was used
+ * so free the allocated hostent.
+ */
+ if (af == AF_INET6 && hp != NULL)
+ freehostent(hp);
+
+ return (oerrno);
+}
+
+
+/*
+ * Convert a network number to the corresponding IP address.
+ * If the RTA_NETMASK hasn't been specified yet set it based
+ * on the class of address.
+ */
+static void
+inet_makenetandmask(in_addr_t net, struct sockaddr_in *sin)
+{
+ in_addr_t addr, mask;
+ char *cp;
+
+ if (net == 0) {
+ mask = addr = 0;
+ } else if (net < 128) {
+ addr = net << IN_CLASSA_NSHIFT;
+ mask = IN_CLASSA_NET;
+ } else if (net < 65536) {
+ addr = net << IN_CLASSB_NSHIFT;
+ mask = IN_CLASSB_NET;
+ } else if (net < 16777216L) {
+ addr = net << IN_CLASSC_NSHIFT;
+ mask = IN_CLASSC_NET;
+ } else {
+ addr = net;
+ if ((addr & IN_CLASSA_HOST) == 0)
+ mask = IN_CLASSA_NET;
+ else if ((addr & IN_CLASSB_HOST) == 0)
+ mask = IN_CLASSB_NET;
+ else if ((addr & IN_CLASSC_HOST) == 0)
+ mask = IN_CLASSC_NET;
+ else {
+ if (IN_CLASSA(addr))
+ mask = IN_CLASSA_NET;
+ else if (IN_CLASSB(addr))
+ mask = IN_CLASSB_NET;
+ else if (IN_CLASSC(addr))
+ mask = IN_CLASSC_NET;
+ else
+ mask = IP_HOST_MASK;
+ mask = inet_makesubnetmask(addr, mask);
+ }
+ }
+ sin->sin_addr.s_addr = htonl(addr);
+
+ if (!(rtm_addrs & RTA_NETMASK)) {
+ rtm_addrs |= RTA_NETMASK;
+ sin = &so_mask.sin;
+ sin->sin_addr.s_addr = htonl(mask);
+ sin->sin_family = AF_INET;
+ cp = (char *)(&sin->sin_addr + 1);
+ while (*--cp == 0 && cp > (char *)sin)
+ ;
+ }
+}
+
+static in_addr_t
+inet_makesubnetmask(in_addr_t addr, in_addr_t mask)
+{
+ int n;
+ struct ifconf ifc;
+ struct ifreq ifreq;
+ struct ifreq *ifr;
+ struct sockaddr_in *sin;
+ char *buf;
+ int numifs;
+ size_t bufsize;
+ int iosoc;
+ in_addr_t if_addr, if_mask;
+ in_addr_t if_subnetmask = 0;
+ short if_flags;
+
+ if (mask == 0)
+ return (0);
+ if ((iosoc = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
+ quit("socket", errno);
+ if (ioctl(iosoc, SIOCGIFNUM, (char *)&numifs) < 0)
+ quit("ioctl", errno);
+ bufsize = numifs * sizeof (struct ifreq);
+ buf = malloc(bufsize);
+ if (buf == NULL)
+ quit("malloc", errno);
+ (void) memset(&ifc, 0, sizeof (ifc));
+ ifc.ifc_len = bufsize;
+ ifc.ifc_buf = buf;
+ if (ioctl(iosoc, SIOCGIFCONF, (char *)&ifc) < 0)
+ quit("ioctl (get interface configuration)", errno);
+ /* Let's check to see if this is maybe a local subnet route. */
+ ifr = ifc.ifc_req;
+ for (n = ifc.ifc_len / sizeof (struct ifreq); n > 0; n--, ifr++) {
+ ifreq = *ifr;
+ /* LINTED */
+ sin = (struct sockaddr_in *)&ifr->ifr_addr;
+ if_addr = ntohl(sin->sin_addr.s_addr);
+
+ if (ioctl(iosoc, SIOCGIFFLAGS, (char *)&ifreq) < 0)
+ quit("ioctl (get interface flags)", errno);
+ if ((ifreq.ifr_flags & IFF_UP) == 0)
+ continue;
+ if_flags = ifreq.ifr_flags;
+
+ if (ioctl(iosoc, SIOCGIFNETMASK, (char *)&ifreq) < 0)
+ quit("ioctl (get netmask)", errno);
+ /* LINTED */
+ sin = (struct sockaddr_in *)&ifreq.ifr_addr;
+ if_mask = ntohl(sin->sin_addr.s_addr);
+ if ((if_addr & mask) == (addr & mask)) {
+ /*
+ * Don't trust pt-pt interfaces if there are
+ * other interfaces.
+ */
+ if (if_flags & IFF_POINTOPOINT) {
+ if_subnetmask = if_mask;
+ continue;
+ }
+ /*
+ * Fine. Just assume the same net mask as the
+ * directly attached subnet interface is using.
+ */
+ return (if_mask);
+ }
+ }
+ if (if_subnetmask != 0)
+ return (if_subnetmask);
+ return (mask);
+}
+
+/*
+ * Interpret an argument as a network address of some kind,
+ * returning B_TRUE if a host address, B_FALSE if a network address.
+ *
+ * If the address family is one looked up in getaddr() using one of the
+ * getipnodebyX() functions (currently only AF_INET6), then callers should
+ * freehostent() the returned "struct hostent" pointer if one was passed in.
+ */
+static boolean_t
+getaddr(int which, char *s, struct hostent **hpp)
+{
+ sup su;
+ struct hostent *hp;
+ boolean_t ret;
+
+ if (s == NULL) {
+ (void) fprintf(stderr,
+ gettext("route: argument required following keyword\n"));
+ usage((char *)NULL);
+ }
+ if (hpp == NULL)
+ hpp = &hp;
+ *hpp = NULL;
+ rtm_addrs |= which;
+ switch (which) {
+ case RTA_DST:
+ su = &so_dst;
+ su->sa.sa_family = af;
+ break;
+ case RTA_GATEWAY:
+ su = &so_gate;
+ su->sa.sa_family = af;
+ break;
+ case RTA_NETMASK:
+ su = &so_mask;
+ su->sa.sa_family = af;
+ break;
+ case RTA_IFP:
+ so_ifp.sdl.sdl_index = if_nametoindex(s);
+ if (so_ifp.sdl.sdl_index == 0) {
+ if (errno == ENXIO) {
+ (void) fprintf(stderr,
+ gettext("route: %s: no such interface\n"),
+ s);
+ exit(1);
+ } else {
+ quit("if_nametoindex", errno);
+ }
+ }
+ so_ifp.sdl.sdl_family = AF_LINK;
+ return (B_FALSE);
+ /*
+ * RTA_SRC has overloaded meaning. It can represent the
+ * src address of incoming or outgoing packets.
+ */
+ case RTA_IFA:
+ su = &so_ifa;
+ su->sa.sa_family = af;
+ break;
+ case RTA_SRC:
+ su = &so_src;
+ su->sa.sa_family = af;
+ break;
+ default:
+ /* NOTREACHED */
+ quit(gettext("Internal Error"), EINVAL);
+ /* NOTREACHED */
+ }
+ if (strcmp(s, "default") == 0) {
+ if (which == RTA_DST) {
+ forcenet = B_TRUE;
+ (void) getaddr(RTA_NETMASK, s, NULL);
+ }
+ if (which == RTA_SRC) {
+ return (B_TRUE);
+ }
+ return (B_FALSE);
+ }
+ switch (af) {
+ case AF_LINK:
+ link_addr(s, &su->sdl);
+ return (B_TRUE);
+ case PF_ROUTE:
+ sockaddr(s, &su->sa);
+ return (B_TRUE);
+ case AF_INET6:
+ switch (which) {
+ case RTA_DST:
+ if (s[0] == '/') {
+ (void) fprintf(stderr,
+ gettext("route: %s: unexpected '/'\n"), s);
+ exit(1);
+ }
+ ret = in6_getaddr(s, &su->sin6, &masklen, hpp);
+ switch (masklen) {
+ case NO_PREFIX:
+ /* Nothing there - ok */
+ return (ret);
+ case BAD_ADDR:
+ (void) fprintf(stderr,
+ gettext("route: bad prefix length in %s\n"),
+ s);
+ exit(1);
+ /* NOTREACHED */
+ default:
+ (void) memset(&so_mask.sin6.sin6_addr, 0,
+ sizeof (so_mask.sin6.sin6_addr));
+ if (!in_prefixlentomask(masklen, IPV6_ABITS,
+ (uchar_t *)&so_mask.sin6.sin6_addr)) {
+ (void) fprintf(stderr,
+ gettext("route: bad prefix length: "
+ "%d\n"), masklen);
+ exit(1);
+ }
+ break;
+ }
+ so_mask.sin6.sin6_family = af;
+ rtm_addrs |= RTA_NETMASK;
+ return (ret);
+ case RTA_GATEWAY:
+ case RTA_IFA:
+ case RTA_SRC:
+ ret = in6_getaddr(s, &su->sin6, NULL, hpp);
+ return (ret);
+ case RTA_NETMASK:
+ (void) fprintf(stderr,
+ gettext("route: -netmask not supported for IPv6: "
+ "use <prefix>/<prefix-length> instead"));
+ exit(1);
+ /* NOTREACHED */
+ default:
+ quit(gettext("Internal Error"), EINVAL);
+ /* NOTREACHED */
+ }
+ case AF_INET:
+ switch (which) {
+ case RTA_DST:
+ if (s[0] == '/') {
+ (void) fprintf(stderr,
+ gettext("route: %s: unexpected '/'\n"), s);
+ exit(1);
+ }
+ ret = in_getaddr(s, &su->sin, &masklen, which, hpp);
+ switch (masklen) {
+ case NO_PREFIX:
+ /* Nothing there - ok */
+ return (ret);
+ case BAD_ADDR:
+ (void) fprintf(stderr,
+ gettext("route: bad prefix length in %s\n"),
+ s);
+ exit(1);
+ /* NOTREACHED */
+ default:
+ (void) memset(&so_mask.sin.sin_addr, 0,
+ sizeof (so_mask.sin.sin_addr));
+ if (!in_prefixlentomask(masklen, IP_ABITS,
+ (uchar_t *)&so_mask.sin.sin_addr)) {
+ (void) fprintf(stderr,
+ gettext("route: bad prefix length: "
+ "%d\n"), masklen);
+ exit(1);
+ }
+ break;
+ }
+ so_mask.sin.sin_family = af;
+ rtm_addrs |= RTA_NETMASK;
+ return (ret);
+ case RTA_GATEWAY:
+ case RTA_IFA:
+ case RTA_NETMASK:
+ case RTA_SRC:
+ ret = in_getaddr(s, &su->sin, NULL, which, hpp);
+ return (ret);
+ default:
+ quit(gettext("Internal Error"), EINVAL);
+ /* NOTREACHED */
+ }
+ default:
+ quit(gettext("Internal Error"), EINVAL);
+ /* NOTREACHED */
+ }
+
+}
+
+/*
+ * Interpret an argument as an IPv4 network address of some kind,
+ * returning B_TRUE if a host address, B_FALSE if a network address.
+ * Note that this *always* tries host interpretation before network
+ * interpretation.
+ *
+ * If the plenp argument is non-NULL, allow <addr>/<n> syntax and
+ * pass out <n> in *plenp.
+ * If <n> doesn't parse return BAD_ADDR as *plenp.
+ * If no /<n> is present return NO_PREFIX as *plenp.
+ */
+static boolean_t
+in_getaddr(char *s, struct sockaddr_in *sin, int *plenp, int which,
+ struct hostent **hpp)
+{
+ struct hostent *hp;
+ struct netent *np;
+ in_addr_t val;
+ char str[BUFSIZ];
+
+ (void) strncpy(str, s, sizeof (str));
+
+ /*
+ * Look for '/'<n> is plenp
+ */
+ if (plenp != NULL) {
+ char *cp;
+
+ *plenp = in_getprefixlen(str, IP_ABITS);
+ if (*plenp == BAD_ADDR)
+ return (B_FALSE);
+ cp = strchr(str, '/');
+ if (cp != NULL)
+ *cp = '\0';
+ } else if (strchr(str, '/') != NULL) {
+ (void) fprintf(stderr, gettext("route: %s: unexpected '/'\n"),
+ str);
+ exit(1);
+ }
+
+ (void) memset(sin, 0, sizeof (*sin));
+ sin->sin_family = AF_INET;
+
+ /*
+ * Note: only the route destination can be a network, so we treat
+ * all other addresses as though "-net" was not specified.
+ */
+ if ((((val = inet_addr(str)) != (in_addr_t)-1) ||
+ strcmp(str, "255.255.255.255") == 0) &&
+ (which != RTA_DST || !forcenet)) {
+ sin->sin_addr.s_addr = val;
+ if (inet_lnaof(sin->sin_addr) != INADDR_ANY ||
+ forcehost)
+ return (B_TRUE);
+ val = ntohl(val);
+ if (which == RTA_DST)
+ inet_makenetandmask(val, sin);
+ return (B_FALSE);
+ }
+ if (!forcehost && (val = inet_network(str)) != (in_addr_t)-1) {
+ if (which == RTA_DST)
+ inet_makenetandmask(val, sin);
+ return (B_FALSE);
+ }
+ if ((which != RTA_DST || !forcenet) &&
+ (hp = gethostbyname(str)) != NULL) {
+ *hpp = hp;
+ (void) memmove(&sin->sin_addr, hp->h_addr,
+ hp->h_length);
+ return (B_TRUE);
+ }
+ if (!forcehost && (np = getnetbyname(str)) != NULL &&
+ (val = np->n_net) != 0) {
+ if (which == RTA_DST)
+ inet_makenetandmask(val, sin);
+ return (B_FALSE);
+ }
+ (void) fprintf(stderr, gettext("%s: bad value\n"), s);
+ exit(1);
+ /* NOTREACHED */
+}
+
+/*
+ * Interpret an argument as an IPv6 network address of some kind,
+ * returning B_TRUE if a host address, B_FALSE if a network address.
+ *
+ * If the last argument is non-NULL allow a <addr>/<n> syntax and
+ * pass out <n> in *plenp.
+ * If <n> doesn't parse return BAD_ADDR as *plenp.
+ * If no /<n> is present return NO_PREFIX as *plenp.
+ */
+static boolean_t
+in6_getaddr(char *s, struct sockaddr_in6 *sin6, int *plenp,
+ struct hostent **hpp)
+{
+ struct hostent *hp;
+ char str[BUFSIZ];
+ int error_num;
+
+ (void) strncpy(str, s, sizeof (str));
+
+ /*
+ * Look for '/'<n> is plenp
+ */
+ if (plenp != NULL) {
+ char *cp;
+
+ *plenp = in_getprefixlen(str, IPV6_ABITS);
+ if (*plenp == BAD_ADDR)
+ return (B_FALSE);
+ cp = strchr(str, '/');
+ if (cp != NULL)
+ *cp = '\0';
+ } else if (strchr(str, '/') != NULL) {
+ (void) fprintf(stderr, gettext("route: %s: unexpected '/'\n"),
+ str);
+ exit(1);
+ }
+
+ (void) memset(sin6, 0, sizeof (struct sockaddr_in6));
+ sin6->sin6_family = AF_INET6;
+
+ hp = getipnodebyname(str, AF_INET6, 0, &error_num);
+ if (hp != NULL) {
+ *hpp = hp;
+ (void) memmove(&sin6->sin6_addr, hp->h_addr, hp->h_length);
+ return (B_TRUE);
+ }
+ if (error_num == TRY_AGAIN) {
+ (void) fprintf(stderr, gettext("route: %s: bad address (try "
+ "again later)\n"), s);
+ } else {
+ (void) fprintf(stderr, gettext("route: %s: bad address\n"), s);
+ }
+ exit(1);
+ /* NOTREACHED */
+}
+
+/*
+ * If "slash" is zero this parses the whole string as
+ * an integer. With "slash" non zero it parses the tail part as an integer.
+ *
+ * If it is not a valid integer this returns BAD_ADDR.
+ * If there is /<n> present this returns NO_PREFIX.
+ */
+int
+in_getprefixlen(char *addr, int max_plen)
+{
+ int prefixlen;
+ char *str, *end;
+
+ str = strchr(addr, '/');
+ if (str == NULL)
+ return (NO_PREFIX);
+ str++;
+
+ prefixlen = strtol(str, &end, 10);
+ if (prefixlen < 0)
+ return (BAD_ADDR);
+ if (str == end)
+ return (BAD_ADDR);
+ if (max_plen != 0 && max_plen < prefixlen)
+ return (BAD_ADDR);
+ else
+ return (prefixlen);
+}
+
+/*
+ * Convert a prefix length to a mask.
+ * Returns B_TRUE if ok. B_FALSE otherwise.
+ * Assumes the mask array is zeroed by the caller.
+ */
+boolean_t
+in_prefixlentomask(int prefixlen, int maxlen, uchar_t *mask)
+{
+ if (prefixlen < 0 || prefixlen > maxlen)
+ return (B_FALSE);
+
+ while (prefixlen > 0) {
+ if (prefixlen >= 8) {
+ *mask++ = 0xFF;
+ prefixlen -= 8;
+ continue;
+ }
+ *mask |= 1 << (8 - prefixlen);
+ prefixlen--;
+ }
+ return (B_TRUE);
+}
+
+void
+rtmonitor(int argc, char *argv[])
+{
+ int n;
+ intmax_t msg[2048 / sizeof (intmax_t)];
+
+ if (tflag)
+ exit(0);
+ verbose = B_TRUE;
+ if (argc > 1) {
+ argv++;
+ if (argc == 2 && **argv == '-') {
+ switch (keyword(*argv + 1)) {
+ case K_INET:
+ af = AF_INET;
+ break;
+ case K_LINK:
+ af = AF_LINK;
+ break;
+ case K_INET6:
+ af = AF_INET6;
+ break;
+ default:
+ usage(*argv);
+ /* NOTREACHED */
+ }
+ } else {
+ usage(*argv);
+ }
+ (void) close(s);
+ s = socket(PF_ROUTE, SOCK_RAW, af);
+ if (s < 0)
+ quit("socket", errno);
+ }
+ for (;;) {
+ n = read(s, msg, sizeof (msg));
+ if (n <= 0)
+ quit("read", errno);
+ (void) printf("got message of size %d\n", n);
+ print_rtmsg((struct rt_msghdr *)msg, n);
+ }
+}
+
+int
+rtmsg(int cmd, int flags)
+{
+ static int seq;
+ int rlen;
+ char *cp = m_rtmsg.m_space;
+ int l;
+
+ errno = 0;
+ (void) memset(&m_rtmsg, 0, sizeof (m_rtmsg));
+ if (cmd == 'a') {
+ cmd = RTM_ADD;
+ } else if (cmd == 'c') {
+ cmd = RTM_CHANGE;
+ } else if (cmd == 'g') {
+ cmd = RTM_GET;
+ if (so_ifp.sa.sa_family == 0) {
+ so_ifp.sa.sa_family = AF_LINK;
+ rtm_addrs |= RTA_IFP;
+ }
+ } else {
+ cmd = RTM_DELETE;
+ }
+#define rtm m_rtmsg.m_rtm
+ rtm.rtm_type = cmd;
+ rtm.rtm_flags = flags;
+ rtm.rtm_version = RTM_VERSION;
+ rtm.rtm_seq = ++seq;
+ rtm.rtm_addrs = rtm_addrs;
+ rtm.rtm_rmx = rt_metrics;
+ rtm.rtm_inits = rtm_inits;
+
+#define NEXTADDR(w, u) \
+ if (rtm_addrs & (w)) { \
+ l = ROUNDUP_LONG(salen(&u.sa)); \
+ (void) memmove(cp, &(u), l); \
+ cp += l; \
+ if (verbose) \
+ sodump(&(u), #u); \
+ }
+ NEXTADDR(RTA_DST, so_dst);
+ NEXTADDR(RTA_GATEWAY, so_gate);
+ NEXTADDR(RTA_NETMASK, so_mask);
+ NEXTADDR(RTA_IFP, so_ifp);
+ NEXTADDR(RTA_IFA, so_ifa);
+ /*
+ * RTA_SRC has overloaded meaning. It can represent the
+ * src address of incoming or outgoing packets.
+ */
+ NEXTADDR(RTA_SRC, so_src);
+#undef NEXTADDR
+ rtm.rtm_msglen = l = cp - (char *)&m_rtmsg;
+ if (verbose)
+ print_rtmsg(&rtm, l);
+ if (debugonly)
+ return (0);
+ if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) {
+ switch (errno) {
+ case ESRCH:
+ case EBUSY:
+ case ENOBUFS:
+ case EEXIST:
+ case ENETUNREACH:
+ case EHOSTUNREACH:
+ case EPERM:
+ break;
+ default:
+ perror(gettext("writing to routing socket"));
+ break;
+ }
+ return (-1);
+ } else if (rlen < (int)rtm.rtm_msglen) {
+ (void) fprintf(stderr,
+ gettext("route: write to routing socket got only %d for "
+ "len\n"), rlen);
+ return (-1);
+ }
+ if (cmd == RTM_GET) {
+ do {
+ l = read(s, (char *)&m_rtmsg, sizeof (m_rtmsg));
+ } while (l > 0 && (rtm.rtm_seq != seq || rtm.rtm_pid != pid));
+ if (l < 0) {
+ (void) fprintf(stderr,
+ gettext("route: read from routing socket: %s\n"),
+ strerror(errno));
+ } else {
+ print_getmsg(&rtm, l);
+ }
+ }
+#undef rtm
+ return (0);
+}
+
+static char *msgtypes[] = {
+ "",
+ "RTM_ADD: Add Route",
+ "RTM_DELETE: Delete Route",
+ "RTM_CHANGE: Change Metrics or flags",
+ "RTM_GET: Report Metrics",
+ "RTM_LOSING: Kernel Suspects Partitioning",
+ "RTM_REDIRECT: Told to use different route",
+ "RTM_MISS: Lookup failed on this address",
+ "RTM_LOCK: fix specified metrics",
+ "RTM_OLDADD: caused by SIOCADDRT",
+ "RTM_OLDDEL: caused by SIOCDELRT",
+ "RTM_RESOLVE: Route created by cloning",
+ "RTM_NEWADDR: address being added to iface",
+ "RTM_DELADDR: address being removed from iface",
+ "RTM_IFINFO: iface status change",
+ 0,
+};
+
+#define NMSGTYPES (sizeof (msgtypes) / sizeof (msgtypes[0]))
+
+static char metricnames[] =
+"\011pksent\010rttvar\7rtt\6ssthresh\5sendpipe\4recvpipe\3expire\2hopcount"
+ "\1mtu";
+static char routeflags[] =
+"\1UP\2GATEWAY\3HOST\4REJECT\5DYNAMIC\6MODIFIED\7DONE\010MASK_PRESENT"
+ "\011CLONING\012XRESOLVE\013LLINFO\014STATIC\015BLACKHOLE"
+ "\016PRIVATE\017PROTO2\020PROTO1\021MULTIRT\022SETSRC";
+static char ifnetflags[] =
+"\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5PTP\6NOTRAILERS\7RUNNING\010NOARP"
+ "\011PPROMISC\012ALLMULTI\013INTELLIGENT\014MULTICAST"
+ "\015MULTI_BCAST\016UNNUMBERED\017DHCP\020PRIVATE"
+ "\021NOXMIT\022NOLOCAL\023DEPRECATED\024ADDRCONF"
+ "\025ROUTER\026NONUD\027ANYCAST\030NORTEXCH\031IPv4\032IPv6"
+ "\033MIP\034NOFAILOVER\035FAILED\036STANDBY\037INACTIVE\040OFFLINE"
+ "\041XRESOLV\042COS\043PREFERRED\044TEMPORARY";
+static char addrnames[] =
+"\1DST\2GATEWAY\3NETMASK\4GENMASK\5IFP\6IFA\7AUTHOR\010BRD\011SRC";
+
+void
+print_rtmsg(struct rt_msghdr *rtm, int msglen)
+{
+ struct if_msghdr *ifm;
+ struct ifa_msghdr *ifam;
+
+ if (!verbose)
+ return;
+ if (rtm->rtm_version != RTM_VERSION) {
+ (void) printf("routing message version %d not understood\n",
+ rtm->rtm_version);
+ return;
+ }
+ if (rtm->rtm_msglen > (ushort_t)msglen) {
+ (void) printf("message length mismatch, in packet %d, "
+ "returned %d\n",
+ rtm->rtm_msglen, msglen);
+ }
+ /*
+ * Since rtm->rtm_type is unsigned, we'll just check the case of zero
+ * and the upper-bound of (NMSGTYPES - 1).
+ */
+ if (rtm->rtm_type == 0 || rtm->rtm_type >= (NMSGTYPES - 1)) {
+ (void) printf("routing message type %d not understood\n",
+ rtm->rtm_type);
+ return;
+ }
+ (void) printf("%s: len %d, ", msgtypes[rtm->rtm_type], rtm->rtm_msglen);
+ switch (rtm->rtm_type) {
+ case RTM_IFINFO:
+ ifm = (struct if_msghdr *)rtm;
+ (void) printf("if# %d, flags:", ifm->ifm_index);
+ bprintf(stdout, ifm->ifm_flags, ifnetflags);
+ pmsg_addrs((char *)(ifm + 1), ifm->ifm_addrs);
+ break;
+ case RTM_NEWADDR:
+ case RTM_DELADDR:
+ ifam = (struct ifa_msghdr *)rtm;
+ (void) printf("metric %d, flags:", ifam->ifam_metric);
+ bprintf(stdout, ifam->ifam_flags, routeflags);
+ pmsg_addrs((char *)(ifam + 1), ifam->ifam_addrs);
+ break;
+ default:
+ (void) printf("pid: %ld, seq %d, errno %d, flags:",
+ rtm->rtm_pid, rtm->rtm_seq, rtm->rtm_errno);
+ bprintf(stdout, rtm->rtm_flags, routeflags);
+ pmsg_common(rtm);
+ }
+}
+
+void
+print_getmsg(struct rt_msghdr *rtm, int msglen)
+{
+ struct sockaddr *dst = NULL, *gate = NULL, *mask = NULL, *src = NULL;
+ struct sockaddr_dl *ifp = NULL;
+ struct sockaddr *sa;
+ char *cp;
+ int i;
+
+ (void) printf(" route to: %s\n", routename(&so_dst.sa));
+ if (rtm->rtm_version != RTM_VERSION) {
+ (void) fprintf(stderr,
+ gettext("routing message version %d not understood\n"),
+ rtm->rtm_version);
+ return;
+ }
+ if (rtm->rtm_msglen > (ushort_t)msglen) {
+ (void) fprintf(stderr,
+ gettext("message length mismatch, in packet %d, "
+ "returned %d\n"), rtm->rtm_msglen, msglen);
+ }
+ if (rtm->rtm_errno) {
+ (void) fprintf(stderr, "RTM_GET: %s (errno %d)\n",
+ strerror(rtm->rtm_errno), rtm->rtm_errno);
+ return;
+ }
+ cp = ((char *)(rtm + 1));
+ if (rtm->rtm_addrs != 0) {
+ for (i = 1; i != 0; i <<= 1) {
+ if (i & rtm->rtm_addrs) {
+ /* LINTED */
+ sa = (struct sockaddr *)cp;
+ switch (i) {
+ case RTA_DST:
+ dst = sa;
+ break;
+ case RTA_GATEWAY:
+ gate = sa;
+ break;
+ case RTA_NETMASK:
+ mask = sa;
+ break;
+ case RTA_IFP:
+ if (sa->sa_family == AF_LINK &&
+ ((struct sockaddr_dl *)sa)->
+ sdl_nlen != 0)
+ ifp = (struct sockaddr_dl *)sa;
+ break;
+ case RTA_SRC:
+ src = sa;
+ break;
+ }
+ ADVANCE(cp, sa);
+ }
+ }
+ }
+ if (dst != NULL && mask != NULL)
+ mask->sa_family = dst->sa_family; /* XXX */
+ if (dst != NULL)
+ (void) printf("destination: %s\n", routename(dst));
+ if (mask != NULL) {
+ boolean_t savenflag = nflag;
+
+ nflag = B_TRUE;
+ (void) printf(" mask: %s\n", routename(mask));
+ nflag = savenflag;
+ }
+ if (gate != NULL && rtm->rtm_flags & RTF_GATEWAY)
+ (void) printf(" gateway: %s\n", routename(gate));
+ if (src != NULL && rtm->rtm_flags & RTF_SETSRC)
+ (void) printf(" setsrc: %s\n", routename(src));
+ if (ifp != NULL) {
+ if (verbose) {
+ int i;
+
+ (void) printf(" interface: %.*s index %d address ",
+ ifp->sdl_nlen, ifp->sdl_data, ifp->sdl_index);
+ for (i = ifp->sdl_nlen;
+ i < ifp->sdl_nlen + ifp->sdl_alen;
+ i++) {
+ (void) printf("%02x ",
+ ifp->sdl_data[i] & 0xFF);
+ }
+ (void) printf("\n");
+ } else {
+ (void) printf(" interface: %.*s\n",
+ ifp->sdl_nlen, ifp->sdl_data);
+ }
+ }
+ (void) printf(" flags: ");
+ bprintf(stdout, rtm->rtm_flags, routeflags);
+
+#define lock(f) ((rtm->rtm_rmx.rmx_locks & RTV_ ## f) ? 'L' : ' ')
+#define msec(u) (((u) + 500) / 1000) /* usec to msec */
+
+ (void) printf("\n%s\n", " recvpipe sendpipe ssthresh rtt,ms "
+ "rttvar,ms hopcount mtu expire");
+ (void) printf("%8d%c ", rtm->rtm_rmx.rmx_recvpipe, lock(RPIPE));
+ (void) printf("%8d%c ", rtm->rtm_rmx.rmx_sendpipe, lock(SPIPE));
+ (void) printf("%8d%c ", rtm->rtm_rmx.rmx_ssthresh, lock(SSTHRESH));
+ (void) printf("%8d%c ", msec(rtm->rtm_rmx.rmx_rtt), lock(RTT));
+ (void) printf("%8d%c ", msec(rtm->rtm_rmx.rmx_rttvar), lock(RTTVAR));
+ (void) printf("%8d%c ", rtm->rtm_rmx.rmx_hopcount, lock(HOPCOUNT));
+ (void) printf("%8d%c ", rtm->rtm_rmx.rmx_mtu, lock(MTU));
+ if (rtm->rtm_rmx.rmx_expire)
+ rtm->rtm_rmx.rmx_expire -= time(0);
+ (void) printf("%8d%c\n", rtm->rtm_rmx.rmx_expire, lock(EXPIRE));
+#undef lock
+#undef msec
+#define RTA_IGN \
+ (RTA_DST|RTA_GATEWAY|RTA_NETMASK|RTA_IFP|RTA_IFA|RTA_BRD|RTA_SRC)
+ if (verbose) {
+ pmsg_common(rtm);
+ } else if (rtm->rtm_addrs &~ RTA_IGN) {
+ (void) printf("sockaddrs: ");
+ bprintf(stdout, rtm->rtm_addrs, addrnames);
+ (void) putchar('\n');
+ }
+#undef RTA_IGN
+}
+
+void
+pmsg_common(struct rt_msghdr *rtm)
+{
+ (void) printf("\nlocks: ");
+ bprintf(stdout, (int)rtm->rtm_rmx.rmx_locks, metricnames);
+ (void) printf(" inits: ");
+ bprintf(stdout, (int)rtm->rtm_inits, metricnames);
+ pmsg_addrs(((char *)(rtm + 1)), rtm->rtm_addrs);
+}
+
+void
+pmsg_addrs(char *cp, int addrs)
+{
+ struct sockaddr *sa;
+ int i;
+
+ if (addrs == 0)
+ return;
+ (void) printf("\nsockaddrs: ");
+ bprintf(stdout, addrs, addrnames);
+ (void) putchar('\n');
+ for (i = 1; i != 0; i <<= 1) {
+ if (i & addrs) {
+ /* LINTED */
+ sa = (struct sockaddr *)cp;
+ (void) printf(" %s", routename(sa));
+ ADVANCE(cp, sa);
+ }
+ }
+ (void) putchar('\n');
+ (void) fflush(stdout);
+}
+
+void
+bprintf(FILE *fp, int b, char *s)
+{
+ int i;
+ boolean_t gotsome = B_FALSE;
+
+ if (b == 0)
+ return;
+ while ((i = *s++) != 0) {
+ if (b & (1 << (i - 1))) {
+ if (!gotsome)
+ i = '<';
+ else
+ i = ',';
+ (void) putc(i, fp);
+ gotsome = B_TRUE;
+ for (; (i = *s) > ' '; s++)
+ (void) putc(i, fp);
+ } else {
+ while (*s > ' ')
+ s++;
+ }
+ }
+ if (gotsome)
+ (void) putc('>', fp);
+}
+
+int
+keyword(char *cp)
+{
+ struct keytab *kt = keywords;
+
+ while (kt->kt_cp && strcmp(kt->kt_cp, cp))
+ kt++;
+ return (kt->kt_i);
+}
+
+void
+sodump(sup su, char *which)
+{
+ static char obuf[INET6_ADDRSTRLEN];
+
+ switch (su->sa.sa_family) {
+ case AF_LINK:
+ (void) printf("%s: link %s; ",
+ which, link_ntoa(&su->sdl));
+ break;
+ case AF_INET:
+ (void) printf("%s: inet %s; ",
+ which, inet_ntoa(su->sin.sin_addr));
+ break;
+ case AF_INET6:
+ if (inet_ntop(AF_INET6, (void *)&su->sin6.sin6_addr, obuf,
+ INET6_ADDRSTRLEN) != NULL) {
+ (void) printf("%s: inet6 %s; ", which, obuf);
+ break;
+ }
+ /* FALLTHROUGH */
+ default:
+ quit(gettext("Internal Error"), EINVAL);
+ /* NOTREACHED */
+ }
+ (void) fflush(stdout);
+}
+
+/* States */
+#define VIRGIN 0
+#define GOTONE 1
+#define GOTTWO 2
+#define RESET 3
+/* Inputs */
+#define DIGIT (4*0)
+#define END (4*1)
+#define DELIM (4*2)
+#define LETTER (4*3)
+
+void
+sockaddr(char *addr, struct sockaddr *sa)
+{
+ char *cp = (char *)sa;
+ int size = salen(sa);
+ char *cplim = cp + size;
+ int byte = 0, state = VIRGIN, new;
+
+ (void) memset(cp, 0, size);
+ cp++;
+ do {
+ if ((*addr >= '0') && (*addr <= '9')) {
+ new = *addr - '0';
+ } else if ((*addr >= 'a') && (*addr <= 'f')) {
+ new = *addr - 'a' + 10;
+ } else if ((*addr >= 'A') && (*addr <= 'F')) {
+ new = *addr - 'A' + 10;
+ } else if (*addr == 0) {
+ state |= END;
+ } else {
+ state |= DELIM;
+ }
+ addr++;
+ switch (state /* | INPUT */) {
+ case GOTTWO | DIGIT:
+ *cp++ = byte;
+ /* FALLTHROUGH */
+ case VIRGIN | DIGIT:
+ state = GOTONE; byte = new; continue;
+ case GOTONE | DIGIT:
+ state = GOTTWO; byte = new + (byte << 4); continue;
+ default: /* | DELIM */
+ state = VIRGIN; *cp++ = byte; byte = 0; continue;
+ case GOTONE | END:
+ case GOTTWO | END:
+ *cp++ = byte;
+ /* FALLTHROUGH */
+ case VIRGIN | END:
+ break;
+ }
+ break;
+ } while (cp < cplim);
+}
+
+int
+salen(struct sockaddr *sa)
+{
+ switch (sa->sa_family) {
+ case AF_INET:
+ return (sizeof (struct sockaddr_in));
+ case AF_LINK:
+ return (sizeof (struct sockaddr_dl));
+ case AF_INET6:
+ return (sizeof (struct sockaddr_in6));
+ default:
+ return (sizeof (struct sockaddr));
+ }
+}
+
+void
+link_addr(const char *addr, struct sockaddr_dl *sdl)
+{
+ char *cp = sdl->sdl_data;
+ char *cplim = sizeof (struct sockaddr_dl) + (char *)sdl;
+ int byte = 0, state = VIRGIN, new;
+
+ (void) memset(sdl, 0, sizeof (struct sockaddr_dl));
+ sdl->sdl_family = AF_LINK;
+ do {
+ state &= ~LETTER;
+ if ((*addr >= '0') && (*addr <= '9')) {
+ new = *addr - '0';
+ } else if ((*addr >= 'a') && (*addr <= 'f')) {
+ new = *addr - 'a' + 10;
+ } else if ((*addr >= 'A') && (*addr <= 'F')) {
+ new = *addr - 'A' + 10;
+ } else if (*addr == 0) {
+ state |= END;
+ } else if (state == VIRGIN &&
+ (((*addr >= 'A') && (*addr <= 'Z')) ||
+ ((*addr >= 'a') && (*addr <= 'z')))) {
+ state |= LETTER;
+ } else {
+ state |= DELIM;
+ }
+ addr++;
+ switch (state /* | INPUT */) {
+ case VIRGIN | DIGIT:
+ case VIRGIN | LETTER:
+ *cp++ = addr[-1];
+ continue;
+ case VIRGIN | DELIM:
+ state = RESET;
+ sdl->sdl_nlen = cp - sdl->sdl_data;
+ continue;
+ case GOTTWO | DIGIT:
+ *cp++ = byte;
+ /* FALLTHROUGH */
+ case RESET | DIGIT:
+ state = GOTONE;
+ byte = new;
+ continue;
+ case GOTONE | DIGIT:
+ state = GOTTWO;
+ byte = new + (byte << 4);
+ continue;
+ default: /* | DELIM */
+ state = RESET;
+ *cp++ = byte;
+ byte = 0;
+ continue;
+ case GOTONE | END:
+ case GOTTWO | END:
+ *cp++ = byte;
+ /* FALLTHROUGH */
+ case RESET | END:
+ break;
+ }
+ break;
+ } while (cp < cplim);
+ sdl->sdl_alen = cp - LLADDR(sdl);
+}
+
+static char hexlist[] = "0123456789abcdef";
+
+char *
+link_ntoa(const struct sockaddr_dl *sdl)
+{
+ static char obuf[64];
+ char *out = obuf;
+ int i;
+ uchar_t *in = (uchar_t *)LLADDR(sdl);
+ uchar_t *inlim = in + sdl->sdl_alen;
+ boolean_t firsttime = B_TRUE;
+
+ if (sdl->sdl_nlen) {
+ (void) memcpy(obuf, sdl->sdl_data, sdl->sdl_nlen);
+ out += sdl->sdl_nlen;
+ if (sdl->sdl_alen)
+ *out++ = ':';
+ }
+ while (in < inlim) {
+ if (firsttime)
+ firsttime = B_FALSE;
+ else
+ *out++ = '.';
+ i = *in++;
+ if (i > 0xf) {
+ out[1] = hexlist[i & 0xf];
+ i >>= 4;
+ out[0] = hexlist[i];
+ out += 2;
+ } else {
+ *out++ = hexlist[i];
+ }
+ }
+ *out = 0;
+ return (obuf);
+}
+
+static mib_item_t *
+mibget(int sd)
+{
+ intmax_t buf[512 / sizeof (intmax_t)];
+ int flags;
+ int i, j, getcode;
+ struct strbuf ctlbuf, databuf;
+ struct T_optmgmt_req *tor = (struct T_optmgmt_req *)buf;
+ struct T_optmgmt_ack *toa = (struct T_optmgmt_ack *)buf;
+ struct T_error_ack *tea = (struct T_error_ack *)buf;
+ struct opthdr *req;
+ mib_item_t *first_item = NULL;
+ mib_item_t *last_item = NULL;
+ mib_item_t *temp;
+
+ tor->PRIM_type = T_SVR4_OPTMGMT_REQ;
+ tor->OPT_offset = sizeof (struct T_optmgmt_req);
+ tor->OPT_length = sizeof (struct opthdr);
+ tor->MGMT_flags = T_CURRENT;
+ req = (struct opthdr *)&tor[1];
+ req->level = MIB2_IP; /* any MIB2_xxx value ok here */
+ req->name = 0;
+ req->len = 0;
+
+ ctlbuf.buf = (char *)buf;
+ ctlbuf.len = tor->OPT_length + tor->OPT_offset;
+ flags = 0;
+ if (putmsg(sd, &ctlbuf, NULL, flags) < 0) {
+ perror("mibget: putmsg (ctl)");
+ return (NULL);
+ }
+ /*
+ * each reply consists of a ctl part for one fixed structure
+ * or table, as defined in mib2.h. The format is a T_OPTMGMT_ACK,
+ * containing an opthdr structure. level/name identify the entry,
+ * len is the size of the data part of the message.
+ */
+ req = (struct opthdr *)&toa[1];
+ ctlbuf.maxlen = sizeof (buf);
+ for (j = 1; ; j++) {
+ flags = 0;
+ getcode = getmsg(sd, &ctlbuf, NULL, &flags);
+ if (getcode < 0) {
+ perror("mibget: getmsg (ctl)");
+ if (verbose) {
+ (void) fprintf(stderr,
+ "# level name len\n");
+ i = 0;
+ for (last_item = first_item; last_item != NULL;
+ last_item = last_item->next_item) {
+ (void) printf("%d %4ld %5ld %ld\n",
+ ++i, last_item->group,
+ last_item->mib_id,
+ last_item->length);
+ }
+ }
+ break;
+ }
+ if (getcode == 0 &&
+ ctlbuf.len >= sizeof (struct T_optmgmt_ack) &&
+ toa->PRIM_type == T_OPTMGMT_ACK &&
+ toa->MGMT_flags == T_SUCCESS &&
+ req->len == 0) {
+ if (verbose) {
+ (void) printf("mibget getmsg() %d returned EOD "
+ "(level %lu, name %lu)\n", j, req->level,
+ req->name);
+ }
+ return (first_item); /* this is EOD msg */
+ }
+
+ if (ctlbuf.len >= sizeof (struct T_error_ack) &&
+ tea->PRIM_type == T_ERROR_ACK) {
+ (void) fprintf(stderr, gettext("mibget %d gives "
+ "T_ERROR_ACK: TLI_error = 0x%lx, UNIX_error = "
+ "0x%lx\n"), j, tea->TLI_error, tea->UNIX_error);
+ errno = (tea->TLI_error == TSYSERR)
+ ? tea->UNIX_error : EPROTO;
+ break;
+ }
+
+ if (getcode != MOREDATA ||
+ ctlbuf.len < sizeof (struct T_optmgmt_ack) ||
+ toa->PRIM_type != T_OPTMGMT_ACK ||
+ toa->MGMT_flags != T_SUCCESS) {
+ (void) printf("mibget getmsg(ctl) %d returned %d, "
+ "ctlbuf.len = %d, PRIM_type = %ld\n",
+ j, getcode, ctlbuf.len, toa->PRIM_type);
+ if (toa->PRIM_type == T_OPTMGMT_ACK) {
+ (void) printf("T_OPTMGMT_ACK: "
+ "MGMT_flags = 0x%lx, req->len = %ld\n",
+ toa->MGMT_flags, req->len);
+ }
+ errno = ENOMSG;
+ break;
+ }
+
+ temp = malloc(sizeof (mib_item_t));
+ if (temp == NULL) {
+ perror("mibget: malloc");
+ break;
+ }
+ if (last_item != NULL)
+ last_item->next_item = temp;
+ else
+ first_item = temp;
+ last_item = temp;
+ last_item->next_item = NULL;
+ last_item->group = req->level;
+ last_item->mib_id = req->name;
+ last_item->length = req->len;
+ last_item->valp = malloc(req->len);
+ if (verbose) {
+ (void) printf("msg %d: group = %4ld mib_id = %5ld "
+ "length = %ld\n",
+ j, last_item->group, last_item->mib_id,
+ last_item->length);
+ }
+
+ databuf.maxlen = last_item->length;
+ databuf.buf = (char *)last_item->valp;
+ databuf.len = 0;
+ flags = 0;
+ getcode = getmsg(sd, NULL, &databuf, &flags);
+ if (getcode < 0) {
+ perror("mibget: getmsg (data)");
+ break;
+ } else if (getcode != 0) {
+ (void) printf("mibget getmsg(data) returned %d, "
+ "databuf.maxlen = %d, databuf.len = %d\n",
+ getcode, databuf.maxlen, databuf.len);
+ break;
+ }
+ }
+
+ /*
+ * On error, free all the allocated mib_item_t objects.
+ */
+ while (first_item != NULL) {
+ last_item = first_item;
+ first_item = first_item->next_item;
+ free(last_item);
+ }
+ return (NULL);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/routeadm.c b/usr/src/cmd/cmd-inet/usr.sbin/routeadm.c
new file mode 100644
index 0000000000..dd7783cf7f
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/routeadm.c
@@ -0,0 +1,1280 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <limits.h>
+#include <ctype.h>
+#include <stropts.h>
+#include <errno.h>
+#include <libintl.h>
+#include <locale.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <procfs.h>
+#include <inet/ip.h>
+#include <inet/nd.h>
+#include <net/if.h>
+
+static char *myname; /* copied from argv[0] */
+
+#define RA_CONF_FILE "/etc/inet/routing.conf"
+#define RA_MAX_CONF_LINE 256
+#define ND_IP_FORWARDING "ip_forwarding"
+#define ND_IP6_FORWARDING "ip6_forwarding"
+#define ND_IP6_SENDREDIR "ip6_send_redirects"
+#define ND_IP6_IGNREDIR "ip6_ignore_redirect"
+#define ND_ON_STR "\0" "1" "\0"
+#define ND_OFF_STR "\0" "0" "\0"
+#define OPT_STRBUFSIZE 1024
+#define RAD_ARGNUM 10
+
+#define IPV4_ROUTING_DAEMON_DEF "/usr/sbin/in.routed"
+#define IPV4_ROUTING_DAEMON_ARGS_DEF ""
+#define IPV4_ROUTING_STOP_CMD_DEF "kill -TERM `cat /var/tmp/in.routed.pid`"
+#define IPV6_ROUTING_DAEMON_DEF "/usr/lib/inet/in.ripngd"
+#define IPV6_ROUTING_DAEMON_ARGS_DEF "-s"
+#define IPV6_ROUTING_STOP_CMD_DEF "kill -TERM `cat /var/tmp/in.ripngd.pid`"
+#define NDPD_DAEMON_DEF "/usr/lib/inet/in.ndpd"
+#define NDPD_STOP_CMD_DEF "kill -TERM `cat /var/run/in.ndpd.pid`"
+
+#define IN_ROUTED_PID "/var/run/in.routed.pid"
+#define IN_RIPNGD_PID "/var/run/in.ripngd.pid"
+#define IN_NDPD_PID "/var/run/in.ndpd.pid"
+
+/*
+ * The rad_stop_cmd is exec-ed only if rad_pidfile is NULL, i.e.,
+ * default routing daemon has changed.
+ */
+typedef struct ra_daemon {
+ size_t rad_argvsize;
+ char **rad_argv;
+ char *rad_pidfile;
+ char *rad_stop_cmd;
+} ra_daemon_t;
+
+static ra_daemon_t v4d, v6d;
+static char *ndpd_args[] = { NDPD_DAEMON_DEF, NULL };
+static ra_daemon_t in_ndpd = { 2, ndpd_args, IN_NDPD_PID, NDPD_STOP_CMD_DEF };
+
+static char nd_ip_forw_on[] = ND_IP_FORWARDING ND_ON_STR;
+static char nd_ip_forw_off[] = ND_IP_FORWARDING ND_OFF_STR;
+static char nd_ip6_forw_on[] = ND_IP6_FORWARDING ND_ON_STR;
+static char nd_ip6_forw_off[] = ND_IP6_FORWARDING ND_OFF_STR;
+static char nd_ip6_sendredir_on[] = ND_IP6_SENDREDIR ND_ON_STR;
+static char nd_ip6_sendredir_off[] = ND_IP6_SENDREDIR ND_OFF_STR;
+static char nd_ip6_ignredir_on[] = ND_IP6_IGNREDIR ND_ON_STR;
+static char nd_ip6_ignredir_off[] = ND_IP6_IGNREDIR ND_OFF_STR;
+
+static int ipsock = -1;
+static boolean_t booting = B_FALSE; /* boot script defaults? */
+static boolean_t forwarding_only = B_FALSE;
+
+typedef enum option_values {
+ OPT_INVALID, OPT_ENABLED, OPT_DISABLED, OPT_DEFAULT, OPT_UNKNOWN
+} oval_t;
+
+#define OPT2STR(oval) \
+ (oval == OPT_ENABLED ? "enabled" : \
+ (oval == OPT_UNKNOWN ? "unknown" : \
+ (oval == OPT_DISABLED ? "disabled" : "default")))
+#define OPT2INTLSTR(oval) \
+ (oval == OPT_ENABLED ? gettext("enabled") : \
+ (oval == OPT_UNKNOWN ? gettext("unknown") : \
+ (oval == OPT_DISABLED ? gettext("disabled") : \
+ gettext("default"))))
+
+typedef oval_t (*ra_stat_func_t)(void);
+typedef void (*ra_update_func_t)(void);
+
+/*
+ * A routeadm option. These options are those that are enabled or disabled
+ * with the -e and -d command-line options.
+ */
+typedef struct ra_opt {
+ const char *opt_name;
+ oval_t opt_new; /* specified on command-line */
+ oval_t opt_newrev; /* new revert value on command-line */
+ oval_t opt_conf; /* value currently configured */
+ oval_t opt_rev; /* revert value configured */
+ oval_t opt_def; /* default value */
+ ra_update_func_t opt_enable;
+ ra_update_func_t opt_disable;
+ ra_stat_func_t opt_getcur;
+} raopt_t;
+
+#define OPT_IS_FORWARDING(opt) \
+ (strcmp((opt).opt_name, "ipv4-forwarding") == 0 || \
+ strcmp((opt).opt_name, "ipv6-forwarding") == 0)
+
+/*
+ * A routeadm variable. These are assigned using the -s command-line
+ * option.
+ */
+typedef struct ra_var {
+ const char *var_name;
+ char *var_new; /* specified on command-line */
+ char *var_conf; /* Currently configured value */
+ char *var_def; /* The variable's default value */
+} ravar_t;
+
+static boolean_t init_daemon(ra_daemon_t *, ravar_t *, ravar_t *, ravar_t *,
+ char *);
+static oval_t v4forw_cur(void);
+static oval_t v4rout_cur(void);
+static oval_t v6forw_cur(void);
+static oval_t v6rout_cur(void);
+static void enable_v4forw(void);
+static void disable_v4forw(void);
+static void enable_v4rout(void);
+static void disable_v4rout(void);
+static void enable_v6forw(void);
+static void disable_v6forw(void);
+static void enable_v6rout(void);
+static void disable_v6rout(void);
+static void usage(void);
+static void ra_update(void);
+static void ra_report(boolean_t);
+static int ra_parseconf(void);
+static int ra_parseopt(char *, int, raopt_t *);
+static int ra_parsevar(char *, int, ravar_t *);
+static int ra_writeconf(void);
+static raopt_t *ra_str2opt(const char *);
+static oval_t ra_str2oval(const char *);
+static ravar_t *ra_str2var(const char *);
+static char *ra_intloptname(const char *);
+static int open_ipsock(void);
+static int ra_ndioctl(int, char *, int);
+static pid_t ra_isrunning(ra_daemon_t *);
+static void ra_rundaemon(ra_daemon_t *);
+static void ra_killdaemon(ra_daemon_t *);
+static int ra_numv6intfs(void);
+static void start_ndpd(void);
+
+
+/*
+ * The list describing the supported options. If an option is added here,
+ * remember to also add support for the human readable description of the
+ * option to the ra_intloptname() function.
+ */
+static raopt_t ra_opts[] = {
+ { "ipv4-forwarding",
+ OPT_INVALID, OPT_INVALID, OPT_INVALID, OPT_DISABLED, OPT_DISABLED,
+ enable_v4forw, disable_v4forw, v4forw_cur },
+ { "ipv4-routing",
+ OPT_INVALID, OPT_INVALID, OPT_INVALID, OPT_ENABLED, OPT_DEFAULT,
+ enable_v4rout, disable_v4rout, v4rout_cur },
+ { "ipv6-forwarding",
+ OPT_INVALID, OPT_INVALID, OPT_INVALID, OPT_DISABLED, OPT_DISABLED,
+ enable_v6forw, disable_v6forw, v6forw_cur },
+ { "ipv6-routing",
+ OPT_INVALID, OPT_INVALID, OPT_INVALID, OPT_DISABLED, OPT_DISABLED,
+ enable_v6rout, disable_v6rout, v6rout_cur },
+ { NULL,
+ OPT_INVALID, OPT_INVALID, OPT_INVALID, OPT_INVALID, OPT_INVALID,
+ NULL, NULL, NULL }
+};
+
+char *v_opt[] = {
+#define IPV4_ROUTING_DAEMON 0
+ "ipv4-routing-daemon",
+#define IPV4_ROUTING_DAEMON_ARGS 1
+ "ipv4-routing-daemon-args",
+#define IPV4_ROUTING_STOP_CMD 2
+ "ipv4-routing-stop-cmd",
+#define IPV6_ROUTING_DAEMON 3
+ "ipv6-routing-daemon",
+#define IPV6_ROUTING_DAEMON_ARGS 4
+ "ipv6-routing-daemon-args",
+#define IPV6_ROUTING_STOP_CMD 5
+ "ipv6-routing-stop-cmd",
+ NULL
+};
+
+
+/*
+ * the list describing the supported routeadm variables.
+ */
+static ravar_t ra_vars[] = {
+ { "ipv4-routing-daemon", NULL, NULL, IPV4_ROUTING_DAEMON_DEF },
+ { "ipv4-routing-daemon-args", NULL, NULL,
+ IPV4_ROUTING_DAEMON_ARGS_DEF },
+ { "ipv4-routing-stop-cmd", NULL, NULL, IPV4_ROUTING_STOP_CMD_DEF },
+ { "ipv6-routing-daemon", NULL, NULL, IPV6_ROUTING_DAEMON_DEF },
+ { "ipv6-routing-daemon-args", NULL, NULL,
+ IPV6_ROUTING_DAEMON_ARGS_DEF },
+ { "ipv6-routing-stop-cmd", NULL, NULL, IPV6_ROUTING_STOP_CMD_DEF },
+ { NULL, NULL, NULL, NULL }
+};
+
+
+static void
+usage(void)
+{
+ (void) fprintf(stderr, gettext(
+ "usage: %1$s [-p] [-R <root-dir>]\n"
+ " %1$s [-e <option>] [-d <option>] [-r <option>]\n"
+ " [-s <var>=<val>] [-R <root-dir>]\n"
+ " %1$s -u\n\n"
+ " <option> is one of:\n"
+ " ipv4-forwarding\n"
+ " ipv4-routing\n"
+ " ipv6-forwarding\n"
+ " ipv6-routing\n\n"
+ " <var> is one of:\n"
+ " ipv4-routing-daemon\n"
+ " ipv4-routing-daemon-args\n"
+ " ipv4-routing-stop-cmd\n"
+ " ipv6-routing-daemon\n"
+ " ipv6-routing-daemon-args\n"
+ " ipv6-routing-stop-cmd\n"), myname);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int opt, status = 0, opt_index;
+ raopt_t *raopt;
+ ravar_t *ravar;
+ oval_t *val;
+ boolean_t modify = B_FALSE;
+ boolean_t update = B_FALSE;
+ boolean_t parseable = B_FALSE;
+ char *options, *value;
+ int fdnull;
+
+ myname = argv[0];
+
+ (void) setlocale(LC_ALL, "");
+
+#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
+#define TEXT_DOMAIN "SYS_TEST"
+#endif
+
+ (void) textdomain(TEXT_DOMAIN);
+
+ while ((opt = getopt(argc, argv, "bd:e:FpR:r:s:u")) != EOF) {
+ switch (opt) {
+ case 'b':
+ /*
+ * This is a project-private option that allows the
+ * boot script to give us revert values for all of
+ * the options. These values will be used if the
+ * user hasn't set the options, or has reverted
+ * them using the -r flag. We save these values in
+ * the config file so that we can fall back to
+ * these when the admin uses "-r <option>".
+ */
+ booting = B_TRUE;
+ break;
+ case 'd':
+ case 'e':
+ case 'r':
+ if ((raopt = ra_str2opt(optarg)) != NULL) {
+ /*
+ * If -b was specified, then the
+ * values given are those we will revert to.
+ */
+ if (booting)
+ val = &raopt->opt_newrev;
+ else
+ val = &raopt->opt_new;
+ switch (opt) {
+ case 'd':
+ *val = OPT_DISABLED;
+ break;
+ case 'e':
+ *val = OPT_ENABLED;
+ break;
+ case 'r':
+ *val = raopt->opt_def;
+ break;
+ }
+ } else if ((ravar = ra_str2var(optarg)) != NULL) {
+ if (opt != 'r') {
+ usage();
+ return (EXIT_FAILURE);
+ }
+ ravar->var_new = ravar->var_def;
+ } else {
+ (void) fprintf(stderr, gettext(
+ "%1$s: invalid option: %2$s\n"),
+ myname, optarg);
+ usage();
+ return (EXIT_FAILURE);
+ }
+ modify = B_TRUE;
+ break;
+ case 'F':
+ /*
+ * This is a project-private option that allows the
+ * net-loopback method to configure IP forwarding
+ * before network interfaces are configured in
+ * net-physical. This allows administrators to
+ * configure interface-specific IP forwarding
+ * settings in /etc/hostname*.* files by using the
+ * "router" or "-router" ifconfig commands.
+ */
+ forwarding_only = B_TRUE;
+ break;
+ case 'p':
+ parseable = B_TRUE;
+ break;
+ case 'R':
+ if (chroot(optarg) == -1) {
+ (void) fprintf(stderr, gettext(
+ "%1$s: failed to chroot to %2$s: %3$s\n"),
+ myname, optarg, strerror(errno));
+ return (EXIT_FAILURE);
+ }
+ break;
+ case 's':
+ options = optarg;
+ while (*options != '\0') {
+ opt_index = getsubopt(&options, v_opt, &value);
+
+ if (value == NULL) {
+ usage();
+ return (EXIT_FAILURE);
+ }
+ if (opt_index == -1) {
+ (void) fprintf(stderr, gettext(
+ "%1$s: invalid variable: %2$s\n"),
+ myname, optarg);
+ usage();
+ return (EXIT_FAILURE);
+ }
+
+ ravar = &ra_vars[opt_index];
+ if ((ravar->var_new = strdup(value)) == NULL) {
+ (void) fprintf(stderr, gettext("%s: "
+ "unable to allocate memory.\n"),
+ myname);
+ return (EXIT_FAILURE);
+ }
+ }
+ modify = B_TRUE;
+ break;
+ case 'u':
+ update = B_TRUE;
+ break;
+ default:
+ usage();
+ return (EXIT_FAILURE);
+ }
+ }
+
+ if (argc > optind) {
+ /* There shouldn't be any extra args. */
+ usage();
+ return (EXIT_FAILURE);
+ }
+
+ if (booting) {
+ fdnull = open("/dev/null", O_RDWR);
+ (void) dup2(fdnull, 2);
+ }
+
+ if (parseable && (update || modify)) {
+ (void) fprintf(stderr, gettext("%s: the -p option cannot be "
+ "used with any of -deru\n"), myname);
+ usage();
+ return (EXIT_FAILURE);
+ }
+
+ if (ra_parseconf() != 0)
+ return (EXIT_FAILURE);
+
+ if (modify)
+ status = ra_writeconf();
+
+ /*
+ * In order to update the running system or print a report, the
+ * daemon structures must reflect the current state of the
+ * daemon configuration variables.
+ */
+ if (!init_daemon(&v4d, &ra_vars[IPV4_ROUTING_DAEMON],
+ &ra_vars[IPV4_ROUTING_DAEMON_ARGS],
+ &ra_vars[IPV4_ROUTING_STOP_CMD], IN_ROUTED_PID) ||
+ !init_daemon(&v6d, &ra_vars[IPV6_ROUTING_DAEMON],
+ &ra_vars[IPV6_ROUTING_DAEMON_ARGS],
+ &ra_vars[IPV6_ROUTING_STOP_CMD], IN_RIPNGD_PID)) {
+ return (EXIT_FAILURE);
+ }
+
+ if (update)
+ ra_update();
+
+ if (!modify && !update)
+ ra_report(parseable);
+
+ return (status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
+}
+
+
+/*
+ * Initialize the daemon structure pointed to by rad with the variables
+ * passed in.
+ */
+static boolean_t
+init_daemon(ra_daemon_t *rad, ravar_t *rv_exec, ravar_t *rv_args,
+ ravar_t *rv_kill, char *rv_pidfile)
+{
+ int i = 1;
+ char *token = rv_args->var_conf;
+ char *args;
+
+ /*
+ * We only use the pidfile if the admin hasn't altered the name
+ * of the daemon or its kill command.
+ */
+ if (strcmp(rv_exec->var_conf, rv_exec->var_def) != 0 ||
+ strcmp(rv_kill->var_conf, rv_kill->var_def) != 0) {
+ rad->rad_pidfile = NULL;
+ rad->rad_stop_cmd = rv_kill->var_conf;
+ } else {
+ rad->rad_pidfile = rv_pidfile;
+ rad->rad_stop_cmd = NULL;
+ }
+ rad->rad_argvsize = RAD_ARGNUM;
+ if ((rad->rad_argv = malloc(RAD_ARGNUM * sizeof (char *))) == NULL) {
+ (void) fprintf(stderr, gettext("%s: out of memory\n"), myname);
+ return (B_FALSE);
+ }
+ rad->rad_argv[0] = rv_exec->var_conf;
+ if ((args = strdup(rv_args->var_conf)) == NULL) {
+ (void) fprintf(stderr, gettext("%s: out of memory\n"), myname);
+ free(rad->rad_argv);
+ return (B_FALSE);
+ }
+ token = strtok(args, " ");
+ while (token != NULL) {
+ if (i == (rad->rad_argvsize - 1)) {
+ rad->rad_argvsize += RAD_ARGNUM;
+ if ((rad->rad_argv = realloc(rad->rad_argv,
+ rad->rad_argvsize * sizeof (char *))) == NULL) {
+ (void) fprintf(stderr,
+ gettext("%s: out of memory\n"), myname);
+ return (B_FALSE);
+ }
+ }
+ rad->rad_argv[i] = token;
+ token = strtok(NULL, " ");
+ i++;
+ }
+ rad->rad_argv[i] = NULL;
+ return (B_TRUE);
+}
+
+/* Apply currently configured values to the running system. */
+static void
+ra_update(void)
+{
+ int i;
+
+ for (i = 0; ra_opts[i].opt_name != NULL; i++) {
+ /*
+ * If we're only updating forwarding settings, skip all
+ * options that aren't related to IP forwarding.
+ */
+ if (forwarding_only && !OPT_IS_FORWARDING(ra_opts[i]))
+ continue;
+
+ /*
+ * Likewise, if we're booting (the net-init boot script has
+ * specified -b on the command line) and we're updating the
+ * rest of the options, skip the forwarding options we set
+ * in the network boot script.
+ */
+ if (booting && OPT_IS_FORWARDING(ra_opts[i]))
+ continue;
+
+ switch (ra_opts[i].opt_conf) {
+ case OPT_ENABLED:
+ (ra_opts[i].opt_enable)();
+ break;
+ case OPT_DISABLED:
+ (ra_opts[i].opt_disable)();
+ break;
+ case OPT_DEFAULT:
+ switch (ra_opts[i].opt_rev) {
+ case OPT_ENABLED:
+ (ra_opts[i].opt_enable)();
+ break;
+ case OPT_DISABLED:
+ (ra_opts[i].opt_disable)();
+ break;
+ }
+ }
+ }
+}
+
+/*
+ * Print the configured values to stdout. If parseable is set, the output
+ * is machine readable. The parseable output is of the form:
+ * <varname> persistent=<opt_conf> default=<opt_rev> current=<opt_getcur()>
+ * for options, and is of the form:
+ * <varname> persistent=<var_conf> default=<var_def>
+ * for variables.
+ */
+static void
+ra_report(boolean_t parseable)
+{
+ int i;
+ char confstr[OPT_STRBUFSIZE];
+ oval_t curval;
+
+ if (parseable) {
+ for (i = 0; ra_opts[i].opt_name != NULL; i++) {
+ curval = (ra_opts[i].opt_getcur)();
+ (void) printf("%s persistent=%s default=%s "
+ "current=%s\n", ra_opts[i].opt_name,
+ OPT2STR(ra_opts[i].opt_conf),
+ OPT2STR(ra_opts[i].opt_rev),
+ OPT2STR(curval));
+ }
+ for (i = 0; ra_vars[i].var_name != NULL; i++) {
+ (void) printf("%s persistent=\"%s\" "
+ "default=\"%s\" \n",
+ ra_vars[i].var_name, ra_vars[i].var_conf,
+ ra_vars[i].var_def);
+ }
+ return;
+ }
+
+ (void) printf(gettext(
+ " Configuration Current Current\n"
+ " Option Configuration System State\n"
+ "---------------------------------------------------------------"
+ "\n"));
+ for (i = 0; ra_opts[i].opt_name != NULL; i++) {
+ if (ra_opts[i].opt_conf == OPT_DEFAULT) {
+ (void) snprintf(confstr, sizeof (confstr),
+ "%s (%s)",
+ OPT2INTLSTR(ra_opts[i].opt_conf),
+ OPT2INTLSTR(ra_opts[i].opt_rev));
+ } else {
+ (void) snprintf(confstr, sizeof (confstr),
+ "%s", OPT2INTLSTR(ra_opts[i].opt_conf));
+ }
+ curval = (ra_opts[i].opt_getcur)();
+ (void) printf(gettext("%1$27s %2$-21s%3$s\n"),
+ ra_intloptname(ra_opts[i].opt_name), confstr,
+ OPT2INTLSTR(curval));
+ }
+ (void) printf("\n");
+ for (i = 0; ra_vars[i].var_name != NULL; i++) {
+ (void) snprintf(confstr, sizeof (confstr), "\"%s\"",
+ ra_vars[i].var_conf);
+ (void) printf(gettext("%1$27s %2$s\n"),
+ ra_intloptname(ra_vars[i].var_name), confstr);
+ }
+}
+
+/*
+ * Parse the configuration file and fill the ra_opts array with opt_conf
+ * and opt_rev values, and the ra_vars array with opt_conf values.
+ */
+static int
+ra_parseconf(void)
+{
+ FILE *fp;
+ uint_t lineno;
+ char line[RA_MAX_CONF_LINE];
+ char *cp, *confstr;
+ raopt_t *raopt;
+ ravar_t *ravar;
+
+ if ((fp = fopen(RA_CONF_FILE, "r")) == NULL) {
+ /*
+ * There's no config file, so we need to create one. The
+ * system doesn't ship with one, so this is not an error
+ * condition.
+ *
+ * If we're being called from the net-loopback boot script
+ * (forwarding_only is set), then there isn't anything for
+ * us to do in the absense of a configuration file. In
+ * this case, we would only set user-configured forwarding
+ * settings. If the routing.conf file doesn't exist, then
+ * we just exit since the user obviously hasn't configured
+ * anything.
+ */
+ if (forwarding_only)
+ exit(EXIT_SUCCESS);
+
+ return (ra_writeconf());
+ }
+
+ for (lineno = 1; fgets(line, sizeof (line), fp) != NULL; lineno++) {
+ if (line[strlen(line) - 1] == '\n')
+ line[strlen(line) - 1] = '\0';
+
+ cp = line;
+
+ /* Skip leading whitespace */
+ while (isspace(*cp))
+ cp++;
+
+ /* Skip comment lines and empty lines */
+ if (*cp == '#' || *cp == '\0')
+ continue;
+
+ /*
+ * Anything else must be of the form:
+ * <option> <value> <default_value>
+ */
+ if ((confstr = strtok(cp, " ")) == NULL) {
+ (void) fprintf(stderr,
+ gettext("%1$s: %2$s: invalid entry on line %3$d\n"),
+ myname, RA_CONF_FILE, lineno);
+ continue;
+ }
+
+ if ((raopt = ra_str2opt(confstr)) != NULL) {
+ if (ra_parseopt(confstr, lineno, raopt) != 0) {
+ (void) fclose(fp);
+ return (-1);
+ }
+ } else if ((ravar = ra_str2var(confstr)) != NULL) {
+ if (ra_parsevar(confstr, lineno, ravar) != 0) {
+ (void) fclose(fp);
+ return (-1);
+ }
+ } else {
+ (void) fprintf(stderr,
+ gettext("%1$s: %2$s: invalid option name on "
+ "line %3$d\n"),
+ myname, RA_CONF_FILE, lineno);
+ continue;
+ }
+ }
+
+ (void) fclose(fp);
+
+ /*
+ * We call ra_writeconf() here in case there were missing entries
+ * in the file. If all entries have been read, ra_writeconf() will
+ * return without having written anything.
+ */
+ return (ra_writeconf());
+}
+
+static int
+ra_parseopt(char *confstr, int lineno, raopt_t *raopt)
+{
+ oval_t oval;
+
+ if (raopt->opt_conf != OPT_INVALID) {
+ (void) fprintf(stderr,
+ gettext("%1$s: %2$s: WARNING, option defined on "
+ "multiple lines, ignoring line %3$d\n"),
+ myname, RA_CONF_FILE, lineno);
+ return (0);
+ }
+
+ if ((confstr = strtok(NULL, " ")) == NULL) {
+ (void) fprintf(stderr,
+ gettext("%1$s: %2$s: missing value on line %3$d\n"),
+ myname, RA_CONF_FILE, lineno);
+ return (0);
+ }
+ if ((oval = ra_str2oval(confstr)) == OPT_INVALID) {
+ (void) fprintf(stderr,
+ gettext("%1$s: %2$s: invalid option "
+ "value on line %3$d\n"),
+ myname, RA_CONF_FILE, lineno);
+ return (0);
+ }
+ raopt->opt_conf = oval;
+
+ if ((confstr = strtok(NULL, " ")) == NULL) {
+ (void) fprintf(stderr,
+ gettext("%1$s: %2$s: missing revert "
+ "value on line %3$d\n"),
+ myname, RA_CONF_FILE, lineno);
+ return (0);
+ }
+ if ((oval = ra_str2oval(confstr)) == OPT_INVALID) {
+ (void) fprintf(stderr,
+ gettext("%1$s: %2$s: invalid revert "
+ "value on line %3$d\n"),
+ myname, RA_CONF_FILE, lineno, confstr);
+ return (0);
+ }
+ raopt->opt_rev = oval;
+ return (0);
+}
+
+static int
+ra_parsevar(char *confstr, int lineno, ravar_t *ravar)
+{
+ if (ravar->var_conf != NULL) {
+ (void) fprintf(stderr,
+ gettext("%1$s: %2$s: WARNING, variable defined on "
+ "multiple lines, ignoring line %3$d\n"),
+ myname, RA_CONF_FILE, lineno);
+ return (0);
+ }
+
+ confstr = strtok(NULL, "=");
+ if (confstr == NULL) {
+ /*
+ * This isn't an error condition, it simply means that the
+ * variable has no value.
+ */
+ ravar->var_conf = "";
+ return (0);
+ }
+
+ if ((ravar->var_conf = strdup(confstr)) == NULL) {
+ (void) fprintf(stderr, gettext("%s: "
+ "unable to allocate memory\n"), myname);
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * Write options to the configuration file. The options are gathered from
+ * the ra_opts[] and ra_vars[] arrays.
+ *
+ * The format of the file is:
+ * - comment lines start with '#'
+ * - other lines are written in the form "<opt_name> <opt_new> <opt_newrev>"
+ */
+static int
+ra_writeconf(void)
+{
+ int fd, i;
+ FILE *fp;
+ mode_t mode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); /* 0644 */
+ boolean_t changed = B_FALSE;
+
+ /*
+ * At this point, the *_conf members are the current configuration
+ * in the /etc/inet/routing.conf file. The *_new members are those
+ * that were passed in on the command line to override the current
+ * configuration.
+ */
+
+ /* Make sure we don't needlessly overwrite the file. */
+ for (i = 0; ra_opts[i].opt_name != NULL; i++) {
+ if (ra_opts[i].opt_conf == OPT_INVALID) {
+ /* there was no configuration for this option */
+ ra_opts[i].opt_conf = ra_opts[i].opt_def;
+ changed = B_TRUE;
+ }
+ if (ra_opts[i].opt_new != OPT_INVALID &&
+ ra_opts[i].opt_conf != ra_opts[i].opt_new) {
+ /* the new configuration overrides the existing one */
+ ra_opts[i].opt_conf = ra_opts[i].opt_new;
+ changed = B_TRUE;
+ }
+ if (ra_opts[i].opt_newrev != OPT_INVALID &&
+ ra_opts[i].opt_rev != ra_opts[i].opt_newrev) {
+ /* a new revert value was passed in */
+ ra_opts[i].opt_rev = ra_opts[i].opt_newrev;
+ changed = B_TRUE;
+ }
+ }
+
+ for (i = 0; ra_vars[i].var_name != NULL; i++) {
+ if (ra_vars[i].var_conf == NULL) {
+ /* the variable wasn't in the configuration file */
+ ra_vars[i].var_conf = ra_vars[i].var_def;
+ changed = B_TRUE;
+ }
+ if (ra_vars[i].var_new != NULL &&
+ strcmp(ra_vars[i].var_conf, ra_vars[i].var_new) != 0) {
+ /* a new variable value was passed in */
+ ra_vars[i].var_conf = ra_vars[i].var_new;
+ changed = B_TRUE;
+ }
+ }
+
+ if (!changed)
+ return (0);
+
+ if ((fd = open(RA_CONF_FILE, O_WRONLY|O_CREAT|O_TRUNC, mode)) == -1) {
+ (void) fprintf(stderr,
+ gettext("%1$s: failed to open %2$s: %3$s\n"),
+ myname, RA_CONF_FILE, strerror(errno));
+ return (-1);
+ }
+ if ((fp = fdopen(fd, "w")) == NULL) {
+ (void) fprintf(stderr,
+ gettext("%1$s: failed to open stream for %2$s: %3$s\n"),
+ myname, RA_CONF_FILE, strerror(errno));
+ return (-1);
+ }
+
+ (void) fputs(
+ "#\n"
+ "# routing.conf\n"
+ "#\n"
+ "# Parameters for IP forwarding and routing.\n"
+ "# Do not edit this file by hand -- use routeadm(1m) instead.\n"
+ "#\n",
+ fp);
+
+ /*
+ * Option entries are of the form:
+ * <name> <val> <revert-val>
+ */
+ for (i = 0; ra_opts[i].opt_name != NULL; i++) {
+ (void) fprintf(fp, "%s %s %s\n",
+ ra_opts[i].opt_name,
+ OPT2STR(ra_opts[i].opt_conf),
+ OPT2STR(ra_opts[i].opt_rev));
+ }
+ /*
+ * Variable entries are of the form:
+ * <name> =<value>
+ */
+ for (i = 0; ra_vars[i].var_name != NULL; i++) {
+ (void) fprintf(fp, "%s =%s\n",
+ ra_vars[i].var_name,
+ ra_vars[i].var_conf);
+ }
+
+ (void) fclose(fp);
+
+ return (0);
+}
+
+
+
+/*
+ * return the ra_opts array element whose opt_name matches the string
+ * passed in as an argument.
+ */
+static raopt_t *
+ra_str2opt(const char *optnamestr)
+{
+ int i;
+
+ for (i = 0; ra_opts[i].opt_name != NULL; i++) {
+ if (strcmp(optnamestr, ra_opts[i].opt_name) == 0)
+ break;
+ }
+ if (ra_opts[i].opt_name == NULL)
+ return (NULL);
+ else
+ return (&ra_opts[i]);
+}
+
+/* Convert a string to an option value. */
+static oval_t
+ra_str2oval(const char *valstr)
+{
+ if (strcmp(valstr, "enabled") == 0)
+ return (OPT_ENABLED);
+ else if (strcmp(valstr, "disabled") == 0)
+ return (OPT_DISABLED);
+ else if (strcmp(valstr, "default") == 0)
+ return (OPT_DEFAULT);
+ return (OPT_INVALID);
+}
+
+static ravar_t *
+ra_str2var(const char *varnamestr)
+{
+ int i;
+
+ for (i = 0; ra_vars[i].var_name != NULL; i++) {
+ if (strcmp(varnamestr, ra_vars[i].var_name) == 0)
+ break;
+ }
+ if (ra_vars[i].var_name == NULL)
+ return (NULL);
+ else
+ return (&ra_vars[i]);
+}
+
+/*
+ * Given an option name, this function provides an internationalized, human
+ * readable version of the option name.
+ */
+static char *
+ra_intloptname(const char *optname)
+{
+ if (strcmp(optname, "ipv4-forwarding") == 0)
+ return (gettext("IPv4 forwarding"));
+ else if (strcmp(optname, "ipv4-routing") == 0)
+ return (gettext("IPv4 routing"));
+ else if (strcmp(optname, "ipv6-forwarding") == 0)
+ return (gettext("IPv6 forwarding"));
+ else if (strcmp(optname, "ipv6-routing") == 0)
+ return (gettext("IPv6 routing"));
+ else if (strcmp(optname, "ipv4-routing-daemon") == 0)
+ return (gettext("IPv4 routing daemon"));
+ else if (strcmp(optname, "ipv4-routing-daemon-args") == 0)
+ return (gettext("IPv4 routing daemon args"));
+ else if (strcmp(optname, "ipv4-routing-stop-cmd") == 0)
+ return (gettext("IPv4 routing daemon stop"));
+ else if (strcmp(optname, "ipv6-routing-daemon") == 0)
+ return (gettext("IPv6 routing daemon"));
+ else if (strcmp(optname, "ipv6-routing-daemon-args") == 0)
+ return (gettext("IPv6 routing daemon args"));
+ else if (strcmp(optname, "ipv6-routing-stop-cmd") == 0)
+ return (gettext("IPv6 routing daemon stop"));
+ /*
+ * If we get here, there's a bug and someone should trip over this
+ * NULL pointer.
+ */
+ return (NULL);
+}
+
+static int
+open_ipsock(void)
+{
+ if (ipsock == -1 && (ipsock = socket(PF_INET6, SOCK_DGRAM, 0)) == -1) {
+ (void) fprintf(stderr,
+ gettext("%1$s: unable to open %2$s: %3$s\n"),
+ myname, IP_DEV_NAME, strerror(errno));
+ }
+ return (ipsock);
+}
+
+static int
+ra_ndioctl(int cmd, char *data, int ilen)
+{
+ struct strioctl stri;
+
+ if (open_ipsock() == -1)
+ return (-1);
+
+ stri.ic_cmd = cmd;
+ stri.ic_timout = 0;
+ stri.ic_len = ilen;
+ stri.ic_dp = data;
+ if (ioctl(ipsock, I_STR, &stri) == -1)
+ return (-1);
+ return (0);
+}
+
+/*
+ * Returns the process id of the specified command if it's running, -1 if
+ * it's not.
+ */
+static pid_t
+ra_isrunning(ra_daemon_t *daemon)
+{
+ FILE *pidfp;
+ pid_t pid = -1;
+ char procpath[MAXPATHLEN];
+ int procfd;
+ psinfo_t ps;
+
+ if (daemon->rad_pidfile == NULL)
+ return (-1);
+
+ if ((pidfp = fopen(daemon->rad_pidfile, "r")) == NULL)
+ return (-1);
+ if (fscanf(pidfp, "%ld", &pid) != 1)
+ return (-1);
+ (void) fclose(pidfp);
+
+ /* Make sure the process we're interested in is still running. */
+ (void) snprintf(procpath, sizeof (procpath), "/proc/%ld/psinfo", pid);
+ if ((procfd = open(procpath, O_RDONLY)) == -1)
+ return (-1);
+ if (read(procfd, &ps, sizeof (ps)) != sizeof (ps)) {
+ (void) close(procfd);
+ return (-1);
+ }
+ (void) close(procfd);
+ if (strncmp(daemon->rad_argv[0], ps.pr_psargs,
+ strlen(daemon->rad_argv[0])) != 0) {
+ return (-1);
+ }
+
+ return (pid);
+}
+
+/*
+ * Fork and exec a daemon, and wait until it has daemonized to return. We
+ * first attempt to kill it if it's already running, as the command-line
+ * arguments may have changed.
+ */
+static void
+ra_rundaemon(ra_daemon_t *daemon)
+{
+ pid_t daemon_pid;
+
+ ra_killdaemon(daemon);
+
+ if ((daemon_pid = fork()) == -1) {
+ (void) fprintf(stderr,
+ gettext("%1$s: unable to fork %2$s: %3$s\n"),
+ myname, daemon->rad_argv[0], strerror(errno));
+ } else if (daemon_pid == 0) {
+ /* We're the child, execute the daemon. */
+ if (execv(daemon->rad_argv[0], daemon->rad_argv) == -1) {
+ (void) fprintf(stderr,
+ gettext("%1$s: unable to execute %2$s: %3$s\n"),
+ myname, daemon->rad_argv[0], strerror(errno));
+ _exit(EXIT_FAILURE);
+ }
+ } else {
+ /* Wait for the child to daemonize or terminate. */
+ (void) wait(NULL);
+ }
+}
+
+/*
+ * If the daemon has a pidfile, use the pid to kill the targeted process.
+ * Otherwise, use the daemon's configured stop command.
+ */
+static void
+ra_killdaemon(ra_daemon_t *daemon)
+{
+ pid_t pid;
+
+ /*
+ * rad_pidfile is cleared out if the user sets a non-default
+ * routing daemon
+ */
+ if (daemon->rad_pidfile != NULL) {
+ if ((pid = ra_isrunning(daemon)) == -1)
+ return;
+ if (kill(pid, SIGTERM) == -1) {
+ (void) fprintf(stderr, gettext(
+ "%1$s: unable to kill %2$s: %3$s\n"), myname,
+ daemon->rad_argv[0], strerror(errno));
+ }
+ } else {
+ if (system(daemon->rad_stop_cmd) == -1)
+ if (!booting) {
+ (void) fprintf(stderr, gettext("%1$s: "
+ "%2$s failed: %3$s\n"),
+ myname, daemon->rad_stop_cmd,
+ strerror(errno));
+ }
+ }
+}
+
+/*
+ * Return the number of IPv6 addresses configured. This answers the
+ * generic question, "is IPv6 configured?". We only start in.ndpd if IPv6
+ * is configured, and we also only enable IPv6 routing if IPv6 is enabled.
+ */
+static int
+ra_numv6intfs(void)
+{
+ static int num = -1;
+ struct lifnum lifn;
+
+ if (num != -1)
+ return (num);
+
+ if (open_ipsock() == -1)
+ return (0);
+
+ lifn.lifn_family = AF_INET6;
+ lifn.lifn_flags = 0;
+
+ if (ioctl(ipsock, SIOCGLIFNUM, &lifn) == -1)
+ return (0);
+
+ return (num = lifn.lifn_count);
+}
+
+/* Run in.ndpd */
+static void
+start_ndpd(void)
+{
+ ra_rundaemon(&in_ndpd);
+}
+
+/* Is ip_forwarding turned on? */
+static oval_t
+v4forw_cur(void)
+{
+ char ndbuf[] = ND_IP_FORWARDING;
+
+ if (ra_ndioctl(ND_GET, ndbuf, sizeof (ndbuf)) == -1)
+ return (OPT_DISABLED);
+ return (atoi(ndbuf) == 0 ? OPT_DISABLED : OPT_ENABLED);
+}
+
+/* Is in.routed running? */
+static oval_t
+v4rout_cur(void)
+{
+ /*
+ * routeadm cannot really know the status of a user-configured
+ * routing daemon. We clear the rad_pidfile field of the daemon
+ * structure when the user configures the daemon.
+ */
+ if (v4d.rad_pidfile == NULL)
+ return (OPT_UNKNOWN);
+ return (ra_isrunning(&v4d) == -1 ? OPT_DISABLED : OPT_ENABLED);
+}
+
+/* Is ip6_forwarding turned on? */
+static oval_t
+v6forw_cur(void)
+{
+ char ndbuf[] = ND_IP6_FORWARDING;
+
+ if (ra_ndioctl(ND_GET, ndbuf, sizeof (ndbuf)) == -1)
+ return (OPT_DISABLED);
+ return (atoi(ndbuf) == 0 ? OPT_DISABLED : OPT_ENABLED);
+}
+
+/* Is in.ripngd running? */
+static oval_t
+v6rout_cur(void)
+{
+ /*
+ * routeadm cannot really know the status of a user-configured
+ * routing daemon. We clear the rad_pidfile field of the daemon
+ * structure when the user configures the daemon.
+ */
+ if (v6d.rad_pidfile == NULL)
+ return (OPT_UNKNOWN);
+ return (ra_isrunning(&v6d) == -1 ? OPT_DISABLED : OPT_ENABLED);
+}
+
+static void
+enable_v4forw(void)
+{
+ (void) ra_ndioctl(ND_SET, nd_ip_forw_on, sizeof (nd_ip_forw_on));
+}
+
+static void
+disable_v4forw(void)
+{
+ (void) ra_ndioctl(ND_SET, nd_ip_forw_off, sizeof (nd_ip_forw_off));
+}
+
+static void
+enable_v4rout(void)
+{
+ if (v4d.rad_argv[0][0] == '\0') {
+ (void) fprintf(stderr, gettext("%1$s: %2$s is not set.\n"
+ " Use -s to set the ipv4-routing-daemon variable, \n"
+ " or use -d to disable ipv4-routing.\n"), myname,
+ ra_intloptname("ipv4-routing-daemon"));
+ } else {
+ ra_rundaemon(&v4d);
+ }
+}
+
+static void
+disable_v4rout(void)
+{
+ ra_killdaemon(&v4d);
+}
+
+/* Turn on ip6_forwarding, ip6_ignore_redirect, and ip6_send_redirects. */
+static void
+enable_v6forw(void)
+{
+ (void) ra_ndioctl(ND_SET, nd_ip6_sendredir_on,
+ sizeof (nd_ip6_sendredir_on));
+ (void) ra_ndioctl(ND_SET, nd_ip6_forw_on, sizeof (nd_ip6_forw_on));
+}
+
+/*
+ * in.ripngd is tied to IPv6 forwarding due to a limitation in its
+ * implementation. It will propagate routes blindly without checking if
+ * forwarding is enabled on the interfaces it's using. Until that's fixed,
+ * make sure in.ripngd doesn't run if IPv6 forwarding isn't enabled.
+ */
+static void
+disable_v6forw(void)
+{
+ pid_t pid;
+
+ if ((pid = ra_isrunning(&v6d)) != -1 &&
+ kill(pid, SIGTERM) == -1) {
+ (void) fprintf(stderr,
+ gettext("%1$s: unable to kill %2$s: %3$s\n"),
+ myname, v6d.rad_argv[0], strerror(errno));
+ }
+
+ (void) ra_ndioctl(ND_SET, nd_ip6_sendredir_off,
+ sizeof (nd_ip6_sendredir_off));
+ (void) ra_ndioctl(ND_SET, nd_ip6_forw_off, sizeof (nd_ip6_forw_off));
+}
+
+/*
+ * We only enable IPv6 routing if there is at least one IPv6 interface
+ * configured.
+ *
+ * If in.ndpd isn't already running, then we start it here because
+ * in.ripngd depends on having routes based on the prefixes configured by
+ * in.ndpd. We only start in.ripngd if IPv6 forwarding is enabled. This
+ * is due to a giant gap in in.ripngd's design which causes in.ripngd to
+ * propagate routes on all interfaces regardless of their forwarding
+ * status. If that's fixed, then we can start in.ripngd regardless of the
+ * global IPv6 forwarding status.
+ */
+static void
+enable_v6rout(void)
+{
+ if (ra_numv6intfs() == 0)
+ return;
+ start_ndpd();
+ if (v6forw_cur() != OPT_ENABLED)
+ return;
+ (void) ra_ndioctl(ND_SET, nd_ip6_ignredir_on,
+ sizeof (nd_ip6_ignredir_on));
+ if (v6d.rad_argv[0][0] == '\0') {
+ (void) fprintf(stderr, gettext("%1$s: %2$s is not set.\n"
+ " Use -s to set the ipv6-routing-daemon variable, \n"
+ " or use -d to disable ipv6-routing.\n"), myname,
+ ra_intloptname("ipv6-routing-daemon"));
+ } else {
+ ra_rundaemon(&v6d);
+ }
+}
+
+static void
+disable_v6rout(void)
+{
+ /*
+ * We always start in.ndpd if there is an IPv6 interface
+ * configured, regardless of the status of IPv6 routing.
+ */
+ if (ra_numv6intfs() > 0)
+ start_ndpd();
+
+ (void) ra_ndioctl(ND_SET, nd_ip6_ignredir_off,
+ sizeof (nd_ip6_ignredir_off));
+ ra_killdaemon(&v6d);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/shell.xml b/usr/src/cmd/cmd-inet/usr.sbin/shell.xml
new file mode 100644
index 0000000000..58e64d2d7b
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/shell.xml
@@ -0,0 +1,147 @@
+<?xml version='1.0'?>
+<!DOCTYPE service_bundle SYSTEM '/usr/share/lib/xml/dtd/service_bundle.dtd.1'>
+
+<!--
+ Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ ident "%Z%%M% %I% %E% SMI"
+
+ NOTE: This service manifest is not editable; its contents will
+ be overwritten by package or patch operations, including
+ operating system upgrade. Make customizations in a different
+ file.
+
+ Service manifests for in.rshd.
+-->
+
+<service_bundle type='manifest' name='SUNWrcmdr:rsh'>
+
+<service
+ name='network/shell'
+ type='service'
+ version='1'>
+
+ <create_default_instance enabled='false' />
+
+ <restarter>
+ <service_fmri value='svc:/network/inetd:default' />
+ </restarter>
+
+ <dependency
+ name='loopback'
+ grouping='require_any'
+ restart_on='error'
+ type='service'>
+ <service_fmri value='svc:/network/loopback' />
+ </dependency>
+
+ <dependency
+ name='network'
+ grouping='optional_all'
+ restart_on='error'
+ type='service'>
+ <service_fmri value='svc:/milestone/network' />
+ </dependency>
+
+ <exec_method
+ type='method'
+ name='inetd_start'
+ exec='/usr/sbin/in.rshd'
+ timeout_seconds='0'>
+ <method_context>
+ <method_credential user='root' group='root' />
+ </method_context>
+ </exec_method>
+
+ <exec_method
+ type='method'
+ name='inetd_disable'
+ exec=':kill'
+ timeout_seconds='0'>
+ </exec_method>
+
+ <!--
+ The proto setting here of both tcp and tcp6only is required in order
+ to prevent breaking applications which assume that the socket they
+ are handed by rsh is of the AF_INET family.
+ -->
+ <property_group name='inetd' type='framework'>
+ <stability value='Evolving' />
+ <propval name='name' type='astring' value='shell' />
+ <propval name='endpoint_type' type='astring' value='stream' />
+ <propval name='wait' type='boolean' value='false' />
+ <propval name='isrpc' type='boolean' value='false' />
+ <property name='proto' type='astring' override='true'>
+ <astring_list>
+ <value_node value='tcp'/>
+ <value_node value='tcp6only'/>
+ </astring_list>
+ </property>
+ </property_group>
+
+ <!--
+ RSH - with kerberos authentication (only works over IPv4)
+ -->
+ <instance name='kshell' enabled='false' >
+ <exec_method
+ type='method'
+ name='inetd_start'
+ exec='/usr/sbin/in.rshd -kc'
+ timeout_seconds='0'>
+ <method_context>
+ <method_credential user='root' group='root' />
+ </method_context>
+ </exec_method>
+
+ <exec_method
+ type='method'
+ name='inetd_disable'
+ exec=':kill'
+ timeout_seconds='0'>
+ </exec_method>
+
+ <property_group name='inetd' type='framework'>
+ <propval name='name' type='astring' value='kshell' />
+ <propval name='proto' type='astring' value='tcp' />
+ </property_group>
+ </instance>
+
+ <stability value='Unstable' />
+
+ <template>
+ <common_name>
+ <loctext xml:lang='C'>
+ rsh
+ </loctext>
+ </common_name>
+ <documentation>
+ <manpage title='in.rshd' section='1M'
+ manpath='/usr/share/man' />
+ <manpage title='rshd' section='1M'
+ manpath='/usr/share/man' />
+ </documentation>
+ </template>
+</service>
+
+</service_bundle>
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/Makefile b/usr/src/cmd/cmd-inet/usr.sbin/snoop/Makefile
new file mode 100644
index 0000000000..e9ff9b46c2
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/Makefile
@@ -0,0 +1,68 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+PROG= snoop
+OBJS= dlprims.o nfs4_xdr.o snoop.o snoop_aarp.o snoop_adsp.o snoop_aecho.o \
+ snoop_apple.o snoop_arp.o snoop_atp.o snoop_bparam.o \
+ snoop_capture.o snoop_dhcp.o snoop_display.o snoop_dns.o snoop_ether.o \
+ snoop_filter.o snoop_http.o snoop_icmp.o snoop_igmp.c snoop_ip.o \
+ snoop_ipaddr.o snoop_ipsec.o snoop_ldap.o snoop_mip.o snoop_mount.o \
+ snoop_nbp.o snoop_netbios.o snoop_nfs.o snoop_nfs3.o snoop_nfs4.o \
+ snoop_nfs_acl.o snoop_nis.o snoop_nisplus.o snoop_nlm.o snoop_ntp.o \
+ snoop_pf.o snoop_ospf.o snoop_ospf6.o snoop_pmap.o snoop_ppp.o \
+ snoop_pppoe.o snoop_rip.o snoop_rip6.o snoop_rpc.o snoop_rpcprint.o \
+ snoop_rpcsec.o snoop_rport.o snoop_rquota.o snoop_rstat.o snoop_rtmp.o \
+ snoop_sctp.o snoop_slp.o snoop_smb.o snoop_socks.o snoop_solarnet.o \
+ snoop_tcp.o snoop_tftp.o snoop_udp.o snoop_zip.o
+
+SRCS= $(OBJS:.o=.c)
+HDRS= snoop.h snoop_mip.h at.h snoop_ospf.h snoop_ospf6.h
+
+include ../../../Makefile.cmd
+
+CPPFLAGS += -I. -I$(SRC)/common/net/dhcp
+LDLIBS += -ldhcputil -ldlpi -lsocket -lnsl
+
+.KEEP_STATE:
+
+.PARALLEL: $(OBJS)
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+ $(LINK.c) -o $@ $(OBJS) $(LDLIBS)
+ $(POST_PROCESS)
+
+install: all $(ROOTUSRSBINPROG)
+
+clean:
+ $(RM) $(OBJS)
+
+lint: lint_SRCS
+
+include ../../../Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/at.h b/usr/src/cmd/cmd-inet/usr.sbin/snoop/at.h
new file mode 100644
index 0000000000..e98072fd73
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/at.h
@@ -0,0 +1,206 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ *
+ */
+
+#ifndef _AT_H
+#define _AT_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * There is a lot of alignment problems in AppleTalk packets.
+ * This is the reason some of the headers use uint8_t arrays instead of the
+ * natural datatype.
+ */
+
+/* AARP */
+
+#define AARP_REQ 1
+#define AARP_RESP 2
+#define AARP_PROBE 3
+
+
+/* DDP */
+
+struct ddp_hdr {
+ uint8_t ddp_hop_len;
+ uint8_t ddp_len_lo;
+ uint16_t ddp_cksum;
+ uint16_t ddp_dest_net;
+ uint16_t ddp_src_net;
+ uint8_t ddp_dest_id;
+ uint8_t ddp_src_id;
+ uint8_t ddp_dest_sock;
+ uint8_t ddp_src_sock;
+ uint8_t ddp_type;
+};
+
+#define ddp_pad(x) ((x)->ddp_hop_len & 0xc0)
+#define ddp_hop(x) (((x)->ddp_hop_len >> 2) & 0xf)
+#define ddp_len(x) ((((x)->ddp_hop_len & 0x3) << 8) + (x)->ddp_len_lo)
+
+#define DDPHDR_SIZE 13
+
+#define DDP_TYPE_RTMPRQ 5
+#define DDP_TYPE_RTMPRESP 1
+#define DDP_TYPE_NBP 2
+#define DDP_TYPE_ATP 3
+#define DDP_TYPE_AEP 4
+#define DDP_TYPE_ZIP 6
+#define DDP_TYPE_ADSP 7
+
+
+/* AECHO */
+
+#define AEP_REQ 1
+#define AEP_REPLY 2
+
+/* NBP */
+
+struct nbp_hdr {
+ uint8_t ddphdr[DDPHDR_SIZE];
+ uint8_t nbp_fun_cnt;
+ uint8_t nbp_id;
+};
+
+#define NBP_BRRQ 1
+#define NBP_LKUP 2
+#define NBP_LKUP_REPLY 3
+#define NBP_FWDREQ 4
+
+
+/* ZIP */
+
+struct zip_hdr {
+ uint8_t ddphdr[DDPHDR_SIZE];
+ uint8_t zip_func;
+ uint8_t zip_netcnt;
+};
+
+#define ZIP_QUERY 1
+#define ZIP_REPLY 2
+#define ZIP_GET_NET_INFO 5
+#define ZIP_GET_NET_INFO_REPLY 6
+#define ZIP_NOTIFY 7
+#define ZIP_EXT_REPLY 8
+
+#define ZIP_ATP_GETMYZONE 7
+#define ZIP_ATP_GETZONELIST 8
+#define ZIP_ATP_GETLOCALZONES 9
+
+#define ZIP_FLG_ONEZ 0x20
+#define ZIP_FLG_USEBRC 0x40
+#define ZIP_FLG_ZINV 0x80
+
+
+/* ATP */
+
+struct atp_hdr {
+ uint8_t ddphdr[DDPHDR_SIZE];
+ uint8_t atp_ctrl;
+ uint8_t atp_seq;
+ uint8_t atp_tid[2];
+ uint8_t atp_user[4];
+};
+
+#define ATPHDR_SIZE 8
+
+#define atp_fun(x) (((x) >> 6) & 0x3)
+#define atp_tmo(x) ((x) & 0x7)
+
+#define ATP_TREQ 1
+#define ATP_TRESP 2
+#define ATP_TREL 3
+#define ATP_FLG_STS 0x08
+#define ATP_FLG_EOM 0x10
+#define ATP_FLG_XO 0x20
+
+
+#define NODE_ID_BROADCAST 0xff
+
+struct ddp_adsphdr {
+ uint8_t ddphdr[DDPHDR_SIZE];
+ uint8_t ad_connid[2]; /* short */
+ uint8_t ad_fbseq[4]; /* long */
+ uint8_t ad_nrseq[4]; /* long */
+ uint8_t ad_rcvwin[2]; /* short */
+ uint8_t ad_desc;
+};
+
+#define AD_CTRL 0x80
+#define AD_ACKREQ 0x40
+#define AD_EOM 0x20
+#define AD_ATT 0x10
+#define AD_CTRL_MASK 0x0f
+
+#define AD_CREQ 0x81 /* Open Conn Request */
+#define AD_CACK 0x82 /* Open Conn Ack */
+#define AD_CREQ_ACK 0x83 /* Open Conn Req+Ack */
+#define AD_CDENY 0x84 /* Open Conn Denial */
+
+struct ddp_adsp_att {
+ struct ddp_adsphdr ad;
+ uint8_t ad_att_code[2]; /* short */
+};
+
+struct ddp_adsp_open {
+ struct ddp_adsphdr ad;
+ uint8_t ad_version[2]; /* short */
+ uint8_t ad_dconnid[2]; /* short */
+ uint8_t ad_attseq[4]; /* long */
+};
+
+#define RTMP_REQ 1
+#define RTMP_RDR_SH 2 /* Route Data Request, split horizon */
+#define RTMP_RDR_NSH 3 /* Route Data Request, no split horizon */
+
+#define RTMP_DIST_MASK 0x1f
+#define RTMP_EXTEND 0x80
+#define RTMP_FILLER 0x82
+
+
+uint16_t get_short(uint8_t *);
+uint32_t get_long(uint8_t *);
+
+extern void interpret_aarp(int, char *, int);
+extern void interpret_at(int, struct ddp_hdr *, int);
+extern void interpret_nbp(int, struct nbp_hdr *, int);
+extern void interpret_rtmp(int, struct ddp_hdr *, int);
+extern void interpret_aecho(int, struct ddp_hdr *, int);
+extern void interpret_atp(int, struct ddp_hdr *, int);
+extern void interpret_adsp(int, struct ddp_adsphdr *, int);
+extern void interpret_ddp_zip(int, struct zip_hdr *, int);
+extern void interpret_atp_zip(int, struct atp_hdr *, int);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _AT_H */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/dlprims.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/dlprims.c
new file mode 100644
index 0000000000..bc3f33d686
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/dlprims.c
@@ -0,0 +1,342 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI" /* SunOS */
+
+#include <sys/types.h>
+#include <sys/stropts.h>
+#include <sys/signal.h>
+#include <sys/dlpi.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "snoop.h"
+
+#define DLMAXWAIT (10) /* max wait in seconds for response */
+#define DLMAXBUF (256)
+
+
+static void sigalrm(int);
+static void strgetmsg(int, struct strbuf *, struct strbuf *, int *, char *);
+static void err(const char *, ...);
+static void syserr(char *);
+
+/*
+ * Issue DL_INFO_REQ and wait for DL_INFO_ACK.
+ */
+void
+dlinforeq(int fd, dl_info_ack_t *infoackp)
+{
+ union DL_primitives *dlp;
+ char buf[DLMAXBUF];
+ struct strbuf ctl;
+ int flags;
+
+ dlp = (union DL_primitives *)buf;
+
+ dlp->info_req.dl_primitive = DL_INFO_REQ;
+
+ ctl.maxlen = DLMAXBUF;
+ ctl.len = DL_INFO_REQ_SIZE;
+ ctl.buf = (char *)dlp;
+
+ flags = RS_HIPRI;
+
+ if (putmsg(fd, &ctl, NULL, flags) < 0)
+ syserr("dlinforeq: putmsg");
+
+ strgetmsg(fd, &ctl, NULL, &flags, "dlinfoack");
+ expecting(DL_INFO_ACK, dlp, "dlinfoack");
+
+ if (ctl.len < DL_INFO_ACK_SIZE)
+ err("dlinfoack: response ctl.len too short: %d", ctl.len);
+
+ if (flags != RS_HIPRI)
+ err("dlinfoack: DL_INFO_ACK was not M_PCPROTO");
+
+ if (infoackp)
+ *infoackp = dlp->info_ack;
+}
+
+/*
+ * Issue DL_ATTACH_REQ.
+ * Return zero on success, nonzero on error.
+ */
+void
+dlattachreq(int fd, ulong_t ppa)
+{
+ union DL_primitives *dlp;
+ char buf[DLMAXBUF];
+ struct strbuf ctl;
+ int flags;
+
+ dlp = (union DL_primitives *)buf;
+
+ dlp->attach_req.dl_primitive = DL_ATTACH_REQ;
+ dlp->attach_req.dl_ppa = ppa;
+
+ ctl.maxlen = DLMAXBUF;
+ ctl.len = DL_ATTACH_REQ_SIZE;
+ ctl.buf = (char *)dlp;
+
+ flags = 0;
+
+ if (putmsg(fd, &ctl, NULL, flags) < 0)
+ syserr("dlattachreq: putmsg");
+
+ strgetmsg(fd, &ctl, NULL, &flags, "dlattachreq");
+ expecting(DL_OK_ACK, dlp, "dlattachreq");
+}
+
+/*
+ * Issue DL_PROMISCON_REQ and wait for DL_OK_ACK.
+ */
+void
+dlpromiscon(int fd, int level)
+{
+ union DL_primitives *dlp;
+ char buf[DLMAXBUF];
+ struct strbuf ctl;
+ int flags;
+
+ dlp = (union DL_primitives *)buf;
+
+ dlp->promiscon_req.dl_primitive = DL_PROMISCON_REQ;
+ dlp->promiscon_req.dl_level = level;
+
+ ctl.maxlen = DLMAXBUF;
+ ctl.len = DL_PROMISCON_REQ_SIZE;
+ ctl.buf = (char *)dlp;
+
+ flags = 0;
+
+ if (putmsg(fd, &ctl, NULL, flags) < 0)
+ syserr("dlpromiscon: putmsg");
+
+ strgetmsg(fd, &ctl, NULL, &flags, "dlpromisconreq");
+ expecting(DL_OK_ACK, dlp, "dlpromisconreq");
+}
+
+void
+dlbindreq(int fd, ulong_t sap, ulong_t max_conind, ushort_t service_mode,
+ ushort_t conn_mgmt)
+{
+ union DL_primitives *dlp;
+ char buf[DLMAXBUF];
+ struct strbuf ctl;
+ int flags;
+
+ dlp = (union DL_primitives *)buf;
+
+ dlp->bind_req.dl_primitive = DL_BIND_REQ;
+ dlp->bind_req.dl_sap = sap;
+ dlp->bind_req.dl_max_conind = max_conind;
+ dlp->bind_req.dl_service_mode = service_mode;
+ dlp->bind_req.dl_conn_mgmt = conn_mgmt;
+ dlp->bind_req.dl_xidtest_flg = 0;
+
+ ctl.maxlen = DLMAXBUF;
+ ctl.len = DL_BIND_REQ_SIZE;
+ ctl.buf = (char *)dlp;
+
+ flags = 0;
+
+ if (putmsg(fd, &ctl, NULL, flags) < 0)
+ syserr("dlbindreq: putmsg");
+
+ ctl.len = 0;
+
+ strgetmsg(fd, &ctl, NULL, &flags, "dlbindack");
+ expecting(DL_BIND_ACK, dlp, "dlbindack");
+
+ if (ctl.len < sizeof (DL_BIND_ACK_SIZE))
+ err("dlbindack: response ctl.len too short: %d", ctl.len);
+}
+
+/* ARGSUSED */
+static void
+sigalrm(int unused)
+{
+ (void) err("sigalrm: TIMEOUT");
+}
+
+void
+strgetmsg(int fd, struct strbuf *ctlp, struct strbuf *datap, int *flagsp,
+ char *caller)
+{
+ int rc;
+ static char errmsg[80];
+
+ /*
+ * Start timer.
+ */
+ if (snoop_alarm(DLMAXWAIT, sigalrm) < 0) {
+ sprintf(errmsg, "%s: alarm", caller);
+ syserr(errmsg);
+ }
+
+ /*
+ * Set flags argument and issue getmsg().
+ */
+ *flagsp = 0;
+ if ((rc = getmsg(fd, ctlp, datap, flagsp)) < 0) {
+ sprintf(errmsg, "%s: getmsg", caller);
+ syserr(errmsg);
+ }
+
+ /*
+ * Stop timer.
+ */
+ if (snoop_alarm(0, sigalrm) < 0) {
+ sprintf(errmsg, "%s: alarm", caller);
+ syserr(errmsg);
+ }
+
+ /*
+ * Check for MOREDATA and/or MORECTL.
+ */
+ if ((rc & (MORECTL | MOREDATA)) == (MORECTL | MOREDATA))
+ err("%s: strgetmsg: MORECTL|MOREDATA", caller);
+ if (rc & MORECTL)
+ err("%s: strgetmsg: MORECTL", caller);
+ if (rc & MOREDATA)
+ err("%s: strgetmsg: MOREDATA", caller);
+
+ /*
+ * Check for at least sizeof (long) control data portion.
+ */
+ if (ctlp->len < sizeof (long))
+ err("%s: control portion length < sizeof (long)",
+ caller);
+}
+
+char *
+show_dltype(int dl_errno)
+{
+ switch (dl_errno) {
+ case DL_ACCESS: /* Improper permissions for request */
+ return ("Improper permissions for request");
+ case DL_BADADDR:
+ return ("DLSAP addr in improper format or invalid");
+ case DL_BADCORR: /* Seq number not from outstand DL_CONN_IND */
+ return ("Seq number not from outstand DL_CONN_IND");
+ case DL_BADDATA: /* User data exceeded provider limit */
+ return ("User data exceeded provider limit");
+ case DL_BADPPA: /* Specified PPA was invalid */
+ return ("Specified PPA was invalid");
+ case DL_BADPRIM: /* Primitive received not known by provider */
+ return ("Primitive received not known by provider");
+ case DL_BADQOSPARAM: /* QOS parameters contained invalid values */
+ return ("QOS parameters contained invalid values");
+ case DL_BADQOSTYPE: /* QOS structure type is unknown/unsupported */
+ return ("QOS structure type is unknown/unsupported");
+ case DL_BADSAP: /* Bad LSAP selector */
+ return ("Bad LSAP selector");
+ case DL_BADTOKEN: /* Token used not an active stream */
+ return ("Token used not an active stream");
+ case DL_BOUND: /* Attempted second bind with dl_max_conind */
+ return ("Attempted second bind with dl_max_conind");
+ case DL_INITFAILED: /* Physical Link initialization failed */
+ return ("Physical Link initialization failed");
+ case DL_NOADDR: /* Provider couldn't allocate alt. address */
+ return ("Provider couldn't allocate alt. address");
+ case DL_NOTINIT: /* Physical Link not initialized */
+ return ("Physical Link not initialized");
+ case DL_OUTSTATE: /* Primitive issued in improper state */
+ return ("Primitive issued in improper state");
+ case DL_SYSERR: /* UNIX system error occurred */
+ return ("UNIX system error occurred");
+ case DL_UNSUPPORTED: /* Requested serv. not supplied by provider */
+ return ("Requested serv. not supplied by provider");
+ case DL_UNDELIVERABLE: /* Previous data unit could not be delivered */
+ return ("Previous data unit could not be delivered");
+ case DL_NOTSUPPORTED: /* Primitive is known but not supported */
+ return ("Primitive is known but not supported");
+ case DL_TOOMANY: /* limit exceeded */
+ return ("Limit exceeded");
+ case DL_NOTENAB: /* Promiscuous mode not enabled */
+ return ("Promiscuous mode not enabled");
+ case DL_BUSY: /* Other streams for PPA in post-attached */
+ return ("Other streams for PPA in post-attached");
+ case DL_NOAUTO: /* Automatic handling XID&TEST not supported */
+ return ("Automatic handling XID&TEST not supported");
+ case DL_NOXIDAUTO: /* Automatic handling of XID not supported */
+ return ("Automatic handling of XID not supported");
+ case DL_NOTESTAUTO: /* Automatic handling of TEST not supported */
+ return ("Automatic handling of TEST not supported");
+ case DL_XIDAUTO: /* Automatic handling of XID response */
+ return ("Automatic handling of XID response");
+ case DL_TESTAUTO: /* AUtomatic handling of TEST response */
+ return ("Automatic handling of TEST response");
+ case DL_PENDING: /* pending outstanding connect indications */
+ return ("Pending outstanding connect indications");
+ }
+ return ("Unknown DLPI error");
+}
+
+int
+expecting(ulong_t prim, union DL_primitives *dlp, char *caller)
+{
+ if (dlp->dl_primitive == DL_ERROR_ACK) {
+ fprintf(stderr, "fatal dlpi error: %s. Device %s\n",
+ show_dltype(dlp->error_ack.dl_errno),
+ device ? device : "unknown");
+ err("%s: DL_ERROR_ACK: dl_errno %d unix_errno %d\n",
+ caller,
+ dlp->error_ack.dl_errno,
+ dlp->error_ack.dl_unix_errno);
+ return (1);
+ }
+
+ if (dlp->dl_primitive != prim) {
+ err("%s: unexpected primitive 0x%x received\n",
+ caller,
+ dlp->dl_primitive);
+ return (1);
+ }
+
+ return (0);
+}
+
+static void
+err(const char *format, ...)
+{
+ va_list alist;
+
+ va_start(alist, format);
+ (void) vfprintf(stderr, format, alist);
+ va_end(alist);
+
+ (void) fprintf(stderr, "\n");
+ exit(1);
+}
+
+void
+syserr(char *s)
+{
+ perror(s);
+ exit(1);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/fakewin.h b/usr/src/cmd/cmd-inet/usr.sbin/snoop/fakewin.h
new file mode 100644
index 0000000000..d075e0b812
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/fakewin.h
@@ -0,0 +1,71 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1993 by Sun Microsystems, Inc.
+ */
+
+#ifndef _FAKEWIN_H
+#define _FAKEWIN_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * This file defines appropriate macros so that
+ * we can use the same codebase for Unix, DOS, and Windows.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef _WINDOWS
+#include <tklib.h>
+
+#define malloc _fmalloc
+#define calloc _fcalloc
+#define free _ffree
+#define strdup _fstrdup
+#define strcpy _fstrcpy
+#define strcmp _fstrcmp
+#define strchr _fstrchr
+#define sprintf wsprintf
+#define vsprintf wvsprintf
+#define memcpy _fmemcpy
+#define strlen _fstrlen
+#else
+#define LPSTR char *
+#endif
+
+#if !defined(_WINDOWS) && !defined(_MSDOS)
+#define _TKFAR
+#endif
+
+#ifndef _WINDOWS
+#define _TKPASCAL
+#define __export
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !_FAKEWIN_H */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/fw.h b/usr/src/cmd/cmd-inet/usr.sbin/snoop/fw.h
new file mode 100644
index 0000000000..32746237c3
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/fw.h
@@ -0,0 +1,147 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1993 by Sun Microsystems, Inc.
+ */
+
+#ifndef _FW_H
+#define _FW_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <rpc/rpc.h>
+#include "fakewin.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Header file for the framework.
+ */
+#define CTXTLEN 1024
+
+struct Op_arg_item {
+ char _TKFAR *name;
+ char _TKFAR *value;
+ struct Op_arg_item _TKFAR *next;
+};
+typedef struct Op_arg_item Op_arg_item;
+
+struct Op_row_link {
+ Op_arg_item _TKFAR *first;
+ Op_arg_item _TKFAR *last;
+ struct Op_row_link _TKFAR *next;
+};
+typedef struct Op_row_link Op_row_link;
+
+struct Op_arg {
+ Op_row_link _TKFAR *first;
+ Op_row_link _TKFAR *last;
+ Op_row_link _TKFAR *curr;
+ Op_arg_item _TKFAR *cura;
+ u_long rows;
+ bool_t xdr_flag;
+};
+typedef struct Op_arg Op_arg;
+
+enum Fw_err {
+ FW_ERR_NONE = 0,
+ FW_ERR_FW = 1,
+ FW_ERR_OP = 2
+};
+typedef enum Fw_err Fw_err;
+
+struct Op_err {
+ Fw_err type;
+ u_long code;
+ bool_t xdr_flag;
+ char _TKFAR *message;
+};
+typedef struct Op_err Op_err;
+
+typedef char invk_context[CTXTLEN];
+
+struct invk_result {
+ Op_err _TKFAR *err;
+ Op_arg _TKFAR *arg;
+ bool_t eof;
+};
+typedef struct invk_result invk_result;
+
+struct invk_request {
+ char _TKFAR *category;
+ char _TKFAR *op;
+ char _TKFAR *vers;
+ char _TKFAR *locale;
+ u_long threshold;
+ invk_context context;
+ Op_arg _TKFAR *arg;
+};
+typedef struct invk_request invk_request;
+
+struct more_request {
+ invk_context context;
+ u_long threshold;
+};
+typedef struct more_request more_request;
+
+struct kill_request {
+ invk_context context;
+};
+typedef struct kill_request kill_request;
+
+#define FW_KV_DELIM "="
+#define FW_KV_DELIM_CH '='
+#define FW_VK_DELIM "\n"
+#define FW_VK_DELIM_CH '\n';
+#define FW_INPUT_VERS_VAL 1
+#define FW_INPUT_VERS_STR "1"
+#define FW_OUTPUT_VERS_VAL 1
+#define FW_OUTPUT_VERS_STR "1"
+#define FW_INPUT_VERS_KEY "_SUNW_AO_INPUT_VERS"
+#define FW_OUTPUT_VERS_KEY "_SUNW_AO_OUTPUT_VERS"
+#define FW_ROW_MARKER_KEY "_SUNW_AO_BEGIN_ROW"
+#define FW_ROW_MARKER FW_ROW_MARKER_KEY FW_KV_DELIM FW_OUTPUT_VERS_STR \
+ FW_VK_DELIM
+#define FW_INPUT_VERS FW_INPUT_VERS_KEY FW_KV_DELIM FW_INPUT_VERS_STR \
+ FW_VK_DELIM
+#define FW_OUTPUT_VERS FW_OUTPUT_VERS_KEY FW_KV_DELIM FW_OUTPUT_VERS_STR \
+ FW_VK_DELIM
+#define FW_ERR_MSG_MAX 2047
+#define FW_UNIX_USER "UU"
+
+#define FW_SUCCESS 0
+#define FW_ERROR -1
+#define FW_TIMEOUT -2
+
+#define SN_LOCALE_PATH_VAR "_SN_LOCALE_PATH"
+#define SN_UNAME_VAR "_SN_UNAME"
+#define SN_UID_VAR "_SN_UID"
+
+#include "fw_lib.h"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !_FW_H */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/fw_lib.h b/usr/src/cmd/cmd-inet/usr.sbin/snoop/fw_lib.h
new file mode 100644
index 0000000000..36e7e40fdd
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/fw_lib.h
@@ -0,0 +1,96 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1993 by Sun Microsystems, Inc.
+ */
+
+#ifndef _FW_LIB_H
+#define _FW_LIB_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __STDC__
+
+extern short net_invoke(char _TKFAR *, char _TKFAR *, char _TKFAR *,
+ char _TKFAR *, u_long, u_long, char _TKFAR *,
+ invk_context, Op_arg _TKFAR * _TKFAR *, Op_err _TKFAR * _TKFAR *, ...);
+extern short net_more(u_long, u_long, invk_context, Op_arg _TKFAR * _TKFAR *,
+ Op_err _TKFAR * _TKFAR *);
+extern short net_end(u_long, invk_context, Op_err _TKFAR * _TKFAR *);
+extern Op_arg _TKFAR *net_arg_init(void);
+extern short net_arg_set(char _TKFAR *, ...);
+extern short net_arg_markrow(void);
+extern short net_arg_get(Op_arg _TKFAR *, char _TKFAR *,
+ char _TKFAR * _TKFAR *);
+extern short net_arg_getnext(Op_arg _TKFAR *, char _TKFAR * _TKFAR *,
+ char _TKFAR * _TKFAR *);
+extern short net_arg_nextrow(Op_arg _TKFAR *);
+extern short net_arg_rowstart(Op_arg _TKFAR *);
+extern short net_arg_reset(Op_arg _TKFAR *);
+extern void net_arg_free(Op_arg _TKFAR *);
+extern void net_err_free(Op_err _TKFAR *);
+extern Op_arg _TKFAR *new_Op_arg(void);
+extern void free_Op_arg(Op_arg _TKFAR *);
+extern void free_Op_err(Op_err _TKFAR *);
+extern short append_Op_arg(Op_arg _TKFAR *, char _TKFAR *, char _TKFAR *);
+extern void fw_err_set(Op_err _TKFAR * _TKFAR *, Fw_err, u_long, ...);
+
+#ifdef _WINDOWS
+extern void net_cleanup(void);
+#endif
+
+#else
+
+extern short net_invoke();
+extern short net_more();
+extern short net_end();
+extern Op_arg _TKFAR *net_arg_init();
+extern short net_arg_set();
+extern short net_arg_markrow();
+extern short net_arg_get();
+extern short net_arg_getnext();
+extern short net_arg_nextrow();
+extern short net_arg_rowstart();
+extern short net_arg_reset();
+extern void net_arg_free();
+extern void net_err_free();
+extern Op_arg _TKFAR *new_Op_arg();
+extern void free_Op_arg();
+extern void free_Op_err();
+extern short append_Op_arg();
+extern void fw_err_set();
+
+#ifdef _WINDOWS
+extern void net_cleanup();
+#endif
+
+#endif /* __STDC__ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !_FW_LIB_H */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/fw_rpc.h b/usr/src/cmd/cmd-inet/usr.sbin/snoop/fw_rpc.h
new file mode 100644
index 0000000000..b44f3cdcac
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/fw_rpc.h
@@ -0,0 +1,78 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1993 by Sun Microsystems, Inc.
+ */
+
+/*
+ * This file contains definitions which are only of interest to the actual
+ * service daemon and client stubs. Normal framework users will not include
+ * this file.
+ */
+
+#ifndef _FW_RPC_H
+#define _FW_RPC_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define FW_NO_CONTEXT "Plead no context"
+
+#define FW_PROG ((unsigned long)(150006))
+#define FW_VERSION ((unsigned long)(1))
+
+#define FW_INVOKE ((unsigned long)(1))
+#define FW_MORE ((unsigned long)(2))
+#define FW_KILL ((unsigned long)(3))
+
+
+/* the xdr functions */
+
+extern bool_t _TKFAR _TKPASCAL xdr_Op_arg(XDR _TKFAR *, Op_arg _TKFAR *);
+extern bool_t _TKFAR _TKPASCAL xdr_Fw_err(XDR _TKFAR *, Fw_err _TKFAR *);
+extern bool_t _TKFAR _TKPASCAL xdr_Op_err(XDR _TKFAR *, Op_err _TKFAR *);
+extern bool_t _TKFAR _TKPASCAL xdr_invk_context(XDR _TKFAR *, invk_context);
+extern bool_t _TKFAR _TKPASCAL xdr_invk_result(XDR _TKFAR *,
+ invk_result _TKFAR *);
+extern bool_t _TKFAR _TKPASCAL xdr_invk_request(XDR _TKFAR *,
+ invk_request _TKFAR *);
+extern bool_t _TKFAR _TKPASCAL xdr_more_request(XDR _TKFAR *,
+ more_request _TKFAR *);
+extern bool_t _TKFAR _TKPASCAL xdr_kill_request(XDR _TKFAR *,
+ kill_request _TKFAR *);
+
+#ifdef _WINDOWS
+extern void thunk_xdrs(void);
+extern void unthunk_xdrs(void);
+extern FARPROC lp_xdr_invk_request, lp_xdr_invk_result;
+extern FARPROC lp_xdr_more_request, lp_xdr_kill_request;
+extern FARPROC lp_xdr_Op_err, lp_xdr_Op_arg;
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !_FW_RPC_H */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/inc.flg b/usr/src/cmd/cmd-inet/usr.sbin/snoop/inc.flg
new file mode 100644
index 0000000000..d7246ea4b0
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/inc.flg
@@ -0,0 +1,30 @@
+#!/bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+find_files "s.*" usr/src/common/net/dhcp
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/nfs4_xdr.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/nfs4_xdr.c
new file mode 100644
index 0000000000..0ee7a6c4e7
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/nfs4_xdr.c
@@ -0,0 +1,2972 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * This file was initially generated using rpcgen. The rpcgen-erated
+ * code used tail recursion to implement linked lists which resulted
+ * in various crashes due to blown stacks. If the NFS4 protocol changes
+ * be sure to either use the NFS4-friendly rpcgen (doesn't use tail
+ * recursion) or do the xdr by hand.
+ *
+ * CAUTION: This file is kept in sync with it's uts counterpart:
+ *
+ * usr/src/uts/common/fs/nfs/nfs4_xdr.c
+ *
+ * However, it is not an exact copy. NEVER copy uts's nfs4_xdr.c
+ * directly over this file. Changes from the uts version must be
+ * integrated by hand into this file.
+ */
+
+#include <rpcsvc/nfs4_prot.h>
+#include <nfs/nfs4.h>
+#include <malloc.h>
+
+#define IGNORE_RDWR_DATA
+
+extern int nfs4_skip_bytes;
+
+bool_t
+xdr_nfs_ftype4(register XDR *xdrs, nfs_ftype4 *objp)
+{
+
+ if (!xdr_enum(xdrs, (enum_t *)objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_nfsstat4(register XDR *xdrs, nfsstat4 *objp)
+{
+
+ if (!xdr_enum(xdrs, (enum_t *)objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_bitmap4(register XDR *xdrs, bitmap4 *objp)
+{
+
+ if (!xdr_array(xdrs, (char **)&objp->bitmap4_val,
+ (uint_t *)&objp->bitmap4_len, ~0,
+ sizeof (uint32_t), (xdrproc_t)xdr_uint32_t))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_offset4(register XDR *xdrs, offset4 *objp)
+{
+
+ if (!xdr_uint64_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_count4(register XDR *xdrs, count4 *objp)
+{
+
+ if (!xdr_uint32_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_length4(register XDR *xdrs, length4 *objp)
+{
+
+ if (!xdr_uint64_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_clientid4(register XDR *xdrs, clientid4 *objp)
+{
+
+ if (!xdr_uint64_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_seqid4(register XDR *xdrs, seqid4 *objp)
+{
+
+ if (!xdr_uint32_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_utf8string(register XDR *xdrs, utf8string *objp)
+{
+
+ if (!xdr_bytes(xdrs, (char **)&objp->utf8string_val,
+ (uint_t *)&objp->utf8string_len, NFS4_MAX_UTF8STRING))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_component4(register XDR *xdrs, component4 *objp)
+{
+
+ if (!xdr_utf8string(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_pathname4(register XDR *xdrs, pathname4 *objp)
+{
+
+ if (!xdr_array(xdrs, (char **)&objp->pathname4_val,
+ (uint_t *)&objp->pathname4_len, NFS4_MAX_PATHNAME4,
+ sizeof (component4), (xdrproc_t)xdr_component4))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_nfs_lockid4(register XDR *xdrs, nfs_lockid4 *objp)
+{
+
+ if (!xdr_uint64_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_nfs_cookie4(register XDR *xdrs, nfs_cookie4 *objp)
+{
+
+ if (!xdr_uint64_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_linktext4(register XDR *xdrs, linktext4 *objp)
+{
+
+ if (!xdr_utf8string(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_sec_oid4(register XDR *xdrs, sec_oid4 *objp)
+{
+
+ if (!xdr_bytes(xdrs, (char **)&objp->sec_oid4_val,
+ (uint_t *)&objp->sec_oid4_len, NFS4_MAX_SECOID4))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_qop4(register XDR *xdrs, qop4 *objp)
+{
+
+ if (!xdr_uint32_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_mode4(register XDR *xdrs, mode4 *objp)
+{
+
+ if (!xdr_uint32_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_changeid4(register XDR *xdrs, changeid4 *objp)
+{
+
+ if (!xdr_uint64_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_verifier4(register XDR *xdrs, verifier4 objp)
+{
+
+ if (!xdr_opaque(xdrs, objp, NFS4_VERIFIER_SIZE))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_nfstime4(register XDR *xdrs, nfstime4 *objp)
+{
+
+ if (!xdr_int64_t(xdrs, &objp->seconds))
+ return (FALSE);
+ if (!xdr_uint32_t(xdrs, &objp->nseconds))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_time_how4(register XDR *xdrs, time_how4 *objp)
+{
+
+ if (!xdr_enum(xdrs, (enum_t *)objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_settime4(register XDR *xdrs, settime4 *objp)
+{
+
+ if (!xdr_time_how4(xdrs, &objp->set_it))
+ return (FALSE);
+ switch (objp->set_it) {
+ case SET_TO_CLIENT_TIME4:
+ if (!xdr_nfstime4(xdrs, &objp->settime4_u.time))
+ return (FALSE);
+ break;
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_nfs_fh4(register XDR *xdrs, nfs_fh4 *objp)
+{
+
+ if (!xdr_bytes(xdrs, (char **)&objp->nfs_fh4_val,
+ (uint_t *)&objp->nfs_fh4_len, NFS4_FHSIZE))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fsid4(register XDR *xdrs, fsid4 *objp)
+{
+
+ if (!xdr_uint64_t(xdrs, &objp->major))
+ return (FALSE);
+ if (!xdr_uint64_t(xdrs, &objp->minor))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fs_location4(register XDR *xdrs, fs_location4 *objp)
+{
+
+ if (!xdr_array(xdrs, (char **)&objp->server.server_val,
+ (uint_t *)&objp->server.server_len, ~0,
+ sizeof (utf8string), (xdrproc_t)xdr_utf8string))
+ return (FALSE);
+ if (!xdr_pathname4(xdrs, &objp->rootpath))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fs_locations4(register XDR *xdrs, fs_locations4 *objp)
+{
+
+ if (!xdr_pathname4(xdrs, &objp->fs_root))
+ return (FALSE);
+ if (!xdr_array(xdrs, (char **)&objp->locations.locations_val,
+ (uint_t *)&objp->locations.locations_len, ~0,
+ sizeof (fs_location4), (xdrproc_t)xdr_fs_location4))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_acetype4(register XDR *xdrs, acetype4 *objp)
+{
+
+ if (!xdr_u_int(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_aceflag4(register XDR *xdrs, aceflag4 *objp)
+{
+
+ if (!xdr_u_int(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_acemask4(register XDR *xdrs, acemask4 *objp)
+{
+
+ if (!xdr_u_int(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_nfsace4(register XDR *xdrs, nfsace4 *objp)
+{
+ if (!xdr_acetype4(xdrs, &objp->type))
+ return (FALSE);
+ if (!xdr_aceflag4(xdrs, &objp->flag))
+ return (FALSE);
+ if (!xdr_acemask4(xdrs, &objp->access_mask))
+ return (FALSE);
+ if (xdrs->x_op == XDR_DECODE) {
+ objp->who.utf8string_val = NULL;
+ objp->who.utf8string_len = 0;
+ }
+ return (xdr_bytes(xdrs, (char **)&objp->who.utf8string_val,
+ (uint_t *)&objp->who.utf8string_len,
+ NFS4_MAX_UTF8STRING));
+}
+
+bool_t
+xdr_specdata4(register XDR *xdrs, specdata4 *objp)
+{
+
+ if (!xdr_uint32_t(xdrs, &objp->specdata1))
+ return (FALSE);
+ if (!xdr_uint32_t(xdrs, &objp->specdata2))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_supported_attrs(register XDR *xdrs, fattr4_supported_attrs *objp)
+{
+
+ if (!xdr_bitmap4(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_type(register XDR *xdrs, fattr4_type *objp)
+{
+
+ if (!xdr_nfs_ftype4(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_fh_expire_type(register XDR *xdrs, fattr4_fh_expire_type *objp)
+{
+
+ if (!xdr_uint32_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_change(register XDR *xdrs, fattr4_change *objp)
+{
+
+ if (!xdr_changeid4(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_size(register XDR *xdrs, fattr4_size *objp)
+{
+
+ if (!xdr_uint64_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_link_support(register XDR *xdrs, fattr4_link_support *objp)
+{
+
+ if (!xdr_bool(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_symlink_support(register XDR *xdrs, fattr4_symlink_support *objp)
+{
+
+ if (!xdr_bool(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_named_attr(register XDR *xdrs, fattr4_named_attr *objp)
+{
+
+ if (!xdr_bool(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_fsid(register XDR *xdrs, fattr4_fsid *objp)
+{
+
+ if (!xdr_fsid4(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_unique_handles(register XDR *xdrs, fattr4_unique_handles *objp)
+{
+
+ if (!xdr_bool(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_lease_time(register XDR *xdrs, fattr4_lease_time *objp)
+{
+
+ if (!xdr_uint32_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_rdattr_error(register XDR *xdrs, fattr4_rdattr_error *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_acl(register XDR *xdrs, fattr4_acl *objp)
+{
+
+ if (!xdr_array(xdrs, (char **)&objp->fattr4_acl_val,
+ (uint_t *)&objp->fattr4_acl_len, ~0,
+ sizeof (nfsace4), (xdrproc_t)xdr_nfsace4))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_aclsupport(register XDR *xdrs, fattr4_aclsupport *objp)
+{
+
+ if (!xdr_uint32_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_archive(register XDR *xdrs, fattr4_archive *objp)
+{
+
+ if (!xdr_bool(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_cansettime(register XDR *xdrs, fattr4_cansettime *objp)
+{
+
+ if (!xdr_bool(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_case_insensitive(register XDR *xdrs, fattr4_case_insensitive *objp)
+{
+
+ if (!xdr_bool(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_case_preserving(register XDR *xdrs, fattr4_case_preserving *objp)
+{
+
+ if (!xdr_bool(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_chown_restricted(register XDR *xdrs, fattr4_chown_restricted *objp)
+{
+
+ if (!xdr_bool(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_fileid(register XDR *xdrs, fattr4_fileid *objp)
+{
+
+ if (!xdr_uint64_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_files_avail(register XDR *xdrs, fattr4_files_avail *objp)
+{
+
+ if (!xdr_uint64_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_filehandle(register XDR *xdrs, fattr4_filehandle *objp)
+{
+
+ if (!xdr_nfs_fh4(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_files_free(register XDR *xdrs, fattr4_files_free *objp)
+{
+
+ if (!xdr_uint64_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_files_total(register XDR *xdrs, fattr4_files_total *objp)
+{
+
+ if (!xdr_uint64_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_fs_locations(register XDR *xdrs, fattr4_fs_locations *objp)
+{
+
+ if (!xdr_fs_locations4(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_hidden(register XDR *xdrs, fattr4_hidden *objp)
+{
+
+ if (!xdr_bool(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_homogeneous(register XDR *xdrs, fattr4_homogeneous *objp)
+{
+
+ if (!xdr_bool(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_maxfilesize(register XDR *xdrs, fattr4_maxfilesize *objp)
+{
+
+ if (!xdr_uint64_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_maxlink(register XDR *xdrs, fattr4_maxlink *objp)
+{
+
+ if (!xdr_uint32_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_maxname(register XDR *xdrs, fattr4_maxname *objp)
+{
+
+ if (!xdr_uint32_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_maxread(register XDR *xdrs, fattr4_maxread *objp)
+{
+
+ if (!xdr_uint64_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_maxwrite(register XDR *xdrs, fattr4_maxwrite *objp)
+{
+
+ if (!xdr_uint64_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_mimetype(register XDR *xdrs, fattr4_mimetype *objp)
+{
+
+ if (!xdr_utf8string(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_mode(register XDR *xdrs, fattr4_mode *objp)
+{
+
+ if (!xdr_mode4(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_mounted_on_fileid(register XDR *xdrs, fattr4_mounted_on_fileid *objp)
+{
+
+ if (!xdr_uint64_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_no_trunc(register XDR *xdrs, fattr4_no_trunc *objp)
+{
+
+ if (!xdr_bool(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_numlinks(register XDR *xdrs, fattr4_numlinks *objp)
+{
+
+ if (!xdr_uint32_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_owner(register XDR *xdrs, fattr4_owner *objp)
+{
+
+ if (!xdr_utf8string(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_owner_group(register XDR *xdrs, fattr4_owner_group *objp)
+{
+
+ if (!xdr_utf8string(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_quota_avail_hard(register XDR *xdrs, fattr4_quota_avail_hard *objp)
+{
+
+ if (!xdr_uint64_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_quota_avail_soft(register XDR *xdrs, fattr4_quota_avail_soft *objp)
+{
+
+ if (!xdr_uint64_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_quota_used(register XDR *xdrs, fattr4_quota_used *objp)
+{
+
+ if (!xdr_uint64_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_rawdev(register XDR *xdrs, fattr4_rawdev *objp)
+{
+
+ if (!xdr_specdata4(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_space_avail(register XDR *xdrs, fattr4_space_avail *objp)
+{
+
+ if (!xdr_uint64_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_space_free(register XDR *xdrs, fattr4_space_free *objp)
+{
+
+ if (!xdr_uint64_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_space_total(register XDR *xdrs, fattr4_space_total *objp)
+{
+
+ if (!xdr_uint64_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_space_used(register XDR *xdrs, fattr4_space_used *objp)
+{
+
+ if (!xdr_uint64_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_system(register XDR *xdrs, fattr4_system *objp)
+{
+
+ if (!xdr_bool(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_time_access(register XDR *xdrs, fattr4_time_access *objp)
+{
+
+ if (!xdr_nfstime4(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_time_access_set(register XDR *xdrs, fattr4_time_access_set *objp)
+{
+
+ if (!xdr_settime4(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_time_backup(register XDR *xdrs, fattr4_time_backup *objp)
+{
+
+ if (!xdr_nfstime4(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_time_create(register XDR *xdrs, fattr4_time_create *objp)
+{
+
+ if (!xdr_nfstime4(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_time_delta(register XDR *xdrs, fattr4_time_delta *objp)
+{
+
+ if (!xdr_nfstime4(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_time_metadata(register XDR *xdrs, fattr4_time_metadata *objp)
+{
+
+ if (!xdr_nfstime4(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_time_modify(register XDR *xdrs, fattr4_time_modify *objp)
+{
+
+ if (!xdr_nfstime4(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_time_modify_set(register XDR *xdrs, fattr4_time_modify_set *objp)
+{
+
+ if (!xdr_settime4(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_attrlist4(register XDR *xdrs, attrlist4 *objp)
+{
+
+ if (!xdr_bytes(xdrs, (char **)&objp->attrlist4_val,
+ (uint_t *)&objp->attrlist4_len, ~0))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4(register XDR *xdrs, fattr4 *objp)
+{
+
+ if (!xdr_bitmap4(xdrs, &objp->attrmask))
+ return (FALSE);
+ if (!xdr_attrlist4(xdrs, &objp->attr_vals))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_change_info4(register XDR *xdrs, change_info4 *objp)
+{
+
+ if (!xdr_bool(xdrs, &objp->atomic))
+ return (FALSE);
+ if (!xdr_changeid4(xdrs, &objp->before))
+ return (FALSE);
+ if (!xdr_changeid4(xdrs, &objp->after))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_clientaddr4(register XDR *xdrs, clientaddr4 *objp)
+{
+
+ if (!xdr_string(xdrs, &objp->r_netid, ~0))
+ return (FALSE);
+ if (!xdr_string(xdrs, &objp->r_addr, ~0))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_cb_client4(register XDR *xdrs, cb_client4 *objp)
+{
+
+ if (!xdr_uint32_t(xdrs, &objp->cb_program))
+ return (FALSE);
+ if (!xdr_clientaddr4(xdrs, &objp->cb_location))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_stateid4(register XDR *xdrs, stateid4 *objp)
+{
+
+ if (!xdr_uint32_t(xdrs, &objp->seqid))
+ return (FALSE);
+ if (!xdr_opaque(xdrs, objp->other, 12))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_nfs_client_id4(register XDR *xdrs, nfs_client_id4 *objp)
+{
+
+ if (!xdr_verifier4(xdrs, objp->verifier))
+ return (FALSE);
+ if (!xdr_bytes(xdrs, (char **)&objp->id.id_val,
+ (uint_t *)&objp->id.id_len, NFS4_OPAQUE_LIMIT))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_open_owner4(register XDR *xdrs, open_owner4 *objp)
+{
+
+ if (!xdr_clientid4(xdrs, &objp->clientid))
+ return (FALSE);
+ if (!xdr_bytes(xdrs, (char **)&objp->owner.owner_val,
+ (uint_t *)&objp->owner.owner_len, NFS4_OPAQUE_LIMIT))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_lock_owner4(register XDR *xdrs, lock_owner4 *objp)
+{
+
+ if (!xdr_clientid4(xdrs, &objp->clientid))
+ return (FALSE);
+ if (!xdr_bytes(xdrs, (char **)&objp->owner.owner_val,
+ (uint_t *)&objp->owner.owner_len, NFS4_OPAQUE_LIMIT))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_nfs_lock_type4(register XDR *xdrs, nfs_lock_type4 *objp)
+{
+
+ if (!xdr_enum(xdrs, (enum_t *)objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_ACCESS4args(register XDR *xdrs, ACCESS4args *objp)
+{
+
+ if (!xdr_uint32_t(xdrs, &objp->access))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_ACCESS4resok(register XDR *xdrs, ACCESS4resok *objp)
+{
+
+ if (!xdr_uint32_t(xdrs, &objp->supported))
+ return (FALSE);
+ if (!xdr_uint32_t(xdrs, &objp->access))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_ACCESS4res(register XDR *xdrs, ACCESS4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ switch (objp->status) {
+ case NFS4_OK:
+ if (!xdr_ACCESS4resok(xdrs, &objp->ACCESS4res_u.resok4))
+ return (FALSE);
+ break;
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_CLOSE4args(register XDR *xdrs, CLOSE4args *objp)
+{
+
+ if (!xdr_seqid4(xdrs, &objp->seqid))
+ return (FALSE);
+ if (!xdr_stateid4(xdrs, &objp->open_stateid))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_CLOSE4res(register XDR *xdrs, CLOSE4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ switch (objp->status) {
+ case NFS4_OK:
+ if (!xdr_stateid4(xdrs, &objp->CLOSE4res_u.open_stateid))
+ return (FALSE);
+ break;
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_COMMIT4args(register XDR *xdrs, COMMIT4args *objp)
+{
+
+ if (!xdr_offset4(xdrs, &objp->offset))
+ return (FALSE);
+ if (!xdr_count4(xdrs, &objp->count))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_COMMIT4resok(register XDR *xdrs, COMMIT4resok *objp)
+{
+
+ if (!xdr_verifier4(xdrs, objp->writeverf))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_COMMIT4res(register XDR *xdrs, COMMIT4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ switch (objp->status) {
+ case NFS4_OK:
+ if (!xdr_COMMIT4resok(xdrs, &objp->COMMIT4res_u.resok4))
+ return (FALSE);
+ break;
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_createtype4(register XDR *xdrs, createtype4 *objp)
+{
+
+ if (!xdr_nfs_ftype4(xdrs, &objp->type))
+ return (FALSE);
+ switch (objp->type) {
+ case NF4LNK:
+ if (!xdr_linktext4(xdrs, &objp->createtype4_u.linkdata))
+ return (FALSE);
+ break;
+ case NF4BLK:
+ case NF4CHR:
+ if (!xdr_specdata4(xdrs, &objp->createtype4_u.devdata))
+ return (FALSE);
+ break;
+ case NF4SOCK:
+ case NF4FIFO:
+ case NF4DIR:
+ break;
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_CREATE4args(register XDR *xdrs, CREATE4args *objp)
+{
+
+ if (!xdr_createtype4(xdrs, &objp->objtype))
+ return (FALSE);
+ if (!xdr_component4(xdrs, &objp->objname))
+ return (FALSE);
+ if (!xdr_fattr4(xdrs, &objp->createattrs))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_CREATE4resok(register XDR *xdrs, CREATE4resok *objp)
+{
+
+ if (!xdr_change_info4(xdrs, &objp->cinfo))
+ return (FALSE);
+ if (!xdr_bitmap4(xdrs, &objp->attrset))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_CREATE4res(register XDR *xdrs, CREATE4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ switch (objp->status) {
+ case NFS4_OK:
+ if (!xdr_CREATE4resok(xdrs, &objp->CREATE4res_u.resok4))
+ return (FALSE);
+ break;
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_DELEGPURGE4args(register XDR *xdrs, DELEGPURGE4args *objp)
+{
+
+ if (!xdr_clientid4(xdrs, &objp->clientid))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_DELEGPURGE4res(register XDR *xdrs, DELEGPURGE4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_DELEGRETURN4args(register XDR *xdrs, DELEGRETURN4args *objp)
+{
+
+ if (!xdr_stateid4(xdrs, &objp->deleg_stateid))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_DELEGRETURN4res(register XDR *xdrs, DELEGRETURN4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_GETATTR4args(register XDR *xdrs, GETATTR4args *objp)
+{
+
+ if (!xdr_bitmap4(xdrs, &objp->attr_request))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_GETATTR4resok(register XDR *xdrs, GETATTR4resok *objp)
+{
+
+ if (!xdr_fattr4(xdrs, &objp->obj_attributes))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_GETATTR4res(register XDR *xdrs, GETATTR4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ switch (objp->status) {
+ case NFS4_OK:
+ if (!xdr_GETATTR4resok(xdrs, &objp->GETATTR4res_u.resok4))
+ return (FALSE);
+ break;
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_GETFH4resok(register XDR *xdrs, GETFH4resok *objp)
+{
+
+ if (!xdr_nfs_fh4(xdrs, &objp->object))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_GETFH4res(register XDR *xdrs, GETFH4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ switch (objp->status) {
+ case NFS4_OK:
+ if (!xdr_GETFH4resok(xdrs, &objp->GETFH4res_u.resok4))
+ return (FALSE);
+ break;
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_LINK4args(register XDR *xdrs, LINK4args *objp)
+{
+
+ if (!xdr_component4(xdrs, &objp->newname))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_LINK4resok(register XDR *xdrs, LINK4resok *objp)
+{
+
+ if (!xdr_change_info4(xdrs, &objp->cinfo))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_LINK4res(register XDR *xdrs, LINK4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ switch (objp->status) {
+ case NFS4_OK:
+ if (!xdr_LINK4resok(xdrs, &objp->LINK4res_u.resok4))
+ return (FALSE);
+ break;
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_open_to_lock_owner4(register XDR *xdrs, open_to_lock_owner4 *objp)
+{
+
+ if (!xdr_seqid4(xdrs, &objp->open_seqid))
+ return (FALSE);
+ if (!xdr_stateid4(xdrs, &objp->open_stateid))
+ return (FALSE);
+ if (!xdr_seqid4(xdrs, &objp->lock_seqid))
+ return (FALSE);
+ if (!xdr_lock_owner4(xdrs, &objp->lock_owner))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_exist_lock_owner4(register XDR *xdrs, exist_lock_owner4 *objp)
+{
+
+ if (!xdr_stateid4(xdrs, &objp->lock_stateid))
+ return (FALSE);
+ if (!xdr_seqid4(xdrs, &objp->lock_seqid))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_locker4(register XDR *xdrs, locker4 *objp)
+{
+
+ if (!xdr_bool(xdrs, &objp->new_lock_owner))
+ return (FALSE);
+ switch (objp->new_lock_owner) {
+ case TRUE:
+ if (!xdr_open_to_lock_owner4(xdrs, &objp->locker4_u.open_owner))
+ return (FALSE);
+ break;
+ case FALSE:
+ if (!xdr_exist_lock_owner4(xdrs, &objp->locker4_u.lock_owner))
+ return (FALSE);
+ break;
+ default:
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_LOCK4args(register XDR *xdrs, LOCK4args *objp)
+{
+
+ if (!xdr_nfs_lock_type4(xdrs, &objp->locktype))
+ return (FALSE);
+ if (!xdr_bool(xdrs, &objp->reclaim))
+ return (FALSE);
+ if (!xdr_offset4(xdrs, &objp->offset))
+ return (FALSE);
+ if (!xdr_length4(xdrs, &objp->length))
+ return (FALSE);
+ if (!xdr_locker4(xdrs, &objp->locker))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_LOCK4denied(register XDR *xdrs, LOCK4denied *objp)
+{
+
+ if (!xdr_offset4(xdrs, &objp->offset))
+ return (FALSE);
+ if (!xdr_length4(xdrs, &objp->length))
+ return (FALSE);
+ if (!xdr_nfs_lock_type4(xdrs, &objp->locktype))
+ return (FALSE);
+ if (!xdr_lock_owner4(xdrs, &objp->owner))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_LOCK4resok(register XDR *xdrs, LOCK4resok *objp)
+{
+
+ if (!xdr_stateid4(xdrs, &objp->lock_stateid))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_LOCK4res(register XDR *xdrs, LOCK4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ switch (objp->status) {
+ case NFS4_OK:
+ if (!xdr_LOCK4resok(xdrs, &objp->LOCK4res_u.resok4))
+ return (FALSE);
+ break;
+ case NFS4ERR_DENIED:
+ if (!xdr_LOCK4denied(xdrs, &objp->LOCK4res_u.denied))
+ return (FALSE);
+ break;
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_LOCKT4args(register XDR *xdrs, LOCKT4args *objp)
+{
+
+ if (!xdr_nfs_lock_type4(xdrs, &objp->locktype))
+ return (FALSE);
+ if (!xdr_offset4(xdrs, &objp->offset))
+ return (FALSE);
+ if (!xdr_length4(xdrs, &objp->length))
+ return (FALSE);
+ if (!xdr_lock_owner4(xdrs, &objp->owner))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_LOCKT4res(register XDR *xdrs, LOCKT4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ switch (objp->status) {
+ case NFS4ERR_DENIED:
+ if (!xdr_LOCK4denied(xdrs, &objp->LOCKT4res_u.denied))
+ return (FALSE);
+ break;
+ case NFS4_OK:
+ break;
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_LOCKU4args(register XDR *xdrs, LOCKU4args *objp)
+{
+
+ if (!xdr_nfs_lock_type4(xdrs, &objp->locktype))
+ return (FALSE);
+ if (!xdr_seqid4(xdrs, &objp->seqid))
+ return (FALSE);
+ if (!xdr_stateid4(xdrs, &objp->lock_stateid))
+ return (FALSE);
+ if (!xdr_offset4(xdrs, &objp->offset))
+ return (FALSE);
+ if (!xdr_length4(xdrs, &objp->length))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_LOCKU4res(register XDR *xdrs, LOCKU4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ switch (objp->status) {
+ case NFS4_OK:
+ if (!xdr_stateid4(xdrs, &objp->LOCKU4res_u.lock_stateid))
+ return (FALSE);
+ break;
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_LOOKUP4args(register XDR *xdrs, LOOKUP4args *objp)
+{
+
+ if (!xdr_component4(xdrs, &objp->objname))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_LOOKUP4res(register XDR *xdrs, LOOKUP4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_LOOKUPP4res(register XDR *xdrs, LOOKUPP4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_NVERIFY4args(register XDR *xdrs, NVERIFY4args *objp)
+{
+
+ if (!xdr_fattr4(xdrs, &objp->obj_attributes))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_NVERIFY4res(register XDR *xdrs, NVERIFY4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_createmode4(register XDR *xdrs, createmode4 *objp)
+{
+
+ if (!xdr_enum(xdrs, (enum_t *)objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_createhow4(register XDR *xdrs, createhow4 *objp)
+{
+
+ if (!xdr_createmode4(xdrs, &objp->mode))
+ return (FALSE);
+ switch (objp->mode) {
+ case UNCHECKED4:
+ case GUARDED4:
+ if (!xdr_fattr4(xdrs, &objp->createhow4_u.createattrs))
+ return (FALSE);
+ break;
+ case EXCLUSIVE4:
+ if (!xdr_verifier4(xdrs, objp->createhow4_u.createverf))
+ return (FALSE);
+ break;
+ default:
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_opentype4(register XDR *xdrs, opentype4 *objp)
+{
+
+ if (!xdr_enum(xdrs, (enum_t *)objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_openflag4(register XDR *xdrs, openflag4 *objp)
+{
+
+ if (!xdr_opentype4(xdrs, &objp->opentype))
+ return (FALSE);
+ switch (objp->opentype) {
+ case OPEN4_CREATE:
+ if (!xdr_createhow4(xdrs, &objp->openflag4_u.how))
+ return (FALSE);
+ break;
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_limit_by4(register XDR *xdrs, limit_by4 *objp)
+{
+
+ if (!xdr_enum(xdrs, (enum_t *)objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_nfs_modified_limit4(register XDR *xdrs, nfs_modified_limit4 *objp)
+{
+
+ if (!xdr_uint32_t(xdrs, &objp->num_blocks))
+ return (FALSE);
+ if (!xdr_uint32_t(xdrs, &objp->bytes_per_block))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_nfs_space_limit4(register XDR *xdrs, nfs_space_limit4 *objp)
+{
+
+ if (!xdr_limit_by4(xdrs, &objp->limitby))
+ return (FALSE);
+ switch (objp->limitby) {
+ case NFS_LIMIT_SIZE:
+ if (!xdr_uint64_t(xdrs, &objp->nfs_space_limit4_u.filesize))
+ return (FALSE);
+ break;
+ case NFS_LIMIT_BLOCKS:
+ if (!xdr_nfs_modified_limit4(xdrs, &objp->nfs_space_limit4_u.
+ mod_blocks))
+ return (FALSE);
+ break;
+ default:
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_open_delegation_type4(register XDR *xdrs, open_delegation_type4 *objp)
+{
+
+ if (!xdr_enum(xdrs, (enum_t *)objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_open_claim_type4(register XDR *xdrs, open_claim_type4 *objp)
+{
+
+ if (!xdr_enum(xdrs, (enum_t *)objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_open_claim_delegate_cur4(register XDR *xdrs, open_claim_delegate_cur4 *objp)
+{
+
+ if (!xdr_stateid4(xdrs, &objp->delegate_stateid))
+ return (FALSE);
+ if (!xdr_component4(xdrs, &objp->file))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_open_claim4(register XDR *xdrs, open_claim4 *objp)
+{
+
+ if (!xdr_open_claim_type4(xdrs, &objp->claim))
+ return (FALSE);
+ switch (objp->claim) {
+ case CLAIM_NULL:
+ if (!xdr_component4(xdrs, &objp->open_claim4_u.file))
+ return (FALSE);
+ break;
+ case CLAIM_PREVIOUS:
+ if (!xdr_open_delegation_type4(xdrs, &objp->open_claim4_u.
+ delegate_type))
+ return (FALSE);
+ break;
+ case CLAIM_DELEGATE_CUR:
+ if (!xdr_open_claim_delegate_cur4(xdrs, &objp->open_claim4_u.
+ delegate_cur_info))
+ return (FALSE);
+ break;
+ case CLAIM_DELEGATE_PREV:
+ if (!xdr_component4(xdrs, &objp->open_claim4_u.
+ file_delegate_prev))
+ return (FALSE);
+ break;
+ default:
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_OPEN4args(register XDR *xdrs, OPEN4args *objp)
+{
+
+ if (!xdr_seqid4(xdrs, &objp->seqid))
+ return (FALSE);
+ if (!xdr_uint32_t(xdrs, &objp->share_access))
+ return (FALSE);
+ if (!xdr_uint32_t(xdrs, &objp->share_deny))
+ return (FALSE);
+ if (!xdr_open_owner4(xdrs, &objp->owner))
+ return (FALSE);
+ if (!xdr_openflag4(xdrs, &objp->openhow))
+ return (FALSE);
+ if (!xdr_open_claim4(xdrs, &objp->claim))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_open_read_delegation4(register XDR *xdrs, open_read_delegation4 *objp)
+{
+
+ if (!xdr_stateid4(xdrs, &objp->stateid))
+ return (FALSE);
+ if (!xdr_bool(xdrs, &objp->recall))
+ return (FALSE);
+ if (!xdr_nfsace4(xdrs, &objp->permissions))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_open_write_delegation4(register XDR *xdrs, open_write_delegation4 *objp)
+{
+
+ if (!xdr_stateid4(xdrs, &objp->stateid))
+ return (FALSE);
+ if (!xdr_bool(xdrs, &objp->recall))
+ return (FALSE);
+ if (!xdr_nfs_space_limit4(xdrs, &objp->space_limit))
+ return (FALSE);
+ if (!xdr_nfsace4(xdrs, &objp->permissions))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_open_delegation4(register XDR *xdrs, open_delegation4 *objp)
+{
+
+ if (!xdr_open_delegation_type4(xdrs, &objp->delegation_type))
+ return (FALSE);
+ switch (objp->delegation_type) {
+ case OPEN_DELEGATE_NONE:
+ break;
+ case OPEN_DELEGATE_READ:
+ if (!xdr_open_read_delegation4(xdrs, &objp->open_delegation4_u.
+ read))
+ return (FALSE);
+ break;
+ case OPEN_DELEGATE_WRITE:
+ if (!xdr_open_write_delegation4(xdrs, &objp->open_delegation4_u.
+ write))
+ return (FALSE);
+ break;
+ default:
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_OPEN4resok(register XDR *xdrs, OPEN4resok *objp)
+{
+
+ if (!xdr_stateid4(xdrs, &objp->stateid))
+ return (FALSE);
+ if (!xdr_change_info4(xdrs, &objp->cinfo))
+ return (FALSE);
+ if (!xdr_uint32_t(xdrs, &objp->rflags))
+ return (FALSE);
+ if (!xdr_bitmap4(xdrs, &objp->attrset))
+ return (FALSE);
+ if (!xdr_open_delegation4(xdrs, &objp->delegation))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_OPEN4res(register XDR *xdrs, OPEN4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ switch (objp->status) {
+ case NFS4_OK:
+ if (!xdr_OPEN4resok(xdrs, &objp->OPEN4res_u.resok4))
+ return (FALSE);
+ break;
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_OPENATTR4args(register XDR *xdrs, OPENATTR4args *objp)
+{
+
+ if (!xdr_bool(xdrs, &objp->createdir))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_OPENATTR4res(register XDR *xdrs, OPENATTR4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_OPEN_CONFIRM4args(register XDR *xdrs, OPEN_CONFIRM4args *objp)
+{
+
+ if (!xdr_stateid4(xdrs, &objp->open_stateid))
+ return (FALSE);
+ if (!xdr_seqid4(xdrs, &objp->seqid))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_OPEN_CONFIRM4resok(register XDR *xdrs, OPEN_CONFIRM4resok *objp)
+{
+
+ if (!xdr_stateid4(xdrs, &objp->open_stateid))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_OPEN_CONFIRM4res(register XDR *xdrs, OPEN_CONFIRM4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ switch (objp->status) {
+ case NFS4_OK:
+ if (!xdr_OPEN_CONFIRM4resok(xdrs, &objp->OPEN_CONFIRM4res_u.
+ resok4))
+ return (FALSE);
+ break;
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_OPEN_DOWNGRADE4args(register XDR *xdrs, OPEN_DOWNGRADE4args *objp)
+{
+
+ if (!xdr_stateid4(xdrs, &objp->open_stateid))
+ return (FALSE);
+ if (!xdr_seqid4(xdrs, &objp->seqid))
+ return (FALSE);
+ if (!xdr_uint32_t(xdrs, &objp->share_access))
+ return (FALSE);
+ if (!xdr_uint32_t(xdrs, &objp->share_deny))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_OPEN_DOWNGRADE4resok(register XDR *xdrs, OPEN_DOWNGRADE4resok *objp)
+{
+
+ if (!xdr_stateid4(xdrs, &objp->open_stateid))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_OPEN_DOWNGRADE4res(register XDR *xdrs, OPEN_DOWNGRADE4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ switch (objp->status) {
+ case NFS4_OK:
+ if (!xdr_OPEN_DOWNGRADE4resok(xdrs, &objp->OPEN_DOWNGRADE4res_u.
+ resok4))
+ return (FALSE);
+ break;
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_PUTFH4args(register XDR *xdrs, PUTFH4args *objp)
+{
+
+ if (!xdr_nfs_fh4(xdrs, &objp->object))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_PUTFH4res(register XDR *xdrs, PUTFH4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_PUTPUBFH4res(register XDR *xdrs, PUTPUBFH4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_PUTROOTFH4res(register XDR *xdrs, PUTROOTFH4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_READ4args(register XDR *xdrs, READ4args *objp)
+{
+
+ if (!xdr_stateid4(xdrs, &objp->stateid))
+ return (FALSE);
+ if (!xdr_offset4(xdrs, &objp->offset))
+ return (FALSE);
+ if (!xdr_count4(xdrs, &objp->count))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_READ4resok(register XDR *xdrs, READ4resok *objp)
+{
+
+ if (!xdr_bool(xdrs, &objp->eof))
+ return (FALSE);
+
+#ifdef IGNORE_RDWR_DATA
+ /*
+ * Try to get length of read, and if that
+ * fails, default to 0. Don't return FALSE
+ * because the other read info will not be
+ * displayed.
+ */
+ objp->data.data_val = NULL;
+ if (!xdr_u_int(xdrs, &objp->data.data_len))
+ objp->data.data_len = 0;
+ nfs4_skip_bytes = objp->data.data_len;
+#else
+ if (!xdr_bytes(xdrs, (char **)&objp->data.data_val,
+ (uint_t *)&objp->data.data_len, ~0))
+ return (FALSE);
+#endif
+ return (TRUE);
+}
+
+bool_t
+xdr_READ4res(register XDR *xdrs, READ4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ switch (objp->status) {
+ case NFS4_OK:
+ if (!xdr_READ4resok(xdrs, &objp->READ4res_u.resok4))
+ return (FALSE);
+ break;
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_READDIR4args(register XDR *xdrs, READDIR4args *objp)
+{
+
+ if (!xdr_nfs_cookie4(xdrs, &objp->cookie))
+ return (FALSE);
+ if (!xdr_verifier4(xdrs, objp->cookieverf))
+ return (FALSE);
+ if (!xdr_count4(xdrs, &objp->dircount))
+ return (FALSE);
+ if (!xdr_count4(xdrs, &objp->maxcount))
+ return (FALSE);
+ if (!xdr_bitmap4(xdrs, &objp->attr_request))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_entry4(register XDR *xdrs, entry4 *objp)
+{
+
+ entry4 *tmp_entry4;
+ bool_t more_data = TRUE;
+ bool_t first_objp = TRUE;
+
+ while (more_data) {
+
+ if (!xdr_nfs_cookie4(xdrs, &objp->cookie))
+ return (FALSE);
+ if (!xdr_component4(xdrs, &objp->name))
+ return (FALSE);
+ if (!xdr_fattr4(xdrs, &objp->attrs))
+ return (FALSE);
+
+ if (xdrs->x_op == XDR_DECODE) {
+
+ void bzero();
+
+ if (!xdr_bool(xdrs, &more_data))
+ return (FALSE);
+
+ if (!more_data) {
+ objp->nextentry = NULL;
+ break;
+ }
+
+ objp->nextentry = (entry4 *)
+ mem_alloc(sizeof (entry4));
+ if (objp->nextentry == NULL)
+ return (NULL);
+ bzero(objp->nextentry, sizeof (entry4));
+ objp = objp->nextentry;
+
+ } else if (xdrs->x_op == XDR_ENCODE) {
+ objp = objp->nextentry;
+ if (!objp)
+ more_data = FALSE;
+
+ if (!xdr_bool(xdrs, &more_data))
+ return (FALSE);
+ } else {
+ tmp_entry4 = objp;
+ objp = objp->nextentry;
+ if (!objp)
+ more_data = FALSE;
+ if (!xdr_bool(xdrs, &more_data))
+ return (FALSE);
+ if (!first_objp)
+ mem_free(tmp_entry4, sizeof (entry4));
+ else
+ first_objp = FALSE;
+ }
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_dirlist4(register XDR *xdrs, dirlist4 *objp)
+{
+
+ if (!xdr_pointer(xdrs, (char **)&objp->entries, sizeof (entry4),
+ (xdrproc_t)xdr_entry4))
+ return (FALSE);
+ if (!xdr_bool(xdrs, &objp->eof))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_READDIR4resok(register XDR *xdrs, READDIR4resok *objp)
+{
+
+ if (!xdr_verifier4(xdrs, objp->cookieverf))
+ return (FALSE);
+ if (!xdr_dirlist4(xdrs, &objp->reply))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_READDIR4res(register XDR *xdrs, READDIR4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ switch (objp->status) {
+ case NFS4_OK:
+ if (!xdr_READDIR4resok(xdrs, &objp->READDIR4res_u.resok4))
+ return (FALSE);
+ break;
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_READLINK4resok(register XDR *xdrs, READLINK4resok *objp)
+{
+
+ if (!xdr_linktext4(xdrs, &objp->link))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_READLINK4res(register XDR *xdrs, READLINK4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ switch (objp->status) {
+ case NFS4_OK:
+ if (!xdr_READLINK4resok(xdrs, &objp->READLINK4res_u.resok4))
+ return (FALSE);
+ break;
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_REMOVE4args(register XDR *xdrs, REMOVE4args *objp)
+{
+
+ if (!xdr_component4(xdrs, &objp->target))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_REMOVE4resok(register XDR *xdrs, REMOVE4resok *objp)
+{
+
+ if (!xdr_change_info4(xdrs, &objp->cinfo))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_REMOVE4res(register XDR *xdrs, REMOVE4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ switch (objp->status) {
+ case NFS4_OK:
+ if (!xdr_REMOVE4resok(xdrs, &objp->REMOVE4res_u.resok4))
+ return (FALSE);
+ break;
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_RENAME4args(register XDR *xdrs, RENAME4args *objp)
+{
+
+ if (!xdr_component4(xdrs, &objp->oldname))
+ return (FALSE);
+ if (!xdr_component4(xdrs, &objp->newname))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_RENAME4resok(register XDR *xdrs, RENAME4resok *objp)
+{
+
+ if (!xdr_change_info4(xdrs, &objp->source_cinfo))
+ return (FALSE);
+ if (!xdr_change_info4(xdrs, &objp->target_cinfo))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_RENAME4res(register XDR *xdrs, RENAME4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ switch (objp->status) {
+ case NFS4_OK:
+ if (!xdr_RENAME4resok(xdrs, &objp->RENAME4res_u.resok4))
+ return (FALSE);
+ break;
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_RENEW4args(register XDR *xdrs, RENEW4args *objp)
+{
+
+ if (!xdr_clientid4(xdrs, &objp->clientid))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_RENEW4res(register XDR *xdrs, RENEW4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_RESTOREFH4res(register XDR *xdrs, RESTOREFH4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_SAVEFH4res(register XDR *xdrs, SAVEFH4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_SECINFO4args(register XDR *xdrs, SECINFO4args *objp)
+{
+
+ if (!xdr_component4(xdrs, &objp->name))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_rpc_gss_svc_t(register XDR *xdrs, rpc_gss_svc_t *objp)
+{
+
+ if (!xdr_enum(xdrs, (enum_t *)objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_rpcsec_gss_info(register XDR *xdrs, rpcsec_gss_info *objp)
+{
+
+ if (!xdr_sec_oid4(xdrs, &objp->oid))
+ return (FALSE);
+ if (!xdr_qop4(xdrs, &objp->qop))
+ return (FALSE);
+ if (!xdr_rpc_gss_svc_t(xdrs, &objp->service))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_secinfo4(register XDR *xdrs, secinfo4 *objp)
+{
+
+ if (!xdr_uint32_t(xdrs, &objp->flavor))
+ return (FALSE);
+ switch (objp->flavor) {
+ case RPCSEC_GSS:
+ if (!xdr_rpcsec_gss_info(xdrs, &objp->secinfo4_u.flavor_info))
+ return (FALSE);
+ break;
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_SECINFO4resok(register XDR *xdrs, SECINFO4resok *objp)
+{
+
+ if (!xdr_array(xdrs, (char **)&objp->SECINFO4resok_val,
+ (uint_t *)&objp->SECINFO4resok_len, ~0,
+ sizeof (secinfo4), (xdrproc_t)xdr_secinfo4))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_SECINFO4res(register XDR *xdrs, SECINFO4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ switch (objp->status) {
+ case NFS4_OK:
+ if (!xdr_SECINFO4resok(xdrs, &objp->SECINFO4res_u.resok4))
+ return (FALSE);
+ break;
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_SETATTR4args(register XDR *xdrs, SETATTR4args *objp)
+{
+
+ if (!xdr_stateid4(xdrs, &objp->stateid))
+ return (FALSE);
+ if (!xdr_fattr4(xdrs, &objp->obj_attributes))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_SETATTR4res(register XDR *xdrs, SETATTR4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ if (!xdr_bitmap4(xdrs, &objp->attrsset))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_SETCLIENTID4args(register XDR *xdrs, SETCLIENTID4args *objp)
+{
+
+ if (!xdr_nfs_client_id4(xdrs, &objp->client))
+ return (FALSE);
+ if (!xdr_cb_client4(xdrs, &objp->callback))
+ return (FALSE);
+ if (!xdr_uint32_t(xdrs, &objp->callback_ident))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_SETCLIENTID4resok(register XDR *xdrs, SETCLIENTID4resok *objp)
+{
+
+ if (!xdr_clientid4(xdrs, &objp->clientid))
+ return (FALSE);
+ if (!xdr_verifier4(xdrs, objp->setclientid_confirm))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_SETCLIENTID4res(register XDR *xdrs, SETCLIENTID4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ switch (objp->status) {
+ case NFS4_OK:
+ if (!xdr_SETCLIENTID4resok(xdrs, &objp->SETCLIENTID4res_u.
+ resok4))
+ return (FALSE);
+ break;
+ case NFS4ERR_CLID_INUSE:
+ if (!xdr_clientaddr4(xdrs, &objp->SETCLIENTID4res_u.
+ client_using))
+ return (FALSE);
+ break;
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_SETCLIENTID_CONFIRM4args(register XDR *xdrs, SETCLIENTID_CONFIRM4args *objp)
+{
+
+ if (!xdr_clientid4(xdrs, &objp->clientid))
+ return (FALSE);
+ if (!xdr_verifier4(xdrs, objp->setclientid_confirm))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_SETCLIENTID_CONFIRM4res(register XDR *xdrs, SETCLIENTID_CONFIRM4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_VERIFY4args(register XDR *xdrs, VERIFY4args *objp)
+{
+
+ if (!xdr_fattr4(xdrs, &objp->obj_attributes))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_VERIFY4res(register XDR *xdrs, VERIFY4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_stable_how4(register XDR *xdrs, stable_how4 *objp)
+{
+
+ if (!xdr_enum(xdrs, (enum_t *)objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_WRITE4args(register XDR *xdrs, WRITE4args *objp)
+{
+
+ if (!xdr_stateid4(xdrs, &objp->stateid))
+ return (FALSE);
+ if (!xdr_offset4(xdrs, &objp->offset))
+ return (FALSE);
+ if (!xdr_stable_how4(xdrs, &objp->stable))
+ return (FALSE);
+
+#ifdef IGNORE_RDWR_DATA
+ /*
+ * try to get length of write, and if that
+ * fails, default to 0. Don't return FALSE
+ * because the other write info will not be
+ * displayed (write stateid).
+ */
+ objp->data.data_val = NULL;
+ if (!xdr_u_int(xdrs, &objp->data.data_len))
+ objp->data.data_len = 0;
+ nfs4_skip_bytes = objp->data.data_len;
+#else
+ if (!xdr_bytes(xdrs, (char **)&objp->data.data_val,
+ (uint_t *)&objp->data.data_len, ~0))
+ return (FALSE);
+#endif
+ return (TRUE);
+}
+
+bool_t
+xdr_WRITE4resok(register XDR *xdrs, WRITE4resok *objp)
+{
+
+ if (!xdr_count4(xdrs, &objp->count))
+ return (FALSE);
+ if (!xdr_stable_how4(xdrs, &objp->committed))
+ return (FALSE);
+ if (!xdr_verifier4(xdrs, objp->writeverf))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_WRITE4res(register XDR *xdrs, WRITE4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ switch (objp->status) {
+ case NFS4_OK:
+ if (!xdr_WRITE4resok(xdrs, &objp->WRITE4res_u.resok4))
+ return (FALSE);
+ break;
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_RELEASE_LOCKOWNER4args(register XDR *xdrs, RELEASE_LOCKOWNER4args *objp)
+{
+
+ if (!xdr_lock_owner4(xdrs, &objp->lock_owner))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_RELEASE_LOCKOWNER4res(register XDR *xdrs, RELEASE_LOCKOWNER4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_ILLEGAL4res(register XDR *xdrs, ILLEGAL4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_nfs_opnum4(register XDR *xdrs, nfs_opnum4 *objp)
+{
+
+ if (!xdr_enum(xdrs, (enum_t *)objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_nfs_argop4(register XDR *xdrs, nfs_argop4 *objp)
+{
+ nfs4_skip_bytes = 0;
+ if (!xdr_nfs_opnum4(xdrs, &objp->argop))
+ return (FALSE);
+ switch (objp->argop) {
+ case OP_ACCESS:
+ if (!xdr_ACCESS4args(xdrs, &objp->nfs_argop4_u.opaccess))
+ return (FALSE);
+ break;
+ case OP_CLOSE:
+ if (!xdr_CLOSE4args(xdrs, &objp->nfs_argop4_u.opclose))
+ return (FALSE);
+ break;
+ case OP_COMMIT:
+ if (!xdr_COMMIT4args(xdrs, &objp->nfs_argop4_u.opcommit))
+ return (FALSE);
+ break;
+ case OP_CREATE:
+ if (!xdr_CREATE4args(xdrs, &objp->nfs_argop4_u.opcreate))
+ return (FALSE);
+ break;
+ case OP_DELEGPURGE:
+ if (!xdr_DELEGPURGE4args(xdrs, &objp->nfs_argop4_u.
+ opdelegpurge))
+ return (FALSE);
+ break;
+ case OP_DELEGRETURN:
+ if (!xdr_DELEGRETURN4args(xdrs, &objp->nfs_argop4_u.
+ opdelegreturn))
+ return (FALSE);
+ break;
+ case OP_GETATTR:
+ if (!xdr_GETATTR4args(xdrs, &objp->nfs_argop4_u.
+ opgetattr))
+ return (FALSE);
+ break;
+ case OP_GETFH:
+ break;
+ case OP_LINK:
+ if (!xdr_LINK4args(xdrs, &objp->nfs_argop4_u.oplink))
+ return (FALSE);
+ break;
+ case OP_LOCK:
+ if (!xdr_LOCK4args(xdrs, &objp->nfs_argop4_u.oplock))
+ return (FALSE);
+ break;
+ case OP_LOCKT:
+ if (!xdr_LOCKT4args(xdrs, &objp->nfs_argop4_u.oplockt))
+ return (FALSE);
+ break;
+ case OP_LOCKU:
+ if (!xdr_LOCKU4args(xdrs, &objp->nfs_argop4_u.oplocku))
+ return (FALSE);
+ break;
+ case OP_LOOKUP:
+ if (!xdr_LOOKUP4args(xdrs, &objp->nfs_argop4_u.oplookup))
+ return (FALSE);
+ break;
+ case OP_LOOKUPP:
+ break;
+ case OP_NVERIFY:
+ if (!xdr_NVERIFY4args(xdrs, &objp->nfs_argop4_u.opnverify))
+ return (FALSE);
+ break;
+ case OP_OPEN:
+ if (!xdr_OPEN4args(xdrs, &objp->nfs_argop4_u.opopen))
+ return (FALSE);
+ break;
+ case OP_OPENATTR:
+ if (!xdr_OPENATTR4args(xdrs, &objp->nfs_argop4_u.opopenattr))
+ return (FALSE);
+ break;
+ case OP_OPEN_CONFIRM:
+ if (!xdr_OPEN_CONFIRM4args(xdrs, &objp->nfs_argop4_u.
+ opopen_confirm))
+ return (FALSE);
+ break;
+ case OP_OPEN_DOWNGRADE:
+ if (!xdr_OPEN_DOWNGRADE4args(xdrs, &objp->nfs_argop4_u.
+ opopen_downgrade))
+ return (FALSE);
+ break;
+ case OP_PUTFH:
+ if (!xdr_PUTFH4args(xdrs, &objp->nfs_argop4_u.opputfh))
+ return (FALSE);
+ break;
+ case OP_PUTPUBFH:
+ break;
+ case OP_PUTROOTFH:
+ break;
+ case OP_READ:
+ if (!xdr_READ4args(xdrs, &objp->nfs_argop4_u.opread))
+ return (FALSE);
+ break;
+ case OP_READDIR:
+ if (!xdr_READDIR4args(xdrs, &objp->nfs_argop4_u.opreaddir))
+ return (FALSE);
+ break;
+ case OP_READLINK:
+ break;
+ case OP_REMOVE:
+ if (!xdr_REMOVE4args(xdrs, &objp->nfs_argop4_u.opremove))
+ return (FALSE);
+ break;
+ case OP_RENAME:
+ if (!xdr_RENAME4args(xdrs, &objp->nfs_argop4_u.oprename))
+ return (FALSE);
+ break;
+ case OP_RENEW:
+ if (!xdr_RENEW4args(xdrs, &objp->nfs_argop4_u.oprenew))
+ return (FALSE);
+ break;
+ case OP_RESTOREFH:
+ break;
+ case OP_SAVEFH:
+ break;
+ case OP_SECINFO:
+ if (!xdr_SECINFO4args(xdrs, &objp->nfs_argop4_u.opsecinfo))
+ return (FALSE);
+ break;
+ case OP_SETATTR:
+ if (!xdr_SETATTR4args(xdrs, &objp->nfs_argop4_u.opsetattr))
+ return (FALSE);
+ break;
+ case OP_SETCLIENTID:
+ if (!xdr_SETCLIENTID4args(xdrs, &objp->nfs_argop4_u.
+ opsetclientid))
+ return (FALSE);
+ break;
+ case OP_SETCLIENTID_CONFIRM:
+ if (!xdr_SETCLIENTID_CONFIRM4args(xdrs, &objp->nfs_argop4_u.
+ opsetclientid_confirm))
+ return (FALSE);
+ break;
+ case OP_VERIFY:
+ if (!xdr_VERIFY4args(xdrs, &objp->nfs_argop4_u.opverify))
+ return (FALSE);
+ break;
+ case OP_WRITE:
+ if (!xdr_WRITE4args(xdrs, &objp->nfs_argop4_u.opwrite))
+ return (FALSE);
+ break;
+ case OP_RELEASE_LOCKOWNER:
+ if (!xdr_RELEASE_LOCKOWNER4args(xdrs,
+ &objp->nfs_argop4_u.oprelease_lockowner))
+ return (FALSE);
+ break;
+ case OP_ILLEGAL:
+ break;
+ default:
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_nfs_resop4(register XDR *xdrs, nfs_resop4 *objp)
+{
+ nfs4_skip_bytes = 0;
+ if (!xdr_nfs_opnum4(xdrs, &objp->resop))
+ return (FALSE);
+ switch (objp->resop) {
+ case OP_ACCESS:
+ if (!xdr_ACCESS4res(xdrs, &objp->nfs_resop4_u.opaccess))
+ return (FALSE);
+ break;
+ case OP_CLOSE:
+ if (!xdr_CLOSE4res(xdrs, &objp->nfs_resop4_u.opclose))
+ return (FALSE);
+ break;
+ case OP_COMMIT:
+ if (!xdr_COMMIT4res(xdrs, &objp->nfs_resop4_u.opcommit))
+ return (FALSE);
+ break;
+ case OP_CREATE:
+ if (!xdr_CREATE4res(xdrs, &objp->nfs_resop4_u.opcreate))
+ return (FALSE);
+ break;
+ case OP_DELEGPURGE:
+ if (!xdr_DELEGPURGE4res(xdrs, &objp->nfs_resop4_u.opdelegpurge))
+ return (FALSE);
+ break;
+ case OP_DELEGRETURN:
+ if (!xdr_DELEGRETURN4res(xdrs, &objp->nfs_resop4_u.
+ opdelegreturn))
+ return (FALSE);
+ break;
+ case OP_GETATTR:
+ if (!xdr_GETATTR4res(xdrs, &objp->nfs_resop4_u.opgetattr))
+ return (FALSE);
+ break;
+ case OP_GETFH:
+ if (!xdr_GETFH4res(xdrs, &objp->nfs_resop4_u.opgetfh))
+ return (FALSE);
+ break;
+ case OP_LINK:
+ if (!xdr_LINK4res(xdrs, &objp->nfs_resop4_u.oplink))
+ return (FALSE);
+ break;
+ case OP_LOCK:
+ if (!xdr_LOCK4res(xdrs, &objp->nfs_resop4_u.oplock))
+ return (FALSE);
+ break;
+ case OP_LOCKT:
+ if (!xdr_LOCKT4res(xdrs, &objp->nfs_resop4_u.oplockt))
+ return (FALSE);
+ break;
+ case OP_LOCKU:
+ if (!xdr_LOCKU4res(xdrs, &objp->nfs_resop4_u.oplocku))
+ return (FALSE);
+ break;
+ case OP_LOOKUP:
+ if (!xdr_LOOKUP4res(xdrs, &objp->nfs_resop4_u.oplookup))
+ return (FALSE);
+ break;
+ case OP_LOOKUPP:
+ if (!xdr_LOOKUPP4res(xdrs, &objp->nfs_resop4_u.oplookupp))
+ return (FALSE);
+ break;
+ case OP_NVERIFY:
+ if (!xdr_NVERIFY4res(xdrs, &objp->nfs_resop4_u.opnverify))
+ return (FALSE);
+ break;
+ case OP_OPEN:
+ if (!xdr_OPEN4res(xdrs, &objp->nfs_resop4_u.opopen))
+ return (FALSE);
+ break;
+ case OP_OPENATTR:
+ if (!xdr_OPENATTR4res(xdrs, &objp->nfs_resop4_u.opopenattr))
+ return (FALSE);
+ break;
+ case OP_OPEN_CONFIRM:
+ if (!xdr_OPEN_CONFIRM4res(xdrs, &objp->nfs_resop4_u.
+ opopen_confirm))
+ return (FALSE);
+ break;
+ case OP_OPEN_DOWNGRADE:
+ if (!xdr_OPEN_DOWNGRADE4res(xdrs, &objp->nfs_resop4_u.
+ opopen_downgrade))
+ return (FALSE);
+ break;
+ case OP_PUTFH:
+ if (!xdr_PUTFH4res(xdrs, &objp->nfs_resop4_u.opputfh))
+ return (FALSE);
+ break;
+ case OP_PUTPUBFH:
+ if (!xdr_PUTPUBFH4res(xdrs, &objp->nfs_resop4_u.opputpubfh))
+ return (FALSE);
+ break;
+ case OP_PUTROOTFH:
+ if (!xdr_PUTROOTFH4res(xdrs, &objp->nfs_resop4_u.opputrootfh))
+ return (FALSE);
+ break;
+ case OP_READ:
+ if (!xdr_READ4res(xdrs, &objp->nfs_resop4_u.opread))
+ return (FALSE);
+ break;
+ case OP_READDIR:
+ if (!xdr_READDIR4res(xdrs, &objp->nfs_resop4_u.opreaddir))
+ return (FALSE);
+ break;
+ case OP_READLINK:
+ if (!xdr_READLINK4res(xdrs, &objp->nfs_resop4_u.opreadlink))
+ return (FALSE);
+ break;
+ case OP_REMOVE:
+ if (!xdr_REMOVE4res(xdrs, &objp->nfs_resop4_u.opremove))
+ return (FALSE);
+ break;
+ case OP_RENAME:
+ if (!xdr_RENAME4res(xdrs, &objp->nfs_resop4_u.oprename))
+ return (FALSE);
+ break;
+ case OP_RENEW:
+ if (!xdr_RENEW4res(xdrs, &objp->nfs_resop4_u.oprenew))
+ return (FALSE);
+ break;
+ case OP_RESTOREFH:
+ if (!xdr_RESTOREFH4res(xdrs, &objp->nfs_resop4_u.oprestorefh))
+ return (FALSE);
+ break;
+ case OP_SAVEFH:
+ if (!xdr_SAVEFH4res(xdrs, &objp->nfs_resop4_u.opsavefh))
+ return (FALSE);
+ break;
+ case OP_SECINFO:
+ if (!xdr_SECINFO4res(xdrs, &objp->nfs_resop4_u.opsecinfo))
+ return (FALSE);
+ break;
+ case OP_SETATTR:
+ if (!xdr_SETATTR4res(xdrs, &objp->nfs_resop4_u.opsetattr))
+ return (FALSE);
+ break;
+ case OP_SETCLIENTID:
+ if (!xdr_SETCLIENTID4res(xdrs, &objp->nfs_resop4_u.
+ opsetclientid))
+ return (FALSE);
+ break;
+ case OP_SETCLIENTID_CONFIRM:
+ if (!xdr_SETCLIENTID_CONFIRM4res(xdrs, &objp->nfs_resop4_u.
+ opsetclientid_confirm))
+ return (FALSE);
+ break;
+ case OP_VERIFY:
+ if (!xdr_VERIFY4res(xdrs, &objp->nfs_resop4_u.opverify))
+ return (FALSE);
+ break;
+ case OP_WRITE:
+ if (!xdr_WRITE4res(xdrs, &objp->nfs_resop4_u.opwrite))
+ return (FALSE);
+ break;
+ case OP_RELEASE_LOCKOWNER:
+ if (!xdr_RELEASE_LOCKOWNER4res(xdrs,
+ &objp->nfs_resop4_u.oprelease_lockowner))
+ return (FALSE);
+ break;
+ case OP_ILLEGAL:
+ if (!xdr_ILLEGAL4res(xdrs, &objp->nfs_resop4_u.opillegal))
+ return (FALSE);
+ break;
+ default:
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_COMPOUND4args(register XDR *xdrs, COMPOUND4args *objp)
+{
+
+ if (!xdr_utf8string(xdrs, &objp->tag))
+ return (FALSE);
+ if (!xdr_uint32_t(xdrs, &objp->minorversion))
+ return (FALSE);
+ if (!xdr_array(xdrs, (char **)&objp->argarray.argarray_val,
+ (uint_t *)&objp->argarray.argarray_len, ~0,
+ sizeof (nfs_argop4), (xdrproc_t)xdr_nfs_argop4))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_COMPOUND4res(register XDR *xdrs, COMPOUND4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ if (!xdr_utf8string(xdrs, &objp->tag))
+ return (FALSE);
+ if (!xdr_array(xdrs, (char **)&objp->resarray.resarray_val,
+ (uint_t *)&objp->resarray.resarray_len, ~0,
+ sizeof (nfs_resop4), (xdrproc_t)xdr_nfs_resop4))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_CB_GETATTR4args(register XDR *xdrs, CB_GETATTR4args *objp)
+{
+
+ if (!xdr_nfs_fh4(xdrs, &objp->fh))
+ return (FALSE);
+ if (!xdr_bitmap4(xdrs, &objp->attr_request))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_CB_GETATTR4resok(register XDR *xdrs, CB_GETATTR4resok *objp)
+{
+
+ if (!xdr_fattr4(xdrs, &objp->obj_attributes))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_CB_GETATTR4res(register XDR *xdrs, CB_GETATTR4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ switch (objp->status) {
+ case NFS4_OK:
+ if (!xdr_CB_GETATTR4resok(xdrs, &objp->CB_GETATTR4res_u.resok4))
+ return (FALSE);
+ break;
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_CB_RECALL4args(register XDR *xdrs, CB_RECALL4args *objp)
+{
+
+ if (!xdr_stateid4(xdrs, &objp->stateid))
+ return (FALSE);
+ if (!xdr_bool(xdrs, &objp->truncate))
+ return (FALSE);
+ if (!xdr_nfs_fh4(xdrs, &objp->fh))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_CB_RECALL4res(register XDR *xdrs, CB_RECALL4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_CB_ILLEGAL4res(register XDR *xdrs, CB_ILLEGAL4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_nfs_cb_opnum4(register XDR *xdrs, nfs_cb_opnum4 *objp)
+{
+
+ if (!xdr_enum(xdrs, (enum_t *)objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_nfs_cb_argop4(register XDR *xdrs, nfs_cb_argop4 *objp)
+{
+
+ if (!xdr_u_int(xdrs, &objp->argop))
+ return (FALSE);
+ switch (objp->argop) {
+ case OP_CB_GETATTR:
+ if (!xdr_CB_GETATTR4args(xdrs, &objp->nfs_cb_argop4_u.
+ opcbgetattr))
+ return (FALSE);
+ break;
+ case OP_CB_RECALL:
+ if (!xdr_CB_RECALL4args(xdrs, &objp->nfs_cb_argop4_u.
+ opcbrecall))
+ return (FALSE);
+ break;
+ case OP_CB_ILLEGAL:
+ break;
+ default:
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_nfs_cb_resop4(register XDR *xdrs, nfs_cb_resop4 *objp)
+{
+
+ if (!xdr_u_int(xdrs, &objp->resop))
+ return (FALSE);
+ switch (objp->resop) {
+ case OP_CB_GETATTR:
+ if (!xdr_CB_GETATTR4res(xdrs, &objp->nfs_cb_resop4_u.
+ opcbgetattr))
+ return (FALSE);
+ break;
+ case OP_CB_RECALL:
+ if (!xdr_CB_RECALL4res(xdrs, &objp->nfs_cb_resop4_u.opcbrecall))
+ return (FALSE);
+ break;
+ case OP_CB_ILLEGAL:
+ if (!xdr_CB_ILLEGAL4res(xdrs,
+ &objp->nfs_cb_resop4_u.opcbillegal))
+ return (FALSE);
+ break;
+ default:
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_CB_COMPOUND4args(register XDR *xdrs, CB_COMPOUND4args *objp)
+{
+
+ if (!xdr_utf8string(xdrs, &objp->tag))
+ return (FALSE);
+ if (!xdr_uint32_t(xdrs, &objp->minorversion))
+ return (FALSE);
+ if (!xdr_uint32_t(xdrs, &objp->callback_ident))
+ return (FALSE);
+ if (!xdr_array(xdrs, (char **)&objp->argarray.argarray_val,
+ (uint_t *)&objp->argarray.argarray_len, ~0,
+ sizeof (nfs_cb_argop4), (xdrproc_t)xdr_nfs_cb_argop4))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_CB_COMPOUND4res(register XDR *xdrs, CB_COMPOUND4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ if (!xdr_utf8string(xdrs, &objp->tag))
+ return (FALSE);
+ if (!xdr_array(xdrs, (char **)&objp->resarray.resarray_val,
+ (uint_t *)&objp->resarray.resarray_len, ~0,
+ sizeof (nfs_cb_resop4), (xdrproc_t)xdr_nfs_cb_resop4))
+ return (FALSE);
+ return (TRUE);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/nis_clnt.h b/usr/src/cmd/cmd-inet/usr.sbin/snoop/nis_clnt.h
new file mode 100644
index 0000000000..146e8f06f4
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/nis_clnt.h
@@ -0,0 +1,260 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1991 by Sun Microsystems, Inc.
+ */
+
+/* EDIT_START */
+
+/*
+ * nis_clnt.h
+ *
+ * This file contains definitions that are only of interest to the actual
+ * service daemon and client stubs. Normal users of NIS will not include
+ * this file.
+ *
+ * NOTE : This include file is automatically created by a combination
+ * of rpcgen and sed. DO NOT EDIT IT, change the nis.x file instead
+ * and then remake this file.
+ */
+
+#ifndef _NIS_CLNT_H
+#define _NIS_CLNT_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define NIS_PROG ((u_long)100300)
+#define NIS_VERSION ((u_long)3)
+
+#ifdef __STDC__
+#define NIS_LOOKUP ((u_long)1)
+extern nis_result * nis_lookup_clnt(ns_request *, CLIENT *);
+extern nis_result * nis_lookup_svc(ns_request *, struct svc_req *);
+#define NIS_ADD ((u_long)2)
+extern nis_result * nis_add_clnt(ns_request *, CLIENT *);
+extern nis_result * nis_add_svc(ns_request *, struct svc_req *);
+#define NIS_MODIFY ((u_long)3)
+extern nis_result * nis_modify_clnt(ns_request *, CLIENT *);
+extern nis_result * nis_modify_svc(ns_request *, struct svc_req *);
+#define NIS_REMOVE ((u_long)4)
+extern nis_result * nis_remove_clnt(ns_request *, CLIENT *);
+extern nis_result * nis_remove_svc(ns_request *, struct svc_req *);
+#define NIS_IBLIST ((u_long)5)
+extern nis_result * nis_iblist_clnt(ib_request *, CLIENT *);
+extern nis_result * nis_iblist_svc(ib_request *, struct svc_req *);
+#define NIS_IBADD ((u_long)6)
+extern nis_result * nis_ibadd_clnt(ib_request *, CLIENT *);
+extern nis_result * nis_ibadd_svc(ib_request *, struct svc_req *);
+#define NIS_IBMODIFY ((u_long)7)
+extern nis_result * nis_ibmodify_clnt(ib_request *, CLIENT *);
+extern nis_result * nis_ibmodify_svc(ib_request *, struct svc_req *);
+#define NIS_IBREMOVE ((u_long)8)
+extern nis_result * nis_ibremove_clnt(ib_request *, CLIENT *);
+extern nis_result * nis_ibremove_svc(ib_request *, struct svc_req *);
+#define NIS_IBFIRST ((u_long)9)
+extern nis_result * nis_ibfirst_clnt(ib_request *, CLIENT *);
+extern nis_result * nis_ibfirst_svc(ib_request *, struct svc_req *);
+#define NIS_IBNEXT ((u_long)10)
+extern nis_result * nis_ibnext_clnt(ib_request *, CLIENT *);
+extern nis_result * nis_ibnext_svc(ib_request *, struct svc_req *);
+#define NIS_FINDDIRECTORY ((u_long)12)
+extern fd_result * nis_finddirectory_clnt(fd_args *, CLIENT *);
+extern fd_result * nis_finddirectory_svc(fd_args *, struct svc_req *);
+#define NIS_STATUS ((u_long)14)
+extern nis_taglist * nis_status_clnt(nis_taglist *, CLIENT *);
+extern nis_taglist * nis_status_svc(nis_taglist *, struct svc_req *);
+#define NIS_DUMPLOG ((u_long)15)
+extern log_result * nis_dumplog_clnt(dump_args *, CLIENT *);
+extern log_result * nis_dumplog_svc(dump_args *, struct svc_req *);
+#define NIS_DUMP ((u_long)16)
+extern log_result * nis_dump_clnt(dump_args *, CLIENT *);
+extern log_result * nis_dump_svc(dump_args *, struct svc_req *);
+#define NIS_CALLBACK ((u_long)17)
+extern bool_t * nis_callback_clnt(netobj *, CLIENT *);
+extern bool_t * nis_callback_svc(netobj *, struct svc_req *);
+#define NIS_CPTIME ((u_long)18)
+extern u_long * nis_cptime_clnt(nis_name *, CLIENT *);
+extern u_long * nis_cptime_svc(nis_name *, struct svc_req *);
+#define NIS_CHECKPOINT ((u_long)19)
+extern cp_result * nis_checkpoint_clnt(nis_name *, CLIENT *);
+extern cp_result * nis_checkpoint_svc(nis_name *, struct svc_req *);
+#define NIS_PING ((u_long)20)
+extern void * nis_ping_clnt(ping_args *, CLIENT *);
+extern void * nis_ping_svc(ping_args *, struct svc_req *);
+#define NIS_SERVSTATE ((u_long)21)
+extern nis_taglist * nis_servstate_clnt(nis_taglist *, CLIENT *);
+extern nis_taglist * nis_servstate_svc(nis_taglist *, struct svc_req *);
+#define NIS_MKDIR ((u_long)22)
+extern nis_error * nis_mkdir_clnt(nis_name *, CLIENT *);
+extern nis_error * nis_mkdir_svc(nis_name *, struct svc_req *);
+#define NIS_RMDIR ((u_long)23)
+extern nis_error * nis_rmdir_clnt(nis_name *, CLIENT *);
+extern nis_error * nis_rmdir_svc(nis_name *, struct svc_req *);
+
+#else /* K&R C */
+
+#define NIS_LOOKUP ((u_long)1)
+extern nis_result * nis_lookup_clnt();
+extern nis_result * nis_lookup_svc();
+#define NIS_ADD ((u_long)2)
+extern nis_result * nis_add_clnt();
+extern nis_result * nis_add_svc();
+#define NIS_MODIFY ((u_long)3)
+extern nis_result * nis_modify_clnt();
+extern nis_result * nis_modify_svc();
+#define NIS_REMOVE ((u_long)4)
+extern nis_result * nis_remove_clnt();
+extern nis_result * nis_remove_svc();
+#define NIS_IBLIST ((u_long)5)
+extern nis_result * nis_iblist_clnt();
+extern nis_result * nis_iblist_svc();
+#define NIS_IBADD ((u_long)6)
+extern nis_result * nis_ibadd_clnt();
+extern nis_result * nis_ibadd_svc();
+#define NIS_IBMODIFY ((u_long)7)
+extern nis_result * nis_ibmodify_clnt();
+extern nis_result * nis_ibmodify_svc();
+#define NIS_IBREMOVE ((u_long)8)
+extern nis_result * nis_ibremove_clnt();
+extern nis_result * nis_ibremove_svc();
+#define NIS_IBFIRST ((u_long)9)
+extern nis_result * nis_ibfirst_clnt();
+extern nis_result * nis_ibfirst_svc();
+#define NIS_IBNEXT ((u_long)10)
+extern nis_result * nis_ibnext_clnt();
+extern nis_result * nis_ibnext_svc();
+#define NIS_FINDDIRECTORY ((u_long)12)
+extern fd_result * nis_finddirectory_clnt();
+extern fd_result * nis_finddirectory_svc();
+#define NIS_STATUS ((u_long)14)
+extern nis_taglist * nis_status_clnt();
+extern nis_taglist * nis_status_svc();
+#define NIS_DUMPLOG ((u_long)15)
+extern log_result * nis_dumplog_clnt();
+extern log_result * nis_dumplog_svc();
+#define NIS_DUMP ((u_long)16)
+extern log_result * nis_dump_clnt();
+extern log_result * nis_dump_svc();
+#define NIS_CALLBACK ((u_long)17)
+extern bool_t * nis_callback_clnt();
+extern bool_t * nis_callback_svc();
+#define NIS_CPTIME ((u_long)18)
+extern u_long * nis_cptime_clnt();
+extern u_long * nis_cptime_svc();
+#define NIS_CHECKPOINT ((u_long)19)
+extern cp_result * nis_checkpoint_clnt();
+extern cp_result * nis_checkpoint_svc();
+#define NIS_PING ((u_long)20)
+extern void * nis_ping_clnt();
+extern void * nis_ping_svc();
+#define NIS_SERVSTATE ((u_long)21)
+extern nis_taglist * nis_servstate_clnt();
+extern nis_taglist * nis_servstate_svc();
+#define NIS_MKDIR ((u_long)22)
+extern nis_error * nis_mkdir_clnt();
+extern nis_error * nis_mkdir_svc();
+#define NIS_RMDIR ((u_long)23)
+extern nis_error * nis_rmdir_clnt();
+extern nis_error * nis_rmdir_svc();
+
+#endif /* K&R C */
+
+/* Now print out the definitions of all the xdr functions */
+
+#ifdef __STDC__
+extern bool_t xdr_nis_attr(XDR *, nis_attr *);
+extern bool_t xdr_nis_name(XDR *, nis_name *);
+extern bool_t xdr_zotypes(XDR *, zotypes *);
+extern bool_t xdr_nstype(XDR *, nstype *);
+extern bool_t xdr_oar_mask(XDR *, oar_mask *);
+extern bool_t xdr_endpoint(XDR *, endpoint *);
+extern bool_t xdr_nis_server(XDR *, nis_server *);
+extern bool_t xdr_directory_obj(XDR *, directory_obj *);
+extern bool_t xdr_entry_col(XDR *, entry_col *);
+extern bool_t xdr_entry_obj(XDR *, entry_obj *);
+extern bool_t xdr_group_obj(XDR *, group_obj *);
+extern bool_t xdr_link_obj(XDR *, link_obj *);
+extern bool_t xdr_table_col(XDR *, table_col *);
+extern bool_t xdr_table_obj(XDR *, table_obj *);
+extern bool_t xdr_objdata(XDR *, objdata *);
+extern bool_t xdr_nis_oid(XDR *, nis_oid *);
+extern bool_t xdr_nis_object(XDR *, nis_object *);
+extern bool_t xdr_nis_error(XDR *, nis_error *);
+extern bool_t xdr_nis_result(XDR *, nis_result *);
+extern bool_t xdr_ns_request(XDR *, ns_request *);
+extern bool_t xdr_ib_request(XDR *, ib_request *);
+extern bool_t xdr_ping_args(XDR *, ping_args *);
+extern bool_t xdr_log_entry_t(XDR *, log_entry_t *);
+extern bool_t xdr_log_entry(XDR *, log_entry *);
+extern bool_t xdr_log_result(XDR *, log_result *);
+extern bool_t xdr_cp_result(XDR *, cp_result *);
+extern bool_t xdr_nis_tag(XDR *, nis_tag *);
+extern bool_t xdr_nis_taglist(XDR *, nis_taglist *);
+extern bool_t xdr_dump_args(XDR *, dump_args *);
+extern bool_t xdr_fd_args(XDR *, fd_args *);
+extern bool_t xdr_fd_result(XDR *, fd_result *);
+
+#else /* K&R C */
+
+bool_t xdr_nis_attr();
+bool_t xdr_nis_name();
+bool_t xdr_zotypes();
+bool_t xdr_nstype();
+bool_t xdr_oar_mask();
+bool_t xdr_endpoint();
+bool_t xdr_nis_server();
+bool_t xdr_directory_obj();
+bool_t xdr_entry_col();
+bool_t xdr_entry_obj();
+bool_t xdr_group_obj();
+bool_t xdr_link_obj();
+bool_t xdr_table_col();
+bool_t xdr_table_obj();
+bool_t xdr_objdata();
+bool_t xdr_nis_oid();
+bool_t xdr_nis_object();
+bool_t xdr_nis_error();
+bool_t xdr_nis_result();
+bool_t xdr_ns_request();
+bool_t xdr_ib_request();
+bool_t xdr_ping_args();
+bool_t xdr_log_entry_t();
+bool_t xdr_log_entry();
+bool_t xdr_log_result();
+bool_t xdr_cp_result();
+bool_t xdr_nis_tag();
+bool_t xdr_nis_taglist();
+bool_t xdr_dump_args();
+bool_t xdr_fd_args();
+bool_t xdr_fd_result();
+
+#endif /* K&R C */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _NIS_CLNT_H */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/ntp.h b/usr/src/cmd/cmd-inet/usr.sbin/snoop/ntp.h
new file mode 100644
index 0000000000..72045ecc7f
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/ntp.h
@@ -0,0 +1,533 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1991, 2000, 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#ifndef _NTP_H
+#define _NTP_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* $Source: /usr/users/louie/ntp/RCS/ntp.h,v $ */
+/* $Revision: 3.4.1.5 $ $Date: 89/04/10 15:55:42 $ */
+
+/*
+ * $Log: ntp.h,v $
+ * Revision 3.4.1.5 89/04/10 15:55:42 louie
+ * Provide default value for number of bits/byte if not defined. Compute the
+ * Window shift mask inside of conditional code on XTAL so we get the correct
+ * value if configured without a crystal controled clock (!!)
+ *
+ * Revision 3.4.1.4 89/03/31 16:34:50 louie
+ * Add bit in flags which allow a peer to be synced to. Changed a char to a bit
+ * field so that it is always signed.
+ *
+ * Revision 3.4.1.3 89/03/29 12:26:18 louie
+ * Removed some unused #defines. Replaced MAXSTRATUM with NTP_INFIN per new
+ * spec. The variable 'mode' in the peer structure has been renamed 'hmode'
+ * per the new spec.
+ *
+ * Revision 3.4.1.2 89/03/22 18:28:18 louie
+ * patch3: Use new RCS headers.
+ *
+ * Revision 3.4.1.1 89/03/20 00:02:53 louie
+ * 1
+ *
+ * Revision 3.4 89/03/17 18:37:00 louie
+ * Latest test release.
+ *
+ * Revision 3.3.1.1 89/03/17 18:23:49 louie
+ * Change CLOCK_FACTOR to be a power of 2.
+ *
+ * Revision 3.3 89/03/15 14:19:36 louie
+ * New baseline for next release.
+ *
+ * Revision 3.2.1.2 89/03/15 13:46:52 louie
+ * The version number for that particular flavor of ntpd <--> ntpdc interaction
+ * is now defined by NTPDC_VERSION. The packet format for the ntpdc program
+ * has changed slightly to improve robustness when dealing with multiple packets
+ * of status data.
+ *
+ * Revision 3.2.1.1 89/03/09 17:11:24 louie
+ * patch1: Updated constants, which were previously in incorrect units.
+ *
+ * Revision 3.2 89/03/07 18:21:45 louie
+ * New version of UNIX NTP daemon and software based on the 6 March 1989
+ * draft of the new NTP protocol specification. This version doesn't
+ * implement authentication, and accepts and send only NTP Version 1
+ * packets.
+ *
+ * Revision 3.1.1.1 89/02/15 08:54:42 louie
+ * *** empty log message ***
+ *
+ *
+ * Revision 3.1 89/01/30 14:43:07 louie
+ * Second UNIX NTP test release.
+ *
+ * Revision 3.0 88/12/12 16:01:07 louie
+ * Test release of new UNIX NTP software. This version should conform to the
+ * revised NTP protocol specification.
+ *
+ */
+
+#ifndef FD_SET
+#define NFDBITS 32
+#define FD_SETSIZE 32
+#define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
+#define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
+#define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
+#define FD_ZERO(p) bzero((char *)(p), sizeof (*(p)))
+#endif
+
+#ifndef NBBY
+#define NBBY 8 /* number of bits per byte */
+#endif
+
+#define MAXNETIF 10
+
+struct intf {
+ int fd;
+ char *name;
+ struct sockaddr_in sin;
+ struct sockaddr_in bcast;
+ struct sockaddr_in mask;
+ int uses;
+ int if_flags;
+};
+extern struct intf addrs[];
+extern int nintf;
+
+/*
+ * Definitions for the masses
+ */
+#define JAN_1970 2208988800 /* 1970 - 1900 in seconds */
+
+/*
+ * Daemon specific (ntpd.c)
+ */
+#define SHIFT_MASK 0xff /* number of intervals to wait */
+
+#ifndef WAYTOOBIG
+#define WAYTOOBIG 1000.0 /* Too many seconds to correct, something is */
+ /* really wrong */
+#endif
+
+#ifndef XTAL
+#define XTAL 1 /* crystal controlled clock by default */
+#endif
+
+#ifndef NTPINITFILE
+#define NTPINITFILE "/etc/ntp.conf"
+#endif
+
+struct list {
+ struct ntp_peer *head;
+ struct ntp_peer *tail;
+ int members;
+};
+
+#define STRMCMP(a, cond, b) \
+ (((a) == UNSPECIFIED ? NTP_INFIN+1 : a) cond \
+ ((b) == UNSPECIFIED ? NTP_INFIN+1 : (b)))
+
+
+/*
+ * Definitions outlined in the NTP spec
+ */
+#define NTP_VERSION 1
+#define NTP_PORT 123 /* for ref only (see /etc/services) */
+#define NTP_INFIN 15
+#define NTP_MAXAGE 86400
+#define NTP_MAXSKW 0.01 /* seconds */
+#define NTP_MINDIST 0.02 /* seconds */
+#define NTP_MINPOLL 6 /* (64) seconds between messages */
+#define NTP_MAXPOLL 10 /* (1024) secs to poll */
+#define NTP_WINDOW 8 /* size of shift register */
+#define NTP_MAXWGT 8 /* maximum allowable dispersion */
+#define NTP_MAXLIST 5 /* max size of selection list */
+#define NTP_MAXSTRA 2 /* max number of strata in selection list */
+#define X_NTP_CANDIDATES 64 /* number of peers to consider when doing */
+ /* clock selection */
+#define NTP_SELECT 0.75 /* weight used to compute dispersion */
+
+#define PEER_MAXDISP 64.0 /* Maximum dispersion */
+#define PEER_THRESHOLD 0.5 /* dispersion threshold */
+#define PEER_FILTER 0.5 /* filter weight */
+
+#if XTAL == 0
+#define PEER_SHIFT 4
+#define NTP_WINDOW_SHIFT_MASK 0x0f
+#else
+#define PEER_SHIFT 8
+#define NTP_WINDOW_SHIFT_MASK 0xff
+#endif
+
+
+/*
+ * 5.1 Uniform Phase Adjustments
+ * Clock parameters
+ */
+#define CLOCK_UPDATE 8 /* update interval (1<<CLOCK_UPDATE secs) */
+#if XTAL
+#define CLOCK_ADJ 2 /* adjustment interval (1<<CLOCK_ADJ secs) */
+#define CLOCK_PHASE 8 /* phase shift */
+#define CLOCK_MAX 0.128 /* maximum aperture (milliseconds) */
+#else
+#define CLOCK_ADJ 0
+#define CLOCK_PHASE 6 /* phase shift */
+#define CLOCK_MAX 0.512 /* maximum aperture (milliseconds) */
+#endif
+#define CLOCK_FREQ 10 /* frequency shift */
+#define CLOCK_TRACK 8
+#define CLOCK_COMP 4
+#define CLOCK_FACTOR 18
+
+/*
+ * Structure definitions for NTP fixed point values
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Integer Part |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Fraction Part |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Integer Part | Fraction Part |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+struct l_fixedpt {
+ u_long int_part;
+ u_long fraction;
+};
+
+struct s_fixedpt {
+ u_short int_part;
+ u_short fraction;
+};
+
+/*
+ * ================= Table 3.3. Packet Variables =================
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |LI | VN | Mode| Stratum | Poll | Precision |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Synchronizing Distance |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Synchronizing Dispersion |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Reference Clock Identifier |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * | Reference Timestamp (64 bits) |
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * | Originate Timestamp (64 bits) |
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * | Receive Timestamp (64 bits) |
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * | Transmit Timestamp (64 bits) |
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Encryption Keyid (32 bits, when A bit set) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * | Message Authentication Code/MAC (when A bit set) |
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+#define MAC_OCTETS_DES 8
+#define MAC_OCTETS_MD5 16
+#define MAC_OCTETS_MIN MAC_OCTETS_DES
+#define MAC_OCTETS_MAX MAC_OCTETS_MD5
+#define AUTH_OCTETS_V3 (MAC_OCTETS_MAX + sizeof (uint32_t))
+
+struct ntpdata {
+ u_char li_vn_mode; /* contains leap indicator, version and mode */
+ u_char stratum; /* Stratum level */
+ u_char ppoll; /* poll value */
+ int precision:8;
+ struct s_fixedpt distance;
+ struct s_fixedpt dispersion;
+ u_long refid;
+ struct l_fixedpt reftime;
+ struct l_fixedpt org;
+ struct l_fixedpt rec;
+ struct l_fixedpt xmt;
+ uint32_t keyid;
+ u_char mac[MAC_OCTETS_MAX];
+};
+
+#define LEN_PKT_NOMAC (sizeof (struct ntpdata) - AUTH_OCTETS_V3)
+
+/*
+ * Leap Second Codes (high order two bits)
+ */
+#define NO_WARNING 0x00 /* no warning */
+#define PLUS_SEC 0x40 /* add a second (61 seconds) */
+#define MINUS_SEC 0x80 /* minus a second (59 seconds) */
+#define ALARM 0xc0 /* alarm condition (clock unsynchronized) */
+
+/*
+ * Clock Status Bits that Encode Version
+ */
+#define NTPVERSION_1 0x08
+#define VERSIONMASK 0x38
+#define LEAPMASK 0xc0
+#define NTPMODEMASK 0x07
+
+/*
+ * Code values
+ */
+#define MODE_UNSPEC 0 /* unspecified */
+#define MODE_SYM_ACT 1 /* symmetric active */
+#define MODE_SYM_PAS 2 /* symmetric passive */
+#define MODE_CLIENT 3 /* client */
+#define MODE_SERVER 4 /* server */
+#define MODE_BROADCAST 5 /* broadcast */
+#define MODE_CONTROL 6 /* control */
+#define MODE_PRIVATE 7 /* private */
+
+/*
+ * Stratum Definitions
+ */
+#define UNSPECIFIED 0
+#define PRIM_REF 1 /* radio clock */
+#define INFO_QUERY 62 /* **** THIS implementation dependent **** */
+#define INFO_REPLY 63 /* **** THIS implementation dependent **** */
+
+
+/* ================= table 3.2 Peer Variables ================= */
+struct ntp_peer {
+ struct ntp_peer *next, *prev;
+ struct sockaddr_in src; /* both peer.srcadr and peer.srcport */
+ int flags; /* local flags */
+#define PEER_FL_CONFIG 1
+#define PEER_FL_AUTHENABLE 2
+#define PEER_FL_SYNC 0x1000 /* peer can bet sync'd to */
+#define PEER_FL_BCAST 0x2000 /* broadcast peer */
+#define PEER_FL_SELECTED 0x8000 /* actually used by query routine */
+
+ int sock; /* index into sockets to derive */
+ /* peer.dstadr and peer.dstport */
+ u_char leap; /* receive */
+ u_char hmode; /* receive */
+ u_char stratum; /* receive */
+ u_char ppoll; /* receive */
+ u_char hpoll; /* poll update */
+ short precision; /* receive */
+ struct s_fixedpt distance; /* receive */
+ struct s_fixedpt dispersion; /* receive */
+ u_long refid; /* receive */
+ struct l_fixedpt reftime; /* receive */
+ struct l_fixedpt org; /* receive, clear */
+ struct l_fixedpt rec; /* receive, clear */
+ struct l_fixedpt xmt; /* transmit, clear */
+ u_long reach; /* receive, transmit, clear */
+ u_long valid; /* packet, transmit, clear */
+ u_long timer; /* receive, transmit, poll update */
+ long stopwatch; /* <<local>> for timing */
+ /*
+ * first order offsets
+ */
+ struct filter {
+ short samples; /* <<local>> */
+ double offset[PEER_SHIFT];
+ double delay[PEER_SHIFT];
+ } filter; /* filter, clear */
+
+ double estdelay; /* filter */
+ double estoffset; /* filter */
+ double estdisp; /* filter */
+
+ u_long pkt_sent; /* <<local>> */
+ u_long pkt_rcvd; /* <<local>> */
+ u_long pkt_dropped; /* <<local>> */
+};
+
+/* ================= table 3.1: System Variables ================= */
+
+struct sysdata { /* procedure */
+ u_char leap; /* clock update */
+ u_char stratum; /* clock update */
+ short precision; /* system */
+ struct s_fixedpt distance; /* clock update */
+ struct s_fixedpt dispersion; /* clock update */
+ u_long refid; /* clock update */
+ struct l_fixedpt reftime; /* clock update */
+ int hold; /* clock update */
+ struct ntp_peer *peer; /* selection */
+ int maxpeers; /* <<local>> */
+ u_char filler; /* put here for %&*%$$ SUNs */
+};
+
+#define NTPDC_VERSION 2
+
+/*
+ * These structures are used to pass information to the ntpdc (control)
+ * program. They are unique to this implementation and not part of the
+ * NTP specification.
+ */
+struct clockinfo {
+ u_long net_address;
+ u_long my_address;
+ u_short port;
+ u_short flags;
+ u_long pkt_sent;
+ u_long pkt_rcvd;
+ u_long pkt_dropped;
+ u_long timer;
+ u_char leap;
+ u_char stratum;
+ u_char ppoll;
+ int precision:8;
+
+ u_char hpoll;
+ u_char filler1;
+ u_short reach;
+
+ long estdisp; /* scaled by 1000 */
+ long estdelay; /* in milliseconds */
+ long estoffset; /* in milliseconds */
+ u_long refid;
+ struct l_fixedpt reftime;
+ struct info_filter {
+ short index;
+ short filler;
+ long offset[PEER_SHIFT]; /* in milliseconds */
+ long delay[PEER_SHIFT]; /* in milliseconds */
+ } info_filter;
+};
+
+struct ntpinfo {
+ u_char version;
+ u_char type; /* request type (stratum in ntp packets) */
+ u_char count; /* number of entries in this packet */
+ u_char seq; /* sequence number of this packet */
+
+ u_char npkts; /* total number of packets */
+ u_char peers;
+ u_char fill3;
+ u_char fill4;
+};
+
+/*
+ * From usr/src/cmd/xntpd/include/ntp_control.h:
+ * Definition of a mode 6 packet.
+ */
+struct ntp_control {
+ u_char li_vn_mode; /* leap, version, mode */
+ u_char r_m_e_op; /* response, more, error, opcode */
+ u_short sequence; /* sequence number of request */
+ u_short status; /* status word for association */
+ u_short associd; /* association ID */
+ u_short offset; /* offset of this batch of data */
+ u_short count; /* count of data in this packet */
+ u_char data[1]; /* data + auth */
+};
+
+#define NTPC_DATA_MAXLEN (480 + AUTH_OCTETS_V3)
+
+/*
+ * Decoding for the r_m_e_op field
+ */
+#define CTL_RESPONSE 0x80
+#define CTL_ERROR 0x40
+#define CTL_MORE 0x20
+#define CTL_OP_MASK 0x1f
+
+/*
+ * Opcodes
+ */
+#define CTL_OP_UNSPEC 0
+#define CTL_OP_READSTAT 1
+#define CTL_OP_READVAR 2
+#define CTL_OP_WRITEVAR 3
+#define CTL_OP_READCLOCK 4
+#define CTL_OP_WRITECLOCK 5
+#define CTL_OP_SETTRAP 6
+#define CTL_OP_ASYNCMSG 7
+#define CTL_OP_UNSETTRAP 31
+
+/*
+ * From usr/src/cmd/xntpd/include/ntp_request.h:
+ * A mode 7 packet is used exchanging data between an NTP server
+ * and a client for purposes other than time synchronization, e.g.
+ * monitoring, statistics gathering and configuration. A mode 7
+ * packet has the following format:
+ */
+
+struct ntp_private {
+ u_char rm_vn_mode; /* response, more, version, mode */
+ u_char auth_seq; /* key, sequence number */
+ u_char implementation; /* implementation number */
+ u_char request; /* request number */
+ u_short err_nitems; /* error code/number of data items */
+ u_short mbz_itemsize; /* item size */
+ char data[1]; /* data area */
+};
+
+#define RESP_BIT 0x80
+#define MORE_BIT 0x40
+#define INFO_VERSION(rm_vn_mode) ((u_char)(((rm_vn_mode)>>3) & 0x7))
+#define INFO_MODE(rm_vn_mode) ((rm_vn_mode) & 0x7)
+
+#define AUTH_BIT 0x80
+#define INFO_SEQ(auth_seq) ((auth_seq) & 0x7f)
+
+#define INFO_ERR(err_nitems) ((u_short)((ntohs(err_nitems) >> 12) & 0xf))
+#define INFO_NITEMS(err_nitems) ((u_short)(ntohs(err_nitems) & 0xfff))
+
+#define INFO_ITEMSIZE(mbz_itemsize) (ntohs(mbz_itemsize) & 0xfff)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _NTP_H */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/slp.h b/usr/src/cmd/cmd-inet/usr.sbin/snoop/slp.h
new file mode 100644
index 0000000000..b01c5eaf2f
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/slp.h
@@ -0,0 +1,146 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1998 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _SNOOP_SLP_H
+#define _SNOOP_SLP_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Structs and definitions for the snoop SLP interpreter only
+ * (This code is not used by the SLP library).
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct slpv1_hdr {
+ unsigned char vers;
+ unsigned char function;
+ unsigned short length;
+ unsigned char flags;
+ unsigned char dialect;
+ unsigned char language[2];
+ unsigned short charset;
+ unsigned short xid;
+};
+
+struct slpv2_hdr {
+ unsigned char vers;
+ unsigned char function;
+ unsigned char l1, l2, l3;
+ unsigned char flags;
+ unsigned char reserved;
+ unsigned char o1, o2, o3;
+ unsigned short xid;
+};
+
+/*
+ * flags
+ */
+#define V1_OVERFLOW 0x80
+#define V1_MONOLINGUAL 0x40
+#define V1_URL_AUTH 0x20
+#define V1_ATTR_AUTH 0x10
+#define V1_FRESH_REG 0x08
+
+#define V2_OVERFLOW 0x80
+#define V2_FRESH 0x40
+#define V2_MCAST 0x20
+
+/*
+ * packet types
+ */
+
+#define V1_SRVREQ 1
+#define V1_SRVRPLY 2
+#define V1_SRVREG 3
+#define V1_SRVDEREG 4
+#define V1_SRVACK 5
+#define V1_ATTRRQST 6
+#define V1_ATTRRPLY 7
+#define V1_DAADVERT 8
+#define V1_SRVTYPERQST 9
+#define V1_SRVTYPERPLY 10
+
+#define V2_SRVRQST 1
+#define V2_SRVRPLY 2
+#define V2_SRVREG 3
+#define V2_SRVDEREG 4
+#define V2_SRVACK 5
+#define V2_ATTRRQST 6
+#define V2_ATTRRPLY 7
+#define V2_DAADVERT 8
+#define V2_SRVTYPERQST 9
+#define V2_SRVTYPERPLY 10
+#define V2_SAADVERT 11
+
+/*
+ * extended packet types
+ */
+#define SCOPERQST 65
+#define SCOPERPLY 66
+#define DARQST 67
+#define DARPLY 68
+#define DASTRIKE 69
+
+
+/*
+ * error codes
+ */
+
+#define OK 0x0000
+#define LANG_NOT_SUPPORTED 0x0001
+#define PROTOCOL_PARSE_ERR 0x0002
+#define INVALID_REGISTRATION 0x0003
+#define SCOPE_NOT_SUPPORTED 0x0004
+#define CHARSET_NOT_UNDERSTOOD 0x0005
+#define AUTHENTICATION_UNKNOWN 0x0005
+#define AUTHENTICATION_INVALID 0x0006
+#define V2_AUTHENTICATION_ABSENT 0x0006
+#define V2_AUTHENTICATION_FAILED 0x0007
+#define V2_VER_NOT_SUPPORTED 0x0009
+#define NOT_SUPPORTED_YET 0x000a
+#define V2_INTERNAL_ERROR 0x000a
+#define REQUEST_TIMED_OUT 0x000b
+#define V2_DA_BUSY_NOW 0x000b
+#define COULD_NOT_INIT_NET_RESOURCES 0x000c
+#define V2_OPTION_NOT_UNDERSTOOD 0x000c
+#define COULD_NOT_ALLOCATE_MEMORY 0x000d
+#define V2_INVALID_UPDATE 0x000d
+#define PARAMETER_BAD 0x000e
+#define V2_RQST_NOT_SUPPORTED 0x000e
+#define INVALID_LIFETIME 0x000f
+
+#define INTERNAL_NET_ERROR 0x000f
+#define INTERNAL_SYSTEM_ERROR 0x0010
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SNOOP_SLP_H */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.c
new file mode 100644
index 0000000000..c042648d1f
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.c
@@ -0,0 +1,1073 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI" /* SunOS */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stropts.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <setjmp.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <signal.h>
+#include <sys/mman.h>
+#include <assert.h>
+#include <sys/sysmacros.h>
+
+#include <sys/socket.h>
+#include <sys/pfmod.h>
+#include <net/if.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <netdb.h>
+
+#include "snoop.h"
+
+int snaplen;
+char *device = NULL;
+
+/* Global error recovery variables */
+sigjmp_buf jmp_env, ojmp_env; /* error recovery jmp buf */
+int snoop_nrecover; /* number of recoveries on curr pkt */
+int quitting; /* user termination flag */
+
+extern int encap_levels; /* variables needing reset on error */
+extern unsigned int total_encap_levels;
+
+struct snoop_handler *snoop_hp; /* global alarm handler head */
+struct snoop_handler *snoop_tp; /* global alarm handler tail */
+time_t snoop_nalarm; /* time of next alarm */
+
+/* protected interpreter output areas */
+#define MAXSUM 8
+#define REDZONE 64
+static char *sumline[MAXSUM];
+static char *detail_line;
+static char *line;
+static char *encap;
+
+int audio;
+int maxcount; /* maximum no of packets to capture */
+int count; /* count of packets captured */
+int sumcount;
+int x_offset = -1;
+int x_length = 0x7fffffff;
+FILE *namefile;
+int Pflg;
+boolean_t qflg = B_FALSE;
+boolean_t rflg = B_FALSE;
+#ifdef DEBUG
+boolean_t zflg = B_FALSE; /* debugging packet corrupt flag */
+#endif
+struct Pf_ext_packetfilt pf;
+
+void usage();
+void show_count();
+void snoop_sigrecover(int sig, siginfo_t *info, void *p);
+static char *protmalloc(size_t);
+static void resetperm(void);
+
+main(argc, argv)
+ int argc; char **argv;
+{
+ extern char *optarg;
+ extern int optind;
+ int c;
+ int filter = 0;
+ int flags = F_SUM;
+ struct Pf_ext_packetfilt *fp = NULL;
+ char *icapfile = NULL;
+ char *ocapfile = NULL;
+ int nflg = 0;
+ int Nflg = 0;
+ int Cflg = 0;
+ int first = 1;
+ int last = 0x7fffffff;
+ int ppa;
+ int use_kern_pf;
+ char *p, *p2;
+ char names[MAXPATHLEN + 1];
+ char self[MAXHOSTNAMELEN + 1];
+ char *argstr = NULL;
+ void (*proc)();
+ extern void cap_write();
+ extern void process_pkt();
+ char *audiodev;
+ int ret;
+ struct sigaction sigact;
+ stack_t sigstk;
+ char *output_area;
+ int nbytes;
+
+ names[0] = '\0';
+ /*
+ * Global error recovery: Prepare for interpreter failures
+ * with corrupted packets or confused interpreters.
+ * Allocate protected output and stack areas, with generous
+ * red-zones.
+ */
+ nbytes = (MAXSUM + 3) * (MAXLINE + REDZONE);
+ output_area = protmalloc(nbytes);
+ if (output_area == NULL) {
+ perror("Warning: mmap");
+ exit(1);
+ }
+
+ /* Allocate protected output areas */
+ for (ret = 0; ret < MAXSUM; ret++) {
+ sumline[ret] = (char *)output_area;
+ output_area += (MAXLINE + REDZONE);
+ }
+ detail_line = output_area;
+ output_area += MAXLINE + REDZONE;
+ line = output_area;
+ output_area += MAXLINE + REDZONE;
+ encap = output_area;
+ output_area += MAXLINE + REDZONE;
+
+ /* Initialize an alternate signal stack to increase robustness */
+ if ((sigstk.ss_sp = (char *)malloc(SIGSTKSZ+REDZONE)) == NULL) {
+ perror("Warning: malloc");
+ exit(1);
+ }
+ sigstk.ss_size = SIGSTKSZ;
+ sigstk.ss_flags = 0;
+ if (sigaltstack(&sigstk, (stack_t *)NULL) < 0) {
+ perror("Warning: sigaltstack");
+ exit(1);
+ }
+
+ /* Initialize a master signal handler */
+ sigact.sa_handler = NULL;
+ sigact.sa_sigaction = snoop_sigrecover;
+ sigemptyset(&sigact.sa_mask);
+ sigact.sa_flags = SA_ONSTACK|SA_SIGINFO;
+
+ /* Register master signal handler */
+ if (sigaction(SIGHUP, &sigact, (struct sigaction *)NULL) < 0) {
+ perror("Warning: sigaction");
+ exit(1);
+ }
+ if (sigaction(SIGINT, &sigact, (struct sigaction *)NULL) < 0) {
+ perror("Warning: sigaction");
+ exit(1);
+ }
+ if (sigaction(SIGQUIT, &sigact, (struct sigaction *)NULL) < 0) {
+ perror("Warning: sigaction");
+ exit(1);
+ }
+ if (sigaction(SIGILL, &sigact, (struct sigaction *)NULL) < 0) {
+ perror("Warning: sigaction");
+ exit(1);
+ }
+ if (sigaction(SIGTRAP, &sigact, (struct sigaction *)NULL) < 0) {
+ perror("Warning: sigaction");
+ exit(1);
+ }
+ if (sigaction(SIGIOT, &sigact, (struct sigaction *)NULL) < 0) {
+ perror("Warning: sigaction");
+ exit(1);
+ }
+ if (sigaction(SIGEMT, &sigact, (struct sigaction *)NULL) < 0) {
+ perror("Warning: sigaction");
+ exit(1);
+ }
+ if (sigaction(SIGFPE, &sigact, (struct sigaction *)NULL) < 0) {
+ perror("Warning: sigaction");
+ exit(1);
+ }
+ if (sigaction(SIGBUS, &sigact, (struct sigaction *)NULL) < 0) {
+ perror("Warning: sigaction");
+ exit(1);
+ }
+ if (sigaction(SIGSEGV, &sigact, (struct sigaction *)NULL) < 0) {
+ perror("Warning: sigaction");
+ exit(1);
+ }
+ if (sigaction(SIGSYS, &sigact, (struct sigaction *)NULL) < 0) {
+ perror("Warning: sigaction");
+ exit(1);
+ }
+ if (sigaction(SIGALRM, &sigact, (struct sigaction *)NULL) < 0) {
+ perror("Warning: sigaction");
+ exit(1);
+ }
+ if (sigaction(SIGTERM, &sigact, (struct sigaction *)NULL) < 0) {
+ perror("Warning: sigaction");
+ exit(1);
+ }
+
+ /* Prepare for failure during program initialization/exit */
+ if (sigsetjmp(jmp_env, 1)) {
+ exit(1);
+ }
+ setvbuf(stdout, NULL, _IOLBF, BUFSIZ);
+
+ while ((c = getopt(argc, argv, "at:CPDSi:o:Nn:s:d:vVp:f:c:x:?rqz"))
+ != EOF) {
+ switch (c) {
+ case 'a':
+ audiodev = getenv("AUDIODEV");
+ if (audiodev == NULL)
+ audiodev = "/dev/audio";
+ audio = open(audiodev, O_WRONLY);
+ if (audio < 0) {
+ pr_err("Audio device %s: %m",
+ audiodev);
+ exit(1);
+ }
+ break;
+ case 't':
+ flags |= F_TIME;
+ switch (*optarg) {
+ case 'r': flags |= F_RTIME; break;
+ case 'a': flags |= F_ATIME; break;
+ case 'd': break;
+ default: usage();
+ }
+ break;
+ case 'P':
+ Pflg++;
+ break;
+ case 'D':
+ flags |= F_DROPS;
+ break;
+ case 'S':
+ flags |= F_LEN;
+ break;
+ case 'i':
+ icapfile = optarg;
+ break;
+ case 'o':
+ ocapfile = optarg;
+ break;
+ case 'N':
+ Nflg++;
+ break;
+ case 'n':
+ nflg++;
+ (void) strlcpy(names, optarg, MAXPATHLEN);
+ break;
+ case 's':
+ snaplen = atoi(optarg);
+ break;
+ case 'd':
+ device = optarg;
+ break;
+ case 'v':
+ flags &= ~(F_SUM);
+ flags |= F_DTAIL;
+ break;
+ case 'V':
+ flags |= F_ALLSUM;
+ break;
+ case 'p':
+ p = optarg;
+ p2 = strpbrk(p, ",:-");
+ if (p2 == NULL) {
+ first = last = atoi(p);
+ } else {
+ *p2++ = '\0';
+ first = atoi(p);
+ last = atoi(p2);
+ }
+ break;
+ case 'f':
+ (void) gethostname(self, MAXHOSTNAMELEN);
+ p = strchr(optarg, ':');
+ if (p) {
+ *p = '\0';
+ if (strcmp(optarg, self) == 0 ||
+ strcmp(p+1, self) == 0)
+ (void) fprintf(stderr,
+ "Warning: cannot capture packets from %s\n",
+ self);
+ *p = ' ';
+ } else if (strcmp(optarg, self) == 0)
+ (void) fprintf(stderr,
+ "Warning: cannot capture packets from %s\n",
+ self);
+ argstr = optarg;
+ break;
+ case 'x':
+ p = optarg;
+ p2 = strpbrk(p, ",:-");
+ if (p2 == NULL) {
+ x_offset = atoi(p);
+ x_length = -1;
+ } else {
+ *p2++ = '\0';
+ x_offset = atoi(p);
+ x_length = atoi(p2);
+ }
+ break;
+ case 'c':
+ maxcount = atoi(optarg);
+ break;
+ case 'C':
+ Cflg++;
+ break;
+ case 'q':
+ qflg = B_TRUE;
+ break;
+ case 'r':
+ rflg = B_TRUE;
+ break;
+#ifdef DEBUG
+ case 'z':
+ zflg = B_TRUE;
+ break;
+#endif /* DEBUG */
+ case '?':
+ default:
+ usage();
+ }
+ }
+
+ if (argc > optind)
+ argstr = (char *)concat_args(&argv[optind], argc - optind);
+
+ /*
+ * Need to know before we decide on filtering method some things
+ * about the interface. So, go ahead and do part of the initialization
+ * now so we have that data. Note that if no device is specified,
+ * check_device selects one and returns it. In an ideal world,
+ * it might be nice if the "correct" interface for the filter
+ * requested was chosen, but that's too hard.
+ */
+ if (!icapfile) {
+ use_kern_pf = check_device(&device, &ppa);
+ } else {
+ cap_open_read(icapfile);
+
+ if (!nflg) {
+ names[0] = '\0';
+ (void) strlcpy(names, icapfile, MAXPATHLEN);
+ (void) strlcat(names, ".names", MAXPATHLEN);
+ }
+ }
+
+ /* attempt to read .names file if it exists before filtering */
+ if ((!Nflg) && names[0] != '\0') {
+ if (access(names, F_OK) == 0) {
+ load_names(names);
+ } else if (nflg) {
+ (void) fprintf(stderr, "%s not found\n", names);
+ exit(1);
+ }
+ }
+
+ if (argstr) {
+ if (!icapfile && use_kern_pf) {
+ ret = pf_compile(argstr, Cflg);
+ switch (ret) {
+ case 0:
+ filter++;
+ compile(argstr, Cflg);
+ break;
+ case 1:
+ fp = &pf;
+ break;
+ case 2:
+ fp = &pf;
+ filter++;
+ break;
+ }
+ } else {
+ filter++;
+ compile(argstr, Cflg);
+ }
+
+ if (Cflg)
+ exit(0);
+ }
+
+ if (flags & F_SUM)
+ flags |= F_WHO;
+
+ /*
+ * If the -o flag is set then capture packets
+ * directly to a file. Don't attempt to
+ * interpret them on the fly (F_NOW).
+ * Note: capture to file is much less likely
+ * to drop packets since we don't spend cpu
+ * cycles running through the interpreters
+ * and possibly hanging in address-to-name
+ * mappings through the name service.
+ */
+ if (ocapfile) {
+ cap_open_write(ocapfile);
+ proc = cap_write;
+ } else {
+ flags |= F_NOW;
+ proc = process_pkt;
+ }
+
+
+ /*
+ * If the -i flag is set then get packets from
+ * the log file which has been previously captured
+ * with the -o option.
+ */
+ if (icapfile) {
+ names[0] = '\0';
+ (void) strlcpy(names, icapfile, MAXPATHLEN);
+ (void) strlcat(names, ".names", MAXPATHLEN);
+
+ if (Nflg) {
+ namefile = fopen(names, "w");
+ if (namefile == NULL) {
+ perror(names);
+ exit(1);
+ }
+ flags = 0;
+ (void) fprintf(stderr,
+ "Creating name file %s\n", names);
+ }
+
+ if (flags & F_DTAIL)
+ flags = F_DTAIL;
+ else
+ flags |= F_NUM | F_TIME;
+
+ resetperm();
+ cap_read(first, last, filter, proc, flags);
+
+ if (Nflg)
+ (void) fclose(namefile);
+
+ } else {
+ const int chunksize = 8 * 8192;
+ struct timeval timeout;
+
+ /*
+ * If listening to packets on audio
+ * then set the buffer timeout down
+ * to 1/10 sec. A higher value
+ * makes the audio "bursty".
+ */
+ if (audio) {
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 100000;
+ } else {
+ timeout.tv_sec = 1;
+ timeout.tv_usec = 0;
+ }
+
+ initdevice(device, snaplen, chunksize, &timeout, fp, ppa);
+ if (! qflg && ocapfile)
+ show_count();
+ resetperm();
+ net_read(chunksize, filter, proc, flags);
+
+ if (!(flags & F_NOW))
+ printf("\n");
+ }
+
+ if (ocapfile)
+ cap_close();
+
+ return (0);
+}
+
+int tone[] = {
+0x034057, 0x026074, 0x136710, 0x126660, 0x147551, 0x034460,
+0x026775, 0x141727, 0x127670, 0x156532, 0x036064, 0x030721,
+0x134703, 0x126705, 0x046071, 0x030073, 0x036667, 0x140666,
+0x126137, 0x064463, 0x031064, 0x072677, 0x135652, 0x141734,
+0x036463, 0x027472, 0x137333, 0x127257, 0x152534, 0x033065,
+0x027723, 0x136313, 0x127735, 0x053473, 0x035470, 0x052666,
+0x167260, 0x140535, 0x045471, 0x034474, 0x132711, 0x132266,
+0x047127, 0x027077, 0x043705, 0x141676, 0x134110, 0x063400,
+};
+
+/*
+ * Make a sound on /dev/audio according
+ * to the length of the packet. The tone
+ * data above is a piece of waveform from
+ * a Pink Floyd track. The amount of waveform
+ * used is a function of packet length e.g.
+ * a series of small packets is heard as
+ * clicks, whereas a series of NFS packets
+ * in an 8k read sounds like a "WHAAAARP".
+ *
+ * Note: add 4 constant bytes to sound segments
+ * to avoid an artifact of DBRI/MMCODEC that
+ * results in a screech due to underrun (bug 114552).
+ */
+void
+click(len)
+ int len;
+{
+ len /= 8;
+ len = len ? len : 4;
+
+ if (audio) {
+ write(audio, tone, len);
+ write(audio, "\377\377\377\377", 4);
+ }
+}
+
+/* Display a count of packets */
+void
+show_count()
+{
+ static int prev = -1;
+
+ if (count == prev)
+ return;
+
+ prev = count;
+ (void) fprintf(stderr, "\r%d ", count);
+}
+
+#define ENCAP_LEN 16 /* Hold "(NN encap)" */
+
+/*
+ * Display data that's external to the packet.
+ * This constitutes the first half of the summary
+ * line display.
+ */
+void
+show_pktinfo(flags, num, src, dst, ptvp, tvp, drops, len)
+ int flags, num, drops, len;
+ char *src, *dst;
+ struct timeval *ptvp, *tvp;
+{
+ struct tm *tm;
+ static struct timeval tvp0;
+ int sec, usec;
+ char *lp = line;
+ int i, start;
+
+ if (flags & F_NUM) {
+ sprintf(lp, "%3d ", num);
+ lp += strlen(lp);
+ }
+ tm = localtime(&tvp->tv_sec);
+
+ if (flags & F_TIME) {
+ if (flags & F_ATIME) {
+ sprintf(lp, "%d:%02d:%d.%05d ",
+ tm->tm_hour, tm->tm_min, tm->tm_sec,
+ tvp->tv_usec / 10);
+ lp += strlen(lp);
+ } else {
+ if (flags & F_RTIME) {
+ if (tvp0.tv_sec == 0) {
+ tvp0.tv_sec = tvp->tv_sec;
+ tvp0.tv_usec = tvp->tv_usec;
+ }
+ ptvp = &tvp0;
+ }
+ sec = tvp->tv_sec - ptvp->tv_sec;
+ usec = tvp->tv_usec - ptvp->tv_usec;
+ if (usec < 0) {
+ usec += 1000000;
+ sec -= 1;
+ }
+ sprintf(lp, "%3d.%05d ", sec, usec / 10);
+ lp += strlen(lp);
+ }
+ }
+
+ if (flags & F_WHO) {
+ sprintf(lp, "%12s -> %-12s ", src, dst);
+ lp += strlen(lp);
+ }
+
+ if (flags & F_DROPS) {
+ sprintf(lp, "drops: %d ", drops);
+ lp += strlen(lp);
+ }
+
+ if (flags & F_LEN) {
+ sprintf(lp, "length: %4d ", len);
+ lp += strlen(lp);
+ }
+
+ if (flags & F_SUM) {
+ if (flags & F_ALLSUM)
+ printf("________________________________\n");
+
+ start = flags & F_ALLSUM ? 0 : sumcount - 1;
+ sprintf(encap, " (%d encap)", total_encap_levels - 1);
+ printf("%s%s%s\n", line, sumline[start],
+ ((flags & F_ALLSUM) || (total_encap_levels == 1)) ? "" :
+ encap);
+
+ for (i = start + 1; i < sumcount; i++)
+ printf("%s%s\n", line, sumline[i]);
+
+ sumcount = 0;
+ }
+
+ if (flags & F_DTAIL) {
+ printf("%s\n\n", detail_line);
+ detail_line[0] = '\0';
+ }
+}
+
+/*
+ * The following two routines are called back
+ * from the interpreters to display their stuff.
+ * The theory is that when snoop becomes a window
+ * based tool we can just supply a new version of
+ * get_sum_line and get_detail_line and not have
+ * to touch the interpreters at all.
+ */
+char *
+get_sum_line()
+{
+ int tsumcount = sumcount;
+
+ if (sumcount >= MAXSUM) {
+ sumcount = 0; /* error recovery */
+ pr_err(
+ "get_sum_line: sumline overflow (sumcount=%d, MAXSUM=%d)\n",
+ tsumcount, MAXSUM);
+ }
+
+ sumline[sumcount][0] = '\0';
+ return (sumline[sumcount++]);
+}
+
+/*ARGSUSED*/
+char *
+get_detail_line(off, len)
+ int off, len;
+{
+ if (detail_line[0]) {
+ printf("%s\n", detail_line);
+ detail_line[0] = '\0';
+ }
+ return (detail_line);
+}
+
+/*
+ * Print an error.
+ * Works like printf (fmt string and variable args)
+ * except that it will subsititute an error message
+ * for a "%m" string (like syslog) and it calls
+ * long_jump - it doesn't return to where it was
+ * called from - it goes to the last setjmp().
+ */
+void
+pr_err(char *fmt, ...)
+{
+ va_list ap;
+ char buf[BUFSIZ], *p2;
+ char *p1;
+
+ strcpy(buf, "snoop: ");
+ p2 = buf + strlen(buf);
+
+ for (p1 = fmt; *p1; p1++) {
+ if (*p1 == '%' && *(p1+1) == 'm') {
+ char *errstr;
+
+ if ((errstr = strerror(errno)) != (char *)NULL) {
+ (void) strcpy(p2, errstr);
+ p2 += strlen(p2);
+ }
+ p1++;
+ } else {
+ *p2++ = *p1;
+ }
+ }
+ if (p2 > buf && *(p2-1) != '\n')
+ *p2++ = '\n';
+ *p2 = '\0';
+
+ va_start(ap, fmt);
+ (void) vfprintf(stderr, buf, ap);
+ va_end(ap);
+ snoop_sigrecover(-1, NULL, NULL); /* global error recovery */
+}
+
+/*
+ * Ye olde usage proc
+ * PLEASE keep this up to date!
+ * Naive users *love* this stuff.
+ */
+void
+usage()
+{
+ (void) fprintf(stderr, "\nUsage: snoop\n");
+ (void) fprintf(stderr,
+ "\t[ -a ] # Listen to packets on audio\n");
+ (void) fprintf(stderr,
+ "\t[ -d device ] # Listen on interface named device\n");
+ (void) fprintf(stderr,
+ "\t[ -s snaplen ] # Truncate packets\n");
+ (void) fprintf(stderr,
+ "\t[ -c count ] # Quit after count packets\n");
+ (void) fprintf(stderr,
+ "\t[ -P ] # Turn OFF promiscuous mode\n");
+ (void) fprintf(stderr,
+ "\t[ -D ] # Report dropped packets\n");
+ (void) fprintf(stderr,
+ "\t[ -S ] # Report packet size\n");
+ (void) fprintf(stderr,
+ "\t[ -i file ] # Read previously captured packets\n");
+ (void) fprintf(stderr,
+ "\t[ -o file ] # Capture packets in file\n");
+ (void) fprintf(stderr,
+ "\t[ -n file ] # Load addr-to-name table from file\n");
+ (void) fprintf(stderr,
+ "\t[ -N ] # Create addr-to-name table\n");
+ (void) fprintf(stderr,
+ "\t[ -t r|a|d ] # Time: Relative, Absolute or Delta\n");
+ (void) fprintf(stderr,
+ "\t[ -v ] # Verbose packet display\n");
+ (void) fprintf(stderr,
+ "\t[ -V ] # Show all summary lines\n");
+ (void) fprintf(stderr,
+ "\t[ -p first[,last] ] # Select packet(s) to display\n");
+ (void) fprintf(stderr,
+ "\t[ -x offset[,length] ] # Hex dump from offset for length\n");
+ (void) fprintf(stderr,
+ "\t[ -C ] # Print packet filter code\n");
+ (void) fprintf(stderr,
+ "\t[ -q ] # Suppress printing packet count\n");
+ (void) fprintf(stderr,
+ "\t[ -r ] # Do not resolve address to name\n");
+ (void) fprintf(stderr,
+ "\n\t[ filter expression ]\n");
+ (void) fprintf(stderr, "\nExample:\n");
+ (void) fprintf(stderr, "\tsnoop -o saved host fred\n\n");
+ (void) fprintf(stderr, "\tsnoop -i saved -tr -v -p19\n");
+ exit(1);
+}
+
+/*
+ * sdefault: default global alarm handler. Causes the current packet
+ * to be skipped.
+ */
+static void
+sdefault(void)
+{
+ snoop_nrecover = SNOOP_MAXRECOVER;
+}
+
+/*
+ * snoop_alarm: register or unregister an alarm handler to be called after
+ * s_sec seconds. Because snoop wasn't written to tolerate random signal
+ * delivery, periodic SIGALRM delivery (or SA_RESTART) cannot be used.
+ *
+ * s_sec argument of 0 seconds unregisters the handler.
+ * s_handler argument of NULL registers default handler sdefault(), or
+ * unregisters all signal handlers (for error recovery).
+ *
+ * Variables must be volatile to force the compiler to not optimize
+ * out the signal blocking.
+ */
+/*ARGSUSED*/
+int
+snoop_alarm(int s_sec, void (*s_handler)())
+{
+ volatile time_t now;
+ volatile time_t nalarm = 0;
+ volatile struct snoop_handler *sh = NULL;
+ volatile struct snoop_handler *hp, *tp, *next;
+ volatile sigset_t s_mask;
+ volatile int ret = -1;
+
+ sigemptyset((sigset_t *)&s_mask);
+ sigaddset((sigset_t *)&s_mask, SIGALRM);
+ if (s_sec < 0)
+ return (-1);
+
+ /* register an alarm handler */
+ now = time(NULL);
+ if (s_sec) {
+ sh = malloc(sizeof (struct snoop_handler));
+ sh->s_time = now + s_sec;
+ if (s_handler == NULL)
+ s_handler = sdefault;
+ sh->s_handler = s_handler;
+ sh->s_next = NULL;
+ (void) sigprocmask(SIG_BLOCK, (sigset_t *)&s_mask, NULL);
+ if (snoop_hp == NULL) {
+ snoop_hp = snoop_tp = (struct snoop_handler *)sh;
+
+ snoop_nalarm = sh->s_time;
+ alarm(sh->s_time - now);
+ } else {
+ snoop_tp->s_next = (struct snoop_handler *)sh;
+ snoop_tp = (struct snoop_handler *)sh;
+
+ if (sh->s_time < snoop_nalarm) {
+ snoop_nalarm = sh->s_time;
+ (void) alarm(sh->s_time - now);
+ }
+ }
+ (void) sigprocmask(SIG_UNBLOCK, (sigset_t *)&s_mask, NULL);
+
+ return (0);
+ }
+
+ /* unregister an alarm handler */
+ (void) sigprocmask(SIG_BLOCK, (sigset_t *)&s_mask, NULL);
+ tp = (struct snoop_handler *)&snoop_hp;
+ for (hp = snoop_hp; hp; hp = next) {
+ next = hp->s_next;
+ if (s_handler == NULL || hp->s_handler == s_handler) {
+ ret = 0;
+ tp->s_next = hp->s_next;
+ if (snoop_tp == hp) {
+ if (tp == (struct snoop_handler *)&snoop_hp)
+ snoop_tp = NULL;
+ else
+ snoop_tp = (struct snoop_handler *)tp;
+ }
+ free((void *)hp);
+ } else {
+ if (nalarm == 0 || nalarm > hp->s_time)
+ nalarm = now < hp->s_time ? hp->s_time :
+ now + 1;
+ tp = hp;
+ }
+ }
+ /*
+ * Stop or adjust timer
+ */
+ if (snoop_hp == NULL) {
+ snoop_nalarm = 0;
+ (void) alarm(0);
+ } else if (nalarm > 0 && nalarm < snoop_nalarm) {
+ snoop_nalarm = nalarm;
+ (void) alarm(nalarm - now);
+ }
+
+ (void) sigprocmask(SIG_UNBLOCK, (sigset_t *)&s_mask, NULL);
+ return (ret);
+}
+
+/*
+ * snoop_recover: reset snoop's output area, and any internal variables,
+ * to allow continuation.
+ * XXX: make this an interface such that each interpreter can
+ * register a reset routine.
+ */
+void
+snoop_recover(void)
+{
+ int i;
+
+ /* Error recovery: reset output_area and associated variables */
+ for (i = 0; i < MAXSUM; i++)
+ sumline[i][0] = '\0';
+ detail_line[0] = '\0';
+ line[0] = '\0';
+ encap[0] = '\0';
+ sumcount = 0;
+
+ /* stacking/unstacking cannot be relied upon */
+ encap_levels = 0;
+ total_encap_levels = 1;
+
+ /* remove any pending timeouts */
+ (void) snoop_alarm(0, NULL);
+}
+
+/*
+ * snoop_sigrecover: global sigaction routine to manage recovery
+ * from catastrophic interpreter failures while interpreting
+ * corrupt trace files/packets. SIGALRM timeouts, program errors,
+ * and user termination are all handled. In the case of a corrupt
+ * packet or confused interpreter, the packet will be skipped, and
+ * execution will continue in scan().
+ *
+ * Global alarm handling (see snoop_alarm()) is managed here.
+ *
+ * Variables must be volatile to force the compiler to not optimize
+ * out the signal blocking.
+ */
+/*ARGSUSED*/
+void
+snoop_sigrecover(int sig, siginfo_t *info, void *p)
+{
+ volatile time_t now;
+ volatile time_t nalarm = 0;
+ volatile struct snoop_handler *hp;
+
+ /*
+ * Invoke any registered alarms. This involves first calculating
+ * the time for the next alarm, setting it up, then progressing
+ * through handler invocations. Note that since handlers may
+ * use siglongjmp(), in the worst case handlers may be serviced
+ * at a later time.
+ */
+ if (sig == SIGALRM) {
+ now = time(NULL);
+ /* Calculate next alarm time */
+ for (hp = snoop_hp; hp; hp = hp->s_next) {
+ if (hp->s_time) {
+ if ((hp->s_time - now) > 0) {
+ if (nalarm == 0 || nalarm > hp->s_time)
+ nalarm = now < hp->s_time ?
+ hp->s_time : now + 1;
+ }
+ }
+ }
+ /* Setup next alarm */
+ if (nalarm) {
+ snoop_nalarm = nalarm;
+ alarm(nalarm - now);
+ } else {
+ snoop_nalarm = 0;
+ }
+
+ /* Invoke alarm handlers (may not return) */
+ for (hp = snoop_hp; hp; hp = hp->s_next) {
+ if (hp->s_time) {
+ if ((now - hp->s_time) >= 0) {
+ hp->s_time = 0; /* only invoke once */
+ if (hp->s_handler)
+ hp->s_handler();
+ }
+ }
+ }
+ } else {
+ snoop_nrecover++;
+ }
+
+ /*
+ * Exit if a signal has occurred after snoop has begun the process
+ * of quitting.
+ */
+ if (quitting)
+ exit(1);
+
+ /*
+ * If an alarm handler has timed out, and snoop_nrecover has
+ * reached SNOOP_MAXRECOVER, skip to the next packet.
+ *
+ * If any other signal has occurred, and snoop_nrecover has
+ * reached SNOOP_MAXRECOVER, give up.
+ */
+ if (sig == SIGALRM) {
+ if (ioctl(STDOUT_FILENO, I_CANPUT, 0) == 0) {
+ /*
+ * We've stalled on output, which is not a critical
+ * failure. Reset the recovery counter so we do not
+ * consider this a persistent failure, and return so
+ * we do not skip this packet.
+ */
+ snoop_nrecover = 0;
+ return;
+ }
+ if (snoop_nrecover >= SNOOP_MAXRECOVER) {
+ fprintf(stderr,
+ "snoop: WARNING: skipping from packet %d\n",
+ count);
+ snoop_nrecover = 0;
+ } else {
+ /* continue trying */
+ return;
+ }
+ } else if (snoop_nrecover >= SNOOP_MAXRECOVER) {
+ fprintf(stderr,
+ "snoop: ERROR: cannot recover from packet %d\n", count);
+ exit(1);
+ }
+
+#ifdef DEBUG
+ fprintf(stderr, "snoop_sigrecover(%d, %p, %p)\n", sig, info, p);
+#endif /* DEBUG */
+
+ /*
+ * Prepare to quit. This allows final processing to occur
+ * after first terminal interruption.
+ */
+ if (sig == SIGTERM || sig == SIGHUP || sig == SIGINT) {
+ quitting = 1;
+ return;
+ } else if (sig != -1 && sig != SIGALRM) {
+ /* Inform user that snoop has taken a fault */
+ fprintf(stderr, "WARNING: received signal %d from packet %d\n",
+ sig, count);
+ }
+
+ /* Reset interpreter variables */
+ snoop_recover();
+
+ /* Continue in scan() with the next packet */
+ siglongjmp(jmp_env, 1);
+ /*NOTREACHED*/
+}
+
+/*
+ * Protected malloc for global error recovery: prepare for interpreter
+ * failures with corrupted packets or confused interpreters. Dynamically
+ * allocate `nbytes' bytes, and sandwich it between two PROT_NONE pages to
+ * catch writes outside of the allocated region.
+ */
+static char *
+protmalloc(size_t nbytes)
+{
+ caddr_t start;
+ int psz = sysconf(_SC_PAGESIZE);
+
+ nbytes = P2ROUNDUP(nbytes, psz);
+ start = mmap(NULL, nbytes + psz * 2, PROT_READ|PROT_WRITE,
+ MAP_PRIVATE|MAP_ANON, -1, 0);
+ if (start == MAP_FAILED) {
+ perror("Error: protmalloc: mmap");
+ return (NULL);
+ }
+ assert(IS_P2ALIGNED(start, psz));
+ if (mprotect(start, 1, PROT_NONE) == -1)
+ perror("Warning: mprotect");
+
+ start += psz;
+ if (mprotect(start + nbytes, 1, PROT_NONE) == -1)
+ perror("Warning: mprotect");
+
+ return (start);
+}
+
+/*
+ * resetperm - reduce security vulnerabilities by resetting
+ * owner/group/permissions. Always attempt setuid() - if we have
+ * permission to drop our privilege level, do so.
+ */
+void
+resetperm(void)
+{
+ if (geteuid() == 0) {
+ (void) setgid(GID_NOBODY);
+ (void) setuid(UID_NOBODY);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.h b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.h
new file mode 100644
index 0000000000..f70873b910
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.h
@@ -0,0 +1,288 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SNOOP_H
+#define _SNOOP_H
+
+#pragma ident "%Z%%M% %I% %E% SMI" /* SunOS */
+
+#include <rpc/types.h>
+#include <sys/pfmod.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/icmp6.h>
+#include <net/pppoe.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Flags to control packet info display
+ */
+#define F_NOW 0x00000001 /* display in realtime */
+#define F_SUM 0x00000002 /* display summary line */
+#define F_ALLSUM 0x00000004 /* display all summary lines */
+#define F_DTAIL 0x00000008 /* display detail lines */
+#define F_TIME 0x00000010 /* display time */
+#define F_ATIME 0x00000020 /* display absolute time */
+#define F_RTIME 0x00000040 /* display relative time */
+#define F_DROPS 0x00000080 /* display drops */
+#define F_LEN 0x00000100 /* display pkt length */
+#define F_NUM 0x00000200 /* display pkt number */
+#define F_WHO 0x00000400 /* display src/dst */
+
+#define MAXLINE (1088) /* max len of detail line */
+
+#define MAX_HDRTRAILER (64) /* max hdr/trailer packet slack */
+
+/*
+ * The RPC XID cache structure.
+ * When analyzing RPC protocols we
+ * have to cache the xid of the RPC
+ * request together with the program
+ * number, proc, version etc since this
+ * information is missing in the reply
+ * packet. Using the xid in the reply
+ * we can lookup this previously stashed
+ * information in the cache.
+ *
+ * For RPCSEC_GSS flavor, some special processing is
+ * needed for the argument interpretation based on its
+ * control procedure and service type. This information
+ * is stored in the cache table during interpretation of
+ * the rpc header and will be referenced later when the rpc
+ * argument is interpreted.
+ */
+#define XID_CACHE_SIZE 256
+struct cache_struct {
+ int xid_num; /* RPC transaction id */
+ int xid_frame; /* Packet number */
+ int xid_prog; /* RPC program number */
+ int xid_vers; /* RPC version number */
+ int xid_proc; /* RPC procedure number */
+ unsigned int xid_gss_proc; /* control procedure */
+ int xid_gss_service; /* none, integ, priv */
+} xid_cache[XID_CACHE_SIZE];
+
+
+/*
+ * The following macros advance the pointer passed to them. They
+ * assume they are given a char *.
+ */
+#define GETINT8(v, ptr) { \
+ (v) = (*(ptr)++); \
+}
+
+#define GETINT16(v, ptr) { \
+ (v) = *(ptr)++ << 8; \
+ (v) |= *(ptr)++; \
+}
+
+#define GETINT32(v, ptr) { \
+ (v) = *(ptr)++ << 8; \
+ (v) |= *(ptr)++; (v) <<= 8; \
+ (v) |= *(ptr)++; (v) <<= 8; \
+ (v) |= *(ptr)++; \
+}
+
+/*
+ * Used to print nested protocol layers. For example, an ip datagram included
+ * in an icmp error, or a PPP packet included in an LCP protocol reject..
+ */
+extern char *prot_nest_prefix;
+
+extern char *get_sum_line(void);
+extern char *get_detail_line(int, int);
+extern struct timeval prev_time;
+extern char *getflag(int, int, char *, char *);
+extern void show_header(char *, char *, int);
+extern void xdr_init(char *, int);
+extern char *get_line(int, int);
+extern int get_line_remain(void);
+extern char getxdr_char(void);
+extern char showxdr_char(char *);
+extern uchar_t getxdr_u_char(void);
+extern uchar_t showxdr_u_char(char *);
+extern short getxdr_short(void);
+extern short showxdr_short(char *);
+extern ushort_t getxdr_u_short(void);
+extern ushort_t showxdr_u_short(char *);
+extern long getxdr_long(void);
+extern long showxdr_long(char *);
+extern ulong_t getxdr_u_long(void);
+extern ulong_t showxdr_u_long(char *);
+extern longlong_t getxdr_longlong(void);
+extern longlong_t showxdr_longlong(char *);
+extern ulonglong_t getxdr_u_longlong(void);
+extern ulonglong_t showxdr_u_longlong(char *);
+extern char *getxdr_opaque(char *, int);
+extern char *getxdr_string(char *, int);
+extern char *showxdr_string(int, char *);
+extern char *getxdr_bytes(uint_t *);
+extern void xdr_skip(int);
+extern int getxdr_pos(void);
+extern void setxdr_pos(int);
+extern char *getxdr_context(char *, int);
+extern char *showxdr_context(char *);
+extern enum_t getxdr_enum(void);
+extern void show_space(void);
+extern void show_trailer(void);
+extern char *getxdr_date(void);
+extern char *showxdr_date(char *);
+extern char *getxdr_date_ns(void);
+char *format_time(int64_t sec, uint32_t nsec);
+extern char *showxdr_date_ns(char *);
+extern char *getxdr_hex(int);
+extern char *showxdr_hex(int, char *);
+extern bool_t getxdr_bool(void);
+extern bool_t showxdr_bool(char *);
+extern char *concat_args(char **, int);
+extern int pf_compile(char *, int);
+extern void compile(char *, int);
+extern void load_names(char *);
+extern void cap_open_read(char *);
+extern void cap_open_write(char *);
+extern void cap_read(int, int, int, void (*)(), int);
+extern void cap_close(void);
+extern int check_device(char **, int *);
+extern void initdevice(char *, ulong_t, ulong_t, struct timeval *,
+ struct Pf_ext_packetfilt *, int);
+extern void net_read(int, int, void (*)(), int);
+extern void click(int);
+extern void show_pktinfo(int, int, char *, char *, struct timeval *,
+ struct timeval *, int, int);
+extern void show_line(char *);
+extern char *getxdr_time(void);
+extern char *showxdr_time(char *);
+extern char *addrtoname(int, void *);
+extern char *show_string(const char *, int, int);
+extern void pr_err(char *, ...);
+extern void check_retransmit(char *, ulong_t);
+extern char *nameof_prog(int);
+extern char *getproto(int);
+extern uint8_t print_ipv6_extensions(int, uint8_t **, uint8_t *, int *, int *);
+extern void protoprint(int, int, ulong_t, int, int, int, char *, int);
+extern char *getportname(int, in_port_t);
+
+extern void interpret_arp(int, struct arphdr *, int);
+extern void interpret_bparam(int, int, int, int, int, char *, int);
+extern void interpret_dns(int, int, const uchar_t *, int);
+extern void interpret_mount(int, int, int, int, int, char *, int);
+extern void interpret_nfs(int, int, int, int, int, char *, int);
+extern void interpret_nfs3(int, int, int, int, int, char *, int);
+extern void interpret_nfs4(int, int, int, int, int, char *, int);
+extern void interpret_nfs4_cb(int, int, int, int, int, char *, int);
+extern void interpret_nfs_acl(int, int, int, int, int, char *, int);
+extern void interpret_nis(int, int, int, int, int, char *, int);
+extern void interpret_nisbind(int, int, int, int, int, char *, int);
+extern void interpret_nisp_cb(int, int, int, int, int, char *, int);
+extern void interpret_nisplus(int, int, int, int, int, char *, int);
+extern void interpret_nlm(int, int, int, int, int, char *, int);
+extern void interpret_pmap(int, int, int, int, int, char *, int);
+extern int interpret_reserved(int, int, in_port_t, in_port_t, char *, int);
+extern void interpret_rquota(int, int, int, int, int, char *, int);
+extern void interpret_rstat(int, int, int, int, int, char *, int);
+extern void interpret_solarnet_fw(int, int, int, int, int, char *, int);
+extern void interpret_ldap(int, char *, int, int, int);
+extern void interpret_icmp(int, struct icmp *, int, int);
+extern void interpret_icmpv6(int, icmp6_t *, int, int);
+extern int interpret_ip(int, struct ip *, int);
+extern int interpret_ipv6(int, ip6_t *, int);
+extern int interpret_ppp(int, uchar_t *, int);
+extern int interpret_pppoe(int, poep_t *, int);
+extern void init_ldap(void);
+extern boolean_t arp_for_ether(char *, struct ether_addr *);
+extern char *ether_ouiname(uint32_t);
+char *tohex(char *p, int len);
+extern char *printether(struct ether_addr *);
+extern char *print_ethertype(int);
+
+/*
+ * Describes characteristics of the Media Access Layer.
+ * The mac_type is one of the supported DLPI media
+ * types (see <sys/dlpi.h>).
+ * The mtu_size is the size of the largest frame.
+ * The header length is returned by a function to
+ * allow for variable header size - for ethernet it's
+ * just a constant 14 octets.
+ * The interpreter is the function that "knows" how
+ * to interpret the frame.
+ */
+typedef struct interface {
+ uint_t mac_type;
+ uint_t mtu_size;
+ uint_t (*header_len)(char *);
+ uint_t (*interpreter)(int, char *, int, int);
+ uint_t mac_hdr_fixed_size;
+} interface_t;
+
+#define IF_HDR_FIXED 0
+#define IF_HDR_VAR 1
+
+extern interface_t INTERFACES[], *interface;
+extern char *device;
+
+extern char *dlc_header;
+
+/*
+ * Global error recovery routine: used to reset snoop variables after
+ * catastrophic failure.
+ */
+void snoop_recover(void);
+
+/*
+ * Global alarm handler structure for managing multiple alarms within
+ * snoop.
+ */
+static struct snoop_handler;
+typedef struct snoop_handler {
+ struct snoop_handler *s_next; /* next alarm handler */
+ time_t s_time; /* time to fire */
+ void (*s_handler)(); /* alarm handler */
+} snoop_handler_t;
+
+#define SNOOP_MAXRECOVER 20 /* maxium number of recoveries */
+#define SNOOP_ALARM_GRAN 3 /* alarm() timeout multiplier */
+
+/*
+ * Global alarm handler management routine.
+ */
+extern int snoop_alarm(int s_sec, void (*s_handler)());
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SNOOP_H */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_aarp.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_aarp.c
new file mode 100644
index 0000000000..32133ee58a
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_aarp.c
@@ -0,0 +1,143 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1991-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include <at.h>
+#include <snoop.h>
+
+static char *printat(uint8_t *);
+
+static char *aarp_opname[] = {
+ "",
+ "AARP Request",
+ "AARP Reply",
+ "AARP Probe",
+};
+
+void
+interpret_aarp(int flags, char *data, int alen)
+{
+ /* LINTED */
+ struct ether_arp *ap = (struct ether_arp *)data;
+
+ extern char *dst_name;
+
+ if (flags & F_SUM) {
+ if (alen < sizeof (struct ether_arp)) {
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "AARP (short packet)");
+ return;
+ }
+
+ switch (ntohs(ap->arp_op)) {
+ case AARP_REQ:
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "AARP C Who is %s ?",
+ printat(ap->arp_tpa));
+ break;
+ case AARP_RESP:
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "AARP R %s is %s",
+ printat(ap->arp_spa),
+ printether((struct ether_addr *)&ap->arp_sha));
+ dst_name = printat(ap->arp_tpa);
+ break;
+ case AARP_PROBE:
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "AARP Probe %s ?",
+ printat(ap->arp_tpa));
+ break;
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("AARP: ", "AARP Frame", alen);
+ show_space();
+
+ if (alen < sizeof (struct ether_arp)) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "AARP (short packet)");
+ return;
+ }
+
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Hardware type = %d",
+ ntohs(ap->arp_hrd));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Protocol type = %04X (%s)",
+ ntohs(ap->arp_pro),
+ print_ethertype(ntohs(ap->arp_pro)));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Length of hardware address = %d bytes",
+ ap->arp_hln);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Length of protocol address = %d bytes",
+ ap->arp_pln);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Opcode %d (%s)",
+ ntohs(ap->arp_op),
+ aarp_opname[ntohs(ap->arp_op)]);
+
+ if (ntohs(ap->arp_hrd) == ARPHRD_ETHER &&
+ ntohs(ap->arp_pro) == ETHERTYPE_AT) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Sender's hardware address = %s",
+ printether((struct ether_addr *)&ap->arp_sha));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Sender's protocol address = %s",
+ printat(ap->arp_spa));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Target hardware address = %s",
+ (ntohs(ap->arp_op) == AARP_REQ ||
+ ntohs(ap->arp_op) == AARP_PROBE) ? "?" :
+ printether((struct ether_addr *)&ap->arp_tha));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Target protocol address = %s",
+ ntohs(ap->arp_op) == REVARP_REQUEST ? "?" :
+ printat(ap->arp_tpa));
+ }
+ show_trailer();
+ }
+}
+
+static char *
+printat(uint8_t *p)
+{
+ static char buf[16];
+
+ (void) snprintf(buf, sizeof (buf), "%d.%d", get_short(&p[1]), p[3]);
+ return (buf);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_adsp.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_adsp.c
new file mode 100644
index 0000000000..e77183718e
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_adsp.c
@@ -0,0 +1,142 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1991-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#include <at.h>
+#include <snoop.h>
+
+static char *adsp_ctrl(uint8_t);
+
+void
+interpret_adsp(int flags, struct ddp_adsphdr *adp, int len)
+{
+ struct ddp_adsp_open *apo;
+
+ if (flags & F_SUM) {
+ if (len < sizeof (struct ddp_adsphdr)) {
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "ADSP (short packet)");
+ return;
+ }
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "ADSP ConnID=%u (%s)",
+ get_short(adp->ad_connid),
+ adsp_ctrl(adp->ad_desc));
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("ADSP: ", "ADSP Header",
+ len - sizeof (struct ddp_adsphdr));
+ show_space();
+
+ if (len < sizeof (struct ddp_adsphdr)) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "(short packet)");
+ return;
+ }
+
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "ConnID = %u, ByteSeq = %u, RecvSeq = %u",
+ get_short(adp->ad_connid),
+ get_long(adp->ad_fbseq),
+ get_long(adp->ad_nrseq));
+
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "RcvWin = %u, Ctrl = 0x%x (%s)",
+ get_short(adp->ad_rcvwin),
+ adp->ad_desc,
+ adsp_ctrl(adp->ad_desc));
+
+ switch (adp->ad_desc) {
+ case AD_CREQ: /* open requests */
+ case AD_CACK:
+ case AD_CREQ_ACK:
+ case AD_CDENY:
+ apo = (struct ddp_adsp_open *)adp;
+ if (len < sizeof (struct ddp_adsp_open)) {
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ "(short packet)");
+ return;
+ }
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Dest ConnID = %u, AttRcvSeq = %u",
+ get_short(apo->ad_dconnid),
+ get_long(apo->ad_attseq));
+ break;
+ }
+
+ if (adp->ad_desc & AD_ATT) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "AttCode = 0x%x",
+ get_short(((struct ddp_adsp_att *)adp)->
+ ad_att_code));
+ }
+ }
+}
+
+static char *adsp_ctrl_msg[] = {
+ "Probe/Ack",
+ "OpenConnReq",
+ "OpenConnAck",
+ "OpenConnReq+Ack",
+ "OpenConnDeny",
+ "CloseConnAdv",
+ "ForwReset",
+ "ForwReset Ack",
+ "RetransAdv",
+ "9", "10", "11", "12", "13", "14", "15",
+};
+
+static char *
+adsp_ctrl(uint8_t ctrl)
+{
+ static char buf[50];
+ char *p = buf;
+ char *tail = &buf[sizeof (buf)];
+
+ if (ctrl & AD_ACKREQ)
+ p += snprintf(p, tail-p, "AckReq");
+
+ if (ctrl & AD_EOM) {
+ p += snprintf(p, tail-p, p == buf ? "EOM" : " EOM");
+ }
+
+ if (ctrl & AD_ATT) {
+ p += snprintf(p, tail-p, p == buf ? "Att" : " Att");
+ }
+
+ if (ctrl & AD_CTRL) {
+ (void) snprintf(p, tail-p, "%s%s", p == buf ? "" : " ",
+ adsp_ctrl_msg[ctrl & AD_CTRL_MASK]);
+ }
+
+ return (buf);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_aecho.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_aecho.c
new file mode 100644
index 0000000000..348f6a91cd
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_aecho.c
@@ -0,0 +1,72 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#include <at.h>
+#include <snoop.h>
+
+void
+interpret_aecho(int flags, struct ddp_hdr *ddp, int len)
+{
+ char *data;
+
+ data = (char *)ddp + DDPHDR_SIZE;
+
+ if (flags & F_SUM) {
+ if (len < DDPHDR_SIZE + 1) {
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "AECHO (short packet)");
+ return;
+ }
+
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "AECHO F=%s LEN=%d",
+ *data == AEP_REQ ? "Request" : "Reply",
+ len);
+ }
+
+ if (flags & F_DTAIL) {
+ if (len < DDPHDR_SIZE + 1) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "AECHO (short packet)");
+ return;
+ }
+
+ show_header("AECHO: ", "AECHO Header", len);
+ show_space();
+
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Func = %d (%s)",
+ data[0],
+ data[0] == AEP_REQ ? "Request" : "Reply");
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Length = %d", len);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_apple.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_apple.c
new file mode 100644
index 0000000000..e1fbed0c57
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_apple.c
@@ -0,0 +1,210 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <at.h>
+#include <snoop.h>
+
+extern char *src_name, *dst_name;
+
+static struct socktable {
+ int pt_num;
+ char *pt_short;
+};
+
+static struct socktable pt_ddp[] = {
+ {1, "RTMP"},
+ {2, "NIS"},
+ {4, "Echoer"},
+ {6, "ZIS"},
+ {0, NULL},
+};
+
+static struct socktable pt_ddp_types[] = {
+ {1, "RTMP Resp"},
+ {2, "NBP"},
+ {3, "ATP"},
+ {4, "AEP"},
+ {5, "RTMP Req"},
+ {6, "ZIP"},
+ {7, "ADSP"},
+ {0, NULL},
+};
+
+static char *
+apple_ddp_type(struct socktable *p, uint16_t port)
+{
+ for (; p->pt_num != 0; p++) {
+ if (port == p->pt_num)
+ return (p->pt_short);
+ }
+ return (NULL);
+}
+
+/*
+ * return the short at p, regardless of alignment
+ */
+
+uint16_t
+get_short(uint8_t *p)
+{
+ return (p[0] << 8 | p[1]);
+}
+
+/*
+ * return the long at p, regardless of alignment
+ */
+uint32_t
+get_long(uint8_t *p)
+{
+ return (p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]);
+}
+
+/*
+ * format a MAC address
+ */
+
+char *
+print_macaddr(uint8_t *ha, int len)
+{
+ static char buf[128];
+ char *p = buf;
+
+ while (len-- != 0) {
+ p += snprintf(p, sizeof (buf) - (p - buf),
+ len > 0 ? "%x:" : "%x", *ha++);
+ }
+ return (buf);
+}
+
+/* ARGSUSED */
+void
+interpret_at(int flags, struct ddp_hdr *ddp, int len)
+{
+ int ddplen;
+ char *pname;
+ char buff [32];
+ static char src_buf[16];
+ static char dst_buf[16];
+
+ if (ddp_pad(ddp) != 0)
+ return; /* unknown AppleTalk proto */
+
+ ddplen = ddp_len(ddp);
+
+ (void) snprintf(src_buf, sizeof (src_buf),
+ "%u.%u", ntohs(ddp->ddp_src_net), ddp->ddp_src_id);
+ src_name = src_buf;
+
+ (void) snprintf(dst_buf, sizeof (dst_buf),
+ "%u.%u", ntohs(ddp->ddp_dest_net), ddp->ddp_dest_id);
+ if (ddp->ddp_dest_id == NODE_ID_BROADCAST)
+ dst_name = "(broadcast)";
+ else
+ dst_name = dst_buf;
+
+ if (flags & F_SUM) {
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "DDP S=%u.%u:%u D=%u.%u:%u LEN=%d",
+ ntohs(ddp->ddp_src_net),
+ ddp->ddp_src_id,
+ ddp->ddp_src_sock,
+ ntohs(ddp->ddp_dest_net),
+ ddp->ddp_dest_id,
+ ddp->ddp_dest_sock,
+ ddp_len(ddp));
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("DDP: ", "DDP Header", ddplen - DDPHDR_SIZE);
+ show_space();
+ pname = apple_ddp_type(pt_ddp, ddp->ddp_src_sock);
+ if (pname == NULL) {
+ pname = "";
+ } else {
+ (void) snprintf(buff, sizeof (buff), "(%s)", pname);
+ pname = buff;
+ }
+
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Source = %s, Socket = %u %s",
+ src_name, ddp->ddp_src_sock, pname);
+ pname = apple_ddp_type(pt_ddp, ddp->ddp_dest_sock);
+ if (pname == NULL) {
+ pname = "";
+ } else {
+ (void) snprintf(buff, sizeof (buff), "(%s)", pname);
+ pname = buff;
+ }
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Destination = %s, Socket = %u %s",
+ dst_name, ddp->ddp_dest_sock, pname);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Hop count = %d",
+ ddp_hop(ddp));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Length = %d",
+ ddp_len(ddp));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Checksum = %04x %s",
+ ntohs(ddp->ddp_cksum),
+ ddp->ddp_cksum == 0 ? "(no checksum)" : "");
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "DDP type = %d (%s)",
+ ddp->ddp_type,
+ apple_ddp_type(pt_ddp_types, ddp->ddp_type));
+ show_space();
+ }
+
+
+ /* go to the next protocol layer */
+
+ switch (ddp->ddp_type) {
+ case DDP_TYPE_NBP:
+ interpret_nbp(flags, (struct nbp_hdr *)ddp, ddplen);
+ break;
+ case DDP_TYPE_AEP:
+ interpret_aecho(flags, ddp, ddplen);
+ break;
+ case DDP_TYPE_ATP:
+ interpret_atp(flags, ddp, ddplen);
+ break;
+ case DDP_TYPE_ZIP:
+ interpret_ddp_zip(flags, (struct zip_hdr *)ddp, ddplen);
+ break;
+ case DDP_TYPE_ADSP:
+ interpret_adsp(flags, (struct ddp_adsphdr *)ddp, ddplen);
+ break;
+ case DDP_TYPE_RTMPRQ:
+ case DDP_TYPE_RTMPRESP:
+ interpret_rtmp(flags, ddp, ddplen);
+ break;
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_arp.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_arp.c
new file mode 100644
index 0000000000..2af76ced99
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_arp.c
@@ -0,0 +1,209 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1991-2001, 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <setjmp.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/if_ether.h>
+#include <netdb.h>
+#include <net/if_types.h>
+
+#include "snoop.h"
+
+extern char *dlc_header;
+extern jmp_buf xdr_err;
+
+static char *printip(unsigned char *);
+static char *addrtoname_align(unsigned char *);
+
+static char unarp_addr[] = "Unknown";
+char *opname[] = {
+ "",
+ "ARP Request",
+ "ARP Reply",
+ "REVARP Request",
+ "REVARP Reply",
+};
+
+void
+interpret_arp(int flags, struct arphdr *ap, int alen)
+{
+ char *line;
+ extern char *src_name, *dst_name;
+ unsigned char *sip, *tip, *sha, *tha;
+ char *smacbuf = NULL, *dmacbuf = NULL;
+ int maclen;
+ ushort_t arpop;
+ boolean_t is_ip = B_FALSE;
+
+ /*
+ * Check that at least the generic ARP header was received.
+ */
+ if (sizeof (struct arphdr) > alen)
+ goto short_packet;
+
+ arpop = ntohs(ap->ar_op);
+ maclen = ap->ar_hln;
+ if (ntohs(ap->ar_pro) == ETHERTYPE_IP)
+ is_ip = B_TRUE;
+
+ sha = (unsigned char *)(ap + 1);
+ sip = sha + maclen;
+ tha = sip + ap->ar_pln;
+ tip = tha + maclen;
+
+ /*
+ * Check that the protocol/hardware addresses were received.
+ */
+ if ((tip + ap->ar_pln) > ((unsigned char *)ap + alen))
+ goto short_packet;
+
+ if (maclen == 0) {
+ smacbuf = dmacbuf = unarp_addr;
+ } else {
+ if (((flags & F_DTAIL) && is_ip) || (arpop == ARPOP_REPLY)) {
+ smacbuf = _link_ntoa(sha, NULL, maclen, IFT_OTHER);
+ if (smacbuf == NULL)
+ pr_err("Warning: malloc failure");
+ }
+
+ if (((flags & F_DTAIL) && is_ip) || (arpop ==
+ REVARP_REQUEST) || (arpop == REVARP_REPLY)) {
+ dmacbuf = _link_ntoa(tha, NULL, maclen, IFT_OTHER);
+ if (dmacbuf == NULL)
+ pr_err("Warning: malloc failure");
+ }
+ }
+
+ src_name = addrtoname_align(sip);
+
+ if (flags & F_SUM) {
+
+ line = get_sum_line();
+
+ switch (arpop) {
+ case ARPOP_REQUEST:
+ (void) snprintf(line, MAXLINE, "ARP C Who is %s ?",
+ printip(tip));
+ break;
+ case ARPOP_REPLY:
+ (void) snprintf(line, MAXLINE, "ARP R %s is %s",
+ printip(sip), smacbuf);
+ dst_name = addrtoname_align(tip);
+ break;
+ case REVARP_REQUEST:
+ (void) snprintf(line, MAXLINE, "RARP C Who is %s ?",
+ dmacbuf);
+ break;
+ case REVARP_REPLY:
+ (void) snprintf(line, MAXLINE, "RARP R %s is %s",
+ dmacbuf, printip(tip));
+ dst_name = addrtoname_align(tip);
+ break;
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("ARP: ", "ARP/RARP Frame", alen);
+ show_space();
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Hardware type = %d", ntohs(ap->ar_hrd));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Protocol type = %04x (%s)", ntohs(ap->ar_pro),
+ print_ethertype(ntohs(ap->ar_pro)));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Length of hardware address = %d bytes", ap->ar_hln);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Length of protocol address = %d bytes", ap->ar_pln);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Opcode %d (%s)", arpop,
+ (arpop > REVARP_REPLY) ? opname[0] : opname[arpop]);
+
+ if (is_ip) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Sender's hardware address = %s", smacbuf);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Sender's protocol address = %s",
+ printip(sip));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Target hardware address = %s",
+ arpop == ARPOP_REQUEST ? "?" : dmacbuf);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Target protocol address = %s",
+ arpop == REVARP_REQUEST ? "?" :
+ printip(tip));
+ }
+ show_trailer();
+ }
+
+ if (maclen != 0) {
+ free(smacbuf);
+ free(dmacbuf);
+ }
+ return;
+
+short_packet:
+ if (flags & F_SUM) {
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "ARP (short packet)");
+ } else if (flags & F_DTAIL) {
+ show_header("ARP: ", "ARP/RARP Frame", alen);
+ show_space();
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "ARP (short packet)");
+ }
+}
+
+char *
+printip(unsigned char *p)
+{
+ static char buff[MAXHOSTNAMELEN + 32];
+ char *ap, *np;
+ struct in_addr a;
+
+ memcpy(&a, p, 4);
+ ap = (char *)inet_ntoa(a);
+ np = (char *)addrtoname(AF_INET, &a);
+ (void) snprintf(buff, MAXHOSTNAMELEN, "%s, %s", ap, np);
+ return (buff);
+}
+
+char *
+addrtoname_align(unsigned char *p)
+{
+ struct in_addr a;
+
+ memcpy(&a, p, 4);
+ return ((char *)addrtoname(AF_INET, &a));
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_atp.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_atp.c
new file mode 100644
index 0000000000..31808ecc15
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_atp.c
@@ -0,0 +1,128 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#include <at.h>
+#include <snoop.h>
+
+static char *atp_ci(uint8_t);
+
+static char *atp_trel[8] = {
+ "30s",
+ "1m",
+ "2m",
+ "4m",
+ "8m",
+ "(undef 5)",
+ "(undef 6)",
+ "(undef 7)"
+};
+
+void
+interpret_atp(int flags, struct ddp_hdr *ddp, int len)
+{
+ struct atp_hdr *atp = (struct atp_hdr *)ddp;
+ int atplen = len - (DDPHDR_SIZE + ATPHDR_SIZE);
+
+ if (flags & F_SUM) {
+ if (atplen < 0) {
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "ATP (short packet)");
+ return;
+ }
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "ATP (%s), TID=%d, L=%d",
+ atp_ci(atp->atp_ctrl),
+ get_short((uint8_t *)&atp->atp_tid),
+ len);
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("ATP: ", "ATP Header", 8);
+ show_space();
+
+ if (atplen < 0) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "ATP (short packet)");
+ return;
+ }
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Length = %d", len);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Ctrl = 0x%x (%s), bitmap/seq = 0x%x",
+ atp->atp_ctrl,
+ atp_ci(atp->atp_ctrl),
+ atp->atp_seq);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "TID = %d, user bytes 0x%x 0x%x 0x%x 0x%x",
+ get_short((uint8_t *)&atp->atp_tid),
+ atp->atp_user[0], atp->atp_user[1],
+ atp->atp_user[2], atp->atp_user[3]);
+ show_space();
+ }
+
+ if (ddp->ddp_dest_sock == DDP_TYPE_ZIP ||
+ ddp->ddp_src_sock == DDP_TYPE_ZIP)
+ interpret_atp_zip(flags, atp, atplen);
+}
+
+static char *
+atp_ci(uint8_t ci)
+{
+ static char buf[50];
+ char *p = buf;
+ char *to = NULL;
+ char *tail = &buf[sizeof (buf)];
+
+ switch (atp_fun(ci)) {
+ case ATP_TREQ:
+ p += snprintf(p, tail-p, "TReq");
+ to = atp_trel[atp_tmo(ci)];
+ break;
+ case ATP_TRESP:
+ p += snprintf(p, tail-p, "TResp");
+ break;
+ case ATP_TREL:
+ p += snprintf(p, tail-p, "TRel");
+ break;
+ }
+
+ p += snprintf(p, tail-p, ci & ATP_FLG_XO ? " XO" : " ALO");
+
+ if (ci & ATP_FLG_EOM)
+ p += snprintf(p, tail-p, " EOM");
+
+ if (ci & ATP_FLG_STS)
+ p += snprintf(p, tail-p, " STS");
+
+ if (to != NULL)
+ (void) snprintf(p, tail-p, " %s", to);
+ return (buf);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_bparam.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_bparam.c
new file mode 100644
index 0000000000..5d1be717bf
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_bparam.c
@@ -0,0 +1,220 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1991, 1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ident "%Z%%M% %I% %E% SMI" /* SunOS */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <setjmp.h>
+#include <string.h>
+
+#include <netinet/in.h>
+#include <rpc/types.h>
+#include <rpc/rpc.h>
+#include <rpc/xdr.h>
+#include <rpc/auth.h>
+#include <rpc/clnt.h>
+#include <rpc/rpc_msg.h>
+#include <rpcsvc/bootparam_prot.h>
+#include "snoop.h"
+
+extern char *dlc_header;
+extern jmp_buf xdr_err;
+
+static void show_address(char *);
+static char *sum_address(void);
+
+static char *procnames_short[] = {
+ "Null", /* 0 */
+ "WHOAMI?", /* 1 */
+ "GETFILE", /* 2 */
+};
+
+static char *procnames_long[] = {
+ "Null procedure", /* 0 */
+ "Who am I?", /* 1 */
+ "Get file name", /* 2 */
+};
+
+#define MAXPROC 2
+
+void
+interpret_bparam(flags, type, xid, vers, proc, data, len)
+ int flags, type, xid, vers, proc;
+ char *data;
+ int len;
+{
+ char *line;
+ char buff[MAX_PATH_LEN + 1];
+ char buff2[MAX_MACHINE_NAME + 1];
+
+ if (proc < 0 || proc > MAXPROC)
+ return;
+
+ if (flags & F_SUM) {
+ if (setjmp(xdr_err)) {
+ return;
+ }
+
+ line = get_sum_line();
+
+ if (type == CALL) {
+ (void) sprintf(line,
+ "BPARAM C %s",
+ procnames_short[proc]);
+ line += strlen(line);
+
+ switch (proc) {
+ case BOOTPARAMPROC_WHOAMI:
+ (void) sprintf(line, " %s",
+ sum_address());
+ break;
+ case BOOTPARAMPROC_GETFILE:
+ (void) getxdr_string(buff,
+ MAX_MACHINE_NAME);
+ (void) sprintf(line, " %s",
+ getxdr_string(buff,
+ MAX_FILEID));
+ break;
+ }
+
+ check_retransmit(line, xid);
+ } else {
+ (void) sprintf(line, "BPARAM R %s ",
+ procnames_short[proc]);
+ line += strlen(line);
+ switch (proc) {
+ case BOOTPARAMPROC_WHOAMI:
+ (void) getxdr_string(buff,
+ MAX_MACHINE_NAME);
+ (void) getxdr_string(buff2,
+ MAX_MACHINE_NAME);
+ (void) sprintf(line, "%s in %s",
+ buff, buff2);
+ break;
+ case BOOTPARAMPROC_GETFILE:
+ (void) getxdr_string(buff,
+ MAX_MACHINE_NAME);
+ (void) sum_address();
+ (void) sprintf(line, "File=%s",
+ getxdr_string(buff,
+ MAX_PATH_LEN));
+ break;
+ }
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("BPARAM: ", "Boot Parameters", len);
+ show_space();
+ if (setjmp(xdr_err)) {
+ return;
+ }
+ (void) sprintf(get_line(0, 0),
+ "Proc = %d (%s)",
+ proc, procnames_long[proc]);
+
+ if (type == CALL) {
+ switch (proc) {
+ case BOOTPARAMPROC_WHOAMI:
+ show_address("Client address");
+ break;
+
+ case BOOTPARAMPROC_GETFILE:
+ (void) showxdr_string(MAX_MACHINE_NAME,
+ "Hostname = %s");
+ (void) showxdr_string(MAX_FILEID,
+ "File = %s");
+ break;
+ }
+ } else {
+ switch (proc) {
+ case BOOTPARAMPROC_WHOAMI:
+ (void) showxdr_string(MAX_MACHINE_NAME,
+ "Client name = %s");
+ (void) showxdr_string(MAX_MACHINE_NAME,
+ "Domain name = %s");
+ show_address("Router addr");
+ break;
+
+ case BOOTPARAMPROC_GETFILE:
+ (void) showxdr_string(MAX_MACHINE_NAME,
+ "Server name = %s");
+ show_address("Server addr");
+ (void) showxdr_string(MAX_PATH_LEN,
+ "Server file = %s");
+ break;
+ }
+ }
+
+ show_trailer();
+ }
+}
+
+static char *
+sum_address()
+{
+ struct in_addr host;
+ extern char *inet_ntoa();
+ int atype;
+
+ atype = getxdr_u_long();
+ if (atype != IP_ADDR_TYPE)
+ return ("?");
+
+ host.S_un.S_un_b.s_b1 = getxdr_char();
+ host.S_un.S_un_b.s_b2 = getxdr_char();
+ host.S_un.S_un_b.s_b3 = getxdr_char();
+ host.S_un.S_un_b.s_b4 = getxdr_char();
+
+ return (inet_ntoa(host));
+}
+
+static void
+show_address(label)
+ char *label;
+{
+ struct in_addr host;
+ extern char *inet_ntoa();
+ int atype;
+
+ atype = getxdr_u_long();
+ if (atype == IP_ADDR_TYPE) {
+ host.S_un.S_un_b.s_b1 = getxdr_char();
+ host.S_un.S_un_b.s_b2 = getxdr_char();
+ host.S_un.S_un_b.s_b3 = getxdr_char();
+ host.S_un.S_un_b.s_b4 = getxdr_char();
+
+ (void) sprintf(get_line(0, 0),
+ "%s = %s (%s)",
+ label,
+ inet_ntoa(host),
+ addrtoname(AF_INET, &host));
+ } else {
+ (void) sprintf(get_line(0, 0),
+ "Router addr = ? (type not known)");
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_capture.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_capture.c
new file mode 100644
index 0000000000..bb30f38d98
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_capture.c
@@ -0,0 +1,912 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI" /* SunOS */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <setjmp.h>
+#include <sys/types.h>
+#include <sys/signal.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <sys/pfmod.h>
+#include <netinet/if_ether.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/stropts.h>
+#include <sys/bufmod.h>
+#include <sys/dlpi.h>
+
+#include <unistd.h>
+#include <stropts.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <values.h>
+#include <libdlpi.h>
+
+#include "snoop.h"
+
+void scan();
+void convert_to_network();
+void convert_from_network();
+void convert_old();
+extern int quitting;
+extern sigjmp_buf jmp_env, ojmp_env;
+int netfd;
+union DL_primitives netdl; /* info_ack for interface */
+char *bufp; /* pointer to read buffer */
+
+extern unsigned int encap_levels;
+
+/*
+ * Convert a device id to a ppa value
+ * e.g. "le0" -> 0
+ */
+int
+device_ppa(device)
+ char *device;
+{
+ char *p;
+ char *tp;
+
+ p = strpbrk(device, "0123456789");
+ if (p == NULL)
+ return (0);
+ /* ignore numbers within device names */
+ for (tp = p; *tp != '\0'; tp++)
+ if (!isdigit(*tp))
+ return (device_ppa(tp));
+ return (atoi(p));
+}
+
+/*
+ * Convert a device id to a pathname.
+ * Level 1 devices: "le0" -> "/dev/le0".
+ * Level 2 devices: "le0" -> "/dev/le".
+ */
+char *
+device_path(device)
+ char *device;
+{
+ static char buff[IF_NAMESIZE + 1];
+ struct stat st;
+ char *p;
+
+ (void) strcpy(buff, "/dev/");
+ (void) strlcat(buff, device, IF_NAMESIZE);
+
+ if (stat(buff, &st) == 0)
+ return (buff);
+
+ for (p = buff + (strlen(buff) - 1); p > buff; p--) {
+ if (isdigit(*p))
+ *p = '\0';
+ else
+ break;
+ }
+ return (buff);
+}
+
+/*
+ * Open up the device, and start finding out something about it,
+ * especially stuff about the data link headers. We need that information
+ * to build the proper packet filters.
+ */
+int
+check_device(devicep, ppap)
+ char **devicep;
+ int *ppap;
+{
+ char *devname;
+ /*
+ * Determine which network device
+ * to use if none given.
+ * Should get back a value like "le0".
+ */
+
+ if (*devicep == NULL) {
+ char *cbuf;
+ static struct ifconf ifc;
+ static struct ifreq *ifr;
+ int s;
+ int n;
+ int numifs;
+ unsigned bufsize;
+
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
+ pr_err("socket");
+
+ if (ioctl(s, SIOCGIFNUM, (char *)&numifs) < 0) {
+ pr_err("check_device: ioctl SIOCGIFNUM");
+ (void) close(s);
+ s = -1;
+ return;
+ }
+
+ bufsize = numifs * sizeof (struct ifreq);
+ cbuf = (char *)malloc(bufsize);
+ if (cbuf == NULL) {
+ pr_err("out of memory\n");
+ (void) close(s);
+ s = -1;
+ return;
+ }
+ ifc.ifc_len = bufsize;
+ ifc.ifc_buf = cbuf;
+ if (ioctl(s, SIOCGIFCONF, (char *)&ifc) < 0) {
+ pr_err("check_device: ioctl SIOCGIFCONF");
+ (void) close(s);
+ s = -1;
+ (void) free(cbuf);
+ return;
+ }
+ n = ifc.ifc_len / sizeof (struct ifreq);
+ ifr = ifc.ifc_req;
+ for (; n > 0; n--, ifr++) {
+ if (strchr(ifr->ifr_name, ':') != NULL)
+ continue;
+ if (ioctl(s, SIOCGIFFLAGS, (char *)ifr) < 0)
+ pr_err("ioctl SIOCGIFFLAGS");
+ if ((ifr->ifr_flags &
+ (IFF_VIRTUAL|IFF_LOOPBACK|IFF_UP|
+ IFF_RUNNING)) == (IFF_UP|IFF_RUNNING))
+ break;
+ }
+
+ if (n == 0)
+ pr_err("No network interface devices found");
+
+ *devicep = ifr->ifr_name;
+ (void) close(s);
+ }
+
+ devname = device_path(*devicep);
+ if ((netfd = open(devname, O_RDWR)) < 0)
+ pr_err("%s: %m", devname);
+
+ *ppap = device_ppa(*devicep);
+
+ /*
+ * Check for DLPI Version 2.
+ */
+ dlinforeq(netfd, &netdl);
+ if (netdl.info_ack.dl_version != DL_VERSION_2)
+ pr_err("DL_INFO_ACK: incompatible version %d",
+ netdl.info_ack.dl_version);
+
+ /*
+ * Attach for DLPI Style 2.
+ */
+ if (netdl.info_ack.dl_provider_style == DL_STYLE2) {
+ dlattachreq(netfd, *ppap);
+ /* Reread more specific information */
+ dlinforeq(netfd, &netdl);
+ }
+
+ /* Enable passive mode so that we can snoop on aggregated links. */
+ dlpi_passive(netfd, -1);
+
+ for (interface = &INTERFACES[0]; interface->mac_type != -1; interface++)
+ if (interface->mac_type == netdl.info_ack.dl_mac_type)
+ break;
+
+ /* allow limited functionality even is interface isn't known */
+ if (interface->mac_type == -1) {
+ fprintf(stderr, "snoop: WARNING: Mac Type = %x not supported\n",
+ netdl.info_ack.dl_mac_type);
+ }
+
+ /* for backward compatibility, allow known interface mtu_sizes */
+ if (interface->mtu_size > (uint_t)netdl.info_ack.dl_max_sdu)
+ netdl.info_ack.dl_max_sdu = (t_scalar_t)interface->mtu_size;
+
+ if (interface->mac_hdr_fixed_size == IF_HDR_FIXED)
+ return (1);
+
+ return (0);
+}
+
+/*
+ * Do whatever is necessary to initialize the interface
+ * for packet capture. Bind the device opened and attached (if DL_STYLE2)
+ * in check_device(), request raw ethernet packets and set promiscuous mode,
+ * push the streams buffer module and packet filter module, set various buffer
+ * parameters.
+ */
+void
+initdevice(device, snaplen, chunksize, timeout, fp, ppa)
+ char *device;
+ ulong_t snaplen, chunksize;
+ struct timeval *timeout;
+ struct Pf_ext_packetfilt *fp;
+ int ppa;
+{
+ union DL_primitives dl;
+ extern int Pflg;
+
+ /*
+ * Bind to SAP 2 on token ring, 0 on other interface types.
+ * (SAP 0 has special significance on token ring)
+ */
+ if (interface->mac_type == DL_TPR)
+ dlbindreq(netfd, 2, 0, DL_CLDLS, 0);
+ else
+ dlbindreq(netfd, 0, 0, DL_CLDLS, 0);
+
+ (void) fprintf(stderr, "Using device %s ", device_path(device));
+
+ /*
+ * If Pflg not set - use physical level
+ * promiscuous mode. Otherwise - just SAP level.
+ */
+ if (!Pflg) {
+ (void) fprintf(stderr, "(promiscuous mode)\n");
+ dlpromiscon(netfd, DL_PROMISC_PHYS);
+ } else {
+ (void) fprintf(stderr, "(non promiscuous)\n");
+ dlpromiscon(netfd, DL_PROMISC_MULTI);
+ }
+
+ dlpromiscon(netfd, DL_PROMISC_SAP);
+
+ if (ioctl(netfd, DLIOCRAW, 0) < 0) {
+ close(netfd);
+ pr_err("ioctl: DLIOCRAW: %s: %m", device_path(device));
+ }
+
+ if (fp) {
+ /*
+ * push and configure the packet filtering module
+ */
+ if (ioctl(netfd, I_PUSH, "pfmod") < 0) {
+ close(netfd);
+ pr_err("ioctl: I_PUSH pfmod: %s: %m",
+ device_path(device));
+ }
+
+ if (strioctl(netfd, PFIOCSETF, -1, sizeof (*fp), fp) < 0) {
+ close(netfd);
+ pr_err("PFIOCSETF: %s: %m", device_path(device));
+ }
+ }
+
+ if (ioctl(netfd, I_PUSH, "bufmod") < 0) {
+ close(netfd);
+ pr_err("push bufmod: %s: %m", device_path(device));
+ }
+
+ if (strioctl(netfd, SBIOCSTIME, -1, sizeof (struct timeval),
+ timeout) < 0) {
+ close(netfd);
+ pr_err("SBIOCSTIME: %s: %m", device_path(device));
+ }
+
+ if (strioctl(netfd, SBIOCSCHUNK, -1, sizeof (uint_t),
+ &chunksize) < 0) {
+ close(netfd);
+ pr_err("SBIOCGCHUNK: %s: %m", device_path(device));
+ }
+
+ if (strioctl(netfd, SBIOCSSNAP, -1, sizeof (uint_t),
+ &snaplen) < 0) {
+ close(netfd);
+ pr_err("SBIOCSSNAP: %s: %m", device_path(device));
+ }
+
+ /*
+ * Flush the read queue, to get rid of anything that
+ * accumulated before the device reached its final configuration.
+ */
+ if (ioctl(netfd, I_FLUSH, FLUSHR) < 0) {
+ close(netfd);
+ pr_err("I_FLUSH: %s: %m", device_path(device));
+ }
+}
+
+/*
+ * Read packets from the network. Initdevice is called in
+ * here to set up the network interface for reading of
+ * raw ethernet packets in promiscuous mode into a buffer.
+ * Packets are read and either written directly to a file
+ * or interpreted for display on the fly.
+ */
+void
+net_read(chunksize, filter, proc, flags)
+ int chunksize, filter;
+ void (*proc)();
+ int flags;
+{
+ int r = 0;
+ struct strbuf data;
+ int flgs;
+ extern int count;
+
+ data.len = 0;
+ count = 0;
+
+ /* allocate a read buffer */
+
+ bufp = malloc(chunksize);
+ if (bufp == NULL)
+ pr_err("no memory for %dk buffer", chunksize);
+
+ /*
+ * read frames
+ */
+ for (;;) {
+ data.maxlen = chunksize;
+ data.len = 0;
+ data.buf = bufp;
+ flgs = 0;
+
+ r = getmsg(netfd, NULL, &data, &flgs);
+
+ if (r < 0 || quitting)
+ break;
+
+ if (data.len <= 0)
+ continue;
+
+ scan(bufp, data.len, filter, 0, 0, proc, 0, 0, flags);
+ }
+
+ free(bufp);
+ close(netfd);
+
+ if (!quitting) {
+ if (r < 0)
+ pr_err("network read failed: %m");
+ else
+ pr_err("network read returned %d", r);
+ }
+}
+
+#ifdef DEBUG
+/*
+ * corrupt: simulate packet corruption for debugging interpreters
+ */
+void
+corrupt(volatile char *pktp, volatile char *pstop, char *buf,
+ volatile char *bufstop)
+{
+ int c;
+ int i;
+ int p;
+ int li = rand() % (pstop - pktp - 1) + 1;
+ volatile char *pp = pktp;
+ volatile char *pe = bufstop < pstop ? bufstop : pstop;
+
+ if (pktp < buf || pktp > bufstop)
+ return;
+
+ for (pp = pktp; pp < pe; pp += li) {
+ c = ((pe - pp) < li ? pe - pp : li);
+ i = (rand() % c)>>1;
+ while (--i > 0) {
+ p = (rand() % c);
+ pp[p] = (unsigned char)(rand() & 0xFF);
+ }
+ }
+}
+#endif /* DEBUG */
+
+void
+scan(buf, len, filter, cap, old, proc, first, last, flags)
+ char *buf;
+ int len, filter, cap, old;
+ void (*proc)();
+ int first, last;
+ int flags;
+{
+ volatile char *bp, *bufstop;
+ volatile struct sb_hdr *hdrp;
+ volatile struct sb_hdr nhdr, *nhdrp;
+ volatile char *pktp;
+ volatile struct timeval last_timestamp;
+ volatile int header_okay;
+ extern int count, maxcount;
+ extern int snoop_nrecover;
+#ifdef DEBUG
+ extern int zflg;
+#endif /* DEBUG */
+
+ proc(0, 0, 0);
+ bufstop = buf + len;
+
+ /*
+ *
+ * Loop through each packet in the buffer
+ */
+ last_timestamp.tv_sec = 0;
+ (void) memcpy((char *)ojmp_env, (char *)jmp_env, sizeof (jmp_env));
+ for (bp = buf; bp < bufstop; bp += nhdrp->sbh_totlen) {
+ /*
+ * Gracefully exit if user terminates
+ */
+ if (quitting)
+ break;
+ /*
+ * Global error recocery: Prepare to continue when a corrupt
+ * packet or header is encountered.
+ */
+ if (sigsetjmp(jmp_env, 1)) {
+ goto err;
+ }
+
+ header_okay = 0;
+ hdrp = (struct sb_hdr *)bp;
+ nhdrp = hdrp;
+ pktp = (char *)hdrp + sizeof (*hdrp);
+
+ /*
+ * If reading a capture file
+ * convert the headers from network
+ * byte order (for little-endians like X86)
+ */
+ if (cap) {
+ /*
+ * If the packets come from an old
+ * capture file, convert the header.
+ */
+ if (old) {
+ convert_old(hdrp);
+ }
+
+ nhdrp = &nhdr;
+
+ nhdrp->sbh_origlen = ntohl(hdrp->sbh_origlen);
+ nhdrp->sbh_msglen = ntohl(hdrp->sbh_msglen);
+ nhdrp->sbh_totlen = ntohl(hdrp->sbh_totlen);
+ nhdrp->sbh_drops = ntohl(hdrp->sbh_drops);
+ nhdrp->sbh_timestamp.tv_sec =
+ ntohl(hdrp->sbh_timestamp.tv_sec);
+ nhdrp->sbh_timestamp.tv_usec =
+ ntohl(hdrp->sbh_timestamp.tv_usec);
+ }
+
+ /* Enhanced check for valid header */
+
+ if ((nhdrp->sbh_totlen == 0) ||
+ (bp + nhdrp->sbh_totlen) < bp ||
+ (bp + nhdrp->sbh_totlen) > bufstop ||
+ (nhdrp->sbh_origlen == 0) ||
+ (bp + nhdrp->sbh_origlen) < bp ||
+ (nhdrp->sbh_msglen == 0) ||
+ (bp + nhdrp->sbh_msglen) < bp ||
+ (bp + nhdrp->sbh_msglen) > bufstop ||
+ (nhdrp->sbh_msglen > nhdrp->sbh_origlen) ||
+ (nhdrp->sbh_totlen < nhdrp->sbh_msglen) ||
+ (nhdrp->sbh_timestamp.tv_sec == 0)) {
+ if (cap)
+ fprintf(stderr, "(warning) bad packet header "
+ "in capture file");
+ else
+ fprintf(stderr, "(warning) bad packet header "
+ "in buffer");
+ (void) fprintf(stderr, " offset %d: length=%d\n",
+ bp - buf, nhdrp->sbh_totlen);
+ goto err;
+ }
+
+ if (nhdrp->sbh_totlen >
+ (uint_t)(netdl.info_ack.dl_max_sdu + MAX_HDRTRAILER)) {
+ if (cap)
+ fprintf(stderr, "(warning) packet length "
+ "greater than MTU in capture file");
+ else
+ fprintf(stderr, "(warning) packet length "
+ "greater than MTU in buffer");
+
+ (void) fprintf(stderr, " offset %d: length=%d\n",
+ bp - buf, nhdrp->sbh_totlen);
+ }
+
+ /*
+ * Check for incomplete packet. We are conservative here,
+ * since we don't know how good the checking is in other
+ * parts of the code. We pass a partial packet, with
+ * a warning.
+ */
+ if (pktp + nhdrp->sbh_msglen > bufstop) {
+ fprintf(stderr, "truncated packet buffer\n");
+ nhdrp->sbh_msglen = bufstop - pktp;
+ }
+
+#ifdef DEBUG
+ if (zflg)
+ corrupt(pktp, pktp + nhdrp->sbh_msglen, buf, bufstop);
+#endif /* DEBUG */
+
+ header_okay = 1;
+ if (!filter ||
+ want_packet(pktp,
+ nhdrp->sbh_msglen,
+ nhdrp->sbh_origlen)) {
+ count++;
+
+ /*
+ * Start deadman timer for interpreter processing
+ */
+ (void) snoop_alarm(SNOOP_ALARM_GRAN*SNOOP_MAXRECOVER,
+ NULL);
+
+ encap_levels = 0;
+ if (!cap || count >= first)
+ proc(nhdrp, pktp, count, flags);
+
+ if (cap && count >= last) {
+ (void) snoop_alarm(0, NULL);
+ break;
+ }
+
+ if (maxcount && count >= maxcount) {
+ fprintf(stderr, "%d packets captured\n", count);
+ exit(0);
+ }
+
+ snoop_nrecover = 0; /* success */
+ (void) snoop_alarm(0, NULL);
+ last_timestamp = hdrp->sbh_timestamp; /* save stamp */
+ }
+ continue;
+err:
+ /*
+ * Corruption has been detected. Reset errors.
+ */
+ snoop_recover();
+
+ /*
+ * packet header was apparently okay. Continue.
+ */
+ if (header_okay)
+ continue;
+
+ /*
+ * Otherwise try to scan forward to the next packet, using
+ * the last known timestamp if it is available.
+ */
+ nhdrp = &nhdr;
+ nhdrp->sbh_totlen = 0;
+ if (last_timestamp.tv_sec == 0) {
+ bp += sizeof (int);
+ } else {
+ for (bp += sizeof (int); bp <= bufstop;
+ bp += sizeof (int)) {
+ hdrp = (struct sb_hdr *)bp;
+ /* An approximate timestamp located */
+ if ((hdrp->sbh_timestamp.tv_sec >> 8) ==
+ (last_timestamp.tv_sec >> 8))
+ break;
+ }
+ }
+ }
+ /* reset jmp_env for program exit */
+ (void) memcpy((char *)jmp_env, (char *)ojmp_env, sizeof (jmp_env));
+ proc(0, -1, 0);
+}
+
+/*
+ * Called if nwrite() encounters write problems.
+ */
+static void
+cap_write_error(const char *msgtype)
+{
+ (void) fprintf(stderr,
+ "snoop: cannot write %s to capture file: %s\n",
+ msgtype, strerror(errno));
+ exit(1);
+}
+
+/*
+ * Writes target buffer to the open file descriptor. Upon detection of a short
+ * write, an attempt to process the remaining bytes occurs until all anticipated
+ * bytes are written. An error status is returned to indicate any serious write
+ * failures.
+ */
+static int
+nwrite(int fd, const void *buffer, size_t buflen)
+{
+ size_t nwritten;
+ ssize_t nbytes = 0;
+ const char *buf = buffer;
+
+ for (nwritten = 0; nwritten < buflen; nwritten += nbytes) {
+ nbytes = write(fd, &buf[nwritten], buflen - nwritten);
+ if (nbytes == -1)
+ return (-1);
+ if (nbytes == 0) {
+ errno = EIO;
+ return (-1);
+ }
+ }
+ return (0);
+}
+
+/*
+ * Routines for opening, closing, reading and writing
+ * a capture file of packets saved with the -o option.
+ */
+static int capfile_out;
+
+/*
+ * The snoop capture file has a header to identify
+ * it as a capture file and record its version.
+ * A file without this header is assumed to be an
+ * old format snoop file.
+ *
+ * A version 1 header looks like this:
+ *
+ * 0 1 2 3 4 5 6 7 8 9 10 11
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+
+ * | s | n | o | o | p | \0| \0| \0| version | data
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+
+ * | word 0 | word 1 | word 2 |
+ *
+ *
+ * A version 2 header adds a word that identifies the MAC type.
+ * This allows for capture files from FDDI etc.
+ *
+ * 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ * | s | n | o | o | p | \0| \0| \0| version | MAC type | data
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ * | word 0 | word 1 | word 2 | word 3
+ *
+ */
+const char *snoop_id = "snoop\0\0\0";
+const int snoop_idlen = 8;
+const int snoop_version = 2;
+
+void
+cap_open_write(name)
+ char *name;
+{
+ int vers;
+ int rc;
+
+ capfile_out = open(name, O_CREAT | O_TRUNC | O_RDWR, 0666);
+ if (capfile_out < 0)
+ pr_err("%s: %m", name);
+
+ vers = htonl(snoop_version);
+ if ((rc = nwrite(capfile_out, snoop_id, snoop_idlen)) == -1)
+ cap_write_error("snoop_id");
+
+ if ((rc = nwrite(capfile_out, &vers, sizeof (int))) == -1)
+ cap_write_error("version");
+}
+
+
+void
+cap_close()
+{
+ close(capfile_out);
+}
+
+static char *cap_buffp = NULL;
+static int cap_len = 0;
+static int cap_new;
+
+void
+cap_open_read(name)
+ char *name;
+{
+ struct stat st;
+ int cap_vers;
+ int *word, device_mac_type;
+ int capfile_in;
+
+ capfile_in = open(name, O_RDONLY);
+ if (capfile_in < 0)
+ pr_err("couldn't open %s: %m", name);
+
+ if (fstat(capfile_in, &st) < 0)
+ pr_err("couldn't stat %s: %m", name);
+ cap_len = st.st_size;
+
+ cap_buffp = mmap(0, cap_len, PROT_READ, MAP_PRIVATE, capfile_in, 0);
+ close(capfile_in);
+ if ((int)cap_buffp == -1)
+ pr_err("couldn't mmap %s: %m", name);
+
+ /* Check if new snoop capture file format */
+
+ cap_new = bcmp(cap_buffp, snoop_id, snoop_idlen) == 0;
+
+ /*
+ * If new file - check version and
+ * set buffer pointer to point at first packet
+ */
+ if (cap_new) {
+ cap_vers = ntohl(*(int *)(cap_buffp + snoop_idlen));
+ cap_buffp += snoop_idlen + sizeof (int);
+ cap_len -= snoop_idlen + sizeof (int);
+
+ switch (cap_vers) {
+ case 1:
+ device_mac_type = DL_ETHER;
+ break;
+
+ case 2:
+ device_mac_type = ntohl(*((int *)cap_buffp));
+ cap_buffp += sizeof (int);
+ cap_len -= sizeof (int);
+ break;
+
+ default:
+ pr_err("capture file: %s: Version %d unrecognized\n",
+ name, cap_vers);
+ }
+
+ for (interface = &INTERFACES[0]; interface->mac_type != -1;
+ interface++)
+ if (interface->mac_type == device_mac_type)
+ break;
+
+ if (interface->mac_type == -1)
+ pr_err("Mac Type = %x is not supported\n",
+ device_mac_type);
+ } else {
+ /* Use heuristic to check if it's an old-style file */
+
+ device_mac_type = DL_ETHER;
+ word = (int *)cap_buffp;
+
+ if (!((word[0] < 1600 && word[1] < 1600) &&
+ (word[0] < word[1]) &&
+ (word[2] > 610000000 && word[2] < 770000000)))
+ pr_err("not a capture file: %s", name);
+
+ /* Change protection so's we can fix the headers */
+
+ if (mprotect(cap_buffp, cap_len, PROT_READ | PROT_WRITE) < 0)
+ pr_err("mprotect: %s: %m", name);
+ }
+ netdl.info_ack.dl_max_sdu = MAXINT; /* Decode any stored packet. */
+}
+
+void
+cap_read(first, last, filter, proc, flags)
+ int first, last;
+ int filter;
+ void (*proc)();
+ int flags;
+{
+ extern int count;
+
+ count = 0;
+
+ scan(cap_buffp, cap_len, filter, 1, !cap_new, proc, first, last, flags);
+
+ munmap(cap_buffp, cap_len);
+}
+
+void
+cap_write(hdrp, pktp, num, flags)
+ struct sb_hdr *hdrp;
+ char *pktp;
+ int num, flags;
+{
+ int pktlen, mac;
+ static int first = 1;
+ struct sb_hdr nhdr;
+ extern boolean_t qflg;
+ int rc;
+
+ if (hdrp == NULL)
+ return;
+
+ if (first) {
+ first = 0;
+ mac = htonl(interface->mac_type);
+ if ((rc = nwrite(capfile_out, &mac, sizeof (int))) == -1)
+ cap_write_error("mac_type");
+ }
+
+ pktlen = hdrp->sbh_totlen - sizeof (*hdrp);
+
+ /*
+ * Convert sb_hdr to network byte order
+ */
+ nhdr.sbh_origlen = htonl(hdrp->sbh_origlen);
+ nhdr.sbh_msglen = htonl(hdrp->sbh_msglen);
+ nhdr.sbh_totlen = htonl(hdrp->sbh_totlen);
+ nhdr.sbh_drops = htonl(hdrp->sbh_drops);
+ nhdr.sbh_timestamp.tv_sec = htonl(hdrp->sbh_timestamp.tv_sec);
+ nhdr.sbh_timestamp.tv_usec = htonl(hdrp->sbh_timestamp.tv_usec);
+
+ if ((rc = nwrite(capfile_out, &nhdr, sizeof (nhdr))) == -1)
+ cap_write_error("packet header");
+
+ if ((rc = nwrite(capfile_out, pktp, pktlen)) == -1)
+ cap_write_error("packet");
+
+ if (! qflg)
+ show_count();
+}
+
+/*
+ * Old header format.
+ * Actually two concatenated structs: nit_bufhdr + nit_head
+ */
+struct ohdr {
+ /* nit_bufhdr */
+ int o_msglen;
+ int o_totlen;
+ /* nit_head */
+ struct timeval o_time;
+ int o_drops;
+ int o_len;
+};
+
+/*
+ * Convert a packet header from
+ * old to new format.
+ */
+void
+convert_old(ohdrp)
+ struct ohdr *ohdrp;
+{
+ struct sb_hdr nhdr;
+
+ nhdr.sbh_origlen = ohdrp->o_len;
+ nhdr.sbh_msglen = ohdrp->o_msglen;
+ nhdr.sbh_totlen = ohdrp->o_totlen;
+ nhdr.sbh_drops = ohdrp->o_drops;
+ nhdr.sbh_timestamp = ohdrp->o_time;
+
+ *(struct sb_hdr *)ohdrp = nhdr;
+}
+
+strioctl(fd, cmd, timout, len, dp)
+int fd;
+int cmd;
+int timout;
+int len;
+char *dp;
+{
+ struct strioctl sioc;
+ int rc;
+
+ sioc.ic_cmd = cmd;
+ sioc.ic_timout = timout;
+ sioc.ic_len = len;
+ sioc.ic_dp = dp;
+ rc = ioctl(fd, I_STR, &sioc);
+
+ if (rc < 0)
+ return (rc);
+ else
+ return (sioc.ic_len);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_dhcp.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_dhcp.c
new file mode 100644
index 0000000000..2fd8871084
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_dhcp.c
@@ -0,0 +1,783 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1991-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/sysmacros.h>
+#include <netinet/in.h>
+#include <netinet/dhcp.h>
+#include <arpa/inet.h>
+#include <dhcp_inittab.h>
+#include <dhcp_symbol.h>
+#include "snoop.h"
+
+extern char *dlc_header;
+static char *show_htype(int);
+static const char *show_msgtype(unsigned char);
+static int show_options(unsigned char *, int);
+static void display_ip(int, char *, char *, unsigned char **);
+static void display_ascii(char *, char *, unsigned char **);
+static void display_number(char *, char *, unsigned char **);
+static void display_ascii_hex(char *, unsigned char **);
+static unsigned char bootmagic[] = BOOTMAGIC; /* rfc 1048 */
+
+static char *option_types[] = {
+"", /* 0 */
+"Subnet Mask", /* 1 */
+"UTC Time Offset", /* 2 */
+"Router", /* 3 */
+"RFC868 Time Servers", /* 4 */
+"IEN 116 Name Servers", /* 5 */
+"DNS Servers", /* 6 */
+"UDP LOG Servers", /* 7 */
+"RFC 865 Cookie Servers", /* 8 */
+"RFC 1179 Line Printer Servers (LPR)", /* 9 */
+"Impress Servers", /* 10 */
+"RFC 887 Resource Location Servers", /* 11 */
+"Client Hostname", /* 12 */
+"Boot File size in 512 byte Blocks", /* 13 */
+"Merit Dump File", /* 14 */
+"DNS Domain Name", /* 15 */
+"SWAP Server", /* 16 */
+"Client Root Path", /* 17 */
+"BOOTP options extensions path", /* 18 */
+"IP Forwarding Flag", /* 19 */
+"NonLocal Source Routing Flag", /* 20 */
+"Policy Filters for NonLocal Routing", /* 21 */
+"Maximum Datagram Reassembly Size", /* 22 */
+"Default IP Time To Live", /* 23 */
+"Path MTU Aging Timeout", /* 24 */
+"Path MTU Size Plateau Table", /* 25 */
+"Interface MTU Size", /* 26 */
+"All Subnets are Local Flag", /* 27 */
+"Broadcast Address", /* 28 */
+"Perform Mask Discovery Flag", /* 29 */
+"Mask Supplier Flag", /* 30 */
+"Perform Router Discovery Flag", /* 31 */
+"Router Solicitation Address", /* 32 */
+"Static Routes", /* 33 */
+"Trailer Encapsulation Flag", /* 34 */
+"ARP Cache Timeout Seconds", /* 35 */
+"Ethernet Encapsulation Flag", /* 36 */
+"TCP Default Time To Live", /* 37 */
+"TCP Keepalive Interval Seconds", /* 38 */
+"TCP Keepalive Garbage Flag", /* 39 */
+"NIS Domainname", /* 40 */
+"NIS Servers", /* 41 */
+"Network Time Protocol Servers", /* 42 */
+"Vendor Specific Options", /* 43 */
+"NetBIOS RFC 1001/1002 Name Servers", /* 44 */
+"NetBIOS Datagram Dist. Servers", /* 45 */
+"NetBIOS Node Type", /* 46 */
+"NetBIOS Scope", /* 47 */
+"X Window Font Servers", /* 48 */
+"X Window Display Manager Servers", /* 49 */
+"Requested IP Address", /* 50 */
+"IP Address Lease Time", /* 51 */
+"Option Field Overload Flag", /* 52 */
+"DHCP Message Type", /* 53 */
+"DHCP Server Identifier", /* 54 */
+"Option Request List", /* 55 */
+"Error Message", /* 56 */
+"Maximum DHCP Message Size", /* 57 */
+"Renewal (T1) Time Value", /* 58 */
+"Rebinding (T2) Time Value", /* 59 */
+"Client Class Identifier =", /* 60 */
+"Client Identifier =", /* 61 */
+"Netware IP Domain =", /* 62 */
+"Netware IP Options =", /* 63 */
+"NISPLUS Domainname", /* 64 */
+"NISPLUS Servers", /* 65 */
+"TFTP Server Name", /* 66 */
+"Option BootFile Name", /* 67 */
+"Mobile IP Agents", /* 68 */
+"Simple Mail (SMTP) Servers", /* 69 */
+"Post Office (POP3) Servers", /* 70 */
+"Net News (NNTP) Servers", /* 71 */
+"WorldWideWeb Servers", /* 72 */
+"Finger Servers", /* 73 */
+"Internet Relay Chat (IRC) Servers", /* 74 */
+"StreetTalk Servers", /* 75 */
+"StreetTalk Directory Assist. Servers", /* 76 */
+"User Class Identifier", /* 77 */
+};
+
+#define OPTIONS_ARRAY_SIZE 78
+
+int
+interpret_dhcp(int flags, PKT *dp, int len)
+{
+ if (flags & F_SUM) {
+ if ((memcmp(dp->cookie, bootmagic, sizeof (bootmagic)) == 0) &&
+ (len >= BASE_PKT_SIZE + 3) &&
+ dp->options[0] == CD_DHCP_TYPE) {
+ (void) sprintf(get_sum_line(),
+ "DHCP/BOOTP %s", show_msgtype(dp->options[2]));
+ } else {
+ switch (ntohs(dp->op)) {
+ case BOOTREQUEST:
+ (void) sprintf(get_sum_line(),
+ "DHCP/BOOTP BOOTREQUEST");
+ break;
+ case BOOTREPLY:
+ (void) sprintf(get_sum_line(),
+ "DHCP/BOOTP BOOTREPLY");
+ break;
+ }
+ }
+ }
+ if (flags & F_DTAIL) {
+ show_header("DHCP: ", "Dynamic Host Configuration Protocol",
+ len);
+ show_space();
+ (void) sprintf(get_line((char *)dp->htype - dlc_header, 1),
+ "Hardware address type (htype) = %d (%s)", dp->htype,
+ show_htype(dp->htype));
+ (void) sprintf(get_line((char *)dp->hlen - dlc_header, 1),
+ "Hardware address length (hlen) = %d octets", dp->hlen);
+ (void) sprintf(get_line((char *)dp->hops - dlc_header, 1),
+ "Relay agent hops = %d", dp->hops);
+ (void) sprintf(get_line((char *)dp->xid - dlc_header, 4),
+ "Transaction ID = 0x%x", ntohl(dp->xid));
+ (void) sprintf(get_line((char *)dp->secs - dlc_header, 2),
+ "Time since boot = %d seconds", ntohs(dp->secs));
+ (void) sprintf(get_line((char *)dp->flags - dlc_header, 2),
+ "Flags = 0x%.4x", ntohs(dp->flags));
+ (void) sprintf(get_line((char *)&dp->ciaddr - dlc_header, 4),
+ "Client address (ciaddr) = %s", inet_ntoa(dp->ciaddr));
+ (void) sprintf(get_line((char *)&dp->yiaddr - dlc_header, 4),
+ "Your client address (yiaddr) = %s",
+ inet_ntoa(dp->yiaddr));
+ (void) sprintf(get_line((char *)&dp->siaddr - dlc_header, 4),
+ "Next server address (siaddr) = %s",
+ inet_ntoa(dp->siaddr));
+ (void) sprintf(get_line((char *)&dp->giaddr - dlc_header, 4),
+ "Relay agent address (giaddr) = %s",
+ inet_ntoa(dp->giaddr));
+ if (dp->htype == 1) {
+ (void) sprintf(get_line((char *)dp->chaddr -
+ dlc_header, dp->hlen),
+ "Client hardware address (chaddr) = %.2X:%.2X:%.2X:%.2X:%.2X:%.2X",
+ dp->chaddr[0],
+ dp->chaddr[1],
+ dp->chaddr[2],
+ dp->chaddr[3],
+ dp->chaddr[4],
+ dp->chaddr[5]);
+ }
+ /*
+ * Check cookie, process options
+ */
+ if (memcmp(dp->cookie, bootmagic, sizeof (bootmagic)) != 0) {
+ (void) sprintf(get_line(0, 0),
+ "Unrecognized cookie: 0x%.2X%.2X%.2X%.2X\n",
+ dp->cookie[0],
+ dp->cookie[1],
+ dp->cookie[2],
+ dp->cookie[3]);
+ return (0);
+ }
+ show_space();
+ show_header("DHCP: ", "(Options) field options", len);
+ show_space();
+ switch (show_options(dp->options, (len - BASE_PKT_SIZE))) {
+ case 0:
+ /* No option overloading */
+ if (*(unsigned char *)(dp->sname) != '\0') {
+ (void) sprintf(get_line(0, 0),
+ "Server Name = %s", dp->sname);
+ }
+ if (*(unsigned char *)(dp->file) != '\0') {
+ (void) sprintf(get_line(0, 0),
+ "Boot File Name = %s", dp->file);
+ }
+ break;
+ case 1:
+ /* file field used */
+ if (*(unsigned char *)(dp->sname) != '\0') {
+ (void) sprintf(get_line(0, 0),
+ "Server Name = %s", dp->sname);
+ }
+ show_space();
+ show_header("DHCP: ", "(File) field options", len);
+ show_space();
+ (void) show_options(dp->file, 128);
+ break;
+ case 2:
+ /* sname field used for options */
+ if (*(unsigned char *)(dp->file) != '\0') {
+ (void) sprintf(get_line(0, 0),
+ "Boot File Name = %s", dp->file);
+ }
+ show_space();
+ show_header("DHCP: ", "(Sname) field options", len);
+ show_space();
+ (void) show_options(dp->sname, 64);
+ break;
+ case 3:
+ show_space();
+ show_header("DHCP: ", "(File) field options", len);
+ show_space();
+ (void) show_options(dp->file, 128);
+ show_space();
+ show_header("DHCP: ", "(Sname) field options", len);
+ show_space();
+ (void) show_options(dp->sname, 64);
+ break;
+ };
+ }
+ return (len);
+}
+static int
+show_options(unsigned char *cp, int len)
+{
+ char *prmpt;
+ unsigned char *end, *vend;
+ unsigned char *start, save;
+ int items, i;
+ int nooverload = 0;
+ ushort_t s_buf;
+ struct in_addr tmp;
+ char scratch[128];
+ dhcp_symbol_t *entry;
+ char *decoded_opt;
+ int opt_len;
+
+ start = cp;
+ end = (unsigned char *)cp + len;
+
+ while (start < end) {
+ if (*start == CD_PAD) {
+ start++;
+ continue;
+ }
+ if (*start == CD_END)
+ break; /* done */
+
+ save = *start++;
+ switch (save) {
+ /* Network order IP address(es) */
+ case CD_SUBNETMASK:
+ case CD_ROUTER_SOLICIT_SERV:
+ case CD_BROADCASTADDR:
+ case CD_REQUESTED_IP_ADDR:
+ case CD_SERVER_ID:
+ /* Single IP address */
+ if (*start != 4) {
+ (void) sprintf(get_line(0, 0),
+ "Error: Bad %s", option_types[save]);
+ } else {
+ start++;
+ display_ip(1, "%s = %s", option_types[save],
+ &start);
+ }
+ break;
+ case CD_ROUTER:
+ case CD_TIMESERV:
+ case CD_IEN116_NAME_SERV:
+ case CD_DNSSERV:
+ case CD_LOG_SERV:
+ case CD_COOKIE_SERV:
+ case CD_LPR_SERV:
+ case CD_IMPRESS_SERV:
+ case CD_RESOURCE_SERV:
+ case CD_SWAP_SERV:
+ case CD_NIS_SERV:
+ case CD_NTP_SERV:
+ case CD_NETBIOS_NAME_SERV:
+ case CD_NETBIOS_DIST_SERV:
+ case CD_XWIN_FONT_SERV:
+ case CD_XWIN_DISP_SERV:
+ case CD_NISPLUS_SERVS:
+ case CD_MOBILE_IP_AGENT:
+ case CD_SMTP_SERVS:
+ case CD_POP3_SERVS:
+ case CD_NNTP_SERVS:
+ case CD_WWW_SERVS:
+ case CD_FINGER_SERVS:
+ case CD_IRC_SERVS:
+ case CD_STREETTALK_SERVS:
+ case CD_STREETTALK_DA_SERVS:
+ /* Multiple IP addresses */
+ if ((*start % 4) != 0) {
+ (void) sprintf(get_line(0, 0),
+ "Error: Bad %s address",
+ option_types[save]);
+ } else {
+ display_ip((*start++ / 4), "%s at = %s",
+ option_types[save], &start);
+ }
+ break;
+ case CD_TFTP_SERV_NAME:
+ case CD_HOSTNAME:
+ case CD_DUMP_FILE:
+ case CD_DNSDOMAIN:
+ case CD_ROOT_PATH:
+ case CD_NIS_DOMAIN:
+ case CD_NETBIOS_SCOPE:
+ case CD_MESSAGE:
+ case CD_NISPLUS_DMAIN:
+ case CD_OPT_BOOTFILE_NAME:
+ case CD_USER_CLASS_ID:
+ /* Ascii strings */
+ display_ascii("%s = %s", option_types[save], &start);
+ break;
+ case CD_TIMEOFFSET:
+ case CD_IPTTL:
+ case CD_PATH_MTU_TIMEOUT:
+ case CD_ARP_TIMEOUT:
+ case CD_TCP_TTL:
+ case CD_TCP_KALIVE_INTVL:
+ case CD_T1_TIME:
+ case CD_T2_TIME:
+ case CD_LEASE_TIME:
+ /* Number: seconds */
+ display_number("%s = %d seconds", option_types[save],
+ &start);
+ break;
+ case CD_IP_FORWARDING_ON:
+ case CD_NON_LCL_ROUTE_ON:
+ case CD_ALL_SUBNETS_LCL_ON:
+ case CD_MASK_DISCVRY_ON:
+ case CD_MASK_SUPPLIER_ON:
+ case CD_ROUTER_DISCVRY_ON:
+ case CD_TRAILER_ENCAPS_ON:
+ case CD_ETHERNET_ENCAPS_ON:
+ case CD_TCP_KALIVE_GRBG_ON:
+ /* Number: hex flag */
+ display_number("%s flag = 0x%x", option_types[save],
+ &start);
+ break;
+ case CD_MAXIPSIZE:
+ case CD_MTU:
+ case CD_MAX_DHCP_SIZE:
+ /* Number: bytes */
+ display_number("%s = %d bytes", option_types[save],
+ &start);
+ break;
+ case CD_CLASS_ID:
+ case CD_CLIENT_ID:
+ case CD_NW_IP_DOMAIN:
+ case CD_NW_IP_OPTIONS:
+ /* Hex ascii strings */
+ display_ascii_hex(option_types[save], &start);
+ break;
+ case CD_BOOT_SIZE:
+ display_number("%s = %d 512 byte blocks",
+ "Boot file size", &start);
+ break;
+ case CD_POLICY_FILTER:
+ if ((*start % 8) != 0) {
+ (void) sprintf(get_line(0, 0),
+ "Error: Bad Policy Filter option");
+ } else {
+ items = *start++ / 8;
+ for (i = 0; i < items; i++) {
+ display_ip(1,
+ "%s = %s",
+ "Policy Destination",
+ &start);
+ display_ip(1, "%s = %s", "Mask",
+ &start);
+ }
+ }
+ break;
+ case CD_PATH_MTU_TABLE_SZ:
+ if (*start % 2 != 0) {
+ (void) sprintf(get_line(0, 0),
+ "Error: Bad Path MTU Table");
+ } else {
+ (void) sprintf(get_line(0, 0),
+ "\tPath MTU Plateau Table:");
+ (void) sprintf(get_line(0, 0),
+ "\t=======================");
+ items = *start / sizeof (ushort_t);
+ ++start;
+ for (i = 0; i < items; i++) {
+ if (IS_P2ALIGNED(start,
+ sizeof (ushort_t))) {
+ /* LINTED: improper alignment */
+ s_buf = *(ushort_t *)start;
+ } else {
+ memcpy((char *)&s_buf,
+ start, sizeof (short));
+ }
+ (void) sprintf(get_line(0, 0),
+ "\t\tEntry %d:\t\t%d", i,
+ ntohs(s_buf));
+ start += sizeof (ushort_t);
+ }
+ }
+ break;
+ case CD_STATIC_ROUTE:
+ if ((*start % 8) != 0) {
+ (void) sprintf(get_line(0, 0),
+ "Error: Bad Static Route option: %d",
+ *start);
+ } else {
+ items = *start++ / 8;
+ for (i = 0; i < items; i++) {
+ memcpy((char *)&tmp, start,
+ sizeof (struct in_addr));
+ (void) strcpy(scratch, inet_ntoa(tmp));
+ start += sizeof (ulong_t);
+ memcpy((char *)&tmp, start,
+ sizeof (struct in_addr));
+ (void) sprintf(get_line(0, 0),
+ "Static route from %s to %s",
+ scratch, inet_ntoa(tmp));
+ start += sizeof (ulong_t);
+ }
+ }
+ break;
+ case CD_VENDOR_SPEC:
+ i = *start++;
+ (void) sprintf(get_line(0, 0),
+ "Vendor-specific Options (%d total octets):", i);
+ /*
+ * We don't know what these things are, so just
+ * display the option number, length, and value
+ * (hex).
+ */
+ vend = (uchar_t *)((uchar_t *)start + i);
+ while (start < vend && *start != CD_END) {
+ if (*start == CD_PAD) {
+ start++;
+ continue;
+ }
+ (void) sprintf(scratch,
+ "\t(%.2d) %.2d octets", *start,
+ *(uchar_t *)((uchar_t *)start + 1));
+ start++;
+ display_ascii_hex(scratch, &start);
+ }
+ start = vend; /* in case CD_END found */
+ break;
+ case CD_NETBIOS_NODE_TYPE:
+ if (*start != 1) {
+ (void) sprintf(get_line(0, 0),
+ "Error: Bad '%s' parameter",
+ option_types[CD_NETBIOS_NODE_TYPE]);
+ } else {
+ char *type;
+ start++;
+ switch (*start) {
+ case 0x1:
+ type = "Broadcast Node";
+ break;
+ case 0x2:
+ type = "Point To Point Node";
+ break;
+ case 0x4:
+ type = "Mixed Mode Node";
+ break;
+ case 0x8:
+ type = "Hybrid Node";
+ break;
+ default:
+ type = "??? Node";
+ break;
+ };
+ (void) sprintf(get_line(0, 0),
+ "%s = %s (%d)",
+ option_types[CD_NETBIOS_NODE_TYPE],
+ type, *start);
+ start++;
+ }
+ break;
+ case CD_OPTION_OVERLOAD:
+ if (*start != 1) {
+ (void) sprintf(get_line(0, 0),
+ "Bad Option Overload value.");
+ } else {
+ start++;
+ nooverload = *start++;
+ }
+ break;
+ case CD_DHCP_TYPE:
+ if (*start < 1 || *start > 7) {
+ (void) sprintf(get_line(0, 0),
+ "Bad DHCP Message Type.");
+ } else {
+ start++;
+ (void) sprintf(get_line(0, 0),
+ "Message type = %s",
+ show_msgtype(*start));
+ start++;
+ }
+ break;
+ case CD_REQUEST_LIST:
+ opt_len = *start++;
+ (void) sprintf(get_line(0, 0),
+ "Requested Options:");
+ for (i = 0; i < opt_len; i++) {
+ entry = NULL;
+ if (*start < OPTIONS_ARRAY_SIZE) {
+ prmpt = option_types[*start];
+ } else {
+ entry = inittab_getbycode(
+ ITAB_CAT_STANDARD|ITAB_CAT_SITE,
+ ITAB_CONS_SNOOP, *start);
+ if (entry == NULL) {
+ if (*start >= DHCP_SITE_OPT &&
+ *start <= DHCP_END_SITE) {
+ prmpt = "Site Option";
+ } else {
+ prmpt = "Unrecognized "
+ "Option";
+ }
+ } else {
+ prmpt = entry->ds_name;
+ }
+ }
+ (void) sprintf(get_line(0, 0),
+ "\t%2d (%s)", *start, prmpt);
+ start++;
+ free(entry);
+ }
+ break;
+ default:
+ opt_len = *start++;
+ entry = inittab_getbycode(
+ ITAB_CAT_STANDARD|ITAB_CAT_SITE,
+ ITAB_CONS_SNOOP, save);
+ if (entry == NULL) {
+ if (save >= DHCP_SITE_OPT &&
+ save <= DHCP_END_SITE)
+ prmpt = "Site";
+ else
+ prmpt = "Unrecognized";
+ decoded_opt = NULL;
+ } else {
+ if (save < OPTIONS_ARRAY_SIZE) {
+ prmpt = option_types[save];
+ } else {
+ prmpt = entry->ds_name;
+ }
+ decoded_opt = inittab_decode(entry, start,
+ opt_len, B_TRUE);
+ }
+ if (decoded_opt == NULL) {
+ (void) sprintf(get_line(0, 0),
+ "%s Option = %d, length = %d octets",
+ prmpt, save, opt_len);
+ start--;
+ display_ascii_hex("\tValue =", &start);
+ } else {
+ (void) sprintf(get_line(0, 0), "%s = %s", prmpt,
+ decoded_opt);
+ start += opt_len;
+ free(decoded_opt);
+ }
+ free(entry);
+ break;
+ };
+ }
+ return (nooverload);
+}
+static char *
+show_htype(int t)
+{
+ switch (t) {
+ case 1:
+ return ("Ethernet (10Mb)");
+ case 2:
+ return ("Experimental Ethernet (3MB)");
+ case 3:
+ return ("Amateur Radio AX.25");
+ case 4:
+ return ("Proteon ProNET Token Ring");
+ case 5:
+ return ("Chaos");
+ case 6:
+ return ("IEEE 802");
+ case 7:
+ return ("ARCNET");
+ case 8:
+ return ("Hyperchannel");
+ case 9:
+ return ("Lanstar");
+ case 10:
+ return ("Autonet");
+ case 11:
+ return ("LocalTalk");
+ case 12:
+ return ("LocalNet");
+ case 13:
+ return ("Ultra Link");
+ case 14:
+ return ("SMDS");
+ case 15:
+ return ("Frame Relay");
+ case 16:
+ return ("ATM");
+ case ARPHRD_IB:
+ return ("IPIB");
+ };
+ return ("UNKNOWN");
+}
+static const char *
+show_msgtype(unsigned char type)
+{
+ /*
+ * note: the ordering here allows direct indexing of the table
+ * based on the RFC2131 packet type value passed in.
+ */
+
+ static const char *types[] = {
+ "BOOTP",
+ "DHCPDISCOVER", "DHCPOFFER", "DHCPREQUEST", "DHCPDECLINE",
+ "DHCPACK", "DHCPNAK", "DHCPRELEASE", "DHCPINFORM"
+ };
+
+ if (type > (sizeof (types) / sizeof (*types)) || types[type] == NULL)
+ return ("UNKNOWN");
+
+ return (types[type]);
+}
+static void
+display_ip(int items, char *fmt, char *msg, unsigned char **opt)
+{
+ struct in_addr tmp;
+ int i;
+
+ for (i = 0; i < items; i++) {
+ memcpy((char *)&tmp, *opt, sizeof (struct in_addr));
+ (void) sprintf(get_line(0, 0), fmt, msg, inet_ntoa(tmp));
+ *opt += 4;
+ }
+}
+static void
+display_ascii(char *fmt, char *msg, unsigned char **opt)
+{
+ static unsigned char buf[256];
+ unsigned char len = **opt;
+ unsigned char slen = len;
+
+ if (len >= sizeof (buf))
+ len = sizeof (buf) - 1;
+ (*opt)++;
+ memcpy(buf, *opt, len);
+ *(unsigned char *)(buf + len) = '\0';
+ (void) sprintf(get_line(0, 0), fmt, msg, buf);
+ (*opt) += slen;
+}
+static void
+display_number(char *fmt, char *msg, unsigned char **opt)
+{
+ int len = **opt;
+ unsigned long l_buf = 0;
+ unsigned short s_buf = 0;
+
+ if (len > 4) {
+ (*opt)++;
+ (void) sprintf(get_line(0, 0), fmt, msg, 0xdeadbeef);
+ return;
+ }
+ switch (len) {
+ case sizeof (uchar_t):
+ (*opt)++;
+ (void) sprintf(get_line(0, 0), fmt, msg, **opt);
+ break;
+ case sizeof (ushort_t):
+ (*opt)++;
+ if (IS_P2ALIGNED(*opt, sizeof (ushort_t)))
+ /* LINTED: improper alignment */
+ s_buf = *(unsigned short *)*opt;
+ else
+ memcpy((char *)&s_buf, *opt, len);
+ (void) sprintf(get_line(0, 0), fmt, msg, ntohs(s_buf));
+ break;
+ case sizeof (ulong_t):
+ (*opt)++;
+ if (IS_P2ALIGNED(*opt, sizeof (ulong_t)))
+ /* LINTED: improper alignment */
+ l_buf = *(unsigned long *)*opt;
+ else
+ memcpy((char *)&l_buf, *opt, len);
+ (void) sprintf(get_line(0, 0), fmt, msg, ntohl(l_buf));
+ break;
+ }
+ (*opt) += len;
+}
+static void
+display_ascii_hex(char *msg, unsigned char **opt)
+{
+ int printable;
+ char buffer[512];
+ char *line, *tmp, *ap, *fmt;
+ int i, j, len = **opt;
+
+ line = get_line(0, 0);
+
+ (*opt)++;
+
+ if (len >= 255) {
+ (void) sprintf(line, "\t%s <TOO LONG>", msg);
+ return;
+ }
+
+ for (printable = 1, tmp = (char *)(*opt), ap = buffer;
+ tmp < (char *)&((*opt)[len]); tmp++) {
+ if (isprint(*tmp))
+ *ap++ = *tmp;
+ else {
+ *ap++ = '.';
+ printable = 0;
+ }
+ }
+ *ap = '\0';
+
+ if (!printable) {
+ for (tmp = (char *)(*opt), ap = buffer;
+ (tmp < (char *)&((*opt)[len])) && (ap < &buffer[512]);
+ tmp++) {
+ ap += sprintf(ap, "0x%02X ", *(uchar_t *)(tmp));
+ }
+ *(--ap) = '\0';
+ i = ap - buffer;
+ fmt = "%s\t%s (unprintable)";
+ } else {
+ i = strlen(buffer);
+ fmt = "%s\t\"%s\"";
+ }
+ (*opt) += len;
+ j = strlen(msg) + (MAXLINE / 2) - 30;
+ if (i > j) {
+ buffer[j - 1] = '.';
+ buffer[j - 2] = '.';
+ buffer[j - 3] = '.';
+ buffer[j] = '\0';
+ }
+ (void) sprintf(line, fmt, msg, buffer);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_display.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_display.c
new file mode 100644
index 0000000000..3471e90c90
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_display.c
@@ -0,0 +1,775 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1991-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/types.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/bufmod.h>
+#include <setjmp.h>
+#include <varargs.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/if_ether.h>
+#include <rpc/types.h>
+#include <rpc/xdr.h>
+#include <sys/dlpi.h>
+#include <inttypes.h>
+
+#include "snoop.h"
+
+char *dlc_header;
+char *src_name, *dst_name;
+int pi_frame;
+int pi_time_hour;
+int pi_time_min;
+int pi_time_sec;
+int pi_time_usec;
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+static void hexdump(char *, int);
+
+/*
+ * This routine invokes the packet interpreters
+ * on a packet. There's some messing around
+ * setting up a few packet-externals before
+ * starting with the ethernet interpreter.
+ * Yes, we assume here that all packets will
+ * be ethernet packets.
+ */
+void
+process_pkt(struct sb_hdr *hdrp, char *pktp, int num, int flags)
+{
+ int drops, pktlen;
+ struct timeval *tvp;
+ struct tm *tm;
+ extern int x_offset;
+ extern int x_length;
+ int offset, length;
+ static struct timeval ptv;
+
+ if (hdrp == NULL)
+ return;
+
+ tvp = &hdrp->sbh_timestamp;
+ if (ptv.tv_sec == 0)
+ ptv = *tvp;
+ drops = hdrp->sbh_drops;
+ pktlen = hdrp->sbh_msglen;
+ if (pktlen <= 0)
+ return;
+
+ /* set up externals */
+ dlc_header = pktp;
+ pi_frame = num;
+ tm = localtime(&tvp->tv_sec);
+ pi_time_hour = tm->tm_hour;
+ pi_time_min = tm->tm_min;
+ pi_time_sec = tm->tm_sec;
+ pi_time_usec = tvp->tv_usec;
+
+ src_name = "?";
+ dst_name = "*";
+
+ click(hdrp->sbh_origlen);
+
+ (*interface->interpreter)(flags, dlc_header, hdrp->sbh_msglen,
+ hdrp->sbh_origlen);
+
+ show_pktinfo(flags, num, src_name, dst_name, &ptv, tvp, drops,
+ hdrp->sbh_origlen);
+
+ if (x_offset >= 0) {
+ offset = MIN(x_offset, hdrp->sbh_msglen);
+ offset -= (offset % 2); /* round down */
+ length = MIN(hdrp->sbh_msglen - offset, x_length);
+
+ hexdump(dlc_header + offset, length);
+ }
+
+ ptv = *tvp;
+}
+
+
+/*
+ * *************************************************************
+ * The following routines constitute a library
+ * used by the packet interpreters to facilitate
+ * the display of packet data. This library
+ * of routines helps provide a consistent
+ * "look and feel".
+ */
+
+
+/*
+ * Display the value of a flag bit in
+ * a byte together with some text that
+ * corresponds to its value - whether
+ * true or false.
+ */
+char *
+getflag(int val, int mask, char *s_true, char *s_false)
+{
+ static char buff[80];
+ char *p;
+ int set;
+
+ (void) strcpy(buff, ".... .... = ");
+ if (s_false == NULL)
+ s_false = s_true;
+
+ for (p = &buff[8]; p >= buff; p--) {
+ if (*p == ' ')
+ p--;
+ if (mask & 0x1) {
+ set = val & mask & 0x1;
+ *p = set ? '1':'0';
+ (void) strcat(buff, set ? s_true: s_false);
+ break;
+ }
+ mask >>= 1;
+ val >>= 1;
+ }
+ return (buff);
+}
+
+XDR xdrm;
+jmp_buf xdr_err;
+int xdr_totlen;
+char *prot_prefix;
+char *prot_nest_prefix = "";
+char *prot_title;
+
+void
+show_header(char *pref, char *str, int len)
+{
+ prot_prefix = pref;
+ prot_title = str;
+ (void) sprintf(get_detail_line(0, len), "%s%s----- %s -----",
+ prot_nest_prefix, pref, str);
+}
+
+void
+xdr_init(char *addr, int len)
+{
+ xdr_totlen = len;
+ xdrmem_create(&xdrm, addr, len, XDR_DECODE);
+}
+
+char *
+get_line(int begin, int end)
+{
+ char *line;
+
+ line = get_detail_line(begin, end);
+ (void) strcpy(line, prot_nest_prefix);
+ (void) strcat(line, prot_prefix);
+ return (line + strlen(line));
+}
+
+int
+get_line_remain(void)
+{
+ return (MAXLINE - strlen(prot_nest_prefix) - strlen(prot_prefix));
+}
+
+void
+show_line(char *str)
+{
+ (void) strcpy(get_line(0, 0), str);
+}
+
+char
+getxdr_char()
+{
+ char s;
+
+ if (xdr_char(&xdrm, &s))
+ return (s);
+ longjmp(xdr_err, 1);
+ /* NOTREACHED */
+}
+
+char
+showxdr_char(char *fmt)
+{
+ int pos; char val;
+
+ pos = getxdr_pos();
+ val = getxdr_char();
+ (void) sprintf(get_line(pos, getxdr_pos()), fmt, val);
+ return (val);
+}
+
+uchar_t
+getxdr_u_char()
+{
+ uchar_t s;
+
+ if (xdr_u_char(&xdrm, &s))
+ return (s);
+ longjmp(xdr_err, 1);
+ /* NOTREACHED */
+}
+
+uchar_t
+showxdr_u_char(char *fmt)
+{
+ int pos;
+ uchar_t val;
+
+ pos = getxdr_pos();
+ val = getxdr_u_char();
+ (void) sprintf(get_line(pos, getxdr_pos()), fmt, val);
+ return (val);
+}
+
+short
+getxdr_short()
+{
+ short s;
+
+ if (xdr_short(&xdrm, &s))
+ return (s);
+ longjmp(xdr_err, 1);
+ /* NOTREACHED */
+}
+
+short
+showxdr_short(char *fmt)
+{
+ int pos; short val;
+
+ pos = getxdr_pos();
+ val = getxdr_short();
+ (void) sprintf(get_line(pos, getxdr_pos()), fmt, val);
+ return (val);
+}
+
+ushort_t
+getxdr_u_short()
+{
+ ushort_t s;
+
+ if (xdr_u_short(&xdrm, &s))
+ return (s);
+ longjmp(xdr_err, 1);
+ /* NOTREACHED */
+}
+
+ushort_t
+showxdr_u_short(char *fmt)
+{
+ int pos;
+ ushort_t val;
+
+ pos = getxdr_pos();
+ val = getxdr_u_short();
+ (void) sprintf(get_line(pos, getxdr_pos()), fmt, val);
+ return (val);
+}
+
+long
+getxdr_long()
+{
+ long l;
+
+ if (xdr_long(&xdrm, &l))
+ return (l);
+ longjmp(xdr_err, 1);
+ /* NOTREACHED */
+}
+
+long
+showxdr_long(char *fmt)
+{
+ int pos; long val;
+
+ pos = getxdr_pos();
+ val = getxdr_long();
+ (void) sprintf(get_line(pos, getxdr_pos()), fmt, val);
+ return (val);
+}
+
+ulong_t
+getxdr_u_long()
+{
+ ulong_t l;
+
+ if (xdr_u_long(&xdrm, &l))
+ return (l);
+ longjmp(xdr_err, 1);
+ /* NOTREACHED */
+}
+
+ulong_t
+showxdr_u_long(char *fmt)
+{
+ int pos;
+ ulong_t val;
+
+ pos = getxdr_pos();
+ val = getxdr_u_long();
+ (void) sprintf(get_line(pos, getxdr_pos()), fmt, val);
+ return (val);
+}
+
+longlong_t
+getxdr_longlong()
+{
+ longlong_t l;
+
+ if (xdr_longlong_t(&xdrm, &l))
+ return (l);
+ longjmp(xdr_err, 1);
+ /* NOTREACHED */
+}
+
+longlong_t
+showxdr_longlong(char *fmt)
+{
+ int pos; longlong_t val;
+
+ pos = getxdr_pos();
+ val = getxdr_longlong();
+ (void) sprintf(get_line(pos, getxdr_pos()), fmt, val);
+ return (val);
+}
+
+u_longlong_t
+getxdr_u_longlong()
+{
+ u_longlong_t l;
+
+ if (xdr_u_longlong_t(&xdrm, &l))
+ return (l);
+ longjmp(xdr_err, 1);
+ /* NOTREACHED */
+}
+
+u_longlong_t
+showxdr_u_longlong(char *fmt)
+{
+ int pos; u_longlong_t val;
+
+ pos = getxdr_pos();
+ val = getxdr_u_longlong();
+ (void) sprintf(get_line(pos, getxdr_pos()), fmt, val);
+ return (val);
+}
+
+bool_t
+getxdr_bool()
+{
+ bool_t b;
+
+ if (xdr_bool(&xdrm, &b))
+ return (b);
+ longjmp(xdr_err, 1);
+ /* NOTREACHED */
+}
+
+bool_t
+showxdr_bool(char *fmt)
+{
+ int pos; bool_t val;
+
+ pos = getxdr_pos();
+ val = getxdr_bool();
+ (void) sprintf(get_line(pos, getxdr_pos()), fmt,
+ val ? "True" : "False");
+ return (val);
+}
+
+char *
+getxdr_opaque(char *p, int len)
+{
+ if (xdr_opaque(&xdrm, p, len))
+ return (p);
+ longjmp(xdr_err, 1);
+ /* NOTREACHED */
+}
+
+char *
+getxdr_string(char *p, /* len+1 bytes or longer */
+ int len)
+{
+ if (xdr_string(&xdrm, &p, len))
+ return (p);
+ longjmp(xdr_err, 1);
+ /* NOTREACHED */
+}
+
+char *
+showxdr_string(int len, /* XDR length */
+ char *fmt)
+{
+ static int buff_len = 0;
+ static char *buff = NULL;
+ int pos;
+
+ /*
+ * XDR strings don't necessarily have a trailing null over the
+ * wire. However, the XDR code will put one in for us. Make sure
+ * we have allocated room for it.
+ */
+ len++;
+
+ if ((len > buff_len) || (buff_len == 0)) {
+ if (buff)
+ free(buff);
+ if ((buff = (char *)malloc(len)) == NULL)
+ pr_err("showxdr_string: no mem");
+ buff_len = len;
+ }
+ pos = getxdr_pos();
+ getxdr_string(buff, len);
+ (void) strcpy(buff+60, "...");
+ (void) sprintf(get_line(pos, getxdr_pos()), fmt, buff);
+ return (buff);
+}
+
+char *
+getxdr_bytes(uint_t *lenp)
+{
+ static char buff[1024];
+ char *p = buff;
+
+ if (xdr_bytes(&xdrm, &p, lenp, 1024))
+ return (buff);
+ longjmp(xdr_err, 1);
+ /* NOTREACHED */
+}
+
+char *
+getxdr_context(char *p, int len)
+{
+ ushort_t size;
+
+ size = getxdr_u_short();
+ if (((int)size > 0) && ((int)size < len) && getxdr_opaque(p, size))
+ return (p);
+ longjmp(xdr_err, 1);
+ /* NOTREACHED */
+}
+
+char *
+showxdr_context(char *fmt)
+{
+ ushort_t size;
+ static char buff[1024];
+ int pos;
+
+ pos = getxdr_pos();
+ size = getxdr_u_short();
+ if (((int)size > 0) && ((int)size < 1024) &&
+ getxdr_opaque(buff, size)) {
+ (void) sprintf(get_line(pos, getxdr_pos()), fmt, buff);
+ return (buff);
+ }
+ longjmp(xdr_err, 1);
+ /* NOTREACHED */
+}
+
+enum_t
+getxdr_enum()
+{
+ enum_t e;
+
+ if (xdr_enum(&xdrm, &e))
+ return (e);
+ longjmp(xdr_err, 1);
+ /* NOTREACHED */
+}
+
+void
+xdr_skip(int delta)
+{
+ uint_t pos;
+ if (delta % 4 != 0 || delta < 0)
+ longjmp(xdr_err, 1);
+ /* Check for overflow */
+ pos = xdr_getpos(&xdrm);
+ if ((pos + delta) < pos)
+ longjmp(xdr_err, 1);
+ /* xdr_setpos() checks for buffer overrun */
+ if (xdr_setpos(&xdrm, pos + delta) == FALSE)
+ longjmp(xdr_err, 1);
+}
+
+int
+getxdr_pos()
+{
+ return (xdr_getpos(&xdrm));
+}
+
+void
+setxdr_pos(int pos)
+{
+ xdr_setpos(&xdrm, pos);
+}
+
+void
+show_space()
+{
+ (void) get_line(0, 0);
+}
+
+void
+show_trailer()
+{
+ show_space();
+}
+
+char *
+getxdr_date()
+{
+ time_t sec;
+ int usec;
+ static char buff[64];
+ char *p;
+ struct tm my_time; /* private buffer to avoid collision */
+ /* between gmtime and strftime */
+ struct tm *tmp;
+
+ sec = getxdr_long();
+ usec = getxdr_long();
+ if (sec == -1)
+ return ("-1 ");
+
+ if (sec < 3600 * 24 * 365) { /* assume not a date */
+ (void) sprintf(buff, "%d.%06d", sec, usec);
+ } else {
+ tmp = gmtime(&sec);
+ (void) memcpy(&my_time, tmp, sizeof (struct tm));
+ strftime(buff, sizeof (buff), "%d-%h-%y %T.", &my_time);
+ p = buff + strlen(buff);
+ (void) sprintf(p, "%06d GMT", usec);
+ }
+ return (buff);
+}
+
+char *
+showxdr_date(char *fmt)
+{
+ int pos;
+ char *p;
+
+ pos = getxdr_pos();
+ p = getxdr_date();
+ (void) sprintf(get_line(pos, getxdr_pos()), fmt, p);
+ return (p);
+}
+
+char *
+getxdr_date_ns(void)
+{
+ time_t sec, nsec;
+
+ sec = getxdr_long();
+ nsec = getxdr_long();
+ if (sec == -1)
+ return ("-1 ");
+ else
+ return (format_time(sec, nsec));
+}
+
+/*
+ * Format the given time.
+ */
+char *
+format_time(int64_t sec, uint32_t nsec)
+{
+ static char buff[64];
+ char *p;
+ struct tm my_time; /* private buffer to avoid collision */
+ /* between gmtime and strftime */
+ struct tm *tmp;
+
+ if (sec < 3600 * 24 * 365) {
+ /* assume not a date; includes negative times */
+ (void) sprintf(buff, "%lld.%06d", sec, nsec);
+ } else if (sec > INT32_MAX) {
+ /*
+ * XXX No routines are available yet for formatting 64-bit
+ * times.
+ */
+ (void) sprintf(buff, "%lld.%06d", sec, nsec);
+ } else {
+ time_t sec32 = (time_t)sec;
+
+ tmp = gmtime(&sec32);
+ memcpy(&my_time, tmp, sizeof (struct tm));
+ strftime(buff, sizeof (buff), "%d-%h-%y %T.", &my_time);
+ p = buff + strlen(buff);
+ (void) sprintf(p, "%09d GMT", nsec);
+ }
+ return (buff);
+}
+
+char *
+showxdr_date_ns(char *fmt)
+{
+ int pos;
+ char *p;
+
+ pos = getxdr_pos();
+ p = getxdr_date_ns();
+ (void) sprintf(get_line(pos, getxdr_pos()), fmt, p);
+ return (p);
+}
+
+char *
+getxdr_time()
+{
+ time_t sec;
+ static char buff[64];
+ struct tm my_time; /* private buffer to avoid collision */
+ /* between gmtime and strftime */
+ struct tm *tmp;
+
+ sec = getxdr_long();
+ if (sec == -1)
+ return ("-1 ");
+
+ if (sec < 3600 * 24 * 365) { /* assume not a date */
+ (void) sprintf(buff, "%d", sec);
+ } else {
+ tmp = gmtime(&sec);
+ memcpy(&my_time, tmp, sizeof (struct tm));
+ strftime(buff, sizeof (buff), "%d-%h-%y %T", &my_time);
+ }
+ return (buff);
+}
+
+char *
+showxdr_time(char *fmt)
+{
+ int pos;
+ char *p;
+
+ pos = getxdr_pos();
+ p = getxdr_time();
+ (void) sprintf(get_line(pos, getxdr_pos()), fmt, p);
+ return (p);
+}
+
+char *
+getxdr_hex(int len)
+{
+ int i, j;
+ static char hbuff[1024];
+ char rbuff[1024];
+ static char *hexstr = "0123456789ABCDEF";
+ char toobig = 0;
+
+ if (len == 0) {
+ hbuff[0] = '\0';
+ return (hbuff);
+ }
+ if (len > 1024)
+ len = 1024;
+ if (len < 0 || xdr_opaque(&xdrm, rbuff, len) == FALSE) {
+ longjmp(xdr_err, 1);
+ }
+
+ if (len * 2 > sizeof (hbuff)) {
+ toobig++;
+ len = sizeof (hbuff) / 2;
+ }
+
+ j = 0;
+ for (i = 0; i < len; i++) {
+ hbuff[j++] = hexstr[rbuff[i] >> 4 & 0x0f];
+ hbuff[j++] = hexstr[rbuff[i] & 0x0f];
+ }
+
+ if (toobig) {
+ hbuff[len * 2 - strlen("<Too Long>")] = '\0';
+ strcat(hbuff, "<Too Long>");
+ } else
+ hbuff[j] = '\0';
+
+ return (hbuff);
+}
+
+char *
+showxdr_hex(int len, char *fmt)
+{
+ int pos;
+ char *p;
+
+ pos = getxdr_pos();
+ p = getxdr_hex(len);
+ (void) sprintf(get_line(pos, getxdr_pos()), fmt, p);
+ return (p);
+}
+
+static void
+hexdump(char *data, int datalen)
+{
+ char *p;
+ ushort_t *p16 = (ushort_t *)data;
+ char *p8 = data;
+ int i, left, len;
+ int chunk = 16; /* 16 bytes per line */
+
+ printf("\n");
+
+ for (p = data; p < data + datalen; p += chunk) {
+ printf("\t%4d: ", p - data);
+ left = (data + datalen) - p;
+ len = MIN(chunk, left);
+ for (i = 0; i < (len / 2); i++)
+ printf("%04x ", ntohs(*p16++) & 0xffff);
+ if (len % 2) {
+ printf("%02x ", *((unsigned char *)p16));
+ }
+ for (i = 0; i < (chunk - left) / 2; i++)
+ printf(" ");
+
+ printf(" ");
+ for (i = 0; i < len; i++, p8++)
+ printf("%c", isprint(*p8) ? *p8 : '.');
+ printf("\n");
+ }
+
+ printf("\n");
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_dns.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_dns.c
new file mode 100644
index 0000000000..6decfe78cf
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_dns.c
@@ -0,0 +1,746 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/tiuser.h>
+#include <arpa/nameser.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include "snoop.h"
+
+/* The string used to indent detail lines */
+#define DNS_INDENT " "
+/*
+ * From RFC1035, the maximum size of a character-string is limited by the
+ * one octet length field. We add one character to that to make sure the
+ * result is terminated.
+ */
+#define MAX_CHAR_STRING_SIZE UCHAR_MAX + 1
+
+/* private functions */
+static char *dns_opcode_string(uint_t opcode);
+static char *dns_rcode_string(uint_t rcode);
+static char *dns_type_string(uint_t type, int detail);
+static char *dns_class_string(uint_t cls, int detail);
+static size_t skip_question(const uchar_t *header, const uchar_t *data,
+ const uchar_t *data_end);
+static size_t print_question(char *line, const uchar_t *header,
+ const uchar_t *data, const uchar_t *data_end, int detail);
+static size_t print_answer(char *line, const uchar_t *header,
+ const uchar_t *data, const uchar_t *data_end, int detail);
+static char *binary_string(char data);
+static void print_ip(int af, char *line, const uchar_t *data, uint16_t len);
+static const uchar_t *get_char_string(const uchar_t *data, char *charbuf,
+ uint16_t datalen);
+static size_t print_char_string(char *line, const uchar_t *data, uint16_t len);
+static const uchar_t *get_domain_name(const uchar_t *header,
+ const uchar_t *data, const uchar_t *data_end, char *namebuf, char *namend);
+static size_t print_domain_name(char *line, const uchar_t *header,
+ const uchar_t *data, const uchar_t *data_end);
+
+void
+interpret_dns(int flags, int proto, const uchar_t *data, int len)
+{
+ typedef HEADER dns_header;
+ dns_header header;
+ char *line;
+ ushort_t id, qdcount, ancount, nscount, arcount;
+ ushort_t count;
+ const uchar_t *questions;
+ const uchar_t *answers;
+ const uchar_t *nservers;
+ const uchar_t *additions;
+ const uchar_t *data_end;
+
+ if (proto == IPPROTO_TCP) {
+ /* not supported now */
+ return;
+ }
+
+ /* We need at least the header in order to parse a packet. */
+ if (sizeof (dns_header) > len) {
+ return;
+ }
+ data_end = data + len;
+ /*
+ * Copy the header into a local structure for aligned access to
+ * each field.
+ */
+ (void) memcpy(&header, data, sizeof (header));
+ id = ntohs(header.id);
+ qdcount = ntohs(header.qdcount);
+ ancount = ntohs(header.ancount);
+ nscount = ntohs(header.nscount);
+ arcount = ntohs(header.arcount);
+
+ if (flags & F_SUM) {
+ line = get_sum_line();
+ line += sprintf(line, "DNS %c ", header.qr ? 'R' : 'C');
+
+ if (header.qr) {
+ /* answer */
+ if (header.rcode == 0) {
+ /* reply is OK */
+ questions = data + sizeof (dns_header);
+ while (qdcount--) {
+ if (questions >= data_end) {
+ return;
+ }
+ questions += skip_question(data,
+ questions, data_end);
+ }
+ /* the answers are following the questions */
+ answers = questions;
+ if (ancount > 0) {
+ (void) print_answer(line,
+ data, answers, data_end, FALSE);
+ }
+ } else {
+ (void) sprintf(line, " Error: %d(%s)",
+ header.rcode,
+ dns_rcode_string(header.rcode));
+ }
+ } else {
+ /* question */
+ questions = data + sizeof (dns_header);
+ if (questions >= data_end) {
+ return;
+ }
+ (void) print_question(line, data, questions, data_end,
+ FALSE);
+ }
+ }
+ if (flags & F_DTAIL) {
+ show_header("DNS: ", "DNS Header", sizeof (dns_header));
+ show_space();
+ if (header.qr) {
+ /* answer */
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Response ID = %d", id);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "%s%s%s",
+ header.aa ? "AA (Authoritative Answer) " : "",
+ header.tc ? "TC (TrunCation) " : "",
+ header.ra ? "RA (Recursion Available) ": "");
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Response Code: %d (%s)",
+ header.rcode, dns_rcode_string(header.rcode));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Reply to %d question(s)", qdcount);
+ questions = data + sizeof (dns_header);
+ count = 0;
+ while (qdcount--) {
+ if (questions >= data_end) {
+ return;
+ }
+ count++;
+ questions += print_question(get_line(0, 0),
+ data, questions, data_end, TRUE);
+ show_space();
+ }
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "%d answer(s)", ancount);
+ answers = questions;
+ count = 0;
+ while (ancount--) {
+ if (answers >= data_end) {
+ return;
+ }
+ count++;
+ answers += print_answer(get_line(0, 0),
+ data, answers, data_end, TRUE);
+ show_space();
+ }
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "%d name server resource(s)", nscount);
+ nservers = answers;
+ count = 0;
+ while (nscount--) {
+ if (nservers >= data_end) {
+ return;
+ }
+ count++;
+ nservers += print_answer(get_line(0, 0), data,
+ nservers, data_end, TRUE);
+ show_space();
+ }
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "%d additional record(s)", arcount);
+ additions = nservers;
+ count = 0;
+ while (arcount-- && additions < data_end) {
+ count++;
+ additions += print_answer(get_line(0, 0), data,
+ additions, data_end, TRUE);
+ show_space();
+ }
+ } else {
+ /* question */
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Query ID = %d", id);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Opcode: %s", dns_opcode_string(header.opcode));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "%s%s",
+ header.tc ? "TC (TrunCation) " : "",
+ header.rd ? "RD (Recursion Desired) " : "");
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "%d question(s)", qdcount);
+ questions = data + sizeof (dns_header);
+ count = 0;
+ while (qdcount-- && questions < data_end) {
+ count++;
+ questions += print_question(get_line(0, 0),
+ data, questions, data_end, TRUE);
+ show_space();
+ }
+ }
+ }
+}
+
+
+static char *
+dns_opcode_string(uint_t opcode)
+{
+ static char buffer[64];
+ switch (opcode) {
+ case ns_o_query: return ("Query");
+ case ns_o_iquery: return ("Inverse Query");
+ case ns_o_status: return ("Status");
+ default:
+ (void) snprintf(buffer, sizeof (buffer), "Unknown (%u)",
+ opcode);
+ return (buffer);
+ }
+}
+
+static char *
+dns_rcode_string(uint_t rcode)
+{
+ static char buffer[64];
+ switch (rcode) {
+ case ns_r_noerror: return ("OK");
+ case ns_r_formerr: return ("Format Error");
+ case ns_r_servfail: return ("Server Fail");
+ case ns_r_nxdomain: return ("Name Error");
+ case ns_r_notimpl: return ("Unimplemented");
+ case ns_r_refused: return ("Refused");
+ default:
+ (void) snprintf(buffer, sizeof (buffer), "Unknown (%u)", rcode);
+ return (buffer);
+ }
+}
+
+static char *
+dns_type_string(uint_t type, int detail)
+{
+ static char buffer[64];
+ switch (type) {
+ case ns_t_a: return (detail ? "Address" : "Addr");
+ case ns_t_ns: return (detail ? "Authoritative Name Server" : "NS");
+ case ns_t_cname: return (detail ? "Canonical Name" : "CNAME");
+ case ns_t_soa: return (detail ? "Start Of a zone Authority" : "SOA");
+ case ns_t_mb: return (detail ? "Mailbox domain name" : "MB");
+ case ns_t_mg: return (detail ? "Mailbox Group member" : "MG");
+ case ns_t_mr: return (detail ? "Mail Rename domain name" : "MR");
+ case ns_t_null: return ("NULL");
+ case ns_t_wks: return (detail ? "Well Known Service" : "WKS");
+ case ns_t_ptr: return (detail ? "Domain Name Pointer" : "PTR");
+ case ns_t_hinfo: return (detail ? "Host Information": "HINFO");
+ case ns_t_minfo:
+ return (detail ? "Mailbox or maillist Info" : "MINFO");
+ case ns_t_mx: return (detail ? "Mail Exchange" : "MX");
+ case ns_t_txt: return (detail ? "Text strings" : "TXT");
+ case ns_t_aaaa: return (detail ? "IPv6 Address" : "AAAA");
+ case ns_t_axfr: return (detail ? "Transfer of entire zone" : "AXFR");
+ case ns_t_mailb:
+ return (detail ? "Mailbox related records" : "MAILB");
+ case ns_t_maila: return (detail ? "Mail agent RRs" : "MAILA");
+ case ns_t_any: return (detail ? "All records" : "*");
+ default:
+ (void) snprintf(buffer, sizeof (buffer), "Unknown (%u)", type);
+ return (buffer);
+ }
+}
+
+static char *
+dns_class_string(uint_t cls, int detail)
+{
+ static char buffer[64];
+ switch (cls) {
+ case ns_c_in: return (detail ? "Internet" : "Internet");
+ case ns_c_chaos: return (detail ? "CHAOS" : "CH");
+ case ns_c_hs: return (detail ? "Hesiod" : "HS");
+ case ns_c_any: return (detail ? "* (Any class)" : "*");
+ default:
+ (void) snprintf(buffer, sizeof (buffer), "Unknown (%u)", cls);
+ return (buffer);
+ }
+}
+
+static size_t
+skip_question(const uchar_t *header, const uchar_t *data,
+ const uchar_t *data_end)
+{
+ const uchar_t *data_bak = data;
+ char dummy_buffer[NS_MAXDNAME];
+
+ data = get_domain_name(header, data, data_end, dummy_buffer,
+ dummy_buffer + sizeof (dummy_buffer));
+ /* Skip the 32 bits of class and type that follow the domain name */
+ data += sizeof (uint32_t);
+ return (data - data_bak);
+}
+
+static size_t
+print_question(char *line, const uchar_t *header, const uchar_t *data,
+ const uchar_t *data_end, int detail)
+{
+ const uchar_t *data_bak = data;
+ uint16_t type;
+ uint16_t cls;
+
+ if (detail) {
+ line += snprintf(line, get_line_remain(),
+ DNS_INDENT "Domain Name: ");
+ }
+ data += print_domain_name(line, header, data, data_end);
+
+ /*
+ * Make sure we don't run off the end of the packet by reading the
+ * type and class.
+ *
+ * The pointer subtraction on the left side of the following
+ * expression has a signed result of type ptrdiff_t, and the right
+ * side has an unsigned result of type size_t. We therefore need
+ * to cast the right side of the expression to be of the same
+ * signed type to keep the result of the pointer arithmetic to be
+ * automatically cast to an unsigned value. We do a similar cast
+ * in other similar expressions throughout this file.
+ */
+ if ((data_end - data) < (ptrdiff_t)(2 * sizeof (uint16_t)))
+ return (data_end - data_bak);
+
+ GETINT16(type, data);
+ GETINT16(cls, data);
+
+ if (detail) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ DNS_INDENT "Class: %u (%s)",
+ cls, dns_class_string(cls, detail));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ DNS_INDENT "Type: %u (%s)", type,
+ dns_type_string(type, detail));
+ } else {
+ (void) sprintf(line + strlen(line), " %s %s \?",
+ dns_class_string(cls, detail),
+ dns_type_string(type, detail));
+ }
+ return (data - data_bak);
+}
+
+static size_t
+print_answer(char *line, const uchar_t *header, const uchar_t *data,
+ const uchar_t *data_end, int detail)
+{
+ const uchar_t *data_bak = data;
+ const uchar_t *data_next;
+ uint16_t type;
+ uint16_t cls;
+ int32_t ttl;
+ uint16_t rdlen;
+ uint32_t serial, refresh, retry, expire, minimum;
+ uint8_t protocol;
+ int linepos;
+ uint16_t preference;
+
+ if (detail) {
+ line += snprintf(line, get_line_remain(),
+ DNS_INDENT "Domain Name: ");
+ }
+ data += print_domain_name(line, header, data, data_end);
+
+ /*
+ * Make sure we don't run off the end of the packet by reading the
+ * type, class, ttl, and length.
+ */
+ if ((data_end - data) <
+ (ptrdiff_t)(3 * sizeof (uint16_t) + sizeof (uint32_t))) {
+ return (data_end - data_bak);
+ }
+
+ GETINT16(type, data);
+ GETINT16(cls, data);
+
+ if (detail) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ DNS_INDENT "Class: %d (%s)", cls,
+ dns_class_string(cls, detail));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ DNS_INDENT "Type: %d (%s)", type,
+ dns_type_string(type, detail));
+ } else {
+ line += strlen(line);
+ line += sprintf(line, " %s %s ",
+ dns_class_string(cls, detail),
+ dns_type_string(type, detail));
+ }
+
+ GETINT32(ttl, data);
+ if (detail) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ DNS_INDENT "TTL (Time To Live): %d", ttl);
+ }
+
+ GETINT16(rdlen, data);
+ if (detail) {
+ line = get_line(0, 0);
+ line += snprintf(line, get_line_remain(), DNS_INDENT "%s: ",
+ dns_type_string(type, detail));
+ }
+
+ if (rdlen > data_end - data)
+ return (data_end - data_bak);
+
+ switch (type) {
+ case ns_t_a:
+ print_ip(AF_INET, line, data, rdlen);
+ break;
+ case ns_t_aaaa:
+ print_ip(AF_INET6, line, data, rdlen);
+ break;
+ case ns_t_hinfo:
+ line += sprintf(line, "CPU: ");
+ data_next = data + print_char_string(line, data, rdlen);
+ if (data_next >= data_end)
+ break;
+ line += strlen(line);
+ line += sprintf(line, "OS: ");
+ (void) print_char_string(line, data_next,
+ rdlen - (data_next - data));
+ break;
+ case ns_t_ns:
+ case ns_t_cname:
+ case ns_t_mb:
+ case ns_t_mg:
+ case ns_t_mr:
+ case ns_t_ptr:
+ (void) print_domain_name(line, header, data, data_end);
+ break;
+ case ns_t_mx:
+ data_next = data;
+ if (rdlen < sizeof (uint16_t))
+ break;
+ GETINT16(preference, data_next);
+ if (detail) {
+ (void) print_domain_name(line, header, data_next,
+ data_end);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ DNS_INDENT "Preference: %u", preference);
+ } else {
+ (void) print_domain_name(line, header, data_next,
+ data_end);
+ }
+ break;
+ case ns_t_soa:
+ if (!detail)
+ break;
+ line = get_line(0, 0);
+ line += snprintf(line, get_line_remain(),
+ DNS_INDENT "MNAME (Server name): ");
+ data_next = data + print_domain_name(line, header, data,
+ data_end);
+ if (data_next >= data_end)
+ break;
+ line = get_line(0, 0);
+ line += snprintf(line, get_line_remain(),
+ DNS_INDENT "RNAME (Resposible mailbox): ");
+ data_next = data_next +
+ print_domain_name(line, header, data_next, data_end);
+ if ((data_end - data_next) < (ptrdiff_t)(5 * sizeof (uint32_t)))
+ break;
+ GETINT32(serial, data_next);
+ GETINT32(refresh, data_next);
+ GETINT32(retry, data_next);
+ GETINT32(expire, data_next);
+ GETINT32(minimum, data_next);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ DNS_INDENT "Serial: %u", serial);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ DNS_INDENT "Refresh: %u Retry: %u "
+ "Expire: %u Minimum: %u",
+ refresh, retry, expire, minimum);
+ break;
+ case ns_t_wks:
+ print_ip(AF_INET, line, data, rdlen);
+ if (!detail)
+ break;
+ data_next = data + sizeof (in_addr_t);
+ if (data_next >= data_end)
+ break;
+ GETINT8(protocol, data_next);
+ line = get_line(0, 0);
+ line += snprintf(line, get_line_remain(),
+ DNS_INDENT "Protocol: %u ", protocol);
+ switch (protocol) {
+ case IPPROTO_UDP:
+ (void) snprintf(line, get_line_remain(), "(UDP)");
+ break;
+ case IPPROTO_TCP:
+ (void) snprintf(line, get_line_remain(), "(TCP)");
+ break;
+ }
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ DNS_INDENT "Service bitmap:");
+ (void) snprintf(line, get_line_remain(),
+ DNS_INDENT "0 8 16 24");
+ linepos = 4;
+ while (data_next < data + rdlen) {
+ if (linepos == 4) {
+ line = get_line(0, 0);
+ line += snprintf(line, get_line_remain(),
+ DNS_INDENT);
+ linepos = 0;
+ }
+ line += snprintf(line, get_line_remain(), "%s",
+ binary_string(*data_next));
+ linepos++;
+ data_next++;
+ }
+ break;
+ case ns_t_minfo:
+ if (!detail)
+ break;
+ line = get_line(0, 0);
+ line += snprintf(line, get_line_remain(),
+ DNS_INDENT "RMAILBX (Resposible mailbox): ");
+ data_next = data + print_domain_name(line, header, data,
+ data_end);
+ line = get_line(0, 0);
+ line += snprintf(line, get_line_remain(),
+ DNS_INDENT "EMAILBX (mailbox to receive err message): ");
+ data_next = data_next + print_domain_name(line, header,
+ data_next, data_end);
+ break;
+ }
+ data += rdlen;
+ return (data - data_bak);
+}
+
+static char *
+binary_string(char data)
+{
+ static char bstring[8 + 1];
+ char *ptr;
+ int i;
+ ptr = bstring;
+ for (i = 0; i < 8; i++) {
+ *ptr++ = (data & 0x80) ? '1' : '0';
+ data = data << 1;
+ }
+ *ptr = (char)0;
+ return (bstring);
+}
+
+static void
+print_ip(int af, char *line, const uchar_t *data, uint16_t len)
+{
+ in6_addr_t addr6;
+ in_addr_t addr4;
+ void *addr;
+
+ switch (af) {
+ case AF_INET:
+ if (len != sizeof (in_addr_t))
+ return;
+ addr = memcpy(&addr4, data, sizeof (addr4));
+ break;
+ case AF_INET6:
+ if (len != sizeof (in6_addr_t))
+ return;
+ addr = memcpy(&addr6, data, sizeof (addr6));
+ break;
+ }
+
+ (void) inet_ntop(af, addr, line, INET6_ADDRSTRLEN);
+}
+
+/*
+ * charbuf is assumed to be of size MAX_CHAR_STRING_SIZE.
+ */
+static const uchar_t *
+get_char_string(const uchar_t *data, char *charbuf, uint16_t datalen)
+{
+ uint8_t len;
+ char *name = charbuf;
+ int i = 0;
+
+ /*
+ * From RFC1035, a character-string is a single length octet followed
+ * by that number of characters.
+ */
+ if (datalen > 1) {
+ len = *data;
+ data++;
+ if (len > 0 && len < MAX_CHAR_STRING_SIZE) {
+ for (i = 0; i < len; i++, data++)
+ name[i] = *data;
+ }
+ }
+ name[i] = '\0';
+ return (data);
+}
+
+static size_t
+print_char_string(char *line, const uchar_t *data, uint16_t len)
+{
+ char charbuf[MAX_CHAR_STRING_SIZE];
+ const uchar_t *data_bak = data;
+
+ data = get_char_string(data, charbuf, len);
+ (void) sprintf(line, "%s", charbuf);
+ return (data - data_bak);
+}
+
+/*
+ * header: the entire message header, this is where we start to
+ * count the offset of the compression scheme
+ * data: the start of the domain name
+ * namebuf: user supplied buffer
+ * return: the next byte after what we have parsed
+ */
+static const uchar_t *
+get_domain_name(const uchar_t *header, const uchar_t *data,
+ const uchar_t *data_end, char *namebuf, char *namend)
+{
+ uint8_t len;
+ char *name = namebuf;
+
+ /*
+ * From RFC1035, a domain name is a sequence of labels, where each
+ * label consists of a length octet followed by that number of
+ * octets. The domain name terminates with the zero length octet
+ * for the null label of the root.
+ */
+
+ while (name < (namend - 1)) {
+ if ((data_end - data) < (ptrdiff_t)(sizeof (uint8_t))) {
+ /* The length octet is off the end of the packet. */
+ break;
+ }
+ GETINT8(len, data);
+ if (len == 0) {
+ /*
+ * Domain names end with a length byte of zero,
+ * which represents the null label of the root.
+ */
+ break;
+ }
+ /*
+ * test if we are using the compression scheme
+ */
+ if ((len & 0xc0) == 0xc0) {
+ uint16_t offset;
+ const uchar_t *label_ptr;
+
+ /*
+ * From RFC1035, message compression allows a
+ * domain name or a list of labels at the end of a
+ * domain name to be replaced with a pointer to a
+ * prior occurance of the same name. In this
+ * scheme, the pointer is a two octet sequence
+ * where the most significant two bits are set, and
+ * the remaining 14 bits are the offset from the
+ * start of the message of the next label.
+ */
+ data--;
+ if ((data_end - data) <
+ (ptrdiff_t)(sizeof (uint16_t))) {
+ /*
+ * The offset octets aren't entirely
+ * contained within this pakcet.
+ */
+ data = data_end;
+ break;
+ }
+ GETINT16(offset, data);
+ label_ptr = header + (offset & 0x3fff);
+ /*
+ * We must verify that the offset is valid by
+ * checking that it is less than the current data
+ * pointer and that it isn't off the end of the
+ * packet.
+ */
+ if (label_ptr > data || label_ptr >= data_end)
+ break;
+ (void) get_domain_name(header, label_ptr, data_end,
+ name, namend);
+ return (data);
+ } else {
+ if (len > (data_end - data)) {
+ /*
+ * The label isn't entirely contained
+ * within the packet. Don't read it. The
+ * caller checks that the data pointer is
+ * not beyond the end after we've
+ * incremented it.
+ */
+ data = data_end;
+ break;
+ }
+ while (len > 0 && name < (namend - 2)) {
+ *name = *data;
+ name++;
+ data++;
+ len--;
+ }
+ *name = '.';
+ name++;
+ }
+ }
+ *name = '\0';
+ return (data);
+}
+
+static size_t
+print_domain_name(char *line, const uchar_t *header, const uchar_t *data,
+ const uchar_t *data_end)
+{
+ char name[NS_MAXDNAME];
+ const uchar_t *new_data;
+
+ new_data = get_domain_name(header, data, data_end, name,
+ name + sizeof (name));
+
+ (void) sprintf(line, "%s", name);
+ return (new_data - data);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ether.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ether.c
new file mode 100644
index 0000000000..22e4943d09
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ether.c
@@ -0,0 +1,1510 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI" /* SunOS */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/dlpi.h>
+#include <sys/sysmacros.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/if_ether.h>
+#include <sys/ib/clients/ibd/ibd.h>
+
+#include "at.h"
+#include "snoop.h"
+
+static uint_t ether_header_len(char *), fddi_header_len(char *),
+ tr_header_len(char *), ib_header_len(char *);
+static uint_t interpret_ether(), interpret_fddi(), interpret_tr();
+static uint_t interpret_ib(int, char *, int, int);
+static void addr_copy_swap(struct ether_addr *, struct ether_addr *);
+
+interface_t *interface;
+interface_t INTERFACES[] = {
+
+ /* IEEE 802.3 CSMA/CD network */
+ { DL_CSMACD, 1550, ether_header_len, interpret_ether, IF_HDR_FIXED },
+
+ /* Ethernet Bus */
+ { DL_ETHER, 1550, ether_header_len, interpret_ether, IF_HDR_FIXED },
+
+ /* Fiber Distributed data interface */
+ { DL_FDDI, 4500, fddi_header_len, interpret_fddi, IF_HDR_VAR },
+
+ /* Token Ring interface */
+ { DL_TPR, 17800, tr_header_len, interpret_tr, IF_HDR_VAR },
+
+ /* Infiniband */
+ { DL_IB, 4096, ib_header_len, interpret_ib, IF_HDR_FIXED },
+
+ { (uint_t)-1, 0, 0, 0, 0 }
+
+};
+
+
+/* externals */
+extern char *dlc_header;
+extern int pi_frame;
+extern int pi_time_hour;
+extern int pi_time_min;
+extern int pi_time_sec;
+extern int pi_time_usec;
+
+char *printether();
+char *print_ethertype();
+static char *print_etherinfo();
+
+char *print_fc();
+char *print_smttype();
+char *print_smtclass();
+
+struct ether_addr ether_broadcast = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+static char *data; /* current data buffer */
+static int datalen; /* current data buffer length */
+
+uint_t
+interpret_ether(flags, e, elen, origlen)
+ int flags;
+ struct ether_header *e;
+ int elen, origlen;
+{
+ char *off;
+ int len;
+ int ieee8023 = 0;
+ extern char *dst_name;
+ int ethertype;
+ boolean_t data_copied = B_FALSE;
+ int blen = MAX(origlen, ETHERMTU);
+
+ if (data != NULL && datalen != 0 && datalen < blen) {
+ free(data);
+ data = NULL;
+ datalen = 0;
+ }
+ if (!data) {
+ data = (char *)malloc(blen);
+ if (!data)
+ pr_err("Warning: malloc failure");
+ datalen = blen;
+ }
+ if (origlen < 14) {
+ if (flags & F_SUM)
+ (void) sprintf(get_sum_line(),
+ "RUNT (short packet - %d bytes)",
+ origlen);
+ if (flags & F_DTAIL)
+ show_header("RUNT: ", "Short packet", origlen);
+ return (elen);
+ }
+ if (elen < 14)
+ return (elen);
+
+ if (memcmp(&e->ether_dhost, &ether_broadcast,
+ sizeof (struct ether_addr)) == 0)
+ dst_name = "(broadcast)";
+ else if (e->ether_dhost.ether_addr_octet[0] & 1)
+ dst_name = "(multicast)";
+
+ ethertype = ntohs(e->ether_type);
+
+ /*
+ * The 14 byte ether header screws up alignment
+ * of the rest of the packet for 32 bit aligned
+ * architectures like SPARC. Alas, we have to copy
+ * the rest of the packet in order to align it.
+ */
+ len = elen - sizeof (struct ether_header);
+ off = (char *)(e + 1);
+ if (ethertype <= 1514) {
+ /*
+ * Fake out the IEEE 802.3 packets.
+ * Should be DSAP=170, SSAP=170, control=3
+ * then three padding bytes of zero,
+ * followed by a normal ethernet-type packet.
+ */
+ ieee8023 = ntohs(e->ether_type);
+ ethertype = ntohs(*(ushort_t *)(off + 6));
+ off += 8;
+ len -= 8;
+ }
+
+ /*
+ * We cannot trust the length field in the header to be correct.
+ * But we should continue to process the packet. Then user can
+ * notice something funny in the header.
+ */
+ if (len > 0 && (off + len <= (char *)e + elen)) {
+ (void) memcpy(data, off, len);
+ data_copied = B_TRUE;
+ }
+
+ if (flags & F_SUM) {
+ (void) sprintf(get_sum_line(),
+ "ETHER Type=%04X (%s), size = %d bytes",
+ ethertype,
+ print_ethertype(ethertype),
+ origlen);
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("ETHER: ", "Ether Header", elen);
+ show_space();
+ (void) sprintf(get_line(0, 0),
+ "Packet %d arrived at %d:%02d:%d.%05d",
+ pi_frame,
+ pi_time_hour, pi_time_min, pi_time_sec,
+ pi_time_usec / 10);
+ (void) sprintf(get_line(0, 0),
+ "Packet size = %d bytes",
+ elen, elen);
+ (void) sprintf(get_line(0, 6),
+ "Destination = %s, %s",
+ printether(&e->ether_dhost),
+ print_etherinfo(&e->ether_dhost));
+ (void) sprintf(get_line(6, 6),
+ "Source = %s, %s",
+ printether(&e->ether_shost),
+ print_etherinfo(&e->ether_shost));
+ if (ieee8023 > 0)
+ (void) sprintf(get_line(12, 2),
+ "IEEE 802.3 length = %d bytes",
+ ieee8023);
+ (void) sprintf(get_line(12, 2),
+ "Ethertype = %04X (%s)",
+ ethertype, print_ethertype(ethertype));
+ show_space();
+ }
+
+ /* Go to the next protocol layer only if data have been copied. */
+ if (data_copied) {
+ switch (ethertype) {
+ case ETHERTYPE_IP:
+ (void) interpret_ip(flags, (struct ip *)data, len);
+ break;
+ /* Just in case it is decided to add this type */
+ case ETHERTYPE_IPV6:
+ (void) interpret_ipv6(flags, (ip6_t *)data, len);
+ break;
+ case ETHERTYPE_ARP:
+ case ETHERTYPE_REVARP:
+ interpret_arp(flags, (struct arphdr *)data, len);
+ break;
+ case ETHERTYPE_PPPOED:
+ case ETHERTYPE_PPPOES:
+ (void) interpret_pppoe(flags, (poep_t *)data, len);
+ break;
+ case ETHERTYPE_AARP: /* AppleTalk */
+ interpret_aarp(flags, data, len);
+ break;
+ case ETHERTYPE_AT:
+ interpret_at(flags, (struct ddp_hdr *)data, len);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return (elen);
+}
+
+/* ARGSUSED */
+uint_t
+ether_header_len(e)
+char *e;
+{
+ return (14);
+}
+
+
+/*
+ * Table of Ethertypes.
+ * Some of the more popular entries
+ * are at the beginning of the table
+ * to reduce search time.
+ */
+struct ether_type {
+ int e_type;
+ char *e_name;
+} ether_type [] = {
+ETHERTYPE_IP, "IP",
+ETHERTYPE_ARP, "ARP",
+ETHERTYPE_REVARP, "RARP",
+ETHERTYPE_IPV6, "IPv6",
+ETHERTYPE_PPPOED, "PPPoE Discovery",
+ETHERTYPE_PPPOES, "PPPoE Session",
+/* end of popular entries */
+ETHERTYPE_PUP, "Xerox PUP",
+0x0201, "Xerox PUP",
+0x0400, "Nixdorf",
+0x0600, "Xerox NS IDP",
+0x0601, "XNS Translation",
+0x0801, "X.75 Internet",
+0x0802, "NBS Internet",
+0x0803, "ECMA Internet",
+0x0804, "CHAOSnet",
+0x0805, "X.25 Level 3",
+0x0807, "XNS Compatibility",
+0x081C, "Symbolics Private",
+0x0888, "Xyplex",
+0x0889, "Xyplex",
+0x088A, "Xyplex",
+0x0900, "Ungermann-Bass network debugger",
+0x0A00, "Xerox IEEE802.3 PUP",
+0x0A01, "Xerox IEEE802.3 PUP Address Translation",
+0x0BAD, "Banyan Systems",
+0x0BAF, "Banyon VINES Echo",
+0x1000, "Berkeley Trailer negotiation",
+0x1000, "IP trailer (0)",
+0x1001, "IP trailer (1)",
+0x1002, "IP trailer (2)",
+0x1003, "IP trailer (3)",
+0x1004, "IP trailer (4)",
+0x1005, "IP trailer (5)",
+0x1006, "IP trailer (6)",
+0x1007, "IP trailer (7)",
+0x1008, "IP trailer (8)",
+0x1009, "IP trailer (9)",
+0x100a, "IP trailer (10)",
+0x100b, "IP trailer (11)",
+0x100c, "IP trailer (12)",
+0x100d, "IP trailer (13)",
+0x100e, "IP trailer (14)",
+0x100f, "IP trailer (15)",
+0x1234, "DCA - Multicast",
+0x1600, "VALID system protocol",
+0x1989, "Aviator",
+0x3C00, "3Com NBP virtual circuit datagram",
+0x3C01, "3Com NBP System control datagram",
+0x3C02, "3Com NBP Connect request (virtual cct)",
+0x3C03, "3Com NBP Connect response",
+0x3C04, "3Com NBP Connect complete",
+0x3C05, "3Com NBP Close request (virtual cct)",
+0x3C06, "3Com NBP Close response",
+0x3C07, "3Com NBP Datagram (like XNS IDP)",
+0x3C08, "3Com NBP Datagram broadcast",
+0x3C09, "3Com NBP Claim NetBIOS name",
+0x3C0A, "3Com NBP Delete Netbios name",
+0x3C0B, "3Com NBP Remote adaptor status request",
+0x3C0C, "3Com NBP Remote adaptor response",
+0x3C0D, "3Com NBP Reset",
+0x4242, "PCS Basic Block Protocol",
+0x4321, "THD - Diddle",
+0x5208, "BBN Simnet Private",
+0x6000, "DEC unass, experimental",
+0x6001, "DEC Dump/Load",
+0x6002, "DEC Remote Console",
+0x6003, "DECNET Phase IV, DNA Routing",
+0x6004, "DEC LAT",
+0x6005, "DEC Diagnostic",
+0x6006, "DEC customer protocol",
+0x6007, "DEC Local Area VAX Cluster (LAVC)",
+0x6008, "DEC unass (AMBER?)",
+0x6009, "DEC unass (MUMPS?)",
+0x6010, "3Com",
+0x6011, "3Com",
+0x6012, "3Com",
+0x6013, "3Com",
+0x6014, "3Com",
+0x7000, "Ungermann-Bass download",
+0x7001, "Ungermann-Bass NIUs",
+0x7002, "Ungermann-Bass diagnostic/loopback",
+0x7003, "Ungermann-Bass ? (NMC to/from UB Bridge)",
+0x7005, "Ungermann-Bass Bridge Spanning Tree",
+0x7007, "OS/9 Microware",
+0x7009, "OS/9 Net?",
+0x7020, "Sintrom",
+0x7021, "Sintrom",
+0x7022, "Sintrom",
+0x7023, "Sintrom",
+0x7024, "Sintrom",
+0x7025, "Sintrom",
+0x7026, "Sintrom",
+0x7027, "Sintrom",
+0x7028, "Sintrom",
+0x7029, "Sintrom",
+0x8003, "Cronus VLN",
+0x8004, "Cronus Direct",
+0x8005, "HP Probe protocol",
+0x8006, "Nestar",
+0x8008, "AT&T/Stanford Univ",
+0x8010, "Excelan",
+0x8013, "SGI diagnostic",
+0x8014, "SGI network games",
+0x8015, "SGI reserved",
+0x8016, "SGI XNS NameServer, bounce server",
+0x8019, "Apollo DOMAIN",
+0x802E, "Tymshare",
+0x802F, "Tigan,",
+0x8036, "Aeonic Systems",
+0x8037, "IPX (Novell Netware)",
+0x8038, "DEC LanBridge Management",
+0x8039, "DEC unass (DSM/DTP?)",
+0x803A, "DEC unass (Argonaut Console?)",
+0x803B, "DEC unass (VAXELN?)",
+0x803C, "DEC unass (NMSV? DNA Naming Service?)",
+0x803D, "DEC Ethernet CSMA/CD Encryption Protocol",
+0x803E, "DEC unass (DNA Time Service?)",
+0x803F, "DEC LAN Traffic Monitor Protocol",
+0x8040, "DEC unass (NetBios Emulator?)",
+0x8041, "DEC unass (MS/DOS?, Local Area System Transport?)",
+0x8042, "DEC unass",
+0x8044, "Planning Research Corp.",
+0x8046, "AT&T",
+0x8047, "AT&T",
+0x8049, "ExperData",
+0x805B, "VMTP",
+0x805C, "Stanford V Kernel, version 6.0",
+0x805D, "Evans & Sutherland",
+0x8060, "Little Machines",
+0x8062, "Counterpoint",
+0x8065, "University of Mass. at Amherst",
+0x8066, "University of Mass. at Amherst",
+0x8067, "Veeco Integrated Automation",
+0x8068, "General Dynamics",
+0x8069, "AT&T",
+0x806A, "Autophon",
+0x806C, "ComDesign",
+0x806D, "Compugraphic Corp",
+0x806E, "Landmark",
+0x806F, "Landmark",
+0x8070, "Landmark",
+0x8071, "Landmark",
+0x8072, "Landmark",
+0x8073, "Landmark",
+0x8074, "Landmark",
+0x8075, "Landmark",
+0x8076, "Landmark",
+0x8077, "Landmark",
+0x807A, "Matra",
+0x807B, "Dansk Data Elektronik",
+0x807C, "Merit Internodal",
+0x807D, "Vitalink",
+0x807E, "Vitalink",
+0x807F, "Vitalink",
+0x8080, "Vitalink TransLAN III Management",
+0x8081, "Counterpoint",
+0x8082, "Counterpoint",
+0x8083, "Counterpoint",
+0x8088, "Xyplex",
+0x8089, "Xyplex",
+0x808A, "Xyplex",
+0x809B, "EtherTalk (AppleTalk over Ethernet)",
+0x809C, "Datability",
+0x809D, "Datability",
+0x809E, "Datability",
+0x809F, "Spider Systems",
+0x80A3, "Nixdorf",
+0x80A4, "Siemens Gammasonics",
+0x80C0, "DCA Data Exchange Cluster",
+0x80C6, "Pacer Software",
+0x80C7, "Applitek Corp",
+0x80C8, "Intergraph",
+0x80C9, "Intergraph",
+0x80CB, "Intergraph",
+0x80CC, "Intergraph",
+0x80CA, "Intergraph",
+0x80CD, "Harris Corp",
+0x80CE, "Harris Corp",
+0x80CF, "Taylor Instrument",
+0x80D0, "Taylor Instrument",
+0x80D1, "Taylor Instrument",
+0x80D2, "Taylor Instrument",
+0x80D3, "Rosemount Corp",
+0x80D4, "Rosemount Corp",
+0x80D5, "IBM SNA Services over Ethernet",
+0x80DD, "Varian Associates",
+0x80DE, "TRFS",
+0x80DF, "TRFS",
+0x80E0, "Allen-Bradley",
+0x80E1, "Allen-Bradley",
+0x80E2, "Allen-Bradley",
+0x80E3, "Allen-Bradley",
+0x80E4, "Datability",
+0x80F2, "Retix",
+0x80F3, "AARP (Appletalk)",
+0x80F4, "Kinetics",
+0x80F5, "Kinetics",
+0x80F7, "Apollo",
+0x80FF, "Wellfleet Communications",
+0x8102, "Wellfleet Communications",
+0x8107, "Symbolics Private",
+0x8108, "Symbolics Private",
+0x8109, "Symbolics Private",
+0x812B, "Talaris",
+0x8130, "Waterloo",
+0x8131, "VG Lab",
+0x8137, "Novell (old) NetWare IPX",
+0x8138, "Novell",
+0x814C, "SNMP over Ethernet",
+0x817D, "XTP",
+0x81D6, "Lantastic",
+0x8888, "HP LanProbe test?",
+0x9000, "Loopback",
+0x9001, "3Com, XNS Systems Management",
+0x9002, "3Com, TCP/IP Systems Management",
+0x9003, "3Com, loopback detection",
+0xAAAA, "DECNET (VAX 6220 DEBNI)",
+0xFF00, "BBN VITAL-LanBridge cache wakeups",
+0, "",
+};
+
+char *
+print_fc(type)
+uint_t type;
+{
+
+ switch (type) {
+ case 0x50: return ("LLC");
+ case 0x4f: return ("SMT NSA");
+ case 0x41: return ("SMT Info");
+ default: return ("Unknown");
+ }
+}
+
+char *
+print_smtclass(type)
+uint_t type;
+{
+ switch (type) {
+ case 0x01: return ("NIF");
+ case 0x02: return ("SIF Conf");
+ case 0x03: return ("SIF Oper");
+ case 0x04: return ("ECF");
+ case 0x05: return ("RAF");
+ case 0x06: return ("RDF");
+ case 0x07: return ("SRF");
+ case 0x08: return ("PMF Get");
+ case 0x09: return ("PMF Change");
+ case 0x0a: return ("PMF Add");
+ case 0x0b: return ("PMF Remove");
+ case 0xff: return ("ESF");
+ default: return ("Unknown");
+ }
+
+}
+char *
+print_smttype(type)
+uint_t type;
+{
+ switch (type) {
+ case 0x01: return ("Announce");
+ case 0x02: return ("Request");
+ case 0x03: return ("Response");
+ default: return ("Unknown");
+ }
+
+}
+char *
+print_ethertype(type)
+ int type;
+{
+ int i;
+
+ for (i = 0; ether_type[i].e_type; i++)
+ if (type == ether_type[i].e_type)
+ return (ether_type[i].e_name);
+ if (type < 1500)
+ return ("LLC/802.3");
+
+ return ("Unknown");
+}
+
+#define MAX_RDFLDS 14 /* changed to 14 from 8 as per IEEE */
+#define TR_FN_ADDR 0x80 /* dest addr is functional */
+#define TR_SR_ADDR 0x80 /* MAC utilizes source route */
+#define ACFCDASA_LEN 14 /* length of AC|FC|DA|SA */
+#define TR_MAC_MASK 0xc0
+#define TR_AC 0x00 /* Token Ring access control */
+#define TR_LLC_FC 0x40 /* Token Ring llc frame control */
+#define LSAP_SNAP 0xaa
+#define LLC_SNAP_HDR_LEN 8
+#define LLC_HDR1_LEN 3 /* DON'T use sizeof(struct llc_hdr1) */
+#define CNTL_LLC_UI 0x03 /* un-numbered information packet */
+
+/*
+ * Source Routing Route Information field.
+ */
+struct tr_ri {
+#if defined(_BIT_FIELDS_HTOL)
+ uchar_t rt:3; /* routing type */
+ uchar_t len:5; /* length */
+ uchar_t dir:1; /* direction bit */
+ uchar_t mtu:3; /* largest frame */
+ uchar_t res:4; /* reserved */
+#elif defined(_BIT_FIELDS_LTOH)
+ uchar_t len:5; /* length */
+ uchar_t rt:3; /* routing type */
+ uchar_t res:4; /* reserved */
+ uchar_t mtu:3; /* largest frame */
+ uchar_t dir:1; /* direction bit */
+#endif
+/*
+ * In little endian machine, the ring field has to be stored in a
+ * ushort_t type. This implies that it is not possible to have a
+ * layout of bit field to represent bridge and ring.
+ *
+ * If the compiler uses _BIT_FIELDS_HTOL and it is a big endian
+ * machine, the following bit field definition will work.
+ *
+ * struct tr_rd {
+ * ushort_t bridge:4;
+ * ushort_t ring:12;
+ * } rd[MAX_RDFLDS];
+ *
+ * If the compiler uses _BIT_FIELDS_LTOH and it is a big endian
+ * machine, the definition can be changed to
+ *
+ * struct tr_rd {
+ * ushort_t bridge:4;
+ * ushort_t ring:12;
+ * } rd[MAX_RDFLDS];
+ *
+ * With little endian machine, we need to use 2 macroes. For
+ * simplicity, since the macroes work for both big and little
+ * endian machines, we will not use bit fields for the
+ * definition.
+ */
+#define bridge(route) (ntohs((ushort_t)(route)) & 0x0F)
+#define ring(route) (ntohs((ushort_t)(route)) >> 4)
+
+ ushort_t rd[MAX_RDFLDS]; /* route designator fields */
+};
+
+struct tr_header {
+ uchar_t ac;
+ uchar_t fc;
+ struct ether_addr dhost;
+ struct ether_addr shost;
+ struct tr_ri ri;
+};
+
+struct llc_snap_hdr {
+ uchar_t d_lsap; /* destination service access point */
+ uchar_t s_lsap; /* source link service access point */
+ uchar_t control; /* short control field */
+ uchar_t org[3]; /* Ethernet style organization field */
+ ushort_t type; /* Ethernet style type field */
+};
+
+struct ether_addr tokenbroadcastaddr2 = {
+ 0xc0, 0x00, 0xff, 0xff, 0xff, 0xff
+};
+
+int Mtutab[] = {516, 1470, 2052, 4472, 8144, 11407, 17800};
+
+char *
+print_sr(struct tr_ri *rh)
+{
+ int hops, ii;
+ static char line[512];
+
+ sprintf(line, "TR Source Route dir=%d, mtu=%d",
+ rh->dir, Mtutab[rh->mtu]);
+
+ hops = (int)(rh->len - 2) / (int)2;
+
+ if (hops) {
+ sprintf(line+strlen(line), ", Route: ");
+ for (ii = 0; ii < hops; ii++) {
+ if (! bridge(rh->rd[ii])) {
+ sprintf(line+strlen(line), "(%d)",
+ ring(rh->rd[ii]));
+ } else {
+ sprintf(line+strlen(line), "(%d)%d",
+ ring(rh->rd[ii]), bridge(rh->rd[ii]));
+ }
+ }
+ }
+ return (&line[0]);
+}
+
+uint_t
+interpret_tr(flags, e, elen, origlen)
+ int flags;
+ caddr_t e;
+ int elen, origlen;
+{
+ struct tr_header *mh;
+ struct tr_ri *rh;
+ uchar_t fc;
+ struct llc_snap_hdr *snaphdr;
+ char *off;
+ int maclen, len;
+ boolean_t data_copied = B_FALSE;
+ extern char *dst_name, *src_name;
+ int ethertype;
+ int is_llc = 0, is_snap = 0, source_routing = 0;
+ int tr_machdr_len(char *, int *, int *);
+ int blen = MAX(origlen, 17800);
+
+ if (data != NULL && datalen != 0 && datalen < blen) {
+ free(data);
+ data = NULL;
+ datalen = 0;
+ }
+ if (!data) {
+ data = (char *)malloc(blen);
+ if (!data)
+ pr_err("Warning: malloc failure");
+ datalen = blen;
+ }
+
+ if (origlen < ACFCDASA_LEN) {
+ if (flags & F_SUM)
+ (void) sprintf(get_sum_line(),
+ "RUNT (short packet - %d bytes)",
+ origlen);
+ if (flags & F_DTAIL)
+ show_header("RUNT: ", "Short packet", origlen);
+ return (elen);
+ }
+ if (elen < ACFCDASA_LEN)
+ return (elen);
+
+ mh = (struct tr_header *)e;
+ rh = (struct tr_ri *)&mh->ri;
+ fc = mh->fc;
+
+ if (is_llc = tr_machdr_len(e, &maclen, &source_routing)) {
+ snaphdr = (struct llc_snap_hdr *)(e + maclen);
+ if (snaphdr->d_lsap == LSAP_SNAP &&
+ snaphdr->s_lsap == LSAP_SNAP &&
+ snaphdr->control == CNTL_LLC_UI) {
+ is_snap = 1;
+ }
+ }
+
+ if (memcmp(&mh->dhost, &ether_broadcast,
+ sizeof (struct ether_addr)) == 0)
+ dst_name = "(broadcast)";
+ else if (memcmp(&mh->dhost, &tokenbroadcastaddr2,
+ sizeof (struct ether_addr)) == 0)
+ dst_name = "(mac broadcast)";
+ else if (mh->dhost.ether_addr_octet[0] & TR_FN_ADDR)
+ dst_name = "(functional)";
+
+ if (is_snap)
+ ethertype = ntohs(snaphdr->type);
+ else {
+ src_name = print_etherinfo(&mh->shost);
+ dst_name = print_etherinfo(&mh->dhost);
+ }
+
+ /*
+ * The 14 byte ether header screws up alignment
+ * of the rest of the packet for 32 bit aligned
+ * architectures like SPARC. Alas, we have to copy
+ * the rest of the packet in order to align it.
+ */
+ if (is_llc) {
+ if (is_snap) {
+ len = elen - (maclen + LLC_SNAP_HDR_LEN);
+ off = (char *)(e + maclen + LLC_SNAP_HDR_LEN);
+ } else {
+ len = elen - (maclen + LLC_HDR1_LEN);
+ off = (char *)(e + maclen + LLC_HDR1_LEN);
+ }
+ } else {
+ len = elen - maclen;
+ off = (char *)(e + maclen);
+ }
+
+ if (len > 0 && (off + len <= (char *)e + elen)) {
+ (void) memcpy(data, off, len);
+ data_copied = B_TRUE;
+ }
+
+ if (flags & F_SUM) {
+ if (source_routing)
+ sprintf(get_sum_line(), print_sr(rh));
+
+ if (is_llc) {
+ if (is_snap) {
+ (void) sprintf(get_sum_line(),
+ "TR LLC w/SNAP Type=%04X (%s), size=%d bytes",
+ ethertype,
+ print_ethertype(ethertype),
+ origlen);
+ } else {
+ (void) sprintf(get_sum_line(),
+ "TR LLC, but no SNAP encoding, size = %d bytes",
+ origlen);
+ }
+ } else {
+ (void) sprintf(get_sum_line(),
+ "TR MAC FC=%02X (%s), size = %d bytes",
+ fc, print_fc(fc), origlen);
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("TR: ", "TR Header", elen);
+ show_space();
+ (void) sprintf(get_line(0, 0),
+ "Packet %d arrived at %d:%02d:%d.%05d",
+ pi_frame,
+ pi_time_hour, pi_time_min, pi_time_sec,
+ pi_time_usec / 10);
+ (void) sprintf(get_line(0, 0),
+ "Packet size = %d bytes",
+ elen);
+ (void) sprintf(get_line(0, 1),
+ "Frame Control = %02x (%s)",
+ fc, print_fc(fc));
+ (void) sprintf(get_line(2, 6),
+ "Destination = %s, %s",
+ printether(&mh->dhost),
+ print_etherinfo(&mh->dhost));
+ (void) sprintf(get_line(8, 6),
+ "Source = %s, %s",
+ printether(&mh->shost),
+ print_etherinfo(&mh->shost));
+
+ if (source_routing)
+ sprintf(get_line(ACFCDASA_LEN, rh->len), print_sr(rh));
+
+ if (is_llc) {
+ (void) sprintf(get_line(maclen, 1),
+ "Dest Service Access Point = %02x",
+ snaphdr->d_lsap);
+ (void) sprintf(get_line(maclen+1, 1),
+ "Source Service Access Point = %02x",
+ snaphdr->s_lsap);
+ (void) sprintf(get_line(maclen+2, 1),
+ "Control = %02x",
+ snaphdr->control);
+ if (is_snap)
+ (void) sprintf(get_line(maclen+3, 3),
+ "SNAP Protocol Id = %02x%02x%02x",
+ snaphdr->org[0], snaphdr->org[1],
+ snaphdr->org[2]);
+ }
+
+ if (is_snap)
+ (void) sprintf(get_line(maclen+6, 2),
+ "SNAP Type = %04X (%s)",
+ ethertype, print_ethertype(ethertype));
+
+ show_space();
+ }
+
+ /* go to the next protocol layer */
+ if (is_snap && data_copied) {
+ switch (ethertype) {
+ case ETHERTYPE_IP:
+ (void) interpret_ip(flags, (struct ip *)data, len);
+ break;
+ /* Just in case it is decided to add this type */
+ case ETHERTYPE_IPV6:
+ (void) interpret_ipv6(flags, (ip6_t *)data, len);
+ break;
+ case ETHERTYPE_ARP:
+ case ETHERTYPE_REVARP:
+ interpret_arp(flags, (struct arphdr *)data, len);
+ break;
+ case ETHERTYPE_AARP: /* AppleTalk */
+ interpret_aarp(flags, data, len);
+ break;
+ case ETHERTYPE_AT:
+ interpret_at(flags, (struct ddp_hdr *)data, len);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return (elen);
+}
+
+
+/*
+ * stuffs length of mac and ri fields into *lenp
+ * returns:
+ * 0: mac frame
+ * 1: llc frame
+ */
+int
+tr_machdr_len(char *e, int *lenp, int *source_routing)
+{
+ struct tr_header *mh;
+ struct tr_ri *rh;
+ uchar_t fc;
+
+ mh = (struct tr_header *)e;
+ rh = (struct tr_ri *)&mh->ri;
+ fc = mh->fc;
+
+ if (mh->shost.ether_addr_octet[0] & TR_SR_ADDR) {
+ *lenp = ACFCDASA_LEN + rh->len;
+ *source_routing = 1;
+ } else {
+ *lenp = ACFCDASA_LEN;
+ *source_routing = 0;
+ }
+
+ if ((fc & TR_MAC_MASK) == 0)
+ return (0); /* it's a MAC frame */
+ else
+ return (1); /* it's an LLC frame */
+}
+
+uint_t
+tr_header_len(e)
+char *e;
+{
+ struct llc_snap_hdr *snaphdr;
+ int len = 0, source_routing;
+
+ if (tr_machdr_len(e, &len, &source_routing) == 0)
+ return (len); /* it's a MAC frame */
+
+ snaphdr = (struct llc_snap_hdr *)(e + len);
+ if (snaphdr->d_lsap == LSAP_SNAP &&
+ snaphdr->s_lsap == LSAP_SNAP &&
+ snaphdr->control == CNTL_LLC_UI)
+ len += LLC_SNAP_HDR_LEN; /* it's a SNAP frame */
+ else
+ len += LLC_HDR1_LEN;
+
+ return (len);
+}
+
+struct fddi_header {
+ uchar_t fc;
+ struct ether_addr dhost, shost;
+ uchar_t dsap, ssap, ctl, proto_id[3];
+ ushort_t type;
+};
+
+uint_t
+interpret_fddi(flags, e, elen, origlen)
+ int flags;
+ caddr_t e;
+ int elen, origlen;
+{
+ struct fddi_header fhdr, *f = &fhdr;
+ char *off;
+ int len;
+ boolean_t data_copied = B_FALSE;
+ extern char *dst_name, *src_name;
+ int ethertype;
+ int is_llc = 0, is_smt = 0, is_snap = 0;
+ int blen = MAX(origlen, 4500);
+
+ if (data != NULL && datalen != 0 && datalen < blen) {
+ free(data);
+ data = NULL;
+ datalen = 0;
+ }
+ if (!data) {
+ data = (char *)malloc(blen);
+ if (!data)
+ pr_err("Warning: malloc failure");
+ datalen = blen;
+ }
+
+ if (origlen < 13) {
+ if (flags & F_SUM)
+ (void) sprintf(get_sum_line(),
+ "RUNT (short packet - %d bytes)",
+ origlen);
+ if (flags & F_DTAIL)
+ show_header("RUNT: ", "Short packet", origlen);
+ return (elen);
+ }
+ if (elen < 13)
+ return (elen);
+
+ (void) memcpy(&f->fc, e, sizeof (f->fc));
+ addr_copy_swap(&f->dhost, (struct ether_addr *)(e+1));
+ addr_copy_swap(&f->shost, (struct ether_addr *)(e+7));
+
+ if ((f->fc&0x50) == 0x50) {
+ is_llc = 1;
+ (void) memcpy(&f->dsap, e+13, sizeof (f->dsap));
+ (void) memcpy(&f->ssap, e+14, sizeof (f->ssap));
+ (void) memcpy(&f->ctl, e+15, sizeof (f->ctl));
+ if (f->dsap == 0xaa && f->ssap == 0xaa) {
+ is_snap = 1;
+ (void) memcpy(&f->proto_id, e+16, sizeof (f->proto_id));
+ (void) memcpy(&f->type, e+19, sizeof (f->type));
+ }
+ } else {
+ if ((f->fc&0x41) == 0x41 || (f->fc&0x4f) == 0x4f) {
+ is_smt = 1;
+ }
+ }
+
+
+ if (memcmp(&f->dhost, &ether_broadcast,
+ sizeof (struct ether_addr)) == 0)
+ dst_name = "(broadcast)";
+ else if (f->dhost.ether_addr_octet[0] & 0x01)
+ dst_name = "(multicast)";
+
+ if (is_snap)
+ ethertype = ntohs(f->type);
+ else {
+ src_name = print_etherinfo(&f->shost);
+ dst_name = print_etherinfo(&f->dhost);
+ }
+
+ /*
+ * The 14 byte ether header screws up alignment
+ * of the rest of the packet for 32 bit aligned
+ * architectures like SPARC. Alas, we have to copy
+ * the rest of the packet in order to align it.
+ */
+ if (is_llc) {
+ if (is_snap) {
+ len = elen - 21;
+ off = (char *)(e + 21);
+ } else {
+ len = elen - 16;
+ off = (char *)(e + 16);
+ }
+ } else {
+ len = elen - 13;
+ off = (char *)(e + 13);
+ }
+
+ if (len > 0 && (off + len <= (char *)e + elen)) {
+ (void) memcpy(data, off, len);
+ data_copied = B_TRUE;
+ }
+
+ if (flags & F_SUM) {
+ if (is_llc) {
+ if (is_snap) {
+ (void) sprintf(get_sum_line(),
+ "FDDI LLC Type=%04X (%s), size = %d bytes",
+ ethertype,
+ print_ethertype(ethertype),
+ origlen);
+ } else {
+ (void) sprintf(get_sum_line(),
+ "LLC, but no SNAP encoding, size = %d bytes",
+ origlen);
+ }
+ } else if (is_smt) {
+ (void) sprintf(get_sum_line(),
+ "SMT Type=%02X (%s), Class = %02X (%s), size = %d bytes",
+ *(uchar_t *)(data+1), print_smttype(*(data+1)), *data,
+ print_smtclass(*data), origlen);
+ } else {
+ (void) sprintf(get_sum_line(),
+ "FC=%02X (%s), size = %d bytes",
+ f->fc, print_fc(f->fc), origlen);
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("FDDI: ", "FDDI Header", elen);
+ show_space();
+ (void) sprintf(get_line(0, 0),
+ "Packet %d arrived at %d:%02d:%d.%05d",
+ pi_frame,
+ pi_time_hour, pi_time_min, pi_time_sec,
+ pi_time_usec / 10);
+ (void) sprintf(get_line(0, 0),
+ "Packet size = %d bytes",
+ elen, elen);
+ (void) sprintf(get_line(0, 6),
+ "Destination = %s, %s",
+ printether(&f->dhost),
+ print_etherinfo(&f->dhost));
+ (void) sprintf(get_line(6, 6),
+ "Source = %s, %s",
+ printether(&f->shost),
+ print_etherinfo(&f->shost));
+
+ if (is_llc) {
+ (void) sprintf(get_line(12, 2),
+ "Frame Control = %02x (%s)",
+ f->fc, print_fc(f->fc));
+ (void) sprintf(get_line(12, 2),
+ "Dest Service Access Point = %02x",
+ f->dsap);
+ (void) sprintf(get_line(12, 2),
+ "Source Service Access Point = %02x",
+ f->ssap);
+ (void) sprintf(get_line(12, 2),
+ "Control = %02x",
+ f->ctl);
+ if (is_snap)
+ (void) sprintf(get_line(12, 2),
+ "Protocol Id = %02x%02x%02x",
+ f->proto_id[0], f->proto_id[1], f->proto_id[2]);
+ } else if (is_smt) {
+ (void) sprintf(get_line(12, 2),
+ "Frame Control = %02x (%s)",
+ f->fc, print_fc(f->fc));
+ (void) sprintf(get_line(12, 2),
+ "Class = %02x (%s)",
+ (uchar_t)*data, print_smtclass(*data));
+ (void) sprintf(get_line(12, 2),
+ "Type = %02x (%s)",
+ *(uchar_t *)(data+1), print_smttype(*(data+1)));
+ } else {
+ (void) sprintf(get_line(12, 2),
+ "FC=%02X (%s), size = %d bytes",
+ f->fc, print_fc(f->fc), origlen);
+ }
+
+ if (is_snap)
+ (void) sprintf(get_line(12, 2),
+ "LLC Type = %04X (%s)",
+ ethertype, print_ethertype(ethertype));
+
+ show_space();
+ }
+
+ /* go to the next protocol layer */
+ if (is_llc && is_snap && f->ctl == 0x03 && data_copied) {
+ switch (ethertype) {
+ case ETHERTYPE_IP:
+ (void) interpret_ip(flags, (struct ip *)data, len);
+ break;
+ /* Just in case it is decided to add this type */
+ case ETHERTYPE_IPV6:
+ (void) interpret_ipv6(flags, (ip6_t *)data, len);
+ break;
+ case ETHERTYPE_ARP:
+ case ETHERTYPE_REVARP:
+ interpret_arp(flags, (struct arphdr *)data, len);
+ break;
+ default:
+ break;
+ }
+
+ }
+
+ return (elen);
+}
+
+uint_t
+fddi_header_len(e)
+char *e;
+{
+ struct fddi_header fhdr, *f = &fhdr;
+
+ (void) memcpy(&f->fc, e, sizeof (f->fc));
+ (void) memcpy(&f->dhost, e+1, sizeof (struct ether_addr));
+ (void) memcpy(&f->shost, e+7, sizeof (struct ether_addr));
+
+ if ((f->fc&0x50) == 0x50) {
+ (void) memcpy(&f->dsap, e+13, sizeof (f->dsap));
+ (void) memcpy(&f->ssap, e+14, sizeof (f->ssap));
+ (void) memcpy(&f->ctl, e+15, sizeof (f->ctl));
+ if (f->dsap == 0xaa && f->ssap == 0xaa) {
+ return (21);
+ }
+ return (16);
+ } else {
+ if ((f->fc&0x41) == 0x41 || (f->fc&0x4f) == 0x4f) {
+ return (13);
+ }
+ }
+}
+
+/*
+ * Print the given Ethernet address
+ */
+char *
+printether(p)
+ struct ether_addr *p;
+{
+ static char buf[256];
+
+ sprintf(buf, "%x:%x:%x:%x:%x:%x",
+ p->ether_addr_octet[0],
+ p->ether_addr_octet[1],
+ p->ether_addr_octet[2],
+ p->ether_addr_octet[3],
+ p->ether_addr_octet[4],
+ p->ether_addr_octet[5]);
+
+ return (buf);
+}
+
+/*
+ * Table of Ethernet Address Assignments
+ * Some of the more popular entries
+ * are at the beginning of the table
+ * to reduce search time. Note that the
+ * e-block's are stored in host byte-order.
+ */
+struct block_type {
+ int e_block;
+ char *e_name;
+} ether_block [] = {
+0x080020, "Sun",
+0x0000C6, "HP",
+0x08002B, "DEC",
+0x00000F, "NeXT",
+0x00000C, "Cisco",
+0x080069, "Silicon Graphics",
+0x000069, "Silicon Graphics",
+0x0000A7, "Network Computing Devices (NCD X-terminal)",
+0x08005A, "IBM",
+0x0000AC, "Apollo",
+/* end of popular entries */
+0x000002, "BBN",
+0x000010, "Sytek",
+0x000011, "Tektronix",
+0x000018, "Webster (?)",
+0x00001B, "Novell",
+0x00001D, "Cabletron",
+0x000020, "DIAB (Data Industrier AB)",
+0x000021, "SC&C",
+0x000022, "Visual Technology",
+0x000029, "IMC",
+0x00002A, "TRW",
+0x00003D, "AT&T",
+0x000049, "Apricot Ltd.",
+0x000055, "AT&T",
+0x00005A, "S & Koch",
+0x00005A, "Xerox 806 (unregistered)",
+0x00005E, "U.S. Department of Defense (IANA)",
+0x000065, "Network General",
+0x00006B, "MIPS",
+0x000077, "MIPS",
+0x000079, "NetWare (?)",
+0x00007A, "Ardent",
+0x00007B, "Research Machines",
+0x00007D, "Harris (3M) (old)",
+0x000080, "Imagen(?)",
+0x000081, "Synoptics",
+0x000084, "Aquila (?)",
+0x000086, "Gateway (?)",
+0x000089, "Cayman Systems Gatorbox",
+0x000093, "Proteon",
+0x000094, "Asante",
+0x000098, "Cross Com",
+0x00009F, "Ameristar Technology",
+0x0000A2, "Wellfleet",
+0x0000A3, "Network Application Technology",
+0x0000A4, "Acorn",
+0x0000A6, "Network General",
+0x0000A7, "Network Computing Devices (NCD X-terminal)",
+0x0000A9, "Network Systems",
+0x0000AA, "Xerox",
+0x0000B3, "CIMLinc",
+0x0000B5, "Datability Terminal Server",
+0x0000B7, "Dove Fastnet",
+0x0000BC, "Allen-Bradley",
+0x0000C0, "Western Digital",
+0x0000C8, "Altos",
+0x0000C9, "Emulex Terminal Server",
+0x0000D0, "Develcon Electronics, Ltd.",
+0x0000D1, "Adaptec Inc. Nodem product",
+0x0000D7, "Dartmouth College (NED Router)",
+0x0000DD, "Gould",
+0x0000DE, "Unigraph",
+0x0000E2, "Acer Counterpoint",
+0x0000E8, "Accton Technology Corporation",
+0x0000EE, "Network Designers Limited(?)",
+0x0000EF, "Alantec",
+0x0000F3, "Gandalf",
+0x0000FD, "High Level Hardware (Orion, UK)",
+0x000143, "IEEE 802",
+0x001700, "Kabel",
+0x004010, "Sonic",
+0x00608C, "3Com",
+0x00800F, "SMC",
+0x008019, "Dayna Communications Etherprint product",
+0x00802D, "Xylogics, Inc. Annex terminal servers",
+0x008035, "Technology Works",
+0x008087, "Okidata",
+0x00808C, "Frontier Software Development",
+0x0080C7, "Xircom Inc.",
+0x0080D0, "Computer Products International",
+0x0080D3, "Shiva Appletalk-Ethernet interface",
+0x0080D4, "Chase Limited",
+0x0080F1, "Opus",
+0x00AA00, "Intel",
+0x00B0D0, "Computer Products International",
+0x00DD00, "Ungermann-Bass",
+0x00DD01, "Ungermann-Bass",
+0x00EFE5, "IBM (3Com card)",
+0x020406, "BBN",
+0x026060, "3Com",
+0x026086, "Satelcom MegaPac (UK)",
+0x02E6D3, "Bus-Tech, Inc. (BTI)",
+0x080001, "Computer Vision",
+0x080002, "3Com (Formerly Bridge)",
+0x080003, "ACC (Advanced Computer Communications)",
+0x080005, "Symbolics",
+0x080007, "Apple",
+0x080008, "BBN",
+0x080009, "Hewlett-Packard",
+0x08000A, "Nestar Systems",
+0x08000B, "Unisys",
+0x08000D, "ICL",
+0x08000E, "NCR",
+0x080010, "AT&T",
+0x080011, "Tektronix, Inc.",
+0x080017, "NSC",
+0x08001A, "Data General",
+0x08001B, "Data General",
+0x08001E, "Apollo",
+0x080022, "NBI",
+0x080025, "CDC",
+0x080026, "Norsk Data (Nord)",
+0x080027, "PCS Computer Systems GmbH",
+0x080028, "TI Explorer",
+0x08002E, "Metaphor",
+0x08002F, "Prime Computer",
+0x080036, "Intergraph CAE stations",
+0x080037, "Fujitsu-Xerox",
+0x080038, "Bull",
+0x080039, "Spider Systems",
+0x08003B, "Torus Systems",
+0x08003E, "Motorola VME bus processor module",
+0x080041, "DCA Digital Comm. Assoc.",
+0x080046, "Sony",
+0x080047, "Sequent",
+0x080049, "Univation",
+0x08004C, "Encore",
+0x08004E, "BICC",
+0x080056, "Stanford University",
+0x080057, "Evans & Sutherland (?)",
+0x080067, "Comdesign",
+0x080068, "Ridge",
+0x08006A, "ATTst (?)",
+0x08006E, "Excelan",
+0x080075, "DDE (Danish Data Elektronik A/S)",
+0x080077, "TSL (now Retix)",
+0x08007C, "Vitalink TransLAN III",
+0x080080, "XIOS",
+0x080081, "Crosfield Electronics",
+0x080086, "Imagen/QMS",
+0x080087, "Xyplex terminal server",
+0x080089, "Kinetics AppleTalk-Ethernet interface",
+0x08008B, "Pyramid",
+0x08008D, "XyVision",
+0x080090, "Retix Inc Bridge",
+0x10005A, "IBM",
+0x1000D4, "DEC",
+0x400003, "NetWare",
+0x800010, "AT&T",
+0xAA0004, "DEC (DECNET)",
+0xC00000, "SMC",
+0, "",
+};
+
+/*
+ * The oui argument should be in host byte-order to conform with
+ * the above array's values.
+ */
+char *
+ether_ouiname(uint32_t oui)
+{
+ uint_t i;
+
+ for (i = 0; ether_block[i].e_block != 0; i++)
+ if (oui == ether_block[i].e_block)
+ return (ether_block[i].e_name);
+
+ return (NULL);
+}
+
+/*
+ * Print the additional Ethernet address info
+ */
+static char *
+print_etherinfo(eaddr)
+ struct ether_addr *eaddr;
+{
+ uint_t addr = 0;
+ char *p = (char *)&addr + 1;
+ char *ename;
+
+ (void) memcpy(p, eaddr, 3);
+
+ if (memcmp(eaddr, &ether_broadcast, sizeof (struct ether_addr)) == 0)
+ return ("(broadcast)");
+
+ if (eaddr->ether_addr_octet[0] & 1)
+ return ("(multicast)");
+
+ addr = ntohl(addr); /* make it right for little-endians */
+ ename = ether_ouiname(addr);
+
+ if (ename != NULL)
+ return (ename);
+ else
+ return ("");
+}
+
+static uchar_t endianswap[] = {
+ 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
+ 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
+ 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
+ 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
+ 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
+ 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
+ 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
+ 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
+ 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
+ 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
+ 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
+ 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
+ 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
+ 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
+ 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
+ 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
+ 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
+ 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
+ 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
+ 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
+ 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
+ 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
+ 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
+ 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
+ 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
+ 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
+ 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
+ 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
+ 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
+ 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
+ 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
+ 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff,
+};
+
+static void
+addr_copy_swap(pd, ps)
+ struct ether_addr *pd;
+ struct ether_addr *ps;
+{
+ pd->ether_addr_octet[0] = endianswap[ps->ether_addr_octet[0]];
+ pd->ether_addr_octet[1] = endianswap[ps->ether_addr_octet[1]];
+ pd->ether_addr_octet[2] = endianswap[ps->ether_addr_octet[2]];
+ pd->ether_addr_octet[3] = endianswap[ps->ether_addr_octet[3]];
+ pd->ether_addr_octet[4] = endianswap[ps->ether_addr_octet[4]];
+ pd->ether_addr_octet[5] = endianswap[ps->ether_addr_octet[5]];
+}
+
+/* ARGSUSED */
+uint_t
+ib_header_len(char *hdr)
+{
+ return (IPOIB_HDRSIZE);
+}
+
+static uint_t
+interpret_ib(int flags, char *header, int elen, int origlen)
+{
+ struct ipoib_header *hdr = (struct ipoib_header *)header;
+ char *off;
+ int len;
+ extern char *dst_name;
+ unsigned short ethertype;
+ int blen = MAX(origlen, 4096);
+
+ if (data != NULL && datalen != 0 && datalen < blen) {
+ free(data);
+ data = NULL;
+ datalen = 0;
+ }
+ if (data == NULL) {
+ data = malloc(blen);
+ if (data == NULL)
+ pr_err("Warning: malloc failure");
+ datalen = blen;
+ }
+ if (origlen < IPOIB_HDRSIZE) {
+ if (flags & F_SUM)
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "RUNT (short packet - %d bytes)", origlen);
+ if (flags & F_DTAIL)
+ show_header("RUNT: ", "Short packet", origlen);
+ return (elen);
+ }
+ if (elen < IPOIB_HDRSIZE)
+ return (elen);
+
+ /*
+ * It is not possible to understand just by looking
+ * at the header whether this was a broad/multi cast
+ * packet; thus dst_name is not updated.
+ */
+ ethertype = ntohs(hdr->ipoib_type);
+ len = elen - IPOIB_HDRSIZE;
+ off = (char *)(hdr + 1);
+ (void) memcpy(data, off, len);
+
+ if (flags & F_SUM) {
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "IPIB Type=%04X (%s), size = %d bytes",
+ ethertype,
+ print_ethertype(ethertype),
+ origlen);
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("IPIB: ", "IPIB Header", elen);
+ show_space();
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Packet %d arrived at %d:%02d:%d.%02d",
+ pi_frame, pi_time_hour, pi_time_min,
+ pi_time_sec, pi_time_usec / 10000);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Packet size = %d bytes", elen, elen);
+ (void) snprintf(get_line(0, 2), get_line_remain(),
+ "Ethertype = %04X (%s)", ethertype,
+ print_ethertype(ethertype));
+ show_space();
+ }
+
+ /* Go to the next protocol layer */
+ switch (ethertype) {
+ case ETHERTYPE_IP:
+ (void) interpret_ip(flags, (struct ip *)data, len);
+ break;
+ case ETHERTYPE_IPV6:
+ (void) interpret_ipv6(flags, (ip6_t *)data, len);
+ break;
+ case ETHERTYPE_ARP:
+ case ETHERTYPE_REVARP:
+ interpret_arp(flags, (struct arphdr *)data, len);
+ break;
+ }
+
+ return (elen);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_filter.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_filter.c
new file mode 100644
index 0000000000..162c693e05
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_filter.c
@@ -0,0 +1,2640 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1991-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI" /* SunOS */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <stddef.h>
+
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <inet/ip6.h>
+#include <inet/ip.h>
+#include <netinet/if_ether.h>
+#include <netinet/tcp.h>
+#include <netinet/udp.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <rpc/rpc.h>
+#include <rpc/rpcent.h>
+
+#include <sys/dlpi.h>
+#include <snoop.h>
+
+#define IPV4_ONLY 0
+#define IPV6_ONLY 1
+#define IPV4_AND_IPV6 2
+
+/*
+ * The following constants represent the offsets in bytes from the beginning
+ * of the IP(v6) header of the source and destination IP(v6) addresses.
+ * These are useful when generating filter code.
+ */
+#define IPV4_SRCADDR_OFFSET 12
+#define IPV4_DSTADDR_OFFSET 16
+#define IPV6_SRCADDR_OFFSET 8
+#define IPV6_DSTADDR_OFFSET 24
+#define IP_VERS(p) (((*(uchar_t *)p) & 0xf0) >> 4)
+#define MASKED_IPV4_VERS 0x40
+#define MASKED_IPV6_VERS 0x60
+#define IP_HDR_LEN(p) (((*(uchar_t *)p) & 0xf) * 4)
+#define TCP_HDR_LEN(p) ((((*((uchar_t *)p+12)) >> 4) & 0xf) * 4)
+/*
+ * Coding the constant below is tacky, but the compiler won't let us
+ * be more clever. E.g., &((struct ip *)0)->ip_xxx
+ */
+#define IP_PROTO_OF(p) (((uchar_t *)p)[9])
+
+/*
+ * AppleTalk uses 802.2 Ethernet encapsulation with LLC/SNAP headers,
+ * for 8 octets of overhead, and the common AppleTalk DDP Ethernet
+ * header is another 4 octets.
+ *
+ * The following constants represents the offsets in bytes from the beginning
+ * of the Ethernet payload to various parts of the DDP header.
+ */
+
+#define AT_DST_NET_OFFSET 12
+#define AT_SRC_NET_OFFSET 14
+#define AT_DST_NODE_OFFSET 16
+#define AT_SRC_NODE_OFFSET 17
+
+int eaddr; /* need ethernet addr */
+
+int opstack; /* operand stack depth */
+
+/*
+ * These are the operators of the user-level filter.
+ * STOP ends execution of the filter expression and
+ * returns the truth value at the top of the stack.
+ * OP_LOAD_OCTET, OP_LOAD_SHORT and OP_LOAD_LONG pop
+ * an offset value from the stack and load a value of
+ * an appropriate size from the packet (octet, short or
+ * long). The offset is computed from a base value that
+ * may be set via the OP_OFFSET operators.
+ * OP_EQ, OP_NE, OP_GT, OP_GE, OP_LT, OP_LE pop two values
+ * from the stack and return the result of their comparison.
+ * OP_AND, OP_OR, OP_XOR pop two values from the stack and
+ * do perform a bitwise operation on them - returning a result
+ * to the stack. OP_NOT inverts the bits of the value on the
+ * stack.
+ * OP_BRFL and OP_BRTR branch to an offset in the code array
+ * depending on the value at the top of the stack: true (not 0)
+ * or false (0).
+ * OP_ADD, OP_SUB, OP_MUL, OP_DIV and OP_REM pop two values
+ * from the stack and perform arithmetic.
+ * The OP_OFFSET operators change the base from which the
+ * OP_LOAD operators compute their offsets.
+ * OP_OFFSET_ZERO sets the offset to zero - beginning of packet.
+ * OP_OFFSET_LINK sets the base to the first octet after
+ * the link (DLC) header. OP_OFFSET_IP, OP_OFFSET_TCP,
+ * and OP_OFFSET_UDP do the same for those headers - they
+ * set the offset base to the *end* of the header - not the
+ * beginning. The OP_OFFSET_RPC operator is a bit unusual.
+ * It points the base at the cached RPC header. For the
+ * purposes of selection, RPC reply headers look like call
+ * headers except for the direction value.
+ * OP_OFFSET_POP restores the offset base to the value prior
+ * to the most recent OP_OFFSET call.
+ */
+enum optype {
+ OP_STOP = 0,
+ OP_LOAD_OCTET,
+ OP_LOAD_SHORT,
+ OP_LOAD_LONG,
+ OP_LOAD_CONST,
+ OP_LOAD_LENGTH,
+ OP_EQ,
+ OP_NE,
+ OP_GT,
+ OP_GE,
+ OP_LT,
+ OP_LE,
+ OP_AND,
+ OP_OR,
+ OP_XOR,
+ OP_NOT,
+ OP_BRFL,
+ OP_BRTR,
+ OP_ADD,
+ OP_SUB,
+ OP_MUL,
+ OP_DIV,
+ OP_REM,
+ OP_OFFSET_POP,
+ OP_OFFSET_ZERO,
+ OP_OFFSET_LINK,
+ OP_OFFSET_IP,
+ OP_OFFSET_TCP,
+ OP_OFFSET_UDP,
+ OP_OFFSET_RPC,
+ OP_OFFSET_SLP,
+ OP_LAST
+};
+
+static char *opnames[] = {
+ "STOP",
+ "LOAD_OCTET",
+ "LOAD_SHORT",
+ "LOAD_LONG",
+ "LOAD_CONST",
+ "LOAD_LENGTH",
+ "EQ",
+ "NE",
+ "GT",
+ "GE",
+ "LT",
+ "LE",
+ "AND",
+ "OR",
+ "XOR",
+ "NOT",
+ "BRFL",
+ "BRTR",
+ "ADD",
+ "SUB",
+ "MUL",
+ "DIV",
+ "REM",
+ "OFFSET_POP",
+ "OFFSET_ZERO",
+ "OFFSET_ETHER",
+ "OFFSET_IP",
+ "OFFSET_TCP",
+ "OFFSET_UDP",
+ "OFFSET_RPC",
+ "OP_OFFSET_SLP",
+ ""
+};
+
+#define MAXOPS 1024
+#define MAXSS 64
+static uint_t oplist[MAXOPS]; /* array of operators */
+static uint_t *curr_op; /* last op generated */
+
+extern int valid_slp(uchar_t *, int); /* decides if a SLP msg is valid */
+extern struct hostent *lgetipnodebyname(const char *, int, int, int *);
+
+static void alternation();
+static uint_t chain();
+static void codeprint();
+static void emitop();
+static void emitval();
+static void expression();
+static struct xid_entry *find_rpc();
+static void optimize();
+static void ethertype_match();
+
+
+/*
+ * Returns the ULP for an IPv4 or IPv6 packet
+ * Assumes that the packet has already been checked to verify
+ * that it's either IPv4 or IPv6
+ *
+ * XXX Will need to be updated for AH and ESP
+ * XXX when IPsec is supported for v6.
+ */
+static uchar_t
+ip_proto_of(uchar_t *ip)
+{
+ uchar_t nxt;
+ boolean_t not_done = B_TRUE;
+ uchar_t *ptr = ip;
+
+ switch (IP_VERS(ip)) {
+ case IPV4_VERSION:
+ return (IP_PROTO_OF(ip));
+ case IPV6_VERSION:
+
+ nxt = ip[6];
+ ptr += 40; /* size of ip6 header */
+ do {
+ switch (nxt) {
+ /*
+ * XXX Add IPsec headers here when supported for v6
+ * XXX (the AH will have a different size...)
+ */
+ case IPPROTO_HOPOPTS:
+ case IPPROTO_ROUTING:
+ case IPPROTO_FRAGMENT:
+ case IPPROTO_DSTOPTS:
+ ptr += (8 * (ptr[1] + 1));
+ nxt = *ptr;
+ break;
+
+ default:
+ not_done = B_FALSE;
+ break;
+ }
+ } while (not_done);
+ return (nxt);
+ default:
+ break; /* shouldn't get here... */
+ }
+ return (0);
+}
+
+/*
+ * Returns the total IP header length.
+ * For v4, this includes any options present.
+ * For v6, this is the length of the IPv6 header plus
+ * any extension headers present.
+ *
+ * XXX Will need to be updated for AH and ESP
+ * XXX when IPsec is supported for v6.
+ */
+static int
+ip_hdr_len(uchar_t *ip)
+{
+ uchar_t nxt;
+ int hdr_len;
+ boolean_t not_done = B_TRUE;
+ int len = 40; /* IPv6 header size */
+ uchar_t *ptr = ip;
+
+ switch (IP_VERS(ip)) {
+ case IPV4_VERSION:
+ return (IP_HDR_LEN(ip));
+ case IPV6_VERSION:
+ nxt = ip[6];
+ ptr += len;
+ do {
+ switch (nxt) {
+ /*
+ * XXX Add IPsec headers here when supported for v6
+ * XXX (the AH will have a different size...)
+ */
+ case IPPROTO_HOPOPTS:
+ case IPPROTO_ROUTING:
+ case IPPROTO_FRAGMENT:
+ case IPPROTO_DSTOPTS:
+ hdr_len = (8 * (ptr[1] + 1));
+ len += hdr_len;
+ ptr += hdr_len;
+ nxt = *ptr;
+ break;
+
+ default:
+ not_done = B_FALSE;
+ break;
+ }
+ } while (not_done);
+ return (len);
+ default:
+ break;
+ }
+ return (0); /* not IP */
+}
+
+static void
+codeprint()
+{
+ uint_t *op;
+
+ printf("User filter:\n");
+
+ for (op = oplist; *op; op++) {
+ if (*op <= OP_LAST)
+ printf("\t%2d: %s\n", op - oplist, opnames[*op]);
+ else
+ printf("\t%2d: (%d)\n", op - oplist, *op);
+
+ switch (*op) {
+ case OP_LOAD_CONST:
+ case OP_BRTR:
+ case OP_BRFL:
+ op++;
+ if ((int)*op < 0)
+ printf("\t%2d: 0x%08x (%d)\n",
+ op - oplist, *op, *op);
+ else
+ printf("\t%2d: %d (0x%08x)\n",
+ op - oplist, *op, *op);
+ }
+ }
+ printf("\t%2d: STOP\n", op - oplist);
+ printf("\n");
+}
+
+
+/*
+ * Take a pass through the generated code and optimize
+ * branches. A branch true (BRTR) that has another BRTR
+ * at its destination can use the address of the destination
+ * BRTR. A BRTR that points to a BRFL (branch false) should
+ * point to the address following the BRFL.
+ * A similar optimization applies to BRFL operators.
+ */
+static void
+optimize(uint_t *oplistp)
+{
+ uint_t *op;
+
+ for (op = oplistp; *op; op++) {
+ switch (*op) {
+ case OP_LOAD_CONST:
+ op++;
+ break;
+ case OP_BRTR:
+ op++;
+ optimize(&oplist[*op]);
+ if (oplist[*op] == OP_BRFL)
+ *op += 2;
+ else if (oplist[*op] == OP_BRTR)
+ *op = oplist[*op + 1];
+ break;
+ case OP_BRFL:
+ op++;
+ optimize(&oplist[*op]);
+ if (oplist[*op] == OP_BRTR)
+ *op += 2;
+ else if (oplist[*op] == OP_BRFL)
+ *op = oplist[*op + 1];
+ break;
+ }
+ }
+}
+
+/*
+ * RPC packets are tough to filter.
+ * While the call packet has all the interesting
+ * info: program number, version, procedure etc,
+ * the reply packet has none of this information.
+ * If we want to do useful filtering based on this
+ * information then we have to stash the information
+ * from the call packet, and use the XID in the reply
+ * to find the stashed info. The stashed info is
+ * kept in a circular lifo, assuming that a call packet
+ * will be followed quickly by its reply.
+ */
+
+struct xid_entry {
+ unsigned x_xid; /* The XID (32 bits) */
+ unsigned x_dir; /* CALL or REPLY */
+ unsigned x_rpcvers; /* Protocol version (2) */
+ unsigned x_prog; /* RPC program number */
+ unsigned x_vers; /* RPC version number */
+ unsigned x_proc; /* RPC procedure number */
+};
+static struct xid_entry xe_table[XID_CACHE_SIZE];
+static struct xid_entry *xe_first = &xe_table[0];
+static struct xid_entry *xe = &xe_table[0];
+static struct xid_entry *xe_last = &xe_table[XID_CACHE_SIZE - 1];
+
+static struct xid_entry *
+find_rpc(struct rpc_msg *rpc)
+{
+ struct xid_entry *x;
+
+ for (x = xe; x >= xe_first; x--)
+ if (x->x_xid == rpc->rm_xid)
+ return (x);
+ for (x = xe_last; x > xe; x--)
+ if (x->x_xid == rpc->rm_xid)
+ return (x);
+ return (NULL);
+}
+
+static void
+stash_rpc(struct rpc_msg *rpc)
+{
+ struct xid_entry *x;
+
+ if (find_rpc(rpc))
+ return;
+
+ x = xe++;
+ if (xe > xe_last)
+ xe = xe_first;
+ x->x_xid = rpc->rm_xid;
+ x->x_dir = htonl(REPLY);
+ x->x_prog = rpc->rm_call.cb_prog;
+ x->x_vers = rpc->rm_call.cb_vers;
+ x->x_proc = rpc->rm_call.cb_proc;
+}
+
+/*
+ * SLP can multicast requests, and recieve unicast replies in which
+ * neither the source nor destination port is identifiable as a SLP
+ * port. Hence, we need to do as RPC does, and keep track of packets we
+ * are interested in. For SLP, however, we use ports, not XIDs, and
+ * a smaller cache size is more efficient since every incoming packet
+ * needs to be checked.
+ */
+
+#define SLP_CACHE_SIZE 64
+static uint_t slp_table[SLP_CACHE_SIZE];
+static int slp_index = 0;
+
+/*
+ * Returns the index of dport in the table if found, otherwise -1.
+ */
+static int
+find_slp(uint_t dport) {
+ int i;
+
+ if (!dport)
+ return (0);
+
+ for (i = slp_index; i >= 0; i--)
+ if (slp_table[i] == dport) {
+ return (i);
+ }
+ for (i = SLP_CACHE_SIZE - 1; i > slp_index; i--)
+ if (slp_table[i] == dport) {
+ return (i);
+ }
+ return (-1);
+}
+
+static void stash_slp(uint_t sport) {
+ if (slp_table[slp_index - 1] == sport)
+ /* avoid redundancy due to multicast retransmissions */
+ return;
+
+ slp_table[slp_index++] = sport;
+ if (slp_index == SLP_CACHE_SIZE)
+ slp_index = 0;
+}
+
+/*
+ * This routine takes a packet and returns true or false
+ * according to whether the filter expression selects it
+ * or not.
+ * We assume here that offsets for short and long values
+ * are even - we may die with an alignment error if the
+ * CPU doesn't support odd addresses. Note that long
+ * values are loaded as two shorts so that 32 bit word
+ * alignment isn't important.
+ *
+ * IPv6 is a bit stickier to handle than IPv4...
+ */
+
+int
+want_packet(uchar_t *pkt, int len, int origlen)
+{
+ uint_t stack[MAXSS]; /* operand stack */
+ uint_t *op; /* current operator */
+ uint_t *sp; /* top of operand stack */
+ uchar_t *base; /* base for offsets into packet */
+ uchar_t *ip; /* addr of IP header, unaligned */
+ uchar_t *tcp; /* addr of TCP header, unaligned */
+ uchar_t *udp; /* addr of UDP header, unaligned */
+ struct rpc_msg rpcmsg; /* addr of RPC header */
+ struct rpc_msg *rpc;
+ int newrpc = 0;
+ uchar_t *slphdr; /* beginning of SLP header */
+ uint_t slp_sport, slp_dport;
+ int off, header_size;
+ uchar_t *offstack[MAXSS]; /* offset stack */
+ uchar_t **offp; /* current offset */
+ uchar_t *opkt = NULL;
+ uint_t olen;
+
+ sp = stack;
+ *sp = 1;
+ base = pkt;
+ offp = offstack;
+
+ header_size = (*interface->header_len)((char *)pkt);
+
+ for (op = oplist; *op; op++) {
+ switch ((enum optype) *op) {
+ case OP_LOAD_OCTET:
+ if ((base + *sp) > (pkt + len))
+ return (0); /* packet too short */
+
+ *sp = *((uchar_t *)(base + *sp));
+ break;
+ case OP_LOAD_SHORT:
+ off = *sp;
+
+ if ((base + off + sizeof (uint16_t) - 1) > (pkt + len))
+ return (0); /* packet too short */
+
+ /*
+ * Handle 2 possible alignments
+ */
+ switch ((((unsigned)base)+off) % sizeof (ushort_t)) {
+ case 0:
+ *sp = ntohs(*((ushort_t *)(base + *sp)));
+ break;
+ case 1:
+ *((uchar_t *)(sp)) =
+ *((uchar_t *)(base + off));
+ *(((uchar_t *)sp) + 1) =
+ *((uchar_t *)(base + off) + 1);
+ *sp = ntohs(*(ushort_t *)sp);
+ break;
+ }
+ break;
+ case OP_LOAD_LONG:
+ off = *sp;
+
+ if ((base + off + sizeof (uint32_t) - 1) > (pkt + len))
+ return (0); /* packet too short */
+
+ /*
+ * Handle 3 possible alignments
+ */
+ switch ((((unsigned)base) + off) % sizeof (uint_t)) {
+ case 0:
+ *sp = *(uint_t *)(base + off);
+ break;
+
+ case 2:
+ *((ushort_t *)(sp)) =
+ *((ushort_t *)(base + off));
+ *(((ushort_t *)sp) + 1) =
+ *((ushort_t *)(base + off) + 1);
+ break;
+
+ case 1:
+ case 3:
+ *((uchar_t *)(sp)) =
+ *((uchar_t *)(base + off));
+ *(((uchar_t *)sp) + 1) =
+ *((uchar_t *)(base + off) + 1);
+ *(((uchar_t *)sp) + 2) =
+ *((uchar_t *)(base + off) + 2);
+ *(((uchar_t *)sp) + 3) =
+ *((uchar_t *)(base + off) + 3);
+ break;
+ }
+ *sp = ntohl(*sp);
+ break;
+ case OP_LOAD_CONST:
+ if (sp >= &stack[MAXSS])
+ return (0);
+ *(++sp) = *(++op);
+ break;
+ case OP_LOAD_LENGTH:
+ if (sp >= &stack[MAXSS])
+ return (0);
+ *(++sp) = origlen;
+ break;
+ case OP_EQ:
+ if (sp < &stack[1])
+ return (0);
+ sp--;
+ *sp = *sp == *(sp + 1);
+ break;
+ case OP_NE:
+ if (sp < &stack[1])
+ return (0);
+ sp--;
+ *sp = *sp != *(sp + 1);
+ break;
+ case OP_GT:
+ if (sp < &stack[1])
+ return (0);
+ sp--;
+ *sp = *sp > *(sp + 1);
+ break;
+ case OP_GE:
+ if (sp < &stack[1])
+ return (0);
+ sp--;
+ *sp = *sp >= *(sp + 1);
+ break;
+ case OP_LT:
+ if (sp < &stack[1])
+ return (0);
+ sp--;
+ *sp = *sp < *(sp + 1);
+ break;
+ case OP_LE:
+ if (sp < &stack[1])
+ return (0);
+ sp--;
+ *sp = *sp <= *(sp + 1);
+ break;
+ case OP_AND:
+ if (sp < &stack[1])
+ return (0);
+ sp--;
+ *sp &= *(sp + 1);
+ break;
+ case OP_OR:
+ if (sp < &stack[1])
+ return (0);
+ sp--;
+ *sp |= *(sp + 1);
+ break;
+ case OP_XOR:
+ if (sp < &stack[1])
+ return (0);
+ sp--;
+ *sp ^= *(sp + 1);
+ break;
+ case OP_NOT:
+ *sp = !*sp;
+ break;
+ case OP_BRFL:
+ op++;
+ if (!*sp)
+ op = &oplist[*op] - 1;
+ break;
+ case OP_BRTR:
+ op++;
+ if (*sp)
+ op = &oplist[*op] - 1;
+ break;
+ case OP_ADD:
+ if (sp < &stack[1])
+ return (0);
+ sp--;
+ *sp += *(sp + 1);
+ break;
+ case OP_SUB:
+ if (sp < &stack[1])
+ return (0);
+ sp--;
+ *sp -= *(sp + 1);
+ break;
+ case OP_MUL:
+ if (sp < &stack[1])
+ return (0);
+ sp--;
+ *sp *= *(sp + 1);
+ break;
+ case OP_DIV:
+ if (sp < &stack[1])
+ return (0);
+ sp--;
+ *sp /= *(sp + 1);
+ break;
+ case OP_REM:
+ if (sp < &stack[1])
+ return (0);
+ sp--;
+ *sp %= *(sp + 1);
+ break;
+ case OP_OFFSET_POP:
+ if (offp < &offstack[0])
+ return (0);
+ base = *offp--;
+ if (opkt != NULL) {
+ pkt = opkt;
+ len = olen;
+ opkt = NULL;
+ }
+ break;
+ case OP_OFFSET_ZERO:
+ if (offp >= &offstack[MAXSS])
+ return (0);
+ *++offp = base;
+ base = pkt;
+ break;
+ case OP_OFFSET_LINK:
+ if (offp >= &offstack[MAXSS])
+ return (0);
+ *++offp = base;
+ base = pkt + header_size;
+ /*
+ * If the offset exceeds the packet length,
+ * we should not be interested in this packet...
+ * Just return 0.
+ */
+ if (base > pkt + len) {
+ return (0);
+ }
+ break;
+ case OP_OFFSET_IP:
+ if (offp >= &offstack[MAXSS])
+ return (0);
+ *++offp = base;
+ ip = pkt + header_size;
+ base = ip + ip_hdr_len(ip);
+ if (base == ip) {
+ return (0); /* not IP */
+ }
+ if (base > pkt + len) {
+ return (0); /* bad pkt */
+ }
+ break;
+ case OP_OFFSET_TCP:
+ if (offp >= &offstack[MAXSS])
+ return (0);
+ *++offp = base;
+ ip = pkt + header_size;
+ tcp = ip + ip_hdr_len(ip);
+ if (tcp == ip) {
+ return (0); /* not IP */
+ }
+ base = tcp + TCP_HDR_LEN(tcp);
+ if (base > pkt + len) {
+ return (0);
+ }
+ break;
+ case OP_OFFSET_UDP:
+ if (offp >= &offstack[MAXSS])
+ return (0);
+ *++offp = base;
+ ip = pkt + header_size;
+ udp = ip + ip_hdr_len(ip);
+ if (udp == ip) {
+ return (0); /* not IP */
+ }
+ base = udp + sizeof (struct udphdr);
+ if (base > pkt + len) {
+ return (0);
+ }
+ break;
+ case OP_OFFSET_RPC:
+ if (offp >= &offstack[MAXSS])
+ return (0);
+ *++offp = base;
+ ip = pkt + header_size;
+ rpc = NULL;
+
+ if (IP_VERS(ip) != IPV4_VERSION &&
+ IP_VERS(ip) != IPV6_VERSION) {
+ if (sp >= &stack[MAXSS])
+ return (0);
+ *(++sp) = 0;
+ break;
+ }
+
+ switch (ip_proto_of(ip)) {
+ case IPPROTO_UDP:
+ udp = ip + ip_hdr_len(ip);
+ rpc = (struct rpc_msg *)(udp +
+ sizeof (struct udphdr));
+ break;
+ case IPPROTO_TCP:
+ tcp = ip + ip_hdr_len(ip);
+ /*
+ * Need to skip an extra 4 for the xdr_rec
+ * field.
+ */
+ rpc = (struct rpc_msg *)(tcp +
+ TCP_HDR_LEN(tcp) + 4);
+ break;
+ }
+ /*
+ * We need to have at least 24 bytes of a RPC
+ * packet to look at to determine the validity
+ * of it.
+ */
+ if (rpc == NULL || (uchar_t *)rpc + 24 > pkt + len) {
+ if (sp >= &stack[MAXSS])
+ return (0);
+ *(++sp) = 0;
+ break;
+ }
+ /* align */
+ (void) memcpy(&rpcmsg, rpc, 24);
+ if (!valid_rpc(&rpcmsg, 24)) {
+ if (sp >= &stack[MAXSS])
+ return (0);
+ *(++sp) = 0;
+ break;
+ }
+ if (ntohl(rpcmsg.rm_direction) == CALL) {
+ base = (uchar_t *)rpc;
+ newrpc = 1;
+ if (sp >= &stack[MAXSS])
+ return (0);
+ *(++sp) = 1;
+ } else {
+ opkt = pkt;
+ olen = len;
+
+ pkt = base = (uchar_t *)find_rpc(&rpcmsg);
+ len = sizeof (struct xid_entry);
+ if (sp >= &stack[MAXSS])
+ return (0);
+ *(++sp) = base != NULL;
+ }
+ break;
+ case OP_OFFSET_SLP:
+ slphdr = NULL;
+ ip = pkt + header_size;
+
+ if (IP_VERS(ip) != IPV4_VERSION &&
+ IP_VERS(ip) != IPV6_VERSION) {
+ if (sp >= &stack[MAXSS])
+ return (0);
+ *(++sp) = 0;
+ break;
+ }
+
+ switch (ip_proto_of(ip)) {
+ struct udphdr udp_h;
+ struct tcphdr tcp_h;
+ case IPPROTO_UDP:
+ udp = ip + ip_hdr_len(ip);
+ /* align */
+ memcpy(&udp_h, udp, sizeof (udp_h));
+ slp_sport = ntohs(udp_h.uh_sport);
+ slp_dport = ntohs(udp_h.uh_dport);
+ slphdr = udp + sizeof (struct udphdr);
+ break;
+ case IPPROTO_TCP:
+ tcp = ip + ip_hdr_len(ip);
+ /* align */
+ memcpy(&tcp_h, tcp, sizeof (tcp_h));
+ slp_sport = ntohs(tcp_h.th_sport);
+ slp_dport = ntohs(tcp_h.th_dport);
+ slphdr = tcp + TCP_HDR_LEN(tcp);
+ break;
+ }
+ if (slphdr == NULL || slphdr > pkt + len) {
+ if (sp >= &stack[MAXSS])
+ return (0);
+ *(++sp) = 0;
+ break;
+ }
+ if (slp_sport == 427 || slp_dport == 427) {
+ if (sp >= &stack[MAXSS])
+ return (0);
+ *(++sp) = 1;
+ if (slp_sport != 427 && slp_dport == 427)
+ stash_slp(slp_sport);
+ break;
+ } else if (find_slp(slp_dport) != -1) {
+ if (valid_slp(slphdr, len)) {
+ if (sp >= &stack[MAXSS])
+ return (0);
+ *(++sp) = 1;
+ break;
+ }
+ /* else fallthrough to reject */
+ }
+ if (sp >= &stack[MAXSS])
+ return (0);
+ *(++sp) = 0;
+ break;
+ }
+ }
+
+ if (*sp && newrpc)
+ stash_rpc(&rpcmsg);
+
+ return (*sp);
+}
+
+static void
+load_const(uint_t constval)
+{
+ emitop(OP_LOAD_CONST);
+ emitval(constval);
+}
+
+static void
+load_value(int offset, int len)
+{
+ if (offset >= 0)
+ load_const(offset);
+
+ switch (len) {
+ case 1:
+ emitop(OP_LOAD_OCTET);
+ break;
+ case 2:
+ emitop(OP_LOAD_SHORT);
+ break;
+ case 4:
+ emitop(OP_LOAD_LONG);
+ break;
+ }
+}
+
+/*
+ * Emit code to compare a field in
+ * the packet against a constant value.
+ */
+static void
+compare_value(uint_t offset, uint_t len, uint_t val)
+{
+ load_const(val);
+ load_value(offset, len);
+ emitop(OP_EQ);
+}
+
+static void
+compare_addr_v4(uint_t offset, uint_t len, uint_t val)
+{
+ load_const(ntohl(val));
+ load_value(offset, len);
+ emitop(OP_EQ);
+}
+
+static void
+compare_addr_v6(uint_t offset, uint_t len, struct in6_addr val)
+{
+ int i;
+ uint32_t value;
+
+ for (i = 0; i < len; i += 4) {
+ value = ntohl(*(uint32_t *)&val.s6_addr[i]);
+ load_const(value);
+ load_value(offset + i, 4);
+ emitop(OP_EQ);
+ if (i != 0)
+ emitop(OP_AND);
+ }
+}
+
+/*
+ * Same as above except do the comparison
+ * after and'ing a mask value. Useful
+ * for comparing IP network numbers
+ */
+static void
+compare_value_mask(uint_t offset, uint_t len, uint_t val, int mask)
+{
+ load_value(offset, len);
+ load_const(mask);
+ emitop(OP_AND);
+ load_const(val);
+ emitop(OP_EQ);
+}
+
+/* Emit an operator into the code array */
+static void
+emitop(enum optype opcode)
+{
+ if (curr_op >= &oplist[MAXOPS])
+ pr_err("expression too long");
+ *curr_op++ = opcode;
+}
+
+/*
+ * Remove n operators recently emitted into
+ * the code array. Used by alternation().
+ */
+static void
+unemit(int numops)
+{
+ curr_op -= numops;
+}
+
+
+/*
+ * Same as emitop except that we're emitting
+ * a value that's not an operator.
+ */
+static void
+emitval(uint_t val)
+{
+ if (curr_op >= &oplist[MAXOPS])
+ pr_err("expression too long");
+ *curr_op++ = val;
+}
+
+/*
+ * Used to chain forward branches together
+ * for later resolution by resolve_chain().
+ */
+static uint_t
+chain(int p)
+{
+ uint_t pos = curr_op - oplist;
+
+ emitval(p);
+ return (pos);
+}
+
+/*
+ * Proceed backward through the code array
+ * following a chain of forward references.
+ * At each reference install the destination
+ * branch offset.
+ */
+static void
+resolve_chain(uint_t p)
+{
+ uint_t n;
+ uint_t pos = curr_op - oplist;
+
+ while (p) {
+ n = oplist[p];
+ oplist[p] = pos;
+ p = n;
+ }
+}
+
+#define EQ(val) (strcmp(token, val) == 0)
+
+char *tkp, *sav_tkp;
+char *token;
+enum { EOL, ALPHA, NUMBER, FIELD, ADDR_IP, ADDR_ETHER, SPECIAL,
+ ADDR_IP6, ADDR_AT } tokentype;
+uint_t tokenval;
+
+/*
+ * This is the scanner. Each call returns the next
+ * token in the filter expression. A token is either:
+ * EOL: The end of the line - no more tokens.
+ * ALPHA: A name that begins with a letter and contains
+ * letters or digits, hyphens or underscores.
+ * NUMBER: A number. The value can be represented as
+ * a decimal value (1234) or an octal value
+ * that begins with zero (066) or a hex value
+ * that begins with 0x or 0X (0xff).
+ * FIELD: A name followed by a left square bracket.
+ * ADDR_IP: An IP address. Any sequence of digits
+ * separated by dots e.g. 109.104.40.13
+ * ADDR_ETHER: An ethernet address. Any sequence of hex
+ * digits separated by colons e.g. 8:0:20:0:76:39
+ * SPECIAL: A special character e.g. ">" or "(". The scanner
+ * correctly handles digraphs - two special characters
+ * that constitute a single token e.g. "==" or ">=".
+ * ADDR_IP6: An IPv6 address.
+ *
+ * ADDR_AT: An AppleTalk Phase II address. A sequence of two numbers
+ * separated by a dot.
+ *
+ * The current token is maintained in "token" and and its
+ * type in "tokentype". If tokentype is NUMBER then the
+ * value is held in "tokenval".
+ */
+
+static const char *namechars =
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-.";
+static const char *numchars = "0123456789abcdefABCDEFXx:.";
+
+void
+next()
+{
+ static int savechar;
+ char *p;
+ int size, size1;
+ int base, colons, dots, alphas, double_colon;
+
+ colons = 0;
+ double_colon = 0;
+
+ if (*tkp == '\0') {
+ token = tkp;
+ *tkp = savechar;
+ }
+
+ sav_tkp = tkp;
+
+ while (isspace(*tkp)) tkp++;
+ token = tkp;
+ if (*token == '\0') {
+ tokentype = EOL;
+ return;
+ }
+
+ /* A token containing ':' cannot be ALPHA type */
+ tkp = token + strspn(token, numchars);
+ for (p = token; p < tkp; p++) {
+ if (*p == ':') {
+ colons++;
+ if (*(p+1) == ':')
+ double_colon++;
+ }
+ }
+
+ tkp = token;
+ if (isalpha(*tkp) && !colons) {
+ tokentype = ALPHA;
+ tkp += strspn(tkp, namechars);
+ if (*tkp == '[') {
+ tokentype = FIELD;
+ *tkp++ = '\0';
+ }
+ } else
+
+ /*
+ * RFC1123 states that host names may now start with digits. Need
+ * to change parser to account for this. Also, need to distinguish
+ * between 1.2.3.4 and 1.2.3.a where the first case is an IP address
+ * and the second is a domain name. 333aaa needs to be distinguished
+ * from 0x333aaa. The first is a host name and the second is a number.
+ *
+ * The (colons > 1) conditional differentiates between ethernet
+ * and IPv6 addresses, and an expression of the form base[expr:size],
+ * which can only contain one ':' character.
+ */
+ if (isdigit(*tkp) || colons > 1) {
+ tkp = token + strspn(token, numchars);
+ dots = alphas = 0;
+ for (p = token; p < tkp; p++) {
+ if (*p == '.')
+ dots++;
+ else if (isalpha(*p))
+ alphas = 1;
+ }
+ if (colons > 1) {
+ if (colons == 5 && double_colon == 0) {
+ tokentype = ADDR_ETHER;
+ } else {
+ tokentype = ADDR_IP6;
+ }
+ } else if (dots) {
+ size = tkp - token;
+ size1 = strspn(token, "0123456789.");
+ if (dots == 1 && size == size1) {
+ tokentype = ADDR_AT;
+ } else
+ if (dots != 3 || size != size1) {
+ tokentype = ALPHA;
+ if (*tkp != '\0' && !isspace(*tkp)) {
+ tkp += strspn(tkp, namechars);
+ if (*tkp == '[') {
+ tokentype = FIELD;
+ *tkp++ = '\0';
+ }
+ }
+ } else
+ tokentype = ADDR_IP;
+ } else if (token + strspn(token, namechars) <= tkp) {
+ /*
+ * With the above check, if there are more
+ * characters after the last digit, assume
+ * that it is not a number.
+ */
+ tokentype = NUMBER;
+ p = tkp;
+ tkp = token;
+ base = 10;
+ if (*tkp == '0') {
+ base = 8;
+ tkp++;
+ if (*tkp == 'x' || *tkp == 'X')
+ base = 16;
+ }
+ if ((base == 10 || base == 8) && alphas) {
+ tokentype = ALPHA;
+ tkp = p;
+ } else if (base == 16) {
+ size = 2 + strspn(token+2,
+ "0123456789abcdefABCDEF");
+ size1 = p - token;
+ if (size != size1) {
+ tokentype = ALPHA;
+ tkp = p;
+ } else
+ /*
+ * handles the case of 0x so an error message
+ * is not printed. Treats 0x as 0.
+ */
+ if (size == 2) {
+ tokenval = 0;
+ tkp = token +2;
+ } else {
+ tokenval = strtoul(token, &tkp, base);
+ }
+ } else {
+ tokenval = strtoul(token, &tkp, base);
+ }
+ } else {
+ tokentype = ALPHA;
+ tkp += strspn(tkp, namechars);
+ if (*tkp == '[') {
+ tokentype = FIELD;
+ *tkp++ = '\0';
+ }
+ }
+ } else {
+ tokentype = SPECIAL;
+ tkp++;
+ if ((*token == '=' && *tkp == '=') ||
+ (*token == '>' && *tkp == '=') ||
+ (*token == '<' && *tkp == '=') ||
+ (*token == '!' && *tkp == '='))
+ tkp++;
+ }
+
+ savechar = *tkp;
+ *tkp = '\0';
+}
+
+static struct match_type {
+ char *m_name;
+ int m_offset;
+ int m_size;
+ int m_value;
+ int m_depend;
+ enum optype m_optype;
+} match_types[] = {
+ /*
+ * Table initialized assuming Ethernet data link headers.
+ */
+ "ip", 12, 2, ETHERTYPE_IP, -1, OP_OFFSET_ZERO,
+ "ip6", 12, 2, ETHERTYPE_IPV6, -1, OP_OFFSET_ZERO,
+ "arp", 12, 2, ETHERTYPE_ARP, -1, OP_OFFSET_ZERO,
+ "rarp", 12, 2, ETHERTYPE_REVARP, -1, OP_OFFSET_ZERO,
+ "pppoed", 12, 2, ETHERTYPE_PPPOED, -1, OP_OFFSET_ZERO,
+ "pppoes", 12, 2, ETHERTYPE_PPPOES, -1, OP_OFFSET_ZERO,
+ "tcp", 9, 1, IPPROTO_TCP, 0, OP_OFFSET_LINK,
+ "tcp", 6, 1, IPPROTO_TCP, 1, OP_OFFSET_LINK,
+ "udp", 9, 1, IPPROTO_UDP, 0, OP_OFFSET_LINK,
+ "udp", 6, 1, IPPROTO_UDP, 1, OP_OFFSET_LINK,
+ "icmp", 9, 1, IPPROTO_ICMP, 0, OP_OFFSET_LINK,
+ "icmp6", 6, 1, IPPROTO_ICMPV6, 1, OP_OFFSET_LINK,
+ "ip-in-ip", 9, 1, IPPROTO_ENCAP, 0, OP_OFFSET_LINK,
+ "esp", 9, 1, IPPROTO_ESP, 0, OP_OFFSET_LINK,
+ "esp", 6, 1, IPPROTO_ESP, 1, OP_OFFSET_LINK,
+ "ah", 9, 1, IPPROTO_AH, 0, OP_OFFSET_LINK,
+ "ah", 6, 1, IPPROTO_AH, 1, OP_OFFSET_LINK,
+ "sctp", 9, 1, IPPROTO_SCTP, 0, OP_OFFSET_LINK,
+ "sctp", 6, 1, IPPROTO_SCTP, 1, OP_OFFSET_LINK,
+ 0, 0, 0, 0, 0, 0
+};
+
+static void
+generate_check(struct match_type *mtp)
+{
+ int offset;
+
+ /*
+ * Note: this code assumes the above dependencies are
+ * not cyclic. This *should* always be true.
+ */
+ if (mtp->m_depend != -1)
+ generate_check(&match_types[mtp->m_depend]);
+
+ offset = mtp->m_offset;
+ if (mtp->m_optype == OP_OFFSET_ZERO) {
+
+ /*
+ * The table is filled with ethernet offsets. Here we
+ * fudge the value based on what know about the
+ * interface. It is okay to do this because we are
+ * checking what we believe to be an IP/ARP/RARP
+ * packet, and we know those are carried in LLC-SNAP
+ * headers on FDDI. We assume that it's unlikely
+ * another kind of packet, with a shorter FDDI header
+ * will happen to match the filter.
+ *
+ * Ether FDDI IPoIB
+ * edst addr 0 1 none
+ * esrc addr 6 7 none
+ * ethertype 12 19 0
+ *
+ * XXX token ring?
+ */
+ if (interface->mac_type == DL_FDDI) {
+ if (offset < 12)
+ offset++;
+ else if (offset == 12)
+ offset = 19;
+ } else if (interface->mac_type == DL_IB) {
+ offset = 0;
+ }
+ }
+
+ if (mtp->m_optype != OP_OFFSET_ZERO) {
+ emitop(mtp->m_optype);
+ load_value(offset, mtp->m_size);
+ load_const(mtp->m_value);
+ emitop(OP_OFFSET_POP);
+ } else {
+ load_value(offset, mtp->m_size);
+ load_const(mtp->m_value);
+ }
+
+ emitop(OP_EQ);
+
+ if (mtp->m_depend != -1)
+ emitop(OP_AND);
+}
+
+/*
+ * Generate code based on the keyword argument.
+ * This word is looked up in the match_types table
+ * and checks a field within the packet for a given
+ * value e.g. ether or ip type field. The match
+ * can also have a dependency on another entry e.g.
+ * "tcp" requires that the packet also be "ip".
+ */
+static int
+comparison(char *s)
+{
+ unsigned int i, n_checks = 0;
+
+ for (i = 0; match_types[i].m_name != NULL; i++) {
+
+ if (strcmp(s, match_types[i].m_name) != 0)
+ continue;
+
+ n_checks++;
+ generate_check(&match_types[i]);
+ if (n_checks > 1)
+ emitop(OP_OR);
+ }
+
+ return (n_checks > 0);
+}
+
+enum direction { ANY, TO, FROM };
+enum direction dir;
+
+/*
+ * Generate code to match an IP address. The address
+ * may be supplied either as a hostname or in dotted format.
+ * For source packets both the IP source address and ARP
+ * src are checked.
+ * Note: we don't check packet type here - whether IP or ARP.
+ * It's possible that we'll do an improper match.
+ */
+static void
+ipaddr_match(enum direction which, char *hostname, int inet_type)
+{
+ bool_t found_host;
+ int m = 0, n = 0;
+ uint_t *addr4ptr;
+ uint_t addr4;
+ struct in6_addr *addr6ptr;
+ int h_addr_index;
+ struct hostent *hp = NULL;
+ int error_num = 0;
+ boolean_t freehp = B_FALSE;
+ boolean_t first = B_TRUE;
+
+ /*
+ * The addr4offset and addr6offset variables simplify the code which
+ * generates the address comparison filter. With these two variables,
+ * duplicate code need not exist for the TO and FROM case.
+ * A value of -1 describes the ANY case (TO and FROM).
+ */
+ int addr4offset;
+ int addr6offset;
+
+ found_host = 0;
+
+ if (tokentype == ADDR_IP) {
+ hp = lgetipnodebyname(hostname, AF_INET,
+ 0, &error_num);
+ if (hp == NULL) {
+ hp = getipnodebyname(hostname, AF_INET,
+ 0, &error_num);
+ freehp = 1;
+ }
+ if (hp == NULL) {
+ if (error_num == TRY_AGAIN) {
+ pr_err("couldn't resolve %s (try again later)",
+ hostname);
+ } else {
+ pr_err("couldn't resolve %s", hostname);
+ }
+ }
+ inet_type = IPV4_ONLY;
+ } else if (tokentype == ADDR_IP6) {
+ hp = lgetipnodebyname(hostname, AF_INET6,
+ 0, &error_num);
+ if (hp == NULL) {
+ hp = getipnodebyname(hostname, AF_INET6,
+ 0, &error_num);
+ freehp = 1;
+ }
+ if (hp == NULL) {
+ if (error_num == TRY_AGAIN) {
+ pr_err("couldn't resolve %s (try again later)",
+ hostname);
+ } else {
+ pr_err("couldn't resolve %s", hostname);
+ }
+ }
+ inet_type = IPV6_ONLY;
+ } else {
+ /* Some hostname i.e. tokentype is ALPHA */
+ switch (inet_type) {
+ case IPV4_ONLY:
+ /* Only IPv4 address is needed */
+ hp = lgetipnodebyname(hostname, AF_INET,
+ 0, &error_num);
+ if (hp == NULL) {
+ hp = getipnodebyname(hostname, AF_INET,
+ 0, &error_num);
+ freehp = 1;
+ }
+ if (hp != NULL) {
+ found_host = 1;
+ }
+ break;
+ case IPV6_ONLY:
+ /* Only IPv6 address is needed */
+ hp = lgetipnodebyname(hostname, AF_INET6,
+ 0, &error_num);
+ if (hp == NULL) {
+ hp = getipnodebyname(hostname, AF_INET6,
+ 0, &error_num);
+ freehp = 1;
+ }
+ if (hp != NULL) {
+ found_host = 1;
+ }
+ break;
+ case IPV4_AND_IPV6:
+ /* Both IPv4 and IPv6 are needed */
+ hp = lgetipnodebyname(hostname, AF_INET6,
+ AI_ALL | AI_V4MAPPED, &error_num);
+ if (hp == NULL) {
+ hp = getipnodebyname(hostname, AF_INET6,
+ AI_ALL | AI_V4MAPPED, &error_num);
+ freehp = 1;
+ }
+ if (hp != NULL) {
+ found_host = 1;
+ }
+ break;
+ default:
+ found_host = 0;
+ }
+
+ if (!found_host) {
+ if (error_num == TRY_AGAIN) {
+ pr_err("could not resolve %s (try again later)",
+ hostname);
+ } else {
+ pr_err("could not resolve %s", hostname);
+ }
+ }
+ }
+
+ switch (which) {
+ case TO:
+ addr4offset = IPV4_DSTADDR_OFFSET;
+ addr6offset = IPV6_DSTADDR_OFFSET;
+ break;
+ case FROM:
+ addr4offset = IPV4_SRCADDR_OFFSET;
+ addr6offset = IPV6_SRCADDR_OFFSET;
+ break;
+ case ANY:
+ addr4offset = -1;
+ addr6offset = -1;
+ break;
+ }
+
+ /*
+ * The code below generates the filter.
+ */
+ if (hp != NULL && hp->h_addrtype == AF_INET) {
+ ethertype_match(ETHERTYPE_IP);
+ emitop(OP_BRFL);
+ n = chain(n);
+ emitop(OP_OFFSET_LINK);
+ h_addr_index = 0;
+ addr4ptr = (uint_t *)hp->h_addr_list[h_addr_index];
+ while (addr4ptr != NULL) {
+ if (addr4offset == -1) {
+ compare_addr_v4(IPV4_SRCADDR_OFFSET, 4,
+ *addr4ptr);
+ emitop(OP_BRTR);
+ m = chain(m);
+ compare_addr_v4(IPV4_DSTADDR_OFFSET, 4,
+ *addr4ptr);
+ } else {
+ compare_addr_v4(addr4offset, 4, *addr4ptr);
+ }
+ addr4ptr = (uint_t *)hp->h_addr_list[++h_addr_index];
+ if (addr4ptr != NULL) {
+ emitop(OP_BRTR);
+ m = chain(m);
+ }
+ }
+ if (m != 0) {
+ resolve_chain(m);
+ }
+ emitop(OP_OFFSET_POP);
+ resolve_chain(n);
+ } else {
+ /* first pass: IPv4 addresses */
+ h_addr_index = 0;
+ addr6ptr = (struct in6_addr *)hp->h_addr_list[h_addr_index];
+ first = B_TRUE;
+ while (addr6ptr != NULL) {
+ if (IN6_IS_ADDR_V4MAPPED(addr6ptr)) {
+ if (first) {
+ ethertype_match(ETHERTYPE_IP);
+ emitop(OP_BRFL);
+ n = chain(n);
+ emitop(OP_OFFSET_LINK);
+ first = B_FALSE;
+ } else {
+ emitop(OP_BRTR);
+ m = chain(m);
+ }
+ IN6_V4MAPPED_TO_INADDR(addr6ptr,
+ (struct in_addr *)&addr4);
+ if (addr4offset == -1) {
+ compare_addr_v4(IPV4_SRCADDR_OFFSET, 4,
+ addr4);
+ emitop(OP_BRTR);
+ m = chain(m);
+ compare_addr_v4(IPV4_DSTADDR_OFFSET, 4,
+ addr4);
+ } else {
+ compare_addr_v4(addr4offset, 4, addr4);
+ }
+ }
+ addr6ptr = (struct in6_addr *)
+ hp->h_addr_list[++h_addr_index];
+ }
+ /* second pass: IPv6 addresses */
+ h_addr_index = 0;
+ addr6ptr = (struct in6_addr *)hp->h_addr_list[h_addr_index];
+ first = B_TRUE;
+ while (addr6ptr != NULL) {
+ if (!IN6_IS_ADDR_V4MAPPED(addr6ptr)) {
+ if (first) {
+ /*
+ * bypass check for IPv6 addresses
+ * when we have an IPv4 packet
+ */
+ if (n != 0) {
+ emitop(OP_BRTR);
+ m = chain(m);
+ emitop(OP_BRFL);
+ m = chain(m);
+ resolve_chain(n);
+ n = 0;
+ }
+ ethertype_match(ETHERTYPE_IPV6);
+ emitop(OP_BRFL);
+ n = chain(n);
+ emitop(OP_OFFSET_LINK);
+ first = B_FALSE;
+ } else {
+ emitop(OP_BRTR);
+ m = chain(m);
+ }
+ if (addr6offset == -1) {
+ compare_addr_v6(IPV6_SRCADDR_OFFSET,
+ 16, *addr6ptr);
+ emitop(OP_BRTR);
+ m = chain(m);
+ compare_addr_v6(IPV6_DSTADDR_OFFSET,
+ 16, *addr6ptr);
+ } else {
+ compare_addr_v6(addr6offset, 16,
+ *addr6ptr);
+ }
+ }
+ addr6ptr = (struct in6_addr *)
+ hp->h_addr_list[++h_addr_index];
+ }
+ if (m != 0) {
+ resolve_chain(m);
+ }
+ emitop(OP_OFFSET_POP);
+ resolve_chain(n);
+ }
+
+ /* only free struct hostent returned by getipnodebyname() */
+ if (freehp) {
+ freehostent(hp);
+ }
+}
+
+/*
+ * Generate code to match an AppleTalk address. The address
+ * must be given as two numbers with a dot between
+ *
+ */
+static void
+ataddr_match(enum direction which, char *hostname)
+{
+ uint_t net;
+ uint_t node;
+ uint_t m, n;
+
+ sscanf(hostname, "%u.%u", &net, &node);
+
+ emitop(OP_OFFSET_LINK);
+ switch (which) {
+ case TO:
+ compare_value(AT_DST_NET_OFFSET, 2, net);
+ emitop(OP_BRFL);
+ m = chain(0);
+ compare_value(AT_DST_NODE_OFFSET, 1, node);
+ resolve_chain(m);
+ break;
+ case FROM:
+ compare_value(AT_SRC_NET_OFFSET, 2, net);
+ emitop(OP_BRFL);
+ m = chain(0);
+ compare_value(AT_SRC_NODE_OFFSET, 1, node);
+ resolve_chain(m);
+ break;
+ case ANY:
+ compare_value(AT_DST_NET_OFFSET, 2, net);
+ emitop(OP_BRFL);
+ m = chain(0);
+ compare_value(AT_DST_NODE_OFFSET, 1, node);
+ resolve_chain(m);
+ emitop(OP_BRTR);
+ n = chain(0);
+ compare_value(AT_SRC_NET_OFFSET, 2, net);
+ emitop(OP_BRFL);
+ m = chain(0);
+ compare_value(AT_SRC_NODE_OFFSET, 1, node);
+ resolve_chain(m);
+ resolve_chain(n);
+ break;
+ }
+ emitop(OP_OFFSET_POP);
+}
+
+/*
+ * Compare ethernet addresses. The address may
+ * be provided either as a hostname or as a
+ * 6 octet colon-separated address.
+ */
+static void
+etheraddr_match(enum direction which, char *hostname)
+{
+ uint_t addr;
+ ushort_t *addrp;
+ int to_offset, from_offset;
+ struct ether_addr e, *ep = NULL;
+ int m;
+
+ /*
+ * First, check the interface type for whether src/dest address
+ * is determinable; if not, retreat early.
+ */
+ switch (interface->mac_type) {
+ case DL_ETHER:
+ from_offset = ETHERADDRL;
+ to_offset = 0;
+ break;
+
+ case DL_IB:
+ /*
+ * If an ethernet address is attempted to be used
+ * on an IPoIB interface, flag error. Link address
+ * based filtering is unsupported on IPoIB, so there
+ * is no ipibaddr_match() or parsing support for IPoIB
+ * 20 byte link addresses.
+ */
+ pr_err("filter option unsupported on media");
+ break;
+
+ case DL_FDDI:
+ from_offset = 7;
+ to_offset = 1;
+ break;
+
+ default:
+ /*
+ * Where do we find "ether" address for FDDI & TR?
+ * XXX can improve? ~sparker
+ */
+ load_const(1);
+ return;
+ }
+
+ if (isxdigit(*hostname))
+ ep = ether_aton(hostname);
+ if (ep == NULL) {
+ if (ether_hostton(hostname, &e))
+ if (!arp_for_ether(hostname, &e))
+ pr_err("cannot obtain ether addr for %s",
+ hostname);
+ ep = &e;
+ }
+ memcpy(&addr, (ushort_t *)ep, 4);
+ addrp = (ushort_t *)ep + 2;
+
+ switch (which) {
+ case TO:
+ compare_value(to_offset, 4, ntohl(addr));
+ emitop(OP_BRFL);
+ m = chain(0);
+ compare_value(to_offset + 4, 2, ntohs(*addrp));
+ resolve_chain(m);
+ break;
+ case FROM:
+ compare_value(from_offset, 4, ntohl(addr));
+ emitop(OP_BRFL);
+ m = chain(0);
+ compare_value(from_offset + 4, 2, ntohs(*addrp));
+ resolve_chain(m);
+ break;
+ case ANY:
+ compare_value(to_offset, 4, ntohl(addr));
+ compare_value(to_offset + 4, 2, ntohs(*addrp));
+ emitop(OP_AND);
+ emitop(OP_BRTR);
+ m = chain(0);
+
+ compare_value(from_offset, 4, ntohl(addr));
+ compare_value(from_offset + 4, 2, ntohs(*addrp));
+ emitop(OP_AND);
+ resolve_chain(m);
+ break;
+ }
+}
+
+static void
+ethertype_match(int val)
+{
+ int m;
+ int ether_offset;
+
+ switch (interface->mac_type) {
+ case DL_ETHER:
+ ether_offset = 12;
+ break;
+
+ case DL_IB:
+ ether_offset = 0;
+ break;
+
+ case DL_FDDI:
+ /* XXX Okay to assume LLC SNAP? */
+ ether_offset = 19;
+ break;
+
+ default:
+ load_const(1); /* Assume a match */
+ return;
+ }
+ compare_value(ether_offset, 2, val); /* XXX.sparker */
+}
+
+/*
+ * Match a network address. The host part
+ * is masked out. The network address may
+ * be supplied either as a netname or in
+ * IP dotted format. The mask to be used
+ * for the comparison is assumed from the
+ * address format (see comment below).
+ */
+static void
+netaddr_match(enum direction which, char *netname)
+{
+ uint_t addr;
+ uint_t mask = 0xff000000;
+ uint_t m;
+ struct netent *np;
+
+ if (isdigit(*netname)) {
+ addr = inet_network(netname);
+ } else {
+ np = getnetbyname(netname);
+ if (np == NULL)
+ pr_err("net %s not known", netname);
+ addr = np->n_net;
+ }
+ addr = ntohl(addr);
+
+ /*
+ * Left justify the address and figure
+ * out a mask based on the supplied address.
+ * Set the mask according to the number of zero
+ * low-order bytes.
+ * Note: this works only for whole octet masks.
+ */
+ if (addr) {
+ while ((addr & ~mask) != 0) {
+ mask |= (mask >> 8);
+ }
+ }
+
+ emitop(OP_OFFSET_LINK);
+ switch (which) {
+ case TO:
+ compare_value_mask(16, 4, addr, mask);
+ break;
+ case FROM:
+ compare_value_mask(12, 4, addr, mask);
+ break;
+ case ANY:
+ compare_value_mask(12, 4, addr, mask);
+ emitop(OP_BRTR);
+ m = chain(0);
+ compare_value_mask(16, 4, addr, mask);
+ resolve_chain(m);
+ break;
+ }
+ emitop(OP_OFFSET_POP);
+}
+
+/*
+ * Match either a UDP or TCP port number.
+ * The port number may be provided either as
+ * port name as listed in /etc/services ("nntp") or as
+ * the port number itself (2049).
+ */
+static void
+port_match(enum direction which, char *portname)
+{
+ struct servent *sp;
+ uint_t m, port;
+
+ if (isdigit(*portname)) {
+ port = atoi(portname);
+ } else {
+ sp = getservbyname(portname, NULL);
+ if (sp == NULL)
+ pr_err("invalid port number or name: %s",
+ portname);
+ port = ntohs(sp->s_port);
+ }
+
+ emitop(OP_OFFSET_IP);
+
+ switch (which) {
+ case TO:
+ compare_value(2, 2, port);
+ break;
+ case FROM:
+ compare_value(0, 2, port);
+ break;
+ case ANY:
+ compare_value(2, 2, port);
+ emitop(OP_BRTR);
+ m = chain(0);
+ compare_value(0, 2, port);
+ resolve_chain(m);
+ break;
+ }
+ emitop(OP_OFFSET_POP);
+}
+
+/*
+ * Generate code to match packets with a specific
+ * RPC program number. If the progname is a name
+ * it is converted to a number via /etc/rpc.
+ * The program version and/or procedure may be provided
+ * as extra qualifiers.
+ */
+static void
+rpc_match_prog(enum direction which, char *progname, int vers, int proc)
+{
+ struct rpcent *rpc;
+ uint_t prog;
+ uint_t m, n;
+
+ if (isdigit(*progname)) {
+ prog = atoi(progname);
+ } else {
+ rpc = (struct rpcent *)getrpcbyname(progname);
+ if (rpc == NULL)
+ pr_err("invalid program name: %s", progname);
+ prog = rpc->r_number;
+ }
+
+ emitop(OP_OFFSET_RPC);
+ emitop(OP_BRFL);
+ n = chain(0);
+
+ compare_value(12, 4, prog);
+ emitop(OP_BRFL);
+ m = chain(0);
+ if (vers >= 0) {
+ compare_value(16, 4, vers);
+ emitop(OP_BRFL);
+ m = chain(m);
+ }
+ if (proc >= 0) {
+ compare_value(20, 4, proc);
+ emitop(OP_BRFL);
+ m = chain(m);
+ }
+
+ switch (which) {
+ case TO:
+ compare_value(4, 4, CALL);
+ emitop(OP_BRFL);
+ m = chain(m);
+ break;
+ case FROM:
+ compare_value(4, 4, REPLY);
+ emitop(OP_BRFL);
+ m = chain(m);
+ break;
+ }
+ resolve_chain(m);
+ resolve_chain(n);
+ emitop(OP_OFFSET_POP);
+}
+
+/*
+ * Generate code to parse a field specification
+ * and load the value of the field from the packet
+ * onto the operand stack.
+ * The field offset may be specified relative to the
+ * beginning of the ether header, IP header, UDP header,
+ * or TCP header. An optional size specification may
+ * be provided following a colon. If no size is given
+ * one byte is assumed e.g.
+ *
+ * ether[0] The first byte of the ether header
+ * ip[2:2] The second 16 bit field of the IP header
+ */
+static void
+load_field()
+{
+ int size = 1;
+ int s;
+
+
+ if (EQ("ether"))
+ emitop(OP_OFFSET_ZERO);
+ else if (EQ("ip") || EQ("ip6") || EQ("pppoed") || EQ("pppoes"))
+ emitop(OP_OFFSET_LINK);
+ else if (EQ("udp") || EQ("tcp") || EQ("icmp") || EQ("ip-in-ip") ||
+ EQ("ah") || EQ("esp"))
+ emitop(OP_OFFSET_IP);
+ else
+ pr_err("invalid field type");
+ next();
+ s = opstack;
+ expression();
+ if (opstack != s + 1)
+ pr_err("invalid field offset");
+ opstack--;
+ if (*token == ':') {
+ next();
+ if (tokentype != NUMBER)
+ pr_err("field size expected");
+ size = tokenval;
+ if (size != 1 && size != 2 && size != 4)
+ pr_err("field size invalid");
+ next();
+ }
+ if (*token != ']')
+ pr_err("right bracket expected");
+
+ load_value(-1, size);
+ emitop(OP_OFFSET_POP);
+}
+
+/*
+ * Check that the operand stack
+ * contains n arguments
+ */
+static void
+checkstack(int numargs)
+{
+ if (opstack != numargs)
+ pr_err("invalid expression at \"%s\".", token);
+}
+
+static void
+primary()
+{
+ int m, s;
+
+ for (;;) {
+ if (tokentype == FIELD) {
+ load_field();
+ opstack++;
+ next();
+ break;
+ }
+
+ if (comparison(token)) {
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("not") || EQ("!")) {
+ next();
+ s = opstack;
+ primary();
+ checkstack(s + 1);
+ emitop(OP_NOT);
+ break;
+ }
+
+ if (EQ("(")) {
+ next();
+ s = opstack;
+ expression();
+ checkstack(s + 1);
+ if (!EQ(")"))
+ pr_err("right paren expected");
+ next();
+ }
+
+ if (EQ("to") || EQ("dst")) {
+ dir = TO;
+ next();
+ continue;
+ }
+
+ if (EQ("from") || EQ("src")) {
+ dir = FROM;
+ next();
+ continue;
+ }
+
+ if (EQ("ether")) {
+ eaddr = 1;
+ next();
+ continue;
+ }
+
+ if (EQ("proto")) { /* ignore */
+ next();
+ continue;
+ }
+
+ if (EQ("broadcast")) {
+ /*
+ * Be tricky: FDDI ether dst address begins at
+ * byte one. Since the address is really six
+ * bytes long, this works for FDDI & ethernet.
+ * XXX - Token ring?
+ */
+ if (interface->mac_type == DL_IB)
+ pr_err("filter option unsupported on media");
+ compare_value(1, 4, 0xffffffff);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("multicast")) {
+ /* XXX Token ring? */
+ if (interface->mac_type == DL_FDDI) {
+ compare_value_mask(1, 1, 0x01, 0x01);
+ } else if (interface->mac_type == DL_IB) {
+ pr_err("filter option unsupported on media");
+ } else {
+ compare_value_mask(0, 1, 0x01, 0x01);
+ }
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("decnet")) {
+ /* XXX Token ring? */
+ if (interface->mac_type == DL_FDDI) {
+ load_value(19, 2); /* ether type */
+ load_const(0x6000);
+ emitop(OP_GE);
+ emitop(OP_BRFL);
+ m = chain(0);
+ load_value(19, 2); /* ether type */
+ load_const(0x6009);
+ emitop(OP_LE);
+ resolve_chain(m);
+ } else {
+ load_value(12, 2); /* ether type */
+ load_const(0x6000);
+ emitop(OP_GE);
+ emitop(OP_BRFL);
+ m = chain(0);
+ load_value(12, 2); /* ether type */
+ load_const(0x6009);
+ emitop(OP_LE);
+ resolve_chain(m);
+ }
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("apple")) {
+ /*
+ * Appletalk also appears in 802.2
+ * packets, so check for the ethertypes
+ * at offset 12 and 20 in the MAC header.
+ */
+ ethertype_match(ETHERTYPE_AT);
+ emitop(OP_BRTR);
+ m = chain(0);
+ ethertype_match(ETHERTYPE_AARP);
+ emitop(OP_BRTR);
+ m = chain(m);
+ compare_value(20, 2, ETHERTYPE_AT); /* 802.2 */
+ emitop(OP_BRTR);
+ m = chain(m);
+ compare_value(20, 2, ETHERTYPE_AARP); /* 802.2 */
+ resolve_chain(m);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("bootp") || EQ("dhcp")) {
+ emitop(OP_OFFSET_LINK);
+ emitop(OP_LOAD_CONST);
+ emitval(9);
+ emitop(OP_LOAD_OCTET);
+ emitop(OP_LOAD_CONST);
+ emitval(IPPROTO_UDP);
+ emitop(OP_OFFSET_IP);
+ compare_value(0, 4,
+ (IPPORT_BOOTPS << 16 | IPPORT_BOOTPC));
+ emitop(OP_BRTR);
+ m = chain(0);
+ compare_value(0, 4,
+ (IPPORT_BOOTPC << 16 | IPPORT_BOOTPS));
+ resolve_chain(m);
+ opstack++;
+ dir = ANY;
+ next();
+ break;
+ }
+
+ if (EQ("ethertype")) {
+ next();
+ if (tokentype != NUMBER)
+ pr_err("ether type expected");
+ ethertype_match(tokenval);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("pppoe")) {
+ ethertype_match(ETHERTYPE_PPPOED);
+ ethertype_match(ETHERTYPE_PPPOES);
+ emitop(OP_OR);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("inet")) {
+ next();
+ if (EQ("host"))
+ next();
+ if (tokentype != ALPHA && tokentype != ADDR_IP)
+ pr_err("host/IPv4 addr expected after inet");
+ ipaddr_match(dir, token, IPV4_ONLY);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("inet6")) {
+ next();
+ if (EQ("host"))
+ next();
+ if (tokentype != ALPHA && tokentype != ADDR_IP6)
+ pr_err("host/IPv6 addr expected after inet6");
+ ipaddr_match(dir, token, IPV6_ONLY);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("length")) {
+ emitop(OP_LOAD_LENGTH);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("less")) {
+ next();
+ if (tokentype != NUMBER)
+ pr_err("packet length expected");
+ emitop(OP_LOAD_LENGTH);
+ load_const(tokenval);
+ emitop(OP_LT);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("greater")) {
+ next();
+ if (tokentype != NUMBER)
+ pr_err("packet length expected");
+ emitop(OP_LOAD_LENGTH);
+ load_const(tokenval);
+ emitop(OP_GT);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("nofrag")) {
+ emitop(OP_OFFSET_LINK);
+ compare_value_mask(6, 2, 0, 0x1fff);
+ emitop(OP_OFFSET_POP);
+ emitop(OP_BRFL);
+ m = chain(0);
+ ethertype_match(ETHERTYPE_IP);
+ resolve_chain(m);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("net") || EQ("dstnet") || EQ("srcnet")) {
+ if (EQ("dstnet"))
+ dir = TO;
+ else if (EQ("srcnet"))
+ dir = FROM;
+ next();
+ netaddr_match(dir, token);
+ dir = ANY;
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("port") || EQ("srcport") || EQ("dstport")) {
+ if (EQ("dstport"))
+ dir = TO;
+ else if (EQ("srcport"))
+ dir = FROM;
+ next();
+ port_match(dir, token);
+ dir = ANY;
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("rpc")) {
+ uint_t vers, proc;
+ char savetoken[32];
+
+ vers = proc = -1;
+ next();
+ (void) strlcpy(savetoken, token, sizeof (savetoken));
+ next();
+ if (*token == ',') {
+ next();
+ if (tokentype != NUMBER)
+ pr_err("version number expected");
+ vers = tokenval;
+ next();
+ }
+ if (*token == ',') {
+ next();
+ if (tokentype != NUMBER)
+ pr_err("proc number expected");
+ proc = tokenval;
+ next();
+ }
+ rpc_match_prog(dir, savetoken, vers, proc);
+ dir = ANY;
+ opstack++;
+ break;
+ }
+
+ if (EQ("slp")) {
+ /* filter out TCP handshakes */
+ emitop(OP_OFFSET_LINK);
+ compare_value(9, 1, IPPROTO_TCP);
+ emitop(OP_LOAD_CONST);
+ emitval(52);
+ emitop(OP_LOAD_CONST);
+ emitval(2);
+ emitop(OP_LOAD_SHORT);
+ emitop(OP_GE);
+ emitop(OP_AND); /* proto == TCP && len < 52 */
+ emitop(OP_NOT);
+ emitop(OP_BRFL); /* pkt too short to be a SLP call */
+ m = chain(0);
+
+ emitop(OP_OFFSET_POP);
+ emitop(OP_OFFSET_SLP);
+ resolve_chain(m);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("ldap")) {
+ dir = ANY;
+ port_match(dir, "ldap");
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("and") || EQ("or")) {
+ break;
+ }
+
+ if (EQ("gateway")) {
+ next();
+ if (eaddr || tokentype != ALPHA)
+ pr_err("hostname required: %s", token);
+ etheraddr_match(dir, token);
+ dir = ANY;
+ emitop(OP_BRFL);
+ m = chain(0);
+ ipaddr_match(dir, token, IPV4_AND_IPV6);
+ emitop(OP_NOT);
+ resolve_chain(m);
+ opstack++;
+ next();
+ }
+
+ if (EQ("host") || EQ("between") ||
+ tokentype == ALPHA || /* assume its a hostname */
+ tokentype == ADDR_IP ||
+ tokentype == ADDR_IP6 ||
+ tokentype == ADDR_AT ||
+ tokentype == ADDR_ETHER) {
+ if (EQ("host") || EQ("between"))
+ next();
+ if (eaddr || tokentype == ADDR_ETHER) {
+ etheraddr_match(dir, token);
+ } else if (tokentype == ALPHA) {
+ ipaddr_match(dir, token, IPV4_AND_IPV6);
+ } else if (tokentype == ADDR_AT) {
+ ataddr_match(dir, token);
+ } else if (tokentype == ADDR_IP) {
+ ipaddr_match(dir, token, IPV4_ONLY);
+ } else {
+ ipaddr_match(dir, token, IPV6_ONLY);
+ }
+ dir = ANY;
+ eaddr = 0;
+ opstack++;
+ next();
+ break;
+ }
+
+ if (tokentype == NUMBER) {
+ load_const(tokenval);
+ opstack++;
+ next();
+ break;
+ }
+
+ break; /* unknown token */
+ }
+}
+
+struct optable {
+ char *op_tok;
+ enum optype op_type;
+};
+
+static struct optable
+mulops[] = {
+ "*", OP_MUL,
+ "/", OP_DIV,
+ "%", OP_REM,
+ "&", OP_AND,
+ "", OP_STOP,
+};
+
+static struct optable
+addops[] = {
+ "+", OP_ADD,
+ "-", OP_SUB,
+ "|", OP_OR,
+ "^", OP_XOR,
+ "", OP_STOP,
+};
+
+static struct optable
+compareops[] = {
+ "==", OP_EQ,
+ "=", OP_EQ,
+ "!=", OP_NE,
+ ">", OP_GT,
+ ">=", OP_GE,
+ "<", OP_LT,
+ "<=", OP_LE,
+ "", OP_STOP,
+};
+
+/*
+ * Using the table, find the operator
+ * that corresponds to the token.
+ * Return 0 if not found.
+ */
+static int
+find_op(char *tok, struct optable *table)
+{
+ struct optable *op;
+
+ for (op = table; *op->op_tok; op++) {
+ if (strcmp(tok, op->op_tok) == 0)
+ return (op->op_type);
+ }
+
+ return (0);
+}
+
+static void
+expr_mul()
+{
+ int op;
+ int s = opstack;
+
+ primary();
+ while (op = find_op(token, mulops)) {
+ next();
+ primary();
+ checkstack(s + 2);
+ emitop(op);
+ opstack--;
+ }
+}
+
+static void
+expr_add()
+{
+ int op, s = opstack;
+
+ expr_mul();
+ while (op = find_op(token, addops)) {
+ next();
+ expr_mul();
+ checkstack(s + 2);
+ emitop(op);
+ opstack--;
+ }
+}
+
+static void
+expr_compare()
+{
+ int op, s = opstack;
+
+ expr_add();
+ while (op = find_op(token, compareops)) {
+ next();
+ expr_add();
+ checkstack(s + 2);
+ emitop(op);
+ opstack--;
+ }
+}
+
+/*
+ * Alternation ("and") is difficult because
+ * an implied "and" is acknowledge between
+ * two adjacent primaries. Just keep calling
+ * the lower-level expression routine until
+ * no value is added to the opstack.
+ */
+static void
+alternation()
+{
+ int m = 0;
+ int s = opstack;
+
+ expr_compare();
+ checkstack(s + 1);
+ for (;;) {
+ if (EQ("and"))
+ next();
+ emitop(OP_BRFL);
+ m = chain(m);
+ expr_compare();
+ if (opstack != s + 2)
+ break;
+ opstack--;
+ }
+ unemit(2);
+ resolve_chain(m);
+}
+
+static void
+expression()
+{
+ int m = 0;
+ int s = opstack;
+
+ alternation();
+ while (EQ("or") || EQ(",")) {
+ emitop(OP_BRTR);
+ m = chain(m);
+ next();
+ alternation();
+ checkstack(s + 2);
+ opstack--;
+ }
+ resolve_chain(m);
+}
+
+/*
+ * Take n args from the argv list
+ * and concatenate them into a single string.
+ */
+char *
+concat_args(char **argv, int argc)
+{
+ int i, len;
+ char *str, *p;
+
+ /* First add the lengths of all the strings */
+ len = 0;
+ for (i = 0; i < argc; i++)
+ len += strlen(argv[i]) + 1;
+
+ /* allocate the big string */
+ str = (char *)malloc(len);
+ if (str == NULL)
+ pr_err("no mem");
+
+ p = str;
+
+ /*
+ * Concat the strings into the big
+ * string using a space as separator
+ */
+ for (i = 0; i < argc; i++) {
+ strcpy(p, argv[i]);
+ p += strlen(p);
+ *p++ = ' ';
+ }
+ *--p = '\0';
+
+ return (str);
+}
+
+/*
+ * Take the expression in the string "expr"
+ * and compile it into the code array.
+ * Print the generated code if the print
+ * arg is set.
+ */
+void
+compile(char *expr, int print)
+{
+ expr = strdup(expr);
+ if (expr == NULL)
+ pr_err("no mem");
+ curr_op = oplist;
+ tkp = expr;
+ dir = ANY;
+
+ next();
+ if (tokentype != EOL)
+ expression();
+ emitop(OP_STOP);
+ if (tokentype != EOL)
+ pr_err("invalid expression");
+ optimize(oplist);
+ if (print)
+ codeprint();
+}
+
+/*
+ * Lookup hostname in the arp cache.
+ */
+boolean_t
+arp_for_ether(char *hostname, struct ether_addr *ep)
+{
+ struct arpreq ar;
+ struct hostent *hp;
+ struct sockaddr_in *sin;
+ int error_num;
+ int s;
+
+ memset(&ar, 0, sizeof (ar));
+ sin = (struct sockaddr_in *)&ar.arp_pa;
+ sin->sin_family = AF_INET;
+ hp = getipnodebyname(hostname, AF_INET, 0, &error_num);
+ if (hp == NULL) {
+ return (B_FALSE);
+ }
+ memcpy(&sin->sin_addr, hp->h_addr, sizeof (sin->sin_addr));
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ return (B_FALSE);
+ }
+ if (ioctl(s, SIOCGARP, &ar) < 0) {
+ close(s);
+ return (B_FALSE);
+ }
+ close(s);
+ memcpy(ep->ether_addr_octet, ar.arp_ha.sa_data, sizeof (*ep));
+ return (B_TRUE);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_http.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_http.c
new file mode 100644
index 0000000000..b6e6b9da26
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_http.c
@@ -0,0 +1,141 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1996-1998,2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Protocol interpreter for the Hypertext Transfer Protocol (HTTP)
+ *
+ * Relevant standards:
+ * Berners-Lee, T., et al: Hypertext Transfer Protocol -- HTTP/1.0.
+ * RFC 1945, May 1996
+ * Fielding, R., et al: Hypertext Transfer Protocol -- HTTP/1.1.
+ * RFC 2068, June 1999
+ */
+
+#include <netinet/in.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include "snoop.h"
+
+#define CR 13 /* "carriage return" character */
+#define LF 10 /* "line feed" character */
+
+/*
+ * Summary lines: packet contents starting with less than MINCHARS
+ * printable characters will not be printed. MAXCHARS is the maximum
+ * number of characters printed.
+ * Detail lines: NLINES is the maximum number of content lines to print
+ */
+#define MINCHARS 10
+#define MAXCHARS 80
+#define NLINES 5
+
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+
+static int printable(const char *line, const char *endp);
+
+int
+interpret_http(int flags, char *line, int fraglen)
+{
+ char *p, *q, *endp;
+ int c;
+ int lineno;
+
+ endp = line + fraglen;
+
+ if (flags & F_SUM) {
+ c = printable(line, endp - 1);
+ if (c < MINCHARS) {
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "HTTP (body)");
+ } else {
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "HTTP %.*s", MIN(c, MAXCHARS), line);
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("HTTP: ", "HyperText Transfer Protocol", fraglen);
+ show_space();
+
+ lineno = 0;
+ for (p = line; p < endp && lineno < NLINES; p = q + 1) {
+ c = printable(p, endp - 1);
+
+ /* stop if no printables, except if at line end */
+ if (c == 0 && *p != CR && *p != LF)
+ break;
+
+ /*
+ * A line may be terminated either by an CR LF sequence
+ * (DOS, Mac), or by LF alone
+ */
+
+ q = memchr(p, CR, (endp - p));
+ if (q != NULL) {
+ if (q < endp - 1 && q[1] == LF)
+ ++q; /* ignore subsequent LF character */
+ } else {
+ q = memchr(p, LF, (endp - p));
+ /* no CR/LF: use end of buffer */
+ if (q == NULL)
+ q = endp - 1;
+ }
+
+ /* truncate lines containing non-printable characters */
+ (void) snprintf(get_line(0, c), get_line_remain(),
+ "%.*s", c, p);
+ ++lineno;
+ }
+
+ if (p < endp) /* there was more data to be printed */
+ (void) snprintf(get_line(0, 5), get_line_remain(),
+ "[...]");
+
+ show_space();
+ }
+
+ return (fraglen);
+}
+
+/*
+ * Return the length of the initial string starting with "startp" and
+ * ending with "endp" (inclusively) consisting only of printable
+ * characters.
+ */
+
+static int
+printable(const char *startp, const char *endp)
+{
+ const char *p = startp;
+
+ while (p <= endp && (isprint(*p) || *p == '\t'))
+ p++;
+
+ return (p - startp);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_icmp.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_icmp.c
new file mode 100644
index 0000000000..5f2b48400a
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_icmp.c
@@ -0,0 +1,1005 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <sys/stropts.h>
+#include <sys/sysmacros.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/udp.h>
+#include <netinet/tcp.h>
+#include <netinet/icmp6.h>
+#include <netinet/ip6.h>
+#include <inet/ip.h>
+#include <inet/ip6.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include "snoop.h"
+#include "snoop_mip.h"
+
+static void interpret_options(char *, int);
+static void interpret_mldv2qry(icmp6_t *, int);
+static void interpret_mldv2rpt(icmp6_t *, int);
+
+
+/* Mobile-IP routines from snoop_mip.c */
+extern void interpret_icmp_mip_ext(uchar_t *, int);
+extern const char *get_mip_adv_desc(uint8_t);
+
+/* Router advertisement message structure. */
+struct icmp_ra_addr {
+ uint32_t addr;
+ uint32_t preference;
+};
+
+/*ARGSUSED*/
+void
+interpret_icmp(int flags, struct icmp *icmp, int iplen, int ilen)
+{
+ char *pt, *pc, *px;
+ char *line;
+ char buff[67627]; /* Router adv. can have 256 routers .... */
+ /* Each router has a name 256 char long .. */
+ char extbuff[MAXHOSTNAMELEN + 1];
+ struct udphdr *orig_uhdr;
+ int num_rtr_addrs = 0;
+ extern char *prot_nest_prefix;
+
+ if (ilen < ICMP_MINLEN)
+ return; /* incomplete header */
+
+ pt = "Unknown";
+ pc = "";
+ px = "";
+
+ switch (icmp->icmp_type) {
+ case ICMP_ECHOREPLY:
+ pt = "Echo reply";
+ (void) sprintf(buff, "ID: %d Sequence number: %d",
+ ntohs(icmp->icmp_id), ntohs(icmp->icmp_seq));
+ pc = buff;
+ break;
+ case ICMP_UNREACH:
+ pt = "Destination unreachable";
+ switch (icmp->icmp_code) {
+ case ICMP_UNREACH_NET:
+ if (ilen >= ICMP_ADVLENMIN) {
+ (void) sprintf(buff, "Net %s unreachable",
+ addrtoname(AF_INET,
+ &icmp->icmp_ip.ip_dst));
+ pc = buff;
+ } else {
+ pc = "Bad net";
+ }
+ break;
+ case ICMP_UNREACH_HOST:
+ if (ilen >= ICMP_ADVLENMIN) {
+ (void) sprintf(buff, "Host %s unreachable",
+ addrtoname(AF_INET,
+ &icmp->icmp_ip.ip_dst));
+ pc = buff;
+ } else {
+ pc = "Bad host";
+ }
+ break;
+ case ICMP_UNREACH_PROTOCOL:
+ if (ilen >= ICMP_ADVLENMIN) {
+ (void) sprintf(buff, "Bad protocol %d",
+ icmp->icmp_ip.ip_p);
+ pc = buff;
+ } else {
+ pc = "Bad protocol";
+ }
+ break;
+ case ICMP_UNREACH_PORT:
+ if (ilen >= ICMP_ADVLENMIN) {
+ orig_uhdr = (struct udphdr *)((uchar_t *)icmp +
+ ICMP_MINLEN + icmp->icmp_ip.ip_hl * 4);
+ switch (icmp->icmp_ip.ip_p) {
+ case IPPROTO_TCP:
+ (void) sprintf(buff, "TCP port %d"
+ " unreachable",
+ ntohs(orig_uhdr->uh_dport));
+ pc = buff;
+ break;
+ case IPPROTO_UDP:
+ (void) sprintf(buff, "UDP port %d"
+ " unreachable",
+ ntohs(orig_uhdr->uh_dport));
+ pc = buff;
+ break;
+ default:
+ pc = "Port unreachable";
+ break;
+ }
+ } else {
+ pc = "Bad port";
+ }
+ break;
+ case ICMP_UNREACH_NEEDFRAG:
+ if (ntohs(icmp->icmp_nextmtu) != 0) {
+ (void) sprintf(buff, "Needed to fragment:"
+ " next hop MTU = %d",
+ ntohs(icmp->icmp_nextmtu));
+ pc = buff;
+ } else {
+ pc = "Needed to fragment";
+ }
+ break;
+ case ICMP_UNREACH_SRCFAIL:
+ pc = "Source route failed";
+ break;
+ case ICMP_UNREACH_NET_UNKNOWN:
+ pc = "Unknown network";
+ break;
+ case ICMP_UNREACH_HOST_UNKNOWN:
+ pc = "Unknown host";
+ break;
+ case ICMP_UNREACH_ISOLATED:
+ pc = "Source host isolated";
+ break;
+ case ICMP_UNREACH_NET_PROHIB:
+ pc = "Net administratively prohibited";
+ break;
+ case ICMP_UNREACH_HOST_PROHIB:
+ pc = "Host administratively prohibited";
+ break;
+ case ICMP_UNREACH_TOSNET:
+ pc = "Net unreachable for this TOS";
+ break;
+ case ICMP_UNREACH_TOSHOST:
+ pc = "Host unreachable for this TOS";
+ break;
+ case ICMP_UNREACH_FILTER_PROHIB:
+ pc = "Communication administratively prohibited";
+ break;
+ case ICMP_UNREACH_HOST_PRECEDENCE:
+ pc = "Host precedence violation";
+ break;
+ case ICMP_UNREACH_PRECEDENCE_CUTOFF:
+ pc = "Precedence cutoff in effect";
+ break;
+ default:
+ break;
+ }
+ break;
+ case ICMP_SOURCEQUENCH:
+ pt = "Packet lost, slow down";
+ break;
+ case ICMP_REDIRECT:
+ pt = "Redirect";
+ switch (icmp->icmp_code) {
+ case ICMP_REDIRECT_NET:
+ pc = "for network";
+ break;
+ case ICMP_REDIRECT_HOST:
+ pc = "for host";
+ break;
+ case ICMP_REDIRECT_TOSNET:
+ pc = "for tos and net";
+ break;
+ case ICMP_REDIRECT_TOSHOST:
+ pc = "for tos and host";
+ break;
+ default:
+ break;
+ }
+ (void) sprintf(buff, "%s %s to %s",
+ pc, addrtoname(AF_INET, &icmp->icmp_ip.ip_dst),
+ addrtoname(AF_INET, &icmp->icmp_gwaddr));
+ pc = buff;
+ break;
+ case ICMP_ECHO:
+ pt = "Echo request";
+ (void) sprintf(buff, "ID: %d Sequence number: %d",
+ ntohs(icmp->icmp_id), ntohs(icmp->icmp_seq));
+ pc = buff;
+ break;
+ case ICMP_ROUTERADVERT:
+
+#define icmp_num_addrs icmp_hun.ih_rtradv.irt_num_addrs
+#define icmp_wpa icmp_hun.ih_rtradv.irt_wpa
+#define icmp_lifetime icmp_hun.ih_rtradv.irt_lifetime
+
+ pt = "Router advertisement";
+ (void) sprintf(buff, "Lifetime %ds [%d]:",
+ ntohs(icmp->icmp_lifetime), icmp->icmp_num_addrs);
+ if (icmp->icmp_wpa == 2) {
+ struct icmp_ra_addr *ra;
+ char ra_buf[MAXHOSTNAMELEN + 32];
+ char ra_ext_buf[50];
+ struct in_addr sin;
+ int icmp_ra_len;
+ int i;
+
+ /* Cannot trust anything from the network... */
+ num_rtr_addrs = MIN((ilen - ICMP_MINLEN) / 8,
+ icmp->icmp_num_addrs);
+
+ ra = (struct icmp_ra_addr *)icmp->icmp_data;
+ for (i = 0; i < num_rtr_addrs; i++) {
+ sin.s_addr = ra->addr;
+ (void) snprintf(ra_buf, sizeof (ra_buf),
+ " {%s %u}",
+ addrtoname(AF_INET, &sin),
+ ntohl(ra->preference));
+ if (strlcat(buff, ra_buf, sizeof (buff)) >=
+ sizeof (buff)) {
+ buff[sizeof (buff) -
+ strlen("<Too Long>)")] = '\0';
+ (void) strlcat(buff, "<Too Long>",
+ sizeof (buff));
+ break;
+ }
+ ra++;
+ }
+
+ icmp_ra_len = ICMP_MINLEN + num_rtr_addrs *
+ sizeof (struct icmp_ra_addr);
+ if (ilen > icmp_ra_len) {
+ int curr_len = ilen - icmp_ra_len;
+ int ocurr_len;
+ exthdr_t *exthdr = (exthdr_t *)ra;
+
+ extbuff[0] = '\0';
+
+ while (curr_len > 0) {
+ /* Append Mobile-IP description */
+ (void) snprintf(ra_ext_buf,
+ sizeof (ra_ext_buf), ", %s",
+ get_mip_adv_desc(exthdr->type));
+ (void) strlcat(extbuff, ra_ext_buf,
+ sizeof (extbuff));
+
+ /* Special case for padding */
+ if (exthdr->type ==
+ ICMP_ADV_MSG_PADDING_EXT) {
+
+ curr_len--;
+ exthdr = (exthdr_t *)
+ ((char *)exthdr + 1);
+ continue;
+ }
+
+ /* else normal extension */
+ ocurr_len = curr_len;
+ curr_len -= sizeof (*exthdr) +
+ exthdr->length;
+ /* detect bad length */
+ if (ocurr_len < curr_len)
+ break;
+ exthdr = (exthdr_t *)
+ ((char *)exthdr +
+ sizeof (*exthdr) +
+ exthdr->length);
+ }
+ px = extbuff;
+ }
+ pc = buff;
+ }
+ break;
+ case ICMP_ROUTERSOLICIT:
+ pt = "Router solicitation";
+ break;
+ case ICMP_TIMXCEED:
+ pt = "Time exceeded";
+ switch (icmp->icmp_code) {
+ case ICMP_TIMXCEED_INTRANS:
+ pc = "in transit";
+ break;
+ case ICMP_TIMXCEED_REASS:
+ pc = "in reassembly";
+ break;
+ default:
+ break;
+ }
+ break;
+ case ICMP_PARAMPROB:
+ pt = "IP parameter problem";
+ switch (icmp->icmp_code) {
+ case ICMP_PARAMPROB_OPTABSENT:
+ pc = "Required option missing";
+ break;
+ case ICMP_PARAMPROB_BADLENGTH:
+ pc = "Bad length";
+ break;
+ case 0: /* Should this be the default? */
+ (void) sprintf(buff, "Problem at octet %d\n",
+ icmp->icmp_pptr);
+ pc = buff;
+ default:
+ break;
+ }
+ break;
+ case ICMP_TSTAMP:
+ pt = "Timestamp request";
+ break;
+ case ICMP_TSTAMPREPLY:
+ pt = "Timestamp reply";
+ break;
+ case ICMP_IREQ:
+ pt = "Information request";
+ break;
+ case ICMP_IREQREPLY:
+ pt = "Information reply";
+ break;
+ case ICMP_MASKREQ:
+ pt = "Address mask request";
+ break;
+ case ICMP_MASKREPLY:
+ pt = "Address mask reply";
+ (void) sprintf(buff, "Mask = 0x%x", ntohl(icmp->icmp_mask));
+ pc = buff;
+ break;
+ default:
+ break;
+ }
+
+ if (flags & F_SUM) {
+ line = get_sum_line();
+ if (*pc) {
+ if (*px) {
+ (void) sprintf(line, "ICMP %s (%s)%s",
+ pt, pc, px);
+ } else {
+ (void) sprintf(line, "ICMP %s (%s)", pt, pc);
+ }
+ } else {
+ (void) sprintf(line, "ICMP %s", pt);
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("ICMP: ", "ICMP Header", ilen);
+ show_space();
+ (void) sprintf(get_line(0, 0), "Type = %d (%s)",
+ icmp->icmp_type, pt);
+ if (*pc) {
+ (void) sprintf(get_line(0, 0), "Code = %d (%s)",
+ icmp->icmp_code, pc);
+ } else {
+ (void) sprintf(get_line(0, 0), "Code = %d",
+ icmp->icmp_code);
+ }
+ (void) sprintf(get_line(0, 0), "Checksum = %x",
+ ntohs(icmp->icmp_cksum));
+
+ if (icmp->icmp_type == ICMP_UNREACH ||
+ icmp->icmp_type == ICMP_REDIRECT) {
+ if (ilen > 28) {
+ show_space();
+ (void) sprintf(get_line(0, 0),
+ "[ subject header follows ]");
+ show_space();
+ prot_nest_prefix = "ICMP:";
+ (void) interpret_ip(flags,
+ (struct ip *)icmp->icmp_data, 28);
+ prot_nest_prefix = "";
+ }
+ } else if (icmp->icmp_type == ICMP_PARAMPROB) {
+ if (ilen > 28) {
+ show_space();
+ (void) sprintf(get_line(0, 0),
+ "[ subject header follows ]");
+ show_space();
+ prot_nest_prefix = "ICMP:";
+ (void) interpret_ip(flags,
+ (struct ip *)icmp->icmp_data, 28);
+ prot_nest_prefix = "";
+ }
+ } else if (icmp->icmp_type == ICMP_ROUTERADVERT) {
+ if (icmp->icmp_wpa == 2) {
+ int icmp_ra_len;
+
+ show_space();
+ icmp_ra_len = ICMP_MINLEN +
+ num_rtr_addrs *
+ sizeof (struct icmp_ra_addr);
+ prot_nest_prefix = "";
+ if (ilen > icmp_ra_len) {
+ interpret_icmp_mip_ext(
+ (uchar_t *)icmp + icmp_ra_len,
+ ilen - icmp_ra_len);
+ }
+ }
+ }
+ show_space();
+ }
+}
+
+/*ARGSUSED*/
+void
+interpret_icmpv6(flags, icmp6, iplen, ilen)
+ int flags;
+ icmp6_t *icmp6;
+ int iplen, ilen;
+{
+ char *pt, *pc;
+ char *line;
+ extern char *prot_nest_prefix;
+ char addrstr[INET6_ADDRSTRLEN];
+ char buff[2048];
+
+ if (ilen < ICMP6_MINLEN)
+ return; /* incomplete header */
+
+ pt = "Unknown";
+ pc = "";
+
+ switch (icmp6->icmp6_type) {
+ case ICMP6_DST_UNREACH:
+ pt = "Destination unreachable";
+ switch (icmp6->icmp6_code) {
+ case ICMP6_DST_UNREACH_NOROUTE:
+ pc = "No route to destination";
+ break;
+ case ICMP6_DST_UNREACH_ADMIN:
+ pc = "Communication administratively prohibited";
+ break;
+ case ICMP6_DST_UNREACH_ADDR:
+ pc = "Address unreachable";
+ break;
+ case ICMP6_DST_UNREACH_NOPORT:
+ if (ilen >= ICMP6_MINLEN + IPV6_HDR_LEN +
+ sizeof (struct udphdr)) {
+
+ ip6_t *orig_ip6hdr = (ip6_t *)&icmp6[1];
+
+ switch (orig_ip6hdr->ip6_nxt) {
+ case IPPROTO_TCP: {
+ struct tcphdr *orig_thdr =
+ (struct tcphdr *)&orig_ip6hdr[1];
+
+ (void) sprintf(buff, "TCP port %hu"
+ " unreachable",
+ ntohs(orig_thdr->th_dport));
+ pc = buff;
+ break;
+ }
+ case IPPROTO_UDP: {
+ struct udphdr *orig_uhdr =
+ (struct udphdr *)&orig_ip6hdr[1];
+
+ (void) sprintf(buff, "UDP port %hu"
+ " unreachable",
+ ntohs(orig_uhdr->uh_dport));
+ pc = buff;
+ break;
+ }
+ default:
+ pc = "Port unreachable";
+ break;
+ }
+ } else {
+ pc = "Bad port";
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ case ICMP6_PACKET_TOO_BIG:
+ pt = "Packet too big";
+ break;
+ case ND_REDIRECT:
+ pt = "Redirect";
+ break;
+ case ICMP6_TIME_EXCEEDED:
+ pt = "Time exceeded";
+ switch (icmp6->icmp6_code) {
+ case ICMP6_TIME_EXCEED_TRANSIT:
+ pc = "Hop limit exceeded in transit";
+ break;
+ case ICMP6_TIME_EXCEED_REASSEMBLY:
+ pc = "Fragment reassembly time exceeded";
+ break;
+ default:
+ break;
+ }
+ break;
+ case ICMP6_PARAM_PROB:
+ pt = "Parameter problem";
+ switch (icmp6->icmp6_code) {
+ case ICMP6_PARAMPROB_HEADER:
+ pc = "Erroneous header field";
+ break;
+ case ICMP6_PARAMPROB_NEXTHEADER:
+ pc = "Unrecognized next header type";
+ break;
+ case ICMP6_PARAMPROB_OPTION:
+ pc = "Unrecognized IPv6 option";
+ break;
+ }
+ break;
+ case ICMP6_ECHO_REQUEST:
+ pt = "Echo request";
+ (void) sprintf(buff, "ID: %d Sequence number: %d",
+ ntohs(icmp6->icmp6_id), ntohs(icmp6->icmp6_seq));
+ pc = buff;
+ break;
+ case ICMP6_ECHO_REPLY:
+ pt = "Echo reply";
+ (void) sprintf(buff, "ID: %d Sequence number: %d",
+ ntohs(icmp6->icmp6_id), ntohs(icmp6->icmp6_seq));
+ pc = buff;
+ break;
+ case MLD_LISTENER_QUERY:
+ if (ilen == MLD_MINLEN)
+ pt = "Group membership query - MLDv1";
+ else if (ilen >= MLD_V2_QUERY_MINLEN)
+ pt = "Group membership query - MLDv2";
+ else
+ pt = "Unknown membership query";
+ break;
+ case MLD_LISTENER_REPORT:
+ pt = "Group membership report - MLDv1";
+ break;
+ case MLD_LISTENER_REDUCTION:
+ pt = "Group membership termination - MLDv1";
+ break;
+ case MLD_V2_LISTENER_REPORT:
+ pt = "Group membership report - MLDv2";
+ break;
+ case ND_ROUTER_SOLICIT:
+ pt = "Router solicitation";
+ break;
+ case ND_ROUTER_ADVERT:
+ pt = "Router advertisement";
+ break;
+ case ND_NEIGHBOR_SOLICIT:
+ pt = "Neighbor solicitation";
+ break;
+ case ND_NEIGHBOR_ADVERT:
+ pt = "Neighbor advertisement";
+ break;
+ default:
+ break;
+ }
+
+ if (flags & F_SUM) {
+ line = get_sum_line();
+ if (*pc)
+ (void) sprintf(line, "ICMPv6 %s (%s)", pt, pc);
+ else
+ (void) sprintf(line, "ICMPv6 %s", pt);
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("ICMPv6: ", "ICMPv6 Header", ilen);
+ show_space();
+ (void) sprintf(get_line(0, 0), "Type = %d (%s)",
+ icmp6->icmp6_type, pt);
+ if (*pc)
+ (void) sprintf(get_line(0, 0), "Code = %d (%s)",
+ icmp6->icmp6_code, pc);
+ else
+ (void) sprintf(get_line(0, 0), "Code = %d",
+ icmp6->icmp6_code);
+ (void) sprintf(get_line(0, 0), "Checksum = %x",
+ ntohs(icmp6->icmp6_cksum));
+
+ switch (icmp6->icmp6_type) {
+ case ICMP6_DST_UNREACH:
+ if (ilen > ICMP6_MINLEN + IPV6_HDR_LEN) {
+ show_space();
+ (void) sprintf(get_line(0, 0),
+ "[ subject header follows ]");
+ show_space();
+ prot_nest_prefix = "ICMPv6:";
+ (void) interpret_ipv6(flags, (ip6_t *)&icmp6[1],
+ ICMP6_MINLEN + IPV6_HDR_LEN);
+ prot_nest_prefix = "";
+ }
+ break;
+ case ICMP6_PACKET_TOO_BIG:
+ show_space();
+ (void) sprintf(get_line(0, 0),
+ " Packet too big MTU = %d", icmp6->icmp6_mtu);
+ show_space();
+ break;
+ case ND_REDIRECT: {
+ nd_redirect_t *rd = (nd_redirect_t *)icmp6;
+
+ (void) sprintf(get_line(0, 0), "Target address= %s",
+ inet_ntop(AF_INET6, (char *)&rd->nd_rd_target,
+ addrstr, INET6_ADDRSTRLEN));
+
+ (void) sprintf(get_line(0, 0),
+ "Destination address= %s",
+ inet_ntop(AF_INET6, (char *)&rd->nd_rd_dst,
+ addrstr, INET6_ADDRSTRLEN));
+ show_space();
+ interpret_options((char *)icmp6 + sizeof (*rd),
+ ilen - sizeof (*rd));
+ break;
+ }
+ case ND_NEIGHBOR_SOLICIT: {
+ struct nd_neighbor_solicit *ns;
+ if (ilen < sizeof (*ns))
+ break;
+ ns = (struct nd_neighbor_solicit *)icmp6;
+ (void) sprintf(get_line(0, 0), "Target node = %s, %s",
+ inet_ntop(AF_INET6, (char *)&ns->nd_ns_target,
+ addrstr, INET6_ADDRSTRLEN),
+ addrtoname(AF_INET6, &ns->nd_ns_target));
+ show_space();
+ interpret_options((char *)icmp6 + sizeof (*ns),
+ ilen - sizeof (*ns));
+ break;
+ }
+
+ case ND_NEIGHBOR_ADVERT: {
+ struct nd_neighbor_advert *na;
+
+ if (ilen < sizeof (*na))
+ break;
+ na = (struct nd_neighbor_advert *)icmp6;
+ (void) sprintf(get_line(0, 0), "Target node = %s, %s",
+ inet_ntop(AF_INET6, (char *)&na->nd_na_target,
+ addrstr, INET6_ADDRSTRLEN),
+ addrtoname(AF_INET6, &na->nd_na_target));
+ (void) sprintf(get_line(0, 0),
+ "Router flag: %s, Solicited flag: %s, "
+ "Override flag: %s",
+ na->nd_na_flags_reserved & ND_NA_FLAG_ROUTER ?
+ "SET" : "NOT SET",
+ na->nd_na_flags_reserved & ND_NA_FLAG_SOLICITED ?
+ "SET" : "NOT SET",
+ na->nd_na_flags_reserved & ND_NA_FLAG_OVERRIDE ?
+ "SET" : "NOT SET");
+
+ show_space();
+ interpret_options((char *)icmp6 + sizeof (*na),
+ ilen - sizeof (*na));
+ }
+ break;
+
+ case ND_ROUTER_SOLICIT: {
+ if (ilen < sizeof (struct nd_router_solicit))
+ break;
+ interpret_options(
+ (char *)icmp6 + sizeof (struct nd_router_solicit),
+ ilen - sizeof (struct nd_router_solicit));
+ break;
+ }
+
+ case ND_ROUTER_ADVERT: {
+ struct nd_router_advert *ra;
+
+ if (ilen < sizeof (*ra))
+ break;
+ ra = (struct nd_router_advert *)icmp6;
+ (void) sprintf(get_line(0, 0),
+ "Max hops= %d, Router lifetime= %d",
+ ra->nd_ra_curhoplimit,
+ ntohs(ra->nd_ra_router_lifetime));
+
+ (void) sprintf(get_line(0, 0),
+ "Managed addr conf flag: %s, Other conf flag: %s",
+ ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED ?
+ "SET" : "NOT SET",
+ ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER ?
+ "SET" : "NOT SET");
+
+ (void) sprintf(get_line(0, 0),
+ "Reachable time: %u, Reachable retrans time %u",
+ ntohl(ra->nd_ra_reachable),
+ ntohl(ra->nd_ra_retransmit));
+ show_space();
+
+ interpret_options((char *)icmp6 + sizeof (*ra),
+ ilen - sizeof (*ra));
+ break;
+ }
+ case ICMP6_PARAM_PROB:
+ if (ilen < sizeof (*icmp6))
+ break;
+ (void) sprintf(get_line(0, 0), "Ptr = %u",
+ ntohl(icmp6->icmp6_pptr));
+ show_space();
+ break;
+
+ case MLD_LISTENER_QUERY: {
+ struct mld_hdr *mldg = (struct mld_hdr *)icmp6;
+
+ if (ilen < MLD_MINLEN)
+ break;
+
+ if (ilen >= MLD_V2_QUERY_MINLEN) {
+ interpret_mldv2qry(icmp6, ilen);
+ } else {
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ "Multicast address= %s",
+ inet_ntop(AF_INET6, mldg->mld_addr.s6_addr,
+ addrstr, INET6_ADDRSTRLEN));
+ }
+ show_space();
+ break;
+ }
+
+ case MLD_LISTENER_REPORT:
+ case MLD_LISTENER_REDUCTION: {
+ struct mld_hdr *mldg;
+
+ if (ilen < sizeof (*mldg))
+ break;
+ mldg = (struct mld_hdr *)icmp6;
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Multicast address= %s", inet_ntop(AF_INET6,
+ mldg->mld_addr.s6_addr, addrstr, INET6_ADDRSTRLEN));
+ show_space();
+ break;
+ }
+
+ case MLD_V2_LISTENER_REPORT: {
+ interpret_mldv2rpt(icmp6, ilen);
+ show_space();
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+}
+
+static void
+interpret_options(optc, ilen)
+ char *optc;
+ int ilen;
+{
+#define PREFIX_OPTION_LENGTH 4
+#define MTU_OPTION_LENGTH 1
+
+#define PREFIX_INFINITY 0xffffffffUL
+
+ struct nd_opt_hdr *opt;
+
+ for (; ilen >= sizeof (*opt); ) {
+ opt = (struct nd_opt_hdr *)optc;
+ if (opt->nd_opt_len == 0)
+ return;
+ switch (opt->nd_opt_type) {
+ case ND_OPT_SOURCE_LINKADDR:
+ case ND_OPT_TARGET_LINKADDR:
+ {
+ struct nd_opt_lla *lopt;
+ char *buf, chbuf[128];
+ uint_t addr_len;
+ int i;
+
+ if (ilen < (int)opt->nd_opt_len * 8)
+ break;
+
+ buf = chbuf;
+
+ lopt = (struct nd_opt_lla *)opt;
+ if (lopt->nd_opt_lla_type == ND_OPT_SOURCE_LINKADDR) {
+ (void) sprintf(get_line(0, 0),
+ "+++ ICMPv6 Source LL Addr option +++");
+ } else {
+ (void) sprintf(get_line(0, 0),
+ "+++ ICMPv6 Target LL Addr option +++");
+ }
+
+ /*
+ * The option length is in 8 octet units, and
+ * includes the first two bytes (the type and
+ * lenght fields) of the option.
+ */
+ addr_len = lopt->nd_opt_lla_len * 8 - 2;
+ for (i = 0; i < addr_len; i++) {
+ snprintf(buf, sizeof (chbuf) - (buf - chbuf),
+ "%x:", lopt->nd_opt_lla_hdw_addr[i]);
+ buf += strlen(buf);
+ if (buf >= &chbuf[sizeof (chbuf)]) {
+ buf = NULL;
+ chbuf[sizeof (chbuf) -
+ strlen("<Too Long>)")] = '\0';
+ (void) strlcat(chbuf, "<Too Long>",
+ sizeof (chbuf));
+ break;
+ }
+ }
+ if (buf)
+ *(buf - 1) = '\0'; /* Erase last colon */
+ (void) sprintf(get_line(0, 0),
+ "Link Layer address: %s", chbuf);
+ show_space();
+ break;
+ }
+ case ND_OPT_MTU: {
+ struct nd_opt_mtu *mopt;
+ if (opt->nd_opt_len != MTU_OPTION_LENGTH ||
+ ilen < sizeof (struct nd_opt_mtu))
+ break;
+ (void) sprintf(get_line(0, 0),
+ "+++ ICMPv6 MTU option +++");
+ mopt = (struct nd_opt_mtu *)opt;
+ (void) sprintf(get_line(0, 0),
+ "MTU = %u ", mopt->nd_opt_mtu_mtu);
+ show_space();
+ break;
+ }
+ case ND_OPT_PREFIX_INFORMATION: {
+ struct nd_opt_prefix_info *popt;
+ char validstr[30];
+ char preferredstr[30];
+ char prefixstr[INET6_ADDRSTRLEN];
+
+ if (opt->nd_opt_len != PREFIX_OPTION_LENGTH ||
+ ilen < sizeof (struct nd_opt_prefix_info))
+ break;
+ popt = (struct nd_opt_prefix_info *)opt;
+ (void) sprintf(get_line(0, 0),
+ "+++ ICMPv6 Prefix option +++");
+ (void) sprintf(get_line(0, 0),
+ "Prefix length = %d ", popt->nd_opt_pi_prefix_len);
+ (void) sprintf(get_line(0, 0),
+ "Onlink flag: %s, Autonomous addr conf flag: %s",
+ popt->nd_opt_pi_flags_reserved &
+ ND_OPT_PI_FLAG_ONLINK ? "SET" : "NOT SET",
+ popt->nd_opt_pi_flags_reserved &
+ ND_OPT_PI_FLAG_AUTO ? "SET" : "NOT SET");
+
+ if (ntohl(popt->nd_opt_pi_valid_time) ==
+ PREFIX_INFINITY)
+ sprintf(validstr, "INFINITY");
+ else
+ sprintf(validstr, "%lu",
+ ntohl(popt->nd_opt_pi_valid_time));
+
+ if (ntohl(popt->nd_opt_pi_preferred_time) ==
+ PREFIX_INFINITY)
+ sprintf(preferredstr, "INFINITY");
+ else
+ sprintf(preferredstr, "%lu",
+ ntohl(popt->nd_opt_pi_preferred_time));
+
+ (void) sprintf(get_line(0, 0),
+ "Valid Lifetime %s, Preferred Lifetime %s",
+ validstr, preferredstr);
+ (void) sprintf(get_line(0, 0), "Prefix %s",
+ inet_ntop(AF_INET6,
+ (char *)&popt->nd_opt_pi_prefix, prefixstr,
+ INET6_ADDRSTRLEN));
+ show_space();
+ }
+ default:
+ break;
+ }
+ optc += opt->nd_opt_len * 8;
+ ilen -= opt->nd_opt_len * 8;
+ }
+}
+
+static void
+interpret_mldv2qry(icmp6_t *icmp6, int ilen)
+{
+ mld2q_t *qry;
+ in6_addr_t *src;
+ int rem = ilen;
+ int srccnt;
+ char addrstr[INET6_ADDRSTRLEN];
+
+ if (ilen < sizeof (*qry)) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Malformed MLD Query");
+ return;
+ }
+ qry = (mld2q_t *)icmp6;
+ rem -= sizeof (*qry);
+ srccnt = ntohs(qry->mld2q_numsrc);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Multicast address= %s", inet_ntop(AF_INET6,
+ &qry->mld2q_addr.s6_addr, addrstr, INET6_ADDRSTRLEN));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "%d Source Address%s:", srccnt, (srccnt == 1) ? "" : "es");
+
+ src = (in6_addr_t *)&qry[1];
+ while (srccnt > 0 && rem >= sizeof (*src)) {
+ rem -= sizeof (*src);
+
+ (void) snprintf(get_line(0, 0), get_line_remain(), " %s",
+ inet_ntop(AF_INET6, src, addrstr, INET6_ADDRSTRLEN));
+
+ srccnt--;
+ src++;
+ }
+}
+
+#define MAX_MLDV2_REPORT_TYPE 6
+
+const char *mldv2rpt_types[] = {
+ "<unknown>",
+ "MODE_IS_INCLUDE",
+ "MODE_IS_EXCLUDE",
+ "CHANGE_TO_INCLUDE",
+ "CHANGE_TO_EXCLUDE",
+ "ALLOW_NEW_SOURCES",
+ "BLOCK_OLD_SOURCES",
+};
+
+static void
+interpret_mldv2rpt(icmp6_t *icmp6, int ilen)
+{
+ mld2r_t *rpt;
+ mld2mar_t *mar;
+ in6_addr_t *src;
+ int rem = ilen, auxlen;
+ uint16_t marcnt, srccnt;
+ char addrstr[INET6_ADDRSTRLEN];
+
+ if (ilen < sizeof (*rpt)) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Malformed MLDv2 Report");
+ return;
+ }
+ rpt = (mld2r_t *)icmp6;
+ mar = (mld2mar_t *)&rpt[1];
+ marcnt = ntohs(rpt->mld2r_nummar);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "%d Multicast Address Record%s:", marcnt, (marcnt == 1) ? "" : "s");
+ rem -= sizeof (*rpt);
+ while (marcnt > 0 && rem >= sizeof (*mar)) {
+ rem -= sizeof (*mar);
+
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Multicast address= %s type = %s", inet_ntop(AF_INET6,
+ &mar->mld2mar_group.s6_addr, addrstr, INET6_ADDRSTRLEN),
+ (mar->mld2mar_type > MAX_MLDV2_REPORT_TYPE) ?
+ "<unknown>" : mldv2rpt_types[mar->mld2mar_type]);
+ srccnt = ntohs(mar->mld2mar_numsrc);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "%d Source Address%s:", srccnt, (srccnt == 1) ? "" : "es");
+
+ src = (in6_addr_t *)&mar[1];
+ while (srccnt > 0 && rem >= sizeof (*src)) {
+ rem -= sizeof (*src);
+
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " %s", inet_ntop(AF_INET6, src, addrstr,
+ INET6_ADDRSTRLEN));
+
+ srccnt--;
+ src++;
+ }
+
+ marcnt--;
+ auxlen = mar->mld2mar_auxlen * 4;
+ rem -= auxlen;
+ mar = (mld2mar_t *)((uint8_t *)src + auxlen);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_igmp.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_igmp.c
new file mode 100644
index 0000000000..b683c813f0
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_igmp.c
@@ -0,0 +1,226 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <sys/stropts.h>
+#include <sys/sysmacros.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/igmp.h>
+#include <inet/ip.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include "snoop.h"
+
+static void interpret_igmpv3qry(struct igmp *, int);
+static void interpret_igmpv3rpt(struct igmp *, int);
+
+
+/*ARGSUSED*/
+void
+interpret_igmp(int flags, char *data, int iplen, int ilen)
+{
+ const char *pt;
+ char *line;
+ struct igmp *igmp = (struct igmp *)data;
+ char addrstr[INET_ADDRSTRLEN];
+
+ if (ilen < IGMP_MINLEN) {
+ /* incomplete header */
+ line = get_sum_line();
+ (void) snprintf(line, MAXLINE, "Malformed IGMP packet");
+ return;
+ }
+
+ switch (igmp->igmp_type) {
+ case IGMP_MEMBERSHIP_QUERY:
+ if (ilen == IGMP_MINLEN) {
+ if (igmp->igmp_code == 0)
+ pt = "v1 membership query";
+ else
+ pt = "v2 membership query";
+ } else if (ilen >= IGMP_V3_QUERY_MINLEN) {
+ pt = "v3 membership query";
+ } else {
+ pt = "Unknown membership query";
+ }
+ break;
+ case IGMP_V1_MEMBERSHIP_REPORT:
+ pt = "v1 membership report";
+ break;
+ case IGMP_V2_MEMBERSHIP_REPORT:
+ pt = "v2 membership report";
+ break;
+ case IGMP_V3_MEMBERSHIP_REPORT:
+ pt = "v3 membership report";
+ break;
+ case IGMP_V2_LEAVE_GROUP:
+ pt = "v2 leave group";
+ break;
+
+ default:
+ pt = "Unknown";
+ break;
+ }
+
+ if (flags & F_SUM) {
+ line = get_sum_line();
+ (void) snprintf(line, MAXLINE, "IGMP %s", pt);
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("IGMP: ", "IGMP Header", ilen);
+ show_space();
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Type = %d (%s)", igmp->igmp_type, pt);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Max Response Time = %d", igmp->igmp_code);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Checksum = %x", ntohs(igmp->igmp_cksum));
+
+ if (igmp->igmp_type == IGMP_MEMBERSHIP_QUERY &&
+ ilen >= IGMP_V3_QUERY_MINLEN) {
+ interpret_igmpv3qry(igmp, ilen);
+ } else if (igmp->igmp_type == IGMP_V3_MEMBERSHIP_REPORT) {
+ interpret_igmpv3rpt(igmp, ilen);
+ } else {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Group = %s",
+ inet_ntop(AF_INET, &igmp->igmp_group.s_addr,
+ addrstr, INET_ADDRSTRLEN));
+ }
+
+ show_space();
+ }
+}
+
+static void
+interpret_igmpv3qry(struct igmp *igmp, int ilen)
+{
+ struct igmp3q *qry;
+ struct in_addr *src;
+ int rem = ilen;
+ int srccnt;
+ char addrstr[INET_ADDRSTRLEN];
+
+ if (ilen < sizeof (*qry)) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Malformed IGMP Query");
+ return;
+ }
+
+ qry = (struct igmp3q *)igmp;
+ rem -= sizeof (*qry);
+ srccnt = ntohs(qry->igmp3q_numsrc);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Group = %s", inet_ntop(AF_INET, &qry->igmp3q_group, addrstr,
+ INET_ADDRSTRLEN));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "%d Source Address%s:", srccnt, (srccnt == 1) ? "" : "es");
+
+ src = (struct in_addr *)&qry[1];
+ while (srccnt > 0 && rem >= sizeof (*src)) {
+ rem -= sizeof (*src);
+
+ (void) snprintf(get_line(0, 0), get_line_remain(), " %s",
+ inet_ntop(AF_INET, &src->s_addr, addrstr, INET_ADDRSTRLEN));
+
+ srccnt--;
+ src++;
+ }
+}
+
+#define MAX_IGMPV3_REPORT_TYPE 6
+
+const char *igmpv3rpt_types[] = {
+ "<unknown>",
+ "MODE_IS_INCLUDE",
+ "MODE_IS_EXCLUDE",
+ "CHANGE_TO_INCLUDE",
+ "CHANGE_TO_EXCLUDE",
+ "ALLOW_NEW_SOURCES",
+ "BLOCK_OLD_SOURCES",
+};
+
+static void
+interpret_igmpv3rpt(struct igmp *igmp, int ilen)
+{
+ struct igmp3r *rpt;
+ struct grphdr *grh;
+ struct in_addr *src;
+ int rem = ilen, auxlen;
+ uint16_t grhcnt, srccnt;
+ char addrstr[INET_ADDRSTRLEN];
+
+ if (ilen < sizeof (*rpt)) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Malformed IGMPv3 Report");
+ return;
+ }
+
+ rpt = (struct igmp3r *)igmp;
+ grh = (struct grphdr *)&rpt[1];
+ grhcnt = ntohs(rpt->igmp3r_numrec);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "%d Group Record%s:", grhcnt, (grhcnt == 1) ? "" : "s");
+ rem -= sizeof (*rpt);
+ while (grhcnt > 0 && rem >= sizeof (*grh)) {
+ rem -= sizeof (*grh);
+
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Group = %s type = %s", inet_ntop(AF_INET,
+ &grh->grphdr_group.s_addr, addrstr, INET_ADDRSTRLEN),
+ (grh->grphdr_type > MAX_IGMPV3_REPORT_TYPE) ?
+ "<unknown>" : igmpv3rpt_types[grh->grphdr_type]);
+ srccnt = ntohs(grh->grphdr_numsrc);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "%d Source Address%s:", srccnt, (srccnt == 1) ? "" : "es");
+
+ src = (struct in_addr *)&grh[1];
+ while (srccnt > 0 && rem >= sizeof (*src)) {
+ rem -= sizeof (*src);
+
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " %s", inet_ntop(AF_INET, &src->s_addr, addrstr,
+ INET_ADDRSTRLEN));
+
+ srccnt--;
+ src++;
+ }
+
+ grhcnt--;
+ auxlen = grh->grphdr_auxlen * 4;
+ rem -= auxlen;
+ grh = (struct grphdr *)((uint8_t *)src + auxlen);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ip.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ip.c
new file mode 100644
index 0000000000..8421eccda6
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ip.c
@@ -0,0 +1,1022 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <sys/stropts.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/icmp6.h>
+#include <netinet/if_ether.h>
+#include <inet/ip6.h>
+#include <inet/ipsecah.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include "snoop.h"
+
+
+/*
+ * IPv6 extension header masks. These are used by the print_ipv6_extensions()
+ * function to return information to the caller about which extension headers
+ * were processed. This can be useful if the caller wants to know if the
+ * packet is an IPv6 fragment, for example.
+ */
+#define SNOOP_HOPOPTS 0x01U
+#define SNOOP_ROUTING 0x02U
+#define SNOOP_DSTOPTS 0x04U
+#define SNOOP_FRAGMENT 0x08U
+#define SNOOP_AH 0x10U
+#define SNOOP_ESP 0x20U
+#define SNOOP_IPV6 0x40U
+
+extern char *dlc_header;
+
+static void prt_routing_hdr();
+static void prt_fragment_hdr();
+static void prt_hbh_options();
+static void prt_dest_options();
+static void print_route();
+static void print_ipoptions();
+char *getproto();
+
+/* Keep track of how many nested IP headers we have. */
+unsigned int encap_levels;
+unsigned int total_encap_levels = 1;
+
+int
+interpret_ip(flags, ip, fraglen)
+ int flags;
+ struct ip *ip;
+ int fraglen;
+{
+ char *data;
+ char buff[24];
+ boolean_t isfrag = B_FALSE;
+ boolean_t morefrag;
+ uint16_t fragoffset;
+ int hdrlen;
+ uint16_t iplen, uitmp;
+ extern char *src_name, *dst_name;
+
+ if (ip->ip_v == IPV6_VERSION) {
+ iplen = interpret_ipv6(flags, (ip6_t *)ip, fraglen);
+ return (iplen);
+ }
+
+ /* XXX Should this count for mix-and-match v4/v6 encapsulations? */
+ if (encap_levels == 0)
+ total_encap_levels = 0;
+ encap_levels++;
+ total_encap_levels++;
+
+ hdrlen = ip->ip_hl * 4;
+ data = ((char *)ip) + hdrlen;
+ iplen = ntohs(ip->ip_len) - hdrlen;
+ fraglen -= hdrlen;
+ if (fraglen > iplen)
+ fraglen = iplen;
+ if (fraglen < 0) {
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "IP truncated: header missing %d bytes", -fraglen);
+ encap_levels--;
+ return (fraglen + iplen);
+ }
+ /*
+ * We flag this as a fragment if the more fragments bit is set, or
+ * if the fragment offset is non-zero.
+ */
+ morefrag = (ntohs(ip->ip_off) & IP_MF) == 0 ? B_FALSE : B_TRUE;
+ fragoffset = (ntohs(ip->ip_off) & 0x1FFF) * 8;
+ if (morefrag || fragoffset != 0)
+ isfrag = B_TRUE;
+
+ if (encap_levels == 1) {
+ src_name = addrtoname(AF_INET, &ip->ip_src);
+ dst_name = addrtoname(AF_INET, &ip->ip_dst);
+ } /* Else we already have the src_name and dst_name we want! */
+
+ if (flags & F_SUM) {
+ if (isfrag) {
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "%s IP fragment ID=%d Offset=%-4d MF=%d TOS=0x%x "
+ "TTL=%d",
+ getproto(ip->ip_p),
+ ntohs(ip->ip_id),
+ fragoffset,
+ morefrag,
+ ip->ip_tos,
+ ip->ip_ttl);
+ } else {
+ (void) strlcpy(buff, inet_ntoa(ip->ip_dst),
+ sizeof (buff));
+ uitmp = ntohs(ip->ip_len);
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "IP D=%s S=%s LEN=%u%s, ID=%d, TOS=0x%x, TTL=%d",
+ buff,
+ inet_ntoa(ip->ip_src),
+ uitmp,
+ iplen > fraglen ? "?" : "",
+ ntohs(ip->ip_id),
+ ip->ip_tos,
+ ip->ip_ttl);
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("IP: ", "IP Header", iplen);
+ show_space();
+ (void) snprintf(get_line((char *)ip - dlc_header, 1),
+ get_line_remain(), "Version = %d", ip->ip_v);
+ (void) snprintf(get_line((char *)ip - dlc_header, 1),
+ get_line_remain(), "Header length = %d bytes", hdrlen);
+ (void) snprintf(get_line((char *)&ip->ip_tos - dlc_header, 1),
+ get_line_remain(), "Type of service = 0x%02x", ip->ip_tos);
+ (void) snprintf(get_line((char *)&ip->ip_tos - dlc_header, 1),
+ get_line_remain(), " xxx. .... = %d (precedence)",
+ ip->ip_tos >> 5);
+ (void) snprintf(get_line((char *)&ip->ip_tos - dlc_header, 1),
+ get_line_remain(), " %s",
+ getflag(ip->ip_tos, IPTOS_LOWDELAY,
+ "low delay", "normal delay"));
+ (void) snprintf(get_line((char *)&ip->ip_tos - dlc_header, 1),
+ get_line_remain(), " %s",
+ getflag(ip->ip_tos, IPTOS_THROUGHPUT,
+ "high throughput", "normal throughput"));
+ (void) snprintf(get_line((char *)&ip->ip_tos - dlc_header, 1),
+ get_line_remain(), " %s",
+ getflag(ip->ip_tos, IPTOS_RELIABILITY,
+ "high reliability", "normal reliability"));
+ (void) snprintf(get_line((char *)&ip->ip_tos - dlc_header, 1),
+ get_line_remain(), " %s",
+ getflag(ip->ip_tos, IPTOS_ECT,
+ "ECN capable transport", "not ECN capable transport"));
+ (void) snprintf(get_line((char *)&ip->ip_tos - dlc_header, 1),
+ get_line_remain(), " %s",
+ getflag(ip->ip_tos, IPTOS_CE,
+ "ECN congestion experienced",
+ "no ECN congestion experienced"));
+ /* warning: ip_len is signed in netinet/ip.h */
+ uitmp = ntohs(ip->ip_len);
+ (void) snprintf(get_line((char *)&ip->ip_len - dlc_header, 2),
+ get_line_remain(), "Total length = %u bytes%s", uitmp,
+ iplen > fraglen ? " -- truncated" : "");
+ (void) snprintf(get_line((char *)&ip->ip_id - dlc_header, 2),
+ get_line_remain(), "Identification = %d", ntohs(ip->ip_id));
+ /* warning: ip_off is signed in netinet/ip.h */
+ uitmp = ntohs(ip->ip_off);
+ (void) snprintf(get_line((char *)&ip->ip_off - dlc_header, 1),
+ get_line_remain(), "Flags = 0x%x", uitmp >> 12);
+ (void) snprintf(get_line((char *)&ip->ip_off - dlc_header, 1),
+ get_line_remain(), " %s",
+ getflag(uitmp >> 8, IP_DF >> 8,
+ "do not fragment", "may fragment"));
+ (void) snprintf(get_line((char *)&ip->ip_off - dlc_header, 1),
+ get_line_remain(), " %s",
+ getflag(uitmp >> 8, IP_MF >> 8,
+ "more fragments", "last fragment"));
+ (void) snprintf(get_line((char *)&ip->ip_off - dlc_header, 2),
+ get_line_remain(), "Fragment offset = %u bytes",
+ fragoffset);
+ (void) snprintf(get_line((char *)&ip->ip_ttl - dlc_header, 1),
+ get_line_remain(), "Time to live = %d seconds/hops",
+ ip->ip_ttl);
+ (void) snprintf(get_line((char *)&ip->ip_p - dlc_header, 1),
+ get_line_remain(), "Protocol = %d (%s)", ip->ip_p,
+ getproto(ip->ip_p));
+ /*
+ * XXX need to compute checksum and print whether it's correct
+ */
+ (void) snprintf(get_line((char *)&ip->ip_sum - dlc_header, 1),
+ get_line_remain(), "Header checksum = %04x",
+ ntohs(ip->ip_sum));
+ (void) snprintf(get_line((char *)&ip->ip_src - dlc_header, 1),
+ get_line_remain(), "Source address = %s, %s",
+ inet_ntoa(ip->ip_src), addrtoname(AF_INET, &ip->ip_src));
+ (void) snprintf(get_line((char *)&ip->ip_dst - dlc_header, 1),
+ get_line_remain(), "Destination address = %s, %s",
+ inet_ntoa(ip->ip_dst), addrtoname(AF_INET, &ip->ip_dst));
+
+ /* Print IP options - if any */
+
+ print_ipoptions(ip + 1, hdrlen - sizeof (struct ip));
+ show_space();
+ }
+
+ /*
+ * If we are in detail mode, and this is not the first fragment of
+ * a fragmented packet, print out a little line stating this.
+ * Otherwise, go to the next protocol layer only if this is not a
+ * fragment, or we are in detail mode and this is the first fragment
+ * of a fragmented packet.
+ */
+ if (flags & F_DTAIL && fragoffset != 0) {
+ (void) snprintf(get_detail_line(data - dlc_header, iplen),
+ MAXLINE,
+ "%s: [%d byte(s) of data, continuation of IP ident=%d]",
+ getproto(ip->ip_p),
+ iplen,
+ ntohs(ip->ip_id));
+ } else if (!isfrag || (flags & F_DTAIL) && isfrag && fragoffset == 0) {
+ /* go to the next protocol layer */
+
+ if (fraglen > 0) {
+ switch (ip->ip_p) {
+ case IPPROTO_IP:
+ break;
+ case IPPROTO_ENCAP:
+ (void) interpret_ip(flags, (struct ip *)data,
+ fraglen);
+ break;
+ case IPPROTO_ICMP:
+ interpret_icmp(flags, (struct icmp *)data,
+ iplen, fraglen);
+ break;
+ case IPPROTO_IGMP:
+ interpret_igmp(flags, data, iplen, fraglen);
+ break;
+ case IPPROTO_GGP:
+ break;
+ case IPPROTO_TCP:
+ interpret_tcp(flags, data, iplen, fraglen);
+ break;
+
+ case IPPROTO_ESP:
+ interpret_esp(flags, data, iplen, fraglen);
+ break;
+ case IPPROTO_AH:
+ interpret_ah(flags, data, iplen, fraglen);
+ break;
+
+ case IPPROTO_OSPF:
+ interpret_ospf(flags, data, iplen, fraglen);
+ break;
+
+ case IPPROTO_EGP:
+ case IPPROTO_PUP:
+ break;
+ case IPPROTO_UDP:
+ interpret_udp(flags, data, iplen, fraglen);
+ break;
+
+ case IPPROTO_IDP:
+ case IPPROTO_HELLO:
+ case IPPROTO_ND:
+ case IPPROTO_RAW:
+ break;
+ case IPPROTO_IPV6: /* IPV6 encap */
+ (void) interpret_ipv6(flags, (ip6_t *)data,
+ iplen);
+ break;
+ case IPPROTO_SCTP:
+ interpret_sctp(flags, data, iplen, fraglen);
+ break;
+ }
+ }
+ }
+
+ encap_levels--;
+ return (iplen);
+}
+
+int
+interpret_ipv6(flags, ip6h, fraglen)
+ int flags;
+ ip6_t *ip6h;
+ int fraglen;
+{
+ uint8_t *data;
+ int hdrlen, iplen;
+ extern char *src_name, *dst_name;
+ int version, flow, class;
+ uchar_t proto;
+ boolean_t isfrag = B_FALSE;
+ uint8_t extmask;
+ /*
+ * The print_srcname and print_dstname strings are the hostname
+ * parts of the verbose IPv6 header output, including the comma
+ * and the space after the litteral address strings.
+ */
+ char print_srcname[MAXHOSTNAMELEN + 2];
+ char print_dstname[MAXHOSTNAMELEN + 2];
+ char src_addrstr[INET6_ADDRSTRLEN];
+ char dst_addrstr[INET6_ADDRSTRLEN];
+
+ iplen = ntohs(ip6h->ip6_plen);
+ hdrlen = IPV6_HDR_LEN;
+ fraglen -= hdrlen;
+ if (fraglen < 0)
+ return (fraglen + hdrlen);
+ data = ((uint8_t *)ip6h) + hdrlen;
+
+ proto = ip6h->ip6_nxt;
+
+ src_name = addrtoname(AF_INET6, &ip6h->ip6_src);
+ dst_name = addrtoname(AF_INET6, &ip6h->ip6_dst);
+
+ /*
+ * Use endian-aware masks to extract traffic class and
+ * flowinfo. Also, flowinfo is now 20 bits and class 8
+ * rather than 24 and 4.
+ */
+ class = ntohl((ip6h->ip6_vcf & IPV6_FLOWINFO_TCLASS) >> 20);
+ flow = ntohl(ip6h->ip6_vcf & IPV6_FLOWINFO_FLOWLABEL);
+
+ /*
+ * NOTE: the F_SUM and F_DTAIL flags are mutually exclusive,
+ * so the code within the first part of the following if statement
+ * will not affect the detailed printing of the packet.
+ */
+ if (flags & F_SUM) {
+ (void) sprintf(get_sum_line(), "IPv6 S=%s D=%s LEN=%d "
+ "HOPS=%d CLASS=0x%x FLOW=0x%x",
+ src_name, dst_name, iplen, ip6h->ip6_hops, class, flow);
+ } else if (flags & F_DTAIL) {
+
+ (void) inet_ntop(AF_INET6, &ip6h->ip6_src, src_addrstr,
+ INET6_ADDRSTRLEN);
+ (void) inet_ntop(AF_INET6, &ip6h->ip6_dst, dst_addrstr,
+ INET6_ADDRSTRLEN);
+
+ version = ntohl(ip6h->ip6_vcf) >> 28;
+
+ if (strcmp(src_name, src_addrstr) == 0)
+ print_srcname[0] = '\0';
+ else
+ snprintf(print_srcname, sizeof (print_srcname),
+ ", %s", src_name);
+
+ if (strcmp(dst_name, dst_addrstr) == 0)
+ print_dstname[0] = '\0';
+ else
+ snprintf(print_dstname, sizeof (print_dstname),
+ ", %s", dst_name);
+
+ show_header("IPv6: ", "IPv6 Header", iplen);
+ show_space();
+
+ (void) sprintf(get_line((char *)ip6h - dlc_header, 1),
+ "Version = %d", version);
+ (void) sprintf(get_line((char *)ip6h - dlc_header, 1),
+ "Traffic Class = %d", class);
+ (void) sprintf(get_line((char *)&ip6h->ip6_vcf - dlc_header, 4),
+ "Flow label = 0x%x", flow);
+ (void) sprintf(get_line((char *)&ip6h->ip6_plen -
+ dlc_header, 2), "Payload length = %d", iplen);
+ (void) sprintf(get_line((char *)&ip6h->ip6_nxt -
+ dlc_header, 1), "Next Header = %d (%s)", proto,
+ getproto(proto));
+ (void) sprintf(get_line((char *)&ip6h->ip6_hops -
+ dlc_header, 1), "Hop Limit = %d", ip6h->ip6_hops);
+ (void) sprintf(get_line((char *)&ip6h->ip6_src - dlc_header, 1),
+ "Source address = %s%s", src_addrstr, print_srcname);
+ (void) sprintf(get_line((char *)&ip6h->ip6_dst - dlc_header, 1),
+ "Destination address = %s%s", dst_addrstr, print_dstname);
+
+ show_space();
+ }
+
+ /*
+ * Print IPv6 Extension Headers, or skip them in the summary case.
+ * Set isfrag to true if one of the extension headers encounterred
+ * was a fragment header.
+ */
+ if (proto == IPPROTO_HOPOPTS || proto == IPPROTO_DSTOPTS ||
+ proto == IPPROTO_ROUTING || proto == IPPROTO_FRAGMENT) {
+ extmask = print_ipv6_extensions(flags, &data, &proto, &iplen,
+ &fraglen);
+ if ((extmask & SNOOP_FRAGMENT) != 0) {
+ isfrag = B_TRUE;
+ }
+ }
+
+ /*
+ * We only want to print upper layer information if this is not
+ * a fragment, or if we're printing in detail. Note that the
+ * proto variable will be set to IPPROTO_NONE if this is a fragment
+ * with a non-zero fragment offset.
+ */
+ if (!isfrag || flags & F_DTAIL) {
+ /* go to the next protocol layer */
+
+ switch (proto) {
+ case IPPROTO_IP:
+ break;
+ case IPPROTO_ENCAP:
+ (void) interpret_ip(flags, (struct ip *)data, fraglen);
+ break;
+ case IPPROTO_ICMPV6:
+ interpret_icmpv6(flags, (icmp6_t *)data, iplen,
+ fraglen);
+ break;
+ case IPPROTO_IGMP:
+ interpret_igmp(flags, data, iplen, fraglen);
+ break;
+ case IPPROTO_GGP:
+ break;
+ case IPPROTO_TCP:
+ interpret_tcp(flags, data, iplen, fraglen);
+ break;
+ case IPPROTO_ESP:
+ interpret_esp(flags, data, iplen, fraglen);
+ break;
+ case IPPROTO_AH:
+ interpret_ah(flags, data, iplen, fraglen);
+ break;
+ case IPPROTO_EGP:
+ case IPPROTO_PUP:
+ break;
+ case IPPROTO_UDP:
+ interpret_udp(flags, data, iplen, fraglen);
+ break;
+ case IPPROTO_IDP:
+ case IPPROTO_HELLO:
+ case IPPROTO_ND:
+ case IPPROTO_RAW:
+ break;
+ case IPPROTO_IPV6:
+ (void) interpret_ipv6(flags, (ip6_t *)data, iplen);
+ break;
+ case IPPROTO_SCTP:
+ interpret_sctp(flags, data, iplen, fraglen);
+ break;
+ case IPPROTO_OSPF:
+ interpret_ospf6(flags, data, iplen, fraglen);
+ break;
+ }
+ }
+
+ return (iplen);
+}
+
+/*
+ * ip_ext: data including the extension header.
+ * iplen: length of the data remaining in the packet.
+ * Returns a mask of IPv6 extension headers it processed.
+ */
+uint8_t
+print_ipv6_extensions(int flags, uint8_t **hdr, uint8_t *next, int *iplen,
+ int *fraglen)
+{
+ uint8_t *data_ptr;
+ uchar_t proto = *next;
+ boolean_t is_extension_header;
+ struct ip6_hbh *ipv6ext_hbh;
+ struct ip6_dest *ipv6ext_dest;
+ struct ip6_rthdr *ipv6ext_rthdr;
+ struct ip6_frag *ipv6ext_frag;
+ uint32_t exthdrlen;
+ uint8_t extmask = 0;
+
+ if ((hdr == NULL) || (*hdr == NULL) || (next == NULL) || (iplen == 0))
+ return (0);
+
+ data_ptr = *hdr;
+ is_extension_header = B_TRUE;
+ while (is_extension_header) {
+
+ /*
+ * There must be at least enough data left to read the
+ * next header and header length fields from the next
+ * header.
+ */
+ if (*fraglen < 2) {
+ proto = IPPROTO_NONE;
+ return (extmask);
+ }
+
+ switch (proto) {
+ case IPPROTO_HOPOPTS:
+ ipv6ext_hbh = (struct ip6_hbh *)data_ptr;
+ exthdrlen = 8 + ipv6ext_hbh->ip6h_len * 8;
+ if (*fraglen <= exthdrlen) {
+ proto = IPPROTO_NONE;
+ return (extmask);
+ }
+ prt_hbh_options(flags, ipv6ext_hbh);
+ extmask |= SNOOP_HOPOPTS;
+ proto = ipv6ext_hbh->ip6h_nxt;
+ break;
+ case IPPROTO_DSTOPTS:
+ ipv6ext_dest = (struct ip6_dest *)data_ptr;
+ exthdrlen = 8 + ipv6ext_dest->ip6d_len * 8;
+ if (*fraglen <= exthdrlen) {
+ proto = IPPROTO_NONE;
+ return (extmask);
+ }
+ prt_dest_options(flags, ipv6ext_dest);
+ extmask |= SNOOP_DSTOPTS;
+ proto = ipv6ext_dest->ip6d_nxt;
+ break;
+ case IPPROTO_ROUTING:
+ ipv6ext_rthdr = (struct ip6_rthdr *)data_ptr;
+ exthdrlen = 8 + ipv6ext_rthdr->ip6r_len * 8;
+ if (*fraglen <= exthdrlen) {
+ proto = IPPROTO_NONE;
+ return (extmask);
+ }
+ prt_routing_hdr(flags, ipv6ext_rthdr);
+ extmask |= SNOOP_ROUTING;
+ proto = ipv6ext_rthdr->ip6r_nxt;
+ break;
+ case IPPROTO_FRAGMENT:
+ ipv6ext_frag = (struct ip6_frag *)data_ptr;
+ exthdrlen = sizeof (struct ip6_frag);
+ if (*fraglen <= exthdrlen) {
+ proto = IPPROTO_NONE;
+ return (extmask);
+ }
+ prt_fragment_hdr(flags, ipv6ext_frag);
+ extmask |= SNOOP_FRAGMENT;
+ /*
+ * If this is not the first fragment, forget about
+ * the rest of the packet, snoop decoding is
+ * stateless.
+ */
+ if ((ipv6ext_frag->ip6f_offlg & IP6F_OFF_MASK) != 0)
+ proto = IPPROTO_NONE;
+ else
+ proto = ipv6ext_frag->ip6f_nxt;
+ break;
+ default:
+ is_extension_header = B_FALSE;
+ break;
+ }
+
+ if (is_extension_header) {
+ *iplen -= exthdrlen;
+ *fraglen -= exthdrlen;
+ data_ptr += exthdrlen;
+ }
+ }
+
+ *next = proto;
+ *hdr = data_ptr;
+ return (extmask);
+}
+
+static void
+print_ipoptions(opt, optlen)
+ uchar_t *opt;
+ int optlen;
+{
+ int len;
+ char *line;
+
+ if (optlen <= 0) {
+ (void) sprintf(get_line((char *)&opt - dlc_header, 1),
+ "No options");
+ return;
+ }
+
+ (void) sprintf(get_line((char *)&opt - dlc_header, 1),
+ "Options: (%d bytes)", optlen);
+
+ while (optlen > 0) {
+ line = get_line((char *)&opt - dlc_header, 1);
+ len = opt[1];
+ switch (opt[0]) {
+ case IPOPT_EOL:
+ (void) strcpy(line, " - End of option list");
+ return;
+ case IPOPT_NOP:
+ (void) strcpy(line, " - No op");
+ len = 1;
+ break;
+ case IPOPT_RR:
+ (void) sprintf(line, " - Record route (%d bytes)",
+ len);
+ print_route(opt);
+ break;
+ case IPOPT_TS:
+ (void) sprintf(line, " - Time stamp (%d bytes)", len);
+ break;
+ case IPOPT_SECURITY:
+ (void) sprintf(line, " - Security (%d bytes)", len);
+ break;
+ case IPOPT_LSRR:
+ (void) sprintf(line,
+ " - Loose source route (%d bytes)", len);
+ print_route(opt);
+ break;
+ case IPOPT_SATID:
+ (void) sprintf(line, " - SATNET Stream id (%d bytes)",
+ len);
+ break;
+ case IPOPT_SSRR:
+ (void) sprintf(line,
+ " - Strict source route, (%d bytes)", len);
+ print_route(opt);
+ break;
+ default:
+ sprintf(line, " - Option %d (unknown - %d bytes) %s",
+ opt[0], len, tohex((char *)&opt[2], len - 2));
+ break;
+ }
+ if (len <= 0) {
+ (void) sprintf(line, " - Incomplete option len %d",
+ len);
+ break;
+ }
+ opt += len;
+ optlen -= len;
+ }
+}
+
+static void
+print_route(opt)
+ uchar_t *opt;
+{
+ int len, pointer;
+ struct in_addr addr;
+ char *line;
+
+ len = opt[1];
+ pointer = opt[2];
+
+ (void) sprintf(get_line((char *)(&opt + 2) - dlc_header, 1),
+ " Pointer = %d", pointer);
+
+ pointer -= IPOPT_MINOFF;
+ opt += (IPOPT_OFFSET + 1);
+ len -= (IPOPT_OFFSET + 1);
+
+ while (len > 0) {
+ line = get_line((char *)&(opt) - dlc_header, 4);
+ memcpy((char *)&addr, opt, sizeof (addr));
+ if (addr.s_addr == INADDR_ANY)
+ (void) strcpy(line, " -");
+ else
+ (void) sprintf(line, " %s",
+ addrtoname(AF_INET, &addr));
+ if (pointer == 0)
+ (void) strcat(line, " <-- (current)");
+
+ opt += sizeof (addr);
+ len -= sizeof (addr);
+ pointer -= sizeof (addr);
+ }
+}
+
+char *
+getproto(p)
+ int p;
+{
+ switch (p) {
+ case IPPROTO_HOPOPTS: return ("IPv6-HopOpts");
+ case IPPROTO_IPV6: return ("IPv6");
+ case IPPROTO_ROUTING: return ("IPv6-Route");
+ case IPPROTO_FRAGMENT: return ("IPv6-Frag");
+ case IPPROTO_RSVP: return ("RSVP");
+ case IPPROTO_ENCAP: return ("IP-in-IP");
+ case IPPROTO_AH: return ("AH");
+ case IPPROTO_ESP: return ("ESP");
+ case IPPROTO_ICMP: return ("ICMP");
+ case IPPROTO_ICMPV6: return ("ICMPv6");
+ case IPPROTO_DSTOPTS: return ("IPv6-DstOpts");
+ case IPPROTO_IGMP: return ("IGMP");
+ case IPPROTO_GGP: return ("GGP");
+ case IPPROTO_TCP: return ("TCP");
+ case IPPROTO_EGP: return ("EGP");
+ case IPPROTO_PUP: return ("PUP");
+ case IPPROTO_UDP: return ("UDP");
+ case IPPROTO_IDP: return ("IDP");
+ case IPPROTO_HELLO: return ("HELLO");
+ case IPPROTO_ND: return ("ND");
+ case IPPROTO_EON: return ("EON");
+ case IPPROTO_RAW: return ("RAW");
+ case IPPROTO_OSPF: return ("OSPF");
+ default: return ("");
+ }
+}
+
+static void
+prt_routing_hdr(flags, ipv6ext_rthdr)
+ int flags;
+ struct ip6_rthdr *ipv6ext_rthdr;
+{
+ uint8_t nxt_hdr;
+ uint8_t type;
+ uint32_t len;
+ uint8_t segleft;
+ uint32_t numaddrs;
+ int i;
+ struct ip6_rthdr0 *ipv6ext_rthdr0;
+ struct in6_addr *addrs;
+ char addr[INET6_ADDRSTRLEN];
+
+ /* in summary mode, we don't do anything. */
+ if (flags & F_SUM) {
+ return;
+ }
+
+ nxt_hdr = ipv6ext_rthdr->ip6r_nxt;
+ type = ipv6ext_rthdr->ip6r_type;
+ len = 8 * (ipv6ext_rthdr->ip6r_len + 1);
+ segleft = ipv6ext_rthdr->ip6r_segleft;
+
+ show_header("IPv6-Route: ", "IPv6 Routing Header", 0);
+ show_space();
+
+ (void) sprintf(get_line((char *)ipv6ext_rthdr - dlc_header, 1),
+ "Next header = %d (%s)", nxt_hdr, getproto(nxt_hdr));
+ (void) sprintf(get_line((char *)ipv6ext_rthdr - dlc_header, 1),
+ "Header length = %d", len);
+ (void) sprintf(get_line((char *)ipv6ext_rthdr - dlc_header, 1),
+ "Routing type = %d", type);
+ (void) sprintf(get_line((char *)ipv6ext_rthdr - dlc_header, 1),
+ "Segments left = %d", segleft);
+
+ if (type == IPV6_RTHDR_TYPE_0) {
+ /*
+ * XXX This loop will print all addresses in the routing header,
+ * XXX not just the segments left.
+ * XXX (The header length field is twice the number of
+ * XXX addresses)
+ * XXX At some future time, we may want to change this
+ * XXX to differentiate between the hops yet to do
+ * XXX and the hops already taken.
+ */
+ ipv6ext_rthdr0 = (struct ip6_rthdr0 *)ipv6ext_rthdr;
+ numaddrs = ipv6ext_rthdr0->ip6r0_len / 2;
+ addrs = (struct in6_addr *)(ipv6ext_rthdr0 + 1);
+ for (i = 0; i < numaddrs; i++) {
+ (void) inet_ntop(AF_INET6, &addrs[i], addr,
+ INET6_ADDRSTRLEN);
+ (void) sprintf(get_line((char *)ipv6ext_rthdr -
+ dlc_header, 1),
+ "address[%d]=%s", i, addr);
+ }
+ }
+
+ show_space();
+}
+
+static void
+prt_fragment_hdr(flags, ipv6ext_frag)
+ int flags;
+ struct ip6_frag *ipv6ext_frag;
+{
+ boolean_t morefrag;
+ uint16_t fragoffset;
+ uint8_t nxt_hdr;
+ uint32_t fragident;
+
+ /* extract the various fields from the fragment header */
+ nxt_hdr = ipv6ext_frag->ip6f_nxt;
+ morefrag = (ipv6ext_frag->ip6f_offlg & IP6F_MORE_FRAG) == 0
+ ? B_FALSE : B_TRUE;
+ fragoffset = ntohs(ipv6ext_frag->ip6f_offlg & IP6F_OFF_MASK);
+ fragident = ntohl(ipv6ext_frag->ip6f_ident);
+
+ if (flags & F_SUM) {
+ (void) sprintf(get_sum_line(),
+ "IPv6 fragment ID=%d Offset=%-4d MF=%d",
+ fragident,
+ fragoffset,
+ morefrag);
+ } else { /* F_DTAIL */
+ show_header("IPv6-Frag: ", "IPv6 Fragment Header", 0);
+ show_space();
+
+ (void) sprintf(get_line((char *)ipv6ext_frag - dlc_header, 1),
+ "Next Header = %d (%s)", nxt_hdr, getproto(nxt_hdr));
+ (void) sprintf(get_line((char *)ipv6ext_frag - dlc_header, 1),
+ "Fragment Offset = %d", fragoffset);
+ (void) sprintf(get_line((char *)ipv6ext_frag - dlc_header, 1),
+ "More Fragments Flag = %s", morefrag ? "true" : "false");
+ (void) sprintf(get_line((char *)ipv6ext_frag - dlc_header, 1),
+ "Identification = %d", fragident);
+
+ show_space();
+ }
+}
+
+static void
+prt_hbh_options(flags, ipv6ext_hbh)
+ int flags;
+ struct ip6_hbh *ipv6ext_hbh;
+{
+ uint8_t *data;
+ uint32_t len, olen;
+ uint8_t op_type;
+ uint8_t op_len;
+ uint8_t nxt_hdr;
+
+ /* in summary mode, we don't do anything. */
+ if (flags & F_SUM) {
+ return;
+ }
+
+ show_header("IPv6-HopOpts: ", "IPv6 Hop-by-Hop Options Header", 0);
+ show_space();
+
+ /*
+ * Store the lengh of this ext hdr in bytes. The caller has
+ * ensured that there is at least len bytes of data left.
+ */
+ len = ipv6ext_hbh->ip6h_len * 8 + 8;
+
+ data = (uint8_t *)ipv6ext_hbh + 2;
+ len -= 2;
+
+ nxt_hdr = ipv6ext_hbh->ip6h_nxt;
+ (void) sprintf(get_line((char *)ipv6ext_hbh - dlc_header, 1),
+ "Next Header = %u (%s)", nxt_hdr, getproto(nxt_hdr));
+
+ while (len > 0) {
+ GETINT8(op_type, data);
+ olen = len;
+ switch (op_type) {
+ case IP6OPT_PAD1:
+ (void) sprintf(get_line((char *)ipv6ext_hbh -
+ dlc_header, 1),
+ "pad1 option ");
+ len--;
+ break;
+ case IP6OPT_PADN:
+ GETINT8(op_len, data);
+ (void) sprintf(get_line((char *)ipv6ext_hbh -
+ dlc_header, 1),
+ "padN option len = %u", op_len);
+ data += op_len; /* skip pads */
+ len -= (op_len + 2);
+ break;
+ case IP6OPT_JUMBO: {
+ uint32_t payload_len;
+
+ GETINT8(op_len, data);
+ (void) sprintf(get_line((char *)ipv6ext_hbh -
+ dlc_header, 1),
+ "Jumbo Payload Option len = %u bytes", op_len);
+ if (op_len == sizeof (uint32_t)) {
+ GETINT32(payload_len, data);
+ (void) sprintf(get_line((char *)ipv6ext_hbh -
+ dlc_header, 1),
+ "Jumbo Payload Length = %u bytes",
+ payload_len);
+ } else {
+ data += op_len;
+ }
+ len -= (op_len + 2);
+ break;
+ }
+ case IP6OPT_ROUTER_ALERT: {
+ uint16_t value;
+ const char *label[] = {"MLD", "RSVP", "AN"};
+
+ GETINT8(op_len, data);
+ (void) snprintf(get_line((char *)ipv6ext_hbh -
+ dlc_header, 1), get_line_remain(),
+ "Router Alert Option len = %u bytes", op_len);
+ if (op_len == sizeof (uint16_t)) {
+ GETINT16(value, data);
+ (void) snprintf(get_line((char *)ipv6ext_hbh -
+ dlc_header, 1), get_line_remain(),
+ "Alert Type = %d (%s)", value,
+ value < sizeof (label) / sizeof (label[0]) ?
+ label[value] : "???");
+ } else {
+ data += op_len;
+ }
+ len -= (op_len + 2);
+ break;
+ }
+ default:
+ GETINT8(op_len, data);
+ (void) sprintf(get_line((char *)ipv6ext_hbh -
+ dlc_header, 1),
+ "Option type = %u, len = %u", op_type, op_len);
+ data += op_len;
+ len -= (op_len + 2);
+ }
+ /* check for corrupt length */
+ if (olen <= len) {
+ (void) sprintf(get_line((char *)ipv6ext_hbh -
+ dlc_header, 1),
+ "Incomplete option len = %u, len = %u", op_type,
+ len);
+ break;
+ }
+ }
+
+ show_space();
+}
+
+static void
+prt_dest_options(flags, ipv6ext_dest)
+ int flags;
+ struct ip6_dest *ipv6ext_dest;
+{
+ uint8_t *data;
+ uint32_t len, olen;
+ uint8_t op_type;
+ uint32_t op_len;
+ uint8_t nxt_hdr;
+ uint8_t value;
+
+ /* in summary mode, we don't do anything. */
+ if (flags & F_SUM) {
+ return;
+ }
+
+ show_header("IPv6-DstOpts: ", "IPv6 Destination Options Header", 0);
+ show_space();
+
+ /*
+ * Store the length of this ext hdr in bytes. The caller has
+ * ensured that there is at least len bytes of data left.
+ */
+ len = ipv6ext_dest->ip6d_len * 8 + 8;
+
+ data = (uint8_t *)ipv6ext_dest + 2; /* skip hdr/len */
+ len -= 2;
+
+ nxt_hdr = ipv6ext_dest->ip6d_nxt;
+ (void) sprintf(get_line((char *)ipv6ext_dest - dlc_header, 1),
+ "Next Header = %u (%s)", nxt_hdr, getproto(nxt_hdr));
+
+ while (len > 0) {
+ GETINT8(op_type, data);
+ olen = len;
+ switch (op_type) {
+ case IP6OPT_PAD1:
+ (void) sprintf(get_line((char *)ipv6ext_dest -
+ dlc_header, 1),
+ "pad1 option ");
+ len--;
+ break;
+ case IP6OPT_PADN:
+ GETINT8(op_len, data);
+ (void) sprintf(get_line((char *)ipv6ext_dest -
+ dlc_header, 1),
+ "padN option len = %u", op_len);
+ data += op_len;
+ len -= (op_len + 2);
+ break;
+ case IP6OPT_TUNNEL_LIMIT:
+ GETINT8(op_len, data);
+ GETINT8(value, data);
+ (void) sprintf(get_line((char *)ipv6ext_dest -
+ dlc_header, 1),
+ "tunnel encapsulation limit len = %d, value = %d",
+ op_len, value);
+ len -= (op_len + 2);
+ break;
+ default:
+ GETINT8(op_len, data);
+ (void) sprintf(get_line((char *)ipv6ext_dest -
+ dlc_header, 1),
+ "Option type = %u, len = %u", op_type, op_len);
+ data += op_len;
+ len -= (op_len + 2);
+ }
+ /* check for corrupt length */
+ if (olen <= len) {
+ (void) sprintf(get_line((char *)ipv6ext_dest -
+ dlc_header, 1),
+ "Incomplete option len = %u, len = %u", op_type,
+ len);
+ break;
+ }
+ }
+
+ show_space();
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ipaddr.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ipaddr.c
new file mode 100644
index 0000000000..ee34a06648
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ipaddr.c
@@ -0,0 +1,431 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1991-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI" /* SunOS */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <netinet/ip.h>
+#include <netdb.h>
+#include <string.h>
+#include <signal.h>
+#include <setjmp.h>
+
+sigjmp_buf nisjmp;
+
+#define MAXHASH 1024 /* must be a power of 2 */
+
+#define SEPARATORS " \t\n"
+
+struct hostdata {
+ struct hostdata *h_next;
+ char *h_hostname;
+ int h_pktsout;
+ int h_pktsin;
+};
+
+struct hostdata4 {
+ struct hostdata4 *h4_next;
+ char *h4_hostname;
+ int h4_pktsout;
+ int h4_pktsin;
+ struct in_addr h4_addr;
+};
+
+struct hostdata6 {
+ struct hostdata6 *h6_next;
+ char *h6_hostname;
+ int h6_pktsout;
+ int h6_pktsin;
+ struct in6_addr h6_addr;
+};
+
+struct hostdata *addhost();
+
+struct hostdata4 *h_table4[MAXHASH];
+struct hostdata6 *h_table6[MAXHASH];
+
+#define iphash(e) ((e) & (MAXHASH-1))
+
+static void
+wakeup(n)
+ int n;
+{
+ siglongjmp(nisjmp, 1);
+}
+
+extern char *inet_ntoa();
+extern boolean_t rflg;
+
+static struct hostdata *
+iplookup(struct in_addr ipaddr)
+{
+ register struct hostdata4 *h;
+ struct hostent *hp = NULL;
+ struct netent *np;
+ int error_num;
+ struct hostdata *retval;
+
+ for (h = h_table4[iphash(ipaddr.s_addr)]; h; h = h->h4_next) {
+ if (h->h4_addr.s_addr == ipaddr.s_addr)
+ return ((struct hostdata *)h);
+ }
+
+ /* not found. Put it in */
+
+ if (ipaddr.s_addr == htonl(INADDR_BROADCAST))
+ return (addhost(AF_INET, &ipaddr, "BROADCAST", NULL));
+ if (ipaddr.s_addr == htonl(INADDR_ANY))
+ return (addhost(AF_INET, &ipaddr, "OLD-BROADCAST", NULL));
+
+ /*
+ * Set an alarm here so we don't get held up by
+ * an unresponsive name server.
+ * Give it 3 sec to do its work.
+ */
+ if (! rflg && sigsetjmp(nisjmp, 1) == 0) {
+ (void) snoop_alarm(3, wakeup);
+ hp = getipnodebyaddr((char *)&ipaddr, sizeof (int),
+ AF_INET, &error_num);
+ if (hp == NULL && inet_lnaof(ipaddr) == 0) {
+ np = getnetbyaddr(inet_netof(ipaddr), AF_INET);
+ if (np)
+ return (addhost(AF_INET, &ipaddr, np->n_name,
+ np->n_aliases));
+ }
+ (void) snoop_alarm(0, wakeup);
+ }
+
+ retval = addhost(AF_INET, &ipaddr,
+ hp ? hp->h_name : inet_ntoa(ipaddr),
+ hp ? hp->h_aliases : NULL);
+ if (hp != NULL)
+ freehostent(hp);
+ return (retval);
+}
+
+static struct hostdata *
+ip6lookup(ip6addr)
+ struct in6_addr *ip6addr;
+{
+ struct hostdata6 *h;
+ struct hostent *hp = NULL;
+ int error_num;
+ char addrstr[INET6_ADDRSTRLEN];
+ const char *addname;
+ struct hostdata *retval;
+
+ for (h = h_table6[iphash(((uint32_t *)ip6addr)[3])]; h;
+ h = h->h6_next) {
+ if (IN6_ARE_ADDR_EQUAL(&h->h6_addr, ip6addr))
+ return ((struct hostdata *)h);
+ }
+
+ /* not in the hash table, put it in */
+ if (IN6_IS_ADDR_UNSPECIFIED(ip6addr))
+ return (addhost(AF_INET6, ip6addr, "UNSPECIFIED", NULL));
+
+ /*
+ * Set an alarm here so we don't get held up by
+ * an unresponsive name server.
+ * Give it 3 sec to do its work.
+ */
+ if (! rflg && sigsetjmp(nisjmp, 1) == 0) {
+ (void) snoop_alarm(3, wakeup);
+ hp = getipnodebyaddr(ip6addr, sizeof (struct in6_addr),
+ AF_INET6, &error_num);
+ (void) snoop_alarm(0, wakeup);
+ } else {
+ hp = NULL;
+ }
+
+ if (hp != NULL)
+ addname = hp->h_name;
+ else {
+ (void) inet_ntop(AF_INET6, ip6addr, addrstr, INET6_ADDRSTRLEN);
+ addname = addrstr;
+ }
+
+ retval = addhost(AF_INET6, ip6addr, addname, hp ? hp->h_aliases : NULL);
+ if (hp != NULL)
+ freehostent(hp);
+ return (retval);
+}
+
+static struct hostdata *
+addhost(family, ipaddr, name, aliases)
+ int family;
+ void *ipaddr;
+ char *name;
+ char **aliases;
+{
+ register struct hostdata **hp, *n = NULL;
+ extern FILE *namefile;
+ int hashval;
+ static char aname[128];
+ char *np;
+ static struct hostdata h;
+ int ind;
+
+ switch (family) {
+ case AF_INET:
+ n = (struct hostdata *)malloc(sizeof (struct hostdata4));
+ if (n == NULL)
+ goto alloc_failed;
+
+ memset(n, 0, sizeof (struct hostdata4));
+ n->h_hostname = strdup(name);
+ if (n->h_hostname == NULL)
+ goto alloc_failed;
+
+ ((struct hostdata4 *)n)->h4_addr = *(struct in_addr *)ipaddr;
+ hashval = ((struct in_addr *)ipaddr)->s_addr;
+ hp = (struct hostdata **)&h_table4[iphash(hashval)];
+ break;
+ case AF_INET6:
+ n = (struct hostdata *)malloc(sizeof (struct hostdata6));
+ if (n == NULL)
+ goto alloc_failed;
+
+ memset(n, 0, sizeof (struct hostdata6));
+ n->h_hostname = strdup(name);
+ if (n->h_hostname == NULL)
+ goto alloc_failed;
+
+ memcpy(&((struct hostdata6 *)n)->h6_addr, ipaddr,
+ sizeof (struct in6_addr));
+ hashval = ((int *)ipaddr)[3];
+ hp = (struct hostdata **)&h_table6[iphash(hashval)];
+ break;
+ default:
+ fprintf(stderr, "snoop: ERROR: Unknown address family: %d",
+ family);
+ exit(1);
+ }
+
+ n->h_next = *hp;
+ *hp = n;
+
+ if (namefile != NULL) {
+ if (family == AF_INET) {
+ np = inet_ntoa(*(struct in_addr *)ipaddr);
+ if (np) {
+ (void) fprintf(namefile, "%s\t%s", np, name);
+ if (aliases) {
+ for (ind = 0;
+ aliases[ind] != NULL;
+ ind++) {
+ (void) fprintf(namefile, " %s",
+ aliases[ind]);
+ }
+ }
+ (void) fprintf(namefile, "\n");
+ }
+ } else if (family == AF_INET6) {
+ np = (char *)inet_ntop(AF_INET6, (void *)ipaddr, aname,
+ sizeof (aname));
+ if (np) {
+ (void) fprintf(namefile, "%s\t%s", np, name);
+ if (aliases) {
+ for (ind = 0;
+ aliases[ind] != NULL;
+ ind++) {
+ (void) fprintf(namefile, " %s",
+ aliases[ind]);
+ }
+ }
+ (void) fprintf(namefile, "\n");
+ }
+ } else {
+ (void) fprintf(stderr, "addhost: unknown family %d\n",
+ family);
+ }
+ }
+ return (n);
+
+alloc_failed:
+ if (n)
+ free(n);
+ (void) fprintf(stderr, "addhost: no mem\n");
+
+ aname[0] = '\0';
+ memset(&h, 0, sizeof (struct hostdata));
+ h.h_hostname = aname;
+ return (&h);
+}
+
+char *
+addrtoname(family, ipaddr)
+ int family;
+ void *ipaddr;
+{
+ switch (family) {
+ case AF_INET:
+ return (iplookup(*(struct in_addr *)ipaddr)->h_hostname);
+ case AF_INET6:
+ return (ip6lookup((struct in6_addr *)ipaddr)->h_hostname);
+ default:
+ fprintf(stderr, "snoop: ERROR: unknown address family: %d\n",
+ family);
+ exit(1);
+ }
+}
+
+void
+load_names(fname)
+ char *fname;
+{
+ char buf[1024];
+ char *addr, *name, *alias;
+ FILE *f;
+ unsigned int addrv4;
+ struct in6_addr addrv6;
+ int family;
+ void *naddr;
+
+ (void) fprintf(stderr, "Loading name file %s\n", fname);
+ f = fopen(fname, "r");
+ if (f == NULL) {
+ perror(fname);
+ return;
+ }
+
+ while (fgets(buf, 1024, f) != NULL) {
+ addr = strtok(buf, SEPARATORS);
+ if (addr == NULL || *addr == '#')
+ continue;
+ if (inet_pton(AF_INET6, addr, (void *)&addrv6) == 1) {
+ family = AF_INET6;
+ naddr = (void *)&addrv6;
+ } else if ((addrv4 = inet_addr(addr)) != -1) {
+ family = AF_INET;
+ naddr = (void *)&addrv4;
+ }
+ name = strtok(NULL, SEPARATORS);
+ if (name == NULL)
+ continue;
+ while ((alias = strtok(NULL, SEPARATORS)) && (*alias != '#')) {
+ (void) addhost(family, naddr, alias, NULL);
+ }
+ (void) addhost(family, naddr, name, NULL);
+ /* Note: certain addresses such as broadcast are skipped */
+ }
+
+ (void) fclose(f);
+}
+
+/*
+ * lgetipnodebyname: looks up hostname in cached address data. This allows
+ * filtering on hostnames from the .names file to work properly, and
+ * avoids name clashes between domains. Note that only the first of the
+ * ipv4, ipv6, or v4mapped address will be returned, because the
+ * cache does not contain information on multi-homed hosts.
+ */
+/*ARGSUSED*/
+struct hostent *
+lgetipnodebyname(const char *name, int af, int flags, int *error_num)
+{
+ int i;
+ struct hostdata4 *h;
+ struct hostdata6 *h6;
+ static struct hostent he; /* host entry */
+ static struct in6_addr h46_addr[MAXADDRS]; /* v4mapped address */
+ static char h_name[MAXHOSTNAMELEN]; /* hostname */
+ static char *list[MAXADDRS]; /* addr_list array */
+ struct hostent *hp = &he;
+ int ind;
+
+ (void) memset((char *)hp, 0, sizeof (struct hostent));
+ hp->h_name = h_name;
+ h_name[0] = '\0';
+ strcpy(h_name, name);
+
+ hp->h_addrtype = AF_INET6;
+
+ hp->h_addr_list = list;
+ for (i = 0; i < MAXADDRS; i++)
+ hp->h_addr_list[i] = NULL;
+ ind = 0;
+
+ /* ipv6 lookup */
+ if (af == AF_INET6) {
+ hp->h_length = sizeof (struct in6_addr);
+ for (i = 0; i < MAXHASH; i++) {
+ for (h6 = h_table6[i]; h6; h6 = h6->h6_next) {
+ if (strcmp(name, h6->h6_hostname) == 0) {
+ if (ind >= MAXADDRS - 1) {
+ /* too many addresses */
+ return (hp);
+ }
+ /* found ipv6 addr */
+ hp->h_addr_list[ind] =
+ (char *)&h6->h6_addr;
+ ind++;
+ }
+ }
+ }
+ }
+ /* ipv4 or v4mapped lookup */
+ if (af == AF_INET || (flags & AI_ALL)) {
+ for (i = 0; i < MAXHASH; i++) {
+ for (h = h_table4[i]; h; h = h->h4_next) {
+ if (strcmp(name, h->h4_hostname) == 0) {
+ if (ind >= MAXADDRS - 1) {
+ /* too many addresses */
+ return (hp);
+ }
+ if (af == AF_INET) {
+ /* found ipv4 addr */
+ hp->h_addrtype = AF_INET;
+ hp->h_length =
+ sizeof (struct in_addr);
+ hp->h_addr_list[ind] =
+ (char *)&h->h4_addr;
+ ind++;
+ } else {
+ /* found v4mapped addr */
+ hp->h_length =
+ sizeof (struct in6_addr);
+ hp->h_addr_list[ind] =
+ (char *)&h46_addr[ind];
+ IN6_INADDR_TO_V4MAPPED(
+ &h->h4_addr,
+ &h46_addr[ind]);
+ ind++;
+ }
+ }
+ }
+ }
+ }
+ return (ind > 0 ? hp : NULL);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ipsec.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ipsec.c
new file mode 100644
index 0000000000..c56c88049a
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ipsec.c
@@ -0,0 +1,234 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/sysmacros.h>
+#include <sys/time.h>
+
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/icmp6.h>
+#include <netinet/if_ether.h>
+#include <inet/ipsecesp.h>
+#include <inet/ipsecah.h>
+#include "snoop.h"
+
+extern char *dlc_header;
+
+int
+interpret_esp(int flags, uint8_t *hdr, int iplen, int fraglen)
+{
+ esph_t *esph = (esph_t *)hdr;
+ esph_t *aligned_esph;
+ esph_t storage; /* In case hdr isn't aligned. */
+ char *line;
+
+ if (fraglen < sizeof (esph_t))
+ return; /* incomplete header */
+
+ if (!IS_P2ALIGNED(hdr, 4)) {
+ aligned_esph = &storage;
+ bcopy(hdr, aligned_esph, sizeof (esph_t));
+ } else {
+ aligned_esph = esph;
+ }
+
+ if (flags & F_SUM) {
+ line = (char *)get_sum_line();
+ /*
+ * sprintf() is safe because line guarantees us 80 columns,
+ * and SPI and replay certainly won't exceed that.
+ */
+ (void) sprintf(line, "ESP SPI=0x%x Replay=%u",
+ ntohl(aligned_esph->esph_spi),
+ ntohl(aligned_esph->esph_replay));
+ line += strlen(line);
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("ESP: ", "Encapsulating Security Payload",
+ sizeof (esph_t));
+ show_space();
+ /*
+ * sprintf() is safe because get_line guarantees us 80 columns,
+ * and SPI and replay certainly won't exceed that.
+ */
+ (void) sprintf(get_line((char *)&esph->esph_spi - dlc_header,
+ 4), "SPI = 0x%x", ntohl(aligned_esph->esph_spi));
+ (void) sprintf(get_line((char *)&esph->esph_replay -
+ dlc_header, 4), "Replay = %u",
+ ntohl(aligned_esph->esph_replay));
+ (void) sprintf(get_line((char *)(esph + 1) - dlc_header,
+ 4), " ....ENCRYPTED DATA....");
+ }
+
+ return (sizeof (esph_t));
+}
+
+int
+interpret_ah(int flags, uint8_t *hdr, int iplen, int fraglen)
+{
+ ah_t *ah = (ah_t *)hdr;
+ ah_t *aligned_ah;
+ ah_t storage; /* In case hdr isn't aligned. */
+ char *line, *buff;
+ uint_t ahlen, auth_data_len;
+ uint8_t *auth_data, *data;
+ int new_iplen;
+ uint8_t proto;
+
+ if (fraglen < sizeof (ah_t))
+ return; /* incomplete header */
+
+ if (!IS_P2ALIGNED(hdr, 4)) {
+ aligned_ah = (ah_t *)&storage;
+ bcopy(hdr, storage, sizeof (ah_t));
+ } else {
+ aligned_ah = ah;
+ }
+
+ /*
+ * "+ 8" is for the "constant" part that's not included in the AH
+ * length.
+ *
+ * The AH RFC specifies the length field in "length in 4-byte units,
+ * not counting the first 8 bytes". So if an AH is 24 bytes long,
+ * the length field will contain "4". (4 * 4 + 8 == 24).
+ */
+ ahlen = (aligned_ah->ah_length << 2) + 8;
+ fraglen -= ahlen;
+ if (fraglen < 0)
+ return; /* incomplete header */
+
+ auth_data_len = ahlen - sizeof (ah_t);
+ auth_data = (uint8_t *)(ah + 1);
+ data = auth_data + auth_data_len;
+
+ if (flags & F_SUM) {
+ line = (char *)get_sum_line();
+ (void) sprintf(line, "AH SPI=0x%x Replay=%u",
+ ntohl(aligned_ah->ah_spi), ntohl(aligned_ah->ah_replay));
+ line += strlen(line);
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("AH: ", "Authentication Header", ahlen);
+ show_space();
+ (void) sprintf(get_line((char *)&ah->ah_nexthdr - dlc_header,
+ 1), "Next header = %d (%s)", aligned_ah->ah_nexthdr,
+ getproto(aligned_ah->ah_nexthdr));
+ (void) sprintf(get_line((char *)&ah->ah_length - dlc_header, 1),
+ "AH length = %d (%d bytes)", aligned_ah->ah_length, ahlen);
+ (void) sprintf(get_line((char *)&ah->ah_reserved - dlc_header,
+ 2), "<Reserved field = 0x%x>",
+ ntohs(aligned_ah->ah_reserved));
+ (void) sprintf(get_line((char *)&ah->ah_spi - dlc_header, 4),
+ "SPI = 0x%x", ntohl(aligned_ah->ah_spi));
+ (void) sprintf(get_line((char *)&ah->ah_replay - dlc_header, 4),
+ "Replay = %u", ntohl(aligned_ah->ah_replay));
+
+ /* * 2 for two hex digits per auth_data byte. */
+ buff = malloc(auth_data_len * 2);
+ if (buff != NULL) {
+ int i;
+
+ for (i = 0; i < auth_data_len; i++)
+ sprintf(buff + i * 2, "%02x", auth_data[i]);
+ }
+
+ (void) sprintf(get_line((char *)auth_data - dlc_header,
+ auth_data_len), "ICV = %s",
+ (buff == NULL) ? "<out of memory>" : buff);
+
+ /* malloc(3c) says I can call free even if buff == NULL */
+ free(buff);
+
+ show_space();
+ }
+
+ new_iplen = iplen - ahlen;
+ proto = aligned_ah->ah_nexthdr;
+
+ /*
+ * Print IPv6 Extension Headers, or skip them in the summary case.
+ */
+ if (proto == IPPROTO_HOPOPTS || proto == IPPROTO_DSTOPTS ||
+ proto == IPPROTO_ROUTING || proto == IPPROTO_FRAGMENT) {
+ (void) print_ipv6_extensions(flags, &data, &proto, &iplen,
+ &fraglen);
+ }
+
+ if (fraglen > 0)
+ switch (proto) {
+ case IPPROTO_ENCAP:
+ (void) interpret_ip(flags, (struct ip *)data,
+ new_iplen);
+ break;
+ case IPPROTO_IPV6:
+ (void) interpret_ipv6(flags, (ip6_t *)data,
+ new_iplen);
+ break;
+ case IPPROTO_ICMP:
+ interpret_icmp(flags, (struct icmp *)data,
+ new_iplen, fraglen);
+ break;
+ case IPPROTO_ICMPV6:
+ interpret_icmpv6(flags, (icmp6_t *)data,
+ new_iplen, fraglen);
+ break;
+ case IPPROTO_TCP:
+ interpret_tcp(flags, data, new_iplen, fraglen);
+ break;
+
+ case IPPROTO_ESP:
+ interpret_esp(flags, data, new_iplen, fraglen);
+ break;
+ case IPPROTO_AH:
+ interpret_ah(flags, data, new_iplen, fraglen);
+ break;
+
+
+ case IPPROTO_UDP:
+ interpret_udp(flags, data, new_iplen, fraglen);
+ break;
+ /* default case is to not print anything else */
+ }
+
+ return (ahlen);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ldap.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ldap.c
new file mode 100644
index 0000000000..bdae248e2e
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ldap.c
@@ -0,0 +1,1481 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2000-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/if_ether.h>
+#include <netinet/udp.h>
+#include "snoop.h"
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+extern char *src_name;
+extern char *dst_name;
+#define MAX_CTX (10)
+#define LINE_LEN (255)
+#define BUF_SIZE (16000)
+static int ldap = 0; /* flag to control initialization */
+struct ctx {
+ int src;
+ int dst;
+ char *src_name;
+ char *dst_name;
+};
+char *osibuff = NULL;
+int osilen = 0;
+char scrbuffer[BUF_SIZE]; /* buffer to accumulate data until a */
+ /* complete LDAPmessage is received */
+char resultcode[LINE_LEN]; /* These are used */
+char operation[LINE_LEN]; /* by -V option. */
+char bb[LINE_LEN];
+
+int gi_osibuf[MAX_CTX];
+int otyp[MAX_CTX];
+int olen[MAX_CTX];
+int level[MAX_CTX];
+
+void decode_ldap(char *buf, int len);
+
+#define X unsigned char
+typedef X * A;
+#define INT(a) ((int)(a))
+#define SCRUB (void) strcat(scrbuffer, bb);
+
+static X hex; /* input hex octet */
+static A *PTRaclass; /* application tag table pointer */
+
+/*
+**----------------------------------------------**
+** ASN.1 Message Printing Macros **
+**----------------------------------------------**
+*/
+
+#define asnshw1(a) {(void)sprintf(bb, a); SCRUB }
+#define asnshw2(a, b) {(void)sprintf(bb, a, b); SCRUB }
+#define asnshw3(a, b, c) {(void)sprintf(bb, a, b, c); SCRUB }
+#define asnshw4(a, b, c, d) {(void)sprintf(bb, a, b, c, d); SCRUB }
+#define asnshw5(a, b, c, d, e) {(void)sprintf(bb, a, b, c, d, e); SCRUB }
+
+/*
+**--------------------------------------**
+** Local Types And Variables **
+**--------------------------------------**
+*/
+
+/*
+** object identifier oid to name mapping description type
+*/
+
+typedef struct {
+ A oidname; /* object identifier string name */
+ X oidcode[16]; /* object identifier hexa code */
+} oidelmT;
+typedef oidelmT *oidelmTp;
+
+/*
+**------------------------------------------**
+** snoop's entry point to ldap decoding **
+**------------------------------------------**
+*/
+
+void
+interpret_ldap(flags, data, fraglen, src, dst)
+int flags;
+char *data;
+int fraglen;
+int src;
+int dst;
+{
+
+ if (!ldap) {
+ init_ldap();
+ ldap = 1;
+ }
+
+ (void) decode_ldap(data, fraglen);
+
+ if (flags & F_DTAIL) {
+ /* i.e. when snoop is run with -v (verbose) */
+ show_header("LDAP: ",
+ "Lightweight Directory Access Protocol Header", fraglen);
+ show_space();
+ printf("%s", scrbuffer);
+ }
+
+ if (flags & F_SUM) {
+ /* i.e. when snoop is run with -V (summary) */
+ (void) strcpy(data, "");
+
+ if (strlen(operation) != 0) {
+ (void) strcat(data, " ");
+ (void) strncat(data, operation, 30);
+ (void) strcpy(operation, "");
+ }
+
+ if (strlen(resultcode) != 0) {
+ (void) strcat(data, " ");
+ (void) strncat(data, resultcode, 30);
+ (void) strcpy(resultcode, "");
+ }
+
+ if (dst == 389) {
+ (void) sprintf(get_sum_line(),
+ "LDAP C port=%d%s", src, data);
+ }
+ if (src == 389) {
+ (void) sprintf(get_sum_line(),
+ "LDAP R port=%d%s", dst, data);
+ }
+ }
+
+ (void) strcpy(scrbuffer, "");
+}
+
+/*
+**--------------------------------------------------------------**
+** known object identifiers: customize to add your own oids **
+**--------------------------------------------------------------**
+*/
+
+static oidelmT OidTab[] = {
+/*
+** X.500 Standardized Attribute Types
+*/
+{(A)"ObjectClass", { 0x03, 0x55, 0x04, 0x00 }},
+{(A)"AliasObjectName", { 0x03, 0x55, 0x04, 0x01 }},
+{(A)"KnowledgeInfo", { 0x03, 0x55, 0x04, 0x02 }},
+{(A)"CommonName", { 0x03, 0x55, 0x04, 0x03 }},
+{(A)"Surname", { 0x03, 0x55, 0x04, 0x04 }},
+{(A)"SerialNumber", { 0x03, 0x55, 0x04, 0x05 }},
+{(A)"CountryName", { 0x03, 0x55, 0x04, 0x06 }},
+{(A)"LocalityName", { 0x03, 0x55, 0x04, 0x07 }},
+{(A)"StateOrProvinceName", { 0x03, 0x55, 0x04, 0x08 }},
+{(A)"StreetAddress", { 0x03, 0x55, 0x04, 0x09 }},
+{(A)"OrganizationName", { 0x03, 0x55, 0x04, 0x0a }},
+{(A)"OrganizationUnitName", { 0x03, 0x55, 0x04, 0x0b }},
+{(A)"Title", { 0x03, 0x55, 0x04, 0x0c }},
+{(A)"Description", { 0x03, 0x55, 0x04, 0x0d }},
+{(A)"SearchGuide", { 0x03, 0x55, 0x04, 0x0e }},
+{(A)"BusinessCategory", { 0x03, 0x55, 0x04, 0x0f }},
+{(A)"PostalAddress", { 0x03, 0x55, 0x04, 0x10 }},
+{(A)"PostalCode", { 0x03, 0x55, 0x04, 0x11 }},
+{(A)"PostOfficeBox", { 0x03, 0x55, 0x04, 0x12 }},
+{(A)"PhysicalDeliveryOffice", { 0x03, 0x55, 0x04, 0x13 }},
+{(A)"TelephoneNUmber", { 0x03, 0x55, 0x04, 0x14 }},
+{(A)"TelexNumber", { 0x03, 0x55, 0x04, 0x15 }},
+{(A)"TeletexTerminalId", { 0x03, 0x55, 0x04, 0x16 }},
+{(A)"FaxTelephoneNumber", { 0x03, 0x55, 0x04, 0x17 }},
+{(A)"X121Address", { 0x03, 0x55, 0x04, 0x18 }},
+{(A)"IsdnAddress", { 0x03, 0x55, 0x04, 0x19 }},
+{(A)"RegisteredAddress", { 0x03, 0x55, 0x04, 0x1a }},
+{(A)"DestinationIndicator", { 0x03, 0x55, 0x04, 0x1b }},
+{(A)"PreferDeliveryMethod", { 0x03, 0x55, 0x04, 0x1c }},
+{(A)"PresentationAddress", { 0x03, 0x55, 0x04, 0x1d }},
+{(A)"SupportedApplContext", { 0x03, 0x55, 0x04, 0x1e }},
+{(A)"Member", { 0x03, 0x55, 0x04, 0x1f }},
+{(A)"Owner", { 0x03, 0x55, 0x04, 0x20 }},
+{(A)"RoleOccupant", { 0x03, 0x55, 0x04, 0x21 }},
+{(A)"SeeAlso", { 0x03, 0x55, 0x04, 0x22 }},
+{(A)"Password", { 0x03, 0x55, 0x04, 0x23 }},
+{(A)"UserCertificate", { 0x03, 0x55, 0x04, 0x24 }},
+{(A)"CaCertificate", { 0x03, 0x55, 0x04, 0x25 }},
+{(A)"AuthorityRevList", { 0x03, 0x55, 0x04, 0x26 }},
+{(A)"CertificateRevList", { 0x03, 0x55, 0x04, 0x27 }},
+{(A)"CrossCertificatePair", { 0x03, 0x55, 0x04, 0x28 }},
+
+/*
+** X.500 Standardized Object Classes
+*/
+{(A)"Top", { 0x03, 0x55, 0x06, 0x00 }},
+{(A)"Alias", { 0x03, 0x55, 0x06, 0x01 }},
+{(A)"Country", { 0x03, 0x55, 0x06, 0x02 }},
+{(A)"Locality", { 0x03, 0x55, 0x06, 0x03 }},
+{(A)"Organization", { 0x03, 0x55, 0x06, 0x04 }},
+{(A)"OrganizationUnit", { 0x03, 0x55, 0x06, 0x05 }},
+{(A)"Person", { 0x03, 0x55, 0x06, 0x06 }},
+{(A)"OrganizationPersion", { 0x03, 0x55, 0x06, 0x07 }},
+{(A)"OrganizationRole", { 0x03, 0x55, 0x06, 0x08 }},
+{(A)"Group", { 0x03, 0x55, 0x06, 0x09 }},
+{(A)"ResidentialPerson", { 0x03, 0x55, 0x06, 0x0A }},
+{(A)"ApplicationProcess", { 0x03, 0x55, 0x06, 0x0B }},
+{(A)"ApplicationEntity", { 0x03, 0x55, 0x06, 0x0C }},
+{(A)"Dsa", { 0x03, 0x55, 0x06, 0x0D }},
+{(A)"Device", { 0x03, 0x55, 0x06, 0x0E }},
+{(A)"StrongAuthenticUser", { 0x03, 0x55, 0x06, 0x0F }},
+{(A)"CaAuthority", { 0x03, 0x55, 0x06, 0x10 }},
+
+/*
+** ACSE Protocol Object Identifiers
+*/
+{(A)"Asn1BER-TS", { 0x02, 0x51, 0x01 }},
+{(A)"Private-TS", { 0x06, 0x2b, 0xce, 0x06, 0x01, 0x04, 0x06 }},
+{(A)"ACSE-AS", { 0x04, 0x52, 0x01, 0x00, 0x01 }},
+
+/*
+** Directory Protocol Oids
+*/
+{(A)"DirAccess-AC", { 0x03, 0x55, 0x03, 0x01 }},
+{(A)"DirSystem-AC", { 0x03, 0x55, 0x03, 0x02 }},
+
+{(A)"DirAccess-AS", { 0x03, 0x55, 0x09, 0x01 }},
+{(A)"DirSystem-AS", { 0x03, 0x55, 0x09, 0x02 }},
+
+/*
+** and add your private object identifiers here ...
+*/
+};
+
+#define OIDNB (sizeof (OidTab) / sizeof (oidelmT)) /* total oid nb */
+
+/*
+** asn.1 tag class definition
+*/
+
+static A class[] = { /* tag class */
+ (A)"UNIV ",
+ (A)"APPL ",
+ (A)"CTXs ",
+ (A)"PRIV "
+};
+
+/*
+** universal tag definition
+*/
+
+static A uclass[] = { /* universal tag assignment */
+(A)"EndOfContents", /* 0 */
+(A)"Boolean", /* 1 */
+(A)"Integer", /* 2 */
+(A)"BitString", /* 3 */
+(A)"OctetString", /* 4 */
+(A)"Null", /* 5 */
+(A)"Oid", /* 6 */
+(A)"ObjDescriptor", /* 7 */
+(A)"External", /* 8 */
+(A)"Real", /* 9 */
+(A)"Enumerated", /* 10 */
+(A)"Reserved", /* 11 */
+(A)"Reserved", /* 12 */
+(A)"Reserved", /* 13 */
+(A)"Reserved", /* 14 */
+(A)"Reserved", /* 15 */
+(A)"Sequence", /* 16 */
+(A)"Set", /* 17 */
+(A)"NumericString", /* 18 */
+(A)"PrintableString", /* 19 */
+(A)"T.61String", /* 20 */
+(A)"VideotexString", /* 21 */
+(A)"IA5String", /* 22 */
+(A)"UTCTime", /* 23 */
+(A)"GeneralizedTime", /* 24 */
+(A)"GraphicString", /* 25 */
+(A)"VisibleString", /* 26 */
+(A)"GeneralString", /* 27 */
+(A)"Reserved", /* 28 */
+(A)"Reserved", /* 29 */
+(A)"Reserved", /* 30 */
+(A)"Reserved" /* 31 */
+};
+
+static A MHSaclass[] = { /* mhs application tag assignment */
+(A)"Bind Request", /* 0 */
+(A)"Bind Response",
+(A)"Unbind Request",
+(A)"Search Request",
+(A)"Search ResEntry",
+(A)"Search ResDone", /* 5 */
+(A)"Modify Request",
+(A)"Modify Response",
+(A)"Add Request",
+(A)"Add Response", /* 9 */
+(A)"Del Request",
+(A)"Del Response",
+(A)"ModDN Request",
+(A)"ModDN Response",
+(A)"Compare Request", /* 14 */
+(A)"Compare Response",
+(A)"Abandon Request",
+(A)"", /* 17 */
+(A)"", /* 18 */
+(A)"Search ResRef", /* 19 */
+(A)"", /* 20 */
+(A)"", /* 21 */
+(A)"", /* 22 */
+(A)"Extended Request",
+(A)"Extended Response",
+(A)"", /* 25 */
+(A)"", /* 26 */
+(A)"", /* 27 */
+(A)"", /* 28 */
+(A)"", /* 29 */
+(A)"", /* 30 */
+(A)"" /* 31 */
+};
+
+
+static A DFTaclass[] = { /* Default Application Tag Assignment */
+(A)"", /* 0 */
+(A)"", /* 1 */
+(A)"", /* 2 */
+(A)"", /* 3 */
+(A)"", /* 4 */
+(A)"", /* 5 */
+(A)"", /* 6 */
+(A)"", /* 7 */
+(A)"", /* 8 */
+(A)"", /* 9 */
+(A)"", /* 10 */
+(A)"", /* 11 */
+(A)"", /* 12 */
+(A)"", /* 13 */
+(A)"", /* 14 */
+(A)"", /* 15 */
+(A)"", /* 16 */
+(A)"", /* 17 */
+(A)"", /* 18 */
+(A)"", /* 19 */
+(A)"", /* 20 */
+(A)"", /* 21 */
+(A)"", /* 22 */
+(A)"", /* 23 */
+(A)"", /* 24 */
+(A)"", /* 25 */
+(A)"", /* 26 */
+(A)"", /* 27 */
+(A)"", /* 28 */
+(A)"", /* 29 */
+(A)"", /* 30 */
+(A)"" /* 31 */
+};
+
+typedef struct asndefS {
+char *name;
+int type;
+int application;
+int nbson;
+struct {
+ char *sonname;
+ struct asndefS *sondef;
+ long tag;
+ } son[50];
+} asndefT, * asndefTp;
+
+#define SEQUENCE 0x0002
+#define SEQUENCEOF 0x0003
+#define SET 0x0004
+#define PRINTABLE 0x0008
+#define ENUM 0x0010
+#define BITSTRING 0x0020
+#define EXTENSION 0x0040
+#define CONTENTTYPE 0x0080
+#define CONTENT 0x0100
+#define CHOICE 0x0200
+
+static asndefT RTSpasswd = { "RTS Authentification data", SET, -1, 2, {
+ {"MTA Name", 0, 0},
+ {"MTA Password", 0, 1}}};
+static asndefT RTSudata = { "RTS User data", SET, -1, 1, {
+ {0, &RTSpasswd, 1}}};
+
+static asndefT baseObject = {"Base Object", PRINTABLE, -1, 0, {0}};
+
+static asndefT scope = {"Scope", ENUM, -1, 3, {
+ {"BaseObject", 0, 0},
+ {"singleLevel", 0, 1},
+ {"wholeSubtree", 0, 2}}};
+
+static asndefT derefAliases = {"DerefAliases", ENUM, -1, 4, {
+ {"neverDerefAliases", 0, 0},
+ {"derefInSearching", 0, 1},
+ {"derefFindingBaseObj", 0, 2},
+ {"derefAlways", 0, 3}}};
+
+static asndefT filter;
+static asndefT and = {"And", SET, -1, 1, {
+ {0, &filter, -1}}};
+static asndefT or = {"Or", SET, -1, 1, {
+ {0, &filter, -1}}};
+static asndefT not = {"Not", SET, -1, 1, {
+ {0, &filter, -1}}};
+static asndefT equalityMatch = {"Equality Match", SEQUENCE, -1, 2, {
+ {"Attr Descr", 0, -1},
+ {"Value", 0, -1}}};
+static asndefT substrings = {"Substring", SEQUENCE, -1, 2, {
+ {"Type", 0, -1},
+ {"Substrings (initial)", 0, 0},
+ {"Substrings (any)", 0, 1},
+ {"Substring (final)", 0, 2}}};
+static asndefT greaterOrEqual = {"Greater Or Equal", SEQUENCE, -1, 2, {
+ {"Attr Descr", 0, -1},
+ {"Value", 0, -1}}};
+static asndefT lessOrEqual = {"Less Or Equal", SEQUENCE, -1, 2, {
+ {"Attr Descr", 0, -1},
+ {"Value", 0, -1}}};
+static asndefT approxMatch = {"Approx Match", SEQUENCE, -1, 2, {
+ {"Attr Descr", 0, -1},
+ {"Value", 0, -1}}};
+static asndefT extensibleMatch = {"Extensible Match", SEQUENCE, -1, 4, {
+ {"MatchingRule", 0, 1},
+ {"Type", 0, 2},
+ {"MatchValue", 0, 3},
+ {"dnAttributes", 0, 4}}};
+
+static asndefT filter = {"Filter", CHOICE, -1, 10, {
+ {0, &and, 0},
+ {0, &or, 1},
+ {0, &not, 2},
+ {0, &equalityMatch, 3},
+ {0, &substrings, 4},
+ {0, &greaterOrEqual, 5},
+ {0, &lessOrEqual, 6},
+ {"Filter: Present", 0, 7},
+ {0, &approxMatch, 8},
+ {0, &extensibleMatch, 9}}};
+
+static asndefT attributedescription = \
+ {"Attribute Description", PRINTABLE, -1, 0, {0}};
+static asndefT attributes = {"Attribute List", SEQUENCEOF, -1, 1, {
+ {0, &attributedescription, -1}}};
+
+static asndefT searchRequest = {"Operation", SEQUENCE, 3, 8, {
+ {0, &baseObject, -1},
+ {0, &scope, -1},
+ {0, &derefAliases, -1},
+ {"SizeLimit", 0, -1},
+ {"TimeLimit", 0, -1},
+ {"TypesOnly", 0, -1},
+ {0, &filter, -1},
+ {0, &attributes, -1}}};
+
+static asndefT objectName = {"Object Name", PRINTABLE, -1, 0, {0}};
+
+static asndefT ldapEntry = {"Entry", PRINTABLE, -1, 0, {0}};
+static asndefT relativeLdapEntry = \
+ {"Relative LDAP Entry", PRINTABLE, -1, 0, {0}};
+static asndefT newSuperior = {"New Superior", PRINTABLE, -1, 0, {0}};
+
+static asndefT vals = {"Vals", SET, -1, 1, {
+ {"Value", 0, -1}}};
+
+static asndefT attribute = {"Attribute", SEQUENCE, -1, 2, {
+ {"Type", 0, -1},
+ {0, &vals, -1}}};
+
+static asndefT partialAttributes = {"Partial Attributes", SEQUENCEOF, -1, 1, {
+ {0, &attribute, -1}}};
+
+static asndefT searchResEntry = {"Operation", SEQUENCE, 4, 2, {
+ {0, &objectName, -1},
+ {0, &partialAttributes, -1}}};
+
+static asndefT authChoice = {"Authentication Choice", CHOICE, -1, 2, {
+ {"Authentication: Simple", 0, 0},
+ {"Authentication: SASL", 0, 3}}};
+
+static asndefT bindRequest = {"Operation", SEQUENCE, 0, 3, {
+ {"Version", 0, -1},
+ {0, &objectName, -1},
+ {0, &authChoice, -1}}};
+
+static asndefT resultCode = {"Result Code", ENUM, -1, 39, {
+ {"Success", 0, 0},
+ {"Operation Error", 0, 1},
+ {"Protocol Error", 0, 2},
+ {"Time Limit Exceeded", 0, 3},
+ {"Size Limit Exceeded", 0, 4},
+ {"Compare False", 0, 5},
+ {"Compare True", 0, 6},
+ {"Auth Method Not supported", 0, 7},
+ {"Strong Auth Required", 0, 8},
+ {"Referral", 0, 10},
+ {"Admin Limit Exceeded", 0, 11},
+ {"Unavailable Critical Extension", 0, 12},
+ {"Confidentiality required", 0, 13},
+ {"SASL Bind In Progress", 0, 14},
+ {"No Such Attribute", 0, 16},
+ {"Undefined Attribute Type", 0, 17},
+ {"Inappropriate Matching", 0, 18},
+ {"Constraint violation", 0, 19},
+ {"Attribute or Value Exists", 0, 20},
+ {"Invalid Attribute Syntax", 0, 21},
+ {"No Such Object", 0, 32},
+ {"Alias Problem", 0, 33},
+ {"Invalid DN Syntax", 0, 34},
+ {"Alias Dereferencing Problem", 0, 36},
+ {"Inappropriate Authentication", 0, 48},
+ {"Invalid Credentials", 0, 49},
+ {"Insufficient Access Rights", 0, 50},
+ {"Busy", 0, 51},
+ {"Unavailable", 0, 52},
+ {"Unwilling To Perform", 0, 53},
+ {"Loop Detect", 0, 54},
+ {"Naming Violation", 0, 64},
+ {"ObjectClass violation", 0, 65},
+ {"Not Allowed On Non Leaf", 0, 66},
+ {"Not Allowed On RDN", 0, 67},
+ {"Entry Already Exists", 0, 68},
+ {"ObjectClass Mods Prohibited", 0, 69},
+ {"Affects Multiple DSAs", 0, 71},
+ {"Other", 0, 80}}};
+
+
+static asndefT referral = {"Referral", SEQUENCEOF, -1, 1, {
+ {"LDAP URL", 0, -1}}};
+
+static asndefT ldapResult = {"LDAP Result", SEQUENCE, -1, 4, {
+ {0, &resultCode, -1},
+ {"Matched DN", 0, -1},
+ {"Error Message", 0, -1},
+ {0, &referral, 3}}};
+
+static asndefT bindResponse = {"Operation", SEQUENCE, 1, 5, {
+ {0, &resultCode, -1},
+ {"Matched DN", 0, -1},
+ {"Error Message", 0, -1},
+ {0, &referral, 3},
+ {"SASL Credentials", 0, 7}}};
+
+static asndefT unbindRequest = {"Operation", SEQUENCE, 2, 0, {0}};
+
+static asndefT searchResDone = {"Operation", SEQUENCE, 5, 4, {
+ {0, &resultCode, -1},
+ {"Matched DN", 0, -1},
+ {"Error Message", 0, -1},
+ {0, &referral, 3}}};
+
+static asndefT seqModOperation = {"Operation", ENUM, -1, 4, {
+ {"Add", 0, 0},
+ {"Delete", 0, 1},
+ {"Replace", 0, 2}}};
+
+static asndefT seqModModification = {"Modification", SEQUENCE, -1, 1, {
+ {0, &attribute, -1}}};
+
+static asndefT seqModification = {"", SEQUENCE, -1, 2, {
+ {0, &seqModOperation, -1},
+ {0, &seqModModification, -1}}};
+
+static asndefT modification = {"Modification", SEQUENCEOF, -1, 1, {
+ {0, &seqModification, -1}}};
+
+static asndefT modifyRequest = {"Operation", SEQUENCE, 6, 2, {
+ {0, &objectName, -1},
+ {0, &modification, -1}}};
+
+static asndefT modifyResponse = {"Operation", SEQUENCE, 7, 4, {
+ {0, &resultCode, -1},
+ {"Matched DN", 0, -1},
+ {"Error Message", 0, -1},
+ {0, &referral, 3}}};
+
+static asndefT addAttributes = {"Attributes", SEQUENCEOF, -1, 1, {
+ {0, &attribute, -1}}};
+
+static asndefT addRequest = {"Operation", SEQUENCE, 8, 2, {
+ {0, &ldapEntry, -1},
+ {0, &addAttributes, -1}}};
+
+static asndefT addResponse = {"Operation", SEQUENCE, 9, 4, {
+ {0, &resultCode, -1},
+ {"Matched DN", 0, -1},
+ {"Error Message", 0, -1},
+ {0, &referral, 3}}};
+
+static asndefT delRequest = {"Operation", SEQUENCE, 10, 1, {
+ {0, &ldapEntry, -1}}};
+
+static asndefT delResponse = {"Operation", SEQUENCE, 11, 4, {
+ {0, &resultCode, -1},
+ {"Matched DN", 0, -1},
+ {"Error Message", 0, -1},
+ {0, &referral, 3}}};
+
+static asndefT modifyDNRequest = {"Operation", SEQUENCE, 12, 4, {
+ {0, &ldapEntry, -1},
+ {0, &relativeLdapEntry, -1},
+ {"Delete Old RDN", 0, -1},
+ {0, &newSuperior, 0}}};
+
+static asndefT modifyDNResponse = {"Operation", SEQUENCE, 13, 4, {
+ {0, &resultCode, -1},
+ {"Matched DN", 0, -1},
+ {"Error Message", 0, -1},
+ {0, &referral, 3}}};
+
+static asndefT ava = {"Ava", SEQUENCE, -1, 2, {
+ {"Attr Descr", 0, -1},
+ {"Value", 0, -1}}};
+
+static asndefT compareRequest = {"Operation", SEQUENCE, 14, 2, {
+ {0, &ldapEntry, -1},
+ {0, &ava, 0}}};
+
+static asndefT compareResponse = {"Operation", SEQUENCE, 15, 4, {
+ {0, &resultCode, -1},
+ {"Matched DN", 0, -1},
+ {"Error Message", 0, -1},
+ {0, &referral, 3}}};
+
+static asndefT abandonRequest = {"Operation", SEQUENCE, 16, 1, {
+ {"Message ID", 0, -1}}};
+
+static asndefT searchResRef = {"Operation", SEQUENCEOF, 19, 1, {
+ {"LDAP URL", 0, -1}}};
+
+static asndefT extendedRequest = {"Operation", SEQUENCE, 14, 2, {
+ {"Request Name", 0, 0},
+ {"Request Value", 0, 1}}};
+
+static asndefT extendedResponse = {"Operation", SEQUENCE, 24, 6, {
+ {0, &resultCode, -1},
+ {"Matched DN", 0, -1},
+ {"Error Message", 0, -1},
+ {0, &referral, 3},
+ {"Response Name", 0, 10},
+ {"Response", 0, 11}}};
+
+static asndefT protocolOp = {"Protocol Op", CHOICE, -1, 20, {
+ {0, &bindRequest, 0},
+ {0, &bindResponse, 1},
+ {0, &unbindRequest, 2},
+ {0, &searchRequest, 3},
+ {0, &searchResEntry, 4},
+ {0, &searchResDone, 5},
+ {0, &modifyRequest, 6},
+ {0, &modifyResponse, 7},
+ {0, &addRequest, 8},
+ {0, &addResponse, 9},
+ {0, &delRequest, 10},
+ {0, &delResponse, 11},
+ {0, &modifyDNRequest, 12},
+ {0, &modifyDNResponse, 13},
+ {0, &compareRequest, 14},
+ {0, &compareResponse, 15},
+ {0, &abandonRequest, 16},
+ {0, &searchResRef, 19},
+ {0, &extendedRequest, 23},
+ {0, &extendedResponse, 24}}};
+
+static asndefT control = {"Control", SEQUENCE, -1, 3, {
+ {"LDAP OID", 0, -1},
+ {"Criticality", 0, -1},
+ {"Control value", 0, -1}}};
+
+static asndefT controls = {"Controls List", SEQUENCEOF, -1, 1, {
+ {0, &control, -1}}};
+
+static asndefT LDAPMessage = { "LDAPMessage", SEQUENCE, -1, 3, {
+ {"Message ID", 0, -1},
+ {0, &protocolOp, -1},
+ {0, &controls, 0}}};
+
+static asndefT MPDU = { "MPDU", SET, -1, 1,
+ {{0, &LDAPMessage, 0}}};
+
+static int mytype[] = {
+0, /* EndOfContents */
+0, /* Boolean */
+0, /* Integer */
+BITSTRING, /* BitString */
+0, /* OctetString */
+0, /* Null */
+0, /* Oid */
+0, /* ObjDescriptor */
+0, /* External */
+0, /* Real */
+ENUM, /* Enumerated */
+0, /* Reserved */
+0, /* Reserved */
+0, /* Reserved */
+0, /* Reserved */
+0, /* Reserved */
+SEQUENCE, /* Sequence */
+SET, /* Set */
+0, /* NumericString */
+0, /* PrintableString */
+0, /* T.61String */
+0, /* VideotexString */
+0, /* IA5String */
+0, /* UTCTime */
+0, /* GeneralizedTime */
+0, /* GraphicString */
+0, /* VisibleString */
+0, /* GeneralString */
+0, /* Reserved */
+0, /* Reserved */
+0, /* Reserved */
+0, /* Reserved */
+};
+
+/*
+**----------------------------------------------**
+** find object identifier in known oid table **
+**----------------------------------------------**
+*/
+static oidmap(oid, olg)
+A oid; /* oid hexa string */
+int olg; /* oid length */
+{
+ register int ix, goon;
+ register A oidptr, tabptr, tabend;
+
+/* returns (oid table size) if not found */
+
+ for (ix = 0; ix < OIDNB; ix++) {
+ oidptr = oid; tabptr = (&(OidTab[ix].oidcode[0]));
+ if (olg == INT(*tabptr++)) {
+ for (tabend = tabptr + olg, goon = 1;
+ (goon) && (tabptr < tabend); ) {
+ if (*tabptr++ != *oidptr++) goon = 0;
+ }
+ if (goon)
+ return (ix);
+ }
+ }
+ return (OIDNB);
+}
+
+/*
+**------------------------------------------------------**
+**read an hexacode and convert it into ascii **
+**------------------------------------------------------**
+*/
+
+static int getnext(int ctxnum)
+{
+ static X c[3]; /* c[0-3] will contain ascii values on exit */
+ hex = 0;
+ if (gi_osibuf[ctxnum] == osilen)
+ return (-1);
+ hex = osibuff[gi_osibuf[ctxnum]++];
+ (void) sprintf((char *)c, "%02x", (hex&0x00FF));
+ return (0);
+}
+
+/*
+**------------------------------------------------------**
+** Skip everything that is not an LDAPMessage **
+**------------------------------------------------------**
+*/
+static char *skipjunk(len, pdu)
+int len;
+char *pdu;
+{
+ int tag;
+ char *buf = pdu;
+ int offset = 0;
+ while (len > 0) {
+ /* size minumum for a sequence + integer = 5 */
+ /* LDAPMessage::= SEQUENCE */
+ if ((len > 5) && (buf[0] == 0x30)) {
+ tag = buf[1]&0x00ff;
+ if (tag < 0x80) {
+ /* length is one one octet */
+ offset = 1;
+ } else {
+ /* length is multiple octet. */
+ offset = 1+ tag&0x007f;
+ }
+ /* Make sure we don't read past the end */
+ /* of the buffer */
+ if (len - (1+offset) > 0) {
+ /* skip after the length */
+ tag = buf[1+offset]&0x00ff;
+ if (tag == 0x02) { /* INTEGER */
+ /* looks like a valid PDU */
+ return (buf);
+ }
+ }
+ }
+ len --;
+ buf++;
+ }
+ return (buf);
+}
+/*
+**----------------------------------------------------------**
+** main routine: decode a TLV; to be called recursively **
+**----------------------------------------------------------**
+*/
+#define GETNEXT(a) (void)getnext(a);
+static int decpdu(pdulen, ASNDESC, ctxnum)
+int pdulen; /* current pdu's length */
+asndefTp ASNDESC;
+int ctxnum;
+{
+ X scrlin[99]; /* screen line */
+ X oidstr[80]; /* oid hexa string */
+ int slen; /* screen line length */
+ int stlv; /* sub-tlv length */
+ int oix; /* oid table index */
+ int effnb; /* effectively traced octet nb */
+ int i, j;
+ int ai = -2;
+ asndefTp SASNDESC = 0;
+ asndefTp TMPDESC = 0;
+ asndefTp GR_TMPDESC = 0;
+ int tmpai = 0;
+ int gr_tmpai = 0;
+ int dontprint = 0;
+ int already = 0;
+ static int rlen = 0; /* tlv's real length */
+
+ ++level[ctxnum]; /* level indicator */
+ effnb = 0;
+
+ /*
+ ** Decode the current TLV segment
+ */
+ while (pdulen > 1) {
+
+ if (getnext(ctxnum)) {
+ break;
+ }
+ if (strlen(scrbuffer)) asnshw2("%s ", "LDAP:");
+ /* screen printing according to level indicator */
+ for (i = 1; i < level[ctxnum]; ++i) asnshw1(" ");
+
+ /* get tag */
+ otyp[ctxnum] = INT(hex); /* single octet type only */
+ --pdulen;
+ ++effnb;
+
+ /* get length */
+ GETNEXT(ctxnum);
+ olen[ctxnum] = INT(hex); /* tlv length */
+ --pdulen;
+ ++effnb;
+
+ /* Continuing decoding of current TLV... */
+ /*
+ ** Snoop's lower layers do not allow us
+ ** to know the true length for
+ ** datastream protocols like LDAP.
+ */
+
+ /* if length is less than 128, we */
+ /* already have the real TLV length. */
+ if (olen[ctxnum] < 128) { /* short length form */
+ rlen = olen[ctxnum];
+ } else { /* long and any form length */
+ /* else we do more getnext()'s */
+ for (rlen = 0, olen[ctxnum] &= 0x0F;
+ (olen[ctxnum]) && (pdulen > 0);
+ --olen[ctxnum], --pdulen, ++effnb) {
+ GETNEXT(ctxnum);
+ rlen = (rlen << 8) | INT(hex);
+ }
+ if (!rlen) {
+ pdulen = 0x7fffffff;
+ }
+ }
+
+ /*
+ ** print the tag class and number
+ */
+ i = otyp[ctxnum]&0x1F;
+ switch (otyp[ctxnum] >> 6) { /* class */
+ case 0: /* universal */
+ if (ASNDESC && i != 0) {
+ int dobreak = 0;
+ switch (ASNDESC->type) {
+ case CONTENT:
+ SASNDESC = ASNDESC;
+ break;
+ case SET:
+ for (ai = 0;
+ ai < ASNDESC->nbson && i < 32 &&
+ ASNDESC->son[ai].sondef &&
+ /*
+ ** For this test SEQUENCE & SEQUENCE OF
+ ** are same, so suppress the last bit
+ */
+ (ASNDESC->son[ai].sondef
+ ->type&0xFE)
+ != mytype[i]; ++ai);
+ if (ai < ASNDESC->nbson) {
+ SASNDESC =
+ ASNDESC->son[ai].sondef;
+ if (ASNDESC->son[ai].sonname) {
+ if (ASNDESC-> \
+ son[ai].sondef && ASNDESC->son[ai].sondef->name)
+ {
+ asnshw2 \
+ ("%s ", "LDAP:");
+ asnshw4 \
+ (" %c[%s %s]",
+ ((otyp[ctxnum]&0x20)?'*':' '), \
+ ASNDESC->son[ai].sonname, \
+ ASNDESC->son[ai].sondef->name);
+ } else {
+ asnshw2 \
+ ("%s ", "");
+ asnshw3 \
+ (" %c[%s]", \
+ ((otyp[ctxnum]&0x20)?'*':' '),
+ ASNDESC->son[ai].sonname);
+ } /* end if */
+ dobreak = 1;
+ } else if
+ (ASNDESC->son[ai].sondef &&
+ ASNDESC->son[ai].sondef->name) {
+ asnshw2 \
+ ("%s ", "LDAP:");
+ asnshw3 \
+ (" %c[%s]", ((otyp[ctxnum]&0x20)?'*':' '),
+ ASNDESC->son[ai].sondef->name);
+ dobreak = 1;
+ } /* end if */
+ } /* end if */
+ break;
+ case CHOICE:
+ if (GR_TMPDESC) {
+ ASNDESC = TMPDESC;
+ TMPDESC = GR_TMPDESC;
+ GR_TMPDESC = 0;
+ } else if (TMPDESC) {
+ ASNDESC = TMPDESC;
+ TMPDESC = 0;
+ }
+ if (gr_tmpai) {
+ ai = tmpai;
+ tmpai = gr_tmpai;
+ gr_tmpai = 0;
+ } else if (tmpai) {
+ ai = tmpai;
+ tmpai = 0;
+ }
+ break;
+
+ case SEQUENCE:
+ if (ai == -2) {
+ ai = 0;
+ } else {
+ do {
+ ai++;
+ } while \
+ (ai < ASNDESC->nbson && i < 32 && mytype[i] && \
+ ASNDESC->son[ai].sondef &&
+ /*
+ ** For this test SEQUENCE & SEQUENCE OF
+ ** are the same, so suppress last bit
+ */
+ (ASNDESC->son[ai].sondef->type&0xFE) != mytype[i]);
+ } /* end if */
+ if (ai < ASNDESC->nbson) {
+ SASNDESC = \
+ ASNDESC->son[ai].sondef;
+ if (ASNDESC->son[ai].sonname) {
+ if \
+ (ASNDESC->son[ai].sondef &&
+ ASNDESC->son[ai].sondef->name) {
+ asnshw4 \
+ (" %c[%s %s]", ((otyp[ctxnum]&0x20)?'*':' '),
+ ASNDESC->son[ai].sonname,
+ ASNDESC->son[ai].sondef->name);
+ } else {
+ asnshw3 \
+ (" %c[%s]", ((otyp[ctxnum]&0x20)?'*':' '),
+ ASNDESC->son[ai].sonname);
+ } /* end if */
+ dobreak = 1;
+ } else if \
+ (ASNDESC->son[ai].sondef &&
+ ASNDESC->son[ai].sondef->name) {
+ asnshw3 \
+ (" %c[%s]", ((otyp[ctxnum]&0x20)?'*':' '),
+ ASNDESC->son[ai].sondef->name);
+ dobreak = 1;
+ } /* end if */
+ } /* end if */
+ break;
+ case SEQUENCEOF:
+ ai = 0;
+ SASNDESC = ASNDESC->son[ai].sondef;
+ if (ASNDESC->son[ai].sonname) {
+ if (ASNDESC->son[ai].sondef && \
+ ASNDESC->son[ai].sondef->name) {
+ asnshw4 \
+ (" %c[%s %s]", ((otyp[ctxnum]&0x20)?'*':' '),
+ ASNDESC->son[ai].sonname,
+ ASNDESC->son[ai].sondef->name);
+ } else {
+ asnshw3 \
+ (" %c[%s]", ((otyp[ctxnum]&0x20)?'*':' '),
+ ASNDESC->son[ai].sonname);
+ } /* end if */
+ dobreak = 1;
+ } else if \
+ (ASNDESC->son[ai].sondef &&
+ ASNDESC->son[ai].sondef->name) {
+ asnshw3 \
+ (" %c[%s]", ((otyp[ctxnum]&0x20)?'*':' '),
+ ASNDESC->son[ai].sondef->name);
+ dobreak = 1;
+ } /* end if */
+ } /* end switch */
+ if (dobreak) {
+ break;
+ } /* end if */
+ } /* end if */
+ if (uclass[i]) {
+ asnshw3 \
+ (" %c[%s]", ((otyp[ctxnum]&0x20)?'*':' '), uclass[i]);
+ } else {
+ asnshw4 \
+ (" %c[%s%d]", ((otyp[ctxnum]&0x20)?'*':' '),
+ class[0], i);
+ }
+ break;
+ case 1: /* application */
+
+ if (ASNDESC) {
+
+ for (ai = 0; ai < ASNDESC->nbson; ++ai) {
+ int i2 = 0;
+
+ if \
+ (ASNDESC->son[ai].sondef &&
+ ASNDESC->son[ai].sondef->type == CHOICE) {
+ while \
+ (i2 < ASNDESC->son[ai].sondef->nbson &&
+ ASNDESC->son[ai].sondef->son[i2].sondef && \
+ ASNDESC->son[ai].sondef->son[i2].sondef->application != i) {
+ i2++;
+ continue;
+ }
+ if \
+ (i2 == ASNDESC->son[ai].sondef->nbson) {
+ ai = ASNDESC->nbson;
+ break;
+ }
+ if (TMPDESC) {
+ GR_TMPDESC = TMPDESC;
+ gr_tmpai = tmpai;
+ }
+ TMPDESC = ASNDESC;
+ ASNDESC = ASNDESC->son[ai].sondef;
+ tmpai = ai;
+ ai = i2;
+ }
+
+ if (ASNDESC->son[ai].sondef && \
+ ASNDESC->son[ai].sondef->application == i) {
+ SASNDESC = \
+ ASNDESC->son[ai].sondef;
+ if (ASNDESC->son[ai].sonname) {
+ if \
+ (ASNDESC->son[ai].sondef->name) {
+ asnshw3 \
+ (" %s %s", ASNDESC->son[ai].sonname,
+ ASNDESC->son[ai].sondef->name);
+ } else {
+ asnshw2 \
+ (" %s", ASNDESC->son[ai].sonname);
+ } /* end if */
+ } else if \
+ (ASNDESC->son[ai].sondef->name) {
+ asnshw2 \
+ (" %s", ASNDESC->son[ai].sondef->name);
+ } /* end if */
+ break;
+ } /* end if */
+ } /* end for */
+ if (ai >= ASNDESC->nbson) {
+ ai = -1; /* not found */
+ } /* end if */
+ } /* end if */
+ if (PTRaclass[i]) {
+ asnshw5 \
+ (" %c[%s%d: %s]", ((otyp[ctxnum]&0x20)?'*':' '),
+ class[1], i, PTRaclass[i]);
+ (void) strcpy(operation, (char *)PTRaclass[i]);
+ } else {
+ asnshw4 \
+ (" %c[%s%d]", ((otyp[ctxnum]&0x20)?'*':' '), \
+ class[1], i);
+ }
+ break;
+
+ case 2: /* context-specific */
+
+ if (TMPDESC) {
+ ASNDESC = TMPDESC;
+ TMPDESC = GR_TMPDESC;
+ already = 1;
+ }
+ if (ASNDESC) {
+
+ for (ai = 0; ai < ASNDESC->nbson; ++ai) {
+ if \
+ (!already && ASNDESC->son[ai].sondef &&
+ ASNDESC->son[ai].sondef->type == CHOICE) {
+ int i2 = 0;
+ while \
+ (i2 < ASNDESC->son[ai].sondef->nbson &&
+ ASNDESC->son[ai].sondef->son[i2].tag != i) {
+ i2++;
+ continue;
+ }
+ if (i2 == \
+ ASNDESC->son[ai].sondef->nbson) {
+ ai = ASNDESC->nbson;
+ break;
+ }
+ if (TMPDESC) {
+ GR_TMPDESC = TMPDESC;
+ gr_tmpai = tmpai;
+ }
+ TMPDESC = ASNDESC;
+ ASNDESC = \
+ ASNDESC->son[ai].sondef;
+ tmpai = ai;
+ ai = i2;
+ }
+
+ if \
+ (ASNDESC->son[ai].tag == i) {
+ SASNDESC = \
+ ASNDESC->son[ai].sondef;
+ if (ASNDESC->son[ai].sonname) {
+ if \
+ (ASNDESC->son[ai].sondef &&
+ ASNDESC->son[ai].sondef->name) {
+ asnshw3 \
+ (" %s %s", ASNDESC->son[ai].sonname,
+ ASNDESC->son[ai].sondef->name);
+ } else {
+ asnshw2 \
+ (" %s", ASNDESC->son[ai].sonname);
+ } /* end if */
+ } else if \
+ (ASNDESC->son[ai].sondef &&
+ ASNDESC->son[ai].sondef->name) {
+ asnshw2 \
+ (" %s", ASNDESC->son[ai].sondef->name);
+ } /* end if */
+ break;
+ } /* end if */
+ } /* end for */
+ if (ai >= ASNDESC->nbson) {
+ ai = -1; /* not found */
+ } /* end if */
+ } /* end if */
+ asnshw3 \
+ (" %c[%d]", ((otyp[ctxnum]&0x20)?'*':' '), i);
+ break;
+
+ case 3: /* private */
+ asnshw4 \
+ (" %c[%s%d]", ((otyp[ctxnum]&0x20)?'*':' '), \
+ class[3], i);
+ } /* esac: tag */
+
+ /*
+ ** print the length - as a debug tool only.
+ */
+ /* asnshw2(" Length=%d ",rlen); */
+ asnshw1("\n");
+ if (rlen > pdulen) {
+ asnshw1("*** Decode length error,");
+ asnshw2(" PDU length = %d ***\n", pdulen);
+ rlen = pdulen;
+ }
+
+ /*
+ ** recursive interpretation of the value if constructor
+ */
+ if (otyp[ctxnum]&0x20) { /* constructor */
+
+ stlv = decpdu((rlen?rlen:pdulen), \
+ ASNDESC && ai != -1 ?(ai == -2 ? ASNDESC:
+ ASNDESC->son[ai].sondef):0, ctxnum);
+ /* recursive decoding */
+ pdulen -= stlv;
+ effnb += stlv;
+ } else if (otyp[ctxnum] == 0x06) {
+ /*
+ ** interpretation of the object identifier
+ */
+ for (j = 0; (rlen) && (pdulen > 0); \
+ --rlen, --pdulen, ++effnb) {
+ GETNEXT(ctxnum);
+ oidstr[j++] = hex;
+ }
+
+ /* interpret the object identifier */
+ oidstr[j++] = '\0';
+ oix = oidmap(oidstr, j-1);
+ asnshw1("\n");
+ if (oix >= 0 && oix < OIDNB) { /* recognized obj id */
+ asnshw2("%s\n", OidTab[oix].oidname);
+ } else {
+ asnshw1("Unknown Oid\n");
+ }
+ } else {
+ /*
+ ** interpretation of other primitive tags
+ */
+ if (!otyp[ctxnum] && !rlen) {
+ /* end of contents: any form length */
+ pdulen = 0;
+ } else {
+ X hexstr[5];
+ int k = 0;
+ int klen = rlen;
+ if (SASNDESC && SASNDESC->type == CONTENT && \
+ SASNDESC->nbson && SASNDESC->son[0].sondef) {
+ (void)
+ decpdu(rlen, SASNDESC->son[0].sondef, ctxnum);
+ } else {
+ if (rlen < 200) {
+ for (j = 0, slen = 0; \
+ (rlen) && (pdulen > 0);
+ --rlen, --pdulen, ++effnb)
+ {
+ if (!slen) {
+ (void) \
+ strcpy((char *)scrlin, "LDAP: "); j += 7;
+ for \
+ (i = 0; i < level[ctxnum]; ++i) {
+ scrlin[j++] = ' ';
+ scrlin[j++] = ' ';
+ scrlin[j++] = ' ';
+ scrlin[j++] = ' ';
+ }
+ }
+
+ GETNEXT(ctxnum);
+ if (k < 5) {
+ hexstr[k++] = hex;
+ } /* end if */
+ if (!isprint(hex)) {
+ hex = '_';
+ dontprint = 1;
+ }
+ scrlin[j++] = hex;
+ if ((slen += 2) >= \
+ (72 - (level[ctxnum] * 3))) {
+ slen = 0;
+ scrlin[j] = 0;
+ if (!dontprint) {
+ asnshw2 \
+ ("%s\n", scrlin);
+ }
+ j = 0;
+ }
+ } /* rof: primitive values */
+ if (slen) {
+ scrlin[j] = 0;
+ if (!dontprint) {
+ asnshw2("%s\n", scrlin);
+ }
+ }
+ dontprint = 0;
+ } else {
+ asnshw2("%s ", "LDAP:");
+ for (i = 0; i < level[ctxnum]; ++i) {
+ asnshw1(" ");
+ scrlin[j++] = ' ';
+ scrlin[j++] = ' ';
+ scrlin[j++] = ' ';
+ }
+
+ for (j = 0; (rlen) && (pdulen > 0); \
+ --rlen, --pdulen, ++effnb) {
+ GETNEXT(ctxnum);
+ if (k < 5) {
+ hexstr[k++] = hex;
+ }
+ }
+ (void) strcpy \
+ ((char *)scrlin, \
+ "*** NOT PRINTED - Too long value ***");
+ asnshw2("%s\n", scrlin);
+ }
+
+ if \
+ (SASNDESC && SASNDESC->type == BITSTRING &&\
+ klen <= 5) {
+ unsigned long bitstr = 0;
+ for (i = 1; i < 5; ++i) {
+ bitstr = \
+ ((bitstr) << 8) + ((i < klen)?hexstr[i]:0);
+ } /* end for */
+ for \
+ (i = 0; i < SASNDESC->nbson; ++i) {
+ if ((bitstr & \
+ ((unsigned long)SASNDESC->son[i].sondef)) ==
+ ((unsigned long)SASNDESC->son[i].tag)) {
+ if \
+ (SASNDESC->son[i].sonname) {
+ int k;
+ asnshw2 \
+ ("%s ", "LDAP:");
+ for \
+ (k = 0; k < level[ctxnum]; ++k) {
+ asnshw1(" ");
+ }
+ asnshw2 \
+ ("%s", SASNDESC->son[i].sonname);
+ } /* end if */
+ } /* end if */
+ } /* end for */
+ } /* end if */
+ if (SASNDESC && \
+ (SASNDESC->type == ENUM ||
+ SASNDESC->type == CONTENTTYPE) && klen <= 5) {
+ unsigned long value = 0;
+ for (i = 0; i < klen; ++i) {
+ value = \
+ ((value) << 8) + hexstr[i];
+ } /* end for */
+ for \
+ (i = 0; i < SASNDESC->nbson; ++i) {
+ if \
+ (value == ((unsigned long)SASNDESC->son[i].tag)) {
+ if \
+ (SASNDESC->son[i].sonname) {
+ int k;
+ asnshw2 \
+ ("%s ", "LDAP:");
+ for \
+ (k = 0; k < level[ctxnum]; ++k) {
+ asnshw1(" ");
+ }
+ asnshw2 \
+ ("%s\n", SASNDESC->son[i].sonname);
+ (void) \
+ strcpy(resultcode, SASNDESC->son[i].sonname);
+ } /* end if */
+ break;
+ } /* end if */
+ } /* end for */
+ } /* end if */
+
+ } /* end if */
+ } /* fi: constructor/obj-id/primitive */
+ } /* fi: tag analysis */
+ } /* elihw: len>1 */
+ --level[ctxnum];
+ return (effnb);
+}
+
+
+/* init_ldap initializes various buffers and variables */
+/* it is called one-time (in snoop_filter.c) only. */
+
+void
+init_ldap()
+{
+ int i;
+
+ for (i = 0; i < MAX_CTX; i++) {
+ gi_osibuf[i] = 0;
+ level[i] = 0;
+ }
+}
+static void
+ldapdump(char *data, int datalen)
+{
+ char *p;
+ ushort_t *p16 = (ushort_t *)data;
+ char *p8 = data;
+ int i, left, len;
+ int chunk = 16; /* 16 bytes per line */
+
+ asnshw1("LDAP: Skipping until next full LDAPMessage\n");
+
+ for (p = data; p < data + datalen; p += chunk) {
+ asnshw2("LDAP:\t%4d: ", p - data);
+ left = (data + datalen) - p;
+ len = MIN(chunk, left);
+ for (i = 0; i < (len / 2); i++)
+ asnshw2("%04x ", ntohs(*p16++) & 0xffff);
+ if (len % 2) {
+ asnshw2("%02x ", *((unsigned char *)p16));
+ }
+ for (i = 0; i < (chunk - left) / 2; i++)
+ asnshw1(" ");
+
+ asnshw1(" ");
+ for (i = 0; i < len; i++, p8++)
+ asnshw2("%c", isprint(*p8) ? *p8 : '.');
+ asnshw1("\n");
+ }
+
+ asnshw1("LDAP:\n");
+}
+
+/* decode_ldap is the entry point for the main decoding function */
+/* decpdu(). decode_ldap() is only called by interpret_ldap. */
+
+void
+decode_ldap(char *buf, int len)
+{
+ asndefTp ASNDESC = 0;
+ char *newbuf;
+ int skipped = 0;
+
+ PTRaclass = MHSaclass;
+ ASNDESC = &MPDU;
+
+
+ newbuf = skipjunk(len, buf);
+ if (newbuf > buf) {
+ skipped = newbuf-buf;
+ ldapdump(buf, newbuf-buf);
+ }
+ buf = newbuf;
+ len = len-skipped;
+ osibuff = buf; /* Undecoded buf is passed by interpret_ldap */
+ osilen = len; /* length of tcp data is also passed */
+
+ (void) decpdu(len, ASNDESC, 0);
+ gi_osibuf[0] = 0;
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_mip.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_mip.c
new file mode 100644
index 0000000000..2db8c6eb2f
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_mip.c
@@ -0,0 +1,869 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <protocols/routed.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include "snoop.h"
+#include "snoop_mip.h"
+
+/*
+ * This defines the length of internal, unbounded buffers. We set
+ * this to be MAXLINE (the maximum verbose display line length) -
+ * 64, which should be enough for all necessary descriptions.
+ */
+#define BUFLEN MAXLINE - 64
+
+extern char *dlc_header;
+extern char *addrtoname();
+
+static enum EXT_TYPE { ADV, REG };
+
+/*
+ * This defines the interface for all extention interpreter
+ * functions. The function will be called with following
+ * parameters:
+ *
+ * type: IN The type code for this extention
+ * len IN The length of the payload (i.e. the
+ * length field in an extension header)
+ * payload IN A pointer to the beginning of the
+ * extension payload
+ */
+typedef void interpreter_f(uint8_t type, uint8_t len, uchar_t *payload);
+
+struct ext_dispatch {
+ uint8_t type;
+ interpreter_f *pfunc;
+};
+
+/* Description structure -- maps type to description */
+struct ext_desc {
+ uint8_t type;
+ const char *desc;
+};
+
+/*
+ * Interpreter function prototypes for both adv and reg. These
+ * all must implement the interpret_f interface defined above.
+ */
+static void spi_ext(uint8_t, uint8_t, uchar_t *);
+static void key_ext(uint8_t, uint8_t, uchar_t *);
+static void trav_ext(uint8_t, uint8_t, uchar_t *);
+static void empty_ext(uint8_t, uint8_t, uchar_t *);
+static void nai_ext(uint8_t, uint8_t, uchar_t *);
+static void chall_ext(uint8_t, uint8_t, uchar_t *);
+static void ma_ext(uint8_t, uint8_t, uchar_t *);
+static void prefix_ext(uint8_t, uint8_t, uchar_t *);
+static void unk_ext(uint8_t, uint8_t, uchar_t *);
+
+/* R E G I S T R A T I O N */
+
+#define REG_TBL_LEN 10 /* update this when adding to the table */
+
+/* Reg: type to description mapping table */
+static struct ext_desc reg_desc[] = {
+ MN_HA_AUTH, "(Mobile-Home Authentication Extension)",
+ MN_FA_AUTH, "(Mobile-Foreign Authentication Extension",
+ FA_HA_AUTH, "(Foreign-Home Authentication Extension)",
+ GEN_AUTH, "(Generalized Authentication Extension)",
+ MN_HA_KEY, "(Mobile-Home Key Extension)",
+ MN_FA_KEY, "(Mobile-Foreign Key Extension)",
+ MN_HA_TRAVERSE, "(Firewall Traversal Extension)",
+ ENCAP_DELIV, "(Encapsulating Delivery Style Extension)",
+ MN_NAI, "(Mobile Node Network Access Identifier)",
+ FA_CHALLENGE, "(Mobile-Foreign Agent Challenge)",
+ 0, "(Unrecognized Extension)"
+};
+
+#define GENAUTH_TBL_LEN 1 /* update this when adding to the table */
+
+/* Subtypes for Generic Authentication Extension type (type 36) */
+static struct ext_desc genauth_desc[] = {
+ GEN_AUTH_MN_AAA, "(MN-AAA Authentication Subtype)",
+ 0, "(Unrecognized Subtype)"
+};
+
+/* Reg: type to function mapping table */
+static struct ext_dispatch reg_dispatch[] = {
+ MN_HA_AUTH, spi_ext,
+ MN_FA_AUTH, spi_ext,
+ FA_HA_AUTH, spi_ext,
+ GEN_AUTH, spi_ext,
+ MN_HA_KEY, key_ext,
+ MN_FA_KEY, key_ext,
+ MN_HA_TRAVERSE, trav_ext,
+ ENCAP_DELIV, empty_ext,
+ MN_NAI, nai_ext,
+ FA_CHALLENGE, chall_ext,
+ 0, unk_ext
+};
+
+/* A D V E R T I S E M E N T */
+
+#define ADV_TBL_LEN 5 /* update this when adding to the table */
+
+/* Adv: type to description mapping table */
+static struct ext_desc adv_desc[] = {
+ ICMP_ADV_MSG_PADDING_EXT, "(Padding)",
+ ICMP_ADV_MSG_MOBILITY_AGT_EXT, "(Mobility Agent Extension)",
+ ICMP_ADV_MSG_PREFIX_LENGTH_EXT, "(Prefix Lengths)",
+ ICMP_ADV_MSG_FA_CHALLENGE, "(Foreign Agent Challenge)",
+ ICMP_ADV_MSG_FA_NAI, "(Foreign Agent NAI)",
+ 0, "(Unrecognized Extension)"
+};
+
+/* Adv: type to function mapping table */
+static struct ext_dispatch adv_dispatch[] = {
+ ICMP_ADV_MSG_PADDING_EXT, NULL, /* never called */
+ ICMP_ADV_MSG_MOBILITY_AGT_EXT, ma_ext,
+ ICMP_ADV_MSG_PREFIX_LENGTH_EXT, prefix_ext,
+ ICMP_ADV_MSG_FA_CHALLENGE, chall_ext,
+ ICMP_ADV_MSG_FA_NAI, nai_ext,
+ 0, unk_ext
+};
+
+#define GETSPI(payload, hi, low) \
+ (void) memcpy(&hi, payload, sizeof (hi)); \
+ (void) memcpy(&low, payload + sizeof (hi), sizeof (low))
+
+static void dumphex(uchar_t *payload, int payload_len, char *buf, char *msg) {
+ int index;
+
+ for (index = 0; index < payload_len; index++) {
+ (void) sprintf(&buf[index * 3], " %.2x", payload[index]);
+ }
+
+ (void) sprintf(get_line((char *)payload-dlc_header, 1), msg, buf);
+}
+
+static const char *get_desc(struct ext_desc table[], uint8_t type, int max) {
+ int i;
+
+ for (i = 0; i < max && table[i].type != type; i++)
+ /* NO_OP */;
+
+ return (table[i].desc);
+}
+
+/*
+ * The following is an accessor for the description table, used by
+ * snoop_icmp.c. This maintains the encapsulation of the internal
+ * description table.
+ */
+const char *get_mip_adv_desc(uint8_t type) {
+ return (get_desc(adv_desc, type, ADV_TBL_LEN));
+}
+
+static interpreter_f *get_interpreter(struct ext_dispatch table[],
+ uint8_t type,
+ int max) {
+ int i;
+
+ for (i = 0; i < max && table[i].type != type; i++)
+ /* NO_OP */;
+
+ return (table[i].pfunc);
+}
+
+static int
+interpret_extensions(uchar_t *ext,
+ int regext_size,
+ enum EXT_TYPE etype) {
+
+ int curr_size = regext_size; /* remaining total for all exts */
+ exthdr_t *exthdr;
+ gen_exthdr_t *gen_exthdr;
+ const char *st;
+ uchar_t *p;
+ interpreter_f *f;
+ uint8_t ext_type;
+ uint16_t ext_len;
+ uint_t ext_hdrlen;
+
+ show_space();
+ exthdr = (exthdr_t *)ALIGN(ext);
+
+
+ do {
+ ext_type = exthdr->type;
+ if (ext_type == GEN_AUTH) {
+ gen_exthdr = (gen_exthdr_t *)exthdr;
+ ext_hdrlen = sizeof (gen_exthdr_t);
+ ext_len = ntohs(gen_exthdr->length);
+ } else {
+ ext_hdrlen = sizeof (exthdr_t);
+ ext_len = exthdr->length;
+ }
+
+ if (!((etype == ADV && ext_type == ICMP_ADV_MSG_PADDING_EXT &&
+ curr_size >= 1) ||
+ curr_size >= ext_hdrlen + ext_len))
+ break;
+
+ /* Print description for this extension */
+ if (etype == ADV) {
+ st = get_desc(adv_desc, ext_type, ADV_TBL_LEN);
+ } else /* REG */ {
+ st = get_desc(reg_desc, ext_type, REG_TBL_LEN);
+ }
+
+ (void) sprintf(get_line((char *)exthdr-dlc_header, 1),
+ "Extension header type = %d %s", ext_type, st);
+
+ if (ext_type == GEN_AUTH) {
+ st = get_desc(genauth_desc, gen_exthdr->subtype,
+ GENAUTH_TBL_LEN);
+ (void) sprintf(get_line((char *)exthdr-dlc_header, 1),
+ "Subtype = %d %s", gen_exthdr->subtype, st);
+ }
+
+ /* Special case for 1-byte padding */
+ if (etype == ADV && ext_type == ICMP_ADV_MSG_PADDING_EXT) {
+ exthdr = (exthdr_t *)((uchar_t *)exthdr + 1);
+ curr_size--;
+ continue;
+ }
+
+ (void) sprintf(get_line((char *)&exthdr->length-dlc_header, 1),
+ "Length = %d", ext_len);
+
+ /* Parse out the extension's payload */
+ p = (uchar_t *)exthdr + ext_hdrlen;
+ curr_size -= (ext_hdrlen + ext_len);
+
+ if (etype == ADV) {
+ f = get_interpreter(adv_dispatch, ext_type, ADV_TBL_LEN);
+ } else /* REG */ {
+ f = get_interpreter(reg_dispatch, ext_type, REG_TBL_LEN);
+ }
+
+ f(ext_type, ext_len, p);
+
+ show_space();
+ exthdr = (exthdr_t *)(p + ext_len);
+ } while (B_TRUE);
+
+ return (0);
+}
+
+void interpret_icmp_mip_ext(uchar_t *p, int len) {
+ show_space();
+ show_header("ICMP: ", " MIP Advertisement Extensions ", len);
+ show_space();
+
+ interpret_extensions(p, len, ADV);
+}
+
+void
+interpret_mip_cntrlmsg(int flags, uchar_t *msg, int fraglen) {
+ char *pt, *pc = NULL;
+ char *line;
+ regreq_t rreq[1];
+ regrep_t rrep[1];
+ int regext_size;
+ uchar_t *regext_data;
+ struct in_addr addr_temp;
+
+
+ /* First byte of the message should be the type */
+ switch (*msg) {
+ case REG_TYPE_REQ:
+ if (fraglen < sizeof (regreq_t))
+ return;
+ pt = (flags & F_DTAIL ? "registration request ":"reg rqst ");
+
+ (void) memcpy(rreq, msg, sizeof (*rreq));
+ regext_size = fraglen - sizeof (regreq_t);
+ regext_data = msg + sizeof (*rreq);
+ break;
+ case REG_TYPE_REP:
+ if (fraglen < sizeof (regrep_t))
+ return;
+ pt = (flags & F_DTAIL ? "registration reply ":"reg reply ");
+
+ (void) memcpy(rrep, msg, sizeof (*rrep));
+ regext_size = fraglen - sizeof (regrep_t);
+ regext_data = msg + sizeof (*rrep);
+
+ switch (rrep->code) {
+ case REPLY_CODE_ACK:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL)) ?
+ "OK" : "OK code 0";
+ break;
+ case REPLY_CODE_ACK_NO_SIMULTANEOUS:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "OK simultaneous bindings" : "OK code 1";
+ break;
+ case REPLY_CODE_FA_NACK_UNSPECIFIED:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "FA denial: unspecified":"FA denial: code 64";
+ break;
+ case REPLY_CODE_FA_NACK_PROHIBITED:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "FA denial: prohibited":"FA denial: code 65";
+ break;
+ case REPLY_CODE_FA_NACK_RESOURCES:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "FA denial: no resources":"FA denial: code 66";
+ break;
+ case REPLY_CODE_FA_NACK_MN_AUTH:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "FA denial: MN auth failed":"FA denial: code 67";
+ break;
+ case REPLY_CODE_FA_NACK_HA_AUTH:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "FA denial: HA auth failed":
+ "FA denial: code 68";
+ break;
+ case REPLY_CODE_FA_NACK_LIFETIME:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "FA denial: lifetime":"FA denial: code 69";
+ break;
+ case REPLY_CODE_FA_NACK_BAD_REQUEST:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "FA denial: bad request": "FA: code 70";
+ break;
+ case REPLY_CODE_FA_NACK_BAD_REPLY:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "FA denial: bad Reply":"FA denial: code 71";
+ break;
+ case REPLY_CODE_FA_NACK_ENCAP_UNAVAILABLE:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "FA denial: encapsulation":"FA denial: code 72";
+ break;
+ case REPLY_CODE_FA_NACK_VJ_UNAVAILABLE:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "FA denial: VJ compression":"FA denial: code 73";
+ break;
+ case REPLY_CODE_FA_NACK_BIDIR_TUNNEL_UNAVAILABLE:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "FA denial: reverse tunnel unavailable":
+ "FA denial: code 74";
+ break;
+ case REPLY_CODE_FA_NACK_BIDIR_TUNNEL_NO_TBIT:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "FA denial: reverse tunnel: missing T-bit":
+ "FA denial: code 75";
+ break;
+ case REPLY_CODE_FA_NACK_BIDIR_TUNNEL_TOO_DISTANT:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "FA denial: reverse tunnel: too distant":
+ "FA denial: code 76";
+ break;
+ case REPLY_CODE_FA_NACK_ICMP_HA_NET_UNREACHABLE:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "FA denial: home network unreachable":
+ "FA denial: code 80";
+ break;
+ case REPLY_CODE_FA_NACK_ICMP_HA_HOST_UNREACHABLE:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "FA denial: HA host unreachable":
+ "FA denial: code 81";
+ break;
+ case REPLY_CODE_FA_NACK_ICMP_HA_PORT_UNREACHABLE:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "FA denial: HA port unreachable":
+ "FA denial: code 82";
+ break;
+ case REPLY_CODE_FA_NACK_ICMP_HA_UNREACHABLE:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "FA denial: HA unreachable":"FA denial: code 88";
+ break;
+ case REPLY_CODE_FA_NACK_UNIQUE_HOMEADDR_REQD:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "FA denial: Unique Home Addr Required":
+ "FA denial: code 96";
+ break;
+ case REPLY_CODE_FA_NACK_MISSING_NAI:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "FA denial: Missing NAI":
+ "FA denial: code 97";
+ break;
+ case REPLY_CODE_FA_NACK_MISSING_HOME_AGENT:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "FA denial: Missing Home Agent":
+ "FA denial: code 98";
+ break;
+ case REPLY_CODE_FA_NACK_UNKNOWN_CHALLENGE:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "FA denial: Unknown Challenge":
+ "FA denial: code 104";
+ break;
+ case REPLY_CODE_FA_NACK_MISSING_CHALLENGE:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "FA denial: Missing Challenge":
+ "FA denial: code 105";
+ break;
+ case REPLY_CODE_FA_NACK_MISSING_MN_FA:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "FA denial: Missing Mobile-Foreign Key Extension":
+ "FA denial: code 106";
+ break;
+ case REPLY_CODE_HA_NACK_UNSPECIFIED:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "HA denial: unspecified":"HA denial: code 128";
+ break;
+ case REPLY_CODE_HA_NACK_PROHIBITED:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "HA denial: prohibited":"HA denial: code 129";
+ break;
+ case REPLY_CODE_HA_NACK_RESOURCES:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "HA denial: no resources":"HA denial: code 130";
+ break;
+ case REPLY_CODE_HA_NACK_MN_AUTH:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "HA denial: MN auth failed":"HA denial: code 131";
+ break;
+ case REPLY_CODE_HA_NACK_FA_AUTH:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "HA denial: FA auth failed":"HA denial: code 132";
+ break;
+ case REPLY_CODE_HA_NACK_ID_MISMATCH:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "HA denial: ID mismatch":"HA denial: code 133";
+ break;
+ case REPLY_CODE_HA_NACK_BAD_REQUEST:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "HA denial: bad request":"HA denial: code 134";
+ break;
+ case REPLY_CODE_HA_NACK_TOO_MANY_BINDINGS:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "HA denial: too many bindings":
+ "HA denial: code 135";
+ break;
+ case REPLY_CODE_HA_NACK_BAD_HA_ADDRESS:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "HA denial: bad HA address":"HA denial: code 136";
+ break;
+ case REPLY_CODE_HA_NACK_BIDIR_TUNNEL_UNAVAILABLE:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "HA denial: no reverse tunnel":
+ "HA denial: code 137";
+ break;
+ case REPLY_CODE_HA_NACK_BIDIR_TUNNEL_NO_TBIT:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "HA denial: reverse tunnel: no T-bit":
+ "HA denial: code 138";
+ break;
+ case REPLY_CODE_HA_NACK_BIDIR_ENCAP_UNAVAILABLE:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "HA denial: encapsulation unavailable":
+ "HA denial: code 139";
+ break;
+ default:
+ pc = "?";
+ break;
+ }
+ break;
+
+ default :
+ break;
+ }
+ if (flags & F_SUM) {
+ line = get_sum_line();
+
+ if (pc != NULL)
+ (void) sprintf(line, "Mobile IP %s(%s)", pt, pc);
+ else
+ (void) sprintf(line, "Mobile IP %s", pt);
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("MIP: ", "Mobile IP Header", fraglen);
+ show_space();
+
+ if (*msg == REG_TYPE_REQ) {
+ (void) sprintf(get_line((char *)&rreq -
+ dlc_header, 1), "Registration header type = %s",
+ pt);
+ (void) sprintf(get_line(
+ (char *)(((uchar_t *)&rreq) + 1) - dlc_header, 1),
+ "%d... .... = %s simultaneous bindings ",
+ (rreq->Simultaneous_registration == 1)? 1 : 0,
+ (rreq->Simultaneous_registration == 1)? "":"no");
+ (void) sprintf(get_line(
+ (char *)(((uchar_t *)&rreq) + 1) - dlc_header, 1),
+ ".%d.. .... = %s broadcast datagrams ",
+ (rreq->Broadcasts_desired == 1) ? 1 : 0,
+ (rreq->Broadcasts_desired == 1) ? "":"no");
+ (void) sprintf(get_line(
+ (char *)(((uchar_t *)&rreq) + 1) - dlc_header, 1),
+ "..%d. .... = %s decapsulation by MN",
+ (rreq->Decapsulation_done_locally == 1) ? 1 : 0,
+ (rreq->Decapsulation_done_locally == 1) ?
+ "" : "no");
+ (void) sprintf(get_line(
+ (char *)(((uchar_t *)&rreq) + 1) - dlc_header, 1),
+ "...%d .... = %s minimum encapsulation ",
+ (rreq->Minimal_encap_desired == 1) ? 1 : 0,
+ (rreq->Minimal_encap_desired == 1) ? "" : "no");
+ (void) sprintf(get_line(
+ (char *)(((uchar_t *)&rreq) + 1) - dlc_header, 1),
+ ".... %d... = %s GRE encapsulation ",
+ (rreq->GRE_encap_desired == 1) ? 1 : 0,
+ (rreq->GRE_encap_desired == 1) ? "" : "no");
+ (void) sprintf(get_line(
+ (char *)(((uchar_t *)&rreq) + 1) - dlc_header, 1),
+ ".... .%d.. = %s VJ hdr Compression ",
+ (rreq->VJ_compression_desired == 1) ? 1 : 0,
+ (rreq->VJ_compression_desired == 1) ? "" : "no");
+ (void) sprintf(get_line(
+ (char *)(((uchar_t *)&rreq) + 1) - dlc_header, 1),
+ ".... ..%d. = %s reverse tunnel",
+ (rreq->BiDirectional_Tunnel_desired == 1) ? 1 : 0,
+ (rreq->BiDirectional_Tunnel_desired == 1) ?
+ "" : "no");
+ if (ntohs(rreq->lifetime) == 0xffff) {
+ (void) sprintf(get_line(
+ (char *)&rreq->lifetime - dlc_header, 1),
+ "Life Time = 0xFFFF (infinity)");
+ } else if (ntohs(rreq->lifetime) == 0) {
+ (void) sprintf(get_line(
+ (char *)&rreq->lifetime - dlc_header, 1),
+ "Life Time = 0 "
+ "(request for de-registration)");
+ } else {
+ (void) sprintf(get_line(
+ (char *)&rreq->lifetime - dlc_header, 1),
+ "Life time = %d seconds",
+ ntohs(rreq->lifetime));
+ }
+ addr_temp.s_addr = rreq->home_addr;
+ (void) sprintf(get_line(
+ (char *)&rreq->home_addr - dlc_header, 1),
+ "Home address = %s, %s",
+ inet_ntoa(addr_temp),
+ addrtoname(AF_INET, &addr_temp));
+ addr_temp.s_addr = rreq->home_agent_addr;
+ (void) sprintf(get_line(
+ (char *)&rreq->home_agent_addr - dlc_header, 1),
+ "Home Agent address = %s, %s",
+ inet_ntoa(addr_temp),
+ addrtoname(AF_INET, &addr_temp));
+ addr_temp.s_addr = rreq->care_of_addr;
+ (void) sprintf(get_line(
+ (char *)&rreq->care_of_addr - dlc_header, 1),
+ "Care of address = %s, %s",
+ inet_ntoa(addr_temp),
+ addrtoname(AF_INET, &addr_temp));
+ (void) sprintf(get_line(
+ (char *)&rreq->identification - dlc_header, 1),
+ "Identification = 0x%x-%x",
+ ntohl(rreq->identification.high_bits),
+ ntohl(rreq->identification.low_bits));
+ } else if (*msg == REG_TYPE_REP) {
+ (void) sprintf(
+ get_line((char *)&rrep->type - dlc_header, 1),
+ "Registration header type = %d (%s)",
+ (int)rrep->type, pt);
+ (void) sprintf(get_line((char *)&rrep - dlc_header, 1),
+ "Code = %d %s", (int)rrep->code, pc);
+ if (ntohs(rrep->lifetime) == 0xffff) {
+ (void) sprintf(get_line(
+ (char *)&rrep->lifetime - dlc_header, 1),
+ "Life time = 0xFFFF (infinity)");
+ } else if (ntohs(rrep->lifetime) == 0) {
+ (void) sprintf(get_line(
+ (char *)&rrep->lifetime - dlc_header, 1),
+ ((rrep->code == REPLY_CODE_ACK) ||
+ (rrep->code ==
+ REPLY_CODE_ACK_NO_SIMULTANEOUS))?
+ "Life time = 0 (de-registeration success)" :
+ "Life time = 0 (de-registration failed)");
+ } else {
+ (void) sprintf(get_line(
+ (char *)&rrep->lifetime - dlc_header, 1),
+ "Life time = %d seconds",
+ ntohs(rrep->lifetime));
+ }
+ addr_temp.s_addr = rrep->home_addr;
+ (void) sprintf(
+ get_line((char *)&rrep->home_addr - dlc_header, 1),
+ "Home address = %s, %s",
+ inet_ntoa(addr_temp),
+ addrtoname(AF_INET, &addr_temp));
+ addr_temp.s_addr = rrep->home_agent_addr;
+ (void) sprintf(get_line(
+ (char *)&rrep->home_agent_addr - dlc_header, 1),
+ "Home Agent address = %s, %s",
+ inet_ntoa(addr_temp),
+ addrtoname(AF_INET, &addr_temp));
+ (void) sprintf(get_line(
+ (char *)&rrep->identification - dlc_header, 1),
+ "Identification = 0x%x-%x",
+ ntohl(rrep->identification.high_bits),
+ ntohl(rrep->identification.low_bits));
+ }
+ fraglen = interpret_extensions(regext_data, regext_size, REG);
+ }
+}
+
+/*ARGSUSED*/
+static void spi_ext(uint8_t type, uint8_t this_ext_len, uchar_t *p) {
+ uint16_t spi_hi, spi_low;
+ char auth_prn_str[BUFLEN];
+
+ /* SPI */
+ GETSPI(p, spi_hi, spi_low);
+ (void) sprintf(get_line((char *)p - dlc_header, 1),
+ "Security Parameter Index = 0x%x%x",
+ ntohs(spi_hi), ntohs(spi_low));
+ p += sizeof (spi_hi) + sizeof (spi_low);
+ this_ext_len -= sizeof (spi_hi) + sizeof (spi_low);
+
+ /* The rest is the authenticator; dump it in hex */
+ dumphex(p,
+ /* don't write past our string buffer ... */
+ (this_ext_len*3 > BUFLEN ? BUFLEN : this_ext_len),
+ auth_prn_str,
+ "Authenticator = %s");
+}
+
+static void key_ext(uint8_t type, uint8_t this_ext_len, uchar_t *p) {
+ uint16_t alg, spi_hi, spi_low;
+ char *alg_string;
+ char *hafa = (type == MN_HA_KEY ? "HA" : "FA");
+ char sec_msg[32];
+ char auth_prn_str[BUFLEN];
+
+ /* Algorithm Type */
+ (void) memcpy(&alg, p, sizeof (alg));
+ alg = ntohs(alg);
+ switch (alg) {
+ case KEY_ALG_NONE:
+ alg_string = "None";
+ break;
+ case SA_MD5_MODE_PREF_SUF:
+ alg_string = "MD5/prefix+suffix";
+ break;
+ case SA_HMAC_MD5:
+ alg_string = "HMAC MD5";
+ break;
+ default:
+ alg_string = "Unknown";
+ break;
+ }
+ (void) sprintf(get_line((char *)p-dlc_header, 1),
+ "Algorithm = 0x%x: %s", alg, alg_string);
+ p += sizeof (alg);
+ this_ext_len -= sizeof (alg);
+
+ /* AAA SPI */
+ GETSPI(p, spi_hi, spi_low);
+ (void) sprintf(get_line((char *)p - dlc_header, 1),
+ "AAA Security Parameter Index = 0x%x%x",
+ ntohs(spi_hi), ntohs(spi_low));
+ p += sizeof (spi_hi) + sizeof (spi_low);
+ this_ext_len -= sizeof (spi_hi) + sizeof (spi_low);
+
+ /* HA / FA SPI */
+ GETSPI(p, spi_hi, spi_low);
+ (void) sprintf(get_line((char *)p - dlc_header, 1),
+ "%s Security Parameter Index = 0x%x%x",
+ hafa, ntohs(spi_hi), ntohs(spi_low));
+ p += sizeof (spi_hi) + sizeof (spi_low);
+ this_ext_len -= sizeof (spi_hi) + sizeof (spi_low);
+
+ /* The rest is the security info; dump it in hex */
+ sprintf(sec_msg, "%s Security Info = %%s", hafa);
+ dumphex(p,
+ /* don't write past our string buffer ... */
+ (this_ext_len*3 > BUFLEN ? BUFLEN : this_ext_len),
+ auth_prn_str,
+ sec_msg);
+}
+
+/*ARGSUSED*/
+static void trav_ext(uint8_t type, uint8_t this_ext_len, uchar_t *p) {
+ struct in_addr addr_temp;
+
+ /* skip reserved */
+ p += 2;
+ this_ext_len -= 2;
+
+ /* Mobile-Home Traversal Address */
+ (void) memcpy(&(addr_temp.s_addr), p, sizeof (addr_temp.s_addr));
+ (void) sprintf(get_line((char *)p-dlc_header, 1),
+ "Mobile-Home Traversal Address= %s, %s",
+ inet_ntoa(addr_temp),
+ addrtoname(AF_INET, &addr_temp));
+ p += sizeof (addr_temp.s_addr);
+ this_ext_len -= sizeof (addr_temp.s_addr);
+
+ /* Home-Mobile Traversal Address */
+ (void) memcpy(&(addr_temp.s_addr), p, sizeof (addr_temp.s_addr));
+ (void) sprintf(get_line((char *)p-dlc_header, 1),
+ "Home-Mobile Traversal Address= %s, %s",
+ inet_ntoa(addr_temp),
+ addrtoname(AF_INET, &addr_temp));
+}
+
+/*ARGSUSED*/
+static void empty_ext(uint8_t type, uint8_t this_ext_len, uchar_t *p) {
+ /* no payload */
+}
+
+/*ARGSUSED*/
+static void nai_ext(uint8_t type, uint8_t this_ext_len, uchar_t *p) {
+ /* payload points to the NAI */
+ char *desc = "Network Access Identifier = ";
+ size_t desclen = strlen(desc) + 1 + this_ext_len;
+
+ (void) snprintf(get_line((char *)p-dlc_header, 1),
+ desclen > MAXLINE ? MAXLINE : desclen,
+ "%s%s", desc, p);
+}
+
+/*ARGSUSED*/
+static void chall_ext(uint8_t type, uint8_t this_ext_len, uchar_t *p) {
+ char auth_prn_str[BUFLEN];
+
+ /* payload points to the challenge */
+ dumphex(p,
+ /* don't write past our string buffer ... */
+ (this_ext_len*3 > BUFLEN ? BUFLEN / 3 : this_ext_len),
+ auth_prn_str,
+ "Challenge = %s");
+}
+
+/*ARGSUSED*/
+static void ma_ext(uint8_t type, uint8_t this_ext_len, uchar_t *p) {
+ mobagtadvext_t adv_ext[1];
+ int i, len;
+ struct in_addr temp_addr;
+
+ (void) memcpy(adv_ext, p - sizeof (exthdr_t), sizeof (*adv_ext));
+ (void) sprintf(get_line(0, 0), "Sequence number = %d",
+ ntohs(adv_ext->sequence_num));
+ (void) sprintf(get_line(0, 0),
+ "Registration lifetime = %d seconds",
+ ntohs(adv_ext->reg_lifetime));
+ if (adv_ext->reg_bit) {
+ (void) sprintf(get_line(0, 0),
+ "1... .... = registration required "
+ "through FA");
+ } else {
+ (void) sprintf(get_line(0, 0),
+ "0... .... = registration not required "
+ "through FA");
+ }
+ if (adv_ext->busy_bit) {
+ (void) sprintf(get_line(0, 0), ".1.. .... = FA busy");
+ } else {
+ (void) sprintf(get_line(0, 0), ".0.. .... = FA not busy");
+ }
+ if (adv_ext->ha_bit) {
+ (void) sprintf(get_line(0, 0), "..1. .... = node is HA");
+ } else {
+ (void) sprintf(get_line(0, 0), "..0. .... = node not HA");
+ }
+ if (adv_ext->fa_bit) {
+ (void) sprintf(get_line(0, 0), "...1 .... = node is FA ");
+ } else {
+ (void) sprintf(get_line(0, 0), "...0 .... = node not FA ");
+ }
+ if (adv_ext->minencap_bit) {
+ (void) sprintf(get_line(0, 0), ".... 1... = minimal encapsulation "
+ "supported");
+ } else {
+ (void) sprintf(get_line(0, 0),
+ ".... 0... = no minimal encapsulation");
+ }
+ if (adv_ext->greencap_bit) {
+ (void) sprintf(get_line(0, 0),
+ ".... .1.. = GRE encapsulation supported");
+ } else {
+ (void) sprintf(get_line(0, 0),
+ ".... .0.. = no GRE encapsulation");
+ }
+ if (adv_ext->vanjacob_hdr_comp_bit) {
+ (void) sprintf(get_line(0, 0),
+ ".... ..1. = VJ header compression");
+ } else {
+ (void) sprintf(get_line(0, 0),
+ ".... ..0. = no VJ header compression");
+ }
+ if (adv_ext->reverse_tunnel_bit) {
+ (void) sprintf(get_line(0, 0),
+ ".... ...1 = reverse tunneling supported");
+ } else {
+ (void) sprintf(get_line(0, 0),
+ ".... ...0 = no reverse tunneling");
+ }
+ (void) sprintf(get_line(0, 0),
+ "Reserved Byte = 0x%x", adv_ext->reserved);
+
+ /* Parse out COA's */
+ p += sizeof (*adv_ext);
+ len = this_ext_len + sizeof (exthdr_t);
+ /* this_ext_len is unsigned, and here we need a signed number */
+ len -= sizeof (*adv_ext);
+
+ for (i = 0; len >= sizeof (temp_addr.s_addr); i++) {
+ memcpy(&(temp_addr.s_addr), p - sizeof (exthdr_t),
+ sizeof (temp_addr.s_addr));
+
+ (void) sprintf(get_line(0, 0),
+ "Care of address-%d = %s, %s", i,
+ inet_ntoa(temp_addr),
+ addrtoname(AF_INET, &temp_addr));
+
+ p += sizeof (temp_addr);
+ len -= sizeof (temp_addr);
+ }
+}
+
+/*ARGSUSED*/
+static void prefix_ext(uint8_t type, uint8_t this_ext_len, uchar_t *p) {
+ int i;
+
+ for (i = 0; i < this_ext_len; i++) {
+ (void) sprintf(get_line(0, 0),
+ "Prefix length of router address[%d] "
+ "= %d bits",
+ i, p[i]);
+ }
+}
+
+/*ARGSUSED*/
+static void unk_ext(uint8_t type, uint8_t this_ext_len, uchar_t *p) {
+ char auth_prn_str[BUFLEN];
+
+ /* Unknown extension; just dump the rest of the payload */
+ dumphex(p,
+ /* don't write past our string buffer ... */
+ (this_ext_len*3 > BUFLEN ? BUFLEN : this_ext_len),
+ auth_prn_str,
+ "Payload = %s");
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_mip.h b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_mip.h
new file mode 100644
index 0000000000..ca40f53df9
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_mip.h
@@ -0,0 +1,327 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _SNOOP_MIP_H
+#define _SNOOP_MIP_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ALIGN(ptr) (ptr)
+
+/*
+ * E X T E N S I O N S
+ */
+
+typedef struct {
+ uchar_t type;
+ uchar_t length;
+} exthdr_t;
+
+/* This header is used for Generalized MIP Authentication Extensions */
+typedef struct {
+ uint8_t type;
+ uint8_t subtype;
+ uint16_t length;
+} gen_exthdr_t;
+
+#define MN_HA_AUTH 32
+#define MN_FA_AUTH 33
+#define FA_HA_AUTH 34
+#define GEN_AUTH 36
+#define MN_HA_KEY 126
+#define MN_HA_TRAVERSE 129
+#define ENCAP_DELIV 130
+#define MN_NAI 131
+#define FA_CHALLENGE 132
+#define MN_FA_KEY 133
+
+/* Subtypes for Generalized MIP Authentication Extension (GEN_AUTH) */
+#define GEN_AUTH_MN_AAA 1
+
+#define KEY_ALG_NONE 0
+#define SA_MD5_MODE_PREF_SUF 2 /* ...in prefix+suffix */
+#define SA_HMAC_MD5 3
+
+/*
+ * R E G I S T R A T I O N P R O T O C O L
+ */
+
+#define REG_TYPE_REQ 1
+#define REG_TYPE_REP 3
+
+typedef struct ident_str {
+ uint32_t high_bits; /* generated by the HA */
+ uint32_t low_bits; /* generated by the MN */
+} ident_t;
+
+#ifdef __sparc
+#ifdef _BIT_FIELDS_HTOL
+typedef struct registration_request_str {
+ uchar_t type; /* must be REG_TYPE_REQ */
+ uchar_t
+ Simultaneous_registration : 1,
+ Broadcasts_desired : 1,
+ Decapsulation_done_locally : 1, /* ...by the popup MN */
+ Minimal_encap_desired : 1,
+ GRE_encap_desired : 1,
+ VJ_compression_desired : 1,
+ BiDirectional_Tunnel_desired : 1,
+ reserved : 1;
+ ushort_t lifetime; /* 0 = dereg; 0xffff = infinity */
+ in_addr_t home_addr; /* address of the MN */
+ in_addr_t home_agent_addr; /* address of a HA */
+ in_addr_t care_of_addr; /* address of decap endpoint */
+ ident_t identification; /* for replay protection */
+} regreq_t;
+#endif /* _BIT_FIELDS_HTOL */
+#endif /* __sparc */
+
+#ifdef __i386
+#ifdef _BIT_FIELDS_LTOH
+typedef struct registration_request_str {
+ uchar_t type; /* must be REG_TYPE_REQ */
+ uchar_t
+ reserved : 1,
+ BiDirectional_Tunnel_desired : 1,
+ VJ_compression_desired : 1,
+ GRE_encap_desired : 1,
+ Minimal_encap_desired : 1,
+ Decapsulation_done_locally : 1, /* ...by the popup MN */
+ Broadcasts_desired : 1,
+ Simultaneous_registration : 1;
+ ushort_t lifetime; /* 0 = dereg; 0xffff = infinity */
+ in_addr_t home_addr; /* address of the MN */
+ in_addr_t home_agent_addr; /* address of a HA */
+ in_addr_t care_of_addr; /* address of decap endpoint */
+ ident_t identification; /* for replay protection */
+} regreq_t;
+#endif /* _BIT_FIELDS_LTOH */
+#endif /* __i386 */
+
+/*
+ * Registration Reply sent by a home agent to a mobile node in
+ * response to a registration request.
+ */
+typedef struct registration_reply_str {
+ uchar_t type; /* must be REG_TYPE_REP */
+ uchar_t code; /* refer to draft document */
+ ushort_t lifetime; /* 0 = dereg; 0xffff = infinity */
+ in_addr_t home_addr; /* address of the mobile node */
+ in_addr_t home_agent_addr; /* address of the home agent */
+ ident_t identification; /* derived from request's field */
+} regrep_t;
+
+/* service ok */
+#define REPLY_CODE_ACK 0
+#define REPLY_CODE_ACK_NO_SIMULTANEOUS 1
+
+/* denied by FA */
+#define REPLY_CODE_FA_NACK_UNSPECIFIED 64
+#define REPLY_CODE_FA_NACK_PROHIBITED 65
+#define REPLY_CODE_FA_NACK_RESOURCES 66
+#define REPLY_CODE_FA_NACK_MN_AUTH 67
+#define REPLY_CODE_FA_NACK_HA_AUTH 68
+#define REPLY_CODE_FA_NACK_LIFETIME 69
+#define REPLY_CODE_FA_NACK_BAD_REQUEST 70
+#define REPLY_CODE_FA_NACK_BAD_REPLY 71
+#define REPLY_CODE_FA_NACK_ENCAP_UNAVAILABLE 72
+#define REPLY_CODE_FA_NACK_VJ_UNAVAILABLE 73
+#define REPLY_CODE_FA_NACK_BIDIR_TUNNEL_UNAVAILABLE 74
+#define REPLY_CODE_FA_NACK_BIDIR_TUNNEL_NO_TBIT 75
+#define REPLY_CODE_FA_NACK_BIDIR_TUNNEL_TOO_DISTANT 76
+#define REPLY_CODE_FA_NACK_ICMP_HA_NET_UNREACHABLE 80
+#define REPLY_CODE_FA_NACK_ICMP_HA_HOST_UNREACHABLE 81
+#define REPLY_CODE_FA_NACK_ICMP_HA_PORT_UNREACHABLE 82
+#define REPLY_CODE_FA_NACK_ICMP_HA_UNREACHABLE 88
+#define REPLY_CODE_FA_NACK_UNIQUE_HOMEADDR_REQD 96
+#define REPLY_CODE_FA_NACK_MISSING_NAI 97
+#define REPLY_CODE_FA_NACK_MISSING_HOME_AGENT 98
+#define REPLY_CODE_FA_NACK_MISSING_HOMEADDR 99
+#define REPLY_CODE_FA_NACK_UNKNOWN_CHALLENGE 104
+#define REPLY_CODE_FA_NACK_MISSING_CHALLENGE 105
+#define REPLY_CODE_FA_NACK_MISSING_MN_FA 106
+
+/* denied by HA */
+#define REPLY_CODE_HA_NACK_UNSPECIFIED 128
+#define REPLY_CODE_HA_NACK_PROHIBITED 129
+#define REPLY_CODE_HA_NACK_RESOURCES 130
+#define REPLY_CODE_HA_NACK_MN_AUTH 131
+#define REPLY_CODE_HA_NACK_FA_AUTH 132
+#define REPLY_CODE_HA_NACK_ID_MISMATCH 133
+#define REPLY_CODE_HA_NACK_BAD_REQUEST 134
+#define REPLY_CODE_HA_NACK_TOO_MANY_BINDINGS 135
+#define REPLY_CODE_HA_NACK_BAD_HA_ADDRESS 136
+#define REPLY_CODE_HA_NACK_BIDIR_TUNNEL_UNAVAILABLE 137
+#define REPLY_CODE_HA_NACK_BIDIR_TUNNEL_NO_TBIT 138
+#define REPLY_CODE_HA_NACK_BIDIR_ENCAP_UNAVAILABLE 139
+
+/*
+ * OTHER EXTENSIONS
+ */
+
+/*
+ * The second set consists of those extensions which may appear only
+ * in ICMP Router Discovery messages [4]. Currently, Mobile IP
+ * defines the following Types for Extensions appearing in ICMP
+ * Router Discovery messages:
+ *
+ * 0 One-byte PaddingOne-byte Padding (encoded with no Length nor
+ * Data field)
+ * 16 Mobility Agent Advertisement
+ * 19 Prefix-Lengths
+ */
+#define ICMP_ADV_MSG_PADDING_EXT 0
+#define ICMP_ADV_MSG_MOBILITY_AGT_EXT 16
+#define ICMP_ADV_MSG_PREFIX_LENGTH_EXT 19
+#define ICMP_ADV_MSG_FA_CHALLENGE 24
+#define ICMP_ADV_MSG_FA_NAI 25
+
+
+/*
+ * Mobility Agent Advertisement Extension
+ * The Mobility Agent Adv Extension follows the ICMP Router
+ * Advertisement fields.It is used to indicate that an ICMP Router
+ * Advertisement message is also an Agent Advertisement being sent
+ * by a mobility agent.
+ *
+ * Type 16
+ * Length (6 + 4*N), where N is the number of care-of addresses
+ * advertised.
+ *
+ * Sequence Number
+ * The count of Agent Advertisement messages sent since the
+ * agent was initialized (Section 2.3.2).
+ *
+ * Registration Lifetime
+ * The longest lifetime (measured in seconds) that this
+ * agent is willing to accept in any Registration Request.
+ * A value of 0xffff indicates infinity. This field has no
+ * relation to the "Lifetime" field within the ICMP Router
+ * Advertisement portion of the Agent Advertisement.
+ *
+ * R Registration required. Registration with this foreign
+ * agent (or another foreign agent on this link) is required
+ * rather than using a co-located care-of address.
+ *
+ * B Busy. The foreign agent will not accept registrations
+ * from additional mobile nodes.
+ *
+ * H Home agent. This agent offers service as a home agent
+ * on the link on which this Agent Advertisement message is
+ * sent.
+ *
+ * F Foreign agent. This agent offers service as a foreign
+ * agent on the link on which this Agent Advertisement
+ * message is sent.
+ *
+ * M Minimal encapsulation. This agent implements receiving
+ * tunneled datagrams that use minimal encapsulation [15].
+ *
+ * G GRE encapsulation. This agent implements receiving
+ * tunneled datagrams that use GRE encapsulation [8].
+ *
+ * V Van Jacobson header compression. This agent supports use
+ * of Van Jacobson header compression [10] over the link
+ * with any registered mobile node.
+ *
+ * reserved sent as zero; ignored on reception.
+ *
+ * Care-of Address(es)
+ * The advertised foreign agent care-of address(es) provided
+ * by this foreign agent. An Agent Advertisement MUST
+ * include at least one care-of address if the 'F' bit
+ * is set. The number of care-of addresses present is
+ * determined by the Length field in the Extension.
+ *
+ * A HA must always be prepared to serve the mobile nodes for
+ * which it is the home agent. A FA may at times be too busy
+ * to serve additional MNs; even so, it must continue to send
+ * Agent Advertisements, so that any mobile nodes already registered
+ * with it will know that they have not moved out of range of the
+ * foreign agent and that the has not failed. A foreign
+ * agent may indicate that it is "too busy" to allow new MNs to
+ * register with it, by setting the 'B' bit in its Agent Adv.
+ * An Agent Adv message MUST NOT have the 'B' bit set if the
+ * 'F' bit is not also set, and at least one of the 'F' bit and the
+ * 'H' bit MUST be set in any Agent Advertisement message sent.
+ *
+ * When a FA wishes to require registration even from those
+ * mobile nodes which have acquired a co-located care-of address, it
+ * sets the 'R' bit to one. Because this bit applies only to foreign
+ * agents, an agent MUST NOT set the 'R' bit to one unless the 'F'
+ * bit is also set to one.
+ */
+#ifdef __sparc
+#ifdef _BIT_FIELDS_HTOL
+typedef struct mobility_agt_adv_extension {
+ uchar_t type;
+ uchar_t length;
+ ushort_t sequence_num;
+ ushort_t reg_lifetime;
+ ushort_t reg_bit:1,
+ busy_bit:1,
+ ha_bit:1,
+ fa_bit:1,
+ minencap_bit:1,
+ greencap_bit:1,
+ vanjacob_hdr_comp_bit:1,
+ reverse_tunnel_bit:1,
+ reserved:8;
+} mobagtadvext_t;
+
+#endif /* _BIT_FIELDS_HTOL */
+#endif /* __sparc */
+
+#ifdef __i386
+#ifdef _BIT_FIELDS_LTOH
+typedef struct mobility_agt_adv_extension {
+ uchar_t type;
+ uchar_t length;
+ ushort_t sequence_num;
+ ushort_t reg_lifetime;
+ uchar_t
+ reverse_tunnel_bit:1,
+ vanjacob_hdr_comp_bit:1,
+ greencap_bit:1,
+ minencap_bit:1,
+ fa_bit:1,
+ ha_bit:1,
+ busy_bit:1,
+ reg_bit:1;
+ uchar_t reserved;
+} mobagtadvext_t;
+#endif /* _BIT_FIELDS_LTOH */
+#endif /* __i386 */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SNOOP_MIP_H */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_mount.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_mount.c
new file mode 100644
index 0000000000..ce2df1f293
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_mount.c
@@ -0,0 +1,563 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1991, 1999-2000 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ident "%Z%%M% %I% %E% SMI" /* SunOS */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <setjmp.h>
+#include <sys/tiuser.h>
+
+#include <rpc/types.h>
+#include <rpc/xdr.h>
+#include <rpc/auth.h>
+#include <rpc/clnt.h>
+#include <rpc/rpc_msg.h>
+#include <nfs/nfs.h>
+#include <rpcsvc/mount.h>
+#include <string.h>
+#include "snoop.h"
+#include "snoop_nfs.h"
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+extern char *dlc_header;
+extern jmp_buf xdr_err;
+
+static void mountcall(int, int);
+static void mountreply(int, int);
+
+static void sum_mountstat(char *);
+static void sum_mountstat3(char *);
+static char *sum_mountfh(void);
+static char *sum_mountfh3(void);
+static char *sum_exports(void);
+static char *sum_mounts(void);
+
+static int detail_mountstat(void);
+static void detail_mountstat3(void);
+static void detail_mountfh(void);
+static void detail_mountfh3(void);
+static void detail_exports(void);
+static void detail_mounts(void);
+
+static char *statusmsg3(ulong_t);
+
+static char *procnames_short[] = {
+ "Null", /* 0 */
+ "Mount", /* 1 */
+ "Get mount list", /* 2 */
+ "Unmount", /* 3 */
+ "Unmountall", /* 4 */
+ "Get export list", /* 5 */
+ "Get export list", /* 6 */
+ "PATHCONF", /* 7 */
+};
+
+static char *procnames_long[] = {
+ "Null procedure", /* 0 */
+ "Add mount entry", /* 1 */
+ "Return mount entries", /* 2 */
+ "Remove mount entry", /* 3 */
+ "Remove all mount entries", /* 4 */
+ "Return export list", /* 5 */
+ "Return export list", /* 6 */
+ "Get POSIX Pathconf info", /* 7 */
+};
+
+#define MAXPROC 7
+
+void
+interpret_mount(flags, type, xid, vers, proc, data, len)
+ int flags, type, xid, vers, proc;
+ char *data;
+ int len;
+{
+ char *line;
+ char buff[MNTPATHLEN + 1];
+
+ if (proc < 0 || proc > MAXPROC)
+ return;
+
+ if (flags & F_SUM) {
+ if (setjmp(xdr_err)) {
+ return;
+ }
+
+ line = get_sum_line();
+
+ if (type == CALL) {
+ (void) sprintf(line, "MOUNT%d C %s",
+ vers, procnames_short[proc]);
+ line += strlen(line);
+ switch (proc) {
+ case MOUNTPROC_MNT:
+ case MOUNTPROC_UMNT:
+ (void) sprintf(line, " %s",
+ getxdr_string(buff, MNTPATHLEN));
+ break;
+ case MOUNTPROC_DUMP:
+ case MOUNTPROC_UMNTALL:
+ case MOUNTPROC_EXPORT:
+ case MOUNTPROC_EXPORTALL:
+#ifdef MOUNTPROC_PATHCONF
+ case MOUNTPROC_PATHCONF:
+ if (vers != 3)
+ (void) sprintf(line, " %s",
+ getxdr_string(buff,
+ MNTPATHLEN));
+#endif
+ break;
+ default:
+ break;
+ }
+
+ check_retransmit(line, xid);
+ } else {
+ (void) sprintf(line, "MOUNT%d R %s ",
+ vers, procnames_short[proc]);
+ line += strlen(line);
+ switch (proc) {
+ case MOUNTPROC_MNT:
+ if (vers == 3)
+ sum_mountstat3(line);
+ else
+ sum_mountstat(line);
+ break;
+ case MOUNTPROC_DUMP:
+ (void) sprintf(line, sum_mounts());
+ break;
+ case MOUNTPROC_UMNT:
+ case MOUNTPROC_UMNTALL:
+ (void) sprintf(line, "reply");
+ break;
+ case MOUNTPROC_EXPORTALL:
+ /*
+ * EXPORTALL is the same as EXPORT in v1
+ * and v2, and it doesn't exist in v3.
+ */
+ if (vers == 3)
+ break;
+ /*FALLTHROUGH*/
+ case MOUNTPROC_EXPORT:
+ (void) sprintf(line, sum_exports());
+ break;
+#ifdef MOUNTPROC_PATHCONF
+ case MOUNTPROC_PATHCONF:
+ if (vers != 2)
+ break;
+#ifdef notyet
+ (void) sprintf(line, sum_ppathcnf());
+#endif
+ break;
+#endif
+ default:
+ break;
+ }
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("MOUNT:", "NFS MOUNT", len);
+ show_space();
+ if (setjmp(xdr_err)) {
+ return;
+ }
+ (void) sprintf(get_line(0, 0),
+ "Proc = %d (%s)",
+ proc, procnames_long[proc]);
+ if (type == CALL)
+ mountcall(proc, vers);
+ else
+ mountreply(proc, vers);
+ show_trailer();
+ }
+}
+
+/*
+ * Interpret call packets in detail
+ */
+
+static void
+mountcall(proc, vers)
+ int proc, vers;
+{
+
+ switch (proc) {
+ case MOUNTPROC_MNT:
+ case MOUNTPROC_UMNT:
+ (void) showxdr_string(MNTPATHLEN, "Directory = %s");
+ break;
+ case MOUNTPROC_DUMP:
+ break;
+ case MOUNTPROC_UMNTALL:
+ break;
+ case MOUNTPROC_EXPORTALL:
+ if (vers == 3)
+ break;
+ break;
+ case MOUNTPROC_EXPORT:
+ break;
+#ifdef MOUNTPROC_PATHCONF
+ case MOUNTPROC_PATHCONF:
+ if (vers != 2)
+ break;
+ (void) showxdr_string(MNTPATHLEN, "File = %s");
+#endif
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * Interpret reply packets in detail
+ */
+
+static void
+mountreply(proc, vers)
+ int proc, vers;
+{
+
+ switch (proc) {
+ case MOUNTPROC_MNT:
+ if (vers == 3) {
+ detail_mountstat3();
+ } else {
+ if (detail_mountstat() == 0) {
+ detail_mountfh();
+ }
+ }
+ break;
+ case MOUNTPROC_DUMP:
+ detail_mounts();
+ break;
+ case MOUNTPROC_UMNT:
+ case MOUNTPROC_UMNTALL:
+ (void) detail_mountstat();
+ break;
+ case MOUNTPROC_EXPORTALL:
+ if (vers == 3)
+ break;
+ /*FALLTHROUGH*/
+ case MOUNTPROC_EXPORT:
+ detail_exports();
+ break;
+#ifdef MOUNTPROC_PATHCONF
+ case MOUNTPROC_PATHCONF:
+#ifdef notyet
+ (void) detail_ppathcnf();
+#endif
+ break;
+#endif
+ default:
+ break;
+ }
+}
+
+static void
+sum_mountstat(line)
+ char *line;
+{
+ ulong_t status;
+ char *str;
+
+ status = getxdr_u_long();
+ if (status == 0)
+ str = "OK";
+ else if ((str = strerror(status)) == (char *)NULL)
+ str = "";
+ (void) strcpy(line, str);
+ if (status == 0) {
+ (void) strcat(line, sum_mountfh());
+ }
+}
+
+static int
+detail_mountstat()
+{
+ ulong_t status;
+ char *str;
+
+ status = getxdr_u_long();
+ if (status == 0)
+ str = "OK";
+ else if ((str = strerror(status)) == (char *)NULL)
+ str = "";
+
+ (void) sprintf(get_line(0, 0), "Status = %d (%s)", status, str);
+
+ return ((int)status);
+}
+
+char *
+sum_mountfh()
+{
+ int fh;
+ static char buff[8];
+
+ fh = sum_filehandle(NFS_FHSIZE);
+ (void) sprintf(buff, " FH=%04X", fh & 0xFFFF);
+ return (buff);
+}
+
+static void
+detail_mountfh()
+{
+ int pos;
+ int fh;
+
+ pos = getxdr_pos();
+ fh = sum_filehandle(NFS_FHSIZE);
+ setxdr_pos(pos);
+ (void) sprintf(get_line(0, 0), "File handle = [%04X]", fh & 0xFFFF);
+ (void) showxdr_hex(NFS_FHSIZE, " %s");
+}
+
+static char *
+print_auth()
+{
+ int i, auth, flavors;
+ char *p;
+ static char buff[64];
+
+ buff[0] = '\0';
+ flavors = getxdr_long();
+ for (i = 0; i < flavors; i++) {
+ if (i > 0)
+ (void) strlcat(buff, ",", sizeof (buff));
+ switch (auth = getxdr_u_long()) {
+ case AUTH_NONE:
+ (void) strlcat(buff, "none", sizeof (buff));
+ break;
+ case AUTH_UNIX:
+ (void) strlcat(buff, "unix", sizeof (buff));
+ break;
+ case AUTH_SHORT:
+ (void) strlcat(buff, "short", sizeof (buff));
+ break;
+ case AUTH_DES:
+ (void) strlcat(buff, "des", sizeof (buff));
+ break;
+ default:
+ p = buff + strlen(buff);
+ if (p < &buff[sizeof (buff)])
+ (void) snprintf(p, sizeof (buff) - strlen(buff),
+ "%d", auth);
+ break;
+ }
+ }
+ return (buff);
+}
+
+static void
+sum_mountstat3(line)
+ char *line;
+{
+ ulong_t status;
+
+ status = getxdr_u_long();
+ (void) strcpy(line, statusmsg3(status));
+ if (status == 0) {
+ (void) strcat(line, sum_mountfh3());
+ (void) strcat(line, " Auth=");
+ (void) strcat(line, print_auth());
+ }
+}
+
+static void
+detail_mountstat3()
+{
+ ulong_t status;
+
+ status = getxdr_u_long();
+ (void) sprintf(get_line(0, 0), "Status = %d (%s)", status,
+ statusmsg3(status));
+ if (status == 0) {
+ detail_mountfh3();
+ (void) sprintf(get_line(0, 0), "Authentication flavor = %s",
+ print_auth());
+ }
+}
+
+char *
+sum_mountfh3()
+{
+ int len;
+ int fh;
+ static char buff[8];
+
+ len = getxdr_long();
+ fh = sum_filehandle(len);
+ (void) sprintf(buff, " FH=%04X", fh & 0xFFFF);
+ return (buff);
+}
+
+static void
+detail_mountfh3()
+{
+ int pos;
+ int i, l, len;
+ int fh;
+
+ len = getxdr_long();
+ pos = getxdr_pos();
+ fh = sum_filehandle(len);
+ setxdr_pos(pos);
+ (void) sprintf(get_line(0, 0), "File handle = [%04X]", fh & 0xFFFF);
+ i = 0;
+ while (i < len) {
+ l = MIN(len - i, 32);
+ (void) showxdr_hex(l, " %s");
+ i += l;
+ }
+}
+
+static char *
+sum_exports()
+{
+ static char buff[MNTPATHLEN + 1];
+ int entries = 0;
+
+ if (setjmp(xdr_err)) {
+ (void) sprintf(buff, "%d+ entries", entries);
+ return (buff);
+ }
+
+ while (getxdr_long()) {
+ (void) getxdr_string(buff, MNTPATHLEN);
+ while (getxdr_long()) {
+ (void) getxdr_string(buff, MNTNAMLEN);
+ }
+ entries++;
+ }
+
+ (void) sprintf(buff, "%d entries", entries);
+ return (buff);
+}
+
+static void
+detail_exports()
+{
+ int entries = 0;
+ char *dirpath, *grpname;
+ char buff[MNTPATHLEN + 1];
+
+ if (setjmp(xdr_err)) {
+ (void) sprintf(get_line(0, 0),
+ " %d+ entries. (Frame is incomplete)",
+ entries);
+ return;
+ }
+
+ while (getxdr_long()) {
+ dirpath = (char *)getxdr_string(buff, MNTPATHLEN);
+ (void) sprintf(get_line(0, 0), "Directory = %s", dirpath);
+ entries++;
+ while (getxdr_long()) {
+ grpname = (char *)getxdr_string(buff, MNTNAMLEN);
+ (void) sprintf(get_line(0, 0), " Group = %s", grpname);
+ }
+ }
+}
+
+static char *
+sum_mounts()
+{
+ int entries = 0;
+ static char buff[MNTPATHLEN + 1];
+
+ if (setjmp(xdr_err)) {
+ (void) sprintf(buff, "%d+ entries", entries);
+ return (buff);
+ }
+
+ while (getxdr_long()) {
+ (void) getxdr_string(buff, MNTNAMLEN);
+ (void) getxdr_string(buff, MNTPATHLEN);
+ entries++;
+ }
+
+ (void) sprintf(buff, "%d entries", entries);
+ return (buff);
+}
+
+static void
+detail_mounts()
+{
+ int entries = 0;
+ char *hostname, *directory;
+ char buff1[MNTNAMLEN + 1], buff2[MNTPATHLEN + 1];
+
+ if (setjmp(xdr_err)) {
+ (void) sprintf(get_line(0, 0),
+ " %d+ entries. (Frame is incomplete)",
+ entries);
+ return;
+ }
+
+ (void) sprintf(get_line(0, 0), "Mount list");
+
+ while (getxdr_long()) {
+ hostname = (char *)getxdr_string(buff1, MNTNAMLEN);
+ directory = (char *)getxdr_string(buff2, MNTPATHLEN);
+ (void) sprintf(get_line(0, 0), " %s:%s", hostname, directory);
+ entries++;
+ }
+}
+
+char *
+statusmsg3(status)
+ ulong_t status;
+{
+
+ switch (status) {
+ case MNT_OK:
+ return ("OK");
+ case MNT3ERR_PERM:
+ return ("Not owner");
+ case MNT3ERR_NOENT:
+ return ("No such file or directory");
+ case MNT3ERR_IO:
+ return ("I/O error");
+ case MNT3ERR_ACCES:
+ return ("Permission denied");
+ case MNT3ERR_NOTDIR:
+ return ("Not a directory");
+ case MNT3ERR_INVAL:
+ return ("Invalid argument");
+ case MNT3ERR_NAMETOOLONG:
+ return ("File name too long");
+ case MNT3ERR_NOTSUPP:
+ return ("Operation not supported");
+ case MNT3ERR_SERVERFAULT:
+ return ("Server error");
+ default:
+ return ("(unknown error)");
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nbp.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nbp.c
new file mode 100644
index 0000000000..fbdaa942ab
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nbp.c
@@ -0,0 +1,145 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#include <at.h>
+#include <snoop.h>
+
+static void show_nbp_tuples(uint8_t *, int, uint8_t *);
+
+static char *nbp_short[] = {
+ "0", /* 0 */
+ "BRRQ ", /* 1 */
+ "LKUP C ", /* 2 */
+ "LKUP R ", /* 3 */
+ "FWD ", /* 4 */
+ "5 ",
+ "6 ",
+ "7 ",
+ "8 ",
+ "9 ",
+ "10 ",
+ "11 ",
+ "RGSTR ", /* 12 */
+ "UNRGSTR", /* 13 */
+ "OK ", /* 14 */
+ "ERROR ", /* 15 */
+};
+
+void
+interpret_nbp(int flags, struct nbp_hdr *nbp, int len)
+{
+ uint8_t *data;
+ int nbp_cnt = nbp->nbp_fun_cnt & 0xf; /* lower four bits */
+ int nbp_op = (nbp->nbp_fun_cnt >> 4) & 0xf; /* upper four bits */
+
+ data = (uint8_t *)(nbp + 1);
+
+ if (flags & F_SUM) {
+ if (len < sizeof (struct nbp_hdr)) {
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "NBP (short packet)");
+ return;
+ }
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "NBP F=%s CNT=%d ID=%d", nbp_short[nbp_op],
+ nbp_cnt, nbp->nbp_id);
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("NBP: ", "NBP Header", len);
+ show_space();
+
+ if (len < sizeof (struct nbp_hdr)) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "NBP (short packet)");
+ return;
+ }
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Length = %d", len);
+
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Func = %d (%s)", nbp_op, nbp_short[nbp_op]);
+
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Tuple count = %d", nbp_cnt);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Id = %d", nbp->nbp_id);
+ show_nbp_tuples(data, nbp_cnt, ((uint8_t *)nbp) + len);
+ }
+}
+
+static void
+show_nbp_tuples(uint8_t *p, int tuples, uint8_t *tail)
+{
+ uint16_t net;
+ uint8_t node;
+ uint8_t sock;
+ uint8_t enumer;
+ char obj[100];
+ char *op;
+ char *otail = &obj[sizeof (obj)];
+
+ while (tuples--) {
+ op = obj;
+ if ((p + 5) > tail)
+ goto out;
+ net = get_short(p);
+ p += 2;
+ node = *p++;
+ sock = *p++;
+ enumer = *p++;
+
+ if (p > tail || &p[1]+p[0] > tail)
+ goto out;
+ op += snprintf(op, otail-op, "%.*s", p[0], &p[1]);
+
+ p = &p[1]+p[0];
+ if (p > tail || &p[1]+p[0] > tail)
+ goto out;
+ op += snprintf(op, otail-op, ":%.*s", p[0], &p[1]);
+
+ p = &p[1]+p[0];
+ if (p > tail || &p[1]+p[0] > tail)
+ goto out;
+ (void) snprintf(op, otail-op, "@%.*s", p[0], &p[1]);
+ p = &p[1]+p[0];
+
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Name = \"%s\"", obj);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Net = %d, node = %d, sock = %d, enum = %d",
+ net, node, sock, enumer);
+ }
+ return;
+out:
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "NBP (short tuple)");
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_netbios.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_netbios.c
new file mode 100644
index 0000000000..23ded99588
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_netbios.c
@@ -0,0 +1,556 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * References used throughout this code:
+ *
+ * [RFC1001] : PROTOCOL STANDARD FOR A NetBIOS SERVICE
+ * ON A TCP/UDP TRANSPORT:
+ * CONCEPTS AND METHODS
+ * NetBIOS Working Group, March 1987
+ *
+ * [RFC1002] : PROTOCOL STANDARD FOR A NetBIOS SERVICE
+ * ON A TCP/UDP TRANSPORT:
+ * DETAILED SPECIFICATIONS
+ * NetBIOS Working Group, March 1987
+ */
+
+#include <fcntl.h>
+#include "snoop.h"
+#include <stdio.h>
+#include <ctype.h>
+#include "snoop.h"
+
+extern char *dlc_header;
+char *show_type();
+
+/* See snoop_smb.c */
+extern void interpret_smb(int flags, uchar_t *data, int len);
+
+/*
+ * NBT Session Packet Header
+ * [RFC 1002, Sec. 4.3.1]
+ */
+struct nbt_ss {
+ uchar_t type;
+ uchar_t flags;
+ ushort_t length;
+};
+
+/*
+ * NBT Session Request Packet trailer
+ * [RFC 1002, Sec. 4.3.2]
+ */
+struct callnames {
+ uchar_t space; /* padding */
+ uchar_t calledname[32];
+ uchar_t nullchar; /* padding */
+ uchar_t space2; /* padding */
+ uchar_t callingname[32];
+ uchar_t nullchar2; /* padding */
+};
+
+
+static void interpret_netbios_names(int flags, uchar_t *data, int len,
+ char *xtra);
+static void netbiosname2ascii(char *asciiname, uchar_t *netbiosname);
+
+/*
+ * Helpers to read network-order values,
+ * with NO alignment assumed.
+ */
+static ushort_t
+getshort(uchar_t *p) {
+ return (p[1] + (p[0]<<8));
+}
+static uint_t
+getlong(uchar_t *p)
+{
+ return (p[3] + (p[2]<<8) + (p[1]<<16) + (p[0]<<24));
+}
+
+/*
+ * NM_FLAGS fields in the NetBIOS Name Service Packet header.
+ * [RFC 1002, Sec. 4.2.1.1]
+ */
+static void
+print_flag_details(int headerflags)
+{
+ if (headerflags & 1<<4)
+ sprintf(get_line(0, 0), " - Broadcast");
+ if (headerflags & 1<<7)
+ sprintf(get_line(0, 0), " - Recursion Available");
+ if (headerflags & 1<<8)
+ sprintf(get_line(0, 0), " - Recursion Desired");
+ if (headerflags & 1<<9)
+ sprintf(get_line(0, 0), " - Truncation Flag");
+ if (headerflags & 1<<10)
+ sprintf(get_line(0, 0), " - Authoritative Answer");
+}
+
+/*
+ * Possible errors in NetBIOS name service packets.
+ * [RFC 1002, Sec. 4.2.6, 4.2.11, 4.2.14]
+ */
+static void
+getrcodeerr(int headerflags, char *errortype)
+{
+ int error = (headerflags & 0xf);
+
+ switch (error) {
+ case 0:
+ sprintf(errortype, "Success");
+ break;
+ case 1:
+ sprintf(errortype, "Format Error");
+ break;
+ case 2:
+ sprintf(errortype, "Server Failure");
+ break;
+ case 3:
+ sprintf(errortype, "Name Error");
+ break;
+ case 4:
+ sprintf(errortype, "Unsupported Request Error");
+ break;
+ case 5:
+ sprintf(errortype, "Refused Error");
+ break;
+ case 6:
+ sprintf(errortype, "Active Error");
+ break;
+ case 7:
+ sprintf(errortype, "Name in Conflict Error");
+ break;
+ default:
+ sprintf(errortype, "Unknown Error");
+ break;
+ }
+}
+
+/*
+ * OPCODE fields in the NetBIOS Name Service Packet header.
+ * [RFC 1002, Sec. 4.2.1.1]
+ */
+static void
+print_ns_type(int flags, int headerflags, char *xtra)
+{
+ int opcode = (headerflags & 0x7800)>>11;
+ int response = (headerflags & 1<<15);
+ char *resptype = response ? "Response" : "Request";
+ char *optype;
+
+ switch (opcode) {
+ case 0:
+ optype = "Query";
+ break;
+ case 5:
+ optype = "Registration";
+ break;
+ case 6:
+ optype = "Release";
+ break;
+ case 7:
+ optype = "WACK";
+ break;
+ case 8:
+ optype = "Refresh";
+ break;
+ default:
+ optype = "Unknown";
+ break;
+ }
+
+ if (flags & F_DTAIL)
+ sprintf(get_line(0, 0), "Type = %s %s", optype, resptype);
+ else
+ sprintf(xtra, "%s %s", optype, resptype);
+}
+
+
+/*
+ * Interpret Datagram Packets
+ * [RFC 1002, Sec. 4.4]
+ */
+void
+interpret_netbios_datagram(int flags, uchar_t *data, int len)
+{
+ char name[24];
+ int packettype = data[0];
+ int packetlen;
+ data++;
+
+ if (packettype < 0x10 || packettype > 0x11)
+ return;
+
+ if (flags & F_SUM) {
+ data += 14;
+ netbiosname2ascii(name, data);
+ sprintf(get_sum_line(),
+ "NBT Datagram Service Type=%d Source=%s",
+ packettype, name);
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("NBT: ", "Netbios Datagram Service Header", len);
+ show_space();
+ sprintf(get_line(0, 0), "Datagram Packet Type = 0x%.2x",
+ packettype);
+ sprintf(get_line(0, 0), "Datagram Flags = 0x%.2x",
+ data[0]);
+ data++;
+ sprintf(get_line(0, 0), "Datagram ID = 0x%.4x",
+ getshort(data));
+ data += 2;
+ sprintf(get_line(0, 0), "Source IP = %d.%d.%d.%d",
+ data[0], data[1], data[2], data[3]);
+ data += 4;
+ sprintf(get_line(0, 0), "Source Port = %d",
+ getshort(data));
+ data += 2;
+ packetlen = getshort(data);
+ sprintf(get_line(0, 0), "Datagram Length = 0x%.4x",
+ packetlen);
+ data += 2;
+ sprintf(get_line(0, 0), "Packet Offset = 0x%.4x",
+ getshort(data));
+ data += 3;
+ netbiosname2ascii(name, data);
+ sprintf(get_line(0, 0), "Source Name = %s", name);
+ data += 34;
+ netbiosname2ascii(name, data);
+ sprintf(get_line(0, 0), "Destination Name = %s", name);
+ sprintf(get_line(0, 0), "Number of data bytes remaining = %d",
+ packetlen - 68);
+ show_trailer();
+ }
+}
+
+/*
+ * Interpret NetBIOS Name Service packets.
+ * [RFC 1002, Sec. 4.2]
+ */
+void
+interpret_netbios_ns(int flags, uchar_t *data, int len)
+{
+ int headerflags, qcount, acount, nscount, arcount;
+ int transid;
+ char name[24];
+ char extra[256];
+ char errortype[50];
+ int rdatalen;
+ int rrflags;
+ int nameptr;
+ int nodecode;
+ char *nodetype;
+ uchar_t *data0 = data;
+
+ transid = getshort(data); data += 2;
+ headerflags = getshort(data); data += 2;
+ qcount = getshort(data); data += 2;
+ acount = getshort(data); data += 2;
+ nscount = getshort(data); data += 2;
+ arcount = getshort(data); data += 2;
+ getrcodeerr(headerflags, errortype);
+
+ if (flags & F_SUM) {
+ print_ns_type(flags, headerflags, extra);
+ data++;
+ netbiosname2ascii(name, data);
+ sprintf(get_sum_line(), "NBT NS %s for %s, %s",
+ extra, name, errortype);
+
+ }
+
+
+ if (flags & F_DTAIL) {
+ show_header("NBT: ", "Netbios Name Service Header", len);
+ show_space();
+ print_ns_type(flags, headerflags, 0);
+ sprintf(get_line(0, 0), "Status = %s", errortype);
+ sprintf(get_line(0, 0), "Transaction ID = 0x%.4x", transid);
+ sprintf(get_line(0, 0), "Flags Summary = 0x%.4x",
+ headerflags);
+ print_flag_details(headerflags);
+ sprintf(get_line(0, 0), "Question count = %d", qcount);
+ sprintf(get_line(0, 0), "Answer Count = %d", acount);
+ sprintf(get_line(0, 0), "Name Service Count = %d", nscount);
+ sprintf(get_line(0, 0),
+ "Additional Record Count = %d", arcount);
+
+ /*
+ * Question Section Packet Description from
+ * [RFC 1002, Sec. 4.2.1.2]
+ */
+
+ if (qcount) {
+ data++;
+ netbiosname2ascii(name, data);
+ sprintf(get_line(0, 0), "Question Name = %s", name);
+ data += 33;
+ sprintf(get_line(0, 0), "Question Type = 0x%.4x",
+ getshort(data));
+ data += 2;
+ sprintf(get_line(0, 0), "Question Class = 0x%.4x",
+ getshort(data));
+ data += 2;
+ }
+
+ /*
+ * Resrouce Record Packet Description from
+ * [RFC 1002, Sec. 4.2.1.3]
+ */
+
+ if ((acount || nscount || arcount) ||
+ (qcount+acount+nscount+arcount == 0)) {
+ /* Second level encoding from RFC883 (p.31, 32) */
+ if (data[0] & 0xc0) {
+ nameptr = getshort(data)&0x3fff;
+ netbiosname2ascii(name, (data0+nameptr+1));
+ sprintf(get_line(0, 0),
+ "Resource Record Name = %s", name);
+ data += 2;
+ } else {
+ data++;
+ netbiosname2ascii(name, data);
+ sprintf(get_line(0, 0),
+ "Resource Record Name = %s", name);
+ data += 33;
+ }
+ sprintf(get_line(0, 0),
+ "Resource Record Type = 0x%.4x",
+ getshort(data));
+ data += 2;
+ sprintf(get_line(0, 0),
+ "Resource Record Class = 0x%.4x",
+ getshort(data));
+ data += 2;
+ sprintf(get_line(0, 0),
+ "Time to Live (Milliseconds) = %d",
+ getlong(data));
+ data += 4;
+ rdatalen = getshort(data);
+ sprintf(get_line(0, 0), "RDATA Length = 0x%.4x",
+ rdatalen);
+ data += 2;
+ /* 15.4.2.1.3 */
+ if (rdatalen == 6) {
+ rrflags = getshort(data);
+ data += 2;
+ sprintf(get_line(0, 0),
+ "Resource Record Flags = 0x%.4x",
+ rrflags);
+ nodecode = (rrflags>>13)& 0x11;
+ if (nodecode == 0) nodetype = "B";
+ if (nodecode == 1) nodetype = "P";
+ if (nodecode == 2) nodetype = "M";
+ sprintf(get_line(0, 0), " - %s, %s node",
+ (rrflags & 1<<15) ?
+ "Group NetBIOS Name":
+ "Unique NetBIOS Name", nodetype);
+ sprintf(get_line(0, 0),
+ "Owner IP Address = %d.%d.%d.%d",
+ data[0], data[1], data[2], data[3]);
+ }
+ }
+ show_trailer();
+
+ }
+}
+
+/*
+ * Interpret NetBIOS session packets.
+ * [RFC 1002, Sec. 4.3]
+ */
+void
+interpret_netbios_ses(int flags, uchar_t *data, int len)
+{
+ struct nbt_ss *ss;
+ uchar_t *trailer;
+ int length = len - 4; /* NBT packet length without header */
+ char *type;
+ char extrainfo[300];
+
+ if (len < sizeof (struct nbt_ss))
+ return;
+
+ /*
+ * Packets that are fragments of a large NetBIOS session
+ * message will have no NetBIOS header. (Only the first
+ * TCP segment will have a NetBIOS header.) It turns out
+ * that very often, such fragments start with SMB data, so
+ * we should try to recognize and decode them.
+ */
+ if (data[0] == 0xff &&
+ data[1] == 'S' &&
+ data[2] == 'M' &&
+ data[3] == 'B') {
+ interpret_smb(flags, data, len);
+ return;
+ }
+
+ /* LINTED PTRALIGN */
+ ss = (struct nbt_ss *)data;
+ trailer = data + sizeof (*ss);
+ extrainfo[0] = '\0';
+
+ if (flags & F_SUM) {
+ switch (ss->type) {
+ case 0x00:
+ type = "SESSION MESSAGE";
+ break;
+ case 0x81:
+ type = "SESSION REQUEST";
+ interpret_netbios_names(flags, trailer,
+ length, extrainfo);
+ break;
+ case 0x82:
+ type = "POSITIVE SESSION RESPONSE";
+ break;
+ case 0x83:
+ type = "NEGATIVE SESSION RESPONSE";
+ break;
+ case 0x84:
+ type = "RETARGET SESSION RESPONSE";
+ break;
+ case 0x85:
+ type = "SESSION KEEP ALIVE";
+ break;
+ default:
+ type = "Unknown";
+ break;
+ }
+ (void) sprintf(get_sum_line(),
+ "NBT Type=%s %sLength=%d", type, extrainfo, length);
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("NBT: ", "NBT Header", len);
+ show_space();
+
+ switch (ss->type) {
+ case 0x00:
+ (void) sprintf(get_line(0, 0),
+ "Type = SESSION MESSAGE");
+ break;
+ case 0x81:
+ (void) sprintf(get_line(0, 0),
+ "Type = SESSION REQUEST");
+ interpret_netbios_names(flags, trailer, length, 0);
+ break;
+ case 0x82:
+ (void) sprintf(get_line(0, 0),
+ "Type = POSITIVE SESSION RESPONSE");
+ break;
+ case 0x83:
+ (void) sprintf(get_line(0, 0),
+ "Type = NEGATIVE SESSION RESPONSE");
+ break;
+ case 0x84:
+ (void) sprintf(get_line(0, 0),
+ "Type = RETARGET SESSION RESPONSE");
+ break;
+ case 0x85:
+ (void) sprintf(get_line(0, 0),
+ "Type = SESSION KEEP ALIVE");
+ break;
+ default:
+ (void) sprintf(get_line(0, 0),
+ "Type = Unknown");
+ break;
+ }
+
+ (void) sprintf(get_line(0, 0), "Length = %d bytes", length);
+ show_trailer();
+ }
+
+ /*
+ * SMB packets have { 0xff, 'S', 'M', 'B' }
+ * in the first four bytes. If we find that,
+ * let snoop_smb.c have a look at it.
+ */
+ if (ss->type == 0x00 &&
+ length > 0 &&
+ trailer[0] == 0xff &&
+ trailer[1] == 'S' &&
+ trailer[2] == 'M' &&
+ trailer[3] == 'B')
+ interpret_smb(flags, trailer, len);
+}
+
+/*
+ * NetBIOS name encoding (First Level Encoding)
+ * [RFC 1001, Sec. 4.1]
+ */
+static void
+netbiosname2ascii(char *aname, uchar_t *nbname)
+{
+ int c, i, j;
+
+ i = j = 0;
+ for (;;) {
+ c = nbname[i++] - 'A';
+ c = (c << 4) +
+ nbname[i++] - 'A';
+ /* 16th char is the "type" */
+ if (i >= 32)
+ break;
+ if (iscntrl(c))
+ c = '.';
+ if (c != ' ')
+ aname[j++] = c;
+ }
+ sprintf(&aname[j], "[%x]", c);
+}
+
+/*
+ * Interpret the names in a Session Request packet.
+ * [RFC 1002, Sec. 4.3.2]
+ */
+static void
+interpret_netbios_names(int flags, uchar_t *data, int len, char *xtra)
+{
+ char calledname[24];
+ char callingname[24];
+ struct callnames *names = (struct callnames *)data;
+
+ if (len < sizeof (*names))
+ return;
+
+ netbiosname2ascii(calledname, names->calledname);
+ netbiosname2ascii(callingname, names->callingname);
+
+ if (flags & F_SUM) {
+ sprintf(xtra, "Dest=%s Source=%s ", calledname, callingname);
+ }
+
+ if (flags & F_DTAIL) {
+ sprintf(get_line(0, 0), "Destination = %s", calledname);
+ sprintf(get_line(0, 0), "Source = %s", callingname);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nfs.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nfs.c
new file mode 100644
index 0000000000..6f52c358f4
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nfs.c
@@ -0,0 +1,691 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1991-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/tiuser.h>
+#include <setjmp.h>
+
+#include <rpc/types.h>
+#include <rpc/xdr.h>
+#include <rpc/auth.h>
+#include <rpc/clnt.h>
+#include <rpc/rpc_msg.h>
+#include <string.h>
+#include "snoop.h"
+#include "snoop_nfs.h"
+
+#include <sys/stat.h>
+#include <rpcsvc/nfs_prot.h>
+
+static char *perms(int);
+static char *filetype(int);
+static char *sum_readdirres(void);
+static void detail_readdirres(void);
+static void detail_diroparg(void);
+static void nfscall2(int);
+static void nfsreply2(int);
+static void detail_mode(int);
+static void detail_sattr(void);
+static void interpret_nfs2(int, int, int, int, int, char *, int);
+
+extern jmp_buf xdr_err;
+
+static char *procnames_short[] = {
+ "NULL2", /* 0 */
+ "GETATTR2", /* 1 */
+ "SETATTR2", /* 2 */
+ "ROOT2", /* 3 */
+ "LOOKUP2", /* 4 */
+ "READLINK2", /* 5 */
+ "READ2", /* 6 */
+ "WRITECACHE2", /* 7 */
+ "WRITE2", /* 8 */
+ "CREATE2", /* 9 */
+ "REMOVE2", /* 10 */
+ "RENAME2", /* 11 */
+ "LINK2", /* 12 */
+ "SYMLINK2", /* 13 */
+ "MKDIR2", /* 14 */
+ "RMDIR2", /* 15 */
+ "READDIR2", /* 16 */
+ "STATFS2", /* 17 */
+};
+
+static char *procnames_long[] = {
+ "Null procedure", /* 0 */
+ "Get file attributes", /* 1 */
+ "Set file attributes", /* 2 */
+ "Get root filehandle", /* 3 */
+ "Look up file name", /* 4 */
+ "Read from symbolic link", /* 5 */
+ "Read from file", /* 6 */
+ "Write to cache", /* 7 */
+ "Write to file", /* 8 */
+ "Create file", /* 9 */
+ "Remove file", /* 10 */
+ "Rename", /* 11 */
+ "Link", /* 12 */
+ "Make symbolic link", /* 13 */
+ "Make directory", /* 14 */
+ "Remove directory", /* 15 */
+ "Read from directory", /* 16 */
+ "Get filesystem attributes", /* 17 */
+};
+
+#define MAXPROC 17
+
+/* ARGSUSED */
+void
+interpret_nfs(flags, type, xid, vers, proc, data, len)
+ int flags, type, xid, vers, proc;
+ char *data;
+ int len;
+{
+
+ if (vers == 2) {
+ interpret_nfs2(flags, type, xid, vers, proc, data, len);
+ return;
+ }
+
+ if (vers == 3) {
+ interpret_nfs3(flags, type, xid, vers, proc, data, len);
+ return;
+ }
+
+ if (vers == 4) {
+ interpret_nfs4(flags, type, xid, vers, proc, data, len);
+ return;
+ }
+}
+
+static void
+interpret_nfs2(flags, type, xid, vers, proc, data, len)
+ int flags, type, xid, vers, proc;
+ char *data;
+ int len;
+{
+ char *line;
+ char buff[NFS_MAXPATHLEN + 1];
+ int off, sz;
+ char *fh;
+
+ if (proc < 0 || proc > MAXPROC)
+ return;
+
+ if (flags & F_SUM) {
+ line = get_sum_line();
+
+ if (type == CALL) {
+ (void) sprintf(line,
+ "NFS C %s",
+ procnames_short[proc]);
+ line += strlen(line);
+ switch (proc) {
+ case NFSPROC_GETATTR:
+ case NFSPROC_READLINK:
+ case NFSPROC_STATFS:
+ case NFSPROC_SETATTR:
+ (void) sprintf(line, sum_nfsfh());
+ break;
+ case NFSPROC_LOOKUP:
+ case NFSPROC_REMOVE:
+ case NFSPROC_RMDIR:
+ case NFSPROC_CREATE:
+ case NFSPROC_MKDIR:
+ fh = sum_nfsfh();
+ (void) sprintf(line, "%s %s",
+ fh,
+ getxdr_string(buff, NFS_MAXNAMLEN));
+ break;
+ case NFSPROC_WRITE:
+ fh = sum_nfsfh();
+ (void) getxdr_long(); /* beginoff */
+ off = getxdr_long();
+ (void) getxdr_long(); /* totalcount */
+ sz = getxdr_long();
+ (void) sprintf(line, "%s at %d for %d",
+ fh, off, sz);
+ break;
+ case NFSPROC_RENAME:
+ fh = sum_nfsfh();
+ (void) sprintf(line, "%s %s",
+ fh,
+ getxdr_string(buff, NFS_MAXNAMLEN));
+ line += strlen(line);
+ fh = sum_nfsfh();
+ (void) sprintf(line, " to%s %s",
+ fh,
+ getxdr_string(buff, NFS_MAXNAMLEN));
+ break;
+ case NFSPROC_LINK:
+ fh = sum_nfsfh();
+ (void) sprintf(line, "%s", fh);
+ line += strlen(line);
+ fh = sum_nfsfh();
+ (void) sprintf(line, " to%s %s",
+ fh,
+ getxdr_string(buff, NFS_MAXNAMLEN));
+ break;
+ case NFSPROC_SYMLINK:
+ fh = sum_nfsfh();
+ (void) sprintf(line, "%s %s",
+ fh,
+ getxdr_string(buff, NFS_MAXNAMLEN));
+ line += strlen(line);
+ (void) sprintf(line, " to %s",
+ getxdr_string(buff, NFS_MAXPATHLEN));
+ break;
+ case NFSPROC_READDIR:
+ fh = sum_nfsfh();
+ (void) sprintf(line, "%s Cookie=%lu",
+ fh, getxdr_u_long());
+ break;
+ case NFSPROC_READ:
+ fh = sum_nfsfh();
+ off = getxdr_long();
+ sz = getxdr_long();
+ (void) sprintf(line, "%s at %d for %d",
+ fh, off, sz);
+ break;
+ default:
+ break;
+ }
+
+ check_retransmit(line, (ulong_t)xid);
+ } else {
+ (void) sprintf(line, "NFS R %s ",
+ procnames_short[proc]);
+ line += strlen(line);
+ switch (proc) {
+ case NFSPROC_CREATE:
+ case NFSPROC_MKDIR:
+ case NFSPROC_LOOKUP:
+ if (sum_nfsstat(line) == 0) {
+ line += strlen(line);
+ (void) sprintf(line, sum_nfsfh());
+ }
+ break;
+ case NFSPROC_READLINK:
+ if (sum_nfsstat(line) == 0) {
+ line += strlen(line);
+ (void) sprintf(line, " (Path=%s)",
+ getxdr_string(buff,
+ NFS_MAXPATHLEN));
+ }
+ break;
+ case NFSPROC_GETATTR:
+ case NFSPROC_SYMLINK:
+ case NFSPROC_STATFS:
+ case NFSPROC_SETATTR:
+ case NFSPROC_REMOVE:
+ case NFSPROC_RMDIR:
+ case NFSPROC_WRITE:
+ case NFSPROC_RENAME:
+ case NFSPROC_LINK:
+ (void) sum_nfsstat(line);
+ break;
+ case NFSPROC_READDIR:
+ if (sum_nfsstat(line) == 0) {
+ line += strlen(line);
+ (void) strcat(line, sum_readdirres());
+ }
+ break;
+ case NFSPROC_READ:
+ if (sum_nfsstat(line) == 0) {
+ line += strlen(line);
+ xdr_skip(68); /* fattrs */
+ (void) sprintf(line, " (%ld bytes)",
+ getxdr_long());
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("NFS: ", "Sun NFS", len);
+ show_space();
+ (void) sprintf(get_line(0, 0), "Proc = %d (%s)",
+ proc, procnames_long[proc]);
+ if (type == CALL)
+ nfscall2(proc);
+ else
+ nfsreply2(proc);
+ show_trailer();
+ }
+}
+
+/*
+ * Print out version 2 NFS call packets
+ */
+static void
+nfscall2(proc)
+ int proc;
+{
+ switch (proc) {
+ case NFSPROC_GETATTR:
+ case NFSPROC_READLINK:
+ case NFSPROC_STATFS:
+ detail_nfsfh();
+ break;
+ case NFSPROC_SETATTR:
+ detail_nfsfh();
+ detail_sattr();
+ break;
+ case NFSPROC_LOOKUP:
+ case NFSPROC_REMOVE:
+ case NFSPROC_RMDIR:
+ detail_diroparg();
+ break;
+ case NFSPROC_MKDIR:
+ case NFSPROC_CREATE:
+ detail_diroparg();
+ detail_sattr();
+ break;
+ case NFSPROC_WRITE:
+ detail_nfsfh();
+ (void) getxdr_long(); /* begoff */
+ (void) showxdr_long("Offset = %d");
+ (void) getxdr_long(); /* totalcount */
+ (void) showxdr_long("(%d bytes(s) of data)");
+ break;
+ case NFSPROC_RENAME:
+ detail_diroparg();
+ detail_diroparg();
+ break;
+ case NFSPROC_LINK:
+ detail_nfsfh();
+ detail_diroparg();
+ break;
+ case NFSPROC_SYMLINK:
+ detail_diroparg();
+ (void) showxdr_string(NFS_MAXPATHLEN, "Path = %s");
+ detail_sattr();
+ break;
+ case NFSPROC_READDIR:
+ detail_nfsfh();
+ (void) showxdr_u_long("Cookie = %lu");
+ (void) showxdr_long("Count = %d");
+ break;
+ case NFSPROC_READ:
+ detail_nfsfh();
+ (void) showxdr_long("Offset = %d");
+ (void) showxdr_long("Count = %d");
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * Print out version 2 NFS reply packets
+ */
+static void
+nfsreply2(proc)
+ int proc;
+{
+ switch (proc) {
+ case NFSPROC_GETATTR:
+ case NFSPROC_SETATTR:
+ case NFSPROC_WRITE:
+ /* attrstat */
+ if (detail_nfsstat() == 0) {
+ detail_fattr();
+ }
+ break;
+ case NFSPROC_LOOKUP:
+ case NFSPROC_CREATE:
+ case NFSPROC_MKDIR:
+ /* diropres */
+ if (detail_nfsstat() == 0) {
+ detail_nfsfh();
+ detail_fattr();
+ }
+ break;
+ case NFSPROC_READLINK:
+ /* readlinkres */
+ if (detail_nfsstat() == 0) {
+ (void) showxdr_string(NFS_MAXPATHLEN, "Path = %s");
+ }
+ break;
+ case NFSPROC_READ:
+ /* readres */
+ if (detail_nfsstat() == 0) {
+ detail_fattr();
+ (void) showxdr_long("(%d byte(s) of data)");
+ }
+ break;
+ case NFSPROC_REMOVE:
+ case NFSPROC_RENAME:
+ case NFSPROC_LINK:
+ case NFSPROC_SYMLINK:
+ case NFSPROC_RMDIR:
+ /* stat */
+ detail_nfsstat();
+ break;
+ case NFSPROC_READDIR:
+ /* readdirres */
+ if (detail_nfsstat() == 0)
+ detail_readdirres();
+ break;
+ case NFSPROC_STATFS:
+ /* statfsres */
+ if (detail_nfsstat() == 0) {
+ (void) showxdr_long("Transfer size = %d");
+ (void) showxdr_long("Block size = %d");
+ (void) showxdr_long("Total blocks = %d");
+ (void) showxdr_long("Free blocks = %d");
+ (void) showxdr_long("Available blocks = %d");
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+detail_diroparg()
+{
+ detail_nfsfh();
+ (void) showxdr_string(NFS_MAXPATHLEN, "File name = %s");
+}
+
+/*
+ * V2 NFS protocol was implicitly linked with SunOS errnos.
+ * Some of the errno values changed in SVr4.
+ * Need to map errno value so that SVr4 snoop will interpret
+ * them correctly.
+ */
+static char *
+statusmsg(status)
+ ulong_t status;
+{
+ switch (status) {
+ case NFS_OK: return ("OK");
+ case NFSERR_PERM: return ("Not owner");
+ case NFSERR_NOENT: return ("No such file or directory");
+ case NFSERR_IO: return ("I/O error");
+ case NFSERR_NXIO: return ("No such device or address");
+ case NFSERR_ACCES: return ("Permission denied");
+ case NFSERR_EXIST: return ("File exists");
+ case NFSERR_XDEV: return ("Cross-device link");
+ case NFSERR_NODEV: return ("No such device");
+ case NFSERR_NOTDIR: return ("Not a directory");
+ case NFSERR_ISDIR: return ("Is a directory");
+ case NFSERR_INVAL: return ("Invalid argument");
+ case NFSERR_FBIG: return ("File too large");
+ case NFSERR_NOSPC: return ("No space left on device");
+ case NFSERR_ROFS: return ("Read-only file system");
+ case NFSERR_OPNOTSUPP: return ("Operation not supported");
+ case NFSERR_NAMETOOLONG: return ("File name too long");
+ case NFSERR_NOTEMPTY: return ("Directory not empty");
+ case NFSERR_DQUOT: return ("Disc quota exceeded");
+ case NFSERR_STALE: return ("Stale NFS file handle");
+ case NFSERR_REMOTE: return ("Object is remote");
+ case NFSERR_WFLUSH: return ("write cache flushed");
+ default: return ("(unknown error)");
+ }
+ /* NOTREACHED */
+}
+
+int
+sum_nfsstat(line)
+ char *line;
+{
+ ulong_t status;
+
+ status = getxdr_long();
+ (void) strcpy(line, statusmsg(status));
+ return (status);
+}
+
+int
+detail_nfsstat()
+{
+ ulong_t status;
+ int pos;
+
+ pos = getxdr_pos();
+ status = getxdr_long();
+ (void) sprintf(get_line(pos, getxdr_pos()),
+ "Status = %lu (%s)",
+ status, statusmsg(status));
+
+ return ((int)status);
+}
+
+int
+sum_filehandle(len)
+ int len;
+{
+ int i, l;
+ int fh = 0;
+
+ for (i = 0; i < len; i += 4) {
+ l = getxdr_long();
+ fh ^= (l >> 16) ^ l;
+ }
+
+ return (fh);
+}
+
+char *
+sum_nfsfh()
+{
+ int fh;
+ static char buff[16];
+
+ fh = sum_filehandle(NFS_FHSIZE);
+ (void) sprintf(buff, " FH=%04X", fh & 0xFFFF);
+ return (buff);
+}
+
+void
+detail_nfsfh()
+{
+ int pos;
+ int fh;
+
+ pos = getxdr_pos();
+ fh = sum_filehandle(NFS_FHSIZE);
+ setxdr_pos(pos);
+ (void) sprintf(get_line(0, 0), "File handle = [%04X]", fh & 0xFFFF);
+ (void) showxdr_hex(NFS_FHSIZE, " %s");
+}
+
+static void
+detail_mode(mode)
+ int mode;
+{
+ char *str;
+
+ switch (mode & S_IFMT) {
+ case S_IFDIR: str = "Directory"; break;
+ case S_IFCHR: str = "Character"; break;
+ case S_IFBLK: str = "Block"; break;
+ case S_IFREG: str = "Regular file"; break;
+ case S_IFLNK: str = "Link"; break;
+ case S_IFSOCK: str = "Socket"; break;
+ case S_IFIFO: str = "Fifo"; break;
+ default: str = "?"; break;
+ }
+
+ (void) sprintf(get_line(0, 0), "Mode = 0%o", mode);
+ (void) sprintf(get_line(0, 0), " Type = %s", str);
+ (void) sprintf(get_line(0, 0),
+ " Setuid = %d, Setgid = %d, Sticky = %d",
+ (mode & S_ISUID) != 0,
+ (mode & S_ISGID) != 0,
+ (mode & S_ISVTX) != 0);
+ (void) sprintf(get_line(0, 0), " Owner's permissions = %s",
+ perms(mode >> 6 & 0x7));
+ (void) sprintf(get_line(0, 0), " Group's permissions = %s",
+ perms(mode >> 3 & 0x7));
+ (void) sprintf(get_line(0, 0), " Other's permissions = %s",
+ perms(mode & 0x7));
+}
+
+void
+detail_fattr()
+{
+ int fltype, mode, nlinks, uid, gid, size, blksz;
+ int rdev, blocks, fsid, fileid;
+
+ fltype = getxdr_long();
+ mode = getxdr_long();
+ nlinks = getxdr_long();
+ uid = getxdr_long();
+ gid = getxdr_long();
+ size = getxdr_long();
+ blksz = getxdr_long();
+ rdev = getxdr_long();
+ blocks = getxdr_long();
+ fsid = getxdr_long();
+ fileid = getxdr_long();
+
+ (void) sprintf(get_line(0, 0),
+ "File type = %d (%s)",
+ fltype, filetype(fltype));
+ detail_mode(mode);
+ (void) sprintf(get_line(0, 0),
+ "Link count = %d, UID = %d, GID = %d, Rdev = 0x%x",
+ nlinks, uid, gid, rdev);
+ (void) sprintf(get_line(0, 0),
+ "File size = %d, Block size = %d, No. of blocks = %d",
+ size, blksz, blocks);
+ (void) sprintf(get_line(0, 0),
+ "File system id = %d, File id = %d",
+ fsid, fileid);
+ (void) showxdr_date("Access time = %s");
+ (void) showxdr_date("Modification time = %s");
+ (void) showxdr_date("Inode change time = %s");
+}
+
+static void
+detail_sattr()
+{
+ int mode;
+
+ mode = getxdr_long();
+ detail_mode(mode);
+ (void) showxdr_long("UID = %d");
+ (void) showxdr_long("GID = %d");
+ (void) showxdr_long("Size = %d");
+ (void) showxdr_date("Access time = %s");
+ (void) showxdr_date("Modification time = %s");
+}
+
+static char *
+filetype(n)
+ int n;
+{
+ switch (n) {
+ case NFREG: return ("Regular File");
+ case NFDIR: return ("Directory");
+ case NFBLK: return ("Block special");
+ case NFCHR: return ("Character special");
+ case NFLNK: return ("Symbolic Link");
+ default: return ("?");
+ }
+}
+
+static char *
+perms(n)
+ int n;
+{
+ static char buff[4];
+
+ buff[0] = n & 4 ? 'r' : '-';
+ buff[1] = n & 2 ? 'w' : '-';
+ buff[2] = n & 1 ? 'x' : '-';
+ buff[3] = '\0';
+ return (buff);
+}
+
+static char *
+sum_readdirres()
+{
+ static char buff[NFS_MAXNAMLEN + 1];
+ int entries = 0;
+
+ if (setjmp(xdr_err)) {
+ (void) sprintf(buff, " %d+ entries (incomplete)", entries);
+ return (buff);
+ }
+ while (getxdr_long()) {
+ entries++;
+ (void) getxdr_long(); /* fileid */
+ (void) getxdr_string(buff, NFS_MAXNAMLEN); /* name */
+ (void) getxdr_u_long(); /* cookie */
+ }
+
+ (void) sprintf(buff, " %d entries (%s)",
+ entries,
+ getxdr_long() ? "No more" : "More");
+ return (buff);
+}
+
+static void
+detail_readdirres()
+{
+ ulong_t fileid, cookie;
+ int entries = 0;
+ char *name;
+ char buff[NFS_MAXNAMLEN + 1];
+
+ (void) sprintf(get_line(0, 0), " File id Cookie Name");
+
+ if (setjmp(xdr_err)) {
+ (void) sprintf(get_line(0, 0),
+ " %d+ entries. (Frame is incomplete)",
+ entries);
+ return;
+ }
+ while (getxdr_long()) {
+ entries++;
+ fileid = getxdr_long();
+ name = (char *)getxdr_string(buff, NFS_MAXNAMLEN);
+ cookie = getxdr_u_long();
+ (void) sprintf(get_line(0, 0),
+ " %7lu %7lu %s",
+ fileid, cookie, name);
+ }
+
+ (void) sprintf(get_line(0, 0), " %d entries", entries);
+ (void) showxdr_long("EOF = %d");
+}
+
+void
+skip_fattr()
+{
+
+ xdr_skip(17 * 4); /* XDR sizeof nfsfattr */
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nfs.h b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nfs.h
new file mode 100644
index 0000000000..576590a506
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nfs.h
@@ -0,0 +1,58 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _SNOOP_NFS_H
+#define _SNOOP_NFS_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Definitions that are shared among the NFS-related interpreters.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern char *sum_nfsfh(void);
+extern void detail_nfsfh(void);
+extern int sum_nfsstat(char *);
+extern int detail_nfsstat(void);
+extern void detail_fattr(void);
+extern void skip_fattr(void);
+extern int sum_filehandle(int);
+
+extern char *sum_nfsfh3(void);
+extern void detail_nfsfh3(void);
+extern int sum_nfsstat3(char *);
+extern void detail_post_op_attr(char *);
+extern int detail_nfsstat3(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SNOOP_NFS_H */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nfs3.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nfs3.c
new file mode 100644
index 0000000000..6355a035a1
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nfs3.c
@@ -0,0 +1,1214 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1991, 1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ident "%Z%%M% %I% %E% SMI" /* SunOS */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/tiuser.h>
+#include <setjmp.h>
+#include <string.h>
+
+#include <rpc/types.h>
+#include <rpc/xdr.h>
+#include <rpc/auth.h>
+#include <rpc/clnt.h>
+#include <rpc/rpc_msg.h>
+#include "snoop.h"
+#include "snoop_nfs.h"
+
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <rpcsvc/nfs_prot.h>
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+extern jmp_buf xdr_err;
+
+static void nfscall3(int);
+static void nfsreply3(int);
+static char *perms(int);
+static char *filetype(int);
+static char *sum_access(void);
+static char *sum_readdirres(void);
+static char *sum_readdirplusres(void);
+static char *sum_createhow(void);
+static char *sum_stablehow(void);
+static void detail_sattr3(void);
+static void detail_diropargs3(void);
+static void detail_readdirres(void);
+static void detail_readdirplusres(void);
+static void detail_fattr3(void);
+static void detail_access(void);
+static void detail_mode(int);
+static void detail_wcc_attr(void);
+static void detail_pre_op_attr(char *);
+static void detail_wcc_data(char *);
+static void skip_postop(void);
+static void skip_wcc_data(void);
+static void skip_sattr3(void);
+
+#define DONT_CHANGE 0
+#define SET_TO_SERVER_TIME 1
+#define SET_TO_CLIENT_TIME 2
+
+#define UNCHECKED 0
+#define GUARDED 1
+#define EXCLUSIVE 2
+
+#define ACCESS3_READ 0x0001
+#define ACCESS3_LOOKUP 0x0002
+#define ACCESS3_MODIFY 0x0004
+#define ACCESS3_EXTEND 0x0008
+#define ACCESS3_DELETE 0x0010
+#define ACCESS3_EXECUTE 0x0020
+
+#define UNSTABLE 0
+#define DATA_SYNC 1
+#define FILE_SYNC 2
+
+#define NF3REG 1 /* regular file */
+#define NF3DIR 2 /* directory */
+#define NF3BLK 3 /* block special */
+#define NF3CHR 4 /* character special */
+#define NF3LNK 5 /* symbolic link */
+#define NF3SOCK 6 /* unix domain socket */
+#define NF3FIFO 7 /* named pipe */
+
+#define NFS3_FHSIZE 64
+
+static char *procnames_short[] = {
+ "NULL3", /* 0 */
+ "GETATTR3", /* 1 */
+ "SETATTR3", /* 2 */
+ "LOOKUP3", /* 3 */
+ "ACCESS3", /* 4 */
+ "READLINK3", /* 5 */
+ "READ3", /* 6 */
+ "WRITE3", /* 7 */
+ "CREATE3", /* 8 */
+ "MKDIR3", /* 9 */
+ "SYMLINK3", /* 10 */
+ "MKNOD3", /* 11 */
+ "REMOVE3", /* 12 */
+ "RMDIR3", /* 13 */
+ "RENAME3", /* 14 */
+ "LINK3", /* 15 */
+ "READDIR3", /* 16 */
+ "READDIRPLUS3", /* 17 */
+ "FSSTAT3", /* 18 */
+ "FSINFO3", /* 19 */
+ "PATHCONF3", /* 20 */
+ "COMMIT3", /* 21 */
+};
+
+static char *procnames_long[] = {
+ "Null procedure", /* 0 */
+ "Get file attributes", /* 1 */
+ "Set file attributes", /* 2 */
+ "Look up file name", /* 3 */
+ "Check access permission", /* 4 */
+ "Read from symbolic link", /* 5 */
+ "Read from file", /* 6 */
+ "Write to file", /* 7 */
+ "Create file", /* 8 */
+ "Make directory", /* 9 */
+ "Make symbolic link", /* 10 */
+ "Make special file", /* 11 */
+ "Remove file", /* 12 */
+ "Remove directory", /* 13 */
+ "Rename", /* 14 */
+ "Link", /* 15 */
+ "Read from directory", /* 16 */
+ "Read from directory - plus", /* 17 */
+ "Get filesystem statistics", /* 18 */
+ "Get filesystem information", /* 19 */
+ "Get POSIX information", /* 20 */
+ "Commit to stable storage", /* 21 */
+};
+
+#define MAXPROC 21
+
+void
+interpret_nfs3(flags, type, xid, vers, proc, data, len)
+ int flags, type, xid, vers, proc;
+ char *data;
+ int len;
+{
+ char *line;
+ char buff[NFS_MAXPATHLEN + 1]; /* protocol allows longer */
+ u_longlong_t off;
+ int sz, how;
+ char *fh, *name;
+
+ if (proc < 0 || proc > MAXPROC)
+ return;
+
+ if (flags & F_SUM) {
+ line = get_sum_line();
+
+ if (type == CALL) {
+ (void) sprintf(line, "NFS C %s",
+ procnames_short[proc]);
+ line += strlen(line);
+ switch (proc) {
+ case NFSPROC3_GETATTR:
+ case NFSPROC3_READLINK:
+ case NFSPROC3_FSSTAT:
+ case NFSPROC3_FSINFO:
+ case NFSPROC3_PATHCONF:
+ (void) sprintf(line, sum_nfsfh3());
+ break;
+ case NFSPROC3_SETATTR:
+ (void) sprintf(line, sum_nfsfh3());
+ break;
+ case NFSPROC3_READDIR:
+ fh = sum_nfsfh3();
+ off = getxdr_u_longlong();
+ (void) getxdr_u_longlong();
+ sz = getxdr_u_long();
+ (void) sprintf(line, "%s Cookie=%llu for %lu",
+ fh, off, sz);
+ break;
+ case NFSPROC3_READDIRPLUS:
+ fh = sum_nfsfh3();
+ off = getxdr_u_longlong();
+ (void) getxdr_u_longlong();
+ sz = getxdr_u_long();
+ (void) sprintf(line,
+ "%s Cookie=%llu for %lu/%lu",
+ fh, off, sz, getxdr_u_long());
+ break;
+ case NFSPROC3_ACCESS:
+ fh = sum_nfsfh3();
+ (void) sprintf(line, "%s (%s)",
+ fh, sum_access());
+ break;
+ case NFSPROC3_LOOKUP:
+ case NFSPROC3_REMOVE:
+ case NFSPROC3_RMDIR:
+ case NFSPROC3_MKDIR:
+ fh = sum_nfsfh3();
+ (void) sprintf(line, "%s %s",
+ fh, getxdr_string(buff,
+ NFS_MAXPATHLEN));
+ break;
+ case NFSPROC3_CREATE:
+ fh = sum_nfsfh3();
+ name = getxdr_string(buff, NFS_MAXPATHLEN);
+ (void) sprintf(line, "%s (%s) %s",
+ fh, sum_createhow(), name);
+ break;
+ case NFSPROC3_MKNOD:
+ fh = sum_nfsfh3();
+ name = getxdr_string(buff, NFS_MAXPATHLEN);
+ how = getxdr_long();
+ (void) sprintf(line, "%s (%s) %s",
+ fh, filetype(how), name);
+ break;
+ case NFSPROC3_READ:
+ fh = sum_nfsfh3();
+ off = getxdr_u_longlong();
+ sz = getxdr_u_long();
+ (void) sprintf(line, "%s at %llu for %lu",
+ fh, off, sz);
+ break;
+ case NFSPROC3_WRITE:
+ fh = sum_nfsfh3();
+ off = getxdr_u_longlong();
+ sz = getxdr_u_long();
+ (void) sprintf(line, "%s at %llu for %lu (%s)",
+ fh, off, sz, sum_stablehow());
+ break;
+ case NFSPROC3_SYMLINK:
+ fh = sum_nfsfh3();
+ (void) sprintf(line, "%s %s",
+ fh, getxdr_string(buff,
+ NFS_MAXPATHLEN));
+ skip_sattr3();
+ line += strlen(line);
+ (void) sprintf(line, " to %s",
+ getxdr_string(buff, NFS_MAXPATHLEN));
+ break;
+ case NFSPROC3_RENAME:
+ fh = sum_nfsfh3();
+ (void) sprintf(line, "%s %s",
+ fh, getxdr_string(buff,
+ NFS_MAXPATHLEN));
+ line += strlen(line);
+ fh = sum_nfsfh3();
+ (void) sprintf(line, " to%s %s",
+ fh, getxdr_string(buff,
+ NFS_MAXPATHLEN));
+ break;
+ case NFSPROC3_LINK:
+ fh = sum_nfsfh3();
+ (void) sprintf(line, "%s", fh);
+ line += strlen(line);
+ fh = sum_nfsfh3();
+ (void) sprintf(line, " to%s %s",
+ fh, getxdr_string(buff,
+ NFS_MAXPATHLEN));
+ break;
+ case NFSPROC3_COMMIT:
+ fh = sum_nfsfh3();
+ off = getxdr_u_longlong();
+ sz = getxdr_u_long();
+ (void) sprintf(line, "%s at %llu for %lu",
+ fh, off, sz);
+ break;
+ default:
+ break;
+ }
+
+ check_retransmit(line, xid);
+ } else {
+ (void) sprintf(line, "NFS R %s ",
+ procnames_short[proc]);
+ line += strlen(line);
+ switch (proc) {
+ case NFSPROC3_LOOKUP:
+ if (sum_nfsstat3(line) == NFS3_OK)
+ (void) strcat(line, sum_nfsfh3());
+ break;
+ case NFSPROC3_CREATE:
+ case NFSPROC3_MKDIR:
+ case NFSPROC3_SYMLINK:
+ case NFSPROC3_MKNOD:
+ if (sum_nfsstat3(line) == NFS3_OK) {
+ if (getxdr_bool())
+ (void) strcat(line,
+ sum_nfsfh3());
+ }
+ break;
+ case NFSPROC3_READLINK:
+ if (sum_nfsstat3(line) == NFS3_OK) {
+ line += strlen(line);
+ skip_postop();
+ (void) sprintf(line, " (Path=%s)",
+ getxdr_string(buff,
+ NFS_MAXPATHLEN));
+ }
+ break;
+ case NFSPROC3_GETATTR:
+ case NFSPROC3_SETATTR:
+ case NFSPROC3_REMOVE:
+ case NFSPROC3_RMDIR:
+ case NFSPROC3_RENAME:
+ case NFSPROC3_LINK:
+ case NFSPROC3_FSSTAT:
+ case NFSPROC3_FSINFO:
+ case NFSPROC3_PATHCONF:
+ (void) sum_nfsstat3(line);
+ break;
+ case NFSPROC3_ACCESS:
+ if (sum_nfsstat3(line) == NFS3_OK) {
+ line += strlen(line);
+ skip_postop();
+ (void) sprintf(line, " (%s)",
+ sum_access());
+ }
+ break;
+ case NFSPROC3_WRITE:
+ if (sum_nfsstat3(line) == NFS3_OK) {
+ line += strlen(line);
+ skip_wcc_data();
+ sz = getxdr_u_long();
+ (void) sprintf(line, " %d (%s)",
+ sz, sum_stablehow());
+ }
+ break;
+ case NFSPROC3_READDIR:
+ if (sum_nfsstat3(line) == NFS3_OK)
+ (void) strcat(line, sum_readdirres());
+ break;
+ case NFSPROC3_READ:
+ if (sum_nfsstat3(line) == NFS3_OK) {
+ line += strlen(line);
+ skip_postop();
+ (void) sprintf(line, " (%lu bytes)",
+ getxdr_u_long());
+ if (getxdr_bool())
+ (void) strcat(line, " EOF");
+ }
+ break;
+ case NFSPROC3_READDIRPLUS:
+ if (sum_nfsstat3(line) == NFS3_OK)
+ (void) strcat(line,
+ sum_readdirplusres());
+ break;
+ case NFSPROC3_COMMIT:
+ (void) sum_nfsstat3(line);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("NFS: ", "Sun NFS", len);
+ show_space();
+ (void) sprintf(get_line(0, 0), "Proc = %d (%s)",
+ proc, procnames_long[proc]);
+ if (type == CALL)
+ nfscall3(proc);
+ else
+ nfsreply3(proc);
+ show_trailer();
+ }
+}
+
+/*
+ * Print out version 3 NFS call packets
+ */
+static void
+nfscall3(proc)
+ int proc;
+{
+ int h;
+
+ switch (proc) {
+ case NFSPROC3_GETATTR:
+ case NFSPROC3_READLINK:
+ case NFSPROC3_FSINFO:
+ case NFSPROC3_FSSTAT:
+ case NFSPROC3_PATHCONF:
+ detail_nfsfh3();
+ break;
+ case NFSPROC3_SETATTR:
+ detail_nfsfh3();
+ detail_sattr3();
+ if (getxdr_bool())
+ (void) showxdr_date_ns("Guard = %s");
+ break;
+ case NFSPROC3_LOOKUP:
+ case NFSPROC3_REMOVE:
+ case NFSPROC3_RMDIR:
+ detail_diropargs3();
+ break;
+ case NFSPROC3_ACCESS:
+ detail_nfsfh3();
+ detail_access();
+ break;
+ case NFSPROC3_MKDIR:
+ detail_diropargs3();
+ detail_sattr3();
+ break;
+ case NFSPROC3_CREATE:
+ detail_diropargs3();
+ h = getxdr_u_long();
+ if (h == EXCLUSIVE)
+ showxdr_hex(8, "Guard = %s");
+ else {
+ (void) sprintf(get_line(0, 0), "Method = %s",
+ h == UNCHECKED ? "Unchecked" : "Guarded");
+ detail_sattr3();
+ }
+ break;
+ case NFSPROC3_MKNOD:
+ detail_diropargs3();
+ h = getxdr_u_long();
+ (void) sprintf(get_line(0, 0), "File type = %s",
+ filetype(h));
+ switch (h) {
+ case NF3CHR:
+ case NF3BLK:
+ detail_sattr3();
+ showxdr_u_long("Major = %lu");
+ showxdr_u_long("Minor = %lu");
+ break;
+ case NF3SOCK:
+ case NF3FIFO:
+ detail_sattr3();
+ break;
+ }
+ break;
+ case NFSPROC3_WRITE:
+ detail_nfsfh3();
+ (void) showxdr_u_longlong("Offset = %llu");
+ (void) showxdr_u_long("Size = %lu");
+ (void) sprintf(get_line(0, 0), "Stable = %s",
+ sum_stablehow());
+ break;
+ case NFSPROC3_RENAME:
+ detail_diropargs3();
+ detail_diropargs3();
+ break;
+ case NFSPROC3_LINK:
+ detail_nfsfh3();
+ detail_diropargs3();
+ break;
+ case NFSPROC3_SYMLINK:
+ detail_diropargs3();
+ detail_sattr3();
+ (void) showxdr_string(MAXPATHLEN, "Path = %s");
+ break;
+ case NFSPROC3_READDIR:
+ detail_nfsfh3();
+ (void) showxdr_u_longlong("Cookie = %llu");
+ (void) showxdr_hex(8, "Verifier = %s");
+ (void) showxdr_u_long("Count = %lu");
+ break;
+ case NFSPROC3_READDIRPLUS:
+ detail_nfsfh3();
+ (void) showxdr_u_longlong("Cookie = %llu");
+ (void) showxdr_hex(8, "Verifier = %s");
+ (void) showxdr_u_long("Dircount = %lu");
+ (void) showxdr_u_long("Maxcount = %lu");
+ break;
+ case NFSPROC3_READ:
+ case NFSPROC3_COMMIT:
+ detail_nfsfh3();
+ (void) showxdr_u_longlong("Offset = %llu");
+ (void) showxdr_long("Count = %lu");
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * Print out version 3 NFS reply packets
+ */
+static void
+nfsreply3(proc)
+ int proc;
+{
+ int bits;
+
+ switch (proc) {
+ case NFSPROC3_GETATTR:
+ if (detail_nfsstat3() == NFS3_OK) {
+ detail_fattr3();
+ }
+ break;
+ case NFSPROC3_SETATTR:
+ (void) detail_nfsstat3();
+ detail_wcc_data("");
+ break;
+ case NFSPROC3_WRITE:
+ if (detail_nfsstat3() == NFS3_OK) {
+ detail_wcc_data("");
+ (void) showxdr_u_long("Count = %lu bytes written");
+ (void) sprintf(get_line(0, 0), "Stable = %s",
+ sum_stablehow());
+ (void) showxdr_hex(8, "Verifier = %s");
+ } else
+ detail_wcc_data("");
+ break;
+ case NFSPROC3_LOOKUP:
+ if (detail_nfsstat3() == NFS3_OK) {
+ detail_nfsfh3();
+ detail_post_op_attr("(object)");
+ }
+ detail_post_op_attr("(directory)");
+ break;
+ case NFSPROC3_CREATE:
+ case NFSPROC3_MKDIR:
+ case NFSPROC3_SYMLINK:
+ case NFSPROC3_MKNOD:
+ if (detail_nfsstat3() == NFS3_OK) {
+ if (getxdr_bool())
+ detail_nfsfh3();
+ else
+ (void) sprintf(get_line(0, 0),
+ "(No file handle available)");
+ detail_post_op_attr("");
+ }
+ detail_wcc_data("");
+ break;
+ case NFSPROC3_READLINK:
+ if (detail_nfsstat3() == NFS3_OK) {
+ detail_post_op_attr("");
+ (void) showxdr_string(MAXPATHLEN, "Path = %s");
+ } else
+ detail_post_op_attr("");
+ break;
+ case NFSPROC3_READ:
+ if (detail_nfsstat3() == NFS3_OK) {
+ detail_post_op_attr("");
+ (void) showxdr_u_long("Count = %lu bytes read");
+ (void) showxdr_bool("End of file = %s");
+ } else
+ detail_post_op_attr("");
+ break;
+ case NFSPROC3_ACCESS:
+ if (detail_nfsstat3() == NFS3_OK) {
+ detail_post_op_attr("");
+ (void) sprintf(get_line(0, 0), "Access = %s",
+ sum_access());
+ } else
+ detail_post_op_attr("");
+ break;
+ case NFSPROC3_REMOVE:
+ case NFSPROC3_RMDIR:
+ (void) detail_nfsstat3();
+ detail_wcc_data("");
+ break;
+ case NFSPROC3_RENAME:
+ (void) detail_nfsstat3();
+ detail_wcc_data("(from directory)");
+ detail_wcc_data("(to directory)");
+ break;
+ case NFSPROC3_LINK:
+ (void) detail_nfsstat3();
+ detail_post_op_attr("");
+ detail_wcc_data("");
+ break;
+ case NFSPROC3_READDIR:
+ if (detail_nfsstat3() == NFS3_OK) {
+ detail_readdirres();
+ } else
+ detail_post_op_attr("");
+ break;
+ case NFSPROC3_READDIRPLUS:
+ if (detail_nfsstat3() == NFS3_OK) {
+ detail_readdirplusres();
+ } else
+ detail_post_op_attr("");
+ break;
+ case NFSPROC3_FSSTAT:
+ if (detail_nfsstat3() == NFS3_OK) {
+ detail_post_op_attr("");
+ (void) showxdr_u_longlong(
+ "Total space = %llu bytes");
+ (void) showxdr_u_longlong(
+ "Available space = %llu bytes");
+ (void) showxdr_u_longlong(
+ "Available space - this user = %llu bytes");
+ (void) showxdr_u_longlong(
+ "Total file slots = %llu");
+ (void) showxdr_u_longlong(
+ "Available file slots = %llu");
+ (void) showxdr_u_longlong(
+ "Available file slots - this user = %llu");
+ (void) showxdr_u_long("Invariant time = %lu sec");
+ } else
+ detail_post_op_attr("");
+ break;
+ case NFSPROC3_FSINFO:
+ if (detail_nfsstat3() == NFS3_OK) {
+ detail_post_op_attr("");
+ (void) show_line("Read transfer sizes:");
+ (void) showxdr_u_long(" Maximum = %lu bytes");
+ (void) showxdr_u_long(" Preferred = %lu bytes");
+ (void) showxdr_u_long(
+ " Suggested multiple = %lu bytes");
+ (void) show_line("Write transfer sizes:");
+ (void) showxdr_u_long(" Maximum = %lu bytes");
+ (void) showxdr_u_long(" Preferred = %lu bytes");
+ (void) showxdr_u_long(
+ " Suggested multiple = %lu bytes");
+ (void) show_line("Directory read size:");
+ (void) showxdr_u_long(" Preferred = %lu bytes");
+ (void) show_line("File system limits:");
+ (void) showxdr_u_longlong(
+ " Max file size = %llu bytes");
+ (void) showxdr_date_ns(
+ " Server minimum time discrimination = %s sec");
+ bits = showxdr_u_long("Properties = 0x%02x");
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(bits, FSF3_LINK,
+ "Hard links supported",
+ "(hard links not supported)"));
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(bits, FSF3_SYMLINK,
+ "Symbolic links supported",
+ "(symbolic links not supported)"));
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(bits, FSF3_HOMOGENEOUS,
+ "Pathconf cannot vary per file",
+ "(pathconf can vary per file)"));
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(bits, FSF3_CANSETTIME,
+ "Server can always set file times",
+ "(server cannot always set file times)"));
+ } else
+ detail_post_op_attr("");
+ break;
+ case NFSPROC3_PATHCONF:
+ if (detail_nfsstat3() == NFS3_OK) {
+ detail_post_op_attr("");
+ (void) showxdr_u_long("Link max = %lu");
+ (void) showxdr_u_long("Name max = %lu");
+ (void) showxdr_bool("No trunc = %s");
+ (void) showxdr_bool("Chown restricted = %s");
+ (void) showxdr_bool("Case insensitive = %s");
+ (void) showxdr_bool("Case preserving = %s");
+ } else
+ detail_post_op_attr("");
+ break;
+ case NFSPROC3_COMMIT:
+ if (detail_nfsstat3() == NFS3_OK) {
+ detail_wcc_data("");
+ (void) showxdr_hex(8, "Verifier = %s");
+ } else
+ detail_wcc_data("");
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+detail_diropargs3()
+{
+
+ detail_nfsfh3();
+ (void) showxdr_string(MAXPATHLEN, "File name = %s");
+}
+
+int
+sum_nfsstat3(line)
+ char *line;
+{
+ ulong_t status;
+ char *p;
+
+ status = getxdr_long();
+ switch (status) {
+ case NFS3_OK: p = "OK"; break;
+ case NFS3ERR_PERM: p = "Not owner"; break;
+ case NFS3ERR_NOENT: p = "No such file or directory"; break;
+ case NFS3ERR_IO: p = "I/O error"; break;
+ case NFS3ERR_NXIO: p = "No such device or address"; break;
+ case NFS3ERR_ACCES: p = "Permission denied"; break;
+ case NFS3ERR_EXIST: p = "File exists"; break;
+ case NFS3ERR_XDEV: p = "Attempted cross-device link"; break;
+ case NFS3ERR_NODEV: p = "No such device"; break;
+ case NFS3ERR_NOTDIR: p = "Not a directory"; break;
+ case NFS3ERR_ISDIR: p = "Is a directory"; break;
+ case NFS3ERR_INVAL: p = "Invalid argument"; break;
+ case NFS3ERR_FBIG: p = "File too large"; break;
+ case NFS3ERR_NOSPC: p = "No space left on device"; break;
+ case NFS3ERR_ROFS: p = "Read-only file system"; break;
+ case NFS3ERR_MLINK: p = "Too many links"; break;
+ case NFS3ERR_NAMETOOLONG:p = "File name too long"; break;
+ case NFS3ERR_NOTEMPTY: p = "Directory not empty"; break;
+ case NFS3ERR_DQUOT: p = "Disc quota exceeded"; break;
+ case NFS3ERR_STALE: p = "Stale NFS file handle"; break;
+ case NFS3ERR_REMOTE: p = "Too many levels of remote in path"; break;
+ case NFS3ERR_BADHANDLE: p = "Illegal NFS file handle"; break;
+ case NFS3ERR_NOT_SYNC: p = "Update synch mismatch"; break;
+ case NFS3ERR_BAD_COOKIE:p = "Readdir cookie is stale"; break;
+ case NFS3ERR_NOTSUPP: p = "Operation not supported"; break;
+ case NFS3ERR_TOOSMALL: p = "Buffer/request too small"; break;
+ case NFS3ERR_SERVERFAULT:p = "Server fault"; break;
+ case NFS3ERR_BADTYPE: p = "Bad type"; break;
+ case NFS3ERR_JUKEBOX: p = "File is temporarily unavailable"; break;
+ default: p = "(unknown error)"; break;
+ }
+
+ (void) strcpy(line, p);
+ return (status);
+}
+
+int
+detail_nfsstat3()
+{
+ ulong_t status;
+ char buff[64];
+ int pos;
+
+ pos = getxdr_pos();
+ status = sum_nfsstat3(buff);
+
+ (void) sprintf(get_line(pos, getxdr_pos()), "Status = %d (%s)",
+ status, buff);
+
+ return ((int)status);
+}
+
+static void
+skip_postop()
+{
+
+ if (getxdr_bool())
+ xdr_skip(21 * 4); /* XDR size of fattr3 */
+}
+
+static void
+skip_wcc_data()
+{
+
+ if (getxdr_bool() > 0)
+ xdr_skip(3 * 8);
+ skip_postop();
+}
+
+static void
+skip_sattr3()
+{
+
+ if (getxdr_bool() > 0)
+ xdr_skip(4); /* mode */
+ if (getxdr_bool() > 0)
+ xdr_skip(4); /* uid */
+ if (getxdr_bool() > 0)
+ xdr_skip(4); /* gid */
+ if (getxdr_bool() > 0)
+ xdr_skip(8); /* size */
+ if (getxdr_bool() > 0)
+ xdr_skip(8); /* atime */
+ if (getxdr_bool() > 0)
+ xdr_skip(8); /* mtime */
+}
+
+char *
+sum_nfsfh3()
+{
+ int len;
+ int fh;
+ static char buff[16];
+
+ len = getxdr_long();
+ fh = sum_filehandle(len);
+ (void) sprintf(buff, " FH=%04X", fh & 0xFFFF);
+ return (buff);
+}
+
+void
+detail_nfsfh3()
+{
+ int pos;
+ int i, l, len;
+ int fh;
+
+ len = getxdr_long();
+ pos = getxdr_pos();
+ fh = sum_filehandle(len);
+ setxdr_pos(pos);
+ (void) sprintf(get_line(0, 0), "File handle = [%04X]", fh & 0xFFFF);
+ i = 0;
+ while (i < len) {
+ l = MIN(len - i, 32);
+ (void) showxdr_hex(l, " %s");
+ i += l;
+ }
+}
+
+static char *
+sum_access()
+{
+ int bits;
+ static char buff[64];
+
+ bits = getxdr_u_long();
+ buff[0] = '\0';
+
+ if (bits & ACCESS3_READ)
+ (void) strcat(buff, "read,");
+ if (bits & ACCESS3_LOOKUP)
+ (void) strcat(buff, "lookup,");
+ if (bits & ACCESS3_MODIFY)
+ (void) strcat(buff, "modify,");
+ if (bits & ACCESS3_EXTEND)
+ (void) strcat(buff, "extend,");
+ if (bits & ACCESS3_DELETE)
+ (void) strcat(buff, "delete,");
+ if (bits & ACCESS3_EXECUTE)
+ (void) strcat(buff, "execute,");
+ if (buff[0] != '\0')
+ buff[strlen(buff) - 1] = '\0';
+
+ return (buff);
+}
+
+static void
+detail_access()
+{
+ uint_t bits;
+
+ bits = showxdr_u_long("Access bits = 0x%08x");
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(bits, ACCESS3_READ, "Read", "(no read)"));
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(bits, ACCESS3_LOOKUP, "Lookup", "(no lookup)"));
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(bits, ACCESS3_MODIFY, "Modify", "(no modify)"));
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(bits, ACCESS3_EXTEND, "Extend", "(no extend)"));
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(bits, ACCESS3_DELETE, "Delete", "(no delete)"));
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(bits, ACCESS3_EXECUTE, "Execute", "(no execute)"));
+}
+
+static void
+detail_mode(mode)
+ int mode;
+{
+
+ (void) sprintf(get_line(0, 0), " Mode = 0%o", mode);
+ (void) sprintf(get_line(0, 0),
+ " Setuid = %d, Setgid = %d, Sticky = %d",
+ (mode & S_ISUID) != 0,
+ (mode & S_ISGID) != 0,
+ (mode & S_ISVTX) != 0);
+ (void) sprintf(get_line(0, 0), " Owner's permissions = %s",
+ perms(mode >> 6 & 0x7));
+ (void) sprintf(get_line(0, 0), " Group's permissions = %s",
+ perms(mode >> 3 & 0x7));
+ (void) sprintf(get_line(0, 0), " Other's permissions = %s",
+ perms(mode & 0x7));
+}
+
+static void
+detail_fattr3()
+{
+ uint_t fltype, mode, nlinks, uid, gid;
+ uint_t major, minor;
+ u_longlong_t size, used, fsid, fileid;
+
+ fltype = getxdr_u_long();
+ mode = getxdr_u_long();
+ nlinks = getxdr_u_long();
+ uid = getxdr_u_long();
+ gid = getxdr_u_long();
+ size = getxdr_u_longlong();
+ used = getxdr_u_longlong();
+ major = getxdr_u_long();
+ minor = getxdr_u_long();
+ fsid = getxdr_u_longlong();
+ fileid = getxdr_u_longlong();
+
+ (void) sprintf(get_line(0, 0),
+ " File type = %d (%s)",
+ fltype, filetype(fltype));
+ detail_mode(mode);
+ (void) sprintf(get_line(0, 0),
+ " Link count = %u, User ID = %u, Group ID = %u",
+ nlinks, uid, gid);
+ (void) sprintf(get_line(0, 0),
+ " File size = %llu, Used = %llu",
+ size, used);
+ (void) sprintf(get_line(0, 0),
+ " Special: Major = %u, Minor = %u",
+ major, minor);
+ (void) sprintf(get_line(0, 0),
+ " File system id = %llu, File id = %llu",
+ fsid, fileid);
+ (void) showxdr_date_ns(" Last access time = %s");
+ (void) showxdr_date_ns(" Modification time = %s");
+ (void) showxdr_date_ns(" Attribute change time = %s");
+ (void) show_line("");
+}
+
+static void
+detail_sattr3()
+{
+ int t;
+
+ if (getxdr_bool())
+ detail_mode(getxdr_u_long());
+ else
+ (void) sprintf(get_line(0, 0), "Mode = (not set)");
+ if (getxdr_bool())
+ (void) showxdr_long("User ID = %d");
+ else
+ (void) sprintf(get_line(0, 0), "User ID = (not set)");
+ if (getxdr_bool())
+ (void) showxdr_long("Group ID = %d");
+ else
+ (void) sprintf(get_line(0, 0), "Group ID = (not set)");
+ if (getxdr_bool())
+ (void) showxdr_u_longlong("Size = %llu");
+ else
+ (void) sprintf(get_line(0, 0), "Size = (not set)");
+
+ if ((t = getxdr_u_long()) == SET_TO_CLIENT_TIME)
+ (void) showxdr_date("Access time = %s (set to client time)");
+ else if (t == SET_TO_SERVER_TIME)
+ (void) sprintf(get_line(0, 0),
+ "Access time = (set to server time)");
+ else
+ (void) sprintf(get_line(0, 0), "Access time = (do not set)");
+
+ if ((t = getxdr_u_long()) == SET_TO_CLIENT_TIME) {
+ (void) showxdr_date(
+ "Modification time = %s (set to client time)");
+ } else if (t == SET_TO_SERVER_TIME)
+ (void) sprintf(get_line(0, 0),
+ "Modification time = (set to server time)");
+ else
+ (void) sprintf(get_line(0, 0),
+ "Modification time = (do not set)");
+ (void) show_line("");
+}
+
+static char *
+filetype(n)
+ int n;
+{
+
+ switch (n) {
+ case NF3REG:
+ return ("Regular File");
+ case NF3DIR:
+ return ("Directory");
+ case NF3BLK:
+ return ("Block special");
+ case NF3CHR:
+ return ("Character special");
+ case NF3LNK:
+ return ("Symbolic Link");
+ case NF3SOCK:
+ return ("Unix domain socket");
+ case NF3FIFO:
+ return ("Named pipe");
+ default:
+ return ("?");
+ }
+ /* NOTREACHED */
+}
+
+static char *
+perms(n)
+ int n;
+{
+ static char buff[4];
+
+ buff[0] = n & 4 ? 'r' : '-';
+ buff[1] = n & 2 ? 'w' : '-';
+ buff[2] = n & 1 ? 'x' : '-';
+ buff[3] = '\0';
+ return (buff);
+}
+
+static void
+detail_wcc_attr()
+{
+
+ (void) showxdr_u_longlong(" Size = %llu bytes");
+ (void) showxdr_date_ns(" Modification time = %s");
+ (void) showxdr_date_ns(" Attribute change time = %s");
+ (void) show_line("");
+}
+
+static void
+detail_pre_op_attr(str)
+ char *str;
+{
+
+ if (getxdr_bool()) {
+ (void) sprintf(get_line(0, 0),
+ "Pre-operation attributes: %s", str);
+ detail_wcc_attr();
+ } else
+ (void) sprintf(get_line(0, 0),
+ "Pre-operation attributes: %s (not available)", str);
+}
+
+void
+detail_post_op_attr(str)
+ char *str;
+{
+
+ if (getxdr_bool()) {
+ (void) sprintf(get_line(0, 0),
+ "Post-operation attributes: %s", str);
+ detail_fattr3();
+ } else
+ (void) sprintf(get_line(0, 0),
+ "Post-operation attributes: %s (not available)", str);
+}
+
+static void
+detail_wcc_data(str)
+ char *str;
+{
+
+ detail_pre_op_attr(str);
+ detail_post_op_attr(str);
+}
+
+static char *
+sum_readdirres()
+{
+ static char buff[NFS_MAXNAMLEN + 1]; /* protocol allows longer names */
+ static int entries;
+
+ entries = 0;
+ if (setjmp(xdr_err)) {
+ (void) sprintf(buff, " %d+ entries (incomplete)", entries);
+ return (buff);
+ }
+ skip_postop();
+ xdr_skip(8); /* cookieverf */
+ while (getxdr_bool()) {
+ entries++;
+ xdr_skip(8); /* fileid */
+ (void) getxdr_string(buff, NFS_MAXNAMLEN); /* name */
+ xdr_skip(8); /* cookie */
+ }
+
+ (void) sprintf(buff, " %d entries (%s)",
+ entries, getxdr_bool() ? "No more" : "More");
+ return (buff);
+}
+
+static char *
+sum_readdirplusres()
+{
+ static char buff[NFS_MAXNAMLEN + 1]; /* protocol allows longer */
+ static int entries;
+ int skip;
+
+ entries = 0;
+ if (setjmp(xdr_err)) {
+ (void) sprintf(buff, " %d+ entries (incomplete)", entries);
+ return (buff);
+ }
+ skip_postop();
+ xdr_skip(8); /* cookieverf */
+ while (getxdr_bool()) {
+ entries++;
+ xdr_skip(8); /* fileid */
+ (void) getxdr_string(buff, NFS_MAXNAMLEN); /* name */
+ xdr_skip(8); /* cookie */
+ skip_postop(); /* post-op */
+ if (getxdr_bool()) {
+ skip = getxdr_long();
+ xdr_skip(RNDUP(skip)); /* fhandle */
+ }
+ }
+
+ (void) sprintf(buff, " %d entries (%s)",
+ entries, getxdr_bool() ? "No more" : "More");
+ return (buff);
+}
+
+static void
+detail_readdirres()
+{
+ static int entries;
+ u_longlong_t fileid, cookie;
+ char *name;
+ char buff[NFS_MAXNAMLEN + 1]; /* protocol allows longer names */
+
+ entries = 0;
+ detail_post_op_attr("");
+ (void) showxdr_hex(8, "Cookie verifier = %s");
+ (void) show_line("");
+ (void) sprintf(get_line(0, 0), " File id Cookie Name");
+
+ if (setjmp(xdr_err)) {
+ (void) sprintf(get_line(0, 0),
+ " %d+ entries. (Frame is incomplete)",
+ entries);
+ return;
+ }
+ while (getxdr_bool()) {
+ entries++;
+ fileid = getxdr_u_longlong();
+ name = (char *)getxdr_string(buff, NFS_MAXNAMLEN);
+ cookie = getxdr_u_longlong();
+ (void) sprintf(get_line(0, 0),
+ " %10llu %10llu %s",
+ fileid, cookie, name);
+ }
+
+ (void) sprintf(get_line(0, 0), " %d entries", entries);
+ (void) showxdr_bool("EOF = %s");
+}
+
+static void
+detail_readdirplusres()
+{
+ static int entries;
+
+ entries = 0;
+ detail_post_op_attr("");
+ (void) showxdr_hex(8, "Cookie verifier = %s");
+ (void) show_line("");
+
+ if (setjmp(xdr_err)) {
+ (void) sprintf(get_line(0, 0),
+ " %d+ entries. (Frame is incomplete)",
+ entries);
+ return;
+ }
+ while (getxdr_bool()) {
+ entries++;
+ (void) sprintf(get_line(0, 0),
+ "------------------ entry #%d",
+ entries);
+ (void) showxdr_u_longlong("File ID = %llu");
+ (void) showxdr_string(NFS_MAXNAMLEN, "Name = %s");
+ (void) showxdr_u_longlong("Cookie = %llu");
+ detail_post_op_attr("");
+ if (getxdr_bool())
+ detail_nfsfh3();
+ else
+ (void) sprintf(get_line(0, 0),
+ "(No file handle available)");
+ }
+
+ (void) show_line("");
+ (void) sprintf(get_line(0, 0), " %d entries", entries);
+ (void) showxdr_bool("EOF = %s");
+}
+
+static char *
+sum_createhow()
+{
+ long how;
+
+ how = getxdr_long();
+ switch (how) {
+ case UNCHECKED:
+ return ("UNCHECKED");
+ case GUARDED:
+ return ("GUARDED");
+ case EXCLUSIVE:
+ return ("EXCLUSIVE");
+ default:
+ return ("?");
+ }
+ /* NOTREACHED */
+}
+
+static char *
+sum_stablehow()
+{
+ long stable;
+
+ stable = getxdr_long();
+ switch (stable) {
+ case UNSTABLE:
+ return ("ASYNC");
+ case DATA_SYNC:
+ return ("DSYNC");
+ case FILE_SYNC:
+ return ("FSYNC");
+ default:
+ return ("?");
+ }
+ /* NOTREACHED */
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nfs4.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nfs4.c
new file mode 100644
index 0000000000..dc8ce89455
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nfs4.c
@@ -0,0 +1,4889 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+
+#include <ctype.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/tiuser.h>
+#include <setjmp.h>
+
+#include <rpc/types.h>
+#include <rpc/xdr.h>
+#include <rpc/auth.h>
+#include <rpc/clnt.h>
+#include <rpc/rpc_msg.h>
+#include "snoop.h"
+
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <rpcsvc/nfs_prot.h>
+/* use the same nfs4_prot.h as the xdr code */
+#include "rpcsvc/nfs4_prot.h"
+
+/*
+ * XXX With NFS v2 and v3, we only need to xdr the pieces that we care
+ * about. Anything else we can ignore and just skip to the next packet.
+ * So all the stuff that deals directly with XDR lives in snoop_display.c
+ * With v4, we need to XDR entire structures so that we can skip over
+ * uninteresting bits in a compound array, so we call XDR directly from
+ * here. We need to rethink how we're going to structure XDR access. Do
+ * we continue to hide it all in snoop_display.c, or do we expose it to all
+ * the protocol modules?
+ */
+extern XDR xdrm;
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+/*
+ * Maximum number of characters to display in compound4 summary line.
+ */
+#define SUM_COMPND_MAX 100
+
+/*
+ * Maximum number of recognized attributes.
+ */
+#define MAX_ATTRIBUTES 56
+
+/*
+ * This data structure provides a more convenient way to access an
+ * attribute bitmask. map[N] = value of bit N in a bitmap4.
+ * It's defined as a struct so as to step around all the weird rules in C
+ * about arrays, pointers, passing them as arguments, etc.
+ */
+
+typedef struct {
+ char map[MAX_ATTRIBUTES];
+} unpkd_attrmap_t;
+
+
+static void sumarg_cb_getattr(char *buf, size_t buflen, void *obj);
+static void dtlarg_cb_getattr(void *obj);
+static void sumarg_cb_recall(char *buf, size_t buflen, void *obj);
+static void dtlarg_cb_recall(void *obj);
+
+
+static void sumarg_access(char *buf, size_t buflen, void *obj);
+static void dtlarg_access(void *obj);
+static void sumarg_close(char *buf, size_t buflen, void *obj);
+static void dtlarg_close(void *obj);
+static void sumarg_commit(char *buf, size_t buflen, void *obj);
+static void dtlarg_commit(void *obj);
+static void sumarg_compnt(char *buf, size_t buflen, void *obj);
+static void dtlarg_compnt(void *obj);
+static void sumarg_create(char *buf, size_t buflen, void *obj);
+static void dtlarg_create(void *obj);
+static void sumarg_delprge(char *buf, size_t buflen, void *obj);
+static void dtlarg_delprge(void *obj);
+static void sumarg_delret(char *buf, size_t buflen, void *obj);
+static void dtlarg_delret(void *obj);
+static void sumarg_getattr(char *buf, size_t buflen, void *obj);
+static void dtlarg_getattr(void *obj);
+static void sumarg_link(char *buf, size_t buflen, void *obj);
+static void dtlarg_link(void *obj);
+static void sum_open_to_lock_owner(char *buf, int buflen,
+ open_to_lock_owner4 *own);
+static void sum_exist_lock_owner(char *buf, int buflen,
+ exist_lock_owner4 *own);
+static void sum_locker(char *buf, size_t buflen, locker4 *lk);
+static void sumarg_lock(char *buf, size_t buflen, void *obj);
+static void detail_open_to_lock_owner(open_to_lock_owner4 *own);
+static void detail_exist_lock_owner(exist_lock_owner4 *own);
+static void detail_locker(locker4 *lk);
+static void dtlarg_lock(void *obj);
+static void sumarg_lockt(char *buf, size_t buflen, void *obj);
+static void dtlarg_lockt(void *obj);
+static void sumarg_locku(char *buf, size_t buflen, void *obj);
+static void dtlarg_locku(void *obj);
+static void sumarg_lookup(char *buf, size_t buflen, void *obj);
+static void dtlarg_lookup(void *obj);
+static void sumarg_open(char *buf, size_t buflen, void *obj);
+static void dtlarg_open(void *obj);
+static void sumarg_openattr(char *buf, size_t buflen, void *obj);
+static void dtlarg_openattr(void *obj);
+static void sumarg_open_confirm(char *buf, size_t buflen, void *obj);
+static void dtlarg_open_confirm(void *obj);
+static void sumarg_open_downgrd(char *buf, size_t buflen, void *obj);
+static void dtlarg_open_downgrd(void *obj);
+static void sumarg_putfh(char *buf, size_t buflen, void *obj);
+static void dtlarg_putfh(void *obj);
+static void sumarg_read(char *buf, size_t buflen, void *obj);
+static void dtlarg_read(void *obj);
+static void sumarg_readdir(char *buf, size_t buflen, void *obj);
+static void dtlarg_readdir(void *obj);
+static void sumarg_release_lkown(char *buf, size_t buflen, void *obj);
+static void dtlarg_release_lkown(void *obj);
+static void sumarg_rename(char *buf, size_t buflen, void *obj);
+static void dtlarg_rename(void *obj);
+static void sumarg_renew(char *buf, size_t buflen, void *obj);
+static void dtlarg_renew(void *buf);
+static void sumarg_secinfo(char *buf, size_t buflen, void *obj);
+static void dtlarg_secinfo(void *obj);
+static void sumarg_setattr(char *buf, size_t buflen, void *obj);
+static void dtlarg_setattr(void *obj);
+static void sumarg_setclid(char *buf, size_t buflen, void *obj);
+static void dtlarg_setclid(void *obj);
+static void sumarg_setclid_cfm(char *buf, size_t buflen, void *obj);
+static void dtlarg_setclid_cfm(void *obj);
+static void dtlarg_verify(void *obj);
+static void sumarg_write(char *buf, size_t buflen, void *obj);
+static void dtlarg_write(void *obj);
+
+static void sumres_cb_getattr(char *buf, size_t buflen, void *obj);
+static void dtlres_cb_getattr(void *obj);
+
+static void sumres_access(char *buf, size_t buflen, void *obj);
+static void dtlres_access(void *obj);
+static void sumres_close(char *buf, size_t buflen, void *obj);
+static void dtlres_close(void *obj);
+static void sumres_commit(char *buf, size_t buflen, void *obj);
+static void dtlres_commit(void *obj);
+static void dtlres_create(void *obj);
+static void sumres_getattr(char *buf, size_t buflen, void *obj);
+static void dtlres_getattr(void *obj);
+static void sumres_getfh(char *buf, size_t buflen, void *obj);
+static void dtlres_getfh(void *obj);
+static void dtlres_link(void *obj);
+static void sumres_lock(char *buf, size_t buflen, void *obj);
+static void dtlres_lock(void *obj);
+static void sumres_lockt(char *buf, size_t buflen, void *obj);
+static void dtlres_lockt(void *obj);
+static void sumres_locku(char *buf, size_t buflen, void *obj);
+static void dtlres_locku(void *obj);
+static void sumres_open(char *buf, size_t buflen, void *obj);
+static void dtlres_open(void *obj);
+static void sumres_open_confirm(char *buf, size_t buflen, void *obj);
+static void dtlres_open_confirm(void *obj);
+static void sumres_open_downgrd(char *buf, size_t buflen, void *obj);
+static void dtlres_open_downgrd(void *obj);
+static void sumres_read(char *buf, size_t buflen, void *obj);
+static void dtlres_read(void *obj);
+static void sumres_readdir(char *buf, size_t buflen, void *obj);
+static void dtlres_readdir(void *obj);
+static void sumres_readlnk(char *buf, size_t buflen, void *obj);
+static void dtlres_readlnk(void *obj);
+static void dtlres_remove(void *obj);
+static void dtlres_rename(void *obj);
+static void sumres_secinfo(char *buf, size_t buflen, void *obj);
+static void dtlres_secinfo(void *obj);
+static void sumres_setattr(char *buf, size_t buflen, void *obj);
+static void dtlres_setattr(void *obj);
+static void sumres_setclid(char *buf, size_t buflen, void *obj);
+static void dtlres_setclid(void *obj);
+static void sumres_write(char *buf, size_t buflen, void *obj);
+static void dtlres_write(void *obj);
+static void sum_nfsstat4(char *buf, size_t buflen, void *obj);
+static void dtl_nfsstat4(void *obj);
+static uint32_t adler16(void *, int);
+static void nfs4_xdr_skip(int nbytes);
+static char *sum_lock_type_name(enum nfs_lock_type4 type);
+
+int nfs4_pkt_start;
+int nfs4_pkt_len;
+int nfs4_skip_bytes;
+int nfs4_fragged_rpc;
+char *nfs4err_fragrpc = "<Fragmented RPC>";
+char *nfs4err_xdrfrag = "<XDR Error or Fragmented RPC>";
+
+/*
+ * need a way to enable this if current testcases are parsing snoop
+ * error text. -- maybe an env var would do as temp workaround until
+ * testcases changed to grep for new error text.
+ */
+int nfs4_use_old_error_text = 0;
+
+/*
+ * Information about each operation that can appear in a compound call.
+ * The function pointers are to formatting functions for summary arguments
+ * and results, and detail arguments & results.
+ */
+
+typedef struct {
+ char *name;
+ void (*sumarg)(char *, size_t, void *);
+ void (*sumres)(char *, size_t, void *);
+ void (*dtlarg)(void *);
+ void (*dtlres)(void *);
+} op_info_t;
+
+static op_info_t cb_opcode_info[] = {
+ {"OP_ZERO", NULL, NULL, NULL, NULL}, /* 0 */
+ {"OP_ONE", NULL, NULL, NULL, NULL},
+ {"OP_TWO", NULL, NULL, NULL, NULL}, /* minor vers */
+ {"CB_GETATTR",
+ sumarg_cb_getattr, sumres_cb_getattr,
+ dtlarg_cb_getattr, dtlres_cb_getattr},
+ {"CB_RECALL",
+ sumarg_cb_recall, sum_nfsstat4,
+ dtlarg_cb_recall, dtl_nfsstat4},
+};
+static uint_t cb_num_opcodes = sizeof (cb_opcode_info) / sizeof (op_info_t *);
+
+static op_info_t opcode_info[] = {
+ {"OP_ZERO", NULL, NULL, NULL, NULL}, /* 0 */
+ {"OP_ONE", NULL, NULL, NULL, NULL},
+ {"OP_TWO", NULL, NULL, NULL, NULL}, /* minor vers */
+ {"ACCESS",
+ sumarg_access, sumres_access, dtlarg_access, dtlres_access},
+ {"CLOSE",
+ sumarg_close, sumres_close, dtlarg_close, dtlres_close},
+ {"COMMIT",
+ sumarg_commit, sumres_commit, dtlarg_commit, dtlres_commit},
+ {"CREATE", /* 5 */
+ sumarg_create, sum_nfsstat4, dtlarg_create, dtlres_create},
+ {"DELEGPURGE",
+ sumarg_delprge, sum_nfsstat4, dtlarg_delprge, dtl_nfsstat4},
+ {"DELEGRETURN",
+ sumarg_delret, sum_nfsstat4, dtlarg_delret, dtl_nfsstat4},
+ {"GETATTR",
+ sumarg_getattr, sumres_getattr, dtlarg_getattr, dtlres_getattr},
+ {"GETFH",
+ NULL, sumres_getfh, NULL, dtlres_getfh},
+ {"LINK", /* 10 */
+ sumarg_link, sum_nfsstat4, dtlarg_link, dtlres_link},
+ {"LOCK",
+ sumarg_lock, sumres_lock, dtlarg_lock, dtlres_lock},
+ {"LOCKT",
+ sumarg_lockt, sumres_lockt, dtlarg_lockt, dtlres_lockt},
+ {"LOCKU",
+ sumarg_locku, sumres_locku, dtlarg_locku, dtlres_locku},
+ {"LOOKUP",
+ sumarg_lookup, sum_nfsstat4, dtlarg_lookup, dtl_nfsstat4},
+ {"LOOKUPP", /* 15 */
+ NULL, sum_nfsstat4, NULL, dtl_nfsstat4},
+ {"NVERIFY",
+ NULL, sum_nfsstat4, dtlarg_verify, dtl_nfsstat4},
+ {"OPEN",
+ sumarg_open, sumres_open, dtlarg_open, dtlres_open},
+ {"OPENATTR",
+ sumarg_openattr, sum_nfsstat4, dtlarg_openattr, dtl_nfsstat4},
+ {"OPEN_CONFIRM",
+ sumarg_open_confirm,
+ sumres_open_confirm,
+ dtlarg_open_confirm,
+ dtlres_open_confirm},
+ {"OPEN_DOWNGRADE",
+ sumarg_open_downgrd,
+ sumres_open_downgrd,
+ dtlarg_open_downgrd,
+ dtlres_open_downgrd},
+ {"PUTFH",
+ sumarg_putfh, sum_nfsstat4, dtlarg_putfh, dtl_nfsstat4},
+ {"PUTPUBFH", /* 20 */
+ NULL, sum_nfsstat4, NULL, dtl_nfsstat4},
+ {"PUTROOTFH",
+ NULL, sum_nfsstat4, NULL, dtl_nfsstat4},
+ {"READ",
+ sumarg_read, sumres_read, dtlarg_read, dtlres_read},
+ {"READDIR",
+ sumarg_readdir, sumres_readdir, dtlarg_readdir, dtlres_readdir},
+ {"READLINK",
+ NULL, sumres_readlnk, NULL, dtlres_readlnk},
+ {"REMOVE", /* 25 */
+ sumarg_compnt, sum_nfsstat4, dtlarg_compnt, dtlres_remove},
+ {"RENAME",
+ sumarg_rename, sum_nfsstat4, dtlarg_rename, dtlres_rename},
+ {"RENEW",
+ sumarg_renew, sum_nfsstat4, dtlarg_renew, dtl_nfsstat4},
+ {"RESTOREFH",
+ NULL, sum_nfsstat4, NULL, dtl_nfsstat4},
+ {"SAVEFH",
+ NULL, sum_nfsstat4, NULL, dtl_nfsstat4},
+ {"SECINFO", /* 30 */
+ sumarg_secinfo, sumres_secinfo, dtlarg_secinfo, dtlres_secinfo},
+ {"SETATTR",
+ sumarg_setattr, sumres_setattr, dtlarg_setattr, dtlres_setattr},
+ {"SETCLIENTID",
+ sumarg_setclid, sumres_setclid, dtlarg_setclid, dtlres_setclid},
+ {"SETCLIENTID_CONFIRM",
+ sumarg_setclid_cfm,
+ sum_nfsstat4,
+ dtlarg_setclid_cfm,
+ dtl_nfsstat4},
+ {"VERIFY",
+ NULL, sum_nfsstat4, dtlarg_verify, dtl_nfsstat4},
+ {"WRITE",
+ sumarg_write, sumres_write, dtlarg_write, dtlres_write},
+ {"RELEASE_LOCKOWNER",
+ sumarg_release_lkown, sum_nfsstat4,
+ dtlarg_release_lkown, dtl_nfsstat4},
+};
+static uint_t num_opcodes = sizeof (opcode_info) / sizeof (op_info_t *);
+
+/*
+ * File types.
+ */
+
+typedef struct {
+ char *short_name; /* for summary output */
+ char *long_name; /* for detail output */
+} ftype_names_t;
+
+static ftype_names_t ftype_names[] = {
+ {"Type 0", "Type 0"},
+ {"REG", "Regular File"},
+ {"DIR", "Directory"},
+ {"BLK", "Block Device"},
+ {"CHR", "Character Device"},
+ {"LNK", "Symbolic Link"}, /* 5 */
+ {"SOCK", "Socket"},
+ {"FIFO", "FIFO"},
+ {"ATTRDIR", "Attribute Directory"},
+ {"NAMEDATTR", "Named Attribute"},
+};
+static uint_t num_ftypes = sizeof (ftype_names) / sizeof (ftype_names_t);
+
+static ftype_names_t open_rflags[] = {
+ {"?", "UNKNOWN"}, /* 0 */
+ {"CF", "CONFIRM"}, /* 1 */
+ {"PL", "POSIX LOCK"}, /* 2 */
+ {"?", "UNKNOWN"},
+};
+static uint_t num_open_rflags =
+ sizeof (open_rflags) / sizeof (ftype_names_t) - 1;
+
+static char *get_flags(uint_t, ftype_names_t *, uint_t, int, char *);
+
+#define sum_open_rflags(flag) \
+ get_flags((flag), open_rflags, num_open_rflags, 1, " RF=")
+
+#define detail_open_rflags(flag) \
+ get_flags((flag), open_rflags, num_open_rflags, 0, NULL)
+
+static void prt_supported_attrs(XDR *);
+static void prt_type(XDR *);
+static void prt_fh_expire_type(XDR *);
+static void prt_change(XDR *);
+static void prt_size(XDR *);
+static void prt_link_support(XDR *);
+static void prt_symlink_support(XDR *);
+static void prt_named_attr(XDR *);
+static void prt_fsid(XDR *);
+static void prt_unique_handles(XDR *);
+static void prt_lease_time(XDR *);
+static void prt_rdattr_error(XDR *);
+static void prt_acl(XDR *);
+static void prt_aclsupport(XDR *);
+static void prt_archive(XDR *);
+static void prt_cansettime(XDR *);
+static void prt_case_insensitive(XDR *);
+static void prt_case_preserving(XDR *);
+static void prt_chown_restricted(XDR *);
+static void prt_filehandle(XDR *);
+static void prt_fileid(XDR *);
+static void prt_mounted_on_fileid(XDR *);
+static void prt_files_avail(XDR *);
+static void prt_files_free(XDR *);
+static void prt_files_total(XDR *);
+static void prt_fs_locations(XDR *);
+static void prt_hidden(XDR *);
+static void prt_homogeneous(XDR *);
+static void prt_maxfilesize(XDR *);
+static void prt_maxlink(XDR *);
+static void prt_maxname(XDR *);
+static void prt_maxread(XDR *);
+static void prt_maxwrite(XDR *);
+static void prt_mimetype(XDR *);
+static void prt_mode(XDR *);
+static void prt_no_trunc(XDR *);
+static void prt_numlinks(XDR *);
+static void prt_owner(XDR *);
+static void prt_owner_group(XDR *);
+static void prt_quota_avail_hard(XDR *);
+static void prt_quota_avail_soft(XDR *);
+static void prt_quota_used(XDR *);
+static void prt_rawdev(XDR *);
+static void prt_space_avail(XDR *);
+static void prt_space_free(XDR *);
+static void prt_space_total(XDR *);
+static void prt_space_used(XDR *);
+static void prt_system(XDR *);
+static void prt_time_access(XDR *);
+static void prt_time_access_set(XDR *);
+static void prt_time_backup(XDR *);
+static void prt_time_create(XDR *);
+static void prt_time_delta(XDR *);
+static void prt_time_metadata(XDR *);
+static void prt_time_modify(XDR *);
+static void prt_time_modify_set(XDR *);
+
+
+
+/*
+ * Information for attributes.
+ * name name of the attribute.
+ * prt_details function to XDR decode the attribute and print it.
+ *
+ * XXX If this table ever gets extensively changed (including
+ * reorganization to track changes to the spec), it would probably be a
+ * good idea to change to a scheme where the table is mechanically
+ * generated. Look at $SRC/uts/common/rpcsvc for how this is done in the
+ * kernel.
+ */
+
+typedef struct {
+ char *name;
+ void (*prt_details)(XDR *);
+} attr_info_t;
+
+static attr_info_t attr_info[MAX_ATTRIBUTES] = {
+ {"SUPPORTED_ATTRS", prt_supported_attrs},
+ {"TYPE", prt_type},
+ {"FH_EXPIRE_TYPE", prt_fh_expire_type},
+ {"CHANGE", prt_change},
+ {"SIZE", prt_size},
+ {"LINK_SUPPORT", prt_link_support}, /* 5 */
+ {"SYMLINK_SUPPORT", prt_symlink_support},
+ {"NAMED_ATTR", prt_named_attr},
+ {"FSID", prt_fsid},
+ {"UNIQUE_HANDLES", prt_unique_handles},
+ {"LEASE_TIME", prt_lease_time}, /* 10 */
+ {"RDATTR_ERROR", prt_rdattr_error},
+ {"ACL", prt_acl},
+ {"ACLSUPPORT", prt_aclsupport},
+ {"ARCHIVE", prt_archive},
+ {"CANSETTIME", prt_cansettime}, /* 15 */
+ {"CASE_INSENSITIVE", prt_case_insensitive},
+ {"CASE_PRESERVING", prt_case_preserving},
+ {"CHOWN_RESTRICTED", prt_chown_restricted},
+ {"FILEHANDLE", prt_filehandle},
+ {"FILEID", prt_fileid}, /* 20 */
+ {"FILES_AVAIL", prt_files_avail},
+ {"FILES_FREE", prt_files_free},
+ {"FILES_TOTAL", prt_files_total},
+ {"FS_LOCATIONS", prt_fs_locations},
+ {"HIDDEN", prt_hidden}, /* 25 */
+ {"HOMOGENEOUS", prt_homogeneous},
+ {"MAXFILESIZE", prt_maxfilesize},
+ {"MAXLINK", prt_maxlink},
+ {"MAXNAME", prt_maxname},
+ {"MAXREAD", prt_maxread}, /* 30 */
+ {"MAXWRITE", prt_maxwrite},
+ {"MIMETYPE", prt_mimetype},
+ {"MODE", prt_mode},
+ {"NO_TRUNC", prt_no_trunc},
+ {"NUMLINKS", prt_numlinks}, /* 35 */
+ {"OWNER", prt_owner},
+ {"OWNER_GROUP", prt_owner_group},
+ {"QUOTA_AVAIL_HARD", prt_quota_avail_hard},
+ {"QUOTA_AVAIL_SOFT", prt_quota_avail_soft},
+ {"QUOTA_USED", prt_quota_used}, /* 40 */
+ {"RAWDEV", prt_rawdev},
+ {"SPACE_AVAIL", prt_space_avail},
+ {"SPACE_FREE", prt_space_free},
+ {"SPACE_TOTAL", prt_space_total},
+ {"SPACE_USED", prt_space_used}, /* 45 */
+ {"SYSTEM", prt_system},
+ {"TIME_ACCESS", prt_time_access},
+ {"TIME_ACCESS_SET", prt_time_access_set},
+ {"TIME_BACKUP", prt_time_backup},
+ {"TIME_CREATE", prt_time_create}, /* 50 */
+ {"TIME_DELTA", prt_time_delta},
+ {"TIME_METADATA", prt_time_metadata},
+ {"TIME_MODIFY", prt_time_modify},
+ {"TIME_MODIFY_SET", prt_time_modify_set},
+ {"MOUNTED_ON_FILEID", prt_mounted_on_fileid},
+};
+
+extern char *get_sum_line();
+
+extern jmp_buf xdr_err;
+
+static void sum_comp4res(char *, char *(*)(void));
+static char *sum_compound4args(void);
+static char *sum_compound4res(void);
+static char *sum_operand(nfs_argop4 *opp);
+static char *sum_result(nfs_resop4 *resp);
+
+static char *sum_cb_compound4args(void);
+static char *sum_cb_compound4res(void);
+static char *sum_cb_operand(nfs_cb_argop4 *opp);
+static char *sum_cb_result(nfs_cb_resop4 *resp);
+
+static void detail_acetype4(acetype4);
+static void detail_uint32_bitmap(uint32_t, char *[], int);
+static void detail_aceflag4(aceflag4);
+static void detail_acemask4(acemask4);
+static void detail_nfs_argop4(void);
+static void detail_nfs_resop4(void);
+static void detail_cb_argop4(void);
+static void detail_cb_resop4(void);
+
+static char *attr_name(uint_t);
+static char *claim_name(enum open_claim_type4 claim_type);
+static char *delegation_type_name(enum open_delegation_type4 type);
+static char *flavor_name(uint_t flavor);
+static char *gss_svc_name(rpc_gss_svc_t svc);
+static char *limitby_name(enum limit_by4 limitby);
+static char *lock_type_name(enum nfs_lock_type4);
+static char *opcode_name(uint_t);
+static char *cb_opcode_name(uint_t opnum);
+static char *status_name(int);
+static char *status_name_compat(int);
+static char *status_name_pcol(int);
+static char *sum_type_name(nfs_ftype4);
+static void sum_access4(char *buf, size_t buflen, uint32_t bits);
+static void detail_access4(char *, uint32_t);
+static void sum_claim(char *buf, size_t buflen, open_claim4 *claim);
+static void detail_claim(open_claim4 *claim);
+static char *sum_clientid(clientid4 client);
+static void detail_clientid(clientid4 client);
+static char *_sum_stateid(stateid4 *, char *prefix);
+static void sum_delegation(char *buf, size_t buflen, open_delegation4 *delp);
+static void detail_delegation(open_delegation4 *delp);
+static void detail_lock_owner(lock_owner4 *owner);
+static void detail_open_owner(open_owner4 *owner);
+static void sum_openflag(char *bufp, int buflen, openflag4 *flagp);
+static char *get_deleg_typestr(open_delegation_type4 dt);
+static void detail_openflag(openflag4 *flagp);
+static void sum_name(char *buf, size_t buflen, open_claim4 *claim);
+static void detail_rpcsec_gss(rpcsec_gss_info *);
+static void detail_secinfo4(secinfo4 *infop);
+static char *sum_space_limit(nfs_space_limit4 *limitp);
+static void detail_space_limit(nfs_space_limit4 *limitp);
+static char *detail_type_name(nfs_ftype4);
+static char *createhow4_name(createhow4 *crtp);
+
+
+static void showxdr_utf8string(char *);
+static char *utf8localize(utf8string *);
+static void utf8free(void);
+static void sum_pathname4(char *, size_t, pathname4 *);
+static void detail_pathname4(pathname4 *pathp);
+static void sum_compname4(char *buf, size_t buflen, component4 *comp);
+static void detail_compname4(component4 *comp);
+
+static void detail_fattr4(fattr4 *attrp);
+static void detail_attr_bitmap(char *, bitmap4 *, unpkd_attrmap_t *);
+static void sum_attr_bitmap(char *buf, size_t buflen, bitmap4 *mapp);
+static void detail_fattr4_change(char *msg, fattr4_change chg);
+static char *sum_fh4(nfs_fh4 *fhp);
+static void detail_fh4(nfs_fh4 *fh);
+
+#define fh4_hash(fh) adler16((fh)->nfs_fh4_val, (fh)->nfs_fh4_len)
+#define stateid_hash(st) adler16((st)->other, sizeof ((st)->other))
+#define owner_hash(own) adler16((own)->owner_val, (own)->owner_len)
+
+#define sum_deleg_stateid(st) _sum_stateid((st), "DST=")
+#define sum_open_stateid(st) _sum_stateid((st), "OST=")
+#define sum_lock_stateid(st) _sum_stateid((st), "LST=")
+#define sum_stateid(st) _sum_stateid((st), "ST=")
+
+#define detail_deleg_stateid(st) _detail_stateid((st), "Delegation ")
+#define detail_open_stateid(st) _detail_stateid((st), "Open ")
+#define detail_lock_stateid(st) _detail_stateid((st), "Lock ")
+#define detail_stateid(st) _detail_stateid((st), "")
+
+#define SPECIAL_STATEID0 "SPC0"
+#define SPECIAL_STATEID1 "SPC1"
+
+#define DONT_CHANGE 0
+#define SET_TO_SERVER_TIME 1
+#define SET_TO_CLIENT_TIME 2
+
+static stateid4 spec_stateid_0 =
+ {0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
+static stateid4 spec_stateid_1 =
+ {0xFFFFFFFF, {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}};
+
+static char *procnames_short[] = {
+ "NULL4", /* 0 */
+ "COMPOUND4" /* 1 */
+};
+
+static char *procnames_long[] = {
+ "Null procedure", /* 0 */
+ "Compound", /* 1 */
+};
+
+static char *cb_procnames_short[] = {
+ "CB_NULL", /* 0 */
+ "CB_COMPOUND" /* 1 */
+};
+
+static char *cb_procnames_long[] = {
+ "Null CallBack procedure", /* 0 */
+ "CallBack compound", /* 1 */
+};
+
+static char *acetype4_names[] = {
+ "ACE4_ACCESS_ALLOWED_ACE_TYPE",
+ "ACE4_ACCESS_DENIED_ACE_TYPE",
+ "ACE4_SYSTEM_AUDIT_ACE_TYPE",
+ "ACE4_SYSTEM_ALARM_ACE_TYPE"
+};
+#define ACETYPE4_NAMES_MAX (sizeof (acetype4_names) / sizeof (char *))
+
+static char *aceflag4_names[] = {
+ "ACE4_FILE_INHERIT_ACE",
+ "ACE4_DIRECTORY_INHERIT_ACE",
+ "ACE4_NO_PROPAGATE_INHERIT_ACE",
+ "ACE4_INHERIT_ONLY_ACE",
+ "ACE4_SUCCESSFUL_ACCESS_ACE_FLAG",
+ "ACE4_FAILED_ACCESS_ACE_FLAG",
+ "ACE4_IDENTIFIER_GROUP"
+};
+#define ACEFLAG4_NAMES_MAX (sizeof (aceflag4_names) / sizeof (char *))
+
+static char *acemask4_names[] = {
+ "ACE4_READ_DATA/ACE4_LIST_DIRECTORY",
+ "ACE4_WRITE_DATA/ACE4_ADD_FILE",
+ "ACE4_APPEND_DATA/ACE4_ADD_SUBDIRECTORY",
+ "ACE4_READ_NAMED_ATTRS",
+ "ACE4_WRITE_NAMED_ATTRS",
+ "ACE4_EXECUTE",
+ "ACE4_DELETE_CHILD",
+ "ACE4_READ_ATTRIBUTES",
+ "ACE4_WRITE_ATTRIBUTES",
+ "UNDEFINED", /* 0x00000200 */
+ "UNDEFINED", /* 0x00000400 */
+ "UNDEFINED", /* 0x00000800 */
+ "UNDEFINED", /* 0x00001000 */
+ "UNDEFINED", /* 0x00002000 */
+ "UNDEFINED", /* 0x00004000 */
+ "UNDEFINED", /* 0x00008000 */
+ "ACE4_DELETE",
+ "ACE4_READ_ACL",
+ "ACE4_WRITE_ACL",
+ "ACE4_WRITE_OWNER",
+ "ACE4_SYNCHRONIZE"
+};
+#define ACEMASK4_NAMES_MAX (sizeof (acemask4_names) / sizeof (char *))
+
+#define MAXPROC 1
+
+/*ARGSUSED*/
+void
+interpret_nfs4_cb(int flags, int type, int xid, int vers, int proc,
+ char *data, int len)
+{
+ char *line = NULL;
+
+ if (proc < 0 || proc > MAXPROC)
+ return;
+
+ if (flags & F_SUM) {
+ line = get_sum_line();
+
+ if (type == CALL) {
+ (void) sprintf(line, "NFS C %s",
+ proc == CB_COMPOUND ? "CB4" :
+ cb_procnames_short[proc]);
+ line += strlen(line);
+
+ if (proc == CB_COMPOUND) {
+ static utf8string tag;
+
+ if (!xdr_utf8string(&xdrm, &tag))
+ longjmp(xdr_err, 1);
+ sprintf(line, " (%.20s) %s",
+ utf8localize(&tag),
+ sum_cb_compound4args());
+ xdr_free(xdr_utf8string, (char *)&tag);
+ }
+ check_retransmit(line, xid);
+ } else {
+ (void) sprintf(line, "NFS R %s ",
+ proc == CB_COMPOUND ? "CB4" :
+ cb_procnames_short[proc]);
+ line += strlen(line);
+ if (proc == CB_COMPOUND)
+ sum_comp4res(line, sum_cb_compound4res);
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("NFS: ", "Sun NFS4 CallBack", len);
+ show_space();
+ (void) sprintf(get_line(0, 0), "Proc = %d (%s)",
+ proc, cb_procnames_long[proc]);
+ if (proc == CB_COMPOUND) {
+ if (type == CALL) {
+ showxdr_utf8string("Tag = %s");
+ detail_cb_argop4();
+ } else {
+ nfsstat4 status;
+
+ status = getxdr_long();
+ showxdr_utf8string("Tag = %s");
+ sprintf(get_line(0, 0), "Status = %d (%s)",
+ status, status_name(status));
+ detail_cb_resop4();
+ }
+ }
+ show_trailer();
+ }
+
+ utf8free(); /* cf. utf8localize() */
+}
+
+
+/*ARGSUSED*/
+void
+interpret_nfs4(int flags, int type, int xid, int vers, int proc,
+ char *data, int len)
+{
+ char *line = NULL;
+
+ if (proc < 0 || proc > MAXPROC)
+ return;
+
+ nfs4_fragged_rpc = 0;
+ nfs4_pkt_len = len;
+ nfs4_pkt_start = xdr_getpos(&xdrm);
+
+ if (flags & F_SUM) {
+ line = get_sum_line();
+
+ if (type == CALL) {
+ (void) sprintf(line, "NFS C %s",
+ proc == NFSPROC4_COMPOUND ? "4" :
+ procnames_short[proc]);
+ line += strlen(line);
+
+ if (proc == NFSPROC4_COMPOUND) {
+ static utf8string tag;
+
+ if (!xdr_utf8string(&xdrm, &tag))
+ longjmp(xdr_err, 1);
+ sprintf(line, " (%.20s) %s",
+ utf8localize(&tag),
+ sum_compound4args());
+ xdr_free(xdr_utf8string, (char *)&tag);
+ }
+ check_retransmit(line, xid);
+ } else {
+ (void) sprintf(line, "NFS R %s ",
+ proc == NFSPROC4_COMPOUND ? "4" :
+ procnames_short[proc]);
+ line += strlen(line);
+
+ if (proc == NFSPROC4_COMPOUND)
+ sum_comp4res(line, sum_compound4res);
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("NFS: ", "Sun NFS", len);
+ show_space();
+ (void) sprintf(get_line(0, 0), "Proc = %d (%s)",
+ proc, procnames_long[proc]);
+ if (proc == NFSPROC4_COMPOUND) {
+ if (type == CALL) {
+ showxdr_utf8string("Tag = %s");
+ detail_nfs_argop4();
+ } else {
+ nfsstat4 status;
+
+ status = getxdr_long();
+ showxdr_utf8string("Tag = %s");
+ sprintf(get_line(0, 0), "Status = %d (%s)",
+ status, status_name(status));
+ detail_nfs_resop4();
+ }
+ }
+ show_trailer();
+ }
+
+ utf8free(); /* cf. utf8localize() */
+}
+
+
+
+/*
+ * Return the names and arguments of the oplist elements, up to
+ * SUM_COMPND_MAX characters. If the elements don't fit, include a "..."
+ * at the end of the string.
+ */
+
+static char *
+sum_compound4args(void)
+{
+ static char buf[SUM_COMPND_MAX + 2]; /* 1 for null, 1 for overflow */
+ int numops;
+ const size_t buflen = sizeof (buf);
+ char *bp;
+ nfs_argop4 one_op;
+ uint32_t minor_version;
+
+ buf[0] = '\0';
+
+ if (setjmp(xdr_err)) {
+ bp = buf + strlen(buf);
+ snprintf(bp, buflen - (bp - buf),
+ nfs4_fragged_rpc ? nfs4err_fragrpc : nfs4err_xdrfrag);
+ return (buf);
+ }
+
+ /*
+ * might be nice to print minor version, but doesn't
+ * seem like very useful info for summary mode
+ */
+ if (!xdr_uint32_t(&xdrm, &minor_version))
+ longjmp(xdr_err, 1);
+
+ numops = getxdr_long();
+ bp = buf;
+ while (numops-- > 0) {
+ char *operand;
+
+ bzero(&one_op, sizeof (one_op));
+
+ if (!xdr_nfs_argop4(&xdrm, &one_op)) {
+ xdr_free(xdr_nfs_argop4, (char *)&one_op);
+ longjmp(xdr_err, 1);
+ }
+ snprintf(bp, buflen - (bp - buf), "%s ",
+ opcode_name(one_op.argop));
+ bp += strlen(bp);
+
+ operand = sum_operand(&one_op);
+ if (strlen(operand) > 0) {
+ snprintf(bp, buflen - (bp - buf), "%s ", operand);
+ bp += strlen(bp);
+ }
+
+ /* nfs4_skip_bytes set by xdr_nfs4_argop4 */
+ if (nfs4_skip_bytes != 0)
+ nfs4_xdr_skip(nfs4_skip_bytes);
+
+ xdr_free(xdr_nfs_argop4, (char *)&one_op);
+
+ /* add "..." if past the "end" of the buffer */
+ if (bp - buf > SUM_COMPND_MAX) {
+ strcpy(buf + SUM_COMPND_MAX - strlen("..."),
+ "...");
+ break;
+ }
+ }
+
+ return (buf);
+}
+
+static void
+nfs4_xdr_skip(int nbytes)
+{
+ int resid, off, len, cur_pos, new_pos;
+
+ len = RNDUP(nbytes);
+ cur_pos = xdr_getpos(&xdrm);
+
+ /*
+ * Time to skip over the rd/wr data. If the
+ * rd/wr data is completely contained in the first
+ * frag, we must skip over it to process the rest of
+ * the packet.
+ *
+ * nfs4_pkt_start: XDR position of start of NFS4 compound
+ * nfs4_pkt_len: number of bytes in pkt relative to
+ * nfs4_pkt_start
+ *
+ * cur_pos: current XDR position
+ * off: current XDR position relative to nfs4_pkt_start
+ * resid: number of unprocessed bytes in current pkt
+ * (relative to cur_pos/off)
+ *
+ * If nbytes <= resid, then we must skip over the rd/wr
+ * bytes so we can read the next op/compound in this
+ * packet. Otherwise, set the fragged flag so we can
+ * display the fragged_rpc message.
+ */
+ off = cur_pos - nfs4_pkt_start;
+ resid = nfs4_pkt_len - off;
+
+ /*
+ * set nfs4_fragged_rpc if the requested number of "skip"
+ * bytes is larger than the bytes remaining in the XDR
+ * stream/current packet. The global is reset to 0 at
+ * start of interpret_nfs4.
+ */
+ new_pos = cur_pos + ((nfs4_fragged_rpc = len > resid) ? resid : len);
+
+ /* there's nothing to do for error case (if it fails pkt is doomed) */
+ xdr_setpos(&xdrm, new_pos);
+}
+
+
+/*
+ * Return the names and arguments of the oplist elements, up to
+ * SUM_COMPND_MAX characters. If the elements don't fit, include a "..."
+ * at the end of the string.
+ */
+static char *
+sum_cb_compound4args(void)
+{
+ static char buf[SUM_COMPND_MAX + 2]; /* 1 for null, 1 for overflow */
+ int numops;
+ const size_t buflen = sizeof (buf);
+ char *bp;
+ nfs_cb_argop4 one_op;
+ uint32_t minor_version, callback_ident;
+
+ buf[0] = '\0';
+ if (setjmp(xdr_err)) {
+ bp = buf + strlen(buf);
+ snprintf(bp, buflen - (bp - buf), "<XDR Error or Fragmented"
+ " RPC>");
+ return (buf);
+ }
+
+ /*
+ * might be nice to print minor version, but doesn't
+ * seem like very useful info for summary mode
+ */
+ if (!xdr_uint32_t(&xdrm, &minor_version))
+ longjmp(xdr_err, 1);
+
+ /* print callback_ident */
+ if (!xdr_uint32_t(&xdrm, &callback_ident))
+ longjmp(xdr_err, 1);
+ snprintf(buf, buflen, "CBID=%u ", callback_ident);
+
+ bp = buf + strlen(buf);
+ numops = getxdr_long();
+
+ while (numops-- > 0) {
+ char *operand;
+
+ bzero(&one_op, sizeof (one_op));
+ if (!xdr_nfs_cb_argop4(&xdrm, &one_op)) {
+ xdr_free(xdr_nfs_cb_argop4, (char *)&one_op);
+ longjmp(xdr_err, 1);
+ }
+
+ snprintf(bp, buflen - (bp - buf), "%s ",
+ cb_opcode_name(one_op.argop));
+ bp += strlen(bp);
+ operand = sum_cb_operand(&one_op);
+ if (strlen(operand) > 0) {
+ snprintf(bp, buflen - (bp - buf), "%s ", operand);
+ bp += strlen(bp);
+ }
+
+ xdr_free(xdr_nfs_cb_argop4, (char *)&one_op);
+
+ /* add "..." if past the "end" of the buffer */
+ if (bp - buf > SUM_COMPND_MAX) {
+ strcpy(buf + SUM_COMPND_MAX - strlen("..."),
+ "...");
+ break;
+ }
+ }
+
+ return (buf);
+}
+
+/*
+ * Return the summarized argument list for the given nfs_argop4.
+ */
+
+static char *
+sum_operand(nfs_argop4 *opp)
+{
+ static char buf[1024];
+ void (*fmtproc)(char *, size_t, void *);
+
+ buf[0] = '\0';
+ if (opp->argop < num_opcodes) {
+ fmtproc = opcode_info[opp->argop].sumarg;
+ if (fmtproc != NULL)
+ fmtproc(buf, sizeof (buf), &opp->nfs_argop4_u);
+ }
+
+ return (buf);
+}
+
+/*
+ * Return the summarized argument list for the given nfs_argop4.
+ */
+
+static char *
+sum_cb_operand(nfs_cb_argop4 *opp)
+{
+ static char buf[1024];
+ void (*fmtproc)(char *, size_t, void *);
+
+ buf[0] = '\0';
+ if (opp->argop < cb_num_opcodes) {
+ fmtproc = cb_opcode_info[opp->argop].sumarg;
+ if (fmtproc != NULL)
+ fmtproc(buf, sizeof (buf), &opp->nfs_cb_argop4_u);
+ }
+
+ return (buf);
+}
+
+/*
+ * Print details about the nfs_argop4 that is next in the XDR stream.
+ */
+
+static void
+detail_nfs_argop4(void)
+{
+ int numops;
+ nfs_argop4 one_op;
+ void (*fmtproc)(void *);
+ uint32_t minor_version;
+
+ if (!xdr_uint32_t(&xdrm, &minor_version))
+ longjmp(xdr_err, 1);
+
+ (void) sprintf(get_line(0, 0), "Minor version = %u",
+ minor_version);
+
+ numops = getxdr_long();
+ (void) sprintf(get_line(0, 0), "Number of operations = %d",
+ numops);
+
+ while (numops-- > 0) {
+ bzero(&one_op, sizeof (one_op));
+
+ if (!xdr_nfs_argop4(&xdrm, &one_op)) {
+ xdr_free(xdr_nfs_argop4, (char *)&one_op);
+ longjmp(xdr_err, 1);
+ }
+
+ get_line(0, 0); /* blank line to separate ops */
+ sprintf(get_line(0, 0), "Op = %d (%s)",
+ one_op.argop, opcode_name(one_op.argop));
+ if (one_op.argop < num_opcodes) {
+ fmtproc = opcode_info[one_op.argop].dtlarg;
+ if (fmtproc != NULL)
+ fmtproc(&one_op.nfs_argop4_u);
+ }
+
+ /* nfs4_skip_bytes set by xdr_nfs_argop4() */
+ if (nfs4_skip_bytes)
+ nfs4_xdr_skip(nfs4_skip_bytes);
+
+ xdr_free(xdr_nfs_argop4, (char *)&one_op);
+ }
+}
+
+
+/*
+ * Print details about the nfs_argop4 that is next in the XDR stream.
+ */
+static void
+detail_cb_argop4(void)
+{
+ int numops;
+ nfs_cb_argop4 one_op;
+ void (*fmtproc)(void *);
+ uint32_t minor_version, callback_ident;
+
+ if (!xdr_uint32_t(&xdrm, &minor_version))
+ longjmp(xdr_err, 1);
+ (void) sprintf(get_line(0, 0), "Minor version = %u",
+ minor_version);
+
+ if (!xdr_uint32_t(&xdrm, &callback_ident))
+ longjmp(xdr_err, 1);
+ (void) sprintf(get_line(0, 0), "Callback Ident = %u",
+ callback_ident);
+
+ numops = getxdr_long();
+ (void) sprintf(get_line(0, 0), "Number of operations = %d",
+ numops);
+
+ while (numops-- > 0) {
+ bzero(&one_op, sizeof (one_op));
+ if (!xdr_nfs_cb_argop4(&xdrm, &one_op)) {
+ xdr_free(xdr_nfs_cb_argop4, (char *)&one_op);
+ longjmp(xdr_err, 1);
+ }
+
+ get_line(0, 0); /* blank line to separate ops */
+ sprintf(get_line(0, 0), "Op = %d (%s)",
+ one_op.argop, cb_opcode_name(one_op.argop));
+ if (one_op.argop < cb_num_opcodes) {
+ fmtproc = cb_opcode_info[one_op.argop].dtlarg;
+ if (fmtproc != NULL)
+ fmtproc(&one_op.nfs_cb_argop4_u);
+ }
+
+ xdr_free(xdr_nfs_cb_argop4, (char *)&one_op);
+ }
+}
+
+/*
+ * component_name: return a printable string for the given component4. I'm
+ * leaving this as a separate function (as opposed to having the callers
+ * call utf8localize() directly) in case the definition of component4
+ * changes.
+ */
+
+static char *
+component_name(component4 *cp)
+{
+ return (utf8localize(cp));
+}
+
+/*
+ * linktext_name. cf. component_name().
+ */
+
+static char *
+linktext_name(linktext4 *lp)
+{
+ return (utf8localize(lp));
+}
+
+/*
+ * stable_how4_name: return a string for "how".
+ */
+
+static char *
+stable_how4_name(stable_how4 how)
+{
+ char *result;
+
+ switch (how) {
+ case UNSTABLE4:
+ result = "ASYNC";
+ break;
+ case DATA_SYNC4:
+ result = "DSYNC";
+ break;
+ case FILE_SYNC4:
+ result = "FSYNC";
+ break;
+ default:
+ result = "?";
+ break;
+ }
+
+ return (result);
+}
+
+/*
+ * sum_open_share_access: return a string corresponding to the
+ * given OPEN share access bitmask.
+ */
+
+static char *
+sum_open_share_access(int32_t mask)
+{
+ char *result;
+
+ switch (mask) {
+ case 0:
+ result = "N";
+ break;
+ case OPEN4_SHARE_ACCESS_READ:
+ result = "R";
+ break;
+ case OPEN4_SHARE_ACCESS_WRITE:
+ result = "W";
+ break;
+ case OPEN4_SHARE_ACCESS_BOTH:
+ result = "RW";
+ break;
+ default:
+ result = "?";
+ break;
+ }
+
+ return (result);
+}
+
+/*
+ * sum_open_share_deny: return a string corresponding to the
+ * given OPEN share deny bitmask.
+ */
+
+static char *
+sum_open_share_deny(int32_t mask)
+{
+ char *result;
+
+ switch (mask) {
+ case OPEN4_SHARE_DENY_NONE:
+ result = "N";
+ break;
+ case OPEN4_SHARE_DENY_READ:
+ result = "R";
+ break;
+ case OPEN4_SHARE_DENY_WRITE:
+ result = "W";
+ break;
+ case OPEN4_SHARE_DENY_BOTH:
+ result = "RW";
+ break;
+ default:
+ result = "?";
+ break;
+ }
+
+ return (result);
+}
+
+static int
+special_stateid(stateid4 *stateid)
+{
+
+ if (! memcmp(stateid, &spec_stateid_0, sizeof (*stateid)))
+ return (0);
+
+ if (! memcmp(stateid, &spec_stateid_1, sizeof (*stateid)))
+ return (1);
+
+ return (-1);
+}
+
+static char *
+_sum_stateid(stateid4 *stateid, char *prefix)
+{
+ static char buf[32];
+ int spec;
+
+ if ((spec = special_stateid(stateid)) < 0)
+ snprintf(buf, sizeof (buf), "%s%04X:%u", prefix,
+ stateid_hash(stateid), stateid->seqid);
+ else
+ snprintf(buf, sizeof (buf), "%s%s", prefix,
+ spec == 0 ? "SPC0" : (spec == 1 ? "SPC1" : "SPC?"));
+ return (buf);
+}
+
+static void
+_detail_stateid(stateid4 *stateid, char *prefix)
+{
+ int spec;
+ char seqstr[32] = {0};
+
+ spec = special_stateid(stateid);
+
+ if (spec < 0)
+ sprintf(get_line(0, 0), "%sState ID hash = %04X",
+ prefix, stateid_hash(stateid));
+ else
+ sprintf(get_line(0, 0), "%sState ID hash = %s", prefix,
+ spec == 0 ? "SPECIAL_0" :
+ (spec == 1 ? "SPECIAL_1" : "SPECIAL_?"));
+
+ sprintf(get_line(0, 0), " len = %u val = %s",
+ sizeof (stateid->other),
+ tohex(stateid->other, sizeof (stateid->other)));
+
+ /*
+ * If spec 0/1 stateid, print seqid in hex; otherwise,
+ * use decimal. This makes it more clear how spec stateids
+ * are constructed [obvious that either all bits are 0, or all
+ * bits are 1].
+ */
+ if (spec == -1)
+ sprintf(seqstr, "%d", stateid->seqid);
+ else
+ sprintf(seqstr, "%08X", stateid->seqid);
+
+ sprintf(get_line(0, 0), " %sState ID Sequence ID = %s",
+ prefix, seqstr);
+}
+
+
+static char *
+sum_lock_denied(LOCK4denied *denied)
+{
+ static char buf[64];
+
+ sprintf(buf, "%s %llu %llu LO=%04X",
+ sum_lock_type_name(denied->locktype),
+ denied->offset, denied->length,
+ owner_hash(&denied->owner.owner));
+
+ return (buf);
+}
+
+static void
+detail_lock_denied(LOCK4denied *denied)
+{
+ sprintf(get_line(0, 0), "Type = %s", lock_type_name(denied->locktype));
+ detail_lock_owner(&denied->owner);
+ sprintf(get_line(0, 0), "Offset = %llu", denied->offset);
+ sprintf(get_line(0, 0), "Length = %llu", denied->length);
+}
+
+/*
+ * sum_createhow4: return the string name of "how".
+ */
+
+static char *
+createhow4_name(createhow4 *crtp)
+{
+ char *result;
+
+ switch (crtp->mode) {
+ case UNCHECKED4:
+ result = "UNCHECKED";
+ break;
+ case GUARDED4:
+ result = "GUARDED";
+ break;
+ case EXCLUSIVE4:
+ result = "EXCLUSIVE";
+ break;
+ default:
+ result = "?";
+ break;
+ }
+
+ return (result);
+}
+
+/*
+ * detail_createhow4: print detail information about "how".
+ */
+
+static void
+detail_createhow4(createhow4 *crtp)
+{
+ sprintf(get_line(0, 0), "Method = %s",
+ createhow4_name(crtp));
+
+ switch (crtp->mode) {
+ case UNCHECKED4:
+ case GUARDED4:
+ detail_fattr4(&crtp->createhow4_u.createattrs);
+ break;
+ case EXCLUSIVE4:
+ sprintf(get_line(0, 0), " Verifier = %s",
+ tohex(crtp->createhow4_u.createverf,
+ NFS4_VERIFIER_SIZE));
+ break;
+ }
+}
+
+static void
+detail_createtype4(createtype4 *crtp)
+{
+ sprintf(get_line(0, 0), "Type = %s",
+ detail_type_name(crtp->type));
+ switch (crtp->type) {
+ case NF4LNK:
+ sprintf(get_line(0, 0), "Linkdata = %s",
+ utf8localize(&crtp->createtype4_u.linkdata));
+ break;
+ case NF4BLK:
+ case NF4CHR:
+ sprintf(get_line(0, 0), "Specdata1 = %04x Specdata2 = %04x",
+ crtp->createtype4_u.devdata.specdata1,
+ crtp->createtype4_u.devdata.specdata2);
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+sumarg_access(char *buf, size_t buflen, void *obj)
+{
+ ACCESS4args *args = (ACCESS4args *)obj;
+
+ sum_access4(buf, buflen, args->access);
+}
+
+static void
+dtlarg_access(void *obj)
+{
+ ACCESS4args *args = (ACCESS4args *)obj;
+
+ detail_access4("Access bits", args->access);
+}
+
+static void
+sumarg_close(char *buf, size_t buflen, void *obj)
+{
+ CLOSE4args *args = (CLOSE4args *)obj;
+
+ snprintf(buf, buflen, "SQ=%u %s",
+ args->seqid, sum_open_stateid(&args->open_stateid));
+}
+
+static void
+dtlarg_close(void *obj)
+{
+ CLOSE4args *args = (CLOSE4args *)obj;
+
+ detail_open_stateid(&args->open_stateid);
+ sprintf(get_line(0, 0), "Sequence ID = %u", args->seqid);
+}
+
+static void
+sumarg_commit(char *buf, size_t buflen, void *obj)
+{
+ COMMIT4args *args = (COMMIT4args *)obj;
+
+ snprintf(buf, buflen, "at %llu for %u ", args->offset,
+ args->count);
+}
+
+static void
+dtlarg_commit(void *obj)
+{
+ COMMIT4args *args = (COMMIT4args *)obj;
+
+ sprintf(get_line(0, 0), "Offset = %llu", args->offset);
+ sprintf(get_line(0, 0), "Count = %u", args->count);
+}
+
+static void
+sumarg_compnt(char *buf, size_t buflen, void *obj)
+{
+ component4 *comp = (component4 *)obj;
+
+ snprintf(buf, buflen, "%s", component_name(comp));
+}
+
+static void
+dtlarg_compnt(void *obj)
+{
+ component4 *comp = (component4 *)obj;
+
+ sprintf(get_line(0, 0), "Name = %s", component_name(comp));
+}
+
+static void
+sumarg_create(char *buf, size_t buflen, void *obj)
+{
+ CREATE4args *args = (CREATE4args *)obj;
+
+ snprintf(buf, buflen, "%s %s ", component_name(&args->objname),
+ sum_type_name(args->objtype.type));
+}
+
+static void
+dtlarg_create(void *obj)
+{
+ CREATE4args *args = (CREATE4args *)obj;
+
+ sprintf(get_line(0, 0), "Name = %s", component_name(&args->objname));
+ detail_createtype4(&args->objtype);
+ detail_fattr4(&args->createattrs);
+}
+
+static void
+sumarg_delprge(char *buf, size_t buflen, void *obj)
+{
+ DELEGPURGE4args *args = (DELEGPURGE4args *)obj;
+
+ snprintf(buf, buflen, "%s", sum_clientid(args->clientid));
+}
+
+static void
+dtlarg_delprge(void *obj)
+{
+ DELEGPURGE4args *args = (DELEGPURGE4args *)obj;
+
+ detail_clientid(args->clientid);
+}
+
+static void
+sumarg_delret(char *buf, size_t buflen, void *obj)
+{
+ DELEGRETURN4args *args = (DELEGRETURN4args *)obj;
+
+ snprintf(buf, buflen, "%s", sum_deleg_stateid(&args->deleg_stateid));
+}
+
+static void
+dtlarg_delret(void *obj)
+{
+ DELEGRETURN4args *args = (DELEGRETURN4args *)obj;
+
+ detail_deleg_stateid(&args->deleg_stateid);
+}
+
+static void
+sumarg_getattr(char *buf, size_t buflen, void *obj)
+{
+ GETATTR4args *args = (GETATTR4args *)obj;
+
+ sum_attr_bitmap(buf, buflen, &args->attr_request);
+}
+
+static void
+dtlarg_getattr(void *obj)
+{
+ GETATTR4args *args = (GETATTR4args *)obj;
+
+ detail_attr_bitmap("", &args->attr_request, NULL);
+}
+
+static void
+sumarg_cb_getattr(char *buf, size_t buflen, void *obj)
+{
+ CB_GETATTR4args *args = (CB_GETATTR4args *)obj;
+ char *bp = buf;
+
+ snprintf(bp, buflen, "%s ", sum_fh4(&args->fh));
+ bp += strlen(bp);
+ sum_attr_bitmap(bp, buflen - (bp - buf), &args->attr_request);
+}
+
+static void
+dtlarg_cb_getattr(void *obj)
+{
+ CB_GETATTR4args *args = (CB_GETATTR4args *)obj;
+
+ detail_fh4(&args->fh);
+ detail_attr_bitmap("", &args->attr_request, NULL);
+}
+
+static void
+sumarg_cb_recall(char *buf, size_t buflen, void *obj)
+{
+ CB_RECALL4args *args = (CB_RECALL4args *)obj;
+ char *bp = buf;
+
+ snprintf(bp, buflen, "%s %s TR=%s", sum_fh4(&args->fh),
+ sum_stateid(&args->stateid), args->truncate ? "T" : "F");
+}
+
+static void
+dtlarg_cb_recall(void *obj)
+{
+ CB_RECALL4args *args = (CB_RECALL4args *)obj;
+
+ detail_fh4(&args->fh);
+ detail_stateid(&args->stateid);
+ sprintf(get_line(0, 0), "Truncate = %s",
+ args->truncate ? "True" : "False");
+}
+
+
+/*
+ * name openhow seqid claim access deny owner
+ */
+static void
+sumarg_open(char *buf, size_t buflen, void *obj)
+{
+ OPEN4args *args = (OPEN4args *)obj;
+ char *bp = buf;
+ int blen = buflen, len;
+
+ sum_name(bp, buflen, &args->claim);
+ bp += (len = strlen(bp));
+ blen -= len;
+
+ sum_openflag(bp, blen, &args->openhow);
+ bp += (len = strlen(bp));
+ blen -= len;
+
+ snprintf(bp, blen, " SQ=%u", args->seqid);
+ bp += (len = strlen(bp));
+ blen -= len;
+
+ sum_claim(bp, blen, &args->claim);
+ bp += (len = strlen(bp));
+ blen -= len;
+
+ snprintf(bp, blen, " AC=%s DN=%s OO=%04X",
+ sum_open_share_access(args->share_access),
+ sum_open_share_deny(args->share_deny),
+ owner_hash(&args->owner.owner));
+}
+
+static void
+dtlarg_open(void *obj)
+{
+ OPEN4args *args = (OPEN4args *)obj;
+
+ detail_claim(&args->claim);
+ detail_openflag(&args->openhow);
+ detail_open_owner(&args->owner);
+ sprintf(get_line(0, 0), "Sequence ID = %u", args->seqid);
+ sprintf(get_line(0, 0), "Access = 0x%x (%s)",
+ args->share_access, sum_open_share_access(args->share_access));
+ sprintf(get_line(0, 0), "Deny = 0x%x (%s)",
+ args->share_deny, sum_open_share_access(args->share_deny));
+}
+
+static void
+sumarg_openattr(char *buf, size_t buflen, void *obj)
+{
+ OPENATTR4args *args = (OPENATTR4args *)obj;
+
+ snprintf(buf, buflen, "CD=%s",
+ args->createdir ? "T" : "F");
+}
+
+static void
+dtlarg_openattr(void *obj)
+{
+ OPENATTR4args *args = (OPENATTR4args *)obj;
+
+ sprintf(get_line(0, 0), "CreateDir = %s",
+ args->createdir ? "True" : "False");
+}
+
+static void
+sumarg_open_confirm(char *buf, size_t buflen, void *obj)
+{
+ char *bp = buf;
+ OPEN_CONFIRM4args *args = (OPEN_CONFIRM4args *)obj;
+
+ snprintf(bp, buflen, "SQ=%u %s", args->seqid,
+ sum_open_stateid(&args->open_stateid));
+}
+
+static void
+dtlarg_open_confirm(void *obj)
+{
+ OPEN_CONFIRM4args *args = (OPEN_CONFIRM4args *)obj;
+
+ sprintf(get_line(0, 0), "Sequence ID = %u", args->seqid);
+ detail_open_stateid(&args->open_stateid);
+}
+
+static void
+sumarg_open_downgrd(char *buf, size_t buflen, void *obj)
+{
+ OPEN_DOWNGRADE4args *args = (OPEN_DOWNGRADE4args *)obj;
+
+ snprintf(buf, buflen, "SQ=%u %s AC=%s DN=%s",
+ args->seqid, sum_open_stateid(&args->open_stateid),
+ sum_open_share_access(args->share_access),
+ sum_open_share_deny(args->share_deny));
+}
+
+static void
+dtlarg_open_downgrd(void *obj)
+{
+ OPEN_DOWNGRADE4args *args = (OPEN_DOWNGRADE4args *)obj;
+
+ sprintf(get_line(0, 0), "Open Sequence ID = %u", args->seqid);
+ detail_open_stateid(&args->open_stateid);
+ sprintf(get_line(0, 0), "Access = 0x%x (%s)",
+ args->share_access, sum_open_share_access(args->share_access));
+ sprintf(get_line(0, 0), "Deny = 0x%x (%s)",
+ args->share_deny, sum_open_share_access(args->share_deny));
+}
+
+static void
+sumarg_putfh(char *buf, size_t buflen, void *obj)
+{
+ PUTFH4args *args = (PUTFH4args *)obj;
+
+ snprintf(buf, buflen, "%s", sum_fh4(&args->object));
+}
+
+static void
+dtlarg_putfh(void *obj)
+{
+ PUTFH4args *args = (PUTFH4args *)obj;
+
+ detail_fh4(&args->object);
+}
+
+static void
+sumarg_link(char *buf, size_t buflen, void *obj)
+{
+ LINK4args *args = (LINK4args *)obj;
+
+ snprintf(buf, buflen, "%s", component_name(&args->newname));
+}
+
+static void
+dtlarg_link(void *obj)
+{
+ LINK4args *args = (LINK4args *)obj;
+
+ sprintf(get_line(0, 0), "New name = %s",
+ component_name(&args->newname));
+}
+
+static void
+sum_open_to_lock_owner(char *buf, int buflen, open_to_lock_owner4 *own)
+{
+ snprintf(buf, buflen, " OSQ=%u %s LSQ=%u LO=%04X", own->open_seqid,
+ sum_open_stateid(&own->open_stateid), own->lock_seqid,
+ owner_hash(&own->lock_owner.owner));
+}
+
+static void
+sum_exist_lock_owner(char *buf, int buflen, exist_lock_owner4 *own)
+{
+ snprintf(buf, buflen, " LSQ=%u %s", own->lock_seqid,
+ sum_lock_stateid(&own->lock_stateid));
+}
+
+static void
+sum_locker(char *buf, size_t len, locker4 *lk)
+{
+ if (lk->new_lock_owner == TRUE)
+ sum_open_to_lock_owner(buf, len, &lk->locker4_u.open_owner);
+ else
+ sum_exist_lock_owner(buf, len, &lk->locker4_u.lock_owner);
+}
+
+static char *
+sum_lock_type_name(enum nfs_lock_type4 type)
+{
+ char *result;
+
+ switch (type) {
+ case READ_LT:
+ result = "RD";
+ break;
+ case WRITE_LT:
+ result = "WR";
+ break;
+ case READW_LT:
+ result = "RDW";
+ break;
+ case WRITEW_LT:
+ result = "WRW";
+ break;
+ default:
+ result = "?";
+ break;
+ }
+
+ return (result);
+}
+
+static void
+sumarg_lock(char *buf, size_t buflen, void *obj)
+{
+ LOCK4args *args = (LOCK4args *)obj;
+ char *bp = buf;
+
+ snprintf(buf, buflen, "%s%s%llu:%llu",
+ sum_lock_type_name(args->locktype),
+ args->reclaim ? " reclaim " : " ",
+ args->offset, args->length);
+
+ bp += strlen(buf);
+ sum_locker(bp, buflen - (bp - buf), &args->locker);
+}
+
+static void
+detail_open_to_lock_owner(open_to_lock_owner4 *own)
+{
+ sprintf(get_line(0, 0), "Open Sequence ID = %u", own->open_seqid);
+ detail_open_stateid(&own->open_stateid);
+ sprintf(get_line(0, 0), "Lock Sequence ID = %u", own->lock_seqid);
+ detail_lock_owner(&own->lock_owner);
+}
+
+static void
+detail_exist_lock_owner(exist_lock_owner4 *own)
+{
+ detail_lock_stateid(&own->lock_stateid);
+ sprintf(get_line(0, 0), "Lock Sequence ID = %u", own->lock_seqid);
+}
+
+static void
+detail_locker(locker4 *lk)
+{
+ if (lk->new_lock_owner == TRUE)
+ detail_open_to_lock_owner(&lk->locker4_u.open_owner);
+ else
+ detail_exist_lock_owner(&lk->locker4_u.lock_owner);
+}
+
+static void
+dtlarg_lock(void *obj)
+{
+ LOCK4args *args = (LOCK4args *)obj;
+
+ sprintf(get_line(0, 0), "Type = %s", lock_type_name(args->locktype));
+ sprintf(get_line(0, 0), "Reclaim = %s",
+ args->reclaim ? "TRUE" : "FALSE");
+ sprintf(get_line(0, 0), "Offset = %llu", args->offset);
+ sprintf(get_line(0, 0), "Length = %llu", args->length);
+ detail_locker(&args->locker);
+}
+
+static void
+sumarg_lockt(char *buf, size_t buflen, void *obj)
+{
+ LOCKT4args *args = (LOCKT4args *)obj;
+
+ snprintf(buf, buflen, "R=%llu:%llu",
+ args->offset, args->length);
+}
+
+static void
+dtlarg_lockt(void *obj)
+{
+ LOCKT4args *args = (LOCKT4args *)obj;
+
+ sprintf(get_line(0, 0), "Type = %s", lock_type_name(args->locktype));
+ detail_lock_owner(&args->owner);
+ sprintf(get_line(0, 0), "Offset = %llu", args->offset);
+ sprintf(get_line(0, 0), "Length = %llu", args->length);
+}
+
+static void
+sumarg_locku(char *buf, size_t buflen, void *obj)
+{
+ LOCKU4args *args = (LOCKU4args *)obj;
+
+ snprintf(buf, buflen, "R=%llu:%llu LSQ=%u %s",
+ args->offset, args->length, args->seqid,
+ sum_lock_stateid(&args->lock_stateid));
+}
+
+
+static void
+dtlarg_locku(void *obj)
+{
+ LOCKU4args *args = (LOCKU4args *)obj;
+
+ sprintf(get_line(0, 0), "Type = %s", lock_type_name(args->locktype));
+ sprintf(get_line(0, 0), "Sequence ID = %u", args->seqid);
+ detail_lock_stateid(&args->lock_stateid);
+ sprintf(get_line(0, 0), "Offset = %llu", args->offset);
+ sprintf(get_line(0, 0), "Length = %llu", args->length);
+}
+
+static void
+sumarg_lookup(char *buf, size_t buflen, void *obj)
+{
+ LOOKUP4args *args = (LOOKUP4args *)obj;
+
+ sum_compname4(buf, buflen, &args->objname);
+}
+
+static void
+dtlarg_lookup(void *obj)
+{
+ LOOKUP4args *args = (LOOKUP4args *)obj;
+
+ detail_compname4(&args->objname);
+}
+
+static void
+sumarg_read(char *buf, size_t buflen, void *obj)
+{
+ READ4args *args = (READ4args *)obj;
+
+ snprintf(buf, buflen, "%s at %llu for %u",
+ sum_stateid(&args->stateid), args->offset, args->count);
+}
+
+static void
+dtlarg_read(void *obj)
+{
+ READ4args *args = (READ4args *)obj;
+
+ sprintf(get_line(0, 0), "Offset = %llu", args->offset);
+ sprintf(get_line(0, 0), "Count = %u", args->count);
+ detail_stateid(&args->stateid);
+}
+
+static void
+sumarg_readdir(char *buf, size_t buflen, void *obj)
+{
+ READDIR4args *args = (READDIR4args *)obj;
+
+ snprintf(buf, buflen, "Cookie=%llu (%s) for %u/%u",
+ args->cookie, tohex(args->cookieverf, NFS4_VERIFIER_SIZE),
+ args->dircount, args->maxcount);
+}
+
+static void
+dtlarg_readdir(void *obj)
+{
+ READDIR4args *args = (READDIR4args *)obj;
+
+ sprintf(get_line(0, 0), "Cookie = %llu", args->cookie);
+ sprintf(get_line(0, 0), "Verifier = %s",
+ tohex(args->cookieverf, NFS4_VERIFIER_SIZE));
+ sprintf(get_line(0, 0), "Dircount = %u", args->dircount);
+ sprintf(get_line(0, 0), "Maxcount = %u", args->maxcount);
+ detail_attr_bitmap("", &args->attr_request, NULL);
+}
+
+static void
+dtlarg_release_lkown(void *obj)
+{
+ RELEASE_LOCKOWNER4args *args = (RELEASE_LOCKOWNER4args *)obj;
+
+ detail_lock_owner(&args->lock_owner);
+}
+
+static void
+sumarg_release_lkown(char *buf, size_t buflen, void *obj)
+
+{
+ RELEASE_LOCKOWNER4args *args = (RELEASE_LOCKOWNER4args *)obj;
+
+ snprintf(buf, buflen, "LO=%04X", owner_hash(&args->lock_owner.owner));
+}
+
+static void
+sumarg_rename(char *buf, size_t buflen, void *obj)
+{
+ RENAME4args *args = (RENAME4args *)obj;
+
+ snprintf(buf, buflen, "%s to %s",
+ component_name(&args->oldname),
+ component_name(&args->newname));
+}
+
+static void
+dtlarg_rename(void *obj)
+{
+ RENAME4args *args = (RENAME4args *)obj;
+
+ sprintf(get_line(0, 0), "Old name = %s",
+ component_name(&args->oldname));
+ sprintf(get_line(0, 0), "New name = %s",
+ component_name(&args->newname));
+}
+
+static void
+sumarg_renew(char *buf, size_t buflen, void *obj)
+{
+ RENEW4args *args = (RENEW4args *)obj;
+
+ snprintf(buf, buflen, "%s", sum_clientid(args->clientid));
+}
+static void
+dtlarg_renew(void *obj)
+{
+ RENEW4args *args = (RENEW4args *)obj;
+
+ detail_clientid(args->clientid);
+}
+
+static void
+sumarg_secinfo(char *buf, size_t buflen, void *obj)
+{
+ SECINFO4args *args = (SECINFO4args *)obj;
+
+ snprintf(buf, buflen, "%s",
+ component_name(&args->name));
+}
+
+static void
+dtlarg_secinfo(void *obj)
+{
+ SECINFO4args *args = (SECINFO4args *)obj;
+
+ sprintf(get_line(0, 0), "Name = %s",
+ component_name(&args->name));
+}
+
+static void
+sumarg_setattr(char *buf, size_t buflen, void *obj)
+{
+ SETATTR4args *args = (SETATTR4args *)obj;
+
+ snprintf(buf, buflen, "%s", sum_stateid(&args->stateid));
+}
+
+static void
+dtlarg_setattr(void *obj)
+{
+ SETATTR4args *args = (SETATTR4args *)obj;
+
+ detail_stateid(&args->stateid);
+ detail_fattr4(&args->obj_attributes);
+}
+
+static void
+sumarg_setclid(char *buf, size_t buflen, void *obj)
+{
+ SETCLIENTID4args *args = (SETCLIENTID4args *)obj;
+
+ snprintf(buf, buflen, "Prog=%u ID=%s Addr=%s CBID=%u",
+ args->callback.cb_program,
+ args->callback.cb_location.r_netid,
+ args->callback.cb_location.r_addr, args->callback_ident);
+}
+
+static void
+dtlarg_setclid(void *obj)
+{
+ SETCLIENTID4args *args = (SETCLIENTID4args *)obj;
+
+ sprintf(get_line(0, 0), "Verifier=%s",
+ tohex(args->client.verifier, NFS4_VERIFIER_SIZE));
+ sprintf(get_line(0, 0), "ID = (%d) %s",
+ args->client.id.id_len,
+ tohex(args->client.id.id_val, args->client.id.id_len));
+
+ sprintf(get_line(0, 0), "Callback Program = %u",
+ args->callback.cb_program);
+ sprintf(get_line(0, 0), "Callback Net ID = %s",
+ args->callback.cb_location.r_netid);
+ sprintf(get_line(0, 0), "Callback Addr = %s",
+ args->callback.cb_location.r_addr);
+ sprintf(get_line(0, 0), "Callback Ident = %u", args->callback_ident);
+}
+
+static void
+sumarg_setclid_cfm(char *buf, size_t buflen, void *obj)
+{
+ SETCLIENTID_CONFIRM4args *args = (SETCLIENTID_CONFIRM4args *)obj;
+
+ snprintf(buf, buflen, "%s CFV=%s", sum_clientid(args->clientid),
+ tohex(args->setclientid_confirm, NFS4_VERIFIER_SIZE));
+}
+
+static void
+dtlarg_setclid_cfm(void *obj)
+{
+ SETCLIENTID_CONFIRM4args *args = (SETCLIENTID_CONFIRM4args *)obj;
+
+ detail_clientid(args->clientid);
+ sprintf(get_line(0, 0), "Set Client ID Confirm Verifier = %s",
+ tohex(args->setclientid_confirm, NFS4_VERIFIER_SIZE));
+}
+
+
+static void
+dtlarg_verify(void *obj)
+{
+ NVERIFY4args *args = (NVERIFY4args *)obj;
+
+ detail_fattr4(&args->obj_attributes);
+}
+
+static void
+sumarg_write(char *buf, size_t buflen, void *obj)
+{
+ WRITE4args *args = (WRITE4args *)obj;
+
+ snprintf(buf, buflen, "%s at %llu for %u",
+ sum_stateid(&args->stateid), args->offset, args->data.data_len);
+}
+
+static void
+dtlarg_write(void *obj)
+{
+ WRITE4args *args = (WRITE4args *)obj;
+
+ sprintf(get_line(0, 0), "Offset = %llu", args->offset);
+ sprintf(get_line(0, 0), "Count = %u", args->data.data_len);
+ sprintf(get_line(0, 0), "Stable = %s", stable_how4_name(args->stable));
+ detail_stateid(&args->stateid);
+}
+
+static char *
+sum_fh4(nfs_fh4 *fh)
+{
+ static char buf[20];
+
+ sprintf(buf, "FH=%04X", fh4_hash(fh));
+
+ return (buf);
+}
+
+static void
+detail_fh4(nfs_fh4 *fh)
+{
+ int i;
+ uchar_t *cp;
+ char *bufp;
+
+ sprintf(get_line(0, 0), "File handle = [%04X]", fh4_hash(fh));
+ bufp = get_line(0, 0);
+ sprintf(bufp, "(%d) ", fh->nfs_fh4_len);
+ bufp += strlen(bufp);
+ /* XXX use tohex()? */
+ for (i = 0, cp = (uchar_t *)fh->nfs_fh4_val;
+ i < fh->nfs_fh4_len;
+ i++, cp++) {
+ if (i != 0 && i % 32 == 0)
+ bufp = get_line(0, 0);
+ sprintf(bufp, "%02x", *cp);
+ bufp += strlen(bufp);
+ }
+}
+
+static void
+detail_fattr4(fattr4 *attrp)
+{
+ unpkd_attrmap_t provided;
+ uint_t attrnum;
+ XDR attrxdr;
+ jmp_buf old_errbuf;
+
+ xdrmem_create(&attrxdr, attrp->attr_vals.attrlist4_val,
+ attrp->attr_vals.attrlist4_len, XDR_DECODE);
+
+ bcopy(xdr_err, old_errbuf, sizeof (old_errbuf));
+ if (setjmp(xdr_err)) {
+ sprintf(get_line(0, 0), "<attr_vals too short>");
+ goto done;
+ }
+
+ detail_attr_bitmap("", &attrp->attrmask, &provided);
+ for (attrnum = 0; attrnum < MAX_ATTRIBUTES; attrnum++) {
+ if (provided.map[attrnum]) {
+ attr_info[attrnum].prt_details(&attrxdr);
+ }
+ }
+
+done:
+ bcopy(old_errbuf, xdr_err, sizeof (old_errbuf));
+}
+
+static void
+sum_attr_bitmap(char *buf, size_t buflen, bitmap4 *mapp)
+{
+ uint_t num_words;
+ char *bp;
+ size_t curlen, remaining;
+
+ buf[0] = '\0';
+ for (num_words = 0; num_words < mapp->bitmap4_len; num_words++) {
+ curlen = strlen(buf);
+ if (curlen + sizeof ("<Too Long>") >= buflen) {
+ strcpy(buf + buflen - sizeof ("<Too Long>"),
+ "<Too Long>");
+ return;
+ }
+ bp = buf + curlen;
+ remaining = buflen - curlen;
+ snprintf(bp, remaining,
+ num_words == 0 ? "%x" : " %x",
+ mapp->bitmap4_val[num_words]);
+ }
+}
+
+/*
+ * Print detail information for the given attribute bitmap, and fill in the
+ * unpacked version of the map if "unpacked" is non-null. Returns the
+ * number of bytes in the bitmap. "prefix" is an initial string that is
+ * printed at the front of each line.
+ */
+
+static void
+detail_attr_bitmap(char *prefix, bitmap4 *bitp, unpkd_attrmap_t *unpacked)
+{
+ uint_t num_words;
+ uint32_t *wp;
+ uint_t byte_num;
+
+ if (unpacked != NULL)
+ memset(unpacked, 0, sizeof (unpkd_attrmap_t));
+
+ /*
+ * Break the bitmap into octets, then print in hex and
+ * symbolically.
+ */
+
+ for (num_words = 0, wp = bitp->bitmap4_val;
+ num_words < bitp->bitmap4_len;
+ num_words++, wp++) {
+ for (byte_num = 0; byte_num < 4; byte_num++) {
+ uchar_t val = (*wp) >> (byte_num * 8);
+ char *buf = get_line(0, 0);
+ uint_t attrnum;
+ int bit;
+
+ sprintf(buf, "%s 0x%02x ", prefix, val);
+ attrnum = num_words * 32 + byte_num * 8;
+ for (bit = 7; bit >= 0; bit--) {
+ if (val & (1 << bit)) {
+ strcat(buf, " ");
+ strcat(buf,
+ attr_name(attrnum + bit));
+ if (unpacked != NULL)
+ unpacked->map[attrnum + bit] =
+ 1;
+ }
+ }
+ }
+ }
+}
+
+/*
+ * Format the summary line results from a COMPOUND4 call.
+ */
+
+static void
+sum_comp4res(char *line, char *(*sumres_fn)(void))
+{
+ nfsstat4 status;
+ static utf8string tag;
+
+ status = getxdr_long();
+ if (!xdr_utf8string(&xdrm, &tag))
+ longjmp(xdr_err, 1);
+
+ sprintf(line, "(%.20s) %s %s", utf8localize(&tag),
+ status_name(status), sumres_fn());
+
+ xdr_free(xdr_utf8string, (char *)&tag);
+}
+
+
+/*
+ * Return a set of summary strings for the result data that's next in the
+ * XDR stream, up to SUM_COMPND_MAX characters. If the strings don't fit,
+ * include a "..." at the end of the string.
+ */
+
+static char *
+sum_compound4res(void)
+{
+ static char buf[SUM_COMPND_MAX + 2]; /* 1 for null, 1 for overflow */
+ int numres;
+ const size_t buflen = sizeof (buf);
+ char *bp;
+ nfs_resop4 one_res;
+
+ buf[0] = '\0';
+ if (setjmp(xdr_err)) {
+ bp = buf + strlen(buf);
+ snprintf(bp, buflen - (bp - buf),
+ nfs4_fragged_rpc ? nfs4err_fragrpc : nfs4err_xdrfrag);
+ return (buf);
+ }
+
+ numres = getxdr_long();
+ bp = buf;
+ while (numres-- > 0) {
+ char *result;
+
+ bzero(&one_res, sizeof (one_res));
+
+ if (!xdr_nfs_resop4(&xdrm, &one_res)) {
+ xdr_free(xdr_nfs_resop4, (char *)&one_res);
+ longjmp(xdr_err, 1);
+ }
+
+ snprintf(bp, buflen - (bp - buf), "%s ",
+ opcode_name(one_res.resop));
+ bp += strlen(bp);
+
+ result = sum_result(&one_res);
+ if (strlen(result) > 0) {
+ snprintf(bp, buflen - (bp - buf), "%s ", result);
+ bp += strlen(bp);
+ }
+
+ /* nfs4_skip_bytes set by xdr_nfs4_argop4() */
+ if (nfs4_skip_bytes != 0)
+ nfs4_xdr_skip(nfs4_skip_bytes);
+
+ xdr_free(xdr_nfs_resop4, (char *)&one_res);
+ /* add "..." if past the "end" of the buffer */
+ if (bp - buf > SUM_COMPND_MAX) {
+ strcpy(buf + SUM_COMPND_MAX - strlen("..."),
+ "...");
+ break;
+ }
+ }
+
+ return (buf);
+}
+
+
+/*
+ * Return a set of summary strings for the result data that's next in the
+ * XDR stream, up to SUM_COMPND_MAX characters. If the strings don't fit,
+ * include a "..." at the end of the string.
+ */
+
+static char *
+sum_cb_compound4res(void)
+{
+ static char buf[SUM_COMPND_MAX + 2]; /* 1 for null, 1 for overflow */
+ int numres;
+ const size_t buflen = sizeof (buf);
+ char *bp;
+ nfs_cb_resop4 one_res;
+
+ buf[0] = '\0';
+ if (setjmp(xdr_err)) {
+ bp = buf + strlen(buf);
+ snprintf(bp, buflen - (bp - buf), "<XDR Error or Fragmented"
+ " RPC>");
+ return (buf);
+ }
+
+ numres = getxdr_long();
+ bp = buf;
+ while (numres-- > 0) {
+ bzero(&one_res, sizeof (one_res));
+ if (!xdr_nfs_cb_resop4(&xdrm, &one_res)) {
+ xdr_free(xdr_nfs_cb_resop4, (char *)&one_res);
+ longjmp(xdr_err, 1);
+ }
+ snprintf(bp, buflen - (bp - buf), "%s %s ",
+ cb_opcode_name(one_res.resop),
+ sum_cb_result(&one_res));
+ bp += strlen(bp);
+
+ xdr_free(xdr_nfs_cb_resop4, (char *)&one_res);
+
+ /* add "..." if past the "end" of the buffer */
+ if (bp - buf > SUM_COMPND_MAX) {
+ strcpy(buf + SUM_COMPND_MAX - strlen("..."),
+ "...");
+ break;
+ }
+ }
+
+ return (buf);
+}
+
+
+/*
+ * Return the summarized results for the given resultdata.
+ */
+
+static char *
+sum_result(nfs_resop4 *resp)
+{
+ static char buf[1024];
+ void (*fmtproc)(char *, size_t, void *);
+
+ buf[0] = '\0';
+ if (resp->resop < num_opcodes)
+ fmtproc = opcode_info[resp->resop].sumres;
+ else if (resp->resop == OP_ILLEGAL)
+ fmtproc = sum_nfsstat4;
+ else
+ fmtproc = NULL;
+
+ if (fmtproc != NULL)
+ fmtproc(buf, sizeof (buf), &resp->nfs_resop4_u);
+
+ return (buf);
+}
+
+/*
+ * Return the summarized results for the given resultdata.
+ */
+
+static char *
+sum_cb_result(nfs_cb_resop4 *resp)
+{
+ static char buf[1024];
+ void (*fmtproc)(char *, size_t, void *);
+
+ buf[0] = '\0';
+ if (resp->resop < cb_num_opcodes)
+ fmtproc = cb_opcode_info[resp->resop].sumres;
+ else if (resp->resop == OP_CB_ILLEGAL)
+ fmtproc = sum_nfsstat4;
+ else
+ fmtproc = NULL;
+
+ if (fmtproc != NULL)
+ fmtproc(buf, sizeof (buf), &resp->nfs_cb_resop4_u);
+
+ return (buf);
+}
+
+
+static void
+dtl_change_info(char *msg, change_info4 *infop)
+{
+ sprintf(get_line(0, 0), "%s:", msg);
+ sprintf(get_line(0, 0), " Atomic = %s",
+ infop->atomic ? "TRUE" : "FALSE");
+ detail_fattr4_change(" Before", infop->before);
+ detail_fattr4_change(" After", infop->after);
+}
+
+static void
+detail_fattr4_change(char *msg, fattr4_change chg)
+{
+ sprintf(get_line(0, 0), "%s: 0x%llx", msg, chg);
+ /* XXX print as time_t, too? */
+}
+
+static void
+sum_nfsstat4(char *buf, size_t buflen, void *obj)
+{
+ nfsstat4 status = *(nfsstat4 *)obj;
+
+ strncpy(buf, status_name(status), buflen);
+}
+
+static void
+dtl_nfsstat4(void *obj)
+{
+ nfsstat4 status = *(nfsstat4 *)obj;
+
+ sprintf(get_line(0, 0), "Status = %d (%s)", status,
+ status_name(status));
+}
+
+static void
+sumres_access(char *buf, size_t buflen, void *obj)
+{
+ ACCESS4res *res = (ACCESS4res *)obj;
+ char *bp = buf;
+ int len, blen = buflen;
+
+ strcpy(bp, status_name(res->status));
+ if (res->status == NFS4_OK) {
+ bp += (len = strlen(bp));
+ blen -= len;
+
+ snprintf(bp, blen, " Supp=");
+ bp += (len = strlen(bp));
+ blen -= len;
+
+ sum_access4(bp, blen, res->ACCESS4res_u.resok4.supported);
+ bp += (len = strlen(bp));
+ blen -= len;
+
+ snprintf(bp, blen, " Allow=");
+ bp += (len = strlen(bp));
+ blen -= len;
+
+ sum_access4(bp, blen, res->ACCESS4res_u.resok4.access);
+ }
+}
+
+static void
+dtlres_access(void *obj)
+{
+ ACCESS4res *res = (ACCESS4res *)obj;
+
+ dtl_nfsstat4(obj);
+ if (res->status == NFS4_OK) {
+ detail_access4("Supported Attributes",
+ res->ACCESS4res_u.resok4.supported);
+ detail_access4("Allowed Attributes",
+ res->ACCESS4res_u.resok4.access);
+ }
+}
+
+static void
+sumres_close(char *buf, size_t buflen, void *obj)
+{
+ CLOSE4res *res = (CLOSE4res *)obj;
+
+ if (res->status == NFS4_OK)
+ snprintf(buf, buflen, "%s",
+ sum_open_stateid(&res->CLOSE4res_u.open_stateid));
+}
+
+static void
+dtlres_close(void *obj)
+{
+ CLOSE4res *res = (CLOSE4res *)obj;
+
+ dtl_nfsstat4(obj);
+ if (res->status == NFS4_OK) {
+ detail_open_stateid(&res->CLOSE4res_u.open_stateid);
+ }
+}
+
+static void
+sumres_commit(char *buf, size_t buflen, void *obj)
+{
+ COMMIT4res *res = (COMMIT4res *)obj;
+
+ if (res->status == NFS4_OK)
+ snprintf(buf, buflen, "Verf=%s",
+ tohex(res->COMMIT4res_u.resok4.writeverf,
+ NFS4_VERIFIER_SIZE));
+}
+
+static void
+dtlres_commit(void *obj)
+{
+ COMMIT4res *res = (COMMIT4res *)obj;
+
+ dtl_nfsstat4(obj);
+ if (res->status == NFS4_OK) {
+ sprintf(get_line(0, 0), "Verifier = %s",
+ tohex(res->COMMIT4res_u.resok4.writeverf,
+ NFS4_VERIFIER_SIZE));
+ }
+}
+
+static void
+dtlres_create(void *obj)
+{
+ CREATE4res *res = (CREATE4res *)obj;
+
+ dtl_nfsstat4(obj);
+ if (res->status == NFS4_OK) {
+ dtl_change_info("Change Information",
+ &res->CREATE4res_u.resok4.cinfo);
+ detail_attr_bitmap("", &res->CREATE4res_u.resok4.attrset,
+ NULL);
+ }
+}
+
+static void
+sumres_getattr(char *buf, size_t buflen, void *obj)
+{
+ GETATTR4res *res = (GETATTR4res *)obj;
+
+ strncpy(buf, status_name(res->status), buflen);
+}
+
+static void
+dtlres_getattr(void *obj)
+{
+ GETATTR4res *res = (GETATTR4res *)obj;
+
+ dtl_nfsstat4(obj);
+ if (res->status == NFS4_OK) {
+ detail_fattr4(&res->GETATTR4res_u.resok4.obj_attributes);
+ }
+}
+
+static void
+sumres_cb_getattr(char *buf, size_t buflen, void *obj)
+{
+ CB_GETATTR4res *res = (CB_GETATTR4res *)obj;
+
+ strncpy(buf, status_name(res->status), buflen);
+}
+
+static void
+dtlres_cb_getattr(void *obj)
+{
+ CB_GETATTR4res *res = (CB_GETATTR4res *)obj;
+
+ dtl_nfsstat4(obj);
+ if (res->status == NFS4_OK) {
+ detail_fattr4(&res->CB_GETATTR4res_u.resok4.obj_attributes);
+ }
+}
+
+
+static void
+sumres_getfh(char *buf, size_t buflen, void *obj)
+{
+ char *bp;
+ GETFH4res *res = (GETFH4res *)obj;
+
+ strncpy(buf, status_name(res->status), buflen);
+ if (res->status == NFS4_OK) {
+ bp = buf + strlen(buf);
+ snprintf(bp, buflen - (bp - buf), " %s",
+ sum_fh4(&res->GETFH4res_u.resok4.object));
+ }
+}
+
+static void
+dtlres_getfh(void *obj)
+{
+ GETFH4res *res = (GETFH4res *)obj;
+
+ dtl_nfsstat4(obj);
+ if (res->status == NFS4_OK) {
+ detail_fh4(&res->GETFH4res_u.resok4.object);
+ }
+}
+
+static void
+dtlres_link(void *obj)
+{
+ LINK4res *res = (LINK4res *)obj;
+
+ dtl_nfsstat4(obj);
+ if (res->status == NFS4_OK) {
+ dtl_change_info("Change Information",
+ &res->LINK4res_u.resok4.cinfo);
+ }
+}
+
+static void
+sumres_lock(char *buf, size_t buflen, void *obj)
+{
+ char *bp;
+ LOCK4res *res = (LOCK4res *)obj;
+
+ strncpy(buf, status_name(res->status), buflen);
+ if (res->status == NFS4_OK) {
+ bp = buf + strlen(buf);
+ snprintf(bp, buflen - (bp - buf), " %s",
+ sum_lock_stateid(&res->LOCK4res_u.resok4.lock_stateid));
+ }
+ if (res->status == NFS4ERR_DENIED) {
+ bp = buf + strlen(buf);
+ snprintf(bp, buflen - (bp - buf), " %s",
+ sum_lock_denied(&res->LOCK4res_u.denied));
+ }
+}
+
+static void
+dtlres_lock(void *obj)
+{
+ LOCK4res *res = (LOCK4res *)obj;
+
+ dtl_nfsstat4(obj);
+ if (res->status == NFS4_OK) {
+ detail_lock_stateid(&res->LOCK4res_u.resok4.lock_stateid);
+ }
+ if (res->status == NFS4ERR_DENIED) {
+ detail_lock_denied(&res->LOCK4res_u.denied);
+ }
+}
+
+static void
+sumres_lockt(char *buf, size_t buflen, void *obj)
+{
+ char *bp;
+ LOCKT4res *res = (LOCKT4res *)obj;
+
+ strcpy(buf, status_name(res->status));
+ if (res->status == NFS4ERR_DENIED) {
+ bp = buf + strlen(buf);
+ snprintf(bp, buflen - (bp - buf), " %s",
+ sum_lock_denied(&res->LOCKT4res_u.denied));
+ }
+}
+
+static void
+dtlres_lockt(void *obj)
+{
+ LOCKT4res *res = (LOCKT4res *)obj;
+
+ dtl_nfsstat4(obj);
+ if (res->status == NFS4ERR_DENIED) {
+ detail_lock_denied(&res->LOCKT4res_u.denied);
+ }
+}
+
+static void
+sumres_locku(char *buf, size_t buflen, void *obj)
+{
+ char *bp;
+ LOCKU4res *res = (LOCKU4res *)obj;
+
+ strncpy(buf, status_name(res->status), buflen);
+ bp = buf + strlen(buf);
+ if (res->status == NFS4_OK)
+ snprintf(bp, buflen - (bp - buf), " %s",
+ sum_lock_stateid(&res->LOCKU4res_u.lock_stateid));
+}
+
+static void
+dtlres_locku(void *obj)
+{
+ LOCKU4res *res = (LOCKU4res *)obj;
+
+ dtl_nfsstat4(obj);
+ if (res->status == NFS4_OK)
+ detail_lock_stateid(&res->LOCKU4res_u.lock_stateid);
+}
+
+static void
+sumres_open(char *buf, size_t buflen, void *obj)
+{
+ char *bp = buf;
+ OPEN4res *res = (OPEN4res *)obj;
+ uint_t rflags;
+ int len, blen = buflen;
+
+ strncpy(bp, status_name(res->status), blen);
+
+ if (res->status == NFS4_OK) {
+ bp += (len = strlen(bp));
+ blen -= len;
+
+ snprintf(bp, blen, " %s",
+ sum_stateid(&res->OPEN4res_u.resok4.stateid));
+ bp += (len = strlen(bp));
+ blen -= len;
+
+ if ((rflags = res->OPEN4res_u.resok4.rflags) != 0) {
+ snprintf(bp, blen, "%s", sum_open_rflags(rflags));
+ bp += (len = strlen(bp));
+ blen -= len;
+ }
+
+ sum_delegation(bp, blen, &res->OPEN4res_u.resok4.delegation);
+ }
+}
+
+static void
+dtlres_open(void *obj)
+{
+ OPEN4res *res = (OPEN4res *)obj;
+
+ dtl_nfsstat4(obj);
+ if (res->status == NFS4_OK) {
+ detail_stateid(&res->OPEN4res_u.resok4.stateid);
+ dtl_change_info("Change Information",
+ &res->OPEN4res_u.resok4.cinfo);
+ sprintf(get_line(0, 0), "Flags = 0x%x (%s)",
+ res->OPEN4res_u.resok4.rflags,
+ detail_open_rflags(res->OPEN4res_u.resok4.rflags));
+ detail_attr_bitmap("", &res->OPEN4res_u.resok4.attrset,
+ NULL);
+ detail_delegation(&res->OPEN4res_u.resok4.delegation);
+ }
+}
+
+static void
+sumres_open_confirm(char *buf, size_t buflen, void *obj)
+{
+ char *bp;
+ OPEN_CONFIRM4res *res = (OPEN_CONFIRM4res *)obj;
+
+ strncpy(buf, status_name(res->status), buflen);
+ if (res->status == NFS4_OK) {
+ bp = buf + strlen(buf);
+ snprintf(bp, buflen - (bp - buf), " %s",
+ sum_open_stateid(&res->OPEN_CONFIRM4res_u.resok4.
+ open_stateid));
+ }
+}
+
+static void
+dtlres_open_confirm(void *obj)
+{
+ OPEN_CONFIRM4res *res = (OPEN_CONFIRM4res *)obj;
+
+ dtl_nfsstat4(obj);
+ if (res->status == NFS4_OK) {
+ detail_open_stateid(&res->OPEN_CONFIRM4res_u.resok4.
+ open_stateid);
+ }
+}
+
+static void
+sumres_open_downgrd(char *buf, size_t buflen, void *obj)
+{
+ char *bp;
+ OPEN_DOWNGRADE4res *res = (OPEN_DOWNGRADE4res *)obj;
+
+ strncpy(buf, status_name(res->status), buflen);
+ if (res->status == NFS4_OK) {
+ bp = buf + strlen(buf);
+ snprintf(bp, buflen - (bp - buf), " %s",
+ sum_open_stateid(&res->OPEN_DOWNGRADE4res_u.resok4.
+ open_stateid));
+ }
+}
+
+static void
+dtlres_open_downgrd(void *obj)
+{
+ OPEN_DOWNGRADE4res *res = (OPEN_DOWNGRADE4res *)obj;
+
+ dtl_nfsstat4(obj);
+ if (res->status == NFS4_OK) {
+ detail_open_stateid(&res->OPEN_DOWNGRADE4res_u.resok4.
+ open_stateid);
+ }
+}
+
+static void
+sumres_read(char *buf, size_t buflen, void *obj)
+{
+ char *bp;
+ READ4res *res = (READ4res *)obj;
+
+ strncpy(buf, status_name(res->status), buflen);
+ if (res->status == NFS4_OK) {
+ bp = buf + strlen(buf);
+ snprintf(bp, buflen - (bp - buf), " (%u bytes) %s",
+ res->READ4res_u.resok4.data.data_len,
+ res->READ4res_u.resok4.eof ? "EOF" : "");
+ }
+}
+
+static void
+dtlres_read(void *obj)
+{
+ READ4res *res = (READ4res *)obj;
+
+ dtl_nfsstat4(obj);
+ if (res->status == NFS4_OK) {
+ sprintf(get_line(0, 0), "Count = %u bytes read",
+ res->READ4res_u.resok4.data.data_len);
+ sprintf(get_line(0, 0), "End of file = %s",
+ res->READ4res_u.resok4.eof ? "TRUE" : "FALSE");
+ }
+}
+
+static void
+sumres_readdir(char *buf, size_t buflen, void *obj)
+{
+ char *bp;
+ READDIR4res *res = (READDIR4res *)obj;
+ int num_entries = 0;
+ entry4 *ep;
+
+ strncpy(buf, status_name(res->status), buflen);
+ if (res->status == NFS4_OK) {
+ for (ep = res->READDIR4res_u.resok4.reply.entries;
+ ep != NULL;
+ ep = ep->nextentry)
+ num_entries++;
+ bp = buf + strlen(buf);
+ snprintf(bp, buflen - (bp - buf), " %d entries (%s)",
+ num_entries,
+ res->READDIR4res_u.resok4.reply.eof
+ ? "No more" : "More");
+ }
+}
+
+static void
+dtlres_readdir(void *obj)
+{
+ READDIR4res *res = (READDIR4res *)obj;
+ int num_entries = 0;
+ entry4 *ep;
+
+ dtl_nfsstat4(obj);
+ if (res->status == NFS4_OK) {
+ for (ep = res->READDIR4res_u.resok4.reply.entries;
+ ep != NULL;
+ ep = ep->nextentry) {
+ num_entries++;
+ sprintf(get_line(0, 0),
+ "------------------ entry #%d",
+ num_entries);
+ sprintf(get_line(0, 0), "Cookie = %llu",
+ ep->cookie);
+ sprintf(get_line(0, 0), "Name = %s",
+ component_name(&ep->name));
+ detail_fattr4(&ep->attrs);
+ }
+ if (num_entries == 0)
+ sprintf(get_line(0, 0), "(No entries)");
+ sprintf(get_line(0, 0), "EOF = %s",
+ res->READDIR4res_u.resok4.reply.eof ? "TRUE" : "FALSE");
+ sprintf(get_line(0, 0), "Verifer = %s",
+ tohex(res->READDIR4res_u.resok4.cookieverf,
+ NFS4_VERIFIER_SIZE));
+ }
+}
+
+static void
+sumres_readlnk(char *buf, size_t buflen, void *obj)
+{
+ char *bp;
+ READLINK4res *res = (READLINK4res *)obj;
+
+ strncpy(buf, status_name(res->status), buflen);
+ if (res->status == NFS4_OK) {
+ bp = buf + strlen(buf);
+ snprintf(bp, buflen - (bp - buf), " %s",
+ linktext_name(&res->READLINK4res_u.resok4.link));
+ }
+}
+
+static void
+dtlres_readlnk(void *obj)
+{
+ READLINK4res *res = (READLINK4res *)obj;
+
+ dtl_nfsstat4(obj);
+ if (res->status == NFS4_OK) {
+ sprintf(get_line(0, 0), "Link = %s",
+ linktext_name(&res->READLINK4res_u.resok4.link));
+ }
+}
+
+static void
+dtlres_remove(void *obj)
+{
+ REMOVE4res *res = (REMOVE4res *)obj;
+
+ dtl_nfsstat4(obj);
+ if (res->status == NFS4_OK) {
+ dtl_change_info("Change Information",
+ &res->REMOVE4res_u.resok4.cinfo);
+ }
+}
+
+static void
+dtlres_rename(void *obj)
+{
+ RENAME4res *res = (RENAME4res *)obj;
+
+ dtl_nfsstat4(obj);
+ if (res->status == NFS4_OK) {
+ dtl_change_info("Source Change Information",
+ &res->RENAME4res_u.resok4.source_cinfo);
+ dtl_change_info("Target Change Information",
+ &res->RENAME4res_u.resok4.target_cinfo);
+ }
+}
+
+static void
+sumres_secinfo(char *buf, size_t buflen, void *obj)
+{
+ char *bp;
+ SECINFO4res *res = (SECINFO4res *)obj;
+
+ strncpy(buf, status_name(res->status), buflen);
+ bp = buf + strlen(buf);
+ if (res->status == NFS4_OK) {
+ uint_t numinfo = res->SECINFO4res_u.resok4.SECINFO4resok_len;
+ secinfo4 *infop;
+
+ for (infop = res->SECINFO4res_u.resok4.SECINFO4resok_val;
+ numinfo != 0;
+ infop++, numinfo--) {
+ snprintf(bp, buflen - (bp - buf), " %s",
+ flavor_name(infop->flavor));
+ bp += strlen(bp);
+ }
+ }
+}
+
+static void
+dtlres_secinfo(void *obj)
+{
+ SECINFO4res *res = (SECINFO4res *)obj;
+
+ dtl_nfsstat4(obj);
+ if (res->status == NFS4_OK) {
+ uint_t numinfo =
+ res->SECINFO4res_u.resok4.SECINFO4resok_len;
+ secinfo4 *infop;
+
+ for (infop = res->SECINFO4res_u.resok4.SECINFO4resok_val;
+ numinfo != 0;
+ infop++, numinfo--) {
+ detail_secinfo4(infop);
+ }
+ }
+}
+
+static void
+sumres_setattr(char *buf, size_t buflen, void *obj)
+{
+ SETATTR4res *res = (SETATTR4res *)obj;
+
+ strncpy(buf, status_name(res->status), buflen);
+ sum_attr_bitmap(buf, buflen, &res->attrsset);
+}
+
+static void
+dtlres_setattr(void *obj)
+{
+ SETATTR4res *res = (SETATTR4res *)obj;
+
+ dtl_nfsstat4(obj);
+ detail_attr_bitmap("", &res->attrsset, NULL);
+}
+
+static void
+sumres_setclid(char *buf, size_t buflen, void *obj)
+{
+ char *bp;
+ SETCLIENTID4res *res = (SETCLIENTID4res *)obj;
+
+ strncpy(buf, status_name(res->status), buflen);
+ switch (res->status) {
+ case NFS_OK:
+ bp = buf + strlen(buf);
+ snprintf(bp, buflen - (bp - buf), " %s CFV=%s",
+ sum_clientid(res->SETCLIENTID4res_u.resok4.clientid),
+ tohex(res->SETCLIENTID4res_u.resok4.setclientid_confirm,
+ NFS4_VERIFIER_SIZE));
+ break;
+ case NFS4ERR_CLID_INUSE:
+ bp = buf + strlen(buf);
+ snprintf(bp, buflen - (bp - buf), " ID=%s Addr=%s",
+ res->SETCLIENTID4res_u.client_using.r_netid,
+ res->SETCLIENTID4res_u.client_using.r_addr);
+ break;
+ }
+}
+
+static void
+dtlres_setclid(void *obj)
+{
+ SETCLIENTID4res *res = (SETCLIENTID4res *)obj;
+
+ dtl_nfsstat4(obj);
+ switch (res->status) {
+ case NFS_OK:
+ detail_clientid(res->SETCLIENTID4res_u.resok4.clientid);
+ sprintf(get_line(0, 0), "Set Client ID Confirm Verifier = %s",
+ tohex(res->SETCLIENTID4res_u.resok4.setclientid_confirm,
+ NFS4_VERIFIER_SIZE));
+ break;
+ case NFS4ERR_CLID_INUSE:
+ sprintf(get_line(0, 0), "Used by Net ID = %s",
+ res->SETCLIENTID4res_u.client_using.r_netid);
+ sprintf(get_line(0, 0), "Used by Addr = %s",
+ res->SETCLIENTID4res_u.client_using.r_addr);
+ break;
+ }
+}
+
+static void
+sumres_write(char *buf, size_t buflen, void *obj)
+{
+ char *bp;
+ WRITE4res *res = (WRITE4res *)obj;
+
+ strncpy(buf, status_name(res->status), buflen);
+ if (res->status == NFS4_OK) {
+ bp = buf + strlen(buf);
+ snprintf(bp, buflen - (bp - buf), " %u (%s)",
+ res->WRITE4res_u.resok4.count,
+ stable_how4_name(res->WRITE4res_u.resok4.committed));
+ }
+}
+
+static void
+dtlres_write(void *obj)
+{
+ WRITE4res *res = (WRITE4res *)obj;
+
+ dtl_nfsstat4(obj);
+ if (res->status == NFS4_OK) {
+ sprintf(get_line(0, 0), "Count = %u bytes written",
+ res->WRITE4res_u.resok4.count);
+ sprintf(get_line(0, 0), "Stable = %s",
+ stable_how4_name(res->WRITE4res_u.resok4.committed));
+ sprintf(get_line(0, 0), "Verifier = %s",
+ tohex(res->WRITE4res_u.resok4.writeverf,
+ NFS4_VERIFIER_SIZE));
+ }
+}
+
+/*
+ * Print details about the nfs_resop4 that is next in the XDR stream.
+ */
+
+static void
+detail_nfs_resop4(void)
+{
+ int numres;
+ nfs_resop4 one_res;
+ void (*fmtproc)(void *);
+
+ numres = getxdr_long();
+ (void) sprintf(get_line(0, 0), "Number of results = %d",
+ numres);
+
+ while (numres-- > 0) {
+ bzero(&one_res, sizeof (one_res));
+
+ if (!xdr_nfs_resop4(&xdrm, &one_res)) {
+ xdr_free(xdr_nfs_resop4, (char *)&one_res);
+ longjmp(xdr_err, 1);
+ }
+
+ get_line(0, 0); /* blank line to separate ops */
+ sprintf(get_line(0, 0), "Op = %d (%s)",
+ one_res.resop, opcode_name(one_res.resop));
+ if (one_res.resop < num_opcodes)
+ fmtproc = opcode_info[one_res.resop].dtlres;
+ else if (one_res.resop == OP_ILLEGAL)
+ fmtproc = dtl_nfsstat4;
+ else
+ fmtproc = NULL;
+
+ if (fmtproc != NULL)
+ fmtproc(&one_res.nfs_resop4_u);
+
+ /* nfs4_skip_bytes set by xdr_nfs_resop4()() */
+ if (nfs4_skip_bytes)
+ nfs4_xdr_skip(nfs4_skip_bytes);
+
+ xdr_free(xdr_nfs_resop4, (char *)&one_res);
+ }
+}
+
+
+/*
+ * Print details about the nfs_cb_resop4 that is next in the XDR stream.
+ */
+
+static void
+detail_cb_resop4(void)
+{
+ int numres;
+ nfs_cb_resop4 one_res;
+ void (*fmtproc)(void *);
+
+ numres = getxdr_long();
+ (void) sprintf(get_line(0, 0), "Number of results = %d",
+ numres);
+
+ while (numres-- > 0) {
+ bzero(&one_res, sizeof (one_res));
+ if (!xdr_nfs_cb_resop4(&xdrm, &one_res))
+ longjmp(xdr_err, 1);
+
+ get_line(0, 0); /* blank line to separate ops */
+ sprintf(get_line(0, 0), "Op = %d (%s)",
+ one_res.resop, cb_opcode_name(one_res.resop));
+ if (one_res.resop < cb_num_opcodes)
+ fmtproc = cb_opcode_info[one_res.resop].dtlres;
+ else if (one_res.resop == OP_CB_ILLEGAL)
+ fmtproc = dtl_nfsstat4;
+ else
+ fmtproc = NULL;
+
+ if (fmtproc != NULL)
+ fmtproc(&one_res.nfs_cb_resop4_u);
+
+ xdr_free(xdr_nfs_cb_resop4, (char *)&one_res);
+ }
+}
+
+
+/*
+ * Return the name of a lock type.
+ */
+static char *
+lock_type_name(enum nfs_lock_type4 type)
+{
+ char *result;
+
+ switch (type) {
+ case READ_LT:
+ result = "READ";
+ break;
+ case WRITE_LT:
+ result = "WRITE";
+ break;
+ case READW_LT:
+ result = "READW";
+ break;
+ case WRITEW_LT:
+ result = "WRITEW";
+ break;
+ default:
+ result = "?";
+ break;
+ }
+
+ return (result);
+}
+
+/*
+ * Return the name of an opcode.
+ */
+
+static char *
+opcode_name(uint_t opnum)
+{
+ static char buf[20];
+
+ if (opnum < num_opcodes)
+ return (opcode_info[opnum].name);
+
+ if (opnum == OP_ILLEGAL)
+ return ("ILLEGAL");
+
+ sprintf(buf, "op %d", opnum);
+ return (buf);
+}
+
+/*
+ * Return the name of an opcode.
+ */
+static char *
+cb_opcode_name(uint_t opnum)
+{
+ static char buf[20];
+
+ if (opnum < cb_num_opcodes)
+ return (cb_opcode_info[opnum].name);
+
+ if (opnum == OP_CB_ILLEGAL)
+ return ("CB_ILLEGAL");
+
+ sprintf(buf, "op %d", opnum);
+ return (buf);
+}
+
+
+/*
+ * Fill in a summary string for the given access bitmask.
+ */
+
+static void
+sum_access4(char *buf, size_t buflen, uint32_t bits)
+{
+ buf[0] = '\0';
+
+ if (bits & ACCESS4_READ)
+ (void) strncat(buf, "rd,", buflen);
+ if (bits & ACCESS4_LOOKUP)
+ (void) strncat(buf, "lk,", buflen);
+ if (bits & ACCESS4_MODIFY)
+ (void) strncat(buf, "mo,", buflen);
+ if (bits & ACCESS4_EXTEND)
+ (void) strncat(buf, "ext,", buflen);
+ if (bits & ACCESS4_DELETE)
+ (void) strncat(buf, "dl,", buflen);
+ if (bits & ACCESS4_EXECUTE)
+ (void) strncat(buf, "exc,", buflen);
+ if (buf[0] != '\0')
+ buf[strlen(buf) - 1] = '\0';
+}
+
+/*
+ * Print detail information about the given access bitmask.
+ */
+
+static void
+detail_access4(char *descrip, uint32_t bits)
+{
+ sprintf(get_line(0, 0), "%s = 0x%08x", descrip, bits);
+
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(bits, ACCESS4_READ, "Read", "(no read)"));
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(bits, ACCESS4_LOOKUP, "Lookup", "(no lookup)"));
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(bits, ACCESS4_MODIFY, "Modify", "(no modify)"));
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(bits, ACCESS4_EXTEND, "Extend", "(no extend)"));
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(bits, ACCESS4_DELETE, "Delete", "(no delete)"));
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(bits, ACCESS4_EXECUTE, "Execute", "(no execute)"));
+}
+
+
+/*
+ * Fill in a summary string for the given open_claim4.
+ */
+static void
+sum_name(char *buf, size_t buflen, open_claim4 *claim)
+{
+ char *bp = buf;
+
+ switch (claim->claim) {
+ case CLAIM_NULL:
+ snprintf(bp, buflen, "%s ",
+ component_name(&claim->open_claim4_u.file));
+ break;
+ case CLAIM_PREVIOUS:
+ break;
+ case CLAIM_DELEGATE_CUR:
+ snprintf(bp, buflen, "%s ",
+ component_name(&claim->open_claim4_u.
+ delegate_cur_info.file));
+ break;
+ case CLAIM_DELEGATE_PREV:
+ snprintf(bp, buflen, "%s ",
+ component_name(&claim->open_claim4_u.
+ file_delegate_prev));
+ break;
+ }
+}
+
+/*
+ * Fill in a summary string for the given open_claim4.
+ */
+static void
+sum_claim(char *buf, size_t buflen, open_claim4 *claim)
+{
+ char *bp = buf;
+
+ switch (claim->claim) {
+ case CLAIM_NULL:
+ snprintf(bp, buflen, " CT=N");
+ break;
+ case CLAIM_PREVIOUS:
+ snprintf(bp, buflen, " CT=P DT=%s",
+ get_deleg_typestr(claim->open_claim4_u.delegate_type));
+ break;
+ case CLAIM_DELEGATE_CUR:
+ snprintf(bp, buflen, " CT=DC %s",
+ sum_deleg_stateid(&claim->open_claim4_u.
+ delegate_cur_info.delegate_stateid));
+ break;
+ case CLAIM_DELEGATE_PREV:
+ snprintf(bp, buflen, " CT=DP");
+ break;
+ default:
+ snprintf(bp, buflen, " CT=?");
+ break;
+ }
+}
+
+static char *
+get_deleg_typestr(open_delegation_type4 dt)
+{
+ char *str = "";
+
+ switch (dt) {
+ case OPEN_DELEGATE_NONE:
+ str = "N";
+ break;
+ case OPEN_DELEGATE_READ:
+ str = "R";
+ break;
+ case OPEN_DELEGATE_WRITE:
+ str = "W";
+ break;
+ default:
+ str = "?";
+ }
+
+ return (str);
+}
+
+/*
+ * Print detail information for the given open_claim4.
+ */
+
+static void
+detail_claim(open_claim4 *claim)
+{
+ sprintf(get_line(0, 0), "Claim Type = %d (%s)",
+ claim->claim, claim_name(claim->claim));
+
+ switch (claim->claim) {
+ case CLAIM_NULL:
+ detail_compname4(&claim->open_claim4_u.file);
+ break;
+ case CLAIM_PREVIOUS:
+ sprintf(get_line(0, 0), "Delegate Type = %s (val = %d)",
+ get_deleg_typestr(claim->open_claim4_u.delegate_type),
+ claim->open_claim4_u.delegate_type);
+ break;
+ case CLAIM_DELEGATE_CUR:
+ detail_compname4(&claim->open_claim4_u.delegate_cur_info.file);
+ detail_deleg_stateid(&claim->open_claim4_u.delegate_cur_info.
+ delegate_stateid);
+ break;
+ case CLAIM_DELEGATE_PREV:
+ detail_compname4(&claim->open_claim4_u.file_delegate_prev);
+ break;
+ }
+}
+
+/*
+ * Return a summary string for the given clientid4.
+ */
+static char *
+sum_clientid(clientid4 client)
+{
+ static char buf[50];
+
+ snprintf(buf, sizeof (buf), "CL=%llx", client);
+
+ return (buf);
+}
+
+/*
+ * Print a detail string for the given clientid4.
+ */
+static void
+detail_clientid(clientid4 client)
+{
+ sprintf(get_line(0, 0), "Client ID = %llx", client);
+}
+
+/*
+ * Write a summary string for the given delegation into buf.
+ */
+
+static void
+sum_delegation(char *buf, size_t buflen, open_delegation4 *delp)
+{
+ switch (delp->delegation_type) {
+ case OPEN_DELEGATE_NONE:
+ snprintf(buf, buflen, " DT=N");
+ break;
+ case OPEN_DELEGATE_READ:
+ snprintf(buf, buflen, " DT=R %s",
+ sum_deleg_stateid(&delp->open_delegation4_u.write.
+ stateid));
+ break;
+ case OPEN_DELEGATE_WRITE:
+ snprintf(buf, buflen, " DT=W %s %s",
+ sum_deleg_stateid(&delp->open_delegation4_u.write.
+ stateid),
+ sum_space_limit(&delp->open_delegation4_u.write.
+ space_limit));
+ break;
+ default:
+ snprintf(buf, buflen, " DT=?");
+ break;
+ }
+}
+
+static void
+detail_delegation(open_delegation4 *delp)
+{
+ sprintf(get_line(0, 0), "Delegation Type = %d (%s)",
+ delp->delegation_type,
+ delegation_type_name(delp->delegation_type));
+
+ switch (delp->delegation_type) {
+ case OPEN_DELEGATE_NONE:
+ /* no-op */
+ break;
+ case OPEN_DELEGATE_READ:
+ detail_deleg_stateid(&delp->open_delegation4_u.read.stateid);
+ sprintf(get_line(0, 0), "Recall = %s",
+ delp->open_delegation4_u.read.recall ?
+ "TRUE" : "FALSE");
+ sprintf(get_line(0, 0), "[nfsacl4]");
+ break;
+ case OPEN_DELEGATE_WRITE:
+ detail_deleg_stateid(&delp->open_delegation4_u.write.stateid);
+ sprintf(get_line(0, 0), "Recall = %s",
+ delp->open_delegation4_u.write.recall ?
+ "TRUE" : "FALSE");
+ detail_space_limit(&delp->open_delegation4_u.write.
+ space_limit);
+ sprintf(get_line(0, 0), "[nfsacl4]");
+ break;
+ }
+}
+
+
+static void
+detail_open_owner(open_owner4 *owner)
+{
+ sprintf(get_line(0, 0), "Open Owner hash = [%04X] ",
+ owner_hash(&owner->owner));
+ sprintf(get_line(0, 0), " len = %u val = %s ",
+ owner->owner.owner_len,
+ tohex(owner->owner.owner_val, owner->owner.owner_len));
+ detail_clientid(owner->clientid);
+}
+
+static void
+detail_lock_owner(lock_owner4 *owner)
+{
+ sprintf(get_line(0, 0), "Lock Owner hash = [%04X] ",
+ owner_hash(&owner->owner));
+ sprintf(get_line(0, 0), " len = %u val = %s ",
+ owner->owner.owner_len,
+ tohex(owner->owner.owner_val, owner->owner.owner_len));
+ detail_clientid(owner->clientid);
+}
+
+static void
+sum_openflag(char *bufp, int buflen, openflag4 *flagp)
+{
+ if (flagp->opentype == OPEN4_CREATE) {
+ switch (flagp->openflag4_u.how.mode) {
+ case UNCHECKED4:
+ snprintf(bufp, buflen, "OT=CR(U)");
+ break;
+ case GUARDED4:
+ snprintf(bufp, buflen, "OT=CR(G)");
+ break;
+ case EXCLUSIVE4:
+ snprintf(bufp, buflen, "OT=CR(E)");
+ break;
+ default:
+ snprintf(bufp, buflen, "OT=CR(?:%d)",
+ flagp->openflag4_u.how.mode);
+ break;
+ }
+ } else
+ snprintf(bufp, buflen, "OT=NC");
+}
+
+static void
+detail_openflag(openflag4 *flagp)
+{
+ sprintf(get_line(0, 0), "Open Type = %s",
+ flagp->opentype == OPEN4_CREATE ? "CREATE" : "NOCREATE");
+ if (flagp->opentype == OPEN4_CREATE)
+ detail_createhow4(&flagp->openflag4_u.how);
+}
+
+/*
+ * Fill in buf with the given path.
+ */
+static void
+sum_pathname4(char *buf, size_t buflen, pathname4 *pathp)
+{
+ char *bp = buf;
+ uint_t component;
+
+ for (component = 0; component < pathp->pathname4_len;
+ component++) {
+ snprintf(bp, buflen - (bp - buf),
+ component == 0 ? "%s" : "/%s",
+ component_name(&pathp->pathname4_val[component]));
+ bp += strlen(bp);
+ }
+}
+
+static void
+sum_compname4(char *buf, size_t buflen, component4 *comp)
+{
+ snprintf(buf, buflen, "%s", component_name(comp));
+}
+
+static void
+detail_compname4(component4 *comp)
+{
+ sprintf(get_line(0, 0), "%s", component_name(comp));
+}
+
+static void
+detail_pathname4(pathname4 *pathp)
+{
+ char *bp = get_line(0, 0);
+ uint_t component;
+
+ sprintf(bp, "File name = ");
+ bp += strlen(bp);
+
+ for (component = 0; component < pathp->pathname4_len; component++) {
+ sprintf(bp, component == 0 ? "%s" : "/%s",
+ component_name(&pathp->pathname4_val[component]));
+ bp += strlen(bp);
+ }
+}
+
+/*
+ * Print detail information about the rpcsec_gss_info that is XDR-encoded
+ * at mem.
+ */
+
+static void
+detail_rpcsec_gss(rpcsec_gss_info *info)
+{
+ sprintf(get_line(0, 0), "OID = %s",
+ tohex(info->oid.sec_oid4_val, info->oid.sec_oid4_len));
+ sprintf(get_line(0, 0), "QOP = %u", info->qop);
+ sprintf(get_line(0, 0), "Service = %d (%s)",
+ info->service, gss_svc_name(info->service));
+}
+
+/*
+ * Print detail information about the given secinfo4.
+ */
+
+static void
+detail_secinfo4(secinfo4 *infop)
+{
+ sprintf(get_line(0, 0), "Flavor = %d (%s)",
+ infop->flavor, flavor_name(infop->flavor));
+ switch (infop->flavor) {
+ case RPCSEC_GSS:
+ detail_rpcsec_gss(&infop->secinfo4_u.flavor_info);
+ break;
+ }
+}
+
+
+/*
+ * Return a summary string corresponding to the given nfs_space_limit4.
+ */
+
+static char *
+sum_space_limit(nfs_space_limit4 *limitp)
+{
+ static char buf[64];
+ int buflen = sizeof (buf);
+
+ buf[0] = '\0';
+ switch (limitp->limitby) {
+ case NFS_LIMIT_SIZE:
+ snprintf(buf, buflen, "LB=SZ(%llu)",
+ limitp->nfs_space_limit4_u.filesize);
+ break;
+ case NFS_LIMIT_BLOCKS:
+ snprintf(buf, buflen, "LB=BL(%u*%u)",
+ limitp->nfs_space_limit4_u.mod_blocks.num_blocks,
+ limitp->nfs_space_limit4_u.mod_blocks.bytes_per_block);
+ break;
+ default:
+ snprintf(buf, buflen, "LB=?(%d)", limitp->limitby);
+ break;
+ }
+
+ return (buf);
+}
+
+/*
+ * Print detail information about the given nfs_space_limit4.
+ */
+
+static void
+detail_space_limit(nfs_space_limit4 *limitp)
+{
+ sprintf(get_line(0, 0), "LimitBy = %d (%s)",
+ limitp->limitby,
+ limitby_name(limitp->limitby));
+
+ switch (limitp->limitby) {
+ case NFS_LIMIT_SIZE:
+ sprintf(get_line(0, 0), "Bytes = %llu",
+ limitp->nfs_space_limit4_u.filesize);
+ break;
+ case NFS_LIMIT_BLOCKS:
+ sprintf(get_line(0, 0), "Blocks = %u",
+ limitp->nfs_space_limit4_u.mod_blocks.num_blocks);
+ sprintf(get_line(0, 0), "Bytes Per Block = %u",
+ limitp->nfs_space_limit4_u.mod_blocks.bytes_per_block);
+ break;
+ }
+}
+
+
+/*
+ * Return the short name of a file type.
+ */
+
+static char *
+sum_type_name(nfs_ftype4 type)
+{
+ static char buf[20];
+
+ if (type < num_ftypes)
+ return (ftype_names[type].short_name);
+ else {
+ sprintf(buf, "type %d", type);
+ return (buf);
+ }
+}
+
+
+/*
+ * Return string with long/short flag names
+ */
+
+static char *
+get_flags(uint_t flag, ftype_names_t *names, uint_t num_flags, int shortname,
+ char *prefix)
+{
+ static char buf[200];
+ char *bp = buf, *str;
+ int i, len, blen = sizeof (buf);
+ ftype_names_t *fn = NULL;
+
+ *bp = '\0';
+
+ if (prefix) {
+ snprintf(bp, blen, "%s", prefix);
+ bp += (len = sizeof (bp));
+ blen -= len;
+ }
+
+ for (i = 0; i < 32; i++)
+ if (flag & (1 << i)) {
+ fn = names + (i < num_flags ? i : num_flags);
+ str = (shortname ? fn->short_name : fn->long_name);
+
+ snprintf(bp, blen, "%s,", str);
+ bp += (len = strlen(bp));
+ blen -= len;
+ }
+
+ if (fn)
+ *(bp - 1) = '\0';
+ else
+ *buf = '\0';
+
+ return (buf);
+}
+
+
+/*
+ * Return the long name of a file type.
+ */
+
+static char *
+detail_type_name(nfs_ftype4 type)
+{
+ static char buf[20];
+
+ if (type < num_ftypes)
+ return (ftype_names[type].long_name);
+ else {
+ sprintf(buf, "type %d", type);
+ return (buf);
+ }
+}
+
+/*
+ * Return the name of an attribute.
+ */
+
+static char *
+attr_name(uint_t attrnum)
+{
+ static char buf[20];
+
+ if (attrnum < MAX_ATTRIBUTES)
+ return (attr_info[attrnum].name);
+ else {
+ sprintf(buf, "attr #%d", attrnum);
+ return (buf);
+ }
+}
+
+/*
+ * Return the name of the given open_claim_type4.
+ */
+
+static char *
+claim_name(enum open_claim_type4 claim_type)
+{
+ char *result;
+
+ switch (claim_type) {
+ case CLAIM_NULL:
+ result = "NULL";
+ break;
+ case CLAIM_PREVIOUS:
+ result = "PREVIOUS";
+ break;
+ case CLAIM_DELEGATE_CUR:
+ result = "DELEGATE CURRENT";
+ break;
+ case CLAIM_DELEGATE_PREV:
+ result = "DELEGATE PREVIOUS";
+ break;
+ default:
+ result = "?";
+ break;
+ }
+
+ return (result);
+}
+
+/*
+ * Return a string naming the given delegation.
+ */
+
+static char *
+delegation_type_name(enum open_delegation_type4 type)
+{
+ char *result;
+
+ switch (type) {
+ case OPEN_DELEGATE_NONE:
+ result = "NONE";
+ break;
+ case OPEN_DELEGATE_READ:
+ result = "READ";
+ break;
+ case OPEN_DELEGATE_WRITE:
+ result = "WRITE";
+ break;
+ default:
+ result = "?";
+ break;
+ }
+
+ return (result);
+}
+
+/*
+ * Return the name of the given authentication flavor.
+ */
+
+static char *
+flavor_name(uint_t flavor)
+{
+ char *result;
+ static char buf[50];
+
+ switch (flavor) {
+ case AUTH_SYS:
+ result = "AUTH_SYS";
+ break;
+ case AUTH_NONE:
+ result = "AUTH_NONE";
+ break;
+ case AUTH_DH:
+ result = "AUTH_DH";
+ break;
+ case RPCSEC_GSS:
+ result = "RPCSEC_GSS";
+ break;
+ default:
+ sprintf(buf, "[flavor %d]", flavor);
+ result = buf;
+ break;
+ }
+
+ return (result);
+}
+
+/*
+ * Return the name of the given rpc_gss_svc_t.
+ */
+
+static char *
+gss_svc_name(rpc_gss_svc_t svc)
+{
+ char *result;
+ static char buf[50];
+
+ switch (svc) {
+ case RPC_GSS_SVC_NONE:
+ result = "NONE";
+ break;
+ case RPC_GSS_SVC_INTEGRITY:
+ result = "INTEGRITY";
+ break;
+ case RPC_GSS_SVC_PRIVACY:
+ result = "PRIVACY";
+ break;
+ default:
+ sprintf(buf, "Service %d", svc);
+ result = buf;
+ break;
+ }
+
+ return (result);
+}
+
+/*
+ * Return a string name for the given limit_by4.
+ */
+
+static char *
+limitby_name(enum limit_by4 limitby)
+{
+ char *result;
+
+ switch (limitby) {
+ case NFS_LIMIT_SIZE:
+ result = "SIZE";
+ break;
+ case NFS_LIMIT_BLOCKS:
+ result = "BLOCKS";
+ break;
+ default:
+ result = "?";
+ break;
+ }
+
+ return (result);
+}
+
+static char *
+status_name(int status)
+{
+ char *p;
+
+ switch (status) {
+ case NFS4_OK: p = "NFS4_OK"; break;
+ case NFS4ERR_PERM: p = "NFS4ERR_PERM"; break;
+ case NFS4ERR_NOENT: p = "NFS4ERR_NOENT"; break;
+ case NFS4ERR_IO: p = "NFS4ERR_IO"; break;
+ case NFS4ERR_NXIO: p = "NFS4ERR_NXIO"; break;
+ case NFS4ERR_ACCESS: p = "NFS4ERR_ACCESS"; break;
+ case NFS4ERR_EXIST: p = "NFS4ERR_EXIST"; break;
+ case NFS4ERR_XDEV: p = "NFS4ERR_XDEV"; break;
+ case NFS4ERR_NOTDIR: p = "NFS4ERR_NOTDIR"; break;
+ case NFS4ERR_ISDIR: p = "NFS4ERR_ISDIR"; break;
+ case NFS4ERR_INVAL: p = "NFS4ERR_INVAL"; break;
+ case NFS4ERR_FBIG: p = "NFS4ERR_FBIG"; break;
+ case NFS4ERR_NOSPC: p = "NFS4ERR_NOSPC"; break;
+ case NFS4ERR_ROFS: p = "NFS4ERR_ROFS"; break;
+ case NFS4ERR_MLINK: p = "NFS4ERR_MLINK"; break;
+ case NFS4ERR_NAMETOOLONG:p = "NFS4ERR_NAMETOOLONG"; break;
+ case NFS4ERR_NOTEMPTY: p = "NFS4ERR_NOTEMPTY"; break;
+ case NFS4ERR_DQUOT: p = "NFS4ERR_DQUOT"; break;
+ case NFS4ERR_STALE: p = "NFS4ERR_STALE"; break;
+ case NFS4ERR_BADHANDLE: p = "NFS4ERR_BADHANDLE"; break;
+ case NFS4ERR_BAD_COOKIE:p = "NFS4ERR_BAD_COOKIE"; break;
+ case NFS4ERR_NOTSUPP: p = "NFS4ERR_NOTSUPP"; break;
+ case NFS4ERR_TOOSMALL: p = "NFS4ERR_TOOSMALL"; break;
+ case NFS4ERR_SERVERFAULT:p = "NFS4ERR_SERVERFAULT"; break;
+ case NFS4ERR_BADTYPE: p = "NFS4ERR_BADTYPE"; break;
+ case NFS4ERR_DELAY: p = "NFS4ERR_DELAY"; break;
+ case NFS4ERR_SAME: p = "NFS4ERR_SAME"; break;
+ case NFS4ERR_DENIED: p = "NFS4ERR_DENIED"; break;
+ case NFS4ERR_EXPIRED: p = "NFS4ERR_EXPIRED"; break;
+ case NFS4ERR_LOCKED: p = "NFS4ERR_LOCKED"; break;
+ case NFS4ERR_GRACE: p = "NFS4ERR_GRACE"; break;
+ case NFS4ERR_FHEXPIRED: p = "NFS4ERR_FHEXPIRED"; break;
+ case NFS4ERR_SHARE_DENIED: p = "NFS4ERR_SHARE_DENIED"; break;
+ case NFS4ERR_WRONGSEC: p = "NFS4ERR_WRONGSEC"; break;
+ case NFS4ERR_CLID_INUSE: p = "NFS4ERR_CLID_INUSE"; break;
+ case NFS4ERR_RESOURCE: p = "NFS4ERR_RESOURCE"; break;
+ case NFS4ERR_MOVED: p = "NFS4ERR_MOVED"; break;
+ case NFS4ERR_NOFILEHANDLE: p = "NFS4ERR_NOFILEHANDLE"; break;
+ case NFS4ERR_MINOR_VERS_MISMATCH: p =
+ "NFS4ERR_MINOR_VERS_MISMATCH"; break;
+ case NFS4ERR_STALE_CLIENTID: p = "NFS4ERR_STALE_CLIENTID"; break;
+ case NFS4ERR_STALE_STATEID: p = "NFS4ERR_STALE_STATEID"; break;
+ case NFS4ERR_OLD_STATEID: p = "NFS4ERR_OLD_STATEID"; break;
+ case NFS4ERR_BAD_STATEID: p = "NFS4ERR_BAD_STATEID"; break;
+ case NFS4ERR_BAD_SEQID: p = "NFS4ERR_BAD_SEQID"; break;
+ case NFS4ERR_NOT_SAME: p = "NFS4ERR_NOT_SAME"; break;
+ case NFS4ERR_LOCK_RANGE: p = "NFS4ERR_LOCK_RANGE"; break;
+ case NFS4ERR_SYMLINK: p = "NFS4ERR_SYMLINK"; break;
+ case NFS4ERR_RESTOREFH: p = "NFS4ERR_RESTOREFH"; break;
+ case NFS4ERR_LEASE_MOVED: p = "NFS4ERR_LEASE_MOVED"; break;
+ case NFS4ERR_ATTRNOTSUPP: p = "NFS4ERR_ATTRNOTSUPP"; break;
+ case NFS4ERR_NO_GRACE: p = "NFS4ERR_NO_GRACE"; break;
+ case NFS4ERR_RECLAIM_BAD: p = "NFS4ERR_RECLAIM_BAD"; break;
+ case NFS4ERR_RECLAIM_CONFLICT: p = "NFS4ERR_RECLAIM_CONFLICT"; break;
+ case NFS4ERR_BADXDR: p = "NFS4ERR_BADXDR"; break;
+ case NFS4ERR_LOCKS_HELD: p = "NFS4ERR_LOCKS_HELD"; break;
+ case NFS4ERR_OPENMODE: p = "NFS4ERR_OPENMODE"; break;
+ case NFS4ERR_BADOWNER: p = "NFS4ERR_BADOWNER"; break;
+ case NFS4ERR_BADCHAR: p = "NFS4ERR_BADCHAR"; break;
+ case NFS4ERR_BADNAME: p = "NFS4ERR_BADNAME"; break;
+ case NFS4ERR_BAD_RANGE: p = "NFS4ERR_BAD_RANGE"; break;
+ case NFS4ERR_LOCK_NOTSUPP: p = "NFS4ERR_LOCK_NOTSUPP"; break;
+ case NFS4ERR_OP_ILLEGAL: p = "NFS4ERR_OP_ILLEGAL"; break;
+ case NFS4ERR_DEADLOCK: p = "NFS4ERR_DEADLOCK"; break;
+ case NFS4ERR_FILE_OPEN: p = "NFS4ERR_FILE_OPEN"; break;
+ case NFS4ERR_ADMIN_REVOKED: p = "NFS4ERR_ADMIN_REVOKED"; break;
+ case NFS4ERR_CB_PATH_DOWN: p = "NFS4ERR_CB_PATH_DOWN"; break;
+ default: p = "(unknown error)"; break;
+ }
+
+ return (p);
+}
+
+char *
+nfsstat4_to_name(int status)
+{
+ return (status_name(status));
+}
+
+/*
+ * Attribute print functions. See attr_info_t.
+ */
+
+static void
+prt_supported_attrs(XDR *xdr)
+{
+ static bitmap4 val;
+
+ if (!xdr_bitmap4(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Supported Attributes:");
+ detail_attr_bitmap("\t", &val, NULL);
+ xdr_free(xdr_bitmap4, (char *)&val);
+}
+
+static void
+prt_type(XDR *xdr)
+{
+ nfs_ftype4 val;
+
+ if (!xdr_nfs_ftype4(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Type = %s", sum_type_name(val));
+}
+
+static void
+prt_fh_expire_type(XDR *xdr)
+{
+ fattr4_fh_expire_type val;
+ char *buf;
+ bool_t first = TRUE;
+
+ if (!xdr_fattr4_fh_expire_type(xdr, &val))
+ longjmp(xdr_err, 1);
+ buf = get_line(0, 0);
+
+ sprintf(buf, "Filehandle expire type = ");
+ if ((val & (FH4_NOEXPIRE_WITH_OPEN | FH4_VOLATILE_ANY |
+ FH4_VOL_MIGRATION | FH4_VOL_RENAME)) == 0) {
+ strcat(buf, "Persistent");
+ return;
+ }
+ if (val & FH4_NOEXPIRE_WITH_OPEN) {
+ strcat(buf, "No Expire With OPEN");
+ first = FALSE;
+ }
+ if (val & FH4_VOLATILE_ANY) {
+ if (first)
+ first = FALSE;
+ else
+ strcat(buf, ", ");
+ strcat(buf, "Volatile at any time");
+ }
+ if (val & FH4_VOL_MIGRATION) {
+ if (first)
+ first = FALSE;
+ else
+ strcat(buf, ", ");
+ strcat(buf, "Volatile at Migration");
+ }
+ if (val & FH4_VOL_RENAME) {
+ if (first)
+ first = FALSE;
+ else
+ strcat(buf, ", ");
+ strcat(buf, "Volatile at Rename");
+ }
+}
+
+static void
+prt_change(XDR *xdr)
+{
+ changeid4 val;
+
+ if (!xdr_changeid4(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Change ID = 0x%llx", val);
+ /* XXX print as time_t, too? */
+}
+
+static void
+prt_size(XDR *xdr)
+{
+ uint64_t val;
+
+ if (!xdr_uint64_t(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Size = %llu", val);
+}
+
+static void
+prt_link_support(XDR *xdr)
+{
+ bool_t val;
+
+ if (!xdr_bool(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Link Support = %s",
+ val ? "TRUE" : "FALSE");
+}
+
+static void
+prt_symlink_support(XDR *xdr)
+{
+ bool_t val;
+
+ if (!xdr_bool(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Symlink Support = %s",
+ val ? "TRUE" : "FALSE");
+}
+
+static void
+prt_named_attr(XDR *xdr)
+{
+ bool_t val;
+
+ if (!xdr_bool(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Has Named Attributes = %s",
+ val ? "TRUE" : "FALSE");
+}
+
+static void
+prt_fsid(XDR *xdr)
+{
+ fsid4 val;
+
+ if (!xdr_fsid4(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "FS ID: Major = %llx, Minor = %llx",
+ val.major, val.minor);
+}
+
+static void
+prt_unique_handles(XDR *xdr)
+{
+ bool_t val;
+
+ if (!xdr_bool(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Unique Handles = %s",
+ val ? "TRUE" : "FALSE");
+}
+
+static void
+prt_lease_time(XDR *xdr)
+{
+ uint32_t val;
+
+ if (!xdr_uint32_t(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Lease Time = %u", val);
+}
+
+static void
+prt_rdattr_error(XDR *xdr)
+{
+ nfsstat4 val;
+
+ if (!xdr_nfsstat4(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Rdattr Error = %u (%s)",
+ val, status_name(val));
+}
+
+static void
+prt_acl(XDR *xdr)
+{
+ static fattr4_acl val;
+ char buffy[NFS4_OPAQUE_LIMIT];
+ int i, len;
+
+ if (!xdr_fattr4_acl(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "ACL of %d entries", val.fattr4_acl_len);
+ for (i = 0; i < val.fattr4_acl_len; i++) {
+ sprintf(get_line(0, 0), "nfsace4[%d]", i);
+
+ sprintf(get_line(0, 0), " type = %x",
+ val.fattr4_acl_val[i].type);
+ detail_acetype4(val.fattr4_acl_val[i].type);
+
+ sprintf(get_line(0, 0), " flags = %x",
+ val.fattr4_acl_val[i].flag);
+ detail_aceflag4(val.fattr4_acl_val[i].flag);
+
+ sprintf(get_line(0, 0), " mask = %x",
+ val.fattr4_acl_val[i].access_mask);
+ detail_acemask4(val.fattr4_acl_val[i].access_mask);
+
+ len = val.fattr4_acl_val[i].who.utf8string_len;
+ if (len >= NFS4_OPAQUE_LIMIT)
+ len = NFS4_OPAQUE_LIMIT - 1;
+ (void) strncpy(buffy, val.fattr4_acl_val[i].who.utf8string_val,
+ len);
+ buffy[len] = '\0';
+ sprintf(get_line(0, 0), " who = %s", buffy);
+ }
+ xdr_free(xdr_fattr4_acl, (char *)&val);
+}
+
+static void
+detail_acetype4(acetype4 type)
+{
+ if (type >= ACETYPE4_NAMES_MAX) {
+ sprintf(get_line(0, 0), " unknown type");
+ } else {
+ sprintf(get_line(0, 0), " %s", acetype4_names[type]);
+ }
+}
+
+static void
+detail_uint32_bitmap(uint32_t mask, char *mask_names[], int names_max)
+{
+ char buffy[BUFSIZ], *name;
+ char *indent = " ";
+ char *spacer = " ";
+ int pending = 0;
+ int bit;
+ int len, namelen, spacelen;
+
+ strcpy(buffy, indent);
+ len = strlen(buffy);
+ spacelen = strlen(spacer);
+
+ for (bit = 0; bit < names_max; bit++) {
+ if (mask & (1 << bit)) {
+ name = mask_names[bit];
+ namelen = strlen(name);
+ /* 80 - 6 for "NFS: " = 74 */
+ if ((len + spacelen + namelen) >= 74) {
+ sprintf(get_line(0, 0), "%s", buffy);
+ strcpy(buffy, indent);
+ len = strlen(buffy);
+ pending = 0;
+ }
+ (void) strlcat(buffy, spacer, sizeof (buffy));
+ (void) strlcat(buffy, name, sizeof (buffy));
+ pending = 1;
+ len += spacelen + namelen;
+ }
+ }
+ if (pending)
+ sprintf(get_line(0, 0), "%s", buffy);
+}
+
+static void
+detail_aceflag4(aceflag4 flag)
+{
+ detail_uint32_bitmap(flag, aceflag4_names, ACEFLAG4_NAMES_MAX);
+}
+
+static void
+detail_acemask4(acemask4 mask)
+{
+ detail_uint32_bitmap(mask, acemask4_names, ACEMASK4_NAMES_MAX);
+}
+
+static void
+prt_aclsupport(XDR *xdr)
+{
+ fattr4_aclsupport val;
+
+ if (!xdr_fattr4_aclsupport(xdr, &val))
+ longjmp(xdr_err, 1);
+ if (val & ACL4_SUPPORT_ALLOW_ACL)
+ sprintf(get_line(0, 0), "ALLOW ACL Supported");
+ if (val & ACL4_SUPPORT_DENY_ACL)
+ sprintf(get_line(0, 0), "DENY ACL Supported");
+ if (val & ACL4_SUPPORT_AUDIT_ACL)
+ sprintf(get_line(0, 0), "AUDIT ACL Supported");
+ if (val & ACL4_SUPPORT_ALARM_ACL)
+ sprintf(get_line(0, 0), "ALARM ACL Supported");
+}
+
+static void
+prt_archive(XDR *xdr)
+{
+ bool_t val;
+
+ if (!xdr_bool(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Archived = %s",
+ val ? "TRUE" : "FALSE");
+}
+
+static void
+prt_cansettime(XDR *xdr)
+{
+ bool_t val;
+
+ if (!xdr_bool(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Server Can Set Time = %s",
+ val ? "TRUE" : "FALSE");
+}
+
+static void
+prt_case_insensitive(XDR *xdr)
+{
+ bool_t val;
+
+ if (!xdr_bool(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Case Insensitive Lookups = %s",
+ val ? "TRUE" : "FALSE");
+}
+
+static void
+prt_case_preserving(XDR *xdr)
+{
+ bool_t val;
+
+ if (!xdr_bool(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Case Preserving = %s",
+ val ? "TRUE" : "FALSE");
+}
+
+static void
+prt_chown_restricted(XDR *xdr)
+{
+ bool_t val;
+
+ if (!xdr_bool(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Chown Is Restricted = %s",
+ val ? "TRUE" : "FALSE");
+}
+
+static void
+prt_filehandle(XDR *xdr)
+{
+ static nfs_fh4 val;
+
+ if (!xdr_nfs_fh4(xdr, &val))
+ longjmp(xdr_err, 1);
+ detail_fh4(&val);
+ xdr_free(xdr_nfs_fh4, (char *)&val);
+}
+
+static void
+prt_fileid(XDR *xdr)
+{
+ uint64_t val;
+
+ if (!xdr_uint64_t(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "File ID = %llu", val);
+}
+
+static void
+prt_mounted_on_fileid(XDR *xdr)
+{
+ uint64_t val;
+
+ if (!xdr_uint64_t(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Mounted On File ID = %llu", val);
+}
+
+static void
+prt_files_avail(XDR *xdr)
+{
+ uint64_t val;
+
+ if (!xdr_uint64_t(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Files Available = %llu", val);
+}
+
+static void
+prt_files_free(XDR *xdr)
+{
+ uint64_t val;
+
+ if (!xdr_uint64_t(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Files Free = %llu", val);
+}
+
+static void
+prt_files_total(XDR *xdr)
+{
+ uint64_t val;
+
+ if (!xdr_uint64_t(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Files Total = %llu", val);
+}
+
+static void
+prt_fs_locations(XDR *xdr)
+{
+ static fs_locations4 val;
+
+ if (!xdr_fs_locations4(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "[fs_locations]"); /* XXX */
+ detail_pathname4(&val.fs_root);
+ xdr_free(xdr_fs_locations4, (char *)&val);
+}
+
+static void
+prt_hidden(XDR *xdr)
+{
+ bool_t val;
+
+ if (!xdr_bool(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Hidden = %s",
+ val ? "TRUE" : "FALSE");
+}
+
+static void
+prt_homogeneous(XDR *xdr)
+{
+ bool_t val;
+
+ if (!xdr_bool(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "FS Is Homogeneous = %s",
+ val ? "TRUE" : "FALSE");
+}
+
+static void
+prt_maxfilesize(XDR *xdr)
+{
+ uint64_t val;
+
+ if (!xdr_uint64_t(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Maximum File Size = %llu", val);
+}
+
+static void
+prt_maxlink(XDR *xdr)
+{
+ uint32_t val;
+
+ if (!xdr_uint32_t(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Maximum Number of Links = %u", val);
+}
+
+static void
+prt_maxname(XDR *xdr)
+{
+ uint32_t val;
+
+ if (!xdr_uint32_t(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Maximum File Name Length = %u", val);
+}
+
+static void
+prt_maxread(XDR *xdr)
+{
+ uint64_t val;
+
+ if (!xdr_uint64_t(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Maximum Read Size = %llu", val);
+}
+
+static void
+prt_maxwrite(XDR *xdr)
+{
+ uint64_t val;
+
+ if (!xdr_uint64_t(xdr, &val))
+ longjmp(xdr_err, 1);
+
+ sprintf(get_line(0, 0), "Maximum Write Size = %llu", val);
+}
+
+static void
+prt_mimetype(XDR *xdr)
+{
+ static utf8string val;
+
+ if (!xdr_utf8string(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "MIME Type = %s", utf8localize(&val));
+ xdr_free(xdr_utf8string, (char *)&val);
+}
+
+static void
+prt_mode(XDR *xdr)
+{
+ mode4 val;
+
+ if (!xdr_mode4(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Mode = 0%03o", val);
+}
+
+static void
+prt_no_trunc(XDR *xdr)
+{
+ bool_t val;
+
+ if (!xdr_bool(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Long Names Are Error (no_trunc) = %s",
+ val ? "TRUE" : "FALSE");
+}
+
+static void
+prt_numlinks(XDR *xdr)
+{
+ uint32_t val;
+
+ if (!xdr_uint32_t(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Number of Links = %u", val);
+}
+
+static void
+prt_owner(XDR *xdr)
+{
+ static utf8string val;
+
+ if (!xdr_utf8string(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Owner = %s", utf8localize(&val));
+ xdr_free(xdr_utf8string, (char *)&val);
+}
+
+static void
+prt_owner_group(XDR *xdr)
+{
+ static utf8string val;
+
+ if (!xdr_utf8string(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Group = %s", utf8localize(&val));
+ xdr_free(xdr_utf8string, (char *)&val);
+}
+
+static void
+prt_quota_avail_hard(XDR *xdr)
+{
+ uint64_t val;
+
+ if (!xdr_uint64_t(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Quota Hard Limit = %llu", val);
+}
+
+static void
+prt_quota_avail_soft(XDR *xdr)
+{
+ uint64_t val;
+
+ if (!xdr_uint64_t(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Quota Soft Limit = %llu", val);
+}
+
+static void
+prt_quota_used(XDR *xdr)
+{
+ uint64_t val;
+
+ if (!xdr_uint64_t(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Quota Used = %llu", val);
+}
+
+static void
+prt_rawdev(XDR *xdr)
+{
+ specdata4 val;
+
+ if (!xdr_specdata4(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Raw Device ID = %u, %u",
+ val.specdata1, val.specdata2);
+}
+
+static void
+prt_space_avail(XDR *xdr)
+{
+ uint64_t val;
+
+ if (!xdr_uint64_t(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Space Available = %llu", val);
+}
+
+static void
+prt_space_free(XDR *xdr)
+{
+ uint64_t val;
+
+ if (!xdr_uint64_t(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Quota Hard Limit = %llu", val);
+}
+
+static void
+prt_space_total(XDR *xdr)
+{
+ uint64_t val;
+
+ if (!xdr_uint64_t(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Total Disk Space = %llu", val);
+}
+
+static void
+prt_space_used(XDR *xdr)
+{
+ uint64_t val;
+
+ if (!xdr_uint64_t(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Space Used (this object) = %llu", val);
+}
+
+static void
+prt_system(XDR *xdr)
+{
+ bool_t val;
+
+ if (!xdr_bool(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "System File = %s",
+ val ? "TRUE" : "FALSE");
+}
+
+static void
+prt_time_access(XDR *xdr)
+{
+ nfstime4 val;
+
+ if (!xdr_nfstime4(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Last Access Time = %s",
+ format_time(val.seconds, val.nseconds));
+}
+
+static void
+prt_time_access_set(XDR *xdr)
+{
+ settime4 val;
+
+ if (!xdr_settime4(xdr, &val))
+ longjmp(xdr_err, 1);
+ if (val.set_it == SET_TO_CLIENT_TIME4) {
+ sprintf(get_line(0, 0), "Access Time = %s (set to client time)",
+ format_time(val.settime4_u.time.seconds,
+ val.settime4_u.time.nseconds));
+ } else if (val.set_it == SET_TO_SERVER_TIME4) {
+ sprintf(get_line(0, 0), "Access Time (set to server time)");
+ } else
+ longjmp(xdr_err, 1);
+}
+
+static void
+prt_time_backup(XDR *xdr)
+{
+ nfstime4 val;
+
+ if (!xdr_nfstime4(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Last Backup Time = %s",
+ format_time(val.seconds, val.nseconds));
+}
+
+static void
+prt_time_create(XDR *xdr)
+{
+ nfstime4 val;
+
+ if (!xdr_nfstime4(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Creation Time = %s",
+ format_time(val.seconds, val.nseconds));
+}
+
+static void
+prt_time_delta(XDR *xdr)
+{
+ nfstime4 val;
+
+ if (!xdr_nfstime4(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Server Time Granularity = %lld.%09d sec",
+ val.seconds, val.nseconds);
+}
+
+static void
+prt_time_metadata(XDR *xdr)
+{
+ nfstime4 val;
+
+ if (!xdr_nfstime4(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Last Metadata Change Time = %s",
+ format_time(val.seconds, val.nseconds));
+}
+
+static void
+prt_time_modify(XDR *xdr)
+{
+ nfstime4 val;
+
+ if (!xdr_nfstime4(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Last Modification Time = %s",
+ format_time(val.seconds, val.nseconds));
+}
+
+static void
+prt_time_modify_set(XDR *xdr)
+{
+ settime4 val;
+
+ if (!xdr_settime4(xdr, &val))
+ longjmp(xdr_err, 1);
+ if (val.set_it == SET_TO_CLIENT_TIME4) {
+ sprintf(get_line(0, 0),
+ "Modification Time = %s (set to client time)",
+ format_time(val.settime4_u.time.seconds,
+ val.settime4_u.time.nseconds));
+ } else if (val.set_it == SET_TO_SERVER_TIME4) {
+ sprintf(get_line(0, 0),
+ "Modification Time (set to server time)");
+ } else
+ longjmp(xdr_err, 1);
+}
+
+/*
+ * Display the UTF8 string that is next in the XDR stream.
+ */
+
+static void
+showxdr_utf8string(char *fmt)
+{
+ static utf8string string;
+
+ if (!xdr_utf8string(&xdrm, &string))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), fmt, utf8localize(&string));
+ xdr_free(xdr_utf8string, (char *)&string);
+}
+
+/*
+ * utf8string is defined in nfs4_prot.x as an opaque array, which means
+ * when it is decoded into a string, the string might not have a trailing
+ * null. Also, the string will still be encoded in UTF-8, rather than
+ * whatever character encoding is associated with the current locale. This
+ * routine converts a utf8string into a (null-terminated) C string. One day
+ * it will convert into the current character encoding, too. To avoid
+ * dealing with storage management issues, it allocates storage for each
+ * new string, then this storage is "freed" when the packet has been
+ * processed.
+ */
+
+#define MAX_UTF8_STRINGS 512
+
+static char *utf_buf[MAX_UTF8_STRINGS];
+static size_t utf_buflen[MAX_UTF8_STRINGS];
+static uint_t cur_utf_buf = 0;
+
+static char *
+utf8localize(utf8string *utf8str)
+{
+ size_t newsize, oldsize, len;
+ char *result, *cp;
+
+ len = utf8str->utf8string_len;
+ if (len == 0)
+ return ("");
+ if (cur_utf_buf >= MAX_UTF8_STRINGS)
+ return ("[Too Many UTF-8 Strings]");
+
+ newsize = oldsize = utf_buflen[cur_utf_buf];
+
+
+ if (oldsize < len + 1) {
+ /* truncate opaques at NFS4_OPAQUE_LIMIT */
+ if (len > NFS4_OPAQUE_LIMIT)
+ len = NFS4_OPAQUE_LIMIT;
+ newsize = len + 1;
+ }
+ if (newsize != oldsize) {
+ utf_buf[cur_utf_buf] = realloc(utf_buf[cur_utf_buf],
+ newsize);
+ if (utf_buf[cur_utf_buf] == NULL) {
+ pr_err("out of memory\n");
+ utf_buflen[cur_utf_buf] = 0;
+ return ("");
+ }
+ utf_buflen[cur_utf_buf] = newsize;
+ }
+
+ result = utf_buf[cur_utf_buf];
+ strncpy(result, utf8str->utf8string_val, len);
+ result[len] = '\0';
+ for (cp = result; cp < result + len; cp++) {
+ if (!isprint(*cp)) {
+ *cp = '.';
+ }
+ }
+
+ cur_utf_buf++;
+
+ return (result);
+}
+
+static void
+utf8free()
+{
+ cur_utf_buf = 0;
+}
+
+
+/*
+ * adler16(): adler32 hash code shamelessly copied and mutiliated from
+ * usr/src/uts/common/io/ppp/spppcomp/zlib.[ch]
+ *
+ * The alg was originally created to provide a running
+ * checksum, but we don't need that -- we just want to
+ * chksum data described by buf,len; therefore, the first
+ * parameter was removed (held the running checksum),
+ * and s1/s2 are always set to their required initial
+ * values (1 and 0). I also ripped out code which only
+ * applied to large data sets (bufs larger than 5k). All
+ * I wanted was their core checksum alg (which is supposed
+ * to do really well). The v2/v3 hash alg didn't work well
+ * at all for v4 stuff -- it produced too many collisions.
+ *
+ * The copyright info from uts/common/io/ppp/spppcomp/zlib.[ch]
+ * is included below.
+ */
+
+/* -----zlib.c copyright info below */
+/*
+ * Copyright 2000 Sun Microsystems, Inc.
+ * All rights reserved.
+ *
+ * Updated from zlib-1.0.4 to zlib-1.1.3 by James Carlson.
+ *
+ * This file is derived from various .h and .c files from the zlib-1.0.4
+ * distribution by Jean-loup Gailly and Mark Adler, with some additions
+ * by Paul Mackerras to aid in implementing Deflate compression and
+ * decompression for PPP packets. See zlib.h for conditions of
+ * distribution and use.
+ *
+ * Changes that have been made include:
+ * - added Z_PACKET_FLUSH (see zlib.h for details)
+ * - added inflateIncomp and deflateOutputPending
+ * - allow strm->next_out to be NULL, meaning discard the output
+ *
+ * $Id: zlib.c,v 1.11 1998/09/13 23:37:12 paulus Exp $
+ */
+/* +++ adler32.c */
+/*
+ * adler32.c -- compute the Adler-32 checksum of a data stream
+ * Copyright (C) 1995-1998 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+/* From: adler32.c,v 1.10 1996/05/22 11:52:18 me Exp $ */
+/* -----zlib.c copyright info above */
+
+/* -----zlib.h copyright info below */
+/*
+ * Copyright 2000 Sun Microsystems, Inc.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation is hereby granted, provided that the above
+ * copyright notice appears in all copies.
+ *
+ * SUN MAKES NO REPRESENTATION OR WARRANTIES ABOUT THE SUITABILITY OF
+ * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+ * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE
+ * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING,
+ * MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES
+ *
+ * This file has been altered from its original by Sun Microsystems to
+ * fit local coding style.
+ */
+/* -----zlib.h copyright info above */
+
+#define DO1(buf, i) {s1 += buf[i]; s2 += s1; }
+#define DO2(buf, i) DO1(buf, i); DO1(buf, i+1);
+#define DO4(buf, i) DO2(buf, i); DO2(buf, i+2);
+#define DO8(buf, i) DO4(buf, i); DO4(buf, i+4);
+#define DO16(buf) DO8(buf, 0); DO8(buf, 8);
+
+static uint32_t
+adler16(void *p, int len)
+{
+ uint32_t s1 = 1;
+ uint32_t s2 = 0;
+ uchar_t *buf = p;
+
+ while (len >= 16) {
+ DO16(buf);
+ buf += 16;
+ len -= 16;
+ }
+
+ while (len > 0) {
+ s1 += *buf++;
+ s2 += s1;
+ len--;
+ }
+
+ return ((uint32_t)(s2 ^ s1) & 0xFFFFU);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nfs_acl.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nfs_acl.c
new file mode 100644
index 0000000000..80d193e796
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nfs_acl.c
@@ -0,0 +1,797 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1991,2001-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI" /* SunOS */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/tiuser.h>
+#include <setjmp.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include <rpc/types.h>
+#include <rpc/xdr.h>
+#include <rpc/auth.h>
+#include <rpc/clnt.h>
+#include <rpc/rpc_msg.h>
+#include <string.h>
+#include "snoop.h"
+
+#include <sys/stat.h>
+
+extern char *get_sum_line();
+extern void check_retransmit();
+extern char *sum_nfsfh();
+extern int sum_nfsstat();
+extern int detail_nfsstat();
+extern void detail_nfsfh();
+extern void detail_fattr();
+extern void skip_fattr();
+extern char *sum_nfsfh3();
+extern int sum_nfsstat3();
+extern int detail_nfsstat3();
+extern void detail_post_op_attr();
+extern void detail_nfsfh3();
+extern int sum_nfsstat4();
+extern int detail_nfsstat4();
+
+extern jmp_buf xdr_err;
+
+static void aclcall2();
+static void aclreply2();
+static void aclcall3();
+static void aclreply3();
+static void aclcall4();
+static void aclreply4();
+static void detail_access2();
+static char *sum_access2();
+static void detail_mask();
+static void detail_secattr();
+static void detail_aclent();
+static char *detail_uname();
+static char *detail_gname();
+static char *detail_perm(ushort_t);
+
+#define ACLPROC2_NULL ((unsigned long)(0))
+#define ACLPROC2_GETACL ((unsigned long)(1))
+#define ACLPROC2_SETACL ((unsigned long)(2))
+#define ACLPROC2_GETATTR ((unsigned long)(3))
+#define ACLPROC2_ACCESS ((unsigned long)(4))
+#define ACLPROC2_GETXATTRDIR ((unsigned long)(5))
+
+#define ACLPROC3_NULL ((unsigned long)(0))
+#define ACLPROC3_GETACL ((unsigned long)(1))
+#define ACLPROC3_SETACL ((unsigned long)(2))
+#define ACLPROC3_GETXATTRDIR ((unsigned long)(3))
+
+#define ACLPROC4_NULL ((unsigned long)(0))
+#define ACLPROC4_GETACL ((unsigned long)(1))
+#define ACLPROC4_SETACL ((unsigned long)(2))
+
+#define NA_USER_OBJ 0x1
+#define NA_USER 0x2
+#define NA_GROUP_OBJ 0x4
+#define NA_GROUP 0x8
+#define NA_CLASS_OBJ 0x10
+#define NA_OTHER_OBJ 0x20
+#define NA_ACL_DEFAULT 0x1000
+
+#define NA_DEF_USER_OBJ (NA_USER_OBJ | NA_ACL_DEFAULT)
+#define NA_DEF_USER (NA_USER | NA_ACL_DEFAULT)
+#define NA_DEF_GROUP_OBJ (NA_GROUP_OBJ | NA_ACL_DEFAULT)
+#define NA_DEF_GROUP (NA_GROUP | NA_ACL_DEFAULT)
+#define NA_DEF_CLASS_OBJ (NA_CLASS_OBJ | NA_ACL_DEFAULT)
+#define NA_DEF_OTHER_OBJ (NA_OTHER_OBJ | NA_ACL_DEFAULT)
+
+#define NA_ACL 0x1
+#define NA_ACLCNT 0x2
+#define NA_DFACL 0x4
+#define NA_DFACLCNT 0x8
+
+#define ACCESS2_READ 0x0001
+#define ACCESS2_LOOKUP 0x0002
+#define ACCESS2_MODIFY 0x0004
+#define ACCESS2_EXTEND 0x0008
+#define ACCESS2_DELETE 0x0010
+#define ACCESS2_EXECUTE 0x0020
+
+static char *procnames_short_v2[] = {
+ "NULL2", /* 0 */
+ "GETACL2", /* 1 */
+ "SETACL2", /* 2 */
+ "GETATTR2", /* 3 */
+ "ACCESS2", /* 4 */
+ "GETXATTRDIR2", /* 5 */
+};
+static char *procnames_short_v3[] = {
+ "NULL3", /* 0 */
+ "GETACL3", /* 1 */
+ "SETACL3", /* 2 */
+ "GETXATTRDIR3", /* 3 */
+};
+static char *procnames_short_v4[] = {
+ "NULL4", /* 0 */
+ "GETACL4", /* 1 */
+ "SETACL4", /* 2 */
+};
+
+static char *procnames_long_v2[] = {
+ "Null procedure", /* 0 */
+ "Get file access control list", /* 1 */
+ "Set file access control list", /* 2 */
+ "Get file attributes", /* 3 */
+ "Check access permission", /* 4 */
+ "Get extended attribute directory", /* 5 */
+};
+static char *procnames_long_v3[] = {
+ "Null procedure", /* 0 */
+ "Get file access control list", /* 1 */
+ "Set file access control list", /* 2 */
+ "Get extended attribute directory", /* 3 */
+};
+static char *procnames_long_v4[] = {
+ "Null procedure", /* 0 */
+ "Get file access control list", /* 1 */
+ "Set file access control list", /* 2 */
+};
+
+#define MAXPROC_V2 5
+#define MAXPROC_V3 3
+#define MAXPROC_V4 2
+
+/* ARGSUSED */
+void
+interpret_nfs_acl(flags, type, xid, vers, proc, data, len)
+ int flags, type, xid, vers, proc;
+ char *data;
+ int len;
+{
+
+ if (vers == 2) {
+ interpret_nfs_acl2(flags, type, xid, vers, proc, data, len);
+ return;
+ }
+
+ if (vers == 3) {
+ interpret_nfs_acl3(flags, type, xid, vers, proc, data, len);
+ return;
+ }
+
+ if (vers == 4) {
+ interpret_nfs_acl4(flags, type, xid, vers, proc, data, len);
+ return;
+ }
+}
+
+interpret_nfs_acl2(flags, type, xid, vers, proc, data, len)
+ int flags, type, xid, vers, proc;
+ char *data;
+ int len;
+{
+ char *line;
+ char buff[2048];
+ int off, sz;
+ char *fh;
+ ulong_t mask;
+
+ if (proc < 0 || proc > MAXPROC_V2)
+ return;
+
+ if (flags & F_SUM) {
+ line = get_sum_line();
+
+ if (type == CALL) {
+ (void) sprintf(line, "NFS_ACL C %s",
+ procnames_short_v2[proc]);
+ line += strlen(line);
+ switch (proc) {
+ case ACLPROC2_GETACL:
+ fh = sum_nfsfh();
+ mask = getxdr_u_long();
+ (void) sprintf(line, "%s mask=%lu", fh, mask);
+ break;
+ case ACLPROC2_SETACL:
+ (void) sprintf(line, sum_nfsfh());
+ break;
+ case ACLPROC2_GETATTR:
+ (void) sprintf(line, sum_nfsfh());
+ break;
+ case ACLPROC2_ACCESS:
+ fh = sum_nfsfh();
+ (void) sprintf(line, "%s (%s)", fh,
+ sum_access2());
+ break;
+ case ACLPROC2_GETXATTRDIR:
+ fh = sum_nfsfh();
+ (void) sprintf(line, "%s create=%s", fh,
+ getxdr_bool() ? "true" : "false");
+ break;
+ default:
+ break;
+ }
+
+ check_retransmit(line, (ulong_t)xid);
+ } else {
+ (void) sprintf(line, "NFS_ACL R %s ",
+ procnames_short_v2[proc]);
+ line += strlen(line);
+ switch (proc) {
+ case ACLPROC2_GETACL:
+ (void) sum_nfsstat(line);
+ break;
+ case ACLPROC2_SETACL:
+ (void) sum_nfsstat(line);
+ break;
+ case ACLPROC2_GETATTR:
+ (void) sum_nfsstat(line);
+ break;
+ case ACLPROC2_ACCESS:
+ if (sum_nfsstat(line) == 0) {
+ skip_fattr();
+ line += strlen(line);
+ (void) sprintf(line, " (%s)",
+ sum_access2());
+ }
+ break;
+ case ACLPROC2_GETXATTRDIR:
+ if (sum_nfsstat(line) == 0) {
+ line += strlen(line);
+ (void) sprintf(line, sum_nfsfh());
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("NFS_ACL: ", "Sun NFS_ACL", len);
+ show_space();
+ (void) sprintf(get_line(0, 0), "Proc = %d (%s)",
+ proc, procnames_long_v2[proc]);
+ if (type == CALL)
+ aclcall2(proc);
+ else
+ aclreply2(proc);
+ show_trailer();
+ }
+}
+
+interpret_nfs_acl3(flags, type, xid, vers, proc, data, len)
+ int flags, type, xid, vers, proc;
+ char *data;
+ int len;
+{
+ char *line;
+ char buff[2048];
+ int off, sz;
+ char *fh;
+ ulong_t mask;
+
+ if (proc < 0 || proc > MAXPROC_V3)
+ return;
+
+ if (flags & F_SUM) {
+ line = get_sum_line();
+
+ if (type == CALL) {
+ (void) sprintf(line, "NFS_ACL C %s",
+ procnames_short_v3[proc]);
+ line += strlen(line);
+ switch (proc) {
+ case ACLPROC3_GETACL:
+ fh = sum_nfsfh3();
+ mask = getxdr_u_long();
+ (void) sprintf(line, "%s mask=%lu", fh, mask);
+ break;
+ case ACLPROC3_SETACL:
+ (void) sprintf(line, sum_nfsfh3());
+ break;
+ case ACLPROC3_GETXATTRDIR:
+ fh = sum_nfsfh3();
+ (void) sprintf(line, "%s create=%s", fh,
+ getxdr_bool() ? "true" : "false");
+ break;
+ default:
+ break;
+ }
+
+ check_retransmit(line, (ulong_t)xid);
+ } else {
+ (void) sprintf(line, "NFS_ACL R %s ",
+ procnames_short_v3[proc]);
+ line += strlen(line);
+ switch (proc) {
+ case ACLPROC3_GETACL:
+ (void) sum_nfsstat3(line);
+ break;
+ case ACLPROC3_SETACL:
+ (void) sum_nfsstat3(line);
+ break;
+ case ACLPROC3_GETXATTRDIR:
+ if (sum_nfsstat3(line) == 0) {
+ line += strlen(line);
+ (void) sprintf(line, sum_nfsfh3());
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("NFS_ACL: ", "Sun NFS_ACL", len);
+ show_space();
+ (void) sprintf(get_line(0, 0), "Proc = %d (%s)",
+ proc, procnames_long_v3[proc]);
+ if (type == CALL)
+ aclcall3(proc);
+ else
+ aclreply3(proc);
+ show_trailer();
+ }
+}
+
+interpret_nfs_acl4(flags, type, xid, vers, proc, data, len)
+ int flags, type, xid, vers, proc;
+ char *data;
+ int len;
+{
+ char *line;
+ char buff[2048];
+ int off, sz;
+ char *fh;
+ ulong_t mask;
+
+ if (proc < 0 || proc > MAXPROC_V4)
+ return;
+
+ if (flags & F_SUM) {
+ line = get_sum_line();
+
+ if (type == CALL) {
+ (void) sprintf(line, "NFS_ACL C %s",
+ procnames_short_v4[proc]);
+ line += strlen(line);
+ switch (proc) {
+ case ACLPROC4_GETACL:
+ fh = sum_nfsfh3();
+ mask = getxdr_u_long();
+ (void) sprintf(line, "%s mask=%lu", fh, mask);
+ break;
+ case ACLPROC4_SETACL:
+ (void) sprintf(line, sum_nfsfh3());
+ break;
+ default:
+ break;
+ }
+
+ check_retransmit(line, (ulong_t)xid);
+ } else {
+ (void) sprintf(line, "NFS_ACL R %s ",
+ procnames_short_v4[proc]);
+ line += strlen(line);
+ switch (proc) {
+ case ACLPROC4_GETACL:
+ (void) sum_nfsstat4(line);
+ break;
+ case ACLPROC4_SETACL:
+ (void) sum_nfsstat4(line);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("NFS_ACL: ", "Sun NFS_ACL", len);
+ show_space();
+ (void) sprintf(get_line(0, 0), "Proc = %d (%s)",
+ proc, procnames_long_v4[proc]);
+ if (type == CALL)
+ aclcall4(proc);
+ else
+ aclreply4(proc);
+ show_trailer();
+ }
+}
+
+int
+sum_nfsstat4(char *line)
+{
+ ulong_t status;
+ char *p, *nfsstat4_to_name(int);
+
+ status = getxdr_long();
+ p = nfsstat4_to_name(status);
+ (void) strcpy(line, p);
+ return (status);
+}
+
+int
+detail_nfsstat4()
+{
+ ulong_t status;
+ char buff[64];
+ int pos;
+
+ pos = getxdr_pos();
+ status = sum_nfsstat4(buff);
+
+ (void) sprintf(get_line(pos, getxdr_pos()), "Status = %d (%s)",
+ status, buff);
+
+ return ((int)status);
+}
+
+/*
+ * Print out version 2 NFS_ACL call packets
+ */
+static void
+aclcall2(proc)
+ int proc;
+{
+
+ switch (proc) {
+ case ACLPROC2_GETACL:
+ detail_nfsfh();
+ detail_mask();
+ break;
+ case ACLPROC2_SETACL:
+ detail_nfsfh();
+ detail_secattr();
+ break;
+ case ACLPROC2_GETATTR:
+ detail_nfsfh();
+ break;
+ case ACLPROC2_ACCESS:
+ detail_nfsfh();
+ detail_access2();
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * Print out version 2 NFS_ACL reply packets
+ */
+static void
+aclreply2(proc)
+ int proc;
+{
+
+ switch (proc) {
+ case ACLPROC2_GETACL:
+ if (detail_nfsstat() == 0) {
+ detail_fattr();
+ detail_secattr();
+ }
+ break;
+ case ACLPROC2_SETACL:
+ if (detail_nfsstat() == 0)
+ detail_fattr();
+ break;
+ case ACLPROC2_GETATTR:
+ if (detail_nfsstat() == 0)
+ detail_fattr();
+ break;
+ case ACLPROC2_ACCESS:
+ if (detail_nfsstat() == 0) {
+ detail_fattr();
+ detail_access2();
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * Print out version 3 NFS_ACL call packets
+ */
+static void
+aclcall3(proc)
+ int proc;
+{
+
+ switch (proc) {
+ case ACLPROC3_GETACL:
+ detail_nfsfh3();
+ detail_mask();
+ break;
+ case ACLPROC3_SETACL:
+ detail_nfsfh3();
+ detail_secattr();
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * Print out version 3 NFS_ACL reply packets
+ */
+static void
+aclreply3(proc)
+ int proc;
+{
+
+ switch (proc) {
+ case ACLPROC3_GETACL:
+ if (detail_nfsstat3() == 0) {
+ detail_post_op_attr("");
+ detail_secattr();
+ }
+ break;
+ case ACLPROC3_SETACL:
+ if (detail_nfsstat3() == 0)
+ detail_post_op_attr("");
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * Print out version 4 NFS_ACL call packets
+ */
+static void
+aclcall4(proc)
+ int proc;
+{
+
+ switch (proc) {
+ case ACLPROC4_GETACL:
+ detail_nfsfh3();
+ detail_mask();
+ break;
+ case ACLPROC4_SETACL:
+ detail_nfsfh3();
+ detail_secattr();
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * Print out version 4 NFS_ACL reply packets
+ */
+static void
+aclreply4(proc)
+ int proc;
+{
+
+ switch (proc) {
+ case ACLPROC4_GETACL:
+ if (detail_nfsstat4() == 0) {
+ detail_post_op_attr("");
+ detail_secattr();
+ }
+ break;
+ case ACLPROC4_SETACL:
+ if (detail_nfsstat4() == 0)
+ detail_post_op_attr("");
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+detail_access2()
+{
+ uint_t bits;
+
+ bits = showxdr_u_long("Access bits = 0x%08x");
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(bits, ACCESS2_READ, "Read", "(no read)"));
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(bits, ACCESS2_LOOKUP, "Lookup", "(no lookup)"));
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(bits, ACCESS2_MODIFY, "Modify", "(no modify)"));
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(bits, ACCESS2_EXTEND, "Extend", "(no extend)"));
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(bits, ACCESS2_DELETE, "Delete", "(no delete)"));
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(bits, ACCESS2_EXECUTE, "Execute", "(no execute)"));
+}
+
+static char *
+sum_access2()
+{
+ int bits;
+ static char buff[22];
+
+ bits = getxdr_u_long();
+ buff[0] = '\0';
+
+ if (bits & ACCESS2_READ)
+ (void) strcat(buff, "read,");
+ if (bits & ACCESS2_LOOKUP)
+ (void) strcat(buff, "lookup,");
+ if (bits & ACCESS2_MODIFY)
+ (void) strcat(buff, "modify,");
+ if (bits & ACCESS2_EXTEND)
+ (void) strcat(buff, "extend,");
+ if (bits & ACCESS2_DELETE)
+ (void) strcat(buff, "delete,");
+ if (bits & ACCESS2_EXECUTE)
+ (void) strcat(buff, "execute,");
+ if (buff[0] != '\0')
+ buff[strlen(buff) - 1] = '\0';
+
+ return (buff);
+}
+
+static void
+detail_mask()
+{
+ ulong_t mask;
+
+ mask = showxdr_u_long("Mask = 0x%lx");
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(mask, NA_ACL, "aclent", "(no aclent)"));
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(mask, NA_ACLCNT, "aclcnt", "(no aclcnt)"));
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(mask, NA_DFACL, "dfaclent", "(no dfaclent)"));
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(mask, NA_DFACLCNT, "dfaclcnt", "(no dfaclcnt)"));
+}
+
+static void
+detail_secattr()
+{
+
+ detail_mask();
+ showxdr_long("Aclcnt = %d");
+ detail_aclent();
+ showxdr_long("Dfaclcnt = %d");
+ detail_aclent();
+}
+
+static void
+detail_aclent()
+{
+ int count;
+ int type;
+ int id;
+ ushort_t perm;
+
+ count = getxdr_long();
+ while (count-- > 0) {
+ type = getxdr_long();
+ id = getxdr_long();
+ perm = getxdr_u_short();
+ switch (type) {
+ case NA_USER:
+ (void) sprintf(get_line(0, 0), "\tuser:%s:%s",
+ detail_uname(id), detail_perm(perm));
+ break;
+ case NA_USER_OBJ:
+ (void) sprintf(get_line(0, 0), "\tuser::%s",
+ detail_perm(perm));
+ break;
+ case NA_GROUP:
+ (void) sprintf(get_line(0, 0), "\tgroup:%s:%s",
+ detail_gname(id), detail_perm(perm));
+ break;
+ case NA_GROUP_OBJ:
+ (void) sprintf(get_line(0, 0), "\tgroup::%s",
+ detail_perm(perm));
+ break;
+ case NA_CLASS_OBJ:
+ (void) sprintf(get_line(0, 0), "\tmask:%s",
+ detail_perm(perm));
+ break;
+ case NA_OTHER_OBJ:
+ (void) sprintf(get_line(0, 0), "\tother:%s",
+ detail_perm(perm));
+ break;
+ case NA_DEF_USER:
+ (void) sprintf(get_line(0, 0), "\tdefault:user:%s:%s",
+ detail_uname(id), detail_perm(perm));
+ break;
+ case NA_DEF_USER_OBJ:
+ (void) sprintf(get_line(0, 0), "\tdefault:user::%s",
+ detail_perm(perm));
+ break;
+ case NA_DEF_GROUP:
+ (void) sprintf(get_line(0, 0), "\tdefault:group:%s:%s",
+ detail_gname(id), detail_perm(perm));
+ break;
+ case NA_DEF_GROUP_OBJ:
+ (void) sprintf(get_line(0, 0), "\tdefault:group::%s",
+ detail_perm(perm));
+ break;
+ case NA_DEF_CLASS_OBJ:
+ (void) sprintf(get_line(0, 0), "\tdefault:mask:%s",
+ detail_perm(perm));
+ break;
+ case NA_DEF_OTHER_OBJ:
+ (void) sprintf(get_line(0, 0), "\tdefault:other:%s",
+ detail_perm(perm));
+ break;
+ default:
+ (void) sprintf(get_line(0, 0), "\tunrecognized entry");
+ break;
+ }
+ }
+}
+
+static char *
+detail_uname(uid_t uid)
+{
+ struct passwd *pwd;
+ static char uidp[10];
+
+ pwd = getpwuid(uid);
+ if (pwd == NULL) {
+ sprintf(uidp, "%d", uid);
+ return (uidp);
+ }
+ return (pwd->pw_name);
+}
+
+static char *
+detail_gname(gid_t gid)
+{
+ struct group *grp;
+ static char gidp[10];
+
+ grp = getgrgid(gid);
+ if (grp == NULL) {
+ sprintf(gidp, "%d", gid);
+ return (gidp);
+ }
+ return (grp->gr_name);
+}
+
+static char *perms[] = {
+ "---",
+ "--x",
+ "-w-",
+ "-wx",
+ "r--",
+ "r-x",
+ "rw-",
+ "rwx"
+};
+static char *
+detail_perm(ushort_t perm)
+{
+
+ if (perm > sizeof (perms) / sizeof (perms[0]))
+ return ("?");
+ return (perms[perm]);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nis.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nis.c
new file mode 100644
index 0000000000..a78c418ac2
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nis.c
@@ -0,0 +1,676 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1991, 1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ident "%Z%%M% %I% %E% SMI" /* SunOS */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <setjmp.h>
+#include <string.h>
+
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/tiuser.h>
+#include <net/if.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <rpc/types.h>
+#include <rpc/xdr.h>
+#include <rpc/auth.h>
+#include <rpc/clnt.h>
+#include <rpc/rpc_msg.h>
+#include <rpcsvc/yp_prot.h>
+#include "snoop.h"
+
+extern char *dlc_header;
+extern jmp_buf xdr_err;
+char *ypbind_error();
+char *sum_ypxfrstat();
+char *sum_ypmaplist();
+void detail_ypmaplist();
+
+static void niscall(int);
+static void nisreply(int);
+static int detail_ypstat(void);
+static int sum_ypstat(char *);
+
+/*
+ * Defines missing from 5.0 yp_prot.h
+ */
+#define YPBINDPROG ((ulong_t)100007)
+#define YPBINDVERS ((ulong_t)2)
+#define YPBINDVERS_ORIG ((ulong_t)1)
+
+/* Procedure symbols */
+
+#define YPBINDPROC_NULL ((ulong_t)0)
+#define YPBINDPROC_DOMAIN ((ulong_t)1)
+#define YPBINDPROC_SETDOM ((ulong_t)2)
+
+#define YPBIND_ERR_ERR 1 /* Internal error */
+#define YPBIND_ERR_NOSERV 2 /* No bound server for passed domain */
+#define YPBIND_ERR_RESC 3 /* System resource allocation failure */
+
+
+static char *procnames_bind_short[] = {
+ "NULL", /* 0 */
+ "DOMAIN", /* 1 */
+ "SETDOMAIN", /* 2 */
+};
+
+static char *procnames_bind_long[] = {
+ "Null procedure", /* 0 */
+ "Get domain name", /* 1 */
+ "Set domain name", /* 2 */
+};
+
+static char *procnames_short[] = {
+ "NULL", /* 0 */
+ "DOMAIN", /* 1 */
+ "DOMAIN_NONACK", /* 2 */
+ "MATCH", /* 3 */
+ "FIRST", /* 4 */
+ "NEXT", /* 5 */
+ "XFR", /* 6 */
+ "CLEAR", /* 7 */
+ "ALL", /* 8 */
+ "MASTER", /* 9 */
+ "ORDER", /* 10 */
+ "MAPLIST", /* 11 */
+ "NEWXFR", /* 12 */
+};
+
+#define MAXPROC_BIND 2
+#define MAXPROC 12
+
+static char *procnames_long[] = {
+ "Null procedure", /* 0 */
+ "Verify domain support", /* 1 */
+ "Verify domain support (broadcast)", /* 2 */
+ "Return value of a key", /* 3 */
+ "Return first key-value pair in map", /* 4 */
+ "Return next key-value pair in map", /* 5 */
+ "Request map update (old)", /* 6 */
+ "Close current map on server", /* 7 */
+ "Get all key-value pairs in map", /* 8 */
+ "Get master server", /* 9 */
+ "Get order", /* 10 */
+ "Return list of supported maps", /* 11 */
+ "Request map update", /* 12 */
+};
+
+void
+interpret_nisbind(flags, type, xid, vers, proc, data, len)
+ int flags, type, xid, vers, proc;
+ char *data;
+ int len;
+{
+ char *line;
+ char buff[YPMAXDOMAIN + 1];
+ unsigned int status;
+
+ if (proc < 0 || proc > MAXPROC_BIND)
+ return;
+
+ if (flags & F_SUM) {
+ if (setjmp(xdr_err)) {
+ return;
+ }
+
+ line = get_sum_line();
+
+ if (type == CALL) {
+ (void) sprintf(line,
+ "NISBIND C %s",
+ procnames_bind_short[proc]);
+ line += strlen(line);
+ switch (proc) {
+ case YPBINDPROC_NULL:
+ break;
+ case YPBINDPROC_DOMAIN:
+ (void) sprintf(line, " %s",
+ getxdr_string(buff, YPMAXDOMAIN));
+ break;
+ case YPBINDPROC_SETDOM:
+ (void) sprintf(line, " %s",
+ getxdr_string(buff, YPMAXDOMAIN));
+ break;
+ default:
+ break;
+ }
+ check_retransmit(line, xid);
+ } else {
+ (void) sprintf(line, "NISBIND R %s ",
+ procnames_bind_short[proc]);
+ line += strlen(line);
+ switch (proc) {
+ case YPBINDPROC_NULL:
+ break;
+ case YPBINDPROC_DOMAIN:
+ status = getxdr_long();
+ if (status == 1) { /* success */
+ (void) strcat(line, "OK");
+ } else { /* failure */
+ status = getxdr_long();
+ (void) sprintf(line, "ERROR=%s",
+ ypbind_error(status));
+ }
+ break;
+ case YPBINDPROC_SETDOM:
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("NISBIND:",
+ "Network Information Service Bind", len);
+ show_space();
+ if (setjmp(xdr_err)) {
+ return;
+ }
+ (void) sprintf(get_line(0, 0),
+ "Proc = %d (%s)",
+ proc, procnames_bind_long[proc]);
+ if (type == CALL) {
+ switch (proc) {
+ case YPBINDPROC_NULL:
+ break;
+ case YPBINDPROC_DOMAIN:
+ (void) showxdr_string(YPMAXDOMAIN,
+ "Domain = %s");
+ break;
+ case YPBINDPROC_SETDOM:
+ (void) showxdr_string(YPMAXDOMAIN,
+ "Domain = %s");
+ (void) showxdr_hex(4, "Address=%s");
+ (void) showxdr_hex(2, "Port=%s");
+ (void) showxdr_u_long("Version=%lu");
+ break;
+ default:
+ break;
+ }
+ } else {
+ switch (proc) {
+ case YPBINDPROC_NULL:
+ break;
+ case YPBINDPROC_DOMAIN:
+ status = getxdr_u_long();
+ (void) sprintf(get_line(0, 0),
+ "Status = %lu (%s)",
+ status,
+ status == 1 ? "OK":"Fail");
+ if (status == 1) {
+ (void) showxdr_hex(4,
+ "Address=%s");
+ (void) showxdr_hex(2,
+ "Port=%s");
+ } else {
+ status = getxdr_u_long();
+ (void) sprintf(get_line(0, 0),
+ "Error = %lu (%s)",
+ status,
+ ypbind_error(status));
+ }
+ break;
+ case YPBINDPROC_SETDOM:
+ break;
+ default:
+ break;
+ }
+ }
+ show_trailer();
+ }
+}
+
+void
+interpret_nis(flags, type, xid, vers, proc, data, len)
+ int flags, type, xid, vers, proc;
+ char *data;
+ int len;
+{
+ char *line;
+ char *dom, *map, *key;
+ int transid, status;
+ /* buffers are all the same size so we don't have to keep track */
+ char buff1[YPMAXRECORD + 1], buff2[YPMAXRECORD + 1];
+ char buff3[YPMAXRECORD + 1];
+
+ if (flags & F_SUM) {
+ if (setjmp(xdr_err)) {
+ return;
+ }
+
+ line = get_sum_line();
+
+ if (type == CALL) {
+ if (proc > MAXPROC)
+ (void) sprintf(line, "NIS C %d", proc);
+ else
+ (void) sprintf(line,
+ "NIS C %s",
+ procnames_short[proc]);
+ line += strlen(line);
+ switch (proc) {
+ case YPPROC_NULL:
+ break;
+ case YPPROC_DOMAIN:
+ case YPPROC_DOMAIN_NONACK:
+ case YPPROC_MAPLIST:
+ /* YPMAXDOMAIN > YPMAXMAP */
+ (void) sprintf(line, " %s",
+ getxdr_string(buff1, YPMAXDOMAIN));
+ break;
+ case YPPROC_FIRST:
+ dom = getxdr_string(buff1, YPMAXDOMAIN);
+ map = getxdr_string(buff2, YPMAXMAP);
+ (void) sprintf(line, " %s", map);
+ break;
+ case YPPROC_MATCH:
+ case YPPROC_NEXT:
+ dom = getxdr_string(buff1, YPMAXDOMAIN);
+ map = getxdr_string(buff2, YPMAXMAP);
+ key = getxdr_string(buff3, YPMAXRECORD);
+ (void) sprintf(line,
+ " %s in %s",
+ key, map);
+ break;
+ case YPPROC_NEWXFR:
+ case YPPROC_XFR:
+ dom = getxdr_string(buff1, YPMAXDOMAIN);
+ map = getxdr_string(buff2, YPMAXMAP);
+ (void) sprintf(line,
+ " map %s in %s",
+ map, dom);
+ break;
+ case YPPROC_CLEAR:
+ break;
+ case YPPROC_ALL:
+ case YPPROC_MASTER:
+ case YPPROC_ORDER:
+ dom = getxdr_string(buff1, YPMAXDOMAIN);
+ map = getxdr_string(buff2, YPMAXMAP);
+ (void) sprintf(line,
+ " map %s in %s",
+ map, dom);
+ break;
+ default:
+ break;
+ }
+ check_retransmit(line, xid);
+ } else {
+ if (proc > MAXPROC)
+ (void) sprintf(line, "NIS R %d ", proc);
+ else
+ (void) sprintf(line, "NIS R %s ",
+ procnames_short[proc]);
+ line += strlen(line);
+ switch (proc) {
+ case YPPROC_NULL:
+ break;
+ case YPPROC_DOMAIN:
+ case YPPROC_DOMAIN_NONACK:
+ (void) sprintf(line, "%s",
+ getxdr_long() ? "OK":"Fail");
+ break;
+ case YPPROC_MATCH:
+ (void) sum_ypstat(line);
+ break;
+ case YPPROC_FIRST:
+ case YPPROC_NEXT:
+ if (sum_ypstat(line) == YP_TRUE) {
+ line += strlen(line);
+ (void) getxdr_string(buff1,
+ YPMAXRECORD);
+ (void) sprintf(line, " key=%s",
+ getxdr_string(buff1,
+ YPMAXRECORD));
+ }
+ break;
+ case YPPROC_NEWXFR:
+ case YPPROC_XFR:
+ transid = getxdr_u_long();
+ status = getxdr_long();
+ (void) sprintf(line, "transid=%lu %s",
+ transid,
+ sum_ypxfrstat(status));
+ break;
+ case YPPROC_CLEAR:
+ break;
+ case YPPROC_ALL:
+ if (getxdr_u_long()) {
+ (void) sum_ypstat(line);
+ line += strlen(line);
+ (void) sprintf(line, " key=%s",
+ getxdr_string(buff1, YPMAXRECORD));
+ } else {
+ (void) sprintf(line,
+ "No more");
+ }
+ break;
+ case YPPROC_MASTER:
+ if (sum_ypstat(line) == YP_TRUE) {
+ line += strlen(line);
+ (void) sprintf(line, " peer=%s",
+ getxdr_string(buff1,
+ YPMAXPEER));
+ }
+ break;
+ case YPPROC_ORDER:
+ if (sum_ypstat(line) == YP_TRUE) {
+ line += strlen(line);
+ (void) sprintf(line, " order=%lu",
+ getxdr_u_long());
+ }
+ break;
+ case YPPROC_MAPLIST:
+ if (sum_ypstat(line) == YP_TRUE) {
+ line += strlen(line);
+ (void) sprintf(line, " %s",
+ sum_ypmaplist());
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("NIS: ", "Network Information Service", len);
+ show_space();
+ if (setjmp(xdr_err)) {
+ return;
+ }
+ (void) sprintf(get_line(0, 0),
+ "Proc = %d (%s)",
+ proc,
+ proc > MAXPROC ? "unknown" : procnames_long[proc]);
+ if (type == CALL)
+ niscall(proc);
+ else
+ nisreply(proc);
+ show_trailer();
+ }
+}
+
+/*
+ * Print out version 2 NIS call packets
+ */
+
+static void
+niscall(proc)
+ int proc;
+{
+ switch (proc) {
+ case YPPROC_NULL:
+ break;
+ case YPPROC_DOMAIN:
+ case YPPROC_DOMAIN_NONACK:
+ case YPPROC_MAPLIST:
+ (void) showxdr_string(YPMAXDOMAIN, "Domain = %s");
+ break;
+ case YPPROC_FIRST:
+ (void) showxdr_string(YPMAXDOMAIN, "Domain = %s");
+ (void) showxdr_string(YPMAXMAP, "Map = %s");
+ break;
+ case YPPROC_MATCH:
+ case YPPROC_NEXT:
+ (void) showxdr_string(YPMAXDOMAIN, "Domain = %s");
+ (void) showxdr_string(YPMAXMAP, "Map = %s");
+ (void) showxdr_string(YPMAXRECORD, "Key = %s");
+ break;
+ case YPPROC_NEWXFR:
+ (void) showxdr_string(YPMAXDOMAIN, "Domain = %s");
+ (void) showxdr_string(YPMAXMAP, "Map = %s");
+ (void) showxdr_u_long("Order = %lu");
+ (void) showxdr_string(YPMAXPEER, "Peer = %s");
+ (void) showxdr_u_long("Transid = %lu");
+ (void) showxdr_u_long("Prog = %lu");
+ (void) showxdr_string(YPMAXPEER, "Name = %s");
+ break;
+ case YPPROC_XFR:
+ (void) showxdr_string(YPMAXDOMAIN, "Domain = %s");
+ (void) showxdr_string(YPMAXMAP, "Map = %s");
+ (void) showxdr_u_long("Order = %lu");
+ (void) showxdr_string(YPMAXPEER, "Peer = %s");
+ (void) showxdr_u_long("Transid = %lu");
+ (void) showxdr_u_long("Prog = %lu");
+ (void) showxdr_u_long("Port = %lu");
+ break;
+ case YPPROC_CLEAR:
+ break;
+ case YPPROC_ALL:
+ case YPPROC_MASTER:
+ case YPPROC_ORDER:
+ (void) showxdr_string(YPMAXDOMAIN, "Domain = %s");
+ (void) showxdr_string(YPMAXMAP, "Map = %s");
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * Print out version 2 NIS reply packets
+ */
+
+void
+nisreply(proc)
+ int proc;
+{
+ unsigned int xfrstat, more;
+
+ switch (proc) {
+ case YPPROC_NULL:
+ break;
+ case YPPROC_DOMAIN:
+ case YPPROC_DOMAIN_NONACK:
+ (void) sprintf(get_line(0, 0),
+ "Result=%s",
+ getxdr_u_long() ? "OK":"Fail");
+ break;
+ case YPPROC_MATCH:
+ (void) detail_ypstat();
+ (void) showxdr_string(YPMAXRECORD, "Value = %s");
+ break;
+ case YPPROC_FIRST:
+ case YPPROC_NEXT:
+ (void) detail_ypstat();
+ (void) showxdr_string(YPMAXRECORD, "Value = %s");
+ (void) showxdr_string(YPMAXRECORD, "Key = %s");
+ break;
+ case YPPROC_NEWXFR:
+ case YPPROC_XFR:
+ (void) showxdr_u_long("Transid = %lu");
+ xfrstat = getxdr_u_long();
+ (void) sprintf(get_line(0, 0),
+ "Transfer status = %lu (%s)",
+ xfrstat, sum_ypxfrstat(xfrstat));
+ break;
+ case YPPROC_CLEAR:
+ break;
+ case YPPROC_ALL:
+ more = getxdr_u_long();
+ (void) sprintf(get_line(0, 0),
+ "More = %s",
+ more ? "true" : "false");
+ if (more) {
+ (void) detail_ypstat();
+ (void) showxdr_string(YPMAXRECORD, "Value = %s");
+ (void) showxdr_string(YPMAXRECORD, "Key = %s");
+ }
+ break;
+ case YPPROC_MASTER:
+ (void) detail_ypstat();
+ (void) showxdr_string(YPMAXPEER, "Peer = %s");
+ case YPPROC_ORDER:
+ (void) detail_ypstat();
+ (void) showxdr_u_long("Order=%lu");
+ break;
+ case YPPROC_MAPLIST:
+ (void) detail_ypstat();
+ detail_ypmaplist();
+ break;
+ default:
+ break;
+ }
+}
+
+char *
+sum_ypxfrstat(status)
+ int status;
+{
+ static char buff [16];
+
+ switch (status) {
+ case 1: return ("Success");
+ case 2: return ("Master's version not newer");
+ case -1: return ("Can't find server for map");
+ case -2: return ("No such domain");
+ case -3: return ("Resource allocation failure");
+ case -4: return ("RPC failure talking to server");
+ case -5: return ("Can't get master address");
+ case -6: return ("NIS server/map db error");
+ case -7: return ("Bad arguments");
+ case -8: return ("Local dbm operation failed");
+ case -9: return ("Local file I/O operation failed");
+ case -10: return ("Map version skew during transfer");
+ case -11: return ("Can't send clear req to local ypserv");
+ case -12: return ("No local order number in map");
+ case -13: return ("Transfer error");
+ case -14: return ("Transfer request refused");
+ default:
+ (void) sprintf(buff, "(%d)", status);
+ return (buff);
+ }
+ /* NOTREACHED */
+}
+
+static int
+sum_ypstat(line)
+ char *line;
+{
+ ulong_t status;
+ char *str;
+ char buff[16];
+
+ status = getxdr_u_long();
+ switch (status) {
+ case YP_TRUE: str = "OK"; break;
+ case YP_NOMORE: str = "No more entries"; break;
+ case YP_FALSE: str = "Fail"; break;
+ case YP_NOMAP: str = "No such map"; break;
+ case YP_NODOM: str = "No such domain"; break;
+ case YP_NOKEY: str = "No such key"; break;
+ case YP_BADOP: str = "Invalid operation"; break;
+ case YP_BADDB: str = "Bad database"; break;
+ case YP_YPERR: str = "Server error"; break;
+ case YP_BADARGS:str = "Bad args"; break;
+ case YP_VERS: str = "Version mismatch"; break;
+ default: (void) sprintf(buff, "(%lu)", status);
+ str = buff;
+ break;
+ }
+ (void) strcpy(line, str);
+ return ((int)status);
+}
+
+static int
+detail_ypstat()
+{
+ ulong_t status;
+ char buff[32];
+
+
+ status = sum_ypstat(buff);
+ (void) sprintf(get_line(0, 0),
+ "Status = %d (%s)",
+ status, buff);
+
+ return ((int)status);
+}
+
+char *
+sum_ypmaplist()
+{
+ static char buff[YPMAXMAP + 1];
+ int maps = 0;
+
+ if (setjmp(xdr_err)) {
+ (void) sprintf(buff, "%d+ maps", maps);
+ return (buff);
+ }
+
+ while (getxdr_long()) {
+ (void) getxdr_string(buff, YPMAXMAP);
+ maps++;
+ }
+
+ (void) sprintf(buff, "%d maps", maps);
+ return (buff);
+}
+
+void
+detail_ypmaplist()
+{
+ int maps = 0;
+
+ if (setjmp(xdr_err)) {
+ (void) sprintf(get_line(0, 0),
+ " %d+ maps. (Frame is incomplete)",
+ maps);
+ return;
+ }
+
+ (void) sprintf(get_line(0, 0), "Map list");
+
+ while (getxdr_long()) {
+ (void) showxdr_string(YPMAXMAP, " %s");
+ maps++;
+ }
+
+ (void) sprintf(get_line(0, 0), "%d maps", maps);
+}
+
+char *
+ypbind_error(err)
+ int err;
+{
+ static char buff[16];
+
+ switch (err) {
+ case YPBIND_ERR_ERR: return ("Internal error");
+ case YPBIND_ERR_NOSERV: return ("Internal error");
+ case YPBIND_ERR_RESC: return ("Resource allocation fail");
+ default:
+ (void) sprintf(buff, "(%d)", err);
+ return (buff);
+ }
+ /* NOTREACHED */
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nisplus.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nisplus.c
new file mode 100644
index 0000000000..e8babbca09
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nisplus.c
@@ -0,0 +1,1695 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1991, 1999, 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <setjmp.h>
+#include <string.h>
+
+#include <netinet/in.h>
+#include <rpc/types.h>
+#include <rpc/rpc.h>
+#include <rpc/xdr.h>
+#include <rpc/auth.h>
+#include <rpc/clnt.h>
+#include <rpc/rpc_msg.h>
+#include <rpcsvc/nis.h>
+#include <rpcsvc/nis_callback.h>
+
+#include "snoop.h"
+#include "nis_clnt.h"
+
+extern char *dlc_header;
+extern jmp_buf xdr_err;
+
+/*
+ * Number of spaces for each level of indentation. Since this value is
+ * assumed in most of the strings below, defining it is pretty quixotic.
+ */
+#define INDENT_SPACES 4
+
+/*
+ * ==== Old (pre Sep '91) format for public keys in NIS+ directories, now
+ * removed from the header files. Should be removed from snoop once
+ * we're sure we won't see old-style packets
+ */
+#ifndef SZ_PKEY
+#define SZ_PKEY 64
+#endif /* SZ_PKEY */
+
+/*
+ * ==== New (Aug '91) NIS+ remote procedure, which hasn't made into our
+ * header files yet. When it does, nuke this stuff.
+ */
+#ifndef NIS_UPDKEYS
+#define NIS_UPDKEYS 24
+#endif /* NIS_UPDKEYS */
+
+/*
+ * ==== Constants for public keys. We should use NIS_MAXKEYLEN and the
+ * key-type constants from the NIS+ header files, but at present (Aug '91)
+ * we're ahead of the header files.
+ */
+#define KEYLEN_FIRST 0
+#define KEYLEN_LIMIT 1024
+#define KEYTYPE_FIRST 0
+#define KEYTYPE_LIMIT 3
+
+
+static char *procnames_short[] = {
+ "Null", /* 0 */
+ "Lookup", /* 1 */
+ "Add", /* 2 */
+ "Modify", /* 3 */
+ "Remove", /* 4 */
+ "IBlist", /* 5 */
+ "IBadd", /* 6 */
+ "IBmodify", /* 7 */
+ "IBremove", /* 8 */
+ "IBfirst", /* 9 */
+ "IBnext", /* 10 */
+ "** Unused 11", /* 11 */
+ "FindDir", /* 12 */
+ "** Unused 13", /* 13 */
+ "Status", /* 14 */
+ "DumpLog", /* 15 */
+ "Dump", /* 16 */
+ "Callback", /* 17 */
+ "CheckpointTime", /* 18 */
+ "Checkpoint", /* 19 */
+ "Ping", /* 20 */
+ "ServerState", /* 21 */
+ "MakeDir", /* 22 */
+ "RemoveDir", /* 23 */
+ "UpdateKeys", /* 24 */
+};
+
+static char *procnames_long[] = {
+ "Null procedure", /* 0 */
+ "Lookup", /* 1 */
+ "Add", /* 2 */
+ "Modify", /* 3 */
+ "Remove", /* 4 */
+ "List IBase", /* 5 */
+ "Add to IBase", /* 6 */
+ "Modify IBase", /* 7 */
+ "Remove from IBase", /* 8 */
+ "IBase First Entry", /* 9 */
+ "IBase Next Entry", /* 10 */
+ "** 11 (Unused) **", /* 11 */
+ "Find Directory", /* 12 */
+ "** 13 (Unused) **", /* 13 */
+ "Get/Reset Statistics", /* 14 */
+ "Dump Directory Log", /* 15 */
+ "Dump Directory Contents", /* 16 */
+ "Check Callback Thread", /* 17 */
+ "Get Checkpoint Time", /* 18 */
+ "Establish Checkpoint", /* 19 */
+ "Ping Replicas", /* 20 */
+ "Change Server State", /* 21 */
+ "Make Directory", /* 22 */
+ "Remove Directory", /* 23 */
+ "Update Public Keys", /* 24 */
+};
+
+#define MAXPROC 24
+
+static void detail_bool(void);
+static void detail_callback(void);
+static void detail_cback_data(void);
+static void detail_cookie(void);
+static void detail_cp_result(void);
+static void detail_cptime(void);
+static void detail_dump_args(void);
+static void detail_ib_request(void);
+static void detail_fd_args(void);
+static void detail_fd_result(void);
+static void detail_log_entry(void);
+static void detail_log_result(void);
+static void detail_nis_attrs(int);
+static void detail_nis_error(void);
+static void detail_nis_name(void);
+static void detail_nis_result(void);
+static void detail_nis_taglist(void);
+static void detail_ns_request(void);
+static void detail_ping_args(void);
+static int is_printable(char *, unsigned int);
+static void sum_bool(char *);
+static void sum_callback(char *);
+static void sum_cback_data(char *);
+static void sum_cp_result(char *);
+static void sum_cptime(char *);
+static void sum_dump_args(char *);
+static void sum_ib_request(char *);
+static void sum_fd_args(char *);
+static void sum_fd_result(char *);
+static void sum_log_result(char *);
+static void sum_nis_error(char *);
+static void sum_nis_name(char *);
+static void sum_nis_result(char *);
+static void sum_nis_taglist(char *);
+static void sum_ns_request(char *);
+static void sum_ping_args(char *);
+
+void
+interpret_nisplus(flags, type, xid, vers, proc, data, len)
+ int flags, type, xid, vers, proc;
+ char *data;
+ int len;
+{
+ char *line;
+
+ if (proc < 0 || proc > MAXPROC)
+ return;
+
+ if (flags & F_SUM) {
+ if (setjmp(xdr_err)) {
+ return;
+ }
+
+ line = get_sum_line();
+
+ if (type == CALL) {
+ (void) sprintf(line,
+ "NIS+ C %s",
+ procnames_short[proc]);
+ line += strlen(line);
+
+ switch (proc) {
+ case NIS_LOOKUP:
+ case NIS_ADD:
+ case NIS_MODIFY:
+ case NIS_REMOVE:
+ sum_ns_request(line);
+ break;
+ case NIS_IBLIST:
+ case NIS_IBADD:
+ case NIS_IBMODIFY:
+ case NIS_IBREMOVE:
+ case NIS_IBFIRST:
+ case NIS_IBNEXT:
+ sum_ib_request(line);
+ break;
+ case NIS_FINDDIRECTORY:
+ sum_fd_args(line);
+ break;
+ case NIS_STATUS:
+ case NIS_SERVSTATE:
+ sum_nis_taglist(line);
+ break;
+ case NIS_DUMPLOG:
+ case NIS_DUMP:
+ sum_dump_args(line);
+ break;
+ case NIS_CALLBACK:
+ sum_callback(line);
+ break;
+ case NIS_PING:
+ sum_ping_args(line);
+ break;
+ case NIS_CPTIME:
+ case NIS_CHECKPOINT:
+ case NIS_MKDIR:
+ case NIS_RMDIR:
+ case NIS_UPDKEYS:
+ sum_nis_name(line);
+ break;
+ default:
+ /* === mutter about bogus procnums? */
+ break;
+ }
+
+ check_retransmit(line, xid);
+ } else {
+ (void) sprintf(line, "NIS+ R %s ",
+ procnames_short[proc]);
+ line += strlen(line);
+ switch (proc) {
+ case NIS_LOOKUP:
+ case NIS_ADD:
+ case NIS_MODIFY:
+ case NIS_REMOVE:
+ case NIS_IBLIST:
+ case NIS_IBADD:
+ case NIS_IBMODIFY:
+ case NIS_IBREMOVE:
+ case NIS_IBFIRST:
+ case NIS_IBNEXT:
+ sum_nis_result(line);
+ break;
+ case NIS_FINDDIRECTORY:
+ sum_fd_result(line);
+ break;
+ case NIS_STATUS:
+ case NIS_SERVSTATE:
+ sum_nis_taglist(line);
+ break;
+ case NIS_DUMPLOG:
+ case NIS_DUMP:
+ sum_log_result(line);
+ break;
+ case NIS_CALLBACK:
+ sum_bool(line);
+ break;
+ case NIS_CPTIME:
+ sum_cptime(line);
+ break;
+ case NIS_CHECKPOINT:
+ sum_cp_result(line);
+ break;
+ case NIS_PING:
+ break;
+ case NIS_MKDIR:
+ case NIS_RMDIR:
+ case NIS_UPDKEYS:
+ sum_nis_error(line);
+ break;
+ default:
+ /* === mutter about bogus procnums? */
+ break;
+ }
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("NIS+: ", "NIS+ Name Service", len);
+ show_space();
+ if (setjmp(xdr_err)) {
+ return;
+ }
+ (void) sprintf(get_line(0, 0),
+ "Proc = %d (%s)",
+ proc, procnames_long[proc]);
+
+ if (type == CALL) {
+ switch (proc) {
+ case NIS_LOOKUP:
+ case NIS_ADD:
+ case NIS_MODIFY:
+ case NIS_REMOVE:
+ detail_ns_request();
+ break;
+ case NIS_IBLIST:
+ case NIS_IBADD:
+ case NIS_IBMODIFY:
+ case NIS_IBREMOVE:
+ case NIS_IBFIRST:
+ case NIS_IBNEXT:
+ detail_ib_request();
+ break;
+ case NIS_FINDDIRECTORY:
+ detail_fd_args();
+ break;
+ case NIS_STATUS:
+ case NIS_SERVSTATE:
+ detail_nis_taglist();
+ break;
+ case NIS_DUMPLOG:
+ case NIS_DUMP:
+ detail_dump_args();
+ break;
+ case NIS_CALLBACK:
+ detail_callback();
+ break;
+ case NIS_PING:
+ detail_ping_args();
+ break;
+ case NIS_CPTIME:
+ case NIS_CHECKPOINT:
+ case NIS_MKDIR:
+ case NIS_RMDIR:
+ case NIS_UPDKEYS:
+ detail_nis_name();
+ break;
+ default:
+ /* === mutter about bogus procnums? */
+ break;
+ }
+ } else {
+ switch (proc) {
+ case NIS_LOOKUP:
+ case NIS_ADD:
+ case NIS_MODIFY:
+ case NIS_REMOVE:
+ case NIS_IBLIST:
+ case NIS_IBADD:
+ case NIS_IBMODIFY:
+ case NIS_IBREMOVE:
+ case NIS_IBFIRST:
+ case NIS_IBNEXT:
+ detail_nis_result();
+ break;
+ case NIS_FINDDIRECTORY:
+ detail_fd_result();
+ break;
+ case NIS_STATUS:
+ case NIS_SERVSTATE:
+ detail_nis_taglist();
+ break;
+ case NIS_DUMPLOG:
+ case NIS_DUMP:
+ detail_log_result();
+ break;
+ case NIS_CALLBACK:
+ detail_bool();
+ break;
+ case NIS_CPTIME:
+ detail_cptime();
+ break;
+ case NIS_CHECKPOINT:
+ detail_cp_result();
+ break;
+ case NIS_PING:
+ break;
+ case NIS_MKDIR:
+ case NIS_RMDIR:
+ case NIS_UPDKEYS:
+ detail_nis_error();
+ break;
+ default:
+ /* === mutter about bogus procnums? */
+ break;
+ }
+ }
+
+ show_trailer();
+ }
+}
+
+static char *cbnames_short[] = {
+ "Null", /* 0 */
+ "Receive", /* 1 */
+ "Finish", /* 2 */
+ "Error", /* 3 */
+};
+
+static char *cbnames_long[] = {
+ "Null procedure", /* 0 */
+ "Receive Callback Data", /* 1 */
+ "Finish Callback", /* 2 */
+ "Callback Error", /* 3 */
+};
+
+void
+interpret_nisp_cb(flags, type, xid, vers, proc, data, len)
+ int flags, type, xid, vers, proc;
+ char *data;
+ int len;
+{
+ char *line;
+
+ if (flags & F_SUM) {
+ if (setjmp(xdr_err)) {
+ return;
+ }
+
+ line = get_sum_line();
+
+ if (type == CALL) {
+ (void) sprintf(line,
+ "NIS+ Callback C %s",
+ cbnames_short[proc]);
+ line += strlen(line);
+
+ switch (proc) {
+ case CBPROC_RECEIVE:
+ sum_cback_data(line);
+ break;
+ case CBPROC_FINISH:
+ /* void: nothing to do */
+ break;
+ case CBPROC_ERROR:
+ sum_nis_error(line);
+ break;
+ default:
+ /* === mutter about bogus procnums? */
+ break;
+ }
+
+ check_retransmit(line, xid);
+ } else {
+ (void) sprintf(line, "NIS+ Callback R %s ",
+ cbnames_short[proc]);
+ line += strlen(line);
+ switch (proc) {
+ case CBPROC_RECEIVE:
+ sum_bool(line);
+ break;
+ case CBPROC_FINISH:
+ case CBPROC_ERROR:
+ /* void: nothing to do */
+ break;
+ default:
+ /* === mutter about bogus procnums? */
+ break;
+ }
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("NIS+ CB: ", "NIS+ Name Service Callback", len);
+ show_space();
+ if (setjmp(xdr_err)) {
+ return;
+ }
+ (void) sprintf(get_line(0, 0),
+ "Proc = %d (%s)",
+ proc, cbnames_long[proc]);
+
+ if (type == CALL) {
+ switch (proc) {
+ case CBPROC_RECEIVE:
+ detail_cback_data();
+ break;
+ case CBPROC_FINISH:
+ /* void: nothing to do */
+ break;
+ case CBPROC_ERROR:
+ detail_nis_error();
+ break;
+ default:
+ /* === mutter about bogus procnums? */
+ break;
+ }
+ } else {
+ switch (proc) {
+ case CBPROC_RECEIVE:
+ detail_bool();
+ break;
+ case CBPROC_FINISH:
+ case CBPROC_ERROR:
+ /* void: nothing to do */
+ break;
+ default:
+ /* === mutter about bogus procnums? */
+ break;
+ }
+ }
+
+ show_trailer();
+ }
+}
+
+/*
+ * stringof_XXX() routines -- return printable representations of various
+ * numeric values. Would be nice if we could get this from the NIS+
+ * library instead of reinventing them.
+ * N.B. Mucho use of pointer-to-static result types (ugh); don't expect the
+ * return value to stay good for long.
+ */
+
+#ifndef RIGHTS_FORMAT
+#define RIGHTS_FORMAT 1
+#endif /* RIGHTS_FORMAT */
+
+static struct {
+ int shift;
+ char *name;
+} rightsclasses[] = {
+#if RIGHTS_FORMAT == 1
+ 24, "",
+ 16, "",
+ 8, "",
+ 0, "",
+#elif RIGHTS_FORMAT == 2
+ 16, "o:",
+ 8, "g:",
+ 0, "w:",
+ 24, "u:",
+#else /* RIGHTS_FORMAT == 3 */
+ 16, "owner:",
+ 8, "group:",
+ 0, "world:",
+ 24, "unauth:",
+#endif /* RIGHTS_FORMAT */
+};
+
+char *
+stringof_rights(rights)
+ unsigned rights;
+{
+ static char rightsbuf[100];
+ char *p;
+ int i;
+
+ sprintf(rightsbuf, "%08x (", rights);
+ p = rightsbuf + strlen(rightsbuf);
+
+ for (i = 0; i < 4; i++) {
+ int shift = rightsclasses[i].shift;
+ strcpy(p, rightsclasses[i].name);
+ p += strlen(p);
+ *p++ = (rights >> shift) & NIS_READ_ACC ? 'r' : '-';
+ *p++ = (rights >> shift) & NIS_MODIFY_ACC ? 'm' : '-';
+ *p++ = (rights >> shift) & NIS_CREATE_ACC ? 'c' : '-';
+ *p++ = (rights >> shift) & NIS_DESTROY_ACC ? 'd' : '-';
+ *p++ = ' ';
+ rights &= ~((NIS_READ_ACC | NIS_MODIFY_ACC |
+ NIS_CREATE_ACC | NIS_DESTROY_ACC) << shift);
+ }
+ if (rights == 0) {
+ p[-1] = ')'; /* Nuke that last space */
+ p[ 0] = 0;
+ } else {
+ sprintf(p, "+ Unknown bits: %08x **)", rights);
+ }
+ return (rightsbuf);
+}
+
+char *
+stringof_ib_flags(flags)
+ unsigned flags;
+{
+ static char flagsbuf[120];
+
+ if (flags == 0) {
+ sprintf(flagsbuf, "%08x", flags);
+ return (flagsbuf);
+ }
+ sprintf(flagsbuf,
+ "%08x (%s%s%s%s",
+ flags,
+ flags & MOD_SAMEOBJ ? "MOD_SAMEOBJ, " : "",
+ flags & REM_MULTIPLE ? "REM_MULTIPLE, " : "",
+ flags & ADD_OVERWRITE ? "ADD_OVERWRITE, " : "",
+ flags & RETURN_RESULT ? "RETURN_RESULT, " : "");
+ flags &= ~(MOD_SAMEOBJ | REM_MULTIPLE | ADD_OVERWRITE | RETURN_RESULT);
+ if (flags != 0) {
+ sprintf(flagsbuf + strlen(flagsbuf),
+ "** Unknown bits %08x **, ", flags);
+ }
+ /* Replace the trailing ", " with ")" */
+ sprintf(flagsbuf + strlen(flagsbuf) - 2, ")");
+ return (flagsbuf);
+}
+
+char *
+stringof_colflags(flags)
+ unsigned flags;
+{
+ static char flagsbuf[120];
+
+ if (flags == 0) {
+ sprintf(flagsbuf, "%08x", flags);
+ return (flagsbuf);
+ }
+ sprintf(flagsbuf,
+ "%08x (%s%s%s%s%s",
+ flags,
+ flags & TA_CASE ? "Case-insensitive, " : "",
+ flags & TA_SEARCHABLE ? "Searchable, " : "",
+ flags & TA_XDR ? "XDR Encoded, " : "",
+ flags & TA_CRYPT ? "Encrypted, " : "",
+ flags & TA_BINARY ? "Binary, " : "");
+ flags &= ~(TA_CASE | TA_SEARCHABLE | TA_XDR | TA_CRYPT | TA_BINARY);
+ if (flags != 0) {
+ sprintf(flagsbuf + strlen(flagsbuf),
+ "** Unknown bits %08x **, ", flags);
+ }
+ /* Replace the trailing ", " with ")" */
+ sprintf(flagsbuf + strlen(flagsbuf) - 2, ")");
+ return (flagsbuf);
+}
+
+char *
+stringof_entryflags(flags)
+ unsigned flags;
+{
+ static char flagsbuf[100];
+
+ if (flags == 0) {
+ sprintf(flagsbuf, "%08x", flags);
+ return (flagsbuf);
+ }
+ sprintf(flagsbuf,
+ "%08x (%s%s%s%s",
+ flags,
+ flags & EN_MODIFIED ? "Modified, " : "",
+ flags & EN_XDR ? "XDR Encoded, " : "",
+ flags & EN_CRYPT ? "Encrypted, " : "",
+ flags & EN_BINARY ? "Binary, " : "");
+ flags &= ~(EN_MODIFIED | EN_XDR | EN_CRYPT | EN_BINARY);
+ if (flags != 0) {
+ sprintf(flagsbuf + strlen(flagsbuf),
+ "** Unknown bits %08x **, ", flags);
+ }
+ /* Replace the trailing ", " with ")" */
+ sprintf(flagsbuf + strlen(flagsbuf) - 2, ")");
+ return (flagsbuf);
+}
+
+char *
+stringof_groupflags(flags)
+ unsigned flags;
+{
+ static char flagsbuf[80];
+
+ if (flags == 0) {
+ sprintf(flagsbuf, "%08x", flags);
+ return (flagsbuf);
+ }
+ sprintf(flagsbuf,
+ "%08x (%s%s%s",
+ flags,
+ flags & NEGMEM_GROUPS ? "Negative, " : "",
+ flags & RECURS_GROUPS ? "Recursive, " : "",
+ flags & IMPMEM_GROUPS ? "Implicit, " : "");
+ flags &= ~(NEGMEM_GROUPS | RECURS_GROUPS | IMPMEM_GROUPS);
+ if (flags != 0) {
+ sprintf(flagsbuf + strlen(flagsbuf),
+ "** Unknown bits %08x **, ", flags);
+ }
+ /* Replace the trailing ", " with ")" */
+ sprintf(flagsbuf + strlen(flagsbuf) - 2, ")");
+ return (flagsbuf);
+}
+
+char *
+stringof_tag(ttype)
+ int ttype;
+{
+ switch (ttype) {
+ case TAG_DEBUG: return ("DEBUG");
+ case TAG_STATS: return ("STATS");
+ case TAG_GCACHE: return ("GCACHE");
+ case TAG_DCACHE: return ("DCACHE");
+ case TAG_OCACHE: return ("OCACHE");
+ case TAG_SECURE: return ("SECURE");
+
+#ifdef undef
+/*
+ * Old tags, removed September '91 (some of the tag-numbers have been
+ * reassigned, just to make things completely confusing).
+ */
+ case TAG_LOOKUPS: return ("LOOKUPS";
+ case TAG_S_LOOKUPS: return ("S_LOOKUPS";
+ case TAG_U_LOOKUPS: return ("U_LOOKUPS";
+#endif /* undef */
+ case TAG_OPSTATS: return ("OPSTATS");
+ case TAG_THREADS: return ("THREADS");
+ case TAG_UPDATES: return ("UPDATES");
+ case TAG_VISIBLE: return ("VISIBLE");
+ case TAG_S_DCACHE: return ("S_DCACHE");
+ case TAG_S_OCACHE: return ("S_OCACHE");
+ case TAG_S_GCACHE: return ("S_GCACHE");
+ case TAG_S_STORAGE: return ("S_STORAGE");
+
+ default: return ("*Unknown*");
+ }
+}
+
+char *
+stringof_otype(otype)
+ int otype;
+{
+ static char buf[30];
+
+ switch (otype) {
+ case NIS_BOGUS_OBJ: return ("BOGUS (uninitialized?)");
+ case NIS_NO_OBJ: return ("NULL");
+ case NIS_DIRECTORY_OBJ: return ("DIRECTORY");
+ case NIS_GROUP_OBJ: return ("GROUP");
+ case NIS_TABLE_OBJ: return ("TABLE");
+ case NIS_ENTRY_OBJ: return ("ENTRY");
+ case NIS_LINK_OBJ: return ("LINK");
+ case NIS_PRIVATE_OBJ: return ("PRIVATE");
+ default: sprintf(buf, "** Unknown (%d) **", otype);
+ return (buf);
+ }
+}
+
+char *
+stringof_entry_t(et)
+ int et;
+{
+ static char buf[30];
+
+ switch (et) {
+ case LOG_NOP: return ("LOG_NOP"); /* === lowercase? */
+ case ADD_NAME: return ("ADD_NAME");
+ case REM_NAME: return ("REM_NAME");
+ case MOD_NAME_OLD: return ("MOD_NAME_OLD");
+ case MOD_NAME_NEW: return ("MOD_NAME_NEW");
+ case ADD_IBASE: return ("ADD_IBASE");
+ case REM_IBASE: return ("REM_IBASE");
+ case MOD_IBASE: return ("MOD_IBASE");
+ case UPD_STAMP: return ("UPD_STAMP");
+ default: sprintf(buf, "** Unknown (%d) **", et);
+ return (buf);
+ }
+}
+
+char *
+stringof_ktype(ktype)
+ unsigned ktype;
+{
+ static char buf[40];
+ char *p;
+
+ sprintf(buf, "%d ", ktype);
+ p = buf + strlen(buf);
+ switch (ktype) {
+ case 0: strcpy(p, "(None)"); break;
+ case 1: strcpy(p, "(Diffie-Hellman)"); break;
+ case 2: strcpy(p, "(RSA)"); break;
+ case 4: strcpy(p, "(Diffie-Hellman Ext)"); break;
+ default: strcpy(p, "(** Unknown **)"); break;
+ }
+ return (buf);
+}
+
+static void
+sumxdr_nis_name(line)
+ char *line;
+{
+ char buff[NIS_MAXNAMELEN + 1];
+
+ (void) sprintf(line, " \"%s\"", getxdr_string(buff, NIS_MAXNAMELEN));
+ /* ==== ? could/should truncate long names a la showxdr_string() ? */
+}
+
+static unsigned
+sumxdr_nis_error(line)
+ char *line;
+{
+ unsigned err = getxdr_u_long();
+ sprintf(line, "[%s]", nis_sperrno(err));
+ return (err);
+}
+
+static void
+sum_ns_request(line)
+ char *line;
+{
+ sumxdr_nis_name(line);
+ /* Leave the optional object for detailed listings */
+}
+
+int nchars_attrval = 32;
+
+static void
+sum_ib_request(line)
+ char *line;
+{
+ unsigned long nattrs;
+ unsigned long len;
+ int pos;
+ char *val;
+ char buff[NIS_MAXATTRNAME + 1];
+
+ sumxdr_nis_name(line);
+ line += strlen(line);
+ nattrs = getxdr_u_long();
+ switch (nattrs) {
+ case 0:
+ /* === Could print "No attrs " */
+ break;
+ case 1:
+ sprintf(line, " [%s = ", getxdr_string(buff, NIS_MAXATTRNAME));
+ line += strlen(line);
+ pos = getxdr_pos();
+ val = getxdr_bytes((uint_t *)&len);
+ if (is_printable(val, len)) {
+ if (len - 1 > nchars_attrval) {
+ sprintf(line, "(%d-character value)", len);
+ } else {
+ sprintf(line, "\"%s\"", val);
+ }
+ } else {
+ if ((len - 1) * 2 > nchars_attrval) {
+ sprintf(line, "(%d-byte value)", len);
+ } else {
+ /*
+ * Too lazy to print hex ourselves; instead,
+ * back up and use snoop's hex-printer
+ */
+ setxdr_pos(pos);
+ len = getxdr_u_long(); /* Yes, we did know it */
+ strcpy(line, getxdr_hex(len));
+ }
+ }
+ line += strlen(line);
+ *line++ = ']';
+ *line = 0;
+ break;
+ default:
+ sprintf(line, " (Num attrs = %lu)", nattrs);
+ /* === Note that we haven't skipped the attrs */
+ break;
+ }
+ /* === Don't bother with flags, obj, cbhost, bufsize, cookie */
+}
+
+static void
+sum_fd_args(line)
+ char *line;
+{
+ sumxdr_nis_name(line);
+#ifdef LESS_TERSE
+ strcat(line, " for");
+ sumxdr_nis_name(line + strlen(line));
+#endif /* LESS_TERSE */
+}
+
+#define MAXTAGLEN 1024 /* num chars of tag to display */
+
+static void
+sum_nis_taglist(line)
+ char *line;
+{
+ char buff[MAXTAGLEN + 1];
+ unsigned ntags;
+ unsigned ttype;
+
+ ntags = getxdr_u_long();
+ if (ntags == 0) {
+ strcpy(line, " [But no tags !?]");
+ return;
+ }
+ ttype = getxdr_u_long();
+ sprintf(line,
+ " %s=\"%s\"", stringof_tag(ttype),
+ getxdr_string(buff, MAXTAGLEN));
+ if (ntags > 1) {
+ sprintf(line + strlen(line), " [and %lu more tags]", ntags - 1);
+ }
+}
+
+static void
+sum_dump_args(line)
+ char *line;
+{
+ sumxdr_nis_name(line);
+ strcat(line, " from ");
+ strcat(line, getxdr_time());
+}
+
+/*ARGSUSED*/
+static void
+sum_callback(line) /* i.e. the netobj argument to NIS_CALLBACK */
+ char *line;
+{
+ /* netobjs are pretty opaque, so don't try to summarize */
+}
+
+static void
+sum_ping_args(line)
+ char *line;
+{
+ sumxdr_nis_name(line);
+ strcat(line, " at ");
+ strcat(line, getxdr_time());
+}
+
+static void
+sum_nis_name(line)
+ char *line;
+{
+ sumxdr_nis_name(line);
+}
+
+static void
+sum_cback_data(line)
+ char *line;
+{
+ (void) sprintf(line, " (%lu entries)", getxdr_u_long());
+}
+
+static void
+sum_nis_result(line)
+ char *line;
+{
+ unsigned nobjs;
+
+ (void) sumxdr_nis_error(line);
+ nobjs = getxdr_u_long();
+ if (nobjs != 0) {
+ sprintf(line + strlen(line),
+ " and %lu object%s", nobjs, (nobjs == 1) ? "" : "s");
+ }
+}
+
+static void
+sum_fd_result(line)
+ char *line;
+{
+ (void) sumxdr_nis_error(line);
+#ifdef LESS_TERSE
+ strcat(line, " from");
+ sumxdr_nis_name(line + strlen(line));
+#endif /* LESS_TERSE */
+}
+
+static void
+sum_log_result(line)
+ char *line;
+{
+ uint_t dummy;
+ unsigned nents;
+
+ (void) sumxdr_nis_error(line);
+ (void) getxdr_bytes(&dummy); /* Skip netobj lr_cookie */
+ nents = getxdr_u_long();
+ sprintf(line + strlen(line),
+ " and %lu log entr%s", nents, (nents == 1) ? "y" : "ies");
+}
+
+static void
+sum_bool(line)
+ char *line;
+{
+ (void) sprintf(line, getxdr_u_long() ? "true" : "false");
+}
+
+static void
+sum_cptime(line)
+ char *line;
+{
+ (void) sprintf(line, " %s", getxdr_time());
+}
+
+static void
+sum_cp_result(line)
+ char *line;
+{
+ (void) sumxdr_nis_error(line);
+}
+
+static void
+sum_nis_error(line)
+ char *line;
+{
+ (void) sumxdr_nis_error(line);
+}
+
+#define SHOW_NIS_NAME(format) (void) showxdr_string(NIS_MAXNAMELEN, format)
+#define SHOW_NAME() SHOW_NIS_NAME("Name = %s")
+
+/*
+ * showxdr_longhex() -- ersatz wrapper around showxdr_hex() to ensure we
+ * don't display too much on one line. Multi-line results will
+ * === probably look pretty awful, but we may at least prevent core-dumps
+ */
+int nwords_longhex = 8;
+
+static void
+showxdr_longhex(len, fmt)
+ int len;
+ char *fmt;
+{
+ int nbytes_longhex = 4 * nwords_longhex;
+
+ while (len > nbytes_longhex) {
+ showxdr_hex(nbytes_longhex, fmt);
+ len -= nbytes_longhex;
+ }
+ showxdr_hex(len, fmt);
+}
+
+static void
+detail_nis_error()
+{
+ enum nis_error status;
+ int pos;
+
+ pos = getxdr_pos();
+ status = (enum nis_error) getxdr_u_long();
+ sprintf(get_line(pos, getxdr_pos()),
+ "Status = %lu (%s)", status, nis_sperrno(status));
+}
+
+void
+detail_nis_oid()
+{
+ showxdr_time(" Object created at %s");
+ showxdr_time(" last modified at %s");
+}
+
+static void
+detail_nis_server(outdent)
+ int outdent; /* Crock to get indentation right. Normally zero; */
+ /* set to four instead to remove leading spaces */
+{
+ unsigned nep;
+ unsigned ktype;
+ int pos1;
+
+ SHOW_NIS_NAME(outdent + " Hostname = %s");
+ nep = getxdr_u_long();
+ while (nep-- > 0) { /* list of endpoints */
+ (void) showxdr_string(1024,
+ outdent + " Uaddr = %s");
+ (void) showxdr_string(1024,
+ outdent + " Family = %s");
+ (void) showxdr_string(1024,
+ outdent + " Protocol = %s");
+ }
+ /*
+ * The format of the public-key information has changed (Aug '91).
+ * Ye olde style was a u_char[64] (which XDR's pretty inefficiently,
+ * but that's another matter). The new format has a key-type
+ * enumeration followed by an opaque<NIS_MAXKEYLEN>. For now
+ * (hence probably for all time) we'll hard-code the values of the
+ * enumeration and of NIS_MAXKEYLEN here rather than getting them
+ * from a header file. Tsk. Also for now, we'll try to guess
+ * whether we're looking at the new format or the old.
+ */
+ pos1 = getxdr_pos();
+ ktype = getxdr_u_long();
+ if (ktype < KEYTYPE_LIMIT || ktype == NIS_PK_DHEXT) {
+ int pos2;
+ unsigned klen;
+
+ pos2 = getxdr_pos();
+ klen = getxdr_u_long();
+ if (klen < KEYLEN_LIMIT) {
+ /*
+ * Smells like new-style key; let's hope so. If we're
+ * wrong, it may cause XDR underflow. Ugh. ====
+ */
+ sprintf(get_line(pos1, pos2),
+ outdent + " Key type = %s",
+ stringof_ktype(ktype));
+ sprintf(get_line(pos2, getxdr_pos()),
+ outdent + " Key length = %lu", klen);
+ if (klen != 0) {
+ if (ktype == NIS_PK_DHEXT) {
+ int marker;
+ int key_count;
+ marker = getxdr_pos();
+ key_count = 1;
+ while (getxdr_pos() < marker + klen) {
+ ulong_t a_u_long;
+ ushort_t keylen;
+ ushort_t algtype;
+ size_t binlen;
+ size_t binpadlen;
+
+ a_u_long = getxdr_u_long();
+ keylen = a_u_long >> 16;
+ algtype = a_u_long &
+ ((ushort_t)0xffff);
+ binlen = (keylen + 7) / 8;
+ binpadlen = ((binlen + 3) / 4)
+ * 4;
+ sprintf(get_line(0, 0),
+ " Key %d length = %hu bits",
+ key_count, keylen);
+ sprintf(get_line(0, 0),
+ " Algorithm type = %hu",
+ algtype);
+ if (keylen != 0)
+ showxdr_hex(binpadlen,
+ " Key value = %s");
+ key_count++;
+ }
+ setxdr_pos(marker + klen);
+ } else {
+ showxdr_hex(klen, outdent +
+ " Key value = %s");
+ }
+ }
+ return;
+ }
+ }
+ /*
+ * It didn't smell new, so assume it's the old u_char[64], which
+ * XDR's as 64 32-bit words (ouch).
+ */
+ setxdr_pos(4*SZ_PKEY + pos1);
+ sprintf(get_line(pos1, getxdr_pos()),
+ outdent + " [Public Key not displayed]");
+}
+
+void
+detail_directory_obj()
+{
+ unsigned nstyp;
+ unsigned nserv, nar;
+ int pos;
+ char *line;
+
+ SHOW_NIS_NAME(" Name = %s");
+ pos = getxdr_pos();
+ nstyp = getxdr_u_long();
+ line = get_line(pos, getxdr_pos());
+ (void) sprintf(line, " NStype = %lu", nstyp);
+ line += strlen(line);
+
+ switch (nstyp) {
+ case NIS:
+ sprintf(line, " (NIS+)");
+ break;
+ case SUNYP:
+ sprintf(line, " (NIS/YP)");
+ break;
+ case DNS:
+ sprintf(line, " (DNS)");
+ break;
+ default:
+ break;
+ }
+
+ nserv = showxdr_u_long(" Servers = %lu");
+ while (nserv-- > 0) { /* array of servers */
+ detail_nis_server(0);
+ }
+ (void) showxdr_u_long(" Time to live = %lu (seconds)");
+ /* === show time in more useful form too? */
+ nar = showxdr_u_long(" Access Rights = %lu");
+ while (nar-- > 0) { /* list of ar masks */
+ unsigned ar, otype;
+ pos = getxdr_pos();
+ ar = getxdr_u_long();
+ otype = getxdr_u_long();
+ sprintf(get_line(pos, getxdr_pos()), " %s for %s",
+ stringof_rights(ar), stringof_otype(otype));
+ }
+}
+
+void
+detail_group_obj()
+{
+ unsigned val;
+ int pos;
+
+ pos = getxdr_pos();
+ val = getxdr_u_long();
+ sprintf(get_line(pos, getxdr_pos()),
+ " Flags = %s", stringof_groupflags(val));
+ val = showxdr_u_long(" Number of Members = %lu");
+ while (val-- > 0) {
+ SHOW_NIS_NAME(" %s");
+ }
+}
+
+void
+detail_table_obj()
+{
+ unsigned ncols;
+ unsigned col;
+ (void) showxdr_string(1024, " Table Type = %s");
+ (void) showxdr_long (" Max Columns = %d");
+ (void) showxdr_char (" Separator = '%c'"); /* ==== improve */
+ ncols = showxdr_u_long(" Num Columns = %lu");
+ for (col = 1; col <= ncols; col++) {
+ int pos1, pos2;
+ unsigned val;
+ char format[23];
+ /* === Print all three on one line? */
+ sprintf(format, "%6d: ColName = \"%%s\"", col);
+ (void) showxdr_string(NIS_MAXATTRNAME, format);
+ pos1 = getxdr_pos();
+ val = getxdr_u_long();
+ pos2 = getxdr_pos();
+ sprintf(get_line(pos1, pos2),
+ " Flags = %s", stringof_colflags(val));
+ val = getxdr_u_long();
+ sprintf(get_line(pos2, getxdr_pos()),
+ " Rights = %s", stringof_rights(val));
+ }
+ (void) showxdr_string(NIS_MAXPATH, " Search Path = \"%s\"");
+}
+
+void
+detail_entry_obj()
+{
+ unsigned col, ncols;
+ char *entyp;
+ int is_nis_object = 0;
+
+ entyp = showxdr_string(1024, " Entry Type = \"%s\"");
+ is_nis_object = (entyp != 0 && strcmp(entyp, "NIS object") == 0);
+ /* Don't rely on entyp[] remaining valid */
+ ncols = showxdr_u_long(" Num Columns = %lu");
+ for (col = 1; col <= ncols; col++) {
+ int pos1, pos2, len;
+ unsigned flags;
+ /* === Print both on one line? */
+ pos1 = getxdr_pos();
+ flags = getxdr_u_long();
+ pos2 = getxdr_pos();
+ if (ncols == 1) {
+ sprintf(get_line(pos1, pos2),
+ " Flags = %s",
+ stringof_entryflags(flags));
+ } else {
+ sprintf(get_line(pos1, pos2),
+ "%6d: Flags = %s",
+ col, stringof_entryflags(flags));
+ }
+ if (flags & (EN_BINARY | EN_CRYPT | EN_XDR)) {
+ len = getxdr_u_long();
+ if (is_nis_object &&
+ len == sizeof (unsigned long) &&
+ (flags & EN_CRYPT) == 0) {
+ /* Special case for type = "NIS object" */
+ unsigned otype = getxdr_u_long();
+ sprintf(get_line(pos2, getxdr_pos()),
+ " Value = %08x (ObjType = %s)",
+ otype, stringof_otype(otype));
+ } else {
+ showxdr_longhex(len,
+ " Value = Binary %s");
+ }
+ } else {
+ showxdr_string(NIS_MAXATTRVAL,
+ " Value = ASCII \"%s\"");
+ }
+ }
+}
+
+void
+detail_link_obj()
+{
+ int pos;
+ int val;
+
+ pos = getxdr_pos();
+ val = getxdr_long();
+ sprintf(get_line(pos, getxdr_pos()),
+ " Real Type= %08x (%s)", val, stringof_otype(val));
+ detail_nis_attrs(0);
+ SHOW_NIS_NAME(" Real Name= %s");
+}
+
+void
+detail_private_obj()
+{
+ /* ==== Need something fancier than this to be really useful */
+ showxdr_longhex(getxdr_u_long(), " Data = Binary %s");
+}
+
+void
+detail_nis_object()
+{
+ unsigned rights, otype;
+ int pos;
+
+ detail_nis_oid();
+ SHOW_NIS_NAME(" Name = %s");
+ SHOW_NIS_NAME(" Owner = %s");
+ SHOW_NIS_NAME(" Group = %s");
+ SHOW_NIS_NAME(" Domain = %s");
+ pos = getxdr_pos();
+ rights = getxdr_u_long();
+ sprintf(get_line(pos, getxdr_pos()), " Rights = %s",
+ stringof_rights(rights));
+ (void) showxdr_u_long(" Lifetime = %8lu (seconds)");
+
+ /* ?? show_space(); */
+ pos = getxdr_pos();
+ otype = getxdr_u_long();
+ (void) sprintf(get_line(pos, getxdr_pos()),
+ " ObjType = %08x (%s)",
+ otype, stringof_otype(otype));
+ switch (otype) {
+ case NIS_DIRECTORY_OBJ:
+ detail_directory_obj();
+ break;
+ case NIS_GROUP_OBJ:
+ detail_group_obj();
+ break;
+ case NIS_TABLE_OBJ:
+ detail_table_obj();
+ break;
+ case NIS_ENTRY_OBJ:
+ detail_entry_obj();
+ break;
+ case NIS_LINK_OBJ:
+ detail_link_obj();
+ break;
+ case NIS_PRIVATE_OBJ:
+ detail_private_obj();
+ break;
+ default:
+ /* ==== Can't do anything clever, right? */
+ break;
+ }
+}
+
+static void
+detail_ns_request()
+{
+ unsigned nobjs;
+
+ SHOW_NAME();
+ nobjs = getxdr_u_long();
+ /* nobjs should be 0 or 1 only; === should check it ? */
+ if (nobjs != 0) {
+ sprintf(get_line(0, 0), "Object included:");
+ }
+ while (nobjs-- > 0) {
+ detail_nis_object();
+ }
+}
+
+static void
+detail_ib_request()
+{
+ unsigned len;
+ unsigned flags;
+ int pos;
+
+ SHOW_NAME();
+ detail_nis_attrs(INDENT_SPACES);
+ pos = getxdr_pos();
+ flags = getxdr_u_long();
+ sprintf(get_line(pos, getxdr_pos()),
+ "Flags = %s", stringof_ib_flags(flags));
+ pos = getxdr_pos();
+ len = getxdr_u_long();
+ if (len == 0) {
+ sprintf(get_line(pos, getxdr_pos()), "No included object");
+ } else {
+ /* len should be 0 or 1 only; === should check it ? */
+ sprintf(get_line(pos, getxdr_pos()), "Included object:");
+ while (len-- > 0) {
+ detail_nis_object();
+ }
+ }
+ pos = getxdr_pos();
+ len = getxdr_u_long();
+ if (len == 0) {
+ sprintf(get_line(pos, getxdr_pos()), "No callback");
+ } else {
+ /* len should be 0 or 1 only; === should check it ? */
+ sprintf(get_line(pos, getxdr_pos()), "Callback info:");
+ while (len-- > 0) {
+ detail_nis_server(INDENT_SPACES);
+ }
+ }
+ (void) showxdr_u_long("Bufsize = %lu");
+ detail_cookie();
+}
+
+static void
+detail_fd_args()
+{
+ SHOW_NAME();
+ SHOW_NIS_NAME("Requester = %s");
+}
+
+static void
+detail_nis_taglist()
+{
+ unsigned ntags;
+ unsigned tagn;
+
+ ntags = showxdr_u_long("Number of tags = %lu");
+ for (tagn = 1; tagn < ntags; tagn++) {
+ unsigned ttype;
+ int pos;
+
+ if (ntags != 1) {
+ sprintf(get_line(0, 0), " Tag %d:", tagn);
+ }
+ pos = getxdr_pos();
+ ttype = getxdr_u_long();
+ sprintf(get_line(pos, getxdr_pos()),
+ " Tag name = %08x (%s)",
+ ttype, stringof_tag(ttype));
+ showxdr_string(1024, " Tag value = \"%s\"");
+ /* ^ ==== nis.x says it's a string rather than an opaque, */
+ /* but is it really? */
+ }
+}
+
+static void
+detail_dump_args()
+{
+ unsigned nserv;
+ int pos;
+
+ SHOW_NAME();
+ showxdr_time("Time = %s");
+
+ pos = getxdr_pos();
+ nserv = getxdr_u_long();
+ if (nserv == 1) {
+ sprintf(get_line(pos, getxdr_pos()), "Callback:");
+ } else if (nserv > 1) {
+ sprintf(get_line(pos, getxdr_pos()),
+ "Num callbacks = %lu (!?):", nserv);
+ }
+ while (nserv-- > 0) {
+ detail_nis_server(INDENT_SPACES);
+ }
+}
+
+static void
+detail_callback() /* i.e. the netobj argument to NIS_CALLBACK */
+{
+ detail_cookie();
+}
+
+static void
+detail_cookie()
+{
+ unsigned len;
+ int pos;
+
+ pos = getxdr_pos();
+ len = getxdr_u_long();
+ if (len == 0) {
+ sprintf(get_line(pos, getxdr_pos()), "Cookie = NULL");
+ } else {
+ showxdr_longhex(len, "Cookie = %s");
+ }
+}
+
+static void
+detail_ping_args()
+{
+ SHOW_NAME();
+ showxdr_time("Time = %s");
+}
+
+static void
+detail_nis_name()
+{
+ SHOW_NAME();
+}
+
+static void
+detail_cback_data()
+{
+ unsigned nobjs, no;
+ nobjs = showxdr_u_long("Number of entries = %lu");
+ for (no = 1; no <= nobjs; no++) {
+ if (getxdr_long() == 0) {
+ sprintf(get_line(0, 0), " Entry %d is NULL (!?)", no);
+ } else {
+ if (nobjs != 1) {
+
+ sprintf(get_line(0, 0), " Entry %d:", no);
+ }
+ detail_nis_object();
+ }
+ }
+}
+
+static void
+detail_nis_result()
+{
+ int pos;
+ unsigned no, nobjs;
+
+ detail_nis_error();
+ pos = getxdr_pos();
+ nobjs = getxdr_u_long();
+ sprintf(get_line(pos, getxdr_pos()), "Number of objects = %lu", nobjs);
+ for (no = 1; no <= nobjs; no++) {
+ if (nobjs != 1) {
+ sprintf(get_line(0, 0), " Object %d:", no);
+ }
+ detail_nis_object();
+ }
+ detail_cookie();
+ showxdr_u_long("Server ticks = %8lu");
+ showxdr_u_long("Database ticks = %8lu");
+ showxdr_u_long("Cache ticks = %8lu"); /* These should always be */
+ showxdr_u_long("Client ticks = %8lu"); /* zero on the wire, yes? */
+}
+
+static void
+detail_fd_result()
+{
+ int pos;
+ unsigned len;
+ int dir_pos;
+ unsigned dir_len;
+ int dir_end;
+
+ detail_nis_error();
+ SHOW_NAME();
+ /* nis.x doesn't say so, but the opaque contains a directory_obj */
+ dir_len = getxdr_u_long(); /* Byte-count at start of opaque */
+ if (dir_len == 0) {
+ sprintf(get_line(0, 0), "Directory object = NULL");
+ } else {
+ sprintf(get_line(0, 0), "Directory object:");
+ dir_pos = getxdr_pos();
+ dir_end = ((dir_len + 3) & ~3) + dir_pos;
+ /* ^^^^^^^^^^ Did XDR do this already? */
+ detail_directory_obj();
+ pos = getxdr_pos();
+ if (pos != dir_end) {
+ sprintf(get_line(pos, dir_end),
+ "(Skipping %d unused bytes at end of directory object)",
+ dir_end - pos);
+ setxdr_pos(dir_end);
+ }
+ }
+ len = getxdr_u_long();
+ if (len == 0) {
+ sprintf(get_line(pos, getxdr_pos()),
+ "Signature = NULL");
+ } else {
+ if (len > 8) {
+ sprintf(get_line(pos, getxdr_pos()),
+ "Signature length is %lu, expected <= 8 bytes",
+ len);
+ }
+ showxdr_longhex(len, "Signature = %s");
+ }
+}
+
+/*
+ * is_printable() -- looks for a string of (len-1) printable characters
+ * followed by a NUL. Characters are deemed printable iff ctype's
+ * isprint() macro says so, thus tab (^I) is regarded as unprintable.
+ * === Is this test more stringent than we want, esp wrt placement of NUL?
+ */
+static int
+is_printable(str, len)
+ char *str;
+ unsigned len;
+{
+ while (len > 1) {
+ if (!isprint(*str)) {
+ return (0);
+ }
+ str++, len--;
+ }
+ return (len == 1 && *str == 0);
+}
+
+static void
+detail_nis_attrs(outdent)
+ int outdent; /* Crock to get indentation right. Normally zero; */
+ /* set to four instead to remove leading spaces */
+{
+ unsigned nattrs;
+
+ nattrs = showxdr_u_long(outdent + " Number of attributes = %lu");
+ while (nattrs-- > 0) {
+ char *str;
+ unsigned len;
+ int pos;
+
+ showxdr_string(NIS_MAXATTRNAME,
+ outdent + " AttrName = %s");
+ pos = getxdr_pos();
+ str = getxdr_bytes(&len);
+ if (is_printable(str, len)) {
+ setxdr_pos(pos);
+ showxdr_string(NIS_MAXATTRVAL, outdent +
+ " AttrVal = ASCII \"%s\"");
+ } else {
+ setxdr_pos(pos);
+ len = getxdr_u_long(); /* Yes, we knew it already */
+ showxdr_longhex(len, outdent +
+ " AttrVal = Binary %s");
+ }
+ }
+}
+
+static void
+detail_log_entry()
+{
+ int pos;
+ long val;
+
+ showxdr_time(" Log Time = %s");
+ pos = getxdr_pos();
+ val = getxdr_long();
+ sprintf(get_line(pos, getxdr_pos()), " Log Type = %d (%s)",
+ val, stringof_entry_t(val));
+ SHOW_NIS_NAME(" Principal = %s");
+ SHOW_NIS_NAME(" Table/dir = %s");
+ detail_nis_attrs(0);
+ sprintf(get_line(0, 0), " Object Value:");
+ detail_nis_object();
+}
+
+static void
+detail_log_result()
+{
+ unsigned len, nents, en;
+ int pos;
+
+ detail_nis_error();
+ pos = getxdr_pos();
+ len = getxdr_u_long();
+ detail_cookie();
+ nents = showxdr_u_long("Number of log entries = %lu");
+ for (en = 1; en <= nents; en++) {
+ if (nents != 1) {
+ sprintf(get_line(0, 0), " Log Entry %d", en);
+ }
+ detail_log_entry();
+ }
+}
+
+static void
+detail_bool()
+{
+ int pos;
+ unsigned val;
+
+ pos = getxdr_pos();
+ val = getxdr_u_long();
+ sprintf(get_line(pos, getxdr_pos()),
+ "Result = %s", val ? "true" : "false");
+}
+
+static void
+detail_cptime()
+{
+ showxdr_time("Time = %s");
+}
+
+static void
+detail_cp_result()
+{
+ detail_nis_error();
+ showxdr_u_long("Ticks (Server) = %lu");
+ showxdr_u_long(" (Database) = %lu");
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nlm.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nlm.c
new file mode 100644
index 0000000000..569aee1936
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nlm.c
@@ -0,0 +1,1133 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1991, 1998, 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <setjmp.h>
+#include <string.h>
+
+#ifdef notdef
+#include <rpc/xdr.h>
+#include <rpc/auth.h>
+#include <rpc/rpc_msg.h>
+#endif /* notdef */
+#include <rpcsvc/nlm_prot.h>
+#include "snoop.h"
+
+extern char *dlc_header;
+extern jmp_buf xdr_err;
+
+extern void check_retransmit();
+static void interpret_nlm_1();
+static void interpret_nlm_3();
+static void interpret_nlm_4();
+static char *nameof_access();
+static char *nameof_mode();
+static char *nameof_stat();
+static char *nameof_stat4();
+static void show_cancargs();
+static void show_cancargs4();
+static void show_lock();
+static void show_lock4();
+static void show_lockargs();
+static void show_lockargs4();
+static void show_netobj();
+static void show_nlm_access();
+static void show_nlm_mode();
+static void show_notify();
+static void show_res();
+static void show_res4();
+static void show_share();
+static void show_shareargs();
+static void show_shareres();
+static void show_shareres4();
+static enum nlm_stats show_stat();
+static enum nlm4_stats show_stat4();
+static void show_testargs();
+static void show_testargs4();
+static void show_testres();
+static void show_testres4();
+static void show_unlockargs();
+static void show_unlockargs4();
+static void skip_netobj();
+static char *sum_lock();
+static char *sum_lock4();
+static char *sum_netobj();
+static char *sum_notify();
+static char *sum_share();
+
+void
+interpret_nlm(flags, type, xid, vers, proc, data, len)
+ int flags, type, xid, vers, proc;
+ char *data;
+ int len;
+{
+ switch (vers) {
+ case 1: interpret_nlm_1(flags, type, xid, vers, proc, data, len);
+ break;
+ case 3: interpret_nlm_3(flags, type, xid, vers, proc, data, len);
+ break;
+ case 4: interpret_nlm_4(flags, type, xid, vers, proc, data, len);
+ break;
+ }
+}
+
+
+/* ------------ V E R S I O N 1 ---------------------------------- */
+
+static char *procnames_short_1[] = {
+ "Null1", /* 0 */
+ "TEST1", /* 1 */
+ "LOCK1", /* 2 */
+ "CANCEL1", /* 3 */
+ "UNLOCK1", /* 4 */
+ "GRANTED1", /* 5 */
+ "TEST MSG1", /* 6 */
+ "LOCK MSG1", /* 7 */
+ "CANCEL MSG1", /* 8 */
+ "UNLOCK MSG1", /* 9 */
+ "GRANTED MSG1", /* 10 */
+ "TEST RES1", /* 11 */
+ "LOCK RES1", /* 12 */
+ "CANCEL RES1", /* 13 */
+ "UNLOCK RES1", /* 14 */
+ "GRANTED RES1", /* 15 */
+};
+
+static char *procnames_long_1[] = {
+ "Null procedure", /* 0 */
+ "Test", /* 1 */
+ "Lock", /* 2 */
+ "Cancel", /* 3 */
+ "Unlock", /* 4 */
+ "Granted", /* 5 */
+ "Test message", /* 6 */
+ "Lock message", /* 7 */
+ "Cancel message", /* 8 */
+ "Unlock message", /* 9 */
+ "Granted message", /* 10 */
+ "Test result", /* 11 */
+ "Lock result", /* 12 */
+ "Cancel result", /* 13 */
+ "Unlock result", /* 14 */
+ "Granted result", /* 15 */
+};
+
+/* Highest procedure number that officially belongs to version 1. */
+#define MAXPROC_1 15
+
+/* ARGSUSED */
+static void
+interpret_nlm_1(flags, type, xid, vers, proc, data, len)
+ int flags, type, xid, vers, proc;
+ char *data;
+ int len;
+{
+ char *line;
+
+ if (proc < 0 || proc > MAXPROC_1)
+ return;
+
+ if (flags & F_SUM) {
+ if (setjmp(xdr_err)) {
+ return;
+ }
+
+ line = get_sum_line();
+
+ if (type == CALL) {
+ (void) sprintf(line,
+ "NLM C %s",
+ procnames_short_1[proc]);
+ line += strlen(line);
+ switch (proc) {
+ case NLM_TEST:
+ case NLM_GRANTED:
+ case NLM_TEST_MSG:
+ case NLM_GRANTED_MSG:
+ /* testargs */
+ (void) strcat(line, sum_netobj("OH"));
+ (void) getxdr_bool(); /* Excl */
+ (void) strcat(line, sum_lock());
+ break;
+ case NLM_LOCK:
+ case NLM_LOCK_MSG:
+ /* lockargs */
+ (void) strcat(line, sum_netobj("OH"));
+ (void) getxdr_bool(); /* Block */
+ (void) getxdr_bool(); /* Excl */
+ (void) strcat(line, sum_lock());
+ break;
+ case NLM_CANCEL:
+ case NLM_CANCEL_MSG:
+ /* cancargs */
+ (void) strcat(line, sum_netobj("OH"));
+ (void) getxdr_bool(); /* Block */
+ (void) getxdr_bool(); /* Excl */
+ (void) strcat(line, sum_lock());
+ break;
+ case NLM_UNLOCK:
+ case NLM_UNLOCK_MSG:
+ /* unlockargs */
+ (void) strcat(line, sum_netobj("OH"));
+ (void) strcat(line, sum_lock());
+ break;
+ case NLM_TEST_RES:
+ /* testres */
+ (void) strcat(line, sum_netobj("OH"));
+ (void) strcat(line, " ");
+ (void) strcat(line,
+ nameof_stat(getxdr_u_long()));
+ break;
+ case NLM_LOCK_RES:
+ case NLM_CANCEL_RES:
+ case NLM_UNLOCK_RES:
+ case NLM_GRANTED_RES:
+ /* res */
+ (void) strcat(line, sum_netobj("OH"));
+ (void) strcat(line, " ");
+ (void) strcat(line,
+ nameof_stat(getxdr_u_long()));
+ break;
+ }
+ check_retransmit(line, (ulong_t)xid);
+ } else {
+ (void) sprintf(line, "NLM R %s",
+ procnames_short_1[proc]);
+ line += strlen(line);
+ switch (proc) {
+ case NLM_TEST:
+ /* testres */
+ (void) strcat(line, sum_netobj("OH"));
+ (void) strcat(line, " ");
+ (void) strcat(line,
+ nameof_stat(getxdr_u_long()));
+ break;
+ case NLM_LOCK:
+ case NLM_CANCEL:
+ case NLM_UNLOCK:
+ case NLM_GRANTED:
+ /* res */
+ (void) strcat(line, sum_netobj("OH"));
+ (void) strcat(line, " ");
+ (void) strcat(line,
+ nameof_stat(getxdr_u_long()));
+ break;
+ }
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("NLM: ", "Network Lock Manager", len);
+ show_space();
+ if (setjmp(xdr_err)) {
+ return;
+ }
+ (void) sprintf(get_line(0, 0),
+ "Proc = %d (%s)",
+ proc, procnames_long_1[proc]);
+ if (type == CALL) {
+ switch (proc) {
+ case NLM_TEST:
+ case NLM_GRANTED:
+ case NLM_TEST_MSG:
+ case NLM_GRANTED_MSG:
+ show_testargs();
+ break;
+ case NLM_LOCK:
+ case NLM_LOCK_MSG:
+ show_lockargs();
+ break;
+ case NLM_CANCEL:
+ case NLM_CANCEL_MSG:
+ show_cancargs();
+ break;
+ case NLM_UNLOCK:
+ case NLM_UNLOCK_MSG:
+ show_unlockargs();
+ break;
+ case NLM_TEST_RES:
+ show_testres();
+ break;
+ case NLM_LOCK_RES:
+ case NLM_CANCEL_RES:
+ case NLM_UNLOCK_RES:
+ case NLM_GRANTED_RES:
+ show_res();
+ break;
+ }
+ } else {
+ switch (proc) {
+ case NLM_TEST:
+ show_testres();
+ break;
+ case NLM_LOCK:
+ case NLM_CANCEL:
+ case NLM_UNLOCK:
+ case NLM_GRANTED:
+ show_res();
+ break;
+ case NLM_TEST_MSG:
+ case NLM_LOCK_MSG:
+ case NLM_CANCEL_MSG:
+ case NLM_UNLOCK_MSG:
+ case NLM_GRANTED_MSG:
+ case NLM_TEST_RES:
+ case NLM_LOCK_RES:
+ case NLM_CANCEL_RES:
+ case NLM_UNLOCK_RES:
+ case NLM_GRANTED_RES:
+ break;
+ }
+ }
+ show_trailer();
+ }
+}
+
+#define roundup(sz) ((sz / 4 + (sz % 4 > 0)) * 4)
+
+/*
+ * Skip a netobj.
+ * Make sure an integral number of words
+ * are skipped.
+ */
+static void
+skip_netobj()
+{
+ int sz = getxdr_u_long();
+
+ xdr_skip(roundup(sz));
+}
+
+static char *
+sum_netobj(handle)
+ char *handle;
+{
+ int i, l, sz;
+ int sum = 0;
+ static char buff[32];
+
+ sz = getxdr_u_long();
+ for (i = 0; i < sz; i += 4) {
+ l = getxdr_long();
+ sum ^= (l >> 16) ^ l;
+ }
+ (void) sprintf(buff, " %s=%04X", handle, sum & 0xFFFF);
+ return (buff);
+}
+
+static void
+show_netobj(fmt)
+ char *fmt;
+{
+ int sz, chunk;
+ char *p;
+ char buff[64];
+ int needspace;
+
+ sz = getxdr_u_long(); /* size of the netobj */
+
+ if (sz == 0) {
+ (void) sprintf(get_line(0, 0), fmt, "<null>");
+ } else {
+ needspace = sz > 16;
+ (void) strcpy(buff, fmt);
+ while (sz > 0) {
+ chunk = sz > 16 ? 16 : sz;
+ sz -= 16;
+ (void) showxdr_hex(chunk, buff);
+ /*
+ * For every line after the first, blank out
+ * everything in the format string before the "%s".
+ */
+ for (p = buff; *p != '%'; p++)
+ *p = ' ';
+ }
+ if (needspace)
+ show_space();
+ }
+}
+
+static char *
+sum_lock()
+{
+ static char buff[LM_MAXSTRLEN + 1];
+ char *cp = buff;
+ long id;
+ ulong_t off, len;
+
+ (void) getxdr_string(buff, LM_MAXSTRLEN); /* Caller */
+ (void) strcpy(buff, sum_netobj("FH")); /* Fh */
+ cp += strlen(buff);
+ skip_netobj(); /* Owner */
+ id = getxdr_long();
+ off = getxdr_u_long();
+ len = getxdr_u_long();
+ (void) sprintf(cp, " PID=%ld Region=%lu:%lu", id, off, len);
+ return (buff);
+}
+
+static void
+show_lock()
+{
+ showxdr_string(LM_MAXSTRLEN, "Caller = %s");
+ show_netobj("Filehandle = %s");
+ show_netobj("Lock owner = %s");
+ showxdr_long("Svid = %ld (process id)");
+ showxdr_u_long("Offset = %lu bytes");
+ showxdr_u_long("Length = %lu bytes");
+}
+
+static void
+show_cancargs()
+{
+ show_netobj("Cookie = %s");
+ showxdr_bool("Block = %s");
+ showxdr_bool("Exclusive = %s");
+ show_lock();
+}
+
+static void
+show_lockargs()
+{
+ show_netobj("Cookie = %s");
+ showxdr_bool("Block = %s");
+ showxdr_bool("Exclusive = %s");
+ show_lock();
+ showxdr_bool("Reclaim = %s");
+ showxdr_long("State = %ld");
+}
+
+static void
+show_unlockargs()
+{
+ show_netobj("Cookie = %s");
+ show_lock();
+}
+
+static void
+show_testargs()
+{
+ show_netobj("Cookie = %s");
+ showxdr_bool("Exclusive = %s");
+ show_lock();
+}
+
+static void
+show_res()
+{
+ show_netobj("Cookie = %s");
+ (void) show_stat();
+}
+
+static char *
+nameof_stat(s)
+ ulong_t s;
+{
+ switch ((enum nlm_stats) s) {
+ case nlm_granted: return ("granted");
+ case nlm_denied: return ("denied");
+ case nlm_denied_nolocks:return ("denied (no locks)");
+ case nlm_blocked: return ("blocked");
+ case nlm_denied_grace_period: return ("denied (grace period)");
+ case nlm_deadlck: return ("deadlock");
+ default: return ("?");
+ }
+}
+
+static enum nlm_stats
+show_stat()
+{
+ enum nlm_stats s;
+
+ s = (enum nlm_stats) getxdr_u_long();
+ (void) sprintf(get_line(0, 0),
+ "Status = %d (%s)",
+ s, nameof_stat((ulong_t)s));
+
+ return (s);
+}
+
+static void
+show_testres()
+{
+ show_netobj("Cookie = %s");
+ if (show_stat() == nlm_denied) {
+ showxdr_bool("Exclusive = %s");
+ showxdr_long("Svid = %ld (process id)");
+ show_netobj("Owner handle = %s");
+ showxdr_u_long("Offset = %lu bytes");
+ showxdr_u_long("Length = %lu bytes");
+ }
+}
+
+
+/* ------------ V E R S I O N 3 ---------------------------------- */
+
+static char *procnames_short_3[] = {
+ "SHARE3", /* 20 */
+ "UNSHARE3", /* 21 */
+ "NM_LOCK3", /* 22 */
+ "FREEALL3", /* 23 */
+};
+
+static char *procnames_long_3[] = {
+ "Share", /* 20 */
+ "Unshare", /* 21 */
+ "Unmonitored lock", /* 22 */
+ "Free all", /* 23 */
+};
+
+/* Maximum procedure number for version 3. */
+#define MAXPROC_3 23
+
+static void
+interpret_nlm_3(flags, type, xid, vers, proc, data, len)
+ int flags, type, xid, vers, proc;
+ char *data;
+ int len;
+{
+ char *line, *pl;
+ ulong_t i;
+
+ if (proc < 0 || proc > MAXPROC_3)
+ return;
+
+ /*
+ * Version 3 is a superset of version 1
+ */
+ if (proc >= 0 && proc <= MAXPROC_1) {
+ interpret_nlm_1(flags, type, xid, vers, proc, data, len);
+ return;
+ }
+
+ if (flags & F_SUM) {
+ if (setjmp(xdr_err)) {
+ return;
+ }
+
+ line = get_sum_line();
+
+ if (type == CALL) {
+ (void) sprintf(line,
+ "NLM C %s",
+ procnames_short_3[proc-20]);
+ line += strlen(line);
+ switch (proc) {
+ case NLM_SHARE:
+ case NLM_UNSHARE:
+ (void) strcat(line, sum_netobj("OH"));
+ (void) strcat(line, sum_share());
+ break;
+ case NLM_NM_LOCK:
+ /* lockargs */
+ skip_netobj();
+ (void) getxdr_u_long(); /* Block */
+ (void) getxdr_u_long(); /* Excl */
+ (void) strcat(line, sum_lock());
+ break;
+ case NLM_FREE_ALL:
+ (void) sprintf(line,
+ " %s", sum_notify());
+ break;
+ }
+ check_retransmit(line, (ulong_t)xid);
+ } else {
+ (void) sprintf(line, "NLM R %s",
+ procnames_short_3[proc-20]);
+ line += strlen(line);
+ switch (proc) {
+ case NLM_SHARE:
+ case NLM_UNSHARE:
+ pl = sum_netobj("OH");
+ i = getxdr_u_long();
+ sprintf(line, "%s %s %ld",
+ pl, nameof_stat(i), getxdr_long());
+ break;
+ case NLM_NM_LOCK:
+ /* res */
+ (void) strcat(line, sum_netobj("OH"));
+ (void) strcat(line, " ");
+ (void) strcat(line,
+ nameof_stat(getxdr_u_long()));
+ break;
+ case NLM_FREE_ALL:
+ break;
+ }
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("NLM: ", "Network Lock Manager", len);
+ show_space();
+ if (setjmp(xdr_err)) {
+ return;
+ }
+ (void) sprintf(get_line(0, 0),
+ "Proc = %d (%s)",
+ proc, procnames_long_3[proc-20]);
+ if (type == CALL) {
+ switch (proc) {
+ case NLM_SHARE:
+ case NLM_UNSHARE:
+ show_shareargs();
+ break;
+ case NLM_NM_LOCK:
+ show_lockargs();
+ break;
+ case NLM_FREE_ALL:
+ show_notify();
+ break;
+ }
+ } else {
+ switch (proc) {
+ case NLM_SHARE:
+ case NLM_UNSHARE:
+ show_shareres();
+ break;
+ case NLM_NM_LOCK:
+ show_res();
+ break;
+ case NLM_FREE_ALL:
+ break;
+ }
+ }
+ show_trailer();
+ }
+}
+
+static char *
+nameof_mode(m)
+ uint_t m;
+{
+ switch ((enum fsh_mode) m) {
+ case fsm_DN: return ("deny none");
+ case fsm_DR: return ("deny read");
+ case fsm_DW: return ("deny write");
+ case fsm_DRW: return ("deny read/write");
+ default: return ("?");
+ }
+}
+
+static char *
+nameof_access(a)
+ uint_t a;
+{
+ switch ((enum fsh_access) a) {
+ case fsa_NONE: return ("?");
+ case fsa_R: return ("read only");
+ case fsa_W: return ("write only");
+ case fsa_RW: return ("read/write");
+ default: return ("?");
+ }
+}
+
+static void
+show_nlm_mode()
+{
+ enum fsh_mode m;
+
+ m = (enum fsh_mode) getxdr_u_long();
+ (void) sprintf(get_line(0, 0),
+ "Mode = %d (%s)",
+ m, nameof_mode((uint_t)m));
+}
+
+static void
+show_nlm_access()
+{
+ enum fsh_access a;
+
+ a = (enum fsh_access) getxdr_u_long();
+ (void) sprintf(get_line(0, 0),
+ "Access = %d (%s)",
+ a, nameof_access((uint_t)a));
+}
+
+static char *
+sum_share()
+{
+ static char buff[LM_MAXSTRLEN + 1];
+ char *cp = buff;
+ ulong_t mode, access;
+
+ (void) getxdr_string(buff, LM_MAXSTRLEN); /* Caller */
+ (void) strcpy(buff, sum_netobj("FH")); /* Fh */
+ cp += strlen(buff);
+ skip_netobj(); /* Owner */
+ mode = getxdr_u_long();
+ access = getxdr_u_long();
+ (void) sprintf(cp, " Mode=%lu Access=%lu", mode, access);
+ return (buff);
+}
+
+static void
+show_share()
+{
+ showxdr_string(LM_MAXSTRLEN, "Caller = %s");
+ show_netobj("Filehandle = %s");
+ show_netobj("Lock owner = %s");
+ show_nlm_mode();
+ show_nlm_access();
+}
+
+static void
+show_shareargs()
+{
+ show_netobj("Cookie = %s");
+ show_share();
+ showxdr_bool("Reclaim = %s");
+}
+
+static void
+show_shareres()
+{
+ show_netobj("Cookie = %s");
+ (void) show_stat();
+ showxdr_long("Sequence = %d");
+}
+
+static void
+show_notify()
+{
+ showxdr_string(LM_MAXNAMELEN, "Name = %s");
+ showxdr_long("State = %d");
+}
+
+#define NOTIFY_PAD (sizeof (" State=-2147483648") + 1)
+
+static char *
+sum_notify()
+{
+ static char buff[LM_MAXNAMELEN + NOTIFY_PAD];
+ char *cp = buff;
+ long state;
+
+ (void) getxdr_string(buff, LM_MAXNAMELEN);
+ cp += strlen(buff);
+ state = getxdr_long();
+ (void) sprintf(cp, " State=%ld", state);
+ return (buff);
+}
+
+/* ------------ V E R S I O N 4 ---------------------------------- */
+
+static char *procnames_short_4[] = {
+ "Null4", /* 0 */
+ "TEST4", /* 1 */
+ "LOCK4", /* 2 */
+ "CANCEL4", /* 3 */
+ "UNLOCK4", /* 4 */
+ "GRANTED4", /* 5 */
+ "TEST MSG4", /* 6 */
+ "LOCK MSG4", /* 7 */
+ "CANCEL MSG4", /* 8 */
+ "UNLOCK MSG4", /* 9 */
+ "GRANTED MSG4", /* 10 */
+ "TEST RES4", /* 11 */
+ "LOCK RES4", /* 12 */
+ "CANCEL RES4", /* 13 */
+ "UNLOCK RES4", /* 14 */
+ "GRANTED RES4", /* 15 */
+ "PROC 16 v4", /* 16 */
+ "PROC 17 v4", /* 17 */
+ "PROC 18 v4", /* 18 */
+ "PROC 19 v4", /* 19 */
+ "SHARE4", /* 20 */
+ "UNSHARE4", /* 21 */
+ "NM_LOCK4", /* 22 */
+ "FREEALL4", /* 23 */
+};
+
+static char *procnames_long_4[] = {
+ "Null procedure", /* 0 */
+ "Test", /* 1 */
+ "Lock", /* 2 */
+ "Cancel", /* 3 */
+ "Unlock", /* 4 */
+ "Granted", /* 5 */
+ "Test message", /* 6 */
+ "Lock message", /* 7 */
+ "Cancel message", /* 8 */
+ "Unlock message", /* 9 */
+ "Granted message", /* 10 */
+ "Test result", /* 11 */
+ "Lock result", /* 12 */
+ "Cancel result", /* 13 */
+ "Unlock result", /* 14 */
+ "Granted result", /* 15 */
+ "Procedure 16", /* 16 */
+ "Procedure 17", /* 17 */
+ "Procedure 18", /* 18 */
+ "Procedure 19", /* 19 */
+ "Share", /* 20 */
+ "Unshare", /* 21 */
+ "Unmonitored lock", /* 22 */
+ "Free all", /* 23 */
+};
+
+/* Maximum procedure number for version 4. */
+#define MAXPROC_4 23
+
+/* ARGSUSED */
+static void
+interpret_nlm_4(flags, type, xid, vers, proc, data, len)
+ int flags, type, xid, vers, proc;
+ char *data;
+ int len;
+{
+ char *line;
+ char *pl;
+ ulong_t i;
+
+ if (proc < 0 || proc > MAXPROC_4)
+ return;
+
+ if (flags & F_SUM) {
+ if (setjmp(xdr_err)) {
+ return;
+ }
+
+ line = get_sum_line();
+
+ if (type == CALL) {
+ (void) sprintf(line,
+ "NLM C %s",
+ procnames_short_4[proc]);
+ line += strlen(line);
+ switch (proc) {
+ case NLMPROC4_TEST:
+ case NLMPROC4_GRANTED:
+ case NLMPROC4_TEST_MSG:
+ case NLMPROC4_GRANTED_MSG:
+ /* testargs */
+ (void) strcat(line, sum_netobj("OH"));
+ (void) getxdr_bool(); /* Excl */
+ (void) strcat(line, sum_lock4());
+ break;
+ case NLMPROC4_LOCK:
+ case NLMPROC4_LOCK_MSG:
+ /* lockargs */
+ (void) strcat(line, sum_netobj("OH"));
+ (void) getxdr_bool(); /* Block */
+ (void) getxdr_bool(); /* Excl */
+ (void) strcat(line, sum_lock4());
+ /* ignore reclaim, state fields */
+ break;
+ case NLMPROC4_CANCEL:
+ case NLMPROC4_CANCEL_MSG:
+ /* cancargs */
+ (void) strcat(line, sum_netobj("OH"));
+ (void) getxdr_bool(); /* Block */
+ (void) getxdr_bool(); /* Excl */
+ (void) strcat(line, sum_lock4());
+ break;
+ case NLMPROC4_UNLOCK:
+ case NLMPROC4_UNLOCK_MSG:
+ /* unlockargs */
+ (void) strcat(line, sum_netobj("OH"));
+ (void) strcat(line, sum_lock4());
+ break;
+ case NLMPROC4_TEST_RES:
+ /* testres */
+ (void) strcat(line, sum_netobj("OH"));
+ (void) strcat(line, " ");
+ (void) strcat(line,
+ nameof_stat4(getxdr_u_long()));
+ break;
+ case NLMPROC4_LOCK_RES:
+ case NLMPROC4_CANCEL_RES:
+ case NLMPROC4_UNLOCK_RES:
+ case NLMPROC4_GRANTED_RES:
+ /* res */
+ (void) strcat(line, sum_netobj("OH"));
+ (void) strcat(line, " ");
+ (void) strcat(line,
+ nameof_stat4(getxdr_u_long()));
+ break;
+ case NLMPROC4_SHARE:
+ case NLMPROC4_UNSHARE:
+ (void) strcat(line, sum_netobj("OH"));
+ (void) strcat(line, sum_share());
+ break;
+ case NLMPROC4_NM_LOCK:
+ /* lockargs */
+ skip_netobj(); /* Cookie */
+ (void) getxdr_bool(); /* Block */
+ (void) getxdr_bool(); /* Excl */
+ (void) strcat(line, sum_lock4());
+ /* skip reclaim & state fields */
+ break;
+ case NLMPROC4_FREE_ALL:
+ (void) sprintf(line,
+ " %s", sum_notify());
+ break;
+ }
+ check_retransmit(line, (ulong_t)xid);
+ } else {
+ (void) sprintf(line, "NLM R %s",
+ procnames_short_4[proc]);
+ line += strlen(line);
+ switch (proc) {
+ case NLMPROC4_TEST:
+ /* testres */
+ (void) strcat(line, sum_netobj("OH"));
+ (void) strcat(line, " ");
+ (void) strcat(line,
+ nameof_stat4(getxdr_u_long()));
+ break;
+ case NLMPROC4_LOCK:
+ case NLMPROC4_CANCEL:
+ case NLMPROC4_UNLOCK:
+ case NLMPROC4_GRANTED:
+ case NLMPROC4_NM_LOCK:
+ /* res */
+ (void) strcat(line, sum_netobj("OH"));
+ (void) strcat(line, " ");
+ (void) strcat(line,
+ nameof_stat4(getxdr_u_long()));
+ break;
+ case NLMPROC4_SHARE:
+ case NLMPROC4_UNSHARE:
+ /* shareres */
+ pl = sum_netobj("OH");
+ i = getxdr_u_long();
+ sprintf(line, "%s %s %ld",
+ pl, nameof_stat4(i), getxdr_long());
+ break;
+ case NLMPROC4_FREE_ALL:
+ break;
+ }
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("NLM: ", "Network Lock Manager", len);
+ show_space();
+ if (setjmp(xdr_err)) {
+ return;
+ }
+ (void) sprintf(get_line(0, 0),
+ "Proc = %d (%s)",
+ proc, procnames_long_4[proc]);
+ if (type == CALL) {
+ switch (proc) {
+ case NLMPROC4_TEST:
+ case NLMPROC4_GRANTED:
+ case NLMPROC4_TEST_MSG:
+ case NLMPROC4_GRANTED_MSG:
+ show_testargs4();
+ break;
+ case NLMPROC4_LOCK:
+ case NLMPROC4_LOCK_MSG:
+ case NLMPROC4_NM_LOCK:
+ show_lockargs4();
+ break;
+ case NLMPROC4_CANCEL:
+ case NLMPROC4_CANCEL_MSG:
+ show_cancargs4();
+ break;
+ case NLMPROC4_UNLOCK:
+ case NLMPROC4_UNLOCK_MSG:
+ show_unlockargs4();
+ break;
+ case NLMPROC4_TEST_RES:
+ show_testres4();
+ break;
+ case NLMPROC4_LOCK_RES:
+ case NLMPROC4_CANCEL_RES:
+ case NLMPROC4_UNLOCK_RES:
+ case NLMPROC4_GRANTED_RES:
+ show_res4();
+ break;
+ case NLMPROC4_SHARE:
+ case NLMPROC4_UNSHARE:
+ show_shareargs();
+ break;
+ case NLMPROC4_FREE_ALL:
+ show_notify();
+ break;
+ }
+ } else {
+ switch (proc) {
+ case NLMPROC4_TEST:
+ show_testres4();
+ break;
+ case NLMPROC4_LOCK:
+ case NLMPROC4_CANCEL:
+ case NLMPROC4_UNLOCK:
+ case NLMPROC4_GRANTED:
+ case NLM_NM_LOCK:
+ show_res4();
+ break;
+ case NLMPROC4_TEST_MSG:
+ case NLMPROC4_LOCK_MSG:
+ case NLMPROC4_CANCEL_MSG:
+ case NLMPROC4_UNLOCK_MSG:
+ case NLMPROC4_GRANTED_MSG:
+ case NLMPROC4_TEST_RES:
+ case NLMPROC4_LOCK_RES:
+ case NLMPROC4_CANCEL_RES:
+ case NLMPROC4_UNLOCK_RES:
+ case NLMPROC4_GRANTED_RES:
+ break;
+ case NLM_SHARE:
+ case NLM_UNSHARE:
+ show_shareres4();
+ break;
+ case NLM_FREE_ALL:
+ break;
+ }
+ }
+ show_trailer();
+ }
+}
+
+static char *
+sum_lock4()
+{
+ static char buff[LM_MAXSTRLEN + 1];
+ char *cp = buff;
+ long id;
+ u_longlong_t off, len;
+
+ (void) getxdr_string(buff, LM_MAXSTRLEN); /* Caller */
+ (void) strcpy(buff, sum_netobj("FH")); /* Fh */
+ cp += strlen(buff);
+ skip_netobj(); /* Owner */
+ id = getxdr_long();
+ off = getxdr_u_longlong();
+ len = getxdr_u_longlong();
+ (void) sprintf(cp, " PID=%ld Region=%llu:%llu", id, off, len);
+ return (buff);
+}
+
+static void
+show_lock4()
+{
+ showxdr_string(LM_MAXSTRLEN, "Caller = %s");
+ show_netobj("Filehandle = %s");
+ show_netobj("Lock owner = %s");
+ showxdr_long("Svid = %ld (process id)");
+ showxdr_u_longlong("Offset = %llu bytes");
+ showxdr_u_longlong("Length = %llu bytes");
+}
+
+static void
+show_cancargs4()
+{
+ show_netobj("Cookie = %s");
+ showxdr_bool("Block = %s");
+ showxdr_bool("Exclusive = %s");
+ show_lock4();
+}
+
+static void
+show_lockargs4()
+{
+ show_netobj("Cookie = %s");
+ showxdr_bool("Block = %s");
+ showxdr_bool("Exclusive = %s");
+ show_lock4();
+ showxdr_bool("Reclaim = %s");
+ showxdr_long("State = %ld");
+}
+
+static void
+show_unlockargs4()
+{
+ show_netobj("Cookie = %s");
+ show_lock4();
+}
+
+static void
+show_testargs4()
+{
+ show_netobj("Cookie = %s");
+ showxdr_bool("Exclusive = %s");
+ show_lock4();
+}
+
+static void
+show_res4()
+{
+ show_netobj("Cookie = %s");
+ (void) show_stat4();
+}
+
+static char *
+nameof_stat4(s)
+ ulong_t s;
+{
+ switch ((enum nlm4_stats) s) {
+ case NLM4_GRANTED: return ("granted");
+ case NLM4_DENIED: return ("denied");
+ case NLM4_DENIED_NOLOCKS:return ("denied (no locks)");
+ case NLM4_BLOCKED: return ("blocked");
+ case NLM4_DENIED_GRACE_PERIOD: return ("denied (grace period)");
+ case NLM4_DEADLCK: return ("deadlock");
+ case NLM4_ROFS: return ("read-only fs");
+ case NLM4_STALE_FH: return ("stale fh");
+ case NLM4_FBIG: return ("file too big");
+ case NLM4_FAILED: return ("failed");
+ default: return ("?");
+ }
+}
+
+static enum nlm4_stats
+show_stat4()
+{
+ enum nlm4_stats s;
+
+ s = (enum nlm4_stats) getxdr_u_long();
+ (void) sprintf(get_line(0, 0),
+ "Status = %d (%s)",
+ s, nameof_stat4((ulong_t)s));
+
+ return (s);
+}
+
+static void
+show_testres4()
+{
+ show_netobj("Cookie = %s");
+ if (show_stat() == NLM4_DENIED) {
+ showxdr_bool("Exclusive = %s");
+ showxdr_long("Svid = %ld (process id)");
+ show_netobj("Owner handle = %s");
+ showxdr_u_longlong("Offset = %llu bytes");
+ showxdr_u_longlong("Length = %llu bytes");
+ }
+}
+
+static void
+show_shareres4()
+{
+ show_netobj("Cookie = %s");
+ (void) show_stat4();
+ showxdr_long("Sequence = %d");
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ntp.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ntp.c
new file mode 100644
index 0000000000..6e2fcec376
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ntp.c
@@ -0,0 +1,516 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1991, 2000, 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/sysmacros.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <string.h>
+#include <tzfile.h>
+#include "snoop.h"
+#include "ntp.h"
+
+/*
+ * In verbose mode, how many octets of the control-mode data payload
+ * are displayed per line of output. The value 64 fits well on an
+ * 80-column screen and, as a power of 2, is easily correlated to
+ * hexadecimal output.
+ */
+#define OCTETS_PER_LINE 64
+
+extern char *dlc_header;
+
+static char *show_leap(int);
+static char *show_mode(int);
+static char *show_ref(int, ulong_t);
+static char *show_time(struct l_fixedpt);
+static double s_fixed_to_double(struct s_fixedpt *);
+static char *iso_date_time(time_t);
+static char *show_operation(int);
+
+int
+interpret_ntp(int flags, struct ntpdata *ntp_pkt, int fraglen)
+{
+ unsigned int i, j, macbytes;
+ unsigned int proto_version;
+ unsigned int datalen;
+ unsigned int linelen = OCTETS_PER_LINE;
+ unsigned int sofar = 0;
+
+ char *datap;
+ char hbuf[2 * MAC_OCTETS_MAX + 1];
+ static char *hexstr = "0123456789ABCDEF";
+
+ union ntp_pkt_buf {
+ struct ntpdata ntp_msg;
+ union ntpc_buf {
+ struct ntp_control chdr;
+ uchar_t data2[NTPC_DATA_MAXLEN - 1];
+ } ntpc_msg;
+ union ntpp_buf {
+ struct ntp_private phdr;
+ uchar_t data2[1];
+ } ntpp_msg;
+ } fragbuf;
+
+ struct ntpdata *ntp = &fragbuf.ntp_msg;
+ struct ntp_control *ntpc = (struct ntp_control *)&fragbuf.ntpc_msg;
+ struct ntp_private *ntpp = (struct ntp_private *)&fragbuf.ntpp_msg;
+
+ /*
+ * Copying packet contents into a local buffer avoids
+ * problems of interpretation if the packet is truncated.
+ */
+ (void) memcpy(&fragbuf, ntp_pkt, MIN(sizeof (fragbuf), fraglen));
+
+ if (flags & F_SUM) {
+ switch (ntp->li_vn_mode & NTPMODEMASK) {
+ case MODE_SYM_ACT:
+ case MODE_SYM_PAS:
+ case MODE_CLIENT:
+ case MODE_SERVER:
+ case MODE_BROADCAST:
+ (void) sprintf(get_sum_line(),
+ "NTP %s [st=%hd] (%s)",
+ show_mode(ntp->li_vn_mode & NTPMODEMASK),
+ ntp->stratum,
+ show_time(ntp->xmt));
+ break;
+ case MODE_CONTROL:
+ (void) sprintf(get_sum_line(),
+ "NTP %s "
+ "(Flags/op=0x%02x Seq=%hu Status=0x%04hx Assoc=%hu)",
+ show_mode(ntpc->li_vn_mode & NTPMODEMASK),
+ ntpc->r_m_e_op,
+ ntohs(ntpc->sequence),
+ ntohs(ntpc->status),
+ ntohs(ntpc->associd));
+ break;
+ default:
+ (void) sprintf(get_sum_line(),
+ "NTP %s",
+ show_mode(ntpp->rm_vn_mode & NTPMODEMASK));
+ break;
+ }
+ }
+
+ proto_version = (ntp->li_vn_mode & VERSIONMASK) >> 3;
+
+ if (flags & F_DTAIL) {
+ show_header("NTP: ", "Network Time Protocol", fraglen);
+ show_space();
+ switch (ntp->li_vn_mode & NTPMODEMASK) {
+ case MODE_SYM_ACT:
+ case MODE_SYM_PAS:
+ case MODE_CLIENT:
+ case MODE_SERVER:
+ case MODE_BROADCAST:
+ (void) sprintf(get_line((char *)ntp->li_vn_mode -
+ dlc_header, 1),
+ "Leap = 0x%x (%s)",
+ (int)(ntp->li_vn_mode & LEAPMASK) >> 6,
+ show_leap(ntp->li_vn_mode & LEAPMASK));
+ (void) sprintf(get_line((char *)ntp->li_vn_mode -
+ dlc_header, 1),
+ "Version = %lu", proto_version);
+ (void) sprintf(get_line((char *)ntp->li_vn_mode -
+ dlc_header, 1),
+ "Mode = %hu (%s)",
+ ntp->li_vn_mode & NTPMODEMASK,
+ show_mode(ntp->li_vn_mode & NTPMODEMASK));
+ (void) sprintf(get_line((char *)ntp->stratum -
+ dlc_header, 1),
+ "Stratum = %d (%s)",
+ ntp->stratum,
+ ntp->stratum == 0 ? "unspecified" :
+ ntp->stratum == 1 ? "primary reference" :
+ "secondary reference");
+ (void) sprintf(get_line((char *)ntp->ppoll - dlc_header, 1),
+ "Poll = %hu",
+ ntp->ppoll);
+ (void) sprintf(get_line((char *)ntp->precision -
+ dlc_header, 1),
+ "Precision = %d seconds",
+ ntp->precision);
+ (void) sprintf(get_line((char *)ntp->distance.int_part -
+ dlc_header, 1),
+ "Synchronizing distance = 0x%04x.%04x (%f)",
+ ntohs(ntp->distance.int_part),
+ ntohs(ntp->distance.fraction),
+ s_fixed_to_double(&ntp->distance));
+ (void) sprintf(get_line((char *)ntp->dispersion.int_part -
+ dlc_header, 1),
+ "Synchronizing dispersion = 0x%04x.%04x (%f)",
+ ntohs(ntp->dispersion.int_part),
+ ntohs(ntp->dispersion.fraction),
+ s_fixed_to_double(&ntp->dispersion));
+ (void) sprintf(get_line((char *)ntp->refid - dlc_header, 1),
+ "Reference clock = %s",
+ show_ref(ntp->stratum, ntp->refid));
+
+ (void) sprintf(get_line((char *)ntp->reftime.int_part -
+ dlc_header, 1),
+ "Reference time = 0x%08lx.%08lx (%s)",
+ ntohl(ntp->reftime.int_part),
+ ntohl(ntp->reftime.fraction),
+ show_time(ntp->reftime));
+
+ (void) sprintf(get_line((char *)ntp->org.int_part -
+ dlc_header, 1),
+ "Originate time = 0x%08lx.%08lx (%s)",
+ ntohl(ntp->org.int_part),
+ ntohl(ntp->org.fraction),
+ show_time(ntp->org));
+
+ (void) sprintf(get_line((char *)ntp->rec.int_part -
+ dlc_header, 1),
+ "Receive time = 0x%08lx.%08lx (%s)",
+ ntohl(ntp->rec.int_part),
+ ntohl(ntp->rec.fraction),
+ show_time(ntp->rec));
+
+ (void) sprintf(get_line((char *)ntp->xmt.int_part -
+ dlc_header, 1),
+ "Transmit time = 0x%08lx.%08lx (%s)",
+ ntohl(ntp->xmt.int_part),
+ ntohl(ntp->xmt.fraction),
+ show_time(ntp->xmt));
+
+ if (proto_version > 3 ||
+ fraglen < (LEN_PKT_NOMAC + MAC_OCTETS_MIN)) {
+ /*
+ * A newer protocol version we can't parse,
+ * or v3 packet with no valid authentication.
+ */
+ break;
+ }
+ (void) sprintf(get_line((char *)ntp->keyid -
+ dlc_header, 1),
+ "Key ID = %8lu", ntohl(ntp->keyid));
+
+ macbytes = fraglen - (LEN_PKT_NOMAC + sizeof (uint32_t));
+
+ for (i = 0, j = 0; i < macbytes; i++) {
+ hbuf[j++] = hexstr[ntp->mac[i] >> 4 & 0x0f];
+ hbuf[j++] = hexstr[ntp->mac[i] & 0x0f];
+ }
+ hbuf[j] = '\0';
+ (void) sprintf(get_line((char *)ntp->mac -
+ dlc_header, 1),
+ "Authentication code = %s", hbuf);
+ break;
+
+ case MODE_CONTROL:
+ /* NTP Control Message, mode 6 */
+
+ (void) sprintf(get_line((char *)ntp->li_vn_mode -
+ dlc_header, 1),
+ "Leap = 0x%x (%s)",
+ (int)(ntp->li_vn_mode & LEAPMASK) >> 6,
+ show_leap(ntp->li_vn_mode & LEAPMASK));
+ (void) sprintf(get_line((char *)ntp->li_vn_mode -
+ dlc_header, 1),
+ "Version = %lu", proto_version);
+ (void) sprintf(get_line((char *)ntp->li_vn_mode -
+ dlc_header, 1),
+ "Mode = %hu (%s)",
+ ntp->li_vn_mode & NTPMODEMASK,
+ show_mode(ntp->li_vn_mode & NTPMODEMASK));
+ (void) sprintf(get_line((char *)ntpc->r_m_e_op -
+ dlc_header, 1),
+ "Flags and operation code = 0x%02x",
+ ntpc->r_m_e_op);
+ (void) sprintf(get_line((char *)ntpc->r_m_e_op -
+ dlc_header, 1),
+ " %s",
+ getflag(ntpc->r_m_e_op, CTL_RESPONSE, "response",
+ "request"));
+ (void) sprintf(get_line((char *)ntpc->r_m_e_op -
+ dlc_header, 1),
+ " %s",
+ getflag(ntpc->r_m_e_op, CTL_ERROR, "error",
+ "success"));
+ (void) sprintf(get_line((char *)ntpc->r_m_e_op -
+ dlc_header, 1),
+ " %s",
+ getflag(ntpc->r_m_e_op, CTL_MORE, "more",
+ "no more"));
+ (void) sprintf(get_line((char *)ntpc->r_m_e_op -
+ dlc_header, 1),
+ " ...x xxxx = %hd (%s)",
+ ntpc->r_m_e_op & CTL_OP_MASK,
+ show_operation(ntpc->r_m_e_op & CTL_OP_MASK));
+ (void) sprintf(get_line((char *)ntpc->sequence -
+ dlc_header, 1),
+ "Sequence = %hu",
+ ntohs(ntpc->sequence));
+ (void) sprintf(get_line((char *)ntpc->status -
+ dlc_header, 1),
+ "Status = 0x%04hx",
+ ntohs(ntpc->status));
+ (void) sprintf(get_line((char *)ntpc->associd -
+ dlc_header, 1),
+ "Assoc ID = %hu",
+ ntohs(ntpc->associd));
+ (void) sprintf(get_line((char *)ntpc->offset -
+ dlc_header, 1),
+ "Data offset = %hu",
+ ntohs(ntpc->offset));
+ (void) sprintf(get_line((char *)ntpc->count -
+ dlc_header, 1),
+ "Data bytes = %hu",
+ ntohs(ntpc->count));
+ datalen = ntohs(ntpc->count);
+ if (datalen == 0) {
+ break;
+ } else if (datalen > NTPC_DATA_MAXLEN) {
+ datalen = NTPC_DATA_MAXLEN;
+ }
+ show_space();
+ datap = (char *)ntpc->data;
+ do {
+ (void) sprintf(get_line(datap -
+ dlc_header, 1),
+ "\"%s\"",
+ show_string(datap, linelen, datalen));
+ sofar += linelen;
+ datap += linelen;
+ if ((sofar + linelen) > datalen) {
+ linelen = datalen - sofar;
+ }
+ } while (sofar < datalen);
+ show_trailer();
+ break;
+
+ case MODE_PRIVATE:
+ /* NTP Private Message, mode 7 */
+
+ (void) sprintf(get_line((char *)ntpp->rm_vn_mode -
+ dlc_header, 1),
+ "Version = %hu",
+ INFO_VERSION(ntpp->rm_vn_mode));
+ (void) sprintf(get_line((char *)ntpp->rm_vn_mode -
+ dlc_header, 1),
+ "Mode = %hu (%s)",
+ INFO_MODE(ntpp->rm_vn_mode),
+ show_mode(INFO_MODE(ntpp->rm_vn_mode)));
+ (void) sprintf(get_line((char *)ntpp->rm_vn_mode -
+ dlc_header, 1),
+ "Flags = 0x%02hx",
+ ntpp->rm_vn_mode);
+ (void) sprintf(get_line((char *)ntpp->rm_vn_mode -
+ dlc_header, 1),
+ " %s",
+ getflag(ntpp->rm_vn_mode, RESP_BIT, "response",
+ "request"));
+ (void) sprintf(get_line((char *)ntpp->rm_vn_mode -
+ dlc_header, 1),
+ " %s",
+ getflag(ntpp->rm_vn_mode, MORE_BIT, "more",
+ "no more"));
+ (void) sprintf(get_line((char *)ntpp->auth_seq -
+ dlc_header, 1),
+ "Authentication and sequence = 0x%02x",
+ ntpp->auth_seq);
+ (void) sprintf(get_line((char *)ntpp->auth_seq -
+ dlc_header, 1),
+ " %s",
+ getflag(ntpp->auth_seq, AUTH_BIT, "authenticated",
+ "unauthenticated"));
+ (void) sprintf(get_line((char *)ntpp->auth_seq -
+ dlc_header, 1),
+ " .xxx xxxx = %hu (sequence number)",
+ INFO_SEQ(ntpp->auth_seq));
+ (void) sprintf(get_line((char *)ntpp->implementation -
+ dlc_header, 1),
+ "Implementation = %hu",
+ ntpp->implementation);
+ (void) sprintf(get_line((char *)ntpp->request -
+ dlc_header, 1),
+ "Request = %hu",
+ ntpp->request);
+ (void) sprintf(get_line((char *)ntpp->err_nitems -
+ dlc_header, 1),
+ "Error = %hu",
+ INFO_ERR(ntpp->err_nitems));
+ (void) sprintf(get_line((char *)ntpp->err_nitems -
+ dlc_header, 1),
+ "Items = %hu",
+ INFO_NITEMS(ntpp->err_nitems));
+ (void) sprintf(get_line((char *)ntpp->mbz_itemsize -
+ dlc_header, 1),
+ "Item size = %hu",
+ INFO_ITEMSIZE(ntpp->mbz_itemsize));
+ break;
+
+ default:
+ /* Unknown mode */
+ (void) sprintf(get_line((char *)ntp->li_vn_mode -
+ dlc_header, 1),
+ "Mode = %hu (%s)",
+ ntp->li_vn_mode & NTPMODEMASK,
+ show_mode(ntp->li_vn_mode & NTPMODEMASK));
+ break;
+ }
+ }
+
+ return (fraglen);
+}
+
+char *
+show_leap(int leap)
+{
+ switch (leap) {
+ case NO_WARNING: return ("OK");
+ case PLUS_SEC: return ("add a second (61 seconds)");
+ case MINUS_SEC: return ("minus a second (59 seconds)");
+ case ALARM: return ("alarm condition (clock unsynchronized)");
+ default: return ("unknown");
+ }
+}
+
+char *
+show_mode(int mode)
+{
+ switch (mode) {
+ case MODE_UNSPEC: return ("unspecified");
+ case MODE_SYM_ACT: return ("symmetric active");
+ case MODE_SYM_PAS: return ("symmetric passive");
+ case MODE_CLIENT: return ("client");
+ case MODE_SERVER: return ("server");
+ case MODE_BROADCAST: return ("broadcast");
+ case MODE_CONTROL: return ("control");
+ case MODE_PRIVATE: return ("private");
+ default: return ("unknown");
+ }
+}
+
+char *
+show_ref(int mode, ulong_t refid)
+{
+ static char buff[MAXHOSTNAMELEN + 32];
+ struct in_addr host;
+ extern char *inet_ntoa();
+
+ switch (mode) {
+ case 0:
+ case 1:
+ (void) strncpy(buff, (char *)&refid, 4);
+ buff[4] = '\0';
+ break;
+
+ default:
+ host.s_addr = refid;
+ (void) sprintf(buff, "%s (%s)",
+ inet_ntoa(host),
+ addrtoname(AF_INET, &host));
+ break;
+ }
+
+ return (buff);
+}
+
+/*
+ * Here we have to worry about the high order bit being signed
+ */
+double
+s_fixed_to_double(struct s_fixedpt *t)
+{
+ double a;
+
+ if (ntohs(t->int_part) & 0x8000) {
+ a = ntohs((int)(~t->fraction) & 0xFFFF);
+ a = a / 65536.0; /* shift dec point over by 16 bits */
+ a += ntohs((int)(~t->int_part) & 0xFFFF);
+ a = -a;
+ } else {
+ a = ntohs(t->fraction);
+ a = a / 65536.0; /* shift dec point over by 16 bits */
+ a += ntohs(t->int_part);
+ }
+ return (a);
+}
+
+/*
+ * Consistent with RFC-3339, ISO 8601.
+ */
+char *
+iso_date_time(time_t input_time)
+{
+ struct tm *time_parts;
+ static char tbuf[sizeof ("yyyy-mm-dd hh:mm:ss")];
+
+ time_parts = localtime(&input_time);
+ (void) strftime(tbuf, sizeof (tbuf), "%Y-%m-%d %H:%M:%S", time_parts);
+ return (tbuf);
+}
+
+/*
+ * The base of NTP timestamps is 1900-01-01 00:00:00.00000
+ */
+char *
+show_time(struct l_fixedpt pkt_time)
+{
+ struct l_fixedpt net_time;
+ unsigned long fracsec;
+ static char buff[32];
+
+ if (pkt_time.int_part == 0) {
+ buff[0] = '\0';
+ return (buff);
+ }
+
+ net_time.int_part = ntohl(pkt_time.int_part) - JAN_1970;
+ net_time.fraction = ntohl(pkt_time.fraction);
+
+ fracsec = net_time.fraction / 42949; /* fract / (2**32/10**6) */
+
+ (void) strlcpy(buff, iso_date_time(net_time.int_part), sizeof (buff));
+ (void) snprintf(buff, sizeof (buff), "%s.%05lu", buff, fracsec);
+
+ return (buff);
+}
+
+char *
+show_operation(int op)
+{
+ switch (op) {
+ case CTL_OP_UNSPEC: return ("unspecified");
+ case CTL_OP_READSTAT: return ("read stats");
+ case CTL_OP_READVAR: return ("read var");
+ case CTL_OP_WRITEVAR: return ("write var");
+ case CTL_OP_READCLOCK: return ("read clock");
+ case CTL_OP_WRITECLOCK: return ("write clock");
+ case CTL_OP_SETTRAP: return ("set trap");
+ case CTL_OP_ASYNCMSG: return ("async msg");
+ case CTL_OP_UNSETTRAP: return ("unset trap");
+ default: return ("unknown");
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ospf.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ospf.c
new file mode 100644
index 0000000000..84234dde3c
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ospf.c
@@ -0,0 +1,772 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/if_ether.h>
+#include <arpa/inet.h>
+#include "snoop.h"
+#include "snoop_ospf.h"
+#include "snoop_ospf6.h"
+
+extern char *dlc_header;
+static char *sum_line;
+
+char *ospf_types[] = {
+ "umd", /* 0 */
+ "Hello", /* 1 */
+ "DD", /* 2 */
+ "LSReq", /* 3 */
+ "LSUpd", /* 4 */
+ "LSAck", /* 5 */
+};
+
+static char *ospf_authtypes[] = {
+ "None", /* 0 */
+ "simple", /* 1 */
+ "md5", /* 2 */
+};
+
+const struct bits ospf_rla_flag_bits[] = {
+ { RLA_FLAG_B, "B" },
+ { RLA_FLAG_E, "E" },
+ { RLA_FLAG_V, "V" },
+ { RLA_FLAG_W, "W" },
+ { 0, NULL }
+};
+
+const struct bits ospf_db_flags_bits[] = {
+ { OSPF_DB_INIT, "I" },
+ { OSPF_DB_MORE, "M" },
+ { OSPF_DB_MASTER, "MS" },
+ { 0, NULL }
+};
+
+const struct bits ospf_option_bits[] = {
+ { OSPF_OPTION_T, "T" },
+ { OSPF_OPTION_E, "E" },
+ { OSPF_OPTION_MC, "MC" },
+ { 0, NULL }
+};
+
+static int interpret_ospf_hello(int, struct ospfhdr *, int);
+static void ospf_print_ls_type(int, uint32_t, struct in_addr, struct in_addr);
+static void interpret_ospf_lsa_hdr(int, struct lsa_hdr *);
+static int interpret_ospf_lsa(int flags, struct lsa *lsa, uchar_t *);
+
+char *
+ospf_print_bits(const struct bits *bp, uchar_t options)
+{
+ static char bitstring[32];
+
+ bitstring[0] = '\0';
+ do {
+ if (options & bp->bit) {
+ strcat(bitstring, bp->str);
+ strcat(bitstring, "/");
+ }
+ } while ((++bp)->bit);
+
+ /* wipe out the trailing "/" */
+ bitstring[strlen(bitstring) - 1] = '\0';
+ return (bitstring);
+}
+
+char *
+ospf_print_lsa_age(long age)
+{
+ long sec, mins, hour;
+ static char lsa_age[16];
+
+ sec = age % 60;
+ mins = (age / 60) % 60;
+ hour = age / 3600;
+ if (hour != 0)
+ snprintf(lsa_age, sizeof (lsa_age), "%u:%02u:%02u",
+ hour, mins, sec);
+ else if (mins != 0)
+ snprintf(lsa_age, sizeof (lsa_age), "%u:%02u", mins, sec);
+ else
+ snprintf(lsa_age, sizeof (lsa_age), "%u", sec);
+ return (lsa_age);
+}
+
+static int
+interpret_ospf_hello(int flags, struct ospfhdr *op, int fraglen)
+{
+ struct in_addr *nbr;
+ int j;
+
+ if (fraglen < OSPF_MIN_HEADER_SIZE + OSPF_MIN_HELLO_HEADER_SIZE)
+ return (-1); /* truncated packet */
+
+ if (flags & F_SUM) {
+ if (op->ospf_hello.hello_dr.s_addr != 0) {
+ (void) sprintf(sum_line, "DR=%s ",
+ inet_ntoa(op->ospf_hello.hello_dr));
+ }
+ sum_line += strlen(sum_line);
+ if (op->ospf_hello.hello_bdr.s_addr != 0) {
+ (void) sprintf(sum_line, "BDR=%s ",
+ inet_ntoa(op->ospf_hello.hello_bdr));
+ }
+ sum_line += strlen(sum_line);
+ nbr = op->ospf_hello.hello_neighbor;
+ j = 0;
+ while ((uchar_t *)nbr < ((uchar_t *)op + fraglen)) {
+ if ((uchar_t *)nbr + sizeof (struct in_addr) >
+ ((uchar_t *)op + fraglen))
+ return (-1); /* truncated */
+ j++;
+ ++nbr;
+ }
+ (void) sprintf(sum_line, "%d nbrs", j);
+ sum_line += strlen(sum_line);
+
+ }
+ if (flags & F_DTAIL) {
+ show_header("OSPF HELLO: ", "Hello Packet",
+ ntohs(op->ospf_len));
+ show_space();
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Options = %s", ospf_print_bits(ospf_option_bits,
+ op->ospf_hello.hello_options));
+ (void) snprintf(get_line(0, 0), get_line_remain(), "Mask = %s",
+ inet_ntoa(op->ospf_hello.hello_mask));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Hello interval = %d",
+ ntohs(op->ospf_hello.hello_helloint));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Priority = %d", op->ospf_hello.hello_priority);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Dead interval = %u", ntohl(op->ospf_hello.hello_deadint));
+ if (op->ospf_hello.hello_dr.s_addr != 0) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Designated Router = %s",
+ inet_ntoa(op->ospf_hello.hello_dr));
+ }
+ if (op->ospf_hello.hello_bdr.s_addr != 0) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Backup Designated Router = %s",
+ inet_ntoa(op->ospf_hello.hello_bdr));
+ }
+ nbr = op->ospf_hello.hello_neighbor;
+ while ((uchar_t *)nbr < ((uchar_t *)op + fraglen)) {
+ if ((uchar_t *)nbr + sizeof (struct in_addr) >
+ ((uchar_t *)op + fraglen))
+ return (-1); /* truncated */
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Neighbor: %s", inet_ntoa(*nbr));
+ ++nbr;
+ }
+ }
+ return (fraglen);
+}
+
+static void
+ospf_print_ls_type(int flags, uint32_t ls_type, struct in_addr ls_stateid,
+ struct in_addr ls_router)
+{
+ switch (ls_type) {
+ case LS_TYPE_ROUTER:
+ if (flags & F_SUM) {
+ sprintf(sum_line, " rtr %s ", inet_ntoa(ls_router));
+ sum_line += strlen(sum_line);
+ }
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Router LSA; Router = %s ", inet_ntoa(ls_router));
+ }
+ break;
+ case LS_TYPE_NETWORK:
+ if (flags & F_SUM) {
+ sprintf(sum_line, " net dr %s ", inet_ntoa(ls_router));
+ sum_line += strlen(sum_line);
+ sprintf(sum_line, "if %s ", inet_ntoa(ls_stateid));
+ sum_line += strlen(sum_line);
+ }
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Network LSA Router = %s ", inet_ntoa(ls_router));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " Interface = %s ",
+ inet_ntoa(ls_stateid));
+ }
+ break;
+ case LS_TYPE_SUM_IP:
+ if (flags & F_SUM) {
+ sprintf(sum_line, " sum %s ", inet_ntoa(ls_stateid));
+ sum_line += strlen(sum_line);
+ sprintf(sum_line, "abr %s ", inet_ntoa(ls_router));
+ sum_line += strlen(sum_line);
+ }
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Summary LSA IP = %s ", inet_ntoa(ls_stateid));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " Area Border Router = %s ",
+ inet_ntoa(ls_router));
+ }
+ break;
+ case LS_TYPE_SUM_ABR:
+ if (flags & F_SUM) {
+ sprintf(sum_line, "abr %s ", inet_ntoa(ls_stateid));
+ sum_line += strlen(sum_line);
+ sprintf(sum_line, "asbr %s ", inet_ntoa(ls_router));
+ sum_line += strlen(sum_line);
+ }
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "ASBR Summary abr = %s ", inet_ntoa(ls_stateid));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " asbr = %s ", inet_ntoa(ls_router));
+ }
+ break;
+ case LS_TYPE_ASE:
+ if (flags & F_SUM) {
+ sprintf(sum_line, " ase %s", inet_ntoa(ls_stateid));
+ sum_line += strlen(sum_line);
+ sprintf(sum_line, " asbr %s", inet_ntoa(ls_router));
+ sum_line += strlen(sum_line);
+ }
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "AS External LSA ase = %s ", inet_ntoa(ls_stateid));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " asbr = %s ", inet_ntoa(ls_router));
+ }
+
+ break;
+ case LS_TYPE_GROUP:
+ if (flags & F_SUM) {
+ sprintf(sum_line, " group %s", inet_ntoa(ls_stateid));
+ sum_line += strlen(sum_line);
+ sprintf(sum_line, " rtr %s", inet_ntoa(ls_router));
+ sum_line += strlen(sum_line);
+ }
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Group LSA %s ", inet_ntoa(ls_stateid));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " rtr = %s ", inet_ntoa(ls_router));
+ }
+ break;
+ default:
+ if (flags & F_SUM) {
+ sprintf(sum_line, " unknown LSA type %d", ls_type);
+ sum_line += strlen(sum_line);
+ }
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Unknown LSA type %d", ls_type);
+ }
+ break;
+ }
+}
+
+static void
+interpret_ospf_lsa_hdr(int flags, struct lsa_hdr *lsah)
+{
+ if (flags & F_SUM)
+ return;
+
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Options = %s",
+ ospf_print_bits(ospf_option_bits, lsah->ls_options));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Sequence = %X ", ntohl(lsah->ls_seq));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Age = %X ", ospf_print_lsa_age(ntohs(lsah->ls_age)));
+ }
+
+ ospf_print_ls_type(flags, lsah->ls_type, lsah->ls_stateid,
+ lsah->ls_router);
+
+}
+
+#define TRUNC(addr) ((uchar_t *)(addr) > fragend)
+static int
+interpret_ospf_lsa(int flags, struct lsa *lsa, uchar_t *fragend)
+{
+ uchar_t *ls_end;
+ int rla_count, k;
+ struct rlalink *rl;
+ struct tos_metric *tosp;
+ struct in_addr *addr;
+ uint32_t *tosmetric;
+ struct aslametric *am;
+ uint32_t tm;
+ int tos, metric;
+
+ interpret_ospf_lsa_hdr(flags, &lsa->ls_hdr);
+
+ ls_end = (uchar_t *)lsa + ntohs(lsa->ls_hdr.ls_length);
+
+ if (TRUNC(ls_end))
+ return (-1);
+
+ switch (lsa->ls_hdr.ls_type) {
+
+ case LS_TYPE_ROUTER:
+ if (TRUNC(&lsa->lsa_un.un_rla.rla_flags))
+ return (-1);
+
+ if (flags & F_DTAIL) {
+ (void) ospf_print_bits(ospf_rla_flag_bits,
+ lsa->lsa_un.un_rla.rla_flags);
+ }
+
+ if (TRUNC(&lsa->lsa_un.un_rla.rla_count))
+ return (-1);
+ rla_count = ntohs(lsa->lsa_un.un_rla.rla_count);
+
+ rl = lsa->lsa_un.un_rla.rla_link;
+ if (TRUNC(rl))
+ return (-1);
+
+ while (rla_count-- != 0) {
+ if (TRUNC((uchar_t *)rl + sizeof (*rl)))
+ return (-1);
+ switch (rl->link_type) {
+ case RLA_TYPE_VIRTUAL:
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(), "Virtual Link");
+ }
+ /* fall through */
+ case RLA_TYPE_ROUTER:
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(), "Neighbor = %s",
+ inet_ntoa(rl->link_id));
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(), "Interface = %s",
+ inet_ntoa(rl->link_data));
+ }
+ break;
+ case RLA_TYPE_TRANSIT:
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ "Designated Router = %s",
+ inet_ntoa(rl->link_id));
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(), "Interface = %s",
+ inet_ntoa(rl->link_data));
+ }
+ break;
+ case RLA_TYPE_STUB:
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(), "Network = %s",
+ inet_ntoa(rl->link_id));
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(), "Mask = %s",
+ inet_ntoa(rl->link_data));
+ }
+ break;
+ default:
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ "Unknown link type %d",
+ rl->link_type);
+ }
+
+ }
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(), "TOS 0 metric = %d",
+ ntohs(rl->link_tos0metric));
+ }
+ tosp = (struct tos_metric *)(
+ (uchar_t *)rl + sizeof (rl->link_tos0metric));
+ for (k = 0; k > (int)rl->link_toscount; ++k, ++tosp) {
+ if (TRUNC(tosp))
+ return (-1);
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ "TOS %d metric = %d",
+ tosp->tos_type,
+ ntohs(tosp->tos_metric));
+ }
+
+ }
+ rl = (struct rlalink *)((uchar_t *)(rl + 1) +
+ ((rl->link_toscount) * sizeof (*tosp)));
+ if (TRUNC(rl))
+ return (-1); /* truncated */
+ }
+ break;
+ case LS_TYPE_NETWORK:
+
+ if (TRUNC(&lsa->lsa_un.un_nla.nla_mask))
+ return (-1);
+
+ if (flags & F_DTAIL) {
+ snprintf(get_line(0, 0), get_line_remain(),
+ "Mask = %s",
+ inet_ntoa(lsa->lsa_un.un_nla.nla_mask));
+ snprintf(get_line(0, 0), get_line_remain(),
+ "Routers:");
+ }
+ addr = lsa->lsa_un.un_nla.nla_router;
+ while ((uchar_t *)addr < ls_end) {
+ if ((uchar_t *)addr + sizeof (struct in_addr) > ls_end)
+ return (-1); /* truncated */
+ if (flags & F_DTAIL) {
+ snprintf(get_line(0, 0), get_line_remain(),
+ "\t%s", inet_ntoa(*addr));
+ }
+ ++addr;
+ }
+ break;
+ case LS_TYPE_SUM_IP:
+
+ if (TRUNC((uchar_t *)&lsa->lsa_un.un_sla.sla_mask +
+ sizeof (struct in_addr)))
+ return (-1);
+
+ if (flags & F_DTAIL) {
+ snprintf(get_line(0, 0), get_line_remain(), "Mask = %s",
+ inet_ntoa(lsa->lsa_un.un_sla.sla_mask));
+ }
+ /* FALLTHROUGH */
+ case LS_TYPE_SUM_ABR:
+ if (TRUNC(&lsa->lsa_un.un_sla.sla_tosmetric))
+ return (-1);
+ tosmetric = lsa->lsa_un.un_sla.sla_tosmetric;
+ while ((uchar_t *)tosmetric < ls_end) {
+ if ((uchar_t *)tosmetric + sizeof (tm) > fragend)
+ return (-1); /* truncated */
+ tm = ntohl(*tosmetric);
+ tos = (tm & SLA_MASK_TOS) >> SLA_SHIFT_TOS;
+ metric = tm & SLA_MASK_METRIC;
+ if (flags & F_DTAIL) {
+ snprintf(get_line(0, 0), get_line_remain(),
+ " tos %d metric %d", tos, metric);
+ }
+ ++tosmetric;
+ }
+ break;
+ case LS_TYPE_ASE:
+ if (TRUNC(&lsa->lsa_un.un_asla.asla_mask))
+ return (-1);
+ if (flags & F_DTAIL) {
+ snprintf(get_line(0, 0), get_line_remain(), "Mask = %s",
+ inet_ntoa(lsa->lsa_un.un_asla.asla_mask));
+ }
+ am = lsa->lsa_un.un_asla.asla_metric;
+ while ((uchar_t *)am < ls_end) {
+ if ((uchar_t *)am + sizeof (tm) > fragend)
+ return (-1); /* truncated */
+ tm = ntohl(am->asla_tosmetric);
+ tos = (tm & ASLA_MASK_TOS) >> ASLA_SHIFT_TOS;
+ metric = tm & ASLA_MASK_METRIC;
+ if (flags & F_DTAIL) {
+ snprintf(get_line(0, 0), get_line_remain(),
+ " type %d tos %d metric %d",
+ (tm & ASLA_FLAG_EXTERNAL) ? 2 : 1,
+ tos, metric);
+ }
+ if (am->asla_forward.s_addr != 0) {
+ if (flags & F_DTAIL) {
+ snprintf(get_line(0, 0),
+ get_line_remain(), " Forward %s",
+ inet_ntoa(am->asla_forward));
+ }
+ }
+ if (am->asla_tag.s_addr != 0) {
+ if (flags & F_DTAIL) {
+ snprintf(get_line(0, 0),
+ get_line_remain(), " Tag %s",
+ inet_ntoa(am->asla_tag));
+ }
+ }
+ ++am;
+ }
+ break;
+ default:
+ if (flags & F_DTAIL) {
+ snprintf(get_line(0, 0), get_line_remain(),
+ " Unknown LSA type %d", lsa->ls_hdr.ls_type);
+
+ }
+ break;
+ }
+ return (0);
+}
+#undef TRUNC
+
+int
+interpret_ospf(int flags, struct ospfhdr *ospf, int iplen, int fraglen)
+{
+ int nlsa, nlsah = 0;
+ struct lsa_hdr *lsah;
+ struct lsr *lsr;
+ struct lsa *lsa;
+ boolean_t trunc = B_FALSE;
+
+ if ((fraglen < OSPF_MIN_HEADER_SIZE) ||
+ (fraglen < ntohs(ospf->ospf_len)))
+ return (fraglen); /* incomplete header */
+
+ if (fraglen > ntohs(ospf->ospf_len))
+ fraglen = ntohs(ospf->ospf_len);
+
+
+ if (ospf->ospf_type > OSPF_TYPE_MAX) {
+ if (flags & F_SUM) {
+ (void) sprintf(sum_line, "Unknown OSPF TYPE %d \n",
+ ospf->ospf_type);
+ sum_line += strlen(sum_line);
+ }
+ if (flags & F_SUM) {
+ show_header("OSPF: ", "OSPF Header", fraglen);
+ show_space();
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Unknown OSPF Type = %d", ospf->ospf_type);
+ }
+ return (fraglen);
+ }
+
+ if (flags & F_SUM) {
+ sum_line = (char *)get_sum_line();
+ (void) sprintf(sum_line, "OSPF %s RTRID=%s ",
+ ospf_types[ospf->ospf_type],
+ inet_ntoa(ospf->ospf_routerid));
+ sum_line += strlen(sum_line);
+ (void) sprintf(sum_line, "AREA=%s LEN=%d ",
+ inet_ntoa(ospf->ospf_areaid),
+ ntohs((ushort_t)ospf->ospf_len));
+ sum_line += strlen(sum_line);
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("OSPF: ", "OSPF Header", fraglen);
+ show_space();
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Version = %d", ospf->ospf_version);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Type = %s", ospf_types[ospf->ospf_type]);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Router ID = %s", inet_ntoa(ospf->ospf_routerid));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Area ID = %s", inet_ntoa(ospf->ospf_areaid));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Checksum = 0x%x", ospf->ospf_chksum);
+
+ if (ospf->ospf_authtype > OSPF_AUTH_TYPE_MAX) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Auth = %d (unknown auth type)",
+ ospf->ospf_authtype);
+ } else {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Auth = %s", ospf_authtypes[ospf->ospf_authtype]);
+ }
+ }
+
+ if (ospf->ospf_version != 2) {
+ if (ospf->ospf_version == 3) {
+ if (flags & F_DTAIL)
+ snprintf(get_line(0, 0), get_line_remain(),
+ "ospfv3 packet in ipv4 header");
+ return (interpret_ospf6(flags, ospf, iplen, fraglen));
+ } else {
+ return (fraglen);
+ }
+ }
+
+ switch (ospf->ospf_type) {
+ case OSPF_TYPE_HELLO:
+ if (interpret_ospf_hello(flags, ospf, fraglen) < 0)
+ trunc = B_TRUE;
+ break;
+
+ case OSPF_TYPE_DB:
+ if (fraglen < OSPF_MIN_HEADER_SIZE + OSPF_MIN_DB_HEADER_SIZE) {
+ trunc = B_TRUE;
+ break;
+ }
+ if (flags & F_SUM) {
+ sprintf(sum_line, " %s %s S %X", ospf_print_bits(
+ ospf_option_bits, ospf->ospf_db.db_options),
+ ospf_print_bits(ospf_db_flags_bits,
+ ospf->ospf_db.db_flags),
+ ntohl(ospf->ospf_db.db_seq));
+ sum_line += strlen(sum_line);
+ }
+ if (flags & F_DTAIL) {
+ show_header("OSPF DB: ", "Database Description Packet",
+ fraglen);
+ show_space();
+ snprintf(get_line(0, 0), get_line_remain(),
+ "Options = %s", ospf_print_bits(
+ ospf_option_bits, ospf->ospf_db.db_options));
+ snprintf(get_line(0, 0), get_line_remain(),
+ "Flags = %s", ospf_print_bits(
+ ospf_db_flags_bits, ospf->ospf_db.db_flags));
+ snprintf(get_line(0, 0), get_line_remain(),
+ "Sequence = 0x%X", ntohl(ospf->ospf_db.db_seq));
+ /* Print all the LS advs */
+ lsah = ospf->ospf_db.db_lshdr;
+ while ((uchar_t *)lsah < ((uchar_t *)ospf + fraglen)) {
+ if ((uchar_t *)lsah + sizeof (struct lsa_hdr) >
+ ((uchar_t *)ospf + fraglen)) {
+ trunc = B_TRUE;
+ break;
+ }
+ interpret_ospf_lsa_hdr(flags, lsah);
+ ++lsah;
+ }
+ }
+ break;
+
+ case OSPF_TYPE_LSR:
+ if (fraglen < OSPF_MIN_HEADER_SIZE + OSPF_MIN_LSR_HEADER_SIZE) {
+ trunc = B_TRUE;
+ break;
+ }
+ if (flags & F_DTAIL) {
+ snprintf(get_line(0, 0), get_line_remain(),
+ "Link State Request Packet");
+ }
+ lsr = ospf->ospf_lsr;
+ while ((uchar_t *)lsr < ((uchar_t *)ospf + fraglen)) {
+ if ((uchar_t *)lsr + sizeof (struct lsr) >
+ ((uchar_t *)ospf + fraglen)) {
+ trunc = B_TRUE;
+ break;
+ }
+ if (flags & F_SUM) {
+ nlsah++;
+ }
+ if (flags & F_DTAIL) {
+ ospf_print_ls_type(flags, ntohl(lsr->ls_type),
+ lsr->ls_stateid, lsr->ls_router);
+ }
+ ++lsr;
+ }
+ if (flags & F_SUM) {
+ sprintf(sum_line, " %d LSAs", nlsah);
+ sum_line += strlen(sum_line);
+ }
+ break;
+
+ case OSPF_TYPE_LSU:
+ if (fraglen < OSPF_MIN_HEADER_SIZE + OSPF_MIN_LSU_HEADER_SIZE) {
+ trunc = B_TRUE;
+ break;
+ }
+ if (flags & F_DTAIL) {
+ show_header("OSPF LSU: ", "Link State Update Packet",
+ fraglen);
+ show_space();
+ }
+ lsa = ospf->ospf_lsu.lsu_lsa;
+ nlsa = ntohl(ospf->ospf_lsu.lsu_count);
+ if (flags & F_SUM) {
+ sprintf(sum_line, "%d LSAs", nlsa);
+ sum_line += strlen(sum_line);
+ break;
+ }
+ while (nlsa-- != 0) {
+ uchar_t *fragend = (uchar_t *)ospf + fraglen;
+ if (((uchar_t *)lsa >= fragend) ||
+ ((uchar_t *)lsa + sizeof (struct lsa_hdr) >
+ fragend) ||
+ ((uchar_t *)lsa + ntohs(lsa->ls_hdr.ls_length) >
+ fragend)) {
+ trunc = B_TRUE;
+ break;
+ }
+
+ if (interpret_ospf_lsa(flags, lsa, fragend) < 0) {
+ trunc = B_TRUE;
+ break;
+ }
+ lsa = (struct lsa *)((uchar_t *)lsa +
+ ntohs(lsa->ls_hdr.ls_length));
+ }
+
+ break;
+
+ case OSPF_TYPE_LSA:
+ if (flags & F_DTAIL) {
+ show_header("OSPF LSA: ", "Link State Ack Packet",
+ fraglen);
+ show_space();
+ }
+ lsah = ospf->ospf_lsa.lsa_lshdr;
+ nlsah = 0;
+ while ((uchar_t *)lsah < ((uchar_t *)ospf + fraglen)) {
+ if ((uchar_t *)lsah + sizeof (struct lsa_hdr) >
+ ((uchar_t *)ospf + fraglen)) {
+ trunc = B_TRUE;
+ break;
+ }
+ nlsah++;
+ if (flags & F_DTAIL)
+ interpret_ospf_lsa_hdr(flags, lsah);
+ ++lsah;
+ }
+ if (flags & F_SUM) {
+ sprintf(sum_line, "%d LSAs", nlsah);
+ sum_line += strlen(sum_line);
+ }
+ break;
+
+ default:
+ /* NOTREACHED */
+ break;
+ }
+ if (trunc) {
+ if (flags & F_SUM) {
+ sprintf(sum_line, "--truncated");
+ sum_line += strlen(sum_line);
+ }
+ if (flags & F_DTAIL)
+ snprintf(get_line(0, 0), get_line_remain(),
+ "--truncated");
+ }
+
+ return (fraglen);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ospf.h b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ospf.h
new file mode 100644
index 0000000000..8bf4d3d709
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ospf.h
@@ -0,0 +1,280 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _OSPF_H
+#define _OSPF_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Definitions for parsing OSPF packets (RFC 2328 and RFC 2740)
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define OSPF_TYPE_UMD 0 /* UMD's special monitoring packets */
+#define OSPF_TYPE_HELLO 1 /* Hello */
+#define OSPF_TYPE_DB 2 /* Database Description */
+#define OSPF_TYPE_LSR 3 /* Link State Request */
+#define OSPF_TYPE_LSU 4 /* Link State Update */
+#define OSPF_TYPE_LSA 5 /* Link State Ack */
+#define OSPF_TYPE_MAX 6
+
+extern char *ospf_types[];
+struct bits {
+ uint32_t bit;
+ const char *str;
+};
+char *ospf_print_bits(const struct bits *, uchar_t);
+char *ospf_print_lsa_age(long);
+
+/* Options *_options */
+#define OSPF_OPTION_T 0x01 /* RFC 2328 T bit: TOS support */
+#define OSPF_OPTION_E 0x02 /* E bit: External routes advertised */
+#define OSPF_OPTION_MC 0x04 /* MC bit: Multicast capable */
+#define OSPF_OPTION_N 0x08 /* N bit: For type-7 LSA */
+#define OSPF_OPTION_R 0x10 /* R bit: Router bit */
+#define OSPF_OPTION_DC 0x20 /* DC bit: Demand circuits */
+
+#define OSPF_OPTION_V6 0x01 /* RFC 2740 V6 bit */
+
+/* ospf_authtype */
+#define OSPF_AUTH_NONE 0 /* No auth-data */
+#define OSPF_AUTH_SIMPLE 1 /* Simple password */
+#define OSPF_AUTH_MD5 2 /* MD5 authentication */
+#define OSPF_AUTH_MD5_LEN 16 /* length of MD5 authentication */
+
+#define OSPF_AUTH_TYPE_MAX 3
+
+/* db_flags */
+#define OSPF_DB_INIT 0x04 /* "I" */
+#define OSPF_DB_MORE 0x02 /* "M" */
+#define OSPF_DB_MASTER 0x01 /* "MS" */
+
+
+/* ls_type */
+#define LS_TYPE_ROUTER 1 /* router link */
+#define LS_TYPE_NETWORK 2 /* network link */
+#define LS_TYPE_SUM_IP 3 /* summary link */
+#define LS_TYPE_SUM_ABR 4 /* summary area link */
+#define LS_TYPE_ASE 5 /* ASE */
+#define LS_TYPE_GROUP 6 /* Group membership (multicast */
+ /* extensions 23 July 1991) */
+#define LS_TYPE_TYPE7 7 /* Type 7 LSA */
+#define LS_TYPE_LINK 8 /* Link LSA */
+#define LS_TYPE_INTRA_AP 9 /* Intra-Area-Prefix */
+#define LS_TYPE_MAX 10
+#define LS_TYPE_MASK 0x1fff
+
+#define LS_TYPE_INTER_AP 3 /* RFC 2740 Inter-Area-Prefix */
+#define LS_TYPE_INTER_AR 4 /* RFC 2740 Inter-Area-Router */
+
+#define LS6_SCOPE_LINKLOCAL 0x0000
+#define LS6_SCOPE_AREA 0x2000
+#define LS6_SCOPE_AS 0x4000
+#define LS6_SCOPE_MASK 0x6000
+
+/* rla_link.link_type */
+#define RLA_TYPE_ROUTER 1 /* point-to-point to another router */
+#define RLA_TYPE_TRANSIT 2 /* connection to transit network */
+#define RLA_TYPE_STUB 3 /* connection to stub network */
+#define RLA_TYPE_VIRTUAL 4 /* virtual link */
+
+/* rla_flags */
+#define RLA_FLAG_B 0x01
+#define RLA_FLAG_E 0x02
+#define RLA_FLAG_V 0x04
+#define RLA_FLAG_W 0x08
+
+
+/* sla_tosmetric breakdown */
+#define SLA_MASK_TOS 0x7f000000
+#define SLA_MASK_METRIC 0x00ffffff
+#define SLA_SHIFT_TOS 24
+
+/* asla_tosmetric breakdown */
+#define ASLA_FLAG_EXTERNAL 0x80000000
+#define ASLA_MASK_TOS 0x7f000000
+#define ASLA_SHIFT_TOS 24
+#define ASLA_MASK_METRIC 0x00ffffff
+
+/* multicast vertex type */
+#define MCLA_VERTEX_ROUTER 1
+#define MCLA_VERTEX_NETWORK 2
+
+/* link state advertisement header */
+struct lsa_hdr {
+ ushort_t ls_age;
+ uchar_t ls_options;
+ uchar_t ls_type;
+ struct in_addr ls_stateid;
+ struct in_addr ls_router;
+ uint32_t ls_seq;
+ ushort_t ls_chksum;
+ ushort_t ls_length;
+};
+
+/* link state advertisement */
+struct lsa {
+ struct lsa_hdr ls_hdr;
+
+ /* Link state types */
+ union {
+ /* Router links advertisements */
+ struct {
+ uchar_t rla_flags;
+ uchar_t rla_zero[1];
+ ushort_t rla_count;
+ struct rlalink {
+ struct in_addr link_id;
+ struct in_addr link_data;
+ uchar_t link_type;
+ uchar_t link_toscount;
+ ushort_t link_tos0metric;
+ } rla_link[1]; /* may repeat */
+ } un_rla;
+
+ /* Network links advertisements */
+ struct {
+ struct in_addr nla_mask;
+ struct in_addr nla_router[1]; /* may repeat */
+ } un_nla;
+
+ /* Summary links advertisements */
+ struct {
+ struct in_addr sla_mask;
+ uint32_t sla_tosmetric[1]; /* may repeat */
+ } un_sla;
+
+ /* AS external links advertisements */
+ struct {
+ struct in_addr asla_mask;
+ struct aslametric {
+ uint32_t asla_tosmetric;
+ struct in_addr asla_forward;
+ struct in_addr asla_tag;
+ } asla_metric[1]; /* may repeat */
+ } un_asla;
+
+ /* Multicast group membership */
+ struct mcla {
+ uint32_t mcla_vtype;
+ struct in_addr mcla_vid;
+ } un_mcla[1];
+ } lsa_un;
+};
+
+/*
+ * TOS metric struct (will be 0 or more in router links update)
+ */
+struct tos_metric {
+ uchar_t tos_type;
+ uchar_t tos_zero;
+ ushort_t tos_metric;
+};
+
+/*
+ * OSPF minimum header sizes
+ */
+#define OSPF_AUTH_SIZE 8
+#define OSPF_MIN_HEADER_SIZE 24
+#define OSPF6_MIN_HEADER_SIZE 16
+#define OSPF_MIN_HELLO_HEADER_SIZE 20
+#define OSPF_MIN_DB_HEADER_SIZE 8
+#define OSPF6_MIN_DB_HEADER_SIZE 12
+#define OSPF_MIN_LSR_HEADER_SIZE 12
+#define OSPF_MIN_LSU_HEADER_SIZE 4
+
+/*
+ * ospf packet header
+ */
+struct ospfhdr {
+ uchar_t ospf_version;
+ uchar_t ospf_type;
+ ushort_t ospf_len;
+ struct in_addr ospf_routerid;
+ struct in_addr ospf_areaid;
+ ushort_t ospf_chksum;
+ ushort_t ospf_authtype;
+ uchar_t ospf_authdata[OSPF_AUTH_SIZE];
+ union {
+
+ /* Hello packet */
+ struct {
+ struct in_addr hello_mask;
+ ushort_t hello_helloint;
+ uchar_t hello_options;
+ uchar_t hello_priority;
+ uint32_t hello_deadint;
+ struct in_addr hello_dr;
+ struct in_addr hello_bdr;
+ struct in_addr hello_neighbor[1]; /* may repeat */
+ } un_hello;
+
+ /* Database Description packet */
+ struct {
+ uchar_t db_zero[2];
+ uchar_t db_options;
+ uchar_t db_flags;
+ uint32_t db_seq;
+ struct lsa_hdr db_lshdr[1]; /* may repeat */
+ } un_db;
+
+ /* Link State Request */
+ struct lsr {
+ uint32_t ls_type;
+ struct in_addr ls_stateid;
+ struct in_addr ls_router;
+ } un_lsr[1]; /* may repeat */
+
+ /* Link State Update */
+ struct {
+ uint32_t lsu_count;
+ struct lsa lsu_lsa[1]; /* may repeat */
+ } un_lsu;
+
+ /* Link State Acknowledgement */
+ struct {
+ struct lsa_hdr lsa_lshdr[1]; /* may repeat */
+ } un_lsa;
+ } ospf_un;
+};
+
+#define ospf_hello ospf_un.un_hello
+#define ospf_db ospf_un.un_db
+#define ospf_lsr ospf_un.un_lsr
+#define ospf_lsu ospf_un.un_lsu
+#define ospf_lsa ospf_un.un_lsa
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _OSPF_H */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ospf6.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ospf6.c
new file mode 100644
index 0000000000..6394c8e9d7
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ospf6.c
@@ -0,0 +1,815 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/if_ether.h>
+#include <arpa/inet.h>
+#include "snoop.h"
+#include "snoop_ospf.h"
+#include "snoop_ospf6.h"
+
+extern char *dlc_header;
+static char *sum_line;
+extern const struct bits ospf_db_flags_bits[];
+extern const struct bits ospf_rla_flag_bits[];
+extern const struct bits ospf_option_bits[];
+
+const struct bits ospf6_option_bits[] = {
+ { OSPF_OPTION_V6, "V6" },
+ { OSPF_OPTION_E, "E" },
+ { OSPF_OPTION_MC, "MC" },
+ { OSPF_OPTION_N, "N" },
+ { OSPF_OPTION_R, "R" },
+ { OSPF_OPTION_DC, "DC" },
+ { 0, NULL }
+};
+
+/*
+ * return a printable string in dotted-decimal notation
+ * for id.
+ */
+static char *
+print_ipaddr(uint32_t id)
+{
+ struct in_addr tmp;
+
+ tmp.s_addr = id;
+ return (inet_ntoa(tmp));
+}
+
+static int
+interpret_ospf6_hello(int flags, struct ospf6hdr *op, int fraglen)
+{
+ uint32_t *nbr;
+ int j;
+
+ if (fraglen < OSPF6_MIN_HEADER_SIZE + OSPF_MIN_HELLO_HEADER_SIZE)
+ return (-1); /* truncated packet */
+
+ if (flags & F_SUM) {
+ if (op->ospf6_hello.hello_dr != 0) {
+ (void) sprintf(sum_line, "DR=%s ",
+ print_ipaddr(op->ospf6_hello.hello_dr));
+ }
+ sum_line += strlen(sum_line);
+ if (op->ospf6_hello.hello_bdr != 0) {
+ (void) sprintf(sum_line, "BDR=%s ",
+ print_ipaddr(op->ospf6_hello.hello_bdr));
+ }
+ sum_line += strlen(sum_line);
+ j = 0;
+ nbr = op->ospf6_hello.hello_neighbor;
+ while ((uchar_t *)nbr < ((uchar_t *)op + fraglen)) {
+ if ((uchar_t *)nbr + sizeof (struct in_addr) >
+ ((uchar_t *)op + fraglen))
+ return (-1); /* truncated */
+ ++nbr;
+ j++;
+ }
+ (void) sprintf(sum_line, "%d nbrs", j);
+ sum_line += strlen(sum_line);
+
+ }
+ if (flags & F_DTAIL) {
+ show_header("OSPF HELLO: ", "Hello Packet",
+ ntohs(op->ospf6_len));
+ show_space();
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Options = %s", ospf_print_bits(ospf6_option_bits,
+ op->ospf6_hello.hello6_options));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Interface ID = %s",
+ print_ipaddr(op->ospf6_hello.hello_ifid));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Hello interval = %d",
+ ntohs(op->ospf6_hello.hello_helloint));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Priority = %d", op->ospf6_hello.hello6_priority);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Dead interval = %u", ntohl(op->ospf6_hello.hello_deadint));
+ if (op->ospf6_hello.hello_dr != 0) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Designated Router = %s",
+ print_ipaddr(op->ospf6_hello.hello_dr));
+ }
+ if (op->ospf6_hello.hello_bdr != 0) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Backup Designated Router = %s",
+ print_ipaddr(op->ospf6_hello.hello_bdr));
+ }
+ nbr = op->ospf6_hello.hello_neighbor;
+ while ((uchar_t *)nbr < ((uchar_t *)op + fraglen)) {
+ if ((uchar_t *)nbr + sizeof (struct in_addr) >
+ ((uchar_t *)op + fraglen))
+ return (-1); /* truncated */
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Neigbor: %s", print_ipaddr(*nbr));
+ ++nbr;
+ }
+ }
+ return (fraglen);
+}
+
+static void
+ospf6_print_ls_type(int flags, uint_t ls6_type, uint32_t ls6_stateid,
+ uint32_t ls6_router)
+{
+ char scope[15];
+
+ if (flags & F_SUM)
+ return;
+
+ switch (ls6_type & LS6_SCOPE_MASK) {
+ case LS6_SCOPE_LINKLOCAL:
+ snprintf(scope, sizeof (scope), "linklocal");
+ break;
+ case LS6_SCOPE_AREA:
+ snprintf(scope, sizeof (scope), "area");
+ break;
+ case LS6_SCOPE_AS:
+ snprintf(scope, sizeof (scope), "AS");
+ break;
+ default:
+ snprintf(scope, sizeof (scope), "");
+ break;
+ }
+ switch (ls6_type & LS_TYPE_MASK) {
+ case LS_TYPE_ROUTER:
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "%s Router = %s", scope, print_ipaddr(ls6_router));
+ }
+ break;
+ case LS_TYPE_NETWORK:
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "%s Net DR %s IF %s", scope,
+ print_ipaddr(ls6_router),
+ print_ipaddr(ls6_stateid));
+ }
+ break;
+ case LS_TYPE_INTER_AP:
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "%s Inter-area-prefix = %s ABR %s", scope,
+ print_ipaddr(ls6_stateid),
+ print_ipaddr(ls6_router));
+ }
+ break;
+ case LS_TYPE_INTER_AR:
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "%s Inter-area-router = %s Router %s", scope,
+ print_ipaddr(ls6_router),
+ print_ipaddr(ls6_stateid));
+ }
+ break;
+ case LS_TYPE_ASE:
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "%s ASE = %s ASBR %s", scope,
+ print_ipaddr(ls6_stateid),
+ print_ipaddr(ls6_router));
+ }
+ break;
+ case LS_TYPE_GROUP:
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "%s group = %s Router %s", scope,
+ print_ipaddr(ls6_stateid),
+ print_ipaddr(ls6_router));
+ }
+ break;
+ case LS_TYPE_TYPE7:
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "%s Type 7 = %s Router %s", scope,
+ print_ipaddr(ls6_stateid),
+ print_ipaddr(ls6_router));
+ }
+ break;
+ case LS_TYPE_LINK:
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "%s link = %s Router %s", scope,
+ print_ipaddr(ls6_stateid),
+ print_ipaddr(ls6_router));
+ }
+ break;
+ case LS_TYPE_INTRA_AP:
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "%s Inter-area-prefix = %s Router %s", scope,
+ print_ipaddr(ls6_stateid),
+ print_ipaddr(ls6_router));
+ }
+ break;
+ default:
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "%s Unknown type = 0x%x", ls6_type);
+ }
+ break;
+ }
+}
+
+static int
+ospf6_print_lsaprefix(int flags, struct lsa6_prefix *lpfx)
+{
+ int k;
+ struct in6_addr prefix;
+ char prefixstr[INET6_ADDRSTRLEN];
+
+ k = (lpfx->lsa6_plen + 31)/32;
+ if (k * 4 > sizeof (struct in6_addr)) {
+ if (flags & F_SUM) {
+ sprintf(sum_line, "Unknown prefix len %d",
+ lpfx->lsa6_plen);
+ sum_line += strlen(sum_line);
+ }
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Unknown prefix len %d", lpfx->lsa6_plen);
+ }
+ }
+ memset((void *)&prefix, 0, sizeof (prefix));
+ memcpy((void *)&prefix, lpfx->lsa6_pfx, k * 4);
+ (void) inet_ntop(AF_INET6, (char *)&prefix, prefixstr,
+ INET6_ADDRSTRLEN);
+ if (flags & F_SUM) {
+ sprintf(sum_line, "%s/%d", prefixstr, lpfx->lsa6_plen);
+ sum_line += strlen(sum_line);
+ }
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "%s/%d", prefixstr, lpfx->lsa6_plen);
+ }
+ if (lpfx->lsa6_popt != 0) {
+ if (flags & F_SUM) {
+ sprintf(sum_line, "(opt = %x)", lpfx->lsa6_popt);
+ sum_line += strlen(sum_line);
+ }
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "(opt = %x)", lpfx->lsa6_popt);
+ }
+ }
+ return (sizeof (*lpfx) - 4 + k * 4);
+}
+
+static void
+interpret_ospf6_lsa_hdr(int flags, struct lsa6_hdr *lsah)
+{
+ if (flags & F_SUM)
+ return;
+
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Sequence = %X ", ntohl(lsah->ls6_seq));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Age = %X ", ospf_print_lsa_age(ntohl(lsah->ls6_age)));
+ }
+
+ ospf6_print_ls_type(flags, lsah->ls6_type, lsah->ls6_stateid,
+ lsah->ls6_router);
+
+}
+
+#define TRUNC(addr) ((uchar_t *)(addr) > fragend)
+static int
+interpret_ospf6_lsa(int flags, struct lsa6 *lsa, uchar_t *fragend)
+{
+ uchar_t *ls_end;
+ int k, j;
+ struct rla6link *rl;
+ uint32_t *addr;
+ struct lsa6_prefix *lpfx;
+ struct llsa *llsa;
+ char addrstr[INET6_ADDRSTRLEN];
+
+ interpret_ospf6_lsa_hdr(flags, &lsa->ls6_hdr);
+
+ ls_end = (uchar_t *)lsa + ntohs(lsa->ls6_hdr.ls6_length);
+
+ if (TRUNC(ls_end))
+ return (-1);
+
+ switch (ntohs(lsa->ls6_hdr.ls6_type)) {
+
+ case LS_TYPE_ROUTER|LS6_SCOPE_AREA:
+ if (TRUNC(&lsa->lsa_un.un_rla.rla6_flags))
+ return (-1);
+
+ (void) ospf_print_bits(ospf_rla_flag_bits,
+ lsa->lsa_un.un_rla.rla6_flags);
+
+ if (TRUNC(&lsa->lsa_un.un_rla.rla6_options))
+ return (-1);
+ (void) ospf_print_bits(ospf_option_bits,
+ ntohl(lsa->lsa_un.un_rla.rla6_options));
+
+ rl = lsa->lsa_un.un_rla.rla_link;
+ if (TRUNC(rl))
+ return (-1);
+
+ while (rl + sizeof (*rl) <= (struct rla6link *)ls_end) {
+ if (TRUNC((uchar_t *)rl + sizeof (*rl)))
+ return (-1);
+ if (flags & F_SUM) {
+ sprintf(sum_line, "{"); /* } (ctags) */
+ sum_line += strlen(sum_line);
+ }
+ switch (rl->link_type) {
+ case RLA_TYPE_VIRTUAL:
+ if (flags & F_SUM) {
+ sprintf(sum_line, "virt ");
+ sum_line += strlen(sum_line);
+ }
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(), "Virtual Link");
+ }
+ /* FALLTHROUGH */
+ case RLA_TYPE_ROUTER:
+ if (flags & F_SUM) {
+ sprintf(sum_line, "nbrid %s",
+ print_ipaddr(rl->link_nrtid));
+ sum_line += strlen(sum_line);
+ sprintf(sum_line, " nbrif %s",
+ print_ipaddr(rl->link_nifid));
+ sum_line += strlen(sum_line);
+ sprintf(sum_line, " if %s",
+ print_ipaddr(rl->link_ifid));
+ sum_line += strlen(sum_line);
+ }
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(), "Neighbor = %s",
+ print_ipaddr(rl->link_nrtid));
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ "Interface = %s id %s",
+ print_ipaddr(rl->link_nifid),
+ print_ipaddr(rl->link_ifid));
+ }
+ break;
+ case RLA_TYPE_TRANSIT:
+ if (flags & F_SUM) {
+ sprintf(sum_line, "dr %s",
+ print_ipaddr(rl->link_nrtid));
+ sum_line += strlen(sum_line);
+ sprintf(sum_line, " drif %s",
+ print_ipaddr(rl->link_nifid));
+ sum_line += strlen(sum_line);
+ sprintf(sum_line, " if %s",
+ print_ipaddr(rl->link_ifid));
+ sum_line += strlen(sum_line);
+ }
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ "Designated Router = %s",
+ print_ipaddr(rl->link_nrtid));
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ "DR Interface = %s id %s",
+ print_ipaddr(rl->link_nifid),
+ print_ipaddr(rl->link_ifid));
+ }
+ break;
+ default:
+ if (flags & F_SUM) {
+ sprintf(sum_line,
+ "Unknown link type %d",
+ rl->link_type);
+ sum_line += strlen(sum_line);
+ }
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ "Unknown link type %d",
+ rl->link_type);
+ }
+
+ }
+ if (flags & F_SUM) {
+ sprintf(sum_line, " metric %d",
+ ntohs(rl->link_metric));
+ sum_line += strlen(sum_line);
+ }
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(), " metric = %d",
+ ntohs(rl->link_metric));
+ }
+ if (flags & F_SUM) { /* { (ctags) */
+ sprintf(sum_line, " }");
+ sum_line += strlen(sum_line);
+ }
+ rl++;
+ if ((uchar_t *)rl > fragend)
+ return (-1); /* truncated */
+ }
+ break;
+ case LS_TYPE_NETWORK | LS6_SCOPE_AREA:
+
+ if (TRUNC(&lsa->lsa_un.un_nla.nla_options))
+ return (-1);
+
+ (void) ospf_print_bits(ospf6_option_bits,
+ ntohl(lsa->lsa_un.un_nla.nla_options));
+
+ if (flags & F_SUM) {
+ sprintf(sum_line, " rtrs");
+ sum_line += strlen(sum_line);
+ }
+ if (flags & F_DTAIL) {
+ snprintf(get_line(0, 0), get_line_remain(),
+ "Routers:");
+ }
+ addr = lsa->lsa_un.un_nla.nla_router;
+ while ((uchar_t *)addr < ls_end) {
+ if ((uchar_t *)addr + sizeof (struct in_addr) > ls_end)
+ return (-1); /* truncated */
+ if (flags & F_SUM) {
+ sprintf(sum_line, " %s", print_ipaddr(*addr));
+ sum_line += strlen(sum_line);
+ }
+ if (flags & F_DTAIL) {
+ snprintf(get_line(0, 0), get_line_remain(),
+ "\t%s", print_ipaddr(*addr));
+ }
+ ++addr;
+ }
+ break;
+ case LS_TYPE_INTER_AP | LS6_SCOPE_AREA:
+
+ if (TRUNC(&lsa->lsa_un.un_inter_ap.inter_ap_metric))
+ return (-1);
+
+ if (flags & F_SUM) {
+ sprintf(sum_line, " metric %s",
+ ntohl(lsa->lsa_un.un_inter_ap.inter_ap_metric) &
+ SLA_MASK_METRIC);
+ sum_line += strlen(sum_line);
+ }
+ if (flags & F_DTAIL) {
+ snprintf(get_line(0, 0), get_line_remain(),
+ "Metric = %s",
+ ntohl(lsa->lsa_un.un_inter_ap.inter_ap_metric) &
+ SLA_MASK_METRIC);
+ }
+ lpfx = lsa->lsa_un.un_inter_ap.inter_ap_prefix;
+ if (lpfx > (struct lsa6_prefix *)ls_end)
+ return (-1);
+ while (lpfx + sizeof (*lpfx) <= (struct lsa6_prefix *)ls_end) {
+ k = ospf6_print_lsaprefix(flags, lpfx);
+ lpfx = (struct lsa6_prefix *)(((uchar_t *)lpfx) + k);
+ if (lpfx > (struct lsa6_prefix *)ls_end)
+ return (-1);
+ }
+ break;
+ case LS_TYPE_LINK:
+ llsa = &lsa->lsa_un.un_llsa;
+ if (TRUNC(llsa->llsa_options))
+ return (-1);
+ ospf_print_bits(ospf6_option_bits, ntohl(llsa->llsa_options));
+ if (TRUNC(llsa->llsa_nprefix))
+ return (-1);
+ (void) inet_ntop(AF_INET6, &llsa->llsa_lladdr,
+ addrstr, INET6_ADDRSTRLEN);
+ if (flags & F_SUM) {
+ sprintf(sum_line, " pri %d lladdr %s npref %d",
+ ntohl(llsa->llsa_priority), addrstr,
+ ntohl(llsa->llsa_nprefix));
+ sum_line += strlen(sum_line);
+ }
+ if (flags & F_DTAIL) {
+ snprintf(get_line(0, 0), get_line_remain(),
+ "Priority %d", ntohl(llsa->llsa_priority));
+ snprintf(get_line(0, 0), get_line_remain(),
+ "Link Local addr %d", addrstr);
+ snprintf(get_line(0, 0), get_line_remain(),
+ "npref %d", ntohl(llsa->llsa_nprefix));
+ }
+ lpfx = llsa->llsa_prefix;
+ for (j = 0; j < ntohl(llsa->llsa_nprefix); j++) {
+ if (TRUNC(lpfx))
+ return (-1);
+ k = ospf6_print_lsaprefix(flags, lpfx);
+ lpfx = (struct lsa6_prefix *)(((uchar_t *)lpfx) + k);
+ }
+ break;
+
+ case LS_TYPE_INTRA_AP | LS6_SCOPE_AREA:
+ if (TRUNC(&lsa->lsa_un.un_intra_ap.intra_ap_rtid))
+ return (-1);
+ ospf6_print_ls_type(flags,
+ ntohs(lsa->lsa_un.un_intra_ap.intra_ap_lstype),
+ lsa->lsa_un.un_intra_ap.intra_ap_lsid,
+ lsa->lsa_un.un_intra_ap.intra_ap_rtid);
+ if (TRUNC(&lsa->lsa_un.un_intra_ap.intra_ap_nprefix))
+ return (-1);
+ if (flags & F_SUM) {
+ sprintf(sum_line, " npref %d",
+ ntohs(lsa->lsa_un.un_intra_ap.intra_ap_nprefix));
+ sum_line += strlen(sum_line);
+ }
+ if (flags & F_DTAIL) {
+ snprintf(get_line(0, 0), get_line_remain(), "NPref %d",
+ ntohs(lsa->lsa_un.un_intra_ap.intra_ap_nprefix));
+ }
+
+ lpfx = lsa->lsa_un.un_intra_ap.intra_ap_prefix;
+ for (j = 0;
+ j < ntohs(lsa->lsa_un.un_intra_ap.intra_ap_nprefix); j++) {
+ if (TRUNC(lpfx))
+ return (-1);
+ k = ospf6_print_lsaprefix(flags, lpfx);
+ lpfx = (struct lsa6_prefix *)(((uchar_t *)lpfx) + k);
+ }
+ break;
+
+ default:
+ if (flags & F_SUM) {
+ sprintf(sum_line, " Unknown LSA type (%d)",
+ lsa->ls6_hdr.ls6_type);
+ sum_line += strlen(sum_line);
+ }
+ if (flags & F_DTAIL) {
+ snprintf(get_line(0, 0), get_line_remain(),
+ " Unknown LSA type %d", lsa->ls6_hdr.ls6_type);
+
+ }
+ break;
+ }
+ return (0);
+}
+#undef TRUNC
+int
+interpret_ospf6(int flags, struct ospf6hdr *ospf, int iplen, int fraglen)
+{
+ boolean_t trunc = B_FALSE;
+ struct lsa6_hdr *lsah;
+ struct lsr6 *lsr;
+ struct lsa6 *lsa;
+ int nlsa, nlsah;
+
+ if ((fraglen < OSPF6_MIN_HEADER_SIZE) ||
+ (fraglen < ntohs(ospf->ospf6_len)))
+ return (fraglen); /* incomplete header */
+
+ if (ospf->ospf6_version != 3) {
+ if (ospf->ospf6_version == 2) {
+ if (flags & F_DTAIL)
+ snprintf(get_line(0, 0), get_line_remain(),
+ "ospfv2 packet in ipv6 header");
+ return (interpret_ospf(flags, ospf, iplen, fraglen));
+ } else {
+ return (fraglen);
+ }
+ }
+
+ if (fraglen > ntohs(ospf->ospf6_len))
+ fraglen = ntohs(ospf->ospf6_len);
+
+ if (ospf->ospf6_type > OSPF_TYPE_MAX) {
+ if (flags & F_SUM) {
+ (void) sprintf(sum_line, "Unknown OSPF TYPE %d \n",
+ ospf->ospf6_type);
+ sum_line += strlen(sum_line);
+ }
+ if (flags & F_SUM) {
+ show_header("OSPFv3: ", "OSPFv3 Header", fraglen);
+ show_space();
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Unknown OSPF Type = %d", ospf->ospf6_type);
+ }
+ return (fraglen);
+ }
+
+ if (flags & F_SUM) {
+ sum_line = (char *)get_sum_line();
+ (void) sprintf(sum_line, "OSPFv3 %s RTRID=%s ",
+ ospf_types[ospf->ospf6_type],
+ print_ipaddr(ospf->ospf6_routerid));
+ sum_line += strlen(sum_line);
+ (void) sprintf(sum_line, "AREA=%s LEN=%d instance %u ",
+ print_ipaddr(ospf->ospf6_areaid),
+ ntohs((ushort_t)ospf->ospf6_len), ospf->ospf6_instanceid);
+ sum_line += strlen(sum_line);
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("OSPFv3: ", "OSPF Header", fraglen);
+ show_space();
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Version = %d", ospf->ospf6_version);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Type = %s", ospf_types[ospf->ospf6_type]);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Router ID = %s", print_ipaddr(ospf->ospf6_routerid));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Area ID = %s", print_ipaddr(ospf->ospf6_areaid));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Checksum = 0x%x", ospf->ospf6_chksum);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Instance = %u", ospf->ospf6_instanceid);
+ }
+
+ switch (ospf->ospf6_type) {
+ case OSPF_TYPE_HELLO:
+ if (interpret_ospf6_hello(flags, ospf, fraglen) < 0)
+ trunc = B_TRUE;
+ break;
+
+ case OSPF_TYPE_DB:
+ if (fraglen < OSPF6_MIN_HEADER_SIZE +
+ OSPF6_MIN_DB_HEADER_SIZE) {
+ trunc = B_TRUE;
+ break;
+ }
+ if (flags & F_SUM) {
+ sprintf(sum_line, " %s %s mtu %u S %X", ospf_print_bits(
+ ospf6_option_bits,
+ ntohl(ospf->ospf6_db.db_options)),
+ ospf_print_bits(ospf_db_flags_bits,
+ ospf->ospf6_db.db_flags),
+ ntohs(ospf->ospf6_db.db_mtu),
+ ntohl(ospf->ospf6_db.db_seq));
+ sum_line += strlen(sum_line);
+ }
+ if (flags & F_DTAIL) {
+ show_header("OSPF DB: ", "Database Description Packet",
+ fraglen);
+ show_space();
+ snprintf(get_line(0, 0), get_line_remain(),
+ "Options = %s", ospf_print_bits(
+ ospf6_option_bits, ospf->ospf6_db.db_options));
+ snprintf(get_line(0, 0), get_line_remain(),
+ "Flags = %s", ospf_print_bits(
+ ospf_db_flags_bits, ospf->ospf6_db.db_flags));
+ snprintf(get_line(0, 0), get_line_remain(),
+ "MTU = %u", ntohl(ospf->ospf6_db.db_seq));
+ snprintf(get_line(0, 0), get_line_remain(),
+ "Sequence = 0x%X", ntohl(ospf->ospf6_db.db_seq));
+ /* Print all the LS advs */
+ lsah = ospf->ospf6_db.db_lshdr;
+ while ((uchar_t *)lsah < ((uchar_t *)ospf + fraglen)) {
+ if ((uchar_t *)lsah + sizeof (struct lsa6_hdr) >
+ ((uchar_t *)ospf + fraglen)) {
+ trunc = B_TRUE;
+ break;
+ }
+ interpret_ospf6_lsa_hdr(flags, lsah);
+ ++lsah;
+ }
+ }
+ break;
+
+ case OSPF_TYPE_LSR:
+ if (fraglen < OSPF6_MIN_HEADER_SIZE +
+ OSPF_MIN_LSR_HEADER_SIZE) {
+ trunc = B_TRUE;
+ break;
+ }
+ if (flags & F_DTAIL) {
+ show_header("OSPF LSR: ", "Link State Request Packet",
+ fraglen);
+ show_space();
+ }
+ lsr = ospf->ospf6_lsr;
+ nlsah = 0;
+ while ((uchar_t *)lsr < ((uchar_t *)ospf + fraglen)) {
+ if ((uchar_t *)lsr + sizeof (struct lsr6) >
+ ((uchar_t *)ospf + fraglen)) {
+ trunc = B_TRUE;
+ break;
+ }
+ nlsah++;
+ if (flags & F_DTAIL) {
+ ospf6_print_ls_type(flags, ntohl(lsr->ls_type),
+ lsr->ls_stateid, lsr->ls_router);
+ }
+ ++lsr;
+ }
+ if (flags & F_SUM) {
+ sprintf(sum_line, "%d LSAs", nlsah);
+ sum_line += strlen(sum_line);
+ }
+ break;
+
+ case OSPF_TYPE_LSU:
+ if (fraglen < OSPF6_MIN_HEADER_SIZE +
+ OSPF_MIN_LSU_HEADER_SIZE) {
+ trunc = B_TRUE;
+ break;
+ }
+ if (flags & F_DTAIL) {
+ show_header("OSPF LSU: ", "Link State Update Packet",
+ fraglen);
+ show_space();
+ }
+ lsa = ospf->ospf6_lsu.lsu_lsa;
+ nlsa = ntohl(ospf->ospf6_lsu.lsu_count);
+ if (flags & F_SUM) {
+ sprintf(sum_line, "%d LSAs", nlsa);
+ sum_line += strlen(sum_line);
+ break;
+ }
+ while (nlsa-- != 0) {
+ uchar_t *fragend = (uchar_t *)ospf + fraglen;
+ if (((uchar_t *)lsa >= fragend) ||
+ ((uchar_t *)lsa + sizeof (struct lsa_hdr) >
+ fragend) ||
+ ((uchar_t *)lsa + ntohs(lsa->ls6_hdr.ls6_length) >
+ fragend)) {
+ trunc = B_TRUE;
+ break;
+ }
+
+ if (interpret_ospf6_lsa(flags, lsa, fragend) < 0) {
+ trunc = B_TRUE;
+ break;
+ }
+ lsa = (struct lsa6 *)((uchar_t *)lsa +
+ ntohs(lsa->ls6_hdr.ls6_length));
+ }
+ break;
+
+ case OSPF_TYPE_LSA:
+ if (flags & F_DTAIL) {
+ show_header("OSPF LSA: ", "Link State Ack Packet",
+ fraglen);
+ show_space();
+ }
+ lsah = ospf->ospf6_lsa.lsa_lshdr;
+ nlsah = 0;
+ while ((uchar_t *)lsah < ((uchar_t *)ospf + fraglen)) {
+ if ((uchar_t *)lsah + sizeof (struct lsa6_hdr) >
+ ((uchar_t *)ospf + fraglen)) {
+ trunc = B_TRUE;
+ break;
+ }
+ nlsah++;
+ if (flags & F_DTAIL)
+ interpret_ospf6_lsa_hdr(flags, lsah);
+ ++lsah;
+ }
+ if (flags & F_SUM) {
+ sprintf(sum_line, "%d LSAs", nlsah);
+ sum_line += strlen(sum_line);
+ }
+ break;
+
+ default:
+ /* NOTREACHED */
+ break;
+ }
+ if (trunc) {
+ if (flags & F_SUM)
+ sprintf(sum_line, "--truncated");
+ if (flags & F_DTAIL)
+ snprintf(get_line(0, 0), get_line_remain(),
+ "--truncated");
+ }
+
+ return (fraglen);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ospf6.h b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ospf6.h
new file mode 100644
index 0000000000..2c71fca89c
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ospf6.h
@@ -0,0 +1,185 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _OSPF6_H
+#define _OSPF6_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Definitions for parsing OSPF packets (RFC 2328)
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct lsa6_hdr {
+ uint16_t ls6_age;
+ uint16_t ls6_type;
+ uint32_t ls6_stateid;
+ uint32_t ls6_router;
+ uint32_t ls6_seq;
+ uint16_t ls6_chksum;
+ uint16_t ls6_length;
+};
+
+struct lsa6_prefix {
+ uint8_t lsa6_plen;
+ uint8_t lsa6_popt;
+ uint16_t lsa6_pmbz;
+ uint8_t lsa6_pfx[4];
+};
+
+/* link state advertisement */
+struct lsa6 {
+ struct lsa6_hdr ls6_hdr;
+
+ /* Link state types */
+ union {
+ /* Router links advertisements */
+ struct {
+ union {
+ uint8_t rla_flg;
+ uint32_t rla_opt;
+ } un_rla_flgopt;
+#define rla6_flags un_rla_flgopt.rla_flg
+#define rla6_options un_rla_flgopt.rla_opt
+ struct rla6link {
+ uint8_t link_type;
+ uint8_t link_zero[1];
+ uint16_t link_metric;
+ uint32_t link_ifid;
+ uint32_t link_nifid;
+ uint32_t link_nrtid;
+ } rla_link[1]; /* may repeat */
+ } un_rla;
+
+ /* Network links advertisements */
+ struct {
+ uint32_t nla_options;
+ uint32_t nla_router[1]; /* may repeat */
+ } un_nla;
+
+ /* Inter Area Prefix LSA */
+ struct {
+ uint32_t inter_ap_metric;
+ struct lsa6_prefix inter_ap_prefix[1];
+ } un_inter_ap;
+
+ /* Link LSA */
+ struct llsa {
+ union {
+ uint8_t pri;
+ uint32_t opt;
+ } llsa_priandopt;
+#define llsa_priority llsa_priandopt.pri
+#define llsa_options llsa_priandopt.opt
+ struct in6_addr llsa_lladdr;
+ uint32_t llsa_nprefix;
+ struct lsa6_prefix llsa_prefix[1];
+ } un_llsa;
+
+ /* Intra-Area-Prefix */
+ struct {
+ uint16_t intra_ap_nprefix;
+ uint16_t intra_ap_lstype;
+ uint32_t intra_ap_lsid;
+ uint32_t intra_ap_rtid;
+ struct lsa6_prefix intra_ap_prefix[1];
+ } un_intra_ap;
+ } lsa_un;
+};
+
+struct ospf6hdr {
+ uint8_t ospf6_version;
+ uint8_t ospf6_type;
+ uint16_t ospf6_len;
+ uint32_t ospf6_routerid;
+ uint32_t ospf6_areaid;
+ uint16_t ospf6_chksum;
+ uint8_t ospf6_instanceid;
+ uint8_t ospf6_rsvd;
+ union {
+
+ /* Hello packet */
+ struct {
+ uint32_t hello_ifid;
+ union {
+ uint8_t pri;
+ uint32_t opt;
+ } hello_priandopt;
+#define hello6_priority hello_priandopt.pri
+#define hello6_options hello_priandopt.opt
+ uint16_t hello_helloint;
+ uint16_t hello_deadint;
+ uint32_t hello_dr;
+ uint32_t hello_bdr;
+ uint32_t hello_neighbor[1]; /* may repeat */
+ } un_hello;
+
+ /* Database Description packet */
+ struct {
+ uint32_t db_options;
+ uint16_t db_mtu;
+ uint8_t db_mbz;
+ uint8_t db_flags;
+ uint32_t db_seq;
+ struct lsa6_hdr db_lshdr[1]; /* may repeat */
+ } un_db;
+
+ /* Link State Request */
+ struct lsr6 {
+ uint16_t ls_mbz;
+ uint16_t ls_type;
+ uint32_t ls_stateid;
+ uint32_t ls_router;
+ } un_lsr[1]; /* may repeat */
+
+ /* Link State Update */
+ struct {
+ uint32_t lsu_count;
+ struct lsa6 lsu_lsa[1]; /* may repeat */
+ } un_lsu;
+
+ /* Link State Acknowledgement */
+ struct {
+ struct lsa6_hdr lsa_lshdr[1]; /* may repeat */
+ } un_lsa;
+ } ospf6_un;
+};
+
+#define ospf6_hello ospf6_un.un_hello
+#define ospf6_db ospf6_un.un_db
+#define ospf6_lsr ospf6_un.un_lsr
+#define ospf6_lsu ospf6_un.un_lsu
+#define ospf6_lsa ospf6_un.un_lsa
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _OSPF6_H */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_pf.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_pf.c
new file mode 100644
index 0000000000..8ef09a2fc9
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_pf.c
@@ -0,0 +1,1197 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI" /* SunOS */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/isa_defs.h>
+
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/dlpi.h>
+#include <net/if.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/if_ether.h>
+#include <netinet/tcp.h>
+#include <netinet/udp.h>
+#include <netdb.h>
+#include <rpc/rpc.h>
+#include <setjmp.h>
+
+#include <sys/pfmod.h>
+#include "snoop.h"
+
+/*
+ * This module generates code for the kernel packet filter.
+ * The kernel packet filter is more efficient since it
+ * operates without context switching or moving data into
+ * the capture buffer. On the other hand, it is limited
+ * in its filtering ability i.e. can't cope with variable
+ * length headers, can't compare the packet size, 1 and 4 octet
+ * comparisons are awkward, code space is limited to ENMAXFILTERS
+ * halfwords, etc.
+ * The parser is the same for the user-level packet filter though
+ * more limited in the variety of expressions it can generate
+ * code for. If the pf compiler finds an expression it can't
+ * handle, it tries to set up a split filter in kernel and do the
+ * remaining filtering in userland. If that also fails, it resorts
+ * to userland filter. (See additional comment in pf_compile)
+ */
+
+extern struct Pf_ext_packetfilt pf;
+static ushort_t *pfp;
+jmp_buf env;
+
+int eaddr; /* need ethernet addr */
+
+int opstack; /* operand stack depth */
+
+#define EQ(val) (strcmp(token, val) == 0)
+#define IPV4_ONLY 0
+#define IPV6_ONLY 1
+#define IPV4_AND_IPV6 2
+
+/*
+ * The following constants represent the offsets in bytes from the beginning
+ * of the packet of the link and IP(v6) layer source/destination/type fields,
+ * initialized for Ethernet. Media specific code can set any unavailable
+ * link layer property's offset to -1 to indicate that the property's value
+ * is not available from the frame.
+ */
+static int link_header_len = 14, link_type_offset = 12;
+static int link_dest_offset = 0, link_src_offset = 6;
+static int link_addr_len = 6;
+
+#define IPV4_SRCADDR_OFFSET (link_header_len + 12)
+#define IPV4_DSTADDR_OFFSET (link_header_len + 16)
+#define IPV6_SRCADDR_OFFSET (link_header_len + 8)
+#define IPV6_DSTADDR_OFFSET (link_header_len + 24)
+#define IPV4_TYPE_OFFSET (link_header_len + 9)
+#define IPV6_TYPE_OFFSET (link_header_len + 6)
+
+static int inBrace = 0, inBraceOR = 0;
+static int foundOR = 0;
+char *tkp, *sav_tkp;
+char *token;
+enum { EOL, ALPHA, NUMBER, FIELD, ADDR_IP, ADDR_ETHER, SPECIAL,
+ ADDR_IP6 } tokentype;
+uint_t tokenval;
+
+enum direction { ANY, TO, FROM };
+enum direction dir;
+
+extern void next();
+
+static void pf_expression();
+
+static void
+pf_emit(x)
+ ushort_t x;
+{
+ if (pfp > &pf.Pf_Filter[PF_MAXFILTERS - 1])
+ longjmp(env, 1);
+ *pfp++ = x;
+}
+
+static void
+pf_codeprint(code, len)
+ ushort_t *code;
+ int len;
+{
+ ushort_t *pc;
+ ushort_t *plast = code + len;
+ int op, action;
+
+ if (len > 0) {
+ printf("Kernel Filter:\n");
+ }
+
+ for (pc = code; pc < plast; pc++) {
+ printf("\t%3d: ", pc - code);
+
+ op = *pc & 0xfc00; /* high 10 bits */
+ action = *pc & 0x3ff; /* low 6 bits */
+
+ switch (action) {
+ case ENF_PUSHLIT:
+ printf("PUSHLIT ");
+ break;
+ case ENF_PUSHZERO:
+ printf("PUSHZERO ");
+ break;
+#ifdef ENF_PUSHONE
+ case ENF_PUSHONE:
+ printf("PUSHONE ");
+ break;
+#endif
+#ifdef ENF_PUSHFFFF
+ case ENF_PUSHFFFF:
+ printf("PUSHFFFF ");
+ break;
+#endif
+#ifdef ENF_PUSHFF00
+ case ENF_PUSHFF00:
+ printf("PUSHFF00 ");
+ break;
+#endif
+#ifdef ENF_PUSH00FF
+ case ENF_PUSH00FF:
+ printf("PUSH00FF ");
+ break;
+#endif
+ }
+
+ if (action >= ENF_PUSHWORD)
+ printf("PUSHWORD %d ", action - ENF_PUSHWORD);
+
+ switch (op) {
+ case ENF_EQ:
+ printf("EQ ");
+ break;
+ case ENF_LT:
+ printf("LT ");
+ break;
+ case ENF_LE:
+ printf("LE ");
+ break;
+ case ENF_GT:
+ printf("GT ");
+ break;
+ case ENF_GE:
+ printf("GE ");
+ break;
+ case ENF_AND:
+ printf("AND ");
+ break;
+ case ENF_OR:
+ printf("OR ");
+ break;
+ case ENF_XOR:
+ printf("XOR ");
+ break;
+ case ENF_COR:
+ printf("COR ");
+ break;
+ case ENF_CAND:
+ printf("CAND ");
+ break;
+ case ENF_CNOR:
+ printf("CNOR ");
+ break;
+ case ENF_CNAND:
+ printf("CNAND ");
+ break;
+ case ENF_NEQ:
+ printf("NEQ ");
+ break;
+ }
+
+ if (action == ENF_PUSHLIT) {
+ pc++;
+ printf("\n\t%3d: %d (0x%04x)", pc - code, *pc, *pc);
+ }
+
+ printf("\n");
+ }
+}
+
+/*
+ * Emit packet filter code to check a
+ * field in the packet for a particular value.
+ * Need different code for each field size.
+ * Since the pf can only compare 16 bit quantities
+ * we have to use masking to compare byte values.
+ * Long word (32 bit) quantities have to be done
+ * as two 16 bit comparisons.
+ */
+static void
+pf_compare_value(int offset, uint_t len, uint_t val)
+{
+ /*
+ * If the property being filtered on is absent in the media
+ * packet, error out.
+ */
+ if (offset == -1)
+ pr_err("filter option unsupported on media");
+
+ switch (len) {
+ case 1:
+ pf_emit(ENF_PUSHWORD + offset / 2);
+#if defined(_BIG_ENDIAN)
+ if (offset % 2)
+#else
+ if (!(offset % 2))
+#endif
+ {
+#ifdef ENF_PUSH00FF
+ pf_emit(ENF_PUSH00FF | ENF_AND);
+#else
+ pf_emit(ENF_PUSHLIT | ENF_AND);
+ pf_emit(0x00FF);
+#endif
+ pf_emit(ENF_PUSHLIT | ENF_EQ);
+ pf_emit(val);
+ } else {
+#ifdef ENF_PUSHFF00
+ pf_emit(ENF_PUSHFF00 | ENF_AND);
+#else
+ pf_emit(ENF_PUSHLIT | ENF_AND);
+ pf_emit(0xFF00);
+#endif
+ pf_emit(ENF_PUSHLIT | ENF_EQ);
+ pf_emit(val << 8);
+ }
+ break;
+
+ case 2:
+ pf_emit(ENF_PUSHWORD + offset / 2);
+ pf_emit(ENF_PUSHLIT | ENF_EQ);
+ pf_emit((ushort_t)val);
+ break;
+
+ case 4:
+ pf_emit(ENF_PUSHWORD + offset / 2);
+ pf_emit(ENF_PUSHLIT | ENF_EQ);
+#if defined(_BIG_ENDIAN)
+ pf_emit(val >> 16);
+#elif defined(_LITTLE_ENDIAN)
+ pf_emit(val & 0xffff);
+#else
+#error One of _BIG_ENDIAN and _LITTLE_ENDIAN must be defined
+#endif
+ pf_emit(ENF_PUSHWORD + (offset / 2) + 1);
+ pf_emit(ENF_PUSHLIT | ENF_EQ);
+#if defined(_BIG_ENDIAN)
+ pf_emit(val & 0xffff);
+#else
+ pf_emit(val >> 16);
+#endif
+ pf_emit(ENF_AND);
+ break;
+ }
+}
+
+/*
+ * same as pf_compare_value, but only for emiting code to
+ * compare ipv6 addresses.
+ */
+static void
+pf_compare_value_v6(int offset, uint_t len, struct in6_addr val)
+{
+ int i;
+
+ for (i = 0; i < len; i += 2) {
+ pf_emit(ENF_PUSHWORD + offset / 2 + i / 2);
+ pf_emit(ENF_PUSHLIT | ENF_EQ);
+ pf_emit(*(uint16_t *)&val.s6_addr[i]);
+ if (i != 0)
+ pf_emit(ENF_AND);
+ }
+}
+
+
+/*
+ * Same as above except mask the field value
+ * before doing the comparison.
+ */
+static void
+pf_compare_value_mask(int offset, uint_t len, uint_t val, int mask)
+{
+ /*
+ * If the property being filtered on is absent in the media
+ * packet, error out.
+ */
+ if (offset == -1)
+ pr_err("filter option unsupported on media");
+
+ switch (len) {
+ case 1:
+ pf_emit(ENF_PUSHWORD + offset / 2);
+#if defined(_BIG_ENDIAN)
+ if (offset % 2)
+#else
+ if (!offset % 2)
+#endif
+ {
+ pf_emit(ENF_PUSHLIT | ENF_AND);
+ pf_emit(mask & 0x00ff);
+ pf_emit(ENF_PUSHLIT | ENF_EQ);
+ pf_emit(val);
+ } else {
+ pf_emit(ENF_PUSHLIT | ENF_AND);
+ pf_emit((mask << 8) & 0xff00);
+ pf_emit(ENF_PUSHLIT | ENF_EQ);
+ pf_emit(val << 8);
+ }
+ break;
+
+ case 2:
+ pf_emit(ENF_PUSHWORD + offset / 2);
+ pf_emit(ENF_PUSHLIT | ENF_AND);
+ pf_emit(htons((ushort_t)mask));
+ pf_emit(ENF_PUSHLIT | ENF_EQ);
+ pf_emit(htons((ushort_t)val));
+ break;
+
+ case 4:
+ pf_emit(ENF_PUSHWORD + offset / 2);
+ pf_emit(ENF_PUSHLIT | ENF_AND);
+ pf_emit(htons((ushort_t)((mask >> 16) & 0xffff)));
+ pf_emit(ENF_PUSHLIT | ENF_EQ);
+ pf_emit(htons((ushort_t)((val >> 16) & 0xffff)));
+
+ pf_emit(ENF_PUSHWORD + (offset / 2) + 1);
+ pf_emit(ENF_PUSHLIT | ENF_AND);
+ pf_emit(htons((ushort_t)(mask & 0xffff)));
+ pf_emit(ENF_PUSHLIT | ENF_EQ);
+ pf_emit(htons((ushort_t)(val & 0xffff)));
+
+ pf_emit(ENF_AND);
+ break;
+ }
+}
+
+/*
+ * Generate pf code to match an IPv4 or IPv6 address.
+ */
+static void
+pf_ipaddr_match(which, hostname, inet_type)
+ enum direction which;
+ char *hostname;
+ int inet_type;
+{
+ bool_t found_host;
+ uint_t *addr4ptr;
+ uint_t addr4;
+ struct in6_addr *addr6ptr;
+ int h_addr_index;
+ struct hostent *hp = NULL;
+ int error_num = 0;
+ boolean_t first = B_TRUE;
+ int pass = 0;
+
+ /*
+ * The addr4offset and addr6offset variables simplify the code which
+ * generates the address comparison filter. With these two variables,
+ * duplicate code need not exist for the TO and FROM case.
+ * A value of -1 describes the ANY case (TO and FROM).
+ */
+ int addr4offset;
+ int addr6offset;
+
+ found_host = 0;
+
+ if (tokentype == ADDR_IP) {
+ hp = getipnodebyname(hostname, AF_INET, 0, &error_num);
+ if (hp == NULL) {
+ if (error_num == TRY_AGAIN) {
+ pr_err("could not resolve %s (try again later)",
+ hostname);
+ } else {
+ pr_err("could not resolve %s", hostname);
+ }
+ }
+ inet_type = IPV4_ONLY;
+ } else if (tokentype == ADDR_IP6) {
+ hp = getipnodebyname(hostname, AF_INET6, 0, &error_num);
+ if (hp == NULL) {
+ if (error_num == TRY_AGAIN) {
+ pr_err("could not resolve %s (try again later)",
+ hostname);
+ } else {
+ pr_err("could not resolve %s", hostname);
+ }
+ }
+ inet_type = IPV6_ONLY;
+ } else if (tokentype == ALPHA) {
+ /* Some hostname i.e. tokentype is ALPHA */
+ switch (inet_type) {
+ case IPV4_ONLY:
+ /* Only IPv4 address is needed */
+ hp = getipnodebyname(hostname, AF_INET, 0, &error_num);
+ if (hp != NULL) {
+ found_host = 1;
+ }
+ break;
+ case IPV6_ONLY:
+ /* Only IPv6 address is needed */
+ hp = getipnodebyname(hostname, AF_INET6, 0, &error_num);
+ if (hp != NULL) {
+ found_host = 1;
+ }
+ break;
+ case IPV4_AND_IPV6:
+ /* Both IPv4 and IPv6 are needed */
+ hp = getipnodebyname(hostname, AF_INET6,
+ AI_ALL | AI_V4MAPPED, &error_num);
+ if (hp != NULL) {
+ found_host = 1;
+ }
+ break;
+ default:
+ found_host = 0;
+ }
+
+ if (!found_host) {
+ if (error_num == TRY_AGAIN) {
+ pr_err("could not resolve %s (try again later)",
+ hostname);
+ } else {
+ pr_err("could not resolve %s", hostname);
+ }
+ }
+ } else {
+ pr_err("unknown token type: %s", hostname);
+ }
+
+ switch (which) {
+ case TO:
+ addr4offset = IPV4_DSTADDR_OFFSET;
+ addr6offset = IPV6_DSTADDR_OFFSET;
+ break;
+ case FROM:
+ addr4offset = IPV4_SRCADDR_OFFSET;
+ addr6offset = IPV6_SRCADDR_OFFSET;
+ break;
+ case ANY:
+ addr4offset = -1;
+ addr6offset = -1;
+ break;
+ }
+
+ if (hp != NULL && hp->h_addrtype == AF_INET) {
+ pf_compare_value(link_type_offset, 2, htons(ETHERTYPE_IP));
+ h_addr_index = 0;
+ addr4ptr = (uint_t *)hp->h_addr_list[h_addr_index];
+ while (addr4ptr != NULL) {
+ if (addr4offset == -1) {
+ pf_compare_value(IPV4_SRCADDR_OFFSET, 4,
+ *addr4ptr);
+ if (h_addr_index != 0)
+ pf_emit(ENF_OR);
+ pf_compare_value(IPV4_DSTADDR_OFFSET, 4,
+ *addr4ptr);
+ pf_emit(ENF_OR);
+ } else {
+ pf_compare_value(addr4offset, 4,
+ *addr4ptr);
+ if (h_addr_index != 0)
+ pf_emit(ENF_OR);
+ }
+ addr4ptr = (uint_t *)hp->h_addr_list[++h_addr_index];
+ }
+ pf_emit(ENF_AND);
+ } else {
+ /* first pass: IPv4 addresses */
+ h_addr_index = 0;
+ addr6ptr = (struct in6_addr *)hp->h_addr_list[h_addr_index];
+ first = B_TRUE;
+ while (addr6ptr != NULL) {
+ if (IN6_IS_ADDR_V4MAPPED(addr6ptr)) {
+ if (first) {
+ pf_compare_value(link_type_offset, 2,
+ htons(ETHERTYPE_IP));
+ pass++;
+ }
+ IN6_V4MAPPED_TO_INADDR(addr6ptr,
+ (struct in_addr *)&addr4);
+ if (addr4offset == -1) {
+ pf_compare_value(IPV4_SRCADDR_OFFSET, 4,
+ addr4);
+ if (!first)
+ pf_emit(ENF_OR);
+ pf_compare_value(IPV4_DSTADDR_OFFSET, 4,
+ addr4);
+ pf_emit(ENF_OR);
+ } else {
+ pf_compare_value(addr4offset, 4,
+ addr4);
+ if (!first)
+ pf_emit(ENF_OR);
+ }
+ if (first)
+ first = B_FALSE;
+ }
+ addr6ptr = (struct in6_addr *)
+ hp->h_addr_list[++h_addr_index];
+ }
+ if (!first) {
+ pf_emit(ENF_AND);
+ }
+ /* second pass: IPv6 addresses */
+ h_addr_index = 0;
+ addr6ptr = (struct in6_addr *)hp->h_addr_list[h_addr_index];
+ first = B_TRUE;
+ while (addr6ptr != NULL) {
+ if (!IN6_IS_ADDR_V4MAPPED(addr6ptr)) {
+ if (first) {
+ pf_compare_value(link_type_offset, 2,
+ htons(ETHERTYPE_IPV6));
+ pass++;
+ }
+ if (addr6offset == -1) {
+ pf_compare_value_v6(IPV6_SRCADDR_OFFSET,
+ 16, *addr6ptr);
+ if (!first)
+ pf_emit(ENF_OR);
+ pf_compare_value_v6(IPV6_DSTADDR_OFFSET,
+ 16, *addr6ptr);
+ pf_emit(ENF_OR);
+ } else {
+ pf_compare_value_v6(addr6offset, 16,
+ *addr6ptr);
+ if (!first)
+ pf_emit(ENF_OR);
+ }
+ if (first)
+ first = B_FALSE;
+ }
+ addr6ptr = (struct in6_addr *)
+ hp->h_addr_list[++h_addr_index];
+ }
+ if (!first) {
+ pf_emit(ENF_AND);
+ }
+ if (pass == 2) {
+ pf_emit(ENF_OR);
+ }
+ }
+
+ if (hp != NULL) {
+ freehostent(hp);
+ }
+}
+
+
+static void
+pf_compare_address(int offset, uint_t len, uchar_t *addr)
+{
+ uint32_t val;
+ uint16_t sval;
+ boolean_t didone = B_FALSE;
+
+ /*
+ * If the property being filtered on is absent in the media
+ * packet, error out.
+ */
+ if (offset == -1)
+ pr_err("filter option unsupported on media");
+
+ while (len > 0) {
+ if (len >= 4) {
+ (void) memcpy(&val, addr, 4);
+ pf_compare_value(offset, 4, val);
+ addr += 4;
+ offset += 4;
+ len -= 4;
+ } else if (len >= 2) {
+ (void) memcpy(&sval, addr, 2);
+ pf_compare_value(offset, 2, sval);
+ addr += 2;
+ offset += 2;
+ len -= 2;
+ } else {
+ pf_compare_value(offset++, 1, *addr++);
+ len--;
+ }
+ if (didone)
+ pf_emit(ENF_AND);
+ didone = B_TRUE;
+ }
+}
+
+/*
+ * Compare ethernet addresses.
+ */
+static void
+pf_etheraddr_match(which, hostname)
+ enum direction which;
+ char *hostname;
+{
+ struct ether_addr e, *ep = NULL;
+
+ if (isxdigit(*hostname))
+ ep = ether_aton(hostname);
+ if (ep == NULL) {
+ if (ether_hostton(hostname, &e))
+ if (!arp_for_ether(hostname, &e))
+ pr_err("cannot obtain ether addr for %s",
+ hostname);
+ ep = &e;
+ }
+
+ switch (which) {
+ case TO:
+ pf_compare_address(link_dest_offset, link_addr_len,
+ (uchar_t *)ep);
+ break;
+ case FROM:
+ pf_compare_address(link_src_offset, link_addr_len,
+ (uchar_t *)ep);
+ break;
+ case ANY:
+ pf_compare_address(link_dest_offset, link_addr_len,
+ (uchar_t *)ep);
+ pf_compare_address(link_src_offset, link_addr_len,
+ (uchar_t *)ep);
+ pf_emit(ENF_OR);
+ break;
+ }
+}
+
+/*
+ * Emit code to compare the network part of
+ * an IP address.
+ */
+static void
+pf_netaddr_match(which, netname)
+ enum direction which;
+ char *netname;
+{
+ uint_t addr;
+ uint_t mask = 0xff000000;
+ struct netent *np;
+
+ if (isdigit(*netname)) {
+ addr = inet_network(netname);
+ } else {
+ np = getnetbyname(netname);
+ if (np == NULL)
+ pr_err("net %s not known", netname);
+ addr = np->n_net;
+ }
+
+ /*
+ * Left justify the address and figure
+ * out a mask based on the supplied address.
+ * Set the mask according to the number of zero
+ * low-order bytes.
+ * Note: this works only for whole octet masks.
+ */
+ if (addr) {
+ while ((addr & ~mask) != 0) {
+ mask |= (mask >> 8);
+ }
+ }
+
+ switch (which) {
+ case TO:
+ pf_compare_value_mask(IPV4_DSTADDR_OFFSET, 4, addr, mask);
+ break;
+ case FROM:
+ pf_compare_value_mask(IPV4_SRCADDR_OFFSET, 4, addr, mask);
+ break;
+ case ANY:
+ pf_compare_value_mask(IPV4_SRCADDR_OFFSET, 4, addr, mask);
+ pf_compare_value_mask(IPV4_DSTADDR_OFFSET, 4, addr, mask);
+ pf_emit(ENF_OR);
+ break;
+ }
+}
+
+static void
+pf_primary()
+{
+ for (;;) {
+ if (tokentype == FIELD)
+ break;
+
+ if (EQ("ip")) {
+ pf_compare_value(link_type_offset, 2,
+ htons(ETHERTYPE_IP));
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("ip6")) {
+ pf_compare_value(link_type_offset, 2,
+ htons(ETHERTYPE_IPV6));
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("pppoe")) {
+ pf_compare_value(link_type_offset, 2,
+ htons(ETHERTYPE_PPPOED));
+ pf_compare_value(link_type_offset, 2,
+ htons(ETHERTYPE_PPPOES));
+ pf_emit(ENF_OR);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("pppoed")) {
+ pf_compare_value(link_type_offset, 2,
+ htons(ETHERTYPE_PPPOED));
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("pppoes")) {
+ pf_compare_value(link_type_offset, 2,
+ htons(ETHERTYPE_PPPOES));
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("arp")) {
+ pf_compare_value(link_type_offset, 2,
+ htons(ETHERTYPE_ARP));
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("rarp")) {
+ pf_compare_value(link_type_offset, 2,
+ htons(ETHERTYPE_REVARP));
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("tcp")) {
+ pf_compare_value(link_type_offset, 2,
+ htons(ETHERTYPE_IP));
+ pf_compare_value(IPV4_TYPE_OFFSET, 1, IPPROTO_TCP);
+ pf_emit(ENF_AND);
+ pf_compare_value(link_type_offset, 2,
+ htons(ETHERTYPE_IPV6));
+ pf_compare_value(IPV6_TYPE_OFFSET, 1, IPPROTO_TCP);
+ pf_emit(ENF_AND);
+ pf_emit(ENF_OR);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("udp")) {
+ pf_compare_value(link_type_offset, 2,
+ htons(ETHERTYPE_IP));
+ pf_compare_value(IPV4_TYPE_OFFSET, 1, IPPROTO_UDP);
+ pf_emit(ENF_AND);
+ pf_compare_value(link_type_offset, 2,
+ htons(ETHERTYPE_IPV6));
+ pf_compare_value(IPV6_TYPE_OFFSET, 1, IPPROTO_UDP);
+ pf_emit(ENF_AND);
+ pf_emit(ENF_OR);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("ospf")) {
+ pf_compare_value(link_type_offset, 2,
+ htons(ETHERTYPE_IP));
+ pf_compare_value(IPV4_TYPE_OFFSET, 1, IPPROTO_OSPF);
+ pf_emit(ENF_AND);
+ pf_compare_value(link_type_offset, 2,
+ htons(ETHERTYPE_IPV6));
+ pf_compare_value(IPV6_TYPE_OFFSET, 1, IPPROTO_OSPF);
+ pf_emit(ENF_AND);
+ pf_emit(ENF_OR);
+ opstack++;
+ next();
+ break;
+ }
+
+
+ if (EQ("sctp")) {
+ pf_compare_value(link_type_offset, 2,
+ htons(ETHERTYPE_IP));
+ pf_compare_value(IPV4_TYPE_OFFSET, 1, IPPROTO_SCTP);
+ pf_emit(ENF_AND);
+ pf_compare_value(link_type_offset, 2,
+ htons(ETHERTYPE_IPV6));
+ pf_compare_value(IPV6_TYPE_OFFSET, 1, IPPROTO_SCTP);
+ pf_emit(ENF_AND);
+ pf_emit(ENF_OR);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("icmp")) {
+ pf_compare_value(link_type_offset, 2,
+ htons(ETHERTYPE_IP));
+ pf_compare_value(IPV4_TYPE_OFFSET, 1, IPPROTO_ICMP);
+ pf_emit(ENF_AND);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("icmp6")) {
+ pf_compare_value(link_type_offset, 2,
+ htons(ETHERTYPE_IPV6));
+ pf_compare_value(IPV6_TYPE_OFFSET, 1, IPPROTO_ICMPV6);
+ pf_emit(ENF_AND);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("ip-in-ip")) {
+ pf_compare_value(link_type_offset, 2,
+ htons(ETHERTYPE_IP));
+ pf_compare_value(IPV4_TYPE_OFFSET, 1, IPPROTO_ENCAP);
+ pf_emit(ENF_AND);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("esp")) {
+ pf_compare_value(link_type_offset, 2,
+ htons(ETHERTYPE_IP));
+ pf_compare_value(IPV4_TYPE_OFFSET, 1, IPPROTO_ESP);
+ pf_emit(ENF_AND);
+ pf_compare_value(link_type_offset, 2,
+ htons(ETHERTYPE_IPV6));
+ pf_compare_value(IPV6_TYPE_OFFSET, 1, IPPROTO_ESP);
+ pf_emit(ENF_AND);
+ pf_emit(ENF_OR);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("ah")) {
+ pf_compare_value(link_type_offset, 2,
+ htons(ETHERTYPE_IP));
+ pf_compare_value(IPV4_TYPE_OFFSET, 1, IPPROTO_AH);
+ pf_emit(ENF_AND);
+ pf_compare_value(link_type_offset, 2,
+ htons(ETHERTYPE_IPV6));
+ pf_compare_value(IPV6_TYPE_OFFSET, 1, IPPROTO_AH);
+ pf_emit(ENF_AND);
+ pf_emit(ENF_OR);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("(")) {
+ inBrace++;
+ next();
+ pf_expression();
+ if (EQ(")")) {
+ if (inBrace)
+ inBraceOR--;
+ inBrace--;
+ next();
+ }
+ break;
+ }
+
+ if (EQ("to") || EQ("dst")) {
+ dir = TO;
+ next();
+ continue;
+ }
+
+ if (EQ("from") || EQ("src")) {
+ dir = FROM;
+ next();
+ continue;
+ }
+
+ if (EQ("ether")) {
+ eaddr = 1;
+ next();
+ continue;
+ }
+
+ if (EQ("inet")) {
+ next();
+ if (EQ("host"))
+ next();
+ if (tokentype != ALPHA && tokentype != ADDR_IP)
+ pr_err("host/IPv4 addr expected after inet");
+ pf_ipaddr_match(dir, token, IPV4_ONLY);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("inet6")) {
+ next();
+ if (EQ("host"))
+ next();
+ if (tokentype != ALPHA && tokentype != ADDR_IP6)
+ pr_err("host/IPv6 addr expected after inet6");
+ pf_ipaddr_match(dir, token, IPV6_ONLY);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("proto")) {
+ next();
+ if (tokentype != NUMBER)
+ pr_err("IP proto type expected");
+ pf_compare_value(IPV4_TYPE_OFFSET, 1, tokenval);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("broadcast")) {
+ pf_compare_value(link_dest_offset, 4, 0xffffffff);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("multicast")) {
+ pf_compare_value_mask(link_dest_offset, 1, 0x01, 0x01);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("ethertype")) {
+ next();
+ if (tokentype != NUMBER)
+ pr_err("ether type expected");
+ pf_compare_value(link_type_offset, 2, htons(tokenval));
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("net") || EQ("dstnet") || EQ("srcnet")) {
+ if (EQ("dstnet"))
+ dir = TO;
+ else if (EQ("srcnet"))
+ dir = FROM;
+ next();
+ pf_netaddr_match(dir, token);
+ dir = ANY;
+ opstack++;
+ next();
+ break;
+ }
+
+ /*
+ * Give up on anything that's obviously
+ * not a primary.
+ */
+ if (EQ("and") || EQ("or") ||
+ EQ("not") || EQ("decnet") || EQ("apple") ||
+ EQ("length") || EQ("less") || EQ("greater") ||
+ EQ("port") || EQ("srcport") || EQ("dstport") ||
+ EQ("rpc") || EQ("gateway") || EQ("nofrag") ||
+ EQ("bootp") || EQ("dhcp") || EQ("slp") || EQ("ldap")) {
+ break;
+ }
+
+ if (EQ("host") || EQ("between") ||
+ tokentype == ALPHA || /* assume its a hostname */
+ tokentype == ADDR_IP ||
+ tokentype == ADDR_IP6 ||
+ tokentype == ADDR_ETHER) {
+ if (EQ("host") || EQ("between"))
+ next();
+ if (eaddr || tokentype == ADDR_ETHER) {
+ pf_etheraddr_match(dir, token);
+ } else if (tokentype == ALPHA) {
+ pf_ipaddr_match(dir, token, IPV4_AND_IPV6);
+ } else if (tokentype == ADDR_IP) {
+ pf_ipaddr_match(dir, token, IPV4_ONLY);
+ } else {
+ pf_ipaddr_match(dir, token, IPV6_ONLY);
+ }
+ dir = ANY;
+ eaddr = 0;
+ opstack++;
+ next();
+ break;
+ }
+
+ break; /* unknown token */
+ }
+}
+
+static void
+pf_alternation()
+{
+ int s = opstack;
+
+ pf_primary();
+ for (;;) {
+ if (EQ("and"))
+ next();
+ pf_primary();
+ if (opstack != s + 2)
+ break;
+ pf_emit(ENF_AND);
+ opstack--;
+ }
+}
+
+static void
+pf_expression()
+{
+ pf_alternation();
+ while (EQ("or") || EQ(",")) {
+ if (inBrace)
+ inBraceOR++;
+ else
+ foundOR++;
+ next();
+ pf_alternation();
+ pf_emit(ENF_OR);
+ opstack--;
+ }
+}
+
+/*
+ * Attempt to compile the expression
+ * in the string "e". If we can generate
+ * pf code for it then return 1 - otherwise
+ * return 0 and leave it up to the user-level
+ * filter.
+ */
+int
+pf_compile(e, print)
+ char *e;
+ int print;
+{
+ char *argstr;
+ char *sav_str, *ptr, *sav_ptr;
+ int inBr = 0, aheadOR = 0;
+
+ argstr = strdup(e);
+ sav_str = e;
+ tkp = argstr;
+ dir = ANY;
+
+ pfp = &pf.Pf_Filter[0];
+ if (setjmp(env)) {
+ return (0);
+ }
+
+ /*
+ * Set media specific packet offsets that this code uses.
+ */
+ if (interface->mac_type == DL_IB) {
+ link_header_len = 4;
+ link_type_offset = 0;
+ link_dest_offset = link_src_offset = -1;
+ link_addr_len = 20;
+ }
+
+ next();
+ pf_expression();
+
+ if (tokentype != EOL) {
+ /*
+ * The idea here is to do as much filtering as possible in
+ * the kernel. So even if we find a token we don't understand,
+ * we try to see if we can still set up a portion of the filter
+ * in the kernel and use the userland filter to filter the
+ * remaining stuff. Obviously, if our filter expression is of
+ * type A AND B, we can filter A in kernel and then apply B
+ * to the packets that got through. The same is not true for
+ * a filter of type A OR B. We can't apply A first and then B
+ * on the packets filtered through A.
+ *
+ * (We need to keep track of the fact when we find an OR,
+ * and the fact that we are inside brackets when we find OR.
+ * The variable 'foundOR' tells us if there was an OR behind,
+ * 'inBraceOR' tells us if we found an OR before we could find
+ * the end brace i.e. ')', and variable 'aheadOR' checks if
+ * there is an OR in the expression ahead. if either of these
+ * cases become true, we can't split the filtering)
+ */
+
+ if (foundOR || inBraceOR) {
+ /* FORGET IN KERNEL FILTERING */
+ return (0);
+ } else {
+
+ /* CHECK IF NO OR AHEAD */
+ sav_ptr = (char *)((uintptr_t)sav_str +
+ (uintptr_t)sav_tkp -
+ (uintptr_t)argstr);
+ ptr = sav_ptr;
+ while (*ptr != '\0') {
+ switch (*ptr) {
+ case '(':
+ inBr++;
+ break;
+ case ')':
+ inBr--;
+ break;
+ case 'o':
+ case 'O':
+ if ((*(ptr + 1) == 'R' ||
+ *(ptr + 1) == 'r') && !inBr)
+ aheadOR = 1;
+ break;
+ case ',':
+ if (!inBr)
+ aheadOR = 1;
+ break;
+ }
+ ptr++;
+ }
+ if (!aheadOR) {
+ /* NO OR AHEAD, SPLIT UP THE FILTERING */
+ pf.Pf_FilterLen = pfp - &pf.Pf_Filter[0];
+ pf.Pf_Priority = 5;
+ if (print) {
+ pf_codeprint(&pf.Pf_Filter[0],
+ pf.Pf_FilterLen);
+ }
+ compile(sav_ptr, print);
+ return (2);
+ } else
+ return (0);
+ }
+ }
+
+ pf.Pf_FilterLen = pfp - &pf.Pf_Filter[0];
+ pf.Pf_Priority = 5; /* unimportant, so long as > 2 */
+ if (print) {
+ pf_codeprint(&pf.Pf_Filter[0], pf.Pf_FilterLen);
+ }
+ return (1);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_pmap.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_pmap.c
new file mode 100644
index 0000000000..fbb9778b5b
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_pmap.c
@@ -0,0 +1,755 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1991, 1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ident "%Z%%M% %I% %E% SMI" /* SunOS */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <setjmp.h>
+#include <sys/tiuser.h>
+#include <string.h>
+
+#include <rpc/types.h>
+#include <rpc/xdr.h>
+#include <rpc/auth.h>
+#include <rpc/clnt.h>
+#include <rpc/rpc_msg.h>
+#include <rpc/pmap_prot.h>
+#include "snoop.h"
+
+/*
+ * Number of bytes to display from a string (address, netid, etc.).
+ */
+#define MAXSTRINGLEN 64
+
+extern char *dlc_header;
+extern jmp_buf xdr_err;
+
+static void interpret_pmap_2(int, int, int, int, int, char *, int);
+static void interpret_pmap_4(int, int, int, int, int, char *, int);
+static void stash_callit(ulong_t, int, int, int, int);
+
+void
+interpret_pmap(flags, type, xid, vers, proc, data, len)
+ int flags, type, xid, vers, proc;
+ char *data;
+ int len;
+{
+ switch (vers) {
+ case 2: interpret_pmap_2(flags, type, xid, vers, proc, data, len);
+ break;
+
+ /* Version 3 is a subset of version 4 */
+ case 3:
+ case 4: interpret_pmap_4(flags, type, xid, vers, proc, data, len);
+ break;
+ }
+}
+
+void show_pmap();
+char *sum_pmaplist();
+void show_pmaplist();
+
+static char *procnames_short_2[] = {
+ "Null", /* 0 */
+ "SET", /* 1 */
+ "UNSET", /* 2 */
+ "GETPORT", /* 3 */
+ "DUMP", /* 4 */
+ "CALLIT", /* 5 */
+};
+
+static char *procnames_long_2[] = {
+ "Null procedure", /* 0 */
+ "Set port", /* 1 */
+ "Unset port", /* 2 */
+ "Get port number", /* 3 */
+ "Dump the mappings", /* 4 */
+ "Indirect call", /* 5 */
+};
+
+#define MAXPROC_2 5
+
+void
+interpret_pmap_2(flags, type, xid, vers, proc, data, len)
+ int flags, type, xid, vers, proc;
+ char *data;
+ int len;
+{
+ char *line;
+ unsigned port, proto;
+ unsigned iprog, ivers, iproc, ilen;
+ extern int pi_frame;
+ struct cache_struct *x, *find_callit();
+ int trailer_done = 0;
+
+ if (proc < 0 || proc > MAXPROC_2)
+ return;
+
+ if (proc == PMAPPROC_CALLIT) {
+ if (type == CALL) {
+ iprog = getxdr_u_long();
+ ivers = getxdr_u_long();
+ iproc = getxdr_u_long();
+ stash_callit(xid, pi_frame,
+ iprog, ivers, iproc);
+ } else {
+ x = find_callit(xid);
+ }
+ }
+
+ if (flags & F_SUM) {
+ if (setjmp(xdr_err)) {
+ return;
+ }
+
+ line = get_sum_line();
+
+ if (type == CALL) {
+ (void) sprintf(line,
+ "PORTMAP C %s",
+ procnames_short_2[proc]);
+ line += strlen(line);
+ switch (proc) {
+ case PMAPPROC_GETPORT:
+ iprog = getxdr_u_long();
+ ivers = getxdr_u_long();
+ proto = getxdr_u_long();
+ (void) sprintf(line,
+ " prog=%d (%s) vers=%d proto=%s",
+ iprog, nameof_prog(iprog),
+ ivers,
+ getproto(proto));
+ break;
+ case PMAPPROC_CALLIT:
+ (void) getxdr_u_long(); /* length */
+ (void) sprintf(line,
+ " prog=%s vers=%d proc=%d",
+ nameof_prog(iprog),
+ ivers, iproc);
+ data += 16; /* prog+ver+proc+len */
+ len -= 16;
+ protoprint(flags, type, xid,
+ iprog, ivers, iproc,
+ data, len);
+ break;
+ default:
+ break;
+ }
+ check_retransmit(line, xid);
+ } else {
+ (void) sprintf(line, "PORTMAP R %s ",
+ procnames_short_2[proc]);
+ line += strlen(line);
+ switch (proc) {
+ case PMAPPROC_GETPORT:
+ port = getxdr_u_long();
+ (void) sprintf(line, "port=%d",
+ port);
+ break;
+ case PMAPPROC_DUMP:
+ (void) sprintf(line, "%s",
+ sum_pmaplist());
+ break;
+ case PMAPPROC_CALLIT:
+ port = getxdr_u_long();
+ ilen = getxdr_u_long();
+ (void) sprintf(line, "port=%d len=%d",
+ port, ilen);
+ if (x != NULL) {
+ protoprint(flags, type, xid,
+ x->xid_prog,
+ x->xid_vers,
+ x->xid_proc,
+ data, len);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("PMAP: ", "Portmapper", len);
+ show_space();
+ if (setjmp(xdr_err)) {
+ return;
+ }
+ (void) sprintf(get_line(0, 0),
+ "Proc = %d (%s)",
+ proc, procnames_long_2[proc]);
+ if (type == CALL) {
+ switch (proc) {
+ case PMAPPROC_NULL:
+ case PMAPPROC_SET:
+ case PMAPPROC_UNSET:
+ break;
+ case PMAPPROC_GETPORT:
+ iprog = getxdr_u_long();
+ (void) sprintf(get_line(0, 0),
+ "Program = %d (%s)",
+ iprog, nameof_prog(iprog));
+ (void) showxdr_u_long("Version = %d");
+ proto = getxdr_u_long();
+ (void) sprintf(get_line(0, 0),
+ "Protocol = %d (%s)",
+ proto, getproto(proto));
+ break;
+ case PMAPPROC_DUMP:
+ break;
+ case PMAPPROC_CALLIT:
+ (void) sprintf(get_line(0, 0),
+ "Program = %d (%s)",
+ iprog, nameof_prog(iprog));
+ (void) sprintf(get_line(0, 0),
+ "Version = %d", ivers);
+ (void) sprintf(get_line(0, 0),
+ "Proc = %d", iproc);
+ (void) showxdr_u_long(
+ "Callit data = %d bytes");
+ show_trailer();
+ trailer_done = 1;
+ data += 16; /* prog+ver+proc+len */
+ len -= 16;
+ protoprint(flags, type, xid,
+ iprog, ivers, iproc,
+ data, len);
+ break;
+ }
+ } else {
+ switch (proc) {
+ case PMAPPROC_NULL:
+ case PMAPPROC_SET:
+ case PMAPPROC_UNSET:
+ break;
+ case PMAPPROC_GETPORT:
+ (void) showxdr_u_long("Port = %d");
+ break;
+ case PMAPPROC_DUMP:
+ show_pmaplist();
+ break;
+ case PMAPPROC_CALLIT:
+ (void) showxdr_u_long("Port = %d");
+ (void) showxdr_u_long("Length = %d bytes");
+ show_trailer();
+ trailer_done = 1;
+ if (x != NULL) {
+ protoprint(flags, type, xid,
+ x->xid_prog,
+ x->xid_vers,
+ x->xid_proc,
+ data, len);
+ }
+ break;
+ }
+ }
+ if (!trailer_done)
+ show_trailer();
+ }
+}
+
+char *
+sum_pmaplist()
+{
+ int maps = 0;
+ static char buff[16];
+
+ if (setjmp(xdr_err)) {
+ (void) sprintf(buff, "%d+ map(s) found", maps);
+ return (buff);
+ }
+
+ while (getxdr_u_long()) {
+ (void) getxdr_u_long(); /* program */
+ (void) getxdr_u_long(); /* version */
+ (void) getxdr_u_long(); /* protocol */
+ (void) getxdr_u_long(); /* port */
+ maps++;
+ }
+
+ (void) sprintf(buff, "%d map(s) found", maps);
+ return (buff);
+}
+
+void
+show_pmaplist()
+{
+ unsigned prog, vers, proto, port;
+ int maps = 0;
+
+ if (setjmp(xdr_err)) {
+ (void) sprintf(get_line(0, 0),
+ " %d+ maps. (Frame is incomplete)",
+ maps);
+ return;
+ }
+
+ (void) sprintf(get_line(0, 0),
+ " Program Version Protocol Port");
+
+ while (getxdr_u_long()) {
+ prog = getxdr_u_long();
+ vers = getxdr_u_long();
+ proto = getxdr_u_long();
+ port = getxdr_u_long();
+ (void) sprintf(get_line(0, 0),
+ "%8d%8d%9d%7d %s",
+ prog, vers, proto, port, nameof_prog(prog));
+ maps++;
+ }
+
+ (void) sprintf(get_line(0, 0), " %d maps", maps);
+}
+
+/*
+ * ******************************************
+ */
+char *sum_rpcblist();
+void show_rpcblist();
+char *sum_rpcb_entry_list();
+void show_rpcb_entry_list();
+
+static char *procnames_short_4[] = {
+ /*
+ * version 3 and 4 procs
+ */
+ "Null", /* 0 */
+ "SET", /* 1 */
+ "UNSET", /* 2 */
+ "GETADDR", /* 3 */
+ "DUMP", /* 4 */
+ "BCAST", /* 5 */
+ "GETTIME", /* 6 */
+ "UADDR2TADDR", /* 7 */
+ "TADDR2UADDR", /* 8 */
+ /*
+ * version 4 procs only
+ */
+ "GETVERSADDR", /* 9 */
+ "INDIRECT", /* 10 */
+ "GETADDRLIST", /* 11 */
+ "GETSTAT", /* 12 */
+};
+
+static char *procnames_long_4[] = {
+ /*
+ * version 3 and 4 procs
+ */
+ "Null procedure", /* 0 */
+ "Set address", /* 1 */
+ "Unset address", /* 2 */
+ "Get address", /* 3 */
+ "Dump the mappings", /* 4 */
+ "Broadcast call (no error)", /* 5 */
+ "Get the time", /* 6 */
+ "Universal to transport address", /* 7 */
+ "Transport to universal address", /* 8 */
+ /*
+ * version 4 procs only
+ */
+ "Get address of specific version", /* 9 */
+ "Indirect call (return error)", /* 10 */
+ "Return addresses of prog/vers", /* 11 */
+ "Get statistics", /* 12 */
+};
+
+#define MAXPROC_4 12
+#define RPCBPROC_NULL 0
+
+void
+interpret_pmap_4(flags, type, xid, vers, proc, data, len)
+ int flags, type, xid, vers, proc;
+ char *data;
+ int len;
+{
+ char *line;
+ unsigned prog, ver;
+ char buff1[MAXSTRINGLEN + 1];
+ int iprog, ivers, iproc, ilen;
+ extern int pi_frame;
+ struct cache_struct *x, *find_callit();
+ int trailer_done = 0;
+
+ if (proc < 0 || proc > MAXPROC_4)
+ return;
+
+ if (proc == RPCBPROC_BCAST) {
+ if (type == CALL) {
+ iprog = getxdr_u_long();
+ ivers = getxdr_u_long();
+ iproc = getxdr_u_long();
+ stash_callit(xid, pi_frame,
+ iprog, ivers, iproc);
+ } else {
+ x = find_callit(xid);
+ }
+ }
+
+ if (flags & F_SUM) {
+ if (setjmp(xdr_err)) {
+ return;
+ }
+
+ line = get_sum_line();
+
+ if (type == CALL) {
+ (void) sprintf(line,
+ "RPCBIND C %s",
+ procnames_short_4[proc]);
+ line += strlen(line);
+ switch (proc) {
+ case RPCBPROC_SET:
+ case RPCBPROC_UNSET:
+ case RPCBPROC_GETADDR:
+ case RPCBPROC_GETVERSADDR:
+ case RPCBPROC_GETADDRLIST:
+ prog = getxdr_u_long();
+ ver = getxdr_u_long();
+ (void) sprintf(line,
+ " prog=%d (%s) vers=%d",
+ prog, nameof_prog(prog),
+ ver);
+ break;
+ case RPCBPROC_BCAST:
+ case RPCBPROC_INDIRECT:
+ (void) getxdr_u_long(); /* length */
+ (void) sprintf(line,
+ " prog=%s vers=%d proc=%d",
+ nameof_prog(iprog),
+ ivers, iproc);
+ data += 16; /* prog+ver+proc+len */
+ len -= 16;
+ protoprint(flags, type, xid,
+ iprog, ivers, iproc,
+ data, len);
+ break;
+ default:
+ break;
+ }
+
+ check_retransmit(line, xid);
+ } else {
+ (void) sprintf(line, "RPCBIND R %s ",
+ procnames_short_4[proc]);
+ line += strlen(line);
+ switch (proc) {
+ case RPCBPROC_GETADDR:
+ case RPCBPROC_TADDR2UADDR:
+ case RPCBPROC_GETVERSADDR:
+ (void) getxdr_string(buff1, MAXSTRINGLEN);
+ (void) sprintf(line,
+ " Uaddr=%s",
+ buff1);
+ break;
+ case RPCBPROC_BCAST:
+ case RPCBPROC_INDIRECT:
+ (void) getxdr_string(buff1, MAXSTRINGLEN);
+ ilen = getxdr_u_long();
+ (void) sprintf(line, "Uaddr=%s len=%d",
+ buff1, ilen);
+ data += 16; /* prog+ver+proc+len */
+ len -= 16;
+ if (x != NULL) {
+ protoprint(flags, type, xid,
+ x->xid_prog,
+ x->xid_vers,
+ x->xid_proc,
+ data, len);
+ }
+ break;
+ case RPCBPROC_DUMP:
+ (void) sprintf(line, "%s",
+ sum_rpcblist());
+ break;
+ case RPCBPROC_GETTIME:
+ (void) sprintf(line, "%s",
+ getxdr_date());
+ break;
+ case RPCBPROC_GETADDRLIST:
+ (void) sprintf(line, "%s",
+ sum_rpcb_entry_list());
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("RPCB: ", "RPC Bind", len);
+ show_space();
+ if (setjmp(xdr_err)) {
+ return;
+ }
+ (void) sprintf(get_line(0, 0),
+ "Proc = %d (%s)",
+ proc, procnames_long_4[proc]);
+ if (type == CALL) {
+ switch (proc) {
+ case RPCBPROC_NULL:
+ break;
+ case RPCBPROC_SET:
+ case RPCBPROC_UNSET:
+ case RPCBPROC_GETADDR:
+ case RPCBPROC_GETVERSADDR:
+ case RPCBPROC_GETADDRLIST:
+ (void) showxdr_u_long("Program = %d");
+ (void) showxdr_u_long("Version = %d");
+ (void) showxdr_string(64, "Netid = %s");
+ break;
+ case RPCBPROC_DUMP:
+ break;
+ case RPCBPROC_BCAST:
+ case RPCBPROC_INDIRECT:
+ (void) sprintf(get_line(0, 0),
+ "Program = %d (%s)",
+ iprog, nameof_prog(iprog));
+ (void) sprintf(get_line(0, 0),
+ "Version = %d", ivers);
+ (void) sprintf(get_line(0, 0),
+ "Proc = %d", iproc);
+ (void) showxdr_u_long(
+ "Callit data = %d bytes");
+ show_trailer();
+ trailer_done = 1;
+ data += 16; /* prog+ver+proc+len */
+ len -= 16;
+ protoprint(flags, type, xid,
+ iprog, ivers, iproc,
+ data, len);
+ break;
+ case RPCBPROC_GETTIME:
+ break;
+ case RPCBPROC_UADDR2TADDR:
+ case RPCBPROC_TADDR2UADDR:
+ break;
+ }
+ } else {
+ switch (proc) {
+ case RPCBPROC_NULL:
+ case RPCBPROC_SET:
+ case RPCBPROC_UNSET:
+ break;
+ case RPCBPROC_GETADDR:
+ case RPCBPROC_TADDR2UADDR:
+ case RPCBPROC_GETVERSADDR:
+ (void) showxdr_string(64, "Uaddr = %s");
+ break;
+ case RPCBPROC_DUMP:
+ show_rpcblist();
+ break;
+ case RPCBPROC_BCAST:
+ case RPCBPROC_INDIRECT:
+ (void) showxdr_string(64, "Uaddr = %s");
+ (void) showxdr_u_long("Length = %d bytes");
+ show_trailer();
+ trailer_done = 1;
+ if (x != NULL) {
+ protoprint(flags, type, xid,
+ x->xid_prog,
+ x->xid_vers,
+ x->xid_proc,
+ data, len);
+ }
+ break;
+ case RPCBPROC_GETTIME:
+ (void) showxdr_date("Time = %s");
+ break;
+ case RPCBPROC_UADDR2TADDR:
+ break;
+ case RPCBPROC_GETADDRLIST:
+ show_rpcb_entry_list();
+ break;
+ }
+ }
+ if (!trailer_done)
+ show_trailer();
+ }
+}
+
+char *
+sum_rpcblist()
+{
+ int maps = 0;
+ static char buff[MAXSTRINGLEN + 1];
+
+ if (setjmp(xdr_err)) {
+ (void) sprintf(buff, "%d+ map(s) found", maps);
+ return (buff);
+ }
+
+ while (getxdr_u_long()) {
+ (void) getxdr_u_long(); /* program */
+ (void) getxdr_u_long(); /* version */
+ (void) getxdr_string(buff, MAXSTRINGLEN); /* netid */
+ (void) getxdr_string(buff, MAXSTRINGLEN); /* uaddr */
+ (void) getxdr_string(buff, MAXSTRINGLEN); /* owner */
+ maps++;
+ }
+
+ (void) sprintf(buff, "%d map(s) found", maps);
+ return (buff);
+}
+
+void
+show_rpcblist()
+{
+ unsigned prog, vers;
+ char netid[MAXSTRINGLEN + 1], uaddr[MAXSTRINGLEN + 1];
+ char owner[MAXSTRINGLEN + 1];
+ int maps = 0;
+
+ if (setjmp(xdr_err)) {
+ (void) sprintf(get_line(0, 0),
+ " %d+ maps. (Frame is incomplete)",
+ maps);
+ return;
+ }
+
+ show_space();
+ (void) sprintf(get_line(0, 0),
+ " Program Vers Netid Uaddr Owner");
+
+ while (getxdr_u_long()) {
+ prog = getxdr_u_long();
+ vers = getxdr_u_long();
+ (void) getxdr_string(netid, MAXSTRINGLEN);
+ (void) getxdr_string(uaddr, MAXSTRINGLEN);
+ (void) getxdr_string(owner, MAXSTRINGLEN);
+ (void) sprintf(get_line(0, 0),
+ "%8d%5d %-12s %-18s %-10s (%s)",
+ prog, vers,
+ netid, uaddr, owner,
+ nameof_prog(prog));
+ maps++;
+ }
+
+ (void) sprintf(get_line(0, 0), " (%d maps)", maps);
+}
+
+char *
+sum_rpcb_entry_list()
+{
+ int maps = 0;
+ static char buff[MAXSTRINGLEN + 1];
+
+ if (setjmp(xdr_err)) {
+ (void) sprintf(buff, "%d+ map(s) found", maps);
+ return (buff);
+ }
+
+ while (getxdr_u_long()) {
+ (void) getxdr_string(buff, MAXSTRINGLEN); /* maddr */
+ (void) getxdr_string(buff, MAXSTRINGLEN); /* nc_netid */
+ (void) getxdr_u_long(); /* nc_semantics */
+ (void) getxdr_string(buff, MAXSTRINGLEN); /* nc_protofmly */
+ (void) getxdr_string(buff, MAXSTRINGLEN); /* nc_proto */
+ maps++;
+ }
+
+ (void) sprintf(buff, "%d map(s) found", maps);
+ return (buff);
+}
+
+char *semantics_strs[] = {"", "CLTS", "COTS", "COTS-ORD", "RAW"};
+
+void
+show_rpcb_entry_list()
+{
+ char maddr[MAXSTRINGLEN + 1], netid[MAXSTRINGLEN + 1];
+ char protofmly[MAXSTRINGLEN + 1], proto[MAXSTRINGLEN + 1];
+ unsigned sem;
+ int maps = 0;
+
+ if (setjmp(xdr_err)) {
+ (void) sprintf(get_line(0, 0),
+ " %d+ maps. (Frame is incomplete)",
+ maps);
+ return;
+ }
+
+ show_space();
+ (void) sprintf(get_line(0, 0),
+ " Maddr Netid Semantics Protofmly Proto");
+
+ while (getxdr_u_long()) {
+ (void) getxdr_string(maddr, MAXSTRINGLEN);
+ (void) getxdr_string(netid, MAXSTRINGLEN);
+ sem = getxdr_u_long();
+ (void) getxdr_string(protofmly, MAXSTRINGLEN);
+ (void) getxdr_string(proto, MAXSTRINGLEN);
+ (void) sprintf(get_line(0, 0),
+ "%-12s %-12s %-8s %-8s %-8s",
+ maddr, netid,
+ semantics_strs[sem],
+ protofmly, proto);
+ maps++;
+ }
+
+ (void) sprintf(get_line(0, 0), " (%d maps)", maps);
+}
+
+#define CXID_CACHE_SIZE 16
+struct cache_struct cxid_cache[CXID_CACHE_SIZE];
+struct cache_struct *cxcpfirst = &cxid_cache[0];
+struct cache_struct *cxcp = &cxid_cache[0];
+struct cache_struct *cxcplast = &cxid_cache[CXID_CACHE_SIZE - 1];
+
+struct cache_struct *
+find_callit(xid)
+ ulong_t xid;
+{
+ struct cache_struct *x;
+
+ for (x = cxcp; x >= cxcpfirst; x--)
+ if (x->xid_num == xid)
+ return (x);
+ for (x = cxcplast; x > cxcp; x--)
+ if (x->xid_num == xid)
+ return (x);
+ return (NULL);
+}
+
+static void
+stash_callit(xid, frame, prog, vers, proc)
+ ulong_t xid;
+ int frame, prog, vers, proc;
+{
+ struct cache_struct *x;
+
+ x = find_callit(xid);
+ if (x == NULL) {
+ x = cxcp++;
+ if (cxcp > cxcplast)
+ cxcp = cxcpfirst;
+ x->xid_num = xid;
+ x->xid_frame = frame;
+ }
+ x->xid_prog = prog;
+ x->xid_vers = vers;
+ x->xid_proc = proc;
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ppp.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ppp.c
new file mode 100644
index 0000000000..a056abd245
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ppp.c
@@ -0,0 +1,1946 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2001-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/sysmacros.h>
+#include <net/ppp_defs.h>
+#include <net/ppp-comp.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <arpa/inet.h>
+#include "snoop.h"
+#include "snoop_ppp.h"
+
+static int interpret_ppp_cp(int, uchar_t *, int, ppp_protoinfo_t *);
+static int interpret_cp_options(uchar_t *, int, ppp_protoinfo_t *);
+static int interpret_ppp_chap(int, uchar_t *, int, ppp_protoinfo_t *);
+static int interpret_ppp_pap(int, uchar_t *, int, ppp_protoinfo_t *);
+static int interpret_ppp_lqr(int, uchar_t *, int, ppp_protoinfo_t *);
+static ppp_protoinfo_t *ppp_getprotoinfo(uint16_t);
+static cp_optinfo_t *ppp_getoptinfo(cp_optinfo_t *, uint16_t);
+static optformat_func_t opt_format_vendor;
+static optformat_func_t opt_format_mru;
+static optformat_func_t opt_format_accm;
+static optformat_func_t opt_format_authproto;
+static optformat_func_t opt_format_qualproto;
+static optformat_func_t opt_format_magicnum;
+static optformat_func_t opt_format_fcs;
+static optformat_func_t opt_format_sdp;
+static optformat_func_t opt_format_nummode;
+static optformat_func_t opt_format_callback;
+static optformat_func_t opt_format_mrru;
+static optformat_func_t opt_format_epdisc;
+static optformat_func_t opt_format_dce;
+static optformat_func_t opt_format_linkdisc;
+static optformat_func_t opt_format_i18n;
+static optformat_func_t opt_format_ipaddresses;
+static optformat_func_t opt_format_ipcompproto;
+static optformat_func_t opt_format_ipaddress;
+static optformat_func_t opt_format_mobileipv4;
+static optformat_func_t opt_format_ifaceid;
+static optformat_func_t opt_format_ipv6compproto;
+static optformat_func_t opt_format_compoui;
+static optformat_func_t opt_format_bsdcomp;
+static optformat_func_t opt_format_staclzs;
+static optformat_func_t opt_format_mppc;
+static optformat_func_t opt_format_gandalf;
+static optformat_func_t opt_format_lzsdcp;
+static optformat_func_t opt_format_magnalink;
+static optformat_func_t opt_format_deflate;
+static optformat_func_t opt_format_encroui;
+static optformat_func_t opt_format_dese;
+static optformat_func_t opt_format_muxpid;
+
+/*
+ * Many strings below are initialized with "Unknown".
+ */
+static char unknown_string[] = "Unknown";
+
+/*
+ * Each known PPP protocol has an associated ppp_protoinfo_t in this array.
+ * Even if we can't decode the protocol (interpret_proto() == NULL),
+ * interpret_ppp() will at least print the protocol's name. There is no
+ * dependency on the ordering of the entries in this array. They have been
+ * ordered such that the most commonly used protocols are near the front.
+ * The array is delimited by a last entry of protocol of type
+ * PPP_PROTO_UNKNOWN.
+ */
+static ppp_protoinfo_t protoinfo_array[] = {
+ { PPP_IP, "IP", interpret_ip, NULL, NULL },
+ { PPP_IPV6, "IPv6", interpret_ipv6, NULL, NULL },
+ { PPP_COMP, "Compressed Data", NULL, NULL, NULL },
+ { PPP_OSI, "OSI", NULL, NULL, NULL },
+ { PPP_AT, "AppleTalk", NULL, NULL, NULL },
+ { PPP_IPX, "IPX", NULL, NULL, NULL },
+ { PPP_VJC_COMP, "VJ Compressed TCP", NULL, NULL, NULL },
+ { PPP_VJC_UNCOMP, "VJ Uncompressed TCP", NULL, NULL, NULL },
+ { PPP_BRIDGE, "Bridging", NULL, NULL, NULL },
+ { PPP_802HELLO, "802.1d Hello", NULL, NULL, NULL },
+ { PPP_MP, "MP", NULL, NULL, NULL },
+ { PPP_ENCRYPT, "Encryption", NULL, NULL, NULL },
+ { PPP_ENCRYPTFRAG, "Individual Link Encryption", NULL, NULL, NULL },
+ { PPP_MUX, "PPP Muxing", NULL, NULL, NULL },
+ { PPP_COMPFRAG, "Single Link Compressed Data", NULL, NULL, NULL },
+ { PPP_FULLHDR, "IP Compression", NULL, NULL, NULL },
+ { PPP_COMPTCP, "IP Compression", NULL, NULL, NULL },
+ { PPP_COMPNONTCP, "IP Compression", NULL, NULL, NULL },
+ { PPP_COMPUDP8, "IP Compression", NULL, NULL, NULL },
+ { PPP_COMPRTP8, "IP Compression", NULL, NULL, NULL },
+ { PPP_COMPTCPND, "IP Compression", NULL, NULL, NULL },
+ { PPP_COMPSTATE, "IP Compression", NULL, NULL, NULL },
+ { PPP_COMPUDP16, "IP Compression", NULL, NULL, NULL },
+ { PPP_COMPRTP16, "IP Compression", NULL, NULL, NULL },
+ { PPP_MPLS, "MPLS", NULL, NULL, NULL },
+ { PPP_MPLSMC, "MPLS M/C", NULL, NULL, NULL },
+ { PPP_LQR, "LQR", interpret_ppp_lqr, "PPP-LQR: ",
+ "Link Quality Report" },
+ { PPP_LCP, "LCP", interpret_ppp_cp, "PPP-LCP: ",
+ "Link Control Protocol" },
+ { PPP_IPCP, "IPCP", interpret_ppp_cp, "PPP-IPCP: ",
+ "IP Control Protocol" },
+ { PPP_IPV6CP, "IPV6CP", interpret_ppp_cp, "PPP-IPV6CP: ",
+ "IPv6 Control Protocol" },
+ { PPP_CCP, "CCP", interpret_ppp_cp, "PPP-CCP: ",
+ "Compression Control Protocol" },
+ { PPP_CCPFRAG, "CCP-Link", interpret_ppp_cp, "PPP-CCP-Link: ",
+ "Per-Link Compression Control Protocol" },
+ { PPP_ECP, "ECP", interpret_ppp_cp, "PPP-ECP: ",
+ "Encryption Control Protocol" },
+ { PPP_ECPFRAG, "ECP-Link", interpret_ppp_cp, "PPP-ECP-Link: ",
+ "Per-Link Encryption Control Protocol" },
+ { PPP_MPLSCP, "MPLSCP", NULL, NULL, NULL },
+ { PPP_OSINLCP, "OSINLCP", NULL, NULL, NULL },
+ { PPP_ATCP, "ATCP", NULL, NULL, NULL },
+ { PPP_IPXCP, "IPXCP", NULL, NULL, NULL },
+ { PPP_BACP, "BACP", NULL, NULL, NULL },
+ { PPP_BCP, "BCP", NULL, NULL, NULL },
+ { PPP_CBCP, "CBCP", NULL, NULL, NULL },
+ { PPP_BAP, "BAP", NULL, NULL, NULL },
+ { PPP_CHAP, "CHAP", interpret_ppp_chap, "CHAP: ",
+ "Challenge Handshake Authentication Protocl" },
+ { PPP_PAP, "PAP", interpret_ppp_pap, "PAP: ",
+ "Password Authentication Protocol" },
+ { PPP_EAP, "EAP", NULL, NULL, NULL },
+ { 0, unknown_string, NULL, NULL, NULL }
+};
+
+static cp_optinfo_t lcp_optinfo[] = {
+ { OPT_LCP_VENDOR, "Vendor-Specific", 6,
+ opt_format_vendor },
+ { OPT_LCP_MRU, "Maximum-Receive-Unit", 4,
+ opt_format_mru },
+ { OPT_LCP_ASYNCMAP, "Async-Control-Character-Map", 6,
+ opt_format_accm },
+ { OPT_LCP_AUTHTYPE, "Authentication-Protocol", 4,
+ opt_format_authproto },
+ { OPT_LCP_QUALITY, "Quality-Protocol", 4,
+ opt_format_qualproto },
+ { OPT_LCP_MAGICNUMBER, "Magic-Number", 6,
+ opt_format_magicnum },
+ { OPT_LCP_PCOMPRESSION, "Protocol-Field-Compression", 2, NULL },
+ { OPT_LCP_ACCOMPRESSION, "Address-and-Control-Field-Compression", 2,
+ NULL },
+ { OPT_LCP_FCSALTERN, "FCS-Alternative", 3,
+ opt_format_fcs },
+ { OPT_LCP_SELFDESCPAD, "Self-Describing-Padding", 3,
+ opt_format_sdp },
+ { OPT_LCP_NUMBERED, "Numbered-Mode", 3,
+ opt_format_nummode },
+ { OPT_LCP_MULTILINKPROC, "Multi-Link-Procedure", 2, NULL },
+ { OPT_LCP_CALLBACK, "Callback", 3,
+ opt_format_callback },
+ { OPT_LCP_CONNECTTIME, "Connect-Time", 2, NULL },
+ { OPT_LCP_COMPOUNDFRAMES, "Compound-Frames", 2, NULL },
+ { OPT_LCP_DATAENCAP, "Nominal-Data-Encapsulation", 2, NULL },
+ { OPT_LCP_MRRU, "Multilink-MRRU", 4,
+ opt_format_mrru },
+ { OPT_LCP_SSNHF, "Multilink-Short-Sequence-Number-Header-Format",
+ 2, NULL },
+ { OPT_LCP_EPDISC, "Multilink-Endpoint-Discriminator", 3,
+ opt_format_epdisc },
+ { OPT_LCP_DCEIDENT, "DCE-Identifier", 3,
+ opt_format_dce },
+ { OPT_LCP_MLPLUSPROC, "Multi-Link-Plus-Procedure", 2, NULL },
+ { OPT_LCP_LINKDISC, "Link Discriminator for BACP", 4,
+ opt_format_linkdisc },
+ { OPT_LCP_AUTH, "LCP-Authentication-Option", 2, NULL },
+ { OPT_LCP_COBS, "COBS", 2, NULL },
+ { OPT_LCP_PFXELISION, "Prefix elision", 2, NULL },
+ { OPT_LCP_MPHDRFMT, "Multilink header format", 2, NULL },
+ { OPT_LCP_I18N, "Internationalization", 6,
+ opt_format_i18n },
+ { OPT_LCP_SDL, "Simple Data Link on SONET/SDH", 2, NULL },
+ { OPT_LCP_MUXING, "Old PPP Multiplexing", 2, NULL },
+ { 0, unknown_string, 0, NULL }
+};
+
+static cp_optinfo_t ipcp_optinfo[] = {
+ { OPT_IPCP_ADDRS, "IP-Addresses", 10,
+ opt_format_ipaddresses },
+ { OPT_IPCP_COMPRESSTYPE, "IP-Compression-Protocol", 4,
+ opt_format_ipcompproto },
+ { OPT_IPCP_ADDR, "IP-Address", 6,
+ opt_format_ipaddress },
+ { OPT_IPCP_MOBILEIPV4, "Mobile-IPv4", 6,
+ opt_format_mobileipv4 },
+ { OPT_IPCP_DNS1, "Primary DNS Address", 6,
+ opt_format_ipaddress },
+ { OPT_IPCP_NBNS1, "Primary NBNS Address", 6,
+ opt_format_ipaddress },
+ { OPT_IPCP_DNS2, "Secondary DNS Address", 6,
+ opt_format_ipaddress },
+ { OPT_IPCP_NBNS2, "Secondary NBNS Address", 6,
+ opt_format_ipaddress },
+ { OPT_IPCP_SUBNET, "IP-Subnet", 6,
+ opt_format_ipaddress },
+ { 0, unknown_string, 0, NULL }
+};
+
+static cp_optinfo_t ipv6cp_optinfo[] = {
+ { OPT_IPV6CP_IFACEID, "Interface-Identifier", 10,
+ opt_format_ifaceid },
+ { OPT_IPV6CP_COMPRESSTYPE, "IPv6-Compression-Protocol", 4,
+ opt_format_ipv6compproto },
+ { 0, unknown_string, 0, NULL }
+};
+
+static cp_optinfo_t ccp_optinfo[] = {
+ { OPT_CCP_PROPRIETARY, "Proprietary Compression OUI", 6,
+ opt_format_compoui },
+ { OPT_CCP_PREDICTOR1, "Predictor type 1", 2, NULL },
+ { OPT_CCP_PREDICTOR2, "Predictor type 2", 2, NULL },
+ { OPT_CCP_PUDDLEJUMP, "Puddle Jumper", 2, NULL },
+ { OPT_CCP_HPPPC, "Hewlett-Packard PPC", 2, NULL },
+ { OPT_CCP_STACLZS, "Stac Electronics LZS", 5,
+ opt_format_staclzs },
+ { OPT_CCP_MPPC, "Microsoft PPC", 6,
+ opt_format_mppc },
+ { OPT_CCP_GANDALFFZA, "Gandalf FZA", 3,
+ opt_format_gandalf },
+ { OPT_CCP_V42BIS, "V.42bis compression", 2,
+ NULL },
+ { OPT_CCP_BSDCOMP, "BSD LZW Compress", 3,
+ opt_format_bsdcomp },
+ { OPT_CCP_LZSDCP, "LZS-DCP", 6,
+ opt_format_lzsdcp },
+ { OPT_CCP_MAGNALINK, "Magnalink", 4,
+ opt_format_magnalink },
+ { OPT_CCP_DEFLATE, "Deflate", 4,
+ opt_format_deflate },
+ { 0, unknown_string, 0, NULL }
+};
+
+static cp_optinfo_t ecp_optinfo[] = {
+ { OPT_ECP_PROPRIETARY, "Proprietary Encryption OUI", 6,
+ opt_format_encroui },
+ { OPT_ECP_DESE, "DESE", 10,
+ opt_format_dese },
+ { OPT_ECP_3DESE, "3DESE", 10,
+ opt_format_dese },
+ { OPT_ECP_DESEBIS, "DESE-bis", 10,
+ opt_format_dese },
+ { 0, unknown_string, 0, NULL }
+};
+
+static cp_optinfo_t muxcp_optinfo[] = {
+ { OPT_MUXCP_DEFAULTPID, "Default PID", 4,
+ opt_format_muxpid },
+ { 0, unknown_string, 0, NULL }
+};
+
+static char *cp_codearray[] = {
+ "(Vendor Specific)",
+ "(Configure-Request)",
+ "(Configure-Ack)",
+ "(Configure-Nak)",
+ "(Configure-Reject)",
+ "(Terminate-Request)",
+ "(Terminate-Ack)",
+ "(Code-Reject)",
+ "(Protocol-Reject)",
+ "(Echo-Request)",
+ "(Echo-Reply)",
+ "(Discard-Request)",
+ "(Identification)",
+ "(Time-Remaining)",
+ "(Reset-Request)",
+ "(Reset-Ack)"
+};
+#define MAX_CPCODE ((sizeof (cp_codearray) / sizeof (char *)) - 1)
+
+static char *pap_codearray[] = {
+ "(Unknown)",
+ "(Authenticate-Request)",
+ "(Authenticate-Ack)",
+ "(Authenticate-Nak)"
+};
+#define MAX_PAPCODE ((sizeof (pap_codearray) / sizeof (char *)) - 1)
+
+static char *chap_codearray[] = {
+ "(Unknown)",
+ "(Challenge)",
+ "(Response)",
+ "(Success)",
+ "(Failure)"
+};
+#define MAX_CHAPCODE ((sizeof (chap_codearray) / sizeof (char *)) - 1)
+
+
+int
+interpret_ppp(int flags, uchar_t *data, int len)
+{
+ uint16_t protocol;
+ ppp_protoinfo_t *protoinfo;
+ uchar_t *payload = data;
+
+ if (len < 2)
+ return (len);
+
+ GETINT16(protocol, payload);
+ len -= sizeof (uint16_t);
+
+ protoinfo = ppp_getprotoinfo(protocol);
+
+ if (flags & F_SUM) {
+ (void) sprintf(get_sum_line(),
+ "PPP Protocol=0x%x (%s)", protocol, protoinfo->name);
+ } else { /* F_DTAIL */
+ show_header("PPP: ", "Point-to-Point Protocol", len);
+ show_space();
+ (void) sprintf(get_line(0, 0), "Protocol = 0x%x (%s)",
+ protocol, protoinfo->name);
+ show_space();
+ }
+
+ if (protoinfo->interpret_proto != NULL) {
+ len = protoinfo->interpret_proto(flags, payload, len,
+ protoinfo);
+ }
+
+ return (len);
+}
+
+/*
+ * interpret_ppp_cp() - Interpret PPP control protocols. It is convenient
+ * to do some of the decoding of these protocols in a common function since
+ * they share packet formats. This function expects to receive data
+ * starting with the code field.
+ */
+static int
+interpret_ppp_cp(int flags, uchar_t *data, int len, ppp_protoinfo_t *protoinfo)
+{
+ uint8_t code;
+ uint8_t id;
+ char *codestr;
+ uint16_t length;
+ uchar_t *datap = data;
+
+ if (len < sizeof (ppp_pkt_t))
+ return (len);
+
+ GETINT8(code, datap);
+ GETINT8(id, datap);
+ GETINT16(length, datap);
+
+ len -= sizeof (ppp_pkt_t);
+
+ if (code <= MAX_CPCODE)
+ codestr = cp_codearray[code];
+ else
+ codestr = "";
+
+ if (flags & F_SUM) {
+ (void) sprintf(get_sum_line(),
+ "%s%s", protoinfo->prefix, codestr);
+ } else { /* (flags & F_DTAIL) */
+ show_header(protoinfo->prefix, protoinfo->description, len);
+ show_space();
+
+ (void) sprintf(get_line(0, 0), "Code = %d %s", code, codestr);
+ (void) sprintf(get_line(0, 0), "Identifier = %d", id);
+ (void) sprintf(get_line(0, 0), "Length = %d", length);
+
+ show_space();
+
+ len = MIN(len, length - sizeof (ppp_pkt_t));
+ if (len == 0)
+ return (len);
+
+ switch (code) {
+ case CODE_VENDOR: {
+ uint32_t magicnum;
+ uint32_t oui;
+ char *ouistr;
+ uint8_t kind;
+
+ if (len < sizeof (magicnum) + sizeof (oui))
+ return (len);
+
+ GETINT32(magicnum, datap);
+ (void) sprintf(get_line(0, 0), "Magic-Number = 0x%08x",
+ magicnum);
+
+ GETINT32(oui, datap);
+ kind = oui & 0x000000ff;
+ oui >>= 8;
+
+ ouistr = ether_ouiname(oui);
+ if (ouistr == NULL)
+ ouistr = unknown_string;
+
+ (void) sprintf(get_line(0, 0), "OUI = 0x%06x (%s)",
+ oui, ouistr);
+ (void) sprintf(get_line(0, 0), "Kind = %d", kind);
+ show_space();
+ break;
+ }
+
+ case CODE_CONFREQ:
+ case CODE_CONFACK:
+ case CODE_CONFNAK:
+ case CODE_CONFREJ:
+ /*
+ * The above all contain protocol specific
+ * configuration options. Parse these options.
+ */
+ interpret_cp_options(datap, len, protoinfo);
+ break;
+
+ case CODE_TERMREQ:
+ case CODE_TERMACK:
+ /*
+ * The arbitrary data in these two packet types
+ * is almost always plain text. Print it as such.
+ */
+ (void) sprintf(get_line(0, 0), "Data = %.*s",
+ length - sizeof (ppp_pkt_t), datap);
+ show_space();
+ break;
+
+ case CODE_CODEREJ:
+ /*
+ * What follows is the rejected control protocol
+ * packet, starting with the code field.
+ * Conveniently, we can call interpret_ppp_cp() to
+ * decode this.
+ */
+ prot_nest_prefix = protoinfo->prefix;
+ interpret_ppp_cp(flags, datap, len, protoinfo);
+ prot_nest_prefix = "";
+ break;
+
+ case CODE_PROTREJ:
+ /*
+ * We don't print the rejected-protocol field
+ * explicitely. Instead, we cheat and pretend that
+ * the rejected-protocol field is actually the
+ * protocol field in the included PPP packet. This
+ * way, we can invoke interpret_ppp() and have it
+ * treat the included packet normally.
+ */
+ prot_nest_prefix = protoinfo->prefix;
+ interpret_ppp(flags, datap, len);
+ prot_nest_prefix = "";
+ break;
+
+ case CODE_ECHOREQ:
+ case CODE_ECHOREP:
+ case CODE_DISCREQ:
+ case CODE_IDENT:
+ case CODE_TIMEREMAIN: {
+ uint32_t magicnum;
+ char *message_label = "Identification = %.*s";
+
+ if (len < sizeof (uint32_t))
+ break;
+
+ GETINT32(magicnum, datap);
+ len -= sizeof (uint32_t);
+ (void) sprintf(get_line(0, 0), "Magic-Number = 0x%08x",
+ magicnum);
+ /*
+ * Unless this is an identification or
+ * time-remaining packet, arbitrary data follows
+ * the magic number field. The user can take a
+ * look at the hex dump for enlightenment.
+ */
+ if (code == CODE_TIMEREMAIN) {
+ uint32_t timeremaining;
+
+ if (len < sizeof (uint32_t))
+ break;
+
+ message_label = "Message = %.*s";
+
+ GETINT32(timeremaining, datap);
+ len -= sizeof (uint32_t);
+ (void) sprintf(get_line(0, 0),
+ "Seconds Remaining = %d", timeremaining);
+ }
+
+ if (code == CODE_IDENT || code == CODE_TIMEREMAIN) {
+ if (len == 0)
+ break;
+
+ (void) sprintf(get_line(0, 0), message_label,
+ len, datap);
+ }
+ show_space();
+ break;
+ }
+
+ /*
+ * Reset-Request and Reset-Ack contain arbitrary data which
+ * the user can sift through using the -x option.
+ */
+ case CODE_RESETREQ:
+ case CODE_RESETACK:
+ default:
+ break;
+ }
+ }
+ return (len);
+}
+
+
+/*
+ * interpret_cp_options() decodes control protocol configuration options.
+ * Since each control protocol has a different set of options whose type
+ * numbers overlap, the protoinfo parameter is used to get a handle on
+ * which option set to use for decoding.
+ */
+static int
+interpret_cp_options(uchar_t *optptr, int len, ppp_protoinfo_t *protoinfo)
+{
+ cp_optinfo_t *optinfo;
+ cp_optinfo_t *optinfo_ptr;
+ uint8_t optlen;
+ uint8_t opttype;
+
+ switch (protoinfo->proto) {
+ case PPP_LCP:
+ optinfo = lcp_optinfo;
+ break;
+ case PPP_IPCP:
+ optinfo = ipcp_optinfo;
+ break;
+ case PPP_IPV6CP:
+ optinfo = ipv6cp_optinfo;
+ break;
+ case PPP_CCP:
+ optinfo = ccp_optinfo;
+ break;
+ case PPP_ECP:
+ optinfo = ecp_optinfo;
+ break;
+ case PPP_MUXCP:
+ optinfo = muxcp_optinfo;
+ break;
+ default:
+ return (len);
+ break;
+ }
+
+ if (len >= 2) {
+ (void) sprintf(get_line(0, 0), "%s Configuration Options",
+ protoinfo->name);
+ show_space();
+ }
+
+ while (len >= 2) {
+ GETINT8(opttype, optptr);
+ GETINT8(optlen, optptr);
+
+ optinfo_ptr = ppp_getoptinfo(optinfo, opttype);
+
+ (void) sprintf(get_line(0, 0), "Option Type = %d (%s)", opttype,
+ optinfo_ptr->opt_name);
+ (void) sprintf(get_line(0, 0), "Option Length = %d", optlen);
+
+ /*
+ * Don't continue if there isn't enough data to
+ * contain this option, or if this type of option
+ * should contain more data than the length field
+ * claims there is.
+ */
+ if (optlen > len || optlen < optinfo_ptr->opt_minsize) {
+ (void) sprintf(get_line(0, 0),
+ "Warning: Incomplete Option");
+ show_space();
+ break;
+ }
+
+ if (optinfo_ptr->opt_formatdata != NULL) {
+ optinfo_ptr->opt_formatdata(optptr,
+ MIN(optlen - 2, len - 2));
+ }
+
+ len -= optlen;
+ optptr += optlen - 2;
+
+ show_space();
+ }
+
+ return (len);
+}
+
+static int
+interpret_ppp_chap(int flags, uchar_t *data, int len,
+ ppp_protoinfo_t *protoinfo)
+{
+ uint8_t code;
+ uint8_t id;
+ char *codestr;
+ uint16_t length;
+ int lengthleft;
+ uchar_t *datap = data;
+
+
+ if (len < sizeof (ppp_pkt_t))
+ return (len);
+
+ GETINT8(code, datap);
+ GETINT8(id, datap);
+ GETINT8(length, datap);
+
+ if (code <= MAX_CHAPCODE)
+ codestr = chap_codearray[code];
+ else
+ codestr = "";
+
+ if (flags & F_SUM) {
+ (void) sprintf(get_sum_line(),
+ "%s%s", protoinfo->prefix, codestr);
+ } else { /* (flags & F_DTAIL) */
+ show_header(protoinfo->prefix, protoinfo->description, len);
+ show_space();
+
+ (void) sprintf(get_line(0, 0), "Code = %d %s", code, codestr);
+ (void) sprintf(get_line(0, 0), "Identifier = %d", id);
+ (void) sprintf(get_line(0, 0), "Length = %d", length);
+
+ show_space();
+
+ if (len < length)
+ return (len);
+
+ lengthleft = len - sizeof (ppp_pkt_t);
+
+ switch (code) {
+ case CODE_CHALLENGE:
+ case CODE_RESPONSE: {
+ uint8_t value_size;
+ uint16_t peername_size;
+
+ if (lengthleft < sizeof (value_size))
+ break;
+
+ GETINT8(value_size, datap);
+ lengthleft -= sizeof (value_size);
+ (void) sprintf(get_line(0, 0), "Value-Size = %d",
+ value_size);
+
+ if (lengthleft < sizeof (peername_size))
+ break;
+ peername_size = MIN(length - sizeof (ppp_pkt_t) -
+ value_size, lengthleft);
+ (void) sprintf(get_line(0, 0), "Name = %.*s",
+ peername_size, datap + value_size);
+
+ break;
+ }
+ case CODE_SUCCESS:
+ case CODE_FAILURE: {
+ uint16_t message_size = MIN(length - sizeof (ppp_pkt_t),
+ lengthleft);
+
+ (void) sprintf(get_line(0, 0), "Message = %.*s",
+ message_size, datap);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ show_space();
+ len -= length;
+ return (len);
+}
+
+static int
+interpret_ppp_pap(int flags, uchar_t *data, int len,
+ ppp_protoinfo_t *protoinfo)
+{
+ uint8_t code;
+ uint8_t id;
+ char *codestr;
+ uint16_t length;
+ int lengthleft;
+ uchar_t *datap = data;
+
+ if (len < sizeof (ppp_pkt_t))
+ return (len);
+
+ GETINT8(code, datap);
+ GETINT8(id, datap);
+ GETINT16(length, datap);
+
+ lengthleft = len - sizeof (ppp_pkt_t);
+
+ if (code <= MAX_PAPCODE)
+ codestr = pap_codearray[code];
+ else
+ codestr = "";
+
+ if (flags & F_SUM) {
+ (void) sprintf(get_sum_line(),
+ "%s%s", protoinfo->prefix, codestr);
+ } else { /* (flags & F_DTAIL) */
+ show_header(protoinfo->prefix, protoinfo->description, len);
+ show_space();
+
+ (void) sprintf(get_line(0, 0), "Code = %d %s", code, codestr);
+ (void) sprintf(get_line(0, 0), "Identifier = %d", id);
+ (void) sprintf(get_line(0, 0), "Length = %d", length);
+
+ show_space();
+
+ if (len < length)
+ return (len);
+
+ switch (code) {
+ case CODE_AUTHREQ: {
+ uint8_t fieldlen;
+
+ if (lengthleft < sizeof (fieldlen))
+ break;
+ GETINT8(fieldlen, datap);
+ (void) sprintf(get_line(0, 0), "Peer-Id Length = %d",
+ fieldlen);
+ lengthleft -= sizeof (fieldlen);
+
+ if (lengthleft < fieldlen)
+ break;
+ (void) sprintf(get_line(0, 0), "Peer-Id = %.*s",
+ fieldlen, datap);
+ lengthleft -= fieldlen;
+
+ datap += fieldlen;
+
+ if (lengthleft < sizeof (fieldlen))
+ break;
+ GETINT8(fieldlen, datap);
+ (void) sprintf(get_line(0, 0), "Password Length = %d",
+ fieldlen);
+ lengthleft -= sizeof (fieldlen);
+
+ if (lengthleft < fieldlen)
+ break;
+ (void) sprintf(get_line(0, 0), "Password = %.*s",
+ fieldlen, datap);
+
+ break;
+ }
+ case CODE_AUTHACK:
+ case CODE_AUTHNAK: {
+ uint8_t msglen;
+
+ if (lengthleft < sizeof (msglen))
+ break;
+ GETINT8(msglen, datap);
+ (void) sprintf(get_line(0, 0), "Msg-Length = %d",
+ msglen);
+ lengthleft -= sizeof (msglen);
+
+ if (lengthleft < msglen)
+ break;
+ (void) sprintf(get_line(0, 0), "Message = %.*s",
+ msglen, datap);
+
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ show_space();
+ len -= length;
+ return (len);
+}
+
+
+static int
+interpret_ppp_lqr(int flags, uchar_t *data, int len,
+ ppp_protoinfo_t *protoinfo)
+{
+ lqr_pkt_t lqr_pkt;
+ if (len < sizeof (lqr_pkt_t))
+ return (len);
+
+ (void) memcpy(&lqr_pkt, data, sizeof (lqr_pkt_t));
+
+ if (flags & F_SUM) {
+ (void) sprintf(get_sum_line(), protoinfo->prefix);
+ } else { /* (flags & F_DTAIL) */
+ show_header(protoinfo->prefix, protoinfo->description, len);
+ show_space();
+
+ (void) sprintf(get_line(0, 0), "Magic-Number = 0x%08x",
+ ntohl(lqr_pkt.lqr_magic));
+ (void) sprintf(get_line(0, 0), "LastOutLQRs = %d",
+ ntohl(lqr_pkt.lqr_lastoutlqrs));
+ (void) sprintf(get_line(0, 0), "LastOutPackets = %d",
+ ntohl(lqr_pkt.lqr_lastoutpackets));
+ (void) sprintf(get_line(0, 0), "LastOutOctets = %d",
+ ntohl(lqr_pkt.lqr_lastoutoctets));
+ (void) sprintf(get_line(0, 0), "PeerInLQRs = %d",
+ ntohl(lqr_pkt.lqr_peerinlqrs));
+ (void) sprintf(get_line(0, 0), "PeerInPackets = %d",
+ ntohl(lqr_pkt.lqr_peerinpackets));
+ (void) sprintf(get_line(0, 0), "PeerInDiscards = %d",
+ ntohl(lqr_pkt.lqr_peerindiscards));
+ (void) sprintf(get_line(0, 0), "PeerInErrors = %d",
+ ntohl(lqr_pkt.lqr_peerinerrors));
+ (void) sprintf(get_line(0, 0), "PeerInOctets = %d",
+ ntohl(lqr_pkt.lqr_peerinoctets));
+ (void) sprintf(get_line(0, 0), "PeerOutLQRs = %d",
+ ntohl(lqr_pkt.lqr_peeroutlqrs));
+ (void) sprintf(get_line(0, 0), "PeerOutPackets = %d",
+ ntohl(lqr_pkt.lqr_peeroutpackets));
+ (void) sprintf(get_line(0, 0), "PeerOutOctets = %d",
+ ntohl(lqr_pkt.lqr_peeroutoctets));
+
+ show_space();
+ }
+
+ len -= sizeof (lqr_pkt_t);
+ return (len);
+}
+
+static ppp_protoinfo_t *
+ppp_getprotoinfo(uint16_t proto)
+{
+ ppp_protoinfo_t *protoinfo_ptr = &protoinfo_array[0];
+
+ while (protoinfo_ptr->proto != proto && protoinfo_ptr->proto != 0) {
+ protoinfo_ptr++;
+ }
+
+ return (protoinfo_ptr);
+}
+
+
+static cp_optinfo_t *
+ppp_getoptinfo(cp_optinfo_t optinfo_list[], uint16_t opt_type)
+{
+ cp_optinfo_t *optinfo_ptr = &optinfo_list[0];
+
+ while (optinfo_ptr->opt_type != opt_type &&
+ optinfo_ptr->opt_name != unknown_string) {
+ optinfo_ptr++;
+ }
+
+ return (optinfo_ptr);
+}
+
+
+/*
+ * Below are the functions which parse control protocol configuration
+ * options. The first argument to these functions (optdata) points to the
+ * first byte of the option after the length field. The second argument
+ * (size) is the number of bytes in the option after the length field
+ * (length - 2).
+ */
+
+/*
+ * The format of the Vendor-Specific option (rfc2153) is:
+ *
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | OUI
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * ... | Kind | Value(s) ...
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
+ */
+/*ARGSUSED1*/
+static void
+opt_format_vendor(uchar_t *optdata, uint8_t size)
+{
+ uint32_t oui;
+ char *ouistr;
+ uint8_t kind;
+
+ GETINT32(oui, optdata);
+ kind = oui & 0x000000ff;
+ oui >>= 8;
+
+ ouistr = ether_ouiname(oui);
+ if (ouistr == NULL)
+ ouistr = unknown_string;
+
+ (void) sprintf(get_line(0, 0), "OUI = 0x%06x (%s)", oui, ouistr);
+ (void) sprintf(get_line(0, 0), "Kind = %d", kind);
+}
+
+/*
+ * The format of the MRU option (rfc1661) is:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | Maximum-Receive-Unit |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+/*ARGSUSED1*/
+static void
+opt_format_mru(uchar_t *optdata, uint8_t size)
+{
+ uint16_t mru;
+
+ GETINT16(mru, optdata);
+ (void) sprintf(get_line(0, 0), "MRU = %d", mru);
+}
+
+/*
+ * The format of the accm option (rfc1662) is:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | ACCM
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * ACCM (cont) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+/*ARGSUSED1*/
+static void
+opt_format_accm(uchar_t *optdata, uint8_t size)
+{
+ uint32_t accm;
+
+ GETINT32(accm, optdata);
+ (void) sprintf(get_line(0, 0), "ACCM = 0x%08x", accm);
+}
+
+/*
+ * The format of the Authentication-Protocol option (rfc1661) is:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | Authentication-Protocol |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Data ...
+ * +-+-+-+-+
+ *
+ * For PAP (rfc1334), there is no data. For CHAP (rfc1994), there is one
+ * byte of data representing the algorithm.
+ */
+static void
+opt_format_authproto(uchar_t *optdata, uint8_t size)
+{
+ uint16_t proto;
+ ppp_protoinfo_t *auth_protoinfo;
+
+ GETINT16(proto, optdata);
+
+ auth_protoinfo = ppp_getprotoinfo(proto);
+
+ (void) sprintf(get_line(0, 0), "Protocol = 0x%x (%s)", proto,
+ auth_protoinfo->name);
+
+ switch (proto) {
+ case PPP_CHAP: {
+ uint8_t algo;
+ char *algostr;
+
+ if (size < sizeof (proto) + sizeof (algo))
+ return;
+
+ GETINT8(algo, optdata);
+ switch (algo) {
+ case 5:
+ algostr = "CHAP with MD5";
+ break;
+ case 128:
+ algostr = "MS-CHAP";
+ break;
+ case 129:
+ algostr = "MS-CHAP-2";
+ break;
+ default:
+ algostr = unknown_string;
+ break;
+ }
+ (void) sprintf(get_line(0, 0), "Algorithm = %d (%s)", algo,
+ algostr);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+/*
+ * The format of the Quality Protocol option (rfc1661) is:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | Quality-Protocol |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Data ...
+ * +-+-+-+-+
+ *
+ * For LQR, the data consists of a 4 byte reporting period.
+ */
+static void
+opt_format_qualproto(uchar_t *optdata, uint8_t size)
+{
+ uint16_t proto;
+ ppp_protoinfo_t *qual_protoinfo;
+
+ GETINT16(proto, optdata);
+
+ qual_protoinfo = ppp_getprotoinfo(proto);
+
+ (void) sprintf(get_line(0, 0), "Protocol = 0x%x (%s)", proto,
+ qual_protoinfo->name);
+
+ switch (proto) {
+ case PPP_LQR: {
+ uint32_t reporting_period;
+
+ if (size < sizeof (proto) + sizeof (reporting_period))
+ return;
+
+ GETINT32(reporting_period, optdata);
+ (void) sprintf(get_line(0, 0), "Reporting-Period = %d",
+ reporting_period);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+/*
+ * The format of the Magic Number option (rfc1661) is:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | Magic-Number
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * Magic-Number (cont) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+/*ARGSUSED1*/
+static void
+opt_format_magicnum(uchar_t *optdata, uint8_t size)
+{
+ uint32_t magicnum;
+
+ GETINT32(magicnum, optdata);
+ (void) sprintf(get_line(0, 0), "Magic Number = 0x%08x", magicnum);
+}
+
+/*
+ * The format of the FCS-Alternatives option (rfc1570) is:
+ *
+ * 0 1 2
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | Options |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+/*ARGSUSED1*/
+static void
+opt_format_fcs(uchar_t *optdata, uint8_t size)
+{
+ uint8_t options;
+
+ GETINT8(options, optdata);
+
+ (void) sprintf(get_line(0, 0), "Options = 0x%02x", options);
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(options, 0x01, "NULL FCS", ""));
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(options, 0x02, "CCITT 16-bit FCS", ""));
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(options, 0x04, "CCITT 32-bit FCS", ""));
+}
+
+/*
+ * The format of the Self-Describing-Padding option (rfc1570) is:
+ *
+ * 0 1 2
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | Maximum |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+/*ARGSUSED1*/
+static void
+opt_format_sdp(uchar_t *optdata, uint8_t size)
+{
+ uint8_t max;
+
+ GETINT8(max, optdata);
+
+ (void) sprintf(get_line(0, 0), "Maximum = %d", max);
+}
+
+/*
+ * The format of the Numbered-Mode option (rfc1663) is:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | Window | Address...
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+/*ARGSUSED1*/
+static void
+opt_format_nummode(uchar_t *optdata, uint8_t size)
+{
+ uint8_t window;
+
+ GETINT8(window, optdata);
+ (void) sprintf(get_line(0, 0), "Window = %d", window);
+}
+
+/*
+ * The format of the Callback option (rfc1570) is:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | Operation | Message ...
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+static void
+opt_format_callback(uchar_t *optdata, uint8_t size)
+{
+ uint8_t operation;
+ char *opstr;
+
+ GETINT8(operation, optdata);
+ switch (operation) {
+ case 0:
+ opstr = "User Authentication";
+ break;
+ case 1:
+ opstr = "Dialing String";
+ break;
+ case 2:
+ opstr = "Location Identifier";
+ break;
+ case 3:
+ opstr = "E.164 Number";
+ break;
+ case 4:
+ opstr = "X.500 Distinguished Name";
+ break;
+ case 6:
+ opstr = "CBCP Negotiation";
+ break;
+ default:
+ opstr = unknown_string;
+ break;
+ }
+
+ (void) sprintf(get_line(0, 0), "Operation = %d (%s)", operation, opstr);
+
+ if (size > sizeof (operation)) {
+ (void) sprintf(get_line(0, 0), "Message = %.*s",
+ size - sizeof (operation), optdata);
+ }
+}
+
+/*
+ * The format of the Multilink-MRRU option (rfc1990) is:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type = 17 | Length = 4 | Max-Receive-Reconstructed-Unit|
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+/*ARGSUSED1*/
+static void
+opt_format_mrru(uchar_t *optdata, uint8_t size)
+{
+ uint16_t mrru;
+
+ GETINT16(mrru, optdata);
+ (void) sprintf(get_line(0, 0), "MRRU = %d", mrru);
+}
+
+/*
+ * The format of the Endpoint Discriminator option (rfc1990) is:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type = 19 | Length | Class | Address ...
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+static void
+opt_format_epdisc(uchar_t *optdata, uint8_t size)
+{
+ uint8_t class;
+ char *classstr;
+ uint8_t addrlen = size - sizeof (class);
+ char *addr;
+
+ GETINT8(class, optdata);
+
+ switch (class) {
+ case 0:
+ classstr = "Null Class";
+ break;
+ case 1:
+ classstr = "Locally Assigned Address";
+ break;
+ case 2:
+ classstr = "IPv4 Address";
+ break;
+ case 3:
+ classstr = "IEE 802.1 Global MAC Address";
+ break;
+ case 4:
+ classstr = "PPP Magic-Number Block";
+ break;
+ case 5:
+ classstr = "Public Switched Network Directory Number";
+ break;
+ default:
+ classstr = unknown_string;
+ break;
+ }
+
+ (void) sprintf(get_line(0, 0), "Address Class = %d (%s)", class,
+ classstr);
+
+ if (addrlen == 0)
+ return;
+
+ addr = (char *)malloc(addrlen);
+ (void) memcpy(addr, optdata, addrlen);
+ switch (class) {
+ case 2: {
+ char addrstr[INET_ADDRSTRLEN];
+
+ if (addrlen != sizeof (in_addr_t))
+ break;
+ if (inet_ntop(AF_INET, addr, addrstr, INET_ADDRSTRLEN) !=
+ NULL) {
+ (void) sprintf(get_line(0, 0), "Address = %s", addrstr);
+ }
+ break;
+ }
+ case 3: {
+ char *addrstr;
+
+ if (addrlen != sizeof (struct ether_addr))
+ break;
+ if ((addrstr = ether_ntoa((struct ether_addr *)addr)) != NULL) {
+ (void) sprintf(get_line(0, 0), "Address = %s", addrstr);
+ }
+ break;
+ }
+ case 5: {
+ /*
+ * For this case, the address is supposed to be a plain
+ * text telephone number.
+ */
+ (void) sprintf(get_line(0, 0), "Address = %.*s", addrlen,
+ addr);
+ }
+ default:
+ break;
+ }
+
+ free(addr);
+}
+
+/*
+ * The DCE identifier option has the following format (from rfc1976):
+ *
+ * 0 1 2
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | Mode |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+/*ARGSUSED1*/
+static void
+opt_format_dce(uchar_t *optdata, uint8_t size)
+{
+ uint8_t mode;
+ char *modestr;
+
+ GETINT8(mode, optdata);
+ switch (mode) {
+ case 1:
+ modestr = "No Additional Negotiation";
+ break;
+ case 2:
+ modestr = "Full PPP Negotiation and State Machine";
+ break;
+ default:
+ modestr = unknown_string;
+ break;
+ }
+ (void) sprintf(get_line(0, 0), "Mode = %d (%s)", mode, modestr);
+}
+
+/*
+ * The format of the Link Discriminator option (rfc2125) is:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | Link Discriminator |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+/*ARGSUSED1*/
+static void
+opt_format_linkdisc(uchar_t *optdata, uint8_t size)
+{
+ uint16_t discrim;
+
+ GETINT16(discrim, optdata);
+
+ (void) sprintf(get_line(0, 0), "Link Discriminator = %d", discrim);
+}
+
+
+/*
+ * The format of the Internationalization option (rfc2484) is:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | MIBenum
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * MIBenum (cont) | Language-Tag...
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+static void
+opt_format_i18n(uchar_t *optdata, uint8_t size)
+{
+ uint32_t mibenum;
+ uint8_t taglen;
+
+ taglen = size - sizeof (mibenum);
+
+ GETINT32(mibenum, optdata);
+ (void) sprintf(get_line(0, 0), "MIBenum = %d", mibenum);
+
+ if (taglen > 0) {
+ (void) sprintf(get_line(0, 0), "Language Tag = %.*s", taglen,
+ optdata);
+ }
+}
+
+/*
+ * The format of the obsolete IP-Addresses option (rfc1172) is:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | Source-IP-Address
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * Source-IP-Address (cont) | Destination-IP-Address
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * Destination-IP-Address (cont) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+/*ARGSUSED1*/
+static void
+opt_format_ipaddresses(uchar_t *optdata, uint8_t size)
+{
+ in_addr_t addr;
+ char addrstr[INET_ADDRSTRLEN];
+
+ (void) memcpy(&addr, optdata, sizeof (in_addr_t));
+ if (inet_ntop(AF_INET, &addr, addrstr, INET_ADDRSTRLEN) != NULL) {
+ (void) sprintf(get_line(0, 0), "Source Address = %s",
+ addrstr);
+ }
+
+ optdata += sizeof (in_addr_t);
+
+ (void) memcpy(&addr, optdata, sizeof (in_addr_t));
+ if (inet_ntop(AF_INET, &addr, addrstr, INET_ADDRSTRLEN) != NULL) {
+ (void) sprintf(get_line(0, 0), "Destination Address = %s",
+ addrstr);
+ }
+}
+
+/*
+ * The format of the IP-Compression-Protocol option (rfc1332) is:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | IP-Compression-Protocol |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Data ...
+ * +-+-+-+-+
+ *
+ * For VJ Compressed TCP/IP, data consists of:
+ *
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Max-Slot-Id | Comp-Slot-Id |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * For IPHC (rfc2509), data consists of:
+ *
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | TCP_SPACE | NON_TCP_SPACE |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | F_MAX_PERIOD | F_MAX_TIME |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | MAX_HEADER | suboptions...
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+static void
+opt_format_ipcompproto(uchar_t *optdata, uint8_t size)
+{
+ uint16_t proto;
+ ppp_protoinfo_t *comp_protoinfo;
+
+ GETINT16(proto, optdata);
+
+ comp_protoinfo = ppp_getprotoinfo(proto);
+
+ (void) sprintf(get_line(0, 0), "Protocol = 0x%x (%s)", proto,
+ comp_protoinfo->name);
+
+ switch (proto) {
+ case PPP_VJC_COMP: {
+ uint8_t maxslotid;
+ uint8_t compslotid;
+
+ if (size < sizeof (proto) + sizeof (maxslotid) +
+ sizeof (compslotid))
+ break;
+
+ GETINT8(maxslotid, optdata);
+ GETINT8(compslotid, optdata);
+ (void) sprintf(get_line(0, 0), "Max-Slot-Id = %d", maxslotid);
+ (void) sprintf(get_line(0, 0), "Comp-Slot Flag = 0x%x",
+ compslotid);
+ break;
+ }
+ case PPP_FULLHDR: {
+ uint16_t tcp_space;
+ uint16_t non_tcp_space;
+ uint16_t f_max_period;
+ uint16_t f_max_time;
+ uint16_t max_header;
+
+ if (size < sizeof (proto) + sizeof (tcp_space) +
+ sizeof (non_tcp_space) + sizeof (f_max_period) +
+ sizeof (f_max_time) + sizeof (max_header))
+ break;
+
+ GETINT16(tcp_space, optdata);
+ GETINT16(non_tcp_space, optdata);
+ GETINT16(f_max_period, optdata);
+ GETINT16(f_max_time, optdata);
+ GETINT16(max_header, optdata);
+
+ (void) sprintf(get_line(0, 0), "TCP_SPACE = %d", tcp_space);
+ (void) sprintf(get_line(0, 0), "NON_TCP_SPACE = %d",
+ non_tcp_space);
+ (void) sprintf(get_line(0, 0), "F_MAX_PERIOD = %d",
+ f_max_period);
+ (void) sprintf(get_line(0, 0), "F_MAX_TIME = %d", f_max_time);
+ (void) sprintf(get_line(0, 0), "MAX_HEADER = %d octets",
+ max_header);
+ }
+ default:
+ break;
+ }
+}
+
+/*
+ * The format of the IP-Address option (rfc1332) is:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | IP-Address
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * IP-Address (cont) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+/*ARGSUSED1*/
+static void
+opt_format_ipaddress(uchar_t *optdata, uint8_t size)
+{
+ in_addr_t ipaddr;
+ char addrstr[INET_ADDRSTRLEN];
+
+ (void) memcpy(&ipaddr, optdata, sizeof (in_addr_t));
+ if (inet_ntop(AF_INET, &ipaddr, addrstr, INET_ADDRSTRLEN) != NULL) {
+ (void) sprintf(get_line(0, 0), "Address = %s", addrstr);
+ }
+}
+
+/*
+ * The format of the Mobile-IPv4 option (rfc2290) is:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | Mobile Node's ...
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * ... Home Address |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+/*ARGSUSED1*/
+static void
+opt_format_mobileipv4(uchar_t *optdata, uint8_t size)
+{
+ in_addr_t ipaddr;
+ char addrstr[INET_ADDRSTRLEN];
+
+ (void) memcpy(&ipaddr, optdata, sizeof (in_addr_t));
+ if (inet_ntop(AF_INET, &ipaddr, addrstr, INET_ADDRSTRLEN) != NULL) {
+ (void) sprintf(get_line(0, 0),
+ "Mobile Node's Home Address = %s", addrstr);
+ }
+}
+
+/*
+ * The format of the Interface-Identifier option (rfc2472) is:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | Interface-Identifier (MS Bytes)
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * Interface-Identifier (cont)
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * Interface-Identifier (LS Bytes) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+/*ARGSUSED1*/
+static void
+opt_format_ifaceid(uchar_t *optdata, uint8_t size)
+{
+ in6_addr_t id;
+ char idstr[INET6_ADDRSTRLEN];
+
+ (void) memset(&id, 0, sizeof (in6_addr_t));
+ (void) memcpy(&id.s6_addr[8], optdata, 8);
+
+ if (inet_ntop(AF_INET6, &id, idstr, INET6_ADDRSTRLEN) != NULL) {
+ (void) sprintf(get_line(0, 0), "Interface ID = %s", idstr);
+ }
+}
+
+/*
+ * The format of the IPv6-Compression-Protocol option (rfc2472) is:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | IPv6-Compression-Protocol |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Data ...
+ * +-+-+-+-+
+ */
+static void
+opt_format_ipv6compproto(uchar_t *optdata, uint8_t size)
+{
+ uint16_t proto;
+ ppp_protoinfo_t *comp_protoinfo;
+
+ GETINT16(proto, optdata);
+
+ comp_protoinfo = ppp_getprotoinfo(proto);
+
+ (void) sprintf(get_line(0, 0), "Protocol = 0x%x (%s)", proto,
+ comp_protoinfo->name);
+
+ switch (proto) {
+ case PPP_FULLHDR: {
+ uint16_t tcp_space;
+ uint16_t non_tcp_space;
+ uint16_t f_max_period;
+ uint16_t f_max_time;
+ uint16_t max_header;
+
+ if (size < sizeof (proto) + sizeof (tcp_space) +
+ sizeof (non_tcp_space) + sizeof (f_max_period) +
+ sizeof (f_max_time) + sizeof (max_header))
+ return;
+
+ GETINT16(tcp_space, optdata);
+ GETINT16(non_tcp_space, optdata);
+ GETINT16(f_max_period, optdata);
+ GETINT16(f_max_time, optdata);
+ GETINT16(max_header, optdata);
+
+ (void) sprintf(get_line(0, 0), "TCP_SPACE = %d", tcp_space);
+ (void) sprintf(get_line(0, 0), "NON_TCP_SPACE = %d",
+ non_tcp_space);
+ (void) sprintf(get_line(0, 0), "F_MAX_PERIOD = %d",
+ f_max_period);
+ (void) sprintf(get_line(0, 0), "F_MAX_TIME = %d", f_max_time);
+ (void) sprintf(get_line(0, 0), "MAX_HEADER = %d octets",
+ max_header);
+ }
+ default:
+ break;
+ }
+}
+
+/*
+ * The format of the Proprietary Compression OUI option (rfc1962) is:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | OUI ...
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * OUI | Subtype | Values...
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
+ */
+/*ARGSUSED1*/
+static void
+opt_format_compoui(uchar_t *optdata, uint8_t size)
+{
+ uint32_t oui;
+ uint8_t subtype;
+ char *ouistr;
+
+ GETINT32(oui, optdata);
+ subtype = oui & 0x000000ff;
+ oui >>= 8;
+
+ ouistr = ether_ouiname(oui);
+ if (ouistr == NULL)
+ ouistr = unknown_string;
+ (void) sprintf(get_line(0, 0), "OUI = 0x%06x (%s)", oui, ouistr);
+ (void) sprintf(get_line(0, 0), "Subtype = 0x%x", subtype);
+}
+
+/*
+ * The format of the Stac LZS configuration option (rfc1974) is:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | History Count |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Check Mode |
+ * +-+-+-+-+-+-+-+-+
+ */
+/*ARGSUSED1*/
+static void
+opt_format_staclzs(uchar_t *optdata, uint8_t size)
+{
+ uint16_t hcount;
+ uint8_t cmode;
+
+ GETINT16(hcount, optdata);
+ GETINT8(cmode, optdata);
+
+ cmode &= 0x07;
+
+ (void) sprintf(get_line(0, 0), "History Count = %d", hcount);
+ (void) sprintf(get_line(0, 0), "Check Mode = %d", cmode);
+}
+
+/*
+ * The format of MPPC configuration option (rfc2118) is:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | Supported Bits |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Supported Bits |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+/*ARGSUSED1*/
+static void
+opt_format_mppc(uchar_t *optdata, uint8_t size)
+{
+ uint32_t sb;
+
+ GETINT32(sb, optdata);
+
+ (void) sprintf(get_line(0, 0), "Supported Bits = 0x%x", sb);
+}
+
+/*
+ * The format of the Gandalf FZA configuration option (rfc1993) is:
+ *
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | History | Version ...
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+/*ARGSUSED1*/
+static void
+opt_format_gandalf(uchar_t *optdata, uint8_t size)
+{
+ uint8_t history;
+
+ GETINT8(history, optdata);
+ (void) sprintf(get_line(0, 0), "Maximum History Size = %d bits",
+ history);
+}
+
+/*
+ * The format of the BSD Compress configuration option (rfc1977) is:
+ *
+ * 0 1 2
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | Vers| Dict |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+/*ARGSUSED1*/
+static void
+opt_format_bsdcomp(uchar_t *optdata, uint8_t size)
+{
+ uint8_t version;
+ uint8_t codesize;
+
+ GETINT8(codesize, optdata);
+
+ version = codesize >> 5;
+ codesize &= 0x1f;
+
+ (void) sprintf(get_line(0, 0), "Version = 0x%x", version);
+ (void) sprintf(get_line(0, 0), "Maximum Code Size = %d bits", codesize);
+}
+
+/*
+ * The format of the LZS-DCP configuration option (rfc1967) is:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | History Count |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Check Mode | Process Mode |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+/*ARGSUSED1*/
+static void
+opt_format_lzsdcp(uchar_t *optdata, uint8_t size)
+{
+ uint16_t history;
+ uint8_t mode;
+ char *modestr;
+
+ GETINT16(history, optdata);
+ (void) sprintf(get_line(0, 0), "History Count = %d", history);
+
+ /* check mode */
+ GETINT8(mode, optdata);
+ switch (mode) {
+ case 0:
+ modestr = "None";
+ break;
+ case 1:
+ modestr = "LCB";
+ break;
+ case 2:
+ modestr = "Sequence Number";
+ break;
+ case 3:
+ modestr = "Sequence Number + LCB (default)";
+ break;
+ default:
+ modestr = unknown_string;
+ break;
+ }
+ (void) sprintf(get_line(0, 0), "Check Mode = %d (%s)", mode, modestr);
+
+ /* process mode */
+ GETINT8(mode, optdata);
+ switch (mode) {
+ case 0:
+ modestr = "None (default)";
+ break;
+ case 1:
+ modestr = "Process-Uncompressed";
+ break;
+ default:
+ modestr = unknown_string;
+ break;
+ }
+ (void) sprintf(get_line(0, 0), "Process Mode = %d (%s)", mode, modestr);
+
+}
+
+/*
+ * The format of the Magnalink configuration option (rfc1975) is:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length |FE |P| History | # Contexts |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+/*ARGSUSED1*/
+static void
+opt_format_magnalink(uchar_t *optdata, uint8_t size)
+{
+ uint8_t features;
+ uint8_t pflag;
+ uint8_t history;
+ uint8_t contexts;
+
+ GETINT8(history, optdata);
+ GETINT8(contexts, optdata);
+
+ features = history >> 6;
+ pflag = (history >> 5) & 0x01;
+ history &= 0x1f;
+
+ (void) sprintf(get_line(0, 0), "Features = 0x%d", features);
+ (void) sprintf(get_line(0, 0), "Packet Flag = %d", pflag);
+ (void) sprintf(get_line(0, 0), "History Size = %d", history);
+ (void) sprintf(get_line(0, 0), "Contexts = %d", contexts);
+}
+
+/*
+ * The format of the Deflate configuration option (rfc1979) is:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length |Window | Method| MBZ |Chk|
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+/*ARGSUSED1*/
+static void
+opt_format_deflate(uchar_t *optdata, uint8_t size)
+{
+ uint8_t window;
+ uint8_t method;
+ uint8_t chk;
+
+ GETINT8(method, optdata);
+ window = method >> 4;
+ method &= 0x0f;
+
+ GETINT8(chk, optdata);
+ chk &= 0x03;
+
+ (void) sprintf(get_line(0, 0), "Maximum Window Size = %d", window);
+ (void) sprintf(get_line(0, 0), "Compression Method = 0x%x", method);
+ (void) sprintf(get_line(0, 0), "Check Method = 0x%x", chk);
+}
+
+/*
+ * The format of the Proprietary Encryption OUI option (rfc1968) is:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | OUI ...
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * OUI | Subtype | Values...
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
+ */
+/*ARGSUSED1*/
+static void
+opt_format_encroui(uchar_t *optdata, uint8_t size)
+{
+ uint32_t oui;
+ uint8_t subtype;
+ char *ouistr;
+
+ GETINT32(oui, optdata);
+ subtype = oui & 0x000000ff;
+ oui >>= 8;
+
+ ouistr = ether_ouiname(oui);
+ if (ouistr == NULL)
+ ouistr = unknown_string;
+ (void) sprintf(get_line(0, 0), "OUI = 0x%06x (%s)", oui, ouistr);
+ (void) sprintf(get_line(0, 0), "Subtype = 0x%x", subtype);
+}
+
+/*
+ * The format of the DESE, DESE-bis, and 3DESE configuration options
+ * (rfc1969, rfc2419, and rfc2420) are:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type = 3 | Length | Initial Nonce ...
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+/*ARGSUSED1*/
+static void
+opt_format_dese(uchar_t *optdata, uint8_t size)
+{
+ (void) sprintf(get_line(0, 0),
+ "Initial Nonce = 0x%02x%02x%02x%02x%02x%02x%02x%02x",
+ optdata[0], optdata[1], optdata[2], optdata[3], optdata[4],
+ optdata[5], optdata[6], optdata[7]);
+}
+
+/*
+ * The format of the PPPMux Default Protocol Id option
+ * (draft-ietf-pppext-pppmux-02.txt) is:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type = 1 | Length = 4 | Default PID |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+/*ARGSUSED1*/
+static void
+opt_format_muxpid(uchar_t *optdata, uint8_t size)
+{
+ uint16_t defpid;
+
+ GETINT16(defpid, optdata);
+ (void) sprintf(get_line(0, 0), "Default PID = %d", defpid);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ppp.h b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ppp.h
new file mode 100644
index 0000000000..0f158c5ad7
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ppp.h
@@ -0,0 +1,192 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _SNOOP_PPP_H
+#define _SNOOP_PPP_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Control Protocol (LCP, IPCP, etc.) message code numbers.
+ */
+#define CODE_VENDOR 0 /* Vendor Specif Code */
+#define CODE_CONFREQ 1 /* Configuration Request */
+#define CODE_CONFACK 2 /* Configuration Ack */
+#define CODE_CONFNAK 3 /* Configuration Nak */
+#define CODE_CONFREJ 4 /* Configuration Reject */
+#define CODE_TERMREQ 5 /* Termination Request */
+#define CODE_TERMACK 6 /* Termination Ack */
+#define CODE_CODEREJ 7 /* Code Reject */
+/*
+ * LCP specific codes.
+ */
+#define CODE_PROTREJ 8 /* Protocol Reject */
+#define CODE_ECHOREQ 9 /* Echo Request */
+#define CODE_ECHOREP 10 /* Echo Reply */
+#define CODE_DISCREQ 11 /* Discard Request */
+#define CODE_IDENT 12 /* Identification */
+#define CODE_TIMEREMAIN 13 /* Time Remaining */
+/*
+ * CCP and ECP specific codes.
+ */
+#define CODE_RESETREQ 14
+#define CODE_RESETACK 15
+
+/*
+ * CHAP codes.
+ */
+#define CODE_CHALLENGE 1
+#define CODE_RESPONSE 2
+#define CODE_SUCCESS 3
+#define CODE_FAILURE 4
+
+/*
+ * PAP codes.
+ */
+#define CODE_AUTHREQ 1
+#define CODE_AUTHACK 2
+#define CODE_AUTHNAK 3
+
+/*
+ * Option types for various control protocols.
+ */
+#define OPT_LCP_VENDOR 0
+#define OPT_LCP_MRU 1
+#define OPT_LCP_ASYNCMAP 2
+#define OPT_LCP_AUTHTYPE 3
+#define OPT_LCP_QUALITY 4
+#define OPT_LCP_MAGICNUMBER 5
+#define OPT_LCP_PCOMPRESSION 7
+#define OPT_LCP_ACCOMPRESSION 8
+#define OPT_LCP_FCSALTERN 9
+#define OPT_LCP_SELFDESCPAD 10
+#define OPT_LCP_NUMBERED 11
+#define OPT_LCP_MULTILINKPROC 12
+#define OPT_LCP_CALLBACK 13
+#define OPT_LCP_CONNECTTIME 14
+#define OPT_LCP_COMPOUNDFRAMES 15
+#define OPT_LCP_DATAENCAP 16
+#define OPT_LCP_MRRU 17
+#define OPT_LCP_SSNHF 18
+#define OPT_LCP_EPDISC 19
+#define OPT_LCP_DCEIDENT 21
+#define OPT_LCP_MLPLUSPROC 22
+#define OPT_LCP_LINKDISC 23
+#define OPT_LCP_AUTH 24
+#define OPT_LCP_COBS 25
+#define OPT_LCP_PFXELISION 26
+#define OPT_LCP_MPHDRFMT 27
+#define OPT_LCP_I18N 28
+#define OPT_LCP_SDL 29
+#define OPT_LCP_MUXING 30
+
+#define OPT_IPCP_ADDRS 1
+#define OPT_IPCP_COMPRESSTYPE 2
+#define OPT_IPCP_ADDR 3
+#define OPT_IPCP_MOBILEIPV4 4
+#define OPT_IPCP_DNS1 129
+#define OPT_IPCP_NBNS1 130
+#define OPT_IPCP_DNS2 131
+#define OPT_IPCP_NBNS2 132
+#define OPT_IPCP_SUBNET 144
+
+#define OPT_IPV6CP_IFACEID 1
+#define OPT_IPV6CP_COMPRESSTYPE 2
+
+#define OPT_CCP_PROPRIETARY 0
+#define OPT_CCP_PREDICTOR1 1
+#define OPT_CCP_PREDICTOR2 2
+#define OPT_CCP_PUDDLEJUMP 3
+#define OPT_CCP_HPPPC 16
+#define OPT_CCP_STACLZS 17
+#define OPT_CCP_MPPC 18
+#define OPT_CCP_GANDALFFZA 19
+#define OPT_CCP_V42BIS 20
+#define OPT_CCP_BSDCOMP 21
+#define OPT_CCP_LZSDCP 23
+#define OPT_CCP_MAGNALINK 24
+#define OPT_CCP_DEFLATE 26
+
+#define OPT_ECP_PROPRIETARY 0
+#define OPT_ECP_DESE 1
+#define OPT_ECP_3DESE 2
+#define OPT_ECP_DESEBIS 3
+
+#define OPT_MUXCP_DEFAULTPID 1
+
+/*
+ * ppp_protoinfo_t's contain properties of PPP protocols which
+ * interpret_ppp() needs in order to properly decode the protocol data.
+ */
+typedef struct ppp_protoinfo {
+ uint16_t proto; /* protocol number */
+ char *name; /* protocol name */
+ int (*interpret_proto)(); /* interpret function */
+ char *prefix; /* string printed on detail lines */
+ char *description; /* string printed in detail header */
+} ppp_protoinfo_t;
+
+
+/*
+ * cp_optinfo contains information on control protocol options.
+ */
+typedef void optformat_func_t(uchar_t *, uint8_t);
+typedef struct cp_optinfo {
+ uint8_t opt_type;
+ char *opt_name;
+ uint8_t opt_minsize; /* min size of option, including type and len */
+ optformat_func_t *opt_formatdata;
+} cp_optinfo_t;
+
+
+/*
+ * Packet format for PPP control and authentication protocols.
+ */
+typedef struct ppp_pkt {
+ uint8_t code;
+ uint8_t id;
+ uint16_t length;
+} ppp_pkt_t;
+
+/*
+ * Structure of an LQR packet.
+ */
+typedef struct lqr_pkt {
+ uint32_t lqr_magic;
+ uint32_t lqr_lastoutlqrs;
+ uint32_t lqr_lastoutpackets;
+ uint32_t lqr_lastoutoctets;
+ uint32_t lqr_peerinlqrs;
+ uint32_t lqr_peerinpackets;
+ uint32_t lqr_peerindiscards;
+ uint32_t lqr_peerinerrors;
+ uint32_t lqr_peerinoctets;
+ uint32_t lqr_peeroutlqrs;
+ uint32_t lqr_peeroutpackets;
+ uint32_t lqr_peeroutoctets;
+} lqr_pkt_t;
+
+#endif /* _SNOOP_PPP_H */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_pppoe.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_pppoe.c
new file mode 100644
index 0000000000..09392718ac
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_pppoe.c
@@ -0,0 +1,399 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/sysmacros.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <net/pppoe.h>
+#include "snoop.h"
+
+/*
+ * These two macros extract the version and type fields respectively from
+ * the first byte of the PPPoE header.
+ */
+#define POE_VERS(x) (((x) >> 4) & 0x0f)
+#define POE_TYPE(x) ((x) & 0x0f)
+
+typedef void interpret_func_t(uint8_t *, uint16_t);
+
+typedef struct taginfo {
+ char *tag_name;
+ uint16_t tag_type;
+ interpret_func_t *interpret_tagvalue;
+} taginfo_t;
+
+
+static char *pppoe_codetoname(int, boolean_t);
+static taginfo_t *pppoe_gettaginfo(uint16_t);
+static void print_hexdata(char *, uint8_t *, uint16_t);
+static void print_utf8string(char *, char *, uint16_t);
+static char *print_linetag(char *);
+static interpret_func_t interpret_tags;
+static interpret_func_t interpret_hexdata;
+static interpret_func_t interpret_service;
+static interpret_func_t interpret_access;
+static interpret_func_t interpret_cookie;
+static interpret_func_t interpret_vendor;
+static interpret_func_t interpret_relay;
+static interpret_func_t interpret_error;
+static interpret_func_t interpret_hurl;
+static interpret_func_t interpret_motm;
+static interpret_func_t interpret_rteadd;
+
+
+static taginfo_t taginfo_array[] = {
+ { "End-Of-List", POETT_END, interpret_hexdata },
+ { "Service-Name", POETT_SERVICE, interpret_service },
+ { "AC-Name", POETT_ACCESS, interpret_access },
+ { "Host-Uniq", POETT_UNIQ, interpret_hexdata },
+ { "AC-Cookie", POETT_COOKIE, interpret_cookie },
+ { "Vendor-Specific", POETT_VENDOR, interpret_vendor },
+ { "Relay-Session-Id", POETT_RELAY, interpret_relay },
+ { "Service-Name-Error", POETT_NAMERR, interpret_error },
+ { "AC-System-Error", POETT_SYSERR, interpret_error },
+ { "Generic-Error", POETT_GENERR, interpret_error },
+ { "Multicast-Capable", POETT_MULTI, interpret_hexdata },
+ { "Host-URL", POETT_HURL, interpret_hurl },
+ { "Message-Of-The-Minute", POETT_MOTM, interpret_motm },
+ { "IP-Route-Add", POETT_RTEADD, interpret_rteadd },
+ { "Unknown TAG", 0, NULL }
+};
+
+
+int
+interpret_pppoe(int flags, poep_t *poep, int len)
+{
+ uint8_t code = poep->poep_code;
+ uint8_t *payload;
+
+ if (len < sizeof (poep_t))
+ return (len);
+
+ payload = (uint8_t *)poep + sizeof (poep_t);
+
+ if (flags & F_SUM) {
+ (void) sprintf(get_sum_line(), "PPPoE %s",
+ pppoe_codetoname(code, B_FALSE));
+ } else { /* flags & F_DTAIL */
+ show_header("PPPoE: ", "PPP Over Ethernet", len);
+ show_space();
+
+ (void) sprintf(get_line(0, 0),
+ "Version = %d", POE_VERS(poep->poep_version_type));
+
+ (void) sprintf(get_line(0, 0),
+ "Type = %d", POE_TYPE(poep->poep_version_type));
+
+ (void) sprintf(get_line(0, 0),
+ "Code = %d (%s)", code, pppoe_codetoname(code, B_TRUE));
+
+ (void) sprintf(get_line(0, 0),
+ "Session Id = %d", ntohs(poep->poep_session_id));
+
+ (void) sprintf(get_line(0, 0),
+ "Length = %d bytes", ntohs(poep->poep_length));
+
+ show_space();
+
+ len -= sizeof (poep_t);
+ len = MIN(len, ntohs(poep->poep_length));
+
+ if (poep->poep_code != 0 && poep->poep_length > 0) {
+ interpret_tags(payload, len);
+ }
+ }
+
+ if (poep->poep_code == 0) {
+ return (interpret_ppp(flags, payload, len));
+ }
+ return (len);
+}
+
+
+/*
+ * interpret_tags() prints PPPoE Discovery Stage TAGs in detail.
+ */
+static void
+interpret_tags(uint8_t *payload, uint16_t length)
+{
+ uint8_t *tagptr = payload;
+ uint16_t tag_length;
+ uint16_t tag_type;
+ uint8_t *tag_value;
+ taginfo_t *tinfo;
+
+ while (length >= POET_HDRLEN) {
+ tag_type = POET_GET_TYPE(tagptr);
+ tag_length = POET_GET_LENG(tagptr);
+
+ tinfo = pppoe_gettaginfo(tag_type);
+
+ show_header("PPPoE: ", tinfo->tag_name,
+ tag_length + POET_HDRLEN);
+
+ (void) sprintf(get_line(0, 0),
+ "Tag Type = %d", tag_type);
+
+ (void) sprintf(get_line(0, 0),
+ "Tag Length = %d bytes", tag_length);
+
+ length -= POET_HDRLEN;
+ if (tag_length > length) {
+ (void) sprintf(get_line(0, 0),
+ "Warning: Truncated Packet");
+ show_space();
+ break;
+ }
+
+ /*
+ * unknown tags or tags which should always have 0 length
+ * are not interpreted any further.
+ */
+ tag_value = POET_DATA(tagptr);
+ if (tag_length != 0 && tinfo->interpret_tagvalue != NULL)
+ tinfo->interpret_tagvalue(tag_value, tag_length);
+
+ show_space();
+ length -= tag_length;
+ tagptr = POET_NEXT(tagptr);
+ }
+}
+
+static char *
+pppoe_codetoname(int code, boolean_t verbose)
+{
+ char *name;
+
+ switch (code) {
+ case POECODE_DATA:
+ name = "Session";
+ break;
+ case POECODE_PADO:
+ if (verbose)
+ name = "Active Discovery Offer";
+ else
+ name = "PADO";
+ break;
+ case POECODE_PADI:
+ if (verbose)
+ name = "Active Discovery Initiation";
+ else
+ name = "PADI";
+ break;
+ case POECODE_PADR:
+ if (verbose)
+ name = "Active Discovery Request";
+ else
+ name = "PADR";
+ break;
+ case POECODE_PADS:
+ if (verbose)
+ name = "Active Discovery Session-Confirmation";
+ else
+ name = "PADS";
+ break;
+ case POECODE_PADT:
+ if (verbose)
+ name = "Active Discovery Terminate";
+ else
+ name = "PADT";
+ break;
+ case POECODE_PADM:
+ if (verbose)
+ name = "Active Discovery Message";
+ else
+ name = "PADM";
+ break;
+ case POECODE_PADN:
+ if (verbose)
+ name = "Active Discovery Network";
+ else
+ name = "PADN";
+ break;
+ default:
+ name = "Unknown Code";
+ }
+
+ return (name);
+}
+
+static taginfo_t *
+pppoe_gettaginfo(uint16_t type)
+{
+ taginfo_t *taginfo_ptr = &taginfo_array[0];
+ int i = 0;
+
+ while (taginfo_ptr->tag_type != type &&
+ taginfo_ptr->interpret_tagvalue != NULL) {
+ taginfo_ptr = &taginfo_array[++i];
+ }
+
+ return (taginfo_ptr);
+}
+
+static void
+interpret_hexdata(uint8_t *tag_value, uint16_t tag_length)
+{
+ char *endofline;
+
+ endofline = print_linetag("Data = ");
+ print_hexdata(endofline, tag_value, tag_length);
+}
+
+static void
+interpret_service(uint8_t *tag_value, uint16_t tag_length)
+{
+ char *endofline;
+
+ endofline = print_linetag("Service Name = ");
+ print_utf8string(endofline, (char *)tag_value, tag_length);
+}
+
+static void
+interpret_access(uint8_t *tag_value, uint16_t tag_length)
+{
+ char *endofline;
+
+ endofline = print_linetag("AC Name = ");
+ print_utf8string(endofline, (char *)tag_value, tag_length);
+}
+
+static void
+interpret_cookie(uint8_t *tag_value, uint16_t tag_length)
+{
+ char *endofline;
+
+ endofline = print_linetag("Cookie = ");
+ print_hexdata(endofline, tag_value, tag_length);
+}
+
+static void
+interpret_vendor(uint8_t *tag_value, uint16_t tag_length)
+{
+ uint8_t *vendor_data;
+ uint32_t vendorid;
+ char *endofline;
+
+ vendorid = ntohl(*(uint32_t *)tag_value);
+ (void) sprintf(get_line(0, 0),
+ "Vendor ID = %d", vendorid);
+
+ if (tag_length > 4) {
+ vendor_data = tag_value + 4;
+ endofline = print_linetag("Vendor Data = ");
+ print_hexdata(endofline, vendor_data, tag_length - 4);
+ }
+}
+
+static void
+interpret_relay(uint8_t *tag_value, uint16_t tag_length)
+{
+ char *endofline;
+
+ endofline = print_linetag("ID = ");
+ print_hexdata(endofline, tag_value, tag_length);
+}
+
+static void
+interpret_error(uint8_t *tag_value, uint16_t tag_length)
+{
+ char *endofline;
+
+ endofline = print_linetag("Error = ");
+ print_utf8string(endofline, (char *)tag_value, tag_length);
+}
+
+static void
+interpret_hurl(uint8_t *tag_value, uint16_t tag_length)
+{
+ char *endofline;
+
+ endofline = print_linetag("URL = ");
+ print_utf8string(endofline, (char *)tag_value, tag_length);
+}
+
+static void
+interpret_motm(uint8_t *tag_value, uint16_t tag_length)
+{
+ char *endofline;
+
+ endofline = print_linetag("Message = ");
+ print_utf8string(endofline, (char *)tag_value, tag_length);
+}
+
+static void
+interpret_rteadd(uint8_t *tag_value, uint16_t tag_length)
+{
+ char dest[INET_ADDRSTRLEN];
+ char mask[INET_ADDRSTRLEN];
+ char gateway[INET_ADDRSTRLEN];
+ uint32_t metric;
+
+ if (tag_length == 16) {
+ (void) inet_ntop(AF_INET, tag_value, dest,
+ INET_ADDRSTRLEN);
+ (void) inet_ntop(AF_INET, &tag_value[4], mask,
+ INET_ADDRSTRLEN);
+ (void) inet_ntop(AF_INET, &tag_value[8], gateway,
+ INET_ADDRSTRLEN);
+ metric = ntohl(*(uint32_t *)&tag_value[12]);
+ sprintf(get_line(0, 0),
+ "Destination\tNetmask\tGateway\tMetric");
+ sprintf(get_line(0, 0),
+ "%s\t%s\t%s\t%d", dest, mask, gateway, metric);
+ }
+}
+
+static void
+print_hexdata(char *line, uint8_t *data, uint16_t length)
+{
+ uint16_t index = 0;
+
+ line += sprintf(line, "0x");
+
+ while (index < length) {
+ line += sprintf(line, "%02x", data[index++]);
+ }
+}
+
+static void
+print_utf8string(char *firstline, char *string, uint16_t length)
+{
+ (void) sprintf(firstline, "%.*s", length, string);
+}
+
+static char *
+print_linetag(char *string)
+{
+ char *line = get_line(0, 0);
+ return (line + sprintf(line, string));
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rip.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rip.c
new file mode 100644
index 0000000000..a421be2e9b
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rip.c
@@ -0,0 +1,319 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1991-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#define RIPVERSION RIPv2
+#include <protocols/routed.h>
+#include "snoop.h"
+
+static char *show_cmd(int);
+
+int
+interpret_rip(int flags, struct rip *rip, int fraglen)
+{
+ const struct netinfo *nip;
+ const struct entryinfo *ep;
+ const struct netauth *nap;
+ int len, count;
+ const char *cmdstr, *auth;
+ struct in_addr dst;
+ uint32_t mval;
+ const struct sockaddr_in *sin;
+ /* Room for IP destination + "/" + IP mask */
+ char addrstr[15+1+15+1];
+ /* Room for "RIPv" + uint8_t as %d */
+ char ripvers[4+3+1];
+
+ /* RIP header is 4 octets long */
+ if ((len = fraglen - 4) < 0)
+ return (0);
+
+ if (flags & F_SUM) {
+ switch (rip->rip_cmd) {
+ case RIPCMD_REQUEST: cmdstr = "C"; break;
+ case RIPCMD_RESPONSE: cmdstr = "R"; break;
+ case RIPCMD_TRACEON: cmdstr = "Traceon"; break;
+ case RIPCMD_TRACEOFF: cmdstr = "Traceoff"; break;
+ case RIPCMD_POLL: cmdstr = "Poll"; break;
+ case RIPCMD_POLLENTRY: cmdstr = "Poll entry"; break;
+ default: cmdstr = "?"; break;
+ }
+
+ if (rip->rip_vers == RIPv1)
+ (void) strlcpy(ripvers, "RIP", sizeof (ripvers));
+ else
+ (void) snprintf(ripvers, sizeof (ripvers), "RIPv%d",
+ rip->rip_vers);
+
+ switch (rip->rip_cmd) {
+ case RIPCMD_REQUEST:
+ case RIPCMD_RESPONSE:
+ case RIPCMD_POLL:
+ nip = rip->rip_nets;
+ auth = "";
+ if (len >= sizeof (*nip) &&
+ nip->n_family == RIP_AF_AUTH) {
+ nap = (struct netauth *)nip;
+ len -= sizeof (*nip);
+ if (nap->a_type == RIP_AUTH_MD5 &&
+ len >= ntohs(nap->au.a_md5.md5_auth_len))
+ len -= ntohs(nap->au.a_md5.
+ md5_auth_len);
+ auth = " +Auth";
+ }
+ count = len / sizeof (*nip);
+ len %= sizeof (*nip);
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "%s %s (%d destinations%s%s)", ripvers, cmdstr,
+ count, (len != 0 ? "?" : ""), auth);
+ break;
+
+ case RIPCMD_TRACEON:
+ case RIPCMD_TRACEOFF:
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "%s %s File=\"%.*s\"", ripvers, cmdstr, len,
+ rip->rip_tracefile);
+ len = 0;
+ break;
+
+ default:
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "%s %d (%s)", ripvers, rip->rip_cmd, cmdstr);
+ len = 0;
+ break;
+ }
+ }
+
+ if (flags & F_DTAIL) {
+
+ len = fraglen - 4;
+ show_header("RIP: ", "Routing Information Protocol", fraglen);
+ show_space();
+ (void) snprintf(get_line((char *)rip->rip_cmd - dlc_header, 1),
+ get_line_remain(), "Opcode = %d (%s)", rip->rip_cmd,
+ show_cmd(rip->rip_cmd));
+ (void) snprintf(get_line((char *)rip->rip_vers - dlc_header, 1),
+ get_line_remain(), "Version = %d", rip->rip_vers);
+
+ switch (rip->rip_cmd) {
+ case RIPCMD_REQUEST:
+ case RIPCMD_RESPONSE:
+ case RIPCMD_POLL:
+ show_space();
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Destination Next Hop "
+ "Tag Metric");
+ for (nip = rip->rip_nets; len >= sizeof (*nip); nip++,
+ len -= sizeof (*nip)) {
+ if (nip->n_family == RIP_AF_AUTH) {
+ nap = (const struct netauth *)nip;
+ if (nap->a_type == RIP_AUTH_NONE) {
+ (void) snprintf(get_line
+ ((char *)nip - dlc_header,
+ sizeof (*nip)),
+ get_line_remain(),
+ " *** Auth None");
+ } else if (nap->a_type == RIP_AUTH_PW) {
+ (void) snprintf(get_line
+ ((char *)nip - dlc_header,
+ sizeof (*nip)),
+ get_line_remain(),
+ " *** Auth PW \"%.*s\"",
+ RIP_AUTH_PW_LEN,
+ nap->au.au_pw);
+ } else if (nap->a_type ==
+ RIP_AUTH_MD5) {
+ (void) snprintf(get_line
+ ((char *)nip - dlc_header,
+ sizeof (*nip)),
+ get_line_remain(),
+ " *** Auth MD5 pkt len %d, "
+ "keyid %d, sequence %08lX, "
+ "authlen %d",
+ ntohs(nap->au.a_md5.
+ md5_pkt_len),
+ nap->au.a_md5.md5_keyid,
+ ntohl(nap->au.a_md5.
+ md5_seqno),
+ ntohs(nap->au.a_md5.
+ md5_auth_len));
+ if (len - sizeof (*nip) >=
+ ntohs(nap->au.a_md5.
+ md5_auth_len))
+ len -= ntohs(nap->au.
+ a_md5.md5_auth_len);
+ else
+ len = sizeof (*nip);
+ } else {
+ (void) snprintf(get_line
+ ((char *)nip - dlc_header,
+ sizeof (*nip)),
+ get_line_remain(),
+ " *** Auth Type %d?",
+ ntohs(nap->a_type));
+ }
+ continue;
+ }
+ if (nip->n_family == RIP_AF_UNSPEC &&
+ rip->rip_cmd == RIPCMD_REQUEST) {
+ (void) snprintf(get_line((char *)nip -
+ dlc_header, sizeof (*nip)),
+ get_line_remain(),
+ " *** All routes");
+ continue;
+ }
+ if (nip->n_family != RIP_AF_INET) {
+ (void) snprintf(get_line((char *)nip -
+ dlc_header, sizeof (*nip)),
+ get_line_remain(),
+ " *** Address Family %d?",
+ ntohs(nip->n_family));
+ continue;
+ }
+ if (nip->n_dst == htonl(RIP_DEFAULT)) {
+ (void) strcpy(addrstr, "default");
+ } else {
+ dst.s_addr = nip->n_dst;
+ (void) strlcpy(addrstr, inet_ntoa(dst),
+ sizeof (addrstr));
+ }
+ if (nip->n_dst != htonl(RIP_DEFAULT) &&
+ rip->rip_vers >= RIPv2) {
+ count = strlen(addrstr);
+ mval = ntohl(nip->n_mask);
+ /* LINTED */
+ if (mval == INADDR_ANY) {
+ /* No mask */;
+ } else if ((mval + (mval & -mval)) ==
+ 0) {
+ (void) snprintf(addrstr + count,
+ sizeof (addrstr) - count,
+ "/%d", 33 - ffs(mval));
+ } else {
+ dst.s_addr = nip->n_mask;
+ (void) snprintf(addrstr + count,
+ sizeof (addrstr) - count,
+ "/%s", inet_ntoa(dst));
+ }
+ }
+ dst.s_addr = nip->n_nhop;
+ mval = ntohl(nip->n_metric);
+ (void) snprintf(get_line((char *)nip -
+ dlc_header, sizeof (*nip)),
+ get_line_remain(),
+ "%-31s %-15s %-6d %d%s",
+ addrstr,
+ dst.s_addr == htonl(INADDR_ANY) ?
+ "--" : addrtoname(AF_INET, &dst),
+ ntohs(nip->n_tag),
+ mval,
+ (mval == HOPCNT_INFINITY ?
+ " (not reachable)" : ""));
+ }
+ break;
+
+ case RIPCMD_POLLENTRY:
+ if (len < sizeof (*ep))
+ break;
+ len -= sizeof (*ep);
+ ep = (const struct entryinfo *)rip->rip_nets;
+ /* LINTED */
+ sin = (const struct sockaddr_in *)&ep->rtu_dst;
+ (void) snprintf(get_line((char *)sin - dlc_header,
+ sizeof (struct sockaddr)), get_line_remain(),
+ "Destination = %s %s",
+ inet_ntoa(sin->sin_addr),
+ addrtoname(AF_INET, (void *)&sin->sin_addr));
+ /* LINTED */
+ sin = (const struct sockaddr_in *)&ep->rtu_router;
+ (void) snprintf(get_line((char *)sin - dlc_header,
+ sizeof (struct sockaddr)), get_line_remain(),
+ "Router = %s %s",
+ inet_ntoa(sin->sin_addr),
+ addrtoname(AF_INET, (void *)&sin->sin_addr));
+ (void) snprintf(get_line((char *)&ep->rtu_flags -
+ dlc_header, 2), get_line_remain(),
+ "Flags = %4x", (unsigned)ep->rtu_flags);
+ (void) snprintf(get_line((char *)&ep->rtu_state -
+ dlc_header, 2), get_line_remain(),
+ "State = %d", ep->rtu_state);
+ (void) snprintf(get_line((char *)&ep->rtu_timer -
+ dlc_header, 4), get_line_remain(),
+ "Timer = %d", ep->rtu_timer);
+ (void) snprintf(get_line((char *)&ep->rtu_metric -
+ dlc_header, 4), get_line_remain(),
+ "Metric = %d", ep->rtu_metric);
+ (void) snprintf(get_line((char *)&ep->int_flags -
+ dlc_header, 4), get_line_remain(),
+ "Int flags = %8x", ep->int_flags);
+ (void) snprintf(get_line((char *)ep->int_name -
+ dlc_header, sizeof (ep->int_name)),
+ get_line_remain(),
+ "Int name = \"%.*s\"", sizeof (ep->int_name),
+ ep->int_name);
+ break;
+
+ case RIPCMD_TRACEON:
+ case RIPCMD_TRACEOFF:
+ (void) snprintf(get_line((char *)rip->rip_tracefile -
+ dlc_header, 2), get_line_remain(),
+ "Trace file = %.*s", len, rip->rip_tracefile);
+ len = 0;
+ break;
+ }
+ }
+
+ return (fraglen - len);
+}
+
+static char *
+show_cmd(int c)
+{
+ switch (c) {
+ case RIPCMD_REQUEST:
+ return ("route request");
+ case RIPCMD_RESPONSE:
+ return ("route response");
+ case RIPCMD_TRACEON:
+ return ("route trace on");
+ case RIPCMD_TRACEOFF:
+ return ("route trace off");
+ case RIPCMD_POLL:
+ return ("route poll");
+ case RIPCMD_POLLENTRY:
+ return ("route poll entry");
+ }
+ return ("?");
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rip6.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rip6.c
new file mode 100644
index 0000000000..ed1e491bfb
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rip6.c
@@ -0,0 +1,144 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1991-1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI" /* SunOS */
+
+#include <ctype.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <net/if.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <inet/led.h>
+#include <inet/ip6.h>
+#include <arpa/inet.h>
+#include <protocols/routed.h>
+#include <protocols/ripngd.h>
+#include "snoop.h"
+
+extern char *dlc_header;
+static char *show_cmd6();
+
+static struct in6_addr all_zeroes_addr = { { 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0 } };
+
+interpret_rip6(flags, rip6, fraglen)
+ int flags;
+ struct rip6 *rip6;
+ int fraglen;
+{
+ char *p;
+ struct netinfo6 *n;
+ int len, count;
+ struct in6_addr *dst;
+ int notdefault = 0;
+ char dststr[INET6_ADDRSTRLEN];
+
+ if (flags & F_SUM) {
+ switch (rip6->rip6_cmd) {
+ case RIPCMD6_REQUEST: p = "C"; break;
+ case RIPCMD6_RESPONSE: p = "R"; break;
+ default: p = "?"; break;
+ }
+
+ switch (rip6->rip6_cmd) {
+ case RIPCMD6_REQUEST:
+ case RIPCMD6_RESPONSE:
+ len = fraglen - 4;
+ count = 0;
+ for (n = rip6->rip6_nets;
+ len >= sizeof (struct netinfo6); n++) {
+ count++;
+ len -= sizeof (struct netinfo6);
+ }
+ (void) sprintf(get_sum_line(),
+ "RIPng %s (%d destinations)", p, count);
+ break;
+ default:
+ (void) sprintf(get_sum_line(), "RIPng %s", p);
+ break;
+ }
+ }
+
+ if (flags & F_DTAIL) {
+
+ show_header("RIPng: ", "Routing Information Protocol for IPv6",
+ fraglen);
+ show_space();
+ (void) sprintf(get_line((char *)rip6->rip6_cmd - dlc_header, 1),
+ "Opcode = %d (%s)",
+ rip6->rip6_cmd,
+ show_cmd6(rip6->rip6_cmd));
+ (void) sprintf(get_line((char *)rip6->rip6_vers -
+ dlc_header, 1), "Version = %d",
+ rip6->rip6_vers);
+
+ switch (rip6->rip6_cmd) {
+ case RIPCMD6_REQUEST:
+ case RIPCMD6_RESPONSE:
+ show_space();
+ (void) sprintf(get_line(0, 0), "Address"
+ " Prefix Metric");
+ len = fraglen - 4;
+ for (n = rip6->rip6_nets;
+ len >= sizeof (struct netinfo6); n++) {
+ if (rip6->rip6_vers > 0) {
+ n->rip6_metric = n->rip6_metric;
+ }
+ dst = &n->rip6_prefix;
+ notdefault = bcmp((caddr_t *)dst,
+ (caddr_t *)&all_zeroes_addr, sizeof (*dst));
+ (void) inet_ntop(AF_INET6, (char *)dst, dststr,
+ INET6_ADDRSTRLEN);
+ (void) sprintf(get_line((char *)n - dlc_header,
+ sizeof (struct netinfo6)),
+ "%-30s %-10d %-5d %s",
+ notdefault ? dststr :
+ " (default)", n->rip6_prefix_length,
+ n->rip6_metric, n->rip6_metric == 16 ?
+ " (not reachable)":"");
+ len -= sizeof (struct netinfo);
+ }
+ break;
+ }
+ }
+ return (fraglen);
+}
+
+static char *
+show_cmd6(c)
+ int c;
+{
+ switch (c) {
+ case RIPCMD6_REQUEST: return ("route request");
+ case RIPCMD6_RESPONSE: return ("route response");
+ }
+ return ("?");
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rpc.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rpc.c
new file mode 100644
index 0000000000..be74bf0819
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rpc.c
@@ -0,0 +1,775 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1991-1998,2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <setjmp.h>
+
+#include <netinet/in.h>
+#include <netdb.h>
+
+#include <sys/tiuser.h>
+#include <rpc/types.h>
+#include <rpc/xdr.h>
+#include <rpc/auth.h>
+#include <rpc/auth_unix.h>
+#include <rpc/auth_des.h>
+#include <rpc/clnt.h>
+#include <rpc/rpc_msg.h>
+#include <rpc/pmap_clnt.h>
+#include <rpc/svc.h>
+#include <rpcsvc/yp_prot.h>
+#include <rpc/pmap_prot.h>
+#include "snoop.h"
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+int pos;
+struct cache_struct *find_xid();
+extern jmp_buf xdr_err;
+void protoprint();
+void print_rpcsec_gss_cred(int xid, int authlen);
+char *nameof_prog();
+char *nameof_astat();
+char *nameof_why();
+
+#define LAST_FRAG ((ulong_t)1 << 31)
+
+interpret_rpc(flags, rpc, fraglen, type)
+ int flags;
+ char *rpc;
+ int fraglen, type;
+{
+ ulong_t xid;
+ int direction;
+ struct cache_struct *x;
+ int rpcvers, prog, vers, proc;
+ int status, astat, rstat, why;
+ char *lp;
+ unsigned recmark;
+ int markpos;
+ extern int pi_frame;
+ int lo, hi;
+
+ xdr_init(rpc, fraglen);
+
+ if (setjmp(xdr_err)) {
+ if (flags & F_DTAIL)
+ (void) sprintf(get_line(0, 0),
+ "---- short frame ---");
+ return (fraglen);
+ }
+
+ if (type == IPPROTO_TCP) { /* record mark */
+ markpos = getxdr_pos();
+ recmark = getxdr_long();
+ }
+
+ xid = getxdr_u_long();
+ direction = getxdr_long();
+
+ if (direction == CALL) {
+ rpcvers = getxdr_long();
+ pos = getxdr_pos();
+ prog = getxdr_long();
+ vers = getxdr_long();
+ proc = getxdr_long();
+ stash_xid(xid, pi_frame, prog, vers, proc);
+ if (!(flags & (F_SUM | F_DTAIL)))
+ protoprint(flags, CALL, xid, prog, vers, proc,
+ rpc, fraglen);
+ } else {
+ x = find_xid(xid);
+ }
+
+ if (flags & F_SUM) {
+ switch (direction) {
+ case CALL:
+ (void) sprintf(get_sum_line(),
+ "RPC C XID=%lu PROG=%d (%s) VERS=%d PROC=%d",
+ xid,
+ prog, nameof_prog(prog),
+ vers, proc);
+ if (getxdr_long() == RPCSEC_GSS) { /* Cred auth type */
+ extract_rpcsec_gss_cred_info(xid);
+ /* RPCSEC_GSS cred auth data */
+ } else {
+ xdr_skip(getxdr_long());
+ /* non RPCSEC_GSS cred auth data */
+ }
+ xdr_skip(4); /* Verf auth type */
+ xdr_skip(RNDUP(getxdr_long())); /* Verf auth data */
+
+ protoprint(flags, CALL, xid, prog, vers, proc,
+ rpc, fraglen);
+ break;
+
+ case REPLY:
+ lp = get_sum_line();
+ if (x == NULL)
+ (void) sprintf(lp, "RPC R XID=%lu", xid);
+ else
+ (void) sprintf(lp, "RPC R (#%d) XID=%lu",
+ x->xid_frame, xid);
+
+ lp += strlen(lp);
+ status = getxdr_long();
+ switch (status) {
+ case MSG_ACCEPTED:
+ /* eat flavor and verifier */
+ (void) getxdr_long();
+ xdr_skip(RNDUP(getxdr_long()));
+ astat = getxdr_long();
+ (void) sprintf(lp, " %s",
+ nameof_astat(astat));
+ lp += strlen(lp);
+
+ switch (astat) {
+ case SUCCESS:
+ if (x) {
+ protoprint(flags, REPLY,
+ xid,
+ x->xid_prog,
+ x->xid_vers,
+ x->xid_proc,
+ rpc, fraglen);
+ }
+ break;
+
+ case PROG_UNAVAIL :
+ case PROG_MISMATCH:
+ case PROC_UNAVAIL :
+ lo = getxdr_long();
+ hi = getxdr_long();
+ (void) sprintf(lp,
+ " (low=%d, high=%d)",
+ lo, hi);
+ break;
+
+ case GARBAGE_ARGS:
+ case SYSTEM_ERR:
+ default:
+ ;
+ }
+ break;
+
+ case MSG_DENIED:
+ rstat = getxdr_long();
+
+ switch (rstat) {
+ case RPC_MISMATCH:
+ lo = getxdr_long();
+ hi = getxdr_long();
+ (void) sprintf(lp,
+ " Vers mismatch (low=%d, high=%d)",
+ lo, hi);
+ break;
+
+ case AUTH_ERROR:
+ why = getxdr_u_long();
+ (void) sprintf(lp,
+ " Can't authenticate (%s)",
+ nameof_why(why));
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("RPC: ", "SUN RPC Header", fraglen);
+ show_space();
+ if (type == IPPROTO_TCP) { /* record mark */
+ (void) sprintf(get_line(markpos, markpos+4),
+ "Record Mark: %s fragment, length = %d",
+ recmark & LAST_FRAG ? "last" : "",
+ recmark & ~LAST_FRAG);
+ }
+
+ (void) sprintf(get_line(0, 0),
+ "Transaction id = %lu",
+ xid);
+ (void) sprintf(get_line(0, 0),
+ "Type = %d (%s)",
+ direction,
+ direction == CALL ? "Call":"Reply");
+
+ switch (direction) {
+ case CALL:
+ rpc_detail_call(flags, xid, rpcvers,
+ prog, vers, proc, rpc, fraglen);
+ break;
+ case REPLY:
+ rpc_detail_reply(flags, xid, x, rpc, fraglen);
+ break;
+ }
+ }
+
+ return (fraglen);
+}
+
+rpc_detail_call(flags, xid, rpcvers, prog, vers, proc, data, len)
+ int flags, xid, rpcvers, prog, vers, proc;
+ char *data;
+ int len;
+{
+ char *nameof_flavor();
+ char *nameof_prog();
+
+ (void) sprintf(get_line(pos, getxdr_pos()),
+ "RPC version = %d",
+ rpcvers);
+ (void) sprintf(get_line(pos, getxdr_pos()),
+ "Program = %d (%s), version = %d, procedure = %d",
+ prog, nameof_prog(prog), vers, proc);
+ print_creds(xid);
+ print_verif(CALL);
+ show_trailer();
+ protoprint(flags, CALL, xid, prog, vers, proc, data, len);
+}
+
+char *
+nameof_flavor(flavor)
+ int flavor;
+{
+ switch (flavor) {
+ case AUTH_NONE : return ("None");
+ case AUTH_UNIX : return ("Unix");
+ case AUTH_SHORT: return ("Unix short");
+ case AUTH_DES : return ("DES");
+ case RPCSEC_GSS: return ("RPCSEC_GSS");
+ default: return ("unknown");
+ }
+}
+
+char *
+tohex(char *p, int len)
+{
+ int i, j;
+ static char hbuff[1024];
+ static char *hexstr = "0123456789ABCDEF";
+ char toobig = 0;
+
+ if (len * 2 > sizeof (hbuff)) {
+ toobig++;
+ len = sizeof (hbuff) / 2;
+ }
+
+ j = 0;
+ for (i = 0; i < len; i++) {
+ hbuff[j++] = hexstr[p[i] >> 4 & 0x0f];
+ hbuff[j++] = hexstr[p[i] & 0x0f];
+ }
+
+ if (toobig) {
+ hbuff[len * 2 - strlen("<Too Long>")] = '\0';
+ strcat(hbuff, "<Too Long>");
+ } else
+ hbuff[j] = '\0';
+
+ return (hbuff);
+}
+
+print_creds(int xid)
+{
+ int pos, flavor, authlen;
+ int uid, gid, len;
+ int tlen, idlen;
+ int i, namekind;
+ char *p, *line;
+
+ pos = getxdr_pos();
+ flavor = getxdr_long();
+ authlen = getxdr_long();
+ (void) sprintf(get_line(pos, getxdr_pos()),
+ "Credentials: Flavor = %d (%s), len = %d bytes",
+ flavor, nameof_flavor(flavor), authlen);
+ if (authlen <= 0)
+ return;
+
+ switch (flavor) {
+ case AUTH_UNIX:
+ (void) showxdr_time(" Time = %s");
+ (void) showxdr_string(MAX_MACHINE_NAME, " Hostname = %s");
+ pos = getxdr_pos();
+ uid = getxdr_u_long();
+ gid = getxdr_u_long();
+ (void) sprintf(get_line(pos, getxdr_pos()),
+ " Uid = %d, Gid = %d",
+ uid, gid);
+ len = getxdr_u_long();
+ line = get_line(pos, len * 4);
+ if (len == 0)
+ (void) sprintf(line, " Groups = (none)");
+ else {
+ (void) sprintf(line, " Groups = ");
+ line += strlen(line);
+ while (len--) {
+ gid = getxdr_u_long();
+ (void) sprintf(line, "%d ", gid);
+ line += strlen(line);
+ }
+ }
+ break;
+
+ case AUTH_DES:
+ namekind = getxdr_u_long();
+ (void) sprintf(get_line(pos, getxdr_pos()),
+ " Name kind = %d (%s)",
+ namekind,
+ namekind == ADN_FULLNAME ?
+ "fullname" : "nickname");
+ switch (namekind) {
+ case ADN_FULLNAME:
+ (void) showxdr_string(64,
+ " Network name = %s");
+ (void) showxdr_hex(8,
+ " Conversation key = 0x%s (DES encrypted)");
+ (void) showxdr_hex(4,
+ " Window = 0x%s (DES encrypted)");
+ break;
+
+ case ADN_NICKNAME:
+ (void) showxdr_hex(4, " Nickname = 0x%s");
+ break;
+ };
+ break;
+
+ case RPCSEC_GSS:
+ print_rpcsec_gss_cred(xid, authlen);
+ break;
+
+ default:
+ (void) showxdr_hex(authlen, "[%s]");
+ break;
+ }
+}
+
+print_verif(direction)
+ int direction;
+{
+ int pos, flavor, verlen;
+
+ pos = getxdr_pos();
+ flavor = getxdr_long();
+ verlen = getxdr_long();
+ (void) sprintf(get_line(pos, getxdr_pos()),
+ "Verifier : Flavor = %d (%s), len = %d bytes",
+ flavor, nameof_flavor(flavor), verlen);
+ if (verlen == 0)
+ return;
+
+ switch (flavor) {
+ case AUTH_DES:
+ (void) showxdr_hex(8, " Timestamp = 0x%s (DES encrypted)");
+ if (direction == CALL)
+ (void) showxdr_hex(4,
+ " Window = 0x%s (DES encrypted)");
+ else
+ (void) showxdr_hex(4, " Nickname = 0x%s");
+ break;
+
+ /* For other flavors like AUTH_NONE, AUTH_UNIX, RPCSEC_GSS etc. */
+ default:
+ (void) showxdr_hex(verlen, "[%s]");
+ break;
+ }
+}
+
+struct rpcnames {
+ int rp_prog;
+ char *rp_name;
+} rpcnames[] = {
+100000, "PMAP", /* Portmapper */
+100001, "RSTAT", /* Remote stats */
+100002, "RUSERS", /* Remote users */
+100003, "NFS", /* Nfs */
+100004, "NIS", /* Network Information Service */
+100005, "MOUNT", /* Mount demon */
+100006, "DBX", /* Remote dbx */
+100007, "NISBIND", /* NIS binder */
+100008, "WALL", /* Shutdown msg */
+100009, "NISPASSWD", /* Yppasswd server */
+100010, "ETHERSTAT", /* Ether stats */
+100011, "RQUOTA", /* Disk quotas */
+100012, "SPRAY", /* Spray packets */
+100013, "IBM3270", /* 3270 mapper */
+100014, "IBMRJE", /* RJE mapper */
+100015, "SELNSVC", /* Selection service */
+100016, "RDATABASE", /* Remote database access */
+100017, "REX", /* Remote execution */
+100018, "ALICE", /* Alice Office Automation */
+100019, "SCHED", /* Scheduling service */
+100020, "LLM", /* Local lock manager */
+100021, "NLM", /* Network lock manager */
+100022, "X25INR", /* X.25 inr protocol */
+100023, "STATMON1", /* Status monitor 1 */
+100024, "STATMON2", /* Status monitor 2 */
+100025, "SELNLIB", /* Selection library */
+100026, "BOOTPARAM", /* Boot parameters service */
+100027, "MAZEPROG", /* Mazewars game */
+100028, "NISUPDATE", /* NIS update */
+100029, "KEYSERVE", /* Key server */
+100030, "SECURECMD", /* Secure login */
+100031, "NETFWDI", /* NFS net forwarder init */
+100032, "NETFWDT", /* NFS net forwarder trans */
+100033, "SUNLINKMAP", /* Sunlink MAP */
+100034, "NETMON", /* Network monitor */
+100035, "DBASE", /* Lightweight database */
+100036, "PWDAUTH", /* Password authorization */
+100037, "TFS", /* Translucent file svc */
+100038, "NSE", /* NSE server */
+100039, "NSE_ACTIVATE", /* NSE activate daemon */
+100040, "SUNVIEW_HELP", /* Sunview help */
+100041, "PNP", /* PNP install */
+100042, "IPADDR_ALLOC", /* IP addr allocator */
+100043, "FILEHANDLE", /* Show filehandle */
+100044, "MVSNFS", /* MVS NFS mount */
+100045, "REM_FILEOP_USER", /* Remote user file operations */
+100046, "BATCH_NISUPDATE", /* Batched ypupdate */
+100047, "NEM", /* Network execution mgr */
+100048, "RAYTRACE_RD", /* Raytrace/mandelbrot remote daemon */
+100049, "RAYTRACE_LD", /* Raytrace/mandelbrot local daemon */
+100050, "REM_FILEOP_GROUP", /* Remote group file operations */
+100051, "REM_FILEOP_SYSTEM", /* Remote system file operations */
+100052, "REM_SYSTEM_ROLE", /* Remote system role operations */
+100055, "IOADMD", /* Ioadmd */
+100056, "FILEMERGE", /* Filemerge */
+100057, "NAMEBIND", /* Name Binding Program */
+100058, "NJE", /* Sunlink NJE */
+100059, "MVSATTR", /* MVSNFS get attribute service */
+100060, "RMGR", /* SunAccess/SunLink resource manager */
+100061, "UIDALLOC", /* UID allocation service */
+100062, "LBSERVER", /* License broker */
+100063, "LBBINDER", /* NETlicense client binder */
+100064, "GIDALLOC", /* GID allocation service */
+100065, "SUNISAM", /* SunIsam */
+100066, "RDBSRV", /* Remote Debug Server */
+100067, "NETDIR", /* Network directory daemon */
+100068, "CMSD", /* Network calendar program */
+100069, "NISXFR", /* NIS transfer */
+100070, "TIMED", /* RPC.timed */
+100071, "BUGTRAQ", /* Bugtraqd */
+100072, "NeFS", /* Internal use only */
+100073, "BILLBOARD", /* Connectathon Billboard - NFS */
+100074, "BILLBOARD", /* Connectathon Billboard - X */
+100075, "SCHEDROOM", /* Sun meeting room scheduler */
+100076, "AUTHNEGOTIATE", /* Authentication negotiation */
+100077, "ATTRPROG", /* Database manipulation */
+100080, "AUTODUMP", /* Sun consulting special */
+100081, "EVENT_SVC", /* Event protocol */
+100085, "ARM_PSD", /* ARM policy */
+100086, "ARMTOD", /* ARM TOD */
+100087, "NA.ADMIN", /* Sun (SNAG) administration agent */
+100099, "PLD", /* Genesil 8.1 hot plot */
+100101, "NA.EVENT", /* SNM (SunNet Manager) event dispatcher */
+100102, "NA.LOGGER", /* SNM report logger */
+100103, "NA.DISCOVER", /* SNM network discovery agent */
+100104, "NA.SYNC", /* SNM sync interface agent */
+100105, "NA.DISKINFO", /* SNM disk info agent */
+100106, "NA.IOSTAT", /* SNM iostat agent */
+100107, "NA.HOSTPERF", /* SNM rstat proxy agent */
+100108, "NA.CONFIG", /* SNM host configuration agent */
+100109, "NA.ACTIVITY", /* SNM activity daemon */
+100111, "NA.LPSTAT", /* SNM printer agent */
+100112, "NA.HOSTMEM", /* SNM host network memory agent */
+100113, "NA.SAMPLE", /* SNM sample agent */
+100114, "NA.X25", /* SNM X.25 agent */
+100115, "NA.PING", /* SNM ping proxy agent */
+100116, "NA.RPCNFS", /* SNM rpc and nfs agent */
+100117, "NA.HOSTIF", /* SNM host interface agent */
+100118, "NA.ETHERIF", /* SNM ethernet interface agent */
+100119, "NA.IPPATH", /* SNM traceroute proxy agent */
+100120, "NA.IPROUTES", /* SNM routing table agent */
+100121, "NA.LAYERS", /* SNM protocol layers gent */
+100122, "NA.SNMP", /* SNM SNMP proxy agent */
+100123, "NA.TRAFFIC", /* SNM network traffic agent */
+100124, "NA.DNI", /* DNI (DECnet) proxy agent */
+100125, "NA.CHAT", /* IBM Channel attach proxy agent */
+100126, "NA.FDDI", /* FDDI agent */
+100127, "NA.FDDISMT", /* FDDI SMT proxy agent */
+100128, "NA.MHS", /* MHS agent */
+100130, "SNM_GRAPHER", /* SNM 3D grapher */
+100132, "NA.TR", /* Token Ring agent */
+100134, "NA.TOKENRING", /* Token Ring agent */
+100136, "NA.FRAMERELAY", /* Frame Relay agent */
+100175, "NA.SNMPTRAP", /* SNM SNMP trap daemon */
+100180, "NA.MIPROUTES", /* SNM multicast routing table agent */
+100201, "MVSNFSSTAT", /* MVS/NFS Memory usage statistic server */
+100227, "NFS_ACL", /* NFS ACL support */
+100300, "NIS+", /* NIS+ name service */
+100302, "NIS+ CB", /* NIS+ callbacks */
+101002, "NSELINKTOOL", /* NSE link daemon */
+101003, "NSELINKAPP", /* NSE link application */
+110001, "GOLABEL", /* SunOS MLS */
+110002, "PUC", /* SunOS MLS */
+150001, "PCNFSD", /* PC passwd authorization */
+150002, "TOPS", /* TOPS name mapping */
+150003, "TOPS", /* TOPS external attribute storage */
+150004, "TOPS", /* TOPS hierarchical file system */
+150005, "TOPS", /* TOPS NFS transparency extensions */
+150006, "SOLARNET_FW", /* SolarNet Framework protocol */
+160001, "CM", /* Nihon Sun - Japanese Input system */
+300004, "FRAME 1", /* Frame program 1 */
+300009, "FRAME 2", /* Frame program 2 */
+390101, "RAP", /* Legato RAP protocol */
+390102, "RAPRD", /* Legato RAP resource dir protocol */
+500021, "ZNS", /* Zeus Network Service */
+};
+
+compare(a, b)
+ register struct rpcnames *a, *b;
+{
+ return (a->rp_prog - b->rp_prog);
+}
+
+char *
+nameof_prog(prog)
+ int prog;
+{
+ struct rpcnames *r;
+ struct rpcnames *bsearch();
+ int elems = sizeof (rpcnames) / sizeof (*r);
+
+ r = bsearch(&prog, rpcnames, elems, sizeof (*r), compare);
+ if (r)
+ return (r->rp_name);
+
+ if (prog >= 0x40000000 && prog <= 0x5fffffff)
+ return ("transient");
+
+ return ("?");
+}
+
+char *
+nameof_astat(status)
+ int status;
+{
+ switch (status) {
+ case SUCCESS : return ("Success");
+ case PROG_UNAVAIL : return ("Program unavailable");
+ case PROG_MISMATCH: return ("Program number mismatch");
+ case PROC_UNAVAIL : return ("Procedure unavailable");
+ case GARBAGE_ARGS : return ("Garbage arguments");
+ case SYSTEM_ERR : return ("System error");
+ default: return ("unknown");
+ }
+}
+
+char *
+nameof_why(why)
+ int why;
+{
+ switch (why) {
+ case AUTH_BADCRED: return ("bogus credentials (seal broken)");
+ case AUTH_REJECTEDCRED: return ("client should begin new session");
+ case AUTH_BADVERF: return ("bogus verifier (seal broken)");
+ case AUTH_REJECTEDVERF: return ("verifier expired or was replayed");
+ case AUTH_TOOWEAK: return ("too weak");
+ case AUTH_INVALIDRESP: return ("bogus response verifier");
+ case AUTH_TIMEEXPIRE: return ("time of credential expired");
+ case AUTH_TKT_FILE: return ("something wrong with ticket file");
+ case AUTH_DECODE: return ("can't decode authenticator");
+ case AUTH_NET_ADDR: return ("net address in ticket wrong");
+ case RPCSEC_GSS_NOCRED: return ("no credentials for user");
+ case RPCSEC_GSS_FAILED: return ("GSS failure, credentials deleted");
+ case AUTH_FAILED:
+ default:
+ return ("unknown reason");
+ }
+}
+
+rpc_detail_reply(flags, xid, x, data, len)
+ int flags, xid;
+ struct cache_struct *x;
+ char *data;
+ int len;
+{
+ int status;
+ int astat, rstat, why;
+ int pos;
+
+ if (x) {
+ (void) sprintf(get_line(0, 0),
+ "This is a reply to frame %d",
+ x->xid_frame);
+ }
+ pos = getxdr_pos();
+ status = getxdr_long();
+ (void) sprintf(get_line(pos, getxdr_pos()),
+ "Status = %d (%s)",
+ status, status ? "Denied" : "Accepted");
+
+ switch (status) {
+ case MSG_ACCEPTED:
+ print_verif(REPLY);
+ pos = getxdr_pos();
+ astat = getxdr_long();
+ (void) sprintf(get_line(pos, getxdr_pos()),
+ "Accept status = %d (%s)",
+ astat, nameof_astat(astat));
+
+ switch (astat) {
+ case SUCCESS:
+ if (x) {
+ show_trailer();
+ protoprint(flags, REPLY, xid,
+ x->xid_prog, x->xid_vers, x->xid_proc,
+ data, len);
+ }
+ break;
+ case PROG_UNAVAIL :
+ break;
+ case PROG_MISMATCH:
+ case PROC_UNAVAIL :
+ showxdr_long(" Low = %d");
+ showxdr_long(" High = %d");
+ break;
+ case GARBAGE_ARGS:
+ case SYSTEM_ERR:
+ default:
+ ;
+ }
+
+ break;
+
+ case MSG_DENIED:
+ pos = getxdr_pos();
+ rstat = getxdr_long();
+ (void) sprintf(get_line(pos, getxdr_pos()),
+ "Reject status = %d (%s)",
+ rstat,
+ rstat ? "can't authenticate"
+ : "version mismatch");
+
+ switch (rstat) {
+ case RPC_MISMATCH:
+ showxdr_long(" Low = %d");
+ showxdr_long(" High = %d");
+ break;
+ case AUTH_ERROR:
+ why = getxdr_u_long();
+ (void) sprintf(get_line(pos, getxdr_pos()),
+ " Why = %d (%s)",
+ why, nameof_why(why));
+ break;
+ }
+ break;
+ }
+}
+
+/*
+ * Return true if this is a valid RPC packet
+ */
+valid_rpc(rpc, rpclen)
+ char *rpc;
+ int rpclen;
+{
+ XDR xdrm;
+ struct rpc_msg msg;
+
+ if (rpclen < 12)
+ return (0);
+
+ xdrmem_create(&xdrm, rpc, rpclen, XDR_DECODE);
+ if (xdr_u_int(&xdrm, &msg.rm_xid) &&
+ xdr_u_int(&xdrm, (uint_t *)&msg.rm_direction)) {
+ switch (msg.rm_direction) {
+ case CALL:
+ if (xdr_rpcvers(&xdrm, &msg.rm_call.cb_rpcvers) &&
+ msg.rm_call.cb_rpcvers == 2)
+ return (1);
+ break;
+ case REPLY:
+ if (xdr_u_int(&xdrm,
+ (uint_t *)&msg.rm_reply.rp_stat) &&
+ (msg.rm_reply.rp_stat == MSG_ACCEPTED ||
+ msg.rm_reply.rp_stat == MSG_DENIED))
+ return (1);
+ break;
+ }
+ }
+
+ return (0);
+}
+
+struct cache_struct *xcpfirst = &xid_cache[0];
+struct cache_struct *xcp = &xid_cache[0];
+struct cache_struct *xcplast = &xid_cache[XID_CACHE_SIZE - 1];
+
+struct cache_struct *
+find_xid(xid)
+ ulong_t xid;
+{
+ struct cache_struct *x;
+
+ for (x = xcp; x >= xcpfirst; x--)
+ if (x->xid_num == xid)
+ return (x);
+ for (x = xcplast; x > xcp; x--)
+ if (x->xid_num == xid)
+ return (x);
+ return (NULL);
+}
+
+stash_xid(xid, frame, prog, vers, proc)
+ ulong_t xid;
+ int frame, prog, vers, proc;
+{
+ struct cache_struct *x;
+
+ x = find_xid(xid);
+ if (x == NULL) {
+ x = xcp++;
+ if (xcp > xcplast)
+ xcp = xcpfirst;
+ x->xid_num = xid;
+ x->xid_frame = frame;
+ }
+ x->xid_prog = prog;
+ x->xid_vers = vers;
+ x->xid_proc = proc;
+ x->xid_gss_proc = RPCSEC_GSS_DATA;
+ x->xid_gss_service = rpc_gss_svc_default;
+}
+
+void
+check_retransmit(line, xid)
+ char *line;
+ ulong_t xid;
+{
+ struct cache_struct *x;
+ extern int pi_frame;
+
+ x = find_xid(xid);
+ if (x && x->xid_frame != pi_frame)
+ (void) strcat(line, " (retransmit)");
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rpcprint.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rpcprint.c
new file mode 100644
index 0000000000..e7fc7fea75
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rpcprint.c
@@ -0,0 +1,111 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1991-2001, 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/tiuser.h>
+#include <rpc/types.h>
+#include <rpc/xdr.h>
+#include <rpc/auth.h>
+#include <rpc/clnt.h>
+#include <rpc/rpc_msg.h>
+#include "snoop.h"
+
+#define RPC_TRANSIENT_START 0x40000000
+#define RPC_TRANSIENT_END 0x5fffffff
+
+int rpcsec_gss_control_proc(int type, int flags, int xid);
+
+int rpcsec_gss_pre_proto(int type, int flags, int xid,
+ int prog, int vers, int proc);
+
+void rpcsec_gss_post_proto(int flags, int xid);
+
+void
+protoprint(flags, type, xid, prog, vers, proc, data, len)
+ ulong_t xid;
+ int flags, type, prog, vers, proc;
+ char *data;
+ int len;
+{
+ char *name;
+ void (*interpreter)(int, int, int, int, int, char *, int);
+
+ switch (prog) {
+ case 100000: interpreter = interpret_pmap; break;
+ case 100001: interpreter = interpret_rstat; break;
+ case 100003: interpreter = interpret_nfs; break;
+ case 100004: interpreter = interpret_nis; break;
+ case 100005: interpreter = interpret_mount; break;
+ case 100007: interpreter = interpret_nisbind; break;
+ case 100011: interpreter = interpret_rquota; break;
+ case 100021: interpreter = interpret_nlm; break;
+ case 100026: interpreter = interpret_bparam; break;
+ case 100227: interpreter = interpret_nfs_acl; break;
+ case 100300: interpreter = interpret_nisplus; break;
+ case 100302: interpreter = interpret_nisp_cb; break;
+ case 150006: interpreter = interpret_solarnet_fw; break;
+ default: interpreter = NULL;
+ }
+
+ /*
+ * if rpc in transient range and proc is 0 or 1, then
+ * guess that it is the nfsv4 callback protocol
+ */
+ if (prog >= RPC_TRANSIENT_START && prog <= RPC_TRANSIENT_END &&
+ (proc == 0 || proc == 1))
+ interpreter = interpret_nfs4_cb;
+
+ /*
+ * If the RPC header indicates it's using the RPCSEC_GSS_*
+ * control procedure, print it.
+ */
+ if (rpcsec_gss_control_proc(type, flags, xid)) {
+ return;
+ }
+
+ if (interpreter == NULL) {
+ if (!(flags & F_SUM))
+ return;
+ name = nameof_prog(prog);
+ if (*name == '?' || strcmp(name, "transient") == 0)
+ return;
+ (void) sprintf(get_sum_line(), "%s %c",
+ name,
+ type == CALL ? 'C' : 'R');
+ } else {
+ /* Pre-processing based on different RPCSEC_GSS services. */
+ if (rpcsec_gss_pre_proto(type, flags, xid, prog, vers, proc))
+ return;
+
+ (*interpreter) (flags, type, xid, vers, proc, data, len);
+
+ /* Post-processing based on different RPCSEC_GSS services. */
+ rpcsec_gss_post_proto(flags, xid);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rpcsec.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rpcsec.c
new file mode 100644
index 0000000000..49fb96de83
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rpcsec.c
@@ -0,0 +1,401 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1996 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/tiuser.h>
+#include <setjmp.h>
+
+#include <rpc/types.h>
+#include <rpc/xdr.h>
+#include <rpc/auth.h>
+#include <rpc/clnt.h>
+#include <rpc/rpc_msg.h>
+#include <rpc/rpcsec_gss.h>
+#include <string.h>
+#include "snoop.h"
+
+extern jmp_buf xdr_err;
+
+struct cache_struct *find_xid();
+char *nameof_prog(int prog);
+
+char *
+rpcsec_gss_proc_to_string(unsigned int proc)
+{
+ switch (proc) {
+ case RPCSEC_GSS_DATA: return "RPCSEC_GSS_DATA"; break;
+ case RPCSEC_GSS_INIT: return "RPCSEC_GSS_INIT"; break;
+ case RPCSEC_GSS_CONTINUE_INIT:
+ return ("RPCSEC_GSS_CONTINUE_INIT");
+ case RPCSEC_GSS_DESTROY:
+ return ("RPCSEC_GSS_DESTROY");
+ default: return ("unknown");
+
+ }
+}
+
+
+char *
+rpcsec_gss_service_to_string(rpc_gss_service_t service)
+{
+ switch (service) {
+ case rpc_gss_svc_none: return "none"; break;
+ case rpc_gss_svc_integrity: return "integrity"; break;
+ case rpc_gss_svc_privacy: return "privacy"; break;
+ default: return "unknown"; break;
+
+ }
+}
+
+/*
+ * Print detailed RPCSEC_GSS cred data.
+ */
+void
+print_rpcsec_gss_cred(int xid, int authlen)
+{
+ unsigned int seq_num;
+ unsigned int handle_len;
+ unsigned int rpcsec_gss_ver;
+ rpc_gss_service_t rpcsec_gss_service;
+ unsigned int rpcsec_gss_proc;
+ char *handle, *line;
+ struct cache_struct *x;
+ int pos;
+
+ pos = getxdr_pos();
+ rpcsec_gss_ver = getxdr_u_long();
+
+ /* see if we know this version or not */
+
+ if (rpcsec_gss_ver != 1) {
+ (void) showxdr_hex(authlen, "[%s]");
+ return;
+ }
+
+ rpcsec_gss_proc = getxdr_u_long();
+ seq_num = getxdr_u_long();
+ rpcsec_gss_service = getxdr_enum();
+
+ (void) sprintf(get_line(pos, getxdr_pos()),
+ " version = %u", rpcsec_gss_ver);
+
+ (void) sprintf(get_line(pos, getxdr_pos()),
+ " gss control procedure = %u (%s)",
+ rpcsec_gss_proc,
+ rpcsec_gss_proc_to_string(rpcsec_gss_proc));
+
+ (void) sprintf(get_line(pos, getxdr_pos()),
+ " sequence num = %u", seq_num);
+
+ (void) sprintf(get_line(pos, getxdr_pos()),
+ " service = %d (%s)", rpcsec_gss_service,
+ rpcsec_gss_service_to_string(rpcsec_gss_service));
+ pos = getxdr_pos();
+ handle_len = getxdr_u_long();
+ handle = getxdr_hex(handle_len);
+ line = get_line(pos, getxdr_pos());
+ sprintf(line, " handle: length = %d, data = [%s]",
+ handle_len, handle);
+ x = find_xid(xid);
+ if (x) {
+ x->xid_gss_proc = rpcsec_gss_proc;
+ x->xid_gss_service = rpcsec_gss_service;
+ }
+}
+
+/*
+ * Based on different RPCSEC_GSS services supported, maybe a
+ * special handling is needed before printing the arguments.
+ *
+ * For integrity service : print the sequence number.
+ * For privacy service : do not print the arguments.
+ */
+int
+rpcsec_gss_pre_proto(int type, int flags, int xid,
+ int prog, int vers, int proc)
+{
+ int seq;
+ struct cache_struct *x;
+
+ if (! (x = find_xid(xid)))
+ return (0);
+
+ switch (x->xid_gss_service) {
+ case rpc_gss_svc_default:
+ case rpc_gss_svc_none:
+ break; /* standard call args */
+ case rpc_gss_svc_integrity:
+ /* length of rpc_gss_data_t encoded in the databody_integ */
+ getxdr_u_long();
+ /* read the seq number */
+ seq = getxdr_u_long();
+ if (flags & F_ALLSUM) {
+ (void) sprintf(get_sum_line(), "%s %c seq_num = %u",
+ "RPC RPCSEC_GSS", type == CALL ? 'C' : 'R',
+ seq);
+ } else if (flags & F_DTAIL) {
+ sprintf(get_line(0, 0),
+ "RPCSEC_GSS data seq_num = %u", seq);
+ show_space();
+ }
+ /* call args follow */
+ break;
+ case rpc_gss_svc_privacy: {
+ char *progname = nameof_prog(prog);
+ char prognum[32];
+
+ if (*progname == '?') {
+ sprintf(prognum, "%d", prog);
+ progname = prognum;
+ }
+
+ if (flags & F_SUM || flags & F_ALLSUM) {
+ (void) sprintf(get_sum_line(),
+ "%s %c %s ver(%d) proc(%d) (data encrypted) ",
+ "RPC RPCSEC_GSS", type == CALL ? 'C' : 'R',
+ progname, vers, proc);
+ } else if (flags & F_DTAIL) {
+ unsigned int args_len;
+
+ args_len = getxdr_u_long();
+ sprintf(get_line(0, 0),
+ "RPCSEC_GSS %s ver(%d) proc(%d)",
+ progname, vers, proc);
+ sprintf(get_line(0, 0),
+ "(%s args encrypted, len = %d bytes)",
+ type == CALL ? "CALL" : "REPLY", args_len);
+ show_space();
+ }
+ }
+ return (1);
+
+ default:
+ break;
+ }
+ return (0);
+}
+
+/*
+ * Based on different RPCSEC_GSS services supported, maybe a
+ * special handling is needed after printing the arguments.
+ *
+ * For integrity service : print the checksum.
+ */
+void
+rpcsec_gss_post_proto(int flags, int xid)
+{
+ char *line;
+
+ struct cache_struct *x;
+
+ if (! (x = find_xid(xid)))
+ return;
+
+ switch (x->xid_gss_service) {
+ case rpc_gss_svc_default:
+ case rpc_gss_svc_none:
+ case rpc_gss_svc_privacy:
+ /* nothing left */
+ break;
+ case rpc_gss_svc_integrity:
+ if (flags & F_ALLSUM) {
+ line = get_sum_line();
+ sprintf(line, "RPC RPCSEC_GSS C (checksum)");
+ } else if (flags & F_DTAIL) {
+ unsigned int checksum_len;
+ char *checksum;
+
+ show_header("RPC: ", "RPCSEC_GSS", 0);
+ show_space();
+ checksum_len = getxdr_u_long();
+ checksum = getxdr_hex(checksum_len);
+ sprintf(get_line(0, 0),
+ "checksum: len = %d", checksum_len);
+ sprintf(get_line(0, 0), "[%s]", checksum);
+ show_trailer();
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * Print RPCSEC_GSS control procedures protocol data,
+ * No-op for RPCSEC_GSS_DATA.
+ */
+int
+rpcsec_gss_control_proc(int type, int flags, int xid)
+{
+ int seq;
+
+ struct cache_struct *x;
+
+ if (! (x = find_xid(xid)))
+ return (0);
+
+ if (x->xid_gss_proc != RPCSEC_GSS_DATA) {
+ if (flags & F_SUM) {
+ if (type == CALL) {
+ (void) sprintf(get_sum_line(), "%s %c %u (%s)",
+ "RPC RPCSEC_GSS",
+ type == CALL ? 'C' : 'R',
+ x->xid_gss_proc,
+ rpcsec_gss_proc_to_string(x->xid_gss_proc));
+ }
+ } else if (flags & F_DTAIL) {
+ if (x->xid_gss_proc == RPCSEC_GSS_INIT ||
+ x->xid_gss_proc == RPCSEC_GSS_CONTINUE_INIT) {
+ if (type == CALL) {
+ print_rpc_gss_init_arg(flags, x);
+ } else {
+ print_rpc_gss_init_res(flags);
+ }
+ }
+ }
+ return (1);
+ }
+
+ return (0);
+}
+
+/*
+ * Skip the header RPCSEC_GSS cred data and
+ * put service and control type in the xid cache.
+ */
+extract_rpcsec_gss_cred_info(int xid)
+{
+ unsigned int seq_num;
+ unsigned int handle_len;
+ unsigned int flavor_len;
+ unsigned int rpcsec_gss_ver;
+ rpc_gss_service_t rpcsec_gss_service;
+ unsigned int rpcsec_gss_proc;
+ struct cache_struct *x;
+
+ flavor_len = getxdr_u_long();
+ rpcsec_gss_ver = getxdr_u_long();
+ /* see if we know this version or not */
+ if (rpcsec_gss_ver != 1) {
+ longjmp(xdr_err, 1);
+ }
+ rpcsec_gss_proc = getxdr_u_long();
+ seq_num = getxdr_u_long();
+ rpcsec_gss_service = getxdr_enum();
+ /* skip the handle */
+ xdr_skip(RNDUP(getxdr_u_long()));
+
+ if (x = find_xid(xid)) {
+ x->xid_gss_service = rpcsec_gss_service;
+ x->xid_gss_proc = rpcsec_gss_proc;
+ }
+
+}
+
+/*
+ * Print the argument data for the RPCSEC_GSS_INIT control procedure.
+ */
+print_rpc_gss_init_arg(flags, x)
+ int flags;
+ struct cache_struct *x;
+{
+
+ char *token, *line;
+ unsigned int token_len;
+ int pos;
+
+ /*
+ * see if we need to print out the rpc_gss_init_arg structure
+ * or not.
+ */
+
+ if (x->xid_gss_proc != RPCSEC_GSS_INIT &&
+ x->xid_gss_proc != RPCSEC_GSS_CONTINUE_INIT) {
+ return;
+ }
+
+ /* print it */
+
+ (void) sprintf(get_line(pos, getxdr_pos()),
+ "RPCSEC_GSS_INIT args:");
+
+ pos = getxdr_pos();
+ token_len = getxdr_u_long();
+ token = getxdr_hex(token_len);
+ line = get_line(pos, getxdr_pos());
+ sprintf(line, " gss token: length = %d, data = [%d bytes]",
+ token_len, token_len);
+
+ show_trailer();
+}
+
+/*
+ * Print the results data for the RPCSEC_GSS_INIT control procedure.
+ */
+print_rpc_gss_init_res(int flags)
+{
+
+ char *handle, *token, *line;
+ unsigned int token_len, handle_len;
+ unsigned int major, minor, seq_window;
+
+ int pos;
+ struct cache_struct *x;
+
+ /* print it */
+
+ (void) sprintf(get_line(pos, getxdr_pos()), "RPCSEC_GSS_INIT result:");
+
+ pos = getxdr_pos();
+ handle_len = getxdr_u_long();
+ handle = getxdr_hex(handle_len);
+ line = get_line(pos, getxdr_pos());
+ sprintf(line, " handle: length = %d, data = [%s]",
+ handle_len, handle);
+ pos = getxdr_pos();
+ major = getxdr_u_long();
+ minor = getxdr_u_long();
+ seq_window = getxdr_u_long();
+
+ (void) sprintf(get_line(pos, getxdr_pos()),
+ " gss_major status = %u", major);
+
+ (void) sprintf(get_line(pos, getxdr_pos()),
+ " gss_minor status = %u", minor);
+
+ (void) sprintf(get_line(pos, getxdr_pos()),
+ " sequence window = %u", seq_window);
+ pos = getxdr_pos();
+ token_len = getxdr_u_long();
+ token = getxdr_hex(token_len);
+ line = get_line(pos, getxdr_pos());
+ sprintf(line, " gss token: length = %d, data = [%d bytes]",
+ token_len, token_len);
+ show_trailer();
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rport.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rport.c
new file mode 100644
index 0000000000..8a65afda5d
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rport.c
@@ -0,0 +1,500 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#pragma ident "%Z%%M% %I% %E% SMI" /* SunOS */
+
+#include <ctype.h>
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <setjmp.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/if_ether.h>
+#include "snoop.h"
+
+#define NULL 0
+
+extern void interpret_mip_cntrlmsg(int, uchar_t *, int);
+
+struct porttable {
+ int pt_num;
+ char *pt_short;
+ char *pt_long;
+};
+
+struct porttable pt_udp[] = {
+ 7, "ECHO", "Echo",
+ 9, "DISCARD", "Discard",
+ 13, "DAYTIME", "Daytime",
+ 19, "CHARGEN", "Character generator",
+ 37, "TIME", "Time",
+ 42, "NAME", "Host name server",
+ 53, "DNS", "Domain Name Server",
+ 67, "BOOTPS", "Bootstrap Protocol Server",
+ 68, "BOOTPC", "Boostrap Protocol Client",
+ 69, "TFTP", "Trivial File Transfer Protocol",
+ 79, "FINGER", "Finger",
+/* 111, "PORTMAP", "Portmapper", Just Sun RPC */
+ 123, "NTP", "Network Time Protocol",
+ 137, "NBNS", "Netbios name service",
+ 138, "NBDG", "Netbios datagram service",
+ 389, "LDAP", "Lightweight Directory Access Protocol",
+ 427, "SLP", "Service Location Protocol",
+/* Mobile IP defines a set of new control messages sent over UDP port 434 */
+ 434, "Mobile IP", "Mobile IP Control Messages",
+ 512, "BIFF", "BIFF",
+ 513, "WHO", "WHO",
+ 514, "SYSLOG", "SYSLOG",
+ 517, "TALK", "TALK",
+ 520, "RIP", "Routing Information Protocol",
+ 550, "NEW-RWHO", "NEW-RWHO",
+ 560, "RMONITOR", "RMONITOR",
+ 561, "MONITOR", "MONITOR",
+ 521, "RIPng", "Routing Information Protocol for IPv6",
+ 1080, "SOCKS", "SOCKS Gateway",
+ 0, NULL, "",
+};
+
+struct porttable pt_tcp[] = {
+ 1, "TCPMUX", "TCPMUX",
+ 7, "ECHO", "Echo",
+ 9, "DISCARD", "Discard",
+ 11, "SYSTAT", "Active users",
+ 13, "DAYTIME", "Daytime",
+ 15, "NETSTAT", "Who is up",
+ 19, "CHARGEN", "Character generator",
+ 20, "FTP-DATA", "File Transfer Protocol (data)",
+ 21, "FTP", "File Transfer Protocol",
+ 23, "TELNET", "Terminal connection",
+ 25, "SMTP", "Simple Mail Transport Protocol",
+ 37, "TIME", "Time",
+ 39, "RLP", "Resource Location Protocol",
+ 42, "NAMESERVER", "Host Name Server",
+ 43, "NICNAME", "Who is",
+ 53, "DNS", "Domain Name Server",
+ 67, "BOOTPS", "Bootstrap Protocol Server",
+ 68, "BOOTPC", "Bootstrap Protocol Client",
+ 69, "TFTP", "Trivial File Transfer Protocol",
+ 70, "GOPHER", "Internet Gopher Protocol",
+ 77, "RJE", "RJE service (private)",
+ 79, "FINGER", "Finger",
+ 80, "HTTP", "HyperText Transfer Protocol",
+ 87, "LINK", "Link",
+ 95, "SUPDUP", "SUPDUP Protocol",
+ 101, "HOSTNAME", "NIC Host Name Server",
+ 102, "ISO-TSAP", "ISO-TSAP",
+ 103, "X400", "X400 Mail service",
+ 104, "X400-SND", "X400 Mail service",
+ 105, "CSNET-NS", "CSNET-NS",
+ 109, "POP-2", "POP-2",
+/* 111, "PORTMAP", "Portmapper", Just Sun RPC */
+ 113, "AUTH", "Authentication Service",
+ 117, "UUCP-PATH", "UUCP Path Service",
+ 119, "NNTP", "Network News Transfer Protocol",
+ 123, "NTP", "Network Time Protocol",
+ 139, "NBT", "Netbios over TCP",
+ 143, "IMAP", "Internet Message Access Protocol",
+ 144, "NeWS", "Network extensible Window System",
+ 389, "LDAP", "Lightweight Directory Access Protocol",
+ 427, "SLP", "Service Location Protocol",
+ 443, "HTTPS", "HTTP over SSL",
+ 445, "SMB", "Direct Hosted Server Message Block",
+ 512, "EXEC", "EXEC",
+ 513, "RLOGIN", "RLOGIN",
+ 514, "RSHELL", "RSHELL",
+ 515, "PRINTER", "PRINTER",
+ 530, "COURIER", "COURIER",
+ 540, "UUCP", "UUCP",
+ 600, "PCSERVER", "PCSERVER",
+ 1524, "INGRESLOCK", "INGRESLOCK",
+ 1080, "SOCKS", "SOCKS Gateway",
+ 2904, "M2UA", "SS7 MTP2 User Adaption Layer",
+ 2905, "M3UA", "SS7 MTP3 User Adaption Layer",
+ 6000, "XWIN", "X Window System",
+ 8080, "HTTP (proxy)", "HyperText Transfer Protocol (proxy)",
+ 9900, "IUA", "ISDN Q.921 User Adaption Layer",
+ 0, NULL, "",
+};
+
+char *
+getportname(int proto, in_port_t port)
+{
+ struct porttable *p, *pt;
+
+ switch (proto) {
+ case IPPROTO_SCTP: /* fallthru */
+ case IPPROTO_TCP: pt = pt_tcp; break;
+ case IPPROTO_UDP: pt = pt_udp; break;
+ default: return (NULL);
+ }
+
+ for (p = pt; p->pt_num; p++) {
+ if (port == p->pt_num)
+ return (p->pt_short);
+ }
+ return (NULL);
+}
+
+int
+reservedport(int proto, int port)
+{
+ struct porttable *p, *pt;
+
+ switch (proto) {
+ case IPPROTO_TCP: pt = pt_tcp; break;
+ case IPPROTO_UDP: pt = pt_udp; break;
+ default: return (NULL);
+ }
+ for (p = pt; p->pt_num; p++) {
+ if (port == p->pt_num)
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * Need to be able to register an
+ * interpreter for transient ports.
+ * See TFTP interpreter.
+ */
+#define MAXTRANS 64
+struct ttable {
+ int t_port;
+ int (*t_proc)();
+} transients [MAXTRANS];
+
+int
+add_transient(int port, int (*proc)())
+{
+ static struct ttable *next = transients;
+
+ next->t_port = port;
+ next->t_proc = proc;
+
+ if (++next >= &transients[MAXTRANS])
+ next = transients;
+
+ return (1);
+}
+
+static struct ttable *
+is_transient(int port)
+{
+ struct ttable *p;
+
+ for (p = transients; p->t_port && p < &transients[MAXTRANS]; p++) {
+ if (port == p->t_port)
+ return (p);
+ }
+
+ return (NULL);
+}
+
+void
+del_transient(int port)
+{
+ struct ttable *p;
+
+ for (p = transients; p->t_port && p < &transients[MAXTRANS]; p++) {
+ if (port == p->t_port)
+ p->t_port = -1;
+ }
+}
+
+static void
+interpret_syslog(int flags, char dir, int port, const char *syslogstr,
+ int dlen)
+{
+ static const char *pris[] = {
+ "emerg", "alert", "crit", "error", "warn", "notice", "info", "debug"
+ };
+ static const char *facs[] = {
+ "kern", "user", "mail", "daemon", "auth", "syslog", "lpr", "news",
+ "uucp", NULL, NULL, NULL, NULL, "audit", NULL, "cron", "local0",
+ "local1", "local2", "local3", "local4", "local5", "local6", "local7"
+ };
+
+ int composit;
+ int pri = -1;
+ int facil = -1;
+ boolean_t bogus = B_TRUE;
+ int priostrlen = 0;
+ int datalen = dlen;
+ char unknown[4]; /* for unrecognized ones */
+ const char *facilstr = "BAD";
+ const char *pristr = "FMT";
+ const char *data = syslogstr;
+
+ /*
+ * Is there enough data to interpret (left bracket + at least 3 chars
+ * which could be digits, right bracket, or space)?
+ */
+ if (datalen >= 4 && data != NULL) {
+ if (*data == '<') {
+ const int FACS_LEN = sizeof (facs) / sizeof (facs[0]);
+ char buffer[4];
+ char *end;
+
+ data++;
+ datalen--;
+
+ strlcpy(buffer, data, sizeof (buffer));
+ composit = strtoul(buffer, &end, 0);
+ data += end - buffer;
+ if (*data == '>') {
+ data++;
+ datalen -= end - buffer + 1;
+
+ pri = composit & 0x7;
+ facil = (composit & 0xF8) >> 3;
+
+ if ((facil >= FACS_LEN) ||
+ (facs[facil] == NULL)) {
+ snprintf(unknown, sizeof (unknown),
+ "%d", facil);
+ facilstr = unknown;
+ } else {
+ facilstr = facs[facil];
+ }
+ pristr = pris[pri];
+ priostrlen = dlen - datalen;
+ bogus = B_FALSE;
+ } else {
+ data = syslogstr;
+ datalen = dlen;
+ }
+ }
+ }
+
+ if (flags & F_SUM) {
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "SYSLOG %c port=%d %s.%s: %s",
+ dir, port, facilstr, pristr,
+ show_string(syslogstr, dlen, 20));
+
+ }
+
+ if (flags & F_DTAIL) {
+ static char syslog[] = "SYSLOG: ";
+ show_header(syslog, syslog, dlen);
+ show_space();
+ (void) sprintf(get_detail_line(0, 80),
+ "%s%sPriority: %.*s%s(%s.%s)", prot_nest_prefix, syslog,
+ priostrlen, syslogstr, bogus ? "" : " ",
+ facilstr, pristr);
+ (void) sprintf(get_line(0, 0),
+ "\"%s\"",
+ show_string(syslogstr, dlen, 60));
+ show_trailer();
+ }
+}
+
+int src_port, dst_port, curr_proto;
+
+int
+interpret_reserved(int flags, int proto, in_port_t src, in_port_t dst,
+ char *data, int dlen)
+{
+ char *pn;
+ int dir, port, which;
+ char pbuff[16], hbuff[32];
+ struct ttable *ttabp;
+
+ src_port = src;
+ dst_port = dst;
+ curr_proto = proto;
+
+ pn = getportname(proto, src);
+ if (pn != NULL) {
+ dir = 'R';
+ port = dst;
+ which = src;
+ } else {
+ pn = getportname(proto, dst);
+ if (pn == NULL) {
+ ttabp = is_transient(src);
+ if (ttabp) {
+ (ttabp->t_proc)(flags, data, dlen);
+ return (1);
+ }
+ ttabp = is_transient(dst);
+ if (ttabp) {
+ (ttabp->t_proc)(flags, data, dlen);
+ return (1);
+ }
+ return (0);
+ }
+
+ dir = 'C';
+ port = src;
+ which = dst;
+ }
+
+ if ((dst == 53 || src == 53) && proto != IPPROTO_TCP) {
+ interpret_dns(flags, proto, (uchar_t *)data, dlen);
+ return (1);
+ }
+
+ if (dst == 514 && proto != IPPROTO_TCP) {
+ /*
+ * TCP port 514 is rshell. UDP port 514 is syslog.
+ */
+ interpret_syslog(flags, dir, port, (const char *)data, dlen);
+ return (1);
+ }
+
+ if (dlen > 0) {
+ switch (which) {
+ case 67:
+ case 68:
+ interpret_dhcp(flags, data, dlen);
+ return (1);
+ case 69:
+ interpret_tftp(flags, data, dlen);
+ return (1);
+ case 80:
+ case 8080:
+ interpret_http(flags, data, dlen);
+ return (1);
+ case 123:
+ interpret_ntp(flags, data, dlen);
+ return (1);
+ case 137:
+ interpret_netbios_ns(flags, data, dlen);
+ return (1);
+ case 138:
+ interpret_netbios_datagram(flags, data, dlen);
+ return (1);
+ case 139:
+ case 445:
+ /*
+ * SMB on port 445 is a subset of NetBIOS SMB
+ * on port 139. The same interpreter can be used
+ * for both.
+ */
+ interpret_netbios_ses(flags, data, dlen);
+ return (1);
+ case 389:
+ interpret_ldap(flags, data, dlen, src, dst);
+ return (1);
+ case 427:
+ interpret_slp(flags, data, dlen);
+ return (1);
+ case 434:
+ interpret_mip_cntrlmsg(flags, (uchar_t *)data, dlen);
+ return (1);
+ case 520:
+ interpret_rip(flags, data, dlen);
+ return (1);
+ case 521:
+ interpret_rip6(flags, data, dlen);
+ return (1);
+ case 1080:
+ if (dir == 'C')
+ interpret_socks_call(flags, data, dlen);
+ else
+ interpret_socks_reply(flags, data, dlen);
+ return (1);
+ }
+ }
+
+ if (flags & F_SUM) {
+ (void) sprintf(get_sum_line(),
+ "%s %c port=%d %s",
+ pn, dir, port,
+ show_string(data, dlen, 20));
+ }
+
+ if (flags & F_DTAIL) {
+ (void) sprintf(pbuff, "%s: ", pn);
+ (void) sprintf(hbuff, "%s: ", pn);
+ show_header(pbuff, hbuff, dlen);
+ show_space();
+ (void) sprintf(get_line(0, 0),
+ "\"%s\"",
+ show_string(data, dlen, 60));
+ show_trailer();
+ }
+ return (1);
+}
+
+char *
+show_string(const char *str, int dlen, int maxlen)
+/*
+ * Prints len bytes from str enclosed in quotes.
+ * If len is negative, length is taken from strlen(str).
+ * No more than maxlen bytes will be printed. Longer
+ * strings are flagged with ".." after the closing quote.
+ * Non-printing characters are converted to C-style escape
+ * codes or octal digits.
+ */
+{
+#define TBSIZE 256
+ static char tbuff[TBSIZE];
+ const char *p;
+ char *pp;
+ int printable = 0;
+ int c, len;
+
+ len = dlen > maxlen ? maxlen : dlen;
+ dlen = len;
+
+ for (p = str, pp = tbuff; len; p++, len--) {
+ switch (c = *p & 0xFF) {
+ case '\n': (void) strcpy(pp, "\\n"); pp += 2; break;
+ case '\b': (void) strcpy(pp, "\\b"); pp += 2; break;
+ case '\t': (void) strcpy(pp, "\\t"); pp += 2; break;
+ case '\r': (void) strcpy(pp, "\\r"); pp += 2; break;
+ case '\f': (void) strcpy(pp, "\\f"); pp += 2; break;
+ default:
+ if (isascii(c) && isprint(c)) {
+ *pp++ = c;
+ printable++;
+ } else {
+ (void) sprintf(pp,
+ isdigit(*(p + 1)) ?
+ "\\%03o" : "\\%o", c);
+ pp += strlen(pp);
+ }
+ break;
+ }
+ *pp = '\0';
+ /*
+ * Check for overflow of temporary buffer. Allow for
+ * the next character to be a \nnn followed by a trailing
+ * null. If not, then just bail with what we have.
+ */
+ if (pp + 5 >= &tbuff[TBSIZE]) {
+ break;
+ }
+ }
+ return (printable > dlen / 2 ? tbuff : "");
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rquota.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rquota.c
new file mode 100644
index 0000000000..13f5dc6cd3
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rquota.c
@@ -0,0 +1,164 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1991, 1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ident "%Z%%M% %I% %E% SMI" /* SunOS */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <setjmp.h>
+#include <string.h>
+
+#include <netinet/in.h>
+#include <rpc/types.h>
+#include <rpc/rpc.h>
+#include <rpc/xdr.h>
+#include <rpc/auth.h>
+#include <rpc/clnt.h>
+#include <rpc/rpc_msg.h>
+#include <rpcsvc/rquota.h>
+#include "snoop.h"
+
+extern char *dlc_header;
+extern jmp_buf xdr_err;
+
+static char *procnames_short[] = {
+ "Null", /* 0 */
+ "GETQUOTA", /* 1 */
+ "GETACTIVE", /* 2 */
+};
+
+static char *procnames_long[] = {
+ "Null procedure", /* 0 */
+ "Get quotas", /* 1 */
+ "Get active quotas", /* 2 */
+};
+
+#define MAXPROC 2
+
+static void show_quota(void);
+
+void
+interpret_rquota(flags, type, xid, vers, proc, data, len)
+ int flags, type, xid, vers, proc;
+ char *data;
+ int len;
+{
+ char *line;
+ char buff[RQ_PATHLEN + 1];
+ int status;
+ int uid;
+
+ if (proc < 0 || proc > MAXPROC)
+ return;
+
+ if (flags & F_SUM) {
+ if (setjmp(xdr_err)) {
+ return;
+ }
+
+ line = get_sum_line();
+
+ if (type == CALL) {
+ (void) getxdr_string(buff, RQ_PATHLEN);
+ uid = getxdr_long();
+ (void) sprintf(line,
+ "RQUOTA C %s Uid=%d Path=%s",
+ procnames_short[proc],
+ uid, buff);
+
+ check_retransmit(line, xid);
+ } else {
+ (void) sprintf(line, "RQUOTA R %s ",
+ procnames_short[proc]);
+ line += strlen(line);
+ status = getxdr_u_long();
+ if (status == Q_OK)
+ (void) sprintf(line, "OK");
+ else if (status == Q_NOQUOTA)
+ (void) sprintf(line, "No quota");
+ else if (status == Q_EPERM)
+ (void) sprintf(line, "No permission");
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("RQUOTA: ", "Remote Quota Check", len);
+ show_space();
+ if (setjmp(xdr_err)) {
+ return;
+ }
+ (void) sprintf(get_line(0, 0),
+ "Proc = %d (%s)",
+ proc, procnames_long[proc]);
+
+ if (type == CALL) {
+ switch (proc) {
+ case RQUOTAPROC_GETQUOTA:
+ case RQUOTAPROC_GETACTIVEQUOTA:
+ (void) showxdr_string(RQ_PATHLEN,
+ "Path = %s");
+ (void) showxdr_long("User id = %d");
+ break;
+ }
+ } else {
+ status = getxdr_u_long();
+ (void) sprintf(get_line(0, 0),
+ "Status = %lu (%s)",
+ status,
+ status == Q_OK ? "OK" :
+ status == Q_NOQUOTA ? "No quota" :
+ status == Q_EPERM ? "No permission":"");
+
+ if (status == Q_OK)
+ show_quota();
+ }
+
+ show_trailer();
+ }
+}
+
+static void
+show_quota()
+{
+ int active;
+
+ (void) showxdr_u_long("Block size = %lu");
+ active = getxdr_u_long();
+ (void) sprintf(get_line(0, 0),
+ " Quota checking = %lu (%s)",
+ active,
+ active ? "on" : "off");
+ (void) showxdr_u_long(" Blocks hard limit = %lu");
+ (void) showxdr_u_long(" Blocks soft limit = %lu");
+ (void) showxdr_u_long(" Current block count = %lu");
+ (void) show_space();
+ (void) showxdr_u_long(" File hard limit = %lu");
+ (void) showxdr_u_long(" File soft limit = %lu");
+ (void) showxdr_u_long(" Current file count = %lu");
+ (void) show_space();
+ (void) showxdr_u_long("Excessive blocks limit = %lu sec");
+ (void) showxdr_u_long("Excessive files limit = %lu sec");
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rstat.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rstat.c
new file mode 100644
index 0000000000..e45205a118
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rstat.c
@@ -0,0 +1,272 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1991, 1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ident "%Z%%M% %I% %E% SMI" /* SunOS */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <setjmp.h>
+#include <sys/tiuser.h>
+
+#include <rpc/types.h>
+#include <rpc/xdr.h>
+#include <rpc/auth.h>
+#include <rpc/clnt.h>
+#include <rpc/rpc_msg.h>
+#include "snoop.h"
+
+extern char *dlc_header;
+extern jmp_buf xdr_err;
+
+void detail_stats(); /* Version 1 */
+void detail_statsswtch(); /* Version 2 */
+void detail_statstime(); /* Version 3 */
+void detail_statsvar(); /* Version 4 */
+
+static char *procnames_short[] = {
+ "Null", /* 0 */
+ "Get Statistics", /* 1 */
+ "Have Disk", /* 2 */
+};
+
+static char *procnames_long[] = {
+ "Null procedure", /* 0 */
+ "Get Statistics", /* 1 */
+ "Have Disk", /* 2 */
+};
+
+#define MAXPROC 2
+
+void
+interpret_rstat(flags, type, xid, vers, proc, data, len)
+ int flags, type, xid, vers, proc;
+ char *data;
+ int len;
+{
+ char *line;
+
+ if (proc < 0 || proc > MAXPROC)
+ return;
+
+ if (flags & F_SUM) {
+ if (setjmp(xdr_err)) {
+ return;
+ }
+
+ line = get_sum_line();
+
+ if (type == CALL) {
+ (void) sprintf(line,
+ "RSTAT C %s",
+ procnames_short[proc]);
+
+ check_retransmit(line, xid);
+ } else {
+ (void) sprintf(line, "RSTAT R %s ",
+ procnames_short[proc]);
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("RSTAT: ", "RSTAT Get Statistics", len);
+ show_space();
+ if (setjmp(xdr_err)) {
+ return;
+ }
+ (void) sprintf(get_line(0, 0),
+ "Proc = %d (%s)",
+ proc, procnames_long[proc]);
+
+ if (type == REPLY) {
+ switch (proc) {
+ case 1:
+ switch (vers) {
+ case 1:
+ detail_stats();
+ break;
+ case 2:
+ detail_statsswtch();
+ break;
+ case 3:
+ detail_statstime();
+ break;
+ case 4:
+ detail_statsvar();
+ break;
+ }
+ break;
+ case 2:
+ (void) showxdr_u_long(
+ "Result = %lu");
+ break;
+ }
+ }
+ show_trailer();
+ }
+}
+
+void
+detail_stats()
+{
+ show_space();
+ (void) sprintf(get_line(0, 0), "CPU Times:");
+ (void) showxdr_long(" Time (1) = %d");
+ (void) showxdr_long(" Time (2) = %d");
+ (void) showxdr_long(" Time (3) = %d");
+ (void) showxdr_long(" Time (4) = %d");
+ show_space();
+ (void) sprintf(get_line(0, 0), "Disk Transfers:");
+ (void) showxdr_long(" Transfers(1) = %d");
+ (void) showxdr_long(" Transfers(2) = %d");
+ (void) showxdr_long(" Transfers(3) = %d");
+ (void) showxdr_long(" Transfers(4) = %d");
+ show_space();
+ (void) showxdr_u_long("Pages in = %lu");
+ (void) showxdr_u_long("Pages out = %lu");
+ (void) showxdr_u_long("Swaps in = %lu");
+ (void) showxdr_u_long("Swaps out = %lu");
+ (void) showxdr_u_long("Interrupts = %lu");
+ show_space();
+ (void) showxdr_long("Receive packets = %d");
+ (void) showxdr_long("Receive errors = %d");
+ (void) showxdr_long("Transmit packets = %d");
+ (void) showxdr_long("Transmit errors = %d");
+ (void) showxdr_long("Collisions = %d");
+}
+
+void
+detail_statsswtch()
+{
+ show_space();
+ (void) sprintf(get_line(0, 0), "CPU Times:");
+ (void) showxdr_long(" Time (1) = %d");
+ (void) showxdr_long(" Time (2) = %d");
+ (void) showxdr_long(" Time (3) = %d");
+ (void) showxdr_long(" Time (4) = %d");
+ show_space();
+ (void) sprintf(get_line(0, 0), "Disk Transfers:");
+ (void) showxdr_long(" Transfers(1) = %d");
+ (void) showxdr_long(" Transfers(2) = %d");
+ (void) showxdr_long(" Transfers(3) = %d");
+ (void) showxdr_long(" Transfers(4) = %d");
+ show_space();
+ (void) showxdr_u_long("Pages in = %lu");
+ (void) showxdr_u_long("Pages out = %lu");
+ (void) showxdr_u_long("Swaps in = %lu");
+ (void) showxdr_u_long("Swaps out = %lu");
+ (void) showxdr_u_long("Interrupts = %lu");
+ show_space();
+ (void) showxdr_long("Receive packets = %d");
+ (void) showxdr_long("Receive errors = %d");
+ (void) showxdr_long("Transmit packets = %d");
+ (void) showxdr_long("Transmit errors = %d");
+ (void) showxdr_long("Collisions = %d");
+ show_space();
+ (void) showxdr_u_long("V switch = %lu");
+ (void) showxdr_long("Average run 0 = %d");
+ (void) showxdr_long("Average run 1 = %d");
+ (void) showxdr_long("Average run 2 = %d");
+ show_space();
+ (void) showxdr_date("Boot time: = %s");
+}
+
+void
+detail_statstime()
+{
+ show_space();
+ (void) sprintf(get_line(0, 0), "CPU Times:");
+ (void) showxdr_long(" Time (1) = %d");
+ (void) showxdr_long(" Time (2) = %d");
+ (void) showxdr_long(" Time (3) = %d");
+ (void) showxdr_long(" Time (4) = %d");
+ show_space();
+ (void) sprintf(get_line(0, 0), "Disk Transfers:");
+ (void) showxdr_long(" Transfers(1) = %d");
+ (void) showxdr_long(" Transfers(2) = %d");
+ (void) showxdr_long(" Transfers(3) = %d");
+ (void) showxdr_long(" Transfers(4) = %d");
+ show_space();
+ (void) showxdr_u_long("Pages in = %lu");
+ (void) showxdr_u_long("Pages out = %lu");
+ (void) showxdr_u_long("Swaps in = %lu");
+ (void) showxdr_u_long("Swaps out = %lu");
+ (void) showxdr_u_long("Interrupts = %lu");
+ show_space();
+ (void) showxdr_long("Receive packets = %d");
+ (void) showxdr_long("Receive errors = %d");
+ (void) showxdr_long("Transmit packets = %d");
+ (void) showxdr_long("Transmit errors = %d");
+ (void) showxdr_long("Collisions = %d");
+ show_space();
+ (void) showxdr_u_long("V switch = %lu");
+ (void) showxdr_long("Average run 0 = %d");
+ (void) showxdr_long("Average run 1 = %d");
+ (void) showxdr_long("Average run 2 = %d");
+ show_space();
+ (void) showxdr_date("Boot time: = %s");
+ (void) showxdr_date("Current time = %s");
+}
+
+void
+detail_statsvar()
+{
+ int i, n;
+
+ show_space();
+ (void) sprintf(get_line(0, 0), "CPU Times:");
+ n = getxdr_u_long();
+ for (i = 1; i <= n; i++) {
+ (void) sprintf(get_line(0, 0),
+ " Time (%2d) = %d", i, getxdr_long());
+ }
+ show_space();
+ (void) sprintf(get_line(0, 0), "Disk Transfers:");
+ n = getxdr_u_long();
+ for (i = 1; i <= n; i++) {
+ (void) sprintf(get_line(0, 0),
+ " Transfers (%2d) = %d", i, getxdr_long());
+ }
+ show_space();
+ (void) showxdr_u_long("Pages in = %lu");
+ (void) showxdr_u_long("Pages out = %lu");
+ (void) showxdr_u_long("Swaps in = %lu");
+ (void) showxdr_u_long("Swaps out = %lu");
+ (void) showxdr_u_long("Interrupts = %lu");
+ show_space();
+ (void) showxdr_long("Receive packets = %d");
+ (void) showxdr_long("Receive errors = %d");
+ (void) showxdr_long("Transmit packets = %d");
+ (void) showxdr_long("Transmit errors = %d");
+ (void) showxdr_long("Collisions = %d");
+ show_space();
+ (void) showxdr_u_long("V switch = %lu");
+ (void) showxdr_long("Average run 0 = %d");
+ (void) showxdr_long("Average run 1 = %d");
+ (void) showxdr_long("Average run 2 = %d");
+ show_space();
+ (void) showxdr_date("Boot time: = %s");
+ (void) showxdr_date("Current time = %s");
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rtmp.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rtmp.c
new file mode 100644
index 0000000000..db3054ab1e
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rtmp.c
@@ -0,0 +1,181 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#include <at.h>
+#include <snoop.h>
+
+static void show_rtmp_tuples(uint8_t *, int);
+
+static char *
+rtmp_func_long(uint8_t fun)
+{
+ switch (fun) {
+ case RTMP_REQ:
+ return ("Request");
+ case RTMP_RDR_SH:
+ return ("Route Data Request, split horizon");
+ case RTMP_RDR_NSH:
+ return ("Route Data Request, no split horizon");
+ default:
+ return ("unknown");
+ }
+}
+
+static char *
+rtmp_func_short(uint8_t fun)
+{
+ switch (fun) {
+ case RTMP_REQ:
+ return ("Req");
+ case RTMP_RDR_SH:
+ return ("RDR, sh");
+ case RTMP_RDR_NSH:
+ return ("RDR, no sh");
+ default:
+ return ("unknown");
+ }
+}
+
+void
+interpret_rtmp(int flags, struct ddp_hdr *ddp, int len)
+{
+ uint8_t *data;
+ uint16_t snet;
+ uint8_t node;
+ int tuples;
+ int runt;
+ char extended;
+
+ len -= DDPHDR_SIZE;
+ if (len < 0)
+ goto out;
+
+ data = (uint8_t *)ddp + DDPHDR_SIZE;
+
+ switch (ddp->ddp_type) {
+ case DDP_TYPE_RTMPRQ: /* simple rtmp */
+ if (len < 1)
+ goto out;
+
+ if (flags & F_SUM) {
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "RTMP F=%s",
+ rtmp_func_short(data[0]));
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("RTMP: ", "RTMP Header", len);
+ show_space();
+
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Func = %d (%s)",
+ data[0], rtmp_func_long(data[0]));
+ }
+ break;
+ case DDP_TYPE_RTMPRESP: /* RTMP data */
+ if (len < 3)
+ goto out;
+
+ snet = get_short(data);
+ if (data[2] != 8) /* ID length is always 8 */
+ return;
+ node = data[3]; /* assume id_len == 8 */
+ extended = (data[6] != RTMP_FILLER) &&
+ (get_short(&data[4]) != 0);
+
+ tuples = (len - 4) / 3;
+ runt = (len - 4) % 3; /* integral length? */
+
+ if (flags & F_SUM) {
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "RTMP Data Snet=%d, Snode=%d%s",
+ snet, node, runt != 0 ? " (short)" : "");
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("RTMP: ", "RTMP Header", len);
+ show_space();
+
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "RTMP Data, Length = %d%s",
+ len, runt != 0 ? " (short packet)" : "");
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Senders Net = %d, Sender Node %d",
+ snet, node);
+ if (extended)
+ show_rtmp_tuples(&data[4], tuples);
+ else
+ show_rtmp_tuples(&data[7], tuples-1);
+ }
+
+ break;
+ }
+ return;
+out:
+ if (flags & F_SUM) {
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "RTMP (short packet)");
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("RTMP: ", "RTMP Header", len);
+ show_space();
+
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "(short packet)");
+ }
+}
+
+static void
+show_rtmp_tuples(uint8_t *p, int tuples)
+{
+ while (tuples > 0) {
+
+ if (p[2] & RTMP_EXTEND) { /* extended tuple? */
+
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ "Network = %d-%d, Distance = %d",
+ get_short(p), get_short(&p[3]),
+ p[2] & RTMP_DIST_MASK);
+ p += 6;
+ tuples -= 2;
+ } else {
+
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ "Network = %d, Distance = %d",
+ get_short(p), p[2]);
+ p += 3;
+ tuples--;
+ }
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_sctp.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_sctp.c
new file mode 100644
index 0000000000..c57da26859
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_sctp.c
@@ -0,0 +1,1207 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/sysmacros.h>
+#include <inet/common.h>
+#include <netinet/in.h>
+#include <netinet/sctp.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include "snoop.h"
+
+/*
+ * Snoop interpreter for SCTP (rfc2960).
+ *
+ * To add support for an upper-layer protocol, modify either
+ * the port-dispatcher in snoop_rport.c, or the protocol ID
+ * dispatcher at the bottom of this file (or both).
+ */
+
+static void interpret_protoid(int, uint32_t, char *, int);
+extern char *prot_prefix;
+
+/*
+ * This defines the length of internal, unbounded buffers. We set
+ * this to be MAXLINE (the maximum verbose display line length) -
+ * 64, which should be enough for all necessary descriptions. 64
+ * bytes seems like a reasonably conservative estimate of the
+ * maximum prefix length snoop may add to any text buffer it hands out.
+ */
+#define BUFLEN MAXLINE - 64
+
+/*
+ * Common structure to hold descriptions and parsers for all
+ * chunks, parameters, and errors. Each parser should implement
+ * this interface:
+ *
+ * void parse(int flags, uint8_t cflags, void *data, int datalen);
+ *
+ * Where flags is the snoop flags, cflags are the chunk flags, data
+ * is the chunk or parameter data (not including the chunk or
+ * parameter header), and datalen is the length of the chunk or
+ * parameter data (again not including any headers).
+ */
+typedef void parse_func_t(int, uint8_t, const void *, int);
+
+typedef struct {
+ uint16_t id;
+ const char *sdesc; /* short description */
+ const char *vdesc; /* verbose description */
+ parse_func_t *parse; /* parser function */
+} dispatch_t;
+
+static void interpret_params(const void *, int, char *, const dispatch_t *,
+ int, int);
+
+/*
+ * Chunk parsers
+ */
+static parse_func_t parse_abort_chunk, parse_data_chunk, parse_error_chunk,
+ parse_init_chunk, parse_opaque_chunk, parse_sack_chunk,
+ parse_shutdone_chunk, parse_shutdown_chunk, parse_asconf_chunk,
+ parse_ftsn_chunk;
+
+
+/*
+ * Chunk parser dispatch table. There are few enough chunks defined
+ * in the core protocol, and they are sequential, so the chunk code
+ * can be used as the index into this array for the common case.
+ * It is still necessary to check that the code and index match,
+ * since optional extensions will not follow sequentially the
+ * core chunks.
+ */
+static const dispatch_t chunk_dispatch_table[] = {
+/* code F_SUM desc F_DTAIL desc parser function */
+ { CHUNK_DATA, "Data", "Data Chunk",
+ parse_data_chunk },
+ { CHUNK_INIT, "Init", "Init Chunk",
+ parse_init_chunk },
+ { CHUNK_INIT_ACK, "Init ACK", "Init ACK Chunk",
+ parse_init_chunk },
+ { CHUNK_SACK, "SACK", "SACK Chunk",
+ parse_sack_chunk },
+ { CHUNK_HEARTBEAT, "Heartbeat", "Heartbeat Chunk",
+ parse_opaque_chunk },
+ { CHUNK_HEARTBEAT_ACK, "Heartbeat ACK", "Heartbeat ACK Chunk",
+ parse_opaque_chunk },
+ { CHUNK_ABORT, "Abort", "Abort Chunk",
+ parse_abort_chunk },
+ { CHUNK_SHUTDOWN, "Shutdown", "Shutdown Chunk",
+ parse_shutdown_chunk },
+ { CHUNK_SHUTDOWN_ACK, "Shutdown ACK", "Shutdown ACK Chunk",
+ NULL },
+ { CHUNK_ERROR, "Err", "Error Chunk",
+ parse_error_chunk },
+ { CHUNK_COOKIE, "Cookie", "Cookie Chunk",
+ parse_opaque_chunk },
+ { CHUNK_COOKIE_ACK, "Cookie ACK", "Cookie ACK Chunk",
+ parse_opaque_chunk },
+ { CHUNK_ECNE, "ECN Echo", "ECN Echo Chunk",
+ parse_opaque_chunk },
+ { CHUNK_CWR, "CWR", "CWR Chunk",
+ parse_opaque_chunk },
+ { CHUNK_SHUTDOWN_COMPLETE, "Shutdown Done", "Shutdown Done",
+ parse_shutdone_chunk },
+ { CHUNK_FORWARD_TSN, "FORWARD TSN", "Forward TSN Chunk",
+ parse_ftsn_chunk },
+ { CHUNK_ASCONF_ACK, "ASCONF ACK", "ASCONF ACK Chunk",
+ parse_asconf_chunk },
+ { CHUNK_ASCONF, "ASCONF", "ASCONF Chunk",
+ parse_asconf_chunk }
+};
+
+/*
+ * Parameter Parsers
+ */
+static parse_func_t parse_encap_param, parse_int32_param, parse_ip4_param,
+ parse_ip6_param, parse_opaque_param, parse_suppaddr_param,
+ parse_unrec_chunk, parse_addip_param, parse_asconferr_param,
+ parse_asconfok_param, parse_addiperr_param;
+
+/*
+ * Parameter parser dispatch table. The summary description is not
+ * used here. Strictly speaking, parameter types are defined within
+ * the context of a chunk type. However, thus far the IETF WG has
+ * agreed to follow the convention that parameter types are globally
+ * unique (and why not, with a 16-bit namespace). However, if this
+ * ever changes, there will need to be different parameter dispatch
+ * tables for each chunk type.
+ */
+static const dispatch_t parm_dispatch_table[] = {
+/* code F_SUM desc F_DTAIL desc parser function */
+ { PARM_UNKNOWN, "", "Unknown Parameter",
+ parse_opaque_param },
+ { PARM_HBINFO, "", "Heartbeat Info",
+ parse_opaque_param },
+ { PARM_ADDR4, "", "IPv4 Address",
+ parse_ip4_param },
+ { PARM_ADDR6, "", "IPv6 Address",
+ parse_ip6_param },
+ { PARM_COOKIE, "", "Cookie",
+ parse_opaque_param },
+ { PARM_UNRECOGNIZED, "", "Unrecognized Param",
+ parse_encap_param },
+ { PARM_COOKIE_PRESERVE, "", "Cookie Preservative",
+ parse_opaque_param },
+ { 10, "", "Reserved for ECN",
+ parse_opaque_param },
+ { PARM_ADDR_HOST_NAME, "", "Host Name Parameter",
+ parse_opaque_param },
+ { PARM_SUPP_ADDRS, "", "Supported Addresses",
+ parse_suppaddr_param },
+ { PARM_ECN_CAPABLE, "", "ECN Capable",
+ parse_opaque_param },
+ { PARM_ADD_IP, "", "Add IP",
+ parse_addip_param },
+ { PARM_DEL_IP, "", "Del IP",
+ parse_addip_param },
+ { PARM_ASCONF_ERROR, "", "ASCONF Error Ind",
+ parse_asconferr_param },
+ { PARM_PRIMARY_ADDR, "", "Set Primary Address",
+ parse_addip_param },
+ { PARM_FORWARD_TSN, "", "Forward TSN",
+ NULL },
+ { PARM_ASCONF_SUCCESS, "", "ASCONF Success Ind",
+ parse_asconfok_param }
+};
+
+/*
+ * Errors have the same wire format at parameters.
+ */
+static const dispatch_t err_dispatch_table[] = {
+/* code F_SUM desc F_DTAIL desc parser function */
+ { SCTP_ERR_UNKNOWN, "", "Unknown Error",
+ parse_opaque_param },
+ { SCTP_ERR_BAD_SID, "", "Invalid Stream ID",
+ parse_opaque_param },
+ { SCTP_ERR_MISSING_PARM, "", "Missing Parameter",
+ parse_opaque_param },
+ { SCTP_ERR_STALE_COOKIE, "", "Stale Cookie",
+ parse_int32_param },
+ { SCTP_ERR_NO_RESOURCES, "", "Out Of Resources",
+ parse_opaque_param },
+ { SCTP_ERR_BAD_ADDR, "", "Unresolvable Address",
+ parse_opaque_param },
+ { SCTP_ERR_UNREC_CHUNK, "", "Unrecognized Chunk",
+ parse_unrec_chunk },
+ { SCTP_ERR_BAD_MANDPARM, "", "Bad Mandatory Parameter",
+ parse_opaque_param },
+ { SCTP_ERR_UNREC_PARM, "", "Unrecognized Parameter",
+ parse_opaque_param },
+ { SCTP_ERR_NO_USR_DATA, "", "No User Data",
+ parse_int32_param },
+ { SCTP_ERR_COOKIE_SHUT, "", "Cookie During Shutdown",
+ parse_opaque_param },
+ { SCTP_ERR_DELETE_LASTADDR, "", "Delete Last Remaining Address",
+ parse_addiperr_param },
+ { SCTP_ERR_RESOURCE_SHORTAGE, "", "Resource Shortage",
+ parse_addiperr_param },
+ { SCTP_ERR_DELETE_SRCADDR, "", "Delete Source IP Address",
+ parse_addiperr_param },
+ { SCTP_ERR_AUTH_ERR, "", "Not authorized",
+ parse_addiperr_param }
+};
+
+/*
+ * These are global because the data chunk parser needs them to dispatch
+ * to ULPs. The alternative is to add source and dest port arguments
+ * to every parser, which seems even messier (since *only* the data
+ * chunk parser needs it)...
+ */
+static in_port_t sport, dport;
+
+/* Summary line miscellany */
+static int sumlen;
+static char scratch[MAXLINE];
+static char *sumline;
+
+#define SUMAPPEND(fmt) \
+ sumlen -= snprintf fmt; \
+ (void) strlcat(sumline, scratch, sumlen)
+
+#define DUMPHEX_MAX 16
+
+static const dispatch_t *
+lookup_dispatch(int id, const dispatch_t *tbl, int tblsz)
+{
+ int i;
+
+ /*
+ * Try fast lookup first. The common chunks defined in RFC2960
+ * will have indices aligned with their IDs, so this works for
+ * the common case.
+ */
+ if (id < (tblsz - 1)) {
+ if (id == tbl[id].id) {
+ return (tbl + id);
+ }
+ }
+
+ /*
+ * Nope - probably an extension. Search the whole table,
+ * starting from the end, since extensions are at the end.
+ */
+ for (i = tblsz - 1; i >= 0; i--) {
+ if (id == tbl[i].id) {
+ return (tbl + i);
+ }
+ }
+
+ return (NULL);
+}
+
+/*
+ * Dumps no more than the first DUMPHEX_MAX bytes in hex. If
+ * the user wants more, they can use the -x option to snoop.
+ */
+static void
+dumphex(const uchar_t *payload, int payload_len, char *msg)
+{
+ int index;
+ int end;
+ char buf[BUFLEN];
+
+ if (payload_len == 0) {
+ return;
+ }
+
+ end = payload_len > DUMPHEX_MAX ? DUMPHEX_MAX : payload_len;
+
+ for (index = 0; index < end; index++) {
+ (void) snprintf(&buf[index * 3], 4, " %.2x", payload[index]);
+ }
+
+ if (payload_len > DUMPHEX_MAX) {
+ (void) strlcat(buf, " ...", BUFLEN);
+ }
+
+ (void) snprintf(get_line(0, 0), BUFLEN, msg, buf);
+}
+
+/*
+ * Present perscribed action for unknowns according to rfc2960. Works
+ * for chunks and parameters as well if the parameter type is
+ * shifted 8 bits right.
+ */
+static const char *
+get_action_desc(uint8_t id)
+{
+ if ((id & 0xc0) == 0xc0) {
+ return (": skip on unknown, return error");
+ } else if ((id & 0x80) == 0x80) {
+ return (": skip on unknown, no error");
+ } else if ((id & 0x40) == 0x40) {
+ return (": stop on unknown, return error");
+ }
+
+ /* Top two bits are clear */
+ return (": stop on unknown, no error");
+}
+
+/* ARGSUSED */
+static void
+parse_asconfok_param(int flags, uint8_t notused, const void *data, int dlen)
+{
+ uint32_t *cid;
+
+ if (dlen < sizeof (*cid)) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " ==> Incomplete ASCONF Success Ind parameter");
+ return;
+ }
+ cid = (uint32_t *)data;
+ (void) snprintf(get_line(0, 0), get_line_remain(), " ASCONF CID = %u",
+ ntohl(*cid));
+}
+
+/* ARGSUSED */
+static void
+parse_asconferr_param(int flags, uint8_t notused, const void *data, int dlen)
+{
+ uint32_t *cid;
+
+ if (dlen < sizeof (*cid)) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " ==> Incomplete ASCONF Error Ind parameter");
+ return;
+ }
+ cid = (uint32_t *)data;
+ (void) snprintf(get_line(0, 0), get_line_remain(), " ASCONF CID = %u",
+ ntohl(*cid));
+
+ interpret_params(cid + 1, dlen - sizeof (*cid), "Error",
+ err_dispatch_table, A_CNT(err_dispatch_table), flags);
+}
+
+/* ARGSUSED */
+static void
+parse_addiperr_param(int flags, uint8_t notused, const void *data, int dlen)
+{
+
+ interpret_params(data, dlen, "Parameter",
+ parm_dispatch_table, A_CNT(parm_dispatch_table), flags);
+}
+
+/* ARGSUSED */
+static void
+parse_addip_param(int flags, uint8_t notused, const void *data, int dlen)
+{
+
+ uint32_t *cid;
+
+ if (dlen < sizeof (*cid)) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " ==> Incomplete ASCONF Error Ind parameter");
+ return;
+ }
+ cid = (uint32_t *)data;
+ (void) snprintf(get_line(0, 0), get_line_remain(), " ASCONF CID = %u",
+ ntohl(*cid));
+
+ interpret_params(cid + 1, dlen - sizeof (*cid), "Parameter",
+ parm_dispatch_table, A_CNT(parm_dispatch_table), flags);
+}
+
+/* ARGSUSED */
+static void
+parse_ip4_param(int flags, uint8_t notused, const void *data, int datalen)
+{
+ char abuf[INET_ADDRSTRLEN];
+ char *ap;
+
+ if (datalen < sizeof (in_addr_t)) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " ==> Incomplete IPv4 Addr parameter");
+ return;
+ }
+
+ ap = (char *)inet_ntop(AF_INET, data, abuf, INET_ADDRSTRLEN);
+ if (ap == NULL) {
+ ap = "<Bad Address>";
+ }
+
+ (void) snprintf(get_line(0, 0), get_line_remain(), " Addr = %s", ap);
+}
+
+/* ARGSUSED */
+static void
+parse_ip6_param(int flags, uint8_t notused, const void *data, int datalen)
+{
+ char abuf[INET6_ADDRSTRLEN];
+ char *ap;
+
+ if (datalen < sizeof (in6_addr_t)) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " ==> Incomplete IPv6 Addr parameter");
+ return;
+ }
+
+ ap = (char *)inet_ntop(AF_INET6, data, abuf, INET6_ADDRSTRLEN);
+ if (ap == NULL) {
+ ap = "<Bad Address>";
+ }
+
+ (void) snprintf(get_line(0, 0), get_line_remain(), " Addr = %s", ap);
+}
+
+/* ARGSUSED */
+static void
+parse_int32_param(int flags, uint8_t notused, const void *data, int datalen)
+{
+ if (datalen < 4) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " ==> Incomplete INT32 parameter");
+ return;
+ }
+ (void) snprintf(get_line(0, 0), get_line_remain(), " INT32 = %u",
+ ntohl(*(uint32_t *)data));
+}
+
+/* ARGSUSED */
+static void
+parse_suppaddr_param(int flags, uint8_t notused, const void *data, int dlen)
+{
+ const uint16_t *type;
+
+ if (dlen < 2) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "==> Incomplete Supported Addr parameter");
+ return;
+ }
+
+ type = data;
+ while (dlen > 0) {
+ switch (ntohs(*type)) {
+ case PARM_ADDR4:
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " IPv4");
+ break;
+ case PARM_ADDR6:
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " IPv6");
+ break;
+ case PARM_ADDR_HOST_NAME:
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " Host Name");
+ break;
+ default:
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Unknown Type (%hu)", ntohs(*type));
+ break;
+ }
+ dlen -= sizeof (*type);
+ type++;
+ }
+}
+
+/*ARGSUSED*/
+static void
+parse_encap_param(int flags, uint8_t notused, const void *data, int dlen)
+{
+ if (dlen < sizeof (sctp_parm_hdr_t)) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "==> Incomplete Parameter");
+ return;
+ }
+
+ interpret_params(data, dlen, "Parameter",
+ parm_dispatch_table, A_CNT(parm_dispatch_table), flags);
+}
+
+/* ARGSUSED */
+static void
+parse_unrec_chunk(int flags, uint8_t cflags, const void *data, int datalen)
+{
+ const sctp_chunk_hdr_t *cp = data;
+ const dispatch_t *dp;
+ const char *actstr;
+
+ if (datalen < sizeof (*cp)) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "==> Incomplete Unrecognized Chunk Error");
+ return;
+ }
+
+ /* Maybe snoop knows about this chunk? */
+ dp = lookup_dispatch(cp->sch_id, chunk_dispatch_table,
+ A_CNT(chunk_dispatch_table));
+ if (dp != NULL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " Chunk Type = %u (%s)", cp->sch_id, dp->vdesc);
+ } else {
+ actstr = get_action_desc(cp->sch_id);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " Chunk Type = %u%s", cp->sch_id, actstr);
+ }
+}
+
+/*
+ * Same as parse_opaque_chunk except for the indentation.
+ */
+/* ARGSUSED */
+static void
+parse_opaque_param(int flags, uint8_t cflags, const void *data, int datalen)
+{
+ dumphex(data, datalen, " Data = %s");
+}
+
+/*
+ * Loops through all parameters (or errors) until it has read
+ * datalen bytes of information, finding a parser for each.
+ * The tbl argument allows the caller to specify which dispatch
+ * table to use, making this function useful for both parameters
+ * and errors. The type argument is used to denote whether this
+ * is an error or parameter in detailed mode.
+ */
+static void
+interpret_params(const void *data, int datalen, char *type,
+ const dispatch_t *tbl, int tbl_size, int flags)
+{
+ const sctp_parm_hdr_t *hdr = data;
+ uint16_t plen;
+ uint16_t ptype;
+ const char *desc;
+ parse_func_t *parse;
+ int pad;
+ const dispatch_t *dp;
+ const char *actstr;
+
+ for (;;) {
+ /*
+ * Adjust for padding: if the address isn't aligned, there
+ * should be some padding. So skip over the padding and
+ * adjust hdr accordingly. RFC2960 mandates that all
+ * parameters must be 32-bit aligned WRT the enclosing chunk,
+ * which ensures that this parameter header will
+ * be 32-bit aligned in memory. We must, of course, bounds
+ * check fraglen before actually trying to use hdr, in
+ * case the packet has been mangled or is the product
+ * of a buggy implementation.
+ */
+ if ((pad = (uintptr_t)hdr % SCTP_ALIGN) != 0) {
+ pad = SCTP_ALIGN - pad;
+ datalen -= pad;
+ /* LINTED pointer cast may result in improper alignment */
+ hdr = (sctp_parm_hdr_t *)((char *)hdr + pad);
+ }
+
+ /* Need to compare against 0 1st, since sizeof is unsigned */
+ if (datalen < 0 || datalen < sizeof (*hdr)) {
+ if (datalen > 0) {
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ "==> Extra data after last parameter");
+ }
+ return;
+ }
+ plen = ntohs(hdr->sph_len);
+ if (datalen < plen || plen < sizeof (*hdr)) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " ==> Incomplete %s", type);
+ return;
+ }
+
+ /* Get description and parser */
+ ptype = ntohs(hdr->sph_type);
+ desc = "Unknown Parameter Type";
+ parse = parse_opaque_param;
+ dp = lookup_dispatch(ptype, tbl, tbl_size);
+ if (dp != NULL) {
+ desc = dp->vdesc;
+ parse = dp->parse;
+ }
+
+ show_space();
+ if (dp != NULL) {
+ actstr = "";
+ } else {
+ actstr = get_action_desc((uint8_t)(ptype >> 8));
+ }
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " ------- SCTP %s Type = %s (%u%s)", type, desc, ptype,
+ actstr);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " Data length = %hu", plen - sizeof (*hdr));
+
+ if (parse != NULL) {
+ parse(flags, 0, (char *)(hdr + 1),
+ plen - sizeof (*hdr));
+ }
+ datalen -= plen;
+ /* LINTED pointer cast may result in improper alignment */
+ hdr = (sctp_parm_hdr_t *)((char *)hdr + plen);
+ }
+}
+
+/* ARGSUSED */
+static void
+parse_ftsn_chunk(int flags, uint8_t cflags, const void *data, int datalen)
+{
+ uint32_t *ftsn;
+ ftsn_entry_t *ftsn_entry;
+
+ if (datalen < (sizeof (*ftsn) + sizeof (*ftsn_entry))) {
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "==> Incomplete FORWARD-TSN chunk");
+ }
+ return;
+ }
+
+ ftsn = (uint32_t *)data;
+ if (flags & F_SUM) {
+ SUMAPPEND((scratch, MAXLINE, "CTSN %x ", ntohl(*ftsn)));
+ return;
+ }
+ (void) snprintf(get_line(0, 0), get_line_remain(), "Cum TSN= %x",
+ ntohl(*ftsn));
+
+ datalen -= sizeof (*ftsn);
+ ftsn_entry = (ftsn_entry_t *)(ftsn + 1);
+ while (datalen >= sizeof (*ftsn_entry)) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "SID = %u : SSN = %u", ntohs(ftsn_entry->ftsn_sid),
+ ntohs(ftsn_entry->ftsn_ssn));
+ datalen -= sizeof (*ftsn_entry);
+ ftsn_entry++;
+ }
+}
+
+/* ARGSUSED */
+static void
+parse_asconf_chunk(int flags, uint8_t cflags, const void *data, int datalen)
+{
+ uint32_t *sn;
+
+ if (datalen < sizeof (*sn)) {
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "==> Incomplete ASCONF chunk");
+ }
+ return;
+ }
+
+ sn = (uint32_t *)data;
+ if (flags & F_SUM) {
+ SUMAPPEND((scratch, MAXLINE, "sn %x ", ntohl(*sn)));
+ return;
+ }
+ (void) snprintf(get_line(0, 0), get_line_remain(), "Serial Number= %x",
+ ntohl(*sn));
+ interpret_params(sn + 1, datalen - sizeof (*sn), "Parameter",
+ parm_dispatch_table, A_CNT(parm_dispatch_table), flags);
+}
+
+static void
+parse_init_chunk(int flags, uint8_t cflags, const void *data, int datalen)
+{
+ const sctp_init_chunk_t *icp = data;
+
+ if (datalen < sizeof (*icp)) {
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "==> Incomplete INIT chunk");
+ }
+ return;
+ }
+
+ if (flags & F_SUM) {
+ SUMAPPEND((scratch, MAXLINE, "tsn %x str %hu/%hu win %u ",
+ ntohl(icp->sic_inittsn), ntohs(icp->sic_outstr),
+ ntohs(icp->sic_instr), ntohl(icp->sic_a_rwnd)));
+ return;
+ }
+
+ (void) snprintf(get_line(0, 0), get_line_remain(), "Flags = 0x%.2x",
+ cflags);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Initiate tag = 0x%.8x", ntohl(icp->sic_inittag));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Advertised receiver window credit = %u", ntohl(icp->sic_a_rwnd));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Outbound streams = %hu", ntohs(icp->sic_outstr));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Inbound streams = %hu", ntohs(icp->sic_instr));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Initial TSN = 0x%.8x", ntohl(icp->sic_inittsn));
+
+ if (datalen > sizeof (*icp)) {
+ interpret_params(icp + 1, datalen - sizeof (*icp),
+ "Parameter", parm_dispatch_table,
+ A_CNT(parm_dispatch_table), flags);
+ }
+}
+
+static void
+parse_data_chunk(int flags, uint8_t cflags, const void *data, int datalen)
+{
+ const sctp_data_chunk_t *dcp = data;
+ char *payload;
+ uint32_t ppid;
+
+ if (datalen < sizeof (*dcp)) {
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "==> Incomplete DATA chunk %d (%d)", datalen,
+ sizeof (*dcp));
+ }
+ return;
+ }
+
+ ppid = ntohl(dcp->sdc_payload_id);
+
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "flags = 0x%.2x", cflags);
+ (void) snprintf(get_line(0, 0), get_line_remain(), " %s",
+ getflag(cflags, SCTP_DATA_UBIT, "unordered", "ordered"));
+ (void) snprintf(get_line(0, 0), get_line_remain(), " %s",
+ getflag(cflags, SCTP_DATA_BBIT,
+ "beginning", "(beginning unset)"));
+ (void) snprintf(get_line(0, 0), get_line_remain(), " %s",
+ getflag(cflags, SCTP_DATA_EBIT, "end", "(end unset)"));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "TSN = 0x%.8x", ntohl(dcp->sdc_tsn));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Stream ID = %hu", ntohs(dcp->sdc_sid));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Stream Sequence Number = %hu", ntohs(dcp->sdc_ssn));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Payload Protocol ID = 0x%.8x", ppid);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Data Length = %d", datalen - sizeof (*dcp));
+ show_space();
+ }
+ if (flags & F_SUM) {
+ SUMAPPEND((scratch, MAXLINE, "tsn %x str %hu/%hu ppid %x ",
+ ntohl(dcp->sdc_tsn), ntohs(dcp->sdc_sid),
+ ntohs(dcp->sdc_ssn), ppid));
+ }
+
+ /*
+ * Go to the next protocol layer, but not if we are in
+ * summary mode only. In summary mode, each ULP parse would
+ * create a new line, and if there were several data chunks
+ * bundled together in the packet, this would confuse snoop's
+ * packet numbering and timestamping.
+ *
+ * SCTP carries two ways to determine an ULP: ports and the
+ * payload protocol identifier (ppid). Since ports are the
+ * better entrenched convention, we first try interpret_reserved().
+ * If that fails to find a parser, we try by the PPID.
+ */
+ if (!(flags & F_ALLSUM) && !(flags & F_DTAIL)) {
+ return;
+ }
+
+ payload = (char *)(dcp + 1);
+ if (!interpret_reserved(flags, IPPROTO_SCTP, sport, dport, payload,
+ datalen - sizeof (*dcp)) && ppid != 0) {
+
+ interpret_protoid(flags, ppid, payload,
+ datalen - sizeof (*dcp));
+ }
+
+ /*
+ * Reset the protocol prefix, since it may have been changed
+ * by a ULP interpreter.
+ */
+ prot_prefix = "SCTP: ";
+}
+
+/* ARGSUSED */
+static void
+parse_sack_chunk(int flags, uint8_t cflags, const void *data, int datalen)
+{
+ const sctp_sack_chunk_t *scp = data;
+ uint16_t numfrags, numdups;
+ sctp_sack_frag_t *frag;
+ int i;
+ uint32_t *tsn;
+
+ if (datalen < sizeof (*scp)) {
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "==> Incomplete SACK chunk");
+ }
+ return;
+ }
+
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Cumulative TSN ACK = 0x%.8x", ntohl(scp->ssc_cumtsn));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Advertised Receiver Window Credit = %u",
+ ntohl(scp->ssc_a_rwnd));
+ numfrags = ntohs(scp->ssc_numfrags);
+ numdups = ntohs(scp->ssc_numdups);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Number of Fragments = %hu", numfrags);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Number of Duplicates = %hu", numdups);
+
+ /* Display any gap reports */
+ datalen -= sizeof (*scp);
+ if (datalen < (numfrags * sizeof (*frag))) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " ==> Malformed gap report listing");
+ return;
+ }
+ frag = (sctp_sack_frag_t *)(scp + 1);
+ for (i = 0; i < numfrags; i++) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " Fragment #%d: Start = %hu, end = %hu", i,
+ ntohs(frag->ssf_start), ntohs(frag->ssf_end));
+ frag += 1;
+ }
+
+ /* Display any duplicate reports */
+ datalen -= numfrags * sizeof (*frag);
+ if (datalen < (numdups * sizeof (*tsn))) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " ==> Malformed duplicate report listing");
+ return;
+ }
+ /* LINTED pointer cast may result in improper alignment */
+ tsn = (uint32_t *)frag;
+ for (i = 0; i < numdups; i++) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " Duplicate #%d: TSN = %x", i, *tsn);
+ tsn++;
+ }
+ }
+ if (flags & F_SUM) {
+ SUMAPPEND((scratch, MAXLINE,
+ "tsn %x win %u gaps/dups %hu/%hu ", ntohl(scp->ssc_cumtsn),
+ ntohl(scp->ssc_a_rwnd), ntohs(scp->ssc_numfrags),
+ ntohs(scp->ssc_numdups)));
+ }
+}
+
+/* ARGSUSED */
+static void
+parse_shutdown_chunk(int flags, uint8_t cflags, const void *data, int datalen)
+{
+ const uint32_t *cumtsn = data;
+
+ if (datalen < sizeof (*cumtsn)) {
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "==> Incomplete Shutdown chunk");
+ }
+ return;
+ }
+
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Cumulative TSN = 0x%.8x", ntohl(*cumtsn));
+ }
+ if (flags & F_SUM) {
+ SUMAPPEND((scratch, MAXLINE, "tsn %x", ntohl(*cumtsn)));
+ }
+}
+
+/* ARGSUSED */
+static void
+parse_error_chunk(int flags, uint8_t cflags, const void *data, int datalen)
+{
+ if (!(flags & F_DTAIL)) {
+ return;
+ }
+
+ interpret_params(data, datalen, "Error", err_dispatch_table,
+ A_CNT(err_dispatch_table), flags);
+}
+
+static void
+parse_abort_chunk(int flags, uint8_t cflags, const void *data, int datalen)
+{
+ if (!(flags & F_DTAIL)) {
+ return;
+ }
+
+ (void) snprintf(get_line(0, 0), get_line_remain(), "flags = 0x%.2x",
+ cflags);
+ (void) snprintf(get_line(0, 0), get_line_remain(), " %s",
+ getflag(cflags, SCTP_TBIT, "TCB not destroyed", "TCB destroyed"));
+
+ interpret_params(data, datalen, "Error", err_dispatch_table,
+ A_CNT(err_dispatch_table), flags);
+}
+
+/* ARGSUSED2 */
+static void
+parse_shutdone_chunk(int flags, uint8_t cflags, const void *data, int datalen)
+{
+ if (!(flags & F_DTAIL)) {
+ return;
+ }
+
+ (void) snprintf(get_line(0, 0), get_line_remain(), "flags = 0x%.2x",
+ cflags);
+ (void) snprintf(get_line(0, 0), get_line_remain(), " %s",
+ getflag(cflags, SCTP_TBIT, "TCB not destroyed", "TCB destroyed"));
+}
+
+/* ARGSUSED */
+static void
+parse_opaque_chunk(int flags, uint8_t cflags, const void *data, int datalen)
+{
+ if (!(flags & F_DTAIL)) {
+ return;
+ }
+ if (datalen == 0) {
+ return;
+ }
+
+ dumphex(data, datalen, "Data = %s");
+}
+
+/*
+ * Loops through all chunks until it has read fraglen bytes of
+ * information, finding a parser for each. If any parameters are
+ * present, interpret_params() is then called. Returns the remaining
+ * fraglen.
+ */
+static int
+interpret_chunks(int flags, sctp_chunk_hdr_t *cp, int fraglen)
+{
+ uint16_t clen;
+ int signed_len;
+ int pad;
+ const char *desc;
+ parse_func_t *parse;
+ const dispatch_t *dp;
+ const char *actstr;
+
+ for (;;) {
+ /*
+ * Adjust for padding: if the address isn't aligned, there
+ * should be some padding. So skip over the padding and
+ * adjust hdr accordingly. RFC2960 mandates that all
+ * chunks must be 32-bit aligned WRT the SCTP common hdr,
+ * which ensures that this chunk header will
+ * be 32-bit aligned in memory. We must, of course, bounds
+ * check fraglen before actually trying to use hdr, in
+ * case the packet has been mangled or is the product
+ * of a buggy implementation.
+ */
+ if ((pad = (uintptr_t)cp % SCTP_ALIGN) != 0) {
+ pad = SCTP_ALIGN - pad;
+ fraglen -= pad;
+ /* LINTED pointer cast may result in improper alignment */
+ cp = (sctp_chunk_hdr_t *)((char *)cp + pad);
+ }
+
+ /* Need to compare against 0 1st, since sizeof is unsigned */
+ if (fraglen < 0 || fraglen < sizeof (*cp)) {
+ if (fraglen > 0 && flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ "==> Extra data after last chunk");
+ }
+ return (fraglen);
+ }
+
+ clen = ntohs(cp->sch_len);
+ if (fraglen < clen) {
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(), "==> Corrupted chunk");
+ }
+ return (fraglen);
+ }
+
+ signed_len = clen - sizeof (*cp);
+ if (signed_len < 0) {
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ "==> Incomplete or corrupted chunk");
+ }
+ return (0);
+ }
+
+ /* Get description and parser */
+ dp = lookup_dispatch(cp->sch_id, chunk_dispatch_table,
+ A_CNT(chunk_dispatch_table));
+ if (dp != NULL) {
+ if (flags & F_SUM) {
+ desc = dp->sdesc;
+ } else if (flags & F_DTAIL) {
+ desc = dp->vdesc;
+ }
+ parse = dp->parse;
+ } else {
+ if (flags & F_SUM) {
+ desc = "UNK";
+ } else if (flags & F_DTAIL) {
+ desc = "Unknown Chunk Type";
+ }
+ parse = parse_opaque_chunk;
+ }
+
+ if (flags & F_SUM) {
+ SUMAPPEND((scratch, MAXLINE, "%s ", desc));
+ }
+ if (flags & F_DTAIL) {
+ show_space();
+
+ if (dp != NULL) {
+ actstr = "";
+ } else {
+ actstr = get_action_desc(cp->sch_id);
+ }
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "------- SCTP Chunk Type = %s (%u%s)", desc,
+ cp->sch_id, actstr);
+
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Chunk length = %hu", clen);
+ }
+
+ if (parse != NULL) {
+ parse(flags, cp->sch_flags, (char *)(cp + 1),
+ signed_len);
+ }
+
+ fraglen -= clen;
+
+ /* LINTED pointer cast may result in improper alignment */
+ cp = (sctp_chunk_hdr_t *)((char *)cp + clen);
+ }
+}
+
+void
+interpret_sctp(int flags, sctp_hdr_t *sctp, int iplen, int fraglen)
+{
+ int len_from_iphdr;
+ sctp_chunk_hdr_t *cp;
+ char *pn;
+ char buff[32];
+
+ /*
+ * Alignment check. If the header is 32-bit aligned, all other
+ * protocol units will also be aligned, as mandated by rfc2960.
+ * Buggy packets will be caught and flagged by chunk and
+ * parameter bounds checking.
+ * If the header is not aligned, however, we drop the packet.
+ */
+ if (!IS_P2ALIGNED(sctp, SCTP_ALIGN)) {
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "==> SCTP header not aligned, dropping");
+ }
+ return;
+ }
+
+ fraglen -= sizeof (*sctp);
+ if (fraglen < 0) {
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "==> Incomplete sctp header");
+ }
+ return;
+ }
+ /* If fraglen is somehow longer than the IP payload, adjust it */
+ len_from_iphdr = iplen - sizeof (*sctp);
+ if (fraglen > len_from_iphdr) {
+ fraglen = len_from_iphdr;
+ }
+
+ /* Keep track of the ports */
+ sport = ntohs(sctp->sh_sport);
+ dport = ntohs(sctp->sh_dport);
+
+ /* Set pointer to first chunk */
+ cp = (sctp_chunk_hdr_t *)(sctp + 1);
+
+ if (flags & F_SUM) {
+ sumline = get_sum_line();
+ *sumline = '\0';
+ sumlen = MAXLINE;
+
+ SUMAPPEND((scratch, MAXLINE, "SCTP D=%d S=%d ", dport, sport));
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("SCTP: ", "SCTP Header", fraglen);
+ show_space();
+
+ pn = getportname(IPPROTO_SCTP, (ushort_t)sport);
+ if (pn == NULL) {
+ pn = "";
+ } else {
+ (void) snprintf(buff, sizeof (buff), "(%s)", pn);
+ pn = buff;
+ }
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Source port = %hu %s", sport, pn);
+
+ pn = getportname(IPPROTO_SCTP, (ushort_t)dport);
+ if (pn == NULL) {
+ pn = "";
+ } else {
+ (void) snprintf(buff, sizeof (buff), "(%s)", pn);
+ pn = buff;
+ }
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Destination port = %hu %s", dport, pn);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Verification tag = 0x%.8x", ntohl(sctp->sh_verf));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "CRC-32c = 0x%.8x", ntohl(sctp->sh_chksum));
+ }
+
+ (void) interpret_chunks(flags, cp, fraglen);
+
+ if (flags & F_DTAIL) {
+ show_space();
+ }
+}
+
+/*
+ * Payload protocol ID table. Add new ULP information and parsers
+ * here.
+ */
+
+struct protoid_table {
+ int pid_num;
+ char *pid_short;
+ char *pid_long;
+};
+
+static struct protoid_table pid_sctp[] = {
+ 1, "IUA", "ISDN Q.921 User Adaption Layer",
+ 2, "M2UA", "SS7 MTP2 User Adaption Layer",
+ 3, "M3UA", "SS7 MTP3 User Adaption Layer",
+ 4, "SUA", "SS7 SCCP User Adaption Layer",
+ 5, "M2PA", "SS7 MTP2-User Peer-to-Peer Adaption Layer",
+ 6, "V5UA", "V5UA",
+ 0, NULL, "",
+};
+
+static void
+interpret_protoid(int flags, uint32_t ppid, char *data, int dlen)
+{
+ struct protoid_table *p;
+ char pbuf[16];
+
+ /*
+ * Branch to a ULP interpreter here, or continue on to
+ * the default parser, which just tries to display
+ * printable characters from the payload.
+ */
+
+ for (p = pid_sctp; p->pid_num; p++) {
+ if (ppid == p->pid_num) {
+ if (flags & F_SUM) {
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "D=%d S=%d %s %s", dport, sport,
+ p->pid_short, show_string(data, dlen, 20));
+ }
+
+ if (flags & F_DTAIL) {
+ (void) snprintf(pbuf, MAXLINE, "%s: ",
+ p->pid_short);
+ show_header(pbuf, p->pid_long, dlen);
+ show_space();
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(), "\"%s\"",
+ show_string(data, dlen, 60));
+ show_trailer();
+ }
+
+ return;
+ }
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_slp.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_slp.c
new file mode 100644
index 0000000000..9feb3e9b27
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_slp.c
@@ -0,0 +1,1963 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1998,2000 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <arpa/inet.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <iconv.h>
+#include "snoop.h"
+#include "slp.h"
+
+#define MAXSUMLEN 30
+
+/* define VERIFYSLP to enable full message checking in summary mode */
+#define VERIFYSLP
+
+/* Globals -- ugly, yes, but fast and easy in macros */
+static int msglength;
+static int retlength;
+static char *msgend; /* the end of the summary message buffer */
+static char *p; /* current position in the packet */
+static char *msgbuf; /* message buffer for summary mode */
+static boolean_t url_auth = B_FALSE;
+static boolean_t attr_auth = B_FALSE;
+static boolean_t fresh = B_FALSE;
+static boolean_t overflow = B_FALSE;
+static int v1_charset = 0; /* character set; only in V1 */
+
+/* Entry points for parsing the protocol */
+static int interpret_slp_v1(int, struct slpv1_hdr *, int);
+static int interpret_slp_v2(int, struct slpv2_hdr *, int);
+
+/* header parsing */
+static int v1_header(int, struct slpv1_hdr *, int);
+static int v2_header(int, struct slpv2_hdr *, int *, int);
+static int v2_finish(struct slpv2_hdr *, int);
+
+/* V2 auth blocks */
+static int slpv2_authblock(int);
+
+/* From snoop_rport: */
+extern int add_transient(int, int (*)());
+
+/*
+ * Functions for parsing each protocol message
+ * Each function takes the interpreter's flags argument as its input
+ * parameter, and returns 1 on success, or 0 on message corruption.
+ * retlength is set as a side-effect in summary mode.
+ */
+static int v2_srv_rqst(int);
+static int v2_srv_rply(int);
+static int v2_srv_reg(int);
+static int v2_srv_dereg(int);
+static int v2_srv_ack(int);
+static int v2_attr_rqst(int);
+static int v2_attr_rply(int);
+static int v2_daadvert(int);
+static int v2_srv_type_rqst(int);
+static int v2_srv_type_rply(int);
+static int v2_saadvert(int);
+
+static int v1_srv_rqst(int);
+static int v1_srv_rply(int);
+static int v1_srv_reg(int);
+static int v1_srv_dereg(int);
+static int v1_srv_ack(int);
+static int v1_attr_rqst(int);
+static int v1_attr_rply(int);
+static int v1_daadvert(int);
+static int v1_srv_type_rqst(int);
+static int v1_srv_type_rply(int);
+
+/*
+ * The dispatch tables for handling individual messages, keyed by
+ * function number.
+ */
+typedef int function_handler();
+
+#define V2_MAX_FUNCTION 11
+
+static function_handler *v2_functions[V2_MAX_FUNCTION + 1] = {
+ (function_handler *) NULL,
+ (function_handler *) v2_srv_rqst,
+ (function_handler *) v2_srv_rply,
+ (function_handler *) v2_srv_reg,
+ (function_handler *) v2_srv_dereg,
+ (function_handler *) v2_srv_ack,
+ (function_handler *) v2_attr_rqst,
+ (function_handler *) v2_attr_rply,
+ (function_handler *) v2_daadvert,
+ (function_handler *) v2_srv_type_rqst,
+ (function_handler *) v2_srv_type_rply,
+ (function_handler *) v2_saadvert };
+
+#define V1_MAX_FUNCTION 10
+
+static function_handler *v1_functions[V1_MAX_FUNCTION + 1] = {
+ (function_handler *) NULL,
+ (function_handler *) v1_srv_rqst,
+ (function_handler *) v1_srv_rply,
+ (function_handler *) v1_srv_reg,
+ (function_handler *) v1_srv_dereg,
+ (function_handler *) v1_srv_ack,
+ (function_handler *) v1_attr_rqst,
+ (function_handler *) v1_attr_rply,
+ (function_handler *) v1_daadvert,
+ (function_handler *) v1_srv_type_rqst,
+ (function_handler *) v1_srv_type_rply };
+
+/* TCP continuation handling */
+static boolean_t tcp_continuation = B_FALSE;
+
+#define MAX_TCPCONT 16
+
+static struct tcp_cont {
+ int dst_port;
+ char *msg;
+ int totallen;
+ int curr_offset;
+} *tcp_cont[MAX_TCPCONT];
+
+static int current_tcp_cont;
+
+static void reg_tcp_cont(char *, int, int, int);
+static int add_tcp_cont(struct tcp_cont *, char *, int);
+static struct tcp_cont *find_tcp_cont(int);
+static void remove_tcp_cont(int);
+
+/* Conversions from numbers to strings */
+static char *slpv2_func(int, boolean_t);
+static char *slpv2_error(unsigned short);
+static char *slpv1_func(int, boolean_t);
+static char *slpv1_error(unsigned short);
+static char *slpv1_charset(unsigned short);
+
+/*
+ * The only external entry point to the SLP interpreter. This function
+ * simply dispatches the packet based on the version.
+ */
+void interpret_slp(int flags, char *slp, int fraglen) {
+ extern int dst_port, curr_proto;
+ struct tcp_cont *tce = NULL;
+
+ msglength = fraglen;
+ retlength = 0;
+ p = slp;
+
+ /* check if this is a TCP continuation */
+ if (flags & F_DTAIL && curr_proto == IPPROTO_TCP) {
+ tce = find_tcp_cont(dst_port);
+ if (tce) {
+ if (add_tcp_cont(tce, slp, fraglen)) {
+ slp = tce->msg;
+ fraglen = tce->curr_offset;
+ tcp_continuation = B_TRUE;
+ }
+ }
+ }
+ if (*slp == 2 || tce)
+ interpret_slp_v2(flags, (void *)slp, fraglen);
+ else
+ interpret_slp_v1(flags, (void *)slp, fraglen);
+
+ tcp_continuation = B_FALSE;
+}
+
+/*
+ * Primitives. These are implemented as much as possible as macros for
+ * speed.
+ */
+
+#define FIELD_DEFAULT 0
+#define FIELD_PREVRESP 1
+#define FIELD_TYPENA 2
+
+static long long netval = 0; /* need signed 64 bit quantity */
+
+/* gets two bytes from p and leaves the result in netval */
+#define nbtohs() \
+ netval = ((int)(p[0] & 0xff)) << 8; \
+ netval += ((int)(p[1] & 0xff))
+
+/* gets four bytes from p and leaves the result in netval */
+#define nbtohl() \
+ netval = ((int)(p[0] & 0xff)) << 24; \
+ netval += ((int)(p[1] & 0xff)) << 16; \
+ netval += ((int)(p[2] & 0xff)) << 8; \
+ netval += ((int)(p[3] & 0xff))
+
+#define get_byte() \
+ if (msglength >= 1) { \
+ netval = *p; \
+ p++; \
+ msglength--; \
+ } else \
+ netval = -1
+
+#define GETBYTE(x) \
+ get_byte(); \
+ if ((retlength = netval) < 0) \
+ return (0); \
+ x = netval
+
+#define SKIPBYTE \
+ get_byte(); \
+ if ((retlength = netval) < 0) \
+ return (0); \
+
+/*
+ * gets two bytes from p, leaves the result in netval, and updates
+ * msglength and p.
+ */
+#define get_short() \
+ if (msglength >= sizeof (unsigned short)) { \
+ nbtohs(); \
+ p += sizeof (unsigned short); \
+ msglength -= sizeof (unsigned short); \
+ } else \
+ netval = -1
+
+#define GETSHORT(x) \
+ get_short(); \
+ if ((retlength = netval) < 0) \
+ return (0); \
+ x = netval
+
+#define SKIPSHORT \
+ get_short(); \
+ if ((retlength = netval) < 0) \
+ return (0)
+
+#define get_int24(pp) \
+ netval = ((int)((pp)[0] & 0xff)) << 16; \
+ netval += ((int)((pp)[1] & 0xff)) << 8; \
+ netval += ((int)((pp)[2] & 0xff))
+
+static void slp_prevresp(char *p) {
+ char *p2;
+
+ /* cycle through all entries */
+ for (; p != NULL; p = p2) {
+ p2 = strchr(p, ',');
+ if (p2 != NULL)
+ *p2++ = '\0';
+
+ /* print entry at p */
+ sprintf(get_line(0, 0), " \"%s\"", p);
+ }
+}
+
+static int skip_field(int type) {
+ unsigned short stringlen;
+
+ get_short();
+ if (netval < 0) {
+ return (-1);
+ }
+ stringlen = netval;
+
+ /* special case for NA field in SrvTypeRqst */
+ if (type == FIELD_TYPENA && stringlen == 0xffff) {
+ stringlen = 0;
+ }
+
+ if (stringlen > msglength) {
+ return (-1);
+ }
+
+ msglength -= stringlen;
+ p += stringlen;
+
+ return (stringlen);
+}
+
+#define SKIPFIELD(type) \
+ if ((retlength = skip_field(type)) < 0) \
+ return (0)
+
+#define GETFIELD \
+ get_short(); \
+ if ((retlength = netval) < 0) \
+ return (0); \
+ strncat(msgbuf, p, (retlength > MAXSUMLEN ? MAXSUMLEN : retlength)); \
+ p += retlength; \
+ msglength -= retlength
+
+/*
+ * Determines from the first five bytes of a potential SLP header
+ * if the following message is really an SLP message. Returns 1 if
+ * it is a real SLP message, 0 if not.
+ */
+int valid_slp(unsigned char *slphdr, int len) {
+ struct slpv1_hdr slp1;
+ struct slpv2_hdr slp2;
+
+ len -= (8 /* udp */ + 20 /* IP */ + 14 /* ether */);
+ /* a valid version will be 1 or 2 */
+ switch (*slphdr) {
+ case 1:
+ memcpy(&slp1, slphdr, 5);
+ /* valid function? */
+ if (slp1.function > V1_MAX_FUNCTION) {
+ return (0);
+ }
+ /* valid length heuristic */
+ if (slp1.length > len) {
+ return (0);
+ }
+ return (1);
+ case 2:
+ memcpy(&slp2, slphdr, 5);
+ /* valid function? */
+ if (slp2.function > V2_MAX_FUNCTION) {
+ return (0);
+ }
+ /* valid length heuristic */
+ get_int24(&(slp2.l1));
+ if (netval > len) {
+ return (0);
+ }
+ return (1);
+ default:
+ return (0);
+ }
+}
+
+/*
+ * Converts a V1 char encoding to UTF8. If this fails, returns 0,
+ * otherwise, 1. This function is the union of iconv UTF-8
+ * modules and character sets registered with IANA.
+ */
+static int make_utf8(char *outbuf, size_t outlen,
+ const char *inbuf, size_t inlen) {
+ iconv_t cd;
+ size_t converted;
+
+ switch (v1_charset) {
+ case 4:
+ case 1004:
+ cd = iconv_open("UTF-8", "8859-1");
+ break;
+ case 5:
+ cd = iconv_open("UTF-8", "8859-2");
+ break;
+ case 6:
+ cd = iconv_open("UTF-8", "8859-3");
+ break;
+ case 7:
+ cd = iconv_open("UTF-8", "8859-4");
+ break;
+ case 8:
+ cd = iconv_open("UTF-8", "8859-5");
+ break;
+ case 9:
+ cd = iconv_open("UTF-8", "8859-6");
+ break;
+ case 10:
+ cd = iconv_open("UTF-8", "8859-7");
+ break;
+ case 11:
+ cd = iconv_open("UTF-8", "8859-8");
+ break;
+ case 12:
+ cd = iconv_open("UTF-8", "8859-9");
+ break;
+ case 13:
+ cd = iconv_open("UTF-8", "8859-10");
+ break;
+ case 37:
+ cd = iconv_open("UTF-8", "ko_KR-iso2022-7");
+ break;
+ case 104:
+ cd = iconv_open("UTF-8", "iso2022");
+ break;
+ case 1000:
+ cd = iconv_open("UTF-8", "UCS-2");
+ break;
+ case 1001:
+ cd = iconv_open("UTF-8", "UCS-4");
+ break;
+ default:
+ /*
+ * charset not set, or reserved, or not supported, so
+ * just copy it and hope for the best.
+ */
+ converted = outlen < inlen ? outlen : inlen;
+ memcpy(outbuf, inbuf, converted);
+ outbuf[converted] = 0;
+ return (1);
+ }
+
+ if (cd == (iconv_t)-1) {
+ return (0);
+ }
+
+ if ((converted = iconv(cd, &inbuf, &inlen, &outbuf, &outlen))
+ == (size_t)-1) {
+ return (0);
+ }
+
+ outbuf[converted] = 0;
+ iconv_close(cd);
+
+ return (1);
+}
+
+static int slp_field(char *tag, int type) {
+ int length;
+
+ get_short();
+ if (netval < 0) {
+ return (-1);
+ }
+ length = netval;
+
+ /* special case for NA field in SrvTypeRqst */
+ if (type == FIELD_TYPENA && length == 0xffff) {
+ sprintf(get_line(0, 0), "%s: length = -1: Use all NAs", tag);
+ return (0);
+ }
+
+ sprintf(get_line(0, 0), "%s: length = %d", tag, length);
+ if (length > msglength) {
+ /* framing error: message is not long enough to contain data */
+ sprintf(get_line(0, 0),
+ " [Framing error: remaining pkt length = %u]",
+ msglength);
+ return (-1);
+ }
+
+ if (length > 0) {
+ char *buf = malloc(length + 1);
+ if (buf != NULL) {
+ if (v1_charset) {
+ if (!make_utf8(buf, length, p, length)) {
+ strcpy(buf, "[Invalid Character Encoding]");
+ }
+ } else {
+ memcpy(buf, p, length);
+ buf[length] = '\0'; /* ensure null-terminated */
+ }
+
+ switch (type) {
+ case FIELD_PREVRESP:
+ slp_prevresp(buf);
+ break;
+
+ default:
+ sprintf(get_line(0, 0), " \"%s\"", buf);
+ break;
+ }
+ free(buf);
+ }
+
+ p += length;
+ msglength -= length;
+ }
+
+ /* return ok */
+ return (0);
+}
+
+static int slpv2_url(int cnt) {
+ time_t exp;
+ int lifetime, length, n;
+
+ /* reserved */
+ get_byte();
+ if (netval < 0)
+ return (-1);
+
+ /* lifetime */
+ get_short();
+ if ((lifetime = netval) < 0)
+ return (-1);
+
+ /* length */
+ get_short();
+ if ((length = netval) < 0)
+ return (-1);
+
+ /* time */
+ exp = time(0) + lifetime;
+ if (cnt == -1)
+ sprintf(get_line(0, 0),
+ "URL: length = %u, lifetime = %d (%24.24s)",
+ length, lifetime, ctime(&exp));
+ else
+ /* number the URLs to make it easier to parse them */
+ sprintf(get_line(0, 0),
+ "URL %d: length = %u, lifetime = %d (%24.24s)",
+ cnt, length, lifetime, ctime(&exp));
+
+ if (length > msglength) {
+ if (!tcp_continuation)
+ /* framing error: message is not long enough to contain data */
+ sprintf(get_line(0, 0),
+ " [Framing error: remaining pkt length = %u]",
+ msglength);
+ return (-1);
+ }
+
+ if (length > 0) {
+ char *buf = malloc(length + 1);
+ if (buf != NULL) {
+ memcpy(buf, p, length);
+ buf[length] = '\0'; /* ensure null-terminated */
+ sprintf(get_line(0, 0), " \"%s\"", buf);
+ free(buf);
+ }
+ }
+ msglength -= length;
+ p += length;
+
+ get_byte();
+ if ((n = netval) < 0)
+ return (-1);
+
+ if (n > 0) {
+ int i;
+ sprintf(get_line(0, 0), "%d Authentication Blocks", n);
+ for (i = 0; i < n; i++)
+ if ((length = slpv2_authblock(i)) < 0)
+ return (-1);
+ }
+ return (0);
+}
+
+#define DOFIELD(tag, type) \
+ if (slp_field(tag, type) < 0) \
+ return (0)
+
+#define V2_DOURL(x) \
+ if (slpv2_url(x) < 0) \
+ return (0)
+
+#define V2_DOERRCODE \
+ if (msglength < sizeof (unsigned short)) \
+ return (0); \
+ nbtohs(); \
+ errcode = netval; \
+ sprintf(get_line(0, 0), "Error code = %d, %s", \
+ errcode, slpv2_error(errcode)); \
+ p += sizeof (unsigned short); \
+ msglength -= sizeof (unsigned short); \
+ if (errcode != OK) \
+ msglength = 0; /* skip rest of message */ \
+ if (errcode != OK) \
+ return (0)
+
+#define V2_DOAUTH(cnt) \
+ if (slpv2_authblock(cnt) < 0) \
+ return (0)
+
+#define V2_DOTIMESTAMP \
+ if (msglength < 4) \
+ return (0); \
+ nbtohl(); \
+ timestamp = netval; \
+ sprintf(get_line(0, 0), "Timestamp = %u, %s", \
+ timestamp, (timestamp ? convert_ts(timestamp) : "0")); \
+ p += 4; \
+ msglength -= 4
+
+/* some V1 macros */
+#define SKIPAUTH(auth) \
+ if (auth && ((retlength = skip_v1authblock()) < 0)) \
+ return (0)
+
+#define DOERRCODE \
+ if (msglength < sizeof (unsigned short)) \
+ return (0); \
+ nbtohs(); \
+ errcode = netval; \
+ sprintf(get_line(0, 0), "Error code = %d, %s", errcode, \
+ slpv1_error(errcode)); \
+ p += sizeof (unsigned short); \
+ msglength -= sizeof (unsigned short); \
+ if (errcode != OK) \
+ return (0)
+
+#define DOURL \
+ if (slpv1_url(url_auth) < 0) \
+ return (0)
+
+#define DOAUTH(auth) \
+ if (auth && slpv1_authblock() < 0) \
+ return (0)
+
+/*
+ * TCP Continuation handling
+ * We keep track of continuations in a fixed size cache, so as to prevent
+ * memory leaks if some continuations are never finished. The continuations
+ * are indexed by their destination ports.
+ */
+static void reg_tcp_cont(char *msg, int totallen,
+ int fraglen, int dst_port) {
+ struct tcp_cont *tce = malloc(sizeof (*tce));
+
+ /* always overwrite the entry at current_tcp_cont */
+ if (tcp_cont[current_tcp_cont]) {
+ free(tcp_cont[current_tcp_cont]->msg);
+ free(tcp_cont[current_tcp_cont]);
+ }
+
+ tce->dst_port = dst_port;
+ tce->msg = malloc(totallen);
+ memcpy(tce->msg, msg, fraglen);
+ tce->totallen = totallen;
+ tce->curr_offset = fraglen;
+
+ tcp_cont[current_tcp_cont++] = tce;
+ if (current_tcp_cont == MAX_TCPCONT)
+ current_tcp_cont = 0;
+}
+
+/* returns 0 if there is a mismatch error, 1 on success */
+static int add_tcp_cont(struct tcp_cont *tce, char *msg, int fraglen) {
+ if ((fraglen + tce->curr_offset) > tce->totallen)
+ return (0);
+
+ memcpy(tce->msg + tce->curr_offset, msg, fraglen);
+ tce->curr_offset += fraglen;
+ return (1);
+}
+
+static struct tcp_cont *find_tcp_cont(int dst_port) {
+ int i;
+ for (i = current_tcp_cont; i >= 0; i--)
+ if (tcp_cont[i] && tcp_cont[i]->dst_port == dst_port)
+ return (tcp_cont[i]);
+
+ for (i = MAX_TCPCONT -1; i > current_tcp_cont; i--)
+ if (tcp_cont[i] && tcp_cont[i]->dst_port == dst_port)
+ return (tcp_cont[i]);
+
+ return (NULL);
+}
+
+static void remove_tcp_cont(int dst_port) {
+ int i;
+ for (i = current_tcp_cont; i >= 0; i--)
+ if (tcp_cont[i] && tcp_cont[i]->dst_port == dst_port) {
+ free(tcp_cont[i]->msg);
+ free(tcp_cont[i]);
+ tcp_cont[i] = NULL;
+ return;
+ }
+
+ for (i = MAX_TCPCONT -1; i > current_tcp_cont; i--)
+ if (tcp_cont[i] && tcp_cont[i]->dst_port == dst_port) {
+ free(tcp_cont[i]->msg);
+ free(tcp_cont[i]);
+ tcp_cont[i] = NULL;
+ return;
+ }
+}
+
+/*
+ * V2 interpreter
+ */
+
+static int interpret_slp_v2(int flags, struct slpv2_hdr *slp, int fraglen) {
+ extern int src_port, dst_port, curr_proto;
+ char msgbuf_real[256];
+ int totallen = 0;
+
+ msgbuf = msgbuf_real;
+
+ /*
+ * Somewhat of a hack to decode traffic from a server that does
+ * not send udp replies from its SLP src port.
+ */
+
+ if (curr_proto == IPPROTO_UDP &&
+ dst_port == 427 &&
+ src_port != 427) {
+ add_transient(src_port, (int (*)())interpret_slp);
+ }
+
+ /* parse the header */
+ if (v2_header(flags, slp, &totallen, fraglen)) {
+
+ if (slp->function <= V2_MAX_FUNCTION && slp->function > 0) {
+
+ /* Parse the message body */
+ if ((v2_functions[slp->function])(flags)) {
+
+ /* finish any remaining tasks */
+ v2_finish(slp, flags);
+
+ }
+
+ }
+
+ }
+
+ /* summary error check */
+ if (flags & F_SUM) {
+ if (retlength < 0) {
+ if (curr_proto == IPPROTO_TCP)
+ sprintf(get_sum_line(),
+ "%s [partial TCP message]", msgbuf);
+ else if (overflow)
+ sprintf(get_sum_line(), "%s [OVERFLOW]", msgbuf);
+ else
+ sprintf(get_sum_line(), "%s [CORRUPTED MESSAGE]", msgbuf);
+ }
+#ifdef VERIFYSLP
+ else if (msglength > 0)
+ sprintf(get_sum_line(), "%s +%d", msgbuf, msglength);
+#endif
+ else
+ sprintf(get_sum_line(), "%s", msgbuf);
+ } else if (flags & F_DTAIL) {
+ /* detailed error check */
+ if (msglength > 0) {
+ if (tcp_continuation) {
+ sprintf(get_line(0, 0),
+ "[TCP Continuation, %d bytes remaining]",
+ totallen - fraglen);
+ } else
+ sprintf(get_line(0, 0),
+ "[%d extra bytes at end of SLP message]", msglength);
+ }
+
+ show_trailer();
+
+ if (tcp_continuation && msglength == 0)
+ remove_tcp_cont(dst_port);
+ }
+
+ return (0);
+}
+
+static int v2_header(int flags,
+ struct slpv2_hdr *slp,
+ int *totallen,
+ int fraglen) {
+ extern int curr_proto, dst_port;
+ char *prototag = (curr_proto == IPPROTO_TCP ? "/tcp" : "");
+
+ if ((slp->flags & V2_OVERFLOW) == V2_OVERFLOW)
+ overflow = B_TRUE;
+
+ /* summary mode header parsing */
+ if (flags & F_SUM) {
+
+ /* make sure we have at least a header */
+ if (msglength < sizeof (*slp)) {
+ sprintf(get_sum_line(), "SLP V2 [Incomplete Header]");
+ return (0);
+ }
+
+ sprintf(msgbuf, "SLP V2 %s [%d%s] ",
+ slpv2_func(slp->function, B_TRUE),
+ ntohs(slp->xid), prototag);
+
+ /* skip to end of header */
+ msgend = msgbuf + strlen(msgbuf);
+ msglength -= sizeof (*slp);
+ p += sizeof (*slp);
+
+ /* skip language tag */
+ SKIPFIELD(FIELD_DEFAULT);
+ } else if (flags & F_DTAIL) {
+ char *lang;
+ int len;
+
+ /* detailed mode header parsing */
+ show_header("SLP: ", "Service Location Protocol (v2)", fraglen);
+ show_space();
+
+ if (msglength < sizeof (*slp)) {
+ sprintf(get_line(0, 0), "==> Incomplete SLP header");
+ return (0);
+ }
+
+ sprintf(get_line(0, 0), "Version = %d", slp->vers);
+ sprintf(get_line(0, 0), "Function = %d, %s",
+ slp->function, slpv2_func(slp->function, B_FALSE));
+ get_int24(&(slp->l1));
+ *totallen = netval;
+ sprintf(get_line(0, 0), "Message length = %u", *totallen);
+ /* check for TCP continuation */
+ if (curr_proto == IPPROTO_TCP &&
+ *totallen > msglength &&
+ !tcp_continuation) {
+ tcp_continuation = B_TRUE;
+ reg_tcp_cont((char *)slp, *totallen, msglength, dst_port);
+ }
+
+ if (!tcp_continuation && *totallen != msglength) {
+ sprintf(get_line(0, 0),
+ " (Stated and on-the-wire lengths differ)");
+ }
+ /* flags */
+ sprintf(get_line(0, 0), "Flags = 0x%02x", slp->flags);
+ sprintf(get_line(0, 0), " %s",
+ getflag(slp->flags, V2_OVERFLOW,
+ "overflow", "no overflow"));
+ sprintf(get_line(0, 0), " %s",
+ getflag(slp->flags, V2_FRESH,
+ "fresh registration", "no fresh registration"));
+ sprintf(get_line(0, 0), " %s",
+ getflag(slp->flags, V2_MCAST,
+ "request multicast / broadcast", "unicast"));
+ /* check reserved flags that must be zero */
+ if ((slp->flags & 7) != 0) {
+ sprintf(get_line(0, 0),
+ " .... .xxx = %d (reserved flags nonzero)",
+ slp->flags & 7);
+ }
+ /* end of flags */
+
+ /* language tag */
+ p = (char *)slp + sizeof (*slp);
+ msglength -= sizeof (*slp);
+ GETSHORT(len);
+ if (len > msglength) {
+ sprintf(get_line(0, 0),
+ "Language Tag Length = %u [CORRUPT MESSAGE]",
+ len);
+ return (0);
+ }
+
+ lang = get_line(0, 0);
+ strcpy(lang, "Language Tag = ");
+ strncat(lang, p, len);
+ sprintf(get_line(0, 0), "XID = %u", ntohs(slp->xid));
+
+ /* set msglength to remaining length of SLP message */
+ p += len;
+ msglength -= len;
+ }
+
+ return (1);
+}
+
+static int v2_finish(struct slpv2_hdr *slp, int flags) {
+ unsigned int firstop;
+
+ if (!(flags & F_DTAIL))
+ return (1);
+
+ /* check for options */
+ get_int24(&(slp->o1));
+ firstop = netval;
+
+ if (firstop) {
+ unsigned short op_id;
+ unsigned short nextop;
+ char *op_class;
+
+ for (;;) {
+ unsigned short real_oplen;
+
+ if (msglength < 4) {
+ sprintf(get_line(0, 0),
+ "Option expected but not present");
+ return (0);
+ }
+
+ nbtohs();
+ op_id = netval;
+ p += sizeof (unsigned short);
+ msglength -= sizeof (unsigned short);
+ nbtohs();
+ nextop = netval;
+ p += sizeof (unsigned short);
+ msglength -= sizeof (unsigned short);
+
+ real_oplen = nextop ? nextop : msglength;
+
+ /* known options */
+ switch (op_id) {
+ case 1:
+ sprintf(get_line(0, 0),
+ "Option: Required Attribute Missing");
+ DOFIELD("Template IDVer", FIELD_DEFAULT);
+ DOFIELD("Required Attrs", FIELD_DEFAULT);
+ break;
+ default:
+ sprintf(get_line(0, 0), "Option: Unknown");
+ p += (real_oplen - 4);
+ msglength -= (real_oplen - 4);
+ break;
+ }
+
+ if (op_id < 0x3fff)
+ op_class = "Standardized, optional";
+ else if (op_id < 0x7fff)
+ op_class = "Standardized, mandatory";
+ else if (op_id < 0x8fff)
+ op_class = "Not standardized, private";
+ else if (op_id < 0xffff)
+ op_class = "Reserved";
+ sprintf(get_line(0, 0), "Option ID = 0x%04x, %s",
+ op_id, op_class);
+ if (nextop &&
+ ((nextop - 4) > msglength) &&
+ !tcp_continuation) {
+ sprintf(get_line(0, 0),
+ "[Framing error: remaining pkt length = %u]",
+ msglength);
+ return (0);
+ }
+
+ sprintf(get_line(0, 0), "Option Length = %u", real_oplen);
+
+ if (!nextop)
+ break;
+ }
+ }
+
+ return (1);
+}
+
+#ifdef VERIFYSLP
+static int skip_v2authblock() {
+ unsigned short length, slen;
+
+ /* auth header */
+ if (msglength < 10)
+ return (-1);
+
+ /* block descriptor: 2 bytes */
+ p += sizeof (unsigned short);
+ /* length */
+ nbtohs();
+ length = netval;
+ p += sizeof (unsigned short);
+ /* timestamp */
+ p += 4;
+ /* SPI String length */
+ nbtohs();
+ slen = netval;
+ p += sizeof (unsigned short);
+
+ msglength -= 10;
+ if (slen > msglength || length > (msglength + 10))
+ return (-1);
+
+ p += slen;
+ msglength -= slen;
+
+ /* structured auth block */
+ p += (length - 10 - slen);
+ msglength -= (length - 10 - slen);
+ return (0);
+}
+#endif
+
+static char *display_bsd(unsigned short bsd) {
+ switch (bsd) {
+ case 1: return ("MD5 with RSA");
+ case 2: return ("DSA with SHA-1");
+ case 3: return ("Keyed HMAC with MD5");
+ default: return ("Unknown BSD");
+ }
+}
+
+static char *slpv2_func(int t, boolean_t s) {
+ static char buf[128];
+
+ switch (t) {
+ case V2_SRVRQST: return s? "SrvRqst" : "Service Request";
+ case V2_SRVRPLY: return s? "SrvRply" : "Service Reply";
+ case V2_SRVREG: return s? "SrvReg" : "Service Registration";
+ case V2_SRVDEREG:
+ return (s ? "SrvDereg" : "Service Deregistration");
+ case V2_SRVACK: return s? "SrvAck" : "Service Acknowledge";
+ case V2_ATTRRQST: return s? "AttrRqst" : "Attribute Request";
+ case V2_ATTRRPLY: return s? "AttrRply" : "Attribute Reply";
+ case V2_DAADVERT: return s? "DAAdvert" : "DA advertisement";
+ case V2_SRVTYPERQST:
+ return (s ? "SrvTypeRqst" : "Service Type Request");
+ case V2_SRVTYPERPLY:
+ return (s ? "SrvTypeRply" : "Service Type Reply");
+ case V2_SAADVERT: return s? "SAAdvert" : "SA advertisement";
+ }
+ sprintf(buf, "(func %d)", t);
+ return (s ? buf : "unknown function");
+}
+
+static char *slpv2_error(unsigned short code) {
+ static char buf[128];
+
+ switch (code) {
+ case OK: return "ok";
+ case LANG_NOT_SUPPORTED: return "language not supported";
+ case PROTOCOL_PARSE_ERR: return "protocol parse error";
+ case INVALID_REGISTRATION: return "invalid registration";
+ case SCOPE_NOT_SUPPORTED: return "scope not supported";
+ case AUTHENTICATION_UNKNOWN: return "authentication unknown";
+ case V2_AUTHENTICATION_ABSENT: return "authentication absent";
+ case V2_AUTHENTICATION_FAILED: return "authentication failed";
+ case V2_VER_NOT_SUPPORTED: return "version not supported";
+ case V2_INTERNAL_ERROR: return "internal error";
+ case V2_DA_BUSY_NOW: return "DA busy";
+ case V2_OPTION_NOT_UNDERSTOOD: return "option not understood";
+ case V2_INVALID_UPDATE: return "invalid update";
+ case V2_RQST_NOT_SUPPORTED: return "request not supported";
+ case INVALID_LIFETIME: return "invalid lifetime";
+ }
+ sprintf(buf, "error %d", code);
+ return (buf);
+}
+
+static char *convert_ts(unsigned int timestamp) {
+ /* timestamp is in UNIX time */
+ static char buff[128];
+
+ strcpy(buff, ctime((time_t *)&timestamp));
+ buff[strlen(buff) - 1] = '\0';
+ return (buff);
+}
+
+static int slpv2_authblock(int cnt) {
+ unsigned short bsd, length, slen;
+ char *pp, *scopes;
+ unsigned int timestamp;
+
+ if (msglength < 10) {
+ sprintf(get_line(0, 0),
+ " [no room for auth block header: remaining msg length = %u]",
+ msglength);
+ return (-1);
+ }
+
+ /* bsd */
+ nbtohs();
+ bsd = netval;
+ p += sizeof (unsigned short);
+
+ /* length */
+ nbtohs();
+ length = netval;
+ p += sizeof (unsigned short);
+
+ /* timestamp */
+ nbtohl();
+ timestamp = netval;
+ p += 4;
+
+ /* SPI String length */
+ nbtohs();
+ slen = netval;
+ p += sizeof (unsigned short);
+
+ msglength -= 10;
+ if (slen > msglength) {
+ sprintf(get_line(0, 0),
+ " [no room for auth block scopes: remaining msg length = %u]",
+ msglength);
+ return (-1);
+ }
+
+ if (length > (msglength + 10)) {
+ if (!tcp_continuation)
+ /* framing error: message is not long enough to contain data */
+ sprintf(get_line(0, 0),
+ " [Framing error: remaining pkt length = %u]",
+ msglength);
+ return (-1);
+ }
+
+ scopes = p;
+ p += slen;
+ msglength -= slen;
+
+ sprintf(get_line(0, 0),
+ "Auth block %d: timestamp = %s", cnt,
+ (timestamp) ? convert_ts(timestamp) : "0");
+
+ pp = get_line(0, 0);
+ strcpy(pp, " SPI = ");
+ strncat(pp, scopes, slen);
+
+ sprintf(get_line(0, 0),
+ " block desc = 0x%04x: %s", bsd, display_bsd(bsd));
+
+ sprintf(get_line(0, 0), " length = %u", length);
+
+ p += (length - 10 - slen);
+ msglength -= (length - 10 - slen);
+ return (0);
+}
+
+static int v2_srv_rqst(int flags) {
+ if (flags & F_SUM) {
+ SKIPFIELD(FIELD_DEFAULT); /* PR list */
+ GETFIELD; /* service type */
+ SKIPFIELD(FIELD_DEFAULT); /* scopes */
+ strcat(msgend, " [");
+ GETFIELD; /* predicate */
+ strcat(msgend, "]");
+ SKIPFIELD(FIELD_DEFAULT); /* SPI */
+ } else if (flags & F_DTAIL) {
+ DOFIELD("Previous responders", FIELD_DEFAULT);
+ DOFIELD("Service type", FIELD_DEFAULT);
+ DOFIELD("Scopes", FIELD_DEFAULT);
+ DOFIELD("Predicate string", FIELD_DEFAULT);
+ DOFIELD("Requested SPI", FIELD_DEFAULT);
+ }
+
+ return (1);
+}
+
+static int v2_srv_rply(int flags) {
+ unsigned short itemcnt, errcode;
+ int n;
+
+ if (flags & F_SUM) {
+ int i, auth_cnt;
+
+ GETSHORT(errcode);
+ if (errcode != OK) {
+ strcat(msgbuf, slpv2_error(errcode));
+ msglength = 0; /* skip rest of message */
+ return (0);
+ } else {
+ GETSHORT(itemcnt);
+ sprintf(msgend, "%d URL entries", itemcnt);
+#ifdef VERIFYSLP
+ for (n = 0; n < itemcnt; n++) {
+ SKIPBYTE; /* reserved */
+ SKIPSHORT; /* lifetime */
+ SKIPFIELD(FIELD_DEFAULT); /* URL */
+ GETBYTE(auth_cnt);
+ for (i = 0; i < auth_cnt; auth_cnt++)
+ if (skip_v2authblock() < 0)
+ return (0);
+ }
+#endif
+ }
+ } else if (flags & F_DTAIL) {
+ V2_DOERRCODE;
+ GETSHORT(itemcnt);
+ sprintf(get_line(0, 0), "URL entry count = %d", itemcnt);
+ for (n = 0; n < itemcnt; n++) {
+ V2_DOURL(n);
+ }
+ }
+
+ return (1);
+}
+
+static int v2_srv_reg(int flags) {
+ int i, auth_cnt;
+
+ if (flags & F_SUM) {
+ SKIPBYTE; /* reserved */
+ SKIPSHORT; /* lifetime */
+ GETFIELD; /* URL */
+#ifdef VERIFYSLP
+ GETBYTE(auth_cnt);
+ for (i = 0; i < auth_cnt; i++)
+ if (skip_v2authblock() < 0)
+ return (0);
+ SKIPFIELD(FIELD_DEFAULT); /* type */
+ SKIPFIELD(FIELD_DEFAULT); /* scopes */
+ SKIPFIELD(FIELD_DEFAULT); /* attrs */
+ GETBYTE(auth_cnt);
+ for (i = 0; i < auth_cnt; i++)
+ if (skip_v2authblock() < 0)
+ return (0);
+#endif
+ } if (flags & F_DTAIL) {
+ V2_DOURL(-1);
+ DOFIELD("Service type", FIELD_DEFAULT);
+ DOFIELD("Scopes", FIELD_DEFAULT);
+ DOFIELD("Attribute list", FIELD_DEFAULT);
+ /* auth */
+ GETBYTE(auth_cnt);
+ for (i = 0; i < auth_cnt; i++)
+ V2_DOAUTH(i);
+ }
+
+ return (1);
+}
+
+static int v2_srv_dereg(int flags) {
+ if (flags & F_SUM) {
+ int i, auth_cnt;
+
+ SKIPFIELD(FIELD_DEFAULT); /* scopes */
+ SKIPBYTE; /* reserved */
+ SKIPSHORT; /* lifetime */
+ GETFIELD; /* URL */
+
+#ifdef VERIFYSLP
+ GETBYTE(auth_cnt);
+ for (i = 0; i < auth_cnt; i++)
+ if (skip_v2authblock() < 0)
+ return (0);
+ SKIPFIELD(FIELD_DEFAULT); /* attrs */
+#endif
+ } else if (flags & F_DTAIL) {
+ DOFIELD("Scopes", FIELD_DEFAULT);
+ V2_DOURL(-1);
+ DOFIELD("Tag list", FIELD_DEFAULT);
+ }
+
+ return (1);
+}
+
+static int v2_srv_ack(int flags) {
+ unsigned short errcode;
+ if (flags & F_SUM) {
+ GETSHORT(errcode);
+ strcat(msgbuf, slpv2_error(errcode));
+ } else if (flags & F_DTAIL) {
+ V2_DOERRCODE;
+ }
+
+ return (1);
+}
+
+static int v2_attr_rqst(int flags) {
+ if (flags & F_SUM) {
+ SKIPFIELD(FIELD_DEFAULT); /* PR list */
+ GETFIELD; /* URL */
+ SKIPFIELD(FIELD_DEFAULT); /* scopes */
+ strcat(msgend, " [");
+ GETFIELD; /* attrs */
+ strcat(msgend, "]");
+
+#ifdef VERIFYSLP
+ SKIPFIELD(FIELD_DEFAULT); /* SPI */
+#endif
+ } else if (flags & F_DTAIL) {
+ DOFIELD("Previous responders", FIELD_DEFAULT);
+ DOFIELD("URL", FIELD_DEFAULT);
+ DOFIELD("Scopes", FIELD_DEFAULT);
+ DOFIELD("Tag list", FIELD_DEFAULT);
+ DOFIELD("Requested SPI", FIELD_DEFAULT);
+ }
+
+ return (1);
+}
+
+static int v2_attr_rply(int flags) {
+ int auth_cnt, i;
+ unsigned short errcode;
+
+ if (flags & F_SUM) {
+ GETSHORT(errcode);
+ if (errcode != OK) {
+ strcat(msgbuf, slpv2_error(errcode));
+ msglength = 0; /* skip rest of message */
+ return (0);
+ } else {
+ GETFIELD; /* attr list */
+
+#ifdef VERIFYSLP
+ GETBYTE(auth_cnt);
+ for (i = 0; i < auth_cnt; i++)
+ if (skip_v2authblock() < 0)
+ return (0);
+#endif
+ }
+ } else if (flags & F_DTAIL) {
+ V2_DOERRCODE;
+ DOFIELD("Attribute list", FIELD_DEFAULT);
+ /* auth */
+ GETBYTE(auth_cnt);
+ for (i = 0; i < auth_cnt; i++)
+ V2_DOAUTH(i);
+ }
+
+ return (1);
+}
+
+static int v2_daadvert(int flags) {
+ int auth_cnt, i;
+ unsigned short errcode;
+ unsigned int timestamp;
+
+ if (flags & F_SUM) {
+ SKIPSHORT; /* error code */
+ SKIPSHORT; SKIPSHORT; /* timestamp */
+ GETFIELD; /* URL */
+
+#ifdef VERIFYSLP
+ SKIPFIELD(FIELD_DEFAULT); /* scopes */
+ SKIPFIELD(FIELD_DEFAULT); /* attrs */
+ SKIPFIELD(FIELD_DEFAULT); /* SPIs */
+
+ GETBYTE(auth_cnt);
+ for (i = 0; i < auth_cnt; i++)
+ if (skip_v2authblock() < 0)
+ return (0);
+#endif
+ } else if (flags & F_DTAIL) {
+ V2_DOERRCODE;
+ V2_DOTIMESTAMP;
+ DOFIELD("URL", FIELD_DEFAULT);
+ DOFIELD("Scope list", FIELD_DEFAULT);
+ DOFIELD("Attribute list", FIELD_DEFAULT);
+ DOFIELD("Configured SPIs", FIELD_DEFAULT);
+ /* auth */
+ GETBYTE(auth_cnt);
+ for (i = 0; i < auth_cnt; i++)
+ V2_DOAUTH(i);
+ }
+
+ return (1);
+}
+
+static int v2_srv_type_rqst(int flags) {
+ if (flags & F_SUM) {
+ SKIPFIELD(FIELD_DEFAULT); /* prev responders */
+ SKIPFIELD(FIELD_TYPENA); /* naming authority */
+ GETFIELD; /* scope */
+ } else if (flags & F_DTAIL) {
+ DOFIELD("Previous responders", FIELD_DEFAULT);
+ DOFIELD("Naming authority", FIELD_TYPENA);
+ DOFIELD("Scopes", FIELD_DEFAULT);
+ }
+
+ return (1);
+}
+
+static int v2_srv_type_rply(int flags) {
+ unsigned short errcode;
+
+ if (flags & F_SUM) {
+ GETSHORT(errcode);
+ if (errcode != OK)
+ strcat(msgbuf, slpv2_error(errcode));
+ else
+ GETFIELD;
+ } else if (flags & F_DTAIL) {
+ V2_DOERRCODE;
+ DOFIELD("Service types", FIELD_DEFAULT);
+ }
+
+ return (1);
+}
+
+static int v2_saadvert(int flags) {
+ int auth_cnt, i;
+
+ if (flags & F_SUM) {
+ GETFIELD; /* URL */
+
+#ifdef VERIFYSLP
+ SKIPFIELD(FIELD_DEFAULT); /* scopes */
+ SKIPFIELD(FIELD_DEFAULT); /* attrs */
+
+ GETBYTE(auth_cnt);
+ for (i = 0; i < auth_cnt; i++)
+ if (skip_v2authblock() < 0)
+ return (0);
+#endif
+ } else if (flags & F_DTAIL) {
+ DOFIELD("URL", FIELD_DEFAULT);
+ DOFIELD("Scopes", FIELD_DEFAULT);
+ DOFIELD("Attribute list", FIELD_DEFAULT);
+ /* auth */
+ GETBYTE(auth_cnt);
+ for (i = 0; i < auth_cnt; i++)
+ V2_DOAUTH(i);
+ }
+
+ return (1);
+}
+
+/*
+ * V1 Interpreter
+ */
+
+static int interpret_slp_v1(int flags, struct slpv1_hdr *slp, int fraglen) {
+ char msgbuf_real[256];
+ extern int src_port, dst_port, curr_proto;
+ boolean_t overflow = B_FALSE;
+
+ msgbuf = msgbuf_real;
+
+ if (msglength >= sizeof (*slp)) {
+ if ((slp->flags & V1_URL_AUTH) == V1_URL_AUTH)
+ url_auth = B_TRUE;
+ if ((slp->flags & V1_ATTR_AUTH) == V1_ATTR_AUTH)
+ attr_auth = B_TRUE;
+ if ((slp->flags & V1_FRESH_REG) == V1_FRESH_REG)
+ fresh = B_TRUE;
+ if ((slp->flags & V1_OVERFLOW) == V1_OVERFLOW)
+ overflow = B_TRUE;
+ }
+
+ /*
+ * Somewhat of a hack to decode traffic from a server that does
+ * not send udp replies from its SLP src port.
+ */
+ if (curr_proto == IPPROTO_UDP &&
+ dst_port == 427 &&
+ src_port != 427)
+ add_transient(src_port, (int (*)())interpret_slp);
+
+ /* parse the header */
+ if (v1_header(flags, slp, fraglen)) {
+
+ if (slp->function <= V1_MAX_FUNCTION && slp->function > 0) {
+
+ /* Parse the message body */
+ (v1_functions[slp->function])(flags);
+
+ }
+
+ }
+
+ /* summary error check */
+ if (flags & F_SUM) {
+ if (retlength < 0) {
+ if (curr_proto == IPPROTO_TCP)
+ sprintf(get_sum_line(),
+ "%s [partial TCP message]",
+ msgbuf);
+ else if (overflow)
+ sprintf(get_sum_line(), "%s [OVERFLOW]", msgbuf);
+ else
+ sprintf(get_sum_line(), "%s [CORRUPTED MESSAGE]", msgbuf);
+ }
+#ifdef VERIFYSLP
+ else if (msglength > 0)
+ sprintf(get_sum_line(), "%s +%d", msgbuf, msglength);
+#endif
+ else
+ sprintf(get_sum_line(), "%s", msgbuf);
+ } else if (flags & F_DTAIL) {
+ /* detail error check */
+ if (msglength > 0) {
+ sprintf(get_line(0, 0),
+ "[%d extra bytes at end of SLP message]", msglength);
+ }
+
+ show_trailer();
+
+ }
+
+ v1_charset = 0;
+
+ return (0);
+}
+
+static int v1_header(int flags,
+ struct slpv1_hdr *slp,
+ int fraglen) {
+ extern int src_port, dst_port, curr_proto;
+ char *prototag = (curr_proto == IPPROTO_TCP? "/tcp" : "");
+
+ if (flags & F_SUM) {
+ char portflag = ' ';
+
+ if (msglength < sizeof (*slp)) {
+ sprintf(msgbuf, "SLP V1 [incomplete header]");
+ return (0);
+ }
+
+ if (slp->vers != 1) {
+ if (curr_proto == IPPROTO_TCP)
+ sprintf(msgbuf, "SLP [TCP Continuation]");
+ else
+ sprintf(msgbuf, "SLP [unknown version %d]", slp->vers);
+ return (0);
+ }
+
+ if (src_port != 427 && dst_port != 427)
+ portflag = '-';
+
+ sprintf(msgbuf, "SLP V1%c%s [%d%s] ", portflag,
+ slpv1_func(slp->function, B_TRUE),
+ ntohs(slp->xid), prototag);
+ msgend = msgbuf + strlen(msgbuf);
+ msglength -= sizeof (*slp);
+ p += sizeof (*slp);
+ } else if (flags & F_DTAIL) {
+ show_header("SLP: ", "Service Location Protocol (v1)", fraglen);
+ show_space();
+
+ if (msglength < sizeof (*slp)) {
+ sprintf(get_line(0, 0), "==> Incomplete SLP header");
+ return (0);
+ }
+
+ sprintf(get_line(0, 0), "Version = %d", slp->vers);
+ if (slp->vers != 1) {
+ if (curr_proto == IPPROTO_TCP)
+ sprintf(get_line(0, 0), "==> TCP continuation");
+ else
+ sprintf(get_line(0, 0), "==> Unexpected version number");
+ return (0);
+ }
+ sprintf(get_line(0, 0), "Function = %d, %s",
+ slp->function, slpv1_func(slp->function, B_FALSE));
+ sprintf(get_line(0, 0), "Message length = %u", ntohs(slp->length));
+
+ /* flags */
+ sprintf(get_line(0, 0), "Flags = 0x%02x", slp->flags);
+ sprintf(get_line(0, 0), " %s",
+ getflag(slp->flags, V1_OVERFLOW,
+ "overflow", "no overflow"));
+ sprintf(get_line(0, 0), " %s",
+ getflag(slp->flags, V1_MONOLINGUAL,
+ "monolingual", "not monolingual"));
+ sprintf(get_line(0, 0), " %s",
+ getflag(slp->flags, V1_URL_AUTH,
+ "url authentication", "no url authentication"));
+ sprintf(get_line(0, 0), " %s",
+ getflag(slp->flags, V1_ATTR_AUTH,
+ "attribute authentication", "no attribute authentication"));
+ sprintf(get_line(0, 0), " %s",
+ getflag(slp->flags, V1_FRESH_REG,
+ "fresh registration", "no fresh registration"));
+ /* check reserved flags that must be zero */
+ if ((slp->flags & 7) != 0) {
+ sprintf(get_line(0, 0),
+ " .... .xxx = %d (reserved flags nonzero)",
+ slp->flags & 7);
+ }
+ /* end of flags */
+
+ sprintf(get_line(0, 0), "Dialect = %u", slp->dialect);
+ sprintf(get_line(0, 0), "Language = 0x%02x%02x, %c%c",
+ slp->language[0], slp->language[1],
+ slp->language[0], slp->language[1]);
+ v1_charset = ntohs(slp->charset);
+ sprintf(get_line(0, 0), "Character encoding = %u, %s",
+ v1_charset,
+ slpv1_charset(v1_charset));
+ sprintf(get_line(0, 0), "XID = %u", ntohs(slp->xid));
+
+ /* set msglength to remaining length of SLP message */
+ msglength -= sizeof (*slp);
+ p += sizeof (*slp);
+ }
+
+ return (1);
+}
+
+static char *slpv1_func(int t, boolean_t s) {
+ static char buf[128];
+ switch (t) {
+ case V1_SRVREQ: return s? "SrvRqst" : "Service Request";
+ case V1_SRVRPLY: return s? "SrvRply" : "Service Reply";
+ case V1_SRVREG: return s? "SrvReg" : "Service Registration";
+ case V1_SRVDEREG: return s?
+ "SrvDereg" : "Service Deregistration";
+ case V1_SRVACK: return s? "SrvAck" : "Service Acknowledge";
+ case V1_ATTRRQST: return s? "AttrRqst" : "Attribute Request";
+ case V1_ATTRRPLY: return s? "AttrRply" : "Attribute Reply";
+ case V1_DAADVERT: return s? "DAAdvert" : "DA advertisement";
+ case V1_SRVTYPERQST:return s? "SrvTypeRqst" : "Service Type Request";
+ case V1_SRVTYPERPLY:return s? "SrvTypeRply" : "Service Type Reply";
+ }
+ sprintf(buf, "(func %d)", t);
+ return (s ? buf : "unknown function");
+}
+
+static char *slpv1_error(unsigned short code) {
+ static char buf[128];
+
+ switch (code) {
+ case OK: return "ok";
+ case LANG_NOT_SUPPORTED: return "language not supported";
+ case PROTOCOL_PARSE_ERR: return "protocol parse error";
+ case INVALID_REGISTRATION: return "invalid registration";
+ case SCOPE_NOT_SUPPORTED: return "scope not supported";
+ case CHARSET_NOT_UNDERSTOOD:return "character set not understood";
+ case AUTHENTICATION_INVALID:return "invalid authentication";
+ case NOT_SUPPORTED_YET: return "not yet supported";
+ case REQUEST_TIMED_OUT: return "request timed out";
+ case COULD_NOT_INIT_NET_RESOURCES:
+ return ("could not initialize net resources");
+ case COULD_NOT_ALLOCATE_MEMORY:
+ return ("could not allocate memory");
+ case PARAMETER_BAD: return "bad parameter";
+ case INTERNAL_NET_ERROR: return "internal network error";
+ case INTERNAL_SYSTEM_ERROR: return "internal system error";
+ }
+ sprintf(buf, "error %d", code);
+ return (buf);
+}
+
+/*
+ * Character set info from
+ * www.isi.edu/in-notes/iana/assignments/character-sets
+ *
+ * Assigned MIB enum Numbers
+ * -------------------------
+ * 0 Reserved
+ * 1 Reserved
+ * 3-106 Set By Standards Organizations
+ * 1000-1010 Unicode / 10646
+ * 2000-2087 Vendor
+ * 2250-2258 Vendor
+ *
+ * MIBenum: 3
+ * Alias: US-ASCII (preferred MIME name)
+ * Source: ECMA registry [RFC1345]
+ *
+ * MIBenum: 106
+ * Name: UTF-8
+ * Source: RFC 2044
+ */
+
+static char *slpv1_charset(unsigned short code) {
+ if (code <= 1)
+ return ("Reserved");
+ if (code == 3)
+ return ("US-ASCII");
+ if (code == 4)
+ return ("latin1");
+ if (code == 106)
+ return ("UTF-8");
+ if (code >= 3 && code <= 106)
+ return ("set by standards organization");
+ if (code >= 1000 && code <= 1010)
+ return ("Unicode variant");
+ if ((code >= 2000 && code <= 2087) ||
+ (code >= 2250 && code <= 2258))
+ return ("Vendor assigned");
+
+ return ("unknown");
+}
+
+#ifdef VERIFYSLP
+static int skip_v1authblock() {
+ unsigned short length;
+
+ /* auth header: 12 bytes total */
+ if (msglength < 12)
+ return (-1);
+
+ /* timestamp: 8 bytes */
+ p += 8; /* timestamp: 8 bytes */
+ p += sizeof (short); /* block descriptor: 2 bytes */
+ nbtohs();
+ length = netval;
+ p += sizeof (short);
+ msglength -= 12;
+
+ if (length > msglength) {
+ /* framing error: message is not long enough to contain data */
+ return (-1);
+ }
+
+ p += length;
+ msglength -= length;
+ return (0);
+}
+#endif
+
+static int slpv1_authblock() {
+ unsigned short bsd, length;
+ char msgbuf[128];
+ int n;
+
+ if (msglength < 12) {
+ sprintf(get_line(0, 0),
+ " [no room for auth block: remaining msg length = %u]",
+ msglength);
+ return (-1);
+ }
+
+ /* timestamp: 8 bytes */
+ *msgbuf = '\0';
+ for (n = 0; n < 8; n++, p += 1) {
+ char tmp[16];
+ sprintf(tmp, "%02x", (unsigned char)(*p));
+ strcat(msgbuf, tmp);
+ }
+
+ nbtohs();
+ bsd = netval;
+ p += sizeof (short);
+ nbtohs();
+ length = netval;
+ p += sizeof (short);
+ msglength -= 12;
+
+ sprintf(get_line(0, 0),
+ " Auth block: timestamp = %s",
+ msgbuf);
+ sprintf(get_line(0, 0),
+ " block desc = 0x%04x, length = %u",
+ bsd, length);
+ if (length > msglength) {
+ /* framing error: message is not long enough to contain data */
+ sprintf(get_line(0, 0),
+ " [Framing error: remaining pkt length = %u]", msglength);
+ return (-1);
+ }
+
+ p += length;
+ msglength -= length;
+ return (0);
+}
+
+static int slpv1_url(boolean_t auth_present) {
+ time_t exp;
+ int lifetime, length;
+
+ get_short();
+ if ((lifetime = netval) < 0)
+ return (-1);
+ get_short();
+ if ((length = netval) < 0)
+ return (-1);
+
+ exp = time(0) + lifetime;
+ sprintf(get_line(0, 0), "URL: length = %u, lifetime = %d (%24.24s)",
+ length, lifetime, ctime(&exp));
+ if (length > msglength) {
+ /* framing error: message is not long enough to contain data */
+ sprintf(get_line(0, 0),
+ " [Framing error: remaining pkt length = %u]", msglength);
+ return (-1);
+ }
+
+ if (length > 0) {
+ char *buf = malloc(length + 1);
+ if (buf != NULL) {
+ if (!make_utf8(buf, length, p, length)) {
+ strcpy(buf, "[Invalid Character Encoding]");
+ }
+ sprintf(get_line(0, 0), " \"%s\"", buf);
+ free(buf);
+ }
+ }
+ msglength -= length;
+ p += length;
+
+ if (auth_present)
+ return (slpv1_authblock());
+
+ return (0);
+}
+
+static int v1_srv_rqst(int flags) {
+ if (flags & F_SUM) {
+ SKIPFIELD(FIELD_PREVRESP); /* prev responders */
+ GETFIELD; /* predicate */
+ } else if (flags & F_DTAIL) {
+ DOFIELD("Previous responders", FIELD_PREVRESP);
+ DOFIELD("predicate string", FIELD_DEFAULT);
+ }
+
+ return (1);
+}
+
+static int v1_srv_rply(int flags) {
+ unsigned short errcode, itemcnt;
+ int n;
+
+ if (flags & F_SUM) {
+ GETSHORT(errcode);
+ if (errcode != OK) {
+ strcat(msgbuf, slpv1_error(errcode));
+ } else {
+ GETSHORT(itemcnt);
+ sprintf(msgend, "%d URL entries", itemcnt);
+#ifdef VERIFYSLP
+ for (n = 0; n < itemcnt; n++) {
+ SKIPSHORT; /* lifetime */
+ SKIPFIELD(FIELD_DEFAULT); /* URL */
+ SKIPAUTH(url_auth); /* URL auth */
+ }
+#endif
+ }
+ } else if (flags & F_DTAIL) {
+ DOERRCODE;
+ GETSHORT(itemcnt);
+ sprintf(get_line(0, 0), "URL entry count = %d", itemcnt);
+ for (n = 0; n < itemcnt; n++) {
+ DOURL;
+ }
+ }
+
+ return (1);
+}
+
+static int v1_srv_reg(int flags) {
+ if (flags & F_SUM) {
+ SKIPSHORT; /* lifetime */
+ GETFIELD; /* URL */
+#ifdef VERIFYSLP
+ SKIPAUTH(url_auth); /* URL auth */
+ SKIPFIELD(FIELD_DEFAULT); /* attribute list */
+ SKIPAUTH(attr_auth); /* attr auth */
+#endif
+ } else if (flags & F_DTAIL) {
+ DOURL;
+ DOFIELD("Attribute list", FIELD_DEFAULT);
+ DOAUTH(attr_auth);
+ }
+
+ return (1);
+}
+
+static int v1_srv_ack(int flags) {
+ unsigned short errcode;
+
+ if (flags & F_SUM) {
+ GETSHORT(errcode);
+ strcat(msgbuf, slpv1_error(errcode));
+ if (errcode == OK && fresh) {
+ strcat(msgbuf, " [Fresh]");
+ }
+ } else if (flags & F_DTAIL) {
+ DOERRCODE;
+ }
+
+ return (1);
+}
+
+static int v1_srv_dereg(int flags) {
+ if (flags & F_SUM) {
+ GETFIELD; /* URL */
+#ifdef VERIFYSLP
+ SKIPAUTH(url_auth);
+ SKIPFIELD(FIELD_DEFAULT); /* tag spec */
+#endif
+ } else if (flags & F_DTAIL) {
+ DOFIELD("URL", FIELD_DEFAULT);
+ DOAUTH(url_auth);
+ DOFIELD("Tag spec", FIELD_DEFAULT);
+ }
+
+ return (1);
+}
+
+static int v1_attr_rqst(int flags) {
+ if (flags & F_SUM) {
+ SKIPFIELD(FIELD_PREVRESP); /* prev responders */
+ GETFIELD; /* URL */
+#ifdef VERIFYSLP
+ SKIPFIELD(FIELD_DEFAULT); /* scope */
+ SKIPFIELD(FIELD_DEFAULT); /* select list */
+#endif
+ } else if (flags & F_DTAIL) {
+ DOFIELD("Previous responders", FIELD_PREVRESP);
+ DOFIELD("URL", FIELD_DEFAULT);
+ DOFIELD("Scope", FIELD_DEFAULT);
+ DOFIELD("Select list", FIELD_DEFAULT);
+ }
+
+ return (1);
+}
+
+static int v1_attr_rply(int flags) {
+ unsigned short errcode;
+
+ if (flags & F_SUM) {
+ GETSHORT(errcode);
+ if (errcode != OK) {
+ strcat(msgbuf, slpv1_error(errcode));
+ } else {
+ GETFIELD; /* attr list */
+#ifdef VERIFYSLP
+ SKIPAUTH(attr_auth);
+#endif
+ }
+ } else if (flags & F_DTAIL) {
+ DOERRCODE;
+ DOFIELD("Attribute list", FIELD_DEFAULT);
+ DOAUTH(attr_auth);
+ }
+
+ return (1);
+}
+
+static int v1_daadvert(int flags) {
+ unsigned short errcode;
+
+ if (flags & F_SUM) {
+ GETSHORT(errcode);
+ if (errcode != OK) {
+ strcat(msgbuf, slpv1_error(errcode));
+ } else {
+ GETFIELD; /* URL */
+#ifdef VERIFYSLP
+ SKIPFIELD(FIELD_DEFAULT); /* scope list */
+#endif
+ }
+ } else if (flags & F_DTAIL) {
+ DOERRCODE;
+ DOFIELD("URL", FIELD_DEFAULT);
+ DOFIELD("Scope list", FIELD_DEFAULT);
+ }
+
+ return (1);
+}
+
+static int v1_srv_type_rqst(int flags) {
+ if (flags & F_SUM) {
+ SKIPFIELD(FIELD_PREVRESP); /* prev responders */
+ SKIPFIELD(FIELD_TYPENA); /* naming authority */
+ GETFIELD; /* scope */
+ } else if (flags & F_DTAIL) {
+ DOFIELD("Previous responders", FIELD_PREVRESP);
+ DOFIELD("Naming authority", FIELD_TYPENA);
+ DOFIELD("Scope string", FIELD_DEFAULT);
+ }
+
+ return (1);
+}
+
+static int v1_srv_type_rply(int flags) {
+ unsigned short errcode, itemcnt;
+ int n;
+
+ if (flags & F_SUM) {
+ GETSHORT(errcode);
+ if (errcode != OK) {
+ strcat(msgbuf, slpv1_error(errcode));
+ } else {
+ GETSHORT(itemcnt);
+ sprintf(msgend, "%d type entries", itemcnt);
+#ifdef VERIFYSLP
+ for (n = 0; n < itemcnt; n++) {
+ SKIPFIELD(FIELD_DEFAULT); /* Service type item */
+ }
+#endif
+ }
+ } else if (flags & F_DTAIL) {
+ DOERRCODE;
+ GETSHORT(itemcnt);
+ sprintf(get_line(0, 0), "Service type count = %d", itemcnt);
+ for (n = 0; n < itemcnt; n++) {
+ DOFIELD(" Service type item", FIELD_DEFAULT);
+ }
+ }
+
+ return (1);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_smb.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_smb.c
new file mode 100644
index 0000000000..789847756e
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_smb.c
@@ -0,0 +1,1672 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999-2000 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * References used throughout this code:
+ *
+ * [CIFS/1.0] : A Common Internet File System (CIFS/1.0) Protocol
+ * Internet Engineering Task Force (IETF) draft
+ * Paul J. Leach, Microsoft, Dec. 1997
+ *
+ * [X/Open-SMB] : X/Open CAE Specification;
+ * Protocols for X/Open PC Interworking: SMB, Version 2
+ * X/Open Document Number: C209
+ */
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "snoop.h"
+
+/* some macros just for compactness */
+#define GETLINE get_line(0, 0)
+#define DECARGS int flags, uchar_t *data, int len, char *extrainfo
+
+/*
+ * SMB Format (header)
+ * [X/Open-SMB, Sec. 5.1]
+ */
+struct smb {
+ uchar_t idf[4]; /* identifier, contains 0xff, 'SMB' */
+ uchar_t com; /* command code */
+ uchar_t rcls; /* error class */
+ uchar_t res;
+ uchar_t err[2]; /* error code */
+ uchar_t flags;
+ uchar_t flags2[2];
+ uchar_t re[12];
+ uchar_t tid[2];
+ uchar_t pid[2];
+ uchar_t uid[2];
+ uchar_t mid[2];
+ /*
+ * immediately after the above 32 byte header:
+ * unsigned char WordCount;
+ * unsigned short ParameterWords[ WordCount ];
+ * unsigned short ByteCount;
+ * unsigned char ParameterBytes[ ByteCount ];
+ */
+};
+
+/* smb flags */
+#define SERVER_RESPONSE 0x80
+
+static void interpret_sesssetupX(DECARGS);
+static void interpret_tconX(DECARGS);
+static void interpret_trans(DECARGS);
+static void interpret_trans2(DECARGS);
+static void interpret_negprot(DECARGS);
+static void interpret_default(DECARGS);
+
+/*
+ * Trans2 subcommand codes
+ * [X/Open-SMB, Sec. 16.1.7]
+ */
+#define TRANS2_OPEN 0x00
+#define TRANS2_FIND_FIRST 0x01
+#define TRANS2_FIND_NEXT2 0x02
+#define TRANS2_QUERY_FS_INFORMATION 0x03
+#define TRANS2_QUERY_PATH_INFORMATION 0x05
+#define TRANS2_SET_PATH_INFORMATION 0x06
+#define TRANS2_QUERY_FILE_INFORMATION 0x07
+#define TRANS2_SET_FILE_INFORMATION 0x08
+#define TRANS2_CREATE_DIRECTORY 0x0D
+
+
+struct decode {
+ char *name;
+ void (*func)(DECARGS);
+ char *callfmt;
+ char *replyfmt;
+};
+
+/*
+ * SMB command codes (function names)
+ * [X/Open-SMB, Sec. 5.2]
+ */
+static struct decode SMBtable[256] = {
+ /* 0x00 */
+ { "mkdir", 0, 0, 0 },
+ { "rmdir", 0, 0, 0 },
+ { "open", 0, 0, 0 },
+ { "create", 0, 0, 0 },
+
+ {
+ "close", 0,
+ /* [X/Open-SMB, Sec. 7.10] */
+ "WFileID\0lLastModTime\0wByteCount\0\0",
+ "wByteCount\0\0"
+ },
+
+ { "flush", 0, 0, 0 },
+ { "unlink", 0, 0, 0 },
+
+ {
+ "mv", 0,
+ /* [X/Open-SMB, Sec. 7.11] */
+ "wFileAttributes\0wByteCount\0"
+ "r\0UFileName\0r\0UNewPath\0\0",
+ "wByteCount\0\0"
+ },
+
+ {
+ "getatr", 0,
+ /* [X/Open-SMB, Sec. 8.4] */
+ "dBytecount\0r\0UFileName\0\0",
+ "wFileAttributes\0lTime\0lSize\0R\0R\0R\0"
+ "R\0R\0wByteCount\0\0"
+ },
+
+ { "setatr", 0, 0, 0 },
+
+ {
+ "read", 0,
+ /* [X/Open-SMB, Sec. 7.4] */
+ "WFileID\0wI/0 Bytes\0LFileOffset\0"
+ "WBytesLeft\0wByteCount\0\0",
+ "WDataLength\0R\0R\0R\0R\0wByteCount\0\0"
+ },
+
+ {
+ "write", 0,
+ /* [X/Open-SMB, Sec. 7.5] */
+ "WFileID\0wI/0 Bytes\0LFileOffset\0WBytesLeft\0"
+ "wByteCount\0\0",
+ "WDataLength\0wByteCount\0\0"
+ },
+
+ { "lock", 0, 0, 0 },
+ { "unlock", 0, 0, 0 },
+ { "ctemp", 0, 0, 0 },
+ { "mknew", 0, 0, 0 },
+
+ /* 0x10 */
+ {
+ "chkpth", 0,
+ /* [X/Open-SMB, Sec. 8.7] */
+ "wByteCount\0r\0UFile\0\0",
+ "wByteCount\0\0"
+ },
+
+ { "exit", 0, 0, 0 },
+ { "lseek", 0, 0, 0 },
+ { "lockread", 0, 0, 0 },
+ { "writeunlock", 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+
+ {
+ "readbraw", 0,
+ /* [X/Open-SMB, Sec. 10.1] */
+ "WFileID\0LFileOffset\0wMaxCount\0"
+ "wMinCount\0lTimeout\0R\0wByteCount\0\0", 0
+ },
+
+ { "readbmpx", 0, 0, 0 },
+ { "readbs", 0, 0, 0 },
+ { "writebraw", 0, 0, 0 },
+ { "writebmpx", 0, 0, 0 },
+ { "writebs", 0, 0, 0 },
+
+ /* 0x20 */
+ { "writec", 0, 0, 0 },
+ { "qrysrv", 0, 0, 0 },
+ { "setattrE", 0, 0, 0 },
+ { "getattrE", 0, 0, 0 },
+
+ {
+ "lockingX", 0,
+ /* [X/Open-SMB, Sec. 12.2] */
+ "wChainedCommand\0wNextOffset\0WFileID\0"
+ "wLockType\0lOpenTimeout\0"
+ "W#Unlocks\0W#Locks\0wByteCount\0\0", 0
+ },
+
+ { "trans", interpret_trans, 0, 0 },
+ { "transs", 0, 0, 0 },
+ { "ioctl", 0, 0, 0 },
+ { "ioctls", 0, 0, 0 },
+ { "copy", 0, 0, 0 },
+ { "move", 0, 0, 0 },
+ { "echo", 0, 0, 0 },
+ { "writeclose", 0, 0, 0 },
+
+ {
+ "openX", 0,
+ /* [X/Open-SMB, Sec. 12.1] */
+ "wChainedCommand\0wNextOffset\0wFlags\0"
+ "wMode\0wSearchAttributes\0wFileAttributes\0"
+ "lTime\0wOpenFunction\0lFileSize\0lOpenTimeout\0"
+ "R\0R\0wByteCount\0r\0UFileName\0\0",
+ "wChainedCommand\0wNextOffset\0WFileID\0"
+ "wAttributes\0lTime\0LSize\0wOpenMode\0"
+ "wFileType\0wDeviceState\0wActionTaken\0"
+ "lUniqueFileID\0R\0wBytecount\0\0"
+ },
+
+ { "readX", 0, 0, 0 },
+ { "writeX", 0, 0, 0 },
+
+ /* 0x30 */
+ { 0, 0, 0, 0 },
+ { "closeTD", 0, 0, 0 },
+ { "trans2", interpret_trans2, 0, 0 },
+ { "trans2s", 0, 0, 0 },
+ {
+ "findclose", 0,
+ /* [X/Open-SMB, Sec. 15.4 ] */
+ "WFileID\0wByteCount\0\0",
+ "wByteCount\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 },
+
+ /* 0x40 */
+ { 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 },
+
+ /* 0x50 */
+ { 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 },
+
+ /* 0x60 */
+ { 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 },
+
+ /* 0x70 */
+ { "tcon", 0, 0, 0 },
+ {
+ "tdis", 0,
+ /* [X/Open-SMB, Sec. 6.3] */
+ "wByteCount\0\0",
+ "wByteCount\0\0"
+ },
+ { "negprot", interpret_negprot, 0, 0 },
+ { "sesssetupX", interpret_sesssetupX, 0, 0 },
+ {
+ "uloggoffX", 0,
+ /* [X/Open-SMB, Sec. 15.5] */
+ "wChainedCommand\0wNextOffset\0\0",
+ "wChainedCommnad\0wNextOffset\0\0" },
+ { "tconX", interpret_tconX, 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 },
+
+ /* 0x80 */
+ { "dskattr", 0, 0, 0 },
+ { "search", 0, 0, 0 },
+ { "ffirst", 0, 0, 0 },
+ { "funique", 0, 0, 0 },
+ { "fclose", 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 },
+
+ /* 0x90 */
+ { 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 },
+
+ /* 0xa0 */
+ /*
+ * Command codes 0xa0 to 0xa7 are from
+ * [CIFS/1.0, Sec. 5.1]
+ */
+ { " NT_Trans", 0, 0, 0 },
+ { " NT_Trans2", 0, 0, 0 },
+ {
+ " NT_CreateX", 0,
+ /* [CIFS/1.0, Sec. 4.2.1] */
+ "wChainedCommand\0wNextOffset\0r\0"
+ "wNameLength\0lCreateFlags\0lRootDirFID\0"
+ "lDesiredAccess\0R\0R\0R\0R\0"
+ "lNTFileAttributes\0lFileShareAccess\0"
+ "R\0R\0lCreateOption\0lImpersonationLevel\0"
+ "bSecurityFlags\0wByteCount\0r\0"
+ "UFileName\0\0",
+ "wChainedCommand\0wNextOffset\0"
+ "bOplockLevel\0WFileID\0lCreateAction\0\0"
+ },
+ { 0, 0, 0, 0 },
+ {
+ " NT_Cancel", 0,
+ /* [CIFS/1.0, Sec. 4.1.8] */
+ "wByteCount\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 },
+
+ /* 0xb0 */
+ { 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 },
+
+ /* 0xc0 */
+ { "splopen", 0, 0, 0 },
+ { "splwr", 0, 0, 0 },
+ { "splclose", 0, 0, 0 },
+ { "splretq", 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 },
+
+ /* 0xd0 */
+ { "sends", 0, 0, 0 },
+ { "sendb", 0, 0, 0 },
+ { "fwdname", 0, 0, 0 },
+ { "cancelf", 0, 0, 0 },
+ { "getmac", 0, 0, 0 },
+ { "sendstrt", 0, 0, 0 },
+ { "sendend", 0, 0, 0 },
+ { "sendtxt", 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 },
+
+ /* 0xe0 */
+ { 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 },
+
+ /* 0xf0 */
+ { 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 }
+};
+
+/* Helpers to get short and int values in Intel order. */
+static ushort_t
+get2(uchar_t *p) {
+ return (p[0] + (p[1]<<8));
+}
+static uint_t
+get4(uchar_t *p) {
+ return (p[0] + (p[1]<<8) + (p[2]<<16) + (p[3]<<24));
+}
+
+/*
+ * This is called by snoop_netbios.c.
+ * This is the external entry point.
+ */
+void
+interpret_smb(int flags, uchar_t *data, int len)
+{
+ struct smb *smb;
+ char *call_reply_detail, *call_reply_sum;
+ struct decode *decoder;
+ char xtra[300];
+ char *line;
+
+ smb = (struct smb *)data;
+ decoder = &SMBtable[smb->com & 255];
+ if (smb->flags & SERVER_RESPONSE) {
+ call_reply_detail = "SERVER RESPONSE";
+ call_reply_sum = "R";
+ } else {
+ call_reply_detail = "CLIENT REQUEST";
+ call_reply_sum = "C";
+ }
+ xtra[0] = '\0';
+
+ /*
+ * SMB Header description
+ * [X/Open-SMB, Sec. 5.1]
+ */
+ if (flags & F_DTAIL) {
+ show_header("SMB: ", "SMB Header", len);
+ show_space();
+ sprintf(GETLINE, "%s", call_reply_detail);
+
+ (void) sprintf(GETLINE, "Command code = 0x%x",
+ smb->com);
+ if (decoder->name)
+ (void) sprintf(GETLINE,
+ "Command name = SMB%s", decoder->name);
+
+ show_space();
+ sprintf(GETLINE, "SMB Status:");
+
+ /* Error classes [X/Open-SMB, Sec. 5.6] */
+ switch (smb->rcls) {
+ case 0x00:
+ sprintf(GETLINE,
+ " - Error class = No error");
+ break;
+ case 0x01:
+ sprintf(GETLINE,
+ " - Error class = Operating System");
+ break;
+ case 0x02:
+ sprintf(GETLINE,
+ " - Error class = LMX server");
+ break;
+ case 0x03:
+ sprintf(GETLINE,
+ " - Error class = Hardware");
+ break;
+ case 0xff:
+ default:
+ sprintf(GETLINE,
+ " - Error class = Incorrect format.");
+ break;
+ }
+
+ if (smb->err[0] != 0x00) {
+ sprintf(GETLINE,
+ " - Error code = %x", smb->err[0]);
+ } else
+ sprintf(GETLINE, " - Error code = No error");
+
+ show_space();
+
+ sprintf(GETLINE, "Header:");
+ sprintf(GETLINE, " - Tree ID (TID) = 0x%.4x",
+ get2(smb->tid));
+ sprintf(GETLINE, " - Process ID (PID) = 0x%.4x",
+ get2(smb->pid));
+ sprintf(GETLINE, " - User ID (UID) = 0x%.4x",
+ get2(smb->uid));
+ sprintf(GETLINE, " - Multiplex ID (MID) = 0x%.4x",
+ get2(smb->mid));
+ sprintf(GETLINE, " - Flags summary = 0x%.2x",
+ smb->flags);
+ sprintf(GETLINE, " - Flags2 summary = 0x%.4x",
+ get2(smb->flags2));
+ show_space();
+ }
+
+ if (decoder->func)
+ (decoder->func)(flags, (uchar_t *)data, len, xtra);
+ else
+ interpret_default(flags, (uchar_t *)data, len, xtra);
+
+ if (flags & F_SUM) {
+ line = get_sum_line();
+ if (decoder->name)
+ sprintf(line,
+ "SMB %s Code=0x%x Name=SMB%s %sError=%x ",
+ call_reply_sum, smb->com, decoder->name, xtra,
+ smb->err[0]);
+
+ else sprintf(line, "SMB %s Code=0x%x Error=%x ",
+ call_reply_sum, smb->com, smb->err[0]);
+
+ line += strlen(line);
+ }
+
+ if (flags & F_DTAIL)
+ show_trailer();
+}
+
+static void
+output_bytes(uchar_t *data, int bytecount)
+{
+ int i;
+ char buff[80];
+ char word[10];
+
+ buff[0] = word[0] = '\0';
+ sprintf(GETLINE, "Byte values (in hex):");
+ for (i = 0; i < bytecount; i++) {
+ sprintf(word, "%.2x ", data[i]);
+ strcat(buff, word);
+ if ((i+1)%16 == 0 || i == (bytecount-1)) {
+ sprintf(GETLINE, "%s", buff);
+ strcpy(buff, "");
+ }
+ }
+}
+
+/*
+ * Based on the Unicode Standard, http://www.unicode.org/
+ * "The Unicode Standard: A Technical Introduction", June 1998
+ */
+static int
+unicode2ascii(char *outstr, int outlen, uchar_t *instr, int inlen)
+{
+ int i = 0, j = 0;
+ char c;
+
+ while (i < inlen && j < (outlen-1)) {
+ /* Show unicode chars >= 256 as '?' */
+ if (instr[i+1])
+ c = '?';
+ else
+ c = instr[i];
+ if (c == '\0')
+ break;
+ outstr[j] = c;
+ i += 2;
+ j++;
+ }
+ outstr[j] = '\0';
+ return (j);
+}
+
+/*
+ * TRANS2 information levels
+ * [X/Open-SMB, Sec. 16.1.6]
+ */
+static void
+get_info_level(char *outstr, int value)
+{
+
+ switch (value) {
+ case 1:
+ sprintf(outstr, "Standard"); break;
+ case 2:
+ sprintf(outstr, "Query EA Size"); break;
+ case 3:
+ sprintf(outstr, "Query EAS from List"); break;
+ case 0x101:
+ sprintf(outstr, "Directory Info"); break;
+ case 0x102:
+ sprintf(outstr, "Full Directory Info"); break;
+ case 0x103:
+ sprintf(outstr, "Names Info"); break;
+ case 0x104:
+ sprintf(outstr, "Both Directory Info"); break;
+ default:
+ sprintf(outstr, "Unknown"); break;
+ }
+}
+
+/*
+ * Interpret TRANS2_QUERY_PATH subcommand
+ * [X/Open-SMB, Sec. 16.7]
+ */
+/* ARGSUSED */
+static void
+output_trans2_querypath(int flags, uchar_t *data, char *xtra)
+{
+ int length;
+ char filename[256];
+
+ if (flags & F_SUM) {
+ length = sprintf(xtra, "QueryPathInfo ");
+ xtra += length;
+ data += 6;
+ (void) unicode2ascii(filename, 256, data, 512);
+ sprintf(xtra, "File=%s ", filename);
+ }
+
+ if (flags & F_DTAIL) {
+ sprintf(GETLINE, "FunctionName = QueryPathInfo");
+ sprintf(GETLINE, "InfoLevel = 0x%.4x",
+ get2(data));
+ data += 6;
+ (void) unicode2ascii(filename, 256, data, 512);
+ sprintf(GETLINE, "FileName = %s",
+ filename);
+ }
+}
+
+/*
+ * Interpret TRANS2_QUERY_FILE subcommand
+ * [X/Open-SMB, Sec. 16.9]
+ */
+/* ARGSUSED */
+static void
+output_trans2_queryfile(int flags, uchar_t *data, char *xtra)
+{
+ int length;
+
+ if (flags & F_SUM) {
+ length = sprintf(xtra, "QueryFileInfo ");
+ xtra += length;
+ sprintf(xtra, "FileID=0x%x ", get2(data));
+ }
+
+ if (flags & F_DTAIL) {
+ sprintf(GETLINE, "FunctionName = QueryFileInfo");
+ sprintf(GETLINE, "FileID = 0x%.4x",
+ get2(data));
+ data += 2;
+ sprintf(GETLINE, "InfoLevel = 0x%.4x",
+ get2(data));
+ }
+}
+
+/*
+ * Interpret TRANS2_SET_FILE subcommand
+ * [X/Open-SMB, Sec. 16.10]
+ */
+/* ARGSUSED */
+static void
+output_trans2_setfile(int flags, uchar_t *data, char *xtra)
+{
+ int length;
+
+ if (flags & F_SUM) {
+ length = sprintf(xtra, "SetFileInfo ");
+ xtra += length;
+ sprintf(xtra, "FileID=0x%x ", get2(data));
+ }
+
+ if (flags & F_DTAIL) {
+ sprintf(GETLINE, "FunctionName = SetFileInfo");
+ sprintf(GETLINE, "FileID = 0x%.4x",
+ get2(data));
+ data += 2;
+ sprintf(GETLINE, "InfoLevel = 0x%.4x",
+ get2(data));
+ }
+}
+
+/*
+ * Interpret TRANS2_FIND_FIRST subcommand
+ * [X/Open-SMB, Sec. 16.3]
+ */
+/* ARGSUSED */
+static void
+output_trans2_findfirst(int flags, uchar_t *data, char *xtra)
+{
+ int length;
+ char filename[256];
+ char infolevel[100];
+
+ if (flags & F_SUM) {
+ length = sprintf(xtra, "Findfirst ");
+ xtra += length;
+ data += 12;
+ (void) unicode2ascii(filename, 256, data, 512);
+ sprintf(xtra, "File=%s ", filename);
+ }
+
+ if (flags & F_DTAIL) {
+ sprintf(GETLINE, "FunctionName = Findfirst");
+ sprintf(GETLINE, "SearchAttributes = 0x%.4x",
+ get2(data));
+ data += 2;
+ sprintf(GETLINE, "FindCount = 0x%.4x",
+ get2(data));
+ data += 2;
+ sprintf(GETLINE, "FindFlags = 0x%.4x",
+ get2(data));
+ data += 2;
+ get_info_level(infolevel, get2(data));
+ sprintf(GETLINE, "InfoLevel = %s",
+ infolevel);
+ data += 6;
+ (void) unicode2ascii(filename, 256, data, 512);
+ sprintf(GETLINE, "FileName = %s",
+ filename);
+ }
+}
+
+
+/*
+ * Interpret TRANS2_FIND_NEXT subcommand
+ * [X/Open-SMB, Sec. 16.4]
+ */
+/* ARGSUSED */
+static void
+output_trans2_findnext(int flags, uchar_t *data, char *xtra)
+{
+ int length;
+ char filename[256];
+ char infolevel[100];
+
+ if (flags & F_SUM) {
+ length = sprintf(xtra, "Findnext ");
+ xtra += length;
+ data += 12;
+ (void) unicode2ascii(filename, 256, data, 512);
+ sprintf(xtra, "File=%s ", filename);
+ }
+
+ if (flags & F_DTAIL) {
+ sprintf(GETLINE, "FunctionName = Findnext");
+ sprintf(GETLINE, "FileID = 0x%.4x",
+ get2(data));
+ data += 2;
+ sprintf(GETLINE, "FindCount = 0x%.4x",
+ get2(data));
+ data += 2;
+ get_info_level(infolevel, get2(data));
+ sprintf(GETLINE, "InfoLevel = %s",
+ infolevel);
+ data += 2;
+ sprintf(GETLINE, "FindKey = 0x%.8x",
+ get4(data));
+ data += 4;
+ sprintf(GETLINE, "FindFlags = 0x%.4x",
+ get2(data));
+ data += 2;
+ (void) unicode2ascii(filename, 256, data, 512);
+ sprintf(GETLINE, "FileName = %s",
+ filename);
+ }
+}
+
+/*
+ * Interpret a "Negprot" SMB
+ * [X/Open-SMB, Sec. 6.1]
+ */
+/* ARGSUSED */
+static void
+interpret_negprot(int flags, uchar_t *data, int len, char *xtra)
+{
+ int length;
+ int bytecount;
+ char dialect[256];
+ struct smb *smbdata;
+ uchar_t *protodata;
+
+ smbdata = (struct smb *)data;
+ protodata = (uchar_t *)data + sizeof (struct smb);
+ protodata++; /* skip wordcount */
+
+ if (smbdata->flags & SERVER_RESPONSE) {
+ if (flags & F_SUM) {
+ sprintf(xtra, "Dialect#=%d ", protodata[0]);
+ }
+ if (flags & F_DTAIL) {
+ sprintf(GETLINE, "Protocol Index = %d",
+ protodata[0]);
+ }
+ } else {
+ /*
+ * request packet:
+ * short bytecount;
+ * struct { char fmt; char name[]; } dialects
+ */
+ bytecount = get2(protodata);
+ protodata += 2;
+ if (flags & F_SUM) {
+ while (bytecount > 1) {
+ length = sprintf(dialect, (char *)protodata+1);
+ protodata += (length+2);
+ bytecount -= (length+2);
+ }
+ sprintf(xtra, "LastDialect=%s ", dialect);
+ }
+ if (flags & F_DTAIL) {
+ sprintf(GETLINE, "ByteCount = %d", bytecount);
+ while (bytecount > 1) {
+ length = sprintf(dialect, (char *)protodata+1);
+ sprintf(GETLINE, "Dialect String = %s",
+ dialect);
+ protodata += (length+2);
+ bytecount -= (length+2);
+ }
+ }
+ }
+}
+
+/*
+ * LAN Manager remote admin function names.
+ * [X/Open-SMB, Appendix B.8]
+ */
+static const char *apinames[] = {
+ "RNetShareEnum",
+ "RNetShareGetInfo",
+ "NetShareSetInfo",
+ "NetShareAdd",
+ "NetShareDel",
+ "NetShareCheck",
+ "NetSessionEnum",
+ "NetSessionGetInfo",
+ "NetSessionDel",
+ "NetConnectionEnum",
+ "NetFileEnum",
+ "NetFileGetInfo",
+ "NetFileClose",
+ "RNetServerGetInfo",
+ "NetServerSetInfo",
+ "NetServerDiskEnum",
+ "NetServerAdminCommand",
+ "NetAuditOpen",
+ "NetAuditClear",
+ "NetErrorLogOpen",
+ "NetErrorLogClear",
+ "NetCharDevEnum",
+ "NetCharDevGetInfo",
+ "NetCharDevControl",
+ "NetCharDevQEnum",
+ "NetCharDevQGetInfo",
+ "NetCharDevQSetInfo",
+ "NetCharDevQPurge",
+ "RNetCharDevQPurgeSelf",
+ "NetMessageNameEnum",
+ "NetMessageNameGetInfo",
+ "NetMessageNameAdd",
+ "NetMessageNameDel",
+ "NetMessageNameFwd",
+ "NetMessageNameUnFwd",
+ "NetMessageBufferSend",
+ "NetMessageFileSend",
+ "NetMessageLogFileSet",
+ "NetMessageLogFileGet",
+ "NetServiceEnum",
+ "RNetServiceInstall",
+ "RNetServiceControl",
+ "RNetAccessEnum",
+ "RNetAccessGetInfo",
+ "RNetAccessSetInfo",
+ "RNetAccessAdd",
+ "RNetAccessDel",
+ "NetGroupEnum",
+ "NetGroupAdd",
+ "NetGroupDel",
+ "NetGroupAddUser",
+ "NetGroupDelUser",
+ "NetGroupGetUsers",
+ "NetUserEnum",
+ "RNetUserAdd",
+ "NetUserDel",
+ "NetUserGetInfo",
+ "RNetUserSetInfo",
+ "RNetUserPasswordSet",
+ "NetUserGetGroups",
+ "NetWkstaLogon",
+ "NetWkstaLogoff",
+ "NetWkstaSetUID",
+ "NetWkstaGetInfo",
+ "NetWkstaSetInfo",
+ "NetUseEnum",
+ "NetUseAdd",
+ "NetUseDel",
+ "NetUseGetInfo",
+ "DosPrintQEnum",
+ "DosPrintQGetInfo",
+ "DosPrintQSetInfo",
+ "DosPrintQAdd",
+ "DosPrintQDel",
+ "DosPrintQPause",
+ "DosPrintQContinue",
+ "DosPrintJobEnum",
+ "DosPrintJobGetInfo",
+ "RDosPrintJobSetInfo",
+ "DosPrintJobAdd",
+ "DosPrintJobSchedule",
+ "RDosPrintJobDel",
+ "RDosPrintJobPause",
+ "RDosPrintJobContinue",
+ "DosPrintDestEnum",
+ "DosPrintDestGetInfo",
+ "DosPrintDestControl",
+ "NetProfileSave",
+ "NetProfileLoad",
+ "NetStatisticsGet",
+ "NetStatisticsClear",
+ "NetRemoteTOD",
+ "NetBiosEnum",
+ "NetBiosGetInfo",
+ "NetServerEnum",
+ "I_NetServerEnum",
+ "NetServiceGetInfo",
+ "NetSplQmAbort",
+ "NetSplQmClose",
+ "NetSplQmEndDoc",
+ "NetSplQmOpen",
+ "NetSplQmStartDoc",
+ "NetSplQmWrite",
+ "DosPrintQPurge",
+ "NetServerEnum2"
+};
+static const int apimax = (
+ sizeof (apinames) /
+ sizeof (apinames[0]));
+
+/*
+ * Interpret a "trans" SMB
+ * [X/Open-SMB, Appendix B]
+ *
+ * This is very much like "trans2" below.
+ */
+/* ARGSUSED */
+static void
+interpret_trans(int flags, uchar_t *data, int len, char *xtra)
+{
+ struct smb *smb;
+ uchar_t *vwv; /* word parameters */
+ int wordcount;
+ uchar_t *byteparms;
+ int bytecount;
+ int parambytes;
+ int paramoffset;
+ int setupcount;
+ int subcode;
+ uchar_t *setupdata;
+ uchar_t *params;
+ int apinum;
+ int isunicode;
+ char filename[256];
+
+ smb = (struct smb *)data;
+ vwv = (uchar_t *)data + sizeof (struct smb);
+ wordcount = *vwv++;
+
+ byteparms = vwv + (2 * wordcount);
+ bytecount = get2(byteparms);
+ byteparms += 2;
+
+ /*
+ * Print the lengths before we (potentially) bail out
+ * due to lack of data (so the user knows why we did).
+ */
+ if (flags & F_DTAIL) {
+ sprintf(GETLINE, "WordCount = %d", wordcount);
+ sprintf(GETLINE, "ByteCount = %d", bytecount);
+ }
+
+ /* Get length and location of params and setup data. */
+ if (!(smb->flags & SERVER_RESPONSE)) {
+ /* CALL */
+ if (wordcount < 14)
+ return;
+ parambytes = get2(vwv + (2 * 9));
+ paramoffset = get2(vwv + (2 * 10));
+ setupcount = *(vwv + (2 * 13));
+ setupdata = vwv + (2 * 14);
+ } else {
+ /* REPLY */
+ if (wordcount < 10)
+ return;
+ parambytes = get2(vwv + (2 * 3));
+ paramoffset = get2(vwv + (2 * 4));
+ setupcount = *(vwv + (2 * 9));
+ setupdata = vwv + (2 * 10);
+ }
+ if (setupcount > 0)
+ subcode = get2(setupdata);
+ else
+ subcode = -1; /* invalid */
+
+ /* The parameters are offset from the SMB header. */
+ params = data + paramoffset;
+ if (parambytes > 0)
+ apinum = params[0];
+ else
+ apinum = -1; /* invalid */
+
+ /* Is the pathname in unicode? */
+ isunicode = smb->flags2[1] & 0x80;
+
+ if (flags & F_DTAIL && !(smb->flags & SERVER_RESPONSE)) {
+ /* This is a CALL. */
+ /* print the word parameters */
+ sprintf(GETLINE, "TotalParamBytes = %d", get2(vwv));
+ sprintf(GETLINE, "TotalDataBytes = %d", get2(vwv+2));
+ sprintf(GETLINE, "MaxParamBytes = %d", get2(vwv+4));
+ sprintf(GETLINE, "MaxDataBytes = %d", get2(vwv+6));
+ sprintf(GETLINE, "MaxSetupWords = %d", vwv[8]);
+ sprintf(GETLINE, "TransFlags = 0x%.4x", get2(vwv+10));
+ sprintf(GETLINE, "Timeout = 0x%.8x", get4(vwv+12));
+ /* skip Reserved2 */
+ sprintf(GETLINE, "ParamBytes = 0x%.4x", parambytes);
+ sprintf(GETLINE, "ParamOffset = 0x%.4x", paramoffset);
+ sprintf(GETLINE, "DataBytes = 0x%.4x", get2(vwv+22));
+ sprintf(GETLINE, "DataOffset = 0x%.4x", get2(vwv+24));
+ sprintf(GETLINE, "SetupWords = %d", setupcount);
+
+ /* That finishes the VWV, now the misc. stuff. */
+ if (subcode >= 0)
+ sprintf(GETLINE, "Setup[0] = %d", subcode);
+ if (apinum >= 0)
+ sprintf(GETLINE, "APIcode = %d", apinum);
+ if (0 <= apinum && apinum < apimax)
+ sprintf(GETLINE, "APIname = %s", apinames[apinum]);
+
+ /* Finally, print the byte parameters. */
+ if (isunicode) {
+ byteparms += 1; /* alignment padding */
+ (void) unicode2ascii(
+ filename, 256, byteparms, bytecount);
+ } else {
+ strcpy(filename, (char *)byteparms);
+ }
+ sprintf(GETLINE, "FileName = %s", filename);
+ }
+
+ if (flags & F_DTAIL && smb->flags & SERVER_RESPONSE) {
+ /* This is a REPLY. */
+ /* print the word parameters */
+ sprintf(GETLINE, "TotalParamBytes = %d", get2(vwv));
+ sprintf(GETLINE, "TotalDataBytes = %d", get2(vwv+2));
+ /* skip Reserved */
+ sprintf(GETLINE, "ParamBytes = 0x%.4x", parambytes);
+ sprintf(GETLINE, "ParamOffset = 0x%.4x", paramoffset);
+ sprintf(GETLINE, "ParamDispl. = 0x%.4x", get2(vwv+10));
+ sprintf(GETLINE, "DataBytes = 0x%.4x", get2(vwv+12));
+ sprintf(GETLINE, "DataOffset = 0x%.4x", get2(vwv+14));
+ sprintf(GETLINE, "DataDispl. = 0x%.4x", get2(vwv+16));
+ sprintf(GETLINE, "SetupWords = %d", setupcount);
+
+ output_bytes(byteparms, bytecount);
+ }
+}
+
+/*
+ * Interpret a "TconX" SMB
+ * [X/Open-SMB, Sec. 11.4]
+ */
+/* ARGSUSED */
+static void
+interpret_tconX(int flags, uchar_t *data, int len, char *xtra)
+{
+ int length;
+ int bytecount;
+ int passwordlength;
+ int wordcount;
+ char tempstring[256];
+ struct smb *smbdata;
+ uchar_t *tcondata;
+
+ smbdata = (struct smb *)data;
+ tcondata = (uchar_t *)data + sizeof (struct smb);
+ wordcount = *tcondata++;
+
+ if (flags & F_SUM && !(smbdata->flags & SERVER_RESPONSE)) {
+ tcondata += 6;
+ passwordlength = get2(tcondata);
+ tcondata = tcondata + 4 + passwordlength;
+ length = sprintf(tempstring, (char *)tcondata);
+ sprintf(xtra, "Share=%s ", tempstring);
+ }
+
+ if (flags & F_SUM && smbdata->flags & SERVER_RESPONSE) {
+ tcondata += 8;
+ length = sprintf(tempstring, (char *)tcondata);
+ sprintf(xtra, "Type=%s ", tempstring);
+ }
+
+ if (flags & F_DTAIL && !(smbdata->flags & SERVER_RESPONSE)) {
+ sprintf(GETLINE, "WordCount = %d", wordcount);
+ sprintf(GETLINE, "ChainedCommand = 0x%.2x",
+ tcondata[0]);
+ tcondata += 2;
+ sprintf(GETLINE, "NextOffset = 0x%.4x",
+ get2(tcondata));
+ tcondata += 2;
+ sprintf(GETLINE, "DisconnectFlag = 0x%.4x",
+ get2(tcondata));
+ tcondata += 2;
+ passwordlength = get2(tcondata);
+ sprintf(GETLINE, "PasswordLength = 0x%.4x",
+ passwordlength);
+ tcondata += 2;
+ bytecount = get2(tcondata);
+ sprintf(GETLINE, "ByteCount = %d", bytecount);
+ tcondata = tcondata + 2 + passwordlength;
+ length = sprintf(tempstring, (char *)tcondata);
+ tcondata += (length+1);
+ sprintf(GETLINE, "FileName = %s", tempstring);
+ length = sprintf(tempstring, (char *)tcondata);
+ tcondata += (length+1);
+ sprintf(GETLINE, "ServiceName = %s", tempstring);
+ }
+
+ if (flags & F_DTAIL && smbdata->flags & SERVER_RESPONSE) {
+ sprintf(GETLINE, "WordCount = %d", wordcount);
+ sprintf(GETLINE, "ChainedCommand = 0x%.2x",
+ tcondata[0]);
+ tcondata += 2;
+ sprintf(GETLINE, "NextOffset = 0x%.4x",
+ get2(tcondata));
+ tcondata += 2;
+ sprintf(GETLINE, "OptionalSupport = 0x%.4x",
+ get2(tcondata));
+ tcondata += 2;
+ bytecount = get2(tcondata);
+ sprintf(GETLINE, "ByteCount = %d", bytecount);
+ tcondata += 2;
+ length = sprintf(tempstring, (char *)tcondata);
+ tcondata += (length+1);
+ sprintf(GETLINE, "ServiceName = %s", tempstring);
+ length = sprintf(tempstring, (char *)tcondata);
+ tcondata += (length+1);
+ sprintf(GETLINE, "NativeFS = %s", tempstring);
+ }
+}
+
+/*
+ * Interpret a "SesssetupX" SMB
+ * [X/Open-SMB, Sec. 11.3]
+ */
+/* ARGSUSED */
+static void
+interpret_sesssetupX(int flags, uchar_t *data, int len, char *xtra)
+{
+ int length;
+ int bytecount;
+ int passwordlength;
+ int isunicode;
+ int upasswordlength;
+ int wordcount;
+ int cap;
+ char tempstring[256];
+ struct smb *smbdata;
+ uchar_t *setupdata;
+
+ smbdata = (struct smb *)data;
+ setupdata = (uchar_t *)data + sizeof (struct smb);
+ wordcount = *setupdata++;
+
+ isunicode = smbdata->flags2[1] & 0x80;
+
+ if (flags & F_SUM && !(smbdata->flags & SERVER_RESPONSE)) {
+ if (wordcount != 13)
+ return;
+ setupdata += 14;
+ passwordlength = get2(setupdata);
+ setupdata += 2;
+ upasswordlength = get2(setupdata);
+ setupdata += 6;
+ cap = get4(setupdata);
+ setupdata = setupdata + 6 + passwordlength + upasswordlength;
+ if (isunicode) {
+ setupdata += 1;
+ (void) unicode2ascii(tempstring, 256, setupdata, 256);
+ sprintf(xtra, "Username=%s ", tempstring);
+ } else {
+ length = sprintf(tempstring, (char *)setupdata);
+ sprintf(xtra, "Username=%s ", tempstring);
+ }
+ }
+
+ if (flags & F_DTAIL && !(smbdata->flags & SERVER_RESPONSE)) {
+ if (wordcount != 13)
+ return;
+ sprintf(GETLINE, "ChainedCommand = 0x%.2x",
+ setupdata[0]);
+ setupdata += 2;
+ sprintf(GETLINE, "NextOffset = 0x%.4x",
+ get2(setupdata));
+ setupdata += 2;
+ sprintf(GETLINE, "MaxBufferSize = 0x%.4x",
+ get2(setupdata));
+ setupdata += 2;
+ sprintf(GETLINE, "MaxMPXRequests = %d",
+ get2(setupdata));
+ setupdata += 2;
+ sprintf(GETLINE, "VCNumber = %d",
+ get2(setupdata));
+ setupdata += 2;
+ sprintf(GETLINE, "SessionKey = %d",
+ get4(setupdata));
+ setupdata += 4;
+ passwordlength = get2(setupdata);
+ sprintf(GETLINE, "PasswordLength = 0x%.4x",
+ passwordlength);
+ setupdata += 2;
+ upasswordlength = get2(setupdata);
+ sprintf(GETLINE, "UnicodePasswordLength = 0x%.4x",
+ upasswordlength);
+ setupdata += 6;
+ cap = get4(setupdata);
+ sprintf(GETLINE, "Capabilities = 0x%0.8x", cap);
+ setupdata += 4;
+ bytecount = get2(setupdata);
+ sprintf(GETLINE, "ByteCount = %d", bytecount);
+ setupdata = setupdata + 2 + passwordlength + upasswordlength;
+ if (isunicode) {
+ setupdata++;
+ length = 2*unicode2ascii(
+ tempstring, 256, setupdata, 256);
+ if (length == 2) {
+ sprintf(GETLINE,
+ "AccountName = %s", tempstring);
+ sprintf(GETLINE,
+ "DomainName = %s", tempstring);
+ setupdata += 3;
+ } else {
+ setupdata += length;
+ sprintf(GETLINE,
+ "AccountName = %s", tempstring);
+ length = 2*unicode2ascii(
+ tempstring, 256, setupdata, 256);
+ setupdata += length;
+ sprintf(GETLINE,
+ "DomainName = %s", tempstring);
+ }
+ length = 2*unicode2ascii(
+ tempstring, 256, setupdata, 256);
+ setupdata += (length+2);
+ sprintf(GETLINE,
+ "NativeOS = %s", tempstring);
+ length = 2*unicode2ascii(
+ tempstring, 256, setupdata, 256);
+ sprintf(GETLINE,
+ "NativeLanman = %s", tempstring);
+ } else {
+ length = sprintf(tempstring, (char *)setupdata);
+ setupdata += (length+1);
+ sprintf(GETLINE, "AccountName = %s", tempstring);
+ length = sprintf(tempstring, (char *)setupdata);
+ setupdata += (length+1);
+ sprintf(GETLINE, "DomainName = %s", tempstring);
+ length = sprintf(tempstring, (char *)setupdata);
+ setupdata += (length+1);
+ sprintf(GETLINE, "NativeOS = %s", tempstring);
+ sprintf(tempstring, (char *)setupdata);
+ sprintf(GETLINE, "NativeLanman = %s", tempstring);
+ }
+ }
+
+ if (flags & F_DTAIL && smbdata->flags & SERVER_RESPONSE) {
+ if (wordcount != 3)
+ return;
+ sprintf(GETLINE, "ChainedCommand = 0x%.2x",
+ setupdata[0]);
+ setupdata += 2;
+ sprintf(GETLINE, "NextOffset = 0x%.4x",
+ get2(setupdata));
+ setupdata += 2;
+ sprintf(GETLINE, "SetupAction = 0x%.4x",
+ get2(setupdata));
+ setupdata += 2;
+ bytecount = get2(setupdata);
+ sprintf(GETLINE, "ByteCount = %d", bytecount);
+ setupdata += 2;
+ length = sprintf(tempstring, (char *)setupdata);
+ setupdata += (length+1);
+ sprintf(GETLINE, "NativeOS = %s", tempstring);
+ length = sprintf(tempstring, (char *)setupdata);
+ setupdata += (length+1);
+ sprintf(GETLINE, "NativeLanman = %s", tempstring);
+ length = sprintf(tempstring, (char *)setupdata);
+ sprintf(GETLINE, "DomainName = %s", tempstring);
+ }
+}
+
+/*
+ * Interpret "Trans2" SMB
+ * [X/Open-SMB, Sec. 16]
+ *
+ * This is very much like "trans" above.
+ */
+/* ARGSUSED */
+static void
+interpret_trans2(int flags, uchar_t *data, int len, char *xtra)
+{
+ struct smb *smb;
+ uchar_t *vwv; /* word parameters */
+ int wordcount;
+ uchar_t *byteparms;
+ int bytecount;
+ int parambytes;
+ int paramoffset;
+ int setupcount;
+ int subcode;
+ uchar_t *setupdata;
+ uchar_t *params;
+ char *name;
+
+ smb = (struct smb *)data;
+ vwv = (uchar_t *)data + sizeof (struct smb);
+ wordcount = *vwv++;
+
+ byteparms = vwv + (2 * wordcount);
+ bytecount = get2(byteparms);
+ byteparms += 2;
+
+ /*
+ * Print the lengths before we (potentially) bail out
+ * due to lack of data (so the user knows why we did).
+ */
+ if (flags & F_DTAIL) {
+ sprintf(GETLINE, "WordCount = %d", wordcount);
+ sprintf(GETLINE, "ByteCount = %d", bytecount);
+ }
+
+ /* Get length and location of params and setup data. */
+ if (!(smb->flags & SERVER_RESPONSE)) {
+ /* CALL */
+ if (wordcount < 14)
+ return;
+ parambytes = get2(vwv + (2 * 9));
+ paramoffset = get2(vwv + (2 * 10));
+ setupcount = *(vwv + (2 * 13));
+ setupdata = vwv + (2 * 14);
+ } else {
+ /* REPLY */
+ if (wordcount < 10)
+ return;
+ parambytes = get2(vwv + (2 * 3));
+ paramoffset = get2(vwv + (2 * 4));
+ setupcount = *(vwv + (2 * 9));
+ setupdata = vwv + (2 * 10);
+ }
+ if (setupcount > 0)
+ subcode = get2(setupdata);
+ else
+ subcode = -1; /* invalid */
+
+ /* The parameters are offset from the SMB header. */
+ params = data + paramoffset;
+
+ if (flags & F_DTAIL && !(smb->flags & SERVER_RESPONSE)) {
+ /* This is a CALL. */
+ /* print the word parameters */
+ sprintf(GETLINE, "TotalParamBytes = %d", get2(vwv));
+ sprintf(GETLINE, "TotalDataBytes = %d", get2(vwv+2));
+ sprintf(GETLINE, "MaxParamBytes = %d", get2(vwv+4));
+ sprintf(GETLINE, "MaxDataBytes = %d", get2(vwv+6));
+ sprintf(GETLINE, "MaxSetupWords = %d", vwv[8]);
+ sprintf(GETLINE, "TransFlags = 0x%.4x", get2(vwv+10));
+ sprintf(GETLINE, "Timeout = 0x%.8x", get4(vwv+12));
+ /* skip Reserved2 */
+ sprintf(GETLINE, "ParamBytes = 0x%.4x", parambytes);
+ sprintf(GETLINE, "ParamOffset = 0x%.4x", paramoffset);
+ sprintf(GETLINE, "DataBytes = 0x%.4x", get2(vwv+22));
+ sprintf(GETLINE, "DataOffset = 0x%.4x", get2(vwv+24));
+ sprintf(GETLINE, "SetupWords = %d", setupcount);
+
+ /* That finishes the VWV, now the misc. stuff. */
+ sprintf(GETLINE, "FunctionCode = %d", subcode);
+ }
+
+ if (!(smb->flags & SERVER_RESPONSE)) {
+ /* This is a CALL. Do sub-function. */
+ switch (subcode) {
+ case TRANS2_OPEN:
+ name = "Open";
+ goto name_only;
+ case TRANS2_FIND_FIRST:
+ output_trans2_findfirst(flags, params, xtra);
+ break;
+ case TRANS2_FIND_NEXT2:
+ output_trans2_findnext(flags, params, xtra);
+ break;
+ case TRANS2_QUERY_FS_INFORMATION:
+ name = "QueryFSInfo";
+ goto name_only;
+ case TRANS2_QUERY_PATH_INFORMATION:
+ output_trans2_querypath(flags, params, xtra);
+ break;
+ case TRANS2_SET_PATH_INFORMATION:
+ name = "SetPathInfo";
+ goto name_only;
+ case TRANS2_QUERY_FILE_INFORMATION:
+ output_trans2_queryfile(flags, params, xtra);
+ break;
+ case TRANS2_SET_FILE_INFORMATION:
+ output_trans2_setfile(flags, params, xtra);
+ break;
+ case TRANS2_CREATE_DIRECTORY:
+ name = "CreateDir";
+ goto name_only;
+
+ default:
+ name = "Unknown";
+ /* fall through */
+ name_only:
+ if (flags & F_SUM)
+ sprintf(xtra, "%s ", name);
+ if (flags & F_DTAIL)
+ sprintf(GETLINE, "FunctionName = %s", name);
+ break;
+ }
+ }
+
+ if (flags & F_DTAIL && smb->flags & SERVER_RESPONSE) {
+ /* This is a REPLY. */
+ /* print the word parameters */
+ sprintf(GETLINE, "TotalParamBytes = %d", get2(vwv));
+ sprintf(GETLINE, "TotalDataBytes = %d", get2(vwv+2));
+ /* skip Reserved */
+ sprintf(GETLINE, "ParamBytes = 0x%.4x", parambytes);
+ sprintf(GETLINE, "ParamOffset = 0x%.4x", paramoffset);
+ sprintf(GETLINE, "ParamDispl. = 0x%.4x", get2(vwv+10));
+ sprintf(GETLINE, "DataBytes = 0x%.4x", get2(vwv+12));
+ sprintf(GETLINE, "DataOffset = 0x%.4x", get2(vwv+14));
+ sprintf(GETLINE, "DataDispl. = 0x%.4x", get2(vwv+16));
+ sprintf(GETLINE, "SetupWords = %d", setupcount);
+
+ output_bytes(byteparms, bytecount);
+ }
+}
+
+
+static void
+interpret_default(int flags, uchar_t *data, int len, char *xtra)
+{
+ int slength;
+ int i;
+ int printit;
+ int wordcount;
+ char *outstr;
+ char *prfmt;
+ char *format;
+ char valuetype;
+ char word[10];
+ char *label;
+ char tempstring[256];
+ uchar_t *comdata, *limit;
+ char buff[80];
+ struct smb *smbdata;
+ struct decode *decoder;
+
+ smbdata = (struct smb *)data;
+ comdata = (uchar_t *)data + sizeof (struct smb);
+ wordcount = *comdata++;
+ limit = data + len;
+
+ decoder = &SMBtable[smbdata->com & 255];
+
+ if (smbdata->flags & SERVER_RESPONSE)
+ format = decoder->replyfmt;
+ else
+ format = decoder->callfmt;
+
+ if (!format || strlen(format) == 0) {
+ if (wordcount == 0 || flags & F_SUM)
+ return;
+ sprintf(GETLINE, "WordCount = %d", wordcount);
+ sprintf(GETLINE, "Word values (in hex):");
+ for (i = 0; i < wordcount; i++) {
+ sprintf(word, "%.4x ", get2(comdata));
+ comdata += 2;
+ if (comdata >= limit)
+ wordcount = i+1; /* terminate */
+ strcat(buff, word);
+ if (((i+1) & 7) == 0 || i == (wordcount-1)) {
+ sprintf(GETLINE, "%s", buff);
+ strcpy(buff, "");
+ }
+ }
+ return;
+ }
+
+
+ valuetype = format[0];
+ while (valuetype != '\0') {
+ if (comdata >= limit)
+ break;
+ if ((flags & F_DTAIL) && valuetype != 'r' && valuetype != 'R')
+ outstr = GETLINE;
+ else
+ outstr = xtra + strlen(xtra);
+ label = format+1;
+ printit = (flags & F_DTAIL) || (valuetype <= 'Z');
+
+ switch (valuetype) {
+ case 'W':
+ case 'w':
+ prfmt = (flags & F_DTAIL) ? "%s = 0x%.4x" : "%s=0x%x ";
+ if (printit)
+ sprintf(outstr, prfmt, label, get2(comdata));
+ comdata += 2;
+ break;
+ case 'D':
+ case 'd':
+ prfmt = (flags & F_DTAIL) ? "%s = %d" : "%s=%d ";
+ if (printit)
+ sprintf(outstr, prfmt, label, get2(comdata));
+ comdata += 2;
+ break;
+ case 'L':
+ case 'l':
+ prfmt = (flags & F_DTAIL) ? "%s = 0x%.8x" : "%s=0x%x ";
+ if (printit)
+ sprintf(outstr, prfmt, label, get4(comdata));
+ comdata += 4;
+ break;
+ case 'B':
+ case 'b':
+ prfmt = (flags & F_DTAIL) ? "%s = 0x%.2x" : "%s=0x%x ";
+ if (printit)
+ sprintf(outstr, prfmt, label, comdata[0]);
+ comdata += 1;
+ break;
+ case 'r':
+ comdata++;
+ break;
+ case 'R':
+ comdata += 2;
+ break;
+ case 'U':
+ case 'u':
+ prfmt = (flags & F_DTAIL) ? "%s = %s" : "%s=%s ";
+ slength = unicode2ascii(tempstring, 256, comdata, 256);
+ if (printit)
+ sprintf(outstr, prfmt, label, tempstring);
+ comdata += (slength*2 + 1);
+ break;
+ case 'S':
+ case 's':
+ prfmt = (flags & F_DTAIL) ? "%s = %s" : "%s=%s ";
+ slength = sprintf(tempstring, (char *)comdata);
+ if (printit)
+ sprintf(outstr, prfmt, label, tempstring);
+ comdata += (slength+1);
+ break;
+ }
+ format += (strlen(format) + 1);
+ valuetype = format[0];
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_socks.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_socks.c
new file mode 100644
index 0000000000..59cd38e833
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_socks.c
@@ -0,0 +1,472 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1998-1999,2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <string.h>
+#include "snoop.h"
+
+static void put_method(char *cp, int method);
+static void put_socks5_addr(char *cp, const unsigned char *buf, int fraglen);
+static void put_socks4_res(char *cp, int code);
+static void put_socks5_res(char *cp, int code);
+
+int
+interpret_socks_call(flags, line, fraglen)
+ int flags;
+ char *line;
+ int fraglen;
+{
+ unsigned char *buf = (unsigned char *)line;
+ char *cp;
+ struct in_addr ipaddr;
+ int i, n;
+
+ if (flags & F_SUM) {
+ cp = get_sum_line();
+ if (fraglen >= 2) {
+ switch (buf[0]) {
+ case 4: /* SOCKS4 */
+ n = buf[1];
+ switch (n) {
+ case 1:
+ case 2:
+ if (fraglen >= 8) {
+ (void) memcpy(&ipaddr, &buf[4],
+ sizeof (ipaddr));
+ (void) sprintf(cp,
+ "SOCKS4 %s %s:%u",
+ addrtoname(AF_INET, &ipaddr),
+ (n == 1)? "CONNECT": "BIND",
+ (buf[2] << 8) | buf[3]);
+ cp += strlen(cp);
+ if (fraglen > 8) {
+ (void) sprintf(cp, " User=");
+ cp += strlen(cp);
+ for (i = 8;
+ i < 40 && i < fraglen;
+ ++i) {
+ if (buf[i] == '\0')
+ break;
+ *cp++ = buf[i];
+ }
+ if (i == 40) {
+ *cp++ = '.';
+ *cp++ = '.';
+ *cp++ = '.';
+ }
+ *cp = '\0';
+ }
+ }
+ break;
+ default:
+ (void) sprintf(cp, "SOCKS4 OP=%u", n);
+ }
+ break;
+ case 5: /* SOCKS5 */
+ n = buf[1];
+ if (2 + n == fraglen) {
+ (void) sprintf(cp,
+ "SOCKS5 CONTACT NMETHODS=%d:", n);
+ cp += strlen(cp);
+ for (i = 0; i < n && 2 + i < fraglen; ++i) {
+ put_method(cp, buf[2 + i]);
+ cp += strlen(cp);
+ }
+ } else if (fraglen >= 6 && buf[2] == 0) {
+ const char *cmd;
+
+ if (n < 1 || n > 3) {
+ (void) sprintf(cp,
+ "SOCKS (send data): %s",
+ show_string(line, fraglen, 20));
+ } else {
+ switch (n) {
+ case 1:
+ cmd = "CONNECT";
+ break;
+ case 2:
+ cmd = "BIND";
+ break;
+ case 3:
+ cmd = "ASSOCIATE_UDP";
+ break;
+ }
+ (void) sprintf(cp, "SOCKS5 %s ", cmd);
+ cp += strlen(cp);
+ put_socks5_addr(cp, &buf[3],
+ fraglen - 3);
+ }
+ } else {
+ (void) sprintf(cp, "SOCKS (send data): %s",
+ show_string(line, fraglen, 20));
+ }
+ break;
+ default:
+ (void) sprintf(cp, "SOCKS (send data): %s",
+ show_string(line, fraglen, 20));
+ }
+ } else {
+ (void) sprintf(cp, "SOCKS (send data): %s",
+ show_string(line, fraglen, 20));
+ }
+
+ } /* if (flags & F_SUM) */
+
+ if (flags & F_DTAIL) {
+ show_header("SOCKS: ", "SOCKS Header", fraglen);
+ show_space();
+ cp = get_line(0, 0);
+ if (fraglen >= 2) {
+ switch (buf[0]) {
+ case 4:
+ (void) sprintf(cp, "Version = 4");
+ n = buf[1];
+ switch (n) {
+ case 1:
+ case 2:
+ (void) sprintf(get_line(0, 0),
+ "Operation = %s",
+ (n == 1)? "CONNECT": "BIND");
+ if (fraglen >= 8) {
+ (void) memcpy(&ipaddr, &buf[4],
+ sizeof (ipaddr));
+ (void) sprintf(get_line(0, 0),
+ "Destination = %s:%u",
+ addrtoname(AF_INET,
+ &ipaddr),
+ (buf[2] << 8) | buf[3]);
+ if (fraglen > 8) {
+ cp = get_line(0, 0);
+ (void) sprintf(cp,
+ "User = ");
+ cp += strlen(cp);
+ for (i = 8;
+ i < 40; ++i) {
+ if
+ (buf[i] == '\0')
+ break;
+ *cp++ = buf[i];
+ }
+ if (i == 40) {
+ *cp++ = '.';
+ *cp++ = '.';
+ *cp++ = '.';
+ }
+ *cp = '\0';
+ }
+ }
+ break;
+ default:
+ (void) sprintf(get_line(0, 0),
+ "Operation = %u (unknown)", n);
+ }
+ break;
+ case 5: /* SOCKS5 */
+ (void) sprintf(cp, "Version = 5");
+ n = buf[1];
+ if (2 + n == fraglen) {
+ (void) sprintf(get_line(0, 0),
+ "Number of methods = %u", n);
+ for (i = 0;
+ i < n && 2 + i < fraglen; ++i) {
+ cp = get_line(0, 0);
+ (void) sprintf(cp,
+ "Method %3u =", i);
+ cp += strlen(cp);
+ put_method(cp, buf[2 + i]);
+ }
+ } else if (fraglen >= 6 && buf[2] == 0) {
+ const char *cmd;
+ if (n < 1 || n > 3) {
+ (void) sprintf(cp,
+ "SOCKS (send data): %s",
+ show_string(line,
+ fraglen, 20));
+ } else {
+ switch (n) {
+ case 1:
+ cmd = "CONNECT";
+ break;
+ case 2:
+ cmd = "BIND";
+ break;
+ case 3:
+ cmd = "ASSOCIATE_UDP";
+ break;
+ }
+ (void) sprintf(get_line(0, 0),
+ "Operation = %s ", cmd);
+ put_socks5_addr(get_line(0, 0),
+ &buf[3], fraglen - 3);
+ break;
+ }
+ } else
+ (void) sprintf(cp,
+ " SOCKS (send data): %s",
+ show_string(line, fraglen,
+ 20));
+ break;
+ default:
+ (void) sprintf(cp,
+ "SOCKS (send data): %s",
+ show_string(line, fraglen, 20));
+ }
+ show_space();
+ } else
+ (void) sprintf(cp,
+ "SOCKS (send data): %s",
+ show_string(line, fraglen, 20));
+ }
+
+out:
+ return (fraglen);
+}
+
+int
+interpret_socks_reply(flags, line, fraglen)
+ int flags;
+ char *line;
+ int fraglen;
+{
+ unsigned char *buf = (unsigned char *)line;
+ char *cp;
+ struct in_addr ipaddr;
+
+ if (flags & F_SUM) {
+ cp = get_sum_line();
+ if (fraglen >= 2) {
+ switch (buf[0]) {
+ case 0:
+ (void) sprintf(cp, "SOCKS4 ");
+ cp += strlen(cp);
+ if (fraglen >= 8) {
+ (void) memcpy(&ipaddr, &buf[4],
+ sizeof (ipaddr));
+ (void) sprintf(cp, "%s:%u ",
+ addrtoname(AF_INET, &ipaddr),
+ (buf[2] << 8) | buf[3]);
+ cp += strlen(cp);
+ }
+ /* reply version, no SOCKS version in v4 */
+ put_socks4_res(cp, buf[1]);
+ break;
+ case 5:
+ (void) sprintf(cp, "SOCKS5 method accepted:");
+ cp += strlen(cp);
+ put_method(cp, buf[1]);
+ break;
+ default:
+ (void) sprintf(cp, "SOCKS (recv data)");
+ }
+ } else
+ (void) sprintf(cp, "SOCKS (recv data)");
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("SOCKS: ", "SOCKS Header", fraglen);
+ show_space();
+ cp = get_line(0, 0);
+ if (fraglen >= 2) {
+ switch (buf[0]) {
+ case 0:
+ /* reply version, no SOCKS version in v4 */
+ (void) sprintf(cp,
+ "Reply version = 0 (SOCKS version 4)");
+ if (fraglen >= 8) {
+ (void) memcpy(&ipaddr, &buf[4],
+ sizeof (ipaddr));
+ (void) sprintf(get_line(0, 0),
+ "Destination %s:%u ",
+ addrtoname(AF_INET, &ipaddr),
+ (buf[2] << 8) | buf[3]);
+ }
+ cp = get_line(0, 0);
+ (void) sprintf(cp, "Result code = %u ", buf[1]);
+ cp += strlen(cp);
+ put_socks4_res(cp, buf[1]);
+ break;
+ case 5:
+ (void) sprintf(cp, "Reply version = 5");
+ if (fraglen == 2) {
+ cp = get_line(0, 0);
+ (void) sprintf(cp, "Method accepted =");
+ cp += strlen(cp);
+ put_method(cp, buf[1]);
+ } else if (fraglen >= 6 && buf[2] == 0x00) {
+ cp = get_line(0, 0);
+ (void) sprintf(cp, "Status = ");
+ cp += strlen(cp);
+ put_socks5_res(cp, buf[1]);
+ put_socks5_addr(get_line(0, 0),
+ &buf[3], fraglen - 3);
+ }
+ break;
+ default:
+ (void) sprintf(cp, "(recv data)");
+ }
+ } else
+ (void) sprintf(cp, "(recv data)");
+ show_space();
+ }
+
+out:
+ return (fraglen);
+}
+
+static void
+put_method(char *cp, int method)
+{
+ switch (method) {
+ case 0:
+ (void) sprintf(cp, " NOAUTH");
+ break;
+ case 1:
+ (void) sprintf(cp, " GSSAPI");
+ break;
+ case 2:
+ (void) sprintf(cp, " USERNAME/PASSWD");
+ break;
+ case 255:
+ (void) sprintf(cp, " NONE");
+ break;
+ default:
+ (void) sprintf(cp, " 0x%02x (unknown)", method);
+ }
+}
+
+static void
+put_socks5_addr(char *cp, const unsigned char *buf, int fraglen)
+{
+ struct in_addr ipaddr;
+ int i;
+
+ switch (buf[0]) {
+ case 1:
+ /* IPv4 */
+ (void) sprintf(cp, "Address = ");
+ cp += strlen(cp);
+ if (1 + 4 + 2 <= fraglen) {
+ (void) memcpy(&ipaddr, &buf[1], sizeof (ipaddr));
+ (void) sprintf(cp, "%s:%u",
+ addrtoname(AF_INET, &ipaddr),
+ (buf[5] << 8) | buf[5 + 1]);
+ } else
+ (void) strcat(cp, "(IPv4)");
+ break;
+ case 3:
+ /* domain name */
+ (void) sprintf(cp, "Domain name = ");
+ cp += strlen(cp);
+ for (i = 0; i <= buf[1] && 1 + i < fraglen; ++i)
+ *cp++ = buf[1 + i];
+ if (1 + i + 2 <= fraglen)
+ (void) sprintf(cp, ":%u",
+ (buf[1 + i] << 8) | buf[1 + i + 1]);
+ else
+ *cp = '\0';
+ break;
+ case 4:
+ /* IPv6 */
+ (void) sprintf(cp, "Address = ");
+ if (1 + 16 <= fraglen) {
+ for (i = 0; i < 16; ++i) {
+ if (i > 0)
+ *cp++ = '.';
+ (void) sprintf(cp, "%u", buf[1 + i]);
+ cp += strlen(cp);
+ }
+ if (1 + 16 + 2 <= fraglen) {
+ (void) sprintf(cp, ":%u",
+ (buf[1 + 16] << 8) | buf[1 + 16 + 1]);
+ }
+ } else
+ (void) strcat(cp, "(IPv6)");
+ break;
+ default:
+ (void) sprintf(cp, "Address type = 0x%02x (unknown)", buf[0]);
+ }
+}
+
+static void
+put_socks4_res(char *cp, int code)
+{
+ switch (code) {
+ case 90:
+ (void) sprintf(cp, "request granted");
+ break;
+ case 91:
+ (void) sprintf(cp, "request rejected or failed");
+ break;
+ case 92:
+ (void) sprintf(cp, "socksd can't connect to client's identd");
+ break;
+ case 93:
+ (void) sprintf(cp, "identity mismatch");
+ break;
+ default:
+ (void) sprintf(cp, "0x%02x (unknown)", code);
+ }
+}
+
+static void
+put_socks5_res(char *cp, int code)
+{
+ switch (code) {
+ case 0:
+ (void) strcpy(cp, "succeeded");
+ break;
+ case 1:
+ (void) strcpy(cp, "general SOCKS server failure");
+ break;
+ case 2:
+ (void) strcpy(cp, "connection not allowed by ruleset");
+ break;
+ case 3:
+ (void) strcpy(cp, "network unreachable");
+ break;
+ case 4:
+ (void) strcpy(cp, "host unreachable");
+ break;
+ case 5:
+ (void) strcpy(cp, "connection refused");
+ break;
+ case 6:
+ (void) strcpy(cp, "TTL expired");
+ break;
+ case 7:
+ (void) strcpy(cp, "command not supported");
+ break;
+ case 8:
+ (void) strcpy(cp, "address type not supported");
+ break;
+ default:
+ (void) sprintf(cp, "code 0x%02x", code);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_solarnet.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_solarnet.c
new file mode 100644
index 0000000000..9e2e74b2b6
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_solarnet.c
@@ -0,0 +1,299 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1993, 1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%W% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <setjmp.h>
+#include <limits.h>
+#include <netinet/in.h>
+#include <string.h>
+
+#include <rpc/types.h>
+#include <rpc/rpc.h>
+#include <rpc/xdr.h>
+#include <rpc/auth.h>
+#include <rpc/clnt.h>
+#include <rpc/rpc_msg.h>
+#include <fw.h>
+#include <fw_rpc.h>
+#include "snoop.h"
+
+extern char *dlc_header;
+extern jmp_buf xdr_err;
+
+static char *procnames_short[] = {
+ "Null", /* 0 */
+ "INVOKE", /* 1 */
+ "MORE", /* 2 */
+ "KILL", /* 3 */
+};
+
+static char *procnames_long[] = {
+ "Null procedure", /* 0 */
+ "Invoke operation", /* 1 */
+ "More data", /* 2 */
+ "Kill operation", /* 3 */
+};
+
+#define MAXPROC 3
+
+enum Rec_type {
+ REC_TYPE_NORM = 0,
+ REC_TYPE_EOR = 1,
+ REC_TYPE_EOF = 2
+};
+typedef enum Rec_type Rec_type;
+
+void
+interpret_solarnet_fw(
+ int flags,
+ int type,
+ int xid,
+ int vers,
+ int proc,
+ char *data,
+ int len)
+{
+ char *line;
+ char buff[CTXTLEN + 1];
+ ulong_t thresh;
+ char op[CTXTLEN + 1];
+ bool_t b;
+ Fw_err e;
+ Rec_type rt;
+ int new_row = 1, row = 0;
+
+ if (proc < 0 || proc > MAXPROC)
+ return;
+
+ if (flags & F_SUM) {
+ if (setjmp(xdr_err)) {
+ return;
+ }
+
+ line = get_sum_line();
+
+ if (type == CALL) {
+ (void) sprintf(line,
+ "SOLARNET C %s",
+ procnames_short[proc]);
+ line += strlen(line);
+
+ switch (proc) {
+ case FW_INVOKE:
+ (void) sprintf(line, " %s",
+ getxdr_string(buff, CTXTLEN));
+ line += strlen(line);
+ (void) sprintf(line, "/%s",
+ getxdr_string(buff, CTXTLEN));
+ line += strlen(line);
+ getxdr_string(buff, CTXTLEN);
+ if (strlen(buff) != 0) {
+ (void) sprintf(line, ".%s", buff);
+ line += strlen(line);
+ }
+ (void) getxdr_string(buff, CTXTLEN);
+ thresh = getxdr_u_long();
+ if (thresh == ULONG_MAX)
+ (void) sprintf(line, " (all)");
+ else
+ (void) sprintf(line, " %lu", thresh);
+ line += strlen(line);
+ (void) getxdr_context(buff, CTXTLEN);
+ break;
+ case FW_MORE:
+ (void) getxdr_context(buff, CTXTLEN);
+ sscanf(buff, "%*s %*s %s.%*s", op);
+ op[strlen(op)-1] = '\0';
+ (void) sprintf(line, " %s", op);
+ line += strlen(line);
+ thresh = getxdr_u_long();
+ if (thresh == ULONG_MAX)
+ (void) sprintf(line, " (all)");
+ else
+ (void) sprintf(line, " %lu", thresh);
+ line += strlen(line);
+ break;
+ case FW_KILL:
+ (void) getxdr_context(buff, CTXTLEN);
+ sscanf(buff, "%*s %*s %s.%*s", op);
+ op[strlen(op)-1] = '\0';
+ (void) sprintf(line, " %s", op);
+ line += strlen(line);
+ break;
+ default:
+ break;
+ }
+
+ check_retransmit(line, xid);
+ } else {
+ (void) sprintf(line, "SOLARNET R %s",
+ procnames_short[proc]);
+ line += strlen(line);
+ b = getxdr_bool();
+ if (b) {
+ e = getxdr_enum();
+ if (e == FW_ERR_FW)
+ sprintf(line, " FW");
+ else if (e == FW_ERR_OP)
+ sprintf(line, " OP");
+ else
+ sprintf(line, " NOERR");
+ line += strlen(line);
+ if (e != FW_ERR_NONE) {
+ sprintf(line, " %lu", getxdr_u_long());
+ line += strlen(line);
+ (void) getxdr_bool();
+ sprintf(line, " %s",
+ getxdr_string(buff, CTXTLEN));
+ line += strlen(line);
+ }
+ } else {
+ sprintf(line, " Success");
+ line += strlen(line);
+ }
+ b = getxdr_bool();
+ if (b) {
+ sprintf(line, " %lu rows", getxdr_u_long());
+ line += strlen(line);
+ } else {
+ sprintf(line, " (No output)");
+ line += strlen(line);
+ }
+ }
+ }
+
+ if ((flags & F_DTAIL)) {
+ show_header("SOLARNET: ", "Solarnet Administration Service",
+ len);
+ show_space();
+ if (setjmp(xdr_err)) {
+ return;
+ }
+ (void) sprintf(get_line(0, 0), "Proc = %d (%s)", proc,
+ procnames_long[proc]);
+ if (type == CALL) {
+ switch (proc) {
+ case FW_INVOKE:
+ (void) showxdr_string(CTXTLEN, "Category: %s");
+ (void) showxdr_string(CTXTLEN, "Operation: %s");
+ (void) showxdr_string(CTXTLEN, "Version: %s");
+ (void) showxdr_string(CTXTLEN, "Locale: %s");
+ (void) showxdr_u_long("Threshold: %lu rows");
+ (void) showxdr_context("Context: %s");
+ b = getxdr_bool();
+ if (!b) {
+ sprintf(get_line(0, 0),
+ "No input arguments");
+ break;
+ }
+ thresh = showxdr_u_long("Input rows = %lu");
+ (void) getxdr_bool();
+ do {
+ rt = getxdr_enum();
+ if (rt == REC_TYPE_NORM) {
+ if (new_row) {
+ sprintf(get_line(0, 0),
+ "Row %d", ++row);
+ new_row = 0;
+ }
+ (void) getxdr_string(buff,
+ CTXTLEN);
+ (void) getxdr_string(op,
+ CTXTLEN);
+ sprintf(get_line(0, 0),
+ "\t%s = %s", buff, op);
+ } else if (rt == REC_TYPE_EOR) {
+ new_row = 1;
+ }
+ } while (rt != REC_TYPE_EOF);
+ break;
+ case FW_MORE:
+ (void) showxdr_context("Context: %s");
+ (void) showxdr_u_long("Threshold: %lu rows");
+ break;
+ case FW_KILL:
+ (void) showxdr_context("Context: %s");
+ break;
+ default:
+ break;
+ }
+ } else {
+ b = getxdr_bool();
+ if (b) {
+ e = getxdr_enum();
+ if (e == FW_ERR_FW) {
+ showxdr_u_long(
+ "Framework error code %lu");
+ } else if (e == FW_ERR_OP) {
+ showxdr_u_long(
+ "Operation error code %lu");
+ } else {
+ showxdr_u_long("No error %*lu");
+ }
+ (void) getxdr_bool();
+ (void) getxdr_string(buff, CTXTLEN);
+ if (e != FW_ERR_NONE) {
+ sprintf(get_line(0, 0),
+ "Error message: %s", buff);
+ } else {
+ }
+ } else {
+ sprintf(get_line(0, 0),
+ "Operation was successful");
+ }
+ b = getxdr_bool();
+ if (b) {
+ showxdr_u_long("Output rows: %lu");
+ (void) getxdr_bool();
+ do {
+ rt = getxdr_enum();
+ if (rt == REC_TYPE_NORM) {
+ if (new_row) {
+ sprintf(get_line(0, 0),
+ "Row %d", ++row);
+ new_row = 0;
+ }
+ (void) getxdr_string(buff,
+ CTXTLEN);
+ (void) getxdr_string(op,
+ CTXTLEN);
+ sprintf(get_line(0, 0),
+ "\t%s = %s", buff, op);
+ } else if (rt == REC_TYPE_EOR) {
+ new_row = 1;
+ }
+ } while (rt != REC_TYPE_EOF);
+ } else {
+ sprintf(get_line(0, 0), "No output");
+ }
+ }
+ show_trailer();
+ }
+
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_tcp.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_tcp.c
new file mode 100644
index 0000000000..4ed56b8c52
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_tcp.c
@@ -0,0 +1,448 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1991-2001,2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/if_ether.h>
+#include <netinet/tcp.h>
+#include "snoop.h"
+
+extern char *dlc_header;
+
+#define TCPOPT_HEADER_LEN 2
+#define TCPOPT_TSTAMP_LEN 10
+#define TCPOPT_SACK_LEN 8
+
+/*
+ * Convert a network byte order 32 bit integer to a host order integer.
+ * ntohl() cannot be used because option values may not be aligned properly.
+ */
+#define GET_UINT32(opt) (((uint_t)*((uchar_t *)(opt) + 0) << 24) | \
+ ((uint_t)*((uchar_t *)(opt) + 1) << 16) | \
+ ((uint_t)*((uchar_t *)(opt) + 2) << 8) | \
+ ((uint_t)*((uchar_t *)(opt) + 3)))
+
+static void print_tcpoptions_summary(uchar_t *, int, char *);
+static void print_tcpoptions(uchar_t *, int);
+
+static const struct {
+ unsigned int tf_flag;
+ const char *tf_name;
+} tcp_flags[] = {
+ { TH_SYN, "Syn" },
+ { TH_FIN, "Fin" },
+ { TH_RST, "Rst" },
+ { TH_PUSH, "Push" },
+ { TH_ECE, "ECE" },
+ { TH_CWR, "CWR" },
+ { 0, NULL }
+};
+
+int
+interpret_tcp(int flags, struct tcphdr *tcp, int iplen, int fraglen)
+{
+ char *data;
+ int hdrlen, tcplen;
+ int sunrpc = 0;
+ char *pname;
+ char buff[32];
+ char *line, *endline;
+ unsigned int i;
+
+ hdrlen = tcp->th_off * 4;
+ data = (char *)tcp + hdrlen;
+ tcplen = iplen - hdrlen;
+ fraglen -= hdrlen;
+ if (fraglen < 0)
+ return; /* incomplete header */
+ if (fraglen > tcplen)
+ fraglen = tcplen;
+
+ if (flags & F_SUM) {
+ line = get_sum_line();
+ endline = line + MAXLINE;
+ (void) snprintf(line, endline - line, "TCP D=%d S=%d",
+ ntohs(tcp->th_dport), ntohs(tcp->th_sport));
+ line += strlen(line);
+
+ for (i = 0; tcp_flags[i].tf_name != NULL; i++) {
+ if (tcp->th_flags & tcp_flags[i].tf_flag) {
+ (void) snprintf(line, endline - line, " %s",
+ tcp_flags[i].tf_name);
+ line += strlen(line);
+ }
+ }
+
+ if (tcp->th_flags & TH_URG) {
+ (void) snprintf(line, endline - line, " Urg=%u",
+ ntohs(tcp->th_urp));
+ line += strlen(line);
+ }
+ if (tcp->th_flags & TH_ACK) {
+ (void) snprintf(line, endline - line, " Ack=%u",
+ ntohl(tcp->th_ack));
+ line += strlen(line);
+ }
+ if (ntohl(tcp->th_seq)) {
+ (void) snprintf(line, endline - line, " Seq=%u Len=%d",
+ ntohl(tcp->th_seq), tcplen);
+ line += strlen(line);
+ }
+ (void) snprintf(line, endline - line, " Win=%d",
+ ntohs(tcp->th_win));
+ print_tcpoptions_summary((uchar_t *)(tcp + 1),
+ (int)(tcp->th_off * 4 - sizeof (struct tcphdr)), line);
+ }
+
+ sunrpc = !reservedport(IPPROTO_TCP, ntohs(tcp->th_dport)) &&
+ !reservedport(IPPROTO_TCP, ntohs(tcp->th_sport)) &&
+ valid_rpc(data + 4, fraglen - 4);
+
+ if (flags & F_DTAIL) {
+
+ show_header("TCP: ", "TCP Header", tcplen);
+ show_space();
+ (void) sprintf(get_line((char *)tcp->th_sport - dlc_header, 2),
+ "Source port = %d",
+ ntohs(tcp->th_sport));
+
+ if (sunrpc) {
+ pname = "(Sun RPC)";
+ } else {
+ pname = getportname(IPPROTO_TCP, ntohs(tcp->th_dport));
+ if (pname == NULL) {
+ pname = "";
+ } else {
+ (void) sprintf(buff, "(%s)", pname);
+ pname = buff;
+ }
+ }
+ (void) sprintf(get_line((char *)tcp->th_dport - dlc_header, 2),
+ "Destination port = %d %s",
+ ntohs(tcp->th_dport), pname);
+ (void) sprintf(get_line((char *)tcp->th_seq - dlc_header, 4),
+ "Sequence number = %u",
+ ntohl(tcp->th_seq));
+ (void) sprintf(get_line((char *)tcp->th_ack - dlc_header, 4),
+ "Acknowledgement number = %u",
+ ntohl(tcp->th_ack));
+ (void) sprintf(get_line(((char *)tcp->th_ack - dlc_header) + 4, 1),
+ "Data offset = %d bytes",
+ tcp->th_off * 4);
+ (void) sprintf(get_line(((char *)tcp->th_flags - dlc_header) + 4, 1),
+ "Flags = 0x%02x",
+ tcp->th_flags);
+ (void) sprintf(get_line(((char *)tcp->th_flags - dlc_header) + 4, 1),
+ " %s",
+ getflag(tcp->th_flags, TH_CWR,
+ "ECN congestion window reduced",
+ "No ECN congestion window reduced"));
+ (void) sprintf(get_line(((char *)tcp->th_flags - dlc_header) + 4, 1),
+ " %s",
+ getflag(tcp->th_flags, TH_ECE,
+ "ECN echo", "No ECN echo"));
+ (void) sprintf(get_line(((char *)tcp->th_flags - dlc_header) + 4, 1),
+ " %s",
+ getflag(tcp->th_flags, TH_URG,
+ "Urgent pointer", "No urgent pointer"));
+ (void) sprintf(get_line(((char *)tcp->th_flags - dlc_header) + 4, 1),
+ " %s",
+ getflag(tcp->th_flags, TH_ACK,
+ "Acknowledgement", "No acknowledgement"));
+ (void) sprintf(get_line(((char *)tcp->th_flags - dlc_header) + 4, 1),
+ " %s",
+ getflag(tcp->th_flags, TH_PUSH,
+ "Push", "No push"));
+ (void) sprintf(get_line(((char *)tcp->th_flags - dlc_header) + 4, 1),
+ " %s",
+ getflag(tcp->th_flags, TH_RST,
+ "Reset", "No reset"));
+ (void) sprintf(get_line(((char *)tcp->th_flags - dlc_header) + 4, 1),
+ " %s",
+ getflag(tcp->th_flags, TH_SYN,
+ "Syn", "No Syn"));
+ (void) sprintf(get_line(((char *)tcp->th_flags - dlc_header) + 4, 1),
+ " %s",
+ getflag(tcp->th_flags, TH_FIN,
+ "Fin", "No Fin"));
+ (void) sprintf(get_line(((char *)tcp->th_win - dlc_header) + 4, 1),
+ "Window = %d",
+ ntohs(tcp->th_win));
+ /* XXX need to compute checksum and print whether correct */
+ (void) sprintf(get_line(((char *)tcp->th_sum - dlc_header) + 4, 1),
+ "Checksum = 0x%04x",
+ ntohs(tcp->th_sum));
+ (void) sprintf(get_line(((char *)tcp->th_urp - dlc_header) + 4, 1),
+ "Urgent pointer = %d",
+ ntohs(tcp->th_urp));
+
+ /* Print TCP options - if any */
+
+ print_tcpoptions((uchar_t *)(tcp + 1),
+ tcp->th_off * 4 - sizeof (struct tcphdr));
+
+ show_space();
+ }
+
+ /* go to the next protocol layer */
+
+ if (!interpret_reserved(flags, IPPROTO_TCP,
+ ntohs(tcp->th_sport),
+ ntohs(tcp->th_dport),
+ data, fraglen)) {
+ if (sunrpc && fraglen > 0)
+ interpret_rpc(flags, data, fraglen, IPPROTO_TCP);
+ }
+
+ return (tcplen);
+}
+
+static void
+print_tcpoptions(opt, optlen)
+ uchar_t *opt;
+ int optlen;
+{
+ int len;
+ char *line;
+ uchar_t *sack_opt;
+ uchar_t *end_opt;
+ int sack_len;
+
+ if (optlen <= 0) {
+ (void) sprintf(get_line((char *)&opt - dlc_header, 1),
+ "No options");
+ return;
+ }
+
+ (void) sprintf(get_line((char *)&opt - dlc_header, 1),
+ "Options: (%d bytes)", optlen);
+
+ while (optlen > 0) {
+ line = get_line((char *)&opt - dlc_header, 1);
+ len = opt[1];
+ switch (opt[0]) {
+ case TCPOPT_EOL:
+ (void) strcpy(line, " - End of option list");
+ return;
+ case TCPOPT_NOP:
+ (void) strcpy(line, " - No operation");
+ len = 1;
+ break;
+ case TCPOPT_MAXSEG:
+ (void) sprintf(line,
+ " - Maximum segment size = %d bytes",
+ (opt[2] << 8) + opt[3]);
+ break;
+ case TCPOPT_WSCALE:
+ (void) sprintf(line, " - Window scale = %d", opt[2]);
+ break;
+ case TCPOPT_TSTAMP:
+ /* Sanity check. */
+ if (optlen < TCPOPT_TSTAMP_LEN) {
+ (void) sprintf(line,
+ " - Incomplete TS option");
+ } else {
+ (void) sprintf(line,
+ " - TS Val = %u, TS Echo = %u",
+ GET_UINT32(opt + 2),
+ GET_UINT32(opt + 6));
+ }
+ break;
+ case TCPOPT_SACK_PERMITTED:
+ (void) sprintf(line, " - SACK permitted option");
+ break;
+ case TCPOPT_SACK:
+ /*
+ * Sanity check. Total length should be greater
+ * than just the option header length.
+ */
+ if (len <= TCPOPT_HEADER_LEN ||
+ opt[1] <= TCPOPT_HEADER_LEN || len < opt[1]) {
+ (void) sprintf(line,
+ " - Incomplete SACK option");
+ break;
+ }
+ sack_len = opt[1] - TCPOPT_HEADER_LEN;
+ sack_opt = opt + TCPOPT_HEADER_LEN;
+ end_opt = opt + optlen;
+
+ (void) sprintf(line, " - SACK blocks:");
+ line = get_line((char *)&opt - dlc_header, 1);
+ (void) sprintf(line, " ");
+ while (sack_len > 0) {
+ char sack_blk[MAXLINE + 1];
+
+ /*
+ * sack_len may not tell us the truth about
+ * the real length... Need to be careful
+ * not to step beyond the option buffer.
+ */
+ if (sack_opt + TCPOPT_SACK_LEN > end_opt) {
+ (void) strcat(line,
+ "...incomplete SACK block");
+ break;
+ }
+ (void) sprintf(sack_blk, "(%u-%u) ",
+ GET_UINT32(sack_opt),
+ GET_UINT32(sack_opt + 4));
+ (void) strcat(line, sack_blk);
+ sack_opt += TCPOPT_SACK_LEN;
+ sack_len -= TCPOPT_SACK_LEN;
+ }
+ break;
+ default:
+ (void) sprintf(line,
+ " - Option %d (unknown - %d bytes) %s",
+ opt[0],
+ len - 2,
+ tohex((char *)&opt[2], len - 2));
+ break;
+ }
+ if (len <= 0) {
+ (void) sprintf(line, " - Incomplete option len %d",
+ len);
+ break;
+ }
+ opt += len;
+ optlen -= len;
+ }
+}
+
+/*
+ * This function is basically the same as print_tcpoptions() except that
+ * all options are printed on the same line.
+ */
+static void
+print_tcpoptions_summary(uchar_t *opt, int optlen, char *line)
+{
+ int len;
+ uchar_t *sack_opt;
+ uchar_t *end_opt;
+ int sack_len;
+ char options[MAXLINE + 1];
+
+ if (optlen <= 0) {
+ return;
+ }
+
+ (void) strcat(line, " Options=<");
+ while (optlen > 0) {
+ len = opt[1];
+ switch (opt[0]) {
+ case TCPOPT_EOL:
+ (void) strcat(line, "eol>");
+ return;
+ case TCPOPT_NOP:
+ (void) strcat(line, "nop");
+ len = 1;
+ break;
+ case TCPOPT_MAXSEG:
+ (void) sprintf(options, "mss %d",
+ (opt[2] << 8) + opt[3]);
+ (void) strcat(line, options);
+ break;
+ case TCPOPT_WSCALE:
+ (void) sprintf(options, "wscale %d", opt[2]);
+ (void) strcat(line, options);
+ break;
+ case TCPOPT_TSTAMP:
+ /* Sanity check. */
+ if (optlen < TCPOPT_TSTAMP_LEN) {
+ (void) strcat(line, "tstamp|");
+ } else {
+ (void) sprintf(options,
+ "tstamp %u %u", GET_UINT32(opt + 2),
+ GET_UINT32(opt + 6));
+ (void) strcat(line, options);
+ }
+ break;
+ case TCPOPT_SACK_PERMITTED:
+ (void) strcat(line, "sackOK");
+ break;
+ case TCPOPT_SACK:
+ /*
+ * Sanity check. Total length should be greater
+ * than just the option header length.
+ */
+ if (len <= TCPOPT_HEADER_LEN ||
+ opt[1] <= TCPOPT_HEADER_LEN || len < opt[1]) {
+ (void) strcat(line, "sack|");
+ break;
+ }
+ sack_len = opt[1] - TCPOPT_HEADER_LEN;
+ sack_opt = opt + TCPOPT_HEADER_LEN;
+ end_opt = opt + optlen;
+
+ (void) strcat(line, "sack");
+ while (sack_len > 0) {
+ /*
+ * sack_len may not tell us the truth about
+ * the real length... Need to be careful
+ * not to step beyond the option buffer.
+ */
+ if (sack_opt + TCPOPT_SACK_LEN > end_opt) {
+ (void) strcat(line, "|");
+ break;
+ }
+ (void) sprintf(options, " %u-%u",
+ GET_UINT32(sack_opt),
+ GET_UINT32(sack_opt + 4));
+ (void) strcat(line, options);
+ sack_opt += TCPOPT_SACK_LEN;
+ sack_len -= TCPOPT_SACK_LEN;
+ }
+ break;
+ default:
+ (void) sprintf(options, "unknown %d", opt[0]);
+ (void) strcat(line, options);
+ break;
+ }
+ if (len <= 0) {
+ (void) sprintf(options, "optlen %d", len);
+ (void) strcat(line, options);
+ break;
+ }
+ opt += len;
+ optlen -= len;
+ if (optlen > 0) {
+ (void) strcat(line, ",");
+ }
+ }
+ (void) strcat(line, ">");
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_tftp.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_tftp.c
new file mode 100644
index 0000000000..38cc16ac34
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_tftp.c
@@ -0,0 +1,182 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1991 by Sun Microsystems, Inc.
+ */
+
+#ident "%Z%%M% %I% %E% SMI"
+
+#include <fcntl.h>
+#include <arpa/tftp.h>
+#include "snoop.h"
+
+extern char *dlc_header;
+char *tftperror();
+char *show_type();
+
+interpret_tftp(flags, tftp, fraglen)
+ int flags;
+ struct tftphdr *tftp;
+ int fraglen;
+{
+ char *name, *mode;
+ extern int src_port, dst_port;
+ int blocksize = fraglen - 4;
+
+ switch (ntohs(tftp->th_opcode)) {
+ case RRQ:
+ case WRQ:
+ add_transient(src_port, interpret_tftp);
+ break;
+ case ERROR:
+ del_transient(src_port);
+ break;
+ }
+
+ if (flags & F_SUM) {
+ switch (ntohs(tftp->th_opcode)) {
+ case RRQ:
+ name = (char *) &tftp->th_stuff;
+ mode = name + (strlen(name) + 1);
+ (void) sprintf(get_sum_line(),
+ "TFTP Read \"%s\" (%s)", name, mode);
+ break;
+ case WRQ:
+ name = (char *) &tftp->th_stuff;
+ mode = name + (strlen(name) + 1);
+ (void) sprintf(get_sum_line(),
+ "TFTP Write \"%s\" (%s)", name, mode);
+ break;
+ case DATA:
+ (void) sprintf(get_sum_line(),
+ "TFTP Data block %d (%d bytes)%s",
+ ntohs(tftp->th_block),
+ blocksize,
+ blocksize < 512 ? " (last block)":"");
+ break;
+ case ACK:
+ (void) sprintf(get_sum_line(),
+ "TFTP Ack block %d",
+ ntohs(tftp->th_block));
+ break;
+ case ERROR:
+ (void) sprintf(get_sum_line(),
+ "TFTP Error: %s",
+ tftperror(ntohs(tftp->th_code)));
+ break;
+ }
+ }
+
+ if (flags & F_DTAIL) {
+
+ show_header("TFTP: ", "Trivial File Transfer Protocol", fraglen);
+ show_space();
+ (void) sprintf(get_line((char *)tftp->th_opcode - dlc_header, 2),
+ "Opcode = %d (%s)",
+ ntohs(tftp->th_opcode),
+ show_type(ntohs(tftp->th_opcode)));
+
+ switch (ntohs(tftp->th_opcode)) {
+ case RRQ:
+ case WRQ:
+ name = (char *) &tftp->th_stuff;
+ mode = name + (strlen(name) + 1);
+ (void) sprintf(
+ get_line(name - dlc_header, strlen(name) + 1),
+ "File name = \"%s\"",
+ name);
+ (void) sprintf(
+ get_line(mode - dlc_header, strlen(mode) + 1),
+ "Transfer mode = %s",
+ mode);
+ break;
+
+ case DATA:
+ (void) sprintf(
+ get_line((char *)tftp->th_block - dlc_header, 2),
+ "Data block = %d%s",
+ ntohs(tftp->th_block),
+ blocksize < 512 ? " (last block)":"");
+ (void) sprintf(
+ get_line((char *)tftp->th_data - dlc_header, blocksize),
+ "[ %d bytes of data ]",
+ blocksize);
+ break;
+
+ case ACK:
+ (void) sprintf(
+ get_line((char *)tftp->th_block - dlc_header, 2),
+ "Acknowledge block = %d",
+ ntohs(tftp->th_block));
+ break;
+
+ case ERROR:
+ (void) sprintf(
+ get_line((char *)tftp->th_code - dlc_header, 2),
+ "Error = %d (%s)",
+ ntohs(tftp->th_code),
+ tftperror(ntohs(tftp->th_code)));
+ (void) sprintf(
+ get_line((char *)tftp->th_data - dlc_header,
+ strlen(tftp->th_data) + 1),
+ "Error string = \"%s\"",
+ tftp->th_data);
+ }
+ }
+
+ return (fraglen);
+}
+
+char *
+show_type(t)
+ int t;
+{
+ switch (t) {
+ case RRQ: return ("read request");
+ case WRQ: return ("write request");
+ case DATA: return ("data packet");
+ case ACK: return ("acknowledgement");
+ case ERROR: return ("error");
+ }
+ return ("?");
+}
+
+char *
+tftperror(code)
+ unsigned short code;
+{
+ static char buf[128];
+
+ switch (code) {
+ case EUNDEF: return ("not defined");
+ case ENOTFOUND: return ("file not found");
+ case EACCESS: return ("access violation");
+ case ENOSPACE: return ("disk full or allocation exceeded");
+ case EBADOP: return ("illegal TFTP operation");
+ case EBADID: return ("unknown transfer ID");
+ case EEXISTS: return ("file already exists");
+ case ENOUSER: return ("no such user");
+ }
+ (void) sprintf(buf, "%d", code);
+
+ return (buf);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_udp.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_udp.c
new file mode 100644
index 0000000000..30b3652222
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_udp.c
@@ -0,0 +1,128 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ident "%Z%%M% %I% %E% SMI" /* SunOS */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/if_ether.h>
+#include <netinet/udp.h>
+#include "snoop.h"
+
+extern char *dlc_header;
+
+interpret_udp(flags, udp, iplen, fraglen)
+ int flags;
+ struct udphdr *udp;
+ int iplen, fraglen;
+{
+ char *data;
+ int udplen;
+ int sunrpc;
+ char *pname;
+ char buff [32];
+
+ if (fraglen < sizeof (struct udphdr))
+ return (fraglen); /* incomplete header */
+
+ data = (char *)udp + sizeof (struct udphdr);
+ udplen = ntohs((u_short)udp->uh_ulen) - sizeof (struct udphdr);
+ fraglen -= sizeof (struct udphdr);
+ if (fraglen > udplen)
+ fraglen = udplen;
+
+ if (flags & F_SUM) {
+ (void) sprintf(get_sum_line(),
+ "UDP D=%d S=%d LEN=%d",
+ ntohs(udp->uh_dport),
+ ntohs(udp->uh_sport),
+ ntohs((u_short)udp->uh_ulen));
+ }
+
+ sunrpc = !reservedport(IPPROTO_UDP, ntohs(udp->uh_dport)) &&
+ !reservedport(IPPROTO_UDP, ntohs(udp->uh_sport)) &&
+ valid_rpc(data, udplen);
+
+ if (flags & F_DTAIL) {
+ show_header("UDP: ", "UDP Header", udplen);
+ show_space();
+ (void) sprintf(get_line((char *)udp->uh_sport - dlc_header, 1),
+ "Source port = %d",
+ ntohs(udp->uh_sport));
+
+ if (sunrpc) {
+ pname = "(Sun RPC)";
+ } else {
+ pname = getportname(IPPROTO_UDP, ntohs(udp->uh_dport));
+ if (pname == NULL) {
+ pname = "";
+ } else {
+ (void) sprintf(buff, "(%s)", pname);
+ pname = buff;
+ }
+ }
+ (void) sprintf(get_line((char *)udp->uh_dport - dlc_header, 1),
+ "Destination port = %d %s",
+ ntohs(udp->uh_dport), pname);
+ (void) sprintf(get_line((char *)udp->uh_ulen - dlc_header, 1),
+ "Length = %d %s",
+ ntohs((u_short)udp->uh_ulen),
+ udplen > fraglen ?
+ "(Not all data contained in this fragment)"
+ : "");
+ (void) sprintf(
+ get_line((char *)udp->uh_sum - dlc_header, 1),
+ "Checksum = %04X %s",
+ ntohs(udp->uh_sum),
+ udp->uh_sum == 0 ? "(no checksum)" : "");
+ show_space();
+ }
+
+
+ /* go to the next protocol layer */
+
+ if (!interpret_reserved(flags, IPPROTO_UDP,
+ ntohs(udp->uh_sport),
+ ntohs(udp->uh_dport),
+ data, fraglen)) {
+ if (fraglen > 0 && sunrpc)
+ interpret_rpc(flags, data, fraglen, IPPROTO_UDP);
+ }
+
+ return (fraglen);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_zip.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_zip.c
new file mode 100644
index 0000000000..b1093b033b
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_zip.c
@@ -0,0 +1,407 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#include <at.h>
+#include <snoop.h>
+
+char *print_macaddr(uint8_t *, int);
+
+static char *zip_flags(char);
+static char *zip_flags_long(char);
+
+void
+interpret_ddp_zip(int flags, struct zip_hdr *zip, int len)
+{
+ int cnt;
+ uint16_t net;
+ uint16_t range;
+ uint8_t *p;
+ char zone[33];
+ char defzone[60] = "";
+ char mcast[50] = "";
+ uint8_t gniflags;
+ uint8_t *tail = (uint8_t *)zip + len;
+
+ if (flags & F_SUM) {
+ if (len < sizeof (struct zip_hdr))
+ goto out;
+
+ switch (zip->zip_func) {
+ case ZIP_QUERY:
+ cnt = zip->zip_netcnt;
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "ZIP Query CNT = %d", cnt);
+ break;
+ case ZIP_REPLY:
+ case ZIP_EXT_REPLY:
+ cnt = zip->zip_netcnt;
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "ZIP Reply CNT = %d", cnt);
+ break;
+ case ZIP_GET_NET_INFO:
+ p = &zip->zip_func;
+
+ if ((p+6 > tail) || (p+7+p[6] > tail))
+ goto out;
+
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "ZIP GNI Zone = \"%.*s\"", p[6], &p[7]);
+ break;
+ case ZIP_GET_NET_INFO_REPLY:
+ p = &zip->zip_func;
+
+ gniflags = p[1];
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "ZIP GNI Rep Flags 0x%x (%s)",
+ gniflags, zip_flags(gniflags));
+ break;
+ default:
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "ZIP CMD = %d", zip->zip_func);
+ break;
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("ZIP: ", "ZIP Header", len);
+ show_space();
+
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Length = %d", len);
+
+ if (len < sizeof (struct zip_hdr))
+ goto out;
+
+ switch (zip->zip_func) {
+ case ZIP_QUERY:
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Query, Network count = %d", zip->zip_netcnt);
+ cnt = zip->zip_netcnt;
+ p = (uint8_t *)(zip + 1);
+ while (cnt--) {
+ if (p+2 > tail)
+ goto out;
+ net = get_short(p);
+ p += 2;
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(), "Net = %d", net);
+ }
+ break;
+ case ZIP_REPLY:
+ case ZIP_EXT_REPLY:
+ cnt = zip->zip_netcnt;
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Reply, Network count = %d", cnt);
+
+ p = (uint8_t *)(zip + 1);
+ while (cnt--) {
+ if (p+2 > tail)
+ goto out;
+ net = get_short(p);
+ p += 2;
+ if (p+1 > tail || (&p[1] + p[0]) > tail)
+ goto out;
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ "Network = %d, Zone = \"%.*s\"",
+ net, p[0], &p[1]);
+ p += p[0] + 1;
+ }
+ break;
+ case ZIP_GET_NET_INFO:
+ p = &zip->zip_func;
+ if (p+1 > tail || (&p[1] + p[0]) > tail)
+ goto out;
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "GetNetInfo Zone = \"%.*s\"", p[0], &p[1]);
+ break;
+ case ZIP_GET_NET_INFO_REPLY:
+ p = &zip->zip_func;
+ if (p+5 > tail)
+ goto out;
+ gniflags = p[1];
+ net = get_short(&p[2]);
+ range = get_short(&p[4]);
+
+ if (p+7 > tail || (&p[7] + p[6]) > tail)
+ goto out;
+ (void) snprintf(zone, sizeof (zone),
+ "%.*s", p[6], &p[7]);
+ p = &p[7] + p[6];
+
+ if ((gniflags & ZIP_FLG_USEBRC) == 0) {
+ if (p+1 > tail || (&p[1] + p[0]) > tail)
+ goto out;
+ (void) snprintf(mcast, sizeof (mcast),
+ "Multicast address = %s",
+ print_macaddr(&p[1], p[0]));
+ }
+
+ if (gniflags & ZIP_FLG_ZINV) {
+ p = &p[1] + p[0];
+ if (p+1 > tail || (&p[1] + p[0]) > tail)
+ goto out;
+ (void) snprintf(defzone, sizeof (defzone),
+ "Default Zone = \"%.*s\"",
+ p[0], &p[1]);
+ }
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "GetNetInfo Reply, Flags 0x%x (%s)",
+ gniflags, zip_flags_long(gniflags));
+
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Network number = %d-%d", net, range);
+
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Zone = \"%s\"", zone);
+
+ if (mcast[0])
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ "%s", mcast);
+
+ if (defzone[0])
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ "%s", defzone);
+
+ break;
+ case ZIP_NOTIFY:
+ p = &zip->zip_func;
+ if (p+5 > tail)
+ goto out;
+
+ gniflags = p[1];
+ net = get_short(&p[2]);
+ range = get_short(&p[4]);
+
+ if (p+7 > tail || (&p[7] + p[6]) > tail)
+ goto out;
+ (void) snprintf(zone, sizeof (zone),
+ "%.*s", p[6], &p[7]);
+ p = &p[7] + p[6];
+
+ if ((gniflags & ZIP_FLG_USEBRC) == 0) {
+ if (p+1 > tail || (&p[1] + p[0]) > tail)
+ goto out;
+ (void) snprintf(mcast, sizeof (mcast),
+ "New Multicast address = %s",
+ print_macaddr(&p[1], p[0]));
+ }
+
+ if (p+1 > tail || (&p[1] + p[0]) > tail)
+ goto out;
+
+ p = &p[1] + p[0];
+
+ if (p+1 > tail || (&p[1] + p[0]) > tail)
+ goto out;
+
+ (void) snprintf(defzone, sizeof (defzone),
+ "New Default Zone = \"%.*s\"",
+ p[0], &p[1]);
+
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Notify, Flags 0x%x (%s)",
+ gniflags, zip_flags_long(gniflags));
+
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Old Zone = \"%s\"", zone);
+
+ if (mcast[0])
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(), "%s", mcast);
+
+ if (defzone[0])
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(), "%s", defzone);
+
+ break;
+ default:
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Op = %d", zip->zip_func);
+ break;
+ }
+ }
+ return;
+out:
+ if (flags & F_SUM)
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "ZIP (short packet)");
+ if (flags & F_DTAIL)
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "ZIP (short packet)");
+}
+
+static char *
+zip_flags(char flags)
+{
+ static char buf[50];
+ char *p = buf;
+ char *tail = &buf[sizeof (buf)];
+
+ buf[0] = '\0';
+
+ if (flags & ZIP_FLG_ZINV)
+ p += snprintf(p, tail-p, "IZ");
+
+ if (flags & ZIP_FLG_USEBRC)
+ p += snprintf(p, tail-p, p == buf ? "UB" : " UB");
+
+ if (flags & ZIP_FLG_ONEZ)
+ (void) snprintf(p, tail-p, p == buf ? "OOZ" : " OOZ");
+
+ return (buf);
+}
+
+static char *
+zip_flags_long(char flags)
+{
+ static char buf[50];
+ char *p = buf;
+ char *tail = &buf[sizeof (buf)];
+
+ buf[0] = '\0';
+
+ if (flags & ZIP_FLG_ZINV)
+ p += snprintf(p, tail-p, "ZoneInvalid");
+
+ if (flags & ZIP_FLG_USEBRC)
+ p += snprintf(p, tail-p,
+ p == buf ? "UseBroadcast" : " UseBroadcast");
+
+ if (flags & ZIP_FLG_ONEZ)
+ (void) snprintf(p, tail-p,
+ p == buf ? "OnlyOneZone" : " OnlyOneZone");
+
+ return (buf);
+}
+
+void
+interpret_atp_zip(int flags, struct atp_hdr *atp, int len)
+{
+ int cnt;
+ uint8_t *data;
+ uint8_t *tail = (uint8_t *)(atp+1) + len;
+
+ if (flags & F_SUM) {
+ if (len < 0) {
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "ZIP (short packet)");
+ return;
+ }
+
+ switch (atp_fun(atp->atp_ctrl)) {
+ case ATP_TREQ:
+ switch (atp->atp_user[0]) {
+ case ZIP_ATP_GETMYZONE:
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "ZIP GetMyZone");
+ break;
+
+ case ZIP_ATP_GETZONELIST:
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "ZIP GetZoneList");
+ break;
+
+ case ZIP_ATP_GETLOCALZONES:
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "ZIP GetLocalZones");
+ break;
+ }
+ break;
+ case ATP_TRESP:
+ cnt = get_short(&atp->atp_user[2]);
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "ZIP ZoneReply, Cnt = %d", cnt);
+
+ break;
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("ZIP: ", "ZIP Header", len);
+ show_space();
+
+ if (len < 0) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "ZIP (short packet)");
+ return;
+ }
+
+ switch (atp_fun(atp->atp_ctrl)) {
+ case ATP_TREQ:
+ switch (atp->atp_user[0]) {
+ case ZIP_ATP_GETMYZONE:
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ "GetMyZone, Start Index = %d",
+ get_short(&atp->atp_user[2]));
+ break;
+ case ZIP_ATP_GETZONELIST:
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ "GetZoneList, Start Index = %d",
+ get_short(&atp->atp_user[2]));
+ break;
+ case ZIP_ATP_GETLOCALZONES:
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ "GetLocalZones, Start Index = %d",
+ get_short(&atp->atp_user[2]));
+ break;
+ }
+ break;
+ case ATP_TRESP:
+ cnt = get_short(&atp->atp_user[2]);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "ZoneReply, Number of Zones = %d, Length = %d",
+ cnt, len);
+
+ data = (uint8_t *)atp + DDPHDR_SIZE + ATPHDR_SIZE;
+
+ while (cnt--) {
+ if (data > tail ||
+ (&data[1] + data[0]) > tail) {
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ "ZoneReply (short packet)");
+ return;
+ }
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ "Zone = \"%.*s\"", data[0], &data[1]);
+ data += data[0] + 1;
+ }
+ break;
+ }
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/soconfig.c b/usr/src/cmd/cmd-inet/usr.sbin/soconfig.c
new file mode 100644
index 0000000000..5d3838623f
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/soconfig.c
@@ -0,0 +1,301 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1991-1996,2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <locale.h>
+
+#define MAXLINELEN 4096
+
+/*
+ * Usage:
+ * sonconfig -f <file>
+ * Reads input from file. The file is structured as
+ * <fam> <type> <protocol> <path>
+ * <fam> <type> <protocol>
+ * with the first line registering and the second line
+ * deregistering.
+ *
+ * soconfig <fam> <type> <protocol> <path>
+ * registers
+ *
+ * soconfig <fam> <type> <protocol>
+ * deregisters
+ */
+
+static int parse_file(char *filename);
+
+static int split_line(char *line, char *argvec[], int maxargvec);
+
+static int parse_params(char *famstr, char *typestr, char *protostr,
+ char *path, int line);
+
+static int parse_int(char *str);
+
+static void usage(void);
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int ret;
+
+ argc--; argv++;
+
+ (void) setlocale(LC_ALL, "");
+#if !defined(TEXT_DOMAIN)
+#define TEXT_DOMAIN "SYS_TEST"
+#endif
+ (void) textdomain(TEXT_DOMAIN);
+
+ if (argc == 2 && strcmp(argv[0], "-f") == 0) {
+ ret = parse_file(argv[1]);
+ exit(ret);
+ }
+ if (argc == 3) {
+ ret = parse_params(argv[0], argv[1], argv[2], NULL, -1);
+ exit(ret);
+ }
+ if (argc == 4) {
+ ret = parse_params(argv[0], argv[1], argv[2], argv[3], -1);
+ exit(ret);
+ }
+ usage();
+ exit(1);
+ /* NOTREACHED */
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, gettext(
+ "Usage: soconfig -f <file>\n"
+ "\tsoconfig <fam> <type> <protocol> <path>\n"
+ "\tsoconfig <fam> <type> <protocol>\n"));
+}
+
+/*
+ * Open the specified file and parse each line. Skip comments (everything
+ * after a '#'). Return 1 if at least one error was encountered; otherwise 0.
+ */
+static int
+parse_file(char *filename)
+{
+ char line[MAXLINELEN];
+ char pline[MAXLINELEN];
+ int argcount;
+ char *argvec[20];
+ FILE *fp;
+ int linecount = 0;
+ int numerror = 0;
+
+ fp = fopen(filename, "r");
+ if (fp == NULL) {
+ perror("soconfig: open");
+ fprintf(stderr, "\n");
+ usage();
+ return (1);
+ }
+
+ while (fgets(line, sizeof (line) - 1, fp) != NULL) {
+ linecount++;
+ strcpy(pline, line);
+ argcount = split_line(pline, argvec,
+ sizeof (argvec) / sizeof (argvec[0]));
+#ifdef DEBUG
+ {
+ int i;
+
+ printf("scanned %d args\n", argcount);
+ for (i = 0; i < argcount; i++)
+ printf("arg[%d]: %s\n", i, argvec[i]);
+ }
+#endif /* DEBUG */
+ switch (argcount) {
+ case 0:
+ /* Empty line - or comment only line */
+ break;
+ case 3:
+ numerror += parse_params(argvec[0], argvec[1],
+ argvec[2], NULL, linecount);
+ break;
+ case 4:
+ numerror += parse_params(argvec[0], argvec[1],
+ argvec[2], argvec[3], linecount);
+ break;
+ default:
+ numerror++;
+ fprintf(stderr,
+ gettext("Malformed line: <%s>\n"), line);
+ fprintf(stderr,
+ gettext("\ton line %d\n"), linecount);
+ break;
+ }
+ }
+ (void) fclose(fp);
+
+ if (numerror > 0)
+ return (1);
+ else
+ return (0);
+}
+
+/*
+ * Parse a line splitting it off at whitspace characters.
+ * Modifies the content of the string by inserting NULLs.
+ */
+static int
+split_line(char *line, char *argvec[], int maxargvec)
+{
+ int i = 0;
+ char *cp;
+
+ /* Truncate at the beginning of a comment */
+ cp = strchr(line, '#');
+ if (cp != NULL)
+ *cp = NULL;
+
+ /* CONSTCOND */
+ while (1) {
+ /* Skip any whitespace */
+ while (isspace(*line) && *line != NULL)
+ line++;
+
+ if (i >= maxargvec)
+ return (i);
+
+ argvec[i] = line;
+ if (*line == NULL)
+ return (i);
+ i++;
+ /* Skip until next whitespace */
+ while (!isspace(*line) && *line != NULL)
+ line++;
+ if (*line != NULL) {
+ /* Break off argument */
+ *line++ = NULL;
+ }
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * Parse the set of parameters and issues the sockconfig syscall.
+ * If line is not -1 it is assumed to be the line number in the file.
+ */
+static int
+parse_params(char *famstr, char *typestr, char *protostr, char *path, int line)
+{
+ int fam, type, protocol;
+
+ fam = parse_int(famstr);
+ if (fam == -1) {
+ fprintf(stderr, gettext("Bad family number: %s\n"), famstr);
+ if (line != -1)
+ fprintf(stderr,
+ gettext("\ton line %d\n"), line);
+ else {
+ fprintf(stderr, "\n");
+ usage();
+ }
+ return (1);
+ }
+
+ type = parse_int(typestr);
+ if (type == -1) {
+ fprintf(stderr,
+ gettext("Bad socket type number: %s\n"), typestr);
+ if (line != -1)
+ fprintf(stderr,
+ gettext("\ton line %d\n"), line);
+ else {
+ fprintf(stderr, "\n");
+ usage();
+ }
+ return (1);
+ }
+
+ protocol = parse_int(protostr);
+ if (protocol == -1) {
+ fprintf(stderr,
+ gettext("Bad protocol number: %s\n"), protostr);
+ if (line != -1)
+ fprintf(stderr,
+ gettext("\ton line %d\n"), line);
+ else {
+ fprintf(stderr, "\n");
+ usage();
+ }
+ return (1);
+ }
+
+
+ if (path != NULL) {
+ struct stat stats;
+
+ if (stat(path, &stats) == -1) {
+ perror(path);
+ if (line != -1)
+ fprintf(stderr,
+ gettext("\ton line %d\n"), line);
+ else {
+ fprintf(stderr, "\n");
+ usage();
+ }
+ return (1);
+ }
+ }
+
+#ifdef DEBUG
+ printf("not calling sockconfig(%d, %d, %d, %s)\n",
+ fam, type, protocol, path == NULL ? "(null)" : path);
+#else
+ if (_sockconfig(fam, type, protocol, path) == -1) {
+ perror("sockconfig");
+ return (1);
+ }
+#endif
+ return (0);
+}
+
+static int
+parse_int(char *str)
+{
+ char *end;
+ int res;
+
+ res = strtol(str, &end, 0);
+ if (end == str)
+ return (-1);
+ return (res);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/sppptun/Makefile b/usr/src/cmd/cmd-inet/usr.sbin/sppptun/Makefile
new file mode 100644
index 0000000000..1ede95a56d
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/sppptun/Makefile
@@ -0,0 +1,59 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 2000-2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+# cmd/cmd-inet/usr.sbin/sppptun/Makefile
+#
+
+PROG= sppptun
+OBJS= sppptun.o
+SRCS= $(OBJS:%.o=%.c)
+
+include ../../../Makefile.cmd
+include ../../Makefile.cmd-inet
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+_msg: $(POFILE)
+
+CPPFLAGS += -I$(SRC)/uts/common
+
+$(PROG): $(OBJS)
+ $(LINK.c) $(OBJS) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+install: $(PROG) .WAIT $(ROOTUSRSBINPROG)
+
+FRC:
+
+clean:
+ $(RM) $(OBJS)
+
+lint: lint_SRCS
+
+include ../../../Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/sppptun/sppptun.c b/usr/src/cmd/cmd-inet/usr.sbin/sppptun/sppptun.c
new file mode 100644
index 0000000000..538996181c
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/sppptun/sppptun.c
@@ -0,0 +1,671 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * sppptun.c - Solaris STREAMS PPP multiplexing tunnel driver
+ * installer.
+ *
+ * Copyright (c) 2000-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <signal.h>
+#include <alloca.h>
+#include <stropts.h>
+#include <fcntl.h>
+#include <locale.h>
+#include <sys/dlpi.h>
+#include <sys/fcntl.h>
+#include <sys/stropts.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <net/sppptun.h>
+
+static char *myname; /* Copied from argv[0] */
+static int verbose; /* -v on command line */
+
+/* Data gathered during per-style attach routine. */
+struct attach_data {
+ ppptun_lname appstr; /* String to append to interface name (PPA) */
+ ppptun_atype localaddr; /* Local interface address */
+ int locallen; /* Length of local address */
+};
+
+/* Per-protocol plumbing data */
+struct protos {
+ const char *name;
+ const char *desc;
+ int (*attach)(struct protos *prot, char *ifname,
+ struct attach_data *adata);
+ int protval;
+ int style;
+};
+
+/*
+ * Print a usage string and terminate. Used for command line argument
+ * errors. Does not return.
+ */
+static void
+usage(void)
+{
+ (void) fprintf(stderr, gettext(
+ "Usage:\n\t%s plumb [<protocol> <device>]\n"
+ "\t%s unplumb <interface-name>\n"
+ "\t%s query\n"), myname, myname, myname);
+ exit(1);
+}
+
+/*
+ * Await a DLPI response to a previous driver command. "etype" is set
+ * to the expected response primitive. "rptr" and "rlen" may point to
+ * a buffer to hold returned data, if desired. Otherwise, "rptr" is
+ * NULL. Returns -1 on error, 0 on success.
+ *
+ * If "rlen" is a positive number, then it indicates the number of
+ * bytes expected in return, and any longer response is truncated to
+ * that value, and any shorter response generates a warning message.
+ * If it's a negative number, then it indicates the maximum number of
+ * bytes expected, and no warning is printed if fewer are received.
+ */
+static int
+dlpi_reply(int fd, int etype, void *rptr, int rlen)
+{
+ /* Align 'buf' on natural boundary for aggregates. */
+ uintptr_t buf[BUFSIZ/sizeof (uintptr_t)];
+ int flags;
+ union DL_primitives *dlp = (union DL_primitives *)buf;
+ struct strbuf ctl;
+
+ /* read reply */
+ ctl.buf = (caddr_t)dlp;
+ ctl.len = 0;
+ ctl.maxlen = BUFSIZ;
+ flags = 0;
+ if (getmsg(fd, &ctl, NULL, &flags) < 0) {
+ perror("getmsg");
+ return (-1);
+ }
+
+ /* Validate reply. */
+ if (ctl.len < sizeof (t_uscalar_t)) {
+ (void) fprintf(stderr, gettext("%s: request: short reply\n"),
+ myname);
+ return (-1);
+ }
+
+ if (dlp->dl_primitive == DL_ERROR_ACK) {
+ (void) fprintf(stderr,
+ gettext("%s: request: dl_errno %lu errno %lu\n"), myname,
+ dlp->error_ack.dl_errno, dlp->error_ack.dl_unix_errno);
+ return (-1);
+ }
+ if (dlp->dl_primitive != etype) {
+ (void) fprintf(stderr, gettext("%s: request: unexpected "
+ "dl_primitive %lu received\n"), myname, dlp->dl_primitive);
+ return (-1);
+ }
+ if (rptr == NULL)
+ return (0);
+ if (ctl.len < rlen) {
+ (void) fprintf(stderr, gettext("%s: request: short information"
+ " received %d < %d\n"), myname, ctl.len, rlen);
+ return (-1);
+ }
+ if (rlen < 0)
+ rlen = -rlen;
+ (void) memcpy(rptr, buf, rlen);
+ return (0);
+}
+
+/*
+ * Send a DLPI Info-Request message and return the response in the
+ * provided buffer. Returns -1 on error, 0 on success.
+ */
+static int
+dlpi_info_req(int fd, dl_info_ack_t *info_ack)
+{
+ dl_info_req_t info_req;
+ struct strbuf ctl;
+ int flags;
+
+ (void) memset(&info_req, '\0', sizeof (info_req));
+ info_req.dl_primitive = DL_INFO_REQ;
+
+ ctl.maxlen = 0;
+ ctl.len = DL_INFO_REQ_SIZE;
+ ctl.buf = (char *)&info_req;
+
+ flags = 0;
+ if (putmsg(fd, &ctl, (struct strbuf *)NULL, flags) < 0) {
+ perror("putmsg DL_INFO_REQ");
+ return (-1);
+ }
+ return (dlpi_reply(fd, DL_INFO_ACK, info_ack, sizeof (*info_ack)));
+}
+
+/*
+ * Send a DLPI Attach-Request message for the indicated PPA. Returns
+ * -1 on error, 0 for success.
+ */
+static int
+dlpi_attach_req(int fd, int ppa)
+{
+ dl_attach_req_t attach_req;
+ struct strbuf ctl;
+ int flags;
+
+ (void) memset(&attach_req, '\0', sizeof (attach_req));
+ attach_req.dl_primitive = DL_ATTACH_REQ;
+ attach_req.dl_ppa = ppa;
+
+ ctl.maxlen = 0;
+ ctl.len = DL_ATTACH_REQ_SIZE;
+ ctl.buf = (char *)&attach_req;
+
+ flags = 0;
+ if (putmsg(fd, &ctl, (struct strbuf *)NULL, flags) < 0) {
+ perror("putmsg DL_ATTACH_REQ");
+ return (-1);
+ }
+ return (dlpi_reply(fd, DL_OK_ACK, NULL, 0));
+}
+
+/*
+ * Send a DLPI Bind-Request message for the requested SAP and set the
+ * local address. Returns -1 for error. Otherwise, the length of the
+ * local address is returned.
+ */
+static int
+dlpi_bind_req(int fd, int sap, uint8_t *localaddr, int maxaddr)
+{
+ dl_bind_req_t bind_req;
+ dl_bind_ack_t *back;
+ struct strbuf ctl;
+ int flags, repsize, rsize;
+
+ (void) memset(&bind_req, '\0', sizeof (*&bind_req));
+ bind_req.dl_primitive = DL_BIND_REQ;
+ /* DLPI SAPs are in host byte order! */
+ bind_req.dl_sap = sap;
+ bind_req.dl_service_mode = DL_CLDLS;
+
+ ctl.maxlen = 0;
+ ctl.len = DL_BIND_REQ_SIZE;
+ ctl.buf = (char *)&bind_req;
+
+ flags = 0;
+ if (putmsg(fd, &ctl, (struct strbuf *)NULL, flags) < 0) {
+ perror("putmsg DL_BIND_REQ");
+ return (-1);
+ }
+
+ repsize = sizeof (*back) + maxaddr;
+ back = (dl_bind_ack_t *)alloca(repsize);
+ if (dlpi_reply(fd, DL_BIND_ACK, (void *)back, -repsize) < 0)
+ return (-1);
+ rsize = back->dl_addr_length;
+ if (rsize > maxaddr || back->dl_addr_offset+rsize > repsize) {
+ (void) fprintf(stderr, gettext("%s: Bad hardware address size "
+ "from driver; %d > %d or %lu+%d > %d\n"), myname,
+ rsize, maxaddr, back->dl_addr_offset, rsize, repsize);
+ return (-1);
+ }
+ (void) memcpy(localaddr, (char *)back + back->dl_addr_offset, rsize);
+ return (rsize);
+}
+
+/*
+ * Return a printable string for a DLPI style number. (Unfortunately,
+ * these style numbers aren't just simple integer values, and printing
+ * with %d gives ugly output.)
+ */
+static const char *
+styleof(int dlstyle)
+{
+ static char buf[32];
+
+ switch (dlstyle) {
+ case DL_STYLE1:
+ return ("1");
+ case DL_STYLE2:
+ return ("2");
+ }
+ (void) snprintf(buf, sizeof (buf), gettext("Unknown (0x%04X)"),
+ dlstyle);
+ return ((const char *)buf);
+}
+
+/*
+ * General DLPI attach function. This is called indirectly through
+ * the protos structure for the selected lower stream protocol.
+ */
+static int
+dlpi_attach(struct protos *prot, char *ifname, struct attach_data *adata)
+{
+ int devfd, ppa, dlstyle, retv;
+ dl_info_ack_t dl_info;
+ char tname[MAXPATHLEN], *cp;
+
+ cp = ifname + strlen(ifname) - 1;
+ while (cp > ifname && isdigit(*cp))
+ cp--;
+ cp++;
+ ppa = strtol(cp, NULL, 10);
+
+ /*
+ * Try once for the exact device name as a node. If it's
+ * there, then this should be a DLPI style 1 driver (one node
+ * per instance). If it's not, then it should be a style 2
+ * driver (attach specifies instance number).
+ */
+ dlstyle = DL_STYLE1;
+ (void) strlcpy(tname, ifname, MAXPATHLEN-1);
+ if ((devfd = open(tname, O_RDWR)) < 0) {
+ if (cp < ifname + MAXPATHLEN)
+ tname[cp - ifname] = '\0';
+ if ((devfd = open(tname, O_RDWR)) < 0) {
+ perror(ifname);
+ return (-1);
+ }
+ dlstyle = DL_STYLE2;
+ }
+
+ if (verbose)
+ (void) printf(gettext("requesting device info on %s\n"),
+ tname);
+ if (dlpi_info_req(devfd, &dl_info))
+ return (-1);
+ if (dl_info.dl_provider_style != dlstyle) {
+ (void) fprintf(stderr, gettext("%s: unexpected DLPI provider "
+ "style on %s: got %s, "), myname, tname,
+ styleof(dl_info.dl_provider_style));
+ (void) fprintf(stderr, gettext("expected %s\n"),
+ styleof(dlstyle));
+ if (ifname[0] != '\0' &&
+ !isdigit(ifname[strlen(ifname) - 1])) {
+ (void) fprintf(stderr, gettext("(did you forget an "
+ "instance number?)\n"));
+ }
+ (void) close(devfd);
+ return (-1);
+ }
+
+ if (dlstyle == DL_STYLE2) {
+ if (verbose)
+ (void) printf(gettext("attaching to ppa %d\n"), ppa);
+ if (dlpi_attach_req(devfd, ppa)) {
+ (void) close(devfd);
+ return (-1);
+ }
+ }
+
+ if (verbose)
+ (void) printf(gettext("binding to Ethertype %04X\n"),
+ prot->protval);
+ retv = dlpi_bind_req(devfd, prot->protval,
+ (uint8_t *)&adata->localaddr, sizeof (adata->localaddr));
+ if (retv < 0) {
+ (void) close(devfd);
+ return (-1);
+ }
+ adata->locallen = retv;
+
+ (void) snprintf(adata->appstr, sizeof (adata->appstr), "%d", ppa);
+ return (devfd);
+}
+
+
+static struct protos proto_list[] = {
+ { "pppoe", "RFC 2516 PPP over Ethernet", dlpi_attach, ETHERTYPE_PPPOES,
+ PTS_PPPOE },
+ { "pppoed", "RFC 2516 PPP over Ethernet Discovery", dlpi_attach,
+ ETHERTYPE_PPPOED, PTS_PPPOE },
+ { NULL }
+};
+
+/*
+ * Issue a STREAMS I_STR ioctl and fetch the result. Returns -1 on
+ * error, or length of returned data on success.
+ */
+static int
+strioctl(int fd, int cmd, void *ptr, int ilen, int olen, const char *iocname)
+{
+ struct strioctl str;
+
+ str.ic_cmd = cmd;
+ str.ic_timout = 0;
+ str.ic_len = ilen;
+ str.ic_dp = ptr;
+
+ if (ioctl(fd, I_STR, &str) == -1) {
+ perror(iocname);
+ return (-1);
+ }
+
+ if (olen >= 0) {
+ if (str.ic_len > olen && verbose > 1) {
+ (void) printf(gettext("%s:%s: extra data received; "
+ "%d > %d\n"), myname, iocname, str.ic_len, olen);
+ } else if (str.ic_len < olen) {
+ (void) fprintf(stderr, gettext("%s:%s: expected %d "
+ "bytes, got %d\n"), myname, iocname, olen,
+ str.ic_len);
+ return (-1);
+ }
+ }
+
+ return (str.ic_len);
+}
+
+/*
+ * Handle user request to plumb a new lower stream under the sppptun
+ * driver.
+ */
+static int
+plumb_it(int argc, char **argv)
+{
+ int devfd, muxfd, muxid;
+ struct ppptun_info pti;
+ char *cp, *ifname;
+ struct protos *prot;
+ char dname[MAXPATHLEN];
+ struct attach_data adata;
+
+ /* If no protocol requested, then list known protocols. */
+ if (optind == argc) {
+ (void) puts("Known tunneling protocols:");
+ for (prot = proto_list; prot->name != NULL; prot++)
+ (void) printf("\t%s\t%s\n", prot->name, prot->desc);
+ return (0);
+ }
+
+ /* If missing protocol or device, then abort. */
+ if (optind != argc-2)
+ usage();
+
+ /* Look up requested protocol. */
+ cp = argv[optind++];
+ for (prot = proto_list; prot->name != NULL; prot++)
+ if (strcasecmp(cp, prot->name) == 0)
+ break;
+ if (prot->name == NULL) {
+ (void) fprintf(stderr, gettext("%s: unknown protocol %s\n"),
+ myname, cp);
+ return (1);
+ }
+
+ /* Get interface and make relative to /dev/ if necessary. */
+ ifname = argv[optind];
+ if (ifname[0] != '.' && ifname[0] != '/') {
+ (void) snprintf(dname, sizeof (dname), "/dev/%s", ifname);
+ ifname = dname;
+ }
+
+ /* Call per-protocol attach routine to open device */
+ if (verbose)
+ (void) printf(gettext("opening %s\n"), ifname);
+ devfd = (*prot->attach)(prot, ifname, &adata);
+ if (devfd < 0)
+ return (1);
+
+ /* Open sppptun driver */
+ if (verbose)
+ (void) printf(gettext("opening /dev/%s\n"), PPP_TUN_NAME);
+ if ((muxfd = open("/dev/" PPP_TUN_NAME, O_RDWR)) < 0) {
+ perror("/dev/" PPP_TUN_NAME);
+ return (1);
+ }
+
+ /* Push sppptun module on top of lower driver. */
+ if (verbose)
+ (void) printf(gettext("pushing %s on %s\n"), PPP_TUN_NAME,
+ ifname);
+ if (ioctl(devfd, I_PUSH, PPP_TUN_NAME) == -1) {
+ perror("I_PUSH " PPP_TUN_NAME);
+ return (1);
+ }
+
+ /* Get the name of the newly-created lower stream. */
+ if (verbose)
+ (void) printf(gettext("getting new interface name\n"));
+ if (strioctl(devfd, PPPTUN_GNAME, pti.pti_name, 0,
+ sizeof (pti.pti_name), "PPPTUN_GNAME") < 0)
+ return (1);
+ if (verbose)
+ (void) printf(gettext("got interface %s\n"), pti.pti_name);
+
+ /* Convert stream name to protocol-specific name. */
+ if ((cp = strchr(pti.pti_name, ':')) != NULL)
+ *cp = '\0';
+ (void) snprintf(pti.pti_name+strlen(pti.pti_name),
+ sizeof (pti.pti_name)-strlen(pti.pti_name), "%s:%s", adata.appstr,
+ prot->name);
+
+ /* Change the lower stream name. */
+ if (verbose)
+ (void) printf(gettext("resetting interface name to %s\n"),
+ pti.pti_name);
+ if (strioctl(devfd, PPPTUN_SNAME, pti.pti_name,
+ sizeof (pti.pti_name), 0, "PPPTUN_SNAME") < 0) {
+ if (errno == EEXIST)
+ (void) fprintf(stderr, gettext("%s: %s already "
+ "installed\n"), myname, pti.pti_name);
+ return (1);
+ }
+
+ /*
+ * Send down the local interface address to the lower stream
+ * so that it can originate packets.
+ */
+ if (verbose)
+ (void) printf(gettext("send down local address\n"));
+ if (strioctl(devfd, PPPTUN_LCLADDR, &adata.localaddr, adata.locallen,
+ 0, "PPPTUN_LCLADDR") < 0)
+ return (1);
+
+ /* Link the lower stream under the tunnel device. */
+ if (verbose)
+ (void) printf(gettext("doing I_PLINK\n"));
+ if ((muxid = ioctl(muxfd, I_PLINK, devfd)) == -1) {
+ perror("I_PLINK");
+ return (1);
+ }
+
+ /*
+ * Give the tunnel driver the multiplex ID of the new lower
+ * stream. This allows the unplumb function to find and
+ * disconnect the lower stream.
+ */
+ if (verbose)
+ (void) printf(gettext("sending muxid %d and style %d to "
+ "driver\n"), muxid, prot->style);
+ pti.pti_muxid = muxid;
+ pti.pti_style = prot->style;
+ if (strioctl(muxfd, PPPTUN_SINFO, &pti, sizeof (pti), 0,
+ "PPPTUN_SINFO") < 0)
+ return (1);
+
+ if (verbose)
+ (void) printf(gettext("done; installed %s\n"), pti.pti_name);
+ else
+ (void) puts(pti.pti_name);
+
+ return (0);
+}
+
+/*
+ * Handle user request to unplumb an existing lower stream from the
+ * sppptun driver.
+ */
+static int
+unplumb_it(int argc, char **argv)
+{
+ char *ifname;
+ int muxfd;
+ struct ppptun_info pti;
+
+ /*
+ * Need to have the name of the lower stream on the command
+ * line.
+ */
+ if (optind != argc-1)
+ usage();
+
+ ifname = argv[optind];
+
+ /* Open the tunnel driver. */
+ if (verbose)
+ (void) printf(gettext("opening /dev/%s\n"), PPP_TUN_NAME);
+ if ((muxfd = open("/dev/" PPP_TUN_NAME, O_RDWR)) < 0) {
+ perror("/dev/" PPP_TUN_NAME);
+ return (1);
+ }
+
+ /* Get lower stream information; including multiplex ID. */
+ if (verbose)
+ (void) printf(gettext("getting info from driver\n"));
+ (void) strncpy(pti.pti_name, ifname, sizeof (pti.pti_name));
+ if (strioctl(muxfd, PPPTUN_GINFO, &pti, sizeof (pti),
+ sizeof (pti), "PPPTUN_GINFO") < 0)
+ return (1);
+ if (verbose)
+ (void) printf(gettext("got muxid %d from driver\n"),
+ pti.pti_muxid);
+
+ /* Unlink lower stream from driver. */
+ if (verbose)
+ (void) printf(gettext("doing I_PUNLINK\n"));
+ if (ioctl(muxfd, I_PUNLINK, pti.pti_muxid) < 0) {
+ perror("I_PUNLINK");
+ return (1);
+ }
+ if (verbose)
+ (void) printf(gettext("done!\n"));
+
+ return (0);
+}
+
+/*
+ * Handle user request to list lower streams plumbed under the sppptun
+ * driver.
+ */
+/*ARGSUSED*/
+static int
+query_interfaces(int argc, char **argv)
+{
+ int muxfd, i;
+ union ppptun_name ptn;
+
+ /* No other arguments permitted. */
+ if (optind != argc)
+ usage();
+
+ /* Open the tunnel driver. */
+ if (verbose)
+ (void) printf(gettext("opening /dev/%s\n"), PPP_TUN_NAME);
+ if ((muxfd = open("/dev/" PPP_TUN_NAME, O_RDWR)) < 0) {
+ perror("/dev/" PPP_TUN_NAME);
+ return (1);
+ }
+
+ /* Read and print names of lower streams. */
+ for (i = 0; ; i++) {
+ ptn.ptn_index = i;
+ if (strioctl(muxfd, PPPTUN_GNNAME, &ptn, sizeof (ptn),
+ sizeof (ptn), "PPPTUN_GNNAME") < 0) {
+ perror("PPPTUN_GNNAME");
+ break;
+ }
+ /* Stop when we index off the end of the list. */
+ if (ptn.ptn_name[0] == '\0')
+ break;
+ (void) puts(ptn.ptn_name);
+ }
+ return (0);
+}
+
+/*
+ * Invoked by SIGALRM -- timer prevents problems in driver from
+ * hanging the utility.
+ */
+/*ARGSUSED*/
+static void
+toolong(int dummy)
+{
+ (void) fprintf(stderr, gettext("%s: time-out in driver\n"), myname);
+ exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+ int opt, errflag = 0;
+ char *arg;
+
+ myname = *argv;
+
+
+ (void) setlocale(LC_ALL, "");
+
+#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
+#define TEXT_DOMAIN "SYS_TEST"
+#endif
+ (void) textdomain(TEXT_DOMAIN);
+
+ /* Parse command line flags */
+ while ((opt = getopt(argc, argv, "v")) != EOF)
+ switch (opt) {
+ case 'v':
+ verbose++;
+ break;
+ default:
+ errflag++;
+ break;
+ }
+ if (errflag != 0 || optind >= argc)
+ usage();
+
+ /* Set alarm to avoid stalling on any driver errors. */
+ (void) signal(SIGALRM, toolong);
+ (void) alarm(2);
+
+ /* Switch out based on user-requested function. */
+ arg = argv[optind++];
+ if (strcmp(arg, "plumb") == 0)
+ return (plumb_it(argc, argv));
+ if (strcmp(arg, "unplumb") == 0)
+ return (unplumb_it(argc, argv));
+ if (strcmp(arg, "query") == 0)
+ return (query_interfaces(argc, argv));
+
+ usage();
+ return (1);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/syncinit.c b/usr/src/cmd/cmd-inet/usr.sbin/syncinit.c
new file mode 100644
index 0000000000..d93bd2a531
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/syncinit.c
@@ -0,0 +1,308 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Initialize and re-initialize synchronous serial clocking and loopback
+ * options. Interfaces through the S_IOCGETMODE and S_IOCSETMODE ioctls.
+ */
+
+#include <sys/types.h>
+#include <ctype.h>
+#include <sys/ioctl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/stream.h>
+#include <sys/stropts.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/ser_sync.h>
+#include <libdlpi.h>
+
+static void usage(void);
+static int prefix(char *arg, char *pref);
+static int lookup(char **table, char *arg);
+
+static char *yesno[] = {
+ "no",
+ "yes",
+ "silent",
+ 0,
+};
+
+static char *txnames[] = {
+ "txc",
+ "rxc",
+ "baud",
+ "pll",
+ "sysclk",
+ "-txc",
+ 0,
+};
+
+static char *rxnames[] = {
+ "rxc",
+ "txc",
+ "baud",
+ "pll",
+ "sysclk",
+ "-rxc",
+ 0,
+};
+
+#ifdef notdef
+static char *txdnames[] = {
+ "txd",
+ " ", /* dummy entry, do not remove */
+ "-txd",
+ 0,
+};
+
+static char *rxdnames[] = {
+ "rxd",
+ "-rxd",
+ 0,
+};
+
+static char *portab[] = {
+ "rs422",
+ "v35",
+ 0,
+};
+#endif
+
+#define equal(a, b) (strcmp((a), (b)) == 0)
+#define MAXWAIT 15
+
+int
+main(int argc, char **argv)
+{
+ char cnambuf[MAXPATHLEN];
+ struct scc_mode sm;
+ struct strioctl sioc;
+ int fd, speed;
+ char *arg, *cp;
+ char loopchange = 0;
+ char echochange = 0;
+ char clockchange = 0;
+ char *devstr = "/dev/";
+ int devstrlen;
+ ulong_t ppa;
+
+ if (argc == 1) {
+ usage();
+ exit(1);
+ }
+ argc--;
+ argv++;
+ devstrlen = strlen(devstr);
+ if (strncmp(devstr, argv[0], devstrlen) != 0) {
+ if (snprintf(cnambuf, sizeof (cnambuf), "%s%s", devstr,
+ argv[0]) >= sizeof (cnambuf)) {
+ (void) fprintf(stderr,
+ "syncinit: invalid device name (too long) %s\n",
+ argv[0]);
+ exit(1);
+ }
+ }
+ cp = cnambuf;
+ while (*cp) /* find the end of the name */
+ cp++;
+ cp--;
+ if (!isdigit(*cp)) {
+ (void) fprintf(stderr,
+ "syncinit: %s missing minor device number\n", argv[0]);
+ exit(1);
+ }
+ while (isdigit(*(cp - 1)))
+ cp--;
+ ppa = strtoul(cp, NULL, 10);
+ *cp = '\0'; /* drop number, leaving name of clone device. */
+ fd = open(cnambuf, O_RDWR|O_EXCL, 0);
+ if (fd < 0) {
+ perror("syncinit: open");
+ exit(1);
+ }
+
+ if (dlpi_attach(fd, MAXWAIT, ppa) != 0) {
+ perror("syncinit: dlpi_attach");
+ exit(1);
+ }
+
+ (void) printf("device: %s ppa: %d\n", cnambuf, (int)ppa);
+
+ argc--;
+ argv++;
+ if (argc) { /* setting things */
+ sioc.ic_cmd = S_IOCGETMODE;
+ sioc.ic_timout = -1;
+ sioc.ic_len = sizeof (struct scc_mode);
+ sioc.ic_dp = (char *)&sm;
+ if (ioctl(fd, I_STR, &sioc) < 0) {
+ perror("S_IOCGETMODE");
+ (void) fprintf(stderr,
+ "syncinit: can't get sync mode info for %s\n",
+ cnambuf);
+ exit(1);
+ }
+ while (argc-- > 0) {
+ arg = *argv++;
+ if (sscanf(arg, "%d", &speed) == 1)
+ sm.sm_baudrate = speed;
+ else if (strchr(arg, '=')) {
+ if (prefix(arg, "loop")) {
+ if (lookup(yesno, arg))
+ sm.sm_config |= CONN_LPBK;
+ else
+ sm.sm_config &= ~CONN_LPBK;
+ loopchange++;
+ } else if (prefix(arg, "echo")) {
+ if (lookup(yesno, arg))
+ sm.sm_config |= CONN_ECHO;
+ else
+ sm.sm_config &= ~CONN_ECHO;
+ echochange++;
+ } else if (prefix(arg, "nrzi")) {
+ if (lookup(yesno, arg))
+ sm.sm_config |= CONN_NRZI;
+ else
+ sm.sm_config &= ~CONN_NRZI;
+ } else if (prefix(arg, "txc")) {
+ sm.sm_txclock = lookup(txnames, arg);
+ clockchange++;
+ } else if (prefix(arg, "rxc")) {
+ sm.sm_rxclock = lookup(rxnames, arg);
+ clockchange++;
+ } else if (prefix(arg, "speed")) {
+ arg = strchr(arg, '=') + 1;
+ if (sscanf(arg, "%d", &speed) == 1) {
+ sm.sm_baudrate = speed;
+ } else
+ (void) fprintf(stderr,
+ "syncinit: %s %s\n",
+ "bad speed:", arg);
+ }
+ } else if (equal(arg, "external")) {
+ sm.sm_txclock = TXC_IS_TXC;
+ sm.sm_rxclock = RXC_IS_RXC;
+ sm.sm_config &= ~CONN_LPBK;
+ } else if (equal(arg, "sender")) {
+ sm.sm_txclock = TXC_IS_BAUD;
+ sm.sm_rxclock = RXC_IS_RXC;
+ sm.sm_config &= ~CONN_LPBK;
+ } else if (equal(arg, "internal")) {
+ sm.sm_txclock = TXC_IS_PLL;
+ sm.sm_rxclock = RXC_IS_PLL;
+ sm.sm_config &= ~CONN_LPBK;
+ } else if (equal(arg, "stop")) {
+ sm.sm_baudrate = 0;
+ } else
+ (void) fprintf(stderr, "Bad arg: %s\n", arg);
+ }
+
+ /*
+ * If we're going to change the state of loopback, and we
+ * don't have our own plans for clock sources, use defaults.
+ */
+ if (loopchange && !clockchange) {
+ if (sm.sm_config & CONN_LPBK) {
+ sm.sm_txclock = TXC_IS_BAUD;
+ sm.sm_rxclock = RXC_IS_BAUD;
+ } else {
+ sm.sm_txclock = TXC_IS_TXC;
+ sm.sm_rxclock = RXC_IS_RXC;
+ }
+ }
+ sioc.ic_cmd = S_IOCSETMODE;
+ sioc.ic_timout = -1;
+ sioc.ic_len = sizeof (struct scc_mode);
+ sioc.ic_dp = (char *)&sm;
+ if (ioctl(fd, I_STR, &sioc) < 0) {
+ perror("S_IOCSETMODE");
+ (void) ioctl(fd, S_IOCGETMODE, &sm);
+ (void) fprintf(stderr,
+ "syncinit: ioctl failure code = %x\n",
+ sm.sm_retval);
+ exit(1);
+ }
+ }
+
+ /* Report State */
+ sioc.ic_cmd = S_IOCGETMODE;
+ sioc.ic_timout = -1;
+ sioc.ic_len = sizeof (struct scc_mode);
+ sioc.ic_dp = (char *)&sm;
+ if (ioctl(fd, I_STR, &sioc) < 0) {
+ perror("S_IOCGETMODE");
+ (void) fprintf(stderr,
+ "syncinit: can't get sync mode info for %s\n",
+ cnambuf);
+ exit(1);
+ }
+ (void) printf(
+"speed=%d, loopback=%s, echo=%s, nrzi=%s, txc=%s, rxc=%s\n",
+ sm.sm_baudrate,
+ yesno[((int)(sm.sm_config & CONN_LPBK) > 0)],
+ yesno[((int)(sm.sm_config & CONN_ECHO) > 0)],
+ yesno[((int)(sm.sm_config & CONN_NRZI) > 0)],
+ txnames[sm.sm_txclock],
+ rxnames[sm.sm_rxclock]);
+ return (0);
+}
+
+static void
+usage()
+{
+ (void) fprintf(stderr, "Usage: syncinit cnambuf \\\n");
+ (void) fprintf(stderr, "\t[baudrate] [loopback=[yes|no]] ");
+ (void) fprintf(stderr, "[echo=[yes|no]] [nrzi=[yes|no]] \\\n");
+ (void) fprintf(stderr, "\t[txc=[txc|rxc|baud|pll]] \\\n");
+ (void) fprintf(stderr, "\t[rxc=[rxc|txc|baud|pll]]\n");
+ exit(1);
+}
+
+static int
+prefix(char *arg, char *pref)
+{
+ return (strncmp(arg, pref, strlen(pref)) == 0);
+}
+
+static int
+lookup(char **table, char *arg)
+{
+ char *val = strchr(arg, '=') + 1;
+ int ival;
+
+ for (ival = 0; *table != 0; ival++, table++)
+ if (equal(*table, val))
+ return (ival);
+ (void) fprintf(stderr, "syncinit: bad arg: %s\n", arg);
+ exit(1);
+ /* NOTREACHED */
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/syncloop.c b/usr/src/cmd/cmd-inet/usr.sbin/syncloop.c
new file mode 100644
index 0000000000..59865b0ecd
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/syncloop.c
@@ -0,0 +1,555 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Synchronous loop-back test program
+ * For installation verification of synchronous lines and facilities
+ */
+
+#include <sys/types.h>
+#include <ctype.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <sys/file.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/stream.h>
+#include <sys/stropts.h>
+#include <sys/poll.h>
+#include <sys/ser_sync.h>
+#include <libdlpi.h>
+
+static void Usage(void);
+static void quiet_period(void);
+static void first_packet();
+static void many_packets();
+static void printhex(char *cp, int len);
+
+static char *portname = NULL;
+static unsigned int speed = 9600;
+static int reccount = 100;
+static int reclen = 100;
+static char loopstr[MAX_INPUT];
+static int looptype = 0;
+static int loopchange = 0;
+static int clockchange = 0;
+static int cfd, dfd; /* control and data descriptors */
+static int data = -1;
+static int verbose = 0;
+
+static char *yesno[] = {
+ "no",
+ "yes",
+ "silent",
+ 0,
+};
+
+static char *txnames[] = {
+ "txc",
+ "rxc",
+ "baud",
+ "pll",
+ "sysclk",
+ "-txc",
+ 0,
+};
+
+static char *rxnames[] = {
+ "rxc",
+ "txc",
+ "baud",
+ "pll",
+ "sysclk",
+ "-rxc",
+ 0,
+};
+
+#define MAXPACKET 4096
+#define MAXWAIT 15
+
+int
+main(int argc, char **argv)
+{
+ char cnambuf[MAXPATHLEN], dnambuf[MAXPATHLEN], *cp, *cpp;
+ struct scc_mode sm;
+ struct strioctl sioc;
+ ulong_t ppa;
+ char *devstr = "/dev/";
+ int devstrlen;
+
+ argc--;
+ argv++;
+ while (argc > 0 && argv[0][0] == '-')
+ switch (argv[0][1]) {
+ case 'c': /* rec count */
+ if (argc < 2)
+ Usage();
+ reccount = atoi(argv[1]);
+ argc -= 2;
+ argv += 2;
+ break;
+ case 'd':
+ if (sscanf(argv[1], "%x", (uint_t *)&data) != 1)
+ Usage();
+ argc -= 2;
+ argv += 2;
+ break;
+ case 'l': /* rec length */
+ if (argc < 2)
+ Usage();
+ reclen = atoi(argv[1]);
+ argc -= 2;
+ argv += 2;
+ break;
+ case 's': /* line speed */
+ if (argc < 2)
+ Usage();
+ speed = atoi(argv[1]);
+ argc -= 2;
+ argv += 2;
+ break;
+ case 't': /* test type */
+ if (argc < 2)
+ Usage();
+ looptype = atoi(argv[1]);
+ argc -= 2;
+ argv += 2;
+ break;
+ case 'v':
+ verbose = 1;
+ argc--;
+ argv++;
+ break;
+ }
+ if (argc != 1)
+ Usage();
+ portname = argv[0];
+
+ devstrlen = strlen(devstr);
+ if (strncmp(devstr, portname, devstrlen) != 0) {
+ if (snprintf(dnambuf, sizeof (dnambuf), "%s%s", devstr,
+ portname) >= sizeof (dnambuf)) {
+ (void) fprintf(stderr,
+ "syncloop: invalid device name (too long) %s\n",
+ portname);
+ exit(1);
+ }
+ }
+
+ dfd = open(dnambuf, O_RDWR);
+ if (dfd < 0) {
+ (void) fprintf(stderr, "syncloop: cannot open %s\n", dnambuf);
+ perror(dnambuf);
+ exit(1);
+ }
+ for (cp = portname; (*cp) && (!isdigit(*cp)); cp++) {}
+ ppa = strtoul(cp, &cpp, 10);
+ if (cpp == cp) {
+ (void) fprintf(stderr,
+ "syncloop: %s missing minor device number\n", portname);
+ exit(1);
+ }
+ *cp = '\0'; /* drop number, leaving name of clone device. */
+ /* the following won't fail since cnambuf and dnambuf are same size */
+ if (strncmp(devstr, portname, devstrlen) != 0) {
+ (void) snprintf(cnambuf, sizeof (cnambuf), "%s%s", devstr,
+ portname);
+ }
+ cfd = open(cnambuf, O_RDWR);
+ if (cfd < 0) {
+ (void) fprintf(stderr, "syncloop: cannot open %s\n", cnambuf);
+ perror(cnambuf);
+ exit(1);
+ }
+
+ if (dlpi_attach(cfd, MAXWAIT, ppa) != 0) {
+ perror("syncloop: dlpi_attach");
+ exit(1);
+ }
+
+ if (reclen < 0 || reclen > MAXPACKET) {
+ (void) printf("invalid packet length: %d\n", reclen);
+ exit(1);
+ }
+ (void) printf("[ Data device: %s | Control device: %s, ppa=%ld ]\n",
+ dnambuf, cnambuf, ppa);
+
+ sioc.ic_cmd = S_IOCGETMODE;
+ sioc.ic_timout = -1;
+ sioc.ic_len = sizeof (struct scc_mode);
+ sioc.ic_dp = (char *)&sm;
+ if (ioctl(cfd, I_STR, &sioc) < 0) {
+ perror("S_IOCGETMODE");
+ (void) fprintf(stderr,
+ "syncloop: can't get sync mode info for %s\n", cnambuf);
+ exit(1);
+ }
+ while (looptype < 1 || looptype > 4) {
+ (void) printf("Enter test type:\n");
+ (void) printf("1: Internal Test\n");
+ (void) printf(
+" (internal data loop, internal clocking)\n");
+ (void) printf("2: Test using loopback plugs\n");
+ (void) printf(
+" (external data loop, internal clocking)\n");
+ (void) printf("3: Test using local or remote modem loopback\n");
+ (void) printf(
+" (external data loop, external clocking)\n");
+ (void) printf("4: Other, previously set, special mode\n");
+ (void) printf("> "); (void) fflush(stdout);
+ (void) fgets(loopstr, sizeof (loopstr), stdin);
+ (void) sscanf(loopstr, "%d", &looptype);
+ }
+ switch (looptype) {
+ case 1:
+ if ((sm.sm_txclock != TXC_IS_BAUD) ||
+ (sm.sm_rxclock != RXC_IS_BAUD))
+ clockchange++;
+ sm.sm_txclock = TXC_IS_BAUD;
+ sm.sm_rxclock = RXC_IS_BAUD;
+ if ((sm.sm_config & CONN_LPBK) == 0)
+ loopchange++;
+ sm.sm_config |= CONN_LPBK;
+ break;
+ case 2:
+ if ((sm.sm_txclock != TXC_IS_BAUD) ||
+ (sm.sm_rxclock != RXC_IS_RXC))
+ clockchange++;
+ sm.sm_txclock = TXC_IS_BAUD;
+ sm.sm_rxclock = RXC_IS_RXC;
+ if ((sm.sm_config & CONN_LPBK) != 0)
+ loopchange++;
+ sm.sm_config &= ~CONN_LPBK;
+ break;
+ case 3:
+ if ((sm.sm_txclock != TXC_IS_TXC) ||
+ (sm.sm_rxclock != RXC_IS_RXC))
+ clockchange++;
+ sm.sm_txclock = TXC_IS_TXC;
+ sm.sm_rxclock = RXC_IS_RXC;
+ if ((sm.sm_config & CONN_LPBK) != 0)
+ loopchange++;
+ sm.sm_config &= ~CONN_LPBK;
+ break;
+ case 4:
+ goto no_params;
+ }
+
+ sm.sm_baudrate = speed;
+
+ sioc.ic_cmd = S_IOCSETMODE;
+ sioc.ic_timout = -1;
+ sioc.ic_len = sizeof (struct scc_mode);
+ sioc.ic_dp = (char *)&sm;
+ if (ioctl(cfd, I_STR, &sioc) < 0) {
+ perror("S_IOCSETMODE");
+ (void) fprintf(stderr,
+ "syncloop: can't set sync mode info for %s\n", cnambuf);
+ exit(1);
+ }
+
+no_params:
+ /* report state */
+ sioc.ic_cmd = S_IOCGETMODE;
+ sioc.ic_timout = -1;
+ sioc.ic_len = sizeof (struct scc_mode);
+ sioc.ic_dp = (char *)&sm;
+ if (ioctl(cfd, I_STR, &sioc) < 0) {
+ perror("S_IOCGETMODE");
+ (void) fprintf(stderr,
+ "syncloop: can't get sync mode info for %s\n", cnambuf);
+ exit(1);
+ }
+ (void) printf("speed=%d, loopback=%s, nrzi=%s, txc=%s, rxc=%s\n",
+ sm.sm_baudrate,
+ yesno[((int)(sm.sm_config & CONN_LPBK) > 0)],
+ yesno[((int)(sm.sm_config & CONN_NRZI) > 0)],
+ txnames[sm.sm_txclock],
+ rxnames[sm.sm_rxclock]);
+
+ quiet_period();
+ first_packet();
+ many_packets();
+ return (0);
+}
+
+static void
+Usage()
+{
+ (void) printf("Usage: syncloop [ options ] portname\n");
+ (void) printf("Options: -c packet_count\n");
+ (void) printf(" -l packet_length\n");
+ (void) printf(" -s line_speed\n");
+ (void) printf(" -t test_type\n");
+ (void) printf(" -d hex_data_byte\n");
+ exit(1);
+}
+
+static int zero_time = 0;
+static int short_time = 1000;
+static int long_time = 4000;
+static char bigbuf[4096];
+static char packet[MAXPACKET];
+static struct pollfd pfd;
+
+static void
+quiet_period()
+{
+ (void) printf("[ checking for quiet line ]\n");
+ pfd.fd = dfd;
+ pfd.events = POLLIN;
+ pfd.revents = 0;
+ while (poll(&pfd, 1, short_time) == 1) {
+ (void) read(dfd, bigbuf, sizeof (bigbuf));
+ }
+ if (poll(&pfd, 1, long_time) == 1) {
+ (void) printf("packet received but none sent!\n");
+ (void) printf("quiesce other end before starting syncloop\n");
+ exit(1);
+ }
+}
+
+static void
+first_packet()
+{
+ int i, len;
+ int pollret;
+ struct strioctl sioc;
+ struct sl_stats start_stats, end_stats;
+
+ for (i = 0; i < reclen; i++)
+ packet[i] = (data == -1) ? rand() : data;
+ (void) printf("[ Trying first packet ]\n");
+ sioc.ic_cmd = S_IOCGETSTATS;
+ sioc.ic_timout = -1;
+ sioc.ic_len = sizeof (struct sl_stats);
+ sioc.ic_dp = (char *)&start_stats;
+ if (ioctl(cfd, I_STR, &sioc) < 0) {
+ perror("S_IOCGETSTATS");
+ exit(1);
+ }
+
+ for (i = 0; i < 5; i++) {
+ if (write(dfd, packet, reclen) != reclen) {
+ (void) fprintf(stderr,
+ "packet write failed, errno %d\n",
+ errno);
+ exit(1);
+ }
+ pfd.fd = dfd;
+ pfd.events = POLLIN;
+ pollret = poll(&pfd, 1, long_time);
+ if (pollret < 0) perror("poll");
+ if (pollret == 0)
+ (void) printf("poll: nothing to read.\n");
+ if (pollret == 1) {
+ len = read(dfd, bigbuf, reclen);
+ if (len == reclen && memcmp(packet, bigbuf, len) == 0)
+ return; /* success */
+ else {
+ (void) printf("len %d should be %d\n",
+ len, reclen);
+ if (verbose) {
+ (void) printf(" ");
+ printhex(bigbuf, len);
+ (void) printf("\nshould be ");
+ printhex(packet, reclen);
+ (void) printf("\n");
+ }
+ }
+ }
+ }
+ (void) printf("Loopback has TOTALLY FAILED - ");
+ (void) printf("no packets returned after 5 attempts\n");
+ sioc.ic_cmd = S_IOCGETSTATS;
+ sioc.ic_timout = -1;
+ sioc.ic_len = sizeof (struct sl_stats);
+ sioc.ic_dp = (char *)&end_stats;
+ if (ioctl(cfd, I_STR, &sioc) < 0) {
+ perror("S_IOCGETSTATS");
+ exit(1);
+ }
+ if (start_stats.opack == end_stats.opack)
+ (void) printf(
+ "No packets transmitted - no transmit clock present\n");
+ exit(1);
+}
+
+static void
+many_packets()
+{
+ struct strioctl sioc;
+ struct sl_stats start_stats, end_stats;
+ struct timeval start_time, end_time;
+ int baddata = 0;
+ float secs, speed;
+ int i, len;
+ int incount = 0;
+ long prev_sec = -1;
+ int pollret;
+
+ (void) printf("[ Trying many packets ]\n");
+ sioc.ic_cmd = S_IOCGETSTATS;
+ sioc.ic_timout = -1;
+ sioc.ic_len = sizeof (struct sl_stats);
+ sioc.ic_dp = (char *)&start_stats;
+ if (ioctl(cfd, I_STR, &sioc) < 0) {
+ perror("S_IOCGETSTATS");
+ exit(1);
+ }
+ (void) gettimeofday(&start_time, 0);
+ end_time = start_time;
+
+ i = 0;
+ while (i < reccount) {
+ if (end_time.tv_sec != prev_sec) {
+ prev_sec = end_time.tv_sec;
+ (void) printf("\r %d ", incount);
+ (void) fflush(stdout);
+ }
+ pfd.fd = dfd;
+ pfd.events = POLLIN;
+ while (pollret = poll(&pfd, 1, zero_time)) {
+ if (pollret < 0)
+ perror("poll");
+ else {
+ (void) lseek(dfd, (long)0, 0);
+ len = read(dfd, bigbuf, reclen);
+ if (len != reclen ||
+ memcmp(packet, bigbuf, len) != 0) {
+ (void) printf("len %d should be %d\n",
+ len, reclen);
+ if (verbose) {
+ (void) printf(" ");
+ printhex(bigbuf, len);
+ (void) printf("\nshould be ");
+ printhex(packet, reclen);
+ (void) printf("\n");
+ }
+ baddata++;
+ }
+ incount++;
+ (void) gettimeofday(&end_time, 0);
+ }
+ }
+ pfd.fd = dfd;
+ pfd.events = POLLIN|POLLOUT;
+ pollret = poll(&pfd, 1, long_time);
+ if (pollret < 0)
+ perror("poll");
+ if (pollret == 0)
+ (void) printf("poll: nothing to read or write.\n");
+ if (pollret == 1) {
+ if (pfd.revents & POLLOUT) {
+ (void) write(dfd, packet, reclen);
+ i++;
+ } else if (!(pfd.revents & POLLIN)) {
+ (void) printf("OUTPUT HAS LOCKED UP!!!\n");
+ break;
+ }
+ }
+ }
+ pfd.fd = dfd;
+ pfd.events = POLLIN;
+ while ((incount < reccount) && (poll(&pfd, 1, long_time) == 1)) {
+ if (end_time.tv_sec != prev_sec) {
+ prev_sec = end_time.tv_sec;
+ (void) printf("\r %d ", incount);
+ (void) fflush(stdout);
+ }
+ len = read(dfd, bigbuf, reclen);
+ if (len != reclen || memcmp(packet, bigbuf, len) != 0) {
+ (void) printf("len %d should be %d\n", len, reclen);
+ if (verbose) {
+ (void) printf(" ");
+ printhex(bigbuf, len);
+ (void) printf("\nshould be ");
+ printhex(packet, reclen);
+ (void) printf("\n");
+ }
+ baddata++;
+ }
+ incount++;
+ (void) gettimeofday(&end_time, 0);
+ }
+ (void) printf("\r %d \n", incount);
+ if (baddata)
+ (void) printf("%d packets with wrong data received!\n",
+ baddata);
+ sioc.ic_cmd = S_IOCGETSTATS;
+ sioc.ic_timout = -1;
+ sioc.ic_len = sizeof (struct sl_stats);
+ sioc.ic_dp = (char *)&end_stats;
+ if (ioctl(cfd, I_STR, &sioc) < 0) {
+ perror("S_IOCGETSTATS");
+ exit(1);
+ }
+ end_stats.ipack -= start_stats.ipack;
+ end_stats.opack -= start_stats.opack;
+ end_stats.abort -= start_stats.abort;
+ end_stats.crc -= start_stats.crc;
+ end_stats.overrun -= start_stats.overrun;
+ end_stats.underrun -= start_stats.underrun;
+ end_stats.ierror -= start_stats.ierror;
+ end_stats.oerror -= start_stats.oerror;
+ if (reccount > end_stats.opack)
+ (void) printf("%d packets lost in outbound queueing\n",
+ reccount - end_stats.opack);
+ if (incount < end_stats.ipack && incount < reccount)
+ (void) printf("%d packets lost in inbound queueing\n",
+ end_stats.ipack - incount);
+ (void) printf("%d packets sent, %d received\n", reccount, incount);
+ (void) printf("CRC errors Aborts Overruns Underruns ");
+ (void) printf(" In <-Drops-> Out\n%9d %9d %9d %9d %12d %12d\n",
+ end_stats.crc, end_stats.abort,
+ end_stats.overrun, end_stats.underrun,
+ end_stats.ierror, end_stats.oerror);
+ secs = (float)(end_time.tv_usec - start_time.tv_usec) / 1000000.0;
+ secs += (float)(end_time.tv_sec - start_time.tv_sec);
+ if (secs) {
+ speed = 8 * incount * (4 + reclen) / secs;
+ (void) printf("estimated line speed = %d bps\n", (int)speed);
+ }
+}
+
+static void
+printhex(char *cp, int len)
+{
+ char c, *hex = "0123456789ABCDEF";
+ int i;
+
+ for (i = 0; i < len; i++) {
+ c = *cp++;
+ (void) putchar(hex[(c >> 4) & 0xF]);
+ (void) putchar(hex[c & 0xF]);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/syncstat.c b/usr/src/cmd/cmd-inet/usr.sbin/syncstat.c
new file mode 100644
index 0000000000..b939067405
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/syncstat.c
@@ -0,0 +1,256 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Display synchronous serial line statistics
+ */
+
+#include <sys/types.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stream.h>
+#include <sys/stropts.h>
+#include <fcntl.h>
+#include <sys/ser_sync.h>
+#include <libdlpi.h>
+
+#define MAXWAIT 15
+
+static struct scc_mode sm;
+static struct sl_stats st;
+
+static void usage(void);
+static void sample(int count, int period);
+
+/*
+ * errstr is MAXPATHLEN + 11; 11 = 10 for "syncstat: " + null
+ */
+static char errstr[11 + MAXPATHLEN] = "syncstat: /dev/";
+static char *ifdevice = errstr + 10;
+static char *ifname = errstr + 15;
+static int fd;
+
+int
+main(int argc, char **argv)
+{
+ int len;
+ char *cp;
+ int do_clear = 0;
+ int period = 0;
+ int isize, osize;
+ int count;
+ struct strioctl sioc;
+ ulong_t ppa;
+
+ if (argc == 1) {
+ usage();
+ exit(1);
+ }
+ argc--; /* skip the command name */
+ argv++;
+
+ /*
+ * The following loop processes command line arguments.
+ * If the argument begins with a '-', it is trated as an option.
+ * The only option currently implemented is "-c" (clears statistics).
+ * If the argument begins with a numeral, it is treated as an interval.
+ * Intervals must be positive integers greater than zero.
+ * Any argument that survives this is treated as a device name to be
+ * found under /dev.
+ */
+ while (argc > 0) {
+ if (argv[0][0] == '-') {
+ if (argc == 1) {
+ usage();
+ exit(1);
+ }
+ if (argv[0][1] != 'c') {
+ usage();
+ exit(1);
+ }
+ do_clear = 1;
+ } else if ((argv[0][0] >= '0') && (argv[0][0] <= '9')) {
+ period = atoi(*argv);
+ if (period == 0) {
+ (void) fprintf(stderr,
+ "syncstat: bad interval: %s\n", *argv);
+ exit(1);
+ }
+ } else {
+ len = sizeof (errstr) - strlen(errstr);
+ if (snprintf(ifname, len, "%s", *argv) >= len) {
+ (void) fprintf(stderr,
+ "syncstat: invalid device name "
+ "(too long) %s\n", *argv);
+ exit(1);
+ }
+ }
+ argc--;
+ argv++;
+ }
+
+ for (cp = ifname; (*cp) && (!isdigit(*cp)); cp++) {}
+ if (*cp == '\0') { /* hit the end without finding a number */
+ (void) fprintf(stderr,
+ "syncstat: %s missing minor device number\n", ifname);
+ exit(1);
+ }
+ ppa = strtoul(cp, NULL, 10);
+ *cp = '\0'; /* drop number, leaving name of clone device. */
+ fd = open(ifdevice, O_RDWR);
+ if (fd < 0) {
+ perror(errstr);
+ exit(1);
+ }
+
+ if (dlpi_attach(fd, MAXWAIT, ppa) != 0) {
+ perror("syncstat: dlpi_attach");
+ exit(1);
+ }
+
+ (void) printf("syncstat: control device: %s, ppa=%ld\n", ifdevice, ppa);
+
+ sioc.ic_cmd = S_IOCGETMODE;
+ sioc.ic_timout = -1;
+ sioc.ic_len = sizeof (struct scc_mode);
+ sioc.ic_dp = (char *)&sm;
+ if (ioctl(fd, I_STR, &sioc) < 0) {
+ perror("S_IOCGETMODE");
+ (void) fprintf(stderr,
+ "syncstat: can't get sync mode info for %s\n",
+ ifname);
+ exit(1);
+ }
+ if (do_clear) {
+ sioc.ic_cmd = S_IOCCLRSTATS;
+ sioc.ic_timout = -1;
+ sioc.ic_len = sizeof (struct sl_stats);
+ sioc.ic_dp = (char *)&st;
+ if (ioctl(fd, I_STR, &sioc) < 0) {
+ perror("S_IOCCLRSTATS");
+ (void) fprintf(stderr,
+ "syncstat: can't clear stats for %s\n",
+ ifname);
+ exit(1);
+ }
+ }
+
+ sioc.ic_cmd = S_IOCGETSTATS;
+ sioc.ic_timout = -1;
+ sioc.ic_len = sizeof (struct sl_stats);
+ sioc.ic_dp = (char *)&st;
+ if (ioctl(fd, I_STR, &sioc) < 0) {
+ perror("S_IOCGETSTATS");
+ (void) fprintf(stderr, "syncstat: can't get stats for %s\n",
+ ifname);
+ exit(1);
+ }
+ if (period) {
+ if (sm.sm_baudrate == 0) {
+ (void) fprintf(stderr, "syncstat: baud rate not set\n");
+ exit(1);
+ }
+ for (count = 0; ; count++) {
+ (void) fflush(stdout);
+ (void) sleep(period);
+ sample(count, period);
+ }
+ }
+ isize = osize = 0;
+ if (st.opack)
+ osize = st.ochar / st.opack;
+ if (st.ipack)
+ isize = st.ichar / st.ipack;
+ (void) printf(
+" speed ipkts opkts undrun ovrrun abort crc isize osize\n");
+ (void) printf(" %7d %7d %7d %7d %7d %7d %7d %7d %7d\n",
+ sm.sm_baudrate,
+ st.ipack,
+ st.opack,
+ st.underrun,
+ st.overrun,
+ st.abort,
+ st.crc,
+ isize, osize);
+ return (0);
+}
+
+static void
+sample(int count, int period)
+{
+ struct sl_stats nst;
+ struct strioctl sioc;
+ int iutil, outil;
+
+ sioc.ic_cmd = S_IOCGETSTATS;
+ sioc.ic_timout = -1;
+ sioc.ic_len = sizeof (struct sl_stats);
+ sioc.ic_dp = (char *)&nst;
+ if (ioctl(fd, I_STR, &sioc) < 0) {
+ perror("S_IOCGETSTATS");
+ (void) fprintf(stderr, "syncstat: can't get stats for %s\n",
+ ifname);
+ exit(1);
+ }
+
+ st.ipack = nst.ipack - st.ipack;
+ st.opack = nst.opack - st.opack;
+ st.ichar = nst.ichar - st.ichar;
+ st.ochar = nst.ochar - st.ochar;
+ st.crc = nst.crc - st.crc;
+ st.overrun = nst.overrun - st.overrun;
+ st.underrun = nst.underrun - st.underrun;
+ st.abort = nst.abort - st.abort;
+ iutil = 8 * st.ichar / period;
+ iutil = 100 * iutil / sm.sm_baudrate;
+ outil = 8 * st.ochar / period;
+ outil = 100 * outil / sm.sm_baudrate;
+ if ((count % 20) == 0) (void) printf(
+" ipkts opkts undrun ovrrun abort crc iutil outil\n");
+ (void) printf(" %7d %7d %7d %7d %7d %7d %6d%% %6d%%\n",
+ st.ipack,
+ st.opack,
+ st.underrun,
+ st.overrun,
+ st.abort,
+ st.crc,
+ iutil, outil);
+
+ st = nst;
+}
+
+static void
+usage()
+{
+ (void) fprintf(stderr, "%s\n",
+ "Usage: syncstat [-c] device [period]");
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/telnet.xml b/usr/src/cmd/cmd-inet/usr.sbin/telnet.xml
new file mode 100644
index 0000000000..5ad1201d43
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/telnet.xml
@@ -0,0 +1,96 @@
+<?xml version="1.0"?>
+<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
+<!--
+ Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ ident "%Z%%M% %I% %E% SMI"
+
+ NOTE: This service manifest is not editable; its contents will
+ be overwritten by package or patch operations, including
+ operating system upgrade. Make customizations in a different
+ file.
+
+ Service manifest for the in.telnetd service.
+-->
+
+<service_bundle type='manifest' name='SUNWtnetr:telnet'>
+
+<service
+ name='network/telnet'
+ type='service'
+ version='1'>
+
+ <create_default_instance enabled='false' />
+
+ <restarter>
+ <service_fmri value='svc:/network/inetd:default' />
+ </restarter>
+
+ <exec_method
+ type='method'
+ name='inetd_start'
+ exec='/usr/sbin/in.telnetd'
+ timeout_seconds='0'>
+ <method_context>
+ <method_credential user='root' group='root' />
+ </method_context>
+ </exec_method>
+
+ <exec_method
+ type='method'
+ name='inetd_disable'
+ exec=':kill'
+ timeout_seconds='0'>
+ </exec_method>
+
+ <property_group name='inetd' type='framework'>
+ <stability value='Evolving' />
+ <propval name='name' type='astring' value='telnet' />
+ <propval name='endpoint_type' type='astring' value='stream' />
+ <propval name='proto' type='astring' value='tcp6' />
+ <propval name='wait' type='boolean' value='false' />
+ <propval name='isrpc' type='boolean' value='false' />
+ </property_group>
+
+ <stability value='Unstable' />
+
+ <template>
+ <common_name>
+ <loctext xml:lang='C'> Telnet server </loctext>
+ </common_name>
+ <description>
+ <loctext xml:lang='C'>
+in.telnetd supports the standard TELNET virtual terminal protocol.
+ </loctext>
+ </description>
+ <documentation>
+ <manpage title='in.telnetd' section='1M'
+ manpath='/usr/share/man' />
+ <manpage title='telnetd' section='1M'
+ manpath='/usr/share/man' />
+ </documentation>
+ </template>
+</service>
+
+</service_bundle>
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/telnetd.dfl b/usr/src/cmd/cmd-inet/usr.sbin/telnetd.dfl
new file mode 100644
index 0000000000..a712bd90fd
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/telnetd.dfl
@@ -0,0 +1,39 @@
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# /etc/default/telnetd
+#
+# telnetd default settings processed via telnetd(1M).
+#
+# BANNER defines the connection banner which is displayed before the
+# telnet login prompt, see telnetd(1M) for details. The following
+# commented line shows the default value.
+#
+#BANNER="\\r\\n\\r\\n`uname -s` `uname -r`\\r\\n\\r\\n"
+#
+#
+# Suppress the telnet banner by supplying a null definition.
+#
+BANNER=""
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/tname.xml b/usr/src/cmd/cmd-inet/usr.sbin/tname.xml
new file mode 100644
index 0000000000..6fb81def92
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/tname.xml
@@ -0,0 +1,95 @@
+<?xml version="1.0"?>
+<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
+<!--
+ Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (the "License"). You may not use this file except in compliance
+ with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ ident "%Z%%M% %I% %E% SMI"
+
+ NOTE: This service manifest is not editable; its contents will
+ be overwritten by package or patch operations, including
+ operating system upgrade. Make customizations in a different
+ file.
+-->
+
+<service_bundle type='manifest' name='SUNWtnamr:tnamed'>
+
+<service
+ name='network/tname'
+ type='service'
+ version='1'>
+
+ <create_default_instance enabled='false' />
+
+ <restarter>
+ <service_fmri value='svc:/network/inetd:default' />
+ </restarter>
+
+ <exec_method
+ type='method'
+ name='inetd_start'
+ exec='/usr/sbin/in.tnamed'
+ timeout_seconds='0'>
+ <method_context>
+ <method_credential user='root' group='root' />
+ </method_context>
+ </exec_method>
+
+ <exec_method
+ type='method'
+ name='inetd_offline'
+ exec=':kill_process'
+ timeout_seconds='0'>
+ </exec_method>
+
+ <exec_method
+ type='method'
+ name='inetd_disable'
+ exec=':kill'
+ timeout_seconds='0'>
+ </exec_method>
+
+ <property_group name='inetd' type='framework'>
+ <propval name='name' type='astring' value='name' />
+ <propval name='endpoint_type' type='astring' value='dgram' />
+ <propval name='wait' type='boolean' value='true' />
+ <propval name='isrpc' type='boolean' value='false' />
+ <propval name='proto' type='astring' value='udp' />
+ </property_group>
+
+ <stability value='Unstable' />
+
+ <template>
+ <common_name>
+ <loctext xml:lang='C'>
+ trivial name server
+ </loctext>
+ </common_name>
+ <documentation>
+ <manpage title='in.tnamed' section='1M'
+ manpath='/usr/share/man' />
+ </documentation>
+ </template>
+</service>
+
+</service_bundle>
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/traceroute/Makefile b/usr/src/cmd/cmd-inet/usr.sbin/traceroute/Makefile
new file mode 100644
index 0000000000..a8f2d2e491
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/traceroute/Makefile
@@ -0,0 +1,90 @@
+#
+# Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+# Copyright (c) 1988, 1989, 1990, 1991, 1992, 1995, 1996, 1997
+# The Regents of the University of California. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that: (1) source code distributions
+# retain the above copyright notice and this paragraph in its entirety, (2)
+# distributions including binary code include the above copyright notice and
+# this paragraph in its entirety in the documentation or other materials
+# provided with the distribution, and (3) all advertising materials mentioning
+# features or use of this software display the following acknowledgement:
+# ``This product includes software developed by the University of California,
+# Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+# the University nor the names of its contributors may be used to endorse
+# or promote products derived from this software without specific prior
+# written permission.
+# THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+#
+# @(#) $Header: Makefile.in,v 1.24 97/04/22 13:31:20 leres Exp $ (LBL)
+
+PROG= traceroute
+TRACEROUTEOBJS= traceroute.o traceroute_aux.o traceroute_aux6.o
+COMMONOBJS= ifaddrlist.o
+OBJS= $(TRACEROUTEOBJS) $(COMMONOBJS)
+SUIDPROG= traceroute
+
+include ../../../Makefile.cmd
+include ../../Makefile.cmd-inet
+
+TRACEROUTESRCS= $(TRACEROUTEOBJS:.o=.c)
+COMMONSRCS= $(CMDINETCOMMONDIR)/$(COMMONOBJS:.o=.c)
+SRCS= $(TRACEROUTESRCS) $(COMMONSRCS)
+HDRS= traceroute.h $(CMDINETCOMMONDIR)/ifaddrlist.h
+
+$(ROOTUSRSBIN)/traceroute := FILEMODE= 04555
+$(ROOTUSRSBIN)/traceroute := OWNER= root
+
+CPPFLAGS += -I$(CMDINETCOMMONDIR)
+
+# Traceroute uses the ancillary data feature which is available only through
+# UNIX 98 standards version of Socket interface. This interface is supposed to
+# be accessed by -lxnet. In addition -lsocket and -lnsl are used to
+# capture new not-yet-standard interfaces. Someday -lxnet alone should be enough
+# when IPv6 inspired new interfaces are part of standards.
+LDLIBS += -lxnet -lsocket -lnsl
+
+# these #defines are required to use UNIX 98 interfaces
+_D_UNIX98_EXTN= -D_XOPEN_SOURCE=500 -D__EXTENSIONS__
+
+$(TRACEROUTEOBJS) := CPPFLAGS += $(_D_UNIX98_EXTN)
+
+LINTFLAGS += $(_D_UNIX98_EXTN)
+
+#
+# Setting the above defines to use the UNIX98 ancillary data feature
+# causes lint to output warnings about lint library declarations
+# conflicting with those in the header files. Since we need these
+# features, the best course of action is to switch the types of the
+# resulting warnings off when running lint.
+#
+LINTFLAGS += -erroff=E_FUNC_DECL_VAR_ARG2 -erroff=E_INCONS_ARG_DECL2 \
+ -erroff=E_INCONS_ARG_USED2 -erroff=E_INCONS_VAL_TYPE_DECL2
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+ $(LINK.c) -o $@ $(OBJS) $(LDLIBS)
+ $(POST_PROCESS)
+
+$(COMMONOBJS): $(COMMONSRCS)
+ $(COMPILE.c) $(COMMONSRCS)
+
+install: all $(ROOTUSRSBINPROG)
+
+clean:
+ $(RM) $(OBJS)
+
+lint: lint_SRCS
+
+include ../../../Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/traceroute/req.flg b/usr/src/cmd/cmd-inet/usr.sbin/traceroute/req.flg
new file mode 100644
index 0000000000..91c08ee16f
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/traceroute/req.flg
@@ -0,0 +1,8 @@
+#!/bin/sh
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+echo_file usr/src/cmd/cmd-inet/Makefile.cmd-inet
+echo_file usr/src/cmd/cmd-inet/common/Makefile
+echo_file usr/src/cmd/cmd-inet/common/ifaddrlist.c
+echo_file usr/src/cmd/cmd-inet/common/ifaddrlist.h
+
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/traceroute/traceroute.c b/usr/src/cmd/cmd-inet/usr.sbin/traceroute/traceroute.c
new file mode 100644
index 0000000000..f442a67ad0
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/traceroute/traceroute.c
@@ -0,0 +1,2184 @@
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 1988, 1989, 1991, 1994, 1995, 1996, 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ *
+ * @(#)$Header: traceroute.c,v 1.49 97/06/13 02:30:23 leres Exp $ (LBL)
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/sysmacros.h>
+
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/udp.h>
+#include <netinet/udp_var.h>
+#include <netinet/ip6.h>
+#include <netinet/icmp6.h>
+
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <malloc.h>
+#include <memory.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+#include <libintl.h>
+#include <locale.h>
+#include <signal.h>
+#include <setjmp.h>
+#include <limits.h>
+#include <zone.h>
+
+#include <priv_utils.h>
+
+
+#include <ifaddrlist.h>
+#include "traceroute.h"
+
+#define MAX_SEQ 65535 /* max sequence value for ICMP */
+#define MAX_TRAFFIC_CLASS 255 /* max traffic class for IPv6 */
+#define MAX_FLOW_LABEL 0xFFFFF /* max flow label for IPv6 */
+#define MAX_TOS 255 /* max type-of-service for IPv4 */
+#define STR_LEN 30
+
+/* store the information about a host */
+struct hostinfo {
+ char *name; /* hostname */
+ int family; /* address family of the IP addresses */
+ int num_addr; /* number of IP addresses */
+ union any_in_addr *addrs; /* list of IP addresses */
+};
+
+/* used to store a bunch of protocol specific values */
+struct pr_set {
+ int family; /* AF_INET or AF_INET6 */
+ char name[STR_LEN]; /* "IPv4" or "IPv6" */
+ char icmp[STR_LEN]; /* "icmp" or "ipv6-icmp" */
+ int icmp_minlen;
+ int addr_len;
+ int ip_hdr_len;
+ int packlen;
+ int sock_size; /* size of sockaddr_in or sockaddr_in6 */
+ struct sockaddr *to;
+ struct sockaddr *from;
+ void *from_sin_addr;
+ union any_in_addr *gwIPlist;
+ /* pointers to v4/v6 functions */
+ struct ip *(*set_buffers_fn) (int);
+ int (*check_reply_fn)(struct msghdr *, int, int, uchar_t *, uchar_t *);
+ boolean_t (*print_icmp_other_fn)(uchar_t, uchar_t);
+ void (*print_addr_fn)(uchar_t *, int, struct sockaddr *);
+
+};
+
+/*
+ * LBNL bug fixed: in LBNL traceroute 'uchar_t packet[512];'
+ * Not sufficient to hold the complete packet for ECHO REPLY of a big probe.
+ * Packet size is reported incorrectly in such a case.
+ * Also this buffer needs to be 32 bit aligned. In the future the alignment
+ * requirement will be increased to 64 bit. So, let's use 64 bit alignment now.
+ */
+static uint64_t packet[(IP_MAXPACKET + 1)/8]; /* received packet */
+
+static struct ip *outip4; /* output buffer to send as an IPv4 datagram */
+static struct ip *outip6; /* output buffer to send as an IPv6 datagram */
+
+/* Used to store the ancillary data that comes with the received packets */
+static uint64_t ancillary_data[(IP_MAXPACKET + 1)/8];
+
+/* first get the gw names, later you'll resolve them based on the family */
+static char *gwlist[MAXMAX_GWS]; /* gateway names list */
+static union any_in_addr gwIPlist[MAX_GWS]; /* gateway IPv4 address list */
+static union any_in_addr gwIP6list[MAX_GWS6]; /* gateway IPv6 address list */
+
+static int family_input = AF_UNSPEC; /* User supplied protocol family */
+static int rcvsock4; /* receive (icmp) socket file descriptor */
+static int sndsock4; /* send (udp/icmp) socket file descriptor */
+static int rcvsock6; /* receive (icmp6) socket file descriptor */
+static int sndsock6; /* send (udp6/icmp6) socket file descriptor */
+int gw_count = 0; /* number of gateways */
+static struct sockaddr_in whereto; /* Who to try to reach */
+static struct sockaddr_in6 whereto6;
+static struct sockaddr_in wherefrom; /* Who we are */
+static struct sockaddr_in6 wherefrom6;
+static int packlen_input = 0; /* user input for packlen */
+
+char *prog;
+static char *source_input = NULL; /* this is user arg. source, doesn't change */
+static char *source = NULL; /* this gets modified after name lookup */
+char *hostname;
+static char *device = NULL; /* interface name */
+static struct pr_set *pr4; /* protocol info for IPv4 */
+static struct pr_set *pr6; /* protocol info for IPv6 */
+static struct ifaddrlist *al4; /* list of interfaces */
+static struct ifaddrlist *al6; /* list of interfaces */
+static uint_t if_index = 0; /* interface index */
+static int num_v4 = 0; /* count of IPv4 addresses */
+static int num_v6 = 0; /* count of IPv6 addresses */
+static int num_ifs4 = 0; /* count of local IPv4 interfaces */
+static int num_ifs6 = 0; /* count of local IPv6 interfaces */
+
+static int nprobes = 3; /* number of probes */
+static int max_ttl = 30; /* max number of hops */
+static int first_ttl = 1; /* initial number of hops */
+ushort_t ident; /* used to authenticate replies */
+ushort_t port = 32768 + 666; /* start udp dest port # for probe packets */
+
+static int options = 0; /* socket options */
+boolean_t verbose = _B_FALSE; /* verbose output */
+static int waittime = 5; /* time to wait for response (in seconds) */
+static struct timeval delay = {0, 0}; /* delay between consecutive probe */
+boolean_t nflag = _B_FALSE; /* print addresses numerically */
+static boolean_t showttl = _B_FALSE; /* print the ttl(hop limit) of recvd pkt */
+boolean_t useicmp = _B_FALSE; /* use icmp echo instead of udp packets */
+boolean_t docksum = _B_TRUE; /* calculate checksums */
+static boolean_t collect_stat = _B_FALSE; /* print statistics */
+boolean_t settos = _B_FALSE; /* set type-of-service field */
+static int max_timeout = 5; /* quit after this consecutive timeouts */
+static boolean_t probe_all = _B_FALSE; /* probe all the IFs of the target */
+static boolean_t pick_src = _B_FALSE; /* traceroute picks the src address */
+
+/*
+ * flow and class are specific to IPv6, tos and off are specific to IPv4.
+ * Each protocol uses the ones that are specific to itself, and ignores
+ * others.
+ */
+static uint_t flow = 0; /* IPv6 flow info */
+static uint_t class = 0; /* IPv6 class */
+uchar_t tos = 0; /* IPv4 type-of-service */
+ushort_t off = 0; /* set DF bit */
+
+static jmp_buf env; /* stack environment for longjmp() */
+boolean_t raw_req; /* if sndsock for IPv4 must be raw */
+
+/* Forwards */
+static uint_t calc_packetlen(int, struct pr_set *);
+extern int check_reply(struct msghdr *, int, int, uchar_t *, uchar_t *);
+extern int check_reply6(struct msghdr *, int, int, uchar_t *, uchar_t *);
+static double deltaT(struct timeval *, struct timeval *);
+static char *device_name(struct ifaddrlist *, int, union any_in_addr *,
+ struct pr_set *);
+extern void *find_ancillary_data(struct msghdr *, int, int);
+static boolean_t has_addr(struct addrinfo *, union any_in_addr *);
+static struct ifaddrlist *find_device(struct ifaddrlist *, int, char *);
+static struct ifaddrlist *find_ifaddr(struct ifaddrlist *, int,
+ union any_in_addr *, int);
+static void get_gwaddrs(char **, int, union any_in_addr *,
+ union any_in_addr *, int *, int *);
+static void get_hostinfo(char *, int, struct addrinfo **);
+char *inet_name(union any_in_addr *, int);
+ushort_t in_cksum(ushort_t *, int);
+extern int ip_hdr_length_v6(ip6_t *, int, uint8_t *);
+void main(int, char **);
+extern char *pr_type(uchar_t);
+extern char *pr_type6(uchar_t);
+extern void print_addr(uchar_t *, int, struct sockaddr *);
+extern void print_addr6(uchar_t *, int, struct sockaddr *);
+extern boolean_t print_icmp_other(uchar_t, uchar_t);
+extern boolean_t print_icmp_other6(uchar_t, uchar_t);
+static void print_stats(int, int, double, double, double, double);
+static void print_unknown_host_msg(const char *, const char *);
+static void record_stats(double, int *, double *, double *, double *, double *);
+static void resolve_nodes(int *, struct addrinfo **);
+static void select_src_addr(union any_in_addr *, union any_in_addr *, int);
+extern void send_probe(int, struct sockaddr *, struct ip *, int, int,
+ struct timeval *, int);
+extern void send_probe6(int, struct msghdr *, struct ip *, int, int,
+ struct timeval *, int);
+extern void set_ancillary_data(struct msghdr *, int, union any_in_addr *, int,
+ uint_t);
+extern struct ip *set_buffers(int);
+extern struct ip *set_buffers6(int);
+extern void set_IPv4opt_sourcerouting(int, union any_in_addr *,
+ union any_in_addr *);
+static void set_sin(struct sockaddr *, union any_in_addr *, int);
+static int set_src_addr(struct pr_set *, struct ifaddrlist **);
+static void setup_protocol(struct pr_set *, int);
+static void setup_socket(struct pr_set *, int);
+static void sig_handler(int);
+static int str2int(const char *, const char *, int, int);
+static double str2dbl(const char *, const char *, double, double);
+static void trace_it(struct addrinfo *);
+static void traceroute(union any_in_addr *, struct msghdr *, struct pr_set *,
+ int, struct ifaddrlist *);
+static void tv_sub(struct timeval *, struct timeval *);
+static void usage(void);
+static int wait_for_reply(int, struct msghdr *, struct timeval *);
+static double xsqrt(double);
+
+/*
+ * main
+ */
+void
+main(int argc, char **argv)
+{
+ struct addrinfo *ai_dst = NULL; /* destination host */
+ /*
+ * "probing_successful" indicates if we could successfully send probes,
+ * not necessarily received reply from the target (this behavior is from
+ * the original traceroute). It's _B_FALSE if packlen is invalid, or no
+ * interfaces found.
+ */
+ boolean_t probing_successful = _B_FALSE;
+ int longjmp_return; /* return value from longjump */
+ int i = 0;
+ char *cp;
+ int op;
+ char *ep;
+ char temp_buf[INET6_ADDRSTRLEN]; /* use for inet_ntop() */
+ double pause;
+
+ /*
+ * A raw socket will be used for IPv4 if there is sufficient
+ * privilege.
+ */
+ raw_req = priv_ineffect(PRIV_NET_RAWACCESS);
+
+ /*
+ * We'll need the privilege only when we open the sockets; that's
+ * when we'll fail if the program has insufficient privileges.
+ */
+ (void) __init_suid_priv(PU_CLEARLIMITSET, PRIV_NET_ICMPACCESS,
+ raw_req ? PRIV_NET_RAWACCESS : NULL, NULL);
+
+ (void) setlinebuf(stdout);
+
+ if ((cp = strrchr(argv[0], '/')) != NULL)
+ prog = cp + 1;
+ else
+ prog = argv[0];
+
+ opterr = 0;
+ while ((op = getopt(argc, argv, "adFIlnrSvxA:c:f:g:i:L:m:P:p:Q:q:s:"
+ "t:w:")) != EOF) {
+ switch (op) {
+ case 'A':
+ if (strcmp(optarg, "inet") == 0) {
+ family_input = AF_INET;
+ } else if (strcmp(optarg, "inet6") == 0) {
+ family_input = AF_INET6;
+ } else {
+ Fprintf(stderr,
+ "%s: unknown address family %s\n",
+ prog, optarg);
+ exit(EXIT_FAILURE);
+ }
+ break;
+
+ case 'a':
+ probe_all = _B_TRUE;
+ break;
+
+ case 'c':
+ class = str2int(optarg, "traffic class", 0,
+ MAX_TRAFFIC_CLASS);
+ break;
+
+ case 'd':
+ options |= SO_DEBUG;
+ break;
+
+ case 'f':
+ first_ttl = str2int(optarg, "first ttl", 1, MAXTTL);
+ break;
+
+ case 'F':
+ off = IP_DF;
+ break;
+
+ case 'g':
+ if (!raw_req) {
+ Fprintf(stderr,
+ "%s: privilege to specify a loose source "
+ "route gateway is unavailable\n",
+ prog);
+ exit(EXIT_FAILURE);
+ }
+ if (gw_count > MAXMAX_GWS) {
+ Fprintf(stderr,
+ "%s: Too many gateways\n", prog);
+ exit(EXIT_FAILURE);
+ }
+ gwlist[gw_count] = strdup(optarg);
+ if (gwlist[gw_count] == NULL) {
+ Fprintf(stderr, "%s: strdup %s\n", prog,
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ ++gw_count;
+ break;
+
+ case 'l':
+ showttl = _B_TRUE;
+ break;
+
+ case 'i':
+ /* this can be IF name or IF index */
+ if_index = (uint_t)strtol(optarg, &ep, 10);
+
+ /* convert IF index <--> IF name */
+ if (errno != 0 || *ep != '\0') {
+ device = optarg;
+ if_index = if_nametoindex((const char *)device);
+
+ /*
+ * In case it fails, check to see if the problem
+ * is other than "IF not found".
+ */
+ if (if_index == 0 && errno != ENXIO) {
+ Fprintf(stderr, "%s: if_nametoindex:"
+ "%s\n", prog, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ } else {
+ device = (char *)malloc(LIFNAMSIZ + 1);
+ if (device == NULL) {
+ Fprintf(stderr, "%s: malloc: %s\n",
+ prog, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ device = if_indextoname(if_index, device);
+ if (device != NULL) {
+ device[LIFNAMSIZ] = '\0';
+ } else if (errno != ENXIO) {
+ /*
+ * The problem was other than "index
+ * not found".
+ */
+ Fprintf(stderr, "%s: if_indextoname:"
+ "%s\n", prog, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (device == NULL || if_index == 0) {
+ Fprintf(stderr, "%s: interface %s "
+ "doesn't match any actual interfaces\n",
+ prog, optarg);
+ exit(EXIT_FAILURE);
+ }
+ break;
+
+ case 'I':
+ useicmp = _B_TRUE;
+ break;
+
+ case 'L':
+ flow = str2int(optarg, "flow label", 0, MAX_FLOW_LABEL);
+ break;
+
+ case 'm':
+ max_ttl = str2int(optarg, "max ttl(hop limit)", 1,
+ MAXTTL);
+ break;
+
+ case 'n':
+ nflag = _B_TRUE;
+ break;
+
+ case 'P':
+ pause = str2dbl(optarg, "pause", 0, INT_MAX);
+ delay.tv_sec = (time_t)pause;
+ delay.tv_usec = (suseconds_t)((pause - delay.tv_sec) *
+ 1000000);
+ break;
+
+ case 'p':
+ port = str2int(optarg, "port", 1, MAX_PORT);
+ break;
+
+ case 'Q':
+ max_timeout = str2int(optarg, "max timeout", 1, -1);
+ break;
+
+ case 'q':
+ nprobes = str2int(optarg, "nprobes", 1, -1);
+ break;
+
+ case 'r':
+ options |= SO_DONTROUTE;
+ break;
+
+ case 'S':
+ collect_stat = _B_TRUE;
+ break;
+
+ case 's':
+ /*
+ * set the ip source address of the outbound
+ * probe (e.g., on a multi-homed host).
+ */
+ source_input = optarg;
+ break;
+
+ case 't':
+ tos = (uchar_t)str2int(optarg, "tos", 0, MAX_TOS);
+ settos = _B_TRUE;
+ break;
+
+ case 'v':
+ verbose = _B_TRUE;
+ break;
+
+ case 'x':
+ docksum = _B_FALSE;
+ break;
+
+ case 'w':
+ waittime = str2int(optarg, "wait time", 2, -1);
+ break;
+
+ default:
+ usage();
+ break;
+ }
+ }
+
+ /*
+ * If it's probe_all, SIGQUIT makes traceroute exit(). But we set the
+ * address to jump back to in traceroute(). Until then, we'll need to
+ * temporarily specify one.
+ */
+ if (probe_all) {
+ if ((longjmp_return = setjmp(env)) != 0) {
+ if (longjmp_return == SIGQUIT) {
+ Printf("(exiting)\n");
+ exit(EXIT_SUCCESS);
+ } else { /* should never happen */
+ exit(EXIT_FAILURE);
+ }
+ }
+ (void) signal(SIGQUIT, sig_handler);
+ }
+
+ if ((gw_count > 0) && (options & SO_DONTROUTE)) {
+ Fprintf(stderr, "%s: loose source route gateways (-g)"
+ " cannot be specified when probe packets are sent"
+ " directly to a host on an attached network (-r)\n",
+ prog);
+ exit(EXIT_FAILURE);
+ }
+
+ i = argc - optind;
+ if (i == 1 || i == 2) {
+ hostname = argv[optind];
+
+ if (i == 2) {
+ /* accept any length now, we'll check it later */
+ packlen_input = str2int(argv[optind + 1],
+ "packet length", 0, -1);
+ }
+ } else {
+ usage();
+ }
+
+ if (first_ttl > max_ttl) {
+ Fprintf(stderr,
+ "%s: first ttl(hop limit) (%d) may not be greater"
+ " than max ttl(hop limit) (%d)\n",
+ prog, first_ttl, max_ttl);
+ exit(EXIT_FAILURE);
+ }
+
+ /* resolve hostnames */
+ resolve_nodes(&family_input, &ai_dst);
+ if (ai_dst == NULL) {
+ exit(EXIT_FAILURE);
+ }
+
+ /*
+ * If it's probe_all, SIGINT makes traceroute skip to probing next IP
+ * address of the target. The new interrupt handler is assigned in
+ * traceroute() function. Until then let's ignore the signal.
+ */
+ if (probe_all)
+ (void) signal(SIGINT, SIG_IGN);
+
+ ident = (getpid() & 0xffff) | 0x8000;
+
+ /*
+ * We KNOW that probe_all == TRUE if family is AF_UNSPEC,
+ * since family is set to the specific AF found unless it's
+ * probe_all. So if family == AF_UNSPEC, we need to init pr4 and pr6.
+ */
+ switch (family_input) {
+ case AF_UNSPEC:
+ pr4 = (struct pr_set *)malloc(sizeof (struct pr_set));
+ if (pr4 == NULL) {
+ Fprintf(stderr,
+ "%s: malloc %s\n", prog, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ pr6 = (struct pr_set *)malloc(sizeof (struct pr_set));
+ if (pr6 == NULL) {
+ Fprintf(stderr,
+ "%s: malloc %s\n", prog, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ setup_protocol(pr6, AF_INET6);
+ setup_protocol(pr4, AF_INET);
+ outip6 = (*pr6->set_buffers_fn)(pr6->packlen);
+ setup_socket(pr6, pr6->packlen);
+
+ outip4 = (*pr4->set_buffers_fn)(pr4->packlen);
+ setup_socket(pr4, pr4->packlen);
+ num_ifs6 = set_src_addr(pr6, &al6);
+ num_ifs4 = set_src_addr(pr4, &al4);
+ break;
+ case AF_INET6:
+ pr6 = (struct pr_set *)malloc(sizeof (struct pr_set));
+ if (pr6 == NULL) {
+ Fprintf(stderr,
+ "%s: malloc %s\n", prog, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ setup_protocol(pr6, AF_INET6);
+ outip6 = (*pr6->set_buffers_fn)(pr6->packlen);
+ setup_socket(pr6, pr6->packlen);
+ num_ifs6 = set_src_addr(pr6, &al6);
+ break;
+ case AF_INET:
+ pr4 = (struct pr_set *)malloc(sizeof (struct pr_set));
+ if (pr4 == NULL) {
+ Fprintf(stderr,
+ "%s: malloc %s\n", prog, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ setup_protocol(pr4, AF_INET);
+ outip4 = (*pr4->set_buffers_fn)(pr4->packlen);
+ setup_socket(pr4, pr4->packlen);
+ num_ifs4 = set_src_addr(pr4, &al4);
+ break;
+ default:
+ Fprintf(stderr, "%s: unknow address family.\n", prog);
+ exit(EXIT_FAILURE);
+ }
+
+ if (num_v4 + num_v6 > 1 && !probe_all) {
+ if (ai_dst->ai_family == AF_INET) {
+ Fprintf(stderr,
+ "%s: Warning: %s has multiple addresses;"
+ " using %s\n", prog, hostname,
+ inet_ntop(AF_INET,
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ (void *)&((struct sockaddr_in *)
+ ai_dst->ai_addr)->sin_addr,
+ temp_buf, sizeof (temp_buf)));
+ } else {
+ Fprintf(stderr,
+ "%s: Warning: %s has multiple addresses;"
+ " using %s\n", prog, hostname,
+ inet_ntop(AF_INET6,
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ (void *)&((struct sockaddr_in6 *)
+ ai_dst->ai_addr)->sin6_addr,
+ temp_buf, sizeof (temp_buf)));
+ }
+ }
+
+ if (num_ifs4 + num_ifs6 > 0) {
+ trace_it(ai_dst);
+ probing_successful = _B_TRUE;
+ }
+
+ (void) close(rcvsock4);
+ (void) close(sndsock4);
+ (void) close(rcvsock6);
+ (void) close(sndsock6);
+
+ /*
+ * if we could probe any of the IP addresses of the target, that means
+ * this was a successful operation
+ */
+ if (probing_successful)
+ exit(EXIT_SUCCESS);
+ else
+ exit(EXIT_FAILURE);
+}
+
+/*
+ * print "unknown host" message
+ */
+static void
+print_unknown_host_msg(const char *protocol, const char *host)
+{
+ Fprintf(stderr, "%s: unknown%s host %s\n", prog, protocol, host);
+}
+
+/*
+ * resolve destination host and gateways
+ */
+static void
+resolve_nodes(int *family, struct addrinfo **ai_dstp)
+{
+ struct addrinfo *ai_dst = NULL;
+ struct addrinfo *aip = NULL;
+ int num_resolved_gw = 0;
+ int num_resolved_gw6 = 0;
+
+ get_hostinfo(hostname, *family, &ai_dst);
+ if (ai_dst == NULL) {
+ print_unknown_host_msg("", hostname);
+ exit(EXIT_FAILURE);
+ }
+ /* Get a count of the v4 & v6 addresses */
+ for (aip = ai_dst; aip != NULL; aip = aip->ai_next) {
+ switch (aip->ai_family) {
+ case AF_INET:
+ num_v4++;
+ break;
+ case AF_INET6:
+ num_v6++;
+ break;
+ }
+ }
+
+ if (*family == AF_UNSPEC && !probe_all) {
+ *family = ai_dst->ai_family;
+ }
+
+ /* resolve gateways */
+ if (gw_count > 0) {
+ get_gwaddrs(gwlist, *family, gwIPlist, gwIP6list,
+ &num_resolved_gw, &num_resolved_gw6);
+
+ /* we couldn't resolve a gateway as an IPv6 host */
+ if (num_resolved_gw6 != gw_count && num_v6 != 0) {
+ if (*family == AF_INET6 || *family == AF_UNSPEC)
+ print_unknown_host_msg(" IPv6",
+ gwlist[num_resolved_gw6]);
+ num_v6 = 0;
+ }
+
+ /* we couldn't resolve a gateway as an IPv4 host */
+ if (num_resolved_gw != gw_count && num_v4 != 0) {
+ if (*family == AF_INET || *family == AF_UNSPEC)
+ print_unknown_host_msg(" IPv4",
+ gwlist[num_resolved_gw]);
+ num_v4 = 0;
+ }
+ }
+
+ *ai_dstp = ai_dst;
+}
+
+/*
+ * Given IP address or hostname, return v4 and v6 hostinfo lists.
+ * Assumes that hostinfo ** ptrs are non-null.
+ */
+static void
+get_hostinfo(char *host, int family, struct addrinfo **aipp)
+{
+ struct addrinfo hints, *ai;
+ struct in6_addr addr6;
+ struct in_addr addr;
+ char temp_buf[INET6_ADDRSTRLEN]; /* use for inet_ntop() */
+ int rc;
+
+ /*
+ * Take care of v4-mapped addresses. It should run same as v4, after
+ * chopping off the prefix, leaving the IPv4 address
+ */
+ if ((inet_pton(AF_INET6, host, &addr6) > 0) &&
+ IN6_IS_ADDR_V4MAPPED(&addr6)) {
+ /* peel off the "mapping" stuff, leaving 32 bit IPv4 address */
+ IN6_V4MAPPED_TO_INADDR(&addr6, &addr);
+
+ /* convert it back to a string */
+ (void) inet_ntop(AF_INET, (void *)&addr, temp_buf,
+ sizeof (temp_buf));
+
+ /* now the host is an IPv4 address */
+ (void) strcpy(host, temp_buf);
+
+ /*
+ * If it's a mapped address, we convert it into IPv4
+ * address because traceroute will send and receive IPv4
+ * packets for that address. Therefore, it's a failure case to
+ * ask get_hostinfo() to treat a mapped address as an IPv6
+ * address.
+ */
+ if (family == AF_INET6) {
+ return;
+ }
+ }
+
+ (void) memset(&hints, 0, sizeof (hints));
+ hints.ai_family = family;
+ hints.ai_flags = AI_ADDRCONFIG;
+ rc = getaddrinfo(host, NULL, &hints, &ai);
+ if (rc != 0) {
+ if (rc != EAI_NONAME)
+ Fprintf(stderr, "%s: getaddrinfo: %s\n", prog,
+ gai_strerror(rc));
+ return;
+ }
+ *aipp = ai;
+}
+
+/*
+ * Calculate the packet length to be used, and check against the valid range.
+ * Returns -1 if range check fails.
+ */
+static uint_t
+calc_packetlen(int plen_input, struct pr_set *pr)
+{
+ int minpacket; /* min ip packet size */
+ int optlen; /* length of ip options */
+ int plen;
+
+ /*
+ * LBNL bug fixed: miscalculation of optlen
+ */
+ if (gw_count > 0) {
+ /*
+ * IPv4:
+ * ----
+ * 5 (NO OPs) + 3 (code, len, ptr) + gateways
+ * IP options field can hold up to 9 gateways. But the API
+ * allows you to specify only 8, because the last one is the
+ * destination host. When this packet is sent, on the wire
+ * you see one gateway replaced by 4 NO OPs. The other 1 NO
+ * OP is for alignment
+ *
+ * IPv6:
+ * ----
+ * Well, formula is different, but the result is same.
+ * 8 byte fixed part for Type 0 Routing header, followed by
+ * gateway addresses
+ */
+ optlen = 8 + gw_count * pr->addr_len;
+ } else {
+ optlen = 0;
+ }
+
+ /* take care of the packet length calculations and checks */
+ minpacket = pr->ip_hdr_len + sizeof (struct outdata) + optlen;
+ if (useicmp)
+ minpacket += pr->icmp_minlen; /* minimum ICMP header size */
+ else
+ minpacket += sizeof (struct udphdr);
+ plen = plen_input;
+ if (plen == 0) {
+ plen = minpacket; /* minimum sized packet */
+ } else if (minpacket > plen || plen > IP_MAXPACKET) {
+ Fprintf(stderr, "%s: %s packet size must be >= %d and <= %d\n",
+ prog, pr->name, minpacket, IP_MAXPACKET);
+ return (0);
+ }
+
+ return (plen);
+}
+
+/*
+ * Sets the source address by resolving -i and -s arguments, or if -i and -s
+ * don't dictate any, it sets the pick_src to make sure traceroute uses the
+ * kernel's pick of the source address.
+ * Returns number of interfaces configured on the source host, 0 on error or
+ * there's no interface which is up amd not a loopback.
+ */
+static int
+set_src_addr(struct pr_set *pr, struct ifaddrlist **alp)
+{
+ union any_in_addr *ap;
+ struct ifaddrlist *al = NULL;
+ struct ifaddrlist *tmp1_al = NULL;
+ struct ifaddrlist *tmp2_al = NULL;
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ struct sockaddr_in *sin_from = (struct sockaddr_in *)pr->from;
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ struct sockaddr_in6 *sin6_from = (struct sockaddr_in6 *)pr->from;
+ struct addrinfo *aip;
+ char errbuf[ERRBUFSIZE];
+ char temp_buf[INET6_ADDRSTRLEN]; /* use for inet_ntop() */
+ int num_ifs; /* all the interfaces */
+ int num_src_ifs; /* exclude loopback and down */
+ int i;
+
+ source = source_input;
+
+ /* get the interface address list */
+ num_ifs = ifaddrlist(&al, pr->family, errbuf);
+ if (num_ifs < 0) {
+ Fprintf(stderr, "%s: ifaddrlist: %s\n", prog, errbuf);
+ exit(EXIT_FAILURE);
+ }
+
+ num_src_ifs = 0;
+ for (i = 0; i < num_ifs; i++) {
+ if (!(al[i].flags & IFF_LOOPBACK) && (al[i].flags & IFF_UP))
+ num_src_ifs++;
+ }
+
+ if (num_src_ifs == 0) {
+ Fprintf(stderr, "%s: can't find any %s network interfaces\n",
+ prog, pr->name);
+ return (0);
+ }
+
+ /* verify the device */
+ if (device != NULL) {
+ tmp1_al = find_device(al, num_ifs, device);
+
+ if (tmp1_al == NULL) {
+ Fprintf(stderr, "%s: %s (index %d) is an invalid %s"
+ " interface\n", prog, device, if_index, pr->name);
+ free(al);
+ return (0);
+ }
+ }
+
+ /* verify the source address */
+ if (source != NULL) {
+ get_hostinfo(source, pr->family, &aip);
+ if (aip == NULL) {
+ Fprintf(stderr,
+ "%s: %s is an invalid %s source address\n",
+ prog, source, pr->name);
+
+ free(al);
+ return (0);
+ }
+
+ source = aip->ai_canonname;
+ ap = (union any_in_addr *)
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ &((struct sockaddr_in6 *)
+ aip->ai_addr)->sin6_addr;
+
+ /*
+ * LBNL bug fixed: used to accept any src address
+ */
+ tmp2_al = find_ifaddr(al, num_ifs, ap, pr->family);
+
+ if (tmp2_al == NULL) {
+ Fprintf(stderr,
+ "%s: %s is an invalid %s source address\n", prog,
+ inet_ntop(pr->family, (const void *)ap,
+ temp_buf, sizeof (temp_buf)),
+ pr->name);
+
+ free(al);
+ freeaddrinfo(aip);
+ return (0);
+ }
+ }
+
+ pick_src = _B_FALSE;
+
+ if (source == NULL) { /* no -s used */
+ if (device == NULL) { /* no -i used, no -s used */
+ pick_src = _B_TRUE;
+ } else { /* -i used, no -s used */
+ /*
+ * -i used, but not -s, and it's IPv4: set the source
+ * address to whatever the interface has configured on
+ * it.
+ */
+ if (pr->family == AF_INET)
+ set_sin(pr->from, &(tmp1_al->addr), pr->family);
+ else
+ pick_src = _B_TRUE;
+ }
+ } else { /* -s used */
+ if (device == NULL) { /* no -i used, -s used */
+ set_sin(pr->from, ap, pr->family);
+
+ if (aip->ai_next != NULL) {
+ Fprintf(stderr,
+ "%s: Warning: %s has multiple "
+ "addresses; using %s\n",
+ prog, source,
+ inet_ntop(pr->family,
+ (const void *)pr->from_sin_addr,
+ temp_buf, sizeof (temp_buf)));
+ }
+ } else { /* -i and -s used */
+ /*
+ * Make sure the source specified matches the
+ * interface address. You only care about this for IPv4
+ * IPv6 can handle IF not matching src address
+ */
+ if (pr->family == AF_INET) {
+ if (!has_addr(aip, &tmp1_al->addr)) {
+ Fprintf(stderr,
+ "%s: %s is not on interface %s\n",
+ prog, source, device);
+ exit(EXIT_FAILURE);
+ }
+ /*
+ * make sure we use the one matching the
+ * interface's address
+ */
+ *ap = tmp1_al->addr;
+ }
+
+ set_sin(pr->from, ap, pr->family);
+ }
+ }
+
+ /*
+ * Binding at this point will set the source address to be used
+ * for both IPv4 (when raw IP datagrams are not required) and
+ * IPv6. If the address being bound to is zero, then the kernel
+ * will end up choosing the source address when the datagram is
+ * sent.
+ *
+ * For raw IPv4 datagrams, the source address is initialized
+ * within traceroute() along with the outbound destination
+ * address.
+ */
+ if (pr->family == AF_INET && !raw_req) {
+ sin_from->sin_family = AF_INET;
+ sin_from->sin_port = htons(ident);
+ if (bind(sndsock4, (struct sockaddr *)pr->from,
+ sizeof (struct sockaddr_in)) < 0) {
+ Fprintf(stderr, "%s: bind: %s\n", prog,
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ } else if (pr->family == AF_INET6) {
+ sin6_from->sin6_family = AF_INET6;
+ sin6_from->sin6_port = htons(ident);
+ if (bind(sndsock6, (struct sockaddr *)pr->from,
+ sizeof (struct sockaddr_in6)) < 0) {
+ Fprintf(stderr, "%s: bind: %s\n", prog,
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ whereto6.sin6_flowinfo = htonl((class << 20) | flow);
+ }
+ *alp = al;
+ return (num_ifs);
+}
+
+/*
+ * Returns the complete ifaddrlist structure matching the desired interface
+ * address. Ignores interfaces which are either down or loopback.
+ */
+static struct ifaddrlist *
+find_ifaddr(struct ifaddrlist *al, int len, union any_in_addr *addr,
+ int family)
+{
+ struct ifaddrlist *tmp_al = al;
+ int i;
+ size_t addr_len = (family == AF_INET) ? sizeof (struct in_addr) :
+ sizeof (struct in6_addr);
+
+ for (i = 0; i < len; i++, tmp_al++) {
+ if ((!(tmp_al->flags & IFF_LOOPBACK) &&
+ (tmp_al->flags & IFF_UP)) &&
+ (memcmp(&tmp_al->addr, addr, addr_len) == 0))
+ break;
+ }
+
+ if (i < len) {
+ return (tmp_al);
+ } else {
+ return (NULL);
+ }
+}
+
+/*
+ * Returns the complete ifaddrlist structure matching the desired interface name
+ * Ignores interfaces which are either down or loopback.
+ */
+static struct ifaddrlist *
+find_device(struct ifaddrlist *al, int len, char *device)
+{
+ struct ifaddrlist *tmp_al = al;
+ int i;
+
+ for (i = 0; i < len; i++, tmp_al++) {
+ if ((!(tmp_al->flags & IFF_LOOPBACK) &&
+ (tmp_al->flags & IFF_UP)) &&
+ (strcmp(tmp_al->device, device) == 0))
+ break;
+ }
+
+ if (i < len) {
+ return (tmp_al);
+ } else {
+ return (NULL);
+ }
+}
+
+/*
+ * returns _B_TRUE if given hostinfo contains the given address
+ */
+static boolean_t
+has_addr(struct addrinfo *ai, union any_in_addr *addr)
+{
+ struct addrinfo *ai_tmp = NULL;
+ union any_in_addr *ap;
+
+ for (ai_tmp = ai; ai_tmp != NULL; ai_tmp = ai_tmp->ai_next) {
+ if (ai_tmp->ai_family == AF_INET6)
+ continue;
+ ap = (union any_in_addr *)
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ &((struct sockaddr_in *)ai_tmp->ai_addr)->sin_addr;
+ if (memcmp(ap, addr, sizeof (struct in_addr)) == 0)
+ break;
+ }
+
+ if (ai_tmp != NULL) {
+ return (_B_TRUE);
+ } else {
+ return (_B_FALSE);
+ }
+}
+
+/*
+ * Resolve the gateway names, splitting results into v4 and v6 lists.
+ * Gateway addresses are added to the appropriate passed-in array; the
+ * number of resolved gateways for each af is returned in resolved[6].
+ * Assumes that passed-in arrays are large enough for MAX_GWS[6] addrs
+ * and resolved[6] ptrs are non-null; ignores array and counter if the
+ * address family param makes them irrelevant.
+ */
+static void
+get_gwaddrs(char **gwlist, int family, union any_in_addr *gwIPlist,
+ union any_in_addr *gwIPlist6, int *resolved, int *resolved6)
+{
+ int i;
+ boolean_t check_v4 = _B_TRUE, check_v6 = _B_TRUE;
+ struct addrinfo *ai = NULL;
+ struct addrinfo *aip = NULL;
+
+ *resolved = *resolved6 = 0;
+ switch (family) {
+ case AF_UNSPEC:
+ break;
+ case AF_INET:
+ check_v6 = _B_FALSE;
+ break;
+ case AF_INET6:
+ check_v4 = _B_FALSE;
+ break;
+ default:
+ return;
+ }
+
+ if (check_v4 && gw_count >= MAX_GWS) {
+ check_v4 = _B_FALSE;
+ Fprintf(stderr, "%s: too many IPv4 gateways\n", prog);
+ }
+ if (check_v6 && gw_count >= MAX_GWS6) {
+ check_v6 = _B_FALSE;
+ Fprintf(stderr, "%s: too many IPv6 gateways\n", prog);
+ }
+
+ for (i = 0; i < gw_count; i++) {
+ if (!check_v4 && !check_v6)
+ return;
+ get_hostinfo(gwlist[i], family, &ai);
+ if (ai == NULL)
+ return;
+ if (check_v4 && num_v4 != 0) {
+ for (aip = ai; aip != NULL; aip = aip->ai_next) {
+ if (aip->ai_family == AF_INET) {
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ bcopy(&((struct sockaddr_in *)
+ aip->ai_addr)->sin_addr,
+ &gwIPlist[i].addr,
+ aip->ai_addrlen);
+ (*resolved)++;
+ break;
+ }
+ }
+ } else if (check_v4) {
+ check_v4 = _B_FALSE;
+ }
+ if (check_v6 && num_v6 != 0) {
+ for (aip = ai; aip != NULL; aip = aip->ai_next) {
+ if (aip->ai_family == AF_INET6) {
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ bcopy(&((struct sockaddr_in6 *)
+ aip->ai_addr)->sin6_addr,
+ &gwIPlist6[i].addr6,
+ aip->ai_addrlen);
+ (*resolved6)++;
+ break;
+ }
+ }
+ } else if (check_v6) {
+ check_v6 = _B_FALSE;
+ }
+ }
+ freeaddrinfo(ai);
+}
+
+/*
+ * set protocol specific values here
+ */
+static void
+setup_protocol(struct pr_set *pr, int family)
+{
+ /*
+ * Set the global variables for each AF. This is going to save us lots
+ * of "if (family == AF_INET)... else .."
+ */
+ pr->family = family;
+
+ if (family == AF_INET) {
+ if (!docksum) {
+ Fprintf(stderr,
+ "%s: Warning: checksums disabled\n", prog);
+ }
+ (void) strcpy(pr->name, "IPv4");
+ (void) strcpy(pr->icmp, "icmp");
+ pr->icmp_minlen = ICMP_MINLEN;
+ pr->addr_len = sizeof (struct in_addr);
+ pr->ip_hdr_len = sizeof (struct ip);
+ pr->sock_size = sizeof (struct sockaddr_in);
+ pr->to = (struct sockaddr *)&whereto;
+ pr->from = (struct sockaddr *)&wherefrom;
+ pr->from_sin_addr = (void *)&wherefrom.sin_addr;
+ pr->gwIPlist = gwIPlist;
+ pr->set_buffers_fn = set_buffers;
+ pr->check_reply_fn = check_reply;
+ pr->print_icmp_other_fn = print_icmp_other;
+ pr->print_addr_fn = print_addr;
+ pr->packlen = calc_packetlen(packlen_input, pr);
+ } else {
+ (void) strcpy(pr->name, "IPv6");
+ (void) strcpy(pr->icmp, "ipv6-icmp");
+ pr->icmp_minlen = ICMP6_MINLEN;
+ pr->addr_len = sizeof (struct in6_addr);
+ pr->ip_hdr_len = sizeof (struct ip6_hdr);
+ pr->sock_size = sizeof (struct sockaddr_in6);
+ pr->to = (struct sockaddr *)&whereto6;
+ pr->from = (struct sockaddr *)&wherefrom6;
+ pr->from_sin_addr = (void *)&wherefrom6.sin6_addr;
+ pr->gwIPlist = gwIP6list;
+ pr->set_buffers_fn = set_buffers6;
+ pr->check_reply_fn = check_reply6;
+ pr->print_icmp_other_fn = print_icmp_other6;
+ pr->print_addr_fn = print_addr6;
+ pr->packlen = calc_packetlen(packlen_input, pr);
+ }
+ if (pr->packlen == 0)
+ exit(EXIT_FAILURE);
+}
+
+/*
+ * setup the sockets for the given protocol's address family
+ */
+static void
+setup_socket(struct pr_set *pr, int packet_len)
+{
+ int on = 1;
+ struct protoent *pe;
+ int type;
+ int proto;
+ int int_op;
+ int rsock;
+ int ssock;
+
+ if ((pe = getprotobyname(pr->icmp)) == NULL) {
+ Fprintf(stderr, "%s: unknown protocol %s\n", prog, pr->icmp);
+ exit(EXIT_FAILURE);
+ }
+
+ /* privilege bracketing */
+ (void) __priv_bracket(PRIV_ON);
+
+ if ((rsock = socket(pr->family, SOCK_RAW, pe->p_proto)) < 0) {
+ Fprintf(stderr, "%s: icmp socket: %s\n", prog, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ if (options & SO_DEBUG) {
+ if (setsockopt(rsock, SOL_SOCKET, SO_DEBUG, (char *)&on,
+ sizeof (on)) < 0) {
+ Fprintf(stderr, "%s: SO_DEBUG: %s\n", prog,
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ }
+ if (options & SO_DONTROUTE) {
+ if (setsockopt(rsock, SOL_SOCKET, SO_DONTROUTE, (char *)&on,
+ sizeof (on)) < 0) {
+ Fprintf(stderr, "%s: SO_DONTROUTE: %s\n", prog,
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (pr->family == AF_INET6) {
+ /* Enable receipt of destination address info */
+ if (setsockopt(rsock, IPPROTO_IPV6, IPV6_RECVPKTINFO,
+ (char *)&on, sizeof (on)) < 0) {
+ Fprintf(stderr, "%s: IPV6_RECVPKTINFO: %s\n", prog,
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ /* Enable receipt of hoplimit info */
+ if (setsockopt(rsock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT,
+ (char *)&on, sizeof (on)) < 0) {
+ Fprintf(stderr, "%s: IPV6_RECVHOPLIMIT: %s\n", prog,
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ }
+
+ /*
+ * Initialize the socket type and protocol based on the address
+ * family, whether or not a raw IP socket is required (for IPv4)
+ * or whether ICMP will be used instead of UDP.
+ *
+ * For historical reasons, the datagrams sent out by
+ * traceroute(1M) do not have the "don't fragment" flag set. For
+ * this reason as well as the ability to set the Loose Source and
+ * Record Route (LSRR) option, a raw IP socket will be used for
+ * IPv4 when run in the global zone. Otherwise, the actual
+ * datagram that will be sent will be a regular UDP or ICMP echo
+ * request packet. However for convenience and for future options
+ * when other IP header information may be specified using
+ * traceroute, the buffer including the raw IP and UDP or ICMP
+ * header is always filled in. When the probe is actually sent,
+ * the size of the request and the start of the packet is set
+ * according to the type of datagram to send.
+ */
+ if (pr->family == AF_INET && raw_req) {
+ type = SOCK_RAW;
+ proto = IPPROTO_RAW;
+ } else if (useicmp) {
+ type = SOCK_RAW;
+ if (pr->family == AF_INET)
+ proto = IPPROTO_ICMP;
+ else
+ proto = IPPROTO_ICMPV6;
+ } else {
+ type = SOCK_DGRAM;
+ proto = IPPROTO_UDP;
+ }
+ ssock = socket(pr->family, type, proto);
+
+ if (ssock < 0) {
+ if (proto == IPPROTO_RAW) {
+ Fprintf(stderr, "%s: raw socket: %s\n", prog,
+ strerror(errno));
+ } else if (proto == IPPROTO_UDP) {
+ Fprintf(stderr, "%s: udp socket: %s\n", prog,
+ strerror(errno));
+ } else {
+ Fprintf(stderr, "%s: icmp socket: %s\n", prog,
+ strerror(errno));
+ }
+ exit(EXIT_FAILURE);
+ }
+
+ if (setsockopt(ssock, SOL_SOCKET, SO_SNDBUF, (char *)&packet_len,
+ sizeof (packet_len)) < 0) {
+ Fprintf(stderr, "%s: SO_SNDBUF: %s\n", prog, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ if (pr->family == AF_INET && raw_req) {
+ if (setsockopt(ssock, IPPROTO_IP, IP_HDRINCL, (char *)&on,
+ sizeof (on)) < 0) {
+ Fprintf(stderr, "%s: IP_HDRINCL: %s\n", prog,
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (options & SO_DEBUG) {
+ if (setsockopt(ssock, SOL_SOCKET, SO_DEBUG, (char *)&on,
+ sizeof (on)) < 0) {
+ Fprintf(stderr, "%s: SO_DEBUG: %s\n", prog,
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ }
+ if (options & SO_DONTROUTE) {
+ if (setsockopt(ssock, SOL_SOCKET, SO_DONTROUTE,
+ (char *)&on, sizeof (on)) < 0) {
+ Fprintf(stderr, "%s: SO_DONTROUTE: %s\n", prog,
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ /*
+ * If a raw IPv4 packet is going to be sent, the Type of Service
+ * field in the packet will be initialized in set_buffers().
+ * Otherwise, it is initialized here using the IPPROTO_IP level
+ * socket option.
+ */
+ if (settos && !raw_req) {
+ int_op = tos;
+ if (setsockopt(ssock, IPPROTO_IP, IP_TOS, (char *)&int_op,
+ sizeof (int_op)) < 0) {
+ Fprintf(stderr, "%s: IP_TOS: %s\n", prog,
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ }
+ if (pr->family == AF_INET) {
+ rcvsock4 = rsock;
+ sndsock4 = ssock;
+ } else {
+ rcvsock6 = rsock;
+ sndsock6 = ssock;
+ }
+ /* Revert to non-privileged user after configuring sockets */
+ (void) __priv_bracket(PRIV_OFF);
+}
+
+/*
+ * If we are "probing all", this function calls traceroute() for each IP address
+ * of the target, otherwise calls only once. Returns _B_FALSE if traceroute()
+ * fails.
+ */
+static void
+trace_it(struct addrinfo *ai_dst)
+{
+ struct msghdr msg6;
+ int num_dst_IPaddrs;
+ struct addrinfo *aip;
+ int i;
+
+ if (!probe_all)
+ num_dst_IPaddrs = 1;
+ else
+ num_dst_IPaddrs = num_v4 + num_v6;
+
+ /*
+ * Initialize the msg6 structure using the hoplimit for the first
+ * probe packet, gateway addresses and the outgoing interface index.
+ */
+ if (ai_dst->ai_family == AF_INET6 || (probe_all && num_v6)) {
+ msg6.msg_control = NULL;
+ msg6.msg_controllen = 0;
+ set_ancillary_data(&msg6, first_ttl, pr6->gwIPlist, gw_count,
+ if_index);
+ }
+
+ /* run traceroute for all the IP addresses of the multihomed dest */
+ for (aip = ai_dst, i = 0; i < num_dst_IPaddrs && aip != NULL; i++) {
+ union any_in_addr *addrp;
+ if (aip->ai_family == AF_INET) {
+ addrp = (union any_in_addr *)
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ &((struct sockaddr_in *)
+ aip->ai_addr)->sin_addr;
+ set_sin((struct sockaddr *)pr4->to, addrp,
+ aip->ai_family);
+ traceroute(addrp, &msg6, pr4, num_ifs4, al4);
+ } else {
+ addrp = (union any_in_addr *)
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ &((struct sockaddr_in6 *)
+ aip->ai_addr)->sin6_addr;
+ set_sin((struct sockaddr *)pr6->to, addrp,
+ aip->ai_family);
+ traceroute(addrp, &msg6, pr6, num_ifs6, al6);
+ }
+ aip = aip->ai_next;
+ if (i < (num_dst_IPaddrs - 1))
+ (void) putchar('\n');
+ }
+}
+
+/*
+ * set the IP address in a sockaddr struct
+ */
+static void
+set_sin(struct sockaddr *sock, union any_in_addr *addr, int family)
+{
+ sock->sa_family = family;
+
+ if (family == AF_INET)
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ ((struct sockaddr_in *)sock)->sin_addr = addr->addr;
+ else
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ ((struct sockaddr_in6 *)sock)->sin6_addr = addr->addr6;
+}
+
+/*
+ * returns the IF name on which the given IP address is configured
+ */
+static char *
+device_name(struct ifaddrlist *al, int len, union any_in_addr *ip_addr,
+ struct pr_set *pr)
+{
+ int i;
+ struct ifaddrlist *tmp_al;
+
+ tmp_al = al;
+
+ for (i = 0; i < len; i++, tmp_al++) {
+ if (memcmp(&tmp_al->addr, ip_addr, pr->addr_len) == 0) {
+ return (tmp_al->device);
+ }
+ }
+
+ return (NULL);
+}
+
+/*
+ * Trace the route to the host with given IP address.
+ */
+static void
+traceroute(union any_in_addr *ip_addr, struct msghdr *msg6, struct pr_set *pr,
+ int num_ifs, struct ifaddrlist *al)
+{
+ int ttl;
+ int probe;
+ uchar_t type; /* icmp type */
+ uchar_t code; /* icmp code */
+ int reply;
+ int seq = 0;
+ char temp_buf[INET6_ADDRSTRLEN]; /* use for inet_ntop() */
+ int longjmp_return; /* return value from longjump */
+ struct ip *ip = (struct ip *)packet;
+ boolean_t got_there = _B_FALSE; /* we hit the destination */
+ static boolean_t first_pkt = _B_TRUE;
+ int hoplimit; /* hoplimit for IPv6 packets */
+ struct in6_addr addr6;
+ int num_src_ifs; /* excludes down and loopback */
+ struct msghdr in_msg;
+ struct iovec iov;
+ int *intp;
+ int sndsock;
+ int rcvsock;
+
+ msg6->msg_name = pr->to;
+ msg6->msg_namelen = sizeof (struct sockaddr_in6);
+ sndsock = (pr->family == AF_INET) ? sndsock4 : sndsock6;
+ rcvsock = (pr->family == AF_INET) ? rcvsock4 : rcvsock6;
+
+ /* carry out the source address selection */
+ if (pick_src) {
+ union any_in_addr src_addr;
+ char *dev_name;
+ int i;
+
+ /*
+ * If there's a gateway, a routing header as a consequence, our
+ * kernel picks the source address based on the first hop
+ * address, rather than final destination address.
+ */
+ if (gw_count > 0) {
+ (void) select_src_addr(pr->gwIPlist, &src_addr,
+ pr->family);
+ } else {
+ (void) select_src_addr(ip_addr, &src_addr, pr->family);
+ }
+ set_sin(pr->from, &src_addr, pr->family);
+
+ /* filter out down and loopback interfaces */
+ num_src_ifs = 0;
+ for (i = 0; i < num_ifs; i++) {
+ if (!(al[i].flags & IFF_LOOPBACK) &&
+ (al[i].flags & IFF_UP))
+ num_src_ifs++;
+ }
+
+ if (num_src_ifs > 1) {
+ dev_name = device_name(al, num_ifs, &src_addr, pr);
+ if (dev_name == NULL)
+ dev_name = "?";
+
+ Fprintf(stderr,
+ "%s: Warning: Multiple interfaces found;"
+ " using %s @ %s\n",
+ prog, inet_ntop(pr->family,
+ (const void *)pr->from_sin_addr,
+ temp_buf, sizeof (temp_buf)),
+ dev_name);
+ }
+ }
+
+ if (pr->family == AF_INET) {
+ outip4->ip_src = *(struct in_addr *)pr->from_sin_addr;
+ outip4->ip_dst = ip_addr->addr;
+ }
+
+ /*
+ * If the hostname is an IPv6 literal address, let's not print it twice.
+ */
+ if (pr->family == AF_INET6 &&
+ inet_pton(AF_INET6, hostname, &addr6) > 0) {
+ Fprintf(stderr, "%s to %s", prog, hostname);
+ } else {
+ Fprintf(stderr, "%s to %s (%s)", prog, hostname,
+ inet_ntop(pr->family, (const void *)ip_addr, temp_buf,
+ sizeof (temp_buf)));
+ }
+
+ if (source)
+ Fprintf(stderr, " from %s", source);
+ Fprintf(stderr, ", %d hops max, %d byte packets\n", max_ttl,
+ pr->packlen);
+ (void) fflush(stderr);
+
+ /*
+ * Setup the source routing for IPv4. For IPv6, we did the required
+ * setup in the caller function, trace_it(), because it's independent
+ * from the IP address of target.
+ */
+ if (pr->family == AF_INET && gw_count > 0)
+ set_IPv4opt_sourcerouting(sndsock, ip_addr, pr->gwIPlist);
+
+ if (probe_all) {
+ /* interrupt handler sig_handler() jumps back to here */
+ if ((longjmp_return = setjmp(env)) != 0) {
+ switch (longjmp_return) {
+ case SIGINT:
+ Printf("(skipping)\n");
+ return;
+ case SIGQUIT:
+ Printf("(exiting)\n");
+ exit(EXIT_SUCCESS);
+ default: /* should never happen */
+ exit(EXIT_FAILURE);
+ }
+ }
+ (void) signal(SIGINT, sig_handler);
+ }
+
+ for (ttl = first_ttl; ttl <= max_ttl; ++ttl) {
+ union any_in_addr lastaddr;
+ int timeouts = 0;
+ double rtt; /* for statistics */
+ int nreceived = 0;
+ double rttmin, rttmax;
+ double rttsum, rttssq;
+ int unreachable;
+
+ got_there = _B_FALSE;
+ unreachable = 0;
+
+ /*
+ * The following line clears both IPv4 and IPv6 address stored
+ * in the union.
+ */
+ lastaddr.addr6 = in6addr_any;
+
+ if ((ttl == (first_ttl + 1)) && (options & SO_DONTROUTE)) {
+ Fprintf(stderr,
+ "%s: host %s is not on a directly-attached"
+ " network\n", prog, hostname);
+ break;
+ }
+
+ Printf("%2d ", ttl);
+ (void) fflush(stdout);
+
+ for (probe = 0; (probe < nprobes) && (timeouts < max_timeout);
+ ++probe) {
+ int cc;
+ struct timeval t1, t2;
+
+ /*
+ * Put a delay before sending this probe packet. Don't
+ * delay it if it's the very first packet.
+ */
+ if (!first_pkt) {
+ if (delay.tv_sec > 0)
+ (void) sleep((uint_t)delay.tv_sec);
+ if (delay.tv_usec > 0)
+ (void) usleep(delay.tv_usec);
+ } else {
+ first_pkt = _B_FALSE;
+ }
+
+ (void) gettimeofday(&t1, NULL);
+
+ if (pr->family == AF_INET) {
+ send_probe(sndsock, pr->to, outip4, seq, ttl,
+ &t1, pr->packlen);
+ } else {
+ send_probe6(sndsock, msg6, outip6, seq, ttl,
+ &t1, pr->packlen);
+ }
+
+ /* prepare msghdr for recvmsg() */
+ in_msg.msg_name = pr->from;
+ in_msg.msg_namelen = pr->sock_size;
+
+ iov.iov_base = (char *)packet;
+ iov.iov_len = sizeof (packet);
+
+ in_msg.msg_iov = &iov;
+ in_msg.msg_iovlen = 1;
+
+ in_msg.msg_control = ancillary_data;
+ in_msg.msg_controllen = sizeof (ancillary_data);
+
+ while ((cc = wait_for_reply(rcvsock, &in_msg,
+ &t1)) != 0) {
+ (void) gettimeofday(&t2, NULL);
+
+ reply = (*pr->check_reply_fn) (&in_msg, cc, seq,
+ &type, &code);
+
+ in_msg.msg_controllen =
+ sizeof (ancillary_data);
+ /* Skip short packet */
+ if (reply == REPLY_SHORT_PKT) {
+ continue;
+ }
+
+ timeouts = 0;
+
+ /*
+ * if reply comes from a different host, print
+ * the hostname
+ */
+ if (memcmp(pr->from_sin_addr, &lastaddr,
+ pr->addr_len) != 0) {
+ (*pr->print_addr_fn) ((uchar_t *)packet,
+ cc, pr->from);
+ /* store the address response */
+ (void) memcpy(&lastaddr,
+ pr->from_sin_addr, pr->addr_len);
+ }
+
+ rtt = deltaT(&t1, &t2);
+ if (collect_stat) {
+ record_stats(rtt, &nreceived, &rttmin,
+ &rttmax, &rttsum, &rttssq);
+ } else {
+ Printf(" %.3f ms", rtt);
+ }
+
+ if (pr->family == AF_INET6) {
+ intp =
+ (int *)find_ancillary_data(&in_msg,
+ IPPROTO_IPV6, IPV6_HOPLIMIT);
+ if (intp == NULL) {
+ Fprintf(stderr,
+ "%s: can't find "
+ "IPV6_HOPLIMIT ancillary "
+ "data\n", prog);
+ exit(EXIT_FAILURE);
+ }
+ hoplimit = *intp;
+ }
+
+ if (reply == REPLY_GOT_TARGET) {
+ got_there = _B_TRUE;
+
+ if (((pr->family == AF_INET) &&
+ (ip->ip_ttl <= 1)) ||
+ ((pr->family == AF_INET6) &&
+ (hoplimit <= 1)))
+ Printf(" !");
+ }
+
+ if (!collect_stat && showttl) {
+ if (pr->family == AF_INET) {
+ Printf(" (ttl=%d)",
+ (int)ip->ip_ttl);
+ } else if (hoplimit != -1) {
+ Printf(" (hop limit=%d)",
+ hoplimit);
+ }
+ }
+
+ if (reply == REPLY_GOT_OTHER) {
+ if ((*pr->print_icmp_other_fn)
+ (type, code)) {
+ unreachable++;
+ }
+ }
+
+ /* special case */
+ if (pr->family == AF_INET &&
+ type == ICMP_UNREACH &&
+ code == ICMP_UNREACH_PROTOCOL)
+ got_there = _B_TRUE;
+
+ break;
+ }
+
+ seq = (seq + 1) % (MAX_SEQ + 1);
+
+ if (cc == 0) {
+ Printf(" *");
+ timeouts++;
+ }
+
+ (void) fflush(stdout);
+ }
+
+ if (collect_stat) {
+ print_stats(probe, nreceived, rttmin, rttmax, rttsum,
+ rttssq);
+ }
+
+ (void) putchar('\n');
+
+ /* either we hit the target or received too many unreachables */
+ if (got_there ||
+ (unreachable > 0 && unreachable >= nprobes - 1))
+ break;
+ }
+
+ /* Ignore the SIGINT between traceroute() runs */
+ if (probe_all)
+ (void) signal(SIGINT, SIG_IGN);
+}
+
+/*
+ * for a given destination address and address family, it finds out what
+ * source address kernel is going to pick
+ */
+static void
+select_src_addr(union any_in_addr *dst_addr, union any_in_addr *src_addr,
+ int family)
+{
+ int tmp_fd;
+ struct sockaddr *sock;
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+ size_t sock_len;
+
+ sock = (struct sockaddr *)malloc(sizeof (struct sockaddr_in6));
+ if (sock == NULL) {
+ Fprintf(stderr, "%s: malloc %s\n", prog, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ (void) bzero(sock, sizeof (struct sockaddr_in6));
+
+ if (family == AF_INET) {
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ sin = (struct sockaddr_in *)sock;
+ sin->sin_family = AF_INET;
+ sin->sin_addr = dst_addr->addr;
+ sin->sin_port = IPPORT_ECHO; /* port shouldn't be 0 */
+ sock_len = sizeof (struct sockaddr_in);
+ } else {
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ sin6 = (struct sockaddr_in6 *)sock;
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_addr = dst_addr->addr6;
+ sin6->sin6_port = IPPORT_ECHO; /* port shouldn't be 0 */
+ sock_len = sizeof (struct sockaddr_in6);
+ }
+
+ /* open a UDP socket */
+ if ((tmp_fd = socket(family, SOCK_DGRAM, 0)) < 0) {
+ Fprintf(stderr, "%s: udp socket: %s\n", prog,
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ /* connect it */
+ if (connect(tmp_fd, sock, sock_len) < 0) {
+ /*
+ * If there's no route to the destination, this connect() call
+ * fails. We just return all-zero (wildcard) as the source
+ * address, so that user can get to see "no route to dest"
+ * message, as it'll try to send the probe packet out and will
+ * receive ICMP unreachable.
+ */
+ if (family == AF_INET)
+ src_addr->addr.s_addr = INADDR_ANY;
+ else
+ src_addr->addr6 = in6addr_any;
+ free(sock);
+ return;
+ }
+
+ /* get the local sock info */
+ if (getsockname(tmp_fd, sock, &sock_len) < 0) {
+ Fprintf(stderr, "%s: getsockname: %s\n", prog,
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ if (family == AF_INET) {
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ sin = (struct sockaddr_in *)sock;
+ src_addr->addr = sin->sin_addr;
+ } else {
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ sin6 = (struct sockaddr_in6 *)sock;
+ src_addr->addr6 = sin6->sin6_addr;
+ }
+
+ free(sock);
+ (void) close(tmp_fd);
+}
+
+/*
+ * Checksum routine for Internet Protocol family headers (C Version)
+ */
+ushort_t
+in_cksum(ushort_t *addr, int len)
+{
+ int nleft = len;
+ ushort_t *w = addr;
+ ushort_t answer;
+ int sum = 0;
+
+ /*
+ * Our algorithm is simple, using a 32 bit accumulator (sum),
+ * we add sequential 16 bit words to it, and at the end, fold
+ * back all the carry bits from the top 16 bits into the lower
+ * 16 bits.
+ */
+ while (nleft > 1) {
+ sum += *w++;
+ nleft -= 2;
+ }
+
+ /* mop up an odd byte, if necessary */
+ if (nleft == 1)
+ sum += *(uchar_t *)w;
+
+ /* add back carry outs from top 16 bits to low 16 bits */
+ sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
+ sum += (sum >> 16); /* add carry */
+ answer = ~sum; /* truncate to 16 bits */
+ return (answer);
+}
+
+/*
+ * Wait until a reply arrives or timeout occurs. If packet arrived, read it
+ * return the size of the packet read.
+ */
+static int
+wait_for_reply(int sock, struct msghdr *msg, struct timeval *tp)
+{
+ fd_set fds;
+ struct timeval now, wait;
+ int cc = 0;
+ int result;
+
+ (void) FD_ZERO(&fds);
+ FD_SET(sock, &fds);
+
+ wait.tv_sec = tp->tv_sec + waittime;
+ wait.tv_usec = tp->tv_usec;
+ (void) gettimeofday(&now, NULL);
+ tv_sub(&wait, &now);
+
+ if (wait.tv_sec < 0 || wait.tv_usec < 0)
+ return (0);
+
+ result = select(sock + 1, &fds, (fd_set *)NULL, (fd_set *)NULL, &wait);
+
+ if (result == -1) {
+ if (errno != EINTR) {
+ Fprintf(stderr, "%s: select: %s\n", prog,
+ strerror(errno));
+ }
+ } else if (result > 0)
+ cc = recvmsg(sock, msg, 0);
+
+ return (cc);
+}
+
+/*
+ * Construct an Internet address representation. If the nflag has been supplied,
+ * give numeric value, otherwise try for symbolic name.
+ */
+char *
+inet_name(union any_in_addr *in, int family)
+{
+ char *cp;
+ static boolean_t first = _B_TRUE;
+ static char domain[NI_MAXHOST + 1];
+ static char line[NI_MAXHOST + 1]; /* assuming */
+ /* (NI_MAXHOST + 1) >= INET6_ADDRSTRLEN */
+ char hbuf[NI_MAXHOST];
+ socklen_t slen;
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+ struct sockaddr *sa;
+ int flags;
+
+ switch (family) {
+ case AF_INET:
+ slen = sizeof (struct sockaddr_in);
+ sin.sin_addr = in->addr;
+ sin.sin_port = 0;
+ sa = (struct sockaddr *)&sin;
+ break;
+ case AF_INET6:
+ slen = sizeof (struct sockaddr_in6);
+ sin6.sin6_addr = in->addr6;
+ sin6.sin6_port = 0;
+ sa = (struct sockaddr *)&sin6;
+ break;
+ deafult:
+ (void) snprintf(line, sizeof (line),
+ "<invalid address family>");
+ return (line);
+ }
+ sa->sa_family = family;
+
+ if (first && !nflag) {
+ /* find out the domain name */
+ first = _B_FALSE;
+ if (gethostname(domain, MAXHOSTNAMELEN) == 0 &&
+ (cp = strchr(domain, '.')) != NULL) {
+ (void) strncpy(domain, cp + 1, sizeof (domain) - 1);
+ domain[sizeof (domain) - 1] = '\0';
+ } else {
+ domain[0] = '\0';
+ }
+ }
+
+ flags = (nflag) ? NI_NUMERICHOST : NI_NAMEREQD;
+ if (getnameinfo(sa, slen, hbuf, sizeof (hbuf), NULL, 0, flags) != 0) {
+ if (inet_ntop(family, (const void *)&in->addr6,
+ hbuf, sizeof (hbuf)) == NULL)
+ hbuf[0] = 0;
+ } else if (!nflag && (cp = strchr(hbuf, '.')) != NULL &&
+ strcmp(cp + 1, domain) == 0) {
+ *cp = '\0';
+ }
+ (void) strlcpy(line, hbuf, sizeof (line));
+
+ return (line);
+}
+
+/*
+ * return the difference (in msec) between two time values
+ */
+static double
+deltaT(struct timeval *t1p, struct timeval *t2p)
+{
+ double dt;
+
+ dt = (double)(t2p->tv_sec - t1p->tv_sec) * 1000.0 +
+ (double)(t2p->tv_usec - t1p->tv_usec) / 1000.0;
+ return (dt);
+}
+
+/*
+ * Subtract 2 timeval structs: out = out - in.
+ * Out is assumed to be >= in.
+ */
+static void
+tv_sub(struct timeval *out, struct timeval *in)
+{
+ if ((out->tv_usec -= in->tv_usec) < 0) {
+ --out->tv_sec;
+ out->tv_usec += 1000000;
+ }
+ out->tv_sec -= in->tv_sec;
+}
+
+/*
+ * record statistics
+ */
+static void
+record_stats(double rtt, int *nreceived, double *rttmin, double *rttmax,
+ double *rttsum, double *rttssq)
+{
+ if (*nreceived == 0) {
+ *rttmin = rtt;
+ *rttmax = rtt;
+ *rttsum = rtt;
+ *rttssq = rtt * rtt;
+ } else {
+ if (rtt < *rttmin)
+ *rttmin = rtt;
+
+ if (rtt > *rttmax)
+ *rttmax = rtt;
+
+ *rttsum += rtt;
+ *rttssq += rtt * rtt;
+ }
+
+ (*nreceived)++;
+}
+
+/*
+ * display statistics
+ */
+static void
+print_stats(int ntransmitted, int nreceived, double rttmin, double rttmax,
+ double rttsum, double rttssq)
+{
+ double rttavg; /* average round-trip time */
+ double rttstd; /* rtt standard deviation */
+
+ if (ntransmitted > 0 && ntransmitted >= nreceived) {
+ int missed = ntransmitted - nreceived;
+ double loss = 100 * (double)missed / (double)ntransmitted;
+
+ if (nreceived > 0) {
+ rttavg = rttsum / nreceived;
+ rttstd = rttssq - (rttavg * rttsum);
+ rttstd = xsqrt(rttstd / nreceived);
+
+ Printf(" %.3f", rttmin);
+ Printf("/%.3f", rttavg);
+ Printf("/%.3f", rttmax);
+
+ Printf(" (%.3f) ms ", rttstd);
+ }
+
+ Printf(" %d/%d pkts", nreceived, ntransmitted);
+
+ if (nreceived == 0)
+ Printf(" (100%% loss)");
+ else
+ Printf(" (%.2g%% loss)", loss);
+ }
+}
+
+/*
+ * square root function
+ */
+double
+xsqrt(double y)
+{
+ double t, x;
+
+ if (y <= 0) {
+ return (0.0);
+ }
+
+ x = (y < 1.0) ? 1.0 : y;
+ do {
+ t = x;
+ x = (t + (y/t))/2.0;
+ } while (0 < x && x < t);
+
+ return (x);
+}
+
+/*
+ * String to double with optional min and max.
+ */
+static double
+str2dbl(const char *str, const char *what, double mi, double ma)
+{
+ double val;
+ char *ep;
+
+ errno = 0;
+
+ val = strtod(str, &ep);
+ if (errno != 0 || *ep != '\0') {
+ Fprintf(stderr, "%s: \"%s\" bad value for %s \n",
+ prog, str, what);
+ exit(EXIT_FAILURE);
+ }
+ if (val < mi && mi >= 0) {
+ Fprintf(stderr, "%s: %s must be >= %f\n", prog, what, mi);
+ exit(EXIT_FAILURE);
+ }
+ if (val > ma && ma >= 0) {
+ Fprintf(stderr, "%s: %s must be <= %f\n", prog, what, ma);
+ exit(EXIT_FAILURE);
+ }
+ return (val);
+}
+
+/*
+ * String to int with optional min and max. Handles decimal and hex.
+ */
+static int
+str2int(const char *str, const char *what, int mi, int ma)
+{
+ const char *cp;
+ int val;
+ char *ep;
+
+ errno = 0;
+
+ if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) {
+ cp = str + 2;
+ val = (int)strtol(cp, &ep, 16);
+ } else {
+ val = (int)strtol(str, &ep, 10);
+ }
+ if (errno != 0 || *ep != '\0') {
+ Fprintf(stderr, "%s: \"%s\" bad value for %s \n",
+ prog, str, what);
+ exit(EXIT_FAILURE);
+ }
+ if (val < mi && mi >= 0) {
+ if (mi == 0) {
+ Fprintf(stderr, "%s: %s must be >= %d\n",
+ prog, what, mi);
+ } else {
+ Fprintf(stderr, "%s: %s must be > %d\n",
+ prog, what, mi - 1);
+ }
+ exit(EXIT_FAILURE);
+ }
+ if (val > ma && ma >= 0) {
+ Fprintf(stderr, "%s: %s must be <= %d\n", prog, what, ma);
+ exit(EXIT_FAILURE);
+ }
+ return (val);
+}
+
+/*
+ * This is the interrupt handler for SIGINT and SIGQUIT. It's completely handled
+ * where it jumps to.
+ */
+static void
+sig_handler(int sig)
+{
+ longjmp(env, sig);
+}
+
+/*
+ * display the usage of traceroute
+ */
+static void
+usage(void)
+{
+ Fprintf(stderr, "Usage: %s [-adFIlnSvx] [-A address_family] "
+"[-c traffic_class] \n"
+"\t[-f first_hop] [-g gateway [-g gateway ...]| -r] [-i iface]\n"
+"\t[-L flow_label] [-m max_hop] [-P pause_sec] [-p port] [-Q max_timeout]\n"
+"\t[-q nqueries] [-s src_addr] [-t tos] [-w wait_time] host [packetlen]\n",
+ prog);
+ exit(EXIT_FAILURE);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/traceroute/traceroute.h b/usr/src/cmd/cmd-inet/usr.sbin/traceroute/traceroute.h
new file mode 100644
index 0000000000..63ec0d8797
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/traceroute/traceroute.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 1988, 1989, 1991, 1994, 1995, 1996, 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ *
+ * @(#)$Header: traceroute.c,v 1.49 97/06/13 02:30:23 leres Exp $ (LBL)
+ */
+
+#ifndef _TRACEROUTE_H
+#define _TRACEROUTE_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MAX_PORT 65535 /* max port value for UDP */
+
+#define REPLY_SHORT_PKT 0 /* check_reply() has a short packet */
+#define REPLY_GOT_GATEWAY 1 /* ... rcvd a reply from an inter. gw */
+#define REPLY_GOT_TARGET 2 /* ... rcvd the reply from the target */
+#define REPLY_GOT_OTHER 3 /* ... received other */
+
+/*
+ * this is the max it can be, yet another factor is PMTU, which is ignored
+ * here
+ */
+#define MAX_GWS6 127
+
+/*
+ * Maximum number of gateways (include room for one noop).
+ * 'in_addr_t' is 32 bits, size of IPv4 address.
+ * Note that the actual number of gateways that can be used for source
+ * routing is one less than the value below. This is because the API requires
+ * the last gateway to be the target address.
+ */
+#define MAX_GWS 9
+
+/* maximum of max_gws */
+#define MAXMAX_GWS MAX(MAX_GWS, MAX_GWS6)
+
+#define A_CNT(ARRAY) (sizeof (ARRAY) / sizeof ((ARRAY)[0]))
+
+#define Fprintf (void)fprintf
+#define Printf (void)printf
+
+struct icmptype_table {
+ int type; /* ICMP type */
+ char *message; /* corresponding string message */
+};
+
+/* Data section of the probe packet */
+struct outdata {
+ uchar_t seq; /* sequence number of this packet */
+ uchar_t ttl; /* ttl packet left with */
+ struct timeval tv; /* time packet left */
+};
+
+extern boolean_t docksum; /* do checksum (IPv4 only) */
+extern int gw_count; /* number of LSRR gateways */
+extern char *hostname;
+extern ushort_t ident; /* identity of this traceroute run */
+extern boolean_t nflag; /* numeric flag */
+extern ushort_t off; /* set DF bit (IPv4 only) */
+extern int packlen; /* packet length */
+extern ushort_t port; /* seed of destination port */
+extern char *prog; /* program name */
+extern boolean_t raw_req; /* if sndsock for IPv4 must be raw */
+extern boolean_t settos; /* set type-of-service (IPv4 only) */
+extern unsigned char tos; /* value of tos to set */
+extern boolean_t useicmp; /* use ICMP or UDP */
+extern boolean_t verbose;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TRACEROUTE_H */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/traceroute/traceroute_aux.c b/usr/src/cmd/cmd-inet/usr.sbin/traceroute/traceroute_aux.c
new file mode 100644
index 0000000000..e8947f9ccb
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/traceroute/traceroute_aux.c
@@ -0,0 +1,566 @@
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 1988, 1989, 1991, 1994, 1995, 1996, 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ *
+ * @(#)$Header: traceroute.c,v 1.49 97/06/13 02:30:23 leres Exp $ (LBL)
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/socket.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <strings.h>
+#include <libintl.h>
+#include <errno.h>
+
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/udp.h>
+#include <netinet/udp_var.h>
+
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <ifaddrlist.h>
+#include "traceroute.h"
+
+/*
+ * IPv4 source routing option.
+ * In order to avoid padding for the alignment of IPv4 addresses, ipsr_addrs
+ * is defined as a 2-D array of uint8_t, instead of 1-D array of struct in_addr.
+ */
+struct ip_sourceroute {
+ uint8_t ipsr_code;
+ uint8_t ipsr_len;
+ uint8_t ipsr_ptr;
+ /* up to 9 IPv4 addresses */
+ uint8_t ipsr_addrs[1][sizeof (struct in_addr)];
+};
+
+int check_reply(struct msghdr *, int, int, uchar_t *, uchar_t *);
+extern ushort_t in_cksum(ushort_t *, int);
+extern char *inet_name(union any_in_addr *, int);
+static char *pr_type(uchar_t);
+void print_addr(uchar_t *, int, struct sockaddr *);
+boolean_t print_icmp_other(uchar_t, uchar_t);
+void send_probe(int, struct sockaddr *, struct ip *, int, int,
+ struct timeval *, int);
+struct ip *set_buffers(int);
+void set_IPv4opt_sourcerouting(int, union any_in_addr *, union any_in_addr *);
+
+/*
+ * prepares the buffer to be sent as an IP datagram
+ */
+struct ip *
+set_buffers(int plen)
+{
+ struct ip *outip;
+ uchar_t *outp; /* packet following the IP header (UDP/ICMP) */
+ struct udphdr *outudp;
+ struct icmp *outicmp;
+ int optlen = 0;
+
+ outip = (struct ip *)malloc((size_t)plen);
+ if (outip == NULL) {
+ Fprintf(stderr, "%s: malloc: %s\n", prog, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ if (gw_count > 0) {
+ /* 8 = 5 (NO OPs) + 3 (code, len, ptr) */
+ optlen = 8 + gw_count * sizeof (struct in_addr);
+ }
+
+ (void) memset((char *)outip, 0, (size_t)plen);
+ outp = (uchar_t *)(outip + 1);
+
+ outip->ip_v = IPVERSION;
+ if (settos)
+ outip->ip_tos = tos;
+
+ /*
+ * LBNL bug fixed: missing '- optlen' before, causing optlen
+ * added twice
+ *
+ * BSD bug: BSD touches the header fields 'len' and 'ip_off'
+ * even when HDRINCL is set. It applies htons() on these
+ * fields. It should send the header untouched when HDRINCL
+ * is set.
+ */
+ outip->ip_len = htons(plen - optlen);
+ outip->ip_off = htons(off);
+ outip->ip_hl = (outp - (uchar_t *)outip) >> 2;
+
+ /* setup ICMP or UDP */
+ if (useicmp) {
+ outip->ip_p = IPPROTO_ICMP;
+
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ outicmp = (struct icmp *)outp;
+ outicmp->icmp_type = ICMP_ECHO;
+ outicmp->icmp_id = htons(ident);
+ } else {
+ outip->ip_p = IPPROTO_UDP;
+
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ outudp = (struct udphdr *)outp;
+ outudp->uh_sport = htons(ident);
+ outudp->uh_ulen =
+ htons((ushort_t)(plen - (sizeof (struct ip) + optlen)));
+ }
+
+ return (outip);
+}
+
+/*
+ * Setup the source routing for IPv4.
+ */
+void
+set_IPv4opt_sourcerouting(int sndsock, union any_in_addr *ip_addr,
+ union any_in_addr *gwIPlist)
+{
+ struct protoent *pe;
+ struct ip_sourceroute *srp;
+ uchar_t optlist[MAX_IPOPTLEN];
+ int i;
+
+ if ((pe = getprotobyname("ip")) == NULL) {
+ Fprintf(stderr, "%s: unknown protocol ip\n", prog);
+ exit(EXIT_FAILURE);
+ }
+
+ /* final hop */
+ gwIPlist[gw_count].addr = ip_addr->addr;
+
+ /*
+ * the option length passed to setsockopt() needs to be a multiple of
+ * 32 bits. Therefore we need to use a 1-byte padding (source routing
+ * information takes 4x+3 bytes).
+ */
+ optlist[0] = IPOPT_NOP;
+
+ srp = (struct ip_sourceroute *)&optlist[1];
+ srp->ipsr_code = IPOPT_LSRR;
+ /* 3 = 1 (code) + 1 (len) + 1 (ptr) */
+ srp->ipsr_len = 3 + (gw_count + 1) * sizeof (gwIPlist[0].addr);
+ srp->ipsr_ptr = IPOPT_MINOFF;
+
+ for (i = 0; i <= gw_count; i++) {
+ (void) bcopy((char *)&gwIPlist[i].addr, &srp->ipsr_addrs[i],
+ sizeof (struct in_addr));
+ }
+
+ if (setsockopt(sndsock, pe->p_proto, IP_OPTIONS, (const char *)optlist,
+ srp->ipsr_len + 1) < 0) {
+ Fprintf(stderr, "%s: IP_OPTIONS: %s\n", prog, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+}
+
+/*
+ * send a probe packet to the destination
+ */
+void
+send_probe(int sndsock, struct sockaddr *to, struct ip *outip,
+ int seq, int ttl, struct timeval *tp, int packlen)
+{
+ int cc;
+ struct udpiphdr *ui;
+ uchar_t *outp; /* packet following the IP header (UDP/ICMP) */
+ struct udphdr *outudp;
+ struct icmp *outicmp;
+ struct outdata *outdata;
+ struct ip tip;
+ int optlen = 0;
+ int send_size;
+
+ /* initialize buffer pointers */
+ outp = (uchar_t *)(outip + 1);
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ outudp = (struct udphdr *)outp;
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ outicmp = (struct icmp *)outp;
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ outdata = (struct outdata *)(outp + ICMP_MINLEN);
+
+ if (gw_count > 0) {
+ /* 8 = 5 (NO OPs) + 3 (code, len, ptr) */
+ optlen = 8 + gw_count * sizeof (struct in_addr);
+ }
+
+ if (raw_req) {
+ send_size = packlen - optlen;
+ } else if (useicmp) {
+ send_size = packlen - optlen - sizeof (struct ip);
+ } else {
+ send_size = packlen - optlen - sizeof (struct ip) -
+ sizeof (struct udphdr);
+ }
+
+ outip->ip_ttl = ttl;
+ outip->ip_id = htons(ident + seq);
+
+ /*
+ * If a raw IPv4 packet is going to be sent, the Time to Live
+ * field in the packet was initialized above. Otherwise, it is
+ * initialized here using the IPPROTO_IP level socket option.
+ */
+ if (!raw_req) {
+ if (setsockopt(sndsock, IPPROTO_IP, IP_TTL, (char *)&ttl,
+ sizeof (ttl)) < 0) {
+ Fprintf(stderr, "%s: IP_TTL: %s\n", prog,
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ /*
+ * In most cases, the kernel will recalculate the ip checksum.
+ * But we must do it anyway so that the udp checksum comes out
+ * right.
+ */
+ if (docksum) {
+ outip->ip_sum =
+ in_cksum((ushort_t *)outip, sizeof (*outip) + optlen);
+ if (outip->ip_sum == 0)
+ outip->ip_sum = 0xffff;
+ }
+
+ /* Payload */
+ outdata->seq = seq;
+ outdata->ttl = ttl;
+ outdata->tv = *tp;
+
+ if (useicmp) {
+ outicmp->icmp_seq = htons(seq);
+ } else {
+ outudp->uh_dport = htons((port + seq) % (MAX_PORT + 1));
+ }
+
+ if (!raw_req)
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ ((struct sockaddr_in *)to)->sin_port = outudp->uh_dport;
+
+ /* (We can only do the checksum if we know our ip address) */
+ if (docksum) {
+ if (useicmp) {
+ outicmp->icmp_cksum = 0;
+ outicmp->icmp_cksum = in_cksum((ushort_t *)outicmp,
+ packlen - (sizeof (struct ip) + optlen));
+ if (outicmp->icmp_cksum == 0)
+ outicmp->icmp_cksum = 0xffff;
+ } else {
+ /* Checksum (must save and restore ip header) */
+ tip = *outip;
+ ui = (struct udpiphdr *)outip;
+ ui->ui_next = 0;
+ ui->ui_prev = 0;
+ ui->ui_x1 = 0;
+ ui->ui_len = outudp->uh_ulen;
+ outudp->uh_sum = 0;
+ outudp->uh_sum = in_cksum((ushort_t *)ui, packlen);
+ if (outudp->uh_sum == 0)
+ outudp->uh_sum = 0xffff;
+ *outip = tip;
+ }
+ }
+
+ if (raw_req) {
+ cc = sendto(sndsock, (char *)outip, send_size, 0, to,
+ sizeof (struct sockaddr_in));
+ } else if (useicmp) {
+ cc = sendto(sndsock, (char *)outicmp, send_size, 0, to,
+ sizeof (struct sockaddr_in));
+ } else {
+ cc = sendto(sndsock, (char *)outp, send_size, 0, to,
+ sizeof (struct sockaddr_in));
+ }
+
+ if (cc < 0 || cc != send_size) {
+ if (cc < 0) {
+ Fprintf(stderr, "%s: sendto: %s\n", prog,
+ strerror(errno));
+ }
+ Printf("%s: wrote %s %d chars, ret=%d\n",
+ prog, hostname, send_size, cc);
+ (void) fflush(stdout);
+ }
+}
+
+/*
+ * Check out the reply packet to see if it's what we were expecting.
+ * Returns REPLY_GOT_TARGET if the reply comes from the target
+ * REPLY_GOT_GATEWAY if an intermediate gateway sends TIME_EXCEEDED
+ * REPLY_GOT_OTHER for other kinds of unreachables indicating none of
+ * the above two cases
+ *
+ * It also sets the icmp type and icmp code values
+ */
+int
+check_reply(struct msghdr *msg, int cc, int seq, uchar_t *type, uchar_t *code)
+{
+ uchar_t *buf = msg->msg_iov->iov_base;
+ struct sockaddr_in *from_in = (struct sockaddr_in *)msg->msg_name;
+ struct icmp *icp;
+ int hlen;
+ int save_cc = cc;
+ struct ip *ip;
+
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ ip = (struct ip *)buf;
+ hlen = ip->ip_hl << 2;
+ if (cc < hlen + ICMP_MINLEN) {
+ if (verbose) {
+ Printf("packet too short (%d bytes) from %s\n",
+ cc, inet_ntoa(from_in->sin_addr));
+ }
+ return (REPLY_SHORT_PKT);
+ }
+ cc -= hlen;
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ icp = (struct icmp *)(buf + hlen);
+
+ *type = icp->icmp_type;
+ *code = icp->icmp_code;
+
+ /*
+ * traceroute interpretes only ICMP_TIMXCEED_INTRANS, ICMP_UNREACH and
+ * ICMP_ECHOREPLY, ignores others
+ */
+ if ((*type == ICMP_TIMXCEED && *code == ICMP_TIMXCEED_INTRANS) ||
+ *type == ICMP_UNREACH || *type == ICMP_ECHOREPLY) {
+ struct ip *hip;
+ struct udphdr *up;
+ struct icmp *hicmp;
+
+ cc -= ICMP_MINLEN;
+ hip = &icp->icmp_ip;
+ hlen = hip->ip_hl << 2;
+ cc -= hlen;
+ if (useicmp) {
+ if (*type == ICMP_ECHOREPLY &&
+ icp->icmp_id == htons(ident) &&
+ icp->icmp_seq == htons(seq))
+ return (REPLY_GOT_TARGET);
+
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ hicmp = (struct icmp *)((uchar_t *)hip + hlen);
+
+ if (ICMP_MINLEN <= cc &&
+ hip->ip_p == IPPROTO_ICMP &&
+ hicmp->icmp_id == htons(ident) &&
+ hicmp->icmp_seq == htons(seq)) {
+ return ((*type == ICMP_TIMXCEED) ?
+ REPLY_GOT_GATEWAY : REPLY_GOT_OTHER);
+ }
+ } else {
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ up = (struct udphdr *)((uchar_t *)hip + hlen);
+ /*
+ * at least 4 bytes of UDP header is required for this
+ * check
+ */
+ if (4 <= cc &&
+ hip->ip_p == IPPROTO_UDP &&
+ up->uh_sport == htons(ident) &&
+ up->uh_dport == htons((port + seq) %
+ (MAX_PORT + 1))) {
+ if (*type == ICMP_UNREACH &&
+ *code == ICMP_UNREACH_PORT) {
+ return (REPLY_GOT_TARGET);
+ } else if (*type == ICMP_TIMXCEED) {
+ return (REPLY_GOT_GATEWAY);
+ } else {
+ return (REPLY_GOT_OTHER);
+ }
+ }
+ }
+ }
+
+ if (verbose) {
+ int i, j;
+ uchar_t *lp = (uchar_t *)ip;
+
+ cc = save_cc;
+ Printf("\n%d bytes from %s to ", cc,
+ inet_ntoa(from_in->sin_addr));
+ Printf("%s: icmp type %d (%s) code %d\n",
+ inet_ntoa(ip->ip_dst), *type, pr_type(*type), *code);
+ for (i = 0; i < cc; i += 4) {
+ Printf("%2d: x", i);
+ for (j = 0; ((j < 4) && ((i + j) < cc)); j++)
+ Printf("%2.2x", *lp++);
+ (void) putchar('\n');
+ }
+ }
+
+ return (REPLY_SHORT_PKT);
+}
+
+/*
+ * convert an ICMP "type" field to a printable string.
+ */
+static char *
+pr_type(uchar_t type)
+{
+ static struct icmptype_table ttab[] = {
+ {ICMP_ECHOREPLY, "Echo Reply"},
+ {1, "ICMP 1"},
+ {2, "ICMP 2"},
+ {ICMP_UNREACH, "Dest Unreachable"},
+ {ICMP_SOURCEQUENCH, "Source Quench"},
+ {ICMP_REDIRECT, "Redirect"},
+ {6, "ICMP 6"},
+ {7, "ICMP 7"},
+ {ICMP_ECHO, "Echo"},
+ {ICMP_ROUTERADVERT, "Router Advertisement"},
+ {ICMP_ROUTERSOLICIT, "Router Solicitation"},
+ {ICMP_TIMXCEED, "Time Exceeded"},
+ {ICMP_PARAMPROB, "Param Problem"},
+ {ICMP_TSTAMP, "Timestamp"},
+ {ICMP_TSTAMPREPLY, "Timestamp Reply"},
+ {ICMP_IREQ, "Info Request"},
+ {ICMP_IREQREPLY, "Info Reply"},
+ {ICMP_MASKREQ, "Netmask Request"},
+ {ICMP_MASKREPLY, "Netmask Reply"}
+ };
+ int i = 0;
+
+ for (i = 0; i < A_CNT(ttab); i++) {
+ if (ttab[i].type == type)
+ return (ttab[i].message);
+ }
+
+ return ("OUT-OF-RANGE");
+}
+
+/*
+ * print the IPv4 src address of the reply packet
+ */
+void
+print_addr(uchar_t *buf, int cc, struct sockaddr *from)
+{
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ struct sockaddr_in *from_in = (struct sockaddr_in *)from;
+ struct ip *ip;
+ union any_in_addr ip_addr;
+
+ ip_addr.addr = from_in->sin_addr;
+
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ ip = (struct ip *)buf;
+
+ if (nflag) {
+ Printf(" %s", inet_ntoa(from_in->sin_addr));
+ } else {
+ Printf(" %s (%s)", inet_name(&ip_addr, AF_INET),
+ inet_ntoa(from_in->sin_addr));
+ }
+
+ if (verbose)
+ Printf(" %d bytes to %s", cc, inet_ntoa(ip->ip_dst));
+}
+
+/*
+ * ICMP messages which doesn't mean we got the target, or we got a gateway, are
+ * processed here. It returns _B_TRUE if it's some sort of 'unreachable'.
+ */
+boolean_t
+print_icmp_other(uchar_t type, uchar_t code)
+{
+ boolean_t unreach = _B_FALSE;
+
+ /*
+ * this function only prints '!*' for ICMP unreachable messages,
+ * ignores others.
+ */
+ if (type != ICMP_UNREACH) {
+ return (_B_FALSE);
+ }
+
+ switch (code) {
+ case ICMP_UNREACH_PORT:
+ break;
+
+ case ICMP_UNREACH_NET_UNKNOWN:
+ case ICMP_UNREACH_NET:
+ unreach = _B_TRUE;
+ Printf(" !N");
+ break;
+
+ case ICMP_UNREACH_HOST_UNKNOWN:
+ case ICMP_UNREACH_HOST:
+ unreach = _B_TRUE;
+ Printf(" !H");
+ break;
+
+ case ICMP_UNREACH_PROTOCOL:
+ Printf(" !P");
+ break;
+
+ case ICMP_UNREACH_NEEDFRAG:
+ unreach = _B_TRUE;
+ Printf(" !F");
+ break;
+
+ case ICMP_UNREACH_SRCFAIL:
+ unreach = _B_TRUE;
+ Printf(" !S");
+ break;
+
+ case ICMP_UNREACH_FILTER_PROHIB:
+ case ICMP_UNREACH_NET_PROHIB:
+ case ICMP_UNREACH_HOST_PROHIB:
+ unreach = _B_TRUE;
+ Printf(" !X");
+ break;
+
+ case ICMP_UNREACH_TOSNET:
+ case ICMP_UNREACH_TOSHOST:
+ unreach = _B_TRUE;
+ Printf(" !T");
+ break;
+
+ case ICMP_UNREACH_ISOLATED:
+ case ICMP_UNREACH_HOST_PRECEDENCE:
+ case ICMP_UNREACH_PRECEDENCE_CUTOFF:
+ unreach = _B_TRUE;
+ Printf(" !U");
+ break;
+
+ default:
+ unreach = _B_TRUE;
+ Printf(" !<%d>", code);
+ break;
+ }
+
+ return (unreach);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/traceroute/traceroute_aux6.c b/usr/src/cmd/cmd-inet/usr.sbin/traceroute/traceroute_aux6.c
new file mode 100644
index 0000000000..b610aceac3
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/traceroute/traceroute_aux6.c
@@ -0,0 +1,744 @@
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+/*
+ * Copyright (c) 1988, 1989, 1991, 1994, 1995, 1996, 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ *
+ * @(#)$Header: traceroute.c,v 1.49 97/06/13 02:30:23 leres Exp $ (LBL)
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/socket.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <strings.h>
+#include <libintl.h>
+#include <errno.h>
+#include <netdb.h>
+
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/udp.h>
+#include <netinet/udp_var.h>
+#include <netinet/ip6.h>
+#include <netinet/icmp6.h>
+
+#include <arpa/inet.h>
+
+#include <ifaddrlist.h>
+#include "traceroute.h"
+
+int check_reply6(struct msghdr *, int, int, uchar_t *, uchar_t *);
+void *find_ancillary_data(struct msghdr *, int, int);
+extern char *inet_name(union any_in_addr *, int);
+static int IPv6_hdrlen(ip6_t *, int, uint8_t *);
+static char *pr_type6(uchar_t);
+void print_addr6(uchar_t *, int, struct sockaddr *);
+boolean_t print_icmp_other6(uchar_t, uchar_t);
+void send_probe6(int, struct msghdr *, struct ip *, int, int,
+ struct timeval *, int);
+void set_ancillary_data(struct msghdr *, int, union any_in_addr *, int, uint_t);
+struct ip *set_buffers6(int);
+static boolean_t update_hoplimit_ancillary_data(struct msghdr *, int);
+
+/*
+ * prepares the buffer to be sent as an IP datagram
+ */
+struct ip *
+set_buffers6(int plen)
+{
+ struct ip *outip;
+ uchar_t *outp;
+ struct udphdr *outudp;
+ struct icmp *outicmp;
+ int optlen = 0;
+
+ outip = (struct ip *)malloc((size_t)plen);
+ if (outip == NULL) {
+ Fprintf(stderr, "%s: malloc: %s\n", prog, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ if (gw_count > 0) {
+ /* ip6_rthdr0 structure includes one gateway address */
+ optlen = sizeof (struct ip6_rthdr0) +
+ gw_count * sizeof (struct in6_addr);
+ }
+
+ (void) memset((char *)outip, 0, (size_t)plen);
+ outp = (uchar_t *)(outip + 1);
+
+ if (useicmp) {
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ outicmp = (struct icmp *)outp;
+ outicmp->icmp_type = ICMP6_ECHO_REQUEST;
+ outicmp->icmp_id = htons(ident);
+ } else {
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ outudp = (struct udphdr *)outp;
+ /*
+ * "source port" is set at bind() call, so we don't do it
+ * again
+ */
+ outudp->uh_ulen = htons((ushort_t)(plen -
+ (sizeof (struct ip6_hdr) + optlen)));
+ }
+
+ return (outip);
+}
+
+/*
+ * Initialize the msghdr for specifying hoplimit, outgoing interface and routing
+ * header for the probe packets.
+ */
+void
+set_ancillary_data(struct msghdr *msgp, int hoplimit,
+ union any_in_addr *gwIPlist, int gw_cnt, uint_t if_index)
+{
+ size_t hoplimit_space;
+ size_t rthdr_space;
+ size_t pktinfo_space;
+ size_t bufspace;
+ struct cmsghdr *cmsgp;
+ uchar_t *cmsg_datap;
+ int i;
+
+ msgp->msg_control = NULL;
+ msgp->msg_controllen = 0;
+
+ /*
+ * Need to figure out size of buffer needed for ancillary data
+ * containing routing header and packet info options.
+ *
+ * Portable heuristic to compute upper bound on space needed for
+ * N ancillary data options. It assumes up to _MAX_ALIGNMENT padding
+ * after both header and data as the worst possible upper bound on space
+ * consumed by padding.
+ * It also adds one extra "sizeof (struct cmsghdr)" for the last option.
+ * This is needed because we would like to use CMSG_NXTHDR() while
+ * composing the buffer. The CMSG_NXTHDR() macro is designed better for
+ * parsing than composing the buffer. It requires the pointer it returns
+ * to leave space in buffer for addressing a cmsghdr and we want to make
+ * sure it works for us while we skip beyond the last ancillary data
+ * option.
+ *
+ * bufspace[i] = sizeof(struct cmsghdr) + <pad after header> +
+ * <option[i] content length> + <pad after data>;
+ *
+ * total_bufspace = bufspace[0] + bufspace[1] + ...
+ * ... + bufspace[N-1] + sizeof (struct cmsghdr);
+ */
+
+ rthdr_space = 0;
+ pktinfo_space = 0;
+ /* We'll always set the hoplimit of the outgoing packets */
+ hoplimit_space = sizeof (int);
+ bufspace = sizeof (struct cmsghdr) + _MAX_ALIGNMENT +
+ hoplimit_space + _MAX_ALIGNMENT;
+
+ if (gw_cnt > 0) {
+ rthdr_space = inet6_rth_space(IPV6_RTHDR_TYPE_0, gw_cnt);
+ bufspace += sizeof (struct cmsghdr) + _MAX_ALIGNMENT +
+ rthdr_space + _MAX_ALIGNMENT;
+ }
+
+ if (if_index != 0) {
+ pktinfo_space = sizeof (struct in6_pktinfo);
+ bufspace += sizeof (struct cmsghdr) + _MAX_ALIGNMENT +
+ pktinfo_space + _MAX_ALIGNMENT;
+ }
+
+ /*
+ * We need to temporarily set the msgp->msg_controllen to bufspace
+ * (we will later trim it to actual length used). This is needed because
+ * CMSG_NXTHDR() uses it to check we have not exceeded the bounds.
+ */
+ bufspace += sizeof (struct cmsghdr);
+ msgp->msg_controllen = bufspace;
+
+ msgp->msg_control = (struct cmsghdr *)malloc(bufspace);
+ if (msgp->msg_control == NULL) {
+ Fprintf(stderr, "%s: malloc %s\n", prog, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ cmsgp = CMSG_FIRSTHDR(msgp);
+
+ /*
+ * Fill ancillary data. First hoplimit, then rthdr and pktinfo if
+ * needed.
+ */
+
+ /* set hoplimit ancillary data */
+ cmsgp->cmsg_level = IPPROTO_IPV6;
+ cmsgp->cmsg_type = IPV6_HOPLIMIT;
+ cmsg_datap = CMSG_DATA(cmsgp);
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ *(int *)cmsg_datap = hoplimit;
+ cmsgp->cmsg_len = cmsg_datap + hoplimit_space - (uchar_t *)cmsgp;
+ cmsgp = CMSG_NXTHDR(msgp, cmsgp);
+
+ /* set rthdr ancillary data if needed */
+ if (gw_cnt > 0) {
+ struct ip6_rthdr0 *rthdr0p;
+
+ cmsgp->cmsg_level = IPPROTO_IPV6;
+ cmsgp->cmsg_type = IPV6_RTHDR;
+ cmsg_datap = CMSG_DATA(cmsgp);
+
+ /*
+ * Initialize rthdr structure
+ */
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ rthdr0p = (struct ip6_rthdr0 *)cmsg_datap;
+ if (inet6_rth_init(rthdr0p, rthdr_space,
+ IPV6_RTHDR_TYPE_0, gw_cnt) == NULL) {
+ Fprintf(stderr, "%s: inet6_rth_init failed\n",
+ prog);
+ exit(EXIT_FAILURE);
+ }
+
+ /*
+ * Stuff in gateway addresses
+ */
+ for (i = 0; i < gw_cnt; i++) {
+ if (inet6_rth_add(rthdr0p,
+ &gwIPlist[i].addr6) == -1) {
+ Fprintf(stderr,
+ "%s: inet6_rth_add\n", prog);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ cmsgp->cmsg_len = cmsg_datap + rthdr_space - (uchar_t *)cmsgp;
+ cmsgp = CMSG_NXTHDR(msgp, cmsgp);
+ }
+
+ /* set pktinfo ancillary data if needed */
+ if (if_index != 0) {
+ struct in6_pktinfo *pktinfop;
+
+ cmsgp->cmsg_level = IPPROTO_IPV6;
+ cmsgp->cmsg_type = IPV6_PKTINFO;
+ cmsg_datap = CMSG_DATA(cmsgp);
+
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ pktinfop = (struct in6_pktinfo *)cmsg_datap;
+ /*
+ * We don't know if pktinfop->ipi6_addr is aligned properly,
+ * therefore let's use bcopy, instead of assignment.
+ */
+ (void) bcopy(&in6addr_any, &pktinfop->ipi6_addr,
+ sizeof (struct in6_addr));
+
+ /*
+ * We can assume pktinfop->ipi6_ifindex is 32 bit aligned.
+ */
+ pktinfop->ipi6_ifindex = if_index;
+ cmsgp->cmsg_len = cmsg_datap + pktinfo_space - (uchar_t *)cmsgp;
+ cmsgp = CMSG_NXTHDR(msgp, cmsgp);
+ }
+
+ msgp->msg_controllen = (char *)cmsgp - (char *)msgp->msg_control;
+}
+
+/*
+ * Parses the given msg->msg_control to find the IPV6_HOPLIMIT ancillary data
+ * and update the hoplimit.
+ * Returns _B_FALSE if it can't find IPV6_HOPLIMIT ancillary data, _B_TRUE
+ * otherwise.
+ */
+static boolean_t
+update_hoplimit_ancillary_data(struct msghdr *msg, int hoplimit)
+{
+ struct cmsghdr *cmsg;
+ int *intp;
+
+ for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL;
+ cmsg = CMSG_NXTHDR(msg, cmsg)) {
+ if (cmsg->cmsg_level == IPPROTO_IPV6 &&
+ cmsg->cmsg_type == IPV6_HOPLIMIT) {
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ intp = (int *)(CMSG_DATA(cmsg));
+ *intp = hoplimit;
+ return (_B_TRUE);
+ }
+ }
+
+ return (_B_FALSE);
+}
+
+/*
+ * send a probe packet to the destination
+ */
+void
+send_probe6(int sndsock, struct msghdr *msg6, struct ip *outip, int seq,
+ int ttl, struct timeval *tp, int packlen)
+{
+ uchar_t *outp;
+ struct icmp *outicmp;
+ struct outdata *outdata;
+ struct iovec iov;
+ int cc;
+ int optlen = 0;
+ int send_size;
+ struct sockaddr_in6 *to6;
+
+ if (gw_count > 0) {
+ /* ip6_rthdr0 structure includes one gateway address */
+ optlen = sizeof (struct ip6_rthdr0) +
+ gw_count * sizeof (struct in6_addr);
+ }
+
+ send_size = packlen - sizeof (struct ip6_hdr) - optlen;
+
+ /* if using UDP, further discount UDP header size */
+ if (!useicmp)
+ send_size -= sizeof (struct udphdr);
+
+ /* initialize buffer pointers */
+ outp = (uchar_t *)(outip + 1);
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ outicmp = (struct icmp *)outp;
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ outdata = (struct outdata *)(outp + ICMP6_MINLEN);
+
+ if (!update_hoplimit_ancillary_data(msg6, ttl)) {
+ Fprintf(stderr,
+ "%s: can't find IPV6_HOPLIMIT ancillary data\n", prog);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Payload */
+ outdata->seq = seq;
+ outdata->ttl = ttl;
+ outdata->tv = *tp;
+
+ if (useicmp) {
+ outicmp->icmp_seq = htons(seq);
+ } else {
+ to6 = (struct sockaddr_in6 *)msg6->msg_name;
+ to6->sin6_port = htons((port + seq) % (MAX_PORT + 1));
+ }
+
+ iov.iov_base = outp;
+ iov.iov_len = send_size;
+
+ msg6->msg_iov = &iov;
+ msg6->msg_iovlen = 1;
+
+ cc = sendmsg(sndsock, msg6, 0);
+
+ if (cc < 0 || cc != send_size) {
+ if (cc < 0) {
+ Fprintf(stderr, "%s: sendmsg: %s\n", prog,
+ strerror(errno));
+ }
+ Printf("%s: wrote %s %d chars, ret=%d\n",
+ prog, hostname, send_size, cc);
+ (void) fflush(stdout);
+ }
+}
+
+/*
+ * Return a pointer to the ancillary data for the given cmsg_level and
+ * cmsg_type.
+ * If not found return NULL.
+ */
+void *
+find_ancillary_data(struct msghdr *msg, int cmsg_level, int cmsg_type)
+{
+ struct cmsghdr *cmsg;
+
+ for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL;
+ cmsg = CMSG_NXTHDR(msg, cmsg)) {
+ if (cmsg->cmsg_level == cmsg_level &&
+ cmsg->cmsg_type == cmsg_type) {
+ return (CMSG_DATA(cmsg));
+ }
+ }
+ return (NULL);
+}
+
+/*
+ * Check out the reply packet to see if it's what we were expecting.
+ * Returns REPLY_GOT_TARGET if the reply comes from the target
+ * REPLY_GOT_GATEWAY if an intermediate gateway sends TIME_EXCEEDED
+ * REPLY_GOT_OTHER for other kinds of unreachables indicating none of
+ * the above two cases
+ *
+ * It also sets the icmp type and icmp code values
+ */
+int
+check_reply6(struct msghdr *msg, int cc, int seq, uchar_t *type, uchar_t *code)
+{
+ uchar_t *buf = msg->msg_iov->iov_base;
+ struct sockaddr_in6 *from_in6 = (struct sockaddr_in6 *)msg->msg_name;
+ icmp6_t *icp6;
+ ulong_t ip6hdr_len;
+ uint8_t last_hdr;
+ int save_cc = cc;
+ char temp_buf[INET6_ADDRSTRLEN]; /* use for inet_ntop() */
+
+ /* Ignore packets > 64k or control buffers that don't fit */
+ if (msg->msg_flags & (MSG_TRUNC|MSG_CTRUNC)) {
+ if (verbose) {
+ Printf("Truncated message: msg_flags 0x%x from %s\n",
+ msg->msg_flags,
+ inet_ntop(AF_INET6,
+ (void *)&(from_in6->sin6_addr),
+ temp_buf, sizeof (temp_buf)));
+ }
+ return (REPLY_SHORT_PKT);
+ }
+ if (cc < ICMP6_MINLEN) {
+ if (verbose) {
+ Printf("packet too short (%d bytes) from %s\n",
+ cc,
+ inet_ntop(AF_INET6,
+ (void *)&(from_in6->sin6_addr),
+ temp_buf, sizeof (temp_buf)));
+ }
+ return (REPLY_SHORT_PKT);
+ }
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ icp6 = (icmp6_t *)buf;
+ *type = icp6->icmp6_type;
+ *code = icp6->icmp6_code;
+
+ /*
+ * traceroute interprets only ICMP6_TIME_EXCEED_TRANSIT,
+ * ICMP6_DST_UNREACH, ICMP6_ECHO_REPLY, ICMP6_PACKET_TOO_BIG and
+ * ICMP6_PARAMPROB_NEXTHEADER, ignores others
+ */
+ if ((*type == ICMP6_TIME_EXCEEDED &&
+ *code == ICMP6_TIME_EXCEED_TRANSIT) ||
+ *type == ICMP6_DST_UNREACH || *type == ICMP6_ECHO_REPLY ||
+ *type == ICMP6_PACKET_TOO_BIG ||
+ (*type == ICMP6_PARAM_PROB &&
+ *code == ICMP6_PARAMPROB_NEXTHEADER)) {
+ ip6_t *hip6;
+ struct udphdr *up;
+ icmp6_t *hicmp6;
+
+ cc -= ICMP6_MINLEN;
+ hip6 = (ip6_t *)&(icp6->icmp6_data32[1]);
+ last_hdr = hip6->ip6_nxt;
+ ip6hdr_len = IPv6_hdrlen(hip6, cc, &last_hdr);
+
+ cc -= ip6hdr_len;
+ if (useicmp) {
+ if (*type == ICMP6_ECHO_REPLY &&
+ icp6->icmp6_id == htons(ident) &&
+ icp6->icmp6_seq == htons(seq)) {
+ return (REPLY_GOT_TARGET);
+ }
+
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ hicmp6 = (icmp6_t *)((uchar_t *)hip6 + ip6hdr_len);
+
+ if (ICMP6_MINLEN <= cc &&
+ last_hdr == IPPROTO_ICMPV6 &&
+ hicmp6->icmp6_id == htons(ident) &&
+ hicmp6->icmp6_seq == htons(seq)) {
+ if (*type == ICMP6_TIME_EXCEEDED) {
+ return (REPLY_GOT_GATEWAY);
+ } else {
+ return (REPLY_GOT_OTHER);
+ }
+ }
+ } else {
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ up = (struct udphdr *)((uchar_t *)hip6 + ip6hdr_len);
+ /*
+ * at least 4 bytes of UDP header is required for this
+ * check
+ */
+ if (4 <= cc &&
+ last_hdr == IPPROTO_UDP &&
+ up->uh_sport == htons(ident) &&
+ up->uh_dport == htons((port + seq) %
+ (MAX_PORT + 1))) {
+ if (*type == ICMP6_DST_UNREACH &&
+ *code == ICMP6_DST_UNREACH_NOPORT) {
+ return (REPLY_GOT_TARGET);
+ } else if (*type == ICMP6_TIME_EXCEEDED) {
+ return (REPLY_GOT_GATEWAY);
+ } else {
+ return (REPLY_GOT_OTHER);
+ }
+ }
+ }
+ }
+
+ if (verbose) {
+ int i, j;
+ uchar_t *lp = (uchar_t *)icp6;
+ struct in6_addr *dst;
+ struct in6_pktinfo *pkti;
+
+ pkti = (struct in6_pktinfo *)find_ancillary_data(msg,
+ IPPROTO_IPV6, IPV6_PKTINFO);
+ if (pkti == NULL) {
+ Fprintf(stderr,
+ "%s: can't find IPV6_PKTINFO ancillary data\n",
+ prog);
+ exit(EXIT_FAILURE);
+ }
+ dst = &pkti->ipi6_addr;
+ cc = save_cc;
+ Printf("\n%d bytes from %s to ", cc,
+ inet_ntop(AF_INET6, (const void *)&(from_in6->sin6_addr),
+ temp_buf, sizeof (temp_buf)));
+ Printf("%s: icmp type %d (%s) code %d\n",
+ inet_ntop(AF_INET6, (const void *)dst,
+ temp_buf, sizeof (temp_buf)),
+ *type, pr_type6(*type), *code);
+ for (i = 0; i < cc; i += 4) {
+ Printf("%2d: x", i);
+ for (j = 0; ((j < 4) && ((i + j) < cc)); j++)
+ Printf("%2.2x", *lp++);
+ (void) putchar('\n');
+ }
+ }
+
+ return (REPLY_SHORT_PKT);
+}
+
+/*
+ * Return the length of the IPv6 related headers (including extension headers)
+ */
+static int
+IPv6_hdrlen(ip6_t *ip6h, int pkt_len, uint8_t *last_hdr_rtrn)
+{
+ int length;
+ int exthdrlength;
+ uint8_t nexthdr;
+ uint8_t *whereptr;
+ ip6_hbh_t *hbhhdr;
+ ip6_dest_t *desthdr;
+ ip6_rthdr_t *rthdr;
+ ip6_frag_t *fraghdr;
+ uint8_t *endptr;
+
+ length = sizeof (ip6_t);
+
+ whereptr = ((uint8_t *)&ip6h[1]); /* point to next hdr */
+ endptr = ((uint8_t *)ip6h) + pkt_len;
+
+ nexthdr = ip6h->ip6_nxt;
+ *last_hdr_rtrn = IPPROTO_NONE;
+
+ if (whereptr >= endptr)
+ return (length);
+
+ while (whereptr < endptr) {
+ *last_hdr_rtrn = nexthdr;
+ switch (nexthdr) {
+ case IPPROTO_HOPOPTS:
+ hbhhdr = (ip6_hbh_t *)whereptr;
+ exthdrlength = 8 * (hbhhdr->ip6h_len + 1);
+ if ((uchar_t *)hbhhdr + exthdrlength > endptr)
+ return (length);
+ nexthdr = hbhhdr->ip6h_nxt;
+ length += exthdrlength;
+ break;
+
+ case IPPROTO_DSTOPTS:
+ desthdr = (ip6_dest_t *)whereptr;
+ exthdrlength = 8 * (desthdr->ip6d_len + 1);
+ if ((uchar_t *)desthdr + exthdrlength > endptr)
+ return (length);
+ nexthdr = desthdr->ip6d_nxt;
+ length += exthdrlength;
+ break;
+
+ case IPPROTO_ROUTING:
+ rthdr = (ip6_rthdr_t *)whereptr;
+ exthdrlength = 8 * (rthdr->ip6r_len + 1);
+ if ((uchar_t *)rthdr + exthdrlength > endptr)
+ return (length);
+ nexthdr = rthdr->ip6r_nxt;
+ length += exthdrlength;
+ break;
+
+ case IPPROTO_FRAGMENT:
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ fraghdr = (ip6_frag_t *)whereptr;
+ if ((uchar_t *)&fraghdr[1] > endptr)
+ return (length);
+ nexthdr = fraghdr->ip6f_nxt;
+ length += sizeof (struct ip6_frag);
+ break;
+
+ case IPPROTO_NONE:
+ default:
+ return (length);
+ }
+ whereptr = (uint8_t *)ip6h + length;
+ }
+ *last_hdr_rtrn = nexthdr;
+
+ return (length);
+}
+
+/*
+ * convert an ICMP6 "type" field to a printable string.
+ */
+static char *
+pr_type6(uchar_t type)
+{
+ static struct icmptype_table ttab6[] = {
+ {ICMP6_DST_UNREACH, "Dest Unreachable"},
+ {ICMP6_PACKET_TOO_BIG, "Packet Too Big"},
+ {ICMP6_TIME_EXCEEDED, "Time Exceeded"},
+ {ICMP6_PARAM_PROB, "Param Problem"},
+ {ICMP6_ECHO_REQUEST, "Echo Request"},
+ {ICMP6_ECHO_REPLY, "Echo Reply"},
+ {MLD_LISTENER_QUERY, "Multicast Listener Query"},
+ {MLD_LISTENER_REPORT, "Multicast Listener Report"},
+ {MLD_LISTENER_REDUCTION, "Multicast Listener Done"},
+ {ND_ROUTER_SOLICIT, "Router Solicitation"},
+ {ND_ROUTER_ADVERT, "Router Advertisement"},
+ {ND_NEIGHBOR_SOLICIT, "Neighbor Solicitation"},
+ {ND_NEIGHBOR_ADVERT, "Neighbor Advertisement"},
+ {ND_REDIRECT, "Redirect Message"}
+ };
+ int i = 0;
+
+ for (i = 0; i < A_CNT(ttab6); i++) {
+ if (ttab6[i].type == type)
+ return (ttab6[i].message);
+ }
+
+ return ("OUT-OF-RANGE");
+}
+
+
+/*
+ * print the IPv6 src address of the reply packet
+ */
+void
+print_addr6(uchar_t *buf, int cc, struct sockaddr *from)
+{
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ struct sockaddr_in6 *from_in6 = (struct sockaddr_in6 *)from;
+ ip6_t *ip;
+ union any_in_addr ip_addr;
+ char *resolved_name;
+ char temp_buf[INET6_ADDRSTRLEN]; /* use for inet_ntop() */
+
+ ip_addr.addr6 = from_in6->sin6_addr;
+
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ ip = (ip6_t *)buf;
+
+ (void) inet_ntop(AF_INET6, &(from_in6->sin6_addr), temp_buf,
+ sizeof (temp_buf));
+ if (!nflag)
+ resolved_name = inet_name(&ip_addr, AF_INET6);
+ /*
+ * If the IPv6 address cannot be resolved to hostname, inet_name()
+ * returns the IPv6 address as a string. In that case, we choose not
+ * to print it twice. This saves us space on display.
+ */
+ if (nflag || (strcmp(temp_buf, resolved_name) == 0))
+ Printf(" %s", temp_buf);
+ else
+ Printf(" %s (%s)", resolved_name, temp_buf);
+
+ if (verbose) {
+ Printf(" %d bytes to %s", cc, inet_ntop(AF_INET6,
+ (const void *) &(ip->ip6_dst), temp_buf,
+ sizeof (temp_buf)));
+ }
+}
+
+/*
+ * ICMP6 messages which doesn't mean we got the target, or we got a gateway, are
+ * processed here. It returns _B_TRUE if it's some sort of 'unreachable'.
+ */
+boolean_t
+print_icmp_other6(uchar_t type, uchar_t code)
+{
+ boolean_t unreach = _B_FALSE;
+
+ switch (type) {
+
+ /* this corresponds to "ICMP_UNREACH_NEEDFRAG" in ICMP */
+ case ICMP6_PACKET_TOO_BIG:
+ unreach = _B_TRUE;
+ Printf(" !B");
+ break;
+
+ case ICMP6_PARAM_PROB:
+ /* this corresponds to "ICMP_UNREACH_PROTOCOL" in ICMP */
+ if (code == ICMP6_PARAMPROB_NEXTHEADER) {
+ unreach = _B_TRUE;
+ Printf(" !R");
+ }
+ break;
+
+ case ICMP6_DST_UNREACH:
+ switch (code) {
+ case ICMP6_DST_UNREACH_NOPORT:
+ break;
+
+ case ICMP6_DST_UNREACH_NOROUTE:
+ unreach = _B_TRUE;
+ Printf(" !H");
+ break;
+
+ case ICMP6_DST_UNREACH_ADMIN:
+ unreach = _B_TRUE;
+ Printf(" !X");
+ break;
+
+ case ICMP6_DST_UNREACH_ADDR:
+ unreach = _B_TRUE;
+ Printf(" !A");
+ break;
+
+ case ICMP6_DST_UNREACH_NOTNEIGHBOR:
+ unreach = _B_TRUE;
+ Printf(" !E");
+ break;
+
+ default:
+ unreach = _B_TRUE;
+ Printf(" !<%d>", code);
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return (unreach);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/wanbootutil.sh b/usr/src/cmd/cmd-inet/usr.sbin/wanbootutil.sh
new file mode 100644
index 0000000000..7280299202
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/wanbootutil.sh
@@ -0,0 +1,43 @@
+#!/usr/bin/pfsh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+
+#
+# wanbootutil is a wrapper that executes programs in /usr/lib/inet/wanboot
+# which are used to perform WAN boot administration tasks.
+#
+
+case "$1" in
+keygen | keymgmt | p12split)
+ exec /usr/lib/inet/wanboot/"$@"
+ ;;
+
+*)
+ echo "Usage: wanbootutil [keygen | keymgmt | p12split] [options] "
+ exit 1
+ ;;
+esac